1. 项目缘起与整体设计思路1.1 为什么偏偏选中 PCA9422 和 STM32F746VG 这对组合STM32F746VG 这颗芯片在嵌入式圈子里算是老熟人了Cortex-M7 内核、216MHz 主频、1MB Flash、320KB RAM还带 LCD-TFT 控制器和 Chrom-ART 图形加速器拿来做带屏的工业 HMI 或者便携式仪器非常合适。但问题也随之而来主频拉满的时候核心电流能到 200mA 以上加上外设和屏幕背光整机峰值功耗轻松突破 500mA。如果用传统的 LDO 供电光是压差损耗就能把一颗 2000mAh 的锂电池在几个小时内榨干。PCA9422 是 NXP 推出的一颗高度集成的电源管理芯片专门针对低功耗微控制器应用设计。它内部集成了线性充电器、多个可编程降压和升压转换器、LDO 以及一个 I2C 可编程的寄存器组。最关键的是它支持锂电池充放电管理并且可以通过 I2C 动态调节各路输出电压和上电时序这对于 STM32F746VG 这种需要多电压域1.2V 内核、3.3V IO、可能的 2.8V 模拟域的 MCU 来说简直是量身定做。我选这对组合的核心逻辑是一颗 PMIC 搞定充电、升压、多路降压和时序控制MCU 通过 I2C 做动态功耗管理整机 BOM 精简、PCB 面积小、软件可控性强。相比用分立 DC-DC 加分立充电 IC 的方案PCA9422 把外围元件数量压到了最低对于空间受限的便携设备来说这个优势是决定性的。1.2 系统电源架构的顶层规划在动手画原理图之前我先在纸上把整个电源树理了一遍。STM32F746VG 的供电需求大致分三块VDD 主供电 1.7V~3.6V典型 3.3V、VDDA 模拟供电通常跟 VDD 同压但需要更干净的电源、VBAT 备份域供电可接纽扣电池或超级电容。另外屏幕背光需要一路恒流升压传感器和外设可能需要独立的 1.8V 或 2.8V。PCA9422 的输出通道分配我最终定成这样BUCK1 输出 3.3V 给 MCU 的 VDD 和大部分数字外设BUCK2 输出 1.8V 给部分低功耗传感器和 IO 电平转换LDO1 输出 3.3V 但专门给 VDDA 模拟域供电LDO 的 PSRR 比 BUCK 好纹波更小LDO2 输出 2.8V 给摄像头或特定模拟外设。充电器部分接锂电池系统电源路径管理自动在适配器和电池之间切换。这个分配方案的好处是数字域和模拟域分开供电BUCK 负责大电流高效率LDO 负责小电流低噪声。实际调试下来VDDA 用 LDO 供电后ADC 采样噪声比用 BUCK 直接供的时候低了将近 15dB这个后面会细说。1.3 上电时序的考量STM32F746VG 对上电时序没有特别苛刻的要求但 PCA9422 的寄存器可以配置各路输出的启动顺序和延迟。我一般会把 BUCK13.3V 主供电设成最先启动延迟 2ms 后启动 LDO1VDDA再延迟 2ms 启动 BUCK2 和 LDO2。这样做是为了让 MCU 内核先稳定再让模拟域上电避免上电瞬间模拟域受到数字域浪涌的干扰。PCA9422 的上电时序通过 I2C 配置寄存器 0x10 到 0x13 来设定每个通道有独立的使能位和延迟档位。延迟档位从 0ms 到 16ms 分 8 档可调步进 2ms。这个灵活性在调试阶段非常有用后面讲到问题排查时会具体说一个因为时序不对导致 MCU 偶尔启动失败的案例。2. 核心细节解析与实操要点2.1 PCA9422 的寄存器映射与 I2C 通信要点PCA9422 的 I2C 从机地址是 0x627 位地址支持标准模式100kHz和快速模式400kHz。我一般用 400kHz因为配置寄存器比较多上电初始化要写十几个寄存器400kHz 下大概 2ms 就能写完100kHz 就要 8ms 左右对于要求快速启动的应用来说差别很明显。寄存器大致分几类充电控制寄存器0x00~0x0F、BUCK 和 LDO 电压配置寄存器0x10~0x2F、中断和状态寄存器0x30~0x3F、以及一些杂项配置。每个 BUCK 的输出电压通过一个 5 位的字段来设置步进是 25mV范围从 0.6V 到 3.775V。LDO 的步进也是 25mV但范围略有不同。这里有个坑我踩过PCA9422 的 BUCK 输出电压寄存器在写入后需要等待至少 500us 才能生效如果你写完立刻去读状态寄存器可能会读到旧值。我一开始没注意这个在初始化代码里写完电压配置马上就去读回来校验结果一直报错后来查了数据手册的时序图才发现需要延时。解决办法很简单写完配置后加一个delay_us(500)再读或者直接不读回靠中断状态位来判断。2.2 STM32F746VG 的 I2C 外设配置STM32F746VG 有四个 I2C 接口我一般用 I2C1 或者 I2C2因为这两个挂在 APB1 总线上时钟频率相对低一些电磁干扰小。配置的时候注意几点时钟频率设 400kHz 时CCR 寄存器的值要根据 APB1 时钟频率来算。假设 APB1 跑 54MHz那 CCR 54MHz / (2 * 400kHz) 67.5取整 68。TRISE 寄存器设成 (1000ns / (1/54MHz)) 1 55。用 HAL 库的话HAL_I2C_Init会自动算这些值但如果你用 LL 库或者直接写寄存器就得自己算。我建议即使是用 HAL 库也把I2C_TIMINGR寄存器的值打印出来看一眼确认跟预期一致。有一次我调试一个板子I2C 死活通信不上最后发现是 CubeMX 里 APB1 时钟配错了实际跑的是 108MHz 而不是 54MHz导致 TIMINGR 算出来的值完全不对。另外PCA9422 的 SDA 和 SCL 线上一定要加上拉电阻典型值 4.7kΩ。如果总线上挂了多个从机可以适当减小到 2.2kΩ。上拉电阻接到 3.3V 还是 1.8V 取决于 PCA9422 的 IO 电压域这个在原理图设计阶段就要确认好。2.3 充电参数的设定与电池选型PCA9422 的线性充电器支持最大 1A 的充电电流通过寄存器 0x02 的 CHG_CURRENT 字段设置步进 50mA。我一般会把充电电流设成电池容量的 0.5C 到 1C 之间。比如一颗 2000mAh 的锂电池充电电流设 1000mA 就是 0.5C大概 2 小时充满设 2000mA 就是 1C1 小时充满但发热会明显一些。充电终止电压通过寄存器 0x03 设置默认是 4.2V也有 4.35V 和 4.4V 的选项但后者需要电池支持。我强烈建议不要为了多榨那点容量去设 4.35V除非电池规格书明确写了支持否则循环寿命会明显下降。实测下来4.2V 终止电压的电池在 500 次循环后容量保持率还有 85% 左右4.35V 的只有 70% 出头。还有一个细节PCA9422 的充电器有热调节功能当芯片温度超过 120°C 时会自动降低充电电流。这个功能在夏天或者密闭外壳里非常有用但如果你发现充电电流莫名其妙变小了先摸一下芯片烫不烫大概率是热调节在起作用。解决办法要么改善散热要么降低充电电流设定值。2.4 低功耗模式的实现思路STM32F746VG 支持 Sleep、Stop 和 Standby 三种低功耗模式。配合 PCA9422 的时候我一般这样设计MCU 进入 Stop 模式前通过 I2C 把 BUCK2 和 LDO2 关掉如果它们供的外设不需要工作只保留 BUCK1 和 LDO1 给 MCU 的待机域供电。然后 MCU 进入 Stop 模式电流从运行时的 150mA 降到 200uA 左右。PCA9422 本身在待机时的静态电流大概是 15uA加上 MCU 的 200uA整机待机电流能控制在 250uA 以内。对于一颗 2000mAh 的电池来说理论上能待机 8000 小时也就是将近一年。当然实际会有各种漏电流但做到几个月待机是没问题的。唤醒的时候PCA9422 的中断引脚会拉低MCU 通过 EXTI 中断唤醒然后在中断服务函数里重新配置 PCA9422 的输出。这里注意唤醒后不要立刻把所有的外设都打开最好分步上电避免瞬间电流过大导致电池保护板触发过流保护。我一般会先开 BUCK1延时 5ms再开 BUCK2 和 LDO再延时 10ms 才让外设初始化。3. 实操过程与核心环节实现3.1 硬件原理图设计要点先说一下 PCA9422 的外围元件。输入侧需要两个 10uF 的陶瓷电容做输入滤波靠近 VIN 引脚放置。电池引脚 BATT 也需要一个 10uF 电容。每个 BUCK 输出需要一个 2.2uH 的电感和一个 22uF 的输出电容电感选饱和电流至少 2A 的屏蔽电感比如 XAL4020 系列。LDO 输出只需要 1uF 到 4.7uF 的陶瓷电容即可。I2C 线上除了上拉电阻我还习惯在 SDA 和 SCL 上各串一个 33Ω 的电阻靠近 MCU 端放置。这个电阻的作用是抑制信号反射和过冲特别是在走线比较长或者有多个从机的时候。实测加了之后 I2C 通信的误码率明显降低尤其是 400kHz 下。还有一个容易忽略的点PCA9422 的散热焊盘一定要接到地平面并且打足够多的过孔。线性充电器在 1A 充电时发热量不小散热焊盘如果接地不好芯片温度会很快升上去触发热调节。我一般会在焊盘下面打 9 个 0.3mm 的过孔均匀分布。3.2 初始化代码的编写与调试初始化代码我分成三步第一步是 I2C 外设初始化第二步是 PCA9422 的寄存器配置第三步是电压输出使能和时序设置。下面是一个简化的初始化流程用 HAL 库写的// 第一步I2C 初始化 hi2c1.Instance I2C1; hi2c1.Init.Timing 0x10909CEC; // 400kHz 54MHz APB1 hi2c1.Init.OwnAddress1 0; hi2c1.Init.AddressingMode I2C_ADDRESSINGMODE_7BIT; HAL_I2C_Init(hi2c1); // 第二步配置 PCA9422 寄存器 uint8_t reg_val; // 设置 BUCK1 输出 3.3V reg_val 0x6C; // 0.6V 0x6C * 25mV 3.3V HAL_I2C_Mem_Write(hi2c1, 0x62 1, 0x10, 1, reg_val, 1, 100); HAL_Delay(1); // 设置 BUCK2 输出 1.8V reg_val 0x30; // 0.6V 0x30 * 25mV 1.8V HAL_I2C_Mem_Write(hi2c1, 0x62 1, 0x12, 1, reg_val, 1, 100); HAL_Delay(1); // 设置 LDO1 输出 3.3V reg_val 0x6C; HAL_I2C_Mem_Write(hi2c1, 0x62 1, 0x14, 1, reg_val, 1, 100); HAL_Delay(1); // 设置 LDO2 输出 2.8V reg_val 0x58; // 0.6V 0x58 * 25mV 2.8V HAL_I2C_Mem_Write(hi2c1, 0x62 1, 0x16, 1, reg_val, 1, 100); HAL_Delay(1); // 第三步使能输出并设置时序 reg_val 0x0F; // 使能所有通道 HAL_I2C_Mem_Write(hi2c1, 0x62 1, 0x18, 1, reg_val, 1, 100); HAL_Delay(1); // 设置上电时序BUCK1 先启动LDO1 延迟 2msBUCK2 和 LDO2 延迟 4ms reg_val 0x24; // 具体位定义参考数据手册 HAL_I2C_Mem_Write(hi2c1, 0x62 1, 0x1A, 1, reg_val, 1, 100);这段代码里我特意在每个写操作后加了 1ms 的延时虽然数据手册说 500us 就够了但多等一点更保险。实际调试的时候我建议先用示波器抓一下各路输出的上电波形确认时序跟预期一致。如果发现某路输出启动太慢或者有异常毛刺再回去调寄存器的延迟档位。3.3 电压和电流的实测验证代码跑起来之后我用万用表测了各路输出的空载电压BUCK1 实测 3.31VBUCK2 实测 1.79VLDO1 实测 3.29VLDO2 实测 2.81V都在 ±1% 以内符合预期。然后接上电子负载BUCK1 在 500mA 负载下电压降到 3.28V压降 30mV主要是 PCB 走线和电感 DCR 造成的可以接受。效率方面BUCK1 在 3.3V 输出、300mA 负载下输入 3.7V 时的效率大概是 88%。这个效率在同类产品里算中规中矩毕竟 PCA9422 的 BUCK 开关频率是 2MHz开关损耗会大一些但换来的是电感和电容可以选小尺寸的。如果你对效率要求特别高可以考虑把开关频率降到 1MHz但需要改寄存器配置而且外围元件要重新选型。充电测试我用了一颗 2000mAh 的电池充电电流设 1000mA从 3.0V 充到 4.2V 用了大概 2 小时 10 分钟。充电过程中芯片表面温度最高到 65°C在可接受范围内。如果外壳是密闭的建议加一个导热垫把热量导到外壳上。3.4 低功耗模式的实测数据我把 MCU 配成 Stop 模式PCA9422 只保留 BUCK1 和 LDO1 输出其他通道关掉。用高精度电流表测下来整机待机电流是 238uA。其中 MCU 大概 200uAPCA9422 静态电流 15uA剩下的 23uA 是 PCB 漏电流和电容漏电流。这个数据跟理论计算基本吻合。唤醒测试我做了 1000 次循环没有出现一次唤醒失败。唤醒时间从 EXTI 中断触发到 MCU 恢复运行大概是 15us加上 PCA9422 重新配置输出的时间总共不到 1ms。对于大多数应用来说这个唤醒速度足够了。这里分享一个省电小技巧如果某路输出在待机时不需要一定要在进入 Stop 模式前通过 I2C 关掉而不是靠 MCU 的 GPIO 去控制外设的使能引脚。因为 PCA9422 关掉输出后那一路的静态电流几乎为零而 GPIO 控制的话外设本身可能还有漏电流。我实测过关掉 BUCK2 和 LDO2 后待机电流从 350uA 降到了 238uA效果很明显。4. 常见问题与排查技巧实录4.1 I2C 通信失败的那些坑I2C 通信不上是最常见的问题我遇到过好几次原因各不相同。第一次是上拉电阻忘了焊SDA 和 SCL 一直是低电平示波器一看就发现了。第二次是地址搞错了PCA9422 的 7 位地址是 0x62但 HAL 库的HAL_I2C_Mem_Write函数需要传入左移一位后的地址也就是 0xC4。我一开始直接传了 0x62结果当然没反应。第三次比较隐蔽I2C 的时钟频率设成了 400kHz但 PCB 走线太长大概有 15cm而且没有做阻抗控制导致信号上升沿变缓从机识别不了。解决办法是把频率降到 100kHz或者在走线中间加一个 I2C 缓冲器。我最后是把频率降到 200kHz既能满足速度要求又保证了可靠性。还有一个问题是电源域不匹配。PCA9422 的 I2C 引脚电压域是跟 VDD_IO 走的如果 VDD_IO 是 1.8V 而 MCU 的 I2C 引脚是 3.3V就需要电平转换。我一般会在设计阶段就把 PCA9422 的 VDD_IO 接到 3.3V跟 MCU 保持一致省掉电平转换芯片。4.2 输出电压不对或者跳变有一次调试BUCK1 设的是 3.3V但实测只有 2.8V而且还在跳。查了半天发现是电感选错了我用的电感饱和电流只有 1A而 BUCK1 的峰值电流超过了 1A导致电感饱和输出电压跌落。换了一个饱和电流 2.5A 的电感后问题解决。还有一次是输出电容太小BUCK 工作在 DCM 模式下轻载时输出电压有低频纹波。后来把输出电容从 10uF 加到 22uF纹波从 50mV 降到了 15mV。所以选输出电容的时候不要只看容值还要看 ESR陶瓷电容的 ESR 低效果好但要注意直流偏压效应标称 22uF 的电容在 3.3V 下可能只有 15uF 的实际容值。4.3 充电异常与电池保护充电电流上不去或者充着充着就停了大概率是触发了保护。PCA9422 有几层保护输入过压保护、电池过压保护、过温保护、充电超时保护。我遇到过一次充电超时保护原因是充电电流设得太小电池容量又大超过了默认的 6 小时超时时间。解决办法是把超时时间改长或者把充电电流设大一点。还有一个问题是电池保护板触发过流保护。有些电池的保护板过流阈值设得比较低比如 1.5A而 PCA9422 的充电电流设了 1A加上系统负载 500mA总电流就超过 1.5A 了保护板直接切断。解决办法要么换保护板阈值更高的电池要么在系统负载大的时候降低充电电流。我一般会在软件里做动态调整检测到系统负载大时自动把充电电流降到 500mA。4.4 常见问题速查表现象可能原因排查方法解决办法I2C 通信失败上拉电阻缺失或阻值不对示波器看 SDA/SCL 波形补焊 4.7kΩ 上拉电阻I2C 通信失败从机地址错误用逻辑分析仪抓包确认地址为 0x62 左移一位输出电压偏低电感饱和测电感电流波形换饱和电流更大的电感输出电压纹波大输出电容不足示波器看纹波加大输出电容或换低 ESR 电容充电电流上不去触发热调节摸芯片温度改善散热或降低充电电流充电中途停止充电超时读状态寄存器延长超时时间或加大充电电流待机电流偏大外设未断电逐一断开外设测电流通过 I2C 关掉不用的输出通道MCU 偶尔启动失败上电时序不对示波器抓各路输出时序调整 PCA9422 时序寄存器4.5 几个容易被忽略的实操心得第一个心得PCA9422 的寄存器配置最好在 MCU 启动的早期完成不要等到 RTOS 跑起来再配。因为 RTOS 的任务调度可能会打断 I2C 传输导致配置不完整。我一般是在SystemInit之后、main函数的最开始就配置 PCA9422这时候中断还没开I2C 传输不会被干扰。第二个心得调试电源的时候一定要用示波器看波形不要只信万用表。万用表测的是平均值看不到纹波、毛刺和瞬态响应。我遇到过好几次万用表读数正常但系统偶尔复位的情况后来用示波器一看发现是上电瞬间有一百多毫秒的电压跌落MCU 的 BOR 复位了。这种问题只有示波器能发现。第三个心得PCB 布局的时候PCA9422 的输入电容和输出电容一定要尽量靠近芯片引脚走线要短而粗。我见过一个设计输入电容离芯片有 2cm 远结果上电瞬间的浪涌电流在走线电感上产生压降导致芯片反复重启。后来把电容挪到芯片旁边问题立刻消失。第四个心得如果项目对成本敏感可以考虑把 PCA9422 的某些通道用分立 LDO 替代。比如 LDO2 如果只需要给一个传感器供电电流又很小用一颗 1 毛钱的 LDO 可能比用 PCA9422 的内部 LDO 更划算。但这样会增加 BOM 数量和 PCB 面积需要权衡。第五个心得PCA9422 的中断引脚一定要接到 MCU 的 EXTI 上并且使能中断。这样充电状态变化、按键按下、过温保护等事件都能及时响应不用轮询寄存器。我一般会把中断服务函数写得尽量短只置一个标志位具体的处理放到主循环里做。5. 项目扩展与个人体会5.1 后续可以扩展的方向这套电源管理方案跑通之后我后来又做了几个扩展。一个是加了电量计功能通过 PCA9422 的库仑计寄存器读取电池电量然后在屏幕上显示百分比。不过 PCA9422 的库仑计精度一般大概 ±5%如果要求高精度还是得外挂一颗专门的电量计芯片。另一个扩展是动态电压调节。STM32F746VG 支持动态调整内核电压配合 PCA9422 的 BUCK1 输出可调可以在低负载时把内核电压从 1.2V 降到 1.0V进一步省电。不过这个需要改 STM32 的电源控制寄存器而且要注意电压调整的顺序先升压再升频先降频再降压顺序反了会死机。还有一个方向是加太阳能充电。PCA9422 的输入电压范围支持到 5.5V可以接一小块太阳能板通过 MPPT 算法跟踪最大功率点。不过太阳能板的输出不稳定需要加一级缓冲电路这个我还在试验阶段等跑通了再分享。5.2 个人在实际操作中的体会这套方案我前前后后调了大概两个月踩了不少坑但也积累了一些经验。最大的体会是电源管理这东西软件配置只占三成硬件设计和元件选型占七成。我一开始把精力都放在写 I2C 配置代码上结果发现输出电压不对、纹波大、充电发热全是硬件问题。后来把电感、电容、PCB 布局都优化了一遍软件几乎没改问题就都解决了。另一个体会是数据手册一定要仔细看特别是时序图和寄存器描述的小字部分。PCA9422 的数据手册有一百多页我第一遍看的时候跳过了很多细节结果调试的时候一个个踩回来。比如那个 500us 的寄存器生效时间就写在时序图的注释里字很小但很关键。最后一个体会是测试一定要覆盖极端情况。常温下跑得好好的不代表高温低温也没问题。我后来把板子放到恒温箱里从 -20°C 到 60°C 都测了一遍发现低温下电池内阻增大充电器会提前进入恒压阶段充电时间变长。这个在数据手册里没有写但实际应用中必须考虑。所以如果你的产品要在宽温范围内工作一定要做高低温测试不要只测常温。