STM32F4 低功耗 STOP 模式下保留 RTC 与备份域数据:规避 SRAM 供电丢失导致的启动异常
👁 1 阅读 · 2026-08-27 · 嵌入式
在电池供电的物联网设备中,STM32F4 的 STOP 模式常被用于降低功耗,同时依赖 RTC 与备份域保存关键数据。然而,若未正确处理 SRAM 供电,唤醒后可能因数据丢失或启动异常导致系统崩溃。本文深入剖析 STOP 模式下的电源域划分,讲解如何通过配置 PWR 寄存器与 RTC 备份寄存器,确保 SRAM 数据安全,并提供完整代码示例与调试注意事项,帮助开发者构建稳定可靠的超低功耗系统。
# 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 掉电恢复,并利用备份寄存器作为数据保险。此方案适用于需要超低功耗且要求快速唤醒的场景,如智能手表、传感器节点等。