GD32F303 debugging notes (1) USART (receive interrupt, receive idle interrupt + DMA, send DMA)

Preface

I previously wrote the article USART (receive interrupt, receive idle interrupt + DMA, send DMA) of GD32F103 debugging notes (2) . This time we will take a look at how the USART of GD32F303 is configured. Combining these two articles, I believe everyone will be very familiar with the USART configuration process of GD32.

DMA

It can greatly reduce the CPU load and data porter. It is recommended to configure the DMA function when the scenario is suitable.

USE

A serial communication protocol, there is a lot of information online, so I won’t go into details here.

Programming of each module

Before configuration, please ensure that you already have a GD32F303 Keil project containing its corresponding standard library .
1. Clock configuration

Figure 1.1 GD32F303 peripheral mounting architecture

  • The above figure illustrates which bus the peripheral clock owned by GD32F303 is mounted on. Here we use USART2 , mounted on the APB1 bus.
  • Here we turn on each GPIO port clock, GPIO pin alternate function clock, DMA clock and USART2 clock.
void SystemClock_Reconfig(void)
{
    
    
	/* Enable all peripherals clocks you need*/
	rcu_periph_clock_enable(RCU_AF);
	
	rcu_periph_clock_enable(RCU_GPIOA);
	rcu_periph_clock_enable(RCU_GPIOB);
	rcu_periph_clock_enable(RCU_GPIOC);
	rcu_periph_clock_enable(RCU_GPIOD);
		
	rcu_periph_clock_enable(RCU_DMA0);

	rcu_periph_clock_enable(RCU_USART2);
}

2. Configure GPIO
Please add image description

  • Here I use PC10 and PC11 as the TX and RX of USART2. The code configuration is as follows:
// USART port and pins definition 
#define USART2_PORT						GPIOC
#define USART2_TX_PIN					GPIO_PIN_10
#define USART2_RX_PIN					GPIO_PIN_11

void GPIO_Init(void)
{
    
    
	/* 使用SW下载,不使用JTAG下载,管脚用作其它功能 */
	gpio_pin_remap_config(GPIO_SWJ_SWDPENABLE_REMAP, ENABLE);

	/* demo board USARTx I/O */
	gpio_pin_remap_config(GPIO_USART2_PARTIAL_REMAP, ENABLE);
	gpio_init(USART2_PORT, GPIO_MODE_AF_PP, GPIO_OSPEED_50MHZ,USART2_TX_PIN);
	gpio_init(USART2_PORT, GPIO_MODE_IN_FLOATING, GPIO_OSPEED_50MHZ,USART2_RX_PIN);	
}

3. Configure DMA
Please add image description

  • As can be seen from the above figure, USART2_TX and UASRT2_RX correspond to DMA0_CH1 and DMA0_CH2.
  • Remember the direction of DMA transmission. The DMA channel corresponding to TX is moved from the variable defined in our memory to the transmit register of USART2, while the DMA channel corresponding to RX is moved from the receive register of USART2 to the variable defined in our memory.
  • DMA is a porter. If it is sending and receiving a single byte, it can also be done with the USART's own reception/transmission completion flag. So if DMA is used on USART, it must be the transmission of multiple bytes of data. USARTx_RX_BY_IDLE_DMA is used here to make a macro distinction.
  • Here, the channel corresponding to TX is closed first after initialization is completed, while the channel corresponding to RX can be opened first after initialization is completed. As for why, let me explain it first, and I will explain it in detail below.
/* USARTx Receiving Mode */
#define USARTx_RX_BY_IDLE_DMA	1		//1:使用接收空闲中断+DMA   0:仅使用接收中断

/* USARTx ADrawbuffer definition */
#define USART2_TX_SIZE	20
#define USART2_RX_SIZE	20

uint8_t USART2TX_Buffer[USART2_TX_SIZE] = {
    
    0x11,0x12,0x13,0x14,0x15,0x16,0x17,0x18};
uint8_t USART2RX_Buffer[USART2_RX_SIZE] = {
    
    0};

void DMA_Init(void)
{
    
    
	dma_parameter_struct dma_init_Usart2_TX = {
    
    0};
	dma_parameter_struct dma_init_Usart2_RX = {
    
    0};

	/* deinitialize DMA channel */
	dma_deinit(DMA0, DMA_CH1);									//USART2_TX
	dma_deinit(DMA0, DMA_CH2);									//USART2_RX

	/* initialize DMA0 channel1(Usart2_TX) */					
	dma_init_Usart2_TX.direction = DMA_MEMORY_TO_PERIPHERAL;
	dma_init_Usart2_TX.memory_addr = (uint32_t)USART2TX_Buffer;
	dma_init_Usart2_TX.memory_inc = DMA_MEMORY_INCREASE_ENABLE;
	dma_init_Usart2_TX.memory_width = DMA_MEMORY_WIDTH_8BIT;
	dma_init_Usart2_TX.number = (uint32_t)USART2_TX_SIZE;
	dma_init_Usart2_TX.periph_addr = (uint32_t)(&USART_DATA(USART2));
	dma_init_Usart2_TX.periph_inc = DMA_PERIPH_INCREASE_DISABLE;
	dma_init_Usart2_TX.periph_width = DMA_PERIPHERAL_WIDTH_8BIT;
	dma_init_Usart2_TX.priority = DMA_PRIORITY_LOW;
	dma_init(DMA0, DMA_CH1, &dma_init_Usart2_TX);
	dma_circulation_disable(DMA0, DMA_CH1);
	
	/* initialize DMA0 channel2(Usart2_RX) */					
	dma_init_Usart2_RX.direction = DMA_PERIPHERAL_TO_MEMORY;
	dma_init_Usart2_RX.memory_addr = (uint32_t)USART2RX_Buffer;
	dma_init_Usart2_RX.memory_inc = DMA_MEMORY_INCREASE_ENABLE;
	dma_init_Usart2_RX.memory_width = DMA_MEMORY_WIDTH_8BIT;
	dma_init_Usart2_RX.number = (uint32_t)USART2_RX_SIZE;
	dma_init_Usart2_RX.periph_addr = (uint32_t)(&USART_DATA(USART2));
	dma_init_Usart2_RX.periph_inc = DMA_PERIPH_INCREASE_DISABLE;
	dma_init_Usart2_RX.periph_width = DMA_PERIPHERAL_WIDTH_8BIT;
	dma_init_Usart2_RX.priority = DMA_PRIORITY_LOW;
	dma_init(DMA0, DMA_CH2, &dma_init_Usart2_RX);	
	dma_circulation_disable(DMA0, DMA_CH2);						

	dma_memory_to_memory_disable(DMA0,DMA_CH1);				//USART2_TX
	dma_memory_to_memory_disable(DMA0,DMA_CH2);				//USART2_RX

	/* enable all DMA channels you need */
	dma_channel_disable(DMA0,DMA_CH1);						//USART2_TX
	dma_channel_enable(DMA0,DMA_CH2);						//USART2_RX
}

4. Configure USART2

  • Baud rate 38400, 8 data bits, 1 stop bit, no parity bit, no hardware flow control, receive/transmit enabled, transmit DMA enabled. Whether receive DMA is enabled is determined by the USARTx_RX_BY_IDLE_DMA macro.
void USARTx_Init(void)
{
    
    	
	/* USART2 configure */
	usart_deinit(USART2);
	usart_baudrate_set(USART2, 38400U);
	usart_word_length_set(USART2, USART_WL_8BIT);
	usart_stop_bit_set(USART2, USART_STB_1BIT);
	usart_parity_config(USART2, USART_PM_NONE);
	usart_hardware_flow_rts_config(USART2, USART_RTS_DISABLE);
	usart_hardware_flow_cts_config(USART2, USART_CTS_DISABLE);
	usart_receive_config(USART2, USART_RECEIVE_ENABLE);
	usart_transmit_config(USART2, USART_TRANSMIT_ENABLE);
	usart_enable(USART2);
	
	/* config USARTx_TX transmit by DMA */
	usart_dma_transmit_config(USART2,USART_DENT_ENABLE);

#if USARTx_RX_BY_IDLE_DMA
	usart_dma_receive_config(USART2,USART_DENR_ENABLE);	
#endif
}

5. Configure interrupt priority group and USART2 interrupt

Please add image description

Please add image description

Please add image description

  • Carefully read the USART interrupt description in the F303 application manual, we can understand that RBNE represents every frame of data received from the RX pin, which is set to 1, and we can read the data from the USART receiving register. IDLEF means that after RX has received multiple frames of data, it will be set to 1 when it detects that the line is idle. The USART receiving register can only receive a single frame of data at a time, which means that except for the last frame of data in the multi-frame data, all previous ones are overwritten. (ignored) is lost, so it must cooperate with the data transmission function of DMA to ensure the complete reception of multi-frame data. I think after seeing this, everyone should understand why I use the USARTx_RX_BY_IDLE_DMA macro to make a distinction.
  • Sets the system-wide interrupt priority group. Here, it is decided according to the USARTx_RX_BY_IDLE_DMA macro to only enable the receive interrupt or only enable the receive idle interrupt.
void NVIC_Init(void)
{
    
    
	/* USART2 IRQ set */
	nvic_irq_enable(USART2_IRQn, 2, 0);
#if USARTx_RX_BY_IDLE_DMA	
	usart_interrupt_enable(USART2, USART_INT_IDLE);
#else		
	usart_interrupt_enable(USART2, USART_INT_RBNE);			
#endif
}
  • Write an interrupt service function.
  • If the USARTx_RX_BY_IDLE_DMA macro is true , it chooses to use the receive idle interrupt + DMA method to receive data. We clear this flag bit by reading the corresponding data register (the clearing method described in the manual), and then reset it to the corresponding DMA receiving channel. When the program runs to this point, the DMA channel has acquired one or more bytes of data (depending on how many bytes of data you have sent). Closing and reopening will make the DMA memory buffer start from scratch again (such as the one we defined The USART1RX_Buffer array is the memory buff of USART1_RX. After receiving 3 bytes of data, it moves from USART1RX_Buffer[0] to USART1RX_Buffer[3]. After resetting DMA, it will start from USART1RX_Buffer[0]). In this way, as long as the maximum number of bytes is not overwritten and the flag is set to 1, the data of variable length can be received perfectly and the program will be interrupted the least number of times.
  • If the USARTx_RX_BY_IDLE_DMA macro is false , only the receive interrupt is selected . Then every time a byte of data is received, an interrupt will be generated, and the content of each byte will be moved from the device data register to our own defined memory variable.
void USART2_IRQHandler(void)
{
    
    
#if USARTx_RX_BY_IDLE_DMA	
	if(RESET != usart_interrupt_flag_get(USART2, USART_INT_FLAG_IDLE))
	{
    
    
		Module.USART2_RX_OK=1;		
		/* clear USART_INT_FLAG_IDLE */
		usart_data_receive(USART2);
		/* disable USART2_RX DMA_Channel */
		dma_channel_disable(DMA0, DMA_CH2);    
		/* reset DMA_Channel CNT */
		dma_transfer_number_config(DMA0, DMA_CH2, USART2_RX_SIZE);
		/* enable USART2_RX DMA_Channel */
		dma_channel_enable(DMA0, DMA_CH2);
	}
#else	
	if(RESET != usart_interrupt_flag_get(USART2, USART_INT_FLAG_RBNE))
	{
    
    
		usart_interrupt_flag_clear(USART2,USART_INT_FLAG_RBNE);
		Module.USART2_RX_OK=1;
		USART2RX_Buffer[0] = usart_data_receive(USART2);
	}
#endif	
}

6. Configure sending DMA
Please add image description

  • If you understand the description of DMA in the above manual, you should also understand here why the DMA channel corresponding to TX cannot be directly enabled after initialization, while the DMA channel corresponding to RX can be directly enabled after initialization. This is to ensure that data is not sent accidentally after initialization .
  • Based on the above description of DMA, we write the underlying function of USART to send DMA as follows:
void Usartx_Transmit_DMA(uint32_t usart_periph,uint8_t* data_buffer,uint8_t length)
{
    
    
	if(usart_periph==USART2)
	{
    
    
		/* Channel disable */
		dma_channel_disable(DMA0, DMA_CH1);
		
		dma_memory_address_config(DMA0, DMA_CH1,(uint32_t)data_buffer);
		dma_transfer_number_config(DMA0,DMA_CH1,length);
		
		/* enable DMA channel to start send */
		dma_channel_enable(DMA0, DMA_CH1);	
	}
}

7. Main function part

  • The main function program logic is as follows:
int main(void)
{
    
    	
	SystemTick_Init();
	
	SystemClock_Reconfig();
	
	GPIO_Init();
	DMA_Init();
	USARTx_Init();
	NVIC_Init();
	while(1)
	{
    
    
//		Usartx_Transmit_DMA(USART2,USART2TX_Buffer,USART2_RX_SIZE);
		Usartx_Transmit_DMA(USART2,(uint8*)"hello,MCU World!",20);
		delay_ms(1000);
	}
}
  • 使用Usartx_Transmit_DMA(USART2,USART2TX_Buffer,USART2_RX_SIZE);
    Please add image description
  • Use Usartx_Transmit_DMA(USART2,(uint8*)“hello,MCU World!”,20);
    Please add image description
  • Modify the content in while(1).
while(1)
{
    
    
	if(Module.USART2_RX_OK)
	{
    
    
		Module.USART2_RX_OK = 0;
		
		Usartx_Transmit_DMA(USART2,USART2RX_Buffer,USART2_RX_SIZE);
	}
}
  • The display effect is as follows:
    Please add image description8. Summary
    At this point, the debugging notes on USART for GD32F303 and GD32F103 have been completed. Not only GD, but any similar USART module can be configured and operated using this process.

!!!This article was originally published by Huanxi 6666 on CSDN. Please indicate the source when copying or reprinting:)!!!

Guess you like

Origin blog.csdn.net/qq_37554315/article/details/120313653