3. LIN总线协议层(一):报文帧结构

各位同学,今天我们正式进入LIN总线的核心——协议层。说白了,就是报文在总线上到底长什么样,怎么发,怎么收。

我记得刚接触LIN时,总觉得它比CAN简单太多,结果第一次调试就栽在同步间隔场的时长上。嗯,细节决定成败,咱们一步步来。

3.1 报文帧的完整结构

一个完整的LIN报文帧,由主机任务发送的帧头,和从机任务发送的响应组成。帧头固定由主机发出,响应可以由主机或从机发出。

整个帧的结构如下:

字段 长度 发送方 说明
同步间隔场 至少13位显性电平 主机 帧起始标志,唤醒总线
同步间隔分隔符 至少1位隐性电平 主机 间隔场与同步场的分界
同步场 1字节 (0x55) 主机 用于从机时钟同步
标识符场 1字节 (PID) 主机 包含ID和奇偶校验位
数据场 1~8字节 从机/主机 实际数据负载
校验和场 1字节 从机/主机 数据校验

关键点:整个帧结构中,只有同步间隔场是显性电平持续,其他所有场都是标准的UART字节格式(1起始位+8数据位+1停止位)。

3.2 各字段详解

3.2.1 同步间隔场

这是LIN帧最特殊的地方。它不是一个字节,而是一段至少13位(bit)的连续显性电平(逻辑0)。

为什么要这么长?因为LIN总线是单线,没有独立的时钟线。从机需要靠这个超长的显性电平来识别帧的开始,并重新同步自己的时钟。

避坑指南:我曾经在一个项目中,把同步间隔场设成了12位。结果大部分从机能正常工作,但有一款从机偶尔会漏帧。查了两天才发现,那款芯片要求至少13位。所以,保守起见,我建议你设成14~15位,留点余量。

同步间隔场之后,紧跟着至少1位的隐性电平(逻辑1),叫间隔分隔符。这个分隔符不能省,否则从机可能把间隔场和后面的同步场连在一起解析。

3.2.2 同步场

同步场固定为字节 0x55,二进制是 01010101。你想想看,这个字节在UART传输时,起始位+数据位+停止位,会形成一串均匀的方波:

起始位 0 + 数据位 10101010 + 停止位 1
→ 电平变化:0→1→0→1→0→1→0→1→0→1

从机就靠这个均匀的跳变沿来测量主机的位时间,然后调整自己的波特率。说白了,就是一次自动校准。

小技巧:调试时,用示波器抓同步场的波形,看跳变沿是否均匀。如果边沿间距不一致,说明主机的时钟抖动太大,或者总线上有干扰。

3.2.3 标识符场

标识符场也叫PID(Protected Identifier),包含6位的帧ID和2位的奇偶校验位。

帧ID范围是0x00~0x3F,共64个。其中:

  • 0x00~0x3B:用于信号传输
  • 0x3C:用于诊断帧(主机请求)
  • 0x3D:用于诊断帧(从机响应)
  • 0x3E~0x3F:保留,未来扩展用

奇偶校验位的计算方式如下:

P0 = ID0 ⊕ ID1 ⊕ ID2 ⊕ ID4
P1 = !(ID1 ⊕ ID3 ⊕ ID4 ⊕ ID5)

其中ID0是ID的最低位,ID5是最高位。P0是偶校验,P1是奇校验再取反。

为什么要有奇偶校验?我遇到过总线被干扰,ID从0x15变成了0x35,结果从机A和从机B同时响应,总线直接乱掉。有了奇偶校验,从机可以拒绝无效的ID,避免这种冲突。

3.2.4 数据场

数据场长度由LDF(LIN描述文件)定义,可以是1~8字节。注意,同一个ID在不同帧中数据长度必须固定,不能变。

数据字节的传输顺序是:先发低字节,再发高字节。每个字节内部,先发最低位(LSB first)。

举个例子,如果数据场要发送 0x1234:

字节0 = 0x34 (先发)
字节1 = 0x12 (后发)

3.2.5 校验和场

校验和场分两种:经典校验和(Classic Checksum)和增强校验和(Enhanced Checksum)。

类型 计算范围 适用版本
经典校验和 仅数据场 LIN 1.x
增强校验和 标识符场 + 数据场 LIN 2.x

计算方法是:将所有参与计算的字节相加,每次相加后如果结果超过0xFF,就减去0xFF(即取和模256,但进位加到最低位)。最后取反。

我个人的习惯:新项目一律用增强校验和。虽然多算一个PID,但安全性高很多。而且LIN 2.x的从机都支持。

3.3 报文帧类型

LIN总线定义了四种帧类型。我刚开始学的时候觉得有点乱,后来发现它们解决的是不同场景下的通信效率问题。

3.3.1 无条件帧

这是最基础的帧类型。主机发帧头,指定ID,对应的从机必须无条件地发送或接收数据。

特点:

  • 一个ID只能分配给一个从机发送
  • 每个调度周期都会发送
  • 用于周期性信号,如车速、转速

3.3.2 事件触发帧

这个设计很巧妙。当多个从机共享同一个ID时,平时只有主机发帧头,从机不响应。只有当某个从机的数据发生变化时,它才响应。

如果多个从机同时响应,就会发生冲突。这时主机需要切换到无条件帧,逐个查询。

实际案例:我在做车门模块时,四个车窗的位置信号用了事件触发帧。平时总线很安静,只有某个车窗动了才发数据。总线负载率从40%降到了5%。

3.3.3 偶发帧

偶发帧和无条件帧很像,但它的发送时机是“有空才发”。如果总线空闲,且数据有更新,就发;否则跳过。

它通常用于非关键信号,比如环境温度、电池电压。这些信号变化慢,没必要每个周期都发。

3.3.4 诊断帧

诊断帧使用固定的ID:

  • 0x3C:主机请求帧(从机接收)
  • 0x3D:从机响应帧(从机发送)

诊断帧的数据场固定为8字节,遵循ISO 14229(UDS)协议。用于读取故障码、写入配置、执行例程等。

注意:诊断帧的优先级高于所有其他帧。在调度表中,诊断帧通常放在最前面。我曾经见过一个项目,诊断帧被普通帧堵住,导致ECU刷写超时。后来把诊断帧的时隙提前,问题就解决了。

3.4 知识结构图

下面这张图总结了本章的核心内容,帮你建立整体认知:

LIN报文帧结构知识体系 LIN报文帧结构知识体系 LIN报文帧 帧头(主机发送) 响应(从机/主机发送) 同步间隔场 同步场 (0x55) 标识符场 (PID) 数据场 (1~8B) 校验和场 报文帧类型 无条件帧 固定ID,周期发送 事件触发帧 变化时响应,可冲突 偶发帧 空闲时发送,非关键 诊断帧 ID 0x3C/0x3D,8字节 核心要点 同步间隔场≥13位显性 | 同步场固定0x55 | PID含6位ID+2位校验 | 数据场1~8字节 | 校验和分经典/增强

3.5 小结

这一章我们拆解了LIN报文帧的每个字段,也梳理了四种帧类型的使用场景。你可能会觉得内容有点多,但别急,这些都是基础中的基础。

我个人建议你先把帧结构记熟,特别是同步间隔场的时长和PID的校验算法。这两个地方是调试时最容易出问题的。