外部中断概述
STM32的每个IO都可以作为外部中断输入
STM32F1的中断控制器支持19个外部中断/事件请求:
线0 ~ 15:对应外部IO口的输入中断。
线16:连接到PVD输出。
线17:连接到RTC闹钟事件。
线18:连接到USB唤醒事件。
每个外部中断线可以独立的配置触发方式(上升沿,下降沿或者双边沿触发(上升沿和下降沿都可)),触发/屏蔽,专用的状态位。
从上面可以看出,STM32供IO使用的中断线只有16个,但是STM32F10x系列的IO口多达上百个,STM32F103ZET6(112),STM32F 103RCT6(51),那么中断线怎么跟IO口对应呢?
GPIO 的管脚 GPIOx.0~GPIOx.15(x=A,B,C,D,E,F,G)分别对应中断线 0~15。这样每个中断线对应了最多 7 个 IO 口,以线 0 为例:它对应了 GPIOA.0、GPIOB.0、GPIOC.0、GPIOD.0、 GPIOE.0、GPIOF.0、GPIOG.0。而中断线每次只能连接到 1 个 IO 口上,这样就需要通过配置来决定对应的中断线配置到哪个 GPIO 上了。
是不是16个中断线就可以分配16个中断服务函数呢?
不是的,IO口外部中断在中断向量表中只分配了7个中断向量,也就是只能使用7个中断服务函数
在STM32中文参考手册9.1.2 中断和异常向量这一章,可以查看到STM32F10XXX产品的中断向量表
其中EXTI的中断向量在表中可以找到,从表中可以看出,外部中断线5 ~ 9分配一个中断向量,共用一个服务函数,外部中断线10~15分配一个中断向量,共用一个中断服务函数。
中断服务函数列表
EXTIO_IRQHandler
EXTI1_IRQHandler
EXTI2_IRQHandler
EXTI3_IRQHandler
EXTI4_IRQHandler
EXTI9_5_IRQHandler
EXTI15_10_IRQHandler
外部中断常用库函数
void GPIO_EXTILineConfig(uint8_t GPIO_PortSource, uint8_t GPIO_PinSource);
exp:GPIO_EXTILineConfig(GPIO_PortSourceGPIOE,GPIO_PinSource2);
void EXTI_Init(EXTI_InitTypeDef* EXTI_InitStruct);
ITStatus EXTI_GetITStatus(uint32_t EXTI_Line);
void EXTL_ClearITPendingBit(uint32_t EXTI_Line);
EXTI_Init函数解析
void EXTI_Init(EXTI_InitTypeDef* EXTI_InitStruct);
结构体类型
typedef struct
{
uint32_t EXTI_Line;
EXTIMode_TypeDef EXTl_Mode;
EXTITrigger_TypeDefEXTI_Trigger;
FunctionalState EXTI_LineCmd;
}EXTI_InitTypeDef;
初始化示例
EXTI_InitStructure.EXTI_Line = EXTI_Line2;
EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt;
EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Falling;
EXTI_InitStructure.EXTI_LineCmd = ENABLE;
EXTI_Init(&EXTI_InitStructure);
外部中断的一般配置步骤
1.初始化IO口为输入。
? GPIO_Init();
2.开启IO口复用时钟。
? RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO,ENABLE);
3.设置IO口与中断线的映射关系。
? void GPIO_EXTILineConfig(); ——stm32f10x_gpio.h
4.初始化线上中断,设置触发条件等。
? EXTl_Init(); ——stm32f10x_exti.h
5.配置中断分组(NVIC),并使能中断。
? NVIC_Init();
6.编写中断服务函数。
? EXTIx_IRQHandler();
7.清除中断标志位
? EXTI_ClearlTPendingBit(); ——在中断服务函数中软件清除标志位
代码示例
外部中断初始化
void EXTIx_Init(void)
{
EXTI_InitTypeDef EXTI_InitStructure;
NVIC_InitTypeDef NVIC_InitStructure;
KEY_Init();
RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO,ENABLE);
GPIO_EXTILineConfig(GPIO_PortSourceGPIOA,GPIO_PinSource8);
EXTI_InitStructure.EXTI_Line = EXTI_Line8;
EXTI_InitStructure.EXTI_LineCmd = ENABLE;
EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt;
EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Falling;
EXTI_Init(&EXTI_InitStructure);
NVIC_InitStructure.NVIC_IRQChannel = EXTI9_5_IRQn;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 2;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 2;
NVIC_Init(&NVIC_InitStructure);
}
外部中断服务函数
一定要记得最后清除中断标志位,不然外部中断触发后回不到main函数中继续执行
void EXTI9_5_IRQHandler(void)
{
delay_ms(10);
if(KEY == 0)
{
GPIO_ResetBits(GPIOA,GPIO_Pin_1);
}
EXTI_ClearITPendingBit(EXTI_Line8);
}
主函数
在主函数中通过串口不断发送 “外部中断实验…\r\n” 提示信息,当按键被按下,外部中断触发后,KEY_FLAG被置位,翻转LED灯状态,清零标志位,并在串口发送外部中断触发信息
#include "LED.h"
#include "delay.h"
#include "KEY.h"
#include "EXTI.h"
#include "USART1.h"
u8 KEY_FLAG = 0;
int main()
{
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
LED_Init();
delay_init();
KEY_Init();
EXTIx_Init();
USART1_Init(9600);
while(1)
{
if(KEY_FLAG == 1)
{
GPIOA->ODR ^= 0x01 << 1;
KEY_FLAG = 0;
printf("按键按下,外部中断触发\r\n");
}
printf("外部中断实验...\r\n");
delay_ms(1000);
}
}
|