基于STM32的电子时钟设计(四按键控制)

基于STM32的电子时钟设计(四按键控制)

一、系统总体设计

1.1 功能需求

1.2 系统框图

┌─────────────────────────────────────────────────────────────┐
│                    STM32电子时钟系统                        │
├─────────────────────────────────────────────────────────────┤
│  STM32F103    │  输入模块    │  显示模块    │  时钟模块    │
│  (主控制器)   │             │             │             │
│               │  • 4个按键   │  • LCD1602   │  • DS1302    │
│  • 时钟逻辑   │  • 消抖处理  │  • LCD12864   │  • DS3231    │
│  • 闹钟判断   │  • 长按短按  │  • OLED      │  • RTC       │
│  • 显示刷新   │             │             │             │
└─────────────────────────────────────────────────────────────┘

二、硬件设计

2.1 硬件连接图

主要器件清单

器件 型号 数量 作用
主控芯片 STM32F103C8T6 1 核心控制
显示模块 LCD1602 1 时间日期显示
时钟芯片 DS1302 1 实时时钟
按键 轻触开关 4 用户输入
蜂鸣器 有源蜂鸣器 1 闹钟提示
晶振 32.768kHz 1 时钟基准

引脚连接表

/******************** 硬件连接定义 ********************/
// GPIO引脚定义
#define KEY_PORT        GPIOA
#define KEY_PIN_1       GPIO_Pin_0    // 按键1:模式切换
#define KEY_PIN_2       GPIO_Pin_1    // 按键2:加
#define KEY_PIN_3       GPIO_Pin_2    // 按键3:减  
#define KEY_PIN_4       GPIO_Pin_3    // 按键4:确认/取消

// LCD1602引脚
#define LCD_RS_PIN      GPIO_Pin_8
#define LCD_RW_PIN      GPIO_Pin_9
#define LCD_EN_PIN      GPIO_Pin_10
#define LCD_D4_PIN      GPIO_Pin_11
#define LCD_D5_PIN      GPIO_Pin_12
#define LCD_D6_PIN      GPIO_Pin_13
#define LCD_D7_PIN      GPIO_Pin_14

// DS1302引脚
#define DS1302_RST_PIN  GPIO_Pin_4
#define DS1302_IO_PIN   GPIO_Pin_5
#define DS1302_SCK_PIN  GPIO_Pin_6

// 蜂鸣器引脚
#define BUZZER_PIN      GPIO_Pin_7

三、软件设计

3.1 按键功能定义

/******************** 按键功能定义 ********************/
typedef enum {
    KEY_MODE = 0,      // 模式切换键
    KEY_UP,            // 增加键
    KEY_DOWN,          // 减少键
    KEY_OK             // 确认/取消键
} KeyType;

// 系统工作模式
typedef enum {
    MODE_NORMAL = 0,    // 正常显示模式
    MODE_SET_TIME,     // 设置时间模式
    MODE_SET_DATE,     // 设置日期模式
    MODE_SET_ALARM,    // 设置闹钟模式
    MODE_ALARM_ON      // 闹钟响铃模式
} WorkMode;

// 设置状态
typedef enum {
    SET_HOUR = 0,      // 设置小时
    SET_MINUTE,        // 设置分钟
    SET_SECOND,        // 设置秒
    SET_YEAR,          // 设置年
    SET_MONTH,         // 设置月
    SET_DAY,           // 设置日
    SET_ALARM_HOUR,    // 设置闹钟小时
    SET_ALARM_MINUTE   // 设置闹钟分钟
} SetState;

3.2 核心数据结构

/******************** 时间结构体 ********************/
typedef struct {
    uint8_t hour;      // 小时 0-23
    uint8_t minute;    // 分钟 0-59
    uint8_t second;    // 秒 0-59
    uint8_t year;      // 年 00-99
    uint8_t month;     // 月 1-12
    uint8_t day;       // 日 1-31
    uint8_t week;      // 星期 1-7
} TimeStruct;

typedef struct {
    uint8_t hour;      // 闹钟小时
    uint8_t minute;    // 闹钟分钟
    uint8_t enable;    // 闹钟使能
} AlarmStruct;

// 全局变量
TimeStruct currentTime;
AlarmStruct alarmTime;
WorkMode workMode = MODE_NORMAL;
SetState setState = SET_HOUR;
uint8_t blinkFlag = 0;      // 闪烁标志
uint8_t alarmRingFlag = 0;  // 闹钟响铃标志

3.3 按键扫描与消抖

/**
 * @file key.c
 * @brief 按键扫描与消抖处理
 */

#include "stm32f10x.h"
#include "key.h"

#define KEY_DELAY_MS    10      // 消抖延时
#define LONG_PRESS_MS   1000    // 长按判定时间

static uint8_t key_state[4] = {1, 1, 1, 1};  // 按键状态
static uint8_t key_press_time[4] = {0};      // 按下时间计数

/**
 * @brief 按键初始化
 */
void Key_Init(void) {
    GPIO_InitTypeDef GPIO_InitStructure;
    
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
    
    GPIO_InitStructure.GPIO_Pin = KEY_PIN_1 | KEY_PIN_2 | KEY_PIN_3 | KEY_PIN_4;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;  // 上拉输入
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_Init(KEY_PORT, &GPIO_InitStructure);
}

/**
 * @brief 按键扫描(单次触发)
 * @return 按下的键值,0表示无按键
 */
uint8_t Key_Scan_Single(void) {
    uint8_t key_value = 0;
    
    // 扫描4个按键
    if (GPIO_ReadInputDataBit(KEY_PORT, KEY_PIN_1) == 0) {
        Delay_ms(KEY_DELAY_MS);  // 消抖
        if (GPIO_ReadInputDataBit(KEY_PORT, KEY_PIN_1) == 0) {
            while (GPIO_ReadInputDataBit(KEY_PORT, KEY_PIN_1) == 0); // 等待松开
            key_value = KEY_MODE;
        }
    }
    else if (GPIO_ReadInputDataBit(KEY_PORT, KEY_PIN_2) == 0) {
        Delay_ms(KEY_DELAY_MS);
        if (GPIO_ReadInputDataBit(KEY_PORT, KEY_PIN_2) == 0) {
            while (GPIO_ReadInputDataBit(KEY_PORT, KEY_PIN_2) == 0);
            key_value = KEY_UP;
        }
    }
    else if (GPIO_ReadInputDataBit(KEY_PORT, KEY_PIN_3) == 0) {
        Delay_ms(KEY_DELAY_MS);
        if (GPIO_ReadInputDataBit(KEY_PORT, KEY_PIN_3) == 0) {
            while (GPIO_ReadInputDataBit(KEY_PORT, KEY_PIN_3) == 0);
            key_value = KEY_DOWN;
        }
    }
    else if (GPIO_ReadInputDataBit(KEY_PORT, KEY_PIN_4) == 0) {
        Delay_ms(KEY_DELAY_MS);
        if (GPIO_ReadInputDataBit(KEY_PORT, KEY_PIN_4) == 0) {
            while (GPIO_ReadInputDataBit(KEY_PORT, KEY_PIN_4) == 0);
            key_value = KEY_OK;
        }
    }
    
    return key_value;
}

/**
 * @brief 按键扫描(支持长按)
 * @return 按键状态和键值
 */
uint8_t Key_Scan_LongPress(uint8_t *press_type) {
    uint8_t key_value = 0;
    uint8_t i;
    static uint8_t last_key_state[4] = {1, 1, 1, 1};
    uint8_t current_key_state[4];
    uint8_t key_pins[4] = {KEY_PIN_1, KEY_PIN_2, KEY_PIN_3, KEY_PIN_4};
    
    *press_type = 0;  // 0:无按键, 1:短按, 2:长按
    
    for (i = 0; i < 4; i++) {
        current_key_state[i] = GPIO_ReadInputDataBit(KEY_PORT, key_pins[i]);
        
        if (last_key_state[i] == 1 && current_key_state[i] == 0) {
            // 按键刚被按下
            Delay_ms(KEY_DELAY_MS);  // 消抖
            key_press_time[i] = 0;
        }
        else if (last_key_state[i] == 0 && current_key_state[i] == 0) {
            // 按键持续按下
            key_press_time[i]++;
            if (key_press_time[i] >= (LONG_PRESS_MS / KEY_DELAY_MS)) {
                *press_type = 2;  // 长按
                key_value = i + 1;
                key_press_time[i] = 0;  // 重置计时
                break;
            }
        }
        else if (last_key_state[i] == 0 && current_key_state[i] == 1) {
            // 按键刚被释放
            if (key_press_time[i] < (LONG_PRESS_MS / KEY_DELAY_MS)) {
                *press_type = 1;  // 短按
                key_value = i + 1;
            }
            key_press_time[i] = 0;
        }
        
        last_key_state[i] = current_key_state[i];
    }
    
    return key_value;
}

3.4 时钟逻辑处理

/**
 * @file clock.c
 * @brief 电子时钟核心逻辑
 */

#include "clock.h"
#include "ds1302.h"
#include "lcd1602.h"

// 星期显示字符串
const char *week_str[] = {"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"};

/**
 * @brief 时间更新显示
 */
void Clock_UpdateDisplay(void) {
    char display_buf[16];
    
    switch (workMode) {
        case MODE_NORMAL:
            // 正常模式显示时间和日期
            sprintf(display_buf, "%02d:%02d:%02d", 
                    currentTime.hour, currentTime.minute, currentTime.second);
            LCD1602_ShowString(0, 0, display_buf);
            
            sprintf(display_buf, "%02d-%02d-%02d %s", 
                    currentTime.year, currentTime.month, currentTime.day,
                    week_str[currentTime.week - 1]);
            LCD1602_ShowString(0, 1, display_buf);
            break;
            
        case MODE_SET_TIME:
            // 设置时间模式,闪烁当前设置项
            if (blinkFlag) {
                switch (setState) {
                    case SET_HOUR:
                        sprintf(display_buf, "  :%02d:%02d", 
                                currentTime.minute, currentTime.second);
                        break;
                    case SET_MINUTE:
                        sprintf(display_buf, "%02d:  :%02d", 
                                currentTime.hour, currentTime.second);
                        break;
                    case SET_SECOND:
                        sprintf(display_buf, "%02d:%02d:  ", 
                                currentTime.hour, currentTime.minute);
                        break;
                }
            } else {
                sprintf(display_buf, "%02d:%02d:%02d", 
                        currentTime.hour, currentTime.minute, currentTime.second);
            }
            LCD1602_ShowString(0, 0, "Set Time:");
            LCD1602_ShowString(0, 1, display_buf);
            break;
            
        case MODE_SET_DATE:
            // 设置日期模式
            if (blinkFlag) {
                switch (setState) {
                    case SET_YEAR:
                        sprintf(display_buf, "  -%02d-%02d", 
                                currentTime.month, currentTime.day);
                        break;
                    case SET_MONTH:
                        sprintf(display_buf, "%02d-  -%02d", 
                                currentTime.year, currentTime.day);
                        break;
                    case SET_DAY:
                        sprintf(display_buf, "%02d-%02d-  ", 
                                currentTime.year, currentTime.month);
                        break;
                }
            } else {
                sprintf(display_buf, "%02d-%02d-%02d", 
                        currentTime.year, currentTime.month, currentTime.day);
            }
            LCD1602_ShowString(0, 0, "Set Date:");
            LCD1602_ShowString(0, 1, display_buf);
            break;
            
        case MODE_SET_ALARM:
            // 设置闹钟模式
            if (blinkFlag) {
                switch (setState) {
                    case SET_ALARM_HOUR:
                        sprintf(display_buf, "  :%02d", alarmTime.minute);
                        break;
                    case SET_ALARM_MINUTE:
                        sprintf(display_buf, "%02d:  ", alarmTime.hour);
                        break;
                }
            } else {
                sprintf(display_buf, "%02d:%02d", alarmTime.hour, alarmTime.minute);
            }
            LCD1602_ShowString(0, 0, "Set Alarm:");
            LCD1602_ShowString(0, 1, display_buf);
            break;
    }
}

/**
 * @brief 按键处理函数
 * @param key_value 键值
 * @param press_type 按键类型(1:短按, 2:长按)
 */
void Clock_KeyHandler(uint8_t key_value, uint8_t press_type) {
    switch (workMode) {
        case MODE_NORMAL:
            // 正常模式下的按键处理
            switch (key_value) {
                case KEY_MODE:  // 模式切换
                    if (press_type == 1) {  // 短按
                        workMode = MODE_SET_TIME;
                        setState = SET_HOUR;
                        LCD1602_Clear();
                    } else if (press_type == 2) {  // 长按
                        alarmTime.enable = !alarmTime.enable;  // 切换闹钟开关
                        LCD1602_ShowString(0, 0, "Alarm:");
                        LCD1602_ShowString(0, 1, alarmTime.enable ? "ON " : "OFF");
                        Delay_ms(1000);
                    }
                    break;
                    
                case KEY_UP:     // 显示日期/时间切换
                    // 可以在时间和日期显示间切换
                    break;
                    
                case KEY_DOWN:   // 亮度调节或其他功能
                    break;
                    
                case KEY_OK:     // 进入设置模式
                    workMode = MODE_SET_TIME;
                    setState = SET_HOUR;
                    LCD1602_Clear();
                    break;
            }
            break;
            
        case MODE_SET_TIME:
            // 设置时间模式
            switch (key_value) {
                case KEY_MODE:  // 切换到下一项
                    setState++;
                    if (setState > SET_SECOND) {
                        setState = SET_HOUR;
                    }
                    break;
                    
                case KEY_UP:     // 增加数值
                    switch (setState) {
                        case SET_HOUR:
                            currentTime.hour = (currentTime.hour + 1) % 24;
                            break;
                        case SET_MINUTE:
                            currentTime.minute = (currentTime.minute + 1) % 60;
                            break;
                        case SET_SECOND:
                            currentTime.second = (currentTime.second + 1) % 60;
                            break;
                    }
                    break;
                    
                case KEY_DOWN:   // 减少数值
                    switch (setState) {
                        case SET_HOUR:
                            currentTime.hour = (currentTime.hour + 23) % 24;
                            break;
                        case SET_MINUTE:
                            currentTime.minute = (currentTime.minute + 59) % 60;
                            break;
                        case SET_SECOND:
                            currentTime.second = (currentTime.second + 59) % 60;
                            break;
                    }
                    break;
                    
                case KEY_OK:     // 确认并退出设置
                    DS1302_SetTime(&currentTime);  // 保存到DS1302
                    workMode = MODE_NORMAL;
                    LCD1602_Clear();
                    break;
            }
            break;
            
        case MODE_SET_DATE:
            // 设置日期模式(类似时间设置)
            switch (key_value) {
                case KEY_MODE:
                    setState++;
                    if (setState > SET_DAY) {
                        setState = SET_YEAR;
                    }
                    break;
                    
                case KEY_UP:
                    switch (setState) {
                        case SET_YEAR:
                            currentTime.year = (currentTime.year + 1) % 100;
                            break;
                        case SET_MONTH:
                            currentTime.month = (currentTime.month % 12) + 1;
                            break;
                        case SET_DAY:
                            currentTime.day = (currentTime.day % 31) + 1;
                            break;
                    }
                    break;
                    
                case KEY_DOWN:
                    switch (setState) {
                        case SET_YEAR:
                            currentTime.year = (currentTime.year + 99) % 100;
                            break;
                        case SET_MONTH:
                            currentTime.month = ((currentTime.month + 10) % 12) + 1;
                            break;
                        case SET_DAY:
                            currentTime.day = ((currentTime.day + 30) % 31) + 1;
                            break;
                    }
                    break;
                    
                case KEY_OK:
                    DS1302_SetDate(&currentTime);
                    workMode = MODE_NORMAL;
                    LCD1602_Clear();
                    break;
            }
            break;
    }
}

/**
 * @brief 闹钟检查函数
 */
void Clock_CheckAlarm(void) {
    if (alarmTime.enable && 
        currentTime.hour == alarmTime.hour &&
        currentTime.minute == alarmTime.minute &&
        currentTime.second == 0) {
        alarmRingFlag = 1;
        workMode = MODE_ALARM_ON;
        LCD1602_Clear();
        LCD1602_ShowString(0, 0, "ALARM!");
        LCD1602_ShowString(0, 1, "Press any key");
    }
}

/**
 * @brief 闹钟响铃处理
 */
void Clock_AlarmHandler(void) {
    static uint8_t buzz_count = 0;
    
    if (alarmRingFlag) {
        // 蜂鸣器响铃
        if (buzz_count < 100) {
            GPIO_SetBits(BUZZER_PORT, BUZZER_PIN);  // 开蜂鸣器
        } else if (buzz_count < 200) {
            GPIO_ResetBits(BUZZER_PORT, BUZZER_PIN); // 关蜂鸣器
        } else {
            buzz_count = 0;
        }
        buzz_count++;
        
        // 任意键停止闹钟
        if (Key_Scan_Single() != 0) {
            alarmRingFlag = 0;
            workMode = MODE_NORMAL;
            GPIO_ResetBits(BUZZER_PORT, BUZZER_PIN);
            LCD1602_Clear();
        }
    }
}

3.5 主程序

/**
 * @file main.c
 * @brief 电子时钟主程序
 */

#include "stm32f10x.h"
#include "clock.h"
#include "key.h"
#include "ds1302.h"
#include "lcd1602.h"
#include "delay.h"

// 系统滴答定时器
volatile uint32_t system_tick = 0;

/**
 * @brief 系统初始化
 */
void System_Init(void) {
    // 初始化系统时钟
    SystemClock_Init();
    
    // 初始化各模块
    Delay_Init();
    Key_Init();
    LCD1602_Init();
    DS1302_Init();
    
    // 读取DS1302时间
    DS1302_GetTime(&currentTime);
    DS1302_GetDate(&currentTime);
    
    // 初始化闹钟
    alarmTime.hour = 7;
    alarmTime.minute = 30;
    alarmTime.enable = 1;
    
    // 初始化蜂鸣器
    GPIO_InitTypeDef GPIO_InitStructure;
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);
    GPIO_InitStructure.GPIO_Pin = BUZZER_PIN;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_Init(BUZZER_PORT, &GPIO_InitStructure);
    GPIO_ResetBits(BUZZER_PORT, BUZZER_PIN);
    
    printf("Electronic Clock Started!\r\n");
}

/**
 * @brief 系统滴答定时器中断
 */
void SysTick_Handler(void) {
    system_tick++;
    
    // 每500ms翻转闪烁标志
    if (system_tick % 500 == 0) {
        blinkFlag = !blinkFlag;
    }
}

/**
 * @brief 主循环
 */
int main(void) {
    uint8_t key_value;
    uint8_t press_type;
    
    // 系统初始化
    System_Init();
    
    while (1) {
        // 按键扫描和处理
        key_value = Key_Scan_LongPress(&press_type);
        if (key_value != 0) {
            Clock_KeyHandler(key_value, press_type);
        }
        
        // 正常模式下更新显示
        if (workMode == MODE_NORMAL) {
            // 每秒更新一次时间显示
            static uint32_t last_update = 0;
            if (system_tick - last_update >= 1000) {
                DS1302_GetTime(&currentTime);
                DS1302_GetDate(&currentTime);
                last_update = system_tick;
            }
        }
        
        // 更新显示
        Clock_UpdateDisplay();
        
        // 检查闹钟
        Clock_CheckAlarm();
        
        // 闹钟响铃处理
        Clock_AlarmHandler();
        
        Delay_ms(10);
    }
}

四、DS1302实时时钟驱动

/**
 * @file ds1302.c
 * @brief DS1302实时时钟驱动
 */

#include "ds1302.h"

// DS1302寄存器地址
#define DS1302_SECOND     0x80
#define DS1302_MINUTE     0x82
#define DS1302_HOUR       0x84
#define DS1302_DATE       0x86
#define DS1302_MONTH      0x88
#define DS1302_DAY        0x8A
#define DS1302_YEAR       0x8C
#define DS1302_CONTROL    0x8E

/**
 * @brief DS1302初始化
 */
void DS1302_Init(void) {
    GPIO_InitTypeDef GPIO_InitStructure;
    
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
    
    // 配置RST引脚
    GPIO_InitStructure.GPIO_Pin = DS1302_RST_PIN;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_Init(DS1302_RST_PORT, &GPIO_InitStructure);
    
    // 配置IO引脚
    GPIO_InitStructure.GPIO_Pin = DS1302_IO_PIN;
    GPIO_Init(DS1302_IO_PORT, &GPIO_InitStructure);
    
    // 配置SCK引脚
    GPIO_InitStructure.GPIO_Pin = DS1302_SCK_PIN;
    GPIO_Init(DS1302_SCK_PORT, &GPIO_InitStructure);
    
    // 禁止写保护
    DS1302_WriteByte(DS1302_CONTROL, 0x00);
}

/**
 * @brief 向DS1302写入一个字节
 */
void DS1302_WriteByte(uint8_t addr, uint8_t data) {
    uint8_t i;
    
    GPIO_SetBits(DS1302_RST_PORT, DS1302_RST_PIN);
    
    for (i = 0; i < 8; i++) {
        GPIO_WriteBit(DS1302_IO_PORT, DS1302_IO_PIN, (addr >> i) & 0x01);
        GPIO_SetBits(DS1302_SCK_PORT, DS1302_SCK_PIN);
        GPIO_ResetBits(DS1302_SCK_PORT, DS1302_SCK_PIN);
    }
    
    for (i = 0; i < 8; i++) {
        GPIO_WriteBit(DS1302_IO_PORT, DS1302_IO_PIN, (data >> i) & 0x01);
        GPIO_SetBits(DS1302_SCK_PORT, DS1302_SCK_PIN);
        GPIO_ResetBits(DS1302_SCK_PORT, DS1302_SCK_PIN);
    }
    
    GPIO_ResetBits(DS1302_RST_PORT, DS1302_RST_PIN);
}

/**
 * @brief 从DS1302读取一个字节
 */
uint8_t DS1302_ReadByte(uint8_t addr) {
    uint8_t i, data = 0;
    
    GPIO_SetBits(DS1302_RST_PORT, DS1302_RST_PIN);
    
    for (i = 0; i < 8; i++) {
        GPIO_WriteBit(DS1302_IO_PORT, DS1302_IO_PIN, (addr >> i) & 0x01);
        GPIO_SetBits(DS1302_SCK_PORT, DS1302_SCK_PIN);
        GPIO_ResetBits(DS1302_SCK_PORT, DS1302_SCK_PIN);
    }
    
    for (i = 0; i < 8; i++) {
        GPIO_SetBits(DS1302_SCK_PORT, DS1302_SCK_PIN);
        data >>= 1;
        if (GPIO_ReadInputDataBit(DS1302_IO_PORT, DS1302_IO_PIN)) {
            data |= 0x80;
        }
        GPIO_ResetBits(DS1302_SCK_PORT, DS1302_SCK_PIN);
    }
    
    GPIO_ResetBits(DS1302_RST_PORT, DS1302_RST_PIN);
    return data;
}

/**
 * @brief BCD转十进制
 */
uint8_t BCD2DEC(uint8_t bcd) {
    return ((bcd >> 4) * 10) + (bcd & 0x0F);
}

/**
 * @brief 十进制转BCD
 */
uint8_t DEC2BCD(uint8_t dec) {
    return ((dec / 10) << 4) | (dec % 10);
}

/**
 * @brief 设置时间
 */
void DS1302_SetTime(TimeStruct *time) {
    DS1302_WriteByte(DS1302_SECOND, DEC2BCD(time->second));
    DS1302_WriteByte(DS1302_MINUTE, DEC2BCD(time->minute));
    DS1302_WriteByte(DS1302_HOUR, DEC2BCD(time->hour));
}

/**
 * @brief 获取时间
 */
void DS1302_GetTime(TimeStruct *time) {
    time->second = BCD2DEC(DS1302_ReadByte(DS1302_SECOND));
    time->minute = BCD2DEC(DS1302_ReadByte(DS1302_MINUTE));
    time->hour = BCD2DEC(DS1302_ReadByte(DS1302_HOUR));
}

五、LCD1602显示驱动

/**
 * @file lcd1602.c
 * @brief LCD1602显示驱动
 */

#include "lcd1602.h"

// 延时函数
static void LCD_DelayUs(uint16_t us) {
    while (us--) {
        __NOP(); __NOP(); __NOP(); __NOP();
    }
}

static void LCD_DelayMs(uint16_t ms) {
    uint16_t i, j;
    for (i = 0; i < ms; i++) {
        for (j = 0; j < 1000; j++) {
            __NOP();
        }
    }
}

/**
 * @brief 写命令
 */
void LCD_WriteCmd(uint8_t cmd) {
    GPIO_ResetBits(LCD_RS_PORT, LCD_RS_PIN);  // RS=0
    GPIO_ResetBits(LCD_RW_PORT, LCD_RW_PIN);  // RW=0
    GPIO_SetBits(LCD_EN_PORT, LCD_EN_PIN);    // EN=1
    
    GPIO_WriteBit(LCD_D4_PORT, LCD_D4_PIN, (cmd >> 4) & 0x01);
    GPIO_WriteBit(LCD_D5_PORT, LCD_D5_PIN, (cmd >> 5) & 0x01);
    GPIO_WriteBit(LCD_D6_PORT, LCD_D6_PIN, (cmd >> 6) & 0x01);
    GPIO_WriteBit(LCD_D7_PORT, LCD_D7_PIN, (cmd >> 7) & 0x01);
    
    LCD_DelayUs(1);
    GPIO_ResetBits(LCD_EN_PORT, LCD_EN_PIN);  // EN=0
    LCD_DelayMs(2);
}

/**
 * @brief 写数据
 */
void LCD_WriteData(uint8_t data) {
    GPIO_SetBits(LCD_RS_PORT, LCD_RS_PIN);    // RS=1
    GPIO_ResetBits(LCD_RW_PORT, LCD_RW_PIN);  // RW=0
    GPIO_SetBits(LCD_EN_PORT, LCD_EN_PIN);    // EN=1
    
    GPIO_WriteBit(LCD_D4_PORT, LCD_D4_PIN, (data >> 4) & 0x01);
    GPIO_WriteBit(LCD_D5_PORT, LCD_D5_PIN, (data >> 5) & 0x01);
    GPIO_WriteBit(LCD_D6_PORT, LCD_D6_PIN, (data >> 6) & 0x01);
    GPIO_WriteBit(LCD_D7_PORT, LCD_D7_PIN, (data >> 7) & 0x01);
    
    LCD_DelayUs(1);
    GPIO_ResetBits(LCD_EN_PORT, LCD_EN_PIN);  // EN=0
    LCD_DelayMs(2);
}

/**
 * @brief LCD初始化
 */
void LCD1602_Init(void) {
    GPIO_InitTypeDef GPIO_InitStructure;
    
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);
    
    // 配置LCD引脚
    GPIO_InitStructure.GPIO_Pin = LCD_RS_PIN | LCD_RW_PIN | LCD_EN_PIN | 
                                LCD_D4_PIN | LCD_D5_PIN | LCD_D6_PIN | LCD_D7_PIN;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
    GPIO_InitStructure.Gpio_Speed = GPIO_Speed_50MHz;
    GPIO_Init(LCD_PORT, &GPIO_InitStructure);
    
    LCD_DelayMs(15);
    
    // 初始化序列
    LCD_WriteCmd(0x33);  // 8位接口转4位接口
    LCD_WriteCmd(0x32);  // 4位接口模式
    LCD_WriteCmd(0x28);  // 4位数据,2行显示,5x7字体
    LCD_WriteCmd(0x0C);  // 显示开,光标关,闪烁关
    LCD_WriteCmd(0x06);  // 文字不动,地址自动+1
    LCD_WriteCmd(0x01);  // 清屏
    LCD_DelayMs(2);
}

/**
 * @brief 显示字符串
 */
void LCD1602_ShowString(uint8_t x, uint8_t y, char *str) {
    uint8_t addr;
    
    if (y == 0) {
        addr = 0x80 + x;
    } else {
        addr = 0xC0 + x;
    }
    
    LCD_WriteCmd(addr);
    
    while (*str) {
        LCD_WriteData(*str++);
    }
}

/**
 * @brief 清屏
 */
void LCD1602_Clear(void) {
    LCD_WriteCmd(0x01);
    LCD_DelayMs(2);
}

参考代码 电子钟课程设计 www.youwenfan.com/contentcsu/60346.html

六、功能扩展建议

6.1 可增加的功能

  1. 温度显示:添加DS18B20温度传感器
  2. 农历显示:增加农历转换算法
  3. 世界时间:支持多时区时间显示
  4. 倒计时功能:添加倒计时器
  5. 秒表功能:增加秒表计时
  6. 背光控制:PWM调节LCD背光亮度
  7. 掉电保存:使用EEPROM保存设置参数

6.2 硬件升级方案

升级项目 原方案 升级方案 优势
显示模块 LCD1602 OLED 128×64 显示效果更好,功耗更低
时钟芯片 DS1302 DS3231 精度更高,温度补偿
按键输入 机械按键 触摸按键 外观更美观
电源管理 直接供电 锂电池+充电管理 便携性更强

6.3 软件优化建议

  1. 省电模式:空闲时进入待机模式
  2. 自动亮度:根据环境光自动调节背光
  3. 语音播报:添加语音模块播报时间
  4. 蓝牙连接:通过蓝牙与手机同步时间
  5. 网络校时:连接WiFi获取网络时间

专注于matlab/simulink,电子电路,编程