中断及串口通信进阶

采用中断模式编程,当开关接高电平时,LED亮灯;接低电平时,LED灭灯。

1.本部分主要解释如何用stm32F103核心板的GPIOA端一管脚接一个LED,GPIOB端口一引脚接一个开关;并采用中断模式编程,当开关接高电平时,LED亮灯;接低电平时,LED灭灯。

1.1 中断技术详解

1.1.1什么是中断?

● 在单片机系统中,如果遇到需要紧急处理的突发事件时,CPU需要迅速的作出反应,暂停正在运行的程序来处理突发事件,这时就需要中断
● 中断是指单片机正在执行程序的时,发生突发事件从而打断当前程序,转而去处理这一事件,当处理完成后再回到原来被打断出继续执行原程序的过程。
● 意义:中断能提高CPU的效率,同事能够对突发事件做出实时处理。实现程序的并行化,实现嵌入式系统进程之间的切换。
中断分为同步中断和异步中断。
● 同步中断——同步中断是当指令执行时由 控制单元产生的,之所以称为同步,是因为只有在一条指令终止执行后 CPU 才会发出中断。
● 异步中断——异步中断是由其他硬件设备依照 CPU 时钟信号随机 产生的。
※ 通常我们所说的中断指的是异步中断,我们将同步中断称为异常。(异常是由程序的错误产生的,或者是由内核必须处理的异常条件产生的)

1.1.2中断源

● 任何能够引发中断的事件都称为中断源。
● 分类:中断源可分为硬件中断源和软件中断源。

硬件中断源:外设,数据通道,时钟电路和故障源(如电源掉电)。
软件中断源: 为调试程序设置的中断,中断指令,执行过程出错。

硬件中断源:外设,数据通道,时钟电路和故障源(如电源掉电)。
软件中断源: 为调试程序设置的中断,中断指令,执行过程出错。

1.1.3中断处理的基本过程

● 中断处理分为如下几个步骤:中断请求,中断响应,保护断点,处理中断,中断返回。

中断请求阶段
● 发生在CPU内部的中断(内部中断),不需要中断请求,CPU内部的中断控制逻辑直接接收处理。
● 外部中断请求由中断源提出。外部中断源利用CPU的中断输入引脚 输入中断请求信号。一般CPU设有两个中断请求输入引脚:可屏蔽中断请求输入引脚和不可屏蔽中断请求输入引脚。

中断请求触发器
● 每个中断源发中断请求信号的时间是不确定的,而CPU在何时响应中断也 是不确定的。所以,每个中断源都有一个中断请求触发器,锁存自己的中断请求信号,并保持到CPU响应这个中断请求之后才将其清除。

中断允许触发器
● 在CPU内部有一个中断允许触发器,当其为“1”时,允许CPU响应中断, 称为开中断。若其为“0”,不允许CPU响应中断,中断被屏蔽,称为关中断。
● 通常,当CPU复位时,中断允许触发器也复位为“0”,即关中断。当 CPU中断响应时,CPU自动关闭中断,禁止接受另一个新的中断。
● 中断允许触发器的状态可以用开中断或关中断指令来设置。

中断判优阶段
● CPU一次只能接受一个中断源的请求,当多个中断源同时向CPU提出中断请求时,CPU必须找出中断优先级最高的中断源,这一过程称为中断判优。
● 中断判优可以采用硬件方法,也可采用软件方法。

软件判优
● CPU检测到中断请求后,首先读取中断请求寄存器的内容,逐位检测它们的状态,检测到某一位为1,就确定对应的中断源有中断请求,转去执行它的中断服务程序。
● 先检测哪一个,哪一个的优先级就高,后检测哪一个,哪一个优先级就低,检测的顺序就是各中断源的优先级顺序。(软件判优耗时较长。如果中断源很多,中断的实时性就很差,但是软件判优优先权安排灵活。)
在这里插入图片描述
中断响应阶段
● 经过中断判优,中断处理就进入中断响应阶段。中断响应时,CPU向中断源发出中断响应信号,同时:
① 保护硬件现场;
② 关中断;
③ 保护断点;
④ 获得中断服务程序的入口地址。

中断服务阶段
中断服务程序的一般结构为:
1)保护现场。 在中断服务程序的起始部分安排若干条入栈指令,将各寄存器的内容压入堆栈保存。
2)开中断。 在中断服务程序执行期间允许级别更高的中断请求中断现 行的中断服务程序,实现中断嵌套。
3)中断服务。 完成中断源的具体要求。
4)恢复现场。 中断服务程序结束前,必须恢复主程序的中断现场。通常是将保存在堆栈中的现场信息弹出到原来的寄存器中。
5)中断返回。 返回到原程序的断点处,继续执行原程序。

中断返回阶段
返回到原程序的断点处,恢复硬件现场,继续执行原程序。
中断返回操作是中断响应操作的逆过程。

1.1.4中断优先级

8086/8088中断优先级:
软件中断(除单步中断)>非屏蔽中断NMI>可屏蔽中断INTR>单步中断
不同优先级中断的处理原则:

不同优先级的多个中断源同时发出中断请求,按照优先级高低依次处理。
低优先级中断正在处理,出现高优先级请求,应转去执行高优先级请求,服务结束后返回原低优先级中断服务程序继续执行。
高优先级中断正在处理,出现低优先级请求,可咋不响应。 中断处理时,出现同级别请求,应在当前中断处理结束后再处理新的请求。

1.2工程新建

1.2.1硬件准备
1.2.2中断工程新创

在这里插入图片描述
● 选择STM32F103C8T6。

1.2.3外设设置

● 设置指示灯LED引脚PB5,设置引脚模式为输出模式GPIO_Output
● 设置按键引脚PA1,设置引脚为外部中断功能,PA1与外部中断线EXIT1连接GPIO_EXIT1

1.2.4GPIO设置

在这里插入图片描述
● 对于开关对应管脚PA1,设置其触发方式为上升沿触发。
在这里插入图片描述
● 使能对应的外部中断线,点击Enabled。
在这里插入图片描述

1.2.5配置中断优先级

● 大多数情况下不必设置中断优先级,直接使用中断编号设置的默认中断优先级。

1.2.6时钟设置

● 设置32MHZ。
在这里插入图片描述

1.2.7工程生成

● 命名与选择MDK-ARM。

1.3中断控LED亮灭

1.3.1代码编写

● 在Keil文件中的gpio.c文件可以找到中断服务函数(void HAL_GPIO_EXTI_IRQHandler(uint16_t GPIO_Pin))。
● 当捕获到上升沿,触发中断,就会进入到这个函数里面。
● 在main.c文件中找个地方重新写一下,因为前面的 __weak 表示此函数为虚函数,需要用户重写的。

void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)
{
	if( GPIO_Pin == A1_EXTI_Pin)//判断外部中断源
	{
		HAL_GPIO_TogglePin(LED_GPIO_Port, LED_Pin);//翻转LED状态
	}
}

1.3.2代码编译

编译发现无错误
在这里插入图片描述

1.3.3烧录实现

在这里插入图片描述

1.3.4最终实现

采用中断模式编程,当中断接高电平时,LED亮灯;接低电平时,LED灭灯。(因为无开关,只能使用插入不同电压实现模拟开关,当中断接口不接高电平或低电平时,LED频闪。)

采用串口中断方式完成单字符接受与字符串接受

2.本部分主要介绍通过STM32F103C8T6接收到字符“s”时,停止持续发送“hello windows!”; 当接收到字符“t”时,持续发送“hello windows!”;接收到字符“stop stm32!”时,停止持续发送“hello windows!”; 当接收到字符“go stm32!”时,持续发送“hello windows!”。

2.1单字符收发

2.1.1工程新创

● 点击ACCESS TO MCU SELECTOR
在这里插入图片描述
● 选择STMF103C8T6芯片。

2.1.2.设置RCC

在这里插入图片描述

2.1.3设置SYS

●Debug选择 Serial Wire

2.1.4设置USART

●Mode选择 Asynchronous

2.1.5设置NVIC

● 点击USART1 global interrupt。
在这里插入图片描述

2.1.6生成项目

在这里插入图片描述

2.2代码编译与收发实现

2.2.1在KEIL中进行代码编写

● 点击在main函数前定义全局变量。

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

2.2.2.main函数中的while循环里面添加传输代码
if(flag==1){
			//发送信息
			HAL_UART_Transmit(&huart1, (uint8_t *)&message, strlen(message),0xFFFF); 
			
			//延时
			HAL_Delay(1000);
		}

2.2.3在main函数下面重写中断处理函数
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{
	
	//当输入的指令为s时,发送提示并改变flag
	if(c=='s'){
		flag=0;
		HAL_UART_Transmit(&huart1, (uint8_t *)&tips2, strlen(tips2),0xFFFF); 
	}
	
	//当输入的指令为t时,发送提示并改变flag
	else if(c=='t'){
		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.2.4 代码汇总

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

void SystemClock_Config(void);

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


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)
{
	
	//当输入的指令为s时,发送提示并改变flag
	if(c=='s'){
		flag=0;
		HAL_UART_Transmit(&huart1, (uint8_t *)&tips2, strlen(tips2),0xFFFF); 
	}
	
	//当输入的指令为t时,发送提示并改变flag
	else if(c=='t'){
		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 */
}

2.2.5 编译
2.2.6 打开串口助手

实现效果:
在这里插入图片描述

2.3字符串发收

2.3.1新建工程

● 生成工程的过程如上

2.3.2代码编写

● main.c文件代码如下:

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


uint8_t aRxBuffer;			
uint8_t Uart1_RxBuff[256];		
uint8_t str1[20] = "stop stm32";
uint8_t str2[20] = "go stm32";
uint8_t Uart1_Rx_Cnt = 0;		
uint8_t	cAlmStr[] = "Êý¾ÝÒç³ö(´óÓÚ256)\r\n";

unsigned int flag = 1;


void SystemClock_Config(void);

int main(void)

  HAL_Init();

 
  SystemClock_Config();

  MX_GPIO_Init();
  MX_USART1_UART_Init();
  HAL_UART_Receive_IT(&huart1, (uint8_t *)&aRxBuffer, 1);

  while (1)
  {
   
	  if(flag == 1)
	  {
	  	printf("ppm Hello windows!\r\n");
	  }
	  
	  else
	  {
		  //printf("stop stm32 NO!\r\n");
	  }
		HAL_Delay(500);
  }
}

void SystemClock_Config(void)
{
  RCC_OscInitTypeDef RCC_OscInitStruct = {0};
  RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};


  RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE;
  RCC_OscInitStruct.HSEState = RCC_HSE_ON;
  RCC_OscInitStruct.HSEPredivValue = RCC_HSE_PREDIV_DIV1;
  RCC_OscInitStruct.HSIState = RCC_HSI_ON;
  RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
  RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE;
  RCC_OscInitStruct.PLL.PLLMUL = RCC_PLL_MUL9;
  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_PLLCLK;
  RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
  RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV2;
  RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1;

  if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_2) != HAL_OK)
  {
    Error_Handler();
  }
}


void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{
  UNUSED(huart);
	if (strcmp(Uart1_RxBuff, str1) == 0) flag = 0;
	if (strcmp(Uart1_RxBuff, str2) == 0) flag = 1;

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

void Error_Handler(void)
{
 
  __disable_irq();
  while (1)
  {
  }
 
}

#ifdef  USE_FULL_ASSERT

void assert_failed(uint8_t *file, uint32_t line)
{
}
#endif 
2.3.3烧录打开串口助手

在这里插入图片描述
●打开串口助手,实现效果如下:
在这里插入图片描述

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

3.1DMA

3.1.1 DMA简介

DMA的出现大大减轻了CPU的工作量。在硬件系统中,主要由CPU(内核)、外设、内存(SRAM)、总线等结构组成,数据经常要在内存和外设之间,外设和外设之间转移。例如:CPU需要处理从外设采集回来的数据,CPU需要先将数据从ADC外设的寄存器读取到内存中(变量)去,然后进行运算处理,这是一般的解决方法。CPU的资源是非常宝贵的,我们可以设法把转移的工作交给其他部件来完成,CPU把更多的资源用于数据运算和中断响应上,如此DMA便登场了。DMA正是为CPU分担数据转移工作,因为DMA的存在,CPU才被解放出来,它可以在数据转移的同时进行数据运算,相应中断,大大提高了效率。

3.1.2 MDA的主要特性

在这里插入图片描述

3.1.3 DMA中断特性

在这里插入图片描述

3.2串口通信DMA传输完成中断

3.2.1 步骤

打开CUBRMX新建项目,芯片选择是F103C8,点击后创建项目;
把PA9和PA10选择为USART1_RX和USART1_TX,然后再点击左边的USART1,将mode置为异步通信模式,我们这里只需要接收,不需要发送,后面再点击add键,添加引脚

3.2.2 代码

Open Project 打开keil5,进入到main.c文件,在main.c文件中的while循环那块的代码如下:

while (1)
  {
   uint8_t send_char[]="hello world\n";//发送的字符串
    HAL_UART_Transmit_DMA(&huart1,(uint8_t *)send_char,0xc);//DMA发送
		HAL_Delay(500);//延时
  }


3.2.3 实验结果

烧录
串口调试
在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值