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 如何配置停机模式
配置过程其实不复杂,我习惯按这几步走:
- 选择电压调节器模式:通过
PWR_CR1的LPDS和LPR位,决定是保持电压调节器开启(Stop0),还是进入低功耗模式(Stop1/Stop2)。 - 配置唤醒源:使能你需要的唤醒中断(如 EXTI、RTC、LPTIM)。
- 执行 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 三种模式的选择策略
怎么选?我总结了一个简单的决策流程:
这个流程我用了好几年,基本没出过问题。但有一点要提醒:唤醒时间不是固定的。它受系统时钟配置、外设状态、甚至温度影响。我建议你在最终产品上实测一下,别光看数据手册。
我的习惯: 在开发阶段,我会把三种模式都写进代码,通过一个宏定义切换。这样调试时先用 Stop0 保证功能正常,最后再切到 Stop2 优化功耗。省得来回改代码。
4.5 避坑指南
最后分享几个我踩过的坑:
- 唤醒后时钟没配好:进入停机模式前,如果你用的是 HSE 或 PLL,唤醒后必须重新配置。我建议把时钟配置封装成一个函数,进入停机模式前和唤醒后都调用一次。
- 外设没关干净:有些外设(如 ADC、DAC)在停机模式下会漏电。我习惯在进入停机模式前,把所有不用的外设时钟都关掉。
- 中断优先级没设对:唤醒中断的优先级如果设得太低,可能会被其他中断抢占,导致唤醒失败。我一般把唤醒中断设为最高优先级。
曾经踩过的坑: 有一次,我在 Stop2 模式下用 RTC 闹钟唤醒,结果死活醒不过来。查了半天,发现是 RTC 的时钟源配成了 LSE,但 LSE 的晶振没起振。后来换成 LSI 就好了。记住:唤醒源的时钟必须稳定,否则它根本不会产生唤醒事件。
嗯,停机模式的内容就这些。记住一句话:选模式看唤醒时间,配寄存器看保持策略,调试时先测唤醒再测功耗。这样基本不会出大问题。