引言
在嵌入式系统中,实时时钟(RTC)依赖 LSE(32.768 kHz 晶振)维持时间基准。然而,晶振老化、温度变化或 EMI 干扰可能导致 LSE 停振,此时 RTC 若继续使用内部 RC 振荡器(LSI),其精度通常只有 ±1% 左右,秒级误差会迅速累积。STM32F4 系列提供了硬件 RTC 校准功能,通过调整同步移位和异步预分频值,即使 LSE 停振后切换到 LSI,也能将秒级误差控制在可接受范围。本文将从原理到实践,详细讲解如何实现这一目标。
原理剖析
1. RTC 时钟架构
STM32F4 的 RTC 核心由两个分频器组成:
-
异步预分频器:默认值为 127(7 位),产生
ck_apre(通常 256 Hz)。 -
同步预分频器:默认值为 255(8 位),产生 1 Hz 的
ck_spre。
当 LSE 正常时,RTC_CLK = 32768 Hz,分频后得到精确的 1 Hz。若 LSE 停振,可切换到 LSI(典型 32 kHz,实际范围 17~47 kHz),此时分频比固定,输出频率偏差极大。
2. 校准原理
RTC 校准通过两个寄存器实现:
-
RTC_CALR:包含
CALM(校准减)和CALP(校准加)字段。-
CALM:在每 2^20 个ck_apre周期内,减少若干脉冲,用于补偿频率偏高。 -
CALP:每 2^20 个周期增加 1 个脉冲,用于补偿频率偏低。
-
- RTC_SHIFTR:用于亚秒级同步,可提前或延迟 RTC 时钟,但校准主要依赖 CALR。
校准公式:
实际频率 = RTC_CLK / ( (ASYNC_PRESC+1) * (SYNC_PRESC+1) ) * (1 - CALM/2^20 + CALP/2^20)
通过调整 CALM 和 CALP,可以在 ±0.477 ppm 的精度内微调频率,对应秒级误差约 ±0.04 秒/天。
3. LSE 停振检测与切换
STM32F4 的 RTC 支持时钟源自动切换(通过 RCC_BDCR 的 RTCSEL 位),但默认不会自动检测 LSE 故障。需要借助 LSE 监视器(LSEM)或外部中断检测,然后软件切换时钟源。本文聚焦于切换后的校准补偿。
配置步骤
1. 初始化 RTC 并启用校准
首先,确保 RTC 已配置为 LSE 或 LSI 时钟源,并启用校准功能。以下代码基于 STM32F4xx HAL 库。
#include "stm32f4xx_hal.h"
RTC_HandleTypeDef hrtc;
void RTC_Init(void) {
__HAL_RCC_PWR_CLK_ENABLE();
__HAL_RCC_BKP_CLK_ENABLE();
HAL_PWR_EnableBkUpAccess();
// 启用 LSI 作为备用时钟(若 LSE 停振)
RCC_LSICmd(ENABLE);
while (RCC_GetFlagStatus(RCC_FLAG_LSIRDY) == RESET);
// 选择 LSI 作为 RTC 时钟(实际应用中可先尝试 LSE,失败后切换)
__HAL_RCC_RTC_CONFIG(RCC_RTCCLKSOURCE_LSI);
__HAL_RCC_RTC_ENABLE();
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();
}
}
2. 计算校准值
实际测量 LSI 频率(例如通过定时器捕获),假设测得 f_lsi = 32000 Hz,而目标频率为 32768 Hz。则频率误差为:
err = (32000 - 32768) / 32768 = -0.0234(即偏低 2.34%)
由于 LSI 频率偏低,需要增加频率,因此应使用 CALP 加脉冲。但 CALP 只能每 2^20 周期加 1 个脉冲,补偿范围有限。对于较大偏差,需结合 CALM 和 CALP。
更实用的方法是:先调整分频值,使输出接近 1 Hz,再用校准微调。例如,若 LSI 为 32000 Hz,可将异步分频设为 124(125 分频),同步分频设为 255(256 分频),得到 32000/(125*256) = 1.0 Hz 恰好。但实际频率可能不是整数,因此需要校准。
假设异步分频为 124,同步为 255,实际 f_rtc = 32000,则输出频率为 1.0 Hz,误差为 0。若实际为 32010 Hz,则输出为 32010/(125*256) = 1.0003125 Hz,偏高,需用 CALM 减少脉冲。
计算 CALM:
CALM = (f_rtc / ( (ASYNC+1)*(SYNC+1) ) - 1) * 2^20
本例中:CALM = (32010/32000 - 1) * 1048576 ≈ 327.68,取整为 328。
3. 写入校准寄存器
void RTC_Calibrate(uint32_t calm, uint32_t calp) {
// 进入初始化模式
HAL_RTC_EnterInitMode(&hrtc);
// 设置 CALM 和 CALP
MODIFY_REG(RTC->CALR, RTC_CALR_CALM, calm);
MODIFY_REG(RTC->CALR, RTC_CALR_CALP, calp << 15); // CALP 是位15
// 退出初始化模式
HAL_RTC_ExitInitMode(&hrtc);
}
注意:CALM 是 9 位(0-511),CALP 是 1 位。写入前需确保 RTC 未处于配置状态。
4. 动态调整策略
由于 LSI 频率随温度变化,建议周期性(如每分钟)测量实际频率并更新校准值。可使用定时器输入捕获或比较器测量 LSI 与 LSE(若可用)的比值。
void RTC_AutoCalibrate(void) {
// 假设通过硬件测量得到 f_lsi(例如 32010 Hz)
uint32_t f_lsi = Measure_LSI_Frequency();
uint32_t target = 32768;
float ratio = (float)f_lsi / target;
// 调整分频值(可选)
uint32_t async = 127;
uint32_t sync = 255;
float out_freq = (float)f_lsi / ((async+1)*(sync+1));
float error = out_freq - 1.0f;
uint32_t calm = 0;
uint32_t calp = 0;
if (error > 0) {
calm = (uint32_t)(error * 1048576.0f);
if (calm > 511) calm = 511;
} else {
calp = 1;
}
RTC_Calibrate(calm, calp);
}
完整代码示例
以下是一个完整的初始化与校准流程,包含 LSE 检测和切换逻辑。
void SystemClock_Config(void);
void RTC_Init_With_Fallback(void);
int main(void) {
HAL_Init();
SystemClock_Config();
// 初始化 RTC,优先 LSE,失败则 LSI
RTC_Init_With_Fallback();
// 每 60 秒自动校准一次
while (1) {
HAL_Delay(60000);
RTC_AutoCalibrate();
}
}
void RTC_Init_With_Fallback(void) {
// 尝试启用 LSE
RCC_LSEConfig(RCC_LSE_ON);
uint32_t timeout = 1000;
while ((RCC_GetFlagStatus(RCC_FLAG_LSERDY) == RESET) && (timeout-- > 0)) {
// 等待 LSE 就绪
}
if (RCC_GetFlagStatus(RCC_FLAG_LSERDY) == SET) {
__HAL_RCC_RTC_CONFIG(RCC_RTCCLKSOURCE_LSE);
} else {
// LSE 停振,切换到 LSI
RCC_LSEConfig(RCC_LSE_OFF);
RCC_LSICmd(ENABLE);
while (RCC_GetFlagStatus(RCC_FLAG_LSIRDY) == RESET);
__HAL_RCC_RTC_CONFIG(RCC_RTCCLKSOURCE_LSI);
}
__HAL_RCC_RTC_ENABLE();
// 后续初始化同上
}
注意事项
-
校准范围:
CALM最大 511,对应补偿约 487 ppm;CALP仅补偿 0.954 ppm。对于 LSI 的大偏差,可能需要调整分频值,否则校准不够。 -
写入时机:修改
CALR必须在 RTC 初始化模式下,否则无效。 - 温度漂移:LSI 频率随温度变化明显,建议定期重新校准,或使用温度传感器进行查表补偿。
- 功耗考虑:LSI 功耗低于 LSE,但精度差,校准可弥补,但需权衡。
- 硬件设计:若 LSE 停振频繁,考虑增加晶振负载电容或使用有源晶振。
总结
通过 STM32F4 的硬件 RTC 校准功能,即使在 LSE 停振后切换到 LSI,也能通过动态调整 CALM/CALP 实现秒级精度保持。本文提供了原理、配置步骤和代码示例,开发者可根据实际场景集成到产品中。关键在于定期测量实际频率并更新校准值,以应对环境变化。