3、CAN协议层:数据帧格式、远程帧、错误帧、过载帧、帧间隔

好,咱们今天来啃CAN协议层这块硬骨头。说实话,很多工程师做CAN驱动,寄存器配得溜,但一说到协议层就含糊。我个人觉得,不理解协议层,你写出来的驱动就是“盲人摸象”。

CAN协议定义了五种帧类型:数据帧、远程帧、错误帧、过载帧、帧间隔。咱们一个一个拆开讲。

3.1 数据帧:最常用的“快递包裹”

数据帧,说白了就是用来发送数据的。你在项目中90%的时间都在跟它打交道。

数据帧的格式分两种:标准帧(11位ID)扩展帧(29位ID)。我刚开始做项目时,总觉得扩展帧高大上,啥都用29位。后来发现,很多老设备只认11位,踩过坑才老实。

来看标准帧的结构:

SOF | 11位ID | RTR | IDE | r0 | DLC | 数据段(0-8字节) | CRC | ACK | EOF

每个字段的含义:

字段 长度 说明
SOF 1 bit 帧起始,显性电平(0),同步用
ID 11 bit 标识符,决定优先级。数值越小,优先级越高
RTR 1 bit 远程帧标志。数据帧里固定为显性(0)
IDE 1 bit 扩展标志。标准帧里固定为显性(0)
r0 1 bit 保留位,必须为显性
DLC 4 bit 数据长度,0~8字节
数据段 0~64 bit 实际数据,最多8字节
CRC 15 bit 循环冗余校验,检测传输错误
ACK 2 bit 应答槽。发送方发隐性,接收方拉显性表示收到
EOF 7 bit 帧结束,全部为隐性电平

重点:ID越小,优先级越高。如果两个节点同时发数据,ID小的节点会赢得仲裁,继续发送。ID大的节点自动退出发送,转为接收。

扩展帧呢?就是把11位ID扩展成29位。结构上多了个SRR位和IDE位。SRR位替代了标准帧的RTR位置,IDE位变成隐性(1)表示这是扩展帧。嗯,这里要注意,扩展帧的仲裁场更长,所以标准帧的优先级天然高于扩展帧——哪怕扩展帧的ID数值更小。

3.2 远程帧:只问不拿

远程帧,说白了就是请求数据。你发一个远程帧,告诉对方:“嘿,把你那个ID的数据发给我。”

远程帧的结构和数据帧几乎一样,唯一的区别是RTR位为隐性(1),而且没有数据段。DLC字段表示你期望对方回复的数据长度。

我记得有一次调试一个电机控制器,对方就是不响应我的远程帧请求。查了半天,发现是DLC设置不对。对方要求DLC=8,我设成了0。嗯,细节决定成败。

小技巧:远程帧在实际项目中用得不多。我个人习惯用数据帧直接发送请求命令,而不是用远程帧。因为远程帧的应答机制比较死板,不如自己控制灵活。

3.3 错误帧:总线上的“警报器”

错误帧,是CAN总线自我保护的机制。当节点检测到错误时,就会发出错误帧,通知所有节点:“刚才那帧数据有问题,别用!”

错误帧由两部分组成:

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

错误分两种:

  • 主动错误:节点发6个显性位,强势打断当前传输
  • 被动错误:节点发6个隐性位,不破坏总线,但告诉别人“我出错了”

我曾经在一个项目中,总线莫名其妙地频繁报错。用示波器一看,有个节点的CAN收发器坏了,一直发显性位,把整个总线都拉死了。这就是典型的“总线锁死”现象。解决办法?换芯片,然后在驱动里加总线恢复机制。

避坑指南:我曾经遇到过,错误帧发多了,节点会从“主动错误”状态变成“被动错误”状态,甚至进入“总线关闭”状态。这时候节点就彻底离线了。所以驱动里一定要监控错误计数器,及时复位。

3.4 过载帧:慢点,我跟不上

过载帧,是接收节点在说:“你发得太快了,我处理不过来,等等我!”

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

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

什么情况下会发过载帧?

  • 接收节点内部缓冲区满了
  • 接收节点在帧间隔期间需要额外延迟

你想想看,如果总线上有个节点处理速度慢,它就会频繁发过载帧。这其实是个信号——你的系统设计可能有问题,要么降低波特率,要么优化接收处理逻辑。

3.5 帧间隔:给总线喘口气

帧间隔,就是两帧之间的空闲时间。它让总线上的节点有时间同步状态,准备接收下一帧。

帧间隔的结构:

  • 间歇场:3个隐性位
  • 总线空闲:任意长度的隐性位(直到有节点开始发送)

对于被动错误节点,帧间隔里还会多一个暂停发送场(8个隐性位)。这是对“犯错”节点的惩罚——让它多等一会儿,别抢总线。

我记得有个项目,总线上挂了很多节点,波特率设得比较高。结果发现偶尔会有丢帧现象。用CAN分析仪抓包一看,帧间隔时间太短,节点来不及同步。后来把波特率降了一档,问题就解决了。嗯,有时候不是硬件不行,是协议层的时序没调好。

3.6 总结一下

这五种帧类型,构成了CAN通信的完整协议栈:

  • 数据帧:发数据,最常用
  • 远程帧:要数据,用得少
  • 错误帧:报错误,保命用
  • 过载帧:求减速,缓冲用
  • 帧间隔:做隔离,同步用

写驱动的时候,你不需要手动构造错误帧或过载帧——CAN控制器硬件会自动处理。但你必须理解它们的工作原理,否则出了问题你都不知道从哪查起。

下一章,咱们聊聊CAN的位时序和同步机制。这东西是理解波特率的基础,也是很多驱动工程师的盲区。到时候见。