资讯动态

PCA9422与PIC18F47K40协同实现电池供电多路电源管理方案

发布时间:2026/10/24 1:27:48 来源:尧图企业网站定制
做便携设备最头疼的事情之一就是电源。电池供电的产品往往不止一路电压核心芯片要 1.2V接口和传感器要 3.3V模拟前端可能还要一路纹波特别小的 1.8V。如果每路都用一个 LDO 或者 DC-DC再用单片机的 GPIO 去控制使能脚代码会越写越多上电时序却越来越不可控低压、过流、短路这些保护也很难统一管理。后来我把目光转到“PMIC MCU”的组合上用一颗专门的电源管理芯片把电源轨、时序、保护都包下来单片机只通过 I2C 去配置和监控。这篇文章就围绕 PCA9422 和 PIC18F47K40 这个组合从硬件接线、I2C 驱动、上电时序到调试踩坑把整套电源管理方案完整拆开讲一遍。适合正在做电池供电设备、电源轨在三路以上、想把电源部分做得更规范的嵌入式工程师参考。1. 整体方案设计为什么要把电源交给 PMIC1.1 直接控 LDO 和 DC-DC 的方式存在什么问题先说一个典型场景一块电池供电的主板有 1.2V 核心电压、3.3V IO 电压、1.8V 模拟电压。如果完全用分立方案单片机上电后要先等自己的电源稳定然后初始化 GPIO再去按顺序打开各路 LDO这个过程中只要有一路电源的启动时间不对就可能让后端芯片进入闩锁或者异常状态。更麻烦的是如果 MCU 本身出了问题电源侧完全没人兜底欠压、短路、过热全靠后端的保护芯片单独处理板子上的器件清单会变得很长。还有一个容易被忽视的问题分立的 DC-DC 输出电压是靠反馈电阻固定的想调整电压就要换电阻。产品做出来以后如果发现核心电压需要从 1.2V 改成 1.1V只能改板或者飞线。这在量产阶段是不可接受的。1.2 PCA9422 在系统里的角色PCA9422 是一颗面向低功耗便携设备的电源管理芯片内部集成了多路 DC-DC 和 LDO并且支持通过 I2C 接口动态配置输出电压、工作模式和保护阈值。在我这个方案里它承担三个核心职责。第一是提供多路电源输出。电池输入进来以后由 PCA9422 内部的多路降压转换器分别产生核心电压、IO 电压和模拟电压每路都能独立配置电压和限流点。第二是管理上电时序。PMIC 可以按照预设顺序依次打开各路输出单片机上电后只需要检查状态寄存器不需要自己在 GPIO 上做复杂的时序控制。第三是异常响应。欠压、过压、过温、过流这些故障都会通过中断引脚通知单片机系统就能在第一时间做保护动作。1.3 PIC18F47K40 在这个方案里的定位PIC18F47K40 是一颗 8 位单片机带丰富的外设资源在这里主要做几件事通过 I2C 总线对 PCA9422 做初始化配置读取 PMIC 的中断和状态信息再配合自身的 ADC 去监测电池电压和一些关键电流信号。它不需要直接参与电源的开关控制主要工作是“配置”和“监控”。选择这样一颗单片机还有一个好处它内部有 CIP核独立外设像 CLC、CWG、NCO 这些模块可以和外部事件联动不需要 CPU 一直介入。比如把 PMIC 的中断引脚连到单片机的引脚上配合定时器做一个硬件级的故障锁存能在几百纳秒内把关键外设拉成安全状态这种响应速度靠轮询是根本做不到的。2. 硬件连接与 PCB 布局要点2.1 最小系统连接关系这个方案里PCA9422 和 PIC18F47K40 之间的连线其实很少主要就是电源、地、I2C 两根线再加一根中断线。下面是我在实际原理图中用到的连接关系。PCA9422 功能引脚连接到 PIC18F47K40说明电池输入 BATT电池正极经保护电路接入输入主电源SCLRC3/SCL1I2C 时钟线SDARC4/SDA1I2C 数据线INT_NRB4 外部中断输入低电平有效故障或事件通知PGOODRC5电源正常指示可做上电完成信号GNDGND公共地单点接入系统地电源输入部分我习惯加一颗串联的磁珠和一个 TVS 管磁珠用来抑制电池线上的高频噪声TVS 做过压保护。输入侧放一到两颗 10uF 陶瓷电容输出侧每路根据负载情况配 10uF 到 22uF 电容。要注意的是陶瓷电容在 DC 偏压下容量会下降所以选择耐压更高的规格比如 25V 耐压的 X7R 电容来保证实际容量。2.2 I2C 上拉电阻与电平匹配PCA9422 的 I2C 接口工作电平要和单片机一致这个方案里二者都工作在 3.3V 电平下。SDA 和 SCL 各接一颗上拉电阻到 3.3V阻值我一般选 4.7kΩ这个阻值在 400kHz 以内的总线速率下没什么问题。如果板子上还有其他 I2C 设备要考虑总线上总的等效上拉电阻是不是太小。总线上挂的设备越多等效电阻越低拉低时流过的电流就越大SSR 就会过大。可以用公式估算上拉电阻上限由总线上升沿时间和总线电容决定下限由端口灌电流能力决定。大多数情况下 2.2kΩ 到 4.7kΩ 是一个安全区间我习惯在原型阶段先放 4.7kΩ如果发现通信时序不好再往下调。还需要注意如果单片机侧有复用引脚默认带上拉电阻可能导致总线上拉电阻并联后阻值过小。最好把引脚的数字输入上拉关闭统一用外部上拉电阻。2.3 中断信号处理PCA9422 的中断输出通常是开漏结构单片机端要启用内部弱上拉或者外部上拉电阻。我先用外部上拉电阻同时把 PIC18F47K40 对应的 RB4 引脚配置为输入和中断上升沿触发默认高电平当 PMIC 检测到故障时拉低单片机就立刻进入中断服务程序。中断引脚上放一个小电容滤除毛刺也有必要但电容不能太大否则会把低电平脉冲拉成圆弧导致边沿检测丢事件。我一般放 1nF 到 10nF这个容值对几十微秒级别的低电平脉冲影响不大又能滤掉高频干扰。2.4 布局与布线经验电源类设计在布局上有个老规矩关键环路面积越小越好。输入电容要靠近 PMIC 的输入引脚输出电容靠近电感和芯片的输出端地平面尽量完整。我踩过的一个教训是把电池输入走线拉得太长结果线路电感太大在负载突变时产生明显的振铃。后来把输入走线加宽到 1mm 以上并让电容尽可能靠近芯片引脚波形立刻干净了很多。对于多层板电源轨的走线尽量放在内层表层用地包围可以减少对外辐射。模拟电源轨和数字电源轨的滤波电容要分别就近放在模拟电路和数字电路附近不要集中放在 PMIC 边上否则滤波效果会打折扣。取样地点应该用单点连接避免数字噪声通过地线耦合进模拟区域。3. PIC18F47K40 的 I2C 驱动与寄存器配置3.1 I2C 主机初始化PIC18F47K40 的 MSSP 模块可以工作在 I2C 主机模式。下面这段初始化代码以 16MHz 系统时钟、200kHz 总线速率为例。关键点是 MSSP 波特率寄存器的值计算公式然后确认引脚方向和数据输出配置。#define I2C_CLOCK_HZ 200000UL #define FCY 16000000UL void i2c_master_init(void) { TRISCbits.TRISC3 1; TRISCbits.TRISC4 1; ANSELCbits.ANSC3 0; ANSELCbits.ANSC4 0; SSP1CON1 0x08; SSP1CON1bits.SSPEN 1; SSP1STATbits.SMP 1; SSP1CON1bits.SSPM 0b1000; SSP1ADD (uint8_t)(FCY / (4UL * I2C_CLOCK_HZ) - 1); SSP1STATbits.CKE 0; }SSP1ADD 的计算 16MHz / 4 / 200kHz - 1 19这里把 SMP 置位是关闭展宽模式适合 100kHz 到 400kHz 的总线速率。如果总线有比较重的负载或者走线比较长可以考虑把速率降到 100kHz重新按公式计算 SSP1ADD。3.2 基本读写函数PMIC 寄存器的访问是一次写一个地址或者连续读多个字节。下面这段代码是单字节读写的基础函数实际项目里我会把错误处理改成返回错误码避免调用方忽略失败。bool pmic_write_reg(uint8_t reg, uint8_t val) { i2c_start(); if (!i2c_write(PMIC_ADDR_WR)) return false; if (!i2c_write(reg)) return false; if (!i2c_write(val)) return false; i2c_stop(); return true; } bool pmic_read_reg(uint8_t reg, uint8_t *val) { i2c_start(); if (!i2c_write(PMIC_ADDR_WR)) return false; if (!i2c_write(reg)) return false; i2c_restart(); if (!i2c_write(PMIC_ADDR_RD)) return false; *val i2c_read(); i2c_stop(); return true; }PMIC_ADDR_WR 和 PMIC_ADDR_RD 分别是从机地址左移一位后的写地址和读地址具体地址以你手头样片的硬件配置脚和数据手册为准。我第一次调的时候就是被 7 位地址和 8 位地址绕晕的从数据手册里拿到 7 位地址后别直接拿去做比较要先左移一位。3.3 上电后先做设备 ID 校验每次上电我先读取 PCA9422 的设备 ID 寄存器用这个值确认 I2C 通信链路是通的也确认 PMIC 没有被错误配置。这个步骤很重要因为如果 PCB 上的芯片焊接有问题或者刚才的 I2C 上拉阻值不对在读 ID 这一步就能发现。void pmic_verify(void) { uint8_t dev_id 0; if (pmic_read_reg(REG_DEV_ID, dev_id)) { if (dev_id EXPECT_DEV_ID) pwr_state PWR_STATE_OK; else pwr_state PWR_STATE_CHIP_ERR; } else { pwr_state PWR_STATE_I2C_ERR; } }我在原型板上加了一个启动 LED读 ID 成功就点亮。这样硬件调试的时候不用接调试器也能快速判断电源和 I2C 是否正常。3.4 配置电源轨输出电压配置电压寄存器的顺序是从低到高逐路写入。下面是我在项目里使用的寄存器映射示例具体地址请以你拿到的芯片手册为准。寄存器功能配置值举例REG_DCDC1_VOUTDCDC1 输出电压0x0A对应 1.2VREG_DCDC2_VOUTDCDC2 输出电压0x14对应 3.3VREG_DCDC3_VOUTDCDC3 输出电压0x0C对应 1.8VREG_PWR_CTL输出使能与工作模式0x07开启三路输出写入的时候要注意有些寄存器的高位是只读的或者是有写保护逻辑的如果发现写入之后读回来不对先检查是否需要对特定寄存器解锁。这个跟单片机配置寄存器很像防呆设计是有意为之。3.5 配置中断使能我把 PMIC 的欠压、过压、过温中断都打开了思路是宁可多报也不漏报。单片机收到中断后先读状态寄存器然后根据故障类型决定处理动作。void pmic_irq_init(void) { pmic_write_reg(REG_IRQ_MASK, 0x07); pmic_write_reg(REG_IRQ_CLEAR, 0xFF); }中断处理函数里有个细节清中断标志时必须先读后写有些 PMIC 的清除动作是“写 1 清 0”和普通寄存器操作正好相反。如果一直不对就会陷入中断风暴这个在后面章节会详细说。4. 上电时序与运行状态管理4.1 明确各路电源的上电顺序这个项目的电源顺序是先 DCDC1 给核心芯片供电稳定后再开 DCDC2 给 IO 供电最后开 DCDC3 给模拟部分供电。模拟电源必须最后上避免模拟电路先工作时受到数字信号的干扰。实现方式有两种。一种是把时序配置写在 PMIC 的启动序列寄存器里让 PMIC 硬件自动按顺序打开输出另一种是单片机在初始化代码里逐路使能输出。我建议先把 PMIC 内部时序寄存器配好让硬件自主工作单片机的 I2C 只负责监控这样即使单片机死机上电时序也能保证。如果 PMIC 没有内部时序寄存器那就要在单片机侧实现一个带有超时判断的顺序使能流程。void power_rail_startup(void) { pmic_write_reg(REG_DCDC1_VOUT, 0x0A); pmic_enable_rail(RAIL_DCDC1); if (!wait_rail_ready(RAIL_DCDC1, 10)) { pwr_state PWR_STATE_DCDC1_FAIL; return; } pmic_write_reg(REG_DCDC2_VOUT, 0x14); pmic_enable_rail(RAIL_DCDC2); if (!wait_rail_ready(RAIL_DCDC2, 10)) { pwr_state PWR_STATE_DCDC2_FAIL; return; } pmic_write_reg(REG_DCDC3_VOUT, 0x0C); pmic_enable_rail(RAIL_DCDC3); if (!wait_rail_ready(RAIL_DCDC3, 10)) { pwr_state PWR_STATE_DCDC3_FAIL; return; } }每一路等待电源稳定都设了超时时间一旦超时立刻停掉整个启动流程。这样可以避免后端芯片在电源不完整的情况下进入未知状态。4.2 运行状态机整个电源管理我把它简化成五个状态单片机的主循环根据状态执行不同的动作。状态说明主要动作PWR_STATE_OFF待机PMIC 未开启等待按键或外部事件PWR_STATE_STARTUP正在配置/开启电源轨按顺序配置各输出PWR_STATE_RUN正常运行周期性采集电压、监控中断PWR_STATE_LOWPOWER低功耗运行切换部分电源轨模式或关闭非必要轨PWR_STATE_FAULT故障锁定停止输出记录故障码状态机的好处是逻辑清晰出现异常时可以在故障状态里安全停留不会因为反复重试导致 PMIC 热积累。4.3 动态调压与低功耗切换动态调压是我后来才意识到好用的功能。本来我担心修改 DCDC 输出电压会带来比较大的电压跌落实际测试时发现PMIC 内部有软启动斜坡控制输出电压是平滑过渡的。我在低功耗模式下把不需要的高性能电源轨电压降下来比单纯关断再开启要温柔得多。void enter_low_power(void) { pmic_write_reg(REG_DCDC1_VOUT, LOW_VOLTAGE_1P0); pmic_write_reg(REG_MODE_CTL, LOW_POWER_MODE); }低功耗模式下功耗和响应速度是矛盾的不要在低功耗状态下还要求所有外设全速运行。正确的做法是降电压、降频率、关外设一步一步来。5. 调试过程中遇到的典型问题与排查方法5.1 I2C 通信超时或读不到设备这个问题的出现频率最高。我遇到过的原因有三个I2C 地址写错了SCL/SDA 其中一根线断了以及 PMIC 的复位引脚一直处于复位状态导致芯片根本没起来。排查时先用逻辑分析仪抓 SDA 的波形如果能抓到正确的地址位但芯片没有 ACK重点查复位引脚和硬件地址配置脚如果连启动条件都没有重点查上拉电阻和引脚配置。先把物理层搞定再去查寄存器配置否则会被 I2C 状态机折腾到崩溃。5.2 上电瞬间电压跌落第一次测试时DCDC1 启动瞬间电压跌落特别明显之后才慢慢回升。原因是启动时负载电容过大或者限流保护被触发了。解决方法是把限流点调高同时在启动前先检查 PMIC 输出电压配置寄存器是否已经写好。如果 PMIC 支持软启动时间配置我把它调到适中的档位既能抑制浪涌又不会让电源建立时间过长。5.3 中断风暴中断引脚一直交替拉低拉高程序看起来像是卡死在中断服务程序里。后来发现是中断标志位没有被正确清除。PMIC 的中断状态寄存器是“写 1 清 0”我却用写 0 去清等于什么都没做。正确的清中断流程是进入服务程序后逐个读状态寄存器把每个标志位记录下来最后用对应位写 1 清零。另外还要检查是不是还有未被处理的故障源先把故障源排除再清标志位。5.4 配置丢失或寄存器恢复默认值有些配置在断电重新上电后会丢失这个倒不奇怪因为 PMIC 分一次性可编程和 RAM 配置两种。调试阶段用 RAM 配置断电就丢这是正常现象。但是如果运行过程中寄存器自己变了那就要查两件事一是总线噪声导致误写二是看门狗或复位引脚被误触发。我在批量功能测试中发现偶尔会出现运行中电压跳变最后定位到是主控的程序跑飞把错误的配置写进去了。后来加了接口数据校验和关键寄存器定期回读问题就消失了。5.5 电源纹波偏大纹波超标时先不要急着换电容先确认负载电流是不是接近该路 DC-DC 的负载范围临界值。在轻载和重载模式下PMIC 会切换 PFM/PWM 模式纹波表现完全不同。如果测量时用的是示波器探头要用地弹簧而不是长接地夹否则测量到的纹波有一大半是探头自身的环路感应出来的。我测量 DCDC3 模拟电源轨时探头用原厂地弹簧测出来纹波大约 8mV用接地夹测出来却有 40mV差距非常大。测量方法不对很容易误判。6. 实测验收与后续扩展6.1 电源系统验收清单每次拿到新板子我会按照下面的清单逐项检查漏掉任何一项后面都可能变成隐患。基本电源轨电压是否与配置一致纹波是否在可接受范围内上电时序是否符合设计要求每路之间的间隔是否足够PMIC 中断能否正常产生和清除故障类型记录是否完整在低功耗模式下电流是否降到预期水平反复开关机和随机断电后系统能否正常恢复。6.2 关键参数速查项目参考值I2C 速率200kHz 到 400kHz先低速验证I2C 上拉电阻4.7kΩ视总线电容调整输入电容10uF 0.1uF 陶瓷电容输出电容每路 10uF 到 22uFPIC18F47K40 主频16MHz 内部振荡器USB 场景用外部晶振中断引脚上拉外部 10kΩ 上拉6.3 可以继续扩展的方向这套组合做完基础电源管理之后还能往后扩不少东西。用 PIC18F47K40 的内置 ADC 做电池电压采集做一个简单的两段式电量估算把 PMIC 的故障记录和单片机运行日志结合起来做成故障黑匣子再加一个低功耗无线模块做成带远程上报功能的传感器节点。PMIC 的扩充能力足够后面加一路电源给新增外设供电也不需要改硬件只要在 I2C 配置里打开对应输出就行。在做完这个项目之后我最大的体会是电源管理方案的稳定不在于单片机里写了多少防护逻辑而在于把合理的硬件拓扑、正确的时序与保护机制规划清楚然后让各器件各司其职。PMIC 负责电源执行与保护单片机负责配置与监控整个系统既简洁又可靠。后续我会找时间把电池电量采集和充电管理部分再单独整理成一篇文章如果各位在调试 PCA9422 或 PIC18F47K40 时也遇到过有意思的问题欢迎一起交流。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑