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读数据服务,那个更实用,但也更容易出幺蛾子。