第二章:CAN报文结构——数据帧、远程帧、错误帧、过载帧、帧间隔,以及ID仲裁机制
大家好,我是你们的老朋友。今天咱们来聊聊CAN总线里最核心的东西——报文结构。
说实话,我刚开始接触CAN总线时,觉得这东西挺抽象的。一堆0和1在线上跑来跑去,怎么就知道谁发了啥?后来亲手抓了几次波形,才真正搞明白。嗯,今天我就把这些经验掰开了揉碎了讲给你听。
2.1 五种帧类型概览
CAN总线上跑的数据,一共就五种帧。我习惯把它们分成两类:一类是正常通信用的,另一类是出问题时用的。
| 帧类型 | 用途 | 谁发起的 |
|---|---|---|
| 数据帧 | 发送数据 | 发送节点 |
| 远程帧 | 请求数据 | 接收节点 |
| 错误帧 | 报告总线错误 | 检测到错误的节点 |
| 过载帧 | 请求延迟发送 | 接收节点(忙时) |
| 帧间隔 | 帧与帧之间的分隔 | 总线空闲 |
你想想看,这就像一群人开会。数据帧就是有人发言,远程帧就是有人点名让某人发言,错误帧就是有人喊“你说错了”,过载帧就是“你慢点说,我记不过来”。帧间隔呢,就是大家换气的时间。
2.2 数据帧——最常用的帧
数据帧是我们平时打交道最多的。我做过一个项目,车上几十个ECU,每天就是靠数据帧互相传递信息。
数据帧的结构,说白了就是七个部分:
- 帧起始(SOF):1个显性位,告诉所有人“我要开始说话了”
- 仲裁场:包含ID和RTR位,决定谁能抢到总线
- 控制场:包含IDE位和DLC(数据长度码)
- 数据场:0~8字节,真正要传的数据
- CRC场:15位CRC校验+1位界定符
- ACK场:2位,接收节点确认收到
- 帧结束(EOF):7个隐性位
重点提醒:数据场最多8个字节。我见过有人想一次传10个字节,结果程序跑飞了。记住,CAN是短帧协议,一次最多8字节,多了就得分包。
2.3 远程帧——我要数据,你给我
远程帧这东西,说白了就是“请求数据”。它的结构和数据帧很像,但有两个关键区别:
- RTR位为隐性(1),表示这是远程帧
- 没有数据场
我曾经在调试一个传感器节点时,发现主控一直收不到数据。抓了波形一看,原来是远程帧的ID写错了。你想想看,ID错了,传感器根本不知道你在叫它,自然不会回应。
我的小技巧:远程帧的DLC要设置成你期望接收的数据长度。比如你想收8个字节,DLC就设成8。虽然远程帧本身不带数据,但这个长度信息很重要。
2.4 错误帧——出问题了怎么办
错误帧是CAN总线自我保护的机制。当某个节点检测到错误时,它会主动发送错误帧,告诉所有人“刚才那帧有问题,别用了”。
错误帧由两部分组成:
- 错误标志:6个连续相同位(显性或隐性)
- 错误界定符:8个隐性位
这里有个坑,我踩过。错误帧一旦发出,所有节点都会丢弃当前帧。如果你的程序没有处理好重发机制,数据就丢了。所以,我建议你在应用层加个超时重传的逻辑。
注意:如果一个节点频繁发送错误帧,它可能会进入“总线关闭”状态。这时候它就彻底离线了。我曾经在实验室里见过一个节点疯狂报错,最后整个总线都瘫痪了。排查了半天,原来是它的晶振频率偏了。
2.5 过载帧——我忙,等会儿
过载帧用得不多,但关键时刻能救命。当接收节点忙不过来时,它可以发送过载帧,让发送节点等一等。
过载帧的结构和错误帧很像:
- 过载标志:6个显性位
- 过载界定符:8个隐性位
说实话,我在实际项目中很少见到过载帧。因为现在的MCU处理速度都很快,8个字节的数据基本不会造成过载。但如果你在做低速总线或者高负载系统,还是得考虑这个情况。
2.6 帧间隔——喘口气的时间
帧间隔就是两帧之间的空闲时间。它分两种情况:
- 主动错误节点:3个隐性位的间隔
- 被动错误节点:8个隐性位的间隔(多等一会儿)
嗯,这里要注意。被动错误节点需要多等一段时间,这是为了让总线上的错误节点“冷静一下”,别让它一直抢总线。
2.7 ID仲裁机制——谁说了算
这是CAN总线最精彩的部分。多个节点同时发送时,怎么决定谁先发?答案就是:ID小的优先。
仲裁的原理很简单:
- 所有节点同时发送起始位(显性)
- 然后逐位比较ID
- 谁先发出隐性位(1),而别人发出显性位(0),谁就退出
- 最后剩下的那个节点获得总线控制权
你想想看,这就像一群人抢着说话。谁的声音小(隐性位),谁就得闭嘴。声音大(显性位)的继续讲。
关键点:ID越小,优先级越高。所以0x000的优先级最高,0x7FF的优先级最低。我在设计系统时,习惯把关键信号(比如刹车、转向)分配小ID,把非关键信号(比如车窗、空调)分配大ID。
举个例子:
节点A发送ID = 0x123(二进制:0001 0010 0011)
节点B发送ID = 0x124(二进制:0001 0010 0100)
逐位比较:
第1位:都是0 → 继续
第2位:都是0 → 继续
...
第9位:A是0,B是1 → B退出,A获胜
我曾经在调试一个多节点系统时,发现某个节点总是抢不到总线。后来一查,它的ID设成了0x7FF,而其他节点都是0x100左右。你想想看,这怎么可能抢得过?
避坑指南:我曾经把两个节点的ID设成了相同的值。结果它们同时发送时,数据场冲突了,CRC校验过不了,整个总线都在报错。所以,ID必须唯一,这是铁律。
2.8 标准帧 vs 扩展帧
CAN 2.0A定义了11位ID(标准帧),CAN 2.0B定义了29位ID(扩展帧)。
| 特性 | 标准帧 | 扩展帧 |
|---|---|---|
| ID长度 | 11位 | 29位 |
| 可寻址节点数 | 2048个 | 5.36亿个 |
| 仲裁场长度 | 12位 | 32位 |
| 兼容性 | 所有CAN控制器都支持 | 需要支持CAN 2.0B的控制器 |
我个人习惯,能用标准帧就用标准帧。为什么呢?因为标准帧的仲裁场短,总线利用率高。除非你的系统节点特别多,或者需要复杂的ID分配,否则没必要用扩展帧。
好了,这一章的内容就到这里。报文结构是CAN总线的基础,搞懂了这些,后面的故障诊断才能有的放矢。下一章我们聊聊物理层,看看那些电平信号到底是怎么回事。
本章小结:
- 五种帧类型:数据帧、远程帧、错误帧、过载帧、帧间隔
- 数据帧最多8字节数据
- 远程帧没有数据场,用于请求数据
- 错误帧是总线自我修复机制
- ID仲裁:ID越小,优先级越高
- 标准帧11位ID,扩展帧29位ID