ST意法半导体
直播中

吴湛

10年用户 928经验值
擅长:电源/新能源
私信 关注
[问答]

HAL_UART_DMAStop不会停止UART RX上的DMA怎么解决?

使用 Nucleo-G431KB 开发板,我玩了一下 UART RX DMA。
我在线性缓冲区上启动了 DMA RX,然后停止并重新启动 DMA。
我刚刚观察到,在接收到几个字节后,HAL_UART_DMAStop 不再停止 UART RX 上的 DMA。HAL_UART_DMAStop 调用不会返回错误,但 HAL_UART_Receive_DMA 会返回错误,因为传输从未停止过。我还观察到字符仍会被接收到 DMA UART RX 缓冲区,确认 DMA 没有停止。
它仅在我在缓冲区中收到几个字节后才会发生。单个字节不会造成任何问题。
我找不到根本原因。
无论如何,仅在循环缓冲区上启动一次 HAL_UART_Receive_DMA 并检查 __HAL_DMA_GET_COUNTER 是输入随机大小的 UART 数据的更好方法。
我只是想知道是否有人也注意到了这一点。
它是 HAL 中的错误吗?
不幸的是,我不再拥有代码,但它只是简单明了,就像在循环中调用一样,大致如下:
  • uint8_t DataRX[256];
  • while (1)
  • {
  •   HAL_UART_Receive_DMA(&huart1, DataRX, sizeof(DataRX))
  •   //Checking length and print buffer (on a different UART)
  •   uint16_t Last_Byte_Index = (uint16_t)(sizeof(DataRX) - __HAL_DMA_GET_COUNTER(huart1.hdmarx));
  •   // ... printing the buffer on another UART
  •   HAL_UART_DMAStop(&huart1);
  • }








回帖(1)

彭玉林

2023-2-1 14:27:20
我一定是因为一些愚蠢的错误而失明了。我没有保留该代码,所以我不得不重做它,然后似乎没有遇到任何问题。我一定有点老了。
尽管如此,无论如何,启用 uart 中断似乎是最佳实践,如果它只是清除错误标志,例如在连接和断开线路时很容易发生的帧错误。
我想我会分享一些直接的示例供其他(合作)开发人员使用。
在阻塞 HAL_Delay 期间,使用 UART RX 上的 DMA 获取字符。
我们可以使用 UART RTO 中断(以位数表示的接收超时)来检测数据接收的结束(可能在接收 Modbus 时,35 位是 3.5 个字符的超时),或者对接收到的数据进行 pol,或者识别一些特定的接收数据。
请注意,我认为应始终避免阻止呼叫。这只是一个简单的演示示例。
还应该尽可能多地使用硬件来完成,因此尽可能多地使用 DMA 通常是一件好事。MCU 然后可以同时执行其他任务。
第一:Mode = DMA_CIRCULAR,启动一次(我认为最好的解决方案,因为不会遗漏任何字符)
第二:DMA_NORMAL 或 DMA_CIRCULAR,启动/停止


  •   /* Mode = DMA_CIRCULAR, start once */

  •   /* Interrupt Enable */
  •   HAL_NVIC_EnableIRQ(USART1_IRQn);

  •   uint8_t DataRX[256];
  •   if (HAL_UART_Receive_DMA(&huart1, DataRX, sizeof(DataRX)) != HAL_OK)
  •   {
  •     /* Error */
  •     char Error[] = "Error HAL_UART_Receive_DMAn";
  •     HAL_UART_Transmit(&huart2, (uint8_t*)Error, strlen(Error), 100); /* To USB serial port */
  •   }

  •   uint16_t First_Byte_Index = 0;
  •   uint16_t Last_Byte_Index_Previous = 0;
  •   while (1)
  •   {
  •     /* Show what's in the DataRX */
  •     uint16_t Last_Byte_Index = (uint16_t)(sizeof(DataRX) - __HAL_DMA_GET_COUNTER(huart1.hdmarx));
  •     uint16_t DataRX_Length = Last_Byte_Index - Last_Byte_Index_Previous;
  •     First_Byte_Index = Last_Byte_Index - DataRX_Length;
  •     /* If the new Index is greater than Index_old, we received something */
  •     if (DataRX_Length)
  •     {
  •       char Received[] = "Received: ";
  •       HAL_UART_Transmit(&huart2, (uint8_t*)Received, strlen(Received), 100); /* To USB serial port */
  •       for (uint16_t i = 0; i < DataRX_Length; i++)
  •       {
  •         uint16_t DataRX_Index = i + First_Byte_Index;
  •         /* Roll over DataRX_index if necessary */
  •         if (DataRX_Index > sizeof(DataRX))
  •         {
  •           DataRX_Index -= sizeof(DataRX);
  •         }
  •         HAL_UART_Transmit(&huart2, &DataRX[DataRX_Index], 1, 100); /* To USB serial port */
  •       }
  •       char CR[] = "n";
  •       HAL_UART_Transmit(&huart2, (uint8_t*)CR, strlen(CR), 100); /* To USB serial port */

  •       /* Reset Index_old to latest Index */
  •       Last_Byte_Index_Previous = Last_Byte_Index;
  •     }

  •     HAL_UART_Transmit(&huart2, (uint8_t*)Test, strlen(Test), 100); /* To USB serial port */

  •     HAL_Delay(1000);
  •     HAL_IWDG_Refresh(&hiwdg); /* Don't forget to take good care of your watch-dog, feed it! */
  •   }



  •   /* Mode = DMA_NORMAL or DMA_CIRCULAR, start/stop */

  •   /* Interrupt Enable */
  •   HAL_NVIC_EnableIRQ(USART1_IRQn);

  •   uint8_t DataRX[256];
  •   while (1)
  •   {
  •     if (HAL_UART_Receive_DMA(&huart1, DataRX, sizeof(DataRX)) != HAL_OK)
  •     {
  •       /* Error */
  •       char Error[] = "Error HAL_UART_Receive_DMAn";
  •       HAL_UART_Transmit(&huart2, (uint8_t*)Error, strlen(Error), 100); /* To USB serial port */
  •     }

  •     HAL_Delay(1000);

  •     /*Checking length and print buffer (on a different UART) */
  •     uint16_t Last_Byte_Index = (uint16_t)(sizeof(DataRX) - __HAL_DMA_GET_COUNTER(huart1.hdmarx));

  •     /* printing the buffer on another UART */
  •     if (Last_Byte_Index)
  •     {
  •       char Received[] = "Received: ";
  •       HAL_UART_Transmit(&huart2, (uint8_t*)Received, strlen(Received), 100); /* To USB serial port */
  •       for (uint16_t i = 0; i < Last_Byte_Index; i++)
  •       {
  •         HAL_UART_Transmit(&huart2, &DataRX, 1, 100); /* To USB serial port */
  •       }
  •       char CR[] = "n";
  •       HAL_UART_Transmit(&huart2, (uint8_t*)CR, strlen(CR), 100); /* To USB serial port */
  •     }

  •     if (HAL_UART_DMAStop(&huart1) != HAL_OK)
  •     {
  •       /* Error */
  •       char Error[] = "Error HAL_UART_DMAStopn";
  •       HAL_UART_Transmit(&huart2, (uint8_t*)Error, strlen(Error), 100); /* To USB serial port */
  •     }

  •     HAL_IWDG_Refresh(&hiwdg); /* Don't forget to take good care of your watch-dog, feed it! */
  •   }

在 stm32g4xx_it.c 中我们清除了一些错误标志,它们会不会发生。我们可以不检查就清除它们,但也许有人会维护帧和奇偶校验错误计数器。

  • /**
  •   * @brief This function handles USART1 global interrupt / USART1 wake-up interrupt through EXTI line 25.
  •   */
  • void USART1_IRQHandler(void)
  • {
  •   /* USER CODE BEGIN USART1_IRQn 0 */

  •   /* Noise immunity feature enabled for RS (Modbus-RTU) interfaces */
  •   /* Clear the errors if they occurred */

  •   /* UART parity error detected */
  •   if (__HAL_UART_GET_FLAG(&huart1, UART_FLAG_PE) == SET)
  •   {
  •     __HAL_UART_CLEAR_PEFLAG(&huart1);
  •   }
  •   /* UART frame error detected or break character is detected */
  •   if (__HAL_UART_GET_FLAG(&huart1, UART_FLAG_FE) == SET)
  •   {
  •     __HAL_UART_CLEAR_FEFLAG(&huart1);
  •   }
  •   /* UART noise error detected */
  •   if (__HAL_UART_GET_FLAG(&huart1, UART_FLAG_NE) == SET)
  •   {
  •     __HAL_UART_CLEAR_NEFLAG(&huart1);
  •   }
  •   /* UART overrun error detected */
  •   if (__HAL_UART_GET_FLAG(&huart1, UART_FLAG_ORE) == SET)
  •   {
  •     __HAL_UART_CLEAR_OREFLAG(&huart1);
  •   }
  •   /* UART idle flag */
  •   if((__HAL_UART_GET_FLAG(&huart1, UART_FLAG_IDLE) != RESET))
  •   {
  •     __HAL_UART_CLEAR_IDLEFLAG(&huart1);
  •   }

  •   /* USER CODE END USART1_IRQn 0 */
  •   HAL_UART_IRQHandler(&huart1);
  •   /* USER CODE BEGIN USART1_IRQn 1 */

  •   /* USER CODE END USART1_IRQn 1 */
  • }
举报

更多回帖

发帖
×
20
完善资料,
赚取积分