zero949079783 发表于 2024-11-10 19:01

FreeRTOS-串口DMA收发不定长数据+队列

本帖最后由 zero949079783 于 2024-11-10 23:39 编辑

FreeRTOS-串口DMA收发不定长数据+队列
#include "Usartapp.c"
#include "main.h"



#define Usart1_RX_TASK_PRIO   3         /* 任务优先级 */
#define Usart1_RX_STK_SIZE      512         /* 任务堆栈大小 */
TaskHandle_t Usart1_RXTask_Handler;         /* 任务句柄 */
void Usart1_RX_task(void *pvParameters);

#define Usart1_TX_TASK_PRIO   4         /* 任务优先级 */
#define Usart1_TX_STK_SIZE      512             /* 任务堆栈大小 */
TaskHandle_t Usart1_TX_Task_Handler;         /* 任务句柄 */
void Usart1_TX_task(void *pvParameters);

/*队列句柄*/
QueueHandle_t RX_queue;                        //串口RX队列
QueueHandle_t TX_queue;                        //串口RX队列

uint16_t bufflen = 0;                                //接收数据长度


void Usart1_RX_Isr_FunCb(uint8_t *data,uint16_t datalen)
{
        BaseType_t xHigherPriorityTaskWoken = pdFALSE;
        bufflen = datalen;       
        xQueueSendFromISR(RX_queue,&data,&xHigherPriorityTaskWoken); //将数据放入消息队列
        if(xHigherPriorityTaskWoken == pdTRUE)
        {       
                portYIELD_FROM_ISR(xHigherPriorityTaskWoken); //如果需要就切换任务
        }
}

void Usart1_RX_task(void *pvParameters)
{

               
        pvParameters=pvParameters;
        Usart1_RX_FunCb(Usart1_RX_Isr_FunCb);
        uint8_t res = 0;
        uint8_t * rev_data ;
        while(1)
        {
                res=xQueueReceive(RX_queue,&rev_data,portMAX_DELAY);                //接收队列的数据
               
                if(res == pdPASS)
                {               
                        Usart1_Txdata(rev_data,bufflen);//向队列放入数据
                }               

        }
}


void Usart1_TX_Fun(uint8_t *data,uint16_t datalen)
{
                xQueueSend(TX_queue,&data,portMAX_DELAY);                                //发送队列的数据
}

void Usart1_TX_task(void *pvParameters)
{


        pvParameters=pvParameters;
        uint8_t res = 0;
        uint8_t *tx_buff;
        Usart1_TX_FunCb(Usart1_TX_Fun);
        while(1)
        {
                res = xQueueReceive(TX_queue,&tx_buff,portMAX_DELAY);                //接收TX队列的数据
                if(res == pdPASS)
                {
                        Uart1_DMA_ENABLE(tx_buff,bufflen);        //启动DMA

                }                       
        }
}


void UsartAPP_Task_Init(void)
{
                       
                RX_queue = xQueueCreate(10,sizeof(uint8_t *));        //串口RX队列,接收为一桢数据        存放地址就可以
               
                if(RX_queue != NULL)
                {
                        printf("\r\n串口RX队列创建成功\r\n");
                }       

                TX_queue = xQueueCreate(10,sizeof(uint8_t *));        //串口TX队列,接收为一桢数据        存放地址就可以
               
                if(TX_queue != NULL)
                {
                        printf("\r\n串口TX队列创建成功\r\n");
                }                       
               
    /* Usart1_RX 任务 */
    xTaskCreate((TaskFunction_t )Usart1_RX_task,                                                        /* 任务函数 */
                (const char*    )"Usart1_RX_task",                                                /* 任务名称 */
                (uint16_t       )Usart1_RX_STK_SIZE,                                        /* 任务堆栈大小 */
                (void*          )NULL,                                                                       /* 传递给任务函数的参数 */
                (UBaseType_t    )Usart1_RX_TASK_PRIO,                                        /* 任务优先级 */
                (TaskHandle_t*)&Usart1_RX_task);                        /* 任务句柄 */       
                                                               
    xTaskCreate((TaskFunction_t )Usart1_TX_task,                                                        /* 任务函数 */
                (const char*    )"Usart1_TX_task",                                                /* 任务名称 */
                (uint16_t       )Usart1_TX_STK_SIZE,                                        /* 任务堆栈大小 */
                (void*          )NULL,                                                                       /* 传递给任务函数的参数 */
                (UBaseType_t    )Usart1_TX_TASK_PRIO,                                        /* 任务优先级 */
                (TaskHandle_t*)&Usart1_TX_task);                        /* 任务句柄 */                                                                       
}





usart_drv.c#include "usart_drv.h"

UCB uart1;        //串口1控制结构体




uint8_t Usart1_RxBuff;               //串口1接收缓冲区
uint8_t Usart1_TxBuff;                //串口1发送缓冲区       




static void(*USART1_TXFun)(uint8_t *data,uint16_t data_len);        //函数指针

void Usart1_TX_FunCb(void(*pFunc)(uint8_t *data,uint16_t datalen))
{
        USART1_TXFun = pFunc;
}
void Usart1_Txdata(uint8_t *data,uint32_t data_len)
{
        if(data_len == 0)        //防止发送0个数据
        {
                uart1.TxState = 0;
                uart1.TxCounter = 0;
                return ;
        }
       
        if((U1_TX_SIZE - uart1.TxCounter) >= data_len)
        {
                USART1_TXFun(data,data_len);
        }


}

void Uart1_DMA_ENABLE(uint8_t *data,uint32_t data_len)
{
        HAL_UART_Transmit_DMA(&uart1.uart,data,data_len);
}

/*************************************************************************
*        函 数 名: UART1_Init
*        功能说明: 串口1初始化
*        形    参:无
*        返 回 值: 无
**************************************************************************/
void UART1_Init(uint32_t baudrate)
{       

        memset(&uart1.uart,0,sizeof(uart1.uart));
        uart1.uart.Instance = USART1;
        uart1.uart.Init.BaudRate = baudrate;
        uart1.uart.Init.WordLength =UART_WORDLENGTH_8B;
        uart1.uart.Init.StopBits=UART_STOPBITS_1;
        uart1.uart.Init.Parity=UART_PARITY_NONE;
        uart1.uart.Init.Mode=UART_MODE_TX_RX;
        uart1.uart.Init.HwFlowCtl=UART_HWCONTROL_NONE;

        HAL_UART_Init(&uart1.uart);
       

        __HAL_UART_CLEAR_IDLEFLAG(&uart1.uart);
        __HAL_UART_ENABLE_IT(&uart1.uart,UART_IT_IDLE);       
        HAL_UART_Receive_DMA(&uart1.uart,Usart1_RxBuff,U1_RX_MAX);
       

}

uint8_t tempbuff1={0};
void Usart1_printf(char *fmt,...)
{

        uint16_t i;
       
        va_listap;       
        va_start(ap,fmt);       
        vsprintf((char *)tempbuff1,fmt,ap);
        va_end(ap);
       
        for(i=0;i<strlen((char *)tempbuff1);i++)
        {
                while(!__HAL_UART_GET_FLAG(&uart1.uart,UART_FLAG_TXE))
                {
                }               
                uart1.uart.Instance->TDR = tempbuff1;               
        }
       
        while(!__HAL_UART_GET_FLAG(&uart1.uart,UART_FLAG_TC))
        {
       
        }               
}



void HAL_UART_MspInit(UART_HandleTypeDef *huart)
{
        GPIO_InitTypeDef GPIO_InitTypeDefStructur;
       
        if(huart ->Instance == USART1)
        {
               
                __HAL_RCC_DMA2_CLK_ENABLE();
                __HAL_RCC_GPIOA_CLK_ENABLE();
                __HAL_RCC_USART1_CLK_ENABLE();
               
       
                GPIO_InitTypeDefStructur.Pin =GPIO_PIN_9;
                GPIO_InitTypeDefStructur.Mode = GPIO_MODE_AF_PP;
                GPIO_InitTypeDefStructur.Speed = GPIO_SPEED_FREQ_HIGH;
                GPIO_InitTypeDefStructur.Pull=GPIO_PULLUP;
                GPIO_InitTypeDefStructur.Alternate=GPIO_AF7_USART1;
               
                HAL_GPIO_Init(GPIOA,&GPIO_InitTypeDefStructur);               
               
                GPIO_InitTypeDefStructur.Pin =GPIO_PIN_10;
                GPIO_InitTypeDefStructur.Alternate=GPIO_AF7_USART1;
                HAL_GPIO_Init(GPIOA,&GPIO_InitTypeDefStructur);       
               
                HAL_NVIC_SetPriority(USART1_IRQn,7,0);
                HAL_NVIC_EnableIRQ(USART1_IRQn);
               
                //TX
                uart1.dmatx.Instance = DMA2_Stream7;
                uart1.dmatx.Init.Request= DMA_REQUEST_USART1_TX;                /* USART1发送DMA */               
                uart1.dmatx.Init.Direction = DMA_MEMORY_TO_PERIPH;                /* 存储器到外设 */
                uart1.dmatx.Init.PeriphInc = DMA_PINC_DISABLE;                               /* 外设非增量模式 */
                uart1.dmatx.Init.MemInc = DMA_MINC_ENABLE;                                                /* 存储器增量模式 */
                uart1.dmatx.Init.PeriphDataAlignment = DMA_PDATAALIGN_BYTE;
                uart1.dmatx.Init.MemDataAlignment = DMA_MDATAALIGN_BYTE;
                uart1.dmatx.Init.Mode = DMA_NORMAL;
                uart1.dmatx.Init.Priority=DMA_PRIORITY_MEDIUM;
                uart1.dmatx.Init.FIFOThreshold=DMA_FIFOMODE_DISABLE;
                                HAL_DMA_Init(&uart1.dmatx);       
               
                __HAL_LINKDMA(huart,hdmatx,uart1.dmatx);                        //连接DMA通道
       
                HAL_NVIC_SetPriority(DMA2_Stream7_IRQn,7,0);
                HAL_NVIC_EnableIRQ(DMA2_Stream7_IRQn);
               
               
                //RX
                uart1.dmarx.Instance = DMA2_Stream2;                        /* 数据流选择 */
                uart1.dmarx.Init.Request= DMA_REQUEST_USART1_RX;        /* USART1发送DMA */
                uart1.dmarx.Init.Direction = DMA_PERIPH_TO_MEMORY;
                uart1.dmarx.Init.PeriphInc = DMA_PINC_DISABLE;
                uart1.dmarx.Init.MemInc = DMA_MINC_ENABLE;
                uart1.dmarx.Init.PeriphDataAlignment = DMA_PDATAALIGN_BYTE;
                uart1.dmarx.Init.MemDataAlignment = DMA_MDATAALIGN_BYTE;
                uart1.dmarx.Init.Mode = DMA_NORMAL;
                uart1.dmarx.Init.Priority=DMA_PRIORITY_MEDIUM;
                uart1.dmarx.Init.FIFOThreshold=DMA_FIFOMODE_DISABLE;
               
                __HAL_LINKDMA(huart,hdmarx,uart1.dmarx);                        //连接DMA通道
                HAL_DMA_Init(&uart1.dmarx);
                HAL_NVIC_SetPriority(DMA2_Stream2_IRQn,7,0);
                HAL_NVIC_EnableIRQ(DMA2_Stream2_IRQn);
        }
       
}



void HAL_UART_TxCpltCallback(UART_HandleTypeDef *huart)
{
        if(huart ->Instance == USART1)
        {

                uart1.TxState=0;

        }
       
}


void Usart1_Send_Data(uint8_t *data,uint16_t datalen)
{
        uint16_t i;
        for(i=0;i<datalen;i++)
        {
                while(!__HAL_UART_GET_FLAG(&uart1.uart,UART_FLAG_TXE));       
                uart1.uart.Instance->TDR = data;               
        }
       
        while(!__HAL_UART_GET_FLAG(&uart1.uart,UART_FLAG_TC));
}


static void(*USART1_RXFun)(uint8_t *data,uint16_t datalen);        //函数指针

void Usart1_RX_FunCb(void(*pFunc)(uint8_t *data,uint16_t datalen))
{
        USART1_RXFun = pFunc;
}

void USART1_IRQHandler(void)
{


        HAL_UART_IRQHandler(&uart1.uart);
        if(__HAL_UART_GET_FLAG(&uart1.uart,UART_FLAG_IDLE))
        {
                       
                __HAL_UART_CLEAR_IDLEFLAG(&uart1.uart);        //清除空闲中断标志
                __HAL_DMA_DISABLE_IT(&uart1.dmarx,DMA_IT_HT);//禁止DMA传输过半中断
               
                uart1.RxCounter = ((U1_RX_MAX) - __HAL_DMA_GET_COUNTER(&uart1.dmarx));        //计算本次的接收数据量
                if(USART1_RXFun != NULL)
                {
                        USART1_RXFun(Usart1_RxBuff,uart1.RxCounter);               
                }               
       
                HAL_UART_DMAStop(&uart1.uart);                                        //停止DMA
                HAL_UART_Receive_DMA(&uart1.uart,Usart1_RxBuff,U1_RX_MAX);

                       
        }
       
}

void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{

        if(huart ->Instance == USART1)
        {
                        if(__HAL_DMA_GET_FLAG(&uart1.dmarx, DMA_FLAG_TCIF2_6) == RESET)
                        {
                                __HAL_DMA_CLEAR_FLAG(&uart1.dmarx, DMA_FLAG_TCIF2_6); //清除DMA2_Steam2传输完成标志
                                HAL_UART_DMAStop(&uart1.uart);                                        //停止DMA
                                HAL_UART_Receive_DMA(&uart1.uart,Usart1_RxBuff,U1_RX_MAX+1);
                        }
                       
                        if(__HAL_DMA_GET_FLAG(&uart1.dmarx, DMA_FLAG_TCIF3_7) == RESET)
                        {
                                __HAL_DMA_CLEAR_FLAG(&uart1.dmarx, DMA_FLAG_TCIF3_7); //清除DMA2_Steam7传输完成标志
                        }                       
        }

}

void DMA2_Stream2_IRQHandler(void)
{
        HAL_DMA_IRQHandler(&uart1.dmarx);
}

void DMA2_Stream7_IRQHandler(void)
{
        HAL_DMA_IRQHandler(&uart1.dmatx);
}

#if 1
#if (__ARMCC_VERSION >= 6010050)            /* 使用AC6编译器时 */
__asm(".global __use_no_semihosting\n\t");/* 声明不使用半主机模式 */
__asm(".global __ARM_use_no_argv \n\t");    /* AC6下需要声明main函数为无参数格式,否则部分例程可能出现半主机模式 */

#else
/* 使用AC5编译器时, 要在这里定义__FILE 和 不使用半主机模式 */
#pragma import(__use_no_semihosting)

struct __FILE
{
    int handle;
    /* Whatever you require here. If the only file you are using is */
    /* standard output using printf() for debugging, no file handling */
    /* is required. */
};

#endif

/* 不使用半主机模式,至少需要重定义_ttywrch\_sys_exit\_sys_command_string函数,以同时兼容AC6和AC5模式 */
int _ttywrch(int ch)
{
    ch = ch;
    return ch;
}

/* 定义_sys_exit()以避免使用半主机模式 */
void _sys_exit(int x)
{
    x = x;
}

char *_sys_command_string(char *cmd, int len)
{
    return NULL;
}

/* FILE 在 stdio.h里面定义. */
FILE __stdout;

/* 重定义fputc函数, printf函数最终会通过调用fputc输出字符串到串口 */
int fputc(int ch, FILE *f)
{
    while ((uart1.uart.Instance->ISR & 0X40) == 0);    /* 等待上一个字符发送完成 */
               
    uart1.uart.Instance->TDR = (uint8_t)ch;            /* 将要发送的字符 ch 写入到DR寄存器 */
    return ch;
}
#endif
/******************************************************************************************/


usart_drv.h#ifndef __USART_DRV_H
#define __USART_DRV_H

#include "stm32h7xx_hal.h"

#include "string.h"
#include "signal.h"
#include "stdarg.h"
#include "stdio.h"

#defineU1_RX_SIZE 257
#defineU1_TX_SIZE 256
#defineU1_RX_MAX         257
#defineU1_TX_MAX         256

typedef struct{
        uint8_t *start;
        uint8_t *end;
}LCB;

typedef struct{
        uint32_t RxCounter;
        uint32_t TxCounter;
        uint32_t TxState;
       
        UART_HandleTypeDef uart;
        DMA_HandleTypeDef dmatx;
        DMA_HandleTypeDef dmarx;       
}UCB;

void UART1_Init(uint32_t baudrate);


void Usart1_PtrInit(void);
void Usart1_Txdata(uint8_t *data,uint32_t data_len);
void Uart1_DMA_ENABLE(uint8_t *data,uint32_t data_len);



void Usart1_printf(char *fmt,...);



void Usart1_Send_Data(uint8_t *data,uint16_t datalen);

void Usart1_RX_FunCb(void(*pFunc)(uint8_t *data,uint16_t datalen));
void Usart1_TX_FunCb(void(*pFunc)(uint8_t *data,uint16_t datalen));
#endif

















mnynt121 发表于 2024-11-11 15:36

当接收到一帧完整的数据后,如果一段时间内没有新的数据到来,会触发串口空闲中断。这个中断用于通知处理器已经接收到完整的数据帧。

AdaMaYun 发表于 2024-11-11 16:05

接收到完整数据帧之后进行中断处理

dspmana 发表于 2024-11-11 16:34

在某些情况下,可以使用DMA的循环模式,这样当指定长度的串口数据通过DMA接收完成后,DMA硬件会自动重新设置传输长度,并开启下一个接收过程。但需要注意,在循环模式下,必须确保数据处理能够及时完成,以避免新接收的数据覆盖正在处理的数据。

zero949079783 发表于 2024-11-11 19:27

mnynt121 发表于 2024-11-11 15:36
当接收到一帧完整的数据后,如果一段时间内没有新的数据到来,会触发串口空闲中断。这个中断用于通知处理器 ...

有数据,都会接收到乱码,这时候就需要做CRC了,这个只是使用DMA+环形队列的操作而已

zero949079783 发表于 2024-11-11 19:29

AdaMaYun 发表于 2024-11-11 16:05
接收到完整数据帧之后进行中断处理

数据接收后,在任务中处理数据。数据先放进队列

zero949079783 发表于 2024-11-11 19:30

dspmana 发表于 2024-11-11 16:34
在某些情况下,可以使用DMA的循环模式,这样当指定长度的串口数据通过DMA接收完成后,DMA硬件会自动重新设 ...

队列就是一个环形接收的,FIFO数据结构

sesefadou 发表于 2024-11-12 15:50

如果长时间没有接收到新的数据帧,可能需要进行超时检测,以确保系统稳定运行。

pentruman 发表于 2024-11-13 11:42

在空闲中断处理函数中,将DMA接收缓冲区中的数据拷贝到队列中,实现数据的入队操作。
在应用程序需要处理数据时,从队列中取出数据进行处理,实现数据的出队操作。

sdlls 发表于 2024-11-13 13:30

定义一个循环队列用于存储接收到的串口数据。循环队列是一种将数组的首位在逻辑上连接起来的环形结构,可以高效地利用存储空间。
初始化队列的头指针和尾指针,以及队列的长度等参数。

iyoum 发表于 2024-11-13 17:15

通过合理设置DMA传输长度和双缓冲机制,可以减少中断次数,提高系统响应速度。

febgxu 发表于 2024-11-13 20:47

DMA缓冲区足够大以容纳可能的最大数据包。如果数据包长度不固定,可能需要动态调整缓冲区大小或使用多个缓冲区。

mmbs 发表于 2024-11-14 17:12

使用环形队列(Circular Buffer)来管理接收到的数据,可以有效提高数据处理的效率。

robertesth 发表于 2024-11-14 21:25

为接收队列分配足够的内存空间,并定期清理不再使用的内存,防止内存溢出。

yorkbarney 发表于 2024-11-17 18:29

在接收过程中,可能会遇到各种错误情况,如接收超时、数据溢出等。

claretttt 发表于 2024-11-17 22:47

通过空闲中断,可以实现接收不定长数据的功能。无论串口接收了多少字节的数据,只要接收停止并产生空闲中断,就意味着有一组完整的数据已经接收完成。

gygp 发表于 2024-11-18 09:30

接收的是不定长数据,需要在数据处理过程中加入适当的完整性校验机制

gygp 发表于 2024-11-20 09:22

合理设置DMA中断和其他相关中断的优先级,确保关键任务能够及时得到处理。

uiint 发表于 2024-11-20 10:38

实施数据校验机制(如CRC校验),确保接收到的数据完整无误。

jackcat 发表于 2024-11-20 16:15

优化DMA的配置和缓冲区大小,以提高数据传输效率。
页: [1] 2
查看完整版本: FreeRTOS-串口DMA收发不定长数据+队列