STM32F4 系列 LSE 停振后的无感恢复与时间补偿:硬件 RTC 备份域实战指南
1. 引言
在嵌入式系统中,RTC(实时时钟)通常依赖 LSE(低速外部晶振,32.768kHz)提供精准计时。然而,LSE 可能因环境干扰、晶振老化或焊接问题而停振。一旦停振,RTC 时间将停止更新,导致系统时间错乱。STM32F4 系列提供了强大的备份域(BKP)机制,结合硬件 RTC 校准功能,可实现无感恢复与时间补偿。本文将深入讲解其原理,并给出完整实现方案。
2. 原理剖析
2.1 LSE 停振检测
STM32F4 的 RTC 模块内置 LSE 监测功能,通过 RTC_ISR 寄存器的 LSERDY 位可查询 LSE 就绪状态。当 LSE 停振时,LSERDY 变为 0。此外,RTC_ISR 的 LSRMF(LSE 监测失败标志)可触发中断,实现实时检测。
2.2 备份域(BKP)的作用
备份域由 VBAT 引脚供电,包含 RTC 寄存器、备份寄存器(BKPxR)和 20 字节的备份 SRAM。即使主电源断电,只要 VBAT 有电,数据依然保留。利用备份寄存器,可保存 LSE 停振前的系统时间戳和补偿参数,为恢复提供依据。
2.3 时间补偿策略
LSE 停振期间,RTC 停止走时。恢复后,系统需计算停振时长并补偿。补偿分为两步:
- 硬件补偿:利用 RTC 的同步预分频器和校准寄存器(RTC_CALR),调整 RTC 计数频率,减少晶振偏差。
- 软件补偿:记录停振开始和结束的系统 tick(如 SysTick),计算实际停振时间,并在恢复后一次性加上。
3. 硬件配置步骤
3.1 使能备份域和 RTC 时钟
首先,使能 PWR 和 RTC 时钟,并允许访问备份域。
void RTC_Init(void) {
// 1. 使能 PWR 时钟
RCC_APB1PeriphClockCmd(RCC_APB1Periph_PWR, ENABLE);
// 2. 允许访问备份域
PWR_BackupAccessCmd(ENABLE);
// 3. 使能 LSE 并等待就绪
RCC_LSEConfig(RCC_LSE_ON);
while (RCC_GetFlagStatus(RCC_FLAG_LSERDY) == RESET);
// 4. 选择 LSE 作为 RTC 时钟源
RCC_RTCCLKConfig(RCC_RTCCLKSource_LSE);
// 5. 使能 RTC 时钟
RCC_RTCCLKCmd(ENABLE);
// 6. 等待 RTC 同步
RTC_WaitForSynchro();
}
3.2 配置 RTC 时间与日期
使用标准库配置 RTC 时间,并启用 LSE 监测中断。
void RTC_TimeConfig(void) {
RTC_InitTypeDef RTC_InitStruct;
RTC_TimeTypeDef RTC_TimeStruct;
RTC_DateTypeDef RTC_DateStruct;
// 使能 RTC 写保护解除
RTC_WriteProtectionCmd(DISABLE);
// 配置 RTC 分频:LSE 32.768kHz,异步分频 128,同步分频 256,得到 1Hz
RTC_InitStruct.RTC_HourFormat = RTC_HourFormat_24;
RTC_InitStruct.RTC_AsynchPrediv = 127;
RTC_InitStruct.RTC_SynchPrediv = 255;
RTC_Init(&RTC_InitStruct);
// 设置时间 12:00:00
RTC_TimeStruct.RTC_H12 = RTC_H12_AM;
RTC_TimeStruct.RTC_Hour = 12;
RTC_TimeStruct.RTC_Minute = 0;
RTC_TimeStruct.RTC_Second = 0;
RTC_SetTime(RTC_Format_BIN, &RTC_TimeStruct);
// 设置日期 2025-01-01
RTC_DateStruct.RTC_Year = 25;
RTC_DateStruct.RTC_Month = 1;
RTC_DateStruct.RTC_Date = 1;
RTC_DateStruct.RTC_WeekDay = 3;
RTC_SetDate(RTC_Format_BIN, &RTC_DateStruct);
// 使能写保护
RTC_WriteProtectionCmd(ENABLE);
}
3.3 配置 LSE 监测中断
在 NVIC 中使能 RTC 全局中断,并在 RTC 中断处理函数中检测 LSE 状态。
void RTC_IRQHandler(void) {
if (RTC_GetITStatus(RTC_IT_LSE) != RESET) {
// 清除中断标志
RTC_ClearITPendingBit(RTC_IT_LSE);
// 检测 LSE 是否停振
if (RTC_GetFlagStatus(RTC_FLAG_LSERDY) == RESET) {
// 记录停振开始时间(基于 SysTick)
LSE_StopTick = GetSysTick();
// 保存当前 RTC 时间到备份寄存器
RTC_TimeTypeDef time;
RTC_GetTime(RTC_Format_BIN, &time);
BKP_WriteBackupRegister(BKP_DR1, time.RTC_Hour * 3600 + time.RTC_Minute * 60 + time.RTC_Second);
// 设置停振标志
LSE_Stopped = 1;
}
}
}
3.4 备份域数据保存
在 LSE 停振时,将当前时间和系统 tick 存入备份寄存器。
void SaveBackupData(uint32_t time, uint32_t tick) {
BKP_WriteBackupRegister(BKP_DR1, time);
BKP_WriteBackupRegister(BKP_DR2, tick);
}
4. 无感恢复与时间补偿实现
4.1 检测 LSE 恢复
在主循环或定时器中断中轮询 LSE 状态,一旦恢复,执行补偿。
void CheckLSE_Recovery(void) {
if (LSE_Stopped && (RCC_GetFlagStatus(RCC_FLAG_LSERDY) == SET)) {
// 计算停振时长(基于 SysTick 差值)
uint32_t stop_duration = GetSysTick() - LSE_StopTick;
// 读取备份的时间戳
uint32_t saved_time = BKP_ReadBackupRegister(BKP_DR1);
// 计算补偿后的时间
uint32_t compensated_time = saved_time + stop_duration / 1000; // 假设 SysTick 为 1ms
// 设置 RTC 时间
RTC_TimeTypeDef time;
time.RTC_H12 = RTC_H12_AM;
time.RTC_Hour = (compensated_time / 3600) % 24;
time.RTC_Minute = (compensated_time % 3600) / 60;
time.RTC_Second = compensated_time % 60;
RTC_SetTime(RTC_Format_BIN, &time);
// 清除标志
LSE_Stopped = 0;
}
}
4.2 硬件校准补偿
利用 RTC 校准寄存器(RTC_CALR)可微调 RTC 频率,减少长期漂移。
void RTC_Calibration(int16_t ppm) {
// 计算校准值:每个 LSB 对应约 0.954 ppm
uint16_t cal = (uint16_t)(ppm / 0.954);
RTC_CalibOutputCmd(DISABLE);
RTC_CalibCmd(ENABLE);
RTC_SetCalibValue(cal);
}
5. 完整代码示例
以下是一个简化的综合示例,展示初始化、中断处理和恢复逻辑。
#include "stm32f4xx.h"
volatile uint32_t LSE_StopTick = 0;
volatile uint8_t LSE_Stopped = 0;
void SysTick_Init(void) {
SysTick_Config(SystemCoreClock / 1000); // 1ms tick
}
uint32_t GetSysTick(void) {
return SysTick->VAL; // 简化,实际应使用计数器
}
int main(void) {
// 初始化时钟、GPIO 等
RTC_Init();
RTC_TimeConfig();
SysTick_Init();
// 使能 LSE 监测中断
RTC_ITConfig(RTC_IT_LSE, ENABLE);
NVIC_EnableIRQ(RTC_IRQn);
while (1) {
CheckLSE_Recovery();
// 其他任务
}
}
6. 注意事项
-
备份域访问:操作备份寄存器前必须使能 PWR 时钟并调用
PWR_BackupAccessCmd(ENABLE),否则写操作无效。 - LSE 监测中断:确保在 NVIC 中正确配置优先级,避免中断丢失。
- 时间补偿精度:SysTick 的精度直接影响补偿结果,建议使用 1ms 或更高精度。
- 校准值范围:RTC_CALR 寄存器为 9 位,最大校准范围约 ±487 ppm,超出需分段处理。
- 电源管理:VBAT 必须接电池或超级电容,否则备份域数据在断电后丢失。
7. 总结
通过 STM32F4 的备份域和 RTC 硬件特性,我们实现了 LSE 停振后的无感恢复与时间补偿。该方法无需外部 RTC 芯片,仅依赖片内资源,有效提升了系统可靠性。在实际项目中,可根据需求调整补偿算法(如使用外部高精度时钟源)。希望本文能为你的嵌入式开发提供实用参考。