4、LIN总线数据场与校验:数据场长度、数据场格式、校验和计算(经典校验与增强校验)、校验和错误处理

好,咱们接着聊。前面几章我们把帧头、同步场、标识符都讲透了。这一章,我重点说说数据场和校验场。

说实话,数据场和校验场是LIN总线里最容易出问题的地方。我见过太多工程师,明明协议栈写得没问题,但就是通信不稳定。最后查下来,十有八九是校验和算错了,或者数据场长度没对齐。

嗯,咱们一个一个来拆解。

4.1 数据场长度:不是你想发多少就发多少

LIN总线的数据场,长度是固定的——1到8个字节。为什么是8个?说白了,LIN总线就是CAN总线的低成本替代方案。CAN报文数据场最长8字节,LIN也沿用了这个设计。

但这里有个坑:数据场长度必须在调度表中显式声明。你不能像CAN那样,靠DLC(数据长度码)来动态决定。LIN的帧头里没有长度信息,长度是事先约定好的。

我个人习惯,在LDF(LIN描述文件)里这样定义:

Signal: EngineSpeed : 16, 0, 1, Master
Signal: CoolantTemp : 8, 16, 1, Slave

Frame: Frame_1 : 0x31, Master, 4
{
    EngineSpeed, 0;
    CoolantTemp, 16;
}

你看,Frame_1后面那个4,就是数据场长度。这里我声明了4个字节,但实际只用了3个字节(16位+8位)。多出来的1个字节怎么办?填充0。这是规范允许的。

注意: 数据场长度不能小于信号实际占用的位数。比如你有一个16位的信号,数据场长度至少是2字节。我曾经见过有人把数据场长度设为1,结果高8位数据全丢了,排查了一整天。

4.2 数据场格式:LSB先行,别搞反了

LIN总线的数据场,遵循小端序(Little-Endian)。什么意思?就是低字节在前,高字节在后。

举个例子。假设你要发送一个16位的信号,数值是0x1234。在LIN总线上,先发0x34,再发0x12

我刚开始做LIN开发时,就犯过这个错。当时用示波器抓波形,怎么看怎么不对。后来才发现,我把高低字节搞反了。嗯,从那以后,我每次写代码都会在注释里标清楚字节序。

数据场的位排列是这样的:

字节序号 Bit 7 Bit 6 Bit 5 Bit 4 Bit 3 Bit 2 Bit 1 Bit 0
Byte 0 数据位(LSB)
Byte 1 数据位
... ...
Byte N 数据位(MSB)

你想想看,如果信号跨字节了怎么办?比如一个12位的信号,从Byte 0的bit 4开始。那它就会占用Byte 0的bit 4-7,以及Byte 1的bit 0-7。嗯,这种跨字节的情况在LIN里很常见,处理起来要格外小心。

4.3 校验和计算:经典校验 vs 增强校验

校验和是LIN总线保证数据完整性的最后一道防线。LIN 2.0之后,引入了两种校验方式:经典校验(Classic Checksum)增强校验(Enhanced Checksum)

4.3.1 经典校验

经典校验只对数据场做校验。说白了,就是把数据场的所有字节加起来,取反,然后发出去。

计算公式:

Checksum = ~(DataByte0 + DataByte1 + ... + DataByteN) & 0xFF

举个例子。假设数据场是0x55, 0xAA, 0x01

  1. 求和:0x55 + 0xAA + 0x01 = 0x100
  2. 取低8位:0x00
  3. 取反:0xFF

所以校验和就是0xFF

小技巧: 求和时如果超过0xFF,只保留低8位。这是很多新手容易忽略的地方。

4.3.2 增强校验

增强校验在经典校验的基础上,把标识符(Protected ID)也加了进来。为什么要这么做?因为经典校验只保护数据,不保护标识符。如果标识符在传输过程中被干扰,从节点可能收到错误的数据却浑然不知。

计算公式:

Checksum = ~(PID + DataByte0 + DataByte1 + ... + DataByteN) & 0xFF

注意,这里的PID是保护标识符,不是原始ID。保护标识符 = 原始ID + 奇偶校验位。这个我们在上一章讲过。

我个人建议,新项目一律用增强校验。为什么?安全性更高。LIN 2.0之后的规范也推荐使用增强校验。只有为了兼容老设备,才不得已用经典校验。

4.4 校验和错误处理:别让错误数据跑出去

校验和算完了,发出去,接收端怎么处理?

接收节点收到一帧数据后,会做两件事:

  1. 用同样的算法重新计算校验和
  2. 把计算结果和收到的校验和做比较

如果一致,说明数据没问题。如果不一致,说明传输过程中发生了错误。

那错误怎么处理?LIN规范里写得很清楚:

  • 从节点:丢弃该帧数据,不更新信号值。等待下一帧。
  • 主节点:同样丢弃数据。但主节点可以决定是否重发。

这里有个细节:校验和错误不会触发任何错误帧。LIN总线没有CAN那样的错误帧机制。校验和错了,就是默默地丢掉。所以,如果你的应用对数据可靠性要求很高,建议在应用层加一个超时重传机制。

避坑指南: 我曾经在一个项目中,发现某个从节点偶尔会收到错误数据。排查了很久,最后发现是校验和计算代码里,把PID加了两遍。嗯,这种低级错误,写代码时一定要仔细。

另外,校验和错误率其实很低。LIN总线的物理层虽然简单,但抗干扰能力并不差。我测过很多项目,在正常环境下,校验和错误率基本在10^-6以下。只有在强电磁干扰环境下,才会明显升高。

好了,数据场和校验场就讲到这里。下一章,我们聊聊LIN总线的状态管理与休眠唤醒。这部分内容,嗯,也挺有意思的。