STM32F4 在 LSE 停振后如何利用 HSI 校准 RTC 并保持时间精度
👁 1 阅读 · 2026-08-27 · 嵌入式
在 STM32F4 系列 MCU 中,RTC 通常依赖外部低速晶振 LSE 提供精确时钟,但在恶劣环境或晶振故障时 LSE 可能停振,导致 RTC 时间漂移甚至失效。本文深入探讨 LSE 停振的检测机制,并给出一种利用内部 HSI 振荡器校准 RTC 的实用方案,通过软件补偿实现时间精度的保持。文章涵盖原理分析、配置步骤、完整代码示例及注意事项,帮助开发者应对这一嵌入式常见挑战。
# 引言
在嵌入式系统中,RTC(实时时钟)是许多应用的核心组件,尤其对于需要时间戳或定时唤醒的设备。STM32F4 的 RTC 通常由外部 32.768 kHz 晶振(LSE)驱动,其精度高、功耗低。然而,LSE 可能因焊接不良、振动或温度变化而停振,导致 RTC 失去时钟源。此时,若未做处理,RTC 将停止计数,时间精度荡然无存。本文将介绍如何检测 LSE 停振,并利用内部 HSI 振荡器(16 MHz)作为备用时钟源,通过软件校准维持 RTC 的时间精度。
# 原理分析
## LSE 停振检测
STM32F4 的 RTC 模块提供了 LSE 时钟安全系统(LSE CSS),通过 RTC_ISR 寄存器的 LSECSSD 位检测 LSE 故障。启用后,若 LSE 振幅异常,硬件会置位该标志并触发中断。但注意,LSE CSS 需要 LSE 处于旁路或启用状态,且仅在部分系列支持(如 F4 全系支持)。
## HSI 作为备用时钟
当 LSE 停振时,RTC 可切换至 HSI 时钟(经分频后)。HSI 频率为 16 MHz,精度约 ±1%,远低于 LSE,但足以维持计数。关键在于校准:通过测量 HSI 与 LSE 的频率差(在 LSE 正常时),或利用外部参考信号(如 GPS 秒脉冲)进行软件校准,补偿频率偏差。
## 校准原理
RTC 的日历计数器以 1 Hz 递增,若时钟源频率偏差,实际时间会漂移。校准思路:
- 在 LSE 正常时,测量 HSI 的实际频率(通过定时器捕获或比较)。
- 当 LSE 停振后,使用 HSI 驱动 RTC,并周期性地根据校准值调整 RTC 的亚秒寄存器(RTC_SSR)或使用 RTC 的同步移位功能(RTC_SHIFTR)进行微调。
- 更简单的方法:利用 RTC 的闹钟或唤醒定时器,在固定周期内补偿累计误差。
# 配置步骤
## 1. 启用 LSE CSS 并配置中断
```c
// 启用 LSE CSS 中断
RTC_ITConfig(RTC_IT_LSECSS, ENABLE);
NVIC_EnableIRQ(RTC_IRQn);
```
在中断处理函数中,检测 LSECSSD 标志,并切换时钟源。
## 2. 测量 HSI 频率(校准数据获取)
在系统初始化时,若 LSE 正常,利用定时器测量 HSI 的实际频率。例如,使用 TIM5 的输入捕获模式,捕获 LSE 的 1 Hz 信号(通过 MCO 输出),计算 HSI 周期。
```c
// 伪代码:测量 HSI 频率
uint32_t measure_hsi_freq(void) {
// 配置 TIM5 捕获 LSE 信号,计数 HSI 周期数
// 返回 HSI 实际频率(如 16000000 ± 偏差)
}
```
## 3. 切换 RTC 时钟源至 HSI
当检测到 LSE 停振后,需重新初始化 RTC 时钟源。注意,RTC 时钟源切换需在备份域复位后执行。
```c
void switch_rtc_clock_to_hsi(void) {
// 1. 进入备份域配置
PWR_BackupAccessCmd(ENABLE);
RCC_BackupResetCmd(ENABLE); // 复位备份域,但会丢失 RTC 数据
RCC_BackupResetCmd(DISABLE);
// 2. 使能 HSI 并选择为 RTC 时钟
RCC_HSICmd(ENABLE);
while(RCC_GetFlagStatus(RCC_FLAG_HSIRDY) == RESET);
RCC_RTCCLKConfig(RCC_RTCCLKSource_HSI_Div8); // HSI/8 = 2 MHz
// 3. 使能 RTC
RCC_RTCCLKCmd(ENABLE);
RTC_WaitForSynchro();
}
```
注意:备份域复位会清除 RTC 寄存器,因此需在切换前保存时间信息(如通过备份寄存器)。
## 4. 软件校准补偿
由于 HSI 精度有限,需周期性校准。利用 RTC 的同步移位功能(RTC_SHIFTR)可调整亚秒值,实现微调。
```c
void rtc_calibrate(int32_t ppm) {
// 计算移位值:每个移位单位对应 1/32768 秒(约 30.5 us)
// 假设 ppm 为百万分之一,则每秒误差为 ppm/1e6 秒
// 移位值 = (ppm * 32768) / 1e6
uint32_t shift = (uint32_t)((ppm * 32768) / 1000000);
RTC_SynchroShiftConfig(shift, 0); // 正值为加,负值为减(通过方向位)
}
```
实际应用中,可定期(如每分钟)根据外部参考(如网络时间)计算误差,并调用校准函数。
# 完整代码示例
以下代码演示了 LSE 停振检测、时钟切换和简单校准流程。
```c
#include "stm32f4xx.h"
// 全局变量保存时间
volatile uint32_t g_rtc_time = 0;
// LSE CSS 中断处理
void RTC_IRQHandler(void) {
if (RTC_GetITStatus(RTC_IT_LSECSS) != RESET) {
RTC_ClearITPendingBit(RTC_IT_LSECSS);
// 保存当前时间(从 RTC 读取)
g_rtc_time = RTC_GetCounter();
// 切换至 HSI
switch_rtc_clock_to_hsi();
// 重新设置时间(从备份寄存器恢复)
RTC_SetCounter(g_rtc_time);
// 启用校准定时器(如每 10 秒校准一次)
start_calibration_timer();
}
}
// 初始化 RTC 并启用 LSE CSS
void rtc_init(void) {
// 使能 PWR 和备份域时钟
RCC_APB1PeriphClockCmd(RCC_APB1Periph_PWR, ENABLE);
PWR_BackupAccessCmd(ENABLE);
RCC_BackupResetCmd(ENABLE);
RCC_BackupResetCmd(DISABLE);
// 启用 LSE
RCC_LSEConfig(RCC_LSE_ON);
while (RCC_GetFlagStatus(RCC_FLAG_LSERDY) == RESET);
// 选择 LSE 作为 RTC 时钟
RCC_RTCCLKConfig(RCC_RTCCLKSource_LSE);
RCC_RTCCLKCmd(ENABLE);
RTC_WaitForSynchro();
// 配置 RTC 中断
RTC_ITConfig(RTC_IT_LSECSS, ENABLE);
NVIC_EnableIRQ(RTC_IRQn);
// 设置初始时间(示例)
RTC_SetCounter(0);
}
// 校准函数:根据误差 ppm 调整 RTC
void rtc_calibrate(int32_t ppm) {
uint32_t shift = (uint32_t)((ppm * 32768) / 1000000);
// 注意:RTC_SHIFTR 的 SUBFS 字段,ADD1S 位用于方向
RTC_SynchroShiftConfig(shift, 0);
}
// 定时器中断(假设每 10 秒触发)
void TIM_IRQHandler(void) {
// 此处可根据外部参考计算误差,并调用 rtc_calibrate
// 例如:误差 = (实际时间 - RTC时间) * 1e6 / 实际时间
}
int main(void) {
// 初始化系统时钟(HSI 作为系统时钟)
SystemInit();
// 初始化 RTC
rtc_init();
while (1) {
// 主循环
}
}
```
# 注意事项
- **备份域复位风险**:切换 RTC 时钟源时,备份域复位会清除所有备份寄存器,务必在切换前保存关键数据(如时间戳)到非易失存储或备份寄存器(但备份寄存器也会被清,所以需提前保存到 Flash 或 EEPROM)。
- **校准精度**:HSI 的精度受温度影响较大,建议在系统运行时定期校准,而非一次性校准。
- **中断优先级**:LSE CSS 中断应设置为高优先级,确保及时响应,避免时间丢失。
- **功耗考虑**:HSI 功耗高于 LSE,若系统对功耗敏感,需权衡。
- **软件补偿限制**:RTC 的同步移位功能只能调整亚秒级,若误差较大(如 HSI 偏差超过 1%),需考虑更复杂的算法,如周期性重置计数器。
# 总结
通过 LSE CSS 检测停振,并利用 HSI 作为备用时钟源,结合软件校准,可以在 LSE 故障时维持 RTC 的时间精度。虽然 HSI 精度有限,但通过定期校准,大多数应用可接受。该方案为嵌入式系统提供了可靠的时钟冗余设计,尤其适用于工业或户外环境。希望本文能帮助开发者应对类似挑战。