3、诊断会话控制(0x10):默认会话、编程会话、扩展会话的切换逻辑、会话状态机、实际项目中的会话管理策略

3.1 会话控制,到底在控制什么?

各位工程师朋友,咱们直接切入正题。诊断会话控制,服务ID是0x10。说白了,它就是ECU的“工作模式切换器”。

你想想看,一辆车上的ECU,不可能时时刻刻都允许你干所有事。比如,你正在高速上开着车,突然来个诊断请求要刷写程序——这不出事了吗?所以,UDS定义了不同的会话状态,来限制你能做什么、不能做什么。

我个人习惯把会话理解成“权限等级”。等级越高,能用的诊断服务就越多,但ECU的实时响应性能也可能受影响。嗯,这里要注意,不是越高越好,够用就行。

3.2 三种核心会话:默认、编程、扩展

UDS标准里定义了三种必须支持的会话。我直接给你列个表,一目了然:

会话类型 会话ID (SID + SubFunction) 典型用途 我项目中的经验
默认会话 (Default Session) 0x10 0x01 上电后的初始状态,读取故障码、读取数据 这是ECU的“安全区”,几乎所有诊断请求都能响应,但写操作基本被禁止
编程会话 (Programming Session) 0x10 0x02 刷写软件、更新固件 这个会话下,ECU会关闭大部分通信和功能,专心做刷写。我曾经遇到过刷写中途掉电,ECU变砖的情况,所以这个会话一定要配合安全访问
扩展会话 (Extended Session) 0x10 0x03 标定、配置、特殊功能测试 比默认会话权限高,但比编程会话“温和”。我常用它来做下线检测(EOL)时的参数配置

核心要点:ECU上电后,默认进入默认会话。如果你想做刷写或标定,必须先发0x10请求切换到对应会话。切换不成功,后续所有高权限服务都会被拒绝。

3.3 会话状态机:切换逻辑与规则

会话切换不是你想切就能切的。它有一套严格的状态机逻辑。我画个简单的状态流转图给你看:

+----------------+       0x10 0x02       +------------------+
|                | --------------------> |                  |
|  默认会话      |                        |  编程会话        |
|  (Default)     | <-------------------- |  (Programming)   |
|                |       0x10 0x01       |                  |
+----------------+                        +------------------+
        |                                        |
        | 0x10 0x03                              | 0x10 0x01
        v                                        v
+----------------+                        +------------------+
|                |                        |                  |
|  扩展会话      |                        |  默认会话        |
|  (Extended)    | --------------------> |  (Default)       |
|                |       0x10 0x01       |                  |
+----------------+                        +------------------+

这里有几个关键规则,我踩过坑,你记一下:

  • 只能从默认会话切换到其他会话。你不能直接从编程会话切到扩展会话,必须先切回默认会话。我曾经在项目里偷懒,想直接跳转,结果ECU直接回复NRC 0x12(子功能不支持),折腾了半天才发现是状态机没走对。
  • 非默认会话有超时机制。ECU内部有个定时器,叫S3Server。如果一段时间内(通常是5秒)没有收到任何诊断请求,ECU会自动切回默认会话。这是为了防止刷写或标定完成后,ECU一直留在高权限状态,影响行车安全。
  • 编程会话是“单行道”。进入编程会话后,ECU通常会禁用DTC存储、禁用通信报文(比如CAN报文),只响应诊断请求。所以,刷写完成后,一定要记得发0x10 0x01切回默认会话,或者让ECU硬件复位。

避坑指南:我曾经在一个量产项目中,发现刷写完成后ECU没有自动退出编程会话。结果车辆下线的EOL测试全部失败,因为ECU还在“刷写模式”,不响应正常的CAN报文。后来我们在刷写脚本里强制加了一条“切回默认会话”的命令,问题才解决。所以,编程会话的退出逻辑,一定要在需求文档里写清楚

3.4 实际项目中的会话管理策略

理论说完了,咱们聊聊实战。会话管理看着简单,但在实际项目中,稍不注意就会出幺蛾子。我分享几个我常用的策略:

3.4.1 会话保持与心跳机制

你想想看,刷写一个大的固件包,可能要几分钟。如果中间因为网络抖动,诊断仪和ECU断开了,ECU的S3Server超时,自动切回了默认会话。这时候刷写工具还在发数据,ECU就会回复NRC 0x78(请求正确接收,但响应待定),然后刷写失败。

我的做法是:在刷写工具里,每隔2秒发一个0x3E(Tester Present)请求。这个请求的作用就是“续命”,告诉ECU“我还活着,别切会话”。

// 伪代码示例:刷写过程中的会话保持
while (刷写未完成) {
    send_diagnostic_request(0x3E, 0x80);  // 发送Tester Present,抑制响应
    sleep(2000);  // 每2秒发一次
    // 继续发送刷写数据...
}

小技巧:0x3E请求的SubFunction参数0x80表示“抑制肯定响应”。这样ECU不会回复,减少了总线负载。但要注意,如果ECU检测到异常,还是会回复NRC的。

3.4.2 安全访问与会话的配合

很多高权限服务(比如写入数据、刷写程序)不仅要求你在扩展或编程会话下,还要求你通过安全访问(0x27服务)的“解锁”验证。这就像你进了银行金库(扩展会话),还得输入密码(安全访问)才能打开保险柜。

我建议的流程是:

  1. 先发0x10切换到扩展或编程会话。
  2. 再发0x27进行安全解锁。
  3. 最后执行具体的诊断服务(比如0x2E写入数据)。

顺序不能乱。我曾经见过一个同事,先解锁再切会话,结果一切会话,安全状态被重置了,又得重新解锁。白白浪费了时间。

3.4.3 多ECU场景下的会话冲突

现在的车都是网络架构,一个诊断仪同时连接多个ECU。如果你对ECU_A发起了编程会话,然后又对ECU_B发起了扩展会话,这时候总线上的报文会非常混乱。ECU_A在编程会话下可能不响应某些报文,导致ECU_B的诊断也失败。

我的策略是:诊断工具端要做好会话管理,同一时间只对一个ECU进行高权限操作。如果必须并行,要确保ECU之间没有总线冲突,或者使用路由网关进行隔离。

3.5 总结与个人心得

会话控制,说白了就是ECU的“门禁系统”。你拿什么卡(会话ID),就能进什么门(执行什么服务)。

我做了这么多年诊断开发,最大的体会是:会话切换的逻辑一定要在项目早期就定下来,并且和系统工程师、软件工程师、测试工程师对齐。否则到了项目后期,你会发现各种奇葩问题——刷写失败、EOL测试不过、售后诊断连不上……追根溯源,都是会话管理没做好。

嗯,今天就聊到这儿。下一章咱们聊聊安全访问(0x27),那个“密码锁”可比会话控制有意思多了。