资讯详情

资讯详情

基于PCA9422与STM32F103的低功耗电源管理设计与实现

做低功耗设备最烦的一件事往往不是代码跑死而是电。前阵子给一个电池供电的采集类装置重做电源部分主控用的就是STM32F103RC外围不但有传感器、通信模块还有屏幕背光和一堆不同电平的接口电压轨从1.8V到5V都有。以前的做法是DCDC、LDO、充电管理各用各的芯片板子上电源相关器件占了一大块软件侧还要分别伺候哪一路起来了没起都不知道。后来换成PCA9422这颗电源管理芯片配合F103做完整的电源管理板子简洁了很多待机功耗也真正压了下来。这篇就把我从硬件设计到固件实现再到调试排错的过程完整捋一遍给正在做电池供电设备的朋友一个参考。1. 为什么一个老掉牙的F103项目也需要正经的电源管理1.1 多路供电和低功耗同时压过来STM32F103RC虽然是一颗十年前的Cortex-M3芯片但在电池供电设备里依然大量存在。它资源足够、外设丰富价格也稳定很多人觉得老片子随便搞搞就行但电源这部分真不是随便搞搞就能过的。我当时做的装置大概有这几路负载主控和传感器走3.3V通信模块需要一路可调的2.85V到3.3V电压还有一路1.8V给少数低速接口的电平转换再加上电池充电管理和整机待机电流的约束。说白了这就是典型的一个小系统里有多个电压域还要兼顾低功耗的场景。用跳线和LDO拼凑也许能点亮但到了性能和功耗都不达标的阶段就不得不回到电源管理本身重新设计。1.2 分立方案的三个痛点之前版本用的分立方案是一颗升压/降压DCDC出3.3V一颗LDO出1.8V再加一颗专门的充电管理芯片电池电压监测靠主控ADC去读。听着也不算太复杂可是真调试起来很折磨人。首先是时序不可控。各路输出的上下电顺序靠RC延时或者使能脚顺序去凑有时候DCDC还没稳定MCU已经开始跑了偶尔出现上电失败复现起来全靠运气。其次是低功耗协同很难做。每颗芯片都有自己的使能、状态脚和工作模式进入睡眠状态时要分别操作漏关一路就可能多耗几十微安。再一个是器件多、BOM面积大电源部分在PCB上占了将近三分之一的面积这对于便携设备来说完全不能接受。1.3 PCA9422加F103的组合解决的是什么PCA9422是一颗面向低功耗便携设备的电源管理芯片内部集成了充电管理、多路降压转换器、LDO以及ADC检测等功能外层接口不过是I2C加一个中断脚。主控通过I2C就能同时控制输出电压、充电电流、工作模式和状态读取F103专注应用逻辑电源策略全部通过寄存器调度。这种组合最大的价值在于可编程三个字。以前换一颗传感器就要换一路LDO现在只需要重新配置对应输出通道的电压寄存器。以前睡眠模式要反复试硬件方案现在只要在固件里切换PMIC的工作状态。所以即便F103本身不算新把PCA9422加进来之后整个系统的电源设计思路还是能紧跟低功耗产品的主流做法的。2. PCA9422的电源架构把它拆开看清楚再设计2.1 从充电到输出一颗芯片里的完整链路我第一次看PCA9422的原理框图时最大的感受是这一颗芯片基本把电池供电设备的前端电源全部包圆了。输入侧可以接USB的5V或者适配器电源内部有线性充电管理单元充电电流可配置支持常见的单节锂电池充电曲线。充电完成后芯片内部还有电源路径管理能够在电池供电和外部输入供电之间自动切换而这一切都是硬件层面的不依赖主控实时干预。再往输出侧看芯片集成了多路降压转换器和至少一路LDO。降压转换器适合给主控、通信模块这类电流需求较大的负载供电LDO则适合给模拟前端或者低速接口提供低噪声电源。此外芯片内部还有ADC采样通路能够监测电池电压、充电电流和某些内部电压省掉了外部采样电阻和运放。对于F103来说这些都是通过I2C读寄存器完成的。2.2 必须吃透的几个关键参数用这类型芯片之前我最担心的是电流余量是不是够。PCA9422的降压转换器输出持续电流能力毕竟是有限度的你不能指望一颗小封装PMIC去带动大功率模块。我的做法是先把每一路负载的最大峰值电流列成表格再跟芯片手册里的参数对比留出至少30%以上的裕量。参数参考范围我的考虑输入电压范围4.0V - 5.5VUSB供电场景下5V会有纹波要确保不超上限降压输出范围0.6V - 3.4V左右根据封装/版本不同有差异先查手册充电电流几十mA到几百mA可配电池容量决定通常按0.5C以内设置待机电流几个uA级别低功耗产品是否达标就看这个I2C速率100k/400k老F103用GPIO模拟时建议100k以内有一点要特别提醒线性充电在输入电压和电池电压接近时充电电流会明显受限热量主要集中在芯片内部所以不要只看最大充电电流参数还要考虑PCB散热条件。我这种小面积板子就规规矩矩把充电电流设置成200mA附近避免长时间高温。2.3 寄存器体系和I2C通信基本思路PCA9422的寄存器体系并不复杂基本上可以分成几大类全局控制寄存器、电压设置寄存器、充电配置寄存器、状态寄存器、中断寄存器和ADC寄存器。开发时最常用的操作就是写电压、读状态、处理中断。下面这组寄存器地址只是我用来演示的示例结构具体地址和位定义一定要以你手里那颗芯片的数据手册为准寄存器功能说明0x00芯片ID/版本上电后先读它确认通信正常0x01全局控制各路输出使能、工作模式0x02DCDC1电压设置设置主控供电轨0x03DCDC2电压设置设置通信模块供电轨0x04LDO电压设置设置小电流负载0x05充电配置充电电流、终止电流0x06状态寄存器充放电、输入电源状态0x07中断屏蔽控制哪些事件产生INT脉冲0x08ADC控制启停内部ADC采样0x09ADC数据读取电池电压等采样结果I2C通信的基本流程和普通EEPROM类似先发器件地址再发寄存器地址然后写数据或者连续读数据。需要注意PCA9422这类PMIC通常不支持太高的I2C速率我统一用100kHz的标准模式通信稳定比速度重要。3. 硬件设计实战电源树、连接与PCB布局检查3.1 先把电源树画清楚画原理图之前我建议先画一张电源树把输入源、PMIC的每个输出通道、负载和允许的电压范围列清楚。很多初级工程师上来就在原理图里拉线结果画完发现某路负载接错了输出通道或者某路的电流需求超出了芯片能力返工成本非常高。我当时的电源树规划大概是这样的USB/电池输入进入PCA9422的输入引脚DCDC1输出3.3V给STM32F103RC、传感器和屏幕逻辑供电DCDC2输出2.85V到3.3V可调给通信模块供电因为模块在发射时电流尖峰大单独一路可以避免和主控互相干扰LDO输出1.8V给低速接口和部分模拟电路供电电池电压通过内部ADC采样充电由PMIC内部线性充电单元完成这棵树画好之后再回头查芯片手册确认每一路输出的最大电流都大于负载峰值电压设置范围也覆盖了需求。3.2 与STM32F103RC的搭接细节PCA9422和F103之间的硬件连接非常少通常只需要I2C、中断和使能控制。下面这个连接表是我在实际项目里常用的接法你可以根据自己板子资源调整。信号PCA9422引脚STM32F103RC引脚注意事项I2C时钟SCLPB6必须接上拉电阻到对应电平I2C数据SDAPB7上拉电阻选4.7k到10k中断输出INTPA0配置为下降沿触发的外部中断使能控制ENPC13高有效还是低有效要看手册充电状态STAT可选也可以不接直接读寄存器上拉电阻是I2C最容易忽略的地方。PCA9422的I2C引脚一般要求外部上拉如果不接通信会时好时坏。我习惯在I2C两个引脚上各放一颗4.7k电阻到3.3V对于100kHz总线来说是一个很稳的组合。如果PCB走线较长可以减小到2.2k但功耗会稍微增加。3.3 PCB布局原则与实战检查PMIC这类芯片的PCB布局比普通数字芯片讲究得多。DCDC的开关节点电流变化非常剧烈布局不好轻则纹波超标重则EMI测试不过。我用的是传统经验法则输入电容尽量贴近芯片的VIN和GND引脚电感放在对应开关节点和输出电容之间反馈走线避开开关节点和电感正下方。还有一点很容易被忽略电池的地和系统的地要处理好。我在这块板上把PMIC的地、MCU的地和模拟部分的地在一点汇集避免充电大电流在地线上造成压差否则ADC读数会飘低功耗电流测量也会很难看。铺铜的时候功率地走线尽量短粗信号地走线不要穿过充电回路。3.4 硬件常见坑提前说这几个坑是我在不同项目里都遇到过的提前说出来免得大家再踩I2C上拉电阻接到了5V而不是3.3V结果通讯偶尔异常因为引脚电平不匹配DCDC输出端的输出电容容量太小通信模块突发大电流时电压被拉低系统复位电池座接触不良充电电流上不去误认为是芯片问题电感饱和电流选小了大电流时效率暴跌纹波变大开关节点铺铜面积过大成了一个小天线EMI数据很难看硬件设计这部分我的体会是先保守再优化。第一版板子宁可把电容、电感选大一点把布局留出调整位置先把功能跑起来实测数据再逐步优化。4. 固件实现I2C驱动、初始化序列和低功耗切换4.1 为什么我不用F103的硬件I2C而是用GPIO模拟STM32F103RC的硬件I2C外设在工程圈里口碑不算好老版本上经常出现总线忙检测导致卡死的情况有人在状态机里加了超时重试才好用。做低功耗产品时I2C一旦卡死PMIC调不动整个系统就废了。我对这部分的稳定性格外在意所以直接选择GPIO软件模拟I2C。软件模拟I2C的好处是行为完全可控时序靠自己延时调节不会莫名其妙进入忙状态。缺点是CPU会占用一点时间但对于100kHz的通信每次读写也就是几十微秒的事对F103来说完全无所谓。低功耗触发时先把I2C引脚释放掉让它们保持高阻或固定电平也不会产生额外的漏电流。下面是一个比较典型的GPIO模拟I2C实现包含起始、停止、写字节和读字节我在项目里直接套用这个骨架#define I2C_SCL_H GPIOB-BSRR GPIO_Pin_6 #define I2C_SCL_L GPIOB-BRR GPIO_Pin_6 #define I2C_SDA_H GPIOB-BSRR GPIO_Pin_7 #define I2C_SDA_L GPIOB-BRR GPIO_Pin_7 #define I2C_SDA_READ ((GPIOB-IDR GPIO_Pin_7) ! 0) static void i2c_delay(void) { volatile int i 10; while (i--); } static void i2c_start(void) { I2C_SDA_H; I2C_SCL_H; i2c_delay(); I2C_SDA_L; i2c_delay(); I2C_SCL_L; i2c_delay(); } static void i2c_stop(void) { I2C_SDA_L; I2C_SCL_H; i2c_delay(); I2C_SDA_H; i2c_delay(); } static void i2c_write_bit(uint8_t bit) { if (bit) I2C_SDA_H; else I2C_SDA_L; i2c_delay(); I2C_SCL_H; i2c_delay(); I2C_SCL_L; i2c_delay(); } static uint8_t i2c_read_bit(void) { uint8_t bit; I2C_SDA_H; I2C_SCL_H; i2c_delay(); bit I2C_SDA_READ; I2C_SCL_L; i2c_delay(); return bit; } static uint8_t i2c_write_byte(uint8_t data) { uint8_t i, ack; for (i 0; i 8; i) i2c_write_bit((data (7 - i)) 0x01); ack i2c_read_bit(); return ack; // 0表示ACK } static uint8_t i2c_read_byte(uint8_t ack) { uint8_t i, data 0; for (i 0; i 8; i) { data (data 1) | i2c_read_bit(); } i2c_write_bit(ack); return data; }写寄存器函数和读寄存器函数也都基于这套底子封装。读的时候记得在最后一个字节发送NACK告诉芯片后面不用再发了否则芯片会继续驱动SDA导致总线冲突。4.2 初始化序列和寄存器配置拿到PCA9422之后第一步不是急着配置电压而是先读芯片ID确认I2C通信没问题。很多问题其实就是I2C没通后面全白查。初始化顺序我建议严格按复位PMIC -- 读ID -- 配置电压 -- 配置充电 -- 使能输出 -- 配置中断这个顺序来。下面是我在项目里使用的初始化流程示意void pca9422_init(void) { uint8_t id pca9422_read_reg(0x00); if (id ! 0x12) { // 示例ID以实际手册为准 error_handler(); return; } // 先软复位让芯片回到默认状态 pca9422_write_reg(0x01, 0x80); // DCDC1输出3.3V具体编码根据手册电压表计算 pca9422_write_reg(0x02, 0x3C); // DCDC2输出2.85V pca9422_write_reg(0x03, 0x34); // LDO输出1.8V pca9422_write_reg(0x04, 0x2A); // 充电电流200mA终止电流50mA pca9422_write_reg(0x05, 0x1C); // 使能DCDC1、DCDC2和LDO输出 pca9422_write_reg(0x01, 0x07); // 打开低电量、充电完成、输入电源插入中断 pca9422_write_reg(0x07, 0x00); }这里面的0x3C、0x34之类的值只是我在演示时随便写的示意编码不一定等于你实际要写入的电压值。PCA9422的电压设置通常是一组编码对应一个电压档位一定要对照数据手册里的电压映射表去查不能靠猜。还有一个细节如果主控要求PMIC在MCU启动前就有稳定输出那么PCA9422的默认输出状态必须在选型阶段就确认好。我在这块板子上让DCDC1默认输出3.3V所以F103一上电就能跑起来然后再通过I2C把其余几路调到目标值。4.3 动态调压和低功耗切换的代码思路有PMIC之后低功耗切换的核心思路是主控和PMIC联动。F103进入STOP模式之前先把PMIC里不需要工作的通道关掉或者把输出电压降到最低保持档位让负载侧处于最低消耗状态。退出时反向操作先把PMIC各通道恢复再让F103继续跑应用。我写了一个简单的状态切换函数void enter_deep_sleep(void) { // 关掉通信模块供电轨 pca9422_disable_dcdc2(); // 关掉LDO pca9422_disable_ldo(); // PMIC进入低功耗模式 pca9422_set_low_power_mode(1); // 配置EXTI唤醒 PWR_EnterSTOPMode(PWR_Regulator_LowPower, PWR_STOPEntry_WFI); // 唤醒后先把PMIC恢复到工作状态 pca9422_set_low_power_mode(0); pca9422_enable_dcdc2(); pca9422_enable_ldo(); }低功耗切换时最忌讳的是顺序颠倒。如果先让F103睡眠再让PMIC切换模式PMIC的I2C主设备已经不工作了指令根本发不出去。反过来唤醒后一定要先恢复PMIC输出再让外设初始化否则外设上电时序不对可能进入异常状态。4.4 中断处理和异常恢复PCA9422的INT引脚一般会在输入电源插入、充电完成、电池低电压等事件发生时拉低。F103这边对应配置一个下降沿EXTI中断在中断服务函数里通过I2C读取状态寄存器判断具体是哪个事件。中断服务函数里的操作要尽量短我只在中断里设置标志位真正的处理放到主循环里。原因是I2C软件模拟本身依赖精准延时如果在中断里做容易被其他中断打断导致时序不满足芯片要求。还有一类情况要特别注意当PMIC因为输入欠压或者过温进入保护状态时它可能不响应I2C或者把输出关断。这时候靠普通流程是读不了状态的需要在固件里做重新初始化逻辑比如定时尝试复位PMIC恢复输出。我在这块板上专门加了一个任务每秒钟检查一次PMIC的ID寄存器一旦读不到正确ID就先软复位再重新配置。5. 实测数据、待机电流问题和一次完整排错记录5.1 电压纹波与效率实测整板调通之后我做了比较完整的电源测试。用示波器测量各输出轨的纹波和瞬态响应用电子负载和电流表记录不同负载电流下的效率。下面这组数据是我这块板子上的实际情况不同板子会有差异但可以作为参考量级输出轨轻载纹波满载纹波备注DCDC1 3.3V20mV左右6mV左右轻载时芯片进入PFM模式纹波变大DCDC2 2.85V18mV左右8mV左右通信模块突发电流时会有短时跌落LDO 1.8V3mV左右4mV左右LDO噪声特征和DCDC明显不同效率方面我实测在中等负载区间的整机效率大约在85%到90%之间轻载时因为没有大电流损耗效率稍低但这正是PFM模式存在的意义换来的是更低的静态电流。如果你的应用对纹波特别敏感可以把DCDC配置成强制PWM模式代价是轻载时电流消耗会明显变大。5.2 待机电流逐项排查低功耗产品最容易死在整机待机电流上。我的第一版整机待机电流测出来有接近1mA完全不能接受。用电流表在电池端串联测量然后逐步断开外围负载最后定位到三个问题第一个问题是某个传感器在软件里初始化和掉电顺序没处理好进入睡眠模式之后引脚电平不对导致传感器没有真正进入低功耗状态白吃了几十uA。第二个问题是PMIC的LDO通道在F103睡眠后依然保持输出而我负责供电的那颗小芯片本身在睡眠状态下根本不需要供电白白浪费电流。后来我把LDO的使能控制放到了睡眠切换函数里待机电流立刻降了一截。第三个问题是I2C上拉电阻漏电这个虽然只有10uA级别但在低功耗产品里也要计较。解决完之后整机待机电流稳定在20uA以内F103的RTC部分加上PMIC自身的待机消耗基本都在这个量级。如果你的系统对电流要求更高那就需要选择支持更彻底运输模式的PMIC方案把主回路直接断开。5.3 一次I2C通信超时的完整排查链路调试过程中遇到过一起很典型的偶发故障值得完整复盘。现象是某个批次的板子在USB反复插拔几次之后F103偶尔读不到PCA9422的状态寄存器概率大概百分之一。由于是偶发第一反应是怀疑I2C时序问题。我先用示波器挂在SCL和SDA引脚上反复触发插拔USB的操作。抓到异常波形之后发现SCL和SDA看起来都正常但SDA上的应答位没有拉低说明PCA9422没有响应主机的请求。最开始我以为是I2C总线上拉电阻阻值不合适把4.7k换成了2.2k故障概率没有明显变化。走查这一步时我重新看了原理图发现USB的5V入口先经过一颗防浪涌器件再走到PCA9422的输入引脚。于是我在故障发生时顺手测了PCA9422的VIN波形结果看到USB插入的瞬间VIN有将近1V的跌落持续时间有几十微秒。这个跌落导致了芯片内部逻辑短暂复位而复位期间它自然不会应答I2C也不会处理中断请求。最后定位的根本原因是防浪涌器件选型偏保守寄生电容偏大再加上输入电容离芯片引脚太远插入瞬间的浪涌电流把输入拉垮了。解决方法是把输入端的22uF电容尽量贴近芯片VIN放置同时换了一颗寄生参数更小的保护器件。改动之后同样的操作循环了两百多次没有再出现通信超时。这个案例给我最大的教训是PMIC的I2C不响应不要只盯通信链路先看供电是不是真的稳。通信协议上的问题通常有确定性而供电瞬间的跌落往往是概率性故障排查方向错了会浪费很多时间。6. 产品化阶段的进阶优化运输模式、电量监测与量产6.1 运输模式与出厂设置电池供电产品从生产到用户手里往往要经历仓储、运输、上架的过程如果期间电池一直给系统供电到用户手里可能已经没电了。PCA9422这类PMIC一般会支持运输模式也就是出厂前通过I2C设置让整机进入一个电流极低的关机状态相当于把电池和负载之间的通路断开。我的实现方式是最后一道测试工序里F103向PCA9422写入运输模式配置寄存器等待几百毫秒让配置生效然后F103自己也进入掉电状态。用户第一次开机时可以通过按键或者充电器插入来唤醒PMIC恢复供电。这个功能在普通LDO方案里完全做不到算是个PMIC方案的额外红利。6.2 电池监测与电量估算的实用做法PCA9422内置ADC通道可以直接读电池电压省掉了外部电阻分压加F103内部ADC的麻烦。不过直接读电压做电量百分比并不准因为锂电池在放电中段的电压变化很平缓而在末段又掉得非常快。我采用的办法是开路电压查表加放电积分的混合方案系统待机时读一次开路电压估算一个基础电量工作过程中用电流积分修正两者加权得到一个最终显示电量。这套方案在我的项目里已经够用实时性、准确率都比单纯查表要好不少。对于更严格的应用那就需要额外的库仑计芯片了。有一个小细节要注意在充电状态下读电池电压会偏高需要加一个是否在充电的判断充电时不要用电量计否则用户体验会很奇怪。6.3 量产测试与一致性小批量打样和真正量产完全是两回事。我用PCA9422做完整电源管理后量产测试流程比之前分立方案简化了很多。以前每一路电压都要单独测试还要测使能时序现在只需要烧录固件后自动执行一套I2C回读校验把各输出轨配置值读回来和标称值比对即可。不过PMIC芯片本身也有个体差异最明显的是充电终止电流和输出电压精度。我建议量产流程里加入一项电池满充判定测试用一截固定放电深度的电池做校准确认充电管理部分每个通道都能正确进入充电完成状态。还有一点PMIC小封装对回流焊温度比较敏感如果PCBA过炉温度控制不好可能出现焊点虚接导致电池功耗异常。整机待机电流测试在量产阶段必须全检单靠抽检很难拦住偶发不良。排产了几批之后我再看当初那块满是分立电源器件的旧板子最大的感触是电源管理这件事用对器件比堆功能更关键。PCA9422给了F103项目一个很完整的电源底座固件侧只需把I2C、初始化和模式切换处理好剩下的电量监测和充电管理基本是顺手得来的能力。如果你也在做一个电池供电的小设备不妨先画好电源树算清每一路负载再决定是继续用分立方案挣扎还是直接上一颗带I2C的可编程PMIC。
觉得有用,分享给同行:

为您的企业打造数字门面

稳重轻奢商务风格,端正雅致视觉,长效耐看不易过时。

立即咨询 →