cancel
Showing results for 
Search instead for 
Did you mean: 

Using DMA with UART on STM32H753ZI and getting HAL_DMA_ERROR_TE (Transmission Error)

KEria.1
Associate III

Hello,

I am trying to set the UART5 TX with the DMA. I am using Cube, and in the GUI I selected UART5 to asynchronous, and added DMA for UART5_TX. Then, in the generated code, I added this part as I didn't see initialization for the DMA (inside the MX_DMA_Init()):

  /* Peripheral DMA init*/
  hdma_uart5_tx.Instance = DMA1_Stream0;
  //hdma_uart5_tx.Init.Channel = DMA_CHANNEL_4;
  hdma_uart5_tx.Init.Direction = DMA_MEMORY_TO_PERIPH;
  hdma_uart5_tx.Init.PeriphInc = DMA_PINC_DISABLE;
  hdma_uart5_tx.Init.MemInc = DMA_MINC_ENABLE;
  hdma_uart5_tx.Init.PeriphDataAlignment = DMA_MDATAALIGN_BYTE;
  hdma_uart5_tx.Init.MemDataAlignment = DMA_MDATAALIGN_BYTE;
  hdma_uart5_tx.Init.Mode = DMA_NORMAL;
  hdma_uart5_tx.Init.Priority = DMA_PRIORITY_MEDIUM;
  hdma_uart5_tx.Init.FIFOMode = DMA_FIFOMODE_DISABLE;
    HAL_DMA_Init(&hdma_uart5_tx);
 
    __HAL_LINKDMA(&huart5,hdmatx,hdma_uart5_tx);

The Main function, I didn't do much except I added this code in the while(1):

while (1)
  {
    /* USER CODE END WHILE */
 
    /* USER CODE BEGIN 3 */
	  if (HAL_UART_Transmit_DMA(&huart5,au8ch, 1) == HAL_OK)
	  {
		  HAL_GPIO_WritePin(GPIOE, GPIO_PIN_1, GPIO_PIN_SET);
	  }
	  else
	  {
		  HAL_GPIO_WritePin(GPIOE, GPIO_PIN_1, GPIO_PIN_RESET);
	  }
	  HAL_Delay(1000);
  }
  /* USER CODE END 3 */

Which is a code to transmit the first byte of an array. I connected the Nucleo-H753ZI board pins to a terminal software, and I didn't get anything. (Note that I was able to get something without using the DMA). The LED turned ON, which means the function is returning HAL_OK, but still nothing on the screen.

I tried to debug the code, I put a breakpoint in the function "void HAL_DMA_IRQHandler(DMA_HandleTypeDef *hdma)" and it stops there, which means it got the interrupt to send something, but then it goes into this autogenerated code:

if(IS_DMA_STREAM_INSTANCE(hdma->Instance) != 0U)  /* DMA1 or DMA2 instance */
  {
    /* Transfer Error Interrupt management ***************************************/
    if ((tmpisr_dma & (DMA_FLAG_TEIF0_4 << (hdma->StreamIndex & 0x1FU))) != 0U)
    {
      if(__HAL_DMA_GET_IT_SOURCE(hdma, DMA_IT_TE) != 0U)
      {
        /* Disable the transfer error interrupt */
        ((DMA_Stream_TypeDef   *)hdma->Instance)->CR  &= ~(DMA_IT_TE);
 
        /* Clear the transfer error flag */
        regs_dma->IFCR = DMA_FLAG_TEIF0_4 << (hdma->StreamIndex & 0x1FU);
 
        /* Update error code */
        hdma->ErrorCode |= HAL_DMA_ERROR_TE;
      }
    }
...
...
...
/* manage error case */
    if(hdma->ErrorCode != HAL_DMA_ERROR_NONE)
    {
      if((hdma->ErrorCode & HAL_DMA_ERROR_TE) != 0U)
      {
        hdma->State = HAL_DMA_STATE_ABORT;
 
        /* Disable the stream */
        __HAL_DMA_DISABLE(hdma);
 
        do
        {
          if (++count > timeout)
          {
            break;
          }
        }
        while((((DMA_Stream_TypeDef   *)hdma->Instance)->CR & DMA_SxCR_EN) != 0U);
 
        /* Process Unlocked */
        __HAL_UNLOCK(hdma);
 
        if((((DMA_Stream_TypeDef   *)hdma->Instance)->CR & DMA_SxCR_EN) != 0U)
        {
          /* Change the DMA state to error if DMA disable fails */
          hdma->State = HAL_DMA_STATE_ERROR;
        }
        else
        {
          /* Change the DMA state to Ready if DMA disable success */
          hdma->State = HAL_DMA_STATE_READY;
        }
      }
 
      if(hdma->XferErrorCallback != NULL)
      {
        /* Transfer error callback */
        hdma->XferErrorCallback(hdma);
      }
    }
  }

Any idea what I may be missing?

1 ACCEPTED SOLUTION

Accepted Solutions
KEria.1
Associate III

@Community member​  and @TDK​ Thank you both for your help.

I figured it out. I had to build void HAL_UART_TxHalfCpltCallback(UART_HandleTypeDef *huart) function in which I put the values I need to send, also in the Cube GUI, I had to make it Normal not circular in order to send the data only once.

It is working to send one array now. Thanks!

Karim

View solution in original post

12 REPLIES 12
TDK
Guru

Make sure the au8ch buffer is somewhere accessible by DMA. What memory address is it at?

If you feel a post has answered your question, please click "Accept as Solution".
KEria.1
Associate III

0x2001fff4

I initialized it at the beginning of the main()

That address is in DTCM, which is not accessible to DMA.
https://community.st.com/s/article/FAQ-DMA-is-not-working-on-STM32H7-devices
If you feel a post has answered your question, please click "Accept as Solution".

I was reading this link, I tried placing it outside the main as a global var but it was 0x20000000 which is still the same domain.

How can it place it elsewhere?

I added this part to my code:

but still getting the same error, the array is now located at 0x20000010 which is still in the same range.

#if defined( __ICCARM__ )
  #define DMA_BUFFER \
      _Pragma("location=\".dma_buffer\"")
#else
  #define DMA_BUFFER \
      __attribute__((section(".dma_buffer")))
#endif
 
DMA_BUFFER unsigned char au8ch[4] = {70,54,35,4};

Have you read the article I gave link to? Read it. Reread it. For gcc (which is the default compiler for CubeIDE), you have to read the section about the linker script.

JW

Thanks, I read it, and reread it, and re-reread it.. Here is what I did:

in the file: STM32H753ZITX_RAM.ld I changed this code (I put what I changed in the comments below in the file) as recommended in solutions 1 and 2.

I only deployed solution 1 at first and it didn't fix it, so I added this section at the end.

  .data :
  {
    . = ALIGN(4);
    _sdata = .;        /* create a global symbol at data start */
    *(.data)           /* .data sections */
    *(.data*)          /* .data* sections */
 
    . = ALIGN(4);
    _edata = .;        /* define a global symbol at data end */
  } >RAM_D1 AT> FLASH /* was >RAM AT> RAM_EXEC*/
 
  /* Uninitialized data section */
  . = ALIGN(4);
  .bss :
  {
    /* This is used by the startup in order to initialize the .bss secion */
    _sbss = .;         /* define a global symbol at bss start */
    __bss_start__ = _sbss;
    *(.bss)
    *(.bss*)
    *(COMMON)
 
    . = ALIGN(4);
    _ebss = .;         /* define a global symbol at bss end */
    __bss_end__ = _ebss;
  } >RAM_D1 AT> FLASH /* was >RAM*/
 
  /* User_heap_stack section, used to check that there is enough RAM left */
  ._user_heap_stack :
  {
    . = ALIGN(8);
    PROVIDE ( end = . );
    PROVIDE ( _end = . );
    . = . + _Min_Heap_Size;
    . = . + _Min_Stack_Size;
    . = ALIGN(8);
  } >RAM_D1 AT> FLASH /* was >RAM*/
 
/* I also added this part: */
  .dma_buffer : 
  {
    *(.dma_buffer)
  } >RAM_D2

Then I added the code in the main.c in the previous comment.

#if defined( __ICCARM__ )
  #define DMA_BUFFER \
      _Pragma("location=\".dma_buffer\"")
#else
  #define DMA_BUFFER \
      __attribute__((section(".dma_buffer")))
#endif
 
DMA_BUFFER unsigned char au8ch[4] = {70,54,35,4};

But still, I got nothing. Any advice?

> But still, I got nothing.

That's an equivalent of "it does not work" and is not helpful.

So, how are your variables allocated now? What's in the mapfile? How is RAM_D2 set? Do you change the linker script you are actually using?

I don't use Cube/CubeIDE/Eclipse so can't really help in this detail.

JW