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

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.

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

汽车要闻

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

头条要闻

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

头条要闻

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

体育要闻

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

娱乐要闻

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

财经要闻

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

科技要闻

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

态度原创

旅游
家居
时尚
游戏
军事航空

旅游要闻

一组AI动态海报,解锁四川文旅融合鲜活场景|2026四川文旅大会特别策划

家居要闻

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

女人过了三四十岁,一定要准备一件小西装,大方得体又显瘦

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

军事要闻

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

无障碍浏览 进入关怀版