[GD32L233C-START Review] 4. USART uses idle interrupts and DMA to receive data of variable length
[Copy link]
For serial port data reception, there are many occasions where it is necessary to receive data of indefinite length. It would be very troublesome to receive indefinite length data only through receiving interrupt.
Fortunately, the USART of GD32's L233 MCU provides an idle interrupt function.
Idle interrupt combined with DMA reception is a very classic method for receiving variable-length data, and it is also a very basic and commonly used method.
The basic idea:
1. Ensure that a frame of data is received through idle interrupt
2. Get the length of the received data through DMA.
3. Get data of specified length from the buffer for application processing
Regarding DMA, it may be a little different from what we used before. I didn’t find a correspondence table between peripherals and DMA channels.
Directly use DMA channel 1 as the receiving channel of USART and find that it can be used normally.
The example program also provides an example of combining DMA in idle mode, and the example program uses USART1.
With a little modification, it can be changed to other USARTs.
This time I will use USART0 to implement the DMA receiving function in idle.
USART0 is directly brought out through the UART to USB interface on the development board hardware, and only one USB cable is needed to communicate with the PC.
The pins used are PA9 (TX), PA10 (RX)
as follows:
The program is mainly divided into several parts:
1. Initialization of usart0: pin configuration, enable sending and receiving, enable idle interrupt, etc.;
2. Receive DMA configuration
3. Configuration of idle interrupt
4. Implementation of interrupt handling function of usart0
details as follows:
1. Initialization of usart0:
void uart0_init(void)
{
nvic_irq_enable(USART0_IRQn, 0);
/* enable GPIO clock */
rcu_periph_clock_enable(RCU_GPIOA);
/* enable USART clock */
rcu_periph_clock_enable(RCU_USART0);
/* connect port to USART TX */
gpio_af_set(GPIOA, GPIO_AF_7, GPIO_PIN_9);
/* connect port to USART RX */
gpio_af_set(GPIOA, GPIO_AF_7, GPIO_PIN_10);
/* configure USART TX as alternate function push-pull */
gpio_mode_set(GPIOA, GPIO_MODE_AF, GPIO_PUPD_PULLUP, GPIO_PIN_9);
gpio_output_options_set(GPIOA, GPIO_OTYPE_PP, GPIO_OSPEED_10MHZ, GPIO_PIN_9);
/* configure USART RX as alternate function push-pull */
gpio_mode_set(GPIOA, GPIO_MODE_AF, GPIO_PUPD_PULLUP, GPIO_PIN_10);
gpio_output_options_set(GPIOA, GPIO_OTYPE_PP, GPIO_OSPEED_10MHZ, GPIO_PIN_10);
/* USART configure */
usart_deinit(USART0);
usart_baudrate_set(USART0, 115200U);
usart_receive_config(USART0, USART_RECEIVE_ENABLE);
usart_transmit_config(USART0, USART_TRANSMIT_ENABLE);
usart_dma_receive_config(USART0, USART_DENR_ENABLE);
//usart_dma_transmit_config(USART1, USART_DENT_ENABLE);
usart_enable(USART0);
}
2. Receive DMA configuration:
void uart0_dma_init(void)
{
dma_parameter_struct dma_init_struct;
/* enable DMA clock */
rcu_periph_clock_enable(RCU_DMA);
/* initialize DMA channel 1 */
dma_deinit(DMA_CH1);
dma_struct_para_init(&dma_init_struct);
dma_init_struct.request = DMA_REQUEST_USART0_RX;
dma_init_struct.direction = DMA_PERIPHERAL_TO_MEMORY;
dma_init_struct.memory_addr = (uint32_t)usart0_rec_buf;
dma_init_struct.memory_inc = DMA_MEMORY_INCREASE_ENABLE;
dma_init_struct.memory_width = DMA_MEMORY_WIDTH_8BIT;
dma_init_struct.number = UART0_DMA_REC_LEN;
dma_init_struct.periph_addr = (uint32_t)&USART_RDATA(USART0);
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);
/* enable DMA channel 1 */
dma_channel_enable(DMA_CH1);
}
3. Configuration of idle interrupt:
void uart0_idle_init(void)
{
/*wait IDLEF set and clear it*/
while(RESET == usart_flag_get(USART0, USART_FLAG_IDLE)) {
}
usart_flag_clear(USART0, USART_FLAG_IDLE);
usart_interrupt_enable(USART0, USART_INT_IDLE);
}
4. Implementation of interrupt handling function of usart0:
void USART0_IRQHandler(void)
{
if(RESET != usart_interrupt_flag_get(USART0, USART_INT_FLAG_IDLE)){
usart_interrupt_flag_clear(USART0, USART_INT_FLAG_IDLE);
/* number of data received */
Usart0RecLen = UART0_DMA_REC_LEN - (dma_transfer_number_get(DMA_CH1));
memcpy(usart0_rec_pro_buf,usart0_rec_buf,Usart0RecLen);
usart0_rec_comp_flag = 1;
/* disable DMA and reconfigure */
dma_channel_disable(DMA_CH1);
dma_transfer_number_config(DMA_CH1, UART0_DMA_REC_LEN);
dma_channel_enable(DMA_CH1);
}
}
Compile, download, debug:
The serial port assistant sends 1234:
The above tests achieved the expected results!
The source code is as follows:
gd_finger.zip
(606.14 KB, downloads: 73)
|