1. 从两个串联电芯说起:为什么被动均衡在储能BMS里依然是主流选择
储能和备用电源系统里,电池组从来不是单节电芯打天下。常见的48V备用电源机架,内部往往是十几节磷酸铁锂或三元锂电芯串联堆出来的;即便是小型的12V/24V储能模组,也至少是3到7串的结构。串联带来的一个天然问题就是:同一批次、同一产线上出来的电芯,容量、内阻、自放电率依然存在离散性,用久了这种离散性会被放大,最终表现为"木桶效应"——整组可用容量由最弱的那节电芯决定。
均衡电路要解决的就是这个离散性问题。行业里把均衡分成两大类:被动均衡和主动均衡。主动均衡用变压器、电感或电容做能量搬运,效率高、发热小,但电路复杂、成本高、EMC难搞;被动均衡则是给电压偏高的电芯并联一个耗散电阻,把多余的能量以热的形式放掉。听起来很"笨",但在储能和备用电源这类对成本敏感、对均衡电流要求不高的场景里,被动均衡依然是绝对主力。
我这次要聊的方案,是用MP2642GR-Z这颗均衡前端配合STM32F439ZG主控,对两个串联电池单元做均衡。两颗电芯的串联结构在备用电源里非常典型——比如两节磷酸铁锂串成6.4V标称,或者两节三元串成7.4V标称,用于小型UPS、应急照明、便携储能箱这类产品。别看只有两串,均衡做不好,整组寿命照样腰斩。
MP2642GR-Z 是一颗专门为多串电池均衡设计的芯片,内部集成了均衡MOSFET和采样通路,支持通过外部电阻设定均衡电流,并且可以通过串行接口读取每节电芯电压、控制每路均衡开关。STM32F439ZG 则是ST的Cortex-M4高性能MCU,180MHz主频、2MB Flash、256KB SRAM,带FPU和DSP指令,用来跑BMS的采样、均衡策略、保护逻辑、通信协议绰绰有余。两者搭配,构成一个"采样+均衡+主控"的完整小系统。
这篇文章我会把整个方案从硬件到软件拆开讲:MP2642GR-Z 的均衡原理和外围怎么配、STM32F439ZG 怎么和它通信、均衡策略怎么写、实测中会遇到哪些坑。适合正在做储能BMS、备用电源管理、或者单纯想搞懂被动均衡怎么落地的人参考。不管你是刚接触BMS的新手,还是想换一颗均衡芯片的老手,应该都能从里面找到能直接抄的东西。
2. MP2642GR-Z 的均衡机制与外围电路设计要点
2.1 被动均衡的物理本质:把多余的能量变成热
先把这个"为什么"讲透。两节串联电芯,假设一节是3.65V满电,另一节只有3.45V,充电时电压高的那节会先到截止电压,充电器必须停,否则高压那节过充。结果就是低压那节永远充不满,整组容量被高压那节"卡"住。放电时反过来,低压那节先到截止电压,整组停放电,高压那节还剩一堆能量放不出来。一充一放,可用容量就损失了。
被动均衡的思路很直接:在充电过程中,检测到哪节电压偏高,就在那节电芯两端并一个电阻,让一部分充电电流从电阻上走掉,不流进电芯。这样高压电芯的充电速度被拖慢,低压电芯继续正常充,最终两节电压趋于一致。均衡电流越大,拉平速度越快,但发热也越猛。
MP2642GR-Z 内部集成了均衡开关管,外部只需要在每节电芯的正负极之间接一个均衡电阻,芯片控制开关管的通断,就能实现"哪节高就放哪节"。均衡电流由外部电阻决定,公式很简单:
I_balance ≈ V_cell / R_balance
比如电芯3.6V,想做到100mA均衡电流,R_balance 取36Ω左右。但实际选型不能只看这个公式,还要考虑电阻的功率、芯片内部开关管的导通电阻、PCB走线的压降。
2.2 均衡电阻的选型:功率和温升是绕不开的坎
很多人第一次做被动均衡,均衡电阻的功率算错,板子跑一会儿就冒烟。这里必须把账算清楚。
假设均衡电流100mA,电芯电压3.6V,电阻上的功耗是:
P = I² × R = 0.1² × 36 = 0.36W
看起来不大,但这是单节的功耗。两节同时均衡就是0.72W,而且这些热量全部集中在电阻和芯片附近。如果选0603封装(额定0.1W),直接烧给你看。至少要选1206(0.25W)甚至2512(1W)封装,并且留足降额余量。
我一般按实际功耗的2倍以上选额定功率。0.36W的功耗,选1W的2512电阻,表面温度能控制在可接受范围。如果空间紧张只能用1206,那就把均衡电流降到50mA以下,功耗降到0.09W,1206勉强能扛。
还有一个容易被忽略的点:均衡电阻不是一直工作的。充电后期电压接近截止时,均衡才频繁动作;放电和静置时基本不均衡。所以平均功耗远低于峰值功耗,但热设计必须按峰值来,因为均衡可能持续几十分钟。
2.3 采样通路的精度:均衡决策全靠它
MP2642GR-Z 的采样精度直接决定均衡策略靠不靠谱。如果采样误差有±20mV,而两节电芯实际只差15mV,芯片可能误判成"已经均衡好了"或者"一直在均衡",策略就乱了。
这颗芯片的电压采样是通过内部多路复用器逐节切换,送到内部ADC。外围要注意两点:一是每节电芯到芯片采样引脚的走线要等长、远离功率回路,二是采样引脚前的RC滤波要配好。我一般用100Ω串联电阻加0.1μF电容,截止频率约16kHz,既能滤掉开关噪声,又不会拖慢采样响应。
采样时序上,芯片内部切换通道后需要一段建立时间,STM32F439ZG 在读取时要注意等转换完成标志,不能连续快速读,否则读到的是上一通道的残留值。这个坑我在第一次调试时踩过,读出来的电压跳来跳去,后来加了10μs的通道切换延时才稳定。
2.4 两串系统的特殊之处:共地问题
两节串联,第一节的负极是系统地,第二节的负极是第一节的正极。MP2642GR-Z 要同时采两节的电压,内部的高压侧电路必须能承受第二节的正极电压(约7.2V)。这颗芯片的耐压设计覆盖了这个范围,但外围的电阻分压网络要算准。
如果芯片的采样输入范围是0到5V,而第二节电芯的正极对地是7.2V,就需要分压。分压电阻的比值要精确,且温漂要匹配,否则两节采样的比例不一致,均衡判断就会偏。我一般用0.1%精度的金属膜电阻做分压,温度系数选25ppm/℃以内。
另外,均衡电阻的接法也要注意:每节的均衡电阻必须接在该节电芯的正负极之间,不能跨节接,否则会形成错误的放电回路。PCB布局时,均衡电阻的地回路要单独走,不要和采样地混在一起,避免均衡电流在采样地上产生压降,干扰采样。
3. STM32F439ZG 与 MP2642GR-Z 的通信接口与驱动实现
3.1 接口选型:SPI还是I2C,取决于你的采样频率需求
MP2642GR-Z 支持串行通信接口,具体是SPI还是I2C要看具体型号配置。假设我们用SPI,因为STM32F439ZG 的SPI外设资源丰富,速率可以拉到十几MHz,适合多通道快速轮询。
SPI接线四根:SCLK、MOSI、MISO、CS。CS用GPIO控制,每个芯片一根。两串系统只有一颗MP2642GR-Z,所以CS就一根。SCLK空闲电平、采样边沿要和芯片手册对齐,我一般用Mode 0(CPOL=0,CPHA=0),即空闲低电平、上升沿采样。
STM32F439ZG 的SPI配置在CubeMX里点几下就行,但有几个参数要注意:波特率预分频不要设太小,先降到1MHz左右调通,再往上提;数据宽度设8位;MSB先行。调通后再根据芯片手册的最大速率往上加。
3.2 寄存器读写时序:别在CS上偷懒
MP2642GR-Z 的寄存器读写有固定时序:CS拉低,发送命令字节(含读写位和地址),然后读或写数据,最后CS拉高。很多人图省事,CS一直拉低,靠命令字节区分操作,结果读出来的数据错位。
正确做法是每次操作都完整地拉低-传输-拉高CS。两次操作之间至少留1μs的CS高电平时间,让芯片内部状态机复位。这个时间在手册里叫CS high time,不同芯片要求不同,MP2642GR-Z 一般要求几百纳秒到1μs,保险起见给2μs。
写寄存器的代码大概长这样:
void MP2642_WriteReg(uint8_t addr, uint8_t data) { HAL_GPIO_WritePin(CS_GPIO_Port, CS_Pin, GPIO_PIN_RESET); uint8_t cmd = 0x80 | (addr & 0x7F); // 写标志位+地址 HAL_SPI_Transmit(&hspi1, &cmd, 1, 100); HAL_SPI_Transmit(&hspi1, &data, 1, 100); HAL_GPIO_WritePin(CS_GPIO_Port, CS_Pin, GPIO_PIN_SET); delay_us(2); }读寄存器类似,只是命令字节的写标志位清零,然后发一个dummy字节读回数据。
3.3 电压采样数据的解析:LSB和量程要搞清楚
MP2642GR-Z 的电压寄存器返回的是ADC码值,需要转换成实际电压。转换公式取决于芯片的内部增益和参考电压。假设参考电压2.5V,12位ADC,那么LSB = 2.5/4096 ≈ 0.61mV。如果电芯电压经过1/2分压,实际电压 = 码值 × 0.61mV × 2。
这里有个坑:分压电阻的比值不是精确的1/2,因为电阻有公差。我一般会在产线校准环节,用标准电压源给每节电芯加已知电压,反算出实际的分压系数,存到STM32的Flash里。这样每块板子的采样精度都能拉到±5mV以内。
STM32F439ZG 的Flash够大,存几个校准系数毫无压力。校准流程可以做成上位机指令触发,也可以在生产工装里自动跑。
3.4 均衡开关的控制:写寄存器和实际通断之间有时间差
控制均衡开关就是往配置寄存器写对应的位。但要注意,写完寄存器到MOSFET实际导通之间有延迟,芯片内部有驱动电路,延迟可能在几十微秒。如果均衡策略是"检测到压差就立刻开均衡",然后马上读电压验证,可能读到的是还没均衡的电压,误以为没生效。
我的做法是:开均衡后至少等100ms再读电压判断效果。这个时间足够驱动电路稳定,也足够均衡电流在电芯上产生可测量的压降变化。
另外,均衡开关不能频繁开关。每次开关都有功耗和EMC噪声,而且频繁开关会让电芯电压波动,干扰采样。我一般设一个滞回窗口:压差大于30mV才开均衡,压差小于10mV才关均衡。中间这段不动作,避免抖动。
4. 两串均衡策略的软件实现:从阈值判断到状态机
4.1 均衡触发条件:不只看压差,还要看充电状态
最简单的均衡策略是"压差大于阈值就均衡",但实际用起来问题很多。比如放电过程中压差大,这时候均衡没意义,因为均衡是耗散能量,放电时耗散只会让整组更快没电。静置时压差大,均衡倒是可以做,但静置时电芯电压本来就在回弹,测不准。
我的策略是分状态:
- 充电中:压差大于30mV,开高压节的均衡。这是最主要的均衡时机,因为充电时高压节本来就会先到截止,均衡能把它拖住。
- 静置中:压差大于50mV,开均衡,但限制单次均衡时间不超过30分钟,避免过度耗散。
- 放电中:不均衡。放电时均衡只会加速整组放电,得不偿失。
这个策略在STM32F439ZG 里用一个状态机实现,状态包括IDLE、CHARGING、DISCHARGING、FLOATING。状态切换靠充电电流检测和电压变化率判断。
4.2 状态机设计:用定时器驱动,别用delay
STM32F439ZG 有丰富的定时器资源,我用TIM6做1ms系统滴答,均衡状态机每100ms跑一次。为什么不用HAL_Delay?因为delay是阻塞的,会卡住其他任务。BMS系统里同时要跑采样、通信、保护,任何一个阻塞都会拖累整体响应。
状态机的核心逻辑:
typedef enum { BAL_IDLE, BAL_CHARGING, BAL_FLOATING, BAL_DISCHARGING } BalState_t; void Bal_StateMachine(void) { static uint32_t last_tick = 0; if (HAL_GetTick() - last_tick < 100) return; last_tick = HAL_GetTick(); float v1 = GetCellVoltage(0); float v2 = GetCellVoltage(1); float diff = v1 - v2; float abs_diff = fabsf(diff); switch (bal_state) { case BAL_CHARGING: if (abs_diff > 0.030f) { if (diff > 0) SetBalance(0, 1), SetBalance(1, 0); else SetBalance(0, 0), SetBalance(1, 1); } else if (abs_diff < 0.010f) { SetBalance(0, 0); SetBalance(1, 0); } break; case BAL_FLOATING: if (abs_diff > 0.050f) { // 同样逻辑,但加时间限制 } break; default: SetBalance(0, 0); SetBalance(1, 0); break; } }这段代码里,滞回窗口用30mV开、10mV关,避免在阈值附近抖动。SetBalance函数封装了SPI写寄存器的操作。
4.3 均衡电流的自适应:根据温度降额
均衡电阻发热,如果环境温度本来就高,再满电流均衡,电阻和芯片都可能过热。MP2642GR-Z 内部有温度检测,STM32F439ZG 可以读出来。我设了三档:
| 芯片温度 | 均衡电流 | 说明 |
|---|---|---|
| < 60℃ | 100% | 正常 |
| 60-80℃ | 50% | 降额 |
| > 80℃ | 0% | 停止均衡 |
降额怎么实现?如果芯片支持PWM调均衡电流,就改占空比;如果不支持,就只能靠间歇工作——开10秒关10秒,等效电流减半。MP2642GR-Z 的均衡开关是数字控制,所以用间歇工作法。
这个逻辑放在状态机里,每次跑的时候检查温度,动态调整均衡的占空比。
4.4 保护逻辑的优先级:均衡必须给保护让路
BMS里保护永远是第一优先级。如果检测到过压、过流、过温,均衡必须立刻停。STM32F439ZG 的中断系统可以做到这一点:保护事件触发中断,中断里直接写寄存器关掉所有均衡开关,不等状态机下一轮。
我一般把过压保护阈值设在电芯规格的上限减50mV,比如磷酸铁锂上限3.65V,保护点设3.60V。一旦某节到3.60V,立刻停均衡并报警。为什么保护点要比均衡阈值高?因为均衡本身会让电压波动,如果保护点设得太接近均衡阈值,均衡一开就可能触发保护,系统反复启停。
5. 实测中暴露的问题与排查过程
5.1 采样值跳变:从电源噪声查到地线布局
板子第一次上电,读出来的两节电压一直在跳,跳动幅度有±30mV。这个幅度已经超过均衡阈值了,状态机疯狂切换,均衡开关噼里啪啦响。
排查过程是这样的:先怀疑SPI通信误码,用逻辑分析仪抓SPI波形,数据本身是稳的,说明通信没问题。然后怀疑芯片采样本身噪声大,把均衡全关掉,只读电压,还是跳。接着用示波器看采样引脚的波形,发现上面叠加了约20mVpp的高频噪声,频率和STM32的SPI时钟一致。
问题定位到SPI走线和采样走线太近,SPI时钟串扰到了采样线上。改板时把采样线远离SPI,并且在采样引脚前加了RC滤波(100Ω+0.1μF),噪声降到5mVpp以内,电压稳定了。
这个坑的教训是:BMS板子上,采样线是敏感信号,必须和数字信号隔离。如果空间允许,采样线走内层,两边包地;如果只能走表层,至少和SPI、PWM这些线保持3倍线宽以上的距离。
5.2 均衡开启后电压反而升高:共地回路的压降在作怪
第二个坑更隐蔽。开第一节均衡后,读第一节电压,发现不降反升,升了约15mV。按理说均衡是放电,电压应该降。
用万用表直接量电芯两端,电压确实在降,但芯片读到的值在升。说明芯片读到的电压和电芯实际电压之间有偏差,而且这个偏差随均衡电流变化。
查PCB发现,第一节电芯的负极采样点和均衡电阻的地回路共用了一段走线。均衡电流100mA流过这段走线,走线电阻假设0.1Ω,产生10mV压降。芯片的采样参考地接在这段走线的芯片侧,而电芯负极在走线的另一侧,所以采样值 = 电芯电压 + 走线压降,看起来就升高了。
解决办法是开尔文连接:采样地从电芯负极单独引一根线到芯片,不和均衡电流回路共用。改板后这个问题消失。
这个坑在单节系统里不明显,因为单节没有共地问题。两串系统里,第一节的采样地就是系统地,很容易和均衡回路混在一起。做两串以上的BMS,开尔文连接是必须的。
5.3 均衡电阻烫手:功率降额没做够
第三块板子,均衡电阻选了1206封装,0.25W额定。实测均衡电流80mA,电芯3.6V,功耗0.23W,接近额定值。跑起来电阻表面温度到了110℃,手都不敢碰。虽然没烧,但长期可靠性堪忧。
后来换成2512封装,1W额定,同样电流下表面温度降到60℃左右。成本增加不多,但可靠性提升明显。如果实在只能用1206,就把均衡电流降到40mA,功耗0.058W,温度能控制在70℃以内。
这里有个经验公式:电阻表面温升 ≈ 功耗 × 热阻。1206的热阻大约200℃/W,0.23W就是46℃温升,加上环境40℃,就是86℃。2512热阻约100℃/W,0.23W只有23℃温升,环境40℃也就63℃。选封装的时候按这个估一下,心里有数。
5.4 状态机死锁:一个没初始化的变量引发的血案
软件跑了一段时间后,均衡突然不工作了,重启又正常。查了半天,发现状态机的状态变量在某个分支里没赋值,导致状态卡在一个未定义值,switch匹配不到任何case,直接掉到default,均衡全关。
这种问题用调试器单步很难复现,因为要跑很久才触发。后来加了状态变量的合法性检查,每次进状态机先判断状态值是否在枚举范围内,不在就强制回IDLE。另外把状态变量初始化写在main开头,确保上电就是确定值。
STM32F439ZG 的SRAM有ECC,未初始化的变量读出来可能是随机值,但不会触发硬件异常。软件上必须自己保证初始化完整。我现在的习惯是:所有全局变量在定义时就给初值,不依赖启动代码的零初始化。
6. 从两串到多串:这套方案的扩展思路与边界
6.1 多颗MP2642GR-Z级联:SPI片选要规划好
两串用一颗芯片,如果要做4串、8串,就需要多颗MP2642GR-Z级联。每颗芯片负责一段电芯,SPI总线共用,片选独立。STM32F439ZG 的GPIO够多,片选不是问题,但要注意SPI总线的负载。每增加一颗芯片,SCLK和MOSI上的容性负载就增加,高速下波形会变差。
我的做法是:SPI速率超过5MHz时,每颗芯片的SCLK走线单独加22Ω串联电阻,做阻抗匹配。片选线也要加,防止反射。如果串数很多(比如16串以上),考虑用SPI缓冲器或者降低速率。
级联时还有一个同步问题:多颗芯片的采样不是同时的,如果均衡策略需要比较不同芯片管辖的电芯电压,要先把所有芯片的采样值读回来,再统一判断。不能读一颗判断一颗,否则判断依据的时间不一致。
6.2 均衡策略的升级:从两两比较到全局最优
两串系统里,均衡就是两节比。多串系统里,均衡策略要复杂得多。最简单的做法是找最高和最低,只均衡最高的那节。但这样效率低,如果有多节偏高,应该同时均衡。
更聪明的做法是算平均值,所有高于平均值+阈值的电芯都开均衡。这样能最快把整组拉平。但要注意,同时均衡多节,总均衡电流大,发热集中,电源和PCB都要扛得住。
STM32F439ZG 的算力足够跑这些策略,180MHz的M4做几十次浮点比较和排序毫无压力。我一般用冒泡排序找最高最低,电芯数不多时够用;如果串数超过20,用快速排序或者直接维护一个最大最小堆。
6.3 这套方案的适用边界:什么时候该换主动均衡
被动均衡的硬伤是效率低、发热大、均衡电流小。两串系统里,100mA均衡电流够用,因为电芯少、容量小。但如果电芯容量到了50Ah以上,100mA均衡电流要几天才能拉平,这时候被动均衡就不合适了。
判断标准很简单:均衡电流应该至少是电芯自放电电流的10倍以上,最好能达到充电电流的1%到5%。如果电芯容量50Ah,充电电流10A,1%就是100mA,刚好卡在被动均衡的上限。再大就该考虑主动均衡了。
另外,如果系统对发热敏感(比如密封机箱、高温环境),被动均衡的耗散热量可能成为热设计的瓶颈,这时候主动均衡的优势就体现出来了。主动均衡把能量从高压节搬到低压节,总发热量小得多。
6.4 实测数据:两串磷酸铁锂的均衡效果
最后分享一组实测数据。两节磷酸铁锂,标称3.2V/5Ah,初始压差80mV(一节3.45V,一节3.37V)。用这套方案,均衡电流100mA,充电中均衡。
| 时间 | 第一节电压 | 第二节电压 | 压差 |
|---|---|---|---|
| 0min | 3.450V | 3.370V | 80mV |
| 10min | 3.445V | 3.385V | 60mV |
| 30min | 3.440V | 3.410V | 30mV |
| 60min | 3.435V | 3.425V | 10mV |
| 90min | 3.430V | 3.428V | 2mV |
90分钟把80mV压差拉到2mV,对于5Ah电芯来说,这个速度可以接受。均衡过程中电阻表面温度最高58℃,芯片温度52℃,都在安全范围。
如果压差更大,比如150mV,时间会拉长到3小时以上。所以被动均衡适合初始一致性较好的电芯,如果电芯出厂离散就很大,被动均衡救不回来,得从电芯分选环节解决。
这套MP2642GR-Z加STM32F439ZG的两串均衡方案,我从打样到量产跑了小半年,硬件改了三版,软件迭代了十几版。最大的体会是:被动均衡电路简单,但细节一点都不少。采样精度、地线布局、电阻功率、状态机健壮性,任何一个环节偷懒,都会在实测中加倍还回来。把这几块做扎实,两串系统的均衡效果完全可以做到让人放心。