说起来有点丢人。去年接了个手持设备项目,客户说"续航越长越好",我拍着胸脯说用STM32L4肯定没问题——结果样机做出来,电池两天就见底了。

后来连着翻车三次才把功耗降下来。写出来给大家避避坑。

第一次翻车:以为进了Stop模式就万事大吉

代码里调了 HAL_PWREx_EnterSTOP2Mode(PWR_STOPENTRY_WFI),电流一测——2.3mA。比正常跑只少了一半。

折腾了半天才找到原因:IO口没处理。

MCU进休眠了,IO口还维持着原来的电平。如果你某个引脚外接了一个上拉电阻到VCC,休眠时这个脚正好是低电平——电流就顺着引脚灌进去了,根本不存在"一休眠就省电"这回事。

// 之前这样写的
HAL_GPIO_WritePin(LED_GPIO_Port, LED_Pin, GPIO_PIN_RESET);
HAL_PWREx_EnterSTOP2Mode(PWR_STOPENTRY_WFI);

// 正确的做法:进休眠前把所有IO设成analog或高阻
for(int i=0; i<16; i++) {
    HAL_GPIO_DeInit(GPIOA, (uint16_t)(1 << i));
}

把所有不用的IO全设成ANALOG模式后,休眠电流从2.3mA降到了8μA。就改了不到十行代码,差了将近三百倍。别问我怎么知道的,拿万用表量了半天的痛你不懂。

第二次翻车:外设吃的电比MCU还多

MCU休眠电流到了8μA,感觉还行了吧?整机一测,待机电流1.8mA。心态当场炸裂。

查了半天,罪魁祸首是一颗LDO。选型图便宜用了颗静态功耗2mA的LDO——比整个MCU休眠时还费电两百多倍。

换成MCP1700,静态电流1.6μA,这才像话。

DC-DC也是一样的坑。轻载时纹波确实大,但如果用带PFM模式的芯片,轻载效率反而比LDO高。我最后搞的方案是:工作时DC-DC供电,休眠时用LDO单独给RTC和备份寄存器供电,中间靠一颗P沟道MOS管切换。

// 关外设电源的顺序也有讲究
HAL_GPIO_WritePin(PWR_EN_GPIO_Port, PWR_EN_Pin, GPIO_PIN_RESET);
HAL_Delay(100);  // 等电容放完电再休眠
HAL_PWREx_EnterSTOP2Mode(PWR_STOPENTRY_WFI);

100ms的延时不是瞎写的。如果外设有大电容,你刚断电就进休眠,外设还没完全掉电,电流会从MCU的IO口往半死不活的外设里漏。

第三次翻车:唤醒之后回不来了

最无语的一次。设备用RTC每分钟唤醒一次采集数据,跑了好几个小时,突然就不唤醒了。看门狗也没配,板子就地躺平。

拿逻辑分析仪抓RTC的WKUP引脚,波形是对的,但MCU就是没反应。翻勘误表才知道——L4系列有个贼坑的bug:如果RTC唤醒中断和某个DMA请求同一时刻触发,中断标志位会被清掉,CPU永远收不到中断信号。

解决办法:别用RTC唤醒中断,让RTC直接触发一个GPIO,用GPIO的外部中断唤醒。虽然绕了点弯路,但可靠太多了。

或者更保险的做法——不管什么唤醒源,先配一个独立看门狗:

void enter_sleep_with_watchdog(void) {
    __HAL_IWDG_RELOAD_COUNTER(&hiwdg);
    HAL_RTCEx_SetWakeUpTimer_IT(&hrtc, 4095, RTC_WAKEUPCLOCK_CK_SPRE_16BITS);
    HAL_PWREx_EnterSTOP2Mode(PWR_STOPENTRY_WFI);
    // 醒来的第一件事
    __HAL_IWDG_RELOAD_COUNTER(&hiwdg);
}

就算主核挂了,IWDG也能把系统硬拉回来。这个习惯后来救过我两次。

几个实测小技巧

测休眠功耗的时候注意万用表量程。mA档测μA级电流,读数会严重偏小。我习惯在电源回路串一个10Ω采样电阻,用示波器看电阻两端的压降反算电流——好处是能抓到瞬态功耗尖峰。

还有PCB漏电流。助焊剂没洗干净,湿度一上来,能白送你几十μA的漏电。上次深圳回南天,好几块板子待机电流莫名飚上去,放烘箱里烤了半天就好了。

低功耗这事没有银弹。先把MCU规格书里每个功耗模式的小字注释啃一遍,再拿万用表挨个排查外设,最后别忘了PCB本身。三步走完,基本能到μA级。

客户后来实测,两节AA电池撑了四个月。虽然离吹的"半年"差了点,至少没翻太大的车。

Logo

智能硬件社区聚焦AI智能硬件技术生态,汇聚嵌入式AI、物联网硬件开发者,打造交流分享平台,同步全国赛事资讯、开展 OPC 核心人才招募,助力技术落地与开发者成长。

更多推荐