MIMX RT1064通过LPUART实现LIN通信

        终于调完了RT0164平台的LIN通信,花点时间整理一下资料。我在网上找了很多关与uart实现LIN通信的资料,很多是STM芯片的,还有一些是RT其他芯片的,具有参考意义,但是不能完全适用1064的开发,最开始是打算使用Flexio是实现LIN通信的,网上也找到一篇文章,链接:https://www.nxp.com/docs/zh/application-note/AN12788.pdf,但是输出波形比uart TX输出的波形缩小了几倍,且波形是尖峰,逻辑分析仪无法识别该波形,估计是一些驱动的配置没弄好,起初还以为是硬件存在问题,修改了还是一样,遂鼓捣了一阵还是放弃了。后面用LPUART实现了。

先补充一下LIN的基本知识(其实网上也有很多,大概说一下就不一一细说啦)

        帧(Frame)包含帧头(Header)和应答(Response)两部分。主机任务负责发送帧头;从机任务接收帧头并对帧头所包含信息进行解析,然后决定是发送应答,还是接收应答,还是不作任何反应。帧在总线上的传输如图所示。

        帧头包括同步间隔段、同步段以及PID(Protected Identifier,受保护ID)段,应答包括数据段和校验和段,如图所示

 

同步间隔段(Break Field)
        同步间隔段由同步间隔(Break)和同步间隔段间隔符(Break Delimiter)构成,如图所示。同步间隔是至少持续 13 位(以主机节点的位速率为准)的显性电平。只有主节点需要发送间隔场,从节点接收间隔场。

        有写芯片可直接支持Break场的输出,直接调用函数即可,但是有些芯片不支持,如我使用的RT1064,需要通过UART模拟LIN通信,但是它没有相关库函数,无法直接产生这样一个显性序列,可以有两种方法实现间隔场的发送。

方法一:例如总线的正常通信波特率为9.6k,从机节点始终以此波特率工作,主机节点在需要发送同步间隔场时将波特率降低到4.8k,并发送0x00,则工作于9.6k 波特率的从机节点将检测到18个连续的显性位,从而判断一个新报文帧的到来。主机节点在发送完同步间隔场后将波特率恢复到9.6k。

        我首先使用方法一实现了间隔场的发送,但是有时候逻辑分析仪无法正确识别出来,使用这种方法发出的波形,空闲时间会等待比较长才发送间隔场,如下波形:

 方法二:先配置好LPUART的初始化,然后加入下面的代码段配置LIN的收发,通过直接操作寄存器去配置,重点是LPUART2->STAT |= LPUART_STAT_BRK13_MASK这一句代码。

void uart_LIN_break(void)
{
	LPUART2->CTRL &= ~(LPUART_CTRL_TE_MASK | LPUART_CTRL_RE_MASK); //Disable UART2 first
	LPUART2->STAT |= LPUART_STAT_BRK13_MASK; //13 bit times,LPUART_STAT_BRK13(1)
	LPUART2->STAT |= LPUART_STAT_LBKDE_MASK;//LIN break detection enable
	LPUART2->BAUD |= LPUART_BAUD_LBKDIE_MASK;//LIN Break Detect Interrupt Enable
	LPUART2->CTRL |= (LPUART_CTRL_TE_MASK | LPUART_CTRL_RE_MASK);
	LPUART2->CTRL |= LPUART_CTRL_RIE_MASK; //Receiver Interrupt Enable
	EnableIRQ(LPUART2_IRQn);
}

 然后直接在自己的代码中直接调用DEMO_LPUART->DATA = 0xE000;就可以发送间隔场了,这些代码为我自己封装,后面等我整理好代码将完整的放上来。

			/*发送间隔段*/
			DEMO_LPUART->DATA = 0xE000;

			/*发送同步段*/
			LIN_SendSync(DEMO_LPUART, &LinMsg, sizeof(LinMsg.FrameSync));
			/*发送受保护ID*/
			LIN_SendPID(DEMO_LPUART, &LinMsg, sizeof(LinMsg.FrameID));
			/*发送数据段*/
			LIN_SendData(DEMO_LPUART, &LinMsg, LinMsg.length);
			/*发送校验和段*/
			LIN_SendCheckSum(DEMO_LPUART, &LinMsg, sizeof(LinMsg.CheckSum));

使用方法二实现的波形如下:逻辑分析仪可以正确识别。

         主节点最难的就是间隔场的发送了,后面的同步场、PID场、数据场、校验和场这些就比较简单了,都是按照UART的格式去发送的,包含1起始位+8数据+1停止位。PID和校验和场的计算大家就可以找其他的资料啦,太多人说这些部分的了。希望间隔场部分对大家的开发有帮助,后续有时间再上传完整的代码给大家参考。

分割线=========================================================

完整代码:

/*******************************************************************************
 * Definitions
 ******************************************************************************/
#define DEMO_LPUART            LPUART2
#define DEMO_LPUART_CLK_FREQ   BOARD_DebugConsoleSrcFreq()
#define DEMO_LPUART_IRQn       LPUART2_IRQn
#define DEMO_LPUART_IRQHandler LPUART2_IRQHandler

/*! @brief Ring buffer size (Unit: Byte). */
#define DEMO_RING_BUFFER_SIZE 8

/*! @brief Ring buffer to save received data. */

/*******************************************************************************
 * Prototypes
 ******************************************************************************/

/*******************************************************************************
 * Variables
 ******************************************************************************/

uint8_t g_tipString[] =
    "Lpuart functional API interrupt example\r\nBoard receives characters then sends them out\r\nNow please input:\r\n";

/*
  Ring buffer for data input and output, in this example, input data are saved
  to ring buffer in IRQ handler. The main function polls the ring buffer status,
  if there are new data, then send them out.
  Ring buffer full: (((rxIndex + 1) % DEMO_RING_BUFFER_SIZE) == txIndex)
  Ring buffer empty: (rxIndex == txIndex)
*/
uint8_t demoRingBuffer[DEMO_RING_BUFFER_SIZE];
volatile uint16_t txIndex; /* Index of the data to send out. */
volatile uint16_t rxIndex; /* Index of the memory to save new arrived data. */

/*******************************************************************************
 * Code
 ******************************************************************************/

typedef struct
{
	uint8_t FrameBreak;
	uint8_t FrameSync;
	uint8_t FrameID;
	uint8_t FrameData[8];
	uint8_t CheckSum;
	uint8_t length;
}LINData_t;

enum
{
	BREAK = 0,
	SYNC,
	PID,
	DATA,
	CHECKSUM,
	SENDDATA
}LINstatus_t;

#define Length 8U

uint8_t state;
lpuart_config_t config;
LINData_t LinRecvMsg;

void uart_LIN_break(void);
uint8_t LINCalcParity(uint8_t id);
uint8_t LINCheckSum(uint8_t PID, uint8_t *buf, uint8_t lens);

void LIN_SendBreak(LPUART_Type *base, LINData_t *txData, size_t txSize);
void LIN_SendSync(LPUART_Type *base, LINData_t *txData, size_t txSize);
void LIN_SendPID(LPUART_Type *base, LINData_t *txData, size_t txSize);
void LIN_SendData(LPUART_Type *base, LINData_t *txData, size_t txSize);
void LIN_SendCheckSum(LPUART_Type *base, LINData_t *txData, size_t txSize);

void DEMO_LPUART_IRQHandler(void)
{
	uint8_t data;
    uint16_t tmprxIndex = rxIndex;
    uint16_t tmptxIndex = txIndex;

    /* If new data arrived. */
    if ((kLPUART_RxDataRegFullFlag)&LPUART_GetStatusFlags(DEMO_LPUART))
    {
        data = LPUART_ReadByte(DEMO_LPUART);

        /* If ring buffer is not full, add data to ring buffer. */
        if (((tmprxIndex + 1) % DEMO_RING_BUFFER_SIZE) != tmptxIndex)
        {
            demoRingBuffer[rxIndex] = data;
            rxIndex++;
            rxIndex %= DEMO_RING_BUFFER_SIZE;
        }
    }
    SDK_ISR_EXIT_BARRIER;
}

void uart_LIN_break(void)
{
	LPUART2->CTRL &= ~(LPUART_CTRL_TE_MASK | LPUART_CTRL_RE_MASK); //Disable UART2 first
	LPUART2->STAT |= LPUART_STAT_BRK13_MASK; //13 bit times,LPUART_STAT_BRK13(1)
	LPUART2->STAT |= LPUART_STAT_LBKDE_MASK;//LIN break detection enable
	LPUART2->BAUD |= LPUART_BAUD_LBKDIE_MASK;//LIN Break Detect Interrupt Enable
	LPUART2->CTRL |= (LPUART_CTRL_TE_MASK | LPUART_CTRL_RE_MASK);
	LPUART2->CTRL |= LPUART_CTRL_RIE_MASK; //Receiver Interrupt Enable
}

/*!
 * @brief Main function
 */
int main(void)
{
    uint16_t tmprxIndex = rxIndex;
    uint16_t tmptxIndex = txIndex;
    LINData_t LinMsg;
    uint32_t i;

    BOARD_ConfigMPU();
    BOARD_InitPins();
    BOARD_BootClockRUN();

    /*
     * config.baudRate_Bps = 115200U;
     * config.parityMode = kLPUART_ParityDisabled;
     * config.stopBitCount = kLPUART_OneStopBit;
     * config.txFifoWatermark = 0;
     * config.rxFifoWatermark = 0;
     * config.enableTx = false;
     * config.enableRx = false;
     */
    LPUART_GetDefaultConfig(&config);
    config.baudRate_Bps = 9600U;
    config.enableTx     = true;
    config.enableRx     = true;

    LPUART_Init(DEMO_LPUART, &config, DEMO_LPUART_CLK_FREQ);

    /* Send g_tipString out. */
//    LPUART_WriteBlocking(DEMO_LPUART, g_tipString, sizeof(g_tipString) / sizeof(g_tipString[0]));

    /* Enable RX interrupt. */
    LPUART_EnableInterrupts(DEMO_LPUART, kLPUART_RxDataRegFullInterruptEnable);

    uart_LIN_break();//LIN Break config
    EnableIRQ(DEMO_LPUART_IRQn);

    //SendData
    LinMsg.FrameBreak = 0x00;
    LinMsg.FrameSync = 0x55;
    LinMsg.FrameID = 0x0E;
    LinMsg.FrameData[0] = 0x01;
    LinMsg.FrameData[1] = 0x02;
    LinMsg.FrameData[2] = 0x03;
    LinMsg.FrameData[3] = 0x04;
    LinMsg.FrameData[4] = 0x05;
    LinMsg.FrameData[5] = 0x06;
    LinMsg.FrameData[6] = 0x07;
    LinMsg.FrameData[7] = 0x08;
    LinMsg.length = 8;

    while(1)
    {
        for(i = 0; i < 800000; i++);

	//  config.baudRate_Bps = 4800U;
	//  LPUART_Init(DEMO_LPUART, &config, DEMO_LPUART_CLK_FREQ);

        /*发送间隔段*/
	//  LIN_SendBreak(DEMO_LPUART, &LinMsg, sizeof(LinMsg.FrameBreak));
		DEMO_LPUART->DATA = 0xE000;

		/*发送同步段*/
		LIN_SendSync(DEMO_LPUART, &LinMsg, sizeof(LinMsg.FrameSync));
		/*发送受保护ID*/
		LIN_SendPID(DEMO_LPUART, &LinMsg, sizeof(LinMsg.FrameID));
		/*发送数据段*/
		LIN_SendData(DEMO_LPUART, &LinMsg, LinMsg.length);
		/*发送校验和段*/
		LIN_SendCheckSum(DEMO_LPUART, &LinMsg, sizeof(LinMsg.CheckSum));
    }
}

void LIN_SendBreak(LPUART_Type *base, LINData_t *txData, size_t txSize)
{
	LPUART_WriteBlocking(base, &txData->FrameBreak, txSize);

    config.baudRate_Bps = 9600U;
    LPUART_Init(DEMO_LPUART, &config, DEMO_LPUART_CLK_FREQ);
}

/*发送同步段*/
void LIN_SendSync(LPUART_Type *base, LINData_t *txData, size_t txSize)
{
	LPUART_WriteBlocking(base, &txData->FrameSync, txSize);
}

/*发送受保护ID段, 6data + 2parity*/
void LIN_SendPID(LPUART_Type *base, LINData_t *txData, size_t txSize)
{
	uint8_t PID;

	PID = LINCalcParity(txData->FrameID);
	LPUART_WriteBlocking(base, &PID, txSize);
}
/*发送数据段*/
void LIN_SendData(LPUART_Type *base, LINData_t *txData, size_t txSize)
{
	LPUART_WriteBlocking(base, txData->FrameData, txSize);
}

/*发送校验和段*/
void LIN_SendCheckSum(LPUART_Type *base, LINData_t *txData, size_t txSize)
{
	uint8_t PID;

	PID = LINCalcParity(txData->FrameID);
	txData->CheckSum = LINCheckSum(PID, txData->FrameData, txData->length);

	LPUART_WriteBlocking(base, &txData->CheckSum, txSize);
}

/*校验和*/
uint8_t LINCheckSum(uint8_t PID, uint8_t *buf, uint8_t lens)
{
    uint8_t i, ckm = 0;
//    uint16_t chm1 = PID;//Enhanced
    uint16_t chm1 = 0;//Classic
    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=((id & 0x01)^((id & 0x02) >> 1)^((id & 0x04) >> 2)^((id & 0x10) >> 4)) << 6;//偶校验位
    p1=(!((id & 0x02) >> 1)^((id & 0x08) >> 3)^((id & 0x10) >> 4)^((id & 0x20) >> 5)) << 7;//奇校验位

    parity|=(p0|p1);

    return parity;
}

  • 0
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值