第一章:UDS诊断协议概述
大家好,我是老张。在汽车电子这行摸爬滚打了十几年,今天咱们来聊聊UDS诊断协议。说实话,这玩意儿刚接触时觉得挺枯燥的,一堆十六进制数、服务ID、子功能...但干久了你会发现,它其实是整车电子系统的“神经系统”。
嗯,咱们先从最基础的讲起。
1.1 UDS协议背景
UDS,全称是Unified Diagnostic Services,统一诊断服务。它为啥会出现?
早年间,各大车企各玩各的。大众用KWP2000,宝马用自己的协议,奔驰也有自己的一套。维修厂里摆满了各种诊断仪,一个品牌一套设备,别提多乱了。
我记得2010年那会儿,我在一家Tier1做ECU开发。客户要求支持三种诊断协议,光协议栈适配就折腾了两个月。后来ISO 14229标准一出来,大家都松了口气——终于有个统一的标准了。
说白了,UDS就是汽车界的“普通话”。不管你是发动机ECU、变速箱TCU,还是BMS电池管理系统,大家用同一种语言沟通。
1.2 OSI七层模型与UDS
很多初学者一听到OSI七层模型就头大。别急,我换个角度给你讲。
你想想看,ECU之间通信,就像两个人打电话。你关心的是“说什么内容”(应用层),但底层还有“怎么拨号”(网络层)、“信号怎么传”(物理层)等等。
UDS主要工作在OSI模型的第7层——应用层。但它依赖下面的几层:
| OSI层 | UDS中的实现 | 我的理解 |
|---|---|---|
| 第7层:应用层 | UDS服务(如10服务、22服务) | 这就是咱们写代码要处理的部分 |
| 第6层:表示层 | 数据格式定义 | 字节序、对齐方式,坑很多 |
| 第5层:会话层 | 诊断会话管理 | 控制通信的建立和结束 |
| 第4层:传输层 | DoIP、CAN TP | 分包和重组,我在这踩过坑 |
| 第3层:网络层 | ISO 15765-2(CAN) | 寻址和路由 |
| 第2层:数据链路层 | CAN控制器 | 硬件相关,一般不用操心 |
| 第1层:物理层 | CAN总线、以太网 | 线束、收发器 |
实际开发中,我们最常打交道的是应用层和传输层。我曾经遇到过一个诡异的问题:诊断请求发出去,ECU没响应。查了两天,最后发现是CAN TP层分包时,序列号没处理好。嗯,这种坑,踩过一次就记住了。
1.3 诊断服务分类
UDS定义了26种诊断服务,从0x10到0x3E。但别被数字吓到,咱们可以分分类:
- 诊断管理类:0x10(诊断会话控制)、0x11(ECU复位)、0x3E(Tester Present)
- 数据读取类:0x22(读取DID)、0x23(读取内存)、0x24(读取缩放数据)
- 数据写入类:0x2E(写入DID)、0x3D(写入内存)
- 故障码相关:0x14(清除DTC)、0x19(读取DTC信息)
- 程序控制类:0x31(例程控制)、0x34(请求下载)、0x36(数据传输)
- 安全相关:0x27(安全访问)
我个人习惯把服务分成“读”和“写”两大类。读服务一般不需要安全访问,写服务基本都要。你想想看,要是谁都能随便写ECU数据,那车还不得乱套?
核心要点:诊断服务ID的高位字节决定了服务类别。0x2X是读,0x3X是写,0x1X是控制类。记住这个规律,看到服务ID就能猜个大概。
1.4 诊断会话与安全访问
这部分是UDS的精髓,也是很多新手容易搞混的地方。
诊断会话,说白了就是ECU的工作模式。UDS定义了三种默认会话:
- 默认会话(Default Session):上电后的初始状态,只支持基本服务
- 编程会话(Programming Session):刷写固件时用,支持0x34/0x36等服务
- 扩展会话(Extended Session):支持更多诊断功能,比如读写DID
为什么要有会话?我举个例子。有一次客户反馈,ECU在行驶过程中突然进入了编程会话。结果呢?CAN总线被诊断报文占满了,正常通信全断了。从那以后,我设计协议栈时都会加一个“会话超时退出”机制——3秒没收到Tester Present报文,自动切回默认会话。
小技巧:开发调试时,可以用0x10服务配合子参数来切换会话。比如0x10 0x03表示进入扩展会话。记得在退出前发0x10 0x01回到默认会话,否则ECU可能一直处于高功耗状态。
安全访问,这是UDS里最“刺激”的部分。它通过种子-密钥机制来验证身份:
// 安全访问流程(伪代码)
// 步骤1:Tester发送0x27 0x01(请求种子)
// 步骤2:ECU返回种子(比如4字节随机数)
// 步骤3:Tester用算法计算密钥,发送0x27 0x02 + 密钥
// 步骤4:ECU验证通过,解锁安全访问
// 我见过最坑的算法实现:
uint32_t calculate_key(uint32_t seed) {
// 有些厂商直接用种子取反当密钥...
return ~seed;
}
嗯,这里要注意。安全访问算法是OEM的机密信息。不同车企算法不同,有的用AES,有的用自定义算法。我曾经见过一个项目,算法里有个bug:种子为0时,密钥计算会死循环。结果产线上刷写ECU时,偶尔会卡住。排查了三天才找到原因。
避坑指南:我曾经在安全访问上栽过跟头。开发时一定要测试边界情况:种子全0、全F、连续请求、超时重试。另外,安全访问失败次数要有限制,一般3次失败后锁定一段时间,防止暴力破解。
最后说一句,诊断会话和安全访问是联动的。进入扩展会话后,如果没做安全访问,很多写服务还是不能用。顺序是:先切会话,再做安全访问,最后执行诊断服务。这个顺序搞反了,ECU会给你回一个NRC 0x33(安全访问拒绝)。
好了,第一章就聊到这儿。UDS诊断协议看似复杂,但拆开来看,无非就是“谁在什么时候能做什么事”。下一章咱们深入讲讲诊断报文的格式和寻址方式,到时候我会分享一些CAN总线抓包分析的经验。