GD32F30x系列---串口通信(USART)基础配置(DMA模式)

GD32F30x系列的USART支持DMA功能,但不是所有的都支持,如手册中说明了UART4:
在这里插入图片描述
这里使用USART的DMA功能+串口空闲中断进行数据的收发,可以与上一节的串口接收中断收发对比,DMA的原理以及说明这里就不再赘述了,可自行查找资料了解一下。
1、先创建一个usart_dma.c和usart_dma.h文件,并放到对应的文件夹中,如下图所示:
在这里插入图片描述
并将创建好的文件添加到工程中,以及将gd32f30x_dma.c库文件添加到keil项目工程中,如下图所示:
在这里插入图片描述
2、实现串口的初始化以及DMA收发功能—这里以USART2为例

//usart_dma.c文件
#include "usart_dma.h"

//<<<<<<<<<<<<<<<<宏定义<<<<<<<<<<<<<<<<
#define USART_TX_RX_BUFF_LEN 	256//DMA缓存数据大小
#define USART_DMA_IDLE			0//DMA状态空闲
#define USART_DMA_BUSY			1//DMA状态忙
//>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
//<<<<<<<<<<<<<<<<结构定义<<<<<<<<<<<<<<<<
#pragma pack(1) 
typedef struct
{
	uint8_t dma_tx_buf[USART_TX_RX_BUFF_LEN];//缓存DMA要发送的数据
	uint8_t dma_rx_buf[USART_TX_RX_BUFF_LEN];//缓存DMA接收到的数据
	uint8_t dma_tx_state;//DMA发送状态
	uint8_t dma_rx_state;//DMA接收状态
}USART_DataInfo;
#pragma pack() 
//>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
USART_DataInfo g_usart_DataInfo;

/*
	串口初始化,使用DMA模式
*/
void gd32_usart_dma_init(void)
{
	usart_deinit(USART2);
	usart_disable(USART2);
	rcu_periph_clock_enable(RCU_GPIOB);
	rcu_periph_clock_enable(RCU_USART2);
	/* connect port to USARTx_Tx */
	gpio_init(GPIOB, GPIO_MODE_AF_PP, GPIO_OSPEED_50MHZ, GPIO_PIN_10);
	/* connect port to USARTx_Rx */
	gpio_init(GPIOB, GPIO_MODE_IPU, GPIO_OSPEED_50MHZ, GPIO_PIN_11);
	nvic_irq_enable(USART2_IRQn,1,0);
	
	usart_interrupt_enable(USART2,USART_INT_IDLE);		//空闲中断
	//usart_interrupt_enable(USART2,USART_INT_RBNE);//接收中断
	usart_baudrate_set(USART2,38400);//波特率设置
	usart_parity_config(USART2,USART_PM_NONE);//校验位设置
	usart_word_length_set(USART2,USART_WL_8BIT);//数据位设置
	usart_stop_bit_set(USART2,USART_STB_1BIT);//停止位设置
	usart_hardware_flow_rts_config(USART2, USART_RTS_DISABLE);//
    usart_hardware_flow_cts_config(USART2, USART_CTS_DISABLE);//硬件流控设置
	usart_data_first_config(USART2,USART_MSBF_LSB);//发送模式--LSB
	usart_transmit_config(USART2,USART_TRANSMIT_ENABLE);//发送使能
	usart_receive_config(USART2,USART_RECEIVE_ENABLE);//接收使能
	usart_enable(USART2);//使能串口
	gd32_usart_dma_tx_init();//DMA发送初始化
	gd32_usart_dma_rx_init();//DMA接收初始化
}

/*
	USART2 DMA发送初始化
*/
void gd32_usart_dma_tx_init(void)
{
	dma_parameter_struct dma_init_struct;
	/* enable DMA1 */
	rcu_periph_clock_enable(RCU_DMA0);
    /* deinitialize DMA channel(USART tx) */
    dma_deinit(DMA0, DMA_CH1);
	dma_init_struct.periph_addr  = (uint32_t)(&USART_DATA(USART2));
    dma_init_struct.periph_width = DMA_PERIPHERAL_WIDTH_8BIT; 
    dma_init_struct.periph_inc   = DMA_PERIPH_INCREASE_DISABLE;
    dma_init_struct.memory_addr  = NULL;
    dma_init_struct.memory_width = DMA_MEMORY_WIDTH_8BIT;
    dma_init_struct.memory_inc   = DMA_MEMORY_INCREASE_ENABLE;
    dma_init_struct.number       = 0;
    dma_init_struct.direction    = DMA_MEMORY_TO_PERIPHERAL;
    dma_init_struct.priority     = DMA_PRIORITY_ULTRA_HIGH;
    dma_init(DMA0, DMA_CH1, &dma_init_struct);
    //configure DMA mode 
    dma_circulation_disable(DMA0, DMA_CH1);
	dma_memory_to_memory_disable(DMA0, DMA_CH1);
	usart_dma_transmit_config(USART2, USART_DENT_ENABLE);
	nvic_irq_enable(DMA0_Channel1_IRQn,2,0);
	dma_interrupt_enable(DMA0, DMA_CH1, DMA_INT_FTF|DMA_INT_ERR);
	g_usart_DataInfo.dma_tx_state = USART_DMA_IDLE;
	return;
}

/*
	USART2 DMA接收初始化
*/
void gd32_usart_dma_rx_init(void)
{
    dma_parameter_struct dma_parameter;
	/* enable DMA0 */
	rcu_periph_clock_enable(RCU_DMA0);
    /* 接收 dm0 channel5(USART1 rx) */
    dma_deinit(DMA0,DMA_CH2);
	dma_parameter.periph_addr  = (uint32_t)(&USART_DATA(USART2));
    dma_parameter.periph_width = DMA_PERIPHERAL_WIDTH_8BIT; 
    dma_parameter.periph_inc   = DMA_PERIPH_INCREASE_DISABLE;
	dma_parameter.memory_addr  = (uint32_t)&g_usart_DataInfo.dma_rx_buf[0];
    dma_parameter.memory_width = DMA_MEMORY_WIDTH_8BIT;
    dma_parameter.memory_inc   = DMA_MEMORY_INCREASE_ENABLE;
    dma_parameter.number       = USART_TX_RX_BUFF_LEN;
    dma_parameter.direction    = DMA_PERIPHERAL_TO_MEMORY;
    dma_parameter.priority     = DMA_PRIORITY_ULTRA_HIGH;	
	dma_init(DMA0, DMA_CH2, &dma_parameter);
    /* configure DMA mode */
    dma_channel_enable(DMA0, DMA_CH2);
	dma_circulation_disable(DMA0, DMA_CH2);
	dma_memory_to_memory_disable(DMA0, DMA_CH2);

	usart_dma_receive_config(USART2, USART_DENR_ENABLE);
	nvic_irq_enable(DMA0_Channel2_IRQn, 2, 1);
	dma_interrupt_enable(DMA0, DMA_CH2, DMA_INT_FTF|DMA_INT_ERR);
	g_usart_DataInfo.dma_rx_state = USART_DMA_BUSY;
}

/*
	USART2 DMA发送数据
*/
void gd32_usart_dma_send(uint8_t *p_buff,uint32_t data_len)
{
	dma_channel_enum dma_channel;
	
	if(data_len >= USART_TX_RX_BUFF_LEN || USART_DMA_BUSY == g_usart_DataInfo.dma_tx_state)
		return;
	memcpy(g_usart_DataInfo.dma_tx_buf, p_buff, data_len);
	g_usart_DataInfo.dma_tx_state = USART_DMA_BUSY;
	dma_channel_disable(DMA0, DMA_CH1);
	dma_memory_address_config(DMA0, DMA_CH1,(uint32_t)&g_usart_DataInfo.dma_tx_buf[0]);
	dma_transfer_number_config(DMA0, DMA_CH1, data_len);
	dma_channel_enable(DMA0, DMA_CH1);
}

/*
	USART2 获取DMA接收数据
*/
uint32_t gd32_usart_dma_get_recv_data(uint8_t *recv_buff,uint32_t data_len)
{
	uint32_t recv_len=0;
	if(data_len >= USART_TX_RX_BUFF_LEN || USART_DMA_BUSY == g_usart_DataInfo.dma_rx_state)
	{
		return 0;
	}

	recv_len = USART_TX_RX_BUFF_LEN - dma_transfer_number_get(DMA0, DMA_CH2);
	if(recv_len > 0)
	{
		if(recv_len> data_len)
		{
			recv_len = data_len;
		}
		memcpy(recv_buff, g_usart_DataInfo.dma_rx_buf, recv_len);
	}
	gd32_usart_dma_rx_init();
	return recv_len;
}

/*
	串口接收中断函数
*/
void USART2_IRQHandler(void)
{
	if(usart_interrupt_flag_get(USART2,USART_INT_FLAG_ERR_ORERR) != RESET
	||usart_interrupt_flag_get(USART2,USART_INT_FLAG_ERR_NERR) != RESET
	||usart_interrupt_flag_get(USART2,USART_INT_FLAG_ERR_FERR) != RESET)
	{
		usart_interrupt_flag_clear(USART2,USART_INT_FLAG_ERR_ORERR);
		usart_interrupt_flag_clear(USART2,USART_INT_FLAG_ERR_NERR);
		usart_interrupt_flag_clear(USART2,USART_INT_FLAG_ERR_FERR);
		return;
	}
	if(usart_interrupt_flag_get(USART2,USART_INT_FLAG_IDLE) != RESET
	&&	RESET != usart_flag_get(USART2, USART_FLAG_IDLE))
	{
		usart_interrupt_flag_clear(USART2,USART_INT_FLAG_IDLE);
		usart_flag_clear(USART2,USART_FLAG_IDLE);
		usart_data_receive(USART2);
		usart_interrupt_disable(USART2, USART_INT_IDLE);
		dma_channel_disable(DMA0, DMA_CH2);
		g_usart_DataInfo.dma_rx_state = USART_DMA_IDLE;
	}
}

/*
	USART2 -- DMA发送完成中断
*/
void DMA0_Channel1_IRQHandler(void)
{
	if(dma_interrupt_flag_get(DMA0, DMA_CH1, DMA_INTF_ERRIF) != RESET)
	{
		dma_interrupt_flag_clear(DMA0, DMA_CH1, DMA_INTF_ERRIF);
	}
	if(dma_interrupt_flag_get(DMA0, DMA_CH1, DMA_INT_FLAG_FTF) != RESET)
	{
		dma_interrupt_flag_clear(DMA0, DMA_CH1, DMA_INT_FLAG_FTF);
		g_usart_DataInfo.dma_tx_state = USART_DMA_IDLE;
	}
}

/*
	USART2 -- DMA接收完成中断
*/
void DMA0_Channel2_IRQHandler(void)
{
	if(dma_interrupt_flag_get(DMA0, DMA_CH2, DMA_INTF_ERRIF) != RESET)
	{
		dma_interrupt_flag_clear(DMA0, DMA_CH2, DMA_INTF_ERRIF);
	}
	if(dma_interrupt_flag_get(DMA0, DMA_CH2, DMA_INTF_FTFIF) != RESET)
	{
		dma_interrupt_flag_clear(DMA0, DMA_CH2, DMA_INTF_FTFIF);
		dma_channel_disable(DMA0, DMA_CH2);
	}
}

//usart_dma.h文件
#ifndef __USART_DMA_H__
#define __USART_DMA_H__

#include "gd32f30x.h"
#include "stdio.h"
#include "string.h"

void gd32_usart_dma_init(void);
void gd32_usart_dma_tx_init(void);
void gd32_usart_dma_rx_init(void);
void gd32_usart_dma_send(uint8_t *p_buff,uint32_t data_len);
uint32_t gd32_usart_dma_get_recv_data(uint8_t *recv_buff,uint32_t data_len);
#endif

3、在main.c文件的main函数中调用串口DMA模式的初始化,并循环调用串口DMA发送,这里循环1S发送字符“1234567890”,连接好硬件板和电脑后,编译下载程序到硬件板中,点击调试运行,如下图所示:
在这里插入图片描述
4、点击运行程序,连接好电脑和硬件板的串口,使用串口调试工具查看当前硬件板发送的数据,如下图所示:
在这里插入图片描述
串口助手每隔1S接收一条1234567890字符,说明程序运行成功;
5,使用串口调试助手发送0x55、0xAA、0x00、0xFF数据到硬件板,设置断点,查看接收到的数据是否正常,如下图所示:
在这里插入图片描述
程序接收到的数据与串口助手发送的数据一致,程序运行正常。
这里我们可以看到使用串口连续发送4个字节的数据,在串口空闲中断中就能够查看到DMA缓存的4个字节的数据,只需要进一次中断即可,如果是串口接收则每接收一个字节需要中断一次,手动将数据缓存,因此使用DMA模式在串口收发频繁的情况下能够大大的节省CPU的开销。
注意:我这里没有调用gd32_usart_dma_get_recv_data(uint8_t *recv_buff,uint32_t data_len)去获取DMA的数据,所以接收完一帧数据后就把DMA关闭了,如果要继续接收的话需要把DMA的数据提取出来重新开启DMA接收才可以,这里就不做处理了。
6、这里在次测试一下使用DMA功能后,用串口连续发送200个字节的数据,查看会占用CPU多少的时间来处理,如下入所示:
在这里插入图片描述
在这里插入图片描述
这里可以查看到使用DMA发送数据之前当前的systick为6006,使用DMA发送数据完成后,系统当前的systick仍然是6006,并且串口也接收到硬件板发送过来的数据,说明使用DMA发送数据基本上是不占用CPU的时间的。
与之前的串口中断接收缓存和串口发送相比,在数据量大的情况下DMA模式能够大大的节省CPU的开销。

  • 6
    点赞
  • 32
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
GD32F30x是一种MCU单片机,具有串口通信功能。在使用GD32F30x进行串口通信时,可以使用固件库中的函数来初始化串口和发送数据。 首先,在main.c文件的main函数中调用串口的初始化函数,并循环调用串口发送函数。可以使用串口调试工具来接收发送的数据。这种方法是通过CPU来发送数据,因此在连续发送200字节的数据时,会消耗一定的CPU时间。 另一种方式是使用DMA模式进行串口通信。同样在main.c文件的main函数中调用串口DMA模式的初始化函数,并循环调用串口DMA发送函数。使用串口调试工具来接收发送的数据。使用DMA模式可以大大节省CPU的开销,在串口收发频繁的情况下尤为有效。 需要注意的是,使用DMA接收数据后,如果需要继续接收,需要重新开启DMA接收。另外,在使用DMA发送数据时,可以观察到系统的systick仍然保持不变,说明使用DMA发送数据基本上不会占用CPU的时间。 因此,GD32F30x可以使用CPU或DMA模式进行串口通信,具体选择哪种方式取决于实际需求和性能要求。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* [GD32F30x系列MCU单片机固件库GD32F30x_Firmware_Library_V2.1.3.zip](https://download.csdn.net/download/SKCQTGZX/85562969)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 33.333333333333336%"] - *2* [GD32F30x系列---串口通信(USART)基础配置(中断接收模式)](https://blog.csdn.net/weixin_43647919/article/details/128181430)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 33.333333333333336%"] - *3* [GD32F30x系列---串口通信(USART)基础配置(DMA模式)](https://blog.csdn.net/weixin_43647919/article/details/128198003)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 33.333333333333336%"] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

小吴同学啊

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值