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

UDS诊断协议详解:ISO-15765传输层与C++代码实现

0
分享至


UDS(Unified Diagnostic Services,统一诊断服务)是汽车电子开发中的核心协议栈,广泛应用于ECU(电子控制单元)的故障读取、刷写、标定等场景。理解UDS,关键在于分清两层:应用层(ISO 14229)定义诊断服务接口,传输层(ISO 15765)负责在CAN/CAN FD总线上可靠地打包、传输和重组数据。

本文将聚焦ISO 15765-2:2004(又称DoCAN)传输层协议,详细拆解四种帧类型、多帧交互流程,并给出可直接使用的C++代码示例。

一、ISO 15765 四种帧类型

由于经典CAN数据场最多8字节,当诊断报文超过8字节时,必须采用多帧传输。ISO 15765定义了以下四种协议数据单元(N_PDU):

帧类型

N_PCI字节高4位

用途

单帧(SF)

0x0

传输≤7字节的用户数据

首帧(FF)

0x1

多帧传输的第一个帧,告知总数据长度

连续帧(CF)

0x2

多帧中的后续数据帧,顺序编号

流控帧(FC)

0x3

接收方控制发送方连续帧的速率和数量


1. 单帧(SF)

  • PCI字节 : 0x0 + 4位数据长度(Data Length),表示有效数据字节数。

  • 示例 : 02 10 01 00 00 00 00 00
    第一个字节 0x02 → 单帧 + 长度2,后续2字节 10 01 是诊断请求(UDS服务ID 0x10)。

2. 首帧(FF)
  • PCI字节 : 0x1 + 12位数据总长度(FF_DL),范围9~4095字节。

  • 示例 : 13 FF 22 F1 90 ...
    0x13FF → 首帧标志,总数据长度为 0x3FF=1023 字节,后续6字节承载数据开头。

3. 连续帧(CF)
  • PCI字节 : 0x2 + 4位序列号(SN),从1开始,每发一帧加1,模16循环(0x1~0xF,然后回到0x0)。

  • 示例 : 21 xx xx ... → 第一个连续帧(SN=1),后面7字节为有效数据。

4. 流控帧(FC)
  • PCI字节 : 0x3 + 流控状态(FS)+ 块大小(BS)+ 最小间隔时间(STmin)。

    • FS : 0x0 =继续发送, 0x1 =等待, 0x2 =溢出/取消。

    • BS :允许连续发送的CF数量(0=无限制)。

    • STmin :连续帧之间的最小间隔(毫秒或微秒编码)。

  • 示例 : 30 00 14 → 流控帧,FS=0(继续),BS=0(无限制),STmin=0x14(20ms)。

二、多帧交互流程(经典案例)

假设诊断仪(Tester)需要向ECU发送一个长请求(例如数据下载),总长度200字节。

Tester                                    ECU
| |
|------- [FF: 13 C8 + 前6字节数据] ----->| // 首帧,总数据0x1C8=456字节(假设)
| |
|<------- [FC: 30 0A 14] ----------------| // 流控:允许连续10个CF,间隔20ms
| |
|------- [CF: 21 + 数据块1] ------------->|
|------- [CF: 22 + 数据块2] ------------->|
| ... |
|------- [CF: 2A + 数据块10] ------------>| // 发完BS=10个CF
| |
|<------- [FC: 30 00 14] ----------------| // 再次流控,块大小无限制
| |
|------- [CF: 2B + 剩余数据] ------------>|
| ... |
|------- [CF: 2x + 最后数据] ------------>| // 所有帧发送完毕

关键规则:

  • 发送方收到FC后才可发出第一个CF。

  • 连续帧的SN必须与FC中的BS配合,达到BS数量后必须等待下一个FC。

  • 如果BS=0,表示无块限制,发送方可连续发送所有CF直到结束。

三、C++代码实现

以下代码实现了一个简化的ISO 15765传输层处理类,支持:

  • 将长数据分割为首帧+连续帧

  • 解析接收到的单帧、首帧、连续帧、流控帧

  • 多帧接收重组(状态机)

3.1 CAN帧结构定义

#include  
          
#include
#include

struct CanFrame {
uint32_t id; // CAN ID (11/29 bit)
uint8_t data[8]; // 数据场
uint8_t len; // 实际数据长度 (0-8)
};

enum class N_PCItype : uint8_t {
SF = 0x0,
FF = 0x1,
CF = 0x2,
FC = 0x3
};
3.2 发送端:数据打包器

class Iso15765Sender {
public:
// 输入: 完整诊断数据 (如UDS请求)
// 输出: 一组CAN帧 (首帧+连续帧)
std::vector packMultiFrame(const std::vector& data, uint32_t canId) {
std::vector frames;
if (data.size() <= 7) {
// 单帧
CanFrame sf{canId, {}, 8};
sf.data[0] = (static_cast(N_PCItype::SF) << 4) | static_cast(data.size());
std::copy(data.begin(), data.end(), sf.data + 1);
frames.push_back(sf);
return frames;
}

// 多帧:构造首帧
CanFrame ff{canId, {}, 8};
ff.data[0] = (static_cast(N_PCItype::FF) << 4) | ((data.size() >> 8) & 0x0F);
ff.data[1] = data.size() & 0xFF;
size_t copyLen = std::min(6, data.size());
std::copy(data.begin(), data.begin() + copyLen, ff.data + 2);
frames.push_back(ff);

// 构造连续帧
uint8_t seqNum = 1;
size_t offset = copyLen;
while (offset < data.size()) {
CanFrame cf{canId, {}, 8};
cf.data[0] = (static_cast(N_PCItype::CF) << 4) | (seqNum++ % 16);
size_t chunkSize = std::min(7, data.size() - offset);
std::copy(data.begin() + offset, data.begin() + offset + chunkSize, cf.data + 1);
frames.push_back(cf);
offset += chunkSize;
}
return frames;
}
};
3.3 接收端:多帧重组状态机

class Iso15765Receiver {
public:
enum class RxState { IDLE, RECEIVING };

// 输入一个CAN帧,输出完整诊断数据(如果已重组完成)
std::vector onReceive(const CanFrame& frame) {
if (frame.len < 1) return {};

uint8_t pci = frame.data[0];
N_PCItype type = static_cast (pci >> 4);

if (type == N_PCItype::SF) {
// 单帧直接返回数据
uint8_t sfLen = pci & 0x0F;
return std::vector(frame.data + 1, frame.data + 1 + sfLen);
}
else if (type == N_PCItype::FF) {
// 开始接收多帧
m_state = RxState::RECEIVING;
m_totalLen = ((pci & 0x0F) << 8) | frame.data[1];
m_buffer.clear();
m_buffer.reserve(m_totalLen);
// 拷贝首帧中的前6字节数据
size_t firstChunk = std::min(6, m_totalLen);
m_buffer.insert(m_buffer.end(), frame.data + 2, frame.data + 2 + firstChunk);
m_nextSeqNum = 1; // 期望的第一个CF序列号
// 如果总长度<=6,实际已收完?但规范要求>7才是多帧,此处安全处理
if (m_buffer.size() >= m_totalLen) {
return complete();
}
return {}; // 等待连续帧
}
else if (type == N_PCItype::CF) {
if (m_state != RxState::RECEIVING) return {};
uint8_t seq = pci & 0x0F;
if (seq != m_nextSeqNum) {
// 序列号错误,可触发错误处理
m_state = RxState::IDLE;
return {};
}
// 拷贝连续帧中7字节数据
size_t remaining = m_totalLen - m_buffer.size();
size_t copyLen = std::min(7, remaining);
m_buffer.insert(m_buffer.end(), frame.data + 1, frame.data + 1 + copyLen);
m_nextSeqNum = (m_nextSeqNum + 1) % 16;
if (m_buffer.size() >= m_totalLen) {
return complete();
}
return {};
}
else if (type == N_PCItype::FC) {
// 流控帧由发送方处理,接收方不需要在此类中实现
return {};
}
return {};
}

private:
std::vector complete() {
m_state = RxState::IDLE;
return std::move(m_buffer);
}

RxState m_state = RxState::IDLE;
uint16_t m_totalLen = 0;
std::vector m_buffer;
uint8_t m_nextSeqNum = 0;
};
3.4 简单测试

#include  
          

void printBytes(const std::vector& bytes) {
for (auto b : bytes) printf("%02X ", b);
printf("\n");
}

int main() {
// 构造一个长诊断请求: 22 F1 90 (读取DID) 加上填充数据到20字节
std::vector request = {0x22, 0xF1, 0x90, 0x01, 0x02, 0x03, 0x04, 0x05,
0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D,
0x0E, 0x0F, 0x10, 0x11};
Iso15765Sender sender;
auto frames = sender.packMultiFrame(request, 0x7DF);

std::cout << "Generated " << frames.size() << " CAN frames:\n";
for (size_t i = 0; i < frames.size(); ++i) {
printf("Frame %zu: ", i);
for (int j = 0; j < frames[i].len; ++j) printf("%02X ", frames[i].data[j]);
printf("\n");
}

// 模拟接收方重组
Iso15765Receiver receiver;
std::vector assembled;
for (auto& frame : frames) {
auto out = receiver.onReceive(frame);
if (!out.empty()) {
assembled = out;
}
}
std::cout << "Reassembled data: ";
printBytes(assembled);
return 0;
}

输出示例:

Generated 4 CAN frames:
Frame 0: 14 22 F1 90 01 02 03 04 // 首帧,总长度20,前6字节
Frame 1: 21 05 06 07 08 09 0A 0B // CF SN=1
Frame 2: 22 0C 0D 0E 0F 10 11 00 // CF SN=2
Reassembled data: 22 F1 90 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F 10 11
注:实际工程中还需处理流控帧的发送逻辑(响应FC、BS限流、STmin延时),上述代码侧重于单帧/多帧的组包与解包。
四、总结
  • ISO 15765-2 是UDS在CAN总线上的“搬运工”,定义了单帧、首帧、连续帧、流控帧四种协议帧。

  • 单帧 用于短数据(≤7字节), 多帧 通过首帧告知总长度、流控帧调节速率、连续帧按序发送。

  • C++实现 时,发送方需按规则拆包,接收方维护状态机重组;流控机制保证了总线负载可控与接收方缓冲安全。

  • 掌握这些底层传输细节,是进行UDS诊断开发、调试以及编写Bootloader刷写逻辑的基础。

当你理解了ISO 15765的“怎么传”,再去学习ISO 14229的“做什么”就会事半功倍。希望本文和代码能帮助你快速上手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.

相关推荐
热点推荐
离婚真相曝光仅6个月,前妻高调曝光追求者,撕碎猴哥仅剩体面

离婚真相曝光仅6个月,前妻高调曝光追求者,撕碎猴哥仅剩体面

挂肚逍遥心
2026-06-08 08:17:28
第一投就刷新亚运会纪录!18岁宁波姑娘严子怡标枪夺金,神力少女“严一枪”养成记

第一投就刷新亚运会纪录!18岁宁波姑娘严子怡标枪夺金,神力少女“严一枪”养成记

都市快报橙柿互动
2026-09-27 21:38:21
某房企大佬,突发丑闻!

某房企大佬,突发丑闻!

地产八卦
2026-09-28 06:22:06
联合国秘书长有句话,他曾挑明:打垮日本,中国才是决定性力量;日本被耗到油尽灯枯,最后低头投降,中国立的是根本大功

联合国秘书长有句话,他曾挑明:打垮日本,中国才是决定性力量;日本被耗到油尽灯枯,最后低头投降,中国立的是根本大功

回京历史梦
2026-09-27 17:58:12
三个中年女人坦白与“小男友”同居感受:甜是真的,难熬也是真的

三个中年女人坦白与“小男友”同居感受:甜是真的,难熬也是真的

千秋文化
2026-09-27 21:06:14
都说油车会淘汰,中秋假期高速却让我见识到电车真的不如油车

都说油车会淘汰,中秋假期高速却让我见识到电车真的不如油车

你在偷看谁
2026-09-26 22:26:17
邱导说,孙颖莎夺冠的奖金加起来可不是一个小数目,可她银行卡里经常就几千块钱的零花钱

邱导说,孙颖莎夺冠的奖金加起来可不是一个小数目,可她银行卡里经常就几千块钱的零花钱

最爱乒乓球
2026-06-27 02:31:16
李敏晚年回忆往事,坦言倘若毛岸英未曾牺牲,贺子珍或许不会在上海独居半生

李敏晚年回忆往事,坦言倘若毛岸英未曾牺牲,贺子珍或许不会在上海独居半生

唠叨说历史
2026-07-31 14:54:44
阿隆索哭晕!切尔西遭连环暴击!5200 万王牌重伤拄拐

阿隆索哭晕!切尔西遭连环暴击!5200 万王牌重伤拄拐

澜归序
2026-09-27 08:17:10
炸裂!日本妻子拍AV挽救婚姻,瞒夫出道老公崩溃,如今丈夫成粉丝

炸裂!日本妻子拍AV挽救婚姻,瞒夫出道老公崩溃,如今丈夫成粉丝

悠悠说世界
2026-08-17 13:48:27
2013年,金正哲联手张成泽发动朝鲜兵变,因一细节败露,双遭反杀

2013年,金正哲联手张成泽发动朝鲜兵变,因一细节败露,双遭反杀

阿胡
2025-03-11 13:28:03
长期在睡前玩手机的人,会付出啥代价?医生:或要承担这5种痛苦

长期在睡前玩手机的人,会付出啥代价?医生:或要承担这5种痛苦

芹姐说生活
2026-09-11 23:30:07
离了王室不好使了?42岁哈里王子进校访问,学生直言听都没听说过

离了王室不好使了?42岁哈里王子进校访问,学生直言听都没听说过

乡野小珥
2026-09-28 03:15:02
上海一位80岁奶奶,一生未婚,临终花光800万,谁也别想继承

上海一位80岁奶奶,一生未婚,临终花光800万,谁也别想继承

千秋文化
2026-09-27 21:01:11
大理通报导游在大巴车上胁迫游客购物:固定证据后罚款六千元

大理通报导游在大巴车上胁迫游客购物:固定证据后罚款六千元

澎湃新闻
2026-09-27 08:58:26
北加州夫妇凌晨遭黑熊袭击!狗狗冲上去护主身亡,主人:它救了我们的命

北加州夫妇凌晨遭黑熊袭击!狗狗冲上去护主身亡,主人:它救了我们的命

华人生活网
2026-09-26 04:29:47
毛主席晚年为何要“批孔”?起初大家都不理解,如今终于明白!

毛主席晚年为何要“批孔”?起初大家都不理解,如今终于明白!

小豫讲故事
2026-09-14 06:00:32
杨幂在直播中提醒网友:剪短发还是要考虑清楚,因为我头发长的比较快,回来以后待了两天就长长了

杨幂在直播中提醒网友:剪短发还是要考虑清楚,因为我头发长的比较快,回来以后待了两天就长长了

韩小娱
2026-09-27 09:15:30
金鹰奖取消人气奖,于和伟杨紫分获视帝视后

金鹰奖取消人气奖,于和伟杨紫分获视帝视后

汪巗的创业之路
2026-09-28 00:28:24
49岁陈建州突发心梗!只差5分钟丧命,小S得知后当场吓哭,妻子范玮琪事发前阻拦他去日本,哽咽后怕:不然就会像大S一样

49岁陈建州突发心梗!只差5分钟丧命,小S得知后当场吓哭,妻子范玮琪事发前阻拦他去日本,哽咽后怕:不然就会像大S一样

In风尚
2026-09-28 06:04:09
2026-09-28 07:03:00
新能源自动驾驶 incentive-icons
新能源自动驾驶
专注于半导体行业资讯
1015文章数 348关注度
往期回顾 全部

科技要闻

星舰第14飞,为什么准备绕地球六圈?

头条要闻

孙颖莎2比4不敌王曼昱摘银 称可能等不到下届亚运会了

头条要闻

孙颖莎2比4不敌王曼昱摘银 称可能等不到下届亚运会了

体育要闻

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

娱乐要闻

刘欢去世后谣言四起,韩磊被翻旧账

财经要闻

“为了看短剧,我妈两年花了22万 ”

汽车要闻

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

态度原创

数码
艺术
手机
时尚
公开课

数码要闻

10499元 微星Claw 8 EX AI+上新:搭载Arc G3处理器、睿频可达4.6GHz

艺术要闻

致敬,刘欢硬笔书法欣赏

手机要闻

iQOO 16升级超感光主摄 蓝厂旗舰同款算法

兰香如故:谁为打工人的情绪工伤买单?

公开课

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

无障碍浏览 进入关怀版