目录
任务要求:
摘要
本设计完成了基于STC89C52的智能送药小车控制系统的设计过程,此系统主要包括51最小开发板、按键控制电路、电机驱动电路、灰度检测传感电路等电路组合而成。以STC89C52为主控芯片及其外围扩展电路实现系统整体功能,用灰度传感器实现小车循迹功能,使用STC89C52驱动L298N芯片控制电机,由PWM控制小车速度,从而使小车顺利通过直线、转弯、识别十字路口并正确转向功能。在硬件设计的基础上实现了电机控制功能以及小车简单循迹的软件设计方案。
关键词:单片机 循迹 PWM L298N 智能送药小车
1、前言
随着人工智能和自动化技术的迅速发展,各种高科技以及前沿技术开始广泛运用于智能小车领域,使智能小车越来越多样化。作为集多种高科技于一身的高薪技术集成产物,它融合了传统机械、传感器、单片机、软件与人工智能技术等多个学科的知识。智能小车的研究和开发正成为广泛关注的焦点。智能化小车是未来人们生活中的重要工具,将会给我们的生活生产带来很大的便利。本设计基于单片机为核心设计了一款智能送药小车,以多路灰度传感器实现循迹、路口识别、终点识别功能,通过 PWM 调整小车姿态。能够在规定医院的药房和病房间根据指令在药房与病房间往返送药。本设计采用STC89C52芯片实现其智能控制功能,通过五路灰度传感器判断小车运行状态及小车位置,经单片机判断后驱动电机驱动模块对小车进行控制。采用的数字量灰度传感器可对被测面的两种颜色差异进行调节比较,对于本设计中病房要求的白红线循迹识别精度高、抗干扰能力强。
2、系统方案设计
本课程设计要求设计智能送药小车,模拟完成在医院药房与病房的药物送取。病房分为近端、中端、远端三部分,要求小车分别送往三端病房并返回,并且运送和返回时间均不超过40秒。
小车系统+7V电源为L298N电机驱动提供电源,经L298N驱动模块内置的稳压模块输出+5V电源为其他模块供电,通过由发光二极管和光敏二极管组成的灰度传感器循迹模块对白色底板和红线对光的反射率不同来实现循迹、转弯和十字路口的判断。由STC98C52通过对I/O口控制驱动L298N模块改变四个直流电机的工作状态,通过四个电机驱动模块和调节PWM输出实现小车前进、后退、转向、掉头、加速、减速功能。
系统软件框图如图2.1所示,当传感器检测到红色轨道后开始判断是否读取到房间信息,进而驱动L298N电机驱动模块来驱动电机运作,使小车前往对应病房。
、
图2.1 系统软件框图
系统硬件框图如图2.2所示,使用+7V电压源为L298N电机驱动模块供电,经L298N内部稳压管输出+5V电压为单片机及其他模块供电。单片机通过I/O口读取灰度传感器和按键数据对小车进行控制。
3、理论分析与计算
3.1 病房选择:
目标病房选择说明示意图如表3.1按键对应病房所示,当按下不同按键使对应端口接地时,通过读取I/O端口,通过按键判断函数进行判断从而执行不同的子程序来进行病房之间的往返,从而达到智能送药的目的。
按键 | |||||||||
KEY1 | KEY2 | KEY3 | KEY4 | KEY5 | KEY6 | KEY7 | KEY8 | 病房号 | |
按键状态 | 0 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 |
1 | 0 | 1 | 1 | 1 | 1 | 1 | 1 | 2 | |
1 | 1 | 0 | 1 | 1 | 1 | 1 | 1 | 3 | |
1 | 1 | 1 | 0 | 1 | 1 | 1 | 1 | 4 | |
1 | 1 | 1 | 1 | 0 | 1 | 1 | 1 | 5 | |
1 | 1 | 1 | 1 | 1 | 0 | 1 | 1 | 6 | |
1 | 1 | 1 | 1 | 1 | 1 | 0 | 1 | 7 | |
1 | 1 | 1 | 1 | 1 | 1 | 1 | 0 | 8 |
表3.1 按键对应病房
3.2 电机引脚及状态
L298N真值表如图3.2所示,ENA为使能引脚,通过单片机输出PWM信号使能ENA来调整电机转速。IN1和IN2分别为高、低和低、高电平时电机对应正转、反转,IN1和IN2同时为低电平或同时为高电平时电机制动。ENA为低电平时使能IN1、IN2引脚无法对电机进行控制。
3.3 灰度传感器及电机状态
本设计采用五路灰度传感器L2、L1、M、R1、R2,当中间三个传感器L1、M、R1检测到红线时认为小车处于红线上,允许启动按键模块,启动后进行自动循迹和路口、终点识别。当M测到红线,L1、R1均未测到时,小车正常前进。当L1和M同时测到红线时,小车小幅度偏离轨道,此时左轮小幅度减速调整姿态直到L1检测不到红线。当L1测到红线而M未测到红线时小车大幅偏离轨道,此时左轮大幅度减速调整小车姿态直到L1和M同时测到红线,此时小车姿态恢复到小幅度偏离状态,左轮由大幅度减速改为小幅度减速至L1未测到红线而M测到红线。右边通过M、R1检测同理。此方法仅通过改变小车左右轮PWM进行姿态调整,且根据小车偏离的情况设置不同的差速,可以让小车在循迹过程中行进路线更为平滑稳定且不易偏离轨道。循迹过程L1、M、R1状态对应小车几种姿态如表3.3.1所示。
L1、M、R1输出状态 | 小车姿态 |
000 | 小车未在红线上 |
001 | 小车大幅度左偏 |
010 | 小车在红线中间 |
011 | 小车小幅度左偏 |
100 | 小车大幅度右偏 |
110 | 小车小幅度右偏 |
表3.3.1 循迹过程传感器状态对应的几种小车姿态
L2和R2传感器用于判断小车是否到达路口及终点,当五个传感器同时识别到红线则表示到达路口,五个传感器同时识别不到红线则表示小车通过终点虚线到达终点白色区域。对于远端病房T型路口则是检测L2、L1或者R2、R1识别到红线而对应R2、L2识别不到红线。路口、终点识别传感器状态如表3.3.2所示。
L2、L1、M、R1、R2输出状态 | 路口、终点判定 |
11111 | 到达路口 |
00000 | 到达终点 |
0XX11 | T型路口(需右转) |
11XX0 | T型路口(需左转) |
表3.3.2 路口、终点识别传感器状态
4、系统电路设计与仿真
4.1系统硬件框图
系统硬件如图4.1所示,由电源部分为L298N电机驱动供电,L298N电机驱动输出的5V电压为单片机和灰度传感器供电。由STC89C52控制L298N来驱动电机转动,通过灰度传感器采集到的不同的样本数据来控制小车处于不同的运动状态,再通过影响小车的运动状态反过来影响采样的数据。
图4.1 系统硬件框图
4.2 单片机最小板电路
本设计采用市场常见51最小开发板,搭配STC89C52芯片,是一个低功耗,高性能的51内核的CMOS 8位单片机,具有在线编程功能,使用简单价格低廉。51最小开发板电路图如图4.2所示。
4.3 驱动电路设计
在小车电机驱动方案的选择上,选择了市面上常见的电机驱动模块L298N双H桥直流电机驱动模块,此驱动板体积小,重量轻,具有强大的驱动能力:2A的峰值电流和46V的峰值电压;内置DC to DC稳压(LM2596稳压管),可提供5V电压为其他模块供电。L298N电路原理图如图4.3所示。
图4.3 L298N电路原理图
4.4 灰度传感器设计
本设计采用五路灰度传感器,每一组传感器均由一只发光二极管和一只光敏二极管组成,两个传感器安装在同一面上,利用不同颜色的被检测面对光的反射强弱程度 不同进行比较,从而输出对应的信号供单片机I/O口采集和控制。数字量灰度传感器可对被探测面的两种颜色差异进行调节比较,只可以用于识别任意颜色中色差较大的两种颜色(例如黑和白、白和绿,黑和红,白和蓝,黄和黑,黄和红,等等),数字量传感器带有电压比较器芯片和可调电位器;电压比较器芯片可以对接收管接收回来的信号进行放大处理,然后经过电位器功率调制,可实现识别灵敏度(或探测距 离)的调节,一般是探测到灰度值高(指接近于白色)的时候输出低电平,而探测到灰度值低(指接近于黑色)的颜色时则输出高电平;数字量为高低电平1和0两种信号状态输出,可以简化程序。灰度传感器原理图如图4.4所示。
图4.4 灰度传感器原理图
5、系统软件设计
系统软件设计部分分为主函数、循迹调速函数、电机控制函数、定时器中端函数、按键扫描函数、前往各个房间函数、延时函数等部分组成。
在检测到小车在红线上时开放按键许可,共8个按键、三个前往近中远病房函数,通过按下的按键确认对应函数前往不同病房。具体函数实现在后文展示。软件逻辑设计图如图5.1所示。
6、系统测试及结果分析
6.1 系统指标参数
6.1.1 STC89C52指标参数
1、电源电压:5.5V~3.3V
2、工作频率:40MHz
3、512字节RAM,8K ROM
6.1.2 灰度传感器参数
①额定电压:直流电源5.0V
②检测高度:1-3cm
6.1.3 电机驱动模块参数
①输出电压:5.0V
②输入电机电压:5.0~36.0V
③输出驱动电流:2A
6.2 测试内容与方法
6.2.1 软件测试
在Keil5中进行程序的编译,检查程序是否有错误。通过不断测试调整,使得最终程序“0 Error(s), 0 Warning(s)”。将软件代码烧录进去后在跑道上进行实测,测试小车是否能检测房间、正确循线,对红黑色块的检测是否灵敏,在十字路口的识别和转弯情况 ,偏移轨迹后能不能返回轨道,还有检测、停车位置的准确度。如果有偏差,调整代码再反复调试。
6.2.2 硬件测试
通电前先检查连线,组装好电路后对照电路图按一定的顺序逐级检查电路连线,排查电源是否接错,电源与地线是否短路,二极管方向和电容正负是否接反,焊点是否焊牢,等等。通电后观察是否有冒烟、异常气味、元器件发烫等异象。
先不加信号,测量各级直流工作电压和电流是否正常,一般对电路的静态工作点调试。 加上输入信号,观测电路输出信号是否符合要求。
6.3 实物外观
将小车按照电路图进行组装后,小车实物外观如图6.3所示。
图6.3 小车实物图
6.4 实物功能测试
本设计的小车功能是从药房出发,分别到近端、中端、远端病房并
返回药房,去和返回的时间均不超过40s。
近端病房:送给小车一个近端病房的房间号,小车检测后循红线在第一个十字路口右转准确到达病房,小车走过终点虚线后停车,1s后倒车并前进,检测到十字路口后转弯返回药房。过程用时小于40s。
中端病房:送给小车中端房间号,小车在第二个十字路口左转,到达病房检
测到超过虚线后停车,1s后倒车并前进,检测到十字路口后左转返回药房。过程用时小于40s。
远端病房:送给小车远端房间号,小车在第三个T字路口左转,第四个T字路口左转,到达病房检测到超过虚线后停车,1s后直行倒车,检测到十字路口后转弯按原路返回药房。过程用时小于40s。
6.5 测试结果分析
经过多次测试调试,整体测试结果能够达到题目要求和预期效果,并且速度远超题目所要求的40s的要求,完美的实现了整个送药的功能。
在测试过程中,小车在跑道上跑的时候车轮碾压会使跑道鼓起反光,或
者跑道所在环境光线的强度变化以及跑到表面脚印等使传感器检测不太准确,以致小车不能成功到达房间。经多次修改程序增加延迟检测以及短时间多次检测的方式可以减小极短时间的干扰。
通过多次对小车整体测试,小车性能达到设计要求,对于环境干扰有较强抵抗能力。经多次测试均能前往目的病房并原路返回,在巡线过程中未出现冲出轨道的情况,转弯和掉头时也能正确在转至中间传感器到达红线时停止。
7、结论
在这次课程实践中,我充分的来利用了自己上大学以来学过的知识,同时加强了自己的动手能力,一切书本上的知识都应该转换成实践才有用处,所以我们应该更加重视起来实践能力,才能充分的运用自己所学的知识。当然小车还有不足之处,比如,模块性能有待提高,选用性能更强、更稳定的芯片等,功能比较少。我们应该更加的深入学习,掌握更多专业相关知识,才能做得更好,成为行业中的佼佼者。
如还需相关资料,可私信我获得,感谢支持
代码:
#include<reg52.h>
#include<intrins.h>
unsigned char i1=0,i2=0;
unsigned char Left_PWM=50,Right_PWM=50;
/*L298N驱动引脚*///
sbit ENA=P1^0;//L298N使能端 左轮
sbit ENB=P1^1;//L298N使能端 右轮
sbit IN1=P1^2;
sbit IN2=P1^3;
sbit IN3=P1^4;
sbit IN4=P1^5;
/*五路灰度传感器*///
sbit IRN1=P2^0;//L2左2
sbit IRN2=P2^1;//L1左1
sbit IRN3=P2^2;//M中间 //黑高白低
sbit IRN4=P2^3;//R1右1
sbit IRN5=P2^4;//R2右2
//按键1~8//
sbit KEY1=P0^0;
sbit KEY2=P0^1;
sbit KEY3=P0^2;
sbit KEY4=P0^3;
sbit KEY5=P0^4;
sbit KEY6=P0^5;
sbit KEY7=P0^6;
sbit KEY8=P0^7;
/
void Time_r0_Init();//定时器装载
void Right_go();//右轮前进
void Right_back();//右轮后退
void Right_stop();//右轮停止
void Left_go();//左轮前进
void Left_back();//左轮后退
void Left_stop();//左轮停止
void Car_go();//小车前进
void Car_back();//小车后退
void Car_stop();//小车停止
void turn_around();//小车掉头
void Left_Z();//左直角转
void Right_z();//右直角转
void turn();//判断转弯情况
void KEY();//按键扫描函数
///
void go_to_near(char a1);//去近端病房 0左1右
void go_to_mid(char a2);//去中端病房 0左1右
void go_to_distant(char a3);//去远端病房 0左左1左右2右左3右右
void delay(unsigned char delay_t);//延时函数
void main(void)
{
P2=0xFF;
Time_r0_Init();
while(1)
{
KEY();
}
}
void Time_r0_Init() //设置定时器0
{ //通过设置定时器0来调制小车的转速
TMOD=0x01; //选择16位的定时器
TH0=(65536-100)/256;
TL0=(65536-100)%256;
ET0=1; //开放定时器中断0
TR0=1;
EA=1;
}
void delay(unsigned char delay_t)
{
unsigned char delayi, delayj;
for(delayi=delay_t;delayi>0;delayi--)
{
for(delayj=110;delayj>0;delayj--);
}
}
void KEY()
{
while(1)
{
if(KEY1==0)
{
delay(20);
if(KEY1==0)
{
go_to_near(0);//去近端左边
break;
}
}
else if(KEY2==0)
{
delay(20);
if(KEY2==0)
{
go_to_near(1);//去近端右边
break;
}
}
else if(KEY3==0)
{
delay(20);
if(KEY3==0)
{
go_to_mid(0);//去中端左边
break;
}
}
else if(KEY4==0)
{
delay(20);
if(KEY4==0)
{
go_to_mid(1);//去中端右边
break;
}
}
else if(KEY5==0)
{
delay(20);
if(KEY5==0)
{
go_to_distant(0);//去远端左左
break;
}
}
else if(KEY6==0)
{
delay(20);
if(KEY6==0)
{
go_to_distant(1);//去远端左右
break;
}
}
else if(KEY7==0)
{
delay(20);
if(KEY7==0)
{
go_to_distant(2);//去远端右左
break;
}
}
else if(KEY8==0)
{
delay(20);
if(KEY8==0)
{
go_to_distant(3);//去远端右右
break;
}
}
}
}
void Right_back()
{
IN3=0;
IN4=1;
}
void Right_go()
{
IN3=1;
IN4=0;
}
void Right_stop()
{
IN3=0;
IN4=0;
}
void Left_back()
{
IN1=0;
IN2=1;
}
void Left_go()
{
IN1=1;
IN2=0;
}
void Left_stop()
{
IN1=0;
IN2=0;
}
void Car_go()
{
Right_go();
Left_go();
}
void Car_stop()
{
Right_stop();
Left_stop();
}
void Car_back()
{
Right_back();
Left_back();
}
void Right_z()//右直角转弯
{
Right_PWM=0;
Left_PWM=30;
while(1)
{
if(IRN5==1)
{
delay(10);
if(IRN5==1)
{
while(1)
{
if(IRN5==0&&IRN3==1)
{
Right_PWM=30;
break;
}
}
break;
}
}
}
}
void Left_z()
{
Right_PWM=30;
Left_PWM=0;
while(1)
{
if(IRN1==1)
{
delay(10);
if(IRN1==1)
{
while(1)
{
if(IRN1==0&&IRN3==1)
{
Left_PWM=30;
break;
}
}
break;
}
}
}
}
void turn_around()//小车掉头
{
Car_stop();
delay(100);
Right_go();
Left_back();
for(i2=0;i2<3;i2++)
{
delay(5000);
}
while(1)
{
if(IRN1==1)
{
delay(10);
if(IRN1==1)
{
while(1)
{
if(IRN3==1)
{
delay(10);
if(IRN3==1)
{
break;
}
}
}
break;
}
}
}
Car_stop();
delay(100);
}
void go_to_near(char a1)//去近端病房
{
char i6=0;
while(1)
{
if((IRN1==0&&IRN5==0)&&(IRN3==1||IRN2==1||IRN4==1))
{
while(1)
{
if(a1==0)
{
if(KEY1==0)
{
delay(20);
if(KEY1==0)
{
Car_go();
delay(30);
break;
}
}
}
if(a1==1)
{
if(KEY2==0)
{
delay(20);
if(KEY2==0)
{
Car_go();
delay(30);
break;
}
}
}
}
break;
}
}
while(1)
{
turn();
if(IRN1==1&&IRN5==1)
{
delay(15);
if(IRN1==1&&IRN5==1)
{
delay(15);
if(IRN1==1&&IRN5==1)
{
while(1)
{
if(IRN1==0&&IRN5==0)
{
if(a1==1)
{
Right_z();
}
else
{
Left_z();
}
i6++;
break;
}
}
}
}
}
if(i6>=1)
{
i6=0;
break;
}
}
while(1)
{
turn();
if(IRN1==0&&IRN2==0&&IRN3==0&&IRN4==0&&IRN5==0)
{
delay(20);
if(IRN1==0&&IRN2==0&&IRN3==0&&IRN4==0&&IRN5==0)
{
delay(20);
if(IRN1==0&&IRN2==0&&IRN3==0&&IRN4==0&&IRN5==0)
{
Car_stop();
delay(100);
break;
}
}
}
}
turn_around();
Car_go();
while(1)
{
turn();
if(IRN1==1&&IRN5==1)
{
delay(15);
if(IRN1==1&&IRN5==1)
{
delay(15);
if(IRN1==1&&IRN5==1)
{
while(1)
{
if(IRN1==0&&IRN5==0)
{
if(a1==1)
{
Left_z();
}
else
{
Right_z();
}
i6++;
break;
}
}
}
}
}
if(i6==1)
{
i6=0;
break;
}
}
while(1)
{
turn();
if(IRN1==0&&IRN2==0&&IRN3==0&&IRN4==0&&IRN5==0)
{
delay(15);
if(IRN1==0&&IRN2==0&&IRN3==0&&IRN4==0&&IRN5==0)
{
delay(15);
if(IRN1==0&&IRN2==0&&IRN3==0&&IRN4==0&&IRN5==0)
{
Car_stop();
delay(100);
turn_around();
}
}
}
}
}
void go_to_mid(char a2)//去中端病房
{
char i7=0;
while(1)
{
if((IRN1==0&&IRN5==0)&&(IRN3==1||IRN2==1||IRN4==1))
{
while(1)
{
if(a2==0)
{
if(KEY3==0)
{
delay(20);
if(KEY3==0)
{
Car_go();
delay(30);
break;
}
}
}
if(a2==1)
{
if(KEY4==0)
{
delay(20);
if(KEY4==0)
{
Car_go();
delay(30);
break;
}
}
}
}
break;
}
}
//
while(1)
{
turn();
if(IRN1==1&&IRN5==1)
{
delay(15);
if(IRN1==1&&IRN5==1)
{
delay(15);
if(IRN1==1&&IRN5==1)
{
while(1)
{
if(IRN1==0&&IRN5==0)//等待通过十字路口
{
delay(20);
if(IRN1==0&&IRN5==0)
{
i7++;
break;
}
}
}
}
}
}
if(i7==1)
{
i7=0;
break;
}
}
///
while(1)
{
turn();
if(IRN1==1&&IRN5==1)
{
delay(15);
if(IRN1==1&&IRN5==1)
{
delay(15);
if(IRN1==1&&IRN5==1)
{
while(1)
{
if(IRN1==0&&IRN5==0)
{
if(a2==1)
{
Right_z();
}
else
{
Left_z();
}
i7++;
break;
}
}
}
}
}
if(i7==1)
{
i7=0;
break;
}
}
/
while(1)
{
turn();
if(IRN1==0&&IRN2==0&&IRN3==0&&IRN4==0&&IRN5==0)
{
delay(15);
if(IRN1==0&&IRN2==0&&IRN3==0&&IRN4==0&&IRN5==0)
{
delay(15);
if(IRN1==0&&IRN2==0&&IRN3==0&&IRN4==0&&IRN5==0)
{
Car_stop();
delay(1000);
break;
}
}
}
}
turn_around();//掉头
Car_go();
while(1)
{
turn();
if(IRN1==1&&IRN5==1)
{
delay(15);
if(IRN1==1&&IRN5==1)
{
delay(15);
if(IRN1==1&&IRN5==1)
{
while(1)
{
if(IRN1==0&&IRN5==0)
{
if(a2==1)
{
Left_z();
}
else
{
Right_z();
}
i7++;
break;
}
}
}
}
}
if(i7==1)
{
i7=0;
break;
}
}
while(1)
{
turn();
if(IRN1==1&&IRN5==1)
{
delay(15);
if(IRN1==1&&IRN5==1)
{
delay(15);
if(IRN1==1&&IRN5==1)
{
while(1)
{
if(IRN1==0&&IRN5==0)//等待通过十字路口