Skip to main content
jblackarty
Associate
February 23, 2015
Question

USART Rx implementation via DMA on per-byte basis

  • February 23, 2015
  • 11 replies
  • 2612 views
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
This topic has been closed for replies.

11 replies

Tesla DeLorean
Guru
February 23, 2015
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 (See Profile) Up vote any posts that you find helpful, it shows what's working..
jblackarty
Associate
February 24, 2015
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
February 26, 2015
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 ! :)

waclawek.jan
Super User
February 26, 2015
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
February 26, 2015
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...

waclawek.jan
Super User
February 26, 2015
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 III
February 26, 2015
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
February 27, 2015
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
March 2, 2015
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.

Danish1
Lead III
March 2, 2015
Posted on March 02, 2015 at 22:35

I mentioned that alternative approach merely to remind you that your suggested approach was not the only way to tackle the problem you cited. Yes everything that is called by the ISR would also have to be in RAM. It's not an approach I like to use.

For me, loss of data would be much more serious than a delay in processing the data.

The delay is often because of other, higher-priority, demands on the processor.

I typically have a large circular buffer into which incoming data is fed by DMA.

I find there is no need to suspend DMA to find out how much has been received so far.

You could use USART RXNE interrupt to signal to your app that there is some incoming data. But then your ISR could cancel the interrupt either by clearing RXNEIE or the global USART Interrupt Enable - rather than clearing the source of the interrupt by reading USART_DR.

And then you only re-enable the interrupt once you have cleared the pending interrupt and consumed all data up to DMA_SxNDTR (which might have moved on since you started processing it).

The order of this is important, and might require careful thought or experimentation, to ensure that you *do* get an interrupt in response to the first character that you haven't processed.

Hope this helps,

Danish