以前做电赛、嵌入式比赛的时候,我几乎从来没关心过“功耗”两个字:板子插着 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);
然后就没了,导致你以为“我只要调这个函数就能低功耗”,结果电流还是很大。
真正的低功耗思路,可以概括为这几步:
-
先回答三个问题:
-
我的设备什么时候必须醒着?(采集、数据处理、通信)
-
我的设备什么时候可以睡?(没事干、等下一周期)
-
睡到什么程度?
-
只是暂停 CPU(Sleep)
-
还是几乎全关(Stop/Standby)
-
-
-
把系统按“状态”划分:
-
工作状态:CPU 跑逻辑 + 必要外设开启
-
休眠状态:尽可能只保留 RTC / 唤醒按键等必要模块
-
-
在每个状态里做对应的优化:
-
工作状态:
-
降低不必要的主频
-
只开必要外设,其他外设时钟全部关掉
-
-
休眠状态:
-
GPIO 一律设置合理模式(大多设为模拟输入)
-
关闭不需要的时钟域
-
进入相应的低功耗模式(Sleep/Stop/Standby)
-
-
-
明确唤醒路径:谁来叫醒我?
-
周期性任务 → 用 RTC 唤醒
-
某些事件触发 → 用外部中断(按键)唤醒
-
多种唤醒源同时存在时,要设计好优先级与处理逻辑
-
五、一个通用的低功耗主循环框架(可直接复用)
下面给出一个基于 Stop 模式 + RTC 唤醒 的一类通用骨架代码,你后面做任何低功耗项目都可以从这套模板改起。
注意:具体函数名(比如
SystemClock_Config、MX_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),进入下一轮循环 */
}
}
六、低功耗调试的一些小建议
很多同学第一次玩低功耗时,常见几个痛点:
-
没有电流表 / 万用表,根本不知道自己省电没
-
建议:哪怕买个几十块钱的万用表,也能看出“几十 mA”和“几 mA / 几百 μA”的区别。
-
条件允许的话,最好串一个小电阻,用示波器看电流波形变化(进睡眠、醒来时的动态)。
-
-
进入低功耗后程序不再跑了,看不到 log,以为死机
-
很正常:你都关时钟 / 关串口了,当然没有 log
-
调试低功耗建议先:
-
打大量 log 验证逻辑
-
确认进入/退出低功耗的时序
-
确认唤醒中断是否正常触发
-
-
-
以为进了低功耗电流一定会大幅下降,结果变化不大
-
往往是:GPIO / 外设 / 时钟没配置好
-
391

被折叠的 条评论
为什么被折叠?



