中断及串口通信

本文围绕STM32串口DMA方式展开,介绍了创建项目的步骤,包括设置RCC、串口、DMA等,还说明了Keil配置、烧录及运行过程。同时以更高波特率发送数据为例,阐述项目修改方法。此外,对DMA原理进行了详细讲解,指出其能提高CPU效率,最后提及试验难点。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

一、创建项目

前言:

当STM32接收到字符“stop stm32!”时,停止持续发送“hello windows!”; 当接收到字符“go stm32!”时,持续发送“hello windows!”

STM32采用串口DMA方式,用115200bps或更高速率向上位机连续发送数据

使用设备:

stm32f103c8t6开发板

1、设置RCC

设置高速外部时钟HSE 选择外部时钟源
在这里插入图片描述

2、设置串口

点击USATR1
设置MODE为异步通信(Asynchronous)
基础参数:波特率为115200 Bits/s。传输数据长度为8Bit。奇偶检验无,停止位1 接收和发送都使能 > GPIO引脚自动设置 USART1_RX/USART_TX
NVIC Settings一栏使能接收中断

在这里插入图片描述
在这里插入图片描述

3、设置DMA

点击DMASettings 点击 Add 添加通道
选择USART_RX USART_TX 传输速率设置为中速
DMA传输模式为正常模式
DMA内存地址自增,每次增加一个Byte(字节)

在这里插入图片描述

4、DMA基础设置

在这里插入图片描述

5、时钟设置

在这里插入图片描述

6、项目文件设置

在这里插入图片描述
在这里插入图片描述
之后GRNERATE CODE创建好工程

二、Keil配置

1、配置下载工具

在这里插入图片描述
在这里插入图片描述

2、编写代码

定义发送信息,最好是全局变量

char message[]="hello Windows\n";//Êä³öÐÅÏ¢

在while循环中加入如下代码

		if(flag==1)
		{
			// 发送信息
			HAL_UART_Transmit(&huart1, (uint8_t *)&message,COUNTOF(message),0xFFFF); 
			// 延时
			HAL_Delay(1000);
		}
		else if(flag==0)
		{
			HAL_Delay(1000);
		}

在main.c中重写USART1_IRQHandler函数,同时记得删掉stm32f1xx_it.c中的同名函数

void USART1_IRQHandler(void)
{
	 HAL_UART_IRQHandler(&huart1); //
   HAL_UART_Receive_IT(&huart1, (uint8_t *)getBuffer,10);  //
}

修改之后的main.c
里面的生成的注释很多,就没有删了

/* USER CODE BEGIN Header */
/**
  ******************************************************************************
  * @file           : main.c
  * @brief          : Main program body
  ******************************************************************************
  * @attention
  *
  * Copyright (c) 2022 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"
#include "string.h"

char myBuffer[] = "I have gotten your message: "; //Óû§ÌáʾÐÅÏ¢
char Enter[] = "\r\n"; //»Ø³µ»»ÐÐ
char getBuffer[100]; //Óû§×Ô¶¨ÒåµÄ»º³åÇø
char value;
char str1[] = "go stm32!";
char str2[] = "stop stm32!";
char c;//Ö¸Áî 0:Í£Ö¹  1:¿ªÊ¼
char message[]="hello Windows\n";//Êä³öÐÅÏ¢
char tips[]="CommandError\n";//Ìáʾ1
char tips1[]="Start.....\n";//Ìáʾ2
char tips2[]="Stop......\n";//Ìáʾ3
int flag=1;//±êÖ¾ 0:Í£Ö¹·¢ËÍ 1.¿ªÊ¼·¢ËÍ
int countofGetBuffer=0;
/* 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 */

/**ep
  * @brief  The application entry point.
  * @retval int
  */
  #define COUNTOF(a) (sizeof(a)/sizeof(*(a)))
	  //jkl
int main(void)
{
  /* USER CODE BEGIN 1 */

  /* USER CODE END 1 */

  /* MCU Configuration--------------------------------------------------------*/

  /* Reset of all peripherals, Initializes the Flash interface and the Systick. */
  HAL_Init();

  /* USER CODE BEGIN Init */

  /* USER CODE END Init */

  /* Configure the system clock */
  SystemClock_Config();

  /* USER CODE BEGIN SysInit */

  /* USER CODE END SysInit */

  /* Initialize all configured peripherals */
  MX_GPIO_Init();
  MX_USART1_UART_Init();
  
  HAL_UART_Receive_IT(&huart1, (uint8_t *)&value, 1);
  //HAL_UART_Receive_IT(&huart1, (uint8_t *)&c, 10);


  /* USER CODE BEGIN 2 */

  /* USER CODE END 2 */

  /* Infinite loop */
  /* USER CODE BEGIN WHILE */
  while (1)
  {
		if(flag==1){
			//·¢ËÍÐÅÏ¢
			HAL_UART_Transmit(&huart1, (uint8_t *)&message,COUNTOF(message),0xFFFF); 
			//ÑÓʱ
			HAL_Delay(1000);
		}else if(flag==0){
			HAL_Delay(1000);
		}
  }
}
 
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart1){
	HAL_UART_IRQHandler(huart1);
	getBuffer[countofGetBuffer++]=value;
	if(strcmp(getBuffer,str1)==0){
		flag = 1;
		HAL_UART_Transmit(huart1, (uint8_t *)&tips1, COUNTOF(tips1),0xFFFF);
		countofGetBuffer = 0;
		memset(getBuffer,0,COUNTOF(getBuffer));
	}else if(strcmp(getBuffer,str2)==0){
	    flag = 0;
		HAL_UART_Transmit(huart1, (uint8_t *)&tips2, COUNTOF(tips2),0xFFFF); 
		countofGetBuffer = 0;
		memset(getBuffer,0,COUNTOF(getBuffer));
	}
	HAL_UART_Receive_IT(huart1, (uint8_t *)&value,1);
}

void USART1_IRQHandler(void)
{
	 HAL_UART_IRQHandler(&huart1); //¸Ãº¯Êý»áÇå¿ÕÖжϱêÖ¾£¬È¡ÏûÖжÏʹÄÜ£¬²¢¼ä½Óµ÷Óûص÷º¯Êý
   HAL_UART_Receive_IT(&huart1, (uint8_t *)getBuffer,10);  //Ìí¼ÓµÄÒ»ÐдúÂë
}

/**
  * @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 */

3、烧录进芯片中

三、运行结果

在这里插入图片描述

四、以更高的波特率向上位机连续发送数据

1、创建项目/项目修改

其实大体和前面的一样,只需要在USART设置时,修改一下参数即可,这里的参数最好不要随便输入,应该是参考了调试助手的波特率之后再进行输入,比如:这里输入了128000,这是软件有的波特率
在这里插入图片描述

2、代码

如上

3、烧录

4、运行

要将这里的波特率进行对应的修改,来保证输出的正确性
在这里插入图片描述

五、DMA简介

1、什么是DMA

DMA(Direct Memory Access,直接存储器访问) 是所有现代电脑的重要特色,它允许不同速度的硬件装置来沟通,而不需要依赖于 CPU 的大量中断负载。

2、原理

DMA 传输将数据从一个地址空间复制到另外一个地址空间。当CPU 初始化这个传输动作,传输动作本身是由 DMA 控制器来实行和完成。典型的例子就是移动一个外部内存的区块到芯片内部更快的内存区。像是这样的操作并没有让处理器工作拖延,反而可以被重新排程去处理其他的工作。DMA 传输对于高效能 嵌入式系统算法和网络是很重要的。
在实现DMA传输时,是由DMA控制器直接掌管总线,因此,存在着一个总线控制权转移问题。即DMA传输前,CPU要把总线控制权交给DMA控制器,而在结束DMA传输后,DMA控制器应立即把总线控制权再交回给CPU。一个完整的DMA传输过程必须经过DMA请求、DMA响应、DMA传输、DMA结束4个步骤。

1、请求

CPU对DMA控制器初始化,并向I/O接口发出操作命令,I/O接口提出DMA请求。

2、响应

DMA控制器对DMA请求判别优先级及屏蔽,向总线裁决逻辑提出总线请求。当CPU执行完当前总线周期即可释放总线控制权。此时,总线裁决逻辑输出总线应答,表示DMA已经响应,通过DMA控制器通知I/O接口开始DMA传输。

3、传输

DMA控制器获得总线控制权后,CPU即刻挂起或只执行内部操作,由DMA控制器输出读写命令,直接控制RAM与I/O接口进行DMA传输。
在DMA控制器的控制下,在存储器和外部设备之间直接进行数据传送,在传送过程中不需要中央处理器的参与。开始时需提供要传送的数据的起始位置和数据长度。

4、结束

当完成规定的成批数据传送后,DMA控制器即释放总线控制权,并向I/O接口发出结束信号。当I/O接口收到结束信号后,一方面停 止I/O设备的工作,另一方面向CPU提出中断请求,使CPU从不介入的状态解脱,并执行一段检查本次DMA传输操作正确性的代码。最后,带着本次操作结果及状态继续执行原来的程序。
由此可见,DMA传输方式无需CPU直接控制传输,也没有中断处理方式那样保留现场和恢复现场的过程,通过硬件为RAM与I/O设备开辟一条直接传送数据的通路,使CPU的效率大为提高。

六、总结

本次的试验中的难点是要接收一个不固定长度的字符串,就需要自己实现一个接收字符串的函数。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值