单片机定时器计数器四种工作方式(单片机应用实践篇之定时)

上节分享了单片机定时/计数器实现数码管动态刷新的程序代码,本节利用定时/计数器以及外部中断实现一简单的电子时钟功能。对应的视频课程为:单片机应用实践篇之定时/计数器实现按键可调电子时钟,视频链接:https://www.ixigua.com/6840071457402257928。此处把视频课程中的程序代码重新整理了下,按照模块化进行了简单的处理,分为main.c,display.c,display.h,time.c,time.h,exit.c,exit.h。此处的按键可调电子时钟功能比较简单,主要是为了给初学单片机的同学介绍一个综合的应用实例,同时,也考虑下状态机的编程思路,及程序的框架处理。程序当然有许多需要改进和优化的地方,此处只是采用了4位数码管进行程序的功能演示,大家如果做实物的话,可以考虑采用时钟专用数码管,另外程序为了演示效果,当前界面是显示的分钟:秒,小时并没有显示,大家可以根据自己的需求,进行相应的界面设计及修改。如果在此代码的基础上,适当的增加些小算法代码就可以实现一个万年历的功能,如果有网友有想要实现万年历的功能代码的话,可以留言,我后面可以进行分享以前调试的一个代码。话不多说,代码撸起来。

//main.c /*********************************************************** 程序功能:按键可调电子时钟的设计 四位数码管显示 分钟:秒(可根据需要调整显示界面) KEY1 外部中断0 状态切换 KEY2 外部中断1 参数调整 产生时间基准 定时器0 数码管动态刷新 定时器1 *************************************************************/ #include <reg51.h> #include "display.h" #include "time.h" #include "exit.h" void showNormal(); void showSec(); void showMin(); void showHour(); //状态变量 unsigned char state = 0; void main() { EX0_Init(); EX1_Init(); Timer1Init(); Timer0Init(); EA = 1; while(1) { switch(state) { case 0:TR0 = 1;showNormal();break;//正常时钟状态 case 1:showSec();break;//调秒 case 2:showMin();break;//调分 case 3:showHour();break;//调时 default:break; } } } //正常时钟显示界面 void showNormal() { LEDBuf[0] = min/10; LEDBuf[1] = min; LEDBuf[2] = sec/10; LEDBuf[3] = sec; } //调秒显示界面 void showSec() { LEDBuf[0] = 23;//关 LEDBuf[1] = 23;//关 LEDBuf[2] = sec/10; LEDBuf[3] = sec; } //调分显示界面 void showMin() { LEDBuf[0] = min/10; LEDBuf[1] = min; LEDBuf[2] = 23;//关 LEDBuf[3] = 23;//关 } //调时显示界面 void showHour() { LEDBuf[0] = hour/10; LEDBuf[1] = hour; LEDBuf[2] = 23;//关 LEDBuf[3] = 23;//关 }

//time.c #include "time.h" #include "display.h" //全局变量定义 unsigned char timerCnt; unsigned char sec; unsigned char min; unsigned char hour; void Timer0Init(void) //50毫秒@12.000MHz { TMOD &= 0xF0; //设置定时器模式 TMOD |= 0x01; //设置定时器模式 TL0 = 0xB0; //设置定时初值 TH0 = 0x3C; //设置定时初值 TF0 = 0; //清除TF0标志 ET0 = 1; } void Timer1Init(void) //1毫秒@12.000MHz { TMOD &= 0x0F; //设置定时器模式 TMOD |= 0x10; //设置定时器模式 TL1 = 0x18; //设置定时初值 TH1 = 0xFC; //设置定时初值 TF1 = 0; //清除TF1标志 ET1 = 1; TR1 = 1; //定时器1开始计时 } /************************************* 1、中断服务函数一定是一个没有返回值的函数 2、中断服务函数一定是没有参数的函数 3、中断服务函数函数名后跟关键字 interrupt 4、interrupt n 0 - 4 5个中断源 8*n 0003H 0003H INT0 000BH T0 0013H INT1 001BH T1 0023H ES 5、中断服务函数不能被主程序或其他程序所调用 6、 n 后面 跟 using m (0 - 3 )工作寄存器组 ***************************************/ void timer0_ISR(void) interrupt 1 { timerCnt ; if(timerCnt >= 20)//20次50ms中断,即1S时间到 { timerCnt = 0; sec ; if(sec >= 60) { sec = 0; min ; if(min >= 60) { min = 0; hour ; if(hour >= 24) { hour = 0; } } } } TL0 = 0xB0; //设置定时初值 TH0 = 0x3C; //设置定时初值 } void timer1_ISR(void) interrupt 3 { TR1 = 0; Display(); TL1 = 0x18; //设置定时初值 TH1 = 0xFC; //设置定时初值 TR1 = 1; }

//time.h #ifndef __TIME_H__ #define __TIME_H__ #include <reg51.h> extern unsigned char sec; extern unsigned char min; extern unsigned char hour; void Timer0Init(void); void Timer1Init(void); #endif

//exti.c #include "exit.h" #include "time.h" //外部变量声明 extern unsigned char state; void EX0_Init() { IT0 = 1; EX0 = 1; } void EX1_Init() { IT1 = 1; EX1 = 1; } void EX0_ISR() interrupt 0 { TR0 = 0; state ; if(state >3 ) state = 0; } void EX1_ISR() interrupt 2 { switch(state) { case 0:break; case 1:sec ;if(sec >= 60) sec = 0;break; case 2:min ;if(min >= 60) min = 0;break; case 3:hour ;if(hour >= 24) hour = 0;break; default:break; } }

//exti.h #ifndef __EXIT_H__ #define __EXIT_H__ #include <reg51.h> void EX0_Init(); void EX1_Init(); #endif

//display.c #include "display.h" unsigned char code leddata[]={ //数码管的段码表 0x3F, //"0" 0x06, //"1" 0x5B, //"2" 0x4F, //"3" 0x66, //"4" 0x6D, //"5" 0x7D, //"6" 0x07, //"7" 0x7F, //"8" 0x6F, //"9" 0x77, //"A" 0x7C, //"B" 0x39, //"C" 0x5E, //"D" 0x79, //"E" 0x71, //"F" 0x76, //"H" 0x38, //"L" 0x37, //"n" 0x3E, //"u" 0x73, //"P" 0x5C, //"o" 0x40, //"-" 0x00, //熄灭 }; unsigned char LEDBuf[] = {22,22,22,22};//数据显示缓冲区 unsigned char code PLACE_CODE[] = {0xfe,0xfd,0xfb,0xf7};//位选信号 void Display() { static unsigned char i = 0; //1、送段码 //2、送位选 //3、延时 //4、消隐 switch(i) { case 0: GPIO_DIG = 0x00; GPIO_DIG = leddata[LEDBuf[0]]; GPIO_PLACE = PLACE_CODE[0]; i ; break; case 1: GPIO_DIG = 0x00; GPIO_DIG = leddata[LEDBuf[1]]; GPIO_PLACE = PLACE_CODE[1]; i ; break; case 2: GPIO_DIG = 0x00; GPIO_DIG = leddata[LEDBuf[2]]; GPIO_PLACE = PLACE_CODE[2]; i ; break; case 3: GPIO_DIG = 0x00; GPIO_DIG = leddata[LEDBuf[3]]; GPIO_PLACE = PLACE_CODE[3]; i = 0; break; default:break; } }

//display.h #ifndef __DISPLAY_H__ #define __DISPLAY_H__ #include <reg51.h> //IO的声明 #define GPIO_DIG P0 //段码 IO #define GPIO_PLACE P2 //位选 IO #define N 4 //数码管个数 unsigned char code leddata[]; extern unsigned char LEDBuf[]; void Display(); #endif

执行代码后,通过Proteus仿真的显示界面如下:

单片机定时器计数器四种工作方式(单片机应用实践篇之定时)(1)

正常时钟显示界面(分钟:秒)

单片机定时器计数器四种工作方式(单片机应用实践篇之定时)(2)

调秒显示界面

单片机定时器计数器四种工作方式(单片机应用实践篇之定时)(3)

调分显示界面

单片机定时器计数器四种工作方式(单片机应用实践篇之定时)(4)

调时显示界面

程序代码仅供初学的同学参考学习,代码可以根据自己的风格进行相应的优化处理,仅供参考。

,

免责声明:本文仅代表文章作者的个人观点,与本站无关。其原创性、真实性以及文中陈述文字和内容未经本站证实,对本文以及其中全部或者部分内容文字的真实性、完整性和原创性本站不作任何保证或承诺,请读者仅作参考,并自行核实相关内容。文章投诉邮箱:anhduc.ph@yahoo.com

    分享
    投诉
    首页