3. CAN数据链路层:CAN帧结构、仲裁机制与位填充
大家好,我是老张。今天咱们聊聊CAN总线最核心的部分——数据链路层。说实话,搞飞控这么多年,我见过太多因为对CAN帧理解不透彻导致的通信故障。有一次在试飞现场,无人机突然失控,排查了整整两天,最后发现是远程帧用错了地方。嗯,从那以后,我对这部分内容就格外上心。
数据链路层,说白了就是负责把物理层的0和1组织成有意义的“消息”。CAN协议在这一层定义了五种帧类型,以及一套精妙的仲裁和同步机制。咱们一个一个来看。
3.1 CAN帧结构:五种帧类型
CAN总线上一共就五种帧:数据帧、远程帧、错误帧、过载帧和帧间隔。我个人习惯把前两种叫“正经干活”的帧,后三种叫“维护秩序”的帧。
3.1.1 数据帧(Data Frame)
这是最常用的帧,用来传输实际数据。标准格式有11位标识符,扩展格式有29位。我在飞控系统里,传感器数据、控制指令全用数据帧传。
数据帧的结构分为七个段:
- 帧起始(SOF):1个显性位,告诉总线“我要发消息了”
- 仲裁段:包含标识符和RTR位。标准帧11位ID,扩展帧29位ID
- 控制段:6位,包含IDE位、保留位和数据长度码(DLC)
- 数据段:0~8字节,你要传的数据就在这儿
- CRC段:15位CRC校验码+1位CRC界定符
- ACK段:2位,发送方发隐性位,接收方拉显性表示确认
- 帧结束(EOF):7个隐性位
关键点:数据段最多8字节。为什么是8?因为CAN总线设计之初考虑的是汽车短消息通信,8字节足够传一个传感器读数或一条控制指令。飞控里传IMU数据,一次6轴数据正好6字节,很合适。
3.1.2 远程帧(Remote Frame)
远程帧是用来请求数据的。它没有数据段,但DLC字段要填你期望接收的数据长度。我曾经在项目中犯过一个错误:用远程帧去请求飞控的状态信息,结果因为DLC填错了,接收方一直没响应。后来查手册才发现,远程帧的DLC必须和对应数据帧的DLC一致。
注意:远程帧的RTR位是隐性(1),数据帧的RTR位是显性(0)。这意味着在仲裁时,数据帧优先级高于远程帧。你想想看,如果总线上同时出现一个数据帧和一个请求同一ID的远程帧,数据帧会赢。
3.1.3 错误帧(Error Frame)
当节点检测到总线错误时,就会发送错误帧。它由6个显性位的错误标志和8个隐性位的错误界定符组成。我建议你在调试时重点关注错误帧——它往往是硬件问题的第一信号。
3.1.4 过载帧(Overload Frame)
过载帧用于节点忙不过来时请求延迟。结构跟错误帧很像,但触发条件不同。实际项目中,过载帧很少见,除非你的MCU处理速度跟不上CAN总线速率。
3.1.5 帧间隔(Interframe Space)
帧与帧之间需要3个隐性位的间隔。这就像说话时的换气,给总线一点恢复时间。
3.2 仲裁机制:CSMA/CR
CAN总线用的是CSMA/CR(载波监听多路访问/冲突解决),不是以太网的CSMA/CD。区别在哪?CSMA/CD是“撞了就退”,CSMA/CR是“边发边比,谁小谁赢”。
具体怎么工作的?
- 所有节点监听总线,空闲就发
- 发送时同时监听总线电平
- 如果自己发隐性位(1),但总线上是显性位(0),说明有更高优先级的节点在发,自己立即退出
- 胜出的节点继续发送,失败的节点自动转为接收
为什么会这样?因为CAN总线是“线与”逻辑:显性位(0)会覆盖隐性位(1)。所以标识符越小,优先级越高。我在飞控系统里,把急停指令的ID设为0x001,普通状态信息设为0x100,这样急停永远优先。
实战技巧:分配CAN ID时,把实时性要求高的消息放在小ID。比如飞控的紧急刹车指令用0x001,姿态数据用0x010,日志信息用0x100。这样即使总线拥堵,关键指令也能优先通过。
3.3 位填充机制(Bit Stuffing)
位填充是为了保证时钟同步。CAN协议规定:连续发送5个相同电平后,必须插入一个相反电平。接收方看到5个相同位后,自动丢弃第6位(填充位)。
举个例子:你要发送的数据是11111 00000,经过位填充后变成111110 000001。多出来的0和1就是填充位。
为什么需要这个机制?因为CAN节点靠电平跳变来同步时钟。如果长时间没有跳变,时钟就会漂移。你想想看,如果连续发30个隐性位,接收方的时钟可能已经偏了半个位时间,再读数据就容易出错。
注意:错误帧的6个显性位故意违反了位填充规则。这是故意的——错误帧就是要打破常规,让所有节点都知道出问题了。
位填充的范围包括:SOF、仲裁段、控制段、数据段和CRC段。ACK段和EOF段不参与填充。我在调试时,经常用示波器看CAN波形,数填充位来判断数据是否正确。
3.4 知识体系总览
下面这张图把CAN数据链路层的核心逻辑串起来了。我画图时习惯把仲裁机制放在中间,因为它决定了整个总线的行为。
3.5 实战中的避坑指南
最后分享几个我踩过的坑:
- ID分配要留余量:别把ID从0x001开始排满,留一些给后期扩展。我曾经因为ID不够用,不得不重新设计整个CAN网络。
- 远程帧慎用:能用数据帧主动上报就别用远程帧去轮询。远程帧会增加总线负载,而且容易造成响应延迟。
- 位填充影响实时性:极端情况下,位填充会让数据帧长度增加最多50%。计算总线负载时,记得把这个余量算进去。
- 错误帧是双刃剑:它能帮你发现问题,但频繁的错误帧会拖垮整个总线。我建议在飞控系统中设置错误计数器,超过阈值就重启节点。
好了,CAN数据链路层的内容就讲到这里。记住一句话:帧结构是骨架,仲裁是规则,位填充是保障。搞懂了这三样,CAN总线通信你就掌握了七成。
公众号:蓝海资料掘金营,微信deep3321