2. CAN总线基础

CAN总线上跑的数据,说白了就是几种固定的“信封”。你只要搞懂这几种信封长什么样,排查故障时一眼就能看出问题出在哪。我刚开始接触CAN的时候,总觉得帧结构太枯燥,直到有一次在产线上抓到一个“幽灵帧”,折腾了两天,最后发现是远程帧的RTR位被误配了。从那以后,我再也不敢小看这些基础结构。

一、标准帧 vs 扩展帧

CAN 2.0规范定义了两种帧格式:标准帧(11位ID)和扩展帧(29位ID)。它们最大的区别就是仲裁场的长度。

对比项 标准帧 扩展帧
标识符长度 11位 29位
IDE位 显性(0) 隐性(1)
帧长度(数据场0字节) 47位 67位
兼容性 所有CAN控制器都支持 需控制器支持CAN 2.0B

我个人习惯在项目初期就定好:能用标准帧就别用扩展帧。为什么?因为扩展帧多出来的18位ID,在总线上就是实打实的传输时间。你想想看,如果一条线上有几百条报文,每帧多20位,总线负载率直接飙升。

避坑指南: 我曾经遇到过一个案例,ECU和传感器都宣称支持CAN 2.0B,结果传感器只发了标准帧,ECU却一直在等扩展帧。两边都收不到数据,但总线错误计数器也没爆。排查了整整一天,最后用示波器一看——IDE位一直是显性,问题就出在ID格式不匹配。

二、数据帧格式详解

数据帧是CAN总线最常用的帧类型。它负责把数据从发送节点送到接收节点。一个完整的数据帧由7个场组成:

  1. 帧起始(SOF):1位显性,同步所有节点的时钟
  2. 仲裁场:11位ID(标准帧)或29位ID(扩展帧)+ RTR位
  3. 控制场:IDE位、保留位r0/r1、DLC(4位数据长度码)
  4. 数据场:0~8字节,实际要传输的数据
  5. CRC场:15位CRC校验码 + 1位CRC界定符
  6. ACK场:1位ACK槽 + 1位ACK界定符
  7. 帧结束(EOF):7位隐性

这里有个关键点:DLC虽然只有4位,但它能表示0~15。但CAN协议规定数据场最多8字节。所以DLC值大于8时,控制器怎么处理?嗯,不同厂家做法不一样。有的直接发8字节,有的报错。我建议你写代码时,DLC一定要和实际数据长度严格对应,别偷懒。

实战要点: 数据场长度固定为0~8字节。如果你要传超过8字节的数据,必须拆分成多帧。我见过有人把DLC设成0,但数据场塞了8个字节——结果接收方只读到0字节,数据全丢了。

三、远程帧

远程帧说白了就是“请求帧”。发送节点发一个远程帧,告诉目标节点:“把你那个ID的数据发给我”。远程帧和数据帧的结构几乎一样,唯一的区别是RTR位:数据帧的RTR是显性(0),远程帧的RTR是隐性(1)。

远程帧没有数据场。它的DLC字段表示请求的数据长度。举个例子:你发一个ID=0x123、DLC=4的远程帧,收到这个帧的节点就应该回复一个ID=0x123、数据场4字节的数据帧。

注意: 远程帧的RTR位是隐性,这意味着它的优先级比同ID的数据帧低。如果总线上同时出现一个数据帧和一个远程帧(ID相同),数据帧会赢得仲裁。我遇到过有人用远程帧做轮询,结果因为优先级问题,数据帧总是先发,远程帧被无限推迟——总线负载直接爆表。

四、错误帧

错误帧是CAN总线最“暴力”的机制。只要任何一个节点检测到错误,它就会立即发送错误帧,把当前传输的报文“撕碎”。错误帧由两部分组成:

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

为什么是6个显性位?因为CAN的位填充规则规定,连续5个相同电平后必须插入一个反相电平。6个连续显性位直接违反了位填充规则,所有节点都会检测到这个错误,然后各自发送错误帧。这就是所谓的“错误扩散”——一个节点犯错,全总线跟着遭殃。

我记得有一次在台架上调试,总线上突然出现大量错误帧。用CANalyzer一看,错误计数器蹭蹭往上涨。最后定位到是一个节点的晶振偏差太大,导致位时序错位。换了个晶振,问题解决。

五、过载帧

过载帧和错误帧长得有点像,但用途完全不同。过载帧是接收节点主动发出的,意思是:“我太忙了,处理不过来,你慢点发”。过载帧的结构:

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

过载帧只在两种情况下产生:

  1. 接收节点在帧间隔期间检测到显性位(误以为有新帧开始)
  2. 接收节点的内部缓冲区满了,需要额外时间处理

说实话,我在实际项目中很少见到过载帧。现在的CAN控制器处理能力都很强,除非你设计得太极限——比如把中断优先级设得比CAN接收中断还高,导致CAN数据来不及处理。嗯,这种坑我踩过。

六、帧间隔

帧间隔是两帧之间的“休息时间”。它由3个隐性位组成。任何节点在发送完一帧后,都必须等待这3个隐性位才能发送下一帧。这个机制保证了总线上的节点不会“抢跑”。

帧间隔还有一个特殊作用:错误被动节点在发送完一帧后,需要等待8个隐性位(而不是3个)才能继续发送。这是协议对“问题节点”的一种惩罚机制——你越容易出错,就越少给你说话的机会。

小技巧: 用示波器抓CAN总线波形时,帧间隔的3个隐性位是最容易辨认的“标尺”。你可以数隐性位的个数来判断当前总线状态是否正常。如果帧间隔里出现了显性位,那大概率是过载帧或者错误帧。

七、帧结构关系图

下面这张图把CAN总线上所有帧类型的关系梳理了一遍。你排查故障时,可以对照这张图快速定位问题出在哪个环节。

CAN帧结构关系图 CAN总线帧类型关系图 CAN总线帧类型 数据帧 远程帧 错误帧 过载帧 标准帧(11位ID) 扩展帧(29位ID) SOF 仲裁场 控制场 数据场 CRC场 ACK场 EOF 错误标志 + 界定符 过载标志 + 界定符 帧间隔:3个隐性位 注:数据场长度0~8字节,远程帧无数据场 错误帧和过载帧可在任何时刻插入,破坏当前传输 数据帧 远程帧 错误帧 过载帧 标准/扩展帧

八、实战总结

搞懂这几种帧结构,你排查CAN故障时就能做到“看帧识问题”:

  • 总线上全是错误帧?先查位时序和终端电阻
  • 收不到数据但错误计数器没涨?检查ID格式(标准/扩展)是否匹配
  • 数据偶尔丢帧?看看是不是远程帧的RTR位配错了
  • 总线负载率异常高?检查帧间隔是否被破坏,或者有过载帧在捣乱