简体   繁体   中英

HAL_UART_Receive_DMA works only once. How can I fix it?

I am working with STM32F103RB board and I want to simply echo everything I receive from my computer via serial port back to that port. I must do it using UART and DMA. I've set up DMA on USART2_RX with CubeMX in normal mode. My problem is that HAL_UART_RxCpltCallback gets called only once. Interestingly I get the response on PC end but with additional 0xFC byte. After that the controller stops reacting to new data sent through the serial port and won't do so until I unplug and plug controller back in with usb. Here is the code for call back and main function:

#define BUF_SIZE 16

uint8_t RX_BUF[BUF_SIZE] = {0};
uint8_t TX_BUF[BUF_SIZE] = {0};

void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{
  HAL_UART_Transmit(&huart2, RX_BUF, BUF_SIZE, 1000);
  HAL_UART_Receive_DMA(&huart2, RX_BUF, BUF_SIZE);
}

int main(void)
{
  /* USER CODE BEGIN 1 */

  /* USER CODE END 1 */

  /* MCU Configuration--------------------------------------------------------*/

  /* Reset of all peripherals, Initializes the Flash interface and the Systick. */
  HAL_Init();

  /* USER CODE BEGIN Init */

  /* USER CODE END Init */

  /* Configure the system clock */
  SystemClock_Config();

  /* USER CODE BEGIN SysInit */

  /* USER CODE END SysInit */

  /* Initialize all configured peripherals */
  MX_GPIO_Init();
  MX_DMA_Init();
  MX_USART2_UART_Init();
  /* USER CODE BEGIN 2 */
  HAL_UART_Receive_DMA(&huart2, RX_BUF, sizeof (RX_BUF));
  /* USER CODE END 2 */

  /* Infinite loop */
  /* USER CODE BEGIN WHILE */
  while (1)
  {
    /* USER CODE END WHILE */

    /* USER CODE BEGIN 3 */
  }
  /* USER CODE END 3 */
}

Edit: I am trying to do this on a Mac through USB-C dongle. Can this be the problem?

Calling complex functions in an ISR is a bad idea. Many of the HAL functions use the system tick to measure timeouts, and depending on your interrupt priorities, the tick may be blocked while your ISR runs.

It would be better to move any complex processing out of your ISR, into the main function. Just set a flag in the ISR to indicate when reception is complete. Something like:

static volatile bool uart_rx_done = false;

void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{
    uart_rx_done = true;
}

int main()
{
    // Initialisation stuff

    for (;;)
    {
        uart_rx_done = false;
        HAL_UART_Receive_DMA(&huart2, RX_BUF, BUF_SIZE);
        while (!uart_rx_done)
            ;

        HAL_UART_Transmit(&huart2, RX_BUF, BUF_SIZE, 1000);
    }
}

First of all, check if you configured uart and dma interrupts correctly. in the stm21f1xx_it.c file you should see these functions

void DMA1_Channel6_IRQHandler(void)
{
  /* USER CODE BEGIN DMA1_Channel6_IRQn 0 */

  /* USER CODE END DMA1_Channel6_IRQn 0 */
  HAL_DMA_IRQHandler(&hdma_usart2_rx);
  /* USER CODE BEGIN DMA1_Channel6_IRQn 1 */

  /* USER CODE END DMA1_Channel6_IRQn 1 */
}


void USART2_IRQHandler(void)
{
  /* USER CODE BEGIN USART2_IRQn 0 */

  /* USER CODE END USART2_IRQn 0 */
  HAL_UART_IRQHandler(&huart2);
  /* USER CODE BEGIN USART2_IRQn 1 */

  /* USER CODE END USART2_IRQn 1 */
}

and also a uart2 configuration in the MX_NVIC_Init() function in your main.c file.

But your main mistake is that you're not activating uart receive interrupt at all. I suggest not using dma for now, and try this to see if the uart interrupt gets activated:

#define BUF_SIZE 16
bool uart_received = false;

uint8_t RX_BUF[BUF_SIZE] = {0};
uint8_t TX_BUF[BUF_SIZE] = {0};

void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{
  // HAL_UART_Transmit(&huart2, RX_BUF, BUF_SIZE, 1000);
  uart_received = true;
  HAL_UART_Receive_IT(&huart2, RX_BUF, BUF_SIZE);
}

int main(void)
{
  /* USER CODE BEGIN 1 */

  /* USER CODE END 1 */

  /* MCU Configuration--------------------------------------------------------*/

  /* Reset of all peripherals, Initializes the Flash interface and the Systick. */
  HAL_Init();

  /* USER CODE BEGIN Init */

  /* USER CODE END Init */

  /* Configure the system clock */
  SystemClock_Config();

  /* USER CODE BEGIN SysInit */

  /* USER CODE END SysInit */

  /* Initialize all configured peripherals */
  MX_GPIO_Init();
  MX_DMA_Init();
  MX_USART2_UART_Init();
  /* USER CODE BEGIN 2 */
  //HAL_UART_Receive_DMA(&huart2, RX_BUF, sizeof (RX_BUF));
  HAL_UART_Receive_IT(&huart2, RX_BUF, BUF_SIZE);
  /* USER CODE END 2 */

  /* Infinite loop */
  /* USER CODE BEGIN WHILE */
  while (1)
  {
      if (uart_received) {
          HAL_UART_Transmit(&huart2, RX_BUF, BUF_SIZE, 1000);
          uart_received = false;
      }
    /* USER CODE END WHILE */

    /* USER CODE BEGIN 3 */
  }
  /* USER CODE END 3 */
}

If this works, then you can use HAL_UARTEx_ReceiveToIdle_DMA for activating uart-dma interrupt and HAL_UARTEx_RxEventCallback for callback function (assuming the dam interrupt is configured).

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM