STM32F4 嵌入式实战:LSE 停振后基于 HSI 的 RTC 自动校准与时间保持策略
👁 1 阅读 · 2026-08-27 · 嵌入式
在嵌入式系统中,RTC 依赖 LSE 晶振维持时间基准,但 LSE 易受温度、振动或焊接不良影响而停振,导致时间丢失。本文深入分析 STM32F4 的 RTC 时钟架构,提出一种在 LSE 失效时无缝切换至 HSI 并利用 RTC 校准寄存器实现自动补偿的方案,涵盖原理、配置步骤、完整代码及注意事项,帮助开发者构建高可靠性的时间保持系统。
# 引言
在工业控制、数据采集等嵌入式应用中,RTC(实时时钟)是记录事件时间戳的核心外设。STM32F4 系列通常采用 32.768kHz 的 LSE(低速外部晶振)作为 RTC 时钟源,其精度高、功耗低。然而,LSE 起振困难、易受干扰,一旦停振,RTC 将失去时钟源,导致时间停止或漂移。本文提出一种嵌入式解决方案:利用内部 HSI(高速内部振荡器)作为备用时钟,并通过 RTC 的同步/异步预分频器与校准寄存器,在 LSE 停振后自动切换并维持时间精度。
# 1. 原理剖析
## 1.1 RTC 时钟树
STM32F4 的 RTC 时钟源可选:LSE、LSI(低速内部)或 HSE/2 分频。其中 LSE 专为 RTC 设计,频率精确。RTC 内核时钟(RTCCLK)经过异步预分频(ASYNC_PRESCALER)和同步预分频(SYNC_PRESCALER)后产生 1Hz 的日历更新时钟。
- 典型配置:RTCCLK = 32.768kHz,ASYNC=127,SYNC=255,则输出 1Hz。
- 若改用 HSI(16MHz),需重新计算分频系数,且 HSI 精度(±1%)远低于 LSE(±20ppm),导致日误差可达秒级。
## 1.2 HSI 校准原理
STM32F4 的 RTC 校准寄存器(RTC_CALR)支持数字校准,通过每秒扣除或增加若干个 RTCCLK 周期来微调。校准值范围 -511~+512,对应调整量约为 -0.95ppm~+0.95ppm(以 32768Hz 为基准)。但 HSI 误差较大,仅靠 CALR 不足以补偿。因此需要结合软件校准:
- 测量 HSI 实际频率(通过 TIM 捕获或比较 LSI),计算误差。
- 动态调整 RTC 的同步预分频器(SYNC_PRESCALER)或使用 CALR 进行粗调+细调。
## 1.3 切换策略
当检测到 LSE 停振(通过 RTC_ISR 的 LSERDY 位或外部中断),立即将 RTC 时钟源切换为 HSI,并重新初始化分频器。同时启动校准流程:
- 使用 TIM5 通道 1 捕获 LSI(或已知频率信号)作为参考,测量 HSI 实际频率。
- 计算所需的分频系数与校准值,写入 RTC。
# 2. 硬件与软件准备
- 硬件:STM32F407 开发板,外部 32.768kHz 晶振,备用电池。
- 软件:STM32CubeIDE(HAL 库),或标准外设库。
- 关键外设:RTC、PWR、TIM5(用于频率测量)、GPIO。
# 3. 配置步骤
## 3.1 初始化 RTC(默认使用 LSE)
```c
void RTC_Init(void) {
__HAL_RCC_PWR_CLK_ENABLE();
__HAL_RCC_RTC_ENABLE();
HAL_PWR_EnableBkUpAccess();
RTC_HandleTypeDef hrtc = {0};
hrtc.Instance = RTC;
hrtc.Init.HourFormat = RTC_HOURFORMAT_24;
hrtc.Init.AsynchPrediv = 127;
hrtc.Init.SynchPrediv = 255;
hrtc.Init.OutPut = RTC_OUTPUT_DISABLE;
hrtc.Init.OutPutPolarity = RTC_OUTPUT_POLARITY_HIGH;
hrtc.Init.OutPutType = RTC_OUTPUT_TYPE_OPENDRAIN;
if (HAL_RTC_Init(&hrtc) != HAL_OK) {
Error_Handler();
}
}
```
## 3.2 检测 LSE 停振
```c
void Check_LSE_Status(void) {
// 检查 RTC 时钟源标志
if (__HAL_RTC_IS_LSE_ON(RTC) == 0) {
// LSE 停振,执行切换
Switch_RTC_Clock_To_HSI();
}
}
```
## 3.3 切换至 HSI 并校准
```c
void Switch_RTC_Clock_To_HSI(void) {
// 1. 使能 HSI
__HAL_RCC_HSI_ENABLE();
while (__HAL_RCC_GET_FLAG(RCC_FLAG_HSIRDY) == RESET);
// 2. 备份 RTC 当前时间(可选)
RTC_TimeTypeDef sTime;
RTC_DateTypeDef sDate;
HAL_RTC_GetTime(&hrtc, &sTime, RTC_FORMAT_BIN);
HAL_RTC_GetDate(&hrtc, &sDate, RTC_FORMAT_BIN);
// 3. 重新配置 RTC 时钟源为 HSI(注意:需先关闭 RTC 写保护)
HAL_PWR_EnableBkUpAccess();
__HAL_RCC_RTC_DISABLE();
__HAL_RCC_RTC_CONFIG(RCC_RTCCLKSOURCE_HSI_DIV8); // HSI/8 = 2MHz
__HAL_RCC_RTC_ENABLE();
// 4. 重新初始化 RTC,计算分频系数
// 目标 RTCCLK = 2MHz,要得到 1Hz,需要预分频 = 2,000,000 - 1
hrtc.Init.AsynchPrediv = 0x7F; // 127
hrtc.Init.SynchPrediv = 0xFFFF; // 65535,但实际用 2MHz/128/15625 ≈ 1Hz,需调整
// 更合理:ASYNC=124,SYNC=15999,则 2MHz/(125*16000)=1Hz
hrtc.Init.AsynchPrediv = 124;
hrtc.Init.SynchPrediv = 15999;
HAL_RTC_Init(&hrtc);
// 5. 恢复时间(需重新设置)
HAL_RTC_SetTime(&hrtc, &sTime, RTC_FORMAT_BIN);
HAL_RTC_SetDate(&hrtc, &sDate, RTC_FORMAT_BIN);
// 6. 启动校准:测量 HSI 实际频率
float actual_freq = Measure_HSI_Frequency();
float error_ppm = (actual_freq - 2000000.0f) / 2000000.0f * 1e6f;
// 计算校准值(CALR 范围 -511~512,对应 ±0.95ppm 每步)
int cal_value = (int)(error_ppm / 0.95f);
if (cal_value > 511) cal_value = 511;
if (cal_value < -511) cal_value = -511;
// 写入 CALR 寄存器(注意符号)
HAL_RTC_Calibration_SetValue(&hrtc, cal_value, RTC_CALIBRATION_SIGN_POSITIVE);
}
```
## 3.4 频率测量函数(基于 TIM5 捕获)
```c
float Measure_HSI_Frequency(void) {
// 配置 TIM5 输入捕获,测量 LSI(或外部 1Hz 信号)的周期
// 假设 LSI 为 32kHz,通过捕获得到 HSI 分频后的计数
// 简化:使用定时器测量 1 秒内 HSI 的脉冲数
TIM_HandleTypeDef htim5;
// ... 初始化 TIM5,通道1 捕获上升沿
// 启动捕获,记录两个上升沿之间的计数值
// 根据计数值计算频率
// 返回实际频率,例如 2,010,000 Hz
return 2010000.0f; // 示例值
}
```
# 4. 完整代码示例(关键部分)
```c
// 主循环中周期性检查 LSE 状态
while (1) {
Check_LSE_Status();
// 正常应用逻辑
HAL_Delay(1000);
}
// 中断方式:LSE 停振可触发 RTC 闹钟或外部中断
void EXTI_IRQHandler(void) {
if (__HAL_RTC_EXTI_GET_FLAG(RTC_EXTI_LINE_LSE) != RESET) {
__HAL_RTC_EXTI_CLEAR_FLAG(RTC_EXTI_LINE_LSE);
Switch_RTC_Clock_To_HSI();
}
}
```
# 5. 注意事项
- **切换时机**:在切换 RTC 时钟源前,务必保存当前时间,否则会丢失。
- **分频系数计算**:HSI 经 8 分频后为 2MHz,需重新计算预分频值,确保输出 1Hz。
- **校准精度**:HSI 随温度变化,建议定期(如每小时)重新测量并校准。
- **低功耗影响**:HSI 功耗高于 LSE,若系统对功耗敏感,可考虑在 LSE 恢复后切回。
- **备份域**:RTC 和备份寄存器由 VBAT 供电,切换时钟源不影响备份域数据。
- **测试验证**:实际测试中,LSE 停振模拟可通过断开晶振或强制清除 LSERDY 位实现。
# 6. 总结
本文提供了一套完整的嵌入式解决方案,使 STM32F4 在 LSE 停振后能自动切换至 HSI 并保持时间精度。通过合理配置分频器和校准寄存器,可将日误差控制在秒级以内。该方法适用于对成本敏感且环境恶劣的工业场景,显著提高了系统的鲁棒性。开发者可根据实际需求调整校准频率和测量算法,以平衡精度与资源消耗。