1. 项目缘起与整体设计思路
嵌入式设备做电源管理,最怕的就是“各管各的”——MCU 自己跑自己的,充电芯片自己充自己的,电量计自己算自己的,最后系统一休眠,整机功耗下不来,电池续航直接崩盘。我接手过不少低功耗便携设备的电源架构评审,十有八九问题都出在“电源域没有统一调度”这件事上。
这次要聊的方案,核心就是用PCA9422这颗电源管理芯片,配合PIC18F4525这颗经典 8 位 MCU,搭一套完整的电源管理系统。PCA9422 是 NXP 家的一颗高度集成的 PMIC,内部集成了线性充电器、多路 Buck/LDO 输出、电量计前端、看门狗和 I2C 控制接口,专门为小容量锂电便携设备设计。PIC18F4525 则是 Microchip 的一款 8 位单片机,40 引脚、32KB Flash、带 MSSP(I2C/SPI)和丰富的中断资源,工业现场用得非常多,资料齐全、开发门槛低。
把这两颗芯片凑到一起,目标很明确:让 MCU 通过 I2C 全权接管 PMIC 的充电参数、输出电压档位、休眠唤醒策略和电量监测,实现“一颗 MCU 管整机电源”的架构。这样做的好处是,电源状态和系统状态始终同步——MCU 知道自己在干什么,就知道电源该怎么配。
为什么不用 MCU 内部自带的电源管理?因为 PIC18F4525 本身没有充电管理,也没有多路可调输出,直接挂锂电上去,过充过放全靠外部保护板硬扛,既不灵活也不安全。而 PCA9422 把充电、降压、电量计都集成在一颗 5mm×5mm 的 QFN 里,外围只需要几个电感和电容,BOM 面积和成本都能压下来。
这套方案适合谁?适合做便携医疗设备、手持工业终端、智能穿戴、无线传感器节点这类产品的硬件工程师和嵌入式开发者。如果你正在纠结“MCU 选型 + 电源架构”怎么搭配,或者手上已经有一块 PIC18 的板子想加锂电池管理,这篇内容可以直接抄作业。
整体设计思路分四层:
- 硬件层:PCA9422 负责功率路径管理,锂电池接 BATT 引脚,系统负载接 Buck 输出,MCU 由其中一路 LDO 供电。
- 通信层:PIC18F4525 通过 MSSP 模块的 I2C 主机模式,读写 PCA9422 的内部寄存器。
- 策略层:MCU 固件里维护一个电源状态机,根据充电状态、电量、按键事件决定进入哪种低功耗模式。
- 保护层:PMIC 硬件级的过压、过流、过温保护作为最后一道防线,MCU 软件层再做一层参数校验。
这个分层的好处是,任何一层出问题都不会直接导致电池损坏。比如 MCU 固件跑飞了,PMIC 自己的看门狗会复位 MCU;如果 PMIC 通信断了,硬件保护仍然生效。
2. PCA9422 与 PIC18F4525 的硬件协同细节
2.1 PCA9422 关键引脚与外围配置
PCA9422 的引脚不算多,但每一个都关系到系统能不能正常跑起来。我按实际画板子的顺序捋一遍。
电源输入侧:VBUS 接 USB 或 DC 适配器,典型 5V。这个引脚旁边必须放一颗 1μF 的陶瓷电容,尽量靠近芯片,否则热插拔时的浪涌可能让芯片误触发过压保护。BATT 接锂电池正极,典型 3.7V 单节锂电。BATT 和 VBUS 之间是内部线性充电器的功率路径,充电电流通过 I2C 配置,最大 500mA。
系统输出侧:PCA9422 有两路 Buck 和两路 LDO。Buck1 通常设 3.3V 给主系统,Buck2 设 1.8V 给外设或传感器,LDO1 和 LDO2 可以设 3.3V 或 1.2V 给 MCU 内核或模拟部分。每路输出的电感选型很关键,Buck 开关频率默认 2MHz,电感推荐 2.2μH,饱和电流至少 1A。我见过有人用 4.7μH 的电感,结果负载瞬态响应变差,MCU 在射频发射时直接复位。
I2C 接口:SDA 和 SCL 引脚,标准 400kHz 速率。PCA9422 的 I2C 地址由 ADDR 引脚决定,接地是 0x6A,接 VDD 是 0x6B。PIC18F4525 这边用 RC3 和 RC4 作为 I2C 引脚,硬件上拉电阻选 4.7kΩ,如果总线电容超过 200pF,要降到 2.2kΩ。
中断输出:PMIC 的 INT 引脚接到 PIC18F4525 的 RB0 外部中断引脚。任何充电状态变化、电量低、过温事件都会拉低 INT,MCU 在中断里读状态寄存器,比轮询效率高得多。
看门狗交互:PCA9422 内置一个独立看门狗,MCU 需要定期通过 I2C 写“喂狗”命令。如果 MCU 死机超过设定时间(默认 2 秒),PMIC 会拉低 RESET 引脚复位 MCU。这个功能在工业现场特别有用,我有个项目在电机旁边跑,EMI 干扰导致 MCU 偶尔跑飞,全靠这个看门狗救回来。
2.2 PIC18F4525 的 I2C 主机配置
PIC18F4525 的 MSSP 模块配置成 I2C 主机模式,步骤不复杂,但有几个寄存器容易配错。
// I2C 主机初始化,400kHz @ 8MHz Fosc SSPCON1 = 0x28; // SSPEN=1, I2C 主机模式 SSPCON2 = 0x00; SSPADD = 0x13; // 8MHz / (4 * 400kHz) - 1 = 4,实际取 0x13 是 100kHz 保守值 SSPSTAT = 0x80; // slew rate 控制关闭 TRISC3 = 1; // SCL 输入 TRISC4 = 1; // SDA 输入这里有个坑:SSPADD 的计算公式是Fosc / (4 * Fscl) - 1。如果系统时钟是 8MHz,想要 400kHz,算出来是 4。但实际调试时我发现,长走线或者上拉电阻偏大时,400kHz 容易丢数据,降到 100kHz 反而稳。所以上面代码里 SSPADD 写了 0x13(对应约 100kHz),这是我在多个项目里验证过的保守值。
I2C 读写函数要封装成带超时退出的形式,不能死等。PIC18 没有硬件超时,得用软件循环计数。
uint8_t I2C_Write(uint8_t addr, uint8_t reg, uint8_t data) { uint16_t timeout = 0; I2C_Start(); while (!I2C_WriteByte(addr << 1)) { if (++timeout > 1000) { I2C_Stop(); return 1; } } I2C_WriteByte(reg); I2C_WriteByte(data); I2C_Stop(); return 0; }注意:PCA9422 的寄存器写入后需要约 100μs 的内部处理时间,连续写多个寄存器时,每写一个要加短延时,否则后一个写入可能被忽略。
2.3 功率路径与地平面处理
PCB 布局上,PCA9422 的功率地和信号地要分开走,最后在芯片底部散热焊盘处单点连接。Buck 的输入电容和输出电容回路要尽可能小,我一般把输入电容放在 VBUS 引脚和 PGND 引脚之间,距离不超过 3mm。
锂电池连接器到 BATT 引脚的走线要够宽,至少 1mm,因为充电电流 500mA 时,1oz 铜厚下 0.5mm 线宽温升已经很明显了。如果结构允许,BATT 走线铺铜处理。
PIC18F4525 的电源引脚每对 VDD/VSS 旁边都要放 0.1μF 去耦电容,AVDD 额外加一颗 1μF。我见过有人省掉 AVDD 的电容,结果 ADC 读电池电压时跳变十几个 LSB,根本没法用。
3. 固件架构与电源状态机实现
3.1 电源状态定义与迁移条件
整套固件的核心是一个电源状态机,我定义了五个状态:
| 状态 | 条件 | Buck1 | LDO1 | MCU 时钟 | 典型电流 |
|---|---|---|---|---|---|
| 充电中 | VBUS 有效,CHG_STAT=1 | 3.3V | 3.3V | 8MHz | 15mA |
| 满电待机 | 电池 >95%,无负载 | 3.3V | 3.3V | 8MHz | 8mA |
| 正常运行 | 电池 >20%,系统活跃 | 3.3V | 3.3V | 8MHz | 25mA |
| 低电预警 | 电池 5%~20% | 3.3V | 3.3V | 4MHz | 12mA |
| 深度休眠 | 电池 <5% 或长按关机 | 0V | 0V | 停止 | <10μA |
状态迁移由三个事件触发:充电器插入/拔出(INT 中断)、电量阈值跨越(MCU 定期读电量计)、按键事件(RB1 外部中断)。
迁移逻辑用 switch-case 实现,每个状态入口处配置 PMIC 输出,出口处保存上下文。这里的关键是:进入深度休眠前,必须先通过 I2C 关闭所有 Buck 和 LDO,然后 MCU 再执行 SLEEP 指令。顺序反了的话,MCU 先睡了,PMIC 还在供电,整机功耗下不来。
3.2 充电参数配置与温度补偿
PCA9422 的充电参数通过一组寄存器配置,我整理成表格方便对照:
| 寄存器 | 功能 | 推荐值 | 说明 |
|---|---|---|---|
| CHG_CTRL1 | 充电电流 | 0x0A | 对应 300mA |
| CHG_CTRL2 | 终止电流 | 0x03 | 对应 30mA |
| CHG_CTRL3 | 浮充电压 | 0x1C | 对应 4.2V |
| TEMP_COMP | 温度补偿 | 0x05 | 高温降额 |
| SAFETY_TIMER | 安全定时 | 0x08 | 8 小时超时 |
充电电流的选择要结合电池规格。我用的是一颗 800mAh 的锂聚合物电池,标准充电倍率 0.5C,所以 300mA 左右合适。如果电池是 2000mAh,可以设到 500mA 上限,但要注意 PMIC 的散热,QFN 封装在 500mA 时芯片表面温度会到 60℃ 以上,PCB 散热焊盘一定要打好过孔。
温度补偿这块,PCA9422 支持外接 NTC 热敏电阻。我选的是 10kΩ B=3435 的 NTC,接在 TS 引脚和 GND 之间,分压电阻用 10kΩ 接 VREF。固件里不需要额外计算,PMIC 硬件自动根据 TS 电压调整充电电流。实测在 45℃ 环境下,充电电流自动从 300mA 降到 150mA,电池温度稳定在 50℃ 以内。
实操心得:NTC 的走线要远离 Buck 电感和功率走线,否则开关噪声会耦合到 TS 引脚,导致温度读数跳变,充电电流频繁切换。我一般把 NTC 放在电池连接器旁边,走线包地处理。
3.3 电量计读取与滤波算法
PCA9422 内部有一个基于电压和电流积分的电量计,MCU 通过 I2C 读两个寄存器:SOC_H和SOC_L,拼成 16 位值,单位是 0.1%。但直接读出来的值跳动很大,尤其是负载突变时,电压法估算的 SOC 会瞬间掉几个百分点。
我的做法是加一层滑动平均滤波,窗口大小 8,再配合负载电流补偿。
#define SOC_WINDOW 8 uint16_t soc_buf[SOC_WINDOW]; uint8_t soc_idx = 0; uint16_t Get_Filtered_SOC(void) { uint32_t sum = 0; uint16_t raw = Read_SOC_Register(); soc_buf[soc_idx++] = raw; if (soc_idx >= SOC_WINDOW) soc_idx = 0; for (uint8_t i = 0; i < SOC_WINDOW; i++) { sum += soc_buf[i]; } return (uint16_t)(sum / SOC_WINDOW); }滤波之后,SOC 变化就平滑多了。但要注意,充电时和放电时的 SOC 曲线不一样,充电时电压偏高,放电时电压偏低。我在固件里根据 CHG_STAT 寄存器判断当前是充是放,分别用不同的补偿系数。充电时 SOC 减 2%,放电时加 2%,这样切换瞬间不会跳变。
另外,电池老化后内阻增大,电压法估算误差会变大。PCA9422 支持库仑计模式,但需要 MCU 定期读取电流寄存器并累加。我在低电预警状态下会启动库仑计校准,用电流积分值修正电压法的偏差,精度能控制在 5% 以内。
4. 低功耗策略与休眠唤醒实操
4.1 PIC18F4525 的低功耗模式选择
PIC18F4525 有三种低功耗模式:Sleep、Idle、Run。Sleep 模式下振荡器停止,电流最低,典型 0.1μA(看门狗关闭时)。Idle 模式内核停,外设还跑,电流约 1mA。Run 就是正常跑。
我的策略是:正常运行用 Run,短时间等待用 Idle,长时间待机用 Sleep。比如系统等待传感器数据时,进 Idle,定时器中断唤醒;如果用户长按关机,进 Sleep,只有 RB0 中断能唤醒。
进入 Sleep 的代码很简单:
void Enter_Deep_Sleep(void) { // 1. 关闭 PMIC 非必要输出 I2C_Write(PCA9422_ADDR, BUCK2_CTRL, 0x00); I2C_Write(PCA9422_ADDR, LDO2_CTRL, 0x00); // 2. 配置唤醒源 INTCONbits.RBIF = 0; INTCONbits.RBIE = 1; // 3. 关闭 MCU 外设 ADCON0 = 0x00; SSPCON1 = 0x00; // 4. 进入 Sleep Sleep(); // 5. 唤醒后重新初始化 System_Init(); }这里有个细节:Sleep 前要把 I2C 模块关掉,因为 MSSP 在 Sleep 时如果引脚上有电平变化,可能产生额外漏电流。我实测过,不关 I2C 的话,休眠电流从 10μA 涨到 80μA,差了 8 倍。
4.2 PMIC 休眠配置与唤醒源管理
PCA9422 本身也有低功耗模式,通过PWR_MODE寄存器配置。在 MCU 进 Sleep 之前,先把 PMIC 设成STANDBY模式,只保留 LDO1 给 MCU 供电,其他输出全关。
唤醒源有两个:按键和充电器插入。按键接在 PMIC 的 PWR_ON 引脚,充电器插入通过 VBUS 检测。这两个事件都会拉低 INT 引脚,MCU 的 RB0 中断唤醒。
但这里有个时序问题:PMIC 从 STANDBY 恢复到 ACTIVE 需要约 2ms,MCU 被唤醒后如果立刻通过 I2C 读 PMIC 寄存器,可能读到旧值。我的做法是唤醒后先延时 5ms,再初始化 I2C 和读状态。
注意:如果系统里有大电容,VBUS 插入瞬间的浪涌可能让 PMIC 的过压保护误触发。我在 VBUS 输入端加了一颗 TVS 管和 10μF 电解电容,实测能有效抑制浪涌。
4.3 实测功耗数据与优化记录
我用一台高精度电流表记录了各状态的整机功耗:
| 状态 | 理论值 | 实测值 | 差异原因 |
|---|---|---|---|
| 正常运行 | 25mA | 27mA | LED 指示灯 |
| 低电预警 | 12mA | 13.5mA | MCU 降频后外设未关 |
| 深度休眠 | <10μA | 12μA | PCB 漏电流 |
| 充电中 | 15mA | 18mA | 充电指示灯 |
深度休眠的 12μA 里,PMIC 静态电流约 5μA,MCU Sleep 电流约 0.1μA,剩下的都是 PCB 漏电流和电容漏电。想再降的话,可以把 LDO1 也关掉,但那样 MCU 就没法被按键唤醒了,得用 PMIC 的 PWR_ON 直接触发复位。这个取舍看产品需求,如果追求极致待机,可以接受复位唤醒。
优化过程中发现一个有意思的现象:PCB 清洗不干净会导致漏电流翻倍。助焊剂残留在高阻抗节点上,形成微弱的导电通路。我用洗板水清洗后,休眠电流从 12μA 降到 8μA。所以做低功耗产品,PCBA 清洗这道工序不能省。
5. 常见问题排查与避坑经验
5.1 I2C 通信失败排查流程
I2C 不通是最常见的问题,我整理了一个排查顺序:
- 先量电压:SDA 和 SCL 在空闲时应该是高电平(约 3.3V)。如果只有 1V 左右,说明上拉电阻太大或者总线电容太大。
- 再量波形:用示波器看 SCL 和 SDA,确认起始条件、地址字节、ACK 位是否正常。如果 ACK 位一直是高,说明从机没响应。
- 检查地址:PCA9422 的 ADDR 引脚电平决定地址,用万用表确认。
- 检查电源:PMIC 的 VDD 和 VDDIO 是否正常,如果 VDDIO 没上电,I2C 引脚不工作。
- 最后换芯片:如果以上都正常,换一颗 PMIC 试试,有可能是芯片损坏。
我遇到过最诡异的一次,I2C 偶尔能通偶尔不通,查了半天发现是 SDA 走线经过 Buck 电感下方,开关噪声耦合导致误码。把走线绕开电感后问题消失。
5.2 充电异常与电池保护触发
充电异常通常表现为:充电电流上不去、充电指示灯不亮、电池电压不上升。
充电电流上不去:先读CHG_STAT寄存器,看 PMIC 报什么状态。如果是THERMAL_REG,说明芯片过热降额了,检查散热。如果是VBUS_OV,说明输入电压过高,检查适配器。
电池电压不上升:可能是电池保护板触发了。锂电池保护板在过放后会切断输出,这时候 PMIC 检测不到电池电压,会认为电池未连接。我的做法是在 BATT 引脚和电池之间加一个 100Ω 电阻和 0.1μF 电容,给保护板一个微弱的唤醒电流。实测有效,但电阻不能太小,否则休眠时漏电流大。
充电指示灯不亮:PCA9422 的 LED 驱动引脚可以直驱 LED,但要注意限流电阻。我一般用 1kΩ,电流约 2mA,亮度够用。如果 LED 不亮,先量 LED 两端电压,再检查寄存器配置。
5.3 休眠唤醒失败与复位循环
休眠唤醒失败通常有两个原因:唤醒源配置错误和PMIC 状态未恢复。
唤醒源配置错误:PIC18F4525 的 RB0 中断要配置成上升沿或下降沿触发,取决于 INT 引脚的极性。PCA9422 的 INT 是低有效,所以配下降沿。如果配成上升沿,永远唤不醒。
PMIC 状态未恢复:MCU 唤醒后,如果立刻读 PMIC 寄存器,可能读到 STANDBY 模式的旧值。我加了一个 5ms 延时,并且读两次,取第二次的值。
复位循环:如果 MCU 唤醒后立刻又进 Sleep,可能是电量判断逻辑有问题。比如电池电压刚好在阈值附近,读一次低于阈值就进 Sleep,醒来读一次又高于阈值,又进 Run,来回切换。我的做法是加 2% 的迟滞,低于 5% 才进 Sleep,高于 7% 才退出。
避坑技巧:调试低功耗时,先把所有唤醒源禁用,只留一个按键,确认能正常唤醒后再逐个加其他源。这样出问题容易定位。
5.4 常见问题速查表
| 现象 | 可能原因 | 排查方法 | 解决措施 |
|---|---|---|---|
| I2C 无 ACK | 地址错误/电源未上 | 量 ADDR 引脚/量 VDDIO | 改地址/查电源 |
| 充电电流小 | 温度降额/输入限流 | 读 CHG_STAT | 改善散热/换适配器 |
| 休眠电流大 | I2C 未关/PCB 漏电 | 分段量电流 | 关 I2C/洗板 |
| 唤醒失败 | 中断极性错 | 示波器看 INT | 改触发沿 |
| SOC 跳变 | 负载突变 | 读电流寄存器 | 加滤波/补偿 |
| 复位循环 | 阈值无迟滞 | 看串口日志 | 加迟滞 |
这套方案我在两个产品上量产过,一个是手持式工业读码器,一个是便携式医疗监测仪。读码器那边环境温度高,PMIC 散热是重点;监测仪那边对休眠电流敏感,PCB 清洗和 I2C 关闭是重点。两个项目跑下来,PCA9422 + PIC18F4525 的组合稳定性没问题,关键是固件里要把状态机写严谨,硬件上把功率路径和地去处理好。
最后分享一个小技巧:PCA9422 的寄存器配置可以保存成一份初始化数组,上电时用循环一次性写入,比逐个写函数调用清爽得多。数组里把地址、寄存器、值三列对齐,后期改参数一目了然。