cancel
Showing results for 
Search instead for 
Did you mean: 

Long wait for UART_FLAG_TC after DMA transfer

mgrunwald9
Associate
Posted on October 25, 2016 at 10:51

Hello,

While implementing a full duplex uart (115kBaud) communication on an STM32F303, I'm struggling quite a bit.

My current problem is that the DMA1_Channel2_IRQn takes 170μs. I'm using DMA to TX data from the STM. Receiving data is handled via interrupt. In the 170μs, two bytes can be received. Since the DMA1_Channel2_IRQn takes so long, the UART Interrupt for receiving is called after these two bytes which results in an overrun error :(

I have attached an oszillogram: 

Ignore yellow(top), 

Red is the duration of UART_WaitOnFlagUntilTimeout(huart, UART_FLAG_TC, RESET, HAL_UART_TXDMA_TIMEOUTVALUE), which is called from DMA1_Channel2_IRQHandler

Blue is Rx data

Green is Tx data

What I don't understand: Why is DMA TC arriving so early (while TXing byte 0x82)? And why does it take so long until the UART TC flag is set?

As you can see on the right side, a DMA_TC is happening while data is received. The RX interrupt can be called only after a byte is lost. How can I handle this? Would nested interrupts solve this problem?

I used the code that has been generated by cube to implement my firmware. I attached the generated code as a ZIP. It's not exactly the same, but I'm not aware of any important changes regarding the DMA/UART.

Any help would be greatly appreciated!

cu

Markus
4 REPLIES 4
ColdWeather
Senior
Posted on October 25, 2016 at 13:10

Imagine, your system wanna send only two bytes via UART using DMA.

The UART has actually a small FIFO: the ''visible'' data register DR and a ''shadow'' shift register, that makes the bit sequence at the TX pin.

The DMA writes the first byte to DR. The byte goes to the yet ''empty'' shadow register immediately, and the serialization starts. DR gets free at this moment and is ready for the next byte. DMA sees this and writes the second byte to DR. So, the DMA has made its job and sets TC (transfer complete!), while the UART is still handling two bytes written: the first is still on the way via TX (think on baud rates that are very very slow comparative to the CPU/DMA speed), while the second byte is waiting in DR. The UART TC bit is set when both shadow and DR registers get empty, and it takes time.

The solution is (I do this way): in the DMA TC interrupt disable it but enable the UART TC interrupt. Process the ''packet complete'' in the UART TC interrupt and disable it, prepare the new packet by reinitializing the DMA and enable its TC interrupt again, and so on. This would never block the RX interrupt of the UART.

The next hint: make the RX interrupt priority higher than TC interrupts of DMA and UART.

Posted on October 25, 2016 at 16:45

Don't use DMA TC interrupt to manage USART TC. Have your USART IRQ Handler manage RXNE and TC

USART TXE asserts, byte N-1 hits the wire

DMA writes last byte to USART DR, byte N

DMA TC asserts

.. one byte time

USART TXE asserts, byte N hits the wire

No DMA service, TXE remains asserted

.. one byte time

USART TC asserts

Tips, Buy me a coffee, or three.. PayPal Venmo
Up vote any posts that you find helpful, it shows what's working..
mgrunwald9
Associate
Posted on October 26, 2016 at 14:08

Hello ColdWeather and clive1

many thanks for your explanations. Now I have been able to get the communication running with very short interrupt times :)

My implementation still lacks elegance, so to say. Here's what I do:

When sending data, I use this function because it offers a bit of convenience:

  halStatus = HAL_UART_Transmit_DMA(&huart3, (uint8_t*) buffer, size);

One (now unwanted part) of the convenience is, that the

usart->XferCpltCallback is set to a default value. After your

explanation, I don't want the default. So what I do when the DMA_IT_TC

asserts, is this:

void DMA1_Channel2_IRQHandler( void )

{

  hdma_usart3_tx.XferCpltCallback = 0;

  //hdma_usart3_tx.XferErrorCallback= 0;

  //hdma_usart3_tx.XferHalfCpltCallback = 0;

  HAL_DMA_IRQHandler(&hdma_usart3_tx);

}

The callback won't be called if it is 0. But this is kind of ugly. The

alternative would be to use HAL_DMA_Start_IT() directly, but there's

so much glue that HAL_UART_Transmit_DMA has already implemented...

UART_IT_TC is then handled in USART3_IRQHandler.

This works perfectly now. But do you have a suggestion how to remove

the callbacks more nicely?

cu

Markus

Walid FTITI_O
Senior II
Posted on October 26, 2016 at 16:03

Hi The Grue, 

Avoid the declaration of local buffer variable and use global one for the buffer to be transmitted through USART/DMA. The function call will extracted from other user file generated on the project.

Give a try with this an tell me if this resolve your problem.

-Hannibal-