DMA with UART: receiving every character twice

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

DMA with UART: receiving every character twice

Jump to solution
7,489 Views
konrada
Contributor IV

Hello all,

 

I'm trying to use DMA to receive from my K70's UART (currently using UART1). When I send characters from my host computer over the serial line to the Kinetis, the debugger shows that the receive buffer contains every character twice. From the DMA TCD, I see that there have been as many major loops as characters received (i.e. twice as many as characters sent). Also, the RXUF (receive underflow) bit is set in UART1_SFIFO. 

 

I'm confident that the DMA source in the DMAMUX, as well as the baud rate, are correct because I'm seeing properly received characters after all. 

I've told the DMA controller to copy one byte from the UARTs D register to my buffer. When polling, that's too little because the protocol requires pollers to read S1 before D; however, I understand from section 57.7 in the K70 RM that S1 needn't be polled in DMA mode.

 

 This is my UART and DMA init code:

 

   /* Calculate baud settings */  /* baud_divisor = clock_speed/ baudrate + 0.5 */  uint32 baud_divisor =       (pInit->m_BaseClock + (8 * baudrate))          / (16 * baudrate);  if (baud_divisor > 0x1fff)     return ;//IO_ERROR;  sci_ptr->BDH = (uint8)((baud_divisor >> 8) & 0xFF);  sci_ptr->BDL = (uint8)(baud_divisor & 0xFF);  sci_ptr->C1 = 0; /* 8-bit mode. Normal operation */  sci_ptr->C2 = 0;   sci_ptr->C3 = 0;   /* Disable all errors interrupts */  sci_ptr->SFIFO = UART_SFIFO_TXOF_MASK | UART_SFIFO_RXUF_MASK ;   /* Reset RXUF and TXOF in SFIFO */  /* set watermark in the almost full TX buffer */  if (((sci_ptr->PFIFO & UART_PFIFO_TXFIFOSIZE_MASK) >> UART_PFIFO_TXFIFOSIZE_SHIFT) == 0) {     /* 1 dataword in D */     sci_ptr->TWFIFO = UART_TWFIFO_TXWATER(0);  }  else {     uint8 txsize = 1 << (((sci_ptr->PFIFO & UART_PFIFO_TXFIFOSIZE_MASK) >> UART_PFIFO_TXFIFOSIZE_SHIFT) + 1);     /* watermark for TX buffer generates interrupts below & equal to watermark */     sci_ptr->TWFIFO = UART_TWFIFO_TXWATER(txsize - 1);  }  /* watermark for RX buffer generates interrupts above & equal to watermark */  sci_ptr->RWFIFO = UART_RWFIFO_RXWATER(1);  /* Enable TX FIFO, DISable RX FIFO for DMA */  sci_ptr->PFIFO = (sci_ptr->PFIFO & ~UART_PFIFO_RXFE_MASK) | UART_PFIFO_TXFE_MASK;   /* Flush RX / TX buffers */     sci_ptr->CFIFO = UART_CFIFO_RXFLUSH_MASK | UART_CFIFO_TXFLUSH_MASK;  sci_ptr->C2 |= UART_C2_RIE_MASK;   /* Enable DMA source for receiver */  sci_ptr->C5 |= UART_C5_RDMAS_MASK;   /* trigger DMA instead of triggering interrupts*/  // Init DMA  // Cancel all requests, enable priority arbitration (0) enable EMLM (minor-loop mapping)  // set the group priorities such that channel 0 is lowest and channel 31 highest  DMA_CR = 0 | DMA_CR_CX_MASK | DMA_CR_EMLM_MASK | DMA_CR_GRP1PRI(1) | DMA_CR_GRP0PRI(0) ;    // Disable error interrupts and clear all pending interrupts.  DMA_EEI = 0;  DMA_INT = 0x0000FFFF;    // Clear all TCDs.  memset((void*)(&DMA_BASE_PTR->TCD[0]), 0, sizeof(DMA_BASE_PTR->TCD));  // Init TCD0 for DMA  uint8 dmaMuxSource = pInit->m_RxDmaMuxSource;  uint8 chan = 0;  DMAMUX0_CHCFG(chan) =  DMAMUX_CHCFG_SOURCE(dmaMuxSource)  | DMAMUX_CHCFG_ENBL_MASK;    uint16 srcWordSize = 1;  const uint32 DMA_TSIZE_8_BITS = 0;        uint32 srcWordSizeCode =  DMA_TSIZE_8_BITS;  uint32 destWordSizeCode = DMA_TSIZE_8_BITS;  UartBuffer volatile* pUartBuf = &m_UartBuffers[channel];    uchar8 volatile* srcAddr = &sci_ptr->D;    DMA_CERQ = DMA_CERQ_CERQ(chan);   // Disable DMA request.  DMA_NBYTES_MLNO(chan) = DMA_NBYTES_MLNO_NBYTES(srcWordSize);   // Number of bytes per minor loop:  DMA_SADDR(chan) = (uint32) srcAddr; // Source address, to be advanced by 0 bytes after one word.   DMA_SOFF(chan) = DMA_SOFF_SOFF(0);       DMA_SLAST(chan) = 0;  DMA_ATTR(chan) =  DMA_ATTR_SSIZE(srcWordSizeCode) | DMA_ATTR_DSIZE(destWordSizeCode) ;     DMA_DOFF(chan) = 1; // advance one byte per write     DMA_CSR(chan) = DMA_CSR_DREQ_MASK; // after the major iterations are through, disable DMA.  uint32 numberOfMinorLoops = BUFFER_SIZE;  uchar8 volatile* destAddr = &pUartBuf->m_Buffer[0];    DMA_CITER_ELINKNO(chan) = numberOfMinorLoops;  DMA_BITER_ELINKNO(chan) = numberOfMinorLoops;  DMA_DADDR(chan) = (uint32) (destAddr);   // Destination address  DMA_DLAST_SGA(chan) = 0;   // for a one-off transfer, we don't care about the DEST pointer afterwards.     DMA_SERQ = DMA_SERQ_SERQ(chan);  /* Transmitter and receiver enable */  sci_ptr->C2 |= UART_C2_RE_MASK + UART_C2_TE_MASK;

There's probably something I'm doing wrong, but what?

Greetings, KA

1 Solution
3,026 Views
konrada
Contributor IV

Additional data point: my example code ran on default oscillator settings. When I let it run inside the application where clocks are already set up for 120MHz, DMA reception works fine on UART1.

View solution in original post

10 Replies
3,027 Views
nurichard
Contributor III

Just for other people who want to look at the code posted by Mark Butcher, I have attached a formatted version

0 Kudos
3,028 Views
mjbcswitzerland
Specialist V

Hi

 

I have used Tx and Rx DMA on all 6 UARTs in the K70 and didn't have a problem with receiving two bytes per Rx request.

Check that the macros used to write the registers are giving the expected values.

Also try not changing any of the FIFO settings (I used defaults).

 

Below is reference code from the uTasker project - it uses an interrupt one half buffer or full buffer and the destination is automatically set back to the start of the buffer. It won't be fully understandable without full source but may give an idea:

 

 

Configuration done once per channel:

 

        KINETIS_DMA_TDC *ptrDMA_TCD = (KINETIS_DMA_TDC *)eDMA_DESCRIPTORS;        ptrDMA_TCD += UART_DMA_RX_CHANNEL[Channel];        fnEnterInterrupt((irq_DMA0_ID + UART_DMA_RX_CHANNEL[Channel]), UART_DMA_RX_INT_PRIORITY[Channel], (void (*)(void))_uart_rx_dma_Interrupt[Channel]); // enter DMA interrupt handler        uart_reg->UART_C5 |= UART_C5_RDMAS;                              // use DMA rather than interrupts for reception        POWER_UP(6, SIM_SCGC6_DMAMUX0);                                  // enable DMA multiplexer 0        *(unsigned char *)(DMAMUX0_BLOCK + UART_DMA_RX_CHANNEL[Channel]) = ((DMAMUX_CHCFG_SOURCE_UART0_RX + (2 * Channel)) | DMAMUX_CHCFG_ENBL); // connect UART rx to DMA channel        ptrDMA_TCD->DMA_TCD_BITER_ELINK = ptrDMA_TCD->DMA_TCD_CITER_ELINK = pars->Rx_tx_sizes.RxQueueSize; // the length of the input buffer in use        ptrDMA_TCD->DMA_TCD_SOFF = 0;                                    // source not increment        ptrDMA_TCD->DMA_TCD_DOFF = 1;                                    // destination incremented      //_SET_DMA_CHANNEL_PRIORITY(UART_DMA_RX_CHANNEL[Channel], UART_DMA_RX_PRIORITY[Channel]); // DMA priority, whereby channel can suspend a lower priority channel (not changed since we use the channel's inherent priority)        ptrDMA_TCD->DMA_TCD_ATTR = (DMA_TCD_ATTR_DSIZE_8 | DMA_TCD_ATTR_SSIZE_8); // transfer sizes always single bytes        ptrDMA_TCD->DMA_TCD_SADDR = (unsigned long)&(uart_reg->UART_D);  // source is the UART's data register        ptrDMA_TCD->DMA_TCD_NBYTES_ML = 1;                               // each request starts a single transfer        if (pars->ucDMAConfig & UART_RX_DMA_HALF_BUFFER) {               // if operating in half-buffer mode            ptrDMA_TCD->DMA_TCD_CSR = (DMA_TCD_CSR_INTMAJOR | DMA_TCD_CSR_INTHALF); // never disable and inform on half and full buffer        }        else {            ptrDMA_TCD->DMA_TCD_CSR = (DMA_TCD_CSR_INTMAJOR);            // never disable and inform on full buffer only        }        ptrDMA_TCD->DMA_TCD_DLASTSGA = (-pars->Rx_tx_sizes.RxQueueSize); // when the buffer has been filled set the destination back to the start of it

 

Operation enabled when receiver started (once per channel)

 

        KINETIS_DMA_TDC *ptrDMA_TCD = (KINETIS_DMA_TDC *)eDMA_DESCRIPTORS;        ptrDMA_TCD += UART_DMA_RX_CHANNEL[channel];        ptrDMA_TCD->DMA_TCD_DADDR = (unsigned long)ptrStart;             // destination is the input tty buffer        DMA_ERQ |= (DMA_ERQ_ERQ0 << UART_DMA_RX_CHANNEL[channel]);       // enable request source        fnRxOn(channel);                                                 // configure receiver pin and enable reception and its interrupt/DMA

 

Check also the errata for the chip. There is a potential problem when receive error interrupts are used due to the fact that reading the status register can cause pending DMA to be reset (or something similar).

 

Regards

 

Mark

 

 

 

0 Kudos
3,028 Views
konrada
Contributor IV

Thanks for the detailed answer.

 

If I enable the FIFO (which is not a long-term viable option; my driver code must work with another board's UART2), I also get two DMA requests per char (as observed with BITER-CITER).

 

I can exclude stale DMA requests directly after init; if I transmit no char, then BITER=CITER.

 

When I install an ISR which performs the read-S1-read-D protocol, and I clear C5.RDMAS, then the ISR is called just once per char. Strange.

 

Could you post some of your UART init code for comparison? 

 

 

0 Kudos
3,028 Views
mjbcswitzerland
Specialist V

Hi

 

This the complete init code:

 

extern void fnConfigSCI(QUEUE_HANDLE Channel, TTYTABLE *pars){    KINETIS_UART_CONTROL *uart_reg = fnSelectChannel(Channel);    unsigned short usDivider;    unsigned char  ucFraction;    switch (Channel) {    case 0:        POWER_UP(4, SIM_SCGC4_UART0);                                    // power up the UART        break;    case 1:        POWER_UP(4, SIM_SCGC4_UART1);                                    // power up the UART        break;    case 2:        POWER_UP(4, SIM_SCGC4_UART2);                                    // power up the UART        break;    case 3:        POWER_UP(4, SIM_SCGC4_UART3);                                    // power up the UART        break;    case 4:        POWER_UP(1, SIM_SCGC1_UART4);                                    // power up the UART        break;    case 5:        POWER_UP(1, SIM_SCGC1_UART5);                                    // power up the UART        break;    default:        return;    }    #ifdef SUPPORT_HW_FLOW    if (pars->Config & RTS_CTS) {                                        // HW flow control defined so configure RTS/CTS pins        fnControlLine(Channel, (CONFIG_RTS_PIN | CONFIG_CTS_PIN), 0);    }    #endif    #if SYSTEM_CLOCK != BUS_CLOCK    if (Channel <= 1) {                                                  // UARTs 0 and 1 are clocked from the core/system clock and the others from the bus clock        switch (pars->ucSpeed) {                   case SERIAL_BAUD_300:            ucFraction = (unsigned char)((float)((((float)SYSTEM_CLOCK/(float)16/(float)300) - (int)(SYSTEM_CLOCK/16/300)) * 32)); // calculate fraction            usDivider = (SYSTEM_CLOCK/16/300);                           // set 300            break;        case SERIAL_BAUD_600:            ucFraction = (unsigned char)((float)((((float)SYSTEM_CLOCK/(float)16/(float)600) - (int)(SYSTEM_CLOCK/16/600)) * 32)); // calculate fraction            usDivider = (SYSTEM_CLOCK/16/600);                           // set 600            break;        case SERIAL_BAUD_1200:            ucFraction = (unsigned char)((float)((((float)SYSTEM_CLOCK/(float)16/(float)1200) - (int)(SYSTEM_CLOCK/16/1200)) * 32)); // calculate fraction            usDivider = (SYSTEM_CLOCK/16/1200);                          // set 1200            break;        case SERIAL_BAUD_2400:            ucFraction = (unsigned char)((float)((((float)SYSTEM_CLOCK/(float)16/(float)2400) - (int)(SYSTEM_CLOCK/16/2400)) * 32)); // calculate fraction            usDivider = (SYSTEM_CLOCK/16/2400);                          // set 2400            break;        case SERIAL_BAUD_4800:            ucFraction = (unsigned char)((float)((((float)SYSTEM_CLOCK/(float)16/(float)4800) - (int)(SYSTEM_CLOCK/16/4800)) * 32)); // calculate fraction            usDivider = (SYSTEM_CLOCK/16/4800);                          // set 4800            break;        case SERIAL_BAUD_9600:            ucFraction = (unsigned char)((float)((((float)SYSTEM_CLOCK/(float)16/(float)9600) - (int)(SYSTEM_CLOCK/16/9600)) * 32)); // calculate fraction            usDivider = (SYSTEM_CLOCK/16/9600);                         // set 9600            break;        case SERIAL_BAUD_14400:            ucFraction = (unsigned char)((float)((((float)SYSTEM_CLOCK/(float)16/(float)14400) - (int)(SYSTEM_CLOCK/16/14400)) * 32)); // calculate fraction            usDivider = (SYSTEM_CLOCK/16/14400);                         // set 14400            break;        default:                                                         // if not valid value set this speed        case SERIAL_BAUD_19200:            ucFraction = (unsigned char)((float)((((float)SYSTEM_CLOCK/(float)16/(float)19200) - (int)(SYSTEM_CLOCK/16/19200)) * 32)); // calculate fraction            usDivider = (SYSTEM_CLOCK/16/19200);                         // set 19200            break;        case SERIAL_BAUD_38400:            ucFraction = (unsigned char)((float)((((float)SYSTEM_CLOCK/(float)16/(float)38400) - (int)(SYSTEM_CLOCK/16/38400)) * 32)); // calculate fraction            usDivider = (SYSTEM_CLOCK/16/38400);                         // set 38400            break;        case SERIAL_BAUD_57600:            ucFraction = (unsigned char)((float)((((float)SYSTEM_CLOCK/(float)16/(float)57600) - (int)(SYSTEM_CLOCK/16/57600)) * 32)); // calculate fraction            usDivider = (SYSTEM_CLOCK/16/57600);                         // set 57600            break;        case SERIAL_BAUD_115200:            ucFraction = (unsigned char)((float)((((float)SYSTEM_CLOCK/(float)16/(float)115200) - (int)(SYSTEM_CLOCK/16/115200)) * 32)); // calculate fraction            usDivider = (SYSTEM_CLOCK/16/115200);                        // set 115200            break;        case SERIAL_BAUD_230400:            ucFraction = (unsigned char)((float)((((float)SYSTEM_CLOCK/(float)16/(float)230400) - (int)(SYSTEM_CLOCK/16/230400)) * 32)); // calculate fraction            usDivider = (SYSTEM_CLOCK/16/230400);                        // set 230400            break;        }    }    else {    #endif        switch (pars->ucSpeed) {                      case SERIAL_BAUD_300:            ucFraction = (unsigned char)((float)((((float)BUS_CLOCK/(float)16/(float)300) - (int)(BUS_CLOCK/16/300)) * 32)); // calculate fraction            usDivider = (BUS_CLOCK/16/300);                              // set 300            break;        case SERIAL_BAUD_600:            ucFraction = (unsigned char)((float)((((float)BUS_CLOCK/(float)16/(float)600) - (int)(BUS_CLOCK/16/600)) * 32)); // calculate fraction            usDivider = (BUS_CLOCK/16/600);                              // set 600            break;        case SERIAL_BAUD_1200:            ucFraction = (unsigned char)((float)((((float)BUS_CLOCK/(float)16/(float)1200) - (int)(BUS_CLOCK/16/1200)) * 32)); // calculate fraction            usDivider = (BUS_CLOCK/16/1200);                             // set 1200            break;        case SERIAL_BAUD_2400:            ucFraction = (unsigned char)((float)((((float)BUS_CLOCK/(float)16/(float)2400) - (int)(BUS_CLOCK/16/2400)) * 32)); // calculate fraction            usDivider = (BUS_CLOCK/16/2400);                             // set 2400            break;        case SERIAL_BAUD_4800:            ucFraction = (unsigned char)((float)((((float)BUS_CLOCK/(float)16/(float)4800) - (int)(BUS_CLOCK/16/4800)) * 32)); // calculate fraction            usDivider = (BUS_CLOCK/16/4800);                             // set 4800            break;        case SERIAL_BAUD_9600:            ucFraction = (unsigned char)((float)((((float)BUS_CLOCK/(float)16/(float)9600) - (int)(BUS_CLOCK/16/9600)) * 32)); // calculate fraction            usDivider = (BUS_CLOCK/16/9600);                            // set 9600            break;        case SERIAL_BAUD_14400:            ucFraction = (unsigned char)((float)((((float)BUS_CLOCK/(float)16/(float)14400) - (int)(BUS_CLOCK/16/14400)) * 32)); // calculate fraction            usDivider = (BUS_CLOCK/16/14400);                            // set 14400            break;        default:                                                         // if not valid value set this speed        case SERIAL_BAUD_19200:            ucFraction = (unsigned char)((float)((((float)BUS_CLOCK/(float)16/(float)19200) - (int)(BUS_CLOCK/16/19200)) * 32)); // calculate fraction            usDivider = (BUS_CLOCK/16/19200);                            // set 19200            break;        case SERIAL_BAUD_38400:            ucFraction = (unsigned char)((float)((((float)BUS_CLOCK/(float)16/(float)38400) - (int)(BUS_CLOCK/16/38400)) * 32)); // calculate fraction            usDivider = (BUS_CLOCK/16/38400);                            // set 38400            break;        case SERIAL_BAUD_57600:            ucFraction = (unsigned char)((float)((((float)BUS_CLOCK/(float)16/(float)57600) - (int)(BUS_CLOCK/16/57600)) * 32)); // calculate fraction            usDivider = (BUS_CLOCK/16/57600);                            // set 57600            break;        case SERIAL_BAUD_115200:            ucFraction = (unsigned char)((float)((((float)BUS_CLOCK/(float)16/(float)115200) - (int)(BUS_CLOCK/16/115200)) * 32)); // calculate fraction            usDivider = (BUS_CLOCK/16/115200);                           // set 115200            break;        case SERIAL_BAUD_230400:            ucFraction = (unsigned char)((float)((((float)BUS_CLOCK/(float)16/(float)230400) - (int)(BUS_CLOCK/16/230400)) * 32)); // calculate fraction            usDivider = (BUS_CLOCK/16/230400);                           // set 230400            break;        }    #if SYSTEM_CLOCK != BUS_CLOCK    }    #endif    uart_reg->UART_C4  = ucFraction;    uart_reg->UART_BDH = (unsigned char)((usDivider >> 8) & 0x1f);       // program speed to divisor latch registers    uart_reg->UART_BDL = (unsigned char)usDivider;                       // the complete baud rate generator setting is committed when this byte is written    if (pars->Config & CHAR_7) {                                         // Kinetis supports only one fixed stop bit        ucUART_mask[Channel] = 0x7f;                                     // set 7 bit mask to remove parity bits        if (pars->Config & (RS232_ODD_PARITY | RS232_EVEN_PARITY)) {            if (pars->Config & RS232_ODD_PARITY) {                uart_reg->UART_C1 = (UART_C1_PE | UART_C1_PT_ODD);       // program odd parity            }            else {                uart_reg->UART_C1 = (UART_C1_PE | UART_C1_PT_EVEN);      // program even parity            }        }        else {            uart_reg->UART_C1 = UART_C1_PARITY_DISABLED;                 // no parity - the UART in the Kinetis will use address mark at the 8th bit position in 7 bit character mode without a parity setting        }    }    else {        ucUART_mask[Channel] = 0xff;                                     // full 8 bit mask        if (pars->Config & (RS232_ODD_PARITY | RS232_EVEN_PARITY)) {            if (pars->Config & RS232_ODD_PARITY) {                uart_reg->UART_C1 = (UART_C1_M | UART_C1_PE | UART_C1_PT_ODD); // program odd parity            }            else {                uart_reg->UART_C1 = (UART_C1_M | UART_C1_PE | UART_C1_PT_EVEN); // program even parity            }        }        else {            uart_reg->UART_C1 = UART_C1_PARITY_DISABLED;                 // no parity - the UART in the Kinetis will use address mark at the 8th bit position in 7 bit character mode without a parity setting        }    }    #ifdef SERIAL_SUPPORT_DMA                                            // {6}    if (pars->ucDMAConfig & UART_TX_DMA) {        KINETIS_DMA_TDC *ptrDMA_TCD = (KINETIS_DMA_TDC *)eDMA_DESCRIPTORS;        ptrDMA_TCD += UART_DMA_TX_CHANNEL[Channel];        ptrDMA_TCD->DMA_TCD_SOFF = 1;                                    // source increment one byte        ptrDMA_TCD->DMA_TCD_DOFF = 0;                                    // destination not incremented      //_SET_DMA_CHANNEL_PRIORITY(UART_DMA_TX_CHANNEL[Channel], UART_DMA_TX_PRIORITY[Channel]); // DMA priority, whereby channel can suspend a lower priority channel (not changed since we use the channel's inherent priority)        ptrDMA_TCD->DMA_TCD_ATTR = (DMA_TCD_ATTR_DSIZE_8 | DMA_TCD_ATTR_SSIZE_8); // transfer sizes always single bytes        ptrDMA_TCD->DMA_TCD_DADDR = (unsigned long)&(uart_reg->UART_D);  // destination is the UART's data register        ptrDMA_TCD->DMA_TCD_NBYTES_ML = 1;                               // each request starts a single transfer        ptrDMA_TCD->DMA_TCD_CSR = (DMA_TCD_CSR_DREQ | DMA_TCD_CSR_INTMAJOR); // stop after the defined number of service requests and interrupt on completion        fnEnterInterrupt((irq_DMA0_ID + UART_DMA_TX_CHANNEL[Channel]), UART_DMA_TX_INT_PRIORITY[Channel], (void (*)(void))_uart_tx_dma_Interrupt[Channel]); // enter DMA interrupt handler        uart_reg->UART_C5 |= UART_C5_TDMAS;                              // use DMA rather than interrupts for transmission        POWER_UP(6, SIM_SCGC6_DMAMUX0);                                  // enable DMA multiplexer 0        *(unsigned char *)(DMAMUX0_BLOCK + UART_DMA_TX_CHANNEL[Channel]) = ((DMAMUX_CHCFG_SOURCE_UART0_TX + (2 * Channel)) | DMAMUX_CHCFG_ENBL); // connect UART tx to DMA channel        uart_reg->UART_C2 |= (UART_C2_TIE);                              // enable the tx dma request (DMA not yet enabled) rather than interrupt mode    }    else {        uart_reg->UART_C5 &= ~(UART_C5_TDMAS);                           // disable tx DMA so that tx interrupt mode can be used    }        #if defined SERIAL_SUPPORT_DMA_RX    if (pars->ucDMAConfig & UART_RX_DMA) {                               // {8}        KINETIS_DMA_TDC *ptrDMA_TCD = (KINETIS_DMA_TDC *)eDMA_DESCRIPTORS;        ptrDMA_TCD += UART_DMA_RX_CHANNEL[Channel];        fnEnterInterrupt((irq_DMA0_ID + UART_DMA_RX_CHANNEL[Channel]), UART_DMA_RX_INT_PRIORITY[Channel], (void (*)(void))_uart_rx_dma_Interrupt[Channel]); // enter DMA interrupt handler        uart_reg->UART_C5 |= UART_C5_RDMAS;                              // use DMA rather than interrupts for reception        POWER_UP(6, SIM_SCGC6_DMAMUX0);                                  // enable DMA multiplexer 0        *(unsigned char *)(DMAMUX0_BLOCK + UART_DMA_RX_CHANNEL[Channel]) = ((DMAMUX_CHCFG_SOURCE_UART0_RX + (2 * Channel)) | DMAMUX_CHCFG_ENBL); // connect UART rx to DMA channel        ptrDMA_TCD->DMA_TCD_BITER_ELINK = ptrDMA_TCD->DMA_TCD_CITER_ELINK = pars->Rx_tx_sizes.RxQueueSize; // the length of the input buffer in use        ptrDMA_TCD->DMA_TCD_SOFF = 0;                                    // source not increment        ptrDMA_TCD->DMA_TCD_DOFF = 1;                                    // destination incremented      //_SET_DMA_CHANNEL_PRIORITY(UART_DMA_RX_CHANNEL[Channel], UART_DMA_RX_PRIORITY[Channel]); // DMA priority, whereby channel can suspend a lower priority channel (not changed since we use the channel's inherent priority)        ptrDMA_TCD->DMA_TCD_ATTR = (DMA_TCD_ATTR_DSIZE_8 | DMA_TCD_ATTR_SSIZE_8); // transfer sizes always single bytes        ptrDMA_TCD->DMA_TCD_SADDR = (unsigned long)&(uart_reg->UART_D);  // source is the UART's data register        ptrDMA_TCD->DMA_TCD_NBYTES_ML = 1;                               // each request starts a single transfer        if (pars->ucDMAConfig & UART_RX_DMA_HALF_BUFFER) {               // if operating in half-buffer mode            ptrDMA_TCD->DMA_TCD_CSR = (DMA_TCD_CSR_INTMAJOR | DMA_TCD_CSR_INTHALF); // never disable and inform on half and full buffer        }        else {            ptrDMA_TCD->DMA_TCD_CSR = (DMA_TCD_CSR_INTMAJOR);            // never disable and inform on full buffer only        }        ptrDMA_TCD->DMA_TCD_DLASTSGA = (-pars->Rx_tx_sizes.RxQueueSize); // when the buffer has been filled set the destination back to the start of it    }    else {        uart_reg->UART_C5 &= ~(UART_C5_RDMAS);                           // disable rx DMA so that rx interrupt mode can be used    }        #endif    #endif}

 

0 Kudos
3,027 Views
mjbcswitzerland
Specialist V

 

Plus routine to configure Rx:

 

// Enable reception on the defined channel - including configuring the receive data input//extern void fnRxOn(QUEUE_HANDLE Channel){    KINETIS_UART_CONTROL *uart_reg = fnSelectChannel(Channel);    switch (Channel) {    case 0:                                                              // configure the UART Rx 0 pin    #if defined UART0_A_LOW        _CONFIG_PERIPHERAL(A, 1, PA_1_UART0_RX);                         // UART0_RX on PA1 (alt. function 2)    #elif defined UART0_ON_B        _CONFIG_PERIPHERAL(B, 16, PB_16_UART0_RX);                       // UART0_RX on PB16 (alt. function 3)    #elif defined UART0_ON_D        _CONFIG_PERIPHERAL(D, 6, PD_6_UART0_RX);                         // UART0_RX on PD6 (alt. function 3)    #else        _CONFIG_PERIPHERAL(A, 15, PA_15_UART0_RX);                       // UART0_RX on PA15 (alt. function 3)    #endif        fnEnterInterrupt(irq_UART0_ID, PRIORITY_UART0, _SCI0_Interrupt); // enter UART0 interrupt handler        break;    case 1:                                                              // configure the UART Rx 1 pin    #if defined UART1_ON_C        _CONFIG_PERIPHERAL(C, 3, PC_3_UART1_RX);                         // UART1_RX on PC3 (alt. function 3)    #else        _CONFIG_PERIPHERAL(E, 1, PE_1_UART1_RX);                         // UART1_RX on PE1 (alt. function 3)    #endif        fnEnterInterrupt(irq_UART1_ID, PRIORITY_UART1, _SCI1_Interrupt); // enter UART1 interrupt handler        break;    case 2:                                                              // configure the UART Rx 2 pin    #if defined KINETIS_K70 && defined UART2_ON_E                        // {3}        _CONFIG_PERIPHERAL(E, 17, PE_17_UART2_RX);                       // UART2_RX on PE17 (alt. function 3)    #elif defined KINETIS_K70 && defined UART2_ON_F        _CONFIG_PERIPHERAL(F, 13, PF_13_UART2_RX);                       // UART2_RX on PF13 (alt. function 4)    #else        _CONFIG_PERIPHERAL(D, 2, PD_2_UART2_RX);                         // UART2_RX on PD2 (alt. function 3)    #endif        fnEnterInterrupt(irq_UART2_ID, PRIORITY_UART2, _SCI2_Interrupt); // enter UART2 interrupt handler        break;    #if UARTS_AVAILABLE > 3    case 3:                                                              // configure the UART Rx 3 pin        #if defined UART3_ON_B        _CONFIG_PERIPHERAL(B, 10, PB_10_UART3_RX);                       // UART3_RX on PB10 (alt. function 3)        #elif defined UART3_ON_C        _CONFIG_PERIPHERAL(C, 16, PC_16_UART3_RX);                       // UART3_RX on PC16 (alt. function 3)        #else        _CONFIG_PERIPHERAL(E, 5, PE_5_UART3_RX);                         // UART3_RX on PE5 (alt. function 3)        #endif        fnEnterInterrupt(irq_UART3_ID, PRIORITY_UART3, _SCI3_Interrupt); // enter UART3 interrupt handler        break;    case 4:                                                              // configure the UART Rx 4 pin        #if defined UART4_ON_C        _CONFIG_PERIPHERAL(C, 14, PC_14_UART4_RX);                       // UART4_RX on PC14 (alt. function 3)        #else        _CONFIG_PERIPHERAL(E, 25, PE_25_UART4_RX);                       // UART4_RX on PE25 (alt. function 3)        #endif        fnEnterInterrupt(irq_UART4_ID, PRIORITY_UART4, _SCI4_Interrupt); // enter UART4 interrupt handler        break;    case 5:                                                              // configure the UART Rx 5 pin        #if defined UART5_ON_D        _CONFIG_PERIPHERAL(D, 8, PD_8_UART5_RX);                         // UART5_RX on PD8 (alt. function 3)        #else        _CONFIG_PERIPHERAL(E, 9, PE_9_UART5_RX);                         // UART5_RX on PE9 (alt. function 3)        #endif        fnEnterInterrupt(irq_UART5_ID, PRIORITY_UART5, _SCI5_Interrupt); // enter UART5 interrupt handler        break;    #endif    }    uart_reg->UART_C2 |= (UART_C2_RE | UART_C2_RIE);                     // enable reception and reception interrupt}

 

In the case of DMA operation the DMA is initialised using

 

 

// The Kinetis buffer has been set up to run continuously in circular buffer mode. This routine therefore doesn't use the buffer pointer and length passed//extern void fnPrepareRxDMA(QUEUE_HANDLE channel, unsigned char *ptrStart, QUEUE_TRANSFER rx_length){        #if defined SERIAL_SUPPORT_DMA_RX    KINETIS_UART_CONTROL *uart_reg = fnSelectChannel(channel);    if (!(uart_reg->UART_C2 & (UART_C2_RE))) {                           // if receiver not yet enabled        KINETIS_DMA_TDC *ptrDMA_TCD = (KINETIS_DMA_TDC *)eDMA_DESCRIPTORS;        ptrDMA_TCD += UART_DMA_RX_CHANNEL[channel];        ptrDMA_TCD->DMA_TCD_DADDR = (unsigned long)ptrStart;             // destination is the input tty buffer        DMA_ERQ |= (DMA_ERQ_ERQ0 << UART_DMA_RX_CHANNEL[channel]);       // enable request source        fnRxOn(channel);                                                 // configure receiver pin and enable reception and its interrupt/DMA    }        #endif}

 

Regards

 

Mark

0 Kudos
3,027 Views
konrada
Contributor IV

Thanks again!

 

I tried several permutations of writes to C2, C5 and the DMA enable register, with no change. 

 

Weirdly enough, when I debug, I observe S1.IDLE=1, although S2.RWUID=0 (by the book, IDLE shouldn't be set at all!). However, as C2.ILIE is initialized as 0 and still reads as 0 in the debugger, I don't think the IDLE interrupt causes a second DMA request, although it would somehow fit the picture. The documentation for C2.ILIE refers to non-existing bit C5.ILDMAS, after all.

 

Hmm. 

0 Kudos
3,027 Views
konrada
Contributor IV

I've tried the same on a borrowed TWRK70 with two pieces of wire. When I send from UART5 to UART0, UART0 receives characters twice. When I send from UART0 to UART5, UART5 receives correctly.

 

When I send from UART5 to UART0 and enable UART0's FIFO with RWFIFO=2, then it almost works perfectly, every character is received only once. However, if I send a single character, it waits unreceived in the FIFO until another char comes along, as is to be expected with RWFIFO=2. 

 

Well, I guess there'll be two serial drivers for me, one FIFO and one DMA.

0 Kudos
3,027 Views
rbphilip
Contributor I

Konrad:

As I begin to dig into the joy of DMA and Kinetis UARTs I've found this thread.

Any chance that you could post or message me your complete DMA driver/test code so that I can avoid days of reinventing the wheel?

Thanks...

Rob

0 Kudos
3,027 Views
konrada
Contributor IV

Additional data point: my example code ran on default oscillator settings. When I let it run inside the application where clocks are already set up for 120MHz, DMA reception works fine on UART1.

3,027 Views
mjbcswitzerland
Specialist V

Hi

 

Note that UARTs 0 and 1 are clocked from the system clock and the other UARTs are clocked from the bus clock.

I see no explanation in this for the behaviour seen, but it is maybe worth considering.

 

Regards

 

Mark

 

 

0 Kudos