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;
Solved! Go to Solution.
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.
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.
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
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.