3. CAN总线数据链路层:帧结构、仲裁、位填充与CRC校验

数据链路层,说白了就是CAN协议的核心战场。物理层只负责把0和1发出去,但怎么组帧、怎么抢总线、怎么保证数据不出错,全在这一层解决。我个人觉得,搞懂这一层,CAN通信就算入门一半了。

咱们直接切入正题,看看CAN帧到底长什么样。

3.1 CAN帧结构:标准帧 vs 扩展帧

CAN总线上一共就四种帧:数据帧、远程帧、错误帧、过载帧。平时打交道最多的就是数据帧。数据帧又分两种——标准帧和扩展帧。区别在哪?就在仲裁段的ID长度上。

对比项 标准帧 (CAN 2.0A) 扩展帧 (CAN 2.0B)
ID长度 11位 29位
IDE位 显性 (0) 隐性 (1)
帧头开销 较小 较大
应用场景 节点少、实时性高 多节点、复杂网络

标准帧的帧结构长这样:

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

扩展帧呢,多了一段18位的扩展ID:

SOF | 11位基础ID | SRR | IDE | 18位扩展ID | RTR | r1,r0 | DLC | 数据段 | CRC | ACK | EOF

关键点:标准帧和扩展帧可以在同一条总线上共存。但IDE位决定了帧类型——显性0表示标准帧,隐性1表示扩展帧。仲裁时,标准帧优先级永远高于扩展帧。

我在项目中遇到过一个问题:两个节点ID都是0x100,但一个是标准帧,一个是扩展帧。结果标准帧总是先发出去,扩展帧一直在等。后来查手册才发现,IDE位在仲裁时是显性优先,标准帧天生占便宜。

3.2 仲裁机制:CSMA/CA + AMP

CAN总线是多主从结构。任何节点都能随时发数据。那问题来了——两个节点同时发,谁先走?

答案是:谁ID小,谁先走。

CAN用的机制叫CSMA/CA + AMP(载波监听多路访问/冲突避免 + 逐位仲裁)。说白了就是:

  1. 先听后说:发之前先看看总线是不是空闲。
  2. 边说边听:发每一位的同时,监听总线电平。
  3. 输了就退:如果自己发的是隐性1,但总线上是显性0,说明有优先级更高的节点在发,自己立刻退出。

你想想看,这个过程有多快?逐位比较,11位ID比完,胜负已分。整个过程发生在微秒级别。

避坑指南:我曾经在一个项目中,把两个节点的ID设成了0x001和0x002。结果0x001的节点一直占着总线,0x002几乎发不出去。后来我把0x002的ID改成了0x100,才平衡了总线负载。记住:ID越小,优先级越高,别让一个节点把总线堵死。

仲裁的胜负规则很简单:

  • 显性位(0) > 隐性位(1)
  • ID数值越小,优先级越高
  • RTR位:数据帧(RTR=0) > 远程帧(RTR=1)
  • 标准帧(IDE=0) > 扩展帧(IDE=1)

3.3 位填充机制

嗯,这里要注意。CAN总线有个硬性要求:连续相同的位不能超过5个。

为什么?因为CAN节点是靠电平跳变来同步时钟的。如果连续发一堆相同的位,没有跳变,节点的时钟就会漂移,最后采样出错。

位填充的规则很简单:

  • 发送方:每检测到连续5个相同位,自动插入一个相反位
  • 接收方:收到连续5个相同位后,自动删除下一个位(填充位)

举个例子:

原始数据:11111 00000 11111
填充后:  111110 000001 111110

填充位用粗体标出来了。注意,填充位只在SOF到CRC段之间生效。ACK、EOF、IFS这些段不填充。

警告:位填充不是可选项,是强制要求。如果你用STM32的bxCAN外设,硬件会自动处理填充和去填充。但如果你用软件模拟CAN协议(比如用定时器+GPIO),位填充必须自己实现。我见过有人忘了做位填充,结果总线上一串0x00发出去,所有节点直接报错。

3.4 CRC校验

CAN帧的CRC段是15位,生成多项式是:

x¹⁵ + x¹⁴ + x¹⁰ + x⁸ + x⁷ + x⁴ + x³ + 1

CRC的计算范围包括:SOF、仲裁段、控制段、数据段。CRC段本身和后面的ACK、EOF不参与计算。

接收方收到帧后,用同样的多项式重新计算CRC。如果计算结果和收到的CRC一致,就发ACK;不一致,就发错误帧。

CRC参数 值
CRC长度 15位
生成多项式 0x4599 (x¹⁵+x¹⁴+x¹⁰+x⁸+x⁷+x⁴+x³+1)
计算范围 SOF ~ 数据段
检错能力 最多5位随机错误,或任意奇数位错误

实际经验:STM32的bxCAN外设,CRC计算是硬件自动完成的。你只需要把数据填进邮箱,硬件会算好CRC并附加到帧尾。但如果你用CAN FD或者自己写协议栈,CRC就得手算。我建议用查表法,别用逐位计算,效率差太多。

3.5 本章知识结构图

下面这张图把数据链路层的核心内容串起来了:

CAN数据链路层核心结构 CAN帧结构 标准帧 扩展帧 仲裁机制 CSMA/CA AMP逐位仲裁 位填充机制 5位相同插反位 接收端去填充 CRC校验(15位,多项式0x4599) 核心要点 ID越小优先级越高 | 连续5位相同必须填充 | CRC覆盖SOF~数据段

3.6 小结

数据链路层是CAN协议的灵魂。帧结构决定了数据怎么装,仲裁机制决定了谁先发,位填充保证了时钟同步,CRC校验保证了数据可靠。这四个东西加在一起,才让CAN总线在工业现场这么恶劣的环境下还能稳定工作。

我个人建议,学到这里先别急着写代码。拿逻辑分析仪抓一段CAN总线波形,对着帧结构图一个一个位去解析。我当年就是这么干的,抓了一下午波形,之后看CAN协议就跟看自己家冰箱说明书一样清楚。