3、CAN网络基础:位填充机制、帧间距、错误帧与过载帧的工作原理

3.1 位填充机制——CAN总线的“心跳节拍”

位填充,说白了就是CAN总线用来保证时钟同步的一种手段。你想想看,如果总线上连续出现太多相同的电平,比如一连串的“0”或者一连串的“1”,接收节点的时钟就容易跑偏。为什么?因为没有跳变沿来校准了。

我个人习惯把位填充叫做“强制插入的节奏感”。规则很简单:发送节点在连续发送5个相同位之后,必须插入一个相反的电平位。举个例子,如果你要发5个连续的“0”,那在第5个“0”之后,硬件会自动塞一个“1”进去。接收端看到这个“1”,就知道这是填充位,不是数据,直接把它丢掉。

关键点:位填充只发生在“数据场”和“CRC场”中。SOF、仲裁场、控制场、ACK场和EOF这些段是不做填充的。嗯,这里要注意,CRC界定符和ACK界定符也是固定的,不参与填充。

我在项目中遇到过一个问题:有个同事在写CAN驱动时,手动组包的时候忘了考虑位填充对实际传输时间的影响。结果总线负载率算出来只有30%,实际跑起来却经常丢帧。后来一查,发现位填充让实际传输的位数多了将近10%。所以,计算总线负载率时,一定要把位填充的开销算进去,一般按10%~15%的余量来估算比较稳妥。

避坑指南:我曾经在测试一个高速CAN节点时,发现它偶尔会进入“总线关闭”状态。排查了很久,最后发现是晶振精度不够,导致位填充后的同步跳变沿偏差累积,超出了容差范围。所以,晶振精度至少要在±0.5%以内,否则位填充机制反而会放大时钟误差。

3.2 帧间距——总线上的“喘息空间”

帧间距,就是两帧CAN报文之间的间隔。为什么要有这个间隔?说白了,就是给总线上的节点一点时间,让它们从上一帧的接收状态切换回空闲状态,准备好接收下一帧。

帧间距由两部分组成:

  • 间歇场(Intermission):3个隐性位。这是强制性的,任何节点都不能在这3个位期间发送数据。
  • 总线空闲(Bus Idle):任意长度的隐性位。只要总线空闲,任何节点都可以开始发送。

这里有个容易混淆的点:被动错误节点在发送完一帧之后,还要额外多等8个位(称为“挂起传输”),然后才能进入间歇场。我刚开始做CAN开发时,就因为这个“挂起传输”没处理好,导致一个被动错误节点老是抢不到总线。后来查了协议才明白,这是为了让被动错误节点“低调一点”,避免它频繁干扰总线。

注意:帧间距的间歇场是3个隐性位,但如果你在总线上看到连续3个隐性位,不一定就是间歇场。也可能是EOF的最后3个位。怎么区分?看ACK场。EOF之后紧接着就是间歇场,中间没有别的界定符。

3.3 错误帧——总线的“自我修复机制”

错误帧,是CAN总线最厉害的地方之一。它不需要上层协议干预,硬件自己就能检测错误并通知所有节点。我个人觉得,这才是CAN能在汽车这种恶劣电磁环境下活下来的根本原因。

错误帧的结构很简单:

  • 错误标志(Error Flag):6个显性位(主动错误节点)或6个隐性位(被动错误节点)。
  • 错误界定符(Error Delimiter):8个隐性位。

为什么会这样?当一个节点检测到错误时,它会立刻发送6个显性位。这6个显性位会破坏当前正在传输的帧,其他节点收到后也会检测到“位填充错误”或“CRC错误”,然后它们也会发送错误标志。结果就是总线上出现6~12个显性位,所有节点都知道出错了,自动丢弃当前帧,准备重发。

我记得有一次在实验室调试,用示波器抓CAN总线波形,看到一连串的显性位,长度还不一样。一开始以为是干扰,后来一算,正好是6个位加6个位,典型的错误帧叠加现象。嗯,从那以后,我养成了一个习惯:看到总线上有超过6个连续显性位,第一反应就是查错误帧

错误类型有5种:

错误类型 检测时机 说明
位错误 发送时 发送节点监控总线,发现实际电平与发送的不一致
填充错误 接收时 连续6个相同位,违反了位填充规则
CRC错误 接收时 接收节点计算的CRC与发送的不一致
形式错误 接收时 固定格式的位(如CRC界定符)出现错误电平
应答错误 发送时 发送节点在ACK槽没有收到显性位

实战经验:我曾经在整车测试时,发现某个节点频繁发送错误帧。用CAN分析仪一抓,全是“形式错误”。后来发现是那个节点的CAN控制器寄存器配置错了,把CRC界定符的长度设成了7个位而不是1个位。这种低级错误,查起来真的很费时间。所以,初始化CAN控制器时,一定要对照数据手册逐位确认寄存器配置

3.4 过载帧——总线的“忙信号”

过载帧,说白了就是节点在说“我太忙了,慢一点”。它和错误帧长得有点像,但用途完全不同。过载帧不是用来报告错误的,而是用来请求延迟下一帧的发送。

过载帧的结构:

  • 过载标志(Overload Flag):6个显性位。
  • 过载界定符(Overload Delimiter):8个隐性位。

什么情况下会触发过载帧?主要有两种:

  1. 接收节点内部条件未准备好:比如接收缓冲区满了,或者CPU正在处理上一帧数据,没空接收新帧。
  2. 在间歇场检测到显性位:正常情况下间歇场应该是3个隐性位,如果某个节点在间歇场期间检测到显性位,它会认为总线出现了异常,于是发送过载帧来“重置”总线状态。

你想想看,第二种情况其实挺有意思的。它说明过载帧不仅是一种“主动请求”,也是一种“被动响应”。我在实际项目中很少看到过载帧,因为现在的CAN控制器缓冲区都比较大,处理速度也快。但如果你在测试中频繁看到过载帧,那就要注意了——很可能是接收节点的中断服务程序写得不好,处理时间太长,导致缓冲区溢出。

重要区别:错误帧和过载帧虽然结构相似,但触发条件完全不同。错误帧是检测到“错误”时触发,过载帧是检测到“过载条件”时触发。而且,过载帧不会像错误帧那样导致节点状态切换(主动错误/被动错误)。

3.5 小结

好了,这一章的内容就这些。位填充保证了时钟同步,帧间距给了节点喘息空间,错误帧实现了自我修复,过载帧提供了流量控制。这四个机制,是CAN总线能在汽车上稳定运行的基础。我个人觉得,理解它们比背一堆寄存器配置更有用。因为当你遇到总线问题时,能快速定位到是哪个环节出了岔子。

下一章,我们会聊CAN的报文格式和ID分配策略。到时候我会分享一些我在整车网络设计中的实际案例,保证比协议栈里的理论有意思得多。