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

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.

相关推荐
热点推荐
差严子怡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关注度
往期回顾 全部

科技要闻

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

头条要闻

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

头条要闻

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

体育要闻

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

娱乐要闻

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

财经要闻

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

汽车要闻

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

态度原创

游戏
亲子
艺术
公开课
军事航空

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

亲子要闻

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

艺术要闻

致敬,刘欢硬笔书法欣赏

公开课

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

军事要闻

特朗普"罕见"提二战中美同盟 日本慌了

无障碍浏览 进入关怀版