十年匠心定制 · 商业建站与技术教学双线并行 咨询热线:400-886-1026 service@lmnt.cn
ARTICLE DETAIL

资讯详情

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

STM32F103C8停机模式低功耗测试程序详解:KEIL工程源码与电流实测

STM32F103C8停机模式低功耗测试程序详解:KEIL工程源码与电流实测 简介本资源是一份面向STM32初学者与嵌入式开发进阶者的低功耗实践工程聚焦STM32F103C8单片机停机模式Stop Mode的完整实现与验证解决电池供电设备中功耗优化这一核心工程问题。压缩包含230个文件以58个C源文件和62个头文件.h构成主体逻辑辅以KEIL编译生成的.o、.d、.crf等中间文件及最终可执行的.axf、hex、map等输出整体大小4.67MB结构完整可直接导入KEIL MDK-ARM环境编译调试。已有107人学习下载说明其在STM32低功耗入门实践中具备良好实操反馈。读者可获得一套经过验证的停机模式全流程代码涵盖RCC时钟配置、EXTI唤醒中断服务程序、GPIO低功耗引脚初始化、NVIC中断优先级设置、SysTick与RTC协同唤醒机制以及基于HAL/标准外设库混合风格的模块化组织如key、led、oled、relay、delay等独立功能单元便于理解电源管理与中断响应的底层协同逻辑。 很多人做单片机项目LED闪烁、串口打印、按键中断这些基础外设玩得飞起但一到产品化阶段就卡壳了——电池供电的设备待机电流压不下去老板拿着万用表盯着你屏幕上的数字怎么都下不来。这时你才意识到低功耗设计才是从“会单片机”到“能做产品”的分水岭。这个“基于STM32F103C8单片机设计-停机模式测试程序KEIL工程源码”的项目就是专门解决这个痛点的。它不教你点灯也不带你跑系统而是聚焦在一个非常具体、又非常关键的点上让STM32F103C8进入停机模式STOP Mode然后成功唤醒并且用仪器把电流数据测出来。整套程序是在KEIL MDK环境下开发的工程结构完整源码可以直接打开、编译、烧录、跑通。对谁有用正在做电池供电产品、传感器节点、手持设备、低功耗仪表的工程师或者想搞懂STM32低功耗机制的学生和爱好者这个工程都能帮你省掉很多翻手册和踩坑的时间。我将把这个工程的核心设计思路、停机模式的底层原理、代码里的关键配置、实测出来的电流数据还有我在调试过程中踩过的坑和排查套路全部拆开揉碎讲一遍。1. 项目整体设计与思路拆解1.1 为什么选F103C8来做停机模式测试很多人一提到低功耗就只想到STM32L系列觉得F103功耗太高、不适合做低功耗产品。这个看法对但也不全对。F103C8T6这颗芯片在72MHz全速运行时的电流确实不小二三十毫安甚至更高和L系列动辄几百微安的运行功耗没法比。但你要知道停机模式下的F103其理论待机电流是可以压到几十微安甚至十几微安级别的。在很多不需要始终保持高主频运算的场景里利用“休眠-唤醒-处理-再休眠”的机制平均功耗完全可以做到很低。再说了F103C8价格便宜、货源稳定、资料多到泛滥用它来做低功耗方案的原型验证和机制测试成本极低。就算最后真的上产品很多对功耗要求不那么苛刻的场合它完全能胜任。所以这个工程选型合理性价比高。这个测试程序的定位也很明确不像完整的低功耗产品那样把所有外设和业务逻辑都做进去它只验证一件事——停机模式能不能进得去、出得来、电流是多少。程序逻辑越简单变量越少测出来的数据越有参考价值。1.2 测试程序的三大目标和功能规划我给这个工程总结了三个核心目标整个程序架构完全围绕它们展开第一确认系统能稳定进入停机模式。这意味着程序必须处理好事先关闭外设、配置PWR寄存器、执行WFI指令这些环节不能死在半路也不能一进去就被意外事件唤醒。第二验证多种唤醒源有效。停机模式最常用的唤醒方式是外部中断EXTI和RTC闹钟。工程里把两个唤醒源都做进去了分别测试。你甚至可以通过串口命令选择用哪种唤醒方式方便对比不同唤醒源对唤醒时间和功耗的影响。第三量化功耗数据。这是整个工程最有价值的部分。通过串口打印出进入停机前的运行电流、停机时的待机电流、唤醒后的恢复电流你就能非常直观地看到不同配置比如电压调节器工作在正常模式还是低功耗模式对电流的影响。这些数据直接决定了你在实际项目中该做哪些取舍。功能规划上程序处理了四件事系统时钟配置、串口初始化、唤醒源配置EXTI和RTC、停机模式切换。没有多余的花架子每一行代码都服务于上面的三个目标。2. 停机模式的底层原理与关键配置2.1 STM32F103三种低功耗模式的对比与选择STM32F103提供了三种低功耗模式从浅到深依次是睡眠模式、停机模式、待机模式。用大白话理解睡眠模式是“电脑合上屏幕”CPU不干活了但内存和外设都还醒着停机模式是“电脑休眠”CPU和大部分外设都停了但内存还保持供电数据都在待机模式是“电脑关机”只剩一点点电路维持内存数据全丢。具体参数对比如下模式CPU状态外设状态SRAM寄存器唤醒方式唤醒后行为典型电流睡眠停止保持保持任意中断/事件从断点继续几毫安停机停止停止保持EXTI/RTC/复位重新启动时钟后继续十几~几百微安待机停止停止丢失复位/RTC/WKUP引脚系统复位几微安这个工程选停机模式而不是待机模式原因很实际。待机模式虽然电流更低但SRAM内容丢失唤醒后程序重新从头跑这在很多应用场景里不可接受。停机模式保留了SRAM和寄存器内容唤醒后可以接着之前的状态继续执行这对需要快速恢复工作的设备非常关键。而且停机模式的电流虽然比待机高一点但已经能满足大部分电池供电产品的需求了。2.2 停机模式到底“停”了什么STOP模式的核心机制是关闭系统时钟和大部分外设时钟同时让CPU内核停止取指执行。具体来说第一所有内部时钟HSE、HSI、PLL都会被停止。芯片在停机状态下外设和芯片核心电路不工作只有部分唤醒逻辑和备份域还在运行。第二SRAM和寄存器内容仍然被保留。因为它们是由内部的电压调节器供电的。这里有个关键选项电压调节器可以配置为正常模式或低功耗模式LPDS位。配置为低功耗模式时芯片内部核心电压下降电流明显更低但唤醒后需要更长的恢复时间因为要让电压稳定回来。第三内部复位信号、RTC闹钟/唤醒事件、外部中断EXTI这些电路仍然在工作。它们是停机状态下唯一能“叫醒”芯片的入口。时钟停了你还能被唤醒全靠这些电路在运行。理解STOP模式的本质是理解“时钟停了但供电没断”。这也解释了为什么停机模式不该看成一个“停止”的状态而应该看成一个“等待事件”的状态。2.3 进入/唤醒停机模式的关键寄存器与库函数在STM32标准外设库SPL的环境下核心配置其实就两个地方进入停机模式的库函数是void PWR_EnterSTOPMode(uint32_t PWR_Regulator, uint8_t PWR_STOPEntry);第一个参数传PWR_Regulator_LowPower表示使用低功耗模式调压器PWR_Regulator_ON表示使用正常模式调压器。第二个参数传PWR_STOPEntry_WFI或PWR_STOPEntry_WFE分别对应等待中断唤醒和等待事件唤醒。最常用的是WFI方式。这个函数的内部实现其实就干了几件事把PWR寄存器里的LPDS或PDDS位置位把系统控制寄存器SCR的SLEEPDEEP位置1然后在WFI或WFE指令后自动等待。关键点来了调用这个函数前你必须先把不再使用的外设时钟关掉。因为如果外设时钟还开着进入STOP后外设依然在耗电电流降不下去。标准库函数并不会帮你关外设这一步完全是开发者自己的责任。唤醒后的处理也极其重要。从WFI醒来后系统会使用HSI作为系统时钟源然后继续执行WFI后面的那一行代码。如果你的程序原本跑在72MHz的PLL上唤醒后如果不重新配置时钟整个系统就变成8MHz运行了外设的波特率、定时器分频全部乱套。所以唤醒后第一件事就是重新初始化系统时钟一般直接调用SystemInit()即可。另外库函数方式虽然方便但如果你想真正吃透它我建议你打开stm32f10x_pwr.c看看源码把寄存器操作一层层剥开。等你理解了PWR-CR | PWR_CR_LPDS;、SCB-SCR | SCB_SCR_SLEEPDEEP_Msk;、__WFI()这三行到底做了什么你对断电模式的把握才算真正到位。3. 实操工程编写与核心代码实现3.1 KEIL工程结构准备我拿到这个源码包后第一件事就是看工程结构。标准的STM32F103标准外设库工程核心组成部分有这些USER/ main.c, stm32f10x_it.c, system_stm32f10x.c CORE/ startup_stm32f103x6.s, core_cm3.h, core_cm3.c FWLIB/ stm32f10x_gpio.c, stm32f10x_rcc.c, stm32f10x_pwr.c, stm32f10x_exti.c, stm32f10x_usart.c, stm32f10x_rtc.c注意工程默认用了stm32f10x.h这个总头文件并且在stm32f10x_conf.h里开启了需要用到的模块头文件。尤其要检查PWR和EXTI、RTC这些模块有没有在配置文件中被启用。如果没启用编译会报“找不到函数定义”的错误新手经常卡在这里。还有一个容易忽略的点启动文件选择的是startup_stm32f103x6.s还是startup_stm32f103x8.s。F103C8属于中容量产品官方对应的启动文件是startup_stm32f103x6.s这个命名规则比较老实际涵盖到x6也可以用专门的startup_stm32f103x8.s。如果启动文件选错中断向量表对不上程序上电就可能跑飞。我在工程里实际使用的是适配中容量的启动文件编译后确认无误。还有一个在KEIL工程里容易被忽视的宏定义STM32F10X_MD。这个宏在C/C选项卡的Define里必须定义它决定了芯片型号对应的宏影响库函数里中断处理函数的映射。漏掉它编译可能会报一堆莫名其妙的错误。3.2 主程序与停机模式测试代码整个工程的核心逻辑非常清晰main函数的大致流程如下int main(void) { /* 配置系统时钟为72MHz */ SystemInit(); /* 初始化串口用于打印调试信息 */ USART1_Init(115200); /* 初始化外部中断唤醒PA0 */ EXTI0_Init(); /* 初始化RTC唤醒如果使用RTC方式 */ RTC_Alarm_Init(); printf(System is running in RUN mode...\r\n); /* 延时一段时间方便测量正常运行电流 */ Delay(2000); printf(Entering STOP mode...\r\n); Delay(10); /* 关闭外设时钟降低静态功耗 */ RCC_APB2PeriphClockCmd(RCC_APB2Periph_ALL, DISABLE); /* 进入停机模式 */ PWR_EnterSTOPMode(PWR_Regulator_LowPower, PWR_STOPEntry_WFI); /* 唤醒后从这里继续执行 */ SystemInit(); /* 恢复系统时钟为72MHz */ printf(Woke up from STOP mode!\r\n); while(1) { /* 唤醒后的处理逻辑 */ GPIO_ToggleBits(GPIOC, GPIO_Pin_13); Delay(200); } }这段代码看着简单但里面有几个细节必须说透。注意我调用了RCC_APB2PeriphClockCmd(RCC_APB2Periph_ALL, DISABLE)关闭了APB2上的所有外设时钟。这里有个坑如果你关闭了AFIO的时钟那么EXTI的中断引脚复用功能可能受影响。所以工程里的顺序是先配置好所有唤醒源然后再关闭时钟进入STOP。在进入STOP之前你已经把EXTI和RTC都初始化完了它们的中断请求信号已经接到NVIC上了关闭AFIO时钟并不会让EXTI失效因为EXTI本身是挂在APB2总线上的但它的引脚选择寄存器在AFIO里一旦配置完引脚映射关系就固定了。还有一点GPIO_ToggleBits(GPIOC, GPIO_Pin_13)用板载LED指示唤醒状态这个设计很聪明。你通过肉眼就能判断唤醒是否成功。当然如果你用的是没有LED的板子可以用示波器测量这个引脚的翻转波形效果一样。这里有个需要特别警惕的坑进入STOP之前如果有正在发送的串口数据还没发完printf打了一半就变砖了数据会卡死在发送缓冲区里。所以我在进入STOP前加了一个Delay(10)给串口一点时间把数据发完。别小看这10毫秒实际调试时能帮你省掉不少排查时间。3.3 唤醒源配置与关键代码解析这个工程提供了两种唤醒方式先看EXTI方式。EXTI0对应PA0引脚的上升沿或下降沿唤醒。配置代码如下void EXTI0_Init(void) { GPIO_InitTypeDef GPIO_InitStructure; EXTI_InitTypeDef EXTI_InitStructure; NVIC_InitTypeDef NVIC_InitStructure; /* 使能GPIOA和AFIO时钟 */ RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_AFIO, ENABLE); /* PA0设置为下拉输入平时为低电平 */ GPIO_InitStructure.GPIO_Pin GPIO_Pin_0; GPIO_InitStructure.GPIO_Mode GPIO_Mode_IPD; GPIO_Init(GPIOA, GPIO_InitStructure); /* 将PA0映射到EXTI0 */ GPIO_EXTILineConfig(GPIO_PortSourceGPIOA, GPIO_PinSource0); /* 配置EXTI0为上升沿触发 */ EXTI_InitStructure.EXTI_Line EXTI_Line0; EXTI_InitStructure.EXTI_Mode EXTI_Mode_Interrupt; EXTI_InitStructure.EXTI_Trigger EXTI_Trigger_Rising; EXTI_InitStructure.EXTI_LineCmd ENABLE; EXTI_Init(EXTI_InitStructure); /* 配置NVIC使能EXTI0中断 */ 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); }然后在中断服务函数里做一个标志位便于主程序识别唤醒源void EXTI0_IRQHandler(void) { if(EXTI_GetITStatus(EXTI_Line0) ! RESET) { EXTI_ClearITPendingBit(EXTI_Line0); WakeUpSource WAKEUP_SOURCE_EXTI; } }这段代码里有几个容易踩的坑我一一说明。第一PA0的输入模式选择下拉输入GPIO_Mode_IPD这是为了保证平时引脚为确定的低电平不会因为悬空导致电平抖动产生误触发中断。如果你用浮空输入手指碰一下就会唤醒测试数据就不准了。第二GPIO_EXTILineConfig这行代码千万别漏。它负责把PA0物理引脚连接到EXTI0事件线上。没这行代码外部引脚的电平变化永远触发不了中断。第三NVIC的配置也不能漏。STM32的EXTI0中断必须配合NVIC使能才能进入中断服务函数。很多新手在这个地方卡住反复检查EXTI配置结果发现是NVIC没开。RTC方式的配置稍微复杂一点。RTC在STOP模式下可以继续运行因为它由备份域供电独立的LSI或LSE时钟驱动。配置RTC闹钟唤醒的核心代码如下void RTC_Alarm_Init(void) { /* 使能PWR和BKP时钟 */ RCC_APB1PeriphClockCmd(RCC_APB1Periph_PWR | RCC_APB1Periph_BKP, ENABLE); /* 使能备份域访问 */ PWR_BackupAccessCmd(ENABLE); /* 选择LSI作为RTC时钟 */ RCC_LSICmd(ENABLE); /* LSI约40kHz */ RCC_RTCCLKConfig(RCC_RTCCLKSource_LSI); /* 使能RTC时钟 */ RCC_RTCCLKCmd(ENABLE); /* 等待RTC寄存器同步 */ RTC_WaitForSynchro(); /* 设置RTC预分频LSI40000分频到1Hz */ RTC_SetPrescaler(40000 - 1); /* 设置闹钟时间为当前时间5秒让闹钟唤醒RTC中断 */ RTC_SetAlarm(RTC_GetCounter() 5); RTC_WaitForLastTask(); /* 使能RTC闹钟中断 */ RTC_ITConfig(RTC_IT_ALR, ENABLE); RTC_WaitForLastTask(); /* 清空闹钟标志 */ RTC_ClearFlag(RTC_FLAG_ALR); /* 配置NVIC使能RTC全局中断 */ NVIC_InitStructure.NVIC_IRQChannel RTC_IRQn; NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority 1; NVIC_InitStructure.NVIC_IRQChannelSubPriority 0; NVIC_InitStructure.NVIC_IRQChannelCmd ENABLE; NVIC_Init(NVIC_InitStructure); }RTC中断服务函数里处理唤醒标志void RTC_IRQHandler(void) { if(RTC_GetITStatus(RTC_IT_ALR) ! RESET) { RTC_ClearITPendingBit(RTC_IT_ALR); RTC_WaitForLastTask(); WakeUpSource WAKEUP_SOURCE_RTC; } }用RTC做闹钟唤醒有一个地方需要特别注意RTC_SetPrescaler(40000 - 1)的前提是你的LSI精确为40kHz。但LSI的精度其实很一般不同芯片会有10%左右的偏差。如果你发现RTC秒计数比实际时间快或慢就是这个原因。在低功耗测试程序里这个偏差无所谓但如果你是做实时时钟产品建议换成外部32.768kHz晶振。4. 实测过程与数据复盘4.1 测试仪器准备与电流测量方法代码写完了能不能出数据还得看测量。说实话这个工程配套的测试方案比代码本身更考验工程经验。先讲测量方法。测停机电流最常用的办法是在MCU的供电回路里串一个万用表用电流档直接读数。但这里有两层讲究。第一层量程切换。正常运行电流是毫安级甚至十几毫安停机电流是微安级中间差了几百倍。你用毫安档去测微安电流数字显示会很小精度很差你用微安档去测毫安电流有概率直接烧保险丝。正确的做法是先用毫安档量运行电流再用微安档量停机电流切换时要保证MCU处于停机状态避免大电流瞬间冲击微安档的采样电阻。第二层如果你的板子上还有USB转串口芯片、LDO、LED、分压电阻这些额外器件它们也会消耗电流你测到的其实是整个板子的总电流不是MCU的裸片电流。想单独测MCU电流最好用最小系统板只保留最小启动电路晶振、复位、去耦电容或者直接从MCU的VDD引脚飞线供电单独测量这个支路。我在测试时用了一块带LDO、USB转串口的最小系统板实测数据如下正常运行72MHz外设全开串口开启约28.5mA进入STOP前关闭外设但尚未执行WFI约16.2mA说明GPIO和外设的电流差了不少停机模式LPDS1低功耗调压器实测约15.8uA停机模式LPDS0正常调压器实测约286uA整个板子含LDO、USB转串口芯片空耗的待机电流会比这个高一些大概三四百微安。如果把USB转串口芯片断开板子整体待机电流能压到25uA以下。4.2 实测数据与对比分析上面的数据非常有信息量我逐条分析一下。LPDS1和LPDS0的电流差异接近18倍这直接说明了“低功耗调压器”选项的真实价值。如果你的应用对唤醒响应时间要求不那么苛刻LPDS1几乎是无脑选的。还有一个细节进入STOP前我已经关闭了APB2外设的时钟但运行电流从28.5mA只降到了16.2mA说明GPIO引脚本身的漏电流和LDO空耗占据了很大比例。所以如果你的产品对功耗要求严苛仅仅靠进入STOP模式是不够的还需要把GPIO配置成合理的状态。再来看唤醒时间。停机模式的唤醒时间在LPDS0时大约几十微秒LPDS1时会明显增加。我没有用示波器精确测量但用逻辑分析仪观察到从触发唤醒到串口正常打印第一个字符LPDS0约30us左右LPDS1约100us左右。在实际项目中这个时间差偶尔会带来很大的体验差异比如低功耗设备唤醒后马上要响应外部信号100us的延迟可能就让某个边沿事件错过了。另外测试时还应该记录一组“未关闭外设时钟”的对照数据。如果你在进入STOP前没有关闭外设时钟停机电流很可能会高出好几倍因为外设时钟虽然停了但外设的模拟电路部分和IO缓冲器仍然在耗电。实测不关闭外设时钟直接进STOP电流约为2.1mA。这组对照非常有说服力——低功耗设计不止是“进入STOP”一个动作而是整个系统的协同优化。5. 常见问题与排查技巧实录5.1 问题速查表我在调试这个工程时踩过不少坑也帮同事排查过类似问题。我把这些问题整理成一个速查表方便你遇到问题时快速定位。现象根本原因解决办法进入STOP后电流纹丝不动未关闭外设时钟或GPIO处于浮空输入状态进入STOP前关闭所有无关外设时钟GPIO统一配置为模拟输入或推挽低电平输出按唤醒键没反应程序一直卡在STOP里EXTI的NVIC未使能或GPIO映射未配置检查NVIC_IRQChannelCmd是否为ENABLE检查GPIO_EXTILineConfig是否调用唤醒后串口数据乱码唤醒后系统时钟变回HSI 8MHz波特率错乱在唤醒后立即调用SystemInit()恢复PLL到72MHz程序一上电频繁自动唤醒唤醒引脚悬空电平抖动触发中断把唤醒引脚配置为内部上拉/下拉输入避免浮空唤醒后程序跑飞进入HardFault中断服务函数里访问了未就绪的外设在进入STOP前关闭外设时钟唤醒后在ISR里不要访问未重新初始化或未开启时钟的外设用ST-Link连接后程序停在STOP模式无法下载芯片处于低功耗状态调试接口被关闭按住复位键点击下载等芯片复位瞬间松开复位键或在程序中提前加一个“进入STOP前延时等待”逻辑给调试器握手留时间独立看门狗导致STOP下反复复位IWDG使用LSI独立时钟STOP模式下仍继续运行进入STOP前关闭IWDG或取消使能或用窗口看门狗其时钟来自PCLK1STOP时同步停止5.2 排查思路详解上面这些问题里我在实际调试中印象最深的有两个详细说说排查过程。第一个是“按唤醒键没反应”。我当时查了很久的EXTI配置代码来回读了好几遍觉得逻辑完全正确EXTI_Line0使能了触发方式上升沿没错GPIO也映射了。后来用调试器打断点才发现中断服务函数根本就没被调用。排查到最后问题出在NVIC我只使能了EXTI0的NVIC通道但忘了设置抢占优先级和子优先级。如果不设置优先级寄存器NVIC是不会帮你判断并响应这个中断的。加上两行优先级配置后问题立即解决。第二个是“唤醒后串口打印乱码”。这个问题的背后机制值得深入理解。WFI唤醒后系统时钟自动切换到HSI8MHz但外设配置还是按72MHz的PLL来设的。以串口为例如果你的USART配置了115200波特率这个数字是在PCLK272MHz的前提下计算出来的。现在PCLK2变成了8MHz那实际波特率就变成了115200乘以8/72约12800接收端当然解不出来打印出来的全是乱码。解决办法就是在唤醒后第一时间调用SystemInit()重新把PLL拉起来。这里有个小细节SystemInit()只负责配置时钟不会重置外设寄存器。所以你只需要在唤醒后调用它外设本身不需要重新初始化串口配置还在恢复时钟后波特率就自动正确了。如果你的程序里把时钟配置写在了别的地方比如自己实现了一个ClockInit()函数那就在唤醒后调用你自己的函数效果一样。第三个值得说的是调试接口的问题。调试器ST-Link/J-Link和STM32的SWD接口在芯片进入STOP模式后会失去响应因为调试接口的时钟也停了。如果你正在调试程序不小心走到了STOP模式KEIL很可能直接弹出“Cannot access target”之类的错误看起来像芯片变砖了。解决办法其实很简单按住板子上的复位键不放在KEIL里点击下载同时在弹出来的下载窗口还没跑完时立刻松开复位键。这样能让调试器在芯片复位后、进入STOP前这段时间内抓住控制权。如果实在抓不住就把BOOT0拉高让芯片从系统存储器启动这样上电后不会执行你的应用程序然后重新烧录。5.3 功耗偏高的排查顺序最后补充一个排查功耗偏高的通用思路。很多人测到电流几百微安甚至几毫安就慌了其实按顺序排查就行。第一步先看是不是进入了真正的STOP模式。可以在进入STOP前点亮一个LED唤醒后熄灭。如果LED一直亮着说明根本没进STOP是程序逻辑出了问题。第二步看GPIO状态。把所有不用的GPIO都配置成模拟输入GPIO_Mode_AIN这个模式下的IO没有寄生电流路径是功耗最低的状态。如果你把IO配置成开漏输出但不拉低或者配置成浮空输入引脚上的寄生二极管就可能产生漏电。第三步看外部器件。LED限流电阻、LDO的静态电流、上拉电阻这些都是电流黑洞。LED串联10K电阻时静态电流都还有0.3mA待机时直接把这个支路切断才对。第四步最后才怀疑芯片本身。芯片底电流可以用数据手册里的参数对比一下如果实测远高于手册值检查是不是买到了翻新片或者焊接温度过高损坏了芯片内部电路。6. 经验总结与后续扩展方向这个停机模式测试工程写完、测完、数据记录完之后我最大的感受是低功耗设计和普通外设编程完全是两种思维。外设编程是你让它干活它怎么执行是确定的低功耗设计是你让它别干活但各种“寄生消耗”总在你意料之外。每一个微安级别的差异背后都可能藏着一个你没注意到的漏电路径。最后分享一个我在多个项目里反复使用的扩展思路。停机模式别单独用把它和RTC闹钟组合起来做一个低功耗任务调度器。具体做法是主程序处理完所有任务后进入STOP模式RTC设置一个定时闹钟比如1秒到10分钟之间闹钟唤醒后执行一轮任务完成后再次进入STOP。这个架构在很多物联网传感器节点中非常实用。代码结构也不复杂就是在main函数的while循环里做一个状态机空闲时调用PWR_EnterSTOPModeRTC中断里设置标志位主循环检测到标志就执行任务。如果还想继续深入建议你研究一下在两个方向上扩展这个工程。第一个是加一个ADC采样外部电压的电路在进入STOP前测一下电池电压能把唤醒后的运行时间做动态调整电池电压低就减少唤醒次数。第二个是配合一个MOS管开关让MCU在STOP模式下直接把外围传感器电路的电源切断这样待机电流就只包含MCU本身和LDO漏电流整体能做到非常低。这个工程源码虽然简单但它是一个可以反复利用的基础模板。以后做任何需要低功耗的产品都可以直接拿它当起点在上面叠加业务逻辑和外设驱动。踩过的坑、总结的数据也都能复用省下的时间实实在在地摆在那里。本文还有配套的精品资源点击获取
返回列表