4. CAN通信协议详解:CAN 2.0B帧结构、总线电平、位时序与错误处理

各位好,我是你们的BMS硬件讲师。今天咱们来啃一块硬骨头——CAN通信协议。说实话,很多工程师做BMS项目,MCU和AFE之间的SPI隔离搞得很溜,但一到CAN总线就发怵。其实CAN协议没那么玄乎,咱们把它拆开揉碎了讲。

我个人习惯把CAN协议分成四个维度去理解:帧结构(数据长什么样)、物理层电平(信号怎么在线上跑)、位时序(时钟怎么对齐)、错误处理(出错了怎么办)。把这四个点吃透,CAN通信这块你就拿下了。

CAN通信协议知识体系 CAN 2.0B 协议 帧结构 总线电平与差分信号 位时序与同步 错误处理机制 SOF 仲裁场 数据场 CRC CAN_H / CAN_L 显性/隐性电平 Tq / 采样点 硬同步/重同步 错误帧 错误计数器 Bus Off 状态

4.1 CAN 2.0B帧结构——数据到底长什么样?

CAN总线上一共跑四种帧:数据帧、远程帧、错误帧、过载帧。咱们BMS里用得最多的是数据帧,我就拿它开刀。

一个标准数据帧(CAN 2.0A)有11位ID,扩展帧(CAN 2.0B)有29位ID。BMS里我建议统一用扩展帧,为什么?因为电池包里的节点太多了——总压、单体电压、温度、绝缘电阻、电流,11位ID根本不够分。

来看数据帧的结构,我把它分成七个段:

段名称位数说明
SOF(帧起始)1显性电平,同步所有节点
仲裁场12或32ID + RTR位,决定谁抢到总线
控制场6IDE + r0 + DLC(数据长度码)
数据场0~64实际数据,BMS里通常是8字节
CRC场1615位CRC + 1位CRC界定符
ACK场2接收节点应答
EOF(帧结束)7隐性电平,帧结束标志

重点:仲裁场的工作原理

CAN总线用"线与"逻辑。显性电平(0)会覆盖隐性电平(1)。多个节点同时发送时,ID小的节点会在仲裁场胜出。我见过不少新手把ID设成0x000,结果这个节点永远抢不到总线——因为ID越小优先级越高,0x000是最高优先级,但如果你设成0x7FF,那基本就是"万年老二"。

数据场最多8字节,为什么是8?这是CAN协议诞生时定的,考虑到实时性和总线负载的平衡。BMS里一个典型的数据帧长这样:

// BMS典型CAN数据帧示例(扩展帧29位ID)
// ID: 0x18FF50E1 (优先级6, PGN=FF50, SA=E1)
// DLC: 8
// Data: [0x12, 0x34, 0x56, 0x78, 0x9A, 0xBC, 0xDE, 0xF0]
// 含义:总电压 = 0x1234 * 0.1V = 466.0V
//       总电流 = 0x5678 * 0.1A = 2213.6A
//        SOC    = 0x9A = 154% (嗯,这数据有问题,后面讲错误处理)

我的经验:BMS里数据场最好固定用8字节,哪怕只传1个字节也填满。为什么?因为很多CAN分析仪和记录仪对DLC变化敏感,固定长度能减少解析出错的可能。

4.2 CAN总线电平与差分信号——物理层那些事

CAN总线用两根线:CAN_H 和 CAN_L。它不靠对地电压传信号,而是靠两根线的电压差。

这就是差分信号的精髓:抗干扰能力强。你想想看,电机驱动器一启动,地线上能蹦出几十伏的噪声,但CAN_H和CAN_L同时被干扰,差值基本不变。

状态CAN_H电压CAN_L电压差分电压逻辑值
显性(Dominant)3.5V1.5V2.0V0
隐性(Recessive)2.5V2.5V0V1

注意:这是ISO 11898-2高速CAN的标准。还有一种低速CAN(ISO 11898-3),电平不一样,但BMS里基本都用高速CAN,速率125k~1Mbps。

重要提醒:终端电阻

CAN总线两端必须各接一个120Ω电阻。没有终端电阻,信号会反射,波形畸变,通信出错。我遇到过最离谱的案例——某供应商把终端电阻焊在PCB上,但没跳线,结果总线怎么都跑不上500k。查了两天,最后发现是电阻没接入网络。

4.3 CAN位时序与同步——时钟是怎么对齐的?

CAN总线没有单独的时钟线。所有节点靠"位同步"来对齐时钟。说白了,就是每个节点用自己的晶振,但通过总线上的电平跳变来校准。

一个CAN位被分成四个段:

  • 同步段(Sync_Seg):1个Tq,用于检测跳变沿
  • 传播段(Prop_Seg):1~8个Tq,补偿物理延迟
  • 相位缓冲段1(Phase_Seg1):1~8个Tq,采样点前调整
  • 相位缓冲段2(Phase_Seg2):1~8个Tq,采样点后调整

采样点通常在Phase_Seg1和Phase_Seg2之间。我个人习惯把采样点设在87.5%的位置,也就是:Sync_Seg + Prop_Seg + Phase_Seg1 占总位时间的87.5%。

位时间计算公式:

位时间 = (Sync_Seg + Prop_Seg + Phase_Seg1 + Phase_Seg2) × Tq

波特率 = 1 / 位时间

例如:Tq = 125ns,Sync=1,Prop=2,PS1=3,PS2=2,总Tq数=8

位时间 = 8 × 125ns = 1μs,波特率 = 1Mbps

同步有两种:硬同步和重同步。硬同步发生在SOF的下降沿,所有节点强制对齐。重同步发生在后续的跳变沿,通过调整Phase_Seg1和Phase_Seg2的长度来补偿时钟偏差。

避坑指南:我曾经在一个项目里把采样点设在50%,结果总线长度超过50米后,通信时好时坏。后来把采样点调到80%以上,问题解决。记住:总线越长,传播延迟越大,采样点越要靠后。

4.4 CAN错误处理机制——出错了怎么办?

CAN协议最牛的地方就是它的错误处理。它不像UART那样丢数据,也不像I2C那样死锁。CAN有五种错误检测机制:

  1. 位错误:发送节点监控总线,发现发送的电平和总线上的不一样
  2. 填充错误:连续5个相同电平后,第6位必须是相反电平(位填充规则)
  3. CRC错误:接收节点计算的CRC和发送的不一致
  4. 格式错误:固定格式的位段(如CRC界定符、EOF)出现错误电平
  5. ACK错误:发送节点在ACK槽没收到显性应答

任何一个节点检测到错误,就会发送错误帧——6个连续的显性位,强制终止当前帧。然后所有节点丢弃这个帧,发送节点自动重发。

每个节点有两个错误计数器:发送错误计数器(TEC)和接收错误计数器(REC)。根据计数值,节点有三种状态:

状态条件行为
错误主动TEC < 127 且 REC < 127正常通信,检测到错误时发送主动错误帧
错误被动TEC > 127 或 REC > 127只能发送被动错误帧,且发送前要等待8个隐性位
Bus OffTEC > 255完全断开总线,不参与任何通信

Bus Off有多可怕?

BMS里如果CAN控制器进入Bus Off,整个电池包就和整车失联了。我遇到过一台车,BMS频繁进入Bus Off,查到最后是CAN收发器的共模电压出了问题——电池包高压侧和低压侧的地电位差太大,导致CAN_H和CAN_L的共模电压超出收发器范围。

解决方案:在CAN隔离芯片的二次侧加共模扼流圈,并确保隔离电源的稳定性。

4.5 小结——CAN协议在BMS中的实战要点

讲了这么多,我总结几条BMS里用CAN的实战经验:

  • 帧结构:用扩展帧(29位ID),数据场固定8字节,DLC写8
  • 总线电平:高速CAN,终端电阻必须接,共模电压不能超过±12V
  • 位时序:采样点设在80%~87.5%,总线越长采样点越靠后
  • 错误处理:监控TEC和REC,Bus Off后要有恢复机制(通常128个11个隐性位后自动恢复)