STM32F373VC与PCA9422 PMIC协同实现低功耗电池供电系统设计
做便携设备最难搞的往往不是功能逻辑本身而是那块电池怎么伺候。我最近带了一个低功耗数据采集项目主控用的是 STM32F373VC电源侧想找一颗能把电池充电、多路输出、系统监控全部打包的 PMIC最后定的是 PCA9422。这两个芯片配合起来把原先需要四五个分立器件才能搞定的电源方案压缩成了一套 I2C 就能配置的完整系统。这个组合做完以后我前后调了两周期间踩了不少坑也把充电、动态调压、低功耗切换、故障保护这些环节都走了一遍。这篇文章就把整个方案从硬件连线、驱动配置到实测数据完整记录下来给正在做电池供电、需要精细电流监控和动态电压调节的朋友做个参考可以直接抄作业。1. 为什么选了 PCA9422 配 STM32F373VC先把需求说清楚1.1 电池供电系统的电源管理难点先还原一下我当时的项目需求这样后面讲的每个设计点你都能对上号。设备用一节 3.7V 锂电池供电USB 5V 可以充电主控是 STM32F373VC需要 3.3V 给 IO 和 Flash但内核逻辑和模拟部分又想单独供 1.8V外设板上还有一个 5V 的传感器需要升压供电系统平时处于待机状态要求整机电流做到微安级别但被唤醒后要能立刻进入满速工作。更麻烦的是这个设备需要持续监测电池电压、负载电流和关键节点温度所以 MCU 必须有一路高精度 ADC 采集通道。这种需求如果用分立方案做至少得拆成这样一颗升压芯片、一颗 LDO、一颗锂电池充电管理芯片、一颗高精度 ADC再加上外围采样电阻和运放。电源轨多了上电时序、保护逻辑、低功耗切换全都要自己用离散电路搭调试量非常大。而且分立方案里每个芯片都有独立的使能引脚、电源状态输出和故障指示MCU 光去读这些状态就要占掉一堆 GPIO。所以我的思路是找一颗集成度足够高的 PMIC把充电、DC-DC、LDO 和监控功能一次性解决MCU 只负责通过 I2C 下发配置和读取状态。1.2 方案选型PMIC 与 MCU 的分工PCA9422 属于面向低功耗 MCU 应用的多功能 PMIC输入范围覆盖 2.5V 到 5.5V正好兼容单节锂电池和 USB 5V 两种输入形态。芯片内部包含一路高效 Buck-boost、两路 LDO、一个完整锂电池线性充电器、一个多通道 ADC以及若干可编程 GPIO 和中断信号。比较关键的是它的输出电压不是靠电阻分压固定的而是直接通过 I2C 寄存器配置支持软件动态调压这就给低功耗策略提供了很大的操作空间。STM32F373VC 这边我主要看中的是它的模拟外设。这颗 MCU 配备了三个 16 位 Σ-Δ ADC可以进行差分输入采样非常适合直接接采样电阻测电流它还有可编程运放和比较器可以做信号调理和硬件阈值触发。换句话说PMIC 负责“管电”MCU 负责“看电”一个做执行一个做感知和决策分工很明确。选型时我也对比过其他方案比如用独立的同步降压芯片加充电芯片组合成本上可能便宜一点但故障保护、动态调压、状态反馈这些都需要额外设计整体工作量反而更高。PCA9422 把这些集成之后硬件 BOM 少了软件驱动也只需要操作一套寄存器接口开发和维护成本都降下来了。2. 硬件设计从电源树到采样链路的细节2.1 整体电源架构与连接关系先画一遍我实际使用的电源树结构。USB 5V 经过接口保护后接到 PCA9422 的 VBUS 输入锂电池正极接到 BAT 引脚电池负极走系统地。Buck-boost 输出作为主电源轨 VDD_MAIN默认配置成 3.3V 给 STM32F373VC 的 VDD 以及板上数字逻辑供电LDO1 输出 1.8V给 MCU 的模拟部分和 SDADC 参考电压使用LDO2 接 5V 传感器需要时软件打开不需要时关闭。这里有两个接线细节容易被忽略。第一是 IRQ 中断引脚PCA9422 的故障和状态变化都是通过这个引脚上报的必须接到 MCU 的 EXTI 输入并且外部上拉到 VDD_MAIN。如果你只是轮询状态寄存器也可以不接但实际产品里用中断才能第一时间响应过压、过温这类紧急事件。第二是 I2C 地址PCA9422 的 7 位地址通常为 0x08总线上最好不要再挂别的设备避免地址冲突我项目里就单独给它占用了一条 I2C 总线反正 STM32F373VC 的 I2C 外设够用。涉及到电池和 USB 热插拔一定要在 VBUS 入口加 TVS 管。PMIC 本身有 OVP 保护但 TVS 能把浪涌钳位在更早的阶段保护充电通路上的电容和内部 MOSFET。我第一版设计省了这颗 TVS后来做 ESD 实验时发现系统在插拔瞬间偶发复位补上之后就再没出现过。2.2 电感、电容与 PCB 布局经验PCA9422 的 Buck-boost 需要一颗功率电感。电感感值直接影响纹波电流和带载能力太小则纹波电流偏大输出纹波电压超标太大则动态响应变慢。我当时实测了 1μH 和 2.2μH 两种感值最终选了 2.2μH 的满载 500mA 输出时纹波大约 12mV动态负载从 10mA 跳到 300mA 时电压跌落约 40mV响应时间可以接受。注意电感的饱和电流至少要留 1.5 倍余量因为 PMIC 在 PFM 和 PWM 切换瞬间电流峰值会冲得比较高。输出电容要特别注意容值和 ESR 的组合。我在 VDD_MAIN 上放了 22μF 陶瓷电容加 0.1μF 高频电容的组合并且尽量靠近负载引脚放置。陶瓷电容 DC 偏压特性会导致实际容值下降所以选型时不能只看标称值要看数据手册里对应电压下的容值曲线否则实际效果会打折扣。这算是我反复强调的一个点很多工程师在这里吃过亏。PCB 布局上功率回路要走粗短走线输入电容和输出电容的地要直接回到 PMIC 的功率地引脚避免和模拟地混在一起。采样电路的地单独走通过一个 0Ω 电阻或者磁珠在单点汇合。第一版 PCB 我没注意采样地和功率地的划分结果 ADC 读到的电流数据在负载切换时会有明显的毛刺后来把地分割重新走了一版才解决。2.3 电压/电流采样链路设计既然 STM32F373VC 有 16 位 SDADC不好好利用就太浪费了。电池电压采样我直接用两个电阻分压把 BAT 电压分到 SDADC 输入范围内同时并联一个小电容滤除高频噪声。分压电阻的阻值要选大一点比如 100kΩ 以上把自身漏电流压到微安级别不然在低功耗模式下分压电阻本身就会吃掉不少电流。电流采样设计要细致一些。我在主电源轨上串入了一颗 10mΩ 的采样电阻SDADC 支持差分输入理论上可以直接接电阻两端但 10mΩ 在 100mA 电流下只有 1mV 压差直接用 SDADC 采样也能测不过分辨率不够理想。所以我在中间加了一级由 STM32F373VC 内部运放构成的差分放大电路增益设置成 20 倍。这样 500mA 负载电流对应采样电阻压降 5mV放大后是 100mVSDADC 量化出来就比较舒服了。增益和采样电阻的乘积决定了电流测量的量程和分辨率过大的增益会导致大电流时 ADC 输入饱和过小则小电流测不准需要根据项目实际电流范围折中。采样走线一定要用开尔文接法也就是从采样电阻两端独立引出两根细线到运放输入端不能直接从功率走线上面拉焊盘。功率走线的铜阻虽然很小在毫欧级别也会引入误差导致大电流时 ADC 读数和实际电流偏得离谱。我实测过直接单点引出和开尔文接法相比在 1A 负载下误差差出了 3% 左右。3. I2C 配置与动态调压让 PMIC 听你指挥3.1 PCA9422 初始化流程与寄存器映射PCA9422 上电后会有默认输出电压一般是 3.3V 或者按照配置引脚设定。MCU 启动后会经历一个阶段PMIC 先用自己的默认配置供电MCU 起来之后再去初始化 PMIC把各路输出调整成目标电压然后开启中断。这个两步走的流程很重要千万别在 MCU 还没跑起来的时候就去依赖 PMIC 的某个 LDO 输出那样初始化时序会很被动。我整理了一份自己项目里用的寄存器初始化清单如下表。具体地址请以 PCA9422 数据手册为准但整体思路是通用的寄存器名称实际写入值说明0x10BUCK 输出电压0x1A配置为 3.3V 输出0x12LDO1 输出电压0x03配置为 1.8V 输出0x14LDO2 使能与电压0x3C默认关闭唤醒后由软件打开0x20充电控制0x2C配置恒流 500mA开启充电0x2A中断使能0x7F使能过压、欠压、过温等中断0x2B中断状态清除0x7F先清一次状态避免残留误报初始化的顺序也有讲究。先把中断屏蔽屏蔽掉清一次状态再配置各路输出最后打开中断。如果反过来可能在配置过程中由于输出电压跳变触发了欠压中断MCU 一开机就收到一堆假故障排查起来非常浪费时间。我第一版代码就是先开中断再写配置结果中断引脚一直在拉低我一度以为芯片坏了。3.2 I2C 驱动要点与读写时序STM32F373VC 的 I2C1 我配成了 400kbps 快速模式PCA9422 完全支持。需要注意的是I2C 上拉电阻不能用芯片内部的上拉必须外部接。上拉取值要根据总线电容和通信速率来选择一般 400kbps 下用 2.2kΩ 到 4.7kΩ。如果板子上 I2C 线走线比较长建议用 2.2kΩ确保上升沿足够陡。读写时序上PCA9422 支持连续地址读写每次读操作可以先写寄存器地址再重新发起读也可以直接一次写地址后连读多个字节。我在读取充放电状态时用了一次写 0x2C 地址、再连读 8 个字节的方式这样可以一次性把健康状态、故障状态、电流数据都读出来。实际测试下来用逻辑分析仪抓波形非常稳没有出现总线冲突或漏数据的情况。一个值得注意的坑是MCU 在 Stop 模式之前I2C 外设会被关掉这时 PMIC 的 IRQ 仍然可以唤醒 MCU——只要中断引脚配置成 EXTI。但 PMIC 内部有些状态位是电平触发而非边沿触发所以唤醒之后第一件事一定要读中断状态寄存器确认具体原因然后再清标志。否则下次相同事件不会再触发中断设备就“静默”了。3.3 动态电压调节与负载切换动态电压调节这个功能在低功耗系统里很好用。我的做法是系统进入待机前先把 Buck-boost 输出从 3.3V 降到 2.0V同时关闭 LDO2并把 LDO1 降到 1.2V 给 MCU 的备份域供电。这样整个电源树的静态功耗会明显下降待机电流能从毫安级别降到微安级别。唤醒的时候再把电压逐级调回去先恢复 LDO1再恢复 BUCK最后打开 LDO2。实现方式很简单就是写几个寄存器值但要注意切换节奏。如果 MCU 还在运行时就突然把电压调低核心频率高的情况下可能直接触发欠压复位。所以调整电压前必须先降低系统时钟或者先把 MCU 切到 Sleep 模式由 PMIC 输出稳定后再继续执行。这个顺序错了调试的时候会看到诡异的现象程序跑着跑着就复位查代码查半天都查不出来是电压问题了。还有一个更细的点PCA9422 支持 PFM/PWM 工作模式切换。PWM 模式纹波小、控制精准但轻载效率低PFM 模式相反轻载效率高但纹波会大一些。我让 PMIC 工作在自动模式由芯片根据负载自行切换MCU 不需要干预。实测下来负载 1mA 以下时系统噪声会略微增大但对我的数字电路影响不大如果你的系统有高精度模拟前端可以考虑强制 PMIC 锁定 PWM 模式来保证采样质量。4. 充电管理、低功耗切换与故障保护4.1 锂电池充电流程与温度保护PCA9422 内置的线性充电器支持标准的 CC/CV 充电流程预充、恒流充电、恒压充电、截止。我需要做的就是把参数写进寄存器然后读取充电状态上报给上层。我项目里电池容量 1500mAh充电电流设成 500mA截止电流设成 50mA恒压 4.2V。涓流预充阶段如果电池电压低于 2.9V芯片会自动用很小电流先充到安全电压再切恒流这个不需要软件处理。温度保护一定要重视。PCA9422 支持通过 NTC 热敏电阻检测电池温度配置了温度窗口之后超出范围充电会暂停或降流。我的做法是在电池包内部贴一个 10kΩ NTC分压后接到 PMIC 的 TS 引脚。寄存器里把窗口设置成 0℃ 到 45℃。这里要特别计算分压电阻和 NTC 的配合不同 NTC 的 B 值不一样不能照着别人的设计直接抄否则温度检测偏差可能非常大。软件端需要做的是周期读取充电状态寄存器把“正在充电”、“充电完成”、“充电异常”上报到主逻辑。充电完成之后芯片会自动停止当电压跌落到重新充电阈值时再次启动充电这个自动重充功能非常实用。我在调试时踩过另一个坑因为我用的电池连接线比较长、内阻大充电截止后电压回落速度快导致 PMIC 频繁进入重新充电状态一晚上反复充放电。最后通过软件在充电完成后关闭重充使能或者延长上报周期才把问题解决。4.2 系统低功耗模式的联动设计STM32F373VC 支持 Sleep、Stop、Standby 三种低功耗模式实际功耗差别很大。我的设计思路是分两级短暂空闲时进 Sleep关闭外设时钟MCU 电流降到毫安以下长时间待机时进 Stop配合 PMIC 降电压整机待机电流才能做到理想值。联动流程是这样的应用层判断一段时间没有事件后先把 ADC、DAC、运放全部关闭把 I2C 外设也停掉然后写 PMIC 寄存器关闭 LDO2、降低 BUCK 输出最后执行 WFI 进入 Stop。唤醒源可以是 RTC 闹钟也可以是 PMIC 的 IRQ 引脚——比如充电状态变化或者外部按键通过 PMIC GPIO 触发。唤醒后的恢复过程要设计成状态机不能直接在中断服务函数里做。原因是 PMIC 电压恢复、ADC 重新校准都需要时间如果在中断里同步执行会导致中断处理时间过长低优先级的中断事件会被饿死。我实际的顺序是中断里只置一个标志位主循环检测到标志后按“恢复 PMIC 配置 → 等待电压稳定 → 重新初始化 ADC → 继续执行任务”这个顺序恢复。电压稳定的等待不能省否则 ADC 启动后采到的是过渡期的电压数据会明显偏差。4.3 故障中断、日志与系统恢复PCA9422 的故障保护覆盖了过压、欠压、过温、过流和充电异常。这些事件发生时芯片会拉低 IRQ 引脚MCU 通过 EXTI 唤醒并读取中断状态寄存器。我的软件里专门做了一个故障处理模块接收到中断后把当前时间、故障类型、电源状态全部记录到 Flash 的日志区然后根据故障严重程度决定是软复位还是进入安全模式。比较棘手的是故障恢复策略。如果只是瞬时过流比如电机启动瞬间电流冲高PMIC 自动恢复后系统可以继续运行但如果是持续过温立刻复位系统只会重复故障循环电池还可能过热损坏。我的做法是记录故障发生时刻的时间戳如果在 30 秒内同类故障连续出现 3 次以上就把系统锁定在低功耗安全模式只保留充电管理和 RTC等待用户手动干预。这个逻辑用状态机写很容易但设计时很容易忽略“同类故障连续计数”这个维度导致系统被单个偶发故障锁死。PCA9422 还有 I2C 看门狗功能就是 MCU 需要定期给 PMIC 喂狗超时后 PMIC 会复位自身配置或者触发系统复位。这个功能我在量产固件里是打开的防止 MCU 死机后系统一直停留在异常状态。要特别注意的是看门狗超时时间要结合实际任务周期配置得宽松一些如果系统正常的任务调度偶尔超过看门狗周期PMIC 会“误杀”正在运行的 MCU这个问题的现场表现和使用硬件看门狗超时一模一样排起来很费劲。5. 实测数据与排坑实录5.1 关键波形与数据整理调试完成后我记录了几组关键数据给大家做个参考锚点。主板在常温下系统运行在 72MHzPMIC 输出 3.3V整机工作电流大概 38mA进入 Sleep 模式并关闭 LDO2 后整机电流 1.2mA进入 Stop 模式且 BUCK 降到 2.0V 后整机待机电流约 38μA其中 PMIC 自身消耗占了大约一半剩下的主要是 MCU 的漏电和采样电阻分压损耗。输出纹波方面PWM 模式下 3.3V 轨纹波实测约 15mVPFM 模式轻载时纹波约 50mV。SDADC 电流采样在全量程范围内测得误差小于 1%在小电流段由于运放偏置电压的影响误差稍大但整体精度在我的项目中完全够用。充电效率方面线性充电到恒压阶段芯片发热比较明显500mA 充电电流下 PMIC 表面温度比环境高了大约 20℃所以大充电电流档位一定要预留散热设计。我也整理了动态响应数据BUCK 电压从 3.3V 降到 2.0V 的软件调整时间约 1ms输出没有出现明显过冲从 2.0V 恢复到 3.3V 约 1.2ms这个速度满足我系统唤醒的要求。如果项目对电压切换速度更敏感建议把输出电容稍微减小但纹波会相应增大需要权衡。5.2 常见问题速查表把我在调试过程中遇到的高频问题整理成了一张速查表每条都是实际踩过的现象直接原因解决方案I2C 总线无 ACK上拉电阻缺失或阻值过大检查外部上拉400kbps 用 2.2kΩ 到 4.7kΩMCU 启动后死机PMIC 输出电压切换过快调整电压前先降低系统时钟或进入 Sleep待机电流偏大分压电阻和 LDO 未关闭选高阻值分压待机时写寄存器关闭 LDO2ADC 电流数据有毛刺采样地混入功率地重新分割地平面单点汇合IRQ 一直为低中断状态未清除初始化时先读状态寄存器并清零充电频繁启停电池线路内阻大触发重充软件禁用自动重充或增大重充滞回休眠后无法唤醒唤醒源配置成电平触发改为边沿触发或唤醒后立即清中断标志负载切换电压跌落过大输出电容偏小或动态响应参数不合适增大输出电容检查 PFM/PWM 模式5.3 几个容易忽略的细节有几个细节我觉得值得单独拎出来说。PCA9422 和 STM32F373VC 的 I2C 电平要检查一下。STM32F373VC 的 IO 供电如果是 3.3V那和 PMIC 的 I2C 通信没问题但如果 MCU 的某些 IO 工作在 1.8V 逻辑就需要确认 PMIC 的 I2C 引脚是否兼容该电压必要时加电平转换。这个在原理图阶段就要确认不然后期飞线很痛苦。采样电阻的精度和温漂也要注意采样电阻直接影响电流数据的准确性。我一开始用的普通厚膜电阻温度升高后阻值漂移明显后来换成了合金电阻温漂系数做到 50ppm 以下。如果项目需要长期监测这个细节关系到数据的可信度。另外一点是 PMIC 默认配置的问题。PCA9422 的默认输出电压可能和你的目标系统不匹配所以硬件上电时如果 MCU 还没初始化 PMIC某些外设可能已经工作在错误的电压下。我的处理方案是在 PMIC 的 EN 引脚或某个 GPIO 上加一个延时控制让 MCU 先跑起来完成 PMIC 配置再打开后级电源。这样虽然增加了少量硬件逻辑但解决了上电瞬间的供电时序问题系统启动稳定多了。最后我再说一个调试习惯每次修改 PMIC 寄存器配置之前先把当前全部寄存器值读出来存一份快照命名为“配置_日期_版本号”方便回退对比。我在调电压切换流程时就是靠快照对比发现某次配置漏写了 LDO1 电压寄存器导致唤醒后 SDADC 参考电压偏低。这个习惯看着麻烦但在复杂配置场景下能省大量排查时间。PCA9422 加 STM32F373VC 这套组合整体跑下来给我的感受是PMIC 把脏活累活都包了MCU 只需要专注策略和数据处理两者配合得当电源管理这块就能做得既省心又可靠。