2. CAN总线数据链路层基础:CAN帧结构、ID仲裁与位填充
大家好,我是老周。今天咱们聊聊CAN总线数据链路层。很多工程师调试CAN通信,上来就抓波形、看报文,但帧结构都没吃透。我见过太多人把远程帧当数据帧解析,或者搞不懂为什么自己的节点总发不出消息。说白了,数据链路层就是CAN通信的“交通规则”,不懂规则,上路必翻车。
2.1 CAN帧的四种类型
CAN总线上一共就四种帧:数据帧、远程帧、错误帧、过载帧。我习惯把它们分成两类——正常通信用的,和异常处理用的。
| 帧类型 | 用途 | 谁发起的 | 常见场景 |
|---|---|---|---|
| 数据帧 | 发送数据 | 发送节点 | ECU上报车速、转速 |
| 远程帧 | 请求数据 | 接收节点 | Tester请求诊断数据 |
| 错误帧 | 报告总线错误 | 检测到错误的节点 | 位错误、填充错误 |
| 过载帧 | 请求延迟 | 接收节点 | 接收缓冲区满 |
2.2 数据帧结构详解
数据帧是咱们打交道最多的帧。标准帧11位ID,扩展帧29位ID。我建议初学者先把标准帧吃透。
一个标准数据帧由以下字段组成:
- SOF(帧起始):1位显性位,同步所有节点
- 仲裁场:11位ID + RTR位(数据帧为显性)
- 控制场:IDE位 + r0 + DLC(4位数据长度码)
- 数据场:0~8字节数据
- CRC场:15位CRC + 1位CRC界定符
- ACK场:1位ACK槽 + 1位ACK界定符
- EOF(帧结束):7位隐性位
关键点: DLC表示数据长度,范围0~8。但注意,DLC=0不代表帧无效,远程帧的DLC就是0。我遇到过有人把DLC=0的帧直接丢弃,结果丢掉了远程请求。
2.3 ID仲裁机制——谁先说话?
CAN总线是“多主”架构,所有节点都能同时发消息。那冲突了怎么办?靠ID仲裁。
仲裁的原理很简单:ID越小,优先级越高。发送时每个节点逐位比较,遇到显性位(0)就赢,隐性位(1)就退。
举个例子:
- 节点A发ID=0x100(二进制:001 0000 0000)
- 节点B发ID=0x200(二进制:010 0000 0000)
在第二位,A发0(显性),B发1(隐性)。A赢了,B自动退出发送,转为接收。整个过程在几个微秒内完成,完全硬件自动处理。
2.4 远程帧与数据帧的区别
远程帧长得跟数据帧几乎一样,就一个地方不同:RTR位为隐性(1)。数据帧的RTR是显性(0)。
远程帧没有数据场,DLC表示请求的数据长度。收到远程帧的节点,如果ID匹配,就发一个同ID的数据帧回去。
| 对比项 | 数据帧 | 远程帧 |
|---|---|---|
| RTR位 | 显性(0) | 隐性(1) |
| 数据场 | 0~8字节 | 无 |
| 用途 | 发送数据 | 请求数据 |
| 仲裁优先级 | 高于同ID远程帧 | 低于同ID数据帧 |
2.5 错误帧与过载帧
错误帧是总线上的“警报”。任何节点检测到错误,就会发6个连续显性位(主动错误标志),然后发8个隐性位(错误界定符)。
错误帧有几种触发条件:
- 位错误:发送的位和总线状态不一致
- 填充错误:连续5个相同位后没出现填充位
- CRC错误:CRC校验不通过
- 形式错误:固定格式位错误
- ACK错误:发送节点没收到ACK
过载帧呢?它用于接收节点告诉总线“我忙,慢点发”。过载帧由6个显性位+8个隐性位组成,跟错误帧很像,但触发条件不同——过载帧只在帧间空间发送。
2.6 位填充规则
位填充是CAN总线保证同步的机制。规则很简单:连续发送5个相同位后,自动插入一个相反位。
比如你发:00000,总线会变成:000001。这个填充位是接收端自动移除的,对上层透明。
位填充的作用:
- 保证时钟同步:每个节点靠跳变沿同步,长时间没跳变会失步
- 防止直流分量累积:平衡显性/隐性比例
- 错误检测:连续6个相同位就是填充错误
注意: 位填充覆盖SOF到CRC场,ACK场和EOF不填充。所以EOF是7个连续隐性位,这是合法的。
2.7 知识体系总览
下面这张图把CAN数据链路层的核心知识点串起来了。我建议你把它存下来,调试时对照着看。
2.8 本章小结
数据链路层是CAN通信的基石。四种帧各有用途,ID仲裁决定了谁先说话,位填充保证了同步和错误检测。我个人觉得,搞懂这些,你就能看懂80%的CAN通信问题了。
嗯,这里要提醒一句:别光看书,去抓几段波形看看。把数据帧、远程帧、错误帧都认出来,比背一百遍定义都管用。我当年就是靠一台示波器,把CAN协议啃下来的。