AnsweredAssumed Answered

K20 UART RX DMA - with a circular buffer

Question asked by Justin Kaufman on Apr 21, 2015
Latest reply on Jun 17, 2015 by Hui_Ma

I'm building an application where I want to have a large RX buffer off UART1 on a K20D7.

 

I've created a DMA transfer from UART1 using the configuration code below. The problem I see is determining whether there has been a buffer overflow. I'm also unclear on the exact operation of DMA_TCD0_DADDR.

 

I'm thinking of DMA_TCD0_DADDR as a head pointer on a circular buffer. Does the DMA actually update the DADDR in a way that I can read from to determine where the next byte will be stored?

 

My approach is that I'll have a tail pointer to pull data off the buffer. If the tail and DADDR are the same, then nothing is in the buffer.

 

The only problem is when DADDR laps the tail pointer. As far as I can tell, there's no way for me to detect this.

 

Suggestions? This must be a relatively common style of serial port.. perhaps there's a better implementation approach?

 

thanks!

Rudy

 

#define baud 9600

#define F_CPU 48000000

#define BAUD2DIV(baud)  (((F_CPU * 2) + ((baud) >> 1)) / (baud))

 


#define RX_BUFFER_SIZE 1024

static volatile uint8_t rx_buffer[RX_BUFFER_SIZE] __attribute__((aligned(1024)));

 

int divisor = BAUD2DIV(baud);

 

SIM_SCGC4 |= SIM_SCGC4_UART1_MASK;

PORTC_PCR3 = PORT_PCR_PE_MASK | PORT_PCR_PS_MASK | PORT_PCR_PFE_MASK | PORT_PCR_MUX(3);

PORTC_PCR4 = PORT_PCR_DSE_MASK | PORT_PCR_SRE_MASK | PORT_PCR_MUX(3);

  UART1_BDH = (divisor >> 13) & 0x1F;

  UART1_BDL = (divisor >> 5) & 0xFF;

  UART1_C4 = divisor & 0x1F;

   UART1_C1 = 0; // configures 8-bit, no parity

  UART1_PFIFO = 0; // no fifo since we're setting up DMA

 

  // UART DMA RECEIVE CONFIGURATION

  //enable DMA clocks

  SIM_SCGC6 |= SIM_SCGC6_DMAMUX_MASK;

  SIM_SCGC7 |= SIM_SCGC7_DMA_MASK;

 

  DMAMUX_CHCFG0 = 0; // disable the channel first

  // config the dma channel

  DMA_DSR_BCR0 |= DMA_DSR_BCR_DONE_MASK;

  DMA_TCD0_CSR = 0;

  DMA_CR = 0; // default control register

  DMA_DCHPRI0 = 0; // DMA channel 0 priority default to 0

  DMA_EEI = 0; // all error interrupts are cleared. might be needed if we're debugging..

  // now set up TCD

  DMA_TCD0_SADDR = (uint32_t) &UART1_D; // reading from UART1 data register

  DMA_TCD0_SOFF = 0;

  DMA_TCD0_ATTR = DMA_ATTR_SMOD(0) | DMA_ATTR_SSIZE(0)    // source does not iterate - no modulo, source is a byte in size,

  | DMA_ATTR_DMOD(0) | DMA_ATTR_DSIZE(10); // destination is a byte, 1 kB of buffer space, address must align with a 1k boundary

  DMA_TCD0_NBYTES_MLNO = 1; // one byte is transferred each time.

  DMA_TCD0_SLAST = 0; // the source address never changes

  DMA_TCD0_DADDR = (uint32_t) &rx_buffer;

  DMA_TCD0_DOFF = 1; // incoming byte increment by 1 through the rx_buffer, wrapping around at the 1k boundary

  DMA_TCD0_CITER_ELINKNO = 1; // single major loop. CITER and BITER must be initialized to the same value.

  DMA_TCD0_BITER_ELINKNO = 1;

  DMA_TCD0_DLASTSGA = 0; // no adjustment to destination address... assuming that destination address has not been changed.

 

  // enable the dma channel

  DMA_ERQ |= DMA_ERQ_ERQ0_MASK;

  DMAMUX_CHCFG0 = DMAMUX_CHCFG_ENBL_MASK | 0x04; // sets channel 0 source to UART1 receive

  UART1_C5 = UART_C5_RDMAS_MASK; // Now, when an character is received, as long as the receive interrupt is enabled, a DMA request will be sent.

 

  UART1_C2 = C2_ENABLE; // enable the UART

 

  // setup the interrupts - now used for TX only...

  NVIC_SET_PRIORITY(IRQ_UART1_STATUS, IRQ_PRIORITY);

  NVIC_ENABLE_IRQ(IRQ_UART1_STATUS);

Outcomes