J1939診斷應(yīng)用層協(xié)議(SAE J1939-73)是重型車輛診斷的核心標(biāo)準(zhǔn),它規(guī)范了故障碼(DTC)的格式、診斷報(bào)文的定義以及多包傳輸機(jī)制。本文結(jié)合協(xié)議原理與C++代碼示例,深入解析DM1報(bào)文的解析與發(fā)送過程。
![]()
1. J1939診斷應(yīng)用層協(xié)議簡介
J1939的診斷應(yīng)用層(對(duì)應(yīng)SAE J1939-73標(biāo)準(zhǔn))不僅覆蓋基礎(chǔ)的故障檢測(cè)功能,還涵蓋了監(jiān)視系統(tǒng)、內(nèi)存存取、數(shù)據(jù)轉(zhuǎn)換、引導(dǎo)載入、標(biāo)定等一系列復(fù)雜的交互功能。其核心目的之一是標(biāo)準(zhǔn)化不同廠商、不同車型的診斷信息交互方式,避免重復(fù)開發(fā)。
這些功能通過不同類型的診斷報(bào)文(Diagnostic Message, DM) 實(shí)現(xiàn),例如:
DM1 :當(dāng)前激活故障碼
DM2 :歷史故障碼
DM3 :歷史故障碼清除/復(fù)位
DM11 :凍結(jié)幀數(shù)據(jù)
在深入代碼之前,需要理解J1939診斷的幾個(gè)核心術(shù)語。
2.1 SPN (可疑參數(shù)編號(hào))
SPN用于標(biāo)識(shí)具體的子系統(tǒng)、部件或故障對(duì)象(如發(fā)動(dòng)機(jī)、傳感器)。它是一個(gè)19位的編號(hào),前511個(gè)與SAE J1587兼容,之后的由SAE定義或廠商自定義。
2.2 DTC (診斷故障碼) 的構(gòu)成
J1939的DTC結(jié)構(gòu)與常見的UDS(3字節(jié))不同,它由4個(gè)字段組成,共占4個(gè)字節(jié):
字段
位數(shù)
描述
SPN
19位
指示故障發(fā)生在哪個(gè)部件/子系統(tǒng)
FMI
5位
故障模式指示器,描述故障類型(如電壓高、數(shù)據(jù)不穩(wěn)定)
OC
7位
故障發(fā)生次數(shù)
CM
1位
SPN轉(zhuǎn)換方式(通常設(shè)為0,表示使用最新版本轉(zhuǎn)換)
2.3 PGN (參數(shù)組編號(hào))
診斷報(bào)文在總線上通過PGN進(jìn)行識(shí)別。例如,**DM1報(bào)文的PGN是65226 (0x00FECA)**。
3. 診斷應(yīng)用示例:DM1報(bào)文(當(dāng)前故障碼)
DM1是診斷中最核心的報(bào)文,用于周期性廣播當(dāng)前處于激活狀態(tài)的故障碼。根據(jù)SAE J1939-73規(guī)范,當(dāng)存在激活故障時(shí),ECU必須每秒發(fā)送一次DM1報(bào)文;如果故障狀態(tài)發(fā)生變化,必須立即發(fā)送。
3.1 DM1報(bào)文格式
DM1報(bào)文數(shù)據(jù)域格式如下(Intel格式,小端模式):
Byte
參數(shù)
描述
1
指示燈狀態(tài)
Bit 7-6: MIL, Bit 5-4: 紅色停止燈, Bit 3-2: 琥珀色警告燈, Bit 1-0: 保護(hù)燈
2
預(yù)留
通常為0xFF
3-6
DTC
SPN (19位) + FMI (5位) + OC (7位) + CM (1位)
7-8
DTC
如果存在多個(gè)故障,繼續(xù)排列
3.2 C++ 代碼示例:解析DM1報(bào)文
以下代碼演示如何從CAN原始數(shù)據(jù)中提取DM1中的第一個(gè)DTC,并解析相關(guān)的指示燈狀態(tài)。
3.3 C++ 代碼示例:發(fā)送DM1報(bào)文#include
#include
// 定義DTC結(jié)構(gòu)體
struct J1939_DTC {
uint32_t spn; // 可疑參數(shù)編號(hào) (0 - 524287)
uint8_t fmi; // 故障模式標(biāo)識(shí) (0 - 31)
uint8_t oc; // 發(fā)生次數(shù) (0 - 126)
bool cm; // 轉(zhuǎn)換方式 (通常為0)
};
// 解析DM1報(bào)文的函數(shù)
// data: 指向CAN數(shù)據(jù)域8字節(jié)的指針
void Parse_DM1(const uint8_t* data) {
// 1. 解析指示燈狀態(tài) (Byte 1)
uint8_t lampStatus = data[0];
bool mil_on = (lampStatus >> 7) & 0x01; // Malfunction Indicator Lamp
bool red_stop_on = (lampStatus >> 5) & 0x01; // Red Stop Lamp
bool amber_warning_on = (lampStatus >> 3) & 0x01; // Amber Warning Lamp
bool protect_on = (lampStatus >> 1) & 0x01; // Protection Lamp
std::cout << "=== DM1 解析結(jié)果 ===" << std::endl;
std::cout << "MIL燈: " << (mil_on ? "點(diǎn)亮" : "熄滅") << std::endl;
std::cout << "紅色停止燈: " << (red_stop_on ? "點(diǎn)亮" : "熄滅") << std::endl;
std::cout << "琥珀色警告燈: " << (amber_warning_on ? "點(diǎn)亮" : "熄滅") << std::endl;
// 2. 解析第一個(gè)DTC (Byte 3-6)
// 注:J1939通常使用Intel格式,即低字節(jié)在前
uint32_t raw_spn_fmi = data[3] | (data[4] << 8) | (data[5] << 16) | (data[6] << 24);
J1939_DTC dtc;
// 提取SPN (低19位: bits 0-18)
dtc.spn = raw_spn_fmi & 0x7FFFF;
// 提取FMI (bits 19-23) -> 右移19位,取低5位
dtc.fmi = (raw_spn_fmi >> 19) & 0x1F;
// 提取CM (bit 24) -> 右移24位,取低1位
dtc.cm = (raw_spn_fmi >> 24) & 0x01;
// 提取OC (bits 25-31) -> 右移25位,取低7位
dtc.oc = (raw_spn_fmi >> 25) & 0x7F;
std::cout << "\n=== 當(dāng)前激活故障碼 (DTC ) ===" << std::endl;
std::cout << "SPN: " << dtc.spn << " (部件標(biāo)識(shí))" << std::endl;
std::cout << "FMI: " << (int)dtc.fmi << " (故障模式)" << std::endl;
std::cout << "OC: " << (int)dtc.oc << " (發(fā)生次數(shù))" << std::endl;
std::cout << "CM: " << dtc.cm << " (轉(zhuǎn)換方式)" << std::endl;
}int main() {
// 模擬總線接收到的一幀DM1報(bào)文
// 場(chǎng)景:發(fā)動(dòng)機(jī)進(jìn)氣歧管壓力(SPN 102)電壓低于正常值(FMI 4),發(fā)生2次
// 數(shù)據(jù)排列 (Intel 格式):
// Byte1: 指示燈(0x04) -> AMBER燈亮
// Byte2: 0xFF
// Byte3-6: SPN=102, FMI=4, OC=2, CM=0
// 計(jì)算 Raw Value: SPN(102) | (FMI(4)<<19) | (CM(0)<<24) | (OC(2)<<25)
// 結(jié)果 = 0x00040066 (小端存儲(chǔ)在內(nèi)存中: 0x66, 0x00, 0x04, 0x00)
uint8_t simulated_dm1[8] = {0x04, 0xFF, 0x66, 0x00, 0x04, 0x00, 0xFF, 0xFF};
Parse_DM1(simulated_dm1);
return 0;
}
在實(shí)際ECU開發(fā)中,需要構(gòu)造并發(fā)送DM1。以下示例展示如何打包一個(gè)DTC并發(fā)送(此處模擬傳輸層發(fā)送函數(shù))。
4. 多包傳輸機(jī)制#include
#include
#include
// 模擬CAN發(fā)送函數(shù)(實(shí)際開發(fā)中替換為驅(qū)動(dòng)層函數(shù))
bool CAN_Send(uint32_t id, const uint8_t* data, uint8_t len) {
std::cout << "發(fā)送CAN ID: 0x" << std::hex << id << std::endl;
std::cout << "數(shù)據(jù): ";
for (int i = 0; i < len; i++) {
std::cout << std::hex << (int)data[i] << " ";
}
std::cout << std::dec << std::endl;
return true;
}
// 發(fā)送DM1報(bào)文的函數(shù)
void Send_DM1(uint8_t lamp_status, const J1939_DTC* dtc_list, uint8_t count) {
uint8_t data[8];
memset(data, 0xFF, sizeof(data)); // 初始化填充0xFF
// 設(shè)置Byte 1: 指示燈狀態(tài)
data[0] = lamp_status;
// 設(shè)置Byte 2: 預(yù)留
data[1] = 0xFF;
if (count > 0 && dtc_list != nullptr) {
// 打包第一個(gè)DTC到 Bytes 3-6
// 構(gòu)造32位原始DTC值: Bytes = SPN + (FMI<<19) + (CM<<24) + (OC<<25)
uint32_t raw_dtc = dtc_list[0].spn;
raw_dtc |= (dtc_list[0].fmi << 19);
raw_dtc |= ((dtc_list[0].cm ? 1 : 0) << 24);
raw_dtc |= (dtc_list[0].oc << 25);
// 寫入數(shù)組 (Intel格式: 低字節(jié)在低地址)
data[3] = raw_dtc & 0xFF;
data[4] = (raw_dtc >> 8) & 0xFF;
data[5] = (raw_dtc >> 16) & 0xFF;
data[6] = (raw_dtc >> 24) & 0xFF;
// 注意:若有多個(gè)DTC,需要利用傳輸層協(xié)議(TP)發(fā)送多包,此處僅演示單包
}
// DM1的PGN是65226,對(duì)應(yīng)CAN ID通常為 0x18FECA00 + Source Address
uint32_t can_id = 0x18FECA00; // 假設(shè)源地址為0
CAN_Send(can_id, data, 8);
}int main() {
J1939_DTC active_dtc;
active_dtc.spn = 100; // 發(fā)動(dòng)機(jī)機(jī)油壓力
active_dtc.fmi = 1; // 數(shù)據(jù)低于正常范圍
active_dtc.oc = 3; // 發(fā)生3次
active_dtc.cm = 0; // 標(biāo)準(zhǔn)轉(zhuǎn)換
// 點(diǎn)亮琥珀色警告燈
uint8_t lamp = 0x04; // Amber燈亮
std::cout << "發(fā)送激活故障碼 DM1..." << std::endl;
Send_DM1(lamp, &active_dtc, 1);
return 0;
}
當(dāng)DM1報(bào)文包含的激活故障碼超過1個(gè)時(shí)(即數(shù)據(jù)超過8字節(jié)),必須使用J1939傳輸層協(xié)議(TP) 進(jìn)行多包傳輸。
**BAM (廣播公告報(bào)文)**:發(fā)送方先發(fā)出PGN 60416的報(bào)文,聲明即將發(fā)送消息的字節(jié)總數(shù)、分包數(shù)量及目標(biāo)PGN(如65226)。
**DT (數(shù)據(jù)傳輸報(bào)文)**:隨后發(fā)送一系列PGN 60160的報(bào)文,每一包包含7字節(jié)的有效載荷,順序拼接成完整的DM1報(bào)文。
下圖展示了多包DM1在總線上的邏輯結(jié)構(gòu):
[ BAM Packet ] --> 告知: 我要發(fā)送22字節(jié)數(shù)據(jù),分4包發(fā),內(nèi)容屬于DM1
[ DT Packet ] --> 序列號(hào)1 + Data(Byte 0-6)
[ DT Packet ] --> 序列號(hào)2 + Data(Byte 7-13)
[ DT Packet ] --> 序列號(hào)3 + Data(Byte 14-20)
[ DT Packet ] --> 序列號(hào)4 + Data(Byte 21-22 + 填充)
5. 總結(jié)J1939診斷應(yīng)用層協(xié)議(J1939-73)通過標(biāo)準(zhǔn)化的SPN、FMI和DM1報(bào)文,為重型車輛提供了高效的故障自診斷方案。理解其DTC的位/字節(jié)布局以及多包傳輸(BAM/DT) 機(jī)制,是進(jìn)行商用車控制器(ECU)開發(fā)和故障診斷的基礎(chǔ)。
與乘用車常用的UDS協(xié)議不同,J1939診斷具有更強(qiáng)的周期性廣播特性(每秒一次的固定心跳),這要求開發(fā)者在設(shè)計(jì)ECU軟件時(shí),需嚴(yán)格遵循其發(fā)送時(shí)序,以防止因報(bào)文速率過高導(dǎo)致總線負(fù)載異常。
特別聲明:以上內(nèi)容(如有圖片或視頻亦包括在內(nèi))為自媒體平臺(tái)“網(wǎng)易號(hào)”用戶上傳并發(fā)布,本平臺(tái)僅提供信息存儲(chǔ)服務(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.