STM32F103C8T6 HAL库 USART1 DMA方式接收数据

前言:        

        前面的两篇文章都说关于发送的,HAL库发送数据可以调用现成的函数,而接收数据,现成函数不太好用。这里为了记录了一下自己参考了网上几个大佬的代码,整理了一下USART1 DMA方式接受数据的代码,这里亲测了一下,传输比较稳定,也没有出现发送数据过快导致串口反应不过来的情况。

正文开始:

        Cubemx配置

        这里跟上一篇博客一样,我就不再赘述了

        额外注意的一点是记得勾选上Use MicroLIB

        代码编写:

        这里我是学习了大佬的博客,传送门

        我稍微做了一下改进,这里还是沿用上一篇文章创建的两个文件 USART_DMA.c和USART.h
按照大佬博客里教的。

        ①  定义一个结构体变量:存放接收的字节数、数据数组。        

        ②  开启DMA:让硬件自动接收数据放到缓存        

        ③  重写回调函数:当一帧数据接收好了,把缓存的数据,转存到全局结构体变量里,备用。        

        ④  在需要使用串口接收的地方,如在while中,判断接收字节数>0,  即为接收到新一帧数据了

         在USART_DMA.h中,我们声明一下我们的结构体

typedef struct						//声明一个结构体,方便管理变量
{						uint16_t		ReceiveNum;		//接受字节数;在中断回调函数中被自动赋值;只要字节数>0,即为接受到新一帧uint8_t		ReceiveData[512];	//接受到的数据uint8_t		BuffTemp[512];		//接受缓存;注意,这个数组只是一个缓存//临时缓存,在DMA空闲中断中将把一帧数据复制到ReceivedData[] 
}myUATR_TypeDef;

        然后在USART_DMA.c中定义一个自己的变量,如果要在其他.c文件引用的,extern一下就行

myUATR_TypeDef myUSART1 = {0};				//用来定义自己的变量

        我采用的方法是,直接将extern myUATR_TypeDef myUSART1;放在USART_DMA.h里面。 

        开启DMA,让硬件自动接收数据,.

        我们整个接收过程,仅使用到1个HAL库函数。只需在main()函数的初始化部分,调用HAL库函数:HAL_UARTEx_ReceiveToIdle_DMA (串口、缓存、字节数) ;        

        参数:串口、接收缓存区、最大接收字节数          

        作用:使能DMA、使能串口的空闲中断,正式进入接收状态。 

        我们需要在main.c中添加代码

HAL_UARTEx_ReceiveToIdle_DMA(&huart1, myUSART1.BuffTemp, sizeof(myUSART1.BuffTemp));  // 开启DMA空闲中断  

         调用函数后,硬件就会立刻进入自动接收状态:从RX引脚接收到的数据,会逐个字节顺序存放到指定缓存中,这里我们指定的缓存是:myUSART1.BuffTemp

        因为函数内部,开启了DMA中断、空闲中断,所以达成下列两个条件之一,就会触发中断:

        ①  DMA接收的字节数,达到了参数中的最大值      

        ②  串口发生空闲中断,即RX引脚,超过1字节的时间,没有新信号。

        当上述中断产生时,硬件自动调用其相关的中断服务函数,再继而调用回调函数。

         重写DMA空闲中断回调函数,(DMA完成中断、空闲中断,所调用的回调函数):         HAL_UARTEx_RxEventCallback(串口,接收到的字节数);      

        弱函数定义在stm32xx_hal_gpio.c文件的底部。

/******************************************************************************* 函  数: HAL_UARTEx_RxEventCallback* 功  能: DMA+空闲中断回调函数* 参  数: UART_HandleTypeDef  *huart   // 触发的串口*          uint16_t             Size    // 接收字节* 返回值: 无* 备  注: 1:这个是回调函数,不是中断服务函数。技巧:使用CubeMX生成的工程中,中断服务函数已被CubeMX安排妥当,我们只管重写回调函数*          2:触发条件:当DMA接收到指定字节数时,或产生空闲中断时,硬件就会自动调用本回调函数,无需进行人工调用;*          2:必须使用这个函数名称,因为它在CubeMX生成时,已被写好了各种函数调用、函数弱定义(在stm32xx_hal_uart.c的底部); 不要在原弱定义中增添代码,而是重写本函数*          3:无需进行中断标志的清理,它在被调用前,已有清中断的操作;*          4:生成的所有DMA+空闲中断服务函数,都会统一调用这个函数,以引脚编号作参数*          5:判断参数传进来的引脚编号,即可知道是哪个串口接收收了多少字节
******************************************************************************/
void HAL_UARTEx_RxEventCallback(UART_HandleTypeDef *huart, uint16_t Size)
{if (huart == &huart1)                                                                    // 判断串口{__HAL_UNLOCK(huart);                                                                 // 解锁串口状态myUSART1.ReceiveNum  = Size;                                                          // 把接收字节数,存入结构体xUSART1.ReceiveNum,以备使用memset(myUSART1.ReceiveData, 0, sizeof(myUSART1.ReceiveData));                         // 清0前一帧的接收数据memcpy(myUSART1.ReceiveData, myUSART1.BuffTemp, Size);                                 // 把新数据,从临时缓存中,复制到xUSART1.ReceiveData[], 以备使用HAL_UARTEx_ReceiveToIdle_DMA(&huart1, myUSART1.BuffTemp, sizeof(myUSART1.BuffTemp));   // 再次开启DMA空闲中断; 每当接收完指定长度,或者产生空闲中断时,就会来到这个}
}

        (1)xUSART1.ReceiveNum = Size;        

        把接收的字节数,存入结构体 xUSART1.ReceiveNum,以备使用 。            

        在程序的其它地方,判断 ReceivNum > 0, 就能知道是否收到新一帧数据了。

        (2)memset(xUSART1.ReceivedData, 0, sizeof(xUSART1.ReceivedData));        

        清0前一帧的数据缓存  

        (3) memcpy(xUSART1.ReceivedData, xUSART1.BuffTemp, Size);              

        把新数据,从临时缓存中,复制到xUSART1.ReceivedData[], 以备使用                

        从结构体和这段回调函数中,可以发现,这是一个双缓存的操作思路。                      .ReceivedData:用于存放接收后完整的一帧数据,对外使用 。              

        .BuffTemp:用于DMA接收过程,是一个中间缓存。

        (4)HAL_UARTEx_ReceiveToIdle_DMA(&huart1, xUSART1.BuffTemp, sizeof(xUSART1.BuffTemp));              

        再次开启DMA空闲中断,进入接收状态。              

        我们在main()函数的初始化部分,已调用过这个函数了,为什么要在回调函数中再次调用?              

        因为在DMA的中断服务函数里,会关闭DMA,即只接收一次。所以,在接收完一帧后,再次调用函数,就能让DMA开始工作接收下一帧。在这个位置调用 ,能让DMA不断地循环工作。              

        其实,在CubeMX配置中,DMA有一个选项 :Mode的circular, 可以让DMA进行连续地的工作,接收完成后,无需在回调函数里再次开启DMA 

         本篇的处理,是保存最后一帧数据。当有新一帧数据来了,会自动盖掉旧帧数据。

         接下来,为了验证,到底这个项目的程序到底好事使不好使,我在这里写了一个测试函数,其中的myprintf函数是用了我上一篇文章的写法

       

void USART_test()
{if(myUSART1.ReceiveNum)			//一旦接受到数据{myUSART1.ReceiveNum = 0;		//将数据清零if(strcmp((char *)myUSART1.ReceiveData,"hello\r\n") == 0)//相等,返回 0;{num++;myprintf("接受次数%d\r\n",num);}}
}

        将其放在while循环中

        

最终效果演示

        在串口助手这里,简单设置一下 ,一定要注意的是,如果点了发送新行之后,一定不要再额外在第一个箭头的后面加回车了,否则是接受不到的。

         接受的速度也很不错,没有出现过卡死的状况,100ms回应一次。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/bicheng/25179.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Elasticsearch 认证模拟题 - 17

这两道题目非常具有代表性,分别是跨集群复制和跨集群检索,需要相应的 许可 这里在虚拟机上搭建集群完成这两道题目,这里补充一下 elasticsearch 和 kibana 的配置文件 # elasticsearch.yml cluster.name: cluster2 node.name: cluster2-node…

Linux之文件操作

目录 第1关:文件的创建 任务描述 相关知识 文件的创建 编程要求 答案: 第2关:文件打开与关闭 任务描述 相关知识 文件的打开 文件的关闭 编程要求 答案: 第3关:文件读写操作 任务描述 相关知识 文件的写操作 文件的读…

【Redis学习笔记05】Jedis客户端(中)

Jedis客户端 1. 命令 1.1 String类型 1.1.1 常见命令 SET命令 语法:SET key value [EX seconds | PX milliseconds] [NX|XX] 说明:将string类型的value值设置到指定key中,如果之前该key存在,则会覆盖原先的值,原先…

前端计网面试题(二)

一、在浏览器中输入url并且按下回车之后发生了什么? 首先解析url,判断url是否合法,如果合法再判断是否完整。如果不合法,则使用用户默认的搜索引擎进行搜索。DNS域名解析获取URL对应的ip地址。(首先看本地是否有缓存&…

HTML开发 Vue2.x + Element-UI 动态生成表单项并添加表单校验

基于vue2.x 和element-ui 动态生成表单项并添加表单校验; 1、需求问题 如下图,项目有个需求,点击添加按钮,新增一行设备信息,且每项信息必填; 2、代码 看到这个需求,首先想到要使用v-for的形…

使用 flask + qwen 实现 txt2sql 流式输出

前言 一般的大模型提供的 api 都是在提问之后过很久才会返回对话内容,可能要耗时在 3 秒以上了,如果是复杂的问题,大模型在理解和推理的耗时会更长,这种展示结果的方式对于用户体验是很差的。 其实大模型也是可以进行流式输出&a…

Vue3 一 快速启动基于Vite 创建项目

编码规范 TypeScript 组合式API setup语法糖 基于Vite 创建项目 WinR输入 CMD 回车后打开CMD命令行 已安装 18.3以上版本的NodeJS,js(安装) 我们用 NPM 方式安装 输入命令npm create vuelatest PS D:\WORK\NodeJS> npm create vuelatest Need to install the following …

《软件定义安全》之一:SDN和NFV:下一代网络的变革

第1章 SDN和NFV:下一代网络的变革 1.什么是SDN和NFV 1.1 SDN/NFV的体系结构 SDN SDN的体系结构可以分为3层: 基础设施层由经过资源抽象的网络设备组成,仅实现网络转发等数据平面的功能,不包含或仅包含有限的控制平面的功能。…

Python语言读取图像

import cv2 import numpy as np width 640 # 图像宽度height 480 # 图像高度channels 3 # 颜色通道数imgEmpty np.empty((height, width, channels), np.uint8) # 创建空白数组imgBlack np.zeros((height, width, channels), np.uint8) # 创建黑色图像 RGB0imgWhite …

STM32 uc/OS-III多任务程序

目录 一、项目创建 二、代码移植 1、uC/OS-III源码处理 2、KEIL文件配置 ​编辑3、文件修改 启动文件 ​编辑app_cfg.h includes.h bsp.c和bsp.h main.c lib_ cfg.h app.c和app.h 三、总结 学习目标: 学习嵌入式实时操作系统(RTOS&#xf…

覆盖路径规划经典算法 The Boustrophedon Cellular Decomposition 论文及代码详解

2000年一篇论文 Coverage of Known Spaces: The Boustrophedon Cellular Decomposition 横空出世,解决了很多计算机和机器人领域的覆盖路径问题,今天我来详细解读这个算法。 The Boustrophedon Cellular Decomposition 算法详解 这篇论文标题为"C…

办理公司诉讼记录删除行政处罚记录删除

企业行政处罚记录是可以做到撤销消除的,一直被大多数企业忽略,如果相关诉讼记录得不到及时删除,不仅影响企业招投标,还影响企业的贷款申请,严重的让企业资金链断裂,影响企业长远发展和企业形象。行政处罚是…

SpringBoot整合RabbitMQ实现消息延迟队列

环境依赖 SpringBoot 3.1.0 JDK 17 前期准备 安装MQ: liunxdockerrabbitmq安装延迟队列插件 实例 实现延迟队列的一种方式是在 RabbitMQ 中使用消息延迟插件,这个插件可以让你在消息发送时设置一个延迟时间,超过这个时间后消息才会被消费者接收到…

Vyper重入漏洞解析

什么是重入攻击 Reentrancy攻击是以太坊智能合约中最具破坏性的攻击之一。当一个函数对另一个不可信合约进行外部调用时,就会发生重入攻击。然后,不可信合约会递归调用原始函数,试图耗尽资金。 当合约在发送资金之前未能更新其状态时&#…

Dubbo 3.x源码(20)—Dubbo服务引用源码(3)

基于Dubbo 3.1,详细介绍了Dubbo服务的发布与引用的源码。 此前我们学习了调用createProxy方法,根据服务引用参数map创建服务接口代理引用对象的整体流程,我们知道会调用createInvokerForRemote方法创建远程引用Invoker,这是Dubbo …

总结七大排序算法

插入排序 直接插入排序是一种简单的插入排序法,其基本思想是:把待排序的记录按其关键码值的大小逐个插入到一个已经排好序的有序序列中,直到所有的记录插入完为止,得到一个新的有序序列 。实际中我们玩扑克牌时,就用了…

Python Mistune库:Markdown解析和处理

更多Python学习内容:ipengtao.com Mistune是一个用于Python的快速且功能强大的Markdown解析库。它以其高性能和灵活性著称,能够轻松扩展和定制。Mistune支持标准的Markdown语法,并且可以通过插件扩展支持更多功能,例如数学公式、高…

数据结构严蔚敏版精简版-栈和队列以及c语言代码实现

1栈的定义和特权 栈(stack)是限定仅在表尾进行插入或删除操作的线性表。 注:虽然说栈的实现就是一端插入和删除,但不一定是在“表尾”,这个“表尾”是广义的。 头插法实现链栈 尾插法实现链栈 因此,对栈来说,表尾…

算法——Floyd判圈算法

介绍 Floyd判圈算法用于判断一个链表中是否有环。 思想 使用快慢指针fast, slow,快指针每次走两步fast fast.next.next,慢指针每次走一步slow slow.next。当出现fast null || fast.next null时,说明链表不存在环,如果存在环…

1.2-自然语言的分布式表示-基于计数的方法

本篇笔记对应的视频链接为: 3-基于计数的方法表示单词-将文字转换成编号的预处理工作_哔哩哔哩_bilibili;4-基于计数的方法表示单词-使用共现矩阵进行单词的分布式表示_哔哩哔哩_bilibili;5-基于计数的方法表示单词-单词之间相似度计算_哔哩哔…