打开网易新闻 查看精彩图片

一、UDS 简介

UDS(Unified Diagnostic Services,统一诊断服务)是汽车电子领域广泛使用的诊断协议标准,定义于 ISO 14229 系列规范。它基于客户端-服务器模型,外部诊断仪(Tester)通过请求报文与车内电子控制单元(ECU)通信,实现读取故障码、读写数据、执行例程、控制会话状态等功能。

UDS 服务使用服务标识符(SID)区分不同功能,每个服务通常包含一个或多个子功能(Subfunction),子功能用于细化操作(例如启用/禁用某种功能、切换模式等)。请求和响应报文遵循固定格式:

  • 请求报文[SID] + [子功能(可选)] + [数据参数(可选)]

  • 肯定响应[SID + 0x40] + [子功能(若请求中包含)] + [响应数据]

  • 否定响应[0x7F] + [请求 SID] + [错误码]

二、0x10 服务:诊断会话控制 (Diagnostic Session Control) 1. 功能描述

0x10服务用于控制 ECU 内部的诊断会话状态。ECU 通常支持多种诊断会话模式,例如:

  • **默认会话 (Default Session)**:基本诊断功能,如读取故障码、读取数据等,安全等级最低。

  • **编程会话 (Programming Session)**:允许写入内存、刷写固件等高风险操作,通常需要先解锁安全访问。

  • **扩展会话 (Extended Session)**:支持更多诊断服务(如例程控制、写入数据),介于默认与编程之间。

不同会话下,ECU 可用的服务集合、通信定时参数、安全权限均不相同。通过切换会话,诊断仪可以根据当前任务激活最合适的 ECU 状态。

2. 子功能定义(符合 ISO 14229-1)

子功能值(十六进制)

会话名称

0x01

默认会话 (Default)

上电后默认会话,支持基础诊断服务

0x02

编程会话 (Programming)

用于 ECU 刷写、BootLoader 激活

0x03

扩展会话 (Extended)

提供额外的诊断服务(如写数据)

0x04

~0x7F

保留

ISO 预留

0x80

~0xBF

制造商自定义

OEM 专用会话

0xC0

~0xFF

供应商自定义

供应商专用会话

3. 报文格式 请求报文(诊断仪 → ECU)

字节

名称

值(示例)

1

SID

0x10

诊断会话控制服务标识

2

子功能

0x01

要切换的会话类型(见上表)

注意:子功能的最高位(Bit7)是抑制肯定响应位(suppressPosRspMsgIndicationBit)。当该位为1时,ECU 不发送肯定响应,仅发送否定响应(若出错)。常见实现中,该位默认为0(即发送肯定响应)。上表中的子功能值均为0x010x02等,未设置该位。

肯定响应报文(ECU → 诊断仪)

字节

名称

值(示例

1

响应 SID

0x50

请求 SID + 0x40

2

子功能

0x01

当前激活的会话(与请求相同)

3

会话参数记录(可选)

0x00

,0x00...

包含定时参数、会话超时时间等(长度可变)

否定响应报文(ECU → 诊断仪)

字节

名称

值(示例)

1

否定响应 SID

0x7F

固定值

2

请求 SID

0x10

指明哪个服务被拒绝

3

错误码 (NRC)

0x12

具体错误原因(见 NRC 表)

常见的否定响应码(NRC):

  • 0x11:服务不支持(子功能无效)

  • 0x12:子功能不支持或不合法

  • 0x13:报文长度错误

  • 0x22:条件不满足(例如当前会话下无法切换)

  • 0x7E:子功能序列错误

  • 0x7F:服务未激活(在当前会话下不可用)

4. 通信示例 示例 1:切换到默认会话

请求10 01(SID=0x10,子功能=0x01)
肯定响应50 01(SID=0x50,子功能=0x01,无参数记录)

示例 2:切换到编程会话

请求10 02
肯定响应50 02

示例 3:切换到扩展会话

请求10 03
肯定响应50 03

若 ECU 处于默认会话且已执行安全访问解锁,则0x020x03的切换应被允许。否则可能返回0x22(条件不满足)。

三、C++ 代码示例

以下代码模拟一个简单的 UDS 诊断客户端,实现0x10服务的请求构造、发送(使用模拟的传输层)和响应解析。实际应用中,底层传输可能基于 CAN(ISO 15765)、DoIP 或串口,此处用控制台打印模拟通信过程。

#include  

#include
#include

// 模拟诊断通信类
class UDSClient {
public:
// 模拟发送UDS请求并接收响应(实际应实现CAN/LIN/TCP等通信)
// 这里用控制台模拟传输过程,返回模拟的ECU响应数据
std::vector Transceive(const std::vector& request) {
std::cout << "发送UDS请求: ";
for (auto byte : request) {
printf("%02X ", byte);
}
std::cout << std::endl;

// 根据请求内容模拟ECU响应
if (request.size() < 2 || request[0] != 0x10) {
// 非0x10服务或长度错误,返回否定响应: 7F 10 13 (报文长度错误)
return {0x7F, 0x10, 0x13};
}

uint8_t subFunction = request[1];
// 检查子功能是否有效(仅支持0x01,0x02,0x03)
if (subFunction != 0x01 && subFunction != 0x02 && subFunction != 0x03) {
// 子功能不支持: 7F 10 12
return {0x7F, 0x10, 0x12};
}

// 模拟条件检查:例如从当前会话切换到编程/扩展会话可能需要先解锁安全访问
// 这里简化处理,总是允许切换,但打印提示
if (subFunction == 0x02) {
std::cout << "模拟条件检查: 切换到编程会话,假设安全解锁已完成。" << std::endl;
} else if (subFunction == 0x03) {
std::cout << "模拟条件检查: 切换到扩展会话,允许。" << std::endl;
}

// 构造肯定响应: 0x50 + 子功能
std::vector positiveResponse = {0x50, subFunction};
return positiveResponse;
}

// 诊断会话控制服务
bool DiagnosticSessionControl(uint8_t subFunction) {
// 构建请求报文: [SID, 子功能]
std::vector request = {0x10, subFunction};

// 发送请求并获取响应
std::vector response = Transceive(request);

// 解析响应
if (response.empty()) {
std::cerr << "错误: 未收到响应。" << std::endl;
return false;
}

// 检查否定响应
if (response[0] == 0x7F) {
if (response.size() >= 3 && response[1] == 0x10) {
std::cerr << "否定响应: NRC = 0x" << std::hex << (int)response[2] << std::dec;
switch (response[2]) {
case 0x12: std::cerr << " (子功能不支持)"; break;
case 0x13: std::cerr << " (报文长度错误)"; break;
case 0x22: std::cerr << " (条件不满足)"; break;
default: std::cerr << " (未知错误)";
}
std::cerr << std::endl;
return false;
} else {
std::cerr << "无效的否定响应格式" << std::endl;
return false;
}
}
// 检查肯定响应
else if (response[0] == 0x50 && response.size() >= 2) {
uint8_t respSubFunc = response[1];
if (respSubFunc == subFunction) {
std::cout << "肯定响应: 成功切换到会话 0x" << std::hex << (int)subFunction << std::dec;
// 额外输出会话名称
if (subFunction == 0x01) std::cout << " (默认会话)";
else if (subFunction == 0x02) std::cout << " (编程会话)";
else if (subFunction == 0x03) std::cout << " (扩展会话)";
std::cout << std::endl;
return true;
} else {
std::cerr << "响应中的子功能(" << std::hex << (int)respSubFunc << ")与请求("
<< (int)subFunction << ")不匹配" << std::dec << std::endl;
return false;
}
}
else {
std::cerr << "未知响应格式: SID = 0x" << std::hex << (int)response[0] << std::dec << std::endl;
return false;
}
}
};

int main() {
UDSClient uds;
// 测试切换到默认会话
std::cout << "\n--- 测试: 切换到默认会话 (0x01) ---" << std::endl;
uds.DiagnosticSessionControl(0x01);
// 测试切换到编程会话
std::cout << "\n--- 测试: 切换到编程会话 (0x02) ---" << std::endl;
uds.DiagnosticSessionControl(0x02);
// 测试切换到扩展会话
std::cout << "\n--- 测试: 切换到扩展会话 (0x03) ---" << std::endl;
uds.DiagnosticSessionControl(0x03);
// 测试无效子功能
std::cout << "\n--- 测试: 无效子功能 (0xFF) ---" << std::endl;
uds.DiagnosticSessionControl(0xFF);
return 0;
}
代码说明
  1. **类UDSClient**:封装诊断通信基本功能。

  • 构建请求报文{0x10, subFunction}

  • 调用Transceive()获取响应。

  • 解析响应,区分肯定响应(0x50)与否定响应(0x7F),打印对应信息并返回布尔结果。

  • Transceive():模拟底层传输。实际应用中,该函数应替换为真实的 CAN/LIN/TCP 发送与接收。代码内部根据请求内容模拟肯定响应或否定响应(子功能校验、长度校验)。

  • DiagnosticSessionControl():实现0x10服务的完整流程:

主函数测试:分别请求切换到默认会话、编程会话、扩展会话,并测试一个无效子功能(0xFF),验证否定响应处理。

运行输出示例

--- 测试: 切换到默认会话 (0x01) ---
发送UDS请求: 10 01
肯定响应: 成功切换到会话 0x1 (默认会话)

--- 测试: 切换到编程会话 (0x02) ---
发送UDS请求: 10 02
模拟条件检查: 切换到编程会话,假设安全解锁已完成。
肯定响应: 成功切换到会话 0x2 (编程会话)

--- 测试: 切换到扩展会话 (0x03) ---
发送UDS请求: 10 03
模拟条件检查: 切换到扩展会话,允许。
肯定响应: 成功切换到会话 0x3 (扩展会话)

--- 测试: 无效子功能 (0xFF) ---
发送UDS请求: 10 FF
否定响应: NRC = 0x12 (子功能不支持)
四、总结
  • 0x10服务是 UDS 中基础且重要的服务,用于在不同诊断会话间切换,控制 ECU 对外提供的诊断接口权限。

  • 子功能(0x01~0x03)定义标准会话,更高值保留或制造商自定义。

  • 请求报文简单(2字节),肯定/否定响应格式清晰。

  • 实现时需注意:

    • 子功能的抑制肯定响应位(Bit7)会影响 ECU 是否返回肯定响应。

    • 某些会话切换可能需要前置条件(例如安全解锁)。

    • 实际产品中,需结合传输协议(如 ISO 15765-2 的 CAN 传输层)处理分段和流控。

通过上述 C++ 示例,开发者可以快速理解并集成0x10服务到自己的诊断工具或 ECU 软件中,仅需替换Transceive()函数中的模拟通信为实际总线操作即可。