做带电池的设备,真正的难点不在“把电供上”,而在没电的时候怎么守住底线、来电的时候怎么安全补电、日常运行当中怎么把每一口电的状态看清楚。我最近完成的一版项目里,主控用了自带网络接口的 PIC18F97J60,电源部分交给 PCA9422,两个芯片通过 I2C 协作,做了一套从充电、放电、路径切换到远程状态上报的完整电源管理方案。整体跑下来,这套组合在中小型便携设备上相当顺手。这篇就把选型思路、硬件连接、软件流程、调试踩坑完整梳理一遍,给正在做电池供电设备或想给产品加远程电源监测的同行做个参考。
1. 为什么是PCA9422加PIC18F97J60这套组合
1.1 先把“完整电源管理”拆开看
很多项目一开始对“电源管理”的认知就是稳压加保护,真正上手才发现,一个完整的电源管理子系统至少要覆盖五件事:一是锂电池的充电过程控制,恒流恒压怎么切换、什么时候终止;二是外部电源和电池之间的路径切换,拔掉适配器不能导致系统复位;三是电池状态监测,电压、电流、温度要有办法读出来;四是异常保护,过压、欠压、过温至少要有关断通道;五是和主控的低功耗策略联动,休眠时谁能唤醒、唤醒后怎么快速上报。
我这次的项目是一个需要断电备电、又需要联网上报状态的小型采集节点,外接电源断开以后要靠电池继续跑一段时间,同时要把电源状态通过以太网发到后台。所以方案里必须有一个懂充电的 PMIC,还要有一个自带网络能力的主控芯片,最好主控和 PMIC 之间还能快速通信。这样一来,选型方向就非常明确。
1.2 PCA9422在系统里扛下电源管理的活
PCA9422 是专为便携设备设计的电源管理芯片,把线性充电管理、电源路径切换、系统电压输出、ADC 采集和中断状态输出集成在一起。相比用“充电IC + 电量计 + 保护IC”拼起来的方案,它的集成度更高,PCB 面积和物料种类都能压下来。
它在系统里承担的角色可以概括成几个层面。充电层面,它负责把外部输入的电源按照设定的电压电流对锂电池充电,支持恒流/恒压/终止这几个标准阶段。路径层面,当外部电源存在时,它优先用外部电源给系统供电,同时给电池充电;外部电源断掉以后自动切到电池路径,切换过程基本是硬件自动完成,不需要主控操心。监测层面,它内部的 ADC 可以采样电池电压、充放电电流和温度相关信号,主控只要通过 I2C 读寄存器就能拿到数据。安全层面,它具备过压、过流、过温等保护功能,并且能通过中断引脚主动通知主控。这几件事凑齐,主控端的软件压力就小很多。
1.3 PIC18F97J60解决“主控+联网”两个问题
PIC18F97J60 这类的芯片有个很实用的特性:内部直接集成了以太网 MAC 和 PHY,两颗网络变压器加一个 RJ45 就能把设备接入以太网,不用再外挂单独的网络控制器。对需要联网上传数据的设备来说,这能省掉一大块硬件设计和驱动开发工作。
主控端的工作分成三块。第一块是应用逻辑,比如周期采集传感器、维护设备状态;第二块是网络协议,跑 TCP/IP 协议栈,提供 HTTP 状态页面或者主动向服务器上报数据;第三块是电源管理协作,通过 I2C 总线去配置 PCA9422、读取电源状态、响应中断事件。三块任务在同一个芯片上跑,代码组织上做好模块划分,优先级控制好,资源完全够用。
如果选型时非要拆开做,也不是不行,但就会变成“MCU + 外部以太网控制器 + 独立充电IC + 独立电量计”的架构,BOM 成本、PCB 面积、软件接口数量都会明显上升。对于中小批量产品来说,这套组合的意义就在于用更少的芯片完成同样的事。
2. 电源架构设计与硬件连接细节
2.1 整体电源拓扑
先画一下我这版方案的电源流。外部 USB 或适配器电源接入 PCA9422 的输入脚,锂电池接在电池接口上,PCA9422 的输出端是系统电源轨,用来给整个设备供电。PIC18F97J60 和其他外围器件不直接接外部电源,而是接在 PCA9422 管理的系统电源轨以及它输出的 LDO 上。
这个拓扑最大的好处是路径切换顺序完全由 PCA9422 控制。外部电源在的时候,VA 走高,系统轨由外电支撑,电池处于充电状态;外部电源拿掉的瞬间,内部开关自动把电池接到系统轨。主控在这个过程中要做的只是在中断引脚收到事件以后读取状态寄存器,确认当前供电来源,然后根据策略决定是否进入低功耗模式或者上报事件。
PIC18F97J60 和 PCA9422 之间则通过 I2C 总线连接,其中 SCL、SDA 分别接到主控的 MSSP 模块引脚,PCA9422 的中断输出接到主控的外部中断引脚,这样充电完成、欠压告警、温度异常这类事件可以第一时间唤醒或打断主控。文字示意如下:
VBUS/适配器 ---> PCA9422 输入 锂电池 ---> PCA9422 电池接口 PCA9422 系统电源轨 ---> 外部负载/传感器 PCA9422 LDO输出 ---> PIC18F97J60 供电 PCA9422 INT ---> PIC18F97J60 RB0外部中断 PCA9422 SCL/SDA ---> PIC18F97J60 MSSP I2C2.2 关键引脚连接和注意事项
硬件上最容易出问题的几个点,我逐个说一下。
I2C 总线部分是首要关注对象。SCL 和 SDA 都要接上拉电阻,阻值在 1k 到 4.7k 之间,具体取值要看总线上的器件数量和通信速率。如果板上 PCA9422 是 3.3V 电平,上拉电源就接到 3.3V,不要图省事直接拉到系统主电源轨,避免电平不匹配。PCA9422 的 I2C 地址通常由地址引脚决定,画板之前先确认地址引脚接法,并在代码里用宏定义把地址封起来,后续改板只改一处。
中断引脚不能悬空,必须接上拉电阻到对应电平。这个引脚在主控侧配置成下降沿或低电平触发的外部中断。我第一次画板就吃过亏,中断脚忘记加上拉,结果 PMIC 没告警时引脚电平不定,主控频繁进中断,整机功耗下不来。后来接了 10k 上拉,问题立刻消失。
电池接口部分要加保护。电池正极进 PCA9422 之前串一个 PTC 自恢复保险丝,同时在靠近芯片的电池引脚处放 1uF 和 0.1uF 陶瓷电容。如果电池自带保护板,也要确认保护板参数和 PCA9422 的充放电截止电压兼容,避免两边保护逻辑打架。
系统电源轨的滤波电容不能省。路径切换瞬间,也就是外部电源断开而电池接管的那段时间,系统轨上会有一个短暂压降,如果电容太小可能导致主控复位。我最终在系统电源轨上放了一颗 47uF 陶瓷电容加三颗 0.1uF 高频去耦电容,实测切换过程压降控制在可接受范围内。温度相关引脚方面,如果用了 NTC 测温,注意 NTC 分压电阻的精度,它直接影响温度保护阈值。
2.3 充电参数的计算逻辑
充电参数不能照着参考设计直接抄,要按实际电池和设备功耗算一遍。
充电电流的选择,一看外部电源能提供多少电流,二看板子散热条件。以标准 USB 接口 5V 供电为例,可用电流约 500mA,线性充电方式下,电池电压接近 4.2V 时,充电管上压差接近 0.8V,按 400mA 充电电流算,热功耗有 0.32W 左右;刚开始充电时电池电压低,压差更大,如果充电电流设置过高,芯片表面温度会非常可观。所以我在这个项目里把充电电流设置为 400mA,留出 100mA 余量给系统本身运行。如果你的设备自身负载就到了 200mA,那充电电流还要往下压,或者考虑外部电源能力更强的适配器。
终止电流一般取充电电流的 10% 左右,也就是 40mA。这个值不能设得太小,否则充电完成后会反复进入补电状态,既影响电池寿命,又会让状态寄存器在两个值之间频繁跳动。
还有一点容易被忽视:充电电压必须和电池规格对应。标称 4.2V 的单节锂电池,充电截止电压设为 4.2V 问题不大;如果用的是 4.35V 的高压电池平台,就要在 PMIC 配置里同步调整,否则电池永远只能充到九成。
3. 固件实现:让PCA9422和PIC真正协作
3.1 软件分层和任务划分
固件结构我按三层来组织,避免所有逻辑堆在主循环里,后期没法维护。
底层是驱动层,包括 I2C 总线驱动、PCA9422 寄存器读写驱动、以太网相关驱动和协议栈接口。中间层是电源管理服务,负责初始化 PMIC、周期性读取状态、处理中断事件、维护当前供电状态和电量估算结果。应用层是设备主逻辑,比如采集业务数据、根据电源状态切换工作模式、通过 HTTP 或主动上报的方式把电源信息发到后台。
三层之间用结构体和状态量解耦。底层驱动只管寄存器读写,不关心数据含义;中间层把寄存器原始值转换成“当前是外部供电还是电池供电”“充电处于哪个阶段”“电池电压多少毫伏”这类语义;应用层只消费中间层提供的结果。这样调试的时候每一层都能单独验证,出问题也容易定位。
3.2 I2C底层驱动实现
PIC18F97J60 的 MSSP 模块可以配置成 I2C 主模式。初始化的时候按 400k 时钟速率计算分频系数,同时把 SCL 和 SDA 引脚设为输入模式,由引脚自身带上拉作用配合外部上拉电阻工作。
#define PCA9422_I2C_ADDR 0x0E // 根据地址引脚配置查手册确认 void i2c_init(void) { TRISC3 = 1; // SCL TRISC4 = 1; // SDA SSPCON1 = 0x28; // I2C 主模式使能 SSPADD = (_XTAL_FREQ / (4 * 400000)) - 1; // 400kHz SSPCON2 = 0x00; } static void i2c_wait_ack(void) { while ((SSPCON2 & 0x1F) || (SSPSTAT & 0x04)); } uint8_t pmic_write_reg(uint8_t reg, uint8_t val) { i2c_start(); i2c_write(PCA9422_I2C_ADDR << 1); // 写地址 if (i2c_ack_status() == I2C_NACK) { i2c_stop(); return PMIC_ERR; } i2c_write(reg); if (i2c_ack_status() == I2C_NACK) { i2c_stop(); return PMIC_ERR; } i2c_write(val); if (i2c_ack_status() == I2C_NACK) { i2c_stop(); return PMIC_ERR; } i2c_stop(); return PMIC_OK; }I2C 驱动里最重要的不是那个正常收发逻辑,而是超时处理。如果总线上某个设备拉低了 SCL,主控可能一直死等。所以我每步操作都加了超时计数,超时后执行总线恢复流程,把 SCL 翻转九次再重新发起通信。这在实际调试中救过我好几次,尤其是在带电插拔的情况下,总线偶尔会进入异常状态。
3.3 PCA9422初始化流程
初始化顺序很关键。上电以后,先读 PMIC 的版本寄存器,确认通信正常;然后依次配置充电电压、充电电流、终止电流和使能位;接着配置 ADC 使能,把电压、电流采样通道打开;再配置中断使能,让需要关注的事件能通过 INT 引脚上报;最后清一次中断状态寄存器,把上电初期可能产生的假中断清掉。
我习惯把初始化参数做成一张表,方便后期调整:
static const struct { uint8_t reg; uint8_t val; } pmic_init_table[] = { { REG_CHG_CTRL, 0x50 }, // 使能充电,充电截止电压4.2V { REG_CHG_CURRENT, 0x03 }, // 充电电流400mA { REG_TERM_CURRENT, 0x01 }, // 终止电流设定 { REG_ADC_CTRL, 0x01 }, // 使能ADC采样 { REG_INT_ENABLE, 0x07 }, // 使能充电状态/欠压/过温中断 { REG_INT_STATUS, 0xFF }, // 清中断标志 };注意寄存器名和具体值要以你手里那版 PCA9422 数据手册为准。不同版本可能寄存器位定义有差异。初始化完成后不要立刻就认为配置写入成功,我习惯读回关键寄存器跟写入值比对,确认没有 I2C 写入错误。这一步发现问题越早,后面排查成本越低。
3.4 运行时的状态采集与电量估算
设备运行中,电源管理服务按固定周期读一次 PCA9422 状态。读的内容包括充电状态寄存器,判断当前是充电中、充电完成、还是处于无外部电源的放电状态;读 ADC 电压通道,得到电池电压原始值;读电流通道,得到充放电电流;如果有温度通道,一并读取换算成温度值。
原始 ADC 值不能直接用。我做了两层处理:第一层是滑动平均滤波,把最近四到八次采样结果做平均,去掉纹波和单次毛刺;第二层是换算校准,根据数据手册里的 ADC 最小分辨率,把原始值换算成毫伏和毫安。这两步做完以后的数据才能送进电量估算模块。
电量估算我用的是电压查表法,精度要求不高的情况下完全够用。先把不同电压点对应的电量百分比做成表格,运行时用插值算出当前 SOC。要注意的是,这个查表必须区分带载状态和空载状态。电池在放电时电压会偏低,在充电时会偏高,我用电流方向作为查表表的切换依据,放电过程用放电核对表,充电过程用充电核对表。实测下来在 20% 到 90% 这个区间误差能控制在小范围内,低于 20% 或高于 90% 误差会变大,那种区间本身对显示精度要求也不高。如果产品需要高精度电量,那就得在 PCA9422 当前方案基础上再加库仑计,或者利用 PMIC 空闲时间做开路电压校准,不过在大多数上报场景里,电压查表法已经够用。
3.5 以太网上报和低功耗状态联动
PIC18F97J60 跑 TCP/IP 协议栈以后,上报电源状态很简单。我在设备内部维护了一个电源状态页面,后台通过 HTTP 请求拉到 JSON 格式的数据,内容包括当前供电来源、电池电压、充放电电流、电量百分比、充电状态和温度告警状态。定期上报的话就由主控主动发起 HTTP 请求,把同样内容 POST 到后台接口。
需要特别处理的是低功耗联动。设备在电池供电状态下,如果长时间没有网络活动,我会让 PIC18F97J60 进入休眠模式,以太网模块也一并停掉,只保留 PCA9422 的 INT 中断唤醒能力。充电事件、低电压告警、温度异常都能通过中断拉起来;网络侧的唤醒就只能在有数据需要上报时由主控定时醒来,或者手动触发网络服务。实测下来,休眠时整板功耗比正常运行降了一个数量级以上,这对电池供电设备非常关键。为了不让网络重连过程浪费太多电量,我在休眠策略里加了阶梯退避:第一次唤醒后网络重连失败就延迟十分钟再试,连续失败就逐步拉长间隔。
4. 调试实录:常见问题和排查技巧
4.1 I2C通信时好时坏
这个坑在原型阶段最常出现。现象是 PCA9422 偶尔读不到数据,重新上电又恢复正常。用逻辑分析仪看波形,发现 SDA 低电平在部分波形上不够干净,毛刺明显。排查了一圈,最终锁定是上拉电阻阻值偏大加上总线电容偏高,信号上升沿太缓。把上拉电阻从 4.7k 换成 2.2k,并尽量缩短 SCL 和 SDA 走线长度后,问题消失。另外一个隐蔽因素是 PCA9422 的供电轨和主控侧的 I2C 上拉电源不是同一个电源域,导致电平参考不一致,后来统一到同一个 LDO 输出后彻底稳定。
如果遇到 I2C 卡死,不要急着复位整板,先看 SCL 是否被拉低。总线被拉死时执行 SCL 翻转九次的恢复操作,多数情况下能救回来。这套恢复逻辑建议直接写进驱动里作为标准流程。
4.2 路径切换瞬间系统电压跌落
现象是外部电源拔掉的瞬间,设备偶发复位,重新上电又正常。抓系统电源轨波形,看到切换瞬间有一个明显下凹。原因是系统轨电容偏小,而外部负载里有传感器启动瞬间电流冲得比较高。
解决方法是先量出切换压降的持续时间和负载电流峰值,再按“最大负载电流乘以持续时间除以允许压降”的公式去算最低电容需求。我在原有电容基础上增加了一颗 47uF 陶瓷电容,并把电容靠近 PCA9422 输出引脚放置,压降从接近 400mV 降到了 100mV 以内。这类问题一定要用示波器抓波形确认,靠猜很难定位。
4.3 电量百分比乱跳
电量显示在一个充电周期里能从 90% 掉到 50% 再弹回 80%,看起来毫无规律。加滤波以后好了一点,但跳变仍然存在。仔细对比发现,跳变发生的时刻都出现在充电阶段切换附近。恒流阶段电池电压偏低,恒压阶段电压接近满电,同样是 90% 的状态电压可能相差很大,直接查表自然跳动。
我把策略改成分区更新:只有电量变化超过一定比例才刷新显示,同时对充电状态切换后的第一次采样做特殊处理,强制等采样稳定后再更新。这样跳变被压制住,显示平滑很多。如果做的是实际产品,建议再加上时间维度的平滑,比如每秒最多更新一个百分点,防止界面乱闪。
4.4 充电提前终止
表现是电池还没充满,PCA9422 就报告充电完成。检查充电参数发现终止电流阈值写的是 0x00 对应的最小档位,但实际系统负载在充电时也存在,这部分负载电流叠加在电池充电电流上,让检测电路提前满足终止条件。
解决思路有两条:一是把终止电流阈值调高到能排除系统底噪的水平;二是在充电检测期间尽量关掉不必要的外设,减小系统负载波动。两者结合以后,充电完成判断就正常了。这里也提醒一句:终止电流不是设得越低越准,跟实际板子的底噪匹配才是关键。
4.5 以太网模块工作时ADC读数明显偏移
网络收发活跃的时候,PCA9422 的 ADC 电压读数出现规律性小跳变。用示波器看电源轨,发现网络变压器附近的电磁干扰耦合到了 PMIC 的采样通道。之前把模拟信号走线和以太网走线放在相邻层,间距不够。
处理起来主要是布局调整:把 PMIC 的 ADC 采样线远离网口区域,同时在采样引脚前增加一级 RC 滤波。软件侧把采样时刻避开网络收发高峰期,比如在以太网帧间隙或者主控本地处理阶段采样。调整后读数稳定很多。这类干扰问题,硬件布局是根因,软件滤波只是缓解手段,根因不除早晚还会出别的怪问题。
5. 完整参考配置与验证流程
5.1 PMIC配置速查表
把调试收敛后的关键配置整理成表,方便其他项目直接对照参考:
| 配置项 | 目标值 | 说明 |
|---|---|---|
| 充电截止电压 | 4.2V | 按电池规格选择 |
| 充电电流 | 400mA | 受外部电源能力与散热限制 |
| 终止电流 | 约充电电流的10% | 需要匹配系统底噪 |
| ADC采样 | 使能电压/电流通道 | 温度通道按需使能 |
| 中断事件 | 充电状态变化、欠压、过温 | INT脚上拉后接入外部中断 |
| I2C速率 | 400kHz | 不稳定时降到100kHz排查 |
这个表只是参考基线,落到具体产品上要做两件事:一是和 PCA9422 的实际数据手册逐位核对;二是用自己手上的电池实测整条充电曲线,确认每个阶段都符合预期。
5.2 关键物料与替代选型
整套方案的关键物料并不复杂:PCA9422 PMIC 一颗,锂电池一颗,PTC 保护器件一颗,I2C 上拉电阻两颗,中断脚上拉电阻一颗,系统轨电容器若干,以太网变压器一颗,RJ45 座子一个。PIC18F97J60 之外的辅助器件只需要晶振、去耦电容和必要的旁路元件。
如果手头的项目对充电电流要求更大,或者需要支持快充协议,那 PCA9422 的方案可能不够,得换支持开关充电的 PMIC;反过来,如果只要做一个简单的电池备电小板,也可以把 PIC18F97J60 换成一个更便宜的普通 MCU,放弃以太网上报能力。这套组合的意义在于它把校园、研发、小批量三个阶段都能覆盖的中间形态给到了你:功能完整,调试成本在可控范围内。
5.3 验证流程和测试工具
完整验证流程我按照从易到难的顺序走,每一步通过再进下一步。
第一步是裸板电源确认。不上电池,用稳压电源给 PCA9422 输入脚供 5V,确认系统轨输出正常,然后用 I2C 工具读版本寄存器,确认通信链路通。
第二步是电池充放电测试。装上电池,跑一个完整充电周期,用示波器和万用表记录电池电压曲线,确认恒流、恒压、终止三个阶段的切换点符合预期。放电测试就通过电子负载设置不同电流档位,观察系统电源轨是否稳定、欠压告警是否按配置触发。
第三步是路径切换测试。在带载状态下反复插拔 USB 电源,每次用示波器抓系统轨电压,确认压降处于可接受范围。这一步至少连续做五十次,不能只看一两次。
第四步是网络功能测试。让设备接入局域网,访问 HTTP 状态页面,确认电源数据能正确显示;再配置主动上报,确认后台能收到数据。测试过程要配合网络抓包,检查是否有 TCP 重传过多的问题。
第五步是低功耗测试。让设备进入休眠状态,测量整板电流,确认达到预期指标;再模拟充电事件和欠压中断,确认设备能从休眠中唤醒并完成一次有效上报。
一点个人经验收尾
这套方案做下来,我最深的体会是:电源管理这种模块,硬件上把拓扑理顺了,软件上把状态机跑顺了,后面基本就是查表和收中断的活。真正让人头疼的往往不是 PCA9422 或 PIC18F97J60 本身,而是两个芯片之间那条 I2C 总线、那根中断线,以及系统轨上那几颗电容。做这类项目,建议一开始就把数据手册里的中断状态、ADC 换算关系、地址引脚配置这些细节点全部核对清楚,别等到板子贴完再翻手册。如果后续要做量产版本,还可以在这个基础上把电量估算换成带库仑计的方案,或者引入远程固件升级功能,让节点设备在电池供电场景下活得更有底气。