本文用于阐述使用51单片机制作万年历过程中的定时部分,软件特性可以在proteus上仿真,使用51单片机定时器T0做万年历计时信号源。采用12MHz晶振,中断1ms计时方式,误差低。下面直接上代码:
电子万年历计时结构体定义用于存储万年历计时时间,包含年份、月份、天数、时分秒、毫秒等等。
typedef struct { // 电子万年历变量
u16 year;
u8 month;
u8 day;
u8 hour;
u8 miunte;
u8 second;
u16 ms;
} time_t;
万年历变量初始化
time_t time = { 2020, 2, 2, 20, 2, 2, 0 }; // 电子万年历变量
u8 time_update = 1; // 更新显示等级,值越小等级越高,1为最高等级,0为最低
初始化定时器T0
初始化T0进行毫秒定时中断,并启动T0。
/*******************************************************************************
* 函 数 名 : timer_init
* 函数功能 : 初始化定时器
* 输 入 : void
* 输 出 : void
* 说 名 : none
*******************************************************************************/
void timer_init(void) // 初始化定时器
{
time.year = 2020; // 电子万年历变量
time.month = 2;
time.day = 2;
time.hour = 20;
time.miunte = 2;
time.second = 2;
time.ms = 0;
time_update = 1; // 更新显示等级,值越小等级越高,1为最高等级,0为最低
TMOD |= 0x01; // 定时器T0工作与方式1
IE |= 0x82;
TL0 = 0x18; // 计时1ms
TH0 = 0xfc;
TR0 = 1; // 启动定时器
}
中断计时程序
年月日,时分秒定时计数,电子万年历的核心代码,计时过程中中引入闰年判断,timer_set_update_level()函数用于统筹当前万年历计时更新的最高位,在lcd1602上显示使用此机制可避免无意义的显示刷新,能有效提高代码执行效率。
/*******************************************************************************
* 函 数 名 : t0_timer
* 函数功能 : T0中断服务
* 输 入 : void
* 输 出 : void
* 说 名 : none
*******************************************************************************/
void t0_timer(void) interrupt 1 // T0中断服务
{
TL0 = 0x18; // 重装初值
TH0 = 0xfc;
time.ms++;
timer_set_update_level(7); // 豪秒更新
if (time.ms < 1000) { // 1秒
return;
}
time.ms = 0; // ms清零
time.second++;
timer_set_update_level(6); // 秒更新
if (time.second < 60) { // 1分
return;
}
time.second = 0; // 秒清零
time.miunte++;
timer_set_update_level(5); // 分更新
if (time.miunte < 60) { // 1时
return;
}
time.miunte = 0; // 分清零
time.hour++;
timer_set_update_level(4); // 时更新
if (time.hour < 24) { // 1天
return;
}
time.hour = 0; // 时清零
time.day++;
timer_set_update_level(3); // 天更新
if ((time.month == 2 && ((time.day == 30) ||
(!(!(time.year % 4) && ((time.year % 100) || !(time.year % 400))) &&
(time.day == 29)))) ||
((time.day == 32) && ((time.month == 1) ||
(time.month == 3) ||
(time.month == 5) ||
(time.month == 7) ||
(time.month == 8) ||
(time.month == 10) ||
(time.month == 12))) ||
((time.day == 31) && ((time.month == 4) ||
(time.month == 6) ||
(time.month == 9) ||
(time.month == 11)))) {
time.day = 1; // 返回1日
time.month++; // 1月
timer_set_update_level(2); // 月更新
} else {
return;
}
if (time.month YEAR_MAX) { // 最大年份
time.year = YEAR_MAX;
}
}
定时器测试代码
用于测试万年历计时的准确性以及可靠性。
/*******************************************************************************
* 函 数 名 : test_timer
* 函数功能 : timer测试代码
* 输 入 : void
* 输 出 : void
* 说 名 : none
*******************************************************************************/
void test_timer(void) // timer测试代码
{
lcd1602_init();
timer_init();
while (1) {
switch (time_update) { // 判断更新
case 1: time_update = 0;
lcd1602_set_pos(0, 0);
lcd1602_write_num(4, time.year);
lcd1602_write_data('-'); // 年更新
case 2: time_update = 0;
lcd1602_set_pos(5, 0);
lcd1602_write_num(2, time.month);
lcd1602_write_data('-'); // 月更新
case 3: time_update = 0;
lcd1602_set_pos(8, 0);
lcd1602_write_num(2, time.day); // 日更新
case 4: time_update = 0;
lcd1602_set_pos(1, 1);
lcd1602_write_num(2, time.hour);
lcd1602_write_data(':'); // 时更新
case 5: time_update = 0;
lcd1602_set_pos(4, 1);
lcd1602_write_num(2, time.miunte);
lcd1602_write_data(':'); // 分更新
case 6: time_update = 0;
lcd1602_set_pos(7, 1);
lcd1602_write_num(2, time.second);
break; // 秒更新
default: break; // 无更新
}
}
}
附录 – 万年历定时代码(timer.h)
#ifndef __TIMER_H__
#define __TIMER_H__
#include "include.h"
#define YEAR_MAX 9999 // 最大年份数
typedef struct { // 电子万年历变量
u16 year;
u8 month;
u8 day;
u8 hour;
u8 miunte;
u8 second;
u16 ms;
} time_t;
void timer_init(void); // 初始化定时器
void timer_deinit(void); // 定时器去初始化
void timer_set_update_level(u8 level); // 刷新时间更新等级
void test_timer(void); // timer测试代码
#endif
附录 – 万年历定时代码(timer.c)
#include
#include "timer.h"
#include "lcd1602.h"
time_t time = { 2020, 2, 2, 20, 2, 2, 0 }; // 电子万年历变量
u8 time_update = 1; // 更新显示等级,值越小等级越高,1为最高等级,0为最低
/*******************************************************************************
* 函 数 名 : timer_init
* 函数功能 : 初始化定时器
* 输 入 : void
* 输 出 : void
* 说 名 : none
*******************************************************************************/
void timer_init(void) // 初始化定时器
{
time.year = 2020; // 电子万年历变量
time.month = 2;
time.day = 2;
time.hour = 20;
time.miunte = 2;
time.second = 2;
time.ms = 0;
time_update = 1; // 更新显示等级,值越小等级越高,1为最高等级,0为最低
TMOD |= 0x01; // 定时器T0工作与方式1
IE |= 0x82;
TL0 = 0x18; // 计时1ms
TH0 = 0xfc;
TR0 = 1; // 启动定时器
}
/*******************************************************************************
* 函 数 名 : timer_deinit
* 函数功能 : 定时器去初始化
* 输 入 : void
* 输 出 : void
* 说 名 : none
*******************************************************************************/
void timer_deinit(void) // 定时器去初始化
{
TR0 = 0; // 关闭定时器
IE &= 0x9d; // 禁止T0中断
TMOD &= 0xfe;
TL0 = 0x18; // 计时1ms
TH0 = 0xfc;
time.year = 0; // 电子万年历变量
time.month = 0;
time.day = 0;
time.hour = 0;
time.miunte = 0;
time.second = 0;
time.ms = 0;
time_update = 0; // 更新显示等级,值越小等级越高,1为最高等级,0为最低
}
/*******************************************************************************
* 函 数 名 : timer_set_update_level
* 函数功能 : 刷新时间更新等级
* 输 入 : level:更新等级
* 输 出 : void
* 说 名 : none
*******************************************************************************/
void timer_set_update_level(u8 level) // 刷新时间更新等级
{
if (time_update != 0) {
if (level < time_update) {
time_update = level;
}
} else {
time_update = level;
}
}
/*******************************************************************************
* 函 数 名 : t0_timer
* 函数功能 : T0中断服务
* 输 入 : void
* 输 出 : void
* 说 名 : none
*******************************************************************************/
void t0_timer(void) interrupt 1 // T0中断服务
{
TL0 = 0x18; // 重装初值
TH0 = 0xfc;
time.ms++;
timer_set_update_level(7); // 豪秒更新
if (time.ms < 1000) { // 1秒
return;
}
time.ms = 0; // ms清零
time.second++;
timer_set_update_level(6); // 秒更新
if (time.second < 60) { // 1分
return;
}
time.second = 0; // 秒清零
time.miunte++;
timer_set_update_level(5); // 分更新
if (time.miunte < 60) { // 1时
return;
}
time.miunte = 0; // 分清零
time.hour++;
timer_set_update_level(4); // 时更新
if (time.hour < 24) { // 1天
return;
}
time.hour = 0; // 时清零
time.day++;
timer_set_update_level(3); // 天更新
if ((time.month == 2 && ((time.day == 30) ||
(!(!(time.year % 4) && ((time.year % 100) || !(time.year % 400))) &&
(time.day == 29)))) ||
((time.day == 32) && ((time.month == 1) ||
(time.month == 3) ||
(time.month == 5) ||
(time.month == 7) ||
(time.month == 8) ||
(time.month == 10) ||
(time.month == 12))) ||
((time.day == 31) && ((time.month == 4) ||
(time.month == 6) ||
(time.month == 9) ||
(time.month == 11)))) {
time.day = 1; // 返回1日
time.month++; // 1月
timer_set_update_level(2); // 月更新
} else {
return;
}
if (time.month YEAR_MAX) { // 最大年份
time.year = YEAR_MAX;
}
}
/*******************************************************************************
* 函 数 名 : test_timer
* 函数功能 : timer测试代码
* 输 入 : void
* 输 出 : void
* 说 名 : none
*******************************************************************************/
void test_timer(void) // timer测试代码
{
lcd1602_init();
timer_init();
while (1) {
switch (time_update) { // 判断更新
case 1: time_update = 0;
lcd1602_set_pos(0, 0);
lcd1602_write_num(4, time.year);
lcd1602_write_data('-'); // 年更新
case 2: time_update = 0;
lcd1602_set_pos(5, 0);
lcd1602_write_num(2, time.month);
lcd1602_write_data('-'); // 月更新
case 3: time_update = 0;
lcd1602_set_pos(8, 0);
lcd1602_write_num(2, time.day); // 日更新
case 4: time_update = 0;
lcd1602_set_pos(1, 1);
lcd1602_write_num(2, time.hour);
lcd1602_write_data(':'); // 时更新
case 5: time_update = 0;
lcd1602_set_pos(4, 1);
lcd1602_write_num(2, time.miunte);
lcd1602_write_data(':'); // 分更新
case 6: time_update = 0;
lcd1602_set_pos(7, 1);
lcd1602_write_num(2, time.second);
break; // 秒更新
default: break; // 无更新
}
}
}
附录 – 通用数据类型定义
typedef char s8;
typedef unsigned char u8;
typedef short s16;
typedef unsigned short u16;
typedef long s32;
typedef unsigned long u32;
typedef char* string;