上期杂谈简单讲了讲CAN的物理层定义,即高低差分信号的电平定义。这次主要来聊聊CAN的数据链路层。
帧定义
在介绍CAN总线的帧定义之前,首先引入了各种“场”的概念,如ACK场等。这些“场”定义了CAN总线帧的各个部分,将这些场按规定的顺序排列起来,就构成了CAN的帧定义。
SOF:帧起始,帧的起始部分,由一个显性位组成,用于标识帧的开始。仲裁场:用于标识帧的优先级,由ID和RTR组成。控制场:用于标识帧的类型,由IDE(标准帧) /r1(扩展帧)、r0和DLC组成。数据场:用于存放帧的数据,由D0到D7组成。CRC场:用于检测帧的完整性,由CRC和DEL组成。ACK场:用于确认帧的接收,由ACK和DEL组成。EOF:帧结束,帧的结束部分,由7个隐性位组成,用于标识帧的结束。ITM:插入延迟,用于插入延迟,由3个隐性位组成。
仲裁场
仲裁场由ID和RTR组成,其中ID用于标识帧的优先级,RTR用于标识帧的类型。ID由11位或29位组成,RTR为1位数据。RTR字段为0时,表示数据帧,RTR字段为1时,表示遥控帧。
仲裁机制
ID越小的报文的优先级越高。当多个报文同时发送时,ID小的报文优先发送。当ID相同时,RTR字段为0的报文的优先级高于RTR字段为1的报文。
其原理是由于总线在空闲状态处于1,因此显性状态0可以把隐性状态1覆盖,节点id一旦被覆盖则进入只听状态。
控制场
控制场由IDE(标准帧) / r1(扩展帧)、r0和DLC组成。IDE字段为0时,表示标准帧,IDE字段为1时,即r1,表示扩展帧。
r0位为保留位,一般置为0,即显性电平。
数据场
顾名思义,此处存放的是需要传输的数据。它由8个字节组成,每。数据场的长度由控制场的DLC字段决定,最大长度为8个字节。在一个字节中,CAN和I2C一样,高位在前,低位在后。而整个数据场内,CAN的字节顺序是低位在前,高位在后。
(使用draw.io绘制,下文同)
CRC场
CRC场由CRC和DEL组成。
CRC为15位循环冗余校验码。
CRC校验
首先将从SOF到数据场的所有位与CRC的最高位依次进行异或运算,每次运算CRC都右移1位,最高到15位为止。如果异或结果为1,则让CRC当前值与CRC-15多项式:1100 0101 1001 1001即0xC599再进行一次异或运算。把CRC场前的所有位遍历完一遍的运算结果即为CRC。

DEL是定界符,固定为隐性电平。
ACK场
ACK场由ACK和DEL组成。
ACK为1位数据,为显性电平时表示接收器正确接收了数据,为隐性电平时表示接收器没有正确接收数据。
DEL同上。
帧类型
CAN总线的帧类型主要分为这几种:数据帧、遥控帧、错误帧、过载帧、帧间隔。
数据帧
标准帧
如下图所示,标准帧由SOF、仲裁场、控制场、数据场、CRC场、ACK场、EOF、ITM组成。 
扩展帧
扩展帧的仲裁场与控制场和标准帧略有区别。 
IDE字段为1时,前面本是RTR的字段便不再起作用,变为永远置1的SRR字段。紧跟其后是扩展帧的18位Extended ID。
控制场的IDE字段变为r1,作为保留位。
遥控帧
遥控帧也分为扩展帧和标准帧两种。和数据帧的唯一区别是没有数据场。
RTR字段为1,表示该帧为遥控帧。作用是希望接收器响应,发送期望ID的数据帧。作用有点类似于LIN协议的发送帧头。
错误帧
错误检测
- 位检测:总线上的位和发送的位不同。
- 填充检测:连续发送了5个相同的极性后,会自动插入一个极性相反的位。接收节点会自动删除位填充。因此一旦有6个或以上相同极性的位,则为填充错误。
- CRC检测:见上文。
- 格式检测:固定格式位场含有非法位。
- ACK检测:ACK位不是显性电平时,为ACK错误。
错误状态机
| 事件 | 发送错误计数器(TEC) | 接收错误计数器(REC) |
|---|---|---|
| 成功发送一帧 | -1(最小为0) | - |
| 成功接收一帧 | - | -1(最小为0) |
| 发送时检测到位错误 | +8 | - |
| 发送时检测到ACK错误 | +8 | - |
| 发送时因错误发送错误标志 | +8 | - |
| 接收时检测到位错误(非填充) | - | +8 |
| 接收时检测到填充错误 | - | +1 |
| 接收时检测到其他错误 | - | +8 |
| 发送被动错误标志后 | +8 | - |
错误状态机如下: 
主动错误帧
处于主动错误状态时,当检测到有错误时,马上发送6个连续的显性位,再发送8个隐性的错误界定符。
被动错误帧
处于被动错误状态时,当检测到有错误时,马上发送6个连续的隐性位,再发送8个隐性的错误界定符。