51单片机课设——避障小车
一、项目基本信息
课程名称 | 单片微机原理及应用 |
---|---|
项目主要 内容摘要 | 超声波避障小车 通过避障小车的设计,掌握设计、调试单片机应用系统的方法。任务涉及的知识点包括汇编(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的字模取用可以通过下载相应的字库或字模取用软件来进行使用。
五、项目设计内容
5.1主控代码
5.1.1引脚预览
/*********************************************************************
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;
**********************************************************************/
5.1.2超声波避障主控
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);
}
}
5.1.3红外主控代码
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);
}
}
}
5.2硬件外设
5.2.1超声波模块(定时器0)
void Hcsr04_Init(void);//发射超声波
void GetDistance(void);//根据接收到的超声波算出距离
void Time0_Init(void);//定时器0初始化 (记录有效回收电平时长)
5.2.2 PWM输出(使用定时器1)
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);
}
5.2.3 舵机模块
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
5.2.4 红外遥控模块(使用了外部中断0 和定时器1)
void EX0init(void);//外部中断0初始化 (用于读取每一位的时间)
void Irpro(void); //将32位电平解码 并将数据放在IRcord[]中
void Ir_work(void);//根据解码值执行相应的程序
void receive(void);//判断是否解码成功,如果成功执行Ir_work()
5.2.5 OLED模块
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);//显示字符串
5.2.6电机
/*****************************
设置轮子的方向——向前、向后
******************************/
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);//停止