1. UDS协议基础:OSI模型与汽车诊断、UDS服务概述、诊断会话控制(0x10)、安全访问(0x27)

各位同学,咱们今天正式开讲UDS诊断协议。说实话,我最早接触UDS是在2015年,那时候刚入行做ECU测试,被一堆0x10、0x27、0x22这些十六进制数搞得晕头转向。后来踩了不少坑,才慢慢摸清楚门道。今天我把这些经验掰开揉碎了讲给你们听。

1.1 OSI模型与汽车诊断

先聊聊OSI模型。很多人觉得这是纯理论,没啥用。我一开始也这么想,直到有一次在项目里排查诊断通信超时问题,折腾了两天没搞定。后来静下心从OSI七层模型一层层分析,才发现是传输层的数据包分片重组出了问题。嗯,从那以后我再也不敢小看这个模型了。

OSI模型在汽车诊断里,其实主要用到下面几层:

OSI层 汽车诊断中的实现 我踩过的坑
应用层(第7层) UDS服务(0x10、0x27等) 服务ID和子功能搞混,ECU直接不理你
表示层(第6层) 数据编码格式 大端小端没对齐,读出来的数据全是错的
会话层(第5层) 诊断会话管理 会话超时没处理好,刷写到一半断了
传输层(第4层) DoIP、CAN TP 多帧传输时序列号搞错,ECU直接丢弃
网络层(第3层) CAN总线寻址 物理寻址和功能寻址用混了
数据链路层(第2层) CAN帧格式 DLC长度没算对,报文被截断
物理层(第1层) CAN总线电平 终端电阻没接,通信时好时坏

说白了,OSI模型就是帮你把复杂问题拆成小块。你想想看,如果诊断通信出问题了,你从哪一层开始查?我个人习惯是从下往上查——先看物理层通不通,再看数据链路层对不对,最后才怀疑应用层。

1.2 UDS服务概述

UDS全称是Unified Diagnostic Services,统一诊断服务。它定义了一套标准化的诊断请求和响应机制。我见过不少新手一上来就背服务ID,结果真到项目里还是不会用。

UDS的核心思想其实很简单:请求-响应。诊断仪发一个请求,ECU回一个响应。就这么回事。但细节里全是魔鬼。

常用的UDS服务大概有这些:

  • 0x10 - 诊断会话控制:切换ECU的工作模式
  • 0x27 - 安全访问:解锁ECU的受保护功能
  • 0x22 - 读取数据标识符:读ECU内部数据
  • 0x2E - 写入数据标识符:写数据到ECU
  • 0x31 - 例程控制:执行ECU内部程序
  • 0x34/0x36/0x37 - 刷写相关:请求下载、传输数据、请求退出
  • 0x19 - 读取故障码:诊断故障码相关
  • 0x14 - 清除故障码:清DTC

这里我多说一句:千万别把所有服务ID死记硬背。我建议你记住最常用的几个,其他的用到的时候查ISO 14229-1标准就行。我在项目里就见过有人把0x22和0x2E搞反了,结果读数据写成了写数据,差点把ECU的标定参数给覆盖了。

核心要点:UDS服务分为6大类,共26个服务。但实际项目中常用的不超过10个。刷写流程中,你主要会用到0x10、0x27、0x34、0x36、0x37、0x31这几个。

1.3 诊断会话控制(0x10)

0x10服务,说白了就是让ECU切换到不同的工作模式。ECU平时在车上跑的时候,不可能一直开着诊断功能——那多费电啊,也不安全。所以它有几个不同的会话状态。

常见的会话类型:

子功能 会话名称 用途 我个人的经验
0x01 默认会话(Default Session) 上电后的初始状态,功能受限 刷写前一定要确认ECU不在默认会话
0x02 编程会话(Programming Session) 刷写固件时使用 这个会话下很多诊断服务不可用
0x03 扩展诊断会话(Extended Diagnostic Session) 常规诊断、标定 我最常用的会话,功能比较全
0x04 安全系统诊断会话 安全相关功能 用得少,但遇到安全气囊ECU时要用

这里有个坑,我必须要讲:会话是有超时机制的。ECU如果在规定时间内没收到任何诊断请求,会自动跳回默认会话。这个时间一般是5秒左右,但不同ECU可能不一样。我曾经在刷写脚本里忘了处理会话保持,结果刷到一半ECU跳回默认会话了,刷写失败,还得重新来一遍。

避坑指南:写自动化脚本时,一定要加一个定时发送0x3E(测试仪在线)的机制,保持会话不超时。我一般设置每2秒发一次,留够余量。

0x10服务的请求格式很简单:

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

响应(肯定):0x50 + 子功能 + P2_Server_max + P2*_Server_max
例如:0x50 0x02 0x00 0x32 0x00 0x0A

响应里的P2_Server_max和P2*_Server_max是什么意思?P2是ECU处理单个请求的最大时间,P2*是ECU处理连续请求的最大时间。这两个参数在写自动化脚本时特别重要——你发完请求后,得等够P2时间再收响应,不然可能收不到。

1.4 安全访问(0x27)

0x27服务,就是给ECU上锁解锁。为什么要锁?你想想看,如果谁都能随便改ECU的数据,那车还安全吗?所以刷写固件、写标定数据这些敏感操作,必须先通过安全访问。

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

  1. 诊断仪发请求种子(0x27 + 0x01/0x03/0x05等)
  2. ECU返回一个随机种子(通常是4字节)
  3. 诊断仪用密钥算法计算种子对应的密钥
  4. 诊断仪发送密钥(0x27 + 0x02/0x04/0x06等 + 密钥数据)
  5. ECU验证密钥,通过则解锁,不通过则锁定一段时间

这里有个关键点:种子和密钥的算法是OEM保密的。不同车企、不同ECU的算法都不一样。我见过最简单的就是种子直接异或一个固定值,也见过用AES加密的。你在做自动化脚本时,需要把算法集成到脚本里。

重要提醒:安全访问有次数限制和锁定时间。一般连续3-5次失败,ECU会锁定几分钟甚至几小时。我在项目里就遇到过,测试脚本写了个死循环,一直发错误的密钥,结果ECU锁了半小时,整个测试团队都在等我解锁。

0x27服务的请求格式:

请求种子:0x27 + 安全等级
例如请求编程会话的种子:0x27 0x05
响应种子:0x67 + 安全等级 + 种子数据
例如:0x67 0x05 0x12 0x34 0x56 0x78

发送密钥:0x27 + 安全等级 + 密钥数据
例如:0x27 0x06 0x9A 0xBC 0xDE 0xF0
响应:0x67 + 安全等级
例如:0x67 0x06

安全等级是什么意思?0x01/0x02是一级,0x03/0x04是二级,0x05/0x06是三级。不同等级对应不同的权限。刷写一般需要三级安全访问。

我的习惯:写自动化脚本时,我会把安全访问封装成一个独立函数。输入种子,输出密钥。这样不管算法怎么变,只需要改这个函数就行。另外,一定要加失败重试机制和最大尝试次数限制,防止脚本把ECU锁死。

好了,这一章的内容就到这里。0x10和0x27是刷写流程的敲门砖,搞不懂这两个服务,后面的刷写流程根本走不通。下一章我们讲0x34请求下载和0x36传输数据,那才是刷写的核心部分。