第2章:LIN协议规范:版本、帧结构、帧类型与调度表
好,咱们接着聊。上一章我大概说了LIN总线是个什么东西,这一章咱们得把协议本身掰开揉碎了讲清楚。说实话,很多做灯光驱动的工程师,对LIN协议的理解就停留在“发个数据灯就亮了”的层面。但真要调试起来,尤其是遇到多节点冲突或者响应超时,不懂协议细节真的会抓瞎。
我个人习惯,学任何总线协议,先抓住三个核心:版本、帧结构、调度方式。LIN也不例外。咱们一个一个来。
2.1 LIN协议版本:别用错了版本
LIN协议从1.0发展到现在的2.2A,中间还有1.3、2.0、2.1。你可能会问:“这么多版本,我该用哪个?”
嗯,这里要注意:目前主流车厂基本都要求LIN 2.1或2.2A。1.x版本太老了,很多新特性不支持,比如诊断功能和配置能力。我在一个项目中就遇到过,客户给的LDF文件是2.0的,但我们的芯片只支持1.3,结果通信就是起不来。后来一查,是帧头校验方式不一样。
所以我的建议是:新项目直接上2.2A。向下兼容性最好,而且工具链支持也最全。
| 版本 | 主要变化 | 建议 |
|---|---|---|
| LIN 1.3 | 基础帧结构,无诊断 | 不推荐新设计 |
| LIN 2.0 | 引入诊断帧、配置 | 部分老项目在用 |
| LIN 2.1 | 完善诊断,增加偶发帧 | 可以接受 |
| LIN 2.2A | 最稳定,工具支持好 | 强烈推荐 |
2.2 LIN帧结构:一帧数据长什么样?
说白了,LIN帧就是一串字节流。但它的结构很讲究。我刚开始学的时候,总觉得它和UART差不多,其实差远了。
一个完整的LIN帧由两部分组成:主机发送的帧头 和 从机发送的响应。帧头固定由主机发出,响应则根据帧ID,由对应的从节点填充。
帧头包含:
- 同步间隔场(Break):至少13个显性位。这是LIN特有的,用来让从节点识别一帧的开始。我曾经调试时发现从节点不响应,用示波器一看,主机发的Break只有11位,从节点根本不认。
- 同步场(Sync):固定为0x55。用来校准从节点的波特率。说白了就是让从节点知道“主机的时钟是这样的,你跟着我走”。
- 标识符场(PID):6位ID + 2位校验。这里有个坑:PID不是直接等于帧ID,而是经过奇偶校验的。你写代码时千万别直接发ID,要算一下校验位。
响应部分包含:
- 数据场:1到8个字节。灯光驱动一般用2-4个字节就够了,比如控制亮度、颜色、开关状态。
- 校验和场(Checksum):经典版校验和(LIN 1.x)或增强版校验和(LIN 2.x)。区别在于是否包含PID。我建议统一用增强版,兼容性更好。
// 一个典型的LIN帧结构(2.2A)
// 帧头:Break + Sync(0x55) + PID
// 响应:Data[0..7] + Checksum
// 举个例子:控制灯光亮度
// PID = 0x31 (ID=0x11, 校验位=01)
// Data[0] = 0x64 // 亮度100%
// Data[1] = 0x01 // 开关状态:开
// Checksum = 计算(Data[0] + Data[1] + PID) // 增强版
2.3 帧类型:四种帧,各有各的用处
LIN协议定义了四种帧类型。你想想看,如果只有一种帧,那多节点通信的效率会很低。所以LIN设计了不同场景下的帧。
无条件帧(Unconditional Frame)
这是最常用的帧。主机发出帧头,对应的从节点必须无条件地发送响应。说白了就是“点到点”的通信。灯光驱动里,主机查询某个灯的状态,用的就是无条件帧。
事件触发帧(Event Triggered Frame)
这个有点意思。它允许多个从节点共享同一个帧ID。平时总线是安静的,只有当某个从节点有事件发生时,它才在对应的时隙里发送数据。如果多个节点同时发,就会冲突,然后主机再通过无条件帧逐个查询。
我在做氛围灯项目时用过这个。多个灯条的状态变化不频繁,用事件触发帧可以大大减少总线负载。但要注意:冲突处理逻辑一定要写对,否则数据会乱。
偶发帧(Sporadic Frame)
这个帧是主机专用的。主机可以在一个调度槽里,选择发送多个偶发帧中的一个。说白了就是“看心情发”。如果某个偶发帧的数据没有更新,主机就跳过它,发下一个。这个在灯光动态效果切换时很有用。
诊断帧(Diagnostic Frame)
诊断帧有固定的ID:0x3C和0x3D。一个用于主机请求,一个用于从机响应。它遵循ISO 14229(UDS)协议。在灯光驱动里,诊断帧用来做配置、读取故障码、在线升级等。
| 帧类型 | 谁发响应 | 典型用途 |
|---|---|---|
| 无条件帧 | 单个从节点 | 读取灯状态、设置亮度 |
| 事件触发帧 | 多个从节点(可能冲突) | 状态变化通知 |
| 偶发帧 | 主机自己 | 动态效果切换 |
| 诊断帧 | 从节点(按UDS协议) | 配置、故障诊断、升级 |
2.4 帧ID与调度表:谁在什么时候说话?
LIN总线是主从结构,所有通信都由主机发起。从节点不能主动说话,只能等主机叫它。那主机怎么知道什么时候叫哪个节点?靠的就是调度表(Schedule Table)。
调度表说白了就是一个时间表。主机按照这个表,循环发送各个帧的帧头。每个帧头对应一个帧ID,从节点收到和自己匹配的ID,就发送响应。
举个例子,一个灯光系统的调度表可能是这样的:
// 调度表示例(简化)
// 时间槽 1: 发送帧ID 0x10(无条件帧,查询左灯状态)
// 时间槽 2: 发送帧ID 0x11(无条件帧,查询右灯状态)
// 时间槽 3: 发送帧ID 0x20(事件触发帧,检测是否有灯状态变化)
// 时间槽 4: 发送帧ID 0x3C(诊断帧,如果有诊断请求)
// 然后循环
帧ID的范围是0x00到0x3F,共64个。其中0x3C和0x3D保留给诊断,0x3E和0x3F保留给用户自定义。实际可用的ID是60个。对于灯光驱动来说,一般一个节点占用1-2个ID就够了。
嗯,说到调度表,还有一个概念叫帧时隙(Frame Slot)。每个帧在调度表中占用一个固定的时间槽。这个时间槽的长度要足够容纳整个帧(帧头+响应+间隔)。如果时间槽太短,帧还没发完下一个就来了,就会出错。
我个人习惯,计算时间槽时留出20%的余量。尤其是波特率有偏差时,这个余量能救命。
小结
这一章我们聊了LIN协议的版本选择、帧结构、四种帧类型,以及调度表和帧ID的关系。说白了,LIN协议并不复杂,但细节很多。你只要记住:版本选2.2A,帧结构搞清楚Break和校验和,帧类型按场景选,调度表是主机的“指挥棒”。
下一章,咱们会深入灯光驱动的具体实现,包括如何配置从节点的LDF文件,以及如何用代码解析帧头。到时候我会分享一些我踩过的坑,保证实用。