1.1 什么是中断?
中断是单片机正在执行程序时,由于内部或外部事件的触发,打断当前程序,转而去处理这一事件,当处理
完成后再回到原来被打断的地方继续执行原程序的过程。
在ARM体系结构中,中断通常由外设或外部输入产生,有时也可以由软件触发。中断是单片机系统处理紧急
或突发事件的重要方式,如定时器溢出、按键输入、串口数据到达等。
1.2 中断的意义
中断的主要意义在于提高CPU的效率,而不会一直占用CPU,实现对突发事件的实时处理,以及实现程序的
并行化和嵌入式系统进程之间的切换。相较于轮询方式(即按照一定的频率和周期不断地检测某些事件的发
生),中断在处理一些偶然发生的事情时效率更高。
1.3 中断优先级
中断具有优先级高低之分,两个中断同时触发,则优先响应高优先级中断,再响应低优先级中断。
1.4 中断嵌套
如果一个高优先级的中断发生,它会立即打断当前正在处理的中断(如果其优先级较低),并首先处理这个
高优先级的中断,这就是所谓的中断嵌套。
1.5 中断执行流程
当中断发生时,STM32的执行流程如下:首先,由外设发出中断请求;然后,处理器暂停当前执行的任务,
保护现场(如将当前位置的PC地址压栈);接着,程序跳转到对应的中断服务程序(ISR)并执行;中断服
务程序执行完毕后,恢复现场(如将栈顶的值送回PC);最后,处理器返回到被中断的位置,继续执行下一
个指令。
2.1 STM32F103C8T6中断数量
STM32F103C8T6 支持的中断共有 70 个,其中包括 10 个内核中断和 60 个外部中断。外部中断包含 EXTI、
TIM、USART、ADC、I2C、SPI 等等。
内核中断
2.2 中断向量表
STM32的中断向量表是一个存储中断处理函数地址的数组,位于Flash区的起始位置。每个数组元素对应一
个中断源,其地址指向相应的中断服务程序。当中断发生时,处理器会根据中断号查找向量表,然后跳转到
对应的中断服务程序执行。
中断向量表的主要作用是解决中断函数地址不固定与中断必须跳转到固定地方执行程序之间的矛盾。由于编
译器每次编译都会为中断函数随机分配地址,但硬件要求中断必须跳转到固定的位置,因此,中断向量表就
作为这样一个固定的地址列表,其中包含了中断函数的地址以及跳转到这些地址的程序。当中断发生时,处
理器会跳转到这个固定的中断向量表,然后根据其中的信息跳转到相应的中断处理函数,从而执行中断。
通俗一点讲就是 中断函数的地址是不固定的 所以需要通过中断向量表的中转地址 找到中断代码 因为向量表里 存的就是中断代码的地址
NVIC
3.1 NVIC介绍
NVIC,即Nested Vectored Interrupt Controller(嵌套向量中断控制器),是STM32中的中断控制器。它
负责管理和协调处理器的中断请求,是STM32中处理异步事件的重要机制。
NVIC提供了灵活、高效、可扩展的中断处理机制,支持多级优先级、多向中断、嵌套向量中断等特性。当一
个中断请求到达时,NVIC会确定其优先级并决定是否应该中断当前执行的程序,以便及时响应和处理该中断
请求。这种设计有助于提高系统的响应速度和可靠性,特别是在需要处理大量中断请求的实时应用程序中。
NVIC 支持:256个中断(16内核+240外部),支持:256个优先级,允许裁剪。
内核中断优先级最高 第一个响应 外部中断通过配置 可以自己做优先级 NVIC对外部中断有使能失能的控制权 和排序先后
中断优先级分为抢占优先级 和 响应 优先级优先级 自然优先级
-
抢占优先级
如果一个中断的优先级高于正在执行的中断 那就打断当前中断 开始执行高抢占优先级 数值越小 优先级越高 高抢占优先级执行完以后 返回之前被打断的抢占优先级 继续执行 数值越小 优先级越高 -
响应优先级
如果两个中断同时到达 他们的抢占优先级相同 那么响应优先级高的中断 首先得到响应 数值越小,优先级越高 (同时)
3.自然优先级
自然优先级是由硬件固定并预先设定的,用户无法更改。 当抢占优先级 和 响应优先级都相同时 自然优先级决定那个中断优先执行
4.优先级执行顺序
当多个中断同时发生时 执行顺序首先由抢占优先级决定。如果抢占优先级相同,则进一步由响应优先级决定。如果响应优先级还相同,那 么就由自然优先级决定谁先执行
在中断嵌套的情况下 高抢占优先级的中断可以打断地抢占优先级的中断,优先执行,但高响应优先级的中断不能打断低相应优先级的中断(前提是他们抢占优先级相同)
5.优先级分组
优先级寄存器IPR有8位 但实际只使用了高4位,用于决定抢占优先级,响应优先级的等级。
NVIC寄存器
NVIC经常用的函数
中断优先级使能 失能
和优先级 优先级分组
3.5 NVIC相关函数介绍
3.6 NVIC配置方法
设置中断分组 → 设置中断优先级 → 使能中断
设置中断分组一般在 HAL_Init 函数中进行。
4. EXTI
4.1 EXTI简介
EXTI 是 External Interrupt 的缩写,表示外部中断事件控制器。EXTI 可以监测指定 GPIO 口的电平信号变
化,并在检测到指定条件时,向内核的中断控制器 NVIC 发出中断申请。NVIC 在裁决后,如果满足条件,会
中断CPU的主程序,使 CPU 转而执行 EXTI 对应的中断服务程序。
EXTI 支持的触发方式:上升沿、下降沿、双边沿或软件触发。
EXTI 支持所有的 GPIO 口,但需要注意的是,相同的 Pin 不能同时触发中断。例如,PA0 和 PB0 不能同时被
配置为中断源。
EXTI 提供了 16 个 GPIO_Pin 的中断线,以及额外的中断线如 PVD 输出、RTC 闹钟、USB 唤醒和以太网唤
醒。
通过适当的配置,EXTI可以实现丰富多样的功能,如响应按键的按下、传感器的状态变化等外部事件。4.2 中断/事件
中断会打断CPU当前正在执行的程序,转而去执行中断服务程序,待中断服务程序执行完毕后,CPU会返回
到原来的程序执行点继续执行。
事件只是简单地表示某个动作或状态的变化,而不会打断CPU当前正在执行的程序。当事件发生时,它会根
据配置来决定是否触发相应的中断。如果开放了对应的中断屏蔽位,事件就可以触发相应的中断,否则事件
只会作为一个信号存在,不会被CPU处理。
中断寄存器
中断函数在gpio里 主要配置在init 还有中断服务函数 回调函数
5. AFIO
5.1 AFIO简介
AFIO 是 Alternate Function Input/Output 的缩写,表示复用功能 IO,主要用于实现 I/O 端口的复用功能以
及外部中断的控制。
STM32上有很多 I/ O口以及内置外设(如I2C、ADC、ISP、USART等)。为了节省引出管脚的数量,这些内
置外设通常与 I/O 口共用管脚,即 I/O 管脚具有复用功能。例如,一个 GPIO 管脚除了可以作为普通的 I/O
端口外,还可以被复用为某个内置外设的功能引脚。
然而,为了优化64脚或100脚封装的外设数量,有时需要将一些复用功能重新映射到其他引脚上。这时,就
可以使用AFIO的复用重映射功能。通过设置复用重映射和调试I/O配置寄存器(AFIO_MAPR),可以实现引
脚的重新映射,使得复用功能不再映射到它们的原始分配上。
此外,AFIO 还用于控制外部中断,用来配置 EXTI 中断线 0~15 对应哪个具体 IO 口。
当需要使能外部中断线或进行外部中断线的映射时,通常需要开启AFIO的时钟。
5.2 AFIO与IO口对应关系
有4个寄存器 每个寄存器通过4位控制一个外部中断输入源 共16个
EXTI配置流程
外部中断点灯 +led2闪烁
#include "exti.h"
#include "sys.h"
#include "delay.h"
#include "led.h"
void exti_init(void)
{
//开启时钟
__HAL_RCC_GPIOA_CLK_ENABLE();
//定义结构体
GPIO_InitTypeDef gpio_initstruct;
gpio_initstruct.Pin = GPIO_PIN_0;
gpio_initstruct.Mode = GPIO_MODE_IT_FALLING;//检测上升沿
gpio_initstruct.Pull = GPIO_PULLUP;
gpio_initstruct.Speed = GPIO_SPEED_HIGH;
HAL_GPIO_Init(GPIOA,&gpio_initstruct);
HAL_NVIC_SetPriority(EXTI0_IRQn,2,0);//设置优先级
HAL_NVIC_EnableIRQ(EXTI0_IRQn);//使能中断线
}
void EXTI0_IRQHandler(void) //中断0服务函数
{
HAL_GPIO_EXTI_IRQHandler(GPIO_PIN_0);//中断服务函数
}
void HAL_GPIO_EXTI_Callback(uint16_t GPIO_PIN)//中断回调函数
{
delay_ms(20);
if(GPIO_PIN == GPIO_PIN_0)//判断来的信号是否是0
{
if(HAL_GPIO_ReadPin(GPIOA,GPIO_PIN_0) == GPIO_PIN_RESET)//判断信号是否是低电平
{
led1_toggle();//翻转
}
}
}
#include "sys.h"
#include "uart1.h"
#include "delay.h"
#include "led.h"
#include "exti.h"
int main(void)
{
HAL_Init(); /* 初始化HAL库 */
stm32_clock_init(RCC_PLL_MUL9); /* 设置时钟, 72Mhz */
led_init();
exti_init();
while(1)
{
led2_off();
delay_ms(500);
led2_on();
delay_ms(500);
}
}