3、CAN总线协议详解:CAN帧结构、仲裁机制、位填充规则

各位同学,今天我们来啃一块硬骨头——CAN总线协议。说实话,我刚入行那会儿,觉得CAN协议就是一堆帧啊、位啊的,枯燥得很。直到有一次在调试一个车载网关时,总线莫名其妙就挂了,我拿着示波器看了三天三夜,才把帧结构、仲裁和位填充这几个东西彻底搞明白。从那以后,我再也不敢小看这些基础概念了。

CAN总线,说白了就是一套“谁先说话谁有理”的通信规则。它不像I2C那样有主机从机之分,也不像UART那样点对点。CAN总线上所有节点都是平等的,谁想发数据,谁就抢。那怎么抢?靠的就是我们今天要讲的仲裁机制。

3.1 CAN帧结构:四种帧类型

CAN协议定义了四种帧:数据帧、远程帧、错误帧、过载帧。我个人习惯把前两种叫“正经帧”,后两种叫“救火帧”。

3.1.1 数据帧

数据帧是干活的主力。它负责把数据从一个节点发到另一个或多个节点。结构如下:

字段 长度 说明
帧起始(SOF) 1 bit 显性电平,标志一帧开始
仲裁场 11/29 bit ID + RTR位,决定优先级
控制场 6 bit IDE、保留位、DLC(数据长度)
数据场 0~64 bit 实际要传的数据
CRC场 16 bit 循环冗余校验
ACK场 2 bit 接收节点回应
帧结束(EOF) 7 bit 隐性电平

这里有个细节我提醒大家注意:数据场最多8个字节。为什么是8?不是16也不是32?我在项目中遇到过有人非要把64字节的数据塞进一帧里,结果只能拆成8帧发,反而增加了总线负载。CAN设计成8字节,是为了保证实时性——你想想看,如果一帧传太多数据,低优先级帧可能永远发不出去。

3.1.2 远程帧

远程帧,说白了就是“请求帧”。一个节点可以发远程帧,让另一个节点把数据发过来。它和数据帧长得几乎一样,唯一的区别是RTR位:数据帧的RTR是显性(0),远程帧的RTR是隐性(1)。

关键区别:远程帧没有数据场。它的DLC字段表示请求的数据长度,但实际不传数据。嗯,这里要注意,有些初学者会搞混,以为远程帧也带数据。

3.1.3 错误帧

错误帧是CAN总线的“警报器”。当节点检测到总线错误时,就会发错误帧。它由两部分组成:

  • 错误标志:6个连续显性位(主动错误)或6个连续隐性位(被动错误)
  • 错误界定符:8个隐性位

我曾经调试过一个项目,总线每隔几秒就出现一次错误帧。一开始以为是干扰,后来发现是一个节点的晶振偏差太大,导致位时序错位。那次教训让我养成了一个习惯:新节点上总线前,先测它的时钟精度

3.1.4 过载帧

过载帧,就是节点喊“慢点,我跟不上了”。它和错误帧结构类似,但触发条件不同:

  • 接收节点内部缓冲区满了
  • 接收节点在帧间隙检测到显性位

过载帧在实际项目中不常见。我做了十年CAN总线,只遇到过两次。一次是一个低端MCU处理不过来高速数据流,频繁发过载帧,最后只能降低波特率解决。

3.2 仲裁机制:谁先说话谁有理

CAN总线的仲裁机制,是我觉得最巧妙的设计之一。它基于“线与”逻辑:显性位(0)会覆盖隐性位(1)。

仲裁过程是这样的:

  1. 所有要发送的节点同时发送ID位
  2. 每个节点在发送位的同时,监听总线电平
  3. 如果发送的是隐性位(1),但总线上是显性位(0),说明有更高优先级的节点在发
  4. 这个节点立即停止发送,转为接收

你想想看,这个过程有多快?位级别的仲裁,不需要任何额外的握手信号。ID值越小,优先级越高。所以0x000是最高优先级,0x7FF是最低优先级。

实战技巧:我在设计系统时,会把关键控制信号(比如刹车、急停)分配小ID,把诊断信息、日志信息分配大ID。这样即使总线负载很高,关键信号也能第一时间发出去。

3.3 位填充规则:保证同步的“小把戏”

位填充,说白了就是防止总线“睡着”。CAN总线靠电平跳变来同步时钟。如果连续出现太多相同的位(比如5个连续的隐性位),接收节点可能丢失同步。

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

举个例子:

原始数据:11111 00000 11111
填充后:  111110 000001 111110

接收节点收到后,会自动去掉这些填充位,恢复原始数据。

注意:位填充只适用于SOF到CRC场之间的数据。ACK场、EOF、帧间隔不进行位填充。我曾经见过一个新手,在计算CRC时把填充位也算进去了,结果校验永远通不过。

位填充还有一个副作用:增加总线负载。理论上,最坏情况下每5个位就要插入1个填充位,负载增加20%。所以你在计算总线利用率时,一定要留出这个余量。

我记得有一次做总线负载分析,算出来利用率只有60%,觉得没问题。结果实际测试时,发现某些数据模式触发了大量位填充,实际负载飙到了75%。从那以后,我算负载时都会加一个1.2的安全系数

3.4 总结与避坑指南

好了,CAN帧结构、仲裁机制、位填充规则,这三个东西是CAN协议的基石。我给大家总结几个实战要点:

  • 帧结构:记住数据帧8字节上限,远程帧无数据场
  • 仲裁:ID越小优先级越高,关键信号用小ID
  • 位填充:算负载时留20%余量,CRC计算不要包含填充位

我曾经犯过一个低级错误:在设计一个多节点系统时,把所有节点的ID都设成了0x100。结果一上电,总线直接瘫痪——所有节点同时发数据,仲裁机制完全失效。嗯,这个教训让我记住了:CAN总线上每个节点的ID必须唯一,这是最基本的要求。

下一章,我们会讲CAN的位时序和同步机制。到时候我会分享一个我亲手踩过的坑——波特率配置不当导致总线间歇性通信失败。敬请期待。