24V 系统与 SAE J2602 实战:12V 上跑得好好的 LIN 子网,接到 24V 母线就”整条失联”——门限比率、偏差预算、J2602 那四条裁剪,我从头对了一遍

2026年10月12日 0 By admin

去年做一台 24V 平台的工程机械,座舱面板、风机、后视镜、雨刮、座椅这几路附件走的是 LIN 子网,一条总线、一个主机、八个从节点。这套东西在 12V 平台上跑了三年,通讯故障一次没出过。整机电气平台换成 24V 之后,问题一次来了三条:

  • 上电后主机能正常发帧,从节点偶发不响应,发生率大概 1%;
  • 充电机一启动、母线冲到 28.8 V,整条子网集体掉线几秒;
  • 客户要求电气与网络口径按 SAE J2602 走(北美供应体系),我们发现原来那套「事件触发帧 + 从节点互读对方应答」的设计,直接不合法。

三条现象看着是三个问题,最后拆出来是同一件事的三个面:我把 12V 平台的 LIN 设计当成了”电气无关”的东西。实际上 LIN 的电平从来不是”12V 逻辑”,而是”本节点 VSUP 的一个比例”;速率和位速率偏差预算是跟着电压等级换口径的;而 J2602 不是”更高级的 LIN”,它是把 LIN 里所有厂商各自解释的地方钉死之后的一个裁剪子集。

这一篇就把 24V 与 J2602 这两件事从头对一遍。数字全部来自公开规范与数据手册(ISO 17987-4、SAE J2602、TI 收发器数据手册),文末给了配套的现场估算器。

LIN 的基础部分(帧结构、PID、校验和、调度表、休眠唤醒、物理层)在《LIN 总线主从调度与报文超时实战》里已经铺过了,这篇只讲 24V 和 J2602 多出来的那一层——上下两层,不重复。顺带说一句 24V 母线的来源:24V 平台的”母线冲到 28.8 V”其实就是两串 12V 铅酸/锂电在充电态,这一点在《电池管理与 SOC 估算实战》里算过。

先把三件事分清:LIN 2.x / ISO 17987 / SAE J2602

这是第一个容易混的地方,混了之后查资料永远查不到点上。

名称 是什么 管什么 关键点
LIN 2.x LIN 联盟的规范(1.0/1.1/1.2/1.3/2.0/2.1/2.2A) 协议 + 物理层 + 节点配置 + 诊断 速率 1~20 kbps 任选,12V 口径;24V 参数在这套规范里没定义
ISO 17987 系列 2016 年把 LIN 收成国际标准(-1/-2/-3/-4 等部分),2025 出第 2 版 同 LIN 2.2A,但补齐了 24V 的电气物理层 24V 的电气参数就写在 17987-4 里;第 2 版还把 master/slave 改称 commander/responder
SAE J2602 北美车厂基于 LIN 2.0 派生的裁剪子集(-1 网络、-2 一致性测试) 只规定”怎么用”,不加新功能 固定 10.417 kbps、禁从-从通信、禁事件触发帧、EMI/ESD 更严

一句话:你要查 24V 的电气参数,去 ISO 17987-4;你要查”哪些用法在北美的供应体系里不许用”,去 SAE J2602。 两者不冲突,是正交的两件事——J2602 的节点通常也符合 ISO 17987-4 的 24V 电气要求。

口径澄清:网上有些厂商支持页面把 J2602 的速率写成 19.2 kbps。NXP 官方技术支持的答复与 SAE J2602-1 的口径都是固定 10.417 kbps,ISO 17987-4 里 24V 的档位命名也是 BR_Range_10K。以 10.417 kbps 为准。

第一条红线:LIN 的电平不是”12V 逻辑”,是”VSUP 的比率”

这是整篇文章最值钱的一条,也是”12V 抄 24V”翻车的头号原因。

LIN 是单线总线,节点靠一只上拉电阻(主机 900~1100 Ω,从机 20~60 kΩ)把总线拉高,任何一个节点把总线拉到地就是显性位(逻辑 0),没人拉的时候就是隐性位(逻辑 1)。电平是按本节点 VSUP 的百分比定义的,不是固定电压:发送端要求显性 ≤0.2×VSUP、隐性 ≥0.8×VSUP;接收端要求显性 ≤0.4×VSUP、隐性 ≥0.6×VSUP;接收门限中心 VBUS_CNT 是 0.475~0.525×VSUP。

把这组比率换算成绝对值,差别就出来了:

门限 比率 12V 平台(VBAT 13.5 V) 24V 平台(VBAT 27.6 V)
发送 显性 ≤ 0.200×VSUP 2.70 V 5.52 V
发送 隐性 ≥ 0.800×VSUP 10.80 V 22.08 V
接收 显性 ≤ 0.400×VSUP 5.40 V 11.04 V
接收 隐性 ≥ 0.600×VSUP 8.10 V 16.56 V
门限中心 0.475~0.525×VSUP 6.41~7.09 V 13.11~14.49 V

看最后两行:在 24V 母线上,”隐性”要求总线电压高于 16.56 V(接收侧)/ 22.08 V(发送侧)。如果是 12V 平台抄过来的设计——比如主机那只 1 kΩ 上拉接在了稳压后的 12V 轨上(很多人下意识觉得”LIN 不就是 12V 逻辑嘛”)——那 24V 母线上的隐性平台只有 11.3 V 左右:

  • 接收侧 11.3 V < 16.56 V → 判不出隐性;
  • 发送侧 11.3 V < 22.08 V → 从节点发出的隐性位不合法。

现象就是”整条子网失联”或者”总线一直贴着低电平”,而示波器上看波形还挺干净,特别容易误判成收发器坏了。

一个反直觉的推论:24V 的接收窗口其实更宽

按比率算,24V 的接收窗口宽度是 27.6×(0.6−0.4) = 5.52 V,比 12V 的 13.5×0.2 = 2.70 V 宽了一倍多。也就是说,从抗绝对干扰的角度看,24V 反而更宽松——同样 1 V 的共模干扰,在 24V 系统里占窗口的比例只有 12V 的一半。

所以”24V 更容易受干扰”这个说法是错的。24V 真正的问题是另外三条:器件耐压、门限口径、速率与偏差预算。这也是为什么排查 24V LIN 问题的第一步不是查 EMC,是量隐性平台的绝对电压。

/* 门限判定的定点实现:把比率乘进去,别在代码里写死 5.4V/8.1V 这种 12V 时代的常数 */
#define LIN_RX_DOM_NUM 4u     /* 接收显性 <= 0.4 x VSUP */
#define LIN_RX_DOM_DEN 10u
#define LIN_RX_REC_NUM 6u     /* 接收隐性 >= 0.6 x VSUP */
#define LIN_RX_REC_DEN 10u

/* vsup_mv: 本节点实测 VSUP(mV);bus_mv: ADC 采到的总线电压(mV)
   返回 0=显性 1=隐性 2=落在门限中间的不确定带(要计数上报,别硬判) */
uint8_t lin_level(int32_t vsup_mv, int32_t bus_mv)
{
    int32_t th_dom = (int32_t)(((int64_t)vsup_mv * LIN_RX_DOM_NUM) / LIN_RX_DOM_DEN);
    int32_t th_rec = (int32_t)(((int64_t)vsup_mv * LIN_RX_REC_NUM) / LIN_RX_REC_DEN);
    if (bus_mv <= th_dom)  return 0;
    if (bus_mv >= th_rec)  return 1;
    return 2;   /* 不确定带:24V 下这个带宽 5.52V,落进来通常意味着上拉轨或器件不对 */
}

ISO 17987-4:24V 到底改了哪些参数

LIN 1.3~2.x 里”供电 7~18 V、耐压 −0.3~40 V”是 12V 系统的口径,24V 系统在这套规范里根本没有定义。24V 的电气参数是 ISO 17987-4 才补齐的(2016 版 5.3.5 章节,2025 第 2 版继续沿用并调整了若干取值)。

参数 12V 系统 24V 系统
工作电压 VBAT(正常) 8 ~ 18 V 16 ~ 36 V(20k 档)/ 8 ~ 36 V(10k 档)
低压工作区间 6.5 ~ 8 V 同 10k 档下限 8 V
非破坏电压 VSUP_NON_OP −0.3 ~ 40 V −0.3 ~ 58 V
总线限流 IBUS_LIM 40 ~ 200 mA 75 ~ 300 mA
主机上拉 R_commander 900 ~ 1100 Ω 900 ~ 1100 Ω(没变)
从机上拉 R_responder 20 ~ 60 kΩ 20 ~ 60 kΩ(没变)
接收门限中心 VBUS_CNT 0.475 ~ 0.525×VSUP 0.475 ~ 0.525×VSUP(没变)
通信不保证 VBAT > 18 V 或 < 16 V(20k) VBAT > 36 V 或 < 16 V(20k)
主节点偏差 < ±0.5% 20k: < ±0.3% / 10k: < ±0.5%
从节点同步后偏差 < ±2% 20k: < ±0.6% / 10k: < ±2%
从-从节点偏差 < ±2% 20k: < ±0.6% / 10k: < ±2%

这张表里能直接读出四条设计约束:

  1. 上拉/下拉电阻、门限中心的比例口径,两套系统完全一样。 变的只有 VSUP 本身。所以”把电阻改一下就能上 24V”是错的方向——要改的是器件耐压、门限换算和速率。
  2. 非破坏耐压从 40 V 提到 58 V。 24V 母线充电可到 28.8 V,再留 load dump 余量,40 V 不够。(TVS、放电管、接地路径那一层的账在《ESD/浪涌防护实战》里算过,这里只管”正常工作时能不能活”这一层。)
  3. 总线限流上限从 200 mA 放宽到 300 mA(下面第三节讲为什么)。
  4. 24V 的偏差预算分 20k / 10k 两档,20k 档比 12V 严一倍以上。 这是选速率的硬约束,也是 24V 系统几乎只跑 10.4 k 的真正原因(第四节展开)。

24V 的 duty-cycle 测试门限是三组,不是一组

再往下一层:LIN 收发器的一致性测试里有 duty cycle 参数(D1~D4),它靠”在规定门限上量总线边沿时间”来判定波形质量。24V 系统的这套测试门限,跟 12V 的 0.4/0.6 不是一组数,而是按速率和 VSUP 区间分三组给(ISO/DIS 17987-4 Param 73/74/75,TI 收发器数据手册会照抄这张表):

测试项 VSUP 区间 位时间 TH_DOM TH_REC 限值
D2_24V(20 kbps) 15.6 ~ 36 V 50 µs 0.302×VSUP 0.446×VSUP ≥ 0.642
D3_24V(10.4 kbps) 7 ~ 36 V 96 µs 0.581×VSUP 0.744×VSUP ≥ 0.386
D4_24V(10.4 kbps) 4.6 ~ 36 V 96 µs 0.284×VSUP 0.422×VSUP ≥ 0.591

换算到 VSUP = 27.6 V:

  • D2_24V:TH_DOM = 8.34 V,TH_REC = 12.31 V
  • D3_24V:TH_DOM = 16.04 V,TH_REC = 20.53 V
  • D4_24V:TH_DOM = 7.84 V,TH_REC = 11.65 V

注意这三组比值:0.284/0.422 和 0.302/0.446 在 0.4/0.6 下面,0.581/0.744 在 0.4/0.6 上面——12V 那组 0.4/0.6 正好被夹在中间。 也就是说,在 24V 上单拿 0.4/0.6 去套”既不充分也不必要”,必须按目标工况(哪一档速率、VSUP 落在哪个区间)查表。

这一条对现场的意义很直白:你拿 12V 的模板去测 24V 收发器,测出来的”合格”没有意义;反过来,24V 上的波形看着”有点怪”,也未必超标。判定必须回到当档的门限上。

第二条红线:显性电流与总线限流

上拉电阻没变(主机 1 kΩ ±,串一只防反灌二极管),但 VSUP 翻倍了。显性位时,电流从 VBAT 经二极管、经主机上拉电阻、经某个节点的输出管灌到地:

I_dom = (VSUP − V_diode − V_DS) / R_master

取 V_diode = 0.7 V、V_DS = 1.0 V(工程典型值)、R_master = 1000 Ω:

工况 VSUP 显性位电流 隐性平台 适用限流区间
12V 静态 12.6 V 10.90 mA 11.90 V 40 ~ 200 mA
12V 充电 13.5 V 11.80 mA 12.80 V 40 ~ 200 mA
24V 静态 25.2 V 23.50 mA 24.50 V 75 ~ 300 mA
24V 充电 27.6 V 25.90 mA 26.90 V 75 ~ 300 mA
24V 上限 28.8 V 27.10 mA 28.10 V 75 ~ 300 mA

同一颗 1 kΩ 上拉,什么都没改,24V 下的显性电流是 12V 的 2.195 倍。 这就直接解释了 ISO 17987-4 为什么把总线限流上限从 200 mA 放宽到 300 mA:这个参数管的是”多节点并联显性或总线对电源/对地短路时,节点能不能活着、主机还能不能区分故障”。24V 下单节点的显性电流已经是 26 mA 量级,多个节点并联很容易顶到 12V 时代的 200 mA 上限。

两条推论:

  1. 总线短路保护的门限要按 24V 复核。 12V 时代”总线拉到 3 V 就是短路”的判据,在 24V 上不一定成立。
  2. 发热预算翻倍。 上拉本身在隐性时不耗电,但显性期间是实打实的 I²R,而且显性占空比越高越热。24V 下若显性占空比 30%,平均上拉电流 = 25.90×0.30 = 7.77 mA(12V 是 11.80×0.30 = 3.54 mA)。调度表里连着发长诊断帧(一条 8 字节诊断帧在 10.4 k 下最长 16.7 ms)时,主机上拉电阻、防反二极管、从节点输出管的温升要单独核一遍——这是 12V 平台上从来不用管的事。

物理层这笔账我们在《RS485/Modbus 抗干扰实战》里算过一遍(偏置电阻、门限、接地),思路完全互通;差别只是 LIN 是单端对地、门限按 VSUP 比率,而 RS485 是差分对、门限是固定的 ±200 mV。

第三条红线:速率、偏差预算和时隙

10.417 kbps 这个数不是拍出来的

10.417 kbps 就是”位时间 96 µs”的另一种写法(1/96 µs = 10416.67 bps)。LIN 的标称速率都这么派生:20 kbps = 50 µs、19.2 kbps = 52.083 µs。所以位时间是本体,bps 是派生物——改速率时要改的是时间轴,不是”波特率寄存器里的那个数”。

顺便一个好消息:96 µs 位时间在常见主频下能整除。

/* STM32 类 USART,16 倍过采样:USARTDIV = f_CK / (16 x baud) */
/* 10.417 kbps:  f_CK = 48 MHz -> USARTDIV = 287.99 -> 取 288
                 f_CK = 24 MHz -> USARTDIV = 143.99 -> 取 144
   两种主频下实际速率都是 10416.667 bps,偏差 -0.003%,可以忽略   */
/* 对照 19.2 kbps @48 MHz: USARTDIV = 156.25 -> 尾数 156、小数 4/16,也是整除的 */

所以速率切换本身不难。难的是所有”以时间为单位”的东西都要跟着重算。

位时间从 52.083 µs 变到 96 µs,全表都要动

LIN 的帧时间是按位算的:帧头固定 34 位(13 位 break + 1 位分隔 + 10 位 sync 场 + 10 位 PID),应答部分 10×(N+1) 位(起始位 + 8 数据位 + 校验 + 停止位,N 是数据字节数)。帧最大时间 = 1.4 × 标称帧时间(40% 余量分给应答空间和字节间间隔)。

位速率 位时间 同步场(8 位) 帧头 标称/最大 4 字节整帧 标称/最大 8 字节整帧 标称/最大 8 字节帧率上限
19200 bps 52.083 µs 416.7 µs 1.771 / 2.479 ms 4.375 / 6.125 ms 6.458 / 9.042 ms 154.8 帧/s
10417 bps 96.000 µs 768.0 µs 3.264 / 4.570 ms 8.064 / 11.290 ms 11.904 / 16.666 ms 84.0 帧/s
20000 bps 50.000 µs 400.0 µs 1.700 / 2.380 ms 4.200 / 5.880 ms 6.200 / 8.680 ms 161.3 帧/s

位时间比 = 19200/10417 = 1.8432。 8 字节帧最大时间从 9.042 ms 涨到 16.666 ms,帧率上限掉了一半(154.8 → 84.0 帧/s)。

于是最典型的翻车现场就是:位速率改了、时基和时隙没改。

时隙规则是 T_Frame_Slot > jitter + T_Frame_Maximum,而且时隙必须是时间基的整数倍、从 tick 起点开始。以 5 ms 时间基、1 ms jitter 为例:

字节数 帧最大(10.4k) 需占用 = 最大 + jitter 最少 mini-slot 19.2k 时代需要几格
1 字节 7.257 ms 8.257 ms 2 格 1 格
2 字节 8.601 ms 9.601 ms 2 格 2 格
4 字节 11.289 ms 12.289 ms 3 格 2 格
8 字节 16.665 ms 17.665 ms 4 格 3 格

19.2k 时代配 5 ms 时间基,1 字节帧最大 3.937 ms + 1 ms jitter 刚好占 1 格;换到 10.4k,同样一个 1 字节帧要 7.257 ms,直接吃掉 2 格。 调度表里每一个时隙都得跟着重算。

不改会怎样?一格的时隙塞不下这一帧,主机的下一个 break 就把应答尾巴顶掉了。从节点不知道自己被截断(它只管收到帧头就应答),只有主机看得到 no_response 在涨。 这正是我们上电后那 1% 偶发不响应的来源——它不是”偶尔干扰”,是”每一格都在悬崖边,偶尔被顶掉”。

一个字节能容多少位时间漂移

偏差预算这条第一次看容易忽略,但它正是”为什么 24V 只跑 10.4 k”的答案。

从节点在同步场(0x55,量”第一个下降沿到最后一个下降沿”= 8 个位时间)上量出主机的位时间,然后按这个位时间采样。偏差预算约束的是”从节点自己时钟相对主节点的偏差”,它最坏情况下的影响可以这样估:一个字节 = 10 个位时间,中位采样的裕量是 0.5 位。

档位 从节点同步后偏差预算 每字节(10 位)累积 占 0.5 位裕量
12V 系统 < ±2.0% 0.200 位 40.0%
24V / BR_Range_20K < ±0.6% 0.060 位 12.0%
24V / BR_Range_10K < ±2.0% 0.200 位 40.0%

看第二行:24V 跑 20 k 时,从节点时钟要比 12V 平台严 3.3 倍(±0.6% vs ±2.0%)。LIN 从节点的卖点本来就是”用廉价 RC 振荡器就能跑,靠同步场对齐”,±0.6% 这个口径下,大量用 RC 时钟的低成本从节点会被挤出预算,尤其是温度扫到 −40 ℃ / +105 ℃ 两端的时候。这就是我们第三条现象里”高温/低温才开始丢帧”的那一类。

而 10k 档把口径放回了 12V 的水平(±0.5% / ±2% / ±2%)。所以:

24V 系统选 10.4 kbps,不是”为了省成本”或”为了降 EMI”这么简单,而是为了让现成的 RC 时钟从节点拿过来就能用,不用换晶振。

再叠加一条:10.4 k 的位时间是 19.2 k 的 1.84 倍,压摆率要求相对位时间放宽了——24V 下幅值更大、边沿更陡,同样的位宽里更容易满足 EMI 要求。速率、预算、EMC 三件事在 10.4 k 这个点上刚好凑齐。

SAE J2602:LIN 的”裁剪版”

J2602 是 SAE 在 2010 年前后把 LIN 2.0 收编成北美车厂口径的产物(-1 管网络定义、-2 管一致性测试)。它不加功能,只做裁剪,目标是”跨供应商、跨车型的可互换性”——一个供应商的 ECU 和另一个供应商的从节点,装上去就该能用,不能靠”我们是这么解释规范的”糊过去。

四条硬裁剪,条条都会打到现场设计:

项目 LIN 2.x SAE J2602 对设计的影响
速率 1 ~ 20 kbps 任选 固定 10.417 kbps 时基/时隙/超时窗口全部按 96 µs 位时间重算
从-从通信 允许(从节点可以读总线上别的应答) 禁止 任何”从从节点直接取信号”的设计直接非法,信号必须走主机转发
事件触发帧 允许(碰撞时自动转无条件帧) 禁止 只剩无条件帧 + 零星帧;碰撞回退那套状态机白写
节点配置 动态配置(NAD 可变) 预定义静态配置 NAD/PID 必须在产线写入并落 NVM,不能靠上电再协商
错误处理 常规诊断服务 更严(J2602-1 下 0xB5 变 Targeted Reset) 从节点必须实现”被点名复位”并回滚到安全状态
EMI / ESD 常规 更严 压摆率、滤波、ESD 等级按汽车级核;工业现场同样受益

从-从通信被禁这条最容易被低估。我们原来有个用法:座椅从节点直接读面板广播的”用户模式”应答,省一次主机转发。在 LIN 2.x 下这是允许的(虽然不推荐),在 J2602 下直接不合法——因为”哪个节点在什么时候应答”必须完全由主机的调度表决定,从节点擅自读别人的应答会破坏这个确定性。

事件触发帧被禁则是砍掉了一整套机制。事件触发帧的本意是”多个从节点共享一个 ID,谁有事件谁应答;碰撞了就退化成无条件帧逐个问”。J2602 干脆不允许——代价是轮询变多、带宽变紧,收益是调度表完全可预测。这在带宽本来就只有 10.4 k 的 24V 系统上是要认真算账的:帧率上限 84 帧/s,扣掉诊断帧和零星帧,剩下的信号帧预算并不宽裕。

这种”为了确定性牺牲带宽/兼容性”的取舍在 CAN 那边也见过——《CAN FD 与经典 CAN 混合组网实战》里那个”一开 BRS 老节点就广播错误帧”的坑,本质也是两套口径混跑把确定性打坏了。错误计数与分桶的排查套路,可以照《CAN 总线错误帧排查实战》那篇来做。

静态节点配置与 Targeted Reset

J2602 要求节点是预定义静态配置:NAD(节点地址)和 PID(帧标识)在产线写入、存 NVM,上电直接可用,不依赖主机来”动态分配”。这带来的直接要求是:

  1. 产线必须有写入工位,把 NAD/PID 按工单写进每个从节点,并且能读回来核对。
  2. 擦写必须原子。 NAD 落在 NVM 里,上电自检若发现 NAD 非法(比如落在保留值、或与工单不符),节点必须能进入一个可被重新配置的安全状态,而不是”带着错 NAD 硬跑”。
  3. 0xB5 这个服务在 J2602-1 下语义变了:LIN 2.x 里 0xB5 是 “Assign NAD via SNPD”(靠节点位置检测自动寻址),J2602 下它变成 Targeted Reset(被点名复位)——主机的”最后手段”,用来把某个行为异常的节点强制拉回初始状态。从节点收到后要复位,并让应用层知道”这次复位是主机要求的”。

第 3 条是 J2602 里少有的”加了行为”的地方,也是现场排障最好用的一招:怀疑某个从节点状态跑飞了,发 0xB5 看它是不是能干净地复位并从安全态重启。如果复位后行为依旧,那就不是软件状态问题,回到电气层查。

节点配置服务 0xB0~0xB7:这张表要背下来

不管是 LIN 2.x 还是 J2602,节点配置与识别都走同一套服务号(0xB0~0xB7),单帧承载、逐节点配置。通配 NAD 是 0x7F,功能寻址 NAD 是 0x7E,响应 RSID = SID + 0x40,未用字节填 0xFF。

SID 服务 LIN 2.x 里 说明
0xB0 Assign NAD 可选 重新分配 NAD;用初始 NAD 或通配 0x7F 发起
0xB1 Assign frame identifier LIN 2.0 已废弃 仅 LIN 2.0 兼容模式保留
0xB2 Read by Identifier 强制 读产品 ID(Id=0) / 序列号(Id=1) / 用户自定义(Id=32~63)
0xB3 Conditional Change NAD 可选 按条件改 NAD,用来分离冲突节点
0xB4 Data Dump 可选 开发用,多数协议栈不实现
0xB5 Assign NAD via SNPD J2602-1 下改为 Targeted Reset J2602 下这条是硬需求
0xB6 Save Configuration 可选 NAD/PID 落 NVM,擦写动作由应用层做
0xB7 Assign frame identifier range 强制 一帧最多改 4 个 PID,比 0xB1 省时间

只有两条是强制的:0xB2(读标识)和 0xB7(改 PID 范围)。0xB2 之所以强制,是因为”能读出自己是谁”是跨供应商互换的前提;0xB7 强制,是因为 PID 必须能被主机统一重排。

一个正响应的完整样子(Assign NAD,0xB0):

请求(MRF,8 字节):  NAD_initial | 0x06 | 0xB0 | SupID_LSB | FunID_LSB | FunID_MSB | FunID_MSB | NewNAD
响应(SRF,8 字节):  NAD_initial | 0x01 | 0xF0 | 0xFF | 0xFF | 0xFF | 0xFF | 0xFF
                       ^ 初始 NAD,不是新 NAD
                                              ^ RSID = 0xB0 + 0x40 = 0xF0,PCI=0x01 表示单帧 1 字节

两个现场特别容易踩的点:

  • 响应里回的是初始 NAD,不是新 NAD。 很多人以为”改了地址当然回新地址”,结果上位机校验对不上,误判成节点没响应。
  • 通配符 0x7F 按功能寻址处理时,数据场必须全 0xFF。 不然合规的从节点会直接不应答,你会以为是节点坏了。

固件侧:从 19.2 k 迁到 10.4 k 要动哪些地方

把上面几条落到代码上,改动的清单其实不长,但漏一条就是一整类现场故障。

一、时轴全部改成”以位时间为单位”。 不要在代码里散落”2.5 ms””5 ms”这种绝对数,统一用位时间派生:

/* 全部用 ns 表示,改速率只改 LIN_TBIT_NS 一处 */
#define LIN_TBIT_NS        96000u                                  /* 96.000 us */
#define LIN_HEADER_NS      (34u * LIN_TBIT_NS)                     /* 3264000 ns */
#define LIN_RESP_NS(n)     (10u * ((n) + 1u) * LIN_TBIT_NS)
#define LIN_FRAME_NOM_NS(n) (LIN_HEADER_NS + LIN_RESP_NS(n))
#define LIN_FRAME_MAX_NS(n) ((LIN_FRAME_NOM_NS(n) * 14u) / 10u)   /* x1.4 */

/* 时隙:向上取到时间基整数倍,且 > 帧最大 + jitter */
static uint32_t lin_slot_ms(uint8_t nbytes, uint32_t base_ms, uint32_t jitter_ms)
{
    uint32_t need_ms = (LIN_FRAME_MAX_NS(nbytes) + 999999u) / 1000000u;
    need_ms += jitter_ms;
    return ((need_ms + base_ms - 1u) / base_ms) * base_ms;
}

二、错误分桶要重排,超时窗口要乘 1.84。 沿用 LIN 那份三层超时模型(位/字节级、帧级应答窗口、会话级),但帧级应答窗口的基准值全部要按新位时间重算——应答窗口定时器不能跟时隙计时器共用(时隙是 10~20 ms 级的周期性计时,应答窗口是 16.7 ms 级的一次性计时,10.4 k 下两者数值已经很接近,共用必然出错)。

三、J2602 下删掉两套状态机。 事件触发帧的碰撞回退、从-从通信的”偷听”逻辑,全部删掉,别留着当”兼容模式”——留着就会有人在某个分支里把它打开。

四、NAD/PID 的 NVM 落盘要原子。 双区 + 校验的自然做法:

typedef struct {
    uint16_t magic;
    uint8_t  nad;
    uint8_t  pid_map[8];
    uint16_t crc16;
} lin_cfg_t;

/* 0xB0 / 0xB7 改的是 RAM 副本;只有 0xB6 (Save Configuration) 才落 NVM。
   写新槽 -> 校验 -> 置 magic -> 才让新槽生效;掉电时旧槽仍然完整 */

五、加一路 VSUP 采样。 门限判定要用实测 VSUP 按比率算(第 1 节的 lin_level())。这是 24V 平台必须有的——12V 时代很多实现直接用固定门限蒙过去,24V 上蒙不了。

现场五招

这五招都是低成本、当天就能做的,按顺序做,能把上面三条红线逐条排除掉。

  1. 示波器上量三个数:隐性平台电压(应 ≥0.8×VSUP,24V 下约 22 V)、显性谷值(应 ≤0.2×VSUP)、位时间(用同步场 8 位量,10.4 k 下应为 768 µs)。三个数一出来,器件选型和上拉轨的问题基本就定位了。
  2. 母线拉到两个边界各测一轮:28.8 V(充电上限)和 16 V(20k 档工作下限)。充电机一启动就掉线这类现象,就在这一步现形。
  3. 确认主机上拉接的是真实 VBAT,不是稳压后的 12V/5V 轨。这一条看着像废话,但它是”整条子网失联”的头号原因。顺手量一下防反二极管的正向压降,别用错成 1 V 以上的管子。
  4. 逐个从节点记录同步场量到的位时间,做一张漂移分布表。找出时钟最差的那颗——24V 20k 档只有 ±0.6% 预算,差的那颗一定是最先在温度边界上丢帧的。
  5. 配置通道自检三步:先发 0xB2 读产品 ID(能读出来说明配置通道活着),再 0xB7 改 PID + 0xB6 保存、断电重启重读(验证真落进 NVM),最后发 0xB5 验 Targeted Reset(J2602 节点)。

自查清单(13 项)

  • ☐ 收发器是否明确标注支持 12V/24V(核对 VSUP_NON_OP 到 58 V,不是 40 V)
  • ☐ 主机上拉接真实 VBAT,1 kΩ(900~1100 Ω),串防反二极管
  • ☐ 从机上拉 20~60 kΩ(典型 30 kΩ)
  • ☐ 隐性平台实测 ≥ 0.8×VSUP(在最低工作电压下量)
  • ☐ 显性谷值实测 ≤ 0.2×VSUP(在最高工作电压 28.8 V 下量)
  • ☐ 总线限流/短路保护在 75~300 mA 区间内复核过
  • ☐ 位速率 10.417 kbps、位时间实测 96 µs;MCU 分频余量核对(48 MHz→288、24 MHz→144)
  • ☐ 所有时隙按 16.666 ms 级帧最大时间重算,且是时间基整数倍
  • ☐ 帧级应答窗口定时器与时隙计时器分开,窗口基准值乘过 1.84
  • ☐ 诊断帧 0x3C / 0x3D 走经典校验和
  • ☐ J2602:无事件触发帧、无从-从通信(代码里也没留”兼容分支”)
  • ☐ NAD/PID 静态写入并落 NVM(0xB0/0xB7 + 0xB6),上电自校验
  • ☐ 0xB5 Targeted Reset 已实现,且复位后回到安全态;唤醒脉冲 250 µs~5 ms、从节点 100 ms 内就绪

配套工具

文里所有数字都可以用 lin24_j2602_calc.py 自己复算(纯标准库、全离线、16 项自检)。几个常用调用:

python lin24_j2602_calc.py iso              # 12V/24V 参数对照表
python lin24_j2602_calc.py thresh 27.6      # 给定 VSUP 的门限绝对值与判定裕量
python lin24_j2602_calc.py duty 27.6        # 24V duty-cycle 三组测试门限
python lin24_j2602_calc.py rate             # 19.2k/10.4k/20k 的帧时间与帧率对照
python lin24_j2602_calc.py slot 10417 5     # 时隙体检 + 速率迁移重算
python lin24_j2602_calc.py curr 27.6        # 显性电流与限流余量
python lin24_j2602_calc.py tol              # 偏差预算与位漂移累积
python lin24_j2602_calc.py j2602            # J2602 裁剪清单
python lin24_j2602_calc.py svc              # 0xB0~0xB7 服务表
python lin24_j2602_calc.py triage           # 症状分诊表

结语

回到开头那三条现象,最后是这样收口的:

现象 根因 处理
上电后 1% 偶发不响应 时基没跟着从 19.2k 重算,1 字节帧在 10.4k 下要占 2 格却只给了 1 格 全部时隙按 96 µs 位时间重排
充电机一启动集体掉线 工作电压窗口 + 24V 门限比率没按实测 VSUP 算 门限改按 VSUP 比率换算,28.8 V/16 V 两边界各测一轮
J2602 下原设计不合法 用了事件触发帧和从-从通信 改主机轮询 + 无条件帧,信号走主机转发;NAD/PID 产线静态写入

一句话总结:24V 不是”电压高一点的 12V”,它是另一套口径。门限按比率、速率按 96 µs、预算按 20k/10k 分档、用法按 J2602 裁剪——四条都对齐了,24V 上的 LIN 子网跟 12V 一样稳。

下一篇打算写逆变器侧的母线电压采样链路(高压分压电阻串 + 隔离放大器):它是上一篇《电流采样链路设计实战:同一台逆变器早上显示 5.00A、中午 5.42A》的姊妹篇,也是”电压/电流双采样”这条线上的另一半——高压侧多电阻串联分压、比值匹配、电阻的电压系数与自热、隔离放大器输入阻抗与偏置电流、以及母线电压采样必须与开关同步的那几条。对我们做离网/储能逆变器的,这条链路的坑不比电流采样少。