GD32L23X USART+DMA idle interrupt to send and receive data

GD32L23X USART + DMA idle interrupt to send and receive data

  • DMA + USART1 sends and receives data
  • Idle interrupt function and macro definition
  • USART1
    • Understanding of hardware flow control concepts (need to add two more lines)
  • DMA
    • DMA direct memory access controller
    • DMAMUX basic functions
  • printf redefinition

DMA + USART1 sends and receives data

Idle interrupt function and macro definition

#define USART1_RDATA_ADDRESS ( & amp;USART_RDATA(USART1))
#define USART1_TDATA_ADDRESS ( & amp;USART_TDATA(USART1))
#define ARRAYNUM(arr_nanme) (uint32_t)(sizeof(arr_nanme) / sizeof(*(arr_nanme)))
#define USART_TX_RX_BUFF_LEN 256
#define USART_DMA_IDLE 0
#define USART_DMA_BUSY 1

#pragma pack(1)
typedef struct
{<!-- -->
    uint8_t dma_tx_buf[USART_TX_RX_BUFF_LEN];
    uint8_t dma_rx_buf[USART_TX_RX_BUFF_LEN];
    uint16_t dma_rev_cnt;
} USART_DataInfo;
USART_DataInfo usart_DataInfo;

static void com_usart_init(uint32_t baudrate);
static void dma_usart_init(void);

static dma_parameter_struct dma_init_struct;
uint8_t rxdata[USART_TX_RX_BUFF_LEN];

void USART1_Init(uint32_t baudrate)
{<!-- -->
    // initialize the com
    com_usart_init(baudrate);
    // configure DMA ch0 and ch1
    dma_usart_init();
}
void USART1_IRQHandler(void)
{<!-- -->
    if(RESET != usart_interrupt_flag_get(USART1, USART_INT_FLAG_IDLE))
    {<!-- -->
        // clear flag
        usart_interrupt_flag_clear(USART1, USART_INT_FLAG_IDLE);
        //disable channel
        dma_channel_disable(DMA_CH1);

        // read
        usart_DataInfo.dma_rev_cnt = sizeof(usart_DataInfo.dma_rx_buf) - dma_transfer_number_get(DMA_CH1);
        memcpy(rxdata, usart_DataInfo.dma_rx_buf, usart_DataInfo.dma_rev_cnt);
        memset(usart_DataInfo.dma_rx_buf, 0, sizeof(usart_DataInfo.dma_rx_buf));

        // reset DMA configure
        dma_channel_enable(DMA_CH1);
    }
}

USART1

Configure 8N1 LSB interrupt IRQ priority 0, no hardware flow control

Understanding the concept of hardware flow control (need to add two more lines)

Commonly used hardware flow controls include RTS/CTS flow control and DTR/DSR (data terminal ready/data set ready) flow control.
RTS (Require ToSend, send request) is an output signal, used to indicate that the device is ready to receive data, low level is active, low level indicates that the device can receive data.
CTS (Clear ToSend, send permission) is an input signal, used to determine whether data can be sent to the other party. Low level is effective. Low level indicates that the device can send data to the other party.

static void com_usart_init(uint32_t baudrate)
{<!-- -->
    /* enable USART clock */
    rcu_periph_clock_enable(RCU_USART1);

    /* enable COM GPIO clock */
    rcu_periph_clock_enable(RCU_GPIOA);

    /* connect port to USART TX */
    gpio_af_set(GPIOA, GPIO_AF_7, GPIO_PIN_2);

    /* connect port to USART RX */
    gpio_af_set(GPIOA, GPIO_AF_7, GPIO_PIN_3);

    /* configure USART TX as alternate function push-pull */
    gpio_mode_set(GPIOA, GPIO_MODE_AF, GPIO_PUPD_PULLUP, GPIO_PIN_2);
    gpio_output_options_set(GPIOA, GPIO_OTYPE_PP, GPIO_OSPEED_10MHZ, GPIO_PIN_2);

    /* configure USART RX as alternate function push-pull */
    gpio_mode_set(GPIOA, GPIO_MODE_AF, GPIO_PUPD_PULLUP, GPIO_PIN_3);
    gpio_output_options_set(GPIOA, GPIO_OTYPE_PP, GPIO_OSPEED_10MHZ, GPIO_PIN_3);

    /* USART configure */
    usart_deinit(USART1);
    usart_baudrate_set(USART1, baudrate);
    usart_word_length_set(USART1, USART_WL_8BIT);
    usart_parity_config(USART1, USART_PM_NONE);
    usart_stop_bit_set(USART1, USART_STB_1BIT);
    usart_hardware_flow_rts_config(USART1, USART_RTS_DISABLE);
    usart_hardware_flow_cts_config(USART1, USART_CTS_DISABLE);
    usart_data_first_config(USART1, USART_MSBF_LSB);
    usart_receive_config(USART1, USART_RECEIVE_ENABLE);
    usart_transmit_config(USART1, USART_TRANSMIT_ENABLE);
    /*USART enable*/
    usart_interrupt_enable(USART1, USART_INT_IDLE);
    nvic_irq_enable(USART1_IRQn, 0);
    usart_enable(USART1);
}

DMA

DMA direct memory access controller

It allows direct data transmission between peripherals and memory without the involvement of the CPU. When the serial port detects data input, let the DMA directly move the received UART data to the memory we have defined. In this process , the CPU is still processing its own affairs. Finally, after the transmission of one frame of data is completed, it tells the main task through an interrupt that the transmission of one frame of data has been completed, and the main task processes the received data accordingly. During the entire process, the program code will only trigger one interrupt, which is the interrupt that is notified to the CPU after the DMA has moved the data. Compared with the previous method, every time a byte is received, an interrupt is required to read the data into a custom FIFO. In the second method, no matter how large the data volume of a frame is, we will only trigger an interrupt.

DAMUX basic functions

The first block is the DMA request router [Request Multiplexer]. Its core function is to implement routing and forwarding of DMA requests. Its component unit is the DMA request routing channel, and the request router is composed of multiple request routing channels.
The second block is the DMA request generator [RequestGenerator]. Its core function is to generate DMA requests.
Compared with other microcontrollers, GD32L23X has the DMAMUX function, and DMA is no longer bound to peripherals.

static void dma_usart_init(void)
{<!-- -->
    dma_parameter_struct dma_init_struct;
    /* initialize DMA channel 2 */
    rcu_periph_clock_enable(RCU_DMA);
    dma_deinit(DMA_CH2);
    dma_struct_para_init( & amp;dma_init_struct);
    dma_init_struct.request = DMA_REQUEST_USART1_TX;
    dma_init_struct.direction = DMA_MEMORY_TO_PERIPHERAL;
    dma_init_struct.memory_addr = NULL;
    dma_init_struct.memory_inc = DMA_MEMORY_INCREASE_ENABLE;
    dma_init_struct.memory_width = DMA_MEMORY_WIDTH_8BIT;
    dma_init_struct.number = 0;
    dma_init_struct.periph_addr = (uint32_t)USART1_TDATA_ADDRESS;
    dma_init_struct.periph_inc = DMA_PERIPH_INCREASE_DISABLE;
    dma_init_struct.periph_width = DMA_PERIPHERAL_WIDTH_8BIT;
    dma_init_struct.priority = DMA_PRIORITY_ULTRA_HIGH;
    dma_init(DMA_CH2, & amp;dma_init_struct);

    /* configure DMA mode */
    dma_circulation_disable(DMA_CH2);
    dma_memory_to_memory_disable(DMA_CH2);

    /* disable the DMAMUX_MUXCH2 synchronization mode */
    dmamux_synchronization_disable(DMAMUX_MUXCH2);

    /* USART DMA enable for transmission and reception */
    usart_dma_transmit_config(USART1, USART_TRANSMIT_DMA_ENABLE);

    /* initialize DMA channel 1 */
    dma_deinit(DMA_CH1);
    dma_struct_para_init( & amp;dma_init_struct);
    dma_init_struct.request = DMA_REQUEST_USART1_RX;
    dma_init_struct.direction = DMA_PERIPHERAL_TO_MEMORY;
    dma_init_struct.memory_addr = (uint32_t) & amp;usart_DataInfo.dma_rx_buf[0];
    dma_init_struct.memory_inc = DMA_MEMORY_INCREASE_ENABLE;
    dma_init_struct.memory_width = DMA_MEMORY_WIDTH_8BIT;
    dma_init_struct.number = ARRAYNUM(usart_DataInfo.dma_rx_buf);
    dma_init_struct.periph_addr = (uint32_t)USART1_RDATA_ADDRESS;
    dma_init_struct.periph_inc = DMA_PERIPH_INCREASE_DISABLE;
    dma_init_struct.periph_width = DMA_PERIPHERAL_WIDTH_8BIT;
    dma_init_struct.priority = DMA_PRIORITY_ULTRA_HIGH;
    dma_init(DMA_CH1, & dma_init_struct);

    /* configure DMA mode */
    dma_circulation_disable(DMA_CH1);
    dma_memory_to_memory_disable(DMA_CH1);
    /* disable the DMAMUX_MUXCH1 synchronization mode */
    dmamux_synchronization_disable(DMAMUX_MUXCH1);
    /* USART DMA enable for reception */
    usart_dma_receive_config(USART1, USART_RECEIVE_DMA_ENABLE);
}

printf redefinition

Add \ r\ n at the end of newline in Xcom

/* retarget the C library printf function to the USART */
int fputc(int ch, FILE* f)
{<!-- -->
    usart_data_transmit(USART1, (uint8_t)ch);
    while(RESET == usart_flag_get(USART1, USART_FLAG_TBE))
        ;
    return ch;
}