4. UDS诊断会话控制(0x10):默认会话、编程会话、扩展会话、会话切换流程与安全策略

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

ECU不能一直用同一种姿态响应诊断请求。有些操作太危险,比如刷写Flash、标定参数,必须进入特定模式才能干。我刚开始做UDS开发时,就吃过这个亏——在默认会话下发了一个写ECU复位类型的请求,结果ECU直接不理我,返回了否定响应。后来才明白,权限不够。

4.1 三种会话:默认、编程、扩展

UDS定义了三种标准会话状态。每个ECU上电后,默认进入默认会话。你可以通过0x10服务主动切换。

会话类型 子功能ID 典型用途 我见过的坑
默认会话 (Default Session) 0x01 读取故障码、读取数据、基本通信 很多ECU在默认会话下禁止写操作
编程会话 (Programming Session) 0x02 刷写Bootloader、应用程序、标定数据 刷写时不能断电,否则ECU变砖
扩展会话 (Extended Session) 0x03 写入参数、执行例程、标定、特殊功能 扩展会话通常需要先解锁安全访问

核心原则:会话等级越高,能执行的诊断服务越多。但高等级会话通常有超时机制,超时后自动退回默认会话。

4.2 会话切换流程:请求与响应

切换会话的流程很简单。Tester发一条0x10服务请求,ECU回复肯定或否定响应。

请求报文格式:

| 字节0 | 字节1 |
| 0x10  | sub-function |

肯定响应格式:

| 字节0 | 字节1 | 字节2-3 |
| 0x50  | sub-function | P2_Server_max (可选) |

注意那个P2_Server_max。它告诉Tester:当前会话下,ECU处理请求的最大时间。我遇到过一些ECU,在编程会话里把P2时间设得很长,因为刷写Flash确实慢。Tester如果按默认会话的超时去等,就会误判超时。

我的习惯:收到肯定响应后,先解析P2_Server_max,动态调整Tester的超时定时器。别写死。

4.3 会话切换流程:状态机与超时

ECU内部维护一个会话状态机。切换不是瞬时的,有严格的时序要求。

UDS会话状态切换流程 默认会话 扩展会话 编程会话 0x10 03 0x10 02 超时 / 0x10 01 超时 / 0x10 01 0x10 02 0x10 03 说明: 1. 实线箭头表示主动切换(通过0x10服务)。 2. 虚线箭头表示被动切换(超时自动回退)。

每个会话都有一个S3 Server超时定时器。如果在这个时间内没有收到任何诊断请求,ECU自动退回默认会话。这个时间通常由OEM定义,常见值是5秒。

我曾经踩过的坑:在编程会话里刷写Bootloader,刷写过程本身需要十几秒。但ECU的S3定时器只有5秒。刷到一半,ECU超时退回默认会话,刷写中断,ECU变砖。后来我学乖了:在刷写大块数据前,先发一条0x3E(Tester Present)服务,保持会话活跃。

4.4 安全策略:会话与安全访问的联动

会话切换本身不需要安全解锁。但进入高等级会话后,很多敏感服务(比如写入数据、执行例程)需要先通过0x27服务(安全访问)解锁。

举个例子:

  1. Tester发送0x10 03,进入扩展会话。ECU响应肯定。
  2. Tester发送0x2E(写入数据),请求写入标定参数。ECU返回否定响应:0x7F 0x2E 0x33(securityAccessDenied)。
  3. Tester发送0x27,完成安全解锁。
  4. Tester再次发送0x2E,ECU接受请求。

你想想看,为什么要有这个设计?

会话控制只是“模式选择”,安全访问才是“权限验证”。两者缺一不可。我见过一些ECU设计,把安全解锁和会话绑定在一起——进入编程会话时自动解锁。这种做法风险很高,万一Tester异常断开,ECU就处于“解锁+编程会话”状态,容易被恶意攻击。

推荐做法:会话切换只负责模式切换,不负责权限验证。安全解锁必须独立完成,并且每次解锁都有时效性(通常解锁后一段时间内有效,超时自动锁定)。

4.5 实际开发中的避坑指南

  • 会话切换失败处理:ECU收到0x10请求后,如果当前正在执行某个例程(比如正在擦除Flash),应该返回否定响应0x7F 0x10 0x22(conditionsNotCorrect)。我遇到过ECU直接忽略请求,导致Tester超时。
  • 会话嵌套:有些ECU支持“会话嵌套”,比如在扩展会话里再进入编程会话。但大部分ECU不支持,切换前必须先退回默认会话。建议查OEM规范。
  • P2_Server_max的解析:肯定响应里的P2_Server_max是2字节,单位是毫秒。有些ECU返回0x0000,表示使用默认值(比如50ms)。别把0x0000当成0ms处理。
  • 多ECU场景:在CAN总线上,多个ECU同时响应0x10请求?不可能。每个ECU有独立的诊断地址,Tester通过CAN ID区分。但要注意,如果两个ECU的地址冲突,总线会乱。

我的测试习惯:写一个自动化脚本,遍历所有会话切换组合(默认->扩展、默认->编程、扩展->编程、编程->扩展、扩展->默认、编程->默认),检查每次切换的响应时间、P2_Server_max值、以及切换后能执行的服务列表。这个脚本能发现很多ECU实现的bug。

4.6 总结

0x10服务是UDS诊断的“门禁系统”。它不负责开门后的权限验证,但决定了你能进哪扇门。默认会话是安全区,编程会话是手术室,扩展会话是调试间。记住:会话有超时,超时自动回退;会话切换不自动解锁,安全访问要单独做。

嗯,这一章就到这里。代码实现和测试用例,我们后面章节再细聊。