4. RTC备份寄存器保存系统状态
任务说明
在低功耗应用中,MCU进入停机模式前需要保存关键系统状态(如运行模式、故障标志、累计唤醒次数),唤醒后能够完整恢复。本案例使用STM32L0系列RTC备份寄存器(BKP Register)实现状态保存与恢复,不依赖外部存储,在VBAT供电下即使主电源断开也能保持数据。
硬件连接
代码实现
#include "stm32l0xx_hal.h"
/* 备份寄存器地址映射(共20个,每个32位) */
#define BKP_REG_MODE 0x01 /* 系统运行模式 */
#define BKP_REG_FAULT 0x02 /* 故障标志 */
#define BKP_REG_WAKE_CNT 0x03 /* 唤醒次数 */
#define BKP_REG_MAGIC 0x04 /* 有效性校验 */
#define MAGIC_VALUE 0xA5C3F1E7
/* 系统状态结构体 */
typedef struct {
uint8_t mode; /* 0:正常, 1:低功耗, 2:诊断 */
uint8_t fault_code; /* 0:无故障, 非0:故障码 */
uint32_t wake_count; /* 累计唤醒次数 */
} SystemState_t;
static SystemState_t g_state;
/* 使能备份域写访问 */
static void BKP_EnableWrite(void)
{
__HAL_RCC_PWR_CLK_ENABLE();
HAL_PWR_EnableBkUpAccess();
}
/* 写入备份寄存器 */
static void BKP_WriteReg(uint32_t reg_idx, uint32_t value)
{
BKP_EnableWrite();
/* STM32L0备份寄存器地址: 0x40002800 + 4*reg_idx */
*(__IO uint32_t *)(RTC_BASE + 0x500 + 4 * reg_idx) = value;
}
/* 读取备份寄存器 */
static uint32_t BKP_ReadReg(uint32_t reg_idx)
{
return *(__IO uint32_t *)(RTC_BASE + 0x500 + 4 * reg_idx);
}
/* 保存系统状态到备份寄存器 */
static void State_Save(void)
{
BKP_WriteReg(BKP_REG_MODE, g_state.mode);
BKP_WriteReg(BKP_REG_FAULT, g_state.fault_code);
BKP_WriteReg(BKP_REG_WAKE_CNT, g_state.wake_count);
BKP_WriteReg(BKP_REG_MAGIC, MAGIC_VALUE);
}
/* 从备份寄存器恢复系统状态 */
static uint8_t State_Restore(void)
{
if (BKP_ReadReg(BKP_REG_MAGIC) != MAGIC_VALUE) {
/* 首次上电或数据无效,使用默认值 */
g_state.mode = 0;
g_state.fault_code = 0;
g_state.wake_count = 0;
return 0; /* 无效恢复 */
}
g_state.mode = (uint8_t)BKP_ReadReg(BKP_REG_MODE);
g_state.fault_code = (uint8_t)BKP_ReadReg(BKP_REG_FAULT);
g_state.wake_count = BKP_ReadReg(BKP_REG_WAKE_CNT);
return 1; /* 有效恢复 */
}
/* 进入低功耗前调用 */
void System_EnterLowPower(void)
{
g_state.mode = 1;
State_Save();
/* 进入停机模式,PA0上升沿唤醒 */
HAL_SuspendTick();
HAL_PWR_EnterSTOPMode(PWR_LOWPOWERREGULATOR_ON, PWR_STOPENTRY_WFI);
/* 唤醒后继续执行 */
HAL_ResumeTick();
SystemClock_Config(); /* 重新配置系统时钟 */
}
/* 主程序示例 */
int main(void)
{
HAL_Init();
SystemClock_Config();
/* 上电恢复状态 */
uint8_t valid = State_Restore();
if (valid) {
/* 有效恢复:累加唤醒次数 */
g_state.wake_count++;
State_Save();
} else {
/* 首次运行:初始化并保存 */
g_state.mode = 0;
g_state.fault_code = 0;
g_state.wake_count = 1;
State_Save();
}
/* 根据恢复的模式执行不同逻辑 */
if (g_state.mode == 1) {
/* 从低功耗唤醒,LED闪烁2次指示 */
for (int i = 0; i < 2; i++) {
HAL_GPIO_WritePin(GPIOB, GPIO_PIN_3, GPIO_PIN_SET);
HAL_Delay(100);
HAL_GPIO_WritePin(GPIOB, GPIO_PIN_3, GPIO_PIN_RESET);
HAL_Delay(100);
}
}
/* 模拟故障发生 */
g_state.fault_code = 0x03; /* 模拟过压故障 */
State_Save();
/* 进入低功耗(等待PA0唤醒) */
System_EnterLowPower();
/* 唤醒后检查状态 */
State_Restore();
/* 此时 g_state.fault_code 仍为 0x03,wake_count 已累加 */
while (1) {
/* 正常运行 */
}
}
验证方法
验证步骤:
- 编译烧录后,首次上电观察LED不闪烁(无效恢复路径),通过调试器读取g_state.wake_count应为1;
- 按下PA0唤醒按键,MCU进入停机模式后唤醒,LED闪烁2次,读取wake_count应为2;
- 再次按下PA0,wake_count递增为3,且fault_code始终为0x03,证明状态在低功耗模式下被正确保存与恢复;
- 断开主电源(仅VBAT供电),等待5秒后重新上电,读取wake_count仍保持断电前的值,证明备份寄存器在VBAT下数据不丢失。
判定标准:唤醒次数持续累加且故障码不丢失,即判定状态保存与恢复功能通过。