如何在51单片机实现电子万年历
51单片机制作万年历的基本功能部分,软件特性可以在proteus上仿真,使用51单片机定时器T0做万年历计时信号源,电子万年历计时系统在timer模块,本模块主要是将年/月/日/星期/时/分/秒等信息输出到lcd1602上显示,以及将年月日换算为星期信息。单片机采用12MHz晶振,中断1ms计时方式,误差低。下面直接上代码:
引用timer系统变量
extern time_t time; // 电子万年历变量
extern u8 time_update; // 更新显示等级,值越小等级越高,1为最高等级,0为最低
对外接口
用于初始化/去初始化电子万年历系统,计算星期信息,以及在lcd1602上显示更新等。
void clock_init(void); // 万年历初始化
void clock_deinit(void); // 万年历去初始化
u8 clock_week_calc(u16 y, u8 m, u8 d); // 星期计算
void clock_display_update(void); // 显示更新
初始化模块
主要是预置年/月/日/星期/时/分/秒/毫秒信息显示格式和初始值。
/*******************************************************************************
* 函 数 名 : clock_init
* 函数功能 : 万年历初始化
* 输 入 : void
* 输 出 : void
* 说 名 : none
*******************************************************************************/
void clock_init(void) // 万年历初始化
{lcd1602_write_cmd(LCD1602_DISPLAY_CONTROL); // 关闭光标和闪烁lcd1602_clean();lcd1602_set_pos(0, 1);lcd1602_write_num(4, time.year);lcd1602_write_data('-');lcd1602_write_num(2, time.month);lcd1602_write_data('-');lcd1602_write_num(2, time.day);lcd1602_set_pos(0, 0);lcd1602_write_num(2, time.hour);lcd1602_write_data(':');lcd1602_write_num(2, time.miunte);lcd1602_write_data(':');lcd1602_write_num(2, time.second);lcd1602_set_pos(11, 0);lcd1602_write_num(3, time.ms);lcd1602_write_str("ms");
}
lcd1602显示实例:
去初始化
清理显示屏。
/*******************************************************************************
* 函 数 名 : clock_deinit
* 函数功能 : 万年历去初始化
* 输 入 : void
* 输 出 : void
* 说 名 : none
*******************************************************************************/
void clock_deinit(void) // 万年历去初始化
{lcd1602_write_cmd(LCD1602_DISPLAY_CONTROL); // 关闭光标和闪烁lcd1602_clean();
}
星期计算
通过给定年/月/日,通过星期计算公式,完成年月日到星期的换算,计算公式可通过星期的产生和定义得出。返回0表示周天,返回1到6表示周一到周六。
code s8 week[7][4]={{"Sun"}, {"Mon"}, {"Tue"}, {"Wed"}, {"Thu"}, {"Fri"}, {"Sat"}};
/*******************************************************************************
* 函 数 名 : clock_week_calc
* 函数功能 : 星期计算
* 输 入 : y/m/d:年/月/日
* 输 出 : u8:星期数
* 说 名 : none
*******************************************************************************/
u8 clock_week_calc(u16 y, u8 m, u8 d) // 星期计算
{u16 week_cnt = 0;u8 week_num;switch (m) { // 从这一年的元旦算到该天为止( 含该天) 的天数case 12: week_cnt += 30;case 11: week_cnt += 31;case 10: week_cnt += 30;case 9: week_cnt += 31;case 8: week_cnt += 31;case 7: week_cnt += 30;case 6: week_cnt += 31;case 5: week_cnt += 30;case 4: week_cnt += 31;case 3: week_cnt += 28;case 2: week_cnt += 31;case 1: week_cnt += d; break;default: break;}if ((!(y % 4) && ((y % 100) || !(y % 400))) && (m > 2)) { // 闰年补一天week_cnt++;}week_num = (y - 1 + (y - 1) / 4 - (y - 1) / 100 + (y - 1) / 400 + week_cnt) % 7;return week_num;
}
显示更新
由于lcd1602写入信息耗时较高,另一方面也并不是时时刻刻年/月/日/星期/时/分/秒/毫秒等信息都在更新或者说有变化,因此利用time_update记录当前需要更新的时间信息有哪些,可以有效提高cpu利用率和性能,在51单片机场景优势非常明显。注:年/月/日/星期/时/分/秒/毫秒更新方向是线性向下的,即:更新年份,必定也需要更新月/日/星期/时/分/秒/毫秒等其他信息。
/*******************************************************************************
* 函 数 名 : clock_display_update
* 函数功能 : 显示更新
* 输 入 : void
* 输 出 : void
* 说 名 : time_update需要及时清除
*******************************************************************************/
void clock_display_update(void) // 显示更新
{switch (time_update) { // 判断更新case 1: time_update = 0; // 年更新lcd1602_set_pos(0, 1);lcd1602_write_num(4, time.year);case 2: time_update = 0; // 月更新lcd1602_set_pos(5, 1);lcd1602_write_num(2, time.month);case 3: time_update = 0; // 日更新lcd1602_set_pos(8, 1);lcd1602_write_num(2, time.day);lcd1602_set_pos(11, 1); // 星期更新lcd1602_write_str(week[clock_week_calc(time.year, time.month, time.day)]);case 4: time_update = 0; // 时更新lcd1602_set_pos(0, 0);lcd1602_write_num(2, time.hour);case 5: time_update = 0; // 分更新lcd1602_set_pos(3, 0);lcd1602_write_num(2, time.miunte);case 6: time_update = 0; // 秒更新lcd1602_set_pos(6, 0);lcd1602_write_num(2, time.second);case 7: time_update = 0; // 毫秒更新lcd1602_set_pos(11, 0);lcd1602_write_num(3, time.ms);break;default: break; // 无更新}
}
附录 - 基本功能(clock.h)
#ifndef __CLOCK_H__
#define __CLOCK_H__#include "include.h"
#include "timer.h"extern time_t time; // 电子万年历变量
extern u8 time_update; // 更新显示等级,值越小等级越高,1为最高等级,0为最低void clock_init(void); // 万年历初始化
void clock_deinit(void); // 万年历去初始化
u8 clock_week_calc(u16 y, u8 m, u8 d); // 星期计算
void clock_display_update(void); // 显示更新#endif
附录 - 基本功能(clock.c)
#include "clock.h"
#include "lcd1602.h"code s8 week[7][4]={{"Sun"}, {"Mon"}, {"Tue"}, {"Wed"}, {"Thu"}, {"Fri"}, {"Sat"}};/*******************************************************************************
* 函 数 名 : clock_init
* 函数功能 : 万年历初始化
* 输 入 : void
* 输 出 : void
* 说 名 : none
*******************************************************************************/
void clock_init(void) // 万年历初始化
{lcd1602_write_cmd(LCD1602_DISPLAY_CONTROL); // 关闭光标和闪烁lcd1602_clean();lcd1602_set_pos(0, 1);lcd1602_write_num(4, time.year);lcd1602_write_data('-');lcd1602_write_num(2, time.month);lcd1602_write_data('-');lcd1602_write_num(2, time.day);lcd1602_set_pos(0, 0);lcd1602_write_num(2, time.hour);lcd1602_write_data(':');lcd1602_write_num(2, time.miunte);lcd1602_write_data(':');lcd1602_write_num(2, time.second);lcd1602_set_pos(11, 0);lcd1602_write_num(3, time.ms);lcd1602_write_str("ms");
}/*******************************************************************************
* 函 数 名 : clock_deinit
* 函数功能 : 万年历去初始化
* 输 入 : void
* 输 出 : void
* 说 名 : none
*******************************************************************************/
void clock_deinit(void) // 万年历去初始化
{lcd1602_write_cmd(LCD1602_DISPLAY_CONTROL); // 关闭光标和闪烁lcd1602_clean();
}/*******************************************************************************
* 函 数 名 : clock_week_calc
* 函数功能 : 星期计算
* 输 入 : y/m/d:年/月/日
* 输 出 : u8:星期数
* 说 名 : none
*******************************************************************************/
u8 clock_week_calc(u16 y, u8 m, u8 d) // 星期计算
{u16 week_cnt = 0;u8 week_num;switch (m) { // 从这一年的元旦算到该天为止( 含该天) 的天数case 12: week_cnt += 30;case 11: week_cnt += 31;case 10: week_cnt += 30;case 9: week_cnt += 31;case 8: week_cnt += 31;case 7: week_cnt += 30;case 6: week_cnt += 31;case 5: week_cnt += 30;case 4: week_cnt += 31;case 3: week_cnt += 28;case 2: week_cnt += 31;case 1: week_cnt += d; break;default: break;}if ((!(y % 4) && ((y % 100) || !(y % 400))) && (m > 2)) { // 闰年补一天week_cnt++;}week_num = (y - 1 + (y - 1) / 4 - (y - 1) / 100 + (y - 1) / 400 + week_cnt) % 7;return week_num;
}/*******************************************************************************
* 函 数 名 : clock_display_update
* 函数功能 : 显示更新
* 输 入 : void
* 输 出 : void
* 说 名 : time_update需要及时清除
*******************************************************************************/
void clock_display_update(void) // 显示更新
{switch (time_update) { // 判断更新case 1: time_update = 0; // 年更新lcd1602_set_pos(0, 1);lcd1602_write_num(4, time.year);case 2: time_update = 0; // 月更新lcd1602_set_pos(5, 1);lcd1602_write_num(2, time.month);case 3: time_update = 0; // 日更新lcd1602_set_pos(8, 1);lcd1602_write_num(2, time.day);lcd1602_set_pos(11, 1); // 星期更新lcd1602_write_str(week[clock_week_calc(time.year, time.month, time.day)]);case 4: time_update = 0; // 时更新lcd1602_set_pos(0, 0);lcd1602_write_num(2, time.hour);case 5: time_update = 0; // 分更新lcd1602_set_pos(3, 0);lcd1602_write_num(2, time.miunte);case 6: time_update = 0; // 秒更新lcd1602_set_pos(6, 0);lcd1602_write_num(2, time.second);case 7: time_update = 0; // 毫秒更新lcd1602_set_pos(11, 0);lcd1602_write_num(3, time.ms);break;default: break; // 无更新}
}