网关下面要挂一串开关量、小传感器和小执行器,上 CAN 太贵、上 RS-485 要两根线还要管地址。LIN 只用一个 12V 单线、最高 20kbit/s、单主多从,从机连晶振都省了,配一颗 TJA1020 收发器就能休眠唤醒。本文用四张表把 LIN 帧结构、标识符奇偶与校验和算法、TJA1020 四种工作模式一次说清,再给出 STM32 主从节点的代码骨架和上云通路。
做 物联网 网关时最常遇到的场景是:主控已经通过 4G/NB-IoT 或以太网上了云,但现场还要接十几个小节点——水阀、门锁、座椅电机、温湿度探头、限位开关。这些节点数据量小、实时性要求不高、但数量多、成本敏感。每接一个就拉两根 RS-485、再分配一个地址,线束和调试成本很快就上去了。结论先放在前面:第一,这类"低速、小包、多节点、成本敏感"的子网,用 LIN 比 RS-485 更省线、比 CAN 更省钱;第二,LIN 的帧结构只有"同步间隔 + 同步场 0x55 + 标识符 + 数据 + 校验和"五段,标识符的两位奇偶和校验和算法必须自己写对;第三,物理层交给 TJA1020,单线 12V、最高 20kbit/s、带休眠唤醒和短路保护,MCU 侧只需一个 UART。下面按选型、帧结构、物理层、代码、上云五步讲透。
先明确 LIN 的定位。LIN 是 Local Interconnect Network,最初为汽车里的车门、座椅、车顶这类"成本敏感、机械元件多"的联合装配单元设计,物理层基于 ISO 9141 标准,用一根 12V 电池单线传输。它的目标不是替代 CAN,而是给 CAN 主干网做辅助子网:把开关、执行元件和传感器从主总线上"分流"下来,让主干网专心跑关键报文。这套思路搬到工业与楼宇物联网里同样成立——主干用 CAN 或以太网,末端的低速小节点走 LIN。
把三条常见子总线放在一起比,岔路口就清楚了。下表按项目实际会关心的维度整理,数据来自各总线标准与器件手册的标称值。
| 对比项 | LIN | RS-485(Modbus RTU) | CAN 2.0B |
|---|---|---|---|
| 信号线数量 | 1 根单线 + 地(12V 电平) | 2 根差分(A/B)+ 地 | 2 根差分(CANH/CANL) |
| 最高速率 | 20 kbit/s | 常见 9.6~115.2 kbit/s | 最高 1 Mbit/s |
| 拓扑与仲裁 | 单主多从,主节点调度,无仲裁 | 主从轮询,无仲裁 | 多主,非破坏性位仲裁 |
| 节点时钟要求 | 从机免晶振,靠同步场自同步 | 每节点需独立晶振/波特率基准 | 每节点需精确晶振 |
| 单帧有效载荷 | 1~8 字节 | Modbus 最多 253 字节 | 最多 8 字节 |
| 典型节点数 | 一般 < 16 个 | 理论 32/128,受驱动能力限制 | 受总线负载限制 |
| 收发器成本量级 | 低(单线,常集成端电阻) | 低 | 中 |
| 适合场景 | 末端低速小节点、执行器、传感器 | 仪表、变频器、工业多机通信 | 整车、电机控制、关键实时网络 |
一句话选型:数据量小、节点多、成本敏感、能接受单主调度,就选 LIN;需要长距离、多字节批量读写、成熟生态,就选 RS-485;需要多主、高实时、带优先级仲裁,才上 CAN。网关里常见的做法是"主干 CAN/以太网 + 支线 LIN",让两类总线各司其职。
LIN 的报文帧分成两段:报文头(主机任务发)和响应(从机任务或主机发)。报文头由同步间隔场、同步场、标识符场组成;响应由 1~8 个数据字节加一个校验和字节组成。主机发完报文头,被标识符寻址的从机在响应场里填数据,主机只管调度、不参与数据搬运——这就是"单主多从"能省掉仲裁机制的原因。
把每一段的定义、长度和取值整理成一张表,写驱动时对着抄即可。
| 字段 | 长度 | 取值 / 规则 | 作用 |
|---|---|---|---|
| 同步间隔场 | ≥13 位显性 + 间隔界定符 | 显性电平持续足够长,再用界定符收尾 | 让失去同步的从机识别到帧起始 |
| 同步场 | 1 字节 | 固定 0x55(0101 0101) | 从机据此测量位时间,实现免晶振自同步 |
| 标识符场 | 1 字节 | 6 位 ID + 2 位奇偶(P0 奇校验、P1 偶校验) | 定义报文内容与数据长度,并做双重奇偶保护 |
| 数据场 | 1~8 字节 | LSB 先发;LIN 1.3 按 ID4/ID5 取 2/4/8,LIN 2.0 可 1~8 | 承载实际数据 |
| 校验和场 | 1 字节 | 经典校验和(只算数据)或增强校验和(含 PID) | 检错,接收端算出结果与收到值不符即判错 |
有两个细节容易踩坑。第一,标识符 0x3C~0x3F(即 60~63)这四个永远用经典校验和,因为它们是保留给诊断与命令帧的,LIN 2.0 里规定的诊断帧(主机请求 0x3C、从机应答 0x3D)就走这几个 ID;第二,LIN 2.0 引入了节点地址 NAD(Node Address for Diagnostic),诊断帧里第一个数据字节默认被解释为 NAD,用来说明"这条诊断是发给哪个从机的"。做网关诊断功能时,NAD 分配(assign NAD)和条件改地址(conditional change NAD)这两条服务要单独留出调度表项。
标识符场的 6 位 ID 决定"这帧谁发、发几个字节",另外 2 位是奇偶校验位。它们的算法是固定的混合奇偶:
P0 = ID0 ⊕ ID1 ⊕ ID2 ⊕ ID4(奇校验)
P1 = ID1 ⊕ ID3 ⊕ ID4 ⊕ ID5(偶校验)
也就是说,P0 参与的是 ID0、ID1、ID2、ID4 四位,P1 参与的是 ID1、ID3、ID4、ID5 四位,两者取不同的位组合,能覆盖单比特错误。ID4、ID5 同时还兼任"长度控制位",决定数据场字节数,对应关系如下。
| ID5 | ID4 | 数据场字节数 NDATA | 说明 |
|---|---|---|---|
| 0 | 0 | 2 | LIN 1.3 兼容长度 |
| 0 | 1 | 2 | LIN 1.3 兼容长度 |
| 1 | 0 | 4 | LIN 1.3 兼容长度 |
| 1 | 1 | 8 | LIN 1.3 兼容长度 |
校验和分两代。经典校验和只对数据字节求和、取反;增强校验和把 PID 也一起求和,性能略好,用于和 LIN 2.0 从机通信。用哪一代由主机按帧 ID 决定:和 LIN 1.3 从机通信用经典,和 LIN 2.0 从机通信用增强。下面这段 C 代码把 PID 计算和两种校验和都写全了,直接搬到 STM32 工程里就能用。
/* LIN 受保护标识符 PID:6 位 ID + 2 位奇偶校验位 */
uint8_t lin_pid(uint8_t id6)
{
uint8_t id = id6 & 0x3F; /* 只取低 6 位 */
uint8_t p0 = (id ^ (id >> 1) ^ (id >> 2) ^ (id >> 4)) & 0x01; /* 奇校验 */
uint8_t p1 = (~(id >> 1 ^ (id >> 3) ^ (id >> 4) ^ (id >> 5))) & 0x01; /* 偶校验 */
return (uint8_t)(id | (p0 << 6) | (p1 << 7));
}
/* 校验和:字节和按 256 取模并取反,进位回卷到低字节 */
static uint8_t lin_sum_fold(uint16_t sum)
{
sum = (sum & 0xFF) + (sum >> 8); /* 进位加到低字节 */
return (uint8_t)(~sum & 0xFF); /* 取反后即校验和 */
}
/* 经典校验和:只累加数据字节 */
uint8_t lin_checksum_classic(const uint8_t *d, uint8_t n)
{
uint16_t sum = 0;
for (uint8_t i = 0; i < n; i++) sum += d[i];
return lin_sum_fold(sum);
}
/* 增强校验和:把 PID 也纳入求和 */
uint8_t lin_checksum_enhanced(uint8_t pid, const uint8_t *d, uint8_t n)
{
uint16_t sum = pid;
for (uint8_t i = 0; i < n; i++) sum += d[i];
return lin_sum_fold(sum);
}
接收端校验很简单:把数据字节(增强模式再加 PID)按同样规则算一遍,结果与收到的校验和字节逐位相同即通过。数据字节的补码与校验和字节相加应当等于 0xFF,这是手册里给的等价判据,实测用哪个都行,前者更直观。
协议写对了,还得有器件把 MCU 的 3.3V 逻辑电平翻译成 LIN 总线的 12V 单线电平。TJA1020 就是干这个的:它一端接协议控制器(TXD/RXD),一端接 LIN 总线,内部集成了从机终端电阻、波形整型、超时保护和瞬态防护。它最高支持 20kBaud,供电直接取自电池(5~27V),不需要额外的 5V 电源,引脚还和标准 K 线收发器兼容。
它有四种工作模式:睡眠、准备、普通斜率、低斜率。模式切换由 NSLP 引脚和 TXD 电平共同决定,这也决定了网关能不能做到"平时休眠、有事唤醒"的低功耗。
| 模式 | NSLP | TXD | RXD | INH | 典型用途 |
|---|---|---|---|---|---|
| 睡眠 | 0 | 弱下拉 | 悬空 | 悬空 | 整机休眠,靠 LIN 或 NWAKE 唤醒 |
| 准备 | 0 | 远程=弱下拉 / 本地=强下拉 | 低(可作唤醒中断) | 高 | 唤醒后的中间态,先读唤醒源再决定下一步 |
| 普通斜率 | 1 | 高=隐性 / 低=显性 | 跟随总线 | 高 | 常规通信(默认) |
| 低斜率 | 1 | 保持低(或 TXD 故障) | 跟随总线 | 高 | 速率 <10kBaud 时进一步降低电磁辐射 |
关键电气与保护参数一并列出,选型和做防护设计时对着看。
| 参数 | 最小值 | 典型值 | 最大值 | 单位 |
|---|---|---|---|---|
| 引脚 BAT 电压 VBAT | 5 | 12 | 27 | V |
| 睡眠模式电流 IBAT | 1 | 3 | 8 | µA |
| 普通斜率隐性电流 | 100 | 400 | 1000 | µA |
| 普通斜率显性电流 | 1 | 3.5 | 8.0 | mA |
| 引脚 LIN 直流电压 | −27 | — | +40 | V |
| ESD(人体模型 HBM) | −4 | — | +4 | kV |
把 TJA1020 和普通收发器比,它的几个保护是"上车"才有的:发送数据 TXD 显性超时(防止 MCU 死机后一直拉显性把总线锁死)、总线终端对电池和地的短路保护、过热保护,以及总线引脚对 ISO 7637 汽车瞬变的耐受。做工业网关时,这些保护恰好对应现场最常见的两类故障——节点死机锁总线、线束对电源/地短路。睡眠模式下它把内部从机端电阻禁能,即使 LIN 对地短路,电池放电电流也被压到极低,这对电池供电的网关很关键。
LIN 的物理层是 UART/SCI 的 8N1 格式,所以 STM32 直接用一路 USART 就能跑,不需要专用外设。主机要做的三件事是:造同步间隔、发 0x55、发 PID 和数据;从机要做的三件事是:检测同步间隔、用 0x55 校准位时间、匹配 PID 后应答。下面给出主机发一帧的骨架,重点看同步间隔怎么造——它是"≥13 位显性",而 UART 正常发一个 0x00 只有起始位加 8 个 0,不够长,常用做法是临时把波特率调低,用一个 0x00 造出足够长的显性。
/* LIN 主机发一帧:同步间隔 + 0x55 + PID + 数据 + 校验和
* 约定:USART 8N1;同步间隔用低波特率发 0x00 造显性
*/
void lin_master_send(uint8_t id6, const uint8_t *data, uint8_t n)
{
uint32_t normal_baud = 9600; /* LIN 常见 9.6k / 19.2k */
uint32_t break_baud = normal_baud / 4; /* 拉低波特率拉长显性位 */
/* 1) 同步间隔:低波特率发 0x00,得到足够长的显性电平 */
usart_set_baud(break_baud);
usart_write_byte(0x00);
usart_wait_tc();
usart_set_baud(normal_baud);
/* 2) 同步场:固定 0x55 */
usart_write_byte(0x55);
/* 3) 标识符场:6 位 ID 加两位奇偶 */
uint8_t pid = lin_pid(id6);
usart_write_byte(pid);
/* 4) 数据场 + 校验和(按帧 ID 选择经典/增强) */
for (uint8_t i = 0; i < n; i++) usart_write_byte(data[i]);
uint8_t cks = (id6 >= 0x3C) /* 诊断帧永远经典 */
? lin_checksum_classic(data, n)
: lin_checksum_enhanced(pid, data, n);
usart_write_byte(cks);
usart_wait_tc();
}
从机侧的关键是不用晶振也能自同步:收到同步间隔后,从机把第一个显性边沿当作帧起点,再用同步场 0x55 的五个下降沿测出实际位时间,随后按这个位时间来采样标识符和数据。因为 0x55 的位模式是 0101 0101,边沿间隔规整,正好用来做位时间的平均测量,这样即使从机用的是内部 RC 振荡器(精度差几个百分点),也能跟上主机的波特率。这也是 LIN 能把从节点成本压下来的核心原因。
软件上还有三点要守:其一,主机必须按调度表发报文头,从机不能主动发,否则总线冲突;其二,从机应答要在一个帧内完成,超时未应答主机应记录并继续调度下一帧;其三,TXD 引脚平时保持隐性(高),只有发显性位时才拉低,配合 TJA1020 的显性超时保护,避免死机锁总线。
网关的完整通路通常是这样的:末端一串 LIN 从节点(水阀、门锁、传感器)挂在 12V 单线上,网关里的 STM32 作为 LIN 主机按调度表轮询,把读到的状态量放进内部数据区;再往上一层,网关通过 RS-485 或 CAN 与上一级控制器交换,或者直接由 RTU 角色经 NB-IoT / 4G 用 MQTT 上报云平台。LIN 负责"最后一米"的低成本接入,主干网负责"最后一公里"的上云。
这个分工和前面那张选型表是呼应的:LIN 天生只适合小包、低速、短距,所以别指望它扛大数据;它真正的价值是把"十几个开关量小节点"的线束成本降到一根线,让网关的支线接得又多又便宜。做楼宇、农业大棚、储能柜这类场景时,这个组合特别划算——现场节点用 LIN 汇总到网关,网关再统一上云,运维只需要盯网关一个点。
给网关加一条 LIN 子总线,落地逻辑可以浓缩成三步:先判断场景——低速、小包、多节点、成本敏感才用 LIN,其余交给 RS-485 或 CAN;再写对协议——帧就五段,PID 的两位奇偶和两种校验和算法必须按标准实现;最后配好物理层——TJA1020 负责 12V 单线、波形整型、休眠唤醒与短路保护,MCU 侧一个 UART 搞定。
工程上还要记住三件事:诊断帧(ID 0x3C~0x3F)永远用经典校验和、从机靠同步场 0x55 自同步省掉晶振、主机必须按调度表发报文头。把这三点守住,再配上"边缘 LIN 接入 + 网关 MQTT 上云"的通路,一条 12V 单线就能把十几个小节点稳稳接进你的物联网系统。
LIN 不是廉价版的 CAN,而是"最后一米"的低成本单线子网——用 TJA1020 把 12V 单线接到 STM32 的 UART 上,写对 PID 奇偶与校验和,再用调度表把从节点轮询起来,网关的支线接入就既有 CAN 的条理、又只有一根线的成本。
本文基于《LIN 介绍》《LIN 规范 V1.2》《LIN 规范 V2.0》《TJA1020 LIN 收发器手册》《TJA1020 LIN 收发器应用指南》等技术资料整理优化,并结合物联网网关子总线接入与嵌入式开发的工程实践进行了系统化梳理。
沧州艾诺威电子 — 国家高新技术企业,20+项国家专利
嵌入式系统开发 · 物联网方案 · AI智能硬件 · 一站式交付
电话:13930711029 | 邮箱:tech@czinv.com | 24小时内响应