# 引言 在电池供电的物联网设备中,STM32F4 常被用于需要周期性唤醒的低功耗任务。通常,RTC 依赖外部低速晶振(LSE,32.768kHz)提供精准时钟,但在高湿度、强振动或低温环境下,LSE 可能停振,导致 RTC 无法计时,系统陷入死循环或无法唤醒。本文提出一种备用方案:当检测到 LSE 停振时,切换至内部低速时钟(LSI),并利用 RTC 备份域寄存器保存唤醒计数,实现无外部晶振的秒级唤醒。 # 原理剖析 ## RTC 与备份域 STM32F4 的 RTC 模块位于备份域(Backup Domain),由 VBAT 引脚供电,当主电源掉电时,RTC 仍能运行。备份域包含 20 个 32 位备份寄存器(BKPxR),用于在复位或掉电后保存用户数据。RTC 时钟源可选 LSE、LSI 或 HSE/128,其中 LSI 约 32kHz,精度较低但无需外部晶振。 ## LSE 停振检测 RTC 提供时钟安全系统(CSS)功能,可监测 LSE 振荡器。当 LSE 失效时,CSS 产生中断,系统可及时响应。然而,CSS 仅适用于 LSE,且需在初始化时使能。若未使能,则需通过轮询 RTC_ISR 的 LSERDY 位检测。 ## 备份域寄存器的作用 备份寄存器在系统复位(包括软件复位)后内容保持不变,但若备份域电源丢失(VBAT 掉电),则数据清零。利用这一特性,我们可在 LSE 停振时,将 RTC 计数周期切换为 LSI,并在备份寄存器中存储唤醒次数或时间戳,实现秒级唤醒的连续性。 # 实现方案 ## 硬件设计 - MCU:STM32F407VET6(或其他 F4 系列) - 外部晶振:LSE(32.768kHz)和 HSE(8MHz) - VBAT 接 3V 纽扣电池或超级电容 ## 软件架构 1. 初始化 RTC 使用 LSE,并使能 CSS 中断。 2. 在 CSS 中断服务函数中,检测到 LSE 失效,切换时钟源至 LSI,并设置唤醒周期为 1 秒。 3. 使用备份寄存器 RTC_BKPxR 存储唤醒计数,每次唤醒后递增。 4. 主程序进入低功耗模式(STOP 模式),RTC 唤醒中断触发后恢复运行。 ## 配置步骤 ### 1. 使能电源和备份域时钟 ```c void RTC_Init(void) { RCC_APB1PeriphClockCmd(RCC_APB1Periph_PWR, ENABLE); PWR_BackupAccessCmd(ENABLE); // 允许访问备份域 RCC_BackupResetCmd(ENABLE); // 复位备份域(可选) RCC_BackupResetCmd(DISABLE); } ``` ### 2. 配置 LSE 和 RTC ```c void RTC_Config_LSE(void) { RCC_LSEConfig(RCC_LSE_ON); while (RCC_GetFlagStatus(RCC_FLAG_LSERDY) == RESET); // 等待 LSE 就绪 RCC_RTCCLKConfig(RCC_RTCCLKSource_LSE); RCC_RTCCLKCmd(ENABLE); RTC_WaitForSynchro(); // 设置 1 秒中断 RTC_ITConfig(RTC_IT_WUT, ENABLE); RTC_WakeUpClockConfig(RTC_WakeUpClock_CK_SPRE_16bits); // 1Hz RTC_SetWakeUpCounter(0); // 1 秒后唤醒 } ``` ### 3. 使能 CSS 中断 ```c void CSS_Init(void) { RCC_LSEConfig(RCC_LSE_ON); RCC_ITConfig(RCC_IT_LSECSS, ENABLE); NVIC_InitTypeDef NVIC_InitStructure; NVIC_InitStructure.NVIC_IRQChannel = RCC_IRQn; NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0; NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0; NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; NVIC_Init(&NVIC_InitStructure); } ``` ### 4. CSS 中断处理函数 ```c void RCC_IRQHandler(void) { if (RCC_GetITStatus(RCC_IT_LSECSS) != RESET) { RCC_ClearITPendingBit(RCC_IT_LSECSS); // 切换至 LSI RCC_LSICmd(ENABLE); while (RCC_GetFlagStatus(RCC_FLAG_LSIRDY) == RESET); RCC_RTCCLKConfig(RCC_RTCCLKSource_LSI); // 重新配置唤醒周期(LSI 频率约 32kHz,需校准) RTC_WakeUpClockConfig(RTC_WakeUpClock_CK_SPRE_16bits); // 使用 1Hz 分频,但 LSI 不精确,需软件校准 RTC_SetWakeUpCounter(0); // 在备份寄存器中标记 LSE 失效 RTC_WriteBackupRegister(RTC_BKP_DR0, 0xA5A5); } } ``` ### 5. 备份寄存器读写函数 ```c uint32_t ReadWakeCounter(void) { return RTC_ReadBackupRegister(RTC_BKP_DR1); } void WriteWakeCounter(uint32_t cnt) { RTC_WriteBackupRegister(RTC_BKP_DR1, cnt); } ``` ### 6. 主程序与低功耗模式 ```c int main(void) { RTC_Init(); CSS_Init(); RTC_Config_LSE(); // 初始化备份寄存器 if (ReadWakeCounter() == 0xFFFFFFFF) WriteWakeCounter(0); while (1) { // 进入 STOP 模式,RTC 唤醒 PWR_EnterSTOPMode(PWR_Regulator_LowPower, PWR_STOPEntry_WFI); // 唤醒后执行任务 uint32_t cnt = ReadWakeCounter(); cnt++; WriteWakeCounter(cnt); // 根据 cnt 执行不同任务 } } ``` # 注意事项 - **LSI 精度问题**:LSI 频率受温度影响,误差可达 10% 以上。若需精确秒级唤醒,可在初始化时测量 LSI 实际频率(通过定时器捕获),并动态调整唤醒计数器值。 - **备份域复位**:在开发调试时,若频繁复位,备份寄存器内容可能保留,导致逻辑混乱。建议在初始化时检查备份寄存器标志(如 DR0 中的魔数),若无效则清零。 - **CSS 中断优先级**:CSS 中断应设为最高优先级,确保 LSE 失效时能及时切换,避免系统挂起。 - **唤醒后时钟源**:切换至 LSI 后,RTC 精度降低,若应用需要精确时间,可考虑使用 HSE/128 作为备用,但 HSE 在 STOP 模式下可能关闭,需权衡。 - **功耗考虑**:LSI 功耗略高于 LSE,但差异不大。在 STOP 模式下,RTC 仍运行,功耗约 1μA 左右。 # 总结 本文通过利用 STM32F4 的备份域寄存器和 CSS 机制,实现了 LSE 停振后的无缝切换,确保秒级唤醒功能不中断。该方法无需额外硬件,仅通过软件配置即可提升系统可靠性,适用于对成本敏感且环境恶劣的嵌入式应用。开发者可根据实际需求扩展,如增加时间校准算法或使用外部 RTC 芯片作为冗余。