☰
2S电池组均衡实战:基于STM32与MP2642的低成本被动均衡方案
2026/10/15 1:37:30 网站建设 项目流程

做储能设备和家用备用电源的人,迟早会碰上两个串联电池单元的均衡问题:单芯不够用,4串又浪费,2S是最常见的妥协,但也是最容易翻车的组合。我最近用 MP2642GR-Z 搭配 STM32F031C6 做了一套2S均衡控制,放在一套备用电源系统里跑,电芯一致性问题算是治住了。方案没有用专门的BMS前端芯片,而是用稳压DCDC加一颗Cortex-M0 MCU自己搭信号链路,好处是灵活、成本低,坏处是采样和时序都得自己兜。这篇就当是给准备设计类似板卡的朋友一份实操笔记,硬件、软件、调试坑一次说全。

1. 项目定位与整体思路

1.1 两个串联电芯为什么“天生就会失衡”

两个电芯串联,不管充电还是放电,流过它们的电流在任意时刻都完全一样。很多人觉得“既然电流一样,容量消耗也应该一样吧”,实际并不是这样。电芯出厂时即使同批次,内阻、容量、自放电率也会有细微差异;进入系统后,散热条件不同、离发热元件远近不同、老化速度不同,这些差异会被循环不断放大。

举个很常见的场景:一套12V备用电源,用两节磷酸铁锂串起来。两节电池静置开路电压分别是3.30V和3.28V,看起来只有20mV差距。充电器以整组电压判断是否充满,当串联总压到7.30V时,可能A电芯已经3.68V,B电芯才3.62V,B还没充满,但A已经接近过充。如果不做均衡,长期下去A更容易衰减,容量越掉越多,B又长期充不满,整个电池组实际可用容量会被“短板电芯”拖死。

这就是均衡存在的底层逻辑:串联电池组没法像并联那样天然自平衡,只能靠外部电路强制把“多出来的电”处理掉,或者把高压电芯的能量搬给低压电芯。2S虽然只有两个单体,但这个问题一点不少见,尤其是在备用电源这种“长期浮充+偶尔深放”的工作条件下,自放电差异带来的失衡会被慢慢放大。

1.2 选 MP2642GR-Z + STM32F031C6 而不选专用AFE的原因

现在市面上做BMS均衡的专用前端很多,内部集成了高精度ADC、均衡MOS、保护逻辑,做成产品当然省事。但在很多中小项目里,专用AFE往往面临两个问题:一是资源用不满,二串均衡根本用不着那么大的芯片;二是封装、供货、调试周期未必跟得上项目节奏,尤其是样品阶段,想快速验证算法和硬件,通用MCU反而更顺手。

MP2642GR-Z 在系统里的角色很明确,它是一颗宽压输入的同步降压转换器,从2S电池组取电,把电压稳到3.3V给 MCU 和外围供电。选它而不是普通LDO,是因为2S电池电压范围比较大,锂离子能到8.4V,磷酸铁锂最低又可能掉到5V出头,LDO直接把高压差变成热量不划算,而且MP2642效率高、外围简单,输出能力也足够。

STM32F031C6 本身就是低成本Cortex-M0,48MHz主频,片内12位ADC、高级定时器PWM、多组GPIO,做两串电压采集和两个均衡通道绰绰有余。而且生态成熟,遇到问题能抄到一堆例程。这套组合的定位是“够用、好买、能自己改”,而不是追求极致集成度,在储能小系统里反而更实际。

1.3 这套系统的均衡工作边界

必须先把边界说清楚:我做的这套是耗散式均衡,也就是被动均衡。较高的电芯通过外部电阻放电,把多余能量以热量形式消耗掉,直到两个电芯电压差进入死区。很多文章一上来就讲主动均衡,其实对于2S小容量备用电源,被动均衡才是经济且务实的选择。

为什么敢这么选?因为备用电源大多数时间是静置和浮充,均衡不需要大电流快速完成,100mA左右慢慢放,完全来得及。如果换成大容量动力电池,或者经常在高倍率充放电下运行,被动均衡的热量和时间都受不了,那时候才值得升级主动均衡。所以本文后续的算法、硬件都围绕这个边界展开,同时我留了一条升级路径,后面单独讲。

2. 硬件电路设计与关键参数计算

2.1 MP2642GR-Z 辅助电源电路设计与出参

MP2642GR-Z 这部分的输入来自整组电池,也就是 Cell1 负极到 Cell2 正极。输出我设定为3.3V,给STM32和外围供电,最大负载按100mA算。这样做的核心原因有两个:一是3.3V直连MCU的VDDA时,ADC参考电压稳定,不会因为电池电压变化导致采样漂移;二是把功率级和信号级从源头分开,均衡开启瞬间的大电流不会直接冲击MCU供电。

电感值可以按典型Buck公式估:

  • 输入最高电压 VIN = 8.4V
  • 输出 VOUT = 3.3V
  • 开关频率 fsw = 500kHz
  • 预计负载 IOUT = 0.1A

最大占空比 D = VOUT / VIN ≈ 0.39,电感纹波电流 ΔIL 一般按 IOUT 的 20%~40% 设计,这里取 ΔIL = 0.15A 左右。电感 L = (VIN - VOUT) × D / (fsw × ΔIL) 算下来大概 22μH,峰值电流不超过0.2A,选额定饱和电流0.5A以上的功率电感就很稳。

输出反馈电阻按照芯片内部参考电压计算。假设参考电压 VREF = 0.6V,要让 VOUT = 3.3V,反馈下电阻取 20kΩ,上电阻就是 (3.3 - 0.6)/0.6 × 20k = 90kΩ,实际取 91kΩ 标准值。注意反馈信号走线一定要远离电感和二极管,否则输出纹波会被耦合进去,引起不稳定。

输入和输出都需要容量充足的陶瓷电容。输入电容放在电池正极附近,起滤除电源线纹波的作用;输出电容主要稳住负载瞬态,我用22μF配合电感形成LC滤波,实测纹波能控制在20mV左右。这些电容必须用X7R或更好材质,X5R在直流偏压下容量衰减明显,别只看标称值。

2.2 STM32F031C6 最小系统与ADC采样链路

STM32F031C6 跑48MHz很简单,直接用内部HSI倍频。VDD接3.3V,VDDA 建议串一个小磁珠再做RC滤波,避免MP2642的开关噪声直接进ADC参考。虽然这颗MCU的ADC参考可以来自VDDA,但很多板子不处理参考电源,导致DCDC纹波叠加在采样值上,均衡判断跟着乱跳,这是很常见的坑。

电压采样电路用电阻分压。两个电芯串联,节点有BAT-、Cell1+、Cell2+三个电位。直接采样Cell1+和Cell2+相对于BAT-的电压,再用程序相减得到Cell2电压,这是最省成本的做法。以锂离子电芯最高4.2V为例,ADC满量程是3.3V,分压比至少要小于0.78。我选了10kΩ + 20kΩ,也就是把电压衰减到2/3,4.2V对应2.8V,留了裕量。

分压电阻用1%精度还不够稳,因为两路电阻的温度系数不一样时,算出来的Cell2电压误差会更大。我后来换成了0.1%精度、25ppm/°C的电阻,并且每块板子用上位机做一次两点校准,把比例因子差异写进Flash。分压输出后面要加RC滤波,我用10kΩ串联电阻加100nF电容,截止频率约160Hz,配合软件低速采样足够过滤高频噪声。

采样通道要注意串扰。STM32F031 的ADC在通道切换时,通道间会有轻微电荷共享,如果前一个通道电压高、后一个通道电压低,结果会有偏差。解决办法是采样时间别太短,我给到了5μs以上,并且每个通道连续采样,DMA搬运到数组后,先去掉最大值最小值再取平均,单次测量抖动能压在±5mV以内。

2.3 均衡开关与限流电阻选型

被动均衡的核心就是“电阻+MOS开关”。两个电芯分别并联一条放电支路,MOS导通时,高压电芯通过电阻放电,电压降下来。大多数低成本方案直接用MCU GPIO驱动N-MOS,GPIO高电平3.3V,所以要选逻辑电平MOS,VGS阈值最好低于1.2V,导通电阻也要小。

均衡电流我按100mA设计。这个值听起来不高,但已经能应对大部分2S备电场景。以磷酸铁锂电芯为例,电压在3.0V~3.6V之间,限流电阻取值按 3.0V / 0.1A ≈ 30Ω,取标准33Ω。电芯电压高到3.6V时,电流约109mA,电阻功耗约0.4W;电压到4.2V(锂离子),功耗会到0.53W。所以电阻绝对不能按1/4W选,最少用1W,最好用2W金属膜电阻,并在PCB上留大面积铜箔散热。

MOS选型的坑在工作区。如果GPIO高电平不够、VGS不足,MOS不会完全导通,内阻会吃掉压差并在MOS上发热,严重时会直接炸管。我实测过有些号称“逻辑电平”的N-MOS,VGS=3.3V时导通电阻还在100mΩ以上,对于0.1A电流不至于炸,但长期可靠性不行。最终选了一颗VGS=4.5V时RDS(ON)小于10mΩ、VGS=2.5V时也能明显导通的管子,并且栅极串联100Ω电阻、10kΩ下拉,保证上电瞬间MCU引脚未初始化时不会误开启均衡。

2.4 保护与低功耗设计

均衡MOS的驱动不管用GPIO直驱还是PWM控制,都必须保证上电默认关断。STM32复位期间GPIO一般处于高阻态,栅极如果悬空,很容易被外部噪声顶到导通,所以下拉电阻是必须的。

还有一个很容易被忽略的细节:均衡电流会影响采样线路。均衡电阻和采样线如果走同一条铜皮,均衡开启时线上的压降会被误当成电芯电压变化。为了避免这个问题,我把采样线直接引到电芯端子附近,再通过细线回到MCU,也就是开尔文接法。功率地只在电池负极单点相连,不让均衡大电流经过采样地的铜皮。

低功耗方面,如果备用电源系统长期在线,MCU常态功耗几毫安问题不大;如果要求极低待机,可以让MP2642的EN受控或加一个PMOS把电源切断,MCU靠RTC定时唤醒。但注意,一旦切断MP2642的电源,MCU也断电了,所以更合适的是保留一颗轻载高效率电源,让MCU进入STOP模式。STOP模式下,所有均衡MOS必须关闭,程序里要在进入低功耗前做一次强制关断,再等待几个毫秒确认。

3. 均衡软件策略与状态机实现

3.1 电压采集滤波与开路电压估计

硬件能拿到原始ADC值,但软件要负责告诉系统“现在要不要均衡”。第一步是滤波。我会在主循环里用DMA连续采样32次,每采样完成后排序,去掉5个最大值和5个最小值,再取平均。这样可以滤掉瞬时的开关噪声。对于低速均衡系统,不需要特别高频,每200ms刷新一次电压数据就够。

第二步是识别“静置状态”。电芯在充电刚结束时,表面有极化电压,测出来的端电压比真实开路电压偏高;放电刚结束又相反。如果直接拿这种电压做均衡判断,会出现“越均衡越乱”的现象。我的做法是:只有当系统中没有明显充放电电流、且持续时间超过5分钟,才认为电池处于静置,此时采集的电压才可信。如果连续静置时间不够,程序就把均衡挂起。

磷酸铁锂的电压平台很平,微小的电压差可能对应的容量差并不小,所以光看绝对电压并不完美。但在低成本2S系统里,没有库仑计,电压均衡还是最实用的。我会在程序里额外保存历史最大压差,观察趋势。如果某个电芯总是“被均衡”的那一侧,说明它容量已经落后,单纯均衡救不回来,只能提示用户更换。

3.2 均衡启动条件与回滞控制

均衡不能太灵敏,也不能太迟钝。我用两个阈值分开控制启动和停止,启动阈值设成50mV,停止阈值设成10mV,中间还有一段保持时间。

为什么要这样设计?如果只有一个阈值,比如压差大于30mV就启动、小于30mV就停止,那么当均衡刚刚让压差降到29mV时,程序立刻关闭均衡,电芯极化电压恢复后压差又弹回35mV,系统就会在启动和停止之间反复震荡。加入回滞之后,启动需要压差够大,停止必须压差进入更小的死区,并且维持一段时间,系统才不会频繁动作。

除了压差阈值,还需要检查“当前是不是适合均衡”:

  • 系统处于放电状态下,禁止均衡,因为放电时端电压受内阻影响,判断不准确;
  • 电芯温度低于10°C或高于45°C,禁止均衡,低温内阻大,高温热量容易累积;
  • 保护板处于过充保护或短路保护状态,禁止一切均衡,先把系统故障排除再说。

3.3 三阶段均衡状态机

整个均衡流程我拆成四个状态:IDLE(空闲)、CHECK(检查条件)、DISCHARGE(放电均衡)、HOLD(保持验证)。这样做的好处是逻辑清晰,不会出现“一边测电压一边又担心均衡电流干扰采样”的纠结。

IDLE状态下,程序按周期刷新电压和系统状态;一旦发现压差大于50mV,进入CHECK。CHECK状态会进行一个固定的静置延时,比如10秒,确保当前电压是稳定的,而不是极化造成的假象。延时之后如果条件仍然满足,程序根据电压高低选择需要放电的电芯。

DISCHARGE状态才是真正打开均衡MOS的阶段。这里可以做成固定电流放电,也可以用PWM控制平均放电电流。我建议用PWM而不全开,好处是可以通过降低占空比来减小发热。比如我把PWM频率设在5kHz,初始占空比100%,如果电芯压差大但散热条件一般,可以改成50%平均放电,温度会低很多。程序里每秒钟刷新一次电压,判断是否达到停止条件。

当压差小于10mV且维持30秒后,进入HOLD状态,强制关闭均衡并等待一段时间。目的是让电芯从放电状态的端电压恢复,观察真实的压差是否还满足要求。如果HOLD结束后压差仍然小于10mV,就认为均衡完成,回到IDLE;如果压差又弹回去,说明均衡电流不够或者电芯极化严重,会重新进入CHECK。

3.4 核心代码实现

代码不追求完整工程,重点看状态机骨架和均衡方向的判断逻辑。定义如下:

#define BAL_START_MV 50u #define BAL_STOP_MV 10u #define BAL_HOLD_MS 30000u #define BAL_MAX_TIME_MS 7200000u typedef enum { BAL_IDLE, BAL_CHECK, BAL_DISCHARGE, BAL_HOLD, BAL_FAULT } bal_state_t; static bal_state_t bal_state = BAL_IDLE; static uint8_t balance_cell = 0; /* 0: none, 1: cell1, 2: cell2 */ void balance_task(void) { static uint32_t t_check_start; static uint32_t t_hold_start; static uint32_t t_discharge_start; static int32_t v1_mv, v2_mv; read_filtered_voltages(&v1_mv, &v2_mv); switch (bal_state) { case BAL_IDLE: if (abs(v1_mv - v2_mv) >= BAL_START_MV && is_balance_allowed()) { bal_state = BAL_CHECK; t_check_start = now_ms(); } break; case BAL_CHECK: /* 先确认电压稳定,再决定放电哪一节 */ if (now_ms() - t_check_start < 10000) { break; } if (!is_balance_allowed() || abs(v1_mv - v2_mv) < BAL_START_MV) { bal_state = BAL_IDLE; break; } if (v1_mv > v2_mv) { balance_cell = 1; } else { balance_cell = 2; } bal_state = BAL_DISCHARGE; t_discharge_start = now_ms(); break; case BAL_DISCHARGE: if (balance_cell == 1) { pwm_set_duty(BAL_TIM_CH1, 100); } else { pwm_set_duty(BAL_TIM_CH2, 100); } if (abs(v1_mv - v2_mv) <= BAL_STOP_MV) { pwm_set_duty(BAL_TIM_CH1, 0); pwm_set_duty(BAL_TIM_CH2, 0); bal_state = BAL_HOLD; t_hold_start = now_ms(); } else if (now_ms() - t_discharge_start > BAL_MAX_TIME_MS) { pwm_set_duty(BAL_TIM_CH1, 0); pwm_set_duty(BAL_TIM_CH2, 0); bal_state = BAL_FAULT; } break; case BAL_HOLD: if (now_ms() - t_hold_start >= BAL_HOLD_MS) { bal_state = BAL_IDLE; } break; case BAL_FAULT: pwm_set_duty(BAL_TIM_CH1, 0); pwm_set_duty(BAL_TIM_CH2, 0); /* 需要人工复位或远程清除 */ break; } }

代码里 is_balance_allowed() 负责判断温度、充放电状态和保护板状态。PWM关闭时,为了避免GPIO输出高阻导致MOS误开,我把PWM输出引脚同时配置成推挽输出并输出低电平。这一点在中断中尤其重要,任何时候都不要让栅极悬空。

4. 实测问题与排查技巧

4.1 MP2642 输出纹波干扰 ADC

第一次上板调试,我遇到最明显的问题是ADC读数跟着均衡MOS开关跳。均衡MOS关断的时候,读到的电压是3.612V;一打开均衡,同样的节点读数突然变成3.588V,偏差将近24mV。这不是电芯电压真的掉了,而是MP2642输出纹波和地平面噪声叠加到了ADC参考上。

处理办法有三层:

第一,把VDDA和VDD从物理上分开。VDD直接走DCDC输出,VDDA串一个几十欧姆电阻再加一个10μF钽电容和100nF陶瓷电容,高频噪声被滤掉一大半。

第二,把分压电阻的采集点远离均衡MOS的电感/开关节点。开关节点的高dV/dt容易通过空间耦合到采样线,如果采样线走得太近,就算ADC参考干净也会采到杂散信号。

第三,软件上把ADC采样时间从默认方案改到5μs以上,并且把PWM均衡的采样点安排在高电平结束后的关断等待期。均衡PWM是固定频率的,只要让ADC触发和PWM同步,避开MOS开关的瞬间,数据质量立刻改善。

4.2 采样点位置导致的电压误判

这类问题在原理图上根本看不出来,必须上电实测。有一版PCB,电压采样线直接从均衡电阻的焊盘上分出来,均衡不启动时电压正常,均衡一启动,采到的电压明显偏低。原因很简单:均衡电阻走的是大电流,那条铜皮存在压降,采样线接到了电阻负载端,相当于把铜皮压降也算进了“电芯电压”。

正确的做法是把采样线单独走到电芯正负极,再到MCU板上的分压电阻,中间不要和均衡功率回路共用铜皮。如果结构上实在做不到,也要在软件里用PWM关断瞬间采样。PWM关断时均衡电流为0,功率铜皮上没压降,采样值才是真实电芯电压。

另外,Cell2电压是采样Cell2正极和Cell1正极相减得到的。如果两路分压电阻位置相距很远,走的铜皮温度不同,电阻值就会漂。我最后把两路分压电阻并排放在板子同一区域,尽可能保证温度一致,误差明显减小。

4.3 均衡电阻过温与MOS管损坏

小容量系统100mA均衡,按理论算电阻功耗不到0.6W,但实际连续跑两个小时,电阻表面温度会非常高。第一次热像仪拍出来,贴片电阻表面已经到130°C,旁边的PCB都变色了,MOS管温度倒是正常。原因是贴片电阻的热阻大,放在小焊盘上散热不够。

解决办法是把1W电阻换成2W直插金属膜,并且把周围PCB铜箔铺宽。如果实在想用贴片,选1210以上封装,多放几个过孔把热量导到背面铜皮。均衡是一种慢过程,热设计不好会直接影响系统寿命。

MOS管损坏多数不是过流,而是导通不彻底。GPIO高电平只有3.3V,如果选的是普通MOS,VGS达不到完全导通的电压,MOS处于线性区,RDS(ON)可能有几十毫欧,0.1A电流也能积累热量,长时间就会炸。建议直接看数据手册VGS=2.5V/3.3V时的RDS(ON),别只看VGS(th)。另外栅极电阻选100Ω到200Ω,可以抑制寄生振荡,但不要太大,否则开关时间变慢,开关损耗反而增加。

4.4 与充电器、保护板协同的时序问题

均衡和充电器同时工作,是最容易出逻辑冲突的场景。有一回我测试浮充均衡,发现均衡电阻一直在发热,但两个电芯的压差却越来越大。抓了充电整组电压曲线才发现,充电器已经进入恒压阶段,我的均衡在里面对高压电芯放电,充电器误以为整组没满,继续把它往高了充,低压电芯反而没有机会被充满。

后来我调整策略:充电末期允许均衡,但均衡电流不要开满;一旦检测到充电电流开始减小,说明充电器正在进入恒压限流,这个时候均衡给高压电芯泄压是对的,能帮低压电芯“抢”到更多充电电流。但均衡不能把高压电芯电压拉到远低于充电目标,否则充电器会一直满负荷输出,均衡电阻白耗电。状态机里加了一个充电阶段判断,只有充电电流小于设定值才放开均衡。

保护板也要注意。如果均衡板接在保护板的P+、P-后面,保护板触发过充或过放后对外断开了,电压采样会直接变成0V。程序必须识别这种异常,不要继续开均衡,否则可能在保护板恢复瞬间产生浪涌。

4.5 问题排查速查表

现象可能原因检查对策
ADC读数随均衡开关跳动DCDC纹波进入ADC参考,或采样点与功率回路共用铜皮VDDA增加LC滤波,采样线改为开尔文接法,PWM关断期间采样
均衡开启后电压反向变大极化电压未静置,或均衡电流过大造成铜皮压降增加CHECK静置时间,降低PWM占空比或减小均衡电流
均衡电阻烫手电阻功率余量不足,散热铜皮不够换2W以上电阻,扩大铺铜,开窗加散热
MOS管温度高但不工作VGS驱动不足,MOS处于线性区换逻辑电平MOS,测栅极波形确认完全导通
均衡完成后压差迅速恢复两端电芯容量差已经超过均衡能力用长期趋势判断,提示更换落后电芯
保护板动作后电压显示异常均衡板接在保护板输出端,逻辑异常增加0V/异常状态检测,禁止均衡

5. 后续升级方向与个人体会

5.1 从被动均衡升级主动均衡的几条路径

如果哪天电池容量变大、均衡电流要求从100mA提升到1A,被动均衡的散热和电能浪费就很难接受了。那时可以保留 STM32F031C6 的采样和状态机,把电阻放电支路替换成“开关电容均衡”或“单电感双向Buck-Boost”,MP2642GR-Z 继续做辅助电源,整体升级成本可控。

开关电容均衡适合相邻电芯,两个电芯之间用一个电容来回搬运电荷,控制简单、没有电感,体积也小,但1A以上的电流效率一般。单电感buck-boost则更灵活,通过一组MOS开关把储能电感轮流连接到高、低压电芯,STM32做PWM控制,能实现真正的能量转移。

不过要提醒的是,主动均衡把“耗散热量”变成“功率转换效率”,并不是无本万利。电感、MOS管、PWM频率、死区时间都增加调试成本,对一般的2S备用电源收益有限。如果项目预算允许、后期需要更大电池组,再升级不迟。

5.2 关于方案成本和可靠性的几点心里话

这套板子做下来,BOM成本很低,但可靠性不是靠省钱省出来的,而是靠采样精度和状态机设计。均衡本质上不是“把电压调平”,而是“给落后电芯一个喘息的机会”。如果电芯本身已经严重老化,被动均衡只能延缓恶化,救不回来。所以我把重点放在怎么准确判断“谁落后、什么时候该出手”,这把功夫比单纯堆均衡管更重要。

最后再分享一个调试技巧:在均衡状态机里加一个历史记录数组,记录每次均衡的方向、持续时间和结束时的压差。如果发现某个电芯几乎每次都在放电,或者每次都很难降到目标阈值,尽早定位真正的问题,别让它一直拖着发热。这个习惯帮我省了非常多现场排查时间。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询