2. UDS协议入门:协议栈架构、SID分类与0x10服务
好,咱们正式开始啃UDS这块硬骨头。
说实话,我刚入行那会儿,看到UDS协议栈的文档,第一反应是——这玩意儿也太抽象了吧?一堆分层、一堆ID,完全不知道从哪下手。后来在项目里被ECU的通信问题折磨了几次,才慢慢摸到门道。
今天这一讲,咱们就把UDS的骨架搭起来。你只要搞懂三件事:协议栈怎么分层、SID怎么分类、0x10服务怎么玩。剩下的,都是在这基础上的血肉。
2.1 UDS协议栈架构:别被分层吓到
UDS协议栈,说白了就是一套通信规则。它规定了诊断仪(Tester)和ECU之间怎么说话、说什么话、听不懂怎么办。
我习惯把它想象成一个快递系统:
- 应用层:你填的快递单——我要查故障码、我要读数据。这就是SID(服务标识符)干的事。
- 会话层/表示层:快递单的格式——必须用标准模板,不能乱写。UDS规定了报文格式。
- 传输层:快递怎么运——走陆运还是空运?数据太大怎么拆包?CAN TP(ISO 15765-2)就是干这个的。
- 网络层/数据链路层:快递车和路——CAN总线、LIN总线这些物理通道。
嗯,这里要注意:UDS本身只定义应用层(ISO 14229-1)。下面那些层,是别的协议管的。但实际开发中,你经常得跟传输层打交道,尤其是做CAN诊断的时候。
我的经验:很多新手一上来就死磕应用层,结果报文发出去没响应,抓瞎了。其实八成是传输层配置错了——比如CAN ID没对上、或者发送周期太快把ECU撑死了。我建议你先用CANoe或者PCAN抓个正常的诊断流,看看人家怎么交互的,比看文档快十倍。
2.2 服务标识符(SID)分类:一张表搞定
SID就是诊断服务的身份证。每个服务都有一个唯一的ID,范围是0x00 ~ 0x3E(实际上常用的是0x10到0x3E)。
UDS把服务分成了6大类,我整理了一张表,你存着就行:
| 类别 | SID范围 | 典型服务 | 干啥用的 |
|---|---|---|---|
| 诊断和通信管理 | 0x10 ~ 0x3E | 0x10(诊断会话控制)、0x11(ECU复位)、0x28(通信控制) | 管理诊断会话、控制通信行为 |
| 数据传输 | 0x22 ~ 0x2E | 0x22(读取数据)、0x2E(写入数据) | 读写ECU内部数据(DID) |
| 存储数据传输 | 0x14 ~ 0x19 | 0x14(清除诊断信息)、0x19(读取故障码) | 操作DTC(诊断故障码) |
| 输入输出控制 | 0x2F ~ 0x31 | 0x2F(输入输出控制) | 强制控制ECU的输入输出(比如让某个继电器吸合) |
| 远程激活 | 0x20 ~ 0x21 | 0x20(停止例程)、0x31(启动例程) | 让ECU执行一段程序(比如自学习、标定) |
| 上传/下载 | 0x34 ~ 0x37 | 0x34(请求下载)、0x36(传输数据) | 刷写ECU固件 |
你可能会问:为什么SID不是连续的?比如0x10到0x3E中间跳了好多?
其实那些空着的,是留给OEM自定义的。比如大众、宝马都有自己的私有服务。我在项目里就遇到过,客户要求用0x3D做一个特殊的防盗匹配服务。嗯,这时候就得看具体的OEM规范了。
避坑指南:我曾经在解析一个诊断日志时,发现ECU对0x22服务没响应,但文档里明明写了支持。查了半天,原来是ECU当前处于扩展会话(0x10 03),而0x22服务只在默认会话(0x10 01)下可用。所以记住:服务能不能用,不光看SID,还得看当前会话状态。
2.3 诊断会话控制(0x10服务):ECU的“工作模式”开关
0x10服务,是UDS里最基础、最重要的服务。没有之一。
为什么?因为ECU不是所有功能都随时开放的。比如刷写固件这种危险操作,ECU必须进入一个“高权限模式”才能执行。0x10服务就是用来切换这些模式的。
2.3.1 三种标准会话
UDS定义了三种标准会话,用子功能(Sub-function)来区分:
- 0x01 默认会话(Default Session):ECU上电后的默认状态。只支持基本诊断服务(比如读故障码)。说白了就是“只读模式”。
- 0x02 编程会话(Programming Session):用于刷写固件。权限最高,但通信速度可能受限(为了可靠性)。
- 0x03 扩展会话(Extended Session):用于高级诊断(比如标定、输入输出控制)。权限介于默认和编程之间。
举个例子,你想想看:
// 诊断仪发送:请求进入扩展会话
请求:02 10 03 00 00 00 00 00
// 解释:02是CAN报文长度,10是SID,03是子功能(扩展会话)
// ECU响应:肯定响应
响应:02 50 03 00 00 00 00 00
// 解释:50 = 10 + 0x40(肯定响应标志),03表示当前会话
注意看,ECU的响应SID是0x50,不是0x10。这是UDS的规则:肯定响应 = 请求SID + 0x40。如果ECU返回0x7F(否定响应),那就说明出问题了。
警告:千万不要以为切换会话是瞬间完成的。我遇到过一些ECU,从默认会话切到编程会话,需要几百毫秒甚至更久。如果你紧接着就发刷写命令,ECU会直接给你一个否定响应(0x78,请求正确但正在处理)。所以,切换会话后,一定要等ECU响应了,再发下一个请求。
2.3.2 会话的“超时”机制
ECU不会永远待在扩展会话里。它有一个S3定时器(通常5秒左右)。如果在这个时间内没有收到任何诊断请求,ECU会自动跳回默认会话。
为什么要这样设计?
说白了就是安全考虑。万一诊断仪掉线了,ECU不能一直开着高权限模式,否则被黑客利用怎么办?
所以,如果你在做长时间的标定或刷写,需要定期发送0x3E(Tester Present)服务来“续命”。这个服务就像心跳包,告诉ECU:“我还活着,别切回去。”
我的习惯:在写自动化测试脚本时,我会在每次发送诊断请求前,先检查当前会话状态。如果发现会话超时了,就自动重新发送0x10服务。这样能避免很多莫名其妙的失败。另外,0x3E服务最好每隔2~3秒发一次,别卡着S3定时器的极限值,万一网络延迟一下就断了。
2.3.3 否定响应码(NRC)
当ECU无法执行0x10服务时,会返回否定响应。常见的NRC有:
- 0x12:子功能不支持(比如你发了0x10 04,但ECU不支持)
- 0x22:条件不满足(比如正在刷写时,不允许切换会话)
- 0x78:请求正确但正在处理(ECU需要时间准备)
举个例子,我曾经调试一个ECU,发送0x10 03后一直收到0x7F 0x10 0x78。一开始我以为是ECU死机了,后来查文档才发现,这个ECU从默认会话切到扩展会话,需要先完成一个内部自检,耗时约1秒。0x78就是告诉诊断仪:“别急,我还在忙。” 嗯,这时候你只需要等待,然后重发请求即可。
2.4 小结
这一讲,我们搞定了UDS的三大基础:
- 协议栈架构:应用层(UDS)→ 传输层(CAN TP)→ 网络层(CAN)。别被分层吓到,你主要关注应用层就行。
- SID分类:6大类,记住0x10、0x22、0x19、0x2E这几个高频服务就够了。
- 0x10服务:ECU的“模式开关”,默认会话、编程会话、扩展会话。切换后记得发0x3E续命。
下一讲,我们会深入0x22和0x2E服务——怎么读写ECU内部的数据。到时候你会看到,DID(数据标识符)才是真正的宝藏。
课后练习:用CANoe或者任何诊断工具,连接一个真实ECU(或者模拟器)。尝试发送0x10 03切换到扩展会话,然后发送0x22读取一个DID(比如VIN码)。看看ECU的响应是什么。如果收到否定响应,试着分析NRC的含义。