AnsweredAssumed Answered

Safe/correct way to pause DMA, modify BCR count, and resume?

Question asked by Mark Haun on Nov 21, 2014
Latest reply on Nov 25, 2014 by Hui_Ma

Hi,

 

I wrote a KL26 UART driver using circular tx/rx buffers and DMA.  It works fine, except once in a while it seems to skip a byte on transmit.  I suspect the problem might have something to do with the way I update BCR (the counter in the DSR_BCR register) when adding data to the transmit buffer.  The code looks like this (data to send is pointed to by "data" and length "len"):

 

    // Disable the UART Tx DMA request while we work.
    DMA0->DMA[n].DCR &= ~(DMA_DCR_ERQ_MASK);

    uint32_t old_bcr = DMA0->DMA[n].DSR_BCR & DMA_DSR_BCR_BCR_MASK;
    if (old_bcr + len < circular_buffer_size)
    {
        // Add new data to tx buffer and update "put" pointer.
        uint32_t bytesToEnd = (1 << UART_TX_BUF_SIZE_ORDER) - txPutPtr;
        if (len <= bytesToEnd)
        {
            memcpy(&txBuf[txPutPtr], data, len);
        }
        else
        {
            memcpy(&txBuf[txPutPtr], &data[0],          bytesToEnd);
            memcpy(&txBuf[0],        &data[bytesToEnd], len-bytesToEnd);
        }

        txPutPtr = (txPutPtr + len) & (circular_buffer_size - 1);

        // Update the DMA channel BCR counter to account for the new data.
        DMA0->DMA[n].DSR_BCR = DMA_DSR_BCR_DONE_MASK;  // must clear DONE first, then write BCR
        DMA0->DMA[n].DSR_BCR = DMA_DSR_BCR_BCR(old_bcr + len);
    }

    // Resume the Tx DMA.
    DMA0->DMA[n].DCR |= DMA_DCR_ERQ_MASK;

 

My question is, is it safe to modify BCR in this manner?  Or is there a better way to ensure DMA is paused while I modify it?

 

Thanks.

Outcomes