第2章 CAN协议基础:帧格式、位填充与CRC校验

做CAN协议一致性测试,绕不开三个最基础的东西:帧长什么样、位怎么填充、CRC怎么算。这三件事看似独立,其实环环相扣。我最早接触CAN时,以为只要把帧格式背下来就行——后来被一个隐性错误狠狠上了一课,才发现位填充和CRC才是真正的“隐形杀手”。咱们按顺序拆开说。

2.1 帧格式——五种帧与一场“握手”

CAN总线上跑着五种帧:数据帧、远程帧、错误帧、过载帧、帧空间。测试中打交道最多的是数据帧和远程帧。

帧类型用途仲裁方式
数据帧发送节点把数据喂给总线ID越小优先级越高
远程帧请求某个ID的数据同样ID下,远程帧优先级低于数据帧
错误帧任何节点发现总线错误时主动发出固定高优先级
过载帧接收节点还没准备好接收下一帧插在帧间隔中
帧空间帧与帧之间的间隔,包括间歇场和总线空闲—

数据帧的结构我习惯分成七个段:SOF(起始帧)、仲裁域、控制域、数据域、CRC域、ACK域、EOF。标准帧11位ID,扩展帧29位ID。这里有个坑——一致性测试会专门检查CRC域是否包含了位填充后的数据,而不是原始位流。

经验之谈: 我用逻辑分析仪抓过一个“正常”的CAN帧,看着波形完美,但CRC校验一直报错。查了一下午才发现,发送节点的位填充算法在连续五个相同位后打了一个反向位,CRC计算器忘记把这个填充位纳入计算范围。协议规定:CRC计算基于“原始未填充位流”,但实际传输的是填充后的位流——测试仪器必须知道如何还原。

2.2 位填充——为什么需要它?又为什么头疼?

位填充的规则很简单:发送器检测到连续5个相同的位(00000或11111),就在第5位之后自动插入一个反向位。接收器看到5个相同位,就自动把第6位扔掉。

  • 规则:连续5个相同位 → 插入一个相反位
  • 作用:维持位同步,防止DC偏移
  • 例外:CRC界定符、ACK槽、ACK界定符、EOF不参与填充

我见过一个真实案例:某款MCU的CAN控制器实现有bug,位填充逻辑只在数据域的比特流里生效,却忘记了CRC域里的填充。结果总线上一出现奇偶性不好的CRC序列就丢帧。一致性测试里专门有一条:强制构造连续5个相同位的数据,观察CRC域是否也被正确填充。

避坑指南: 我曾经在实验室调一块板子,用示波器看帧尾的EOF段多了一个位。查半天发现是位填充计数器在CRC界定符之后没复位,把EOF前的CRC序列多算了一位。ISO 11898-1规定:位填充在CRC界定符之后立即停止,这之后所有位(包括ACK、EOF)都不再填充。如果你的控制器在EOF上还做填充,那一定不合格。

2.3 CRC校验——算法逻辑与测试陷阱

CAN的CRC是个15位的生成多项式:x¹⁵ + x¹⁴ + x¹⁰ + x⁸ + x⁷ + x⁴ + x³ + 1。计算范围从SOF到数据域结束,不包括位填充位。我刚开始做测试时总觉得CRC既然有硬件算,就不用管细节。直到有一次误码率测试不过,才发现CRC的校验范围没搞清楚——数据域之后的CRC序列本身是不参与CRC计算的。

计算步骤:

  1. 初始化CRC寄存器为全0
  2. 对SOF到数据域结束的每个位(不含填充位),逐位移入寄存器
  3. 最后得到15位CRC序列,填入CRC域
  4. 接收器用同样的多项式计算,若结果等于0,说明无误

多项式对应的二进制表示为:1 1000 0100 1100 1001(0x4599)。注意,这个15位数据在总线上传输时是高字节在前,还是低字节在前?我见过不少工程师搞反了——CAN协议规定:CRC序列的发送顺序是MSB first。

特性数值
生成多项式0x4599(15位)
CRC序列长度15位
校验范围SOF + 仲裁域 + 控制域 + 数据域(不含填充位)
发送顺序MSB first
余数检测接收方CRC寄存器最终应为0

一致性测试里有一个经典案例:构造一个包含突发错误的帧,观察DUT能否正确报出CRC错误。测试仪器会在CRC域之前或之后的某个位置注入一个位翻转,然后检查DUT的ERR引脚或状态寄存器。我用这个方法查出过两个不同供应商的CAN控制器都有同样的bug:它们把ACK slot也纳入了CRC计算范围。

核心要点: CAN的CRC校验范围不包含位填充位,也不包含CRC本身、ACK、EOF。一旦你搞混了这个范围,所有后续的误码率测试都是白做。我自己设计校验工具时,每次都先用一个已知的黄金帧验证CRC计算器是否正确。

2.4 本章知识结构——三件事怎么串起来?

下面这张图展示了帧格式、位填充与CRC的交互关系。注意CRC计算发生在位填充之前,而最终在总线上传输的是填充后的位流。这就是很多测试出问题的根源。

CAN数据帧生成与校验流程 发送端:原始数据 组装帧 (SOF ~ 数据域) 计算CRC(不含填充位) 位填充(含CRC域) 发送到总线 接收端:总线数据 去填充(恢复原始位流) 提取CRC并与本地计算比较 若结果≠0 → 错误帧 物理总线传输(带填充的位流) 关键差异 • CRC计算用原始位流 • 总线传输用填充位流 • 接收端先解填充再校验CRC • 测试仪器必须能独立模拟这个过程

2.5 一致性测试中的常见陷阱与实操建议

做一致性测试时,很多工程师只关心“帧能不能被解析”,忽略了位填充和CRC的边界情况。我总结几个高频出错点:

  • 位填充后CRC计算错误: 测试仪器模拟发送时,如果没在CRC域之后关闭位填充,会导致EOF提前或延后。ISO 11898-1明确说:CRC界定符之后位填充停止,但有些老款控制器在ACK段仍会尝试填充——直接报错。
  • CRC边界条件: 当数据域长度=0时(远程帧),CRC计算范围如何?标准答案:只有SOF + ID + RTR + 控制域。我曾经遇到一个DUT在RTR=1时把数据域占位也纳入了CRC,导致误码。
  • 远程帧与数据帧的区分: 远程帧无数据域,但CRC仍然计算。有些廉价CAN接口芯片在远程帧上漏掉了CRC,导致接收端永远回复错误帧。
  • 填充位对同步抖动的影响: 我在做EMC测试时发现,连续填充位越多,位定时恢复电路的误差越大。一致性测试会构造“65个连续显性位”(最坏情况),看DUT能否保持同步不掉帧。
测试小技巧: 我习惯用两个脚本——一个用纯软件模拟CAN协议栈,独立计算CRC和位填充;另一个用被测控制器的硬件发送数据,然后比对两者结果。几乎每次都能发现硬件与软件之间的微小差异。另外,别忘了测试总线空闲时的隐性电平连续15位以上,这是确认软件/硬件是否能正确退出接收状态的关键用例。