1. 从两串电池的电压差说起:为什么被动均衡在储能场景里依然能打
储能和备用电源系统里,两串电池单元串联使用是最常见的架构之一。无论是小型家庭储能柜、通信基站的后备电池组,还是便携式储能电源,只要涉及多节电芯串联,就绕不开一个老生常谈的问题:串联电芯之间的电压不一致。这个问题看起来不起眼,但它是导致整组电池容量衰减加速、循环寿命缩短的头号杀手之一。
我接触过不少做BMS(电池管理系统)的团队,很多人一上来就想上主动均衡,觉得被动均衡"太low"。但实际项目跑下来,尤其是中小功率的储能和备用电源场景,被动均衡配合一颗靠谱的模拟前端芯片,反而是性价比最高、最稳定的方案。这次要聊的,就是围绕MP2642GR-Z这颗电池管理AFE芯片,搭配STM32F415ZG主控,对两个串联电池单元做均衡的完整思路和实操细节。
MP2642GR-Z是一颗面向多串电池组的模拟前端,内部集成了电压采集、均衡驱动和保护逻辑,通过I2C或SPI与主控通信。STM32F415ZG则是ST的Cortex-M4高性能MCU,带FPU和丰富的外设资源,跑BMS的采集、均衡策略和通信协议绰绰有余。两者搭配,构成一个典型的"AFE采集+MCU决策"的两串电池均衡系统。
这篇文章适合谁看?如果你正在做储能BMS、备用电源管理板,或者单纯想搞清楚"两颗串联电池怎么自动均衡"这件事的底层逻辑和落地细节,那接下来的内容应该能帮你少走不少弯路。我会从均衡的必要性讲起,拆到MP2642GR-Z的寄存器配置、STM32F415ZG的均衡策略实现,再到实测中那些文档里不会写的坑。
2. 两颗串联电芯的不一致性到底从哪来,被动均衡又凭什么够用
2.1 电芯不一致的物理根源
先把这个问题的本质说清楚。两颗标称容量相同、出厂电压相同的电芯,串联之后用着用着电压就分家了,原因主要有三个层面。
第一是制造离散性。即便是同一批次、同一产线的电芯,实际容量、内阻、自放电率都会有微小差异。容量差个1%~2%在工业级电芯里很正常,但串联使用时,充电时容量小的那颗先到截止电压,放电时它又先到欠压保护点。日积月累,两颗电芯的SOC(荷电状态)差距会越来越大。
第二是温度梯度。电池组里两颗电芯的散热条件很难做到完全一致,靠近功率器件的那颗温度偏高,温度高的电芯自放电更快、老化也更快,内阻上升,充放电时电压偏移更明显。
第三是自放电差异。这个在备用电源场景里尤其致命。备用电源长期处于浮充或静置状态,自放电率高的那颗电芯电压会慢慢往下掉,几个月下来两颗电芯的电压差可能就到几十甚至上百毫伏。
注意:电压差本身不是目的,电压差背后反映的是SOC差异。均衡的目标是让两颗电芯的SOC趋于一致,而不是单纯把电压拉到相等。在磷酸铁锂这类平台电压很平的化学体系里,光看电压判断SOC会非常不准,这点后面会展开说。
2.2 被动均衡的工作逻辑与适用边界
被动均衡的原理朴素到不能再朴素:给电压高的那颗电芯并联一个电阻,让它通过电阻放电,把多出来的能量以热量的形式消耗掉,等它降到和另一颗电芯差不多的时候停止放电。
很多人嫌弃它"浪费能量",这话没错,但要看场景。对于两串电池的小系统,均衡电流通常只有几十毫安,均衡时间也就几十分钟到几小时,浪费的能量微乎其微。而主动均衡需要变压器、电感、开关矩阵,成本和PCB面积翻好几倍,对于两串这种低串数场景,投入产出比并不划算。
被动均衡真正需要关注的边界是热。均衡电阻消耗的功率全部变成热量,如果均衡电流设得太大、PCB散热没做好,电阻和周边器件会过热。MP2642GR-Z内部集成了均衡开关,外部只需要接均衡电阻,但电阻的选型和布局有讲究,后面细说。
2.3 为什么选MP2642GR-Z而不是分立方案
用分立MOS加电阻也能做均衡,但那样主控要自己处理电压采集、比较、开关时序,还要做保护。MP2642GR-Z把这些都集成进去了:它负责采集每颗电芯的电压,内部有均衡驱动电路,主控只需要通过通信接口读电压、下均衡命令。这样STM32F415ZG的负担就轻很多,可以把精力放在均衡策略、SOC估算和系统通信上。
选STM32F415ZG也是同样的逻辑。它主频高、带硬件浮点,跑一些简单的卡尔曼滤波或者查表法SOC估算毫无压力,而且它的多路ADC、CAN、UART资源丰富,方便和上位机或其他模块通信。对于两串电池这种规模,这颗MCU其实是"性能过剩"的,但过剩意味着余量,后续要加功能不用换主控。
3. MP2642GR-Z的均衡通道配置:寄存器、电阻与热设计
3.1 均衡通道的硬件连接方式
MP2642GR-Z对两串电池的典型连接是这样的:电池组正极接芯片的BAT+,两颗电芯之间的中点接CELL1,电池组负极接BAT-(也就是GND参考)。每颗电芯对应一个均衡输出引脚,外部接一颗均衡电阻到对应的电芯正极或通过内部开关到地,具体接法要看芯片数据手册的推荐电路。
这里有个容易搞错的地方:均衡电阻是接在电芯两端,通过芯片内部的开关控制通断。当某颗电芯需要均衡时,芯片闭合对应的内部开关,电芯通过外部电阻放电。所以外部电阻的阻值直接决定了均衡电流大小。
均衡电流的计算很直接:
I_balance = V_cell / R_balance假设电芯电压3.7V,想要100mA的均衡电流,那么R_balance = 3.7V / 0.1A = 37Ω。实际选型时取标准值39Ω,对应均衡电流约95mA。电阻的功率也要算:
P = V_cell² / R_balance = 3.7² / 39 ≈ 0.35W所以至少要选0.5W的电阻,留足余量。我一般会选1W的封装,温升更可控。
3.2 寄存器层面的均衡使能配置
MP2642GR-Z通过I2C和主控通信,均衡控制通常落在几个关键寄存器上。虽然具体寄存器地址要查对应版本的数据手册,但逻辑是通用的:有一个均衡配置寄存器,每一位对应一颗电芯的均衡开关;还有一个均衡状态寄存器,用来回读当前哪些通道正在均衡。
配置流程大致是:
- 初始化I2C外设,确认能读到芯片的设备ID,验证通信正常。
- 配置采集相关寄存器,设置电压采集的过采样率和转换周期。
- 写均衡配置寄存器,把需要均衡的电芯对应位置1,其余置0。
- 定期回读均衡状态寄存器和电压寄存器,确认均衡按预期进行。
// 伪代码示意,实际寄存器地址以数据手册为准 #define MP2642_ADDR 0x0B #define REG_BALANCE_CFG 0x12 #define REG_CELL1_VOLT 0x14 #define REG_CELL2_VOLT 0x16 void balance_enable(uint8_t cell_mask) { uint8_t cfg = cell_mask & 0x03; // 只关心低两位,对应两颗电芯 i2c_write_reg(MP2642_ADDR, REG_BALANCE_CFG, cfg); } uint16_t read_cell_voltage(uint8_t cell) { uint8_t reg = (cell == 1) ? REG_CELL1_VOLT : REG_CELL2_VOLT; uint8_t buf[2]; i2c_read_reg(MP2642_ADDR, reg, buf, 2); return (buf[0] << 8) | buf[1]; }提示:写均衡配置之前,务必确认芯片处于正常工作模式,有些AFE在上电默认是休眠或低功耗模式,需要先发唤醒或模式切换命令,否则寄存器写了不生效,这个坑我踩过。
3.3 均衡电阻的布局与散热经验
均衡电阻虽然简单,但布局不好会带来两个问题:一是电阻发热影响旁边电芯的电压采集精度,二是热量堆积导致电阻寿命下降。
我的做法是:均衡电阻尽量远离AFE芯片和电压采集走线,放在板边或者有铜皮散热的位置。电阻下方铺铜,通过过孔把热量导到背面。如果空间允许,两颗电芯的均衡电阻分开布置,避免热量互相叠加。另外,均衡电阻的走线要够宽,100mA电流虽然不大,但走线太细会有压降,影响实际均衡电流。
实测下来,39Ω/1W的电阻在95mA均衡电流下,表面温度大概比环境高20~30℃,手摸上去是温热但不烫手。如果发现电阻烫得厉害,要么是阻值选小了,要么是散热没做好,需要重新核算。
4. STM32F415ZG上的均衡策略:从阈值判断到滞回控制
4.1 最朴素的阈值均衡及其问题
最容易想到的策略是:读两颗电芯电压,如果差值超过某个阈值(比如30mV),就对电压高的那颗开启均衡,直到差值降到阈值以下。
#define BALANCE_THRESHOLD_MV 30 void balance_task(void) { uint16_t v1 = read_cell_voltage(1); uint16_t v2 = read_cell_voltage(2); int16_t diff = (int16_t)v1 - (int16_t)v2; if (diff > BALANCE_THRESHOLD_MV) { balance_enable(0x01); // 均衡电芯1 } else if (diff < -BALANCE_THRESHOLD_MV) { balance_enable(0x02); // 均衡电芯2 } else { balance_enable(0x00); // 都关掉 } }这个逻辑能跑,但有个明显问题:在阈值附近会频繁开关均衡。电压差刚好在30mV上下波动时,均衡一会儿开一会儿关,不仅效率低,还会让电阻反复热胀冷缩,长期可靠性受影响。
4.2 加入滞回窗口让均衡更稳定
解决办法是引入滞回。开启均衡的阈值设高一点,比如30mV;关闭均衡的阈值设低一点,比如10mV。这样一旦开始均衡,就要一直均衡到差值降到10mV以下才停,中间不会因为小幅波动反复切换。
#define BALANCE_ON_MV 30 #define BALANCE_OFF_MV 10 static uint8_t balancing = 0; void balance_task_hysteresis(void) { uint16_t v1 = read_cell_voltage(1); uint16_t v2 = read_cell_voltage(2); int16_t diff = (int16_t)v1 - (int16_t)v2; int16_t abs_diff = (diff > 0) ? diff : -diff; if (!balancing) { if (abs_diff > BALANCE_ON_MV) { balancing = 1; balance_enable(diff > 0 ? 0x01 : 0x02); } } else { if (abs_diff < BALANCE_OFF_MV) { balancing = 0; balance_enable(0x00); } } }滞回窗口的宽度要根据实际电芯特性和系统噪声来定。如果电压采集噪声大,窗口要适当放宽;如果电芯一致性很好,窗口可以窄一点,均衡更精细。
4.3 什么时候不该均衡:充电中、放电中还是静置时
这是很多人忽略的一点:均衡不是任何时候都能做的。
充电过程中,两颗电芯电压都在上升,此时电压差可能被充电电流放大,直接均衡容易误判。放电过程中同理,大电流放电时内阻差异导致的电压差并不代表真实的SOC差异。最理想的均衡时机是静置状态,此时电芯电压接近开路电压,最能反映SOC。
但备用电源系统长期浮充,静置机会少。折中方案是:在充电末期(电流已经很小、接近充满)做均衡,此时电压相对稳定,均衡效果最好。或者在系统待机、负载很轻的时候做均衡。
我在实际项目里的策略是分状态处理:
| 系统状态 | 是否均衡 | 说明 |
|---|---|---|
| 大电流充电 | 否 | 电压差受内阻影响大,误判风险高 |
| 充电末期(小电流) | 是 | 电压稳定,均衡效果好 |
| 大电流放电 | 否 | 同上,且放电时均衡意义不大 |
| 静置/待机 | 是 | 最理想的开路电压状态 |
| 保护触发时 | 否 | 优先处理保护,均衡暂停 |
4.4 均衡与电压采集的时序配合
还有一个细节:均衡开启时,被均衡电芯的电压采集会受影响。因为均衡电流流过电芯内阻和线路阻抗,会在采集点产生额外的压降,导致读到的电压偏低。如果均衡和采集同时进行,可能读到错误的电压,进而做出错误决策。
稳妥的做法是:采集电压时先关闭均衡,等电压稳定后再读,读完再恢复均衡。或者利用AFE的采集和均衡分时机制,在均衡开关的间隙做采集。MP2642GR-Z这类芯片通常支持这种分时操作,具体要看数据手册的时序要求。
uint16_t read_voltage_with_balance_off(uint8_t cell) { uint8_t saved = current_balance_mask; balance_enable(0x00); // 先关均衡 delay_ms(2); // 等待电压稳定 uint16_t v = read_cell_voltage(cell); balance_enable(saved); // 恢复均衡 return v; }这个2ms的等待时间不是随便定的,要根据电芯和线路的RC时间常数来。线路阻抗和电芯内阻越小,稳定越快。实测中2~5ms足够,如果发现读数还是不稳,可以适当加长。
5. 实测中暴露的问题:从电压跳变到均衡失效的排查链路
5.1 电压读数偶尔跳变几十毫伏
项目初期遇到一个现象:两颗电芯的电压读数偶尔会突然跳变几十毫伏,导致均衡逻辑误动作。排查过程分几步走。
先怀疑是I2C通信受干扰。用示波器抓I2C的SCL和SDA波形,发现波形干净,没有明显毛刺,通信错误计数也很低,基本排除通信问题。
再怀疑是采集时序问题。检查代码发现,采集电压和均衡控制是并发进行的,均衡开启时采集确实会偏低。改成前面说的"关均衡-等待-采集-恢复"时序后,跳变明显减少,但偶尔还有。
最后定位到电源纹波。AFE的供电如果纹波大,内部基准会受影响,采集精度下降。在AFE供电脚加了一颗10μF和一颗0.1μF的电容,纹波从几十毫伏降到几毫伏,电压读数就稳了。
经验:AFE的供电去耦电容千万别省,尤其是靠近芯片的0.1μF,它对高频噪声的抑制效果非常明显。很多采集精度问题,根源都在电源上。
5.2 均衡开启后电压不降反升
另一个诡异现象:对电芯1开启均衡后,电芯1的电压不但没降,反而略微上升。这个一开始让人摸不着头脑。
后来想明白了:均衡电流流过电芯内阻,方向是从电芯正极流出,在电芯内阻上产生的压降方向是抬高采集点电压还是降低,取决于采集点和均衡电流路径的相对关系。如果采集点和均衡电流共用一段走线,且走线有阻抗,均衡电流就会在采集点叠加一个电压。
解决办法是采集走线和均衡走线分开,采用开尔文连接(Kelvin connection)的思路,采集点直接从电芯极耳引出,不和均衡电流路径共用。改板之后,均衡时电压读数正常下降,问题解决。
这个坑很典型,本质是电流路径和电压采集路径的隔离问题。凡是涉及电流和电压同时测量的场景,都要注意这一点。
5.3 均衡电阻发热导致邻近采集漂移
前面提到均衡电阻要远离采集走线,这个不是理论,是实测教训。早期布局时均衡电阻离AFE比较近,均衡一段时间后,AFE附近的温度升高,电压采集出现缓慢漂移,两颗电芯读数都偏高。
用热成像仪一看,均衡电阻表面温度到了80℃以上,热量通过铜皮传到了AFE。把电阻移到板边、加大散热铜皮、并在电阻和AFE之间开槽隔断热传导路径后,漂移消失。
| 问题现象 | 根本原因 | 解决措施 |
|---|---|---|
| 电压读数跳变 | 电源纹波大 | 增加去耦电容 |
| 均衡时电压不降反升 | 采集与均衡走线共用 | 开尔文连接,路径分离 |
| 采集缓慢漂移 | 均衡电阻热传导 | 电阻远离AFE,开槽隔断 |
| 均衡频繁开关 | 无滞回 | 加入滞回窗口 |
6. 均衡策略的进阶优化:SOC估算与温度补偿的引入
6.1 光看电压不够:平台电压体系的困境
前面反复强调,均衡的目标是SOC一致,不是电压一致。在磷酸铁锂体系里,电压平台非常平,SOC从30%到70%电压可能只变化几十毫伏,这时候用电压差判断均衡需求会非常不准。
对于两串系统,如果电芯是磷酸铁锂,我的建议是结合安时积分做SOC估算。STM32F415ZG有足够的算力跑简单的安时积分:记录充放电电流对时间的积分,得到每颗电芯的SOC变化。均衡时优先参考SOC差,电压差作为辅助判断。
安时积分的实现要点:
float soc[2] = {50.0f, 50.0f}; // 初始SOC假设50% float capacity_ah = 5.0f; // 电芯容量5Ah void soc_update(float current_a, float dt_s) { // current_a > 0 表示充电,< 0 表示放电 float delta_soc = (current_a * dt_s / 3600.0f) / capacity_ah * 100.0f; soc[0] += delta_soc; soc[1] += delta_soc; // 限幅 for (int i = 0; i < 2; i++) { if (soc[i] > 100.0f) soc[i] = 100.0f; if (soc[i] < 0.0f) soc[i] = 0.0f; } }安时积分的问题是累积误差,需要定期用电压点校准。比如充电到截止电压时,把SOC强制校准到100%;放电到欠压点时校准到0%。这样误差不会无限累积。
6.2 温度对均衡决策的影响
温度会影响电芯内阻和开路电压,进而影响均衡判断。低温时内阻大,同样的电流产生的电压偏移更大;高温时自放电快,SOC下降更快。
我的做法是在均衡策略里引入温度权重:温度低时放宽电压差阈值(因为内阻导致的电压差本来就大),温度高时收紧阈值(自放电差异更值得关注)。同时,如果某颗电芯温度明显偏高,优先对它做均衡或者降低整体均衡强度,避免热失控风险。
温度采集用NTC热敏电阻贴在电芯表面,STM32F415ZG的ADC读取分压后的电压,查表换算成温度。NTC的选型要注意B值和工作温区,储能场景一般选-40~125℃范围的。
6.3 均衡电流的自适应调整
固定均衡电流在某些场景下不够灵活。比如电芯电压差很大时,希望均衡快一点;电压差很小时,希望均衡慢一点、精细一点。可以通过PWM控制均衡开关的占空比来调节平均均衡电流。
MP2642GR-Z的均衡开关如果支持PWM控制,就可以实现这个自适应。占空比大,平均电流大,均衡快;占空比小,平均电流小,均衡精细。STM32F415ZG的定时器资源丰富,输出PWM控制均衡使能引脚很方便。
不过要注意,PWM频率不能太高,否则开关损耗和EMI会增加;也不能太低,否则电流纹波大。一般选1~10kHz比较合适,具体看芯片的开关特性和电阻的热时间常数。
7. 系统集成与通信:让均衡状态可观测、可配置
7.1 均衡状态的本地指示与上报
一个成熟的BMS不能只是闷头均衡,还要让用户或上位机知道当前在干什么。我在板上加了两颗LED,分别指示两颗电芯的均衡状态,调试时一眼就能看出均衡有没有在跑。
同时,通过UART或CAN把均衡状态、电芯电压、SOC、温度上报给上位机。STM32F415ZG的CAN外设很适合储能系统的通信,抗干扰能力强,适合长距离传输。上报的数据格式要设计得紧凑一点,比如用固定的帧结构,方便上位机解析。
typedef struct { uint16_t cell1_mv; uint16_t cell2_mv; int16_t current_ma; int8_t temp_c; uint8_t balance_state; // bit0: cell1, bit1: cell2 uint8_t soc1; uint8_t soc2; } bms_report_t;7.2 参数的可配置化设计
均衡阈值、滞回窗口、均衡电流这些参数,最好不要写死在代码里。不同项目、不同电芯,最优参数不一样。我的做法是把这些参数放在Flash的一个配置区,通过上位机命令可以修改,掉电不丢失。
STM32F415ZG的Flash操作要注意擦写寿命和对齐要求。参数区单独划一个扇区,修改时先擦除再写入,写入前做好校验。如果参数不经常改,这个方案完全够用;如果改得频繁,可以外挂一颗小容量EEPROM。
7.3 故障保护与均衡的互锁
均衡不能凌驾于保护之上。如果出现过压、欠压、过温、过流等故障,均衡必须立即停止。这个互锁逻辑要在软件里做死:保护状态优先级最高,一旦触发,先关均衡,再处理保护。
void safety_check(void) { if (overvoltage || undervoltage || overtemp || overcurrent) { balance_enable(0x00); // 立即停止均衡 enter_protection_mode(); } }这个检查要放在主循环的高优先级位置,或者用中断触发,确保响应及时。均衡电阻虽然功率不大,但在故障状态下继续均衡可能加剧问题,不能掉以轻心。
8. 一些文档里不会写的实操心得
做这个项目前后折腾了小半年,有些经验是数据手册和参考设计里找不到的,分享几条。
关于MP2642GR-Z的初始化。这颗芯片上电后不会自动开始采集,需要主控发命令唤醒并配置。我见过有人调试时读不到电压,查了半天以为是I2C地址错了,其实是芯片还在休眠。初始化流程一定要按数据手册的时序来,该等的延时不能省。
关于STM32F415ZG的ADC和AFE的关系。既然用了AFE采集电压,STM32自己的ADC就别去重复采集电芯电压了,留给温度、电流这些AFE不管的信号。分工明确,代码也清晰。
关于均衡电阻的选型。不要只看阻值和功率,还要看温度系数。有些廉价电阻温度系数大,发热后阻值变化明显,均衡电流就不准了。选温度系数小的金属膜电阻,贵一点但省心。
关于测试。均衡功能一定要用真实电芯测,不要只用电源模拟。电源模拟不出电芯内阻和自放电特性,很多问题只有在真实电芯上才会暴露。我建议至少用两颗容量有差异的电芯做对比测试,人为制造不一致,验证均衡效果。
关于长期可靠性。均衡电阻反复热胀冷缩,焊点容易疲劳。如果产品要过振动或温度循环测试,均衡电阻的焊盘要加大,或者用贴片电阻加底部填充胶。这个在实验室里短期测不出来,但量产后的失效案例不少。
最后说一句,两串电池的均衡看起来简单,但要做好、做稳,细节非常多。MP2642GR-Z加STM32F415ZG这个组合,硬件上够用,软件上有发挥空间,适合作为储能和备用电源BMS的入门到进阶方案。把均衡策略、采集时序、热设计这几块吃透,整个系统的可靠性会上一个台阶。