一、为什么要做有什么用
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板卡的例程,有需要的也可以去看,都是一个系列差别不大的。