3、UDS诊断服务基础:UDS服务分类(强制/可选)、常用服务ID(SID)介绍、诊断会话控制(0x10服务)
好,咱们正式开始聊UDS诊断服务。这一节是基础中的基础,但也是整个Bootloader诊断功能的骨架。你想想看,ECU刷写、故障排查、产线测试,哪一样离得开UDS?我个人习惯是把UDS服务先分个类,心里有个谱,后面学起来就顺了。
3.1 UDS服务分类:强制与可选
UDS(ISO 14229)定义了一大堆诊断服务,但不是每个服务都必须实现。标准里把它们分成了三类:强制服务、条件服务和可选服务。说白了,强制服务是底线,你不实现它,诊断仪连门都进不来。
强制服务(Mandatory):
- 诊断会话控制(0x10)—— 必须要有,这是入口
- 安全访问(0x27)—— 必须要有,这是锁
- 通信控制(0x28)—— 必须要有,用于控制报文收发
- 测试仪在线(0x3E)—— 必须要有,保持会话不超时
- ECU复位(0x11)—— 必须要有,刷完要重启
条件服务(Conditional):
- 比如读取数据(0x22)、写入数据(0x2E),如果你的ECU需要标定或读取内部参数,那就得实现
- 例程控制(0x31),如果你的ECU需要执行特定算法(比如擦除Flash),那就得实现
可选服务(Optional):
- 比如读取故障码(0x19)、清除故障码(0x14),如果你的ECU不需要诊断故障码,可以不做
- 但说实话,现在几乎没有ECU不做故障码的,所以实际上大部分可选服务也成了事实上的标配
重要提醒:在Bootloader中,我们通常只实现刷写相关的服务。比如0x10、0x27、0x28、0x3E、0x11、0x22、0x2E、0x31、0x34、0x36、0x37。其他服务(如0x19)可以放在应用层实现。
3.2 常用服务ID(SID)介绍
UDS服务ID是一个字节,范围从0x00到0xFF。但常用的就那么十几个。我整理了一张表,你直接拿去用:
| SID | 服务名称 | 强制/可选 | Bootloader中是否常用 |
|---|---|---|---|
| 0x10 | 诊断会话控制 | 强制 | 是 |
| 0x11 | ECU复位 | 强制 | 是 |
| 0x22 | 读取数据 | 条件 | 是(读版本号等) |
| 0x27 | 安全访问 | 强制 | 是 |
| 0x28 | 通信控制 | 强制 | 是 |
| 0x2E | 写入数据 | 条件 | 是(写配置等) |
| 0x31 | 例程控制 | 条件 | 是(擦除/检查Flash) |
| 0x34 | 请求下载 | 条件 | 是 |
| 0x36 | 数据传输 | 条件 | 是 |
| 0x37 | 请求退出传输 | 条件 | 是 |
| 0x3E | 测试仪在线 | 强制 | 是 |
嗯,这里要注意:SID的bit6(0x40)是响应位。比如你发0x10请求,ECU正常响应时返回0x50。这个设计挺巧妙的,一眼就能看出是请求还是响应。
3.3 诊断会话控制(0x10服务)
0x10服务是所有诊断服务的入口。没有它,其他服务都用不了。为什么?因为ECU上电后默认处于默认会话(Default Session),在这个会话里,大部分诊断服务是被禁用的。你想想看,如果随便一个报文就能让ECU进入刷写模式,那车在路上开着开着突然被刷了,多危险。
0x10服务的请求格式:
请求:0x10 + 子功能(会话类型)
响应:0x50 + 子功能 + P2_Server_Max + P2_Star_Server_Max
常见的会话类型:
- 0x01:默认会话(Default Session)—— 上电后的初始状态,功能受限
- 0x02:编程会话(Programming Session)—— 刷写专用,Bootloader里最常用
- 0x03:扩展会话(Extended Session)—— 用于标定、调试,功能比默认多但比编程少
- 0x04:安全系统诊断会话(Safety System Diagnostic Session)—— 用于安全相关系统
个人经验:我在项目中遇到过一个问题:ECU从编程会话切回默认会话时,没有正确清理资源,导致下次进入编程会话时Flash操作失败。后来我加了一个会话退出时的资源清理函数,问题就解决了。所以,会话切换时一定要做好状态机管理。
0x10服务的响应中,P2_Server_Max和P2_Star_Server_Max是什么意思?
这两个参数告诉诊断仪:ECU处理一个请求需要多长时间。P2_Server_Max是正常响应时间,默认50ms。P2_Star_Server_Max是扩展响应时间,默认2000ms。如果ECU需要更长时间处理(比如擦除Flash),就会先发一个0x78的挂起响应,告诉诊断仪「别急,我在忙」。我曾经调试过一个Bug,就是P2_Star时间设得太短,导致诊断仪超时重发请求,结果ECU同时处理两个擦除命令,直接挂了。
0x10服务的状态机流转:
默认会话 (0x01)
|
├── 请求0x10 0x02 → 进入编程会话
├── 请求0x10 0x03 → 进入扩展会话
└── 请求0x10 0x01 → 保持在默认会话(或从其他会话切回)
编程会话 (0x02)
|
├── 请求0x10 0x01 → 切回默认会话
├── 请求0x10 0x03 → 切换到扩展会话
└── 请求0x10 0x02 → 保持在编程会话
扩展会话 (0x03)
|
├── 请求0x10 0x01 → 切回默认会话
├── 请求0x10 0x02 → 切换到编程会话
└── 请求0x10 0x03 → 保持在扩展会话
避坑指南:我曾经遇到过一个案例:ECU在编程会话中收到了一个非预期的0x10请求(比如切到扩展会话),结果刷写流程被打断了,导致Flash数据损坏。所以,在Bootloader中,一定要对会话切换做严格的权限检查。比如,正在刷写时,只允许切回默认会话,不允许切到其他会话。
代码示例:0x10服务处理函数(简化版)
// 诊断会话控制服务处理
uint8_t DiagSessCtrl_Handle(uint8_t subFunc)
{
uint8_t response[4];
switch(subFunc)
{
case 0x01: // 默认会话
g_activeSession = SESSION_DEFAULT;
// 关闭编程相关功能
Flash_Disable();
break;
case 0x02: // 编程会话
// 检查是否允许进入编程会话
if(g_appStatus == APP_STATUS_RUNNING)
{
// 应用运行时,需要先请求进入编程模式
return NRC_CONDITIONS_NOT_CORRECT;
}
g_activeSession = SESSION_PROGRAMMING;
// 初始化Flash驱动
Flash_Init();
break;
case 0x03: // 扩展会话
g_activeSession = SESSION_EXTENDED;
break;
default:
return NRC_SUB_FUNCTION_NOT_SUPPORTED;
}
// 构建响应:0x50 + 子功能 + P2时间 + P2*时间
response[0] = 0x50;
response[1] = subFunc;
response[2] = 0x32; // P2_Server_Max = 50ms
response[3] = 0xE8; // P2_Star_Server_Max = 2000ms
// 发送响应
CanTp_SendResponse(response, 4);
return 0;
}
好了,这一节的内容就这些。0x10服务看似简单,但它是整个诊断体系的基石。你想想看,没有会话管理,ECU就像一扇没有锁的门,谁都能进。下一节我们聊安全访问(0x27服务),那是真正的「锁」。