STM32F103C8T6 HAL库 USART1 DMA方式接收数据

2024-06-10 11:12

本文主要是介绍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回应一次。

这篇关于STM32F103C8T6 HAL库 USART1 DMA方式接收数据的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1048004

相关文章

Java AOP面向切面编程的概念和实现方式

《JavaAOP面向切面编程的概念和实现方式》AOP是面向切面编程,通过动态代理将横切关注点(如日志、事务)与核心业务逻辑分离,提升代码复用性和可维护性,本文给大家介绍JavaAOP面向切面编程的概... 目录一、AOP 是什么?二、AOP 的核心概念与实现方式核心概念实现方式三、Spring AOP 的关

Linux挂载linux/Windows共享目录实现方式

《Linux挂载linux/Windows共享目录实现方式》:本文主要介绍Linux挂载linux/Windows共享目录实现方式,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地... 目录文件共享协议linux环境作为服务端(NFS)在服务器端安装 NFS创建要共享的目录修改 NFS 配

Vue3视频播放组件 vue3-video-play使用方式

《Vue3视频播放组件vue3-video-play使用方式》vue3-video-play是Vue3的视频播放组件,基于原生video标签开发,支持MP4和HLS流,提供全局/局部引入方式,可监听... 目录一、安装二、全局引入三、局部引入四、基本使用五、事件监听六、播放 HLS 流七、更多功能总结在 v

Java发送SNMP至交换机获取交换机状态实现方式

《Java发送SNMP至交换机获取交换机状态实现方式》文章介绍使用SNMP4J库(2.7.0)通过RCF1213-MIB协议获取交换机单/多路状态,需开启SNMP支持,重点对比SNMPv1、v2c、v... 目录交换机协议SNMP库获取交换机单路状态获取交换机多路状态总结交换机协议这里使用的交换机协议为常

k8s admin用户生成token方式

《k8sadmin用户生成token方式》用户使用Kubernetes1.28创建admin命名空间并部署,通过ClusterRoleBinding为jenkins用户授权集群级权限,生成并获取其t... 目录k8s admin用户生成token创建一个admin的命名空间查看k8s namespace 的

uni-app小程序项目中实现前端图片压缩实现方式(附详细代码)

《uni-app小程序项目中实现前端图片压缩实现方式(附详细代码)》在uni-app开发中,文件上传和图片处理是很常见的需求,但也经常会遇到各种问题,下面:本文主要介绍uni-app小程序项目中实... 目录方式一:使用<canvas>实现图片压缩(推荐,兼容性好)示例代码(小程序平台):方式二:使用uni

Pandas处理缺失数据的方式汇总

《Pandas处理缺失数据的方式汇总》许多教程中的数据与现实世界中的数据有很大不同,现实世界中的数据很少是干净且同质的,本文我们将讨论处理缺失数据的一些常规注意事项,了解Pandas如何表示缺失数据,... 目录缺失数据约定的权衡Pandas 中的缺失数据None 作为哨兵值NaN:缺失的数值数据Panda

java读取excel文件为base64实现方式

《java读取excel文件为base64实现方式》文章介绍使用ApachePOI和EasyExcel处理Excel文件并转换为Base64的方法,强调EasyExcel适合大文件且内存占用低,需注意... 目录使用 Apache POI 读取 Excel 并转换为 Base64使用 EasyExcel 处

Spring Boot中获取IOC容器的多种方式

《SpringBoot中获取IOC容器的多种方式》本文主要介绍了SpringBoot中获取IOC容器的多种方式,包括直接注入、实现ApplicationContextAware接口、通过Spring... 目录1. 直接注入ApplicationContext2. 实现ApplicationContextA

linux查找java项目日志查找报错信息方式

《linux查找java项目日志查找报错信息方式》日志查找定位步骤:进入项目,用tail-f实时跟踪日志,tail-n1000查看末尾1000行,grep搜索关键词或时间,vim内精准查找并高亮定位,... 目录日志查找定位在当前文件里找到报错消息总结日志查找定位1.cd 进入项目2.正常日志 和错误日