cancel
Showing results for 
Search instead for 
Did you mean: 

Is there a good way to implement a UART driver that uses DMA and freeRTOS?

sima2
Associate III
Posted on March 07, 2015 at 00:35

Is there a good way to implement a UART driver that uses DMA and freeRTOS? Or should I let the RXE TXNE interrupts handle the queueing and use no DMA?

It would be nice to use tha DMA to shuffle data to an array and then let the RTOS put them into a queue when possible.

5 REPLIES 5
jpeacock
Associate II
Posted on March 07, 2015 at 22:44

The decision to go to DMA instead of IRQs is dependent on the volume of data and the data rate.  Low volume, like a console input from human typing, not much benefit.  Machine to machine with large packets, definitely.

Using DMA for TX is an easy decision, do it.  Unless your output is very low then using TX DMA on a USART has no down side.  The overhead of TX DMA is about 1% of IRQs if you send many large blocks.  And the logic in the driver is actually simpler than handling TXNE.  If you are using half duplex with line turnaround just make sure you add on an IRQ to handle the TX complete interrupt after DMA completes.

RX DMA is more problematic.  A good design cannot assume reception of messages with an exact block size, even if the source is known.  There's always a chance of noise corrupting a message as it moves through the transmission cable.  So you have to be able to detect inter-message gaps to timeout RX DMA between messages.  That's the basis of connecting the RXD pin to a timer input, a hardware watchdog to detect no activity for a fixed time period.   (An alternative is an external retriggered one-shot, some type of 555 or variant, reset by RXD and an output trigger to an external interrupt pin.)

I use a FreeRTOS based USART driver with both TX and RX DMA, half duplex RS-485 supporting several network protocols.  At 460Kbits/sec the overhead is very low for messages up to 520 bytes long.  The line turnaround gap at the end of every message guarantees a sufficient inter-message gap for RX DMA to work.  It is somewhat complicated to build and requires a good working knowledge of async design, and especially the techniques in how to work without moving data between locations, not recommended for a beginner.  But for a commercial product it definitely is a competitive advantage in that high data rates can be maintained with less hardware compared to single byte IRQ drivers.

  Jack Peacock

childresss
Associate II
Posted on March 08, 2015 at 00:10

Is that driver with RX DMA proprietary?

I presume it has uses a timer to truncate the DMA as needed.

jpeacock
Associate II
Posted on March 09, 2015 at 18:43

Unfortunately yes, it's proprietary otherwise I'd show some excerpts.  Quite a bit of work went into the driver, and the competition uses similar Cortex M controllers, so it isn't going to be available.

But knowing it can be done is half the design effort.  I use TIM 9 for the RX DMA timer since it has a reset pin input, but any timer with a reset capable pin input works (not all timers support this).  Overall my logic is to enable IRQ for the first character in a message, set up but not enable DMA for the rest of the message (largest possible size, 520 in my case), set up timer but not start it, and then wait for the first byte.

Soon as the first character arrives I stuff it in the buffer, turn off RXNE IRQ, enable the timer (30 bit times fits my inter-message gap for any data rate) and enable the RX DMA to complete the transfer.

If the timer overflows then it has found an inter-message gap.  I stop the timer and RX DMA, which in turn triggers a DMA TC.  The DMA TC signals a completed packet.  Packet length is determined by remaining transfer count in the DMA register (max - remaining count).  That's it, except for any line turnaround or starting a receive for the next message.

TX DMA is simpler.  Set up the DMA for the message address and byte count, enable DMA, wait for DMA TC.  At DMA TC the message is sent but the last byte may still be in transit, so I enable the USART TC interrupt.  At the USART TC the last byte is complete and the line can turn from TX to RX mode.

I use FreeRTOS mutex flags to serialize access to the USART and DMA hardware, and a message queue with the packet address and length to pass the completed data on to the network listener.  I don't pass the entire message because I don't want to move large amounts of data around in memory.  Better to use a pool of message buffers than copy buffers.

ray
Associate II
Posted on March 16, 2015 at 09:25

Do you try to use IDLE interrupt? It can detect RX line is idle after reception is end.

jpeacock
Associate II
Posted on March 16, 2015 at 18:40

The problem with IDLE is what defines end of reception.  There are two parameters: inter-character gap, time between two sequential bytes; and inter-message gap; time between end of a message and start of next message.  How does IDLE distinguish between the two?  An idle frame is only one character so it's likely to interrupt on inter-char gaps, especially if the remote device can't stream at full speed.

If you can guarantee the remote device strictly follows timing well enough to avoid one byte gaps then yes, IDLE can be used.  In practice it's not a reliable way to build a driver.

  Jack