基于51单片机的红外寻迹小车(设计思路+注意事项+源代码)

基于51单片机的四路驱动红外寻迹小车

文章目录

基于51单片机的四路驱动红外寻迹小车@[TOC]

前言一、原理及设计思路1.红外寻迹小车的基本原理:2.单片机处理数据3.红外探头布局及调整

二、硬件1.芯片2.驱动3.烧录工具4.比较器5.其他

二、烧录工具及编译工具环境搭建1.烧录工具2.编译环境搭建

三、电路1.探头2.比较器

四、程序设计1.PWM调速2.循迹情况的判断

总结

前言

本人本科为化院生,该项目仅为爱好,如有不妥处,欢迎所有指导与批评。 该站已有大部分关于该项目的文章,本文主要目的在于分享自己在制作过程中遇到的问题与如何处理以及进行整合,如有冒犯请联系删除。

不能保证该文章可以帮您完成比赛,但是按照步骤做一个可循迹简单路线的小车没有问题。

一、原理及设计思路

1.红外寻迹小车的基本原理:

通过红外探头(由发射管和接收管组成,如st188、tcrt5000)实现对路线的判断,因为黑线对白线对红外外线的吸收程度不同,反射到接收管的红外光强度不同,从而产生不同的电压,达到粗略识别“路线”的效果。每个传感器最佳识别距离在其参数手册中均有标注,可使用 半导小芯 查询

2.单片机处理数据

由于赛道只有黑白色,可对应至单片机的高低电平处理(单片机低电平范围为0-0.3vcc,高电平为0.7vcc-vcc),由此需要使用到比较器。

3.红外探头布局及调整

依据不同的赛道,红外探头布局有不同,主体还是依据赛道调整,不一定使用线性布局,条件允许3*3布局也是不错的选择(此时须注意靠近车身的探头由于遮挡,电压值可能与外围相差较大);注意探头高度,过高可能探头间存在相互干扰,过低可能无法接收到反射回来的红外光,以实验为准。

二、硬件

1.芯片

该项目使用stc89c52rc作为控制芯片,淘宝有售,价格在3-4元不等。搭配最小系统板使用(pdd价格6元)

2.驱动

本人使用的是l298n四路驱动,价格为28,有点贵(下图)。可用两片l298n双路驱动替代。但四路驱动提供5v片内取电,可直接为单片机供电,可有效简化车身用线。 附接线图

3.烧录工具

可使用usb-ttl烧录器,接线方式为烧录器上的vss与3v3用跳线帽短接,txd接单片机rxd接口,rxd接口接单片机txd接口。需要冷启动烧录。 TTL烧录器 USB烧录器,据说可以免手动冷启动烧录但我不会,还望指教

4.比较器

当红外探头的电压值位于0.3vcc-07vcc之间,此时会由于单片机识别不准确二误判,可使用比较器改善。使用方法为给定一个判断阈值,当探头的OUT口低于该阈值则输出0V,反之则为高电平(需要用到上拉电阻)。

5.其他

电机在这里考虑价格,新手可使用小黄电机(一般买车架的时候会配套)。线使用杜邦线,母对母和母对公的都需要。 面包板可买可不买,可以用来做预实验以检验想法是否正确。

二、烧录工具及编译工具环境搭建

1.烧录工具

烧录软件可使用stc-isp,需要安装驱动(CH341SER.exe,已在链接中)。

2.编译环境搭建

51的程序使用c,与之配套的是keil5.但是keil5编程时不太好用(个人觉得),可以转用VSC编译,详细见大佬的教程

三、电路

1.探头

2.比较器

注意上拉电阻,建议换成可变电阻;同样,比较器的阈值输入端也建议使用可变电阻

四、程序设计

1.单片机的中断系统 中断的概念: CPU在正常执行程序的过程中,由于某种已经预见的外部的或CPU内部事件的发生,使CPU暂停执行当前的程序,而去处理临时发生的事件,在事件处理完毕后,在返回原先暂停的程序继续向下执行,这个过程就叫中断(来自《单片机技术》) 中断的模式

interrupt0 表示 外部中断0 interrupt1 表示 定时器中断0 interrupt2 表示 外部中断1 interrupt3 表示 定时器中断1 interrupt4 表示 串口中断

1.PWM调速

通俗的讲PWM就是将输出的变化的电压-时间图像转变为恒定的电压-时间图像 得到类似方波的电压图像 这其中会涉及到“占空比的概念”(是一个脉冲周期内,高电平的时间与整个周期时间的比例),在实际应用中,即占空比越高,对应的电压值越大 四路驱动需要设定4个电压调制,两路驱动则对应减去即可,代码如下:

void T0isp() interrupt 1

{

unsigned char i,j,k,m;

TL0 = (91036-40)%256;//设置定时初始值

TH0 = (91036-40)/256;//设置定时初始值

i++;//变量i用于控制pwm1占空比

j++;//变量j用于控制pwm2占空比

k++;

m++;

if(i < pwm1)//pwm1值以下,输出高电平

EN1 = 1;

else EN1 = 0;

//if(i >= 40)

if(i > 100)

i = 0;

if(j < pwm2)

EN2 = 1;

else EN2 = 0;

if(j > 100)

j = 0;

if(k < pwm3)

EN3 = 1;

else EN3 = 0;

if(k > 100)

k = 0;

if(m < pwm4)

EN4 = 1;

else EN4 = 0;

if(m > 100)

m = 0;

}

2.循迹情况的判断

我使用的是4路传感器,如下

011 101

布局(1代表探头)。对所有的基础红外寻迹小车而言,把每种情况考虑到是很重要的,如果存在某种你没考虑到的情况,而实际测试中小车又扫到了,大概率是会出问题的。 附上我的代码(仅供参考):

/*循迹情况分析 白线记为0;黑线记为1 标注为中1为白 0为黑*/

void xunji()

{

unsigned char a;

if(mid1==0 &&left2==0 &&right3==0 &&right4==0) //1111 all write

a = 4;

else if(mid1==1 &&left2==1 &&right3==1 &&right4==1) //0000 all black

a = 0;

else if(mid1==1 &&left2==0 &&right3==0 &&right4==0) //0111 straight

a = 0;

else if (mid1==0 &&left2==1 &&right3==0 &&right4==0) //1011 corrent_left

a = 1;

else if(mid1==0 &&left2==0 &&right3==1 &&right4==0) //1101 corrent_right

a = 2;

else if(mid1==0 &&left2==0 &&right3==0 &&right4==1) //1110 straight

a = 0;

else if(mid1==1 &&left2==1 &&right3==0 &&right4==0) //0011 left_corrent

a = 1;

else if(mid1==1 &&left2==0 &&right3==1 &&right4==0) //0101 right_corrent

a = 2;

else if(mid1==1 &&left2==0 &&right3==0 &&right4==1) //0110 straight

a = 0;

else if(mid1==0 &&left2==0 &&right3==1 &&right4==1) //1100 right_corrent

a = 3;

else if(mid1==0 &&left2==1 &&right3==0 &&right4==1) //1010 right_corrent

a = 4;

else if(mid1==0 &&left2==1 &&right3==1 &&right4==0) //1001 straight

a = 0;

else if(mid1==0 &&left2==1 &&right3==1 &&right4==1) //1000 turnright

a = 4;

else if(mid1==1 &&left2==0 &&right3==1 &&right4==1) //0100 straight

a = 0;

else if(mid1==1 &&left2==0 &&right3==1 &&right4==1) //0010 turnright

a = 4;

else if(mid1==1 &&left2==1 &&right3==1 &&right4==0) //0001 straight

a = 0;

switch (a)

{

case 0:straight();break;

case 1:turnleft();break;

case 2:turnright();break;

case 3:turnleft2();break;

case 4:turnright2();break;

default:break;

}

}

可以说这种简单循迹小车的核心就在循迹情况的分析。存在的情况总数n

n=2^x(x为探头数量)

总结

末尾附上源码:

#include //通用51头文件

//电机驱动IO口

sbit IN1=P2^7;

sbit IN2=P3^3;

sbit IN3=P3^4;

sbit IN4=P2^6;

sbit IN5=P3^5;

sbit IN6=P2^5;

sbit IN7=P2^4;

sbit IN8=P3^6;

//驱动使能端 L298N四路

sbit EN1=P2^3;

sbit EN2=P2^2;

sbit EN3=P2^1;

sbit EN4=P2^0;

//寻迹口五组红外对管

sbit mid1=P1^0;

sbit left2=P1^1;

sbit right3=P1^2;

sbit right4=P1^3;

unsigned char pwm1,pwm2,pwm3,pwm4;

void xunji();//循迹函数

//void turnleft();//左转函数(纠正)

void turnleft2();//左转函数2:大角度转弯

//void turnright();//右转函数(纠正)

void turnright2();//右转函数2:大角度转弯

void straight(); //直行函数

void stop();//停车函数

void Timer0Init(void)

{

TMOD |= 0x01;//设置定时器模式

TL0 = (91036-40)%256;//设置定时初始值

TH0 = (91036-40)/256;//设置定时初始值

TR0 = 1;//定时器0开始计时

ET0 = 1;//定时器0中断使能

EA = 1;//中断使能

}

void main()

{

unsigned char b,n;

b = 0;

n = 0;

Timer0Init();//定时器0初始化

while(1)

{

xunji();//循环执行循迹函数

b = xunji;

if(b==0){n++;}

if(n>=26){stop();}

}

}

void T0isp() interrupt 1

{

unsigned char i,j,k,m;

TL0 = (91036-40)%256;//设置定时初始值

TH0 = (91036-40)/256;//设置定时初始值

i++;//变量i用于控制pwm1占空比

j++;//变量j用于控制pwm2占空比

k++;

m++;

if(i < pwm1)//pwm1值以下,输出高电平

EN1 = 1;

else EN1 = 0;

//if(i >= 40)

if(i > 100)

i = 0;

if(j < pwm2)

EN2 = 1;

else EN2 = 0;

if(j > 100)

j = 0;

if(k < pwm3)

EN3 = 1;

else EN3 = 0;

if(k > 100)

k = 0;

if(m < pwm4)

EN4 = 1;

else EN4 = 0;

if(m > 100)

m = 0;

}

/*pwm调速

pwm1左前(1) pwm3右前(6)

pwm2左后(4) pwm4右后(7)

*/

void straight() //直行

{

IN1 = 1;IN2 = 0; IN5 = 0;IN6 = 1;

IN3 = 0;IN4 = 1; IN7 = 1;IN8 = 0;

pwm1=95;pwm3=95;

pwm2=95;pwm4=95;

}

void turnleft()//向左纠正

{

IN1 = 0;IN2 = 1; IN5 = 0;IN6 = 1;

IN3 = 1;IN4 = 0; IN7 = 1;IN8 = 0;

pwm1=62;pwm3=62;

pwm2=62;pwm4=62;

}

void turnleft2()//左转

{

IN1 = 0;IN2 = 1; IN5 = 0;IN6 = 1;

IN3 = 1;IN4 = 0; IN7 = 1;IN8 = 0;

pwm1=87;pwm3=87;

pwm2=87;pwm4=87;

}

void turnright()//向右纠正

{

IN1 = 1;IN2 = 0; IN5 = 1;IN6 = 0;

IN3 = 0;IN4 = 1; IN7 = 0;IN8 = 1;

pwm1=62;pwm3=62;

pwm2=62;pwm4=62;

}

void turnright2()//右转

{

IN1 = 1;IN2 = 0; IN5 = 1;IN6 = 0;

IN3 = 0;IN4 = 1; IN7 = 0;IN8 = 1;

pwm1=87;pwm3=87;

pwm2=87;pwm4=87;

}

void stop() //驻车

{

IN1 = 0;IN2 = 0; IN5 = 0;IN6 = 0;

IN3 = 0;IN4 = 0; IN7 = 0;IN8 = 0;

pwm1=0;pwm3=0;

pwm2=0;pwm4=0;

}

/*循迹情况分析 白线记为0;黑线记为1 标注为中1为白 0为黑*/

void xunji()

{

unsigned char a;

if(mid1==0 &&left2==0 &&right3==0 &&right4==0) //1111 all write

a = 4;

else if(mid1==1 &&left2==1 &&right3==1 &&right4==1) //0000 all black

a = 0;

else if(mid1==1 &&left2==0 &&right3==0 &&right4==0) //0111 straight

a = 0;

else if (mid1==0 &&left2==1 &&right3==0 &&right4==0) //1011 corrent_left

a = 1;

else if(mid1==0 &&left2==0 &&right3==1 &&right4==0) //1101 corrent_right

a = 2;

else if(mid1==0 &&left2==0 &&right3==0 &&right4==1) //1110 straight

a = 0;

else if(mid1==1 &&left2==1 &&right3==0 &&right4==0) //0011 left_corrent

a = 1;

else if(mid1==1 &&left2==0 &&right3==1 &&right4==0) //0101 right_corrent

a = 2;

else if(mid1==1 &&left2==0 &&right3==0 &&right4==1) //0110 straight

a = 0;

else if(mid1==0 &&left2==0 &&right3==1 &&right4==1) //1100 right_corrent

a = 3;

else if(mid1==0 &&left2==1 &&right3==0 &&right4==1) //1010 right_corrent

a = 4;

else if(mid1==0 &&left2==1 &&right3==1 &&right4==0) //1001 straight

a = 0;

else if(mid1==0 &&left2==1 &&right3==1 &&right4==1) //1000 turnright

a = 4;

else if(mid1==1 &&left2==0 &&right3==1 &&right4==1) //0100 straight

a = 0;

else if(mid1==1 &&left2==0 &&right3==1 &&right4==1) //0010 turnright

a = 4;

else if(mid1==1 &&left2==1 &&right3==1 &&right4==0) //0001 straight

a = 0;

switch (a)

{

case 0:straight();break;

case 1:turnleft();break;

case 2:turnright();break;

case 3:turnleft2();break;

case 4:turnright2();break;

default:break;

}

}

其中停车函数存在问题,还望大佬指点

写本文章的目的仅是为了帮助正在做或想做该项目的人提供一点帮助,同时记录一下自己过去一个月所做的事。大部分知识详解来自本站大佬。祝你诸事顺利吖~~