刚拿到手这块板子时,我其实被标题里的“完整电源管理”吸引住了。PCA9422是一颗集成了多路DC-DC、LDO和充电管理的PMIC,STM32F302VC则是一颗带FPU的Cortex-M4F控制芯片,直接把它俩放在同一套系统里,能解决的问题远比“找个稳压芯片”要多。这篇文章不讲空概念,就围绕硬件连接、固件初化、上电时序、电池管理和联调踩坑这几个真实环节展开,把我实际做这套方案的过程和改造思路完整写一遍,希望能给正在做便携设备、低功耗产品或需要多路电源域控制的工程师提供一套可直接套用的实践参考。
1. 选型动机:为什么把 PCA9422 交给 STM32F302VC 管理
我见过不少项目,电源部分就是一颗LDO加几个电容,MCU能跑起来就算完事。但对于带屏幕、带无线模块、带多个传感器的设备,这种草率做法会带来一连串后续问题:上电瞬间电流冲击大、不同外设之间相互干扰、休眠时静态电流降不下去。选 PCA9422 加 STM32F302VC 这个组合,本质上是想把“电源管理”从硬件上的被动稳压,升级成软件可配置、可监控、可动态调整的主动系统。
选择 STM32F302VC 并不是因为它性能强,而是因为这颗芯片的模拟外设配置很适合做电源管理节点。它内部集成了多路ADC、DAC和比较器,配合丰富的高级定时器,可以非常自然地承担电压采集、电流计算、充放电状态判断这类实时任务。更重要的是,STM32F302VC 的工作电压范围宽,功耗模式多,可以和 PMIC 的输出电压域做灵活的匹配。相比必须挂一颗专用电源管理芯片再加一颗低端 MCU 去“打辅助”的方案,主控直接用 STM32F302VC,可以减少一颗独立芯片,也省去了主控与电源管理之间的额外通信链路。
那 PCA9422 在这套方案里扮演什么角色?它的核心价值是“集成”和“可控”。PCA9422 内部集成了两个降压转换器和若干 LDO,还包含了电池充电管理模块,外围只需要少量电感电容就能构成多路输出。它通过 I2C 接口开放了输出电压调节、限流配置、工作模式切换、状态读取等能力,这意味着电源系统的行为可以由 MCU 按场景动态调整,而不是出厂焊死的一套固定参数。
这套组合的典型应用场景包括:带彩屏的便携仪表、低功耗传感器节点、带4G或Wi-Fi模块的手持设备,以及一些需要对 sensor 供电进行独立开关控制的边缘计算盒子。共同点是,它们都有多个供电域、需要较长的电池续航、而且对电源状态的实时监控有明确要求。在这种场景下,PCA9422 提供“执行能力”,STM32F302VC 提供“控制大脑”,两者配合才能实现真正的完整电源管理。
1.1 没有上电时序,MCU 会被自己外设“拉死”
我最初调试这个方案时,遇到过一件很诡异的事:代码烧进去,系统偶尔能正常启动,偶尔死机,按压复位键还不一定能救回来。后来用逻辑分析仪同时抓了几路电源轨和复位信号,才发现问题是外设上电顺序不正确。
STM32F302VC 本身对这个问题的敏感性一般,但它外面挂的 LCD 模块、LTE 模组和传感器就不一样了。如果 LCD 的电源轨先于 IO 口初始化建立,那么 MCU 的引脚在复位期间可能处于高阻态,LCD 的电源会通过内部保护二极管反向灌进 MCU 的电源网络。这种情况下,MCU 主电源电压会被抬高,复位电路失效,整个系统的启动行为就变成“碰运气”。
PCA9422 的多个输出通道正好可以用来解决这类问题。通过合理规划输出通道的使能顺序,确保数字核心先上电,IO 外设电源后上电,外部模组最后上电,就能避免绝大多数由于电源竞争导致的启动异常。很多人觉得上电时序只有大公司产品才需要关注,但实际上任何带多个电源域的设备都逃不开这个问题,只是有些设备运气好没暴露而已。
1.2 芯片规格互补性分析
这套方案里,PCA9422 和 STM32F302VC 的分工非常明确:
| 功能模块 | PCA9422 | STM32F302VC |
|---|---|---|
| 电压变换 | 多路 DCDC + LDO | 不参与,只负责控制 |
| 电池充电 | 内置充电管理 | 通过 I2C 读取充电状态 |
| 系统监控 | 提供 ADC 采样和状态寄存器 | 执行算法、判断策略 |
| 时序控制 | GPIO 使能输入 | 软件编排时序状态机 |
| 低功耗 | 支持低静态电流模式 | 支持睡眠、停止、待机模式 |
这种分工的好处是,硬件上的电源“肌肉”和软件上的控制“神经”各司其职,互不干扰。PCA9422 不需要跑代码,它的模拟电路天然具有快速响应能力,可以及时处理过流、过压等突发状况;而 STM32F302VC 则可以专注于策略层面的判断:当前该不该升压、该不该关掉某路 LDO、电量偏低时要不要提醒用户切换省电模式。
从成本角度来说,PCA9422 虽然是颗功能丰富的 PMIC,但对于批量产品而言,用一颗集成芯片替代多颗分离式的 DC-DC、LDO 和充电芯片,BOM 物料数量和贴片成本都会明显下降。STM32F302VC 作为一颗通用 MCU,采购渠道广、开发资料多、上手门槛低,整体风险控制在工程上完全可接受。
1.3 适用方案的产品画像
并不是所有项目都需要这么完整的电源管理方案。如果产品只是三节干电池供电、单路 3.3V 输出、对续航不敏感,那用一颗经典 LDO 加旁路电容就绰绰有余。但下面这些特征出现任意两三条,就该考虑用 PCA9422 + STM32F302VC 这种组合:
- 系统同时需求 3.3V、1.8V、5V 等多路电压;
- 主控和外设需要独立供电域,以实现按需开关;
- 设备使用单节锂离子电池,需要充电和放电同时管理;
- 产品对休眠电流有明确指标,例如低于 50uA;
- 工程师希望能在不改硬件的情况下,通过固件调整输出电压。
拿着这几个特征去对照自己的项目,就能判断这套方案是不是合适,而不是盲目跟风。我见过有人做一台简单的温湿度计也强行上了 PMIC,最后发现成本翻倍、调试复杂,意义不大。选型这件事,还是得回到产品目标本身。
2. 硬件连接设计:引脚规划才是这套系统的地基
选定了芯片组合,接下来最重要的一步不是写代码,而是把硬件连接图画对。PCA9422 和 STM32F302VC 之间的连接,主要集中在 I2C 控制总线、GPIO 使能/中断信号以及 ADC 采样通道这三类线上。尤其需要注意,STM32F302VC 不只是把 PCA9422 当作一个“受控电源芯片”,它还会读取 PMIC 内部的 ADC 采样结果,所以在画原理图时,采样点和信号回流路径要提前规划。
2.1 控制总线接线:I2C 与中断信号的连接方式
PCA9422 的 I2C 从机地址由外部引脚的电平状态决定,默认情况下如果 I2C 地址引脚接地,则从机地址为 0x64;如果接高电平,则为 0x65。在硬件设计时,我建议把地址引脚接到一个上拉电阻到 VDDIO,同时预留一个 0 欧姆电阻到地,这样后期如果想挂两片 PCA9422 或者改地址,还能通过贴电阻的方式切换。
I2C 总线需要接上拉电阻,这个大家都很清楚,但阻值的选取经常被忽略。STM32F302VC 工作在 3.3V 逻辑电平,PCA9422 的 I2C 引脚也支持 3.3V,所以上拉电阻选 2.2kΩ 到 4.7kΩ 比较合适。有些工程师习惯性用 10kΩ,结果 I2C 通信在 400kHz 速率下上升沿过慢,导致信号完整性问题。建议先把 PCA9422 当作唯一从机、总线电容较小的场景来折算:400kHz 下总线上拉电阻不要大于 4.7kΩ。
中断信号线也是必须规划的。PCA9422 能够产生充电完成、故障、低电压等中断事件,这些事件通过中断引脚通知 MCU。推荐把中断引脚连接到 STM32F302VC 的一个 EXTI 外部中断引脚上,并且配置为下降沿触发。这样 MCU 不需要轮询 PMIC 状态寄存器,只有在确实发生事件时才被唤醒,对低功耗设计非常友好。
2.2 GPIO 使能和状态回读引脚的接线设计
除了 I2C,PCA9422 还有一些专用控制脚,可以让硬件逻辑直接控制电源输出的开关。STM32F302VC 的 GPIO 只要配置为推挽输出模式,就可以直接驱动这些输入脚。要注意的是,PCA9422 的使能输入内部可能有上拉或下拉,实际极性要以数据手册为准。我给的建议是,画原理图时在每个使能脚上预留一个串联 1kΩ 电阻,方便测量电平,也可以防止因为 GPIO 配置错误导致短路。
多个 GPIO 同时控制多个电源输出时,STM32F302VC 的 GPIO 初始化顺序就很重要。如果在一上电就把全部 GPIO 拉高,PCA9422 会按照硬件默认配置输出,这不一定符合预期的时序要求。更稳妥的做法是在固件启动初期,先把所有控制 GPIO 都配置为输入模式(高阻态),等系统时钟就绪、I2C 初始化完成之后,再切换到输出模式并按状态机逐个使能输出通道。
2.3 ADC 采样链路的电路考量
STM32F302VC 自带的 ADC 在这里用来采样电池电压和系统重要电压点。PCA9422 内部虽然也有 ADC,但它主要服务于自身保护和状态上报,采样精度和灵活性都不如直接用 MCU 的 ADC 来得高。因此,我保留了一个独立的分压采样网络,将电池正极电压分压后接入 STM32F302VC 的 ADC 通道。
分压电阻的选值需要控制静态电流和采样精度两个因素。以单节锂电池满电 4.2V 为例,如果要让 ADC 输入不超过 3.3V,分压比大约为 1:0.78。可以采用 100kΩ 和 27kΩ 的组合,分压系数约为 0.79,静态电流在 33uA 左右,对电池容量影响可以忽略。同时需要在 ADC 引脚并联一个 0.1uF 电容,用于滤除高频噪声,并在软件中启用 STM32 内部 ADC 的采样保持时间,确保采样稳定。
3. 固件工程的起点:I2C 读写与寄存器配置
硬件连好之后,剩下的事情基本都在固件里完成。这套方案的固件核心,首先是要建立一条稳定、可靠的 I2C 通信链路,然后才能去配置 PCA9422 的寄存器。很多项目失败,并不是寄存器配置不对,而是 I2C 底层都不稳定,导致配置写到一半总线卡死。
3.1 给 STM32F302VC 写一套可靠的 I2C 底层
STM32F302VC 的 I2C 外设支持标准模式(100kHz)和快速模式(400kHz)。我的习惯是先把通信速率设置为 400kHz,因为 PCA9422 的寄存器操作比较多,特别是在初始化阶段要连续写几十个寄存器,速率太低会拖慢启动时间。
初始化 I2C1 外设的代码看起来大致是这样的:
void i2c1_init(void) { LL_APB1_GRP1_EnableClock(LL_APB1_GRP1_PERIPH_I2C1); LL_I2C_InitTypeDef I2C_InitStruct = {0}; I2C_InitStruct.PeripheralMode = LL_I2C_MODE_I2C; I2C_InitStruct.Timing = LL_I2C_GetTiming(400000, 0, 0, 0, 0); I2C_InitStruct.AnalogFilter = LL_I2C_ANALOGFILTER_ENABLE; I2C_InitStruct.OwnAddress1 = 0x00; LL_I2C_Init(I2C1, &I2C_InitStruct); LL_I2C_Enable(I2C1); LL_GPIO_SetPinMode(GPIOB, LL_GPIO_PIN_6 | LL_GPIO_PIN_7, LL_GPIO_MODE_ALTERNATE); LL_GPIO_SetAFPin_0_7(GPIOB, LL_GPIO_PIN_6, LL_GPIO_AF_4); LL_GPIO_SetAFPin_0_7(GPIOB, LL_GPIO_PIN_7, LL_GPIO_AF_4); LL_GPIO_SetPinSpeed(GPIOB, LL_GPIO_PIN_6 | LL_GPIO_PIN_7, LL_GPIO_SPEED_HIGH); }这段代码用 LL 库实现,初始化完成后 I2C1 的 SCL 和 SDA 分别映射到 PB6 和 PB7。这里强调引脚复用功能配置,是因为很多人在这里出错,GPIO 没有正确设置为复用模式,导致 SCL/SDA 完全没有波形输出。
底层驱动里,我建议写一组统一的 I2C 读写函数,所有上层代码都通过这两个函数访问 PCA9422。这样如果后续需要切换到软件模拟 I2C,或者更换总线引脚,只需要修改底层,而不用改动应用层代码。
3.2 一份可直接对照的 PCA9422 寄存器初始化清单
PCA9422 的寄存器组织方式相对规整,主要分电源配置、充电配置、状态读取和中断控制几类。初始化过程中,推荐按照“先全局后局部、先电源后充电”的顺序进行配置。下面是一份我实测可用的初始化流程:
| 步骤 | 寄存器功能 | 写入值说明 |
|---|---|---|
| 1 | 复位 PMIC | 写入软复位命令,确保从默认状态开始 |
| 2 | 配置 SW1 输出电压 | 按系统主电源需求设为 3.3V |
| 3 | 配置 SW2 输出电压 | 按外设需求设为 1.8V |
| 4 | 配置 LDO 输出电压 | 设为 3.3V 或按传感器需求 |
| 5 | 配置充电电流 | 根据电池容量设置在 500mA~1A |
| 6 | 配置充电电压 | 标准 4.2V 或 4.35V 电芯对应值 |
| 7 | 使能中断输出 | 打开充电完成和故障中断使能位 |
| 8 | 使能各通道输出 | 逐个使能,而不是一次性全部打开 |
关键点是第 8 步。这里隐藏了一个策略:先通过 I2C 配置好所有电压参数,但不立即使能输出,等 STM32F302VC 的所有初始化完成之后,再通过软件执行上电时序。这样可以完全避免“MCU 还在配置途中 PMIC 已经乱输出”的问题。
某些寄存器还需要设置斜坡上升时间,也就是输出电压从零爬升到目标值所用的时间。PCA9422 提供了斜坡时间配置位,合理的设置能显著抑制上电瞬间的浪涌电流。对于有大容量电容负载的输出通道,我建议将斜坡时间设置为 1ms 级别,而不是用最快的斜坡,否则电源瞬间从 0V 拉到 3.3V,会造成输入电压跌落,甚至触发 PMIC 的欠压保护。
3.3 配置一致性校验:防止寄存器被悄悄改写
PMIC 的寄存器不是永久的,任何一次静电放电、电源毛刺或者 I2C 总线上的干扰信号,都可能让寄存器值发生偏移。虽然概率不高,但在医疗设备或工业控制等对可靠性要求高的场合,不能忽视这一点。
我的做法是在初始化完成后,每隔一段时间主动读取关键寄存器并与期望值比对。具体来说,在 RTOS 的周期性任务里,将电源配置寄存器组做一次“配置摘要”校验。如果校验失败,则执行重新初始化流程,将全部寄存器恢复到期望值。
static const uint8_t pmic_expect_cfg[PMIC_CFG_REG_NUM] = { PMIC_REG_SW1_CFG, PMIC_REG_SW2_CFG, PMIC_REG_LDO_CFG, PMIC_REG_CHARGE_CFG, PMIC_REG_INT_EN }; uint8_t pmic_cfg_check(void) { uint8_t buf[PMIC_CFG_REG_NUM]; if (i2c_read_regs(PCA9422_I2C_ADDR, PMIC_REG_CFG_START, buf, PMIC_CFG_REG_NUM) != 0) { return 0; } return memcmp(buf, pmic_expect_cfg, PMIC_CFG_REG_NUM) == 0; }这段代码将配置寄存器组读回来和期望值比对。注意,PCA9422 的多个寄存器地址不一定连续,实际产品中需要把非连续的地址单独读取、一起比对。我就是因为想省事用一个连续读,结果发现中间夹杂了几个不用关注的寄存器,校验结果一直错误,后来改成逐组读取才解决。
4. 电源时序编排:从复位到稳定运行的软件状态机
电源时序是这套方案里最容易被低估的部分,也是最值得写代码去精心设计的地方。PCA9422 虽然有多个输出通道,但每个通道的使能排序可以由 MCU 的 GPIO 控制,加上 I2C 配置寄存器,就构成了一个完整的软件电源时序状态机。
4.1 分域供电的设计思路
一个典型的嵌入式系统可以拆成几个电源域:核心域(MCU、DDR)、IO 域(电平转换、LED)、外设域(传感器、LCD、无线模组)。不同的域需要不同的上电顺序,原因主要有两个:一是防止信号脚在浮空状态下被外部电源反灌;二是保证某些需要稳定参考电压的器件在上电时能正确完成复位。
我设计的软件状态机有四个阶段:
| 阶段 | 动作 | 说明 |
|---|---|---|
| PWR_STATE_INIT | 配置 PCA9422 寄存器,所有输出 disable | 初始化 I2C 和 GPIO,等待系统时钟稳定 |
| PWR_STATE_CORE | 使能核心电压域 | MCU 自身供电稳定,启动内部外设 |
| PWR_STATE_IO | 使能 IO 电压域 | 电平转换、判断等电路开始工作 |
| PWR_STATE_PERIPH | 逐个使能外设电源 | 传感器、LCD、无线模组依次上电 |
每个阶段之间加入固定延时,延时大小根据负载的软启动时间确定。比如无线模组的数据手册通常会给“上电到模块就绪”的时间,这个时间就是 PWR_STATE_PERIPH 阶段里给该模组单独留的等待时间。不要图快把这个时间压缩,模组上电不完整,后面连接网络的失败率会显著上升。
4.2 通过 GPIO 控制外部负载电源的开关
除了 PCA9422 本身输出的几路电源,系统中还有一些功率比较大的负载不适合直接接在 PMIC 输出上。比如加热器、大功率 LED 阵列、电机驱动模块,这些东西瞬间电流能到 1A 以上,如果也走 PCA9422,其他输出通道的电压会被拉偏。
这时候合理的做法是:用 PCA9422 的某一路输出作为“控制电源”,再通过一个 P 沟道 MOSFET 或者负载开关去控制大功率负载。STM32F302VC 负责控制 MOSFET 的栅极,PCA9422 只承担逻辑级别的控制电源。这样既保留了软件对电源的完全控制力,又避免了 PMIC 过载。
MOSFET 的栅极驱动电压要和 STM32F302VC 的 GPIO 电平匹配。STM32F302VC 的 GPIO 输出高电平接近 3.3V,所以最好选择逻辑电平型 MOSFET,保证 3.3V 驱动下能够完全导通。如果选错了普通电平型管子,栅极电压不够,MOSFET 会工作在线性区,导通内阻增大、发热严重,反而把系统搞坏。
4.3 深度睡眠状态下的电压维持策略
低功耗产品的关键指标之一是休眠电流,通常要求整机电流低于 50uA。在 STM32F302VC 进入停止模式之前,需要先调整 PCA9422 的工作状态,把不必要的输出通道关闭,保留 MCU 所需的待机电源通道。
PCA9422 支持低功耗模式,在这种模式下,某些 DCDC 转换器会切换到脉冲频率调制模式,以降低静态电流。要让 PMIC 进入这种模式,需要先关闭不需要的外设电源通道,然后把保留通道的强制 PWM 模式切换为自动省电模式,最后通过 I2C 写入低功耗配置位。
这里有一个先后顺序的讲究:必须先关外设电源,再让 DCDC 进入低功耗模式。如果反过来,DCDC 在低功耗模式下带载能力减弱,而外设电源通道还没关断,瞬间的大负载变化会导致输出电压跌落,STM32F302VC 检测到欠压后可能被复位。我一开始就是顺序写反了,导致休眠后隔一秒钟就自动复位一次,花了半天时间才排查出来。
5. 充电管理、电量监测与低功耗协同
如果产品使用锂电池,PCA9422 内置的充电管理功能可以让系统只需要一颗芯片就完成充放电控制。但充电管理不仅仅是“插上充电器就能充”,还需要 MCU 根据电池状态调整充电策略,并对电量进行动态估计。
5.1 充电流程和状态获取
PCA9422 的充电模块支持标准的 CC-CV 充电流程:先恒流充电,电压达到目标值后转为恒压充电,电流下降到设定阈值后判断为充电完成。整个过程的状态都可以通过 I2C 寄存器读取,MCU 只需要周期性地轮询状态位即可。
轮询不能太频繁。充电状态变化本身是慢变量,每隔一秒钟读取一次就足够了。过于频繁的 I2C 读取会增加系统的平均电流,反而影响电池续航。我在实现时把状态轮询放在一个 1Hz 的定时器回调里,只在状态发生变化时才通过回调函数通知上层应用刷新 UI 或更新状态标志。
充电完成之后,PCA9422 并不会完全停止管控电池,而是会进入补充充电模式,在电池电压回落一定程度后再次小电流充电。MCU 需要识别这种模式,避免把“已充满”和“补充中”搞混。一般来说,充电状态寄存器中会有不同的状态码表示这两种情况,实际项目中要对着寄存器的位定义仔细区分。
5.2 利用 STM32F302VC 的 ADC 实现电量估计
PCA9422 内部虽然可以上传电池电压信息,但这个数据的精度和采样率都比较有限。我在 STM32 侧用 ADC 独立采样电池电压,用于电量算法。
最基础的电量估计方法是查表法:将电池开路电压与荷电状态(SOC)对应关系做成一张表,通过采样电压查表得到剩余电量百分比。这张表需要根据实际使用的电芯型号标定,不同厂商、不同容量的电芯,开路电压曲线会有差异。用同一个表去套不同电芯,结果可能偏差 15% 以上。
为了得到更稳定的电压采样值,我在软件里做了多次采样取平均,并且去掉了开机和充电瞬间的采样点。充电状态下,电池电压会被充电器拉高,直接查表会得出偏高的电量,所以需要先检测是否处于充电状态,如果是充电状态,就按“充电中”分支处理数据,而不是直接用电压查表。
ADC 采样的具体实现上,STM32F302VC 的 ADC 分辨率是 12 位,在 3.3V 参考下,理论上每个 LSB 对应约 0.8mV,精度足够。关键在于参考电压源要稳定。如果直接用 MCU 的 VDD 作为参考电压,而 VDD 本身又来自电池经过 DCDC 的输出,那么当电池电压波动时,参考电压也会漂移,采样结果就不准了。建议在条件允许时使用 MCU 内部参考电压或者独立的带隙基准,实测下来电量估计误差能减小好几个百分点。
5.3 动态电压调节与续航优化
STM32F302VC 的主频可以动态调整,PCA9422 的输出电压也可以由软件改写,两者结合就实现了动态电压调节。在高负载场景,比如 LCD 刷新加通信过程中,将核心电压提高到较高水平,保证 MCU 在高主频下稳定运行;在待机场景,将电压降低到 MCU 的最低工作电压,配合降低主频和关断内部外设,能显著降低功耗。
需要说明的是,改变输出电压之前,必须先让 MCU 的 Flash 等待周期、内部电源管理模块等做好相应准备。STM32F302VC 在运行状态下改写供电电压,可能出现 Flash 读取错误,表现为程序跑飞、跳转到硬件错误中断。正确的流程是:先降低主频至目标频率以下,再修改 PCA9422 输出电压,等电压稳定后再提升主频到目标值。
实测经验是,把核心电压从 3.3V 降到 2.9V,MCU 在 72MHz 主频下运行会变得不稳定,因为 STM32F302VC 在 72MHz 时需要的工作电压高于 2.9V。所以动态调压不能随意为之,而是要查阅 MCU 数据手册中的电压-频率曲线,确保每一档频率都有对应的最低电压要求,留出安全裕量。
6. 联调中的实际问题与解决方法
讲完设计和实现,第六节重点记录我在实际联调中遇到的几个真实问题。这些问题不一定每个人都会碰到,但碰到之后如果没有排查思路,很容易在实验室里耗上一整天。
6.1 I2C 从设备不响应的排查
我在最初调试时,经常遇到 I2C 通信失败,最典型的症状是主机发送地址后,PCA9422 没有响应 ACK。排查这类问题需要按照信号链逐级检查。
首先用示波器抓取 SCL 和 SDA 波形,确认 SCL 是否有正常的时钟输出。如果 SCL 有波形而 SDA 始终为高,说明 PCA9422 没有拉低 SDA 应答,可能是从机地址不对或总线被锁。第二次检查 I2C 地址引脚电平,如果硬件上地址引脚虚焊或接错,地址就会对不上。第三,检查 PCA9422 的供电是否正常,PMIC 在主电源未建立时 I2C 模块不工作。
如果确认以上都没有问题,就要怀疑是 STM32F302VC 的 I2C 引脚配置错误。我曾经因为引脚复用功能选择错误,导致 I2C 引脚实际上被配置成了普通 GPIO,输出和输入状态混乱,总线一直忙。多加一步 GPIO 初始化检查,往往能少走很多弯路。
6.2 ADC 端电压噪声的处理
电池电压采样值如果出现明显跳动,数据从 3.8V 一下子跳到 3.5V 再跳回来,大多不是电池电压真的变了,而是采样链路引入了噪声。这种噪声的来源可能有两个:一是电池电压采样线走线过长,经过了高噪声区域;二是 STM32F302VC 的采样时间太短,外接电容上的电压还没稳定到实际值就完成了采样。
处理方法也有两面:硬件上,采样电阻后级加大电容,比如在 ADC 引脚并联 0.1uF 陶瓷电容,并且在布局上尽量缩短这个电容到 MCU 引脚的距离;软件上,增加采样保持时间,或者对多次采样结果做排序中值滤波。我在原有 16 次采样平均的基础上,又叠加了一次滑动平均,采集到的电池电压曲线立刻平滑了很多。
最根本的长期方案是校准。每批产品可以在生产测试线上,通过外部高精度电压源给产品一个标准电压,记录 ADC 原始值,生成个体校准参数存进 Flash。这样即使 ADC 存在零偏或增益误差,也能在软件里补偿回来。
6.3 我踩过的寄存器初始化顺序坑
PCA9422 的寄存器不是完全独立的,某些寄存器的生效条件依赖于其他寄存器的配置。比如充电使能位,如果在电池类型选择和充电电压寄存器之前就置位,PMIC 可能按照错误的电压参数开始充电,导致过充或充不满。
这类问题的难点在于,错误不是立即显现的。充电电压设置错误,电池可能在数小时后甚至下一次充电时才表现出异常。所以我在初始化代码中,强制规定了一个只写一次的配置流程:软复位、基础配置、通道配置、使能输出、充电使能。任何一次改动都走完整流程,不在运行中单独修改充电电压等关键参数。
此外,我还遇到了一个关于 I2C 写入时机的问题:在 PCA9422 处于状态转换期间(比如从充电切换到充电完成)写入关键寄存器,会导致写入失败。这个问题的原因可能是 PMIC 内部状态机正在执行硬件逻辑,占用了内部总线资源。解决办法是软件侧增加写入重试机制,同一个寄存器写三次,直到读回验证成功为止。
(正文结束)