一个LIN信息帧包含帧头(Header)和响应(Response)。
帧头(Header) 包含:间隔域(Break Field),同步域(Sync Field)和受保护的标识符域(Protected Indentifier Field);
响应(Response) 包含:数据域(Data1~N),校验和(Checksum)。
字节之间有字节间隔(InterByteSpace),在头信息和响应之间有一个响应间隔(ResponseSpace),这两个间隔的最小值为0。
除起始域(Header)与响应间隔(ResponseSpace),其他部分都是以字节为单位传送,每个字节都有自己的格式,称之为字节域(ByteField)。
在LIN 模式下,根据LIN的标准,每个字节域的开始都是由一个值为0的起始位(显性的),后面跟随8个位的数据(LSB 优先),最后以一个值为1的停止位(隐性的)结束。
字节域(Byte Field):1位起始位(Start Bit,显性,值为0)+ 8位数据位 + 1位停止位(Stop Bit,隐性,值为1),是一种标准UART 数据传输格式。
同步域(Sync Byte Field),是固定值(0x55),二进制是01010101,由于是先发送低位,也就是先发送1。
受保护ID域(Protected Identifier Field),前6位为帧ID(Frame ID),最高两位是奇偶校验。
帧ID(Frame ID),前6位为帧ID,范围在0x00~0x3F之间。帧ID标识了帧的类别。从机任务对于帧头作出的反应(接收/发送/忽略应答部分)都是依据帧ID判断的。如果帧ID传输错误,将会导致信号无法正确到达目的地。因此引入奇偶校验位。
奇偶校验(Parity Check),最高两位是奇偶校验,其中ID6 是ID0、ID1、ID2、ID4 的奇校验,ID7 是ID1、ID3、ID4、ID5 的偶校验。
校验公式如下,其中“⊕”代表“异或”运算,“¬”代表“取非”运算。
P0 = ID0 ⊕ID1 ⊕ID2 ⊕ID4
P1 = ¬ (ID1 ⊕ID3 ⊕ID4 ⊕ID5)
(由公式可以看出,PID不会出现全0或全1的情况,因此,如果从机节点收到了“0xFF”或“0x00”,可判断为传输错误。)
下列是NUC029xANSeriesBSP固件库中,ID域奇偶校验位求值函数的代码:
/*---------------------------------------------------------------------------------------------------------*/
/* Compute Parity Value */
/*---------------------------------------------------------------------------------------------------------*/
// 获取ID域的奇偶校验值(指定ID),返回ID域值
// 计算指定ID的奇偶校验位,并返回ID域值
uint8_t GetParityValue(uint32_t u32id)
{
uint32_t u32Res = 0, ID[6], p_Bit[2] , mask = 0;
// 将ID存入ID[0:5]
for(mask = 0; mask < 6; mask++)
ID[mask] = (u32id & (1 << mask)) >> mask;
// 获取ID域奇偶检验位值
// ID6(p_Bit[0])是ID0、ID1、ID2、ID4的奇校验
// ID7(p_Bit[1])是ID1、ID3、ID4、ID5的偶校验
p_Bit[0] = (ID[0] + ID[1] + ID[2] + ID[4]) % 2;
p_Bit[1] = (!((ID[1] + ID[3] + ID[4] + ID[5]) % 2));
// 得到ID域值u32Res(前6位是ID,高两位是奇偶校验位)
u32Res = u32id + (p_Bit[0] << 6) + (p_Bit[1] << 7);
// 返回ID域值
return u32Res;
}
【校验和求值示例】
ID域对0x12的ID求校验位
xx01 0010 (0x12)
ID6 = (ID[0] + ID[1] + ID[2] + ID[4]) % 2; = 0
ID7 = (!((ID[1] + ID[3] + ID[4] + ID[5]) % 2)); = 1
得到ID域为
1001 0010 (0x92)
LIN协议中,一帧信息的响应域由数据域和校验码域两部分构成。
每个数据域的开始都是由一个值为0的起始位 (显性的),后面跟随8个位的数据(LSB 优先),最后以一个值为1的停止位(隐性的)结束。
校验和(Checksum),是所有数据(可能包含ID域)带进位的8位和(即所有数据累加,大于等于256时,减去255)。
校验和计算方法:将校验对象的各字节作带进位二进制加法(每当结果大于等于256 时就减去255),并将所得最终的和逐位取反,以该结果作为要发送的校验和。
接收方根据校验和类型,对接收数据作相同的带进位二进制加法,最终的和不取反,并将该和与接收到的校验和作加法,如果结果为0xFF,则校验和无误。这在一定程度上保证了数据传输的正确性。
下列是NUC029xANSeriesBSP固件库中,校验和求值函数的代码:
/* CheckSum Method */ // 校验模式
#define MODE_CLASSIC 2 // 经典模式-不包含ID的校验位
#define MODE_ENHANCED 1 // 增强模式-包含ID的校验位
/*---------------------------------------------------------------------------------------------------------*/
/* Compute CheckSum Value , MODE_CLASSIC:(Not Include ID) MODE_ENHANCED:(Include ID) */
/*---------------------------------------------------------------------------------------------------------*/
// 获取校验和的值(发送数组,选择校验模式)
// 计算检验和:所有数据累加,大于等于256时,减去255;并将所得最终的和逐位取反
uint32_t GetCheckSumValue(uint8_t *pu8Buf, uint32_t u32ModeSel)
{
uint32_t i, CheckSum = 0;
// 计算检验和
// 所有数据累加,大于等于256时,减去255
// pu8Buf=g_u8SendData,[0]为同步域[1]为ID域
// 包含ID的校验位,从[1]位ID域开始求和
// 不包含ID的检验位,从[2]位响应域开始求和
for(i = u32ModeSel; i <= 9; i++)
{
CheckSum += pu8Buf[i];
if(CheckSum >= 256)
CheckSum -= 255;
}
// 并将所得最终的和逐位取反
return (255 - CheckSum);
}
【校验和求值示例】
数据1域0x1:g_u8SendData[3] = 01
数据2域0x2:g_u8SendData[4] = 02
数据3域0x3:g_u8SendData[5] = 03
数据4域0x4:g_u8SendData[6] = 04
数据5域0x5:g_u8SendData[7] = 05
数据6域0x6:g_u8SendData[8] = 06
数据7域0x7:g_u8SendData[9] = 07
数据8域0x8:g_u8SendData[10] = 08
校验和含ID:g_u8SendData[11] = 49
求校验和(不含ID)
01+02+03+04+05+06+07+08=0x24
~0x24=0xDB
得g_u8SendData[11] = DB
求校验和(含ID)
92+01+02+03+04+05+06+07+08=1011 0110=0xB6
~0xB6=0100 1001=0x49
得g_u8SendData[11] = 49
LIN总线发送(TX)编程流程如下:
下列是NUC029xANSeriesBSP固件库中,LIN总线主机发送LIN帧头、响应域的代码:
/*---------------------------------------------------------------------------------------------------------*/
/* Global variables */
/*---------------------------------------------------------------------------------------------------------*/
// 发送数组[]={同步域,ID域,数据位,校验和}
uint8_t g_u8SendData[12] = {0}; // 发送数组
volatile int32_t g_i32pointer = 0; // 发送数组下标
/*---------------------------------------------------------------------------------------------------------*/
/* Master send header and response */
/*---------------------------------------------------------------------------------------------------------*/
// LIN主机测试(指定ID,选择模式)
// 发送LIN帧头、发送响应域
void LIN_MasterTest(uint32_t u32id, uint32_t u32ModeSel)
{
// 发送数组
uint32_t testPattern[8] = {0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8};
/*Send ID=0x35 Header and Response TestPatten*/
// 发送LIN帧头(指定ID域u32id)
// 发送响应域(检验模式)
LIN_SendHeader(u32id);
LIN_SendResponse(u32ModeSel, &testPattern[0]);
}
/*---------------------------------------------------------------------------------------------------------*/
/* Send LIN Header Field */
/*---------------------------------------------------------------------------------------------------------*/
// 发送LIN帧头(指定ID域)
// 设定LIN模式Break域长度\
// 发送同步域0x55\
// 发送ID域(通过GetParityValue获取指定ID的ID域)
void LIN_SendHeader(uint32_t u32id)
{
g_i32pointer = 0 ; // g_u8SendData发送数组
/* Set LIN operation mode, Tx mode and break field length is 13 bits */
// Uart1设置LIN TX Break模式使能,设定13位break域
UART_SelectLINMode(UART1, UART_ALT_CSR_LIN_TX_EN_Msk, 11);
// 同步域0x55存入发送数组
// 获取u32id的ID域的值存入发送数组
// 通过UART1将发送数组发送出去,两个八位字节
g_u8SendData[g_i32pointer++] = 0x55 ; // SYNC Field
g_u8SendData[g_i32pointer++] = GetParityValue(u32id); // ID+Parity Field
UART_Write(UART1, g_u8SendData, 2);
}
/*---------------------------------------------------------------------------------------------------------*/
/* Send LIN Response Field */
/*---------------------------------------------------------------------------------------------------------*/
// 发送响应域(选择校验模式,发送数组)
// 将数据位、校验和存入发送数组,通过UART1发送
void LIN_SendResponse(int32_t checkSumOption, uint32_t *pu32TxBuf)
{
int32_t i32;
// 将pu32TxBuf存入发送数组g_u8SendData
for(i32 = 0; i32 < 8; i32++)
g_u8SendData[g_i32pointer++] = pu32TxBuf[i32] ;
// 将GetCheckSumValue获取的校验值存入发送数组
g_u8SendData[g_i32pointer++] = GetCheckSumValue(g_u8SendData, checkSumOption) ; //CheckSum Field
// 通过UART1从发送数组[2]开始发送9位响应域(数据位+检验和)
UART_Write(UART1, g_u8SendData + 2, 9);
}
LIN 总结接收(RX)编程流程如下:
参考:
- 《TRM_NUC029_Series_SC_Rev1.04.pdf》
- 《 LIN协议中的帧结构与同步域(0x55)》杭州山不高
声明:[笔记整理] 内容整理自网络,版权归原作者所有,若有侵权请联系删除。