基于MP2642GR-Z与STM32F410RB的两串锂电池被动均衡方案详解 1. 从两个串联电芯说起为什么被动均衡在储能BMS里依然是主力方案储能和备用电源系统里两串电芯的架构非常常见——12V/24V的备用电源、小型储能包、通信基站的后备电池组很多都是两串锂电芯串联而成。串联之后最头疼的问题就是两颗电芯出厂容量、内阻、自放电率不可能完全一致充放电循环几十次之后电压差就会慢慢拉开。电压高的那颗先到截止电压整个电池组就得停止充电另一颗还没充满放电时电压低的那颗先到欠压保护点整组又得停。结果就是整组可用容量被最弱的那颗电芯拖死循环寿命也跟着缩短。解决这个问题的核心手段就是均衡。均衡分两大类被动均衡和主动均衡。主动均衡用变压器、电感或者电容把电量从高电压电芯搬到低电压电芯效率高、发热小但电路复杂、成本高、EMC难搞。被动均衡则是给电压高的电芯并联一个电阻把多余的电量以热量形式消耗掉电路简单、成本低、可靠性高。在储能和备用电源这类对成本敏感、均衡电流要求不大的场景里被动均衡依然是绝对主力。这次要聊的方案就是用MP2642GR-Z这颗均衡控制芯片配合STM32F410RB主控对两个串联电池单元做被动均衡。MP2642GR-Z 是一颗专门为多串电池均衡设计的芯片内部集成了均衡MOSFET和驱动逻辑外部只需要很少的元件就能搭出均衡电路。STM32F410RB 则是ST的Cortex-M4系列MCU带浮点单元、12位ADC、多路定时器用来做电压采样、均衡策略控制和状态上报非常合适。整套方案的目标很明确用最低的BOM成本和最简的软件逻辑把两颗串联电芯的电压差控制在合理范围内延长电池组寿命。这篇文章会从硬件设计、采样精度、均衡策略、软件实现、实测踩坑几个维度把这套方案完整拆开讲。不管你是刚接触BMS的新手还是做过几版均衡板的老手应该都能从里面找到可以直接抄作业的东西。2. MP2642GR-Z 的引脚逻辑与外围电路设计细节2.1 这颗芯片到底解决了什么问题MP2642GR-Z 的核心价值在于把均衡所需的功率MOSFET、栅极驱动、过流保护、状态指示全部集成到一颗芯片里。传统分立方案要用两颗MOSFET加两颗电阻加驱动电路占板面积大而且MOSFET的栅极驱动电压需要仔细设计否则容易工作在放大区发热。MP2642GR-Z 内部集成了低导通电阻的MOSFET外部只需要接均衡电阻和少量滤波电容芯片自己处理栅极驱动和过流保护。它的工作逻辑是这样的芯片检测到某颗电芯电压超过设定阈值并且与另一颗电芯的压差超过均衡开启门限就打开对应通道的MOSFET让电芯通过外部均衡电阻放电。放电电流由外部电阻决定芯片内部有电流检测和限流保护防止电阻短路或者MOSFET过流烧毁。芯片还提供状态输出引脚可以把正在均衡的状态告诉MCU方便软件做状态机管理。对于两串电池来说MP2642GR-Z 的典型接法是芯片的BAT1、BAT2引脚分别接两颗电芯的正极GND接电池组负极两个均衡输出引脚分别通过均衡电阻接到对应电芯的正极。芯片内部会自动比较两颗电芯的电压决定打开哪个通道。这种芯片自主均衡MCU监控的架构可以大大减轻MCU的软件负担MCU只需要定期读取电压和状态做异常保护和策略调整。2.2 外围元件选型均衡电阻不是随便选的均衡电阻的选型直接决定了均衡电流和发热量。假设单颗电芯容量是2000mAh我们希望均衡电流在50mA到100mA之间那么均衡电阻怎么算均衡电流 I Vcell / R_balance。电芯电压按3.7V典型值算如果R_balance取47Ω电流就是3.7/47≈78.7mA。这个电流下电阻上的功耗 P I²R 0.0787² × 47 ≈ 0.29W。所以电阻必须选0805以上封装最好用1206或者2512功率裕量至少留2倍也就是选0.5W以上的电阻。如果选0603封装0.1W的额定功率根本扛不住电阻会烧糊。实际选型时还要考虑均衡电阻的温漂。普通厚膜电阻温漂100ppm/°C温度升高后阻值变化均衡电流也会漂。如果对均衡精度要求高可以选合金电阻或者低温漂的薄膜电阻但成本会上去。对于储能和备用电源这种场景普通厚膜电阻完全够用没必要过度设计。另外均衡电阻的布局很关键。电阻要远离温度敏感元件比如NTC热敏电阻和MCU的晶振。均衡时电阻表面温度可能到60-80°C如果紧挨着NTC温度采样会严重失真导致MCU误判电池温度过高而停止充电。我一般会把均衡电阻放在板子边缘下面铺铜散热NTC放在电池连接器附近两者至少隔开10mm以上。2.3 滤波与保护那些容易忽略的小元件MP2642GR-Z 的电压检测引脚前面必须加RC滤波。电池组到芯片的走线会引入噪声尤其是储能系统里有大功率逆变器或者DC-DC在开关噪声耦合到电压检测线上会导致芯片误判电芯电压频繁开启关闭均衡。RC滤波的典型值是1kΩ电阻加0.1μF电容截止频率约1.6kHz既能滤掉高频开关噪声又不会影响电压检测的响应速度。还有一个容易踩的坑芯片的电源引脚需要加去耦电容而且必须是低ESR的陶瓷电容容值0.1μF到1μF。如果只用大容量电解电容高频噪声滤不掉芯片内部逻辑可能误动作。我习惯在每颗电芯的检测引脚旁边放一个0.1μF的0402电容紧贴引脚效果比放在远处好很多。保护方面均衡电阻两端最好并联一个TVS或者稳压二极管防止电池组反接或者浪涌电压击穿芯片。虽然MP2642GR-Z 内部有一定的耐压能力但储能现场环境恶劣多一层保护多一份安心。TVS选击穿电压略高于电芯最高电压的型号比如5V到6V的TVS响应时间要快结电容要小。3. STM32F410RB 的采样通道配置与电压换算3.1 为什么选这颗MCUSTM32F410RB 在STM32家族里属于中低端型号Cortex-M4内核主频100MHz带浮点运算单元Flash 128KBRAM 32KB。对于两串电池均衡这种应用它的资源绰绰有余。选它的理由有几个第一它有多路12位ADC采样速率快可以轮流采集两颗电芯的电压和温度第二它带硬件浮点做电压换算和滤波算法时不用软件模拟浮点代码效率高第三它的定时器资源丰富可以用一个定时器做均衡周期控制另一个做看门狗或者通信超时第四价格便宜供货稳定适合量产。相比用8位单片机做均衡控制STM32F410RB 的优势在于ADC精度和运算能力。8位机的ADC通常只有10位而且参考电压不稳定采样误差可能到几十毫伏。对于电芯电压差只有几十毫伏的均衡场景这个误差太大了。STM32F410RB 的12位ADC配合外部基准电压采样误差可以控制在几毫伏以内完全满足均衡判断的需求。3.2 电压采样电路分压电阻怎么算两颗串联电芯的电压范围是6V到8.4V按三元锂算而STM32F410RB 的ADC参考电压是3.3V所以必须用分压电路把电压降到3.3V以内。分压电阻的选择要兼顾功耗和抗噪。假设分压电阻上端R1100kΩ下端R247kΩ分压比是47/(10047)0.3197。电芯最高8.4V时ADC输入电压是8.4×0.3197≈2.69V低于3.3V有足够的裕量。分压电阻上的电流是8.4/(147k)≈57μA功耗很小不会明显影响电池自放电。但是分压电阻不能选太大否则ADC采样时输入阻抗不够低采样保持电容充电不足会导致采样值偏低。STM32F410RB 的ADC输入阻抗要求是小于50kΩ所以分压电阻的并联值要小于这个数。R1和R2并联是100k//47k≈31.9kΩ满足要求。如果选1MΩ和470kΩ并联值约320kΩ采样就会出问题读数会明显偏低。分压电阻的精度直接影响电压采样精度。普通1%精度的电阻分压比误差约1.4%8.4V时误差约118mV这个误差太大了。均衡判断的压差门限通常只有30-50mV采样误差比门限还大根本没法用。所以分压电阻必须选0.1%精度的或者用软件校准。我一般选0.1%精度的薄膜电阻成本比普通电阻贵几毛钱但省去了校准的麻烦。3.3 ADC采样时序与滤波处理STM32F410RB 的ADC采样时间需要根据输入阻抗来配置。分压电阻并联值约32kΩADC采样保持电容约8pF充电时间常数约256ns。为了充分充电采样时间至少要设置到1.5μs以上。STM32F410RB 的ADC时钟是APB2分频假设ADC时钟30MHz采样周期数选84个采样时间就是84/30M2.8μs足够。采样时序上我习惯用定时器触发ADC每100ms采集一轮两颗电芯的电压和温度。采集时用DMA搬运数据不占用CPU。每轮采集做16次过采样然后取平均值这样可以把随机噪声降低4倍。16次过采样后有效分辨率可以到13位左右电压分辨率约0.4mV完全够用。软件滤波方面除了过采样还会加一个滑动平均滤波。每颗电芯保存最近8次的采样值去掉最大最小值后取平均。这样可以滤掉突发的脉冲干扰比如逆变器开关瞬间耦合进来的噪声。滑动窗口的长度要权衡窗口太长响应慢均衡状态切换不及时窗口太短滤波效果差。8次窗口在100ms采样周期下响应时间约800ms对于均衡这种慢过程完全够用。4. 均衡策略设计什么时候开、什么时候关、开多久4.1 均衡开启与关闭的门限设定均衡策略的核心是三个参数均衡开启压差、均衡关闭压差、均衡最小电芯电压。这三个参数设不好要么均衡频繁启停要么该均衡的时候不均衡。均衡开启压差是指两颗电芯电压差超过多少时开始均衡。设太小比如10mV采样误差都可能超过这个值会导致误均衡设太大比如100mV电芯已经明显不均衡了才动作效果差。根据经验开启压差设在30-50mV比较合适。我一般设40mV配合0.1%精度的采样电路误判概率很低。均衡关闭压差是指压差降到多少时停止均衡。如果关闭压差和开启压差一样会出现均衡-停止-压差又变大-再均衡的振荡。所以关闭压差要比开启压差小形成迟滞。比如开启40mV关闭20mV这样压差降到20mV以下才停不会频繁启停。均衡最小电芯电压是指电芯电压低于多少时禁止均衡。均衡是放电过程如果电芯已经快没电了还均衡会加速过放。一般设3.0V到3.2V低于这个值就停止均衡保护电芯。4.2 均衡周期与占空比控制被动均衡会产生热量如果一直开着均衡电阻和芯片温度会持续升高。所以均衡不能连续开要用周期性的方式比如开2秒关1秒或者开1秒关1秒。占空比控制在50%到70%之间既能保证均衡速度又能控制温升。均衡周期的设定要考虑热时间常数。均衡电阻的热时间常数通常几秒到几十秒周期太短温度还没升上去就关了温升不明显周期太长比如开10秒关10秒电阻温度会升得很高。我一般用开2秒关1秒的周期实测电阻表面温度稳定在60°C左右芯片温度在50°C左右都在安全范围内。占空比还可以根据温度动态调整。如果NTC检测到电池温度超过45°C就把占空比降到30%减少发热如果温度低于10°C均衡效率会下降可以把占空比提到80%加快均衡。这种动态调整策略在储能系统里很实用因为储能电池的温度变化范围很大。4.3 状态机设计与异常处理均衡控制用状态机实现最清晰。状态机有四个状态IDLE空闲、BALANCING均衡中、COOLDOWN冷却、FAULT故障。IDLE状态下定期检查压差和电压满足条件就进入BALANCINGBALANCING状态下控制均衡周期同时监控压差和温度压差降到关闭门限或者温度过高就进入COOLDOWNCOOLDOWN状态下等待一段时间让电阻冷却然后回到IDLEFAULT状态下关闭所有均衡等待故障清除。异常处理要覆盖几种情况电芯电压采样异常比如读数突然跳到0或者满量程可能是采样电路故障要立即停止均衡并报错温度传感器异常读数超出合理范围也要停止均衡均衡超时比如连续均衡超过1小时压差还没降下来可能是电芯老化严重或者均衡电路故障要停止并报错。状态机的每个状态切换都要有日志记录方便后期分析。STM32F410RB 的Flash可以划出一块区域做日志存储记录每次均衡的开始时间、结束时间、初始压差、结束压差、最高温度。这些数据对于评估电池健康状态和优化均衡策略非常有价值。5. 软件实现从ADC读取到均衡控制的完整代码逻辑5.1 ADC初始化与DMA配置STM32F410RB 的ADC配置用HAL库比较方便但HAL库的ADC初始化代码比较冗长我习惯用LL库或者直接寄存器操作代码更精简。下面是ADC初始化的关键步骤// 使能ADC时钟和GPIO时钟 RCC-APB2ENR | RCC_APB2ENR_ADC1EN; RCC-AHB1ENR | RCC_AHB1ENR_GPIOAEN; // 配置PA0和PA1为模拟输入 GPIOA-MODER | GPIO_MODER_MODER0 | GPIO_MODER_MODER1; // ADC校准 ADC1-CR2 | ADC_CR2_ADON; for(volatile int i0; i1000; i); ADC1-CR2 | ADC_CR2_CAL; while(ADC1-CR2 ADC_CR2_CAL); // 配置ADC为独立模式扫描模式连续转换 ADC1-CR1 ADC_CR1_SCAN; ADC1-CR2 ADC_CR2_CONT | ADC_CR2_DMA | ADC_CR2_DDS; // 配置采样时间通道0和1 ADC1-SMPR2 ADC_SMPR2_SMP0_2 | ADC_SMPR2_SMP1_2; // 84周期 // 配置规则序列 ADC1-SQR1 0; // 2个转换 ADC1-SQR3 (0 0) | (1 5); // 通道0和通道1 // 使能DMA ADC1-CR2 | ADC_CR2_DMA;这段代码的关键点是采样时间设置。SMP0_2和SMP1_2对应84个采样周期在30MHz ADC时钟下约2.8μs满足分压电阻的充电要求。DMA配置成循环模式ADC连续转换DMA自动搬运数据到缓冲区CPU完全不参与。5.2 电压换算与校准ADC读数是12位无符号整数0到4095对应0到3.3V。电压换算公式是#define VREF 3.3f #define DIVIDER_RATIO 0.3197f float adc_to_voltage(uint16_t adc_value) { float adc_voltage (float)adc_value * VREF / 4095.0f; return adc_voltage / DIVIDER_RATIO; }但是实际分压比和参考电压都有误差直接算出来的电压可能偏几十毫伏。所以要做两点校准在已知电压下读ADC值算出实际的VREF和分压比。校准方法是用高精度电源给一个已知电压比如4.000V读ADC值然后反推系数。// 校准系数 float vref_cal 3.3f; float ratio_cal 0.3197f; // 校准过程输入4.000V读ADC值adc_cal // 实际分压比 (adc_cal * vref_cal / 4095) / 4.000 // 然后更新ratio_cal校准系数存在Flash里每次上电读取。如果不想做产线校准也可以用0.1%精度的分压电阻和外部基准电压芯片把误差控制在可接受范围内。外部基准可以用TL431或者REF3033精度比MCU内部参考高很多。5.3 均衡控制主循环主循环用定时器中断触发每100ms执行一次。流程是读取ADC缓冲区的最新数据做滑动平均滤波换算成电压然后跑状态机。void balance_task(void) { // 读取ADC值 uint16_t adc_cell1 adc_buffer[0]; uint16_t adc_cell2 adc_buffer[1]; // 换算电压 float v_cell1 adc_to_voltage(adc_cell1); float v_cell2 adc_to_voltage(adc_cell2); // 滑动平均滤波 float v1_filtered moving_average(filter1, v_cell1); float v2_filtered moving_average(filter2, v_cell2); // 计算压差 float v_diff fabsf(v1_filtered - v2_filtered); // 状态机 switch(balance_state) { case IDLE: if(v_diff BALANCE_START_THRESHOLD v1_filtered MIN_CELL_VOLTAGE v2_filtered MIN_CELL_VOLTAGE) { balance_state BALANCING; balance_timer 0; } break; case BALANCING: // 控制均衡周期 if(balance_timer BALANCE_ON_TIME) { enable_balance(v1_filtered v2_filtered ? 1 : 2); } else if(balance_timer BALANCE_ON_TIME BALANCE_OFF_TIME) { disable_balance(); } else { balance_timer 0; } balance_timer; // 检查退出条件 if(v_diff BALANCE_STOP_THRESHOLD || v1_filtered MIN_CELL_VOLTAGE || v2_filtered MIN_CELL_VOLTAGE || temperature MAX_TEMP) { disable_balance(); balance_state COOLDOWN; cooldown_timer 0; } break; case COOLDOWN: cooldown_timer; if(cooldown_timer COOLDOWN_TIME) { balance_state IDLE; } break; case FAULT: disable_balance(); // 等待故障清除 break; } }这段代码的核心是状态机的三个退出条件压差降到关闭门限、电芯电压低于最小电压、温度过高。三个条件任意一个满足就退出均衡进入冷却状态。冷却时间一般设30秒到60秒让均衡电阻充分散热。5.4 状态上报与通信接口STM32F410RB 可以通过UART或者CAN把均衡状态上报给上位机。UART最简单用printf重定向就行。上报的数据包括两颗电芯电压、压差、均衡状态、均衡累计时间、最高温度、故障标志。void report_status(void) { printf(V1%.3f,V2%.3f,Diff%.3f,State%d,BalTime%lu,Temp%.1f,Fault%d\n, v1_filtered, v2_filtered, v_diff, balance_state, total_balance_time, temperature, fault_flag); }上报周期可以设1秒一次数据量不大UART波特率115200完全够。如果储能系统有多组电池可以用CAN总线组网每个电池组一个节点上位机轮询查询。CAN的抗干扰能力比UART强适合工业现场。6. 实测踩坑记录那些文档里不会写的经验6.1 均衡电阻发热导致温度采样失真第一版板子调试时发现一个怪现象均衡开启后NTC温度读数会上升5-10°C但电池表面温度用红外测温枪测并没有明显变化。排查后发现是均衡电阻离NTC太近电阻的热量通过PCB铜箔传到NTC焊盘导致NTC测到的是电阻温度而不是电池温度。解决办法有两个一是拉开均衡电阻和NTC的物理距离至少10mm以上中间用开槽或者挖空隔开二是NTC的走线用细线减少铜箔传热。我最后把均衡电阻移到板子另一侧NTC放在电池连接器旁边问题解决。这个坑很隐蔽因为均衡电阻的发热是渐进的温度读数缓慢上升很容易误判为电池真的在发热。6.2 ADC采样受逆变器噪声干扰储能系统里逆变器开关频率通常在20kHz到100kHz开关瞬间的dv/dt会耦合到电压采样线上。第一版板子没有加RC滤波ADC读数在逆变器工作时会跳变几十毫伏导致均衡误判。加了1kΩ0.1μF的RC滤波后跳变降到几毫伏以内。但RC滤波的电阻不能太大否则ADC采样保持电容充电不足读数会偏低。1kΩ是折中值再大就要增加采样时间。另外采样线要走差分或者用地线包围减少环路面积降低噪声耦合。6.3 均衡MOSFET体二极管导致的漏电MP2642GR-Z 内部集成的MOSFET在关闭时体二极管仍然存在。如果两颗电芯压差较大高电压电芯的电流会通过体二极管漏到低电压电芯形成微弱的漏电。这个漏电电流很小通常几十微安对于大容量电池影响不大但对于小容量电池比如500mAh以下长期漏电会导致电芯缓慢不均衡。解决办法是在均衡电阻上串联一个二极管阻止反向漏电。二极管选低压降的肖特基比如BAT54压降0.3V左右对均衡电流影响很小。这个改动会增加一点成本但对于小容量电池组是必要的。6.4 状态机振荡问题早期版本的均衡策略没有迟滞开启和关闭门限都是40mV。结果压差在40mV附近时均衡频繁启停继电器或者MOSFET开关次数暴增寿命受影响。后来加了20mV的迟滞开启40mV关闭20mV振荡消失。迟滞的大小要权衡迟滞太小振荡依然存在迟滞太大压差降到关闭门限以下才停均衡效果变差。20mV的迟滞配合40mV的开启门限实测效果最好。如果采样噪声比较大迟滞可以适当加大到30mV。6.5 Flash日志写入寿命问题STM32F410RB 的Flash擦写寿命约1万次如果每次均衡都写日志每天均衡10次一年就是3650次不到3年Flash就写坏了。解决办法是用EEPROM或者FRAM做日志存储或者用Flash的磨损均衡算法把日志分散到不同扇区。我最后用了一片I2C接口的FRAM容量4KB擦写寿命100亿次随便写。FRAM的价格比EEPROM贵一点但省心。如果成本敏感也可以用铁电存储器或者带电池的SRAM但要注意电池寿命。7. 方案扩展从两串到多串的演进思路两串均衡是最基础的场景实际储能系统里经常有4串、8串甚至16串。MP2642GR-Z 本身支持多片级联每片负责两串通过菊花链或者隔离通信把状态汇总到主MCU。STM32F410RB 的UART或者SPI可以接多片MP2642GR-Z轮询读取每片的电压和状态。多串场景下均衡策略要更复杂一些。两串时只需要比较两颗电芯多串时要找出最高电压和最低电压的电芯对最高电压的电芯做均衡。均衡顺序可以按压差从大到小排序优先均衡压差最大的。均衡时间也要动态分配压差大的多均衡一会儿压差小的少均衡一会儿。另外多串电池组的采样线更多噪声耦合路径也更复杂。每根采样线都要加RC滤波而且采样线的布局要避免平行走线减少串扰。多串电池组的均衡电阻更多发热量更大PCB的散热设计要更仔细必要时加散热片或者风扇。从两串方案过渡到多串硬件上主要是增加MP2642GR-Z的数量和采样通道软件上主要是把状态机从两两比较改成全局排序。核心的均衡逻辑和异常处理思路是一样的掌握了这个方案扩展到多串并不难。