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个字节(假设)
3.2 多帧传输:当数据塞不下的时候
刚才说了,VIN码17个字节,加上服务ID和子功能,总共20多个字节。一条CAN帧最多8个字节,显然不够。这时候就需要多帧传输了。
多帧传输,说白了就是把一条诊断请求或响应拆成多个CAN帧来发。它分为三个阶段:
- 首帧(FF):告诉接收方“我要发一个长消息”,并告知总长度。
- 流控制帧(FC):接收方告诉发送方“我准备好了,你发吧”,并指定发送参数。
- 连续帧(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个字节都是有效数据,没有填充。嗯,这里我搞混了,重新算:
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。
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需要更多时间处理,稍后会再发响应 |
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必须主动发送正响应或负响应。
好了,这一章的内容就到这里。单帧与多帧传输、流控制协议、正响应与负响应,这三个东西是UDS协议的基石。你想想看,没有它们,诊断仪和ECU之间连最基本的“对话”都做不到。下一章咱们聊聊更高级的话题——诊断会话控制与安全访问,到时候你会看到,这些基础机制是如何支撑起更复杂的诊断功能的。