cancel
Showing results for 
Search instead for 
Did you mean: 

How do I process just UART RX interrupt events while using HAL_UART_Transmit_DMA

Nix Wayne
Associate III

Hi,

I am working with F413 and I am using USART3 to receive and trasmit data over UART. As I need to receive unknow size of data I wrote my own interrupt handler for RX. However I would like to transmit data out with a help of DMA.

If I call regular poll UART_Transmit(...) function, device will send out data and response from the target will be picked up by RX IRQ handler to a buffer without a problem. I wan't to get rid of blocking UART_Transmit() function. For that reason I configure and setup DMA so I could use non-blocking UART_Transmit_DMA(...) function.

With logic analyzer I can see properly formed outgoing and incoming traffic. I would say that DMA transmit works fine... but now I have problem with RX IRQ handler...

0693W00000DnwvKQAR.png 

My receive IRQ handler looks like this:

/* USART3 RX handler */
UART_RX_INTERRUPT_HANDLER_DECLARATION(USART3)
{
	/* Check if receive register is not empty status */																	
  if __HAL_UART_GET_FLAG(&UARTS_Instance()->consts[UART_INSTANCE_3].huart, USART_SR_RXNE)														
	{																																												
		/* Disable/ clear flag */																													
		__HAL_UART_CLEAR_FLAG(&UARTS_Instance()->consts[UART_INSTANCE_3].huart, USART_SR_RXNE);													
																																											
		/* Process received data */																											  
		 UARTS_Instance()->rx_irq_callback(UARTS_Instance()->consts[UART_INSTANCE_3].index ,__HAL_UART_FLUSH_DRREGISTER(&UARTS_Instance()->consts[UART_INSTANCE_3].huart));		
 
		/* Flush register*/																																	
		__HAL_UART_FLUSH_DRREGISTER(&UARTS_Instance()->consts[UART_INSTANCE_3].huart);																			
																																												
		/* Re-enable interrupt */																														
		__HAL_UART_ENABLE_IT(&UARTS_Instance()->consts[UART_INSTANCE_3].huart, UART_IT_RXNE);		
 
	}			
}	

What happens now is that I am stuck in this handler and it gets called repeatedly, therfore my program "freezes". If I put breakpoint inside handler and inspect peripheral registers I see there's TCIE register enable.... although I never enable it manually anywhere in my code ( I've checked it several times)... So now I am thinking if DMA could have any impact on that and how I should properly handle flags and interrupts within that handler (while DMA Stream has it's own handler - HAL_DMA_IRQHandler(&dma) ).

0693W00000Dnx3nQAB.png

void DMA1_Stream3_IRQHandler(void)
{
  HAL_DMA_IRQHandler(&UARTS_Instance()->consts[UART_INSTANCE_3].hdma);	
}

Any help would be much appreciated!

1 ACCEPTED SOLUTION

Accepted Solutions
Guenael Cadier
ST Employee

Hello @Nix Wayne​ 

Here is the sequence that happens when calling HAL_UART_Transmit_DMA() :

  • this function will initiate the transfer using DMA, and register UART internal callback to DMA events as Transfer Complete
  • When DMA completes the transfers it executes the registered callback
  • But from DMA point of view, Transfer Complete means that last data has been copied from user buffer to DR (UART register for data transmit)
  • Then, inside HAL UART, the registered callback has been designed to ensure that last character is indeed really sent on the Tx line (and not just copied into DR)
  • This is done by enabling TCIE and waiting for TC interrupt to be raised.
  • when TC interrupt is raised for last transmitted char, transfer is considered as complete at HAL level.

So, problem you encounter, is related to use of HAL API for transmit DMA, while call of HAL_UART_IRQHandler is not executed when a UART interrupt is raised. The management of the TC interrupt is missing in your IRQ handler. As for DMA, standard use of HAL services, requires call of HAL_UART_IRQHandler in USART3_IRQHandler().

A workaround for mixing use of HAL services without using HAL IRQ Handler function, could be to just implement the management of the TC interrupt into your IRQ handler. Here is a draft proposal (not tested nor compiled) that might work if TC interrupt is used only for HAL purposes.

Goal is to replicate what HAL IRQ Handler default function would have done on a TC interrupt at the end of a DMA transmission.

/* USART3 RX handler */
UART_RX_INTERRUPT_HANDLER_DECLARATION(USART3)
{
  /* Check if receive register is not empty status */
  if __HAL_UART_GET_FLAG(&UARTS_Instance()->consts[UART_INSTANCE_3].huart, USART_SR_RXNE)
  {
    ...
  }
  
  /* Check if TC flag is set and TCIE is enabled */
  if (   ( (&UARTS_Instance()->consts[UART_INSTANCE_3].huart->Instance->CR1 & (USART_CR1_TCIE)) == (USART_CR1_TCIE)) 
      && (__HAL_UART_GET_FLAG(&UARTS_Instance()->consts[UART_INSTANCE_3].huart, USART_SR_TC)
	 )
  {
    /* Disable the UART Transmit Complete Interrupt 
	   (TCIE has been enabled on DMA TX Complete event) */
    __HAL_UART_DISABLE_IT(&UARTS_Instance()->consts[UART_INSTANCE_3].huart, UART_IT_TC);
 
    /* Tx process is ended, restore UART Handle state to Ready 
	   (if not done, this might prevent further transmits) */
    &UARTS_Instance()->consts[UART_INSTANCE_3].huart->gState = HAL_UART_STATE_READY;
  }
}

What is the version of your STM324 HAL Firmware package ?

Hope this helps.

Regards

View solution in original post

3 REPLIES 3
TDK
Guru

If you want to use HAL, you need to let the HAL framework handle interrupts and clearing of flags. Here, you would call HAL_UART_IRQHandler within the interrupt and implement HAL_UART_RxCpltCallback if you want something ran when the transfer is complete.

If you want to handle each individual RXNE event, you can use HAL_UART_Transmit_IT with a transfer size of 1 each time.

If you feel a post has answered your question, please click "Accept as Solution".
Guenael Cadier
ST Employee

Hello @Nix Wayne​ 

Here is the sequence that happens when calling HAL_UART_Transmit_DMA() :

  • this function will initiate the transfer using DMA, and register UART internal callback to DMA events as Transfer Complete
  • When DMA completes the transfers it executes the registered callback
  • But from DMA point of view, Transfer Complete means that last data has been copied from user buffer to DR (UART register for data transmit)
  • Then, inside HAL UART, the registered callback has been designed to ensure that last character is indeed really sent on the Tx line (and not just copied into DR)
  • This is done by enabling TCIE and waiting for TC interrupt to be raised.
  • when TC interrupt is raised for last transmitted char, transfer is considered as complete at HAL level.

So, problem you encounter, is related to use of HAL API for transmit DMA, while call of HAL_UART_IRQHandler is not executed when a UART interrupt is raised. The management of the TC interrupt is missing in your IRQ handler. As for DMA, standard use of HAL services, requires call of HAL_UART_IRQHandler in USART3_IRQHandler().

A workaround for mixing use of HAL services without using HAL IRQ Handler function, could be to just implement the management of the TC interrupt into your IRQ handler. Here is a draft proposal (not tested nor compiled) that might work if TC interrupt is used only for HAL purposes.

Goal is to replicate what HAL IRQ Handler default function would have done on a TC interrupt at the end of a DMA transmission.

/* USART3 RX handler */
UART_RX_INTERRUPT_HANDLER_DECLARATION(USART3)
{
  /* Check if receive register is not empty status */
  if __HAL_UART_GET_FLAG(&UARTS_Instance()->consts[UART_INSTANCE_3].huart, USART_SR_RXNE)
  {
    ...
  }
  
  /* Check if TC flag is set and TCIE is enabled */
  if (   ( (&UARTS_Instance()->consts[UART_INSTANCE_3].huart->Instance->CR1 & (USART_CR1_TCIE)) == (USART_CR1_TCIE)) 
      && (__HAL_UART_GET_FLAG(&UARTS_Instance()->consts[UART_INSTANCE_3].huart, USART_SR_TC)
	 )
  {
    /* Disable the UART Transmit Complete Interrupt 
	   (TCIE has been enabled on DMA TX Complete event) */
    __HAL_UART_DISABLE_IT(&UARTS_Instance()->consts[UART_INSTANCE_3].huart, UART_IT_TC);
 
    /* Tx process is ended, restore UART Handle state to Ready 
	   (if not done, this might prevent further transmits) */
    &UARTS_Instance()->consts[UART_INSTANCE_3].huart->gState = HAL_UART_STATE_READY;
  }
}

What is the version of your STM324 HAL Firmware package ?

Hope this helps.

Regards

Hi Guenael,

YES! That solved my problem...

I was missing only one line:

UARTS_Instance()->consts[UART_INSTANCE_3].huart->gState = HAL_UART_STATE_READY;

All information are in your reply!!! I

Thanks again! You rock and I owe you a beer! 😉

Nix