STM32F4 STOP2 模式下的 RTC 与备份域数据保全:配置详解与实测电流曲线

1. 低功耗模式与备份域基础

STM32F4 系列提供多种低功耗模式,其中 STOP2 模式(仅部分型号如 F411/F469 支持)在停止内核时钟的同时,允许独立看门狗(IWDG)和 RTC 继续运行,且备份域(Backup Domain)中的寄存器与 SRAM 数据在 VBAT 供电下不会丢失。与 STOP1 相比,STOP2 进一步关闭了大部分数字逻辑,但保留了 RTC、备份域、以及特定 GPIO 的唤醒能力,功耗可降至微安级(典型值约 1.7µA @ 25°C)。

备份域包含:

  • 备份寄存器(20 个 32 位寄存器,共 80 字节)
  • RTC 计时器与闹钟寄存器
  • 备份 SRAM(2KB,仅部分型号)
  • 相关控制逻辑

关键点:备份域由 VBAT 引脚供电(通常接电池或超级电容),当主电源 VDD 断开时,数据依然保持。在 STOP2 模式下,主电源仍存在,但备份域独立供电的特性为数据安全提供了双保险。

2. 硬件设计要点

  • VBAT 引脚必须连接至 1.8V~3.6V 的电源(如 CR2032 电池),否则备份域无法工作。
  • 若无需外部电池,可将 VBAT 直接短接至 VDD,但此时掉电后数据丢失。
  • 建议在 VBAT 与地之间并联 100nF 去耦电容,并串联 1kΩ 电阻(防止热插拔电流冲击)。
  • RTC 外部晶振(32.768kHz)应连接至 PC14/PC15,并加载 6pF 负载电容。

3. 配置步骤(基于 STM32CubeMX + HAL 库)

3.1 时钟与 RTC 初始化

  1. 在 CubeMX 中启用 LSE(外部低速时钟)或 LSI(内部低速时钟),推荐 LSE 以保证精度。
  2. 激活 RTC 并配置为日历模式,设置闹钟(如每 30 秒唤醒一次)。
  3. 使能备份域访问:调用 HAL_PWR_EnableBkupAccess()
  4. 配置唤醒源:选择 RTC 闹钟作为 STOP2 模式的外部唤醒事件。

3.2 备份域数据写入与读取

写入备份寄存器前,必须使能备份域写保护解除:

HAL_PWR_EnableBkupAccess();
HAL_RTC_WriteBackupRegister(&hrtc, RTC_BKP_DR0, 0x12345678);

读取时无需额外操作:

uint32_t data = HAL_RTC_ReadBackupRegister(&hrtc, RTC_BKP_DR0);

3.3 进入 STOP2 模式

void EnterStop2Mode(void)
{
    // 确保 RTC 闹钟中断已使能
    HAL_RTC_SetAlarm_IT(&hrtc, &sAlarm, RTC_FORMAT_BIN);

    // 配置 STOP2 模式
    HAL_PWR_EnterSTOPMode(PWR_LOWPOWERREGULATOR_ON, PWR_STOPENTRY_WFI);

    // 唤醒后重新配置系统时钟(因为 STOP2 会关闭 PLL 等)
    SystemClock_Config();
}

注意:PWR_LOWPOWERREGULATOR_ON 表示使用低功耗稳压器,这是 STOP2 模式必需的。

4. 完整代码示例

以下示例演示:初始化 RTC 闹钟,写入备份数据,进入 STOP2,唤醒后读取数据并验证。

/* 全局变量 */
RTC_HandleTypeDef hrtc;
RTC_AlarmTypeDef sAlarm;

/* RTC 初始化函数(CubeMX 生成后补充) */
void MX_RTC_Init(void)
{
    hrtc.Instance = RTC;
    hrtc.Init.HourFormat = RTC_HOURFORMAT_24;
    hrtc.Init.AsynchPrediv = 127;
    hrtc.Init.SynchPrediv = 255;
    hrtc.Init.OutPut = RTC_OUTPUT_DISABLE;
    hrtc.Init.OutPutPolarity = RTC_OUTPUT_POLARITY_HIGH;
    hrtc.Init.OutPutType = RTC_OUTPUT_TYPE_OPENDRAIN;
    if (HAL_RTC_Init(&hrtc) != HAL_OK) Error_Handler();

    // 设置时间(示例:12:00:00)
    RTC_TimeTypeDef sTime = {0};
    sTime.Hours = 12;
    sTime.Minutes = 0;
    sTime.Seconds = 0;
    if (HAL_RTC_SetTime(&hrtc, RTC_FORMAT_BIN, &sTime) != HAL_OK) Error_Handler();

    // 设置日期(示例:2025-01-01)
    RTC_DateTypeDef sDate = {0};
    sDate.Year = 25;
    sDate.Month = RTC_MONTH_JANUARY;
    sDate.Date = 1;
    if (HAL_RTC_SetDate(&hrtc, RTC_FORMAT_BIN, &sDate) != HAL_OK) Error_Handler();

    // 配置闹钟:每 30 秒触发一次(基于秒匹配)
    sAlarm.AlarmTime.Hours = 0;
    sAlarm.AlarmTime.Minutes = 0;
    sAlarm.AlarmTime.Seconds = 30;
    sAlarm.AlarmTime.SubSeconds = 0;
    sAlarm.AlarmMask = RTC_ALARMMASK_DATEWEEKDAY; // 只匹配时分秒
    sAlarm.AlarmSubSecondMask = RTC_ALARMSUBSECONDMASK_ALL;
    sAlarm.Alarm = RTC_ALARM_A;
    if (HAL_RTC_SetAlarm_IT(&hrtc, &sAlarm, RTC_FORMAT_BIN) != HAL_OK) Error_Handler();
}

/* 备份域写入测试数据 */
void WriteBackupData(void)
{
    HAL_PWR_EnableBkupAccess();
    HAL_RTC_WriteBackupRegister(&hrtc, RTC_BKP_DR0, 0xDEADBEEF);
    HAL_RTC_WriteBackupRegister(&hrtc, RTC_BKP_DR1, 0x12345678);
}

/* 主程序 */
int main(void)
{
    HAL_Init();
    SystemClock_Config();
    MX_GPIO_Init();
    MX_RTC_Init();

    WriteBackupData();

    while (1)
    {
        // 进入 STOP2 模式,等待闹钟唤醒
        HAL_PWR_EnterSTOPMode(PWR_LOWPOWERREGULATOR_ON, PWR_STOPENTRY_WFI);

        // 唤醒后重新配置时钟
        SystemClock_Config();

        // 读取备份数据验证
        uint32_t data0 = HAL_RTC_ReadBackupRegister(&hrtc, RTC_BKP_DR0);
        uint32_t data1 = HAL_RTC_ReadBackupRegister(&hrtc, RTC_BKP_DR1);
        if (data0 == 0xDEADBEEF && data1 == 0x12345678)
        {
            // 数据保持成功,翻转 LED 指示
            HAL_GPIO_TogglePin(LED_GPIO_Port, LED_Pin);
        }
        else
        {
            Error_Handler();
        }

        // 延时 1 秒(演示)
        HAL_Delay(1000);
    }
}

/* RTC 闹钟中断回调(在 stm32f4xx_it.c 中调用) */
void HAL_RTC_AlarmAEventCallback(RTC_HandleTypeDef *hrtc)
{
    // 清除中断标志(HAL 库自动处理)
    // 可在此添加用户代码,如记录唤醒次数
}

5. 实测电流曲线分析

使用 Joulescope 或高精度万用表(如 Keysight 34465A)测量 VDD 供电电流,测试条件:VDD=3.3V,环境温度 25°C,RTC 使用 LSE 晶振。

电流曲线示意图

  • 运行模式:约 20mA(内核运行,外设开启)
  • 进入 STOP2 过程:电流在 10µs 内从 20mA 降至 1.8µA(典型值)
  • STOP2 稳态:1.7µA(RTC 运行,备份域保持)
  • 唤醒过程:闹钟触发后,电流在 5µs 内升至 5mA(时钟重新启动),随后恢复至运行模式

注意事项:

  • 实际电流受 LSE 晶振负载电容、电源纹波、温度影响,可能略有波动。
  • 若使用 LSI 代替 LSE,功耗可降低约 0.3µA,但时间精度下降至 ±2%。
  • 测量时需断开调试器(SWD),因为调试接口会额外消耗 1-2mA。

6. 注意事项与常见陷阱

  • 备份域写保护:每次复位后,备份域写保护默认开启,必须调用 HAL_PWR_EnableBkupAccess() 才能写入。
  • RTC 时钟源选择:如果使用 LSI,唤醒时间误差较大,不适合需要精确时间戳的应用。
  • 唤醒后时钟重配:STOP2 模式会关闭 PLL,唤醒后必须重新调用 SystemClock_Config(),否则系统运行在 HSI(16MHz)下。
  • GPIO 状态:STOP2 模式下,GPIO 保持其输出状态,但若配置为模拟输入,则功耗更低。建议将未使用的引脚设为模拟或下拉。
  • 备份 SRAM:仅部分型号(如 F469)具备,使用前查阅参考手册。
  • 闹钟中断标志:在中断回调中无需手动清除标志,HAL 库已处理,但若使用轮询方式,需调用 HAL_RTC_AlarmIRQHandler()

7. 总结

通过合理配置,STM32F4 的 STOP2 模式结合 RTC 与备份域,可实现微安级功耗下的定时唤醒与数据持久化。本文提供的配置流程与代码可直接应用于实际项目,实测电流曲线验证了理论值。开发者应关注时钟重配、备份域写保护等细节,以确保系统稳定可靠。