2、LIN总线协议基础(上):物理层与数据链路层

大家好,我是老李。做嵌入式这些年,从CAN到LIN,从12V到48V,摸过的总线协议少说也有七八种。但说实话,LIN总线是我个人觉得最「亲切」的一个——它简单、便宜、够用。今天咱们就来聊聊LIN总线的物理层和数据链路层,把那些最基础、也最容易踩坑的地方说清楚。

2.1 LIN总线物理层

2.1.1 电压与电平

LIN总线的物理层,说白了就是一根线。对,就一根。它基于单线12V总线,参考地是GND。电平定义是这样的:

  • 隐性电平(Recessive):总线电压接近12V(通常为Vbat),对应逻辑"1"
  • 显性电平(Dominant):总线电压接近0V(通常低于1V),对应逻辑"0"

嗯,这里要注意:LIN的隐性电平是12V,显性电平是0V。这和CAN刚好相反——CAN的隐性电平是2.5V,显性电平是1.5V。我第一次做LIN项目时,就因为这个搞反了,调试了一整天。

关键参数:

  • 总线电压范围:8V ~ 18V(车规级)
  • 隐性电平:≥ 0.8 × Vbat
  • 显性电平:≤ 0.2 × Vbat
  • 接收阈值:通常为Vbat的50%左右

2.1.2 波特率

LIN总线的波特率,标准定义是1kbps ~ 20kbps。为什么这么慢?你想想看,LIN总线设计之初就是为了替代那些简单的开关、传感器、小电机控制——比如车窗升降、后视镜调节、座椅位置记忆。这些应用对实时性要求不高,慢一点没关系,关键是便宜。

实际项目中,最常用的波特率是:

波特率 典型应用 备注
9.6 kbps 低速传感器 兼容性好,容错高
10.4 kbps 通用控制 最常用,我90%的项目都用这个
19.2 kbps 快速响应 对时钟精度要求高
20.0 kbps 极限速率 不建议,容错空间小

个人经验:我建议新手项目先用9.6 kbps。为什么?因为LIN总线的时钟源通常是RC振荡器,精度只有±15%左右。波特率越低,时钟误差的容忍度越大。我曾经有个项目,从机芯片的RC振荡器漂了20%,9.6 kbps还能跑,换成19.2 kbps直接废了。

2.1.3 拓扑结构

LIN总线采用单主多从的拓扑结构。什么意思?就是一条总线上,只有一个主机节点(Master),其他都是从机节点(Slave)。主机负责调度、发送帧头;从机只能响应,不能主动发数据。

典型的拓扑是这样的:

+--------+     +--------+     +--------+     +--------+
| Master |-----| Slave1 |-----| Slave2 |-----| Slave3 |
+--------+     +--------+     +--------+     +--------+
     |
   12V + GND

嗯,这里有个关键点:LIN总线的节点数最多16个(包括主机)。为什么是16?因为LIN帧的ID字段只有6位,其中0x3C和0x3D保留用于诊断,所以实际可用的ID是0x00 ~ 0x3B,共60个。但物理层上,受限于总线负载和驱动能力,标准建议不超过16个节点。

避坑指南:我曾经在一个项目中,硬接了18个从机。结果呢?总线波形严重畸变,隐性电平拉不到12V,显性电平也降不到0V。最后只能拆成两个LIN网络,加了一个网关。所以,别贪多,16个是上限,10个以内最稳。

2.2 LIN总线数据链路层

2.2.1 帧结构

LIN总线的帧结构,说白了就是「谁说话、说什么、说得对不对」。一个完整的LIN帧由两部分组成:帧头(Header)帧响应(Response)

帧结构长这样:

+-------------------+-------------------+
|     帧头          |     帧响应        |
| (由主机发送)      | (由从机发送)      |
+-------------------+-------------------+
| 同步间隔场        | 数据场            |
| 同步场            | 校验和场          |
| 标识符场          |                   |
+-------------------+-------------------+

整个帧的长度,取决于数据场的字节数。标准LIN帧的数据场是1~8字节。为什么是8?和CAN一样?其实不是巧合,LIN在设计时确实参考了CAN的帧结构,但简化了很多。

2.2.2 帧头(Header)

帧头由主机节点发送,包含三个部分:

  1. 同步间隔场(Synch Break):至少13位的显性电平(逻辑0),用于唤醒总线和同步时钟。这是LIN帧的起始标志。
  2. 同步场(Synch Field):一个固定的字节0x55(二进制01010101),用于从机校准波特率。
  3. 标识符场(Identifier Field):6位ID + 2位奇偶校验,共1字节。ID范围0x00 ~ 0x3F,其中0x3C和0x3D保留。

你可能会问:为什么同步间隔场要至少13位?因为LIN总线上可能同时有多个节点在发送,13位的显性电平足够长,确保所有节点都能检测到帧起始。我见过有些芯片的同步间隔检测阈值是11位,但标准要求13位,别省那2位。

标识符的奇偶校验算法:

// ID: 6位标识符 (bit0 ~ bit5)
// P0: 偶校验位 (bit6)
// P1: 奇校验位 (bit7)

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

嗯,这个算法我建议你直接抄,别自己写。我当年手算过一次,算错了,调试了三天才发现是校验位写反了。

2.2.3 帧响应(Response)

帧响应由从机节点发送,包含两个部分:

  1. 数据场(Data Field):1~8字节,具体长度由LDF文件(LIN描述文件)定义。数据场的内容取决于信号定义。
  2. 校验和场(Checksum Field):1字节,用于校验数据完整性。LIN 1.x使用经典校验和,LIN 2.x使用增强校验和。

校验和的计算方式:

// 经典校验和(LIN 1.x):只校验数据场
checksum = 0;
for (i = 0; i < data_length; i++) {
    checksum += data[i];
    if (checksum > 0xFF) {
        checksum -= 0xFF;
    }
}
checksum = 0xFF - checksum;

// 增强校验和(LIN 2.x):校验数据场 + 标识符
checksum = 0;
checksum += identifier;  // 注意:只加ID,不加奇偶位
for (i = 0; i < data_length; i++) {
    checksum += data[i];
    if (checksum > 0xFF) {
        checksum -= 0xFF;
    }
}
checksum = 0xFF - checksum;

个人建议:现在的新项目基本都是LIN 2.x了,直接用增强校验和。但如果你要兼容老设备,得在LDF里声明校验和类型。我有个项目就是没注意这个,主机用增强校验和,从机用经典校验和,结果通信一直报错。后来查了三天LDF才发现。

2.3 总结与避坑

好了,今天的内容就这些。我帮你总结几个关键点:

  • 物理层:单线12V,隐性=12V,显性=0V,波特率别超过20kbps,节点别超过16个
  • 帧结构:帧头(主机发)+ 帧响应(从机发),数据场1~8字节
  • 同步间隔:至少13位显性,别省
  • 校验和:LIN 2.x用增强校验和,别忘了加标识符

下一章,咱们接着聊LIN总线的调度机制和LDF文件。到时候我会拿一个实际项目中的LDF文件出来,一行一行给你拆解。嗯,那才是真正干活的东西。

最后提醒一句:LIN总线看着简单,但坑不少。尤其是时钟精度、同步间隔长度、校验和类型这三个地方,我几乎每个项目都踩过。你如果刚开始做LIN,建议先把这三个点吃透,能省不少调试时间。


公众号:蓝海资料掘金营,微信deep3321