☰
STM32F723ZE电源管理实战:PCA9422 PMIC配置与I2C驱动开发
2026/10/10 1:14:28 网站建设 项目流程

1. 项目缘起与整体设计思路

STM32F723ZE 这颗芯片在圈子里算是老熟人了,216MHz 的 Cortex-M7 内核、带 FPU、带 Chrom-ART 图形加速器,还有 512KB Flash 和 256KB RAM,做图形界面或者中高端控制类产品非常顺手。但真正把板子画出来、焊上去、跑起来之后,很多人会卡在一个看似不起眼、实则要命的地方——电源管理。STM32F723ZE 的供电域分得比较细,VDD、VDDA、VREF+、VBAT、VCAP1、VCAP2 这些引脚一个都不能含糊,尤其是 VCAP1 和 VCAP2 上必须挂 2.2μF 的低 ESR 电容,否则内核 LDO 不稳,芯片跑着跑着就复位。再加上如果产品是电池供电,还需要考虑充电管理、电量计、多路电压域的动态调节,这时候光靠一颗 LDO 或者 DC-DC 就不够了。

PCA9422 就是在这种需求下进入视野的。它是 NXP 推出的一颗高度集成的电源管理 IC,专门面向低功耗 MCU 和嵌入式应用,内部集成了两路 Buck、两路 LDO、一个电池充电器、一个电量计,还有 I2C 可编程的电压调节和多种低功耗模式。用它来给 STM32F723ZE 供电,可以把整块板子的电源树从“一堆分立器件”压缩成“一颗 PMIC + 几个外围电容电感”,PCB 面积能省下至少三分之一,BOM 也清爽很多。

这个项目的核心目标就是:用 PCA9422 作为主电源管理芯片,为 STM32F723ZE 提供完整、可控、可监测的供电方案,同时通过 I2C 总线让 STM32 能够动态读取电源状态、调节各路输出电压、管理充电过程,最终实现一套“MCU 自己管自己电源”的闭环系统。适合谁来参考?如果你正在做电池供电的 STM32F7 项目、或者想从分立电源方案迁移到 PMIC 方案、又或者单纯想搞清楚 PMIC 和 MCU 之间到底怎么配合,这篇内容应该能帮你少走不少弯路。

整体设计思路可以拆成三层:第一层是电源树规划,确定哪路电给谁、电压多少、电流多大;第二层是 PCA9422 的配置,包括上电默认值、I2C 寄存器映射、充电参数、低功耗模式切换;第三层是 STM32 侧的软件驱动,通过 I2C 读写 PCA9422 寄存器,实现状态监测和动态调节。三层之间是递进关系,电源树没规划好,后面配置就是瞎调;配置没搞明白,软件驱动写出来也是白搭。

注意:PCA9422 的 I2C 地址是 7 位 0x2C(写地址 0x58,读地址 0x59),这个地址在 STM32 的 HAL 库里配置时要左移一位,很多人第一次写会搞错。

2. PCA9422 核心特性与电源树规划

2.1 为什么选 PCA9422 而不是分立方案

先说说选型逻辑。STM32F723ZE 的典型工作电压是 1.7V 到 3.6V,内核电压 1.2V 由内部 LDO 或者外部 SMPS 提供。如果直接用一颗 3.3V LDO 供电,简单是简单,但电池利用率极低——锂电池从 4.2V 放到 3.4V 这段能量基本浪费了。用 Buck 把电池电压降到 3.3V,效率能到 90% 以上,但普通 Buck 芯片没有充电管理,也没有电量计,还得再加一堆外围。

PCA9422 把这几个功能全塞进去了:输入电压范围 2.5V 到 5.5V,支持锂离子和锂聚合物电池充电,充电电流可编程到 500mA,两路 Buck 输出范围 0.6V 到 3.3V(步进 25mV),两路 LDO 输出范围 1.5V 到 3.3V(步进 100mV),还有 7 位 ADC 做电量计和温度监测。I2C 接口最高 400kHz,寄存器映射清晰,datasheet 虽然有一百多页,但常用寄存器就那么二十来个。

对比一下分立方案:一颗充电 IC + 一颗 Buck + 一颗 LDO + 一颗电量计,至少四颗芯片,PCB 面积翻倍,I2C 或者 SPI 接口还得分别处理。PCA9422 一颗全搞定,而且各路输出的上电时序可以通过寄存器配置,这对 STM32F723ZE 这种多电源域芯片来说太重要了——VDD 和 VDDA 的上电顺序如果不对,芯片内部可能产生闩锁电流。

2.2 电源树的具体分配

STM32F723ZE 的电源引脚大致分这几类:

  • VDD(多引脚):主数字电源,典型 3.3V,给内核、外设、GPIO 供电
  • VDDA:模拟电源,给 ADC、DAC、运放供电,必须和 VDD 同电位或略高
  • VREF+:ADC 参考电压,可以接 VDDA 或者外部基准
  • VBAT:备份域电源,接纽扣电池或者超级电容
  • VCAP1、VCAP2:内核 LDO 输出,各挂 2.2μF 低 ESR 电容
  • VDDUSB:USB 外设电源,独立供电时可以 3.3V

用 PCA9422 来分配的话,我通常这样规划:

PCA9422 输出电压设置供给目标最大电流备注
Buck13.3VSTM32 VDD、VDDUSB500mA主数字电源
Buck21.8V外部 SRAM、SD 卡500mA可动态调节
LDO13.3VSTM32 VDDA、VREF+200mA低噪声,给模拟域
LDO21.2V外部 PHY 或传感器200mA可关闭省电
VBAT 直通3.0VSTM32 VBAT-接纽扣电池

Buck1 设 3.3V 是因为 STM32F723ZE 的主电源范围是 1.7V 到 3.6V,3.3V 是标准值,而且 USB 外设也需要 3.3V。Buck2 设 1.8V 是为了给外部低速外设供电,如果不需要可以关掉。LDO1 单独给 VDDA 供电,是因为 Buck 的开关噪声会耦合到模拟域,LDO 的 PSRR 在 1kHz 到 100kHz 范围内通常有 60dB 以上,能有效滤掉 Buck 的纹波。LDO2 给一些可选的外设,不用的时候通过 I2C 关掉,静态电流能降到微安级。

实操心得:VCAP1 和 VCAP2 的 2.2μF 电容一定要用 X5R 或 X7R 材质,ESR 低于 10mΩ,而且尽量靠近引脚放置。我见过有人用 1μF 代替,结果芯片跑 USB 的时候随机死机,换了电容立马就好。

2.3 上电时序与默认配置

PCA9422 上电后各路输出的默认值可以通过 OTP(一次性可编程)或者外部电阻配置,但更灵活的方式是让 STM32 通过 I2C 在启动后重新配置。不过这里有个鸡生蛋问题:STM32 自己还没上电,怎么通过 I2C 配置 PCA9422?所以 PCA9422 必须有一组安全的上电默认值,保证 STM32 能先跑起来,然后再由软件接管。

我通常把 PCA9422 的默认配置设成:Buck1 上电即输出 3.3V,Buck2 默认关闭,LDO1 上电输出 3.3V,LDO2 默认关闭。这样 STM32 一上电就能拿到 VDD 和 VDDA,内部复位释放后开始执行代码,第一件事就是通过 I2C 初始化 PCA9422,把不需要的通道关掉,把需要动态调节的通道设好电压范围。

上电时序方面,PCA9422 内部有软启动电路,Buck 的启动时间典型值是 1ms,LDO 是 200μs。如果 STM32 的复位阈值是 2.0V,那么 Buck1 从 0V 升到 3.3V 的过程中,大约 0.6ms 的时候就会超过 2.0V,STM32 开始释放复位。但此时 VDDA 可能还没稳,所以软件里最好在 main 函数开头加一个 10ms 的延时,等所有电源都稳定了再初始化外设。

3. PCA9422 寄存器配置与 I2C 通信实操

3.1 关键寄存器速查

PCA9422 的寄存器地址空间是 8 位,常用寄存器我整理了一张表,方便你写驱动的时候直接查:

寄存器地址名称功能常用值
0x00DEVICE_ID器件 ID读回 0x42
0x01STATUS1充电状态、电源状态只读
0x02STATUS2故障状态只读
0x10BUCK1_CFGBuck1 电压配置0x1E = 3.3V
0x11BUCK2_CFGBuck2 电压配置0x0C = 1.8V
0x12LDO1_CFGLDO1 电压配置0x12 = 3.3V
0x13LDO2_CFGLDO2 电压配置0x00 = 关闭
0x20CHG_CFG充电电流配置0x08 = 400mA
0x21CHG_VOLT充电截止电压0x0C = 4.2V
0x30ADC_CTRLADC 控制0x01 = 启动
0x31ADC_DATAADC 结果只读

Buck 的电压计算公式是:Vout = 0.6V + code × 25mV。比如要输出 3.3V,code = (3.3 - 0.6) / 0.025 = 108 = 0x6C。但注意寄存器里不是直接写 0x6C,而是有一个偏移和使能位。具体来说,BUCK1_CFG 的 bit7 是使能位,bit6 到 bit0 是电压代码。所以写 3.3V 的时候,实际写入的值是 0x80 | 0x6C = 0xEC。这个细节 datasheet 里写得比较绕,我第一次调的时候在这里卡了半天。

LDO 的电压计算公式是:Vout = 1.5V + code × 100mV。要输出 3.3V,code = (3.3 - 1.5) / 0.1 = 18 = 0x12。LDO1_CFG 的 bit7 也是使能位,所以写入 0x80 | 0x12 = 0x92。

3.2 STM32 侧 I2C 驱动编写

STM32F723ZE 有多个 I2C 外设,我一般用 I2C1,因为它的引脚通常在 PB6 和 PB7,布线方便。HAL 库的初始化代码这里不赘述,重点说读写 PCA9422 的封装函数。

#define PCA9422_ADDR (0x2C << 1) // HAL 库需要左移一位 uint8_t PCA9422_ReadReg(uint8_t reg) { uint8_t val; HAL_I2C_Mem_Read(&hi2c1, PCA9422_ADDR, reg, I2C_MEMADD_SIZE_8BIT, &val, 1, 100); return val; } void PCA9422_WriteReg(uint8_t reg, uint8_t val) { HAL_I2C_Mem_Write(&hi2c1, PCA9422_ADDR, reg, I2C_MEMADD_SIZE_8BIT, &val, 1, 100); }

这两个函数是最基础的,实际用的时候还要加错误重试。PCA9422 的 I2C 接口在电源不稳的时候可能会 NACK,所以最好写一个带重试的版本:

HAL_StatusTypeDef PCA9422_WriteReg_Retry(uint8_t reg, uint8_t val, uint8_t retry) { HAL_StatusTypeDef status; while (retry--) { status = HAL_I2C_Mem_Write(&hi2c1, PCA9422_ADDR, reg, I2C_MEMADD_SIZE_8BIT, &val, 1, 100); if (status == HAL_OK) return HAL_OK; HAL_Delay(1); } return status; }

初始化流程我通常这样写:

void PCA9422_Init(void) { // 验证器件 ID uint8_t id = PCA9422_ReadReg(0x00); if (id != 0x42) { // 器件异常,可以点亮错误 LED 或者进入安全模式 Error_Handler(); } // Buck1 设 3.3V 并使能 PCA9422_WriteReg_Retry(0x10, 0x80 | 0x6C, 3); // Buck2 设 1.8V 并使能 PCA9422_WriteReg_Retry(0x11, 0x80 | 0x30, 3); // LDO1 设 3.3V 并使能 PCA9422_WriteReg_Retry(0x12, 0x80 | 0x12, 3); // LDO2 关闭 PCA9422_WriteReg_Retry(0x13, 0x00, 3); // 充电电流 400mA,截止电压 4.2V PCA9422_WriteReg_Retry(0x20, 0x08, 3); PCA9422_WriteReg_Retry(0x21, 0x0C, 3); }

注意:写寄存器的时候不要一次性把使能位和电压代码分开写,因为 PCA9422 的寄存器是单字节读写,分两次写会导致中间状态输出电压跳变。比如先写 0x6C 再写 0x80,中间那一瞬间 Buck 会输出一个错误的电压,可能触发 STM32 的欠压复位。

3.3 动态电压调节的实现

STM32F723ZE 支持动态电压调节,比如在跑 216MHz 的时候内核需要 1.2V,降到 120MHz 的时候可以降到 1.0V 省电。但内核电压是内部 LDO 管的,外部只能调 VDD。不过对于外部 SRAM 或者传感器,动态调压就很有用了。

假设 Buck2 给外部 SRAM 供电,SRAM 在待机的时候可以降到 1.5V 保持数据,读写的时候需要 1.8V。实现起来就是改 BUCK2_CFG 的电压代码:

void Set_Buck2_Voltage(float volt) { if (volt < 0.6f) volt = 0.6f; if (volt > 3.3f) volt = 3.3f; uint8_t code = (uint8_t)((volt - 0.6f) / 0.025f); PCA9422_WriteReg_Retry(0x11, 0x80 | code, 3); }

调用Set_Buck2_Voltage(1.5)就能把 Buck2 降到 1.5V。但要注意,降压之前最好先让 SRAM 进入低功耗模式,否则电压跌落过程中可能丢数据。

4. 充电管理与电量计实战

4.1 充电参数配置

PCA9422 的充电器支持三种模式:预充、恒流充、恒压充。预充电流通常是恒流充的 10%,用于深度放电的电池唤醒。恒流充电流通过 CHG_CFG 寄存器设置,公式是:Icharge = code × 50mA。要设 400mA,code = 8 = 0x08。截止电压通过 CHG_VOLT 设置,公式是:Vterm = 3.5V + code × 50mV。要设 4.2V,code = 14 = 0x0E。但我前面表格里写的是 0x0C,对应 4.1V,这个看电池规格,一般锂离子电池截止电压是 4.2V,所以应该用 0x0E。

这里更正一下:CHG_VOLT 寄存器写 0x0E 才是 4.2V。我刚开始调的时候按 0x0C 配的,结果电池只能充到 4.1V,容量少了 10% 左右。

充电状态可以通过 STATUS1 寄存器读取:

bit含义说明
7CHG_STAT[2]充电状态高位
6CHG_STAT[1]充电状态中位
5CHG_STAT[0]充电状态低位
4保留-
3VBUS_OK输入电源有效
2BAT_OK电池存在
1保留-
0保留-

CHG_STAT 的编码:000 表示未充电,001 表示预充,010 表示恒流充,011 表示恒压充,100 表示充电完成,101 表示故障。软件里可以轮询这个寄存器,把充电状态显示在屏幕上或者通过串口打印出来。

4.2 电量计的使用

PCA9422 内部有一个 7 位 ADC,可以测量电池电压、充电电流、输入电压和温度。ADC_CTRL 寄存器 bit0 写 1 启动转换,转换完成后 ADC_DATA 寄存器里就是结果。电池电压的计算公式是:Vbat = ADC_DATA × 20mV。比如读回 0x64 = 100,对应 2.0V,这显然不对,因为锂电池最低也有 3.0V。实际上 ADC 的满量程是 5.12V,7 位分辨率,所以 LSB = 5.12 / 128 = 40mV。读回 100 对应 4.0V,这就合理了。

电量百分比不能只看电压,因为锂电池的放电曲线中间很平,3.7V 到 3.5V 之间可能放了 60% 的电。更准的方法是电压查表加电流积分。我通常做一个 11 点的电压-电量表:

电池电压剩余电量
4.20V100%
4.10V90%
4.00V80%
3.90V65%
3.80V50%
3.70V35%
3.60V25%
3.50V15%
3.40V8%
3.30V3%
3.00V0%

软件里用线性插值算百分比,精度够用了。如果要更准,可以在充电和放电的时候分别用不同的表,因为充电和放电的端电压不一样。

实操心得:ADC 测量的时候最好连续读 8 次取平均,因为 PCA9422 的 ADC 没有硬件滤波,单次读数跳动可能有 ±2 个 LSB,对应 ±80mV,算成电量就是 ±5% 的误差。

5. 低功耗模式与系统级省电策略

5.1 PCA9422 的低功耗模式

PCA9422 支持多种低功耗模式,通过 PWR_MODE 寄存器(地址 0x40)切换:

  • 模式 0:全功能,所有通道开启
  • 模式 1:Buck1 和 LDO1 开启,Buck2 和 LDO2 关闭
  • 模式 2:仅 Buck1 开启,其他全关
  • 模式 3:深度睡眠,仅保留 VBAT 直通和充电器

STM32F723ZE 本身也有多种低功耗模式:Sleep、Stop、Standby。两者配合起来,可以实现非常低的系统功耗。比如系统空闲的时候,STM32 进入 Stop 模式,同时通过 I2C 把 PCA9422 切到模式 2,只保留 Buck1 给 STM32 的待机电路供电。这时候整机电流可以降到 200μA 左右。

唤醒的时候,STM32 通过外部中断或者 RTC 唤醒,第一件事就是把 PCA9422 切回模式 0,等电源稳定后再恢复外设。

void Enter_LowPower_Mode(void) { // 关闭不需要的外设 HAL_GPIO_WritePin(GPIOA, GPIO_PIN_5, GPIO_PIN_RESET); // 关闭传感器 // PCA9422 切到模式 2 PCA9422_WriteReg_Retry(0x40, 0x02, 3); // STM32 进入 Stop 模式 HAL_PWR_EnterSTOPMode(PWR_LOWPOWERREGULATOR_ON, PWR_STOPENTRY_WFI); // 唤醒后恢复 PCA9422_WriteReg_Retry(0x40, 0x00, 3); HAL_Delay(5); // 等电源稳定 }

5.2 动态功耗优化

除了低功耗模式,还可以根据负载动态调整 Buck 的工作频率。PCA9422 的 Buck 支持 PWM 和 PFM 两种模式,PWM 模式纹波小但效率低,PFM 模式效率高但纹波大。轻载的时候切到 PFM,重载的时候切回 PWM,可以兼顾效率和噪声。

切换通过 BUCK_CFG 寄存器的 bit6 控制:0 是 PWM,1 是 PFM。我通常设一个阈值,比如负载电流小于 50mA 的时候切 PFM,大于 50mA 切 PWM。负载电流可以通过 ADC 测量,或者根据系统状态估算。

注意:PFM 模式下 Buck 的输出纹波可能达到 50mV 以上,如果 VDDA 是用 Buck 供电的,会影响 ADC 精度。所以模拟域最好还是用 LDO 供电,或者至少在 ADC 采样的时候切回 PWM 模式。

6. 常见问题与排查技巧实录

6.1 I2C 通信失败

这是最常见的问题,现象是 HAL_I2C_Mem_Read 返回 HAL_ERROR 或者 HAL_TIMEOUT。排查步骤:

  1. 用示波器看 SCL 和 SDA 有没有波形,如果没有,检查 GPIO 配置和上拉电阻。PCA9422 的 I2C 引脚需要 4.7kΩ 上拉到 3.3V,如果上拉电阻太大,上升沿太慢,400kHz 下会通信失败。
  2. 如果有波形但 NACK,检查从机地址。PCA9422 的 7 位地址是 0x2C,HAL 库要左移一位变成 0x58。用逻辑分析仪抓一下,看地址字节是不是 0x58。
  3. 如果地址对但还是 NACK,检查 PCA9422 的 VDD 是否正常。PCA9422 的 I2C 接口是由 VDD 供电的,如果 VDD 没起来,I2C 引脚是浮空的,自然不响应。

6.2 Buck 输出电压不对

现象是设了 3.3V 但实测只有 1.2V 或者 0V。原因可能有:

  • 使能位没写。BUCK_CFG 的 bit7 是使能位,如果只写了电压代码没写使能位,输出是关闭的。
  • 电感选型不对。PCA9422 的 Buck 推荐电感值是 2.2μH,饱和电流至少 1A。如果电感太小,峰值电流触发限流,输出电压会掉。
  • 反馈引脚接错。PCA9422 的 Buck 是内部反馈,不需要外部电阻分压,但 FB 引脚必须接到输出电容的正端,如果悬空或者接错,输出会异常。

6.3 充电电流上不去

现象是设了 400mA 但实际只有 100mA。检查:

  • 输入电源的限流。PCA9422 的输入电流限制默认是 500mA,如果输入源本身只能提供 300mA,充电电流会被拉低。可以通过 INPUT_LIMIT 寄存器(地址 0x22)调整,但不要超过输入源的能力。
  • 电池电压太低。如果电池电压低于 3.0V,PCA9422 会进入预充模式,电流只有恒流模式的 10%。这是正常行为,等电池电压升到 3.0V 以上就会自动切到恒流充。
  • 温度保护。PCA9422 内部有温度监测,如果芯片温度超过 120°C,会自动降低充电电流。检查 PCB 散热是否足够,Buck 和充电器的地平面要尽量大。

6.4 系统随机复位

这个最头疼,现象是跑着跑着突然复位,没有规律。排查思路:

  • 先看 VCAP1 和 VCAP2 的电容。用示波器看这两个引脚上的电压,正常应该是 1.2V 左右,纹波小于 20mV。如果纹波很大或者电压偏低,换低 ESR 的 2.2μF 电容。
  • 再看 VDD 的纹波。Buck 的输出纹波在 PWM 模式下应该小于 30mV,如果超过 50mV,可能是电感或者输出电容的问题。输出电容推荐 22μF,至少两个 10μF 并联。
  • 最后看软件。有没有在中断里调用 I2C 读写?I2C 是阻塞操作,如果在高优先级中断里调用,可能阻塞其他中断,导致看门狗复位。建议把 I2C 操作放到主循环或者低优先级任务里。

6.5 常见问题速查表

现象可能原因排查方法解决方案
I2C NACK地址错误逻辑分析仪抓地址确认地址 0x2C 左移一位
I2C NACK上拉电阻太大看 SCL 上升沿换 4.7kΩ 上拉
Buck 输出 0V使能位没写读回寄存器写入时或上 0x80
Buck 输出偏低电感饱和看电感电流波形换 2.2μH/1A 电感
充电电流小输入限流测输入电压调整 INPUT_LIMIT
随机复位VCAP 电容不对测 VCAP 纹波换 2.2μF X5R
随机复位中断里调 I2C看中断优先级移到主循环
ADC 跳动大没有滤波连续读 8 次软件取平均

最后分享一个小技巧:PCA9422 的 STATUS2 寄存器里有故障标志位,包括过温、过流、欠压等。系统复位后第一件事就是读这个寄存器,把故障原因存到备份寄存器或者 Flash 里,下次调试的时候就能知道上次为什么复位。这个习惯帮我省了很多抓波形的时间。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询