赞
踩
课程名称 | 单片微机原理及应用 |
---|---|
项目主要 内容摘要 | 超声波避障小车 通过避障小车的设计,掌握设计、调试单片机应用系统的方法。任务涉及的知识点包括汇编(C51)程序语言、MCS-51单片机I/O应用、人机接口应用技术等。 要求所设计的避障小车完成以下功能: (1)小车可以前进、后退,左转和右转; (2)能够对小车运动方向上的障碍物进行躲避动作。 |
本项目有两种模式,一种是小车避障模式,一种是红外遥控模式,避障模式是通过HC-SR04来确定前方有无障碍物,HC-SR04检测到一定距离返回单片机,当小于某个值时,单片机会给舵机发送命令朝右转,舵机右转后继续扫描,若右方仍有障碍物时小车就会自动左转并将自动将舵机转回正前方。小车遥控模式则是通过38KHZ红外模块接受信息并解码(38KHZ红外遥控模块可以通过按下按键自动发射一段特定的频段,我们可以通过特定的解码方式来判断按下的按键),然后通过51单片机控制小车的运动。下图是小车的侧视图和俯瞰图
本项目是基于超声波模块的避障小车,除此之外我们还添加了红外遥控模块和oled作为附加功能,本项目对于cpu我们取用51的最小系统板,过程中共采用一个定时器和一个外部中断,其中PWM波的控制和38KHZ红外模块的解码在同一个定时器重断中,HC-SR04则在主程序中直接运行,外部中断0则被用于超声波模块的信号采集。下面将对硬件部分及部分软件进行叙述。
1,最小系统板
对于51单片机只需在XTAL1和XYAL2之间加一个晶振,并在晶振两端各加一个电容与地相接即可,单片机内部有反相器,加上电容与晶振即可起振,复位电路未焊接,主要是通过开关电源进行复位,然后通过TTL转USB模块连接单片机的串行口进行烧录程序。
2,L298N
L298N是专用驱动集成电路,可以增大输出电流,增强输出功率。其输出电流为2A,最高电流4A,最高工作电压50V,其输入端可以与单片机直接相联,从而很方便地受单片机控制。当驱动直流电机时,可以直接控制步进电机,并可以实现电机正转与反转并通过对ENA和ENB引脚输入PWM波来控制电机的速度,实现此功能只需改变输入端的逻辑电平。
L298可驱动2个电动机,OUT1,OUT2和OUT3,OUT4之间可分别接电动机,本实验装置我们选用驱动一台电动机。5,7,10,12脚接输入控制电平,控制电机的正反转。EnA,EnB接控制使能端,控制电机的停转。下图是L298N的使用逻辑表及其外形图。
3,舵机及HC-SR04模块
对于舵机,只需给它一个合适的PWM波,便可实现对其旋转角度的控制,其主要有三个引脚,别为VCC,GND和下面主要详细叙述超声波测距。
超声波测距原理:超声波测距是通过不断检测超声波发射后遇到障碍物所反射的回波, 从而测出发射和接收回波的时间差Δt , 然后求出距离S 。在速度v 已知的情况下,距离S 的计算,公式如下:S = vΔt/ 2在空气中,常温下超声波的传播速度是334 米/秒,但其传播速度V 易受空气中温度、湿度、压强等因素的影响,其中受温度的影响较大,如温度每升高1 ℃, 声速增加约0. 6 米/ 秒。因此在测距精度要求很高的情况下, 应通过温度补偿的方法对传播速度加以校正。已知现场环境温度T 时, 超声波传播速度V 的计算公式如下:
V = 331. 5+0.607T
这样, 只要测得超声波发射和接收回波的时间差Δt 以及现场环境温度T,就可以精确计算出发射点到障碍物之间的距离。
HC-SR04超声波测距模块简介:HC-SR04超声波测距模块可提供2cm-400cm的非接触式距离感测功能,测距精度可达高到3mm;模块包括超声波发射器、接收器与控制电路。HC-SR04超声波测距模块实物图如下:
HC-SR04超声波测距模块工作原理:
1、采用IO 触发测距,给控制端至少10us 的高电平信号;
2、模块自动发送8个40khz 的方波,自动检测是否有信号返回;
3、有信号返回,通过IO 输出高电平,高电平持续时间就是超声波从发射到返回时间.测试距离=(高电平时间*声速(340M/S))/2,其时序图如下:
4,38KHZ 红外模块
发射器采用红外发光二级管发射红外光波;接收器由红外接收二极管、三极管或硅光电池组成,它们将发射器发射的红外光接收转换为相应的电信号。主要采用NEC协议来解出按了什么键。
红外遥控器采用的NEC 载波频率为 38Khz,数据帧格式主要是引导码 +识别码 + 识别码反码 + 键值 + 键值反码 + 结束码的格式,其中引导码为9ms 高电平 + 4.5ms 低电平的频段,识别码中0码为0.56 ms 高电平 + 0.56 ms 低电平格式,1 码 为 0.56ms 高电平 + 1.68 ms 低电平的格式,结束码则是0.56ms 的高电平形式,总体发送格式如下图:
对于这种模式,遥控解码就只需要获取每个波形高低电平时长,就可以知道收到的数据是1还是0,最后读取整个数据即可。由于51单片机只能设置下降沿捕获,故我们可以根据其中各位高低电平的总体时长来解码,解码具体过程如下:
获取每个波形高低电平时长利用定时器去获取,初始化时,将定时器通道配置为下降沿捕获,当捕获到一个下降沿时,计数器等清0,即得到捕获一位码值的时间,然后如果码值大于(码值为0的总时间+码值为1的总时间)/则判定为高电平,否则判定为低电平。
的重复码则判断当收到重复码时,如果溢出中断标志>9时,就代表重复码收完了。
4,OLED模块
我们的oled模块是IIC接口,引脚分别为VCC,GND,SCL,SDA,是基于I2C通讯应用的模块。
通常情况下,一个完整的I2C通信过程包括:开始条件,地址传送,数据传送,停止条件等4个部分。
主机在 SCL 线上输出串行时钟信号,数据在 SDA 线上进行传输,每传输一个字节(最高位 MSB 开始传输)后面跟随一个应答位,一个 SCL 时钟脉冲传输一个数据位。
当总线上的主机都不驱动总线,总线进入空闲状态, SCL 和 SDA 都为高电平。总线空闲状态下总线上设备都可以通过发送开始条件启动通信。
当 SCL 线为高时,SDA 线上出现由高到低的信号,表明总线上产生了起始信号。 SDA 线上出现由低到高的信号,表明总线上产生了停止信号,如下图所示:
而后根据类似的电平变化进行通讯传输地址、数据和应答信号。
对于oled的字模取用可以通过下载相应的字库或字模取用软件来进行使用。
/********************************************************************* sbit OC1_PWM = P1^4; 左 sbit OC2_PWM = P1^5; 右 sbit Ain1_R = P2^0; //rear sbit Trig = P1^0; //超声波测距模块 sbit Ain2_R = P2^1; sbit Echo = P1^1; sbit Bin1_R = P2^2; sbit Bin2_R = P2^3; sbit OC3_PWM = P1^7; 右 sbit Engine_PWM = P1^3; //舵机 sbit OC4_PWM = P1^6; 左 sbit Ain1_F = P2^4; //front sbit Ain2_F = P2^5; sbit OLED_SCL=P0^0; //OLED显示 sbit Bin1_F = P2^6; sbit OLED_SDIN=P0^1; sbit Bin2_F = P2^7; **********************************************************************/
void Run(void) { OLED_ShowNum(0,0,Distance,3,16); if(Angle == 3 && Distance >= 30) //舵机90度且测距大于30 { GoStraight(7,1);//直走 Hcsr04_Init(); GetDistance(); OLED_ShowNum(0,0,Distance,3,16); } else if(Angle == 3 && Distance < 30)//舵机90度且测距小于30 { Stop(); //停止 SetAngle(0); Delay(500); Hcsr04_Init(); GetDistance(); OLED_ShowNum(0,0,Distance,3,16); if(Angle == 1 && Distance < 30)//舵机0度且测距小于30 { Rotation(0,1,10,1); //右轮快 左转 } else { Rotation(10,1,0,1); //左轮快 右转 } SetAngle(90); //舵机90度 Delay(500); } }
while(1)//mode模式变量 Moving遥控标志变量 { ''''''' else if(mode==1) //如果是遥控模式 { if(Moving==0) //停止 { Stop(); } else if(Moving==1)//前进 { GoStraight(10,1); } else if(Moving==2)//后退 { GoStraight(10,0); } else if(Moving==3)//左转 { Rotation(0,1,10,1); } else if(Moving==4)//右转 { Rotation(10,1,0,1); } } }
void Hcsr04_Init(void);//发射超声波
void GetDistance(void);//根据接收到的超声波算出距离
void Time0_Init(void);//定时器0初始化 (记录有效回收电平时长)
void PWM_Timer1Init(void); //定时器1 初始化 void SetAngle(unsigned char Value); //舵机PWM控制 void SetCompare(unsigned char Y_Position, unsigned char OC1,unsigned char OC2); void timer1_isr(void) interrupt 3 //0.5ms ½øÒ»´ÎÖÐ¶Ï { TL1 = 0x34; TH1 = 0xFE; //steer PWM 用于控制舵机 占空比由Angle调节 if(counter_num < Angle) Engine_PWM = 1; else Engine_PWM = 0; //OC1 PWM 用于控制电机 if(counter_num < OC1_Compare) OC1_PWM = 1; else OC1_PWM = 0; //OC2 PWM if(counter_num < OC2_Compare) OC2_PWM = 1; else OC2_PWM = 0; //OC2 PWM if(counter_num < OC3_Compare) OC3_PWM = 1; else OC3_PWM = 0; //OC2 PWM if(counter_num < OC4_Compare) OC4_PWM = 1; else OC4_PWM = 0; //define Period_falg 40 counter_num++; if(counter_num == Period_falg) { counter_num = 0; } irtime=irtime+2; //用于红外的解码 // OLED_ShowNum(0,5,irtime,3,16); }
unsigned char angle_flag[6]={1,2,3,4,5}; void SetAngle(unsigned char Value) { switch(Value) { case 0:Angle = angle_flag[0];break; case 45:Angle = angle_flag[1];break; case 90:Angle = angle_flag[2];break; case 135:Angle = angle_flag[3];break; case 180:Angle = angle_flag[4];break; default: break; } } //0°------1 45°-------2 //90°------3 135°------4 180°-------5
void EX0init(void);//外部中断0初始化 (用于读取每一位的时间)
void Irpro(void); //将32位电平解码 并将数据放在IRcord[]中
void Ir_work(void);//根据解码值执行相应的程序
void receive(void);//判断是否解码成功,如果成功执行Ir_work()
void OLED_ShowChar(u8 x,u8 y,u8 chr,u8 Char_Size);//显示字符
void OLED_ShowNum(u8 x,u8 y,u32 num,u8 len,u8 size);//显示数字
void OLED_ShowString(u8 x,u8 y, u8 *p,u8 Char_Size);//显示字符串
/*****************************
设置轮子的方向——向前、向后
******************************/
void SetWheelDir(uchar Y_Position, uchar X, uchar In1, uchar In2);
/*****************************
限制轮子的速度0-40
******************************/
void SpeedLimit(char Speed);
void GoStraight(char Speed, char dir);//dir=1直走 dir=0后退
/*****************************
Speed_A左轮速度 Dir_A左轮方向
Speed_B右轮速度 Dir_B右轮方向
******************************/
void Rotation(char Speed_A, char Dir_A, char Speed_B, char Dir_B);
void Stop(void);//停止
******************/
void SetWheelDir(uchar Y_Position, uchar X, uchar In1, uchar In2);
/*****************************
限制轮子的速度0-40
******************************/
void SpeedLimit(char Speed);
void GoStraight(char Speed, char dir);//dir=1直走 dir=0后退
/*****************************
Speed_A左轮速度 Dir_A左轮方向
Speed_B右轮速度 Dir_B右轮方向
******************************/
void Rotation(char Speed_A, char Dir_A, char Speed_B, char Dir_B);
void Stop(void);//停止
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。