LIN是Local Interconnect Network的缩写,是基于UART/SCI(Universal Asynchronous Receiver-Transmitter/Serial Communication Interface 通用异步收发器/串行通信接口)的低成本串行通信协议。可用于汽车、家电、办公设备等多种领域。
这里不再多说lin的特点。这篇只讲LIN发送。
1、LIN协议层
帧的结构
Frame包含Header和Response两部分。主机负责发送帧头,从机任务接收并对帧头所包含信息进行解析,然后决定是发送应答还是接收应答,还是不作出反应。
帧头包括同步间隔段、同步段以及PID(Protected Identifier,受保护ID)段,应答包括数据段和校验和段,其中值“0”为显性电平(Dominant),值“1”为隐性电平(Recessive)
1.1同步间隔段(Break Field)
同步间隔段由同步间隔(Break)和同步间隔段间隔符(Break Delimiter)构成,同步间隔是至少持续13位的显性电平,Break Field是一个Frame开始的标志。同步间隔段间隔符是至少持续1位的隐性电平。
1.2同步段(Sync Byte Field)
这里有个字节域(Byte Field)概念,Byte Field包括1位Start Bit 显性,+8位data bit + 1位Stop Bit隐性,是一种标准的UART数据传输格式。在lin帧中,数据传输都是先发送LSB(least significan bit),最后发送MSB(Most Significant Bit, 最高有效位),LIN同步以下降沿位判断标志,采用字节0x55(二进制为 01010101b)。
1.3受保护ID段(Protected Identifier Field)
受保护ID段的前6位叫作帧ID(Frame ID),加上两个奇偶校验位后称作为受保护ID.
帧ID的范围在0x00~0x3F之间,共64个,奇偶校验位的校验公式如下
1.4数据段(Data Field)
节点发送的数据位于数据段,包含 1 到 8 个字节(注 1),先发送编号最低的字节 DATA1,编号依次增加
1.5校验和段(Checksum Field)
Checksum Field 是对帧中所传输的内容进行校验
校验和分为标准型校验和(Classic Checksum)及增强型校验和(Enhanced Checksum)
例如:采用标准型校验和,Data1 = 0x4A,Data2 = 0x55,Data3 = 0x93,Data4 = 0xE5,计算方法如下
2、以下为一个小工装的部分代码
代码所用MCU为 stm32,源代码有点乱,只发部分,水平有限仅供参考
/*
*******************************************************************************************
* LIN总线分析头文件
* 文件名称: LIN.h
* 版本号 : V0.9
* 编写人 : Andy.Wu
* 编写日期 :
*******************************************************************************************
*/
#ifndef __LIN_H
#define __LIN_H
/*******************************包含的文件******************************/
#include "main.h"
/*******************************宏定义************************************/
//#define LIN_MODULE
#define BIT(A,B) ((A>>B)&0x01)
/*******************************数据类型定义***************************/
typedef struct
{
#define SYNC_BYTE_FIELD 0x55
#define LIN_BUFF_SIZE 8
#define LIN_PID_KEY LIN_PID_32_0x20
#define LIN_PID_ACC LIN_PID_33_0x21
uint8_t length;
uint8_t FrameSync;
uint8_t FrameId;
uint8_t FrameData[LIN_BUFF_SIZE];
}LinSendTypeDef;
/*******************************函数声明***************************/
void LIN_SendDataFrame(UART_HandleTypeDef *huart, LinSendTypeDef *LinMsg, uint8_t state);
#endif
LIN.c
/*
*******************************************************************************************
* LIN总线分析文件
* 文件名称: LIN.c
* 版本号 : V0.9
* 编写人 : Andy.Wu
* 编写日期 :
*******************************************************************************************
*/
uint8_t LINCheckSumEn(uint8_t PID, uint8_t *buf, uint8_t lens)
{
uint8_t i, ckm = 0;
uint16_t chm1 = PID;
for(i = 0; i < lens; i++)
{
chm1 += *(buf++);
}
ckm = chm1 / 256;
ckm = ckm + chm1 % 256;
ckm = 0xFF - ckm;
return ckm;
}
//计算校验位
uint8_t LINCalcParity(uint8_t id)
{
uint8_t parity, p0,p1;
parity=id;
p0=(BIT(parity,0)^BIT(parity,1)^BIT(parity,2)^BIT(parity,4))<<6; //偶校验位
p1=(!(BIT(parity,1)^BIT(parity,3)^BIT(parity,4)^BIT(parity,5)))<<7; //奇校验位
parity|=(p0|p1);
return parity;
}
//******************************************************************
// 函数: void LIN_SendSnycSeg(UART_HandleTypeDef *huart, LinSendTypeDef *LinMsg)
// 功能: 发送同步间隔段
// 输入: huart:UART_HandleTypeDef结构的指针; LinMsg:LinSendTypeDef结构体的指针
// 输出: 无
//******************************************************************
void LIN_SendSnycSeg(UART_HandleTypeDef *huart, LinSendTypeDef *LinMsg)
{
while(HAL_UART_Transmit_IT(huart, &(LinMsg->FrameSync), 1) != HAL_OK);
}
//******************************************************************
// 函数: void LIN_SendPID(UART_HandleTypeDef *huart, LinSendTypeDef *LinMsg)
// 功能: 发送PID
// 输入: huart:UART_HandleTypeDef结构的指针; LinMsg:LinSendTypeDef结构体的指针
// 输出: 无
//******************************************************************
void LIN_SendPID(UART_HandleTypeDef *huart, LinSendTypeDef *LinMsg)
{
uint8_t parity;
parity = LINCalcParity(LinMsg->FrameId);
while(HAL_UART_Transmit_IT(huart, &parity, 1) != HAL_OK);
//debug_printf("parity = 0x%x, ID = 0x%x\r\n", parity, LinMsg->FrameId);
}
//******************************************************************
// 函数: void LIN_SendData(UART_HandleTypeDef *huart, LinSendTypeDef *LinMsg)
// 功能: 发送数据内容
// 输入: huart:UART_HandleTypeDef结构的指针; LinMsg:LinSendTypeDef结构体的指针
// 输出: 无
//******************************************************************
void LIN_SendData(UART_HandleTypeDef *huart, LinSendTypeDef *LinMsg) //uint8_t *buff, uint8_t len
{
while(HAL_UART_Transmit_IT(huart, LinMsg->FrameData, LinMsg->length) != HAL_OK);
}
//******************************************************************
// 函数: void LIN_SendDataFrame(UART_HandleTypeDef *huart, LinSendTypeDef *LinMsg, uint8_t state)
// 功能: 发送数据内容校验和
// 输入: huart:UART_HandleTypeDef结构的指针; LinMsg:LinSendTypeDef结构体的指针;state :状态
// 输出: 无
//******************************************************************
void LIN_SendDataFrame(UART_HandleTypeDef *huart, LinSendTypeDef *LinMsg, uint8_t state)
{
uint8_t ckm;
//uint8_t test[8] = {0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08};
if(state==LIN_KEY_ON)
{
LinMsg->FrameId = LIN_PID_KEY;
LinMsg->FrameSync = SYNC_BYTE_FIELD;
LinMsg->length = 4;
LinMsg->FrameData[0] = 0;
LinMsg->FrameData[1] = 0;
LinMsg->FrameData[2] = 0;
LinMsg->FrameData[3] = 0x05;
}
else if(state==LIN_KEY_OFF)
{
LinMsg->FrameId = LIN_PID_KEY;
LinMsg->FrameSync = SYNC_BYTE_FIELD;
LinMsg->length = 4;
LinMsg->FrameData[0] = 0;
LinMsg->FrameData[1] = 0;
LinMsg->FrameData[2] = 0;
LinMsg->FrameData[3] = 0x85;
}
while(HAL_LIN_SendBreak(huart) != HAL_OK); //同步间隔段
LIN_SendSnycSeg(huart, LinMsg); //同步段
LIN_SendPID(huart, LinMsg); //受保护ID
//LIN_SendHead(huart, LinMsg);
LIN_SendData(huart, LinMsg);
ckm = LINCheckSumEn(LINCalcParity(LinMsg->FrameId), LinMsg->FrameData, LinMsg->length); // Send checksum field
while(HAL_UART_Transmit_IT(huart, &ckm, 1) != HAL_OK);
}
参考文献:《lin入门书》 RENESAS