
做储能后备电源的人心里基本都有一笔账电芯出厂挑得再匀串联跑上两三百个循环之后压差该出来还是会出来。尤其是两节串联的电池包看着组数少好像好管实际上很多量产项目恰恰就栽在“只做保护不做均衡”上面。我最近用 MP2642GR-Z 这款 2S 均衡芯片配 STM32F100ZE 做主控做了一套针对两个串联电池单元的均衡系统用在小型储能和备用电源场景里。这篇文章把方案选型、硬件链路、均衡策略、固件实现和实测踩坑一次性写清楚给同样在做 2S BMS 的朋友做个参考。1. 两节串联电池的均衡问题为什么储能和备用电源系统绕不开它1.1 不均衡从哪来制造差异只是起点两节电池串联工作流过同一个电流但每一节的开路电压、实际容量、内阻、自放电率都不可能完全一样。出厂的时候一致性再好也只是初始状态。温度场不一样老化速度不一样循环几次之后差异就开始显现。尤其是备用电源系统电池经常长时间处于浮充状态轻微的不均衡会被涓流慢慢放大等到真正需要放电支撑负载的时候整个系统的可用容量已经被“短板”锁死了。我在做这两串电池的均衡之前先花了一天时间把两节电芯的压差变化趋势记录下来满电静置压差大概 15mV放到一半时压差能到 40mV 以上末端甚至超过 100mV。这个数据让我意识到均衡不是做一个“有就行”的功能而是要持续跟踪、动态调整的过程。1.2 不均衡带来什么后果容量被“短板”锁死两节串联电池组的总容量并不是两节电芯容量的平均值而是无限接近容量最小的那一节。举个例子一节电池额定 3Ah另一节额定 2.8Ah系统放电时 2.8Ah 那节先放空保护板必须切断输出这时候那节 3Ah 的电池里面还剩下 0.2Ah 没放出来结果就是整组可用容量只有 2.8Ah 左右。对储能和备用电源来说客户买的就是“能存多少电、能撑多长时间”这 0.2Ah 看着不多但放到几十安时的大电池组上对应的可能就是几分钟到几十分钟的后备时间。充电端也是一样容量小的一节先到满电为了保护它整组必须停止充电。日积月累好的电池永远吃不饱差的电池永远被保护系统容量衰减速度会明显快于单体自然老化。1.3 为什么保护板替代不了均衡传统保护板 IC 只管过压、欠压、过流、短路这些安全门限门限到了就断开 MOSFET保护的是“不出事故”。均衡做的是另一件事在安全门限之内主动把高电压电池的能量处理掉或者搬给低电压电池让两节电池的工作状态尽量保持一致。很多人觉得 2S 系统太简单保护板加一个均衡电阻就行了。实际上在储能这种高频充放、长期浮充的场景里没有均衡策略的保护板只能保证“不会立刻出问题”但系统的能量利用效率一直在下降。均衡管的是“慢慢变差”这件事保护板管的是“突然变坏”这件事两个缺一不可。2. 系统方案选型均衡芯片与主控的分工边界2.1 为什么用专用均衡芯片而不是自己搭自己搭一个被动均衡电路并不难一个差分运放加一个功率三极管加一个限流电阻就能跑起来。但做到“两个单元都能采准、都能独立控制、还能和主控通信”这一步分立方案的复杂度和故障点就开始失控了。2S 系统电池总电压虽然不高差分采样、共模电压、功率开关驱动、温度补偿这些问题一个都不少。MP2642GR-Z 这颗芯片的价值在于它把两节电芯的电压检测、均衡功率开关、以及相关的保护逻辑集成在一起MCU 只需要通过数字接口去读电压、写控制字。这样硬件上省掉了大量差分电阻和运放BOM 面积更小故障点更少而且芯片内部的采样校准和温度补偿能力是通用的 MCU ADC 很难直接替代的。2.2 MP2642GR-Z 在系统里的定位把 MP2642GR-Z 理解为“均衡前端”比较准确它负责电池电压的直接测量、均衡通路的通断和功率执行。这类芯片通常会把两节电芯的电压检测通路压缩到引脚内部外部只需要按手册配好均衡电容、滤波电容和使能引脚。MCU 不需要关心均衡电流具体怎么流过内部功率管只需要告诉芯片“现在该均衡了均衡哪一节”然后芯片内部完成具体的功率操作。实际项目中我把 MP2642GR-Z 放在电池组附近走线尽量短粗数字接口单独引到主控板。因为均衡动作会产生脉冲电流如果把芯片放得离电池很远寄生电感会带来振铃轻则采样抖动重则干扰整个系统的地平面。2.3 STM32F100ZE 的资源盘点STM32F100ZE 是 Cortex-M3 内核主频 72MHz512KB Flash64KB RAM三路 12 位 ADC多路 UART、I2C、SPI。对 2S 均衡这种规模来说资源余量非常充足。整个固件跑下来Flash 占用不到 20KBRAM 占用不到 4KB剩余的存储空间完全可以留给后续扩展比如再加温度采集、循环计数、通信协议升级等。选择这颗片子的另一个考虑是团队接手成本低。F1 系列几乎是入门标配资料多示例代码也全即使后续换人维护也不会出现“只有一个人看得懂”的情况。对于储能这种强调长期稳定运营的产品这一点比追求最新内核重要得多。2.4 数据流与控制流的关系整个系统的控制流很清晰MP2642GR-Z 负责采样两节电芯电压STM32F100ZE 通过 I2C 周期读取然后在主循环里跑均衡策略把“均衡哪一节、开还是关”的指令写回芯片。与此同时STM32F100ZE 通过 UART 把两节电压、均衡状态、电芯温度、故障码上报给上位机或者储能控制器。这两条链路是并行工作的控制链路要快采样周期通常在 100ms 以内通信链路要稳上报周期反而不能太频繁避免占用过多 CPU 时间或者被无关的干扰帧打断。我最后的处理方式是控制链路用定时器驱动通信链路用主循环里的状态机驱动两者互不干扰。单元职责交互方式2S 电池组储能本体提供 6.0V~8.4V 电源功率路径MP2642GR-Z电压检测、均衡功率执行、保护辅助I2C 接口STM32F100ZE均衡策略、故障判断、对外通信GPIO/ADC/UART上位机/储能控制器状态显示、系统联动、远程管理UART/RS4853. 硬件链路设计从电池采样到均衡驱动每个节点都要算清楚3.1 系统电源架构2S 锂离子电池标称电压 7.2V~7.4V工作电压范围大概在 6.0V 到 8.4V如果是磷酸铁锂则是 5.0V 到 7.3V。MCU 和均衡芯片都需要 3.3V 供电所以第一件事就是把电池总压降到稳定的 3.3V。低功耗场景用 LDO 就够。但需要注意的是LDO 的功耗是压差乘以电流总压 8.4V、负载电流 50mA 的时候LDO 上会消耗 250mW 左右的功率。储能设备往往要求待机功耗很低所以我在方案里加了一个开关控制MCU 休眠时把均衡芯片和通信接口的电源直接断掉只保留主控自身的最低功耗供电。这个细节让整机待机功耗降到了可以忽略的程度。3.2 电压采样通道信芯片还是信 MCU这是整个硬件设计里最需要想清楚的地方。MCU 直接采样 2S 电池时第二节电池的正极相对系统地有接近 8V 的共模电压必须用差动分压网络才能安全接入 ADC 引脚。分压电阻的精度和温漂会直接变成采样误差而且共模噪声会随着均衡电流的开关而波动。我的建议是电池电压的最终判断以 MP2642GR-Z 内部检测结果为准。芯片设计时已经针对两节单体做了内部缩放和校准手册会给出精度指标通常比 MCU 加外部电阻分压的方案更可靠。MCU 自身 ADC 只用来采集辅助信号比如采温度、检测板载电源电压不参与电池电压判断。这样分工可以避免两套采样系统互相打架调试时也容易定位问题。3.3 均衡支路与功率估算被动均衡的电流路径是电池正极、均衡电阻、开关管、回到电池负极。以一节 3.6V 电芯为例均衡电阻选 33Ω均衡电流就是 3.6V / 33Ω ≈ 109mA电阻上消耗的功率约 0.4W。这个功率不大但均衡电阻必须远离电解电容和 MCUPCB 上要留出足够的铜皮散热否则长时间均衡时局部高温会让附近元件漂移。主动均衡则通过电容或电感在两个单体间搬移电荷能量不是变成热而是转移到低电压电池。好处是效率高发热小代价是外围储能元件的取值、布局和芯片的控制时序都必须严格按手册来。尤其是电容均衡电荷搬移电容要尽量靠近芯片引脚回路面积要小否则寄生电感会直接吃掉效率甚至产生过冲电压。3.4 通信接口与地线处理MP2642GR-Z 的 I2C 接口需要上拉电阻。板上走线短常见的 4.7kΩ 上拉电阻就够上拉电源用 MCU 的 3.3V不要用电池电压直接上拉避免电平不匹配。地线处理是最容易忽略的坑。均衡开关动作时电流会在功率地回路上产生瞬态压降如果采样地和功率地在芯片附近共用同一段走线采样到的电压就会跟着抖。我的做法是数字地、模拟地和功率地分开走线最后在电源入口处单点汇接这样均衡动作对采样回路的影响可以压到最小。4. 均衡策略细节阈值、滞回与状态机设计4.1 均衡启动与停止的判据均衡策略最朴素的做法是检测到两节电压差超过某个阈值就打开均衡低于某个阈值就关闭。但阈值怎么定需要结合电池化学体系。三元锂电电压曲线在末端变化明显压差能够比较真实地反映容量差我习惯设 30mV 启动、10mV 停止。磷酸铁锂的电压平台非常平缓中段电压几乎不随容量变化这时候单靠压差判断均衡很容易误判看起来压差很小实际容量差已经很大等电压差暴露出来时往往已经在平台边缘了。所以磷酸铁锂系统里需要辅助手段比如记录静置电压、结合充放电容量来修正均衡目标或者干脆把均衡启动阈值调得更小、均衡时间拉得更长让系统慢慢“磨”。4.2 被动均衡与主动均衡的控制差异被动均衡的控制逻辑最简单B0 电压高于 B1就打开 B0 的均衡开关电流流过电阻把多余能量耗掉。比如压差高了 100mVVcell 是 3.6V均衡电流 100mA 左右一小时能搬掉约 360mAh 的“等效容量差”。对储能备用电源这种长期浮充的场景把顶部多出来的那点容量慢慢耗掉完全够用而且电路极简、可靠。主动均衡多了一层决策能量搬过去多少合适。搬得太少压差收敛慢搬得太多会给低压电池造成持续的充电脉冲反而加剧极化。我的做法是不要陷入连续均衡而是“均衡 60 秒、停止采样、确认压差缩小后再继续”。这样每一步都有反馈不会出现过度均衡导致原先的高压电池变成低压电池的情况。4.3 滞回窗口的作用均衡控制如果只有启动阈值没有停止阈值会出现在启动阈值附近反复通断的振荡现象压差刚超过阈值均衡一开压差掉下来低于阈值均衡一关压差又升上去。这个振荡对 MOSFET 和均衡电容都不友好。滞回窗口就是为了解决这个问题。以 30mV 启动、10mV 停止为例一旦进入均衡状态必须等压差收敛到 10mV 以下才关闭相当于给控制逻辑加了一个“缓冲区”。窗口设置不要太宽太宽会导致均衡过度也不要太窄太窄起不到防振荡的作用。我最后调到 25~35mV 启动、8~12mV 停止实测效果不错。4.4 状态机设计不止三个状态均衡控制需要一个明确的有限状态机。最基本的状态有三个空闲、均衡中、故障。但我后来发现在储能系统里还需要一个“暂停”状态——当充电器在大功率充电时均衡动作可能会和充电过程互相影响这时候应该先暂停均衡等充电电流稳定下来再恢复。状态触发条件动作IDLE 空闲压差 30mV关闭所有均衡开关BAL 均衡中压差 ≥ 30mV打开对应均衡开关PAUSE 暂停充电电流过大/温度过高临时关闭均衡条件恢复后重新进入 BALFAULT 故障通信连续失败/电压采样异常关闭均衡并锁定需要人工恢复状态机的好处是所有行为都可以预判不会出现在中断里改错状态、或者两个条件同时触发导致逻辑混乱的情况。尤其是多故障叠加时状态机加故障码的方式比简单的 if-else 分支可靠得多。5. STM32F100ZE 固件落地采样、滤波与均衡控制5.1 初始化流程STM32F100ZE 的初始化分四步首先要配置系统时钟F1 系列通常用外部晶振倍频到 72MHz然后配置 GPIO把 I2C 引脚设为开漏复用把控制均衡的引脚设为推挽输出接着配置 I2C 外设通信速率设在 400kHz尽量接近芯片支持的上限但也要留足裕量最后配置一个定时器用于周期触发均衡主循环。实际项目里我用了两个定时器一个 100ms 周期的定时器负责采样和均衡控制另一个 1s 周期的定时器负责统计运行时间和上报数据。控制链路和通信链路分开比在一个主循环里按时间戳硬分配更容易维护。5.2 电压读取与滤波从 MP2642GR-Z 读回来的寄存器值要按手册给出的换算公式转换成分辨率后的毫伏数。数据手册里一般会给出类似“电压寄存器值 × LSB 值 实际电压”的公式这一步务必从手册原文抄不要凭猜测。采样滤波我用的是滑动平均加一级简单中值剔除。均衡开关动作瞬间寄存器值可能会出现一个孤立的大跳变如果直接参与平均会把真实电压带偏。所以我的流程是连续采 5 次去掉最大值和最小值剩下 3 次取平均。这比单纯多采几次取平均更抗偶发干扰。uint16_t read_cell_voltage(uint8_t cell_channel) { uint32_t sum 0; uint16_t sample[5]; // 读取5次丢弃最大最小剩余求平均 for (int i 0; i 5; i) { sample[i] mp2642_read_voltage(cell_channel); } // 冒泡排序取中间3个值 for (int i 0; i 4; i) { for (int j i 1; j 5; j) { if (sample[j] sample[i]) { swap(sample[i], sample[j]); } } } sum sample[1] sample[2] sample[3]; return (uint16_t)(sum / 3); }5.3 均衡状态机移植成代码状态机在代码里用 switch-case 实现关键是状态转移条件必须只依赖当前状态和最新采样值不能跨状态写共享变量。下面是一段简化后的均衡 poll 函数实际工程里还会加看门狗喂狗和错误计数。typedef enum { BAL_IDLE, BAL_BALANCING, BAL_PAUSE, BAL_FAULT } bal_state_t; volatile bal_state_t bal_state BAL_IDLE; uint16_t v_cell0, v_cell1; void balancing_poll(void) { // 读取两节电压 v_cell0 read_cell_voltage(CELL0); v_cell1 read_cell_voltage(CELL1); int16_t dv (int16_t)v_cell0 - (int16_t)v_cell1; switch (bal_state) { case BAL_IDLE: if (abs(dv) 30) { if (dv 0) mp2642_enable_balance(CELL0); else mp2642_enable_balance(CELL1); bal_state BAL_BALANCING; } break; case BAL_BALANCING: if (glb_temp 55) { // 温度过高先暂停 mp2642_disable_balance(); bal_state BAL_PAUSE; } else if (abs(dv) 10) { mp2642_disable_balance(); bal_state BAL_IDLE; } break; case BAL_PAUSE: if (glb_temp 50) bal_state BAL_BALANCING; // 温度回落重新均衡 break; default: bal_state BAL_FAULT; mp2642_disable_balance(); break; } }注意一个容易被忽视的问题均衡状态机的“风扇效应”。如果系统进入均衡后因为压差短暂变小就退出紧接着又因为压差恢复而重新进入会把均衡管频繁开关。所以我在实际代码里加了进入均衡后的最短持续时间锁存至少保持 30 秒避免短周期反复通断。5.4 与 MP2642GR-Z 的 I2C 交互细节I2C 通信要注意异常处理。芯片所在的电池组区域电磁环境并不干净均衡开关动作时产生的电流脉冲可能耦合到 I2C 线路上。如果通信校验失败不要立即报故障应该重新读取三次连续失败次数超过设定阈值才进入 FAULT 状态。我还把“写寄存器”的动作从中断里移到了主循环。因为写均衡开关的同时I2C 线上不能有正在进行的读操作中断里抢占会造成协议错乱。定时器中断只负责置一个标志位主循环检测到标志位之后再执行读和写这个设计保证通信过程始终是串行的。5.5 对外通信协议STM32F100ZE 通过 UART 上报数据协议格式我设计得很简单帧头0xAA 0x55、数据长度、两节电压各 2 字节、均衡状态 1 字节、温度 1 字节、CRC16 校验 2 字节、帧尾。上位机只要按这个协议解析即可。通信周期不是越快越好。储能控制器通常不需要毫秒级的数据500ms 上报一次足够。我对 UART 进行了空闲中断处理保证主循环不会被大量的发送中断打断。调试阶段可以把上报周期刻意改成 100ms方便观察均衡过程中的电压曲线。6. 实测数据与调试避坑均衡效果怎么评判哪些坑必须避开6.1 实测均衡效果我搭了一个 2S 三元锂电测试架人为制造压差到 120mV然后观察被动均衡的收敛过程。均衡电流大约 100mA前 5 分钟压差快速下降后面越来越慢最终稳定在 10mV 左右整个过程大约 15 分钟。指标均衡前均衡 5 分钟均衡 15 分钟电芯 0 电压4.12V4.08V4.035V电芯 1 电压4.00V4.01V4.025V压差120mV70mV10mV需要注意的是被动均衡消耗的是高电压电池的能量这些能量最终变成了均衡电阻上的热。所以均衡过程中系统整体荷电状态会有少量下降这属于正常现象。如果均衡时间过长且压差几乎没有收敛就要怀疑是不是电池已经出现了较大的不可逆容量差单纯靠均衡已经无法解决需要考虑更换电芯。6.2 调试中发现的实际问题第一个坑是 ADC 参考电压漂移。虽然电池电压判断主要靠 MP2642GR-Z但我一开始也用 MCU ADC 去采 MCU 板上的 3.3V 电源电压做过参考校正。后来发现当均衡电阻发热时LDO 的输出会跟着漂移MCU 参考电压变化导致辅助采样通道的数据也跟着变。最后我把参考电压校正逻辑去掉了改用芯片出厂校准值问题消失。第二个坑是地线干扰导致继电器误动。备用电源系统里有一个输出继电器的控制引脚标称耐压和电流都足够但在均衡动作瞬间继电器控制线居然发生了误触发。用示波器抓才发现均衡脉冲电流在 PCB 地线上造成了超过 1V 的瞬态压差而这个地线恰好也是继电器驱动电路的回流路径。后来我把继电器驱动的回流路径独立出来问题彻底解决。第三个坑是均衡芯片的寄存器默认值。有一版固件升级之后均衡功能突然失效查了很久发现新版本驱动库在初始化时给芯片写入了一个“关闭所有均衡功能”的默认寄存器值。所以初始化时序一定要先读后写不要盲目往寄存器里填固定值尤其不要依赖固件库里的默认结构体。6.3 均衡效果的好与坏怎么看评判均衡系统不是看压差是不是绝对为零而是看两点一是在充电末端系统能不能把压差稳定在保护阈值以下二是在长期浮充之后系统的可用容量是不是能保持稳定。把压差做到 5mV 以内当然好但代价可能是长时间均衡、发热和芯片寿命损耗这在工程上不一定值得。我在这个项目里的经验是把目标设定为“充电末端压差低于 20mV、浮充 24 小时后压差不扩大”这个标准已经能保证系统容量利用率在 95% 以上。再往下压投入产出比就很低了。均衡系统做的不是“完美”而是“够用且稳定”。6.4 接下来可以对系统做哪些扩展这套硬件和固件框架跑通之后可以扩展的方向不少。比较实用的是增加电芯内阻估算通过记录不同电流下的电压响应来推算内阻变化这个数据可以预警电池老化。也可以在现有 UART 协议上叠加 RS485 总线让多个储能模块可以并联通信组成更大规模的储能系统。还有一个值得做的扩展是温度点阵的均匀化。我现在只采样了一路电池温度如果电池包比较长不同位置的电芯温度差异可能会超过 5℃这会影响均衡策略的判断。加两到三路温度采样之后可以把“温度最高的一节优先暂停均衡”这样的保护逻辑加进去让系统更贴近实际工况。最后分享一点我个人在实际调试里的体会均衡参数最好不要凭空定先把电池充到末端记录电压曲线和拐点位置再回头定启动阈值效果比拍脑袋设一个经验值好得多。均衡不是越频繁越好也不是越快越好它要匹配电池自己的脾气。这套 MP2642GR-Z 加 STM32F100ZE 的方案我在测试台上跑了两个多月压差一直稳定在计划范围内发热和故障率都可控后续如果要移植到别的电池化学体系只需要重新标定阈值和状态机参数框架本身不需要大改。