多彩编程 多彩编程MZPH · CODE BLOG
ARTICLE DETAIL

文章详情

深耕前端与后端开发技术的一线实战笔记与踩坑复盘。

PIC18F87J11与PCA9422协同:I2C总线上的嵌入式电源管理实战

PIC18F87J11与PCA9422协同:I2C总线上的嵌入式电源管理实战 在嵌入式设备里电源管理往往是最容易被低估的一块。很多人以为“接上电池、LDO输出3.3V、单片机跑起来”就算完成了电源设计等真调到低功耗、充电、多路电压切换时才发现处处是坑。我最近做的项目里用 PCA9422 这颗电源管理芯片搭配 PIC18F87J11 这颗 8 位单片机把整机电源管理完整收口到了 I2C 总线上调试体验好了不止一个档次。这套组合的思路其实很简单PCA9422 负责充电、降压、LDO、负载开关这些“重活”PIC18F87J11 负责策略——什么时候开哪路电、调多高电压、进什么低功耗状态全部通过寄存器读写完成。相比传统的一颗充电IC加几颗LDO、再用一堆GPIO控制开关管的方案这套玩法最大的优势是电源状态可见、可控、可配置。这篇文章就围绕这套组合的硬件连接、软件驱动、充电流程、动态调压和典型故障展开适合正在做低功耗电池设备、想把电源从“玄学”变成“科学”的开发者参考。1. 为什么要用专用PMIC而不是继续堆分立器件1.1 电池供电设备真正要管好的五件事带电池的设备电源管理从来不是“给芯片供上电”这么简单。我做过几款便携设备总结下来一颗电池要管好五件事。首先是充电。单节锂电池不是直接怼电压就能充的要经历预充、恒流、恒压、截止判断几个阶段充电电流、截止电压、温度保护都要设得合理。用分立元件搭充电电路不是不行但那些参数全是硬件电阻电容写死的想调一个值就得换料。第二是供电。系统里往往同时存在 3.3V 的 MCU、1.8V 的传感器、5V 的 USB 通信设备不同模块要求不同电压轨。多个 LDO 叠下来板子面积和静态功耗都很难看。第三是时序。开机时先给主控供电再给外设供电关机时顺序反过来否则外设还在工作、主控先断电可能出现总线锁死或复位异常。第四是监控。电池电压还剩多少、充电插头有没有插入、当前处于什么充电阶段这些状态不读出来低电关机、电量显示都无从谈起。第五是低功耗。设备睡眠时哪些轨要彻底关断、哪些轨要保留漏电流要控制在微安级别靠纯硬件很难灵活切换。这五件事用一颗 PMIC 加一个 MCU 的寄存器读写配合适当的软件状态机确实比一堆模拟器件更好收拾。1.2 PCA9422 与 PIC18F87J11 的组合逻辑选 PCA9422是因为它在单颗芯片内集成了充电管理、多路可编程输出电源轨、负载开关和丰富的状态寄存器控制和状态都暴露在 I2C 总线上。选 PIC18F87J11是因为它带 MSSP 模块可以配置成 I2C 主机IO 数量充足低功耗模式也够用做策略控制刚刚好。我实际用下来的感受是这套组合最值钱的地方是把“电源”变成了一个普通的外设。操作电源轨的开和关就像操作一个 GPIO 一样简单读取充电状态就像读一个寄存器一样直接。原先需要示波器、万用表反复测量才能确认的问题现在固件里打印一串十六进制数就能定位。多说一句如果项目需求更复杂、需要多组高速通信接口或者要跑实时操作系统也可以考虑换成更高端的 MCU但核心思路不变让 PMIC 干模拟的活让 MCU 干策略的活。2. 硬件连接方案引脚级配合与电路细节2.1 I2C 控制通道与中断反馈PIC18F87J11 的 MSSP 模块配置为 I2C 主模式接 PCA9422 的 SDA、SCL。要注意的是这两个引脚都必须加上拉电阻。线路上拉电阻我习惯选 4.7kΩI2C 速率设 100kbps。电源管理对通信速率要求不高100k 反而更稳抗干扰能力比 400k 好不少。PCA9422 的中断输出引脚也一定要接出来接到 PIC18F87J11 的 RB0 或者任意带外部中断的 IO 上配置成下降沿触发。充电阶段切换、插拔 USB、电池异常、输出电压到位这些事件 PMIC 会通过中断脚主动通知 MCU。如果不用中断全部靠 MCU 定时轮询状态寄存器一是响应不及时二是 MCU 频繁醒来反而费电。我在原理图上还专门加了一个 100Ω 的串联电阻在中断线上算是个简单的 ESD 缓冲。另外SDA、SCL 靠近 MCU 一端可以各加一个 33pF 的电容接地对波形边沿有一定整形作用成本忽略不计。2.2 电源轨划分与旁路电容选型PCA9422 一般提供多路输出具体几路取决于封装与配置项目里我规划了三条主要轨电源轨默认电压给谁供电备注主系统轨3.3VPIC18F87J11、接口芯片支持软件调压传感器轨1.8V温湿度传感器、ADC前端可独立关断备份轨3.0V实时钟、掉电保持区低功耗常开这里我把传感器轨单独出来就是希望低功耗模式下整条轨彻底关闭把传感器静态功耗降为零。主系统轨保留实时钟轨不能断这样睡眠电流就能压得很低。每路输出旁边都要放一个 10μF 加一个 0.1μF 的去耦电容。电容必须选 X5R 或 X7R 规格还要注意耐压值。很多新手吃亏在陶瓷电容的直流偏压特性上——同样是 10μF 的电容个头越小加电压后实际容量越打折扣。标称 10μF 在 3.3V 下可能只剩 6μF 甚至更低所以容值我习惯取 1.5 倍余量耐压取最高工作电压的 1.5 到 2 倍。2.3 使能控制与电池温度检测PCA9422 的使能脚怎么接取决于你的开关机策略。我希望实现“按键软关机 低电自动关机”所以使能脚直接接到了 PIC18F87J11 的一个普通 GPIO 上。开机时按键拉高使能脚PMIC 上电MCU 起来后再通过寄存器确认状态关机时 MCU 先将外设轨关断、保存数据最后把引脚拉低整个系统真正断电。这个方案省掉了外部电源开关逻辑全在固件里。电池温度检测用的是电池包内的 NTC接到 PCA9422 的温度检测脚。这里有个容易踩的坑NTC 的分压电阻如果不匹配读出来的温度会偏差很大。我调试时发现用 10kΩ NTC 配 10kΩ 上拉在 25 度附近读数还算正常但到 0 度和 45 度偏差超过 5 度。后来直接用 NTC 分压查表再配合两个温度点的实测校准才把误差压到 1 度以内。充电时的温度保护阈值同样建议结合电池规格书和实测数据设置。3. 让 PMIC 开口说话I2C 驱动与核心寄存器操作3.1 PIC18F87J11 的 MSSP 初始化I2C 驱动是整个软件的基础。下面的代码只是我工程里抽出来的逻辑代码底层调用请按你自己平台的 API 替换。PIC18 系列的 MSSP 模块初始化大致是这样void i2c_master_init(void) { // 假设引脚由内部弱上拉可省略外部电阻仍然建议外部上拉 SSPCON1 0x28; // I2C 主模式使能 SSPCON2 0x00; SSPADD 0x4F; // 100kHz Fosc 8MHz SSPSTAT 0x80; // Slew rate disable }MSSP 的中断我开了但主循环里主要用同步轮询方式读写中断只做错误标志捕捉。对于寄存器数量有限的 PMIC完全没必要把 I2C 通信做得太异步简单可靠优先。3.2 寄存器读写的基本函数PCA9422 的从机地址不是 0x48 就是 0x4F 这类具体看 A0 引脚电平别背我的地址一定要按数据手册确认。读写函数的本质就是标准 I2C 时序#define PMIC_I2C_ADDR 0x48 // 请按数据手册和硬件连接确认 void pmic_write_reg(uint8_t reg, uint8_t val) { I2C_Start(); I2C_Write((PMIC_I2C_ADDR 1) | 0); I2C_Write(reg); I2C_Write(val); I2C_Stop(); } uint8_t pmic_read_reg(uint8_t reg) { uint8_t val; I2C_Start(); I2C_Write((PMIC_I2C_ADDR 1) | 0); I2C_Write(reg); I2C_RepeatedStart(); I2C_Write((PMIC_I2C_ADDR 1) | 1); val I2C_Read(0); // 最后一个字节发NACK I2C_Stop(); return val; }最开始我遇到过读完一个寄存器后总线卡死的情况原因就是最后一个字节看了 ACK 而不是 NACK。I2C 主机读最后一个字节时必须回 NACK从机才知道“别发了”这个细节丢给习惯写 STM32 库函数的人特别容易漏。3.3 关键寄存器分类与操作要点把寄存器大致分成四类排查时思路会清晰很多电源轨配置类每路的输出电压档位、软启动斜率、使能位。充电控制类充电电流、截止电流、充电电压、温度保护阈值。状态类VBUS 是否在位、充电状态机所处阶段、各电源轨是否达到目标电压。中断标志类充电完成、输入插拔、故障等事件标志。操作电源轨时优先级最高的经验是“写完使能位之后必须回读状态位确认”。有几次我写完使能位立刻去开下一路结果电压还没建立好后级芯片上电瞬间出现诡异复位。调压也一样改完寄存器轮询新电压寄存器等它稳定再启动负载。下面是一段把某路电源轨电压从 3.3V 调整到 2.8V 的示意代码#define REG_VOUT_DCDC 0x15 // 示例地址 #define VOUT_2V8 0x28 // 对应编码具体查手册 void set_main_rail_2v8(void) { uint8_t wb 0; pmic_write_reg(REG_VOUT_DCDC, VOUT_2V8); // 等待电压切换完成多数PMIC用状态位表示DVS切换完成 do { wb pmic_read_reg(0x20); // 示例状态寄存器 } while ((wb 0x01) 0); }4. 充电管理、动态调压与开关机时序4.1 充电状态机的固件实现锂电池充电本质是一台状态机预充、恒流、恒压、截止、再充电。PCA9422 内部会走这个过程MCU 要做的不是模仿它而是读懂它、根据状态做界面显示和告警。我在主循环里每 500ms 读一次状态寄存器判断当前状态#define REG_CHG_STATUS 0x30 // 示例地址 #define CHG_PRE 0x00 #define CHG_CC 0x01 #define CHG_CV 0x02 #define CHG_DONE 0x03 const char* chg_state_str(uint8_t s) { switch (s) { case CHG_PRE: return Precharge; case CHG_CC: return CC; case CHG_CV: return CV; case CHG_DONE:return Done; default: return Unknown; } }充电电流的设置要根据电池容量来。我用的电池是 1000mAh恒流阶段设了 500mA也就是 0.5C。预充电流设 100mA截止电流设 50mA。充电电压设 4.2V。如果手头的电池内阻偏大同样 500mA 下会提前进入恒压段充电时间明显变长这说明充电电流该适当下调。充电时的温度保护也不可忽略。我在固件里读到电池温度低于 0 度或者高于 45 度会直接关闭充电器件并置故障标志而不是等到硬件过温保护触发。4.2 DVS 动态调压降频之前先降压PCA9422 支持软件动态调压这功能在低功耗任务里特别有用。很多 8 位 MCU 支持主频切换但大家常常忘记工作频率降下来之后需要的核心电压其实也能跟着降。电压降了动态功耗才能实打实降下来。我把任务分成三个性能档位工作模式CPU主频核心电压适用场景高性能全速3.3V批量数据采集、无线发包标准中速3.0V日常轮询低速低速2.8V睡眠前准备顺序上必须先降电压再降频率或者电压频率同步降。如果先降频率再降电压中间这段时间电压是“过剩”的多耗了不必要的功率如果升频时先升频率再升电压处理器可能在欠压下跑容易跑飞。我自己就用错过顺序连续重启好几次才反应过来。4.3 上电、断电与休眠流程上电流程我总结成一套固定步骤按键或外部事件拉高使能脚PCA9422 开始输出。MCU 复位I2C 初始化。读 PMIC 芯片版本和状态寄存器确认通信正常。配置各路电源轨电压、软启动斜率。读取电池电压、充电状态、温度。打开外设供电进入主循环。断电流程的顺序刚好反过来尤其注意要先把外设轨关掉再让 MCU 保存数据、拉低使能脚。还有一点MCU 拉低使能脚之前要确认 I2C 总线不会再发起通信否则 PMIC 已经下电、总线被拉死下一次上电可能无法复位。休眠流程就更细了关外设轨、把主轨电压往低调、减频率、屏蔽不必要中断、关 ADC、最后执行休眠指令。唤醒后先恢复电压再开外设轨。这套流程走下来整机睡眠电流从几个毫安降到了几十微安水平。5. 联调中的典型翻车现场定位与修复思路5.1 I2C 读回来全是 0xFF设备没应答这是我第一次上电遇到的最直接问题。I2C 扫描地址时每个地址都返回 0xFF板子上根本没有设备应答。排查链路可以复现一下先测使能脚发现是低电平。PCA9422 没被使能自然不响应任何 I2C 操作。这个最简单但也是最容易忽略的。很多 PMIC 上电初始处于待机或运输模式必须有使能信号才进入工作状态。拉高使能脚后还是 0xFF再用示波器抓 SCL 和 SDA。看地址帧结束之后到底有没有 ACK 位露出来。没有 ACK基本是从机地址写错、硬件地址引脚接错、或者从机是 7 位地址被误当成 8 位地址移位处理。最后还发现一个隐藏问题我把 SDA 的上拉电阻接到了某路可关断电源轨上而调试时那路轨还没打开。上拉没电I2C 当然不通。这也是为什么我后来始终把 I2C 上拉单独接到常开轨。5.2 输出纹波偏大后级芯片随机复位主轨 3.3V 上量到几十毫伏的纹波后级芯片偶尔复位。最开始怀疑 PMIC 坏了后来一量发现纹波频率正好是降压开关的频率定位到输出电容配置。把电容从 10μF 换成 22μF纹波明显下降。再看布局输出电容离 PMIC 输出脚太远中间还穿了个过孔等效串联电感太大。挪到同一层贴近输出脚之后问题彻底消失。电源 layout 的经典原则就是“输出电容尽量靠近输出引脚回路面积越小越好”这话看十遍不如踩一次坑记得牢。5.3 睡眠电流怎么也降不下去调试低功耗那几天我心里始终绷着一根弦。单片机已经睡了PMIC 外设轨也关了但整机睡眠电流还在 1.2mA 左右徘徊。最后用逐段断开法排查先把 I2C 上拉电阻从 4.7kΩ 改成 100kΩ电流立刻掉了一部分。再查 NTC 分压偏置发现它一直通着电浪费了几百微安。把 NTC 的测量改成间歇式也就是每次测量前给偏置供电、测完就断又省下一截。最后发现 PMIC 本身在轻载时处于强制 PWM 模式而不是省电模式睡眠负载那么轻PWM 开关损耗一直是满的。通过寄存器把工作模式切到 PFM才真正把静态功耗压下来。这个教训是PMIC 不是只有电压输出工作模式同样也要动态管理。现象可能原因确认方法解决办法I2C 读 0xFF使能脚没拉高量 EN 电平配置使能信号无 ACK 应答从机地址错误示波器抓波形核对地址与移位输出纹波大电容位置过远纹波频率分析就近布局加大电容睡眠电流高上拉/偏置未关断逐段断开测量间歇供电/换 PFM最后分享一点体会这套 PCA9422 加 PIC18F87J11 的方案真正值得借鉴的地方不是某颗芯片的参数有多强而是把电源管理的每个环节都变成了可控、可读、可编程的寄存器操作。我之前调分立电源方案出了问题全靠猜现在直接在固件里把状态寄存器打印出来问题基本一目了然。要说下一步还能怎么扩展我会考虑把充电曲线数据和整机功耗统计定期存下来通过日志接口回传这样设备在用户手里跑几个月电量和电池衰减情况都能摸得清清楚楚。对于手里正好有这两颗芯片、准备做电池设备电源方案的朋友我的建议是先把 I2C 通信跑稳再把充电状态和电源轨控制调通这套基础打牢了后续低功耗和动态调压都是水到渠成的事。
返回列表