cancel
Showing results for 
Search instead for 
Did you mean: 

DMA transmit/receive on lpuart1

mantisrobot
Associate III

I'm using the NUCLEO-H753ZI dev board and have been testing various tx/rx reception modes using HAL routines with RTOS for all the available uarts. I have them all working well except lpuart1. When I try to receive or transmit in DMA mode I get no interrupts and no data out from the tx pin.

Am I missing something? Does this uart support DMA transfers? I see it uses BDMA instead of DMA, bot I'm not sure what the difference is.

This is the auto generated init code:

static void MX_LPUART1_UART_Init(void)
{
 
  /* USER CODE BEGIN LPUART1_Init 0 */
 
  /* USER CODE END LPUART1_Init 0 */
 
  /* USER CODE BEGIN LPUART1_Init 1 */
 
  /* USER CODE END LPUART1_Init 1 */
  hlpuart1.Instance = LPUART1;
  hlpuart1.Init.BaudRate = 115200;
  hlpuart1.Init.WordLength = UART_WORDLENGTH_8B;
  hlpuart1.Init.StopBits = UART_STOPBITS_1;
  hlpuart1.Init.Parity = UART_PARITY_NONE;
  hlpuart1.Init.Mode = UART_MODE_TX_RX;
  hlpuart1.Init.HwFlowCtl = UART_HWCONTROL_NONE;
  hlpuart1.Init.OneBitSampling = UART_ONE_BIT_SAMPLE_DISABLE;
  hlpuart1.Init.ClockPrescaler = UART_PRESCALER_DIV1;
  hlpuart1.AdvancedInit.AdvFeatureInit = UART_ADVFEATURE_NO_INIT;
  hlpuart1.FifoMode = UART_FIFOMODE_DISABLE;
  if (HAL_UART_Init(&hlpuart1) != HAL_OK)
  {
    Error_Handler();
  }
  if (HAL_UARTEx_SetTxFifoThreshold(&hlpuart1, UART_TXFIFO_THRESHOLD_1_8) != HAL_OK)
  {
    Error_Handler();
  }
  if (HAL_UARTEx_SetRxFifoThreshold(&hlpuart1, UART_RXFIFO_THRESHOLD_1_8) != HAL_OK)
  {
    Error_Handler();
  }
  if (HAL_UARTEx_DisableFifoMode(&hlpuart1) != HAL_OK)
  {
    Error_Handler();
  }
  /* USER CODE BEGIN LPUART1_Init 2 */
 
  /* USER CODE END LPUART1_Init 2 */
 
}
 
/**
  * Enable DMA controller clock
  */
static void MX_BDMA_Init(void)
{
 
  /* DMA controller clock enable */
  __HAL_RCC_BDMA_CLK_ENABLE();
 
  /* DMA interrupt init */
  /* BDMA_Channel0_IRQn interrupt configuration */
  HAL_NVIC_SetPriority(BDMA_Channel0_IRQn, 5, 0);
  HAL_NVIC_EnableIRQ(BDMA_Channel0_IRQn);
  /* BDMA_Channel1_IRQn interrupt configuration */
  HAL_NVIC_SetPriority(BDMA_Channel1_IRQn, 5, 0);
  HAL_NVIC_EnableIRQ(BDMA_Channel1_IRQn);
 
}

7 REPLIES 7
mantisrobot
Associate III

UPDATE

When using lpuart1 in DMA mode I get continuous UART_DMAError() errors, tracking through the code it seems to be a transfer error:

hdma->ErrorCode = HAL_DMA_ERROR_TE;

0693W00000HnhZeQAJ.jpgI don't really see enough initialization here. CubeMX buries and obfuscates, so makes for bad examples.

Try using SRAM4 in the AHB4 / APB4 / D3 Domain for the memory buffering

Tips, Buy me a coffee, or three.. PayPal Venmo
Up vote any posts that you find helpful, it shows what's working..
mantisrobot
Associate III

Hi,

Thanks for your response.

I think you're probably correct in your answer and my DMA buffer should be in the SRAM4 ram. Unfortunately I have no idea how to place this in SRAM4 using CubeIDE.

Any thoughts?

Not my tools, but generally GNU/GCC would be via the linker script and __attribute__((section(".sram4"))) type construction

If it's not part of the memory visible to the linker, your could test with a simple pointer assignment.

uint8_t *Buffer4 = (uint8_t *)0x38000000; // 64KB at 0x38000000

Tips, Buy me a coffee, or three.. PayPal Venmo
Up vote any posts that you find helpful, it shows what's working..

Ok,

I found this article:

https://community.st.com/s/article/FAQ-DMA-is-not-working-on-STM32H7-devices

And now have the DMA working on lpuart1. I added the following to my linker script: STM32H753ZITX_FLASH.ld

  .dma_buffer :
  {
  	*(.dma_buffer)
  } >RAM_D3

Then added this definition in my code:

#if defined( __ICCARM__ )
  #define DMA_BUFFER \
      _Pragma("location=\".dma_buffer\"")
#else
  #define DMA_BUFFER \
      __attribute__((section(".dma_buffer")))
#endif

Then declared the DMA buffer as follows:

DMA_BUFFER uint8_t	dmaBuff[LPUART_RX_BUFFER];

Thanks for pointing me in the right direction!

Now I have a new issue, any overrun error on the UART never seems to get cleared. Any idea what I need to do to clear an overrun? I can ask a new question in the forum but thought you might have an idea.

Cheers.

On the H7 series STM32 the Noise, Framing and Overrun have bit level clearing flags. These are for the L0, but think they are the same here

 uint32_t isr = USART2->ISR;

 if (isr & (USART_ISR_ORE | USART_ISR_NE | USART_ISR_FE))

 {

  if (isr & USART_ISR_ORE)

   USART2->ICR = USART_ICR_ORECF;

  if (isr & USART_ISR_NE)

   USART2->ICR = USART_ICR_NCF;

  if (isr & USART_ISR_FE)

   USART2->ICR = USART_ICR_FECF;

 }

On the RX DMA side, I tend to prefer a large enough circular buffer that to act as a HW FIFO that I can sweep/harvest periodically. Obviously on M7 watch for cache and write-thru coherency vs DMA

Tips, Buy me a coffee, or three.. PayPal Venmo
Up vote any posts that you find helpful, it shows what's working..

Hi,

Well it looks like the UART_ICR_OREF is cleared within the HAL ISR here:

    /* UART Over-Run interrupt occurred -----------------------------------------*/
    if (((isrflags & USART_ISR_ORE) != 0U)
        && (((cr1its & USART_CR1_RXNEIE_RXFNEIE) != 0U) ||
            ((cr3its & (USART_CR3_RXFTIE | USART_CR3_EIE)) != 0U)))
    {
      __HAL_UART_CLEAR_FLAG(huart, UART_CLEAR_OREF);
 
      huart->ErrorCode |= HAL_UART_ERROR_ORE;
    }

In my ErrorCallback routine I check the uart->ErrorCode and can see it was an ORE, however, the only way I can get things working again is to disable and re-enable the uart.. as below

void HAL_UART_ErrorCallback(UART_HandleTypeDef* huart)
{
	switch(huart->ErrorCode)
	{
	case HAL_UART_ERROR_NONE:
		asm("NOP");
		break;
	case HAL_UART_ERROR_PE:
		asm("NOP");
		break;
	case HAL_UART_ERROR_NE:
		asm("NOP");
		break;
	case HAL_UART_ERROR_FE:
		asm("NOP");
		break;
	case HAL_UART_ERROR_ORE:
		__HAL_UART_DISABLE(&SPEKTRUM_UART);;
		asm("NOP");
		__HAL_UART_ENABLE(&SPEKTRUM_UART);
		break;
	case HAL_UART_ERROR_DMA:
		asm("NOP");
		break;
	case HAL_UART_ERROR_RTO:
		asm("NOP");
		break;
	}
   
  // restart DMA receive..
}

I'm completely new to the STM32 processor, so this is a steep learning curve! The DMA circular buffer is exactly what I'm trying to implement although currently using a linear buffer and re-starting DMA reception on completion/timeout. Then I periodically empty the buffer parsing data in my thread.