4. UDS服务详解(上):诊断会话控制(0x10)、ECU复位(0x11)、安全访问(0x27)、通信控制(0x28)

好,咱们今天开始啃UDS服务里最核心的几个服务。说实话,这4个服务是诊断开发中打交道最多的。你想想看,不管你是做Bootloader刷写,还是做产线EOL测试,又或者是售后诊断,几乎每一步都绕不开它们。

我个人习惯把这四个服务称为「诊断四件套」——会话控制是开门钥匙,ECU复位是重启大法,安全访问是权限验证,通信控制是网络开关。咱们一个一个来拆解。

4.1 诊断会话控制(0x10)—— 诊断的「门禁系统」

先说0x10服务。它的作用说白了就是:告诉ECU「我要开始诊断了,请切换到对应的工作模式」。

ECU平时在车上跑着,不可能一直开着诊断窗口等你。那样太耗资源,也不安全。所以UDS定义了几种会话状态,ECU默认处于默认会话(Default Session)。只有收到0x10服务请求,才会切换到其他会话。

常见的会话类型

会话名称 会话ID(SubFunction) 典型用途
默认会话 0x01 上电后的初始状态,只支持少量服务
编程会话 0x02 Bootloader刷写、软件升级
扩展会话 0x03 读写数据、例程控制等高级诊断功能
安全系统诊断会话 0x04 安全相关系统的诊断(如气囊、刹车)

这里有个细节要注意:会话切换是有层级关系的。比如从默认会话可以直接切到编程会话,但从编程会话切回默认会话,ECU通常会执行一次内部复位。嗯,这一点在刷写流程里特别重要,我后面会讲到。

请求/响应格式

请求:0x10 + SubFunction(会话ID)

肯定响应:0x50 + SubFunction + 可选参数(如P2Server_max, P2*Server_max)

否定响应:0x7F + 0x10 + NRC(如0x12=子功能不支持,0x22=条件不满足)

举个例子,用CANoe发送一个切换到扩展会话的请求:

// 发送:切换到扩展会话(0x03)
diagRequest ECU.DiagnosticSessionControl(0x03);

// 响应示例(肯定):
// 50 03 00 32 00 0A
// 其中 0x0032 = 50ms(P2Server_max),0x000A = 10ms(P2*Server_max)

我在项目中遇到过一个问题:某ECU在切换会话时,响应里带的P2时间参数是0x0000。这意味着什么?意味着ECU告诉你「我没有超时限制」。但实际测试发现,ECU在5秒后自动切回了默认会话。后来查协议才发现,P2=0表示使用默认值(通常是50ms或5000ms)。所以,千万别把0当成「无限等待」

我的小技巧:在CANoe的CAPL脚本里,建议每次发送0x10请求后,都检查一下响应中的P2参数。如果发现异常值,及时打日志报警。我曾经因为这个习惯,提前发现了一个ECU的时序Bug。

4.2 ECU复位(0x11)—— 重启的艺术

0x11服务,就是让ECU执行复位操作。你可能会想:「复位有什么好讲的?不就是断电重启吗?」嗯,没那么简单。

UDS定义了多种复位类型,每种复位的行为和影响范围都不一样。

子功能 复位类型 说明
0x01 硬复位(Hard Reset) 模拟断电重启,所有RAM数据丢失
0x02 钥匙通断复位(Key Off/On Reset) 模拟点火开关OFF再ON,部分ECU会保持一些状态
0x03 软复位(Soft Reset) 仅复位应用层软件,不重新初始化硬件
0x04 使能快速路由(Enable Rapid Power Shutdown) 用于快速下电场景,如紧急断电

请求格式很简单:0x11 + SubFunction。肯定响应是0x51 + SubFunction

但这里有个坑:ECU在收到复位请求后,是先发响应再复位,还是先复位再发响应? 协议规定是「先发响应,再执行复位」。但我在实际测试中遇到过反过来的情况——ECU先复位了,响应没发出来,导致诊断仪一直等超时。

避坑指南:我曾经在某个项目中,用CANoe连续发送0x11复位请求,结果ECU在第3次复位后「死机」了。后来分析发现,ECU的复位逻辑里有个计数器,连续复位超过2次就会进入保护模式。所以,不要在生产测试中频繁发送复位请求,至少间隔3秒以上。

另外,复位后ECU会回到默认会话。如果你在编程会话中发送了复位,那么刷写进度就中断了。所以刷写流程的最后一步通常是「复位ECU」,而不是在过程中复位。

4.3 安全访问(0x27)—— 诊断的「保险柜」

0x27服务,是UDS里最「神秘」的服务之一。它的作用是:验证诊断仪是否有权限执行某些敏感操作(如刷写、解锁、标定等)。

说白了,就是ECU问你:「你说你是诊断仪,你怎么证明?」

安全访问的流程是经典的「挑战-应答」模式:

  1. 请求种子(Seed):诊断仪发送0x27 + 请求种子子功能(如0x01)
  2. ECU返回种子:ECU返回一串随机数(种子),通常是4字节
  3. 计算密钥(Key):诊断仪用约定的算法,将种子计算成密钥
  4. 发送密钥:诊断仪发送0x27 + 发送密钥子功能(如0x02)+ 密钥数据
  5. ECU验证:ECU用同样的算法计算,比对密钥是否一致

如果密钥正确,ECU返回肯定响应,并解锁安全访问权限。如果错误,ECU返回否定响应,并可能启动「延时锁定」机制——比如连续输错3次,锁定30秒。

请求/响应示例

请求种子:27 01

响应种子:67 01 12 34 56 78(种子=0x12345678)

发送密钥:27 02 9A BC DE F0(密钥=0x9ABCDEF0)

验证通过:67 02

这里有个关键点:种子和密钥的算法是OEM自定义的。有的用简单的异或,有的用AES加密,有的用自定义的查表法。所以你在做诊断开发时,一定要拿到OEM提供的安全算法文档。

我记得有一次,客户说他们的ECU安全访问总是失败。我一看日志,发现诊断仪发送的密钥长度是4字节,但ECU期望的是5字节。查协议才发现,这个ECU的种子是5字节的。所以,一定要确认种子长度,不要想当然。

我的经验:在CANoe里调试安全访问时,我习惯用CAPL写一个「安全访问助手」函数。把种子和密钥的算法封装好,每次调用时自动完成挑战-应答流程。这样既省时间,又不容易出错。

4.4 通信控制(0x28)—— 诊断的「静音模式」

最后一个,0x28服务。这个服务的作用是:控制ECU的网络通信行为。说白了,就是让ECU「闭嘴」或者「说话」。

你可能会问:「为什么要让ECU闭嘴?」

举个例子:你在刷写ECU时,不希望ECU还在总线上发CAN报文干扰刷写过程。或者你在做某些测试时,需要模拟ECU离线状态。这时候,0x28服务就派上用场了。

0x28服务有三个主要的控制类型:

子功能 控制类型 说明
0x00 使能Rx/Tx(恢复正常通信) ECU恢复正常收发
0x01 使能Rx,禁用Tx(只收不发) ECU可以接收报文,但不发送任何报文
0x02 禁用Rx,使能Tx(只发不收) ECU可以发送报文,但不接收任何报文
0x03 禁用Rx/Tx(完全静默) ECU既不收也不发

请求格式:0x28 + SubFunction + 通信类型。其中通信类型用于指定控制哪个网络(如CAN、LIN、FlexRay等)。

举个例子,让ECU只收不发:

// 发送:使能Rx,禁用Tx(子功能=0x01,通信类型=0x01表示CAN)
28 01 01

// 肯定响应:48 01 01

这里有个容易踩的坑:0x28服务是有时效性的。ECU在收到0x28请求后,会进入指定的通信模式。但如果ECU发生了复位,或者会话超时切回了默认会话,通信模式会自动恢复为正常模式。所以,如果你需要长时间保持静默,记得在会话超时前重新发送0x28请求。

避坑指南:我曾经在刷写测试中,用0x28禁用了ECU的发送功能。结果刷写完成后,忘记恢复通信,导致ECU一直「沉默」。售后那边反馈说ECU不上线,查了半天才发现是通信控制没恢复。所以,一定要在测试脚本的结尾加上恢复通信的步骤,或者用try-finally结构保证恢复。

另外,0x28服务通常需要先进入扩展会话或编程会话才能使用。如果你在默认会话下发送0x28,ECU会返回NRC 0x22(条件不满足)。

小结

好了,这4个服务咱们就聊到这儿。总结一下:

  • 0x10:诊断的入口,切换会话状态
  • 0x11:ECU复位,注意复位类型和时序
  • 0x27:安全访问,挑战-应答模式,算法要保密
  • 0x28:通信控制,让ECU静默或恢复通信

下一章咱们继续聊剩下的几个核心服务:读取故障码(0x19)、读取数据(0x22)、写入数据(0x2E)等。到时候我会结合CANoe的实际操作,给大家演示怎么用CAPL脚本自动化测试这些服务。

嗯,今天就到这儿。有问题随时找我。