☰
基于PCA9422的STM32F107电池供电电源管理与低功耗设计
2026/10/10 11:15:04 网站建设 项目流程

最近在做一款电池供电的手持数据采集终端,主控选了 STM32F107VC,外设配置一上来,电源设计就成了最难啃的那块骨头:主控和存储要 3.3V,以太网 PHY 要 1.8V,传感器数字核要 1.2V,模拟前端还要一路干净的 2.8V,另外还得把单节锂电池充电和整机低功耗管起来。

刚开始图省事,用几颗 LDO 分立方案拼凑,实测下来发热、纹波、上电时序三个问题接踵而至:5V 适配器直接供 LDO,150mA 降到 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~150mA
VDD_PHY1.8V以太网 PHY 数字/IO30~80mA
VDD_SENSOR1.2V传感器数字核心30~80mA
AVDD_ANALOG2.8VADC 基准、运放10~20mA
VBAT_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)

代入 L=1μ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 的低功耗模式做好配合。我总结的标准流程是:

  1. 关闭以太网 PHY 使能,通过 PMIC 关闭 DCDC2。
  2. 配置 RTC 唤醒源和 EXTI 引脚唤醒。
  3. 调用系统进入 STOP 模式。
  4. 唤醒后先恢复 PMIC 输出电压。
  5. 等待电压稳定,再重新初始化 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 上报。

日志格式尽量精简,我实际用的是纯文本结构:

[T=001234] vbat=3941mV chg=CC dcdc1=3300mV dcdc2=1804mV fault=0x00

这个看起来简单,但真正量产调试时非常好用。可以在现场设备跑了一个月之后,回看故障数据,快速判断是充电问题、电源轨问题还是主控低功耗切换问题。

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 铺铜要充分,这是散热问题的最优解,后补的空间很小。

最后一个实际操作技巧:调试阶段第一次上电,先把所有寄存器的默认值全部读出来打印,和手册逐项对照一遍,然后再开始修改配置。我见过不少开发者在配置改了一半之后遇到奇怪问题,最后发现是默认值理解错误导致的。多花这五分钟,后面能省下大把排查时间。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询