AnsweredAssumed Answered

Overruns with Back-to-back USART Rx DMA on STM32F05x

Question asked by singer.marc on Dec 17, 2013
Latest reply on Dec 28, 2013 by singer.marc
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.

Outcomes