STM32F4 的 RTC 时间守护:LSE 停振后如何用 HSI 校准并保持精度
👁 1 阅读 · 2026-08-27 · 嵌入式
在嵌入式系统中,RTC 常依赖 LSE 晶振维持时间,但 LSE 易受环境干扰停振,导致时间丢失。本文深入剖析 STM32F4 的 RTC 时钟架构,提出一种基于 HSI 的自动校准方案:通过检测 LSE 故障,无缝切换至 HSI 并利用 RTC 的异步预分频器进行软件补偿,实现秒级精度保持。文章涵盖原理分析、寄存器配置、中断处理及完整代码示例,助你构建高可靠的时钟系统。
# 引言
在工业控制、物联网设备中,RTC(实时时钟)是时间戳、定时唤醒的核心。STM32F4 系列默认使用 LSE(32.768kHz 外部晶振)驱动 RTC,其精度高、功耗低。但 LSE 起振慢、抗振性差,在强电磁干扰或晶振老化时易停振。一旦 LSE 失效,RTC 若未配置备用时钟,将停止计时,造成系统时间错乱。本文介绍一种实用方案:利用内部 HSI(16MHz)作为备份时钟,并通过软件校准补偿频率偏差,确保时间连续且精度可控。
# 1. 原理剖析:RTC 时钟树与 LSE 故障机制
STM32F4 的 RTC 时钟源可选 LSE、LSI(约 32kHz)或 HSE 分频。RTC 内核使用 1Hz 的计数时钟,由异步预分频器(ASYNC_PRE)和同步预分频器(SYNC_PRE)对时钟源分频得到。典型配置:LSE 32.768kHz,ASYNC=127,SYNC=255,输出 1Hz。
LSE 停振时,硬件会置位 RTC_ISR 中的 LSERDY 位(若使能),但 RTC 不会自动切换时钟源。若继续使用 LSE,RTC 将停止更新。因此,需在软件中检测 LSE 故障,并切换至 HSI。HSI 为 16MHz,经 PWR 域的 RTC 时钟选择器(RTCSEL)可配置为 HSI/32(即 500kHz),再经预分频器得到 1Hz。但 HSI 精度(±1%)远低于 LSE(±20ppm),直接使用会导致日误差达 864 秒,必须校准。
# 2. 校准策略:基于 HSI 的软件补偿
HSI 频率固定,但受温度影响漂移。校准思路:在 LSE 正常时,测量 HSI 的实际频率(通过定时器捕获或 RTC 校准寄存器),计算偏差系数。当切换至 HSI 后,利用 RTC 的同步预分频器(可动态调整)或软件累加器进行补偿。
具体实现:
- 在 LSE 运行期间,使用 TIM5 的捕获通道测量 HSI 周期,得到实际频率 f_HSI_actual。
- 计算理想分频值:理想情况下,HSI/32=500kHz,预分频后 1Hz。实际频率偏差导致每秒计数偏差 Δ = f_HSI_actual/32 - 500000。
- 切换后,每秒软件增加或减少相应计数,或调整 SYNC_PRE 值(但 SYNC_PRE 只能在 RTC 初始化时设置,运行中修改需重新初始化,故采用软件补偿更灵活)。
# 3. 配置步骤
1. **使能 PWR 和 RTC 时钟**:
- 开启 PWR 时钟,进入备份域访问。
- 使能 RTC 时钟(通过 RCC_BDCR 寄存器)。
2. **检测 LSE 状态**:
- 读取 RTC_ISR 的 LSERDY 位,若为 0 表示 LSE 未就绪。
- 也可使能 LSE 中断(LSIE)以异步通知。
3. **切换时钟源至 HSI**:
- 修改 RCC_BDCR 的 RTCSEL 位为 10(HSI/32)。
- 注意:切换前需确保 RTC 已停止(通过 RTC_CR 的 BCE 位控制备份域写保护)。
4. **配置 RTC 预分频器**:
- 设置 ASYNC_PRE=127,SYNC_PRE=255(针对 500kHz 输入)。
- 若 HSI 实际频率有偏差,可微调 SYNC_PRE,但需重新初始化 RTC。
5. **软件补偿**:
- 在秒中断中,根据校准系数调整 RTC 计数(通过 RTC_SSR 或软件变量)。
# 4. 完整代码示例
以下代码基于 STM32F407,使用 HAL 库,实现 LSE 故障检测、切换 HSI 及软件补偿。
```c
// 全局变量
volatile uint32_t g_rtc_second_count = 0;
volatile uint8_t g_lse_failure = 0;
float g_hsi_calibration = 1.0f; // 校准系数,实际频率/理想频率
// LSE 故障中断回调(在 RTC_IRQHandler 中调用)
void HAL_RTC_LSE_ErrorCallback(RTC_HandleTypeDef *hrtc) {
g_lse_failure = 1;
// 切换时钟源至 HSI
__HAL_RCC_RTC_CONFIG(RCC_RTCCLKSOURCE_HSI_DIV32);
// 重新初始化 RTC 预分频器
RTC_TimeTypeDef sTime = {0};
RTC_DateTypeDef sDate = {0};
HAL_RTC_GetTime(hrtc, &sTime, RTC_FORMAT_BIN);
HAL_RTC_GetDate(hrtc, &sDate, RTC_FORMAT_BIN);
// 停止 RTC
HAL_RTC_DeInit(hrtc);
// 重新配置
RTC_InitTypeDef init = {0};
init.HourFormat = RTC_HOURFORMAT_24;
init.AsynchPrediv = 127;
init.SynchPrediv = 255;
init.OutPut = RTC_OUTPUT_DISABLE;
HAL_RTC_Init(hrtc);
// 设置时间
HAL_RTC_SetTime(hrtc, &sTime, RTC_FORMAT_BIN);
HAL_RTC_SetDate(hrtc, &sDate, RTC_FORMAT_BIN);
}
// 秒中断回调(用于软件补偿)
void HAL_RTCEx_RTCEventCallback(RTC_HandleTypeDef *hrtc) {
g_rtc_second_count++;
if (g_lse_failure) {
// 补偿:每秒根据校准系数调整计数
// 例如,若实际频率偏高,则每秒多计数 0.001 秒,通过软件累加
static float error_accum = 0.0f;
error_accum += (g_hsi_calibration - 1.0f);
if (error_accum >= 1.0f) {
// 增加 1 秒(通过设置 RTC 的 SSR 或直接修改时间)
RTC_TimeTypeDef sTime;
HAL_RTC_GetTime(hrtc, &sTime, RTC_FORMAT_BIN);
sTime.Seconds++;
if (sTime.Seconds >= 60) sTime.Seconds = 0;
HAL_RTC_SetTime(hrtc, &sTime, RTC_FORMAT_BIN);
error_accum -= 1.0f;
}
}
}
// 初始化时测量 HSI 频率(示例:使用 TIM5 捕获)
void Measure_HSI_Frequency(void) {
// 配置 TIM5 输入捕获,测量 HSI/32 信号(需外部连接或内部路由)
// 此处省略具体配置,假设得到实际频率 f_actual
float f_actual = 500123.0f; // 示例值
g_hsi_calibration = f_actual / 500000.0f;
}
// 主程序初始化
int main(void) {
HAL_Init();
// 使能 PWR 时钟,允许访问备份域
__HAL_RCC_PWR_CLK_ENABLE();
HAL_PWR_EnableBkUpAccess();
// 使能 LSE 并等待就绪
__HAL_RCC_LSE_CONFIG(RCC_LSE_ON);
while (__HAL_RCC_GET_FLAG(RCC_FLAG_LSERDY) == RESET) {
// 超时处理
}
// 配置 RTC 时钟源为 LSE
__HAL_RCC_RTC_CONFIG(RCC_RTCCLKSOURCE_LSE);
__HAL_RCC_RTC_ENABLE();
// 初始化 RTC(略)
// 使能 LSE 错误中断
HAL_RTCEx_SetLSECallback(HAL_RTC_LSE_ErrorCallback);
// 测量 HSI 校准系数
Measure_HSI_Frequency();
// 主循环
while (1) {
// 应用逻辑
}
}
```
# 5. 注意事项
- **备份域写保护**:修改 RTC 时钟源前,必须关闭备份域写保护(通过 PWR_CR 的 DBP 位),否则操作无效。
- **切换时机**:LSE 停振后,RTC 可能已停止,切换前需保存当前时间,避免丢失。
- **校准精度**:HSI 受温度影响,建议定期在 LSE 正常时重新测量校准系数,并存储到备份寄存器。
- **中断优先级**:LSE 错误中断应设为高优先级,确保及时响应。
- **测试验证**:在强干扰环境下测试,观察时间误差,必要时调整补偿算法(如使用 PID)。
# 结语
通过上述方案,STM32F4 在 LSE 失效时能自动切换至 HSI,并借助软件校准保持时间精度,极大提升了系统可靠性。该方法适用于对成本敏感、无法使用外部高精度晶振的场景。开发者可根据实际需求优化补偿算法,实现更精准的计时。