1. 从一次总线调试翻车说起:为什么这两个协议总被搞混
刚入行那会儿,我接手过一个车载节点的通信调试任务。硬件同事把CAN收发器焊好,线束接上,终端电阻也量了,120欧姆没问题。上电之后用分析仪抓包,总线上确实有报文在跑,ID、DLC、数据段都看得见。但问题是,我按手头那份"CAN协议说明"去解析数据,怎么都对不上——发动机转速的字节位置跟文档里写的完全不一样,车速信号更是找不到。
折腾了大半天,请教了一位老师傅,他看了一眼我手里的文档就笑了:"你看的是ISO 11898,但这条总线上跑的是J1939。物理层一样,上层完全两码事。"
这句话点醒了我。ISO 11898和SAE J1939经常被新手混为一谈,根本原因在于它们确实共享同一套物理层和部分数据链路层基础,但在协议栈里所处的位置、解决的问题、定义的规则完全不在一个层面上。打个比方,ISO 11898像是规定了"公路怎么修、车道多宽、限速多少",而J1939则是在这条公路上进一步规定了"货车拉什么货、货物怎么贴标签、司机之间怎么对话"。路是同一条路,但跑在上面的规矩截然不同。
这篇内容就是想把这件事彻底讲清楚。不管你是刚接触车载网络的嵌入式新手,还是从其他总线(比如Modbus、SPI、IIC)转过来做CAN通信的工程师,只要花几分钟把下面这几个层面的区别理顺,以后拿到任何一份CAN相关文档,你都能快速判断它讲的是哪一层的东西,不至于像我当年那样对着错误的参考文档死磕。
核心关键词先摆出来:CAN总线、ISO 11898、SAE J1939、协议分层、ECU通信。这几个词贯穿全文,搞懂它们之间的关系,基本就抓住了车载CAN通信的主干。
2. 协议栈视角:ISO 11898和J1939根本不在一个楼层
2.1 用"盖楼"类比理解协议分层
要讲清楚这两个协议的区别,最有效的切入角度是协议栈分层。我习惯用盖楼来类比:
- 地基和承重墙:物理层,负责电信号怎么传、线缆什么规格、电平怎么定义。ISO 11898主要就在这一层以及数据链路层的一部分。
- 楼层和房间隔断:数据链路层,负责帧怎么组、仲裁怎么做、错误怎么检测。ISO 11898-1定义了CAN的帧格式和仲裁机制。
- 房间里的家具和规矩:应用层,负责数据代表什么含义、怎么打包、怎么寻址。SAE J1939主要在这一层,同时它自己也定义了一套网络层和传输层规则。
所以你看,ISO 11898是"楼"本身的标准,J1939是"楼里怎么办公"的标准。一栋楼可以只盖好框架不装修(只用ISO 11898),也可以按J1939的规矩精装修(跑J1939协议)。这就是为什么同一条CAN总线上,物理层测量结果一样,但上层解析规则可以完全不同。
2.2 ISO 11898到底规定了什么
ISO 11898这个标准其实是一个系列,常见的有几个部分:
| 标准编号 | 覆盖内容 | 通俗理解 |
|---|---|---|
| ISO 11898-1 | 数据链路层与物理信令 | CAN帧格式、仲裁、错误处理 |
| ISO 11898-2 | 高速物理介质连接 | 高速CAN收发器电气特性 |
| ISO 11898-3 | 低速容错物理层 | 低速CAN,容错能力更强 |
新手最常接触的是ISO 11898-2,也就是高速CAN的物理层标准。它规定了:
- 差分信号传输,CAN_H和CAN_L两根线
- 显性位和隐性位的电平定义
- 终端电阻120欧姆(两端各一个)
- 最高速率1Mbps(经典CAN)
- 总线拓扑为线性总线,节点挂在主干上
注意:很多人以为ISO 11898就是"CAN协议"的全部,其实它只管到数据链路层。你抓到一个CAN帧,能看懂它是标准帧还是扩展帧、ID是多少、数据几个字节,这是ISO 11898-1的功劳。但数据段里每个字节什么意思,ISO 11898不管。
2.3 J1939在ISO 11898之上又加了什么
SAE J1939是建立在CAN基础之上的应用层协议,主要面向商用车、重型车辆、工程机械等领域。它复用了ISO 11898定义的物理层和数据链路层,然后往上叠了好几层自己的规则:
- 网络层:定义了29位扩展ID的分配方式,把ID拆成优先级、PGN、源地址等字段
- 传输层:定义了多包传输机制(TP.CM和TP.DT),解决CAN单帧最多8字节的限制
- 应用层:定义了各种参数组(PGN)和可疑参数(SPN),规定了每个参数怎么解析
换句话说,J1939把CAN的29位ID从"一个标识符"变成了"一套寻址系统"。这是理解J1939最关键的一步。在ISO 11898层面,ID就是一个用于仲裁和过滤的编号;在J1939层面,这29位被拆解得明明白白,每一位都有含义。
2.4 一张表看清两者的层级关系
| 对比维度 | ISO 11898 | SAE J1939 |
|---|---|---|
| 协议栈位置 | 物理层+数据链路层 | 应用层(含网络层、传输层) |
| 核心职责 | 电信号传输、帧格式、仲裁 | 数据含义、寻址、多包传输 |
| ID使用方式 | 标识符,用于仲裁和过滤 | 29位拆分为优先级/PGN/源地址 |
| 数据段含义 | 不定义,8字节裸数据 | 按PGN定义每个字节含义 |
| 典型速率 | 最高1Mbps | 通常250kbps或500kbps |
| 终端电阻 | 120欧姆 | 沿用ISO 11898,同样120欧姆 |
这张表建议新手存下来。以后拿到任何CAN相关材料,先问一句:它讲的是左边这列还是右边这列?答案立刻就清楚了。
3. 29位ID的拆解:J1939最核心的那把钥匙
3.1 为什么J1939要用29位扩展帧
经典CAN有11位标准帧和29位扩展帧两种。11位ID最多2048个标识符,对于简单系统够用,但对于一辆重型卡车动辄几十上百个ECU、每个ECU要发多种参数的场景,11位根本不够分。
J1939选择了29位扩展帧,并且把这29位做了极其精细的划分。这是J1939区别于"裸CAN"最本质的特征。如果你只学ISO 11898,你看到的是一个29位数字;如果你学了J1939,你看到的是优先级、参数组编号、源地址三个有意义的字段。
3.2 29位到底怎么分
J1939的29位ID结构如下:
| 位段 | 名称 | 含义 |
|---|---|---|
| 28-26(3位) | Priority | 优先级,0最高,7最低 |
| 25(1位) | Reserved | 保留位,通常为0 |
| 24(1位) | Data Page | 数据页,用于扩展PGN范围 |
| 23-16(8位) | PDU Format | PDU格式,决定PDU1还是PDU2 |
| 15-8(8位) | PDU Specific | PDU特定字段,含义随PF变化 |
| 7-0(8位) | Source Address | 源地址,标识发送节点 |
这里有个关键分水岭:PDU Format(PF)的值决定了PDU Specific(PS)字段的含义。
- 当PF < 240时,是PDU1格式,PS字段表示目标地址(Destination Address),也就是点对点通信
- 当PF >= 240时,是PDU2格式,PS字段表示组扩展(Group Extension),也就是广播通信
这个规则我第一次看的时候也觉得绕,后来用一句话记住了:PF小于240是"我跟你说话",PF大于等于240是"我向大家广播"。
3.3 PGN是怎么算出来的
PGN(Parameter Group Number)是J1939里表示"一组参数"的编号。它的计算方式跟PDU格式有关:
- PDU1格式(PF < 240):PGN = Data Page + PF + 00(PS固定为0)
- PDU2格式(PF >= 240):PGN = Data Page + PF + PS
举个例子,发动机转速的PGN是61444(0xF004)。拆开看:
- PF = 0xF0 = 240,属于PDU2
- PS = 0x04
- Data Page = 0
- PGN = 0 + 0xF0 + 0x04 = 0xF004 = 61444
再比如请求PGN(Request)是59904(0xEA00):
- PF = 0xEA = 234,小于240,属于PDU1
- PS = 目标地址
- PGN = 0 + 0xEA + 0x00 = 0xEA00 = 59904
提示:算PGN的时候,PDU1格式下PS要当作0来处理,这是新手最容易算错的地方。我第一次手算PGN就栽在这里,把目标地址也加进去了,结果怎么都对不上。
3.4 源地址和地址声明
J1939里每个ECU都有一个源地址(Source Address),范围0到253。其中:
- 0到127:常用固定地址,比如发动机ECU通常是0,制动ECU通常是11
- 128到247:动态分配地址
- 248到253:特殊用途
- 254:空地址(Null Address)
- 255:全局地址(Global Address),广播时用
地址不是随便设的,J1939有一套地址声明(Address Claim)机制。ECU上电后会发一个地址声明报文(PGN 60928),如果发现地址冲突,会通过优先级仲裁决定谁保留、谁换地址。这套机制保证了即插即用的网络管理能力,也是J1939比裸CAN高级的地方。
3.5 一个实际抓包例子
假设你在总线上抓到一帧:
ID: 0x18F00400 Data: FF FF 7D 00 FF FF FF FF按J1939拆解:
- 0x18F00400 = 二进制 0001 1000 1111 0000 0000 0100 0000 0000
- 优先级 = 110 = 6
- 保留位 = 0
- 数据页 = 0
- PF = 0xF0 = 240
- PS = 0x04
- 源地址 = 0x00 = 0(发动机ECU)
PGN = 0xF004 = 61444,查J1939标准可知这是"电子发动机控制器1"(EEC1)。数据段前两个字节FF FF是发动机扭矩相关,第三、四字节7D 00按小端解析是0x007D = 125,对应发动机转速。J1939里转速的分辨率是0.125 rpm/bit,所以实际转速 = 125 × 0.125 = 15.625 rpm?不对,这里要再乘一个系数,实际转速计算是 (0x007D) × 0.125 = 15.625,但标准里EEC1的转速字节是第4、5字节,且分辨率是0.125 rpm/bit,所以125 × 0.125 = 15.625 rpm显然不对,说明我取的字节位置需要再核对。
这个例子说明一个重点:光会拆ID还不够,还得会查PGN对应的SPN定义,知道每个字节的位置、分辨率、偏移量。这就是J1939应用层的核心工作。
4. 物理层和终端电阻:两者共享但常被误解的部分
4.1 终端电阻到底该接几个、接在哪
CAN总线的终端电阻是新手最容易出问题的地方。ISO 11898-2规定,高速CAN总线两端各需要一个120欧姆的终端电阻,总共两个,并联后总线静态电阻约60欧姆。
我见过太多现场问题出在这里:
- 有的节点板子上自带了120欧姆,结果一条总线上接了五六个,并联后电阻低到20多欧姆,通信直接瘫
- 有的整条总线一个终端电阻都没有,短距离低速勉强能跑,一上速度就丢帧
- 有的只在主机端接了一个,以为"有一个就行"
注意:终端电阻的作用是消除信号反射,匹配总线特性阻抗。CAN总线特性阻抗约120欧姆,所以两端各接一个120欧姆,让信号到达末端时被吸收而不是反射回来。接多了相当于阻抗不匹配,接少了反射严重,两种情况都会导致通信异常。
正确的做法是:整条总线只有最远的两个物理端点各接一个120欧姆,中间节点一律不接。如果你用的是成品线束,通常线束两端已经集成好了;如果是自己搭的测试台,一定要确认清楚每个节点的电阻配置。
4.2 波特率和线长的关系
ISO 11898-2对波特率和总线长度的关系有明确建议:
| 波特率 | 最大总线长度 |
|---|---|
| 1 Mbps | 40米 |
| 500 kbps | 100米 |
| 250 kbps | 250米 |
| 125 kbps | 500米 |
| 50 kbps | 1000米 |
这个表背后的逻辑是:波特率越高,位时间越短,信号在总线上的传播延迟占比就越大,允许的总线长度就越短。信号在铜线里的传播速度大约是光速的2/3,也就是每米约5纳秒。1Mbps时一个位时间是1微秒,信号跑一个来回的时间必须远小于位时间,否则采样点会错位。
J1939常用的250kbps和500kbps,对应总线长度250米和100米,覆盖了大部分商用车和工程机械的场景。这也是为什么J1939没有选1Mbps——车太长,1Mbps跑不了那么远。
4.3 采样点设置:容易被忽略的细节
CAN的位时间分成几个段:同步段、传播段、相位缓冲段1、相位缓冲段2。采样点就在相位缓冲段1和2之间。ISO 11898建议采样点在位时间的75%到87.5%之间。
这个参数在配置CAN控制器的时候要设置。如果总线上所有节点的采样点不一致,通信距离一长就容易出错。我遇到过一批节点,有的用75%,有的用87.5%,短距离测试没问题,装车后跑起来就间歇性丢帧,查了好久才发现是采样点不统一。
提示:J1939对采样点有推荐值,通常建议在87.5%左右。配置的时候尽量让所有节点保持一致,尤其是不同厂商的ECU混用的时候,这一项要提前确认。
4.4 共模电感和隔离:工业场景的加分项
在实验室里跑CAN,不接共模电感、不做隔离,通常也能通。但到了工业现场或者车载环境,电磁干扰严重,地电位差大,这时候就需要:
- 共模电感:抑制共模干扰,提高抗扰度
- 隔离收发器:把CAN总线侧和MCU侧电气隔离,防止地环路和浪涌损坏
这些不是ISO 11898强制要求的,但属于工程实践中的常规做法。J1939的应用场景(商用车、工程机械)环境恶劣,所以实际产品里几乎都会加隔离。
5. 多包传输:J1939解决8字节限制的独门方案
5.1 为什么需要多包传输
CAN单帧最多8字节数据,这是ISO 11898-1定死的。但J1939应用层很多参数组远超8字节,比如故障码列表、软件版本信息、标定数据,动辄几十上百字节。怎么办?
J1939的传输层定义了多包传输机制,用两个专门的PGN来协调:
- TP.CM(Transport Protocol Connection Management):PGN 60416,负责协商传输
- TP.DT(Transport Protocol Data Transfer):PGN 60160,负责实际数据传输
5.2 多包传输的完整流程
以发送一个长报文为例,流程是这样的:
- 发送方发TP.CM.BAM或TP.CM.RTS:BAM是广播方式,RTS是点对点方式。报文里包含总字节数、总包数、目标PGN
- 接收方回应TP.CM.CTS(点对点方式):告诉发送方可以开始发,以及每次发几包
- 发送方发TP.DT:每包7字节有效数据,第1字节是包序号
- 接收方收齐后组包:按序号拼接,还原完整数据
BAM方式不需要接收方回应,直接连发,适合广播;RTS/CTS方式有流控,适合点对点可靠传输。
5.3 多包传输的实操坑
我在实际项目里踩过几个多包传输的坑:
- 包序号从1开始,不是0:TP.DT的第一个字节是序号,从1递增。我一开始按0开始写,接收方组包全乱
- 最后一包不足7字节要填充:J1939规定用0xFF填充,接收方按总字节数截断
- BAM传输间隔有要求:包与包之间最小间隔50ms到200ms,发太快接收方处理不过来
- 超时处理:点对点传输如果CTS没来,发送方要超时重试,不能死等
注意:多包传输是J1939应用层的一部分,ISO 11898完全不涉及。如果你只用ISO 11898,遇到超过8字节的数据就得自己想办法拆包组包,而J1939已经把这套机制标准化了。
5.4 和CAN FD的区别
这里顺带提一句CAN FD。CAN FD是ISO 11898-1的后续演进,数据段最多64字节,速率也可以更高。但J1939目前主流还是基于经典CAN,虽然有J1939-22(基于CAN FD的新版),但存量市场里经典CAN的J1939还是绝对主力。
所以新手不要一上来就想着用CAN FD绕过8字节限制,先搞清楚J1939的多包传输机制,这是理解现有车载网络的基础。
6. 实际项目中怎么快速判断该用哪套规则
6.1 看文档标题和来源
拿到一份CAN相关文档,先看标题:
- 标题里有"ISO 11898"、"物理层"、"数据链路层"、"收发器"、"终端电阻"——这是底层标准
- 标题里有"J1939"、"PGN"、"SPN"、"商用车"、"重型车辆"——这是应用层协议
6.2 看ID是11位还是29位
抓包的时候看ID:
- 11位标准帧:可能是ISO 11898裸CAN,也可能是其他应用层协议(如CANopen)
- 29位扩展帧:大概率是J1939,但也要确认,因为CANopen也用29位
进一步确认:把29位ID按J1939规则拆,如果PF、PS、SA字段拆出来合理(PF值常见、SA在0-253范围),基本就是J1939。
6.3 看数据段是否有规律
J1939的数据段通常有明确的字节定义,比如:
- 前两字节常是扭矩或转速
- 有分辨率(如0.125 rpm/bit)和偏移量
- 多字节参数通常小端排列
裸CAN的数据段往往没有统一规律,取决于具体项目自定义。
6.4 一个实用的判断流程
| 判断步骤 | 观察点 | 结论 |
|---|---|---|
| 第一步 | 文档是否提到PGN/SPN | 是→J1939 |
| 第二步 | ID是11位还是29位 | 29位→倾向J1939 |
| 第三步 | 29位ID拆解是否合理 | PF/PS/SA合理→J1939 |
| 第四步 | 数据段是否有标准分辨率 | 有→J1939 |
| 第五步 | 是否有多包传输 | 有TP.CM/TP.DT→J1939 |
这套流程走下来,基本不会判断错。
6.5 常见误区澄清
最后澄清几个新手常犯的误区:
- 误区一:ISO 11898就是CAN协议的全部。实际上它只管物理层和数据链路层,应用层是空的
- 误区二:J1939是另一种总线。不是,J1939跑在CAN总线上,物理层完全一样
- 误区三:学了J1939就不用学ISO 11898。不行,物理层出问题的时候,J1939知识帮不了你,还得回到ISO 11898查终端电阻、查电平、查采样点
- 误区四:J1939只用于商用车。虽然它起源于商用车,但现在工程机械、农业机械、船舶、发电机组都在用,覆盖面很广
7. 给新手的上手路径建议
如果你刚接触这块,我建议按这个顺序上手:
- 先搞懂ISO 11898-2的物理层:终端电阻、电平、波特率、线长,这些是基础中的基础,出问题最先查这里
- 再学ISO 11898-1的帧格式:标准帧、扩展帧、仲裁、错误帧,能看懂抓包数据
- 然后学J1939的29位ID拆解:优先级、PGN、SA,这是J1939的钥匙
- 接着学常用PGN和SPN:先掌握发动机、车速、制动这几个高频的
- 最后学多包传输和地址声明:这是进阶内容,实际项目遇到再深入
工具方面,一台CAN分析仪(支持J1939解析的更好)、一个示波器(看波形和电平)、一个万用表(量终端电阻)基本就够了。软件上,很多分析仪自带J1939数据库,能直接显示PGN名称和参数值,省去手动查表的麻烦。
我在实际带新人的时候发现,最容易卡住的地方不是协议本身多难,而是没有建立起"分层"的思维习惯。一旦你养成拿到问题先问"这是哪一层的事"的习惯,ISO 11898和J1939的区别就再也不会搞混了。物理层的问题查ISO 11898,数据含义的问题查J1939,各归各管,排查效率会高很多。
最后分享一个我自己的小技巧:在项目里建一个自己的PGN速查表,把常用PGN、对应SPN、字节位置、分辨率、偏移量都整理进去,用的时候直接查,比翻标准文档快得多。这个表随着项目积累会越来越值钱,是实打实的经验资产。