单片机at89c51

用at89c51单片机实现“万年钟”
1)基本功能
使用字符型 LCD 显示器显示当前时间。
显示格式为“年-月-日”
时时:分分:秒秒
用 4 个功能键操作来设置当前时间, 4 个功能键接在 P1.0~P1.3 引脚上
2)扩展功能
显示星期
备注:
功能键 1-K4 功能如下。
K1-进入设置年,月,日,星期,小时,分钟,秒。
K2-设置所选位的加 1
K3-设置所选位减1
K4-确认完成设置。


#include <reg51.h>

// 定义 LCD1602 的引脚连接
#define LCD_RS P0_0
#define LCD_RW P0_1
#define LCD_EN P0_2
#define LCD_DATA P2

// 定义功能键连接的引脚
#define FUNC_KEY_1 P1_0
#define FUNC_KEY_2 P1_1
#define FUNC_KEY_3 P1_2
#define FUNC_KEY_4 P1_3

// 定义初始时间
unsigned char year = 20;   // 年份,例如 2020 年
unsigned char month = 6;   // 月份
unsigned char day = 5;     // 日期
unsigned char hour = 12;   // 小时
unsigned char minute = 0;  // 分钟
unsigned char second = 0;  // 秒钟

// 定义星期数组
unsigned char weekday[7][4] = {"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"};
unsigned char currentWeekday = 0;  // 当前星期几的索引

// 延时函数
void delay(unsigned int ms) {
    unsigned int i, j;
    for (i = 0; i < ms; i++)
        for (j = 0; j < 125; j++);
}

// 向 LCD 发送命令
void lcdCommand(unsigned char cmd) {
    LCD_RS = 0;
    LCD_RW = 0;
    LCD_DATA = cmd;
    LCD_EN = 1;
    delay(5);
    LCD_EN = 0;
}

// 向 LCD 发送数据
void lcdData(unsigned char dat) {
    LCD_RS = 1;
    LCD_RW = 0;
    LCD_DATA = dat;
    LCD_EN = 1;
    delay(5);
    LCD_EN = 0;
}

// 初始化 LCD
void lcdInit() {
    lcdCommand(0x38);  // 设置显示模式为 8 位数据接口
    lcdCommand(0x0C);  // 显示开,光标关闭
    lcdCommand(0x06);  // 光标移动设置:右移
    lcdCommand(0x01);  // 清屏
    delay(10);
}

// 在 LCD 上显示时间和日期
void lcdDisplayDateTime() {
    lcdCommand(0x80);  // 设置光标位置为第一行第一列
    lcdData('2');      // 显示 "20",表示年份
    lcdData('0');
    lcdData((year / 10) + '0');
    lcdData((year % 10) + '0');
    lcdData('-');
    lcdData((month / 10) + '0');
    lcdData((month % 10) + '0');
    lcdData('-');
    lcdData((day / 10) + '0');
    lcdData((day % 10) + '0');

    lcdCommand(0xC0);  // 设置光标位置为第二行第一列
    lcdData((hour / 10) + '0');
    lcdData((hour % 10) + '0');
    lcdData(':');
    lcdData((minute / 10) + '0');
    lcdData((minute % 10) + '0');
    lcdData(':');
    lcdData((second / 10) + '0');
    lcdData((second % 10) + '0');

    lcdData(' ');
    lcdData(' ');
    lcdData(' ');
    lcdData(weekday[currentWeekday][0]);
    lcdData(weekday[currentWeekday][1]);
    lcdData(weekday[currentWeekday][2]);
}

// 增加当前时间
void incrementTime() {
    second++;
    if (second >= 60) {
        second = 0;
        minute++;
        if (minute >= 60) {
            minute = 0;
            hour++;
            if (hour >= 24) {
                hour = 0;
                day++;
                currentWeekday++;
                if (currentWeekday >= 7) {
                    currentWeekday = 0;
                    month++;
                    if (month >= 13) {
                        month = 1;
                        year++;
                    }
                }
            }
        }
    }
}

// 减少当前时间
void decrementTime() {
    if (second > 0) {
        second--;
    } else {
        if (minute > 0) {
            minute--;
            second = 59;
        } else {
            if (hour > 0) {
                hour--;
                minute = 59;
                second = 59;
            } else {
                if (day > 1) {
                    day--;
                    currentWeekday--;
                    if (currentWeekday < 0) {
                        currentWeekday = 6;
                        month--;
                        if (month < 1) {
                            month = 12;
                            year--;
                        }
                    }
                }
            }
        }
    }
}

// 主函数
void main() {
    lcdInit();  // 初始化 LCD

    while (1) {
        lcdDisplayDateTime();  // 显示当前时间和日期

        if (FUNC_KEY_1 == 0) {
            // 进入设置模式
            while (FUNC_KEY_1 == 0) {
                // 等待 FUNC_KEY_1 松开
            }

            // 选择要设置的位
            unsigned char settingIndex = 0;  // 0 表示年份,1 表示月份,以此类推
            while (1) {
                lcdCommand(0x8F);  // 设置光标位置为第二行最后一列
                lcdData('^');

                if (FUNC_KEY_4 == 0) {
                    // 确认设置完成,退出设置模式
                    while (FUNC_KEY_4 == 0) {
                        // 等待 FUNC_KEY_4 松开
                    }
                    break;
                }

                if (FUNC_KEY_2 == 0) {
                    // 增加当前位的值
                    while (FUNC_KEY_2 == 0) {
                        // 等待 FUNC_KEY_2 松开
                    }
                    switch (settingIndex) {
                        case 0:  // 年份
                            year++;
                            if (year >= 100) {
                                year = 0;
                            }
                            break;
                        case 1:  // 月份
                            month++;
                            if (month >= 13) {
                                month = 1;
                            }
                            break;
                        case 2:  // 日期
                            day++;
                            if (day >= 32) {
                                day = 1;
                            }
                            break;
                        case 3:  // 星期
                            currentWeekday++;
                            if (currentWeekday >= 7) {
                                currentWeekday = 0;
                            }
                            break;
                        case 4:  // 小时
                            hour++;
                            if (hour >= 24) {
                                hour = 0;
                            }
                            break;
                        case 5:  // 分钟
                            minute++;
                            if (minute >= 60) {
                                minute = 0;
                            }
                            break;
                        case 6:  // 秒钟
                            second++;
                            if (second >= 60) {
                                second = 0;
                            }
                            break;
                    }
                }

                if (FUNC_KEY_3 == 0) {
                    // 减少当前位的值
                    while (FUNC_KEY_3 == 0) {
                        // 等待 FUNC_KEY_3 松开
                    }
                    switch (settingIndex) {
                        case 0:  // 年份
                            year--;
                            if (year >= 100) {
                                year = 99;
                            }
                            break;
                        case 1:  // 月份
                            month--;
                            if (month <= 0) {
                                month = 12;
                            }
                            break;
                        case 2:  // 日期
                            day--;
                            if (day <= 0) {
                                day = 31;
                            }
                            break;
                        case 3:  // 星期
                            currentWeekday--;
                            if (currentWeekday < 0) {
                                currentWeekday = 6;
                            }
                            break;
                        case 4:  // 小时
                            hour--;
                            if (hour >= 24) {
                                hour = 23;
                            }
                            break;
                        case 5:  // 分钟
                            minute--;
                            if (minute >= 60) {
                                minute = 59;
                            }
                            break;
                        case 6:  // 秒钟
                            second--;
                            if (second >= 60) {
                                second = 59;
                            }
                            break;
                    }
                }

                settingIndex++;
                if (settingIndex >= 7) {
                    settingIndex = 0;
                }

                delay(200);  // 延时一段时间,避免按键抖动
            }
        }

        incrementTime();  // 增加当前时间

        delay(1000);  // 延时 1 秒
    }
}