# STM32F4 低功耗 STOP 模式:保留 RTC 与备份域数据的完整指南 ## 一、为什么 STOP 模式会丢失 SRAM 数据? STM32F4 系列(如 STM32F407)的电源系统分为多个域: - **VDD 域**:主电源,包括 CPU、Flash、SRAM1/SRAM2 等。 - **备份域**:由 VBAT 供电,包含 RTC、备份寄存器(20 个 32 位)、备份 SRAM(4KB,仅部分型号)。 在 **STOP 模式**下,VDD 域中的大部分外设时钟停止,但 SRAM 和寄存器内容**保持供电**(只要 VDD 仍存在)。然而,若进入 STOP 模式时未正确设置 `PWR_CR` 寄存器的 `FPDS` 位(Flash 掉电),Flash 可能掉电,导致唤醒后无法从 Flash 读取代码,引发启动异常。此外,若使用 **待机模式(Standby)**,整个 VDD 域掉电,SRAM 数据必然丢失,因此必须使用 STOP 模式来保留 SRAM。 **核心问题**:STOP 模式下,若 `FPDS=1`(Flash 掉电),唤醒后需要重新初始化 Flash 接口;若 `LPDS=1`(内部稳压器低压),SRAM 供电电压降低,可能影响数据保持。因此,要保留 SRAM 数据,必须确保 `LPDS=0`(保持正常电压),并正确处理 Flash 掉电后的恢复。 ## 二、配置步骤:保留 RTC 与备份域数据 ### 1. 使能 PWR 和 RTC 时钟 ```c void RTC_Init(void) { RCC_APB1PeriphClockCmd(RCC_APB1Periph_PWR, ENABLE); PWR_BackupAccessCmd(ENABLE); // 允许访问备份域 RCC_LSEConfig(RCC_LSE_ON); // 启用外部低速晶振(32.768kHz) while (RCC_GetFlagStatus(RCC_FLAG_LSERDY) == RESET); RCC_RTCCLKConfig(RCC_RTCCLKSource_LSE); RCC_RTCCLKCmd(ENABLE); RTC_InitTypeDef RTC_InitStruct; RTC_InitStruct.RTC_HourFormat = RTC_HourFormat_24; RTC_InitStruct.RTC_AsynchPrediv = 0x7F; RTC_InitStruct.RTC_SynchPrediv = 0xFF; RTC_Init(&RTC_InitStruct); } ``` ### 2. 写入备份寄存器保存关键数据 ```c void BackupData_Write(uint32_t data) { PWR_BackupAccessCmd(ENABLE); // 确保备份域可写 RTC_WriteBackupRegister(RTC_BKP_DR0, data); } uint32_t BackupData_Read(void) { return RTC_ReadBackupRegister(RTC_BKP_DR0); } ``` ### 3. 进入 STOP 模式并保留 SRAM ```c void EnterStopMode(void) { // 保存 SRAM 数据(无需额外操作,只要 VDD 存在) // 关键:设置 PWR 寄存器,确保 SRAM 供电正常 PWR_StopModeCmd(DISABLE); // 禁用 STOP 模式下的低压(LPDS=0) PWR_FlashPowerDownCmd(ENABLE); // 允许 Flash 掉电(可选,降低功耗) // 配置唤醒源:RTC 闹钟或外部中断 RTC_AlarmCmd(RTC_Alarm_A, ENABLE); // 进入 STOP 模式 PWR_EnterSTOPMode(PWR_Regulator_ON, PWR_STOPEntry_WFI); // 唤醒后:系统时钟默认 HSI,需重新配置 SystemInit(); // 若 Flash 掉电,需重新初始化 Flash 接口 if (PWR_GetFlagStatus(PWR_FLAG_FLASH_PD) != RESET) { FLASH_Unlock(); FLASH_OB_Unlock(); // 重新配置 Flash 等待周期等(通常 SystemInit 已处理) PWR_ClearFlag(PWR_FLAG_FLASH_PD); } } ``` ### 4. 唤醒后检查备份域数据完整性 ```c void CheckAndRestore(void) { uint32_t saved = BackupData_Read(); if (saved != 0xDEADBEEF) { // 预设魔数 // 数据丢失,执行恢复逻辑 BackupData_Write(0xDEADBEEF); } } ``` ## 三、完整代码示例:RTC 闹钟唤醒 + 数据保持 ```c #include "stm32f4xx.h" volatile uint32_t g_counter = 0; void RTC_Config(void) { // 使能 PWR 时钟并允许备份域访问 RCC_APB1PeriphClockCmd(RCC_APB1Periph_PWR, ENABLE); PWR_BackupAccessCmd(ENABLE); // 启用 LSE RCC_LSEConfig(RCC_LSE_ON); while (RCC_GetFlagStatus(RCC_FLAG_LSERDY) == RESET); // 配置 RTC 时钟源 RCC_RTCCLKConfig(RCC_RTCCLKSource_LSE); RCC_RTCCLKCmd(ENABLE); // 初始化 RTC RTC_InitTypeDef rtc_init; rtc_init.RTC_HourFormat = RTC_HourFormat_24; rtc_init.RTC_AsynchPrediv = 0x7F; rtc_init.RTC_SynchPrediv = 0xFF; RTC_Init(&rtc_init); // 设置闹钟 10 秒后 RTC_AlarmTypeDef alarm; alarm.RTC_AlarmTime.RTC_H12 = RTC_H12_AM; alarm.RTC_AlarmTime.RTC_Hours = 0; alarm.RTC_AlarmTime.RTC_Minutes = 0; alarm.RTC_AlarmTime.RTC_Seconds = 10; alarm.RTC_AlarmMask = RTC_AlarmMask_DateWeekDay; // 仅匹配时间 RTC_SetAlarm(RTC_Format_BIN, RTC_Alarm_A, &alarm); RTC_AlarmCmd(RTC_Alarm_A, ENABLE); // 使能闹钟中断 RTC_ITConfig(RTC_IT_ALRA, ENABLE); NVIC_EnableIRQ(RTC_Alarm_IRQn); } void RTC_Alarm_IRQHandler(void) { if (RTC_GetITStatus(RTC_IT_ALRA) != RESET) { RTC_ClearITPendingBit(RTC_IT_ALRA); g_counter++; // 唤醒后重新配置时钟 SystemInit(); } } int main(void) { // 初始化 RTC 和备份寄存器 RTC_Config(); BackupData_Write(0xDEADBEEF); while (1) { // 正常工作 // 进入 STOP 模式前,保存计数器到备份寄存器 BackupData_Write(g_counter); EnterStopMode(); // 唤醒后检查数据 uint32_t val = BackupData_Read(); if (val != g_counter) { // 数据不一致,恢复 g_counter = val; } } } ``` ## 四、注意事项与常见陷阱 - **LPDS 位必须清零**:`PWR_StopModeCmd(DISABLE)` 确保内部稳压器在 STOP 模式下保持正常电压,否则 SRAM 可能因电压过低而丢失数据。 - **Flash 掉电后的恢复**:若使能了 `PWR_FlashPowerDownCmd(ENABLE)`,唤醒后必须重新初始化 Flash 接口(通常 `SystemInit()` 会处理,但需检查 `PWR_FLAG_FLASH_PD` 标志并清除)。 - **备份域访问权限**:写入备份寄存器前必须调用 `PWR_BackupAccessCmd(ENABLE)`,否则写操作无效。 - **系统时钟重新配置**:STOP 模式唤醒后,系统时钟自动切换到 HSI(16MHz),若原系统时钟为 PLL,需重新配置。 - **使用待机模式的误区**:待机模式(Standby)会掉电 SRAM,若需保留数据,必须使用 STOP 模式。 - **调试建议**:在进入 STOP 模式前,将关键数据写入备份寄存器,并在唤醒后比较,以验证数据完整性。 ## 五、总结 通过正确配置 PWR 寄存器,STM32F4 可以在 STOP 模式下保留 SRAM 和备份域数据,同时利用 RTC 实现定时唤醒。关键在于确保 `LPDS=0`、处理 Flash 掉电恢复,并利用备份寄存器作为数据保险。此方案适用于需要超低功耗且要求快速唤醒的场景,如智能手表、传感器节点等。