当前位置:文档之家› 超声波避障小车

超声波避障小车

《单片机课程设计》设计报告设计课题:超声波避障小车专业班级:电子信息工程121班学生姓名:范东耀指导教师:蔡岗设计时间: 2015年7月8日赣南师范学院科技学院数学与信息科学系超声波避障小车一、设计任务与要求1.设计任务:1、采用超声波模块实现小车自动避障功能。

2、用LCD1602显示当前的障碍距离。

2.扩展部分:测出当前小车的行驶速度,并用LCD1602显示当前速度。

二、方案设计与论证1设计方案系统采用51单片机作为核心控制单元用于智能车系统的控制,驱动板则以L289N驱动芯片为核心,应用超声波模块及光电传感器和LCD液晶模块,成功的实现了小车的避障、测速和显示功能这三大功能。

在超声波检测到障碍物之后,主控芯片根据距离值控制电机的转动,在与障碍物距离较大的情况下,快速前进,在与障碍物距离较小但未到达临界转弯方向值得时候,慢速前进。

在与障碍物距离较近时,小车转弯,在与障碍物很近时,小车后退转弯,来进行避障。

测速传感器为光电测速传感器,在单位时间内计算脉冲的次数,然后再进行转换和处理即得到所测量的速度。

通过软件pwm进行调速。

通过LCD1602显示障碍距离及当前的小车行驶速度。

2 原理框图简要原理框图如图1所示。

图1 系统原理框图三、电路设计1 电路设计(1)超声波测距模块:超声波测距的原理是首先利用单片机输出一个40kHz的触发信号,把触发信号通过TRIG管脚输入到超声波测距模块,再由超声波测距模块的发射器向某一方向发射超声波,在发射时刻的同时单片机通过软件开始计时,超声波在空气中传播,途中碰到障碍物返回,超声波测距模块的接收器收到反射波后通过产生一个回应信号并通过ECHO脚反馈给单片机,此时单片机就立即停止计时。

时序图如图1所示。

由于超声波在空气中的传播速度为340m/s,根据计时器记录的时间t,就可以计算出发射点距障碍物的距离,即:S=VT/2,通过单片机来算出距离。

超声波测距原理图如图2所示。

图2 超声波测距原理(2)显示模块:系统采用LCD1602显示,它不仅节省了单片机的资源,相比较数码管液晶显示更加直观、节能,同时可以直接显示字母、数字、符号等,具有灵活易操作的特性。

故采用LCD显示。

引脚功能说明:LCD1602采用标准的16脚(带背光)接口,各引脚接口说明如表1所示:表1 LCD1602引脚说明(3)电机驱动模块:本系统采用由双极性管组成的H桥电路(L298N)。

用单片机控制晶体管使之工作在占空比可调的开关状态,精确调整电机转速。

这种电路由于工作在管子的饱和截止模式下,则效率非常高;H桥电路保证了可以简单地实现转速和方向的控制,电子开关的速度很快,稳定性也很高。

而且它有更强的驱动能力。

L298N 有过电流保护功能,当出现电机卡死时,可以保护电路和电机等。

驱动电机的运行,I/O端口状态与电机制动对照表,如下表2所示。

表2 I/O端口状态与电机制动对照表(4)光耦传感器光耦传感器原理是传感器开孔圆盘的转轴与减速电机转轴相连,光源的光通过开孔盘的孔和缝隙反射到光敏元件上,开孔盘旋转体转一周,光敏元件上照到光的次数等于盘上的开孔数,从而测出旋转体旋转速度。

灵敏度较高,但容易受外界光源的影响。

虽然光电传感器受外界光源影响很大,但是它使用方便、安装简单,还有本设计要求的准确度不是很高,因此就选择了光耦测速传感器。

光耦测速传感器的原理图如图3所示。

图3 光耦测速传感器原理图2 主要性能参数计算当单片机的给一个20us的触发信号给超声波模块时,TRIG由低电平转换为高电平,TRIG=1,单片机开时计时,开启中断,并记录时间为T1,接收换能器等待接收回波,ECHO持续为高电平的时间为发射时间。

换能器接收回波将超声波转换为电信号,送至单片机,记录时间为T2。

超声波发射的时间为:T2-T1,C 为声速,计算发射距离为: L = (T2-T1)*C/2 。

速度的计算是通过小车轮胎的周长除以测码盘转过孔的个数,得出一个码盘孔对应的长度为1CM,通过定时器取1秒时间,1秒转过的码盘孔个数就是速度。

3程序流程图本设计系统软件由主程序﹑定时子程序、电机驱动子程序﹑中断子程序、显示子程序﹑算法子程序构成。

主程序流程图如图4所示。

图4主程序流程图四、电路制作及调试1 实物图通过以上步骤,制作出实物图。

正面图如图5所示,反面图如图6所示。

图5 实物图正面图6 实物图反面2 电路调试测试在小车的调试的过程中,碰到的最大的问题就是程序有时会跑飞,起初我觉得出现问题的原因是有以下三点,第一,程序的编写过于繁杂,数据处理和逻辑判断太多,芯片处理不过来,第二,数据经过处理后,传送出去未来得及经过处理就已经改变了值,导致芯片来不及处理而跑飞,第三,数据的类型没有定义好,导致数据的值超过定义的类型的极限。

经过几天的程序调试,我简化了程序的编写,在重要数据的传送过程中加入了延时函数,保证数据有充足的传送时间,并把各种参数定义的类型检查了几遍,结果并没有解决程序跑飞的问题。

最后我发现问题出在电源上,由于小车负载较多,电源带载能力有限,导致芯片受到干扰而跑飞,我将耗电能力较强的直流电机的电源断开,电源只给芯片和其他模块供电,程序能够良好的运行。

最后我通过在程序中加入“看门狗”的程序,判断程序是否跑飞,若程序跑飞,将软件复位。

能有效的解决程序跑飞的问题。

在做测速模块时,发现单片机的资源不够用,单片机只有两个定时器,我在做超声波测距时已经用了一个定时器的计数模式做测距,小车的PWM调速用了一个定时器。

而做测速还需要两个定时器,一个定时器做计数,计码盘转的圈数,一个定时器定1秒的时间,从而可以算出当前的小车速度。

我通过学习,了解了定时器复用,可以从PWM中的1MS定时做处理,取出1S的时间,用外部中断计码盘的圈数,再通过数据处理,可以得出小车的速度。

3 元件清单元件清单如表3所示。

表3 元件清单五参考文献[1]潘永雄.沙河.电子线路CAD实用教程(第四版)[M].西安电子科技大学出版社,2012.[2]高吉祥.电子技术基础实验与课程设计(第二版)[M].电子工业出版社,2005.[3]王港元.电子设计制作基础[M].江西科学技术出版社,2011.[4]彭介华.电子技术课程设计指导[M].高等教育出版社,2009.[5]陈伯时.电力拖动自动控制系统[M].机械工业出版社,2012.赣南师范学院科技学院2014-2015学年第二学期期末考试《单片机课程设计》成绩评定表专业:电子信息工程班级:121班学号: 1220085110 姓名:范东耀#include <AT89x51.H>#include <intrins.h>#define uchar8 unsigned char#define uint16 unsigned int#define pwm_left 70 //设置左右两边的PWM都为45sfr WDT_CONTR = 0xE1; //用sfr定义看门狗特殊功能寄存器#define RX P3_1 //收#define TX P3_0 //发#define LCM_RW P2_5 //定义LCD引脚#define LCM_RS P2_6#define LCM_E P2_7#define LCM_Data P0#define Busy 0x80 //0124用于检测LCM状态字中的Busy标识sbit tracking_right1 = P1 ^ 4; //右边电机sbit tracking_right2 = P1 ^ 5;sbit tracking_left1 = P1 ^ 2; //左边电机sbit tracking_left2 = P1 ^ 3;sbit pwm2 = P1 ^ 7;uint16 count1=0,count2=0,F=0;uint16 tmp=0;void LCMInit(void);void DisplayOneChar(unsigned char X, unsigned char Y, unsigned char DData);void DisplayListChar(unsigned char X, unsigned char Y, unsigned char code *DData);void Delay5Ms(void);void Delay400Ms(void);void Decode(unsigned char ScanCode);void WriteDataLCM(unsigned char WDLCM);void WriteCommandLCM(unsigned char WCLCM,BuysC);unsigned char ReadDataLCM(void);unsigned char ReadStatusLCM(void);unsigned char code email[] ={"juli"};unsigned char code ASCII[20] ={'0','1','2','3','4','5','6','7','8','9','.','-','M','m','/','s','S', '=','V','c'};unsigned char code A[] = {"sudu"};static unsigned char DisNum = 0; //显示用指针unsigned int time=0;unsigned long S=0;bit flag =0;unsigned char disbuff[4] ={ 0,0,0,0,}; //超声波距离值赋值进来 unsigned char disbuffsudu[4] ={ 1,2,3,4,};//速度值赋值进来void delayms(uint16 c){uint16 a,b;for(a=0;a<c;a++)for(b=0;b<113;b++);}void turn_left() //左转函数{tracking_right1 = 1;tracking_right2 = 0;tracking_left1 = 0;tracking_left2 = 0;}void big_left() //大左转函数{tracking_right1 = 1;tracking_right2 = 0;tracking_left1 = 0;tracking_left2 = 1;}void turn_right() //右转函数{tracking_right1 = 0;tracking_right2 = 0;tracking_left1 = 1;tracking_left2 = 0;}void go_straight() //直走函数{tracking_right1 = 1;tracking_right2 = 0;tracking_left1 = 1;tracking_left2 = 0;}void right_back()//右后退函数{tracking_right1 = 0;tracking_right2 = 1;tracking_left1 = 0;tracking_left2 = 0;}void left_back()//左后退函数{tracking_right1 = 0;tracking_right2 = 0;tracking_left1 = 0;tracking_left2 = 1;}void tracking(S) //避障函数{if(S<=15){right_back();delayms(10);}else if((S>15)&&(S<30)){turn_right();delayms(10);}else{go_straight();delayms(10);}}void Timer1Init(void) //pwm{TMOD &= 0x0F; //设置定时器模式TMOD |= 0x10; //设置定时器模式TL1 = 0x66; //设置定时初值TH1 = 0xFC; //设置定时初值,定时1MS TF1 = 0; //清除TF0标志TR1 = 1; //定时器0开始计时EA=1; //开总中断ET1=1; //开定时器1中断}void serve_T1() interrupt 3 //pwm{TL1 = 0x66; //设置定时初值TH1 = 0xFC; //设置定时初值count1++;if(++count2<=(pwm_left)){pwm2=1;}else if(count2<=100){pwm2=0;}else count2=0;if(1000==count1) //1s的定时{count1=0; //定时标志位清零,为下次做准备F=tmp; //F为每秒记录下来的脉冲,tmp为外部中断的个数,读取计数数据delayms(10);tmp=0;delayms(5);}}void counter(void) interrupt 0{EX0=0;tmp++; //外部中断计数delayms(10);EX0=1;}void cesu(F){disbuffsudu[1]=F/10%10; //取十位disbuffsudu[2]=F%10; //取个位DisplayOneChar(8, 1, ASCII[disbuffsudu[1]]);DisplayOneChar(9, 1, ASCII[disbuffsudu[2]]);DisplayOneChar(10, 1, ASCII[19]); //显示cDisplayOneChar(11, 1, ASCII[13]); //显示mDisplayOneChar(12, 1, ASCII[14]); //显示/DisplayOneChar(13, 1, ASCII[15]); //显示s}//写数据void WriteDataLCM(unsigned char WDLCM){ReadStatusLCM(); //检测忙LCM_Data = WDLCM;LCM_RS = 1;LCM_RW = 0;LCM_E = 0; //若晶振速度太高可以在这后加小的延时LCM_E = 0; //延时LCM_E = 1;}//写指令void WriteCommandLCM(unsigned char WCLCM,BuysC) //BuysC为0时忽略忙检测{if (BuysC) ReadStatusLCM(); //根据需要检测忙LCM_Data = WCLCM;LCM_RS = 0;LCM_RW = 0;LCM_E = 0;LCM_E = 0;LCM_E = 1;}//读数据unsigned char ReadDataLCM(void){LCM_RS = 1;LCM_RW = 1;LCM_E = 0;LCM_E = 0;LCM_E = 1;return(LCM_Data);}//读状态unsigned char ReadStatusLCM(void){LCM_Data = 0xFF;LCM_RS = 0;LCM_RW = 1;LCM_E = 0;LCM_E = 0;LCM_E = 1;while (LCM_Data & Busy); //检测忙信号return(LCM_Data);}void LCMInit(void) //LCM初始化{LCM_Data = 0;WriteCommandLCM(0x38,0); //三次显示模式设置,不检测忙信号Delay5Ms();WriteCommandLCM(0x38,0);Delay5Ms();WriteCommandLCM(0x38,0);Delay5Ms();WriteCommandLCM(0x38,1); //显示模式设置,开始要求每次检测忙信号WriteCommandLCM(0x08,1); //关闭显示WriteCommandLCM(0x01,1); //显示清屏WriteCommandLCM(0x06,1); // 显示光标移动设置WriteCommandLCM(0x0F,1); // 显示开及光标设置}//按指定位置显示一个字符void DisplayOneChar(unsigned char X, unsigned char Y, unsigned char DData){Y &= 0x1;X &= 0xF; //限制X不能大于15,Y不能大于1if (Y) X |= 0x40; //当要显示第二行时地址码+0x40;X |= 0x80; //算出指令码WriteCommandLCM(X, 1); //发命令字WriteDataLCM(DData); //发数据}//按指定位置显示一串字符void DisplayListChar(unsigned char X, unsigned char Y, unsigned char code *DData){unsigned char ListLength;ListLength = 0;Y &= 0x1;X &= 0xF; //限制X不能大于15,Y不能大于1while (DData[ListLength]>0x19) //若到达字串尾则退出{if (X <= 0xF) //X坐标应小于0xF{DisplayOneChar(X, Y, DData[ListLength]); //显示单个字符ListLength++;X++;}}}//5ms延时void Delay5Ms(void){unsigned int TempCyc = 5552;while(TempCyc--);//400ms延时void Delay400Ms(void){unsigned char TempCycA = 5;unsigned int TempCycB;while(TempCycA--){TempCycB=7269;while(TempCycB--);};}void Conut(void){time=TH0*256+TL0;TH0=0;TL0=0;S=(time*1.7)/100; //算出来是CMdelayms(5);if((S>=900)||flag==1) //超出测量范围显示“-”{flag=0;DisplayOneChar(9, 0, ASCII[11]);DisplayOneChar(10, 0, ASCII[10]); //显示点DisplayOneChar(11, 0, ASCII[11]);DisplayOneChar(12, 0, ASCII[11]);DisplayOneChar(13, 0, ASCII[12]); //显示M}else{disbuff[0]=S%1000/100;//先对1000取余,再对100取整disbuff[1]=S%1000%100/10;disbuff[2]=S%1000%10%10;DisplayOneChar(9, 0, ASCII[disbuff[0]]);DisplayOneChar(10, 0, ASCII[10]); //显示点DisplayOneChar(11, 0, ASCII[disbuff[1]]);DisplayOneChar(12, 0, ASCII[disbuff[2]]);DisplayOneChar(13, 0, ASCII[12]); //显示M}}void zd0() interrupt 1 //T0中断用来计数器溢出,超过测距范{flag=1; //中断溢出标志}void StartModule() //启动模块{TX=1; //启动一次模块 _nop_();_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();TX=0;}void main(void){WDT_CONTR = 0x34; //看门口启动,硬件清零后重新计数Timer1Init();IT0=1; //外部中断0设置为下降沿触发EA=1;EX0=1; //外部中断允许LCMInit(); //LCM初始化Delay5Ms(); //延时片刻(可不要)DisplayListChar(2, 0, email);DisplayListChar(2, 1, A);TMOD=0x01; //设T0为方式1,GATE=1;TH0=0;TL0=0;ET0=1; //允许T0中断EA=1; //开启总中断while(1){StartModule();while(!RX); //当RX为零时等待TR0=1; //开启计数while(RX); //当RX为1计数并等待TR0=0; //关闭计数Conut(); //计算delayms(80); //80MStracking(S);cesu(F);WDT_CONTR = 0x34; //看门狗启动,硬件清零后重新计数}}。

相关主题