3、低功耗模式全景:STM32睡眠、停止、待机模式详解与唤醒源

做低功耗设计,说白了就是跟芯片的“休息方式”打交道。STM32 不像普通单片机只有一种睡法,它提供了三种深度不同的睡眠模式。选对了,功耗能压到微安级;选错了,要么醒不过来,要么功耗白降。

我个人习惯把这三模式比作“打盹”、“深睡”和“关机”。咱们一个一个拆开看。

3.1 三种低功耗模式的核心差异

先看一张总览表,心里有个底:

特性 睡眠模式 (Sleep) 停止模式 (Stop) 待机模式 (Standby)
CPU 状态 停止 停止 断电
SRAM 状态 保持 保持 丢失
寄存器状态 保持 保持 丢失
外设时钟 可关闭 全部关闭 全部关闭
典型电流 ~mA 级 ~µA 级 ~nA 级
唤醒时间 ~µs 级 ~10µs 级 ~ms 级
唤醒源 任意中断 EXTI、RTC、I2C、USART 等 WKUP 引脚、RTC、NRST

嗯,这里要注意:待机模式下的 SRAM 数据会全部丢失。如果你有需要保持的变量,必须放在备份寄存器里,或者干脆别用待机模式。

3.2 睡眠模式:最快进出,适合短时等待

睡眠模式是功耗最低的“清醒”状态。CPU 停了,但所有外设时钟还在跑。你想想看,如果你只是等一个 SPI 传输完成,或者等一个定时器溢出,用睡眠模式最合适。

进入方式很简单:执行 WFI 或 WFE 指令就行。HAL 库封装成了:

HAL_PWR_EnterSLEEPMode(PWR_MAINREGULATOR_ON, PWR_SLEEPENTRY_WFI);

我在项目中遇到过一个问题:用睡眠模式时,如果中断优先级配置不当,唤醒后程序会卡在中断里出不来。后来发现是 SLEEPONEXIT 位被意外置位了。这个位会让 CPU 在执行完中断后再次进入睡眠,而不是返回主循环。

我的习惯:在进入睡眠前,先确认所有外设的 DMA 传输已经完成。否则 DMA 还在搬运数据,你这边睡了,数据就丢了。

3.3 停止模式:深度睡眠,保留现场

停止模式是我用得最多的模式。它把 CPU 和所有外设的时钟都关了,但 SRAM 和寄存器内容还在。说白了,就是“按了暂停键”。

进入停止模式的代码:

HAL_PWR_EnterSTOPMode(PWR_LOWPOWERREGULATOR_ON, PWR_STOPENTRY_WFI);

这里有个关键参数:稳压器模式。你可以选主稳压器(MR)或低功耗稳压器(LPR)。LPR 功耗更低,但唤醒时间会变长。我一般做电池供电产品时用 LPR,做响应要求高的产品用 MR。

唤醒停止模式,必须通过 EXTI 中断。RTC 闹钟、外部 GPIO 引脚、甚至 USB 唤醒信号都可以配置成 EXTI 源。

我曾经踩过的坑:停止模式唤醒后,系统时钟会自动切换到 HSI。如果你之前用的是 HSE 或 PLL,必须在唤醒后重新配置时钟。否则外设跑在 8MHz 上,而你以为是 72MHz,时序全乱。

3.4 待机模式:最低功耗,但一切归零

待机模式是 STM32 的“终极省电方案”。除了 RTC 和备份寄存器,整个芯片几乎都断电了。电流可以低到几十纳安。

进入待机模式:

HAL_PWR_EnterSTANDBYMode();

唤醒方式只有三种:

  • WKUP 引脚上升沿(PA0 等,具体看型号)
  • RTC 闹钟或唤醒事件
  • NRST 复位引脚

注意,待机模式唤醒后,芯片会从头执行代码,就像按了复位键一样。所以你必须能在 main() 函数开头判断出是“冷启动”还是“待机唤醒”。

判断方法:

if (__HAL_PWR_GET_FLAG(PWR_FLAG_SB) != RESET) {
    // 这是从待机模式唤醒的
    __HAL_PWR_CLEAR_FLAG(PWR_FLAG_SB);
} else {
    // 这是正常上电复位
}

3.5 唤醒源配置实战

唤醒源配置是低功耗设计的核心。我画了一张图,帮你理清思路:

STM32 低功耗模式与唤醒源关系图 睡眠模式 Sleep 停止模式 Stop 待机模式 Standby 唤醒源组 A 任意中断 任意事件 DMA 传输完成 SysTick 中断 (最快唤醒) 唤醒源组 B EXTI 线 0~15 RTC 闹钟/唤醒 I2C 地址匹配 USART 起始位 USB 唤醒信号 唤醒源组 C WKUP 引脚上升沿 RTC 闹钟/唤醒 NRST 复位引脚 (数据全部丢失) (从头执行代码) 选择模式时,先问自己三个问题:数据需要保持吗?唤醒要多快?功耗要多低? 答案决定了你用 Sleep、Stop 还是 Standby。

3.6 实际项目中的选择策略

我做了几个低功耗项目后,总结了一套选择逻辑:

  1. 如果唤醒时间要求 < 5µs → 用睡眠模式。比如传感器数据采集,每次采集间隔 1ms,睡眠模式足够快。
  2. 如果电池供电,且需要保持数据 → 用停止模式。比如智能手表,大部分时间屏幕熄灭,但 RAM 里的计步数据不能丢。
  3. 如果产品要放一年不充电 → 用待机模式。比如遥控器,平时完全断电,只有按键按下才唤醒。
关键提醒:停止模式和待机模式之间,还有一个“待机 + RTC”的折中方案。RTC 在待机模式下依然可以工作,功耗只增加几百纳安。如果你需要定时唤醒,这个方案性价比极高。

3.7 避坑指南:我踩过的三个雷

讲真,低功耗模式看着简单,实际调试起来坑不少。我分享三个亲身经历:

  • 坑一:GPIO 浮空漏电。 我曾经有个产品,待机电流比手册多了 10µA。查了两天才发现,有个没用的 GPIO 引脚没配置,处于浮空状态。浮空引脚会反复翻转,产生额外功耗。解决方案:把所有未使用的 GPIO 都配置成模拟输入模式。
  • 坑二:RTC 时钟源没配好。 停止模式下,如果 RTC 用的是 LSE 外部晶振,而晶振起振时间太长,会导致唤醒延迟。我后来改用 LSI 内部低速时钟,虽然精度差一点,但唤醒稳定多了。
  • 坑三:调试器影响功耗。 这个很多人不知道。SWD 接口的 SWCLK 和 SWDIO 在调试状态下会持续拉高,导致芯片无法真正进入低功耗模式。量产时一定要把这两个引脚配置成普通 GPIO 或上拉输入。