2. UDS协议基础:UDS协议栈架构、服务标识符(SID)分类、诊断会话控制
大家好,我是老张。今天咱们聊聊UDS协议的基础。说实话,很多刚入行的工程师一看到UDS协议栈就头大,觉得太复杂。其实没那么可怕,你把它拆开来看,无非就是三件事:谁在说话、说什么话、什么时候能说。
我在项目里见过不少同事,上来就啃协议栈源码,结果越看越迷糊。我个人习惯是,先搞懂架构,再抠细节。今天我就按这个思路来。
2.1 UDS协议栈架构
UDS协议栈,说白了就是一套分层结构。它基于OSI七层模型,但在汽车电子里,我们通常只关心下面几层。
| OSI层 | UDS对应层 | 典型协议/功能 |
|---|---|---|
| 应用层 | UDS应用层 | 诊断服务(如读取DTC、写入数据) |
| 表示层 | (通常合并到应用层) | 数据格式定义 |
| 会话层 | (通常合并到传输层) | 会话管理 |
| 传输层 | ISO 15765-2 (DoCAN) | 分段传输、流控制 |
| 网络层 | ISO 15765-2 (网络层) | 地址分配、路由 |
| 数据链路层 | CAN 2.0 / CAN FD | 帧格式、仲裁 |
| 物理层 | ISO 11898 | 电气特性、位时序 |
你想想看,最上层是咱们写的诊断服务代码,最下层是CAN总线上的电平信号。中间这些层,就是帮我们把"读故障码"这种高级指令,一步步变成CAN总线上的0和1。
嗯,这里要注意:在DoCAN(CAN上的诊断协议)里,传输层特别重要。它负责把超过8字节的诊断消息拆成多个CAN帧,再在接收端拼回去。我刚开始做OTA刷写时,就遇到过因为传输层配置不对,导致大文件传输总超时的问题。后来发现是流控制参数没调好。
核心要点:UDS协议栈的每一层都有明确分工。应用层只管"做什么",传输层只管"怎么传",底层只管"怎么发"。各层之间通过标准接口通信,互不干扰。
2.2 服务标识符(SID)分类
SID,全称Service Identifier,就是诊断服务的身份证号。每个诊断服务都有一个唯一的SID,范围是0x00到0x3E。
我个人习惯把SID分成四大类,这样好记:
- 读取类(0x10-0x1F):从ECU读取信息。比如读取数据(0x22)、读取故障码(0x19)。
- 写入类(0x20-0x2F):向ECU写入数据。比如写入数据(0x2E)、写入内存(0x3D)。
- 控制类(0x30-0x3F):控制ECU行为。比如例程控制(0x31)、输入输出控制(0x2F)。
- 特殊类(0x10、0x11等):会话控制、ECU复位等特殊操作。
举个例子,OTA刷写最常用的几个SID:
- 0x10:诊断会话控制。刷写前必须先切换到扩展会话或编程会话。
- 0x27:安全访问。刷写前要解锁,防止非法操作。
- 0x34:请求下载。告诉ECU:"我要发数据了,准备好接收。"
- 0x36:传输数据。真正发刷写数据。
- 0x37:请求传输退出。告诉ECU:"数据发完了,你处理一下。"
- 0x31:例程控制。刷写完成后,执行校验或复位。
小技巧:SID的响应也有规律。请求SID是0xXX,肯定响应就是0xCX(请求SID + 0x40)。比如0x22的肯定响应是0x62。否定响应则统一用0x7F开头,后面跟请求SID和错误码。
我曾经在项目里遇到过一个坑:某个ECU对0x22服务的肯定响应,返回的SID居然是0x62没错,但数据格式跟协议文档对不上。查了两天才发现,是供应商把字节序搞反了。所以啊,协议归协议,实际实现还得靠实测验证。
2.3 诊断会话控制
诊断会话控制,服务ID是0x10。这个服务特别重要,它决定了ECU当前处于什么"模式"。
UDS定义了三种标准会话:
| 会话类型 | 子功能ID | 说明 |
|---|---|---|
| 默认会话 | 0x01 | 上电后的默认状态,功能受限 |
| 扩展会话 | 0x02 | 允许更多诊断服务,比如读写数据 |
| 编程会话 | 0x03 | 允许刷写操作,通常需要安全解锁 |
为什么要有会话控制?说白了就是安全考虑。你想想看,如果车辆在行驶中,突然有人通过诊断口刷写ECU,那得多危险。所以ECU默认只允许读故障码这种基本操作。想干"大事",得先切换到对应会话。
会话切换的流程很简单:
// 请求:切换到编程会话
// 请求ID: 0x10 0x03
// 肯定响应: 0x50 0x03
// 如果切换失败,返回否定响应
// 0x7F 0x10 0x12 // 0x12表示子功能不支持
嗯,这里有个细节:会话是有超时时间的。ECU内部有个定时器,如果在规定时间内没有收到任何诊断请求,就会自动切回默认会话。这个时间通常是5秒左右,但不同ECU可能不一样。
注意:在OTA刷写过程中,一定要定期发送诊断请求(比如0x3E 0x00 TesterPresent),否则ECU会认为诊断会话超时,自动退出编程会话。我见过有人刷写到一半去接电话,回来发现ECU已经切回默认会话了,刷写失败。
另外,会话切换时,ECU可能会做一些内部处理。比如从默认会话切到编程会话,ECU可能会关闭一些非必要的功能模块,释放内存资源。我在一个项目中就遇到过,切到编程会话后,CAN通信的波特率居然变了,搞得我排查了半天。
最后说一句,会话控制是UDS协议里最基础也最容易出错的地方。我个人建议,在写诊断测试用例时,第一个测试项就应该是会话切换。如果这个都过不了,后面的服务根本跑不起来。
好了,这一章就到这里。下一章咱们聊聊安全访问和刷写流程,那才是OTA刷写的重头戏。