外部中断(EXTI)是一种硬件触发机制,它允许外部信号(例如按键、传感器等)引发微控制器的中断处理程序。使用外部中断,可以实现快速响应外部事件的功能。
在STM32F40x系列中,外部中断功能由EXTI模块提供。每个GPIO引脚都可以被配置为外部中断输入,并且可以根据需要选择触发方式和触发条件。EXTI模块可以检测到引脚上的信号边沿(上升沿、下降沿或两者)或电平(高电平、低电平),在探测到指定的边沿或电平时触发相应的中断。
EXTI控制器的主要特性如下:
1、每个中断/事件线上都具有独立的触发和屏蔽
2、每个中断线都具有专用的状态位
3、支持多达23个软件事件/中断请求
4、检测脉冲宽度低于APB2时钟宽度的外部信号。
外部中断/事件控制器包含多达22个用于产生事件/中断请求的边沿检测器。每根输入线都可单独进行配置,以选择类型(中断或事件)和相应的触发事件(上升沿触发、下降沿触发或边沿触发)。每根输入线还可单独屏蔽。挂起寄存器用于保持中断请求的状态线。
每个中断线都是相互独立的,具体请参见下面各图说明:
0~4是独立的中断服务函数
5~9共用中断服务函数
10~15共用中断服务函数
其中,每个中断设有状态位,每个中断/事件都有独立的触发和屏蔽设置。STM32F407
的22个外部中断为:
EXTI线0~15:对应外部IO口的输入中断。
EXTI线16:连接到PVD输出。EXTI线17:连接到RTC闹钟事件。
EXTI线18:连接到USBOTG FS唤醒事件。EXTI线19:连接到以太网唤醒事件。
EXTI线20:连接到USB OTG HS(在FS中配置)唤醒事件。EXTI线21:连接到RTC入侵和时间戳事件。
EXTI线22:连接到RTC唤醒事件。
外部中断/事件控制器框图
另外七根EXTI线连接方式如下:
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唤醒事件
要产生中断,必须先配置好并使能中断线。根据需要的边沿检测设置⒉个触发寄存器,同时在中断屏蔽寄存器的相应位写“1”使能中断请求。当外部中断线上出现选定信号沿时,便会产生中断请求,对应的挂起位也会置1。在挂起寄存器的对应位写“1”,将清除该中断请求。
要产生事件,必须先配置好并使能事件线。根据需要的边沿检测设置2个触发寄存器,同时在事件屏蔽寄存器的相应位写“1”允许事件请求。当事件线上出现选定信号沿时,便会产生事件脉冲,对应的挂起位不会置1。
通过在软件中对软件中断/事件寄存器写“1”,也可以产生中断/事件请求。
1、使能IO口时钟,初始化IO口为输入
2、使能SYSCFG时钟,设置IO口与中断线的映射关系
3、初始化线上中断,设置触发条件等
4、配置中断分组(NVIC),并使能中断
5、编写中断服务函数
第一个参数EXTI_Line:中断线的标号,对于我们的外部中断,取值范围为EXTI_Line0~EXTI_Line15;
第二个参数 EXTI_Mode:中断模式,可选值为中断EXTI_Mode_Interrupt和事件EXTI_Mode_Event;
第三个参数 EXTI_Trigger:触发方式,可以是下降沿触发EXTI_Trigger_Falling,上升沿触发EXTI_Trigger_Rising,或者任意电平(上升沿和下降沿)触发EXTI_Trigger_Rising_Falling;
第四个参数 EXTI_LineCmd:使能中断线。
ITStatus EXTI_GetITStatus(uint32_t EXTI_Line);该函数一般使用在中断服务函数的
开头判断中断是否发生;
(2)第二个函数是清除某个中断线上的中断标志位:
void EXTI_ClearITPendingBit(uint32_t EXTI_Line);该函数一般应用在中断服务函数结束之前,清除中断标志位。
#include "exti.h"
void EXTIX_Init(void){
NVIC_InitTypeDef NVIC_InitStructure;
EXTI_InitTypeDef EXTI_InitStructure;
KEY_Init(); //按键对应的IO口初始化
RCC_APB2PeriphClockCmd(RCC_APB2Periph_SYSCFG, ENABLE);//使能SYSCFG时钟
SYSCFG_EXTILineConfig(EXTI_PortSourceGPIOE, EXTI_PinSource2);//PE2连接线2
SYSCFG_EXTILineConfig(EXTI_PortSourceGPIOE, EXTI_PinSource3);//PE3连接线3
SYSCFG_EXTILineConfig(EXTI_PortSourceGPIOE, EXTI_PinSource4);//PE4连接线4
SYSCFG_EXTILineConfig(EXTI_PortSourceGPIOA, EXTI_PinSource0);//PA0连接线0
/*配置EXTI_Line0 */
EXTI_InitStructure.EXTI_Line = EXTI_Line0;//LINE0
EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt;//中断事件
EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Rising; //上升沿触发
EXTI_InitStructure.EXTI_LineCmd = ENABLE;//使能LINE0
EXTI_Init(&EXTI_InitStructure);
/*配置EXTI_Line2,3,4 */
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;//外部中断0
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0x00;//抢占优先级0
NVIC_InitStructure.NVIC_IRQChannelSubPriority =0x02;//响应优先级2
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;//使能外部中断通道
NVIC_Init(&NVIC_InitStructure);//配置NVIC
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);//配置NVIC
NVIC_InitStructure.NVIC_IRQChannel = EXTI3_IRQn;//外部中断3
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0x02;//抢占优先级2
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0x02;//响应优先级2
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;//使能外部中断通道
NVIC_Init(&NVIC_InitStructure);//配置NVIC
NVIC_InitStructure.NVIC_IRQChannel = EXTI4_IRQn;//外部中断4
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0x01;//抢占优先级1
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0x02;//响应优先级2
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;//使能外部中断通道
NVIC_Init(&NVIC_InitStructure);//配置NVIC
}
//外部中断0服务程序
void EXTI0_IRQHandler(void){
Delay_ms(10);//消抖
if(WK_UP==1){
BEEP=!BEEP; //蜂鸣器翻转
}
EXTI_ClearITPendingBit(EXTI_Line0); //清除LINE0上的中断标志位
}
//外部中断2服务程序
voidEXTI2_IRQHandler(void){
Delay_ms(10);//消抖
if(KEY2==0){
LED1 = !LED1;
}
EXTI_ClearITPendingBit(EXTI_Line2);//清除LINE2上的中断标志位
}
//外部中断3服务程序
void EXTI3_IRQHandler(void){
Delay_ms(10);//消抖
if(KEY1==0){
LED2 = !LED2;
}
EXTI_ClearITPendingBit(EXTI_Line3); //清除LINE3上的中断标志位
}
//外部中断4服务程序
void EXTI4_IRQHandler(void){
Delay_ms(10);//消抖
if(KEY0==0){
LED3 = !LED3;
}
EXTI_ClearITPendingBit(EXTI_Line4);//清除LINE4上的中断标志位
}
#ifndef __EXTI_H
#define __EXTI_H
#include "stm32f4xx.h"
#include "key.h"
#include "led.h"
#include "beep.h"
void EXTIX_Init(void);
#endif
#include "key.h"
#include "systick.h"
//按键初始化函数
void KEY_Init(void){
GPIO_InitTypeDef GPIO_InitStructure;
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA|RCC_AHB1Periph_GPIOE,ENABLE);//使能GPIOA,GPIOE时钟
GPIO_InitStructure.GPIO_Pin= GPIO_Pin_2|GPIO_Pin_3|GPIO_Pin_4;//KEY0 KEY1 KEY2对应引脚
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN;//普通输入模式
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;//100M
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;//上拉
GPIO_Init(GPIOE, &GPIO_InitStructure);//初始化GPIOE2,3,4
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;//WK_UP对应引脚PA0
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_DOWN ;//下拉
GPIO_Init(GPIOA, &GPIO_InitStructure);//初始化GPIOA0
}
//按键处理函数
//返回按键值//mode:0,不支持连续按;1,支持连续按;//0,没有任何按键按下
//1,KEY0按下2,KEY1按下3,KEY2按下4,WKUP按下WK_UP //注意此函数有响应优先级,KEY0>KEY1>KEY2>WK_UP!!
u8 KEY_Scan(u8 mode){
static u8 key_up=1;//按键按松开标志
if(mode)key_up=1; //支持连按
if(key_up&&(KEY0==0||KEY1==0||KEY2==0||WK_UP==1)){
Delay_ms(10);//去抖动
key_up=0;
if(KEY0==0)return 1;
else if(KEY1==0)return 2;
else if(KEY2==0)return 3;
else if(WK_UP==1)return 4;
}
else if(KEY0==1&&KEY1==1&&KEY2==1&&WK_UP==0)key_up=1;
return 0;//无按键按下
}
#ifndef __KEY_H
#define __KEY_H
#include "stm32f4xx.h"
#include "systick.h"
#include "io_bit.h"
#define WK_UP PAin(0)
#define KEY0 PEout(2)
#define KEY1 PEout(3)
#define KEY2 PEout(4)
void KEY_Init(void);
u8 KEY_Scan(u8);
#endif
#include "led.h"
#include "key.h"
#include "beep.h"
#include "usart1.h"
#include "exti.h"
#include "systick.h"
int main(void){
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);//设置系统中断优先级分组2
SysTick_Init(168); //初始化延时函数
usart1_init(115200);//串口初始化
LED_Init();//初始化LED端口
BEEP_Init();//初始化蜂鸣器端口
EXTIX_Init ();//初始化外部中断输入
LED1 = 1;//先点亮LED1灯
while(1){
printf("OK\r\n"); //打印OK提示程序运行
Delay_ms(1000); //每隔1s打印一次
}
下载代码后,在串口调试助手里面可以看到如下图所示信息:
从图可看出,程序已经在运行了,此时可以通过按下KEY0、KEY1、KEY2和KEY_UP来观察DS0、DS1以及蜂鸣器是否跟着按键的变化而变化。
EXTI是一种处理外部事件的机制,它允许外部设备触发中断来通知处理器处理新的事件。EXTI的工作原理是通过将外部设备的触发信号连接到处理器的中断引脚上,在外部设备触发时,产生一个中断请求信号。
处理器在接收到中断请求信号后,会立即暂停当前任务,保存当前执行状态,并跳转到中断处理程序中执行特定的指令。中断处理程序是事先编写好的处理器响应中断的程序,它会对触发中断的事件进行处理,并根据需要采取相应的措施。
EXTI的使用需要配置中断引脚和中断优先级,并编写相应的中断处理程序。在配置EXTI时,需要考虑外部事件的触发条件、中断优先级和中断响应方式等因素。
函数名 |
描述 |
EXTI_DeInit |
将外设EXTI寄存器重设为缺省值 |
EXTI_Init |
根据EXTI_InitStruct中指定的参数初始化外设EXTI寄存器 |
EXTI_StructInit |
把EXTI_InitStruct中的每一个参数按缺省值填入 |
EXTI_GenerateSWInterrupt |
产生一个软件中断 |
EXTI_GetFlagStatus |
检查指定的EXTI线路标志位设置与否 |
EXTI_ClearFlag |
清除EXTI线路挂起标志位 |
EXTI_GetITStatus |
检查指定的EXTI线路触发请求发生与否 |
EXTI_ClearITPendingBit |
清除EXTI线路挂起位 |
关于以上函数中的参数详细说明可参考STM32固件库使用手册的中文翻译版。
对于主函数中所涉及的LED灯、蜂鸣器函数均可参考按键配置函数的内容或者参考作者所写的3.STM32F40x 模块化内容及代码编写(以点亮LED灯模块为例)这一篇文章进行配置LED灯和蜂鸣器的函数代码,并且串口初始化函数也可参考4.STM32F40x 串口通信(文中以USART1为例子)的这一篇文章进行配置函数。这三个函数配置很简单,我就不重复写出来了。
注意:本人所写文章均用于记录自己的学习过程!!!!