简介:STM32L系列官方例程包是一套面向低功耗嵌入式开发的完整示例集合,基于意法半导体官方标准外设库V1.3.1构建,适配基于Cortex-M0+或Cortex-M3内核的超低功耗MCU。例程覆盖模数转换、数模转换、外部中断、I2C总线通信、通用输入输出控制、串口收发等常用外设驱动,并延伸至电池供电设备、传感器采集、调试终端等典型场景,适合嵌入式工程师与学生快速上手。资源共1443个文件,压缩包约24.25MB,以C源码和头文件为主体,辅以网页文档、示意图、文本说明及IDE工程文件,目录结构清晰,便于按模块查阅和直接编译。除基础外设外,还提供基于STM32L152-EVAL评估板的LCD显示、SDIO存储卡、定时器、实时时钟、Flash访问等应用级例程,帮助理解时钟树、中断优先级与低功耗模式的协同设计。已有2826人浏览学习,整套例程遵循官方库组织规范,既可作为项目基础模板,也可作为驱动开发和调试排错参考。
1. 为什么大家都在找STM32L例程
STM32L从名字就能看出来,L不是白叫的,Low power。它把超低功耗这一块做到了一个很舒服的位置,和STM32F系列最大的区别,不是性能,而是整个系统的功耗设计哲学:从内核、时钟、外设到电源管理,每一层都在为“省电”服务。你搜索STM32L例程,搜到的基本都是嵌入式开发者在做电池供电设备、传感器节点、智能仪表、可穿戴设备时整理出来的工程模板,目标很明确:把一个能跑、能睡、能唤醒的外设工程快速搭起来。
找这类例程的大概有三类人。一类是从STM32F103这类经典片子切过来的,F103的寄存器、标准外设库他们很熟,但换到L系列后发现HAL库、LL库、低功耗外设全都不一样,需要一份例程做对照。第二类是直接做产品的,比如水表、气表、定位器、温湿度记录仪,他们不想从零看手册,就想拿一个现成工程,把传感器、无线模块接上,然后把整机电流调到微安级。还有一类是学习型用户,手里有NUCLEO-L476RG或者STM32L072 Discovery板,想理解LPTIM、LPUART、RTC唤醒这些新东西到底怎么用。这三类人目标不同,但最后都会落到同一个问题上:一套写得清楚、能直接跑的STM32L例程,到底包含哪些东西?这篇文章就把这个问题讲透。
我在嵌入式一线干了十几年,早期做STM32F系列,后来转向低功耗产品开发,STM32L系列成了我最常用的平台。下面这些内容不是手册翻译,而是我从实际项目中提炼出来的经验。准备从环境搭建、例程结构、低功耗核心、调试避坑等几个维度,把一套有参考价值的STM32L例程该有的样子完整拆给你看。
2. 环境搭建:CubeMX、HAL库还是LL库
2.1 开发环境怎么搭最省心
我用过的组合里,最推荐的是STM32CubeMX生成工程,再配Keil MDK或者STM32CubeIDE编译调试。STM32CubeMX选芯片型号时,直接输入STM32L4、STM32L0或者STM32L1,列表会筛出对应型号。关键一步是在Project Manager里选Toolchain/IDE,常用的是MDK-ARM或STM32CubeIDE。如果你用的是CubeIDE,CubeMX生成的.ioc文件可以直接打开,不需要再手工移植。
关于库的选择,这里必须多说两句。STM32L系列有HAL库和LL库两套API,很多人一上来就选HAL,其实不然。HAL库的特点是抽象层次高,外设初始化、数据收发都有完整封装,但封装也带来了额外的函数调用开销和锁机制,对低功耗场景不友好。LL库在寄存器和HAL之间做了一层轻薄封装,很多外设操作可以做到时钟级精确控制,特别适合PWR、LPTIM、RTC这种低功耗外设。我的做法是:外层逻辑用HAL,低功耗模式切换和唤醒配置用LL,两者可以混用。CubeMX里其实每个外设都可以单独勾选Use LL或者Use HAL,不用全项目统一,这点很多人不知道。
2.2 官方例程包的结构和定位技巧
ST官方把例程包叫STM32Cube_FW_L4(或L0、L1),在STM32CubeMX安装目录的Repository文件夹下可以找到,比如STM32Cube\Repository\STM32Cube_FW_L4_V1.17.0。打开之后,主要看Project、Drivers、Middlewares三个目录。最值得看的是Projects下的板级例程,命名规则很清晰,比如Projects\NUCLEO-L476RG\Examples\PWR\PWR_STOP2。每个例程文件夹里有Core、App、EWARM(IAR)、MDK-ARM、STM32CubeIDE几个子目录。想直接在Keil里跑,就进MDK-ARM双击开发工程。想用命令行看代码,Core和App下的源码就是全部主体。
定位技巧:如果你要的是低功耗相关,优先看Examples\PWR;要串口,就看Examples\UART或Examples\LPUART;要做I2C传感器读取,看Examples\I2C。官方例程虽然写得偏验证板,但代码风格统一,注释也算到位,关键是PWR例程里把Stop模式、Standby模式、不同唤醒源的配置都演示了,这比任何第三方教程都靠谱。我第一次调Stop2模式,就是直接拿官方PWR_STOP2例程改出来的,省了至少一天查手册的时间。
3. 一套STM32L例程的核心内容拆解
3.1 GPIO和时钟:所有例程的地基
不管是什么L系列例程,GPIO部分都是第一步。STM32L和F系列一样,GPIO有输入、输出、复用、模拟四种模式,但低功耗设计里有几个F系列不太强调的细节:一是未使用的引脚在进入低功耗前要设置为模拟模式,因为数字输入模式会产生漏电流,悬空引脚更是容易造成额外功耗;二是输出引脚的速度等级不要盲目设High,高速率意味着更大的翻转电流和EMI,外部设备不要求高速的话,用Low或Medium就足够。
时钟部分,官方例程一般默认用MSI(Multi-speed internal oscillator)作为系统时钟,因为MSI可以做到低功耗和高精度的平衡,而且可以自动根据电压调节频率。STM32L4的MSI最大可以到48MHz甚至更高,但实际例程很多时候只跑4MHz或者8MHz,因为很多低功耗应用根本不需要高主频,频率越高功耗越大,这是最简单的功耗公式:P = C × V² × f。在例程里改时钟,我习惯用CubeMX里的Clock Configuration页面去可视化调整,直接看HCLK、PCLK1、PCLK2等总线频率的变化,避免手工配置时把总线时钟配超了。
3.2 低功耗三档:Sleep、Stop、Standby
低功耗是STM32L例程的重头戏。绝大多数例程都会把三个睡眠档位讲清楚:
Sleep模式最简单,CPU停止,外设还在跑,中断随时可以唤醒。适合那些有数据正在传输、暂时等一等的场景。Stop模式(STM32L4里细分为Stop0/1/2,L0/L1也有类似Stop模式)会把大部分时钟停掉,SRAM保持,唤醒后可以从断点继续执行,典型功耗在微安级别。Standby模式就彻底了,除了RTC、备份寄存器和几个唤醒引脚,其他全断电,功耗可以到纳安级,代价是唤醒后整个程序相当于重新启动。
我之前做过一个电池供电的温湿度记录仪,就是用Stop2模式做周期唤醒:RTC每隔5分钟触发一次中断,唤醒后读温湿度传感器,存到Flash里,然后重新进Stop2。实测整机平均电流还不到3微安,三节AAA电池撑了大半年。这个工程的核心就是模仿官方PWR_STOP2例程里的进入流程,先把串口关掉,再把ADC关掉,配置唤醒源,设置低功耗稳压器模式,最后执行WFI(或WFE)。注意,WFE其实在某些场景下比WFI更合适,比如需要事件唤醒时,两者区别在ARM文档里有专门说明,但例程里常见用法是WFI,原因是它最简单直观。
进入Stop前,一定要把外设时钟关掉,用__HAL_RCC_xxx_CLK_DISABLE()逐个关闭,否则外设虽然没运行,但它的时钟还在,会不断产生噪声和漏电。很多初学者写的低功耗例程电流测下来不理想,问题大多出在这里。
下表是我常用的三种睡眠模式对比,整理例程时可以直接参考:
| 模式 | CPU状态 | 外设时钟 | SRAM | 常用唤醒源 | 实测电流参考 |
|---|---|---|---|---|---|
| Sleep | 停止 | 保持 | 保留 | 任意中断 | 毫安级别 |
| Stop2 | 停止 | 大部分关闭 | 保留 | RTC/LPTIM/EXTI/LPUART | 微安级别 |
| Standby | 停止 | 全部关闭 | 丢失 | RTC/唤醒引脚/复位 | 纳安级别 |
3.3 串口、I2C、ADC的例程细节
串口在低功耗场景里常常换成LPUART(Low Power UART),它可以在Stop模式下继续监听外部数据,不需要CPU全程介入,用起来和普通UART类似,但引脚要连到特定的低功耗引脚上。很多例程会在系统手册里标注哪些引脚支持LPUART功能,比如L4系列的PA0、PA1之类的复用引脚。如果只是做调试打印,我建议进入Stop前把UART关掉,等唤醒后再开,否则串口模块在睡眠状态下的空闲中断很容易把系统不小心拉起来。
I2C例程注意点在于STM32L系列很多信号还是开漏输出,需要外部上拉或者内部上拉。官方例程一般会在I2C初始化里把内部上拉打开,但如果你外接传感器板上已经有上拉电阻,内部上拉和外部上拉并联之后,可能导致总上拉太强,I2C上升沿变慢,通讯出错。所以拿到别人移植好的I2C例程,先检查上拉配置。
ADC部分,STM32L系列支持硬件过采样和多种采样时间调节。低功耗例程里用ADC,一般不会让ADC持续采样,而是等唤醒后一次性触发采样,采完马上关掉。这部分例程我最常用的写法是:HAL_ADC_Start_DMA读一个DMA buffer,数据到了之后在中断里立刻HAL_ADC_Stop_DMA,避免DMA一直在跑把功耗带上去。
4. 移植和调试中的常见坑与实测经验
4.1 低功耗模式下调试器掉线
这是几乎所有第一次搞STM32L低功耗的人都会遇到的问题:仿真器在程序进入Stop或者Standby之后,IDE的调试连接会断掉,断点再也打不中,甚至Keil报Cannot access target。原因很简单,调试接口本身也是外设,功耗一降,内核时钟停了,SWD调试模块也就没法工作。
解决方法有两个。一是临时屏蔽掉进入低功耗的代码,先跑通功能,最后再把低功耗打开。这是最稳妥的流程,别一上来就全开低功耗然后想在上电那一刻打断点。二是用DBGMCU配置寄存器,比如在调试期间把DBG_STOP位、DBG_STANDBY位打开,这样CPU进入低功耗模式后,调试器能检测到连接,强行保持内核时钟,不会完全掉线。但是要注意,这个寄存器是调试专用的,设备量产后如果还留着,会让功耗升高,所以量产固件里必须把这个配置去掉或做好条件编译。
4.2 唤醒源优先级和看门狗打架
低功耗例程里,唤醒源一般有RTC闹钟、LPTIM超时、LPUART接收、外部中断引脚等。多个唤醒源同时开的时候,一定要在唤醒中断处理里做出优先级判断。我在实际项目里遇到过一个问题:设备在低功耗模式下应该靠外部中断唤醒,但每次按键唤醒后,看门狗立刻把系统复位了。折腾了很久才发现,代码在进入Stop前没有把独立看门狗暂停,而看门狗在Stop模式下还在计数,一旦超时,系统只能复位。
解决办法是在进Stop前及时喂狗,或者根据需求开RTC去周期性地喂狗,再彻底进入低功耗模式。LPTIM也可以作为一个低功耗喂狗定时器,在Stop模式下定时唤醒系统去喂狗,然后继续睡,这样既保证看门狗的监控功能,又不会让复位打断正常工作流程。
4.3 电流测量时被板载器件坑
如果用的是NUCLEO或Discovery板,测低功耗电流时,板上那颗ST-LINK的功耗是跑不掉的。以NUCLEO为例,ST-LINK部分和MCU部分共用一个供电连接,哪怕仿真器没用来在线调试,只要供电在,ST-LINK芯片就要吃掉几个毫安,你还想测微安电流,完全测不出来。
解决办法是看板子原理图,找到把ST-LINK和MCU供电隔开的那颗跳线帽或0欧电阻,NUCLEO板上一般是一个黄色跳线,标签是SB或IDD相关。我用NUCLEO-L476RG测低功耗时,会直接把跳线拔掉,然后用外部3.3V电源从MCU的VDD引脚单独供电,并把电流表串进电源回路里。这时候测到的才是MCU本身的真实功耗。没有跳线帽的板子,用刻刀切断PCB走线也行,但那种方法太伤板子,不推荐。
4.4 引脚浮空与GPIO速率对功耗的影响
很多例程跑在正常模式时一切正常,一进低功耗,电流就是比手册多出几十微安。排查方向除了时钟和外设,九成问题出在GPIO上。未使用的GPIO如果没有配置成模拟模式,它们会处于数字输入状态,引脚悬空时电平不确定,输入缓冲器里的CMOS反相器会在大约中间电压附近来回翻转,每根引脚都可能多出微安级的漏电。几十个引脚加起来,电流就上去了。
另一种情况是GPIO输出高电平时,外设模块处于空闲状态,但引脚上的外部电阻还在消耗电流。例如,你用GPIO去控制LED,输出高电平时LED点亮,虽然逻辑没问题,但进低功耗前不清掉这个状态,LED就一直亮着,电流一下子多了好几毫安。所以在进入低功耗前,要遍历一遍所有GPIO,把控制外设电源的引脚保持低电平,把悬空的引脚配成模拟输入,这个习惯要内化成肌肉记忆。
为了快速排查引脚问题,我整理了一个检查表,在调试低功耗例程时逐条对照:
| 检查项 | 操作 | 预期效果 |
|---|---|---|
| 未使用引脚 | 配置为模拟模式 | 消除输入缓冲器漏电 |
| 使用中引脚 | 确认为明确电平 | 避免悬空翻转 |
| LED/指示引脚 | 进入低功耗前拉低 | 避免外部电阻耗流 |
| 外设时钟 | 逐个关闭不需要的外设 | 减少内部时钟活动 |
| 调试口 | 量产版本关闭DBGMCU配置 | 降低量产后功耗 |
5. 我的实战建议:一个电池设备的低功耗例程怎么落地
5.1 参考路径与最小改动方案
如果你要开发一个基于STM32L系列的电池供电产品,我的建议是不要从零写,也不要大改特改。最佳路径是:先找同型号官方板子的PWR例程,比如STM32Cube_FW_L4下NUCLEO-L476RG的PWR_STOP2例程,然后在此基础上只改三处:第一,把系统时钟频率改成你产品所需的最低频率;第二,把唤醒源从官方的按键外部中断改成你产品实际的RTC闹钟或者LPTIM定时器;第三,把你产品的外设初始化代码加进去,但同时补上进入低功耗前关闭这些外设的代码。这样整个低功耗核心逻辑是官方验证过的,你只需要处理自己产品的外设部分,调试难度降低一个量级。
我之前做过一个烟雾报警器的低功耗改造项目,就是在官方PWR_STOP2例程基础上,加了一个SGP30温湿度传感器和一个NB-IoT模组。SGP30用I2C读,NB-IoT用UART通信,两套外设在正常情况下跑,进Stop前把两个外设的时钟关掉,GPIO配成浮空输入或固定在低电平,然后靠RTC每天固定时间唤醒,采集数据、上传云端,再继续睡。整机平均电流实测控制在10微安以内,完全满足烟感国标对电池寿命的要求。
5.2 实测数据与避坑补充
我这里放一个最近实测的NUCLEO-L476RG裸板数据,给各位一个参考范围:运行态跑4MHz MSI时,MCU电流大概在1.3mA左右;进Stop2模式,所有外设关闭、SRAM保持,电流在1.2uA左右;如果启用LPTIM作为定时唤醒源,额外增加0.3uA左右。到了Standby模式,电流能压到0.1uA以下,但唤醒就要走完整复位流程。这些数值跟手册数据差别不大,关键是要排除板载ST-LINK和LED的消耗。
最后分享一个很多人都会踩的坑:调试阶段用串口打印数据,忙起来就忘了在进低功耗之前把串口关掉,结果设备看起来进入睡眠了,实际串口空闲中断把系统一次次唤醒,或者功耗莫名其妙高了几百微安。我的习惯是在低功耗入口函数里,用条件编译包一段“关闭所有调试外设”的代码,调试开启时走打印路径,量产版本直接走低功耗路径,两边互不干扰。这样既保留调试能力,也不用担心产品上线后功耗翻车。
如果你也在折腾STM32L的低功耗,不妨按这个思路去整理自己的例程,先跑通官方PWR例程,再逐步加外设,最后再处理细节坑。你会发现,低功耗开发并不是玄学,只要把每一层功耗来源都搞清楚,数据完全在可控范围内。
本文还有配套的精品资源,点击获取