2. CAN协议详解:从标准帧到错误处理

大家好,我是你们的老朋友。今天咱们来啃一块硬骨头——CAN协议。说实话,我刚入行那会儿,觉得CAN协议就是一堆帧、仲裁、校验的堆砌,背下来就行了。直到我在台架上被一个莫名其妙的错误帧折磨了三天,才明白:纸上得来终觉浅,绝知此事要躬行。

好,废话不多说。咱们从最基础的帧结构开始,一步步把CAN协议吃透。

2.1 CAN 2.0A/B:标准帧与扩展帧

CAN协议有两个版本:2.0A和2.0B。说白了,区别就在ID长度上。

  • CAN 2.0A:只支持11位ID,也就是标准帧。
  • CAN 2.0B:支持11位ID和29位ID,也就是标准帧+扩展帧。

你可能会问:为什么要有扩展帧?嗯,我举个例子。早期车上节点少,11位ID够用。现在一辆车几十个ECU,11位ID根本不够分。29位ID一出来,地址空间从2048个飙到5亿多个,够你用一辈子了。

标准帧结构(11位ID)

标准帧长这样:

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

每个字段的作用:

字段长度说明
SOF1 bit帧起始,显性电平,同步用
ID11 bits标识符,决定优先级
RTR1 bit远程帧请求,0=数据帧,1=远程帧
IDE1 bit标识符扩展位,标准帧里为0
r01 bit保留位
DLC4 bits数据长度,0-8字节
Data0-64 bits实际数据
CRC15 bits循环冗余校验
ACK2 bits应答槽
EOF7 bits帧结束

扩展帧结构(29位ID)

扩展帧就是把ID从11位扩展到29位。结构上多了个SRR位和IDE位:

SOF | 11位基础ID | SRR | IDE | 18位扩展ID | RTR | r1 | r0 | DLC | 数据段 | CRC | ACK | EOF

关键区别:扩展帧的IDE位为1,标准帧的IDE位为0。仲裁时,标准帧优先级高于扩展帧。为什么?因为IDE位在仲裁场里,标准帧的IDE=0(显性),扩展帧的IDE=1(隐性),显性赢。

我个人习惯:能用标准帧就别用扩展帧。扩展帧虽然地址多,但帧长,总线利用率低。我在一个项目中见过有人非要用29位ID传个开关信号,纯粹是浪费带宽。

2.2 CAN总线仲裁机制

仲裁,说白了就是抢总线。多个节点同时发数据,谁赢?

规则很简单:ID越小,优先级越高。显性电平(0)压倒隐性电平(1)。

举个例子:

  • 节点A发ID=0x100(二进制:001 0000 0000)
  • 节点B发ID=0x200(二进制:010 0000 0000)

从第3位开始,A发0(显性),B发1(隐性)。A赢了,B自动退出发送,转为接收。

避坑指南:我曾经遇到过一个问题——两个节点ID相同,同时发数据。结果总线一直冲突,错误帧满天飞。后来查规范才发现:同一总线上不允许有相同ID的节点同时发送。所以设计时一定要保证ID唯一性。

还有一点要注意:远程帧的RTR位为1(隐性),数据帧的RTR位为0(显性)。所以如果数据帧和远程帧ID相同,数据帧赢。这个我在调试一个传感器节点时踩过坑,远程帧发出去没人理,后来发现是ID冲突了。

2.3 位填充与CRC校验

位填充机制

位填充是为了保证时钟同步。CAN总线用NRZ编码,如果连续发太多相同的位,接收方可能丢失同步。

规则:连续发送5个相同位后,自动插入一个相反位

比如你要发:11111 00000

实际发:111110 000001

接收方看到6个相同位,就知道第6位是填充位,去掉就行。

注意:CRC界定符、ACK槽、EOF这些固定格式的字段不进行位填充。我见过有人把EOF里的填充位也算进去,结果CRC算出来不对,折腾了半天。

CRC校验

CAN的CRC是15位的,生成多项式是:

x^15 + x^14 + x^10 + x^8 + x^7 + x^4 + x^3 + 1

计算范围:从SOF到数据段结束(不包括填充位)。

实际项目中,你不需要手算CRC。控制器硬件自动算。但你要知道:CRC错误是最常见的错误类型之一。我在HIL测试中经常故意注入CRC错误,看ECU能不能正确识别并丢弃错误帧。

经验之谈:如果你在台架上发现某个节点频繁发错误帧,先查CRC。很多时候是时钟精度不够,导致采样点偏移,CRC算出来不对。我遇到过用1%精度的晶振跑高速CAN(1Mbps),结果CRC错误率高达5%。换成0.1%精度的晶振,问题消失。

2.4 CAN错误处理机制

CAN的错误处理,说白了就是:谁犯错,谁背锅。每个节点都有两个计数器:

  • TEC:发送错误计数器
  • REC:接收错误计数器

错误状态分三种:

状态条件行为
错误主动TEC < 127 且 REC < 127正常收发,检测到错误发主动错误帧
错误被动TEC > 127 或 REC > 127只能发被动错误帧,且发送前要等8个隐性位
总线关闭TEC > 255完全离线,不参与任何总线活动

错误类型有5种:

  1. 位错误:发送时监控总线,发现电平不一致
  2. 填充错误:连续6个相同位(违反位填充规则)
  3. CRC错误:接收方算出的CRC和发送方不一致
  4. 形式错误:固定格式字段(如EOF)出现错误电平
  5. ACK错误:发送方没收到应答

避坑指南:我曾经调试过一个项目,某个节点总是进入总线关闭状态。查了半天,发现是它的CAN收发器供电不稳,导致发送时电平异常,频繁触发位错误。TEC一路飙升到255,直接离线。解决方案:在收发器电源上加个电容滤波,问题解决。

还有一点:错误帧的优先级是最高的。一旦检测到错误,节点立刻发6个显性位(主动错误帧),打断当前传输。这意味着总线上的错误会迅速传播。所以设计时一定要控制错误率,别让一个坏节点拖垮整条总线。

嗯,CAN协议的核心内容就这些。从帧结构到仲裁,从位填充到错误处理,每个环节都环环相扣。你想想看,一个协议能活30多年,靠的就是这些精巧的设计。

下一章咱们聊CAN FD,那个更有意思。先消化这些,有问题随时问我。