I'm writing a DMA driven UART driver, concentrating on the TX side first. Please help me understand the "UART DMA Service Request" and "Basic Transfer" interaction. First, what is the condition under which the service request is asserted by the UART? Is this the TDRE condition? Is this service request to the DMA level or edge sensitive?
I presume that in the case of the UART TX, the DMA minor loop must be set to 1 (I'm assuming only a one byte FIFO of UART2 to 5), and that the "Basic Transfer" or "Minor Loop" will not throttle based on the state of the DMA request? If so this requires a minor loop of one, and a major loop count that holds the number of bytes to transfer.
If this is the case, is there a coherent way to append to the major loop count (for a subsequent write to the UART) even with a DMA in progress? For instance, by stopping DMA first? I would like to try to avoid using interrupts to chain the next DMA transfer.
Thanks,
pmt
Solved! Go to Solution.
You could look into the section "Dynamic Scatter-Gather" in the RM. It contains a protocol for updating the next-descriptor pointer.
Minor loops of 1 are good.
And to save you time on the RX side, here's a few hints:
Related to this I can't seem to get the TX DMA request from UART5 working. Here's my checklist:
TXWATER = 0;
DMA and DMAMUX0 clock sources are enabled
TIE and TDMAS are set
CHCFG[0] enable masks is set and source is set for 13 (UART5 TX)
Various other parameters are set.
I can coax one character at a time out the UART by repeatedly setting the DMA_CSR_START bit, but I only get a single character. By CITER decrements as expected with each start. TDRE is asserted.
It's like to UART5 DMA request source simply isn't hooked up to DMA channel 0.
Any ideas?
pmt
Hi Pmt,
I am also writing routines to transmit a buffer out on UART3 via DMA channel 0. Followed the steps suggested by you
these are my configurations
/*URAT configuration*/
UART_C2_REG(uartch) &= ~(UART_C2_TE_MASK
| UART_C2_RE_MASK );
/* Configure the UART for 8-bit mode, no parity */
UART_C1_REG(uartch) = 0; /* We need all default settings, so entire register is cleared */
/* Calculate baud settings */
sbr = (uint16)((sysclk*1000)/(baud * 16));
/* Save off the current value of the UARTx_BDH except for the SBR field */
temp = UART_BDH_REG(uartch) & ~(UART_BDH_SBR(0x1F));
UART_BDH_REG(uartch) = temp | UART_BDH_SBR(((sbr & 0x1F00) >> 8));
UART_BDL_REG(uartch) = (uint8)(sbr & UART_BDL_SBR_MASK);
/* Determine if a fractional divider is needed to get closer to the baud rate */
brfa = (((sysclk*32000)/(baud * 16)) - (sbr * 32));
/* Save off the current value of the UARTx_C4 register except for the BRFA field */
temp = UART_C4_REG(uartch) & ~(UART_C4_BRFA(0x1F));
UART_C4_REG(uartch) = temp | UART_C4_BRFA(brfa);
UART_C5_REG(uartch) = 0xA0;
UART_PFIFO_REG(uartch) = 0xEE;
UART_CFIFO_REG(uartch) = 0xC0;
UART_S2_REG(uartch) = 0x00;
UART_C3_REG(uartch) = 0x00;
UART_TWFIFO_REG(uartch) = UART_TWFIFO_TXWATER(0);
UART_RWFIFO_REG(uartch) = UART_RWFIFO_RXWATER(1);
/* Enable receiver and transmitter */
UART_C2_REG(uartch) |= (UART_C2_TE_MASK
| UART_C2_RE_MASK | UART_C2_TIE_MASK | UART_C2_RIE_MASK);
/*Uart configuration looks good as I am bal eto put data on UART3_D buffer and watch on Tera term*/
/*DMA configuration*/
DMA_MemMapPtr DMA0 = DMA_BASE_PTR;
DMAMUX_MemMapPtr DMAMUX0 = 0x40021000;
DMAMUX0->CHCFG[0] = 0;
DMAMUX_CHCFG_REG(DMAMUX_BASE_PTR,0) = DMAMUX_CHCFG_ENBL_MASK|DMAMUX_CHCFG_SOURCE(0);
DMA0->TCD[0].BITER_ELINKNO = 0;
DMA0->TCD[0].CITER_ELINKNO = 0;
DMA0->TCD[0].SADDR = (uint32_t) &gau8Source0[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 = 1;
DMA0->TCD[0].SLAST = 0;
DMA0->TCD[0].DADDR = (uint32_t)(0x4006D007) ;//&(UART5->D);
DMA0->TCD[0].DOFF = 0;
DMA0->TCD[0].DLAST_SGA = 0;
DMA0->TCD[0].CSR = DMA_CSR_DREQ_MASK | DMA_CSR_DONE_MASK | DMA_CSR_INTMAJOR_MASK;
u32fnNVIC_EnableIRQ(INT_DMA0);
u32fnNVIC_EnableIRQ(INT_DMA1);
/*Transfer */
DMA0->TCD[0].BITER_ELINKNO = DMA_BITER_ELINKNO_BITER(8);
DMA0->TCD[0].CITER_ELINKNO = DMA_CITER_ELINKNO_CITER(8);
DMA0->SERQ = DMA_SERQ_SERQ(0);
--------------------------------------
Observation
1. Controller hits exception when I step through
DMAMUX_CHCFG_REG(DMAMUX_BASE_PTR,0) = DMAMUX_CHCFG_ENBL_MASK|DMAMUX_CHCFG_SOURCE(0);
2. How I can initiate the transfer on UART ?
Anil
For UART Tx/Rx DMA on any Kinetis (LPUART/UART) just copy the solution from the free open source uTasker project on Github.
To your questions:
1) Have you enabled clock gating to the DMAMUX before using it?
2) If you have the Tx enabled and in DMA Tx mode it will immediately start transfers when the DMA controller is enabled (since it will have a DMA trigger pending).
Regards
Mark
For less questions and faster, cheaper developments: try uTasker for Kinetis
I resolved my DMA signaling issue by starting the DMA with the SERQ register instead of the CSR START.
Thank konrada for the tips and heads up on the RX DMA. I'll let you know what I find.
pmt
Hi pmt.
I am also writing routines to transmit a buffer out on UART3 via DMA channel 0. I have setup the perepherals in the same way as you have (as far as you have described above). Can you share your code so I can compare too see if I have missed anything? Here is my setup:
//Init UART DMA for TX | ||
UART3_C2 |= (1<<UART_C2_TIE_SHIFT); | ||
UART3_C5 |= UART_C5_TDMAS_MASK; | ||
UART3_TWFIFO = 0;//TXWATER = 0; | ||
//Init DMA channel 0 | ||
enable_irq(0); // Enable DMA0 IRQ | ||
SIM_SCGC6 |= SIM_SCGC6_DMAMUX_MASK; // Enable Clock gating for the DMA and DMA MUX | ||
SIM_SCGC7 |= SIM_SCGC7_DMA_MASK; | ||
// Disable DMA Mux channel first | ||
DMAMUX_CHCFG0 = 0x00; //DMAMUX0_CHCFG0 = 0x00; | ||
DMA_TCD0_SOFF = 1; //Source increment. | ||
DMA_TCD0_NBYTES_MLNO = 1; ///1 Byte per dma request. | ||
// Set Destination Address | ||
DMA_TCD0_DADDR = UART_D_REG(PortBasePtr); //Points to UART3 base | ||
DMA_TCD0_DOFF = 0; //Do not increment destination address | ||
DMA_TCD0_ATTR = 0; // transfer sizes always single bytes | ||
DMA_TCD0_CSR = DMA_CSR_INTMAJOR_MASK; //The end-of-major loop interrupt is enabled | ||
DMA_ERQ |= DMA_ERQ_ERQ0_MASK; //The DMA request signal for the corresponding channel is enabled | ||
DMAMUX_CHCFG0 = 9; //UART3_TX | ||
DMAMUX_CHCFG0 |= DMAMUX_CHCFG_ENBL_MASK;//Enable DMA channel. | ||
DMA_TCD0_CSR |= DMA_CSR_DONE_MASK; | ||
DMA_TCD0_SADDR = SOURCE_ADDRESS; | ||
DMA_TCD0_CITER_ELINKNO = DMA_TCD0_BITER_ELINKNO = 16; //Num of bytes to send | ||
DMA_TCD0_CSR |= DMA_CSR_START_MASK; //Start DMA DMA_SERQ |= DMA_SERQ_SERQ_MASK; |
I am using Kinetis K60 MCU.
BR
Hi Dee,
I am also writing routines to transmit a buffer out on UART3 via DMA channel 0.
Any breakthrough for the above problem, I am stuck with the same issue. Your input would really help.
Hi Dee,
Below are snippets for UART5 which were derived from help from people on the board along with one of the Freescale engineers that sent me stock DMA/UART validation code. Note that I set up my TX configuration for a hardware modulo ring buffer (which must be aligned properly). Your application may differ.
//////////////////////////////////////////
// Setup
//////////////////////////////////////////
// Enable DMA clocks
SIM->SCGC6 |= SIM_SCGC6_DMAMUX0_MASK;
SIM->SCGC6 |= SIM_SCGC6_DMAMUX1_MASK;
SIM->SCGC7 |= SIM_SCGC7_DMA_MASK;
// Enable UART5, PORT clocks, and TX line
SIM->SCGC1 |= SIM_SCGC1_UART5_MASK;
SIM->SCGC5 |= SIM_SCGC5_PORTE_MASK;
PORTE->PCR[8] = (3UL << 8);
// Set baud rate
UART5->BDH = SBR >> 8;
UART5->BDL = SBR & 0xFF;
UART5->C1 = 0;
UART5->C4 = BRFA;
// Set FIFO water marks (not used in default 'legacy' port configuration)
UART5->TWFIFO = UART_TWFIFO_TXWATER(0);
UART5->RWFIFO = UART_RWFIFO_RXWATER(1);
// Enable transmitter
UART5->C2 = 0;
UART5->C2 |= UART_C2_TE_MASK;
UART5->S2 = 0;
UART5->C3 = 0;
// Configure TX DMA
DMAMUX0->CHCFG[0] = 0;
DMAMUX0->CHCFG[0] = DMAMUX_CHCFG_ENBL_MASK | DMAMUX_CHCFG_SOURCE(13);
UART5->C2 |= UART_C2_TIE_MASK;
UART5->C5 |= UART_C5_TDMAS_MASK;
DMA0->TCD[0].BITER_ELINKNO = 0;
DMA0->TCD[0].CITER_ELINKNO = 0;
DMA0->TCD[0].SADDR = (uint32_t) &TxBuf[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 = 1;
DMA0->TCD[0].SLAST = 0;
DMA0->TCD[0].DADDR = (uint32_t) &(UART5->D);
DMA0->TCD[0].DOFF = 0;
DMA0->TCD[0].DLAST_SGA = 0;
DMA0->TCD[0].CSR = DMA_CSR_DREQ_MASK | DMA_CSR_DONE_MASK | DMA_CSR_INTMAJOR_MASK;
NVIC_EnableIRQ(DMA0_DMA16_IRQn);
//////////////////////////////////////////
// Prime the next DMA transaction
//////////////////////////////////////////
// append the next DMA transaction and start TX
DMA0->TCD[0].BITER_ELINKNO = DMA_BITER_ELINKNO_BITER(NumToTx);
DMA0->TCD[0].CITER_ELINKNO = DMA_CITER_ELINKNO_CITER(NumToTx);
DMA0->SERQ = DMA_SERQ_SERQ(0);
Regards
You could look into the section "Dynamic Scatter-Gather" in the RM. It contains a protocol for updating the next-descriptor pointer.
Minor loops of 1 are good.
And to save you time on the RX side, here's a few hints: