使用 IO 口外部中断的一般步骤:
1)初始化 IO 口为输入。
2)开启 IO 口复用时钟,设置 IO 口与中断线的映射关系。
3)初始化线上中断,设置触发条件等。
4)配置中断分组(NVIC),并使能中断。
5)编写中断服务函数。
基础知识略过,
第一步.配置 GPIO 与中断线的映射关系的函数
1 void SYSCFG_EXTILineConfig(uint8_t EXTI_PortSourceGPIOx, uint8_t EXTI_PinSourcex)
配置实例:
1 void SYSCFG_EXTILineConfig(uint8_t EXTI_PortSourceGPIOI, uint8_t EXTI_PinSource8)
将中断线 8 与 GPIOI 映射起来,那么很显然是 GPIOI.8 与 EXTI8 中断线连接了。设置好中断线映射之后,那么到底来自这个IO口的中断时通过什么方式出发的呢?接下来我们就要设置该中断线上中断的初始化参数了。
第二步.中断线上中断的初始化是通过函数 EXTI_Init()实现的。EXTI_Init()函数的定义是:
1 void EXTI_Init ( EXTI_InitTypeDef * EXTI_InitStruct )
下面我们用一个使用范例来说明这个函数的使用:
1 EXTI_InitTypeDef EXTI_InitStructure; 2 EXTI_InitStructure.EXTI_Line=EXTI_Line8; 3 EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt; 4 EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Falling; 5 EXTI_InitStructure.EXTI_LineCmd = ENABLE; 6 EXTI_Init(&EXTI_InitStructure); //根据 EXTI_InitStruct 中指定的 7 //参数初始化外设 EXTI 寄存器
STM32 的外设的初始化都是通过结构体来设
置初始值的,这里就不罗嗦结构体初始化的过程了。我们来看看结构体 EXTI_InitTypeDef 的成员变量:
1 typedef struct 2 { 3 uint32_t EXTI_Line; /*!< Specifies the EXTI lines to be enabled or disabled. 4 This parameter can be any combination value of @ref EXTI_Lines */ 5 6 EXTIMode_TypeDef EXTI_Mode; /*!< Specifies the mode for the EXTI lines. 7 This parameter can be a value of @ref EXTIMode_TypeDef */ 8 9 EXTITrigger_TypeDef EXTI_Trigger; /*!< Specifies the trigger signal active edge for the EXTI lines. 10 This parameter can be a value of @ref EXTITrigger_TypeDef */ 11 12 FunctionalState EXTI_LineCmd; /*!< Specifies the new state of the selected EXTI lines. 13 This parameter can be set either to ENABLE or DISABLE */ 14 }EXTI_InitTypeDef;
简单介绍一下:第一个EXTI_Line,中断线;第二个EXTI_Mode:可选值为中断 EXTI_Mode_Interrupt 和事件 EXTI_Mode_Event;第三个参数是触发方式,可以是下降沿触发 EXTI_Trigger_Falling,上升沿触发 EXTI_Trigger_Rising,或者任意电平(上升沿和下降沿)触发EXTI_Trigger_Rising_Falling
我们设置好中断线和 GPIO 映射关系,然后又设置好了中断的触发模式等初始化参数。 既然是外部中断,涉及到中断我们当然还要设置 NVIC 中断优先级。
第三步,配置中断优先级组 和 NVIC 结构体初始化
中断优先级组:
1 void NVIC_PriorityGroupConfig(uint32_t NVIC_PriorityGroup);
1 /** 2 * @brief Configures the priority grouping: pre-emption priority and subpriority. 3 * @param NVIC_PriorityGroup: specifies the priority grouping bits length. 4 * This parameter can be one of the following values: 5 * @arg NVIC_PriorityGroup_0: 0 bits for pre-emption priority 6 * 4 bits for subpriority 7 * @arg NVIC_PriorityGroup_1: 1 bits for pre-emption priority 8 * 3 bits for subpriority 9 * @arg NVIC_PriorityGroup_2: 2 bits for pre-emption priority 10 * 2 bits for subpriority 11 * @arg NVIC_PriorityGroup_3: 3 bits for pre-emption priority 12 * 1 bits for subpriority 13 * @arg NVIC_PriorityGroup_4: 4 bits for pre-emption priority 14 * 0 bits for subpriority 15 * @note When the NVIC_PriorityGroup_0 is selected, IRQ pre-emption is no more possible. 16 * The pending IRQ priority will be managed only by the subpriority. 17 * @retval None 18 */
NVIC结构体:
1 void NVIC_Init(NVIC_InitTypeDef* NVIC_InitStruct)
1 typedef struct 2 { 3 uint8_t NVIC_IRQChannel; /*!< Specifies the IRQ channel to be enabled or disabled. 4 This parameter can be an enumerator of @ref IRQn_Type 5 enumeration (For the complete STM32 Devices IRQ Channels 6 list, please refer to stm32f4xx.h file) */ 7 8 uint8_t NVIC_IRQChannelPreemptionPriority; /*!< Specifies the pre-emption priority for the IRQ channel 9 specified in NVIC_IRQChannel. This parameter can be a value 10 between 0 and 15 as described in the table @ref MISC_NVIC_Priority_Table 11 A lower priority value indicates a higher priority */ 12 13 uint8_t NVIC_IRQChannelSubPriority; /*!< Specifies the subpriority level for the IRQ channel specified 14 in NVIC_IRQChannel. This parameter can be a value 15 between 0 and 15 as described in the table @ref MISC_NVIC_Priority_Table 16 A lower priority value indicates a higher priority */ 17 18 FunctionalState NVIC_IRQChannelCmd; /*!< Specifies whether the IRQ channel defined in NVIC_IRQChannel 19 will be enabled or disabled. 20 This parameter can be set either to ENABLE or DISABLE */ 21 } NVIC_InitTypeDef;
再来个配置实例:
1 NVIC_InitTypeDef NVIC_InitStructure; 2 NVIC_PriorityGroupConfig(NVIC_PriorityGroup_1); 3 NVIC_InitStructure.NVIC_IRQChannel = EXTI9_5_IRQn; 4 NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0; 5 NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0; 6 NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; 7 NVIC_Init(&NVIC_InitStructure);
第四步.中断服务ISR 我们配置完中断优先级之后,接着我们要做的就是编写中断服务函数。中断服务函数的名
字是在 MDK 中事先有定义的。这里需要说明一下,STM32 的 IO 口外部中断函数只有 6 个,
分别为:
1 EXPORT EXTI0_IRQHandler 2 EXPORT EXTI1_IRQHandler 3 EXPORT EXTI2_IRQHandler 4 EXPORT EXTI3_IRQHandler 5 EXPORT EXTI4_IRQHandler 6 EXPORT EXTI9_5_IRQHandler 7 EXPORT EXTI15_10_IRQHandler
中断线 0-4 每个中断线对应一个中断函数,中断线 5-9 共用中断函数 EXTI9_5_IRQHandler,中断线 10-15 共用中断函数 EXTI15_10_IRQHandler。 在编写中断服务函数的时候会经常使用到两个函数,第一个函数是判断某个中断线上的中断是否发生(标志位是否置位) :
1 ITStatus EXTI_GetITStatus ( uint32_t EXTI_Line )
这个函数一般使用在中断服务函数的开头判断中断是否发生。另一个函数是清除某个中断线上的中断标志位:
1 void EXTI_ClearITPendingBit ( uint32_t EXTI_Line )
这个函数一般应用在中断服务函数结束之前,清除中断标志位。常用的中断服务函数格式为:
void EXTI2_IRQHandler(void) { if(EXTI_GetITStatus(EXTI_Line8)!=RESET)//判断某个线上的中断是否发生 { 中断逻辑… EXTI_ClearITPendingBit(EXTI_Line8); //清除 LINE 上的中断标志位 } }
在这里需要说明一下,固件库还提供了两个函数用来判断外部中断状态以及清除外部状态标志位的函数 EXTI_GetFlagStatus 和 EXTI_ClearFlag,他们的作用和前面两个函数的作用类似。只是EXTI_GetITStatus 函数中会先判断这种中断是否使能,使能了才去判断中断标志位,而EXTI_GetFlagStatus 直接用来判断状态标志位。