STM32F4 LSE 停振无感恢复:硬件 RTC 备份域时间戳补偿实战

1. 问题背景与原理

LSE(低速外部晶振,32.768kHz)是 STM32F4 RTC 的常用时钟源,其精度高、温漂小。但 LSE 对 PCB 布局敏感,且易受 EMI 干扰,导致停振(LSE Oscillator Failure)。停振后,若 RTC 仍配置为 LSE,则时间停止更新,系统时间戳出现“冻结”或跳变。

核心机制

  • RTC 备份域(Backup Domain)包含 RTC 寄存器、备份寄存器(BKP)和 20×32 位备份 RAM,由 VBAT 供电,独立于主电源。
  • RTC_ISR 寄存器的 LSERDY 位反映 LSE 就绪状态,LSEDRV[1:0] 可调整驱动能力。
  • 当 LSE 停振时,硬件自动将 RTC 时钟源切换至 LSI(约 32kHz,精度差),但时间戳会因频率偏差产生累计误差。

无感恢复策略

  1. 检测 LSE 停振(轮询 LSERDY 或利用 RTC_Alarm 中断)。
  2. 切换至 LSI 并记录切换时刻的 RTC 计数(备份域保存)。
  3. 在 LSE 恢复后,计算 LSI 运行期间的偏移量,通过 RTC 校准寄存器(RTC_CALR)或软件补偿修正时间戳。

2. 硬件与软件准备

  • 硬件:STM32F407 开发板(或任意 F4 系列),外部 32.768kHz 晶振,VBAT 接 3V 电池。
  • 软件:STM32CubeIDE(或 Keil),HAL 库。

3. 配置步骤

3.1 使能备份域访问与 LSE

void RTC_Init(void)
{
    __HAL_RCC_PWR_CLK_ENABLE();
    __HAL_RCC_BKP_CLK_ENABLE();
    HAL_PWR_EnableBkUpAccess();  // 允许访问备份域

    // 启用 LSE 并等待就绪
    RCC_OscInitTypeDef osc = {0};
    osc.OscillatorType = RCC_OSCILLATORTYPE_LSE;
    osc.LSEState = RCC_LSE_ON;
    if (HAL_RCC_OscConfig(&osc) != HAL_OK) {
        // 处理错误,可能 LSE 未焊接或损坏
    }

    // 配置 RTC 时钟源为 LSE
    RCC_PeriphCLKInitTypeDef periph = {0};
    periph.PeriphClockSelection = RCC_PERIPHCLK_RTC;
    periph.RTCClockSelection = RCC_RTCCLKSOURCE_LSE;
    HAL_RCCEx_PeriphCLKConfig(&periph);

    // 使能 RTC
    __HAL_RCC_RTC_ENABLE();
}

3.2 检测 LSE 停振与切换 LSI

利用 RTC 唤醒定时器或主循环轮询 LSERDY。推荐使用 RTC 闹钟中断(每秒触发)来检查,避免阻塞。

void Check_LSE_Status(void)
{
    if (__HAL_RCC_GET_FLAG(RCC_FLAG_LSERDY) == RESET) {
        // LSE 停振!
        // 1. 保存当前 RTC 计数到备份寄存器
        uint32_t current_cnt = HAL_RTC_GetCounter(&hrtc);
        HAL_RTCEx_BKUPWrite(&hrtc, RTC_BKP_DR0, current_cnt);

        // 2. 切换 RTC 时钟源到 LSI
        RCC_PeriphCLKInitTypeDef periph = {0};
        periph.PeriphClockSelection = RCC_PERIPHCLK_RTC;
        periph.RTCClockSelection = RCC_RTCCLKSOURCE_LSI;
        HAL_RCCEx_PeriphCLKConfig(&periph);

        // 3. 标记停振状态(备份寄存器位)
        HAL_RTCEx_BKUPWrite(&hrtc, RTC_BKP_DR1, 0xCAFE);
    }
}

3.3 LSE 恢复与时间戳补偿

当 LSE 重新就绪时,需要计算 LSI 运行期间的误差。LSI 频率典型值 32kHz,但偏差可达 ±5%。补偿方法:

  • 记录 LSI 运行时长(通过 RTC 计数差值),乘以频率偏差系数(可预校准)。
  • 或使用 RTC 校准寄存器(RTC_CALR)进行硬件校准,但该校准仅适用于 LSE 模式。

软件补偿示例:

void LSE_Recovery_Compensation(void)
{
    if (__HAL_RCC_GET_FLAG(RCC_FLAG_LSERDY) == SET) {
        // 检查是否曾停振(备份寄存器标记)
        if (HAL_RTCEx_BKUPRead(&hrtc, RTC_BKP_DR1) == 0xCAFE) {
            // 获取 LSI 运行期间的计数增量
            uint32_t saved_cnt = HAL_RTCEx_BKUPRead(&hrtc, RTC_BKP_DR0);
            uint32_t current_cnt = HAL_RTC_GetCounter(&hrtc);
            uint32_t delta = current_cnt - saved_cnt;

            // 假设 LSI 实际频率为 32000Hz,LSE 为 32768Hz,误差比例
            // 实际应用中需根据实测校准,此处示例:误差 = delta * (32768-32000)/32768
            int32_t compensation = (int32_t)(delta * 768 / 32768); // 约 2.3%

            // 切换回 LSE
            RCC_PeriphCLKInitTypeDef periph = {0};
            periph.PeriphClockSelection = RCC_PERIPHCLK_RTC;
            periph.RTCClockSelection = RCC_RTCCLKSOURCE_LSE;
            HAL_RCCEx_PeriphCLKConfig(&periph);

            // 调整 RTC 计数器(注意:写入计数器会触发同步)
            HAL_RTC_SetCounter(&hrtc, current_cnt + compensation);

            // 清除标记
            HAL_RTCEx_BKUPWrite(&hrtc, RTC_BKP_DR1, 0x0000);
        }
    }
}

4. 完整代码示例(主循环集成)

int main(void)
{
    HAL_Init();
    SystemClock_Config();
    RTC_Init();

    // 设置每秒唤醒中断(用于检测)
    HAL_RTCEx_SetWakeUpTimer_IT(&hrtc, 32767, RTC_WAKEUPCLOCK_CK_SPRE_16BITS);
    HAL_NVIC_EnableIRQ(RTC_WKUP_IRQn);

    while (1)
    {
        // 主循环可执行其他任务
        // 检测与恢复在中断中处理,或轮询调用
        Check_LSE_Status();
        LSE_Recovery_Compensation();
        HAL_Delay(100);
    }
}

void RTC_WKUP_IRQHandler(void)
{
    HAL_RTCEx_WakeUpTimerIRQHandler(&hrtc);
}

void HAL_RTCEx_WakeUpTimerEventCallback(RTC_HandleTypeDef *hrtc)
{
    // 每秒触发一次,检查 LSE 状态
    Check_LSE_Status();
    LSE_Recovery_Compensation();
}

5. 注意事项

  • 备份域写入保护:操作 BKP 寄存器前必须使能 PWR 和 BKP 时钟,并调用 HAL_PWR_EnableBkUpAccess()
  • LSI 精度:LSI 偏差较大,补偿系数需根据实际硬件测量(可存储于备份寄存器)。
  • 切换时钟源时:RTC 计数器会继续运行,但切换瞬间可能产生毛刺,建议在切换前同步(HAL_RTC_WaitForSynchro)。
  • 低功耗模式:若进入 STOP 模式,LSE 可能被关闭,需在唤醒后重新检测。
  • 调试建议:使用逻辑分析仪监控 LSE 波形,或通过串口打印 RTC 时间验证补偿效果。

6. 总结

通过备份域寄存器保存关键状态,结合 RTC 硬件特性,实现了 LSE 停振时的无感切换与恢复补偿,避免了系统时间异常。此方案适用于对时间连续性要求高的嵌入式系统,如数据采集、通信节点等。