STM32F4 无晶振秒中断:LSE 停振后利用 RTC 备份域寄存器的可靠实现
👁 1 阅读 · 2026-08-27 · 嵌入式
在 STM32F4 系列中,LSE(低速外部晶振)常被用于 RTC 以产生秒中断,但晶振失效或未焊接时系统会陷入瘫痪。本文深入探讨 LSE 停振检测机制,并给出一种基于 RTC 备份域寄存器的无外部晶振秒中断方案,通过切换至 LSI 并利用备份寄存器保存校准参数,实现高可靠、低成本的秒中断。文章包含原理分析、寄存器配置步骤、完整代码示例及工程注意事项,适合嵌入式开发者直接落地。
# STM32F4 无晶振秒中断:LSE 停振后利用 RTC 备份域寄存器的可靠实现
## 1. 问题背景与挑战
在工业控制或低功耗设备中,STM32F4 常依赖 LSE(32.768kHz 晶振)驱动 RTC 产生秒中断。然而,LSE 可能因焊接不良、环境振动或温度漂移而停振。一旦停振,RTC 时钟源失效,秒中断消失,系统时间基准崩溃。传统方案是直接切换至 LSI(内部低速 RC,约 32kHz),但 LSI 精度差(典型 ±5%),导致秒中断误差累积。更棘手的是,RTC 的时钟源选择寄存器(RCC_BDCR)位于备份域,一旦 LSE 停振,写操作可能被锁定。
本文提出一种策略:利用 RTC 备份域寄存器(BKPxR)保存 LSI 校准参数,并在 LSE 停振后自动切换至 LSI,同时通过备份寄存器记录切换状态,实现无外部晶振的可靠秒中断。该方案无需额外硬件,仅依赖 STM32F4 内置资源。
## 2. 原理剖析:RTC 备份域与时钟源管理
### 2.1 备份域架构
STM32F4 的备份域包括 RTC、备份寄存器(20 个 32 位 BKPxR)和备份 SRAM(4KB,仅 F4 系列部分型号)。这些资源由 VBAT 供电,当主电源掉电时仍保持数据。RTC 时钟源可选 HSE/128、LSI 或 LSE,通过 RCC_BDCR 寄存器的 RTCSEL[1:0] 位配置。
### 2.2 LSE 停振检测
LSE 停振可通过 RCC_BDCR 的 LSERDY 位监测。当该位为 0 时,表示 LSE 未就绪。但仅靠轮询不够,因为 LSE 可能瞬间抖动。更可靠的方法是启用 LSE 的旁路模式(LSEBYP=1),将 LSE 引脚配置为外部时钟输入,但本文聚焦无外部晶振场景,故采用软件检测:连续多次读取 LSERDY,若持续为 0 则判定停振。
### 2.3 关键寄存器操作
- **RCC_BDCR**:备份域控制寄存器,需先解除写保护(PWR_CR 的 DBP 位)。
- **RTC_CR**:RTC 控制寄存器,包含时钟源选择(通过 RTCSEL 在 RCC_BDCR 中)和中断使能。
- **BKPxR**:备份寄存器,用于存储校准值和状态标志。
## 3. 实现方案设计
### 3.1 总体流程
1. 系统初始化时,尝试启用 LSE,并等待就绪。
2. 若 LSE 就绪,则使用 LSE 作为 RTC 时钟,并周期性测量 LSI 频率(通过定时器或 RTC 校准),将校准系数存入 BKP0R。
3. 若 LSE 未就绪或运行中停振,则切换 RTC 时钟源至 LSI,并从 BKP0R 读取校准系数,用于软件补偿秒中断周期。
4. 在备份寄存器 BKP1R 中记录当前时钟源状态(0=LSE,1=LSI)。
### 3.2 校准原理
LSI 频率不精确,但可通过测量其相对 LSE 的偏差来校准。当 LSE 正常时,利用 RTC 的亚秒寄存器或外部定时器,测量 LSI 在 1 秒内的实际计数,得到校准因子。例如,若 LSI 实际频率为 32.1kHz,则 1 秒计数为 32100,而理想为 32000,校准因子为 32100/32000=1.003125。切换至 LSI 后,每次秒中断产生时,软件调整下一次中断的预分频值。
## 4. 配置步骤与代码实现
### 4.1 初始化备份域访问
```c
void BKP_Enable(void) {
__HAL_RCC_PWR_CLK_ENABLE();
HAL_PWR_EnableBkUpAccess(); // 解除备份域写保护
}
```
### 4.2 LSE 检测与切换逻辑
```c
uint8_t CheckAndSwitchRTCClock(void) {
// 检查 LSE 状态
if (READ_BIT(RCC->BDCR, RCC_BDCR_LSERDY) == 0) {
// LSE 停振,切换至 LSI
RCC->BDCR &= ~RCC_BDCR_RTCSEL; // 清除选择位
RCC->BDCR |= RCC_BDCR_RTCSEL_1; // 选择 LSI (RTCSEL=10)
// 读取校准系数(若存在)
uint32_t calib = BKP->BKP0R;
if (calib != 0xFFFFFFFF) {
// 应用校准系数到 RTC 预分频器
RTC->PRER = (RTC->PRER & ~RTC_PRER_PREDIV_S) | ((calib & 0xFFFF) << 16);
}
// 记录状态
BKP->BKP1R = 1;
return 1;
} else {
// LSE 正常,使用 LSE,并校准 LSI
CalibrateLSI();
BKP->BKP1R = 0;
return 0;
}
}
```
### 4.3 LSI 校准函数(在 LSE 正常时调用)
```c
void CalibrateLSI(void) {
// 使用 TIM5 测量 LSI 频率(假设 TIM5 时钟来自 LSE,或使用 RTC 亚秒)
// 简化示例:利用 RTC 的 1Hz 中断,计数 LSI 的脉冲数
// 实际实现需配置 TIM 输入捕获或使用 LSI 作为 TIM 时钟
// 此处仅示意,假设测得频率为 32000 + offset
uint32_t measured_freq = 32100; // 示例值
uint32_t prediv_s = (measured_freq / 2) - 1; // 异步预分频,产生 1Hz
BKP->BKP0R = prediv_s; // 保存校准值
}
```
### 4.4 秒中断配置(以 LSI 为例)
```c
void RTC_Init_With_LSI(void) {
// 使能 PWR 和备份域
BKP_Enable();
// 使能 LSI
RCC->CSR |= RCC_CSR_LSION;
while ((RCC->CSR & RCC_CSR_LSIRDY) == 0);
// 选择 LSI 作为 RTC 时钟
RCC->BDCR |= RCC_BDCR_RTCSEL_1; // RTCSEL=10
RCC->BDCR |= RCC_BDCR_RTCEN; // 使能 RTC
// 等待 RTC 同步
RTC->ISR |= RTC_ISR_INIT;
while ((RTC->ISR & RTC_ISR_INITF) == 0);
// 配置预分频:LSI 约 32kHz,异步预分频 127,同步预分频 255,得到 1Hz
RTC->PRER = (127 << 16) | 255;
// 退出初始化模式
RTC->ISR &= ~RTC_ISR_INIT;
// 使能秒中断
RTC->CR |= RTC_CR_TSIE;
NVIC_EnableIRQ(RTC_Alarm_IRQn); // 或 RTC_WKUP_IRQn,取决于配置
}
```
### 4.5 中断服务函数
```c
void RTC_Alarm_IRQHandler(void) {
if (RTC->ISR & RTC_ISR_TSF) {
RTC->ISR &= ~RTC_ISR_TSF; // 清除标志
// 秒中断处理:可读取 BKP1R 判断时钟源,并做时间补偿
uint8_t src = BKP->BKP1R;
if (src == 1) {
// LSI 模式,可进行软件补偿(例如调整下一次中断时间)
}
// 用户代码:翻转 LED 等
}
}
```
## 5. 注意事项与优化建议
- **备份域写保护**:任何对 RCC_BDCR 或 BKPxR 的写操作前,必须设置 PWR_CR 的 DBP=1,否则写无效。
- **LSE 抖动处理**:检测 LSE 停振时,建议连续读取 LSERDY 至少 10 次,每次间隔 1ms,避免误判。
- **校准系数更新**:在 LSE 正常时,定期(如每小时)重新校准 LSI,并更新 BKP0R,以应对温度漂移。
- **备份寄存器数量**:BKP0R 和 BKP1R 是 32 位,但只有低 16 位有效(部分型号),注意掩码。
- **低功耗模式**:若进入待机模式,备份域保持供电,但 LSI 可能关闭,需在唤醒后重新初始化。
- **调试建议**:使用逻辑分析仪观察秒中断引脚,对比 LSE 和 LSI 模式下的周期误差。
## 6. 总结
本文通过利用 STM32F4 的备份域寄存器,实现了 LSE 停振后无缝切换至 LSI 的秒中断方案。核心在于提前校准 LSI 并存储参数,切换时快速恢复精度。该方法无需外部晶振,成本低且可靠性高,适用于对时间精度要求不极端(误差 <1%)的嵌入式应用。开发者可根据实际需求扩展校准算法,例如使用 RTC 的 tamper 检测或外部高精度时钟源。