网易首页 > 网易号 > 正文 申请入驻

汽车诊断协议精讲:ISO 14229(UDS)与 ISO 15765 的协作机制及代码示例

0
分享至


1. 引言

现代汽车电子系统由数十个甚至上百个电子控制单元(ECU)构成,它们通过 CAN、LIN、FlexRay 等总线协同工作。当车辆出现故障时,诊断工具(如诊断仪)需要与 ECU 通信,读取故障码、实时数据,执行动作测试等。这一套通信规则就是 汽车诊断协议。

在基于 CAN 总线的诊断应用中,最核心的两个标准是:

  • ISO 14229 :定义了 统一诊断服务(UDS,Unified Diagnostic Services) ,是 应用层 协议,规定了诊断请求和响应的格式、服务含义。

  • ISO 15765 :定义了 基于 CAN 总线的诊断传输(DoCAN,Diagnostics over CAN) ,是 传输层/网络层 协议,负责将应用层 UDS 消息拆分成适合 CAN 总线传输的小帧,并在接收端重组。

很多初学者容易混淆这两者。一句话总结:UDS 告诉你“问什么问题”,ISO 15765 教你“怎样把问题装进 CAN 帧里送过去”。而 ISO 14229 正是 UDS 的标准编号。

本文将从概念到代码,深入浅出地比较这两个协议,并给出 C++ 实现示例。

2. 协议栈概览(OSI 模型视角)

OSI 层

汽车诊断协议栈

标准

作用

应用层

UDS

ISO 14229

诊断服务定义:读数据、写数据、例程控制等

传输层

DoCAN (ISO 15765-2)

ISO 15765-2

分段、重组、流控制

数据链路层

CAN 2.0B

ISO 11898

8 字节 CAN 帧、仲裁、错误检测

物理层

CAN 物理层

ISO 11898

电平、拓扑


  • UDS (ISO 14229) 与 ISO 15765 不是对立关系,而是上下层协作关系。

  • ISO 15765 同时也定义了与 UDS 无关的 时间参数 (如 P2、P2* 等)和 通信控制 。

3. UDS (ISO 14229) 详解 —— “说什么”

UDS 是一套面向汽车场景设计的诊断服务集合,每个服务用 服务标识符(SID, Service Identifier) 表示(1 字节)。典型服务包括:

SID (hex)

服务名称

功能简述

0x10

Diagnostic Session Control

切换诊断会话(默认、编程、扩展等)

0x11

ECU Reset

重置 ECU

0x22

Read Data By Identifier

通过 DID 读取数据(如 VIN、软件版本)

0x2E

Write Data By Identifier

通过 DID 写入数据

0x19

Read DTC Information

读取故障码

0x14

Clear Diagnostic Information

清除故障码

0x31

Routine Control

启动/停止例程(如动作测试)


3.1 UDS 请求/响应格式

请求格式(诊断仪 → ECU):

[SID] [子功能(可选)] [参数...]

例如:读取 DID 0xF190 的数据(假设 DID 占 2 字节)

22 F1 90
  • 0x22 = ReadDataByIdentifier SID

  • 0xF190 = 数据标识符(如 VIN)

肯定响应格式(ECU 成功处理):

[SID+0x40] [子功能(若请求有)] [数据...]

例如对上述请求的肯定响应:

62 F1 90 41 32 ... (实际数据)
  • 0x62 = 0x22+0x40

否定响应格式(ECU 无法处理):

0x7F [SID] [NRC]

例如:请求的 DID 不支持,返回 7F 22 31(0x31 = RequestOutOfRange)

3.2 子功能与抑制肯定响应位

许多服务(如 SessionControl、ECUReset)的第一个字节称为子功能,其最高位(bit7)为 抑制肯定响应位(SPR, Suppress Pos Resp):

  • SPR = 1:ECU 不发送肯定响应,只发送否定响应(用于减少总线负载)。

  • SPR = 0:ECU 发送肯定响应。

例如:切换到扩展会话(子功能 0x03),且不需要肯定响应:

10 83   // 0x80 | 0x03 = 0x83
3.3 UDS 示例(无分段)

一个完整诊断请求通过 CAN 发送(假设不超 8 字节):

  • 请求: 10 03 (切换到扩展会话,需要肯定响应)

  • 响应: 50 03 00 32 01 F4 (肯定响应,附带一些参数)

4. ISO 15765 详解 —— “怎么送”

CAN 数据链路层一帧最多承载 8 字节数据,而一个 UDS 消息可能长达几十甚至几百字节(如读取 VIN 的 17 个字符,加上 DID 等)。ISO 15765-2(常称为 ISO-TP)定义了分段传输协议,将长消息切分成多个 CAN 帧,并管理发送/接收状态。

4.1 四种帧类型(N_PDU 类型)

帧类型

简称

用途

协议控制信息(PCI)字节

单帧

SF

消息总长 ≤ 7 字节(经典 CAN)

第一个字节高 4 位 = 0x0,低 4 位表示数据长度

首帧

FF

长消息的第一帧,告知总长度

第一个字节高 4 位 = 0x1,低 4 位 + 第二字节组成 12 位总长

连续帧

CF

首帧之后的后续数据

第一个字节高 4 位 = 0x2,低 4 位表示序列号(SN,0~15 循环)

流控制帧

FC

接收端控制发送端速率

第一个字节高 4 位 = 0x3,低 4 位表示流控制状态;后续字节包含块大小(BS)和最小间隔时间(STmin)


4.2 帧格式详表(经典 CAN,11 位 ID,单字节 PCI)

单帧 (SF)

| Byte0        | Byte1 ~ Byte7 (最多7字节数据) |
| 0x0 | Len | UDS 数据 (Len 字节) |
  • Len = 4 位,范围 0~7(实际长度 0~7,若消息总长 >7 必须用多帧)

  • 注意:有些实现使用 首个字节为 PCI,剩余 7 字节 ,Len 不包含 PCI 自身。

首帧 (FF)

| Byte0        | Byte1        | Byte2~Byte7 |
| 0x1 | Len_H | Len_L | 前 6 字节 UDS 数据 |
  • 总长度 = (Len_H << 8) | Len_L,范围 8~4095 字节

连续帧 (CF)

| Byte0        | Byte1~Byte7 |
| 0x2 | SN | 后续 7 字节 UDS 数据 |
  • SN 从 1 开始,每发一个 CF 加 1,到 15 后回绕到 0。

流控制帧 (FC)

| Byte0        | Byte1        | Byte2        | Byte3~Byte7(保留)|
| 0x3 | FS | BS (块大小) | STmin (最小间隔) | 0x00... |
  • FS (Flow Status):0=继续发送(CTS),1=等待(WAIT),2=溢出(OVFLW)

  • BS (Block Size):发送端每发 BS 个 CF 后,需等待下一个 FC 才可继续(0 表示无限制)

  • STmin:两个连续帧之间的最小间隔时间(0~127 或 0xF1~0xF9 编码)

4.3 多帧传输流程示例

发送方(如诊断仪)想发送一个 20 字节的 UDS 消息:

  1. 发送 首帧 :PCI=0x10,长度=20,并携带前 6 字节数据。

  2. 接收方收到 FF 后,计算所需 CF 数量,准备接收缓冲区,并发送 流控制帧 (BS, STmin)。

  3. 发送方根据 FC 发送 连续帧 ,每帧 7 字节数据,直到全部发完。

5. UDS 与 ISO 15765 的比较

比较维度

UDS (ISO 14229)

ISO 15765-2 (DoCAN)

OSI 层

应用层

传输层 / 网络层

关注点

诊断服务语义:做什么操作、数据含义、错误码

消息分段、流控制、CAN 帧打包与重组

数据单元

UDS 请求/响应(无长度上限)

N_PDU(单帧/首帧/连续帧/流控制帧)

关键参数

SID、子功能、DID、DTC、NRC

块大小(BS)、最小间隔(STmin)、序列号(SN)

独立性

可运行于其他传输层(如 DoIP、UART)

专为 CAN 设计,可传输任何上层数据(不仅是 UDS,也可以是 OBD、KWP2000)

标准文档

ISO 14229-1

ISO 15765-2

核心关系:UDS 消息被封装成 ISO 15765 的 N_PDU 载荷。没有 ISO 15765,UDS 无法穿越 CAN 总线的 8 字节限制;没有 UDS,ISO 15765 只是在传输无意义的字节流。

6. C++ 代码举例:实现 ISO 15765 打包器与 UDS 消息组装

以下代码演示:

  • UDSRequest 类:构造一个简单的 UDS 请求(如 ReadDataByIdentifier)。

  • ISO15765Packer 类:将 UDS 请求字节流转换为多个 CAN 帧( vector )。

  • ISO15765Unpacker 类:将收到的 CAN 帧还原成 UDS 消息。

为了简洁,假设 CAN 帧结构为 {id, data[8], dlc},且使用标准 CAN ID(0x7DF 诊断请求,0x7E8 响应)。仅实现发送端打包逻辑。

6.1 基础数据结构

#include  
          
#include
#include
#include

struct CanFrame {
uint32_t id; // 标准 11 位 ID
uint8_t dlc; // 数据长度 (0~8)
uint8_t data[8];
};

// 辅助打印
void printCanFrame(const CanFrame& frame) {
std::cout << "CAN ID: 0x" << std::hex << frame.id << " DLC:" << std::dec << (int)frame.dlc << " Data:";
for (int i = 0; i < frame.dlc; ++i) {
std::cout << " " << std::hex << (int)frame.data[i];
}
std::cout << std::dec << std::endl;
}
6.2 UDS 请求构造(应用层)

class UDSRequest {
public:
UDSRequest(uint8_t sid, conststd::vector& data = {})
: m_sid(sid), m_data(data) {}

std::vector serialize() const {
std::vector msg;
msg.push_back(m_sid);
msg.insert(msg.end(), m_data.begin(), m_data.end());
return msg;
}

// 示例:构造读取 DID 的请求 (0x22)
static UDSRequest makeReadDataByIdentifier(uint16_t did) {
return UDSRequest(0x22, {static_cast(did >> 8), static_cast(did & 0xFF)});
}

private:
uint8_t m_sid;
std::vector m_data;
};
6.3 ISO 15765 打包器(将 UDS 消息转成 CAN 帧序列)

class ISO15765Packer {
public:
// 输入:UDS 原始字节流(无 PCI)
// 输出:CAN 帧列表(请求 ID 固定为 0x7DF 示例)
static std::vector pack(const std::vector& udsMsg, uint32_t canId = 0x7DF) {
std::vector frames;
size_t len = udsMsg.size();

if (len <= 7) {
// 单帧模式
CanFrame sf;
sf.id = canId;
sf.dlc = static_cast(1 + len); // PCI + 数据长度
sf.data[0] = static_cast(0x00 | len); // 单帧 PCI
std::copy(udsMsg.begin(), udsMsg.end(), &sf.data[1]);
frames.push_back(sf);
} else {
// 多帧模式:首帧 + 连续帧
// 首帧
CanFrame ff;
ff.id = canId;
ff.dlc = 8; // 首帧总是 8 字节
uint16_t totalLen = static_cast(len);
ff.data[0] = 0x10 | ((totalLen >> 8) & 0x0F); // 高 4 位为 1,低 4 位为长度高 4 位
ff.data[1] = totalLen & 0xFF; // 长度低 8 位
size_t firstPayload = std::min((size_t)6, len); // 首帧携带 6 字节数据
std::copy(udsMsg.begin(), udsMsg.begin() + firstPayload, &ff.data[2]);
frames.push_back(ff);

// 后续数据按 7 字节分块,生成连续帧
size_t offset = firstPayload;
uint8_t sn = 1; // 连续帧序列号从 1 开始
while (offset < len) {
CanFrame cf;
cf.id = canId;
cf.dlc = 8;
cf.data[0] = 0x20 | (sn & 0x0F); // 连续帧 PCI
size_t chunkSize = std::min((size_t)7, len - offset);
std::copy(udsMsg.begin() + offset, udsMsg.begin() + offset + chunkSize, &cf.data[1]);
// 如果不足 7 字节,剩余字节填 0x00(可选,但通常 DLC=8 会包含无用数据)
if (chunkSize < 7) {
std::fill(&cf.data[1 + chunkSize], &cf.data[8], 0x00);
}
frames.push_back(cf);
offset += chunkSize;
sn = (sn + 1) & 0x0F;
}
}
return frames;
}
};
6.4 使用示例

int main() {
// 1. 构造 UDS 请求:读取 DID = 0xF190
auto udsReq = UDSRequest::makeReadDataByIdentifier(0xF190);
std::vector udsMsg = udsReq.serialize(); // 输出: 0x22 0xF1 0x90 (3 字节)

// 2. 打包成 CAN 帧(单帧)
auto frames = ISO15765Packer::pack(udsMsg, 0x7DF);
for (constauto& frame : frames) {
printCanFrame(frame);
}

// 3. 模拟一个较长的 UDS 消息(例如 20 字节的写数据请求)
std::vector longMsg(20, 0xAA); // 模拟 20 字节数据
longMsg[0] = 0x2E; // WriteDataByIdentifier SID
longMsg[1] = 0xF1;
longMsg[2] = 0x90;
// 剩余 17 字节为写入值

auto framesLong = ISO15765Packer::pack(longMsg, 0x7DF);
std::cout << "\nLong message (20 bytes) packed into " << framesLong.size() << " frames:\n";
for (constauto& frame : framesLong) {
printCanFrame(frame);
}

return0;
}

预期输出:

CAN ID: 0x7df DLC:4 Data: 0 22 f1 90
Long message (20 bytes) packed into 4 frames:
CAN ID: 0x7df DLC:8 Data: 10 14 2e f1 90 aa aa aa // 首帧,总长度20
CAN ID: 0x7df DLC:8 Data: 21 aa aa aa aa aa aa aa // CF SN=1
CAN ID: 0x7df DLC:8 Data: 22 aa aa aa aa aa aa aa // CF SN=2
CAN ID: 0x7df DLC:8 Data: 23 aa aa aa aa aa aa aa // CF SN=3 (最后7字节,但剩余6字节,尾部补0)
6.5 ISO 15765 解包器(片段)

实际 ECU 需要接收 CAN 帧并重组。以下展示核心逻辑:

class ISO15765Unpacker {
public:
// 输入:一个 CAN 帧,输出:是否完成一个完整的 UDS 消息
bool feed(const CanFrame& frame, std::vector& outUdsMsg) {
if (frame.dlc == 0) returnfalse;
uint8_t pciType = (frame.data[0] >> 4) & 0x0F;
switch (pciType) {
case0x0: { // 单帧
uint8_t len = frame.data[0] & 0x0F;
outUdsMsg.assign(&frame.data[1], &frame.data[1] + len);
returntrue;
}
case0x1: { // 首帧
uint16_t totalLen = ((frame.data[0] & 0x0F) << 8) | frame.data[1];
m_buffer.clear();
m_buffer.reserve(totalLen);
// 首帧携带 6 字节数据
size_t payloadLen = std::min((size_t)6, frame.dlc - 2);
m_buffer.insert(m_buffer.end(), &frame.data[2], &frame.data[2] + payloadLen);
m_expectedLen = totalLen;
m_nextSn = 1;
m_waitingForFC = false; // 本示例简化流控制,实际需要等待 FC
returnfalse;
}
case0x2: { // 连续帧
uint8_t sn = frame.data[0] & 0x0F;
if (sn != m_nextSn) {
// 序列号错误,可做错误处理
returnfalse;
}
size_t payloadLen = std::min((size_t)7, frame.dlc - 1);
m_buffer.insert(m_buffer.end(), &frame.data[1], &frame.data[1] + payloadLen);
m_nextSn = (m_nextSn + 1) & 0x0F;
if (m_buffer.size() >= m_expectedLen) {
outUdsMsg.swap(m_buffer);
returntrue;
}
returnfalse;
}
case0x3: // 流控制帧(发送端处理,接收端不处理)
default:
returnfalse;
}
}
private:
std::vector m_buffer;
uint16_t m_expectedLen = 0;
uint8_t m_nextSn = 0;
bool m_waitingForFC = false;
};
7. 实际应用中的重要参数与注意事项
  1. CAN ID 分配:诊断通信通常使用 11 位 ID,如物理寻址(特定 ECU)和功能寻址(广播给所有 ECU)。UDS 不规定 ID,ISO 15765 也不规定,通常由整车厂定义(例如 0x7DF 用于功能请求,0x7E8 为第一个 ECU 的响应)。

  2. 流控制参数:

    • **BS (Block Size)**:接收端可以限制连续帧个数,防止发送端淹没接收端缓冲区。

    • **STmin (Separation Time)**:两个连续帧之间的最小时间间隔,用于低速 ECU。

  3. 时间参数 P2 / P2*:UDS 应用层定义了 ECU 处理请求的最大响应时间(P2 = 50ms 典型值),ISO 15765 不涉及这个,但诊断仪必须实现超时管理。

  4. 多协议并存:UDS 也可以运行在 DoIP(以太网)、LIN、FlexRay 上,而 ISO 15765 专用于 CAN。因此 ISO 15765 可视为 UDS over CAN 的“运输层”。

8. 结论
  • UDS (ISO 14229) 是汽车诊断的“语言”,定义了丰富而统一的服务,让诊断工具与不同 ECU 能够互通。

  • ISO 15765 是这套语言在 CAN 总线上的“信使”,解决了 CAN 帧过小的限制,提供了可靠的分段、重组与流控制机制。

  • 二者相辅相成:没有 UDS,ISO 15765 传输的只是无意义的字节;没有 ISO 15765,UDS 将无法穿越 CAN 总线的长度壁垒。

理解这两者的分工是开发任何基于 CAN 的诊断工具或 ECU 固件的基础。通过本文的 C++ 代码示例,读者可以亲手打包一个 UDS 请求,观察它如何被拆分成 CAN 帧,从而更直观地掌握汽车诊断协议栈的层次协作。在实际工程中,建议使用成熟的 ISO-TP 库(如 isotp-c)和 UDS 库,但掌握原理始终是调试复杂问题的关键。

特别声明:以上内容(如有图片或视频亦包括在内)为自媒体平台“网易号”用户上传并发布,本平台仅提供信息存储服务。

Notice: The content above (including the pictures and videos if any) is uploaded and posted by a user of NetEase Hao, which is a social media platform and only provides information storage services.

相关推荐
热点推荐
差严子怡8米!日本奥运冠军痛哭:不甘心让她一枝独秀 明年要赢她

差严子怡8米!日本奥运冠军痛哭:不甘心让她一枝独秀 明年要赢她

风过乡
2026-09-28 09:06:08
洛夫顿离开上海得不偿失!发文开启勿扰模式,重返大鲨鱼障碍重重

洛夫顿离开上海得不偿失!发文开启勿扰模式,重返大鲨鱼障碍重重

体育见习官
2026-09-28 07:58:50
时隔两年半冲突视频曝光!斯图尔特挥拳尤班克斯 曾因此被禁赛3场

时隔两年半冲突视频曝光!斯图尔特挥拳尤班克斯 曾因此被禁赛3场

罗说NBA
2026-09-28 06:21:57
曼联无意让卡里克下课,重用拉什福德起作用!聘拉爵心仪主帅需1500万

曼联无意让卡里克下课,重用拉什福德起作用!聘拉爵心仪主帅需1500万

罗米的曼联博客
2026-09-28 10:14:03
CBA速递,上海大鲨鱼锁定麦基,六十万美元非常平价,三剑客浮现

CBA速递,上海大鲨鱼锁定麦基,六十万美元非常平价,三剑客浮现

体坛小快灵
2026-09-28 08:43:41
“升糖直接爆了”,家长晒儿子上学早餐,网友:吃完了倒头就睡

“升糖直接爆了”,家长晒儿子上学早餐,网友:吃完了倒头就睡

泽泽先生
2026-09-21 13:37:01
刘欢病逝2天,别墅、版权收入等资产曝光,遗产分配成焦点

刘欢病逝2天,别墅、版权收入等资产曝光,遗产分配成焦点

往史过眼云烟
2026-09-27 16:40:27
儿子作文里写:“我爸爸妈妈从来不吵架,但他们也不怎么说话”

儿子作文里写:“我爸爸妈妈从来不吵架,但他们也不怎么说话”

自由心向往之
2026-09-25 08:30:13
拒绝网红脸!央视秋晚四美火出圈,观众:这才是真审美

拒绝网红脸!央视秋晚四美火出圈,观众:这才是真审美

阿废冷眼观察所
2026-09-27 12:56:52
国家统计局:2026年1—8月份全国规模以上工业企业利润增长15.7%

国家统计局:2026年1—8月份全国规模以上工业企业利润增长15.7%

界面新闻
2026-09-28 09:31:49
台湾提出和统条件:一国三制,“外交权”和拥有军队?是痴心妄想

台湾提出和统条件:一国三制,“外交权”和拥有军队?是痴心妄想

老头的传奇色彩
2026-08-06 17:59:31
广州一工人误踩8层自建房楼顶绿色帆布坠入电梯井,连破两道安全网跌至一楼,调查报告披露:承包人无资质冒用他人名义,未设警示标志

广州一工人误踩8层自建房楼顶绿色帆布坠入电梯井,连破两道安全网跌至一楼,调查报告披露:承包人无资质冒用他人名义,未设警示标志

扬子晚报
2026-09-28 10:35:00
红薯正大量上市!研究发现:糖尿病吃一次红薯,等于给血糖添堵?

红薯正大量上市!研究发现:糖尿病吃一次红薯,等于给血糖添堵?

老李讲健康
2026-09-28 07:30:07
“磨膝大户”被揪出,是跑步的20倍,医生:不想软骨磨光,早扔掉

“磨膝大户”被揪出,是跑步的20倍,医生:不想软骨磨光,早扔掉

看世界的人
2026-09-20 22:04:47
王思聪坐香奈儿VIP贵宾室,懒懒旁边试包,消费降级传闻又被打脸

王思聪坐香奈儿VIP贵宾室,懒懒旁边试包,消费降级传闻又被打脸

梦醉为红颜一笑
2026-09-27 14:27:39
骁龙8EE6+11000mAh!荣耀新机官宣,9月28日发布

骁龙8EE6+11000mAh!荣耀新机官宣,9月28日发布

科技堡垒
2026-09-28 10:19:38
“抱歉,我们不喜欢肥头大耳的”,女孩公开追求体制内男,面相输了

“抱歉,我们不喜欢肥头大耳的”,女孩公开追求体制内男,面相输了

世界圈
2026-08-15 10:29:14
新央企首任董事长,未公布被调查就直接“双开”

新央企首任董事长,未公布被调查就直接“双开”

评论员岳连
2026-09-27 20:54:46
一个国家能蠢到什么程度?看看法国就明白了:法国黑人接近800万,巴黎新生儿里70%是黑人

一个国家能蠢到什么程度?看看法国就明白了:法国黑人接近800万,巴黎新生儿里70%是黑人

怪味历史连连看
2026-09-08 01:55:32
越老越妖,梅西30岁后罚进43粒直接任意球,比30岁前多10粒

越老越妖,梅西30岁后罚进43粒直接任意球,比30岁前多10粒

懂球帝
2026-09-28 10:05:05
2026-09-28 11:35:00
新能源自动驾驶 incentive-icons
新能源自动驾驶
专注于半导体行业资讯
1015文章数 348关注度
往期回顾 全部

汽车要闻

MAZDA EZ-60激光版上市 焕新全系11.39万元起

头条要闻

牛弹琴:内塔尼亚胡有大麻烦了 不是命硬就能扛过去的

头条要闻

牛弹琴:内塔尼亚胡有大麻烦了 不是命硬就能扛过去的

体育要闻

114项指控成立,曼城已经完蛋了吗?

娱乐要闻

好汉刘欢:一副好嗓子,一颗赤子心

财经要闻

标价298元核桃油, 实为大豆油冒充

科技要闻

智能体惹了多少祸,OpenAI自己没查清!

态度原创

家居
旅游
游戏
亲子
公开课

家居要闻

2026建博会(广州) 公装联探展交流活动

旅游要闻

一组AI动态海报,解锁四川文旅融合鲜活场景|2026四川文旅大会特别策划

星际争霸新游已经"可以玩"了!亲身感受刺蛇爆头

亲子要闻

江苏产妇产下5斤9两男宝,脐带刚剪断,宝宝突然刺耳尖叫

公开课

李玫瑾:为什么性格比能力更重要?

无障碍浏览 进入关怀版