第一回:
第一幕:先搞懂“电费花在哪了”(第一性原理)
芯片的功耗(电费)只有两个来源,这是最底层的物理公式:
动态功耗(开关损耗):P=C×V2×fP=C×V2×f。(门电路开关翻转时,给电容充放电的损耗。)
结论:频率(f)越高,耗电越凶。如果不给时钟(停掉f),这部分功耗直接归零。
静态功耗(漏电损耗):P=Ileak×VP=Ileak×V。(即使门电路不动,只要通电(V),晶体管就像漏水的破管子,一直在漏电。)
结论:电压(V)越高,漏得越凶。如果想杜绝漏电,只能物理断电(V=0)。
核心逻辑就一句话:省电 =降频(关时钟)+降压(关电源)。但关得越狠,醒过来就越慢,RAM里的数据也越容易丢。
第二幕:三种模式的物理本质(把芯片当“办公楼”)
把芯片想象成一栋办公楼,CPU是办公楼里的员工,RAM是办公桌上的文件,外设(UART、定时器)是打印机和传真机。
1. Sleep(睡眠)—— “员工打盹,机器照转”
物理操作:只停掉CPU的时钟(把f降为0),但不停电。所有外设(打印机)还在工作,RAM文件完好无损。
本质:动态功耗几乎归零,但静态漏电依然存在(电压没降)。
痛点:只要有任意一个中断(比如按键),员工立马秒醒,继续干活。
适用场景:需要实时响应的任务。比如每隔 1ms 就要处理一次数据,中间的空闲时间就睡,醒来立即干活,延迟低至几个微秒。
2. Stop(停止)—— “全公司熄灯,但文件不许动”
物理操作:不仅关掉CPU时钟,连大部分高频外设时钟(PLL、晶振)也强行掐断。核心电压(Vcore)可能会略微降低,但RAM(文件)保持供电。
本质:动态功耗彻底清零,静态漏电因为降压也小了很多。整个芯片处于“冻结”状态,只有少数几个低速时钟(如RTC)在跑。
痛点:唤醒需要重新“开灯”(等待晶振稳定),需要几十微秒才能恢复。但最大的好处是:唤醒后,程序原地运行,所有变量都在,不需要重新初始化。
适用场景:需要保存现场的间歇性工作。比如传感器采集数据,每分钟采集一次,中间深睡,醒来直接发数据。
3. Standby(待机)—— “拉总闸,文件粉碎,只留看门大爷”
物理操作:直接切断核心电源(Vcore = 0)。RAM里的所有数据全部灰飞烟灭(掉电丢失)。只给备份域(RTC和几个唤醒引脚)供一点微安级别的电。
本质:静态漏电降到物理极限(因为没电了),功耗最低(nA级别)。
痛点:唤醒等于芯片复位重启,程序从头跑,延迟高达几毫秒。所有变量必须重新从Flash读取初始化。
适用场景:超长待机的电池设备。比如遥控器、电子标签,一年按不了几次,平时必须彻底断电。
第三幕:给新手的实战“灵魂三问”(工作中怎么选)
在做项目选型时,不要看电流参数,先问自己三个问题:
“我能忍受多久的唤醒时间?”
< 10μs 选Sleep。
< 1ms 选Stop。
1ms 可以选Standby。
“唤醒后,我还能接受系统复位吗?”
如果要保留现场(比如电机PWM占空比、传感器校准值),千万别用Standby(会丢),只能用Stop或Sleep。
如果每次醒来都是全新开始,果断用Standby,省电效果立竿见影。
“我的外设需要后台干活吗?”
如果进入低功耗后,还需要定时器计数或串口接收数据,只能用Sleep(因为Stop会把外设时钟掐断)。
第四幕:真正的工作“避坑指南”
给小白最值钱的实操建议:
1. 管好你的GPIO(漏电路径杀手)
很多新手测功耗发现比手册高10倍,罪魁祸首是浮空的IO口。
物理原理:浮空IO电压悬浮在1.5V左右,导致CMOS管上下同时微导通,产生穿透电流。
实战操作:进入任意低功耗前,把未使用的IO强制设为模拟输入模式(Analog Mode),或者设为固定电平(高或低)并关闭内部上拉/下拉。
2. 去耦电容的“反噬”
注意:进入Stop或Standby前,主频从几十MHz猛降下来,但外部LDO(低压差稳压器)的响应速度跟不上。
实战操作:进入低功耗的指令后,立刻加一个短的延时(如1ms),等待外部电容放电稳定后再挂起系统,否则唤醒瞬间电压跌落会导致复位。
3. Debug(调试)期间的“假死”
痛点:很多芯片在Sleep/Stop模式下会关闭SWD(调试接口)时钟。如果你在仿真时直接进低功耗,调试器会断开,你再也点不了暂停。
实战操作:开发调试阶段,务必加宏定义。Debug版本永远只进Sleep,不进Stop/Standby;Release版本再启用深度休眠。或者使用
__WFI指令配合DBGMCU寄存器保持调试时钟。
终极总结(给小白的大脑存档)
需要记住这三句话:
Sleep:关机不关屏(只关CPU),随时敲键盘都能唤醒,干活最利索。
Stop:关机又关屏,但内存资料还在,醒来接着刚才的断点写,性价比最高(大部分项目中首选)。
Standby:拔掉电源线,内存全清,想开机必须按电源键重启,极致省电但伤筋动骨。
工作中的万能法则(无脑推荐):
如果你的产品不是手表手环那种极致功耗需求,优先用Stop模式。它兼顾了省电(uA级)和唤醒后的连续性(不用重新初始化复杂的外设)。把Stop玩透了,你再根据唤醒时间的长短,决定是降级到Sleep(更快)还是升级到Standby(更省)。
第二回:
第一性原理复习(一张图看懂省电本质)
芯片的电流(I<sub>total</sub>) =动态电流(I<sub>dynamic</sub> ∝ 频率 f) +静态漏电(I<sub>leak</sub> ∝ 电压 V)。
关时钟(降f)→ 消灭动态功耗。
关电源(降V,甚至V=0)→ 消灭静态漏电。
代价是:关得越狠,唤醒越慢,数据越容易丢。
一、终极对比表(省电量 + 唤醒方式 + 延时 + 现场保留)
可以打印出来贴在工位上的“三字经”:
| 模式 | 省电程度(典型值) | 动/静态原理 | 怎么唤醒(门禁) | 唤醒延时(起床气) | 唤醒后程序从哪跑? |
|---|---|---|---|---|---|
| Sleep | mA 级(节省~60%) | 关CPU时钟,外设时钟照跑,电压不动。漏电依旧。 | 任意中断(EXTI、UART、定时器)或事件。 | 极快(~2~5 μs) | 原地踏步:睡前的下一行代码继续跑。 |
| Stop | μA 级(节省~90%) | 关CPU时钟 +关高频晶振(HSE/HSI),仅留低速时钟(LSI/LSE)。核心电压略微降压。 | 外部中断(按键) +RTC闹钟/唤醒定时器+ 特定外设(低功耗UART/I2C)。 | 中等(~30~50 μs) (等晶振重新起振) | 原地踏步:所有RAM变量完好,醒来继续跑。 |
| Standby | nA 级(节省~99.9%) | 物理拉闸(关Vcore),RAM全丢。只有备份域(RTC)有电。 | 极少:WKUP引脚(按键)、RTC闹钟、NRST复位、IWDG看门狗。 | 极慢(~1~5 ms) (相当于重新上电) | 系统复位:程序从头(main函数)跑,变量需重新初始化。 |
二、实操第一步:睡前准备(如何正确“关灯锁门”)
很多小白直接调用PWR_EnterSTOPMode(),结果测出几百微安,比手册多10倍。因为外设没关干净,GPIO在漏电!
1. 关掉不用的外设时钟(省动态功耗)
进入低功耗前,不要把时钟关闭当成负担,要把它当成“电费账单”。
执行逻辑:凡是醒来一瞬间不需要自启的外设,统统关掉时钟(RCC->APB1ENR / APB2ENR 对应位写0)。
实战建议:不要在进入休眠前一个一个数着关。推荐使用“白名单法”——在初始化时,把所有外设时钟默认设为Disable,只有当前任务用到的外设才开。进入休眠时,只需反初始化(DeInit)当前用过的外设,时钟自然就断了。
2. 管好GPIO(干掉漏电路径!物理硬伤)
这是第一性原理最典型的应用:浮空的GPIO电压在1.5V左右,会导致CMOS管的P管和N管同时微导通,产生从VDD到VSS的穿透电流(几mA!)。
必杀技(二选一):
绝招A(最干净):将所有未使用或已关闭外设的IO口,统统设置为模拟输入模式(GPIO_Mode_AIN)。模拟模式下,内部采样开关断开,数字通路彻底切断,漏电降到nA级。
绝招B(次选):设为推挽输出,并固定输出低电平(或高电平,取决于外部电路),同时关闭内部上拉/下拉电阻。
工作中切记:如果引脚外接了分压电阻或LED,设成输出低电平比输出高电平更省电(因为电流直接灌入芯片地,避免经过内部上拉电阻消耗电源)。
三、实操第二步:进入姿势(关总闸的流程)
正确的进入代码顺序应该是这样的(伪代码逻辑):
(代码部分为AI生成)
c
// 1. 关掉非必要外设时钟(如ADC、SPI、UART等) RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1, DISABLE); // 2. 将对应的GPIO引脚设为模拟输入(关掉漏电) GPIO_InitStruct.Mode = GPIO_MODE_ANALOG; HAL_GPIO_Init(GPIOA, &GPIO_InitStruct); // 3. 清空所有挂起的中断标志位(防止刚睡就被无效中断唤醒) __HAL_RCC_CLEAR_RESET_FLAGS(); // 4. 进入Stop或Standby模式(以Stop为例) HAL_PWR_EnterSTOPMode(PWR_LOWPOWERREGULATOR_ON, PWR_STOPENTRY_WFI); // 执行完这一句,芯片“冻结”,直到唤醒中断到来。
四、实操第三步:醒后收拾(RTC唤醒后的“灾难恢复”)
这是工作中最大的坑!唤醒不等于能正常工作。因为你睡觉时把高频时钟(PLL)关了,醒来后芯片默认跑的是内部低速时钟(MSI或HSI,通常只有几MHz)。
如果你唤醒后直接去跑UART通信(需要115200波特率),算出来的全是乱码,因为时钟频率不对!
1. 必须重配系统时钟(灵魂操作)
在唤醒后的第一行代码(如果是Standby)或中断服务函数里(如果是Stop/Sleep),必须立即调用系统时钟配置函数(如SystemClock_Config()),把PLL、AHB/APB分频系数重新设置回最高主频(如168MHz)。
2. 专门针对“RTC唤醒”的延时处理(致命细节)
RTC唤醒通常使用低速外部晶振(LSE,32.768kHz)或内部低速振荡器(LSI)。
物理硬伤:晶振是机械器件,起振需要时间(几百毫秒到几秒不等)。
工作中经常发生的故障:RTC闹钟唤醒了,但程序去读RTC时间寄存器,发现读出来是0xFFFF或错误值。因为晶振还没稳定,RTC时钟无效!
实战铁律:
在唤醒后的SystemClock_Config()里,如果涉及到重新开启 LSE,必须加入超时等待:
(代码部分为AI生成)
c
// 开启LSE并等待就绪,必须加超时,防止死机 RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_LSE; RCC_OscInitStruct.LSEState = RCC_LSE_ON; if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK) { // 超时处理:如果晶振坏了,切到内部LSI作为RTC时钟源,保证功能不挂死 Error_Handler(); }五、工作中的“动态决策”实战法则
工作中做低功耗选型时,不用纠结数据手册,按这个流程图来拍板:
问:我需要超低功耗(uA以下)吗?
要 → 选Standby(但要做系统复位重初始化,代码要健壮)。
不要 → 往下看。
问:我醒来后要立刻发送大量数据,且不能复位?
要 → 选Stop。核心秘诀:Stop唤醒后,把时钟重配放在中断退出后的第一行,并加入一小段
delay_us(100),等待外部晶振(HSE)完全稳定再去操作外设,否则容易出帧错误。
问:我的唤醒源是RTC每分钟唤醒一次,且设备在户外高温环境?
强烈建议用 Stop + LSI(内部低速时钟)。因为 LSE(外部晶振)在高温或潮湿下容易停振,导致设备永远睡死过去。内部LSI虽然不准,但绝对可靠。用LSI做RTC唤醒,可以省去外部晶振起振的麻烦延时。
终极组合拳(项目中最优解):
平时:用Stop模式(省电,唤醒快,现场保留)。
电量低于10%时:主动切到Standby(极致省电,但牺牲响应速度,提醒用户赶紧充电)。
调试阶段:强制设为Sleep(因为Stop会关掉SWD调试接口,导致无法烧录,必须按复位键卡时间点,非常痛苦)。
定心丸:
不要怕进低功耗。记住:关外设时钟就像出门关空调,设GPIO模拟输入就像拔掉所有没用的插头,唤醒重配时钟就像回来先把总电闸推上去,RTC延时等待就像等灯泡钨丝预热。
工作中,90%的低功耗故障(唤醒后死机、通信乱码、电流超标)都出在“时钟重配”和“GPIO浮空”这两个点上。
溪云初起日沉阁,山雨欲来风满楼。——许浑《咸阳城东楼》(一作《咸阳城西楼晚眺》)