3. CAN总线数据链路层:帧结构与帧类型
数据链路层,说白了就是CAN协议最核心的“语法规则”。物理层只管把0和1发到总线上,但这一层才决定了这些0和1到底是什么意思。我刚开始接触CAN时,总觉得帧结构很枯燥,直到有一次在调试一个电机控制器时,发现总线莫名其妙挂死——后来查出来是ACK场出了问题。从那以后,我对每一帧的每个位都不敢马虎。
3.1 CAN帧结构:从SOF到EOF
一个标准的CAN数据帧,由7个不同的场组成。我习惯把它想象成一封挂号信:有信封、有地址、有内容、还有回执。下面这张图可以帮你快速建立整体印象。
3.2 各场详解
3.2.1 SOF(帧起始)
SOF就是一个显性位(逻辑0)。它告诉总线上所有节点:“我要开始发数据了”。总线空闲时是隐性电平,SOF这个显性位一出现,所有节点都会同步到这一时刻。嗯,这里要注意:SOF也是硬同步的触发点,所有节点的时钟都会在SOF的下降沿重新对齐。
3.2.2 仲裁场
仲裁场包含标识符(ID)和RTR位。标准帧是11位ID,扩展帧是29位ID。ID越小,优先级越高。为什么?因为显性位(0)会覆盖隐性位(1)。
仲裁机制的核心逻辑:
- 多个节点同时发送时,从ID的最高位开始逐位比较
- 谁先发出显性位(0),谁就赢得总线
- 输的节点自动转为接收模式,不破坏总线数据
我在一个项目里遇到过这样的坑:两个ECU的ID设成了0x123和0x124,结果0x123总是抢到总线,0x124的报文经常发不出去。后来我们重新规划了ID分配策略,把紧急报文放在小ID上。
3.2.3 控制场
控制场包含IDE位、保留位r0和DLC(数据长度码)。DLC用4位表示数据场有多少字节,范围是0~8。IDE位用来区分是标准帧还是扩展帧——标准帧的IDE是显性,扩展帧是隐性。
| DLC值 | 数据字节数 | 常见用途 |
|---|---|---|
| 0 | 0字节 | 远程帧请求 |
| 1~7 | 1~7字节 | 短数据包 |
| 8 | 8字节 | 标准数据包 |
3.2.4 数据场
数据场就是真正要传输的数据,最多8个字节。为什么是8字节?这是早期设计时的一个权衡——既要保证实时性,又要能传输足够的信息。说实话,在现在的车载网络中,8字节有时候真不够用,所以后来才有了CAN FD(后面章节会讲)。
3.2.5 CRC场
CRC场包含15位CRC校验码和1位隐性定界符。发送节点计算CRC,接收节点重新计算并比对。如果不一致,接收节点会发送错误帧。
我个人的经验:CRC校验是发现总线噪声干扰的第一道防线。在实验室里可能很少触发,但上了实车,尤其是靠近电机控制器的地方,CRC错误会明显增多。我曾经在一条CAN线上加了铁氧体磁环,CRC错误率从千分之三降到了几乎为零。
3.2.6 ACK场
ACK场只有2位:ACK槽和ACK定界符。发送节点在ACK槽发送隐性位,任何正确接收到帧的节点,都会在ACK槽把总线拉成显性。如果发送节点没检测到显性,就知道没人收到——它会重发。
注意:ACK场是“线与”逻辑。只要有一个节点正确接收,ACK槽就是显性。但这也意味着:如果总线上只有一个节点(比如调试时只接了一个ECU),它自己发自己收,ACK永远成功——这可能会掩盖一些接收问题。
3.2.7 EOF(帧结束)
EOF是7个连续的隐性位。为什么是7位?这是为了和错误帧的6个显性位区分开。如果接收节点检测到EOF不是7个隐性位,就会认为总线有错误。
3.3 帧类型
CAN总线有4种帧类型。我经常把它们分成两组:数据帧和远程帧是“正常通信”,错误帧和过载帧是“异常处理”。
| 帧类型 | 用途 | 关键特征 |
|---|---|---|
| 数据帧 | 发送数据 | 包含数据场,RTR=0 |
| 远程帧 | 请求数据 | 无数据场,RTR=1 |
| 错误帧 | 报告总线错误 | 6个显性位+8个隐性位 |
| 过载帧 | 请求延迟 | 与错误帧结构类似 |
3.3.1 数据帧
数据帧就是最常用的帧类型。它携带数据从发送节点到接收节点。标准帧和扩展帧的区别主要在ID长度上。
3.3.2 远程帧
远程帧没有数据场。它的作用是:一个节点向另一个节点请求数据。比如,发动机ECU可以发一个远程帧请求车速传感器的数据。收到远程帧的节点,会发一个同ID的数据帧作为响应。
我记得有一次,一个同事发现总线负载率异常高,查了半天才发现是某个节点在不停地发远程帧,而对应的节点根本没响应——结果就是远程帧一直重发,把总线塞满了。
3.3.3 错误帧
错误帧由6个显性位和8个隐性位组成。任何节点检测到总线错误,都会立即发送错误帧。错误帧会破坏当前正在传输的帧,迫使发送节点重发。
错误帧触发的条件:
- 位错误:发送节点发现总线电平与自己发送的不一致
- 填充错误:连续6个相同电平(违反位填充规则)
- CRC错误:接收节点计算的CRC与发送的不一致
- 形式错误:固定格式的位(如EOF)不是预期值
- ACK错误:发送节点没检测到ACK显性位
3.3.4 过载帧
过载帧和错误帧结构很像,但用途完全不同。过载帧是接收节点主动发出的,意思是:“我还没准备好,请等一下”。过载帧通常在两种情况下触发:
- 接收节点内部缓冲区满了,需要时间处理
- 总线空闲时,接收节点检测到显性位(可能是噪声)
说实话,过载帧在实际车载网络中很少见。现在的CAN控制器都有足够的硬件缓冲,很少会因为软件来不及处理而主动发过载帧。如果你在总线上频繁看到过载帧,那大概率是硬件设计有问题。
3.4 位填充机制
CAN总线有一个很有意思的规则:连续发送5个相同电平后,必须插入一个相反电平。这就是位填充。从SOF到CRC场结束,所有位都受这个规则约束。
为什么要这么做?说白了就是为了保证时钟同步。如果没有位填充,连续十几个隐性位,接收节点的时钟可能就漂走了。插入一个翻转电平,相当于给接收节点一个重新同步的机会。
避坑指南:我曾经在写CAN驱动时,自己组帧发送,忘了在位填充后重新计算CRC。结果发出去的帧总是被对方拒绝。查了两天才发现——CRC是在位填充之前计算的,而不是之后。这个顺序千万别搞反。
3.5 本章小结
CAN帧结构是理解整个CAN协议的基础。SOF负责同步,仲裁场决定优先级,数据场承载信息,CRC场保证完整性,ACK场确认接收,EOF标记结束。四种帧类型各司其职,共同构成了一个可靠、实时的通信系统。
我个人觉得,理解CAN帧结构最好的方法就是拿一个CAN分析仪,抓一段真实的CAN总线数据,然后一帧一帧地解析。看着示波器上的波形和协议分析器上的字段一一对应,那种感觉比看十遍书都管用。