2. UDS协议核心概念:服务分类、会话控制、安全访问与诊断数据库
好,咱们进入第二章。这一章的内容,说白了就是UDS诊断的“地基”。你后面所有的开发工作,都建立在这些概念之上。我个人习惯把这一章叫做“诊断三要素”:服务、会话、安全。再加上一个数据库,就是你的“作战地图”。
2.1 UDS服务分类:强制与可选
UDS协议里定义了一大堆服务,但ECU不需要全部支持。ISO 14229-1把服务分成了三类:强制、可选、以及制造商自定义。嗯,这里要注意,强制服务是必须实现的,否则你的ECU连诊断仪都连不上。
强制服务(Mandatory):
- 0x10(诊断会话控制)—— 后面细讲
- 0x11(ECU复位)—— 我见过有些工程师把复位逻辑写错了,导致ECU反复重启
- 0x27(安全访问)—— 保护敏感数据
- 0x28(通信控制)—— 控制报文收发
- 0x3E(Tester Present)—— 保持会话活跃
- 0x83(访问时间参数)—— 设置超时时间
- 0x84(安全数据传输)—— 加密传输
- 0x85(控制DTC设置)—— 开启/关闭故障码记录
- 0x86(响应事件)—— 事件响应机制
- 0x87(链路控制)—— 波特率切换等
可选服务(Optional):
- 0x22(读取数据标识符)—— 最常用的服务之一
- 0x2E(写入数据标识符)—— 写配置参数
- 0x19(读取DTC信息)—— 读故障码
- 0x14(清除DTC信息)—— 清故障码
- 0x31(例程控制)—— 执行特定功能
- 0x34/0x36/0x37(下载/上传/传输退出)—— 刷写相关
避坑指南:我曾经在一个项目里,客户要求支持0x22读取VIN码,但ECU的NVM(非易失性存储器)驱动还没写好。结果诊断仪一读就返回NRC 0x31(请求超出范围)。后来我学乖了,强制服务必须在项目初期就实现并测试通过。
2.2 诊断会话控制(0x10)
0x10服务是用来切换ECU的工作模式的。ECU默认处于默认会话(Default Session),只能执行一些基础服务。你需要切换到扩展会话(Extended Session)或编程会话(Programming Session)才能做更高级的操作。
会话类型:
| 子功能 | 会话名称 | 典型用途 |
|---|---|---|
| 0x01 | 默认会话 | 上电后的初始状态,只能执行强制服务 |
| 0x02 | 编程会话 | 刷写固件,需要安全访问 |
| 0x03 | 扩展会话 | 读写参数、执行例程,需要安全访问 |
| 0x40-0x5F | 制造商自定义 | 各OEM自己定义的特殊会话 |
你想想看,为什么要有会话机制?说白了就是为了安全。默认会话下,你只能看看版本号、读读故障码。想改参数?先切到扩展会话,再通过安全访问验证身份。这样能防止误操作。
我的经验:在实现0x10服务时,要注意会话超时(S3Server)。如果超过一定时间(通常是5秒)没有收到任何诊断请求,ECU会自动切回默认会话。我建议把超时时间设得比标准值稍长一点,比如6秒,避免测试时频繁掉线。
2.3 安全访问(0x27)
0x27服务是ECU的“看门狗”。你想写数据、刷固件、执行敏感例程?先过我这关。它的流程是:
- 诊断仪发送请求种子(Seed)(子功能0x01/0x03/0x05...)
- ECU返回一个随机种子(通常是4字节)
- 诊断仪用约定的算法计算密钥(Key)
- 诊断仪发送发送密钥(子功能0x02/0x04/0x06...)
- ECU验证密钥,成功则解锁,失败则返回NRC 0x35(无效密钥)
代码示例(伪代码):
// 安全访问实现示例
uint8_t seed[4];
uint8_t key[4];
// 步骤1:生成种子
void GenerateSeed(uint8_t* seed) {
seed[0] = rand() % 256;
seed[1] = rand() % 256;
seed[2] = rand() % 256;
seed[3] = rand() % 256;
}
// 步骤2:计算密钥(简单异或算法,实际项目更复杂)
void CalculateKey(uint8_t* seed, uint8_t* key) {
key[0] = seed[0] ^ 0x5A;
key[1] = seed[1] ^ 0xA5;
key[2] = seed[2] ^ 0x3C;
key[3] = seed[3] ^ 0xC3;
}
// 步骤3:验证密钥
bool VerifyKey(uint8_t* receivedKey) {
uint8_t expectedKey[4];
CalculateKey(currentSeed, expectedKey);
return (memcmp(receivedKey, expectedKey, 4) == 0);
}
注意:我曾经遇到一个坑——种子生成后,如果连续多次验证失败,ECU应该进入“延迟模式”,比如锁定30秒。有些工程师忘了实现这个机制,结果被暴力破解了。ISO 14229-1里明确要求了失败计数器(SecurityAccessFailedCount)和延迟时间(SecurityAccessDelayTime)。
2.4 通信矩阵与诊断数据库(CDD/ODX)
好了,前面说的都是“怎么做”。现在说说“做什么”——也就是你的诊断需求从哪来。
通信矩阵(Communication Matrix):
说白了就是一张大表,定义了ECU之间怎么通信。包括:
- CAN ID(物理寻址/功能寻址)
- 报文周期
- 信号定义(起始位、长度、缩放因子、偏移量)
- 诊断请求/响应的CAN ID分配
诊断数据库(CDD/ODX):
这两个文件格式,是诊断开发的“圣经”。
- CDD(CANdela Diagnostic Description):Vector公司的格式,用工具(如CANdelaStudio)编辑。我习惯用它做原型开发。
- ODX(Open Diagnostic Data Exchange):ASAM标准格式,基于XML。OEM之间交换诊断数据时常用。
一个典型的ODX文件结构:
<?xml version="1.0" encoding="UTF-8"?>
<ODX>
<ECU>
<DIAG_LAYER>
<SERVICES>
<SERVICE ID="0x22">
<SHORT-NAME>ReadDataByIdentifier</SHORT-NAME>
<REQUEST>
<PARAM>
<ID>DID_VIN</ID>
<LENGTH>17</LENGTH>
</PARAM>
</REQUEST>
</SERVICE>
</SERVICES>
</DIAG_LAYER>
</ECU>
</ODX>
实战建议:我个人习惯在项目启动时,先拿到CDD或ODX文件,然后用工具(如Vector Diva)自动生成一部分代码。这样能减少手写代码的错误。但要注意,自动生成的代码往往比较“死板”,有些特殊逻辑(比如安全访问算法)还是得自己手写。
2.5 本章小结
这一章我们聊了:
- UDS服务分强制和可选,强制服务必须实现
- 0x10服务控制ECU的工作模式,默认会话只能做基础操作
- 0x27服务通过种子-密钥机制保护敏感操作
- CDD/ODX文件是诊断开发的“蓝图”,一定要吃透
下一章,我们会深入0x22和0x2E服务——也就是读写数据标识符。这两个服务,你几乎每天都会用到。到时候我会分享一些我在调试时遇到的“灵异事件”。