STM32 低功耗设计入门:搞懂 Sleep / Stop / Standby 和功耗思维

以前做电赛、嵌入式比赛的时候,我几乎从来没关心过“功耗”两个字:板子插着 USB,一直亮着灯、串口疯狂打印,很爽。
但只要你想做一个电池供电、能长期跑在现场的设备,低功耗就是避不开的坑。
这篇文章就从零开始,带你把 STM32 的低功耗模式和整体思路捋清楚。

一、为什么要在意低功耗?比赛项目 和 真正设备 的区别

在学校做项目 / 比赛的时候,我们的默认状态大概是这样:

  • 板子插着 USB,5V / 3.3V 源源不断

  • OLED 常亮、LED 常闪、串口不停打 log

  • while(1) 死循环跑满 CPU

这些都没问题,只要评委看到作品正常跑就行了。

但是,一旦你想做的东西是:

  • 电池供电的无线传感节点

  • 放甲醛/烟雾传感器在房间里长期监测

  • 某种遥控器、开关、手持设备,只是偶尔按一下

  • 甚至你想做一个“真正能卖”的产品

你就会面临一个非常现实的问题:

电池撑不了多久,设备要么频繁换电池,要么几天就趴窝。

解决方案只有一个:
让 MCU 和整个系统在“绝大部分时间里都在睡觉”,只在必要的时候醒来干活。

所以接下来,我们先搞清楚:
MCU 的电是哪里花掉的?

二、STM32 的功耗都花在哪几个地方?

想做低功耗,千万不要一上来就写:

HAL_PWR_EnterSTOPMode(...);

你得先有一个功耗来源的全局观,大致可以分成四类:

1. CPU + 时钟频率

  • CPU 每执行一条指令,内部寄存器、总线、触发器都在翻转

  • 主频越高,翻转越快,动态功耗越大

  • 有的系列还支持调节内核电压,电压越高也越耗电

所以一个典型的低功耗思路:

在能跑满需求的前提下,尽量降低频率,不需要 168MHz 就别一直顶着跑。

2. 外设模块(ADC / TIM / USART / SPI / I2C …)

很多人以为“我没在用某个外设,就不会耗电”,但实际情况是:

  • 只要你在 RCC 里给它打开了时钟,它就会有静态/动态功耗

  • 哪怕你没用 ADC 采样,只要 ADC 时钟开着,它也在吃电

典型浪费场景:

  • CubeMX 初始化了一堆外设,你实际没用,但时钟全开着

  • 一个定时器被配置了 PWM,但你只是为了测试点亮个 LED,后来忘记关

3. GPIO 引脚状态

GPIO 也会耗电,尤其是这些情况:

  • 悬空输入:引脚浮着,外部干扰让电平随机跳,高频抖动→ 反复翻转 → 动态功耗

  • 错误上下拉:外部电路 + 内部上下拉组合成电流回路

  • 强驱动对抗:你内部推挽输出高,外部又硬拉低,压根是短路……

低功耗设计里一个常见操作就是:

所有不用的引脚统一配置为 模拟输入(Analog),这样内部数字输入电路关闭,泄漏极小。

4. 片上模拟模块 / LDO / 其它电源相关

  • 有些 STM32 系列内部有 LDO、内部电压参考、比较器等模拟模块

  • 这些模块如果不关,也会有基础电流消耗

简单总结一句:

低功耗绝对不是只“睡 CPU”那么简单,而是一个系统级的事。

接下来就轮到本文主角:
STM32 里那几个官方提供的“睡眠模式”。

三、STM32 的三大低功耗模式:Sleep / Stop / Standby

不同系列的细节会有差异(F1、F4、L 系列略有不同),但整体概念是通用的。
在多数 STM32 里,低功耗模式大致有这几种:

  • Sleep 模式

  • Stop 模式

  • Standby 模式

下面用“人话 + 表格”帮你捋清楚。


3.1 Sleep 模式:让 CPU 打个盹

特点:

  • CPU 内核停止执行指令

  • 大部分外设继续运行(如果它们的时钟没关)

  • 系统时钟树基本不变

  • 任意能产生中断的外设都可以把它唤醒(EXTI、TIM、USART…)

可以理解为:

“我先暂停一下主循环,外设你们先自己玩,有事就中断叫醒我。”

使用场景:

  • CPU 计算任务不重,大部分时间在等外设

  • 比如等串口数据、等采样结果、等按键

优点:

  • 唤醒非常快

  • 使用简单,不需要重新配置系统时钟

缺点:

  • 节电效果有限:外设都在跑,主时钟也没关,功耗下降不算太多


3.2 Stop 模式:让大部分人睡,只留几个门卫

特点(以常见系列为例):

  • 关闭主时钟 (HCLK) 和多数 PLL / HSE 时钟

  • SRAM 和寄存器内容保持

  • 一般只保留低速时钟(LSI/LSE),给 RTC 等模块使用

  • 唤醒源有限:RTC 闹钟、外部中断引脚、某些低功耗外设中断等

  • 唤醒后通常需要重新配置系统时钟(重新启用 HSE/PLL)

可以理解为:

“整个宿舍熄灯睡觉,只留阿姨 + 楼道灯 + 闹钟。
闹钟响了 / 别人拍门了,大家再起床干活。”

使用场景:

  • 电池供电的周期采集设备

    • 大部分时间什么都不做

    • 每隔 N 秒醒一次:采集、处理、发数据 → 再睡

  • 无线传感节点、简易 IoT 设备

优点:

  • 功耗相比 Sleep 大幅降低

  • 能保持 SRAM & 寄存器:醒来继续执行后面的代码,不用重启程序

缺点:

  • 唤醒流程比 Sleep 复杂一点,尤其是需要恢复时钟

  • 可用的外设非常有限,唤醒条件要提前设计好


3.3 Standby 模式:彻底睡死,等人把你拍醒

特点:

  • 几乎所有内部模块关闭

  • SRAM 数据丢失,只有电池供电域 / Backup 寄存器 / RTC 可选保留

  • 唤醒方式类似“重新上电”:WKUP 引脚 / RTC 闹钟 / 复位等

  • 唤醒后程序从 Reset 开始执行(main 从头来)

可以理解为:

“关机睡觉,第二天闹钟响了再开机。”

使用场景:

  • 遥控器 / 开关 / 门铃这类:

    • 平时基本不干活

    • 只有按键 / 触发时才醒一会

  • 需要 极长待机时间 的设备,追求 μA 级甚至更低的电流

优点:

  • 功耗最低,可以做到 μA 级甚至更低(具体看芯片系列)

  • 非常适合“长期待机,偶尔工作”的场景

缺点:

  • 醒来相当于重新上电,程序要从头跑

  • 需要你在程序里自行通过 Backup 寄存器 / Flash 保存必要状态


3.4 模式对比表(可直接放到 优快云)

你可以放一个表格总结一下:

模式CPU 状态SRAM/寄存器唤醒后从哪执行唤醒方式功耗等级典型应用场景
Run运行保留最高正常逻辑处理
Sleep停止保留接着往下执行任意可产生中断的外设CPU 空闲,但外设要跑
Stop停止保留接着往下执行RTC/EXTI/低功耗外设中断周期采集的电池节点
Standby停止丢失相当于重新上电WKUP 引脚 / RTC / 重置最低遥控器、超长待机、极低功耗设备

四、低功耗设计的思路:不是加一行函数那么简单

很多教程会直接告诉你:

HAL_PWR_EnterSTOPMode(PWR_MAINREGULATOR_ON, PWR_SLEEPENTRY_WFI);

然后就没了,导致你以为“我只要调这个函数就能低功耗”,结果电流还是很大。

真正的低功耗思路,可以概括为这几步:

  1. 先回答三个问题:

    • 我的设备什么时候必须醒着?(采集、数据处理、通信)

    • 我的设备什么时候可以睡?(没事干、等下一周期)

    • 睡到什么程度?

      • 只是暂停 CPU(Sleep)

      • 还是几乎全关(Stop/Standby)

  2. 把系统按“状态”划分:

    • 工作状态:CPU 跑逻辑 + 必要外设开启

    • 休眠状态:尽可能只保留 RTC / 唤醒按键等必要模块

  3. 在每个状态里做对应的优化:

    • 工作状态:

      • 降低不必要的主频

      • 只开必要外设,其他外设时钟全部关掉

    • 休眠状态:

      • GPIO 一律设置合理模式(大多设为模拟输入)

      • 关闭不需要的时钟域

      • 进入相应的低功耗模式(Sleep/Stop/Standby)

  4. 明确唤醒路径:谁来叫醒我?

    • 周期性任务 → 用 RTC 唤醒

    • 某些事件触发 → 用外部中断(按键)唤醒

    • 多种唤醒源同时存在时,要设计好优先级与处理逻辑

五、一个通用的低功耗主循环框架(可直接复用)

下面给出一个基于 Stop 模式 + RTC 唤醒 的一类通用骨架代码,你后面做任何低功耗项目都可以从这套模板改起。

注意:具体函数名(比如 SystemClock_ConfigMX_RTC_Init)需要根据你自己的 CubeMX 工程调整。

int main(void)
{
    HAL_Init();
    SystemClock_Config();   // 初始化系统时钟(HSE + PLL 等)

    MX_GPIO_Init();
    MX_RTC_Init();
    // 这里初始化你需要的外设,比如 ADC / USART / I2C 等
    // MX_USART2_UART_Init();
    // MX_ADC1_Init();
    // ...

    while (1)
    {
        /* 1. 执行本周期任务:采集 + 处理 + 上报 */
        Do_Measurement();      // 采集传感器
        Do_Processing();       // 算法处理
        Do_Communication();    // 串口 / 无线发送数据

        /* 2. 进入低功耗前的准备 */
        Prepare_Gpio_For_LowPower();  // 配置不用的 GPIO 为模拟输入等
        Disable_Unused_Peripherals(); // 关掉本周期不再使用的外设

        /* 可选:挂起 SysTick 避免它打断休眠 */
        HAL_SuspendTick();

        /* 3. 进入 Stop 模式,等待中断/RTC 唤醒 */
        HAL_PWR_EnterSTOPMode(PWR_MAINREGULATOR_ON, PWR_SLEEPENTRY_WFI);

        /* 4. 唤醒后恢复系统时钟 */
        SystemClock_Config();  // 再次打开 HSE/PLL,恢复到正常频率
        HAL_ResumeTick();      // 恢复 SysTick

        /* 5. 回到 while(1),进入下一轮循环 */
    }
}

六、低功耗调试的一些小建议

很多同学第一次玩低功耗时,常见几个痛点:

  1. 没有电流表 / 万用表,根本不知道自己省电没

    • 建议:哪怕买个几十块钱的万用表,也能看出“几十 mA”和“几 mA / 几百 μA”的区别。

    • 条件允许的话,最好串一个小电阻,用示波器看电流波形变化(进睡眠、醒来时的动态)。

  2. 进入低功耗后程序不再跑了,看不到 log,以为死机

    • 很正常:你都关时钟 / 关串口了,当然没有 log

    • 调试低功耗建议先:

      • 打大量 log 验证逻辑

      • 确认进入/退出低功耗的时序

      • 确认唤醒中断是否正常触发

  3. 以为进了低功耗电流一定会大幅下降,结果变化不大

    • 往往是:GPIO / 外设 / 时钟没配置好

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

热爱嵌入式的乌鸦

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值