4. 电源管理模式深度解析:Run、Sleep、Low-power Run、Stop 0/1/2、Standby、Shutdown 模式切换与实测
各位做电池项目的朋友,电源管理这块儿,说白了就是跟功耗较劲。STM32L4 给了我们七种模式,从跑得最快的 Run 到几乎断电的 Shutdown。怎么选?什么时候切?切换代价多大?这章我带你一个一个摸清楚。
核心原则:性能越高,功耗越大;睡越死,唤醒越慢。没有银弹,只有权衡。
4.1 七种模式速览
先看一张总表,心里有个谱。我习惯把这七种模式分成三档:干活档(Run、Low-power Run)、打盹档(Sleep、Stop 0/1/2)、死睡档(Standby、Shutdown)。
| 模式 | CPU 状态 | SRAM 保留 | 典型电流 | 唤醒源 | 唤醒时间 |
|---|---|---|---|---|---|
| Run | 运行 | 全部 | ~100 µA/MHz | — | — |
| Sleep | 暂停 | 全部 | ~50 µA/MHz | 任何中断 | ~5 个周期 |
| Low-power Run | 降频运行 | 全部 | ~8 µA | — | — |
| Stop 0 | 停止 | 全部 | ~100 µA | 任意 EXTI | ~5 µs |
| Stop 1 | 停止 | 全部 | ~3 µA | 任意 EXTI | ~5 µs |
| Stop 2 | 停止 | 部分 | ~1 µA | 有限 EXTI | ~6 µs |
| Standby | 断电 | 2 KB | ~0.3 µA | WKUP 引脚、RTC | ~50 µs |
| Shutdown | 断电 | 无 | ~0.02 µA | WKUP 引脚 | ~150 µs |
看到没?从 Stop 0 到 Stop 2,电流差了 100 倍,但唤醒时间几乎没变。这就是 L4 的厉害之处。
4.2 干活档:Run 与 Low-power Run
Run 模式没啥好说的,就是正常跑。但要注意,L4 在 Run 模式下可以动态调频调压。我有个项目,平时跑 80 MHz,空闲时降到 2 MHz,电流从 8 mA 直接掉到 200 µA。这个技巧后面章节会细讲。
Low-power Run 是个好东西。它让 CPU 还能跑,但只能跑 2 MHz,电压也降到最低。适合什么场景?比如你正在做 ADC 采样,采样率不高,但又不想进 Stop 模式来回折腾。
我的习惯:如果任务能在 1 ms 内干完,我倾向于进 Stop 模式;如果任务要持续几十毫秒,Low-power Run 更划算。因为进 Stop 再出来,那几十微秒的唤醒时间也是成本。
4.3 打盹档:Sleep 与 Stop 0/1/2
Sleep 模式最简单,CPU 停了,外设还在跑。适合那种外设自己干活、CPU 等着收结果的场景。比如 SPI 发完一帧数据,CPU 睡过去,DMA 传完再叫醒它。
Stop 模式才是重头戏。L4 给了三个版本,我当年第一次看到时也懵了。其实区别就两点:稳压器状态和SRAM 保留范围。
- Stop 0:主稳压器开着,所有 SRAM 都保留。唤醒最快,但功耗最高。
- Stop 1:主稳压器关了,用低功耗稳压器。所有 SRAM 保留,但功耗降了一个数量级。
- Stop 2:低功耗稳压器,但只保留前 64 KB SRAM。后 64 KB 断电。
我在一个传感器节点项目里,数据量不大,就用了 Stop 2。把关键变量放在前 64 KB,后 64 KB 放临时缓冲区,反正唤醒后重新初始化就行。电流从 3 µA 降到了 1 µA,电池寿命多了半年。
注意:Stop 2 模式下,不是所有 EXTI 线都能唤醒。只有 PWR 唤醒引脚和部分 RTC 事件可以。设计时一定要看数据手册的唤醒源表格,别到时候叫不醒。
4.4 死睡档:Standby 与 Shutdown
这两个模式,CPU 彻底断电了。区别在于:Standby 还留了 2 KB 的备份 SRAM 和 RTC,Shutdown 连 RTC 都关了,只剩几个唤醒引脚还能工作。
我曾经做过一个产品,用户一年才换一次电池。平时就是 Shutdown,每天 RTC 闹钟唤醒一次,采集数据、发送、再睡回去。0.02 µA 的待机电流,几乎可以忽略不计。
但代价是什么?唤醒后相当于复位,所有变量都得重新初始化。所以,Standby 适合需要保留少量状态(比如计数器、校准值)的场景,Shutdown 适合那种「醒来后从头再来」的场景。
4.5 模式切换实战代码
光说不练假把式。下面给几个最常用的切换代码片段。注意,切换前一定要关掉不用的外设时钟,否则电流降不下去。
进入 Stop 2 模式
// 进入 Stop 2 模式
// 1. 关掉不用的外设时钟
__HAL_RCC_GPIOA_CLK_DISABLE();
__HAL_RCC_GPIOB_CLK_DISABLE();
// ... 其他外设
// 2. 配置 Stop 模式等级
HAL_PWREx_EnableLowPowerRunMode(); // 可选,如果之前是 Low-power Run
HAL_PWREx_EnterSTOP2Mode(PWR_STOPENTRY_WFI);
// 3. 醒来后恢复时钟
SystemClock_Config(); // 重新配置系统时钟
进入 Standby 模式
// 进入 Standby 模式
// 1. 使能唤醒引脚(以 PA0 为例)
HAL_PWR_EnableWakeUpPin(PWR_WAKEUP_PIN1);
// 2. 清除唤醒标志
__HAL_PWR_CLEAR_FLAG(PWR_FLAG_WU);
// 3. 进入 Standby
HAL_PWR_EnterSTANDBYMode();
// 4. 醒来后从复位向量表重新执行
进入 Shutdown 模式
// 进入 Shutdown 模式
// 1. 使能唤醒引脚
HAL_PWR_EnableWakeUpPin(PWR_WAKEUP_PIN1);
// 2. 清除标志
__HAL_PWR_CLEAR_FLAG(PWR_FLAG_WU);
// 3. 进入 Shutdown
HAL_PWREx_EnterSHUTDOWNMode();
// 4. 醒来后也是复位,但 RTC 不保留
避坑指南:我曾经在进入 Standby 前忘了关 ADC,结果待机电流多了 2 µA。查了两天才发现,ADC 的校准电路在 Standby 模式下还在耗电。所以,进低功耗前,把所有模拟外设都关掉,包括 ADC、DAC、OPAMP、COMP。
4.6 实测数据对比
下面是我用 L476RG 开发板实测的数据。环境:室温 25°C,VDD = 3.3 V,所有 GPIO 配置为模拟输入(最省电)。
| 模式 | 实测电流 | 唤醒时间 | 备注 |
|---|---|---|---|
| Run (80 MHz) | 8.2 mA | — | Flash 预取开启 |
| Sleep (80 MHz) | 4.1 mA | ~0.1 µs | 外设时钟全开 |
| Low-power Run (2 MHz) | 8.5 µA | — | 内部 RC 振荡器 |
| Stop 0 | 112 µA | 4.8 µs | 主稳压器开启 |
| Stop 1 | 3.2 µA | 5.1 µs | 低功耗稳压器 |
| Stop 2 | 1.1 µA | 5.6 µs | 部分 SRAM 断电 |
| Standby | 0.35 µA | 48 µs | RTC 运行 |
| Shutdown | 0.02 µA | 145 µs | RTC 关闭 |
看到 Stop 0 和 Stop 1 的差距了吗?同样是所有 SRAM 保留,就因为稳压器不同,电流差了 35 倍。所以,除非你需要极快的唤醒响应,否则别用 Stop 0。
4.7 模式切换决策流程
最后,我画了一张决策图,帮你快速选模式。嗯,这个图我每次做新项目都会拿出来看一眼。
说白了,选模式就是回答三个问题:CPU 要不要跑?SRAM 要留多少?唤醒要多快?回答完,答案自然就出来了。
最后一句经验之谈:别在代码里硬编码模式。我习惯在工程里定义一个 power_profile.h,把模式选择、唤醒源、唤醒时间都配成宏。换电池方案时,改几个宏就行,不用动业务逻辑。