cancel
Showing results for 
Search instead for 
Did you mean: 

STM32L4 UART DMA TX Callback not called

Malte Moers
Associate
Posted on June 15, 2017 at 13:00

Dear community,

I used CubeMX to setup a project for UART DMA transfers RX and TX. Apart from my protocol logic, I added two callbacks:

void HAL_UART_RxCpltCallback(UART_HandleTypeDef *UartHandle)
{
 rx_cplt=true;
}
void HAL_UART_TxCpltCallback(UART_HandleTypeDef *UartHandle)
{
 tx_cplt=true;
}�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?

and start transfers with calls to

HAL_UART_Receive_DMA(&huart2, (uint8_t *)buf, len);
HAL_UART_Transmit_DMA(&huart2, (uint8_t *)x, len)�?�?�?�?

Both work fine in the sense that the data is transmitted, as I can see from logic analyzer and host side. But the problem is, that only the Rx Callback is called after transfer has finished.

I walked through the source code with debugger and found that the interrupt for TX complete is actually triggered, but the callback function is never called from HAL implementation. I checked the file 'stm32l4xx_hal_uart.c' and found the following function, which is called after the UART DMA TX transfer has completed:

static void UART_DMATransmitCplt(DMA_HandleTypeDef *hdma)
{
 UART_HandleTypeDef* huart = (UART_HandleTypeDef*)(hdma->Parent);
 
 /* DMA Normal mode */
 if ( HAL_IS_BIT_CLR(hdma->Instance->CCR, DMA_CCR_CIRC) )
 {
 huart->TxXferCount = 0;
 /* Disable the DMA transfer for transmit request by resetting the DMAT bit
 in the UART CR3 register */
 CLEAR_BIT(huart->Instance->CR3, USART_CR3_DMAT);
 /* Enable the UART Transmit Complete Interrupt */
 SET_BIT(huart->Instance->CR1, USART_CR1_TCIE);
 }
 /* DMA Circular mode */
 else
 {
 HAL_UART_TxCpltCallback(huart);
 }
}�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?

Essentially, this tells me, that when in normal mode (not circular), the registered callback is never executed. Is this a bug in the HAL implementation?

After adding the callback in the code executed if normal mode is activated, everything works like a charm. I would prefer not to change the HAL library code, though.

Has anybody experienced this problem before? Am I using the driver correctly?

You can find the complete source code of my application in the attachment.

Cheers,

Malte

#uart-tx #uart #dma
1 ACCEPTED SOLUTION

Accepted Solutions
Zt Liu
Senior III
Posted on June 15, 2017 at 16:05

You should turn on global UART IRQ in cubeMX, this handler is not seen in your code.

so that in your stm32l4xx_it.c, you should see like this

void USART2_IRQHandler(void)

{

   /* USER CODE BEGIN USART2_IRQn 0 */

   /* USER CODE END USART2_IRQn 0 */

   HAL_UART_IRQHandler(&huart2);

   /* USER CODE BEGIN USART2_IRQn 1 */

   /* USER CODE END USART2_IRQn 1 */

}

In the HAL_UART_IRQHander(),

your HAL_UART_TxCpltCallback() would be called in the UART_EndTransmit_IT();

You can also see the examples in your downloaded CUBE example concerning UART_DMA

Good Luck!

View solution in original post

16 REPLIES 16
Zt Liu
Senior III
Posted on June 15, 2017 at 16:05

You should turn on global UART IRQ in cubeMX, this handler is not seen in your code.

so that in your stm32l4xx_it.c, you should see like this

void USART2_IRQHandler(void)

{

   /* USER CODE BEGIN USART2_IRQn 0 */

   /* USER CODE END USART2_IRQn 0 */

   HAL_UART_IRQHandler(&huart2);

   /* USER CODE BEGIN USART2_IRQn 1 */

   /* USER CODE END USART2_IRQn 1 */

}

In the HAL_UART_IRQHander(),

your HAL_UART_TxCpltCallback() would be called in the UART_EndTransmit_IT();

You can also see the examples in your downloaded CUBE example concerning UART_DMA

Good Luck!

Posted on June 15, 2017 at 16:56

Thanks for the quick response. Your suggestion to activate the global UART IRQ worked perfectly!

schperplata
Associate III
Posted on August 13, 2017 at 00:28

Hello,

Ihave a similar problem withUART DMA transfer complete/half transfer callback function using CubeMx and HAL.

I use UART with DMA on UART RX. I have enabled DMA transfer complete and half transfer interrupts in usart.c:

/* USART1 DMA Init */
/* USART1_RX Init */
hdma_usart1_rx.Instance = DMA1_Channel3;
hdma_usart1_rx.Init.Direction = DMA_PERIPH_TO_MEMORY;
hdma_usart1_rx.Init.PeriphInc = DMA_PINC_DISABLE;
hdma_usart1_rx.Init.MemInc = DMA_MINC_ENABLE;
hdma_usart1_rx.Init.PeriphDataAlignment = DMA_PDATAALIGN_BYTE;
hdma_usart1_rx.Init.MemDataAlignment = DMA_MDATAALIGN_BYTE;
hdma_usart1_rx.Init.Mode = DMA_CIRCULAR;
hdma_usart1_rx.Init.Priority = DMA_PRIORITY_LOW;
if (HAL_DMA_Init(&hdma_usart1_rx) != HAL_OK)
{
 _Error_Handler(__FILE__, __LINE__);
}
__HAL_LINKDMA(uartHandle,hdmarx,hdma_usart1_rx);�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?
HAL_DMA_RegisterCallback(&hdma_usart1_rx, HAL_DMA_XFER_HALFCPLT_CB_ID, handle_uart_dma_data); 
HAL_DMA_RegisterCallback(&hdma_usart1_rx, HAL_DMA_XFER_CPLT_CB_ID, handle_uart_dma_data);
 
__HAL_DMA_CLEAR_FLAG(&hdma_usart1_rx, DMA_FLAG_HT3); // DMA CH3
__HAL_DMA_ENABLE_IT(&hdma_usart1_rx, DMA_IT_HT); // enable Half transfer flag
__HAL_DMA_CLEAR_FLAG(&hdma_usart1_rx, DMA_FLAG_TC3); // DMA CH3
__HAL_DMA_ENABLE_IT(&hdma_usart1_rx, DMA_IT_TC); // enable Transfer Complete interrupt�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?�?

I defined my handle_uart_dma_data function in separate file:

.h

#include ''stm32f0xx_hal.h''
#include ''stm32f0xx_hal_dma.h''
 ...
void handle_uart_dma_data(DMA_HandleTypeDef *hdma);
 ...�?�?�?�?�?

.c

void handle_uart_dma_data(DMA_HandleTypeDef *hdma){
 LED_1_ON();
}�?�?�?

Problem is, this function is never executed.

HAL_DMA_RegisterCallback executeswith HAL_OK status, while debugger shows different pointer, to UART __weak functions. 0690X00000607qDQAQ.png

Why are called this functions instead of my registered callback?

void HAL_UART_TxCpltCallback(UART_HandleTypeDef *huart);
void HAL_UART_TxHalfCpltCallback(UART_HandleTypeDef *huart);
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart);
void HAL_UART_RxHalfCpltCallback(UART_HandleTypeDef *huart);
void HAL_UART_ErrorCallback(UART_HandleTypeDef *huart);�?�?�?�?�?

Zt Liu
Senior III
Posted on August 17, 2017 at 11:32

in static void UART_DMAReceiveCplt(DMA_HandleTypeDef *hdma)

If your dma is not in circular mode,

The library clear Error flags and disable UART DMA Receiver, also set the handle's RxState to HAL_UART_STATE_READY. and clear its RxXferCount.

static void UART_DMAReceiveCplt(DMA_HandleTypeDef *hdma)

{

UART_HandleTypeDef* huart = ( UART_HandleTypeDef* )((DMA_HandleTypeDef* )hdma)->Parent;

/* DMA Normal mode*/

if((hdma->Instance->CR & DMA_SxCR_CIRC) == 0U)

{

   huart->RxXferCount = 0U;

   /* Disable RXNE, PE and ERR (Frame error, noise error, overrun error) interrupts */

   CLEAR_BIT(huart->Instance->CR1, USART_CR1_PEIE);

   CLEAR_BIT(huart->Instance->CR3, USART_CR3_EIE);

   /* Disable the DMA transfer for the receiver request by setting the DMAR bit

   in the UART CR3 register */

   CLEAR_BIT(huart->Instance->CR3, USART_CR3_DMAR);

   /* At end of Rx process, restore huart->RxState to Ready */

      huart->RxState = HAL_UART_STATE_READY;

   }

   HAL_UART_RxCpltCallback(huart);

}

What's the problem using void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)??

Posted on August 17, 2017 at 11:06

If you did call HAL_UART_Receive_DMA(...), then the HAL library do the following

...

/* Set the UART DMA transfer complete callback */

huart->hdmarx->XferCpltCallback = UART_DMAReceiveCplt;

/* Set the UART DMA Half transfer complete callback */

huart->hdmarx->XferHalfCpltCallback = UART_DMARxHalfCplt;

/* Set the DMA error callback */

huart->hdmarx->XferErrorCallback = UART_DMAError;

/* Set the DMA abort callback */

huart->hdmarx->XferAbortCallback = NULL;

 ...

Posted on August 17, 2017 at 11:18

Hello,

yes, I figured it out, but I don't see the point - are there any good  reasons that HAL library re-sets this callback to this predefined callback?

Thank you!

Posted on August 17, 2017 at 11:39

There is no problem, except that I couldn't find anywhere those things clearly written - why and when to use which handler. Some things in HAL works out of the box with ease, but other (like this) - you must know the inner working of functions and connections beetween hardware, like UART <-> DMA. 

If there is anywhere a document describing how all this functions are called and connected, I am happy to read it, let me know. 

Anyway, thank you for reply.
Posted on August 17, 2017 at 11:55

In each webpage of STM32FX Cube, there are user manuals for HAL and LL...

For example, in F4 Cube, there is

http://www.st.com/content/ccc/resource/technical/document/user_manual/2f/71/ba/b8/75/54/47/cf/DM00105879.pdf/files/DM001…

However.....I rarely read them for I guess it's just generated from the src files using Doxygen.....

So I always give a quick read on the source files.

Also, the go to definition or go to reference Function in your IDE may help you give insights on the source.

Good Luck!

Greg Anderson
Associate
Posted on February 10, 2018 at 20:49

Since no one has mentioned Transmit Complete Interrupt Enable bit yet, I will do so, because the HAL Library function UART_DMATransmitCplt (DMA_HandleTypeDef *hdma) has what I view as a flaw. Inside that function there is a line (shown below) to set the UART CR1 TCIE IRQ enable flag (Transmit Complete Interrupt Enable). The issue that can occur (and perhaps does happen in your code) is that a higher priority (by design) interrupt happens sometime after the DMA has transferred the last data byte into the USARTs Transmit Data Register, but before the TCIE bit is set. If the higher priority interrupt lasts long enough so that the USART has completely shifted out the last TX byte, then the TCIE bit is being set too late to take effect. I'd recommend leaving the HAL function as-is, but use the macro provided to set the CR1.TCIE control bit immediately after the transmission is started. Doing so should not lead to any premature callback since the trigger for transmit complete is a combination of the last bit of the USART TX shift register becoming empty AND the USART TDR register is also empty; with a DMA this event should not occur until all data bytes have been shipped out. I suppose if there is concurrent DMA activity going on, there may be a chance that a premature Transmit Complete IRQ could happen; so at least consider that possibility as well. Hope this helps. I'm assuming you have long since found your workaround, but maybe this will help someone else in the future.

static void UART_DMATransmitCplt(DMA_HandleTypeDef *hdma)
 /* Enable the UART Transmit Complete Interrupt */
 SET_BIT(huart->Instance->CR1, USART_CR1_TCIE);