3、CAN协议层:CAN帧种类与数据帧结构详解

大家好,我是你们的嵌入式讲师。今天咱们聊聊CAN协议层最核心的内容——帧的种类和结构。

说实话,我刚接触CAN总线那会儿,最头疼的就是这五种帧。数据帧、远程帧、错误帧、过载帧、帧间隔,光名字就够绕的。但后来我发现,只要搞懂了数据帧,其他的就迎刃而解了。

3.1 五种帧的总体概览

CAN总线上跑的数据,说白了就是这五种帧。我习惯把它们分成两类:

  • 正常通信帧:数据帧、远程帧
  • 总线管理帧:错误帧、过载帧、帧间隔

你想想看,正常通信的时候,大部分时间都是数据帧在跑。远程帧偶尔出现,用来请求数据。而错误帧和过载帧,嗯,我希望你永远别在项目里见到它们——见到了就说明出问题了。

核心记忆口诀:数据帧传数据,远程帧要数据,错误帧报故障,过载帧求暂停,帧间隔做分隔。

下面这张图,是我自己画的总线帧结构全景图,建议你保存下来多看几遍。

CAN总线五种帧结构全景图 数据帧 传输数据(最常用) 远程帧 请求数据(无数据段) 错误帧 总线错误通知 过载帧 接收过载请求延迟 帧间隔 帧与帧之间的分隔 关键说明 • 数据帧和远程帧由节点主动发送,属于正常通信帧 • 错误帧和过载帧由硬件自动触发,属于总线管理帧 • 帧间隔是强制性的,任何帧发送前都必须有帧间隔 • 标准帧11位ID,扩展帧29位ID,两者不兼容

3.2 数据帧——CAN通信的主角

数据帧是CAN总线上最常见的帧类型。我做过统计,在一个正常的CAN网络中,数据帧占了总流量的95%以上。所以,搞懂数据帧,你就掌握了CAN通信的80%。

3.2.1 数据帧的两种格式

数据帧分为两种格式:

特性 标准帧(CAN 2.0A) 扩展帧(CAN 2.0B)
ID长度 11位 29位
仲裁场长度 12位 32位
兼容性 所有CAN控制器都支持 仅CAN 2.0B控制器支持
应用场景 简单网络、低速系统 复杂网络、多节点系统

我个人建议,如果你的项目节点数不超过10个,用标准帧就够了。扩展帧虽然ID空间大,但帧头更长,总线利用率反而低一些。

3.2.2 数据帧的完整结构

数据帧由7个段组成。我习惯把它拆成三部分来看:

  • 起始部分:帧起始(SOF)
  • 控制部分:仲裁场、控制场
  • 数据部分:数据场、CRC场、应答场
  • 结束部分:帧结束(EOF)

下面这张表,是我当年做项目时贴在工位上的,现在分享给你:

段名称 位数 说明
帧起始(SOF) 1位 显性位(0),同步所有节点
仲裁场 12位(标准)/32位(扩展) ID + RTR位,决定总线优先级
控制场 6位 IDE位、保留位、DLC(数据长度码)
数据场 0~64位(0~8字节) 实际传输的数据内容
CRC场 16位 15位CRC校验 + 1位CRC界定符
应答场 2位 ACK Slot + ACK界定符
帧结束(EOF) 7位 全部为隐性位(1)

避坑指南:我曾经在一个项目中,DLC设置成了9,结果接收节点死活收不到数据。后来才发现,CAN数据场最大只有8字节。DLC的值只能是0~8,超过8的会被当作8处理,但有些老控制器会直接报错。

3.3 远程帧——请求数据的利器

远程帧的结构和数据帧几乎一样,唯一的区别是:远程帧没有数据场。

你可能会问:没有数据场,那它怎么传数据?

其实远程帧的作用不是传数据,而是请求数据。节点A发送一个远程帧,告诉节点B:把你的数据发给我。节点B收到后,就会发送一个同ID的数据帧作为响应。

远程帧的RTR位是隐性位(1),而数据帧的RTR位是显性位(0)。这就是两者的根本区别。

注意:远程帧的DLC必须设置为期望接收的数据长度。比如你想请求8字节数据,DLC就设成8。如果DLC设置不对,接收节点可能不会响应。

3.4 错误帧——总线的自我修复机制

错误帧是CAN总线最强大的特性之一。当任何节点检测到总线错误时,它会立即发送错误帧,通知所有节点:刚才的数据作废。

错误帧由两部分组成:

  • 错误标志:6个连续显性位(主动错误)或6个连续隐性位(被动错误)
  • 错误界定符:8个连续隐性位

我记得有一次调试,总线莫名其妙地频繁报错。用示波器一看,发现错误帧一个接一个。后来查出来是一个节点的终端电阻焊错了,导致信号反射。嗯,从那以后我每次布线都会再三检查终端电阻。

3.5 过载帧——接收端的喘息机会

过载帧的作用很简单:告诉发送端,我这边处理不过来了,你等会儿再发。

过载帧的结构和错误帧很像:

  • 过载标志:6个连续显性位
  • 过载界定符:8个连续隐性位

过载帧通常在两种情况下触发:

  1. 接收节点内部缓冲区满了,来不及处理
  2. 总线空闲时,节点检测到显性位(异常情况)

3.6 帧间隔——帧与帧之间的呼吸

帧间隔不是真正的帧,而是帧与帧之间的空闲时间。任何节点在发送下一帧之前,都必须等待至少3个隐性位(帧间隔)。

帧间隔分为两部分:

  • 间歇场:3个隐性位
  • 总线空闲:任意长度的隐性位

你想想看,如果没有帧间隔,总线上的帧就会连在一起,接收端根本分不清哪里是帧头、哪里是帧尾。

实战经验:我曾经在做一个高速CAN项目时,为了提升总线利用率,把帧间隔缩短到了2个位。结果总线直接崩溃,所有节点都报错。后来查标准才知道,帧间隔的3个隐性位是硬性规定,不能改。

3.7 数据帧的位填充机制

数据帧在发送时,还有一个隐藏的机制——位填充。CAN协议规定:连续发送5个相同位后,必须插入一个相反位。

为什么要这么做?

说白了,就是为了保证接收端的时钟同步。如果连续发送太多相同的位,接收端的时钟就会漂移,导致采样错误。

位填充的规则很简单:

  • 从SOF到CRC场结束,每5个相同位后插入1个相反位
  • ACK场和EOF不参与位填充

举个例子:如果你要发送的数据是 11111 00000,经过位填充后变成 111110 000001。注意,填充位是自动插入的,接收端会自动移除。

小技巧:在设计CAN报文时,尽量避免连续5个相同位。虽然硬件会自动处理,但过多的填充位会降低总线利用率。我一般会通过调整数据内容来减少填充位的出现。

3.8 数据帧的优先级仲裁

CAN总线的仲裁机制,是它最牛的地方。多个节点同时发送时,谁优先级高谁先发。

仲裁发生在仲裁场,具体规则是:

  • ID值越小,优先级越高
  • 显性位(0)覆盖隐性位(1)
  • 数据帧优先级高于远程帧(RTR位决定)

我举个例子:节点A的ID是0x100,节点B的ID是0x200。如果它们同时发送,节点A的ID更小,所以节点A赢得仲裁,节点B自动退出发送。

这个机制的好处是:总线冲突不会导致数据丢失。输掉仲裁的节点会在总线空闲后自动重发。

3.9 实战:如何解析一个数据帧

假设你在示波器上抓到了一个CAN数据帧,二进制序列如下:

0 001 0001 000 10101010 11001100 010101010101010 1 1 1111111

我们来一步步解析:

  1. SOF:第一个0,帧起始
  2. 仲裁场:001 0001 000,这是11位ID,二进制转十六进制是0x088
  3. 控制场:RTR位是0(数据帧),IDE位是0(标准帧),DLC是0100(4字节)
  4. 数据场:10101010 11001100,只有2字节?不对,DLC是4,但数据只有2字节?

嗯,这里要注意:DLC表示数据长度,但实际数据可以少于DLC。不过大多数情况下,DLC和实际数据长度是一致的。

解析完这个帧,我们得到:

  • 帧类型:标准数据帧
  • ID:0x088
  • 数据长度:4字节
  • 数据:0xAA 0xCC(假设后两字节为0)

总结一下:数据帧是CAN通信的核心,搞懂它就能搞定大部分CAN开发工作。远程帧、错误帧、过载帧、帧间隔虽然不常用,但关键时刻能救命。建议你在实际项目中,多看看示波器上的波形,把理论和实践结合起来。