资讯动态

STM32F103 低功耗三档实战:Sleep/Stop/Standby 与漏电排查全流程拆解

发布时间:2026/8/31 5:10:20 来源:尧图企业网站定制
STM32F103 低功耗三档实战Sleep/Stop/Standby 与漏电排查全流程拆解做电池供电的温湿度采集器样机调试完一看待机电流8.4mA跟预期差了三个数量级。第一反应是 MCU 没睡好翻手册、改配置折腾一下午电流纹丝不动。最后拿万用表一段段量才发现罪魁祸首根本不是芯片——板上的 AMS1117 稳压芯片静态电流就有 5mA。把 LDO 换成低静态型号再把 GPIO 浮空引脚收拾干净待机电流掉到 20μA 以下。本文拆解 F103 三档低功耗模式的完整用法以及那次排查换来的漏电清单。一、三档模式功耗越低代价越大F103 的低功耗只有三档Sleep、Stop、Standby。三者不是简单的「越来越省电」而是「省电程度」和「唤醒代价」的组合取舍Sleep 睡眠CPU 停外设时钟照跑任何中断都能唤醒醒来从 WFI 下一条继续。省电有限mA 级但用起来几乎零成本。Stop 停止所有时钟停1.8V 域继续供电SRAM 和寄存器全保留EXTI、RTC 闹钟都能唤醒。典型 ~20μA是「要保数据 要省电」的主流选择。代价是唤醒后时钟回退到 HSI 8MHz必须重新配置。Standby 待机1.8V 域直接断电SRAM 内容全丢只有备份域RTC、备份寄存器活着。典型 ~2μA唤醒只能靠 PA0 上升沿、RTC 闹钟或复位——醒来等于重启从 main 重新跑。选档逻辑很简单任务完成后还要留外设值班 → Sleep要保 SRAM 数据、隔一段时间被唤醒干活 → Stop只做定时上报、醒来全量初始化 → Standby。F103 没有 LPTIM 也没有 tickless别指望超出这三档之外的花样。顺带一提Standby 开着 RTC 走时的话电流从 2μA 涨到 3μA 出头——备份域这点功耗和 1.8V 域断电省的比起来不值一提。二、Sleep最浅的一档用起来几乎零成本Sleep 的进入简单得不像话——一条__WFI()就够。它只停 CPU 核心外设时钟、中断控制器全都照常工作所以任何中断都能把它叫醒醒来直接回到 WFI 下一条指令时钟不用重配、外设不用恢复/* 普通 SleepSLEEPDEEP 必须为 0浅睡 */__WFI();/* 睡到下一个中断到来 *//* 醒来后什么都不用做直接继续 */两个注意点。第一SLEEPDEEP 是粘滞位——如果之前进过 Stop 或 Standby这个位还留在 1 上这时调用 __WFI 会直接睡进深睡唤醒条件全变。想浅睡先清零SCB-SCR ~SCB_SCR_SLEEPDEEP_Msk。第二SysTick 的中断还开着的话它每毫秒叫醒你一次Sleep 实际睡不住——想保持沉睡先SysTick-CTRL 0停掉它或者接受「每毫秒眨一下眼」的伪睡眠。省电效果上Sleep 停在 mA 级外设还在跑适合「等一个外设事件、几分钟内就要醒」的场景真要长时间待机往下走 Stop。三、睡进去容易醒过来有讲究三档都靠 WFI或 WFE进入区别在 SCB 的 SLEEPDEEP 位和 PWR_CR 的 PDDS 位怎么组合。而「唤醒后从哪继续」决定了每档的收尾工作Sleep 唤醒中断服务程序退出后直接回到 WFI 后面时钟没变什么都不用重做——但注意 SysTick 中断还开着的话它每毫秒唤醒一次Sleep 根本睡不住想真睡要么停 SysTick 要么接受周期眨眼。Stop 唤醒断点继续但SYSCLK 已经回退到 HSI 8MHz——HSE 和 PLL 在进 Stop 时被关了。72MHz 下配的串口波特率、延时循环全乱套醒来第一件事就是重配时钟。Standby 唤醒没有「断点」可言整个流程就是一次复位。判断「上电启动」还是「待机唤醒」靠 PWR_CSR 的 SBFStandby 标志位——PWR_GetFlagStatus(PWR_FLAG_SB) 返回 SET 就说明是从 Standby 醒的。四、主案例Stop 模式按键唤醒场景采集器干完活进 StopSRAM 里的数据保留按一下唤醒键PA0 接外部中断醒来继续跑。整体分「睡前三件事」和「醒来两步走」。voidEnter_StopMode(void){/* 睡前三件事 *//* ① 唤醒源PA0 外部中断上升沿 */EXTI_InitTypeDef EXTI_InitStructure;EXTI_InitStructure.EXTI_LineEXTI_Line0;EXTI_InitStructure.EXTI_ModeEXTI_Mode_Interrupt;EXTI_InitStructure.EXTI_TriggerEXTI_Trigger_Rising;EXTI_InitStructure.EXTI_LineCmdENABLE;EXTI_Init(EXTI_InitStructure);NVIC_EnableIRQ(EXTI0_IRQn);/* ② 关掉不用的外设时钟USART1、SPI1 等逐个 DISABLE */RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1|RCC_APB2Periph_SPI1,DISABLE);/* ③ 未用 GPIO 全部配成 Analog堵住引脚漏电 */GPIO_InitTypeDef GPIO_InitStructure;/* 注意排除 PA0AIN 会断开数字输入路径唤醒脚必须留在数字模式 */GPIO_InitStructure.GPIO_PinGPIO_Pin_All~GPIO_Pin_0;GPIO_InitStructure.GPIO_ModeGPIO_Mode_AIN;GPIO_Init(GPIOA,GPIO_InitStructure);GPIO_InitStructure.GPIO_PinGPIO_Pin_All;GPIO_Init(GPIOB,GPIO_InitStructure);GPIO_Init(GPIOC,GPIO_InitStructure);/* 进 Stop低功耗调压器 WFI */PWR_EnterSTOPMode(PWR_Regulator_LowPower,PWR_STOPEntry_WFI);/* ---------- 唤醒后从这里继续 ---------- */SysClock_Reconfig();/* 第一步重配时钟回 72MHz */Periph_Restore();/* 第二步恢复外设时钟与功能 */}PWR_EnterSTOPMode 内部会把 SLEEPDEEP 置 1 再执行 WFI返回后代码从调用处继续——先重配时钟再碰任何外设这是 Stop 唤醒的铁律。voidSysClock_Reconfig(void)/* Stop 唤醒后恢复 72MHz */{RCC_HSEConfig(RCC_HSE_ON);while(RCC_GetFlagStatus(RCC_FLAG_HSERDY)RESET);RCC_PLLCmd(ENABLE);while(RCC_GetFlagStatus(RCC_FLAG_PLLRDY)RESET);RCC_SYSCLKConfig(RCC_SYSCLKSource_PLLCLK);while(RCC_GetSYSCLKSource()!0x08);/* 0x08 PLL 生效 */}一个值得说明的选择PWR_EnterSTOPMode 的第一个参数是调压器模式——PWR_Regulator_ON 保持调压器全速唤醒最快PWR_Regulator_LowPower 让调压器进入低功耗状态电流更低但唤醒要额外多等调压器恢复的时间。对绝大多数应用LowPower 是默认选择对唤醒延迟敏感的比如外部信号唤醒后立刻要采样用 ON 换那几微秒。跑通这条链用万用表串联在电源上看空闲电流从十几毫安掉到几十微安按一下按键立刻回到运行态——四个数量级的差距就是 F103 低功耗的第一份成果。五、Standby最省电但数据全丢某些场景可以接受「醒来重新来」比如只靠 RTC 定时上报的传感器或者按键开机的遥控器。这时 Standby 是正解——把 1.8V 域整个断电SRAM 清零电流压到 2μA 级。voidEnter_StandbyMode(void){RCC_APB1PeriphClockCmd(RCC_APB1Periph_PWR,ENABLE);/* 使能 WKUP 引脚PA0 上升沿唤醒内部上拉 */PWR_WakeUpPinCmd(ENABLE);/* 清掉遗留的唤醒标志避免一进就被叫醒 */PWR_ClearFlag(PWR_FLAG_WU);/* 进入待机函数内部置 PDDS1、SLEEPDEEP1、执行 WFI */PWR_EnterSTANDBYMode();}三个细节第一WKUP 唤醒走的是 PWR 模块的专用唤醒电路不需要配 EXTI——这是它和 Stop 按键唤醒最大的区别第二进之前清一次 WUF 唤醒标志否则上次唤醒的遗留标志可能让你一进就醒第三醒来后程序从 main 开头跑要在 main 最前面用 PWR_FLAG_SB 区分上电还是待机唤醒分流走初始化流程或数据恢复流程——典型写法是 main 开头读一次标志存到全局变量初始化做完再按它决定「要不要从备份寄存器恢复上次的计数、要不要重发没发完的报文」。备份域里那几个备份寄存器就是 Standby 模式下唯一能带话给「下一次开机」的信箱。RTC 闹钟唤醒在 Stop/Standby 都可用LSE 32.768kHz 在深睡时保持RTC 闹钟事件经 EXTI 线 17 触发唤醒。配置要点是先开备份域访问权限再配闹钟和 EXTI17voidRTC_AlarmWake_Config(void){RCC_APB1PeriphClockCmd(RCC_APB1Periph_PWR|RCC_APB1Periph_BKP|RCC_APB1Periph_RTC,ENABLE);RCC_BackupResetCmd(ENABLE);/* 复位备份域清 RTC 状态 */RCC_BackupResetCmd(DISABLE);PWR_BackupAccessCmd(ENABLE);/* DBP1才允许写 RTC 寄存器 *//* LSE 32.768kHz 作为 RTC 时钟深睡期间保持走时 */RCC_LSEConfig(RCC_LSE_ON);while(RCC_GetFlagStatus(RCC_FLAG_LSERDY)RESET);RCC_RTCCLKConfig(RCC_RTCCLKSource_LSE);RCC_RTCCLKCmd(ENABLE);RTC_SetPrescaler(32767);/* 1Hz 计数 */RTC_SetAlarm(RTC_GetCounter()30);/* 30 秒后闹钟 *//* 闹钟事件挂 EXTI 线 17上升沿中断 */EXTI_InitTypeDef EXTI_InitStructure;EXTI_InitStructure.EXTI_LineEXTI_Line17;EXTI_InitStructure.EXTI_ModeEXTI_Mode_Interrupt;EXTI_InitStructure.EXTI_TriggerEXTI_Trigger_Rising;EXTI_InitStructure.EXTI_LineCmdENABLE;EXTI_Init(EXTI_InitStructure);NVIC_EnableIRQ(RTC_IRQn);/* RTC_IRQHandler 里清 RTC_IT_ALR */}定时上报型产品用这条链路进 Stop/Standby 前设好闹钟到点自动醒、采样、上报、再睡待机电流只有 2μA 级电池按年算寿命。六、漏电排查板级才是大头MCU 睡到 2μA 之后真正决定整板待机电流的是 MCU 之外的邻居。那次 8.4mA 排查的教训全在图里按「影响从大到小」排LDO 静态电流AMS1117 这类老派稳压器静态电流 5mA 级——MCU 再省也白搭。换低静态型号RT9013、ME6211 约 30μAHT7333 约 2μA整板待机下限直接降三个数量级。常通外设USB 转串口芯片、板载 LED 加限流电阻3.3V/1kΩ 就是 3.3mA 每路、电池检测分压电阻全都在持续吃电。待机时把它们的供电断开或用 IO 控制 LED 的电源。浮空 GPIO休眠前所有未用引脚配成 AnalogGPIO_Mode_AIN断开施密特触发器的输入路径——多脚叠加是笔不小的暗账。上拉下拉打架内部上拉 40kΩ 和外部 10kΩ 下拉同时挂着持续电流逃不掉统一成一种接法。还有一个「醒来后」的隐藏开销进 Stop 前把 GPIO 全配成 Analog 了唤醒恢复功能模式时别忘了 GPIO 的 Speed 和 Mode 一起配回来——只配 Mode 不配 Speed某些复用功能跑起来会莫名不稳定。同理ADC 校准值、DMA 配置这类现场态能存进备份寄存器Standby 下唯一活着的地方就在睡前存好醒来直接恢复省掉重新校准的等待。测量也讲究万用表串联电流档量 μA 级必须换 μA 档mA 档显示 0.00 会误判「没漏电」或者串一个 1Ω 采样电阻mV 档读数直接对应 mA——1mV1mA10mV10mA量级一目了然。排查手法是「分段拔电」先断开 USB 芯片、再摘 LED、最后查 GPIO看总电流掉到哪一步元凶就锁定在哪一层。七、六个最容易翻车的点SLEEPDEEP 粘滞进过一次 Stop/Standby 后它保持 1下次想浅睡实际直接睡进深睡——想用 Sleep 前先把 SCB_SCR 的 SLEEPDEEP 清 0。唤醒后不重配时钟Stop 唤醒 SYSCLK 回退 HSI 8MHz波特率、延时全乱——醒来先重开 HSEPLL 再碰外设。Standby 唤醒当普通复位没查 PWR_FLAG_SB 就把待机唤醒当上电启动跑——先判断再分流。LDO 静态电流吃掉所有省电AMS1117 5mA 级静态电流是板级第一大坑——换低静态 LDO。浮空引脚漏电休眠前 GPIO 全配 Analog醒来再恢复——多脚叠加的暗账不小。量程用错测不出数mA 档看 μA 级电流显示 0.00 误判没漏电——换 μA 档或用 1Ω 采样电阻。小结F103 的三档低功耗本质是「保留什么、怎么醒来」的取舍Sleep 保外设、Stop 保 SRAM、Standby 只保备份域。动手前先想清楚「唤醒后我打算从哪继续」再选档——选错档的代价是改完才发现数据没了或电流下不去。真正把整板待机压下来MCU 模式只是第一步LDO、外设、GPIO 的板级排查才是大头。再往下走低功耗和 RTOS 的结合值得单独一篇uC/OS-II 的空闲任务天然就是做低功耗的钩子——任务全挂起时进 Stop中断唤醒后回到任务调度。下篇预告K1 任务堆栈实战把「栈会不会爆、爆了怎么救」一次讲透。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价