ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

STM32F407 Stop模式故障排查:从36.8mA到1.4mA的低功耗实战

STM32F407 Stop模式故障排查:从36.8mA到1.4mA的低功耗实战 做低功耗项目时第一次在 STM32F407 上跑 STOP MODE我以为把主频降下来、关几个外设时钟就算完事了。结果按下按键进入 Stop 后电流表纹丝不动地停在 36.8mA板子也彻底睡死过去。后来又在唤醒阶段栽了两次跟头一次是唤醒后直接 HardFault一次是按键死活唤不醒。折腾了整整两天才把问题链路完全理顺。这篇内容就是我基于 STM32F407 的 Stop 模式故障排查做的一次完整复盘。适合正被这几个问题折磨的人进 Stop 后功耗降不下去、进入后无法唤醒、唤醒后程序跑飞以及在使用 USB 虚拟串口、LAN8720 这类外设时被低功耗电流卡住的人。我会从 Stop 模式的底层机制讲起再给一套可以照着抄的配置模板和排查方法最后把外围器件与 PCB 层面的隐藏坑也一并点出来。1. 先弄明白F407的Stop模式到底关闭了什么很多 Stop Mode Failure 的根源是根本没搞清 Stop 模式在 F407 上究竟停了什么、没停什么。F407 不是一颗简单的低功耗单片机它的电源域和时钟树逻辑直接影响你对故障的判断方向。1.1 Sleep、Stop、Standby根本不是一回事我见过不少人在论坛上问为什么 Stop 模式电流比 Sleep 还高其实多半是模式就配错了。F407 有三种低功耗模式Stop 夹在中间最容易出现看着像进了实际没进的尴尬情况。先把三者的核心差异列出来特性SleepStopStandby内核时钟关闭外设时钟可选继续全部关闭全部关闭1.2V 域供电保持保持可选调压器低功耗模式完全掉电SRAM/寄存器内容保持保持仅备份域保持进入指令WFI/WFESLEEPDEEP WFI/WFESLEEPDEEP PDDS WFI/WFE唤醒源任意中断/事件EXTI、RTC、IWDG、WKUP引脚等WKUP引脚、RTC、NRST、IWDG唤醒后恢复继续执行继续执行但时钟树需要重新配置相当于一次上电复位关键就在 Stop 这一行唤醒后程序是继续执行不是从头复位。而 Standby 唤醒后是完整复位。很多代码在 Stop 唤醒后死掉是因为开发者把 Stop 当成了 Standby以为醒来会重新走一遍SystemClock_Config实际上程序直接跳到了__WFI()的下一行此时 HSE、PLL 甚至主时钟还停在停止状态接着跑外设代码自然出问题。1.2 F407的双电源域和内部调压器F407 在内部有两块供电区域VDD 直接供电的部分以及由内部调压器降压得到的 1.2V 数字逻辑域。Stop 模式下1.2V 域仍然有电所以 SRAM、寄存器、内核状态都不丢但 1.2V 域如果完全掉电就是 Standby 了。这里有一个经常被忽略的位PWR-CR寄存器里的LPDS位。进入 Stop 时如果LPDS 0内部调压器保持正常模式唤醒速度更快但功耗会明显偏高如果LPDS 1调压器进入低功耗模式电流能再降一个量级但需要确保 Vcap 外面那颗电容容量足够否则唤醒时电压建立不够快轻则唤醒延迟增大重则直接复位。F407 数据手册对 Vcap 电容有明确要求通常要接 2.2uF 到 4.7uF 的低 ESR 电容并且尽量靠近芯片 Vcap 引脚。我见过一块自绘小板子在 Stop LPDS 下偶尔唤醒失败最后查到就是 Vcap 电容摆放太远、ESR 偏大导致的。1.3 进入Stop的软件路径SLEEPDEEP、PDDS、LPDS与WFI/WFE进入 Stop 模式的寄存器配置并不复杂但每一位都不能错。之前我需要调用HAL_PWR_EnterSTOPMode底层做的事本质上是// 设置 SLEEPDEEP让 WFI/WFE 进入深度睡眠而不是普通睡眠 SCB-SCR | SCB_SCR_SLEEPDEEP_Msk; // 关闭 PDDS保证进入的是 Stop 而不是 Standby PWR-CR ~PWR_CR_PDDS; // 开启调压器低功耗模式进一步降功耗 PWR-CR | PWR_CR_LPDS; // 等待调压器进入低功耗模式完成 while ((PWR-CSR PWR_CSR_LPDSRF) 0) {} // 进入睡眠 __WFI();使用 HAL 库时等价写法HAL_PWR_EnterSTOPMode(PWR_LOWPOWERREGULATOR_ON, PWR_STOPENTRY_WFI);但这里有几个 HAL 库不会替你处理的问题__WFI()执行后如果唤醒源来了程序会从__WFI()的下一条指令继续执行而不是重新跑main。所以进入 Stop 前你必须提前想好唤醒后我这一行后面要干什么。有人图方便直接让main里的代码从初始化开始线性执行那唤醒后会再把外设初始化一遍重复初始化导致 HardFault 只是时间问题。唤醒条件的选择也需要注意WFI需要已使能的中断来唤醒WFE可以由事件唤醒。大部分场景用 WFI 更符合直觉但如果你用了__WFE()还得考虑SEVONPEND位的状态否则事件被挂起后唤醒时机可能和你预期的不一致。2. 三个高频故障现象实测现象、根因、定位手段我遇到过的 Stop Mode Failure 基本可以归成三类电流降不下去、睡了醒不来、醒来就死。这三类问题的排查方向完全不同如果一上来就盯着代码看很容易绕圈子。2.1 现象一进入了但电流纹丝不动现象描述按键后程序确实跑到了__WFI()用调试器看 PC 指针也停住了但电流一直没变化甚至比运行时的平均电流还高。这时候很多人会怀疑 Stop 模式本身坏了其实多半是内核睡了外围没睡。F407 在 Stop 模式下所有时钟停止但引脚状态保持原样。如果某个 GPIO 在高电平驱动一个 LED、某个引脚悬空导致内部电路漏电、USB 的 DP 上拉还在电流自然下不来。Stop 模式只负责关内核和时钟不负责帮你管理外部电路供电这是 F407 低功耗设计里最容易被低估的一点。定位方法也很粗暴有效把板子上除了最小系统以外的所有外设供电切断看电流是否明显下降。将所有未使用 GPIO 统一配置为模拟输入GPIO_MODE_ANALOG或者有计划地设置为固定电平并开启内部上/下拉。逐个外设断电测试比如先把 USB、以太网 PHY 的电源断开再测一次电流。有些外设芯片即使主控停止了只要它自己还在供电就会继续耗电。比如 LAN8720 这类 PHY你关了 RMII 时钟它内部的 PLL 或收发器可能仍然工作具体后面我会专门展开。2.2 现象二睡了就醒不来现象描述电流降下来了但按唤醒按键/产生唤醒事件后板子毫无反应。排除硬件问题后多半是 EXTI 唤醒链路配置不全。F407 在 Stop 模式下任意配置为 EXTI 的 GPIO 都能作为唤醒源。但完整链路包含四层GPIO 外部中断使能、EXTI 中断屏蔽位、NVIC 中断通道使能、以及中断服务函数中清除挂起位。很多人在 GPIO 配置里开了EXTI_Mode_Interrupt却忘了在 NVIC 里使能对应中断导致 WFI 一直等不到唤醒源。典型的标准库配置如下void EXTI_Key_Init(void) { GPIO_InitTypeDef GPIO_InitStructure; EXTI_InitTypeDef EXTI_InitStructure; NVIC_InitTypeDef NVIC_InitStructure; RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA, ENABLE); RCC_APB2PeriphClockCmd(RCC_APB2Periph_SYSCFG, ENABLE); GPIO_InitStructure.GPIO_Pin GPIO_Pin_0; GPIO_InitStructure.GPIO_Mode GPIO_Mode_IN; GPIO_InitStructure.GPIO_PuPd GPIO_PuPd_UP; GPIO_Init(GPIOA, GPIO_InitStructure); SYSCFG_EXTILineConfig(EXTI_PortSourceGPIOA, EXTI_PinSource0); EXTI_InitStructure.EXTI_Line EXTI_Line0; EXTI_InitStructure.EXTI_Mode EXTI_Mode_Interrupt; EXTI_InitStructure.EXTI_Trigger EXTI_Trigger_Falling; EXTI_InitStructure.EXTI_LineCmd ENABLE; EXTI_Init(EXTI_InitStructure); NVIC_InitStructure.NVIC_IRQChannel EXTI0_IRQn; NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority 0; NVIC_InitStructure.NVIC_IRQChannelSubPriority 0; NVIC_InitStructure.NVIC_IRQChannelCmd ENABLE; NVIC_Init(NVIC_InitStructure); }在中断服务函数里务必清 EXTI 挂起位void EXTI0_IRQHandler(void) { if (EXTI_GetITStatus(EXTI_Line0) ! RESET) { EXTI_ClearITPendingBit(EXTI_Line0); // 设置唤醒标志 wakeup_flag 1; } }如果使用 HAL 库调用HAL_GPIO_EXTI_IRQHandler(GPIO_PIN_0)后它内部会清中断标志不是清EXTI-PR的挂起位。这个问题在唤醒链路里特别隐蔽。还有一个容易踩的坑唤醒源如果是按键硬件上必须有消抖。Stop 模式唤醒并不需要按键持续多久但如果在进入 Stop 前按键已经被按下、端口已处于触发电平那么唤醒可能发生在你执行__WFI()之前的一瞬间等进入睡眠后反而没有新的边沿来唤醒表现就是睡死。解决方法是进入 Stop 前先等待按键释放再加软件消抖或者用带边沿中断的专用按键方案。2.3 现象三醒来就HardFault现象描述能唤醒但程序一恢复执行就进入 HardFault或者表现为主循环卡死、外设状态错乱。这个问题最常见的原因是唤醒后的时钟树处于一个半废状态。F407 从 Stop 唤醒后系统时钟默认回到 HSI原来的 PLL 配置、Flash 等待周期都可能不匹配。你的代码如果在__WFI()后面直接操作高速外设比如启动 DMA、访问 RTC、刷新 LCD而系统时钟还在未稳定状态内核运行频率和 Flash 等待周期不匹配就会产生总线错误或断言失败。所以进入 Stop 的模式下唤醒后第一件事不是处理业务而是重新配置时钟。很多标准库例程习惯把SystemInit()放在main开头但 Stop 唤醒不会重新执行它需要你在唤醒路径上显式调用时钟恢复函数。3. 完整复现一次Stop模式排查从36.8mA到1.4mA这一节我用自己实际做过的一个低功耗采集节点作为案例复盘。节点主控是 STM32F407ZGT6带 USB 虚拟串口用于调试外接 LAN8720 做 UDP 数据上报平时通过秒脉冲触发同步待机时进入 Stop 模式。开发环境是标准库因为当时项目代码基于标准库维护HAL 迁移成本太高。3.1 硬件环境与初始状态板子上除了 F407还有 USB 转串口芯片、LAN8720、一颗外部 25MHz 晶振给以太网 PHY 提供参考时钟源、若干 LED 和按键。最初按下唤醒按键进入 Stop 后实测整板电流 36.8mA和跑空转 while 循环时的 41mA 相差无几。这明显不正常理论上 F407 停止模式本体电流应该在 mA 级别以下。3.2 第一刀断开SWD调试器电流立刻下降第一次排查时我犯了新手常犯的错用 ST-Link 连着一块测电流。结果发现电流始终有 20mA 左右的下不去断开 ST-Link 后立刻降到十几毫安。这是因为调试器的 SWDIO/SWCLK 引脚电平会受调试器影响而且调试状态下部分调试组件时钟无法完全关闭。如果你要做正式的 Stop 模式功耗评估不要只依赖调试器复位后进入低功耗最好完全断电后用外部独立按键触发进入 Stop再串入电流表测量。不然你的故障可能是调试器带来的假象。3.3 第二刀逐个清掉未用IO和外设时钟断开调试器后整板仍有 15.6mA我怀疑外围 IO 和未用外设时钟还在耗电。于是把所有未用引脚都配置成GPIO_Mode_AN避免悬空引入漏电流。然后通过RCC-AHB1ENR、RCC-APB1ENR、RCC-APB2ENR逐个核对把打印、存储等不需要的外设时钟全部关闭。一个容易漏的地方是即使你关闭了外设时钟复位状态下的外设寄存器仍然会维持某些引脚的现有电平。所以关闭时钟不等于断开耗电路径引脚电平必须由 GPIO 配置来固定。完成这一步后电流降到了 8.7mA。剩下的大头明显来自 USB 和 LAN8720。3.4 第三刀USB和PHY的硬处理USB 虚拟串口在进入 Stop 前如果只是把时钟关了并不够。USB OTG 外设内部会有上拉电阻、收发器以及 PA11/PA12 上的信号状态会继续耗电。我的处理方法是先调用USB_Stop()或HAL_PCD_DeInit()把 USB 外设彻底挂起再把 PA11/PA12 配置为模拟输入或固定电平。这一步之后电流降到 5.4mA。LAN8720 更麻烦。它之前的 REF_CLK 是从 STM32F407 的 MCO1 引脚输出的 50MHz一旦进入 StopMCO1 停止输出但 LAN8720 自身仍在供电芯片内部还会继续消耗不小的电流。最稳妥的是通过一个专用 GPIO 控制 LAN8720 的硬件复位脚并在进入 Stop 前拉低它的复位脚或直接切断 PHY 供电。软件上也可以对 LAN8720 写功耗管理寄存器让它进入 Power-Down 模式。LAN8720 的基本模式控制寄存器地址 0里 bit 11 是 Power Down可以通过 MDIO 接口写uint16_t reg; // 读当前 BCR 寄存器 lan8720_read_reg(0, reg); // 置位 Power Down 位 reg | (0x1 11); lan8720_write_reg(0, reg);唤醒后再清掉这个 bit等待 PHY 重新协商。完成 PHY 处理后电流降到了 2.1mA。3.5 第四刀调压器LPDS、Flash等待周期与最终配置最后我把进入 Stop 的代码从HAL_PWR_EnterSTOPMode(PWR_REGULATOR_ON, ...)改为PWR_LOWPOWERREGULATOR_ON同时检查了 RTC 是否还在运行。如果 RTC 在跑它的供电域是独立于核心的不影响 Stop 电流但会贡献毫安级别取决于备份域电路。最终整板电流稳定在 1.4mA 左右。考虑到板上还挂着 RTC、一个按键上拉电阻以及一颗 LED 等这个水平对这块板子已经比较理想了。如果你用最小系统板理论上 F407 的 Stop 模式内部电流能到几十微安量级。下面是进入 Stop 前我实际执行的配置清单整理成表方便对照项目处理方式说明未使用 GPIO全部配置为模拟输入或固定电平防止浮空引脚漏电板上 LED关闭或改为外设控制进入前不点亮LED 驱动电流往往远大于 MCU 本身USB OTG彻底 DeInitPA11/PA12 固定电平不能只关时钟LAN8720拉低复位脚或写 Power Down 寄存器PHY 必须显式断电调试器断开 SWD 再测功耗调试器会引入假电流调压器开启 LPDS 低功耗模式需要 Vcap 电容满足要求待唤醒外设保留 EXTI 唤醒源注意 NVIC 中断必须使能RTC/备份域按需保留RTC 仍会耗电需要评估3.6 唤醒后的迟滞问题这里要再补一个案例参数配置完成后我以为大功告成了结果用示波器量按键按下到系统恢复执行主线代码的时间发现高达 140ms。这个时间包含了 PHY 复位后的软件延时、时钟重新稳定、以及唤醒源中断里的一些初始化操作。对普通按键唤醒这个延迟可以接受但如果是秒脉冲同步触发的高精度采集节点140ms 就意味着事件窗口错失。优化办法是把唤醒后不需要立即重新配置的外设初始化放到主循环里按需执行而不是一股脑在中断服务函数里做同时进入 Stop 前不要给 PHY 写 Power-Down改为直接切断电因为它恢复协商时间接近百毫秒如果只是周期唤醒做 UDP 上报需要把这段延迟算进业务逻辑里。4. 唤醒后的半复位状态恢复时钟与外设的正确顺序Stop 唤醒最容易被低估的一点就是代码是在__WFI()之后继续跑的但系统时钟已经变了。F407 从 Stop 唤醒后主时钟源回落到 HSI 16MHzPLL、HSE 都没有自动恢复。所以如果代码里没有做时钟重建接下来所有基于时钟的延时、波特率、通信时序全部会乱套。4.1 为什么唤醒后必须重新配置时钟直接看现象__WFI()之后立刻调用HAL_Delay(10)程序可能直接死循环。因为 HAL_Delay 依赖 SysTick而 SysTick 的时钟源HCLK变成了 HSI如果 SysTick 没有重新初始化它的重装载值还是原来 168MHz 主频下的值导致uwTick增长异常。从 Stop 唤醒后要先把HAL_RCC_ClockConfig()重新执行一遍或者在你的标准库工程里手动调用SystemClock_Config()并且注意需要先重新配置 Flash 等待周期否则 PLL 切换后 Flash 读取时序不匹配HardFault 随时会来。如果用的是标准库我习惯在进入 Stop 前保存一个标志比如一个全局变量volatile uint8_t stop_mode_flag 0; void Enter_StopMode(void) { stop_mode_flag 1; // 关闭无关外设时钟、配置唤醒源 ... SCB-SCR | SCB_SCR_SLEEPDEEP_Msk; PWR-CR ~PWR_CR_PDDS; PWR-CR | PWR_CR_LPDS; __WFI(); // 唤醒后从这里继续执行 stop_mode_flag 0; SystemClock_Config(); // 重新配置系统时钟 SysTick_Config(SystemCoreClock / 1000); // 重新配置 SysTick // 按需恢复外设 }4.2 SysTick、延时函数和中断优先级在唤醒后的重置除了 SysTick还要注意中断优先级分组。NVIC_PriorityGroupConfig一般在工程初始化阶段只执行一次但如果你用的是 FreeRTOS它的vPortSetupTimerInterrupt会依赖 SysTick 和中断优先级。如果唤醒路径上又重新初始化了外设中断优先级分组没有保持好状态也可能错乱。我的习惯是所有中断源在main初始化阶段就配置好Stop 期间不清不关唤醒后只重新配置时钟和延时相关部分别在唤醒路径上做全套外设重初始化。4.3 恢复外设的一个可靠顺序如果非要重新初始化外设我建议按这个顺序来实测能减少很多奇怪故障重新配置系统时钟和 Flash 等待周期确保内核和外设总线频率正确。重新配置 SysTick保证HAL_Delay、OS Tick可用。恢复 GPIO 复用功能尤其是 USART、SPI、USB、ETH 需要的引脚。恢复各外设时钟和寄存器配置可以调用对应外设的 Init 函数但要注意之前的中断是否还挂着。清除中断标志再使能 NVIC 中断。最后再处理高层的业务状态比如缓存区、协议栈。不要在唤醒后直接操作一个尚未恢复时钟的外设寄存器那是 HardFault 的高发区。5. 外围器件与PCB层面把Stop模式搞崩的细节很多 Stop MODE FAILURE 不是 MCU 代码问题而是外围器件把功耗拉住了或者把唤醒引脚、复位状态干扰了。这里把几个我踩过的坑集中讲一下。5.1 USB虚拟串口的完整关闭动作项目里用 USB 虚拟串口时进入 Stop 之前如果漏了某个环节电流会高得离谱。最简单的做法是把USB_Stop()或HAL_PCD_DeInit()调一遍但还不够。USB 外设即使 DeInitPA11/PA12 上的电平仍然由 GPIO 驱动而 USB 数据线是有上拉的DP 引脚上的 1.5k 上拉会通过 USB 电缆形成通路即便没接主机也会耗电。我的处理是把 PA11、PA12 在进入 Stop 前配置回普通 GPIO 输出低电平或者配置为模拟输入。这样 USB 收发器和外部上拉都不会形成明显漏电路径。唤醒后如果需要恢复 USB再重新配置复用功能和USB_Init()。如果你在做 USB 虚拟串口版的标准库项目可以参考这个流程而不只是禁用 USB 中断。5.2 LAN8720 在 Stop 模式下的耗电与软件掉电LAN8720 这类 PHY 芯片是低功耗项目的重灾区。F407 本身不带以太网 MAC 的低功耗仲裁你需要在进入 Stop 前自己处理 PHY 状态。我之前提过优先用 GPIO 控制 PHY 的复位脚进入时拉低唤醒后拉高并等待其复位完成。如果硬件上没留这个控制脚可以通过 MDIO 写寄存器让 PHY 进入 Power-Down 模式但要注意有些 PHY 芯片在 Power-Down 后仍然有 100uA 级别的基础电流取决于具体型号和数据手册。另外 LAN8720 的时钟源如果来自 F407 的 MCO那么 Stop 模式下 MCO 停止输出PHY 会失去参考时钟但它的时钟检测电路可能还在做一些异常尝试。反正这类外设在低功耗待机时要么彻底断电要么显式进入掉电状态不能只靠主控停了它就会停的假设。5.3 按键唤醒的硬件消抖、EXTI与Wakeup Pin的区别F407 的 PA0 可以映射为WKUP引脚但那是 Standby 模式下专用。Stop 模式下用 PA0 做 EXTI 唤醒是可以的但逻辑完全不同Standby 的 WKUP 引脚即使不配置 EXTI 也能唤醒因为它是电源控制模块直接检测的Stop 模式则必须走 EXTI 链路。所以网上一些说F407的PA0能唤醒Stop的帖子其实只对了一半如果没把 EXTI、NVIC 配完PA0 什么也干不了。硬件上按键要接一个对 GND 的电容比如 100nF做硬件消抖避免进入 Stop 时按键抖动产生多个边沿导致唤醒源在上一次边沿已经被消费掉。软件层面我建议在main的主循环里检测进入 Stop 的条件并要求按键状态为已释放后才发起 Stop 流程。5.4 Vcap、去耦电容和最小系统板的电流基准还有一次我帮朋友看一块 F407 最小系统板进入 Stop 后电流一直有 3mA 多怎么查都下不去。最后发现是板上的电源指示灯 LED 电阻太小3.3V 经过 1k 限流电阻点亮换算下来就是 3mA 左右。这块板根本没法做精准低功耗评估除非把 LED 拆了或者控制它的供电。Vcap 电容、VR 基准、复位电路的漏电流也都会影响最终达到的电流值。所以当你拿别人的板子能做到 XX uA来对比时先确认你们的最小系统外围是否一致。F407 本身的 Stop 电流确实很低但整板电流绝不是 MCU 数据手册上的数值而是所有还在供电电路的总和。6. 从半年项目里总结的几条低功耗实践最后一节说点更实际的。经历了几个项目的打磨后我总结了几条比较通用的低功耗调试经验尤其适合 F407 这种外设丰富的芯片。6.1 电流测量注意电流表本身的压降用万用表电流档串接测低功耗电流时电流档的内阻会产生压降可能导致 MCU 供电电压低于正常工作范围进入 Stop 后行为异常。我建议用台式万用表的电压档配合采样电阻测量或者用专用低功耗电流测量仪器。如果只是快速定位可以用一个 10 欧姆采样电阻串在供电回路上用示波器或万用表测电阻两端电压再换算成电流。采样电阻太大会让压降过大影响工作太小则信号难测。6.2 唤醒时间测量不要用LED来验证唤醒不要在唤醒路径里点 LED 来验证因为点 LED 本身就是大电流行为还会把你系统时钟是否有效的问题和外围驱动问题混在一起。我习惯用一个空闲 GPIO 来翻转电平用示波器看它从低到高的时间这样既不影响电流又能量出唤醒延迟。如果想看系统时钟是否已经稳定可以让唤醒后在 GPIO 上输出一个频率固定的方波然后对比示波器上的频率是否准确。如果频率差一半说明 PLL 配置有问题。6.3 IWDG、RTC闹钟和秒脉冲中断在Stop下的行为这里有个容易误解的点独立看门狗IWDG在 Stop 模式下会继续计数但它不会因为溢出而唤醒 MCU也不能在 Stop 下喂狗。所以你如果开了 IWDG进入 Stop 前必须把喂狗机制安排好否则唤醒后可能刚跑几步就触发看门狗复位。RTC 闹钟和秒脉冲中断可以在 Stop 模式下唤醒 MCU因为 RTC 在备份域时钟源依赖 LSE 或 LSI。在做秒脉冲同步采集时我通常用 RTC 闹钟或 RTC 唤醒定时器产生周期性唤醒然后进入 Stop这样既能保证时间基准又能控制平均功耗。但要注意唤醒后要重新获取时间并处理进位竞争避免主循环刚醒来又错过秒信号。6.4 一个可复制的测试骨架最后给一个我常用的 Stop 模式测试骨架方便你快速验证板子和代码是否正常volatile uint8_t wakeup_flag 0; void EnterStopMode(void) { // 关闭高耗电外设配置唤醒源 Usb_DeInit(); Eth_EnterPowerDown(); Gpio_EnterLowPower(); printf(enter stop\r\n); // 配置 SysTick 不重要了先关掉唤醒后再开 SysTick-CTRL 0; SCB-SCR | SCB_SCR_SLEEPDEEP_Msk; PWR-CR ~PWR_CR_PDDS; PWR-CR | PWR_CR_LPDS; __WFI(); // 唤醒后恢复 SystemClock_Config(); SysTick_Config(SystemCoreClock / 1000); Gpio_ExitLowPower(); Eth_ExitPowerDown(); Usb_Init(); wakeup_flag 0; } int main(void) { HAL_Init(); SystemClock_Config(); GPIO_Init(); USART_Init(); // 配置按键 EXTI while (1) { if (按键按下 允许进入Stop) { // 延时消抖 delay_ms(50); if (按键仍按下) { EnterStopMode(); } } if (wakeup_flag) { // 执行唤醒后业务 } } }这套骨架里所有硬件的退出低功耗操作都放在__WFI()之后并且顺序严格按照时钟、GPIO、外设、业务来。按键使用 EXTI 中断置位wakeup_flag主循环检测到标志后再做处理这样避免在中断里做耗时操作也避免唤醒后中断嵌套导致时钟还没恢复就调用复杂函数。对于 F407 的 Stop 模式我现在最大的体会是它不是一颗一睡就万事大吉的芯片。要想真正把功耗降下去必须在硬件选型、外设状态、时钟恢复、唤醒事件这几条线上一并考虑。尤其当你手头还带着 USB、LAN8720 这类外设时关掉主控时钟只是万里长征第一步。希望这篇复盘能帮你少走一些弯路下次再看到 Stop MODE FAILURE 时能沿着现象一路定位到根因。
返回列表