3. 诊断请求与响应机制:单帧与多帧传输、流控制协议、正响应与负响应

好,咱们进入第三章节。这一章可以说是UDS协议里最“硬核”的部分之一了。你想想看,诊断仪发一条请求,ECU怎么回?数据包太大塞不进一帧怎么办?ECU怎么告诉你“我听懂了”或者“你发的东西有问题”?这些就是咱们今天要聊透的东西。

我个人习惯把这一章分成三个核心模块:单帧与多帧传输流控制协议、以及正响应与负响应。咱们一个一个来。

3.1 单帧传输:最基础的“一问一答”

先说最简单的场景。诊断仪发一条请求,比如读取ECU的VIN码(车辆识别码)。如果VIN码很短,比如17个字节,加上服务ID和子功能,总共也就20来个字节。CAN标准帧的数据场最多8个字节,但20个字节显然塞不下。不过别急,咱们先看能塞下的情况。

单帧传输,说白了就是一条CAN帧搞定一个诊断请求或响应。它的格式很简单:

  • 第一个字节:PCI(协议控制信息),高4位表示帧类型(单帧是0x0),低4位表示数据长度(0-7)。
  • 后续字节:真正的诊断数据,比如服务ID、子功能、参数等。

举个例子,诊断仪发送“读取数据标识符”请求(服务ID = 0x22),数据标识符是0xF190。那么单帧长这样:

CAN数据场:0x03 0x22 0xF1 0x90
解释:
- 0x03:单帧(0x0),数据长度3字节(0x03)
- 0x22:服务ID
- 0xF1 0x90:数据标识符

ECU收到后,如果数据能塞进一帧,就回一个单帧响应。比如:

CAN数据场:0x06 0x62 0xF1 0x90 0x01 0x02 0x03
解释:
- 0x06:单帧,数据长度6字节
- 0x62:正响应(服务ID + 0x40)
- 0xF1 0x90:数据标识符
- 0x01 0x02 0x03:VIN码的前3个字节(假设)
我的小经验: 单帧传输最怕什么?怕你算错数据长度。我在项目中遇到过,有人把PCI的低4位写成了0x08,但实际数据只有7个字节。ECU解析时会多读一个字节,结果整个响应都乱了。记住:数据长度 = 后续字节数,不包括PCI本身。

3.2 多帧传输:当数据塞不下的时候

刚才说了,VIN码17个字节,加上服务ID和子功能,总共20多个字节。一条CAN帧最多8个字节,显然不够。这时候就需要多帧传输了。

多帧传输,说白了就是把一条诊断请求或响应拆成多个CAN帧来发。它分为三个阶段:

  1. 首帧(FF):告诉接收方“我要发一个长消息”,并告知总长度。
  2. 流控制帧(FC):接收方告诉发送方“我准备好了,你发吧”,并指定发送参数。
  3. 连续帧(CF):发送方把剩下的数据一帧一帧发出去。

咱们还是用VIN码的例子。假设ECU要返回VIN码(17字节),加上正响应头(3字节),总共20字节。首帧长这样:

首帧(FF):
CAN数据场:0x14 0x62 0xF1 0x90 0x01 0x02 0x03 0x04
解释:
- 0x14:首帧(0x1),总数据长度20字节(0x14)
- 0x62 0xF1 0x90:正响应头
- 0x01 0x02 0x03 0x04:VIN码的前4个字节

注意,首帧只发了7个字节(PCI + 6字节数据),剩下的13个字节需要后续的连续帧来发。

然后,诊断仪收到首帧后,需要回复一个流控制帧:

流控制帧(FC):
CAN数据场:0x30 0x08 0x00
解释:
- 0x30:流控制帧(0x3),流状态(0x0 = 继续发送)
- 0x08:块大小(BS),表示每发8帧后需要再等一次流控制
- 0x00:最小间隔时间(STmin),表示连续帧之间至少间隔0ms

最后,ECU收到流控制帧后,开始发连续帧:

连续帧1(CF):
CAN数据场:0x21 0x05 0x06 0x07 0x08 0x09 0x0A 0x0B
解释:
- 0x21:连续帧(0x2),序列号1(0x01)
- 0x05~0x0B:VIN码的第5~11个字节

连续帧2(CF):
CAN数据场:0x22 0x0C 0x0D 0x0E 0x0F 0x10 0x11 0x12
解释:
- 0x22:连续帧(0x2),序列号2(0x02)
- 0x0C~0x12:VIN码的第12~18个字节(最后多一个字节?不,VIN码只有17个字节,这里最后一个字节是填充的?不对,总数据长度20字节,首帧发了6字节,连续帧1发了7字节,连续帧2发了7字节,正好20字节。嗯,这里要注意,连续帧2的最后一个字节是VIN码的第17个字节,但数据场有8个字节,所以最后一个字节是填充的0x00?不,UDS协议不允许填充,必须发实际数据。所以连续帧2只发6个字节?不对,总数据长度20字节,首帧发了6字节,连续帧1发了7字节,连续帧2只需要发7字节?20 - 6 - 7 = 7,没错。但VIN码只有17个字节,加上正响应头3个字节,正好20字节。所以连续帧2的7个字节都是有效数据,没有填充。嗯,这里我搞混了,重新算:
我曾经踩过的坑: 多帧传输中,连续帧的序列号是从1开始循环的(0x01~0x0F,然后回到0x00)。有一次我在调试时,连续帧序列号写成了0x00开头,结果ECU直接丢弃了所有连续帧,因为协议规定首帧之后第一个连续帧的序列号必须是1。记住:序列号从1开始,到0x0F后回到0x00

3.3 流控制协议:谁说了算?

流控制协议,说白了就是接收方控制发送方的节奏。为什么需要这个?因为ECU的处理能力可能不如诊断仪快。如果诊断仪一股脑把连续帧全发过来,ECU可能来不及处理,导致丢帧。

流控制帧有三个参数:

参数 字节位置 说明
流状态(FS) PCI字节的低4位 0x0 = 继续发送(CTS),0x1 = 等待(WAIT),0x2 = 溢出(OVFLW)
块大小(BS) 第2个字节 发送方在发完BS帧后,必须等待下一个流控制帧。0x00表示没有限制。
最小间隔时间(STmin) 第3个字节 连续帧之间的最小间隔,单位是毫秒或微秒(取决于编码)。

举个例子,如果BS = 5,STmin = 10ms,那么发送方每发5个连续帧后,必须等接收方再发一个流控制帧,而且连续帧之间至少间隔10ms。

核心要点: 流控制协议是UDS多帧传输的“交通警察”。没有它,多帧传输就是一场灾难。我在项目中遇到过,某个ECU的STmin设置成了0x00(0ms),结果诊断仪连续帧发得太快,ECU的CAN控制器缓冲区溢出,直接丢帧。后来我把STmin改成了0x0A(10ms),问题就解决了。

3.4 正响应与负响应:ECU的“点头”和“摇头”

诊断仪发了一条请求,ECU怎么回应?无非两种:正响应(我听懂了,这是你要的数据)和负响应(我听懂了,但我做不到)。

正响应的格式很简单:服务ID + 0x40,然后跟上响应数据。比如:

请求:0x22 0xF1 0x90(读取数据标识符0xF190)
正响应:0x62 0xF1 0x90 0x01 0x02 0x03(服务ID 0x22 + 0x40 = 0x62,数据标识符0xF190,数据0x01 0x02 0x03)

负响应的格式是:0x7F + 请求的服务ID + 负响应码(NRC)。比如:

请求:0x22 0xF1 0x90(读取数据标识符0xF190)
负响应:0x7F 0x22 0x12(服务ID 0x22,NRC 0x12 = 子功能不支持)

常见的负响应码有哪些?我列个表:

NRC 含义 常见场景
0x10 一般拒绝 ECU正在忙,无法处理请求
0x11 服务不支持 ECU不支持这个服务ID
0x12 子功能不支持 服务ID支持,但子功能不支持
0x13 消息长度错误 请求的数据长度不对
0x22 条件不满足 ECU当前状态不允许执行该请求
0x31 请求超出范围 参数值超出允许范围
0x33 安全访问拒绝 需要解锁安全访问才能执行
0x78 请求正确但响应待定 ECU需要更多时间处理,稍后会再发响应
我的调试习惯: 遇到负响应时,别急着怀疑ECU。先检查请求格式对不对。我曾经花了一整天调试一个“服务不支持”的负响应,最后发现是请求的CAN ID写错了。记住:负响应是ECU在帮你排错,不是ECU在耍脾气。

3.5 实战中的“坑”与“避坑指南”

嗯,讲了这么多理论,咱们来点实战经验。我总结几个常见的坑:

  • 坑1:多帧传输中超时。UDS协议规定,连续帧之间的间隔不能超过某个值(通常是100ms)。如果发送方发得太慢,接收方会超时并丢弃整个消息。我曾经遇到过,诊断仪在处理其他任务时耽误了发送连续帧,结果ECU直接超时,整个诊断会话都断了。解决办法:用RTOS或者中断来保证连续帧的发送时序
  • 坑2:流控制帧的BS参数设置不合理。如果BS设置得太小(比如1),发送方每发一帧就要等一次流控制,效率极低。如果BS设置得太大(比如255),接收方可能来不及处理。我建议:BS设置在8~16之间,STmin设置在10~50ms之间,这是一个比较平衡的配置。
  • 坑3:负响应码0x78的误用。0x78表示“请求正确但响应待定”,ECU会在稍后主动发送响应。但很多工程师把这个NRC当成“延迟响应”来用,结果诊断仪等不到响应,直接超时。记住:0x78只适用于ECU需要长时间处理的情况,比如写入Flash。处理完成后,ECU必须主动发送正响应或负响应。
我曾经踩过的一个大坑: 有一次在量产车上,诊断仪发送了一个多帧请求,ECU回复了流控制帧后,诊断仪开始发连续帧。但ECU的CAN控制器在接收连续帧时,突然收到了一个优先级更高的CAN报文(比如发动机转速),导致连续帧接收中断。ECU的协议栈没有做重传机制,结果整个诊断请求都失败了。从那以后,我要求所有项目都必须实现多帧传输的超时重传机制,至少重试3次。

好了,这一章的内容就到这里。单帧与多帧传输、流控制协议、正响应与负响应,这三个东西是UDS协议的基石。你想想看,没有它们,诊断仪和ECU之间连最基本的“对话”都做不到。下一章咱们聊聊更高级的话题——诊断会话控制与安全访问,到时候你会看到,这些基础机制是如何支撑起更复杂的诊断功能的。