I'm trying to use DMA for UART2 on TWR - K65F 180M. The reason is UART2 is readily available through the USB and easy to test.
I came across this example application low_power_dma_uart_demo, which tried to add the DMA to LPUART0. I did not test it as LPUART0 is not readily available on any connectors.
But I tried to make some changes to adapt the code to UART2. Below is my code.
int main (void)
uint32 reg_temp = 0;
register uint16 sbr, brfa;
extern int periph_clk_khz;
extern int mcg_clk_hz;
int uartClock = periph_clk_khz;
char TxBuf = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
// Enable DMA clocks
SIM_SCGC6 |= SIM_SCGC6_DMAMUX_MASK;
SIM_SCGC7 |= SIM_SCGC7_DMA_MASK;
// Enable UART5, PORT clocks, and TX line
SIM_SCGC4 |= SIM_SCGC4_UART2_MASK;
SIM_SCGC5 |= SIM_SCGC5_PORTE_MASK;
PORTE_PCR17 = PORT_PCR_MUX(0x3);
PORTE_PCR16 = PORT_PCR_MUX(0x3);
/* Make sure that the transmitter and receiver are disabled while we
* change settings.
UART_C2_REG(TERM_PORT) &= ~(UART_C2_TE_MASK
| UART_C2_RE_MASK );
/* Configure the UART for 8-bit mode, no parity */
UART_C1_REG(TERM_PORT) = 0; /* We need all default settings, so entire register is cleared */
/* Calculate baud settings */
sbr = (uint16)((uartClock*1000)/(115200 * 16));
/* Save off the current value of the UARTx_BDH except for the SBR field */
temp = UART_BDH_REG(TERM_PORT) & ~(UART_BDH_SBR(0x1F));
UART_BDH_REG(TERM_PORT) = temp | UART_BDH_SBR(((sbr & 0x1F00) >> 8));
UART_BDL_REG(TERM_PORT) = (uint8)(sbr & UART_BDL_SBR_MASK);
/* Determine if a fractional divider is needed to get closer to the baud rate */
brfa = (((uartClock*32000)/(115200 * 16)) - (sbr * 32));
/* Save off the current value of the UARTx_C4 register except for the BRFA field */
temp = UART_C4_REG(TERM_PORT) & ~(UART_C4_BRFA(0x1F));
UART_C4_REG(TERM_PORT) = temp | UART_C4_BRFA(brfa);
// Set FIFO water marks (not used in default 'legacy' port configuration)
UART_TWFIFO_REG(TERM_PORT) = UART_TWFIFO_TXWATER(0);
UART_RWFIFO_REG(TERM_PORT) = UART_RWFIFO_RXWATER(1);
UART_C5_REG(TERM_PORT) |= UART_C5_TDMAS_MASK; // use DMA rather than interrupts for transmission
/* Enable receiver and transmitter */
UART_C2_REG(TERM_PORT) |= ( UART_C2_TE_MASK | UART_C2_RE_MASK | UART_C2_TIE_MASK | UART_C2_TCIE_MASK );
enable_irq(1); //see table: 3-5 , page-85
// Enable Global Interrupts
DMAMUX_CHCFG1 = 0x00;
DMA_TCD1_BITER_ELINKNO = 1;
DMA_TCD1_CITER_ELINKNO = 1;
// Source address
DMA_TCD1_SADDR = (uint32_t)TxBuf;
DMA_TCD1_SOFF = 1; //Source increment (Source Address Signed Offset)
DMA_TCD1_SLAST = 0; //Last Source Address Adjustment
// Destination address
DMA_TCD1_DADDR = (uint32_t) &UART2_D; // Set Destination Address. Points to UART2 base
DMA_TCD1_DOFF = 0; //Do not increment destination address
DMA_TCD1_DLASTSGA = 0x0; //Destination last address adjustment
DMA_TCD1_ATTR = (0 | DMA_ATTR_SSIZE(0) | DMA_ATTR_DSIZE(0)); // transfer sizes always single bytes (000 - 8-bit)
DMA_TCD1_NBYTES_MLNO = sizeof(TxBuf); //minor loop mapping is disabled; Number of bytes to be transferred in each service request of the channel
DMA_TCD1_CSR = (DMA_CSR_INTMAJOR_MASK | DMA_CSR_DREQ_MASK); //The end-of-major loop interrupt is enabled (Check)
DMAMUX_CHCFG1 = (DMAMUX_CHCFG_SOURCE(7) // UART2-Tx is source 7 //refer page 103 of the datasheet
| DMAMUX_CHCFG_ENBL_MASK); // enable the DMA channel request
DMA_TCD1_CSR |= DMA_CSR_START_MASK;
DMA_SERQ |= 1;//DMA_SERQ_SERQ(1);
When I run this code, I could just see the first byte on the terminal (In this case just 'A'). As you can see I'm trying to send 26 bytes through UART. After running the code I can see SADDR actually offset by 26 bytes which indicates that the DMA has transferred all 26 bytes but the UART is not fast enough to capture and send all 26 bytes. This could be because both DMA and UART are configured with different clock source (DMA-System Clock; UART2-Bus Clock).
The low_power_dma_uart_demo example confirms this with the following comment.
// Switch the system clock to the IR Clock !!!
// NOTE: Due to a rev 1.0 errata, the UART and DMA must be clocked from
// sources that are approximately the same frequency. Otherwise, the
// DMA will finish before it is time.
In which they try to use the IR clock to configure LPUART.
My question is how do I configure UART2 and DMA to use a clock source of same frequency? Could you please give me an example?
I did try the above code with slight modification for Memory to memory transfer and it works perfectly. So I believe if I make UART2 and DMA to use the same clock frequency this issue should be fixed.
I have also attached my sysinit.c file for your reference.
Any help is much appreciated. Thank you.
Original Attachment has been moved to: sysinit.c.zip