Kinetis K20 UART TX with DMA

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

Kinetis K20 UART TX with DMA

Jump to solution
4,525 Views
Nixon
Contributor II

Hi,

I am currently working on small project and I would like to use DMA for UART transmission.  My current configuration is not working properly. I configured everything and in interrupt mode my UART TX is working ok but not with DMA. DMA always transfers only one byte.

I would be really grateful for any help.


Here is my code for transfering 4 bytes from memory to UART:


// Enable DMA clocks

  SIM->SCGC6 |= SIM_SCGC6_DMAMUX_MASK;

  SIM->SCGC7 |= SIM_SCGC7_DMA_MASK;

 

  // Enable UART1, PORT clocks, and TX line

  // configure TX pin

  SIM->SCGC5 |=  SIM_SCGC5_PORTE_MASK;

  PORTE->PCR[0] &= ~PORT_PCR_MUX_MASK;

  PORTE->PCR[0] |= PORT_PCR_MUX(GPIO_AF_ALT3);

 

  SIM->SCGC4 |= SIM_SCGC4_UART1_MASK;

  br_mpx = __UART_BRR(48000000, 9600);   // calculate baud rate mpx from desired clock

 

  // clear baud rate registers

  UART1->BDH &= ~(UART_BDH_SBR_MASK);   

  UART1->BDL = 0;

 

  // set baud rate

  UART1->BDH |= HI_BYTE(br_mpx);         

  UART1->BDL |= LO_BYTE(br_mpx);

  // Set baud rate

  UART1->C1  = 0;

  // Set FIFO water marks

  UART1->TWFIFO = UART_TWFIFO_TXWATER(1);

  UART1->RWFIFO = UART_RWFIFO_RXWATER(1);

  // Enable transmitter

  UART1->C2 = 0;

  UART1->C2 |= UART_C2_TE_MASK;

  UART1->S2 = 0;

  UART1->C3 = 0;

  // Configure TX DMA

  DMAMUX->CHCFG[0] &= ~(DMAMUX_CHCFG_ENBL_MASK);

  DMA0->CR = 0;

 

  DMAMUX->CHCFG[0] = DMAMUX_CHCFG_SOURCE(DMAMUX_UART1_SLOT);

  UART1->C2 |= UART_C2_TIE_MASK;

  UART1->C5 |= UART_C5_TDMAS_MASK;

  DMA0->TCD[0].BITER_ELINKNO = 1;

  DMA0->TCD[0].CITER_ELINKNO = 1;

  DMA0->TCD[0].SADDR = (uint32_t) &data_in[0];

  DMA0->TCD[0].ATTR = DMA_ATTR_SMOD(8) | DMA_ATTR_SSIZE(0) | DMA_ATTR_DMOD(0) | DMA_ATTR_DSIZE(0);

  DMA0->TCD[0].SOFF = 1;

  DMA0->TCD[0].NBYTES_MLNO = 4;

  DMA0->TCD[0].SLAST = 0;

  DMA0->TCD[0].DADDR = (uint32_t) &(UART1->D);

  DMA0->TCD[0].DOFF = 0;

  DMA0->TCD[0].DLAST_SGA = 0;

  DMA0->ERQ &= ~DMA_ERQ_ERQ0_MASK;

  DMAMUX->CHCFG[0] |= DMAMUX_CHCFG_ENBL_MASK;

  NVIC_EnableIRQ(DMA0_IRQn);

 

  DMA0->TCD[0].CSR = DMA_CSR_DREQ_MASK | DMA_CSR_DONE_MASK | DMA_CSR_INTMAJOR_MASK;

 

  DMA0->ERQ |= DMA_ERQ_ERQ0_MASK;

Labels (1)
0 Kudos
1 Solution
835 Views
Nixon
Contributor II

Hi,

I found an error in configuration. It was a problem with DMAMUX_UART1_SLOT constant value which was wrong. Manual says that it should be a source number (slot) and I defined constat by slot nr. of peripheral which is 107 instead of source number which is under DMA_MUX0 section as number 5. After that change DMA works just fine.

View solution in original post

0 Kudos
3 Replies
836 Views
Nixon
Contributor II

Hi,

I found an error in configuration. It was a problem with DMAMUX_UART1_SLOT constant value which was wrong. Manual says that it should be a source number (slot) and I defined constat by slot nr. of peripheral which is 107 instead of source number which is under DMA_MUX0 section as number 5. After that change DMA works just fine.

0 Kudos
835 Views
mjbcswitzerland
Specialist V

Hi

The number of bytes to transfer is controlled by DMA_TCD_BITER_ELINK and DMA_TCD_CITER_ELINK so it seems correct that the above nitialisation would only send 1 byte.

Each time there is a buffer ready for transmission the length needs to be configured accordingly.

Example from the uTasker Kinetis project:

// Start transfer of a block via DMA

//

extern QUEUE_TRANSFER fnTxByteDMA(QUEUE_HANDLE channel, unsigned char *ptrStart, QUEUE_TRANSFER tx_length)

{

    KINETIS_DMA_TDC *ptrDMA_TCD = (KINETIS_DMA_TDC *)eDMA_DESCRIPTORS;

    ptrDMA_TCD += UART_DMA_TX_CHANNEL[channel];

    ptrDMA_TCD->DMA_TCD_BITER_ELINK = ptrDMA_TCD->DMA_TCD_CITER_ELINK = tx_length; // the number of service requests (the number of bytes to be transferred)

    ptrDMA_TCD->DMA_TCD_SADDR = (unsigned long)ptrStart;                 // source is tty output buffer

    DMA_ERQ |= (DMA_ERQ_ERQ0 << UART_DMA_TX_CHANNEL[channel]);           // enable request source

    return tx_length;

}


Note that the uTasker project [http://www.utasker.com/kinetis.html] supports Rx and Tx DMA on all 6 UARTs. Its Kinetis simulator also simulates the UART DMA operation to allow simple analysis and testing.

Regards

Mark

835 Views
Nixon
Contributor II

Hi Mark,

thanks for your reply. I tried to modify my code in way you proposed so here it is:

My data source is defined as:

  uint8_t data_in[16];

 

   for (idx = 0; idx<16; idx++) {

    data_in[idx] = 65 + idx;

  }

and code as follows (except UART initialization part which is ok):

  ch = 0;

  // Clear all TCDs. 

  memset((void*)(&DMA0->TCD[ch]), 0, sizeof(DMA0->TCD)); 

 

  DMA0->TCD[ch].SADDR = (uint32_t) &data_in[0];

  DMA0->TCD[ch].SOFF = 1;//DMA_SOFF_SOFF(1);

  DMA0->TCD[ch].SLAST = 0;

 

  DMA0->TCD[ch].DADDR = (uint32_t) &(UART1->D);

  DMA0->TCD[ch].DOFF = DMA_DOFF_DOFF(0);

  DMA0->TCD[ch].DLAST_SGA = 0;

 

 

  DMA0->TCD[ch].ATTR = DMA_ATTR_SSIZE(0) | DMA_ATTR_DSIZE(0);

  DMA0->TCD[ch].NBYTES_MLNO = 1;

  DMA0->TCD[ch].BITER_ELINKNO = 0;

  DMA0->TCD[ch].CITER_ELINKNO = 0;

  DMAMUX->CHCFG[ch] = DMAMUX_CHCFG_SOURCE(DMAMUX_UART1_SLOT);

  DMAMUX->CHCFG[ch] |= DMAMUX_CHCFG_ENBL_MASK;

  DMA0->TCD[ch].CSR = DMA_CSR_DREQ_MASK | DMA_CSR_INTMAJOR_MASK ;

 

  NVIC_EnableIRQ(DMA0_IRQn); 

  DMA0->TCD[ch].BITER_ELINKNO = DMA_BITER_ELINKNO_BITER(4);

  DMA0->TCD[ch].CITER_ELINKNO = DMA_CITER_ELINKNO_CITER(4);

  DMA0->TCD[ch].CSR |=  DMA_CSR_START_MASK;

  DMA0->ERQ = DMA_ERQ_ERQ0_MASK < ch;


My request is to transfer four bytes but all I get is still a single char which is really strange.

Thanks for help.

0 Kudos