51单片机自动壁障小车设计,附原理图程序全套制作资料
大家好,欢迎关注电气技术微课堂!
在科学探索及紧急抢险中经常要对一些危险或人类不能直接到达的地域进行探测,这就需要用机器人来完成。而机器人在复杂地形行进时自动避障是一项必不可少也是最基本的功能。避障功能在日常生活中也是比较常见的,像是风靡一时的自动清扫机器人,只需放在地上一会就可以将你的屋子打扫干净,这里面的最基本功能也是避障,当它检测到前方有障碍就会绕开。这样就可以躲避家具将你的地板清理干净了。因此,自动避障系统的研发就应运而生。
MCS-51《单片机原理及接口技术》是中职院校电气自动化专业开设的 一门必修课程,该课程主要是通过对单片机的内部结构、相关外围电路及编程语言的学习,使学生掌握用单片机进行开发设计一些实用电子电路的能力,自动避障小车就是基于这一系统开发而成的。自动避障小车可以作为地域探索机器人和紧急抢险机器人的运动系统,让机器人在行进中自动避过障碍物。
一、本设计任务和主要内容
本设计是对以单片机STC89C52RC为核心的系统根据感测模块传输的前方路面信息,控制小车行驶走向的软、硬件设计开发。系统要能够做到准确及时监测前方路面信息并传输给主控模块,做到根据前方路面信息及时调整小车的走向,做到显示小车的走向和小车已经行驶过的路程。
壁障小车的主要功能是:
① 感测模块实时监测路面情况并及时将障碍物的位置传输给单片机;
② 单片机核心模块根据感测模块给予的信息控制小车两电机转动;
③ 电机驱动模块驱动两电机转动,实现转向与行走。
二、系统主要硬件电路设计
根据设计要求,我们的自动避障小车主要由五个模块构成:车体框架、电源及稳压模块、主控模块、探测模块、电机驱动模块组成。 各模块分述如下:
图1 系统模块组成框图
2.1 小车避障原理分析
小车车头处装有三个红外探头,中间一个光电开关对向正前方,两侧的红外探头向两边各分开30度,(如图2所示)。小车在行进过程中由红外探头向前方发射出红外线,当红外线遇到障碍物时发生漫反射,反射光被光电开关接收。小车根据三个探头接受信号的情况来判断前方障碍物的分布并做出相应的动作。
图2 自动避障小车车体及避障原理图
红外探头选用的是E12-D15NK型红外避障传感器,这是一种集发射与接收于一体的光电传感器,发射光经过调制后发出,接收头对反射光进行解调输出。有效的避免了可见光的干扰。分别探测正前方,前右侧,前左侧障碍物信息,在特殊地形(如障碍物密集地形)可将正前方的光电开关移置后方进行探测。E12-D15NK光电开关平均有效探测距离0~30cm可调,且抗外界背景光干扰能力强,可在日光下正常工作(理论上应避免日光和强光源的直接照射)。我们小车换档调速后的最大制动距离不超过30cm,一般在10~20cm左右,因而探测距离满足我们的小车需求示意图如下:
图4 红外避障传感器原理图
电气特性:
红色:VCC;黑色:GND;黄色:信号输出;白色:和红线一起外接电位器。
工作电压:5VDC
工作电流:10-15mA
驱动电流:100mA
感应距离:1-15CM
机械特性:
颜色:橙黄色
直径:12MM
长度:35MM
引线长度:25CM(不含接头)
2.2电源模块
方案一: 采用交流电经直流稳压处理后供电 采用交流电提供直流稳压电源,电流驱动能力及电压稳定性最好,且负载对电源影响也最小。但由于需要电线对小车供电,极大影响了壁障小车行动的灵活性及地形的适应能力。而且壁障小车极易把拖在地上的电线识别为障碍物,人为增加了不必要的障碍。故我放弃了这一方案。
方案二: 采用蓄电池供电 蓄电池具有较强的电流驱动能力和较好的电压稳定性能,且成本低廉。可采用蓄电池经7812芯片稳压后给电机供电,再经过降压接7805芯片给单片机及其他逻辑单元供电。但蓄电池体积相对庞大,且重量过大,造成电机负载过大,不适合我们采用的小车车架(玩具电动车车架)。故我放弃了这一方案。
方案三: 采用干电池组进行供电 采用四节干电池降压至5V后给单片机及其他逻辑单元供电,另取六节干电池为电机及光电开关供电。这样电机启动及制动时的短暂电压干扰不会影响到逻辑单元和单片机的工作。干电池用电池盒封装,体积和重量较小,同时玩具车底座可以安装四节干电池,正好可为单片机及其他逻辑单元供电。在稳压方面,起始时考虑使用7805芯片对6V的电池电压进行降压稳压。但考虑到这样使得7805芯片消耗大量能量,降低电池寿命;同时,由于STC89C51、光电开关、小车电机对于供电电压要求并不苛刻,故我们将6V电池电压接一个二极管降压后直接给单片机及其他逻辑单元供电。而电机和光电开关的电源不做稳压处理。这样只需在小车主板上加两个调速按钮,根据电池电量选择合适功率即可,甚至于可直接在软件里设置自动换挡。 综合考虑,我采用方案三。
2.3主控模块 3.1、STC89C52RC单片机最小系统
我采用的是STC公司的51内核单片机STC89C52RC,单片机最小系统及概述如下:STC89C52RC单片机介绍
STC89C52RC单片机是宏晶科技推出的新一代高速/低功耗/超强抗干扰的单片机,指令代码完全兼容传统8051单片机,12时钟/机器周期和6时钟/机器周期可以任意选择。
主要特性如下:
1. 增强型8051单片机,6时钟/机器周期和12时钟/机器周期可以任意选择,指令代码完全兼容传统8051.
2. 工作电压:5.5V~3.3V(5V单片机)/3.8V~2.0V(3V单片机)
3. 工作频率范围:0~40MHz,相当于普通8051的0~80MHz,实际工作频率可达48MHz
4. 用户应用程序空间为8K字节
5. 片上集成512字节RAM
6. 通用I/O口(32个),复位后为:P1/P2/P3/P4是准双向口/弱上拉,P0口是漏极开路输出,作为总线扩展用时,不用加上拉电阻,作为I/O口用时,需加上拉电阻。
7. ISP(在系统可编程)/IAP(在应用可编程),无需专用编程器,无需专用仿真器,可通过串口(RxD/P3.0,TxD/P3.1)直接下载用户程序,数秒即可完成一片
8. 具有EEPROM功能
9. 具有看门狗功能
10. 共3个16位定时器/计数器。即定时器T0、T1、T2
11. 外部中断4路,下降沿中断或低电平触发电路,Power Down模式可由外部中断低电平触发中断方式唤醒
12. 通用异步串行口(UART),还可用定时器软件实现多个UART
13. 工作温度范围:-40~+85℃(工业级)/0~75℃(商业级)
14. PDIP封装
在探测模块和单片机中断接口之间、独立按键与单片机中断接口之间,需要经过电平的逻辑处理进行连接。主要涉及到一个三输入或非门和一个二输入与门。这两个逻辑关系我们直接选用74HC系列的集成芯片实现。 由于三输入或非门在市场上很难购买到,我们采用了两个二输入或非门和一个二输入与门完成了三输入或非门。由于我们采用的74HC08(四二输入与门)、74HC02(四二输入或非门)均为四二输入的,各提供四个二输入与门和四个二输入或非门,我们用各用一片芯片即可实现所需逻辑功能。
2.4电机驱动模块
本系统采用了L298N芯片来驱动电机 ,L298N是一个具有高电压大电流的全桥驱动芯片,输出电压最高可达50V,可以直接通过电源来调节输出电压;可以直接用单片机的IO口提供信号,而且带有使能端,方便PWM调速,电路简单,性能稳定,使用比较方便。L298N芯片可以驱动两个二相电机,也可以驱动一个四相电机,正好符合我们小车两个二相电机的驱动要求。 综合考虑,我采用L298N芯片驱动小车电机。
最终方案如下: 使用干电池组对系统供电,改造玩具电动车作为小车底座,采用STC89C52RC作为主控芯片,采用E12-D15NK光电开关进行障碍物探测,使用L298N驱动直流电机。逻辑关系处理使用74HC系列芯片完成。
2.5 总电路图
图5 总电路图设计
三、系统软件设计
3.1程序流程图
本系统设计流程图如下
图6 系统软件主流程图
3.2系统程序清单
#include <reg51.h>
#define uchar unsigned char
#define uintunsigned int
/********************
端口定义
*********************/
sbit IN1=P1^0; //P10与电机驱动IN1相连
sbit IN2=P1^1 //P11与电机驱动IN2相连
sbit IN3=P1^2; //P12与电机驱动IN3相连
sbit IN4=P1^3; //P13与电机驱动IN4相连
uchar INS=P2; //P2端口的^0、P2^1、P2^2分别与左、中、右红外模块输出信号线线相连
uint D=200;//定义延迟函数的参数
/************************
各个子函数定义
***********************/
void Go(void)
{
IN1=0;
IN2=0;
IN3=1;
IN4=0;
}
void Back(void)
{
IN1=0;
IN2=0;
IN3=0;
IN4=1;
}
void Go_left(void)
{
IN1=1;
IN2=0;
IN3=1;
IN4=0;
}
void Go_right(void)
{
IN1=0;
IN2=1;
IN3=1;
IN4=0;
}
void Stop(void)
{
IN1=IN2=IN3=IN4=0;
}
/***
void Back_left(void)
{
IN1=1;
IN2=0;
IN3=0;
IN4=1;
}
void Back_right(void)
{
IN1=0;
IN2=1;
IN3=0;
IN4=1;
}
***/
void Delay(uint n)
{
uint i,j;
i=j=n;
for(;i>0;i--)
for(;j>0;j--);
}
void Be_move(void)
{
uchar temp;
temp=INS&0x07;
switch(temp)
{
case 0x01:Go_right();Go();Delay(D);break;
case 0x02:
case 0x03:
case 0x07:Back();Delay(D);Go_right();Go();Delay(D);break;
case 0x06:Back();Delay(D);Go_left();Go();Delay(D);break;
case 0x04:Go_left();Go();Delay();break;
case 0x05:Go();Delay();break;
default:Stop();Delay();break;
}
}
void main()
{
uchar temp;
while(1)
{
temp=INS&0x07;
if(temp==0x00) Go();
else Be_move();
}
}
AOVX资产监测设备之华大单片机Boot软件升级
近期笔者在研究如何从软件方面提升资产监测设备的性能,从而提升用户的使用体验感。
资产监测设备的主要作用是帮助用户监测设备的实时位置、实时状态避免运动中的货物出现丢失等情况。AOVX环境监测设备在智慧物流方面发挥了重要的作用,例如在运输贵重货物过程中,只需要将该设备安装在货物中,用户即可远程了解货物的位置、货物是否出现暴力扔件、以及避免货物在运输过程中的丢失。
资产监测设备中的华大单片机,对该设备的工作发挥了重要的作用。笔者的软件团队对单片机进行了软件升级。
具体流程如下:
具体流程如下:/**
** \brief 上位机数据帧解析及处理**** \param [in] None**** \retval Ok APP程序升级完成,并接受到跳转至APP命令** \retval OperationInProgress 数据处理中** \retval Error 通讯错误********************************************************************************/en_result_t Modem_Process(void){uint8_t u8Cmd, u8FlashAddrValid, u8Cnt, u8Ret;uint16_t u16DataLength, u16PageNum, u16Ret;uint32_t u32FlashAddr, u32FlashLength, u32Temp;
if (enFrameRecvStatus == FRAME_RECV_PROC_STATUS) //有数据帧待处理, enFrameRecvStatus值在串口中断中调整
{
u8Cmd = u8FrameData[PACKET_CMD_INDEX]; //获取帧指令码
if (PACKET_CMD_TYPE_DATA == u8FrameData[PACKET_TYPE_INDEX]) //如果是数据指令
{
u8FlashAddrValid = 0u;
u32FlashAddr = u8FrameData[PACKET_ADDRESS_INDEX] + //读取地址值
(u8FrameData[PACKET_ADDRESS_INDEX + 1] << 8) +
(u8FrameData[PACKET_ADDRESS_INDEX + 2] << 16) +
(u8FrameData[PACKET_ADDRESS_INDEX + 3] << 24);
if ((u32FlashAddr >= (FLASH_BASE + BOOT_SIZE)) && (u32FlashAddr < (FLASH_BASE + FLASH_SIZE))) //如果地址值在有效范围内
{
u8FlashAddrValid = 1u; //标记地址有效
}
}
switch (u8Cmd) //根据指令码跳转执行
{
case PACKET_CMD_HANDSHAKE : //握手帧 指令码
u8FrameData[PACKET_RESULT_INDEX] = PACKET_ACK_OK; //返回状态为:正确
Modem_SendFrame(&u8FrameData[0], PACKET_INSTRUCT_SEGMENT_SIZE); //发送应答帧给上位机
break;
case PACKET_CMD_ERASE_FLASH : //擦除flash 指令码
if ((u32FlashAddr % FLASH_SECTOR_SIZE) != 0) //如果擦除地址不是页首地址
{
u8FlashAddrValid = 0u; //标记地址无效
}
if (1u == u8FlashAddrValid) //如果地址有效
{
u32Temp = u8FrameData[PACKET_DATA_INDEX] + //获取待擦除flash尺寸
(u8FrameData[PACKET_DATA_INDEX + 1] << 8) +
(u8FrameData[PACKET_DATA_INDEX + 2] << 16) +
(u8FrameData[PACKET_DATA_INDEX + 3] << 24);
u16PageNum = FLASH_PageNumber(u32Temp); //计算需擦除多少页
for (u8Cnt=0; u8Cnt<u16PageNum; u8Cnt++) //根据需要擦除指定数量的扇区
{
u8Ret = Flash_EraseSector(u32FlashAddr + (u8Cnt * FLASH_SECTOR_SIZE));
if (Ok != u8Ret) //如果擦除失败,反馈上位机错误代码
{
u8FrameData[PACKET_RESULT_INDEX] = PACKET_ACK_ERROR;
break;
}
}
if (Ok == u8Ret) //如果全部擦除成功,反馈上位机成功
{
u8FrameData[PACKET_RESULT_INDEX] = PACKET_ACK_OK;
}else //如果擦除失败,反馈上位机错误超时标志
{
u8FrameData[PACKET_RESULT_INDEX] = PACKET_ACK_TIMEOUT;
}
}
else //地址无效,反馈上位机地址错误
{
u8FrameData[PACKET_RESULT_INDEX] = PACKET_ACK_ADDR_ERROR;
}
Modem_SendFrame(&u8FrameData[0], PACKET_INSTRUCT_SEGMENT_SIZE); //发送应答帧到上位机
break;
case PACKET_CMD_APP_DOWNLOAD : //数据下载 指令码
if (1u == u8FlashAddrValid) //如果地址有效
{
u16DataLength = u8FrameData[FRAME_LENGTH_INDEX] + (u8FrameData[FRAME_LENGTH_INDEX + 1] << 8)
- PACKET_INSTRUCT_SEGMENT_SIZE; //获取数据包中的数据长度(不包含指令码指令类型等等)
if (u16DataLength > PACKET_DATA_SEGMENT_SIZE) //如果数据长度大于最大长度
{
u16DataLength = PACKET_DATA_SEGMENT_SIZE; //设置数据最大值
}
u8Ret = Flash_WriteBytes(u32FlashAddr, (uint8_t *)&u8FrameData[PACKET_DATA_INDEX], u16DataLength); //把所有数据写入flash
if (Ok != u8Ret) //如果写数据失败
{
u8FrameData[PACKET_RESULT_INDEX] = PACKET_ACK_ERROR; //反馈上位机错误 标志
}
else //如果写数据成功
{
u8FrameData[PACKET_RESULT_INDEX] = PACKET_ACK_OK; //反馈上位机成功 标志
}
}
else //如果地址无效
{
u8FrameData[PACKET_RESULT_INDEX] = PACKET_ACK_ADDR_ERROR; //反馈上位机地址错误
}
Modem_SendFrame(&u8FrameData[0], PACKET_INSTRUCT_SEGMENT_SIZE); //发送应答帧到上位机
break;
case PACKET_CMD_CRC_FLASH : //查询flash校验值 指令码
if (1u == u8FlashAddrValid) //如果地址有效
{
u32FlashLength = u8FrameData[PACKET_DATA_INDEX] +
(u8FrameData[PACKET_DATA_INDEX + 1] << 8) +
(u8FrameData[PACKET_DATA_INDEX + 2] << 16) +
(u8FrameData[PACKET_DATA_INDEX + 3] << 24); //获取待校验flash大小
if ((u32FlashLength + u32FlashAddr) > (FLASH_BASE + FLASH_SIZE)) //如果flash长度超出有效范围
{
u8FrameData[PACKET_RESULT_INDEX] = PACKET_ACK_FLASH_SIZE_ERROR; //反馈上位机flash尺寸错误
}else
{
u16Ret = Cal_CRC16(((unsigned char *)u32FlashAddr), u32FlashLength);//读取flash指定区域的值并计算crc值
u8FrameData[PACKET_FLASH_CRC_INDEX] = (uint8_t)u16Ret; //把crc值存储到应答帧
u8FrameData[PACKET_FLASH_CRC_INDEX+1] = (uint8_t)(u16Ret>>8);
u8FrameData[PACKET_RESULT_INDEX] = PACKET_ACK_OK; //反馈上位机成功 标志
}
}
else //如果地址无效
{
u8FrameData[PACKET_RESULT_INDEX] = PACKET_ACK_ADDR_ERROR; //反馈上位机地址错误
}
Modem_SendFrame(&u8FrameData[0], PACKET_INSTRUCT_SEGMENT_SIZE+2); //发送应答帧到上位机
break;
case PACKET_CMD_JUMP_TO_APP : //跳转至APP 指令码
Flash_EraseSector(BOOT_PARA_ADDRESS); //擦除BOOT parameter 扇区
u8FrameData[PACKET_RESULT_INDEX] = PACKET_ACK_OK; //反馈上位机成功
Modem_SendFrame(&u8FrameData[0], PACKET_INSTRUCT_SEGMENT_SIZE); //发送应答帧到上位机
return Ok; //APP更新完成,返回OK,接下来执行跳转函数,跳转至APP
case PACKET_CMD_APP_UPLOAD : //数据上传
if (1u == u8FlashAddrValid) //如果地址有效
{
u32Temp = u8FrameData[PACKET_DATA_INDEX] +
(u8FrameData[PACKET_DATA_INDEX + 1] << 8) +
(u8FrameData[PACKET_DATA_INDEX + 2] << 16) +
(u8FrameData[PACKET_DATA_INDEX + 3] << 24); //读取上传数据长度
if (u32Temp > PACKET_DATA_SEGMENT_SIZE) //如果数据长度大于最大值
{
u32Temp = PACKET_DATA_SEGMENT_SIZE; //设置数据长度为最大值
}
Flash_ReadBytes(u32FlashAddr, (uint8_t *)&u8FrameData[PACKET_DATA_INDEX], u32Temp); //读flash数据
u8FrameData[PACKET_RESULT_INDEX] = PACKET_ACK_OK; //反馈上位机成功 标志
Modem_SendFrame(&u8FrameData[0], PACKET_INSTRUCT_SEGMENT_SIZE + u32Temp);//发送应答帧到上位机
}
else //如果地址无效
{
u8FrameData[PACKET_RESULT_INDEX] = PACKET_ACK_ADDR_ERROR; //反馈上位机地址错误 标志
Modem_SendFrame(&u8FrameData[0], PACKET_INSTRUCT_SEGMENT_SIZE); //发送应答帧到上位机
}
break;
case PACKET_CMD_START_UPDATE : //启动APP更新(此指令正常在APP程序中调用)
u8FrameData[PACKET_RESULT_INDEX] = PACKET_ACK_OK; //反馈上位机成功 标志
Modem_SendFrame(&u8FrameData[0], PACKET_INSTRUCT_SEGMENT_SIZE); //发送应答帧到上位机
break;
}
enFrameRecvStatus = FRAME_RECV_IDLE_STATUS; //帧数据处理完成,帧接收状态恢复到空闲状态
}
return OperationInProgress; //返回,APP更新中。。。
}
同样从事软件工作相关的用户,对资产监测设备感兴趣的话可以参考该文档。
相关问答
单片机 C语言,switch, case ,break的含义,格式,用法?根据变量值执行不同的操作,如下面:switch(a){case0:led1=1;break;case1:led2=1;break;case2:led1=0;break;default:break;}...
单片机 中if与switch的差异有哪些?switch的用法switch和case一起使用,结束处用break终止。switchcase其实是一个跳转表,只要case后面的内容和判别表达式的内容一致,就会顺序执行后面的语句...s.....
单片机 C语言的switch语句中的break这在哪?根据程序的写法,即使有不影响实现SWITCH语句的功能,case3.4.5.6,程序仍然能够正确跳到case3.4.5.6对应部分。一、switch语句的结构switch语句的结构如下图所...
用C语言写51 单片机 闹钟程序 - 156****1626 的回答 - 懂得汇编做的可以不.有秒表.有闹钟.//*********************************************************//实现2113功能:本程序实现在P1口模拟一个...
51 单片机 定时闹铃(c语言) - ivguNtz1qo 的回答 - 懂得我来2113发一个C语言51单片机时钟程序,希望能帮到你5261/*程序功能:带定时4102闹铃时钟*//*----------------------------------------------...
没有基础学 单片机 开发可以吗?谢谢邀请,可以的,但需要学习的专业知识很多,会比相关专业毕业的人员更大的难度如果您是一个年纪偏大比如40岁以上中年人,想从事单片机开发工作,个人不推荐...谢...
单片机 菜鸟求教,关于延迟和按键去抖的問題-ZOL问答case12:P2=0x0b;P0=Tab[ge4];break;}//从这里开始结束,以下是原文count++;if(count==50)//差点忘了,20要改50,因为定时器被我改成20...
51 单片机 可以输出锯齿信号吗?根据51单片机产生锯齿波c语言,单片机产生方波、锯齿波、三角波程序#include#defineucharunsignedchar#defineuintunsignedin...51单片机可...
让小车到达指定位置有哪些PID算法?许多学生不知道PID是什么,因为许多学生不是自动化的。他们需要信息和程序来开口说话。很明显,这种学习方法是错误的。至少,首先,您需要理解PID是什么。首先,...i...
如何使用泰克示波器记录连续波形?这个简单,把两个方波进行异或,用430单片机的定时器A测量异或后的方波的脉冲宽度,在对测量数据进行简单的计算就可得到相位差。。。具体程序如下:P1SEL|=BIT2...