4、UDS诊断协议入门:ISO 14229概述、诊断会话控制(0x10)、安全访问(0x27)、例程控制(0x31)

各位同学,今天我们来聊聊UDS诊断协议。说实话,这是BootLoader开发中最绕不开的一环。你想想看,没有诊断协议,ECU就是个黑盒子,你没法跟它对话,更别提升级了。

UDS的全称是Unified Diagnostic Services,统一诊断服务。它定义在ISO 14229标准里。说白了,它就是一套汽车电子设备之间沟通的“普通话”。不管你是哪个供应商做的ECU,只要支持UDS,大家就能用同样的指令去访问它。

我个人习惯把UDS理解成“ECU的API接口”。你发送一个请求,ECU给你一个响应。就这么简单。但背后的细节,嗯,咱们得好好捋一捋。

4.1 ISO 14229概述

ISO 14229是一个大家族,它定义了26种诊断服务。每个服务都有一个唯一的SID(Service Identifier,服务标识符)。比如0x10是诊断会话控制,0x27是安全访问,0x31是例程控制。

这些服务被分成了六大类:

  • 诊断和通信管理类:包括0x10(诊断会话控制)、0x11(ECU复位)、0x3E(测试仪在线)等。这些是基础服务,用来建立和管理诊断连接。
  • 数据传输类:包括0x22(读取数据)、0x2E(写入数据)、0x23(读取内存)等。用来读写ECU内部的数据。
  • 存储数据传输类:包括0x14(清除诊断信息)、0x19(读取DTC信息)等。用来管理故障码。
  • 输入输出控制类:包括0x2F(输入输出控制)。用来强制控制ECU的输入输出引脚。
  • 远程激活类:包括0x31(例程控制)。用来执行ECU内部的特定程序。
  • 上传下载类:包括0x34(请求下载)、0x35(请求上传)、0x36(传输数据)、0x37(请求传输退出)。这些是BootLoader升级的核心服务。

这里有个小经验:在BootLoader开发中,最常用的服务其实就那几个——0x10、0x27、0x31、0x34、0x36、0x37。其他的服务,说实话,用得不多。但作为基础,你都得了解。

4.2 诊断会话控制(0x10)

0x10服务,用来切换ECU的诊断会话状态。ECU上电后,默认处于默认会话(Default Session)。在这个会话里,很多诊断服务是被禁用的。比如你想刷写程序,ECU会直接拒绝你。

为什么会这样?这是安全考虑。你想想看,如果随便一个诊断仪都能刷写ECU,那车还不得乱套?

0x10服务支持三种会话:

子功能 会话名称 说明
0x01 默认会话 上电后的初始状态,功能受限
0x02 编程会话 允许刷写操作,BootLoader升级时使用
0x03 扩展会话 允许更多诊断功能,如读写配置参数

请求格式很简单:

请求:0x10 + 子功能
例如:0x10 0x02  // 请求切换到编程会话

响应格式:

肯定响应:0x50 + 子功能 + P2_Server_Max + P2*_Server_Max
例如:0x50 0x02 0x32 0x00 0x64
// 0x32 = 50ms(P2_Server_Max,即最大响应时间)
// 0x00 0x64 = 100ms(P2*_Server_Max,即扩展响应时间)

我在项目中遇到过一个问题:有些ECU在切换会话后,会重置一些内部状态。比如你正在做安全访问,突然切了会话,安全状态就丢了。所以,我建议你在切换会话前,先确认当前状态,避免踩坑。

小技巧: 编程会话和扩展会话是有超时机制的。如果ECU在P2*_Server_Max时间内没有收到任何诊断请求,它会自动切回默认会话。所以,你的诊断仪需要定期发送0x3E(测试仪在线)来保持会话。

4.3 安全访问(0x27)

0x27服务,用来解锁ECU的安全保护。说白了,就是“对暗号”。你发送一个种子(Seed),ECU给你一个密钥(Key),你算对了,它就让你进去。

为什么需要这个?因为有些操作太危险了。比如刷写程序、修改配置参数,这些操作如果被误操作,可能导致ECU变砖。所以,ECU会要求你先通过安全访问验证。

安全访问的流程是这样的:

  1. 请求种子:诊断仪发送0x27 + 子功能(奇数),ECU返回种子。
  2. 发送密钥:诊断仪计算密钥,发送0x27 + 子功能(偶数)+ 密钥,ECU验证。
  3. 验证通过:ECU返回肯定响应,解锁成功。

举个例子:

// 步骤1:请求种子(安全等级1)
请求:0x27 0x01
响应:0x67 0x01 0xAA 0xBB 0xCC 0xDD
// 种子是 0xAA 0xBB 0xCC 0xDD

// 步骤2:发送密钥(假设算法结果是 0x11 0x22 0x33 0x44)
请求:0x27 0x02 0x11 0x22 0x33 0x44
响应:0x67 0x02  // 肯定响应,解锁成功

这里有个关键点:安全算法是OEM(主机厂)定义的,每个项目都不一样。有的用简单的异或,有的用AES加密,有的用自定义算法。我曾经遇到过一个项目,算法里还混了时间戳,搞得我调试了好几天。

避坑指南: 我曾经在安全访问上栽过跟头。当时没注意,连续发送了3次错误密钥,ECU直接锁死了,必须断电重启才能恢复。所以,一定要在代码里实现错误计数和锁定机制。另外,种子是有时效性的,一般几十毫秒内必须完成密钥发送,超时了种子就失效了。

4.4 例程控制(0x31)

0x31服务,用来让ECU执行一段预定义的程序。你可以把它理解成“ECU的API调用”。比如擦除Flash、检查CRC、执行自检,这些都可以通过例程控制来实现。

0x31服务有三个子功能:

子功能 名称 说明
0x01 启动例程 开始执行例程
0x02 停止例程 中断正在执行的例程
0x03 请求例程结果 获取例程的执行状态或结果

请求格式:

请求:0x31 + 子功能 + 例程ID + 可选参数
例如:0x31 0x01 0xFF 0x00  // 启动例程ID为0xFF00的例程

响应格式:

肯定响应:0x71 + 子功能 + 例程ID + 可选结果
例如:0x71 0x01 0xFF 0x00 0x00  // 例程启动成功

在BootLoader中,0x31服务用得特别多。比如:

  • 擦除Flash:启动一个擦除例程,传入要擦除的地址和大小。
  • 检查编程条件:比如检查电压是否正常、点火开关是否打开。
  • 验证程序完整性:刷写完成后,启动CRC校验例程,检查数据是否正确。

我个人习惯把例程控制设计成“原子操作”。什么意思?就是例程一旦启动,要么成功,要么失败,中间不能被打断。你想想看,如果擦除Flash擦到一半,突然来个复位,那ECU就真的变砖了。

重点总结:
  • 0x10用来切换会话,是诊断的“大门”。
  • 0x27用来安全验证,是诊断的“钥匙”。
  • 0x31用来执行程序,是诊断的“工具”。
  • 这三个服务是BootLoader升级的基石,必须熟练掌握。

好了,这一章的内容就到这里。下一章我们会深入讲解0x34、0x36、0x37这三个上传下载服务,也就是真正的刷写流程。到时候我会结合一个实际项目,带大家走一遍完整的升级过程。