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

文章详情

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

基于PCA9422与STM32L021K4的低功耗电源管理设计实战

基于PCA9422与STM32L021K4的低功耗电源管理设计实战 1. 项目缘起与整体设计思路低功耗嵌入式设备做多了迟早会撞上一个绕不开的坎主控芯片本身能跑到微安级休眠但板子上的电源树却像个漏水的桶静态电流怎么都压不下去。我手头有个便携式传感终端的项目主控选的是STM32L021K4这颗Cortex-M0的芯片在Stop模式下典型电流不到1微安按理说两节纽扣电池撑个一两年不成问题。但实际打样回来一测整机休眠电流飙到80微安以上电池三个月就见底。问题不在主控而在供电架构——LDO常开、分压电阻常挂、充电管理芯片自己也在耗电。这个项目要解决的核心问题就是用一颗专门的电源管理IC把整块板子的供电、充电、复位、看门狗、电量监测全部管起来让主控只管干活和睡觉不用操心电源的事。选型上我锁定了PCA9422这是一颗面向低功耗应用的电源管理芯片集成了线性充电器、多路可配置LDO、I2C接口、复位输出和看门狗定时器。搭配STM32L021K4两者通过I2C通信主控可以动态配置各路输出电压、读取充电状态和故障标志甚至在深度休眠时把不需要的电源轨彻底关掉。为什么不用分立方案我试过用一颗充电IC加一颗LDO再加一颗复位芯片的搭法BOM成本确实低一点但板子面积大了将近一倍而且三颗芯片各自的静态电流加起来很难压到10微安以下。PCA9422把这几样东西集成到一颗QFN封装里静态电流典型值只有几微安而且各路LDO的使能、电压、放电电阻都能通过寄存器控制灵活性远超固定输出的分立LDO。对于STM32L021K4这种引脚不多、资源有限的M0芯片来说用I2C扩展出一套完整的电源管理能力比自己用GPIO去凑要干净得多。这套方案适合谁参考如果你在做电池供电的便携设备主控是STM32L0、L1或者类似的低功耗MCU板子上有锂电池或纽扣电池需要充电管理、多路供电和低静态功耗那这个组合值得认真考虑。如果你只是做个USB供电的小玩具那没必要上PMIC一颗LDO就够了。下面我把整个设计过程拆开讲从芯片选型理由、硬件连接、寄存器配置到固件实现和踩坑记录尽量把每个决策背后的逻辑说清楚。2. 核心器件解析与硬件设计要点2.1 PCA9422的关键特性与选型考量PCA9422是一颗I2C可编程的电源管理IC输入电压范围覆盖USB 5V和单节锂电的3.0V到4.5V内置线性充电器支持最大500mA充电电流充电电压和电流都能通过寄存器调节。它集成了四路LDO其中两路是低噪声LDO适合给模拟传感器或射频部分供电另外两路是通用LDO可以给数字外设和IO供电。每路LDO的输出电压从0.8V到3.3V可调步进25mV或50mV具体取决于档位。还有一个关键特性是每路LDO都有独立的使能位和放电电阻控制位这意味着主控可以在进入低功耗模式前把不用的电源轨关掉并且让输出电容上的残压快速泄放避免出现反灌或上电时序问题。选它而不是选更便宜的固定输出PMIC核心原因是STM32L021K4的供电需求会随工作模式变化。比如ADC采样时需要干净的3.3V模拟供电而休眠时只需要给RTC和备份寄存器供1.8V其他外设全部断电。固定输出的PMIC做不到这种动态调整要么多花GPIO去控制外部MOS管要么就得接受不必要的功耗。PCA9422用I2C一条总线解决了所有问题主控在运行时可随时改电压、关通道休眠前写几个寄存器就能把整机功耗压到最低。还有一个容易被忽略的点是复位和看门狗。STM32L021K4内部有独立看门狗但在极端情况下比如时钟失效导致程序跑飞内部看门狗可能也失效。PCA9422自带一个可编程看门狗定时器超时后可以拉低复位引脚给主控一个硬复位。这个功能在无人值守的传感节点里非常实用相当于多了一层保险。复位阈值和看门狗超时时间都能通过I2C配置不需要额外的外部元件。2.2 STM32L021K4的资源匹配与接口规划STM32L021K4是一颗32引脚、16KB Flash、2KB RAM的Cortex-M0主频最高32MHz。资源确实紧张但用来做电源管理的主控绰绰有余。它有两个I2C接口我用I2C1接PCA9422速率设到100kHz就够了因为电源寄存器的配置不需要高速通信。I2C1的SCL和SDA引脚需要外部上拉电阻我选了4.7kΩ上拉到1.8V这个阻值在100kHz下波形很干净功耗也可以接受。注意上拉电压要和PCA9422的I2C电平匹配PCA9422的I2C引脚耐压是3.6V但为了和STM32L021K4的1.8V IO域一致我把PCA9422的I2C供电也设在1.8V。STM32L021K4的GPIO数量有限所以我把PCA9422的中断输出引脚接到主控的一个外部中断GPIO上。PCA9422在充电完成、充电故障、看门狗超时、输入电压跌落等事件发生时都会拉低中断引脚主控在休眠时可以通过外部中断唤醒然后读状态寄存器判断具体事件。这样就不需要主控轮询进一步降低功耗。另外我把PCA9422的复位输出接到STM32L021K4的NRST引脚这样PCA9422的看门狗超时可以直接复位主控不需要软件干预。电源域划分上我把STM32L021K4的VDD和VDDA都接到PCA9422的LDO1输出设成1.8V。LDO2设成3.3V给外部传感器供电LDO3设成2.5V给一个模拟前端LDO4暂时不用在初始化时关掉。这样主控在休眠时可以只保留LDO1把LDO2和LDO3关掉传感器和模拟前端的功耗直接归零。实测下来整机休眠电流从原来的80微安降到了6微安左右其中PCA9422自身耗电约3微安STM32L021K4在Stop模式下约1微安剩下的是板子上的漏电流。2.3 原理图设计与布局注意事项原理图上有几个地方需要特别小心。第一是充电输入和系统输出的关系。PCA9422的VBUS引脚接USB 5VVBAT接锂电池SYS引脚是系统输出内部有一个电源路径管理当VBUS存在时优先用VBUS供电并给电池充电VBUS拔掉后自动切到电池。SYS引脚需要接一个至少10微法的电容我用了22微法因为线性充电器在切换瞬间会有电压跌落电容太小会导致系统复位。这个电容要尽量靠近PCA9422的SYS引脚放置走线要短而粗。第二是I2C总线的上拉。前面说了用4.7kΩ上拉到1.8V但要注意PCA9422的I2C引脚在关断模式下不能有电流倒灌。我在SCL和SDA上各串了一个100Ω电阻虽然理论上I2C不需要串阻但实际调试中发现串了之后波形过冲明显减小尤其是在线缆较长的情况下。这个100Ω不会影响通信因为I2C是开漏输出串阻上的压降可以忽略。第三是LDO输出的电容选择。PCA9422的数据手册要求每路LDO输出至少接1微法陶瓷电容我用了2.2微法X5R。注意陶瓷电容的直流偏压效应2.2微法在1.8V下实际容量可能只有1.5微法左右所以不要卡着最小值选。另外LDO2和LDO3给外部传感器供电如果传感器对电源纹波敏感可以在LDO输出后面再加一个π型滤波但会增加成本和面积我这边传感器要求不高就没加。布局上PCA9422底部的散热焊盘必须接到地平面并且打至少9个过孔到内层地。充电电流500mA时芯片会有一定温升散热焊盘接地能有效降低热阻。VBUS和VBAT的走线要足够宽我用了0.5mm因为500mA下0.5mm走线的温升大约10度可以接受。I2C走线远离VBUS和VBAT的功率走线避免开关噪声耦合到通信线上。晶振和复位线也要远离功率区域STM32L021K4的NRST引脚上我加了100nF电容到地防止复位线上的毛刺导致误复位。3. 寄存器配置与固件实现细节3.1 PCA9422的I2C寄存器映射与初始化流程PCA9422的寄存器空间不算复杂但有几个坑需要注意。它的I2C从机地址是7位的默认是0x2C但可以通过外部引脚配置成其他地址。我板子上把地址引脚接地所以地址就是0x2C。写寄存器时先发从机地址加写位然后发寄存器地址再发数据。读寄存器时先发寄存器地址然后重启总线发从机地址加读位。STM32L021K4的I2C外设支持这种重复起始条件用HAL库的话调用HAL_I2C_Mem_Write和HAL_I2C_Mem_Read就能搞定。初始化流程我分成几步。第一步是读设备ID寄存器确认通信正常PCA9422有一个只读的Device ID寄存器值应该是0x22。如果读不到或者值不对说明I2C通信有问题需要检查上拉、地址和时序。第二步是配置充电参数包括充电电流、充电终止电压、输入电流限制。我设的充电电流是200mA因为我的电池是500mAh的锂聚合物0.4C充电比较稳妥。充电终止电压设4.2V输入电流限制设500mA这样USB供电时不会把电脑的USB口拉垮。第三步是配置各路LDO。LDO1设1.8V使能放电电阻使能。LDO2设3.3V初始使能放电电阻使能。LDO3设2.5V初始使能放电电阻使能。LDO4关掉放电电阻使能。这里注意放电电阻的作用是在LDO关断后给输出电容一个泄放通路如果不使能输出电容上的电会慢慢漏掉可能导致下次上电时外设处于不确定状态。第四步是配置看门狗和复位。看门狗超时设2秒复位阈值设2.8V使能看门狗。第五步是配置中断使能充电完成、充电故障、看门狗超时和输入跌落中断。初始化代码用HAL库写起来很直接但要注意每次写寄存器后要加一点延时因为PCA9422内部寄存器写入需要时间。我实测下来连续写多个寄存器时每个寄存器之间加100微秒延时比较稳妥。如果连续写太快偶尔会出现某个寄存器没写进去的情况。这个延时可以用HAL_Delay或者简单的循环延时实现初始化阶段对时间不敏感。3.2 动态电源管理的固件架构固件的主循环很简单初始化外设读传感器处理数据然后进入Stop模式等待中断。关键在进入Stop模式之前要做一系列电源管理操作。首先判断当前是否需要外部传感器工作如果不需要就把LDO2和LDO3关掉。关之前要确保传感器已经进入休眠或者断电状态否则突然断电可能导致传感器内部状态异常。我一般先给传感器发一个休眠命令等10毫秒再关LDO。关LDO的操作是写PCA9422的使能寄存器把对应位清零。写完之后要读回确认因为I2C通信偶尔会受干扰。确认关断后再把STM32L021K4的未使用GPIO配置成模拟输入或者输出低避免浮空引脚漏电。然后配置唤醒源我用了两个一个是PCA9422的中断引脚另一个是RTC闹钟。RTC闹钟用来做周期性唤醒比如每10秒醒来读一次传感器。PCA9422中断用来响应充电插入、充电完成等事件。进入Stop模式的代码用HAL库的HAL_PWR_EnterSTOPMode参数用PWR_LOWPOWERREGULATOR_ON和PWR_STOPENTRY_WFI。唤醒后第一件事是重新配置系统时钟因为Stop模式下HSI时钟会停唤醒后默认用HSI需要重新切到HSE或者MSI。我用的MSI唤醒后把MSI调到2MHz够用了。然后读PCA9422的中断状态寄存器判断是什么事件再决定下一步动作。如果是充电插入就打开LDO2和LDO3让传感器工作如果是RTC闹钟就读传感器数据然后继续睡。这里有个细节PCA9422的中断引脚是开漏输出需要上拉。我把它上拉到1.8V和主控IO电平一致。中断是低电平有效所以主控的外部中断要配置成下降沿触发。唤醒后要读PCA9422的中断状态寄存器来清除中断标志否则中断引脚不会释放。这个清除操作是写1清零不是写0容易搞错。我一开始写0结果中断一直不释放查了半天手册才发现是写1清零。3.3 充电管理与电量监测的实现充电管理这块PCA9422内部有充电状态机主控只需要读状态寄存器就能知道当前是预充、恒流充电、恒压充电还是充电完成。我固件里做了一个简单的状态机根据充电状态决定LED指示和系统行为。比如充电完成时如果系统在运行就点亮一个LED如果系统在休眠就通过中断唤醒后点亮LED几秒钟再灭掉。充电故障时读故障寄存器判断是过温、超时还是电池异常然后记录到Flash里供后续分析。电量监测我没有用库仑计因为成本和板面积都不允许。我用的是STM32L021K4的ADC读电池分压电压分压电阻用1兆欧和1兆欧这样分压电流只有2微安左右对功耗影响很小。ADC参考电压用内部1.224V的VREFINT这样不需要外部基准。读到的电压经过校准后换算成电池电压再根据锂电的放电曲线估算剩余电量。这个方法精度一般误差大概10%到15%但对于显示大概电量够用了。如果要更准可以在PCA9422的SYS引脚和VBAT引脚之间加一个采样电阻用ADC读采样电阻上的压降来算电流但那样会增加功耗和成本。ADC采样时机也有讲究。我放在系统唤醒后、传感器工作之前采样因为传感器工作时电源纹波会干扰ADC。采样时连续读16次取平均去掉最大最小值这样能滤掉大部分噪声。电池电压的换算公式是Vbat ADC值 × VREFINT校准值 / 4096 × 2。VREFINT的校准值存在STM32L021K4的系统存储区地址是0x1FF80078上电时读一次存到变量里。这个校准值出厂时已经测好精度比直接用标称1.224V高很多。4. 实操过程与关键环节记录4.1 硬件焊接与上电调试步骤板子回来之后第一步是目视检查PCA9422的焊接。QFN封装容易虚焊尤其是底部散热焊盘。我用放大镜看了每个引脚确认没有连锡和虚焊。然后用万用表测VBUS、VBAT、SYS对地的阻抗正常应该在几百千欧以上如果短路说明有焊接问题。测I2C的SCL和SDA对地阻抗应该在上拉电阻的阻值附近如果明显偏低说明有短路。上电顺序很重要。我先不焊STM32L021K4只焊PCA9422和外围元件用可调电源给VBUS供5V限流设100mA。上电后测SYS电压应该是3.3V左右取决于LDO1的默认输出。如果SYS电压不对先查PCA9422的焊接和外围元件。然后测I2C总线电压SCL和SDA应该都是1.8V上拉电压。如果不对查上拉电阻和PCA9422的I2C供电。确认PCA9422工作正常后再焊STM32L021K4。焊好后用ST-Link连接SWD接口注意STM32L021K4的SWD引脚是PA13和PA14但这两个引脚在复位后默认是SWD功能不需要额外配置。上电后ST-Link应该能识别到芯片。如果识别不到检查NRST引脚电压正常应该是高电平。如果NRST被PCA9422拉低说明看门狗超时了需要先通过I2C关掉看门狗再调试。第一次下载程序后先跑一个I2C扫描程序确认能读到PCA9422的地址0x2C。如果读不到用示波器看SCL和SDA波形检查是否有起始条件、地址字节和应答位。我遇到过一次读不到的情况示波器上看SDA在地址字节后没有被拉低说明PCA9422没有应答。查了半天发现是I2C地址配错了板子上的地址引脚我以为是接地实际是悬空导致地址变成了0x2D。把地址引脚可靠接地后就正常了。4.2 功耗测试与优化过程功耗测试我用的是高精度电流表串联在电池和板子之间量程设到微安级。测试前先把板子上的LED和调试接口的功耗去掉LED拆掉SWD接口在测试时不连接。第一次测休眠电流是15微安比预期的6微安高不少。排查发现两个问题一是STM32L021K4的未使用GPIO有些配置成了输入浮空导致漏电二是PCA9422的LDO2和LDO3虽然关了但输出电容上的残压通过传感器的输入保护二极管倒灌产生了额外电流。第一个问题的解决方法是把所有未使用的GPIO配置成模拟输入并且在进入Stop模式前把GPIO时钟关掉。STM32L021K4的GPIO在模拟输入模式下漏电最小实测比浮空输入低了好几微安。第二个问题是在LDO2和LDO3的输出和传感器之间各串了一个肖特基二极管防止残压倒灌。肖特基的压降大约0.2V对3.3V的传感器来说可以接受。串了二极管之后休眠电流降到了7微安左右。还有一个漏电点是PCA9422的I2C上拉电阻。4.7kΩ上拉到1.8V当I2C总线空闲时上拉电阻上的电流是1.8V除以4.7kΩ大约0.38毫安。这个电流虽然不大但它是持续存在的。我把上拉电阻改成了10kΩ电流降到0.18毫安但波形仍然可以接受因为100kHz下10kΩ上拉的上升时间大约1微秒远小于时钟周期的十分之一。改完之后休眠电流降到了6微安左右达到了预期目标。4.3 充电测试与异常处理充电测试我用了一个可调电源模拟USB 5V串联电流表看充电电流。第一次充电时发现充电电流只有50mA远低于设定的200mA。读PCA9422的充电状态寄存器显示处于预充模式。预充模式是因为电池电压低于3.0VPCA9422自动进入小电流预充。等电池电压升到3.0V以上后充电电流自动升到200mA。这个是正常行为不是故障。但后来遇到一个异常充电到4.1V左右时充电电流突然降到0状态寄存器显示充电完成。用万用表测电池电压确实是4.1V但设定的终止电压是4.2V。读充电终止电压寄存器发现值被改成了4.1V。查代码发现是初始化时写寄存器的顺序有问题先写了终止电压再写了其他寄存器但其他寄存器里有一个位影响了终止电压的映射。后来调整了写入顺序先写配置寄存器再写终止电压问题解决。这个坑说明PCA9422的寄存器之间有关联写顺序不能随意。还有一个异常是充电时如果同时打开LDO2和LDO3给传感器供电充电电流会波动。原因是输入电流限制设的500mA但传感器工作时瞬间电流可能超过500mA导致输入电压跌落PCA9422自动降低充电电流来维持系统电压。解决方法是把输入电流限制提高到800mA或者让传感器在充电时进入低功耗模式。我选了后者因为USB口不一定能提供800mA。在充电状态下固件会检测到VBUS存在然后让传感器进入休眠充电完成后再唤醒。5. 常见问题与排查技巧实录5.1 I2C通信失败排查速查表I2C通信失败是调试PCA9422时最常见的问题我整理了一个排查顺序从简单到复杂逐项检查。现象可能原因排查方法解决方法完全读不到设备供电未建立测PCA9422的VDD和I2C供电引脚电压检查供电走线和焊接完全读不到设备地址错误读地址引脚电压确认地址配置按数据手册调整地址引脚读到的值全为0xFF上拉缺失测SCL和SDA空闲电压加上拉电阻到正确电压读到的值全为0x00总线被拉低测SCL和SDA对地阻抗检查短路或从机故障偶尔通信失败时序问题示波器看波形上升沿减小上拉阻值或降低速率写寄存器不生效写保护读回寄存器值确认检查是否需要解锁序列中断不释放清除方式错误读中断状态寄存器写1清零而非写0这个表是我实际踩坑总结出来的按顺序查基本能覆盖90%的问题。其中“写寄存器不生效”那个坑我印象最深PCA9422有些寄存器是写保护的需要先写一个解锁序列才能修改。我一开始不知道写了好几次都没反应后来读手册才发现。解锁序列是向特定寄存器写0x5A和0xA5然后再写目标寄存器。这个序列在初始化时执行一次就行不需要每次写都解锁。5.2 功耗异常排查与优化技巧功耗异常排查的核心思路是分段测量。先把板子分成几个电源域分别测每个域的电流。我的板子分成三个域PCA9422自身、STM32L021K4、外部传感器。测PCA9422自身电流时把LDO全部关掉只留VBAT供电测到的电流就是PCA9422的静态电流。如果这个值超过10微安说明PCA9422配置有问题检查是否有LDO没关干净或者看门狗在运行。测STM32L021K4的电流时把PCA9422的LDO1打开其他LDO关掉主控进入Stop模式测到的电流应该小于2微安。如果偏高检查GPIO配置和时钟配置。STM32L021K4在Stop模式下如果GPIO配置不当漏电可能达到几十微安。我一般把所有未使用GPIO设成模拟输入并且关闭GPIO时钟。另外注意调试接口的引脚SWDIO和SWCLK在休眠时如果保持调试功能会有额外功耗。可以在休眠前把这两个引脚配置成普通GPIO唤醒后再恢复。外部传感器的功耗排查要看传感器手册确认休眠电流是否在预期范围内。有些传感器虽然发了休眠命令但内部LDO还在工作休眠电流比手册标称值高很多。这种情况要么换传感器要么在传感器前面加一个负载开关休眠时彻底断电。我用的传感器休眠电流标称1微安实测2微安可以接受。如果实测值远超标称先检查外围电路是否有漏电比如上拉电阻、分压电阻。5.3 充电异常与电池管理避坑指南充电异常最常见的是充电电流不达标。除了前面说的预充模式还有一个原因是输入电压不足。PCA9422的VBUS欠压锁定阈值是4.0V左右如果USB线太长或者接触不良VBUS到PCA9422的电压可能低于4.0V导致充电电流受限。用万用表测PCA9422的VBUS引脚电压如果低于4.5V换一根短一点的USB线或者换一个USB口。另一个坑是电池温度检测。PCA9422支持外接热敏电阻做温度检测如果不接默认认为温度正常。但如果接了热敏电阻但阻值不对PCA9422可能误判为过温而停止充电。我一开始用了一个10kΩ的NTC但PCA9422要求的是10kΩ的线性热敏电阻两者温度曲线不同导致常温下被误判为过温。换了正确的热敏电阻后正常。如果不做温度检测把温度检测引脚接地或者悬空具体看数据手册。电池电量估算的坑在于放电曲线的非线性。锂电池在3.7V到3.9V之间电压变化很平缓ADC的微小误差会导致电量显示跳变。我的处理方法是做电压迟滞和滑动平均连续读16次取平均并且电量变化超过5%才更新显示。另外在低电量时低于3.4V加快采样频率因为这时候电压下降很快需要更及时地提醒用户。这些细节在数据手册里不会写都是实际用出来的经验。6. 个人实操体会与扩展思路这套PCA9422加STM32L021K4的方案我前后打了三版板子第一版功耗没达标第二版充电有问题第三版才稳定下来。最大的体会是电源管理芯片的寄存器配置不能想当然一定要对着数据手册逐条确认尤其是写顺序和写保护。另外功耗优化是一个系统工程不能只盯着主控板子上的每一个电阻、每一个电容、每一个未使用的引脚都可能是漏电点。我现在的习惯是每版板子回来先不焊主控只焊电源部分把电源树调通了再焊主控这样排查问题会简单很多。这个方案后续还可以扩展。比如PCA9422的中断引脚可以接多个事件我目前只用了充电和看门狗其实还可以用输入电压跌落中断来做掉电保护在VBUS拔掉的瞬间把关键数据存到Flash。另外PCA9422的LDO输出电压可以通过I2C动态调整我目前是固定配置其实可以根据主控的工作频率动态调压比如主控跑32MHz时LDO1设1.8V跑2MHz时设1.5V进一步降低动态功耗。这些扩展不需要改硬件只需要改固件有兴趣的可以试试。最后分享一个小技巧PCA9422的I2C地址可以通过地址引脚配置成四个不同的值如果板子上有多颗PCA9422可以用不同的地址挂在同一条I2C总线上。我有个项目用了两颗一颗管主电源一颗管传感器电源通过地址区分固件里封装成两个设备句柄用起来和单颗没什么区别。这个技巧在多电源域的板子上很实用可以省掉I2C多路复用器。
返回列表