STM32F4 无外部晶振的秒级唤醒:LSE 停振后 RTC 备份域寄存器的妙用
👁 1 阅读 · 2026-08-27 · 嵌入式
在低功耗嵌入式设计中,LSE 晶振停振常导致 RTC 失效,进而影响定时唤醒功能。本文深入剖析 STM32F4 的 RTC 备份域寄存器机制,提出一种在无外部晶振条件下,利用备份域寄存器实现秒级唤醒的软硬件协同方案。文章涵盖原理分析、配置步骤、完整代码示例及工程注意事项,帮助开发者应对晶振失效场景,提升系统鲁棒性。
# 引言
在电池供电的物联网设备中,STM32F4 常被用于需要周期性唤醒的低功耗任务。通常,RTC 依赖外部低速晶振(LSE,32.768kHz)提供精准时钟,但在高湿度、强振动或低温环境下,LSE 可能停振,导致 RTC 无法计时,系统陷入死循环或无法唤醒。本文提出一种备用方案:当检测到 LSE 停振时,切换至内部低速时钟(LSI),并利用 RTC 备份域寄存器保存唤醒计数,实现无外部晶振的秒级唤醒。
# 原理剖析
## RTC 与备份域
STM32F4 的 RTC 模块位于备份域(Backup Domain),由 VBAT 引脚供电,当主电源掉电时,RTC 仍能运行。备份域包含 20 个 32 位备份寄存器(BKPxR),用于在复位或掉电后保存用户数据。RTC 时钟源可选 LSE、LSI 或 HSE/128,其中 LSI 约 32kHz,精度较低但无需外部晶振。
## LSE 停振检测
RTC 提供时钟安全系统(CSS)功能,可监测 LSE 振荡器。当 LSE 失效时,CSS 产生中断,系统可及时响应。然而,CSS 仅适用于 LSE,且需在初始化时使能。若未使能,则需通过轮询 RTC_ISR 的 LSERDY 位检测。
## 备份域寄存器的作用
备份寄存器在系统复位(包括软件复位)后内容保持不变,但若备份域电源丢失(VBAT 掉电),则数据清零。利用这一特性,我们可在 LSE 停振时,将 RTC 计数周期切换为 LSI,并在备份寄存器中存储唤醒次数或时间戳,实现秒级唤醒的连续性。
# 实现方案
## 硬件设计
- MCU:STM32F407VET6(或其他 F4 系列)
- 外部晶振:LSE(32.768kHz)和 HSE(8MHz)
- VBAT 接 3V 纽扣电池或超级电容
## 软件架构
1. 初始化 RTC 使用 LSE,并使能 CSS 中断。
2. 在 CSS 中断服务函数中,检测到 LSE 失效,切换时钟源至 LSI,并设置唤醒周期为 1 秒。
3. 使用备份寄存器 RTC_BKPxR 存储唤醒计数,每次唤醒后递增。
4. 主程序进入低功耗模式(STOP 模式),RTC 唤醒中断触发后恢复运行。
## 配置步骤
### 1. 使能电源和备份域时钟
```c
void RTC_Init(void) {
RCC_APB1PeriphClockCmd(RCC_APB1Periph_PWR, ENABLE);
PWR_BackupAccessCmd(ENABLE); // 允许访问备份域
RCC_BackupResetCmd(ENABLE); // 复位备份域(可选)
RCC_BackupResetCmd(DISABLE);
}
```
### 2. 配置 LSE 和 RTC
```c
void RTC_Config_LSE(void) {
RCC_LSEConfig(RCC_LSE_ON);
while (RCC_GetFlagStatus(RCC_FLAG_LSERDY) == RESET); // 等待 LSE 就绪
RCC_RTCCLKConfig(RCC_RTCCLKSource_LSE);
RCC_RTCCLKCmd(ENABLE);
RTC_WaitForSynchro();
// 设置 1 秒中断
RTC_ITConfig(RTC_IT_WUT, ENABLE);
RTC_WakeUpClockConfig(RTC_WakeUpClock_CK_SPRE_16bits); // 1Hz
RTC_SetWakeUpCounter(0); // 1 秒后唤醒
}
```
### 3. 使能 CSS 中断
```c
void CSS_Init(void) {
RCC_LSEConfig(RCC_LSE_ON);
RCC_ITConfig(RCC_IT_LSECSS, ENABLE);
NVIC_InitTypeDef NVIC_InitStructure;
NVIC_InitStructure.NVIC_IRQChannel = RCC_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
}
```
### 4. CSS 中断处理函数
```c
void RCC_IRQHandler(void) {
if (RCC_GetITStatus(RCC_IT_LSECSS) != RESET) {
RCC_ClearITPendingBit(RCC_IT_LSECSS);
// 切换至 LSI
RCC_LSICmd(ENABLE);
while (RCC_GetFlagStatus(RCC_FLAG_LSIRDY) == RESET);
RCC_RTCCLKConfig(RCC_RTCCLKSource_LSI);
// 重新配置唤醒周期(LSI 频率约 32kHz,需校准)
RTC_WakeUpClockConfig(RTC_WakeUpClock_CK_SPRE_16bits); // 使用 1Hz 分频,但 LSI 不精确,需软件校准
RTC_SetWakeUpCounter(0);
// 在备份寄存器中标记 LSE 失效
RTC_WriteBackupRegister(RTC_BKP_DR0, 0xA5A5);
}
}
```
### 5. 备份寄存器读写函数
```c
uint32_t ReadWakeCounter(void) {
return RTC_ReadBackupRegister(RTC_BKP_DR1);
}
void WriteWakeCounter(uint32_t cnt) {
RTC_WriteBackupRegister(RTC_BKP_DR1, cnt);
}
```
### 6. 主程序与低功耗模式
```c
int main(void) {
RTC_Init();
CSS_Init();
RTC_Config_LSE();
// 初始化备份寄存器
if (ReadWakeCounter() == 0xFFFFFFFF) WriteWakeCounter(0);
while (1) {
// 进入 STOP 模式,RTC 唤醒
PWR_EnterSTOPMode(PWR_Regulator_LowPower, PWR_STOPEntry_WFI);
// 唤醒后执行任务
uint32_t cnt = ReadWakeCounter();
cnt++;
WriteWakeCounter(cnt);
// 根据 cnt 执行不同任务
}
}
```
# 注意事项
- **LSI 精度问题**:LSI 频率受温度影响,误差可达 10% 以上。若需精确秒级唤醒,可在初始化时测量 LSI 实际频率(通过定时器捕获),并动态调整唤醒计数器值。
- **备份域复位**:在开发调试时,若频繁复位,备份寄存器内容可能保留,导致逻辑混乱。建议在初始化时检查备份寄存器标志(如 DR0 中的魔数),若无效则清零。
- **CSS 中断优先级**:CSS 中断应设为最高优先级,确保 LSE 失效时能及时切换,避免系统挂起。
- **唤醒后时钟源**:切换至 LSI 后,RTC 精度降低,若应用需要精确时间,可考虑使用 HSE/128 作为备用,但 HSE 在 STOP 模式下可能关闭,需权衡。
- **功耗考虑**:LSI 功耗略高于 LSE,但差异不大。在 STOP 模式下,RTC 仍运行,功耗约 1μA 左右。
# 总结
本文通过利用 STM32F4 的备份域寄存器和 CSS 机制,实现了 LSE 停振后的无缝切换,确保秒级唤醒功能不中断。该方法无需额外硬件,仅通过软件配置即可提升系统可靠性,适用于对成本敏感且环境恶劣的嵌入式应用。开发者可根据实际需求扩展,如增加时间校准算法或使用外部 RTC 芯片作为冗余。