概述

打断CPU执行正常的程序,转而处理紧急程序,然后返回原暂停的程序继续运行,就叫中断。

中断的作用和意义

  1. 实时控制:在确定时间内对相应事件作出响应,如:温度监控
  2. 故障处理:检测到故障,需要第一时间处理,如:电梯门夹人了
  3. 数据传输:不确定数据何时会来,如:串口数据接收

[!IMPORTANT]

中断的意义:高效处理紧急程序,不会一直占用CPU资源

执行流程

中断的执行流程

NVIC

基本概念

用于统一分配中断优先级和管理中断的

==Nested vectored interrupt controller==,嵌套向量中断控制器,属于内核(M3/4/7)

NVIC支持:256个中断(16内核+240外部),支持:256个优先级,允许裁剪!

STM32型号 内核中断 外部中断 中断优先级
STM32F103xx 10 60 16
STM32F407xx 10 82 16
STM32F429xx 10 91 16
STM32F750xx 10 98 16
STM32F767xx 10 110 16
STM32H743xx 10 150 16
STM32H750xx 10 150 16

中断向量表

定义一块固定的内存,以字节对齐,存放各个==中断服务函数程序的首地址==。

中断向量表定义在==启动文件中==,当发生中断,CPU会自动执行对应的中断服务函数,以下有个中断向量表的例子:

Position Priority Priority type Acronym Description Address
0 - - MSP初值 主栈指针初始化地址 0x0000_0000
1 -3 fixed Reset 复位向量 0x0000_0004
2 -2 fixed NMI WKUPx中断 低功耗模式错误中断 0x0000_0008
3 -1 fixed HardFault HardFault中断向量 0x0000_000C
4-10 - - - Reserved 0x0000_0010~0x0000 002B
11 3 settable SVC SVCall系统服务请求 0x0000 002C
12-13 - - - Reserved 0x0000_0030~0x0000 0037
14 5 settable PendSV 可挂起系统服务请求 0x0000_0038
15 6 settable Systick 内部定时器中断向量 0x0000_003C
16 7 settable WDT 窗口看门狗或独立看门狗 0x0000_0040
17 8 settable SVD 中断 电源监测报警中断 0x0000_0044
18 9 settable RTCA/RTCB 实时时钟中断 0x0000_0048
19 10 settable FLASH NVMIF中断 0x0000_004C
20 11 settable FDET XTLF 或XTHF停振检测 中断 系统时钟选择错误中断 0x0000_0050
21 12 settable ADC ADC转换完成中断 0x0000_0054
22 13 settable DAC DAC中断 0x0000_0058
23 14 settable SPI0 SPI中断 0x0000_005C
24 15 settable SPI1 : 0x0000_0060
25 16 settable SPI2 : 0x0000_0064
26 17 settable UART0 UART中断 0x0000_0068
27 18 settable UART1 : 0x0000_006C
28 19 settable UART3 : 0x0000_0070
29 20 settable UART4 : 0x0000_0074
30 21 settable UART5 : 0x0000_0078
31 22 settable U7816 U7816中断 0x0000_007C
32 23 settable LPUARTX LPUART0/1/2中断 0x0000_0080
33 24 settable I2C I2C中断 0x0000_0084
34 25 settable CCL 时钟校准中断 0x0000_0088
35 26 settable AES AES中断 0x0000_008C
36 27 settable LPTIM LPTIM16或LPTIM32中 断 0x0000_0090
37 28 settable DMA DMA中断 0x0000_0094
38 29 settable WKUPX WKUP引脚中断 0x0000_0098
39 30 settable LUT LUT中断 0x0000_009C
40 31 settable BSTIM BSTIM16或BSTIM32中 断 0x0000_00A0
41 32 settable COMPX COMPx中断 0x0000_00A4
42 33 settable GPT0,1 通用定时器0.1中断 0x0000_00A8
43 34 settable GPT2 通用定时器2中断 0x0000_00AC
44 35 settable ATIM 高级定时器中断 0x0000_00B0
45 36 settable VREF1p2 VREF_VREG 1.2V 内部基准电压建立中断 VREF_VREG中断 0x0000_00B4
46 37 settable EXTI 外部引脚中断 0x0000_00B8
47 38 settable CAN CAN2.0中断 x0000_0OBC 0x0000_00BC

相关寄存器

NVIC相关寄存器 位数 寄存器个数 备注
中断使能寄存器(ISER) 32 8 每个位控制一个中断
中断除能寄存器(ICER) 32 8 每个位控制一个中断
应用程序中断及复位控制寄存器(AIRCR) 32 1 位[10:8]控制优先级分组
中断优先级寄存器 (IPR) 8 240 8个位对应一个中断,而 STM32只使用高4位

NVIC还有:中断挂起,解挂,激活标志等非常有用的功能。

基本结构

NVIC是一个内核外设,是CPU的小助手

NVIC的基本结构

上图为外部中断,其中的n代表一个外设可能同时占用多个中断通道,所以有n条线

NVIC只有一个输出口,NVIC根据每个中断的优先级分配中断的先后顺序,通过一个输出口告诉CPU应该处理哪个中断

中断优先级

  1. ==抢占优先级==(pre):高抢占优先级可以打断正在执行的低抢占优先级中断
  2. ==响应优先级==(sub):当抢占优先级相同时,响应优先级高的先执行,但是不能互相打断
  3. 抢占和响应都相同的情况下,自然优先级越高的,先执行
  4. ==自然优先级==:中断向量表的优先级
  5. 数值越小,表示优先级越高

[!IMPORTANT]

抢占优先级 > 响应优先级 > 自然优先级

NVIC的中断优先级由优先级寄存器的4位(0~15)决定,这4位可以进行切分,分为高n位的抢占优先级和低4-n位的响应优先级抢占优先级高的可以中断嵌套,响应优先级高的可以优先排队,抢占优先级和响应优先级均相同的按中断号排队

优先级分组 AIRCR[10:8] IPRx bit[7:4]分 配 分配结果
0 111 None : [7:4] 0位抢占优先级,4位响应优先级
1 110 [7] : [6:4] 1位抢占优先级,3位响应优先级
2 101 [7:6] : [5:4] 2位抢占优先级,2位响应优先级
3 100 [7:5] : [4] 3位抢占优先级,1位响应优先级
4 011 [7:4] : None 4位抢占优先级,0位响应优先级

[!note]

特别提示:一个工程中,一般只设置一次中断优先级分组。

编号 自然优先级 对应外设 抢占 响应 执行顺序
3 10 RTC 2 1 2
6 13 EXTI0 3 0 4
7 14 EXTI1 2 0 1
-1 6 Systick 3 0 3

[!note]

EXTI1和RTC可以打断:EXTIO和Systick的中断,获得优先执行!

EXTI

External(Extended)interrupt/event Controller,外部(扩展)中断事件控制器包含20个产生事件/中断请求的边沿检测器,即总共:20条EXTI线(F1)

中断和事件的理解:

  • 中断:要进入NVIC,有相应的中断服务函数,需要CPU处理
  • 事件:不进入NVIC,仅用于内部硬件自动控制的,如:TIM、DMA、ADC

产生的中断和事件都会有他们的响应函数

  • 中断响应申请中断,让CPU执行中断函数
  • 事件响应,外部中断信号不会通向CPU,而是通向其他外设,用来触发其他外设的操作(ADC转换、DMA等)

EXTI(Extern Interrupt)外部中断

  • EXTI可以监测指定GPIO口的电平信号,当其指定的GPIO口产生电平变化时,EXTI将立即向NVIC发出中断申请,经过NVIC裁决后即可中断CPU主程序,使CPU执行EXTI对应的中断程序
  • 支持的触发方式:上升沿/下降沿/双边沿/软件触发
  • 支持的GPIO口:所有GPIO口,但相同的Pin不能同时触发中断
  • 通道数:16个GPIO Pin,外加PVD输出、RTC闹钟、USB唤醒、以太网唤醒
  • 触发响应方式:中断响应/事件响应

[!tip]

当有多个中断引脚的时候,我们需要选择不同PIN的引脚,比如PA10和PB10就不能同时作为中断引脚

事件请求表

中断线 F1 F4 F7 H7
EXTI线0~15:对应GPIO PIN 0~15
EXTI线16:PVD输出 参考H7参考手册(中文版)657页
EXTI线17:RTC闹钟事件
EXTI线18:USB OTG FS唤醒事件
EXTI线19:以太网唤醒事件
EXTI线20:USB OTG HS唤醒事件
EXTI线21:RTC 入侵和时间戳事件
EXTI线22:RTC 唤醒事件
EXTI线23:LPTIM1 异步事件

整体结构

EXTI整体结构

每个GPIO端口有16个引脚,而EXTI实际上只有16个通道,所以==每个端口只能有一个中断引脚==,GPIO口加上PVD、RTC、USB、ETH等一共20个信号。

[!TIP]

注意,EXTI 5 ~ 9使用同一个中断函数,EXTI 10 ~ 15使用一个中断函数,所以在写中断函数的时候,我们需要根据标志位来区分是哪个中断

到其他外设的20个信号是事件触发时的信号

信号方向:==输入线 > 边沿检测电路(用于上升沿触发或者下降沿触发)> 检测上升沿、下降沿、双边沿及软件触发 > 出现分支==

  • 中断:==如果请求挂起寄存器置1 > NVIC中断控制器==
  • 事件:通过事件屏蔽寄存器做一个开关的作用 -> 脉冲发生器,给一个电平脉冲,用来触发其他外设的动作

AFIO

Alternate Function IO,即复用功能lO,主要用于重映射和外部中断映射配置

  1. 调试I○配置:AFIO MAPR[26:24],配置JTAG/SWD的开关状态

  2. 重映射配置:AFIO MAPR,部分外设IO重映射配置

  3. 外部中断配置:AFIO EXTICR1 ~ 4,配置EXTI中断线0 ~ 15对应具体哪个1O口

    [!NOTE]

    特别注意:配置AFIO寄存器之前要使能AFIO时钟,方法如下:
    HAL RCC AFIO CLK ENABLE(); 对应RCC APB.2ENR寄存器位0

AFIO主要完成两个任务:

  1. 复用功引脚重映射
  2. 中断引脚选择

如将GPIO A PIN1引脚作为TIM1的引脚

EXTI和IO的对应关系

ETIO和IO的对应关系

这里可以看出来一条线只能对应一个口,如不能同时使用PA0和PB0,只能从P[A~K]0中选一个

中断配置

中断配置步骤:

  1. 使能GPIO时钟:使能GPIO时钟
  2. 设置GPIO输入模式:上/下拉/浮空输入
  3. 使能AFIO/SYSCFGI时钟:设置AFIO/SYSCFGE时钟开启寄存器
  4. 设置EXTI和IO对应关系
  5. 设置EXTI屏蔽,上/下:设置EXTI对应通道前的屏蔽和上升沿/下降沿确发,IMR、RTSR/FTSR沿
  6. 设置NVIC:分3步:设置优先级分组、设置优先级、使能中断
  7. 设计中断服务函数:编写对应中断的中断服务函数!清中断标志!

实验

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
void GPIO_init(){	
//配置GPIO和AFIO时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO,ENABLE);

//配置GPIO
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOB,&GPIO_InitStructure);

//配置AFIO
GPIO_EXTILineConfig(GPIO_PortSourceGPIOB,GPIO_PinSource9);

//配置EXTI
EXTI_InitTypeDef EXTI_InitStructure;
EXTI_InitStructure.EXTI_Line = EXTI_Line9;
EXTI_InitStructure.EXTI_LineCmd = ENABLE;
EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt;
EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Falling;
EXTI_Init(&EXTI_InitStructure);

//配置NVIC
NVIC_InitTypeDef NVIC_InitStruct;
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_1);
NVIC_InitStruct.NVIC_IRQChannel = EXTI9_5_IRQn;
NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority = 3;
NVIC_InitStruct.NVIC_IRQChannelSubPriority = 3;
NVIC_InitStruct.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStruct);
}

void EXTI9_5_IRQHandler(void){
if(EXTI_GetITStatus(EXTI_Line9) == SET){
EXTI_ClearITPendingBit(EXTI_Line9);
}
}