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

文章详情

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

可编程PMIC实现多路电源管理:PCA9422与PIC32MX695F512L实战

可编程PMIC实现多路电源管理:PCA9422与PIC32MX695F512L实战 做嵌入式这些年我最大的感触是十块板子里有八块电源设计是能用但不敢碰的状态。尤其是带电池、带多路DDR/传感器/无线模组的设备MCU内置那两路DCDC加几个LDO根本转不开更别提上电时序和动态调压这种讲究活。之前接手一个手持设备项目MCU用的就是PIC32MX695F512L系统里四路电压轨要求严格错峰上电还得在休眠态把核电压主动拉低光靠芯片自带电源模块根本做不到。后来我把整条电源链路改成由PCA9422这颗可编程PMIC统一管理MCU通过I2C总线实时配置输出、切换状态、回读电压电流才把问题彻底解决。这篇文章就把这套PCA9422 PIC32MX695F512L完整电源管理方案从头到尾拆开讲为什么需要独立PMIC、硬件上怎么连、软件驱动怎么写、实测会遇到哪些坑以及还能往外扩展什么。适合正在做低功耗手持设备、电池供电产品或者想搞明白MCU如何管住多路电源的嵌入式工程师参考。1. 为什么要用一颗独立PMICMCU自带电源轨的极限1.1 单芯片上电方案的局限在哪里PIC32MX695F512L这颗MCU外设确实够全USB、CAN、UART、ADC一路不缺Flash也有512KB跑一个中等复杂度的固件没问题。但它的内置电源管理能力很弱典型的供电接口就是一组VDD/VSS加上AVDD/AVSS内部虽然有稳压电路但外部还得自己给内核和I/O分配电压。实际项目里根本不会用它去直接带动多个负载因为MCU自带的LDO只是为自身供电设计的带载能力、纹波抑制、时序控制都谈不上。更麻烦的是多电压轨设备的要求传感器要3.3V、无线模组要3.3V但需要单独控制供电开关、DDR或者某些模拟前端要1.8V、MCU内核如果允许还要在休眠时降到一个更低电压。这些需求如果全靠分立LDO加MOS管开关搭板子上会多出八九个器件每个器件的使能脚还得MCU用GPIO单独控制时序靠软件反复延时去凑。结果就是硬件上零散、软件里全是延时、调试时一塌糊涂。1.2 独立PMIC解决了哪几类具体问题PCA9422这类可编程PMIC本质是把多路DCDC/LDO 时序控制 状态切换 电压回读集成到一颗芯片里MCU只需要通过I2C读写寄存器就能控制整棵电源树。它解决的问题可以归成三类一是时序问题。独立PMIC内部有上电序列逻辑哪一路先升、哪一路后升、各路之间间隔多少可以通过寄存器配置。设备上电时MCU还没运行固件电源树也能按既定顺序起来这在MCU复位之前就保证了系统状态正确。二是功耗问题。动态电压调节DVS是这类PMIC的标配能力MCU全速跑的时候把核电压设到高位进入休眠时通过I2C写一次寄存器就能把电压降下来省下的功耗非常可观。用分立LDO方案很难做到这种代码可控的降压。三是监测和故障保护。很多可编程PMIC内置ADC能回读输入电压、各路输出电压和输出电流。MCU随时可以知道现在整板到底在耗多少电、电压有没有跌出了问题还能触发中断处理。这比拿万用表手工量强太多了。1.3 为什么是PCA9422搭配PIC32MX695F512L选这套组合没有特别玄学完全是项目约束下最顺手的选择。PIC32MX695F512L的优势是外设强、管脚多、官方库函数齐全做人机交互加通信协议栈都够用PCA9422这边属于通用性很好的I2C可配置PMIC输出电压轨多、封装适中、寄存器映射清晰而且它本身不挑MCU平台不管你是PIC、AVR还是ARM只要I2C从机能读能写就行。还有一个关键点PIC32MX695F512L的I2C外设支持100kHz和400kHz两种模式PCA9422的I2C从接口也兼容这些速率两边对得上。很多PMIC只支持快速模式如果MCU端只能跑标准模式也能通信但速率上要注意别超出。这套方案里CPU负责策略、PMIC负责执行职责边界非常干净。2. 硬件链路设计PCA9422 与 PIC32MX695F512L 的握手细节2.1 先把各路电压需求列成矩阵画原理图之前我习惯先把整板的电压需求拉一张表每个电压轨对应哪些负载、最大电流大概多少、要不要可控开关、上下电有没有顺序要求。不要嫌这一步啰嗦后面寄存器配置全靠这张表撑腰。以我当时的手持设备为例大致是这么个情况电压轨主要负载典型电流时序要求休眠时状态VBUS输入电池或USB输入充电时最大1.5A最先出现常驻3.3V_IOMCU I/O、传感器、LCD200mA第一路升压保持输出1.8V_COREMCU内核、DDR接口350mA第二路升压降为0.9V或关断3.3V_RF无线模组电源峰值500mA第三路由GPIO使能直接关闭3.3V_ADC模拟前端基准50mA紧随CORE关闭这张表同时定义了PMIC的通道分配VBUS走输入直通3.3V_IO和1.8V_CORE由PMIC的DCDC通道输出3.3V_RF和3.3V_ADC可以由PMIC的LDO通道带不够的话LDO输出外面再加一颗MOS开关做负载关断。每路电压都要想清楚上电顺序正常工作时电压休眠时电压这些最后都会变成寄存器里的数值。2.2 I2C接口与使能引脚的连接细节PCA9422与PIC32MX695F512L之间的控制链路其实就三根线SDA、SCL、INT。SDA和SCL接到MCU的I2C外设引脚INT则接到MCU的一个外部中断输入。硬件上要注意几个容易被忽略的点。第一个是I2C地址的选择。PCA9422通常有至少一个地址配置引脚比如A0、A1硬件上接到VDD或GND就能组合出不同从机地址。如果板上以后可能挂两片PMIC或者I2C总线上还有其他设备一定要在原理图阶段就把地址错开。我当时用的是默认地址省事但原理图上留了地址引脚的跳线电阻位方便以后改。第二个是线上拉电阻。I2C总线需要上拉上拉电阻值按总线速率和总线上挂的设备数量来选。400kHz下一般选2.2kΩ到4.7kΩ设备多的话适当往小的方向走。PCA9422数据手册通常会给出推荐范围照着手册选就不会有大问题。第三个是INT引脚。PMIC的故障中断输出一般是开漏结构外部得加上拉电阻然后接到MCU的中断脚。MCU这边配置成上升沿或下降沿触发具体看PMIC的中断极性。这里最容易翻车的是忘了加上拉导致中断信号永远拉不起来故障触发时MCU完全无感知。2.3 电平匹配与默认启动状态MCU没跑起来之前谁提供电压这是一个很多人会漏掉的连环问题PIC32MX695F512L上电启动时需要I/O供电而I/O供电又来自于PMIC的某一路输出。那么PMIC自己还没被配置之前它输出的默认电压是多少够不够让MCU正常启动可编程PMIC一般有两种做法一种是通过外部设定电阻或者由内部OTP一次性可编程存储在出厂时固化一组默认电压和时序另一种是支持启动后保持默认配置、MCU可覆盖的模式。PCA9422就属于后者。我们在设计时把3.3V_IO这一路设为PMIC默认上电即输出的通道默认输出电压配到3.3V这样MCU一上电就能获得稳定的I/O供电开始运行固件等固件跑起来之后再由软件去配置其余通道。还有一个细节是使能脚EN的处理。有些PMIC的EN脚可以由MCU的GPIO控制用来做系统级的软关机/硬关机。我在原理图里把EN接到一个GPIO并且在GPIO旁边放了RC延时目的是防止上电瞬间GPIO随机翻转导致PMIC被意外关断。MCU启动期间GPIO默认是输入高阻RC电路能保证EN在复位窗口期内一直保持有效电平。这个细节救过我一次后面调试章节会细说。3. 软件侧的关键逻辑上电状态机与动态电压调节3.1 初始化流程I2C外设配置与设备探测硬件焊好之后软件的第一步永远是从I2C通信开始。PIC32MX695F512L上使用官方外设库配置I2C很简单选好引脚复用功能设定波特率开外设然后发一个起始条件扫描设备地址。我习惯写一个pmc_probe()函数让它去读PMIC的ID寄存器。读不到或者读到全FF说明要么地址不对、要么总线有问题、要么芯片没上电。这个探测步骤看起来多余但在批量调试时能少走很多弯路——先把PMIC到底在不在总线上确认掉再谈后续。地址注意PCA9422的I2C地址是7位地址读写时还要拼上读写位。如果你用的库函数需要传8位地址要记得左移一位。这个细节几乎每个第一次用I2C驱动PMIC的人都会栽一次我当时排查这个地址位移就花了半个下午。3.2 寄存器配置启动序列、限流与反馈补偿初始化PMIC的寄存器组我建议按下面的顺序来先配置全局寄存器比如软复位、中断屏蔽。确保芯片处于一个已知的干净状态。再配置每一路DCDC/LDO的输出电压和使能位。对照物理设计的电压表把寄存器填进去。配置上电时序寄存器。PCA9422这类芯片通常支持事件触发式的序列第0路电压到位后延时T1再开启第1路第1路到位后再延时T2开启第2路。这个延时值要跟负载的上电要求对齐比如MCU内核电源与I/O电源之间一般要求几毫秒的间隔。配置限流和过压/欠压保护阈值。这个不要用默认值务必按实际负载电流设置。设太大会让PMIC在短路时因为保护阈值太高而扛很久设太小又会在负载尖峰时误触发保护。电压精度和纹波相关的寄存器也得留意。有些PMIC内部DCDC转换器的开关频率是固定的有些可以配置。开关频率高纹波容易压得住但效率会稍低频率低则效率好、纹波大。手持设备一般推荐把DCDC配置在高的开关频率档这样电感能选小一号纹波也更干净。3.3 动态电压调节与系统状态机真正让这套方案从遥控电源开关升级成完整电源管理的是系统状态机。我把它定义成三个状态RUN、SLEEP、SHUTDOWN外加一个过渡状态。RUN态下MCU全速运行PMIC各路电压按正常值输出。进入SLEEP前MCU先通过I2C把核电压从1.8V降到0.9V同时关掉RF通道和ADC基准通道然后才执行休眠指令。唤醒的时候反过来先恢复核电压再使能其他通道等PMIC报告各路上电完成后再继续跑代码。这个顺序如果倒了轻则唤醒后行为异常重则把模拟前端拉到不稳定的欠压状态。SHUTDOWN态则由EN引脚控制MCU在关机前把需要保存的数据落Flash然后主动拉低ENPMIC按配置的关断时序逐路掉电。这里要注意的是EN拉低后PMIC内部逻辑也会掉电I2C会失去应答所以固件里不要奢望关机后还能通过I2C回读状态一切要在掉电前完成。3.4 软件流程伪代码写一段伪代码示意核心逻辑实际工程里按这个骨架填充就行void pmic_init(void) { i2c1_init(I2C_CLK_400KHZ); pmc_wait_device_ready(PMIC_I2C_ADDR); // 读ID寄存器 pmc_soft_reset(); pmc_set_voltage(CH_DCDC1, 3300); // 3V3_IO pmc_set_voltage(CH_DCDC2, 1800); // 1V8_CORE pmc_set_voltage(CH_LDO1, 3300); // 3V3_RF pmc_set_voltage(CH_LDO2, 3300); // 3V3_ADC pmc_set_sequence(PWR_SEQ_LUT); // 上电时序表 pmc_set_limits(CH_DCDC2, 450, 1500); // 限流过压阈值 pmc_enable_all(); }void system_enter_sleep(void) { pmc_set_voltage(CH_DCDC2, 900); // 核电压降为0.9V pmc_disable_output(CH_LDO1); // 关闭RF pmc_disable_output(CH_LDO2); // 关闭ADC基准 pmic_state STATE_SLEEP; enter_sleep_asm(); // MCU进入休眠 }void system_wakeup(void) { pmc_set_voltage(CH_DCDC2, 1800); // 先恢复核电压 pmc_enable_output(CH_LDO1); // 再开RF pmc_enable_output(CH_LDO2); // 再开ADC基准 while (pmc_check_power_good() 0); // 等待电源就绪 pmic_state STATE_RUN; }代码里要特别注意pmc_set_voltage()是否阻塞等PMIC确认。多数PMIC的I2C写入只是把目标电压写进寄存器实际电压爬升是硬件完成的所以软件里紧接着读回电压状态时要留出电压稳定的时间。我一般会在电压切换后加一个1ms到2ms的小延时再读状态寄存器让DCDC的输出电容完成充放电。4. 实测数据与排障记录时序余量、纹波和I2C通信4.1 上电时序的实测方法配完寄存器第一次上电别急着跑应用先拿示波器看各路电压的爬升顺序。方法是用四通道同时抓VBUS、3V3_IO、1V8_CORE和RF通道触发沿设在VBUS上升沿时基放到每格20ms左右就能清楚看到各路之间错峰启动的间隔。正常情况应该是VBUS先到然后3V3_IO延迟几毫秒开始爬升接着1V8_CORE再延迟几毫秒爬升。每路的上升沿都应该是平滑的单次爬坡不带回沟。如果看到电压先上冲再回落说明DCDC的软启动时间设得太短或者补偿参数不合适。如果是两路同时开始爬升去检查时序寄存器大概率是某个使能位提前置1了。4.2 我踩过的两个典型坑EN引脚的启动抖动与I2C总线失联第一个坑在EN引脚上。最开始设计时我把EN直接接到MCU的GPIO没有加RC延时。结果在MCU上电复位那几百毫秒里GPIO处于未初始化状态内部上下拉未知EN被随机拉高拉低了几次。现象就是PMIC反复启停3.3V输出抖动MCU都跑起来了PMIC还在那抽搐。后来在EN到地之间并联了一个100nF电容再串了一个10kΩ电阻到GPIO形成RC延时让EN在MCU启动期间保持稳定高电平问题立刻消失。第二个坑更隐蔽I2C总线跑着跑着就失联PMIC不回ACKMCU端I2C状态机卡死在等待应答。查来查去发现是SDA线上挂了太多设备总线电容过大400kHz下的上升沿已经不满足I2C规范的要求。把总线速率降回100kHz之后通信恢复正常。后来我把板上总线上拉电阻从4.7kΩ改到2.2kΩ再跑400kHz也很稳定。这里要提醒的是不要想当然认为I2C是低速总线就随便布线PMIC的I2C引脚如果走线过长且没有地线包裹照样会有信号完整性问题。4.3 功耗实测对比动态调压和负载关断省了多少做完状态机之后我专门测了一轮功耗用的是一块精度比较好的电流表串联在输入回路上分别记录RUN态、SLEEP态和SHUTDOWN态的整板电流系统状态核电压RF通道ADC通道整板输入电流RUN全速运行1.8V开启开启145mARUN空闲循环1.8V开启开启88mASLEEP降核压关外设0.9V关闭关闭23mASHUTDOWNEN拉低关闭关闭关闭0.6mA从88mA降到23mA主要功劳就是核电压降了一半加上关掉了RF和ADC两路负载。这个差距在电池供电设备里非常显著如果电池容量是2000mAh88mA下的续航约22小时SLEEP态下能撑到87小时。如果你的设备有大量时间处于等待唤醒状态动态电压调节方案带来的收益比单纯优化MCU代码大得多。5. 这套方案还能怎么扩展故障记录与低功耗策略5.1 把PMIC的ADC回读变成完整的电池监测PCA9422这类PMIC如果内置了输入电压和输入电流回读功能别浪费直接用起来。固件里开一个定时任务每秒钟读一次输入电压和系统总电流做一阶滤波后存到环形缓冲区。当检测到输入电压低于设定阈值时判定电池进入低压区间触发低电量告警电流突然升高且持续时间超过阈值时可以怀疑有负载异常或者是瞬时短路。这些数据不只是给自己看还可以通过串口或者USB传给上位机。之前在某设备上我就是把功耗日志格式化输出接入一个简单的上位机看板工具整板的功耗曲线一目了然找问题快多了。5.2 故障中断与看门狗的联动PMIC的INT引脚接进MCU的外部中断后中断服务函数里第一时间去读PMIC的中断状态寄存器判断到底是过压、欠压、过流还是过热触发了中断。记录现场后根据故障严重程度决定处理策略故障类型处理策略某路过压先记录寄存器状态再软复位PMIC重配电压某路欠压检查使能状态连续两次欠压则进入SHUTDOWN过流若是瞬时尖峰只记录持续超过阈值则关闭该路过热立即进入SLEEP态降低整板功耗等待降温我在实际项目里把INT中断和MCU内部看门狗联动起来如果PMIC报出欠压但MCU主循环还卡在某个等待上看门狗会先复位MCU复位后固件读PMIC的中断状态发现是欠压故障就跳过有问题的初始化段直接进入低功耗状态。这套组合让设备在故障后不会反复死循环重启而是稳定落在一个安全状态。5.3 通过串口/USB记录电源事件日志最后再分享一个扩展点电源事件日志。每次PMIC状态切换、每次故障中断、每次动态电压调节固件都往日志区写一条带时间戳的记录。这个日志可以放在内存环形区也可以通过物理接口外发。设备在实验室里怎么跑都正常一到现场就偶尔重置这种问题最难查——有了电源事件日志就可以让现场人员把设备接上串口输出回来分析日志看到底是电压跌落还是过流触发。我最后那版固件就是这样处理的日志里能看到某次唤醒之后1V8_CORE电压爬升花了12ms而其他时候都是7ms再结合当时温度传感器数据定位到是低温环境让PMIC的软启动时间变长了。这种细节没有日志光靠示波器蹲守根本抓不到。批量调试的时候这个日志功能还能反过来用同一批机器上电后各读一条PMIC寄存器快照回传对比一下就知道哪台机器的电源配置在产线上被意外改过省掉了一大半售后排查的时间。电源管理做到这个程度才算真正完整。
返回列表