Showing results for 
Search instead for 
Did you mean: 

STM32f030 HAL: Problem with UART DMA receive: memory increment not set properly (with workaround)

Associate II

I have written a program in the STM32CubeIDE to receive UART data using DMA. I am using the STM32F030K6T6 processor. I am using the HAL functions, eventually doing a call to HAL_UART_Receive_DMA.

In the *.ioc file, USART1_RX is set to use DMA1 Channel 3, Peropheral to memory, and Memory increment is checked.

Stepping through the HAL code with the IDE, there is generated code like this in main:

  /* Initialize all configured peripherals */

The call to MX_USART1_UART_Init gives rise to the following stack trace:

(gdb) bt
#0  HAL_DMA_Init (hdma=0x200001a4 <hdma_usart1_rx>) at ../Drivers/STM32F0xx_HAL_Driver/Src/stm32f0xx_hal_dma.c:140
#1  0x08001340 in HAL_UART_MspInit (uartHandle=0x200001e8 <huart1>) at ../Core/Src/usart.c:96
#2  0x08004a64 in HAL_RS485Ex_Init (huart=0x200001e8 <huart1>, Polarity=0, AssertionTime=0, DeassertionTime=0)
    at ../Drivers/STM32F0xx_HAL_Driver/Src/stm32f0xx_hal_uart_ex.c:189
#3  0x0800126a in MX_USART1_UART_Init () at ../Core/Src/usart.c:45
#4  0x08000742 in main () at ../Core/Src/main.c:122

The HAL_DMA_Init function tries to set the DMA's CCR register with among other things the memory increment bit. However, when I examine the CCR register after the following snippet, it is zero even though tmp is 0x80 (the memory increment bit is set):

  /* Get the CR register value */
  tmp = hdma->Instance->CCR;
  /* Clear PL, MSIZE, PSIZE, MINC, PINC, CIRC, DIR bits */
  tmp &= ((uint32_t)~(DMA_CCR_PL    | DMA_CCR_MSIZE  | DMA_CCR_PSIZE  | \
                      DMA_CCR_MINC  | DMA_CCR_PINC   | DMA_CCR_CIRC   | \
  /* Prepare the DMA Channel configuration */
  tmp |=  hdma->Init.Direction        |
          hdma->Init.PeriphInc           | hdma->Init.MemInc           |
          hdma->Init.PeriphDataAlignment | hdma->Init.MemDataAlignment |
          hdma->Init.Mode                | hdma->Init.Priority;
  /* Write to DMA Channel CR register */
  hdma->Instance->CCR = tmp;  

There must be something that is preventing the memory increment bit from being set here, which causes it to be lost, and my code not to work. This is where I believe there is a bug in the HAL.

As a work-around I have added a line to HAL_DMA_Start_IT function in stm32f0xx_hal_dma.c to set the bit here. It's surely not a good fix, since it should not always be set here, but it works in my particular case:

  	/* Enable the transfer complete, & transfer error interrupts */
  	/* Half transfer interrupt is optional: enable it only if associated callback is available */
    if(NULL != hdma->XferHalfCpltCallback )
      hdma->Instance->CCR |= (DMA_IT_TC | DMA_IT_HT | DMA_IT_TE);
  		hdma->Instance->CCR |= (DMA_IT_TC | DMA_IT_TE);
  		hdma->Instance->CCR &= ~DMA_IT_HT;
    // Workaround to set the missing memory increment bit
    hdma->Instance->CCR |= DMA_CCR_MINC;
  	/* Enable the Peripheral */
  	hdma->Instance->CCR |= DMA_CCR_EN;

Associate II

@Piranha​ Thank you for your quick reply!