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
Danish1
Lead II
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

Posted on March 30, 2015 at 13:31

Just FYI - the mentioned ''cautionary note'' has been removed from RM0090 as of rev.9.

JW