news 2026/10/10 4:53:24

低功耗设备电源管理方案:PCA9422 PMIC与STM32F100ZE协作实战

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
低功耗设备电源管理方案:PCA9422 PMIC与STM32F100ZE协作实战

做低功耗物联网设备的朋友,应该都对电源管理这块又爱又恨。我前阵子用 PCA9422 和 STM32F100ZE 这两颗料,完整搭过一套电源管理方案,把锂电池充电、多路供电、电量监测、低功耗切换这几件事全收了进去。PCA9422 作为 PMIC 负责底层电源变换和充电控制,STM32F100ZE 作为主控负责配置、监控和策略调度,两端配合下来,整个板子的电源部分非常干净。这篇文章就围绕这个组合,把整体架构、硬件设计、固件实现和调试过程里比较关键的东西完整讲一遍,适合正准备做类似低功耗产品、或者想搞明白 PMIC 如何跟 MCU 协作的朋友参考。

1. 整体设计思路与方案选型

1.1 这里说的“完整电源管理”到底要管什么

很多人一提电源管理,第一反应就是“能开机、电压对就行”,但真正做产品就会发现远没那么简单。我当时做的项目是一个低功耗温湿度标签,一节锂电池供电,板上同时挂了主控、无线通信模块、传感器、LED 指示灯,还有一块很小的屏幕。仔细一列需求,发现电源这块要管的事特别多:电池要能充,充电电流要能根据电源适配器能力调整;系统需要多路电压,3.3V 给主控和传感器,1.8V 给无线模块和部分外设,LED 驱动还需要一个独立可关断的供电轨;待机功耗要压到微安级,否则标签放半个月就没电;另外还要实时知道电池还剩多少电、有没有过温、充电有没有异常。

如果这些事儿全用分离器件去拼,电路会变得非常庞大。一路 LDO 加一路 DCDC,再加一个充电管理 IC,最后接一堆电平转换、MOS 管开关,硬件改版一次,软件也跟着遭罪。所以这类需求下,最自然的选择就是把“电源管理”这件事交给一颗专门的 PMIC,把“策略和控制”交给 MCU。PMC 管的不是某一路电,而是一个完整的电源子系统。

1.2 为什么选 PCA9422 搭配 STM32F100ZE

先说 PCA9422。这颗 PMIC 吸引我的点主要有三个。第一,它把充电回路和多路 DC-DC 整合在一起,外围只需要很少的阻容和电感,BOM 很干净。第二,关键参数全部可以通过 I2C 改写,输出电压、充电电流、中断使能这些都不用改电阻,调试阶段特别省事。第三,它内部带了 ADC 和放电监测相关电路,电池电压、电流、温度这类信息可以直接从寄存器里读,省掉了外部分压采样网络。

再说为什么主控选了 STM32F100ZE。这颗芯片虽然是 Cortex-M3 内核、主频 24MHz,性能不算强,但做电源管理策略绰绰有余。而且它的外设资源非常齐全,I2C、USART、ADC、定时器、丰富 GPIO 全都有,144 引脚封装给扩展留了很大余量。我当时手上正好有这颗料,加上它价格实在,就直接定了。这里有个经验:选主控不要只看算力,要看接口数量够不够、低功耗模式好不好用、调试工具熟不熟。F100ZE 的低功耗模式在 Stop 下能保持 RAM 内容,配合 RTC 唤醒做低功耗标签项目完全够用。

这类组合的另一个优势是,芯片之间的协作关系非常清晰。PCA9422 负责“把电池和外部输入变成各路稳定的电源”,STM32F100ZE 负责“告诉 PMIC 怎么配置、读取状态、在合适时机切到合适的电源策略”。相当于 PMIC 是执行层,MCU 是管理层,职责边界清楚,出了问题也容易定位。

1.3 系统电源树设计与启动顺序

电源树是整个设计的骨架,我一般先画清楚再动电路。这个项目的电源树大致是这样的:

电源通道输出电压主要负载时序/开关策略
输入 VBAT3.0V~4.2V电池直接通路始终在线
BUCK 通道 03.3VMCU、传感器、指示灯常开,系统启动后输出
BUCK 通道 11.8V无线模块、电平转换由 GPIO 控制使能
LDO 通道3.0VLCD 背光/模拟电路按需开,低功耗时关断
充电输入5V USB充电回路检测到 VBus 才启用

这个划分的原则有两条。一是主控供电必须最可靠,所以 3.3V 这一路放在常开通道上,上电先建立,确保 MCU 第一时间跑起来。二是耗电大或不需要一直供电的外设,尽量放在可独立关断的通道上,这样进入低功耗模式时可以直接把对应输出切掉,把待机电流压下去。启动顺序方面,PCA9422 上电后先进入默认安全状态,由 MCU 初始化完成后写入新的配置,再逐个使能输出通道。这样就避免了外设在主控还没初始化时就进入未知状态。

这套设计下来,比原来的分离器件方案简洁很多。硬件上少了一堆 LDO、MOS 管和分压电阻,软件上也统一成一个 I2C 读写的问题,整个项目后续迭代都轻松不少。

2. 硬件设计要点与关键参数

2.1 PCA9422 的功能拆解

PCA9422 这一类 PMIC 的寄存器结构虽然各家有差异,但功能模块大致是固定的。我按功能拆开讲,方便大家对照手册快速定位。

第一个模块是充电控制。它支持对单节锂电池进行线性充电,输入一般接 5V USB。需要关注的寄存器主要是充电电流设定、充电截止电压、充电超时时间、充电状态标志。充电电流建议根据电池容量来定,我实际用的是 0.5C 左右,也就是 1000mAh 的电池按 500mA 充,既能保证速度,又不会让电池温升太快。如果输入电源能力有限,还需要把输入电流限制配小一点,防止把适配器拉垮。

第二个模块是降压输出通道。PCA9422 上有多个功率输出通道,我用到的有降压通道和 LDO 通道。降压通道效率高,适合电流大的负载;LDO 通道纹波小,适合模拟电路,但压差大时功耗也会大。输出通道的电压值、软启动时间、使能控制都在寄存器里配置。

第三个模块是监测与中断。内部 ADC 可以采电池电压、充电电流、芯片温度等参数,状态寄存器里也会有过压、欠压、过温、充电完成、充电异常这些标志。中断引脚 INT 可以配置成事件触发,MCU 通过外部中断来感知电源状态变化,不用一直轮询。

使用 PCA9422 时最重要的一点是,上电默认值不一定适合你的系统。有些输出通道可能默认关闭,有些中断默认屏蔽,所以初始化代码里第一件事就是完整配置一遍,而不是假设“上电就能用”。

2.2 STM32F100ZE 的资源分配与引脚规划

STM32F100ZE 的资源在这个项目里不算紧张,但引脚规划我还是提前做了表,避免画板时乱了。

功能STM32F100ZE 引脚/外设说明
I2C 总线I2C1,SCL/SDA 接到 PCA9422400kHz 快速模式,加 4.7k 上拉
电源中断输入GPIO 外部中断,如 PA0下拉配置,上升沿/低电平有效需按手册
充电状态 LEDPB0 推挽输出接 LED 限流电阻
无线模块使能PB1 推挽输出控制 BUCK 输出通道使能
RTC 唤醒内部 RTC用于周期性唤醒低功耗状态
SWD 调试口PA13/PA14保留,方便调试

I2C 这块要特别注意。PCA9422 是标准的 I2C 从机,地址由硬件或寄存器决定,上电后建议先读一次芯片 ID 确认总线没问题。STM32 的 I2C 引脚默认是开漏模式,所以外部必须有上拉电阻,我用的是 4.7kΩ,400kHz 下波形很好。如果板上还有其他 I2C 设备,要注意地址冲突和总线电容,总线过长或挂载设备过多时要适当降低速率。

引脚分配上,另一个容易忽视的点是中断引脚。PCA9422 的 INT 脚要接到 STM32 上带外部中断能力的引脚,并且要配置为上拉或下拉的输入模式,防止悬空误触发。我在早期调试时就是没注意这个,充电器一插上就触发中断,查了半天才发现是引脚配置问题。

2.3 PCB 布局与滤波的细节

电源电路的 PCB 布局,直接影响纹波和稳定性。PCA9422 的降压通道用的电感,我建议选屏蔽式功率电感,型号按输出电流和纹波要求来。电容方面,输入输出都要放足够的陶瓷电容,特别要注意靠近芯片引脚放置,缩短回路路径。

有两种比较容易踩的坑。一是地回路处理不当,大电流的功率地和小信号的模拟地混在一起,导致 ADC 采样值乱跳。二是电感底下的铺铜处理,大面积铺铜会改变电感特性,反而可能让噪声变大。比较好的做法是,按照芯片手册里的参考布局来画,功率回路尽量短粗,反馈走线远离电感,保持 PCB 布局整洁。

另外,如果板上同时有无线模块,电源噪声会直接影响射频性能。我在布局时尽量让无线模块的供电线远离电感,并在靠近无线模块的位置额外加了一级小电容滤波,实测对射频灵敏度有帮助。

3. 固件实现与核心流程

3.1 I2C 驱动编写与寄存器读写封装

固件开发的第一步是把 I2C 驱动跑通。STM32F100ZE 的 I2C1 用标准外设库或 HAL 都可以,我这边用的是 HAL 库,先把初始化写好:

#include "stm32f1xx_hal.h" I2C_HandleTypeDef hi2c1; void MX_I2C1_Init(void) { hi2c1.Instance = I2C1; hi2c1.Init.ClockSpeed = 400000; hi2c1.Init.DutyCycle = I2C_DUTYCYCLE_2; hi2c1.Init.OwnAddress1 = 0; hi2c1.Init.AddressingMode = I2C_ADDRESSINGMODE_7BIT; hi2c1.Init.DualAddressMode = I2C_DUALADDRESS_DISABLE; hi2c1.Init.OwnAddress2 = 0; hi2c1.Init.GeneralCallMode = I2C_GENERALCALL_DISABLE; hi2c1.Init.NoStretchMode = I2C_NOSTRETCH_DISABLE; HAL_I2C_Init(&hi2c1); }

初始化完成后,读写封装成两个函数:

#define PCA9422_I2C_ADDR 0x28 // 实际地址以芯片手册为准 uint8_t pca9422_read_reg(uint8_t reg) { uint8_t val = 0; HAL_I2C_Mem_Read(&hi2c1, PCA9422_I2C_ADDR << 1, reg, I2C_MEMADD_SIZE_8BIT, &val, 1, 100); return val; } void pca9422_write_reg(uint8_t reg, uint8_t val) { HAL_I2C_Mem_Write(&hi2c1, PCA9422_I2C_ADDR << 1, reg, I2C_MEMADD_SIZE_8BIT, &val, 1, 100); }

寄存器地址和具体位域定义,建议在头文件里做成宏,方便维护。封装层面我习惯再加一层“读改写”函数,因为很多寄存器只有某些位是可写的,直接整片覆盖容易误清状态位。比如先读回寄存器值,改完目标位段再写回去,这样能减少因为不了解保留位含义而踩的坑。

3.2 上电初始化流程与关键配置

系统上电以后,STM32F100ZE 先执行基础时钟配置和 I2C 初始化,然后就进入 PCA9422 的初始化流程。我的实际顺序如下:

void pca9422_init(void) { uint8_t id = 0; // 第 1 步:检查 I2C 通信是否正常 id = pca9422_read_reg(REG_CHIP_ID); if (id != EXPECT_CHIP_ID) { error_handler(); return; } // 第 2 步:设置各路输出电压 pca9422_write_reg(REG_BUCK0_CFG, 0x1A); // 3.3V pca9422_write_reg(REG_BUCK1_CFG, 0x0F); // 1.8V pca9422_write_reg(REG_LDO_CFG, 0x14); // 3.0V // 第 3 步:配置充电电流与截止电压 pca9422_write_reg(REG_CHG_CFG, 0x32); // 500mA pca9422_write_reg(REG_CHG_VLIM, 0x04); // 4.2V // 第 4 步:使能需要的中断 pca9422_write_reg(REG_INT_MASK, 0x00); // 开启所有电源事件中断 // 第 5 步:使能输出通道 pca9422_write_reg(REG_CTRL, 0x07); }

每步为什么要这么做,我简单说一下。第 1 步读芯片 ID 是最重要的一次握手,它同时验证了地址、上拉、电平转换、I2C 时序,如果这里不过,后面全都不用谈。第 2 步必须在使能输出之前完成,否则通道已经输出默认电压,再改电压会有短暂波动,可能引起负载复位。第 3 步充电参数放到输出使能之后配置,是因为充电回路通常在检测到 VBus 以后才真正工作,不会跟主电源输出抢初始化资源。第 4 步和第 5 步打开中断和输出,顺序上先配中断再使能输出,可以保证输出建立过程中的异常事件第一时间被捕获。

需要提醒的是,芯片上电默认的 ID 核对完成以后,不要着急把整段初始化封装成一个“看不见细节”的黑盒函数。调试阶段尽量把每一步的寄存器值通过串口打印出来,或者用逻辑分析仪抓 I2C 波形确认。曾经有一次我改了输出电压配置,但代码里少写了一行寄存器使能,结果输出还是默认值,抓波形才发现写进去的值根本没生效。

3.3 电量监测与低功耗状态切换

电量监测这块,我主要是通过 PCA9422 内部 ADC 读电池电压寄存器来实现,配合软件做简单的电量百分比映射。比如读回电压值经过换算得到毫伏,再按电池放电曲线分几个区间,显示成电量百分比。不要求非常精确的话,这个方案性价比很高。

uint16_t get_battery_mv(void) { uint8_t adc_h = pca9422_read_reg(REG_VBAT_ADC_H); uint8_t adc_l = pca9422_read_reg(REG_VBAT_ADC_L); uint16_t raw = (adc_h << 8) | adc_l; return (uint16_t)((float)raw * ADC_LSB_MV); }

低功耗状态切换是整个项目里比较关键的部分。我的做法是,STM32F100ZE 需要进入 Stop 模式前,先把外设关掉,然后把 PCA9422 不需要的输出通道关闭,再设置 RTC 唤醒时间,最后执行 STOP 指令:

void enter_low_power_mode(void) { // 关闭无线模块供电通道 pca9422_write_reg(REG_CTRL, 0x03); // 仅保留 3.3V 通道 // 关闭无关外设时钟 __HAL_RCC_GPIOB_CLK_DISABLE(); __HAL_RCC_GPIOA_CLK_DISABLE(); // 关闭 I2C,省掉总线静态功耗 HAL_I2C_DeInit(&hi2c1); // 配置 RTC 唤醒 HAL_RTC_SetAlarm_IT(&hrtc, &sAlarm, RTC_ALARM_A); // 进入 Stop 模式 HAL_SuspendTick(); HAL_PWR_EnterSTOPMode(PWR_LOWPOWERREGULATOR_ON, PWR_STOPENTRY_WFI); }

唤醒以后,重新初始化 I2C,把 PCA9422 的输出通道重新打开,再恢复外设配置。这中间要注意一个细节:从 Stop 模式唤醒后,外设时钟状态可能已经改变,不能直接继续原先的操作,而是要重新做一遍初始化。我的代码里把“电源管理初始化”单独抽成了一个函数,唤醒和上电都调用它,减少重复代码。

3.4 中断处理与故障恢复

PCA9422 的 INT 引脚接到 STM32 的外部中断,中断回调里读状态寄存器,判断具体是什么事件:

void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin) { if (GPIO_Pin == INT_PIN) { uint8_t status = pca9422_read_reg(REG_INT_STATUS); if (status & FLAG_CHG_DONE) { // 充电完成,更新电池状态 } if (status & FLAG_CHG_FAULT) { // 充电异常,进入保护逻辑 pca9422_write_reg(REG_CTRL, 0x00); } if (status & FLAG_OVERTEMP) { // 过温保护,关闭非必要输出 pca9422_write_reg(REG_CTRL, 0x01); } } }

中断处理的第一原则是快进快出。中断里不要做 I2C 长阻塞操作,尤其是 400kHz 通信在中断里等应答这种事,会拖慢整个系统。我一般是在中断标志位置位后,只做最少的处理,具体逻辑放到主循环的任务里执行。如果某些状态必须立即响应,那也要设置超时保护,防止读状态寄存器卡死在中断里。

故障恢复方面,比较有效的做法是维护一个“电源状态机”,把正常运行、低功耗、充电中、故障保护这些状态管理起来。每个中断事件都触发状态机迁移,避免出现多套逻辑互相打架。我早期没做状态机时,出现过充电完成中断和低功耗切换同时发生导致输出混乱的情况,后来统一用一个状态机管理,问题就没了。

4. 调试实战与问题排查记录

4.1 I2C 通信异常的排查顺序

这个项目里我遇到最多的就是 I2C 通信问题。整理一下排查顺序,基本能解决九成的情况。

第一,检查地址。PCA9422 的 7 位地址写没写错,发送时有没有正确左移一位变成 8 位地址。我第一次就犯过这个错,直接把 7 位地址传给了 HAL 库,结果总线 ACK 一直收不到。第二,检查上拉电阻。I2C 开漏总线没有上拉啥都白搭,4.7k 是常用值,但总线过长、挂载设备多时可以换 2.2k 试试。第三,抓波形。用逻辑分析仪抓 SCL/SDA,看起始条件、地址帧、数据帧的时序对不对。很多“偶发错误”其实是上拉太弱导致上升沿过缓。第四,看电平匹配。如果 PCA9422 的电源域和 STM32 的 I2C 引脚电平不一致,需要加电平转换,否则读到的数据会乱码。

4.2 电源纹波超标的处理

有段时间无线模块的发射距离明显不够,排查到最后发现是 1.8V 电源轨纹波超标。改动主要有三处:把降压电感的额定电流提高了一档,避免大电流时电感饱和导致纹波变大;在输出端增加了一颗 10µF 陶瓷电容,同时保证它尽量靠近负载端;把反馈走线从电感下方绕开。改完以后纹波从 60mV 降到了 20mV 以内,问题解决。

4.3 充电管理上的典型 Bug

充电这块最开始有个问题:插上 USB 后,充电状态一直显示异常。后来发现是充电电流设得太大,而 USB 输入端的限流保护被触发了。把充电电流从 800mA 降到 500mA 以后,问题消失。还有个问题是电池电压采样不稳定,最后确认是 PCB 上采样走线太靠近电感,在地回路里出现了电压偏置,调整走线位置后数据就稳定了。

4.4 实测数据与优化记录

项目优化前优化后
待机电流35µA8.5µA
无线发射时系统电流67mA61mA
1.8V 纹波60mV18mV
电池电量误差±20%±8%

待机电流从 35µA 降到 8.5µA,主要是把 LDO 通道彻底关断、I2C 总线释放、并且把 INT 引脚的中断配置成了仅在事件发生时唤醒,避免每 10 秒就会被唤醒一次去轮询状态。电量误差减小是因为在软件里做了电压补偿,并且过滤了瞬时负载变化引起的电压跌落。

这几个问题排查下来,我最大的体会是电源调试不能靠猜。每次改动只动一个变量,改完以后用示波器或电流表记录数据,前后对比,比凭感觉试要高效得多。尤其是 PMIC 这种 I2C 配置的设备,寄存器状态和硬件电路是绑在一起的,错误的寄存器值会让硬件表现得很像一个“硬件故障”,这时候一定要先回到寄存器层面验证配置。

最后分享一个小技巧:在做这类电源管理项目时,板上一定预留一个独立的调试串口和一对 I2C 测试点。调试阶段把初始化过程、状态切换、中断触发都通过串口打印出来,I2C 测试点用来夹逻辑分析仪探头,很多看似玄学的问题,抓到波形和日志以后,基本都能定位到具体原因。这套 PCA9422 加 STM32F100ZE 的方案,我后续在低功耗传感类产品上还会继续用,整体来说是省心且可控的组合。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/10 4:52:52

基于STM32F042与PCA9422的低功耗电源管理方案设计

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/10/10 4:52:30

OFDM多径时延与多普勒频移仿真:MATLAB链路实现与避坑指南

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/10/10 4:50:38

多航天器编队变换如何用MAPPO:环境建模、奖励设计与训练调优

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/10/10 4:50:26

给编程智能体写插件:余额胶囊、任务面板与番茄钟实战指南

先说个真实场景。上个月我用 dsh——我日常主力用的 DeepSeek 编程智能体&#xff0c;社区里也有人把它简写成 DSH——跑一个中型后端重构任务。原以为一两个小时就能收工&#xff0c;结果凌晨三点它还在终端里刷日志&#xff1a;一会儿往项目里塞新文件&#xff0c;一会儿又把…

作者头像 李华