STM32—HAL库中断/DMA控制和完成串口通信

这篇具有很好参考价值的文章主要介绍了STM32—HAL库中断/DMA控制和完成串口通信。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

目录

一、解决的问题

二、串口通讯协议和RS-232的介绍以及USB/TTL转232模块的工作原理 

 1、 串口协议和RS-232标准:

 (1)串口协议:

(2)RS-232 标准: 

 2、RS232电平与TTL电平的区别 

 3、USB/TTL转232“模块(CH340芯片为例) 

(1)基本原理: 

(2)CH340模块介绍:

​三、搭建STM32开发环境(HAL库环境)

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

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

 (1)本工程中几个函数简介:

(2)编写代码思路: 

 (3)完善keil5工程代码:

六、基于中断控制串口通信的电路连接与烧录运行

 1、电路连接:

 2、 USB转TTL环境配置:

 3、下载烧录软件与串口通信软件: 

 4、keil5工程里面对于USB转TTL的配置:

​5、编译生产hex文件,用于后面的烧录步骤:

​6、烧录:

7、配置XCOM,打开XCOM软件,按下图所示进行配置:

​8、运行结果演示:

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

 1、进入keil5仿真:

 2、开始仿真:

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

九、完善通过DMA方式控制串口通信的keil5工程

1、 本工程中的几个函数简介:

2、代码编写思路

3、完善keil5工程代码:

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

1、电路的连接、软件的下载以及环境的配置:

2、运行结果演示: 

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

 1、进入keil5仿真与开始仿真:

2、仿真结果演示:​

十二、总结

十三、参考资料


一、解决的问题

     1、使用HAL库(或标准库)方式,设置USART1 波特率为115200,1位停止位,无校验位,分别采用中断方式、DMA方式完成下列任务:

     STM32系统给上位机(win10)连续发送“hello windows!”;当上位机给stm32发送字符“stop”后,stm32暂停发送“hello windows!”;发送一个字符“start”后,stm32继续发送;

    2、 在没有示波器条件下,可以使用Keil的软件仿真逻辑分析仪功能观察串口输出波形,并分析时序状态正确与否,计算波特率实际为多少。

二、串口通讯协议和RS-232的介绍以及USB/TTL转232模块的工作原理 

 1、 串口协议和RS-232标准:

 (1)串口协议:

        串口通讯(Serial Communication)是一种设备间非常常用的串行通讯方式,因为它简单、便捷,因此大部分电子设备都支持该通讯方式,电子工程师在调试设备时也经常使用该通 讯方式输出调试信息。
       在计算机科学里,大部分复杂的问题都可以通过分层来简化。如芯片被分为内核层和片上外设;STM32标准库则是在寄存器与用户代码之间的软件层。对于通讯协议,我们也以分层的方式来理解,最基本的是把它分为物理层和协议层。

名称 组成作用
物理层 具有机械、电子功能部分的特性,确保原始数据在物理媒体的传输
协议层 规定通讯逻辑,统一收发双方的数据打包、解包标准。

 在串口通讯的物理层有很多标准及变种,下面主要讲解 RS-232 标准!

(2)RS-232 标准: 

 RS-232 标准主要规定了信号的用途通讯接口以及信号的电平标准。 

hal库dma工程,STM32F103C8T6,stm32,单片机,嵌入式硬件       在上面的通讯方式中,两个通讯设备的“DB9接口”之间通过串口信号线建立起连接,串口信号线中使用“RS-232标准”传输数据信号。由于RS-232电平标准的信号不能直接被控制器直接识别,所以这些信号会经过一个“电平转换芯片”转换成控制器能识别的“TTL校准”的电平信号,才能实现通讯。 

 2、RS232电平与TTL电平的区别 

 根据通讯使用的电平标准不同,串口通讯可分为 TTL标准和 RS-232标准:

标准名称 逻辑1 逻辑0
TLL 2.4V~5V 0~0.5V
RS-232 -15V~3V +3V~+15V

       从表格中不难看出,两种标准划分的逻辑电压不同。在电子电路中常使用 TTL 的电平标准,理想状态下,使用 5V 表示二进制逻辑 1,使用 0V 表示逻辑 0;而为了增加串口通讯的远距离传输及抗干扰能力,它使用-15V表示逻辑 1,+15V 表示逻辑 0。

 下图为用RS232与TTL电平校准表示同一个信号时的对比: 

hal库dma工程,STM32F103C8T6,stm32,单片机,嵌入式硬件

 3、USB/TTL转232“模块(CH340芯片为例) 

(1)基本原理: 

       USB转串口即实现计算机USB接口到物理串口之间的转换。可以为没有串口的计算机或其他USB主机增加串口,使用USB转串口设备等于将传统的串口设备变成了即插即用的USB设备。

      USB主机检测到USB转串口设备插入后,首先会对设备复位,然后开始USB枚举过程。USB枚举时过程会获取设备描述符、配置描述符、接口描述符等。描述符中会包含USB设备的厂商ID,设备ID和Class类别等信息。操作系统会根据该信息为设备匹配相应的USB设备驱动。

      USB虚拟串口的实现在系统上依赖于USB转串口驱动,一般由厂家直接提供,也可以使用操作系统自带的CDC类串口驱动等。驱动主要分为2个功能,其一注册USB设备驱动,完成对USB设备的控制与数据通讯,其二注册串口驱动,为串口应用层提供相应的实现方法。

串口收发对应的驱动数据流向一览表:

发送or接收 数据流向
串口发送 串口应用发送数据→USB串口驱动获取数据→驱动将数据经过USB通道发送给USB串口设备→USB串口设备接收到数据通过串口发送
串口接收 USB串口设备接收串口数据→将串口数据经过USB打包后上传给USB主机→USB串口驱动获取到通过USB上传的串口数据→驱动将数据保存在串口缓冲区提供给串口应用读取

  (2)CH340模块介绍:

CH340电路与实物图: 

hal库dma工程,STM32F103C8T6,stm32,单片机,嵌入式硬件

TXD:发送端,一般表示为自己的发送端,正常通信必须接另一个设备的RXD。

RXD:接收端,一般表示为自己的接收端,正常通信必须接另一个设备的TXD。

正常通信的时候本身的TXD永远接设备的RXD。

hal库dma工程,STM32F103C8T6,stm32,单片机,嵌入式硬件

  USB转TTL串口模块与单片机连接电路图如下所示:

  三、搭建STM32开发环境(HAL库环境)

  请参考我的这篇博客:STM32使用HAL库点亮流水灯-CSDN博客

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

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

hal库dma工程,STM32F103C8T6,stm32,单片机,嵌入式硬件 (2)选择的单片机型号以及点击开始工程项目: 

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

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

 hal库dma工程,STM32F103C8T6,stm32,单片机,嵌入式硬件

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

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

hal库dma工程,STM32F103C8T6,stm32,单片机,嵌入式硬件

hal库dma工程,STM32F103C8T6,stm32,单片机,嵌入式硬件

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

 (1)本工程中几个函数简介:

 HAL_UART_Receive_IT:

HAL_UART_Receive_IT(UART_HandleTypeDef *huart, uint8_int *data, uint16_t Size)
 
/*
	huart:使用哪个串口进行通信
	data: 一个地址,用于保存接受到的数据
	Size: 接收的数据个数
*/

      在调用此函数后,程序会将对应串口的接收中断开启,当我们向单片机发送数据时会触发这个中断。在触发这个中断后,程序会接收数据到你传入的地址中,会读取Size个数据。读取完成后,关闭接收中断使能。

      由于程序在接收完数据后会关闭接收中断。因此这个函数我们要写在main的死循环中,保证接收中断可以一直开启。

 HAL_UART_Transmit_IT:

HAL_UART_Transmit_IT(UART_HandleTypeDef *huart, uint8_int *data, uint16_t Size)
 
/*
	huart:使用哪个串口进行通信
	data: 一个地址,里面是要发送的数据通常是数组
	Size: 发送的数据个数
*/

       使用这个函数开启发送中断,发送寄存器为空时触发中断,将要发送的数据送入发送寄存器并发送。发送完成后关闭中断。在此实验中,我们把它当做普通的发送函数即可。

HAL_GPIO_WritePin: 

HAL_GPIO_WritePin(GPIOX,GPIO_PIN_X,GPIO_PIN_STATUS)
/*
	GPIOX:目标GPIO的组号
	GPIO_PIN_X: 目标GPIO的引脚编号 
	GPIO_PIN_STATUS: 引脚状态
*/

 使用这个函数修改GPIO_ODR寄存器,将非复用输出的GPIO引脚输出电平设置成自己想要的。 

 HAL_Delay(uint ms): 

HAL_Delay(uint ms)

延时ms函数。 

(2)编写代码思路: 

main函数外用一个char类型的数组:rcData,接收发过来的连续字符串,默认为:start
main函数中进入死循环,调用HAL_UART_Receive_IT使能接收中断
如果电脑发送了字符串,接收变量flag的值会变
如果接收变量为:start,led阴极置低电平,led亮,向电脑发数据“hello windows!”
如果接收变量为:stop!,led阴极置高电平,led灭,不向电脑发送数据

 (3)完善keil5工程代码:

 首先,点击刚刚生成的keil5工程文件,双击main.c文件,然后再main.c中找到图示框住的函数, 接着右击此函数,进入其定义的地方处:

hal库dma工程,STM32F103C8T6,stm32,单片机,嵌入式硬件(2)将图中框住的部分改为SET即可:此步骤是将这个GPIO口设置为高电平,初始时不亮!

hal库dma工程,STM32F103C8T6,stm32,单片机,嵌入式硬件 (3)回到main.c文件中,详细编写主要代码:

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

 HAL_UART_Receive_IT(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size)
  UART_HandleTypeDef *huart      UATR的别名    
 huart1  *pData      			接收到的数据存放地址
 Size                      		接收的字节数
 功能:串口中断接收,以中断方式接收指定长度数据。
 大致过程是,设置数据存放位置,接收数据长度,然后使能串口接收中断。
 接收到数据时,会触发串口中断。
 再然后,串口中断函数处理,直到接收到指定长度数据
 而后关闭中断,进入中断接收回调函数,不再触发接收中断。(只触发一次中断)

 具体化运用在本项目中代码为:

HAL_UART_Receive_IT(&huart1,(uint8_t*)rcData,5);

 (2:用字符串进行判断,因此接受变量用数组来储存,Size要改成数组的大小为:6。单片机收到串口助手发的信息后,与"stop2!"和"start"进行匹配。根据匹配结果执行不同的代码。“stop!”,"start"与收到的数据都用uint8_t数组保存。为执行匹配操作,我们需要写一个函数对每一位进行判断与匹配:

int strEqual(char rcData[15],char rcData2[15]){
	for(uint8_t i = 0 ; i < 15 ; i++){
		if (rcData[i] != rcData2[i]) return 0;
	}
	return 1;
}

 (3:在main函数前面添加上如下代码(接收信息储存数组,接收信息匹配处理函数,信息标志flag):

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;

 (4: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);
		}

 (5:在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);
}

 (6:完善之后的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的连接,参考下图: 

hal库dma工程,STM32F103C8T6,stm32,单片机,嵌入式硬件 hal库dma工程,STM32F103C8T6,stm32,单片机,嵌入式硬件

 PA0——黄灯;

连接好的实物图如下所示:

hal库dma工程,STM32F103C8T6,stm32,单片机,嵌入式硬件

 2、 USB转TTL环境配置:

需要在电脑上安装CH340驱动(USB串口驱动)或者CH341驱动(USB串口驱动):

在网上下载好所需的CH340驱动(USB串口驱动)或者CH341驱动(USB串口驱动),如下图所示:

hal库dma工程,STM32F103C8T6,stm32,单片机,嵌入式硬件

 点击CH341SER_2.EXE进行安装CH341驱动,会弹出一下弹窗,点击安装,等待安装成功即可:

hal库dma工程,STM32F103C8T6,stm32,单片机,嵌入式硬件

 3、下载烧录软件与串口通信软件: 

烧录软件推荐使用:FLYMCU,如下图所示图样: 

hal库dma工程,STM32F103C8T6,stm32,单片机,嵌入式硬件

串口通信软件推荐使用:XCOM,如下图所示图样:

hal库dma工程,STM32F103C8T6,stm32,单片机,嵌入式硬件

请自行在网上把这两个软件下载好,为后面的烧录运行做好准备! 

 4、keil5工程里面对于USB转TTL的配置:

 5、编译生产hex文件,用于后面的烧录步骤:

 6、烧录:

(1)将USB转TTL插上电脑的USB接口上去,打开刚刚下载好的FLYMCU软件,按照下图所示进行相关配置,其中的第二步就是选择刚刚上一步编译生成的hex文件:

hal库dma工程,STM32F103C8T6,stm32,单片机,嵌入式硬件

(2)改变STM32最小系统板子的跳线帽连接方式:BOOTO的跳线帽连接方式由0——>1:

hal库dma工程,STM32F103C8T6,stm32,单片机,嵌入式硬件

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

第一步:

hal库dma工程,STM32F103C8T6,stm32,单片机,嵌入式硬件

第二步:

hal库dma工程,STM32F103C8T6,stm32,单片机,嵌入式硬件

烧录成功示意图:

hal库dma工程,STM32F103C8T6,stm32,单片机,嵌入式硬件

烧录完成之后还需要下面的关键一步,把STM32最小系统板子的跳线帽连接方式还原:BOOTO的跳线帽连接方式由1——>0:

hal库dma工程,STM32F103C8T6,stm32,单片机,嵌入式硬件

  7、配置XCOM,打开XCOM软件,按下图所示进行配置:

  8、运行结果演示:

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

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

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

hal库dma工程,STM32F103C8T6,stm32,单片机,嵌入式硬件

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

 1、进入keil5仿真:

(1)点击第一步,Target界面中,选择跟正确的晶振大小,使用8MHz的外部晶振:

hal库dma工程,STM32F103C8T6,stm32,单片机,嵌入式硬件

(2)接着进行Debug页的设置:  

hal库dma工程,STM32F103C8T6,stm32,单片机,嵌入式硬件

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

hal库dma工程,STM32F103C8T6,stm32,单片机,嵌入式硬件

  (4)选择逻辑分析仪: hal库dma工程,STM32F103C8T6,stm32,单片机,嵌入式硬件

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

hal库dma工程,STM32F103C8T6,stm32,单片机,嵌入式硬件

 波形信息配置:添加波形信息的时候,如果自己选择的串口配置的是USART1,就在添加信息里面输入:USART1_RS;同理可得,如果自己选择的串口配置的是USART2,就在添加信息里面输入:USART2_RS;我项目里配置的是USART1,所以我在添加信息时就输入:USART1_RS。

hal库dma工程,STM32F103C8T6,stm32,单片机,嵌入式硬件

我这里选择的USART1对应的波形颜色是绿色!!!

 2、开始仿真:

(1)点击图示圈住的部分,进行仿真   

hal库dma工程,STM32F103C8T6,stm32,单片机,嵌入式硬件

可以调节下图所示几个比较重要与常用的按钮来进行对图像的总体放大与缩小等等操作 :

hal库dma工程,STM32F103C8T6,stm32,单片机,嵌入式硬件

  (2)仿真结果:

hal库dma工程,STM32F103C8T6,stm32,单片机,嵌入式硬件hal库dma工程,STM32F103C8T6,stm32,单片机,嵌入式硬件

hal库dma工程,STM32F103C8T6,stm32,单片机,嵌入式硬件hal库dma工程,STM32F103C8T6,stm32,单片机,嵌入式硬件

放大处理之后波形图像:

hal库dma工程,STM32F103C8T6,stm32,单片机,嵌入式硬件hal库dma工程,STM32F103C8T6,stm32,单片机,嵌入式硬件hal库dma工程,STM32F103C8T6,stm32,单片机,嵌入式硬件

hal库dma工程,STM32F103C8T6,stm32,单片机,嵌入式硬件

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

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

hal库dma工程,STM32F103C8T6,stm32,单片机,嵌入式硬件

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

hal库dma工程,STM32F103C8T6,stm32,单片机,嵌入式硬件

 (3)按照下图所示进行配置RCC:

hal库dma工程,STM32F103C8T6,stm32,单片机,嵌入式硬件

 (4)设置USART1:选择异步通信、参数选择默认和使能串口:

hal库dma工程,STM32F103C8T6,stm32,单片机,嵌入式硬件

(5)添加两个通道:

hal库dma工程,STM32F103C8T6,stm32,单片机,嵌入式硬件

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

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

hal库dma工程,STM32F103C8T6,stm32,单片机,嵌入式硬件

hal库dma工程,STM32F103C8T6,stm32,单片机,嵌入式硬件

九、完善通过DMA方式控制串口通信的keil5工程

1、 本工程中的几个函数简介:

  • HAL_UART_Transmit_DMA():串口DMA模式发送
 HAL_UART_Transmit_DMA(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size)
功能:串口通过DMA发送指定长度的数据。

参数:

UART_HandleTypeDef *huart UATR的别名 如 : UART_HandleTypeDef huart1; 别名就是huart1
*pData 需要发送的数据
Size 发送的字节数

 本文运用举例:

HAL_UART_Transmit_DMA(&huart1, (uint8_t *)message, sizeof(message));
  • HAL_UART_Receive_DMA():串口DMA模式接收
HAL_UART_Receive_DMA(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size)
功能:串口通过DMA接受指定长度的数据。

参数:

UART_HandleTypeDef *huart UATR的别名 如 : UART_HandleTypeDef huart1; 别名就是huart1
*pData 需要存放接收数据的数组
Size 接受的字节数

 本文运用举例:

HAL_UART_Receive_DMA(&huart1,(uint8_t*)rx_buf,5);//设置DMA接收到的数据存放在rx_buf中
  •  HAL_Delay(uint ms): 
HAL_Delay(uint ms)

延时ms函数。  

2、代码编写思路

main函数外用一个uint8_t类型的数组:rcData,接收发过来的连续字符串,默认为:start
main函数中进入死循环,调用HAL_UART_Receive_DMA()
如果电脑发送了字符串,接收变量flag的值会变
如果接收变量为:start,led阴极置低电平,向电脑发数据“hello windows!”
如果接收变量为:stop!,led阴极置高电平,不向电脑发送数据

3、完善keil5工程代码:

 (1)在main.c文件中,详细编写主要代码:

(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);
}

  (2:用字符串进行判断,因此接受变量用数组来储存,Size要改成数组的大小为:6。单片机收到串口助手发的信息后,与"stop2!"和"start"进行匹配。根据匹配结果执行不同的代码。“stop!”,"start"与收到的数据都用uint8_t数组保存。为执行匹配操作,我们需要写一个函数对每一位进行判断与匹配:

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;
}

  (3:在main函数前面添加上如下代码(接收信息储存数组,接收信息匹配处理函数,信息标志flag):

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;
}

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

     if(flag==1)
	  {
	    HAL_UART_Transmit_DMA(&huart1, (uint8_t *)message, sizeof(message));
	    HAL_Delay(600);
	  }

 5:在main函数下面重写串口接收完成回调函数:

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);
}

  (6:完善之后的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方式控制串口通信的电路连接与烧录运行

1、电路的连接、软件的下载以及环境的配置:

参考本文前面第六大点基于中断方式控制串口通信的电路连接与烧录与运行!基本上一致!!!

2、运行结果演示: 

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

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

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

hal库dma工程,STM32F103C8T6,stm32,单片机,嵌入式硬件

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

 1、进入keil5仿真与开始仿真:

参考本文前面第七大点:基于中断控制串口通信的keil5仿真调试!里面有详细介绍了关于如何进入keil5仿真、环境设置和详细操作等等!!!

2、仿真结果演示:

hal库dma工程,STM32F103C8T6,stm32,单片机,嵌入式硬件hal库dma工程,STM32F103C8T6,stm32,单片机,嵌入式硬件hal库dma工程,STM32F103C8T6,stm32,单片机,嵌入式硬件

 放大处理之后波形图像:

hal库dma工程,STM32F103C8T6,stm32,单片机,嵌入式硬件hal库dma工程,STM32F103C8T6,stm32,单片机,嵌入式硬件hal库dma工程,STM32F103C8T6,stm32,单片机,嵌入式硬件

十二、总结

      本人在这篇blog:STM32使用HAL库中断控制串口通信-CSDN博客,已经提前接触并且完成了有关中断控制串口通信:向单片机发送单个字符的实验;本次又在这个的实验的基础上完成了对于利用中断控制串口通信:向单片机发送连续字符串的实验。总的来说,是温习前面已经会了的中断控制串口通信;从局部来说,也是进步:学习并且尝试了新的内容:向单片机发送连续字符串的实验而不是单个字符!!!

     对于DMA控制串口通信方式,本人之前既没有听说过,也没有接触过。在本实验中,本人学习了有关DMA的知识,并且同时付出行动去实践实验,最终成功利用DMA方式也实现了串口通信:向单片机发送单个字符的实验!!!

    谢谢你的观看,希望你能有所收获!完!!!

十三、参考资料

1、stm32使用hal库中断控制串口通信_stm32 hal库串口中断接收_终极末影龙的博客-CSDN博客 

2、HAL库中断方式进行串口通信_醉意丶千层梦的博客-CSDN博客 

3、STM32使用HAL库中断控制串口通信-CSDN博客 

4、基于HAL库实现DMA串口通信_hal_dma_start_it_醉意丶千层梦的博客-CSDN博客

5、STM32F0x HAL库学习笔记(7)DMA数据的传输配置:串口数据的DMA发送与接收_hal_dma_start-CSDN博客

6、【STM32】HAL库 STM32CubeMX教程十一---DMA (串口DMA发送接收)_hal库dma串口接收-CSDN博客文章来源地址https://www.toymoban.com/news/detail-788330.html

到了这里,关于STM32—HAL库中断/DMA控制和完成串口通信的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • STM32F4_HAL库_串口阻塞/中断/DMA三种方式发送数据的配置

    串口阻塞发送的意思就是,发送一段数据,在没有发送完所有数据之前,一直停留在此发送函数(可设定阻塞时间),这个过程中会阻塞别的程序运行; HAL库的配置分为两个层次,一个是HAL库内部调用的、与MCU硬件相关的初始化xxx_MspInit,一个是我们外部调用的初始化xxx_In

    2023年04月25日
    浏览(53)
  • HAL库学习05---串口通信(三种方式轮询、中断、DMA)

    串行通信的通讯方式可以分为两类: 1、同步通信 ,带时钟信号的传输,如SPI、IIC、USART 2、异步传输 ,不带时钟信号的传输,如UART、USART UART通用异步收发器 :UART口指的是一种物理接口形式(硬件) UART是异步,全双工串口总线。它比同步串口复杂很多。有两根线,一根TXD用于

    2024年02月11日
    浏览(35)
  • 【STM32】CUBEMX之串口:串口三种模式(轮询模式、中断模式、DMA模式)的配置与使用示例 + 串口重定向 + 使用HAL扩展函数实现不定长数据接收

    目录   总览 使用CUBEMX创建工程的基本配置 CUBEMX中的配置 Keil中的配置 实物连接 串口轮询模式 轮询模式HAL库函数 特点 实验一:发送数据给单片机并让其返回相同值 串口重定向 串口中断模式 在CUBEMX中打开串口中断 中断模式HAL库函数 特点 实验二:使用中断回调完成实验一

    2024年04月10日
    浏览(74)
  • STM32 HAL DMA中断配置

    使用HAL库方式DMA中断时,在网上找了好多资料都没有怎么介绍。所以就自己研究了一下,并做个记录。我的芯片型号是STM32G030。下面我以I2C传数据为例介绍下HAL库是如何使用DMA中断的。 我使用的是I2C2,简单配置下参数,加上DMA通道。 DMA貌似默认开启了中断,蓝色的勾勾是我

    2024年04月23日
    浏览(46)
  • STM32机器人控制开发教程No.4 使用串口通信控制电机(基于HAL库)

    在机器人控制中,单片机(Arduino/STM32)与上位机(Raspberry Pi/NVIDIA Jetson nano)之间的通信经常采用串口通信的方式,那应该如何使用STM32的串口通信以及根据自己定义的协议来完成数据的接收与发送呢?在本篇文章中将给你演示如何通过自定协议来完成对电机的控制以及获取编码

    2023年04月25日
    浏览(57)
  • 【STM32】HAL库 STM32CubeMX——DMA (串口DMA发送接收)

    软件: STM32CubeMX KEIL5 mcuisp 串口通信助手 硬件: STM32F103C8Tx 杜邦线,面包板,USB转TTL DMA,全称Direct Memory Access,即直接存储器访问。 DMA传输将数据从一个地址空间复制到另一个地址空间,提供在外设和存储器之间或者存储器和存储器之间的高速数据传输。 我们知道系统的运

    2024年02月12日
    浏览(66)
  • STM32(HAL)串口中断接收

    目录 1、简介 2 基础配置 2.1.1 SYS配置  2.1.2 RCC配置 2.2 串口外设配置  2.3 项目生成  3、KEIL端程序整合 本文对HAL串口中断函数进行介绍。 2.1.1 SYS配置  2.1.2 RCC配置 首先在main.c文件中进行接受变量声明。  接着在主函数的while循环中进行接收中断,如下所示:  最后在主函数

    2024年02月14日
    浏览(52)
  • 【STM32】HAL库 串口中断发送与接收

    【STM32】HAL库 新建MDK工程 【STM32】HAL库 串口轮询发送 使用stm32串口中断发送和中断接收 在主函数前开启中断,接受字节数为5 接受5个字节后,进入中断接收完成回调函数,重新再开启中断,并把接收到的数据返回 修改接收数组长度,改为开启串口空闲中断 接收事件回调函数

    2024年02月08日
    浏览(61)
  • STM32 HAL库 串口中断接收数据包

    目录 一、CUBEmx配置 1.设置系统时钟,配置SYS,配置时钟树  ​编辑  2.配置串口USART1 3.配置NVIC,开启串口中断 ​编辑4.点击GENERATE CODE输出文件即可 二、代码部分 0.串口重定向——printf 1.关于舵机 2.开启串口中断函数 3.编写串口回调函数 4.主函数部分 三、实验现象: 四、总

    2024年02月04日
    浏览(44)
  • STM32基于HAL库的串口接受中断和空闲中断

    在通信方面。UART由于全双工通信,可以同时接受数据和发送数据而被广泛使用。 而接受数据则又有很多种方法 比如: 1根据结束符判断,数据是字符串形式,所以一般串口接受的接受符就是 \\\"rn\\\"  换成16进制ascil码显示就是 0X0D   0X0A (对应rn)  2定时器中断,设计\\\"喂狗信号量

    2023年04月08日
    浏览(49)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包