硬件部分
控制芯片
我使用的是STC89C52。
车体
可以购买车体套件,套件包含了车轮,电机,底盘以及连接的小零件。
不建议购买金属底盘,小车所使用的部件大多在底部有裸露引脚,金属底盘会导致短路。
3.L298N电机驱动模块
输出A/B:连接左右电机。
通道A/B使能:把跳线帽拔下后就可以通过PWM调速对电机进行调速。
逻辑输入:与最小系统板引脚连接,IN1和IN2控制左侧电机,IN3和IN4控制右侧电机。
12V供电:连接电源正极。
供电GND:连接电源正极以及单片机GND。
5V供电:连接单片机VCC。
建议加装稳压模块,因为L298N输出电压不稳定。
红外循迹模块
可以选用四路,二路,五路。
红外循迹模块由探头和主控板组成。当感应器接收到返回的红外光时,主控板上的指示灯会熄灭,同时输出高电平;若感应器为接收到返回的红外光,则指示灯亮,同时输出低电平。
主控板上有四个旋钮,可调节四个探头的灵敏度,在小车组装完成后,上电调节灵敏度。调节至指示灯处于亮灭边界即可。
电池
推荐使用12V可充电锂电池组,可以反复使用,不建议使用车体套件附带的电池盒,因为使用电池盒时电压不够大,电机动力不足。购买的电池组需要一个DC转接头。
5.51最小系统板
也可以直接使用51学习板,但其体积较大,不便于安装。
注:还需要一些其余零件,例如黑色电工胶布,杜邦线,螺丝,根据个人需求购买电烙铁,电机上有两个小铜环用于接电,可以直接用线搭上去,但是容易断裂,建议焊接。
程序部分
我的程序采用了模块化编程,也可以不用,但是代码会比较长。
编写程序前需要学习PWM调速以及定时器。
小车的转弯靠左右轮的差速,在转弯时一个轮子正转,一个反转效果比较好。
主函数
//驱动主函数
#include<REGX52.H>
#include"timer.h"
#include"Delay.h"
#include"MOTIONCONTROL.h"
#include"drive.h"
#include"direction.h"
void main()
{
Timer0_Init();
EA=1;
while(1)
{
CONTROL();
}
}
定时器
#include <REGX52.H>
sbit ENA=P1^1;
sbit ENB=P1^0;
unsigned int PWML,PWMR,t=0;//PWML控制左轮,PWMR控制右轮,t为比较值
void Timer0_Init()
{
TMOD &= 0xF0;
TMOD |= 0x01;
TL0 = 65436%256;
TH0 = 65436/256;
TF0 = 0;
TR0 = 1;
ET0=1;
EA=1;
PT0=0;
}//100us
void Timer0_Rountinue() interrupt 1
{
TL0 = 65436%256;
TH0 = 65436/256;
t++;
if(t<PWML) ENA=1;
else ENA=0;
if(t<PWMR) ENB=1;
else ENB=0;
t%=100;
}
循迹函数
#include<REGX52.H>
#include"timer.h"
#include"direction.h"
#include"drive.h"
#include"Delay.h"
sbit D1=P2^3;
sbit D2=P2^2;
sbit D3=P2^1;
sbit D4=P2^0;
void CONTROL()//识别到黑线,灯灭,输出1
{
if(D1==0&&D2==0&&D3==0&&D4==0)//前进
{
PWML=14;
PWMR=14;
cargo();
}
if(D1==0&&D2==1&&D3==1&&D4==0)//前进
{
PWML=15;
PWMR=15;
cargo();
}
if(D1==1&&D2==1&&D3==1&&D4==1)//停止
{
PWML=0;
PWMR=0;
carstop();
}
if(D1==1&&D2==0&&D3==0&&D4==0)//右转大弯,左轮大
{
PWML=20;
PWMR=9;
carright();
}
if(D1==0&&D2==0&&D3==0&&D4==1)//左转大弯,右轮大
{
PWML=9;
PWMR=20;
carleft();
}
if(D1==0&&D2==0&&D3==1&&D4==0)//左转小弯,右轮大
{
PWML=14;
PWMR=16;
cargo();
}
if(D1==0&&D2==1&&D3==0&&D4==0)//右转小弯,左轮大
{
PWML=16;
PWMR=14;
cargo();
}
if(D1==1&&D2==1&&D3==0&&D4==0)//右转大弯,左轮大
{
PWML=20;
PWMR=9;
carright();
}
if(D1==1&&D2==1&&D3==1&&D4==0)//右转大弯,左轮大
{
PWML=20;
PWMR=9;
carright();
}
if(D1==0&&D2==1&&D3==1&&D4==1)//左转大弯,右轮大
{
PWML=9;
PWMR=20;
carleft();
}
if(D1==1&&D2==0&&D3==1&&D4==0)//右转大弯,左轮大
{
PWML=20;
PWMR=9;
carright();
}
if(D1==0&&D2==1&&D3==0&&D4==1)//左转大弯,右轮大
{
PWML=9;
PWMR=20;
carleft();
}
if(D1==0&&D2==0&&D3==1&&D4==1)//左转大弯,右轮大
{
PWML=9;
PWMR=20;
carleft();
}
}
方向相关函数
sbit IN1=P0^7;
sbit IN2=P0^6;
sbit IN3=P0^5;
sbit IN4=P0^4;
// IN1=1;
// IN2=0;
void Leftstop()
{
IN1=0;
IN2=0;
}
void Rightstop()
{
IN3=0;
IN4=0;
}
void Leftforward()
{
IN1=0;
IN2=1;
}
void Rightforward()
{
IN3=1;
IN4=0;
}
void Leftback()
{
IN1=1;
IN2=0;
}
void Rightback()
{
IN3=0;
IN4=1;
}
#include <REGX52.H>
#include"drive.h"
#include"timer.h"
void cargo()
{
Leftforward();
Rightforward();
}
void carstop()
{
Rightstop();
Leftstop();
}
void carleft()
{
Leftback();
Rightforward();
}
void carright()
{
Leftforward();
Rightback();
}
经验及错误总结
*定时器里面的TR0必须注意要置1,不然无法进入中断程序,占空比设置多少电机都是全速前进。
*若使用模块化编程,PWML和PWMR的值应设置为全局变量,不然其它文件不能使用PWML和PWMR的值。
*使用电烙铁一定要注意安全,最好找些教学视频后再上手。电烙铁长时间干烧会导致烙铁头氧化,不易上锡。
*电池组不能直接为单片机供电,不然芯片会被烧毁。
*小车最后虽然能完成要求,但因为程序上的不足并不能走好直线,如果想取得更好的结果,可以学习PID。
*循迹函数中有转小弯部分,若想使用该部分最好将中间两个探头分开些距离(具体取决于自己的实际需求),并且两个轮子的PWM值差不能太大。我最开始将两个探头紧挨在一起,想用来调节前进姿态,让小车走直线的轨迹更完美,但实际结果是小车反而更容易冲出轨道。
*小车循迹函数探头条件的if语句最好不要太简单,例如直接if(D1==1)就让小车进入右转,这样代码容易发生冲突,我在调试过程中因为循迹代码的冲突,导致车轮又正转又反转,最后的结果就是车轮无法运动。
*我的车最后要比较完美得走直线只能够把速度设置得很小,可能是受制于硬件。万向轮的存在让二驱的小车不是很适合走直线,地面不平整很容易使万向轮方向改变,即使两轮速度一致也会发生偏移。
小车速度很小的情况下需要外力才能正常运行。
*在编写代码过程中要注意自己设置的引脚是什么。我在编写调速函数时把左右轮设置反了,在调试过程中转向一直无法正常发生,甚至出现左转变右转,右转变左转的情况。定时器在从stc中复制下来的过程中,不知道是自己的原因还是stc的问题,TR0被置零,小车一直无法调速。