LIN驱动架构

1.dLIN.h和dLIN.c驱动层代码

/*
 * dLIN.h
 *
 *  Created on: 2024/10/25
 *      Author: WZX
 */
 
 
#ifndef _DLIN_H_
#define _DLIN_H_


#include <stdint.h>


typedef enum
{
	CLASSIC,				// 经典校验
	ENHANCE					// 增强校验
} LIN_CHECKSUM_TYPE;

typedef enum
{
	RECV,					// 接收
	SEND					// 发送
} LIN_TRANSFER_TYPE;

typedef enum
{
	LIN_BUS_WAIT_BREAK,					// 等待Break信号
	LIN_BUS_WAIT_SYNC,					// 等待接收同步信号
	LIN_BUS_WAIT_PID,					// 等待接收PID
	LIN_BUS_WAIT_DATA,					// 等待接收数据
	LIN_BUS_WAIT_CHECKSUM,				// 等待接收校验和
	LIN_BUS_SENDING_DATA,				// 正在发送数据
} LIN_BUS_STATUS;

typedef enum
{
	LIN_BUS_VOLTAGE_NORMAL = 0,	        // 电源正常
	LIN_BUS_VOLTAGE_OVER   = 1,	        // 电源过压
	LIN_BUS_VOLTAGE_UNDER  = 2,	        // 电源欠压
} LIN_BUS_VOLTAGE_STATUS;

typedef union
{
	uint8_t Value;
	struct
	{
		uint8_t Reset						:	1;	// 重置 ------------ 优先级最低
		uint8_t Rx_Overflow_Err     		:	1;	// 接收过载错误
		uint8_t Rx_Sync_Err				    :	1;	// 接收同步段错误
		uint8_t Tx_ReadBack_Err			    :	1;	// 发送时读回值错误
		uint8_t Rx_Check_Sum_Err			:	1;	// 接收校验和错误
		uint8_t Rx_ByteField_Framing_Err	:	1;	// 字节字段帧错误
		uint8_t Rx_Pid_Check_Err			:	1;	// 接收PID校验错误 -- 优先级最高
	} Bit;
} LIN_BUS_ERR_TYPE;


typedef struct
{
	uint8_t Data[10];				// 发送数据缓冲区
	uint8_t Len;					// 发送数据长度
} LIN_BUS_SEND_TYPE;


typedef struct
{
	LIN_BUS_STATUS Bus_State;				//总线状态
	LIN_BUS_ERR_TYPE Bus_Error;				//总线错误状态
	LIN_BUS_VOLTAGE_STATUS Bus_Voltage;		//总线电压状态
	uint8_t Bus_Is_Sending;					//总线是否在发送中
	uint16_t Bus_IDLE_Time;					//总线空闲时间
	void (*LIN_Init)(void);					//lin初始化
	void (*LIN_Wait_Break)(void);			//lin等待间隔帧
	void (*LIN_InSleep)(void);				//lin进入低功耗
	void (*LIN_OutSleep)(void);				//lin退出低功耗
	void (*LIN_IntoIDLE_Tick)(void);		//lin进入idle计时
} LIN_TypeDef_t;


void dLIN_Init(LIN_TypeDef_t *lin_t);
void dLIN_Receive_Break(void);
uint16_t dLIN_Get_IDLE_Time(void);
void dLIN_Into_IDLE_Tick(void);

uint8_t dLIN_Cal_Pid(uint8_t Id);
uint8_t dLIN_Check_Pid(uint8_t Pid);
uint8_t dLIN_Cal_CheckSum_Classic(uint8_t* Data, uint8_t Len);
uint8_t dLIN_Cal_CheckSum_Enhance(uint8_t Pid, uint8_t* Data, uint8_t Len);


#endif


/*
 * dLIN.c
 *
 *  Created on: 2024/10/25
 *      Author: WZX
 */
 
 
#include "dLIN.h"
#include <stddef.h>


static LIN_TypeDef_t *lin = NULL;


/*********************************************************************
 * @fn      dLIN_Init
 *
 * @brief   lin初始化配置.
 *
 * @param   none
 *
 * @return  none
 */
void dLIN_Init(LIN_TypeDef_t *lin_t)
{
	if(lin_t == NULL)
	{
		return;
	}
	lin = lin_t;
	
	lin->LIN_Init();
	lin->LIN_Wait_Break();
}


/*********************************************************************
 * @fn      dLIN_Receive_Break
 *
 * @brief   lin接收break.
 *
 * @param   none
 *
 * @return  none
 */
void dLIN_Receive_Break(void)
{
	lin->LIN_Wait_Break();
}


/*********************************************************************
 * @fn      dLIN_Get_IDLE_Time
 *
 * @brief   获取lin空闲时间.
 *
 * @param   none
 *
 * @return  none
 */
uint16_t dLIN_Get_IDLE_Time(void)
{
	return lin->Bus_IDLE_Time;
}


/*********************************************************************
 * @fn      dLIN_Into_IDLE_Tick
 *
 * @brief   lin进入idle计数.
 *
 * @param   none
 *
 * @return  none
 */
void dLIN_Into_IDLE_Tick(void)
{
	lin->LIN_IntoIDLE_Tick();
}


/*
	lin pid校验
	id  0x00 0x01 0x02 0x03 0x04 0x05 0x06 0x07 0x08 0x09 0x0a 0x0b 0x0c 0x0d 0x0e 0x0f
	pid 0x80 0xc1 0x42 0x03 0xc4 0x85 0x06 0x47 0x08 0x49 0xca 0x8b 0x4c 0x0d 0x8e 0xcf

	id  0x10 0x11 0x12 0x13 0x14 0x15 0x16 0x17 0x18 0x19 0x1a 0x1b 0x1c 0x1d 0x1e 0x1f
	pid 0x50 0x11 0x92 0xd3 0x14 0x55 0xd6 0x97 0xd8 0x99 0x1a 0x5b 0x9c 0xdd 0x5e 0x1f

	id  0x20 0x21 0x22 0x23 0x24 0x25 0x26 0x27 0x28 0x29 0x2a 0x2b 0x2c 0x2d 0x2e 0x2f
	pid 0x20 0x61 0xe2 0xa3 0x64 0x25 0xa6 0xe7 0xa8 0xe9 0x6a 0x2b 0xec 0xad 0x2e 0x6f

	id  0x30 0x31 0x32 0x33 0x34 0x35 0x36 0x37 0x38 0x39 0x3a 0x3b 0x3c 0x3d 0x3e 0x3f
	pid 0xf0 0xb1 0x32 0x73 0xb4 0xf5 0x76 0x37 0x78 0x39 0xba 0xfb 0x3c 0x7d 0xfe 0xbf
*/
#define BIT(Data, Bit)	((Data >> Bit) & 0x01)


/*********************************************************************
 * @fn      dLIN_Cal_Pid
 *
 * @brief   lin计算pid.
 *
 * @param   none
 *
 * @return  none
 */
uint8_t dLIN_Cal_Pid(uint8_t Id)
{
	// P1 P0 ID5 ID4 ID3 ID2 ID1 ID0
	// P1 =!(ID1^ID3^ID4^ID5)
	// P0 = (ID0^ID1^ID2^ID4)
	uint8_t Pid = 0;
	uint8_t P1 = !(BIT(Id, 1) ^BIT(Id, 3) ^BIT(Id, 4) ^BIT(Id, 5));
	uint8_t P0 =   BIT(Id, 0) ^BIT(Id, 1) ^BIT(Id, 2) ^BIT(Id, 4);

    Pid  = (Id & 0x3F);
	Pid |= (P1<<7) | (P0<<6);

	return Pid;
}


/*********************************************************************
 * @fn      dLIN_Check_Pid
 *
 * @brief   lin校验pid.
 *
 * @param   none
 *
 * @return  none
 */
uint8_t dLIN_Check_Pid(uint8_t Pid)
{
	// P1 P0 ID5 ID4 ID3 ID2 ID1 ID0
	// P1 =!(ID1^ID3^ID4^ID5)
	// P0 = (ID0^ID1^ID2^ID4)
    uint8_t P0, P1;
	P1 = !(BIT(Pid, 1) ^BIT(Pid, 3) ^BIT(Pid, 4) ^BIT(Pid, 5));
    P0 =   BIT(Pid, 0) ^BIT(Pid, 1) ^BIT(Pid, 2) ^BIT(Pid, 4);

	if ((P1 == BIT(Pid, 7)) && (P0 == BIT(Pid, 6)))
	{
		return 1;
	}
	return 0;
}


/*********************************************************************
 * @fn      dLIN_Cal_CheckSum_Classic
 *
 * @brief   lin经典校验.
 *
 * @param   none
 *
 * @return  none
 */
uint8_t dLIN_Cal_CheckSum_Classic(uint8_t* Data, uint8_t Len)
{
  	uint8_t  i;
  	uint16_t CheckSum = 0;

	for(i=0; i<Len; i++)
	{
		CheckSum += Data[i];
		if (CheckSum >= 0x100)
		{
			CheckSum &= 0x00FF;
			CheckSum += 1;
		}
	}

	CheckSum ^= 0xFF;		// 取反
	return (uint8_t)CheckSum;
}


/*********************************************************************
 * @fn      dLIN_Cal_CheckSum_Enhance
 *
 * @brief   lin增强校验.
 *
 * @param   none
 *
 * @return  none
 */
uint8_t dLIN_Cal_CheckSum_Enhance(uint8_t Pid, uint8_t* Data, uint8_t Len)
{
	uint8_t  i;
	uint16_t CheckSum = Pid;

	for(i=0; i<Len; i++)
	{
		CheckSum += Data[i];

		if (CheckSum >= 0x100)
		{
			CheckSum &= 0x00FF;
			CheckSum += 1;
		}
	}

	CheckSum ^= 0xFF;		// 取反
	return (uint8_t)CheckSum;
}

2.Config_LIN.c、Cmd_LIN.h、Cmd_LIN.h

这个部分分出三个主要是因为lin着实有点难写,需要的内容有点多,在Cmd中调用Config的代码,实现解耦。

/*
 * Config_LIN.c
 *
 *  Created on: 2024/10/25
 *      Author: WZX
 */
 
 
#include "Cmd_LIN.h"


static void LIN_Intp_Interrupt(void);
static void LIN_Tim_Break_Interrupt(void);
static void LIN_Receive_Interrupt(void);
static void LIN_Transmit_Interrupt(void);


/*********************************************************************
 * @fn      LIN_Conifg_Init
 *
 * @brief   lin配置.
 *
 * @param   none
 *
 * @return  none
 */
void LIN_Conifg_Init(void)
{
    //这里放lin的引脚和检测同步间隔帧的初始化代码
}


/*********************************************************************
 * @fn      LIN_Wait_Break_Succ_Callback
 *
 * @brief   lin等待break信号成功回调函数.
 *
 * @param   none
 *
 * @return  none
 */
__weak void LIN_Wait_Break_Succ_Callback(void)
{

}


/*********************************************************************
 * @fn      LIN_Wait_Break_Fail_Callback
 *
 * @brief   lin等待break同步信号失败回调函数.
 *
 * @param   none
 *
 * @return  none
 */
__weak void LIN_Wait_Break_Fail_Callback(void)
{

}


/*********************************************************************
 * @fn      LIN_Receive_Error_Callback
 *
 * @brief   lin接收数据错误回调函数.
 *
 * @param   none
 *
 * @return  none
 */
__weak void LIN_Receive_Error_Callback(void)
{

}


/*********************************************************************
 * @fn      LIN_Receive_Callback
 *
 * @brief   lin接收数据回调函数.
 *
 * @param   none
 *
 * @return  none
 */
__weak void LIN_Receive_Callback(uint8_t Data)
{

}


/*********************************************************************
 * @fn      LIN_Transmit_Callback
 *
 * @brief   lin发送数据回调函数.
 *
 * @param   none
 *
 * @return  none
 */
__weak void LIN_Transmit_Callback(void)
{

}


//intp中断--作用未知
static void LIN_Intp_Interrupt(void)
{

}


//tim用作等待同步信号
static void LIN_Tim_Break_Interrupt(void)
{
  	static volatile uint32_t g_tm40_ch3_width = 0UL;
	INTC_ClearPendingIRQ(LIN_TIM_IRQ);

    //这个是检测同步间隔的长度
    g_tm40_ch3_width = (uint32_t)(TM40->TDR[3] + 1UL);
	
	if(g_tm40_ch3_width > LIN_BREAK_TIME) {
		LIN_Wait_Break_Succ_Callback();
		INTC_ClearPendingIRQ(LIN_UART_RX_IRQ);
		INTC_EnableIRQ(LIN_UART_RX_IRQ);
		TIM_Stop(TM40, TIM_Index_Channel3);
	} else {
		LIN_Wait_Break_Fail_Callback();
	}  
}

//lin接收数据中断
static void LIN_Receive_Interrupt(void)
{
	volatile uint8_t err_type;
	
	INTC_ClearPendingIRQ(LIN_UART_RX_IRQ);
	INTC_EnableIRQ(LIN_UART_RX_IRQ);
	
	err_type = UART_GetErrStaus(LIN_UART_BASE, UART_FLAG_FEF | UART_FLAG_PEF | UART_FLAG_OVF);
	if(err_type) {
		LIN_Receive_Error_Callback();
	}
	LIN_Receive_Callback(LIN_UART_RX_DATA);
}

//lin发送数据中断
static void LIN_Transmit_Interrupt(void)
{
	INTC_ClearPendingIRQ(LIN_UART_TX_IRQ);
	
	LIN_Transmit_Callback();
}


/*
 * Cmd_LIN.h
 *
 *  Created on: 2024/10/25
 *      Author: WZX
 */
 
 
#ifndef _CMD_LIN_H_
#define _CMD_LIN_H_


void Cmd_LIN_Init(void);
__weak void LIN_Conifg_Init(void);


#endif


/*
 * Cmd_LIN.c
 *
 *  Created on: 2024/10/25
 *      Author: WZX
 */
 
 
#include "Cmd_LIN.h"
#include "dLIN.h"
#include "Cmm_LIN.h"


static LIN_TypeDef_t lin_temp;
static Queue_LIN_Para *lin_rx_para;
static Queue_LIN_Para *lin_tx_para;
static Communicate_LIN_Message_Type *lin_cmm_message = NULL;
static uint8_t lin_function_size;

static void lin_receive_break(void);
static void lin_insleep(void);
static void lin_outsleep(void);
static void lin_into_idle_time(void);


/*********************************************************************
 * @fn      Cmd_LIN_Init
 *
 * @brief   lin初始化配置.
 *
 * @param   none
 *
 * @return  none
 */
void Cmd_LIN_Init(void)
{
	lin_rx_para = Calloc(1, sizeof(struct __Queue_LIN_Para));
	lin_tx_para = Calloc(1, sizeof(struct __Queue_LIN_Para));
	lin_cmm_message = Cmm_LIN_Get_Function();
	lin_function_size = Cmm_LIN_Get_Sizeof_Function();
	
	lin_temp.Bus_State = LIN_BUS_WAIT_BREAK;
	lin_temp.Bus_Voltage = LIN_BUS_VOLTAGE_NORMAL;
	lin_temp.Bus_Error.Value = 0;
	lin_temp.Bus_Is_Sending = 0;
	lin_temp.Bus_IDLE_Time = 0;
	lin_temp.LIN_Init = LIN_Conifg_Init;
	lin_temp.LIN_InSleep = lin_insleep;
	lin_temp.LIN_OutSleep = lin_outsleep;
	lin_temp.LIN_IntoIDLE_Tick = lin_into_idle_time;
	lin_temp.LIN_Wait_Break = lin_receive_break;
	dLIN_Init(&lin_temp);
}

//使能接收同步信号中断
static void lin_receive_break(void)
{
    //这个放同步间隔功能函数
}

//lin进入低功耗
static void lin_insleep(void)
{

}

//lin退出低功耗
static void lin_outsleep(void)
{

}

//lin进入空闲的时间
static void lin_into_idle_time(void)
{
	if(lin_temp.Bus_IDLE_Time < 0xFFF)
	{
		lin_temp.Bus_IDLE_Time++;
	}
}

//lin发送数据
static void lin_send_data(uint8_t ID)
{
	if(lin_temp.Bus_Is_Sending) {
		return;
	}
	if(lin_cmm_message[ID].Len == 0) {
		return;
	}
	
	lin_cmm_message[ID].Communicate_LIN((void *)lin_cmm_message[ID].pBuffer);
	if(lin_cmm_message[ID].ChkSumType == CLASSIC) {
		lin_cmm_message[ID].pBuffer[lin_cmm_message[ID].Len] = dLIN_Cal_CheckSum_Classic(lin_cmm_message[ID].pBuffer, \
																		lin_cmm_message[ID].Len);
	} else {
		lin_cmm_message[ID].pBuffer[lin_cmm_message[ID].Len] = dLIN_Cal_CheckSum_Enhance(dLIN_Cal_Pid(lin_cmm_message[ID].ID), \
																	lin_cmm_message[ID].pBuffer, lin_cmm_message[ID].Len);
	}
	lin_tx_para->ID = lin_cmm_message[ID].ID;
	lin_tx_para->Lenght = lin_cmm_message[ID].Len + 1;
	lin_tx_para->CheckSum_Type = lin_cmm_message[ID].ChkSumType;
	memcpy((uint8_t *)lin_tx_para->Buffer, lin_cmm_message[ID].pBuffer, lin_tx_para->Lenght);
	lin_temp.Bus_Is_Sending = 1;
	lin_receive_break();

	INTC_ClearPendingIRQ(UART0_IRQn);
	INTC_SetPendingIRQ(UART0_IRQn);
	INTC_EnableIRQ(UART0_IRQn);
}


/*********************************************************************
 * @fn      LIN_Conifg_Init
 *
 * @brief   lin初始化配置.
 *
 * @param   none
 *
 * @return  none
 */
__weak void LIN_Conifg_Init(void)
{

}


/*********************************************************************
 * @fn      LIN_Wait_Break_Succ_Callback
 *
 * @brief   lin等待break信号成功回调函数.
 *
 * @param   none
 *
 * @return  none
 */
void LIN_Wait_Break_Succ_Callback(void)
{
	lin_temp.Bus_State = LIN_BUS_WAIT_SYNC;
}


/*********************************************************************
 * @fn      LIN_Wait_Break_Fail_Callback
 *
 * @brief   lin等待break同步信号失败回调函数.
 *
 * @param   none
 *
 * @return  none
 */
void LIN_Wait_Break_Fail_Callback(void)
{
    if(lin_temp.Bus_State != LIN_BUS_SENDING_DATA) {
	    lin_temp.Bus_State = LIN_BUS_WAIT_BREAK;
    }
	lin_receive_break();
}


/*********************************************************************
 * @fn      LIN_Transmit_Callback
 *
 * @brief   lin发送数据回调函数.
 *
 * @param   none
 *
 * @return  none
 */
void LIN_Transmit_Callback(void)
{
	static uint8_t tx_count = 0;
	
	if(tx_count < lin_tx_para->Lenght) {
		UART0_TX = lin_tx_para->Buffer[tx_count++];
	} else {
		lin_temp.Bus_Is_Sending = 0;
		tx_count = 0;
		INTC_DisableIRQ(UART0_IRQn);
        //这个是主机读,从机发送完成后的,这个是用的队列,可以参考我之前的队列
		//Cmd_LIN_Rx_Push_aData(lin_tx_para);
	}
}


/*********************************************************************
 * @fn      LIN_Receive_Callback
 *
 * @brief   lin接收数据回调函数.
 *
 * @param   none
 *
 * @return  none
 */
void LIN_Receive_Callback(uint8_t Data)
{
	volatile uint8_t id, lin_tx_err = 0;
	static uint8_t rx_count = 0;
	static uint8_t tx_rxcb_count = 0;
	static uint8_t tx_rxcb_buffer[10];
	
	//lin总线清除空闲时间
	lin_temp.Bus_IDLE_Time = 0;
	switch(lin_temp.Bus_State) {
		//lin发送数据回读并校验
		case LIN_BUS_SENDING_DATA:
			tx_rxcb_buffer[tx_rxcb_count++] = Data;
			lin_tx_err = 0;
			if(tx_rxcb_count >= lin_tx_para->Lenght) {
				for(uint8_t i = 0; i < tx_rxcb_count; i++) {
					if(tx_rxcb_buffer[i] != lin_tx_para->Buffer[i]) {
						lin_tx_err = 1;
						break;
					}
				}
				if(lin_tx_err) {
					lin_temp.Bus_Error.Bit.Tx_ReadBack_Err = 1;
				} else {
					lin_temp.Bus_Error.Value = 0;
				}
                //将队列接收放接收数据完成
                //Cmd_LIN_Rx_Push_aData(lin_tx_para);
				lin_temp.Bus_State = LIN_BUS_WAIT_BREAK;
				lin_receive_break();
			}
			break;
			
		//lin当前正在接收同步帧0x55
		case LIN_BUS_WAIT_SYNC:
			if(Data == 0x55) {
				lin_temp.Bus_State = LIN_BUS_WAIT_PID;
			} else {
				lin_receive_break();
				lin_temp.Bus_Error.Bit.Rx_Sync_Err = 1;
				lin_temp.Bus_State = LIN_BUS_WAIT_BREAK;
			}
			break;
			
		//lin当前正在接收PID并且决定发送数据还是接收数据
		case LIN_BUS_WAIT_PID:
			if(dLIN_Check_Pid(Data)) {
				id = Data & 0x3F;
				for(uint8_t i = 0; i < lin_function_size; i++) {
					if(id == lin_cmm_message[i].ID) {
						if(lin_cmm_message[i].TransfDir == RECV) {
							lin_rx_para->ID = id;
							lin_rx_para->CheckSum_Type = lin_cmm_message[i].ChkSumType;
							lin_rx_para->Lenght = lin_cmm_message[i].Len;
							lin_temp.Bus_State = LIN_BUS_WAIT_DATA;
						} else {
							if(lin_temp.Bus_Voltage == LIN_BUS_VOLTAGE_NORMAL) {
								if(lin_temp.Bus_Is_Sending == 0) {
									if(lin_cmm_message[i].Len) {
										tx_rxcb_count = 0;
										lin_temp.Bus_State = LIN_BUS_SENDING_DATA;
										lin_send_data(i);
									} else {
										lin_temp.Bus_State = LIN_BUS_WAIT_BREAK;
										lin_receive_break();
									}
								}
							} else {
								lin_temp.Bus_State = LIN_BUS_WAIT_BREAK;
								lin_receive_break();
							}
						}
						return;
					}
				}
				lin_receive_break();
				lin_temp.Bus_State = LIN_BUS_WAIT_BREAK;
			}
			break;
			
		//lin当前正在接收数据
		case LIN_BUS_WAIT_DATA:
			lin_rx_para->Buffer[rx_count++] = Data;
			if(rx_count >= lin_rx_para->Lenght) {
				rx_count = 0;
				lin_temp.Bus_State = LIN_BUS_WAIT_CHECKSUM;
			}
			break;
			
		//lin当前做检验
		case LIN_BUS_WAIT_CHECKSUM:
			if(lin_rx_para->CheckSum_Type == CLASSIC) {
				if(Data == dLIN_Cal_CheckSum_Classic((uint8_t *)&lin_rx_para->Buffer, lin_rx_para->Lenght)) {
					//Cmd_LIN_Rx_Push_aData(lin_rx_para);
				} else {
					lin_temp.Bus_Error.Bit.Rx_Check_Sum_Err = 1;
				}
			} else {
				if(Data == dLIN_Cal_CheckSum_Enhance(dLIN_Cal_Pid(lin_rx_para->ID), \
							(uint8_t *)&lin_rx_para->Buffer, lin_rx_para->Lenght)) {
					//Cmd_LIN_Rx_Push_aData(lin_rx_para);
				} else {
					lin_temp.Bus_Error.Bit.Rx_Check_Sum_Err = 1;
				}
			}
			lin_temp.Bus_State = LIN_BUS_WAIT_BREAK;
			lin_receive_break();
			break;
		default:
			break;
	}
}


3.Task_LIN.h、Task_LIN.c

这个是lin的主任务,我是放在10ms的时间片上

/*
 * Task_LIN.h
 *
 *  Created on: 2024/10/25
 *      Author: WZX
 */
 
 
#ifndef _TASK_LIN_H_
#define _TASK_LIN_H_


void Task_LIN(void);


#endif


/*
 * Task_LIN.c
 *
 *  Created on: 2024/10/25
 *      Author: WZX
 */
 
 
#include "Task_LIN.h"
#include "Cmd_Queue.h"
#include "Cmm_LIN.h"
#include "dLIN.h"


/*********************************************************************
 * @fn      Task_LIN
 *
 * @brief   lin任务.
 *
 * @param   none
 *
 * @return  none
 */
void Task_LIN(void)
{
	#define Task_LIN_Is_Stop_Time			100
	#define Task_LIN_Is_IntoSleep_Time		400
	
    //这里也是队列,参考之前的文章    
	static Queue_LIN_Para rx_temp;
	
	
	//需要添加检测12v电源检测
	
	if(Cmd_LIN_Rx_Pop_aData(rx_temp) == 0)
	{
		if(Cmm_LIN_Set_Function(rx_temp.ID, (void *)&rx_temp.Buffer) != 0)
		{
			
		}
	}
	
	dLIN_Into_IDLE_Tick();
	if(dLIN_Get_IDLE_Time() > Task_LIN_Is_Stop_Time)
	{
		//超时
	}
	if(dLIN_Get_IDLE_Time() > Task_LIN_Is_IntoSleep_Time)
	{
		//进入低功耗
	}
	else
	{
		//退出低功耗
	}
}


4.Cmm_LIN.h、Cmm_LIN.c、Cmm_LIN_DBL.h

/*
 * Cmm_LIN.h
 *
 *  Created on: 2024/10/25
 *      Author: WZX
 */
 
 
#ifndef _CMM_LIN_H_
#define _CMM_LIN_H_


#include <stdint.h>
#include "dLIN.h"


typedef struct
{
	uint8_t ID;								// ID
	uint8_t Len;							// 数据长度
	LIN_TRANSFER_TYPE TransfDir;			// 传输方向
	LIN_CHECKSUM_TYPE ChkSumType;			// 校验类型
	uint8_t *pBuffer;
	void (*Communicate_LIN)(void *Buf);		// 帧ID回调函数
} Communicate_LIN_Message_Type;


void *Cmm_LIN_Get_Function(void);
uint8_t Cmm_LIN_Get_Sizeof_Function(void);
uint8_t Cmm_LIN_Set_Function(uint8_t ID, void *Buf);


#endif


/*
 * Cmm_LIN_DBL.h
 *
 *  Created on: 2024/10/25
 *      Author: WZX
 */
 
 
#ifndef _CMM_LIN_DBL_H_
#define _CMM_LIN_DBL_H_


#include <stdint.h>


#define Cmm_LIN_DBL_Max_Buf			8


/**
 *  @brief 0x3C -- 测试接收数据
 */
typedef union
{
	uint8_t Buffer[Cmm_LIN_DBL_Max_Buf];
	struct 
	{
		struct
		{
			uint8_t Valve :8;
		} Data1;
		
		struct
		{
			uint8_t Valve :8;
		} Data2;
		
		struct
		{
			uint8_t Valve :8;
		} Data3;
		
		struct
		{
			uint8_t Valve :8;
		} Data4;
		
		struct
		{
			uint8_t Valve :8;
		} Data5;
		
		struct
		{
			uint8_t Valve :8;
		} Data6;
		
		struct
		{
			uint8_t Valve :8;
		} Data7;
		
		struct
		{
			uint8_t Valve :8;
		} Data8;
	} bit;
} Cmm_LIN_DBL_0x3C_RecTest;


/**
 *  @brief 0x3D -- 测试发送数据
 */
typedef union
{
	uint8_t Buffer[Cmm_LIN_DBL_Max_Buf];
	struct 
	{
		struct
		{
			uint8_t Valve :8;
		} Data1;
		
		struct
		{
			uint8_t Valve :8;
		} Data2;
		
		struct
		{
			uint8_t Valve :8;
		} Data3;
		
		struct
		{
			uint8_t Valve :8;
		} Data4;
		
		struct
		{
			uint8_t Valve :8;
		} Data5;
		
		struct
		{
			uint8_t Valve :8;
		} Data6;
		
		struct
		{
			uint8_t Valve :8;
		} Data7;
		
		struct
		{
			uint8_t Valve :8;
		} Data8;
	} bit;
} Cmm_LIN_DBL_0x3D_SendTest;


#endif


/*
 * Cmm_LIN.c
 *
 *  Created on: 2024/10/25
 *      Author: WZX
 */
 
 
#include "Cmm_LIN.h"
#include "Cmm_LIN_DBL.h"
#include "Wos_Debug.h"
#include <stddef.h>


/*!< UART功能命令函数 */
static void Cmm_LIN_Receive_Test(void *Buf);
static void Cmm_LIN_Transmit_Test(void *Buf);


/*!< LIN功能命令列表 */
static uint8_t Cmm_LIN_Transmit_Finish = 0;
static uint8_t Cmm_LIN_Receive_Test_Buffer[8];
static uint8_t Cmm_LIN_Transmit_Test_Buffer[8 + 1];		//发送数据的时候需要多加一个字节做校验
static const Communicate_LIN_Message_Type CMM_LIN[] = 
{
//	ID			收发长度		传输方向		校验类型			存储参数								功能函数
	{0x3C, 		8,			RECV,		CLASSIC,		&Cmm_LIN_Receive_Test_Buffer[0],	Cmm_LIN_Receive_Test},
	{0x3D,		8,			SEND,		CLASSIC,		&Cmm_LIN_Transmit_Test_Buffer[0],	Cmm_LIN_Transmit_Test},
};


/*********************************************************************
 * @fn      Cmm_LIN_Get_Function
 *
 * @brief   LIN获取功能首地址.
 *
 * @param   none
 *
 * @return  none
 */
void *Cmm_LIN_Get_Function(void)
{
	return (void *)&CMM_LIN;
}


/*********************************************************************
 * @fn      Cmm_LIN_Get_Sizeof_Function
 *
 * @brief   LIN获取当前的帧ID数量.
 *
 * @param	ID:功能ID号.
 *
 * @param   Buf:接收数据帧.
 *
 * @return  Command_StatusTypeDef:状态标志位
 */
uint8_t Cmm_LIN_Get_Sizeof_Function(void)
{
	return sizeof(CMM_LIN)/sizeof(CMM_LIN[0]);
}


/*********************************************************************
 * @fn      Cmd_LIN_Set_Function
 *
 * @brief   LIN功能命令设置函数(只用于接收lin的命令帧).
 *
 * @param	ID:功能ID号.
 *
 * @param   Buf:接收数据帧.
 *
 * @return  Command_StatusTypeDef:状态标志位
 */
uint8_t Cmm_LIN_Set_Function(uint8_t ID, void *Buf)
{
	for(uint8_t i = 0; i < (sizeof(CMM_LIN)/sizeof(CMM_LIN[0])); i++)
	{
		if(ID == CMM_LIN[i].ID)
		{
			if(CMM_LIN[i].Communicate_LIN != NULL)
			{
				if(CMM_LIN[i].TransfDir == RECV)
				{
					memcpy((uint8_t *)CMM_LIN[i].pBuffer, Buf, CMM_LIN[i].Len);
				}
				else
				{
					Cmm_LIN_Transmit_Finish = 1;
				}
				CMM_LIN[i].Communicate_LIN((void *)Buf);
				Cmm_LIN_Transmit_Finish = 0;
			}
			return 0;
		}
	}
	return 1;
}


/*********************************************************************
 * @fn      Cmm_LIN_Receive_Test
 *
 * @brief   LIN接收数据测试.
 *
 * @param   Buf:接收数据帧
 *
 * @return  none
 */
static void Cmm_LIN_Receive_Test(void *Buf)
{
	Cmm_LIN_DBL_0x3C_RecTest *temp = (Cmm_LIN_DBL_0x3C_RecTest *)Buf;

	Debug("lin 0x3c data is %x %x %x %x %x %x %x %x", temp->bit.Data1.Valve, temp->bit.Data2.Valve, temp->bit.Data3.Valve, \
	temp->bit.Data4.Valve, temp->bit.Data5.Valve, temp->bit.Data6.Valve, temp->bit.Data7.Valve, temp->bit.Data8.Valve);
}


/*********************************************************************
 * @fn      Cmm_LIN_Transmit_Test
 *
 * @brief   LIN发送数据测试.
 *
 * @param   Buf:接收数据帧
 *
 * @return  none
 */
static void Cmm_LIN_Transmit_Test(void *Buf)
{
	Cmm_LIN_DBL_0x3D_SendTest *temp = (Cmm_LIN_DBL_0x3D_SendTest *)Buf;
	
	if(!Cmm_LIN_Transmit_Finish)
	{//这里是发送数据时需要处理的任务
		for(uint8_t i = 0; i < 8; i++)
		{
			temp->Buffer[i] = Cmm_LIN_Receive_Test_Buffer[i] + 1;
		}
	}
	else
	{//这里是发送数据完成需要处理的任务
		Debug("lin 0x3D data is %x %x %x %x %x %x %x %x", temp->bit.Data1.Valve, temp->bit.Data2.Valve, temp->bit.Data3.Valve, \
		temp->bit.Data4.Valve, temp->bit.Data5.Valve, temp->bit.Data6.Valve, temp->bit.Data7.Valve, temp->bit.Data8.Valve);
	}
}


<think>好的,我现在需要解决LIN通信中的读超时问题。首先,我得了解LIN总线的基本原理和常见故障点。LIN(Local Interconnect Network)是一种低成本的车载网络协议,通常用于车身控制模块,如车窗、座椅调节等。它采用单线传输,主从结构,主节点控制通信时序,从节点响应。 读超时可能由多种因素引起。首先,硬件连接问题,比如线缆损坏、接触不良或终端电阻不正确。LIN总线需要终端电阻来匹配阻抗,通常主节点会集成一个1kΩ的电阻,从节点可能也有,如果电阻值不对,信号反射会导致通信错误。其次,软件配置问题,比如波特率设置错误、帧ID配置不匹配或超时时间设置过短。波特率标准为1kbps到20kbps,不同车型可能不同,必须严格匹配。另外,主节点发送的帧头从节点响应的间隔时间(Tresp)如果超过规范,也会导致超时。 接下来想到的是信号干扰问题。LIN总线容易受到电磁干扰,尤其是在发动机舱等环境复杂的区域。检查线路是否靠近高压线或电机,必要时增加屏蔽或调整布线路径。此外,电源电压不稳也可能导致节点工作异常,用万用表测量供电电压是否在正常范围(通常9-18V)。 然后是诊断工具的使用。通过LIN分析仪或示波器抓取总线波形,观察主节点发送的帧头和从节点的响应是否符合规范。如果从节点没有响应,可能是从节点故障或软件逻辑错误。如果响应存在但数据错误,需检查数据校验(如经典校验或增强校验)是否正确应用。例如,校验和错误会导致主节点丢弃响应数据,误认为超时。 另外,软件层面的超时处理机制需要检查。比如,主节点在发送帧头后是否等待足够时间(Tresp_max通常为1.4ms)让从节点响应。如果超时时间设置过短,即使从节点正常响应,主节点也可能提前判定超时。此外,检查是否有其他任务阻塞了通信进程,导致响应处理延迟。 还有可能从节点进入错误状态,比如过温保护或看门狗复位,导致暂时不响应。此时需要检查从节点的状态寄存器或日志,确认是否有错误标志置位。如果是固件问题,可能需要更新从节点的程序。 最后,考虑网络负载问题。虽然LIN总线是低速网络,但如果主节点频繁调度多个帧,可能导致响应延迟累积。优化调度表,合理安排帧的发送顺序和时间间隔,避免冲突。 总结下来,解决步骤可能包括:检查物理连接和终端电阻,验证波特率和帧配置,使用工具分析信号质量,测试电源稳定性,检查超时参数,排查节点故障,优化调度策略。需要逐步排除可能因素,从硬件到软件,从配置到环境,系统性地诊断。</think>### LIN通信读超时问题解决方案 #### 1. **检查物理层连接** - **线缆接头**:确认LIN总线线缆无破损、接触不良,尤其是连接器氧化或松脱问题。使用万用表测量总线电压,正常空闲时应为电池电压(约12V),通信时会有明显波动[^1]。 - **终端电阻**:主节点通常集成1kΩ电阻,从节点可能为30kΩ。总线上等效电阻应接近1kΩ(主节点电阻从节点并联)。若电阻异常,需检查节点是否损坏。 #### 2. **验证通信参数配置** - **波特率匹配**:确保主从节点波特率一致(典型值如19.2kbps)。通过示波器测量位时间(如52μs对应19.2kbps)验证实际速率。 - **帧格式ID**:检查主节点发送的帧ID是否从节点订阅的ID匹配。例如,主节点发送$ID=0x22$,从节点需配置为响应此ID。 #### 3. **信号质量分析** - **示波器捕获波形**:观察主节点发送的帧头(包含同步间隔、同步字段、PID)是否完整,从节点响应是否存在延迟或畸变。正常响应应在帧头后1ms内返回。 - **抗干扰措施**:若波形存在毛刺或震荡,需增加屏蔽层或调整布线远离干扰源(如电机、高压线)。 #### 4. **超时机制优化** - **调整超时阈值**:主节点等待从节点响应的超时时间(如从默认10ms延长至15ms),需根据实际响应时间$T_{resp}$调整。计算公式: $$ T_{timeout} \geq T_{resp\_max} + T_{margin} $$ 其中$T_{margin}$为安全余量(通常2-3ms)。 - **错误重试机制**:配置主节点在超时后自动重发帧头(最多3次),避免偶发干扰导致的误判。 #### 5. **节点状态诊断** - **从节点供电测试**:测量从节点供电电压是否稳定(如9-16V)。低压可能导致节点复位。 - **节点日志分析**:通过诊断工具(如UDS协议)读取从节点的故障码,确认是否存在硬件故障(如过温、短路)。 #### 6. **软件逻辑排查** - **任务优先级调整**:若使用RTOS,确保通信任务优先级高于非实时任务,避免因任务阻塞导致响应延迟。 - **校验和验证**:检查数据校验算法(如增强校验和包含PID数据),若校验错误,主节点会丢弃响应,表现为超时。 #### 示例代码(波特率配置) ```c // 配置LIN节点波特率为19.2kbps void LIN_ConfigureBaudRate() { uint32_t system_clock = 48000000; // 48MHz系统时钟 uint32_t prescaler = system_clock / (16 * 19200) - 1; // 计算分频值 LIN_REGISTER.BAUDRATE = prescaler; // 写入硬件寄存器 } ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值