ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

STM32F723ZE + PCA9422 PMIC 电源设计与DVFS动态调压实战

STM32F723ZE + PCA9422 PMIC 电源设计与DVFS动态调压实战 这段时间一直在调一块以 STM32F723ZE 为主控的高性能板子电源管理部分我直接选了 PCA9422 这颗 PMIC 来搭而不是像以前那样用一堆 LDO、分离 DC-DC 拼凑。整机的目标很明确单节锂电池供电要同时稳住 3.3V 主 IO、可动态调节的 1.2V 内核电压、1.8V 外设轨和一路低噪声模拟电源还得支持睡眠唤醒和低功耗切换。PCA9422 加 STM32F723ZE 这套组合刚好把“高性能内核供电”和“灵活低功耗调度”这两件事合在了一起。这篇文章就围绕这套方案的完整落地过程来写适合正在做 MCU 加 PMIC 电源管理的嵌入式工程师也适合刚接触动态电压调节 DVFS 的开发者参考。1. 先捋清楚这套电源系统到底要解决什么问题1.1 单板供电需求拆解在做任何电源设计之前第一步不是选芯片而是把板上所有负载的电压、电流、噪声要求列清楚。STM32F723ZE 这颗 M7 主控本身就有多个电源引脚主电源 VDD 通常工作在 3.3V内核电源 VDDCORE 需要 1.2V 左右的低压大电流备份域 VBAT 要常供电VREF 给 ADC 做基准还有 VCAP 等引脚需要配套电容。这些还不是全部板上还挂了 SD 卡、传感器、显示模组和模拟采集前端。我当时整理出来的电源轨需求大概是这样的电源轨负载类型标称电压电流预估噪声要求输入 VBUS锂电池/USB 5V3.7V~5.0V最大 2A防倒灌BUCK1MCU 主电源 VDD 及数字外设3.3V600mA普通BUCK2MCU 内核 VDDCORE0.9V~1.3V 动态800mA纹波尽量小BUCK3传感器/外设接口1.8V300mA普通LDO模拟前端/基准3.3V100mA低噪声这个表里的关键是 BUCK2。Cortex-M7 在 216MHz 满载运行时和降频运行时对内核电压的要求差异很大。固定给 1.2V 当然能跑但在低频待机场景下白白浪费动态功耗。所以第二路 Buck 必须支持动态调压也就是 DVFS。1.2 为什么是 PCA9422 而不是分离方案以前很多人做这种板子习惯用“一个 3.3V LDO 一个 1.2V DCDC 一个 1.8V LDO”拼起来。这么做不是不行但问题很多。LDO 效率太低内核电流到八百度毫安时热量惊人分离 DCDC 的上电时序要额外加复位芯片或者用 GPIO 硬拉使能脚去排顺序动态调压更是无从谈起除非再用一个数字电位器去调反馈电阻这会绕非常多的路。PCA9422 这颗 PMIC 正好把这些痛点集中解决了。它内部集成了多路高效降压转换器和一路低噪声 LDO 型输出输入范围覆盖 2.7V 到 5.5V单节锂电池和 USB 5V 都能直接用。更重要的是它带有 I2C 接口所有输出轨的电压、限流、软启动、上电顺序都可以通过寄存器配置运行过程中改寄存器就能完成动态调压。注意实际项目中如果用的是其他型号的 PMIC寄存器定义和通道数量可能不同但“先读手册确认通道能力再规划分配”这个思路是通用的。选 PCA9422 还有一层原因它内部带了 ADC可以监测输入电压、芯片温度和输出电压状态。这意味着 MCU 可以定期通过 I2C 读取整机的电源健康状况而不用额外挂电压监测芯片。这对便携设备来说节省的不只是 BOM 成本还有 PCB 面积和调试时间。2. 电源拓扑和硬件连接设计2.1 三路 Buck 加一路 LDO 怎么分配最合理芯片选完之后接下来的问题是“哪一路送哪个负载”。我刚拿到 PCA9422 的引脚定义时第一反应就是把三路 Buck 全都拉到 3.3V反正都能输出再用 LDO 转各种小电压。这个想法很快被自己否了。BUCK 的转换效率虽然高但开关纹波对模拟电路来说是灾难让模拟电源轨直接吃 Buck 的纹波ADC 采样会抖得你怀疑人生。最终我采用的分配策略BUCK1 给 STM32F723ZE 的 VDD 主电源以及 SD 卡、电平转换器等数字外设固定 3.3V。BUCK2 专门给内核 VDDCORE。这一路电流最大而且要做 DVFS必须独立控制不能和其他负载混在一起。BUCK3 给板载传感器和 1.8V 逻辑接口。传感器对纹波不敏感用 Buck 供电效率更好。LDO 单独给模拟前端和电压基准3.3V 输出前面串一颗磁珠和 π 型滤波把开关噪声基本滤干净。这个分配方案的核心思路是“按噪声敏感度分层”。数字负载能吃 Buck模拟负载必须吃线性电源内核这种大电流动态负载则要有专属通道。混用的话你很难排查到底是谁把谁的噪声带进来了。2.2 引脚级连接细节硬件连接上PMIC 和 MCU 之间的主要接口就是 I2C 和中断线。SCL、SDA 接 STM32F723ZE 的 I2C1 引脚两个线上各拉一个 4.7kΩ 上拉到 3.3V。这里要特别留意 I2C 的电平域如果 PMIC 的 I2C 电源域是 1.8V 而 MCU 是 3.3V就需要电平转换或确认双方是否兼容。PCA9422 的工作电压范围比较宽但还是建议按手册明示的电平来接。中断脚 INT 接到 STM32 的一个 EXTI 引脚上。PMIC 内部发生欠压、过流、过温这些事件时INT 会拉低来通知 MCU。这个脚一定不要悬空哪怕软件初始化还没写完也要通过硬件上拉保证默认状态是确定的。使能控制要看具体引脚。部分 PMIC 支持通过 EN 引脚控制整个芯片进入待机也支持仅通过寄存器关闭某个通道。实际调试中我是用一颗 GPIO 控制总 EN再通过寄存器去做各通道的独立开关。这样硬件上有兜底任何时候 GPIO 拉低整机电源立刻断掉比单纯依赖 I2C 命令可靠得多。2.3 布局布线与电感电容选择电源布局这件事真的是“板子贴片之前多花一小时调板少熬三个夜”。PCA9422 的 Buck 频率在中高频范围SW 节点是最大的噪声源PCB 上这个节点的面积一定要小铺铜形状尽量接近正方形不要拉长走线。输出电容要贴近 PMIC 的反馈采样点从电容正极采样不要从远端负载采样否则负载瞬态响应会被走线电阻拖垮。电感选型上不要贪便宜。额定电流至少留 1.5 倍余量饱和电流要大于最大输出电流DC 阻抗越小越好。内核这一路电流大我用了 2.2uH/2A 的电感外设路电流小1uH 就够。输出电容按手册推荐来内核路额外并联一个小容量的陶瓷电容用于高频去耦。布局中的地平面也要动点脑筋。功率地和模拟地在 PMIC 的散热焊盘下方单点连接不要大面积铺通了事。实际测下来这样处理之后模拟 LDO 输出端的纹波能控制在几个毫伏以内。3. 驱动层设计让 PMIC 真正跑起来3.1 先打通 I2C 链路硬件上电之后第一件事不是配输出电压而是先确认 MCU 能不能正常读写 PMIC 寄存器。我用的是 STM32 的 HAL 库I2C 配置为 400kHz 快速模式从机地址按手册填写。uint8_t pca9422_read_reg(uint8_t reg_addr, uint8_t *value) { return HAL_I2C_Mem_Read(hi2c1, PCA9422_I2C_ADDR, reg_addr, I2C_MEMADD_SIZE_8BIT, value, 1, 100); } uint8_t pca9422_write_reg(uint8_t reg_addr, uint8_t value) { return HAL_I2C_Mem_Write(hi2c1, PCA9422_I2C_ADDR, reg_addr, I2C_MEMADD_SIZE_8BIT, value, 1, 100); }读寄存器时先读一个芯片 ID 寄存器验证通信。如果返回值不是预期值大概率是地址不对、上拉电阻虚焊或者电平不匹配。这里有个容易踩的坑I2C 的HAL_I2C_Mem_Read本质上是“先发寄存器地址再发读方向”有些 PMIC 对寄存器地址的接收有时间要求如果每次通信间隔太长或总线上有其他设备抢占有可能会导致读回 0xFF。3.2 初始化配置流程PCA9422 这类 PMIC 的寄存器通常有写保护机制。初始化时第一步是写入解锁命令然后才能修改关键配置。我把初始化函数拆成了清晰的几步这个习惯强烈建议保持调试时会省很多事。void pca9422_init(void) { pca9422_unlock(); /* 配置 BUCK1: 3.3V, 使能 */ pca9422_write_reg(PCA9422_BUCK1_CFG, 0x33); pca9422_write_reg(PCA9422_BUCK1_EN, 0x01); /* 配置 BUCK2: 1.2V, 使能 */ pca9422_write_reg(PCA9422_BUCK2_CFG, PCA9422_VOLT_1V2); pca9422_write_reg(PCA9422_BUCK2_EN, 0x01); /* 配置 BUCK3: 1.8V, 使能 */ pca9422_write_reg(PCA9422_BUCK3_CFG, PCA9422_VOLT_1V8); pca9422_write_reg(PCA9422_BUCK3_EN, 0x01); /* 配置 LDO: 3.3V, 使能 */ pca9422_write_reg(PCA9422_LDO_CFG, PCA9422_LDO_3V3); pca9422_write_reg(PCA9422_LDO_EN, 0x01); pca9422_lock(); }关键点是“先配电压、后使能输出”。如果先使能再改电压输出会先跳到默认值再慢慢爬升很可能触发负载设备的不正常复位尤其是内核这种对电压变化极敏感的负载。上电瞬间还要注意软启动斜率PCA9422 寄存器里可以配置启动时间。负载端有大电容时软启动过快会触发过流保护表现为输出反复打嗝、电压上不去。3.3 中断与电源状态监测PMIC 的好处就是它自己能感知自己的健康状态。PCA9422 内部的 ADC 可以采集输入电压、输出电压采样和芯片温度。我写了一个周期 1 秒的状态巡检任务用 I2C 读 ADC 转换结果和预设阈值做比较。INT 中断处理这里有一个很实用的建议进中断服务函数后第一件事是把所有状态寄存器读一遍再清中断标志。很多 PMIC 的中断标志是“读后自动清零”或“写 1 清零”只清一个标志可能漏掉同时发生的其他异常。我之前就犯过这个错欠压标志清了但过温标志还挂着结果 PMIC 一直拉低 INTMCU 端反复进中断查了半天才发现是标志没清干净。4. DVFS 动态电压调节实战4.1 为什么非要用 DVFS嵌入式设备做低功耗时大家最先想到的是降 CPU 频率毕竟“跑得慢就省电”这个直觉正确。但从功耗公式 P 正比于 C·V²·f 来看电压的影响是平方级的。频率从 216MHz 降到 108MHz动态功耗只降一半但如果同时把内核电压从 1.2V 降到 1.0V功耗能降到原来的三分之二再乘以一半效果明显更好。STM32F723ZE 这类 M7 芯片的数据手册里通常会针对不同频率给出对应的内核电压范围。高频跑满至少要 1.2V 以上中低频则可以用 1.0V 甚至更低。这正好和 PCA9422 的 I2C 动态调压能力配合起来MCU 要提速时先把电压调上去再提升频率要降速时先把频率降下来再调低电压。4.2 运行态调压的软件流程这里的顺序问题特别关键。升频必须“先升压、再升频”因为从 108MHz 突然切到 216MHz内核电流会瞬间上升如果电压还没到位可能直接触发欠压复位。降频则反过来“先降频、再降压”否则内核还跑在高频电压被拉低之后同样会崩。void dvfs_set_high_performance(void) { /* 先把 BUCK2 电压升到 1.26V */ pca9422_write_reg(PCA9422_BUCK2_CFG, PCA9422_VOLT_1V26); HAL_Delay(2); __disable_irq(); HAL_RCC_ClockConfig(RCC_ClkInitStruct, FLASH_LATENCY_6); __enable_irq(); } void dvfs_set_low_power(void) { __disable_irq(); HAL_RCC_ClockConfig(RCC_ClkInitStruct, FLASH_LATENCY_2); __enable_irq(); /* 频率降到位之后再把 BUCK2 电压降到 1.0V */ pca9422_write_reg(PCA9422_BUCK2_CFG, PCA9422_VOLT_1V0); HAL_Delay(2); }代码里我刻意加了延时。这不是随意延的而是调压后 PMIC 输出电压和输出电容充放电需要时间。上面示例的 2ms 已经比较保守实际跳变时间大约在几百微秒到 1ms 级别跟输出电容容值和负载电流都有关系。严谨的做法是写一个“等待电压稳定”的轮询函数通过 PMIC 的状态寄存器确认输出已经到达目标值再继续往下走。4.3 睡眠与唤醒的电源调度低功耗不只是跑起来的时候调调电压真正省电靠的是睡眠状态下的电源策略。我的板子待机场景是外部 RTC 唤醒系统大部分时间停在 STM32 的 Stop 模式此时整机只保留待机需求最小的轨。进入睡眠前的调度顺序所有传感器掉电调用对应供电 Buck 的关闭寄存。LDO 可以关闭模拟前端进入断电态。BUCK1 保持 3.3V给主控的唤醒逻辑供电。BUCK2 降到最低可运行电压保证 SRAM 不掉电。关闭 I2C 和 EXTI 之外的所有外设时钟。唤醒之后最怕的是某个电源轨还没就绪外设就急着初始化。我实际调试中碰到过唤醒后传感器读到的数据全是 0xFF就是因为传感器供电轨用的是 BUCK3唤醒代码里先初始化了 I2C 去读传感器但 BUCK3 的软启动还没完成。后来在唤醒流程前面加了一个读取 PMIC 输出状态寄存器的等待循环确认所有电源轨稳定后再继续问题就消失了。5. 踩坑实录问题与排查5.1 I2C 无应答或读到 0xFF这个现象最让人头疼。我排查过一个很隐蔽的问题PMIC 的 I2C 地址是 7 位格式但是 HAL 库的地址参数需要左移一位变成 8 位格式。如果直接填 7 位地址写寄存器时看似没问题读的时候死活没响应。后来我把读写函数里的地址参数统一做了一个移位宏再也没在这个问题上栽过。5.2 上电时内核输出打嗝BUCK2 输出端我放了很大一颗电容导致软启动时 PMIC 检测到过流输出进入打嗝保护内核电压上不去主控一直復位。解决办法是把软启动时间寄存器调到更慢的档位输出的爬坡速度降下来电容充电电流自然就小了系统顺利启动。5.3 DVFS 升降压导致系统复位现象是跑高负载时随机复位或者从低负载切高负载时偶发死机。抓逻辑分析仪发现调压命令已经发出但 MCU 侧等待电压稳定的时间太短电压还没爬到位就去切高频了。把等待时间拉长并且每次调压都读回 PMIC 的状态标志确认输出就绪复位就再也没有复现过。5.4 模拟电源纹波导致 ADC 跳动LDO 后端虽然滤波做得不错但一开始模拟地和数字地没有做单点连接数字开关噪声通过地平面耦合进了模拟回路。后续在 PCB 上做了地分割模拟地和功率地通过磁珠和单点过孔连接ADC 采样的跳动幅度大幅下降。我整理了一张排查速查表基本覆盖了这套方案的大多数问题现象可能原因排查思路I2C 读不到 PMIC地址位宽错误、上拉缺失用逻辑分析仪抓波形确认寄存器地址发送是否正确内核电压上不去软启动过快、输出电容过大调慢软启动斜率检查限流寄存器配置高频运行时复位DVFS 电压没到位就升频延长等待时间轮询状态寄存器确认电压稳定整机待机电流偏大某个 Buck/LDO 没关逐个通道关闭并测量电流定位没断电的设备模拟 ADC 采样抖动地回流、LDO 滤波不足检查地平面分割LDO 后加 π 型滤波最后再说两句整套方案调下来我最深的体会是PMIC 不是焊上去、初始化一下就完事的器件它其实是整机电源策略的执行者。你把硬件设计、I2C 配置、DVFS 调度、中断处理这几个环节串起来才算是真正把 PCA9422 和 STM32F723ZE 这套组合的能力用满。建议接手的开发者拿到板子后先把 PMIC 的寄存器手册从头到尾翻一遍弄清楚每一位的默认值和锁定位再动手写驱动能少走很多弯路。遇到问题不要急着改软件先测 PMIC 每一路输出的实际电压和纹波很多时候问题其实出在硬件布局上补几颗电容、调整一下采样点位置比改代码有效得多。
RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进