K20 UART RX DMA - with a circular buffer

cancel
Showing results for 
Show  only  | Search instead for 
Did you mean: 

K20 UART RX DMA - with a circular buffer

1,980 Views
rudymoore
Contributor I

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);

Labels (1)
0 Kudos
3 Replies

741 Views
Hui_Ma
NXP TechSupport
NXP TechSupport

Hi Justin,

I check your eDMA setting only use one major loop for one minor loop.

I would recommend you to use below eDMA setting, one major loop with more minor loop.

Each minor loop could transfer one byte from UART1 data to RX buffer.

DMA.png

It could set one major loop with N(buffer byte size) minor loop, each UART receive will transfer one byte data to RX buffer.

Customer could read DMA_TCDn_BITER_ELINKNO [BITER] bits value to check how many bytes could be load into the RX buffer.

BTW: I check below code with problem:

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

The DMA_ATTR_DSIZE value also should be 0.

Wish it helps.


Have a great day,
Ma Hui

-----------------------------------------------------------------------------------------------------------------------
Note: If this post answers your question, please click the Correct Answer button. Thank you!
-----------------------------------------------------------------------------------------------------------------------

0 Kudos

741 Views
jinxin_cn
Contributor I

Hi Ma Hui

If the SSIZE set to 001(16-bits), how could I control the data receive between big-endian and little-endian?

B.R.

Jin Xin

0 Kudos

741 Views
Hui_Ma
NXP TechSupport
NXP TechSupport

Hi Jin Xin,

Kinetis product only supports the little endian, although ARM Cortex M4 core support both big and little endian.


Have a great day,
Ma Hui

-----------------------------------------------------------------------------------------------------------------------
Note: If this post answers your question, please click the Correct Answer button. Thank you!
-----------------------------------------------------------------------------------------------------------------------

0 Kudos