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

[复制链接]
4990|46
 楼主| zero949079783 发表于 2024-11-10 19:01 | 显示全部楼层 |阅读模式
本帖最后由 zero949079783 于 2024-11-10 23:39 编辑

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



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

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

  10. /*队列句柄*/
  11. QueueHandle_t RX_queue;                        //串口RX队列
  12. QueueHandle_t TX_queue;                        //串口RX队列

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


  14. void Usart1_RX_Isr_FunCb(uint8_t *data,uint16_t datalen)
  15. {
  16.         BaseType_t xHigherPriorityTaskWoken = pdFALSE;
  17.         bufflen = datalen;       
  18.         xQueueSendFromISR(RX_queue,&data,&xHigherPriorityTaskWoken); //将数据放入消息队列
  19.         if(xHigherPriorityTaskWoken == pdTRUE)
  20.         {       
  21.                 portYIELD_FROM_ISR(xHigherPriorityTaskWoken); //如果需要就切换任务
  22.         }
  23. }

  24. void Usart1_RX_task(void *pvParameters)
  25. {

  26.                
  27.         pvParameters=pvParameters;
  28.         Usart1_RX_FunCb(Usart1_RX_Isr_FunCb);
  29.         uint8_t res = 0;
  30.         uint8_t * rev_data ;
  31.         while(1)
  32.         {
  33.                 res=xQueueReceive(RX_queue,&rev_data,portMAX_DELAY);                //接收队列的数据
  34.                
  35.                 if(res == pdPASS)
  36.                 {               
  37.                         Usart1_Txdata(rev_data,bufflen);//向队列放入数据
  38.                 }               

  39.         }
  40. }


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

  45. void Usart1_TX_task(void *pvParameters)
  46. {


  47.         pvParameters=pvParameters;
  48.         uint8_t res = 0;
  49.         uint8_t *tx_buff;
  50.         Usart1_TX_FunCb(Usart1_TX_Fun);
  51.         while(1)
  52.         {
  53.                 res = xQueueReceive(TX_queue,&tx_buff,portMAX_DELAY);                //接收TX队列的数据
  54.                 if(res == pdPASS)
  55.                 {
  56.                         Uart1_DMA_ENABLE(tx_buff,bufflen);        //启动DMA

  57.                 }                       
  58.         }
  59. }


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

  69.                 TX_queue = xQueueCreate(10,sizeof(uint8_t *));        //串口TX队列,接收为一桢数据        存放地址就可以
  70.                
  71.                 if(TX_queue != NULL)
  72.                 {
  73.                         printf("\r\n串口TX队列创建成功\r\n");
  74.                 }                       
  75.                
  76.     /* Usart1_RX 任务 */
  77.     xTaskCreate((TaskFunction_t )Usart1_RX_task,                                                        /* 任务函数 */
  78.                 (const char*    )"Usart1_RX_task",                                                /* 任务名称 */
  79.                 (uint16_t       )Usart1_RX_STK_SIZE,                                        /* 任务堆栈大小 */
  80.                 (void*          )NULL,                                                                         /* 传递给任务函数的参数 */
  81.                 (UBaseType_t    )Usart1_RX_TASK_PRIO,                                        /* 任务优先级 */
  82.                 (TaskHandle_t*  )&Usart1_RX_task);                        /* 任务句柄 */       
  83.                                                                
  84.     xTaskCreate((TaskFunction_t )Usart1_TX_task,                                                        /* 任务函数 */
  85.                 (const char*    )"Usart1_TX_task",                                                /* 任务名称 */
  86.                 (uint16_t       )Usart1_TX_STK_SIZE,                                        /* 任务堆栈大小 */
  87.                 (void*          )NULL,                                                                         /* 传递给任务函数的参数 */
  88.                 (UBaseType_t    )Usart1_TX_TASK_PRIO,                                        /* 任务优先级 */
  89.                 (TaskHandle_t*  )&Usart1_TX_task);                        /* 任务句柄 */                                                                       
  90. }





usart_drv.c
  1. #include "usart_drv.h"

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




  3. uint8_t Usart1_RxBuff[U1_RX_SIZE];                 //串口1接收缓冲区
  4. uint8_t Usart1_TxBuff[U1_TX_SIZE];                //串口1发送缓冲区       




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

  6. void Usart1_TX_FunCb(void(*pFunc)(uint8_t *data,uint16_t datalen))
  7. {
  8.         USART1_TXFun = pFunc;
  9. }
  10. void Usart1_Txdata(uint8_t *data,uint32_t data_len)
  11. {
  12.         if(data_len == 0)        //防止发送0个数据
  13.         {
  14.                 uart1.TxState = 0;
  15.                 uart1.TxCounter = 0;
  16.                 return ;
  17.         }
  18.        
  19.         if((U1_TX_SIZE - uart1.TxCounter) >= data_len)
  20.         {
  21.                 USART1_TXFun(data,data_len);
  22.         }


  23. }

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

  28. /*************************************************************************
  29. *        函 数 名: UART1_Init
  30. *        功能说明: 串口1初始化
  31. *        形    参:无
  32. *        返 回 值: 无
  33. **************************************************************************/
  34. void UART1_Init(uint32_t baudrate)
  35. {       

  36.         memset(&uart1.uart,0,sizeof(uart1.uart));
  37.         uart1.uart.Instance = USART1;
  38.         uart1.uart.Init.BaudRate = baudrate;
  39.         uart1.uart.Init.WordLength =UART_WORDLENGTH_8B;
  40.         uart1.uart.Init.StopBits=UART_STOPBITS_1;
  41.         uart1.uart.Init.Parity=UART_PARITY_NONE;
  42.         uart1.uart.Init.Mode=UART_MODE_TX_RX;
  43.         uart1.uart.Init.HwFlowCtl=UART_HWCONTROL_NONE;

  44.         HAL_UART_Init(&uart1.uart);
  45.        

  46.         __HAL_UART_CLEAR_IDLEFLAG(&uart1.uart);
  47.         __HAL_UART_ENABLE_IT(&uart1.uart,UART_IT_IDLE);       
  48.         HAL_UART_Receive_DMA(&uart1.uart,Usart1_RxBuff,U1_RX_MAX);
  49.        

  50. }

  51. uint8_t tempbuff1[256]={0};
  52. void Usart1_printf(char *fmt,...)
  53. {

  54.         uint16_t i;
  55.        
  56.         va_list  ap;       
  57.         va_start(ap,fmt);       
  58.         vsprintf((char *)tempbuff1,fmt,ap);
  59.         va_end(ap);
  60.        
  61.         for(i=0;i<strlen((char *)tempbuff1);i++)
  62.         {
  63.                 while(!__HAL_UART_GET_FLAG(&uart1.uart,UART_FLAG_TXE))
  64.                 {
  65.                 }               
  66.                 uart1.uart.Instance->TDR = tempbuff1[i];               
  67.         }
  68.        
  69.         while(!__HAL_UART_GET_FLAG(&uart1.uart,UART_FLAG_TC))
  70.         {
  71.        
  72.         }               
  73. }



  74. void HAL_UART_MspInit(UART_HandleTypeDef *huart)
  75. {
  76.         GPIO_InitTypeDef GPIO_InitTypeDefStructur;
  77.        
  78.         if(huart ->Instance == USART1)
  79.         {
  80.                
  81.                 __HAL_RCC_DMA2_CLK_ENABLE();
  82.                 __HAL_RCC_GPIOA_CLK_ENABLE();
  83.                 __HAL_RCC_USART1_CLK_ENABLE();
  84.                
  85.        
  86.                 GPIO_InitTypeDefStructur.Pin =GPIO_PIN_9;
  87.                 GPIO_InitTypeDefStructur.Mode = GPIO_MODE_AF_PP;
  88.                 GPIO_InitTypeDefStructur.Speed = GPIO_SPEED_FREQ_HIGH;
  89.                 GPIO_InitTypeDefStructur.Pull=GPIO_PULLUP;
  90.                 GPIO_InitTypeDefStructur.Alternate=GPIO_AF7_USART1;
  91.                
  92.                 HAL_GPIO_Init(GPIOA,&GPIO_InitTypeDefStructur);               
  93.                
  94.                 GPIO_InitTypeDefStructur.Pin =GPIO_PIN_10;
  95.                 GPIO_InitTypeDefStructur.Alternate=GPIO_AF7_USART1;
  96.                 HAL_GPIO_Init(GPIOA,&GPIO_InitTypeDefStructur);       
  97.                
  98.                 HAL_NVIC_SetPriority(USART1_IRQn,7,0);
  99.                 HAL_NVIC_EnableIRQ(USART1_IRQn);
  100.                
  101.                 //TX
  102.                 uart1.dmatx.Instance = DMA2_Stream7;
  103.                 uart1.dmatx.Init.Request= DMA_REQUEST_USART1_TX;                /* USART1发送DMA */               
  104.                 uart1.dmatx.Init.Direction = DMA_MEMORY_TO_PERIPH;                /* 存储器到外设 */
  105.                 uart1.dmatx.Init.PeriphInc = DMA_PINC_DISABLE;                                 /* 外设非增量模式 */
  106.                 uart1.dmatx.Init.MemInc = DMA_MINC_ENABLE;                                                /* 存储器增量模式 */
  107.                 uart1.dmatx.Init.PeriphDataAlignment = DMA_PDATAALIGN_BYTE;
  108.                 uart1.dmatx.Init.MemDataAlignment = DMA_MDATAALIGN_BYTE;
  109.                 uart1.dmatx.Init.Mode = DMA_NORMAL;
  110.                 uart1.dmatx.Init.Priority=DMA_PRIORITY_MEDIUM;
  111.                 uart1.dmatx.Init.FIFOThreshold=DMA_FIFOMODE_DISABLE;
  112.                                 HAL_DMA_Init(&uart1.dmatx);       
  113.                
  114.                 __HAL_LINKDMA(huart,hdmatx,uart1.dmatx);                        //连接DMA通道
  115.        
  116.                 HAL_NVIC_SetPriority(DMA2_Stream7_IRQn,7,0);
  117.                 HAL_NVIC_EnableIRQ(DMA2_Stream7_IRQn);
  118.                
  119.                
  120.                 //RX
  121.                 uart1.dmarx.Instance = DMA2_Stream2;                        /* 数据流选择 */
  122.                 uart1.dmarx.Init.Request= DMA_REQUEST_USART1_RX;        /* USART1发送DMA */
  123.                 uart1.dmarx.Init.Direction = DMA_PERIPH_TO_MEMORY;
  124.                 uart1.dmarx.Init.PeriphInc = DMA_PINC_DISABLE;
  125.                 uart1.dmarx.Init.MemInc = DMA_MINC_ENABLE;
  126.                 uart1.dmarx.Init.PeriphDataAlignment = DMA_PDATAALIGN_BYTE;
  127.                 uart1.dmarx.Init.MemDataAlignment = DMA_MDATAALIGN_BYTE;
  128.                 uart1.dmarx.Init.Mode = DMA_NORMAL;
  129.                 uart1.dmarx.Init.Priority=DMA_PRIORITY_MEDIUM;
  130.                 uart1.dmarx.Init.FIFOThreshold=DMA_FIFOMODE_DISABLE;
  131.                
  132.                 __HAL_LINKDMA(huart,hdmarx,uart1.dmarx);                        //连接DMA通道
  133.                 HAL_DMA_Init(&uart1.dmarx);
  134.                 HAL_NVIC_SetPriority(DMA2_Stream2_IRQn,7,0);
  135.                 HAL_NVIC_EnableIRQ(DMA2_Stream2_IRQn);
  136.         }
  137.        
  138. }



  139. void HAL_UART_TxCpltCallback(UART_HandleTypeDef *huart)
  140. {
  141.         if(huart ->Instance == USART1)
  142.         {

  143.                 uart1.TxState=0;

  144.         }
  145.        
  146. }


  147. void Usart1_Send_Data(uint8_t *data,uint16_t datalen)
  148. {
  149.         uint16_t i;
  150.         for(i=0;i<datalen;i++)
  151.         {
  152.                 while(!__HAL_UART_GET_FLAG(&uart1.uart,UART_FLAG_TXE));       
  153.                 uart1.uart.Instance->TDR = data[i];               
  154.         }
  155.        
  156.         while(!__HAL_UART_GET_FLAG(&uart1.uart,UART_FLAG_TC));
  157. }


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

  159. void Usart1_RX_FunCb(void(*pFunc)(uint8_t *data,uint16_t datalen))
  160. {
  161.         USART1_RXFun = pFunc;
  162. }

  163. void USART1_IRQHandler(void)
  164. {


  165.         HAL_UART_IRQHandler(&uart1.uart);
  166.         if(__HAL_UART_GET_FLAG(&uart1.uart,UART_FLAG_IDLE))
  167.         {
  168.                        
  169.                 __HAL_UART_CLEAR_IDLEFLAG(&uart1.uart);        //清除空闲中断标志
  170.                 __HAL_DMA_DISABLE_IT(&uart1.dmarx,DMA_IT_HT);//禁止DMA传输过半中断
  171.                
  172.                 uart1.RxCounter = ((U1_RX_MAX) - __HAL_DMA_GET_COUNTER(&uart1.dmarx));        //计算本次的接收数据量
  173.                 if(USART1_RXFun != NULL)
  174.                 {
  175.                         USART1_RXFun(Usart1_RxBuff,uart1.RxCounter);               
  176.                 }               
  177.        
  178.                 HAL_UART_DMAStop(&uart1.uart);                                        //停止DMA
  179.                 HAL_UART_Receive_DMA(&uart1.uart,Usart1_RxBuff,U1_RX_MAX);

  180.                        
  181.         }
  182.        
  183. }

  184. void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
  185. {

  186.         if(huart ->Instance == USART1)
  187.         {
  188.                         if(__HAL_DMA_GET_FLAG(&uart1.dmarx, DMA_FLAG_TCIF2_6) == RESET)
  189.                         {
  190.                                 __HAL_DMA_CLEAR_FLAG(&uart1.dmarx, DMA_FLAG_TCIF2_6); //清除DMA2_Steam2传输完成标志
  191.                                 HAL_UART_DMAStop(&uart1.uart);                                        //停止DMA
  192.                                 HAL_UART_Receive_DMA(&uart1.uart,Usart1_RxBuff,U1_RX_MAX+1);
  193.                         }
  194.                        
  195.                         if(__HAL_DMA_GET_FLAG(&uart1.dmarx, DMA_FLAG_TCIF3_7) == RESET)
  196.                         {
  197.                                 __HAL_DMA_CLEAR_FLAG(&uart1.dmarx, DMA_FLAG_TCIF3_7); //清除DMA2_Steam7传输完成标志
  198.                         }                       
  199.         }

  200. }

  201. void DMA2_Stream2_IRQHandler(void)
  202. {
  203.         HAL_DMA_IRQHandler(&uart1.dmarx);
  204. }

  205. void DMA2_Stream7_IRQHandler(void)
  206. {
  207.         HAL_DMA_IRQHandler(&uart1.dmatx);
  208. }

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

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

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

  223. #endif

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

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

  235. char *_sys_command_string(char *cmd, int len)
  236. {
  237.     return NULL;
  238. }

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

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


usart_drv.h
  1. #ifndef __USART_DRV_H
  2. #define __USART_DRV_H

  3. #include "stm32h7xx_hal.h"

  4. #include "string.h"
  5. #include "signal.h"
  6. #include "stdarg.h"
  7. #include "stdio.h"

  8. #define  U1_RX_SIZE 257
  9. #define  U1_TX_SIZE 256
  10. #define  U1_RX_MAX         257
  11. #define  U1_TX_MAX         256

  12. typedef struct{
  13.         uint8_t *start;
  14.         uint8_t *end;
  15. }LCB;

  16. typedef struct{
  17.         uint32_t RxCounter;
  18.         uint32_t TxCounter;
  19.         uint32_t TxState;
  20.        
  21.         UART_HandleTypeDef uart;
  22.         DMA_HandleTypeDef dmatx;
  23.         DMA_HandleTypeDef dmarx;       
  24. }UCB;

  25. void UART1_Init(uint32_t baudrate);


  26. void Usart1_PtrInit(void);
  27. void Usart1_Txdata(uint8_t *data,uint32_t data_len);
  28. void Uart1_DMA_ENABLE(uint8_t *data,uint32_t data_len);



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



  30. void Usart1_Send_Data(uint8_t *data,uint16_t datalen);

  31. void Usart1_RX_FunCb(void(*pFunc)(uint8_t *data,uint16_t datalen));
  32. void Usart1_TX_FunCb(void(*pFunc)(uint8_t *data,uint16_t datalen));
  33. #endif

















串口DMA队列操作.rar

8.48 MB, 下载次数: 17

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的配置和缓冲区大小,以提高数据传输效率。
您需要登录后才可以回帖 登录 | 注册

本版积分规则

33

主题

91

帖子

1

粉丝
快速回复 在线客服 返回列表 返回顶部