cancel
Showing results for 
Search instead for 
Did you mean: 

HAL_UART_Transmit_DMA issues | STM32F3

Ade J.2
Associate

Currently I am working on UART DMA and I encountered problems after transmitting data via DMA UART. After 1 successful data transmission, the state flag remains on BUSY. After a search, I ended up on this thread and this fixed the problem.

Thread

static
void
UART_DMATransmitCplt(DMA_HandleTypeDef *hdma) 
 
{
 
UART_HandleTypeDef* huart = ( UART_HandleTypeDef* )((DMA_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 setting the DMAT bit
 
in the UART CR3 register */
 
CLEAR_BIT(huart->Instance->CR3, USART_CR3_DMAT);
 
 
 
/* Enable the UART Transmit Complete Interrupt */
 
__HAL_UART_ENABLE_IT(huart, UART_IT_TC);
 
huart->State=HAL_UART_STATE_READY; 
//<--- i add this line to solve the //problem 
 
}
 
/* DMA Circular mode */
 
else
 
{
 
HAL_UART_TxCpltCallback(huart);
 
}
 
}

Line 31 in code snippet

However, is this a good solution (and should this be implemented in the stm32f3 library) or am I missing something that causes this problem of only 1 successful transmission?

1 ACCEPTED SOLUTION

Accepted Solutions
Guenael Cadier
ST Employee

Hello @Ade J.2​ 

Here is how UART DMA Tx transfers are expected to work :

  • DMA transfers, after UART and DMA initialisation steps, are initiated by HAL_UART_Transmit_DMA()
  • This API configure the Transfer Complete callback to UART_DMATransmitCplt(), and starts the transfer.
  • DMA manages transfers of user buffer data through UART TDR register, and when completed, UART_DMATransmitCplt() is executed.
  • At this moment, the Transfer on UART lines, is not completed : only the job of DMA is completed, i.e. copying the user buffer data into UART TDR register.
  • That's why the code in UART_DMATransmitCplt() is enabling the UART TC interrupt : the UART transfer from application point of view will be considered as completed only when TC bit will be set to 1 (after completion of transfer of last data in TDR).
  • So the HAL_UART_TxCpltCallback() callback (user view) is not called directly in UART_DMATransmitCplt(), but will rather be called in UART IRQ handler that will be raised when TC bit will be set to 1 in UART Status register.

In your case, could you check that UART IRQ is properly enabled, with corresponding IRQ handler ?

(that could be an explanation why state remains in BUSY and HAL_UART_TxCpltCallback() is not called).

FYI, requirement of checking TC status of last transmitted byte was required by some applications that enter low power modes just after an transmission. If HAL sets state to ready directly in UART_DMATransmitCplt(), and user disables UART clocks after state is back to ready, last data copied by DMA, is not transmitted. As I mentioned, at a given instant, transfer is completed from DMA point of view, but not from UART Tx line yet.

Hope this will help you.

Regards

Guenael

View solution in original post

3 REPLIES 3
Guenael Cadier
ST Employee

Hello @Ade J.2​ 

Here is how UART DMA Tx transfers are expected to work :

  • DMA transfers, after UART and DMA initialisation steps, are initiated by HAL_UART_Transmit_DMA()
  • This API configure the Transfer Complete callback to UART_DMATransmitCplt(), and starts the transfer.
  • DMA manages transfers of user buffer data through UART TDR register, and when completed, UART_DMATransmitCplt() is executed.
  • At this moment, the Transfer on UART lines, is not completed : only the job of DMA is completed, i.e. copying the user buffer data into UART TDR register.
  • That's why the code in UART_DMATransmitCplt() is enabling the UART TC interrupt : the UART transfer from application point of view will be considered as completed only when TC bit will be set to 1 (after completion of transfer of last data in TDR).
  • So the HAL_UART_TxCpltCallback() callback (user view) is not called directly in UART_DMATransmitCplt(), but will rather be called in UART IRQ handler that will be raised when TC bit will be set to 1 in UART Status register.

In your case, could you check that UART IRQ is properly enabled, with corresponding IRQ handler ?

(that could be an explanation why state remains in BUSY and HAL_UART_TxCpltCallback() is not called).

FYI, requirement of checking TC status of last transmitted byte was required by some applications that enter low power modes just after an transmission. If HAL sets state to ready directly in UART_DMATransmitCplt(), and user disables UART clocks after state is back to ready, last data copied by DMA, is not transmitted. As I mentioned, at a given instant, transfer is completed from DMA point of view, but not from UART Tx line yet.

Hope this will help you.

Regards

Guenael

Ade J.2
Associate

Sorry for the late response, was on vacation for a week.

Thanks for the clear explanation. When I indeed enable USARTx global interrupt, it works as it should.

Thanks for the solution.

STM32F411 , I have the same problem and spent almost half of day with this! Using CubeMX 6.12. and calling function HAL_UART_Transmit_DMA in dma normal mode it send only one packet and every next packet not sending since uart is stuck in busy state. Even enabling uart global interupt didn't help! Solution for this bug is to add to USART1_IRQHandler this:

 

 

if (USART1->SR & USART_SR_IDLE)
{
        USART1->DR; //fake read to clear flag
}

 

 

After this piece of code HAL_UART_Transmit_DMA is working in dma normal mode. My hope ST devs do something for this bug, thank you.