Part Number: TMS320F28388D
I am
currently developing the UART communication on the CM core with the usage of
the uDMA.
- Arm Compiler Version 18.12.5
- F2838x Support Library v3.04.00.00
I try to
receive data from the RX channel of the UART in a mix between reading out the
FIFO directly and using the uDMA. The reason I do this mix is because i have
different frames with different lenghts. For this purpose, the first 3 bytes (header)
of each frame are read directly from the FIFO with the driverLib. The header describes the information
of the following data. After that I know exactly how much data follow. Now I
pass this task on to the uDMA in order to receive the further data. Here the
udma reports back it has received all the data but it happens too early (not
all data are on the UART already).
Frame:
A frame
consists of a header, data and one CRC byte. The header contains the type of
frame, a counter of the frame and, most importantly, the length of the
following data.
For
this example i have choose a small frame to represent the problem. In
the image i have a logic analyser recording how the example frames look like.
The header is
processed as follows. As soon as the FIFO is filled to 2/8 (UART_FIFO_RX2_8) an interrupt (UART_INT_RX) signals that the header should
be in the FIFO. They are read out with the usage of driverlib and processed.
This code is shown below.
Now I know
exactly how much data has to be received to collect the full frame. This should be done by the uDMA. The uDMA starts to receive data correctly, but reports back a finish much too
early. Not all data of the frame are on the UART at this time. This is shown in the next recording.
According
to the UDMA_ControlTable, the uDMA is also configured as i expect.
The data are
copyed in my buffer. I have partially filled the buffer with 0xFF before, that
can be ignored.
0xC4,
0x00 and 0x09 are received from the FIFO with the driverLib. After this the uDMA
starts work and collect the following bytes. After the 0x02 is received, the uDMA
sends the interrupt (UART_INT_DMATX). In this moment i have a capture of the memory.
The uDMA
fills the buffer with the wrong data until it reaches the dstEndAdress. What is
the reason for it? Can this happen if the rx FIFO is empty in the moment it
want to read?
The
initialization of the UART and the uDMA.
void initUart()
{
SysCtl_enablePeripheral(SYSCTL_PERIPH_CLK_UART0);
UART_setConfig(UART0_BASE, CM_CLK_FREQ , 3125000, (UART_CONFIG_WLEN_8 | UART_CONFIG_STOP_ONE | UART_CONFIG_PAR_EVEN));
UART_enableModule(UART0_BASE);
UART_enableFIFO(UART0_BASE);
// Loop Back disabled
UART_disableLoopback(UART0_BASE);
UART_registerInterrupt(INT_UART0, &sioRxTxIntHandler);
UART_setFIFOLevel(UART0_BASE, UART_FIFO_TX1_8, UART_FIFO_RX2_8);
UART_clearInterruptStatus(UART0_BASE, UART_INT_RX | UART_INT_DMARX | UART_INT_DMATX);
UART_enableInterrupt(UART0_BASE, UART_INT_RX | UART_INT_DMATX);
}
void initDma()
{
SysCtl_enablePeripheral(SYSCTL_PERIPH_CLK_UDMA);
// Enable DMA for Tx and Rx events
//
UART_enableDMA(UART0_BASE, UART_DMA_TX | UART_DMA_RX);
//
// Enable UDMA
//
UDMA_enable(UDMA_BASE);
//
// Point at the control table to use for channel control structures.
//
UDMA_setControlBase(UDMA_BASE, ucControlTable);
UDMA_registerInterrupt(UDMA_ERR_INT, &dmaErrorInteruptHandler);
//
// Put the attributes in a known state for the uDMA software channel.
// These should already be disabled by default.
//
UDMA_disableChannelAttribute(UDMA_BASE, UDMA_CHANNEL_UART0_TX, UDMA_CH_ATTR_ALL);
UDMA_disableChannelAttribute(UDMA_BASE, UDMA_CHANNEL_UART0_RX, UDMA_CH_ATTR_ALL);
UDMA_enableChannelAttribute(UDMA_BASE, UDMA_CHANNEL_UART0_RX, UDMA_CH_ATTR_HIGH_PRIORITY | UDMA_CH_ATTR_USEBURST);
UDMA_setChannelControlParams(UDMA_BASE, (UDMA_CHANNEL_UART0_TX | UDMA_PRI_SELECT),
(UDMA_SIZE_8 | UDMA_SRC_INC_8 | UDMA_DST_INC_NONE
| UDMA_ARB_16));
UDMA_setChannelControlParams(UDMA_BASE, (UDMA_CHANNEL_UART0_RX | UDMA_PRI_SELECT),
(UDMA_SIZE_8 | UDMA_SRC_INC_NONE | UDMA_DST_INC_8
| UDMA_ARB_16 | UDMA_NEXT_USEBURST));
}
The transfer parameters of the uDMA filled like in code below. I have try the UDMA_MODE_AUTO and the UDMA_MODE_BASIC with the same results.
void rxConfigDMA(uint8_t* rxBufferDma, uint8_t transferSize)
{
UDMA_setChannelTransferParams(UDMA_BASE, (UDMA_CHANNEL_UART0_RX | UDMA_PRI_SELECT),
(void *)(UART0_BASE + UART_O_DR), rxBufferDma, UDMA_MODE_AUTO,
transferSize);
}
The Interrupt handler looks like:
__attribute__((ramfunc)) __interrupt void sioRxTxIntHandler(void)
{
isrStatusMask = UART_getInterruptStatus(UART0_BASE, UART_MASKED_INT);
// Received Header + Data Byte
// Interrupt called if 4 Byte in FIFO (3 byte Header + 1 Data byte)
if ((isrStatusMask & UART_INT_RX) == UART_INT_RX)
{
UART_clearInterruptStatus(UART0_BASE, UART_INT_RX);
while (UART_isDataAvailable(UART0_BASE))
{
isrByteRead = UART_readCharNonBlocking(UART0_BASE);
// reads data until a new frame is detected ore fifo empty
if (!(stsCtrl.Control.Field.ReceivingFrame)) // only a control flag
{
// DSO2X_FT_CYCLIC_DATA_EXCH == 0xC4
if ((isrByteRead == DSO2X_FT_CYCLIC_DATA_EXCH) || (isrByteRead == DSO2X_FT_ACYCLIC) || (isrByteRead == DSO2X_FT_ERROR_FRAME))
{
// reset my buffer
clearSioBuffer(&DSO2X_RxBuf[stsCtrl.Control.Field.RxWrBufIdx]);
// now a frame is in receiving mode
stsCtrl.Control.Field.ReceivingFrame = 1; // only a flag for control
}
// add received Byte to buffer
if (stsCtrl.Control.Field.ReceivingFrame)
{
DSO2X_RxBuf[stsCtrl.Control.Field.RxWrBufIdx].F.raw[DSO2X_RxBuf[stsCtrl.Control.Field.RxWrBufIdx].ByteCount] = isrByteRead;
DSO2X_RxBuf[stsCtrl.Control.Field.RxWrBufIdx].ByteCount++;
}
// DSO2X_FRAME_HEAD_SIZE = 3
if (DSO2X_RxBuf[stsCtrl.Control.Field.RxWrBufIdx].ByteCount >= DSO2X_FRAME_HEAD_SIZE)
{
// calculate the lenght that should received by the uDMA DSO2X_FRAME_HEAD_SIZE = 3 DSO2X_FRAME_BCC_SIZE = 1
isrDmaReadLenght = DSO2X_RxBuf[stsCtrl.Control.Field.RxWrBufIdx].F.header.BLen + DSO2X_FRAME_HEAD_SIZE - DSO2X_RxBuf[stsCtrl.Control.Field.RxWrBufIdx].ByteCount + DSO2X_FRAME_BCC_SIZE;
if ((isrDmaReadLenght > 0) && ((isrDmaReadLenght + DSO2X_RxBuf[stsCtrl.Control.Field.RxWrBufIdx].ByteCount) < DSO2X_MAX_FRAME_SIZE))
{
if (!UDMA_isChannelEnabled(UDMA_BASE, UDMA_CHANNEL_UART0_RX))
{
UART_disableInterrupt(UART0_BASE, UART_INT_RX);
UART_enableInterrupt(UART0_BASE, UART_INT_DMARX);
UART_enableDMA(UART0_BASE, UART_DMA_RX);
rxConfigDMA(DSO2X_RxBuf[stsCtrl.Control.Field.RxWrBufIdx].F.raw + DSO2X_RxBuf[stsCtrl.Control.Field.RxWrBufIdx].ByteCount, isrDmaReadLenght);
DSO2X_RxBuf[stsCtrl.Control.Field.RxWrBufIdx].ByteCount += isrDmaReadLenght;
UDMA_enableChannel(UDMA_BASE, UDMA_CHANNEL_UART0_RX);
}
break;
}
else
{
stsCtrl.Control.Field.RxWrBufIdx++;
stsCtrl.Control.Field.ReceivingFrame = 0;
}
}
}
}
if ((isrStatusMask & UART_INT_DMARX) == UART_INT_DMARX) // DMA Controller Finished Receiving Frame
{
UART_disableDMA(UART0_BASE, UART_DMA_RX);
// reset interrupt mask
UART_clearInterruptStatus(UART0_BASE, UART_INT_DMARX);
// disable the dma interrupt
UART_disableInterrupt(UART0_BASE, UART_INT_DMARX);
// enable interrupt to receive the next header
UART_enableInterrupt(UART0_BASE, UART_INT_RX);
// here are done some with the received data
// …
}
}
Additional Note
If I compile my code with optlevel=off, the code is slower. That leads to, that the uDMA starts here work later. It means the uDMA begin to collect data from the FIFO when the complete frame already there. In this case it works but it is no oppertunity because i must be fast and the frames can be much bigger as in the example.