cancel
Showing results for 
Search instead for 
Did you mean: 

USART Rx implementation via DMA on per-byte basis

jblackarty
Associate II
Posted on February 23, 2015 at 21:04

I would like to verify whether my solution is correct and robust. I already read a lot of similar topics, but didn't found reliable confirmation of my ideas. I just learned that many people suffer from stupid GP DMA implementation in STM32 and being advised to use dirty hacks like timers or even external rx pin interconnections (holy sh*t!)...

Hope my solution will be useful for others.

Problem:

System has large interrupt latencies (due to flash programming operations requiring disabling interrupts), which requires to use DMA for USART Rx.

Implementation requirements:

- write to circular buffer (classic circular buffer with read/write pointers, having nothing common with that stupid circular/double-buffering modes in DMA);

- signal each byte reception (since it's not possible to predict DMA transfer size);

- signal overflow condition and stop writing to buffer (in order to not disrupt data still not read by application);

- interrupt handler execution time must be small.

Proposed solution:

Allocate small(!) DMA buffer, configure it in non-circular direct mode.

Use USART RXNE interrupt handler to process reception in this way:

- stop DMA

- read DMA_SxNDTR register to calculate how much bytes received

- flush data from DMA buffer to software circular buffer (if not overflowed)

- restart DMA

- signal reception to application.

Use USART ORE flag to signal overflow to application.

Why small DMA buffer ? Because interrupt handler must have time to process it completely, while DMA suspended and before next symbol arrives from USART shift register. For example, just 12 bytes will be enough to protect 1Mbaud stream from 100us latency, and make interrupt handler execution time short enough.

Things make me worrying:

- Reference Manual states: ''Note: If DMA is used for reception, do not enable the RXNEIE bit.''

- DMA transfer suspended while peripheral enabled. I expect that it's ok for peripheral-to-memory mode (at least). In case, if USART receive symbol, it should normally assert DMA request, and when DMA transfer enabled again, it should handle request immidiately, and everything should be ok...

Please, confirm that solution is completely correct, i.e. it doesn't use undocumented features, doesn't rely on undefined behavior, does account all possible tricky edge cases.

Thanks,

Artem

#stm32-usart-rx-dma-interrupt
11 REPLIES 11
Posted on February 23, 2015 at 22:24

>>- Reference Manual states: ''Note: If DMA is used for reception, do not enable the RXNEIE bit.''

Yes, because you can only actually clear it by reading the USARTx->DR, now admittedly the DMA should get to it long before you're interrupt, if it does get to the interrupt and the DMA's not clearing it, you'll be stuck in the interrupt indefinitely. Having parallel DMA/IRQ just seems to create lots of race hazards.

For a 1-byte solution, interrupts work just fine, the problem most people have is doing far too much in the interrupt service routines.

Tips, Buy me a coffee, or three.. PayPal Venmo
Up vote any posts that you find helpful, it shows what's working..
jblackarty
Associate II
Posted on February 24, 2015 at 03:47

Ah. Now I've got it! Thank you.

So... it means that my solution is incorrect. It was last chance. (I already spent several days trying to invent how to deal with this weird  DMA.)

P.S. Thank you, STMicroelectronics for such ''high-performance and flexible'' MCUs. We just should continue to be in pain...

jblackarty
Associate II
Posted on February 26, 2015 at 06:29

I didn't give up too easy and found out something you might be interested also !

AN3109 fragments:

The receiver receives m (potentially unknown) data items using DMA. It is still possible to get the peripheral’s RXNE interrupt even when using DMA. In fact, the interrupt from the peripheral emulates the FIFO nonempty interrupt.

 

...

There is no need to clear the RXNE flag in the receive interrupt routine, as it was automatically cleared by the DMA data read operation. However, the interrupt remains pending in the NVIC (even though the RXNE flag is no longer set).

''FIFO nonempty interrupt'' - that's exactly what my intention to use this interrupt as. Also I inspected ''ARMv7-M Architecture Reference Manual'' carefully and come to following conclusion. If USART peripheral asserts RXNE interrupt signal regradless of mode, and processor always latches signal before DMA deasserts it, then interrupt guaranteed to be invoced (unless DMA explicitly clears NVIC pending flag, which seems to be highly unlikely). If so, it will always get to interrupt.

Also it's possible to implement protection from scenario you described, when it may be stuck in interrupt indefinitely. Thank you for pointing that out. I'm ready to fight these race hazards ! 🙂

Posted on February 26, 2015 at 08:51

Are you aware of the fact that the named AN deals with the 'F10x family?

JW

jblackarty
Associate II
Posted on February 26, 2015 at 09:05

Yes, I noticed it some time ago and just now finished comparison and came to conclusion that it has no difference with F20x, I'm targeting. Or does have ? The only small difference I found is that F10x Reference Manual doesn't includes that note about RXNEIE...

Posted on February 26, 2015 at 09:27

>The only small difference I found is that F10x Reference Manual doesn't includes that note about RXNEIE...

And how small is that difference? 😉

JW

Danish1
Lead II
Posted on February 26, 2015 at 14:11

An alternative solution to the problem would be to have:

 - the Vector Table

 - and USART RX ISR

both in RAM.

A way to disable all other interrupts would be to set RAM-based interrupts as the highest priority ones and then set BASEPRI to the appropriate level.

Hope this helps,

Danish

jblackarty
Associate II
Posted on February 27, 2015 at 04:52

>>The only small difference I found is that F10x Reference Manual doesn't includes that note about RXNEIE...

 

 

>And how small is that difference? 😉

 

 

Are you going to ruin my last hope ? 🙂

>An alternative solution to the problem would be to have:

 

> - the Vector Table

 

> - and USART RX ISR

 

>both in RAM.

 

 

>A way to disable all other interrupts would be to set RAM-based interrupts as the highest priority ones and then set BASEPRI to >the appropriate level.

It's not clear what relation it have to defined problem. Assuming that you suppose solution for the example cause of problem (flash programming operations), it's not correct.

Firstly, it's not enough to place only vector table and ISR in RAM, because ISR accesses other code in Flash (particullary, it calls RTOS functions in order to signal event to application). Therefore, whole code should be placed in RAM.

Secondly, it was just example of what may cause high interrupt latency. My intention is to implement generic USART driver tolerant to any predefined max latency.
jblackarty
Associate II
Posted on March 02, 2015 at 12:54

I've just got official answer from ST !

That note is just safety warning to not clear RXNE status in interrupt routine in any way.

In summary, I can assume my solution to be correct, since there are no other disproofs was given.