“低功耗设备做电源管理,绕不开的一句话就是:省下来的每微安都是设计审美的胜利。”
这次说的是一套完全从零搭起来的电池供电方案,主控是 STM32L031C6,电源管理芯片用了 PCA9422。两个芯片都不算热门网红款,但拼在一起能把整机的待机电流、动态功耗、充电管理全都管起来,尤其适合那些要跑数月甚至一年不换电池的小型物联网节点、医疗贴片或者便携检测设备。
我把它整理成了一份可以照着抄的实战记录,从硬件选型、原理图设计到 I2C 寄存器配置、低功耗状态机,再到调试过程中真实踩过的坑,全部拆开讲清楚。如果你是刚接触 PMIC 加低功耗 MCU 组合的入门者,或者想给自己项目换一套更省电的供电方案,这篇内容可以参考。
1. 为什么选 PCA9422 加 STM32L031C6 这对组合
1.1 先看懂 PCA9422 在电源链路里的位置
PCA9422 是一颗非常典型的锂电池供电场景 PMIC,封装不大,但内部集成了电池充电管理、降压转换器、一路到多路 LDO 输出,以及看门狗、欠压锁定、过流保护这些基础保护机制。它的设计思路很明确:用一颗芯片代替一堆分立 DCDC、LDO 和充电管理电路,把电源侧的静态电流压低,同时把 PCB 面积和放错概率一起减下来。
STM32L031C6 则是低功耗 MCU 里的老熟人,Cortex-M0+ 内核,运行功耗低,支持多种睡眠模式,还带硬件 I2C、UART、SPI 和 ADC。实际做下来,它最舒服的搭配就是 PCA9422 这种自带输出电压控制、还能反馈中断信号的 PMIC,因为 MCU 可以把大部分时间睡在 Stop 模式里,等 PMIC 用中断把它拉醒。
选品时我对照过几款常见的超低功耗 PMIC,PCA9422 吸引我的点有三个:一是它的待机静态电流能做到个位数微安级,二是 I2C 寄存器完全开放,可以动态关掉无用输出轨,三是充电曲线的调节范围足够覆盖大多数 300mA 到 800mA 的小容量电池充电场景。
1.2 为什么不用分立 LDO 加 DCDC 拼方案
有人会问,既然有了 STM32L031C6,为什么不干脆用一颗 LDO 把电池电压降到 3.3V,再配一颗充电芯片了事?这种方案在小电流传感器板子里很常见,我也做过几版,但问题在实际测量阶段暴露得很快。
分立器件组成的电源链路过压过流保护要自己搭,充电截止要自己写算法,主控休眠时 LDO 自身静态电流还在继续烧。比如一颗 3.3V LDO 的空载电流动辄 20 到 50 微安,对于整个系统底电流预算卡在 10 微安以内的项目来说,已经完全不可接受。PCA9422 把待机模式下的静态电流压到比 MCU 自己的 RTC 唤醒电路还低,这才是整套方案最核心的价值。
还有一个关键点是“电源轨的动态控制”。PCA9422 的每路输出都可以通过 I2C 开关,主控可以根据当前任务状态把传感器电源、射频模块电源单独切断,这在分立方案里往往要加 MOSFET 开关电路,既占体积又增加导通电阻。PMIC 的好处是这些开关逻辑已经做到芯片内部,路径短,压降小,管理起来也顺手得多。
1.3 功耗预算和实际应用的匹配度
这套组合的目标应用范围很清晰:锂电池容量在 200mAh 到 1500mAh 之间,系统峰值电流不超过几百毫安,绝大多数时间处于浅睡眠或者深度睡眠状态。我实际用它做过一个每五分钟采集一次温湿度并通过低功耗射频上报的小设备,整机在深度睡眠下的实测电流能压到 7 微安左右,这对 PMIC 加 MCU 的组合来说表现已经不错。
预算拆解大致是:PCA9422 自身待机消耗 2 到 3 微安,STM32L031C6 的 Stop 模式加 RTC 唤醒约 1.5 微安到 2 微安,再加上外部上拉电阻、电压采样分压电路的漏电,总共就落到 6 到 8 微安。如果用得好,这几乎是当前工艺条件下同等级方案的一个比较合理的下限。
2. 硬件设计和物料清单要点
2.1 原理图设计关键位置和电源树规划
这块板子上的电源树我分成了三级:第一级是电池直接进入 PCA9422 充电输入和主降压输入;第二级是 PCA9422 输出的一路核心系统电压,用于 MCU 和数字逻辑;第三级是 PCA9422 的两路 LDO 输出,分别给传感器小系统和高精度模拟前端供电。
原理图里第一需要注意的地方是电池端到 PCA9422 的输入路径必须加一个尽可能靠近芯片 VIN 引脚的 1uF 陶瓷电容,同时在电池正负极之间再放一个 10uF 到 22uF 的储能电容。原因很简单:当 MCU 或射频模块瞬间抽流时,电池内阻和线缆电感的压降必须靠这组电容来缓冲,否则容易触发 PMIC 的欠压保护。
第二处关键位置是 I2C 的两根线 SDA 和 SCL。注意 PCA9422 的 I2C 地址是由 ADDR 引脚决定的,原理图里要给 ADDR 留一个默认电阻配置,不能悬空。上拉电阻我选的是 4.7kΩ,因为 MCU 供电是 3.3V,PCA9422 的逻辑电平也是 3.3V 兼容,这个阻值配合 400kHz 的 I2C 速率完全没有问题。
还有一个容易忽略的地方是 PCA9422 的 INT 中断引脚。这颗芯片会把电池充电完成、输入电源插拔、欠压保护触发等事件都映射到 INT 引脚,所以必须接到 MCU 的一个具有中断唤醒能力的 GPIO 上,不能随便接。我接的是 PA0,这样整个系统在深度睡眠时依然可以被 PMIC 的充电事件唤醒。
2.2 PCB 布局与地回路处理
电源芯片的布局经验和普通数字电路不太一样。我在第一版 PCB 上吃过亏,当时把 PCA9422 的输入电容放得比较远,结果示波器实测在射频模块发射时,系统电压上出现了一个明显的高频振铃,幅度接近 300mV,导致 MCU 频繁复位。
后来重新布局,规则很简单:输入电容、输出电容、电感必须尽量紧贴对应引脚;降压转换器的功率回路(输入电容、电感、输出电容之间形成的环路)面积越小越好;地平面尽量连续,不要在功率回路上挖孔或走信号线。
PCA9422 的芯片底部有散热焊盘,建议原理图里把它接地,PCB 上打几个过孔到主地平面。这颗芯片本身功耗不大,但稳定散热能避免高温下充电电流被保护机制限制,影响充满时间。
LDO 输出的走线要注意一点:模拟供电的 LDO 输出要走细短管线到负载,不要跟数字电源在远距离上并行,否则噪声容易串过去。我在温湿度传感器供电上单独加了一个 0.1uF 的滤波电容,实际采样数据的抖动明显小于不加的情况。
2.3 物料选型与关键参数计算
PCA9422 的降压转换器需要一颗电感,电感值的选择直接影响纹波电流。我按负载最大 300mA 设计,开关频率 1.5MHz,输入电压来自锂电池最高 4.2V,输出电压 3.3V。电感值计算结果落在 2.2uH 左右比较合适,我选了额定饱和电流 1A 以上的功率电感,这样负载瞬变时不会有明显退感。
电容方面,降压输出侧用了一颗 22uF 的 X5R 陶瓷电容做主体储能,再加一颗 0.1uF 高频去耦电容。这里特别说明一下,X5R 和 X7R 的额定电压建议至少留 1.5 倍以上的降额,否则直流偏压下实际容量可能缩水过半,实测纹波会明显变大。
电池充电电阻的选型要配合锂电池的容量。我那块电池容量是 550mAh,充电电流设为 0.5C 也就是 275mA。PCA9422 充电电流可以通过设定电阻或者 I2C 配置调整,我选择用 I2C 动态设置,因为这样可以在系统调试时随意改变充电电流,而不用改电阻重新打板。
还有一组容易被忽视的参数是电池电压采样分压电阻。如果要用 ADC 精确判断电池剩余量,分压电阻的精度必须在 1% 或者更好,并且静态分压电流要尽量小,否则这颗电阻会不断消耗电池能量。我用了两颗 1MΩ 电阻串联,总静态电流不到 2 微安,再把分压后的信号经过一个 RC 低通进 MCU 的 ADC 引脚。
3. I2C 通信和寄存器配置实战
3.1 从零手写一套够用的 I2C 读写驱动
STM32L031C6 硬件上有 I2C 外设,可以直接用标准外设库或者 LL 库,但为了让读者能明白底层发生了什么,我建议先从硬件 I2C 的时序入手写一个简化版本,再封装成上层寄存器读写函数。
实际项目中我用了 STM32 的硬件 I2C 接口,配置为 400kHz 快速模式,主模式收发。以下是 I2C 写寄存器的基本函数,基于 HAL 库实现,逻辑清晰:
#include "stm32l0xx_hal.h" #define PCA9422_I2C_ADDR 0x12 // 根据 ADDR 引脚确认 static I2C_HandleTypeDef hi2c1; uint8_t pca9422_write_reg(uint8_t reg, uint8_t val) { uint8_t buf[2] = { reg, val }; HAL_StatusTypeDef ret = HAL_I2C_Master_Transmit(&hi2c1, PCA9422_I2C_ADDR << 1, buf, 2, 100); if (ret != HAL_OK) { // 可以在这里打日志,或者做重试 return 1; } return 0; } uint8_t pca9422_read_reg(uint8_t reg, uint8_t *val) { HAL_StatusTypeDef ret1 = HAL_I2C_Master_Transmit(&hi2c1, PCA9422_I2C_ADDR << 1, ®, 1, 100); if (ret1 != HAL_OK) { return 1; } HAL_StatusTypeDef ret2 = HAL_I2C_Master_Receive(&hi2c1, PCA9422_I2C_ADDR << 1, val, 1, 100); if (ret2 != HAL_OK) { return 1; } return 0; }写读函数时有个小细节:读寄存器之前必须先发一个写命令把目标寄存器地址发送给芯片,然后立刻重发 START 进入读模式。HAL 库里分两次调用 Master_Transmit 和 Master_Receive 即可,但因为中间有系统调度延迟,如果对整个时序要求比较严苛,可以在两次调用之间不释放总线,用 HAL_I2C_Master_Sequential_Transmit 这种组合帧方式来做。
3.2 常用寄存器解读与配置顺序
PCA9422 的寄存器不算多,但贸然乱配置很容易把输出弄丢。我根据数据手册和实验总结了一套稳定的上电配置顺序,这个顺序比具体某一个寄存器的值更关键。
第一步是先关掉不必要的 LDO 输出,把默认上电打开的所有外部负载断电,这样 MCU 在初始化阶段能少一些意外的电流冲击。第二步设置降压转换器的输出电压。第三步根据电池类型设置充电电压和充电电流。第四步打开需要的 LDO 并设定电压。第五步配置中断使能,告诉 PCA9422 哪些事件可以触发 INT 引脚。
关键寄存器信息我整理成了表格,供大家参考:
| 寄存器功能 | 常见地址(我手头这颗) | 关键位说明 | 我用到的值 |
|---|---|---|---|
| 系统状态 | 0x01 | 充电状态、输入电源检测 | 只读,用于查询 |
| 降压输出控制 | 0x02 | 输出电压选择、输出使能 | 配置为 3.3V,使能 |
| LDO1 控制 | 0x03 | 输出电压选择、使能 | 配置为 2.8V,使能 |
| LDO2 控制 | 0x04 | 输出电压选择、使能 | 默认关闭 |
| 充电控制 | 0x05 | 充电电压、恒流值选择 | 4.2V,250mA |
| 中断使能 | 0x06 | INT 引脚事件使能 | 使能充电完成和输入拔插 |
| 芯片复位 | 0x0F | 软件复位 | 只在异常时写 0x01 |
配置过程中我最想提醒的一点是:不要在芯片正常工作时只写一个电压寄存器,却不写使能位。PCA9422 很多输出轨默认是关闭的,只改电压不会生效,检查的时候容易误判成芯片坏了。
3.3 中断处理与状态上报
PMIC 的优势之一是把电源事件主动推给主控。我在代码里把 INT 引脚的下降沿配成了外部中断,STM32L031C6 从 Stop 模式被唤醒后,第一件事就是读 PCA9422 的系统状态寄存器,判断唤醒原因。
典型的处理流程是:MCU 读取 0x01 寄存器,判断是不是充电完成事件,如果是就更新电池状态标志,进而决定是否关闭充电通路降低功耗;如果检测到输入电源拔出事件,就要立刻把系统切到电池供电模式,并且关闭大电流的外设。
还有一个经验是,读取状态寄存器后最好再读一次,因为有些 PMIC 的中断状态是读后自动清除的,如果系统里 Demo 多个任务共享 I2C 总线,一次没读干净可能丢掉事件。我在正式固件里设置了一个简单状态机,每次电源事件至少连续读两次确认,再进入相应的业务逻辑,实测下来误触发几乎为零。
4. 低功耗状态机设计与实测调优
4.1 状态划分:正常运行、浅睡眠、深度睡眠
整个系统我定义了四种工作状态。正常运行态下 PCA9422 全部输出轨出于全速状态,MCU 时钟跑到 32MHz,处理采集、通信这类高负载任务。浅睡眠态下 MCU 进入 Low-power Run 模式,PCA9422 关掉模拟 LDO 输出,只保留 3.3V 主电源和 RTC 相关外设。深度睡眠态下 PCA9422 连主降压输出的负荷都尽量拉低,MCU 进入 Stop 模式,只留外部中断和 RTC 唤醒。
状态切换的核心逻辑就在于“谁有权关电源轨”。我的策略是,只有当 MCU 确认自己已经把需要保存的数据都写进了非易失区,才会向 PCA9422 发出关断相应 LDO 的指令。如果数据还没落盘,哪怕睡眠性能再好,也绝对不能关电源,否则丢掉数据反而得不偿失。
这样的设计让系统对“睡眠”和“数据安全”解耦开:睡眠由 MCU 主动请求,电源轨的关闭和恢复由 PMIC 的 I2C 指令执行,数据安全的约束只体现在状态机流程里,不必去修改硬件配置。
4.2 实测电流数据与对比分析
我在三个典型外部条件下做了整机电流实测,数据如下:
| 系统状态 | PCA9422 MCU 模式 | 实测整机平均电流 | 备注 |
|---|---|---|---|
| 正常运行(射频发送) | 全速运行,全部轨打开 | 峰值 82mA,平均 35mA | 射频发送持续时间约 20ms |
| 浅睡眠 | MCU Low-power Run,LDO1 关 | 约 12 微安 | 保留 RTC 和外部中断 |
| 深度睡眠 | MCU Stop,主输出保持 | 约 6.8 微安 | 每 30 秒由 RTC 唤醒一次 |
需要特别说明的是,这个 6.8 微安是在 3.3V 主输出一直打开的前提下测到的。如果项目允许极低功耗场景下把主输出也关掉,只靠 PCA9422 的看门狗和 MCU 的少量状态维持,整机电流还能再降一截,但我没这么做,因为频繁关闭主输出会让系统唤醒时间多出几毫秒,还增加了状态恢复复杂度。
对比以前用分立 LDO 搭建的旧方案,旧方案浅睡眠电流大约是 45 微安,深度睡眠也有 18 微安。换上 PMIC 方案后,浅睡眠电流下降了接近四倍,脉冲式任务的单次功耗节省更是明显。如果按典型的日使用频率(10 次正常任务、每次 5 秒)估算,同等电池容量下续航时间大概能提升 20% 以上。
4.3 降低底电流的几个小技巧
第一个技巧是调整 PCA9422 的降压转换器开关模式。刚拿到的第一个版本固件里,我只设置了输出电压,没有启用省电模式,结果实测底电流多了 3.5 微安。后来把降压转换器配置为轻负载下自动进入 PFM 模式,待机电流立刻降下来。注意要确认 MCU 正常工作时能接受 PFM 模式下稍大的纹波。
第二个技巧是关闭所有不用的外部上拉电阻。PCA9422 的 I2C 引脚本来就需要上拉,但板上的其他 GPIO 如果闲置且被默认配置成输入且上拉,每个上拉会在睡眠时产生约 4 微安的额外电流。我在睡眠前把所有未使用的 GPIO 全部重配置为模拟输入模式,一下省掉了不少底电流。
第三个技巧是利用 PCA9422 的“放电电阻”或输出放电功能。有些输出轨关断后残存在大电容上的电荷会让电压缓慢跌落,可能触发 MCU 的误复位或者 ADC 测量偏差。利用寄存器的放电功能让关断的轨快速泄放,既能避免这个问题,也能让下一次上电时时序更干净。
5. 调试记录与常见问题排查
5.1 I2C 通信不稳定的第一嫌疑犯
我第一版工程刚焊好的时候,发现 PCA9422 偶尔读回 0xFF,所有寄存器都像是失效状态。示波器抓 SDA 波形发现,在数据低电平时有尖刺,而且主控发送时钟与数据线之间存在明显相位差。
排查一圈后锁定在 I2C 总线上拉电阻的阻值。之前我用了一个 10kΩ 的上拉,配合 400kHz 通信时,因为线路电容大约 200pF,上升沿时间不够,导致一部分数据位被主控误读。换回 4.7kΩ 之后,通信彻底稳定。经验是:I2C 总线频率越高,上拉电阻就要越小,但也不能太小,否则功耗会上升,具体阻值建议用示波器实测为准。
5.2 电压跌落导致 MCU 复位的元凶
在一次射频模块发送阶段,MCU 的复位引脚被持续拉低,查程序完全找不到原因。后来确认是 PCA9422 降压输出在瞬态负载下出现了几十毫秒的电压跌落,低于 MCU 的复位阈值。
这个问题的根源是负载瞬变超过了 PMIC 的响应能力。解决办法有两个方向:一是把降压输出电容从 22uF 增加到 47uF,给瞬态吸收留足余量;二是在射频模块的电源走线上加一个 10uF 的本地储能电容,把大电流脉冲限制在靠近负载的小范围里。我两个都做了之后,用示波器测最差情况下电压跌落只有 120mV,勉强卡在 MCU 允许范围内,后续如果再出现掉压,就该考虑使用负载开关把射频模块与主电源轨彻底隔离。
5.3 电池电量读取不准的处理
电池电量的 ADC 采样值在不同温度、不同负载下偏差很大。最初我用简单的电压到百分比线性映射,结果在 3.6V 到 3.7V 之间跳变特别快,一点都不准。
后来我把采样条件统一固定:只在深度睡眠唤醒后立即采集电压,并在采集前关闭所有非必要外设,让系统处于一个相对稳定的负载状态。然后在代码里用查表法,根据该型号电池的实测放电曲线,把不同电压映射到剩余电量。这样一来,虽然做不到那种高端电量计芯片的精度,但在 550mAh 电池场景下,电量误差能控制在大约 5% 以内,已经够用。
5.4 一个容易忽略的电源时序坑
PCA9422 的复位输出和 MCU 的复位引脚之间也存在时序匹配问题。一次我在测试低功耗唤醒时发现,有时从 Stop 模式唤醒后,MCU 会先执行一小段代码,然后立刻复位,循环往复。
排查下来是 PCA9422 的复位输出引脚过长地维持低电平,导致 MCU 在已经复位释放后又等到了第二次复位边沿。解决办法是把 PCA9422 的复位延时寄存器适当调短,同时让 MCU 的复位源配置为仅接受外部复位引脚的下降沿,而不接受上升沿后的毛刺。这类时序问题在原理图阶段很难预料,只有实测时能暴露出来,我把它记在这里,提醒大家做完整电源管理项目时,不要把复位和电源放到同一个调试循环里。
6. 一点额外经验:从原理图到量产还要注意什么
这个小节不是总结,就是想把仿真、量测之外的一条完整链路再补齐说一下。我在几轮改板过程中,最深刻的体会是 PCA9422 和 STM32L031C6 的组合极度依赖“可观测性”。芯片功能再强,如果软件里没有留一个可以查询 PMIC 全部寄存器状态的诊断接口,遇到电源问题就基本只能靠猜。
我的建议是在固件里留一个串口调试命令,输入固定指令后能依次读出 PCA9422 的所有关键寄存器,并打印出当前输出电压配置、使能状态、充电状态和中断标志。这样无论再怎么乱,至少能先看到“芯片自己认为的状态”是什么。实际量产前的调测阶段,这个命令帮我省掉了一半以上的排错时间。
另一个建议是尽早把 PCA9422 的输入电源检测功能用起来。当产品用 USB 调试时,系统会被充电输入和电池供电的优先级折腾得很狼狈。PCA9422 自带检测输入电源状态的能力,我把它接到 MCU 事件里,让系统在插上 USB 的瞬间自动切换到“调试模式”,关闭深度睡眠的激进策略,这个体验带来的是调试效率的大幅提升。
最后说一句实在话,电源管理这个领域没有一劳永逸的配置文件。哪怕同样是 PCA9422 加 STM32L031C6,只要电池容量、峰值电流、工作温度范围不一样,寄存器配置和状态机参数都要重新调一遍。把这套思路作为底子,后面的项目会越走越快。