2. CAN总线协议回顾:CAN帧结构、ID仲裁、位填充与错误帧机制

好,咱们直接进入正题。做CANalyzer的故障注入,你得先懂CAN协议本身。不然你注入的故障是啥、会产生什么后果,你心里没底。这一节,我把CAN协议里最核心的几个点给你捋一遍。

2.1 CAN帧结构:数据是怎么装进“信封”的?

CAN总线上的数据,是以“帧”为单位传输的。你可以把帧想象成一个信封。信封上写着收件人地址、发件人信息,里面装着你要寄的东西。

CAN帧分好几种,最常见的是数据帧远程帧。咱们做测试,打交道最多的就是数据帧。

一个标准的数据帧(CAN 2.0A),长这样:

SOF | 11位ID | RTR | IDE | r0 | DLC | 数据段(0-8字节) | CRC | ACK | EOF

我来拆开说:

  • SOF (Start of Frame):帧起始,一个显性位。告诉总线上所有节点:“我要发数据了!”
  • ID (Identifier):标识符,11位。这是CAN协议的灵魂。它决定了消息的优先级,也决定了谁能抢到总线。
  • RTR (Remote Transmission Request):远程帧标志。显性表示数据帧,隐性表示远程帧。我平时很少用远程帧,但你要知道它存在。
  • IDE (Identifier Extension):扩展标志。显性表示标准帧,隐性表示扩展帧(CAN 2.0B,29位ID)。
  • r0:保留位,显性。
  • DLC (Data Length Code):数据长度码,4位。表示数据段有几个字节。范围0-8。
  • 数据段:0到8个字节。这就是你真正要传的数据。
  • CRC (Cyclic Redundancy Check):循环冗余校验,15位。用来检查数据在传输过程中有没有出错。
  • ACK (Acknowledge):应答位。发送节点发隐性位,接收节点如果正确收到,就拉成显性位。这是CAN总线“一应答多”的机制。
  • EOF (End of Frame):帧结束,7个隐性位。

重点:ID和DLC是我们在CANalyzer里最常修改的字段。做故障注入时,改ID可以模拟不同优先级节点的行为;改DLC可以制造长度异常。

2.2 ID仲裁:谁先说话,谁说了算?

CAN总线是“多主”总线。什么意思?就是所有节点都能同时发数据。那冲突了怎么办?

靠ID仲裁。

我打个比方。你想想看,一群人在一个房间里同时说话,谁声音大谁就能被听到。CAN总线也是这个道理。不过它比的不是声音大小,而是ID的二进制值

规则很简单:ID值越小,优先级越高

具体怎么仲裁的?

  1. 所有想发数据的节点,同时从SOF开始往总线上发数据。
  2. 每个节点在发送ID的每一位时,都会监听总线电平。
  3. 如果自己发的是隐性位(1),但总线上是显性位(0),说明有更高优先级的节点在发数据。自己立刻停止发送,转为接收。
  4. 谁先发完整个ID,谁就赢得了仲裁,继续发后面的数据。

举个例子:

  • 节点A的ID是 0x100(二进制:001 0000 0000)
  • 节点B的ID是 0x200(二进制:010 0000 0000)

从最高位开始比。第1位,A发0,B发0,一样。第2位,A发0,B发1。A是显性(0),B是隐性(1)。A赢了,B退出。

实战技巧:我在做ECU测试时,经常故意把测试设备的ID设得很小(比如0x001),这样它总能抢到总线。但要注意,别把真正的关键消息(比如刹车信号)给堵了。我曾经在一次测试中,就因为ID设得太小,把ABS控制器的消息给延迟了,差点出问题。

2.3 位填充:为什么CAN要“插队”?

位填充,是CAN协议里一个容易被忽略但极其重要的机制。

它的目的是什么?保证时钟同步

CAN总线没有单独的时钟线。所有节点靠数据信号上的跳变沿来同步时钟。如果总线上长时间没有跳变(比如连续发5个相同的位),节点的时钟就会漂移,导致采样错误。

所以协议规定:在发送连续5个相同位之后,必须插入一个相反位

举个例子:

你要发送的数据是:11111 00000

经过位填充后变成:111110 000001

接收节点收到后,会自动把填充位去掉,还原原始数据。

注意:位填充覆盖的范围是SOF到CRC之前。CRC段、ACK段、EOF段不进行位填充。这个细节在做故障注入时很重要——你如果往CRC段里塞填充位,接收节点会直接报错。

位填充还有一个副作用:它会让实际传输的位数变长。你想想看,如果数据里全是0或全是1,那填充位会很多。极端情况下,一个标准数据帧可能从47位膨胀到100多位。这也是为什么CAN总线的实际带宽利用率不是100%。

2.4 错误帧机制:CAN怎么“自我修复”?

CAN总线的一大优点就是可靠性高。这得益于它完善的错误检测和处理机制。

CAN定义了5种错误:

错误类型 检测位置 说明
位错误 发送节点 发送的位与监听到的位不一致(仲裁期间除外)
填充错误 所有节点 检测到连续6个相同位,违反位填充规则
CRC错误 接收节点 计算出的CRC与接收到的CRC不匹配
格式错误 所有节点 检测到固定格式位(如EOF、ACK)的电平错误
ACK错误 发送节点 发送节点在ACK槽没有检测到显性位

任何一个节点检测到错误,就会立刻发送错误帧

错误帧长什么样?

6个显性位(错误标志) | 8个隐性位(错误界定符)

发送错误帧的节点,会主动拉6个显性位。这6个显性位会破坏当前正在传输的帧,让所有节点都发现总线出错了。然后所有节点都丢弃当前帧,等待总线空闲后重新发送。

关键点:错误帧的6个显性位,本身也违反了位填充规则(连续6个相同位)。这会导致其他节点也检测到填充错误,进而发送自己的错误帧。结果就是总线上出现一连串的错误帧,形成“错误风暴”。

CAN控制器内部还有两个计数器:发送错误计数器(TEC)接收错误计数器(REC)。根据这两个计数器的值,节点会处于三种状态之一:

  • 错误主动(Error Active):TEC < 128,REC < 128。节点正常通信,检测到错误时发送主动错误帧(6个显性位)。
  • 错误被动(Error Passive):TEC >= 128 或 REC >= 128。节点可以通信,但检测到错误时只能发送被动错误帧(6个隐性位)。
  • 总线关闭(Bus Off):TEC > 255。节点完全退出总线,不再参与任何通信。

避坑指南:我曾经在测试一个BMS(电池管理系统)时,因为CAN收发器硬件问题,导致节点频繁发送错误帧。TEC很快飙升到255,节点直接总线关闭了。整个BMS与VCU失联,车辆报“高压系统故障”。排查了好久才发现是硬件问题。所以,做故障注入时,一定要监控TEC和REC的值,它们能告诉你节点是不是快“撑不住”了。

2.5 小结:这些知识怎么用在CANalyzer里?

好了,CAN协议的核心机制就这些。你可能会问:“这些跟CANalyzer故障注入有什么关系?”

关系大了去了。

  • 帧结构:你可以在CANalyzer里手动构造一个帧,修改ID、DLC、数据段,甚至故意破坏CRC。这就是最基础的故障注入。
  • ID仲裁:你可以通过修改ID,模拟高优先级消息抢占总线,或者让低优先级消息永远发不出去。
  • 位填充:你可以故意制造连续6个相同位,触发填充错误。看看ECU会怎么反应。
  • 错误帧机制:你可以用CANalyzer的“错误帧生成器”主动发送错误帧,测试ECU的错误处理能力。或者通过大量注入错误,把节点逼到“总线关闭”状态。

后面的章节,我会带你一个一个动手做。咱们先从最简单的开始:用CANalyzer的“CAN IG”模块,手动构造一个带CRC错误的帧,看看ECU会不会报错。