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服务实现时,有几个地方特别容易出问题:
- 会话切换时的NRC处理:如果当前正在执行某个例程(31服务),这时候收到10服务请求,应该先终止例程再切会话。我曾经忘了处理这个,结果例程跑了一半,会话切了,ECU状态乱掉了。
- 安全访问状态重置:从非默认会话切回默认会话时,安全访问的解锁状态必须重置。也就是说,你在扩展会话里解锁了,一切回默认,锁又锁上了。这是规范要求的,但很多人忘记实现。
- DTC状态变化:会话切换时,某些DTC的状态位可能会变化。比如编程会话中产生的DTC,切回默认后可能变成“已存储”。这个要看具体OEM的需求。
- 多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服务——安全访问,到时候你会更理解为什么会话控制这么重要。