This thread has been locked.

If you have a related question, please click the "Ask a related question" button in the top right corner. The newly created question will be automatically linked to this question.

[参考译文] TMS320F28388D:在接收到所有预期数据之前、UDMA 已完成对 UART 的读取

Guru**** 2615265 points
请注意,本文内容源自机器翻译,可能存在语法或其它翻译错误,仅供参考。如需获取准确内容,请参阅链接中的英语原文或自行翻译。

https://e2e.ti.com/support/microcontrollers/c2000-microcontrollers-group/c2000/f/c2000-microcontrollers-forum/1052560/tms320f28388d-udma-finished-reading-uart-before-all-expected-data-received

器件型号:TMS320F28388D

I am currently developing the UART communication on the CM core with the usage of the uDMA.

  • ARM 编译器  版本18.12.5
  • F2838x 支持库 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);
    }

中断处理程序如下所示:

  __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
	        // …

        }
    }

附加说明

如果我使用 optlevel=off 编译代码、代码会更慢。 这会导致 uDMA 从这里开始的工作时间较晚。 这意味着当完整的帧已经存在时、UDMA 开始从 FIFO 中收集数据。 在本例中、它可以工作、但它不具有可调性、因为我必须快速、并且帧可以比示例中大得多。


  • 请注意,本文内容源自机器翻译,可能存在语法或其它翻译错误,仅供参考。如需获取准确内容,请参阅链接中的英语原文或自行翻译。

    您好!

    看起来、即使在 FIFO 中接收到数据之前、UDMA 也会开始收集数据、因此它会读取已失效的数据。 通常、只有当所有数据都在 DMA 将读取的 FIFO 中时、才会触发 DMA。 您可以延迟 DMA 触发器或检查 RXFF 位状态以查看 FIFO 是否已满、然后启动 DMA。  

    此致、

    Vivek Singh

  • 请注意,本文内容源自机器翻译,可能存在语法或其它翻译错误,仅供参考。如需获取准确内容,请参阅链接中的英语原文或自行翻译。

    这意味着 UDMA 接收的数据不能超过 FIFO 的大小? 如果我的数据大于 FIFO 大小、该怎么办? 我有高达100字节的帧。

    您所喜欢的方式是:当 FIFO 已满时启动 UDMA、使用 UDMA 读取数据 、直到 FIFO 为空。  我重复一下、直到接收到所有数据。 我看到 该解决方案存在多个问题。 我无法计算接收到的数据、也不知道帧何时完成。   

    另一个问题是:如果我重新启动并配置 UDMA、并且每16个字节分析一次数据、我认为我不会像接收有关 UART driverlib 函数的所有数据和自己处理逻辑那样快。

    我 的主要目标是:我希望通过使用 uDMA、可以将处理器工作负载降低相同或更好的性能。

  • 请注意,本文内容源自机器翻译,可能存在语法或其它翻译错误,仅供参考。如需获取准确内容,请参阅链接中的英语原文或自行翻译。

    通常 情况下、它的工作方式是对 DMA 触发器进行编程、当 DMA 接收到触发器时、它将执行所需的传输、在下一次触发时、它将再次执行传输、并且它将继续直到完全传输(未编程的传输)完成。 但在这里、您不使用触发器、而是通过 SW 启动传输、因此出现了这个问题。