第三章 UDS诊断协议详解:核心服务与机制
各位同学,今天我们来啃一块硬骨头——UDS诊断协议。说实话,我刚入行那会儿,看到UDS的文档也是一脸懵。服务号、子功能、正响应负响应……信息量太大了。但后来我发现,只要抓住几个核心服务,再配合CANoe的实战演练,这东西其实没那么玄乎。
这一章,我会把UDS里最常用的几个服务讲透。包括诊断会话控制、安全访问,还有那些你天天打交道的服务号:10、22、2E、31。嗯,咱们一个一个来。
3.1 UDS服务概述
UDS,全称是Unified Diagnostic Services,统一诊断服务。它定义在ISO 14229标准里。说白了,它就是一套ECU和诊断仪之间沟通的“语言”。
每个UDS服务都有一个唯一的服务ID(SID)。比如你想读数据,就用0x22服务;你想写数据,就用0x2E服务。我习惯把SID理解成“指令码”,你发一个指令,ECU就知道你要干嘛。
这里有个关键点:UDS的请求和响应是有固定格式的。请求格式一般是:
| SID (1字节) | 子功能 (可选) | 数据参数 (可变长度) |
响应格式分两种:
- 正响应:SID + 0x40 + 数据。比如0x22服务的正响应是0x62。
- 负响应:0x7F + SID + NRC(否定响应码)。比如你发了个不合法的请求,ECU会回0x7F 0x22 0x31。
重要提醒:负响应码0x31表示“请求超出范围”。我在项目中遇到过好几次,明明觉得请求没问题,结果一直报0x31。后来发现是数据长度算错了。所以,写代码时一定要仔细核对数据长度和范围。
3.2 诊断会话控制(0x10服务)
0x10服务,用来切换ECU的诊断会话模式。ECU平时可能处于低功耗状态,只有进入诊断会话,才能执行读写、刷写等操作。
常见的会话模式有:
| 子功能 | 会话名称 | 说明 |
|---|---|---|
| 0x01 | 默认会话 | ECU上电后的默认状态,功能受限 |
| 0x02 | 编程会话 | 用于ECU刷写,允许访问Flash |
| 0x03 | 扩展会话 | 用于高级诊断,比如读写校准数据 |
你想想看,为什么要有会话控制?因为安全。ECU不能随便让人读写。只有进入正确的会话,才能解锁对应的功能。比如刷写前,必须先切换到编程会话(0x02)。
个人经验:我建议在自动化测试脚本里,每次执行诊断操作前,都先发一个0x10服务确认当前会话。如果ECU没响应,或者响应了负码,就不要再往下走了。否则很容易把ECU搞“死”。
3.3 安全访问机制(0x27服务)
安全访问,说白了就是“对暗号”。ECU会给你一个种子(Seed),你拿种子算出一个密钥(Key),再发回去。ECU验证通过,才允许你执行敏感操作。
流程是这样的:
- 诊断仪发0x27 0x01(请求种子)
- ECU回复种子数据(比如4字节随机数)
- 诊断仪用算法计算密钥,发0x27 0x02 + 密钥
- ECU验证通过,回复正响应
这里有个坑:不同ECU的种子-密钥算法可能不一样。有的用简单的异或,有的用AES加密。我曾经遇到一个项目,算法文档里写的是“标准算法”,结果实际测试时怎么都对不上。后来发现是文档版本错了。所以,拿到算法后,一定要先在CANoe里写个小脚本验证一下。
避坑指南:我曾经在刷写测试中,因为安全访问失败,导致ECU进入了“锁定状态”。必须断电重启才能恢复。所以,自动化脚本里一定要加超时处理和重试机制。连续失败3次,就停止测试并报警。
3.4 读取数据(0x22服务)
0x22服务,用来读取ECU内部的数据。比如读取VIN码、软件版本、故障码等。请求格式很简单:
请求:0x22 + DID(2字节数据标识符)
响应:0x62 + DID + 数据
DID是啥?就是数据的“身份证号”。比如0xF190代表VIN码,0xF195代表软件版本号。每个DID对应的数据长度和格式,都在ECU的诊断规范里定义好了。
我习惯在CANoe里建一个DID列表,用CAPL脚本循环读取。这样能快速验证所有DID是否都能正常响应。如果某个DID返回负码0x31,说明要么DID不存在,要么当前会话不支持。
3.5 写入数据(0x2E服务)
0x2E服务,和0x22正好相反——往ECU里写数据。比如修改VIN码、配置参数等。请求格式:
请求:0x2E + DID + 数据
响应:0x6E + DID
注意:写操作通常需要先进入扩展会话,并且通过安全访问。否则ECU会拒绝。我见过不少新手,直接发0x2E请求,结果收到0x7F 0x2E 0x33(安全访问拒绝)。
实战建议:在刷写流程中,0x2E服务常用于写入刷写前的配置参数。比如关闭某些通信通道,或者设置刷写模式。写完后,记得用0x22读回来确认一下,确保数据真的写进去了。
3.6 例程控制(0x31服务)
0x31服务,用来执行ECU内部的“例程”。例程可以理解成一段预置的程序,比如擦除Flash、检查内存、复位某些模块等。
请求格式:
0x31 + 子功能 + RID(例程标识符,2字节)+ 数据
子功能有三种:
- 0x01:启动例程
- 0x02:停止例程
- 0x03:查询例程结果
举个例子,刷写前需要擦除Flash,就会发0x31 0x01 + RID(擦除例程)。ECU执行完后,会返回正响应,里面可能包含执行结果或状态。
这里有个细节:有些例程执行时间很长,比如擦除整个Flash可能需要几十秒。CANoe里一定要设置足够的超时时间。我一般设成30秒,如果超时了,就发0x31 0x03查询一下状态,看看是不是还在跑。
小技巧:在CANoe的Test Module里,可以用waitForDiagResponse函数配合超时参数,优雅地处理长时间运行的例程。别用死等,那样会把整个测试流程卡住。
3.7 本章小结
这一章我们聊了UDS的四个核心服务:
- 0x10:诊断会话控制,切换ECU的工作模式
- 0x27:安全访问,保护敏感操作
- 0x22/0x2E:读写数据,操作ECU内部参数
- 0x31:例程控制,执行预置程序
说实话,这些服务在刷写流程里几乎都会用到。你掌握了它们,就等于拿到了UDS诊断的“钥匙”。下一章,我们会把这些服务串起来,模拟一个完整的ECU刷写流程。到时候,你就能看到它们是怎么协同工作的了。
嗯,今天就到这里。有问题随时在群里问我,或者翻翻CANoe的帮助文档。记住,多动手,多抓log,诊断这东西,练得多了自然就熟了。