1. J1939协议帧格式解析与构造
本案例实现一个最小可用的 J1939 帧解析/构造工具:把 29 位扩展 CAN ID 拆成优先级、PGN、源地址、目标地址,并能反向拼回扩展帧 ID。J1939 的 29 位 ID 结构为:3 位优先级、1 位保留、1 位数据页、8 位 PDU 格式、8 位 PDU 特定、8 位源地址。当 PDU 格式为 PDU1(PF < 240)时,PDU 特定字段是目标地址,PGN 的末字节为 0;当为 PDU2(PF ≥ 240)时,PDU 特定字段属于 PGN,目标地址为广播(0xFF)。
实现代码
#include <stdio.h>
#include <stdint.h>
typedef struct {
uint8_t priority; // 0..7
uint32_t pgn; // 18-bit PGN
uint8_t src; // source address
uint8_t dst; // destination (0xFF = broadcast)
} J1939Frame;
// 解析 29 位扩展 ID
J1939Frame j1939_parse(uint32_t id29) {
J1939Frame f;
f.priority = (id29 >> 26) & 0x7;
uint8_t dp = (id29 >> 24) & 0x1;
uint8_t pf = (id29 >> 16) & 0xFF;
uint8_t ps = (id29 >> 8) & 0xFF;
f.src = id29 & 0xFF;
if (pf < 240) { // PDU1: PS 是目标地址
f.dst = ps;
f.pgn = ((uint32_t)dp << 16) | ((uint32_t)pf << 8);
} else { // PDU2: PS 属于 PGN
f.dst = 0xFF;
f.pgn = ((uint32_t)dp << 16) | ((uint32_t)pf << 8) | ps;
}
return f;
}
// 构造 29 位扩展 ID
uint32_t j1939_build(const J1939Frame *f) {
uint8_t dp = (f->pgn >> 16) & 0x1;
uint8_t pf = (f->pgn >> 8) & 0xFF;
uint8_t ps = (pf < 240) ? f->dst : (f->pgn & 0xFF);
uint32_t id = ((uint32_t)(f->priority & 0x7) << 26)
| ((uint32_t)dp << 24)
| ((uint32_t)pf << 16)
| ((uint32_t)ps << 8)
| f->src;
return id;
}
int main(void) {
// 例1: PDU1 请求帧, 优先级6, PGN 0xEA00, 目标0x12, 源0x34
J1939Frame a = {6, 0xEA00, 0x34, 0x12};
uint32_t ida = j1939_build(&a);
J1939Frame ra = j1939_parse(ida);
printf("PDU1 id=0x%08X pri=%u pgn=0x%05X src=0x%02X dst=0x%02X\n",
ida, ra.priority, ra.pgn, ra.src, ra.dst);
// 例2: PDU2 广播帧, 优先级3, PGN 0xFEF1, 源0x56
J1939Frame b = {3, 0xFEF1, 0x56, 0xFF};
uint32_t idb = j1939_build(&b);
J1939Frame rb = j1939_parse(idb);
printf("PDU2 id=0x%08X pri=%u pgn=0x%05X src=0x%02X dst=0x%02X\n",
idb, rb.priority, rb.pgn, rb.src, rb.dst);
return 0;
}
运行与验证
gcc j1939.c -o j1939 && ./j1939
预期输出(由代码逻辑唯一确定):
PDU1 id=0x18EA1234 pri=6 pgn=0x0EA00 src=0x34 dst=0x12
PDU2 id=0x18FEF156 pri=3 pgn=0x0FEF1 src=0x56 dst=0xFF
判定通过条件:两行输出中优先级、PGN、源地址、目标地址与构造时输入一致;PDU1 帧目标地址为 0x12,PDU2 帧目标地址为 0xFF。可再自行修改输入值,验证 build→parse 往返结果不变。