2、诊断请求与响应:请求报文格式、肯定响应与否定响应、NRC详解、物理寻址与功能寻址

好,咱们今天聊点实在的。诊断请求和响应,说白了就是ECU和诊断仪之间的对话规则。你发一句,它回一句,但怎么发、怎么回,这里头门道不少。我刚开始做UDS开发那会儿,就因为在报文格式上栽过跟头,后来才明白,这些基础东西不搞清楚,后面调试起来真要命。

2.1 请求报文格式:你说话得有个规矩

诊断请求报文,结构其实挺简单。它由三部分组成:

  • 服务标识符(SID):1个字节,告诉ECU你想干啥。比如0x10是诊断会话控制,0x22是读取数据。
  • 子功能(Sub-function):可选,1个字节。有些服务需要它,比如0x10服务后面跟着0x01就是默认会话,0x02是编程会话。
  • 数据参数(Data):可变长度,具体看服务定义。

举个例子,一个读取VIN码的请求报文:

请求:22 F1 90
解释:
0x22 - 服务ID(读取数据)
0xF1 - 数据标识符高字节
0x90 - 数据标识符低字节(组合起来0xF190就是VIN码的DID)

嗯,这里要注意。子功能字节的最高位(bit 7)有特殊含义。如果置1,表示这是一个抑制肯定响应的请求。什么意思?就是告诉ECU:你处理完就行,不用回我“收到”了。我在项目中遇到过,批量刷写时用这个能省不少总线时间。

关键点:请求报文长度最小2字节(SID + 1字节数据),最大取决于诊断协议栈配置,一般不超过4095字节。

2.2 肯定响应与否定响应:ECU的两种回答

ECU收到请求后,要么说“行”,要么说“不行”。

肯定响应的格式是:请求SID + 0x40 + 响应数据。比如上面那个读取VIN码的请求,肯定响应就是:

响应:62 F1 90 4C 46 56 33 30 31 32 33 34 35 36 37 38 39
解释:
0x62 - 肯定响应SID(0x22 + 0x40)
0xF1 0x90 - 回显请求中的DID
后面17个字节 - VIN码数据

否定响应就有点意思了。格式是:0x7F + 请求SID + NRC。NRC就是否定响应码,告诉你为什么不行。

否定响应:7F 22 12
解释:
0x7F - 否定响应标志
0x22 - 回显请求的服务ID
0x12 - NRC,表示“子功能不支持”

我个人习惯,调试时先看否定响应。它直接告诉你问题出在哪,比瞎猜快多了。

2.3 NRC详解:ECU的“拒绝理由”大全

NRC(Negative Response Code)是UDS协议里最实用的部分。我整理了一份常用NRC表,你收好:

NRC值 名称 含义 常见场景
0x10 General Reject 一般性拒绝 请求格式错误,但说不清具体原因
0x11 Service Not Supported 服务不支持 ECU根本不认识这个SID
0x12 Sub-function Not Supported 子功能不支持 SID认识,但子功能没实现
0x13 Incorrect Message Length 报文长度错误 多发了或少发了字节
0x22 Conditions Not Correct 条件不满足 当前会话下不允许该操作
0x31 Request Out of Range 请求超出范围 DID不存在或数据值非法
0x33 Security Access Denied 安全访问被拒绝 没解锁就尝试写操作
0x78 Request Correctly Received - Response Pending 请求已收到,正在处理 ECU需要长时间处理,让你等等

避坑指南:我曾经遇到过一个ECU,每次发诊断请求都返回0x78。一开始我以为程序死循环了,后来才发现是ECU在处理NVM写入,需要几百毫秒。0x78其实是告诉你“别急,我在忙”。正确的做法是收到0x78后,等一段时间再重新发请求。

为什么0x78这么特殊?因为它不是真正的否定。ECU说“我收到了,但还没处理完,你先等着”。这时候诊断仪应该等待,而不是直接报错。你想想看,如果ECU正在擦写Flash,你催它也没用。

2.4 物理寻址与功能寻址:找一个人还是找一群人

这是UDS里一个很巧妙的设计。说白了就是:你发的请求,是给一个ECU,还是给一群ECU?

物理寻址:一对一。请求报文的目标地址是某个特定ECU的物理地址。只有这个ECU会响应。比如你想读取发动机ECU的故障码,就用物理寻址。

功能寻址:一对多。请求报文的目标地址是一个功能组地址。总线上所有支持该功能的ECU都会收到并处理,但只有符合条件的ECU才会响应。比如你想让所有ECU同时进入扩展会话,就用功能寻址。

在CAN总线上,这两种寻址体现在CAN ID上。举个例子:

物理请求:0x7DF → 目标ECU的物理地址是0x7DF
功能请求:0x7DF → 但这里的0x7DF是功能地址,所有ECU都认

物理响应:0x7E8 → 源ECU用自己的物理地址回复
功能响应:一般不响应,或者用物理地址单独回复

嗯,这里有个坑。功能寻址的请求,ECU通常不会发肯定响应。为什么?你想想看,如果10个ECU同时响应,总线不就乱套了?所以功能寻址一般用于广播类操作,比如“所有ECU,请进入编程会话”。每个ECU收到后自己处理,但不回复。

重要提醒:功能寻址的否定响应是可以发的。如果某个ECU不支持该功能,它会用物理地址回复一个否定响应。我曾经调试时发现,一个ECU收到功能寻址的0x10服务请求后,返回了0x7F 10 0x22。排查了半天,原来是它当前处于编程会话,不允许重复进入。这个0x22(条件不满足)就是通过物理地址发回来的。

我个人习惯,在项目初期先用物理寻址把每个ECU单独调通。等所有ECU都稳定了,再测试功能寻址。这样出问题时,能快速定位是哪个ECU在捣乱。

好了,这一章的内容就这些。请求报文怎么拼、肯定否定怎么区分、NRC怎么查、寻址方式怎么选,都是UDS开发的基本功。下一章我们聊聊诊断会话管理,那可是ECU的“工作模式切换器”。