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

文章详情

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

基于PCA9422的STM32F107电池供电电源管理与低功耗设计

基于PCA9422的STM32F107电池供电电源管理与低功耗设计 最近在做一款电池供电的手持数据采集终端主控选了 STM32F107VC外设配置一上来电源设计就成了最难啃的那块骨头主控和存储要 3.3V以太网 PHY 要 1.8V传感器数字核要 1.2V模拟前端还要一路干净的 2.8V另外还得把单节锂电池充电和整机低功耗管起来。刚开始图省事用几颗 LDO 分立方案拼凑实测下来发热、纹波、上电时序三个问题接踵而至5V 适配器直接供 LDO150mA 降到 3.3V压差带来的损耗直接让 PCB 局部发烫各路电源的上升沿参差不齐MCU 偶尔启动失败待机时 LDO 静态电流也没有优势。后来把方案推倒重来用 PCA9422 这颗 PMIC 配合 STM32F107VC 做完整电源管理充电、多路 DCDC/LDO、动态调压、状态监控全部闭环到 I2C 总线上整机功耗和启动可靠性才算真正稳住。这篇文章把我从选型、原理图设计、寄存器配置到实测排坑的关键点完整复盘一遍适合正在做电池供电 STM32 项目、对功耗和上电时序有硬指标的朋友参考。1. 为什么用 PCA9422 给 STM32F107VC 供电选型逻辑与方案定位1.1 手持终端的四路电源需求先把需求摆清楚。STM32F107VC 本身工作电压范围 2.0~3.6V但项目里不只有 MCU还有以太网 PHY、SD 卡、传感器、模拟前端每一路对电压和噪声的要求都不一样。我这边的实际电源轨是这样分配的电源轨电压负载典型电流VDD_MAIN3.3VMCU、Flash、SD 卡60~150mAVDD_PHY1.8V以太网 PHY 数字/IO30~80mAVDD_SENSOR1.2V传感器数字核心30~80mAAVDD_ANALOG2.8VADC 基准、运放10~20mAVBAT_STBY3.3VRTC 备份域、唤醒电路10uA难就难在最后一列。运行状态下最大负载来自 MCU 和外设但低功耗模式下只有 RTC 和唤醒电路在跑要求电源系统自己消耗的电流尽量小。这种“宽动态范围”的供电需求恰好是分立的 LDO 方案最不擅长的事。1.2 PCA9422 在方案里的角色PCA9422 是一颗面向低功耗 MCU 系统的 PMIC集成三路同步降压 DCDC、两路 LDO、单节锂电池充电器和完整的 I2C 控制接口输入电压范围大约 2.5~5.5V可以同时兼容 USB 5V 输入和单节锂电 3.0~4.2V 输入。它在整个系统里的角色很简单把电池或适配器电压变成六路输出并承担充电管理任务。开发阶段可以通过 I2C 随时改每路电压和开关状态不需要换电阻或者飞线改硬件这个便利性在调试低功耗策略时非常重要。相比之前“充电芯片 多颗 LDO 开关电源”的分散方案PCA9422 把所有电源功能和保护逻辑放到一颗芯片里板子面积缩小三分之一以上整机待机时的电源系统静态功耗也降了一个数量级。1.3 对比独立 LDO 和 DCDC 的取舍很多工程师第一反应是“就这么点电流LDO 够用了”。我做过对比放在一起看更清楚方案优点缺点纯 LDO电路简单、噪声小、成本低压差大时效率低、发热明显、多路面积大、待机电流不可控独立 DCDC LDO效率高、每路可按需优化环路补偿设计工作量大、占板面积大、上电时序需要额外电路保证集成 PMIC高集成度、I2C 可调、时序可编程、保护齐全初期配置复杂、强依赖寄存器手册、选型自由度略低最终选 PMIC 还有一个关键因素项目对体积和整机功耗有硬性要求而且后续还可能调整工作电压来压功耗。PCA9422 的动态调压DVS能力正好满足这个需求这是分立方案很难低成本实现的功能。2. 电源树设计与关键参数的计算2.1 电源树分配确定了 PMIC 之后先把完整电源树画出来。我这个项目是双输入设计USB 5V 适配器和单节锂电池二者通过 PCA9422 的输入引脚接入由 PMIC 内部完成路径切换和充电管理。各输出通道分配如下DCDC1 → 3.3V / 800mA主数字电源给 MCU、Flash、SD 卡DCDC2 → 1.8V / 400mA以太网 PHY 电源DCDC3 → 1.2V / 400mA传感器数字核心电源LDO1 → 3.3V / 50mA常开待机电源给 RTC 备份域、唤醒按键LDO2 → 2.8V / 50mA模拟电源给 ADC 基准和运放供电这里最重要的一条原则是模拟电源绝对不能直接并联到数字 3.3V 上。我一开始偷懒试图省一路 LDO结果 ADC 采样噪声一直压不下去最后老老实实把模拟轨独立出来问题立刻消失。2.2 电感、输出电容与纹波计算DCDC 输出滤波器的参数直接影响纹波和瞬态响应。以 DCDC1 为例输入 3.7V、输出 3.3V、开关频率约 2MHz 时电感纹波电流可以用这个公式估算ΔI (VIN - VOUT) × VOUT / (L × fSW × VIN)代入 L1μH 试算ΔI (3.7 - 3.3) × 3.3 / (1×10⁻⁶ × 2×10⁶ × 3.7) ≈ 178mA这个纹波电流偏大对输出电容要求高也会增加输出电压纹波。把电感换成 2.2μH 再算ΔI (3.7 - 3.3) × 3.3 / (2.2×10⁻⁶ × 2×10⁶ × 3.7) ≈ 81mA这个值就合理多了。输出电容我选 22μF X7R 陶瓷电容ESR 大约 10mΩ 级别那么纹波电压大概是 81mA × 10mΩ ≈ 0.8mV完全满足要求。电感选型还有一条容易忽略的饱和电流必须留够余量。按最大负载 800mA、纹波电流 81mA 计算峰值电流接近 880mA我最终选了饱和电流 1.5A 以上的电感避免负载突然增大时电感饱和导致 DCDC 工作异常。2.3 布局布线的硬规矩电源部分的布局布线我总结了几个必须遵守的规矩输入电容要紧贴 PMIC 的 VIN 引脚输出电容要紧贴 SW 引脚采样反馈线要远离电感。大电流路径走线加宽过孔要足够多充电回路尤其要注意。WLCSP 封装的 PMIC 焊盘扇出要按官方推荐设计不能用普通 BGA 的随意方式处理。电池、PMIC、充电回路下方的地平面要完整最好再铺一块大面积铜帮助散热。有一次 PCB 回来之后充电时芯片附近局部烫手就是地平面不完整、散热路径被切断导致的。重新铺铜之后温度下降了接近 10°C这个问题后面会单独细说。3. I2C 配置流程与上电时序控制3.1 寄存器映射与默认配置PCA9422 所有输出参数都通过 I2C 寄存器控制。先提醒一句不同批次的数据手册寄存器编号可能略有差异下面给出的编号是我们项目实际使用的移植到自己的板子上时务必核对当前手册版本。我习惯把寄存器定义统一放到头文件里#define PMIC_I2C_ADDR 0x42U /* 由ADDR引脚配置决定 */ #define PMIC_REG_DEVID 0x00U /* 器件版本 */ #define PMIC_REG_DCDC1 0x10U /* DCDC1 控制 */ #define PMIC_REG_DCDC2 0x11U /* DCDC2 控制 */ #define PMIC_REG_DCDC3 0x12U /* DCDC3 控制 */ #define PMIC_REG_LDO1 0x13U /* LDO1 控制 */ #define PMIC_REG_LDO2 0x14U /* LDO2 控制 */ #define PMIC_REG_MODE 0x16U /* 工作模式 */ #define PMIC_REG_CHG_CTL 0x17U /* 充电控制 */ #define PMIC_REG_CHG_STAT 0x18U /* 充电状态 */ #define PMIC_REG_SYS_STAT 0x19U /* 系统状态 */每路 DCDC 寄存器的组成一般是“使能位 输出电压档位 工作模式位”。电压档位用 VSET 编码表示具体哪一档对应多少毫伏必须以手册里的编码表为准别凭记忆填。3.2 初始化代码与断电重启初始化逻辑很简单等 PMIC 通信正常后逐路写入配置。我的示例代码如下static void pmic_i2c_write(uint8_t reg, uint8_t val) { uint8_t buf[2] { reg, val }; while (HAL_I2C_Master_Transmit(hi2c_pmic, PMIC_I2C_ADDR, buf, 2, 100) ! HAL_OK) { HAL_Delay(1); } } void pmic_power_init(void) { uint8_t dev_id 0; /* 先读版本号确认I2C链路通畅 */ HAL_I2C_Master_Receive(hi2c_pmic, PMIC_I2C_ADDR, dev_id, 1, 100); /* DCDC1: 3.3V 使能 */ pmic_i2c_write(PMIC_REG_DCDC1, 0x0DU); /* DCDC2: 1.8V 使能 */ pmic_i2c_write(PMIC_REG_DCDC2, 0x14U); /* DCDC3: 1.2V 使能 */ pmic_i2c_write(PMIC_REG_DCDC3, 0x1BU); /* LDO1: 3.3V 常开 */ pmic_i2c_write(PMIC_REG_LDO1, 0x0DU); /* LDO2: 2.8V 使能 */ pmic_i2c_write(PMIC_REG_LDO2, 0x0FU); /* 充电使能目标500mA预充50mA */ pmic_i2c_write(PMIC_REG_CHG_CTL, 0x26U); }写入顺序我是刻意安排的先 DCDC1让 MCU 主电源先起来再去处理其它电源轨。因为整个初始化代码就是 MCU 在跑主电源不稳定后面一切都白搭。这里有一个必须注意的工程点如果 PMIC 没有内部 OTP 保存配置断电后配置会丢MCU 就有“先有鸡还是先有蛋”的问题。我选的是出厂默认使能 DCDC1 并输出 3.3V 的版本后缀所以 MCU 一通电就能跑起来再靠软件把其它轨打开。如果你选的型号默认全关那就必须额外加一个给 MCU 最小系统供电的轻载 LDO否则 MCU 根本没法执行初始化代码。3.3 用示波器验证上电时序软件写完之后上电时序一定要用示波器实测不能靠猜。我当时用四通道示波器同时观察 DCDC1、DCDC2、DCDC3 和 LDO2 的输出波形。正常的波形应该是3.3V 主电源先爬升稳定后 DCDC2 的 1.8V 开始爬升然后是 DCDC3 的 1.2V最后 LDO2 建立。每路上升时间大约 100~500μs过冲不能超过额定值的 5%。我第一次测的时候发现 1.8V 比 3.3V 还早起来PHY 先于 MCU 上电这会导致 MCU 启动时访问未就绪的外设。解决办法是把 PMIC 的上电时序寄存器重新配置强制按 DCDC1 → DCDC2 → LDO2 的顺序执行。还有一点MCU 的复位释放不能刚好卡在电压爬坡的半山腰。最稳妥的做法是用 PMIC 的电源正常POK信号或者独立的复位芯片确认各路电压都稳定之后再释放复位。如果只是用简单的 RC 延时低温环境下电压爬升变慢就很容易出现概率性启动失败。4. 电池充电管理从预充到涓流的完整状态机4.1 CC-CV 充电曲线在 PCA9422 上的落地单节锂电池充电遵循 CC-CV 曲线PCA9422 的充电器内部已经实现了这个流程软件只需要设置参数并监控状态即可。我这边设置的参数是目标电压 4.2V、恒流 500mA、预充 50mA。完整的充电过程分成四个阶段阶段触发条件充电电流结束条件预充电池电压 3.0V50mA电池电压 ≥ 3.0V恒流电池电压 3.0~4.2V500mA电池电压达到 4.2V恒压电池电压 4.2V逐渐下降电流降至 50mA满充电流小于截止阈值停止充电电压回落则重启为什么要预充如果电池过放得很厉害内部阻抗偏大直接大电流充很容易出问题。先小电流把电压拉回安全区间再进入正常充电流程这是锂电池保护的标准做法。4.2 充电状态读取与安全保护充电状态通过状态寄存器读取我写了一个简单的任务函数void battery_task_1s(void) { uint8_t st read_pmic_reg(PMIC_REG_CHG_STAT); if (st 0x01U) { /* 充电中 */ } if (st 0x02U) { /* 满充 */ } if (st 0x04U) { /* 故障需要记录并报警 */ } }除了充电状态PCA9422 还有过压、过流、欠压、过温保护。调试时我把状态寄存器的值通过串口打印出来配合无线充电状态变化能非常直观地看到整个充电过程的状态迁移。软件侧还需要维护一个充电状态机空闲 → 预充 → 恒流 → 恒压 → 满充 → 错误。错误状态必须支持故障恢复比如清除故障标志之后重新检测电池电压再决定是重新充电还是继续报错。4.3 电量监测的简易做法PMIC 一般不直接给出剩余电量百分比需要 MCU 自己估算。我的做法是用 ADC 采样电池电压通过查表法得到大致电量。关键提醒电池电压在充电和放电状态下差距很大不能用同一个表。我实际把充电态和放电态分开查表放电态用开路电压法粗略估算充电态则显示为“充电中”而不是具体百分比。我使用的简化 OCV 表大致如下电池电压剩余电量4.2V100%4.1V90%4.0V80%3.9V65%3.8V50%3.7V35%3.6V20%3.5V10%3.3V0%ADC 采样要多次平均最好在负载稳定时采集否则电机或射频模块一启动电压瞬间跌落会被误判成低电量。5. 低功耗策略DVS 调压与模式切换5.1 功耗预算表和实测数据手持终端的功耗预算是整机能不能超过四小时连续工作的核心指标。我把运行、睡眠、待机三种模式的实际功耗都测了一遍工作模式MCU以太网 PHY其它外设PMIC 自身总计全速运行60mA40mA20mA1mA约 121mA睡眠2mA01mA0.3mA约 3.3mA待机20uA003.5uA约 24uA注意那个 PMIC 自身功耗这是很多方案翻车的根源。分立 LDO 在待机时静态电流经常是几十 μA 起步PCA9422 待机时能压到个位数 μA这意味着整机待机时间可以按年计算而不是按月计算。5.2 DVS 动态调压是怎么实现的DVS 动态调压是整个方案最有价值的功能。原理不复杂在负载较轻的时候通过 I2C 把 DCDC 输出电压调低负载恢复之前再把电压调回来。对 3.3V 的数字电源我睡眠时降到 2.9V仍然满足 STM32F107VC 的 2.0~3.6V 工作范围。代码实现是这样的void sys_enter_sleep_power(void) { /* 关闭传感器数字核电源 */ pmic_i2c_write(PMIC_REG_DCDC3, 0x00U); /* DCDC1 从3.3V降档到2.9V */ pmic_i2c_write(PMIC_REG_DCDC1, 0x0BU); } void sys_exit_sleep_power(void) { /* 先恢复DCDC1到3.3V */ pmic_i2c_write(PMIC_REG_DCDC1, 0x0DU); /* 等待电压稳定后再开DCDC3 */ HAL_Delay(2); pmic_i2c_write(PMIC_REG_DCDC3, 0x1BU); }这里有个细节恢复的顺序必须和降序相反而且每个步骤之间要留时间让电压稳定。我最初写的时候顺序反了结果传感器上电瞬间看到了一个明显的电压毛刺虽然没坏但这种隐患不值得留。5.3 与 STM32 低功耗模式的联动完成了 PMIC 侧的调压接下来要和 STM32F107VC 的低功耗模式做好配合。我总结的标准流程是关闭以太网 PHY 使能通过 PMIC 关闭 DCDC2。配置 RTC 唤醒源和 EXTI 引脚唤醒。调用系统进入 STOP 模式。唤醒后先恢复 PMIC 输出电压。等待电压稳定再重新初始化 I2C 外设和以太网 PHY。特别注意STOP 模式下 I2C 外设时钟会关闭所以唤醒之后 I2C 必须重新初始化否则 PMIC 通信会一直卡死。很多人在这一步踩坑以为 PMIC 出问题了其实是自己外设没恢复。另外不要在 PMIC 处于降档状态时让外设提前上电一定要按“先恢复电源再恢复外设”的顺序。我在代码里用了一个全局状态位记录当前 PMIC 处于哪一种电源模式避免外部中断在恢复过程中触发不完整的电源切换。6. 这几类坑我在调试时一个都没少踩6.1 I2C 扫不到 PMIC地址、上拉、线序刚拿到样板时第一版固件跑起来就发现读不到 PMIC 的器件版本。查了半天最后定位到是 I2C 引脚配置和地址的问题。排查过程是这样的先用万用表量 SCL 和 SDA 是否有上拉电压发现两路电平都异常偏低然后检查 ADDR 引脚的上下拉电阻确认我们的板子上构建的地址确实是 0x42最后用逻辑分析仪观察 I2C 波形结果发现数据线上噪声很重。这里给出一个 I2C 扫描函数调试初期非常管用void i2c_scan_bus(void) { for (uint8_t addr 0x08; addr 0x77; addr) { HAL_I2C_Master_Transmit(hi2c_pmic, addr, NULL, 0, 10); if (HAL_OK HAL_I2C_GetError(hi2c_pmic)) { printf(Found device at 0x%02X\n, addr); } } }最终确认是板上的上拉电阻用了 10kΩ总线电容又偏大导致边沿太缓。换成 2.2kΩ 上拉电阻之后通信恢复正常。这个经验是I2C 上拉电阻要结合实际总线长度和电容选不是随便放一个 10kΩ 就完事。6.2 DCDC 纹波和啸叫电感与电容选型失误DCDC1 输出 3.3V示波器测出峰峰值纹波超过 80mV而且负载稍大时电感发出轻微啸叫。纹波偏大的原因有两点第一我最初选了 1μH 的电感纹波电流偏大第二输出端使用了普通电容ESR 偏高。改成 2.2μH 电感之后纹波电流理论值从 178mA 降到 81mA输出端换成 22μF X7R 陶瓷电容并联 0.1μF实测纹波降到 10mV 以内。啸叫的根源在 PFM 模式轻载时 DCDC 会进入跳周期模式开关频率可能落到音频范围。解决方法是把 PFM 模式改为强制 PWM 模式代价是轻载效率下降。我的取舍是睡眠时直接关掉这一路运行状态才开强制 PWM这样既没有啸叫也保住了待机功耗。6.3 启动复位异常时序错位与软启动冲突有一版程序上电后 MCU 偶发卡死看门狗不断复位。示波器录像回放发现问题出在复位引脚释放时刻3.3V 电压还没有完全建立复位就已经拉高了MCU 在欠压状态下开始执行指令自然不稳定。我做了两个修改第一调整 PMIC 上电时序确保 DCDC1 输出稳定后再让其它电源轨启动第二用 PMIC 的电源正常信号控制 MCU 复位引脚确保各路电压全部就绪后再释放复位。修改之后连跑了上百次上电测试再没有出现过启动失败。另外一个和软启动相关的情况如果 DCDC 输出端挂了太大的总电容上电瞬间会给电容充电电流过大触发 PMIC 的过流保护。我当时在 DCDC2 输出端额外并了两颗 100μF 钽电容结果 PMIC 启动失败。把总输出电容控制在数据手册建议范围内之后问题消失。6.4 充电发热电流设定与散热铺铜充电时外壳局部烫手红外测温枪测到 47°C。分析下来有三个因素充电电流设置为 500mA芯片下方地平面不完整电池连接线压降导致的额外损耗也都集中在板子上。处理方式是把充电电流降到 300mA总充电时间变长但温度降到了 38°C 左右重点重新铺了充电回路的铜在芯片正下方的底层加了大面积地铜并用密集过孔连接顶层和底层。如果项目对充电速度有硬指标那就必须在 PCB 设计阶段就把散热铜面积留足事后补救的效果终归有限。7. 把这套电源管理再往前推一步7.1 故障日志与远程上报硬件和基本驱动稳定之后我继续做了一层故障记录。每秒钟把 PMIC 状态、充电状态、电池电压记录到内存环形缓冲区异常时通过串口或者 CAN 上报。日志格式尽量精简我实际用的是纯文本结构[T001234] vbat3941mV chgCC dcdc13300mV dcdc21804mV fault0x00这个看起来简单但真正量产调试时非常好用。可以在现场设备跑了一个月之后回看故障数据快速判断是充电问题、电源轨问题还是主控低功耗切换问题。7.2 做成可复用的电源驱动模块电源管理代码不要和业务逻辑混在一起。我最后整理成了 pmic.c 和 pmic.h 两个文件对外暴露几个接口typedef struct { uint16_t battery_mv; uint8_t chg_status; uint8_t fault_flags; uint8_t power_mode; } pmic_status_t; void pmic_init(void); void pmic_task_1s(pmic_status_t *st); void pmic_enter_sleep_power(void); void pmic_exit_sleep_power(void);如果项目里用了 RTOS直接把 pmic_task_1s 挂到一个 1 秒周期的软件定时器任务里就行。这样主业务代码完全不需要关心 PMIC 寄存器电源相关的问题都集中在驱动层处理。7.3 量产角度的几个建议从样机走向量产有几个点值得提前考虑如果 PCA9422 支持一次性可编程配置量产时最好直接烧录默认电源配置减少软件初始化失败造成的启动风险窗口。不同批次的 PMIC 寄存器手册版本需要核对建议在固件里留一个“手册版本号 驱动版本号”的对照信息方便现场定位。产线测试项目至少要覆盖空载电流、各路输出电压、充电电流、电池电压采样校准。电池接口、充电芯片、PMIC 附近的 PCB 铺铜要充分这是散热问题的最优解后补的空间很小。最后一个实际操作技巧调试阶段第一次上电先把所有寄存器的默认值全部读出来打印和手册逐项对照一遍然后再开始修改配置。我见过不少开发者在配置改了一半之后遇到奇怪问题最后发现是默认值理解错误导致的。多花这五分钟后面能省下大把排查时间。
返回列表