基于STM32的 电子时钟设计 源代码(无实物) 基于STM32F103战舰开发板利用LCD屏来实时显示温湿度传感器DHT11所测得的数据和时间日期的信息;需要实现的功能是:整点报时功能;倒计时功能;当前时间日期可以设置且掉电后保持 可设置的闹铃时间到蜂鸣器发出嘀嘀滴(长)声 短按KEY_UP 循环切换温度阈值/时间/闹钟/倒计时/主界面功能 长按KEY_UP 保存当前参数值 按键0 循环切换设置哪一个值 按键1 上调数值(主界面时在开启倒计时功能启动倒计时) 按键2 下调数值(主界面时在开启倒计时功能暂停倒计时) 通过按键控制的功能:通过按键可以设置上调和下调温度阈值,可以设定一个温度标准值当所测温度超过标准值时设置蜂鸣器进行报警(短声嘀嘀)和相灯的快频率闪烁(超温报警) 注意: 1、设定时间日期星期时按下设置的键相应的参数会闪动提示正在修改数据 2、设定完以后再按一下 KEY_UP键就退出设定的界面回到正常显示状态 3、用DHT11显示温湿度精确到小数点后一位 (如36.3) 4、月日分秒温湿度数据动态刷新显示 RTC实时时钟 看门狗 定时器中断 DHT11最近在捣鼓一个基于STM32的电子时钟项目虽然手头没有实物但代码已经调得差不多了。这个时钟功能挺全的整点报时、倒计时、温湿度监测、闹钟、超温报警该有的都有了。用的是STM32F103战舰开发板配合LCD屏和DHT11传感器下面分享一些关键代码和实现思路。一、整体架构设计整个系统围绕几个核心模块展开RTC实时时钟、DHT11温湿度采集、LCD显示驱动、按键处理和蜂鸣器控制。采用状态机的方式管理不同界面主界面、时间设置、闹钟设置等通过定时器中断确保时间精度。// 系统状态定义 typedef enum { SYS_MAIN, // 主界面 SYS_TIME_SET, // 时间设置 SYS_ALARM_SET, // 闹钟设置 SYS_TEMP_SET, // 温度阈值设置 SYS_COUNTDOWN // 倒计时界面 } SystemState; SystemState g_sys_state SYS_MAIN;二、RTC时钟与掉电保持STM32的RTC模块自带电池备份域掉电后依靠纽扣电池保持运行。初始化时先检查是否是第一次上电如果是就需要设置初始时间。void RTC_Init(void) { // 检查是否为首次配置 if (BKP_ReadBackupRegister(BKP_DR1) ! 0x5050) { RCC_APB1PeriphClockCmd(RCC_APB1Periph_PWR | RCC_APB1Periph_BKP, ENABLE); PWR_BackupAccessCmd(ENABLE); // 初始化RTC时钟源 RCC_LSEConfig(RCC_LSE_ON); while (RCC_GetFlagStatus(RCC_FLAG_LSERDY) RESET); RCC_RTCCLKConfig(RCC_RTCCLKSource_LSE); RCC_RTCCLKCmd(ENABLE); // 设置时间 RTC_SetTime(2024, 1, 1, 12, 0, 0); BKP_WriteBackupRegister(BKP_DR1, 0x5050); } } // 获取当前时间 RTC_Time get_current_time(void) { RTC_Time time; time.year RTC_GetCounter() / 31536000 1970; // 简化计算 // ... 其他时间字段计算 return time; }这里有个小技巧通过备份寄存器BKP_DR1写入特定值0x5050作为是否首次配置的标志。如果不是首次直接从RTC计数器读取时间。三、DHT11温湿度采集DHT11是单总线数字传感器时序要求比较严格。采集时需要先发送开始信号然后等待传感器响应。#define DHT11_OUT_HIGH() GPIO_SetBits(GPIOB, GPIO_Pin_12) #define DHT11_OUT_LOW() GPIO_ResetBits(GPIOB, GPIO_Pin_12) #define DHT11_IN() GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_12) uint8_t DHT11_Read_Data(float *temp, float *humi) { uint8_t buf[5] {0}; // 主机拉低18ms DHT11_OUT_LOW(); delay_ms(18); DHT11_OUT_HIGH(); delay_us(30); // 等待传感器响应 if (DHT11_IN() RESET) { while (DHT11_IN() RESET); // 等待拉高 while (DHT11_IN() SET); // 等待拉低 // 读取40位数据 for (int i 0; i 40; i) { while (DHT11_IN() RESET); delay_us(40); buf[i/8] 1; if (DHT11_IN() SET) { buf[i/8] | 1; while (DHT11_IN() SET); } } // 校验数据 if (buf[4] (buf[0] buf[1] buf[2] buf[3])) { *humi buf[0] buf[1] * 0.1; *temp buf[2] buf[3] * 0.1; return 1; } } return 0; }注意点DHT11的时序很关键微秒级延时必须准确。读取温度值后需要转换成带一位小数的浮点数格式显示。四、按键处理与状态切换通过KEY_UP键短按循环切换功能长按保存设置。这里用状态机实现界面切换按键检测放在定时器中断里做防抖处理。void KEY_Handler(void) { static uint32_t key_press_time 0; if (KEY_UP 0) { // 按键按下 if (key_press_time 0) { key_press_time GetTickCount(); } else if (GetTickCount() - key_press_time 1000) { // 长按1秒 // 保存当前设置到Flash Save_Settings(); key_press_time 0; } } else { // 按键释放 if (key_press_time 0 GetTickCount() - key_press_time 1000) { // 短按切换系统状态 g_sys_state (g_sys_state 1) % 5; Update_Display_Mode(); } key_press_time 0; } }五、整点报时与闹钟功能整点报时比较简单每分钟检查一次是否到整点。闹钟功能需要比较当前时间与设定的闹钟时间。void Check_Alarm(void) { RTC_Time now get_current_time(); // 整点报时 if (now.minute 0 now.second 0) { BEEP_Beep(500, 3); // 响3次每次500ms } // 闹钟检查 if (g_alarm.enable now.hour g_alarm.hour now.minute g_alarm.minute now.second 3) { // 响3秒 BEEP_Beep(1000, 1); // 长响1秒 } // 超温报警 if (g_current_temp g_temp_threshold) { BEEP_Beep(200, 5); // 短促报警 LED_Flash(100); // LED快速闪烁 } }蜂鸣器控制函数BEEP_Beep()通过PWM产生不同频率的声音实现嘀嘀滴的长短音区别。六、倒计时功能的实现倒计时功能在主界面下通过按键1启动按键2暂停。使用一个单独的定时器进行秒级计时。typedef struct { uint32_t total_seconds; uint32_t remaining_seconds; uint8_t is_running; } Countdown; Countdown g_countdown {0}; void Countdown_Start(void) { if (g_countdown.total_seconds 0) { g_countdown.remaining_seconds g_countdown.total_seconds; g_countdown.is_running 1; } } // 在1秒定时器中断中调用 void Countdown_Update(void) { if (g_countdown.is_running g_countdown.remaining_seconds 0) { g_countdown.remaining_seconds--; if (g_countdown.remaining_seconds 0) { // 倒计时结束触发提示 BEEP_Beep(1000, 3); g_countdown.is_running 0; } } }七、看门狗与系统稳定性为了防止程序跑飞加入了独立看门狗IWDG。在main循环中定期喂狗。void IWDG_Init(uint16_t reload) { IWDG_WriteAccessCmd(IWDG_WriteAccess_Enable); IWDG_SetPrescaler(IWDG_Prescaler_32); // 32分频 IWDG_SetReload(reload); // 设置重载值 IWDG_ReloadCounter(); // 重载计数器 IWDG_Enable(); // 使能看门狗 } // 在主循环中定期调用 void Feed_Dog(void) { IWDG_ReloadCounter(); }八、显示效果的优化为了让设置时参数闪动更明显采用交替显示的方式正常显示0.5秒空白0.5秒。void Display_Blink_Value(uint16_t x, uint16_t y, uint16_t value, uint8_t blink) { static uint8_t blink_state 0; static uint32_t last_time 0; if (GetTickCount() - last_time 500) { blink_state !blink_state; last_time GetTickCount(); } if (!blink || blink_state) { LCD_ShowNum(x, y, value, 2, 16); } else { LCD_ShowString(x, y, , 16); // 显示空格实现闪烁效果 } }九、一些踩过的坑DHT11时序问题最开始直接用delay函数结果经常读取失败。后来改用在定时器中断中计数的方式稳定性大大提升。RTC初始化忘记使能备份域访问权限导致时间无法保存。调试了半天才发现少了PWR_BackupAccessCmd(ENABLE)这一行。按键长按检测最初在主循环中检测响应不灵敏。放到定时器中断中每10ms检测一次后效果好了很多。显示刷新所有数据同时刷新会导致LCD闪烁。后来改为分时刷新时间每秒刷新温湿度每5秒刷新界面切换时立即刷新。这个项目虽然只是软件模拟但涵盖了STM32开发的很多基础知识点GPIO控制、定时器、中断、RTC、Flash读写等。最重要的是状态机的运用让复杂的多功能切换变得清晰可控。基于STM32的 电子时钟设计 源代码(无实物) 基于STM32F103战舰开发板利用LCD屏来实时显示温湿度传感器DHT11所测得的数据和时间日期的信息;需要实现的功能是:整点报时功能;倒计时功能;当前时间日期可以设置且掉电后保持 可设置的闹铃时间到蜂鸣器发出嘀嘀滴(长)声 短按KEY_UP 循环切换温度阈值/时间/闹钟/倒计时/主界面功能 长按KEY_UP 保存当前参数值 按键0 循环切换设置哪一个值 按键1 上调数值(主界面时在开启倒计时功能启动倒计时) 按键2 下调数值(主界面时在开启倒计时功能暂停倒计时) 通过按键控制的功能:通过按键可以设置上调和下调温度阈值,可以设定一个温度标准值当所测温度超过标准值时设置蜂鸣器进行报警(短声嘀嘀)和相灯的快频率闪烁(超温报警) 注意: 1、设定时间日期星期时按下设置的键相应的参数会闪动提示正在修改数据 2、设定完以后再按一下 KEY_UP键就退出设定的界面回到正常显示状态 3、用DHT11显示温湿度精确到小数点后一位 (如36.3) 4、月日分秒温湿度数据动态刷新显示 RTC实时时钟 看门狗 定时器中断 DHT11代码仓库地址[此处省略实际项目中需填写]下次准备加上网络对时和天气显示功能让这个电子时钟更加实用。不过那是另一个故事了。