2. CAN总线基础
CAN协议的核心,说白了就是一套“谁先说话谁有理”的通信规则。你想想看,总线上那么多节点,同时发数据怎么办?谁先发?发错了怎么处理?这些问题的答案,都藏在CAN的帧结构和仲裁机制里。
我个人习惯把CAN帧理解成“带信封的快递”。信封上写着优先级(仲裁段),里面装着数据(数据段)。快递员(总线)看到两个信封同时出现,会自动比较谁的优先级更高,然后让高优先级的先走。
2.1 CAN数据帧结构详解
数据帧是CAN通信中最常用的帧类型。它负责把数据从一个节点发送到另一个或多个节点。标准格式的数据帧有11位标识符,扩展格式有29位。我建议初学者先吃透标准格式,扩展格式只是标识符变长了,结构逻辑完全一样。
一个完整的数据帧由7个段组成:
| 段名称 | 位数 | 作用 |
|---|---|---|
| 帧起始(SOF) | 1 | 同步所有节点,标志数据帧开始 |
| 仲裁段 | 12或32 | 包含标识符和RTR位,决定优先级 |
| 控制段 | 6 | 包含IDE位、保留位和DLC(数据长度码) |
| 数据段 | 0~64 | 实际要传输的数据,最多8字节 |
| CRC段 | 16 | 循环冗余校验,检测传输错误 |
| ACK段 | 2 | 接收节点确认收到正确数据 |
| 帧结束(EOF) | 7 | 标志数据帧结束 |
这里有个关键点:数据段最多8字节。为什么是8?不是16或32?我记得早期设计时,工程师们认为汽车控制信号通常不会超过8字节,而且短帧有利于实时性。这个设计一直沿用至今,虽然现在有些应用觉得8字节不够用,但CAN FD(灵活数据速率)已经扩展到了64字节,那是后话了。
避坑指南:我曾经在一个项目中,看到有人把超过8字节的数据硬塞进CAN帧里,结果数据被截断,接收端解析出来的值全是错的。记住:CAN数据段最多8字节,超过的部分必须分包发送。
2.2 CAN远程帧与错误帧
除了数据帧,CAN还有两种重要的帧类型:远程帧和错误帧。
2.2.1 远程帧
远程帧的作用是“请求数据”。一个节点可以发送远程帧,告诉另一个节点:“请把你某条ID的数据发给我”。远程帧的结构和数据帧几乎一样,唯一的区别是RTR位(远程传输请求位)。
- 数据帧的RTR位:显性(0),表示有数据要发送
- 远程帧的RTR位:隐性(1),表示请求数据
远程帧没有数据段,所以DLC(数据长度码)虽然存在,但实际不传输数据。DLC的作用是告诉被请求节点:你发回来的数据应该有多长。
个人经验:远程帧在实际项目中用得不多。我见过很多工程师直接用数据帧轮询,而不是用远程帧。原因很简单:远程帧需要接收节点额外处理,而数据帧轮询更直观。但如果你追求总线利用率,远程帧确实能减少不必要的广播流量。
2.2.2 错误帧
错误帧是CAN协议的自愈机制。当任何节点检测到总线错误时,它会立即发送错误帧,通知所有节点:“刚才那帧数据有问题,大家别用”。
错误帧由两部分组成:
- 错误标志:6个连续的显性位(主动错误)或6个连续的隐性位(被动错误)
- 错误界定符:8个连续的隐性位
CAN协议定义了5种错误类型:
| 错误类型 | 检测条件 | 我遇到过的案例 |
|---|---|---|
| 位错误 | 发送节点监控总线,发现发送的电平和实际电平不一致 | 曾经有个节点驱动能力不足,发送显性位时拉不到底,被自己检测到位错误 |
| 填充错误 | 连续6个相同电平出现(违反位填充规则) | 某次干扰导致总线出现连续6个隐性位,整个网络瞬间报错 |
| CRC错误 | 接收节点计算的CRC与发送节点的不一致 | 线束过长导致信号衰减,CRC经常算不对 |
| 形式错误 | 帧格式中固定为隐性/显性的位出现相反电平 | 很少单独出现,通常伴随其他错误 |
| ACK错误 | 发送节点没有收到ACK确认 | 节点离线或总线断开时最常见 |
重要提醒:错误帧不是用来“处理”的,而是用来“避免”的。如果你的系统频繁出现错误帧,说明硬件设计或软件配置有问题。我曾经调试过一个项目,错误帧占到了总线流量的30%,最后发现是终端电阻没焊好。
2.3 CAN总线仲裁机制
仲裁机制是CAN协议最精妙的设计之一。它解决了“多个节点同时发送时谁先发”的问题,而且不需要中央调度器。
仲裁的原理很简单:标识符越小,优先级越高。所有节点同时发送时,总线会逐位比较标识符。显性位(0)会覆盖隐性位(1)。谁先发送隐性位而别人发送显性位,谁就失去仲裁,自动转为接收状态。
举个例子:
- 节点A发送标识符:0x123(二进制:0001 0010 0011)
- 节点B发送标识符:0x456(二进制:0100 0101 0110)
从最高位开始比较:
- 第1位:A发0,B发0 → 相同,继续
- 第2位:A发0,B发1 → A的显性位覆盖B的隐性位 → B失去仲裁
B检测到自己失去仲裁后,立即停止发送,转为接收模式。A继续发送剩余数据,整个过程对A来说是透明的,它根本不知道B曾经想发数据。
关键理解:仲裁机制保证了“高优先级帧零延迟”。低优先级帧不会阻塞高优先级帧,因为仲裁是在发送过程中实时完成的。这比传统的令牌环或主从轮询方式高效得多。
下面这张图展示了仲裁过程的完整流程:
仲裁机制有几个重要特性:
- 非破坏性:获胜节点不受影响,继续发送
- 实时性:高优先级帧的延迟只取决于自身长度,不受低优先级帧影响
- 公平性:所有节点都有机会发送,只是优先级不同
设计建议:分配标识符时,把实时性要求高的信号(如发动机转速、刹车状态)放在小ID,把非实时信号(如车窗状态、座椅调节)放在大ID。我习惯把ID范围分成几个区间:0x000~0x0FF给关键控制信号,0x100~0x3FF给状态信息,0x400~0x7FF给诊断和配置数据。
2.4 位填充机制
位填充是CAN协议保证时钟同步的重要手段。规则很简单:连续发送5个相同电平后,自动插入一个相反电平。
为什么要这么做?因为CAN节点靠电平跳变来同步时钟。如果长时间没有跳变(比如连续发送30个隐性位),节点的时钟就会漂移,导致采样错误。
位填充的影响:
- 数据帧的实际长度 = 固定位 + 数据位 + 填充位(最多增加约20%)
- 填充位由发送节点自动插入,接收节点自动移除,软件不需要关心
- 错误帧和帧结束段不进行位填充
曾经踩过的坑:有一次我在做总线负载率计算时,忘了考虑位填充的影响。结果实际负载率比理论值高了15%,导致系统在高负载下频繁丢帧。从那以后,我计算负载率时都会预留20%的余量给位填充。
2.5 本章小结
CAN协议的核心就三件事:帧结构、错误处理、仲裁机制。数据帧是主力,远程帧是辅助,错误帧是保镖。仲裁机制保证了高优先级帧的实时性,位填充保证了时钟同步的可靠性。
理解这些基础,你才能设计出稳定可靠的CAN通信系统。下一层我们要深入CAN控制器的硬件设计,看看这些协议是如何在芯片层面实现的。