1. UDS协议概述:从背景到实践
大家好,我是你们的老朋友。今天咱们开始聊UDS协议——这个在汽车电子领域绕不开的话题。说实话,我刚入行那会儿,UDS对我来说就是个黑盒子。直到有一次,我在调试一个ECU的通信问题时,被逼着把协议栈从头啃了一遍...嗯,从那以后,我才真正理解了它的价值。
1.1 UDS协议背景:为什么需要它?
先说说背景。汽车电子发展到现在,一辆车里有几十甚至上百个ECU(电子控制单元)。每个ECU都可能出问题,怎么诊断?怎么维修?
早期各家车企各玩各的,诊断协议五花八门。比如大众用KWP2000,福特用UBP,通用用GMLAN。这给维修厂和诊断工具厂商带来了巨大麻烦——你想想看,一个诊断仪要支持几十种协议,光适配就够头疼的。
2006年,ISO 14229标准发布,UDS(Unified Diagnostic Services)正式登场。它的目标很明确:统一诊断服务的接口和流程。说白了,就是让所有ECU用同一种语言说话。
核心要点:UDS不是通信协议,而是应用层协议。它定义的是「服务」——比如读故障码、写数据、执行例程等。至于底层怎么传,那是CAN、LIN、以太网的事。
我个人习惯把UDS比作「汽车医生的问诊语言」。医生问「你哪里疼?」病人回答「我头疼」——这就是一个服务请求和响应。UDS做的就是定义这些「问诊术语」和「回答格式」。
1.2 OSI七层模型与UDS:它在哪一层?
很多初学者搞不清UDS和CAN的关系。我经常被问到:「UDS是不是跑在CAN上的?」答案是:UDS可以跑在CAN上,但不止于此。
咱们看看OSI七层模型:
| OSI层 | 功能 | UDS相关 |
|---|---|---|
| 7 应用层 | 用户接口 | UDS (ISO 14229-1) |
| 6 表示层 | 数据格式化 | UDS数据格式定义 |
| 5 会话层 | 会话管理 | 诊断会话控制 |
| 4 传输层 | 端到端传输 | ISO 15765-2 (CAN TP) |
| 3 网络层 | 路由与寻址 | CAN ID、N_TA等 |
| 2 数据链路层 | 帧封装 | CAN 2.0 / CAN FD |
| 1 物理层 | 电气信号 | CAN High/Low |
看到了吗?UDS在第七层——应用层。它不关心底层是CAN还是以太网,它只定义「服务」本身。
我的经验:在项目中,我经常看到有人把UDS和CAN混为一谈。记住:UDS是「说什么」,CAN是「怎么说」。你可以在CAN上跑UDS,也可以在以太网上跑UDS(DoIP),甚至可以在LIN上跑UDS的子集。
为什么会这样设计?因为汽车电子架构在演进。从CAN到CAN FD,再到车载以太网,底层通信技术在变,但诊断服务的逻辑可以保持不变。这就是分层的好处。
1.3 UDS服务分类:三大类服务
UDS定义了26个服务(SID,Service Identifier),分为三大类:
1.3.1 诊断和通信管理类
这类服务负责管理诊断会话本身。包括:
- 10 服务 - 诊断会话控制(后面重点讲)
- 11 服务 - ECU复位
- 27 服务 - 安全访问(解锁ECU)
- 28 服务 - 通信控制(比如关闭某些报文)
- 3E 服务 - 待机握手(防止会话超时)
- 83 服务 - 访问时间参数
1.3.2 数据传输类
这类服务负责读写数据:
- 22 服务 - 按标识符读取数据(ReadDataByIdentifier)
- 23 服务 - 按地址读取内存(ReadMemoryByAddress)
- 2E 服务 - 按标识符写入数据(WriteDataByIdentifier)
- 3D 服务 - 写入内存(WriteMemoryByAddress)
1.3.3 存储数据传输类
这类服务处理故障码和例程:
- 14 服务 - 清除诊断信息(清故障码)
- 19 服务 - 读取故障码信息
- 31 服务 - 例程控制(执行特定程序)
- 34 服务 - 请求下载(刷写用)
- 36 服务 - 传输数据(刷写用)
- 37 服务 - 请求退出传输
避坑指南:我曾经在项目中遇到一个问题——用22服务读数据时,ECU返回了否定响应。查了半天,发现是会话没切到扩展模式。记住:很多服务需要在特定会话模式下才能用。这是新手最容易踩的坑。
1.4 诊断会话控制:一切从这里开始
诊断会话控制(10服务)是UDS的入口。为什么这么说?因为ECU上电后默认处于非诊断模式,你要先告诉它:「嘿,我要开始诊断了!」
10服务的请求格式很简单:
请求: 10 [子功能]
响应: 50 [子功能] [P2ServerMax] [P2StarServerMax]
子功能定义了会话类型:
| 子功能 | 会话名称 | 说明 |
|---|---|---|
| 0x01 | 默认会话 | ECU上电后的默认状态,功能受限 |
| 0x02 | 编程会话 | 用于刷写ECU,通常需要安全访问 |
| 0x03 | 扩展会话 | 用于读写数据、执行例程等 |
| 0x04 | 安全系统诊断会话 | 用于安全相关系统(如气囊) |
响应中的P2ServerMax和P2StarServerMax是两个超时参数:
- P2ServerMax:ECU处理请求的最大时间(默认50ms)
- P2StarServerMax:ECU处理请求的扩展时间(默认2000ms)
我的习惯:在实际开发中,我会先发一个10 01切换到默认会话(虽然ECU已经在默认会话了),目的是确认通信链路正常。然后再发10 03切换到扩展会话。这叫「握手确认」,能避免很多通信问题。
这里有个重要概念——会话超时。ECU进入诊断会话后,如果一段时间内没有收到任何诊断请求(默认5秒),会自动切回默认会话。所以你需要定期发3E服务(待机握手)来保持会话。
注意:不同ECU的会话超时时间可能不同。我遇到过某款ECU的扩展会话超时只有2秒,导致我频繁掉线。后来查了数据手册才发现。所以,拿到一个新ECU,第一件事就是查它的会话超时参数。
最后,用一段Python代码演示诊断会话控制的流程:
import can
def send_diagnostic_request(bus, arb_id, data):
"""发送UDS诊断请求"""
msg = can.Message(
arbitration_id=arb_id,
data=data,
is_extended_id=False
)
bus.send(msg)
# 等待响应
response = bus.recv(timeout=1.0)
return response
def switch_to_extended_session(bus, arb_id=0x7DF):
"""切换到扩展会话"""
# 10 03 - 请求扩展会话
request = [0x10, 0x03]
response = send_diagnostic_request(bus, arb_id, request)
if response and response.data[0] == 0x50:
print(f"切换到扩展会话成功")
print(f"P2ServerMax: {response.data[2]} ms")
print(f"P2StarServerMax: {response.data[3]} ms")
return True
else:
print(f"切换失败,响应: {response.data}")
return False
# 使用示例
bus = can.interface.Bus(channel='can0', bustype='socketcan')
switch_to_extended_session(bus)
嗯,代码很简单,但实际项目中要考虑的东西很多——比如CAN ID的过滤、多帧传输、超时重试等。这些后面章节会详细讲。
总结一下今天的内容:UDS是汽车诊断的统一语言,它在OSI第七层工作,定义了三大类服务。而诊断会话控制(10服务)是所有诊断操作的前提。记住:没有会话,就没有诊断。
下一章,咱们深入聊聊「22服务 - 按标识符读取数据」,这是实际项目中最常用的服务之一。到时候我会分享一些我在数据解析上踩过的坑。