如何在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; // 无更新
}
}
版权声明:本文为weixin_44413515原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。