初学者的第一个项目:基于51单片机循迹小车
准备工作
1)控制芯片:
由于刚学完51基础课程,所以这里直接用51学习板(STC89C52RC)。
2)车身:
可以直接在某宝购买,一般都会直接配齐(亚克力车身,电机,轮胎以及固定螺丝)。
3)L298N电机驱动模块:
L298N模块可以驱动 电机正反转,并且能直接给单片机供电(L298N接入12V)。
输出 A/B: 分别接 左/右 电机 (电机接法如上图所示)
通道A/B使能: 当把帽子拔下就可以通过PWM对电机进行调速
逻辑输入: IN1/2 控制 输出A 端 电机的正反转
IN3/4 控制 输出B 端 电机的正反转
电机的正反转 与 电机和输出A/B的接线有关,接线之后应当先调试判断是否方向正确,若方向不对因把电机与输出A/B的接线换序
接线:按照上图进行接线,输出A/B 接电机,逻辑输入接单片机I/O口
4)红外循迹模块
推荐使用TCR5000循迹模块(单个的循迹模块可以根据自己的实际情况进行调整扫描位置)
循迹原理: 利用红外线对于不同颜色具有不同的反射性质的特点。在小车行驶过程中传感器的红外发射二极管不断发射红外光,当红外光遇到白色地面时发生漫反射,红外对管接收管接收反射光;如果遇到黑线则红外光被吸收,则红外管接收不到信号。
红外对管采集回来的信号通过2路循迹传感器模块里面的LM339比较器后输出高或低电平,从而实现信号的检测。
接线:(A0可以不接)D0为输出信号接单片机I/O口
注意:有的厂商设置的是红外管接收不到信号 输出高电平,有的厂商设置的则是红外管接收不到信号 输出低电平。故在接线后一定要调试(这里是输出高电平)
5)电源
强烈建议用 12V锂电池,之前自己用的是9V碱性电池,虽然电压有9V左右,但电流严重不足。
所以可以直接用12V2000ma左右的锂电池 就可以直接驱动L298N而且还能通过L298N直接给单片机供电,而且锂电池还能 充电反复使用。
功能介绍
根据TCR5000循迹模块的工作原理,用单片机I/O口接收TCR5000循迹模块发出的信号,当单片机I/O口接收到低电平/高电平时就表示检测到轨道(黑线),然后给L298N的逻辑输入脚信号,让电机做出相应的动作。
小车转弯可以通过一侧轮胎正转另一侧不动实现,但这样小车移动的角度就很大,所以一般不推荐;另一种方法则是通过一侧轮胎正转另一侧反转,这样小车移动角度就会小很多,甚至于原地打转都行。
源码(仅供参考)
主函数(采用模块化编程,后续头文件自己补充啦)
#include <REGX52.H>
#include "trac.h"
#include "pwm.h"
int main ()
{
Timer0_PWM_Init();
P0=0XFF;
while(1)
{
Trac();
}
}
PWM函数
#include <REGX52.H>
sbit ENA=P2^0; //ENA使然口
sbit ENB=P2^5; //ENB使然口
unsigned char compare_ENA,compare_ENB;//PWM比较值
unsigned char count_ENA,count_ENB; //PWM计数值
/***********************************************************************************
* @brief 定时器0初始化
* @param
* @retval
*/
void Timer0_PWM_Init(void) //100微秒@11.0592MHz
{
TMOD &= 0xF0; //设置定时器模式
TMOD |= 0x01; //设置定时器模式
TL0 = 0xA4; //设置定时初值
TH0 = 0xFF; //设置定时初值
TF0 = 0; //清除TF0标志
TR0 = 1; //定时器0开始计时
ET0=1;
EA=1;
}
/***********************************************************************************
* @brief PWM比较值赋值函数
* @param
* @retval
*/
void PWM_Compare( unsigned char Date1,Date2)
{
compare_ENA=Date1;
compare_ENB=Date2;
}
/***********************************************************************************
* @brief 定时器0执行函数
* @param
* @retval
*/
void Timer0_PWM_Run(void) interrupt 1
{
// TL0 = 0xA4; //设置定时初值
// TH0 = 0xFF; //设置定时初值
TL0 = 0xE9; //设置定时初值
TH0 = 0xFF; //设置定时初值
count_ENA++;
count_ENA%=100; //控制范围0~99
if(count_ENA<compare_ENA)
{
ENA=1;
}
else if(count_ENA>compare_ENA)
{
ENA=0;
}
count_ENB++;
count_ENB%=100; //控制范围0~99
if(count_ENB<compare_ENB)
{
ENB=1;
}
else if(count_ENB>compare_ENB)
{
ENB=0;
}
}
循迹函数
#include <REGX52.H>
#include "pwm.h"
/***********************************************************************************
* @brief 红外对管从左至右返回信号脚
*/
sbit IR_1=P0^3;
sbit IR_2=P0^4;
sbit IR_3=P0^5;
sbit IR_4=P0^6;
/***********************************************************************************
* @brief L298N信号输出 10正转 01反转
*/
sbit IN1=P2^1;
sbit IN2=P2^2;
sbit IN3=P2^3;
sbit IN4=P2^4;
/***********************************************************************************
* @brief 控制车轮旋转方向 10正转 01反转
* @param a,b,,c,d
* @retval
*/
void L298N_Init(unsigned char a,b,c,d)
{
IN1=a;
IN2=b;
IN3=c;
IN4=d;
}
/***********************************************************************************
* @brief 循迹模块执行函数
* @param 无
* @retval 无
*/
void Trac()
{
if(IR_1==0 && IR_2==0 && IR_3==1 && IR_4==0 ) //中间检测到黑线,此时小车位置中间偏右
{
PWM_Compare( 20,20); //左右轮都为20%
L298N_Init(1,0,1,0); //左右轮都正传
}
else if(IR_1==1 && IR_2==0 && IR_3==0 && IR_4==0 ) //最左边检测到黑线
{
PWM_Compare( 30,40); //左轮为30% 右轮为40%
L298N_Init(0,1,1,0); //左轮反传 右轮正转
}
else if(IR_1==0 && IR_2==1 && IR_3==0 && IR_4==0 ) //中间检测到黑线,此时小车位置中间偏左
{
PWM_Compare( 23,23); //左轮为23% 右轮为23%
L298N_Init(1,0,1,0); //左轮正传 右轮正转
}
else if(IR_1==0 && IR_2==0 && IR_3==0 && IR_4==1 ) //最右边检测到黑线
{
PWM_Compare( 40,30); //左轮为40% 右轮为30%
L298N_Init(1,0,0,1); //左轮正传 右轮反转
}
else if(IR_1==0 && IR_2==0 && IR_3==0 && IR_4==0 ) //未检测到黑线
{
PWM_Compare( 0,0);
L298N_Init(0,0,0,0); //轮胎不运动
}
else if(IR_1==1 && IR_2==1 && IR_3==1 && IR_4==1 ) //四路都检测到黑线
{
PWM_Compare( 0,0);
L298N_Init(0,0,0,0); //轮胎不运动
}
else if(IR_1==0 && IR_2==1 && IR_3==1 && IR_4==0 ) //中间两路检测到黑线
{
PWM_Compare( 17,17); //左轮为17% 右轮为17%
L298N_Init(1,0,1,0); //左轮正传 右轮正转
}
}