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.