无主之地2配置高吗|看真人裸体BBBBB|秋草莓丝瓜黄瓜榴莲色多多|真人強奷112分钟|精品一卡2卡3卡四卡新区|日本成人深夜苍井空|八十年代动画片

網(wǎng)易首頁 > 網(wǎng)易號 > 正文 申請入駐

UDSonCAN 多幀傳輸完全指南:首幀、連續(xù)幀、流控幀一網(wǎng)打盡

0
分享至


1. 引言

UDS (Unified Diagnostic Services,統(tǒng)一診斷服務(wù)) 是汽車電子領(lǐng)域最主流的診斷協(xié)議,通常運行于 CAN 總線之上,形成 UDSonCAN。然而 UDS 單條報文最大可達 4095 字節(jié),而標準 CAN 數(shù)據(jù)場僅 8 字節(jié)(CAN FD 可達 64 字節(jié),本文以經(jīng)典 CAN 為例)。為了在有限帶寬上可靠傳輸大塊數(shù)據(jù),ISO 15765-2(即傳輸層,TP)定義了分段與重組機制,通過單幀、首幀、連續(xù)幀和流控幀這四類協(xié)議數(shù)據(jù)單元(PDU),實現(xiàn)了從單幀到多幀的無縫傳輸。理解這一機制,是開發(fā) UDS 診斷棧的基礎(chǔ)。

本文將深入淺出地剖析 UDSonCAN 的傳輸層核心機制,并用 C++ 代碼示例展示如何實現(xiàn)發(fā)送方與接收方的關(guān)鍵邏輯。

2. UDSonCAN 協(xié)議棧架構(gòu)

UDSonCAN 遵循 OSI 模型分層:

  • 應(yīng)用層:UDS 服務(wù)(如 0x10 診斷會話控制、0x22 讀數(shù)據(jù)等)

  • 傳輸層:ISO 15765-2,負責(zé)分段與重組

  • 數(shù)據(jù)鏈路層:CAN 幀 (ID, DLC, 8 字節(jié)數(shù)據(jù))

傳輸層 PDU 通過 CAN 數(shù)據(jù)場中的第一個字節(jié)(或前兩個字節(jié))的N_PCI(協(xié)議控制信息)來區(qū)分幀類型。

幀類型

N_PCI 值 (高半字節(jié))

縮寫

單幀

0x0

SF

完整消息 ≤ 7 字節(jié)

首幀

0x1

FF

多幀消息的第一幀,攜帶總長度

連續(xù)幀

0x2

CF

后續(xù)數(shù)據(jù)塊,帶序列號

流控幀

0x3

FC

接收方控制發(fā)送方節(jié)奏(塊大小、間隔時間)


3. 單幀傳輸(Single Frame)

當(dāng) UDS 請求或響應(yīng)總長度≤ 7 字節(jié)時,使用單幀。格式如下:

字節(jié)0: SF 標識 (0x0) + 有效數(shù)據(jù)長度 (低4位)
字節(jié)1~7: 應(yīng)用層數(shù)據(jù) (最多7字節(jié))

示例:請求22 F1 86(讀 DID 0xF186),長度 3 字節(jié) → 單幀 PCI =0x03(高4位0,低4位3),CAN 數(shù)據(jù)為03 22 F1 86 00 00 00 00

C++ 發(fā)送單幀示例

#include  

#include

// 假設(shè)底層發(fā)送CAN幀的函數(shù)
extern bool CanSendFrame(uint32_t canId, const std::vector& data);

bool SendSingleFrame(uint32_t canId, const std::vector& udsData) {
if (udsData.size() > 7) return false; // 超長需多幀
std::vector canData(8, 0);
canData[0] = static_cast(udsData.size()); // 高4位0,低4位長度
std::copy(udsData.begin(), udsData.end(), canData.begin() + 1);
return CanSendFrame(canId, canData);
}
4. 多幀傳輸機制(Multi-frame)

當(dāng) UDS 消息長度超過 7 字節(jié)時,發(fā)送方將其拆分為:

  • 一個首幀(FF):告知總長度

  • 若干個連續(xù)幀(CF):攜帶后續(xù)數(shù)據(jù)

  • 接收方通過流控幀(FC)管理發(fā)送速率

4.1 首幀(First Frame)

字節(jié)0: FF標識 (0x1) + 總長度高4位
字節(jié)1: 總長度低8位
字節(jié)2~7: 前6字節(jié)數(shù)據(jù)

總長度是一個 12 位值(最大 4095),拆分到字節(jié)0低4位和字節(jié)1整個8位。

4.2 連續(xù)幀(Consecutive Frame)

字節(jié)0: CF標識 (0x2) + 序列號 SN (低4位, 0~15)
字節(jié)1~7: 后續(xù)7字節(jié)數(shù)據(jù)

序列號從 1 開始,每發(fā)一幀加 1,循環(huán) 0~15。用于檢測丟幀。

4.3 流控幀(Flow Control)

接收方在收到 FF 后,若緩沖區(qū)允許,需發(fā)送 FC 告知發(fā)送方:

  • 塊大小(BS):允許連續(xù)發(fā)送的 CF 幀數(shù)(0 表示無限)

  • 最小間隔時間(STmin):兩幀 CF 之間的最小間隔(ms 或 100us 單位)

字節(jié)0: FC標識 (0x3) + 流控狀態(tài) (通常0=繼續(xù)發(fā)送, 1=等待, 2=溢出)
字節(jié)1: 塊大小 BS
字節(jié)2: 最小間隔 STmin (編碼格式見標準)
字節(jié)3~7: 未使用,填0
5. 多幀傳輸時序與狀態(tài)機

典型的多幀發(fā)送(請求讀取大量數(shù)據(jù),如 0x22 讀長 VIN 碼):

診斷儀(發(fā)送方)                     ECU(接收方)
| |
|---- FF (總長度=50, 前6字節(jié)) ---->|
| | 解析FF,分配緩沖區(qū)
|<---- FC (BS=5, STmin=10ms) ------|
| |
|---- CF (SN=1, 7字節(jié)) ----------->|
| (等待10ms) |
|---- CF (SN=2, 7字節(jié)) ----------->|
| (等待10ms) |
|---- CF (SN=3, 7字節(jié)) ----------->|
| (等待10ms) |
|---- CF (SN=4, 7字節(jié)) ----------->|
| (等待10ms) |
|---- CF (SN=5, 7字節(jié)) ----------->| 收到5幀后
| | 發(fā)送新的FC
|<---- FC (BS=5, STmin=10ms) ------|
|---- CF (SN=6, 7字節(jié)) ----------->|
... ...

接收方狀態(tài)機簡化如下:

  • WAIT_FF:等待首幀,超時則中止

  • WAIT_CF:等待連續(xù)幀,并計數(shù),每 BS 幀后等待新 FC

  • WAIT_FC(發(fā)送方視角):發(fā)送完 FF 后等待 FC,超時重試或中止

6. C++ 代碼示例:實現(xiàn)發(fā)送方和接收方核心邏輯

以下代碼演示了發(fā)送方如何將任意長度的 UDS 消息分段發(fā)送,以及接收方如何重組為完整消息。為簡潔起見,省略了超時、錯誤重傳等細節(jié),僅突出核心機制。

6.1 發(fā)送方類UdsTpSender

#include  

#include
#include
#include
#include


class UdsTpSender {
public:
using CanTxFunc = std::function& data)>;
UdsTpSender(CanTxFunc txFunc) : txFunc_(txFunc) {}
// 發(fā)送UDS消息(可能拆分為多幀)
bool Send(uint32_t canId, const std::vector& udsData) {
if (udsData.size() <= 7) {
return SendSingleFrame(canId, udsData);
} else {
return SendMultiFrame(canId, udsData);
}
}
private:
bool SendSingleFrame(uint32_t canId, const std::vector& data) {
std::vector canData(8, 0);
canData[0] = static_cast(data.size()); // SF PCI
std::copy(data.begin(), data.end(), canData.begin() + 1);
return txFunc_(canId, canData);
}
bool SendMultiFrame(uint32_t canId, const std::vector& udsData) {
// 1. 發(fā)送首幀 (FF)
uint16_t totalLen = static_cast(udsData.size());
std::vector ffData(8, 0);
ffData[0] = 0x10 | ((totalLen >> 8) & 0x0F); // 高4位=1, 低4位=長度高4位
ffData[1] = totalLen & 0xFF;
// 拷貝前6字節(jié)數(shù)據(jù)到 ffData[2..7]
size_t firstChunk = std::min(6, udsData.size());
std::copy(udsData.begin(), udsData.begin() + firstChunk, ffData.begin() + 2);
if (!txFunc_(canId, ffData)) return false;
// 2. 等待接收方流控幀 (實際應(yīng)使用異步接收回調(diào),這里簡化為同步獲取)
// 實際項目中需要配合接收隊列和超時機制。此處僅展示收到FC后的行為。
// 假設(shè)我們通過回調(diào)得到FC參數(shù): bs, stmin
// 本示例模擬一個默認FC: BS=0(無限), STmin=0(無間隔)
uint8_t blockSize = 0; // 0表示無限
uint8_t stMin = 0; // 0ms
// 偽代碼:實際應(yīng)等待接收FC幀,解析其字節(jié)1和字節(jié)2
// WaitForFlowControl(bs, stmin);
// 3. 發(fā)送連續(xù)幀
size_t offset = firstChunk;
uint8_t seqNum = 1;
while (offset < udsData.size()) {
// 如果 blockSize > 0,需要每發(fā)送 blockSize 幀后等待新的FC
// 本示例簡化:連續(xù)發(fā)完所有CF
std::vector cfData(8, 0);
cfData[0] = 0x20 | (seqNum & 0x0F); // CF PCI + 序列號
size_t copySize = std::min(7, udsData.size() - offset);
std::copy(udsData.begin() + offset, udsData.begin() + offset + copySize,
cfData.begin() + 1);
if (!txFunc_(canId, cfData)) return false;
offset += copySize;
seqNum = (seqNum + 1) & 0x0F;
// 遵守STmin延時
if (stMin > 0) {
std::this_thread::sleep_for(std::chrono::milliseconds(stMin));
}
}
return true;
}
CanTxFunc txFunc_;
};
6.2 接收方類UdsTpReceiver

接收方需要維護多個會話(不同 CAN ID 可能同時多幀傳輸),為簡明,僅處理單會話。

class UdsTpReceiver {
public:
enum State { WAIT_FF, WAIT_CF };
void OnCanFrame(uint32_t canId, const std::vector& canData) {
if (canData.empty()) return;
uint8_t pci = canData[0] >> 4;
uint8_t lenLow = canData[0] & 0x0F;
switch (pci) {
case 0x0: { // 單幀
size_t dataLen = lenLow;
std::vector udsMsg(canData.begin() + 1, canData.begin() + 1 + dataLen);
OnCompleteMessage(canId, udsMsg);
break;
}
case 0x1: { // 首幀
uint16_t totalLen = (lenLow << 8) | canData[1];
// 前6字節(jié)數(shù)據(jù)位置 canData[2..7]
reassembled_.clear();
reassembled_.reserve(totalLen);
size_t firstChunk = std::min(6, totalLen);
reassembled_.insert(reassembled_.end(), canData.begin() + 2, canData.begin() + 2 + firstChunk);
expectedSeq_ = 1;
remaining_ = totalLen - firstChunk;
state_ = WAIT_CF;
// 發(fā)送流控幀 (BS=0無限, STmin=0)
SendFlowControl(canId, 0, 0);
break;
}
case 0x2: { // 連續(xù)幀
if (state_ != WAIT_CF) return;
uint8_t seq = lenLow;
if (seq != expectedSeq_) {
// 序列號錯誤,可發(fā)送溢出流控或忽略
return;
}
size_t copySize = std::min(7, remaining_);
reassembled_.insert(reassembled_.end(), canData.begin() + 1, canData.begin() + 1 + copySize);
remaining_ -= copySize;
expectedSeq_ = (expectedSeq_ + 1) & 0x0F;
if (remaining_ == 0) {
OnCompleteMessage(canId, reassembled_);
state_ = WAIT_FF;
}
break;
}
case 0x3: // 流控幀(接收方不應(yīng)收到主動發(fā)送的FC,除非作為發(fā)送方)
default:
break;
}
}
void SetMessageCallback(std::function&)> cb) {
onComplete_ = cb;
}
private:
void SendFlowControl(uint32_t canId, uint8_t blockSize, uint8_t stMin) {
std::vector fc(8, 0);
fc[0] = 0x30; // FC PCI + flowStatus=0(繼續(xù)發(fā)送)
fc[1] = blockSize;
fc[2] = stMin;
// 實際需要調(diào)用底層發(fā)送,此處略
// CanSendFrame(canId, fc);
}
State state_ = WAIT_FF;
std::vector reassembled_;
uint8_t expectedSeq_;
size_t remaining_;
std::function&)> onComplete_;
};
6.3 集成示例

#include  



int main() {
auto txFunc = [](uint32_t id, const std::vector& data) {
std::cout << "Tx CAN ID 0x" << std::hex << id << ": ";
for (auto b : data) printf("%02X ", b);
std::cout << std::endl;
return true;
};
UdsTpSender sender(txFunc);
std::vector longUds = {0x22, 0xF1, 0x86, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07};
sender.Send(0x7DF, longUds);
// 接收側(cè)模擬
UdsTpReceiver receiver;
receiver.SetMessageCallback([](uint32_t id, const std::vector& msg) {
std::cout << "Rcvd complete UDS (" << msg.size() << " bytes): ";
for (auto b : msg) printf("%02X ", b);
std::cout << std::endl;
});
// 模擬收到首幀和連續(xù)幀...
// receiver.OnCanFrame(0x7DF, {0x10, 0x0A, 0x22, 0xF1, 0x86, 0x01, 0x02, 0x03});
// receiver.OnCanFrame(0x7DF, {0x21, 0x04, 0x05, 0x06, 0x07, 0x00, 0x00, 0x00});
return 0;
}
7. 關(guān)鍵注意事項
  • 流控幀的 STmin 編碼:ISO 15765-2 定義了多種間隔值,如 0x00~0x7F 表示 0~127 ms,0xF1 表示 100 μs 等。實現(xiàn)時需解析并精確延時。

  • 多會話并發(fā):不同 CAN ID 或不同診斷會話可能同時傳輸多幀,接收方需按(canId, sourceAddr)維護獨立的重組緩沖區(qū)。

  • 超時處理:發(fā)送 FF 后等待 FC 超時(通常 1000ms),接收方等待 CF 超時(通常 100ms),需有定時器機制。

  • 錯誤恢復(fù):序列號錯誤、緩沖區(qū)溢出時應(yīng)發(fā)送 FC 狀態(tài) = 溢出(0x2)或中止多幀傳輸。

8. 總結(jié)

UDSonCAN 的單幀與多幀傳輸機制,通過精巧的四類 PCI 類型和流控握手,實現(xiàn)了有限帶寬下的可靠大塊數(shù)據(jù)傳輸。理解 SF、FF、CF、FC 的含義及狀態(tài)轉(zhuǎn)換,是開發(fā)車載診斷工具、ECU 固件或仿真器的基礎(chǔ)。本文提供的 C++ 核心代碼展示了發(fā)送與重組的基本骨架,實際產(chǎn)品中還需加入超時管理、多會話并發(fā)、錯誤恢復(fù)等模塊,但萬變不離其宗——ISO 15765-2 定義的這一套簡單而強大的協(xié)議,正是汽車診斷可靠性的基石。

掌握從單幀到多幀的“傳輸奧秘”,你將能輕松應(yīng)對各種 UDS 診斷開發(fā)場景。

特別聲明:以上內(nèi)容(如有圖片或視頻亦包括在內(nèi))為自媒體平臺“網(wǎng)易號”用戶上傳并發(fā)布,本平臺僅提供信息存儲服務(wù)。

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.

相關(guān)推薦
熱點推薦
啥都和愛國扯到一起,就是一種病

啥都和愛國扯到一起,就是一種病

老唐有話說
2026-05-14 16:18:17
A股:剛剛,國務(wù)院國資委發(fā)布,不出意外的話,下周將迎來新變化

A股:剛剛,國務(wù)院國資委發(fā)布,不出意外的話,下周將迎來新變化

云鵬敘事
2026-05-16 00:00:09
國家發(fā)改委主任鄭柵潔會見波音公司總裁奧特伯格

國家發(fā)改委主任鄭柵潔會見波音公司總裁奧特伯格

新京報
2026-05-15 20:29:22
上海87-82戰(zhàn)勝北京!賽后數(shù)據(jù)一清二楚,不是王哲林 最大功臣是他

上海87-82戰(zhàn)勝北京!賽后數(shù)據(jù)一清二楚,不是王哲林 最大功臣是他

小火箭愛體育
2026-05-15 21:32:09
難怪黃仁勛那么積極跟著特朗普訪華,一到北京就拿下了大額訂單。

難怪黃仁勛那么積極跟著特朗普訪華,一到北京就拿下了大額訂單。

魔都姐姐雜談
2026-05-14 22:09:10
人活多久,看喝酒就知道?壽命短的人,喝酒一般有這6個特征

人活多久,看喝酒就知道?壽命短的人,喝酒一般有這6個特征

芹姐說生活
2026-05-14 23:38:55
上海奪G1但3人需總結(jié)!盧偉應(yīng)變差點,白邊優(yōu)勢被打沒,弗格太鐵

上海奪G1但3人需總結(jié)!盧偉應(yīng)變差點,白邊優(yōu)勢被打沒,弗格太鐵

籃球資訊達人
2026-05-16 01:09:21
38.98萬,夸張啊...

38.98萬,夸張啊...

放毒
2026-05-15 19:14:23
中紀委再次重拳出擊!這4個領(lǐng)域?qū)⒈粐啦椋@4種行為將被嚴肅處理

中紀委再次重拳出擊!這4個領(lǐng)域?qū)⒈粐啦椋@4種行為將被嚴肅處理

細說職場
2026-05-15 14:01:05
鄺兆鐳U17亞洲杯首秀!送助攻后或舊傷復(fù)發(fā),只踢半場仍獲贊

鄺兆鐳U17亞洲杯首秀!送助攻后或舊傷復(fù)發(fā),只踢半場仍獲贊

奧拜爾
2026-05-16 02:13:48
悲催!上海一母親將700萬遺產(chǎn)給兒子,6年后才發(fā)現(xiàn)被女兒徹底拉黑

悲催!上海一母親將700萬遺產(chǎn)給兒子,6年后才發(fā)現(xiàn)被女兒徹底拉黑

火山詩話
2026-05-15 06:49:15
特朗普還沒回國,就開始放狠話了

特朗普還沒回國,就開始放狠話了

利刃號
2026-05-15 17:16:39
第一次感受到“荔枝核的威力”,泡水里20天,長成“粉盆栽”

第一次感受到“荔枝核的威力”,泡水里20天,長成“粉盆栽”

美家指南
2026-05-15 15:27:43
國宴名場面刷屏:穿紅衣的服務(wù)員火了,這才是大國該有的體面

國宴名場面刷屏:穿紅衣的服務(wù)員火了,這才是大國該有的體面

娛樂洞察點點
2026-05-15 12:40:18
“錢車兩空”!男子以租代購跑網(wǎng)約車,三年還清13.5萬,過戶前一夜車被拖走

“錢車兩空”!男子以租代購跑網(wǎng)約車,三年還清13.5萬,過戶前一夜車被拖走

網(wǎng)約車觀察室
2026-05-14 10:00:49
張雪宣布停產(chǎn)!博主:雷軍出問題你建議退款 自己出問題只補償

張雪宣布停產(chǎn)!博主:雷軍出問題你建議退款 自己出問題只補償

念洲
2026-05-14 14:29:33
不是洛夫頓!不是古德溫!許利民贊上海隊1人,威廉姆斯傷情出爐

不是洛夫頓!不是古德溫!許利民贊上海隊1人,威廉姆斯傷情出爐

老吳說體育
2026-05-15 23:31:25
這跟不穿有啥區(qū)別?趙露思演唱會內(nèi)衣外穿:被眾嘲一套比一套辣眼

這跟不穿有啥區(qū)別?趙露思演唱會內(nèi)衣外穿:被眾嘲一套比一套辣眼

胡一舸南游y
2026-05-13 15:23:56
看好誰當(dāng)選臺北市長?1.4萬人網(wǎng)絡(luò)投票結(jié)果一面倒

看好誰當(dāng)選臺北市長?1.4萬人網(wǎng)絡(luò)投票結(jié)果一面倒

新時光點滴
2026-05-16 00:10:48
沙拉維深情告別羅馬:我即將離開,但我的一部分靈魂將永駐于此

沙拉維深情告別羅馬:我即將離開,但我的一部分靈魂將永駐于此

懂球帝
2026-05-16 02:43:35
2026-05-16 02:59:00
新能源自動駕駛 incentive-icons
新能源自動駕駛
專注于半導(dǎo)體行業(yè)資訊
977文章數(shù) 347關(guān)注度
往期回顧 全部

科技要聞

直降千元起步!蘋果華為率先開啟618讓利

頭條要聞

黃仁勛在北京喝豆汁痛苦皺眉 問“這是什么東西”

頭條要聞

黃仁勛在北京喝豆汁痛苦皺眉 問“這是什么東西”

體育要聞

德約科維奇買的球隊,從第6級聯(lián)賽升入法甲

娛樂要聞

方媛為何要來《桃花塢6》沒苦硬吃?

財經(jīng)要聞

騰訊掉隊,馬化騰戳破真相

汽車要聞

高爾夫GTI刷新紐北紀錄 ID. Polo GTI迎全球首秀

態(tài)度原創(chuàng)

藝術(shù)
時尚
親子
家居
公開課

藝術(shù)要聞

1008米!沙特“世界第一高樓”項目,為何極有可能建成?

頂級團隊拍出來的作品不如素人,問題出在哪兒了?

親子要聞

孕婦補鈣怕刺激怎么選?液體鈣無添加配方實測,藍帽認證更靠譜

家居要聞

110㎡淡而有致的生活表達

公開課

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

無障礙瀏覽 進入關(guān)懷版