自行车里程表设计论文【摘要】以AT89S52型单片机为核心,实时测量并显示自行车行驶过程中的各项参数。
【关键词】单片机LCD干簧管累计里程速度【作品要求】设计一个可以适用自行车的轻便、省电、全天候野外使用的自行车里程表。
(1)基计要求总里程〈999.99km;可以轮流显示或选择显示(用十进制数):里程——当前行驶里程;速度——当前平均速度km/h;最大速度——本次行驶中的最大速度;时间——当前行驶累计时间,时、分、秒;电源不高于5V,体积小、结构可靠,便于安装及使用。
(2)发挥部分可以显示最大加速度;用可编程器件实现;用单片机实现【方案设计与讨论】1.速度测量原理测量自行车的速度的原理有两种:1)测量一定时间间隔t1里自行车车轮转过的圈数qs。
假设车轮周长为tc,则速度V=tc*qs/t12)测量自行车车轮转过一圈的时间t2,则速度V=tc/t2本里程表是根据原理2计算速度的。
2.传感器的选择1)红外对管。
把红外对管分别安装在自行车车轮的两侧,当车轮转动时,辐条会阻挡红外对管的光路,接收管输出低电平,单片机根据此信号可计算里程、速度等。
红外对管的优点是测量精度高,缺点是安装比较复杂和容易受外来光线、灰尘等的影响。
2)开关型霍尔传感器。
霍尔传感器是利用霍尔效应把磁输入信号转换成电信号的器件。
把开关型霍尔传感器安装在自行车贴近车轮的支架上,磁钢安装在辐条上,当磁钢靠近霍尔传感器的时候,传感器输出一个无抖动的低电平,单片机根据此信号可计算里程、速度等。
霍尔传感器的优点是稳定和安装简易,缺点是成本较高。
3)干簧管。
干簧管是一种磁敏的有触点无源电子开关元件,应用在里程表上的原理与开关型霍尔传感器类似,把干簧管安装在自行车贴近车轮的支架上,磁钢安装在辐条上,当磁钢靠近霍尔传感器的时候,干簧管闭合,单片机根据此信号可计算里程、速度等。
干簧管的优点是成本低廉和安装简易,缺点是比较脆弱和不够稳定。
本里程表选用干簧管作为传感器。
给干簧管套上废弃笔杆,可克服其脆弱的缺点;软件防抖可克服其不够稳定的缺点。
3.显示模块的选择1)动态扫描LED数码管显示。
里程表的显示内容以数字为主,利用LED数码管可基本满足使用要求,且成本较低。
但是用动态扫描的方式驱动数码管,亮度太低,在阳光下几乎看不见显示内容,失去使用价值。
2)串行静态LED数码管显示。
把单片机的串行口设置为方式0(同步移位寄存器),输出显示信息,可实现LED数码管的静态显示,其亮度令人满意。
但由于要使用74HC164/74LS164串并转换芯片驱动LED数码管,因此会带来体积大、成本高、功耗高等的缺点。
3)LCD液晶显示模块。
液晶显示模块具有体积小、功耗低、显示内容丰富等特点,现在字符型液晶显示模块已经是单片机应用设计中最常用的信息显示器件了。
本里程表使用1602 LCD作为显示模块。
【功能描述】以AT89S52型单片机为核心,实时测量并显示自行车行驶过程中的各项参数,包括当前行驶累计时间、当前行驶累计里程(m/km自动调整)、当前速度(km/h)、最大速度(km/h)、平均速度(km/h)、加速度(m/s2)、当前时间等,各参数分屏显示。
可更改自行车轮胎直径,适应不同的自行车,通用性好。
本里程表具有时钟功能,不安装在自行车上时也可作为时钟使用,实用性高。
【操作说明】里程表板面如上图所示,包括电源2pin排针、干簧管3pin排针、液晶显示器、液晶背光开关、电源开关、电源LED指示灯、功能按钮SW1-SW5、Reset 按钮。
接通电源或按Reset后,显示欢迎画面:DigitalBikeMeterWelcome….2秒后进入时钟设置画面:Set Time00:00:00按SW1-SW3调整时钟后,按SW4确认。
接着进入自行车轮胎半径设置画面:Set Bike Tire L=55cm默认设置为55cm,对应22英寸自行车轮胎半径。
按SW1、SW2调整轮胎直径后,按SW4确认,里程表开始工作。
各项参数分成四屏选择显示,按动SW5(Disp)按以下次序进行切换:【结构框图】系统由干簧管、设置选择模块、显示模块、蜂鸣器模块、供电模块和单片机小系统构成。
由设置选择模块选择显示模式后,单片机实时采集、处理后显示。
【具体硬件电路及工作原理】S::当前行驶累计里程(单位自动调整) S<1000m,格式xxx.x m, S>1000m,格式xxx.xx km ) Time :当前行驶累计时间V: 当前速度(km/h ) Vm: 最大速度(km/h )Va: 平均速度(km/h ) a: 加速度(m/s2)Time Now 当前时间里程、速度、加速度等都是由干簧管测量。
已知自行车轮胎的直径tl,轮子每转动一圈,安装在车轮辐条上的磁钢接近干簧管一次,干簧管闭合,送一个下降沿信号给单片机的外部中断0,产生一次中断,圈数qs加1。
两个相邻的下降沿信号的时间由单片机定时器1计时(设为ssj),那么计算累计里程S和当前速度V的公式为:S=tl*3.14*qs V=tl*3.14/ssj若速度大于28.8km/h(8m/s)则P2.4输出低电平,蜂鸣器报警,提示速度过大。
处理速度数据时同时刷新最大速度及计算Vm、平均速度Va、加速度a。
单片机定时器0定时时间为50ms,每20次刷新系统时钟及计算累计行驶时间。
【相关元器件及其简介】(1)AT89S52单片机简介AT89S52是一种低功耗、高性能CMOS 8位微控制器,具有8K 在系统可编程Flash 存储器。
使用Atmel 公司高密度非易失性存储器技术制造,与工业80C51 产品指令和引脚完全兼容。
片上Flash允许程序存储器在系统可编程,亦适于常规编程器。
在单芯片上,拥有灵巧的8 位CPU 和在系统可编程Flash,使得AT89S52为众多嵌入式控制应用系统提供高灵活、超有效的解决方案。
AT89S52具有以下标准功能: 8k字节Flash,256字节RAM,32 位I/O 口线,看门狗定时器,2 个数据指针,三个16 位定时器/计数器,一个6向量2级中断结构,全双工串行口,片内晶振及时钟电路。
另外,AT89S52 可降至0Hz 静态逻辑操作,支持2种软件可选择节电模式。
空闲模式下,CPU停止工作,允许RAM、定时器/计数器、串口、中断继续工作。
掉电保护方式下,RAM内容被保存,振荡器被冻结,单片机一切工作停止,直到下一个中断或硬件复位为止。
(2)1602LCD液晶显示模块液晶显示模块具有体积小、功耗低、显示内容丰富等特点,现在字符型液晶显示模块已经是单片机应用设计中最常用的信息显示器件了。
本里程表使用常见的1602字符型LCD模块。
1602可以显示2行16个字符,有8位数据总线D0-D7,和RS、R/W、E三个控制端口,工作电压为5V,并且带有字符对比度调节和背光。
(3)干簧管干式舌簧管简称干簧管,是利用磁场信号来控制的一种线路开关元件。
干式舌簧管以其结构固有的特点,目前已被广泛应用到各种自动化和微型化的自动控制零件及通讯检测设备中,作为灵敏而快速的开闭及转换电路的执行工件。
在本里程表中,干簧管安装在自行车贴近辐条的支架上,磁钢安装在辐条上。
当磁钢远离干簧管时,干簧管断开,单片机外部中断0(P3.2)保持高电平。
当磁钢靠近干簧管时,干簧管闭合,送一个下降沿信号给单片机的外部中断0,产生一次中断。
【系统调试过程简述】自行车里程表的原理比较简单,我编好一个比较简单但是核心的程序(只计算里程和速度)在Keil C上仿真,经过短暂的调试就成功了,这证明我关于里程表的最核心的想法是正确的,这给了我后面的制作和调试很大的信心。
紧接着是在单片机上搭建硬件,重点是按键防抖动。
我的模式选择按钮是接到单片机的外部中断的,一开始我按照普通按钮的防抖动的方法测试,没有成功。
于是在中断服务程序的开始关闭中断,末尾再开中断,还是不行。
后来在中断服务程序的末尾加了这样一句:IE1=0;其作用就是清除中断标志。
因为即使单片机的外部中断是关闭的,但只要在中断服务程序执行期间按键抖动,中断标志又会置1,若没有清除中断标志,退出中断服务程序后又会执行一次服务程序,这样按一次按键就会执行两次中断服务程序。
我的里程表的最初版本是利用串行LED数码管显示作为显示模块的(简介请参阅附录二)。
但制成后发现体积太大,而且功耗比较高,不适合用于要求小巧、坚固、耐用的自行车里程表上。
后来改用1602LCD,里程表体积大幅度减小,经测试,背光开启时电流为45mA左右,背光关闭时为25mA左右,2500mah AA×4的电池组供电最多可达100小时,可以满足使用要求。
附录程序流程图及程序代码程序代码#include<reg52.h>#define uchar unsigned char/****************************************************************引脚定义:P20=SW1时钟设置时更改“时”,直径设置时更改十位P21=SW2 时钟设置时更改“分”的十位,直径设置时更改个位P22=SW3 时钟设置时更改“分”的个位P23=Enter确认更改设置P32=Disp 正常工作时更改显示模式********************************************************************* *******/sbit P20=P2^0;sbit P21=P2^1;sbit P22=P2^2;sbit P23=P2^3;sbit P32=P3^2;sbit RS=P3^7;sbit RW=P3^6;sbit E=P3^5;sbit busy=P1^7;/****************************************************************变量定义:i 延时变量qsls 圈数临时变量,用于判断里程显示选用m还是km作为单位qs 圈数sj 累计行驶时间,单位为秒totallc 累计里程pjsd 平均速度ssji 定时器T120ms中断次数,用于计算速度等sji 本次速度对应的20ms中断次数sjiold 上次速度对应的20ms中断次数aa 加速度sd2 当前速度mxsd 用于更新最大速度mxsd2 最大速度对应的20ms次数tc 轮胎周长*s 字符串显示子程序字符指针line 字符串显示子程序列数row 字符串显示子程序行数com 写控制字dat 显示码字gw 累计里程、当前速度、累计时间、最大速度、平均速度、加速度的个位sw 累计里程、当前速度、累计时间、最大速度、平均速度、加速度的十位bw 累计里程、当前速度、累计时间、最大速度、平均速度、加速度的百位qw 累计里程、当前速度、累计时间、最大速度、平均速度、加速度的千位ww 累计里程、累计时间的万位sww 累计里程、累计时间的十万位ssj 定时器T0 50ms中断次数,每20次sj加1cgw 当前时间(时钟)的个位csw 当前时间(时钟)的十位cbw 当前时间(时钟)的百位cqw 当前时间(时钟)的千位cww 当前时间(时钟)的万位csww 当前时间(时钟)的十万位cww2 当前时间(时钟)的辅助万位MODE 显示模式tr1 轮胎设置变量1(十位)tr2 轮胎设置变量2(个位)********************************************************************* *******/unsigned int i,qsls;unsigned long qs,sj,totallc,pjsd,ssji,sji,sjiold,aa,sd2,mxsd,mxsd2,tc;uchar*s,line,row,com,dat,gw,sw,bw,qw,ww,sww,ssj,cgw,csw,cbw,cqw,cww,csww,cww2,M ODE,tr1,tr2;/***********************************voidwait()*****************************该函数的作用是对LCD进行检测,看LCD是否处于忙的状态.当bflag=1时表示忙,此时不可以向LCD进行读写操作.而当busy=0时,表示可以向它读写数据.********************************************************************* *******/void wait(){P1=0xff;RW=1; RS=0;do{E=0;E=1;}while(busy==1);}/**************************************voiddispone()*******************************该函数的作用是向LCD写入数据并显示出来.********************************************************************* **************/void dispone(dat){P1=dat;RW=0; RS=1;E=0; E=1;wait();}/**********************************voidwrcom()*******************************该函数的作用是向LCD写入控制字.********************************************************************* *********/void wrcom(com){P1=com;RW=0; RS=0;E=0; E=1;wait();}/***************************************voidinit_LCD()*******************************该函数的作用是初始化LCD.********************************************************************* ********************/void init_LCD(){wrcom(0x01);wrcom(0x06);wrcom(0x38);wrcom(0x0c);}/****************************************voiddispmore()********************************该函数的作用是向LCD写入一串数据,并把数据串显示出来.********************************************************************* *********************/void dispmore(line,row,uchar dat[],i) //格式为dispmore(第几行,第几列,开始要显示的字符地址,显示几个字符);{uchar com;s=dat;if(line==1){com=0x80+row-1;wrcom(com);while((i--)!=0&&com<=0x8f){dispone(*s);com++;s++;}}else{com=0xc0+row-1;wrcom(com);while((i--)!=0&&com<=0xcf){dispone(*s);com++;s++;}}}/****************************************voidttimep()********************************累计行驶时间数据处理及显示子程序********************************************************************* *********************/void ttimep(){sww=sj/36000;ww=sj%36000/3600;qw=sj%3600/600;bw=sj%600/60;sw=sj%60/10;gw=sj%10;wrcom(0xc1);dispone('T');dispone('i');dispone('m');dispone('e');dispone(' ');dispone(' ');dispone(sww+0x30);dispone(ww+0x30);dispone(':');dispone(bw+0x30);dispone(':');dispone(sw+0x30);dispone(gw+0x30);}/****************************************voidtimer1()********************************T1中断服务子程序,每20ms中断一次,ssji加1,根据公式S=tc*qs和V=tc/ssj 计算累计里程和********************************************************************* *********************/void timer1() interrupt 3{ssji++;TH1=0xb1;TL1=0xdf;}/****************************************voidlcp()********************************累计里程数据处理和显示子程序,若<999m则单位为m,若>999m则单位为km ********************************************************************* *********************/void lcp(){qsls=100000/tc;if(qs>qsls){sww=qs*tc/10000000;ww=qs*tc%10000000/1000000;qw=qs*tc%1000000/100000;bw=qs*tc%10000000/10000;sw=qs*tc%10000/1000;wrcom(0x81);dispone('S');dispone(' ');dispone(' ');dispone(' ');dispone(' ');dispone(sww+0x30);dispone(ww+0x30);dispone(qw+0x30);dispone('.');dispone(sw+0x30);dispone(' ');dispone('k');dispone('m');}else{qw=qs*tc%100000/10000;bw=qs*tc%10000/1000;sw=qs*tc%1000/100;gw=qs*tc%100/10;wrcom(0x81);dispone('S');dispone(' ');dispone(' ');dispone(' ');dispone(' ');dispone(' ');dispone(' ');dispone(qw+0x30);dispone(bw+0x30);dispone(sw+0x30);dispone('.');dispone(gw+0x30);dispone(' ');dispone('m');}}/****************************************voidpjsdp()********************************平均速度数据处理及显示子程序********************************************************************* *********************/void pjsdp(){pjsd=qs*tc*36/sj;qw=pjsd%100000/10000;bw=pjsd%10000/1000;sw=pjsd%1000/100;gw=pjsd%100/10;wrcom(0x82);dispone('V');dispone('a');dispone(' ');dispone(' ');dispone(qw+0x30);dispone(bw+0x30);dispone('.');dispone(sw+0x30);dispone(gw+0x30);dispone(' ');dispone('k');dispone('m');dispone('/');dispone('h');}/****************************************voidsdp()********************************当前速度数据处理及显示子程序********************************************************************* *********************/void sdp(){if(sji!=0)sd2=tc*1800/sji;else sd2=0;qw=sd2%100000/10000;bw=sd2%10000/1000;sw=sd2%1000/100;gw=sd2%100/10;wrcom(0x82);dispone('V');dispone(' ');dispone(' ');dispone(' ');dispone(qw+0x30);dispone(bw+0x30);dispone('.');dispone(sw+0x30);dispone(gw+0x30);dispone(' ');dispone('k');dispone('m');dispone('/');dispone('h');}/****************************************voidmxsdp()********************************最大速度数据处理及显示子程序********************************************************************* *********************/void mxsdp(){if(mxsd!=0)mxsd2=tc*1800/mxsd;else mxsd2=0;qw=mxsd2%100000/10000;bw=mxsd2%10000/1000;sw=mxsd2%1000/100;gw=mxsd2%100/10;wrcom(0xc2);dispone('V');dispone('m');dispone(' ');dispone(' ');dispone(qw+0x30);dispone(bw+0x30);dispone('.');dispone(sw+0x30);dispone(gw+0x30);dispone(' ');dispone('k');dispone('m');dispone('/');dispone('h');}/****************************************voidclkp()********************************当前时间(时钟)数据处理及显示子程序********************************************************************* *********************/void clkp(){wrcom(0xc4);dispone(csww+0x30);dispone(cww+0x30);dispone(':');dispone(cqw+0x30);dispone(cbw+0x30);dispone(':');dispone(csw+0x30);dispone(cgw+0x30);}/****************************************voidapfz()********************************加速度数据处理及显示子程序辅助程序********************************************************************* *********************/void apfz(){wrcom(0xc2);dispone('a');wrcom(0xc6);dispone(qw+0x30);dispone(bw+0x30);dispone('.');dispone(sw+0x30);dispone(gw+0x30);dispone(' ');dispone('m');dispone('/');dispone('s');dispone('2');}/****************************************voidap()********************************加速度数据处理及显示子程序********************************************************************* *********************/void ap(){if (sjiold==sji){gw=sw=bw=qw=0;apfz();}else if(sjiold<sji){aa=360*tc*(sji-sjiold)/sji/sjiold/(sji+sjiold);qw=aa/1000;bw=aa%1000/100;sw=aa%100/10;gw=aa%10;wrcom(0xc5);dispone('-');apfz();}else if(sjiold>sji){aa=360*tc*(sjiold-sji)/sji/sjiold/(sji+sjiold);qw=aa/1000;bw=aa%1000/100;sw=aa%100/10;gw=aa%10;wrcom(0xc5);dispone(' ');apfz();}}/****************************************voidint0()********************************外部中断0(干簧管)中断服务程序********************************************************************* ********************/void int0() interrupt 0{EX0=0;qs++; //圈数+1TR1=0;sjiold=sji; //停止T1计时sji=ssji;if (mxsd!=0) //更新最大速度{if (sji<mxsd) mxsd=sji;}else mxsd=sji;TH1=0xb1; //重置T1定时常数TL1=0xdf;ssji=0;TR1=1; //T1重新开始计时switch(MODE){case 0:wrcom(0x01);lcp();ttimep();break;case 1:wrcom(0x01);sdp();mxsdp();break;case 2:wrcom(0x01);pjsdp();ap();break;case 3:wrcom(0x01);dispmore(1,5,"Time Now",8);clkp();break;default:break;}for(i=0;i<32000;i++);IE0=0;EX0=1;}/****************************************voidint1()********************************显示模式选择按钮(Disp)中断服务子程序********************************************************************* *********************/void int1() interrupt 2{EX1=0;if(++MODE>3) MODE=0;switch(MODE){case 0:wrcom(0x01);lcp();ttimep();break;case 1:wrcom(0x01);sdp();mxsdp();break;case 2:wrcom(0x01);pjsdp();ap();break;case 3:wrcom(0x01);dispmore(1,5,"Time Now",8);clkp();break;default:break;}for(i=0;i<32000;i++);IE1=0;EX1=1;}/****************************************vvoidtimer0()********************************T0中断服务子程序,主要用于计算累计行驶时间,50ms一次中断,每20次sj++,更新累计行驶时间(单位秒)********************************************************************* **********************/void timer0() interrupt 1{TH0=0x3c;TL0=0xb0;if(++ssj>19){sj++;ssj=0;cgw++;if(cgw>9){cgw=0;csw++;if(csw>5){csw=0;++cbw;if(cbw>9){cbw=0;++cqw;if(cqw>5){cqw=0;cww2++;if(cww2>23)cww2=0;csww=cww2/10;cww=cww2%10;}}}}if (MODE==0) {lcp();ttimep();}if (MODE==3) clkp();}}/****************************************voidmain()********************************主程序,初始化LCD,显示欢迎信息,更改时钟、直径设置,开中断、计时器等********************************************************************* *********************/void main(){init_LCD();dispmore(1,1,"-Digital Meter-",16);for(i=0;i<25000;i++);dispmore(2,1," Welcome ",16);for(i=0;i<25000;i++);dispmore(2,1," Welcome. ",16);for(i=0;i<25000;i++);dispmore(2,1," Welcome.. ",16);for(i=0;i<25000;i++);dispmore(2,1," Welcome... ",16);for(i=0;i<25000;i++);dispmore(2,1," Welcome.... ",16);for(i=0;i<25000;i++);dispmore(2,1," Welcome..... ",16);for(i=0;i<30000;i++);wrcom(0x01);MODE=0;qs=0;P2=0xff;cgw=csw=cbw=cqw=cww=csww=cww2=0;wrcom(0x01);dispmore(1,5,"Set Time",8);clkp();while(1){if(P23==0){for(i=0;i<4000;i++);if(P23==0)break;}if(P20==0){for(i=0;i<4000;i++);if(P20==0){cww2++;if(cww2>23)cww2=0;csww=c ww2/10;cww=cww2%10;clkp();for(i=0;i<32000;i++);}}if(P21==0){for(i=0;i<4000;i++);if(P21==0){if(++cqw>5)cqw=0;clkp();for(i=0;i<320 00;i++);}}if(P22==0){for(i=0;i<4000;i++);if(P22==0){if(++cbw>9)cbw=0;clkp();for(i=0;i<320 00;i++);}}}for(i=0;i<30000;i++);tr1=5;tr2=5;wrcom(0x01);dispmore(1,1,"Set Bike Tire L=",16);wrcom(0xc9);dispone('c');wrcom(0xca);dispone('m');P2=0xff;wrcom(0xc6);dispone(tr1+0x30);wrcom(0xc7);dispone(tr2+0x30);while(1){if(P20==0){for(i=0;i<4000;i++);if(P20==0){if(++tr1>9)tr1=0;wrcom(0xc6);dispone(t r1+0x30);for(i=0;i<32000;i++);}}if(P21==0){for(i=0;i<4000;i++);if(P21==0){if(++tr2>9)tr2=0;wrcom(0xc7);dispone(t r2+0x30);for(i=0;i<32000;i++);}}if(P23==0){for(i=0;i<4000;i++);if(P23==0)break;}}tc=3.14*(10*tr1+tr2);TMOD=0x11; //T0和T1均是定时器方式TH0=0x3c; //置定时常数TL0=0xb0;TH1=0xb1;TL1=0xdf;IT0=1; //外部中断触发方式均是边缘触发IT1=1;IE=0x9f; //开中断IP=0x02;TR0=1; //T0和T1开始计时TR1=1;P32=1;IE0=0;wrcom(0x01);for (;;);}。