基于STM32的电子时钟设计(四按键控制)
一、系统总体设计
1.1 功能需求
- 时间显示:时、分、秒(24小时制)
- 日期显示:年、月、日、星期
- 按键控制:4个独立按键实现设置、调整、切换
- 闹钟功能:可设置闹钟时间
- 显示方式:LCD1602/LCD12864/OLED显示屏
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(¤tTime); // 保存到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(¤tTime);
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(¤tTime);
DS1302_GetDate(¤tTime);
// 初始化闹钟
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(¤tTime);
DS1302_GetDate(¤tTime);
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 可增加的功能
- 温度显示:添加DS18B20温度传感器
- 农历显示:增加农历转换算法
- 世界时间:支持多时区时间显示
- 倒计时功能:添加倒计时器
- 秒表功能:增加秒表计时
- 背光控制:PWM调节LCD背光亮度
- 掉电保存:使用EEPROM保存设置参数
6.2 硬件升级方案
| 升级项目 |
原方案 |
升级方案 |
优势 |
| 显示模块 |
LCD1602 |
OLED 128×64 |
显示效果更好,功耗更低 |
| 时钟芯片 |
DS1302 |
DS3231 |
精度更高,温度补偿 |
| 按键输入 |
机械按键 |
触摸按键 |
外观更美观 |
| 电源管理 |
直接供电 |
锂电池+充电管理 |
便携性更强 |
6.3 软件优化建议
- 省电模式:空闲时进入待机模式
- 自动亮度:根据环境光自动调节背光
- 语音播报:添加语音模块播报时间
- 蓝牙连接:通过蓝牙与手机同步时间
- 网络校时:连接WiFi获取网络时间