STM32F103C8T6 RTC闹钟实战:用LSI时钟源做个定时提醒器(附完整代码)
2026/4/20 9:32:28 网站建设 项目流程

STM32F103C8T6 RTC闹钟实战:打造高精度定时提醒系统

在嵌入式开发中,实时时钟(RTC)模块的应用场景非常广泛。对于STM32初学者来说,掌握RTC闹钟功能不仅能加深对芯片外设的理解,更能为实际项目开发打下坚实基础。本文将带你从零开始,基于STM32F103C8T6的LSI时钟源,构建一个完整的定时提醒系统。

1. 项目规划与硬件设计

1.1 需求分析与功能定义

我们的目标是创建一个可自定义提醒时间的桌面定时器,主要功能包括:

  • 基于RTC的精确时间记录
  • 可编程闹钟触发
  • 多种提醒方式(LED/蜂鸣器)
  • 简单的时间设置接口

硬件选型清单

组件型号/参数数量备注
MCUSTM32F103C8T61蓝色pill开发板
时钟源LSI(内部40kHz)1无需外部晶振
提醒装置LED/有源蜂鸣器各1用户可选
输入设备轻触按键3设置/调整用
显示设备0.96寸OLED1可选

1.2 电路连接要点

核心电路连接示意图:

STM32F103C8T6 ├── PC13 → LED阳极(阴极接地) ├── PB8 → 蜂鸣器正极 ├── PA0 → 设置按键 ├── PA1 → 增加按键 ├── PA2 → 减少按键 └── I2C1 → OLED显示屏

提示:蜂鸣器需串联100Ω限流电阻,LED建议使用220Ω限流电阻。

2. RTC基础与LSI时钟配置

2.1 RTC模块工作原理

STM32的RTC模块是一个独立的BCD计时器,具有以下关键特性:

  • 32位可编程计数器
  • 自动处理闰年计算
  • 闹钟中断功能
  • 低功耗模式下仍可运行

时钟源对比表

特性LSILSEHSE
精度±5%±20ppm±50ppm
频率40kHz32.768kHz4-16MHz
功耗极低
稳定性一般优秀优秀
需要外部元件

2.2 LSI时钟配置实战

使用LSI作为RTC时钟源的初始化代码:

void RTC_Init(void) { // 启用PWR和BKP时钟 RCC_APB1PeriphClockCmd(RCC_APB1Periph_PWR | RCC_APB1Periph_BKP, ENABLE); PWR_BackupAccessCmd(ENABLE); // 检查是否是首次配置 if (BKP_ReadBackupRegister(BKP_DR1) != 0xA5A5) { // 启用LSI时钟 RCC_LSICmd(ENABLE); while(RCC_GetFlagStatus(RCC_FLAG_LSIRDY) == RESET); // 配置RTC时钟源 RCC_RTCCLKConfig(RCC_RTCCLKSource_LSI); RCC_RTCCLKCmd(ENABLE); // 等待RTC同步 RTC_WaitForSynchro(); RTC_WaitForLastTask(); // 设置预分频器 RTC_SetPrescaler(40000 - 1); // 40kHz/40000 = 1Hz RTC_WaitForLastTask(); // 标记已初始化 BKP_WriteBackupRegister(BKP_DR1, 0xA5A5); } }

注意:LSI时钟精度相对较低,适合对时间精度要求不高的应用。如需更高精度,建议使用外部LSE晶振。

3. 闹钟功能实现

3.1 闹钟中断配置

完整的闹钟中断配置流程:

void RTC_Alarm_Config(void) { NVIC_InitTypeDef NVIC_InitStructure; // 配置RTC全局中断 NVIC_InitStructure.NVIC_IRQChannel = RTC_IRQn; NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1; NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1; NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; NVIC_Init(&NVIC_InitStructure); // 使能闹钟中断 RTC_ITConfig(RTC_IT_ALR, ENABLE); RTC_WaitForLastTask(); } // 中断服务程序 void RTC_IRQHandler(void) { if(RTC_GetITStatus(RTC_IT_ALR) == SET) { // 触发提醒动作 GPIO_SetBits(GPIOC, GPIO_Pin_13); // LED亮 // 或 GPIO_SetBits(GPIOB, GPIO_Pin_8); // 蜂鸣器响 RTC_ClearITPendingBit(RTC_IT_ALR); RTC_WaitForLastTask(); } }

3.2 动态设置闹钟时间

实用的闹钟设置函数,支持任意时间点触发:

void Set_Alarm(uint8_t hours, uint8_t minutes, uint8_t seconds) { time_t current_time, alarm_time; struct tm time_struct; // 获取当前RTC时间 current_time = RTC_GetCounter(); time_struct = *localtime(&current_time); // 设置闹钟时间(保留年月日,只修改时分秒) time_struct.tm_hour = hours; time_struct.tm_min = minutes; time_struct.tm_sec = seconds; alarm_time = mktime(&time_struct); RTC_SetAlarm(alarm_time); RTC_WaitForLastTask(); // 如果需要重复闹钟,可以在此设置下一次触发时间 }

4. 用户交互与功能扩展

4.1 时间设置界面实现

通过三个按键实现完整的时间设置功能:

// 按键处理函数示例 void KEY_Handler(void) { static uint8_t selected_field = 0; // 0-小时,1-分钟,2-秒 if(KEY_Setting_Pressed()) { selected_field = (selected_field + 1) % 3; } else if(KEY_Up_Pressed()) { Adjust_Time(selected_field, 1); // 增加 } else if(KEY_Down_Pressed()) { Adjust_Time(selected_field, -1); // 减少 } } // 时间调整函数 void Adjust_Time(uint8_t field, int8_t delta) { time_t current_time; struct tm time_struct; current_time = RTC_GetCounter(); time_struct = *localtime(&current_time); switch(field) { case 0: // 小时 time_struct.tm_hour = (time_struct.tm_hour + delta + 24) % 24; break; case 1: // 分钟 time_struct.tm_min = (time_struct.tm_min + delta + 60) % 60; break; case 2: // 秒 time_struct.tm_sec = (time_struct.tm_sec + delta + 60) % 60; break; } RTC_SetCounter(mktime(&time_struct)); RTC_WaitForLastTask(); }

4.2 番茄钟工作模式实现

扩展功能:实现25分钟工作+5分钟休息的番茄钟循环:

void Tomato_Mode_Start(void) { time_t current_time; struct tm time_struct; // 获取当前时间 current_time = RTC_GetCounter(); time_struct = *localtime(&current_time); // 设置25分钟后闹钟(工作时间) time_struct.tm_min += 25; RTC_SetAlarm(mktime(&time_struct)); RTC_WaitForLastTask(); // 在OLED显示状态 OLED_ShowString(1, 1, "Work Mode: 25:00"); } // 在闹钟中断中处理模式切换 void RTC_IRQHandler(void) { if(RTC_GetITStatus(RTC_IT_ALR) == SET) { if(current_mode == WORK_MODE) { // 切换到休息模式 current_mode = BREAK_MODE; Set_Break_Alarm(5); // 5分钟休息 OLED_ShowString(1, 1, "Break Time: 5:00"); } else { // 切换回工作模式 current_mode = WORK_MODE; Set_Work_Alarm(25); // 25分钟工作 OLED_ShowString(1, 1, "Work Mode: 25:00"); } RTC_ClearITPendingBit(RTC_IT_ALR); RTC_WaitForLastTask(); } }

5. 系统优化与实用技巧

5.1 提高LSI时钟精度的方法

虽然LSI精度有限,但可以通过以下方法改善:

  1. 温度补偿:记录不同温度下的时钟偏差,软件补偿
  2. 定期同步:通过外部时间源(如GPS)定期校准
  3. 平均算法:统计长期偏差,动态调整预分频值

校准代码示例:

void LSI_Calibration(float ppm) { uint32_t prescaler = 40000; // 默认值 // 根据ppm误差调整预分频值 prescaler = (uint32_t)(40000 * (1 + ppm/1000000.0)); RTC_SetPrescaler(prescaler - 1); RTC_WaitForLastTask(); }

5.2 低功耗优化

实现电池供电时的省电设计:

void Enter_LowPower_Mode(void) { // 关闭外设时钟 RCC_APB1PeriphClockCmd(RCC_APB1Periph_ALL, DISABLE); RCC_APB2PeriphClockCmd(RCC_APB2Periph_ALL, DISABLE); // 配置唤醒源为RTC闹钟 PWR_WakeUpPinCmd(ENABLE); RTC_ClearFlag(RTC_FLAG_ALRAF); // 进入待机模式 PWR_EnterSTANDBYMode(); }

5.3 常见问题排查

RTC初始化失败的可能原因

  1. 未正确启用PWR和BKP时钟
  2. 忘记调用PWR_BackupAccessCmd(ENABLE)
  3. 未等待LSI就绪(RCC_FLAG_LSIRDY)
  4. 未正确处理RTC同步和任务等待

闹钟不触发的检查步骤

  1. 确认RTC_ITConfig(RTC_IT_ALR, ENABLE)已调用
  2. 检查NVIC中断配置是否正确
  3. 验证闹钟时间是否设置在未来
  4. 确保RTC计数器正常运行(可通过RTC_GetCounter()读取)

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询