Hi.
In our project we receive data via UART (linflexd1). For this functionality, the MQX UART driver with interrupts is used. With a baud rate of 115200, the 4 byte RX buffer is overflowing , before the RX interrupt can copy the data.
To work around this limitation, I'm using UART RX DMA for receiving the data without using interrupts.
For testing purposes I configured the eDMA to do 4 major iterations, one byte each and generate an end-of-major loop interrupt afterwards. This basically works fine.
However, in about half of the transfers, additional bytes with the value of Zero are copied by the DMA engine. For example, sending 4 bytes "abcd" might be "a", "b", "c", 0x0, "d" in the DMA destination buffer. Sometimes its one Zero byte, sometimes two. Also the position is different each time. The Zero byte also occurs, if no end-of-major loop interrupt is configured. I already did a measurement of the RX line with an oscilloscope/logic analyzer: there are no additional bytes on the wire, which would explain this behaviour.
It seems to me, that sometimes a DMA transfer is triggered, when no new data is in the UART RX FIFO. Thus, a byte with the value zero is transfered.
Has anyone experience with UART RX DMA transfers and/or got it working reliable?
I can also supply the configuration of UART, DMAMUX and eDMA if this helps.
Thanks.
With best regards,
Thomas Huettenhofer
Hi,
UART driver updated to use the eDMA was introduced in MQX 4.1.0, it is for TWR-VF65GS10. However this driver is not fully tested on Kinetis and even not documented in IO user guide. I would suggest you write your own driver for MQX.
Our customer implemented the UART with DMA at register programming level. I think it is very help to you. You can ported to MQX 4.0
Please refer to the following link.
https://community.nxp.com/message/584042?commentID=584042#comment-584042
Have a great day,
Soledad
-----------------------------------------------------------------------------------------------------------------------
Note: If this post answers your question, please click the Correct Answer button. Thank you!
-----------------------------------------------------------------------------------------------------------------------
I'd like to add the configuration of linflexd1 as UART and DMA setup. Maybe this helps, analysing my problem.
Partially this is code from the MQX UART driver, which I extended for testing purposes, partially it is taken from sample code and/or own libraries.
The DMA transfer is working, but I get additional bytes (sometimes) with the value zero in the destination buffer.
Any help is appreciated.
Thanks in advance.
Setup of UART DMA on Channel 4
#define BT_DMA_CHANNEL 4
EDMA.CR.B.ERCA = 1; // Round Robin Channel Arbitration is enabled
uint8_t channel = BT_DMA_CHANNEL; // use DMA channel 4 for UART RX
uint8_t source = 33; // DMA MUX Source Number, See MPC5645S Reference Manual, page 645
/* Configure DMA Channel Mux
*/
DMAMUX.CHCONFIG[channel].R = 0; /* Disable and clear mux channel */
DMAMUX.CHCONFIG[channel].B.SOURCE = source; /* Assign source with channel */
/* Disable and clear EDMA for desired channel
*/
EDMA.CERQ.B.CERQ = BT_DMA_CHANNEL; // Clear enable DMA request
EDMA.CEEI.B.CEEI = BT_DMA_CHANNEL; // Clear enable error interrupt
EDMA.CERR.B.CER = BT_DMA_CHANNEL; // Clear DMA error
EDMA.CDNE.B.CDNE = BT_DMA_CHANNEL; // Clear Done Status bit
EDMA.CPR[channel].R = 0; // Channel priority
struct EDMA_TCD_STD_tag tcd_uart;
memset((void*)&tcd_uart, 0, sizeof(tcd_uart)); // Clear structure
// tcd configuration as described in MPC5645S Reference Manual, page 1032
tcd_uart.SOFF = 0; // no source increment
tcd_uart.SSIZE = 0; // size = 1byte
tcd_uart.DOFF = 1; // byte-wise increment
tcd_uart.DSIZE = 0; // byte-wise transfer
tcd_uart.NBYTESu.B.NBYTES = 1; // minor loop transfer: 1 byte
tcd_uart.D_REQ = 1; // Disable DMA after major iteration finished
tcd_uart.INT_MAJ = 1; // Interrupt after major iteration count finished
tcd_uart.START = 0; // Do not start manually
// Write TCD for channel 4
memcpy((void*)&EDMA.TCD[BT_DMA_CHANNEL], tcd, sizeof(struct EDMA_TCD_STD_tag));
// Enable DMA RX interrupt for DMA channel 4
_int_install_isr(MPXD20_INTC_EDMA_CHANNEL_0_VECTOR + BT_DMA_CHANNEL, bt_dma_rx_interrupt_handler, 0);
_bsp_int_init(MPXD20_INTC_EDMA_CHANNEL_0_VECTOR + BT_DMA_CHANNEL, 4, 0, TRUE);
// calculate source and destination addresses
uint32_t src_addr = (uint32_t) ((char*) &linflexd_ptr->BDRM[3]);
uint32_t dest_addr = (uint32_t)&s_bt_dma_buffer;
uint16_t count = 4; // Major iteration counter: 4.
// Set Source and Destination addresses, major iteration counter (count) and enable channel
EDMA.CDNE.B.CDNE = channel; // Clear DONE status
EDMA.TCD[channel].SADDR = s_address;
EDMA.TCD[channel].DADDR = d_address;
EDMA.TCD[channel].BITER = count;
EDMA.TCD[channel].CITER = count;
EDMA.SERQ.B.SERQ = channel; // Set enable DMA request
DMAMUX.CHCONFIG[channel].B.ENBL = 1; // Enable mux channel
Initialisation of UART1
uartcr = LINFLEXD_UARTCR_UART; // Enable UART Mode
uartcr |= LINFLEXD_UARTCR_RFBM; // Enable FIFO mode for RX buffer (mandatory for DMA)
uartcr |= LINFLEXD_UARTCR_WL0; // UART word length: 8bit
linflexd_ptr = (VLINFLEXD_1_2_3_REG_STRUCT_PTR) io_info_ptr->LINFLEXD_PTR; // Cast linflexd ptr, as linflex 1,2 and 3 structure differs from linflex0
// Save interrupt enable state - reconfigure may be done from polled or interrupt mode driver
linier = linflexd_ptr->LINIER;
/* Go into init mode */
linflexd_ptr->LINCR1 = LINFLEXD_LINCR1_INIT;
linflexd_ptr->LINCR1 = LINFLEXD_LINCR1_INIT;
/* Select UART mode and set parameters */
linflexd_ptr->UARTCR = LINFLEXD_UARTCR_UART;
linflexd_ptr->UARTCR = uartcr;
/* Calculate baud rate */
lfdiv = (io_info_ptr->INIT.CLOCK_SPEED*2/io_info_ptr->INIT.BAUD_RATE + 1)/2;
linflexd_ptr->LINFBRR = lfdiv & 0xf;
linflexd_ptr->LINIBRR = lfdiv>>4;
/* Disable interrupts */
linflexd_ptr->LINIER = 0;
/* Enable Tx and Rx */
uartcr |= LINFLEXD_UARTCR_RXEN | LINFLEXD_UARTCR_TXEN;
linflexd_ptr->UARTCR = uartcr;
// Enable DMA RX Interrupt
linflexd_ptr->DMARXE = 0x0000ffff; // taken from sample code. Channels can't be enabled individually.
/* Leave init mode */
linflexd_ptr->LINCR1 &= ~LINFLEXD_LINCR1_INIT;
/* Restore interrupt enable state */
linflexd_ptr->LINIER = linier;