2. CAN诊断基础回顾:CAN总线物理层与数据链路层、CAN诊断协议(ISO 15765)核心机制

好,咱们进入正题。这一章我打算带大家快速回顾一下CAN诊断的基础。你可能会问:“都做DoIP融合了,还回头看CAN干嘛?” 嗯,这个问题问得好。我个人习惯是,不管上层协议多花哨,底层的东西必须吃透。DoIP再厉害,最终落地时,很多诊断逻辑还是得跟CAN打交道。说白了,CAN诊断是根,DoIP是枝叶。

2.1 CAN总线物理层:那些容易被忽略的“硬”道理

先聊聊物理层。很多人觉得物理层就是两根线(CAN_H和CAN_L),没什么好讲的。但我告诉你,我在项目里踩过的坑,有一半都出在物理层上。

CAN总线用的是差分信号传输。什么意思?就是靠CAN_H和CAN_L的电压差来传递“显性”(Dominant)和“隐性”(Recessive)电平。显性对应逻辑0,隐性对应逻辑1。你想想看,为什么这么设计?抗干扰啊!两根线绞在一起,外界噪声对两根线的影响几乎一样,电压差基本不变。这招儿,比单端传输靠谱多了。

关键参数速查表(高速CAN,ISO 11898-2):

参数 典型值 说明
总线电平(显性) CAN_H: 3.5V, CAN_L: 1.5V 差分电压约2V
总线电平(隐性) CAN_H: 2.5V, CAN_L: 2.5V 差分电压约0V
终端电阻 120Ω(两端各一个) 用于消除信号反射
最大传输距离 40m @ 1Mbps 速率越低,距离越远

⚠️ 避坑指南: 我曾经在一个项目中,发现诊断通信时好时坏。查了三天,最后发现是终端电阻虚焊了。没有终端电阻,信号在总线末端会反射,造成位错误。记住,CAN总线两端必须各有一个120Ω电阻,缺一不可。

2.2 数据链路层:CAN帧的结构与仲裁机制

物理层搞定了,数据怎么打包发送?这就轮到数据链路层了。CAN 2.0规范定义了两种帧格式:标准帧(11位ID)和扩展帧(29位ID)。咱们做诊断,基本都用扩展帧,因为ID空间大,好规划。

一个CAN数据帧长什么样?我习惯把它拆成几个关键部分:

  • 帧起始(SOF): 一个显性位,告诉所有节点“我要发数据了”。
  • 仲裁场: 包含ID和RTR位。ID决定了消息的优先级,数值越小,优先级越高。
  • 控制场: 包含IDE位和DLC(数据长度码)。DLC告诉你后面跟了多少字节数据,范围是0~8。
  • 数据场: 0~8字节,诊断报文就塞在这里。
  • CRC场: 循环冗余校验,检查数据有没有传错。
  • ACK场: 接收节点如果正确收到,会在这一位发显性电平,告诉发送方“我收到了”。

这里有个很有意思的机制——总线仲裁。为什么CAN总线不会冲突?因为它是“线与”逻辑。多个节点同时发送时,谁先发隐性位(1),谁就自动退出发送。说白了,ID最小的那个节点,会赢得仲裁,继续发完它的数据。这个设计太巧妙了,不需要中央调度,完全分布式。

💡 我的经验: 设计诊断ID时,一定要把高优先级的报文(比如安全相关的)分配小ID。我见过一个项目,把诊断请求的ID设得比心跳报文还大,结果诊断一启动,心跳老丢,ECU就被误判为离线了。嗯,这种低级错误,咱们不能犯。

2.3 CAN诊断协议(ISO 15765)核心机制:从“发出去”到“收回来”

好了,CAN帧能发能收了。但诊断报文往往超过8个字节,怎么办?ISO 15765(也叫DoCAN)就是来解决这个问题的。它定义了如何在CAN数据场里传输诊断消息。

ISO 15765的核心,说白了就是“单帧”与“多帧”的传输机制。

2.3.1 单帧(SF, Single Frame)

如果诊断消息(比如一个UDS请求)加上协议控制信息,总长度不超过7个字节(CAN FD是64字节,但咱们先聊经典CAN),那就用一个CAN帧搞定。第一个字节的高四位是“0”,表示这是单帧。低四位表示数据长度。

// 举个例子:诊断请求“读取数据(22 01 F1 90)”
// 这是一个4字节的UDS请求
// 封装成CAN数据场:
// 字节0: 0x04  (高四位0=单帧,低四位4=数据长度)
// 字节1: 0x22  (UDS服务ID)
// 字节2: 0x01
// 字节3: 0xF1
// 字节4: 0x90
// 字节5-7: 填充(通常为0xAA或0xCC)

2.3.2 多帧(FF/CF, First Frame / Consecutive Frame)

当诊断消息超过7个字节时,就需要拆成多个CAN帧发送。流程是这样的:

  1. 第一帧(FF): 发送方先发一个特殊帧,告诉接收方“我要发一个长消息,总长度是XX字节”。第一个字节高四位是“1”,表示这是第一帧。低四位和第二个字节组合,表示总长度(最大4095字节)。
  2. 流控帧(FC, Flow Control): 接收方收到FF后,回复一个FC帧。它告诉发送方:“我准备好了,你可以发了。一次发几个包?间隔多久?” 这就像两个人打电话,一个人说“我要说一段话”,另一个人说“好,你说吧,一次说三句,每句间隔一秒”。
  3. 连续帧(CF, Consecutive Frame): 发送方收到FC后,开始按顺序发送数据块。每个CF帧的第一个字节高四位是“2”,低四位是序列号(从1开始,循环到15后归1)。

多帧传输的“握手”过程:

步骤 发送方 接收方 说明
1 发送FF(第一帧) - 告知总长度,例如256字节
2 - 发送FC(流控帧) BS=3(一次发3帧),STmin=10ms(帧间隔10ms)
3 发送CF#1, CF#2, CF#3 - 连续发送3帧,每帧间隔10ms
4 - 发送FC(继续流控) BS=3,继续发下一批
... ... ... 重复直到所有数据发完

⚠️ 我曾经踩过的坑: 有一次,ECU升级时总是超时失败。排查发现,Tester发送FC帧时,STmin设得太小(0ms),而ECU的CAN控制器处理不过来,导致连续帧丢失。后来我把STmin调到2ms,问题就解决了。记住,STmin不是越小越好,得看ECU的处理能力。

2.4 小结:CAN诊断的“魂”是什么?

回顾一下,CAN诊断的核心就三件事:

  • 物理层: 两根线,差分信号,终端电阻不能少。
  • 数据链路层: 帧结构、仲裁机制,ID决定优先级。
  • 传输协议(ISO 15765): 单帧搞定小数据,多帧握手传大数据。

你想想看,这些机制虽然看起来简单,但每一个细节都影响着诊断的可靠性。后面我们讲DoIP时,你会发现很多思想都是从CAN诊断借鉴过来的。比如DoIP的“路由激活”和“诊断连接”,本质上和CAN的“流控机制”是一个道理——都是在建立可靠的通信通道。

嗯,这一章就到这里。下一章我们正式进入DoIP的世界,看看以太网是怎么把诊断玩出花样的。