HAL_UART_Transmit_DMA function sending wrong data

dmastm32uart

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;

}

Best Answer

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.

Related Topic