NXP i.MX RT1064串口Lpuart + DMA +空闲中断实现

本文讲述了作者在使用NXPLPUART进行串口通信时,如何优化中断接收、解决资料稀缺问题,以及如何配置DMA和空闲中断以实现不定长接收。作者分享了代码示例和初次使用新库的心得体会。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

一、为什么要做有什么用

1.单独的单字节串口进中断接收在数据量大的时候效率会低很多

2.不定长接收的时候会很麻烦,必须要双方都定义好一套收发协议,这样的好处可以不管发送端发什么,接收端只需要自己拿数据判断是不是自己想要的,或者自己判断是自己要的哪一个数据。

二、做的过程中遇到的困难以及解决办法

1.NXP的这款芯片除了官方的EVK倒是没有像正点或者野火做的stm32那样子有视频和很多历程,甚至于很难搜索到关于RT1064的文章,更多的是RT1052,做的时候找资料很难找到。

2.参考了野火的RT1052的例程,发现有个问题,他的例程没办法做到不定长,必须要接收区满才会中断。去看官方的例程发现野火就是照搬的。后面思考发现,这段历程里面缺了空闲中断,于是采用在空闲中断里面去读取数据。

3.由于NXP的sdk库,官网的API介绍是英文的,很难看懂,所以一个一个去底层测试那些变量的返回值,以及判断中断响应方式花了很多时间。但是呢路就一条,无非就是配置串口->打开中断的空闲中断模式->配置DMA数据源地址,传输字节数,循环还是不循环。。。。。与stm32的流程大差不差。只不过一个新的库,毕竟是两个不同的团队写的。最重要的就是找到对应的库函数,已经各个中断所需要的标志位去判断罢了。

三、最终代码

1.Lpuart.c的代码:主要是初始化,以及定义结构体数据缓存

#include "Hl_Lpuart.h"
#include "zf_driver_uart.h"
#include "Pad_Config.h"


//收发缓冲区
AT_NONCACHEABLE_SECTION_INIT(uint8_t g_rxBuffer[ECHO_BUFFER_LENGTH]) = {0};

/*句柄定义*/
lpuart_edma_handle_t g_lpuartEdmaHandle;     //串口DMA传输句柄
edma_handle_t        g_lpuartTxEdmaHandle;   //串口DMA发送句柄
edma_handle_t        g_lpuartRxEdmaHandle;   //串口DMA接收句柄

Lpuart_DMA_Type U_1;



/* 初始化串口所使用的DMA-----------------------------------------------------------------------------------------------------------------*/
void LPUART_DMA_Init(_Bool LPUART_DMA_Mode)
{
	if(LPUART_DMA_Mode == 1){
			lpuart_config_t LpuartConfig;
			
		  //串口的引脚基础配置初始化
			LPUART_GPIO_Init(LPUART1_TX_GPIO, LPUART1_TX_GPIO_PIN, LPUART1_RX_GPIO, LPUART1_RX_GPIO_PIN, LPUART1_TX_IOMUXC, LPUART1_RX_IOMUXC);
			
			/* 获取LPUART默认配置 */
			LPUART_GetDefaultConfig(&LpuartConfig);
			
			/* 在默认配置基础上继续配置参数 */
			LpuartConfig.baudRate_Bps = 115200;
			LpuartConfig.enableTx     = true;
			LpuartConfig.enableRx     = true;
			LpuartConfig.rxIdleConfig = kLPUART_IdleCharacter1;
			LpuartConfig.rxIdleType   = kLPUART_IdleTypeStopBit;
			LPUART_Init(DEMO_LPUART, &LpuartConfig, DEMO_LPUART_CLK_FREQ);

			//配置串口参数的时候,rxIdleConfig和rxIdleType 我设置成了从停止位开始检测,连续检测到一个字符的空闲时触发空闲中断,
			//不过这两个完全按照默认的设置也能实现功能,目前还不清楚为什么。
			
			/* 清除可能残留的标志位,并使能空闲中断 */
			LPUART_ClearStatusFlags(DEMO_LPUART, kLPUART_IdleLineFlag);
			LPUART_EnableInterrupts(DEMO_LPUART, kLPUART_IdleLineInterruptEnable);
			

			edma_config_t config;
			
			/*初始化DMAMUX */
			DMAMUX_Init(LPUART_DMAMUX_BASEADDR);
			/* 为LPUART设置DMA传输通道 */
			DMAMUX_SetSource(LPUART_DMAMUX_BASEADDR, LPUART_TX_DMA_CHANNEL, LPUART_TX_DMA_REQUEST);
			DMAMUX_SetSource(LPUART_DMAMUX_BASEADDR, LPUART_RX_DMA_CHANNEL, LPUART_RX_DMA_REQUEST);
			DMAMUX_EnableChannel(LPUART_DMAMUX_BASEADDR, LPUART_TX_DMA_CHANNEL);
			DMAMUX_EnableChannel(LPUART_DMAMUX_BASEADDR, LPUART_RX_DMA_CHANNEL);
			
			/* 初始化DMA */
			EDMA_GetDefaultConfig(&config);
			EDMA_Init(LPUART_DMA_BASEADDR, &config);
			
			/* 创建eDMA传输句柄 */
			EDMA_CreateHandle(&g_lpuartTxEdmaHandle, LPUART_DMA_BASEADDR, LPUART_TX_DMA_CHANNEL);
			EDMA_CreateHandle(&g_lpuartRxEdmaHandle, LPUART_DMA_BASEADDR, LPUART_RX_DMA_CHANNEL);
			
			/* 创建 LPUART DMA 句柄 */
			LPUART_TransferCreateHandleEDMA(DEMO_LPUART, &g_lpuartEdmaHandle, NULL, NULL, &g_lpuartTxEdmaHandle, &g_lpuartRxEdmaHandle);

			
			/* 打开串口中断 */
			//EnableIRQ(LPUART1_IRQn);
			uart_rx_interrupt(UART_1, ZF_ENABLE);                              // 开启 UART_INDEX 的接收中断
			NVIC_SetPriority(LPUART1_IRQn, 6);      
			
			/* 启动传输 */
			U_1.Rx_Buf.data     = g_rxBuffer;
			U_1.Rx_Buf.dataSize = ECHO_BUFFER_LENGTH;
			LPUART_ReceiveEDMA(DEMO_LPUART, &g_lpuartEdmaHandle, &U_1.Rx_Buf);
			
	}else;
}



void LPUART_GPIO_Init(GPIO_Type *base_tx, uint32_t pin_tx, GPIO_Type *base_rx, uint32_t pin_rx,  uint32 muxRegister, uint32 muxMode, uint32 inputRegister, uint32 inputDaisy, uint32 configRegister, uint32 muxRegister2, uint32 muxMode2, uint32 inputRegister2, uint32 inputDaisy2, uint32 configRegister2)
{
  /* 定义gpio初始化配置结构体 */
  gpio_pin_config_t config = {0};
	
  /*GPIO配置*/   
  config.direction     = kGPIO_DigitalInput;  //输入模式
  config.interruptMode = kGPIO_NoIntmode;     //不使用中断
  GPIO_PinInit(base_rx, pin_rx, &config);
  
  config.direction = kGPIO_DigitalOutput;     //输出模式
  config.outputLogic =  1;                    //默认高电平,在输入模式下配置该选项无效
  GPIO_PinInit(base_tx, pin_tx, &config);
  
  IOMUXC_SetPinMux(muxRegister, muxMode, inputRegister, inputDaisy, configRegister, 0U);                                      
  IOMUXC_SetPinMux(muxRegister2, muxMode2, inputRegister2, inputDaisy2, configRegister2, 0U);
  
  IOMUXC_SetPinConfig(muxRegister, muxMode, inputRegister, inputDaisy, configRegister, UART_RX_PAD_CONFIG_DATA);
  IOMUXC_SetPinConfig(muxRegister2, muxMode2, inputRegister2, inputDaisy2, configRegister2, UART_TX_PAD_CONFIG_DATA);

}

2.Lpuart.h的代码

#include "main.h"
#include "fsl_dmamux.h"
#include "fsl_common.h"
#include "fsl_iomuxc.h"
#include "fsl_lpuart.h"
#include "board.h"
#include "fsl_lpuart_edma.h"

typedef struct{
	lpuart_transfer_t Tx_Buf;
	lpuart_transfer_t Rx_Buf;
	uint32_t TxData_Index;
	uint32_t RxData_Index;
}Lpuart_DMA_Type; extern Lpuart_DMA_Type U_1;

extern lpuart_edma_handle_t g_lpuartEdmaHandle;    //串口DMA传输句柄
extern edma_handle_t        g_lpuartTxEdmaHandle;  //串口DMA发送句柄
extern edma_handle_t        g_lpuartRxEdmaHandle;  //串口DMA接收句柄

//定义本程序使用的串口
#define DEMO_LPUART            LPUART1               
#define DEMO_LPUART_CLK_FREQ   BOARD_DebugConsoleSrcFreq() //UART时钟频率
#define LPUART_TX_DMA_CHANNEL  0U                          //UART发送使用的DMA通道号
#define LPUART_RX_DMA_CHANNEL  1U                          //UART接收使用的DMA通道号
#define LPUART_TX_DMA_REQUEST  kDmaRequestMuxLPUART1Tx     //定义串口DMA发送请求源
#define LPUART_RX_DMA_REQUEST  kDmaRequestMuxLPUART1Rx     //定义串口DMA接收请求源
#define LPUART_DMAMUX_BASEADDR DMAMUX           				   //定义所使用的DMA多路复用模块(DMAMUX)
#define LPUART_DMA_BASEADDR    DMA0                				 //定义使用的DMA
#define ECHO_BUFFER_LENGTH     8                           //UART接收和发送数据缓冲区长度


#define LPUART1_RX_GPIO                GPIO1
#define LPUART1_RX_GPIO_PIN            (13U)
#define LPUART1_RX_IOMUXC              IOMUXC_GPIO_AD_B0_13_LPUART1_RX

#define LPUART1_TX_GPIO                GPIO1
#define LPUART1_TX_GPIO_PIN            (12U)
#define LPUART1_TX_IOMUXC              IOMUXC_GPIO_AD_B0_12_LPUART1_TX




/*******************************************************************************
 * uart引脚配置
 ******************************************************************************/
#define UART_RX_PAD_CONFIG_DATA         (SRE_0_SLOW_SLEW_RATE| \
                                        DSE_6_R0_6| \
                                        SPEED_1_MEDIUM_100MHz| \
                                        ODE_0_OPEN_DRAIN_DISABLED| \
                                        PKE_1_PULL_KEEPER_ENABLED| \
                                        PUE_1_PULL_SELECTED| \
                                        PUS_3_22K_OHM_PULL_UP| \
                                        HYS_0_HYSTERESIS_DISABLED) 
    /* 配置说明 : */
    /* 转换速率: 转换速率慢
        驱动强度: R0/6 
        带宽配置 : medium(100MHz)
        开漏配置: 关闭 
        拉/保持器配置: 使能
        拉/保持器选择: 上下拉
        上拉/下拉选择: 22K欧姆上拉(选择了保持器此配置无效)
        滞回器配置: 禁止 */ 

#define UART_TX_PAD_CONFIG_DATA            (SRE_0_SLOW_SLEW_RATE| \
                                        DSE_6_R0_6| \
                                        SPEED_1_MEDIUM_100MHz| \
                                        ODE_0_OPEN_DRAIN_DISABLED| \
                                        PKE_1_PULL_KEEPER_ENABLED| \
                                        PUE_1_PULL_SELECTED| \
                                        PUS_3_22K_OHM_PULL_UP| \
                                        HYS_0_HYSTERESIS_DISABLED)
    /* 配置说明 : */
    /* 转换速率: 转换速率慢
        驱动强度: R0/6 
        带宽配置 : medium(100MHz)
        开漏配置: 关闭 
        拉/保持器配置: 使能
        拉/保持器选择: 上下拉
        上拉/下拉选择: 22K欧姆上拉(选择了保持器此配置无效)
        滞回器配置: 禁止 */    
				
				



void LPUART_DMA_Init(_Bool LPUART_DMA_Mode);

/* LPUART 回调函数 */
void LPUART_UserCallback(LPUART_Type *base, lpuart_edma_handle_t *handle, status_t status, void *userData);

void LPUART_GPIO_Init(GPIO_Type *base_tx, uint32_t pin_tx, GPIO_Type *base_rx, uint32_t pin_rx,  uint32 muxRegister, uint32 muxMode, uint32 inputRegister, uint32 inputDaisy, uint32 configRegister, uint32 muxRegister2, uint32 muxMode2, uint32 inputRegister2, uint32 inputDaisy2, uint32 configRegister2);

3.串口回调函数代码

extern SemaphoreHandle_t UartSem_Handle;
extern QueueHandle_t     Uart_Queue;
void Uart_Callback_Debug_1(void)
{ 
  /* 判断中断源 */
  if(LPUART_GetStatusFlags(DEMO_LPUART) & kLPUART_IdleLineFlag)
  {
    /* 清除空闲中断标志位 */
    LPUART_ClearStatusFlags(DEMO_LPUART, kLPUART_IdleLineFlag);
    /* 获取当前已接收字节数 */
    LPUART_TransferGetReceiveCountEDMA(DEMO_LPUART, &g_lpuartEdmaHandle, (uint32_t*)&U_1.RxData_Index);
    /* 关闭DMA传输 */
    LPUART_TransferAbortReceiveEDMA(DEMO_LPUART, &g_lpuartEdmaHandle);

    /*数据处理-------------------------------------------------------------------------------------------------------*/
		//Dbp("%d %s\r\n", U_1.Rx_Buf.dataSize, U_1.Rx_Buf.rxData);		
		if((UART_Check_str(U_1.Rx_Buf.rxData, U_1.RxData_Index) == 1) && (U_1.RxData_Index == 3)){ 
				uint8_t num = 1;
				if(xQueueSendToBackFromISR(Uart_Queue, &num, NULL) != pdPASS) Dbp("Uart_Callback_Debug_1_xQueueSendToBackFromISR_error!!!!!!!!!!!!!!\r\n");
		}else{
				memset(U_1.Rx_Buf.rxData, 0, U_1.RxData_Index);
			  Dbp("error\r\n");
		}

    /* 重新开始DMA接收传输 */
    LPUART_ReceiveEDMA(DEMO_LPUART, &g_lpuartEdmaHandle, &U_1.Rx_Buf);
  }
}
void LPUART1_IRQHandler(void)
{           
		//Dbp("Enter\r\n");  
	  Uart_Callback_Debug_1();	
}

四、总结

1.不是什么很难很高深的东西,但因为第一次用这个库,云里雾里总共花了六个小时才弄完,测试了一下1ms自动发送然后接收回应都是不会出现乱码情况,效果会好一些比之前,有需要的可以自行试试。

2.上面的代码使用了逐飞的部分库,基本能不用就没有用了,它封装的无非就是在NXP库的基础上封装的,有任何问题可以评论,我再回答。

3.上述代码还使用了FreeRtos,回调哪里有几行直接忽视就好了。

4.上述代码还参考了野火的RT1052板卡的例程,有需要的也可以去看,都是一个系列差别不大的。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值