目录
一、EXTI简介
EXTI(Extern Interrupt)外部中断。EXTI可以监测指定GPIO口的电平信号,当其指定的GPIO口产生电平变化时,EXTI将立即向NVIC发出中断申请,经过NVIC裁决后即可中断CPU主程序,使CPU执行EXTI对应的中断程序。
STM32F4的EXTI包含多达 23 个用于产生事件/中断请求的边沿检测器包含多达 23 个用于产生事件/中断请求的边沿检测器,并且每个都可以进行单独配置。
且可以配置成(支持的触发方式):上升沿触发、下降沿触发、边沿(双边)触发、软件触发
支持的GPIO口:所有GPIO口,但相同的Pin不能同时触发中断 (例如PA1与PB1不能同时)
通道数:16个GPIO_Pin(主要),外加PVD输出、RTC闹钟、USB OTG FS唤醒、以太网唤醒、USB OTG HS唤醒、RTC入侵和时间戳、RTC事件唤醒
触发响应方式:中断响应/事件响应(事件响应是指中断产生后通向其他外设)
二、EXTI 工作的基本结构

其中(16)上的数字表示有16个通道。首先要经过 SYSCFG 进行中断引脚的选择,然后再进入EXTI 边沿检测,同时还有 7 个通道直接接到 EXTI 上,所以一共有23个通道;经过EXTI后根据这几条中断通道进入NVIC分配中断优先级。另外还有23条通向其他外设,也就是事件响应。
可以看到其中 EXTI9_5 和 EXTI15_10;这是外部中断的5~9会触发同一个中断函数,外部中断10~15会触发同一个中断函数。
如果对NVIC如何分配优先级知识不清楚可以了解 STM32-中断-NVIC优先级的分配。
三、SYSCFG 系统配置控制器
SYSCFG(system configuration) 主要用于 地址重映射、选择以太网PHY接口、管理GPIO外部中断线连接。这次用到的是管理GPIO中断线。(而对于STM32F1xxx 则是AFIO来管理)

这些梯形就是数据选择器,可以有多个输入,条件控制,一个输出。通过SYSCFG_EXTICR1~4寄存器来配置选择。图中可以看出,相同的Pin脚不能同时被选择。例如 EXTI0 通道,只能选择Pin值为0的端口,只能选择PA0,PB0,...PI0中的一个。例如GPIOA_Pin_0和GPIOB_Pin_0,同为0的端口只能选一个。
四、EXTI 内部框图

由图可知主要有两条线路功能,右边是 23 条输入线。
先看第一条路线,首先23条检测线进入 边沿检测电路,可以通过上升沿触发选择寄存器/下降沿触发选择寄存器来选择是哪种方式触发。或者选择软件中断事件寄存器选择软件触发,然后会和中断屏蔽寄存器一起经过一个与门,这个与门相当于是起到了一个开关的作用。在中断屏蔽寄存器的相应位写 ‘1’,就能打开开关,使能中断请求;反之写 ‘0’,则取消中断请求。 挂起请求寄存器会在边沿检测电路选择触发的信号沿的时候就会置 1 。然后再至 NVIC 分配处理优先级。
第二条路线与第一条路线前半部分相同,在选择何种触发方式后,会经过一个由事件屏蔽寄存器控制的与门。事件屏蔽寄存器写 ‘1’,则能通过脉冲发生器,产生一个脉冲信号至事件脉冲,选择中断触发事件,中断产生后通向其他外设。
而这些寄存器可以通过AMBA APB总线,外设接口来直接访问。
五、编写 外部中断 初始化函数
1.首先我们要知道 如图1 所示,顺序就是先配置GPIO外设,再通过SYSCFG选择引脚,然后通过EXTI选择何种触发,最后至 配置 NVIC。
注意:要用外设就要先打开这个外设时钟。所以需要打开 GPIO 和 SYSCFG 的外设时钟。EXTI 和 NVIC(内核外设) 则不需要打开外设时钟。
void EXTI_Init(void)
{
//1.先开启GPIO外设时钟,在AHB1总线上
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA); //开启了GPIOA外设
//2.开启SYSCFG外设时钟,此外设在APB2总线上
RCC_APB2PeriphClockCmd(RCC_APB2Periph_SYSCFG, ENABLE);
//3.所有需要的外设时钟已经打开,然后开始初始化。
//先初始化GPIOA,用结构体初始化
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_2;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN;//普通输入模式
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;//100M
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;//上拉
GPIO_Init(GPIOA, &GPIO_InitStructure);//初始化GPIOA,2
//SYSCFG选择连接通道 PA2连到中断线2
SYSCFG_EXTILineConfig(EXTI_PortSourceGPIOA, EXTI_PinSource2)
//配置 EXTI 初始化
EXTI_InitTypeDef EXTI_InitStructure;
EXTI_InitStructure.EXTI_Line = EXTI_Line2; //LINE2
EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt; //中断事件
EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Rising; //上升沿触发
EXTI_InitStructure.EXTI_LineCmd = ENABLE; //使能LINE2
EXTI_Init(&EXTI_InitStructure);
//选择 NVIC 优先级分组
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);//设置系统中断优先级分组2
//NVIC初始化
NVIC_InitTypeDef NVIC_InitStructure;
NVIC_InitStructure.NVIC_IRQChannel = EXTI2_IRQn;//外部中断2
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0x03;//抢占优先级3
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0x02;//子优先级2
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;//使能外部中断通道
NVIC_Init(&NVIC_InitStructure);
}
//外部中断2服务程序
void EXTI2_IRQHandler(void)
{
if(EXTI_GetITStatus(EXTI_Line2) == SET) //判断是否进入中断
{
//..中断程序
EXTI_ClearITPendingBit(EXTI_Line2);//清除LINE2上的中断标志位
}
}
还有要注意的点。中断程序尽量简单,运行时间短,这样可以不阻塞主程序的运行。
而NVIC优先级分组一般就只选择一次,可以把 NVIC_PriorityGroupConfig(),移到main函数里,后续换优先级分组也在main函数中换。