一、中断的概念
1.异常与中断
异常
异常是由操作系统和硬件实现的,是控制流中的突变,用来响应处理器状态的某些变化。异常分为中断(interrupt)、陷阱(trap)、故障(fault)和终止(abort)。
(1)陷阱(trap)
陷阱是有意的异常,主要用来切换用户态和内核态,其提供的一个切换过程称为系统调用。用户希望调用系统资源的时候,可以执行syscall指令,这时会导致一个异常陷阱,从而进入内核态。
(2)故障(fault)
故障由错误情况引起,它可能被故障处理程序修正,从而重新执行当前指令,也有可能无法被修正,即进入终止(abort),cpu会终止该应用程序。例:取数据时发生“缺页”,导致故障,操作系统通过将所需页面从磁盘调入内存,此时故障处理完毕,操作系统重新执行取数据指令。
(3)终止(abort)
终止是不可恢复的致命错误导致的,通常是硬件错误,例如DRAM或SRAM损坏导致存储器校验错。此时会终止运行的应用程序。
中断
中断是处理来自处理器外部的I/O设备的信号的结果。硬件中断是异步的,硬件中断的异常处理程序被称为中断处理程序。
(1)硬中断与软中断
硬中断是由硬件产生的,例如磁盘,定时器,键盘等。软中断是一组静态定义的下半部分接口,可以在所有处理器上同时执行。软中断不会抢占软中断,硬中断会抢占软中断。
(2)可屏蔽中断和不可屏蔽中断
可屏蔽中断和不可屏蔽中断都属于外部中断,不可屏蔽中断源提出请求,CPU必须响应,而对可屏蔽中断源的请求,CPU可选择响应。
2.事件
对于处理器的外设,是通过内部信号来协同工作的,这个信号则为事件。事件通常和中断绑定在一起,一个事件会引起一个中断或者引起另一个事件。例如:定时器计数完毕,通过计数完毕事件引起中断,或导致另一个事件开始。
二、外部中断/事件控制器(EXTI)
1.EXTI框图
EXTI 可分为两大部分功能,一个是产生中断,另一个是产生事件,这两个功能从硬件上就有所 不同。
对于事件:外部电路的I/O设备的高低电平变化通过输入线被边沿检测电路捕获(通过上升沿或下降沿触发),进入2(或门),并与软件中断做或运算,其中一个发生即可。接着进入4(与门)若事件屏蔽器允许则进入脉冲发生器,此时发出的脉冲可以被另一个事件或中断捕获。
对于中断:1,2电路过程相同,此时中断信号被请求挂起寄存器挂起,与中断屏蔽寄存器作为3号与门的两个输入端,两者同时为1则触发NVIC中断。
三、中断实验
1.背景
实验板:指南者STM32F103VET6
配置环境:STM32CubeMX
开发环境:Keil
实现功能:利用按键进入中断,并在中断中控制LED的亮灭
2.配置环境
参照按键和LED的原理图配置。
对于按键,配置其为GPIO_EXIT,对于LED配置为GPIO_Output。
3.生成代码
略,可看之前的文章。
4.代码解析
(1)引脚的宏定义
在main.h中,STM32帮我们定义了一些宏来增强程序可读性。
/* Private defines -----------------------------------------------------------*/
//按键的宏定义
#define KEY2_EXIT_Pin GPIO_PIN_13
#define KEY2_EXIT_GPIO_Port GPIOC
#define KEY2_EXIT_EXTI_IRQn EXTI15_10_IRQn
#define KEY1_EXIT_Pin GPIO_PIN_0
#define KEY1_EXIT_GPIO_Port GPIOA
#define KEY1_EXIT_EXTI_IRQn EXTI0_IRQn
//LED的宏定义
#define GREEN_Pin GPIO_PIN_0
#define GREEN_GPIO_Port GPIOB
#define BLUE_Pin GPIO_PIN_1
#define BLUE_GPIO_Port GPIOB
#define RED_Pin GPIO_PIN_5
#define RED_GPIO_Port GPIOB
(2)GPIO的初始化配置
将按键1和按键2配置为中断源,同时配置LED初始为高电平,即灭的状态。此代码在gpio.c文件中配置。
void MX_GPIO_Init(void)
{
GPIO_InitTypeDef GPIO_InitStruct = {0};
/* GPIO Ports Clock Enable */
__HAL_RCC_GPIOC_CLK_ENABLE();
__HAL_RCC_GPIOA_CLK_ENABLE();
__HAL_RCC_GPIOB_CLK_ENABLE();
/*Configure GPIO pin Output Level */
HAL_GPIO_WritePin(GPIOB, GREEN_Pin|BLUE_Pin|RED_Pin, GPIO_PIN_SET); //LED的默认电平为高电平
/*Configure GPIO pin : PtPin */
GPIO_InitStruct.Pin = KEY2_EXIT_Pin; //按键2的引脚
GPIO_InitStruct.Mode = GPIO_MODE_IT_RISING; //KEY2设定为检测到上升沿触发中断
GPIO_InitStruct.Pull = GPIO_NOPULL; //设置为既不上拉也不下拉,即浮空输入
HAL_GPIO_Init(KEY2_EXIT_GPIO_Port, &GPIO_InitStruct); //结构体初始化按键
/*Configure GPIO pin : PtPin */
GPIO_InitStruct.Pin = KEY1_EXIT_Pin; //按键1的引脚
GPIO_InitStruct.Mode = GPIO_MODE_IT_RISING_FALLING; //KEY1设定为检测到上升沿或下降沿触发中断,即按一次按键可触发两次
GPIO_InitStruct.Pull = GPIO_NOPULL; //设置为既不上拉也不下拉,即浮空输入
HAL_GPIO_Init(KEY1_EXIT_GPIO_Port, &GPIO_InitStruct); //结构体初始化按键
/*Configure GPIO pins : PBPin PBPin PBPin */
GPIO_InitStruct.Pin = GREEN_Pin|BLUE_Pin|RED_Pin; //LED的引脚
GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP; //模式为推挽输出
GPIO_InitStruct.Pull = GPIO_NOPULL; //设置为既不上拉也不下拉
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW; //速度设置为低速
HAL_GPIO_Init(GPIOB, &GPIO_InitStruct); //结构体初始化LED
/* EXTI interrupt init*/
HAL_NVIC_SetPriority(EXTI0_IRQn, 4, 0); //按键1的主优先级和次优先级分别为4和0
HAL_NVIC_EnableIRQ(EXTI0_IRQn);
HAL_NVIC_SetPriority(EXTI15_10_IRQn, 5, 0); //按键2的主优先级和次优先级分别为5和0
HAL_NVIC_EnableIRQ(EXTI15_10_IRQn);
}
除此之外,我们还可以在gpio.h中配置一些自己想要的宏定义。
/* USER CODE BEGIN Private defines */
#define RED_ON HAL_GPIO_WritePin(GPIOB, RED_Pin, GPIO_PIN_RESET) //低电平红灯亮
#define RED_OFF HAL_GPIO_WritePin(GPIOB, RED_Pin, GPIO_PIN_SET) //高电平红灯灭
#define RED_TOGGLE HAL_GPIO_TogglePin(GPIOB,RED_Pin) //翻转
#define GREEN_ON HAL_GPIO_WritePin(GPIOB, GREEN_Pin, GPIO_PIN_RESET) //低电平绿灯亮
#define GREEN_OFF HAL_GPIO_WritePin(GPIOB, GREEN_Pin, GPIO_PIN_SET) //高电平绿灯灭
#define GREEN_TOGGLE HAL_GPIO_TogglePin(GPIOB,GREEN_Pin) //翻转
#define BLUE_ON HAL_GPIO_WritePin(GPIOB, BLUE_Pin, GPIO_PIN_RESET) //低电平蓝灯亮
#define BLUE_OFF HAL_GPIO_WritePin(GPIOB, BLUE_Pin, GPIO_PIN_SET) //高电平蓝灯灭
#define BLUE_TOGGLE HAL_GPIO_TogglePin(GPIOB,BLUE_Pin) //翻转
/* USER CODE END Private defines */
(3)中断服务程序
我们将中断服务程序中对单片机的控制都放在stm32f1xx_it.c文件中,我们将在这里利用两个按键的中断来对LED进行亮灭控制。(注:在使用LED的宏时,不要忘记在stm32f1xx_it.c中添加头文件gpio.h)
/* Includes ------------------------------------------------------------------*/
#include "main.h"
#include "stm32f1xx_it.h"
#include "gpio.h" //此为LED和按键的头文件
按键1配置的为上升沿和下降沿都触发中断,故按下按键时红灯亮,松开时红灯灭,而按键2则是按下时绿灯亮。
/**
* @brief This function handles EXTI line0 interrupt.
*/
void EXTI0_IRQHandler(void) //按键1的中断服务程序
{
/* USER CODE BEGIN EXTI0_IRQn 0 */
//确保是否产生了EXTI Line中断
if(__HAL_GPIO_EXTI_GET_IT(KEY1_EXIT_Pin) != RESET)
{
// 红灯 取反
RED_TOGGLE;
//清除中断标志位
__HAL_GPIO_EXTI_CLEAR_IT(KEY1_EXIT_Pin);
}
/* USER CODE END EXTI0_IRQn 0 */
}
/**
* @brief This function handles EXTI line[15:10] interrupts.
*/
void EXTI15_10_IRQHandler(void) //按键2的中断服务程序
{
/* USER CODE BEGIN EXTI15_10_IRQn 0 */
//确保是否产生了EXTI Line中断
if(__HAL_GPIO_EXTI_GET_IT(KEY2_EXIT_Pin) != RESET)
{
// 绿灯 取反
GREEN_TOGGLE;
//清除中断标志位
__HAL_GPIO_EXTI_CLEAR_IT(KEY2_EXIT_Pin);
}
/* USER CODE END EXTI15_10_IRQn 0 */
}
此处的 __HAL_GPIO_EXTI_GET_IT()函数用来判断是否产生了中断,在这里即是否检测到按键的上升沿或下降沿。__HAL_GPIO_EXTI_CLEAR_IT()函数用于清除中断标志位,若不清除则会卡在当前中断,不会响应其他已经触发的新中断。