GD32F303调试小记(一)之USART(接收中断、接收空闲中断+DMA、发送DMA)

2023-11-19

前言

之前写了GD32F103调试小记(二)之USART(接收中断、接收空闲中断+DMA、发送DMA)一文。这次我们来看看GD32F303的USART是如何配置的,结合这两篇文章,相信大家GD32的USART配置流程会十分熟悉。

DMA

能大大减轻CPU负荷,数据的搬运工,建议场景合适的情况下,都去配置DMA功能。

USART

一种串行通信协议,网上资料很多,这里不多说。

各模块程序编写

在配置前,请确保你已经有一个GD32F303包含其对应标准库的Keil工程
1、时钟配置

图1.1 GD32F303外设挂载架构

  • 上图说明了GD32F303所拥有的外设时钟挂载在哪个总线上。这里我们使用USART2,挂载在APB1总线上。
  • 这里我们开启各GPIO端口时钟、GPIO引脚复用功能时钟、DMA时钟和USART2的时钟。
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、配置GPIO
请添加图片描述

  • 这里我用到了PC10和PC11作为USART2的TX与RX,代码配置如下:
// 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、配置DMA
请添加图片描述

  • 上图可知,USART2_TX和UASRT2_RX对应DMA0_CH1和DMA0_CH2。
  • 记住DMA传输的方向,TX对应的DMA通道是从我们内存中定义的变量搬运到USART2的发送寄存器中,而RX对应的DMA通道是从USART2的接收寄存器搬运到我们内存里定义的变量。
  • DMA是个搬运工,如果是单个字节的发送与接收,单凭USART自带的接收/发送完成标志位也可以做到。所以如果在USART上用到了DMA一定是多个字节数据的传输。这里用USARTx_RX_BY_IDLE_DMA做一个宏的区分。
  • 这里TX对应的通道初始化完成后先关闭掉,而RX对应的通道是初始化完成后可以先开启。至于为什么,容我先卖个关子,在下面我会详细解释。
/* 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、配置USART2

  • 波特率38400,数据位8位,停止位1位,无校验位,不使用硬件流控制,接收/发送使能,发送DMA使能。接收DMA是否使能由USARTx_RX_BY_IDLE_DMA这个宏决定。
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、配置中断优先级组与USART2中断

请添加图片描述

请添加图片描述

请添加图片描述

  • 仔细阅读F303应用手册中关于USART的中断描述,我们可以理解RBNE代表每从RX脚上接收到一帧的数据,就置1,我们能从USART接收寄存器读取该数据。IDLEF代表当RX接收完多帧数据后,检测到线上空闲就置1,而USART接收寄存器每次只能接收单个帧数据,意味着多帧数据里除了最后一帧数据,前面的都被覆盖(忽略)掉了,所以必须配合DMA的数据传输功能才能保证多帧数据的接收完整。我想看到这里,大家也应该理解我这里为什么用USARTx_RX_BY_IDLE_DMA这个宏作区分了吧。
  • 设置整个系统中断优先级组。这里根据USARTx_RX_BY_IDLE_DMA这个宏决定仅开接收中断还是仅开接收空闲中断。
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
}
  • 编写中断服务函数。
  • 如果USARTx_RX_BY_IDLE_DMA这个宏为,即选择使用接收空闲中断+DMA的方式接收数据。我们通过读相应的数据寄存器去清这个标志位(手册描述的清位方式),再重新置为对应的DMA接收通道。程序运行到此处时,DMA通道已经获取了一个或多个字节的数据(看你发了多少字节的数据),关闭再开启则是让DMA的内存buffer再次从头开始(比如我们定义的USART1RX_Buffer数组是USART1_RX的内存buff,在接受了3个字节的数据后,从USART1RX_Buffer[0]移到了USART1RX_Buffer[3],复位DMA后又将从USART1RX_Buffer[0]开始)。这样只要保证最大字节数不被覆盖,在有标志位置1后及时处理,就能完美接收不定长的数据,且程序被中断的次数最少。
  • 如果USARTx_RX_BY_IDLE_DMA这个宏为,即选择仅使用接收中断。那么每接收到一个字节的数据就会产生一次中断,再将每个字节的内容从设备数据寄存器搬运到我们自己定义的内存变量里。
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、配置发送DMA
请添加图片描述

  • 如果理解了上述手册中对DMA的描述,这里也应该明白了,为什么TX对应初始化完的DMA通道初始化完后不能直接使能,而RX对应的DMA通道初始化完后能直接使能。还是为了保证初始化完后避免误发数据
  • 根据上述对DMA的描述,我们对USART发送DMA的底层函数编写如下:
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、主函数部分

  • 主函数程序逻辑如下:
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);
    请添加图片描述
  • 使用Usartx_Transmit_DMA(USART2,(uint8*)“hello,MCU World!”,20);
    请添加图片描述
  • 修改while(1)里的内容。
while(1)
{
	if(Module.USART2_RX_OK)
	{
		Module.USART2_RX_OK = 0;
		
		Usartx_Transmit_DMA(USART2,USART2RX_Buffer,USART2_RX_SIZE);
	}
}
  • 显示效果如下:
    请添加图片描述8、总结
    至此,GD32F303与GD32F103关于USART的调试小记已全部完成。不止是GD的,任何与之类似的USART模块都可以以此流程去配置操作。

!!!本文为欢喜6666在CSDN原创发布,复制或转载请注明出处 :)!!!

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

GD32F303调试小记(一)之USART(接收中断、接收空闲中断+DMA、发送DMA) 的相关文章

随机推荐

  • 最短路径-C++算法

    C 算法之 最短路径 基础2 2020版 前记 通过前面那份讲义 你应该对基础知识有所了解 今天我们来看下floyed算法的实现 复习 什么是最短路径 百度中的定义 用于计算一个节点到其他所有节点的最短路径 主要特点是以起始点为中心向外层层
  • Mixed Reality Toolkit-Unity 开发系列—Input 模块

    在 Mixed Reality Toolkit Unity 开发系列 Sharing 模块 中 我们首次开讲 HoloLens 开发神器 微软原生开发包 Mixed Reality Toolkit Unity 简称 MRTK 并介绍了 Sh
  • 如何拿到半数面试公司Offer——我的Python求职之路

    从八月底开始找工作 短短的一星期多一些 面试了9家公司 拿到5份Offer 可能是因为我所面试的公司都是些创业性的公司吧 不过还是感触良多 因为学习Python的时间还很短 没想到还算比较容易的找到了工作 就把这些天的面试经验和大家分享一下
  • c++ 链表的创建与链表常见操作

    c 链表的创建与链表常见操作 一 链表定义 struct 下面的结构体定义了C 语言中的一种常见的链表节点 包括数据 指针和两种种不同类型的构造函数 struct ListNode int val 存储数据 ListNode next ne
  • java集合框架(一)——框架结构及基础接口

    java util包是一个重要的包 包含许多类和接口 它还包括java的一个功能强大的子系统 集合框架 集合框架是一个复杂接 口和类的层 提供了管理对象组的优化技术 使用集合框架的目的有几个 首先这种框架必须是高性能的 基础集合 动态数组
  • Java String6,7,8,9区别 以及 有哪些方式可以提高性能

    String 对象在jdk不同版本中的实现 String对象是 Java 中使用最频繁的对象之一 所以 Java 公司也在不断的对String对象的实现进行优化 以便提升String对象的性能 看下面这张图 一起了解一下String对象的优
  • 每日一个Python基础练习题

    题目 有四个数字 1 2 3 4 能组成多少个互不相同且无重复数字的三位数 各是多少 可填在百位 十位 个位的数字都是1 2 3 4 组成所有的排列后再去 掉不满足条件的排列 代码 a 100 b 10 c r 1 for a in ran
  • 朗润国际期货技术分析——日内交易图表类型

    一张图表胜过千言万语 你以前听说过这句话 对吗 在日内交易员的世界里 一张图表往往比千言万语更有价值 它不仅告诉我们过去发生了什么 而且告诉我们一个市场在未来可能会做什么 它将继续走高还是走低 它是否处于一个区间 也许一个重大的方向变化或反
  • 2D/3D人体姿态估计 (2D/3D Human Pose Estimation)

    1 基本概念 算法改进入口 网络设计 特征流 损失函数 数据集的重要性 只要有一个好的 针对性的数据集 问题都可以解决 过集成新一代AutoML技术 可降低算法试错成本 人体姿态估计 Human Pose Estimation 是指图像或视
  • 在C语言中,“>>=”;“<<=”;“&=”;“

    C语言中 gt gt 的意思是 右移后赋值 示例 x 8 1000 x gt gt 3 结果为 0001 C语言中 lt lt 的意思是 左移后赋值 示例 x 1 0001 x lt lt 3 结果为 1000 C语言中 的意思是 按位与后
  • pycharm 代码上传到gitee仓库里

    目录 一 准备 二 1 新建仓库 2 commit和push 一 准备 1 在pycharm中安装插件 github git 2 在本地安装git 二 1 新建仓库 首先在pycharm中打开自己的本地项目 如果没有配置过 主界面应该是这样
  • 2023年数学建模B组:利用AHP层次分析法解决实际问题(Matlab)

    目录 利用AHP层次分析法解决实际问题 Matlab实现 介绍 案例背景 步骤1 建立层次结构模型
  • Flutter酷炫的路由动画效果

    现在Flutter的路由效果已经非常不错了 能满足大部分App的需求 但是谁不希望自己的App更酷更炫那 下面介绍几个酷炫的路由动画 其实路由动画的原理很简单 就是重写并继承PageRouterBuilder这个类里的transitions
  • 详解JS前端异步文件加载篇之Async与Defer区别

    目录 同步 异步及推迟的概念 async和defer解决文件加载阻塞问题 在了解async和defer的区别之前 我们需要先了解同步 异步和推迟的概念 同步 异步及推迟的概念 假如现在有一条非常狭隘的胡同 里面有两个人挨着走 那么现在请问后
  • Java集合的两种遍历方式

    Java集合共有两种遍历方式 增强for循环 foreach 迭代器 Main方法 public static void main String args 创建集合 Collection collection new ArrayList 添
  • XXL-JOB分布式任务调度平台配置详解

    XXL JOB是一个分布式任务调度平台 其核心设计目标是开发迅速 学习简单 轻量级 易扩展 个人建议 对于需要定时调度任务开箱即用的小伙伴来说 完全可以学习参考下 本文主要介绍了Xxl Job分布式任务调度框架的配置信息详解 以及路由策略
  • git clone下新项目后运行报错‘vue-cli-service‘ 不是内部或外部命令,也不是可运行的程序或批处理文件。

    报错 vue cli service 不是内部或外部命令 也不是可运行的程序 或批处理文件 因为项目里还没有node modules这个包 需要运行npm install 运行后没有报错但是有个警告 npm WARN read shrink
  • MySQL导入导出数据mysqldump,mysql,select into file,load data

    研发人员往往需要从数据库中导出数据 或者将数据导入到数据库中 一些客户端工具提供了简单方便的功能 可以让他们不用使用命令进行操作 但是客户端工具可能会受到环境的限制而不能使用 所以 研发人员有必要掌握一些常用的命令来进行操作数据 MySQL
  • 残差神经网络的研究

    目录 一 ResNet残差神经网络 1 1 提出 1 2 作用 1 3 应用场景 1 4 残差单元的结构 1 4 1 残差网络得名的原因 1 4 2 残差网络可以有效缓解退化现象的原因 1 4 3 数学原理 二 附录 2 1 残差神经网络可
  • GD32F303调试小记(一)之USART(接收中断、接收空闲中断+DMA、发送DMA)

    前言 之前写了GD32F103调试小记 二 之USART 接收中断 接收空闲中断 DMA 发送DMA 一文 这次我们来看看GD32F303的USART是如何配置的 结合这两篇文章 相信大家GD32的USART配置流程会十分熟悉 DMA 能大