1.概述
上节我们熟悉了国民技术N32A455的CAN通讯,本节我们来熟悉国民技术N32A455的LIN通讯,在汽车电子组网应用中,通常CAN会作为高速通讯的网络,而LIN则会作为低事通讯的网络,这两个网络相互配合,实现了车机整体的通讯网络。
N32A455的LIN其实是基于通用同步异步收发器(USART)的LIN模式实现的,通过RX\TX引脚与LIN收发芯片连接,通过LIN底层通讯协议,实现LIN的数据交互。
2.LIN协议
这部分在UM手册上并没有过多的去描述,只提及到了LIN的断开帧;但在我们使用LIN通讯时,必须要先搞懂LIN通讯协议哈,所以网罗了一些LIN的资料,供大家借鉴哦
3.LIN示例程序
依据官方提供的LIN主、从机示例程序,例程功能如下:若接收到主机请求帧(0x3C)会打印接收到的8字节数据(0x0F);若接收到从机应答帧(0x3D)将发送8个字节应答数据(0x01)给到主机节点。
3.1.LIN主机实现
#include "main.h"
#include "lin_master.h"
M_LIN_EX_MSG M_TxMsg;
void SetFrameMsg(M_LIN_EX_MSG *dst_Msg, M_LIN_EX_MSG src_Msg)
{
int i = 0;
Memset(dst_Msg, 0, sizeof(M_LIN_EX_MSG));
dst_Msg->Check = src_Msg.Check;
dst_Msg->DataLen = src_Msg.DataLen;
dst_Msg->Sync = src_Msg.Sync;
dst_Msg->PID = src_Msg.PID;
for(i = 0; i < src_Msg.DataLen; i++)
{
dst_Msg->Data[i] = src_Msg.Data[i];
}
log_info("SetFrameMsg ID:0x%02x\r\n", dst_Msg->PID);
}
void SetFramePID(M_LIN_EX_MSG *src_Msg)
{
uint8_t p0 = 0, p1 = 0;
uint8_t LIN_ID = src_Msg->PID, PID = 0x00;
p0 = (LIN_ID & 0x01) ^ ((LIN_ID & 0x02) >> 1) ^ ((LIN_ID & 0x04) >> 2) ^ ((LIN_ID & 0x10) >> 4);//????
p0 = p0 & 0x01;
p1 = ~(((LIN_ID & 0x02) >> 1) ^ ((LIN_ID & 0x08) >> 3) ^ ((LIN_ID & 0x10) >> 4) ^ ((LIN_ID & 0x20) >> 5));
p1 = p1 & 0x01;
PID = (p1 << 7) | (p0 << 6) | LIN_ID;
src_Msg->PID = PID;
log_info("p0 = %02x;p1 = %02x;PID = %02x\r\n", p0, p1, PID);
}
uint8_t MasterGetCheckSum(uint8_t *pData, uint8_t len)
{
uint16_t check_sum_temp = 0;
uint8_t i;
for(i = 0; i < len; i++)
{
check_sum_temp += pData[i];
if(check_sum_temp > 0xFF)
{
check_sum_temp -= 0xFF;
}
}
return (~check_sum_temp) & 0xFF;
}
void SetFrameChecksum(M_LIN_EX_MSG *Msg)
{
uint8_t CheckSum = 0;
uint8_t len = Msg->DataLen;
if(Msg->Check)
{
CheckSum = MasterGetCheckSum(&Msg->PID, len + 1);
}
else
{
CheckSum = MasterGetCheckSum(Msg->Data, len);
}
if(len < 8)
{
Msg->Data[len] = CheckSum;
}
else
{
Msg->Check = CheckSum;
}
}
void MasterSendBytes(uint8_t *pBuf, uint8_t Len)
{
USART_Break_Frame_Send(USARTx);
while(Len--)
{
while(USART_Flag_Status_Get(USARTx, USART_FLAG_TXC ) == RESET);
USART_Data_Send(USARTx, *pBuf++);
}
while(USART_Flag_Status_Get(USARTx, USART_FLAG_TXC ) == RESET);
}
void MasterSendFrame(M_LIN_EX_MSG Msg)
{
if(Msg.DataLen)
{
MasterSendBytes(&Msg.Sync, Msg.DataLen + 3);
}
else
{
MasterSendBytes(&Msg.Sync, 2);
}
}
void FrameHandle(void)
{
uint8_t tmp_PID = M_TxMsg.PID;
SetFramePID(&M_TxMsg);
switch (tmp_PID)
{
case 0x3C://Master request frame
SetFrameChecksum(&M_TxMsg);
break;
case 0x3D://Slave reply frame
M_TxMsg.DataLen = 0;
break;
default:
break;
}
MasterSendFrame(M_TxMsg);
}
static ErrorStatus USART_ByteReceive(uint8_t *Data, uint32_t TimeOut)
{
uint32_t Counter = 0;
while((USART_Flag_Status_Get(USARTx, USART_FLAG_RXDNE) == RESET) && (Counter != TimeOut))
{
Counter++;
}
if(Counter != TimeOut)
{
*Data = (uint8_t)USART_Data_Receive(USARTx);
return SUCCESS;
}
else
{
return ERROR;
}
}
uint32_t Master_RecData(uint8_t *pdata, uint8_t length)
{
int i = 0;
uint8_t Data = 0;
uint32_t number = 0;
while(i < length)
{
i++;
if((USART_ByteReceive(&Data, SC_RECEIVE_TIMEOUT)) == SUCCESS)
{
pdata[number] = Data;
number++;
}
}
return number;
}
ErrorStatus WaitFrameRes(uint8_t *dst_data, uint8_t length)
{
//int i = 0;
int datalen = 0;
uint8_t recv_data[16];
uint8_t CheckSum = 0;
datalen = Master_RecData(recv_data, 16);
if(datalen)
{
#if 0
log_info("recv_data:");
for(i = 1; i < datalen; i++)
{
log_info("0x%x\r\n", recv_data[i]);
}
#endif
CheckSum = MasterGetCheckSum(recv_data, datalen - 1);
log_info("CheckSum:0x%x\r\n", CheckSum);
if(CheckSum == recv_data[datalen - 1])
{
if( (datalen - 2) > length)
{
Buffercopy(dst_data, &recv_data[0], length);
}
else
{
Buffercopy(dst_data, &recv_data[0], datalen - 1);
}
return SUCCESS;
}
}
return ERROR;
}
void TestMasterReqFrame(void)
{
int i = 0;
M_LIN_EX_MSG CurLINTxMsg;
CurLINTxMsg.Check = CLASSIC;
CurLINTxMsg.DataLen = 8;
CurLINTxMsg.Sync = 0x55;
CurLINTxMsg.PID = 0x3C;
for(i = 0; i < CurLINTxMsg.DataLen; i++)
{
CurLINTxMsg.Data[i] = 0x0F;
}
SetFrameMsg(&M_TxMsg, CurLINTxMsg);
FrameHandle();
}
void TestSlaveResFrame(void)
{
M_LIN_EX_MSG CurLINTxMsg;
CurLINTxMsg.Check = CLASSIC;
CurLINTxMsg.DataLen = 0;
CurLINTxMsg.Sync = 0x55;
CurLINTxMsg.PID = 0x3D;
SetFrameMsg(&M_TxMsg, CurLINTxMsg);
FrameHandle();
}
void TestLinMaster(void)
{
int i = 0, count = 0;
uint8_t recv_data[8];
TestMasterReqFrame();
delay_xms(20);
while(count < 4)
{
Memset(recv_data, 0, 8);
TestSlaveResFrame();
if(WaitFrameRes(recv_data, 8) == SUCCESS)
{
log_info("recv_data:\r\n");
for(i = 0; i < 8; i++)
{
log_info("recv_data[%d] = 0x%x\r\n", i, recv_data[i]);
}
break;
}
else
{
log_info("slave no response!!\r\n");
count++;
}
}
}
#include <stdio.h>
#include "main.h"
static uint8_t max_ms = 116;
/**
*\*\name delay_xms.
*\*\fun delay program.
*\*\param nms
*\*\return none
**/
void delay_xms(uint32_t nms)
{
uint16_t i;
uint16_t count_1 = nms / max_ms;
uint16_t count_2 = nms % max_ms;
if(0 == count_1)
{
systick_delay_ms(nms);
}
else
{
for(i = 0; i < count_1; i++)
{
systick_delay_ms(max_ms);
}
if(count_2 != 0)
{
systick_delay_ms(count_2);
}
}
}
/**
*\*\name Memset.
*\*\fun memery set a value.
*\*\param s source
*\*\param c value
*\*\param count number
*\*\return s
**/
void *Memset(void *s, s8 c, u32 count)
{
s8 *xs = (s8 *) s;
while (count--) // clear 17byte buffer
{
*xs++ = c;
}
return s;
}
/**
*\*\name Buffercopy.
*\*\fun Compares two buffers.
*\*\param pBuffer1
*\*\param pBuffer2
*\*\param buffer's length
*\*\return s
**/
void Buffercopy(uint8_t *dest, uint8_t *src, uint16_t BufferLength)
{
while (BufferLength--)
{
*dest = *src;
dest++;
src++;
}
}
/**
*\*\name main.
*\*\fun Main program.
*\*\param none
*\*\return none
**/
int main(void)
{
USART_InitType USART_InitStructure;
log_init();
printf("\r\n test LIN master mode\r\n");
/* System Clocks Configuration */
RCC_Configuration();
/* NVIC configuration */
NVIC_Configuration();
/* Configure the GPIO ports */
GPIO_Configuration();
/* USARTx configuration ------------------------------------------------------*/
USART_InitStructure.BaudRate = 9600;
USART_InitStructure.WordLength = USART_WL_8B;
USART_InitStructure.StopBits = USART_STPB_1;
USART_InitStructure.Parity = USART_PE_NO;
USART_InitStructure.HardwareFlowControl = USART_HFCTRL_NONE;
USART_InitStructure.Mode = USART_MODE_RX | USART_MODE_TX;
/* Configure USARTx */
USART_Initializes(USARTx, &USART_InitStructure);
/* Enable the USARTx LIN mode*/
USART_LIN_Break_Detect_Length_Set(USARTx,USART_LINBDL_10B);
USART_LIN_Enable(USARTx);
/* Enable the USARTx */
USART_Enable(USARTx);
while (1)
{
TestLinMaster();
delay_xms(500);
}
}
/**
*\*\name RCC_Configuration.
*\*\fun Configures the different system clocks.
*\*\param none
*\*\return none
**/
void RCC_Configuration(void)
{
/* Enable GPIO clock */
GPIO_AHBClkCmd(USARTx_GPIO_CLK);
RCC_APB2_Peripheral_Clock_Enable(RCC_APB2_PERIPH_AFIO);
/* Enable USARTx Clock */
USART_APBxClkCmd(USARTx_CLK);
}
/**
*\*\name NVIC_Configuration.
*\*\fun Configures the nested vectored interrupt controller.
*\*\param none
*\*\return none
**/
void NVIC_Configuration(void)
{
NVIC_InitType NVIC_InitStructure;
NVIC_Priority_Group_Set(NVIC_PER2_SUB2_PRIORITYGROUP);
/* Enable the USARTx Interrupt */
NVIC_InitStructure.NVIC_IRQChannel = USARTx_IRQn;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Initializes(&NVIC_InitStructure);
}
/**
*\*\name GPIO_Configuration.
*\*\fun Configures the different GPIO ports.
*\*\param none
*\*\return none
**/
void GPIO_Configuration(void)
{
GPIO_InitType GPIO_InitStructure;
/* Initialize GPIO_InitStructure */
GPIO_Structure_Initialize(&GPIO_InitStructure);
/* Configure USARTx Tx as alternate function push-pull */
GPIO_InitStructure.Pin = USARTx_TxPin;
GPIO_InitStructure.GPIO_Mode = GPIO_MODE_AF_PP;
GPIO_InitStructure.GPIO_Alternate = USARTx_Tx_GPIO_AF;
GPIO_Peripheral_Initialize(USARTx_GPIO, &GPIO_InitStructure);
/* Configure USARTx Rx as alternate function push-pull */
GPIO_InitStructure.Pin = USARTx_RxPin;
GPIO_InitStructure.GPIO_Alternate = USARTx_Rx_GPIO_AF;
GPIO_Peripheral_Initialize(USARTx_GPIO, &GPIO_InitStructure);
}
3.2.LIN从机实现
#include "main.h"
#include "lin_driver.h"
LIN_STATE LinRxState = IDLE;
uint8_t LINRxDataIndex = 0;
LIN_EX_MSG LINRxDataBuffer[2];//双缓冲接收数据,可以减少数据出错概率
uint8_t IDType[64] = {ID_TYPE_SR};
uint8_t GotMsgFlag = 0;
LIN_EX_MSG *pLINMsg;
LIN_EX_MSG LINTxMsg;
void LIN_SendBytes(uint8_t *pBuf, uint8_t Len)
{
while(Len--)
{
while(USART_GetFlagStatus(USARTz, USART_FLAG_TXC ) == RESET);
USART_SendData(USARTz, *pBuf++);
}
while(USART_GetFlagStatus(USARTz, USART_FLAG_TXC ) == RESET);
}
uint8_t LIN_GetCheckSum(uint8_t *pData, uint8_t len)
{
uint16_t check_sum_temp = 0;
uint8_t i;
for(i = 0; i < len; i++)
{
check_sum_temp += pData[i];
if(check_sum_temp > 0xFF)
{
check_sum_temp -= 0xFF;
}
}
return (~check_sum_temp) & 0xFF;
}
void LIN_SetResp(uint8_t ID, uint8_t *pData, uint8_t Len, uint8_t CheckType)
{
uint8_t i = 0;
uint8_t CheckSum = 0;
if(Len > 8)
{
Len = 8;
}
LINTxMsg.PID = GET_PID(ID);
for(i = 0; i < Len; i++)
{
LINTxMsg.Data[i] = pData[i];
}
if(CheckType)
{
CheckSum = LIN_GetCheckSum(&LINTxMsg.PID, Len + 1);
}
else
{
CheckSum = LIN_GetCheckSum(LINTxMsg.Data, Len);
}
if(Len < 8)
{
LINTxMsg.Data[Len] = CheckSum;
}
else
{
LINTxMsg.Check = CheckSum;
}
LINTxMsg.DataLen = Len;
}
void LIN_EX_RxAsync(uint8_t data)
{
switch(LinRxState)
{
case IDLE:
break;
case SYNCH:
if(data == 0x55)
{
LINRxDataBuffer[LINRxDataIndex].Sync = 0x55;
LinRxState = ID_LEN;
}
else
{
LinRxState = IDLE;
}
break;
case ID_LEN:
if(GET_PID(data) == data)
{
LINRxDataBuffer[LINRxDataIndex].PID = data;
LINRxDataBuffer[LINRxDataIndex].DataLen = 0;
if(IDType[data & 0x3F] == ID_TYPE_SR)
{
LinRxState = DATA_GET;
}
else
{
//接收到主机发送的帧头,从机模式发送数据
if(((LINTxMsg.PID & 0x3F) == (data & 0x3F)) && (LINTxMsg.DataLen > 0))
{
LIN_SendBytes(LINTxMsg.Data, LINTxMsg.DataLen + 1);
LINTxMsg.DataLen = 0;
}
LinRxState = IDLE;
}
}
else
{
LinRxState = IDLE;
}
break;
case DATA_GET:
LINRxDataBuffer[LINRxDataIndex].Data[LINRxDataBuffer[LINRxDataIndex].DataLen] = data;
LINRxDataBuffer[LINRxDataIndex].Check = data;
LINRxDataBuffer[LINRxDataIndex].DataLen++;
if(LINRxDataBuffer[LINRxDataIndex].DataLen >= 8)
{
LinRxState = CHECKSUM;
}
else
{
LinRxState = DATA_GET;
}
break;
case CHECKSUM:
LINRxDataBuffer[LINRxDataIndex].Check = data;
pLINMsg = &LINRxDataBuffer[LINRxDataIndex];
GotMsgFlag = 1;
LINRxDataIndex = (LINRxDataIndex + 1) % 2;
LinRxState = IDLE;
break;
default:
break;
}
}
void LIN_IRQHandler(void)
{
//BREAK中断
if(USART_GetIntStatus(USARTz, USART_INT_LINBD) == SET)
{
USART_ClrIntPendingBit(USARTz, USART_INT_LINBD);//清除LIN间隔场检测标志位
//读状态寄存器和数据寄存器是为了清除FE标志
USARTz->STS;
USARTz->DAT;
LinRxState = SYNCH;
return;
}
//接收数据中断
if (USART_GetIntStatus(USARTz, USART_INT_RXDNE) == SET)
{
USART_ClrIntPendingBit(USARTz, USART_INT_RXDNE); //清除接收中断标志位
if(USART_GetIntStatus(USARTz, USART_INT_FEF) == RESET)
{
LIN_EX_RxAsync((uint8_t)USART_ReceiveData(USARTz));
}
}
}
/**
* [url=home.php?mod=space&uid=159083]@brief[/url] 执行主机下发的命令
* @param pData 收到的数据,该函数会默认读取8字节数据
* @param pFunResp 命令响应函数指针,需要自己实现
* @retval 无
*/
void BOOT_ExecutiveCommand(uint8_t *pData, FUN_RESP pFunResp)
#if 1
{
int i = 0;
log_info("BOOT_ExecutiveCommand\r\n");
for( i = 0; i < 8; i++)
{
log_info("pData[%d] = %02x \r\n", i, pData[i]);
pData[i] = 0x1;
}
pFunResp(pData, 8);
}
#endif
#include <stdio.h>
#include "main.h"
/** @addtogroup N32A45X_StdPeriph_Examples
* @{
*/
void RCC_Configuration(void);
void GPIO_Configuration(void);
void NVIC_Configuration(void);
static uint8_t max_ms = 116;
extern uint8_t IDType[64];
extern uint8_t GotMsgFlag;
extern LIN_EX_MSG *pLINMsg;
void delay_xms(uint32_t nms)
{
uint16_t i;
uint16_t count_1 = nms / max_ms;
uint16_t count_2 = nms % max_ms;
if(0 == count_1)
{
systick_delay_ms(nms);
}
else
{
for(i = 0; i < count_1; i++)
{
systick_delay_ms(max_ms);
}
if(count_2 != 0)
{
systick_delay_ms(count_2);
}
}
}
/**
* @brief Compares two buffers.
* @param pBuffer1, pBuffer2: buffers to be compared.
* @param BufferLength buffer's length
* [url=home.php?mod=space&uid=784970]@return[/url] PASSED: pBuffer1 identical to pBuffer2
* FAILED: pBuffer1 differs from pBuffer2
*/
void Buffercopy(uint8_t *dest, uint8_t *src, uint16_t BufferLength)
{
while (BufferLength--)
{
*dest = *src;
dest++;
src++;
}
}
void LIN_RespData(uint8_t *pData, uint8_t Len)
{
LIN_SetResp(MSG_SEND_ID, pData, Len, CHECK_TYPE);
}
/**
* @brief Main program
*/
int main(void)
{
USART_InitType USART_InitStructure;
log_init();
printf("\r\n test LIN slave mode\r\n");
/* System Clocks Configuration */
RCC_Configuration();
/* NVIC configuration */
NVIC_Configuration();
/* Configure the GPIO ports */
GPIO_Configuration();
/* USARTy and USARTz configuration ------------------------------------------------------*/
USART_InitStructure.BaudRate = 9600;
USART_InitStructure.WordLength = USART_WL_8B;
USART_InitStructure.StopBits = USART_STPB_1;
USART_InitStructure.Parity = USART_PE_NO;
USART_InitStructure.HardwareFlowControl = USART_HFCTRL_NONE;
USART_InitStructure.Mode = USART_MODE_RX | USART_MODE_TX;
/* Configure USARTy and USARTz */
USART_Init(USARTz, &USART_InitStructure);
/* Enable the USARTz LIN mode*/
USART_ConfigLINBreakDetectLength(USARTz, USART_LINBDL_10B);
USART_EnableLIN(USARTz, ENABLE);
/* Enable USARTz Receive and Transmit interrupts */
USART_ConfigInt(USARTz, USART_INT_RXDNE, ENABLE);
//USART_ConfigInt(USARTz, USART_INT_TXDE, ENABLE);
USART_ConfigInt(USARTz, USART_INT_LINBD, ENABLE);
/* Enable the USARTz */
USART_Enable(USARTz, ENABLE);
//配置从机ID模式
IDType[MSG_RECEIVE_ID & 0x3F] = ID_TYPE_SR;
IDType[MSG_SEND_ID & 0x3F] = ID_TYPE_SW;
while (1)
{
if(GotMsgFlag)
{
BOOT_ExecutiveCommand(pLINMsg->Data, LIN_RespData);
GotMsgFlag = 0;
}
}
}
/**
* @brief Configures the different system clocks.
*/
void RCC_Configuration(void)
{
/* Enable GPIO clock */
RCC_EnableAPB2PeriphClk(USARTz_GPIO_CLK | RCC_APB2_PERIPH_AFIO, ENABLE);
/* Enable USARTz Clock */
USARTz_APBxClkCmd(USARTz_CLK, ENABLE);
}
/**
* @brief Configures the different GPIO ports.
*/
void GPIO_Configuration(void)
{
GPIO_InitType GPIO_InitStructure;
/* Configure USARTz Rx as input floating */
GPIO_InitStructure.Pin = USARTz_RxPin;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
GPIO_InitPeripheral(USARTz_GPIO, &GPIO_InitStructure);
/* Configure USARTz Tx as alternate function push-pull */
GPIO_InitStructure.Pin = USARTz_TxPin;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
GPIO_InitPeripheral(USARTz_GPIO, &GPIO_InitStructure);
/* Configure TJA1027 SLP as ouput push-pull */
GPIO_InitStructure.Pin = SLP_Pin;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_InitPeripheral(SLP_GPIO, &GPIO_InitStructure);
GPIO_SetBits(SLP_GPIO,SLP_Pin);
#ifdef _USART2_
GPIO_ConfigPinRemap(GPIO_RMP3_USART2, ENABLE);
GPIO_ConfigPinRemap(GPIO_RMP_SW_JTAG_NO_NJTRST, ENABLE);
#endif
#ifdef _USART4_
GPIO_ConfigPinRemap(GPIO_RMP3_UART4, ENABLE);
#endif
}
/**
* @brief Configures the nested vectored interrupt controller.
*/
void NVIC_Configuration(void)
{
NVIC_InitType NVIC_InitStructure;
/* Configure the NVIC Preemption Priority Bits */
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_0);
/* Enable the USARTz Interrupt */
NVIC_InitStructure.NVIC_IRQChannel = USARTz_IRQn;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
}
#ifdef USE_FULL_ASSERT
/**
* @brief Reports the name of the source file and the source line number
* where the assert_param error has occurred.
* @param file pointer to the source file name
* @param line assert_param error line source number
*/
void assert_failed(const uint8_t *expr, const uint8_t *file, uint32_t line)
{
/* User can add his own implementation to report the file name and line number,
ex: printf("Wrong parameters value: file %s on line %d\r\n", file, line) */
/* Infinite loop */
while (1)
{
}
}
#endif
4.测试
4.1.测试环境
由于没有2块N32A455开发板,所以我找了一块N32G430C8L7_STB_V1.0开发板作为LIN主机,搭配N32A455开发板作为LIN从机一起测试;但是N32G430C8L7_STB_V1.0开发板上并没有板载LIN收发芯片,所以我们还是通过交叉连接USART的RX和TX,对LIN的通讯功能进行测试,毕竟功能OK,其它只是固定的外围硬件电路,关系不大!
4.2.测试结果
5.注意事项
5.1、当使用N32A455的USART作为LIN模式通讯时,以下配置位必须全部被清零 :USART_CTRL2.STPB[1:0] 、USART_CTRL2.CLKEN 、USART_CTRL3.SCMEN 、USART_CTRL3.HDMEN 、USART_CTRL3.IRDAMEN。
5.2.在 LIN 模式下发送数据时,数据长度只能配置为 8 位。