近期学习了一下J1939的协议, 本以为凭借着对CAN通讯扎实的知识基础学习这个应是轻松拿捏,但是没想到零零散散也用了2-3天的时间才把原理和协议大致看懂. 因为手头上还没有现成的工具和环境,因此非常遗憾这里没有包括实践的过程,后面有时间我再补充一些实际的数据流把.
老实说这个过程有点超出我的预期, 虽然对于这个知识在互联网上并不缺乏公开的分享,但是实际的学习过程还是有点困扰.因为大部分的资料,文章都是围绕着协议本身进行解释,没有站在应用者的视角展示一个完整的场景.导致我看完了很多文章之后始终有两个问题不能理解, 1. 这个协议怎么使用?发出来的数据流是什么?通过融合多种教材以及其他辅助手段,我逐渐在理论层面理解了上面的问题,接下来我就把这些经过加工的内容分享给大家.
- J1939标准协议建立在CAN(控制器区域网络,ISO11898)上
- 物理层(J1939/11)
- 应用层(J1939/71和J1939/73)
- 报文格式及使用方法(J1939/21)
- J1939协议传送速度稳定在250kbps
- 屏蔽双绞线,总线阻抗为120Ω。
- J1939使用CAN 2.0B协议中定义的29位标识符
- J1939 中有两种通信方式,分别是点对点和广播通信
- 1939共定义了五种报文类型,分别为命令、请求、广播/响应、确认和组功能。


📌P(Priority),优先级字段,用于在仲裁过程中控制报文优先级。
📌EDP(Extended Data Page),为扩展数据页位,在J1939中固定取值为0。
📌DP(Data Page),数据页位,可用来扩展参数组的数量。
📌PF(PDU Format),为PDU格式字段,通过不同的取值来确定参数组编号(Parameter Group Number, PGN)是按照PDU1格式还是PDU2格式。
📌PS(PDU Specific),特定协议数据单元字段,
- 当PF的值为0-239时PS表示目标地址(PDU1格式),
- 当PF的值为240-255时PS字段表示组扩展(PDU2格式),且报文只能广播。
📌SA(Source Address),源地址字段,整个网络中SA的值必须是唯一的。
📌 Data Field,数据场,对于Classic CAN来说是8个字节,而CANFD则最多有64个字节数据。如果需要传输更多的数据,则需要借助J1939的传输协议。
PGN(Parameter Group Number)是 J1939 应用层用来标识一组参数或报文类型的编号。它决定了节点该如何解析载荷,反映了报文的功能和结构。这部分是J1939中比较重要的概念.
下面提供一些这结构的示意图,作为参考(借用Vector手册中的图片).



J1939 扩展帧 ID(29 位)从高到低依次为
- Priority(3 位)
- Reserve R(1 位)
- Data Page DP(1 位)
- PDU Format PF(8 位)
- PDU1 点对点 <240 即0xF0
- PDU2 广播 >=240 即0xF0
- PDU Specific PS(8 位)
- Source Address SA(8 位)
- ID 字段里的 SA(Source Address)永远是“发送该帧的节点地址”
PGN 由 DP、PF 和 PS(或 0x00)三部分组成,共 18 位
在 J1939 的 PGN 计算规则里,只有当 PDU Format(PF)≥ 240(PDU2/广播)时,才把 PS 字段当作 Group Extension (GE) 加入 PGN。
因为在 CM 控制报文里 PF = 0xEC (236 < 240),属于 PDU1 格式:
- PS 被当作目标地址,不作 PGN 一部分
- PGN 的 GE(即 PS 低字节)强制置 0
- J1939-DA以表格形式列出了所有指定的SPN,SPN是由SAE 指定的数字。
- PGN的描述中指定了SPN在PGN内的起始位置
- J1939标准将SPN描述定义为 SLOT (Scaling, Limit, Offset and Transfer Function, 比例、界限、偏移和转换函数)
- 多帧没有SPN字段的概念,之后再完成多帧发送后才能从全部的Payload种提取出那些事SPN


- 如果SPN的所有位均为1,则SPN的状态为SNA
- 11 – SNA (Signal Not Available, 信号不可用)。
SPN 在 PDU1 报文中的体现
- CAN ID(PDU1)里没有 SPN 字段PDU1 的 29 位扩展 ID 只包含
- Priority/R/DP/PF(报文类型)
- PS(目标节点地址)
- SA(源节点地址)
这些位均与节点寻址和 PGN(高两字节)有关,不涉及任何 SPN。
SPN 概念在 PayLoad里
SPN (Suspect Parameter Number)是在完成会话层(TP.CM/TP.DT)重组或者单帧载荷到达后,才根据PGN 定义去映射:
- 确定该 PGN(如 0xF004 “发动机转速”)对应哪些 SPN
- 在数据字段里定位各 SPN 的起始字节/位
- 按照 SPN 的比例因子 & 偏移量换算工程值
举例:发动机转速 SPN190 位于 PDU2 广播回复的 Byte1–Byte2
|
Plain Text |
- ID 格式(PDU1/PDU2)从不携带 SPN 信息,SPN 只在解析了完整Payload后,按 J1939/71 表格映射, SPN 是根据某个 PGN 的定义去解析它的 Paylaod, 换句话说:PGN 告诉你——“去这段Payload里,用 J1939/71 的那张表找 SPN”.
假设 有一帧报文是节点 0x17 向节点 0xCA 发起点对点 Request,索要 PGN 0x00F004(发动机转速) – 上层接收器看到此帧后,会回复一条 PDU2 广播或 PDU1 点对点的发动机转速报文, 下面我以这个完整的数据流来解释下PDU1和PDU2的两种情况.
Rqst:08│18 EA CA 17│04 F0 00 FF FF FF FF FF
PDU1 点对点通讯
- PDU Format 1 specific
- 含有指定目标地址
- PF < 0xF0 (240)
- 点对点通信
- PS 为指定接收节点地址
节点 0x17 向节点 0xCA 发起点对点 Request
08│18 EA CA 17│04 F0 00 FF FF FF FF FF
“08” DLC(Data Length Code),8 字节
“18 EA CA 17” —— 29 位扩展 ID (Priority=6, R=0, DP=0, PF=0xEA, PS=0xCA, SA=0x17)
“04 F0 00 FF FF FF FF FF” —— Data[1..8]
CAN ID (29bit):0x18EACA17
二进制拆解(29 位扩展帧)
110 0 0 11101010 11001010 00010111
- Priority (28–26): 110₂ = 6
- R (25) : 0
- DP (24) : 0
- PF (23–16): 11101010₂ = 0xEA (234 < 240)
- PF < 0xF0 → PDU1(点对点报文)
- PS (15–8) : 11001010₂ = 0xCA (目标地址 DA=202)
- DA = PS = 0xCA
- SA (7–0) : 00010111₂ = 0x17 (源地址 SA=23)
- PGN = DP << 16 + PF << 8 + 0x00 = 0xEA00
Payload (8 bytes)
此示例为“请求发动机转速”(PGN=0x00F004)的 Request 报文:

点击图片可查看完整电子表格
代码
|
Plain Text |
- Byte1–3:LSB→MSB 拼成 0x00F004,表示“发动机转速”参数组
- Byte4–8:0xFF 填充,表示无效/保留
综上所述, 一帧PDU1的单帧请求就解析完了. 假设我们通过PDU1进行响应,
节点 0xCA 以 PDU1 格式向节点 0x17 单点回复发动机转速(1000 rpm)
08│18 EF 17 CA│40 1F FF FF FF FF FF FF
"08" DLC = 8(8 字节数据区)
"18 EF 17 CA" 29 位扩展 ID = 0x18F017CA
- Priority (28–26) = 110₂ = 6
- R (25) = 0
- DP (24) = 0
- PF (23–16) = 11101111₂ = 0xEF (<0xF0 PDU1)
- PS (15–8) = 00010111₂ = 0x17 (目标地址 DA)
- SA (7–0) = 11001010₂ = 0xCA (源地址)
- 二进制字段:
Data[1..8] = 40 1F FF FF FF FF FF FF
- Byte1–2 = 0x1F40 = 8000 → 8000 × 0.125 rpm/bit = 1000 rpm
- Byte3–8 = 0xFF 填充(无意义/保留)
这里为什么0x1F40 是1000rpm的物理意义,这部分内容是J1939 SPN定义的这个概念我们会在下一个章节介绍.
J1939 的 PDU1(点对点)通信中:
- CAN ID 中的 SA(Source Address)永远标识“本帧发送者”
- PS(PDU1 下的 PDU Specific)永远标识“本帧目标者”
因此,若节点 A(地址 0x17)向节点 B(地址 0xCA)发起请求:
代码
|
Plain Text |
回复帧要让 B → A,那么就得把“我是谁 (SA)”和“我要发给谁 (PS)”互换:
代码
|
Plain Text |
PDU2 广播通讯
节点 0xCA 以 PDU2 格式向节点 0x17 单点回复发动机转速(1000 rpm)
08│18 F0 04 CA│40 1F FF FF FF FF FF FF
“08” DLC = 8(8 字节数据区)
“18 F0 04 CA” 29 位扩展 ID = 0x18F004CA
- Priority (28–26) = 110₂ = 6
- R (25) = 0
- DP (24) = 0
- PF (23–16) = 11110000₂ = 0xF0 (≥0xF0 → PDU2)
- PS (15–8) = 00000100₂ = 0x04 (Group Extension GE)
- SA (7–0) = 11001010₂ = 0xCA (源地址)
- 二进制字段: 110 0 0 11110000 00000100 11001010
Data[1..8] = 40 1F FF FF FF FF FF FF
- Byte1–2 = 0x1F40 = 8000 → 8000 × 0.125 rpm/bit = 1000 rpm
- Byte3–8 = 0xFF 填充(无意义/保留)
― PGN 计算 PGN = DP<<16 | PF<<8 | PS = 0x00_ F0_ 04 = 0xF004(发动机转速广播)
Data[1..8] = 40 1F FF FF FF FF FF FF
- Byte1–2 = 0x1F40 = 8000 → 8000 × 0.125 rpm/bit = 1000 rpm
- Byte3–8 = 0xFF 填充(无意义/保留)
- 物理值 = 8000 × 0.125 rpm/bit = 1000 rpm
当一次要发送的数据长度超过 CAN 最大单帧 8 字节时,SAE J1939 采用“传输协议(Transport Protocol, TP)”来完成多帧分段和重组。TP 包含两部分:
- 连接管理(Connection Management, CM)
- 数据传输(Data Transfer, DT)
RTS/CTS + DT 点对点
- 发起方通过 RTS(Request To Send)控制报文发起一次单点传输
- 接收方通过 CTS(Clear To Send)控制报文授权分批接收
- 数据分段后依序通过 DT 报文发送
下面采用点对点模式(RTS/CTS+DT),假设源地址 SA=0x01,目的地址 DA=0x02,PGN=0x00F004,总数据长度 20 字节,共分 3 包。

点击图片可查看完整电子表格
说明
- 步骤 1 和 2 属于 Connection Management(TP.CM)报文
- 步骤 3–5 属于 Data Transfer(TP.DT)报文
CM_RTS 报文(TP.CM)
CAN ID:18 EC 02 01 Data:14 00 03 F0 00 FF FF FF

点击图片可查看完整电子表格
CM_CTS 报文(TP.CM)
CAN ID:18 EC 01 02 Data:02 01 F0 00 FF FF FF FF

点击图片可查看完整电子表格
DT 报文(TP.DT)
Data Transfer 报文,每帧第 0 字节为序号 SN,后 1–7 字节为实际数据或填充 FF。

点击图片可查看完整电子表格
PGN 解析
需要注意的是, 这段数据流中CANID+Payload中包含了两个PGN的协议格式
在 J1939 29 位扩展 ID 中,PGN(Parameter Group Number)由数据页 (DP)、PDU 格式 (PF) 和 PDU 专用 (PS) 共同构成。其计算方法为:
|
Plain Text |
- CM_RTS/CTS 报文中的 PGN
- CAN ID(16 进制): 0x18EC0201 (RTS)和 0x18EC0102 (CTS)
- 拆解字段:
- DP = 0x00
- PF = 0xEC (236)
- PS = 目的地址 (0x02 或 0x01)
- 因为 PF = 0xEC < 0xF0,属于 PDU1 格式,PGN 的 PS 字段置 0
- 计算得到:
|
Plain Text |
- 对应十进制:60416

- DT 报文中的 PGN
- CAN ID(16 进制): 0x18EB0102
- 拆解字段:
- DP = 0x00
- PF = 0xEB (235)
- PS = 目的地址 (0x01)
- 同样 PF = 0xEB < 0xF0,属于 PDU1,PS 置 0
- 计算得到:
|
Plain Text |
- 对应十进制:60160

BAM + DT 广播
- 发起方通过 BAM(Broadcast Announce Message)一次性宣布总长度和分段信息
- 所有节点无需握手,直接监听并依序通过 DT 接收数据
BAM 控制报文
- PGN = 0x18ECFF01
- Data 字段(8 字节)
- Byte0–1:总字节数
- Byte2:分包总数
- Byte3–5:PGN(低→高)
- Byte6–7:保留
DT 数据报文
- PGN = 0x18EBFF01
- Data[0]:分包序号 SN
- Data[1–7]:载荷;最后一个包不足 7 字节时,空位填 0xFF
- 无握手,所有节点根据 SN 顺序重组数据
假设要广播 20 字节的数据,PGN = 0x00F004,由地址 0x01 发出:

点击图片可查看完整电子表格

BAM 报文(TP.CM.BAM,PGN = 0x00EC00)
29 位扩展 ID 拆解:
- 优先级 Priority = 6 (0x18 >> 26)
- 数据页 DP = 0
- PDU Format PF = 0xEC (< 240 → PDU1)
- PDU Specific PS = 0xFF (全局广播地址)
- 源地址 SA = 0x01
PGN 计算: PGN = (DP << 16) | (PF << 8) | 0x00 = 0x00EC00
Data[0..7] 拆解:
- Byte0 = 0x14 → 总长度 LSB = 20 字节
- Byte1 = 0x00 → 总长度 MSB
- Byte2 = 0x03 → 分包总数 = 3
- Byte3 = 0x04 → 应用层 PGN_LSB
- Byte4 = 0xF0 → 应用层 PGN_MID
- Byte5 = 0x00 → 应用层 PGN_MSB ⇒ 应用 PGN = 0x00F004
- Byte6–7 = 0xFF 0xFF → 保留
- 所有监听了 PGN=0x00F004 的节点看到 BAM 即进入重组模式
- 按 SN 顺序接收 DT 并剔除填充字节 0xFF,重组出完整 20 字节数据


1万+

被折叠的 条评论
为什么被折叠?



