DMA and UART questions

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

DMA and UART questions

Jump to solution
5,158 Views
pmt
Contributor V

 

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

 

  

 

0 Kudos
1 Solution
2,237 Views
konrada
Contributor IV

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:

  1. UART RX DMA doesn't work with the internal default clock source (at least not on my TWRK60, TWRK70, and custom board). You receive chars twice.
  2. Before starting the RX side, read erratum e2584; it means you can't use the IDLE interrupt to capture single received chars.
  3. Disable the RX FIFO when using DMA. It almost works.

View solution in original post

0 Kudos
8 Replies
2,237 Views
pmt
Contributor V

 

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

0 Kudos
2,237 Views
anilrayan
Contributor I

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 ?

0 Kudos
2,236 Views
mjbcswitzerland
Specialist V

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

2,236 Views
pmt
Contributor V

 

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

0 Kudos
2,236 Views
deekay
Contributor I

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


0 Kudos
2,236 Views
anilrayan
Contributor I

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.

0 Kudos
2,237 Views
pmt
Contributor V

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

0 Kudos
2,238 Views
konrada
Contributor IV

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:

  1. UART RX DMA doesn't work with the internal default clock source (at least not on my TWRK60, TWRK70, and custom board). You receive chars twice.
  2. Before starting the RX side, read erratum e2584; it means you can't use the IDLE interrupt to capture single received chars.
  3. Disable the RX FIFO when using DMA. It almost works.
0 Kudos