第二章 诊断协议基础:KWP2000回顾、CAN总线基础、UDS与CAN的绑定关系

各位同学,欢迎来到第二章。

上一章我们聊了UDS的总体框架,算是打了个照面。这一章,咱们得把地基夯实了。说白了,UDS不是凭空冒出来的,它有自己的“前辈”——KWP2000。而且,UDS和CAN总线是“黄金搭档”,不理解CAN,你就没法真正理解UDS是怎么在车上跑起来的。

我个人习惯,学任何新东西之前,先看看它从哪来,再看看它依赖什么。这样心里才有底。

2.1 回顾KWP2000:UDS的“老大哥”

KWP2000,全称是Keyword Protocol 2000。在UDS成为主流之前,它可是诊断协议里的“扛把子”。

我记得刚入行那会儿,项目里用的还是KWP2000。那时候的ECU,资源紧张得很,KWP2000算是比较“省吃俭用”的协议了。

2.1.1 KWP2000的核心特点

  • 基于关键字寻址:诊断仪发送一个“关键字”来唤醒ECU,有点像对暗号。
  • 支持多种物理层:它可以在K线(ISO 9141)上跑,也可以在CAN线上跑。这一点和UDS很像,但UDS更“专一”一些。
  • 报文结构:KWP2000的报文格式相对简单,但灵活性不如UDS。

核心区别:KWP2000的“服务ID”和“数据”是混在一起的,而UDS把服务ID和子功能分得很清楚。你想想看,UDS这种设计,扩展性是不是强多了?

2.1.2 从KWP2000到UDS的演进

为什么我们要抛弃KWP2000,转向UDS?

原因其实很简单:标准化和灵活性。KWP2000虽然好用,但各家OEM(原始设备制造商)都有自己的“小九九”,导致协议实现五花八门。UDS的出现,就是为了统一江湖。

我在项目中遇到过,同一个诊断仪,换了个品牌的ECU,KWP2000的报文就得重新适配。那叫一个头疼。UDS就好多了,大家遵循同一个标准,兼容性问题少了一大半。

我的建议:如果你还在维护老项目,KWP2000的知识还有点用。但新项目,直接上UDS,别犹豫。

2.2 CAN总线基础:UDS的“高速公路”

UDS本身只是一个“应用层”协议,它得有个“运输工具”才能把数据发出去。这个工具,在汽车领域,绝大多数时候就是CAN总线。

CAN总线,全称Controller Area Network。它不是什么新鲜玩意儿,但胜在稳定、可靠、实时性好。说白了,它就是为汽车这种“恶劣”环境量身定做的。

2.2.1 CAN总线的物理层与数据链路层

这部分内容,教科书上讲得很多。我挑几个和UDS关系最密切的点来说。

  • 差分信号:CAN用CAN_H和CAN_L两根线传输,抗干扰能力极强。你想想看,发动机舱里电磁干扰那么严重,单端信号早就废了。
  • 帧格式:CAN有标准帧(11位ID)和扩展帧(29位ID)。UDS通常使用扩展帧,因为29位ID能容纳更多的信息,比如源地址、目标地址。
  • 仲裁机制:CAN总线是“多主”的,谁都能发。但万一两个节点同时发呢?靠ID仲裁,ID越小,优先级越高。UDS的诊断报文,ID通常设得很小,保证它不会被其他报文“挤掉”。

注意:CAN总线的“位填充”机制,有时候会坑人。我曾经调试一个UDS刷写功能,发现数据总是对不齐,查了半天,原来是CAN控制器在连续5个相同位后自动插入了一个反向位。嗯,这里要注意,你的上位机软件得能正确处理这个。

2.2.2 CAN报文的结构

一个标准的CAN数据帧,长这样:

| SOF | 仲裁场 | 控制场 | 数据场 | CRC场 | ACK场 | EOF |
|  1  |  12/32 |   6    | 0-8字节 |  16   |   2   |  7  |

对于UDS来说,最重要的就是仲裁场数据场

  • 仲裁场:决定了这条报文是发给谁的,以及它的优先级。
  • 数据场:最多8个字节。UDS的请求和响应,就塞在这8个字节里。

关键点:CAN报文的数据场只有8个字节。这意味着,如果你的UDS诊断数据超过8个字节,就得用“多帧传输”了。这就是我们后面要讲的“TP层”(传输协议层)的由来。

2.3 UDS与CAN的绑定关系:怎么“塞”进去的?

好了,现在我们知道UDS是应用层,CAN是传输层。那UDS的请求和响应,具体是怎么封装到CAN报文里的呢?

说白了,就是把UDS的“服务ID”和“数据”,直接放到CAN报文的数据场里

2.3.1 单帧诊断请求

假设我们要发送一个“读取ECU硬件版本”的请求。UDS的服务ID是0x22(ReadDataByIdentifier),数据标识符是0xF190。

那么,CAN报文的数据场就是:

| 字节0 | 字节1 | 字节2 | 字节3 | 字节4 | 字节5 | 字节6 | 字节7 |
|  0x03 |  0x22 |  0xF1 |  0x90 |  0x00 |  0x00 |  0x00 |  0x00 |
  • 字节0 (0x03):这是“单帧”的标识,0x03表示后面有3个有效字节(0x22, 0xF1, 0x90)。
  • 字节1 (0x22):UDS的服务ID。
  • 字节2-3 (0xF1, 0x90):数据标识符。
  • 字节4-7:填充字节,没用。

一个小技巧:字节0的高4位(0x0)表示这是单帧,低4位(0x03)表示数据长度。这个格式,在ISO 15765-2(TP层协议)里有明确定义。

2.3.2 多帧诊断请求

如果数据超过8个字节,比如我们要刷写一个Bootloader,数据可能有几千字节。这时候,就得用多帧了。

多帧传输分为两步:

  1. 第一帧(FF):告诉ECU,我要发一个大包,总长度是多少。
  2. 后续帧(CF):把数据分成8字节一段,一段一段发过去。

举个例子,第一帧的数据场可能是:

| 字节0 | 字节1 | 字节2 | 字节3 | ... |
|  0x10 |  0x0A |  0x22 |  0xF1 | ... |
  • 字节0 (0x10):高4位0x1表示这是第一帧,低4位0x0是长度的高4位。
  • 字节1 (0x0A):这是长度的低8位。0x10和0x0A组合起来,0x010A = 266字节,表示后面总共有266个字节要发。
  • 字节2开始:就是UDS的实际数据了。

避坑指南:我曾经遇到过一个ECU,它只支持单帧,不支持多帧。我发了一个多帧请求,它直接不理我。后来查手册才发现,这个ECU的TP层实现有bug。所以,写代码前,一定要确认ECU的TP层能力。

2.3.3 网络层寻址:物理请求与功能请求

UDS在CAN上跑,还有一个重要概念:寻址方式

  • 物理请求:点对点。诊断仪发给一个特定的ECU。CAN ID里会带上目标ECU的地址。
  • 功能请求:广播。诊断仪发给总线上所有ECU。所有ECU都会收到,但只有符合条件的ECU才会响应。

举个例子:

请求类型 CAN ID (29位扩展帧) 说明
物理请求 0x18DA00F1 发给地址为0xF1的ECU
功能请求 0x18DB00F1 发给所有地址为0xF1的ECU(广播)

你看,CAN ID里最后两个字节(0x00F1)就是目标地址。物理请求和功能请求,就差了一个字节(0xDA vs 0xDB)。

我的经验:功能请求在“刷写”场景下特别有用。比如你要让所有ECU同时进入“编程模式”,发一个功能请求,所有ECU都响应,效率高得多。

2.4 本章小结

这一章,我们回顾了KWP2000,了解了CAN总线的基础,最重要的是,搞清楚了UDS是怎么和CAN“绑定”在一起的。

说白了,UDS就是应用层的“语言”,CAN就是底层的“高速公路”。UDS的请求和响应,被封装成CAN报文的数据场,通过CAN总线发出去。

下一章,我们会深入UDS的“服务”层,看看那些0x10、0x22、0x2E到底是什么意思。到时候,你会发现,理解了CAN和UDS的绑定关系,后面的内容就顺理成章了。

课后思考:如果CAN报文的数据场只有8个字节,那UDS的“多帧传输”是怎么保证数据完整性的?提示:想想CRC和流控制帧(FC)。


公众号:蓝海资料掘金营,微信deep3321