Electronic – STM32 USART with DMA — which interrupts to use

dmaembeddedfirmwaremicrocontrollerstm32

I'm working on firmware for an STM32F103 which is communicating over RS232 at 115200 baud with a motor controller. The motor controller (a Copley Xenus XTL) operates on a "speak when spoken to" protocol. I'm using the ASCII programming interface in the linked documents. The STM32 always be sending the same command ("g r0x18") to poll a register, and the motor controller replies with a variable number (~4-10 bytes) of characters terminated by a carriage return ("v 12345", where the number of digits is variable). I have code to parse the response and pull a numeric value out of it. Once the response is parsed, the register poll command should be transmitted to the motor controller again. The STM32 is also reading an ADC channel over DMA in circular mode in the background.

I'd like to implement this using the DMA controller to make everything as non-blocking as possible, but I'm a bit confused as to which interrupts I should be using and when they fire. Without using the DMA controller, the parsing code currently resides in the USART RXNE interrupt. Suppose I transmit command and the motor controller starts to reply. I believe the RXNE interrupt fires for each received byte, but what about the DMA transfer complete interrupt? Is there even any functional difference in this case between using the DMA TC interrupt and the USART RXNE interrupt?

Best Answer

You can configure DMA to work in cyclic mode, and with reasonable big buffer you can pull characters as often as you want with simple poll function. Just store index of last character retrieved and use DMA register to check how many characters it has to receive until roll over (AFAIR register has NDTR in its name), and process characters received since last call, then update index of last character retrieved.

Described DMA usage makes your poll independent from context you call it from as long as you prevent preemption. Then you can use RX interrupt to trigger poll call(s), or you can do it in other context (for example with some periodic events).

This is generally efficient if frames are long enough, baudrate is big and interrupt latency is bigger than reception time of single char.

But if data is received slow enough you can get away with processing it char by char current way, and using DMA may be overkill.

As @Chris Stratton pointed you can also just set DMA for single transmission, and wait long enough, change protocol or use other signal as "end of transmission" - and then process frame in DMA buffer.