ARTICLE · INTELLIGENCE

战地情报 · 详情页

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

基于PCA9422与PIC18F85J10的便携设备电源管理方案

基于PCA9422与PIC18F85J10的便携设备电源管理方案

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

嵌入式设备做多了,你会发现一个规律:功能越复杂的产品,电源管理越不能凑合。我手头这个项目是一个便携式数据采集终端,主控用的是 PIC18F85J10,外设包括一颗 128x64 的 LCD、一个 SD 卡座、若干模拟传感器接口,还有一路用于调试的串口。整机靠一块单节锂离子电池供电,用户期望的续航是连续工作 8 小时以上,待机状态下能撑过一周。

最早我图省事,直接用了一颗 LDO 加一颗简单的充电 IC,结果实测下来问题一大堆:电池充到 4.1V 之后充电电流迟迟降不下来,芯片发热明显;系统在 LCD 背光开启的瞬间,3.3V 轨上会出现接近 200mV 的跌落,导致主控偶尔复位;待机功耗更是惨不忍睹,整机静态电流接近 8mA,电池放两天就空了。痛定思痛,我决定把电源部分彻底重做,选型上锁定了 PCA9422 这颗电源管理芯片,配合 PIC18F85J10 的片上外设做完整的电源管理方案。

PCA9422 是一颗面向低功耗应用的电源管理 IC,内部集成了线性充电器、多路可配置的稳压输出、电量计接口以及 I2C 通信接口。它最大的好处是把充电、放电、电压转换、电量监测这几件事集成到了一颗芯片里,外围元件少,而且所有关键参数都能通过 I2C 动态调整。PIC18F85J10 这边则负责跟 PCA9422 通信、读取状态、根据系统负载动态切换电源模式,同时利用自身的低功耗特性配合整体功耗控制。

这套方案的核心思路可以概括成一句话:让电源管理芯片做它擅长的事,让主控做决策,两者通过 I2C 协同工作。PCA9422 负责硬件层面的充电控制、电压转换和电流监测,PIC18F85J10 负责软件层面的状态机管理、模式切换和异常处理。这样分工的好处是,主控不需要频繁轮询电源状态,大部分时间可以待在休眠模式里,只在 PCA9422 产生中断时才醒来处理。

为什么不用分立方案?我算过一笔账:如果用独立的充电 IC、DC-DC、LDO 和电量计,光是外围元件就多了十几个,PCB 面积至少增加 30%,而且各芯片之间的时序配合、上电顺序、故障联动都要自己写逻辑,调试成本极高。PCA9422 把这些东西集成到一起,虽然单颗芯片价格贵一些,但整体 BOM 成本和开发时间反而更划算。

提示:选型时不要只看芯片单价,要把外围元件、PCB 面积、调试工时都算进去。集成方案在中小批量项目里往往更划算。

2. PCA9422 核心特性与硬件设计要点

2.1 芯片内部结构与关键参数

PCA9422 的内部结构可以分成四个主要模块:线性充电器、升压/降压转换器、LDO 输出、以及 I2C 控制与监测单元。充电器部分支持单节锂离子电池的恒流恒压充电,充电电流可以通过寄存器在 10mA 到 500mA 之间调节,恒压值默认 4.2V,也能配置成 4.35V 或 4.4V 以适配高压电芯。升压转换器负责在电池电压较低时把系统电压抬到 3.3V 或 5V,降压转换器则在电池电压较高时把电压降下来,两者配合实现宽输入范围的稳定输出。

LDO 部分提供了一路低噪声输出,专门给模拟传感器和射频模块供电。I2C 接口支持标准模式(100kHz)和快速模式(400kHz),地址可以通过 ADDR 引脚配置成两个可选值之一。芯片还内置了一个 10 位 ADC,用来监测电池电压、充电电流和芯片温度,这些数据都能通过 I2C 读出来。

关键参数我整理了一张表,方便选型时对照:

参数项典型值可配置范围说明
充电电流100mA10mA ~ 500mA步进 10mA
充电恒压4.2V4.2V / 4.35V / 4.4V根据电芯规格选择
升压输出3.3V3.0V ~ 5.0V效率约 85%
降压输出3.3V1.8V ~ 3.3V效率约 90%
LDO 输出2.8V1.8V ~ 3.3V噪声低于 30uVrms
静态电流15uA—待机模式下
I2C 速率400kHz100kHz / 400kHz快速模式

2.2 外围电路设计与布局注意事项

PCA9422 的外围电路不算复杂,但有几个地方特别容易踩坑。电感的选择是第一大坑。升压和降压转换器都需要外接电感,数据手册推荐值是 2.2uH,但实际选型时要注意电感的饱和电流必须大于峰值开关电流。我一开始用了一颗饱和电流只有 500mA 的小电感,结果系统在大负载时升压输出直接掉到 2.8V,主控反复复位。后来换成饱和电流 1.5A 的屏蔽电感,问题立刻消失。

输入输出电容的布局是第二大坑。PCA9422 的开关频率在 1MHz 左右,高频环路面积必须尽可能小。我的做法是把输入电容和输出电容都放在芯片同一面,距离引脚不超过 3mm,接地端直接打过孔到地平面。这样做的原因是高频开关电流的回路面积越小,辐射和传导干扰就越低,输出电压纹波也更小。

I2C 上拉电阻的选择也有讲究。400kHz 快速模式下,上拉电阻太大会导致上升沿变缓,通信失败;太小则增加静态功耗。我实测下来,2.2k 到 4.7k 之间比较合适,具体值取决于总线电容。如果总线上挂了多个从机,建议用 2.2k;只有 PCA9422 一个从机时,4.7k 就够了。

注意:PCA9422 的 ADDR 引脚不能悬空,必须明确接高或接低,否则 I2C 地址不确定,通信会时好时坏。

2.3 与 PIC18F85J10 的硬件连接

PIC18F85J10 这边,我用的是 MSSP 模块的 I2C 模式,SCL 和 SDA 分别接到 RC3 和 RC4。PCA9422 的中断输出引脚接到 RB0,配置成外部中断输入。这样当充电完成、电池电压过低、或者芯片温度过高时,PCA9422 会拉低中断线,PIC18F85J10 从休眠中醒来处理。

电源轨的连接上,PCA9422 的升压输出给 LCD 背光和 SD 卡供电,降压输出给主控和数字外设供电,LDO 输出给模拟传感器供电。这样分轨供电的好处是可以单独控制每一路的开关,比如待机时关掉背光和 SD 卡,只保留主控和 LDO,功耗能降到 1mA 以下。

3. 软件架构与 I2C 通信实现

3.1 PIC18F85J10 的 I2C 驱动编写

PIC18F85J10 的 MSSP 模块用起来不算复杂,但有几个寄存器配置容易搞混。我先把关键配置列出来:

// I2C 初始化,400kHz @ 16MHz Fosc SSPCON1 = 0x28; // SSPEN=1, I2C 主模式 SSPCON2 = 0x00; SSPADD = 0x27; // 时钟分频,计算公式见下文 SSPSTAT = 0x80; // slew rate 控制关闭 TRISC3 = 1; // SCL 输入 TRISC4 = 1; // SDA 输入

SSPADD 的计算公式是:SSPADD = (Fosc / (4 * Fscl)) - 1。以 16MHz 晶振、400kHz 总线速率为例,SSPADD = (16000000 / (4 * 400000)) - 1 = 10 - 1 = 9,也就是 0x09。等等,这里我写 0x27 是 39,对应的是 100kHz 的情况。实际项目中我用的是 100kHz,因为 PCA9422 的寄存器读写不频繁,100kHz 足够用,而且抗干扰能力更强。

写一个字节的流程是:启动条件 → 发送从机地址+写位 → 等待 ACK → 发送寄存器地址 → 等待 ACK → 发送数据 → 等待 ACK → 停止条件。读一个字节则是:启动 → 发送从机地址+写位 → 等待 ACK → 发送寄存器地址 → 等待 ACK → 重启 → 发送从机地址+读位 → 等待 ACK → 读取数据 → 发送 NACK → 停止。

unsigned char I2C_WriteByte(unsigned char addr, unsigned char reg, unsigned char data) { I2C_Start(); if (I2C_Send(addr << 1)) return 1; // 写地址 if (I2C_Send(reg)) return 2; // 寄存器地址 if (I2C_Send(data)) return 3; // 数据 I2C_Stop(); return 0; } unsigned char I2C_ReadByte(unsigned char addr, unsigned char reg, unsigned char *buf) { I2C_Start(); if (I2C_Send(addr << 1)) return 1; if (I2C_Send(reg)) return 2; I2C_Restart(); if (I2C_Send((addr << 1) | 1)) return 3; *buf = I2C_Receive(); I2C_SendNACK(); I2C_Stop(); return 0; }

每个 I2C 操作都要加超时机制,否则总线被拉死的时候主控会卡在里面出不来。我的做法是在等待 ACK 的循环里加一个计数器,超过 1000 次就强制退出并返回错误码。

3.2 PCA9422 寄存器配置与初始化流程

PCA9422 的寄存器不算多,但每个位的含义要搞清楚。我挑几个关键的说明一下。充电控制寄存器里,bit7 是充电使能,bit6 到 bit0 是充电电流设置,步进 10mA。比如要设 200mA,就写0x80 | 20,也就是 0x94。输出电压寄存器里,bit5 到 bit3 控制升压输出电压,bit2 到 bit0 控制降压输出电压,具体对应关系要查数据手册的表格。

初始化流程我按这个顺序来:

  1. 上电后先延时 10ms,等 PCA9422 内部电路稳定。
  2. 读一次芯片 ID 寄存器,确认 I2C 通信正常。
  3. 配置充电电流为 200mA,恒压 4.2V。
  4. 配置升压输出 3.3V,降压输出 3.3V,LDO 输出 2.8V。
  5. 使能充电中断、电池低电压中断、过温中断。
  6. 清除所有中断标志,最后使能充电。
void PCA9422_Init(void) { Delay_ms(10); unsigned char id; if (I2C_ReadByte(PCA9422_ADDR, REG_CHIP_ID, &id)) { // 通信失败,进入错误处理 Error_Handler(ERR_I2C); } I2C_WriteByte(PCA9422_ADDR, REG_CHG_CTRL, 0x94); // 200mA 充电 I2C_WriteByte(PCA9422_ADDR, REG_VOUT_CTRL, 0x2A); // 3.3V/3.3V I2C_WriteByte(PCA9422_ADDR, REG_LDO_CTRL, 0x08); // 2.8V I2C_WriteByte(PCA9422_ADDR, REG_INT_MASK, 0x1C); // 使能三类中断 I2C_WriteByte(PCA9422_ADDR, REG_INT_FLAG, 0xFF); // 清标志 I2C_WriteByte(PCA9422_ADDR, REG_CHG_CTRL, 0x94); // 再次使能充电 }

提示:初始化时不要一次性把所有寄存器都写完,中间要留时间让芯片内部状态机切换。我试过连续写五个寄存器,结果最后一个经常写不进去,后来每个写操作之间加 1ms 延时就好了。

3.3 电源状态机设计与模式切换逻辑

整个系统的电源状态我分成了四种:充电模式、正常工作模式、低功耗模式、休眠模式。状态切换由 PIC18F85J10 根据 PCA9422 上报的电池电压、充电状态和系统负载来决定。

充电模式下,系统由外部电源供电,PCA9422 同时给电池充电。这时候主控可以全速运行,LCD 背光全开,SD 卡正常读写。当充电完成中断触发后,如果外部电源还在,系统保持正常工作模式;如果外部电源拔掉,系统根据电池电压决定进入低功耗还是休眠。

正常工作模式下,电池电压高于 3.5V,系统全功能运行。当电池电压降到 3.5V 以下时,主控主动关闭 LCD 背光和 SD 卡,进入低功耗模式。低功耗模式下,主控把系统时钟降到 4MHz,关闭不必要的外设,只保留 I2C 和中断。当电池电压降到 3.3V 以下时,系统进入休眠模式,主控进入深度睡眠,只靠 PCA9422 的中断唤醒。

void Power_StateMachine(void) { unsigned char vbat; I2C_ReadByte(PCA9422_ADDR, REG_VBAT_ADC, &vbat); float voltage = vbat * 6.0 / 255.0; // 分压比 1:6 switch (current_state) { case STATE_CHARGING: if (charge_done) current_state = STATE_NORMAL; break; case STATE_NORMAL: if (voltage < 3.5) current_state = STATE_LOW_POWER; break; case STATE_LOW_POWER: if (voltage < 3.3) current_state = STATE_SLEEP; else if (voltage > 3.6) current_state = STATE_NORMAL; break; case STATE_SLEEP: if (voltage > 3.4) current_state = STATE_LOW_POWER; break; } }

状态切换的时候要注意,从正常工作切到低功耗时,要先关外设再降时钟,顺序反了会导致外设状态异常。从低功耗切回正常时,要先升时钟再开外设,否则外设初始化会失败。

4. 充电管理与电池监测实操

4.1 充电电流与恒压参数的整定过程

充电参数的整定不能拍脑袋,要根据电芯规格来。我用的是一颗标称容量 2000mAh 的单节锂离子电芯,标准充电电流是 0.5C,也就是 1000mA,但 PCA9422 最大只能到 500mA,所以我就设了 500mA。等等,前面初始化里我写的是 200mA,这是因为实际测试中发现 500mA 充电时芯片温度上升太快,超过 60 度就触发了过温保护,充电断断续续。后来降到 200mA,温升控制在 20 度以内,虽然充电时间长了点,但稳定性好很多。

恒压值的选择也要看电芯。普通锂离子电芯是 4.2V,高压电芯是 4.35V 或 4.4V。我用的电芯规格书标的是 4.2V,所以寄存器就设 4.2V。这里有个细节:PCA9422 的恒压精度是 ±1%,也就是 4.158V 到 4.242V 之间,对于大多数应用足够了。如果对精度要求更高,可以在软件里读 ADC 值做二次校准。

充电截止电流我设的是 50mA,也就是 0.025C。这个值不能太小,否则充电时间会很长;也不能太大,否则电池充不满。实测下来,50mA 截止时电池电压在 4.18V 左右,拔掉充电器后静置半小时会降到 4.15V,属于正常范围。

4.2 电池电压 ADC 读取与电量估算

PCA9422 内部的 10 位 ADC 读出来的电池电压是经过分压的,分压比是 1:6。也就是说,ADC 读到的 1V 对应实际电池电压 6V。计算公式是:Vbat = ADC_Value * Vref / 1024 * 6。Vref 是内部参考电压,典型值 1.2V。代入进去,Vbat = ADC_Value * 1.2 / 1024 * 6 = ADC_Value * 0.00703。

电量估算我用的是最简单的电压查表法。锂离子电池的放电曲线不是线性的,4.2V 到 3.7V 之间电压下降很慢,3.7V 到 3.3V 之间下降很快。我实测了一组数据,整理成表格:

电池电压剩余电量系统状态
4.20V100%充电完成
4.00V80%正常工作
3.80V50%正常工作
3.60V20%低功耗
3.40V5%低功耗
3.30V0%休眠

这个表格是用实际电池放电测试出来的,不同电芯会有差异,但大致趋势一致。软件里用一个查表函数,根据 ADC 读到的电压值返回对应的电量百分比。

注意:ADC 读取时要连续读三次取平均值,单次读取的波动可能有 ±2 个 LSB,对应电压误差约 14mV,会影响电量判断。

4.3 充电异常处理与温度保护

充电过程中最常见的异常是过温和充电超时。PCA9422 内置了温度监测,当芯片温度超过 120 度时会自动停止充电,并在中断标志里置位。PIC18F85J10 检测到这个中断后,会记录一次过温事件,如果连续发生三次,就主动把充电电流降到 100mA,并点亮一个告警指示灯。

充电超时是另一个要处理的问题。正常情况下,2000mAh 电池用 200mA 充电,大概 10 小时充满。我在软件里设了一个 12 小时的超时计数器,如果超过 12 小时还没充满,就强制停止充电,并标记电池可能老化。这个计数器在每次充电开始时清零,充电完成后也清零。

还有一种异常是电池未连接。PCA9422 在充电时如果检测不到电池电压上升,会认为电池未连接,中断标志里会有相应位。这时候主控会关闭充电,并在 LCD 上显示提示信息。

5. 低功耗设计与实测数据

5.1 各模式下的功耗实测与优化

低功耗设计是这套方案的重点,我花了不少时间做实测和优化。先看一组实测数据:

工作模式主控时钟外设状态整机电流续航估算
正常工作16MHz全开45mA约 44 小时
低功耗4MHz背光关、SD 关8mA约 250 小时
休眠31kHz仅 I2C 和中断0.8mA约 2500 小时

正常工作模式下 45mA 的电流,主要消耗在 LCD 背光(约 20mA)、SD 卡(约 10mA)和主控本身(约 15mA)。低功耗模式下关掉背光和 SD 卡,主控降频到 4MHz,电流降到 8mA。休眠模式下主控进入深度睡眠,只有 PCA9422 的静态电流和主控的休眠电流,合计 0.8mA。

优化过程中发现一个细节:SD 卡座即使不读写,只要供电还在,就会消耗 2mA 左右的电流。所以低功耗模式下必须把 SD 卡的供电彻底切断,不能只靠软件禁用。我在 SD 卡供电轨上加了一个 MOS 管,由主控的一个 IO 控制,低功耗时直接关断。

另一个细节是LCD 背光的升压电路。背光 LED 需要 12V 左右,用的是升压驱动。这个驱动芯片即使背光调到最暗,静态电流也有 1mA。后来我换了一颗带使能引脚的驱动芯片,不用的时候直接关断,又省了 1mA。

5.2 休眠唤醒机制与中断处理

休眠唤醒靠的是 PCA9422 的中断输出。PIC18F85J10 配置成外部中断 INT0 下降沿触发,进入休眠前先清除中断标志,使能中断,然后执行SLEEP()指令。当 PCA9422 产生中断时,INT0 引脚被拉低,主控从休眠中醒来,进入中断服务程序。

中断服务程序里要做的事情很简单:先读 PCA9422 的中断标志寄存器,判断是哪种中断,然后设置一个全局变量,最后清除中断标志。具体的中断处理放在主循环里做,这样中断服务程序可以尽可能短,减少唤醒后的功耗。

void __interrupt() ISR(void) { if (INT0IF) { unsigned char flags; I2C_ReadByte(PCA9422_ADDR, REG_INT_FLAG, &flags); wakeup_reason = flags; I2C_WriteByte(PCA9422_ADDR, REG_INT_FLAG, 0xFF); INT0IF = 0; } }

唤醒后主控要根据wakeup_reason决定下一步动作。如果是充电完成中断,就更新 LCD 显示;如果是电池低电压中断,就切换到低功耗模式;如果是过温中断,就降低充电电流。

提示:休眠前一定要把 I2C 总线释放,SCL 和 SDA 都置为输入状态,否则 PCA9422 可能因为总线被拉低而无法产生中断。

5.3 实测续航与充电时间记录

最终实测下来,2000mAh 电池在正常工作模式下能撑 44 小时,低功耗模式下 250 小时,休眠模式下超过 100 天。充电时间方面,200mA 充电从 3.3V 到 4.2V 大约需要 9 小时,从 3.8V 到 4.2V 大约需要 4 小时。这个充电速度不算快,但对于数据采集终端这种不需要频繁充电的应用来说足够了。

充电过程中的温升我也记录了一下:环境温度 25 度时,充电电流 200mA,芯片表面温度稳定在 42 度左右,电池表面温度 35 度左右。这个温升在安全范围内,用户手感只是温热,不会觉得烫。

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

6.1 I2C 通信失败排查

I2C 通信失败是最常见的问题,我遇到过好几次。第一次是上拉电阻忘了焊,SCL 和 SDA 一直是低电平,主控根本发不出启动条件。第二次是 ADDR 引脚悬空,PCA9422 的地址不确定,有时候能通有时候不能通。第三次是总线电容太大,上升沿太缓,400kHz 下误码率很高,降到 100kHz 就正常了。

排查 I2C 问题的步骤我总结成了一张表:

现象可能原因排查方法解决方法
完全无响应上拉电阻缺失万用表测 SCL/SDA 电压补焊上拉电阻
时好时坏ADDR 悬空测 ADDR 引脚电压明确接高或接低
高速下误码总线电容大示波器看上升沿降低速率或减小上拉
特定寄存器写不进时序太紧逻辑分析仪抓波形增加写操作间隔

6.2 充电异常与电池检测问题

充电异常我遇到最多的是充电电流上不去。一开始设 500mA,结果实际只有 100mA 左右。查了半天发现是输入电源的限流设低了,USB 口只给了 500mA,PCA9422 的输入电流限制也是 500mA,两者叠加导致充电电流被限制。后来换了一个 2A 的电源适配器,充电电流立刻上去了。

另一个问题是电池电压读不准。ADC 读出来的值跳动很大,有时候差 0.2V。后来发现是 ADC 参考电压不稳定,PCA9422 内部参考电压受温度影响较大。解决办法是在软件里做滑动平均,连续读 8 次去掉最大最小值再取平均,波动就小多了。

还有一种情况是充电指示灯不亮。检查后发现是 LED 的限流电阻太大,电流只有 0.5mA,亮度太低看不见。换成 1k 电阻,电流 2mA,亮度就正常了。

6.3 系统复位与电源跌落问题

系统复位是电源设计中最头疼的问题。我遇到过两种情况:一种是 LCD 背光开启瞬间复位,另一种是 SD 卡写入时复位。前者是因为背光升压电路的输入电容太小,瞬间大电流把 3.3V 轨拉低了;后者是因为 SD 卡写入时电流脉冲太大,超过了降压转换器的响应速度。

解决办法分两步:第一步是在背光升压电路的输入端并联一颗 100uF 的钽电容,提供瞬态电流;第二步是在 SD 卡的供电轨上串联一颗 10uH 的电感,配合 100uF 电容组成 LC 滤波器,平滑电流脉冲。这两步做完之后,系统复位问题彻底消失。

注意:钽电容的 ESR 要选低一些的,否则滤波效果打折扣。我试过普通电解电容,效果明显不如钽电容。

6.4 低功耗模式下的异常唤醒

低功耗模式下偶尔会异常唤醒,一晚上醒好几次,导致功耗偏高。排查后发现是 PCA9422 的中断标志没有清除干净,残留的标志位导致中断线一直处于低电平。后来在休眠前加了一步:先读一次中断标志寄存器,再写 0xFF 清除所有标志,然后再读一次确认标志为 0,最后才进入休眠。这样处理后,异常唤醒就再也没出现过。

还有一个原因是 I2C 总线在休眠时被外部干扰拉低,导致 PCA9422 误判为通信请求。解决办法是在 SCL 和 SDA 上各加一颗 100pF 的电容到地,滤除高频干扰。这个电容不能太大,否则会影响正常通信的上升沿。

7. 一些实操心得与扩展思路

这套电源管理方案从选型到调试完成,前后花了大概三周时间,其中大部分时间用在功耗优化和异常排查上。有几个心得值得分享。

第一,不要迷信数据手册的典型值。数据手册上写的静态电流 15uA,是在特定条件下的值,实际电路中因为外围元件漏电、PCB 表面污染等原因,往往会高一些。我实测的休眠电流是 0.8mA,比理论值高了不少,后来发现是 LCD 驱动芯片的漏电流,换了一颗低漏电的型号才降下来。

第二,软件状态机要留够冗余。我一开始把状态切换条件写得很死,比如电池电压低于 3.5V 就切低功耗,结果电池电压在 3.5V 附近波动时,系统在正常和低功耗之间反复切换,功耗反而更高。后来加了 0.1V 的迟滞,问题就解决了。

第三,测试要覆盖极端情况。我模拟过电池从满电放到空电的全过程,也模拟过充电时拔掉电源、放电时插上电源的切换场景。这些极端情况在实验室里多测几次,总比在用户手里出问题强。

这套方案后续还可以扩展。比如加一个库仑计芯片,用电流积分法估算电量,比电压查表法准确得多。或者把 PCA9422 的 I2C 接口接到一个低功耗的协处理器上,让主控彻底休眠,协处理器负责电源管理,这样休眠电流还能再降一个数量级。再或者,把充电参数做成可配置的,通过串口或者无线接口下发,适配不同规格的电池。

我个人在实际操作中的体会是,电源管理这件事,硬件是基础,软件是关键,测试是保障。硬件设计好了,软件逻辑清晰了,再加上充分的测试,才能做出一套稳定可靠的电源方案。PCA9422 加 PIC18F85J10 这个组合,在中小功率便携设备里算是比较均衡的选择,集成度高,开发难度适中,值得一试。

RELATED READING

延伸阅读

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