ARTICLE · INTELLIGENCE

战地情报 · 详情页

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

PCA9422与PIC18LF4620低功耗电源管理实战:从配置到调试

PCA9422与PIC18LF4620低功耗电源管理实战:从配置到调试

电池供电产品里,电源管理从来不是“把电压接对”这么简单。手头这块板子用 PCA9422 搭配 PIC18LF4620 做了整套电源管理,PCA9422 负责电池充放电、多路电压输出和状态上报,PIC18LF4620 负责系统逻辑、I2C 配置和低功耗切换。这套组合非常适合便携仪表、智能手环、遥控器、小型传感器节点这类电池容量不大、对体积和待机电流都很敏感的设备。本文会把硬件连接、寄存器初始化顺序、充电状态读取、低功耗切换和调试方法整体复盘一遍,尤其是 PMIC 里那些容易被忽略的状态位和控制时序。

1. 为什么要单独做电源管理

1.1 PIC18LF4620 的低功耗并非万能

PIC18LF4620 确实是一颗很省电的单片机,同一系列里,LF 后缀版本把工作电压下探到 1.8V,Sleep 模式下典型电流可以拉到个位数微安级别,配合 WDT 和中断唤醒,做低功耗产品的底子很好。但 MCU 省电不代表整机省电,产品里往往还有显示屏、传感器、无线模块、运放电路,这些外设的供电不是一颗 MCU 能管好的。

几个实际场景就能说明问题:第一,系统里需要多个电压域,模拟部分用 3.3V,数字内核用 1.8V,传感器可能还要单独断电;第二,电池充电需要限压限流,充满后还要检测终止电流,靠分立元件搭充电电路非常占板面积,也很难把充电状态上报给 MCU;第三,睡眠和唤醒时,各路电源需要一个明确的先后顺序,我用 GPIO 控制 MOS 管也能实现,但时序一旦做错,MCU 可能还没初始化完就看门狗复位。这些问题集中在一起,就用到了我们今天的主角 PCA9422。

1.2 PCA9422 在这里的角色

PCA9422 是一颗面向电池设备的 PMIC,典型的资源包括电池充电管理、内部升压或降压转换、多路输出、I2C 配置接口、状态寄存器和中断引脚。用它之后,电源策略从硬件电路搬到了软件里,输出电压、充电电流、开关时序、低功耗模式都可以通过 I2C 寄存器动态调整。

我在这块板子上没有把 PMIC 当成一个单纯的电源芯片用,而是让它承担四件事:

  • 把 USB 5V 和电池供电统筹起来,做输入路径管理
  • 给系统提供两路主输出,一路 3.3V 给外设,一路 1.8V 给数字部分
  • 对电池做完整的充电流程控制,包括预充、恒流、恒压、截止判断
  • 把充电状态、输入电源状态、各路输出状态通过 I2C 暴露给 PIC18LF4620

这样做的好处是,MCU 不需要用 ADC 去频繁测电池电压来判断充电阶段,直接读状态寄存器就能知道当前处于哪一种阶段。硬件少了一大块,调试交互区也从示波器看波形变成了逻辑分析仪看 I2C 报文,可控性提升非常明显。PIC18LF4620 本身有 MSSP 模块,硬件 I2C 直接可用,不需要软件模拟 I2C,CPU 占用率也很低。

2. 硬件连接与系统拓扑

2.1 整机电源树设计

先看一下完整的电源结构。这板子的输入有两个来源:USB 5V 和单节锂电池,两者通过 PCA9422 内部的路径管理切换。PMIC 输出端分别生成 3.3V、1.8V 两路,另外留一路直接给 PIC18LF4620 供电,电压用 3.3V。

为什么要分两路而不是统一 3.3V?因为板上有一个 1.8V 的数字逻辑器件,如果从 3.3V 经过一颗 LDO 降压到 1.8V,LDO 的静态电流在低功耗模式下会成为负担,而且 LDO 压差大时效率很低。PMIC 直接输出 1.8V,在几种负载电流区间里都能保持较高的转换效率。更重要的是,1.8V 这路负载在睡眠时可以单独关闭,不需要 3.3V 的外设供电跟着一起受影响。

PIC18LF4620 的 VDD 也连接在 3.3V 上,但 MCU 单独加了一颗 0.1uF 的退耦电容。在布线时,PMIC 输出的主电容放在电源入口,小容值电容靠近各负载引脚。这点别省,输出纹波和瞬间压降很多时候就是少了这些小电容造成的。

2.2 电池充电路径

电池充电部分,我们用的是一颗单节锂离子电池,标称 3.7V,充电限制电压 4.2V。PCA9422 的充电配置里需要设定预充电压阈值、预充电流、恒流充电电流、恒压充电电压和终止电流。这些参数不是靠数据手册随便填的,得根据电池规格书来。

我这边电池容量是 500mAh,按照 0.5C 左右的常规充电率,恒流阶段设置 250mA。预充阶段设置在 2.9V 以下,预充电流 30mA,防止过放的电池在恒流大电流下损坏。恒压 4.2V,终止电流设置 25mA,也就是当恒压阶段电流掉到 25mA 时就认为电池已经充满。

这里有一个很多人忽略的点:充电回路上的 PCB 走线电阻、电池连接器接触电阻和采样电阻误差,都会影响实际到达电池的电压。理论设置 4.2V 结束时,电池端可能只有 4.18V,长期下来容量损耗能到几个百分点。我看着数据手册上的充电电压精度,再用万用表实测了电池接口处的电压,把恒压值向上修正了大约 20mV,这才保证电池端实际能到 4.2V。

2.3 I2C 与中断链路

PCA9422 作为 I2C 从机,PIC18LF4620 作为主机。SCL、SDA 两根线都接了 4.7k 欧姆上拉电阻到 3.3V,因为 PMIC 的 I2C 引脚通常是开漏结构,必须外部上拉才能拉高。这个电阻值在 400kbps 速率下没问题,如果只跑 100kbps,可以适当地换成 10k 甚至更大,但上拉太弱会导致上升沿变缓,极端情况下通信不稳定。

还有一个 RESET 引脚,我用 PIC18LF4620 的一个 GPIO 去控制,用来在做低功耗唤醒时让 PMIC 重新启动,避免状态机里出现奇奇怪怪的残留配置。中断引脚 INT 则接到 MCU 的外部中断输入,当充饱、充电异常、输入插入、输出掉电等事件发生时,PMIC 会拉低 INT 通知 MCU,MCU 在中断里通过 I2C 读 STATUS 寄存器确认具体原因。

PIC18LF4620 的 MSSP 引脚默认可能是输入模式,需要先把 TRIS 配好。另外注意上电时序,PIC 的 VDD 还没稳定时,GPIO 可能是高阻或不定状态,这会干扰 PMIC 的 EN 和 I2C 总线。好在我的系统里 PIC18LF4620 和 PMIC 同时上电,PMIC 的默认输出状态可以先用。如果必须要求 MCU 先跑起来再开 PMIC,就要加上 EN 控制,并把 MCU 的几个关键 GPIO 初始化为确定电平。

3. 固件实现的初始化与状态读取

3.1 I2C 读写底层函数

PIC18LF4620 使用 MSSP 模块做硬件 I2C,初始化时把 I2C 模式设置为 100kbps 或 400kbps,地址长度 7 位。实际项目里我直接把速率设成 400kbps,原因很简单:PMIC 配置过程只发生在开机瞬间和模式切换瞬间,数据量不大,400kbps 足够快,而且 PMIC 的 I2C 时序经过逻辑分析仪验证能稳定工作在这个速率下。

底层写函数核心就是 start、发送地址、发送寄存器地址、发送数据、stop 这几步。为了减少重复代码,我封装成下面这样:

void pmic_write(uint8_t reg, uint8_t dat) { i2c_start(); i2c_write(PMIC_ADDR_W); i2c_write(reg); i2c_write(dat); i2c_stop(); }

读函数从 PMIC 读取一个字节时,需要注意“读寄存器地址”和“读数据”之间要插入 restart 信号,不能直接用 stop 再加 start,否则某些从机芯片在 stop 后会释放总线,重新 start 后又需要重复发送设备地址,整个流程变复杂且容易出错。标准流程如下:

uint8_t pmic_read(uint8_t reg) { i2c_start(); i2c_write(PMIC_ADDR_W); i2c_write(reg); i2c_restart(); i2c_write(PMIC_ADDR_R); uint8_t dat = i2c_read(ACK); i2c_stop(); return dat; }

i2c_read 的参数表示主机读完这一字节后是回 ACK 还是 NACK。因为这里只读单个字节,所以读完一字节后回 NACK,告诉从机不需要继续发送了。如果一次要连续读取多个寄存器的值,可以在最后一个字节读完后返回 NACK。寄存器地址寄存器会自动递增的话,非常适合批量读取,PCA9422 这类 PMIC 大多支持连续读。

3.2 上电配置的顺序

PMIC 上电后会按默认配置工作,但默认配置不一定符合产品要求,所以 PIC18LF4620 要在初始化阶段把整套配置写进去。顺序很关键,不要上来就改输出寄存器,先把输入电源检测状态看一遍,再配置充电参数,最后配置各路输出电压并使能中断。

我写的初始化流程是这样:

  1. 等待 PMIC 完成内部上电复位
  2. 读取设备 ID 寄存器,确认 I2C 通信正常,防止后续配置全做了无用功
  3. 读取输入电源状态寄存器,确认是 USB 供电还是电池供电,并判断电压是否在合法范围
  4. 配置充电电压、电流、终止电流和预充参数
  5. 配置各路输出电压目标值
  6. 按系统需要的顺序使能各路输出
  7. 清除所有挂起的中断状态位,打开需要上报的中断屏蔽位
  8. 使能 PMIC 中断引脚

设备 ID 校验这一步非常建议保留。有次我换了 PCB 样品,芯片焊错方向,I2C 地址全对不上,如果没有 ID 校验环节,那一套初始化代码会在完全未知的设备上执行,很难排查。ID 读出来不对最好直接卡住并输出错误,而不是继续往下跑。

输出电压的配置是控制 PMIC 内部 DCDC 或 LDO 的目标值。我这边把输出 1 配成 3.3V,输出 2 配成 1.8V,在配置寄存器里写入对应档位编码。代码中用宏定义包一层,不要直接手写十六进制值:

#define VOUT1_3V3 0x16 #define VOUT2_1V8 0x0C pmic_write(REG_VOUT1, VOUT1_3V3); pmic_write(REG_VOUT2, VOUT2_1V8);

如果配置寄存器变更后输出电压没立刻变化,还需要单独发送一个“电压变化使能”命令或触发寄存器更新位。很多 PMIC 为了避免输出在配置过程中抖动,会把新的目标电压存到影子寄存器,等使能位更新后才真正生效。这时候不要反复写电压寄存器,否则会一直停留在旧的输出电压上。

3.3 电池状态与中断处理

充电状态读取是最能体现 PMIC 价值的地方。PCA9422 的 STATUS 寄存器会给出一个状态组合,区分预充阶段、恒流充电阶段、恒压充电阶段、充电完成、无电池、充电异常等状态。PIC18LF4620 只需要周期性地读一个寄存器,就能知道充电进行到哪一步。

我在代码里用了一个简单的状态映射表:

switch (charge_status) { case CHG_PRE_CHARGE: update_led(CHG_LED_SLOW); break; case CHG_FAST_CHARGE: update_led(CHG_LED_ON); break; case CHG_DONE: update_led(CHG_LED_DIM); break; default: update_led(CHG_LED_ERROR); break; }

那个充电完成的判断,完全依赖 PMIC 终止电流检测,比 MCU 用 ADC 量电压判断要准得多。AD 采样误差、分压电阻温漂、参考电压精度都会影响判断,而 PMIC 芯片内部检测不到万用表级别,但重复性和一致性很好,产品量产时不易出现不同批次充电判定不一致的问题。

另外要注意,每当中断引脚拉低,MCU 进入中断服务函数后,第一件事是读取 STATUS 或 INT 寄存器,把中断标志读掉。只读不写寄存器通常也能自动清标志。有一些 PMIC 如果不清标志,INT 引脚会一直保持低电平,导致 MCU 反复进中断,整机功耗瞬间高出几毫安。这种问题用示波器看 INT 引脚会非常明显,拉低后没有恢复高。

4. 低功耗状态切换与续航估算

4.1 系统功耗状态划分

低功耗产品不能只有一个运行模式和睡眠模式,我把整机状态分成了四档:

  • 工作状态:所有功能开启,PMIC 各路输出正常,PIC18LF4620 运行主循环
  • 空闲状态:显示关闭,传感器停止采样,PMIC 保持输出电压,但关闭非必要外设供电轨
  • 睡眠状态:PIC18LF4620 进入 Sleep,PMIC 切换到低功耗模式,保留一待机电压轨,其余输出关闭
  • 关机状态:PMIC 输出基本关闭,仅保留充电检测和唤醒检测

这种分档比单一种类 Sleep 要实用,因为休眠意味着产品还能随时被按键、传感器中断或定时器唤醒;而关机则可能要求只有插上 USB 才能重新启动,两者对 PMIC 的配置完全不同。

PIC18LF4620 的 Sleep 模式可以让 CPU 停止,但 MCU 本身还挂在 3.3V 上,静态功耗已经压得很低。更大的收益在于 PMIC 关闭那些外设供电轨。比如一个图像传感器在 enable 引脚悬空或上电但没工作时,电流能到几百微安到毫安级,这在整机睡眠时是不可接受的,必须把整个电源域切断。

4.2 通过寄存器切换输出轨

睡眠状态切换时,PIC18LF4620 会先对使用中的外设完成一次停车,比如说把传感器配置保存好、把射频模块进入复位状态,然后才把 PMIC 输出 2 关掉或降低电压。这一步顺序反了,外设还没停稳就断电,唤醒后可能初始化失败。

我的睡眠流程里把 1.8V 轨直接关断,3.3V 轨保留,同时把 3.3V 上的外设供电在 PMIC 内部切换到较低功耗模式。这样,PIC18LF4620 的唤醒源仍然能工作,但绝大多数负载已经断电,整机睡眠电流从原来的 6.8mA 降到 48uA,效果非常明显。

代码里关键就是三个操作:

pmic_write(REG_PWR_CTRL, RUN_MODE_CONFIG); // 进入睡眠前先配置 PMIC 输出 pmic_write(REG_VOUT2, VOUT2_SLEEP_VALUE); pmic_write(REG_SLEEP_MODE, SLEEP_ENABLE); PIC_Sleep(); // 唤醒后恢复工作模式 pmic_write(REG_SLEEP_MODE, SLEEP_DISABLE); pmic_write(REG_VOUT2, VOUT2_1V8); pmic_write(REG_PWR_CTRL, RUN_MODE_CONFIG);

这些寄存器具体定义要查 PMIC 数据手册,但思路是一致的。睡眠时不直接关断 PMIC 主电源,而是把输出电压降到刚好维持 RAM 不掉电的水平,或者直接关闭某一路输出,让 MCU 通过中断唤醒后再把配置写回来。PIC18LF4620 的 BOR(掉电检测)在电压跌落时要能扛得住,所以保留 3.3V 火线是底线。

4.3 一组续航实测数据

用这套配置跑下来的实测数据,贴出来供参考:

  • 工作状态平均电流 42mA,屏幕开启,传感器以 10Hz 采样
  • 空闲状态平均电流 4.5mA,屏幕关闭,传感器待机
  • 睡眠状态平均电流 48uA,仅保留 RTC 定时唤醒功能
  • 关机状态平均电流 5uA

拿 500mAh 电池算续航:假设一天里工作 2 小时,空闲 8 小时,睡眠 14 小时,那么一天的耗电量约等于 42mA x 2h + 4.5mA x 8h + 48uA x 14h,大约 120.7mAh。500mAh 电池理论可以撑 4 天左右。如果睡眠电流没调好,比如 PMIC 多路输出全部保持开启,睡眠电流到了 500uA,那 14 小时就会多消耗 6.3mAh,占比不大,但当你希望几个月不充电时,每一微安都是命。所以低功耗产品的核心,不一定在主芯片选型,而是电源域怎么切。

5. 调试中常见的坑与排查

5.1 I2C 不 ACK 的奇怪问题

I2C 通信失败是我遇到最多的坑。现象是 MCU 发送完设备地址后,SCL 上有正常的时钟,但 SDA 在应答位时始终为高,也就是从机没有应答。排查顺序建议这样:

先看地址对不对。PMIC 的 I2C 地址会根据地址引脚的电平变化,不能只对照数据手册的默认值,还要确认硬件上引脚是接地还是接高。我有一次就是把地址引脚默认悬空,以为悬空等于接地,结果和实际地址差了一位。

再看电源是否正常。PMIC 的 I2C 模块如果没有得到内部供电,从机根本不会拉低 SDA 应答。此时用示波器看 PMIC 的供电引脚纹波,如果电压在阈值附近反复抖动,也会出现间歇性无应答。

最后上拉电阻很关键。如果上拉电阻太大,或者 SCL、SDA 走线过长,通信速率又开到了 400kbps,上升沿会拉不动,逻辑分析仪看起来波形类似斜坡而不是方波。这种情况从机芯片还能勉强工作,但误码率升高。减少速率、换更小阻值的上拉、缩短走线,三个办法都能解决。

5.2 输出电压偏差与纹波

PMIC 内部 DCDC 的输出电压和设定值差了 100mV 以上,不一定是芯片坏了,先看反馈采样点。很多 PMIC 是从芯片引脚上采样输出电压,如果 PCB 上输出走线太长,负载端的电压就会明显低于芯片检测点电压,因为走线上有欧姆级的寄生电阻。解决办法是让采样走线直接连接到负载端的电容上,形成开尔文连接,避免大电流走线经过采样点。

纹波过大的原因,大多是电感选型不合适。输出电感的值决定了纹波电流的大小,电感量太小纹波大,太大又会影响瞬态响应。选型时按数据手册的推荐电感值范围,不要为了省体积用远低于推荐值的封装。DCR 过高也会在重负载下出现明显压降,所以电感参数不能只看感值,DCR、饱和电流都要检查。

如果加了输出电容后纹波依旧很大,可以看看周围是否有其他高频开关信号耦合,比如同一块板上的无线模块。这种干扰不是 PMIC 自身问题,而是布局问题,把负载走线远离天线和射频走线可以改善。

5.3 充电电流始终不对

设置 250mA 恒流充电,实际测量发现只有 150mA。原因可能有两个方向,一是 USB 输入限流设置太低,PMIC 在输入总功率受限时主动降低了充电电流;二是充电检测电阻附近有其他负载,抢走了电流。我检查后是输入限流寄存器没配,默认值把输入功率限制得很小,导致系统负载一大就无法提供足够的充电电流。

如果你遇到充电电流小于设定值,先不要怀疑 PMIC 的精度,先把所有负载电流估算一遍,再看输入电压是不是被拉低。USB 供电时如果线材电阻大,PMIC 的输入电压下降后会自动减小充电占空比,从而降低电流。重新设置更高输入电流限制、更换更粗的 USB 线和连接器,问题就消失了。

还有一次是充电状态一直卡在预充阶段,电池电压已经 3.8V 了,状态还在预充不跳恒流。查了半天发现是预充到恒流的电压阈值配置被我写错了,预充电压阈值设成了比电池电压高的值,系统认为电池一直处于预充状态。这种问题完全靠示波器看不出来,必须读 STATUS 寄存器才能察觉。

5.4 整机功耗降不下去

睡眠电流比估算值高出几百微安时,第一个要看的就是 PMIC 各路输出是否真的关了。有些 GPIO 控制的负载,看起来供电轨已经关闭,但负载芯片的某些引脚通过 MCU 的 GPIO 灌回了电流,形成寄生供电回路。

我在实际板子上遇到过传感器芯片的 I2C 引脚靠 MCU 的 SCL、SDA 高电平供电,即使 PMIC 那一路电源关了,芯片内部仍然有微弱电流通路。解决方案是在睡眠前把相关 GPIO 全部设为输入,或者输出低电平,不能只想着切断主供电。电源域能关是一回事,信号路径上的漏电也要堵住。

另外一个隐蔽来源是 PMIC 反馈电阻网络。有些 PMIC 的反馈分压电阻一直在消耗电流,虽然单颗电阻有几十微安级别,但整机睡眠目标做到 50uA 时,这一点就非常致命。极低功耗产品可以考虑带负载断开功能的 PMIC 版本,或者让 PMIC 在睡眠模式下断开反馈网络。不过 PCA9422 这类芯片内部设计已经考虑过此场景,重点还是检查外部有没有多余的电阻路径。

6. 一些可以继续做的方向

这套电源管理架构做稳定之后,我顺手做了几个扩展,也建议你可以试试。第一是加入电量统计。PCA9422 提供了充电状态和电池电压信息,但如果没有库仑计,很难精确知道剩余电量。可以外接一颗电流检测电路,让 PIC18LF4620 的 ADC 周期性采样电流,在软件里做积分。虽然不如独立电量计 IC 精度高,但对大多数产品来说已经足够好用。

第二是动态电压调节。把 PMIC 的输出轨和系统运行状态深度绑定,比如 CPU 全速跑时 3.3V 保持稳定,进入轻负载后把电压降到 3.0V,配合 PIC18LF4620 的代码执行频率调整,可以有效降低系统功耗。

第三是参数配置的固件升级预留。因为 PMIC 所有寄存器都能通过 I2C 配置,我建议在固件里预留一个配置表区域,把充电电压、充电电流、各路输出电压、睡眠模式配置都放在统一的数据结构里。这样后续修改电池规格或者更换显示屏时,只需要更新配置表,不用改主程序逻辑,维护成本低很多。

最后提醒一点,做电源管理调试的时候,一定要把寄存器读回来确认,不要只凭示波器看到波形正常就当配置成功。很多 PMIC 支持回读校验,这个功能能帮你少走弯路。我每次写完配置之后,都会顺手把关键寄存器读回来比对一遍,和配置值不一致时直接报错提示,这比出了问题再去翻逻辑分析仪要省事得多。

RELATED READING

延伸阅读

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