主控制函数包括初始化各个功能模块,车程数据的保存与恢复,将车程车速实时显示。其中初始化包括系统初始化、定时器和外部中断初始化、LCD初始化。主控制程序工作流程图如图4-1。中断工作可依据中断位置分为外部中断0和定时器中断0。外部中断0程序分别对里程计数和速度计数。定时器0中断程序任务是对计数器装入初值;时间每隔1s将速度计数值进行累加并对时间和速度计数清0。流程如4-2,4-3所示。

图4-1 控制函数流程图 图4-2 外部中断流程图 图4-3 定时器中断流程图
4.2 系统的初始化系统初始化包含主控制函数初始化工作、LCD初始化、中断定时器初始化。主函数初始化需要定义主函数中使用的变量。LCD初始化任务是设定LCD工作方式,具体流程图如图4-4所示。中断与定时器初始化是调整中断与定时器工作方式如图4-5所示。

图4-4 LCD初始化流程图 图4-5 中断与定时器初始化流程图
4.3 车程车速数据的实时显示4.3.1 车程车速数据的获得与计算公式车轮一周安装8个挡板,均匀分布在车轮上,车轮在转动过程中,会使红外线接收管在通断状态进行变换。每变换一次红外线电路输出端都会进行高低电平的转换,形成一次跳变沿。跳变沿可触发单片机外部中断,执行中断程序时对跳变沿进行计数。为了程序的简单化(因为用于速度计数的变量要不断进行清零)。可设置两个变量分别计数,一个用于计算速度,一个用于计算里程。速度度计数值设为speedsum,里程计数值设为milesum。设速度为V单位km/h,车程为S单位为km,车轮周长为L单位为cm,计时时间为T单位为s。
速度的计算公式为:
V=L/8*speedsum/T*3600/100000 (km/h) (4-1)
里程计算公式为:
S=milesum*L/8/100000 (km) (4-2)
同时要完成液晶上显示详细变量数据,务必把变量各位上的数字分离出来。根据设计任务,速度与里程需要精确到小数点后一位。具体方法如下:将变量乘10赋值给t,按下面公式计算便可得各位上的数字(其它以此类推)
qwei=t/1000000%10; bwei=t/100000%10;
swei=t/10000%10; gwei= t/1000%10; hpoint=t/100%10;
4.3.2 设定字符在液晶上的显示位置12864液晶可以显示四行,但行与行间的地址并不是连续的,所以将设定显示位置写成函数形式,以方便调用,防止出错。液晶显示设计流程图如图4-6所示。
设要设定的行数为X,X值从0到3。列数为Y,Y值从0到7。

图4-6 字符在液晶显示位置控制流程图
4.3.3 车程车速数据的显示数据显示要实现将速度和里程显示出来。具体是,第一行居中显示“天天如意”,第二行依次显示“速度:

km/h”字符和实时速度。第三行依次显示“里程:

km”字符和实时里程。根据12864液晶的特性,固定字符显示在固定位置不需要重复传送数据,对于变量速度和里程需要不停的传送数据。因此可将显示部分两部分来写。一部分显示固定的字符,另一部分显示变量。要将变量显示在液晶上,需要将其各位上的数分离出来,然后分别显示。这里如果高位为0时,不能显示。本设计需要将速度和里程两具变量显示在液晶上,显示这两变量原理相同,这里只写出其中一个的流程图。流程图如图4-7。设要显示的位数为3位,需要更高位显示功能则以此类推。

图4-7 液晶显示车速数据位数控制流程图
4.4车程车速数据的保存与恢复 根据设计任务要求,需对里程数据完成断电保护工作预防特殊情况下重要数据断电不遗失,上电时能及时恢复。本系统采用单片机EEPROM完成数据掉电保护工作。基于程序将数据备份到单片机EEPROM内存储,上电时可及时完成恢复工作。
4.4.1对单片机EEPROM的基本操作流程图单片机EEPROM读写删三个操作。三个操作的工作流程示意图对应如图4-8~4-10所示。

图4-8 读流程图 图4-9 写流程图 图4-10 删流程图
4.4.2里程数据的保存与恢复里程speedsum为32位long型数据,而STC89C52单片机只能进行字节读,字节写,扇区擦除。要想保存speedsum信息数据,移动把speedsum分解成字节型信息数据。设4个unsigned char型变量a1~a4分别用于保存speedsum低8位,次低8位,次高8位,高8位。在C语言中Long型信息数据赋值给char型信息数据只把最低8位赋值给char信息数据。将speedsum直接赋值给a1,可得低8位,再将speedsum分别移位8位,16位,24位分别赋值给a2~a4,就可成功分speedsum。然后,再分别保存a1~a4,便可间接保存speedsum。
当系统上电时恢复speedsum,取出a1~a4,分别移位相加便可得到speedsum。具体方法如下:取出a1~a4,强制类型转成到long型,再分别按位左移0位,8位,16位,24位,然后再相加赋给speedsum便可恢复。
具体程序设计流程图如4-11和4-12所示:

图4-11数据恢复流程图 图4-12 信息数据保存流程图
5系统调试根据硬件设计原理图完成硬件焊制工作并组装,组装后利用Keil软件将写好的程序存储到单片机内部。依据实际任务需求,完成车程车速实时显示及为验证车辆车速行驶检测的准确性。基于如下形式检测车程车速的误差大小及准确度。单片机计算车程速度主要是基于对P3.2端口电平信号跳变沿累计求和计数,基于内部时钟频率计时累计而获取。单片机完成跳变沿计数具体值可以基于调整单片机内部相关程序呈现数据于液晶。测试也可基于人工计数,但需保证单片机计数具体值与实际值一直就能达到车速数据测量的准确性及精度。具体检测数据结果记录详情如表5-1所示。
表5-1 跳变沿计数

系统经过调试后的得到液晶显示图和实物结果图如5-1和5-2所示

图5-1 液晶显示车速结果图

图5-2 系统整体实物结果图
6 总结伴随目前车辆控制技术和轨道交通工具的飞速发展,在全球各国家,不管重要的交通运输业还是用户个体,车辆是长距离运输的不可缺少的重要交通工具之一。当面对汽车等驾驶员长时间高速行驶车辆时,容易造成疲劳而造成速度控制不到位的状况,及在限速路段总频频发生超速现象,对自己和其他人们均都造成重大安全隐患。本文设计就是基于解决这一问题的分枝做的主要工作成果如下:
系统基于红外线收发工作原理完成了信息数据采集工作,依据红外线接收管判定了是否出现红外线照射,从而形成通断电路,进而产生了高低电压数据以生成电信号。利用了核心控制模块基于STC89C52单片进行完成信号数据处理,将数据处理值通过液晶显示器完成了显示。说明了系统的整体布局设计方案和整体规划流程,阐述了硬件和软件设计核心工作原理,最终对系统整体及实物进行软硬件功能的调试和运行测试,证实系统设计符合实际标准且可行。
代码:
- #include<reg52.h>
- #include<intrins.h>
- #define uchar unsigned char
- #define RdCommand 0x01 //定义ISP的操作命令
- #define PrgCommand 0x02
- #define EraseCommand 0x03
- #define WaitTime 0x01 //定义CPU的等待时间
-
- sfr ISP_DATA=0xe2; //寄存器申明
- sfr ISP_ADDRH=0xe3;
- sfr ISP_ADDRL=0xe4;
- sfr ISP_CMD=0xe5;
- sfr ISP_TRIG=0xe6;
- sfr ISP_CONTR=0xe7;
-
- sbit rs=P2^4;
- sbit rw=P2^5;
- sbit e=P2^6;
- sbit psb=P2^1;
-
- uchar code table1[]={" 天天如意"};
- uchar code table2[]={"速度: km/h"};
- uchar code table3[]={"里程: km"};
- uchar code table4[]={" --石头"};
-
- /**********************************/
- void delay(int k); //延迟ms
- void writecmd(uchar cmd); //lcd写命令
- void writedata(uchar dat); //lcd写数据
- void init(); //初始化lcd工作方式
- void setadd(uchar x,uchar y); //设定lcd显示地址
- void display(); //lcd显示
- void ISP_IAP_enable(void); //ISP_EN=1
- void ISP_IAP_disable(void); //ISP_EN=0
- void ISP_TRIGER(void); //激活ISP_CMD
- unsigned char byte_read(unsigned int byte_addr);//读字节
- void SectorErase(unsigned int sector_addr);//删除扇区
- void byte_write(unsigned int byte_addr, uchar original_data);//写字节
-
- /**********************************/
- uchar num,num1;
- long speedsum=0; //外部中断计数
- int timnum=0,k,i,able=0; //定时器中断计数
- unsigned long milesum=0; //总里程计数,浮点型
- int shifen,baifen,ge,shi,bai,qian,wan,milshifen,milge,milshi;
- int milbai,milqian,milwan,milshiwan; //display中的参数;
- unsigned long t,y,s; //中间数据,t为每小时走的距离(m) y为计算里程的中间数字
-
- /**********************************/
- main()
- {
- uchar a1~a4; //分别为milesum的第1,2,3,4个8位
- TMOD=0x01; //定时器0工作方式16位
- TH0=(65536-46080)/256; //高8位初值
- TL0=(65536-46080)%256; //低八位初值 晶振12Mhz时 计满一次为1us
- EA=1; /*开总中断*/
- EX0=1; /*开外部中断0*/
- IT0=1; /*外部中断0为跳变沿触发*/
- TR0=1; //启动定时器0
- ET0=1; //开外部中断0
- PT0=1; //定时器0置为高优先级
-
- EA=0;
- a1=byte_read(0x2000); //读资料a1
- a2=byte_read(0x2001); //读资料a2
- a3=byte_read(0x2200); //读资料a3
- a4=byte_read(0x2201); //读资料a4
- EA=1;
- milesum=(unsigned long)(a1+((long)a2<<8)+((long)a3<<16)+((long)a4<<24));
- //恢复milesum值
- if(milesum>4800000000)
- milesum=0;
- init();
- setadd(1,0);//设置显示位置
- i=0;
- while(table1[i]!='\0')
- {
- writedata(table1[i]);
- i++;
- }
- i=0;
- setadd(2,0);//设置显示位置
- while(table2[i]!='\0')
- {
- writedata(table2[i]);
- i++;
- }
- i=0;
- setadd(3,0);//设置显示位置
- while(table3[i]!='\0')
- {
- writedata(table3[i]);
- i++;
- }
-
- i=0;
- setadd(4,0);//设置显示位置
- while(table4[i]!='\0')
- {
- writedata(table4[i]);
- i++;
- }
- while(1)
- {
- a1=milesum&0x000000ff; //将第个八位取出
- a2=milesum>>8; //将第个八位取出
- a3=milesum>>16; //将第个八位取出
- a4=milesum>>24;
- if(num>=20) //将第个八位取出
- {
- num=0;
- SectorErase(0x2000);//擦除扇区
- byte_write(0x2000,a1);//重新写入数据
- byte_write(0x2001,a2);//重新写入数据
- SectorErase(0x22200);//擦除扇区
- byte_write(0x2200,a3);//重新写入数据
- byte_write(0x2201,a4);//重新写入数据
- }
- //if(able==1)
- display();
- }
- }
-
- /***************外部中断程序*******************/
- void exter0() interrupt 0
- {
- speedsum++; //外部中断
- milesum++; //总里程计
- }
-
- /****************定时器0中断程序******************/
- void T0_time() interrupt 1
- {
- TR0=0; //关闭定时器0
- TH0=(65536-46080)/256; //高8位初值
- TL0=(65536-46080)%256; //低八位初值 晶振12Mhz时 计满一次为50ms
- timnum++;
- num++ ; //计时器计数
- if(timnum>=20) //如果到1s时
- {
- s=speedsum;
- timnum=0;
- speedsum=0;
- able=1;
- }
- else
- able=0;
-
- TR0=1;//启动定时器0
- }
-
- /**************显示速度,里程********************/
- void display()
- {
- //显示速度
- t=s*2.08*3600/100; //t为每小时走的距离(m) s为每秒外部中断次数
- //2.08cm为每两孔之间的距离 *3600化为每小时;
- // /100距离化为米
- qian=t/1000000%10;
- bai=t/100000%10;
- shi=t/10000%10;
- ge= t/1000%10;
- shifen=t/100%10;
- setadd(2,3); //设置指针
-
- if(bai==0) //百位为0时不显示
- writedata(' ');
- else
- writedata(0x30+bai);
- if(bai==0&&shi==0) //百位,十位为0时不显示十位
- writedata(' ');
- else
- writedata(0x30+shi);//显示十位
- writedata(0x30+ge); //显示个位
- writedata('.'); //显示小数点
- writedata(0x30+shifen);//显示十分位
-
- //显示里程
- y=milesum*2.08/10000;
- //除10*milesum得里程的长度(cm)(2为2cm 除100000化为km
- //乘10是为了下面得十分位 y为long型
- milshifen=y%10;
- milge=y/10%10;
- milshi=y/100%10;
- milbai=y/1000%10;
- milqian=y/10000%10;
- milwan=y/100000%10;
- milshiwan=y/1000000%10;
- setadd(3,3); //设置指针位置
- if(milwan==0)
- writedata(' ');
- else
- writedata(0x30+milwan);
- if(milwan==0&&milqian==0)
- writedata(' ');
- else
- writedata(0x30+milqian);
- if(milwan==0&&milqian==0&&milbai==0)
- writedata(' ');
- else
- writedata(0x30+milbai);
- if(milwan==0&&milqian==0&&milbai==0&&milshi==0)
- writedata(' ');
- else
- writedata(0x30+milshi);
- writedata(0x30+milge);
- writedata('.');
- writedata(0x30+milshifen)
- }
-
- /***************延迟ms*******************/
- void delay(int k)//
- {
- int d,s;
- for(d=k;d>0;d--)
- {
- for(s=110;s>0;s--);
- }
- }
- /****************写命令******************/
- void writecmd(uchar cmd)
- {
- e=0;
- rs=0;
- rw=0;
- P0=cmd;
- delay(5);
- e=1;
- delay(5);
- e=0;
- }
- /****************写数据******************/
- void writedata(uchar dat)
- {
- e=0;
- rs=1;
- rw=0;
- P0=dat;
- delay(5);
- e=1;
- delay(5);
- e=0;
- }
- /****************lcd初始化******************/
- void init()
- {psb=1; //设置并口工作方式
- writecmd(0x30);//8位数据模式,基本指令操作
- delay(5);
- writecmd(0x0c);//显示开,关光标
- delay(5);
- writecmd(0x01);//清显示屏内容
- delay(5);
- }
- /****************设定要显示的位置******************/
- // 1234行01234567列
- void setadd(uchar x,uchar y)
- {
- switch(x)
- {
- case 1: writecmd(0x80+y);break;
- case 2: writecmd(0x90+y);break;
- case 3: writecmd(0x88+y);break;
- case 4: writecmd(0x98+y);break;
- default:break;
- }
- }
- /* ================ 打开 ISP,IAP 功能 ================= */
- void ISP_IAP_enable(void)
- {
- EA = 0; /* 关中断 */
- ISP_CONTR = WaitTime; /* 写入硬件延时 */
- ISP_CONTR = 0x80; /* ISPEN=1 */
- }
- /* =============== 关闭 ISP,IAP 功能 ================== */
- void ISP_IAP_disable(void)
- {
- ISP_CONTR = 0x7f; /* ISPEN = 0 */
- ISP_TRIG = 0x00;
- EA = 1; /* 开中断 */
- }
- /* ================ 公用的触发代码 ==================== */
- void ISP_TRIGER(void)
- {
- ISP_IAP_enable(); /* 打开 ISP,IAP 功能 */
- ISP_TRIG = 0x46; /* 触发ISP_IAP命令字节1 */
- ISP_TRIG = 0xb9; /* 触发ISP_IAP命令字节2 */
- _nop_();
- }
- /* ==================== 字节读 ======================== */
- unsigned char byte_read(unsigned int byte_addr)
- {
- ISP_ADDRH = (unsigned char)(byte_addr >> 8); /* 地址赋值 */
- ISP_ADDRL = (unsigned char)(byte_addr );
- ISP_CMD = I0; /* 清除低3位 */
- ISP_CMD = RdCommand; /* 写入读命令 */
- ISP_TRIGER(); /* 触发执行 */
- ISP_IAP_disable(); /* 关闭ISP,IAP功能 */
- return (ISP_DATA); /* 返回读到的数据 */
- }
-
- /* ================== 扇区擦除 ======================== */
- void SectorErase(unsigned int sector_addr)
- {
- unsigned int iSectorAddr;
- iSectorAddr = sector_addr /* 取扇区地址 */
- ISP_ADDRH = (unsigned char)(iSectorAddr >> 8);
- ISP_ADDRL = 0x00;
- ISP_CMD = 0; /* 清空低3位 */
- ISP_CMD = EraseCommand; /* 擦除命令3 */
- ISP_TRIGER(); /* 触发执行 */
- ISP_IAP_disable(); /* 关闭ISP,IAP功能 */
- }
- /* ==================== 字节写 ======================== */
- void byte_write(unsigned int byte_addr, unsigned char original_data)
- {
- ISP_ADDRH = (unsigned char)(byte_addr >> 8); /* 取地址 */
- ISP_ADDRL = (unsigned char)(byte_addr);
- ISP_CMD = 0; /* 清低3位 */
- ISP_CMD = ISP_CMD | PrgCommand; /* 写命令2 */
- ISP_DATA = original_data; /* 写入数据准备 */
- ISP_TRIGER(); /* 触发执行 */
- ISP_IAP_disable(); /* 关闭IAP功能 */}</font></font>
另外给大家分享2个相关的程序:
参考资料.7z
(924.28 KB)
(下载次数: 12, 2020-5-15 17:25 上传)
下载积分: 黑币 -5