Arbitrary Length Transmit and Receive - GDMA

This sample uses UART and GDMA to communicate data of arbitrary length with a PC terminal.

The SoC uses GDMA to receive data input from the PC terminal, transferring the data from the UART RX FIFO peripheral to Memory. Meanwhile, in the UART_FLAG_RX_IDLE interrupt, it retrieves the length of data of arbitrary length, processes the data, and sets the received_flag.

Once the received_flag is set, the SoC uses GDMA to transfer the received data from Memory to the UART TX FIFO peripheral, sending it back to the PC terminal.

In this sample, any length of data sent by the PC terminal will receive the same data information back from the SoC.

Users can change the pin configuration, GDMA configuration, and other information in the sample through different macro configurations. For specific macro configurations, see Configurations.

Requirements

For requirements, please refer to the Requirements.

In addition, it is necessary to install serial port assistant tools such as PuTTY or UartAssist on the PC terminal.

Configurations

  1. The following macros can be configured to modify GDMA channel settings.

    // Set the following macros to modify the UART TX GDMA Channel configurations.
    #define UART_TX_GDMA_CHANNEL_NUM            GDMA_CH_NUM3
    #define UART_TX_GDMA_CHANNEL                GDMA_Channel3
    #define UART_TX_GDMA_CHANNEL_IRQN           GDMA_Channel3_IRQn
    #define UART_TX_GDMA_Handler                GDMA_Channel3_Handler
    
    // Set the following macros to modify the UART RX GDMA Channel configurations.
    #define UART_RX_GDMA_CHANNEL_NUM            GDMA_CH_NUM4
    #define UART_RX_GDMA_CHANNEL                GDMA_Channel4
    #define UART_RX_GDMA_CHANNEL_IRQN           GDMA_Channel4_IRQn
    #define UART_RX_GDMA_Handler                GDMA_Channel4_Handler
    
  2. The following macros can be configured to modify the pin definitions.

    #define UART_TX_PIN                         P3_0
    #define UART_RX_PIN                         P3_1
    
  3. The following macros can be configured to modify the Block Size of GDMA RX Channel.

    #define GDMA_BLOCK_SIZE                     5
    

Wiring

Connect P3_0(UART TX Pin) to the RX pin of the FT232 and P3_1(UART RX Pin) to the TX pin of the FT232.

Building and Downloading

For building and downloading, please refer to the Building and Downloading.

Experimental Verification

Preparation Phase

  1. Start a PC terminal like PuTTY or UartAssist and connect to the used COM port with the following UART settings:

  • Baud rate: 115200

  • 8 data bits

  • 1 stop bit

  • No parity

  • No hardware flow control

Testing Phase

  1. After resetting the EVB, observe the log information as shown in the Debug Analyzer.

    Start uart tx rx unfixedlen by gdma test!
    
  2. Type strings on the PC terminal and observe that the same string appears on the PC terminal. Simultaneously, observe that received data and interrupt information appear in the Debug Analyzer. Assuming the input data length on the PC terminal is number, the interrupt log for different cases is as follows:

    GDMA0_Channel4_Handler  /* time = (number / block_size) > 0 triggers time interrupts and print logs. */
    
    UART_FLAG_RX_IDLE       /* time = (number % block_size) > 0 and meeting the UART_FLAG_RX_IDLE interrupt condition triggers time interrupts and print logs. */
    value is 0x..
    value is 0x..
    value is 0x..
    ...
    
    UART_TX_GDMA_Handler    /* Once received_flag is set, the SoC uses GDMA to send number length data back to the PC terminal. Upon completion, it triggers the interrupt and prints logs. */
    

Code Overview

This section introduces the code and process description for initialization and corresponding function implementation in the sample.

Source Code Directory

The directory for project file and source code are as follows:

  • Project directory: sdk\samples\peripheral\uart\unfixedlen_gdma\proj

  • Source code directory: sdk\samples\peripheral\uart\unfixedlen_gdma\src

Initialization

The initialization flow for peripherals can refer to Initialization Flow in General Introduction.

  1. Call Pad_Config() and Pinmux_Config() to configure the PAD and PINMUX of the corresponding pins. If UART_CONFIG_HW_FLOW_CTRL is set to 1, the CTS and RTS pins still need to be configured.

    void board_uart_init(void)
    {
        Pad_Config(UART_TX_PIN, PAD_PINMUX_MODE, PAD_IS_PWRON, PAD_PULL_UP, PAD_OUT_DISABLE, PAD_OUT_HIGH);
        Pad_Config(UART_RX_PIN, PAD_PINMUX_MODE, PAD_IS_PWRON, PAD_PULL_UP, PAD_OUT_DISABLE, PAD_OUT_HIGH);
    
        Pinmux_Config(UART_TX_PIN, UART3_TX);
        Pinmux_Config(UART_RX_PIN, UART3_RX);
    }
    
  2. Call RCC_PeriphClockCmd() to enable the UART clock.

  3. Initialize the UART peripheral:

    1. Define the UART_InitTypeDef type UART_InitStruct, and call UART_StructInit() to pre-fill UART_InitStruct with default values.

    2. Modify the UART_InitStruct parameters as needed. The UART initialization parameter configuration is shown in the table below.

    3. Call UART_Init() to initialize the UART peripheral.

UART Initialization Parameters

UART Hardware Parameters

Setting in the UART_InitStruct

UART

UART Baudrate Parameter - div

UART_InitTypeDef::UART_Div

BaudRate_Table[BAUD_RATE_115200].div

UART Baudrate Parameter - ovsr

UART_InitTypeDef::UART_Ovsr

BaudRate_Table[BAUD_RATE_115200].ovsr

UART Baudrate Parameter - ovsr_adj

UART_InitTypeDef::UART_OvsrAdj

BaudRate_Table[BAUD_RATE_115200].ovsr_adj

GDMA Enable

UART_InitTypeDef::UART_DmaEn

ENABLE

TX GDMA Enable

UART_InitTypeDef::UART_TxDmaEn

ENABLE

RX GDMA Enable

UART_InitTypeDef::UART_RxDmaEn

ENABLE

TX Waterlevel

UART_InitTypeDef::UART_TxWaterLevel

1

RX Waterlevel

UART_InitTypeDef::UART_RxWaterLevel

1

  1. Configure UART receive idle interrupt UART_INT_RX_IDLE and line status interrupt UART_INT_LINE_STS, and configure NVIC. For NVIC related configuration, refer to Interrupt Configuration.

  2. Initialize the GDMA peripheral:

    1. Define a GDMA_InitTypeDef type GDMA_InitStruct, and call GDMA_StructInit() to pre-fill GDMA_InitStruct with default values.

    2. Modify the GDMA_InitStruct parameters as needed. The initialization parameters for the GDMA TX and RX channels are configured as shown in the table below. Call GDMA_Init() to initialize the GDMA peripheral.

    3. Configure the GDMA total transfer complete interrupt GDMA_INT_Transfer and NVIC. For NVIC configurations, refer to Interrupt Configuration.

    4. Call GDMA_Cmd() to enable the corresponding GDMA channel transfer.

GDMA Initialization Parameters

GDMA Hardware Parameters

Setting in the GDMA_InitStruct

GDMA TX Channel

GDMA RX Channel

Channel Num

GDMA_InitTypeDef::GDMA_ChannelNum

3

4

Transfer Direction

GDMA_InitTypeDef::GDMA_DIR

GDMA_DIR_MemoryToPeripheral

GDMA_DIR_PeripheralToMemory

Buffer Size

GDMA_InitTypeDef::GDMA_BufferSize

-

GDMA_BLOCK_SIZE

Source Address Increment or Decrement

GDMA_InitTypeDef::GDMA_SourceInc

DMA_SourceInc_Inc

DMA_SourceInc_Fix

Destination Address Increment or Decrement

GDMA_InitTypeDef::GDMA_DestinationInc

DMA_DestinationInc_Fix

DMA_DestinationInc_Inc

Source Data Size

GDMA_InitTypeDef::GDMA_SourceDataSize

GDMA_DataSize_Byte

GDMA_DataSize_Byte

Destination Data Size

GDMA_InitTypeDef::GDMA_DestinationDataSize

GDMA_DataSize_Byte

GDMA_DataSize_Byte

Source Burst Transaction Length

GDMA_InitTypeDef::GDMA_SourceMsize

GDMA_Msize_1

GDMA_Msize_1

Destination Burst Transaction Length

GDMA_InitTypeDef::GDMA_DestinationMsize

GDMA_Msize_1

GDMA_Msize_1

Source Address

GDMA_InitTypeDef::GDMA_SourceAddr

GDMA_Tx_Buf

(&(UART_DEMO->UART_RBR_THR))

Destination Address

GDMA_InitTypeDef::GDMA_DestinationAddr

(&(UART_DEMO->UART_RBR_THR))

GDMA_Rx_Buf

Source Handshake

GDMA_InitTypeDef::GDMA_SourceHandshake

-

GDMA_Handshake_UART3_RX

Destination Handshake

GDMA_InitTypeDef::GDMA_DestHandshake

GDMA_Handshake_UART3_TX

-

Functional Implementation

The flow for UART to send and receive data of arbitrary length via GDMA is shown in the diagram:

Here should be UART TRX unfixedlen GDMA flow

UART GDMA transmitting and receiving data of arbitrary Length flow

After enabling the GDMA RX channel transmission, when characters are input at the PC terminal, the GDMA RX channel moves the data from (&(UART_DEMO->UART_RBR_THR)) to GDMA_Rx_Buf. Assuming the input data length on the PC terminal is number, the interrupt for different cases is as follows:

  1. If number / GDMA_BLOCK_SIZE > 0, meaning the length of data received by UART is greater than GDMA_BLOCK_SIZE, a GDMA RX interrupt will be triggered every time GDMA_BLOCK_SIZE amount of data is received, triggering a total of number / block_size times. The data received in the GDMA interrupt is saved to GDMA_Tx_Buf, and the GDMA RX channel is re-enabled for continued reception.

    void GDMA0_Channel4_Handler(void)
    {
        DBG_DIRECT("GDMA0_Channel4_Handler");
    
        /*  Clear interrupt */
        GDMA_Cmd(UART_RX_GDMA_CHANNEL_NUM, DISABLE);
        GDMA_ClearAllTypeINT(UART_RX_GDMA_CHANNEL_NUM);
        receive_offset += GDMA_BLOCK_SIZE;
        count += 1;
    
        ...
        memcpy(GDMA_Tx_Buf + GDMA_BLOCK_SIZE * (count - 1), GDMA_Rx_Buf, GDMA_BLOCK_SIZE);
    
        GDMA_ClearINTPendingBit(UART_RX_GDMA_CHANNEL_NUM, GDMA_INT_Transfer);
        /*  reset gdma param    */
        GDMA_SetDestinationAddress(UART_RX_GDMA_CHANNEL, (uint32_t)GDMA_Rx_Buf);
        GDMA_Cmd(UART_RX_GDMA_CHANNEL_NUM, ENABLE);
    }
    
  2. If number / GDMA_BLOCK_SIZE = 0, since the amount of data transferred is insufficient, no GDMA RX interrupt will be triggered. Once all data in the UART RX FIFO is moved, if no data is placed in the RX FIFO within the idle timeout period, the UART_INT_RX_IDLE interrupt will be triggered.

    1. Pause the UART_RX_GDMA_CHANNEL channel transmission and disable the UART_INT_RX_IDLE interrupt.

    2. Obtain the data transfer amount for this GDMA and save the received data to GDMA_Tx_Buf.

    3. Record the length of data received receive_offset and set receiveflg to true, indicating data reception is complete.

    4. Reinitialize the GDMA RX channel, clear the UART FIFO, and re-enable the UART_INT_RX_IDLE interrupt for the next round of data transfer reception.

    void UART3_Handler(void)
    {
        ...
        if (UART_GetFlagState(UART3, UART_FLAG_RX_IDLE) == SET)
        {
            /*  Suspend GDMA_Channel2   */
            GDMA_SuspendCmd(UART_RX_GDMA_CHANNEL, ENABLE);
            UART_INTConfig(UART3, UART_INT_RX_IDLE, DISABLE);
            data_len = GDMA_GetTransferLen(UART_RX_GDMA_CHANNEL);
            ...
    
            if (data_len)
            {
                receive_offset += data_len;
                memcpy(GDMA_Tx_Buf + GDMA_BLOCK_SIZE * count, GDMA_Rx_Buf, data_len);
    
                ...
                GDMA_Cmd(UART_RX_GDMA_CHANNEL_NUM, DISABLE);
                GDMA_SuspendCmd(UART_RX_GDMA_CHANNEL, DISABLE);
                driver_gdma4_init();
                /* GDMA TX flag */
                receiveflg = true;
            }
            /* Run here if data length = N * GDMA_BLOCK_SIZE,  */
            else
            {
                GDMA_SuspendCmd(UART_RX_GDMA_CHANNEL, DISABLE);
                receiveflg = true;
            }
            UART_ClearRxFIFO(UART3);
            UART_INTConfig(UART3, UART_INT_RX_IDLE, ENABLE);
        }
    
        ...
    }
    
  3. Once the received_flag is set to true, call GDMA_SetBufferSize() to update the GDMA TX Block Size, call GDMA_Cmd() to enable GDMA TX channel transmission, and GDMA will transfer data from GDMA_Tx_Buf to the UART TX FIFO.

    while (1)
    {
        if (receiveflg)
        {
            GDMA_SetBufferSize(UART_TX_GDMA_CHANNEL, receive_offset);
            GDMA_Cmd(UART_TX_GDMA_CHANNEL_NUM, ENABLE);
            ...
        }
    }
    
  4. After GDMA TX transmission is complete, the GDMA_INT_Transfer interrupt will be triggered. Once UART data transmission is complete, information can be seen on the PC terminal.