基于单片机的自动分拣小车的设计
第1章、课题内容
1.系统应用单片机的基础功能,设计相关的电子电路,搭配合适的电子器件和传感器技术等,实现自动分拣的功能。
2.对方案的总体成份进行论证和设计。
3.根据总体功能进行硬件设计和软件设计。
4.对硬件系统和软件系统进行调试与检测。
第2章、自动分拣小车的总体方案设计
2.1 总体系统的任务描述
该设计的任务是:小车来到上货点并放上货物后,经过扫码识别和服务器判断,给小车发送指令信息,接收到后小车能够将货物分拣到指定位置。小车总体功能结构如图2-1所示。
本设计需要的模块有:循迹避障小车模块、定位卸货模块、无线交互模块。
2.2主控制器单片机的选择
本设计采用STC89C52RC型号的单片机,这款单片机芯片为STC公司生产的产品,为8051内核,内含Flash E2PROM存储器,为CMOS产品,芯片内部存续存储空间的大型为8KB。单片机内部RAM随机读写存储器大小为512B。选用的工业级双列直插式封装型号的单片机[8]。
除此之外,STC89C52单片机具有以下标准功能:4个外部中断,看门狗定时器,MAX810复位电路,16 位定时器3个,全双工串行口,7向量4级中断结构一个。可降至0Hz 静态逻辑操作,支持2种软件可选择节电模式。空闲时CPU 停止工作,可允许串口、RAM、中断、定时器/计数器继续工作。掉电保护模式下,冻结振荡器,RAM里的内容被保存,一切工作停止,直到下一个硬件复位或中断[9]。
51系列单片机在市面上综合的成本较低,相比于stm32,其开发的简单应用更为低价。对比其他八位单片机,51系列有乘法和除法指令,RAM区间有一个双重功能的地址区间,从硬件到软件有一套完整的位处理器。因此在市场中还是占有很大份额,在未来一段时间内将有大量市场[10]。
第3章 循迹避障小车的硬件设计
实现小车基础的循迹避障功能,需要用到驱动系统提供小车行驶的动力,和循迹避障系统判断小车执行的行驶指令。本设计分别对两个模块进行论证以完成该硬件系统的设计。
3.1驱动系统硬件设计
根据本设计的思路,由单片机根据所检测的数据进行判断,然后执行对应的功能,实现对自动分拣小车的智能控制。在此过程中,动力系统为小车提供动力,以实现在各功能中的小车的移动。
3.1.1 动力系统的选用
方案一:V-M直流调速系统。V-M双闭环直流调速系统是工业生产中使用最多的传动装置之一,常用于机床、冶金等许多机械领域。具有调速范围广、稳定性好、响应快、抗干扰能力强等优点,在直流调速系统中最为成熟。其缺点是只能单向导通,运行要求条件严格,且对其他电子设备可能会造成干扰[11]。
方案二:PWM直流调速系统。PWM直流电机普遍用于单片机或其他微处理器当中,是利用数字输出对模拟电路进行控制的技术,节省能源,成本较低。实现了用数字方式控制模拟信号,降低了成本和功耗[12]。
分析V-M系统和PWM系统,对比两者,PWM具有以下优点:
(1) PWM系统功耗较低,在同样的电流下,发热损耗比V-M系统低。
(2) PWM系统开关频率高,快速响应性能好,调速范围高[12]。
(3) PWM系统控制较为方便,设计简单,适用于单片机小板的搭配使用。
(4) 可设计成可逆型PWM系统,实现电机反转,小车倒退。
综上所述,采用可逆PWM直流调速系统。安装简单,控制方便。并使用市面上常见的L298N驱动芯片,驱动电机工作
3.1.2 L298N芯片的驱动原理
L298N芯片是一种高电压大电流的电机驱动芯片,工作电压较高,含H型全桥式驱动器两个,可用于驱动直流、步进电机。采用TTL逻辑电平信号控制,能直接连接到单片机接口。如图3-1所示,为芯片的接脚原理图。
直流电机没有正负极,但为了方便设计,我们模拟电机某同一端为正,另一端为负极。该设计将芯片的5、6、7、10、11、12接脚接到51单片机的P1.1、P1.0、P1.2、P1.4、P1.3、P1.5接口。芯片的2和3输出口接到左电机正负极,13、14接到右电机正负极。电机需要4节1.5V5号电池带动,外接一个电池盒用来放置电池。
当单片机P1.0信号为“1”,左电机输出电路开关打开,P1.1输出信号“1”,P1.2信号“0”,OUT1口输出一个VCC高电平,OUT2输出低电平0V,左电机正转。同样,P1.3信号“1”,P1.4信号“0”,P1.5信号“1”,则右电机反转。这样便实现了小车前进。在程序中,可以这样定义:
(1)执行左电机正转:{ P1_0 = 1 ; P1_1 = 1 ; P1_2 = 0 }
(2)执行左电机停转:{ P1_0 = 0 ; P1_1 = 0 ; P1_2 = 0 }
(3)执行左电机反转:{ P1_0 = 1 ; P1_1 = 0 ; P1_2 = 1 }
(4)执行右电机正转:{ P1_3 = 1 ; P1_4 = 1 ; P1_5 = 0 }
(5)执行右电机停转:{ P1_3 = 0 ; P1_4 = 0 ; P1_5 = 0 }
(6)执行右电机反转:{ P1_3 = 1 ; P1_4 = 0 ; P1_5 = 1 }
3.2 循迹避障系统的硬件设计
3.2.1 循迹系统的硬件设计
本设计中采用的红外循迹模块为TCRT5000红外反射传感器。该传感器的工作原理是依靠红外发射二极管不断发射红外线,经物体反射回接收管。接收到的信号经过比较器电路处理,输出数字信号。使用者可通过旋钮调节检测的灵敏程度,在该设计中,用于检测是否行驶在浅色地面布置好的黑线上,进而通过程序判断调整电子旋转来保持循迹行驶[13]。
如图3-2为TCRT5000红外反射传感器原理图,图中红外对接管用于发射和接收红外信号。平时电源接通时,电源指示灯有电流通过,指示灯亮。图中10K的可调电阻就是芯片上的可调旋钮,用于改变阻值而调整感应的灵敏度。LM3939是一个比较器,当正端电压大于负端,输出高电平,开关指示灯不亮。当正端电压小于负端电压,输出低电平0,开关指示灯形电流流通,灯亮。在实际中,当红外线被黑线吸收,接口管未接收到足够红外线,接收管未接通,则比较器正端电压一定大于负端电压,输出高电平。当传感器在白色地面上,红外接收管接收到红外线,接收器形成通路,比较器正端电压为0。而负端连接滑动变阻器,其电压一定大于0,所以比较器输出0,开关指示灯亮。根据其检测到黑线输出1,未检测到黑线输出0的特点,左循迹传感器输出连接单片机P3.6接口,右循迹传感器输出连接P3.7接口,在代码中可这样设计:
(1)判断两边未检测到黑线:{P3_6 == 0 ; P3_7 == 0 } 执行 前行
(2)判断左边检测到黑线:{P3_6 == 1 ; P3_7 == 0 } 执行 左转
(3)判断右边检测到黑线:{P3_6 == 0 ; P3_7 == 1 } 执行 右转
(4)判断两边都检测到黑线:{P3_6 == 1 ; P3_7 == 1 } 执行 停车
3.2.2 避障系统的硬件设计
避障系统和循迹系统所采用的模块原理一样,也是采用同样的红外发射和红外接收的方法工作。当发射管发射出的红外线遇到障碍物,反射回来的红外线被接收管接通。比较器输出低电平0;当未遇到障碍物,接收管未接通,比较器输出高电平1。该模块检测距离约2~30cm,检测角度35度,可通过调节旋钮滑动变阻器来调整检测的距离。
本设计采用红外发射式传感器仅用于检测障碍物,市面上虽有更好的超声波传感器等模块,但由于小车模拟环境是在仓库等工作环境,甚至在无人的仓库中工作,所以用红外检测障碍物就已经足够了。另外,使用自动避障行驶容易造成仓库工作中小车群组的意外失误,可能一个小车避障成功,其他的就得重新规划路线,增大了管理系统的负担。所以为了方便方便总控系统的线路计算,本设计检测到障碍物后只执行停车指令,用红外检测模块就已足够。
本设计中左避障传感器输出接P3.4,右避障输出端接P3.5,在程序中可以这样设置:
(1)判断左方遇到障碍:{ P3_4 == 0 ; P3_5 == 1 } 执行 停车
(2)判断右方遇到障碍:{ P3_4 == 1 ; P3_5 == 0 } 执行 停车
(3)判断两方都遇到障碍:{ P3_4 == 0 ; P3_5 == 0 } 执行 停车
第4章 定位和卸货系统的硬件设计
定位卸货系统由定位系统和舵机卸货系统构成,定位系统用来判断小车是否行驶到指定的卸货点,舵机卸货系统则是用于操作货物卸下。
4.1 定位系统的硬件设计
4.1.1 光敏电阻传感器功能的设计
定位系统采用光敏电阻传感器和LED发光二极管搭配使用组成定位点的识别系统,设计中使用4个LED发光二极管和一个光敏电阻传感器,实际使用方法如图4-1所示,在路线中设置四个点,各点适当位置放置一个LED发光二极管,小车侧边安装一个光敏电阻传感器。四个定位点位置模拟仓库中的上货点、A区、B区、C区。当小车到达第一个点,光敏电阻传感器感应到光线达到标准,输出低电平。然后判断wifi信号是否存入变量中,如果wifi变量为0,则小车停车。等上货后,wifi变量不等于0,光敏信号为0,则小车循迹前进。当光敏电阻传感器感应不到标准强度光线后,输出信号“1”,此时对比变量,给另一个变量值加一。如此循环,直到小车到达wifi信息所对应的定位点后,停车卸货。
4.1.2 光敏电阻传感器的工作原理
如图4-2所示,光敏电阻传感器的工作原理和红外循迹避障传感器的原理有异曲同工之妙。当光线强度达到设定的阈值后,光敏电阻接通,LM393比较器的正端电压小于负端电压,输出低电平信号。当光强未达到设定的阈值,比较器正端电压大于负端,输出高电平信号。常见的光敏电阻传感器有两种接脚规格,一种是有AO模拟量输出口的四线制模块,另一种是只有输出电平信号的DO端三线制模块。显然,在本设计中,仅使用了光敏电阻传感器检测是否有光强这一特点,直接使用DO输出的电平信号参与判断。因此,若成本需要,可只选择三线制模块。
在本设计中,使用P2.0接口连接光敏电阻的DO口,当光强达到阈值传感器输出“0”,光强未达到阈值输出“1”,因此程序中可这样设计:
(1)判断到达定位点: {P2_0 == 0 }
(2)判断未到定位点:{P2_0 == 1 }
4.2 卸货系统的硬件设计
4.2.1 舵机功能的设计
本设计采用SG90 9g型号的舵机,用于执行卸货动作。当卸货条件达成时,舵机子程序驱动舵机旋转90度,将放置在称重模块上的货物推下。动作完成后,卸货功能完成,舵机复位,并将变量归零,程序又开始循环变量判断,小车继续循迹绕回初始点停车。如图4-3为小车第二层的舵机安装平面图,将wifi模块放到第二层避免其他干扰。
4.2.2 舵机的工作原理
驱动舵机转动需要通过信号线给舵机一个周期性的直流偏置电压,舵机会根据输入的电压和基准电压进行比较,得到电压差输出,根据电压差正负确定旋转方向。另外,根据输入方波的周期来判断旋转的角度,该参数t使用定时器0来计数,该单片机晶振计时为每次0.1ms,计数的次数决定参数t的周期,参数t的说明如表4.1所示。
让设计中舵机旋转90度,需要输入一个周期为1.5ms的方波,待转动完成后,再输入一个为0.5ms的方波,电机开始逆转复位。
如图4-4,设计中将舵机信号线接到单片机P2.7接口,通过该接口输出的方波控制舵机旋转90度和复位。
第5章 无线传输与称重系统硬件设计
无线传输系统意为通过无线连接,客户机与服务机进行信息交换,并对传输的数据进行处理。本系统分为WIFI无线模块、红外遥控模块、称重模块。
5.1 WIFI无线模块的硬件设计
5.1.1 WIFI传输功能的设计
该设计采用的是市面上普遍应用的ESP8266WIFI模块,此模块有三种工作模式:Station客户端模式、AP接入点模式、二者结合模式[14]。如图5-1所示,该设计使用android手机app扫描货物上的条形码或者二维码,识别出数字后,根据选择的区间范围发送给esp8266模块数据,单片机获取传来的wifi数据,并称重上传数据,然后判断在哪个LED定位点停车卸货。此系统需要用到ESP8266模块、手机APP、打印好的条形码/二维码来构成WIFI无线传输系统。使用wifi模块作为传输系统模块可以很好的适应当下wifi发展的进程,如今的工厂、仓库、商店,无处不在使用wifi无线网络来搭建互联环境。使用该模块的传输方式,能直接应用于有wifi网络的工作场景。本设计使用手机app控制,对比蓝牙传输方式,该方法也能使用同一网络下的服务器进行数据收发,而不像蓝牙模块,还需要搭配手机蓝牙或者蓝牙收发器才能使用。
5.1.2 ESP8266模块的工作原理
如图5-2所示,在正常工作的模式下,ESP8266的3、5、7引脚接电源正极,且必须为3.0V~3.6V,最佳为3.3V,所以电压过高可以串联一个10K电阻。芯片2引脚接地,芯片的发送端TXD接口接单片机的接收端RXD接口,芯片的接收端RXD接口接单片机的发送端TXD接口。使用时,通过单片机程序发送指令,让芯片进入指定模式,这里可以通过if语句判断0和1来执行发送的指令内容,建立好TCP协议后,便可以收发数据。
5.2 红外遥控系统的硬件设计
5.2.1 红外遥控功能的设计
红外遥控功能的设计意为人工处理临时问题时的手动控制方式,在单片机初始化后,或者中断期间通过按钮切换自动和手动模式,设计采用的是IR1838型号的红外接收管。红外遥控通过遥控器发射的红外信号,让小车实现遥控停车、直行、转弯、倒退四个简单动作,使用通用的21键红外遥控器来进行遥控操作。
5.2.2 红外遥控工作原理
使用遥控器时,当按键按下超过36ms,振荡器让芯片激活,红外发射口发射出一组108ms的脉冲。该脉冲由9ms的引导码,4.5ms的结果码,9ms18ms的低八位地址码,9ms18ms的高八位地址码,9ms~18ms的八位数据码和该数据码的反码构成[16]。单片机接收到后,对信号进行判断。检测到非合法红外信号则跳出,检测到合法的红外信号,表明接收到遥控器的信号。然后等待9ms低电平过去,再判断是否为连发码,若为连发码则跳出,直到检测到是引导码,再等待4.5ms结果码高电平过去,之后接收到的便是用户码。开始对用户码进行解码。随后对已解码的用户码进行判断,判断成功则执行设定的程序,即小车的前进、转弯、倒退。如图5-3所示,为遥控器每个按键对应的编码数值,经协议解码后,单片机可以获得对应的编码数值。
如图5-4所示,为红外接收模块的引脚图,设计中将信号输出口接到单片机P3.2接口。程序根据编码的判断,执行对应设计好的程序。代码中使用了五个编码信息:0x46、0x15、0x44、0x43、0x40。在程序中采用for循环运算判断是否有这五个指令常量数组中的其中一个,例如检测到0x46编码,则执行前进子程序。
5.3 称重模块的硬件设计
5.3.1 称重传感器功能的设计
称重传感器选择的是桥式压力应变传感器和型号为hx711的24位A/D转换器芯片。hx711模块专用于高精度电子秤,其集成了所需的外围电路,抗干扰能力强,能直接连接单片机使用。当单片机判断获得的wifi数据符合后,执行称重功能,获得24位AD信号后,通过公式计算重量。再将条码数据和计算结果发送到手机端,手机程序根据获得的数据在列表框中加入数值显示。随后小车开始行驶,开始下一流程。图5-5为称重模块的功能结构图。
5.3.2 压电传感器工作原理
压电传感器是一种有源传感器,其装有的压电电阻材料受力后会产生电荷,通过计算信号而获取重量值数据。本设计采用的是HX711 A/D模块作为该系统的A/D转换模块,可以通过数字输出DOUT接口直接发送至单片机。PD_SCK数字输入接口,用于断电控制和串口时钟输入。711 模块A 通道带有128 倍信号增益,可以将5mV 的电压放大128 倍,然后采样输出24bit AD 转换的值,单片机通过指定时序将24bit 数据读出。当执行称重时,单片机控制PD_SCK端从高电平变为低电平,HX711芯片输出25个脉冲信号,其中24位信号由单片机计算出重量数值。假设重力为A Kg,(x<5Kg),测量出来的AD 值为y,传感器输出,发送给AD 模块儿的电压为A Kg * 4.3mV / 5Kg = 0.86A mV。经过128 倍增益后为128 * 0.86A = 110.08AmV。转换为24bit 数字信号为110.08A mV * 224 / 4.3V = 429496.7296A。因此得出A = y / 429496.7296,计算公式便是Weight_Shiwu = (unsigned long)((float)Weight_Shiwu/429.5) [15]。
6章 程序设计与调试
6.1 总程序结构与主函数的设计
6.1.1主要程序结构框图
6.1.2 主函数的设计
void main() //主程序入口
{
bit ExeFlag=0; //定义可执行位变量
EX0=0; //同意开启外部中断1
IT0=1; //设定外部中断1为低边缘触发类型
EA=1;
TMOD=0X01;
TH0= 0XFc;//1ms定时
TL0= 0X18;
TR0= 1;
ET0= 1;
Init_Tim1(); //串口初始化子程序
#if(0) //里面数值填0代表是手机与Wifi模块直连模式,里面数值改为1代表是手机与家庭路由器连接
SendString("AT+CWMODE_DEF=1\r\n"); //进入模式1 Station
HAL_Delay(100); //延时100ms
SendString("AT+CWJAP_DEF=\"SCY\",\"bilibili\"\r\n"); //连接家庭路由器
HAL_Delay(3000);
#else
SendString("AT+CWMODE_DEF=2\r\n");//进入模式2 SoftAP
#endif
HAL_Delay(100);//延时100ms
SendString("ATE0\r\n");//关闭回显
HAL_Delay(100);
SendString("AT+CIPMUX=1\r\n");//wifi模块多连接模式
HAL_Delay(100);
SendString("AT+CIPSERVER=1,333\r\n");//建立TCP服务器
while(1) //程序主循环
{
if(P3_3 == 0)
{
delay_nms(10);
if(P3_3 == 0)
{
temp++ ; while(!P3_3); } //检测到按钮按下
BUZZ=0; //蜂鸣器发出“滴”声响。
delay(50);
BUZZ=1; //响50ms后关闭蜂鸣器
}
if(temp > 2) //如果按下超过2次,即第三次还原成1
{
temp = 1; }
switch(temp)
{
case 1: Car_Avoidance();EX0 = 0;break; //执行自动模式子程序
case 2: BUZZ = 1; EX0 = 1;break; //打开中断,解码红外遥控指令
}}}
6.2 测试与调试
6.2.1 循迹避障模块的测试记录
(1)试验所用模块:89C52RC单片机主板、电机驱动模块、直流电机、小车底盘和红外避障模块。
(2)试验过程:安装好图6-2所示小车,编写红外循迹避障程序,烧写进单片机试验。
(3)试验结果:前方有障碍物时小车停车不及时。
(4)解决方法:将障碍物检测放在循迹条件父级,让小车需要先满足无障碍才能继续循迹判断和电机转动。
6.2.2 无线传输模块的测试记录
(1)试验所用模块:已测试好的模块、红外无线遥控模块和wifi无线模块。
(2)试验过程:加装图6-3所示的无限传输模块,烧写遥控代码测试和wifi串口测试。
(3)试验结果:遥控正常,小车可以根据红外信号做出动作。Wifi模块正常,但使用手机串口调试发送指令后未能成功打开蜂鸣器
(4)解决方法:WiFi模块传输的数据是字符串,程序判断的条件也应该是字符串的判断。
6.2.3 自动分拣小车的整体测试记录
(1)试验所用模块:测试好的所有模块、安装好舵机、第二层车身板、LED灯4个、制作好的手机APP和印有二维码的模拟货物,搭建好演示环境。
(2)试验过程:完善代码并烧写进小车,分别测试遥控模式和自动模式是否达到预期要求。
(3)试验结果:遥控模式正常,切换自动模式正常,定位卸货系统正常,但出现电机驱动电压不足的问题,小车行驶不流畅。
(4)解决方法:寻找是否有短路或者串口冲突的问题,或者使用pwm调速,增大电机驱动电压。
参考文献
[1]黄启明.自动分拣系统及其应用前景分析[J/OL].百度文库.2002. https://wenku.baidu.com/view/66676dbdfab069dc50220155.html
[2]最低工资调整趋势及最低工资调整对制造企业的影响研究[J/OL].百度文库.2012. https://wenku.baidu.com/view/b7dee826482fb4daa58d4b8c.html
[3]杨雪.2019年中国自动分拣机器人市场调研报告-行情分析与发展趋势研究[J/OL].观研网.2019. http://www.gyii.cn/baogao/jixieshebei/zhuanyongjixie/2019/0320/230335.html
[4]国内外自动物料分拣机器人研究现状[J/OL].六维论文网.2018. http://www.lwfree.cn/yanjiu/20180507/15068.html
[5]李娜娜,王莉,戴建明.卷烟配送中心自动分拣系统的规划设计[J/OL].蚂蚁文库.2008. http://www.mayiwenku.com/p-5346607.html
[6]gfl2739122.基于arduino的分拣小车设计[D].道客巴巴.2018. http://www.doc88.com/p-2856482187231.html
[7]自动分拣系统国内外研究现状和发展趋势[J/OL]. 六维论文网.2019. http://www.lwfree.cn/yanjiu/20180403/12308.html
[8]郭天祥.新概念51单片机C语言教程[M].北京:电子工业出版社.2009
[9]STC89C52RC 单片机中文资料[J/OL].豆丁网.2012. https://www.docin.com/p-328439472.html
[10]lhl545545.51单片机优缺点及应用领域介绍[J/OL].电子发烧友网.2018. http://www.elecfans.com/emb/danpianji/20180910772848.html
[11]碰撞的艺术.不可逆V-M双闭环直流调速系统设计[J/OL].百度文库.2019. https://wenku.baidu.com/view/a5a57f9609a1284ac850ad02de80d4d8d15a0195.html
[12]bboyfeiyu_g. PWM控制直流电机[J/OL].百度文库.2018. https://wenku.baidu.com/view/cbb0df5ef02d2af90242a8956bec0975f465a4ce.html
[13]anazel.红外循迹以及红外避障模块电路设计[J/OL].新浪博客.2018. http://blog.sina.com.cn/s/blog_49677f890102xbye.html
[14]狗头青的征战史.ESP8266-3种模式用法[J/OL].CSDN学院.2018. https://blog.csdn.net/weixin_40504976/article/details/84283320
[15]lyj159 .hx711管脚接口应用说明[J/OL].电子发烧友.2017 . http://www.elecfans.com/dianyuan/566982.html
[16]zxnsirius.51单片机PWM双舵机控制详解[J/OL].CSDN学院.2016. https://blog.csdn.net/zxnsirius/article/details/51002580
[17]心尘海底4.分拣系统应用现状及趋势[J/OL].百度文库.2018. https://wenku.baidu.com/view/f0f031db83c4bb4cf6ecd142.html
[18]文鸿涛. 手动压机生产线自动化改造技术[J/OL]. 百度文库, 2008. https://wenku.baidu.com/view/b6090f67773231126edb6f1aff00bed5b8f37367.html
[19]卯邓,谢文娣. 基于STM32F103ZET6的仓库智能运货车设计[J/OL]. 科技视界, 2018. http://www.cnki.com.cn/Article/CJFDTOTAL-KJSJ201807115.htm
[20]田军营,田博韬. 智能机器人设计速成[M].北京:科学普及出版社, 2017
附录一:主板原理图
附录二:C程序
/*****************************************************************
* 名称:自动分拣小车
/******************************************************************
头文件
******************************************************************/
#include<AT89X52.H> //包含51单片机头文件,内部有各种寄存器定义
#include <intrins.h>
#include <string.h>
#include <stdio.h>
#define uint unsigned int //重定义无符号整数类型
#define uchar unsigned char //重定义无符号字符类型
/****************************************************************************
** 电机驱动引脚定义
****************************************************************************/
#define Left_moto_pwm P1_0 //PWM信号端
#define Right_moto_pwm P1_3 //PWM信号端
#define Left_moto_go {P1_1=1,P1_2=0;} //左电机向前走
#define Left_moto_back {P1_1=0,P1_2=1;} //左边电机向后转
#define Left_moto_Stop {P1_1=0,P1_2=0;} //左边电机停转
#define Right_moto_go {P1_4=1,P1_5=0;} //右边电机向前走
#define Right_moto_back {P1_4=0,P1_5=1;} //右边电机向后走
#define Right_moto_Stop {P1_4=0,P1_5=0;} //右边电机停转
/****************************************************************************
** 避障引脚定义
****************************************************************************/
#define Left_bizhang_led P3_4 //左避障传感器
#define Right_bizhang_led P3_5 //右避障传感器
/****************************************************************************
** 循迹引脚定义
****************************************************************************/
#define Left_xunji_led P3_6 //左循迹传感器
#define Right_xunji_led P3_7 //右循迹传感器
/****************************************************************************
** 红外接收引脚定义
****************************************************************************/
sbit IR1838IN=P3^2;
/****************************************************************************
** 蜂鸣器引脚定义
****************************************************************************/
sbit BUZZ=P2^3;
/****************************************************************************
** 光敏电阻引脚定义
****************************************************************************/
sbit GMDZ=P0^1;
/****************************************************************************
** 舵机引脚定义
****************************************************************************/
unsigned int DJPWM_Value; //定义pwm值
unsigned char flag;
unsigned char order=0;
unsigned int rsd;
void DelayDJ(); //舵机延时函数声明
#define T2_PWMout P2_7 //舵机控制端
sbit KEY1=P3^4;
unsigned int PWMTimes=0;//高电平时间
unsigned char angle=0; //测试角度
bit SWdir=0;
void T2_Init();//初始化定时器2
void SetMotoangle(float angle);//设置舵机角度
/****************************************************************************
** 变量定义
****************************************************************************/
unsigned int LEDState = 0;
unsigned int LEDOOne = 0;
unsigned int LEDCount = 0;
unsigned int LEDBack = 0;
unsigned int WifiST = 0;
unsigned int XHXHXHXH = 0;
/****************************************************************************
** WIFI定义
****************************************************************************/
char Recive_table[20]=""; //接收缓冲,最大20个字节
char Recive_state = 0; //接收完成标志
void WIFI_Init(void);
void Uart_Init(void);
void ms_delay(int t);
void LED(void);
/****************************************************************************
** 变量定义
***************************************************************************
unsigned char pwm_val_left =0;//变量定义
unsigned char push_val_left =0;// 左电机占空比N/20
unsigned char pwm_val_right =0;
unsigned char push_val_right=0;// 右电机占空比N/20
bit Right_moto_stop=1;
bit Left_moto_stop =1;
unsigned int time=0;
uchar code RecvData[]={0x46,0x15,0x44,0x43,0x40};
uchar IRCOM[7];
unsigned char RunFlag=0; //定义运行标志位
unsigned char temp = 1;
void Delay100ms();
/****************************************************************************
** 舵机延时函数
****************************************************************************/
void DelayDJ()
{
unsigned char i, j;
i = 195;
j = 138;
do
{
while (--j);
} while (--i);
}
/****************************************************************************
** 延时函数
****************************************************************************/
void delay(unsigned int k)
{
unsigned int x,y;
for(x=0;x<k;x++)
for(y=0;y<2000;y++);
}
/****************************************************************************
** 延时函数
****************************************************************************/
void delayms(unsigned char x)
{
unsigned char i;
while(x--)
{
for (i = 0; i<13; i++) {
}
}
}
/****************************************************************************
** 延时函数
****************************************************************************/
void Delay1ms(unsigned int i)
{
unsigned char j,k;
do{
j = 10;
do{
k = 50;
do{
_nop_();
}while(--k);
}while(--j);
}while(--i);
}
/****************************************************************************
** 延时函数
****************************************************************************/
void Delay()
{
uint DelayTime=30000;
while(DelayTime--);
return;
}
/****************************************************************************
** 延时函数
****************************************************************************/
void delay_nus(unsigned int i)
{
i=i/10;
while(--i);
}
/****************************************************************************
** 延时函数
****************************************************************************/
void delay_nms(unsigned int n)
{
n=n+1;
while(--n)
delay_nus(900);
}
/****************************************************************************
** 小车前进函数
****************************************************************************/
void run(void)
{
push_val_left=20; //速度调节变量 0-20。。。0最小,20最大
push_val_right=20; //速度调节变量 0-20。。。0最小,20最大
Left_moto_go ; //左电机往前
Right_moto_go ; //右电机往前
}
/****************************************************************************
** 小车后退函数
****************************************************************************/
void backrun(void)
{
push_val_left=20; //速度调节变量 0-20。。。0最小,20最大
push_val_right=20; //速度调节变量 0-20。。。0最小,20最大
Left_moto_back; //左电机往后
Right_moto_back; //右电机往后
}
/****************************************************************************
** 小车左转函数
****************************************************************************/
void leftrun(void)
{
push_val_left=20; //速度调节变量 0-20。。。0最小,20最大
push_val_right=20; //速度调节变量 0-20。。。0最小,20最大
Left_moto_back; //左电机往后
Right_moto_go; //右电机往前
}
/****************************************************************************
** 小车右转函数
****************************************************************************/
void rightrun(void)
{
push_val_left=20; //速度调节变量 0-20。。。0最小,20最大
push_val_right=20; //速度调节变量 0-20。。。0最小,20最大
Right_moto_back; //右电机向后
Left_moto_go; //左电机向前
}
/****************************************************************************
** 小车停车函数
****************************************************************************/
void stop(void)
{
Left_moto_Stop; //左电机往前
Right_moto_Stop; //右电机往前
}
/****************************************************************************
** 左电机调速
****************************************************************************/
void pwm_out_left_moto(void)
{
if(Left_moto_stop)
{
if(pwm_val_left<=push_val_left)
{
Left_moto_pwm=1;
}
else
{
Left_moto_pwm=0;
}
if(pwm_val_left>=20)
pwm_val_left=0;
}
else
{
Left_moto_pwm=0;
}
}
/****************************************************************************
** 右电机调速
****************************************************************************/
void pwm_out_right_moto(void)
{
if(Right_moto_stop)
{
if(pwm_val_right<=push_val_right)
{
Right_moto_pwm=1;
}
else
{
Right_moto_pwm=0;
}
if(pwm_val_right>=20)
pwm_val_right=0;
}
else
{
Right_moto_pwm=0;
}
}
/****************************************************************************
** TIMER0中断服务子函数产生PWM信号
****************************************************************************/
void timer0()interrupt 1 using 2
{
TH0=0XFc; //1Ms定时
TL0=0X18;
time++;
pwm_val_left++;
pwm_val_right++;
pwm_out_left_moto();
pwm_out_right_moto();
}
/****************************************************************************
** 小车循迹函数
****************************************************************************/
void Car_Traction()
{
if(Left_bizhang_led==1&&Right_bizhang_led ==1)
{
if(Left_xunji_led == 0 && Right_xunji_led == 0) //无黑线
{
run(); //前进
delay_nms (10);
}
else if(Left_xunji_led == 0 && Right_xunji_led == 1) //右边遇黑线
{
rightrun(); //右转
delay_nms (10);
}
else if(Left_xunji_led == 1 && Right_xunji_led == 0) //左边遇黑线
{
leftrun(); //左转
delay_nms (10);
}
else if(Left_xunji_led == 1 && Right_xunji_led == 1) //左右都遇到黑线
{
stop(); //停车
delay_nms (10);
} }
else{
stop(); //停车
delay_nms (10);
}
}
/****************************************************************************
** 小车等待上货
****************************************************************************/
void Car_StopAndWait()
{
stop();
delay_nms (1000);
BUZZ=0;
delay_nms (10);
BUZZ=1;
}
/****************************************************************************
** 计算出高电平时间
****************************************************************************/
void tm2_isr() interrupt 5 using 1
{
static bit SW=0;
unsigned int T2count;
TF2 = 0;//标志位清零
SW=~SW;//状态取反
T2_PWMout=SW;//更新输出
if(SW)//低电平时间
{
T2count=0xB1E0+PWMTimes;//0xB1E0即为20ms中断用时
RCAP2L=T2count;
RCAP2H=T2count>>8;
}
else//高电平时间
{
T2count=0-PWMTimes;
RCAP2L=T2count;
RCAP2H=T2count>>8;
}
}
/****************************************************************************
** 计算出高电平时间
****************************************************************************/
void SetMotoangle(float angle)
{
//防止越界而损伤电机
if(angle > 91) angle = 91;
if(angle <5) angle = 5;
PWMTimes=500+angle*2000.0/180;//计算出高电平时间
}
/****************************************************************************
** 卸货
****************************************************************************/
void XH01()
{
if(angle==90)
{
angle=0;}
else if(angle==0)angle=90;
SetMotoangle(angle);
DelayDJ();
if(angle==90)
{
delay_nms (10);
BUZZ=0;
delay_nms (10);
BUZZ=1;
LEDOOne = 4 - WifiST;
WifiST = 0;
LEDCount = 0;
LEDBack = 0;
XHXHXHXH = 0;
}
}
/****************************************************************************
** 归零
****************************************************************************/
void Car_AutoZero()
{
LEDState = 0;
LEDOOne = 0;
LEDCount = 0;
LEDBack = 0;
WifiST = 0;
}
/****************************************************************************
** 获取wifi信号
****************************************************************************/
void Car_WifiGet()
{
ms_delay(10) ;
if(Recive_state == 1)
{
ES=0; //清空接收标志位
if((Recive_table[0]=='+')&&(Recive_table[1]=='I')&&(Recive_table[2]=='P'))//接收到的字符串形式为+IPD,x,x:y
{
if((Recive_table[3]=='D')&&(Recive_table[6]==','))
{
if(Recive_table[9]=='1')
{
delay_nms (10);
BUZZ=0;
delay_nms (10);
BUZZ=1;WifiST=1;}
if(Recive_table[9]=='2')
{
delay_nms (10);
BUZZ=0;
delay_nms (10);
BUZZ=1;
WifiST=2;
}
if(Recive_table[9]=='3')
{
delay_nms (10);
BUZZ=0;
delay_nms (10);
BUZZ=1;
WifiST=3;
}
if(Recive_table[9]=='9')
{
delay_nms (10);
BUZZ=0;
delay_nms (10);
BUZZ=1;
delay_nms (10);
BUZZ=0;
delay_nms (10);
BUZZ=1;
WifiST=0;
}
}
}
memset(Recive_table,'\0',20);
Recive_state = 0;
ES=1; //打开接收标志位
}
}
/****************************************************************************
** 小车自动函数
****************************************************************************/
void Car_AutoTractionAndWork()
{
Car_WifiGet();
if(GMDZ == 0)
{
LEDState =1;
if(WifiST > 0)
{
if(WifiST > LEDCount)
{
Car_Traction();
}
if(WifiST == LEDCount)
{
stop(); //停车
XH01();
}
}
if(WifiST == 0)
{
if(LEDBack == LEDOOne)
{
Car_StopAndWait();
}
if(LEDBack != LEDOOne)
{
Car_Traction();
}
}
}
if(GMDZ == 1)
{
if(LEDState == 1)
{
if(WifiST > 0)
{
LEDCount=++LEDCount;
LEDState =0;
}
if(WifiST == 0)
{
LEDBack=++LEDBack;
LEDState = 0;
}
}
if(LEDState == 0)
{
Car_Traction();
}
}
}
/****************************************************************************
** 红外解码控制小车子函数
****************************************************************************/
void Control_Car(unsigned char RunType)
{
stop(); //停车
switch(RunType) //判断小车动作类型
{
case 0: //前进
{
stop(); //停止反冲
Delay1ms(160);
run(); //前进
break;
}
case 1: //后退
{
stop(); //停止反冲
Delay1ms(160);
backrun(); //后退
break;
}
case 2: //左转
{
stop(); //停车反冲
Delay1ms(160);
leftrun(); //左转
break;
}
case 3: //右转
{
stop(); //停车反冲
Delay1ms(160);
rightrun(); //右转函数
break;
}
case 4: //停车
{
stop(); //停车
break;
}
}
}
/****************************************************************************
** 外部中断解码程序
****************************************************************************/
void IR1838_IN() interrupt 0 using 0
{
unsigned char j,k,N=0; //定义临时接收变量
EX0 = 0; //关闭外部中断
delayms(15); //消抖
if (IR1838IN==1)
{
EX0 =1; //外部中断开
return; //返回
}
while (!IR1838IN) //等IR1838IN变为高电平,跳过9ms的前导低电平信号。
{
delayms(1); //延时等待
}
for (j=0;j<4;j++) //采集红外遥控器数据
{
for (k=0;k<8;k++) //分四次采集8位数据
{
while (IR1838IN) //等IR1838IN变为低电平,跳过4.5ms的前导高电平信号。
{
delayms(1); //延时等待
}
while (!IR1838IN) //等IR1838IN变为高电平
{
delayms(1); //延时等待
}
while (IR1838IN) //计算IR1838IN高电平时长
{
delayms(1); //延时等待
N++; //计数器加加
if (N>=30) //判断计数器累加值
{
EX0=1; //打开外部中断功能
return; //返回
}
}
IRCOM[j]=IRCOM[j] >> 1; //进行数据位移操作并自动补零
if (N>=8) //判断数据长度
{
IRCOM[j] = IRCOM[j] | 0x80; //数据最高位补1
}
N=0; //清零位数计录器
}
}
if (IRCOM[2]!=~IRCOM[3]) //判断地址码是否相同
{
EX0=1; //打开外部中断
return; //返回
}
for(j=0;j<10;j++) //循环进行键码解析
{
if(IRCOM[2]==RecvData[j]) //进行键位对应
{
Control_Car(j);
}
}
EX0 = 1; //外部中断开
}
/******************************************************************
函 数: void Uart_Interrupt() interrupt 4
功 能: 串口中断函数,将收到的字符存到Recive_table[]数组中
参 数: 无
返回值: 无
*******************************************************************/
void Uart_Interrupt() interrupt 4
{
static char i=0; //因为是一位一位接收,所以用static
if(RI==1)
{
ES = 0;
RI=0;
Recive_table[i]=SBUF;
i++;
if((Recive_table[i-1] == '\n'))
{
Recive_table[i]='\0';
i=0;
Recive_state = 1;
}
ES = 1;
}
else
TI = 0;
}
/******************************************************************
函 数: void Uart_Init(void) 返回值: 无 参 数: 无
功 能: 串口初始化,波特率为9600(这个不会,上网百度)
*******************************************************************/
void Uart_Init(void)
{
TMOD|=0x20;
TH1=0xfD;
TL1=0xfD;
TR1=1;
REN=1;
SM0=0;
SM1=1;
EA=1;
ES=1;
}
/******************************************************************
函 数: void ms_delay(int t) 返回值: 无 参 数: 无
功 能: 毫秒级延时
*******************************************************************/
void ms_delay(int t)
{
int i,j;
for(i=t;i>0;i--)
for(j=110;j>0;j--);
}
/******************************************************************
函 数: void LED(void) 返回值: 无 参 数: 无
功 能: 发送完命令后显示用的函数
*******************************************************************/
void LED(void)
{
P2 = 0;
ms_delay(100);
P2 = 0xff;
ms_delay(100);
}
/******************************************************************
函 数: void WIFI_Init(void) 返回值: 无 参 数: 无
功 能: wifi初始化
*******************************************************************/
void WIFI_Init(void)
{
ES = 0;
TI = 1;
printf("AT+RST\r\n");
LED();
ms_delay(1000) ;
printf("AT+CWMODE=3\r\n");
LED();
ms_delay(1000) ;
printf("AT+CIPMUX=1\r\n");
LED();
ms_delay(1000) ;
printf("AT+CIPSERVER=1,8080\r\n");
LED();
ms_delay(1000) ;
printf("AT+CIOBAUD=9600\r\n"); // 设置与单片机一致的波特率
LED();
ms_delay(1000) ;
while(!TI);
TI = 0;
ES = 1;
}
/****************************************************************************
** 舵机初始化
****************************************************************************/
void T2_Init()
{
unsigned int time=65536-20000;
RCAP2L = TL2 = time; //initial timer2 low byte
RCAP2H = TH2 = time >> 8; //initial timer2 high byte
TR2 = 1; //timer2 start running
ET2 = 1; //enable timer2 interrupt
EA = 1; //open global interrupt switch
}
/****************************************************************************
** 主函数
****************************************************************************/
void main() //主程序入口
{
bit ExeFlag=0; //定义可执行位变量
EX0=0; //同意开启外部中断1
IT0=1; //设定外部中断1为低边缘触发类型
EA=1;
TMOD|=0X01;
TH0= 0XFc; //1ms定时
TL0= 0X18;
TR0= 1;
ET0= 1;
T2_Init();
/********************功能初始化***********************/
Uart_Init();//串口初始化,波特率为9600
ms_delay(1000) ;
WIFI_Init(); //wifi初始化
while(1) //程序主循环
{
if(P3_3 == 0)
{
delay_nms(10);
if(P3_3 == 0)
{
temp++;
while(!P3_3);
}
BUZZ=0; //50次检测S5确认是按下之后,蜂鸣器发出“滴”声响,然后启动小车。
delay(50);
BUZZ=1; //响50ms后关闭蜂鸣器
}
if(temp > 3)
{
temp = 1;
}
switch(temp)
{
case 1: Car_AutoTractionAndWork();EX0 = 0;break;
case 2: BUZZ = 1; EX0 = 1;break;
case 3: Car_AutoZero();EX0 = 0;break;
}
}
}