一.基本概念
STM32F4的每个IO都可以作为外部中断输入。
STM32F4的中断控制器支持22个外部中断/事件请求:
EXTI线0~15:对应外部IO口的输入中断。
EXTI线16:连接到PVD输出。
EXTI线17:连接到RTC闹钟事件。
EXTI线18:连接到USB OTG FS唤醒事件。
EXTI线19:连接到以太网唤醒事件。
EXTI线20:连接到USB OTG HS(在FS中配置)唤醒事件。
EXTI线21:连接到RTC入侵和时间戳事件。
EXTI线22:连接到RTC唤醒事件。 每个外部中断线可以独立的配置触发方式(上升沿,下降沿或者双边沿触发),触发/屏蔽,专用的状态位。
2.中断线与IO口的对应关系
中断线只有16根,因此
将PIOx.0映射到EXTI0
GPIOx.1映射到EXTI1
.......
GPIOx.15映射到EXTI15
每一根中断线接收多个端口的映射,就解决了中断线不够用的情况
3.对于每个中断线,可以设置相应的触发方式(上升沿触发,下降沿触发,边沿触发)以及使能。
IO口外部中断在中断向量表中只分配了7个中断向量,也就是只能使用7个中断服务函数
从表中可以看出,外部中断线5~9分配一个中断向量,共用一个服务函数外部中断线10~15分配一个中断向量,共用一个中断服务函数。
4.中断服务函数列表:
EXTI0_IRQHandler
EXTI1_IRQHandler
EXTI2_IRQHandler
EXTI3_IRQHandler
EXTI4_IRQHandler
EXTI9_5_IRQHandler
EXTI15_10_IRQHandler
外部中断常用库函数:
①void SYSCFG_EXTILineConfig(uint8_t EXTI_PortSourceGPIOx, uint8_t EXTI_PinSourcex); //设置IO口与中断线的映射关系 exp: SYSCFG_EXTILineConfig(EXTI_PortSourceGPIOE, EXTI_PinSource2); //将PE2映射到2号中断线
②void EXTI_Init(EXTI_InitTypeDef* EXTI_InitStruct); //初始化中断线:触发方式等
③ITStatus EXTI_GetITStatus(uint32_t EXTI_Line); //判断中断线中断状态,是否发生
④void EXTI_ClearITPendingBit(uint32_t EXTI_Line); //清除中断线上的中断标志位
⑤RCC_APB2PeriphClockCmd(RCC_APB2Periph_SYSCFG, ENABLE);//使能SYSCFG时钟 //这个函数非常重要,在使用外部中断的时候一定要先使能SYSCFG时钟
二、中断编写
1.中断配置一般过程
①使能SYSCFG时钟: RCC_APB2PeriphClockCmd(RCC_APB2Periph_SYSCFG, ENABLE);
② 初始化IO口为输入。 GPIO_Init();
③设置IO口与中断线的映射关系。 void SYSCFG_EXTILineConfig();
④初始化线上中断,设置触发条件等。 EXTI_Init();
⑤配置中断分组(NVIC),并使能中断。 NVIC_Init();
⑥编写中断服务函数。 EXTIx_IRQHandler();
⑦清除中断标志位 EXTI_ClearITPendingBit();
2.硬件连接
3.
中断服务函数:delay_ms(10)用于判断按键按下若超过10ms,就说明是真的按下了,也就是消除抖动的作用。
void EXTIx_IRQHandler(void)
{
delay_ms(10);
if(WK_UP==1)
{
BEEP=!BEEP;
}
EXTI_ClearITPendingBit(EXTI_Linex);
}
4.初始化线上中断,映射到2号中断线,设置模式为中断,下降沿触发,初始使能。
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);
5.配置中断分组(NVIC),并使能中断。设置中断通道,抢占优先级,子优先级,使能外部中断通道。
NVIC_InitStructure.NVIC_IRQChannel = EXTI0_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0x00;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0x02;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
6.完整exti代码:
其中
key0对应蜂鸣器响停,抢占优先级:0,响应优先级:2,中断线0,中断处理函数EXTI0_IRQn
key1对应LED0翻转,抢占优先级:3,响应优先级:2,中断线2,中断处理函数EXTI2_IRQn
key2对应LED1翻转,抢占优先级:2,响应优先级:2,中断线3,中断处理函数EXTI3_IRQn
key3对应LED0,LED1同时翻转,抢占优先级:1,响应优先级:2,中断线4,中断处理函数EXTI4_IRQn
void EXTI0_IRQHandler(void)
{
delay_ms(10);
if(WK_UP==1)
{
BEEP=!BEEP;
}
EXTI_ClearITPendingBit(EXTI_Line0);
}
void EXTI2_IRQHandler(void)
{
delay_ms(10);
if(KEY2==0)
{
LED0=!LED0;
}
EXTI_ClearITPendingBit(EXTI_Line2);
void EXTI3_IRQHandler(void)
{
delay_ms(10);
if(KEY1==0)
{
LED1=!LED1;
}
EXTI_ClearITPendingBit(EXTI_Line3);
}
void EXTI4_IRQHandler(void)
{
delay_ms(10);
if(KEY0==0)
{
LED0=!LED0;
LED1=!LED1;
}
EXTI_ClearITPendingBit(EXTI_Line4);
}
void EXTIX_Init(void)
{
NVIC_InitTypeDef NVIC_InitStructure;
EXTI_InitTypeDef EXTI_InitStructure;
KEY_Init();
RCC_APB2PeriphClockCmd(RCC_APB2Periph_SYSCFG, ENABLE);
SYSCFG_EXTILineConfig(EXTI_PortSourceGPIOE, EXTI_PinSource2);
SYSCFG_EXTILineConfig(EXTI_PortSourceGPIOE, EXTI_PinSource3);
SYSCFG_EXTILineConfig(EXTI_PortSourceGPIOE, EXTI_PinSource4);
SYSCFG_EXTILineConfig(EXTI_PortSourceGPIOA, EXTI_PinSource0);
EXTI_InitStructure.EXTI_Line = EXTI_Line0;
EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt;
EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Rising;
EXTI_InitStructure.EXTI_LineCmd = ENABLE;
EXTI_Init(&EXTI_InitStructure);
EXTI_InitStructure.EXTI_Line = EXTI_Line2 | EXTI_Line3 | EXTI_Line4;
EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt;
EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Falling;
EXTI_InitStructure.EXTI_LineCmd = ENABLE;
EXTI_Init(&EXTI_InitStructure);
NVIC_InitStructure.NVIC_IRQChannel = EXTI0_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0x00;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0x02;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
NVIC_InitStructure.NVIC_IRQChannel = EXTI2_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0x03;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0x02;//×ÓÓÅÏȼ¶2
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
NVIC_InitStructure.NVIC_IRQChannel = EXTI3_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0x02;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0x02;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
NVIC_InitStructure.NVIC_IRQChannel = EXTI4_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0x01;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0x02;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
}
7.总结
在进行STM32F407外部中断按键实验时,通过key0绑定蜂鸣器,key1绑定led0(亮灭),key2绑定led1(亮灭),key3绑定led0和led1(同时反转),样例给定时延为10ms,在进行按键检测时,发现并未体现函数中的抢占优先级,思考过后可能是由于时延太短,无法在10ms内看出其中的差别,因此我首先将时延设置为1000ms,给予充分的观察时间,设立优先级:
首先保持子优先级(响应优先级)相同,全部设为2,对于抢占优先级:
0级:key0(蜂鸣器)
1级:key1(LED0)
2级:key2(LED1)
3级:key3(LED0和LED1)
在进行试验时,按下key0与key1,在按下的时候有意的略微早一点按下key1,然后再按下key0,按照优先级抢占原则,key1的优先级为1,key0的优先级为0,虽然先按下key1,先进入key1的中断处理,但是由于key0的抢占优先级高于key1,于是将会先响应key0的中断,再响应key1的中断,最后实验发现确实如此,蜂鸣器先响,然后led0才亮,此后尝试了key2,key3均符合该原理。
另设优先级:
key0,key1抢占优先级设为0,子优先级(响应优先级)设为1,3
key2,key3抢占优先级设为1,子优先级(响应优先级)设为2,2
实验发现:
抢占优先级等级高于子优先级(响应优先级),子优先级低的抢占优先级高的会先响应,在抢占优先级相同的情况下,子优先级(响应优先级)高的将会先响应,两个优先级均相同的情况下,先来的会先响应。