https://zhuanlan.zhihu.com/p/123293254

1

STM32F4 的每个 IO 都可以作为外部中断的中断输入口,这点也是 STM32F4 的强大之处。STM32F429 的中断控制器

支持 22个外部中断/事件请求。每个中断设有状态位,每个中断/事件都有独立的触发和屏蔽设置。

STM32F429有22个外部中断为,我们这里只看IO口的16个外部中断:EXTI 线 0~15:对应外部 IO 口的输入中断。

STM32F4 供 IO 口使用的中断线只有 16 个,但是 STM32F4 的 IO 口却远远不止 16 个,那么 STM32F4 是怎么把 16 个中断线和 IO 口一一对应起来的呢?于是 STM32就这样设计,GPIO 的引脚 GPIOx.0-GPIOx.15(x=A,B,C,D,E,F,G,H,I)分别对应中断线 0~15。这样每个中断线对应了最多 9 个 IO 口,以线 0 为例:它对应了 GPIOA.0、GPIOB.0、GPIOC.0、GPIOD.0、GPIOE.0、GPIOF.0、GPIOG.0,GPIOH.0,GPIOI.0。而中断线每次只能连接到 1 个 IO口上,这样就需要通过配置来决定对应的中断线配置到哪个 GPIO 上了。 我举一个例子:我们一个学校(对应一个单片机)有16个老师(对应16根中断线)。但是我们现在有9个班级(GPIOA.0-GPIOI)。每个班级有16个同学(GPIOA_0.....GPIOA_15)。如何让这16位老师负责9个班级一共9X16=144个学生呢? 现在的方法就是:让第1个老师负责每个班级的第1位同学。让第2个老师负责每个班级的第2位同学........,让第16个老师负责每个班级的第16位同学这样就可以了对吧。

程序配置

1.第一步当然是初始化你的IO口了对吧。比如我们开始写按键的时候是这样写的。

void KEY_Init(void)
{
  GPIO_InitTypeDef GPIO_InitStructure;
  RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);  //使能PA端口时钟
  GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;           //端口配置
  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;          //上拉输入
  GPIO_Init(GPIOC, &GPIO_InitStructure);       //根据设定参数初始化GPIOC
}

2.初始化了IO口,接下来我们要干嘛呢?你不是要让按键按下了之后去干别的事吗?那就打开IO口的复用功能: 使能EXTI外设对应的时钟----注意:当使用EXTI外设时,使能的是AFIO时钟,而不是EXTI外设时钟

RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO,ENABLE);

3.现在就是要把中断线和对应的IO口的关系给连接上! 打开stm32f4xx_gpio.h。因为每一组GPIO都有16个管脚,所以这里最大是从GPIO_PinSource0到GPIO_PinSource15。

利用**GPIO_EXTILineConfig()**将EXTI线0连接到端口GPIOA的第0个针脚上 具体代码:

GPIO_EXTILineConfig(GPIO_PortSourceGPIOA,GPIO_PinSource0);

4.接下来就是初始化EXTI了

void exti_Init(void)
{  
EXTI_InitTypeDef   EXTI_InitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO,ENABLE); //外部中断,需要使能AFIO时钟
GPIO_EXTILineConfig(GPIO_PortSourceGPIOA,GPIO_PinSource0);//将EXTI线连接到对应的IO端口上
EXTI_InitStructure.EXTI_Line  =   EXTI_Line0;  //常用的就是EXTI_Line0-EXTI_Line015负责gpio管脚的那几个
EXTI_InitStructure.EXTI_Mode =    EXTI_Mode_Interrupt;
EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Falling; //下降沿触发
EXTI_InitStructure.EXTI_LineCmd = ENABLE;              //中断线使能
EXTI_Init(&EXTI_InitStructure);                   //初始化中断
}

5.配置NVIC中断优先级 嵌套向量中断控制器(NVIC) 在 NVIC 有一个专门的寄存器:中断优先级寄存器 NVIC_IPRx(在 F429 中,x=0...90)用来配置外部中断的优先级,IPR宽度为 8bit,原则上每个外部中断可配置的优先级为0~255,数值越小,优先级越高。但是绝大多数 CM4芯片都会精简设计,以致实际上支持的优先级数减少,在 F429 中,只使用了高 4bit,就是每个外部中断可配置的优先级为0-16。如下所示:

用于表达优先级的这 4bit,又被分组成抢占优先级和子优先级。如果有多个中断同时响应,抢占优先级高的就会 抢占 抢占优先级低的优先得到执行,如果抢占优先级相同,就比较子优先级。如果抢占优先级和子优先级都相同的话,就比较他们的硬件中断编号,编号越小,优先级越高。 优先级的分组 这里我们用阶级来表示抢占优先级用阶层来表示子优先级,通常我们响应优先级也叫做子优先级。

如果我们按照NVIC_PriorityGroup_4这样分组的话,系统就分配了4位抢占优先级。0位响应优先级。就分了16个阶级(2^4=16),0个阶层。 比如我来了一个中断叫做外部中断1(EXTI1_IRQn)。他的抢占优先级就可以设置为0-15.响应优先级就只能设置为0,假如在这个时候又来了一个中断叫做外部中断2(EXTI2_IRQn)。他的抢占优先级就可以设置为0-15.响应优先级就只能设置为0。这个个中断可以设置为一样的,也可以设置为不一样的。如果假如够设置抢占优先级为4,那么系统就看哪一个中断县发生,先发生就先执行。

NVIC_PriorityGroupConfig(NVIC_PriorityGroup_4);//设置NVIC中断分组4:4位抢占优先级,0位响应优先级
NVIC_InitStructure.NVIC_IRQChannel = EXTI1_IRQn;//使能外部中断通道
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 4;//抢占优先级4  因为为分组为4 这里可以设置为0-15
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;//响应优先级0
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;//使能外部中断通道
NVIC_Init(&NVIC_InitStructure);   //根据NVIC_InitStruct中指定的参数初始化外设NVIC寄存器

如果我们按照NVIC_PriorityGroup_2这样分组的话,系统就分配了2位抢占优先级。2位响应优先级。就分了4个阶级(2^2=4),4个阶层。 比如我来了一个中断叫做外部中断1(EXTI1_IRQn)。他的抢占优先级就不能设置为0-15,范围应该是0-3,响应优先级就只能设置为0,假如在这个时候又来了一个中断叫做外部中断2(EXTI2_IRQn)。他的抢占优先级就设置范围为0-3.响应优先级范围也是0-3。同样的他们的优先级可以设置为一样的,也可以设置为不一样的。如果外部中断1的抢占优先级为2,响应优先级为1,外部中断2的抢占优先级为2,响应优先级为0。那么当两个中断同时发生的时候就会首先响应外部中断2,因为外部中断2响应优先级高于外部中断1.数值越小优先级越高.

NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);//设置NVIC中断分组2:2位抢占优先级,2位响应优先级
NVIC_InitStructure.NVIC_IRQChannel = EXTI1_IRQn;//使能外部中断通道
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 2;//抢占优先级2  因为为分组为2 这里可以设置为0-3
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;//响应优先级0
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;//使能外部中断通道
NVIC_Init(&NVIC_InitStructure);   //根据NVIC_InitStruct中指定的参数初始化外设NVIC寄存器

NVIC_InitStructure.NVIC_IRQChannel = EXTI2_IRQn;         //使能外部中断通道
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 2;//抢占优先级2  因为为分组为4 这里可以设置为0-3
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;   //响应优先级0
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;//使能外部中断通道
NVIC_Init(&NVIC_InitStructure);    //根据NVIC_InitStruct中指定的参数初始化外设NVIC寄存器

一般情况下,系统代码执行过程中,只设置一次中断优先级分组,比如分组2,设置好分组之后一般不会再改变分组。 随意改变分组会导致中断管理混乱,程序出现意想不到的执行结果。换句话说NVIC_PriorityGroupConfig();这个函数在你的整个程序中只能设置一次,这个要切记!!!

6.用到的管脚与中断线的序号是要一一对应的,,不管A--H用的哪一组的管脚,PIN1就要对应EXTI1.....PIN15就要对应EXTI15。同时也要对应相应的中断函数,在库函数中EXTI0_IRQn,EXTI1_IRQn,EXTI2_IRQn,EXTI3_IRQn,EXTI4_IRQn,EXTI9_5_IRQn(EXTI5-EXTI9都对应这个中断),EXTI15_10_IRQn(EXITI10-EXTI15都对应这个中断函数)。这些中断通道全都在stm32f4xx.h中用了一个枚举结构体包起来了。想用哪一个找到对应的通道写上就可以了。