4、诊断会话控制(0x10):会话切换与超时管理

诊断会话控制,服务ID是0x10。说白了,它就是ECU的“工作模式切换器”。

你想想看,ECU平时在车上跑,不可能一直开着诊断功能。那样不安全,也浪费资源。所以UDS定义了三种基本会话:默认会话、编程会话和扩展会话。它们各有各的权限,各有各的用途。

4.1 三种会话,各司其职

会话类型 子功能ID 主要用途 典型权限
默认会话 (Default) 0x01 ECU上电后的初始状态 只读诊断数据,读取DTC
编程会话 (Programming) 0x02 刷写Bootloader或应用程序 允许内存写入,禁止非安全操作
扩展会话 (Extended) 0x03 高级诊断、标定、配置 读写参数,执行例程,IO控制

我个人习惯把默认会话比作“待机模式”。ECU刚上电,或者诊断超时后,都会回到这里。在这个模式下,你只能看看故障码,读读版本号。想干点别的?不行,得先切换。

扩展会话是“调试模式”。我当年做ADAS控制器标定时,天天跟它打交道。在这个会话下,你能做IO控制、读写参数、执行例程。说白了,就是给工程师用的“后门”。

编程会话是“刷写模式”。这个最敏感。你想想,刷写过程中要是出了岔子,ECU可能就变砖了。所以进入编程会话前,通常需要先做安全解锁(0x27服务)。

核心要点: 三种会话的权限是递增的。默认会话权限最低,编程会话权限最高。但高权限也意味着高风险,所以进入编程会话的门槛也最高。

4.2 会话切换机制

切换会话,就是发一条0x10服务请求。请求格式很简单:

请求: 0x10 + 子功能ID
响应: 0x50 + 子功能ID + 会话超时时间

举个例子,我想从默认会话切换到扩展会话:

请求: 02 10 03
响应: 06 50 03 00 0B B8

响应里的 00 0B B8 是超时时间,单位是毫秒。换算一下,0x0BB8 = 3000ms,也就是3秒。这意味着,如果3秒内没有收到任何诊断请求,ECU会自动跳回默认会话。

这里有个坑,我踩过。有一次,我在做Bootloader开发,发现刷写总是中途失败。查了半天,原来是超时时间设得太短。刷写一包数据要200ms,但超时只有100ms。结果ECU以为通信断了,自己切回了默认会话。嗯,从那以后,我每次都会仔细检查超时参数。

避坑指南: 我曾经遇到过,有些ECU在切换会话时,会先检查当前状态。比如,你正在执行一个例程,突然发个0x10过去想切会话。ECU可能会拒绝,返回NRC 0x22(条件不满足)。所以,切换前最好确保没有正在执行的操作。

4.3 会话状态机

会话切换不是乱来的。它有一套严格的状态机。我画了一张图,你看一眼就明白了。

UDS 会话状态机 默认会话 S1 (Default) 扩展会话 S3 (Extended) 编程会话 S2 (Programming) 10 03 超时 / 10 01 10 02 (需安全解锁) 10 02 超时 / 10 01 状态机规则说明: 1. 默认会话是“根状态”,任何会话超时都会回到这里。 2. 扩展会话和编程会话之间可以直接切换,无需经过默认。 3. 从默认直接切编程,通常需要先安全解锁(0x27)。 4. 超时时间由ECU在响应中告知,Tester必须遵守。 5. 切换会话时,ECU会终止当前正在执行的诊断服务。

从图里你能看到,默认会话是“根”。所有会话超时后,都会回到这里。扩展和编程之间可以直接切换,但编程会话的门槛更高——通常需要先做安全解锁。

我遇到过一种情况:有些ECU设计得比较“死板”,从默认会话直接切编程会话,它直接拒绝。你必须先切到扩展,再切到编程。为什么?因为扩展会话里可以做安全解锁,解锁完了再进编程,流程更安全。你想想看,是不是这个理?

4.4 超时管理

超时管理是会话控制里最容易出问题的地方。我见过太多开发者在超时参数上栽跟头。

ECU在响应0x10服务时,会返回一个超时时间(P2_Server_max)。这个时间告诉Tester:你必须在这么长时间内发下一条请求,否则我就切回默认了。

实战技巧: 我建议你在Tester端实现一个“心跳”机制。比如,超时时间是5秒,那你就每隔4秒发一条0x3E(Tester Present)请求。这样就能保持会话不超时。我曾经在一个项目中,就是因为忘了发心跳,导致刷写到一半被中断,ECU变砖了。后来花了半天时间才救回来。

超时时间不是固定的。不同ECU、不同会话,超时时间可能不一样。一般来说:

  • 默认会话: 超时时间通常较长,5-10秒。
  • 扩展会话: 超时时间中等,3-5秒。
  • 编程会话: 超时时间最短,1-3秒。因为刷写时通信频率高,超时时间短一点更安全。

但要注意,超时时间不是越长越好。你想想,如果超时时间设成1分钟,那Tester断开后,ECU要等1分钟才能回到默认。这期间,其他诊断设备就没办法连上来了。

注意事项: 我曾经在Bootloader开发中,把编程会话的超时时间设成了10秒。结果刷写时,Tester发完一包数据后,等了8秒才发下一包。ECU以为超时了,切回了默认。刷写失败。后来我把超时时间改成了30秒,问题解决。但30秒又太长,万一Tester真断了,ECU要等30秒才能恢复。所以,超时时间要权衡,不能太短,也不能太长。

4.5 会话切换的NRC处理

会话切换不是总能成功的。ECU可能会返回否定响应码(NRC)。常见的NRC有:

NRC 含义 常见原因
0x12 子功能不支持 你发的子功能ID不在ECU支持列表里
0x22 条件不满足 当前有正在执行的服务,或者没做安全解锁
0x13 消息长度错误 请求格式不对,比如少了子功能ID
0x31 请求超出范围 子功能ID有效,但当前状态下不允许切换

我遇到过最坑的一次,是0x22。当时我在调试一个ECU,想从默认切到编程。发了0x10 02,结果返回0x22。查了半天,发现是ECU的Bootloader正在后台运行一个自检程序。自检没完成,就不让切会话。嗯,后来我等到自检结束,再发请求,就成功了。

所以,遇到NRC别慌。先看看ECU当前在干什么,再对症下药。

4.6 小结

诊断会话控制,说白了就是ECU的“权限管理”。默认会话是基础,扩展会话是调试,编程会话是刷写。切换时要遵守状态机规则,注意超时时间,处理好NRC。

我个人觉得,会话控制是UDS协议里最基础,但也最容易出错的部分。很多刷写失败、诊断中断的问题,根源都在会话管理上。你只要把状态机和超时搞明白了,后面学Bootloader设计就会轻松很多。