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.mfbz.cn/a/696066.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

【python】OpenCV—Histogram Matching(9.2)

学习来自OpenCV基础(17)基于OpenCV、scikit-image和Python的直方图匹配 文章目录 直方图匹配介绍scikit-image 中的直方图匹配小试牛刀风格迁移 直方图匹配介绍 直方图匹配(Histogram Matching)是一种图像处理技术,旨…

一文详解大模型微调全流程

节前,我们星球组织了一场算法岗技术&面试讨论会,邀请了一些互联网大厂朋友、参加社招和校招面试的同学. 针对算法岗技术趋势、大模型落地项目经验分享、新手如何入门算法岗、该如何准备、面试常考点分享等热门话题进行了深入的讨论。 汇总合集&…

11-数组与指针深入理解——题型理解

11-数组与指针深入理解——题型理解 文章目录 11-数组与指针深入理解——题型理解一、理解题1二、理解题二三、理解题三四、理解题四五、理解题五六、理解题六 一、理解题1 #include <stdio.h>int main(void) {int (*p)[5] NULL; // 定义一个指向 拥有5个整型数据的数组…

Tessy学习系列(三):单元测试——官方例程isValueInRange

一、工程创建 &#xff08;1&#xff09;新建工程 注意&#xff1a;工程名称以及路劲不能包含空格和中文 &#xff08;2&#xff09;新建测试集与单元测试模块 新建测试集 新建单元测试模块 设置测试模块为单元测试模块并选择GNU GCC编译器如果需要其他的编译器&#xff0c;…

力扣每日一题 6/10

881.救生艇[中等] 题目&#xff1a; 给定数组 people 。people[i]表示第 i 个人的体重 &#xff0c;船的数量不限&#xff0c;每艘船可以承载的最大重量为 limit。 每艘船最多可同时载两人&#xff0c;但条件是这些人的重量之和最多为 limit。 返回 承载所有人所需的最小船…

kubesz(一键安装k8s)

引言 Kubernetes&#xff08;K8s&#xff09;是一个开源的容器编排系统&#xff0c;用于自动化部署、扩展和管理容器化应用程序。kubeasz 是一个用于快速搭建 Kubernetes 高可用集群的项目&#xff0c;它基于 Ansible&#xff0c;通过提供一套简单、易用的配置&#xff0c;使得…

杨校老师项目之基于SpringBoot的理发店的预约管理系统

原系统是SSMJSP页面构成&#xff0c;先被修改为SpringBoot JSP页面 自助下载渠道: https://download.csdn.net/download/kese7952/89417001&#xff0c;或 点我下载 理发师信息&#xff1a; 理发师详细信息 公告信息 员工登录&#xff1a; 管理员登录

94、二叉树的迭代遍历

实现对二叉树的前后序非递归遍历 题解&#xff1a; 递归的实现就是&#xff1a;递去&#xff0c;归来。每一次递归调用都会把函数的局部变量、参数值和返回地址等压入调用栈中&#xff0c;然后递归返回的时候&#xff0c;从栈顶弹出上一次递归的各项参数&#xff0c;所以这就是…

有点好玩的python运维脚本

python运维脚本 1. 常用端口扫描2. 文件整理 1. 常用端口扫描 在计算机网络中&#xff0c;端口是一个通信端点&#xff0c;允许不同的进程或服务通过网络连接和交换数据。端口通过数值来标识&#xff0c;并与特定的协议相关联。未采取适当安全措施而保持端口开放&#xff0c;可…

上位机图像处理和嵌入式模块部署(f407 mcu vs h750)

【 声明&#xff1a;版权所有&#xff0c;欢迎转载&#xff0c;请勿用于商业用途。 联系信箱&#xff1a;feixiaoxing 163.com】 在目前工业控制上面&#xff0c;f103和f407是用的最多的两种stm32 mcu。前者频率低一点&#xff0c;功能少一点&#xff0c;一般用在低端的嵌入式设…

搞懂银行的各类号码 — Account Number, Routing Number 和 Swift Code

1. 前言2. 名词解释 2.1. Debit Card Number 储蓄卡卡号2.2. Account Number 账户号码2.3. Routing Number 路由号码2.4. SWIFT Code SWIFT 号码3. 查找信息 3.1. 支票3.2. 网上银行3.3. 手机银行4. SWFIT Code 4.1. 看懂 SWIFT Code4.2. 询问银行4.3. Google 大神4.4. 部分常用…

GitLab代码导出 gitlab4j-api 实现

目录 GitLab简介 GitLab 的主要特点包括&#xff1a; GitLab代码导出 gitlab4j-api 添加 gitlab4j-api 依赖 使用 gitlab4j-api 获取特定命名空间下的所有项目 说明 注意事项 GitLab简介 GitLab 是一个开源的代码仓库和协作平台&#xff0c;主要用于版本控制和源代码管理…

堆和栈(heap and stack)

1、堆&#xff1a;一块内存空间&#xff0c;可以从中分配一个小buffer&#xff0c;用完后再把它放回去。 2、栈&#xff1a;也是一块内存空间&#xff0c;cpu的sp寄存器指向它&#xff0c;它可以用于函数调用、局部变量、多任务系统里保存现场。 PUSH [r3-r6,lr]; #将r3到r6寄…

未来几年,同样的性能,推理功耗降低为现在的几万分之一,有可能吗

未来几年,同样的性能,推理功耗降低为现在的几万分之一,有可能吗 一.数据二.抓取LLM排行榜,相同的MMLU精度,模型参数量缩减倍数三.其它 有人说未来几年,推理功耗能降低为现在的几万分之一,好奇怎么能做到呢 一.数据 二.抓取LLM排行榜,相同的MMLU精度,模型参数量缩减倍数 import…

Docker Swarm集群部署管理

Docker Swarm集群管理 文章目录 Docker Swarm集群管理资源列表基础环境一、安装Docker二、部署Docker Swarm集群2.1、创建Docker Swarm集群2.2、添加Worker节点到Swarm集群2.3、查看Swarm集群中Node节点的详细状态信息 三、Docker Swarm管理3.1、案例概述3.2、Docker Swarm中的…

1035 插入与归并(测试点6)

solution 类型判断&#xff1a;插入排序中已排序的部分有序&#xff0c;未排序的和原数组元素相同&#xff1b;否则为归并排序测试点6&#xff1a;对于归并排序的子序列长度&#xff0c;不能简单视为前k个有序则子序列长度就是k 例如该测试用例的归并排序的子序列长度应该为2&…

C# BindingSource 未完

数据绑定导航事件数据验证自定义示例示例总结 在 C#中&#xff0c; BindingSource 是一个非常有用的控件&#xff0c;它提供了数据绑定的基础设施。 BindingSource 允许开发者将数据源&#xff08;如数据库、集合、对象等&#xff09;与用户界面控件&#xff08;如文本框、下…

测试基础12:测试用例设计方法-边界值分析

课程大纲 1、定义 经验发现&#xff0c;较多的错误往往发生在输入或输出范围的边界上&#xff0c;因为边界值是代码判断语句的点&#xff0c;一般容易出问题&#xff08;数值写错、多加或丢失等号、写错不等号方向…&#xff09;。所以增加对取值范围的边界数据的测试&#xff…

Vue3父组件如何访问子组件属性和方法

本篇内容主要是父组件如何访问子组件的属性和方法 文章目录 子组件 //son.vue代码const list (info) >{console.log(info) }const name ref("XXXX")//子组件向父组件暴露了一个方法&#xff0c;然后父组件就可以去使用子组件里面的一些属性和方法了 //子组件向…

车载电子电气架构 - 智能座舱技术及功能应用

我是穿拖鞋的汉子,魔都中坚持长期主义的汽车电子工程师。 老规矩,分享一段喜欢的文字,避免自己成为高知识低文化的工程师: 屏蔽力是信息过载时代一个人的特殊竞争力,任何消耗你的人和事,多看一眼都是你的不对。非必要不费力证明自己,无利益不试图说服别人,是精神上的节…