文章目录
目录
文章目录
前言
一、LIN总线是什么?
1.LIN总线的标准
2.LIN总线形式
3、帧的构成
二、UART模拟LIN从机的实现
1.方案概述
2.串口中断处理
3、同步间隔断中断处理
4、接收中断处理
验证测试
1、CANoe IG发送帧头
?2、示波器观测波形
总结
前言
在汽车领域,CAN总线和LIN总线是应用比较多的总线,因此需要对LIN总线有深入的了解。
本文主要梳理关键知识点,帮助大家建立整体的概念,重点在于知识体系的构建。
一、LIN总线是什么?
1.LIN总线的标准
目前各大主机厂广泛会用的是LIN2.1,波特率采用19200。
2.LIN总线形式
?LIN在总线上的展现形式,主机发帧头(对比CAN的ID),从机响应数据(对比CAN的8字节数据)
3、帧的构成
?同步段为? 0x55
?ID段为? ? ?帧ID+校验
数据段为? 帧的数据内容
校验和? ? 数据段字节和ID的校验和
二、UART模拟LIN从机的实现
1.方案概述
用接收中断进行同步段验证、数据接收
用发送中断进行数据和校验的发送
2.串口中断处理
在中断中处理
同步间隔中断
接收中断
发送中断
void UartLIN_Handler(void *device, uint32_t wpara, uint32_t lpara)
{
// Receive break field
if (0 != (lpara & UART_LSR1_FBRK_Msk))
{
LIN_IfcAux();
UART_LIN->LSR1 |= UART_LSR1_FBRK_Msk; ///<write 1 to clear break status
}
// Receive data
if (0 != (wpara & UART_LSR0_DR_Msk))
{
LIN_IfcRx();
}
// Transmit data done
if (0 != (wpara & UART_LSR0_THRE_Msk))
{
LIN_IfcTx();
}
}
3、同步间隔断中断处理
void LIN_IfcAux(void)
{
if (g_mode == SLAVE_MODE)
{
if (g_state == LIN_STATE_IDLE)
{
g_state = g_autoBaud ? LIN_STATE_RECEIVE_IDENTIFIER : LIN_STATE_RECEIVE_SYNC;
g_direction = DIR_RX;
g_txCount = 0;
g_rxCount = 0;
USART_TXE_INT_Enable(USART1_SFR, FALSE);
}
else
{
//fprintf("Frame err\r\n");
g_state = g_autoBaud ? LIN_STATE_RECEIVE_IDENTIFIER : LIN_STATE_RECEIVE_SYNC;
g_direction = DIR_RX;
g_txCount = 0;
g_rxCount = 0;
USART_TXE_INT_Enable(USART1_SFR, FALSE);
}
}
}
4、接收中断处理
void LIN_IfcRx(void)
{
uint8_t data = 0, id = 0, index = 0, checksum = 0, frameNum = 0;
FRAME_ID_TBL *frameTable = 0, *frameInfo = 0;
data = USART_ReceiveData(USART1_SFR);
//fprintf("%02x ", data);
if (g_direction == DIR_TX)
{
#if 0
if (data != g_buffer[g_rxCount])
{
// Send error
//fprintf("bit err\r\n");
}
g_rxCount++;
// transmit done
if (g_rxCount == g_tranByte)
{
frameInfo = &g_scheduleTable[g_schTblIdx].schedule[g_frameIdx];
if (g_state == LIN_STATE_SEND_SYNC)
{
// Start transmit or receive data
if (frameInfo->dir == DIR_TX)
{
g_state = LIN_STATE_SEND_DATA;
g_direction = DIR_TX;
g_tranByte = frameInfo->dtl + 1;
memcpy((void *)g_buffer, (const void *)frameInfo->data, (unsigned int)g_tranByte - 1);
checksum = LIN_MakeChecksum((frameInfo->cst == ENHANCED_CHECKSUM) ? g_protectId : 0,
frameInfo->dtl, frameInfo->data);
g_buffer[g_tranByte - 1] = checksum;
g_txCount = 0;
g_rxCount = 0;
//UART_SetTXEInterrupt(UART_LIN, ENABLE);
USART_Send(USART1_SFR, g_buffer, g_tranByte);
}
else
{
g_state = LIN_STATE_RECEIVE_DATA;
g_direction = DIR_RX;
g_recvByte = frameInfo->dtl + 1;
g_rxCount = 0;
}
}
else if (g_state == LIN_STATE_SEND_DATA)
{
// Transmit frame done
g_state = LIN_STATE_IDLE;
}
}
#endif
}
else
{
if (g_mode == SLAVE_MODE)
{
if (g_state == LIN_STATE_RECEIVE_SYNC)
{
// Check sync field
if (data == 0x55)
{
g_state = LIN_STATE_RECEIVE_IDENTIFIER;
}
else
{
g_state = LIN_STATE_IDLE;
//fprintf("Sync err\r\n");
}
}
else if (g_state == LIN_STATE_RECEIVE_IDENTIFIER)
{
// Check protect id
id = data & 0x3F;
if (data == LIN_MakeProtId(id))
{
frameTable = g_scheduleTable[g_schTblIdx].schedule;
frameNum = g_scheduleTable[g_schTblIdx].frameNum;
for (index = 0; index < frameNum; index++)
{
frameInfo = &frameTable[index];
// Find needed id
if (id == frameInfo->id)
{
g_frameIdx = index;
g_protectId = data;
//fprintf("%d\r\n", id);
// Start transmit or receive data
if (frameInfo->dir == DIR_TX)
{
g_state = LIN_STATE_SEND_DATA;
g_direction = DIR_TX;
g_tranByte = frameInfo->dtl + 1;
memcpy((void *)g_buffer, (const void *)frameInfo->data, g_tranByte - 1);
checksum = LIN_MakeChecksum((frameInfo->cst == ENHANCED_CHECKSUM) ? g_protectId : 0,
frameInfo->dtl, frameInfo->data);
g_buffer[g_tranByte - 1] = checksum;
g_txCount = 0;
g_rxCount = 0;
//UART_SetTXEInterrupt(UART_LIN, ENABLE);
//USART_Send(USART1_SFR, g_buffer, g_tranByte);
USART_TXE_INT_Enable(USART1_SFR, TRUE);
//delay_us(2);
//g_state = LIN_STATE_IDLE;
//g_direction = DIR_RX;
//g_txCount = 0;
//g_rxCount = 0;
}
else
{
g_state = LIN_STATE_RECEIVE_DATA;
g_direction = DIR_RX;
g_recvByte = frameInfo->dtl + 1;
g_rxCount = 0;
}
break;
}
}
// Not needed id
if (index == frameNum)
{
g_state = LIN_STATE_IDLE;
//fprintf("Err Id:0x%x\r\n", data);
}
}
else
{
g_state = LIN_STATE_IDLE;
//fprintf("Pid:0x%x err\r\n", data);
}
}
else if (g_state == LIN_STATE_RECEIVE_DATA)
{
if (g_rxCount < g_recvByte)
{
g_buffer[g_rxCount] = data;
g_rxCount++;
}
// Receive frame done
if (g_rxCount == g_recvByte)
{
frameInfo = &g_scheduleTable[g_schTblIdx].schedule[g_frameIdx];
checksum = LIN_MakeChecksum((frameInfo->cst == ENHANCED_CHECKSUM) ? g_protectId : 0,
g_recvByte - 1, g_buffer);
if (checksum == g_buffer[g_rxCount - 1])
{
// Checksum is successful.
// Save data
memcpy((void *)frameInfo->data, (const void *)g_buffer, g_rxCount - 1);
}
else
{
// Checksum is failed.
//fprintf("Cs err\r\n");
}
g_state = LIN_STATE_IDLE;
}
}
else
{
}
}
}
}
发送中断处理
void LIN_IfcTx(void)
{
if (g_direction == DIR_TX)
{
if (g_mode == SLAVE_MODE)
{
if (g_state == LIN_STATE_SEND_DATA)
{
// Send data
if (g_txCount < g_tranByte)
{
//UART_SendData(UART_LIN, g_buffer[g_txCount]);
USART_SendData(USART1_SFR, g_buffer[g_txCount]);
g_txCount++;
if (g_txCount == g_tranByte)
{
//UART_SetTXEInterrupt(UART_LIN, DISABLE);
USART_TXE_INT_Enable(USART1_SFR, FALSE);
}
}
else
{
//UART_SetTXEInterrupt(UART_LIN, DISABLE);
USART_TXE_INT_Enable(USART1_SFR, FALSE);
g_direction = DIR_RX;
g_txCount = 0;
g_rxCount = 0;
}
}
else
{
//UART_SetTXEInterrupt(UART_LIN, DISABLE);
USART_TXE_INT_Enable(USART1_SFR, FALSE);
}
}
}
}
数据结构和变量
typedef enum
{
LIN_STATE_IDLE, ///< Idle state
LIN_STATE_SEND_BREAK, ///< Send break state
LIN_STATE_RECEIVE_BREAK, ///< Receive break state
LIN_STATE_SEND_SYNC, ///< Send sync state
LIN_STATE_RECEIVE_SYNC, ///< Receive sync state
LIN_STATE_SEND_IDENTIFIER, ///< Send identifier state
LIN_STATE_RECEIVE_IDENTIFIER, ///< Receive identifier state
LIN_STATE_SEND_DATA, ///< Send data state
LIN_STATE_RECEIVE_DATA, ///< Receive data state
LIN_STATE_SEND_CHECKSUM, ///< Send checksum state
LIN_STATE_BUSSLEEP ///< Bus sleep state
} lin_ifcState;
typedef struct {
uint8_t id; ///< LIN message frame ID for this frame
uint8_t dir; ///< Name of the node that publishes the data
uint8_t cst; ///< Type of checksum to be used (enhanced or classic)
uint8_t dtl; ///< Number of bytes in the data field
uint16_t delayTime; ///< Actual slot time in ms for the frame
uint8_t *data; ///< Address of the structure that lists the signals
} FRAME_ID_TBL;
typedef struct {
uint8_t frameNum; ///< Number of frame slots in the schedule table
FRAME_ID_TBL *schedule; ///< Address of the schedule table
} SCHEDULE_TBL;
//#define UART_LIN UART1
// Checksum type
#define ENHANCED_CHECKSUM 0 ///< Enhanced checksum type
#define CLASSIC_CHECKSUM 1 ///< Classic checksum type
// Direction
#define DIR_RX 0 ///< receive direction
#define DIR_TX 1 ///< transmit direction
// Node mode
#define MASTER_MODE 0 ///< master mode
#define SLAVE_MODE 1 ///< slave mode
extern void LIN_IfcRx(void);
extern void LIN_IfcAux(void);
extern void LIN_IfcTx(void);
extern void LinSlaveInit(void);
void LinSlaveInit(void)
{
}
uint8_t g_LinData[4][8] =
{
{0xA1,0xB2,0xC3,0xD4,0xE5,0xF6,0x97,0x88},
{0x21,0x22,0x23,0x24,0x25,0x26,0x27,0x28},
{0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08},
{0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08},
};
///< Master send header and receive data
FRAME_ID_TBL g_frameTable1[] =
{
{0x23, 1, 0, 3, 60, g_LinData[0]},
{0x21, 0, 0, 8, 60, g_LinData[1]}
};
///< Schedule table
SCHEDULE_TBL g_scheduleTable[] =
{
{2, &g_frameTable1[0]}
};
uint32_t g_sendTimes = 0xFFFF; ///< Send frame times
uint8_t g_mode = SLAVE_MODE; ///< Master or slave mode
uint8_t g_state = 0; ///< Lin state
uint8_t g_schTblIdx = 0; ///< Schedule table index
uint8_t g_frameIdx = 0; ///< Frame index
uint8_t g_protectId = 0; ///< Protect id
uint8_t g_firstFlag = 1; ///< First time flag
uint8_t g_direction = 0; ///< Send direction
uint8_t g_tranByte = 0; ///< Need transmit bytes
uint8_t g_recvByte = 0; ///< Need receive bytes
uint8_t g_txCount = 0; ///< Transmit data count
uint8_t g_rxCount = 0; ///< Receive data count
uint8_t g_autoBaud = 0; ///< Auto baud rate
uint8_t g_breakThre = 1; ///< Break threshold
uint8_t g_buffer[20] = {0}; ///< Data buffer
uint8_t LIN_MakeProtId(uint8_t idData)
{
union {
uint8_t byte;
struct {
uint8_t d0: 1;
uint8_t d1: 1;
uint8_t d2: 1;
uint8_t d3: 1;
uint8_t d4: 1;
uint8_t d5: 1;
uint8_t p0: 1;
uint8_t p1: 1;
} bit;
} buf;
/* Load the desired message frame ID into the LSBits of the buffer. */
buf.byte = idData & (uint8_t)0x3F;
/* Set the two parity bits. */
buf.bit.p1 = ~(buf.bit.d1 ^ buf.bit.d3 ^ buf.bit.d4 ^ buf.bit.d5);
buf.bit.p0 = buf.bit.d0 ^ buf.bit.d1 ^ buf.bit.d2 ^ buf.bit.d4;
return buf.byte;
}
uint8_t LIN_MakeChecksum(uint8_t protectId, uint8_t length, uint8_t *data)
{
uint8_t i = 0, checksum = 0;
uint16_t sum = protectId;
for (i = 0; i < length; i++)
{
sum += data[i];
if (sum >= 0x100)
{
sum -= 0xFF;
}
}
checksum = ~(uint8_t)sum;
return checksum;
}
验证测试
1、CANoe IG发送帧头
?2、示波器观测波形
总结
利用串口可以完美模拟从机,发送和接收采用中断的形式有利于提高程序的执行效率。
5.1的最后一天假,完美收官
|