Dear Freescale Community
Task to solve:
Kinetis K60 (K60P144M150SF3) has to receive data over UART (hardware flow control) with a high baud rate (576000kbps), data is sent from an i.MX6 with a UART-TX DMA transfer.
Known errata for Kinetis:
- http://www.freescale.com/files/microcontrollers/doc/errata/KINETIS_4N30D.pdf
Suggested workaround according to errata:
"For UARTs without a FIFO (or if the FIFO is disabled): Delay might need to be added between characters on the transmit side in order to allow time for the negation of /RTS to be recognized before the next character is sent."
--> not applicable, the i.MX6 uses a DMA transfer for the UART data.
Problems:
Proposed solution for discussion:
I can see currently a single solution to the problem, which is kind of ugly but would at least guarantee the correct data receiption:
Question:
Is the DMA_TCD_CITER_ELINK the correct register to read the current DMA write position?
pmtmjbcswitzerland Maybe one of you could have a look at my proposed solution?
Solved! Go to Solution.
Michael
The uTasker project includes K60 Rx DMA ring buffer support - either circular as you have suggested as solution, full-buffer or half-buffer interrupt (less useful though). Full source available at:
http://www.utasker.com/forum/index.php?topic=1721.0
Usage: http://www.utasker.com/docs/uTasker/uTaskerUART.PDF
The ring buffer size can be any length (doesn't need to be a modulo length).
With Rx DMA there is little point in HW flow control since the DMA ensures that no reception is lost at the byte level. You just need to ensure that the application polls at a rate that doesn't allow the ring buffer to overflow - the K60 has quite a lot of RAM so the buffer size can be set fairly large to bridge the worst case.
Personnally I have used the method in a communication product using multiple UARTs in the 1.5MBaud region (specifically to route IP data over serial based RF links) and know of no issues (in production for around 3 years and several thousand in the field). In parallel there are Ethernet and USB operations taking place.
Typical usage:
// Configuration // TTYTABLE tInterfaceParameters; // table for passing information to driver tInterfaceParameters.Channel = 0; // set UART channel for serial use tInterfaceParameters.ucSpeed = SERIAL_BAUD_921600; // baud rate tInterfaceParameters.Rx_tx_sizes.RxQueueSize = (4 * 1024); // input buffer size tInterfaceParameters.Rx_tx_sizes.TxQueueSize = (1 * 1024); // output buffer size tInterfaceParameters.Config = (CHAR_8 + NO_PARITY + ONE_STOP + CHAR_MODE); tInterfaceParameters.ucDMAConfig = (UART_TX_DMA | UART_RX_DMA); // activate DMA on transmission and use free-running DMA on rx if ((SerialPortID = fnOpen(TYPE_TTY, ucDriverMode, &tInterfaceParameters)) != NO_ID_ALLOCATED) { // open or change the channel with defined configurations (initially inactive) fnDriver(SerialPortID, (TX_ON | RX_ON), 0); // enable rx and tx } // In polling task // unsigned char ucRxBuffer[RX_BUFFER_SIZE]; unsigned long ulLength = fnRead(SerialPortID, ucRxBuffer, RX_BUFFER_SIZE); // extract max RX_BUFFER_SIZE size from the UART input buffer if (ulLength != 0) { // process ulLength of reception }
Although it would be nice to have some other mechanisms in the HW to signal the DMA progress you shouldn't have any practical issues with this on the K60.
Regards
Mark
Michael
The uTasker project includes K60 Rx DMA ring buffer support - either circular as you have suggested as solution, full-buffer or half-buffer interrupt (less useful though). Full source available at:
http://www.utasker.com/forum/index.php?topic=1721.0
Usage: http://www.utasker.com/docs/uTasker/uTaskerUART.PDF
The ring buffer size can be any length (doesn't need to be a modulo length).
With Rx DMA there is little point in HW flow control since the DMA ensures that no reception is lost at the byte level. You just need to ensure that the application polls at a rate that doesn't allow the ring buffer to overflow - the K60 has quite a lot of RAM so the buffer size can be set fairly large to bridge the worst case.
Personnally I have used the method in a communication product using multiple UARTs in the 1.5MBaud region (specifically to route IP data over serial based RF links) and know of no issues (in production for around 3 years and several thousand in the field). In parallel there are Ethernet and USB operations taking place.
Typical usage:
// Configuration // TTYTABLE tInterfaceParameters; // table for passing information to driver tInterfaceParameters.Channel = 0; // set UART channel for serial use tInterfaceParameters.ucSpeed = SERIAL_BAUD_921600; // baud rate tInterfaceParameters.Rx_tx_sizes.RxQueueSize = (4 * 1024); // input buffer size tInterfaceParameters.Rx_tx_sizes.TxQueueSize = (1 * 1024); // output buffer size tInterfaceParameters.Config = (CHAR_8 + NO_PARITY + ONE_STOP + CHAR_MODE); tInterfaceParameters.ucDMAConfig = (UART_TX_DMA | UART_RX_DMA); // activate DMA on transmission and use free-running DMA on rx if ((SerialPortID = fnOpen(TYPE_TTY, ucDriverMode, &tInterfaceParameters)) != NO_ID_ALLOCATED) { // open or change the channel with defined configurations (initially inactive) fnDriver(SerialPortID, (TX_ON | RX_ON), 0); // enable rx and tx } // In polling task // unsigned char ucRxBuffer[RX_BUFFER_SIZE]; unsigned long ulLength = fnRead(SerialPortID, ucRxBuffer, RX_BUFFER_SIZE); // extract max RX_BUFFER_SIZE size from the UART input buffer if (ulLength != 0) { // process ulLength of reception }
Although it would be nice to have some other mechanisms in the HW to signal the DMA progress you shouldn't have any practical issues with this on the K60.
Regards
Mark
Thanks Mark, but the codebase of uTasker is huge (11'000 lines of code for kinetis.c).
I guess I found the define (SERIAL_SUPPORT_DMA_RX_FREERUN), that's the mode I am looking for.
I would like to write here a summary of the register configurations, limited to the information what's written in the reference manual.
Configuration: UART4 RX should generate a DMA request on channel 0
Michael
That looks right.
This is the generic code that does the same in the uTasker driver:
Channel = 0..5 |
UART_DMA_RX_CHANNEL[Channel] specifies the DMA channel used by the UART
pars->Rx_tx_sizes.RxQueueSize is the ring buffer size
ptrStart is the start address of the ring buffer
usDMA_progress[] is used later to monitor the DMA progress
KINETIS_DMA_TDC *ptrDMA_TCD = (KINETIS_DMA_TDC *)eDMA_DESCRIPTORS; ptrDMA_TCD += UART_DMA_RX_CHANNEL[Channel]; // set the DMA channel register 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 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 ptrDMA_TCD->DMA_TCD_CSR = 0; usDMA_progress[Channel] = ptrDMA_TCD->DMA_TCD_BITER_ELINK; ptrDMA_TCD->DMA_TCD_SLAST = 0; // no change to address when the buffer has filled 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 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
Regards
Mark