当前位置:   article > 正文

智能小车项目_麦克纳姆轮小车电机选择

麦克纳姆轮小车电机选择

智能小车项目

最近花时间做了一个小车,所以在这里做个总结。

材料选型

电机选型:1:48的减速电机(这里的1:48是指减速比,是指电机通过牺牲速度来获得扭矩的方法。减速比越大,它的速度就越慢,扭矩就越大)

轮子:麦克纳姆轮(麦克纳姆轮是一种全向轮,由于其独特的构造,才使得麦克纳姆轮随意改变行驶方向的特性,但在安装的时候要注意麦克纳姆轮的安装方向)

轮子的正确分布方向:

关于麦克纳姆轮的详细介绍请看这篇博文:麦克纳姆轮(万向轮)驱动和玩法_麦克纳姆轮转向_

电机驱动选型:L298N

由于麦轮对扭矩要求比较大,所以普通的L9110S满足不了需求。而且当给L298N输入7V以上的电压时,可以向板外供电5V,所以优先选用L298N。

L298N的详细资料:链接:https://pan.baidu.com/s/1gyQNSwDTgpNtM-_t4V5dSQ 提取码:52x7

电池选型:选用18650锂电池,有试过别的电池,结果都是供电有问题。


小车启动

怎么让小车动起来?即怎么用单片机控制电机转动?

很简单,给电机驱动模块的两个引脚一高一低,电机就会转动

代码实现:

/*****main.c*****/

#include "motor.h"

void main()
{
	while(1)
	{
		Car_Move();
	}
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
/*****motor.c*****/

#include "pin.h"
#include "delay.h"

void Go_Forward()			//正转
{
	A1_1 = 0;				//A1轮
	A1_2 = 1;
	
	B1_1 = 0;				//B1轮
	B1_2 = 1;
	
	B2_1 = 0;				//B2轮
	B2_2 = 1;				
	
	A2_1 = 0;				//A2轮
	A2_2 = 1;				
}

void Go_Back()				//反转
{
	A1_1 = 1;				//A1轮
	A1_2 = 0;
	
	B1_1 = 1;				//B1轮
	B1_2 = 0;
	
	B2_1 = 1;				//B2轮
	B2_2 = 0;				
	
	A2_1 = 1;				//A2轮
	A2_2 = 0;				
}

void Right_Tran()				//向右平移
{
	A1_1 = 0;				//A1轮
	A1_2 = 1;
	
	B1_1 = 1;				//B1轮
	B1_2 = 0;
	
	B2_1 = 1;				//B2轮
	B2_2 = 0;				
	
	A2_1 = 0;				//A2轮
	A2_2 = 1;				
}

void Left_Tran()			//向左平移
{
	A1_1 = 1;				//A1轮
	A1_2 = 0;
	
	B1_1 = 0;				//B1轮
	B1_2 = 1;
	
	B2_1 = 0;				//B2轮
	B2_2 = 1;				
	
	A2_1 = 1;				//A2轮
	A2_2 = 0;				
}

void Turn_Right()			//右转
{
	A1_1 = 0;				//A1轮
	A1_2 = 1;
	
	B1_1 = 1;				//B1轮
	B1_2 = 0;
	
	B2_1 = 0;				//B2轮
	B2_2 = 1;	
	
	A2_1 = 1;				//A2轮
	A2_2 = 0;	
}

void Turn_Left()				//左转
{
	A1_1 = 1;				//A1轮
	A1_2 = 0;
	
	B1_1 = 0;				//B1轮
	B1_2 = 1;
	
	B2_1 = 1;				//B2轮
	B2_2 = 0;		
	
	A2_1 = 0;				//A2轮
	A2_2 = 1;	
}

void Car_Move()
{
	Go_Forward();
	Delay1000ms();
	Go_Back();
	Delay1000ms();
	Turn_Left();
	Delay1000ms();
	Turn_Right();
	Delay1000ms();
	Left_Tran();
	Delay1000ms();
	Right_Tran();
	Delay1000ms();
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
/*****delay.c*****/

#include "intrins.h"

void Delay1000ms()		//@11.0592MHz
{
	unsigned char i, j, k;

	_nop_();
	i = 8;
	j = 1;
	k = 243;
	do
	{
		do
		{
			while (--k);
		} while (--j);
	} while (--i);
}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
/*****pin.h*****/

#include <reg52.h>

sbit A1_1 = P1^0;					//麦轮分布:ABBA
sbit A1_2 =	P1^1;
sbit A2_1 =	P1^6;
sbit A2_2 =	P1^7;
sbit B1_1 = P1^4;
sbit B1_2 = P1^5;
sbit B2_1 =	P1^2;
sbit B2_2 = P1^3;

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13

串口(蓝牙)控制小车

​ 只让小车自己跑感觉少了点意思,所以加入串口控制,发送命令来控制小车进行前后左右等运动,就像之前用串口控制灯的亮灭。

/*****main.c*****/

#include "uart.h"


void main()
{
	UART_Init();
	while(1)
	{
		
	}
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
/*****uart.c****/

#include "reg52.h"
#include "string.h"
#include "motor.h"
#define SIZE 12

sfr AUXR = 0x8E;

char buffer[SIZE];

void UART_Init()
{
	SCON = 0x50;	//配置SCON为8位UART,波特率可变,REN允许串行接收
	TMOD &= 0x0F;	//定时器1高位清零,地位不变
	TMOD |= 0x20;	//定时器1采用8位重装计数
	TL1 = 0xFD;	
	TH1 = 0xFD;		//9600波特率对应初值
	TR1 = 1;			//启动定时器1
	AUXR = 0x01;	//禁止ALE信号输出
	EA = 1;				//开启总中断
	ES = 1;				//开启串口中断
}

void UART_handler () interrupt 4
{
	static int i = 0;		//静态变量,初始化执行一次
	char tmp;
	if(RI)	//如果接收到数据,硬件将RI自动置1,需要软件清0
	{
		RI = 0;
		tmp = SBUF;
		if(tmp == 'M')
		{
			i = 0;
		}
		buffer[i] = tmp;
		i++;
		if(buffer[0] == 'M')
		{
			switch(buffer[1])
			{
				case '1':
					Go_Forward();		//发送M1前进,依此类推
					break;
				case '2':
					Go_Back();
					break;
				case '3':
					Turn_Left();
					break;
				case '4':
					Turn_Right();
					break;
				case '5':
					Left_Tran();
					break;
				case '6':
					Right_Tran();
					break;
				default:
					Car_Stop();
					break;
			}
		}
		
	
		if(i == SIZE)
		{
			
			i = 0;
			memset(buffer,'\0',SIZE);
		}
	}
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75

小车点动

​ 通过蓝牙模块能够控制小车的运动状态,如果想实现按下按键启动,松开按键停止的效果可以借助STC-ISP的工具来实现。

/*****main.c*****/

#include "uart.h"
#include "motor.h"

void main()
{
	UART_Init();
	while(1)
	{
		Car_Stop();		//通过在main函数离不断地调用Car_Stop();使小车一直处于停止状态,在串口那里发指令给延时让小车运动20ms,这样就会形成发一次指令运动20ms的效果,如果一直发指令,小车就会一直运动,停止发指令,小车就会调用Car_Stop();函数停止运动
	}
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
/*****uart.c*****/

#include "reg52.h"
#include "string.h"
#include "motor.h"
#include "delay.h"
sbit LED1 = P3^7;
#define SIZE 12

sfr AUXR = 0x8E;

char buffer[SIZE];

void UART_Init()
{
	SCON = 0x50;	//配置SCON为8位UART,波特率可变,REN允许串行接收
	TMOD &= 0x0F;	//定时器1高位清零,地位不变
	TMOD |= 0x20;	//定时器1采用8位重装计数
	TL1 = 0xFD;	
	TH1 = 0xFD;		//9600波特率对应初值
	TR1 = 1;			//启动定时器1
	AUXR = 0x01;	//禁止ALE信号输出
	EA = 1;				//开启总中断
	ES = 1;				//开启串口中断
}

void UART_handler () interrupt 4
{
	static int i = 0;		//静态变量,初始化执行一次
	char tmp;
	if(RI)	//如果接收到数据,硬件将RI自动置1,需要软件清0
	{
		RI = 0;
		tmp = SBUF;
		if(tmp == 'M')
		{
			i = 0;
		}
		buffer[i] = tmp;
		i++;
		if(buffer[0] == 'M')
		{
			switch(buffer[1])
			{
				case '1':
					Go_Forward();
					Delay20ms();		//给延时让小车运动一会儿,以下同上
					break;
				case '2':
					Go_Back();
					Delay20ms();
					break;
				case '3':
					Turn_Left();
					Delay20ms();
					break;
				case '4':
					Turn_Right();
					Delay20ms();
					break;
				case '5':
					Left_Tran();
					Delay20ms();
					break;
				case '6':
					Right_Tran();
					Delay20ms();
					break;
				default:
					Car_Stop();
					break;
			}
		}
		
	
		if(i == SIZE)
		{
			
			i = 0;
			memset(buffer,'\0',SIZE);
		}
	}
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83

PWM控制小车速度

在前边的运动过程中,小车一直处于全速运行的状态,那么如何实现小车转的慢一会儿快一会儿?

用PWM来控制小车速度

​ 前边用PWM来控制舵机,在20ms的一个脉冲内保持0.5ms的高电平,剩下的都为低电平,舵机就会转动0°;当在20ms的一个脉冲内保持2ms的高电平,剩下的都为低电平,舵机就会转动135°。

​ 这里用PWM控制小车速度就是在20ms的一个周期内,如果小车在20ms内全速运行,那么小车的速度就达到了峰值;如果小车在10ms内全速运行,剩下时间内一直停止,那么小车的速度就是全速运行的一半。依此类推,如果小车全速运行的时间越短,停止的时间越长,那么小车的速度就会越慢,以此可以实现小车控制小车速度的功能。

代码实现:

#include "motor.h"
#include "delay.h"
#include "timer.h"

extern char speed;		

void main()
{
	Timer0Init();
	while(1)
	{
		speed = 25;
		Delay2000ms();
		speed = 50;
		Delay2000ms();
		speed = 80;
		Delay2000ms();
	}
}
/*
extern的作用:

C51头文件使用extern的目的是外部变量或函数声明。
使用时要注意:
1.在使用extern进行外部变量声明时,不能重新给变量赋值。例如:
extern unsigned char i=0;(错误,编译器会报错)
extern unsigned char i;(正确)
其他文件使用extern外部变量声明的变量时(i),可以赋值。(unsigned char i=0;)

2.extern只是声明不是定义。extern只是告诉编译器声明的变量(i),是在之前已经定义过的,且在这里要用到。
*/
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
/*****timer.c*****/

#include "reg52.h"
#include "motor.h"

char cnt = 0;
char speed;

void Timer0Init()
{
	TMOD &= 0xF0;
	TMOD |= 0x01;
	TL0 = 0x1A;
	TH0 = 0xFF;
	EA =1;
	ET0 = 1;
	TR0 = 1;
	TF0 = 0;
}

void Timer0 () interrupt 1		//PWM是50Hz,也就是20ms,将20ms分为80份,定一个0.25ms出来
{
	cnt++;
	TL0 = 0x1A;
	TH0 = 0xFF;
	if(cnt < speed)
	{
		Go_Forward();
	}
	else
	{
		Car_Stop();
	}
	if(cnt == 80)
	{
		cnt = 0;
	}
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38

PWM控制小车差速运动

​ 前边实现了对小车速度的控制,现在要实现差速运动,即左右轮速度不一样,那么就需要各自的前进函数和停止函数,以此来实现小车的差速运动。

代码实现:

/*****main.c*****/

#include "motor.h"
#include "delay.h"
#include "timer.h"

extern char speedLeft;
extern char speedRight;

void main()
{
	Timer0Init();
	Timer1Init();
	
	while(1)
	{
		speedLeft = 40;
		speedRight = 80;
		Delay1000ms();
		speedLeft = 80;
		speedRight = 40;
		Delay1000ms();
	}
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
/*****motor.c*****/

#include "pin.h"
#include "delay.h"

void Go_Forward_Right()			//正转
{
	B1_1 = 0;				//B1轮
	B1_2 = 1;		
	
	A2_1 = 0;				//A2轮
	A2_2 = 1;				
}

void Go_Forward_Left()
{
	A1_1 = 0;				//A1轮
	A1_2 = 1;
	
	B2_1 = 0;				//B2轮
	B2_2 = 1;		
}

void Car_Stop_Right()
{
	B1_1 = 0;				//B1轮
	B1_2 = 0;
	
	A2_1 = 0;				//A2轮
	A2_2 = 0;	
}

void Car_Stop_Left()
{
	A1_1 = 0;				//A1轮
	A1_2 = 0;
	
	B2_1 = 0;				//B2轮
	B2_2 = 0;		
}

void Go_Back()				//反转
{
	A1_1 = 1;				//A1轮
	A1_2 = 0;
	
	B1_1 = 1;				//B1轮
	B1_2 = 0;
	
	B2_1 = 1;				//B2轮
	B2_2 = 0;				
	
	A2_1 = 1;				//A2轮
	A2_2 = 0;				
}

void Right_Tran()				//向右平移
{
	A1_1 = 0;				//A1轮
	A1_2 = 1;
	
	B1_1 = 1;				//B1轮
	B1_2 = 0;
	
	B2_1 = 1;				//B2轮
	B2_2 = 0;				
	
	A2_1 = 0;				//A2轮
	A2_2 = 1;				
}

void Left_Tran()			//向左平移
{
	A1_1 = 1;				//A1轮
	A1_2 = 0;
	
	B1_1 = 0;				//B1轮
	B1_2 = 1;
	
	B2_1 = 0;				//B2轮
	B2_2 = 1;				
	
	A2_1 = 1;				//A2轮
	A2_2 = 0;				
}

void Turn_Left()			//左转
{
	A1_1 = 0;				//A1轮
	A1_2 = 1;
	
	B1_1 = 1;				//B1轮
	B1_2 = 0;
	
	B2_1 = 0;				//B2轮
	B2_2 = 1;	
	
	A2_1 = 1;				//A2轮
	A2_2 = 0;	
}

void Turn_Right()				//右转
{
	A1_1 = 1;				//A1轮
	A1_2 = 0;
	
	B1_1 = 0;				//B1轮
	B1_2 = 1;
	
	B2_1 = 1;				//B2轮
	B2_2 = 0;		
	
	A2_1 = 0;				//A2轮
	A2_2 = 1;	
}


  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117
/*****timer.c*****/

#include "reg52.h"
#include "motor.h"

char cntLeft = 0;
char speedLeft;

char cntRight = 0;
char speedRight;

void Timer0Init()
{
	TMOD &= 0xF0;
	TMOD |= 0x01;
	TL0 = 0x1A;
	TH0 = 0xFF;
	EA =1;
	ET0 = 1;
	TR0 = 1;
	TF0 = 0;
}

void Timer1Init()
{
	TMOD &= 0x0F;
	TMOD |= 0x10;
	TL1 = 0x1A;
	TH1 = 0xFF;
	EA =1;
	ET1 = 1;
	TR1 = 1;
	TF1 = 0;
}

void Timer0_Handler () interrupt 1		//PWM是50Hz,也就是20ms,将20ms分为80份,定一个0.25ms出来
{
	cntLeft++;
	TL0 = 0x1A;
	TH0 = 0xFF;
	if(cntLeft < speedLeft)
	{
		Go_Forward_Left();
	}
	else
	{
		Car_Stop_Left();
	}
	if(cntLeft == 80)
	{
		cntLeft = 0;
	}
}

void Timer1_Handler () interrupt 3		//PWM是50Hz,也就是20ms,将20ms分为80份,定一个0.25ms出来
{
	cntRight++;
	TL1 = 0x1A;
	TH1 = 0xFF;
	if(cntRight < speedRight)
	{
		Go_Forward_Right();
	}
	else
	{
		Car_Stop_Right();
	}
	if(cntRight == 80)
	{
		cntRight = 0;
	}
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72

循迹小车

循迹小车就是利用TCRT5000红外循迹传感器,利用红外容易被黑色吸收,白色反射的原理进行循迹。

下面是TCRT5000的工作原理

根据循迹模块的工作原理我们就能对小车的运动方向做出判断:

当左循迹和右循迹都照到白色的地图上时,左右模块都能接收到返回来的红外输出低电平,此时开关指示灯亮,小车前进;

当左循迹模块压线,右循迹模块照到白色地图上时,左循迹模块发出的红外被黑线吸收,输出高电平,此时开关指示灯灭,右循迹模块能够反射回来,输出低电平,开关指示灯亮,所以此时小车应该左转;

当右循迹模块压线,左循迹模块照到白色地图上时,右循迹模块发出的红外被黑线吸收,输出高电平,此时开关指示灯灭,左循迹模快能够反射回来,输出低电平,开关指示灯亮,所以此时小车应该右转;

当小车跑出地图后,左右循迹模块发出的红外都被吸收,输出高电平,此时开关指示灯灭,所以此时小车应该停下。

代码实现:

/*****main.c*****/

#include "motor.h"
#include "pin.h"

//直行:左右传感器都反射回来输出低电平亮
//左转:左传感器压线被吸收返回高电平灭,右传感器反射回来输出低电平亮
//右转:右传感器压线被吸收返回高电平灭,左传感器反射回来输出低电平亮
//停止:左右传感器都检测跑出跑道,输高电平灭,停止
void main()
{
	while(1)
	{
			if(Lefttrack == 0 && Righttrack == 0)
			{
				Go_Forward();
			}
			if(Lefttrack == 1  && Righttrack == 0)
			{
				Turn_Left();
			}
			if(Lefttrack == 0  && Righttrack == 1)
			{
				Turn_Right();
			}
			if(Lefttrack == 1 && Righttrack == 1)
			{
				Car_Stop();
			}
 	}
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
/*****pin.h*****/

#include <reg52.h>

sbit A1_1 = P1^0;					//麦轮分布:ABBA
sbit A1_2 =	P1^1;
sbit A2_1 =	P1^6;
sbit A2_2 =	P1^7;
sbit B1_1 = P1^4;
sbit B1_2 = P1^5;
sbit B2_1 =	P1^2;
sbit B2_2 = P1^3;


sbit Lefttrack 	= P2^7;
sbit Righttrack = P2^6;		
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16

循迹小车优化

​ 由于上个代码在左右转弯的时候,每个电机都是全速运行,所以会看到小车在拐弯地时候一卡一卡的。真正实际情况中,在转弯的时候每个轮子的速度都不一样,所以下面加入电机调速来优化循迹小车。

​ 电机调速就利用上边PWM来调速,在转弯的时候给左右轮子不同的速度,使它转弯变得更平滑一些。还可以调节上边的电位器来改变红外反射的距离,配合代码调节循迹小车。

代码实现:

/*****main.c*****/

#include "motor.h"
#include "pin.h"
#include "timer.h"

extern char speedLeft;
extern char speedRight;

void main()
{
	Timer0Init();
	Timer1Init();

	while(1)
	{
		if(Lefttrack == 0 && Righttrack == 0)
		{
			speedLeft = 70;
			speedRight = 70;
		}
		if(Lefttrack == 1 && Righttrack == 0)			//左转
		{
			speedLeft = 25;		
			speedRight = 75;							//speedRight>speedLeft,左转
		}
		if(Lefttrack == 0 && Righttrack == 1)			//右转
		{
			speedLeft = 70;
			speedRight = 25;							//speedLeft>speedRight右转
		}
		if(Lefttrack == 1 && Righttrack == 1)
		{
			speedLeft = 0;
			speedRight = 0;													
		}
	}
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
/*****timer.c*****/

#include "reg52.h"
#include "motor.h"

char cntLeft = 0;
char speedLeft;

char cntRight = 0;
char speedRight;

void Timer0Init()
{
	TMOD &= 0xF0;
	TMOD |= 0x01;
	TL0 = 0x1A;
	TH0 = 0xFF;
	EA =1;
	ET0 = 1;
	TR0 = 1;
	TF0 = 0;
}

void Timer1Init()
{
	TMOD &= 0x0F;
	TMOD |= 0x10;
	TL1 = 0x1A;
	TH1 = 0xFF;
	EA =1;
	ET1 = 1;
	TR1 = 1;
	TF1 = 0;
}

void Timer0_Handler () interrupt 1		//PWM是50Hz,也就是20ms,将20ms分为80份,定一个0.25ms出来
{
	cntLeft++;
	TL0 = 0x1A;
	TH0 = 0xFF;
	if(cntLeft < speedLeft)
	{
		Go_Forward_Left();
	}
	else
	{
		Car_Stop_Left();
	}
	if(cntLeft == 80)
	{
		cntLeft = 0;
	}
}

void Timer1_Handler () interrupt 3		//PWM是50Hz,也就是20ms,将20ms分为80份,定一个0.25ms出来
{
	cntRight++;
	TL1 = 0x1A;
	TH1 = 0xFF;
	if(cntRight < speedRight)
	{
		Go_Forward_Right();
	}
	else
	{
		Car_Stop_Right();
	}
	if(cntRight == 80)
	{
		cntRight = 0;
	}
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72

跟随小车

​ 跟随小车用的模块和循迹的是一样的,都是TCRT5000红外传感器,只不过循迹的传感器是在下方,跟随用到的是在前方。

根据跟随模块的工作原理对小车的运动方向做出判断:

当物体在正前方的时候,左跟随模块和右跟随模块检测到前边有物体被挡住的时候,红外就会反射回来,输出低电平,此时开关指示灯亮,所以小车应该前进;

当物体在左前方的时候,左跟随模块检测到左边有物体时,左跟随模块发射的红外就会被反射回来,输出低电平,此时开关指示灯亮;右跟随模块没有检测到物体,红外不会反射回来,输出高电平,开关指示灯灭,所以小车应该左转;

当物体在右前方的时候,右跟随模块检测到右边有物体时,右跟随模块发射的红外就会被反射回来,输出低电平,此时开关指示灯亮;左跟随模块没有检测到物体,红外不会反射回来,输出高电平,开关指示灯灭,所以小车应该右转;

当小车前面没有物体的时候,左右跟随模块发射红外都不会反射回来,输出高电平,此时开关指示灯灭,所以小车应该停下来。

代码实现:(具体的参数需要根据场地去调整)

/*****main.c*****/

#include "motor.h"
#include "pin.h"

//直行:左右传感器都反射回来输出低电平亮
//左转:左传感器检测到前方有物体,反射回来输出低电平亮,右传感器没有检测到输出高电平灭
//右转:右传感器检测到前方有物体,反射回来输出低电平亮,左传感器没有检测到输出高电平灭
//停止:左右传感器都没有检测到前方有物体,输出高电平灭
void main()
{
	while(1)
	{
		if(Leftfollow == 0 && Rightfollow == 0)
		{
			Go_Forward();
		}
		if(Leftfollow == 0 && Rightfollow == 1)
		{
			Turn_Left();
		}
		if(Leftfollow == 1 && Rightfollow == 0)
		{
			Turn_Right();
		}
		if(Leftfollow == 1 && Rightfollow == 1)
		{
			Car_Stop();
		}
	}
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
/*****pin.h*****/

#include <reg52.h>

sbit A1_1 = P1^0;					//麦轮分布:ABBA
sbit A1_2 =	P1^1;
sbit A2_1 =	P1^6;
sbit A2_2 =	P1^7;
sbit B1_1 = P1^4;
sbit B1_2 = P1^5;
sbit B2_1 =	P1^2;
sbit B2_2 = P1^3;

sbit Lefttrack 	= P2^7;
sbit Righttrack = P2^6;
sbit Rightfollow = P2^4;
sbit Leftfollow = P2^5;
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17

避障小车

​ 避障小车的原理是通过超声波配合舵机获取障碍物的距离,从而小车作出相应的前进、后退、左转、右转运动。

代码实现:

/*****main.c*****/

#include "SG90.h"
#include "HC04.h"
#include "delay.h"
#include "motor.h"


void main()
{
	double disMiddle;
	double disLeft;
	double disRight;
	
	Timer0Init();
	Timer1Init();
	sgMiddle();
	Delay300ms();
	Delay300ms();
	while(1)
	{
		disMiddle = Get_distance();
		
		if(disMiddle > 30)
		{
			Go_Forward();//前进
		}
		else if(disMiddle < 10)
		{
			Go_Back();//后退
//			Delay200ms();
//			Car_Stop();
		}
		else
		{
			Car_Stop();
			sgLeft();		//测左边距离
			Delay300ms();
			disLeft = Get_distance();
			
			sgMiddle();
			Delay300ms();
			
			sgRight();	//测右边距离
			Delay300ms();
			disRight = Get_distance();
			
			if(disLeft < disRight)
			{
				Turn_Right();
				Delay150ms();
				Car_Stop();
			}
			if(disRight < disLeft)
			{
				Turn_Left();
				Delay150ms();
				Car_Stop();
			}
		}
		sgMiddle();
		Delay300ms();
	}
}


  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
/*****Hc04.c*****/

#include "pin.h"
#include "delay.h"


void Timer1Init()
{
	TMOD &= 0x0F;		//设置定时器模式
	TMOD |= 0x10;
	TL1 = 0;
	TH1 = 0;
}


void HC_START()
{
	Trig = 0;
	Trig = 1;
	Delay10us();
	Trig = 0;
}

double Get_distance()
{
	double time;
	TL1 = 0;
	TH1 = 0;
	
	HC_START();
	while(Echo == 0);
	TR1 = 1;
	
	while(Echo == 1);
	TR1 = 0;
	
	time = (TH1*256+TL1)*1.085;
	return (time*0.017);
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
/*****SG90.c*****/

#include "reg52.h"
#include "pin.h"

int cnt = 0;
int angel;
int angel_back;


void Timer0Init()
{
	TMOD &= 0xF0;		//设置定时器模式
	TMOD |= 0x01;
	TL0 = 0x33;
	TH0 = 0xFE;
	EA =1;
	ET0 = 1;
	TR0 = 1;
	TF0 = 0;
}

void sgLeft()
{
	angel = 5;
	angel_back = angel;
	cnt = 0;
}

void sgMiddle()
{
	angel = 3;
	if(angel_back != angel)
	{
		cnt = 0;
	}
	angel_back = angel;
}

void sgRight()
{
	angel = 1;
	angel_back = angel;
	cnt = 0;
}

void Timer0 () interrupt 1
{
	cnt++;
	TL0 = 0x33;
	TH0 = 0xFE;
	if(cnt < angel)
	{
		sg90 = 1;
	}
	else
	{
		sg90 = 0;
	}
	if(cnt == 40)
	{
		cnt = 0;
		sg90 = 1;
	}
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
/*****pin.h*****/

#include <reg52.h>

sbit LED1 = P3^7;
sbit LED2 = P3^6;
sbit sg90 = P2^3;
sbit Trig = P2^2;
sbit Echo = P2^1;

sbit A1_1 = P1^0;					//麦轮分布:ABBA
sbit A1_2 =	P1^1;
sbit A2_1 =	P1^6;
sbit A2_2 =	P1^7;
sbit B1_1 = P1^4;
sbit B1_2 = P1^5;
sbit B2_1 =	P1^2;
sbit B2_2 = P1^3;

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19

测速小车

​ 测速模块的原理:接好VCC和GND,模块信号指示灯会亮,当模块的槽中无遮挡时,接收管导通,模块OUT输出高电平,遮挡时,OUT输出低电平,信号指示灯灭。将测速模块接到外部中断引脚,检测是否有遮挡来检测电机的转速。

​ 小车的直径是5.8cm,周长为18.212cm,小车的码盘有20个格子,每经过一个各自会产生(遮挡)高电平和(不遮挡)低电平,那么一个脉冲就是5.8 * 3.14 / 20 = 0.9106 cm

​ 定时器可以定时0.25ms,那么经过3600次爆表就经过了0.9s,此时统计脉冲数,即速度。

小车测速并发送到串口

代码实现:

/*****main.c*****/

/*测速模块接到P3.2外部中断0引脚,设置下降沿触发,每当码盘经过测速模块时就会触
发一次下降沿,此时启动外部中断0,对speedCnt的值进行累加,同时定时器0在工作,
定时一个单位0.25ms,经过3600次爆表计时0.9s。此时统计speedCnt的值,将speedCnt的值
保存在speed中,然后复位进行下一次测速,最后通过sprintf完成数据转换将speed的值发
送到串口上去
*/

#include "uart.h"
#include "timer.h"
#include "stdio.h"

extern unsigned int speedB1;
extern unsigned int speedA1;
extern char signal;
char speedMeg1[24];
char speedMeg2[24];

void main()
{
	Timer0Init();
	INT0_Init();
	INT1_Init();
	UART_Init();
	while(1)
	{
		if(signal == 1)
		{
			sprintf(speedMeg1,"speedA1: %d cm/s\r\n",speedA1);		
	//sprintf 格式化输出函数,将"speedA1: %d cm/s\r\n"这一串装到speedMeg1中,其中%d以speedA1来填充
			SendString(speedMeg1);
			sprintf(speedMeg2,"speedB1: %d cm/s\r\n",speedB1);
			SendString(speedMeg2);
			signal = 0;
		}
	}
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
/*****timer.c*****/

#include "reg52.h"
#include "motor.h"

unsigned int cnt = 0;
char signal = 0;

unsigned int speedA1;
unsigned int speedCntA1;

unsigned int speedB1;
unsigned int speedCntB1;

void Timer0Init()
{
	TMOD &= 0xF0;
	TMOD |= 0x01;
	TL0 = 0x1A;
	TH0 = 0xFF;
	EA =1;
	ET0 = 1;
	TR0 = 1;
	TF0 = 0;
}

void INT0_Init()
{
	EA = 1;
	EX0 = 1;		//设置外部中断0打开
	IT0 = 1;		//设置外部中断由下降沿触发
}

void INT1_Init()
{
	EA = 1;
	EX1 = 1;
	IT1 = 1;
}

void Timer0 () interrupt 1		//PWM是50Hz,也就是20ms,将20ms分为80份,定一个0.25ms出来
{
	cnt++;
	TL0 = 0x1A;
	TH0 = 0xFF;
	
	if(cnt == 3600)			//报表3600次,经过了0.9s
	{
		cnt = 0;
		signal = 1;
		speedA1 = speedCntA1;		//测速模块A1轮
		speedCntA1 = 0;
		
		speedB1 = speedCntB1;		//测速模块B1轮
		speedCntB1 = 0;
	}
}

void INT0_Handler () interrupt 0
{
	speedCntA1++;
}

void INT1_Handler () interrupt 2
{
	speedCntB1++;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
/*****pin.h*****/

#include <reg52.h>

sbit A1_1 = P1^0;					//麦轮分布:ABBA
sbit A1_2 =	P1^1;
sbit A2_1 =	P1^6;
sbit A2_2 =	P1^7;
sbit B1_1 = P1^4;
sbit B1_2 = P1^5;
sbit B2_1 =	P1^2;
sbit B2_2 = P1^3;

sbit speedA1 = P3^2;
sbit speedB1 = P3^3;
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15

​ 这个既然能发送到串口,也就能发送到蓝牙模块,通过蓝牙模块进行小车的控制,并且小车的速度会实时的传到手机上。


小车测速并发送到oled屏幕上

​ 利用之前开发oled屏幕的经验,把小车的速度实时的显示到oled屏幕上去,同时还发送到串口。

代码实现:

/*****main.c*****/

/*测速模块接到P3.2外部中断0引脚,设置下降沿触发,每当码盘经过测速模块时就会触
发一次下降沿,此时启动外部中断0,对speedCnt的值进行累加,同时定时器0在工作,
定时一个单位0.25ms,经过3600次爆表计时0.9s。此时统计speedCnt的值,将speedCnt的值
保存在speed中,然后复位进行下一次测速,最后通过sprintf完成数据转换将speed的值发
送到串口上去
*/

#include "uart.h"
#include "timer.h"
#include "stdio.h"
#include "oled.h"

extern unsigned int speedB1;
extern unsigned int speedA1;
extern char signal;
char speedMeg1[24];
char speedMeg2[24];

void main()
{
	
	Timer0Init();
	INT0_Init();
	INT1_Init();
	UART_Init();
	Oled_RST();
	Oled_Init();
	Oled_Clean();
	
	while(1)
	{
		if(signal == 1)
		{
			sprintf(speedMeg1,"speed_A1: %d cm/s",speedA1);
			SendString(speedMeg1);
			SendString("\r\n");
			sprintf(speedMeg2,"speed_B1: %d cm/s",speedB1);
			SendString(speedMeg2);
			signal = 0;
		}
		Oled_Show_String(1,1,speedMeg1);	//在第一行第一列显示A1轮的速度
		Oled_Show_String(2,1,speedMeg2);	//在第二行第一列显示B1轮的速度
	}
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
/*****oled.c*****/

#include "IIC.h"
#include "intrins.h"
#include "pin.h"
#include "oledFun.h"

void Oled_Write_Cmd(char cmd)
{
	IIC_Start();						//起始信号
	IIC_SendByte(0x78);			//发送从机地址
	IIC_ACK();							//应答信号
	IIC_SendByte(0x00);			//写命令0x00
	IIC_ACK();							//应答信号
	IIC_SendByte(cmd);			//发送命令
	IIC_ACK();							//应答信号
	IIC_Stop();							//停止信号
}

void Oled_Write_Data(char Data)
{
	IIC_Start();						//起始信号
	IIC_SendByte(0x78);			//发送从机地址
	IIC_ACK();							//应答信号
	IIC_SendByte(0x40);			//写数据0x40
	IIC_ACK();							//应答信号
	IIC_SendByte(Data);			//发送命令
	IIC_ACK();							//应答信号
	IIC_Stop();							//停止信号
}

void Oled_Init(void)
{
	Oled_Write_Cmd(0xAE);//--display off
	Oled_Write_Cmd(0x00);//---set low column address
	Oled_Write_Cmd(0x10);//---set high column address
	Oled_Write_Cmd(0x40);//--set start line address
	Oled_Write_Cmd(0xB0);//--set page address
	Oled_Write_Cmd(0x81); // contract control
	Oled_Write_Cmd(0xFF);//--128
	Oled_Write_Cmd(0xA1);//set segment remap
	Oled_Write_Cmd(0xA6);//--normal / reverse
	Oled_Write_Cmd(0xA8);//--set multiplex ratio(1 to 64)
	Oled_Write_Cmd(0x3F);//--1/32 duty
	Oled_Write_Cmd(0xC8);//Com scan direction
	Oled_Write_Cmd(0xD3);//-set display offset
	Oled_Write_Cmd(0x00);//
	Oled_Write_Cmd(0xD5);//set osc division
	Oled_Write_Cmd(0x80);//
	Oled_Write_Cmd(0xD8);//set area color mode off
	Oled_Write_Cmd(0x05);//
	Oled_Write_Cmd(0xD9);//Set Pre-Charge Period
	Oled_Write_Cmd(0xF1);//
	Oled_Write_Cmd(0xDA);//set com pin configuartion
	Oled_Write_Cmd(0x12);//
	Oled_Write_Cmd(0xDB);//set Vcomh
	Oled_Write_Cmd(0x30);//
	Oled_Write_Cmd(0x8D);//set charge pump enable
	Oled_Write_Cmd(0x14);//
	Oled_Write_Cmd(0xAF);//--turn on oled panel
}

void Oled_Clean()
{
	unsigned char i,j;		//用char会造成越界-128 - 127
	
	for(i=0;i<8;i++)
	{
		Oled_Write_Cmd(0xB0 + i);		//Page 0 
		Oled_Write_Cmd(0x00);
		Oled_Write_Cmd(0x10);		//第0列到第127列,写入数据后,列地址会自动偏移
		for(j=0;j<128;j++)
		{
			Oled_Write_Data(0);		//Page 0 -Page 7,第0列到第127列都写入0清屏
		}
	}
}

void Delay200ms()		//@11.0592MHz
{
	unsigned char i, j, k;

	_nop_();
	i = 2;
	j = 103;
	k = 147;
	do
	{
		do
		{
			while (--k);
		} while (--j);
	} while (--i);
}

void Oled_RST()
{
	RES = 0;
	Delay200ms();
	RES = 1;
}

void Oled_Show_Byte(char rows,char columns,char oledByte)
{ 
	unsigned int i;
 
	//显示字符的上半部分
	Oled_Write_Cmd(0xb0+(rows*2-2));  //选择行
	//选择列                        
	Oled_Write_Cmd(0x00+(columns&0x0f));                          
	Oled_Write_Cmd(0x10+(columns>>4));      
	//显示数据                       
	for(i=((oledByte-32)*16);i<((oledByte-32)*16+8);i++){
		Oled_Write_Data(F8X16[i]);                            
	}
 
 	//显示字符的下半部分
	Oled_Write_Cmd(0xb0+(rows*2-1));  //选择行  
	//选择列                    
	Oled_Write_Cmd(0x00+(columns&0x0f));                          
	Oled_Write_Cmd(0x10+(columns>>4)); 
	//显示数据                           
	for(i=((oledByte-32)*16+8);i<((oledByte-32)*16+8+8);i++){
		Oled_Write_Data(F8X16[i]);                            
	}       
}
 
//OLED显示一个字符串
void Oled_Show_String(char rows,char columns,char *str)
{
	while(*str != '\0'){
		Oled_Show_Byte(rows,columns,*str);
		str++;
		columns += 8;   
	}       
}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117
  • 118
  • 119
  • 120
  • 121
  • 122
  • 123
  • 124
  • 125
  • 126
  • 127
  • 128
  • 129
  • 130
  • 131
  • 132
  • 133
  • 134
  • 135
  • 136
  • 137
/*****pin.h*****/

#include <reg52.h>

sbit A1_1 = P1^0;					//麦轮分布:ABBA
sbit A1_2 =	P1^1;
sbit A2_1 =	P1^6;
sbit A2_2 =	P1^7;
sbit B1_1 = P1^4;
sbit B1_2 = P1^5;
sbit B2_1 =	P1^2;
sbit B2_2 = P1^3;

sbit speedA1 = P3^2;
sbit speedB1 = P3^3;

sbit SCL = P3^4;
sbit SDA = P3^5;
sbit RES = P3^6;
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19

WiFi控制小车并实时传输

前边用过WIFi模块控制LED,它有两个模式,一个是当作客户端接入家里的路由器;另一个是它当作路由器,然后由别的设备当作客户端接入,这里采用第二种方式来控制小车。

这里我主要介绍用手机软件来进行连接小车WiFi模块的网络

  1. 首先用usb转ttl模块获得esp8266的IP地址,端口号默认333,然后将8266接到单片机上,使用单片机来将8266配置成路由模式。
  2. 然后用手机搜索WiFi名字,可能是安信可,也可能是ESP开头的名字,点击连接(这一步很重要,目的是为了两个设备在同一ip下,如果不连接网络,连接8266的路由时绝对连接不上的!!!)
  3. 然后打开手机软件,点击客户端,输入8266的ip和端口号就可以连接了。
  4. 然后通过手机软件发送指令就可以控制小车运动,同时速度也会传到手机上。

代码实现:

/*****main.c*****/

#include "uart.h"
#include "timer.h"
#include "stdio.h"
#include "oled.h"
#include "esp8266.h"
#include "delay.h"

extern unsigned int speedB1;
extern unsigned int speedA1;
extern char signal;
char speedMeg1[24];
char speedMeg2[24];
code char Send_Data[] = "AT+CIPSEND=0,34\r\n";	//这里如果字节开辟的不够大,串口会自动截取前边的数据进行发送

void main()
{
	
	Timer0Init();
	UART_Init();
	
	Delay1000ms();
	InitWifi();
	Link_Client();
	
	INT0_Init();
	INT1_Init();
	Oled_RST();
	Oled_Init();
	Oled_Clean();
	
	while(1)
	{
		if(signal == 1)
		{
			SendString(Send_Data);
			Delay1000ms();
			sprintf(speedMeg1,"speed_A1: %d cm/s",speedA1);
			SendString(speedMeg1);
			SendString("\r\n");
			sprintf(speedMeg2,"speed_B1: %d cm/s",speedB1);
			SendString(speedMeg2);
			signal = 0;
		}
		Oled_Show_String(1,1,speedMeg1);
		Oled_Show_String(2,1,speedMeg2);
	}
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
/*****uart.c*****/

#include "reg52.h"
#include "string.h"
#include "motor.h"
#define SIZE 12

sfr AUXR = 0x8E;

char buffer[SIZE];

extern char AT_OK_Flag;			//发送AT指令返回标志
extern char Con_Client_Flag;	//连接服务器成功标志

void UART_Init()
{
	SCON = 0x50;	//配置SCON为8位UART,波特率可变,REN允许串行接收
	TMOD &= 0x0F;	//定时器1高位清零,地位不变
	TMOD |= 0x20;	//定时器1采用8位重装计数
	TL1 = 0xFD;	
	TH1 = 0xFD;		//9600波特率对应初值
	TR1 = 1;			//启动定时器1
	AUXR = 0x01;	//禁止ALE信号输出
	EA = 1;				//开启总中断
	ES = 1;				//开启串口中断
}

void SendByte(char Senddata)
{
	SBUF = Senddata;
	while(!TI);
	TI = 0;
}

void SendString(char *str)
{
	while(*str != '\0')
	{
		SendByte(*str);
		str++;
	}
}

void UART_handler () interrupt 4
{
	static int i = 0;		//静态变量,初始化执行一次
	char tmp;
	if(RI)	//如果接收到数据,硬件将RI自动置1,需要软件清0
	{
		RI = 0;
		tmp = SBUF;
		if(tmp == 'M' || tmp == 'O' || tmp == '0')
		{
			i = 0;
		}
		buffer[i] = tmp;
		i++;
		
		if(buffer[0] == 'O' && buffer[1] == 'K')
		{
			AT_OK_Flag = 1;
			memset(buffer,'\0',SIZE);
		}
		
		if(buffer[0] == '0' && buffer[2] == 'C')
		{
			Con_Client_Flag = 1;
			memset(buffer,'\0',SIZE);
		}
		
		if(buffer[0] == 'M')
		{
			switch(buffer[1])
			{
				case '1':
					Go_Forward();
					break;
				case '2':
					Go_Back();
					break;
				case '3':
					Turn_Left();
					break;
				case '4':
					Turn_Right();
					break;
				case '5':
					Left_Tran();
					break;
				case '6':
					Right_Tran();
					break;
				default:
					Car_Stop();
					break;
			}
		}		
	
		if(i == SIZE)
		{
			
			i = 0;
			memset(buffer,'\0',SIZE);
		}
	}
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
/*****esp8266.c*****/

#include "uart.h"

code char Double_Mode[] = "AT+CWMODE=2\r\n";
code char More_Con[] = "AT+CIPMUX=1\r\n";
code char Start_Ser[] = "AT+CIPSERVER=1\r\n";

char AT_OK_Flag = 0;
char Con_Client_Flag = 0;

void InitWifi()
{
	SendString(Double_Mode);		//配置双模模式
	while(!AT_OK_Flag);
	AT_OK_Flag = 0;
	SendString(More_Con);				//使能更多链接
	while(!AT_OK_Flag);
	AT_OK_Flag = 0;
}

void Link_Client()
{
	SendString(Start_Ser);			//连接服务器
	while(!Con_Client_Flag);
	AT_OK_Flag = 0;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27

4G模块控制实时传输

​ 前边用4G模块控制LED,它的原理是家里使用的路由器的网络是局域网,外网不能访问,所以要有一个桥梁将局域网(内网)变成一个公网(外网)以供外网设备进行连接,可以借助花生壳软件进行内网穿透,然后外网设备就可以进行连接,最后由服务器去控制外网设备。

​ 这里还是用手机软件来搭建一个服务器,然后用花生壳软件进行内网穿透,4G模块连接穿透后的IP就连接上了服务器,最后通过服务器发送指令就可以控制小车运动了,同时小车的速度还会实时传输到手机上。

  1. 首先建立一个TCP服务器

  1. 将服务器的ip和端口号输入到花生壳软件中

  1. 用花生壳软件内网穿透后的ip和端口号来配置4G模块

注意:首先要建立TCP服务器,要不然花生壳软件会连接失败!!!

因为4G模块自己有心跳包,但我们这里要发送速度,为了避免造成影响,所以配置4G模块不发送心跳包。

代码没有改变(和蓝牙模块一样),4G模块主要是配置问题,配置好了它会自动进入透传模式。


语音控制小车

语音小车用语音模块SU-03T,通过配置语音词条可以实现对小车的控制,

​ 设置A25,A26,A27这三个引脚的初始状态都为高电平,然后通过配置三个引脚的电平状态来控制小车在避障、循迹、跟随、停止四种模式下切换。

  1. 当A25 == 0 && A26 == 1 && A27 == 1时,小车处于循迹模式;
  2. 当A25 == 1 && A26 == 0 && A27 == 1时,小车处于跟随模式;
  3. 当A25 == 1 && A26 == 1 && A27 == 0时,小车处于避障模式;
  4. 当A25 == 0 && A26 == 0 && A27 == 0时,小车处于停止模式。

​ 最后将这三个引脚与单片机进行连接来控制小车的状态

代码实现:

/*****main.c*****/

#include "SG90.h"
#include "HC04.h"
#include "delay.h"
#include "oled.h"
#include "mode.h"
#include "pin.h"
#include "motor.h"

#define Avoid 1
#define Follow 2
#define Track  3
#define Stop 4

extern char dir;

void main()
{
	int mark = 0;
	
	Timer0Init();
	Timer1Init();
	sgMiddle();
	Delay300ms();
	Delay300ms();
	Oled_RST();
	Oled_Init();
	Oled_Clean();
	Oled_Show_String(2,5,"****Ready****");
	
	while(1)
	{
		if(A25 == 0 && A26 == 1 && A27 == 1)
		{
			if(mark != Track)	//如果第二次循环进来还是循迹模式,那就不执行清屏函数,避免它一直刷屏
			{
				Oled_Clean();
				Oled_Show_String(2,1,"**Track_Mode**");
			}
			Track_Mode();
			mark = Track;	
		}
		
		if(A25 == 1 && A26 == 0 && A27 == 1)
		{
			if(mark != Follow)
			{
				Oled_Clean();
				Oled_Show_String(2,1,"**Follow_Mode**");
			}
			Follow_Mode();
			mark = Follow;	
		}
		
		if(A25 == 1 && A26 == 1 && A27 == 0)
		{
			if(mark != Avoid)
			{
				Oled_Clean();
				Oled_Show_String(2,1,"***Avoid_Mode***");
			}
			Avoid_Mode();
			mark = Avoid;	
		}
		
		if(A25 == 0 && A26 == 0 && A27 == 0)
		{
			if(mark != Stop)
			{
				Oled_Clean();
				Oled_Show_String(2,1,"***Stop_Mode***");
			}
			Car_Stop();
			mark = Stop;	
		}
	}
}


  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
/*****mode.c*****/

#include "pin.h"
#include "SG90.h"
#include "HC04.h"
#include "motor.h"
#include "delay.h"

double disMiddle;
double disLeft;
double disRight;
	
void Avoid_Mode()
{
	disMiddle = Get_distance();
		
		if(disMiddle > 30)
		{
			Go_Forward();//前进
		}
		else if(disMiddle < 10)
		{
			Go_Back();//后退
//			Delay200ms();
//			Car_Stop();
		}
		else
		{
			Car_Stop();
			sgLeft();		//测左边距离
			Delay300ms();
			disLeft = Get_distance();
			
			sgMiddle();
			Delay300ms();
			
			sgRight();	//测右边距离
			Delay300ms();
			disRight = Get_distance();
			
			if(disLeft < disRight)
			{
				Turn_Left();
				Delay150ms();
				Car_Stop();
			}
			if(disRight < disLeft)
			{
				Turn_Right();
				Delay150ms();
				Car_Stop();
			}
		}
		sgMiddle();
		Delay300ms();
}


void Track_Mode()
{
		if(Lefttrack == 0 && Righttrack == 0)
		{
			Go_Forward();
		}
			if(Lefttrack == 1  && Righttrack == 0)
		{
			Turn_Left();
		}
			if(Lefttrack == 0  && Righttrack == 1)
		{
			Turn_Right();
		}
			if(Lefttrack == 1 && Righttrack == 1)
		{
			Car_Stop();
		}
}

void Follow_Mode()
{
		if(Leftfollow == 0 && Rightfollow == 0)
		{
			Go_Forward();
		}
		if(Leftfollow == 0 && Rightfollow == 1)
		{
			Turn_Left();
		}
		if(Leftfollow == 1 && Rightfollow == 0)
		{
			Turn_Right();
		}
		if(Leftfollow == 1 && Rightfollow == 1)
		{
			Car_Stop();
		}
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97

显示效果:视频发到抖音去了(DX_dingding)

总结

小车项目中遇到的问题:

  1. 小车不按自己设定的方向走,可能是没共地;
  2. 小车上电以后只能向前走,可能是电源电压不足,小车进行转向的时候,需要比较的的电流;
  3. 蓝牙模块在发送指令控制小车进行运动时会发生断联的情况,蓝牙模块供电不足;
  4. 如果使用金属底板,要注意模块和单片机不能直接放到金属板上,可能会导电烧毁板子。可以在底下垫一层纸起到绝缘的效果;
  5. 当小车组装起来的时候,可以用万用表测量一下,防止模块正负极接反导致模块烧毁;
  6. 电池选用18650锂电池。
声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/我家自动化/article/detail/476825
推荐阅读
相关标签
  

闽ICP备14008679号