做便携式采集设备那次主控选型倒是没纠结太久最后定的是PIC18LF4620——8位、低功耗、外设够用开发起来也简单。真正让我头疼的是电源三路电压轨、锂电池充电、USB供电路径切换、低功耗待机这几件事堆在一起不是一颗LDO能扛住的。当时脑子里其实闪过好几个方案最后在板子上加了PCA9422把电源管理逻辑全部揽下才算是把整个方案理顺了。这篇文章就把从选型、硬件设计到固件实现和调试的完整过程梳理一遍给正在做同类电池供电设备的朋友一个能直接参考的路线。1. 选型逻辑为什么是 PCA9422 PIC18LF4620 这对组合1.1 这个项目要解决什么电源问题先交代一下项目背景。我们当时做的是一个手持式环境监测终端用单节锂电池供电同时要支持USB口充电和固件升级。整个系统需要的电压轨有三路3.3V给主控、传感器和显示模块这是系统最重的一路负载峰值电流能到500mA左右1.8V给外部存储、实时时钟和一部分IO电平转换1.2V给射频前端和内部比较器等高精度模拟模块这一路对纹波比较敏感。除了电压轨之外还得处理锂电池的恒流恒压充电、USB接入时的电源路径切换以及设备进入深度休眠时整个系统的功耗控制。这三件事单独做都不难但凑在一起方案就会变得很乱。最初我确实考虑过全分立方案一颗线性充电IC管充电一颗降压DCDC管3.3V再串两颗LDO管1.8V和1.2V。这套电路非常成熟市面上每一类器件都有大量选择。但真把BOM拉出来一算问题就来了整个电源部分要用到七八个主器件再配合负载开关、使能逻辑、各种容阻PCB面积被吃掉很大一块。功耗也难做——每颗器件本身的静态电流叠在一起待机时整个系统的底电流很容易跑到一两百微安以上。1.2 分立方案为什么被放弃除了面积和功耗分立方案还有一个很麻烦的隐患时序。多芯片方案要把使能脚、复位脚、PG信号互相拉来拉去才能保证上电时各路电压按照正确的顺序建立和撤销。稍微有一个器件的行为和预期不一样轻则外设初始化失败重则逻辑器件闩锁。项目走到后期要改电源参数时分立方案基本只能动硬件每次都是两三天周期。相比之下PCA9422这类集成PMIC把降压转换、低压差稳压、电池充电、电源路径管理和状态监控全部做进一颗芯片里中间的分立器件被大幅砍掉。更重要的是所有功能都由I2C寄存器控制前期调试和后期参数优化都变成软件工作。这正是我最终选择它的核心原因——不是因为它参数有多极致而是因为它让电源管理从硬件活儿变成了软件活儿。1.3 两颗芯片各自的分工这套系统里PCA9422和PIC18LF4620的分工非常明确。PCA9422是执行者负责把输入电压变成多路稳定的输出实时监视电流和温度并在需要的时候报中断PIC18LF4620是决策者负责上电时把寄存器配置灌进去运行中根据系统负载和状态切换电源模式在故障时读取状态并执行复位保护。从职责边界看PIC18LF4620其实没有参与任何电压转换动作它只是通过I2C管理PCA9422。这个分工让我们团队里做驱动的人不用关心电感电容怎么选做硬件的人也不用写一行寄存器配置代码两边靠一份寄存器映射表对接效率高很多。2. 系统电源架构从电池到每一路电压的完整链路2.1 输入侧USB/适配器与电池之间的路径切换先看输入侧。系统输入有两个来源锂电池电压范围3.0V到4.2V和USB口的5V电源。PCA9422内部集成了电源路径管理作用相当于一个自动切换开关当USB或适配器插入时系统负载直接由外部电源供电同时PMIC内部充电器给电池充电外部电源拔出后系统自动切换为由电池供电中间不需要MCU干预。这个机制非常像家里用的UPS不间断电源市电正常时负载用市电顺便给电池充着电市电断电电池立刻顶上电视和电脑不会黑屏。放在设备里好处是插着USB工作时电池不会反复充放电循环寿命明显更好。实际配置中我把充电管理设为单节锂离子电池的标准曲线恒流阶段以设定的电流充电电压到4.2V后转入恒压电流逐渐下降降到终止电流以下就停止充电并给出充电完成标志。这个曲线是PMIC内部硬件完成的MCU只需要在初始化时把目标电压和电流写进寄存器即可。2.2 输出轨设计三路电压怎么分配、怎么接负载PCA9422内部集成了两路降压转换器和一路低压差线性稳压器正好对应系统的三路需求。我的分配方式是输出轨来源目标电压负载对象特点VDD_3V3BUCK13.3V主控、传感器、显示器电流大效率优先VDD_1V8BUCK21.8V存储、RTC、IO电平功耗低占空比小VDD_1V2LDO1.2V射频前端、模拟比较器低噪声纹波优先为什么把3.3V给BUCK1而不是给LDO因为这一路电流大BUCK的效率通常能做到90%以上而LDO压差大的时候效率只有百分之三四十差出来的能量全变成热。反过来1.2V模拟负载电流很小用LDO虽然效率不高但胜在输出纹波极低不会给模拟电路引入开关噪声。电源轨的分配本质就是一个效率优先给重负载纯净优先给敏感负载的过程。3.3V这一路还有一个特殊作用它同时给PIC18LF4620供电。也就是说MCU的VDD直接挂在BUCK1输出上PMIC和MCU形成了一条完整的反馈链MCU通过I2C向PCA9422下发配置PCA9422输出的电压再供给MCU自身。这个闭环一开始让我有点担心启动顺序但实际操作下来PCA9422的默认输出在上电时就能建立MCU随后启动并接管配置时序是安全的。2.3 电源路径管理下各个状态的电流走向为了让整机功耗可控我把系统运行状态分成四挡运行态、轻载态、休眠态、深度休眠态。每一挡对应不同的电源配置运行态BUCK1、BUCK2、LDO全部开启所有外设正常供电轻载态BUCK1、BUCK2开启LDO关闭MCU降频运行外设逐块断电休眠态只保留BUCK1给MCU供电BUCK2和LDO关闭MCU执行SLEEP指令深度休眠态BUCK1切换到轻载节能模式MCU的所有外设时钟关闭只保留唤醒源。这里有个容易被忽略的点BUCK在轻载时如果继续跑在固定开关频率的PWM模式下空载损耗会很高。PCA9422支持自动在轻载时切换到PFM突发模式也就是负载小的时候降低开关频率、跳过若干周期从而把待机功耗压下来。这个切换可以靠硬件自动完成也可以由MCU写寄存器强制指定。我在休眠态一律强制成PFM模式等唤醒后再切回PWM这个切换动作由电源状态机统一处理。3. 硬件实战原理图、选型与布局要点3.1 PCA9422 最小外围电路PCA9422虽然集成度高但毕竟是电源芯片外围的电感电容选型直接决定性能。我第一版原理图的核心元件参数如下输入侧我在USB口和电池接口都加了ESD保护器件同时放置了22uF的陶瓷输入电容。这个电容离芯片的VBUS和VBAT引脚尽可能是毫米级的距离目的是吸收热插拔时的浪涌电流同时稳定芯片内部路径管理的输入阻抗。BUCK路的电感我选了4.7uH额定饱和电流按输出峰值电流的1.3倍留余量。这里要特别说明一下电感饱和电流宁大勿小一旦电感到达饱和点感值会突然跌落电流波形迅速畸变轻则纹波变大重则芯片过流保护。我习惯在选型表里同时看直流电阻DCRDCR越低满载效率越高但也不能无脑选大封装PCB面积在那里摆着。输出电容方面BUCK1和BUCK2都用了2颗22uF陶瓷电容并联再各加一颗0.1uF高频电容吸收开关尖峰。LDO输出则用1uF加0.1uF组合因为LDO负载轻不需要太大容值。3.2 PIC18LF4620 与 PCA9422 的接口连接两颗芯片之间的物理连接非常少真正用得上的只有六根线信号方向作用SCLMCU → PCA9422I2C时钟SDA双向I2C数据INTPCA9422 → MCU中断请求ENMCU → PCA9422芯片总使能GPIO1PCA9422 → MCU电源插入状态GPIO2PCA9422 → MCU充电状态 / 充电完成I2C的两根线都加了上拉电阻这里有个坑上拉电阻阻值不是随便选的。我总线跑100kHz时用10kΩ跑400kHz时换成了2.2kΩ。原因很简单上拉电阻太大会让上升沿变缓在400kHz下容易触发通信错误太小又在低功耗状态白白增加电流。如果你系统里I2C总线很短、器件又少甚至可以试试4.7kΩ作为折中值我们后期量产板就是这么做的。INT引脚接在MCU的RB0上配置为下降沿触发。PCA9422检测到充电完成、输入电源切换、输出电压跌落、过温等事件时会拉低INTMCU在中断里读状态寄存器区分具体原因。EN引脚我没有直接接VCC而是用MCU的一个普通IO控制这样可以在软件里执行完整的下电流程而不是靠外部电路瞬间切断电源。3.3 PCB 布局中的电源与信号处理电源板的布局规则说白了就三条回路短、地完整、噪声别串进信号。BUCK的开关节点是整块板子上噪声最凶的地方电流变化率极大。我把电感摆得离芯片SW引脚尽可能近并且在SW节点的正下方不铺信号线而是保留完整的地平面。这一点非常重要开关节点的高频噪声如果耦合进I2C的SDA走线轻则通信偶发失败重则整个系统莫名其妙复位排查起来非常痛苦。另外模拟的1.2V LDO输出我用了一小块独立的铺铜与数字地通过单点连接。虽然在低频电路里数字地模拟地分开有时候被认为是玄学但对带射频前端的系统来说这种隔离确实能把底噪压下去几个dB值得做。板子做完之后我还预留了三个输出电压测试点和一个I2C测试点。测试点这东西看着不起眼调试的时候省太多事了——探头一夹就能看到波形不用拿万用表到处找引脚。4. 固件实现从 I2C 裸机驱动到模式切换4.1 I2C 通信层的实现PIC18LF4620自带MSSP模块可以工作在I2C主机模式所以通信层完全不用软件模拟时序。我基于XC8编译器写了一套精简的I2C读写函数核心思路就是把MSSP的几个寄存器操作封装成start、stop、write、read四个原语再组合成寄存器的读写接口。初始化部分关键代码如下void i2c_init(void) { // 设置I2C主机模式 SSP1CON1 0b00101000; // 8MHz系统时钟下SPPADD49 得到约100kHz的SCL频率 SSP1ADD 49; // 使能MSSP模块 SSP1CON1 | 0x80; }接着是面向PCA9422的寄存器读写函数#define PCA9422_ADDR 0x6C uint8_t pca9422_read_reg(uint8_t reg) { uint8_t val 0; i2c_start(); i2c_write(PCA9422_ADDR 1); // 主机写寻址PMIC i2c_write(reg); // 指定寄存器地址 i2c_restart(); i2c_write((PCA9422_ADDR 1) | 1); // 转为读方向 val i2c_read(0); // 读一字节发NACK i2c_stop(); return val; } void pca9422_write_reg(uint8_t reg, uint8_t val) { i2c_start(); i2c_write(PCA9422_ADDR 1); i2c_write(reg); i2c_write(val); i2c_stop(); }封装的好处是整个固件其他模块根本不用关心总线时序需要调PMIC参数时直接调这两个函数就够了。实际运行中我测过100kHz下每次寄存器写操作耗时不到0.5毫秒对电源切换来说绰绰有余。4.2 PCA9422 寄存器配置序列芯片上电后PCA9422会用一组默认参数工作但默认参数不一定匹配我们的系统所以PIC复位后要尽快写入配置。我整理了一份配置表用一个结构体数组维护寄存器地址和期望值初始化时统一灌入typedef struct { uint8_t reg; uint8_t val; } pca9422_cfg_t; const pca9422_cfg_t pmic_init_cfg[] { {0x02, 0x01}, // 使能全部输出轨 {0x03, 0x2C}, // BUCK1输出电压 3.3V {0x04, 0x1C}, // BUCK2输出电压 1.8V {0x05, 0x12}, // LDO输出电压 1.2V {0x08, 0x20}, // 充电目标电压4.2V恒流400mA {0x0A, 0x1F}, // 屏蔽部分不关心的中断源 // ... 其他寄存器 }; void pca9422_init(void) { uint8_t i; uint8_t count sizeof(pmic_init_cfg) / sizeof(pca9422_cfg_t); // 确保EN引脚为高先让PMIC工作 EN_IO 1; for (i 0; i count; i) { pca9422_write_reg(pmic_init_cfg[i].reg, pmic_init_cfg[i].val); } }有一点要提醒寄存器映射表在不同芯片版本之间可能存在差异。我写进代码之前专门把数据手册里的寄存器定义和头文件逐一核对过还通过0x00设备ID寄存器读回了芯片版本号确保我的配置表和实际硬件对应。硬件芯片的版本字段和生产批次有关拿到新的批次最好在样机上读一遍确认。4.3 电源状态机的实现要点电源管理最忌讳散落式代码——这里改一个寄存器、那里切一个引脚时间一长自己都搞不清系统处于什么状态。我用了一个简单状态机把所有模式切换统一管理typedef enum { PM_RUN, PM_LIGHT, PM_SLEEP, PM_DEEP_SLEEP } pm_state_t; void pm_set_state(pm_state_t new_state) { switch (new_state) { case PM_LIGHT: pca9422_write_reg(PCA9422_REG_EN, 0x03); // 关闭LDO MCU_LIGHT(); break; case PM_SLEEP: pca9422_write_reg(PCA9422_REG_EN, 0x01); // 保留BUCK1 pca9422_write_reg(PCA9422_REG_MODE, 0x02); // BUCK1进入PFM break; case PM_DEEP_SLEEP: pca9422_write_reg(PCA9422_REG_EN, 0x01); // 只留BUCK1 pca9422_write_reg(PCA9422_REG_MODE, 0x03); // 轻载最优化 break; default: // 从状态机之外的所有入口统一先恢复全供电 break; } current_state new_state; }每次状态迁移都只经过这一个入口避免同时两处代码对PMIC发出冲突指令。实际调试时我在入口处放了一个调试变量记录当前状态调完功耗再统一关闭非常省心。进入休眠前还有一个细节要先把I2C总线释放干净确保PMIC不会因为总线上的残留时序误动作。我的做法是在休眠前先把SDA和SCL配置为普通IO输出高电平唤醒后再重新初始化MSSP模块。这能规避掉一批诡异的低功耗唤醒失败问题。4.4 中断与状态监控电源管理不能只靠MCU单方面命令还需要PMIC主动上报异常。PCA9422的中断控制器会在充放电路径切换、充电完成、过压过流、芯片过温等场景下触发INT引脚拉低。我的中断处理逻辑非常直接中断标志置位后第一时间读状态寄存器并把状态值存进一个全局变量主循环里再做详细处理。void __interrupt() ISR(void) { if (INTF) { INTF 0; pmic_status pca9422_read_reg(PCA9422_REG_STATUS); pmic_int_pending 1; } }主循环里读取pmic_int_pending后会根据状态位做对应动作。例如检测到充电完成就把充电状态灯熄灭检测到输入电源拔出就把系统策略切到电池优先屏幕亮度降一档。这套中断机制让MCU完全不需要轮询PMIC状态待机时MCU睡得很沉PMIC一有事才会把它叫醒对功耗优化很关键。5. 实测数据与波形5.1 电压精度与负载调整率板子回来之后第一件事就是测各路电压。空载情况下三路输出和设定值之间的偏差都在1%以内这精度对系统里的数字器件和模拟前端都足够了。满载情况下观察负载调整率也很理想输出轨设定值空载实测满载实测最大偏差VDD_3V33.30V3.31V3.28V0.9%VDD_1V81.80V1.80V1.79V0.6%VDD_1V21.20V1.20V1.19V0.8%这个结果说明BUCK的环路补偿和电感电容搭配是合理的。如果哪一路在负载动态变化时出现明显跌落或振铃第一件事不是改寄存器而是查LC参数是否匹配PMIC的期望范围。5.2 整机功耗与续航估算整机功耗是我这个项目最关心的指标之一。用精密电流表分别测量四挡状态下的整机电流电源状态MCU状态PMIC状态整机电流运行态8MHz全速运行PWM模式三路全开132.0mA轻载态降频运行BUCK1/BUCK2开启LDO关闭11.8mA休眠态SLEEP仅BUCK1 PFM模式42uA深度休眠态深度SLEEP轻载最优化配置9.6uA深度休眠态9.6uA的数字让我比较满意。这里要强调一下测量方法测休眠电流时不能直接在电源输入端串万用表看平均电流因为系统会周期性唤醒执行任务平均法会把峰值电流摊进去掩盖真实底电流。我是在唤醒源引脚外接信号发生器触发一次唤醒用示波器电流探头看整个周期内的电流波形把真正休眠段的平台电流读出来。按这个数值配一块1000mAh电池纯待机续航可以按天算实际使用中就算每天唤醒几十次执行测量任务也能撑过一周。5.3 瞬态响应与纹波改善电源管理方案最怕负载突然从几十毫安跳到几百毫安。我用电子负载做了100mA到500mA的阶跃测试示波器看到3.3V输出有约60mV的跌落恢复时间大概40us这个动态响应对于数字主控和普通传感器完全没问题。纹波方面PWM模式下3.3V输出的开关纹波在15mV左右1.2V的LDO输出纹波在5mV以内。如果产品对纹波要求更苛刻我建议在输出端再加一级LC滤波或者增大输出电容但我这个项目没那么敏感保持现状即可。6. 调试踩坑总结从能跑到稳定跑6.1 首版板子不上电的教训第一版样板焊接完成后上电的一瞬间整块板子纹丝不动3.3V输出完全没有。当时第一反应是PCA9422没工作查了一圈才发现问题出在EN引脚EN被误接为默认低电平芯片处在完全关断状态。虽然数据手册上写了EN内部有下拉但我没有在原理图阶段就给EN外接一个合适的上拉或明确由MCU控制导致芯片一直被关在笼子里。从此以后凡是带使能引脚的电源芯片我都要在原理图评审时逐颗确认三件事默认状态是什么、复位期间是什么状态、MCU接管之前会不会有冲突。让MCU的IO去控制EN没有问题但必须保证MCU复位期间IO状态是确定的高电平或低电平否则会出现上电时序的毛刺。6.2 I2C 通信不稳定的根源调试中间遇到过I2C通信偶发失败现象是读寄存器有时返回0xFF多试几次又恢复正常。一开始怀疑是MSSP模块配置问题查了很久代码最后发现是飞线导致的调试时我用一组长导线把PCA9422的I2C接口引出来接逻辑分析仪导线上的分布电容和寄生电感把总线上升沿拖垮了。把长飞线撤掉、改用板载测试点后通信立刻稳定。这件事情说明I2C的速率和线缆长度是强相关的低速100kHz也架不住半米长杜邦线的寄生参数。批量验证时如果必须通过延长线连接我会把I2C频率降到50kHz以下或者采用隔离缓冲器。6.3 待机电流居高不下第一版固件完成后深度休眠电流测出来是60多微安和预期的10微安差距很大。逐个部件排查后定位到PCA9422的BUCK还持续工作在PWM模式——默认配置没有自动切换PFM空载时依然保持高频开关状态白白消耗几毫瓦功率。改为在进入休眠前显式强制PFM模式之后底电流立刻降到了预期范围。这个坑提醒我很多PMIC的默认工作模式是完全独立于MCU的思考方式的集成度再高软件层面的功耗策略不到位硬件能力也发挥不出来。最好的习惯是每进一个睡眠状态代码里都要重新明确所有电源轨的工作模式和使能状态不要依赖芯片出厂默认值。6.4 充电发热与散热处理还有一次充电测试让我印象深刻。整机放在密闭外壳里以500mA电流充电十几分钟后芯片表面温度逼近60℃外壳摸上去微微发烫。虽然这个温度对芯片本身没有威胁但锂电池对温度敏感长期在高温环境下充电会加速老化。处理办法是我在固件里把充电电流降低到300mA同时把充电座底部铺铜范围加大利用铜皮散热。降流之后充电时间虽然长了大概半小时但整个充电过程外壳温度稳定在40℃以下更稳妥。对便携产品来说充电速度从来不是越高越好必须结合散热条件和电池规格做一个平衡。打样到现在这套方案前后迭代了三版整体运行很稳定。如果你正准备把PCA9422和PIC18LF4620用在类似的电池供电设备里我的建议是硬件层面多花时间核对电感饱和电流和EN上电时序固件层面尽早把电源状态机搭起来别让寄存器配置散落在各个业务模块里。电源管理这种活前期规划越刻意后期量产越省心。后面我打算把这套状态机框架单独抽出来做一个通用模块到时候再整理一篇文章。
阅读完成 · 觉得有帮助?