【自学嵌入式:stm32单片机】EXTI外部中断
EXTI外部中断
中断系统
- 中断:在主程序运行过程中,出现了特定的中断触发条件(中断使得CPU暂停当前正在运行的程序,转而去处理中断程序源),处理完成后又返回原来被暂停的位置继续运行
- 中断优先级:当有多个中断源同时申请中断时,CPU会根据中断源的轻重缓急进行裁决,优先响应更加紧急的中断源
- 中断嵌套:当一个中断程序正在运行时,又有新的更高优先级的中断源申请中断,CPU再次暂停当前中断程序,转而去处理新的中断程序,处理完成后依次进行返回
中断执行流程

STM32中断
- 68个可屏蔽中断通道,包含EXTI、TIM、ADC、USART、SPI、I2C、RTC等多个外设
- 使用NVIC统一管理中断,每个中断通道都拥有16个可编程的优先等级,可对优先级进行分组,进一步设置抢占优先级和响应优先级
内核中断
| 位置 | 优先级 | 优先级类型 | 名称 | 说明 | 地址 | 
|---|---|---|---|---|---|
| - | - | - | 保留 | 0x0000_0000 | |
| -3 | 固定 | Reset | 复位 | 0x0000_0004 | |
| -2 | 固定 | NMI | 不可屏蔽中断 RCC时钟安全系统(CSS)联接到NMI向量 | 0x0000_0008 | |
| -1 | 固定 | 硬件失效(HardFault) | 所有类型的失效 | 0x0000_000C | |
| 0 | 可设置 | 可设置 | 存储管理(MemManage) | 存储器管理 | 0x0000_0010 | 
| 1 | 可设置 | 可设置 | 总线错误(BusFault) | 预取指失败,存储器访问失败 | 0x0000_0014 | 
| 2 | 可设置 | 可设置 | 错误应用(UsageFault) | 未定义的指令或非法状态 | 0x0000_0018 | 
| - | - | - | 保留 | 0x0000_001C ~0x0000_002B | |
| 3 | 可设置 | 可设置 | SVCcall | 通过SWI指令的系统服务调用 | 0x0000_002C | 
| 4 | 可设置 | 可设置 | 调试监控(DebugMonitor) | 调试监控器 | 0x0000_0030 | 
| - | - | - | 保留 | 0x0000_0034 | |
| 5 | 可设置 | 可设置 | PendSV | 可挂起的系统服务 | 0x0000_0038 | 
| 6 | 可设置 | 可设置 | SysTick | 系统嘀嗒定时器 | 0x0000_003C | 
外设中断
| 位置 | 优先级 | 优先级类型 | 名称 | 说明 | 地址 | 
|---|---|---|---|---|---|
| 0 | 7 | 可设置 | WWDG | 窗口定时器中断 ,窗口看门狗,用来检测程序运行的中断,如果程序卡死了,窗口看门狗就会申请中断 | 0x0000_0040 | 
| 1 | 8 | 可设置 | PVD | 连到EXTI的电源电压检测(PVD)中断,电源电压不足,PVD会申请中断,是不是电池没电了,要赶紧保存一下重要数据 | 0x0000_0044 | 
| 2 | 9 | 可设置 | TAMPER | 侵入检测中断 | 0x0000_0048 | 
| 3 | 10 | 可设置 | RTC | 实时时钟(RTC)全局中断 | 0x0000_004C | 
| 4 | 11 | 可设置 | FLASH | 闪存全局中断 | 0x0000_0050 | 
| 5 | 12 | 可设置 | RCC | 复位和时钟控制(RCC)中断 | 0x0000_0054 | 
| 6 | 13 | 可设置 | EXTI0 | EXTI线0中断 | 0x0000_0058 | 
| 7 | 14 | 可设置 | EXTI1 | EXTI线1中断 | 0x0000_005C | 
| 8 | 15 | 可设置 | EXTI2 | EXTI线2中断 | 0x0000_0060 | 
| 9 | 16 | 可设置 | EXTI3 | EXTI线3中断 | 0x0000_0064 | 
| 10 | 17 | 可设置 | EXTI4 | EXTI线4中断 | 0x0000_0068 | 
| 11 | 18 | 可设置 | DMA1通道1 | DMA1通道1全局中断 | 0x0000_006C | 
| 12 | 19 | 可设置 | DMA1通道2 | DMA1通道2全局中断 | 0x0000_0070 | 
| 13 | 20 | 可设置 | DMA1通道3 | DMA1通道3全局中断 | 0x0000_0074 | 
| 14 | 21 | 可设置 | DMA1通道4 | DMA1通道4全局中断 | 0x0000_0078 | 
| 15 | 22 | 可设置 | DMA1通道5 | DMA1通道5全局中断 | 0x0000_007C | 
| 16 | 23 | 可设置 | DMA1通道6 | DMA1通道6全局中断 | 0x0000_0080 | 
| 17 | 24 | 可设置 | DMA1通道7 | DMA1通道7全局中断 | 0x0000_0084 | 
| 18 | 25 | 可设置 | ADC1_2 | ADC1和ADC2全局中断 | 0x0000_0088 | 
| 19 | 26 | 可设置 | USB_HP_CAN_TX | USB高优先级或CAN发送中断 | 0x0000_008C | 
| 20 | 27 | 可设置 | USB_LP_CAN_RX0 | USB低优先级或CAN接收0中断 | 0x0000_0090 | 
| 21 | 28 | 可设置 | CAN_RX1 | CAN接收1中断 | 0x0000_0094 | 
| 22 | 29 | 可设置 | CAN_SCE | CAN SCE中断 | 0x0000_0098 | 
| 23 | 30 | 可设置 | EXTI9_5 | EXTI线[9:5]中断 | 0x0000_009C | 
| 24 | 31 | 可设置 | TIM1_BRK | TIM1刹车中断 | 0x0000_00A0 | 
| 25 | 32 | 可设置 | TIM1_UP | TIM1更新中断 | 0x0000_00A4 | 
| 26 | 33 | 可设置 | TIM1_TRG_COM | TIM1触发和通信中断 | 0x0000_00A8 | 
| 27 | 34 | 可设置 | TIM1_CC | TIM1捕获比较中断 | 0x0000_00AC | 
| 28 | 35 | 可设置 | TIM2 | TIM2全局中断 | 0x0000_00B0 | 
| 29 | 36 | 可设置 | TIM3 | TIM3全局中断 | 0x0000_00B4 | 
| 30 | 37 | 可设置 | TIM4 | TIM4全局中断 | 0x0000_00B8 | 
| 31 | 38 | 可设置 | I2C1_EV | I²C1事件中断 | 0x0000_00BC | 
| 32 | 39 | 可设置 | I2C1_ER | I²C1错误中断 | 0x0000_00C0 | 
| 33 | 40 | 可设置 | I2C2_EV | I²C2事件中断 | 0x0000_00C4 | 
| 34 | 41 | 可设置 | I2C2_ER | I²C2错误中断 | 0x0000_00C8 | 
| 35 | 42 | 可设置 | SPI1 | SPI1全局中断 | 0x0000_00CC | 
| 36 | 43 | 可设置 | SPI2 | SPI2全局中断 | 0x0000_00D0 | 
| 37 | 44 | 可设置 | USART1 | USART1全局中断 | 0x0000_00D4 | 
| 38 | 45 | 可设置 | USART2 | USART2全局中断 | 0x0000_00D8 | 
| 39 | 46 | 可设置 | USART3 | USART3全局中断 | 0x0000_00DC | 
| 40 | 47 | 可设置 | EXTI15_10 | EXTI线[15:10]中断 | 0x0000_00E0 | 
| 41 | 48 | 可设置 | RTCAlarm | 连到EXTI的RTC闹钟中断 | 0x0000_00E4 | 
| 42 | 49 | 可设置 | USB唤醒 | 连到EXTI的从USB待机唤醒中断 | 0x0000_00E8 | 
| 43 | 50 | 可设置 | TIM8_BRK | TIM8刹车中断 | 0x0000_00EC | 
| 44 | 51 | 可设置 | TIM8_UP | TIM8更新中断 | 0x0000_00F0 | 
| 45 | 52 | 可设置 | TIM8_TRG_COM | TIM8触发和通信中断 | 0x0000_00F4 | 
| 46 | 53 | 可设置 | TIM8_CC | TIM8捕获比较中断 | 0x0000_00F8 | 
| 47 | 54 | 可设置 | ADC3 | ADC3全局中断 | 0x0000_00FC | 
| 48 | 55 | 可设置 | FSMC | FSMC全局中断 | 0x0000_0100 | 
| 49 | 56 | 可设置 | SDIO | SDIO全局中断 | 0x0000_0104 | 
| 50 | 57 | 可设置 | TIM5 | TIM5全局中断 | 0x0000_0108 | 
| 51 | 58 | 可设置 | SPI3 | SPI3全局中断 | 0x0000_010C | 
| 52 | 59 | 可设置 | UART4 | UART4全局中断 | 0x0000_0110 | 
| 53 | 60 | 可设置 | UART5 | UART5全局中断 | 0x0000_0114 | 
| 54 | 61 | 可设置 | TIM6 | TIM6全局中断 | 0x0000_0118 | 
| 55 | 62 | 可设置 | TIM7 | TIM7全局中断 | 0x0000_011C | 
| 56 | 63 | 可设置 | DMA2通道1 | DMA2通道1全局中断 | 0x0000_0120 | 
| 57 | 64 | 可设置 | DMA2通道2 | DMA2通道2全局中断 | 0x0000_0124 | 
| 58 | 65 | 可设置 | DMA2通道3 | DMA2通道3全局中断 | 0x0000_0128 | 
| 59 | 66 | 可设置 | DMA2通道4_5 | DMA2通道4和DMA2通道5全局中断 | 0x0000_012C | 
表格中的EXTI0~EXTI4,下面的EXTI9_5~ EXTI15_10是本节要用到的外部中断资源,表右侧有中断地址,程序中的中断函数的地址是由编译器来分配的,是不固定的,中断跳转由于硬件的限制,只能跳到固定的地址执行程序,所以为了能让硬件跳转到一个不固定的中断函数里,这里就需要在内存中定义一个地址的列表,这个列表地址是固定的,中断发送后,就跳到这个固定位置,然后在这个固定位置,由编译器,再加上一条跳转到中断函数的代码,这样中断跳转就可以跳到任意位置,这个中断地址的列表,就叫中断向量表。
【注】用AI解释一下就是:
咱们用生活里的事儿打个比方,你就明白了:
假设 STM32 芯片是一个 “小区”,里面有很多 “住户”—— 就是你写的各种程序(包括中断函数)。这些住户今天可能住 201,明天可能搬去 302,地址不固定(就像你说的,中断函数地址由编译器分配,不固定)。
现在小区里突然发生了 “突发事件”(比如着火、漏水,对应芯片里的 “中断”,比如按键按下、定时器到点),这时候需要专门的人来处理(就是 “中断函数”)。但小区的 “保安”(硬件)比较死板,他只认识小区里几个固定的 “岗亭”(固定地址),遇到事儿了只能先跑到岗亭,不能直接去找人(硬件限制,只能跳固定地址)。
那怎么让保安找到真正处理事儿的人呢?小区物业就搞了个 “岗亭列表”(中断向量表):每个岗亭都是固定位置(比如 1 号岗在大门左,2 号岗在 3 号楼底),而且每个岗亭里都贴了一张纸条,写着 “处理 XX 事件的人现在在 XXX 地址”(编译器提前写好的跳转指令)。
所以流程就是:
发生突发事件(中断);
保安(硬件)只能先跑到对应的固定岗亭(向量表的固定地址);
岗亭里的纸条(编译器写的跳转代码)告诉保安,真正处理的人在哪个不固定的地址(中断函数);
保安再跑去那个地址,让处理的人干活。
这个 “岗亭列表”,就是中断向量表。它的作用就是用固定的 “中间站”,连接硬件只能认的固定地址和不固定的中断函数地址。
我们用C语言编程是不需要管这个中断向量表的,因为编译器都帮我们做好了
NVIC基本结构

NVIC是一个内核外设,是CPU的助手,STM32中断非常多,如果把这些线都引到CPU上,CPU还得引出很多线进行适配,设计上就很麻烦,并且如果很多中断同时申请,或者中断很懂产生了拥堵,CPU也会很难处理,毕竟CPU主要是用来运算的,中断分配的任务就放到别的地方,所以NVIC就出现了,NVIC有很多输入口,都接着中断线路,每条线写着\(n\)的意思是一个外设可能会同时占用多个中断通道,所以这里有\(n\)条线,NVIC根据每个中断的优先级分配中断的先后顺序,之后,通过右边这一个输出口告诉CPU该处理哪个中断,对于中断先后顺序分配的任务,CPU不需要知道。
NVIC优先级分组
- NVIC的中断优先级由优先级寄存器的4位(0~15)决定(16个优先级,值越小,优先级越高),这4位可以进行切分,分为高n位的抢占优先级和低4-n位的响应优先级,响应优先级可以处理非常紧急的中断(插队)
- 抢占优先级高的可以中断嵌套(可以不等上一个中断处理完,先处理这个非常紧急的中断),响应优先级高的可以优先排队,抢占优先级和响应优先级均相同的按中断号排队
| 分组方式 | 抢占优先级 | 响应优先级 | 
|---|---|---|
| 分组0 | 0位,取值为0 | 4位,取值为0~15 | 
| 分组1 | 1位,取值为0~1 | 3位,取值为0~7 | 
| 分组2 | 2位,取值为0~3 | 2位,取值为0~3 | 
| 分组3 | 3位,取值为0~7 | 1位,取值为0~1 | 
| 分组4 | 4位,取值为0~15 | 0位,取值为0 | 
【注】让AI解释一下:
“分组” 的核心:定两部分各占几位
这 4 位怎么分给 “抢占” 和 “响应”?这就是 “分组” 要做的事。STM32 规定了 5 种分组方式(编号 0~4),比如:
分组 0:抢占优先级占 0 位,响应优先级占 4 位。
(意思是:所有中断抢占优先级都一样,只能靠响应优先级排序,且响应优先级有 16 级)
分组 2:抢占优先级占 2 位,响应优先级占 2 位。
(抢占优先级有 4 级:0~3;响应优先级也有 4 级:0~3)
分组 4:抢占优先级占 4 位,响应优先级占 0 位。
(只有抢占优先级,16 级;响应优先级无效)
举个例子:分组 2 的情况
假设系统用了分组 2(2 位抢占,2 位响应):
中断 A:抢占优先级 0(最高),响应优先级 0
中断 B:抢占优先级 1,响应优先级 0
中断 C:抢占优先级 0,响应优先级 1
规则如下:
A 和 B 同时来:A 抢占优先级更高,先处理 A(A 能打断 B)。
A 和 C 同时来:抢占优先级相同,看响应优先级,A 响应更高,先处理 A。
正在处理 B 时来了 A:A 抢占优先级更高,会打断 B,先处理 A,A 结束后再接着处理 B。
正在处理 A 时来了 C:两者抢占优先级相同,C 不能打断 A,只能等 A 处理完再执行 C。
EXTI
- EXTI(Extern Interrupt)外部中断
- EXTI可以监测指定GPIO口的电平信号,当其指定的GPIO口产生电平变化时,EXTI将立即向NVIC发出中断申请,经过NVIC裁决后即可中断CPU主程序,使CPU执行EXTI对应的中断程序
- 支持的触发方式:上升沿/下降沿/双边沿(上升沿和下降沿都行)/软件触发(写代码主动触发也行)
- 支持的GPIO口:所有GPIO口,但相同的Pin不能同时触发中断 (比如说,PA1和PB1不能同时中断,简单说:同号引脚(Pin1)的不同端口(PA/PB/PC...)不能同时配置触发中断(因为共用一条线),但可以切换配置分别使用。)
- 通道数:16个GPIO_Pin,外加PVD输出、RTC闹钟、USB唤醒、以太网唤醒 (除了16个GPIO引脚,其他都是来“蹭网”的,用来唤醒STM32,借助它发生电平变化就中断的原理,要传输数据,唤醒休眠的stm32)
- 触发响应方式:中断响应/事件响应 (如果选择触发事件,那外部中断的信号就不会通向CPU了,触发其他外设操作)
EXTI基本结构

外部中断9-5和外部中断15-10触发同一个中断函数,在编程的时候,我们在这两个中断函数里,需要再根据标志位区分到底是哪个中断进来的
AFIO复用IO口

- AFIO主要用于引脚复用功能的选择和重定义(引脚默认定义的功能重新定义)
- 在STM32中,AFIO主要完成两个任务:复用功能引脚重映射、中断引脚选择
EXTI框图

软件中断和硬件中断接在或门上,任意为1都能触发中断,然后兵分两路,触发中断首先会置体格挂起寄存器,这相当于是一个中断标志位,可以读取这个寄存器判断是哪个通道触发的中断,如果中断挂起寄存器值1就继续向左走,和中断屏蔽寄存器共同进入一个与门,然后是NVIC中断控制器
 
                    
                 
                
            
         浙公网安备 33010602011771号
浙公网安备 33010602011771号