学习stm32中断、DMA通信原理和编程方法

重庆交通大学信息科学与工程学院

《嵌入式系统基础A》课程

实验报告

班 级: 物联网工程2002

姓名-学号 : 徐权-632007060327

实验项目名称: STM32串口通信编程

实验项目性质: 设计性

实验所属课程: 《嵌入式系统基础A》

实验室(中心): 南岸校区语音大楼

指 导 教 师 : 娄路

完成时间: 2021 10 23

一、实验内容和任务
学习stm32中断、DMA通信原理和编程方法。使用stm32tubemx和HAL库分别完成以下编程练习:

  1. 用stm32F103核心板的GPIOA端一管脚接一个LED,GPIOB端口一引脚接一个开关(用杜邦线模拟代替)。采用中断模式编程,当开关接高电平时,LED亮灯;接低电平时,LED灭灯。

  2. 采用串口中断方式重做上周的串口通信作业,分别实现:1)当stm32接收到字符“s”时,停止持续发送“hello windows!”; 当接收到字符“t”时,持续发送“hello windows!”(提示:采用一个全局标量做信号灯);2)当stm32接收到字符“stop stm32!”时,停止持续发送“hello windows!”; 当接收到字符“go stm32!”时,持续发送“hello windows!”(提示:要将接收到的连续字符保存到一个字符数组里,进行判别匹配。写一个接收字符串的函数。参考:https://blog.csdn.net/qq_43279579/article/details/110138564,此学长用的是中断方式,标准库,但是总体思路一样)。

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

参考:

https://blog.csdn.net/qq_47281915/article/details/121024427

https://blog.csdn.net/qq_47281915/article/details/121053903

https://www.cnblogs.com/breezy-ye/articles/12157442.html

https://blog.csdn.net/as480133937/article/details/104827639/

二、实验要求

1. 分组要求:每个学生独立完成,即1人1组。

2. 程序及报告文档要求:具有较好的可读性,如叙述准确、标注明确、截图清晰等。

3.项目代码上传github,同时把项目完整打包为zip文件,与实验报告(Markdown源码及PDF文件)、作业博客地址一起提交到学习通。

三. 实验过程介绍 (此处可以填博客内容)

1. 用stm32F103核心板的GPIOA端一管脚接一个LED,GPIOB端口一引脚接一个开关(用杜邦线模拟代替)。采用中断模式编程,当开关接高电平时,LED亮灯;接低电平时,LED灭灯。

一丶通过STMCube配置项目
RCC配置:
在这里插入图片描述

SYS配置:
在这里插入图片描述
CLOCK:
在这里插入图片描述
配置GPIO:
在这里插入图片描述
这里要注意:
把A4配置为高电平
把B9中断配置为上升沿和下降沿都触发
然后创建项目就OK
二、通过KEil配置代码
在main.c中加入:

void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)
{
    /* Prevent unused argument(s) compilation warning */
    HAL_GPIO_TogglePin(GPIOA,GPIO_PIN_4);  //????
    /* NOTE: This function Should not be modified, when the callback is needed,
             the HAL_GPIO_EXTI_Callback could be implemented in the user file
     */
}

注意不是加入到int main函数当中;
然后编译生成hex文件
然后烧录

实例:
插入B9灯灭
在这里插入图片描述
拔出B9灯亮
在这里插入图片描述

2. 采用串口中断方式重做上周的串口通信作业,分别实现:1)当stm32接收到字符“s”时,停止持续发送“hello windows!”; 当接收到字符“t”时,持续发送“hello windows!”(提示:采用一个全局标量做信号灯);2)当stm32接收到字符“stop stm32!”时,停止持续发送“hello windows!”; 当接收到字符“go stm32!”时,持续发送“hello windows!”

1)
(1)STM32CubeMX配置
USART1设置:
在这里插入图片描述
RCC设置:
在这里插入图片描述
sys配置:
在这里插入图片描述
USART1选上这个
在这里插入图片描述
(2)编写代码
在main.c里声名全局变量

char c;//指令 0:停止  1:开始
char message[]="hello Windows\n";//输出信息
char tips[]="CommandError\n";//提示1
char tips1[]="s\n";//提示2
char tips2[]="t";//提示3
int flag=0;//标志 0:停止发送 1.开始发送

main.c全部函数


#include "main.h"
#include "usart.h"
#include "gpio.h"
#include <string.h>

void SystemClock_Config(void);

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=0;//标志 0:停止发送 1.开始发送


int main(void)
{
	HAL_Init();
  SystemClock_Config();
  MX_GPIO_Init();
  MX_USART1_UART_Init();
	
	//设置接受中断
	HAL_UART_Receive_IT(&huart1, (uint8_t *)&c, 1);

	
	//当flag为1时,每秒发送一次信息
	//当flag为0时,停止
  while (1)
  {
		if(flag==1){
			//发送信息
			HAL_UART_Transmit(&huart1, (uint8_t *)&message, strlen(message),0xFFFF); 
			
			//延时
			HAL_Delay(1000);
		}
  }
}

void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{
	
	//当输入的指令为0时,发送提示并改变flag
	if(c=='t'){
		flag=0;
		HAL_UART_Transmit(&huart1, (uint8_t *)&tips2, strlen(tips2),0xFFFF); 
	}
	
	//当输入的指令为1时,发送提示并改变flag
	else if(c=='s'){
		flag=1;
		HAL_UART_Transmit(&huart1, (uint8_t *)&tips1, strlen(tips1),0xFFFF); 
	}
	
	//当输入不存在指令时,发送提示并改变flag
	else {
		flag=0;
		HAL_UART_Transmit(&huart1, (uint8_t *)&tips, strlen(tips),0xFFFF); 
	}

	//重新设置中断
		HAL_UART_Receive_IT(&huart1, (uint8_t *)&c, 1);  
}
/* USER CODE END 4 */
/**
  * @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 */
}




重写中断函数

void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{
	
	//当输入的指令为0时,发送提示并改变flag
	if(c=='t'){
		flag=0;
		HAL_UART_Transmit(&huart1, (uint8_t *)&tips2, strlen(tips2),0xFFFF); 
	}
	
	//当输入的指令为1时,发送提示并改变flag
	else if(c=='s'){
		flag=1;
		HAL_UART_Transmit(&huart1, (uint8_t *)&tips1, strlen(tips1),0xFFFF); 
	}
	
	//当输入不存在指令时,发送提示并改变flag
	else {
		flag=0;
		HAL_UART_Transmit(&huart1, (uint8_t *)&tips, strlen(tips),0xFFFF); 
	}

	//重新设置中断
		HAL_UART_Receive_IT(&huart1, (uint8_t *)&c, 1);  
}

实例:
在这里插入图片描述
2)在main.c和usart.c中添加头文件#include "stdio.h"之后,在usart.c文件中,添加如下代码,进行重定义

/* USER CODE BEGIN 1 */

//加入以下代码,支持printf函数,而不需要选择use MicroLIB	  
//#define PUTCHAR_PROTOTYPE int fputc(int ch, FILE *f)	
#if 1
//#pragma import(__use_no_semihosting)             
//标准库需要的支持函数                 
struct __FILE 
{ 
	int handle; 
}; 

FILE __stdout;       
//定义_sys_exit()以避免使用半主机模式    
void _sys_exit(int x) 
{ 
	x = x; 
} 
//重定义fputc函数 
int fputc(int ch, FILE *f)
{ 	
	 HAL_UART_Transmit(&huart1, (uint8_t *)&ch, 1, 0x0001);  
	return ch;
}
#endif 


在main.c中设置两个字符型数组,用来存放需要判别的字符串(记得添加头文件#include “string.h”)。同时设置FLAG标志变量,来判断电脑向stm32发送了哪一个字符串。

uint8_t aRxBuffer;
uint8_t Uart1_RxBuff[256];
uint8_t str1[20]="go stm32!";
uint8_t str2[20]="stop stm32!";
uint8_t Uart1_Rx_Cnt=0;
uint8_t cAlmStr[]="数据溢出(256)\r\n";

在main.c主函数while(1)中添加一下内容(根据你要发送的字符串来修改)

  while (1)
  {
    /* USER CODE END WHILE */

    /* USER CODE BEGIN 3 */
	  if(flag == 1){		
	  		printf("hello windows!\r\n");}
	  else{
		  //printf("stop stm32 NO!\r\n");}
		HAL_Delay(500);

修改回调函数

void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{
  /* Prevent unused argument(s) compilation warning */
  UNUSED(huart);
  /* NOTE: This function Should not be modified, when the callback is needed,
           the HAL_UART_TxCpltCallback could be implemented in the user file
   */
	if (strcmp(Uart1_RxBuff, str1) == 0) flag = 0;
	if (strcmp(Uart1_RxBuff, str2) == 0) flag = 1;

 	//if(Uart1_RxBuff[0]=='g') flag = 1;
	//if(Uart1_RxBuff[0]=='s') flag = 0;
	
	if(Uart1_Rx_Cnt >= 255)  //溢出判断
	{
		Uart1_Rx_Cnt = 0;
		memset(Uart1_RxBuff,0x00,sizeof(Uart1_RxBuff));
		HAL_UART_Transmit(&huart1, (uint8_t *)&cAlmStr, sizeof(cAlmStr),0xFFFF);	
	}
	else
	{
		Uart1_RxBuff[Uart1_Rx_Cnt++] = aRxBuffer;   //接收数据转存
	
		if((Uart1_RxBuff[Uart1_Rx_Cnt-1] == 0x0A)&&(Uart1_RxBuff[Uart1_Rx_Cnt-2] == 0x0D)) //判断结束位
		{
			HAL_UART_Transmit(&huart1, (uint8_t *)&Uart1_RxBuff, Uart1_Rx_Cnt,0xFFFF); //将收到的信息发送出去
			Uart1_Rx_Cnt = 0;
			memset(Uart1_RxBuff,0x00,sizeof(Uart1_RxBuff)); //清空数组
		}
	}
	
	HAL_UART_Receive_IT(&huart1, (uint8_t *)&aRxBuffer, 1);   //再开启接收中断
}

实例演示:
在这里插入图片描述

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

一、工程设置:
1.sys设置
在这里插入图片描述
2.RCC设置
在这里插入图片描述
设置串口

在这里插入图片描述
DMA设置

点击DMA Setting的Add添加通道,传输速率设置为中速Medium
在这里插入图片描述

在这里插入图片描述
时钟设置
在这里插入图片描述
在main.c文件添加代码

uint8_t Senbuff[] = "Hello world/n";  //定义数据发送数组

在while函数中加入

  HAL_UART_Transmit_DMA(&huart1, (uint8_t *)Senbuff, sizeof(Senbuff));
  HAL_Delay(1000);


实例演示:
在这里插入图片描述
总结:通过此次学习我学会了如何使用串口DMA方式向上位机连续发送数据,如何使用串口中断,更加强化了我的动手能力和学习新知识能力,学习就是反复从错误中纠正自己。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值