4. 停机模式(Stop Mode):Stop0、Stop1、Stop2 的区别与选择

停机模式,说白了就是让内核睡觉,但外设还能干活。

我刚开始接触低功耗设计时,总觉得停机模式就是“关掉一切”。后来踩了坑才明白——停机模式其实是把内核时钟关了,但 SRAM 和寄存器内容得保住,部分外设还能继续跑。这样一觉醒来,程序能接着往下走,不用从头复位。

STM32 的停机模式分三种:Stop0、Stop1、Stop2。它们之间的差异,我习惯用一句话概括——唤醒越快,漏电越大;唤醒越慢,越省电。

4.1 三种停机模式的核心差异

先看一张对比表,心里有个底:

特性 Stop0 Stop1 Stop2
内核电压(VOS) VOS0(最高性能) VOS1(中等) VOS2(最低)
SRAM 保持 全部保持 全部保持 部分保持(可配置)
唤醒时间 约 2 µs 约 5 µs 约 10 µs
典型电流(@25°C) 约 200 µA 约 50 µA 约 10 µA
适用场景 需要快速响应 平衡功耗与响应 极致省电

你想想看,如果产品是电池供电的传感器节点,每隔几秒才发一次数据,那 Stop2 就是首选。但如果是工业控制,要求中断响应在 3 µs 以内,那就得用 Stop0。

我的经验: 别一上来就选 Stop2。我有个项目,为了省电直接上了 Stop2,结果唤醒时间太长,导致通信超时。后来换成 Stop1,电流只多了 40 µA,但通信稳定了。选模式前,先算清楚你的唤醒时间预算。

4.2 SRAM 与寄存器的保持策略

进入停机模式后,SRAM 和寄存器到底保不保?这个问题,我当年也迷糊过。

其实规则很简单:SRAM 内容默认全部保持,但你可以通过配置让部分 SRAM 掉电,进一步省电。寄存器方面,内核寄存器(如 R0-R15、PSP、MSP)会丢失,但外设寄存器(如 USART、SPI、GPIO 的配置)会保持。

具体来说:

  • SRAM1 和 SRAM2:在 Stop0 和 Stop1 下全部保持。Stop2 下,你可以通过 PWR_CR2 寄存器的 SRAM2PDS 位,让 SRAM2 掉电。
  • 备份 SRAM(Backup SRAM):只要 VBAT 有电,它就一直在。我习惯把关键数据(如校准参数、设备状态)放这里。
  • 外设寄存器:全部保持。但要注意,有些外设(如 RTC、LPTIM、IWDG)在停机模式下还能继续跑,它们的寄存器当然也保持。
注意: 我曾经犯过一个错——在进入 Stop2 前,把数据存在了 SRAM2,然后配置了 SRAM2 掉电。醒来后数据全乱了。后来我改用 SRAM1 存关键数据,SRAM2 只放临时缓存。记住:掉电的 SRAM 区域,数据就是丢了,别指望它还在。

4.3 如何配置停机模式

配置过程其实不复杂,我习惯按这几步走:

  1. 选择电压调节器模式:通过 PWR_CR1 的 LPDS 和 LPR 位,决定是保持电压调节器开启(Stop0),还是进入低功耗模式(Stop1/Stop2)。
  2. 配置唤醒源:使能你需要的唤醒中断(如 EXTI、RTC、LPTIM)。
  3. 执行 WFI 或 WFE 指令:进入停机模式。

下面是一个进入 Stop2 模式的代码示例:

// 进入 Stop2 模式
void Enter_Stop2_Mode(void)
{
    // 1. 配置 PWR 寄存器
    //    - LPDS = 1:低功耗调节器
    //    - LPR = 1:低功耗调节器在停机模式下关闭
    //    - SRAM2PDS = 1:SRAM2 掉电(可选)
    HAL_PWR_Config(PWR_LOWPOWERREGULATOR_ENABLE | PWR_STOPENTRY_WFI);
    
    // 2. 使能唤醒源(例如 RTC 闹钟)
    HAL_RTC_SetAlarm_IT(&hrtc, &sAlarm, FORMAT_BIN);
    
    // 3. 进入停机模式
    HAL_PWR_EnterSTOPMode(PWR_LOWPOWERREGULATOR_ENABLE, PWR_STOPENTRY_WFI);
    
    // 4. 唤醒后重新配置系统时钟
    SystemClock_Config();
}
关键点: 唤醒后,系统时钟默认使用 HSI。如果你之前用的是 HSE 或 PLL,必须在唤醒后重新配置时钟。我见过有人忘了这一步,结果外设跑在错误频率上,通信全乱套。

4.4 三种模式的选择策略

怎么选?我总结了一个简单的决策流程:

停机模式选择决策流程 开始选择 唤醒时间 < 3 µs? 是 Stop0 否 唤醒时间 < 8 µs? 是 Stop1 否 Stop2 说明:唤醒时间包括从唤醒事件到 CPU 开始执行第一条指令的时间。 实际值受系统时钟配置和外设状态影响,建议实测确认。

这个流程我用了好几年,基本没出过问题。但有一点要提醒:唤醒时间不是固定的。它受系统时钟配置、外设状态、甚至温度影响。我建议你在最终产品上实测一下,别光看数据手册。

我的习惯: 在开发阶段,我会把三种模式都写进代码,通过一个宏定义切换。这样调试时先用 Stop0 保证功能正常,最后再切到 Stop2 优化功耗。省得来回改代码。

4.5 避坑指南

最后分享几个我踩过的坑:

  • 唤醒后时钟没配好:进入停机模式前,如果你用的是 HSE 或 PLL,唤醒后必须重新配置。我建议把时钟配置封装成一个函数,进入停机模式前和唤醒后都调用一次。
  • 外设没关干净:有些外设(如 ADC、DAC)在停机模式下会漏电。我习惯在进入停机模式前,把所有不用的外设时钟都关掉。
  • 中断优先级没设对:唤醒中断的优先级如果设得太低,可能会被其他中断抢占,导致唤醒失败。我一般把唤醒中断设为最高优先级。
曾经踩过的坑: 有一次,我在 Stop2 模式下用 RTC 闹钟唤醒,结果死活醒不过来。查了半天,发现是 RTC 的时钟源配成了 LSE,但 LSE 的晶振没起振。后来换成 LSI 就好了。记住:唤醒源的时钟必须稳定,否则它根本不会产生唤醒事件。

嗯,停机模式的内容就这些。记住一句话:选模式看唤醒时间,配寄存器看保持策略,调试时先测唤醒再测功耗。这样基本不会出大问题。