1. 低功耗基础概念:STM32的几种低功耗模式

做嵌入式开发,尤其是电池供电的项目,低功耗是绕不开的坎。我记得刚入行那会儿,总觉得让单片机“睡觉”很简单,结果产品做出来,待机电流比预期大了十几倍……嗯,从那以后,我才开始认真研究STM32到底是怎么“睡”的。

STM32的低功耗模式,说白了就是三种:Sleep(睡眠)、Stop(停止)、Standby(待机)。它们就像三个不同深度的睡眠状态,功耗越来越低,但唤醒代价也越来越大。

核心原则:功耗越低,唤醒后需要重新初始化的外设就越多。选型就是在这两者之间找平衡。

1.1 Sleep Mode(睡眠模式)

这是最浅的睡眠。CPU核心时钟停了,但所有外设(比如USART、SPI、定时器)都还在跑。你想想看,就像你躺在床上闭着眼,但耳朵还在听周围的声音。

  • 电流消耗: 典型值在mA级别,具体取决于外设开启数量
  • 唤醒源: 任何中断或事件都能唤醒,包括外设中断、外部中断
  • 唤醒时间: 极快,几乎无延迟(几个CPU周期)
  • 唤醒后状态: 代码从唤醒中断处继续执行,所有寄存器、SRAM内容完全保留

我在项目中用过Sleep模式做“空闲等待”。比如等待串口接收完一帧数据,CPU没必要空转,进Sleep等中断就行。但说实话,这种模式省电效果有限,适合对功耗不敏感、但要求快速响应的场景。

个人习惯: 如果项目里外设还在频繁工作(比如ADC连续采样),我一般不会用Sleep,因为外设耗电可能比CPU还大。这时候Sleep的意义不大。

1.2 Stop Mode(停止模式)

Stop模式就深了一层。CPU停了,所有外设的时钟也停了。但SRAM和寄存器内容还在,因为主电源没断。这就像你关掉房间的灯和空调,但冰箱还在通电。

这里有个关键点:1.2V域供电还在。所以RTC(实时时钟)和备份寄存器可以继续工作,前提是你配置了LSE或LSI作为RTC时钟源。

特性 Stop模式说明
CPU时钟 停止
外设时钟 停止(可配置部分外设保持)
SRAM/寄存器 保留
典型电流 几十µA到几百µA(取决于RTC是否开启)
唤醒源 EXTI中断、RTC闹钟、RTC唤醒事件、I2C/USART等特定外设
唤醒时间 几µs到几十µs(需要恢复HSI/HSE振荡器)

我曾经在一个手持设备上用Stop模式配合RTC闹钟,每秒钟唤醒一次采集传感器数据。电流控制在50µA左右,两节AA电池撑了半年多。嗯,这里要注意:唤醒后系统时钟默认切回HSI,如果你原来用的是HSE+PLL,需要重新配置时钟树。

避坑指南: 我曾经在Stop模式下忘了关闭调试接口(SWD),结果电流死活降不下来。调试时SWD会阻止MCU进入深度睡眠。解决办法是在进入Stop前调用 DBGMCU->CR &= ~(DBGMCU_CR_DBG_STOP)。

1.3 Standby Mode(待机模式)

这是最深度的睡眠。说白了,就是几乎完全断电。1.2V域大部分关闭,SRAM和寄存器内容全部丢失。只有备份域(RTC、备份寄存器、低速振荡器)和唤醒逻辑还在工作。

你想想看,这就像你出门旅行,把家里总闸拉了,只留了一个摄像头(RTC)和门铃(唤醒引脚)。

  • 电流消耗: 典型值在1µA以下(关闭RTC时甚至几百nA)
  • 唤醒源: RTC闹钟、RTC唤醒事件、WKUP引脚上升沿、NRST复位、BOR欠压复位
  • 唤醒时间: 最长,需要芯片重新上电启动(ms级别)
  • 唤醒后状态: 相当于复位,代码从main函数开始执行,SRAM内容丢失

Standby模式是我做超低功耗产品的首选。比如电池供电的温湿度传感器,每10分钟采集一次数据,其余时间进Standby。电流可以做到1.5µA左右,一节CR2032纽扣电池能用两年。

关键区别: Standby唤醒后,程序是从头开始跑的。你需要通过检查 PWR->CSR 寄存器的 WUF 标志位来判断是上电复位还是Standby唤醒,然后决定是否恢复现场。

1.4 三种模式对比与选型依据

我把三种模式的核心差异整理成了一张表,方便你快速对比:

对比项 Sleep Stop Standby
CPU状态 停止 停止 断电
外设状态 运行 停止(可配置) 断电
SRAM/寄存器 保留 保留 丢失
RTC 可选 可选 可选(备份域)
典型电流 mA级 几十µA 1µA以下
唤醒时间 ns级 µs级 ms级
唤醒源数量 多(所有中断) 中等(EXTI+特定外设) 少(RTC+WKUP引脚)
代码恢复方式 中断返回 中断返回 从头执行

选型其实不难,我一般按这个思路来:

  1. 需要快速响应、外设不能停? 用Sleep。比如等待串口数据、按键消抖。
  2. 需要中等功耗、保留现场、定时唤醒? 用Stop。比如传感器周期性采集、低功耗蓝牙广播。
  3. 需要极致省电、唤醒频率很低? 用Standby。比如环境监测、资产追踪、智能表计。

我的经验: 如果项目里需要保留少量数据(比如校准参数、设备ID),但又想用Standby,可以把数据存到备份寄存器里。备份域在Standby模式下不掉电,唤醒后直接读就行,不用重新初始化。

1.5 知识结构总览

下面这张图把三种模式的关系和选型逻辑串起来了,你可以对照着理解:

STM32 低功耗模式选型逻辑 应用需求:功耗 vs 响应速度 Sleep 模式 Stop 模式 Standby 模式 特性 CPU停,外设运行 mA级电流 ns级唤醒 特性 CPU停,外设停 几十µA电流 µs级唤醒 特性 几乎完全断电 1µA以下电流 ms级唤醒 典型场景 串口等待、按键扫描 典型场景 传感器周期采集、BLE广播 典型场景 环境监测、资产追踪 选型口诀:响应快用Sleep,省电用Standby,中间用Stop

1.6 进入与退出模式的基本代码框架

最后,我贴三段最基础的进入代码,让你有个直观印象。具体细节后面章节会展开。

进入Sleep模式:

// 最简单的Sleep,执行完WFI就睡,任意中断唤醒
__WFI();  // Wait For Interrupt

进入Stop模式:

// 配置PWR寄存器,进入Stop模式
HAL_PWR_EnterSTOPMode(PWR_LOWPOWERREGULATOR_ON, PWR_STOPENTRY_WFI);
// 唤醒后需要重新配置系统时钟
SystemClock_Config();

进入Standby模式:

// 清除唤醒标志,使能WKUP引脚(如果使用),然后进入Standby
HAL_PWR_DisableWakeUpPin(PWR_WAKEUP_PIN1);
HAL_PWR_EnableWakeUpPin(PWR_WAKEUP_PIN1);
HAL_PWR_EnterSTANDBYMode();
// 代码不会执行到这里,唤醒后从main重新开始

注意: 进入Standby前,记得把不需要的GPIO全部配置成模拟输入或上拉/下拉,避免引脚浮空导致漏电流。我见过有人没做这一步,Standby电流直接飙到20µA。