第2章:LIN总线物理层
大家好,欢迎来到第二章。上一章我们聊了LIN总线的基本概念,今天咱们深入到底层——物理层。
物理层这东西,说白了就是信号怎么在线上跑。很多工程师觉得协议层更重要,但我个人经验告诉我,物理层出问题,你上层协议写得再好也白搭。我曾经在一个项目中,从节点老是丢帧,查了三天协议栈,最后发现是收发器的电平匹配出了问题。嗯,从那以后,我每次做LIN项目,第一件事就是先把物理层吃透。
2.1 LIN总线电平定义
LIN总线的电平定义,其实挺简单的。它基于12V电池电压,用的是单线传输。
总线有两种状态:
- 显性电平(Dominant):电压接近0V,逻辑值为'0'
- 隐性电平(Recessive):电压接近12V(实际是电池电压),逻辑值为'1'
你想想看,这和CAN总线有点像,但又不完全一样。CAN是差分信号,LIN是单线。单线的好处是成本低,坏处嘛——抗干扰能力弱一些。
具体的电压阈值,我给大家整理了一个表格:
| 参数 | 最小值 | 典型值 | 最大值 |
|---|---|---|---|
| 隐性电平(接收器) | 0.6 × Vbat | Vbat | Vbat |
| 显性电平(接收器) | 0V | 0V | 0.4 × Vbat |
| 隐性电平(发送器) | 0.8 × Vbat | Vbat | Vbat |
| 显性电平(发送器) | 0V | 0V | 0.2 × Vbat |
2.2 LIN总线拓扑结构
LIN总线的拓扑结构,说白了就是一条线把所有节点串起来。我习惯叫它「菊花链」或者「总线型」拓扑。
结构特点:
- 一根主线上挂多个节点
- 每个节点通过收发器连接到总线
- 总线两端各有一个1kΩ的上拉电阻
- 主节点内部还有一个额外的上拉电阻和二极管
为什么会这样设计?因为LIN总线在隐性状态时,总线电压要靠上拉电阻拉到Vbat。如果没这个电阻,总线就浮空了,那信号就没法看了。
LIN总线还有一个限制:节点数量最多16个。为什么?因为总线负载能力有限,节点多了,隐性电平就拉不上去。我建议实际项目中控制在12个以内,留点余量。
2.3 LIN总线节点类型
LIN总线只有两种节点:主节点(Master)和从节点(Slave)。
主节点
主节点是总线的「老大」。它负责:
- 发送同步间隔场(Sync Break)
- 发送同步场(Sync Byte)
- 发送报文ID(PID)
- 管理调度表
- 监控总线状态
说白了,总线上什么时候发数据、发什么数据,都是主节点说了算。从节点只能乖乖等着,被叫到了才能回话。
从节点
从节点就简单多了。它只做两件事:
- 收到主节点的请求后,发送响应数据
- 或者接收主节点发来的数据
从节点不能主动发数据。这一点和CAN总线很不一样。CAN总线是「谁抢到谁发」,LIN总线是「老大让你发你才能发」。
2.4 LIN总线收发器
收发器(Transceiver)是节点和总线之间的「翻译官」。MCU出来的是3.3V或5V的逻辑电平,但总线上跑的是12V电平。收发器负责做这个电平转换。
常见的LIN收发器芯片:
- TJA1020(NXP)
- TJA1021(NXP)
- TJA1027(NXP)
- MCP2003(Microchip)
- MLX80020(Melexis)
我个人比较喜欢用TJA1027,因为它体积小、功耗低,而且兼容LIN 2.x和SAE J2602标准。
收发器的主要功能:
- 发送功能:将MCU的TX信号转换成总线电平
- 接收功能:将总线电平转换成MCU的RX信号
- 总线唤醒:检测总线上的唤醒信号
- 故障保护:过温保护、短路保护等
这里有个关键点:收发器的上拉电阻。主节点的收发器内部通常集成了一个1kΩ的上拉电阻和一个串联二极管。从节点的收发器则没有这个电阻,或者需要外部加。
最后,给大家一个在CANoe中配置LIN收发器的示例:
// 在CANoe的CAPL中配置LIN主节点
variables
{
// 定义LIN通道参数
const int LIN_CHANNEL = 1;
const float LIN_BAUDRATE = 19200; // 标准LIN速率
}
on start
{
// 初始化LIN总线
linSetChannel(LIN_CHANNEL, LIN_BAUDRATE);
// 配置主节点
linSetMaster(LIN_CHANNEL, 1); // 1表示主节点
// 设置收发器模式
linSetTransceiverMode(LIN_CHANNEL, 0); // 0=正常模式
write("LIN总线初始化完成,波特率: %d", LIN_BAUDRATE);
}
// 发送一个LIN帧
void SendLinFrame(long id, byte data[], int dlc)
{
LinFrame frame;
frame.ID = id;
frame.DLC = dlc;
frame.Data = data;
// 发送帧
linTransmit(LIN_CHANNEL, frame);
write("已发送LIN帧,ID: 0x%X", id);
}
嗯,这个代码很简单,但实际项目中,你还要考虑调度表、错误处理、唤醒策略等等。这些我们后面的章节会详细讲。
好了,第二章就到这里。物理层是基础,基础不牢,地动山摇。下一章我们讲LIN总线的数据链路层,看看报文是怎么组织的。