做嵌入式产品的这几年,我越来越确信一件事:产品卡顿、死机、掉电丢数据,很多时候不是主控本身的问题,而是电源管理没做好。尤其是电池供电的设备,上电顺序、电压跌落、低功耗切换、异常保护,每一项都能让项目在试产阶段“花式翻车”。这次我想完整记录一下,我是怎么用 PCA9422 这类可编程 PMIC,配合 PIC18F87J50 这颗 8 位 MCU,把一套多路输出、带时序控制、带故障保护、带低功耗切换的电源管理子系统做出来的。
这套方案更适合便携仪表、可穿戴设备、以及“电池 + 多路电源轨 + 需要长时间待机”的嵌入式产品。文章里会讲清楚为什么选这对组合、硬件上怎么布局、寄存器怎么配、固件状态机怎么写,以及打样之后我实际踩到的几个坑。内容偏实战,如果你正准备把“一团散乱的 LDO+DC-DC”升级成“可编程、可监控的电源系统”,这篇应该能帮你省不少时间。
1. 完整电源管理到底要管哪些事
1.1 一次“灵异重启”逼出来的需求清单
我最早做电源设计的时候,思路特别朴素:主控要几伏就给几伏,一个 LDO 不够就加两个,传感器要 5V 就再引一路。第一版样机跑功能没问题,一到整机测试就露馅了。现象很诡异:设备启动的瞬间,屏幕会闪一下,偶尔直接复位,十次里有两三次;有时候按电源键关机再开,系统会卡在初始化阶段。
用示波器同时挂多路电源波形才发现,问题不在软件,而在上电时序。MCU 内核电压和 IO 电压几乎是同时爬升的,但片内外设和外挂传感器上电更慢,MCU 在电压还没稳定的窗口期里就开始读外设,读到的全是乱值,于是 watchdog 误判、复位、重启。这个排查过程折腾了一周,最后结论就一句话:电源管理不是“把电压供上去就行”,它是一套完整的策略。
我把需求拆了一下,大概有这几条:
- 多路输出电压轨必须按顺序建立,每路之间还要有可控的时间间隔;
- 每一路都要有软启动,避免上电瞬间电流冲击把输入电源拉垮;
- 系统运行期间要能监控过压、欠压、过温状态,出问题能及时关断或告警;
- 待机状态下要能把整机功耗压到微安级,而不是靠“把所有芯片 disable”这种笨办法;
- 关机时要有可控的掉电顺序,避免部分电路先掉电、把高电平漏进其它电源域。
这些需求单靠分立电源芯片很难优雅实现,毕竟每路输出用硬件 RC 去凑时序,改一版就要重新调一版。可编程 PMIC 加 MCU 的组合,才是我真正想要的东西。
1.2 电源管理本质上是在做“策略”
如果把电源管理抽象成一句话,我认为它就是在做三件事:建立电压、保持电压、撤销电压。建立时要讲究顺序和斜率,保持时要讲究稳定和效率,撤销时要讲究安全和无残留。这三件事都跟时间强相关,而时间相关的逻辑,最适合放在可编程控制器里做。
PMIC 的强项是高频、模拟、功率部分。它能很快地把输入电压转成多路干净的输出,能检测电流电压温度,能自动在 PWM 和 PFM 模式之间切换以获得效率。但它不擅长做“决策”,比如:当前处于待机模式要不要关掉某一路输出;某一路电压跌落是该复位外设还是该直接关机;故障发生之后是自动重试还是锁死等待人工介入。这些策略如果全用硬件逻辑实现,复杂度会爆炸。
所以我给了自己一个分工:PCA9422 负责把电压变出来、把状态量测出来,PIC18F87J50 负责把电源系统当成一个“状态机”来管。按键开关机、系统休眠唤醒、故障恢复策略、动态电压切换,全部由 MCU 统一决策。PMIC 是“手”,MCU 是“脑”,这样整个电源系统既灵活又可维护。
2. 为什么是 PCA9422 加 PIC18F87J50 这对组合
2.1 PCA9422 的几个让我下决心的特性
选 PCA9422 之前我对比过几颗 PMIC,最后定下来是因为它把“可编程 + 多路输出 + 状态监控”这几个点平衡得比较好。首先是它内部集成了多路 DC-DC 和 LDO,覆盖了数字核心、模拟电路、外设 IO 这些常见电源域,外围电感和电容数量可控,不会把板子面积撑爆。
其次,这颗芯片支持通过 I2C 接口实时调整输出电压和工作模式。这意味着我不需要为了改一版电压去重新换电阻分压网络,直接在代码里写一个寄存器就能在 1.8V 和 3.3V 之间切换。更重要的是,它能把 PFM/PWM 模式配置做成动态的:重负载时用 PWM 保证纹波和动态响应,轻负载时切到 PFM 把静态损耗降下来。对于电池供电的设备,这个切换收益非常明显。
它还提供了中断输出和状态寄存器。当输入电压跌到阈值以下、输出过流、芯片过温时,会有对应的标志位和中断信号通知 MCU。就冲这一点,它就比“瞎子式”的分立电源方案高一个级别。硬件上出了问题,固件至少能知道是哪里出了问题,而不是对着一个万用表盲猜。
2.2 用 8 位 MCU 会不会性能不够
说实话,我第一次把电源管理任务交给一颗 8 位 MCU 时也犹豫过:要不要上 32 位?后来想明白了,电源管理这个场景对算力要求极低,它需要的是实时响应、稳定可靠的 IO 控制、丰富的外设接口和低功耗能力,而不是跑算法。
PIC18F87J50 在这个项目里负责三件事:一是通过 MSSP 模块以 I2C 主模式读写 PCA9422 的寄存器;二是通过外部中断和普通 GPIO 感知按键、PMIC 中断、以及各种电源状态信号;三是用一个主循环加状态机来管理“开机、运行、休眠、故障、关机”这些流程。这些工作对主频要求不高,但对 IO 时序的确定性要求很高,8 位 MCU 反而因为没有复杂操作系统抢占,更容易做到可控。
它还有几个对低功耗设计很友好的点:支持多级休眠模式,休眠时内核电流能压得很低;内部振荡器可以在休眠时继续给 RTC 或者定时器供时钟,这样系统不需要外挂一颗专用 RTC 芯片;外部中断可以作为唤醒源,按一下按键就能从最深睡眠模式下醒过来。这颗芯片对我来说最大的意义是:整个电源管理子系统的“大脑”不需要额外增加一颗昂贵的主控芯片,用系统里本来就要用到的 MCU 就能兼顾。
2.3 两者怎么连:一个最小系统连接方案
实际连接关系很直接:
- PCA9422 的 I2C 引脚(SCL、SDA)接 PIC18F87J50 的 MSSP 模块对应引脚,两根线上各加 2.2kΩ 左右上拉电阻到 3.3V;
- PCA9422 的中断输出脚接 PIC18F87J50 的一个外部中断输入,配置成下降沿触发;
- PCA9422 的各路输出按电源轨规划接到 MCU 和各个外设的电源脚;
- 系统电源按键用一个 GPIO 加 RC 消抖电路实现,同时接到 PMIC 的使能逻辑和 MCU 的外部中断;
- MCU 的 I2C 总线不要漏接去耦电容,注意放在总线靠近主控的一端。
这套连接方案的好处是,电源系统的控制路径和状态监测路径完全独立。MCU 可以通过 I2C 随时修改 PMIC 行为,也可以通过中断线被动等待 PMIC 报故障,两条路径互为备份。
3. 供电路数越多越容易翻车:电源树与 PCB 布局
3.1 先从电源树开始,别急着连线
很多人一上来就画原理图,然后被飞线绕晕。我的习惯是先画一张电源树表格,把每一路输出、电压、最大电流、负载类型和“是否需要受控时序”都列清楚,之后原理图和 PCB 才有依据。下面是我这个项目的电源树:
| 电源轨 | 目标电压 | 主要负载 | 最大电流估算 | 时序要求 |
|---|---|---|---|---|
| VOUT1 | 1.8V | MCU 内核、数字逻辑 | 600mA | 上电第一顺序 |
| VOUT2 | 3.3V | 外设 IO、传感器、LCD | 800mA | 紧随 VOUT1 之后 |
| LDO | 2.5V | 模拟电路、基准源 | 100mA | VOUT2 稳定之后 |
| 备份轨 | 3.0V | RTC、备份 SRAM | 50mA | 常驻,不受开关机控制 |
列完这张表,很多问题就暴露出来了:比如备份轨不能跟着主电源一起掉电,否则 RTC 时间一断电就丢,这路电必须直接接在电池或者备用超级电容后面。再比如模拟电路的 2.5V 对纹波很敏感,如果直接从 DC-DC 输出引,纹波可能超标,所以这一路选择了 LDO 输出。这些都是在画原理图之前就应该定下来的决策。
3.2 电感、电容与测量点
电源设计里最容易被低估的就是无源器件的选型。DC-DC 输出电感如果饱和电流留余量不够,重载瞬间电感值骤跌,输出纹波瞬间变大,甚至可能触发 PMIC 过流保护。我的经验是电感的饱和电流至少按最大输出电流的 1.3 倍留余量,同时注意电感的 DCR,DCR 太大会导致重载效率下降。
输出电容方面,MLCC 的低 ESR 优势很明显,但要注意直流偏压特性。某些小封装的 10μF 电容在加上 3.3V 电压之后实际容量可能只剩 4~5μF,设计裕量时不能只看标称值。输入电容也不能省,放在 PMIC 输入脚附近,用来吸收开关动作产生的电流尖峰,否则输入电压会在开关瞬间出现很大的振铃。
还有一个容易被忽略的细节是测量点。电源调试时免不了要探示波器,如果 PCB 上连一个测试点都没留,就只能把探头扎到芯片引脚附近,不仅危险,测出来的信号还可能受探头地线影响而失真。我为每一路输出都预留了一个小的测试焊盘,并且把地测试点放在功率地入口附近,这样“电源-负载-地”的测量回路面积最小,波形最干净。
3.3 地平面和铺铜的经验
多路电源共地时,我最常遇到的问题就是“地弹”。数字电路的地电流和模拟电路的地电流耦合在一起,轻则 ADC 采样值抖动,重则 MCU 在电源切换瞬间被干扰复位。这个项目里我采用了分区铺铜加单点汇地的思路:数字区的地和模拟区的地在平面上物理隔开,最终汇聚在系统主接地点。
具体操作上,功率级的开关节点下面尽量不要走敏感信号线,避免高频噪声耦合;PMIC 的散热焊盘要打足够多的过孔到地平面,否则满载 1A 以上时芯片表面温度会明显偏高。很多人觉得电源 PWM 开关频率不算高,布线可以随意一点,实际上近场耦合造成的 EMI 问题往往就是从这个“随意”开始的。
4. 寄存器配置:电压、时序和保护阈值怎么落到 PCA9422 上
4.1 上电第一件事:先读回版本号和默认状态
PCA9422 这类芯片出厂时有一套默认配置,但默认值不一定符合你的系统需求。我接到第一片样片时,做的第一件事不是去改电压,而是写一个最简单的 I2C 读操作,把芯片 ID 寄存器读回来,同时确认总线时序和从机地址没问题。
这一步的收益立竿见影。如果连 ID 都读不到,那要么是 I2C 引脚接反了,要么是上拉电阻太小、总线拉不动,要么是芯片本身没正常上电。先把这条链路打通,再谈配置,否则后面所有寄存器操作都会让你怀疑人生。
uint8_t pmic_read_reg(uint8_t reg) { uint8_t val; i2c_start(); i2c_write(PMIC_ADDR_W); // 写方向从机地址 i2c_write(reg); // 寄存器地址 i2c_restart(); i2c_write(PMIC_ADDR_R); // 读方向从机地址 val = i2c_read(); // 读取数据 i2c_send_nack(); i2c_stop(); return val; }我记得第一次读完 ID 后,紧接着做了一轮连续读操作,把芯片所有寄存器默认值都导出来存成一份存档,之后调试时对比默认值和当前值,能非常快地定位“是谁把配置改掉了”的问题。
4.2 输出电压与工作模式配置
配置电压之前,先确定每一路的目标电压和最大负载电流。这个项目里我把 VOUT1 配成了 1.8V,VOUT2 配成了 3.3V,LDO 配成了 2.5V。不同型号甚至同一型号的不同版本,输出电压对应的寄存器字段编码都可能不一样,所以我这里只列逻辑字段,具体数值落到你的板子上时一定要以官方寄存器手册为准。
| 配置项 | 逻辑字段 | 设置值 | 说明 |
|---|---|---|---|
| VOUT1 电压 | VSEL1 | 1.8V | 数字核心 |
| VOUT2 电压 | VSEL2 | 3.3V | 外设 IO |
| LDO 电压 | VSEL_LDO | 2.5V | 模拟电路 |
| DC-DC 模式 | MODE | AUTO | 自动 PFM/PWM |
| 软启动时间 | SS | 1ms | 降低上电冲击 |
| UVLO 阈值 | UVLO | 2.9V | 输入低于则关断 |
配置的顺序也有讲究。我的做法是先配 UVLO 和保护相关寄存器,再配电压和模式,最后才打开输出使能。这样即使配置过程中发生了意外掉电,保护机制也是先就位的,不会出现“电压还没配好就先输出”的危险窗口。
4.3 时序与保护阈值怎么定
多路输出的时序可以通过 PMIC 的 enable 位依次打开来实现。比如上电时先使能 VOUT1,延时 2ms 等电压稳定,再使能 VOUT2,再过 2ms 打开 LDO。这个延时可以由 PIC18F87J50 精确控制,顺序和间隔写在状态机的 RAMP 阶段里。
保护阈值方面,我重点调了三个:输入欠压锁定(UVLO)、输出过流保护和过温保护。UVLO 设得太低,电池电压已经掉到危险区了 PMIC 还在工作,系统容易在低电压下反复重启;设得太高,电池还有余量就被强制关机,用户体验很糟糕。我最后是把阈值定在 2.9V,保证电池放完最后一格前系统能先进入告警状态,然后优雅关机,而不是瞬间断电丢数据。
5. 固件侧的核心:I2C 通信与五态电源状态机
5.1 MSSP 主模式下最容易翻车的几个点
PIC18F87J50 的 MSSP 模块做主模式时,几个容易出错的地方值得专门提醒一下:
第一是 ACK/NACK 的处理。写操作时从机在每收到一个字节后都会拉低 SDA 表示 ACK,MSSP 硬件会自动处理,但读操作结束前主机必须主动发一个 NACK,告诉从机“不要再发了”。如果漏掉这一步,总线会一直处于怪异的半占用状态,后续通信全部乱套。
第二是总线死锁恢复。如果通信中途 MCU 被复位,I2C 总线可能停在从机等待释放的状态。我写了一个简单的恢复函数:把 SCL 引脚临时切换成 GPIO 模式,手动拉 9 个时钟脉冲,再重新初始化 MSSP。这个动作能把处于 ad hoc 状态的从机总线状态机复位,成本极低,但能避免大量“上电偶尔通信失败”的玄学问题。
第三是上拉电阻不能只依赖内部弱上拉。MSSP 的内部上拉太弱,高速通信时波形上升沿会明显变缓。我在硬件上加了 2.2kΩ 外部上拉,把速率定在 400kHz,实测波形和稳定性都很理想。
5.2 五态状态机:把复杂的电源行为管明白
固件里最重要的一段代码是电源管理状态机。我用五个状态覆盖了系统所有可能遇到的情况:
| 状态 | 名称 | 主要行为 |
|---|---|---|
| OFF | 关机态 | 主输出关闭,MCU 保持低功耗,等待按键或中断 |
| PRE_ON | 预启动 | 打开备份轨和基础供电,初始化部分外设 |
| RAMP | 上电斜坡 | 按固定时序依次使能 VOUT1、VOUT2、LDO |
| RUN | 正常运行 | 周期巡检电压、电流、温度,响应故障 |
| FAULT | 故障处理 | 读取故障标志,执行重试或安全关断 |
状态机的核心思想是:任何时刻,系统都必须知道自己处于哪个阶段,以及下一步该做什么。比如在 RAMP 阶段,如果 VOUT2 电压没有在预期时间内达到 POWER_GOOD,状态机不会傻等,而是直接跳到 FAULT,把已打开的电压轨依次关掉,避免 MCU 在电压不足的情况下继续执行复杂逻辑。
5.3 中断里只做标记,主循环里做决策
PCA9422 的中断引脚接到 PIC18F87J50 的外部中断后,我踩过一个典型的坑:刚开始我在中断服务函数里直接读 I2C 寄存器、直接关电源,结果中断函数执行时间过长,偶尔会和主循环里的 I2C 通信发生冲突。
后来改成“中断里只做标记,主循环里做决策”的模式:
volatile uint8_t pmic_irq_flag = 0; void __interrupt() ext_int_handler(void) { if (INTxIF) { pmic_irq_flag = 1; // 只标记事件 INTxIF = 0; // 清中断标志 } } void main_loop(void) { while (1) { if (pmic_irq_flag) { pmic_irq_flag = 0; handle_pmic_event(); // 在主循环里读寄存器、做决策 } power_state_machine(); // ...其他任务 } }这样做的好处是中断处理时间极短,不会阻塞其它实时任务;I2C 操作全部集中在主循环里,避免资源竞争。代价是事件响应有几十毫秒的延迟,但对于电源系统来说,这几十毫秒完全够用,真正紧急的硬件保护在 PMIC 内部已经由硬件完成了。
6. 打样后的实测结果与坑
6.1 开机瞬间反复复位:地弹与被忽略的输入电容
第一版 PCB 打样回来后,我最期待的“开机”测试就翻车了。现象是样机上电瞬间,屏幕刚亮就灭,然后重启,周而复始。我最初怀疑是 PMIC 配置写错了,查了半天寄存器,发现配置流程完全正确。
后来把示波器探头挂在输入电源引脚上,看到了非常明显的跌落:开机瞬间输入电压被拉低了将近 1V,低于 UVLO 阈值,PMIC 立刻关断,关断后输入电压又回升,于是再次尝试开机。问题根源有两处:一是输入端的储能电容容值太小,开机浪涌电流直接把输入拉垮;二是整个 PCB 的输入走线过长,等效电感加重了瞬态跌落。
解决办法也很直接:在 PCB 输入端加大容值的输入电容,并尽量靠近 PMIC;同时把软启动时间从默认值调大一点,让浪涌电流的峰值更平缓。调整之后,开机复位的问题彻底消失。
6.2 纹波测量里的“假数据”
有一次我测 VOUT2 的纹波,示波器显示 50mV 左右,明显偏大。我以为是 PCB 布局问题,正打算重新改版,结果顺手打开了示波器探头的 20MHz 带宽限制,波形立刻干净了一大截,真实纹波只有 18mV。
很多时候,高速开关噪声混在低频纹波里,会让人误判电源质量。测电源纹波的标准做法是:探头使用接地弹簧,不要用长地线夹;开启带宽限制;测量点选在输出电容两端,而不是远端的负载端。另外,不要用示波器默认的 1x 探头档位去测毫伏级纹波,那是拿大炮打蚊子,噪声全进来了。
6.3 休眠电流多出来 100μA,最后是 GPIO 浮空惹的祸
整机电流测试时,系统进入休眠模式后电流始终在 150μA 左右,怎么优化都降不到设计目标的 50μA 以下。逐项排查,最后发现 MCU 上有两个 GPIO 是浮空的,它们的漏电流虽然单看不大,但加起来却成了整机功耗的大头。
另外,I2C 上拉电阻在系统休眠时也一直在耗电,两个电阻各灌约 1.5mA,虽然在 MCU 休眠时并不大,但对我来说这个电流属于“白付的电费”。最后我在休眠前把 PMIC 和 MCU 的通信停掉,同时把上拉电阻改接到一个可关断的电源域,才把这部分电流压下去。
这个坑提醒我:低功耗设计不只是选一颗低功耗 MCU,而是要把每一颗电阻、每一个浮空引脚都当作“漏电嫌疑人”来对待。
6.4 一次 I2C 抢占总线导致的配置错乱
还有一次比较隐蔽的问题:设备正常运行几个小时后,PMIC 输出电压跳变了一下。查日志发现是 MCU 在中断函数里误触发了一次 I2C 读操作(正是我前面提到的那种设计失误),读到的数据后半段全是 0xFF,写回时覆盖了正常配置。
这个问题的修复正是通过“中断只做标记”解决的。同时也让我养成了一个习惯:所有 PMIC 寄存器配置模块,写入时都要带校验读回,并在系统运行期间周期性地把关键寄存器和备份值做比对,一旦发现漂移立刻重新写入。这算是个土办法,但在可靠性要求较高的设备上非常管用。
做完这个项目之后,我自己最大的体会是:电源管理芯片再怎么智能,最终还是要靠上层固件把它“理顺”。PCA9422 提供了优秀的硬件基础,PIC18F87J50 把策略执行得很干净,两者各司其职,整个系统的稳定性才是可维护、可复现的。后面如果再往下走,我打算在故障预测、电池充放电状态估算上加一些更细的算法,但那已经属于另一块大饼了。至少到现在为止,这套“PMIC + MCU 状态机”方案已经陪我稳定跑过了三个项目迭代,每次改电压、改时序都只需要改寄存器配置,再也不用拿着烙铁换电阻了。