3. LIN协议入门
各位好,我是老周。今天咱们来啃LIN总线协议层的第一块硬骨头——帧结构。说实话,很多工程师做LIN项目好几年,对帧结构的理解还停留在“照着规格书填寄存器”的阶段。我个人觉得,不理解帧结构,调试的时候就像蒙着眼睛开车,出了问题根本不知道往哪查。
LIN总线的帧结构,说白了就是一套固定的“对话格式”。主节点和从节点之间怎么打招呼、怎么传数据、怎么确认消息没出错,全写在这套格式里了。咱们今天就把每个字段掰开揉碎了讲清楚。
核心要点:一个完整的LIN帧由6个场(Field)组成——同步间隔场、同步场、标识符场、数据场、校验和场,以及帧尾的应答间隔。本章重点讲解前5个场。
3.1 帧结构总览
先看整体结构。一个LIN帧就像一列火车,每节车厢都有固定用途。我习惯把帧结构画成下面这张图,方便团队里的新人快速建立全局观。
每个场都有严格的时间要求。LIN总线是单线传输,所有节点都挂在同一条线上。所以时序必须精确,否则就会乱套。下面咱们逐个场来拆解。
3.2 同步间隔场(Sync Break Field)
同步间隔场是帧的“起跑信号”。它的作用很简单——告诉所有从节点:“注意,新的一帧要开始了,准备好接收!”
这个场由主节点发送,持续至少13个位的显性电平(逻辑0)。为什么是13位?因为正常的数据字节最多连续出现9个显性位(比如0x00),13位可以确保从节点不会把数据误判为同步间隔。
实战经验:我在做车身控制模块时,遇到过一个问题——某个从节点偶尔会漏掉帧头。查了半天,发现是同步间隔场的长度只有12位。有些从节点的容限比较窄,12位刚好不够触发同步检测。后来我统一把同步间隔场设为14位,问题就解决了。建议你们也留点余量。
同步间隔场之后,还有一个同步间隔分隔符(Break Delimiter),至少1位的隐性电平(逻辑1)。这个分隔符的作用是把同步间隔场和后面的同步场隔开,防止误判。
3.3 同步场(Sync Field)
同步场的值固定为0x55。为什么是0x55?因为0x55的二进制是01010101,正好是交替的0和1。从节点用这个字节来校准自己的位时钟。
你想想看,LIN总线没有单独的时钟线,所有节点靠自己的晶振来采样总线电平。但晶振总有误差,温度一变、电压一抖,采样点就可能偏移。同步场就是给从节点一个“对表”的机会。
从节点在收到同步场后,会测量第一个下降沿到最后一个下降沿之间的时间,然后算出每个位的宽度,调整自己的采样点。这个过程叫“位同步”。
| 参数 | 值 | 说明 |
|---|---|---|
| 同步场数据 | 0x55 | 固定值,不可更改 |
| 位模式 | 01010101 | LSB先发送,所以线上先看到0 |
| 作用 | 位时钟校准 | 从节点据此调整采样点 |
| 发送方 | 主节点 | 每个帧都必须发送 |
注意:有些低成本的从节点芯片,晶振精度只有±2%。如果主节点的晶振精度也是±1%,累积误差可能达到3%。在20kbps的波特率下,一个位的误差只有几微秒,但累积到一帧的末尾就可能采样错位。所以同步场不是摆设,它真的能救命。
3.4 标识符场(Identifier Field, ID)
标识符场是帧的“身份证”。它告诉所有从节点:“这帧数据是给谁的,或者谁可以发数据。”标识符场共8位,其中6位是ID值,2位是奇偶校验位。
结构如下:
- ID0~ID5:6位标识符,取值范围0~63(共64个ID)
- P0、P1:奇偶校验位,用于检测ID传输是否出错
奇偶校验的计算方式:
P0 = ID0 ⊕ ID1 ⊕ ID2 ⊕ ID3 ⊕ ID4
P1 = ID1 ⊕ ID2 ⊕ ID3 ⊕ ID4 ⊕ ID5
说白了,P0是ID0到ID4的异或,P1是ID1到ID5的异或。从节点收到标识符场后,会重新计算奇偶校验,如果和收到的P0、P1不一致,就说明ID传错了,直接丢弃这帧数据。
ID的分配不是随意的。LIN规范把ID分成了几类:
| ID范围 | 用途 | 数据长度 |
|---|---|---|
| 0x00~0x3B (0~59) | 信号承载帧 | 由LDF文件定义(2/4/8字节) |
| 0x3C (60) | 诊断帧(主节点请求) | 8字节 |
| 0x3D (61) | 诊断帧(从节点响应) | 8字节 |
| 0x3E~0x3F (62~63) | 保留(用于扩展) | — |
我记得有一次做项目,新来的同事把诊断帧的ID写成了0x3E,结果从节点死活不响应。查了半天LDF文件才发现,0x3E是保留ID,从节点根本不认。所以ID分配一定要严格按照规范来。
3.5 数据场(Data Field)
数据场是帧的“ payload ”,承载实际要传输的信号。长度可以是1~8字节,具体由LDF(LIN描述文件)定义。
数据场的发送方取决于通信方向:
- 主节点发送帧:主节点发数据场,从节点接收
- 从节点发送帧:主节点只发帧头(同步间隔+同步场+ID),数据场由被寻址的从节点填充
数据场里的每个字节都是LSB先发送。比如你要发送0x53(二进制01010011),线上先看到的是1(LSB),然后是1、0、0、1、0、1、0(MSB)。
避坑指南:我曾经遇到过一个bug——从节点发送的数据总是错位。用示波器抓波形一看,发现从节点把MSB先发出去了。原来那个从节点的UART配置成了MSB first。LIN总线强制要求LSB first,这一点在选型时一定要确认清楚。
3.6 校验和场(Checksum Field)
校验和场是帧的“安全锁”。它用来检测数据在传输过程中有没有被干扰。LIN总线有两种校验和算法:
| 类型 | 计算范围 | 适用版本 |
|---|---|---|
| 经典校验和 | 数据场所有字节 | LIN 1.x |
| 增强校验和 | 标识符场 + 数据场所有字节 | LIN 2.x |
计算方法是:把所有参与计算的字节相加,只保留低8位,然后取反(按位取反)。如果结果是0x00,就说明数据没问题。
举个例子,假设数据场是0x4A、0x55、0x93:
0x4A + 0x55 + 0x93 = 0x132
保留低8位:0x32
取反:0xCD
所以校验和 = 0xCD
个人习惯:我写代码时,校验和计算一般用查表法,比逐字节累加快很多。尤其是在8位单片机上,查表法能省下几十个指令周期。如果你们用的是Cortex-M系列,那直接用硬件CRC模块也行,但要注意多项式配置要和LIN规范一致。
3.7 帧的完整传输过程
把上面几个场串起来,一个完整的帧传输过程是这样的:
- 主节点拉低总线13+位(同步间隔场)
- 主节点发送1位隐性分隔符
- 主节点发送0x55(同步场)
- 主节点发送标识符场(ID + 奇偶校验)
- 根据ID方向,主节点或从节点发送数据场(1~8字节)
- 发送方计算并发送校验和场(1字节)
- 接收方在帧末尾插入应答间隔(从节点确认)
整个过程都是字节传输,每个字节包含1个起始位(显性)、8个数据位(LSB first)、1个停止位(隐性)。所以一个字节实际占用10个位时间。
重要提醒:帧与帧之间必须保持至少一个位的间隔(帧间空间)。如果连续两帧之间没有间隔,从节点可能会把第二帧的同步间隔场误判为第一帧的数据。我见过有人把帧间空间设成0,结果整个网络乱成一锅粥。至少留1位,建议留2~3位。
好了,帧结构的核心内容就这些。同步间隔场负责“叫醒”总线,同步场负责“对表”,标识符场负责“点名”,数据场负责“传话”,校验和场负责“验货”。每个场各司其职,配合起来才能让LIN总线稳定工作。