1. CAN协议基础:从物理层到仲裁机制

各位同学,咱们今天正式开讲CAN协议。说实话,CAN总线在汽车电子里太常见了,从发动机ECU到车窗控制器,几乎每个节点都在用它通信。我最早接触CAN是在做发动机标定项目的时候,那时候被总线错误帧搞得焦头烂额。后来才明白,很多问题都是因为没吃透基础。

这一章,咱们就把CAN协议的底子打牢。我会从物理层特性、报文帧结构、仲裁机制三个维度展开。你想想看,搞懂了这些,后面学CANape和INCA的标定配置,就会顺手很多。

1.1 CAN总线物理层特性

CAN总线物理层,说白了就是信号怎么在线上跑。它用的是差分电压传输,两根线——CAN_H和CAN_L。为什么要用差分?抗干扰能力强啊。车上的电磁环境多复杂,点火线圈、电机、继电器,哪个不是干扰源?差分信号能把这些共模噪声抵消掉。

关键参数速查表

参数 高速CAN (ISO 11898-2) 低速CAN (ISO 11898-3)
最大速率 1 Mbps 125 kbps
总线长度(1 Mbps时) 约40 m 约500 m
显性电平(Dominant) CAN_H ≈ 3.5V, CAN_L ≈ 1.5V CAN_H ≈ 4V, CAN_L ≈ 1V
隐性电平(Recessive) CAN_H ≈ 2.5V, CAN_L ≈ 2.5V CAN_H ≈ 1.75V, CAN_L ≈ 3.25V
终端电阻 120 Ω(两端各一个) 通常不需要

这里有个坑,我必须要提醒你。终端电阻不是随便焊上去的。高速CAN总线两端必须各接一个120Ω电阻,作用是匹配阻抗、抑制信号反射。我曾经在一个项目里看到有人只在一端接了电阻,结果总线在长距离传输时波形反射得一塌糊涂,通信时好时坏。排查了两天才找到原因。

注意:终端电阻必须接在总线物理拓扑的两端,而不是ECU内部随便找个地方焊。如果总线是星形拓扑(虽然不推荐),终端电阻的位置更要仔细计算。

再说说位时序。CAN总线是异步通信,没有单独的时钟线。每个节点靠自己的晶振来采样总线电平。为了保证所有节点能同步,CAN协议定义了同步段(Sync_Seg)、传播段(Prop_Seg)、相位缓冲段1(Phase_Seg1)和相位缓冲段2(Phase_Seg2)。

嗯,这里要注意,采样点位置很关键。我一般习惯把采样点设在位时间的87.5%左右,也就是Phase_Seg1比Phase_Seg2长一点。这样能容忍更大的晶振误差。如果你用CANape做标定,可以在DBC文件里配置这些参数,但底层驱动里也得对得上。

1.2 CAN报文帧结构

CAN协议定义了四种帧类型:数据帧、远程帧、错误帧、过载帧。咱们做标定,打交道最多的是数据帧和远程帧。错误帧嘛,你肯定不想看到它,但真出现了,得知道怎么分析。

1.2.1 数据帧

数据帧是干活的主力。它把ECU里的传感器值、控制参数、标定数据从一个节点传到另一个节点。数据帧分标准帧(11位ID)和扩展帧(29位ID)。

我个人习惯,在标定场景里尽量用标准帧。为什么?帧头短,总线利用率高。29位ID虽然地址空间大,但多出来的18位ID在仲裁时没什么优势,反而增加了总线负载。

标准数据帧结构(逐字段)

字段 位数 说明
SOF (Start of Frame) 1 显性电平,标志帧开始
仲裁场 (Identifier + RTR + IDE) 12 11位ID + 1位RTR(数据帧为0)
控制场 (r0 + DLC) 6 1位保留位 + 4位数据长度码
数据场 0~64 0~8字节,标定数据放这里
CRC场 16 15位CRC + 1位CRC界定符
ACK场 2 1位ACK Slot + 1位ACK界定符
EOF (End of Frame) 7 隐性电平,标志帧结束

数据场最多8个字节。做标定的时候,经常需要传超过8字节的参数,比如一个标定MAP表。这时候就得用多帧传输或者分段传输。CANape和INCA都支持这种机制,但底层还是拆成多个数据帧来发。

1.2.2 远程帧

远程帧,说白了就是“请求帧”。一个节点发远程帧,告诉另一个节点:“把你那个ID的数据发给我”。远程帧和数据帧结构几乎一样,唯一的区别是RTR位为1(隐性)。

我记得在做一个OBD标定项目时,诊断仪就是用远程帧来请求ECU的故障码。ECU收到远程帧后,把对应的数据帧发回来。这个机制在标定工具链里也很常用——标定工具发远程帧,ECU响应数据帧,完成一次测量值的读取。

1.2.3 错误帧

错误帧是CAN节点发现自己或别人出错时发的。它由6个连续的显性位(错误标志)和8个隐性位(错误界定符)组成。错误帧一出现,总线上的所有节点都会检测到,然后丢弃当前正在传输的帧。

实战经验:如果你在CANape的Trace窗口里看到大量错误帧,别急着怀疑硬件。先检查一下总线负载率是不是太高了?或者某个节点的位时序配置和总线其他节点不一致?我曾经遇到一个案例,一个ECU的晶振偏差太大,导致采样点偏移,频繁产生错误帧。换了个晶振就好了。

1.2.4 过载帧

过载帧用得比较少。它表示节点“忙不过来”,需要延迟下一个数据帧或远程帧的发送。过载帧的结构和错误帧类似,但触发条件不同。过载帧是节点主动发的,错误帧是节点检测到错误被动发的。

1.3 CAN总线仲裁机制

仲裁机制是CAN协议最巧妙的设计之一。多个节点同时发送时,谁优先级高谁先发,而且不会破坏正在发送的数据。怎么做到的?靠“线与”逻辑和ID的二进制比较。

你想想看,CAN总线是“显性”覆盖“隐性”。显性电平(逻辑0)比隐性电平(逻辑1)优先级高。当两个节点同时发送时,它们逐位比较ID。如果一个节点发送了隐性位(1),但总线上检测到显性位(0),说明有更高优先级的节点在发,这个节点就自动退出仲裁,转为接收状态。

举个例子:

  • 节点A发送ID:0x7F0(二进制:111 1111 0000)
  • 节点B发送ID:0x7E0(二进制:111 1110 0000)

从高位开始逐位比较。前6位都是1,没问题。第7位,节点A发1(隐性),节点B发0(显性)。节点A检测到总线上是0,知道自己输了,立刻停止发送,转为接收。节点B继续发完剩下的位。整个过程没有数据丢失,也没有冲突。

仲裁规则总结:

  • ID值越小,优先级越高(0x000最高,0x7FF最低)
  • 数据帧优先级高于远程帧(RTR位:数据帧=0,远程帧=1)
  • 标准帧优先级高于扩展帧(IDE位:标准帧=0,扩展帧=1)

做标定的时候,这个机制很重要。标定工具通常用高优先级ID(比如0x100~0x1FF)来发送标定指令和请求,确保标定操作不会被其他ECU的周期性报文打断。ECU的周期性状态报文,一般用低优先级ID(比如0x700~0x7FF)。

我曾经在标定一个变速箱ECU时,发现标定写入总是超时。排查后发现,变速箱ECU自己的状态报文ID是0x100,和标定工具的请求ID冲突了。每次标定工具发请求,变速箱ECU的状态报文也在发,两者ID相同,但RTR位不同,导致仲裁混乱。后来把状态报文ID改到0x700,问题解决。

1.4 本章知识体系总览

下面这张图,把CAN协议基础的三个核心模块串起来了。你可以把它当作本章的思维导图。

CAN协议基础 - 知识体系 物理层特性 报文帧结构 总线仲裁机制 差分信号 终端电阻 位时序 采样点 数据帧 远程帧 错误帧 过载帧 线与逻辑 ID优先级 逐位仲裁 无损传输 核心:物理层是基础,帧结构是载体,仲裁机制是灵魂 三者共同保证CAN总线在恶劣电磁环境下的可靠通信

这张图把本章的三个核心模块——物理层、帧结构、仲裁机制——以及它们各自的子知识点都列出来了。你可以看到,物理层是基础,帧结构是载体,仲裁机制是灵魂。三者缺一不可。

学习建议:如果你刚开始接触CAN,建议先花时间把物理层的位时序和采样点搞明白。很多通信问题,根源都在这里。你可以用CANape的CAN总线分析功能,抓一段波形看看,直观感受一下显性电平和隐性电平的区别。