前阵子做了一块带屏幕显示、数据采集和无线通信的便携设备,主控选了 STM32F423RH,电源部分没有沿用常见的“LDO + DC-DC 拼板方案”,而是改用 PCA9422 这颗 PMIC 做全套电源管理。整套系统调通以后,从待机到满负载的电压切换、多路供电的上下电时序、输入源切换和低压保护,基本都能靠 I2C 在软件层统一控制,板级器件少了三分之一,调试时间也省了一大截。这篇文章就把我的方案、选型思路、软件框架和踩过的坑完整记录下来,给正在拿这两颗芯片做低功耗或便携设备的同学做个参考。
1. 项目概述:为什么要把电源管理交给独立 PMIC
1.1 这套方案解决的三个实际问题
很多人做嵌入式设备,电源部分习惯直接拼分离器件:一路 LDO 给 MCU,一路 DC-DC 给无线模块,再用一个三极管或者负载开关控制断电。小负载、单电源的设备这么干完全没问题,但设备一旦超过两路供电,麻烦就来了。
我这次的需求是三路主电压加一路备用电压:3.3V 给传感器和电平转换、2.8V 给显示屏背光和接口、1.8V 给外部存储和逻辑电路,另外还需要一路微安级的待机电源维持 RTC。用分离器件做的话,每一路都要独立计算功耗、压差、散热,还要做上电时序。时序最简单粗暴的做法是用 RC 延时,但 RC 延时的精度很差,温度一变就可能出现某一路先掉电、某一路后上电的竞争问题,而且一旦要改时序只能改硬件。
换成 PCA9422 之后,三路 DC-DC 和一路 LDO 集成在一颗芯片里,上下电顺序、电压目标值、软启动时间都通过寄存器配置,软件随时能改。这才是真正“完整电源管理”的起点:硬件负责执行,软件负责决策。
1.2 PCA9422 与 STM32F423RH 的分工设计
选这两颗芯片搭配,核心逻辑是“模拟域和数字域分离”。
PCA9422 属于模拟域,负责所有电压变换、上电时序、过流保护、热关断,还有低功耗模式下的供电切换。它内部集成了多个开关电源通道,本身具备默认的上电配置,也就是说哪怕 MCU 还没初始化、I2C 还没跑起来,系统也能按出厂默认值正常启动。这一点对带显示屏的设备特别重要,因为上电就要能看到开机画面,不允许“等 MCU 配置好了电源才起来”。
STM32F423RH 属于数字域,负责电源管理策略的决策。它通过 I2C 读写 PCA9422 的寄存器,按照系统当前的负载状态动态调整各路电压;检测到电池电压偏低时就通知 PMIC 降低某些通道的输出;进入低功耗之前,先把不用的通道关掉,再把 I2C 总线挂起。两者之间除了 I2C,还接了一条 PMIC 的中断线 IRQ,PMIC 检测到输入欠压、过温、电源跌落这些异常时,会主动拉低 IRQ 通知 MCU。MCU 可以在 Sleep 模式下用 EXTI 唤醒,不用一直轮询。
1.3 整体供电架构
系统的电源树大致是这样的:
| 电源通道 | 默认电压 | 主要负载 | 软件可控能力 |
|---|---|---|---|
| 通道 0(DC-DC) | 3.3V | 传感器、接口电平转换 | 动态调压、开关 |
| 通道 1(DC-DC) | 2.8V | 显示屏、背光 | 动态调压、开关 |
| 通道 2(DC-DC) | 1.8V | 存储器、逻辑电路 | 动态调压、开关 |
| LDO | 3.0V | RTC、待机唤醒电路 | 始终供电,开关受控 |
| VIN | 5V/锂电 | 系统总输入 | 由 PMIC 管输入切换 |
输入源是 USB 5V 和单节锂电池,正常情况下由 USB 供电并同时为电池充电,拔掉 USB 后无缝切换到电池。PCA9422 内部有输入源管理与理想二极管逻辑,外部只需要加少量保护元件,不需要再单独设计一个电源切换电路。
这套架构里,STM32 的核心电压不需要额外处理,因为 F423 的 VDD 工作范围宽,直接挂在 3.3V 通道上即可。真正需要精细调压的是无线模块和显示屏这类外设,它们在不同发射功率和亮度下对电压余量的要求差异很大,这也是后面做动态调压的主要对象。
2. 硬件设计要点与供电链路细节
2.1 输入源设计:USB 与锂电池的无缝切换
输入源切换是整块板上最容易出问题的部分。一开始我试过用“二极管 OR 加 PMOS 负载开关”的方式做切换,原理上没问题,但电池电压接近 4.2V、USB 是 5V 时,PMOS 的体二极管会引入漏电流,待机电流很难压下去。换成 PCA9422 之后,输入切换由芯片内部的电源路径管理完成,VIN 端只要满足芯片要求的输入电压范围,芯片会自动选择优先级更高的输入源。
实际设计时需要注意输入端的浪涌电容。USB 热插拔瞬间会产生很大的浪涌电流,如果 VIN 端只放一个 10µF 陶瓷电容,插拔瞬间就可能触发 PMIC 的过流保护。我最后加了 1Ω 电阻 + 47µF 电解电容做缓启动,同时在电阻前留了 TVS 的位置,实测热插拔不会再复位。
锂电池供电路径上要特别注意保护电阻的取值。某国产锂电池内阻偏低,在满电 4.2V 时直接接 PMIC 的 VIN,瞬时电流能达到 2A 以上,对细走线是很大的压力。我在电池接入端串了 0.1Ω 采样电阻,顺便用来做电池电流监测,一举两得。
2.2 I2C 与控制引脚的连接方案
PCA9422 的 I2C 接口是标准的 SCL/SDA 两根线,我把它挂在 STM32F423RH 的同一个 I2C 外设上,总线速率跑 400kHz。板子上面的上拉电阻我一开始选了 4.7kΩ,结果在 400kHz 时波形上升沿偏慢,SDA 在某些温度条件下偶发超时。后来换成 2.2kΩ,问题消失。
这里有个经验:PMIC 的 I2C 从机地址通常由外部引脚电平或内部选项决定,画板子之前一定要确认地址,避免和板上其他 I2C 设备冲突。我这块板上还有一个传感器和一个屏幕控制芯片,三个设备的地址在 7 位地址范围内没有重叠,但排查 I2C 通信时依然要先确认地址匹配,否则示波器上看到的波形再漂亮也白搭。
IRQ 引脚一定要接成推挽或开漏并能被 MCU 外部中断唤醒的形式。我实际用的是开漏输出,MCU 端开启内部上拉,接入一个未占用的 EXTI 引脚。需要注意,PMIC 的 IRQ 是事件触发后需要软件清除中断标志才恢复高电平的,不是简单的边沿触发就完事。如果只配置了上升沿唤醒,IRQ 没有被及时清掉,下次事件永远不会再唤醒 MCU,这是低功耗设备很典型的隐性 bug。
2.3 器件周边电路与 PCB 布局
PCA9422 这类集成 PMIC 对 PCB 布局的要求比分离电源严格不少。三路 DC-DC 的开关节点都是高 di/dt 区域,电感必须靠近芯片的 SW 引脚,反馈走线要远离电感下方,输出电容要尽量贴着芯片的输出引脚,回流地线走单独过孔下到完整地平面。
第一版板子我把储能电感放在第二层,反馈网络走线从电感正下方穿过,结果通道 0 的输出纹波超过 60mV,还干扰了 ADC 采样。重新布局以后,所有反馈走线绕到电感侧面,输出纹波压到 15mV 以内,ADC 采样值立刻稳定。
去耦电容的搭配也要讲究。每个通道的输出端我放了 22µF 主电容和 0.1µF 高频去耦电容,输入总线上放了 4.7µF 和 0.1µF。注意陶瓷电容的直流偏置特性,22µF 的 0805 电容在 3.3V 偏压下实际容量可能只剩 50%,所以选型时最好选 25V 耐压规格,容量衰减更小。输入总线上的电容容值不能过大,否则上电瞬间充电电流太大会让 PMIC 认为输入源异常。
另外,PCA9422 的工作状态引脚,例如模式选择脚和使能脚,不要悬空,全部通过 10kΩ 电阻上拉或下拉,确保在 MCU 还没有配置任何寄存器之前,芯片进入的是你预期的默认模式,而不是留在不确定状态。
3. 驱动实现:让 MCU 完全掌控电源
3.1 I2C 物理层与寄存器读写封装
我基于 STM32 HAL 库封装了 PMIC 的读写接口。核心是复用同一个 I2C 句柄,所有 PMIC 操作都走注册的全局设备地址,并且统一使用寄存器地址自动递增的模式。基础读写函数长这样:
#define PMIC_DEV_ADDR (0x08 << 1) // 根据数据手册确认 #define PMIC_REG_VERSION 0x00 // 版本寄存器,示例 static I2C_HandleTypeDef *pmic_i2c; void PMIC_Init(I2C_HandleTypeDef *hi2c) { pmic_i2c = hi2c; } uint8_t PMIC_ReadReg(uint8_t reg) { uint8_t val = 0; HAL_I2C_Mem_Read(pmic_i2c, PMIC_DEV_ADDR, reg, I2C_MEMADD_SIZE_8BIT, &val, 1, 10); return val; } void PMIC_WriteReg(uint8_t reg, uint8_t val) { HAL_I2C_Mem_Write(pmic_i2c, PMIC_DEV_ADDR, reg, I2C_MEMADD_SIZE_8BIT, &val, 1, 10); }读多字节时用 HAL_I2C_Mem_Read 指定长度即可。这里有个小细节:I2C 超时时间不要给太短。PMIC 在低功耗模式下 I2C 可能会因为内部时钟变慢而出现时钟拉伸,给 1ms 超时会导致偶发失败,我最终统一给了 10ms,牺牲一点点异常检测时间换稳定性。
如果需要频繁切换寄存器组,可以用 DMA 模式做批量读写,避免 CPU 在高速 I2C 传输时被占用。但要注意 DMA 回调里不能直接做电源状态切换操作,最好只是更新标志,主循环再处理。
3.2 上电默认配置的读取与校验
上电以后第一件事不是急着改配置,而是先读取版本寄存器和默认配置,确认通信链路是通的,并把芯片出厂默认值打印到调试串口。这样后续就算改错了寄存器,也能知道是从什么基础上改的。
void PMIC_PrintDefaultConfig(void) { uint8_t version = PMIC_ReadReg(PMIC_REG_VERSION); printf("PMIC version: 0x%02X\r\n", version); for (uint8_t reg = 0; reg < PMIC_REG_CONFIG_END; reg++) { printf("REG[0x%02X] = 0x%02X\r\n", reg, PMIC_ReadReg(reg)); } }这一步非常值得做。我第一次上电时就发现通道 1 的默认电压和预想的不一样,原来这颗芯片的出厂默认配置面向的典型应用是 1.0V 核心供电场景,而我要的 2.8V 需要显式配置。如果不先 dump 一遍,直接按自己的预期去写寄存器,可能会漏掉某个模式位,导致电压怎么都调不上去。
校验环节还要注意:有些寄存器是“写入后立即生效”,有些是需要额外置位提交(commit)才会更新。读写之前先确认当前寄存器属于哪类,再决定要不要补一次提交操作。这块建议严格照数据手册的描述处理。
3.3 动态调压与模式切换实现
动态调压是这套方案的核心卖点。显示屏在低亮度时 2.8V 余量过大,纯浪费功耗;无线模块在发射功率从 0dBm 升到 20dBm 时,需要瞬间把电压拉高保证射频指标。用分离电源基本没法实现这种动态调整,而 PCA9422 支持通过 I2C 修改目标电压,配合内部软启动斜坡,可以做到输出电压变化平滑无毛刺。
切换电压的核心代码逻辑是这样的:
void PMIC_SetChannelVoltage(uint8_t channel, uint16_t mv) { uint8_t vsel = PMIC_VoltageToCode(mv); uint8_t reg = PMIC_REG_VOUT_BASE + channel * PMIC_REG_VOUT_STEP; PMIC_WriteReg(reg, vsel); // 需要提交的寄存器补一次 commit 触发 if (PMIC_NeedCommit(reg)) { PMIC_WriteReg(PMIC_REG_COMMIT, 0x01); } // 等待输入源与负载调整完成 HAL_Delay(PMIC_SETTLE_TIME_MS); }在修改电压之前要特别注意当前负载情况。如果系统正处在高电流状态,贸然把电压往下调,PMIC 会进入欠压保护,直接把整个通道关掉。我的处理方式是:先降低负载(比如熄灭背光、降低无线发射占空比),再调电压,等电压稳定后再恢复负载。
动态调压的频率也要控制。频繁调压会让 PMIC 内部的状态机反复切换,开关电源的响应会有延迟,如果业务代码里每个循环都调一次电压,不仅白耗电,还可能让输出电压来回震荡。我最后把电压调整做成“请求-确认”机制:业务模块提交目标电压请求,电源管理进程做最小变更判断,只有变化量超过 100mV 才真正写寄存器。
4. 电源管理策略:状态机与能效优化
4.1 系统运行状态定义与切换
完整的电源管理不能只是“能调压”,必须站在系统层面定义运行状态。我把系统分成五个状态:开机、满载运行、轻载运行、待机、深度睡眠。每个状态对应一组 PMIC 通道配置和 MCU 自身的功耗模式。
| 状态 | 通道 0 | 通道 1 | 通道 2 | LDO | MCU 状态 |
|---|---|---|---|---|---|
| RUN_FULL | 3.3V | 2.8V | 1.8V | 3.0V | 运行,最高频率 |
| RUN_LIGHT | 3.3V | 2.5V | 1.8V | 3.0V | 运行,降频 |
| SLEEP | 3.3V | 关闭 | 1.8V | 3.0V | Stop 模式 |
| DEEP_SLEEP | 关闭 | 关闭 | 1.8V | 3.0V | Standby,RTC 唤醒 |
| SHUTDOWN | 关闭 | 关闭 | 关闭 | 3.0V | 关机,按键唤醒 |
状态切换由事件驱动:用户按键、传感器中断、无线模块收发完成、外部数据到达。每次切换前,MCU 先把要关闭的外设断电,再通知 PMIC 关通道,最后自己进入对应的低功耗模式,这样能避免在高速负载下直接断电压造成数据丢失。
这里比较关键的是 SLEEP 状态下的通道策略。一开始我把通道 1 在待机时保留 2.8V,后来用功耗仪一测,待机电流多了 3mA,原来是显示屏接口电路在 2.8V 下有静态电流。改成待机时关掉通道 1,把保留电压的任务交给 LDO 通道的 3.0V 待机供电,待机电流直接降到 180µA 以下。
4.2 电池监测与低压保护
电池电压监测我用了 STM32F423RH 的 ADC,通过分压电阻采样 VIN 的电池电压。分压电阻选的 100kΩ/47kΩ,把 4.2V 拉到 1.3V 以内,既满足 ADC 参考电压范围,又不会引入过大的漏电流。
低压保护逻辑要考虑迟滞,不然电池电压在阈值附近波动时,系统会在运行和关机之间反复横跳,这是用电池设备最常见的故障之一。实际代码里做了两级阈值:3.5V 进入低电量模式,禁止无线发射;3.3V 执行关机保存,关机后只有按键和充电插入可以唤醒。这个迟滞范围足够大,实测两天都没有出现抖动。
PMIC 自身的电源跌落中断也要接好。当输入源瞬间跌落时,PCA9422 会进入欠压保护并拉低 IRQ,MCU 收到中断后立刻保存关键数据、关闭高耗电外设。这一步的时序很重要,我实测从 IRQ 拉低到系统掉电大概只有几十毫秒,所以数据保存代码必须放在中断里最前面的位置,不能做阻塞操作。
4.3 功耗实测与调优
整机功耗我做了一轮完整的测量,数据可以作为类似项目的参考:
| 状态 | 系统电流 | 说明 |
|---|---|---|
| RUN_FULL + 无线发射 | 180mA | 发射 20dBm,屏幕全亮 |
| RUN_LIGHT + 采集 | 45mA | 关闭屏幕,降频到 32MHz |
| SLEEP | 180µA | PMIC 保持两路输出,MCU Stop |
| DEEP_SLEEP | 12µA | 仅 LDO 待机,RTC 运行 |
把功耗从 3mA 压到 180µA 的过程中,我发现最大的收益来自“减少不必要的外设供电”,而不是单纯调低 MCU 频率。传感器在待机时如果不关供电,就算 MCU 进了 Sleep,传感器芯片自己也要吃十几毫安。
另外要提一个容易被忽视的点:I2C 上拉电阻在掉电时也会漏电。DEEP_SLEEP 模式下如果 I2C 总线仍然由 MCU 内部上拉接到电源,就会一直消耗电流。正确做法是待机时把 I2C 引脚配置为模拟输入,释放上拉,需要唤醒时再重新初始化外设。
5. 调试实录与常见问题速查
5.1 I2C 通信不稳定、读不到数据
这个坑我在调第二版板子时遇到了。现象是 PMIC 的版本寄存器偶尔能读到,偶尔超时,示波器看总线波形正常,但就是不稳定。排查下来原因是 I2C 上拉电阻 4.7kΩ 在 400kHz 下偏大,再加上总线上挂了三个设备,容性负载累计超过 200pF,上升沿变缓导致从机无法可靠采样。
解决方式是把上拉电阻换到 2.2kΩ,并把 I2C 速率先降到 100kHz 做功能验证,确认逻辑无误后再提升到 400kHz。另外,I2C 设备地址和寄存器地址可以用逻辑分析仪抓包确认,比我盯着示波器猜速度得多。
5.2 输出电压与寄存器配置不一致
如果你写了目标电压,但实测输出电压不对,先检查是不是漏了提交操作。有些 PMIC 寄存器写了之后不会立刻生效,必须等主控写一个 store/commit 命令才会更新输出。其次是检查写入的编码值是否在有效范围内,超出范围的值会被芯片忽略或者钳位到安全值。
还有一个容易忽略的点是负载效应。通道输出端接了大电容时,调低电压的瞬间会有一个较长放电过程,电压不是立刻跌到目标值的。如果此时马上测量,看到的数值会滞后于配置。这时候要看数据手册里的斜坡时间参数,耐心等待几个毫秒再判断。
5.3 低功耗唤醒后系统异常
低功耗唤醒后系统异常,这个问题我排查了很久。现象是从 SLEEP 模式唤醒后,屏幕显示花屏,无线模块连接断开。后来发现是唤醒流程里少了一步:唤醒后 MCU 必须先重新初始化 I2C 外设和 GPIO 状态,再去读写 PMIC 寄存器,因为 Sleep 模式已经把 I2C 外设时钟关了,直接读寄存器自然失败,导致 PMIC 配置回退到了默认值。
正确流程是:唤醒 → 恢复系统时钟 → 重新初始化 I2C → 读取 PMIC 当前配置 → 如果需要,重新加载运行态配置 → 恢复外设驱动。顺序不能乱,先保证电源配置正确,再恢复外设,否则外设初始化过程中可能会拉着不正确的电压工作。
5.4 常见问题速查表
| 现象 | 可能原因 | 解决方法 |
|---|---|---|
| I2C 超时 | 上拉电阻偏大、总线电容过大 | 换 2.2kΩ 上拉,降低速率 |
| 输出电压不对 | 未提交、编码超范围、负载效应 | 执行 commit,检查编码,等待斜坡 |
| 待机电流偏大 | I2C 上拉漏电、外设掉电不彻底 | 释放上拉,彻底关闭外设供电 |
| 唤醒后花屏 | 唤醒后未重新初始化 I2C | 按“时钟→I2C→PMIC→外设”顺序恢复 |
| 热插拔复位 | VIN 浪涌过大 | 加缓启动电阻和输入电容 |
| 频繁关机重启 | 低压阈值无迟滞 | 设置高低两个阈值,加迟滞判断 |
写在最后的一点经验
这套 PCA9422 + STM32F423RH 的电源管理方案,我前后迭代了三版硬件,软件上最花时间的反而不是调压算法,而是状态切换时各种边界条件的处理。经验就一条:每次状态切换都先列一张“哪些外设要断电、哪些寄存器要恢复、IRQ 是否已清除”的清单,按顺序执行,不要凭感觉跳步骤。只要你把电源树理清楚,把状态机定义清楚,这种集成 PMIC 方案带来的收益会超过你多花的那点学习成本。以后如果再升级,我大概率会继续沿用这套框架,只是把决策逻辑从 MCU 挪到更上层的应用处理器模型里,底层这套 I2C 调电、模式切换的套路依然通用。