3、诊断会话控制(0x10):默认会话、扩展会话、编程会话的切换逻辑、会话状态机、超时处理
各位工程师,咱们今天聊聊诊断会话控制。这个服务,服务ID是0x10,说白了就是让ECU知道「你现在该用哪种模式干活」。
我刚开始做UDS测试那会儿,总觉得会话切换不就是发个指令嘛,有啥难的?结果第一次在实车上测试,就因为超时问题把ECU给锁死了。嗯,从那以后我再也不敢小看这个0x10服务了。
3.1 三种会话模式,你搞清楚了吗?
UDS定义了三种标准会话,咱们一个一个说。
| 会话类型 | Sub-function值 | 典型用途 |
|---|---|---|
| 默认会话(Default Session) | 0x01 | 上电后的初始状态,只允许读取基本数据 |
| 扩展会话(Extended Session) | 0x02 | 允许读写参数、执行例程,但不能刷写 |
| 编程会话(Programming Session) | 0x03 | 刷写固件、更新软件,权限最高 |
你想想看,ECU上电后默认就在默认会话里。这时候你能干啥?读个DTC、读个版本号,仅此而已。想改参数?不行。想刷写?更不行。
我个人习惯,在测试开始前一定先确认当前会话状态。怎么确认?发个0x10 0x01,如果ECU回复肯定响应,说明已经在默认会话了。如果回复NRC 0x12(子功能不支持),那就有意思了——说明ECU可能被其他会话锁住了。
3.2 会话切换逻辑,别搞反了方向
会话切换不是你想怎么切就怎么切的。它有一套严格的规则。
核心规则:
- 默认会话 → 扩展会话:允许
- 默认会话 → 编程会话:允许
- 扩展会话 → 默认会话:允许(通过0x10 0x01或超时)
- 扩展会话 → 编程会话:允许
- 编程会话 → 默认会话:允许(通过0x10 0x01或超时)
- 编程会话 → 扩展会话:不允许(必须经过默认会话)
我在项目中遇到过一个问题:测试工程师从编程会话直接发0x10 0x02想切到扩展会话,结果ECU回了NRC 0x12。他当时就懵了,以为ECU坏了。其实不是,这就是规范规定的——编程会话只能切回默认会话,不能直接跳到扩展会话。
为什么会这样?说白了,安全考虑。编程会话权限最高,你刷完固件后,系统希望你回到一个「干净」的状态(默认会话),再重新选择下一步操作。这是防止误操作的设计。
3.3 会话状态机,一张图说清楚
我习惯把会话切换想象成一个状态机。每个会话就是一个状态,0x10指令就是触发状态转换的事件。
状态机示意(文字版):
[默认会话] ──0x10 0x02──→ [扩展会话]
[默认会话] ──0x10 0x03──→ [编程会话]
[扩展会话] ──0x10 0x01──→ [默认会话]
[扩展会话] ──0x10 0x03──→ [编程会话]
[编程会话] ──0x10 0x01──→ [默认会话]
[任何会话] ──超时/S3定时器──→ [默认会话]
注意看最后一条:任何会话超时后都会回到默认会话。这是UDS的「安全兜底」机制。你想想看,如果ECU在编程会话里突然断开了连接,没有超时机制,那ECU就一直卡在编程模式,下次上电可能就起不来了。
我的小技巧:在CANoe中测试会话切换时,我会在CAPL脚本里加一个定时器,模拟S3超时。这样能验证ECU的超时行为是否符合规范。具体做法是:发送0x10 0x03进入编程会话,然后停止发送任何诊断请求,等待S3定时器到期,观察ECU是否自动回到默认会话。
3.4 超时处理,S3定时器你设对了吗?
超时处理是会话控制里最容易踩坑的地方。UDS规范里提到了两个关键定时器:S3Server和S3Client。
| 定时器 | 位置 | 作用 |
|---|---|---|
| S3Server | ECU端 | ECU监控诊断请求的间隔时间,超时则自动回到默认会话 |
| S3Client | 测试工具端 | 测试工具监控ECU响应的间隔时间,超时则判定连接断开 |
S3Server的典型值是5秒。也就是说,ECU在非默认会话下,如果5秒内没有收到任何诊断请求,就会自动切回默认会话。
我曾经踩过一个坑:在测试扩展会话下的参数写入功能时,我发了一个写请求,然后花了6秒钟去查看数据手册,结果回来发现ECU已经回到默认会话了。写入操作自然失败了。从那以后,我养成了一个习惯——在非默认会话下操作时,每隔3秒发一个0x3E(Tester Present)请求,保持会话活跃。
注意:0x3E请求不会重置S3定时器吗?会的。但要注意,0x3E的响应不能占用太长的总线时间。有些ECU实现得比较严格,如果0x3E请求和响应之间的间隔过长,ECU可能认为连接不稳定,提前切回默认会话。
3.5 实战:在CANoe中测试会话切换
好了,理论说完了,咱们看看在CANoe里怎么测。
我一般用CAPL写一个简单的测试脚本。核心逻辑就是:发请求、收响应、判断NRC。
// 测试用例:从默认会话切换到扩展会话
void Test_SessionSwitch_DefaultToExtended()
{
byte request[2] = {0x10, 0x02}; // 0x10服务,子功能0x02
byte response[8];
int result;
// 发送请求
result = SendDiagnosticRequest(request, 2, response, 8);
if (result == 0)
{
// 检查响应ID
if (response[0] == 0x50 && response[1] == 0x02)
{
write("测试通过:成功切换到扩展会话");
}
else
{
write("测试失败:响应异常,NRC = 0x%02x", response[2]);
}
}
else
{
write("测试失败:发送请求超时");
}
}
这个脚本看着简单,但实际测试中我会加更多细节。比如:
- 在切换前先读取当前会话状态(通过0x10 0x01)
- 切换后立即发送一个只有扩展会话才支持的服务(比如0x2E写数据),验证权限是否正确
- 测试超时场景:进入扩展会话后,停止发送任何请求,等待6秒,再发一个0x22读数据,看ECU是否正常响应
我个人习惯,在测试报告中会把每个测试用例的CANoe trace截图贴进去。这样出了问题,一眼就能看出是请求没发出去,还是响应没回来,还是NRC不对。
3.6 避坑指南
最后,分享几个我踩过的坑,你们遇到了可以少走弯路。
- 坑一:编程会话切到扩展会话,直接发0x10 0x02。结果?NRC 0x12。正确做法是先发0x10 0x01回到默认会话,再发0x10 0x02。
- 坑二:S3定时器设置太短。有些ECU的S3Server只有2秒,你发完请求稍微一犹豫,会话就切回去了。建议在测试前先读一下ECU的S3定时器值(通过0x22读数据标识符)。
- 坑三:忘记发0x3E。在扩展会话或编程会话下做长时间测试时,一定要定期发0x3E。我一般用CAPL的on timer循环,每2秒发一次。
- 坑四:会话切换后,之前使能的服务可能失效。比如你在扩展会话下使能了某个例程,切回默认会话后,这个例程可能自动停止。测试时要注意这个行为。
嗯,会话控制这部分内容就这些。说白了,记住三件事:会话切换有方向、超时一定会回默认、0x3E是你的好朋友。下次咱们聊0x22读数据服务,那个更实用,但也更容易出幺蛾子。