news 2026/10/24 3:22:58

基于PCA9422与PIC18F87J50的便携设备可编程电源管理设计

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
基于PCA9422与PIC18F87J50的便携设备可编程电源管理设计

手里这个可穿戴小设备改了四次电源方案,最后一版总算稳定下来:主控用 PIC18F87J50,电源部分全部交给 PCA9422 这颗 PMIC,两个芯片之间就靠一条 I2C 总线通信。之前用分立 LDO 加充电管理芯片拼出来的方案,BOM 散出去四十几颗料,每次上电都像开盲盒,时序乱、纹波大、电池电压检测还老飘。换这套组合之后,BUCK 输出电压、充电电流、负载开关全都能用寄存器改,调试只需要改一个字节,不用再动烙铁。

这篇文章就把这套完整电源管理的设计思路、寄存器配置、PIC18F87J50 侧的驱动实现、实测数据和踩过的坑一起整理出来。适合正在做便携设备、电池供电产品,或者想在 8 位 MCU 平台上引入可配置电源方案的读者参考,内容偏实操,不会跟你绕理论。

1. 方案为什么这样搭

1.1 这类产品真正缺什么

便携产品往往同时要满足三件事:电池要能充、系统要有多路稳定电压、整机待机功耗要压得住。看似不难,但放在一起就很别扭。分立方案的典型状态是:BOM 里既有升压也有降压,还有一堆使能脚、反馈电阻、分压检测电路,每一路都靠硬件定死。真要调一路电压,得换电阻;要改充电电流,得换电阻;想测电池电压,还得自己布局一个分压网络,再靠 MCU 的 ADC 慢慢校正。

PCA9422 这种 PMIC 把充电、路径管理、两路 DC-DC、一路 LDO、负载开关整合在一个封装里,关键参数全走 I2C 寄存器。也就是说,硬件上你只需要接好电感、电容和电池,剩下全部交给软件。对量产调试来说,这个转变是本质性的:软件改寄存器比换贴片电阻快得多,而且每台机器还能在产线单独校准参数,不需要为不同电池容量准备不同料号的硬件版本。

1.2 两个芯片各管什么

PIC18F87J50 是一款带丰富外设的 8 位 MCU,本身带 USB、多路定时器、ADC 和硬件 I2C 模块,跑 3.3V 逻辑,非常适合做电池供电产品的主控。PCA9422 则专管功率:输入 USB/适配器的 5V 或者电池电压进来,经过内部路径管理和转换,输出一路、两路、三路不同的电源轨,同时承担电池充电控制。

两个芯片的协作逻辑很简单:PCA9422 负责"电",PIC18F87J50 负责"脑"。MCU 通过 I2C 读取 PMIC 的状态寄存器,知道现在是不是有外部电源、电池电压到了多少、充电有没有完成;再通过写寄存器,把各路输出电压切到合适的档位、把充电电流调到合适的值。它们之间唯一协作通道就是 I2C,占两根 IO 线,外加一个可选的中断脚。

芯片职责与对方协作方式
PCA9422电池充电、输入路径切换、多路 DC-DC/LDO 输出、状态上报接受 I2C 配置,输出电源状态位
PIC18F87J50系统逻辑、任务调度、功耗状态管理、充电策略决策通过 I2C 读写寄存器,控制 PMIC 工作模式

2. PCA9422 核心功能拆解与配置逻辑

2.1 输入路径与电池充电怎么处理

PCA9422 内部是典型的"系统带路径管理"架构:外部 5V 存在时,系统的负载优先由外部输入供电,同时 PMIC 内部以恒定电流、恒定电压的方式给电池充电;外部电源断开后,无缝切换到电池供电。好处很明显,外部电源在场时系统不会因为电池被边充边放而打嗝,电池寿命也更好保。对耳机仓、手环、便携标签这类产品来说,这套逻辑是刚需。

充电部分的配置点主要是三个:充电电流、截止电压和终止判断。充电电流一般按电池容量的 0.5C 来选,500mAh 的电池就配 250mA 左右,留一点余量到 300mA 也行,但别超太多,线性充电的功耗会直接变成热量。截止电压就按电池规格写,常见三元锂电是 4.2V,也有一些 4.35V 的配方,写错一格电池寿命会明显打折。这里建议直接读 PMIC 状态寄存器里的充电结束标志,不要自己用 ADC 去判电池满没满,PMIC 自己对 CV 阶段的截止电流判断比外部 ADC 可靠得多。

2.2 多路电源轨怎么规划

拿到一颗 PMIC,第一步不是去翻寄存器表,而是把你系统里所有负载的电压、电流、噪声要求列成一张表,再挨个对上电源轨。我这套设备里是这样分配的:

电源轨来源电压供谁特点
VDD_MAINBUCK13.3V主控、外设、LED电流大,效率优先
VDD_IOBUCK21.8V传感器、存储卡接口低纹波
VDD_ANALDO1.8V模拟前端、基准超低噪声,始终在线
VBAT电池直连3.0-4.2V后备供电、RTC常电,不受开关控制

这个分配是反过来推出来的:先把对噪声最敏感的放到 LDO,把大电流的放到效率高的 BUCK1,BUCK2 给不上不下的中间档。顺序上,我习惯让 LDO 那条常电轨直接挂在电池后面,保证系统即便是深度睡眠也有一路干净电源能维持 RTC 和唤醒逻辑;BUCK1 和 BUCK2 则按需使能,睡眠时可以直接关掉,省下它们的静态电流。

2.3 寄存器访问与初始化顺序

PCA9422 的寄存器拓扑大致分四块:系统状态/ID 类、BUCK 输出电压和使能类、充电控制类、中断/辅助功能类。7 位 I2C 从机地址我手头这颗是 0x10,写地址就是 0x20、读地址 0x21,不同批次可能不同,上电第一件事先扫一遍总线确认,别拿着数据手册里的地址硬写到程序里。

初始化顺序上,我建议严格按这个来:先读 ID 寄存器确认芯片在线且版本正确;然后配置 BUCK 的电压档位和限流,但先不要使能输出;接着配置充电参数,包括充电电压、充电电流、终止电流;再配置中断掩码,把需要 MCU 响应的状态位放开;最后才统一使能各路输出。这样做的原因很简单,避免芯片在未配置完整的情况下带着默认电压起载,万一默认值跟后级负载不匹配,上电瞬间就可能出事。

// 伪代码形式的初始化顺序,具体寄存器地址请对照用手头芯片的数据手册 #define PCA9422_ID_REG 0x00 #define PCA9422_BUCK1_CFG 0x05 #define PCA9422_BUCK2_CFG 0x08 #define PCA9422_CHARGE_CFG 0x0C #define PCA9422_ENABLE_REG 0x10 void pca9422_init(void) { uint8_t id = pca9422_read_reg(PCA9422_ID_REG); if (id == 0x00) { // 芯片没应答或者读到的 ID 异常,先停下排查硬件 report_power_error(); return; } // 先写电压配置,不使能 pca9422_write_reg(PCA9422_BUCK1_CFG, 0x2A); // BUCK1 -> 3.3V 档 pca9422_write_reg(PCA9422_BUCK2_CFG, 0x18); // BUCK2 -> 1.8V 档 pca9422_write_reg(PCA9422_CHARGE_CFG, 0x29); // 充电电压 4.2V,电流 300mA // 统一使能,保证上电时序一致 pca9422_write_reg(PCA9422_ENABLE_REG, 0x07); }

3. PIC18F87J50 侧的软硬件实现

3.1 硬件连接和总线设计

PIC18F87J50 的 MSSP 模块可以配置成 I2C 主机,SCL 和 SDA 各接一颗上拉电阻。PCA9422 工作电压范围比较宽,和 MCU 同用 3.3V 逻辑时直接连就行,不需要电平转换。上拉电阻我建议 2.2k 到 4.7k 之间,总线电容不大就选 4.7k,省电;如果 I2C 线上还挂了多个从机,比如传感器、充电仓通信,就降到 2.2k,保证上升沿够快。

比较容易被忽略的是中断脚。PCA9422 一般会有状态输出脚,用来上报充电完成、输入插入、欠压等事件,这个脚要接到 PIC18F87J50 的边沿中断口上。即使你对自己的轮询频率有信心,也一定要接,否则充电完成的时刻可能晚几分钟才被读到,直接影响用户体验。

PCA9422 引脚接 PIC18F87J50 引脚备注
SCLSCL(MSSP 时钟)上拉 4.7k 到 3.3V
SDASDA(MSSP 数据)上拉 4.7k 到 3.3V
INTB/状态输出外部中断输入低有效,触发充电完成/掉电事件

3.2 I2C 驱动与 PMIC 访问封装

PIC 的硬件 I2C 模块用起来不难,麻烦的是时序细节。我自己习惯封装三个函数:读寄存器、写寄存器、批量读状态。写寄存器时要注意,PMIC 有些寄存器是易失的,掉电或进入某些低功耗模式后会复位回默认值,所以每次从睡眠唤醒之后,需要重新执行一遍关键寄存器初始化,不能指望靠寄存器默认值活着。

// 基于 PIC18F87J50 MSSP 模块的 I2C 主模式驱动片段 // 时钟频率 100kHz 示例 #define FOSC 8000000UL #define I2C_SPEED 100000UL void i2c_master_init(void) { SSPADD = (FOSC / (4UL * I2C_SPEED)) - 1; // 计算波特率寄存器 SSPCON1 = 0x08; // I2C 主模式,使能 MSSP SSPCON2 = 0x00; SSPSTAT = 0x80; // 标准速率模式 } uint8_t pca9422_write_reg(uint8_t reg, uint8_t val) { uint8_t i2c_timeout = 0; while (SSPSTAT & 0x04) { // 等待总线空闲 if (++i2c_timeout > 200) return 0xFF; } SSPCON2bits.SEN = 1; // 发起 START while (SSPCON2bits.SEN); SSPBUF = 0x20; // 写地址 0x20 while (SSPSTATbits.R_nW && !SSPCON2bits.ACKSTAT); if (SSPCON2bits.ACKSTAT) { // 无应答则放弃 SSPCON2bits.PEN = 1; return 0xFF; } SSPBUF = reg; // 写寄存器地址 while (!SSPSTATbits.BF); SSPCON2bits.ACKDT = 0; SSPCON2bits.ACKEN = 1; SSPBUF = val; // 写数据 while (!SSPSTATbits.BF); SSPCON2bits.ACKEN = 1; SSPCON2bits.PEN = 1; // 发起 STOP while (SSPCON2bits.PEN); return 0; }

我单独提一句总线等待机制。用循环轮询 MSSP 状态位的写法在工程里问题不大,但一定要加超时保护,不然 SDA 被某个从机拉死,主控就会一直卡在等待循环里,整机看起来就像死机了。我后来给所有 I2C 操作都套了一个通用的超时宏,实测下来排查问题省了大量时间。

3.3 上电与低功耗状态机

有了 I2C 驱动,剩下的就是状态管理。我把它拆成五态:上电复位态、运行态、充电态、睡眠态、深度睡眠态。每个状态里定义好哪些电源轨开着、充电参数怎么设置、中断关注哪些事件。

上电复位后,MCU 先做基础初始化,接着读一次 PMIC 全状态寄存器,判断当前有没有外部电源、电池电量如何,再决定进入充电态还是运行态。进入充电态前,把充电电流按电池特性设好,并且打开充电完成中断。睡眠态下,BUCK1 可以保持 3.3V 但降载,BUCK2 和外围负载关掉,LDO 常电轨保留 1.8V 给 RTC;深度睡眠再进一步。每切换一次状态,都把 PMIC 的关键寄存器重写一遍,因为 PCA9422 的部分配置在掉电后会恢复默认,这个坑我第一次没注意,导致睡眠唤醒后 BUCK 电压跌了一档,摸了半天才发现是寄存器没重写。

typedef enum { PWR_STATE_RESET = 0, PWR_STATE_RUN, PWR_STATE_CHARGE, PWR_STATE_SLEEP, PWR_STATE_DEEP_SLEEP } pwr_state_t; void power_state_switch(pwr_state_t new_state) { switch (new_state) { case PWR_STATE_SLEEP: // 关闭 BUCK2,BUCK1 保持,LDO 保持 pca9422_write_reg(PCA9422_ENABLE_REG, 0x05); break; case PWR_STATE_DEEP_SLEEP: // 保留 LDO 常电轨 pca9422_write_reg(PCA9422_ENABLE_REG, 0x01); break; default: pca9422_write_reg(PCA9422_ENABLE_REG, 0x07); break; } // 无论进哪个状态,先读回确认 verify_power_state(); }

4. 实操记录:从寄存器到整机上电

4.1 先让芯片自己跑起来

我建议你第一次拿到板子,不要先连电池,不要跑 MCU 代码,只上外部 5V 输入。用示波器看 PCA9422 默认输出:上电瞬间每个 BUCK 是否有软启动斜坡、LDO 是否有输出、静态电流多少。这一步能确认焊接、电感、电容这三件套没有问题。

我当时在这步就抓到一个问题:有一颗 BUCK 的空载输出正常,一加负载就跌到一半,最后发现是电感虚焊。如果直接写代码跑系统,这个问题会被误判成寄存器配置错误,排查路径完全不同。所以"先硬件裸跑、再软件介入"的顺序,能帮你把故障面缩到最小。

4.2 用寄存器把电压调到目标值

PCA9422 的 BUCK 输出电压一般是一段范围,用寄存器里的电压档位字段选择,我手里这颗 BUCK1 可以在 0.6V 到 3.3V 之间按步进调。如果你想出 3.3V,就按数据手册给的编码表算一个值写进去,然后用万用表量出来的实际电压和设定值对一下。

实测下来,写寄存器后电压稳定时间大约在 1ms 到 2ms 之间,受输出电容影响。这里有一个技巧:先读完当前寄存器值,只修改电压位段,其他位保持不变,不要整字节写死,否则可能把软启动时间、限流配置这类藏在同一寄存器里的位给覆盖掉。

// 只更新 BUCK1 电压位段的示例 uint8_t tmp = pca9422_read_reg(PCA9422_BUCK1_CFG); tmp &= 0x0F; // 保留低 4 位原有配置 tmp |= (0x2A & 0xF0); // 写入新的电压编码高 4 位 pca9422_write_reg(PCA9422_BUCK1_CFG, tmp);

4.3 充电路径验证

接上电池后,先用可调电源限流供电,观察充电电流是否按寄存器设定值走。线性充电的发热主要集中在 PMIC 封装上,充电 300mA 时我实测外壳温度比环境高十几度,这是正常范围,如果发现烫到不能摸,就检查电池是否接反或者充电电流配置是不是写错档了。

路径管理验证要做的动作是:外部 5V 插入瞬间,看系统总线上电压有没有跌落,波形应该是平的一条线,才说明路径切换没有问题。再快速拔掉外部电源,看系统瞬间切到电池供电,同样不应有电压中断。这个切换过程用示波器看触发沿,比用万用表靠谱得多。

4.4 整机功耗实测

系统功耗验证我按三档测:运行态、睡眠态、深度睡眠态。用精密电流表串在电池路径上,测整套系统的消耗,注意 PMIC 内部还有它的静态电流,这部分是省不掉的,选型时就要对数据手册里的 Iq 心里有数。

状态实测电流说明
运行态(蓝牙扫描+传感器采集)58mA峰值会到 130mA,均值稳定
睡眠态(BUCK1 降载,BUCK2 关)1.2mA主要消耗来自 LDO 常电轨和 MCU 掉电模式
深度睡眠(仅 RTC 唤醒)42uAPMIC 静态电流加 MCU 最小功耗

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

5.1 I2C 总线被拉死

最经典的现象就是 MCU 跑起来后,所有 I2C 操作都超时,示波器看 SDA 一直被拉低。原因通常是某个从机进入了错误状态,或者总线没有释放。处理方法:首先把所有从机的 SDA 脚置为高阻,看谁在拉低;其次检查上电顺序,很多 PMIC 要求输入电源先于 I2C 通信稳定,如果 MCU 起步比 PMIC 快,第一次通信就会撞上 PMIC 还在初始化。

我后来加了这么一段处理:MCU 上电后先延时 10ms 再初始化 I2C,然后先读一次 ID,失败的话再延时重试三次。这个方法简单粗暴,但排掉了很多冷启动的怪问题。

5.2 输出电压比设定值低

遇到这种问题,先排除负载过流和电感问题,再看寄存器是不是没写进去。我踩过一次低级坑:读回寄存器发现值是对的,但输出电压就是不对,后来发现 PMIC 上电时被 MCU 的程序干扰,配置写了一半 MCU 崩了,之后 PMIC 停在中间态。解决办法是配置写完一定要读回校验,校验失败就整片重新初始化。

5.3 BUCK 空载输出跳变

PMIC 的 DC-DC 轻载时通常进入 PFM 模式,输出会有小幅纹波,这不一定是故障。但如果跳变幅度超过 50mV,先查输出电容够不够,再查反馈路径有没有被干扰。还有一点,示波器探头接地线太长也会看到一堆假纹波,探头接地要挂在芯片附近的 GND 上,别夹在电源输入那边的地线上。

5.4 充电永远不结束

充电一直指示进行中,先看状态寄存器里的电流是不是已经低于终止电流。如果 PMIC 的终止电流设置得比系统负载还小,或者电池一直挂着负载在充,它确实永远觉得还没充满。这也是我把系统负载和充电路径分开设计的另一个原因:要确保充电末段电流能降下来,否则会一直充到过压保护,电池寿命会受影响。

6. 布局与测试的小经验

6.1 PCB 布局要点

PCA9422 的 BUCK 部分开关频率不低,SW 节点的 di/dt 很大。布局上,电感要尽量靠近 SW 脚,输入电容和输出电容都要紧挨着芯片对应引脚,形成小的回路面积。最忌讳的是把 I2C 的 SCL/SDA 走线平行放在电感底下,我第一版板子就是这么干的,结果通信偶尔出错,加屏蔽罩才好,其实重新布线就能解决。

PMIC 的散热焊盘一定要接到足够大的地铜皮上,充电时候的热量主要靠它走。过孔别舍不得打,焊盘底下打一整排过孔到背面地层,实测外壳温度能低好几度。

6.2 调试工具怎么配

最低配置是一台带深存储的示波器和一个可调直流电源。示波器两通道就够了:一路测系统电压,一路测 SW 节点或者 I2C 波形。有条件再配一个电子负载,测 BUCK 恒流能力会方便很多。电流波形不要用万用表,测动态功耗变化要用电流探头或者低边采样电阻转差分。

我个人在调试阶段会在电池正极串一个 10mΩ 采样电阻,用示波器直接看整机电流的实时波形。这个视角比看电压有用多了,睡眠唤醒瞬间的电流尖峰、PMIC 路径切换时的小毛刺,都能一眼看到。

最后再分享一个习惯:每调好一版参数,就把寄存器清单导出来存成一份文本,跟固件版本一起打标签。PMIC 这类可编程电源,最怕的其实就是"这版参数到底设了啥"说不清楚。有了寄存器快照,板子换一批、固件升级一次,对照起来就非常快。

这套 PCA9422 加 PIC18F87J50 的组合,现在已经成为我做便携设备电源方案的默认起点。你可以先用它把产品跑起来,后面再根据实际功耗数据决定要不要换更省的 PMIC 或者把某一路改成 MCU 自己的休眠电源策略。可编程电源的好处就是,硬件一旦定型,后面所有优化都还在软件能触及的范围里。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/24 3:22:14

便携浏览器安全测试工作台:Profile隔离、多开与证据归档

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/10/24 3:22:08

偏振图像去噪:保偏振角精度与斯托克斯分量的物理约束方法

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/10/24 3:20:09

主程序/子程序架构:2025年仍被低估的稳定架构风格

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/10/24 3:20:07

WPS JS宏批量合并Excel文件:跨工作簿数据汇总实战方案

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/10/24 3:19:45

p5.js粒子系统:从Mover到Particle的生命周期建模

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/10/24 3:19:12

宝塔插件开发全流程:从工程结构到上架避坑指南

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华