2. CAN数据链路层:帧结构与位填充

数据链路层,说白了就是CAN协议最核心的部分。你想想看,物理层只管把0和1发到总线上,但怎么区分哪段是地址、哪段是数据、哪段是校验?这就是数据链路层干的事。

我个人习惯把CAN帧结构比作一个快递包裹——有收件人地址、有内容、有签名确认。今天我们就把它拆开,看看每一部分到底长什么样。

2.1 CAN帧的完整结构

先看一张总览图,把整个帧结构印在脑子里。

CAN标准数据帧结构(11位ID) SOF 仲裁场 (11位ID+RTR) 控制场 (IDE+r0+DLC) 数据场 (0~8字节) CRC场 (15位+定界符) ACK场 (2位) EOF (7位隐性) 关键说明: • 标准帧总位数:1 + 12 + 6 + 0~64 + 16 + 2 + 7 = 44~108位(不含位填充) • 扩展帧(29位ID)仲裁场不同,其余字段相同 • 帧起始(SOF)为1位显性位,标志总线从空闲变为忙碌 • 帧结束(EOF)为7位隐性位,之后总线进入间歇场(ITM)

这张图把标准数据帧的7个主要字段都标出来了。我刚开始学CAN的时候,就是靠这张图反复对照,才把帧结构记牢的。

2.2 逐字段拆解

2.2.1 SOF(帧起始)

SOF只有1位,必须是显性(逻辑0)。它干的事很简单——告诉总线上所有节点:「我要发数据了,大家准备好!」

总线空闲时是隐性电平(逻辑1)。SOF这一位显性,相当于一个「唤醒信号」。我记得有一次调试,发现某个节点总是不响应,查了半天,原来是它的SOF检测电路有问题,把显性位当成了噪声滤掉了。

2.2.2 仲裁场

这是CAN协议最精彩的部分。仲裁场包含:

  • 标识符(ID):标准帧11位,扩展帧29位
  • RTR位:远程帧请求位(0=数据帧,1=远程帧)

仲裁的原理很简单:谁ID小,谁优先级高。多个节点同时发送时,通过「线与」机制逐位比较,ID小的节点自动获胜。

实战经验:我建议你把关键报文(如刹车、转向)分配小ID(如0x001~0x100),把诊断、配置类报文分配大ID(如0x600~0x7FF)。这样能保证实时性。

扩展帧的仲裁场多了18位扩展ID,结构如下:

字段 位数 说明
基础ID 11位 与标准帧相同
SRR位 1位 替代远程请求位(隐性)
IDE位 1位 标识符扩展位(1=扩展帧)
扩展ID 18位 ID的18~28位
RTR位 1位 远程帧请求

2.2.3 控制场

控制场共6位,结构如下:

  • IDE位(1位):标识符扩展位。标准帧=0,扩展帧=1
  • r0位(1位):保留位,必须为显性
  • DLC(4位):数据长度码,表示数据场有多少字节

DLC的取值范围是0~8,对应数据场0~8字节。注意,DLC=0也是合法的——远程帧就没有数据场。

避坑指南:我曾经遇到过一个问题:某个节点发送DLC=8,但实际数据场只填了4个有效字节。接收方读到了后面4个随机值,导致误动作。记住:DLC必须与实际数据长度一致,不要多填。

2.2.4 数据场

数据场长度0~8字节,是帧里唯一承载用户数据的地方。发送什么、怎么解析,完全由上层协议决定。

我个人习惯把数据场的字节分配做成一张表,贴在项目文档里。比如:

字节 信号 范围 分辨率
Byte0 车速 0~250 km/h 1 km/h
Byte1 发动机转速 0~8000 rpm 0.125 rpm
Byte2~3 油门踏板位置 0~100% 0.1%

这样设计,解析代码写起来特别清晰,调试也方便。

2.2.5 CRC场

CRC场共16位,包含:

  • CRC序列(15位):对SOF、仲裁场、控制场、数据场进行CRC计算
  • CRC定界符(1位):隐性位,用于分隔

CRC的生成多项式是:x¹⁵ + x¹⁴ + x¹⁰ + x⁸ + x⁷ + x⁴ + x³ + 1。你不需要背这个多项式,但要知道它的检错能力——能检测出所有≤5位的突发错误,以及99.998%的其他错误。

关键点:CRC计算覆盖的范围是从SOF到数据场结束,不包括CRC场本身和ACK场。接收节点会重新计算CRC,与发送节点附带的CRC序列比对。不一致就说明帧被破坏了。

2.2.6 ACK场

ACK场只有2位:

  • ACK槽(1位):发送节点发隐性位,接收节点如果正确收到,就拉成显性
  • ACK定界符(1位):隐性位

这个机制很有意思。发送节点发出ACK槽位后,会监听总线。如果总线上是显性,说明至少有一个节点正确收到了;如果还是隐性,说明没人收到——发送节点会重发。

你想想看,这相当于一个「集体确认」机制。不需要每个节点单独回复ACK,效率很高。

2.2.7 EOF(帧结束)

EOF共7位,全部为隐性位。它的作用是让总线恢复隐性状态,为下一帧做准备。

EOF之后,总线进入间歇场(ITM,3位隐性),然后才是总线空闲。这个间歇场是协议规定的「冷却时间」,防止连续帧之间产生干扰。

2.3 位填充机制

位填充是CAN协议里一个容易被忽视、但极其重要的机制。它的规则很简单:

位填充规则:发送节点在连续发送5个相同电平的位之后,自动插入1个相反电平的位。接收节点在收到5个相同电平后,自动丢弃后面的第6位(填充位)。

为什么要这么做?两个原因:

  1. 保证时钟同步:CAN总线没有单独的时钟线,节点靠电平跳变来同步时钟。如果长时间没有跳变,时钟就会漂移。
  2. 防止误判:连续的隐性位可能被误认为总线空闲。

举个例子:假设数据场里有连续6个1(隐性位),发送时会在第5个1后面插入1个0(显性位)。接收方看到5个1后,知道第6位是填充位,直接丢弃。

实战经验:我曾经调试过一个CAN通信不稳定的问题。用示波器抓波形,发现总线上有一段特别长的隐性电平。后来查出来,是某个节点的位填充逻辑有bug,在连续5个隐性位后没有插入显性位,导致其他节点时钟失步。从那以后,我每次做CAN测试,都会特意构造一些「极端数据」来验证位填充逻辑。

位填充覆盖的范围是:从SOF到CRC场结束(包括CRC定界符)。ACK场和EOF不参与位填充。

这意味着,一个标准数据帧的实际位数不是固定的。如果数据场全是0x00(8个0),位填充会频繁插入1;如果数据场全是0xFF(8个1),也会频繁插入0。最坏情况下,帧长度会增加约20%。

2.4 帧类型小结

CAN协议定义了4种帧类型,我们这章重点讲的是数据帧。其他三种简单提一下:

帧类型 用途 有无数据场
数据帧 发送数据 有(0~8字节)
远程帧 请求数据 无
错误帧 报告总线错误 无
过载帧 请求延迟发送 无

远程帧的帧结构与数据帧几乎一样,区别在于RTR位=1,且没有数据场。错误帧和过载帧我们后面章节再细讲。

2.5 本章要点回顾

  • CAN标准数据帧由SOF、仲裁场、控制场、数据场、CRC场、ACK场、EOF共7个字段组成
  • 仲裁场通过ID逐位仲裁,ID越小优先级越高
  • 数据场最多8字节,DLC必须与实际数据长度一致
  • CRC场提供强大的检错能力,覆盖SOF到数据场
  • ACK场实现集体确认,不需要单独回复
  • 位填充保证时钟同步,每5个相同电平后插入1个相反电平

嗯,帧结构这块内容比较多,但它是CAN协议的基础。你把这些字段的位宽、作用、顺序记清楚,后面学CAN的收发器、控制器、应用层协议都会轻松很多。