电子设计大赛运动小车追踪系统
运作设计方案
1. 系统方案设计
1.1 设计任务
(1)制作遥控电动小车,具有遥控前进、转向、倒车功能,可实时显示小
车当前运动速度,并无线传输至遥控器端显示,速度误差小于
10%,小车速度大
于 0.1 米/ 秒。
(2)制作小车位置跟踪节点,可实时测量小车与已知点距离及速度,测量误差小于 10%。
(3) 增加小车运动位置跟踪节点,可在平面进行位置定位,示意图如下 , 定位误差小于 10%。
(4)将各小车位置跟踪节点数据传输至遥控器并进行实时位置显示。
2. 方案论证与选择
2.1 单片机的选定
方案一:采用型号为 AT89C52 的单片机作为主控制器,使用霍尔传感器进行测量的直流电机转速测量系统。 AT89C52 是带 4K 字节闪烁可编程擦除只读存储器的低电压、高性能 CMOS8位微处理器。它将多功能 8 位 CPU和闪烁存储器
[3]
组合在单个芯片中,为许多控制提供了灵活性高且价格低廉的方案 。 方案二:采用单片机 C8051F060 作为主控制器, 使用霍尔传感器进行测量的直流电机转速测量系统。 C8051F060 系列单片机是美国 CYGNAL 公司推出的一种与 51 系列单片机核兼容的单片机 [4] 。C8051F060 作为新一代 8051 单片机,具有功能强大、体积小、工作稳定等特点,适用于复杂控制系统。
因此选择方案一。
AT89C52是美国 ATMEL公司生产的低电压,高性能 CMOS8位单片机。片含 8kbytes 的可反复擦写的只读程序存储器( PEROM)和 256bytes 的随机存储数据存储器( ROM),器件采用 ATMEL公司的高密度,非易失性存储技术生产,与标准MCS-51指令系统及 8052 产品引脚兼容,片置通用 8 位中央处理器(CPU)和 Flash 存储单元,功能强大的 AT89C52单片机适合于许多较为复杂的控制应用场合。
3. 系统功能模块的划分
按照设计要求, 系统可以分为以下几个基本功能模块: 电源模块、红外发射
模块、键盘模块、红外接收模块、电机驱动模块以及辅助模块,超声波定位模块
等。有些模块的功能是由硬件完成, 有些模块的功能由软件完成, 有些模块则是 由软件、硬件共同配合完成。 将系统拆分成以上的这些基本功能模块后, 再根据 各个模块所要完成的功能分别去设计, 也就是按照 “逐步求精” 的思想去设计本 系统,这将使设计工作细化,也有助于制定进度安排。
由于系统需要完成的功能较多,遥控设计方案一则采用两片 AT89C52单片机:遥控模块和接收模块。方案二则采用一片 AT89C52单片机:接收模块(主要是对遥控器进行软件解码) 。所以当采用两个单片机时则构成主从式的结构,各分担一部分控制与运算功能, 这样两个单片机即同时工作。 所谓主从式结构是指从单片机发出相应的指令, 主单片机接收命令来执行相应的功能, 这样的结构在某种程度上可以简化系统的设计。
主单片机负责红外遥控接收、 小车的运动以及处理遥控命令等功能; 从单片机则主要负责红外信号的发生等功能, 在探测到障碍时将有关信息报告给主单片机进行处理,并由主单片机来采取相应的措施。
3.1 设计方案
根据设计思路, 初步分析完成本设计需要的以下几个模块: 系统原理图如图 2.1 所示 :
电源模块
电源模块
红外发射
液晶显示
遥 控 发 射
遥 控 接 收
红外接收
霍尔测速
键盘模块
电机驱动
图 2.1 系统原理图
3.2 系统软件主要特色
软件在一个智能系统中扮演着举足轻重的作用, 软件设计的好坏直接关系着整个系统的性能。所以从软件的设计方面一定要精心细致。
本设计采用 keil 软件的 C 语言来完成系统的编程工作。 目前流行的 51 系列单片机开发软件是德国 Keil 公司推出的 Keil C51 软件,它是一个基于 32 位Windows环境的应用程序,支持 C 语言和汇编语言编程。 Keil 提供包括 C编译器、宏汇编、连接器、库管理和一个功能强大的仿真调试器等在的完整开发方案, 是一个实时的操作系统。
为了便于编程, 将逻辑控制层和具体的硬件相分离开是很有必要的。 硬件驱动层的软件负责直接操作硬件, 并且给上层的软件提供一定的接口, 这样有助于上层的软件实现更复杂的功能, 并且系统的硬件有所改动时也只需改动相应的驱动模块即可。 软件结构示意图如图 2.2 所示:
逻辑控制层
硬件驱动层
硬件设备层
图 2.2 软件分层结构示意图
4. 各模块的详细设计
4.1 红外遥控模块的设计
4.1.1 遥控模块的功能需求
遥控模块采用红外遥控方式, 因为采用红外遥控抗干扰能力强, 且不会对周围的无线电设备产生干扰电波, 接口简单;但是采用红外方式遥控的距离比较有限,一般在几米之。 鉴于本设计不需要远距离遥控, 所以综合考虑之下采用红外遥控较为合适。
后来又设计了另一套遥控模块,超再生带解码四路遥控接收模块 ( 模块分三种类型:自锁,非锁,互锁。我们采用的是自锁 ) 可以和发射器组成四路无线发 射接收电路,来实现小车的遥控功能,可将遥控围扩增至 10 米左右。 本设计中要求能用遥控器控制小车的前进、后退、左转、右转、停止、自动驾驶等功能,遥控距离在几米之即可; 并且在遥控方案二中采用现有的电视遥控器进行遥控则遥控效果更稳定些, 利用 Keil 软件对遥控器编码进行详细的解码。
4.1.2 遥控模块原理
红外遥控,即以红外线作为通信载体, 通过红外光在空中的传播来传输数据的通信方式, 它由发射端和接收端来完成。 在发射端,发送的数字信号经过适当的调制编码后,送入电光变换电路,经红外发射管转变为红外光脉冲发射到空中;在接收端,红外接收器对接收到的红外光脉冲进行光电变换, 解调,再经单片机 处理,便可以恢复出原数据信号。遥控模块原理图如图 3.1 所示:
键 盘
放
编码与调 试
解 调
大
单片机解 码
3.
1 遥控模块原理图
4.1.3 红外遥控管的选择
遥控模块的选择有声、光、磁、电、波等几种形式,考虑到声音遥控受外界干扰较大,光控也有同样的弊病,磁、电等都受发射距离的限制,所以我们采用
AT89C52单片机和 HS0038一体化红外接收头作为小车遥控控制启停的控制器。考虑到红外发光管的驱动电流过小会影响到小车的控制性能, 我们用三极管组成电流放大电路来驱动红外发射管, 在接收部分我们采用三个接收管并联的接收方式,做到接收各个角度的红外信号,以保证信号的实时性、稳定性。因此,此红外接收系统可以保证正确的接收到红外脉冲信号。 红外接收管是根据发射器发射的红外脉冲信号进行调制和处理, 将所得到的信号送给红外接收的单片机进行处
理,进而判断所要执行的相应命令,最后将控制信号送给电机驱动电路 [7] 。在遥控解码制作中, 软件设计有一个很大的难度, 解码程序的调试和编写是难点也是亮点。实际上,由于发射能力强,传输距离远,接受模块精度高,抗干扰能力强 且识码率高,所以,此红外发射、接收模块不仅能完成小车的遥控控制的功能,还能实现其它的许多功能。
4.1.4 红外遥控接收电路
红外遥控接收电路选用专用红外接收模块 HS0038,如图 3.5 所示。该接收模块是一个三端元件, HS0038的 VCC接+5V电源, GND接地。其工作原理为:首先,通过红外光敏元件将接收到的载波频率为 38kHz 的脉冲调制红外光信号转化为电信号,再由前置放大器和自动增益控制电路进行放大处理; 然后,通过带通滤波器进行滤波, 滤波后的信号由解调电路进行解调; 最后,由输出级电路进行反向放大输出。当 HS0038接收到红外信号,就会把红外信号转换的电平从 OUT 引脚输出,进入 AT89C52的 P3.2 脚,产生 INT0 中断,进入外部中断 0 服务程序,进而判断是什么信号。
VCC
OUT
HS0038 INTO(P3.2)
GND
AT89C52
图 3.5 红外接收模块
HS0038
4.2 键盘模块
键盘模块:发射键有 6 个,其中 5 个分别用于控制小车的前进、 后退、左转、
右转、停止以及自动驾驶能用。每个按键接 10KΩ上拉电阻,不作用时每个按键均为高电平。 通过 1/0 口连接。将每个按钮的一端接到单片机的 I/O 口,另一端接地,这是最简单的办法,六个按钮分别接到 P1.0、 P1.1、 P1.2、 P1.3、 P1.4 和 P1.5 。对于这种键各程序能采用持续查询的办法,功能就是:检测是否有键闭合,如有键闭合,则去除键抖动,判断键号并转入对应的键处理。
4.3 电机驱动模块
方案一:使用 L297 驱动芯片做直流电机驱动,原理:芯片的 PWM斩波器电路可开关模式下调节步进电机绕组中的电机绕组中的电流。该集成电路采用了SGS公司的模拟 / 数字兼容的 I2L 技术,使用 5V 的电源电压,全部信号的连接都与 TFL/CMOS或集电极开路的晶体管兼容。 L297 的芯片引脚特别紧凑,采用双列直插 20 脚塑封封装。
方案二 : 使用 L298 驱动芯片做直流电机驱动, 驱动原理和 297 相似,但是该芯片使用时产生热量过多。
综合考虑,采用方案一。
4.4 显示模块
方案一:采用 LCD1602液晶显示器, LCD有明显的优点 : 功耗的,美观,寿命长,尺寸小,使用方便,但其成本相对 LED较高。
方案二:采用 LED数码管显示, LED数码管亮度高,但不足以达到实验显示要求,且难以控制。
综合考虑采用方案一。
4.5 霍尔测速模块论证与选择
方案一:采用型号为 A3144 的霍尔片作为霍尔测速模块的核心,该霍尔片体积小,安装灵活,价格合理,可用于测速,可与普通的磁钢片配合工作。
方案二:采用型号为 CHV-20L 的霍尔元器件作为霍尔测速模块的核心 , 该霍尔器件额定电流为 100mA,输出电压为 5V, 电源为 12~15V。体积较大,价格昂贵。
因此选择方案一。
4.6 电源模块论证与选择
方案一:采用交流 220V/50Hz 电源转换为直流 5V电源作为电源模块。该方案实施简单,电路搭建方便,可作为单片机开发常备电源使用。
方案二:采用干电池串并联达到 5V作为电源模块。该方案实施简单,无需搭建电路,但相对该方案不够稳定,电池耗电快,带负载后压降过高,可能无法使系统稳定持续运行。
方案三:采用可充电锂电池结合稳压模块作为电源模块。 该方案简单易行, 而且相对稳定、误差小,但该方案相对价格过高,针对该设计要求。
4.7 无线通信模块
无线通信模块的发射与接收主要采用 nRF2401作为主工作核心, nRF2401 是单片射频收发芯片,工作于 2.4 ~2.5GHz ISM 频段。 nRF2401最大传输速率为250Kbps、1Mbps,可以和各种单片机和微控制器连接,控制简单方便。配合简单的通信协议,就可以使用 nRF2401实现无线数据传输。 采用点对多点半双工通信机制,设计一个简单有效的通信协议, 实现对所采集到的数据进行有效传送。 最
简单的多机通信方式就是使用串行通信,所以使用单片机串行口配合 nRF2401 芯片,就可以实现简单有效的点对多点通信。
5. 超声波定位的系统设计
PC 机
串口线
D点 C点
目标点( x, y ,z)
A点 B点
ABCD组成 15m*15m的正方形,而且 ABCD处在同一高度,目标点在正方向之
5.1 超声波定位系统的原理及结构
定位节点由微处理器模块、 无线通信模块、超声波模块、电源模块部分组成。
发射器
高层应用
射频模块
接收器
存储器
物理层
介质访问
超声波模块
微处理器模块 无线通信模块
电源模块
图 4.1 定位节点结构图
发射头选用 TCT40-16T,接受头选用 TCT40-16R 二者的频率都在 40+1.0KHz,
方向角为 60 度,温度在 -20---70 ℃。
CC2420的选择性和敏感性指数超过了 IEEE802.15.4 标准的要求, 可确保短
距离通信的有效性和可靠性。
实现定位的流程图如下:
已 知节
目标节点
目 标 节
目 标 节 点 启 动
点 收 到 超 声 波 停 止 定 时 器 信 号
取 出 定 时 器 中 数 值 送 到 上 位 机
上 位 机
点 发射 搜 寻目
收的到射 频信号开
根 据 定
位 算 法
标 节点 并 发送 超 声波
启超声波 接收电路
定时器
计 算 出
目 标 节
点 的 三
维坐标
图 4.1 定位实现流程图
6. 系统软件的设计
6.1 小车遥控模块的程序框图如下:
系统初始
系统数据转化
与 LCD 显示
外部中断
Y N
处理器对中断数
据处理
L297 驱动直流电
动机车体
5.2 定位算法
在传感器网络中, 定位算法有好多种, 在这里我用的是三维位置测量法。 具体是发射节点的坐标用三个值进行表示,假设为( x,y,z).A 点的坐标为( 0,0,h),b 点的坐标为( 0,m,h ),c 点坐标为( n,0,h ),D 点的坐标为( n,m,h ).计算出( X, Y, Z)。
本设计是基于超声波测距技术的三维定位系统 , 精度可以达到厘米级。
5.3 发射板控制程序设计
发射板控制程序由主程序和键盘扫描程序组成, 在主程序中,采用调用键扫描子程序来完成各个按键的功能,
5.4 接收处理程序的设计
1. 初始化程序: 对转向电机、 前后轮驱动电机上电时设为停止状态, 开中断允许等。
2. 主程序:根据标志位 00H 的值判断进入控制状态。 3. 中断接收程序: 对第一位脉冲的宽度进行验证, 然后进行计数, 根据脉冲的个数进行相应的控制操作,其程序流程图如图 5.2 所示:
中断接收开始
N
低电平脉宽 >2ms
Y
接收并对低电平脉宽计数
高电平脉宽 >3ms
N
Y
按脉冲个数执行相应功能程序
中断返回
图 5.4 遥控小汽车中断接收程序流程图
7. 参考文献
[1] 董少明.单片机原理与应用. :理工大学, 2009
[2] 黄智伟.全国大学生电子设计竞赛电路设计. :航空航天, 2006 [3] 毅刚等.电子系统设计. :工业大学, 2008
[4] 华嵩 王伟 Protel 电路原理图与 PCB设计 108 例。中国青年 2006 [5] 宏杉.红外遥控技术 [R] .伟福公司, 2003.
[6] 齐英鑫 . 基于 80C51 单片机的智能电动小车 [J]. 科技信息, 2008(31)8. 项目进度安排:
(资料查阅、实验方案制定、实验研究、实验数据分析、撰写研究论文和总结报告、结题验收等)
2011 年 12 月初选定题目和撰写课题方案。
.
2012 年 1 月初查阅 52 单片机、电机驱动、液晶显示、超声波传感器等各方面的资料。
2012 年 2 月论证方案,完成运动小车追踪系统的规划设计,选购所需器件及工具。
2012 年 3 月初编写各模块的源代码,及绘制电路图,并且通过初步仿真 2012 年 3 月中下旬制作实物,并且调试以达到要求,撰写论文及实验报告,总结实践、心得体会。
2012 年 4 月:参加决赛及答辩。
附录 : 系统硬件电路图
附录表 1 遥控系统功能键列表
实际制作过程中红红外发射接收模块, 超声波测距模块, 霍尔测速模块, 电机驱动模块,无线传输模块, 四键遥控器和超再生固定码接收模块均采用现成模 块
按键编号 按键码 按键名 功能
1 2 3 4
0x10 0x1D 0x12 0x19 0x11
K1 UP DOWN L R
停止 前进 后退 左转 右转
5
附录 2 遥控发射电路
图附录 2 遥控发射电路
附录 3 遥控接收电路
图附录 3 遥控接收电路
附录 4 电压转换电路
图附录 4 电压转换电路
附录 5 电机驱动电路
图附录 5 电机驱动电路
附录 6 无线通信原理图
U2
VCC
R1
1M
X1 4.000MHZ
TXE N1 PW R_UP2 GND 3 DIN 4 DOUT5 CS 6 VCC 7
TXE N
PW R_UP GND DIN DOU T CS VCC IOL
C2 22pF
C5 4.7uF
C1 22pF
U1
C4
15nF C3 820pF
L1 22nH
R2 4.7K
C6 100nF
C7 1nF
1 2 3 4 5 6 7 8
DIN 9 DOU T 10
XC1 XC2 VDD TXE N VSS PW R_UP FILT1 VSS VCO 1 ANT 1 VCO 2 ANT 2 VSS VSS VDD VDD
CS DIN
DOU TRF PW R nRF401
20 19 TXE N 18 PW R_UP 17 16 15 14 13 12CS 11
C10 3.3pF
C9
R4
220pF
18K
C8 220pF
C11 5.6pF
R3 22k
图附录 6 无线通信原理图
附:最终程序
测速遥控系统 :
#include #define c(x)(x*110592/120000) #define shuma P0 //== 定义端口和全局变量 Unsigned char Ir_Buf[4]; sbit LED_0=P2^3; sbit LED_1=P2^2; sbit LED_2=P2^1; sbit LED_3=P2^0; sbit Beep=P3^7; // sbit IRIN = P3^2; // sbit zheng=P2^4 ; sbit fan=P2^5; sbit left=P2^6; sbit right=P2^7; sbit set5=P1^4; // sbit set6=P1^5; sbit set7=P1^6; 定义四线步进电机 4 个 IO 口 蜂鸣器 红外接收头 =================================== sbit set8=P1^7; unsigned char m,n,counter; unsigned int d; unsigned int code ton[7]; unsigned char IRCOM[7]; unsigned char keys=0xff; //== 声明子函数 =================================== void display(unsigned char d1,unsigned char d2,unsigned char d3,unsigned char d4); void IRdelay(unsigned char x); void delay1ms(int); void inc(); void dec(); void go(); void back(); void turn_lef(); void turn_rig(); void stop(); void cool(); void shuai(); char on=0; char b,c,e; unsigned char a[16]={0xc0,0xf9,0xa4,0xb0,0x99,0x92,0x82,0xf8, 0x80,0x90,0x88,0x83,0xc6,0x86,0x8e,0x82}; // 共阳极数码管的段码 0123456789ABCDEF //== 主程序 =================================== void main( void ) { unsigned int d=0,m=1; IE=0x8B; TCON=0x01; TMOD=0X51; //GATE C/T M1 M0 GATE C/T M1 M0 计数器 T1 定时器//0 1 0 1 0 0 0 1 TH1=0; // 计数器初始值 TL1=0; TH0=0xed; // 装定时器初值 TL0=0xff; EA=1; // IE=0X00; //EA - ET1 ES ET1 EX1 ET0 EX0 // 100000 10 TR1=1; TR0=1; TF0=1; while(1) { T0 int temp1,temp2; temp1=TL1;temp2=TH1; counter=(temp2<<8)+temp1; if(set5==1) {go();delay1ms(100);}; if(set6==1) {back();delay1ms(100);}; if(set7==1) {turn_lef();delay1ms(100);}; if(set8==1) {turn_rig();delay1ms(100);}; switch(keys) { case case case case case case case case 0x0C: 0x18: 0x5E: 0x08: 0x15: 0x07: 0x1C: 0x16: go();break; back();break; turn_lef();break; turn_rig();break; dec();break; inc();break; cool();break; stop();break; shuai();break; case 0x5A: default:break; } delay1ms(100-on); stop(); delay1ms(on); } } //T0 中断,时间是 5ms,定时中断,霍尔测速,并将速度值转化为各位显示 ============= void int0( ) interrupt 1 { unsigned char d1,d2,d3,d4; TMOD=0X51; TH0=0xed; TL0=0xff; n++; if(n>=200)//5ms 的中断,循环 { n=0; d=counter; // 装定时器初值实现 5ms定时 200 次,是 1 秒 TH1=0; TL1=0; } // 每 1S 清一次定时器 d1=(d/1000)%10; d2=(d/100)%10; // 把速度转化为 4 位 d3=(d/10)%10; d4=d%10; if(d2>=1) {Beep=0;} else {Beep=1;} display(d1,d2,d3,d4); } //8 中断专用的显示函数,数码管显示 void display(unsigned char d4) { if(m==1) { LED_0=0; LED_1=LED_2=LED_3=1; shuma=a[d1]; } if(m==2) { LED_1=0; LED_0=LED_2=LED_3=1; shuma=a[d2]; } if(m==3) { LED_2=0; LED_1=LED_0=LED_3=1; shuma=a[d3]; } if(m==4) { LED_3=0; LED_1=LED_2=LED_0=1; shuma=a[d4]; } m++; if(m>=5) // 超速报警 ======================= d2,unsigned char d3,unsigned char d1,unsigned char // 使能该数码管控制位 // 其他控制位无效 // 按照数据点亮该数码管 // 数码管位循环扫描 m=1; } //== 中断读取红外键值程序 void { unsigned char j,k,N=0; IRdelay(15); if (IRIN==1) int1(void) interrupt 0 ========================= { return;} // 确认 IR 信号出现 // 等 IR 变为高电平,跳过 9ms的前导低电平信号。 while (!IRIN) {IRdelay(1);} for (j=0;j<4;j++) // 收集四组数据 { // 每组数据有 8 位 for (k=0;k<8;k++) { while (IRIN) while (!IRIN) {IRdelay(1);} while (IRIN) / / 等 IR 变为低电平 , 跳过 4.5ms 的前导高电平信号。 {IRdelay(1);} // 等 IR 变为高电平 // 计算 IR 高电平时长 { IRdelay(1); N++; if (N>=30) 计数过长自动离开。 { return;} //0.14ms } 数据最高位补“ 0” // 高电平计数完毕 IRCOM[j]=IRCOM[j] >> 1; // if (N>=8) {IRCOM[j] = IRCOM[j] | 0x80;} N=0; }//end for k }//end for j keys=IRCOM[2]; } // 数据最高位补“ 1” //==0.14ms 延时 =============================== void IRdelay(unsigned char x) //x*0.14MS { unsigned char i; while(x--) { for (i = 0; i<13; i++) {} } } //== 加速程序 ================================= void { if(on!=100) on+=20; keys=0; } //== 延时程序 ================================== void {int i,j; delay1ms(int x) inc() for(i=0;i } ; //== 减速程序 ================================== void { dec() if(on!=0) keys=0; } on-=20; //== 方向向前 ================================== void { go() zheng=1; fan=0; left=1; right=0; } //== 方向向后 ================================== void { back() zheng=0; fan=1; left=0; right=1; } //== 左转 ====================================== void { turn_lef() zheng=0; fan=1; left=1; right=1; } //== 右转 ====================================== void { turn_right() zheng=1; fan=1; left=0; right=1; } //== 停止程序 ================================== void { stop() zheng=1; fan=1; left=1; right=1; } void //== 固定轨迹运动 S 型运动 ================================== cool() zheng=0; fan=1; left=1; right=1; delay1ms(200); delay1ms(200); { delay1ms(200); delay1ms(200); delay1ms(200); delay1ms(200); zheng=1; fan=1; left=0; right=1; delay1ms(200); delay1ms(200); } delay1ms(200); delay1ms(200); delay1ms(200); delay1ms(200); //== 固定轨迹运动 ================================== void shuai() zheng=0; { fan=1; left=1; right=0; } 测距定位系统: #include // 器件配置文件 #define __INTRINS_H__ extern void extern bit _nop_ (void); _testbit_ (bit); extern unsigned char _cror_ (unsigned char, unsigned char); extern unsigned int _iror_ (unsigned int, unsigned char); extern unsigned long _lror_ (unsigned long, unsigned char); extern unsigned char _crol_ (unsigned char, unsigned char); extern unsigned int _irol_ (unsigned int, unsigned char); extern unsigned long _lrol_ (unsigned long, unsigned char); extern unsigned char _chkfloat_(float); #endif #define RX1 P2_7 #define TX1 P2_6 #define RX2 P2_5 #define TX2 P2_4 #define LCM_RW P2_1 // #define LCM_RS P2_0 #define LCM_E #define Busy P2_2 0x80 // #define LCM_Data P0 定义 LCD引脚 用于检测 LCM状态字中的 Busy 标识 //== 子函数声明 ================================== void LCMInit(void); void DisplayOneChar(unsigned char X, unsigned char Y, unsigned char DData); void DisplayListChar(unsigned char X, unsigned char Y, unsigned char code *DData); void Delay5Ms(void); void Delay400Ms(void); void Decode(unsigned char ScanCode); void WriteDataLCM(unsigned char WDLCM); void WriteCommandLCM(unsigned char WCLCM,BuysC); unsigned char ReadDataLCM(void); unsigned char ReadStatusLCM(void); unsigned char code mcustudio[] ={\"zuofang unsigned char code email[] = {\"qianfang unsigned char code Cls[] = {\" \ unsigned char code ASCII[17] = {'0','1','2','3','4','5','6','7','8','9','.','-','M','q','z','f',}; static unsigned char DisNum = 0; // unsigned int time1=0; unsigned int time2=0; unsigned long S1=0; unsigned long S2=0; bit bit flag1 =0; flag2 =0; 显示用指针 \ \ unsigned char disbuff[8] ={ 0,0,0,0,0,0,0,0,}; // 写数据 ================================== void WriteDataLCM(unsigned char WDLCM) { ReadStatusLCM(); // LCM_Data = WDLCM; LCM_RS = 1; LCM_RW = 0; 检测忙 LCM_E = 0; // 若晶振速度太高可以在这后加小的延时 LCM_E = 0; // 延时 LCM_E = 1; } // 写指令 ================================== void WriteCommandLCM(unsigned char WCLCM,BuysC) //BuysC{ if (BuysC) ReadStatusLCM(); // 根据需要检测忙 LCM_Data = WCLCM; LCM_RS = 0; LCM_RW = 0; LCM_E = 0; LCM_E = 0; LCM_E = 1; } // 读数据 ================================== unsigned char ReadDataLCM(void) { LCM_RS = 1; LCM_RW = 1; LCM_E = 0; LCM_E = 0; LCM_E = 1; return(LCM_Data); } // 读状态 ================================== unsigned char ReadStatusLCM(void) { LCM_Data = 0xFF; LCM_RS = 0; LCM_RW = 1; LCM_E = 0; LCM_E = 0; LCM_E = 1; while (LCM_Data & Busy); // 检测忙信号 return(LCM_Data); } //LCM 初始化 ================================== 为 0 时忽略忙检测 void LCMInit(void) { LCM_Data = 0; WriteCommandLCM(0x38,0); // Delay5Ms(); WriteCommandLCM(0x38,0); Delay5Ms(); WriteCommandLCM(0x38,0); Delay5Ms(); 三次显示模式设置,不检测忙信号 WriteCommandLCM(0x38,1); // WriteCommandLCM(0x08,1); // WriteCommandLCM(0x01,1); // WriteCommandLCM(0x06,1); // WriteCommandLCM(0x0F,1); // } 显示模式设置 , 开始要求每次检测忙信号 关闭显示 显示清屏 显示光标移动设置 显示开及光标设置 // 按指定位置显示一个字符 ================================== void DisplayOneChar(unsigned char X, unsigned char Y, unsigned char DData) { Y &= 0x1; X &= 0xF; // X |= 0x80; // 限制 X 不能大于 15, Y 不能大于 当要显示第二行时地址码 发命令字 发数据 1 if (Y) X |= 0x40; // +0x40; 算出指令码 WriteCommandLCM(X, 1); // WriteDataLCM(DData); // } // 按指定位置显示一串字符 ================================== void DisplayListChar(unsigned char X, unsigned char Y, unsigned char code *DData) { unsigned char ListLength; ListLength = 0; Y &= 0x1; X &= 0xF; // { if (X <= 0xF) //X { DisplayOneChar(X, Y, DData[ListLength]); // 限制 X 不能大于 15, Y 不能大于 1 while (DData[ListLength]>0x19) // 若到达字串尾则退出 坐标应小于 0xF 显示单个字符 ListLength++; X++; } } } //5ms 延时 ================================== void Delay5Ms(void) { unsigned int TempCyc = 5552; while(TempCyc--); } //400ms 延时 ================================== void Delay400Ms(void) { unsigned char TempCycA = 5; unsigned int TempCycB; while(TempCycA--) { TempCycB=7269; while(TempCycB--); }; } // 超声波测距及距离计算 void Conut1(void) // { time1=TH0*256+TL0; TH0=0; TL0=0; ================================== 模块一 S1=(time1*1.7)/100; { flag1=0; // 算出来是 CM if((S1>=700)||flag1==1) // 超出测量围显示“ - ” DisplayOneChar(0, 1, ASCII[11]); DisplayOneChar(1, 1, ASCII[10]); DisplayOneChar(2, 1, ASCII[11]); DisplayOneChar(3, 1, ASCII[11]); DisplayOneChar(4, 1, ASCII[12]); } else { disbuff[0]=S1%1000/100; disbuff[1]=S1%1000%100/10; disbuff[2]=S1%1000%10 %10; // 显示点 // 显示 M DisplayOneChar(0, 1, ASCII[disbuff[0]]); DisplayOneChar(1, 1, ASCII[10]); DisplayOneChar(2, 1, ASCII[disbuff[1]]); DisplayOneChar(3, 1, ASCII[disbuff[2]]); DisplayOneChar(4, 1, ASCII[12]); DisplayOneChar(6, 1, ASCII[13]); DisplayOneChar(7, 1, ASCII[15]); } } void Conut2(void)// { time2=TH1*256+TL1; TH1=0; TL1=0; // 显示点 // 显示 M 模块二 S2=(time2*1.7)/100; { flag2=0; // 算出来是 CM if((S2>=700)||flag2==1) // 超出测量围显示“ - ” DisplayOneChar(0, 0, ASCII[11]); DisplayOneChar(1, 0, ASCII[10]); DisplayOneChar(2, 0, ASCII[11]); DisplayOneChar(3, 0, ASCII[11]); DisplayOneChar(4, 0, ASCII[12]); } else { disbuff[3]=S2%1000/100; disbuff[4]=S2%1000%100/10; disbuff[5]=S2%1000%10 %10; DisplayOneChar(0, 0, ASCII[disbuff[3]]); DisplayOneChar(1, 0, ASCII[10]); DisplayOneChar(2, 0, ASCII[disbuff[4]]); DisplayOneChar(3, 0, ASCII[disbuff[5]]); DisplayOneChar(4, 0, ASCII[12]); DisplayOneChar(6, 0, ASCII[14]); DisplayOneChar(7, 0, ASCII[15]); } } // 中断函数,超出测量围时 void zd0() interrupt 1 { flag1=1; // 显示点 // 显示 M // 显示点 // 显示 M ================================== //T0 中断用来计数器溢出 , 超过测距围 // 中断溢出标志 } void zd1() interrupt 3 //T1 中断用来计数器溢出 , 超过测距围 { flag2=1; // 中断溢出标志 } // 启动模块 ================================== void StartModule1() { TX1=1; _nop_(); _nop_(); _nop_(); _nop_(); _nop_(); _nop_(); _nop_(); _nop_(); _nop_(); _nop_(); _nop_(); _nop_(); _nop_(); _nop_(); _nop_(); _nop_(); _nop_(); _nop_(); _nop_(); _nop_(); _nop_(); TX1=0; } void StartModule2() { TX2=1; _nop_(); _nop_(); _nop_(); _nop_(); _nop_(); _nop_(); _nop_(); _nop_(); _nop_(); _nop_(); _nop_(); // // // 启动模块 启动模块一 启动模块二 _nop_(); _nop_(); _nop_(); _nop_(); _nop_(); _nop_(); _nop_(); _nop_(); _nop_(); _nop_(); TX2=0; } // 延时子程序 ================================== void delayms(unsigned int ms) { unsigned char i=100,j; for(;ms;ms--) { while(--i) { j=10; while(--j); } } } // 主函数 ================================== void main(void) { unsigned char TempCyc; Delay400Ms(); // 启动等待,等 LCM讲入工作状态 Delay5Ms(); // 延时片刻 ( 可不要 ) DisplayListChar(0, 0, mcustudio); DisplayListChar(0, 1, email); ReadDataLCM();// 测试用句无意义 for (TempCyc=0; TempCyc<10; TempCyc++) Delay400Ms(); // while(1) { TMOD=0x11; TH0=0; // 设 T0 为方式 1, GATE=1; 延时 DisplayListChar(0, 1, Cls); TL0=0; ET0=1; // 允许 T0 中断 TH1=0; TL1=0; ET1=1; EA=1; while(1) { StartModule1(); // DisplayOneChar(0, 1, ASCII[0]); while(!RX1); TR0=1; while(RX1); TR0=0; Conut1(); delayms(80); StartModule2(); // DisplayOneChar(0, 1, ASCII[0]); while(!RX2); TR1=1; while(RX2); TR1=0; Conut2(); delayms(80); } // 当 RX为零时等待 // 开启计数 // 当 RX为 1 计数并等待 // 关闭计数 // 计算 //80MS // 当 RX为零时等待 // 开启计数 // 当 RX为 1 计数并等待 // 关闭计数 // 计算 //80MS } }
因篇幅问题不能全部显示,请点此查看更多更全内容