一、什么是中断

1.中断即打断,实至CPU再执行当前程序时,由于系统出现了某种需要处理的紧急情况,CPU暂停正在执行的程序,转而去执行另一段特殊程序来处理的出现的紧急事务,处理结束后CPU自动返回到原先暂停的程序中去继续执行,这种执行过程由于外界的的原因被中间打断的情况成为中断。

2.中断的作用:

中断使得计算机系统具备应对对处理突发事件的能力,提高了CPU的工作效率,如果没有中断系统,CPU就只能按照原来的程序编写的先后顺序,对各个外设进行查询和处理,即轮询工作方式,轮询方法貌似公平,但实际工作效率却很低,却不能及时响应紧急事件。

在这里插入图片描述

3.中断编程

在配置每个中断的时候一般有 3个编程要点:
1、使能外设某个中断,这个具体由每个外设的相关中断使能位控制。比如串口有发送完成中断,接收完成中断,这两个中断都
由串口控制寄存器的相关中断使能位控制。
2、初始化 NVIC_InitTypeDef结构体,配置中断优先级分组,设置抢占优先级和子优先级,使能中断请求。
3、编写中断服务函数
在启动文件 startup_stm32f429_439xx.s 中我们预先为每个中断都写了一个中断服务函数,只是这些中断函数都是为空,为的只
是初始化中断向量表。实际的中断服务函数都需要我们重新编写,中断服务函数我们统一写在 stm32f4xx_it.c这个库文件中。关
于中断服务函数的函数名必须跟启动文件里面预先设置的一样,如果写错,系统就在中断向量表中找不到中断服务函数的入口直
接跳转到启动文件里面预先写好的空函数,并且在里面无限循环,实现不了中断。
在这里插入图片描述

二、什么是NVIC

NVIC 是嵌套向量中断控制器,控制着整个芯片中断相关的功能,它跟内核紧密耦合,是内核里面的一个外设。但是各个芯片厂商在设计芯片的时候会对Cortex-M3内核里面的NVIC进行裁剪,把不需要的部分去掉,所以说 STM32的 NVIC是 Cortex-M3 的 NVIC 的一个子集。
优先级分组真值表:
在这里插入图片描述

2.优先级定义:

在 NVIC 有一个专门的寄存器:中断优先级寄存器 NVIC_IPRx(在 F429 中,x=0…90)用来配置外部中断的优先级,IPR宽度为
8bit,原则上每个外部中断可配置的优先级为0~255,数值越小,优先级越高。

三、STM32CUBEMX生成中断点灯代码

1.首先打开STM32CUBEMX,新建一个工程
选择芯片*STM32F103C8在这里插入图片描述

2.点击开始工程,配置系统调试接口 SYS,选择 Serial Wire
在这里插入图片描述

3.配置外设 RCC ,选择 HSE (外部高速时钟)为 Crystal/Ceramic Resonator(晶振/陶瓷谐振器)
在这里插入图片描述
4.设置LED灯引脚为PA1的输出模式 GPIO_Output,按键引脚为PB15的外部中断模式GPIO_EXIT1
在这里插入图片描述
LED对应PA1管脚,User Label处设置为LED
在这里插入图片描述
开关对应PB15管脚,GPIO mode处设置为上升沿触发,User Label处设置为 KEY
在这里插入图片描述
:External Interrupt Mode with Rising edge trigger detection 上升沿
External Interrupt Mode with Falling edge trigger detection 下降沿
External Interrupt Mode with Rising/Falling edge trigger detection 上升/下降沿
使能外部中断线,点击Enabled
在这里插入图片描述
设置时钟树,选择PLLCLK,修改HCLK为36MHz
在这里插入图片描述
设置项目名称和路径,修改Toolchain/IDEMDK-ARM
在这里插入图片描述
打开keil,打开建好的工程LED,在main.c文件中添加以下代码

/*
* @brief EXTI line detection callbacks. 
* @param GPIO_Pin: Specifies the pins connected EXTI line,
* @retval None 
*/ 
void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin) 
{ 
	if( GPIO_Pin == KEY_Pin ) // 判断外部中断源
		{
			HAL_GPIO_TogglePin(LED_GPIO_Port, LED_Pin); // 翻转LED状态
		}
}

在这里插入图片描述
:本任务只设置一个外部中断引脚,因此不需要判断多个引脚。如果系统中设置了多个外部中断,建议使用 switch-case 进行多分支判断。
在这里插入图片描述
打开串口调试助手:
在这里插入图片描述
请添加图片描述

实际效果:
请添加图片描述

四、STM32CUBEMX生成中断通信代码

1.创建新工程

点击 File->New Pioject或ACCEE TO MCU SELECTOR,创建新工程
选择 STM32F103C8 芯片,点击 Start Project 进入工程
在这里插入图片描述
设置时钟RCC,点击HSE,选择Crystal/Ceramic Resonator
在这里插入图片描述

设置USART1,点击Mode,选择Asynchronous
在这里插入图片描述
设置中断,在NVIC Settings中点击Enabled。
点击Clock Configuration选择HSE和PLLCLK,修改HCLK值
在这里插入图片描述
设置项目名称和路径,修改Application Structure为Basic,Toolchain/IDE为MDK-ARM,生成项目
在这里插入图片描述

2.程序编写

1)stm32f1xx_hal.c文件

添加头文件

#include <stdio.h>
extern UART_HandleTypeDef huart1;   //声明串口

在这里插入图片描述

重写fget和fput函数

/**
  * 函数功能: 重定向c库函数printf到DEBUG_USARTx
  * 输入参数: 无
  * 返 回 值: 无
  * 说    明:无
  */
int fputc(int ch, FILE *f)
{
  HAL_UART_Transmit(&huart1, (uint8_t *)&ch, 1, 0xffff);
  return ch;
}
 
/**
  * 函数功能: 重定向c库函数getchar,scanf到DEBUG_USARTx
  * 输入参数: 无
  * 返 回 值: 无
  * 说    明:无
  */
int fgetc(FILE *f)
{
  uint8_t ch = 0;
  HAL_UART_Receive(&huart1, &ch, 1, 0xffff);
  return ch;
}

在这里插入图片描述
2)main.c文件
在主函数main的while循环中添加发送数据

printf("hello windows!\r\n");
HAL_Delay(1000);

在这里插入图片描述
添加定义,接收串口数据

#include "stdio.h"
#include <string.h>
 
#define RXBUFFERSIZE  256     //最大接收字节数
char RxBuffer[RXBUFFERSIZE];   //接收数据
uint8_t aRxBuffer;			//接收中断缓冲
uint8_t Uart1_Rx_Cnt = 0;		//接收缓冲计数

在主函数main中添加开启接收中断的语句

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

在这里插入图片描述

添加中断回调函数

/* USER CODE BEGIN 4 */
 
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(Uart1_Rx_Cnt >= 255)  //溢出判断
	{
		Uart1_Rx_Cnt = 0;
		memset(RxBuffer,0x00,sizeof(RxBuffer));
		HAL_UART_Transmit(&huart1, (uint8_t *)"数据溢出", 10,0xFFFF); 	
        
	}
	else
	{
		RxBuffer[Uart1_Rx_Cnt++] = aRxBuffer;   //接收数据转存
	
		if((RxBuffer[Uart1_Rx_Cnt-1] == 0x0A)&&(RxBuffer[Uart1_Rx_Cnt-2] == 0x0D)) //判断结束位
		{
			HAL_UART_Transmit(&huart1, (uint8_t *)&RxBuffer, Uart1_Rx_Cnt,0xFFFF); //将收到的信息发送出去
            while(HAL_UART_GetState(&huart1) == HAL_UART_STATE_BUSY_TX);//检测UART发送结束
			Uart1_Rx_Cnt = 0;
			memset(RxBuffer,0x00,sizeof(RxBuffer)); //清空数组
		}
	}
	
	HAL_UART_Receive_IT(&huart1, (uint8_t *)&aRxBuffer, 1);   //再开启接收中断
}
/* USER CODE END 4 */

在这里插入图片描述
在这里插入图片描述
3)波形观察
打开Serial Windows中的UART #1窗口和逻辑分析仪,添加要观察的端口,修改各个显示方式,将Display Type处设置为Bit。

3.烧录操作

在这里插入图片描述

4.实际效果

在这里插入图片描述

五、DMA通信

1.DMA介绍

DMA,全称Direct Memory Access,即直接存储器访问。
DMA传输将数据从一个地址空间复制到另一个地址空间,提供在外设和存储器之间或者存储器和存储器之间的高速数据传输。
在这里插入图片描述
DMA数据传输方式:
在这里插入图片描述
STM32的DMA控制器
① STM32F411微控制器具备两个DMA控制器:DMA1和DMA2,每个控制器有8个数据流,每个数据流可以映射到8个通道(或请求);
② 每一个DMA控制器用于管理一个或多个外设的存储器访问请求,并通过总线仲裁器来协调各个DMA请求的优先级;
③ 数据流(stream)是用于连接传输源和传输目标的数据通路,每个数据流可以配置为不同的传输源和传输目标,这些传输源和传输目标称为通道(Channel) ;
④ 具备16字节的FIFO。使能FIFO功能后,源数据先送入FIFO,达到FIFO的触发阈值后,再传送到目标地址。
在这里插入图片描述

2.创建DMA工程

基本步骤:
点击 File->New Pioject或ACCEE TO MCU SELECTOR,创建新工程
选择 STM32F103C8 芯片,点击 Start Project 进入工程
设置时钟RCC,点击HSE,选择Crystal/Ceramic Resonator
设置USART1,点击Mode,选择Asynchronous,在NVIC Settings中点击Enabled(中断)
在USART1中,点击DMA Settings的Add,添加USART_RX 和USART_TX,传输速率设置为中速Medium,模式设置为Normal,右侧选择Memory;在最右侧的System view中选择DMA,点击Add,添加MEMTOMEM。
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

设置时钟源,点击Clock Configuration选择HSE和PLLCLK。
在这里插入图片描述
设置项目文件,修改Application Structure为Basic,Toolchain/IDE为MDK-ARM。
在这里插入图片描述
在这里插入图片描述
3.编写编程
main.c文件的主函数main

uint8_t Senbuff[] = "Goodbye\r\n";  //定义数据发送数组

主函数main的while循环

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

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

3.调试和野火操作

1)连接
使用USB-TTL直接进行串口下载,将USB-TTL的GND和3.3V接入STM32系统板的GND和3.3V,然后TXD和RXD分别接入A10和A9引脚。

2)烧录
BOOT0置1,BOOT1置0,打开mcuisp,选择文件路径,开始烧录。
在这里插入图片描述

4.实际效果

在这里插入图片描述

六、总结

本次实验我发现DMA运输并不占CPU资源,工作相比中断更加高效,也学会了了解了stm32中断、DMA通信原理和编程方法,收获很丰富,这几次实验的成果让我终于感觉像是个入门级的嵌入式菜鸟,路很长,但步伐很沉稳。

更多推荐