备用电源在仓库里放了小半年,再拿出来巡测时发现两个串联电芯的电压差了接近 280 mV。这个情况太典型了,尤其是两串电池的小储能系统,长期浮充或长期静置,电芯之间稍微有点差异就会被放大。再往后发展,容量下降、保护误触发、甚至电芯极化,问题一个接一个。所以我在这个系统里直接把均衡方案做进去,用的是MP2642GR-Z和STM32L031K6的组合:前者负责把电压高的电芯里的电荷“搬”到电压低的电芯,后者负责电压监测、状态判断、使能控制和低功耗调度。这套方案的好处是硬件做减法、控制做加法,你不用堆一堆电阻去烧热量,也不会因为固定阈值把系统做死。
这篇内容从均衡原理、芯片分工、硬件搭建、固件状态机写到实测数据和排坑经验,适合正在做 2S 锂电池储能、备用电源、便携 UPS 的人参考。不管你是刚接触电池管理系统,还是已经在用被动均衡想升级,都应该能从这里拿到可直接落地的思路。
1. 为什么必须给两个串联电芯补均衡
1.1 不均衡的三个来源
先说一个容易被忽略的事实:同样是出厂批次的两节电芯,容量、内阻、自放电率不可能是完全一致的。电芯制造时的电解液浸润程度、隔膜厚度、极片涂布均匀度,都会带来细微差别。这些差别在单节电芯使用时几乎无感,但一旦串联,流过两节电芯的电流完全相同,差异就会被“积分”起来。
第二个来源是自放电差异。锂电芯的自放电率不是固定值,跟存储温度、SOC 位置都有关系。备用电源系统中,电池经常长时间处于高电量或满电状态,两节电芯自放电速度如果差 0.5%/月,半年就能差出百分之三的 SOC,折算到电压上就是几十毫伏到一两百毫伏的偏差。
第三个来源是温度和热场分布。在储能机箱里,两节电芯位置不同,散热条件不同,紧挨着功率器件的那节电芯温度更高,内阻更小,充放电时工作更“积极”。温度每高 10 度,化学反应速率和内部损耗的变化都不一样,长期运行下来,两节电芯的“劳逸不均”就变成了明显的电压差。
1.2 不均衡带来的真实危害
很多人觉得两个电芯电压差个两三百毫伏无关痛痒,反正都能用。但实际上,串联电池组的可用容量是“木桶效应”,最弱的那节电芯决定整组容量。
充电时,电压高的电芯先到达满电上限,BMS 为了保护它必须切断充电,此时电压低的电芯还没充满。放电时,电压低的电芯先到达放电截止,为了保护它必须切断负载,此时高压电芯还有余电。这样反复循环,低压电芯永远充不满、放不光,高压电芯每次都被“保护性截断”,长期处于高 SOC 状态,加速正极材料结构退化和电解液分解。两节电芯的差距会越拉越大,直到系统容量衰减到只能当半个电池用。
更麻烦的是安全性。电芯长期过充,内部的锂枝晶生长风险会增加,这在家用储能和备用电源场景里是不能接受的。即便没有到热失控的程度,过充电芯的内阻变大、发热增加,也足够让保护板提前告警,影响整个系统的可用性。
1.3 被动均衡和主动均衡怎么选
传统的被动均衡方案,原理简单,成本低,就是把高压电芯的能量通过电阻直接耗散掉。但用在储能和备用电源系统里有两个明显问题:一是均衡电流小,几十到一百毫安,压差磨平速度很慢;二是电阻发热,长时间均衡时 PCB 上会出现明显热斑,对紧密布局的备用电源设备很不友好。
我这次选择主动均衡,直接原因就是不想让均衡变成“烤电芯”。MP2642GR-Z 这类主动均衡芯片,内部通过开关电容或电感拓扑把电荷从高压电芯转移到低压电芯,能量没有变成热量,而是回到电池组里继续使用。效率上来了,均衡电流也可以做得更大,系统散热压力却小得多。
从实现复杂度看,主动均衡芯片需要 MCU 配合做状态判断,但这不是负担,反而是优势。你可以灵活设定启动差值、停止差值、均衡窗口、静置时间,行为完全由软件决定,刚好符合备用电源这种“大部分时间静置、偶尔工作”的场景。
2. 系统方案:MP2642GR-Z 与 STM32L031K6 的配合
2.1 芯片分工逻辑
MP2642GR-Z 在这个系统里的角色,我用一句话概括:它是一颗“搬运工”芯片。它负责根据两端电芯的实际电压,把电荷从电压更高的一侧,通过外部泵电容送到电压更低的一侧。它本身自带功率开关和必要的保护逻辑,你不需要搭复杂的桥式电路,不需要额外搞大功率 MOSFET 驱动,只需要在它周围放对电容和电阻,就能完成核心的电荷搬运动作。
STM32L031K6 在这里是“调度者”。它采集两节电芯的电压,判断当前压差是否超过阈值,要不要开启均衡、均衡多久、什么时候停下来让电芯恢复、恢复之后重新采样。它不直接参与电荷转移,但所有关键决策都由它来控制。
为什么把“搬运”和“调度”分开?因为电池均衡这件事,不只是硬件上能搬电荷就完事了。电池电压在搬运过程中会动态变化,VCELL1 和 VCELL2 会互相趋近,如果控制信号一直开着,可能会出现来回倒腾的现象。用 MCU 做闭环控制,设定回差和静置时间,才能让均衡“一次到位而不是反复振荡”。
2.2 系统总体拓扑与工作流
从整体看,系统主要有三块:电池部分、均衡执行部分、控制监测部分。两节电芯串联后的正极接负载和充放电管理,中间节点接 MP2642GR-Z 的辅助绕组或电容网络,STM32L031K6 的 ADC 通过分压采样两个电芯电压,GPIO 输出一个使能信号控制 MP2642GR-Z 工作。
工作流程大致是这样:上电或者定时唤醒后,STM32 先关断均衡,让电芯开路静置一段时间,然后连续采样多轮电压,取平均。如果两个电芯的压差大于设定启动阈值,比如 20 mV,MCU 就把 EN 拉高,让 MP2642GR-Z 开始搬运电荷。运行固定时间后,MCU 拉低 EN,停止均衡,再静置几十毫秒到几百毫秒,等电芯电压回稳,重新采样判断。如此循环,直到压差小于停止阈值,比如 5 mV,算作均衡完成。
这个“跑一会儿、停一会儿、再测量”的模式,是主动均衡项目里最容易踩坑的点。如果不停下来直接测,芯片内部的开关动作和电容充放电会产生测量误差,你看到的电压是叠加了噪声的,会做出错误判断。
2.3 电压采样链路与分压计算
STM32L031K6 的 ADC 是 12 位,参考电压我直接用 3.3 V 系统电源,单个 LSB 大约是 0.8 mV。但电池电压最高能到 4.2 V,超出了 MCU 的采样范围,所以必须用分压电阻把电压按比例缩小。
我选用的分压方案是两只 10 kΩ 电阻串联,从电池正极串联到 GND,中间抽头送到 MCU ADC。那么分压比就是 1/2。电池电压 4.2 V 时,ADC 输入引脚电压是 2.1 V;电池电压 3.0 V 时,ADC 输入是 1.5 V,全程保证在安全范围内。
这里有个细节:为什么用 10kΩ 而不是 100kΩ?从分压原理上讲,两者都能得到同样的分压比,但 100kΩ 等效源阻抗太高,ADC 内部采样电容充电时间变长,采样结果容易偏低。我实测下来,10kΩ 配合 1ms 级采样周期很稳定,而且功耗增加可以忽略不计。如果你的系统对功耗极其敏感,也可以把电阻加大到 47kΩ 或 68kΩ,但采样时间必须相应拉长,并且要做多次平均。
为了减少电源纹波对采样结果的干扰,我在 ADC 输入引脚前加了一级 RC 滤波,电阻 100Ω,电容 100nF,截止频率在十几千赫兹,足够过滤掉 MP2642GR-Z 开关噪声的高频成分。这个细节看着小,但直接决定了均衡阈值的判断准不准。
3. 硬件搭建:从外围电路到实际接线
3.1 MP2642GR-Z 周边电路搭法
MP2642GR-Z 的周边电路并不复杂,核心是外部泵电容和电源去耦。泵电容的位置和容值对均衡效率影响特别大,我实际采用的数据是规格书典型应用中给的值,具体容量以你拿到的芯片手册为准。需要提醒的是,这个电容建议选择陶瓷电容,低 ESR、温度特性稳定的规格要优先,不能随意换钽电容或电解电容,否则高频开关状态下损耗会非常大,芯片表面温度会异常升高。
芯片的使能引脚由 STM32 的 GPIO 控制,高电平有效。我给 EN 引脚加了一个 10kΩ 下拉电阻,防止 MCU 未初始化时引脚悬空导致均衡误开启。这个下拉电阻在调试阶段帮了大忙,因为 MCU 刚上电的几百毫秒内 GPIO 状态是未定义的,如果 EN 被外部噪声拉高,芯片会突然开始搬电荷,容易让整个系统进入意外状态。
再一个细节是 MP2642GR-Z 的供电和地线。它搬运电荷时会有较大的瞬态电流,所以在芯片电源脚旁边要放一个 100nF 加一个 10uF 或更大的陶瓷电容,并且要尽量贴近引脚。地线要单独走进了主滤波电容的地,再经过单点连接到系统总地,避免均衡电流在控制地线上产生压降,干扰 STM32 的 ADC 参考电压。
3.2 STM32L031K6 最小系统与接口映射
STM32L031K6 本身是单核 Cortex-M0+,主频 32 MHz,虽然算力不高,但干均衡控制这种活绰绰有余。它的优势是低功耗模式非常完整,有 Sleep、Stop、Standby 多档可选,刚好匹配备用电源“长期静置、定期唤醒测量”的逻辑。
我使用的接口映射如下:
| 功能 | STM32 引脚 | 说明 |
|---|---|---|
| 电芯1电压采样 | PA0 | 通过分压电阻接电芯1正极 |
| 电芯2电压采样 | PA1 | 通过分压电阻接电芯2正极 |
| 均衡使能输出 | PB1 | 接 MP2642GR-Z EN 引脚 |
| 充电状态输入 | PB0 | 读取充电器状态信号,有充电时禁止均衡 |
| SWD调试 | PA13/PA14 | 烧录与调试 |
| 运行指示灯 | PC6 | 状态显示,可省 |
供电方面,两节电芯串联后电压范围大约在 6.0V 到 8.4V,我给 MCU 单独用了一颗低压差 LDO 降到 3.3V。选择 LDO 而不是 DC-DC 的原因,一是静态功耗小,二是输出电压纹波低,对 ADC 采样更友好。虽然 LDO 把一部分能量转成了热,但这个系统本来就是以小电流监测为主,发热完全可控。
3.3 布线与布局里的关键细节
硬件布局上我踩过几次坑,这里直接分享结论。
第一,采样线必须从电芯正负极直接引出,哪怕路径绕一点,也不要去就近在功率线上搭头。因为功率线流过均衡电流时会有压降,你采到的是“线上的电压”,不是“电芯的电压”,测量误差源头就在这里。
第二,MCU 的采样地和均衡电流的地要分开。我最初做样板时把共地连得很随意,结果使能均衡后,ADC 值周期性跳动,压差判断完全失真。后来改成单点接地,MCU 的数字地和模拟地再在靠近 MCU 处汇合,问题就消失了。
第三,MP2642GR-Z 的泵电容要离芯片越近越好,走线要短且宽,否则等效串联电感太大,高频充放电时会形成振铃,均衡效率直接打折。
第四,分压电阻的精度不要低于 1%。10kΩ 的 5% 误差电阻,两只串联的分压比可能偏出 5%,两个电芯通道的测量结果本身就不一致,你再怎么校准也校准不回来。我最后选了 0.1% 精度的贴片电阻,并且在校准程序里把实际分压比读进去,这样系统误差才能控制在几个毫伏以内。
4. 固件实现:从电压采样到均衡控制状态机
4.1 ADC 采样与软件校准
固件部分的第一个核心是电压采样。我用的是 STM32 的标准库接口,采集 16 次然后取平均,消除随机噪声。代码逻辑大概是这样:
uint16_t adc_read_channel(uint32_t channel) { ADC_ChannelConfTypeDef sConfig = {0}; sConfig.Channel = channel; sConfig.Rank = 1; sConfig.SamplingTime = ADC_SAMPLINGTIME_38CYCLES_5; HAL_ADC_ConfigChannel(&hadc, &sConfig); HAL_ADC_Start(&hadc); if (HAL_ADC_PollForConversion(&hadc, 100) == HAL_OK) { return HAL_ADC_GetValue(&hadc); } return 0; } float read_cell_voltage(uint32_t channel) { uint32_t sum = 0; for (int i = 0; i < 16; i++) { sum += adc_read_channel(channel); } float raw = sum / 16.0f; float v_adc = raw * 3.3f / 4095.0f; return v_adc * 2.0f; }这个代码能跑,但是只看了一半。真正的电压值需要校准,因为 ADC 的参考电压并不是理想 3.3V,分压电阻的实际阻值也有偏差。我在调试阶段用高精度万用表测了两个电芯的静态电压,同时读出 MCU 显示值,计算出一个比例系数,存到一个变量里。具体做法是:万用表电压除以上面read_cell_voltage得到的电压,得到一个cal_factor,然后最终电压等于read_cell_voltage * cal_factor。
校准这一步是主动均衡阈值判断可靠的前提。如果你预期控制到 5 mV 的停止阈值,那么整个采样链路的误差必须小于 5 mV。我实测校准完成后,两个通道的测量误差在 3 mV 左右,可以接受。
4.2 均衡控制状态机写法
均衡控制不能是“压差大就一直开着”的简单逻辑,必须设计成状态机。我定义的状态包括空闲、测量、均衡、静置、故障五个状态,转换条件如下:
typedef enum { STATE_IDLE, STATE_MEASURE, STATE_BALANCE, STATE_REST, STATE_FAULT } balancer_state_t; balancer_state_t state = STATE_IDLE; static uint32_t balancer_tick = 0; static uint32_t rest_tick = 0; void balancer_update(void) { switch (state) { case STATE_IDLE: if (定时器唤醒标志 == 1) { HAL_GPIO_WritePin(BAL_EN_GPIO_Port, BAL_EN_Pin, GPIO_PIN_RESET); state = STATE_MEASURE; } break; case STATE_MEASURE: vcell1 = read_cell_voltage(ADC_CH_0); vcell2 = read_cell_voltage(ADC_CH_1); if (vcell1 > 3.0f && vcell2 > 3.0f && vcell1 < 4.4f && vcell2 < 4.4f) { if (fabsf(vcell1 - vcell2) > 0.020f) { state = STATE_BALANCE; balancer_tick = 0; } else { state = STATE_IDLE; } } else { state = STATE_FAULT; } break; case STATE_BALANCE: HAL_GPIO_WritePin(BAL_EN_GPIO_Port, BAL_EN_Pin, GPIO_PIN_SET); balancer_tick++; if (balancer_tick > BALANCE_RUN_MS) { HAL_GPIO_WritePin(BAL_EN_GPIO_Port, BAL_EN_Pin, GPIO_PIN_RESET); state = STATE_REST; rest_tick = 0; } break; case STATE_REST: rest_tick++; if (rest_tick > BALANCE_REST_MS) { state = STATE_MEASURE; } break; case STATE_FAULT: HAL_GPIO_WritePin(BAL_EN_GPIO_Port, BAL_EN_Pin, GPIO_PIN_RESET); break; } }状态机的关键参数是启动阈值、停止阈值、运行时间和静置时间。我第一版脚本用的启动阈值是 20 mV、停止阈值是 5 mV、单次均衡最长 1 分钟、静置 200 毫秒。你可以根据系统电压和电芯容量调节,但是务必遵守一个原则:停止阈值越低,对采样精度要求越高,判定越困难,别为了表面好看把停止阈值设成 1mV,然后发现系统一直在反复均衡。
4.3 低功耗调度与异常保护
备用电源系统的痛点就是大多数时间不用它,但你不能让它一直费电。STM32L031K6 的低功耗能力很适合这类场景。我在空闲阶段做了两个处理。
第一,MCU 进入 Stop 模式,搭配 RTC 定时唤醒。RTC 每隔 20 秒唤醒一次,唤醒后只做一件事:采样两节电芯电压,判断是否需要均衡,不需要就马上重新进入 Stop。这样整板平均电流可以压到非常低,对电池组自身容量影响可以忽略不计。
第二,充电状态下禁止均衡。如果系统正在充电,充电器的端电压会叠加到电芯电压上,此时采样结果不能真实反映电芯状态,均衡判断没有意义。我用 PB0 引脚接收充电器的工作状态,高电平表示充电中,状态机在充电状态下强制跳到空闲。
异常保护方面,我加了电压上下限判断,超出 3.0V 到 4.4V 范围直接进故障状态并关断均衡。这个范围虽然比实际电芯工作范围宽,但能有效避免采样链路开路或短路时系统做出一连串错误动作。
HAL_PWR_EnterSTOPMode(PWR_LOWPOWERREGULATOR_ON, PWR_STOPENTRY_WFI); SystemClock_Config();代码很简单,但要注意从 Stop 模式唤醒后需要重新配置时钟,否则主频和 ADC 时钟状态不对,程序会在无征兆的地方卡住。我一开始没重新配置时钟,唤醒后读 ADC 死活不对,后来查了 HAL 库才想起还有这一步。
5. 实测数据与故障排查实录
5.1 实测过程与数据
完整装配后,我用一对容量 2000mAh、开路压差 280 mV 的电芯做了长时间均衡测试。电芯初始电压是 3.85V 和 3.57V,系统每 15 分钟记录一次压差,测试结果如下:
| 时间(分钟) | 压差(mV) | 电芯1电压(V) | 电芯2电压(V) |
|---|---|---|---|
| 0 | 280 | 3.85 | 3.57 |
| 15 | 198 | 3.82 | 3.60 |
| 30 | 142 | 3.80 | 3.64 |
| 45 | 95 | 3.78 | 3.67 |
| 60 | 63 | 3.76 | 3.69 |
| 90 | 28 | 3.74 | 3.71 |
| 120 | 9 | 3.73 | 3.72 |
两小时左右压差进入 10 mV 以内,基本达到均衡目标。整个过程中,MP2642GR-Z 外壳手感温度比环境温度高 2 到 3 度,和被动均衡那种烫手的情况完全不在一个量级。
这里要说明一点,均衡速度受到电芯极化效应影响。测试前半程压差下降快,是因为高电芯电压有“虚高”成分,电荷一开始搬运,虚高部分快速回落。后半程压差下降慢,是因为真实 SOC 差异需要更多电荷转移,这是正常现象,不用怀疑硬件有问题。
5.2 常见问题与解决方法
我在调试过程中整理了一张问题排查表,这些坑都很典型,你大概率也会碰到。
| 现象 | 可能原因 | 处理办法 |
|---|---|---|
| ADC 读数整体偏高 | 分压电阻阻值不准 | 用万用表实测分压电阻,修正分压比,写入校准系数 |
| 使能均衡后电压读数跳动 | 采样地与均衡地未分开 | 改为单点接地,采样回路不要经过功率走线 |
| 芯片表面明显发热 | 泵电容类型或容量不合适 | 换成低 ESR 陶瓷电容,核对规格书推荐容值 |
| 均衡停止后压差很快回升 | 系统仍有负载或自放电差异大 | 检查是否有残留负载,提升启动阈值避免频繁动作 |
| MCU 进入低功耗后电流偏大 | GPIO 引脚悬空或未配置模拟模式 | 未用引脚配置为模拟输入,关闭空闲外设时钟 |
| 唤醒后 ADC 读数异常 | Stop 模式唤醒后未重配时钟 | 调用 HAL_RCC_ClockConfig 重新配置系统时钟 |
还有一个特别容易被忽略的问题:如果均衡之后电芯压差长时间不变,别急着怀疑硬件,先看状态机是不是根本没进入均衡状态。我一开始把fabsf用成了abs,浮点比较被截断成整数,压差 280 mV 也触发不了均衡。
5.3 长期运行与后续优化建议
最后聊聊长期使用中的判断标准。主动均衡能够弥合电芯之间因自放电差异造成的压差,但它无法修复电芯自身内阻和容量的差异。如果你发现同一组电芯每次均衡后,压差都很快重新拉大,每次均衡时间越来越长,说明电芯已经明显劣化。这个时候最合理的做法是换掉性能最差的那节电芯,而不是继续靠均衡硬扛。
从后续优化的角度看,这个系统还有两个可以扩展的方向。一个是在采样链路上加 NTC 温度传感器,把温度作为均衡策略的输入变量,温度过高时降低均衡电流强度;另一个是在 MCU 上挂一组参数存储,把每次均衡起止时间、压差变化趋势记录下来,方便后续判断电芯健康度趋势。
我个人在实际运行中的体会是:主动均衡不能替代电芯筛选,也不能替代 BMS 的保护逻辑,它解决的是“电芯差异累积”这个慢变量问题。设计时不要把均衡想得太神圣,把它当成一个日常维护机制就好。阈值设合理、采样做准确、状态机够稳,这套方案能一直安静地运行很久。