4. CAN协议基础:从物理层到BMS实战

各位同学好,我是老张。在储能系统里摸爬滚打了十几年,今天咱们聊聊CAN总线。说实话,CAN协议在BMS里就像人的神经系统——电池数据、控制指令全得靠它跑。我见过太多项目因为CAN通信出问题,现场调试到凌晨三点。所以这一章,咱们把CAN的底裤都扒清楚。

4.1 CAN总线物理层特性

先说说物理层。CAN总线用两根线:CAN_H和CAN_L。它靠差分信号传输,说白了就是两根线的电压差来决定是0还是1。

关键参数:

  • 显性电平(逻辑0):CAN_H ≈ 3.5V,CAN_L ≈ 1.5V,压差2V
  • 隐性电平(逻辑1):CAN_H ≈ 2.5V,CAN_L ≈ 2.5V,压差0V
  • 总线终端电阻:120Ω(两端各一个)
  • 最大通信距离:40m @ 1Mbps,1000m @ 50kbps

为什么用差分信号?抗干扰啊!我在储能电站现场遇到过,逆变器一启动,单端信号全乱套。但CAN总线稳如老狗,因为干扰同时作用在两根线上,压差不变。嗯,这里要注意:终端电阻必须加,不加的话信号反射能把波形打成狗啃的。

避坑指南:我曾经在调试一个40尺集装箱的BMS时,发现CAN通信偶尔丢包。查了两天,最后发现是终端电阻焊在了PCB上,但线缆太长导致阻抗不匹配。后来改成在接线端子处加120Ω电阻,问题解决。

4.2 CAN 2.0A与CAN 2.0B帧格式

CAN协议有两个版本:2.0A和2.0B。区别在哪?说白了就是ID长度不同。

特性 CAN 2.0A(标准帧) CAN 2.0B(扩展帧)
ID长度 11位 29位
最大节点数 2048个 5.36亿个
帧头开销 47位 67位
兼容性 基础 兼容2.0A

数据帧结构是这样的:

标准帧(CAN 2.0A):
SOF + 11位ID + RTR + IDE + r0 + 4位DLC + 0-8字节数据 + 15位CRC + ACK + EOF

扩展帧(CAN 2.0B):
SOF + 11位ID + SRR + IDE + 18位扩展ID + RTR + r1 + r0 + 4位DLC + 0-8字节数据 + 15位CRC + ACK + EOF

我个人习惯在BMS里用CAN 2.0B。为什么?因为储能系统动辄上百个电池模组,11位ID根本不够分。你想想看,每个模组要上报电压、温度、SOC,还要下发均衡指令,29位ID才能把设备类型、模组编号、数据类型都编码进去。

4.3 CAN报文ID与仲裁机制

仲裁机制是CAN总线的精髓。多个节点同时发送时,ID小的优先。怎么做到的?靠线与逻辑——显性电平(0)会覆盖隐性电平(1)。

举个例子:

节点A发送ID:0x123(二进制:0001 0010 0011)
节点B发送ID:0x456(二进制:0100 0101 0110)

仲裁过程:
第1位:A发0,B发0 → 继续
第2位:A发0,B发1 → A的0覆盖B的1,B退出
结果:A获胜,继续发送数据

实战经验:我在设计BMS通信协议时,会把紧急报文(比如过压报警)的ID设得很小,比如0x001。普通数据(比如温度巡检)用大ID,比如0x300。这样紧急情况永远优先。记住:ID越小,优先级越高。

为什么会这样设计?因为CAN总线没有主从关系,所有节点平等。靠仲裁机制解决冲突,不需要额外的握手协议。这在储能系统里太重要了——电池保护动作必须毫秒级响应,等主机轮询黄花菜都凉了。

4.4 CAN在BMS中的应用

BMS里CAN总线主要干三件事:

  1. 电池数据上报:每个模组定时发送电压、温度、SOC
  2. 控制指令下发:均衡开启/关闭、继电器控制
  3. 故障报警:过压、欠压、过温、绝缘故障

我参与的一个50MW储能项目,用了三级CAN网络:

  • 一级:电池模组内部CAN(250kbps,短距离)
  • 二级:电池簇CAN(500kbps,簇内通信)
  • 三级:系统级CAN(1Mbps,BMS到PCS、EMS)

这里有个坑:不同速率的总线之间必须加网关。我曾经见过有人直接把不同速率的CAN节点挂同一根线上,结果整个网络瘫痪。嗯,CAN控制器会检测到错误帧然后不断重发,形成总线风暴。

典型BMS报文定义示例:

// 电池电压报文
ID: 0x100 + 模组编号 (0-15)
数据: 8字节,每2字节一个电芯电压,单位mV
示例:0x100 发送 0x0BB8 0x0BC0 0x0BA8 0x0BB2
      → 电芯1: 3000mV, 电芯2: 3008mV, 电芯3: 2984mV, 电芯4: 2994mV

// 故障报警报文
ID: 0x001 (最高优先级)
数据: 第1字节故障类型,第2字节故障模组
示例:0x001 发送 0x01 0x03
      → 模组3发生过压故障

避坑指南:我曾经在项目里遇到一个诡异问题——BMS偶尔会误报绝缘故障。查了三天,最后发现是CAN总线上的某个节点在发送报文时,数据域的第7位刚好和绝缘检测仪器的报文ID冲突,导致数据被错误解析。从那以后,我要求所有BMS报文的数据域第一位必须固定为0xAA作为帧头校验。

最后说一句:CAN总线虽然可靠,但不是万能的。在强电磁干扰环境(比如大功率逆变器旁边),建议用CAN FD或者光纤转换。我见过一个光伏电站,CAN总线被逆变器的IGBT开关干扰得完全没法用,最后全部换成光纤环网才搞定。

CAN协议在BMS中的应用架构 物理层 CAN_H / CAN_L 差分信号 数据链路层 CAN 2.0A / 2.0B 帧格式 应用层 BMS自定义协议 BMU 电池管理单元 BCU 电池簇控制器 BAU 电池阵列控制器 CAN总线(500kbps) CAN报文示例: ID: 0x101 DLC: 8 数据: 0x0BB8 0x0BC0 0x0BA8 0x0BB2 → 模组1电芯电压: 3000mV, 3008mV, 2984mV, 2994mV ID: 0x001 DLC: 2 数据: 0x01 0x03 → 模组3发生过压故障(最高优先级) 仲裁机制:ID越小优先级越高,显性电平(0)覆盖隐性电平(1)

个人建议:刚开始做BMS通信设计时,先画好ID分配表。把紧急报警、控制指令、周期数据、诊断数据分区间管理。我习惯用0x000-0x0FF给报警,0x100-0x2FF给控制,0x300-0x7FF给数据。这样即使以后扩展,也不会乱。


公众号:蓝海资料掘金营,微信deep3321