目录
前言
在大学期间,通过实验室考核做的51循迹小车(循迹、超声波测距),有很多的不足,主要记录自己的学习过程(含代码)
一、硬件准备
1.51单片机
2.小车底板(亚克力板)
3.两个前轮子、电机、一个万向轮(买小车的时候都配有)
4.若干不同高度铜柱、若干杜邦线、面包板
5.超声波
6.电源模块(建议12V可充电锂电池)
7.红外循迹模块
8.L298N
二、模块介绍
1.电机驱动模块
控制引脚
- IN1 & IN2 电机驱动器A的输入引脚,控制电机A转动及旋转角度
- IN1输入高电平HIGH,IN2输入低电平LOW,对应电机A正转
- IN1输入低电平LOW,IN2输入高电平HIGH,对应电机A反转
- IN1、IN2同时输入高电平HIGH或低电平LOW,对应电机A停止转动
- 调速就是改变IN1、IN2高电平的占空比(需拔掉ENA处跳帽)
- IN3 & IN4 电机驱动器B的输入引脚,控制电机B转动及旋转角度
- IN3输入高电平HIGH,IN4输入低电平LOW,对应电机B正转
- IN3输入低电平LOW,IN4输入高电平HIGH,对应电机B反转
- IN3、IN4同时输入高电平HIGH或低电平LOW,对应电机B停止转动
- 调速就是改变IN3、IN4高电平的占空比(需拔掉ENB处跳帽)
输出引脚
- OUT1 & OUT2 电机驱动器A的输出引脚,接直流电机A或步进电机的A+和A-
- OUT3 & OUT4 电机驱动器B的输出引脚,接直流电机B或步进电机的B+和B-
调速控制引脚(不用时跳帽不要拔)
- ENA 电机A调速开关引脚,拔掉跳帽,使用PWM对电机A调速,插上电机A高速运行
- ENB 电机B调速开关引脚,拔掉跳帽,使用PWM对电机B调速,插上电机B高速运行
L298N工作逻辑IO口输入方式(高电平→1,低电平→0)
直流电机 | 旋转方式 | IN1 | IN2 | IN3 | IN4 | PWM调速信号 | |
ENA | ENB | ||||||
M1 | 正转 | 1 | 0 | / | / | 1 | / |
反转 | 0 | 1 | / | / | 1 | / | |
停止 | 0 | 0 | / | / | 1 | / | |
M2 | 正转 | / | / | 1 | 0 | / | 1 |
反转 | / | / | 0 | 1 | / | 1 | |
停止 | / | / | 0 | 0 | / | 1 |
2.红外循迹模块
本次用到的红外循迹模块的传感器为TCRT5000,检测反射距离为1mm~25mm,可转动突出的正方形中间的电位器调节灵敏度,输出形式为0 和 1。
引脚
- VCC:3.3V~5V(接单片机的VCC / 3.3V / 5V)
- GND:接地(接单片机GND)
- DO:TTL电平输出(接单片机IO口)
- AO:模拟信号输出(此引脚可不接)
工作原理
- TCRT5000是一种红外线传感器,它可以检测物体是否在其检测范围内。它的工作原理是利用红外线的反射来检测物体的存在。
- TCRT5000由发射管和接收管组成。发射管发射出红外线,接收管接收反射回来的红外线。
- 当有物体进入检测范围时,它会反射红外线,接收管会接收到反射回来的红外线信号。这个信号会被放大并转换成数字信号,然后被传输到控制器或处理器中进行处理。
由于黑色是不反射红外线的,故传感器检测到黑线时,输出高电平,开关指示灯亮(传感器接电后电源指示灯常亮)。
当调试时发现小车经过黑线而循迹模块开关指示灯不亮时可调节循迹模块离地面的距离或调节传感器灵敏度。
3.超声波测距模块
此模块用到了 HC-SR04 ,HC-SR04超声波测距模块是一种基于超声波测距原理的传感器,可以通过发送超声波脉冲并接收其回波来计算目标与传感器之间的距离。测距范围为2cm~400cm,精度为3mm,工作电压为5V,工作电流为15mA。
HC-SR04 完整规格:
工作电压 | 直流5V |
---|---|
工作电流 | 15mA |
运行频率 | 40KHz |
最大范围 | 4m |
最小范围 | 2cm |
测距精度 | 3mm |
测量角度 | 15° |
触发输入信号 | 10µS TTL脉冲 |
尺寸 | 45 x 20 x 15mm |
引脚
- VCC:电源线(接单片机5V)
- GND:接地(接单片机GND)
- Trig:触发引脚,触发测距(接单片机IO口)
- Echo:回波引脚,传回时间差(接单片机IO口)
超声波时序图
工作原理:首先给Trig引脚提供一个10us以上(可20us)的高电平触发脉冲(放在中断函数里) –> 模块内部将发射8个40KHz的脉冲并检测回波 –> 如果Echo引脚为高电平,启动定时器 –> 当Echo引脚为低电平时,关闭定时器 –> 计算计时器时间间隔 –> 根据公式计算距离
PS:计算距离公式:s = t * 0.034 / 2 (单位为厘米)
三、代码部分(仅供参考)
#include <REGX52.H>
#include <intrins.h>
sbit IN1=P1^0; //单片机的IO口
sbit IN2=P1^1;
sbit IN3=P1^3;
sbit IN4=P1^2;
sbit D1=P1^4;
sbit D2=P1^5;
sbit D3=P1^6;
sbit D4=P1^7;
sbit ENA=P2^5; //l298n左使能端口
sbit ENB=P2^4; //l298n右使能端口
sbit Trig=P2^0;
sbit Echo=P2^1;
//pwm:脉冲宽度调制
//占空比:一个脉冲周期内,高电平时间/整个周期时间
unsigned int PWMR=0,PWML=0,pwm_t=0;
//右轮占空比
//左轮占空比
//设定的比较值
/************************超声波模块****************************/
unsigned long distance;
unsigned long s;
unsigned int t=0;
unsigned int counter=0;
bit flag=0; //中断溢出标志
/*bit:标志位,只能0/1
/************************数码管显示****************************/
unsigned char NixieTable[]={0X3F,0X06,0X5B,0X4F,0X66,0X6D,0X7D,0X07,0X7F,0X6F};
//定义数码管显示码0、1、2、3、4、5、6、7、8、9
unsigned char dis_LED[4]={0,0,0,0};
void Timer0_Init(void) //500us@11.0592MHz
{
TMOD = 0x11; //定时器0和定时器1工作方式为1
TL1 = 0xf8; //定时2ms
TH1 = 0x30;
ET0=1; //允许T0中断
ET1 = 1; //允许T1中断
TR1 = 1; //打开定时器1
EA = 1; //打开总中断
TMOD |= 0x11; //设置定时器0和1模式1
TL0 = (65536-100)%256; //设置定时初始值
TH0 = (65536-100)/256;
TR0 = 1; //定时器0开始计时
ET0 = 1;
EA = 1;
PT0=1;
}
void Timer2_Isr(void) interrupt 5
{
TF2 = 0; //清除TF2标志
flag=1; //溢出标志
}
void Timer2_Init(void) //2毫秒@12.000MHz
{
T2MOD = 0; //清零T2MOD寄存器
T2CON = 0; //清零T2CON寄存器
//设置定时初始值
TL2 = 0; //清零TL2寄存器
TH2 = 0; //清零TH2寄存器
RCAP2L = 0x30; //设置RCAP2L寄存器为0x30
RCAP2H = 0xF8; //设置RCAP2H寄存器为0xF8
TR2 = 1; //启动定时器2
ET2 = 1; //允许定时器2中断
}
/************************延时函数***********************/
void Delayms(unsigned int xms)
{
while(xms--){
unsigned char i, j;
i = 2;
j = 239;
do
{
while (--j);
} while (--i);
}
}
/************************延时20us***********************/
void Delay20us() //@11.0592MHz
{
unsigned char i;
_nop_();
i = 6;
while (--i);
}
void RightMotoForward() //右轮向前
{
IN3=1;
IN4=0;}
void RightMotoBack() //右轮向后
{
IN3=0;
IN4=1;}
void LeftMotoBack() //左轮向后
{
IN1=0;
IN2=1;}
void LeftMotoForward() //左轮向前
{
IN1=1;
IN2=0;}
void RightMotoStop() //右轮停下
{
IN3=0;
IN4=0;}
void LeftMotoStop() //左轮停下
{
IN1=0;
IN2=0;}
void CarGo() //左右轮都向前
{RightMotoForward();LeftMotoForward();}
void CarBack() //左右轮都向后
{RightMotoBack();LeftMotoBack();}
void CarRight() //向右转
{RightMotoStop();LeftMotoForward();}
void CarLeft() //向左转
{LeftMotoStop();RightMotoForward();}
void CarStop() //停下
{RightMotoStop();LeftMotoStop();}
void Track() // 灭1黑 亮0 检测到黑线为灭1
{
if(D1==0&&D2==0&&D3==0&&D4==0) //小车停下 未检测到黑线
{
Delayms(50);
if(D1==0&&D2==0&&D3==0&&D4==0) //小车停下 未检测到黑线
{
PWML=30; //左电机初始转速
PWMR=30; //右电机初始转速
CarGo();
Delayms(10);
}
CarStop();
}
if(D2==1&&D3==1) {
//小车两边都感应到黑线 小车直行 全灭都为1
//Delayms(100);
if(D2==1&&D3==1) {
PWML=30; //左电机初始转速
PWMR=30; //右电机初始转速
CarGo();
}
Delayms(10);
}
if(D1==0&&D2==0&&D3==1&&D4==0) //小车右边扫描到黑线 小车偏左 向右移动
{
PWML=30; //左电机初始转速
PWMR=30; //右电机初始转速
CarRight();
Delayms(10);
if(D1==0&&D2==0&&D3==0&&D4==0){
PWML=30;
PWMR=30;
CarGo();
Delayms(10);
if(D1==0&&D2==0&&D3==0&&D4==0){
PWML=30; //左电机初始转速
PWMR=35; //右电机初始转速
CarRight();
Delayms(10);
}
}
}
if(D1==0&&D2==1&&D3==0&&D4==0) //小车左边扫描到黑线 小车偏右 小车应该向左移动
{
PWML=35; //左电机初始转速
PWMR=30; //右电机初始转速
CarLeft();
Delayms(10);
if(D1==0&&D2==0&&D3==0&&D4==0){
PWML=30;
PWMR=30;
CarGo();
Delayms(10);
if(D1==0&&D2==0&&D3==0&&D4==0){
PWML=35; //左电机初始转速
PWMR=30; //右电机初始转速
CarLeft();
Delayms(10);
}
}
}
if(D1==0&&D2==0&&D3==0&&D4==1){
PWML=30; //左电机初始转速
PWMR=35; //右电机初始转速
CarRight();
Delayms(50);
if(D1==0&&D2==0&&D3==0&&D4==0){
PWML=30;
PWMR=30;
CarGo();
Delayms(10);
if(D1==0&&D2==0&&D3==0&&D4==0){
PWML=30; //左电机初始转速
PWMR=35; //右电机初始转速
CarRight();
Delayms(10);
}
}
}
if(D1==1&&D2==0&&D3==0&&D4==0){
PWML=35; //左电机初始转速
PWMR=30; //右电机初始转速
CarLeft();
Delayms(50);
if(D1==0&&D2==0&&D3==0&&D4==0){
PWML=30;
PWMR=30;
CarGo();
Delayms(10);
if(D1==0&&D2==0&&D3==0&&D4==0){
PWML=35; //左电机初始转速
PWMR=30; //右电机初始转速
CarLeft();
Delayms(10);
}
}
}
if((D1==1&&D2==1&&D3==0&&D4==0) || (D1==1&&D2==1&&D3==1&&D4==0)) //直角左拐
{
PWML=20; //左电机初始转速
PWMR=20; //右电机初始转速
CarGo();
Delayms(100);
if(D1==0&&D2==0&&D3==0&&D4==0){
CarStop();
Delayms(100);
PWML=30; //左电机初始转速
PWMR=25; //右电机初始转速
CarLeft(); //原地左拐
Delayms(100);
}
}
if((D1==0&&D2==0&&D3==1&&D4==1) || (D1==0&&D2==1&&D3==1&&D4==1)) //直角右拐
{
PWML=20; //左电机初始转速
PWMR=20; //右电机初始转速
CarGo();
Delayms(100);
if(D1==0&&D2==0&&D3==0&&D4==0){
CarStop();
Delayms(100);
PWML=25; //左电机初始转速
PWMR=30; //右电机初始转速
CarRight(); //原地右拐
Delayms(100);
}
}
// if((D1==1&&D2==0&&D3==0&&D4==0)||(D1==1&&D2==0&&D3==1&&D4==0)||(D1==1&&D2==1&&D3==1&&D4==0)||(D1==1&&D2==1&&D3==0&&D4==0)) //锐角左拐
// {
// PWML=10; //左电机初始转速
// PWMR=10; //右电机初始转速
// CarGo();
// Delayms(2000);
// if(D1==0&&D2==0&&D3==0&&D4==0){
// CarStop();
// Delayms(2000);
// PWML=5; //左电机初始转速
// PWMR=0; //右电机初始转速
// CarLeft();
// }
// Delayms(2000);
// }
//
// if((D1==0&&D2==0&&D3==0&&D4==1)||(D1==0&&D2==1&&D3==0&&D4==1)||(D1==0&&D2==1&&D3==1&&D4==1)||(D1==0&&D2==0&&D3==1&&D4==1)) //锐角左拐
// {
// PWML=15; //左电机初始转速
// PWMR=15; //右电机初始转速
// Cargo();
// Delayms(2000);
// if(D1==0&&D2==0&&D3==0&&D4==0){
// CarStop();
// Delayms(2000);
// PWML=5; //左电机初始转速
// PWMR=15; //右电机初始转速
// CarRight();
// }
// Delayms(2000);
// }
//
//
// if(D1==0&&D2==1&&D3==1&&D4==0){
// if(D1==1){
// CarLeft();
// }
// if(D4==1){
// CarRight();
// }
// }
}
void chaoshengbo(){
//定义超声波避障最小距离
while(!Echo);
TR2=1;
while(Echo);
TR2=0;
distance=200; //20cm
t=TH2*256+TL2;
TH2=0;
TL2=0;
s=(long)(t*0.17); //算出为毫米mm
if(s<=(long)distance){
CarStop();
}
if((s>=4000) || (flag==1)) //超出测距范围显示“----”
{
flag=0;
dis_LED[0]=0X40;
dis_LED[1]=0X40;
dis_LED[2]=0X40;
dis_LED[3]=0X40;
}else{
dis_LED[0]=NixieTable[s%10/1]; //个位
dis_LED[1]=NixieTable[s%100/10]; //十位
dis_LED[2]=NixieTable[s%1000/100]; //百位
dis_LED[3]=NixieTable[s%10000/1000]; //千位
}
}
void Nixie(){
unsigned int i;
unsigned int j;
for(i=0;i<4;i++){
switch(i){
case 0:P2_4=0;P2_3=0;P2_2=0;break; //LED1亮
case 1:P2_4=0;P2_3=0;P2_2=1;break; //LED2亮
case 2:P2_4=0;P2_3=1;P2_2=0;break; //LED3亮
case 3:P2_4=0;P2_3=1;P2_2=1;break; //LED4亮
}
if(i==1){
P0=dis_LED[i]+0x80; //加小数点
}else{
P0=dis_LED[i];
}
j=10; //扫描间隔时间设定
while(j--);
P0=0x00; //消隐
}
}
void Timer0_Isr(void) interrupt 1 //T0中断用来计数器溢出 超出测距范围
{
//重新赋初值
TH0=(64536-100)/256;
//TH0 = 0xFF;
TL0=(64536-100)%256+1; //有1微秒误差
//TL0 = 0xA4;
pwm_t++;
if(pwm_t<PWML){
ENA=1;
}
else{
ENA=0; //电机停止
}
if(pwm_t<PWMR){
ENB=1;
}
else{
ENB=0; //电机停止
}
if(pwm_t>=100) //一个周期
{
pwm_t=0;
}
}
void Timer1_Isr(void) interrupt 3 //T1中断扫描数码管和计200ms启动模块
{
TH1=0XF8; //定时2ms
TL1=0X30;
Nixie();
counter++;
if(counter>=100){
counter=0;
Trig=1;
Delay20us();
Trig=0;
}
}
void main()
{
//定时器初始化
Timer0_Init();
Timer2_Init();
while(1)
{
CarGo();
Track(); // 循迹函数
chaoshengbo(); //超声波函数
}
}