HAL库中断与DMA的串口通信大师

HAL库中断与DMA的串口通信大师

一、DMA介绍

直接存储器存取(DMA)是一种数据传输技术,它可以提供外设与存储器或存储器与存储器之间的高速数据传输,无需CPU干预,从而节省了CPU的资源。在STM32F103C8T6微控制器中,DMA资源分为DMA1和DMA2,其中DMA1拥有7个通道可供配置。

DMA1通过这些通道可以实现并行的数据传输,每个通道都支持软件触发和特定的硬件触发。这意味着可以通过编程的方式或通过外部硬件信号来触发DMA传输。这些通道的独立性使得多个外设或存储器之间可以同时进行数据传输,提高了系统性能和效率。

使用DMA技术可以有效地减少CPU的负载,因为CPU无需实时参与数据传输过程,可以将更多的时间用于执行其他任务。此外,DMA还可以实现高速数据传输,大大提升了系统的响应速度和数据处理能力。

DMA框图如下:

在这里插入图片描述

二、中断模式实现串口通信

在上周的实验中我们已经初步了解过了串口通信,接下来就需要我们在原有代码上进行提高和修改了。

1. 创建工程

首先打开STM32CubeMX,在主界面点击新建工程

在这里插入图片描述

随后在新弹出的界面中搜索STM32F103C8,并点击对应芯片。

在这里插入图片描述

开启USART1串口,设置为异步通信,并设置USART1中断

在这里插入图片描述

然后点击“project manager",选择”project“,写入工程名字以及文件路径。编辑器选择MDK-ARM
在这里插入图片描述

然后点击”code generator“,选择以下选项
在这里插入图片描述

然后点击"GENERATE CODE",就可以生成工程了
在这里插入图片描述

2. 代码编写

不同于上次实验的是,本次实验要求发送一个字符串来控制发送。我们需要定义一个接受数组,利用数组接收从电脑端发来的消息,并对其进行处理与判断。

根据以上思路,我们在main.c中编写字符串比较函数。

int stringcompare(char str1[6],char str2[6])
	{
		uint8_t i=0;
	
		for(i = 0 ; i < 6 ; i++)
		{
		if (str1[i] != str2[i]) 
			
			return 0;//如果输入数据与原有数据不符,返回0
	}
	
	return 1;//如果位数全部相同,则返回1
}

主函数如下:

char start[6] = "start";
uint8_t flag=2;//设置标志位
 
int main(void)
{
  	HAL_Init();
  	SystemClock_Config();
  	MX_GPIO_Init();
  	MX_USART1_UART_Init();
	  uint8_t hello[20]="Hello,Windows!\r\n";
	  HAL_UART_Receive_IT(&huart1,(uint8_t*)start,5);
	
  	while (1)
  	{
		
		 if(flag==1)//标志位为1,发送hellowindows
		{					
		    HAL_UART_Transmit_IT(&huart1,hello,20);
		    HAL_Delay(1000);
		}
		else if(flag==0)//标志位为0,停止发送.
		{
			flag=3;
		}
	
  }
}

中断控制函数(HAL_UART_Receive_IT(&huart1,(uint8_t*)start,5)函数是HAL库自动配置的串口中断函数,在接收到电脑端发送的字符后自动触发串口中断,并执行中断程序的代码。):

void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{

	if(stringcompare(start,"stop!"))//输入为stop时,标志位置0
	{
		flag=0;
		
	}
	

	else if(stringcompare(start,"start"))//输入为 start时,标志位置1
	{
		flag=1;
	}
	
	//重新设置中断
    HAL_UART_Receive_IT(&huart1,(uint8_t*)start,5);
}

完整代码如下

#include "main.h"
#include "usart.h"
#include "gpio.h"
 

 
void SystemClock_Config(void);

int stringcompare(char str1[6],char str2[6])//这是字符串比较函数
{
	for(uint8_t i = 0 ; i < 6 ; i++)
    {
		if (str1[i] != str2[i]) 
            return 0;
	}
	return 1;
}
 
char start[6] = "start";//初始的字符串,默认为发送
uint8_t flag=3;//初始标志位
uint8_t hello[20]="Hello,Windows!";//需要发送的字符串
 
int main(void)
{
  	HAL_Init();
  	SystemClock_Config();
  	MX_GPIO_Init();
  	MX_USART1_UART_Init();
	  HAL_UART_Receive_IT(&huart1,(uint8_t*)start,5);
	
  	while (1)
  	{
		
		 if(flag==1)//开始发送
		{					
		    HAL_UART_Transmit_IT(&huart1,hello,20);
		    HAL_Delay(1000);
		}
		else if(flag==0)
		{
			flag=3;//停止发送
		}
  }
}
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)//中断回调函数
{

	if(stringcompare(start,"stop!"))//如果发送的是stop!,则停止发送字符串
	{
		flag=0;
	}
	else if(stringcompare(start,"start"))//如果发送的是start,则继续发送
	{
		flag=1;
	}
    HAL_UART_Receive_IT(&huart1,(uint8_t*)start,5);//再次设置中断,等待下一个字符串
}
void SystemClock_Config(void)
{
  RCC_OscInitTypeDef RCC_OscInitStruct = {0};
  RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};

  RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSI;
  RCC_OscInitStruct.HSIState = RCC_HSI_ON;
  RCC_OscInitStruct.HSICalibrationValue = RCC_HSICALIBRATION_DEFAULT;
  RCC_OscInitStruct.PLL.PLLState = RCC_PLL_NONE;
  if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK)
  {
    Error_Handler();
  }
  RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK
                              |RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2;
  RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_HSI;
  RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
  RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV1;
  RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1;
  if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_0) != HAL_OK)
  {
    Error_Handler();
  }
}

void Error_Handler(void)
{

  __disable_irq();
  while (1)
  {
  }
  
}
#ifdef  USE_FULL_ASSERT

  
void assert_failed(uint8_t *file, uint32_t line)
{

}
#endif 



	

3.3 烧录并测试结果

我们打开野火串口助手,设置参数如下:

在这里插入图片描述

然后点击打开串口,显示结果如下:

在这里插入图片描述

三、DMA模式实现串口通信

1. 工程的建立

DMA工程只需要在原有的基础上加上了一些参数而已。在原有中断配置的基础上,打开USART1的DMA Settings选项,在栏中进行添加

在这里插入图片描述

在这里插入图片描述

将USART1_RX和USART1_TX全部添加进来就可以了。

2. 主函数编写

编程思路跟中断方法类似,只是我们所用的函数不一样。原有的中断触发相关函数全部改为DMA函数,如下所示


 HAL_StatusTypeDef HAL_UART_Transmit_DMA(UART_HandleTypeDef *huart, const uint8_t *pData, uint16_t Size)//DMA方式下的发送函数
 
 HAL_StatusTypeDef HAL_UART_Receive_DMA(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size)//DMA模式下触发中断函数

那么主函数代码编写如下:


#include "main.h"
#include "usart.h"
#include "gpio.h"
 

 
void SystemClock_Config(void);

int stringcompare(char str1[6],char str2[6])//这是字符串比较函数
{
	for(uint8_t i = 0 ; i < 6 ; i++)
    {
		if (str1[i] != str2[i]) 
            return 0;
	}
	return 1;
}
 
char start[6] = "start";//初始的字符串,默认为发送
uint8_t flag=3;//初始标志位
uint8_t hello[20]="Hello,Windows!";//需要发送的字符串
 
int main(void)
{
  	HAL_Init();
  	SystemClock_Config();
  	MX_GPIO_Init();
  	MX_USART1_UART_Init();
	  HAL_UART_Receive_DMA(&huart1,(uint8_t*)start,5);//原来这里是IT,现在换成了DMA
	
  	while (1)
  	{
		
		 if(flag==1)//开始发送
		{					
		    HAL_UART_Transmit_DMA(&huart1,hello,20);
		    HAL_Delay(1000);
		}
		else if(flag==0)
		{
			flag=3;//停止发送
		}
  }
}
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)//中断回调函数
{

	if(stringcompare(start,"stop!"))//如果发送的是stop!,则停止发送字符串
	{
		flag=0;
	}
	else if(stringcompare(start,"start"))//如果发送的是start,则继续发送
	{
		flag=1;
	}
    HAL_UART_Receive_DMA(&huart1,(uint8_t*)start,5);//再次设置中断,等待下一个字符串
}
void SystemClock_Config(void)
{
  RCC_OscInitTypeDef RCC_OscInitStruct = {0};
  RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};

  RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSI;
  RCC_OscInitStruct.HSIState = RCC_HSI_ON;
  RCC_OscInitStruct.HSICalibrationValue = RCC_HSICALIBRATION_DEFAULT;
  RCC_OscInitStruct.PLL.PLLState = RCC_PLL_NONE;
  if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK)
  {
    Error_Handler();
  }
  RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK
                              |RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2;
  RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_HSI;
  RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
  RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV1;
  RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1;
  if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_0) != HAL_OK)
  {
    Error_Handler();
  }
}

void Error_Handler(void)
{

  __disable_irq();
  while (1)
  {
  }
  
}
#ifdef  USE_FULL_ASSERT

  
void assert_failed(uint8_t *file, uint32_t line)
{

}
#endif 

3. 烧录并测试结果

在这里插入图片描述

四、总结

实验中,我们首先配置了USART1的相关参数,并初始化USART1。然后,我们使用中断方式和DMA方式分别实现了串口数据的发送和接收。在中断方式中,我们通过配置USART1的中断使能,并在中断处理函数中判断接收到的字符是否为"stop"或"start",从而控制是否发送数据。在DMA方式中,我们配置了USART1的DMA传输,并设置DMA的数据长度和传输方向。通过启动DMA传输,在接收到"stop"或"start"字符之后,我们通过修改字符来控制是否发送数据。

总体而言,通过这个实验,我再次感受到HAL库的简单便捷。无论是配置还是后期编程,HAL库总是能够系统性地帮我们先配置好模块功能,让我们更容易去上手。

五、参考

https://blog.csdn.net/weixin_66084944/article/details/133913040

  • 3
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
在使用STM32 HAL进行串口通信时,可以使用中断接收和DMA发送的方式来提高通信效率。 首先需要初始化串口,并配置接收中断DMA发送。以下是一个示例代码: ``` UART_HandleTypeDef huart1; void MX_USART1_UART_Init(void) { huart1.Instance = USART1; huart1.Init.BaudRate = 115200; huart1.Init.WordLength = UART_WORDLENGTH_8B; huart1.Init.StopBits = UART_STOPBITS_1; huart1.Init.Parity = UART_PARITY_NONE; huart1.Init.Mode = UART_MODE_TX_RX; huart1.Init.HwFlowCtl = UART_HWCONTROL_NONE; huart1.Init.OverSampling = UART_OVERSAMPLING_16; if (HAL_UART_Init(&huart1) != HAL_OK) { Error_Handler(); } /* Enable the UART Parity Error Interrupt */ HAL_UART_Receive_IT(&huart1, rxBuffer, 1); /* Enable the DMA transfer for transmit */ HAL_UART_Transmit_DMA(&huart1, txBuffer, strlen((char *)txBuffer)); } void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart) { if (huart == &huart1) { /* process received data */ HAL_UART_Receive_IT(&huart1, rxBuffer, 1); } } void HAL_UART_TxCpltCallback(UART_HandleTypeDef *huart) { if (huart == &huart1) { /* transmit completed */ } } void HAL_UART_ErrorCallback(UART_HandleTypeDef *huart) { if (huart == &huart1) { /* handle UART error */ } } ``` 在上面的代码中,`USART1`是串口的实例,`rxBuffer`和`txBuffer`是接收和发送缓冲区。在串口初始化时,使用`HAL_UART_Receive_IT`函数开启接收中断,并使用`HAL_UART_Transmit_DMA`函数开启DMA发送。在接收中断回调函数`HAL_UART_RxCpltCallback`中,可以对接收到的数据进行处理,并继续接收下一个字节。在发送完成回调函数`HAL_UART_TxCpltCallback`中,可以进行一些操作,例如将发送缓冲区中的数据更新,等待下一次发送。在出现UART错误时,`HAL_UART_ErrorCallback`函数会被调用,可以在该函数中处理错误。 需要注意的是,在使用DMA发送时,需要保证发送缓冲区的数据不会被修改,直到DMA发送完成。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值