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 应用场景选择:一张图帮你决策

下面这张图是我自己画的决策流程。每次拿到新项目,先跑一遍这个流程,基本就知道该用哪种模式了。

低功耗模式选型决策流程 开始选型 需要频繁响应中断? 是 否 Sleep 模式 需要保留 SRAM? 唤醒快 是 否 Stop 模式 Standby 模式 选型要点 Sleep:μs 级唤醒,适合高频中断 | Stop:μA 级功耗,保留上下文 | Standby:nA 级功耗,唤醒即复位

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。