【stm32外设】封装在hal库上的串口接收驱动(DMA)

【stm32外设】封装在hal库上的串口接收驱动(DMA)


缘由作者能力有限,不能对实现原理做到好的阐述,故不对实现原理做过多的阐述,想学习可参考学习本文的参考链接。

cubemx配置

这里我们启用DMA发送和接收数据,传输模式都采用normal正常模式。

normal正常模式:当一次DMA数据传输完后,停止DMA传送 ,也就是只传输一次。

circular循环传输模式:当传输结束时,硬件自动会将传输数据量寄存器进行重装,进行下一轮的数据传输。 也就是多次传输模式。

在这里插入图片描述

相关代码

这里实现的思想和使用(非DMA)空闲中断接收数据的思想是一样的,等待一帧数据接收完成后,会产生空闲中断,在中断里处理DMA数据即可。注意事项写在bsp_usart_dma.h文件开头处了。

bsp_usart_dma.c
#include "./usart/bsp_usart_dma.h"

#include <string.h>
#include <stdio.h>


/* 缓存串口接收数据的结构体 */
typedef struct stru_usart
{
	unsigned char buf[USART_DMA_BUF_SIZE];
	unsigned char flag;
	unsigned int  len;
} stru_usart_t;


#if USART1_DMA_EN
	extern DMA_HandleTypeDef hdma_usart1_rx;
	static stru_usart_t usart_dma_1;		/* 缓存串口1接收到的数据 */
#endif

#if USART2_DMA_EN
	extern DMA_HandleTypeDef hdma_usart2_rx;
	static stru_usart_t usart_dma_2;		/* 缓存串口2接收到的数据 */
#endif

#if USART3_DMA_EN
	extern DMA_HandleTypeDef hdma_usart3_rx;
	static stru_usart_t usart_dma_3;		/* 缓存串口3接收到的数据 */
#endif

#if USART4_DMA_EN
	extern DMA_HandleTypeDef hdma_usart4_rx;
	static stru_usart_t usart_dma_4;		/* 缓存串口4接收到的数据 */
#endif

#if USART5_DMA_EN
	extern DMA_HandleTypeDef hdma_usart5_rx;
	static stru_usart_t usart_dma_5;		/* 缓存串口5接收到的数据 */
#endif


/* bsp_usart_dma_init:串口DMA接收初始化函数
 * huart	 	想要初始化的串口的句柄
 * huart_type	想要初始化的串口编号,BSP_USART_DMA
 * return		成功返回0,失败返回-1 */
int bsp_usart_dma_init
	(
	UART_HandleTypeDef 	*huart,
	unsigned char 		huart_type
	)
{
	switch (huart_type)
	{
		/* 串口1 */
		#if USART1_DMA_EN
			case BSP_USART_DMA_1:
				/* 开启DMA接收中断 */
				HAL_UART_Receive_DMA(huart, usart_dma_1.buf, USART_DMA_BUF_SIZE);	
				/* 使能空闲中断 */
				__HAL_UART_ENABLE_IT(huart, UART_IT_IDLE);		
				/* 清除第一次空闲中断标志,方便后续接收数据 */
				__HAL_UART_CLEAR_IDLEFLAG(huart);		
				return 0;
		#endif
			
			/* 串口2 */
		#if USART2_DMA_EN
			case BSP_USART_DMA_2:
				/* 开启DMA接收中断 */
				HAL_UART_Receive_DMA(huart, usart_dma_2.buf, USART_DMA_BUF_SIZE);	
				/* 使能空闲中断 */
				__HAL_UART_ENABLE_IT(huart, UART_IT_IDLE);		
				/* 清除第一次空闲中断标志,方便后续接收数据 */
				__HAL_UART_CLEAR_IDLEFLAG(huart);		
				return 0;
		#endif
			
			/* 串口3 */
		#if USART3_DMA_EN
			case BSP_USART_DMA_3:
				/* 开启DMA接收中断 */
				HAL_UART_Receive_DMA(huart, usart_dma_3.buf, USART_DMA_BUF_SIZE);	
				/* 使能空闲中断 */
				__HAL_UART_ENABLE_IT(huart, UART_IT_IDLE);		
				/* 清除第一次空闲中断标志,方便后续接收数据 */
				__HAL_UART_CLEAR_IDLEFLAG(huart);		
				return 0;
		#endif
			
			/* 串口4 */
		#if USART4_DMA_EN
			case BSP_USART_DMA_4:
				/* 开启DMA接收中断 */
				HAL_UART_Receive_DMA(huart, usart_dma_4.buf, USART_DMA_BUF_SIZE);	
				/* 使能空闲中断 */
				__HAL_UART_ENABLE_IT(huart, UART_IT_IDLE);		
				/* 清除第一次空闲中断标志,方便后续接收数据 */
				__HAL_UART_CLEAR_IDLEFLAG(huart);		
				return 0;
		#endif
			
			/* 串口5 */
		#if USART5_DMA_EN
			case BSP_USART_DMA_5:
				/* 开启DMA接收中断 */
				HAL_UART_Receive_DMA(huart, usart_dma_5.buf, USART_DMA_BUF_SIZE);	
				/* 使能空闲中断 */
				__HAL_UART_ENABLE_IT(huart, UART_IT_IDLE);		
				/* 清除第一次空闲中断标志,方便后续接收数据 */
				__HAL_UART_CLEAR_IDLEFLAG(huart);		
				return 0;
		#endif
	}
	
	return -1;
}


/* bsp_usart_dma_handler:串口接收的中断处理函数
 * huart	 	串口的句柄
 * huart_type	串口编号,BSP_USART_DMA
 * return		成功返回0,失败返回-1 */
int bsp_usart_dma_handler
	(
	UART_HandleTypeDef  *huart,
	unsigned char 		huart_type
	)
{
	switch (huart_type)
	{
		#if USART1_DMA_EN
			case BSP_USART_DMA_1:
				/* 判断是否为空闲中断 */
				if (__HAL_UART_GET_FLAG(huart, UART_FLAG_IDLE) != RESET)	
				{
					/* 清除空闲中断标志,方便后续接收数据 */
					__HAL_UART_CLEAR_IDLEFLAG(huart);	
					/* 停止接收DMA,防止后续数据影响数据的接收 */
					HAL_UART_DMAStop(huart);	
					
					usart_dma_1.flag = 1;
					/* 计算获取总共接收到的buf的长度,__HAL_DMA_GET_COUNTER获取DMA中未传输的数据个数 */
					usart_dma_1.len = USART_DMA_BUF_SIZE - __HAL_DMA_GET_COUNTER(&hdma_usart1_rx);
						
					HAL_UART_Receive_DMA(huart, usart_dma_1.buf, USART_DMA_BUF_SIZE);						
					return 0;
				}
		#endif
		
		#if USART2_DMA_EN
			case BSP_USART_DMA_2:
				/* 判断是否为空闲中断 */
				if (__HAL_UART_GET_FLAG(huart, UART_FLAG_IDLE) != RESET)	
				{
					/* 清除空闲中断标志,方便后续接收数据 */
					__HAL_UART_CLEAR_IDLEFLAG(huart);	
					/* 停止接收DMA,防止后续数据影响数据的接收 */
					HAL_UART_DMAStop(huart);	
					
					usart_dma_2.flag = 1;
					/* 计算获取总共接收到的buf的长度,__HAL_DMA_GET_COUNTER获取DMA中未传输的数据个数 */
					usart_dma_2.len = USART_DMA_BUF_SIZE - __HAL_DMA_GET_COUNTER(&hdma_usart2_rx);
						
					HAL_UART_Receive_DMA(huart, usart_dma_2.buf, USART_DMA_BUF_SIZE);						
					return 0;
				}
		#endif
				
		#if USART3_DMA_EN
			case BSP_USART_DMA_3:
				/* 判断是否为空闲中断 */
				if (__HAL_UART_GET_FLAG(huart, UART_FLAG_IDLE) != RESET)	
				{
					/* 清除空闲中断标志,方便后续接收数据 */
					__HAL_UART_CLEAR_IDLEFLAG(huart);	
					/* 停止接收DMA,防止后续数据影响数据的接收 */
					HAL_UART_DMAStop(huart);	
					
					usart_dma_3.flag = 1;
					/* 计算获取总共接收到的buf的长度,__HAL_DMA_GET_COUNTER获取DMA中未传输的数据个数 */
					usart_dma_3.len = USART_DMA_BUF_SIZE - __HAL_DMA_GET_COUNTER(&hdma_usart3_rx);
						
					HAL_UART_Receive_DMA(huart, usart_dma_3.buf, USART_DMA_BUF_SIZE);						
					return 0;
				}
		#endif
				
		#if USART4_DMA_EN
			case BSP_USART_DMA_4:
				/* 判断是否为空闲中断 */
				if (__HAL_UART_GET_FLAG(huart, UART_FLAG_IDLE) != RESET)	
				{
					/* 清除空闲中断标志,方便后续接收数据 */
					__HAL_UART_CLEAR_IDLEFLAG(huart);	
					/* 停止接收DMA,防止后续数据影响数据的接收 */
					HAL_UART_DMAStop(huart);	
					
					usart_dma_4.flag = 1;
					/* 计算获取总共接收到的buf的长度,__HAL_DMA_GET_COUNTER获取DMA中未传输的数据个数 */
					usart_dma_4.len = USART_DMA_BUF_SIZE - __HAL_DMA_GET_COUNTER(&hdma_usart4_rx);
						
					HAL_UART_Receive_DMA(huart, usart_dma_4.buf, USART_DMA_BUF_SIZE);						
					return 0;
				}
		#endif
				
		#if USART5_DMA_EN
			case BSP_USART_DMA_5:
				/* 判断是否为空闲中断 */
				if (__HAL_UART_GET_FLAG(huart, UART_FLAG_IDLE) != RESET)	
				{
					/* 清除空闲中断标志,方便后续接收数据 */
					__HAL_UART_CLEAR_IDLEFLAG(huart);	
					/* 停止接收DMA,防止后续数据影响数据的接收 */
					HAL_UART_DMAStop(huart);	
					
					usart_dma_5.flag = 1;
					/* 计算获取总共接收到的buf的长度,__HAL_DMA_GET_COUNTER获取DMA中未传输的数据个数 */
					usart_dma_5.len = USART_DMA_BUF_SIZE - __HAL_DMA_GET_COUNTER(&hdma_usart5_rx);
						
					HAL_UART_Receive_DMA(huart, usart_dma_5.buf, USART_DMA_BUF_SIZE);						
					return 0;
				}
		#endif
	}
	
	return -1;
}


/* bsp_usart_dma_get:获取串口接收到的数据的函数(DMA)
 * buf		 	获取到的数据复制到buf里
 * huart_type	串口编号,BSP_USART_DMA
 * return		接收到的数据长度,失败返回-1 */
int bsp_usart_dma_get
	(
	unsigned char buf[],
	unsigned char huart_type
	)
{
	int len = -1;
	switch (huart_type)
	{
		#if USART1_DMA_EN
			case BSP_USART_DMA_1:
				if (usart_dma_1.flag == 1)	/* 判断串口是否有接收到数据 */
				{
					strncpy((char*)buf, (const char*)usart_dma_1.buf, usart_dma_1.len);
					len = usart_dma_1.len;
					memset(&usart_dma_1, 0, sizeof(stru_usart_t));
				}
				break;
		#endif
		
		#if USART2_DMA_EN
			case BSP_USART_DMA_2:
				if (usart_dma_2.flag == 1)	/* 判断串口是否有接收到数据 */
				{
					strncpy((char*)buf, (const char*)usart_dma_2.buf, usart_dma_2.len);
					len = usart_dma_2.len;
					memset(&usart_dma_2, 0, sizeof(stru_usart_t));
				}
				break;
		#endif
				
		#if USART3_DMA_EN
			case BSP_USART_DMA_3:
				if (usart_dma_3.flag == 1)	/* 判断串口是否有接收到数据 */
				{
					strncpy((char*)buf, (const char*)usart_dma_3.buf, usart_dma_3.len);
					len = usart_dma_3.len;
					memset(&usart_dma_3, 0, sizeof(stru_usart_t));
				}
				break;
		#endif
				
		#if USART4_DMA_EN
			case BSP_USART_DMA_4:
				if (usart_dma_4.flag == 1)	/* 判断串口是否有接收到数据 */
				{
					strncpy((char*)buf, (const char*)usart_dma_4.buf, usart_dma_4.len);
					len = usart_dma_4.len;
					memset(&usart_dma_4, 0, sizeof(stru_usart_t));
				}
				break;
		#endif
				
		#if USART5_DMA_EN
			case BSP_USART_DMA_5:
				if (usart_dma_5.flag == 1)	/* 判断串口是否有接收到数据 */
				{
					strncpy((char*)buf, (const char*)usart_dma_5.buf, usart_dma_5.len);
					len = usart_dma_5.len;
					memset(&usart_dma_5, 0, sizeof(stru_usart_t));
				}
				break;
		#endif
	}
	
	return len;
}
bsp_usart_dma.h
/**
 * author:临木木
 * 适用于stm32裸机的usart接收驱动(DMA)
 * 主程序启用前运行bsp_usart_dma_init()函数初始化串口配置
 * bsp_usart_dma_handler() 需要放到#include "stm32f1xx_it.c"文件的USARTx_IRQHandler()函数里
 *
 * 使用HAL_UART_Transmit_DMA()前需判断所需要的串口是否空闲,再继续发送新的数据,不然会导致输出的数据会不对
 * 例:  while ((&huart1)->gState == HAL_UART_STATE_BUSY_TX);
 *		HAL_UART_Transmit_DMA(&huart1, "hello stm32", strlen("hello stm32"));
 */
#ifndef __BSP_USART_DMA_H__
#define __BSP_USART_DMA_H__

#include "usart.h"


#define USART_DMA_BUF_SIZE	256

/* 启用哪个置1即可,未启用,不应该置1,会报错 */
#define USART1_DMA_EN	1
#define USART2_DMA_EN	0
#define USART3_DMA_EN	0
#define USART4_DMA_EN	0
#define USART5_DMA_EN	0

/* huart_type */
enum BSP_USART_DMA
{
	BSP_USART_DMA_1,
	BSP_USART_DMA_2,
	BSP_USART_DMA_3,
	BSP_USART_DMA_4,
	BSP_USART_DMA_5
};


int bsp_usart_dma_init
	(
	UART_HandleTypeDef 	*huart,
	unsigned char 		huart_type
	);

int bsp_usart_dma_handler
	(
	UART_HandleTypeDef  *huart,
	unsigned char 		huart_type
	);

int bsp_usart_dma_get
	(
	unsigned char buf[],
	unsigned char huart_type
	);


#endif
例子
/* 初始化 */
bsp_usart_dma_init(&huart1, BSP_USART_DMA_1);
// 如果在初始化后立即使用HAL_UART_Transmit_DMA()输出,需使用延时函数等待,否则会报错,不知道问什么
// HAL_Delay(0);
// HAL_UART_Transmit_DMA(&huart1, "hello stm32", strlen("hello stm32"));

/* 接收数据 */
static int len = 0;
static unsigned char buf[256];
len = bsp_usart_dma_get(buf, BSP_USART_DMA_1);
if (len != -1)
{
    /* 等待串口空闲再发送数据 */
	while ((&huart1)->gState == HAL_UART_STATE_BUSY_TX);
    HAL_UART_Transmit_DMA(&huart1, buf, len);
}
参考链接
  • 8
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: STM32F1_HAL库使用手册文件是STM32F1系列单片机的编程接口及其应用程序的软件开发包。该手册具有非常详细的介绍和说明,包括HAL库的功能、使用方法、配置及编译软件等多个方面。 首先,该手册详细讲解了STM32F1系列单片机的基本知识,如器件特性、体系结构和系统时钟等。其次,该手册介绍了HAL库的结构、API(应用程序接口)函数,文件和数据类型,并通过代码示例来演示如何使用HAL库进行应用程序的开发。此外,该手册还列出了各类功能实现的驱动库函数。 在使用HAL库进行开发时,手册中提供了充足的文献资料,可以帮助开发人员解决各种问题,例如在编写驱动程序时如何使用HAL中的定时计数器、串口转换器和DMA等。手册中还提供了各种应用示例,如PWM控制、定时器中断、SPI通讯和ADC采集等等,展示了HAL库在各种应用场景下的使用方法。 最后,该手册中还包含了硬件抽象层标准库的配置工具,如CubeMX和STM32工具箱,用于帮助开发人员更为快速地进行底层开发工作,降低了开发成本和时间成本。 综上所述,STM32F1系列单片机HAL库使用手册文件是非常重要的开发工具。它深入浅出地解释了HAL库如何应用于STM32F1系列单片机开发。对于开发人员来说,熟练掌握该手册的内容,可以促进开发过程,提高开发效率。 ### 回答2: stm32f1_hal库是ST公司提供的一种硬件抽象层的库文件,支持对于STM32F1系列的微控制器进行控制并输出操作。这个编程库文件非常适合初学者或者是想要快速地进行STM32F1系列微控制器开发的程序员,因为它提供一种抽象的、高级的、更易于理解的方式来写代码。同时,stm32f1_hal库的使用手册文件非常重要,因为它是学习和使用stm32f1_hal库的关键,提供了详细的操作指南和样例代码。 stm32f1_hal库使用手册文件包含五个章节:库的概述、库的安装、库的使用、库的例子和库中的附录。第一个章节介绍了stm32f1_hal库的主要特性,这些特性包括高级的外设驱动、支持中斷实时和低功耗模式、易于使用和理解的API等等。 第二个章节讲解了如何在STM32F1系列微控制器中安装该库,此处需要注意的是不同的开发环境安装该库的方法可能不同。 第三个章节是重点所在,讲解了常见的库API及其使用方法,例如初始化外设、读取和写入数据。此处需要注意的是,代码中所调用的API需要根据不同的外设进行调整。 第四个章节列出了一些常见的例子,展示了如何使用stm32f1_hal库来实现不同的功能,例如LED、串口通信、时钟控制等等。 最后一个章节中提供了附录中的内容,为在实际开发中对库API的使用提供支持。总之,stm32f1_hal库使用手册文件是使用STM32F1微控制器开发的重要参考书,值得认真参考。 ### 回答3: stm32f1_hal库STM32F1系列的外设驱动库。使用手册文件提供了完整的、详细的说明,包含了使用方法、函数及其参数的解释、编程范例等,是程序员使用stm32f1_hal库的重要参考资料。 手册文件主要介绍了STM32F1系列芯片的存储器、时钟、GPIO、中断、USART、SPI、I2C、DMA、ADC、DAC等各种外设的使用方法。通过手册,用户可以了解到如何对寄存器进行初始化,使外设工作正常。手册还提供了各种编程范例,程序员可以根据自己的需求进行调整和优化,大大提高了开发效率。 需要特别提醒的一点是,由于HAL库是由ST官方提供的驱动库,所以不同芯片的HAL库会有一些差别,用户在选择芯片型号后,务必下载对应的版本的手册。另外,由于HAL库是基于底层库的封装,对于一些特殊的需求,或者需要更高的性能的场合,程序员也可以直接使用底层库进行编程。 总之,stm32f1_hal库使用手册文件是STM32F1系列的外设驱动库的重要参考资料。对于初学者来说,掌握使用手册,能够快速地编写STM32F1的应用程序;对于有经验的开发者,可以通过更深入的阅读和理解手册,更好地优化应用程序,提高应用程序的稳定性和性能。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值