2013-12-16 03:05 PM
We're using a UART to pass control information between two STM32 parts. The bit rate is 500Kbaud which gives us about 20us per byte. The aim is to use DMA to receive the byte stream in the background and process the bytes in the foreground. With simple RXNE interrupts we're able to keep up and the data received is clean. However, using DMA we're finding that we lose a byte to an overrun at the start of each DMA transaction during an active transfer.
To isolate the problem, we enabled the error interrupt, EIE in CR3, and we toggle an IO line whenever there is an overrun. We toggle a different IO line whenever we initiate and complete a DMA transaction. We're not using a circular buffer because the stream is not aligned in any way. We detect when the line goes idle and flush the bytes already received to the foreground thread.The logic analyzer capture shows that we require 3.8us to turn the DMA transaction around when it completes. This should be fast enough to reenable DMA between two bytes. The overrun occurs 40.1us from the end of the last transaction which is exactly what you'd expect if the first byte fails to activate the DMA transfer and the second byte causes the overrun.The behavior is very consistent. In fact, every DMA transfer seems to have an overrun about 40us from the start.Here is the code we're using to initialize the DMA transfer. The custom macros handle some of the glue that binds devices to their registers. UART_CR3(uart_) |= UART_CR3_DMAR; size_t ib = transfer_base (); DMA_xCR(map) = 0; DMA_xNDTR(map) = avail; DMA_xPAR(map) = reinterpret_cast<uint32_t> (&UART_RDR(uart_)); DMA_xMAR(map) = reinterpret_cast<uint32_t> (&buffer_[ib]); DMA_IFCR_CLEAR(map, DMA_IFCR_ALL); DMA_xCR(map) = 0 | DMA_CR_PL_HIGH | DMA_CR_MSIZE_8 | DMA_CR_PSIZE_8 | DMA_CR_MINC | DMA_CR_DIR_P2M | DMA_CR_TCIE | DMA_CR_TEIE#if defined (DMA_CR_DM) | DMA_CR_DM#endif ; barrier; schedule_rx_dma_timer (avail); DMA_xCR(map) |= DMA_CR_EN;Suggestions welcome.2013-12-27 05:53 PM
I believe we found the root cause.
We're implementing something like the scheme described in AN3109 for UART Rx DMA. The time to turn-around a completed DMA transaction and start a new one is short, about 9us. The timer that checks for an idle line is quick as well, about 12us. The timer has a higher priority than the DMA. So, if the timer triggers near the end of a DMA transaction and if there is anything else of a high priority that delays the DMA turn-around then it is possible to drop a character.A solution will probably require an intermediate, circular DMA buffer where the data are copied out at every 1/2 cycle or when the timer expires. That way, the DMA will never be off-line.Cheers