1
\$\begingroup\$

I am trying to use UART with DMA,

before the DMA I tried polling method with while(HAL_UART_Transmit(&huart2, (uint8_t *)rs485TxBuffer, 17, 100) != HAL_OK); function and I could communicate successfully,

When I using the

while(HAL_UART_Transmit_DMA(&huart2, (uint8_t *)rs485TxBuffer, 17)!= HAL_OK);

function, data not sending correctly, just first byte correct another bytes are wrong. Why this happened ? How can I solve this problem ?

This is my initilizing sequence:

  MX_DMA_Init();
  MX_GPIO_Init();
  MX_USART1_UART_Init();
  MX_USART2_UART_Init();

Below lines try to send data:

 while(1U)
  {

   // Communication Test Block //

    while(HAL_UART_Transmit_DMA(&huart2, (uint8_t *)rs485TxBuffer, 17)!= HAL_OK);
        // while(HAL_UART_Transmit(&huart2, (uint8_t *)rs485TxBuffer, 17, 100) != HAL_OK);

}

This is the initilizing functions:

..
else if(huart->Instance==USART2)
      {
      /* USER CODE BEGIN USART2_MspInit 0 */
    
      /* USER CODE END USART2_MspInit 0 */
        /* Peripheral clock enable */
        __HAL_RCC_USART2_CLK_ENABLE();
    
        __HAL_RCC_GPIOD_CLK_ENABLE();
        /**USART2 GPIO Configuration
        PD5     ------> USART2_TX
        PD6     ------> USART2_RX
        */
        GPIO_InitStruct.Pin = USART2_RS485_TX_Pin|USART2_RS485_RX_Pin;
        GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
        GPIO_InitStruct.Pull = GPIO_PULLUP;
        GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
        GPIO_InitStruct.Alternate = GPIO_AF7_USART2;
        HAL_GPIO_Init(GPIOD, &GPIO_InitStruct);
    
        /* USART2 DMA Init */
        /* USART2_RX Init */
        hdma_usart2_rx.Instance = DMA1_Stream5;
        hdma_usart2_rx.Init.Channel = DMA_CHANNEL_4;
        hdma_usart2_rx.Init.Direction = DMA_PERIPH_TO_MEMORY;
        hdma_usart2_rx.Init.PeriphInc = DMA_PINC_DISABLE;
        hdma_usart2_rx.Init.MemInc = DMA_MINC_ENABLE;
        hdma_usart2_rx.Init.PeriphDataAlignment = DMA_PDATAALIGN_BYTE;
        hdma_usart2_rx.Init.MemDataAlignment = DMA_MDATAALIGN_BYTE;
        hdma_usart2_rx.Init.Mode = DMA_NORMAL;
        hdma_usart2_rx.Init.Priority = DMA_PRIORITY_MEDIUM;
        hdma_usart2_rx.Init.FIFOMode = DMA_FIFOMODE_DISABLE;
        if (HAL_DMA_Init(&hdma_usart2_rx) != HAL_OK)
        {
          Error_Handler();
        }
    
        __HAL_LINKDMA(huart,hdmarx,hdma_usart2_rx);
    
        /* USART2_TX Init */
        hdma_usart2_tx.Instance = DMA1_Stream6;
        hdma_usart2_tx.Init.Channel = DMA_CHANNEL_4;
        hdma_usart2_tx.Init.Direction = DMA_MEMORY_TO_PERIPH;
        hdma_usart2_tx.Init.PeriphInc = DMA_PINC_DISABLE;
        hdma_usart2_tx.Init.MemInc = DMA_MINC_ENABLE;
        hdma_usart2_tx.Init.PeriphDataAlignment = DMA_PDATAALIGN_HALFWORD;
        hdma_usart2_tx.Init.MemDataAlignment = DMA_MDATAALIGN_HALFWORD;
        hdma_usart2_tx.Init.Mode = DMA_NORMAL;
        hdma_usart2_tx.Init.Priority = DMA_PRIORITY_MEDIUM;
        hdma_usart2_tx.Init.FIFOMode = DMA_FIFOMODE_DISABLE;
        if (HAL_DMA_Init(&hdma_usart2_tx) != HAL_OK)
        {
          Error_Handler();
        }
    
        __HAL_LINKDMA(huart,hdmatx,hdma_usart2_tx);
    
        /* USART2 interrupt Init */
    
        HAL_NVIC_SetPriority(USART2_IRQn, 0, 0);
        HAL_NVIC_EnableIRQ(USART2_IRQn);
      /* USER CODE BEGIN USART2_MspInit 1 */
    
      /* USER CODE END USART2_MspInit 1 */
      }

This is the interrupt function in stm32f4xx_it.c

void DMA1_Stream5_IRQHandler(void)
{
  /* USER CODE BEGIN DMA1_Stream5_IRQn 0 */

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

  /* USER CODE END DMA1_Stream5_IRQn 1 */
}

/**
  * @brief This function handles DMA1 stream6 global interrupt.
  */
void DMA1_Stream6_IRQHandler(void)
{
  /* USER CODE BEGIN DMA1_Stream6_IRQn 0 */

  /* USER CODE END DMA1_Stream6_IRQn 0 */
  HAL_DMA_IRQHandler(&hdma_usart2_tx);
  /* USER CODE BEGIN DMA1_Stream6_IRQn 1 */

  /* USER CODE END DMA1_Stream6_IRQn 1 */
}

/**
  * @brief This function handles USART2 global interrupt.
  */
void USART2_IRQHandler(void)
{
  /* USER CODE BEGIN USART2_IRQn 0 */
// SerialPrint("Hi");
  /* USER CODE END USART2_IRQn 0 */
  HAL_UART_IRQHandler(&huart2);
  /* USER CODE BEGIN USART2_IRQn 1 */

  /* USER CODE END USART2_IRQn 1 */
}

EDIT:

I tried these method but not worked for me.

  while(1U)
  {

   // Communication Test Block //

    HAL_UART_Transmit_DMA(&huart2, (uint8_t *)rs485TxBuffer, 17);
    while(!dmaTransmitCompletedFlag);
    dmaTransmitCompletedFlag = 0;
   }

  ..
void HAL_UART_TxHalfCpltCallback(UART_HandleTypeDef *huart)
{

  dmaTransmitCompletedFlag = 1;

}
\$\endgroup\$
6
  • \$\begingroup\$ You do transmit with non-blocking mode, the DMA takes care of it, then you get an interrupt when the transmission is over. Waiting for HAL_OK is wrong. \$\endgroup\$ Commented Jun 16, 2021 at 7:26
  • \$\begingroup\$ I added another method I've already tried, please check. \$\endgroup\$
    – mathco
    Commented Jun 16, 2021 at 7:29
  • \$\begingroup\$ What about to wait a complete transmission. UART_DMATransmitCplt \$\endgroup\$ Commented Jun 16, 2021 at 7:32
  • \$\begingroup\$ while(dmaTransmitCompletedFlag); and not while(!dmaTransmitCompletedFlag); \$\endgroup\$ Commented Jun 16, 2021 at 7:34
  • \$\begingroup\$ @MarkoBuršič I dont understand sorry. How can I do that ?while(dmaTransmitCompletedFlag) always true, what is the purpose? \$\endgroup\$
    – mathco
    Commented Jun 16, 2021 at 7:38

3 Answers 3

4
\$\begingroup\$

Problem:

    hdma_usart2_tx.Init.PeriphDataAlignment = DMA_PDATAALIGN_HALFWORD;
    hdma_usart2_tx.Init.MemDataAlignment = DMA_MDATAALIGN_HALFWORD;

These two should be set to byte alignment. It's set to halfword, so it does DMA on 2 bytes, but the UART register only has 1 byte, so half the bytes fall into a black hole:

uint8_t rs485TxBuffer[17]={0x02,0x0A,0x00,0x01,0x00,0x00,0x00,0x01,0x0D,..} 
but I see the data like 02 00 00 00 0D 00 00 84 0A 02 04 on the line. 

Only the bytes in bold are transmitted:

0x02,0x0A,0x00,0x01,0x00,0x00,0x00,0x01,0x0D

Also you can enable the UART FIFO.

About this code:

while(HAL_UART_Transmit_DMA(&huart2, (uint8_t *)rs485TxBuffer, 17)!= HAL_OK);

If the DMA core is busy, HAL_UART_Transmit_DMA will return HAL_BUSY. So the while(); will wait. When the DMA core is available, it will initiate the transfer and return HAL_OK. Note this while() exits after the transfer is initiated, but it does not wait for the transfer to finish. The transfer will happen in the background, which is the point of DMA. But if you touch some UART registers, or use HAL_UART_Transmit while the DMA is running, then it could mess with it and that would explain your problems. To know if the transfer is finished, use the interrupt. And if you use a global variable, set it to volatile.

\$\endgroup\$
4
  • \$\begingroup\$ Thank you very much, when I update configuration like this hdma_usart2_tx.Init.PeriphDataAlignment = DMA_PDATAALIGN_BYTE; hdma_usart2_tx.Init.MemDataAlignment = DMA_MDATAALIGN_BYTE; Its work for me. \$\endgroup\$
    – mathco
    Commented Jun 16, 2021 at 8:13
  • \$\begingroup\$ Excellent news! \$\endgroup\$
    – bobflux
    Commented Jun 16, 2021 at 8:17
  • 1
    \$\begingroup\$ Bumped into this while looking for solution why my SPI would lose every other byte if using DMA for endianness swapping on incoming half-words. For me critical pointer was part of the sentence "it does DMA on 2 bytes, but the UART registers only has 1 byte, so half the bytes fall into a black hole". In my case SPI does have 16bit DR register, but it gets only half filled, if used in 8bit mode. Anyhow, came here to thank you :) \$\endgroup\$
    – stiebrs
    Commented Mar 14, 2023 at 7:21
  • \$\begingroup\$ You're welcome, glad it could help! \$\endgroup\$
    – bobflux
    Commented Mar 14, 2023 at 9:08
1
\$\begingroup\$

You may not re-transmit until the previous message isn't completely sent.

dmaTransmitCompletedFlag = 1;
while(1U)
  {

   // Communication Test Block //
    
    if dmaTransmitCompletedFlag 
    {
       dmaTransmitCompletedFlag = 0;
       HAL_UART_Transmit_DMA(&huart2, (uint8_t *)rs485TxBuffer, 17);
    }
  }

  ..
void UART_DMATransmitCplt(&huart2)
{

  dmaTransmitCompletedFlag = 1;

}
\$\endgroup\$
3
  • \$\begingroup\$ I've tried below code, but I couldnt send correct data again. It doesnt work for me. \$\endgroup\$
    – mathco
    Commented Jun 16, 2021 at 7:53
  • \$\begingroup\$ uint8_t rs485TxBuffer[17]={0x02,0x0A, 0x00,0x01,0x00,0x00,0x00,0x01,0x0D,..} but I see the data like 02 00 00 00 0D 00 00 84 0A 02 04 on the line. \$\endgroup\$
    – mathco
    Commented Jun 16, 2021 at 7:54
  • 1
    \$\begingroup\$ when I use the HAL_UART_Transmit function instead of HAL_UART_Transmit_DMA everything is going great . I dont understand why ? \$\endgroup\$
    – mathco
    Commented Jun 16, 2021 at 7:56
0
\$\begingroup\$

Disable Circular mode in the gpdma channel configuration and then after the tx complete interrupt will be triggered properly

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.