# 引言 在工业控制、数据采集等嵌入式应用中,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 并保持时间精度。通过合理配置分频器和校准寄存器,可将日误差控制在秒级以内。该方法适用于对成本敏感且环境恶劣的工业场景,显著提高了系统的鲁棒性。开发者可根据实际需求调整校准频率和测量算法,以平衡精度与资源消耗。