cancel
Showing results for 
Search instead for 
Did you mean: 

Real-life UART with DMA using HAL ?

ranran
Senior II

Hello,

Is it possible to use uart with dma, so that on idle we also get informed, so that we can read the rx data ?

I found no example in hal cube for using UART with DMA in such "real life" scenario, only in simple full buffer fill - which is not practical.

I use stm32h7.

Thanks,

ranran

1 ACCEPTED SOLUTION

Accepted Solutions

No.

HAL covers only a small subset of what the STM32 hardware can do. It is good enough to get started with STM32, create a prototype fast, but its limitations become soon apparent.

As far as I can tell, the UART idle interrupt is neither checked nor handled in the HAL interrupt handler. Enabling, checking and handling it separately might or might not confuse HAL code.

You can of course set

USARTx->CR1 |= USART_CR1_IDLEIE;

in your code, and have an interrupt handler which can deal with it. Then you must either check the HAL code that it won't get confused by anything you do with the registers, or implement all of the UART handling without HAL. The latter approach has the advantage that the register interface is far better documented as HAL functions.

DMA TC and HT interrupts are handled by HAL, but they are not exactly useful for this task (maybe TC for detecting buffer rollover). You can read the DMA stream NDTR register to find out the amount of data in the buffer.

You might also want to check out the UART receiver timeout feature (RTOR register and the associated interrupt mask and flag bits) which provides a more flexible way of detecting gaps in the transmission.

View solution in original post

9 REPLIES 9
S.Ma
Principal

Application in mind examples are a minority vs peripheral in mind ones for STM32.

Dig maybe through Tilen's https://github.com/MaJerle

Use DMA in rollover buffer big enough to make sure your regular poll from main loop don't miss any incoming bytes.

Time interval checks avoid interrupt at the expense of latency.

ranran
Senior II

Thanks, I am familiar with Tilen's git (very good tutorial), and I see that Tilen use LL driver in his implementation.

Do you think it is possible to catch TC, HC, and idle interrupts using HAL (instead of LL) ?

No.

HAL covers only a small subset of what the STM32 hardware can do. It is good enough to get started with STM32, create a prototype fast, but its limitations become soon apparent.

As far as I can tell, the UART idle interrupt is neither checked nor handled in the HAL interrupt handler. Enabling, checking and handling it separately might or might not confuse HAL code.

You can of course set

USARTx->CR1 |= USART_CR1_IDLEIE;

in your code, and have an interrupt handler which can deal with it. Then you must either check the HAL code that it won't get confused by anything you do with the registers, or implement all of the UART handling without HAL. The latter approach has the advantage that the register interface is far better documented as HAL functions.

DMA TC and HT interrupts are handled by HAL, but they are not exactly useful for this task (maybe TC for detecting buffer rollover). You can read the DMA stream NDTR register to find out the amount of data in the buffer.

You might also want to check out the UART receiver timeout feature (RTOR register and the associated interrupt mask and flag bits) which provides a more flexible way of detecting gaps in the transmission.

Thanks,

It is a pity that there is no "real-life" example for UART-DMA in h7cube.

Thanks!

I will first try to use the HAL, I'll update here if I get it works.

Thanks a lot for your suggestions, very useful !

Yes, this might be the most frequently missed feature of HAL, receiving variable sized packets (not only on UART). It's not anticipated in the interface. I've seen lots of people trying to solve it, coming up with various non-working hacks. Don't waste your time.

I'd suggest not using the LL functions either, but accessing the registers directly.

  • As you are already familiar with HAL, you might want to look into the HAL functions to see how they work. You'd see register accesses there, HAL doesn't go through LL.
  • The most detailed and authorative documentation available, the reference manual, documents register usage. To use LL, you'd have to find the LL function that does exactly that to a particular register what you want. Very time-consuming and entirely unnecessary.

Hi,

Seems that someone shared his solution for using uart +dma using HAL:

https://controllerstech.com/receive-uart-data-using-dma-and-idle-line-detection/

Source code can be downloaded from:

https://drive.google.com/file/d/1jBmK9f9K9kw-bj4ua0qxZ4Nh4X8E8RDU/view

And another example, even simpler here:

https://community.st.com/s/question/0D50X00009XkhGfSAJ/cubemx-feature-request-add-usart-rx-idle-handling

I think the best is this:

https://github.com/akospasztor/stm32-dma-uart

Yet, it is not using HC (which I need in my case), only TC+idle.

Thanks

[]

Hi, I'm also intrested in reaching same goal and I'm working on Nucleo-L476RG

I'm trying the example in https://github.com/akospasztor/stm32-dma-uart and I have this code:

    UART_Init();
    DMA_Init();
    
    /* Start DMA */
    if(HAL_UART_Receive_DMA(&huart2, (uint8_t*)dma_rx_buf, DMA_BUF_SIZE) != HAL_OK){        
        Error_Handler();
    }

and this are the functions for init:

void UART_Init(void) {
	huart2.Instance = USART2;
	huart2.Init.BaudRate = 115200;
	huart2.Init.WordLength = UART_WORDLENGTH_8B;
	huart2.Init.StopBits = UART_STOPBITS_1;
	huart2.Init.Parity = UART_PARITY_NONE;
	huart2.Init.Mode = UART_MODE_TX_RX;
	huart2.Init.HwFlowCtl = UART_HWCONTROL_NONE;
	huart2.Init.OverSampling = UART_OVERSAMPLING_16;
	huart2.Init.OneBitSampling = UART_ONE_BIT_SAMPLE_DISABLE;
	huart2.AdvancedInit.AdvFeatureInit = UART_ADVFEATURE_NO_INIT;
	if (HAL_UART_Init(&huart2) != HAL_OK) {
		Error_Handler();
	}
 
	/* UART2 IDLE Interrupt Configuration */
	SET_BIT(USART2->CR1, USART_CR1_IDLEIE);
	HAL_NVIC_SetPriority(USART2_IRQn, 0, 0);
	HAL_NVIC_EnableIRQ(USART2_IRQn);
}
 
/* DMA Configuration */
void DMA_Init(void) {
	__HAL_RCC_DMA1_CLK_ENABLE();
 
	hdma_usart2_rx.Instance = DMA1_Channel6;
	hdma_usart2_rx.Init.Request = DMA_REQUEST_2;
	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_CIRCULAR;
	hdma_usart2_rx.Init.Priority = DMA_PRIORITY_VERY_HIGH;
	if (HAL_DMA_Init(&hdma_usart2_rx) != HAL_OK) {
		Error_Handler();
	}
 
	__HAL_LINKDMA(&huart2, hdmarx, hdma_usart2_rx);
 
	/* DMA Interrupt Configuration */
	HAL_NVIC_SetPriority(DMA1_Channel6_IRQn, 0, 0);
	HAL_NVIC_EnableIRQ(DMA1_Channel6_IRQn);
}

When I execute HAL_UART_Receive_DMA, the MCD freezes and does not continue execution.

I notice that if I comment HAL_NVIC_EnableIRQ(USART2_IRQn); the execution continues but, obviously the callback doesn't work.

How can I solve?