4. STM32低功耗模式详解

功耗优化这件事,说白了就是跟每一毫安时较劲。很多工程师把精力全放在CPU睡眠模式上,却忽略了外设——那些悄悄在后台吃电的"小老鼠"。我做过一个表计项目,CPU睡眠时电流只有5μA,可一开RTC,直接飙到15μA。嗯,问题就出在外设配置上。

今天咱们就聊聊四个常用外设的低功耗玩法:RTC、ADC、DAC、定时器。它们各有各的脾气,配置对了,功耗能降一个数量级。

4.1 RTC:时间不能停,但电流可以降

RTC是低功耗场景的常客。它需要一直跑,但电流能不能再压一压?当然可以。

4.1.1 时钟源选择

RTC的时钟源直接影响功耗。我习惯用下表做选型参考:

时钟源 典型电流 精度 适用场景
LSE(32.768kHz外部晶振) ~0.6μA ±5ppm 高精度计时、日历
LSI(~40kHz内部RC) ~0.4μA ±5% 低精度唤醒定时
HSE分频(外部高频晶振) ~3μA+ 高 不推荐用于RTC低功耗

我个人建议:如果只是做周期性唤醒,用LSI就够了。需要精确时间戳或日历,才上LSE。别为了省那0.2μA去用HSE,得不偿失。

4.1.2 影子寄存器与自动刷新

RTC有个坑——影子寄存器。默认情况下,RTC每秒钟自动把日历数据同步到影子寄存器,供CPU读取。这个同步过程会产生额外的总线访问电流。

我曾经在一个电池供电的温湿度记录仪上,发现RTC电流比手册多了0.3μA。查了半天,原来是影子寄存器自动刷新没关。解决方案很简单:

// 关闭RTC影子寄存器自动刷新
RTC->CR |= RTC_CR_BYPSHAD;  // 旁路影子寄存器
// 之后读取RTC寄存器前,先检查RSF标志
while(!(RTC->ISR & RTC_ISR_RSF));  // 等待同步
uint32_t time = RTC->TR;  // 读取时间寄存器
小技巧:关闭影子寄存器后,每次读RTC前要轮询RSF标志。如果读频率很低(比如1秒1次),这点CPU开销可以忽略,但电流能省下0.2~0.3μA。

4.2 ADC:采样完就断电,别让它闲着

ADC是功耗大户。一个连续采样的ADC,电流轻松跑到几百μA。但很多场景下,我们只是每隔几秒采一次数据。这时候,按需供电就特别重要。

4.2.1 自动关断模式(Auto-Off)

STM32G0/L4系列支持ADC的自动关断模式。每次转换完成后,ADC自动断电,下次触发时再自动上电。这个模式能省掉90%以上的ADC待机电流。

// 配置ADC自动关断模式
ADC1->CFGR2 |= ADC_CFGR2_AUTO_OFF;  // 使能自动关断
// 配置常规转换
ADC1->CFGR1 |= ADC_CFGR1_CONT;      // 连续转换模式
// 启动转换
ADC1->CR |= ADC_CR_ADSTART;

注意:自动关断模式下,第一次转换需要额外等待ADC稳定时间(tSTAB,约几个μs)。如果采样间隔很短(比如小于1ms),自动关断反而可能增加平均功耗。我一般只在采样间隔大于10ms时才用这个模式。

4.2.2 采样时间与分辨率权衡

ADC的功耗跟采样时间和分辨率直接相关。采样时间越长、分辨率越高,功耗越大。我整理了一个经验表:

分辨率 采样周期 单次转换电流 适用场景
12位 1.5周期 ~80μA 快速检测、电池电压监测
10位 7.5周期 ~60μA 传感器数据采集
8位 1.5周期 ~40μA 粗略阈值判断
避坑:我曾经在ADC输入阻抗较高的场景下,为了省电把采样时间设成1.5周期,结果采样值偏差了5%。后来发现是采样电容没充满。记住:低功耗不能牺牲信号完整性。高阻抗输入(>10kΩ)时,采样周期至少设到7.5周期以上。

4.3 DAC:输出完就休眠

DAC在低功耗场景下用得少,但一旦用了,功耗问题就很突出。DAC的静态电流通常在100~200μA,如果一直开着,对电池是很大的负担。

4.3.1 输出缓冲器控制

DAC内部有输出缓冲器,用来提高驱动能力。但缓冲器本身会消耗电流。如果负载阻抗很高(比如接运放输入端),可以关掉缓冲器:

// 关闭DAC输出缓冲器
DAC->CR &= ~DAC_CR_BOFF1;  // 通道1关缓冲
// 设置输出值
DAC->DHR12R1 = 2048;  // 1.65V输出(3.3V参考)
// 使能DAC
DAC->CR |= DAC_CR_EN1;

关掉缓冲器后,DAC输出阻抗会升高到约15kΩ,但电流能从150μA降到30μA。嗯,这个取舍很划算。

4.3.2 波形发生器的低功耗用法

STM32的DAC内置了三角波/噪声发生器。如果用它做低频信号源(比如1kHz以下),可以配合定时器触发,每次转换完自动进入低功耗模式。

我做过一个音频信号发生器项目,用DAC输出1kHz正弦波,配合定时器触发和DMA,平均电流只有80μA。关键配置:

  • 定时器触发频率 = 采样率(比如32kHz)
  • DMA传输波形表,CPU全程睡眠
  • DAC输出缓冲器关闭(负载是运放高阻输入)

4.4 定时器:别让它在后台空转

定时器是STM32里最容易被忽视的功耗源。很多人习惯开一个定时器做系统心跳,却忘了它一直在跑。

4.4.1 时钟分频与预分频器

定时器的功耗跟时钟频率成正比。一个跑在72MHz的定时器,电流轻松到1mA以上。但很多场景下,我们只需要1ms的定时周期,完全可以用更低的主频。

我建议的做法:

  • 用LPTIM(低功耗定时器)替代普通TIM
  • LPTIM可以在LSE或LSI下运行,电流只有几μA
  • 如果必须用普通TIM,把预分频器设到最大
// 配置LPTIM在LSE下运行,1秒中断
LPTIM1->CFGR = LPTIM_CFGR_CKSEL;  // 选择LSE作为时钟源
LPTIM1->PSC = 0xFFFF;             // 最大预分频
LPTIM1->ARR = 32768;              // 1秒周期(LSE=32.768kHz)
LPTIM1->CR |= LPTIM_CR_ENABLE;    // 使能

4.4.2 脉冲宽度调制(PWM)的功耗陷阱

用定时器输出PWM时,如果占空比不是0%或100%,GPIO会持续翻转,产生额外的动态功耗。频率越高,功耗越大。

我记得有个同事做LED呼吸灯,PWM频率设到20kHz,结果整机电流多了2mA。后来改成1kHz,肉眼完全看不出闪烁,电流降了1.5mA。

经验法则:PWM频率够用就行。LED调光用1kHz,电机控制用10~20kHz,音频应用才需要50kHz以上。每降低10倍频率,GPIO动态功耗大约降90%。

4.5 知识体系总览

下面这张图总结了四个外设的低功耗配置要点,方便你快速回顾:

外设模块按需供电知识体系 RTC ADC DAC 定时器 时钟源选择 影子寄存器 自动关断 采样时间 缓冲器控制 波形发生器 LPTIM替代 PWM频率 核心原则 1. 按需供电:用之前打开,用完之后立刻关 2. 时钟降频:能跑低速就别跑高速 3. 硬件替代软件:用外设自动功能代替CPU干预 4. 权衡取舍:低功耗不能牺牲信号完整性和功能

4.6 实战配置清单

最后,我把自己做项目时常用的外设低功耗配置清单列出来,你可以直接当checklist用:

外设 配置项 推荐值 省电效果
RTC 时钟源 LSI(非精确计时) 省0.2μA
RTC 影子寄存器 关闭(BYPSHAD=1) 省0.3μA
ADC 自动关断 使能(采样间隔>10ms) 省90%待机电流
ADC 采样时间 满足精度下的最小值 省20~50%转换电流
DAC 输出缓冲器 高阻负载时关闭 省120μA
定时器 时钟源 LSE/LSI优先 省90%以上
定时器 PWM频率 满足应用的最低值 省50~90%动态功耗
我的习惯:每次画完原理图,先列一个"外设供电清单",标清楚每个外设在什么阶段打开、什么阶段关闭。然后对照手册,把每个外设的待机电流和运行电流算一遍。这样板子回来调试时,心里就有底了。

嗯,外设的低功耗配置,说白了就是"用多少给多少"。别让RTC用着LSE的精度却只做唤醒定时,别让ADC开着连续转换却只采一两个点,别让DAC开着缓冲器却接了个高阻负载。这些细节,才是真正拉开功耗差距的地方。