1. 低功耗模式概述:STM32 三种睡眠模式的选型与实战
做嵌入式开发,尤其是电池供电的项目,低功耗永远是绕不开的话题。我记得刚入行那会儿,总觉得低功耗就是「把主频降下来、没事就睡一觉」。结果第一个物联网项目,电池撑了不到三天就趴窝了。后来才明白,STM32 的低功耗模式远不止「睡觉」这么简单——你得知道什么时候该浅睡,什么时候该深睡,什么时候干脆「假死」。
这一章,咱们就把 STM32 的三种低功耗模式掰开揉碎。说白了,就是 Sleep、Stop、Standby。你想想看,这三种模式就像人的三种休息状态:打个盹、睡一觉、直接关机。选对了,功耗能降到 μA 级;选错了,外设跑不起来,中断唤不醒,调试的时候能把你逼疯。
1.1 三种模式的核心差异
先看一张表,把它们的功耗、唤醒源、保留状态一次性说清楚。我习惯把这表贴在工位上,每次选型前扫一眼,基本不会翻车。
| 特性 | Sleep(睡眠) | Stop(停止) | Standby(待机) |
|---|---|---|---|
| CPU 时钟 | 关闭 | 关闭 | 关闭 |
| SRAM / 寄存器 | 全部保留 | 全部保留 | 仅备份域保留 |
| 外设时钟 | 可配置(WFI/WFE) | 关闭(可配置部分保持) | 全部关闭 |
| 典型功耗(@72MHz) | ~5 mA | ~20 μA | ~2 μA |
| 唤醒源 | 任意中断 / 事件 | EXTI、RTC、I2C、USART 等 | WKUP 引脚、RTC、NRST |
| 唤醒时间 | ~1 μs | ~5 μs | ~50 μs(复位启动) |
| 代码是否继续执行 | 是(中断返回后) | 是(中断返回后) | 否(从头启动) |
关键点:Sleep 模式只是停了 CPU 时钟,外设还能跑。Stop 模式连 PLL、HSE 都关了,但 SRAM 内容还在。Standby 模式最狠——除了备份域,其他全部断电,唤醒等于复位。
1.2 Sleep 模式:最轻量的「打盹」
Sleep 模式适合那种「我需要频繁响应中断,但大部分时间在等事件」的场景。比如按键扫描、传感器数据轮询。我有个项目做手持遥控器,按键按下才发数据,平时就睡在 Sleep 模式里。功耗从 20 mA 降到了 5 mA 左右,电池续航直接翻倍。
进入 Sleep 有两种方式:WFI(Wait For Interrupt)和 WFE(Wait For Event)。区别很简单——WFI 等中断来了才醒,WFE 等事件来了就醒。事件比中断轻量,不需要进 ISR,适合那种「我只想继续执行,不想处理中断」的情况。
// 进入 Sleep 模式(WFI 方式)
__WFI(); // 执行完这条,CPU 就睡了
// 进入 Sleep 模式(WFE 方式)
__WFE(); // 事件来了自动醒,继续往下跑
我的习惯:如果唤醒后需要立即处理数据,用 WFI;如果只是等一个标志位变化,用 WFE。WFE 能省掉一次中断进出栈的开销,虽然不大,但积少成多。
1.3 Stop 模式:深度睡眠,但留一手
Stop 模式是我用得最多的。它把主时钟全关了,但 SRAM 和寄存器内容都还在。唤醒后能接着跑,不用重新初始化。适合那种「我需要低功耗,但外设还得偶尔工作」的场景,比如 RTC 定时唤醒、串口空闲中断唤醒。
这里有个坑——进入 Stop 模式前,一定要把用到的外设时钟配置好。我曾经在调试一个 NB-IoT 模块时,进入 Stop 前忘了使能 RTC 时钟,结果 RTC 闹钟根本唤不醒 MCU。查了两天才发现,RTC 的时钟源在 Stop 模式下被切断了。
// 进入 Stop 模式(带 RTC 唤醒)
HAL_RTCEx_SetWakeUpTimer_IT(&hrtc, 4000, RTC_WAKEUPCLOCK_RTCCLK_DIV16);
HAL_PWR_EnterSTOPMode(PWR_LOWPOWERREGULATOR_ON, PWR_STOPENTRY_WFI);
// 唤醒后,需要重新配置系统时钟
SystemClock_Config();
注意:Stop 模式唤醒后,系统时钟默认使用 HSI。如果你的外设需要 HSE 或 PLL,必须在唤醒后手动重新配置时钟树。我见过有人忘了这一步,USART 波特率直接跑飞,数据全乱码。
1.4 Standby 模式:真正的「关机」
Standby 模式是最省电的,但代价也最大——唤醒等于复位,所有未保存在备份域的数据都会丢失。适合那种「几个月才唤醒一次」的场景,比如水表、烟感、资产追踪器。
我记得有个项目做智能门锁,电池要撑两年。平时就睡 Standby,每天定时唤醒一次上报状态。唤醒后初始化、发数据、再睡回去,整个过程不到 100 ms。平均功耗做到了 3 μA 以下,两节 AA 电池真的撑了两年多。
// 进入 Standby 模式
HAL_PWR_EnableWakeUpPin(PWR_WAKEUP_PIN1);
HAL_PWR_EnterSTANDBYMode();
// 唤醒后,从 main 函数重新开始执行
避坑指南:我曾经在 Standby 模式下想保留一个计数值,结果忘了把它存到备份寄存器里。唤醒后计数值清零,整个逻辑全乱了。后来我养成了习惯——所有需要保留的数据,要么写备份寄存器,要么写 Flash。
1.5 应用场景选择:一张图帮你决策
下面这张图是我自己画的决策流程。每次拿到新项目,先跑一遍这个流程,基本就知道该用哪种模式了。
1.6 功耗对比:数据说话
光说理论不够,咱们看一组实测数据。这是我用 STM32L476 开发板测的,主频 80 MHz,所有外设默认关闭。你感受一下差距。
| 模式 | 典型功耗 | 唤醒时间 | 适合场景 |
|---|---|---|---|
| Run(运行) | ~50 mA | — | 正常处理 |
| Sleep | ~5 mA | ~1 μs | 按键扫描、轮询 |
| Stop(LP 稳压器) | ~20 μA | ~5 μs | RTC 定时、串口唤醒 |
| Standby | ~2 μA | ~50 μs | 电池供电、长期休眠 |
我的建议:如果项目对功耗要求不高(比如插电设备),用 Sleep 就够了。如果电池供电且需要频繁唤醒,用 Stop。如果电池供电且唤醒间隔超过 1 分钟,果断上 Standby。别为了省那几 μA 把系统搞复杂了——调试成本也是成本。
1.7 本章小结
三种模式,三种取舍。Sleep 保性能,Stop 保上下文,Standby 保功耗。选型的时候,先问自己三个问题:
- 唤醒频率多高?——高频用 Sleep,低频用 Standby。
- 数据能不能丢?——不能丢用 Stop,能丢用 Standby。
- 唤醒后要做什么?——接着跑用 Stop,重新初始化用 Standby。