STM32F4 系列 LSE 停振后:硬件 RTC 备份域实现时间无缝恢复

1. 问题背景与挑战

在工业控制、物联网设备等嵌入式应用中,RTC(实时时钟)通常依赖 LSE(32.768kHz 低速外部晶振)提供精准时基。然而,LSE 可能因以下原因停振:

  • 晶振老化或焊接不良
  • 强电磁干扰(EMI)导致振荡器停振
  • 温度骤变引起频率偏移

一旦 LSE 停振,RTC 将失去时钟源,时间立即停止更新。若系统未做处理,重启后时间可能回退到初始值,导致日志时间戳错误、通信协议超时等问题。

2. 备份域与 RTC 硬件架构

STM32F4 的备份域(Backup Domain)包含 RTC、备份寄存器(BKP)和相关的电源控制逻辑。其关键特性:

  • 由 VBAT 引脚供电(通常接纽扣电池),当主电源 VDD 掉电时,备份域仍可工作。
  • 备份域复位独立于系统复位,只有备份域复位(BDRST)或 VBAT 掉电才会清除。
  • RTC 时钟源可选:LSE、LSI(内部低速 RC)或 HSE 分频。

核心机制:当 LSE 停振时,硬件可自动切换至 LSI(约 32kHz,精度较低),保证 RTC 继续计数,同时置位 RTC_ISR 中的 LSERDY 标志位(或通过中断通知软件)。软件检测到后,可采取补偿措施,并在 LSE 恢复后重新切换回 LSE。

3. 无缝恢复原理

无缝恢复的核心在于:

  1. 硬件自动切换:配置 RTC 时钟源为 LSE,并启用 LSE 旁路或 CSS(时钟安全系统)监测。当 LSE 失效时,硬件自动将 RTC 时钟切换到 LSI,RTC 计数不中断。
  2. 备份域保持:RTC 寄存器(包括时间、日期、闹钟等)位于备份域,不受主电源复位影响,因此时间数据得以保留。
  3. 软件补偿:LSI 精度较低(典型 ±5%),长时间运行会产生累积误差。软件可在 LSE 恢复后,根据 LSI 运行时长和校准系数修正时间。

4. 配置步骤

4.1 使能备份域访问和 LSE

// 1. 使能 PWR 和 RCC 时钟
RCC_APB1PeriphClockCmd(RCC_APB1Periph_PWR, ENABLE);
RCC_APB1PeriphClockCmd(RCC_APB1Periph_BKP, ENABLE);

// 2. 使能备份域访问(解除写保护)
PWR_BackupAccessCmd(ENABLE);

// 3. 使能 LSE 并等待就绪(可选超时)
RCC_LSEConfig(RCC_LSE_ON);
while (RCC_GetFlagStatus(RCC_FLAG_LSERDY) == RESET) {
    // 超时处理,例如 500ms
}

4.2 配置 RTC 时钟源和预分频

// 选择 LSE 作为 RTC 时钟
RCC_RTCCLKConfig(RCC_RTCCLKSource_LSE);

// 使能 RTC 时钟
RCC_RTCCLKCmd(ENABLE);

// 等待 RTC 寄存器同步(APB 与 RTC 时钟域同步)
RTC_WaitForSynchro();

// 设置预分频:LSE=32768Hz,目标 1Hz,则异步预分频=127,同步预分频=255
RTC_InitTypeDef RTC_InitStruct;
RTC_InitStruct.RTC_HourFormat = RTC_HourFormat_24;
RTC_InitStruct.RTC_AsynchPrediv = 127;
RTC_InitStruct.RTC_SynchPrediv = 255;
RTC_Init(&RTC_InitStruct);

4.3 启用 LSE 时钟安全系统(CSS)

CSS 用于监测 LSE 失效,并自动切换时钟源。注意:STM32F4 的 CSS 仅适用于 LSE,且切换目标是 LSI。

// 使能 LSE CSS 中断(可选,用于通知软件)
RCC_LSECSSConfig(RCC_LSE_CSS_ON);
RCC_LSECSSITConfig(ENABLE);

// 在 NVIC 中使能 RCC_IRQn(具体中断号见参考手册)
NVIC_EnableIRQ(RCC_IRQn);

4.4 中断处理函数(检测 LSE 失效)

void RCC_IRQHandler(void) {
    if (RCC_GetITStatus(RCC_IT_LSECSS) != RESET) {
        RCC_ClearITPendingBit(RCC_IT_LSECSS);
        // LSE 失效,RTC 已自动切换到 LSI
        // 记录时间戳,设置标志位,供主程序处理
        lse_failure_flag = 1;
    }
}

4.5 时间恢复与补偿

当 LSE 恢复后(例如通过外部检测或定期检查),可重新切换回 LSE,并修正时间。

// 重新使能 LSE 并等待就绪
RCC_LSEConfig(RCC_LSE_ON);
while (RCC_GetFlagStatus(RCC_FLAG_LSERDY) == RESET) {
    // 超时处理
}

// 切换 RTC 时钟源回 LSE(注意:需先禁用 RTC 时钟?实际 STM32F4 允许直接切换,但建议在 RTC 使能时操作)
RCC_RTCCLKConfig(RCC_RTCCLKSource_LSE);

// 等待同步
RTC_WaitForSynchro();

// 计算 LSI 运行期间的误差(假设 LSI 频率为 32000Hz,实际需测量)
// 例如:记录 LSI 运行秒数,乘以误差系数,然后调整时间
uint32_t lsi_seconds = get_lsi_seconds(); // 通过备份寄存器记录
int32_t error_ppm = 50000; // 假设 LSI 误差 +5%
int32_t correction = (int32_t)lsi_seconds * error_ppm / 1000000;
RTC_AdjustTime(correction); // 自定义函数,调整 RTC 时间

5. 完整代码示例(简化)

// 初始化函数
void RTC_Init_With_LSE_CSS(void) {
    // 使能 PWR 和备份域
    RCC_APB1PeriphClockCmd(RCC_APB1Periph_PWR, ENABLE);
    PWR_BackupAccessCmd(ENABLE);

    // 使能 LSE,等待就绪(超时 1 秒)
    RCC_LSEConfig(RCC_LSE_ON);
    uint32_t timeout = 1000;
    while (RCC_GetFlagStatus(RCC_FLAG_LSERDY) == RESET) {
        if (--timeout == 0) {
            // LSE 启动失败,可以启用 LSI 作为备用,或报错
            RCC_LSICmd(ENABLE);
            while (RCC_GetFlagStatus(RCC_FLAG_LSIRDY) == RESET);
            RCC_RTCCLKConfig(RCC_RTCCLKSource_LSI);
            break;
        }
    }

    // 配置 RTC 时钟源(若 LSE 成功则用 LSE,否则用 LSI)
    if (timeout > 0) {
        RCC_RTCCLKConfig(RCC_RTCCLKSource_LSE);
    }
    RCC_RTCCLKCmd(ENABLE);
    RTC_WaitForSynchro();

    // 配置预分频(根据实际时钟源调整)
    RTC_InitTypeDef RTC_InitStruct;
    RTC_InitStruct.RTC_HourFormat = RTC_HourFormat_24;
    if (timeout > 0) {
        RTC_InitStruct.RTC_AsynchPrediv = 127;
        RTC_InitStruct.RTC_SynchPrediv = 255;
    } else {
        // LSI 约 32000Hz,预分频需调整
        RTC_InitStruct.RTC_AsynchPrediv = 124;
        RTC_InitStruct.RTC_SynchPrediv = 255;
    }
    RTC_Init(&RTC_InitStruct);

    // 使能 LSE CSS 中断
    RCC_LSECSSConfig(RCC_LSE_CSS_ON);
    RCC_LSECSSITConfig(ENABLE);
    NVIC_EnableIRQ(RCC_IRQn);
}

// 中断处理
void RCC_IRQHandler(void) {
    if (RCC_GetITStatus(RCC_IT_LSECSS) != RESET) {
        RCC_ClearITPendingBit(RCC_IT_LSECSS);
        // 记录失效时间,保存到备份寄存器
        uint32_t current_time = RTC_GetCounter(); // 假设使用计数器模式
        BKP_WriteBackupRegister(BKP_DR1, current_time);
        // 设置标志
        lse_failure = 1;
    }
}

6. 注意事项

  • 备份域电源:确保 VBAT 引脚连接电池或大电容,否则主电源掉电后备份域数据丢失。
  • LSI 精度:LSI 频率随温度变化,误差可达 5% 以上,长时间运行需定期校准。可在 LSE 正常时测量 LSI 频率,存储校准系数。
  • CSS 中断:LSE CSS 中断在检测到失效时触发,但切换是硬件自动完成的,无需软件干预。注意清除中断标志,避免反复触发。
  • 切换时钟源时的同步:修改 RTC 时钟源后,必须等待 RTC 同步(RTC_WaitForSynchro),否则读写寄存器可能无效。
  • 备份寄存器:用于保存关键状态(如 LSE 失效标志、运行时长),但备份寄存器数量有限(STM32F4 通常 20 个),合理规划。
  • 测试:实际应用中,可通过人为断开 LSE 晶振(如用镊子短路)模拟停振,验证恢复逻辑。

7. 总结

通过 STM32F4 的备份域和 RTC 硬件自动切换机制,可以在 LSE 停振时无缝切换到 LSI,保持时间连续,并在 LSE 恢复后修正误差。该方法无需外部硬件,仅需合理配置寄存器和中断,即可显著提升系统可靠性。开发者应根据实际需求,结合备份寄存器和校准算法,构建更完善的时间保持方案。