第11章单片机应用系统设计实例A11.1单片机电子时钟的设计B11.2多路数字电压表的设计11.1单片机电子时钟的设计通常通过用单片机设计电子时钟有两种方法:1、通过单片机内部的定时器/计数器。这种方法硬件线路简单,采用软件编程实现时钟计数,一般称为软时钟。系统的功能一般与软件设计相关,通常用在对时间精度要求不高的场合;2、采用时钟芯片。它的功能强大,功能部件集成在芯片内部,自动产生时钟等相关功能。硬件成本相对较高,软件编程简单。通常用在对时钟精度要求较高的场合。11.1.1软时钟的基本原理软时钟是利用单片机内部的定时器/计数器来实现,它的处理过程如下:首先设定单片机内部的一个定时器/计数器工作于定时方式,对机器周期计数形成基准时间(如50ms或10ms),然后用另一个定时器/计数器或软件计数的方法对基准时间计数形成秒(对50ms记数20次或10ms计数100次),秒计60次形成分,分计60次形成小时,小时计24次则计满一天。然后通过数码管把它们的内容在相应位置显示出来即可。11.1.2系统硬件电路的设计共阳极阴极驱动取反AT89C5211.1.3系统软件程序的设计电子时钟的软件系统由主程序和子程序组成,主程序程序包含初始化参数设置、按键处理、数码管显示模块等,在设计时各个模块都采用子程序结构设计,在主程序中调用。由于定时器/计数器采用中断方式处理,因此还要编写定时器/中断服务子程序,在定时器/计数器中断服务程序中对时钟进行调整。开始显示单元清零T0、T1设为16位计数模式允许T0中断调用显示子程序进入功能程序按下键否?否是一.主程序主程序执行流程如图,主程序先对显示单元和定时器/计数器初始化,然后重复调用数码管显示模块和按键处理模块,当有键按下,则转入相应的功能程序。二.数码管显示模块本系统共用8个数码管,从右到左依次显示秒个位、秒十位、横线、分个位、分十位、横线、时个位和时十位。数码管显示的信息用8个内存单元存放,这8个内存单元称为显示缓冲区,其中秒个位和秒十位、分个位和分十位、时个位和时十位分别由秒数据、分数据和小时数据分拆得到。在本系统中数码管显示采用软件译码动态显示。在存储器中首先建立一张显示信息的字段码表,显示时,先从显示缓冲区中取出显示的信息,然后通过查表程序在字段码表中查出所显示的信息的字段码,从P0口输出,同时在P2口将对应的位选码输出选中显示的数码管,就能在相应的数码管上显示显示缓冲区的内容。三.定时器/计数器T0中断服务程序定时器/计数器T0用于时间计时。选择方式1,重复定时,定时时间设为50ms,定时时间到则中断,在中断服务程序中用一个计数器对50ms计数,计20次则对秒单元加1,秒单元加到60则对分单元加1,同时秒单元清0;分单元加到60则对时单元加1,同时分单元清0;时单元加到24则对时单元清0,标志一天时间计满。在对各单元计数的同时,把它们的值放到存储单元的指定位置。定时器/计数器T0中断服务程序流程图如图。开始现场保护,重置初值启动下一个50ms50ms计数器加1秒单元加1,50ms计数器清0,秒写入秒个位和秒十位50ms计数器=20?否是分单元加1,秒单元清0,分写入分个位和分十位秒单元=60?时单元加1,分单元清0,时写入时个位和时十位分单元=60?时单元清0时单元=24?中断返回否否否是是是定时器/计数器T0中断服务程序四.按键处理模块按键处理设置为:如没有按键,则时钟正常走时。当按下K0按键时,进入调分状态,时钟停止走动;按K1可K2按键可进行加1或减1操作;继续按K0键可分别进行分和小时的调整;最后按K0键将退出调整状态,时钟开始计时运行。五.汇编语言源程序清单ORG0000HLJMPSTARTORG000BHLJMPINTT0;主程序START:MOVR0,#70HMOVR7,#0CHINIT:MOV@R0,#00HINCR0DJNZR7,INIT;70H-7CH全部清0MOVTMOD,#01H;T0工作在方式1MOVTL0,#0B0H;T0定时50msMOVTH0,#3CHSETBEASETBET0SETBTR0START1:LCALLSCAN;调用显示子程序LCALLKEYSCAN;调用键盘子程序SJMPSTART1;延时1ms子程序DL1MS:MOVR6,#14H;1µsDL1:MOVR7,#19H;1µsDL2:DJNZR7,DL2;25次×2µs=50µsDJNZR6,DL1;2µsRET;2µs1µs+(25次×2µs+3µs)×20次+2µs=1063µs=1.063ms≈1ms;延时20ms子程序DL20MS:ACALLSCANACALLSCANACALLSCANRET1T1T2T2T2T;数码管显示程序SCAN:MOVA,78H;秒MOVB,#0AHDIVAB;A-商,B-余数MOV71H,A;秒十位MOV70H,B;秒个位MOVA,79H;分MOVB,#0AHDIVABMOV74H,AMOV73H,BMOVA,7AH;时MOVB,#0AHDIVABMOV77H,AMOV76H,BMOVR1,#70H;循环扫描8位显示MOVR5,#0FEH;位选码初值MOVR6,#08H;8位显示SCAN1:MOVA,R5MOVP2,AMOVA,@R1MOVDPTR,#TABMOVCA,@A+DPTR;远程查表MOVP0,A;段选码MOVA,R5LCALLDL1MSINCR1MOVA,R5RRAMOVR5,ADJNZR6,SCANMOVP2,#0FFHMOVP0,#0FFHRETTAB:DB0C0H,0F9H,0A4H,0B0H,99H,92H,82H,0F8H,80H,90H,0BFH;“0~9”,“-”的七段共阳极字段码;定时器/计数器T0中断服务程序INTT0:PUSHACC;现场保护PUSHPSWCLRET0CLRTR0MOVTL0,#0B0H;T0定时50msMOVTH0,#3CHSETBTR0INC7BH;50ms定时到记数MOVA,7BHCJNEA,#14H,OUTT0;计20次(即1s)MOV7BH,#00INC78H;秒加1MOVA,78HCJNEA,#3CH,OUTT0;计60sMOV78H,#00INC79H;分加1MOVA,79HCJNEA,#3CH,OUTT0;不等跳,计60分MOV79H,#00INC7AH;时加1MOVA,7AHCJNEA,#18H,OUTT0;计24时MOV7AH,#00OUTT0:SETBET0RETI;按键处理程序KEYSCAN:CLREAJNBP1.0,KEYSCAN0;为0跳JNBP1.1,KEYSCAN1JNBP1.2,KEYSCAN2KEYOUT:SETBEARETKEYSCAN0:LCALLDL20MS;K0去抖JBP1.0,KEYOUT;为1跳WAIT0:JNBP1.0,WAIT0;P1.0=0跳INC7CH;K0松开后7CH加1CLRET0CLRTR0CJNEA,#03H,KEYOUT;不等跳;K0按1下或2下返回,按3下7CH清0,不能调秒MOV7CH,#00SETBET0SETBTR0SJMPKEYOUTKEYSCAN1:LCALLDL20MS;K1键去抖JBP1.1,KEYOUTWAIT1:JNBP1.1,WAIT1;P1.1=0跳,等待松开K1MOVA,7CHCJNEA,#02H,KSCAN11;不等跳,K0按2下,调整分INC79H;分加1MOVA,79HCJNEA,#3CH,KEYOUT;为60分吗?MOV79H,#00SJMPKEYOUTKSCAN11:INC7AH;时加1MOVA,7AHCJNEA,#18H,KEYOUT;为24时吗?MOV7AH,#00SJMPKEYOUT//按1下调时,按2下调分KEYSCAN2:LCALLDL20MS;K2去抖JBP1.2,KEYOUT;P1.2=1跳WAIT2:JNBP1.2,WAIT2;松开K2MOVA,7CHCJNEA,#02H,KSCAN21;K2按2下吗?按2下调整分DEC79H;分减1MOVA,79HCJNEA,#0FFH,KEYOUTMOV79H,#3BH;计59分SJMPKEYOUTKSCAN21:DEC7AH;时减1MOVA,7AHCJNEA,#0FFH,KEYOUTMOV7AH,#17H;计23时SJMPKEYOUTEND六.C语言源程序清单//采用8位LED软件译码动态显示程序//使用AT89C51单片机,12MHz晶振,P0口输出字段码,P2口输出位选码,用共阳极LED数码管,key0为调时位选择键,key1为加1键,key2为减1键#include“reg51.h”#definecharunsignedcharcharcodedis_7[12]={0xc0,0xf9,0xa4,0xb0,0x99,0x92,0xb2,0xf8,0x80,0x90,0xff,0xbf};//共阳极LED数码管“0~9”、“灭”和“-”的字段码chardatascan_con[8]={0xfe,0xfd,0xfb,0xf7,0xef,0xdf,0xbf,0x7f};//位选择码chardatadis[8]={0x00,0x00,0x0b,0x00,0x00,0xb0,0x00,0x00};//显示缓冲区,时、分、秒初始为0,0x0b为“-”的编码chardatatimedata[3]={0x00,0x00,0x00};//分别为秒、分和小时的值chardatams50=0x00,con=0x00,con1=0x00,con2=0x00;//计数次数初值sbitkey0=P1^0;sbitkey1=P1^1;sbitkey2=P1^2;//1ms延时函数delay1ms(intt){inti,j;for(i=0;it;i++)for(j=0;j120;j++);}//按键处理函数keyscan()//按1下调分,按2下调时{EA=0;if(key0==0)//按下K0{delay1ms(10);//K0去抖while(key0==0)con++;TR0=0;ET0=0;if(con=3){con=0;TR0=1;ET0=1;}}}if(con!=0){if(key1==0){delay1ms(10);//K1去抖while(key1==0);timedata[con]++;if(con==2)con1=24;elsecon1=60;if(timedata[con]=con1){timedata[con]=0;}}}if(con!=0){if(key2==0){delay1ms(10);//K2去抖while(key2==0);timedata[con]--;if(con==2)con2=23;elsecon2=59;if(timedata[con]=0){timedata[con]=con2;}}}EA=1;}//数码管显示函数scan(){chark;dis[0]=timedata[0]%10;dis[1]=timedata[0]/10;//秒dis[3]=timedata[1]%10;dis[4]=timedata[1]/10;//分dis[6]=timedata[2]%10;dis[7]=timedata[2]/10;//时for(k=0;k8;k++){P0=dis_7[dis[k]];P2=scan_con[k];delay1ms(1);P2=0xff;}}//主函数main(){TH0=0x3c;TL0=0xb0;//T0定时50msTMOD=0x01;ET0=1;TR0=1;EA=1;while(1){scan();keyscan();}}//定时器、计数器T0中断服务函数voidtime_intt0(void)interrupt1{ET0=0;TR0=0;TH0=0x3c;TL0=0xb0;TR0=1;ms50++;If(ms50==20)//循环20次,计1s{ms50=0x00;timedata[0]++;//秒