使用STM32 HAL库实现串口通信的中断和DMA控制

一.何为串口通信协议

串口通信指两个或两个以上的设备使用串口按位(bit)发送和接收字节。可以在使用一根线发送数据的同时用另一根线接收数据。 串口通信协议就是串口通讯时共同遵循的协议。 协议的内容是每一个bit 所代表的意义。 常用的串口通信协议 有以下几种
1 . RS-232(ANSI/EIA-232标准) 只支持 点对点, 最大距离 50英尺。最大速度为128000bit/s, 距离越远 速度越慢。 支持全双工(发送同时也可接收)。
2 . RS-422(EIA RS-422-AStandard),支持点对多一条平衡总线上连接最多10个接收器 将传输速率提高到10Mbps,传输距离延长到4000英尺(约1219米),所以在100kbps速率以内,传输距离最大。支持全双工(发送同时也可接收)。
3 .RS-485(EIA-485标准)是RS-422的改进, 支持多对多(2线连接),从10个增加到32个,可以用超过4000英尺的线进行串行通行。速率最大10Mbps。支持全双工(发送同时也可接收)。2线连接时 是半双工状态。

二.RS-232

RS-232标准接口(又称EIA RS-232)是常用的串行通信接口标准之一,它是由美国电子工业协会(EIA)联合贝尔系统公司、调制解调厂家及计算机终端生产厂家于1970年共同制定,其全名是“数据终端设备( DTE)和数据通信设备(DCE)之间串行二进制数据交换接口技术标准”。

三.TTL电平与RS232电平的区别

什么是TTL电平、RS232电平?它们有什么区别呢?

(一)、TTL电平标准

输出 L: <0.8V ; H:>2.4V。

输入 L: <1.2V ; H:>2.0V

TTL器件输出低电平要小于0.8V,高电平要大于2.4V。输入,低于1.2V就认为是0,高于2.0就认为是1。于是TTL电平的输入低电平的噪声容限就只有(0.8-0)/2=0.4V,高电平的噪声容限为(5-2.4)/2=1.3V。

(二)、RS232标准

在TXD和RXD数据线上:
  (1)逻辑1为-3~-15V的电压
  (2)逻辑0为3~15V的电压
在RTS、CTS、DSR、DTR和DCD等控制线上:
  (1)信号有效(ON状态)为3~15V的电压
  (2)信号无效(OFF状态)为-3~-15V的电压
这是由通信协议RS-232规定的。
RS-232:标准串口,最常用的一种串行通讯接口。有三种类型(A,B和C),它们分别采用不同的电压来表示on和off。最被广泛使用的是RS-232C,它将mark(on)比特的电压定义为-3V到-12V之间,而将space(off)的电压定义到+3V到+12V之间。传送距离最大为约15米,最高速率为20kb/s。RS-232是为点对点(即只用一对收、发设备)通讯而设计的,其驱动器负载为3~7kΩ。所以RS-232适合本地设备之间的通信。

四、了解“USB/TTL转232“模块(以CH340芯片模块为例)的工作原理。

CH340 是一个USB 总线的转接芯片,实现USB 转串口、USB 转IrDA 红外或者USB 转打印口。为了增加串口通讯的远距离传输及抗干扰能力,RS-232标准使用-15V 表示逻辑 1, +15V 表示逻辑 0。常常会使用 MH340芯片对 USB/TTL与RS-232电平的信号进行转换。
CH340模块介绍:
CH340电路与实物图:

五、利用HAL库新建一个中断控制串口通信的工程

(1)打开STM32CubeMX,在主界面点击:ACCESS TO MCU SELECTOR:

(2)选择的单片机型号以及点击开始工程项目:

(3)配置GPIO:PA0。如果仅仅是完成串口通信的话,这一步可以跳过。但是根据实验要求,为了区分串口通信的开启与关闭,要使用一个LED灯来显示。当串口通信开启(STM32向电脑发送信息)的时候,LED灯亮,当串口通信关闭(STM32停止向电脑发送消息)的时候,LED灯灭。

(4)配置USART1,我们使用USART1进行数据传输。在这个界面按下图进行配置。我们对USART1的配置要做的只有两件事:一是选择串口工作模式为异步,二是开启USART1全局中断

(5)进入Project Manager(工程管理),进行工程设置点击生成工程与代码:

注意:路径不能包含中文和空格,不然生成的工程文件无法在Keil中打开;

六、完善通过中断方式控制串口通信的keil5工程

回到main.c文件中,详细编写主要代码:

(1:设置接收中断,函数原型简介:

HAL_UART_Receive_IT(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size)
  UART_HandleTypeDef *huart      UATR的别名    
 huart1  *pData      			接收到的数据存放地址
 Size                      		接收的字节数

main里面的while(1)替换为如下信息接收与发送处理代码:

        if(flag==0)
		{
			HAL_GPIO_WritePin(GPIOA,GPIO_PIN_0,GPIO_PIN_SET);
		}
		else if(flag==1)
		{					
			HAL_GPIO_WritePin(GPIOA,GPIO_PIN_0,GPIO_PIN_RESET);
		    uint8_t hello[20]="hello windows!\n";
		    HAL_UART_Transmit_IT(&huart1,hello,20);
		    HAL_Delay(600);
		}

在main函数下面重写中断处理函数

void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{
	//当输入的指令为“stop!"时,发送提示并改变flag=0
	if(strEqual(rcData,"stop!"))
	{
		flag=0;
	}
	
	//当输入的指令为"start"时,发送提示并改变flag=1
	else if(strEqual(rcData,"start"))
	{
		flag=1;
	}
	//重新设置中断
    HAL_UART_Receive_IT(&huart1,(uint8_t*)rcData,5);
}

完善之后的main.c的全部代码编写如下所示:

/* USER CODE BEGIN Header */
/**
  ******************************************************************************
  * @file           : main.c
  * @brief          : Main program body
  ******************************************************************************
  * @attention
  *
  * Copyright (c) 2023 STMicroelectronics.
  * All rights reserved.
  *
  * This software is licensed under terms that can be found in the LICENSE file
  * in the root directory of this software component.
  * If no LICENSE file comes with this software, it is provided AS-IS.
  *
  ******************************************************************************
  */
/* USER CODE END Header */
/* Includes ------------------------------------------------------------------*/
#include "main.h"
#include "usart.h"
#include "gpio.h"
 
/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */
 
/* USER CODE END Includes */
 
/* Private typedef -----------------------------------------------------------*/
/* USER CODE BEGIN PTD */
 
/* USER CODE END PTD */
 
/* Private define ------------------------------------------------------------*/
/* USER CODE BEGIN PD */
 
/* USER CODE END PD */
 
/* Private macro -------------------------------------------------------------*/
/* USER CODE BEGIN PM */
 
/* USER CODE END PM */
 
/* Private variables ---------------------------------------------------------*/
 
/* USER CODE BEGIN PV */
 
/* USER CODE END PV */
 
/* Private function prototypes -----------------------------------------------*/
void SystemClock_Config(void);
/* USER CODE BEGIN PFP */
 
/* USER CODE END PFP */
 
/* Private user code ---------------------------------------------------------*/
/* USER CODE BEGIN 0 */
 
/* USER CODE END 0 */
int strEqual(char rcData[6],char rcData2[6]){
	for(uint8_t i = 0 ; i < 6 ; i++){
		if (rcData[i] != rcData2[i]) return 0;
	}
	return 1;
}
 
char rcData[6] = "start";
uint8_t flag=1;
 
int main(void)
{
  	HAL_Init();
  	SystemClock_Config();
  	MX_GPIO_Init();
  	MX_USART1_UART_Init();
 
	HAL_UART_Receive_IT(&huart1,(uint8_t*)rcData,5);
	
  	while (1)
  	{
		if(flag==0)
		{
			HAL_GPIO_WritePin(GPIOA,GPIO_PIN_0,GPIO_PIN_SET);
		}
		else if(flag==1)
		{					
			HAL_GPIO_WritePin(GPIOA,GPIO_PIN_0,GPIO_PIN_RESET);
		    uint8_t hello[20]="hello windows!\n";
		    HAL_UART_Transmit_IT(&huart1,hello,20);
		    HAL_Delay(600);
		}
  }
}
 
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{
	//当输入的指令为“stop!"时,发送提示并改变flag=0
	if(strEqual(rcData,"stop!"))
	{
		flag=0;
	}
	
	//当输入的指令为"start"时,发送提示并改变flag=1
	else if(strEqual(rcData,"start"))
	{
		flag=1;
	}
	//重新设置中断
    HAL_UART_Receive_IT(&huart1,(uint8_t*)rcData,5);
}
/**
  * @brief System Clock Configuration
  * @retval None
  */
void SystemClock_Config(void)
{
  RCC_OscInitTypeDef RCC_OscInitStruct = {0};
  RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};
  /** Initializes the RCC Oscillators according to the specified parameters
  * in the RCC_OscInitTypeDef structure.
  */
  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();
  }
  /** Initializes the CPU, AHB and APB buses clocks
  */
  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();
  }
}
/* USER CODE BEGIN 4 */
/* USER CODE END 4 */
/**
  * @brief  This function is executed in case of error occurrence.
  * @retval None
  */
void Error_Handler(void)
{
  /* USER CODE BEGIN Error_Handler_Debug */
  /* User can add his own implementation to report the HAL error return state */
  __disable_irq();
  while (1)
  {
  }
  /* USER CODE END Error_Handler_Debug */
}
#ifdef  USE_FULL_ASSERT
/**
  * @brief  Reports the name of the source file and the source line number
  *         where the assert_param error has occurred.
  * @param  file: pointer to the source file name
  * @param  line: assert_param error line source number
  * @retval None
  */
void assert_failed(uint8_t *file, uint32_t line)
{
  /* USER CODE BEGIN 6 */
  /* User can add his own implementation to report the file name and line number,
     ex: printf("Wrong parameters value: file %s on line %d\r\n", file, line) */
  /* USER CODE END 6 */
}
#endif /* USE_FULL_ASSERT */

七、1、电路连接:

USB转TTL与STM32的连接,参考下图:

点击FLYMCU的开始编程(P),接着马上点击STM32最小系统板子的复位键即可完成烧录:

第一步:

烧录成功示意图:

运行结果演示:
打开串口,并且同时点击STM32最小系统板子的复位键即可开始运行:

注:输入“start”:让STM32单片机继续向电脑发送信息;

输入“stop!”:让STM32单片机停止向电脑发送消息 ;

dma

八、基于中断控制串口通信的keil5仿真调试

1、进入keil5仿真:
(1)点击第一步,Target界面中,选择跟正确的晶振大小,使用8MHz的外部晶振:
(2)接着进行Debug页的设置:

(3)点击图示圈住的地方进入仿真调试界面:

(4)选择逻辑分析仪:

(5)点击Setup设置添加要进行观察的引脚波形信息:

波形信息配置:添加波形信息的时候,如果自己选择的串口配置的是USART1,就在添加信息里面输入:USART1_RS;

仿真结果:

九、利用HAL库新建一个DMA控制串口通信的工程

1.设置RCC

2.设置USART1
选择异步通信
参数选择默认

使能串口

添加两个通道

3.创建项目


3、完善keil5工程代码:
(1)在main.c文件中,详细编写主要代码:

void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{
	//当输入的指令为“stop!"时,发送提示并改变flag=0
	if(strEqual(rx_buf,"stop!"))
	{
		flag=0;
	}
	
	//当输入的指令为"start"时,发送提示并改变flag=1
	else if(strEqual(rx_buf,"start"))
	{
		flag=1;
	}
	HAL_UART_Receive_DMA(&huart1,(uint8_t*)rx_buf,5);
}

(2)完善之后的main.c的全部代码编写如下所示:

/* USER CODE BEGIN Header */
/**
  ******************************************************************************
  * @file           : main.c
  * @brief          : Main program body
  ******************************************************************************
  * @attention
  *
  * Copyright (c) 2023 STMicroelectronics.
  * All rights reserved.
  *
  * This software is licensed under terms that can be found in the LICENSE file
  * in the root directory of this software component.
  * If no LICENSE file comes with this software, it is provided AS-IS.
  *
  ******************************************************************************
  */
/* USER CODE END Header */
/* Includes ------------------------------------------------------------------*/
#include "main.h"
#include "dma.h"
#include "usart.h"
#include "gpio.h"
 
/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */
 
/* USER CODE END Includes */
 
/* Private typedef -----------------------------------------------------------*/
/* USER CODE BEGIN PTD */
 
/* USER CODE END PTD */
 
/* Private define ------------------------------------------------------------*/
/* USER CODE BEGIN PD */
 
/* USER CODE END PD */
 
/* Private macro -------------------------------------------------------------*/
/* USER CODE BEGIN PM */
 
/* USER CODE END PM */
 
/* Private variables ---------------------------------------------------------*/
 
/* USER CODE BEGIN PV */
 
/* USER CODE END PV */
 
/* Private function prototypes -----------------------------------------------*/
void SystemClock_Config(void);
/* USER CODE BEGIN PFP */
 
/* USER CODE END PFP */
 
/* Private user code ---------------------------------------------------------*/
/* USER CODE BEGIN 0 */
 
/* USER CODE END 0 */
uint8_t flag=1;
uint8_t rx_buf[6];//接收串口数据存放的数组
 
int strEqual(char rcData[6],char rcData2[6])
	{
	for(uint8_t i = 0 ; i < 6 ; i++){
		if (rcData[i] != rcData2[i]) return 0;
	}
	return 1;
}
 
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{
	//当输入的指令为“stop!"时,发送提示并改变flag=0
	if(strEqual(rx_buf,"stop!"))
	{
		flag=0;
	}
	
	//当输入的指令为"start"时,发送提示并改变flag=1
	else if(strEqual(rx_buf,"start"))
	{
		flag=1;
	}
	HAL_UART_Receive_DMA(&huart1,(uint8_t*)rx_buf,5);
}
/**
  * @brief  The application entry point.
  * @retval int
  */
int main(void)
{
  HAL_Init();
  uint8_t message[] = "hello windows!\n";  //定义数据发送数组
  SystemClock_Config();
  MX_GPIO_Init();
  MX_DMA_Init();
  MX_USART1_UART_Init();
  HAL_UART_Receive_DMA(&huart1,(uint8_t*)rx_buf,5);//设置DMA接收到的数据存放在rx_buf中
  while (1)
  {
      if(flag==1)
	  {
	    HAL_UART_Transmit_DMA(&huart1, (uint8_t *)message, sizeof(message));
	    HAL_Delay(600);
	  }
  }
}
/**
  * @brief System Clock Configuration
  * @retval None
  */
void SystemClock_Config(void)
{
  RCC_OscInitTypeDef RCC_OscInitStruct = {0};
  RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};
  /** Initializes the RCC Oscillators according to the specified parameters
  * in the RCC_OscInitTypeDef structure.
  */
  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();
  }
  /** Initializes the CPU, AHB and APB buses clocks
  */
  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();
  }
}
/* USER CODE BEGIN 4 */
/* USER CODE END 4 */
/**
  * @brief  This function is executed in case of error occurrence.
  * @retval None
  */
void Error_Handler(void)
{
  /* USER CODE BEGIN Error_Handler_Debug */
  /* User can add his own implementation to report the HAL error return state */
  __disable_irq();
  while (1)
  {
  }
  /* USER CODE END Error_Handler_Debug */
}
#ifdef  USE_FULL_ASSERT
/**
  * @brief  Reports the name of the source file and the source line number
  *         where the assert_param error has occurred.
  * @param  file: pointer to the source file name
  * @param  line: assert_param error line source number
  * @retval None
  */
void assert_failed(uint8_t *file, uint32_t line)
{
  /* USER CODE BEGIN 6 */
  /* User can add his own implementation to report the file name and line number,
     ex: printf("Wrong parameters value: file %s on line %d\r\n", file, line) */
  /* USER CODE END 6 */
}
#endif /* USE_FULL_ASSERT */

十、基于DMA方式控制串口通信的电路连接与烧录运行

运行结果演示:
打开串口,并且同时点击STM32最小系统板子的复位键即可开始运行:

注:输入“start”:让STM32单片机继续向电脑发送信息;

输入“stop!”:让STM32单片机停止向电脑发送消息 ;

dma

十一、基于DMA控制串口通信的keil5仿真调试

1、进入keil5仿真与开始仿真:
与之前一样

2、仿真结果演示:


总结:
虽然在实验中遇到了很多问题,但通过同学的指导最后也是完成了这次的实验。

作者:我是麻瓜387

物联沃分享整理
物联沃-IOTWORD物联网 » 使用STM32 HAL库实现串口通信的中断和DMA控制

发表评论