提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档
文章目录
- 基本要求
- 设计思路
- 代码
- 最终效果
基本要求
使用定时器/计数器实现电子钟,用LED数码管显示时、分、秒,24小时制,数码管上电初始显示为“12.00.00”,到24小时清零,然后反复,晶振11.0592MHz。理论误差小于0.1秒/天。
扩展要求:简单的可调电子钟。增加“时增加”、“分增加”2个按键,按下“时增加”按键,时显示加1,加满24归零,按下“分增加”按键时,分显示加1,加满60归零。
设计思路
开启定时器中断,在定时器中断函数中设置时间变量,用于在main函数中计算小时, 分钟,秒。这里我采用的是定时器0的工作方式一,因为从定时,计数器产生中断请求到响应中断,需要3~8个机器周期。定时中断子程序中的数据人栈和重装定时,计数器的初值还需要占用数个机器周期。此外,从中断入口转到中断子程序也要占用一定的 机器周期。所以要在定时器中断中进行误差补偿,我开发板中用的是11.0592M的晶振, 那么一个机器周期为(1/11.0592M)*12,定时50ms为(50*10^-3)/机器周期=46080。可以 将TH0和TL0从产生溢出到被重新赋值的已有的计数值读出,并补偿到计数值初值中去,可以消除定时/计数器的定时误差。
先在定时器中断中禁止所有中断请求,关闭T0,即EA=0,TR0=0,将TL0中已计数 值写入修正变量fixtime,并加上修正操作所需的机器周期,经过仿真调试大概为10 个机器周期,即0x0A,所以fixtime=TL0+0x0A,再将fixtime赋给TL0,因为 (65536-46080)%256=0,所以新的TL0=0+fixtime,因为(65536-46080)/256=76=0x4C,且在 修正TL0时可能产生进位,要补偿到TH0,所以TH0=0x4C+(char)CY,CY为PSW寄存器中的进位标志位,之后再开启所有中断请求,开启T0,即EA=1,TR0=1。这样就完成了整个定时器误差补偿的过程。
利用数码管的动态扫描方式,将计算得出的时间分别给到给数码管显示数组中的常量表达式中,让数码管动态显示时间。
利用两个按键改变小时和分钟的数值,那么按键应进行消抖处理,且不能用while语句 进行按键释放处理,那样可能会导致数码管闪动。可以先定义一个有返回值的按键扫描 函数Key_Scan( ),让它返回按键值1或2,无按键按下则返回0。再定义一个按键处理 函数KEY_Handle( ),在函数中进行位运算,key_value用于记录按键 值,key_up用于记 录按键释放,key_down用于记录哪个按键按下,key_old用于记录上次的按键值,此函数可以实现“记忆”功能,即如果按键一直按着,也不会出现多次触发的现象,也解决了用while语句检测按键释放而导致数码管闪动的问题。再将此函数放入定时器1的中断处理函数中,进行每50ms一次的按键扫描,定时器1同样也进行误差补偿处理。
代码
代码如下:
#include <reg52.h> #include <intrins.h> #define uchar unsigned char #define uint unsigned int sbit dula = P2^6; sbit wela = P2^7; sbit key1 = P3^4; sbit key2 = P3^5; uchar fixtime; uint count_miao,count_fen0,count_fen1,count_shi,num_miao,num_fen,num_shi; uint ge,shi,bai,qian,wan,shiwan; uchar key_value,key_up,key_down,key_old; float number,uwTick,uwTick_KEY; uchar table[] = { 0x3f,0x06,0x5b,0x4f, 0x66,0x6d,0x7d,0x07, 0x7f,0x6f,0x77,0x7c, 0x39,0x5e,0x79,0x71 }; uchar table_dot[]= { 0xbf,0x86,0xdb,0xcf, 0xe6,0xed,0xfd,0x87, 0xff,0xef,0xf7,0xfc, 0xb9,0xde,0xf9,0xf1 }; void delay(unsigned int x) { unsigned int i,j; for(i=1;i>0;i--) for(j=x;j>0;j--); } void clean() { wela = 1; P0 = 0xff; wela = 0; dula = 1; P0 = 0x00; dula = 0; } void dula_open() { dula = 1; dula = 0; P0 = 0xff; } void wela_open() { wela = 1; wela = 0; delay(1); clean(); } void display(uint shiwan,uint wan,uint qian,uint bai,uint shi,uint ge) { P0 = table[shi]; dula_open(); P0 = 0xef; wela_open(); P0 = table[ge]; dula_open(); P0 = 0xdf; wela_open(); P0 = table[qian]; dula_open(); P0 = 0xfb; wela_open(); P0 = table_dot[bai]; dula_open(); P0 = 0xf7; wela_open(); P0 = table[shiwan]; dula_open(); P0 = 0xfe; wela_open(); P0 = table_dot[wan]; dula_open(); P0 = 0xfd; wela_open(); } uchar key_scan() { uchar key = 0; if(key1 == 0) { return key = 1; } if(key2 == 0) { return key = 2; } return key; } void key_handle() { key_value = key_scan(); key_down = key_value & (key_value ^ key_old); key_up = ~key_value & (key_value ^ key_old); key_old = key_value; if(key_down == 1) { num_shi++; if(num_shi == 24)num_shi = 0; } if(key_down == 2) { if(num_fen == 59) { num_fen = 0; num_shi++; if(num_shi == 24)num_shi=0; } else num_fen++; } } void T0_Handle_Program() { if(count_miao == 20) { count_miao = 0; num_miao++; if(num_miao == 60)num_miao = 0; } if(count_fen0 == 1200) { count_fen0 = 0; num_fen++; if(num_fen == 60) { num_fen=0; num_shi++; if(num_shi == 24)num_shi=0; } if(count_fen1 == 72000) { num_fen=0; } } if(count_shi == 72000) { count_shi = 0; num_shi++; if(num_shi == 24)num_shi = 0; } } void main() { TMOD = 0x11; TH0 = (65536-46080)/256; TL0 = (65536-46080)%256; TH1 = (65536-46080)/256; TL1 = (65536-46080)%256; EA = 1; ET0 = 1; ET1 = 1; TR0 = 1; TR1 = 1; num_shi = 12; while(1) { shiwan = num_shi/10; wan = num_shi%10; qian = num_fen/10; bai = num_fen%10; shi = num_miao/10; ge = num_miao%10; display(shiwan,wan,qian,bai,shi,ge); } } void T0_Handle() interrupt 1 { EA = 0; TR0 = 0; fixtime = TL0+0x0A; TL0 = fixtime; TH0 = 0x4C+(char)CY; EA = 1; TR0 = 1; count_fen0++; count_fen1++; count_shi++; count_miao++; T0_Handle_Program(); } void T1_Handle() interrupt 3 { EA = 0; TR1 = 0; fixtime = TL1+0x0A; TL1 = fixtime; TH1 = 0x4C+(char)CY; EA = 1; TR1 = 1; key_handle(); }
最终效果
误差:
可以看到在调试中,1分钟内的误差为0,理论误差满足设计要求。
效果: