4. 外设模块的按需供电:RTC、ADC、DAC、定时器的低功耗配置
功耗优化这件事,说白了就是跟每一毫安时较劲。很多工程师把精力全放在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; // 读取时间寄存器
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 | 粗略阈值判断 |
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。
4.5 知识体系总览
下面这张图总结了四个外设的低功耗配置要点,方便你快速回顾:
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开着缓冲器却接了个高阻负载。这些细节,才是真正拉开功耗差距的地方。