4. RTC备份寄存器保存系统状态

任务说明

在低功耗应用中,MCU进入停机模式前需要保存关键系统状态(如运行模式、故障标志、累计唤醒次数),唤醒后能够完整恢复。本案例使用STM32L0系列RTC备份寄存器(BKP Register)实现状态保存与恢复,不依赖外部存储,在VBAT供电下即使主电源断开也能保持数据。

硬件连接

STM32L052 RTC + Backup Register VBAT 3V NRST 按键 PA0 WKUP LED (PB3) PB3 UART2 TX PA2 备份域(VBAT供电) RTC + BKP寄存器 主电源断开仍保持

代码实现


#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) {
        /* 正常运行 */
    }
}

验证方法

验证步骤:

  1. 编译烧录后,首次上电观察LED不闪烁(无效恢复路径),通过调试器读取g_state.wake_count应为1;
  2. 按下PA0唤醒按键,MCU进入停机模式后唤醒,LED闪烁2次,读取wake_count应为2;
  3. 再次按下PA0,wake_count递增为3,且fault_code始终为0x03,证明状态在低功耗模式下被正确保存与恢复;
  4. 断开主电源(仅VBAT供电),等待5秒后重新上电,读取wake_count仍保持断电前的值,证明备份寄存器在VBAT下数据不丢失。

判定标准:唤醒次数持续累加且故障码不丢失,即判定状态保存与恢复功能通过。