[STM32H5] 【NUCLEO- H563ZI 测评】串口空闲中断+DMA接收不定长数据

[复制链接]
 楼主| OldestTrick 发表于 2023-8-24 22:27 | 显示全部楼层 |阅读模式
#申请原创#  1、串口是设备与设备之间或者MCU和外设之间常用的通信模式,一个好用的串口接收程序对 协议解析,数据接收都非常方便。
很多人都应该用过串口空闲中断+DMA这个模式用于接收不定长的数据这,STM32H5的库函数对这个方式进行封装,直接调用库函数就可以使用空闲中断DMA接收数据了。
为了方便测试,下面的还是使用USART3进行演示,USART3可以和stlink的VCP进行通信。
2、首先是对串口的DMA进行初始化,设置GPDMA1_REQUEST_USART3_RX接收,初始化dma的通道,设置dma 工作在CircularMode模式。最后将DMA和USART3相关联。
  1. /* USART3 DMA Init */
  2.      /* GPDMA1_REQUEST_USART3_RX Init */
  3.      NodeConfig.NodeType = DMA_GPDMA_LINEAR_NODE;
  4.      NodeConfig.Init.Request = GPDMA1_REQUEST_USART3_RX;
  5.      NodeConfig.Init.BlkHWRequest = DMA_BREQ_SINGLE_BURST;
  6.      NodeConfig.Init.Direction = DMA_PERIPH_TO_MEMORY;
  7.      NodeConfig.Init.SrcInc = DMA_SINC_FIXED;
  8.      NodeConfig.Init.DestInc = DMA_DINC_INCREMENTED;
  9.      NodeConfig.Init.SrcDataWidth = DMA_SRC_DATAWIDTH_BYTE;
  10.      NodeConfig.Init.DestDataWidth = DMA_DEST_DATAWIDTH_BYTE;
  11.      NodeConfig.Init.SrcBurstLength = 1;
  12.      NodeConfig.Init.DestBurstLength = 1;
  13.      NodeConfig.Init.TransferAllocatedPort = DMA_SRC_ALLOCATED_PORT0|DMA_DEST_ALLOCATED_PORT0;
  14.      NodeConfig.Init.TransferEventMode = DMA_TCEM_BLOCK_TRANSFER;
  15.      NodeConfig.Init.Mode = DMA_NORMAL;
  16.      NodeConfig.TriggerConfig.TriggerPolarity = DMA_TRIG_POLARITY_MASKED;
  17.      NodeConfig.DataHandlingConfig.DataExchange = DMA_EXCHANGE_NONE;
  18.      NodeConfig.DataHandlingConfig.DataAlignment = DMA_DATA_RIGHTALIGN_ZEROPADDED;
  19.      if (HAL_DMAEx_List_BuildNode(&NodeConfig, &Node_GPDMA1_Channel0) != HAL_OK)
  20.      {
  21.        Error_Handler();
  22.      }

  23.      if (HAL_DMAEx_List_InsertNode(&List_GPDMA1_Channel0, NULL, &Node_GPDMA1_Channel0) != HAL_OK)
  24.      {
  25.        Error_Handler();
  26.      }

  27.      if (HAL_DMAEx_List_SetCircularMode(&List_GPDMA1_Channel0) != HAL_OK)
  28.      {
  29.        Error_Handler();
  30.      }

  31.      handle_GPDMA1_Channel0.Instance = GPDMA1_Channel0;
  32.      handle_GPDMA1_Channel0.InitLinkedList.Priority = DMA_LOW_PRIORITY_LOW_WEIGHT;
  33.      handle_GPDMA1_Channel0.InitLinkedList.LinkStepMode = DMA_LSM_FULL_EXECUTION;
  34.      handle_GPDMA1_Channel0.InitLinkedList.LinkAllocatedPort = DMA_LINK_ALLOCATED_PORT0;
  35.      handle_GPDMA1_Channel0.InitLinkedList.TransferEventMode = DMA_TCEM_BLOCK_TRANSFER;
  36.      handle_GPDMA1_Channel0.InitLinkedList.LinkedListMode = DMA_LINKEDLIST_CIRCULAR;
  37.      if (HAL_DMAEx_List_Init(&handle_GPDMA1_Channel0) != HAL_OK)
  38.      {
  39.        Error_Handler();
  40.      }

  41.      if (HAL_DMAEx_List_LinkQ(&handle_GPDMA1_Channel0, &List_GPDMA1_Channel0) != HAL_OK)
  42.      {
  43.        Error_Handler();
  44.      }

  45.      __HAL_LINKDMA(huart, hdmarx, handle_GPDMA1_Channel0);

  46.      if (HAL_DMA_ConfigChannelAttributes(&handle_GPDMA1_Channel0, DMA_CHANNEL_NPRIV) != HAL_OK)
  47.      {
  48.        Error_Handler();
  49.      }
ST的库里面封装了下面这个函数用于启动dma接收  HAL_UARTEx_ReceiveToIdle_DMA(&huart3, aRXBufferUser, RX_BUFFER_SIZE))。这里面的数组是接收数据存储的地方,长度是一次最大的接收长度。在这个函数里面可以看到这个函数使能了串口空闲中断,并设置DMA接收的参数。这个函数非常方便,不需要我们去额外配置空闲中断,直接调用即可。
  1. HAL_StatusTypeDef HAL_UARTEx_ReceiveToIdle_DMA(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size)
  2. {
  3.   HAL_StatusTypeDef status;

  4.   /* Check that a Rx process is not already ongoing */
  5.   if (huart->RxState == HAL_UART_STATE_READY)
  6.   {
  7.     if ((pData == NULL) || (Size == 0U))
  8.     {
  9.       return HAL_ERROR;
  10.     }

  11.     /* Set Reception type to reception till IDLE Event*/
  12.     huart->ReceptionType = HAL_UART_RECEPTION_TOIDLE;
  13.     huart->RxEventType = HAL_UART_RXEVENT_TC;

  14.     status =  UART_Start_Receive_DMA(huart, pData, Size);

  15.     /* Check Rx process has been successfully started */
  16.     if (status == HAL_OK)
  17.     {
  18.       if (huart->ReceptionType == HAL_UART_RECEPTION_TOIDLE)
  19.       {
  20.         __HAL_UART_CLEAR_FLAG(huart, UART_CLEAR_IDLEF);
  21.         ATOMIC_SET_BIT(huart->Instance->CR1, USART_CR1_IDLEIE);
  22.       }
  23.       else
  24.       {
  25.      
  26.         status = HAL_ERROR;
  27.       }
  28.     }

  29.     return status;
  30.   }


为了验证功能,将接收的数据再次发送到stlink vcp 上
  1. void UserDataTreatment(UART_HandleTypeDef *huart, uint8_t* pData, uint16_t Size)
  2. {
  3.   
  4.   uint8_t* pBuff = pData;
  5.   uint8_t  i;

  6.     for (i = 0; i < Size; i++)
  7.   {
  8.     while (!(__HAL_UART_GET_FLAG(huart, UART_FLAG_TXE))) {}
  9.     huart->Instance->TDR = *pBuff;
  10.     pBuff++;
  11.   }

  12. }
还有就是USART3的中断函数了,里面直接对空闲中断做了判断,如果使能了DMA就调用对应的回调函数,读取接收到的数据。
3、测试结果:

Video_2023-08-24_155419.gif
测试代码如下,需要注意要把代码解压到STM32Cube_FW_H5_V1.1.0\Projects\NUCLEO-H563ZI\Examples\UART路径下面
UART_ReceptionToIdle_CircularDMA.rar (637.55 KB, 下载次数: 29)


uiint 发表于 2023-9-5 10:07 | 显示全部楼层
如何通过DMA实现不定长USART接收
maqianqu 发表于 2023-9-5 10:45 | 显示全部楼层
串口空闲中断是指在串口通信过程中,当检测到线路处于空闲状态时,由硬件自动产生一个中断信号,通知CPU可以进行数据发送或接收操作。这种中断信号可以帮助CPU更好地控制串口通信的流程。
mickit 发表于 2023-9-5 11:06 | 显示全部楼层
串口空闲中断+DMA接收不定长数据的开发需要使用HAL库提供的DMA驱动程序和API,同时需要注意串口空闲中断的配置和DMA接收的配置。
51xlf 发表于 2023-9-5 13:37 | 显示全部楼层
串口空闲中断+DMA接收不定长数据是一种常用的数据接收方式,可以在不影响主程序的情况下实现数据的接收和处理。
pl202 发表于 2023-9-5 18:13 | 显示全部楼层
串口接收不固定数据长度包,可以怎么处
mnynt121 发表于 2023-9-5 18:42 | 显示全部楼层
stm32 串口发送数组 cpu可以工作吗
Bowclad 发表于 2023-9-5 21:38 | 显示全部楼层
uiint 发表于 2023-9-5 10:07
如何通过DMA实现不定长USART接收

用空闲中断
maudlu 发表于 2023-9-5 22:09 | 显示全部楼层
通常采用DMA做USART接收固定长度的数据,或通过DMA完成固定数量的不间断数据流。
zwll 发表于 2023-9-5 22:10 | 显示全部楼层
硬件自动产生一个中断信号
modesty3jonah 发表于 2023-9-7 12:14 | 显示全部楼层
如何做到让串口接受不同长度的数据
everyrobin 发表于 2023-9-7 13:01 | 显示全部楼层
如何实现从串口接收一串不定长度的数据
yorkbarney 发表于 2023-9-7 16:06 | 显示全部楼层
需要在DMA接收完成后通过中断或其他方式通知CPU进行处理。这种方式可以避免CPU在接收数据时被频繁打断,提高系统的效率。
Undshing 发表于 2023-9-7 22:26 | 显示全部楼层
everyrobin 发表于 2023-9-7 13:01
如何实现从串口接收一串不定长度的数据

利用空闲中断就可以
c1486361959 发表于 2023-9-16 14:54 | 显示全部楼层
串口空闲中断
Jacquetry 发表于 2023-9-16 21:59 | 显示全部楼层
pl202 发表于 2023-9-5 18:13
串口接收不固定数据长度包,可以怎么处

可以用空闲中断
您需要登录后才可以回帖 登录 | 注册

本版积分规则

23

主题

45

帖子

7

粉丝
快速回复 返回顶部 返回列表