2、CAN帧结构:数据帧、远程帧、错误帧、过载帧、帧间隔
好,咱们接着聊。上一章我们把CAN总线的物理层和位时序讲透了,这一章我带你看看CAN总线上到底跑的是什么“信使”。
说白了,CAN总线上就五种帧:数据帧、远程帧、错误帧、过载帧,还有帧间隔。你想想看,一个系统要正常工作,总得有人发数据、有人要数据、有人报错、有人喊“慢点”,还得有人负责中场休息。这五种帧就是干这些活的。
2.1 数据帧——最常用的“快递员”
数据帧,顾名思义,就是用来传输数据的。我习惯把它叫做“标准快递包裹”。
数据帧分两种格式:标准帧(11位ID)和扩展帧(29位ID)。结构上大同小异,我直接给你拆开看:
标准帧结构:
SOF + 11位ID + RTR + IDE + r0 + DLC + 数据段(0-8字节) + CRC + ACK + EOF
扩展帧结构:
SOF + 11位ID + SRR + IDE + 18位ID + RTR + r1 + r0 + DLC + 数据段(0-8字节) + CRC + ACK + EOF
这里有几个关键点,我得跟你强调一下:
- SOF(帧起始):一个显性位,告诉总线上所有人“我要发数据了”。
- ID(标识符):决定优先级。数值越小,优先级越高。我在项目中遇到过有人把ID设成全0,结果那个节点一直抢总线,其他节点根本发不出去——嗯,这是个坑。
- RTR(远程发送请求位):数据帧里RTR=0(显性),远程帧里RTR=1(隐性)。这是区分数据帧和远程帧的关键。
- DLC(数据长度码):4位,表示数据段有多少字节。范围0~8,注意DLC=0也是合法的,就是发个空包。
- 数据段:0~8字节,这就是你要传的“货”。
- CRC(循环冗余校验):15位CRC值+1位CRC界定符。用来检查数据有没有传错。
- ACK(应答):发送节点发隐性位,接收节点如果收到正确的帧,就在这个位置拉成显性。我曾经调试一个节点,发现它总是不应答,查了半天是CAN控制器初始化没配好——低级错误,但很隐蔽。
- EOF(帧结束):7个隐性位,表示帧结束了。
重点记忆:数据帧的RTR位是显性(0),远程帧的RTR位是隐性(1)。这是区分两者的唯一标志。
2.2 远程帧——“给我发点数据过来”
远程帧,说白了就是“请求帧”。一个节点不发数据,而是问另一个节点:“嘿,把你那个数据给我发一份。”
远程帧的结构和数据帧几乎一样,区别只有两点:
- RTR = 1(隐性):这是远程帧的标志。
- 没有数据段:DLC虽然写了长度,但数据段是空的。DLC在这里表示“我想要多少字节的数据”。
举个例子:你有一个温度传感器节点,它平时不主动发数据。主控节点发一个远程帧,ID是温度传感器的ID,DLC=2,意思是“温度传感器,把你的2字节温度数据发给我”。温度传感器收到后,就会发一个数据帧回来。
我的经验:远程帧在实际项目中用得不多。我更喜欢让传感器节点定时主动上报数据,而不是靠主控去轮询。轮询会增加总线负载,而且实时性不好。但有些场合,比如诊断模式,远程帧还是很方便的。
2.3 错误帧——出问题了,大家注意!
错误帧是CAN总线最强大的特性之一。任何一个节点检测到错误,就会立刻发送错误帧,通知总线上所有节点:“刚才那帧有问题,别用了。”
错误帧由两部分组成:
- 错误标志:6个连续相同位(显性或隐性)。主动错误节点发6个显性位,被动错误节点发6个隐性位。
- 错误界定符:8个隐性位。
为什么会是6个?你想想看,CAN总线用的是“显性位覆盖隐性位”的机制。6个连续显性位,足以破坏任何正常帧的位填充规则(正常帧最多5个连续相同位)。这样一来,所有节点都会检测到位填充错误,进而发送自己的错误帧——这就是所谓的“错误扩散”。
避坑指南:我曾经调试过一个设备,总线上老是出现错误帧。查了半天,发现是一个节点的晶振偏差太大,导致位时序对不上。嗯,从那以后我每次选型都会仔细看晶振的精度,尤其是高温环境下的漂移。
2.4 过载帧——“慢点,我处理不过来了”
过载帧和错误帧长得很像,但用途完全不同。过载帧是接收节点主动发出的,意思是“我太忙了,你慢点发”。
过载帧的结构:
- 过载标志:6个显性位。
- 过载界定符:8个隐性位。
触发过载帧的条件有三个:
- 接收节点内部条件没准备好,比如缓冲区满了。
- 在帧间隔期间,检测到显性位(这属于总线错误,但用过载帧来处理)。
- 过载标志的采样问题(这个比较少见,我就不展开了)。
说实话,我在实际项目中很少见到过载帧。现在的CAN控制器缓冲区都挺大的,而且大家设计时都会算好总线负载率。如果你发现总线上频繁出现过载帧,那说明你的系统设计有问题——要么总线负载太高,要么某个节点处理能力不够。
2.5 帧间隔——中场休息
帧间隔,就是两帧之间的“空闲时间”。它让总线上的节点有时间准备接收下一帧。
帧间隔的结构:
- 间歇场:3个隐性位。
- 总线空闲:任意长度的隐性位,直到有节点开始发送SOF。
对于被动错误节点,帧间隔还会多一个挂起传输场(8个隐性位)。这是对被动错误节点的“惩罚”——它发完一帧后,必须多等8个位时间才能发下一帧。
关键点:帧间隔不是可选的,是强制存在的。任何两帧之间都必须有至少3个隐性位的间歇场。否则,总线仲裁会出问题。
2.6 五种帧的关系——一张表说清楚
我整理了一张表,方便你对比记忆:
| 帧类型 | 用途 | RTR位 | 数据段 | 谁发送 |
|---|---|---|---|---|
| 数据帧 | 传输数据 | 0(显性) | 0~8字节 | 发送节点 |
| 远程帧 | 请求数据 | 1(隐性) | 无 | 请求节点 |
| 错误帧 | 报告错误 | 无 | 无 | 检测到错误的节点 |
| 过载帧 | 请求延迟 | 无 | 无 | 接收节点 |
| 帧间隔 | 帧间分隔 | 无 | 无 | 总线自动产生 |
嗯,这一章的内容就这些。五种帧,各有各的职责。数据帧和远程帧是“业务帧”,负责干活;错误帧和过载帧是“管理帧”,负责维护秩序;帧间隔是“休息时间”,让总线喘口气。
下一章,我会带你深入CAN总线的仲裁机制——为什么两个节点同时发数据不会冲突?为什么ID小的节点优先级高?这些问题的答案,都在下一章里。