如何在51单片机实现电子万年历

  • Post author:
  • Post category:其他




如何在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 版权协议,转载请附上原文出处链接和本声明。