4、诊断会话控制(0x10):默认会话、编程会话、扩展会话的切换机制、会话状态机、SID与子功能编码

好,咱们今天聊一个非常核心的话题——诊断会话控制。服务ID是0x10,简称10服务。

说实话,这个服务在UDS协议里地位很特殊。你想想看,ECU上电后到底该听谁的?能不能刷写?能不能调参数?这些都由会话控制来决定。我个人习惯把10服务叫做“看门大爷”,它决定了后续所有诊断服务的“准入权限”。

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

UDS定义了三种标准会话,咱们一个一个说。

会话模式 子功能编码 典型用途 我见过的坑
默认会话 (Default Session) 0x01 ECU上电后的初始状态,只允许读取基本诊断信息 很多工程师以为默认会话啥都能干,其实它权限最低
编程会话 (Programming Session) 0x02 刷写软件、更新固件,需要安全访问配合 刷写过程中如果掉电,ECU可能变砖,所以要做好恢复机制
扩展会话 (Extended Session) 0x03 读写参数、执行例程、标定调试 扩展会话超时后自动切回默认,这个超时时间很多人忘记配置

嗯,这里要注意:三种会话不是平级的。默认会话是基础,编程和扩展都是从默认会话切换过去的。你不能直接从编程切到扩展,必须先回默认,再切过去。这个规则我在项目里吃过亏,后面会细说。

4.2 会话状态机:切换的底层逻辑

会话切换不是你想切就能切的。ECU内部维护了一个状态机,我画个简化的逻辑给你看:

// 伪代码:会话状态机核心逻辑
typedef enum {
    SESSION_DEFAULT = 0x01,
    SESSION_PROGRAMMING = 0x02,
    SESSION_EXTENDED = 0x03
} SessionType;

SessionType currentSession = SESSION_DEFAULT;

void HandleDiagnosticSessionControl(uint8_t subFunction) {
    // 检查当前会话是否允许切换到目标会话
    if (IsTransitionAllowed(currentSession, subFunction) == FALSE) {
        SendNegativeResponse(NRC_INVALID_SESSION);  // 0x12
        return;
    }
    
    // 执行切换
    currentSession = (SessionType)subFunction;
    
    // 启动会话超时定时器(S3Server定时器)
    StartSessionTimer();
    
    // 发送肯定响应
    SendPositiveResponse(subFunction);
}

为什么要有这个状态机?说白了就是为了安全。你想想看,如果随便一个诊断仪都能让ECU进入编程模式,那车在路上开着突然被刷写了,多危险。

我在项目中遇到过一个问题:某客户要求支持从扩展会话直接切到编程会话。我查了AUTOSAR规范,明确说不允许。后来跟客户解释了半天,最后他们接受了“先回默认再切”的方案。所以,不要试图绕过状态机,这是安全底线。

4.3 SID与子功能编码:报文怎么拼

10服务的请求报文格式很简单:

请求: 0x10 + 子功能编码
响应: 0x50 + 子功能编码 + 会话参数(可选)

举个例子:

  • 请求进入编程会话:02 10 02(假设CAN ID是0x7DF)
  • 肯定响应:02 50 02
  • 否定响应:03 7F 10 12(NRC=0x12,会话不支持)

这里有个细节:子功能编码的bit6和bit7有特殊含义。bit6叫“抑制肯定响应位”(SuppressPosRspMsgIndicator),如果置1,ECU就不发肯定响应了。bit7保留,必须为0。

重要:子功能编码 = 会话类型 | (抑制位 << 6)

比如:想进入编程会话但不想要肯定响应,就发 0x42(0x02 | 0x40)

我曾经踩过一个坑:测试时发现ECU对某些10服务请求没反应,查了半天,原来是测试工具把抑制位置1了,ECU确实没发响应,但测试脚本在傻等。嗯,这种问题排查起来很头疼。

4.4 会话超时与自动回退

ECU不会永远待在编程或扩展会话里。每个会话都有一个超时定时器,叫S3Server定时器。超时后自动切回默认会话。

这个超时时间通常由OEM定义,常见值:

  • 默认会话:通常不超时,或者超时时间很长(比如1小时)
  • 扩展会话:5秒到30秒不等
  • 编程会话:刷写过程中通常禁用超时,但刷写完成后立即切回

我的建议:在BSW实现中,每次收到有效的诊断请求(不仅仅是10服务),都应该重置S3Server定时器。这叫“会话保持”。否则你调个参数调一半,超时了,又得重新切会话,很烦人。

4.5 避坑指南:我踩过的几个雷

做10服务实现时,有几个地方特别容易出问题:

  1. 会话切换时的NRC处理:如果当前正在执行某个例程(31服务),这时候收到10服务请求,应该先终止例程再切会话。我曾经忘了处理这个,结果例程跑了一半,会话切了,ECU状态乱掉了。
  2. 安全访问状态重置:从非默认会话切回默认会话时,安全访问的解锁状态必须重置。也就是说,你在扩展会话里解锁了,一切回默认,锁又锁上了。这是规范要求的,但很多人忘记实现。
  3. DTC状态变化:会话切换时,某些DTC的状态位可能会变化。比如编程会话中产生的DTC,切回默认后可能变成“已存储”。这个要看具体OEM的需求。
  4. 多ECU场景下的同步:如果网关同时转发10服务到多个ECU,要确保所有ECU的会话状态一致。否则会出现“A ECU在编程,B ECU还在默认”的尴尬局面。

警告:千万不要在编程会话中实现“永久停留在编程模式”的逻辑。我曾经见过一个供应商的代码,刷写完成后忘了切回默认会话,结果ECU一直处于编程模式,导致车辆无法正常启动。这种问题在产线上很难发现,但到了用户手里就是重大故障。

4.6 实际项目中的实现建议

如果你在AUTOSAR BSW中实现10服务,我建议关注以下几点:

  • 使用BswM模块:AUTOSAR的BswM(Basic Software Mode Manager)天然支持会话管理,别自己造轮子。
  • 配置好S3Server定时器:在Dcm模块的配置里,每个会话的超时时间要单独设置。我习惯把扩展会话设成10秒,编程会话设成30秒。
  • 做好日志记录:每次会话切换都打一条日志,包括时间戳、旧会话、新会话、触发源。调试时你会感谢这个习惯的。
  • 测试覆盖所有切换路径:默认->编程、默认->扩展、编程->默认、扩展->默认,以及非法切换(比如编程->扩展),每个路径都要测到。

好了,关于诊断会话控制,核心内容就这些。记住一句话:会话是诊断的“门禁”,管好门禁,后面的服务才能安全运行。下一节咱们聊27服务——安全访问,到时候你会更理解为什么会话控制这么重要。