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

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.

相关推荐
热点推荐
弄巧成拙!网传义乌一饭店:客人白酒啤酒饮料连菜都自带,14个人只花430元,包厢还弄得满地垃圾

弄巧成拙!网传义乌一饭店:客人白酒啤酒饮料连菜都自带,14个人只花430元,包厢还弄得满地垃圾

王老师你还好吗
2026-09-28 01:39:54
20名“双一流”大学生、研究生"翻墙"被处分 ,网传被举报:“一个姐们差一名保研,拿翻墙这事把前面所有人全举报了”

20名“双一流”大学生、研究生"翻墙"被处分 ,网传被举报:“一个姐们差一名保研,拿翻墙这事把前面所有人全举报了”

双一流高校
2026-09-28 00:09:38
西贝又上热搜了,不是罗永浩,是俩探店博主线下吃出来的热度。

西贝又上热搜了,不是罗永浩,是俩探店博主线下吃出来的热度。

荆楚寰宇文枢
2026-09-27 20:53:44
单飞后,半价“问界”来了!赛力斯官宣,新车9月28日发布!

单飞后,半价“问界”来了!赛力斯官宣,新车9月28日发布!

电动内参
2026-09-27 18:36:24
英国政府杀人诛心!引用中方判决,拒绝乱港分子入境,蟑螂天塌了

英国政府杀人诛心!引用中方判决,拒绝乱港分子入境,蟑螂天塌了

不甜的李子
2026-09-27 21:44:04
知名投资人曝刘欢真实死因是肝癌!8月底他曾在餐厅吃饭状态不错

知名投资人曝刘欢真实死因是肝癌!8月底他曾在餐厅吃饭状态不错

一个小豹子
2026-09-27 09:40:19
湖南一男子拾荒21年后领得42万养老金:当过国企工人,下岗后一直未缴费以为领不到

湖南一男子拾荒21年后领得42万养老金:当过国企工人,下岗后一直未缴费以为领不到

潇湘晨报
2026-09-27 20:04:27
击中了!俄罗斯国防部宣布重大战果!

击中了!俄罗斯国防部宣布重大战果!

故事终将光明磊落
2026-09-27 10:49:40
理想汽车抽奖送iPhone,“唯一中奖人”是罗永浩!此前刚发文炮轰苹果新款折叠屏,罗永浩最新回应:将以抽奖形式送网友

理想汽车抽奖送iPhone,“唯一中奖人”是罗永浩!此前刚发文炮轰苹果新款折叠屏,罗永浩最新回应:将以抽奖形式送网友

大风新闻
2026-09-27 18:14:18
绝了!唐僧取经先被办证窗口问崩溃,教师资格证采矿证捕捞证一张不能少

绝了!唐僧取经先被办证窗口问崩溃,教师资格证采矿证捕捞证一张不能少

三言四拍
2026-09-27 19:24:04
五十年代究竟杀了多少地主恶霸?难道五六十年代还不如解放前吗?

五十年代究竟杀了多少地主恶霸?难道五六十年代还不如解放前吗?

历史甄有趣
2026-09-27 17:15:19
王曼昱夺冠庆祝!国乒将帅现场保持沉默,王励勤见她俩举起国旗后鼓掌

王曼昱夺冠庆祝!国乒将帅现场保持沉默,王励勤见她俩举起国旗后鼓掌

三十年莱斯特城球迷
2026-09-27 21:53:46
1:4完败!输球不可怕,可怕的是早田希娜赛后这番话,彻底破防!

1:4完败!输球不可怕,可怕的是早田希娜赛后这番话,彻底破防!

田先生篮球
2026-09-27 15:28:28
4-2逆转夺冠!中国男乒21岁王牌崛起:四杀日乒名将+扎心张本智和

4-2逆转夺冠!中国男乒21岁王牌崛起:四杀日乒名将+扎心张本智和

李喜林篮球绝杀
2026-09-27 20:20:04
青年男女疑高铁上不雅举动引热议 女方还有腹肌 网友:肯定练过

青年男女疑高铁上不雅举动引热议 女方还有腹肌 网友:肯定练过

环球趣闻分享
2026-09-27 13:36:44
“45秒,全没了!”柏林宣布退出,奥运会从顶级IP沦为财政噩梦

“45秒,全没了!”柏林宣布退出,奥运会从顶级IP沦为财政噩梦

史智文道
2026-09-27 19:19:01
俞敏洪:如果拼了命,英年早逝的话,和不那么拼命,而活到八九十岁,看世界变化,我认为不那么拼命活八九十岁,是一件更加合算的事

俞敏洪:如果拼了命,英年早逝的话,和不那么拼命,而活到八九十岁,看世界变化,我认为不那么拼命活八九十岁,是一件更加合算的事

品读时刻
2026-09-10 09:07:58
库页岛打出“三绝不”:不属于中国、不像俄罗斯、不承认历史

库页岛打出“三绝不”:不属于中国、不像俄罗斯、不承认历史

纪史行者
2026-09-19 17:40:52
震惊!网传某半导体大厂号召全员降薪共渡难关,一同降薪20%,财报曝光:老总年薪大涨33%,年薪反倒从600万涨至800万

震惊!网传某半导体大厂号召全员降薪共渡难关,一同降薪20%,财报曝光:老总年薪大涨33%,年薪反倒从600万涨至800万

火山詩话
2026-09-27 13:25:11
日媒:严子怡一枪砸穿日本纪录 日本网友:她去参加男子比赛也会赢

日媒:严子怡一枪砸穿日本纪录 日本网友:她去参加男子比赛也会赢

劲爆体坛
2026-09-27 23:15:02
2026-09-28 06:23:00
新能源自动驾驶 incentive-icons
新能源自动驾驶
专注于半导体行业资讯
1015文章数 348关注度
往期回顾 全部

科技要闻

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

头条要闻

教师称成绩排名靠后被强制培训 广西南丹通报调查情况

头条要闻

教师称成绩排名靠后被强制培训 广西南丹通报调查情况

体育要闻

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

娱乐要闻

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

财经要闻

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

汽车要闻

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

态度原创

时尚
本地
游戏
家居
房产

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

本地新闻

中秋逛白塔寺,体验国医妙荟雅集

Halo工作室曾规划《光环:初次反击》改编战役

家居要闻

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

房产要闻

全年霸榜TOP1!南海・叁號院周年官宣:新作即将登场

无障碍浏览 进入关怀版