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

J1939诊断应用层协议详解:从DM1报文格式到DTC解析的完整指南

0
分享至

J1939诊断应用层协议(SAE J1939-73)是重型车辆诊断的核心标准,它规范了故障码(DTC)的格式、诊断报文的定义以及多包传输机制。本文结合协议原理与C++代码示例,深入解析DM1报文的解析与发送过程。


1. J1939诊断应用层协议简介

J1939的诊断应用层(对应SAE J1939-73标准)不仅覆盖基础的故障检测功能,还涵盖了监视系统、内存存取、数据转换、引导载入、标定等一系列复杂的交互功能。其核心目的之一是标准化不同厂商、不同车型的诊断信息交互方式,避免重复开发。

这些功能通过不同类型的诊断报文(Diagnostic Message, DM) 实现,例如:

  • DM1 :当前激活故障码

  • DM2 :历史故障码

  • DM3 :历史故障码清除/复位

  • DM11 :冻结帧数据

2. 核心概念解析

在深入代码之前,需要理解J1939诊断的几个核心术语。

2.1 SPN (可疑参数编号)

SPN用于标识具体的子系统、部件或故障对象(如发动机、传感器)。它是一个19位的编号,前511个与SAE J1587兼容,之后的由SAE定义或厂商自定义。

2.2 DTC (诊断故障码) 的构成

J1939的DTC结构与常见的UDS(3字节)不同,它由4个字段组成,共占4个字节:

字段

位数

描述

SPN

19位

指示故障发生在哪个部件/子系统

FMI

5位

故障模式指示器,描述故障类型(如电压高、数据不稳定)

OC

7位

故障发生次数

CM

1位

SPN转换方式(通常设为0,表示使用最新版本转换)


2.3 PGN (参数组编号)

诊断报文在总线上通过PGN进行识别。例如,**DM1报文的PGN是65226 (0x00FECA)**。

3. 诊断应用示例:DM1报文(当前故障码)

DM1是诊断中最核心的报文,用于周期性广播当前处于激活状态的故障码。根据SAE J1939-73规范,当存在激活故障时,ECU必须每秒发送一次DM1报文;如果故障状态发生变化,必须立即发送。

3.1 DM1报文格式

DM1报文数据域格式如下(Intel格式,小端模式):

Byte

参数

描述

1

指示灯状态

Bit 7-6: MIL, Bit 5-4: 红色停止灯, Bit 3-2: 琥珀色警告灯, Bit 1-0: 保护灯

2

预留

通常为0xFF

3-6

DTC

SPN (19位) + FMI (5位) + OC (7位) + CM (1位)

7-8

DTC

如果存在多个故障,继续排列


3.2 C++ 代码示例:解析DM1报文

以下代码演示如何从CAN原始数据中提取DM1中的第一个DTC,并解析相关的指示灯状态。

#include  
          
#include

// 定义DTC结构体
struct J1939_DTC {
uint32_t spn; // 可疑参数编号 (0 - 524287)
uint8_t fmi; // 故障模式标识 (0 - 31)
uint8_t oc; // 发生次数 (0 - 126)
bool cm; // 转换方式 (通常为0)
};

// 解析DM1报文的函数
// data: 指向CAN数据域8字节的指针
void Parse_DM1(const uint8_t* data) {
// 1. 解析指示灯状态 (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 解析结果 ===" << std::endl;
std::cout << "MIL灯: " << (mil_on ? "点亮" : "熄灭") << std::endl;
std::cout << "红色停止灯: " << (red_stop_on ? "点亮" : "熄灭") << std::endl;
std::cout << "琥珀色警告灯: " << (amber_warning_on ? "点亮" : "熄灭") << std::endl;

// 2. 解析第一个DTC (Byte 3-6)
// 注:J1939通常使用Intel格式,即低字节在前
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=== 当前激活故障码 (DTC ) ===" << std::endl;
std::cout << "SPN: " << dtc.spn << " (部件标识)" << std::endl;
std::cout << "FMI: " << (int)dtc.fmi << " (故障模式)" << std::endl;
std::cout << "OC: " << (int)dtc.oc << " (发生次数)" << std::endl;
std::cout << "CM: " << dtc.cm << " (转换方式)" << std::endl;
}

int main() {
// 模拟总线接收到的一帧DM1报文
// 场景:发动机进气歧管压力(SPN 102)电压低于正常值(FMI 4),发生2次
// 数据排列 (Intel 格式):
// Byte1: 指示灯(0x04) -> AMBER灯亮
// Byte2: 0xFF
// Byte3-6: SPN=102, FMI=4, OC=2, CM=0
// 计算 Raw Value: SPN(102) | (FMI(4)<<19) | (CM(0)<<24) | (OC(2)<<25)
// 结果 = 0x00040066 (小端存储在内存中: 0x66, 0x00, 0x04, 0x00)
uint8_t simulated_dm1[8] = {0x04, 0xFF, 0x66, 0x00, 0x04, 0x00, 0xFF, 0xFF};
Parse_DM1(simulated_dm1);
return 0;
}
3.3 C++ 代码示例:发送DM1报文

在实际ECU开发中,需要构造并发送DM1。以下示例展示如何打包一个DTC并发送(此处模拟传输层发送函数)。

#include  
          
#include
#include

// 模拟CAN发送函数(实际开发中替换为驱动层函数)
bool CAN_Send(uint32_t id, const uint8_t* data, uint8_t len) {
std::cout << "发送CAN ID: 0x" << std::hex << id << std::endl;
std::cout << "数据: ";
for (int i = 0; i < len; i++) {
std::cout << std::hex << (int)data[i] << " ";
}
std::cout << std::dec << std::endl;
return true;
}

// 发送DM1报文的函数
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
// 设置Byte 1: 指示灯状态
data[0] = lamp_status;
// 设置Byte 2: 预留
data[1] = 0xFF;
if (count > 0 && dtc_list != nullptr) {
// 打包第一个DTC到 Bytes 3-6
// 构造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);
// 写入数组 (Intel格式: 低字节在低地址)
data[3] = raw_dtc & 0xFF;
data[4] = (raw_dtc >> 8) & 0xFF;
data[5] = (raw_dtc >> 16) & 0xFF;
data[6] = (raw_dtc >> 24) & 0xFF;
// 注意:若有多个DTC,需要利用传输层协议(TP)发送多包,此处仅演示单包
}
// DM1的PGN是65226,对应CAN ID通常为 0x18FECA00 + Source Address
uint32_t can_id = 0x18FECA00; // 假设源地址为0
CAN_Send(can_id, data, 8);
}

int main() {
J1939_DTC active_dtc;
active_dtc.spn = 100; // 发动机机油压力
active_dtc.fmi = 1; // 数据低于正常范围
active_dtc.oc = 3; // 发生3次
active_dtc.cm = 0; // 标准转换
// 点亮琥珀色警告灯
uint8_t lamp = 0x04; // Amber灯亮
std::cout << "发送激活故障码 DM1..." << std::endl;
Send_DM1(lamp, &active_dtc, 1);
return 0;
}
4. 多包传输机制

当DM1报文包含的激活故障码超过1个时(即数据超过8字节),必须使用J1939传输层协议(TP) 进行多包传输。

  1. **BAM (广播公告报文)**:发送方先发出PGN 60416的报文,声明即将发送消息的字节总数、分包数量及目标PGN(如65226)。

  2. **DT (数据传输报文)**:随后发送一系列PGN 60160的报文,每一包包含7字节的有效载荷,顺序拼接成完整的DM1报文。

下图展示了多包DM1在总线上的逻辑结构:

[ BAM Packet ] --> 告知: 我要发送22字节数据,分4包发,内容属于DM1
[ DT Packet ] --> 序列号1 + Data(Byte 0-6)
[ DT Packet ] --> 序列号2 + Data(Byte 7-13)
[ DT Packet ] --> 序列号3 + Data(Byte 14-20)
[ DT Packet ] --> 序列号4 + Data(Byte 21-22 + 填充)
5. 总结

J1939诊断应用层协议(J1939-73)通过标准化的SPN、FMI和DM1报文,为重型车辆提供了高效的故障自诊断方案。理解其DTC的位/字节布局以及多包传输(BAM/DT) 机制,是进行商用车控制器(ECU)开发和故障诊断的基础。

与乘用车常用的UDS协议不同,J1939诊断具有更强的周期性广播特性(每秒一次的固定心跳),这要求开发者在设计ECU软件时,需严格遵循其发送时序,以防止因报文速率过高导致总线负载异常。

特别声明:以上内容(如有图片或视频亦包括在内)为自媒体平台“网易号”用户上传并发布,本平台仅提供信息存储服务。

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.

相关推荐
热点推荐
张本智和:阿拉米扬以往发球就算不上完全合规,现在又肆无忌惮

张本智和:阿拉米扬以往发球就算不上完全合规,现在又肆无忌惮

懂球帝
2026-09-27 15:40:00
你听过最炸裂的三观是什么?网友:我承认老一辈吃盐比我吃的饭多

你听过最炸裂的三观是什么?网友:我承认老一辈吃盐比我吃的饭多

另子维爱读史
2026-09-03 20:47:51
历史性转向!爆买超6万亿!美债美股,惊现罕见一幕

历史性转向!爆买超6万亿!美债美股,惊现罕见一幕

证券时报
2026-09-27 23:18:03
女篮颁奖典礼满是遗憾!王思雨杨舒予早早摘下铜牌两人皆带伤鏖战

女篮颁奖典礼满是遗憾!王思雨杨舒予早早摘下铜牌两人皆带伤鏖战

走进事件的中心
2026-09-28 04:45:13
曾国藩警告:和强势的人打交道,顺从是险,对抗是蠢,应该这样做

曾国藩警告:和强势的人打交道,顺从是险,对抗是蠢,应该这样做

纪史行者
2026-09-14 07:00:22
美国放行伊朗总统,专机起飞后怕夜长梦多,全程不敢落地第三国

美国放行伊朗总统,专机起飞后怕夜长梦多,全程不敢落地第三国

靓仔情感
2026-09-28 04:11:08
9-5领先被翻盘!王曼昱打碎日本32年梦想,张本美和:我真是完败

9-5领先被翻盘!王曼昱打碎日本32年梦想,张本美和:我真是完败

而长终
2026-09-28 04:22:06
发现一个规则,顶层的家庭里,绝对不会让孩子去碰这三件事……

发现一个规则,顶层的家庭里,绝对不会让孩子去碰这三件事……

新东方家庭教育
2026-09-20 10:19:36
《兰香如故》林绣茹戏台一笑!袁绍辉至死不知,他丢的不是妾是妻

《兰香如故》林绣茹戏台一笑!袁绍辉至死不知,他丢的不是妾是妻

东方不败然多多
2026-09-20 08:29:54
专家再次预测中国房价走势,大概率又是对的,提前做好2个准备

专家再次预测中国房价走势,大概率又是对的,提前做好2个准备

铭记历史呀
2026-07-31 18:45:19
金鹰奖取消人气奖,于和伟杨紫分获视帝视后

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

汪巗的创业之路
2026-09-28 00:28:24
回国才敢说句心里话,在俄罗斯,对方知道你是中国人,态度全变了

回国才敢说句心里话,在俄罗斯,对方知道你是中国人,态度全变了

潘軮旅行浪子
2026-09-21 17:07:42
章子怡命可真的苦
如果当年嫁给撒贝宁,结局就会不同

章子怡命可真的苦 如果当年嫁给撒贝宁,结局就会不同

小琦聊生活
2026-09-26 12:33:26
几乎全是假货!利润高达2400%,为何消费者还前赴后继争相购买?

几乎全是假货!利润高达2400%,为何消费者还前赴后继争相购买?

星星跌入梦里中
2026-09-26 05:47:11
《兰香如故》林锦歧拼命护送回京的太子,只做了9个月的皇帝,却开启了大明王朝的“仁宣之治”!

《兰香如故》林锦歧拼命护送回京的太子,只做了9个月的皇帝,却开启了大明王朝的“仁宣之治”!

牛锅巴小钒
2026-09-27 11:03:55
9月起,出租房屋要缴这些税!

9月起,出租房屋要缴这些税!

法律内参
2026-09-25 01:42:09
不藏了!72岁唐国强承认与刘晓庆的真实关系,曾志伟曾看的很清楚

不藏了!72岁唐国强承认与刘晓庆的真实关系,曾志伟曾看的很清楚

观察者海风
2026-08-16 03:43:59
美国顶级智库倒闭,原因离谱:把歼20贬得一无是处,把美军坑惨了

美国顶级智库倒闭,原因离谱:把歼20贬得一无是处,把美军坑惨了

铁锤简科
2026-09-27 13:42:11
禁止全部中国外交官入境,不准两岸统一,这个国家比美国还嚣张?

禁止全部中国外交官入境,不准两岸统一,这个国家比美国还嚣张?

尘世闲云
2026-08-14 16:10:13
小姨子和我老婆是双胞胎,有一次我喝醉后,把小姨子当成了老婆

小姨子和我老婆是双胞胎,有一次我喝醉后,把小姨子当成了老婆

千秋文化
2026-08-15 20:23:02
2026-09-28 05:40:49
新能源自动驾驶 incentive-icons
新能源自动驾驶
专注于半导体行业资讯
1015文章数 348关注度
往期回顾 全部

汽车要闻

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

头条要闻

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

头条要闻

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

体育要闻

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

娱乐要闻

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

财经要闻

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

科技要闻

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

态度原创

游戏
艺术
亲子
旅游
公开课

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

艺术要闻

致敬,刘欢硬笔书法欣赏

亲子要闻

两岁小屁孩开车演示倒车入库,这水平你服不服吧?

旅游要闻

告别千篇一律夜市,贵阳青云市集读懂年轻人旅行偏爱!

公开课

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

无障碍浏览 进入关怀版