转自“STM32F4外部中断入口和外部中断线 - Justice_Gao的日志 单片机论坛
http://www.51hei.com/bbs/blog-218435-6926.html”
并根据自身体会修改
目录
简介
使用库函数配置外部中断的步骤。
1) 使能IO口时钟,初始化IO口为输入
2) 调用库函数 NVIC_PriorityGroupConfig() 设置优先级分组
3) 开启SYSCFG时钟
4) 设置IO口与中断线的映射关系。
5) 初始化线上中断,设置触发条件等。
6) 配置中断分组(NVIC),并使能中断。
7) 编写中断服务函数。
STM32F4的每个IO都可以作为外部中断的中断输入口,这点也是STM32F4的强大之处。STM32F407的中断控制器支持22个外部中断/事件请求。每个中断设有状态位,每个中断/事件都有独立的触发和屏蔽设置。STM32F407的22个外部中断为:
EXTI线0~15:对应外部IO口的输入中断。
EXTI线16:连接到VD输出。
EXTI线17:连接到RTC闹钟事件。
EXTI线18:连接到USB OTG FS唤醒事件。
EXTI线19:连接到以太网唤醒事件。
EXTI线20:连接到USB OTG HS(在FS中配置)唤醒事件。
EXTI线21:连接到RTC入侵和时间戳事件。
EXTI线22:连接到RTC唤醒事件。
从上可知,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上了。GPIO与中断线的映射关系如下图所示:
要使用IO口作为中断输入,要使能相应的IO口时钟,以及初始化相应的IO口为输入模式。
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA, ENABLE);
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_8;
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;
GPIO_Init(GPIOA, &GPIO_InitStructure);
设定了优先级分组才能在后续步骤正确设置中断优先级
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
配置GPIO与中断线的映射关系,首先要打开SYSCFG时钟。
RCC_APB2PeriphClockCmd(RCC_APB2Periph_SYSCFG, ENABLE);
只要使用到外部中断,就必须打开SYSCFG时钟。
配置GPIO与中断线的映射关系:
SYSCFG_EXTILineConfig(EXTI_PortSourceGPIOA, EXTI_PinSource8);
将中断线8与GPIOA映射,GPIOA.8与EXTI8中断线连接了。
EXTI_InitTypeDef EXTI_InitStructure;
EXTI_InitStructure.EXTI_Line = EXTI_Line8;
EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt;
EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Falling;
EXTI_InitStructure.EXTI_LineCmd = ENABLE;
EXTI_Init(&EXTI_InitStructure);
设置中断线8上的中断为下降沿触发。EXTI_InitTypeDef结构的说明
typedef struct
{ uint32_t EXTI_Line;
EXTIMode_TypeDef EXTI_Mode;
EXTITrigger_TypeDef EXTI_Trigger;
FunctionalState EXTI_LineCmd;
}EXTI_InitTypeDef;
4个成员:
1、中断线标号,外部中断,取值范围为EXTI_Line0~EXTI_Line15。
2、中断模式,可选值为中断EXTI_Mode_Interrupt和事件EXTI_Mode_Event。
3、触发方式,可以是下降沿触发EXTI_Trigger_Falling,上升沿触发EXTI_Trigger_Rising,或者任意电平(上升沿和下降沿)触发EXTI_Trigger_Rising_Falling。
4、使能中断线了。
NVIC_InitTypeDef NVIC_InitStructure;
NVIC_InitStructure.NVIC_IRQChannel = EXTI9_5_IRQn; // 开启映射到固定的中断函数
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0; //抢占优先级为0
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1; //响应优先级为1
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //使能
NVIC_Init(&NVIC_InitStructure);
EXTI9_5_IRQn 表示中断函数 EXTI9_5_IRQHandler
中断服务函数的名字是在MDK中事先有定义的。STM32F4的IO口外部中断函数只有7个,分别为:
EXPORT EXTI0_IRQHandler
EXPORT EXTI1_IRQHandler
EXPORT EXTI2_IRQHandler
EXPORT EXTI3_IRQHandler
EXPORT EXTI4_IRQHandler
EXPORT EXTI9_5_IRQHandler
EXPORT EXTI15_10_IRQHandler
中断线0-4每个中断线对应一个中断函数,中断线5-9共用中断函数EXTI9_5_IRQHandler,中断线10-15共用中断函数EXTI15_10_IRQHandler。
在编写中断服务函数的时候会经常使用到两个函数,一个函数是判断某个中断线上的中断是否发生(标志位是否置位):
ITStatus EXTI_GetITStatus(uint32_t EXTI_Line);
另一个函数是清除某个中断线上的中断标志位,一般应用在中断服务函数结束之前,清除中断标志位。
void EXTI_ClearITPendingBit(uint32_t EXTI_Line);
常用的中断服务函数格式为:
void EXTI3_IRQHandler(void)
{
if(EXTI_GetITStatus(EXTI_Line3)!=RESET)//判断某个线上的中断是否发生
{ …中断逻辑…
EXTI_ClearITPendingBit(EXTI_Line3); //清除LINE上的中断标志位
}
}
固件库还提供了两个函数用来判断外部中断状态以及清除外部状态标志位的函数EXTI_GetFlagStatus和EXTI_ClearFlag,他们的作用和前面两个函数的作用类似。只是在EXTI_GetITStatus函数中会先判断这种中断是否使能,使能了才去判断中断标志位,而EXTI_GetFlagStatus直接用来判断状态标志位。
使用IO口外部中断的一般步骤:
1) 使能IO口时钟,初始化IO口为输入
2) 调用库函数 NVIC_PriorityGroupConfig() 设置优先级分组
3) 开启SYSCFG时钟
4) 设置IO口与中断线的映射关系。
5) 初始化线上中断,设置触发条件等。
6) 配置中断分组(NVIC),并使能中断。
7) 编写中断服务函数。
通过以上几个步骤的设置,我们就可以正常使用外部中断了。