STM32F4 无外部晶振的可靠唤醒:LSE 停振后利用 RTC 备份域寄存器的实战指南
👁 1 阅读 · 2026-08-27 · 嵌入式
在 STM32F4 系列开发中,LSE(低速外部晶振)常被用于 RTC 实时时钟,但晶振起振慢、易受干扰,一旦停振可能导致系统唤醒失败。本文深入探讨如何利用 RTC 备份域寄存器(BKP)在 LSE 失效时实现无外部晶振的可靠唤醒,涵盖原理、配置步骤、完整代码及注意事项,帮助开发者构建高鲁棒性的低功耗系统。
# 引言
在嵌入式低功耗设计中,RTC(实时时钟)是定时唤醒的常用外设,而 STM32F4 的 RTC 通常依赖 LSE(32.768kHz 外部晶振)保证精度。然而,LSE 存在起振慢(典型 1-2 秒)、抗干扰差、在恶劣环境下易停振等问题。一旦 LSE 停振,RTC 无法工作,系统可能无法按计划唤醒,导致功能失效。
本文提出一种解决方案:利用 RTC 备份域寄存器(BKP)在 LSE 停振时自动切换至 LSI(内部低速 RC 振荡器),并借助备份寄存器保存唤醒标志,实现无外部晶振的可靠唤醒。该方法兼顾精度与可靠性,适用于对时间精度要求不苛刻但强调唤醒可靠性的场景。
# 原理剖析
## 1. RTC 时钟源与备份域
- STM32F4 的 RTC 可选择 LSE、LSI 或 HSE 分频作为时钟源。LSE 精度高(±20ppm),但依赖外部晶振;LSI 精度低(典型 ±1.5%),但无需外部元件,启动快。
- 备份域(Backup Domain)包括 RTC、备份寄存器(BKP)和备份 SRAM,由 VBAT 供电,当主电源掉电时仍可保持数据。备份寄存器可用于存储唤醒标志、系统状态等关键信息。
- 当 LSE 停振时,RTC 会检测到时钟失效(通过 RTC_ISR 的 LSERDY 位),但默认不会自动切换时钟源。我们需要在软件中处理。
## 2. 唤醒机制设计
- 目标:在 LSE 正常时使用 LSE 保证时间精度;当 LSE 停振时,自动切换至 LSI,并利用备份寄存器记录“LSE 失效”标志,同时继续维持 RTC 计数,确保唤醒中断按时触发。
- 唤醒流程:系统进入 STOP 模式前,设置 RTC 闹钟(Alarm A)为唤醒源。若 LSE 停振,RTC 仍以 LSI 运行,闹钟依然有效。唤醒后,通过备份寄存器判断是否发生了 LSE 失效,从而决定是否重新校准或采取补救措施。
# 配置步骤
## 1. 使能电源和备份域时钟
- 必须使能 PWR 和 RCC 的备份域时钟,并允许访问备份域寄存器。
```c
void BKP_Config(void) {
__HAL_RCC_PWR_CLK_ENABLE();
__HAL_RCC_BKP_CLK_ENABLE();
HAL_PWR_EnableBkUpAccess(); // 允许访问备份域
}
```
## 2. 配置 RTC 时钟源和闹钟
- 优先尝试 LSE,若 LSE 起振超时或运行中失效,则切换至 LSI。
```c
void RTC_Init(void) {
RTC_HandleTypeDef hrtc;
hrtc.Instance = RTC;
hrtc.Init.HourFormat = RTC_HOURFORMAT_24;
hrtc.Init.AsynchPrediv = 0x7F; // 128 分频
hrtc.Init.SynchPrediv = 0xFF; // 256 分频,产生 1Hz
hrtc.Init.OutPut = RTC_OUTPUT_DISABLE;
hrtc.Init.OutPutPolarity = RTC_OUTPUT_POLARITY_HIGH;
hrtc.Init.OutPutType = RTC_OUTPUT_TYPE_OPENDRAIN;
// 尝试使能 LSE
__HAL_RCC_LSE_CONFIG(RCC_LSE_ON);
uint32_t timeout = HAL_GetTick() + 2000; // 2 秒超时
while (__HAL_RCC_GET_FLAG(RCC_FLAG_LSERDY) == RESET) {
if (HAL_GetTick() >= timeout) {
// LSE 起振失败,切换至 LSI
__HAL_RCC_LSE_CONFIG(RCC_LSE_OFF);
__HAL_RCC_LSI_ENABLE();
while (__HAL_RCC_GET_FLAG(RCC_FLAG_LSIRDY) == RESET);
hrtc.Init.SynchPrediv = 0x1F; // 调整分频以适配 LSI 频率(约 32kHz)
break;
}
}
// 选择 RTC 时钟源
if (__HAL_RCC_GET_FLAG(RCC_FLAG_LSERDY) == SET) {
__HAL_RCC_RTC_CONFIG(RCC_RTCCLKSOURCE_LSE);
} else {
__HAL_RCC_RTC_CONFIG(RCC_RTCCLKSOURCE_LSI);
// 记录 LSE 失效标志到备份寄存器
HAL_RTCEx_BKUPWrite(&hrtc, RTC_BKP_DR0, 0xCAFE); // 自定义标志
}
__HAL_RCC_RTC_ENABLE();
HAL_RTC_Init(&hrtc);
// 配置闹钟 A(例如 10 秒后唤醒)
RTC_AlarmTypeDef sAlarm = {0};
sAlarm.AlarmTime.Hours = 0;
sAlarm.AlarmTime.Minutes = 0;
sAlarm.AlarmTime.Seconds = 10;
sAlarm.AlarmTime.SubSeconds = 0;
sAlarm.AlarmMask = RTC_ALARMMASK_NONE;
sAlarm.AlarmSubSecondMask = RTC_ALARMSUBSECONDMASK_ALL;
sAlarm.Alarm = RTC_ALARM_A;
HAL_RTC_SetAlarm_IT(&hrtc, &sAlarm, RTC_FORMAT_BIN);
}
```
## 3. 进入 STOP 模式并等待唤醒
- 在进入 STOP 前,确保 RTC 闹钟中断已使能,并配置 EXTI 线。
```c
void EnterStopMode(void) {
// 使能 RTC 闹钟中断
HAL_NVIC_SetPriority(RTC_Alarm_IRQn, 0, 0);
HAL_NVIC_EnableIRQ(RTC_Alarm_IRQn);
// 进入 STOP 模式
HAL_PWR_EnterSTOPMode(PWR_LOWPOWERREGULATOR_ON, PWR_STOPENTRY_WFI);
// 唤醒后重新配置系统时钟(因为 STOP 模式会关闭 HSI/HSE)
SystemClock_Config();
}
// 中断服务函数
void RTC_Alarm_IRQHandler(void) {
HAL_RTC_AlarmIRQHandler(&hrtc);
}
void HAL_RTC_AlarmAEventCallback(RTC_HandleTypeDef *hrtc) {
// 唤醒后检查备份寄存器
uint32_t flag = HAL_RTCEx_BKUPRead(hrtc, RTC_BKP_DR0);
if (flag == 0xCAFE) {
// LSE 失效,执行补救措施(如重新校准或记录日志)
// 此处可清除标志
HAL_RTCEx_BKUPWrite(hrtc, RTC_BKP_DR0, 0x0000);
}
// 正常唤醒处理
}
```
## 4. 完整示例代码
- 以下是一个简化但完整的示例,展示了从初始化到唤醒的完整流程。
```c
// main.c
RTC_HandleTypeDef hrtc;
int main(void) {
HAL_Init();
SystemClock_Config();
BKP_Config();
RTC_Init();
while (1) {
// 模拟低功耗运行
EnterStopMode();
// 唤醒后执行任务
HAL_Delay(100);
}
}
void SystemClock_Config(void) {
// 标准时钟配置,使用 HSI 或 HSE,此处省略
}
```
# 注意事项
- **备份寄存器访问**:必须先调用 `HAL_PWR_EnableBkUpAccess()`,否则写操作无效。
- **LSI 精度**:LSI 的精度较差,若需要精确时间,建议在 LSE 失效时定期校准或使用外部时间同步。
- **时钟切换时序**:在运行中检测到 LSE 失效时,切换时钟源需谨慎,确保 RTC 不会因时钟切换产生毛刺。建议在进入低功耗前完成切换。
- **备份寄存器掉电保持**:备份寄存器由 VBAT 供电,若 VBAT 也掉电,数据会丢失。设计时需考虑。
- **中断优先级**:RTC 闹钟中断优先级应高于其他低优先级中断,避免唤醒后响应延迟。
- **调试建议**:在开发阶段,可通过串口打印备份寄存器内容,验证 LSE 失效标志是否正常写入。
# 总结
通过利用 RTC 备份域寄存器,我们可以在 STM32F4 上实现 LSE 停振后的自动时钟源切换,并保存关键状态,从而保证系统在无外部晶振的情况下依然能够可靠唤醒。该方法不仅提高了系统的鲁棒性,还降低了对外部晶振的依赖,适用于工业控制、便携设备等对可靠性要求高的场景。希望本文能为你提供实用的参考。