☰
STM32F103C8停机模式低功耗测试程序详解:KEIL工程源码与电流实测
2026/10/26 0:15:04 网站建设 项目流程

简介:本资源是一份面向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预分频,LSI=40000,分频到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和外设的电流差了不少)
  • 停机模式(LPDS=1,低功耗调压器):实测约15.8uA
  • 停机模式(LPDS=0,正常调压器):实测约286uA

整个板子(含LDO、USB转串口芯片空耗)的待机电流会比这个高一些,大概三四百微安。如果把USB转串口芯片断开,板子整体待机电流能压到25uA以下。

4.2 实测数据与对比分析

上面的数据非常有信息量,我逐条分析一下。

LPDS=1和LPDS=0的电流差异接近18倍,这直接说明了“低功耗调压器”选项的真实价值。如果你的应用对唤醒响应时间要求不那么苛刻,LPDS=1几乎是无脑选的。

还有一个细节:进入STOP前我已经关闭了APB2外设的时钟,但运行电流从28.5mA只降到了16.2mA,说明GPIO引脚本身的漏电流和LDO空耗占据了很大比例。所以,如果你的产品对功耗要求严苛,仅仅靠进入STOP模式是不够的,还需要把GPIO配置成合理的状态。

再来看唤醒时间。停机模式的唤醒时间,在LPDS=0时大约几十微秒,LPDS=1时会明显增加。我没有用示波器精确测量,但用逻辑分析仪观察到,从触发唤醒到串口正常打印第一个字符,LPDS=0约30us左右,LPDS=1约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或取消使能,或用窗口看门狗(其时钟来自PCLK1,STOP时同步停止)

5.2 排查思路详解

上面这些问题里,我在实际调试中印象最深的有两个,详细说说排查过程。

第一个是“按唤醒键没反应”。我当时查了很久的EXTI配置,代码来回读了好几遍,觉得逻辑完全正确:EXTI_Line0使能了,触发方式上升沿没错,GPIO也映射了。后来用调试器打断点才发现,中断服务函数根本就没被调用。排查到最后,问题出在NVIC:我只使能了EXTI0的NVIC通道,但忘了设置抢占优先级和子优先级。如果不设置优先级寄存器,NVIC是不会帮你判断并响应这个中断的。加上两行优先级配置后,问题立即解决。

第二个是“唤醒后串口打印乱码”。这个问题的背后机制值得深入理解。WFI唤醒后,系统时钟自动切换到HSI(8MHz),但外设配置还是按72MHz的PLL来设的。以串口为例,如果你的USART配置了115200波特率,这个数字是在PCLK2=72MHz的前提下计算出来的。现在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_EnterSTOPMode,RTC中断里设置标志位,主循环检测到标志就执行任务。

如果还想继续深入,建议你研究一下在两个方向上扩展这个工程。第一个是加一个ADC采样外部电压的电路,在进入STOP前测一下电池电压,能把唤醒后的运行时间做动态调整,电池电压低就减少唤醒次数。第二个是配合一个MOS管开关,让MCU在STOP模式下直接把外围传感器电路的电源切断,这样待机电流就只包含MCU本身和LDO漏电流,整体能做到非常低。

这个工程源码虽然简单,但它是一个可以反复利用的基础模板。以后做任何需要低功耗的产品,都可以直接拿它当起点,在上面叠加业务逻辑和外设驱动。踩过的坑、总结的数据,也都能复用,省下的时间实实在在地摆在那里。

本文还有配套的精品资源,点击获取

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询