实践内容:以STM32F407开发板为例,将摁下按键Key_0和Key_1作为中断源来控制LED灯的亮与灭。
由原理图可知:控制按键Key_0和Key_1的GPIO端口是GPIOE,引脚是PE4和PE3。
LED原理图见文章:http://t.csdnimg.cn/owJuB
在主程序的运行过程中,当有特定的事件发⽣时,CPU暂停正在执行的程序,保留现场后⾃动地
转去执行该事件,执行完成后⼜返回原来被暂停的位置继续运行。
事件的发出者--------中断源
此次事件本身-------中断
当有多个中断源同时申请中断时,CPU会根据中断源的轻重缓急进⾏裁决,优先响应更加紧
急的中断源。由此也就出现了中断的嵌套。
当⼀个中断程序正在运行时,又有新的更高优先级的中断源申请中断,CPU再次暂停当前中断
程序,转而去处理新的中断程序,处理完成后依次进行返回 。如图:
内部中断:由处理器或者程序内部产生的中断。这些中断通常是由于执行指令时产生的错误、特殊条件或程序中的其他异常引起的。
例子:除零错误、溢出错误等。
外部中断:由外部设备或硬件产生的中断。这些中断是来自处理器之外的信号,通常来自设备(如键盘、鼠标、定时器、IO 端口等)。我们平常说的中断是指外部中断。
例子:按下键盘的某个键、鼠标移动、定时器到时等。
中断响应:某个事件发⽣时,触发了中断,打断了正在执行的CPU,让CPU转而处理此事件。
事件响应:某个事件发生时,触发了特定的信号,此信号不经过CPU,而是触发了其他外设模块。
核心区分:有无经过CPU
作用:中断机制允许计算机实时响应外部设备的事件,如键盘输入、鼠标移动、定时器到时等。
意义:这确保了计算机可以与外部环境进行交互,使用户能够在需要时与计算机进行实时通信。
作用:通过允许处理器在必要时暂停当前执行的任务,转而执行中断服务程序,中断提高了系统的效率。
意义:在等待某些事件发生时,处理器可以执行其他任务,从而充分利用计算资源,提高系统整体的效率。
作用:中断用于处理各种异常和错误,例如除零错误、溢出错误等。
意义:通过中断机制,计算机可以在出现错误或异常时进行适当的处理,防止系统崩溃或出现不可预测的行为。
STM32F407具有82个可屏蔽中断通道(就是中断源),10个系统中断。这些中断统一由NVIIC来管理,每个中断通道都拥有16个可编程的优先等级,可对优先级进行分组。如图:
typedef struct
{
__IOM uint32_t ISER[8U]; /*!< Offset: 0x000 (R/W) Interrupt Set Enable Register */
uint32_t RESERVED0[24U];
__IOM uint32_t ICER[8U]; /*!< Offset: 0x080 (R/W) Interrupt Clear Enable Register */
uint32_t RESERVED1[24U];
__IOM uint32_t ISPR[8U]; /*!< Offset: 0x100 (R/W) Interrupt Set Pending Register */
uint32_t RESERVED2[24U];
__IOM uint32_t ICPR[8U]; /*!< Offset: 0x180 (R/W) Interrupt Clear Pending Register */
uint32_t RESERVED3[24U];
__IOM uint32_t IABR[8U]; /*!< Offset: 0x200 (R/W) Interrupt Active bit Register */
uint32_t RESERVED4[56U];
__IOM uint8_t IP[240U]; /*!< Offset: 0x300 (R/W) Interrupt Priority Register (8Bit wide) */
uint32_t RESERVED5[644U];
__OM uint32_t STIR; /*!< Offset: 0xE00 ( /W) Software Trigger Interrupt Register */
} NVIC_Type;
ISER[8](Interrupt Set Enable Register):中断使能寄存器,每个位对应一个中断号,该位设置为 1 表示允许相应的中断。
ICER[8](Interrupt Clear Enable Registers):中断除能寄存器,每个位对应一个中断号,该位设置为 1 表示禁用相应的中断。
ISPR[8](Interrupt Set Pending Registers):中断使能挂起控制寄存器,通过置 1,可以将
正在进行的中断挂起,而执行同级或更高级别的中断。写 0 是无效的。
ICPR[8](Interrupt Clear Pending Registers):中断解除挂起控制寄存器,该位设置为 1 表示清除相应中断的挂起状态。
IABR[8](Interrupt Active Bit Registers):是⼀个中断激活标志位寄存器组。对应位所代表
的中断和 ISER ⼀样,如果为 1,则表示该位所对应的中断正在被执行。这是⼀个只读寄存器,通
过它可以知道当前在执行的中断是哪⼀个。在中断执行完了由硬件自动清零。
IP [240](Interrupt Priority Registers):中断优先级控制寄存器组。由240个8bit的寄存器
组成,每个可屏蔽中断占⽤8bit。但我们只⽤IP[81] - IP[0]这82个。并且,每个中断通道占用的8bit并没有全部使用,而是只用了高4位(2^4=16,这也是有16个优先级的原因)。
由于每个中断通道占用的8bit并没有全部使用,而是只用了高4位,我们对这4位又进行了划分,分为⾼n位的抢占优先级和低4-n位的响应优先级。
组别 | 抢占优先级 | 响应优先级 |
分组0 | 0位,取值为0 | 4位,取值为0~15 |
分组1 | 1位,取值为0~1 | 3位,取值为0~7 |
分组2 | 2位,取值为0~3 | 2位,取值为0~3 |
分组3 | 3位,取值为0~7 | 1位,取值为0~1 |
分组4 | 4位,取值为0~15 | 0位,取值为0 |
抢占优先级:高抢占优先级可以打断正在执行的低抢占优先级。
响应优先级:当抢占优先级相同时,响应优先级高(数值小)先执行,但是不能互相打断。
抢占优先级和响应优先级相同的,按中断号(中断向量表)先后执行。
NVIC_IRQChannel:中断源/中断通道。
NVIC_IRQChannelPreemptionPriority:抢占优先级。
NVIC_IRQChannelSubPriority:响应优先级。
NVIC_IRQChannelCmd:使能中断,由于NVIC属于内核中的器件,默认时钟也是开启的,所以大部分情况下不需要单独设置使能中断。
EXTI可以监测指定GPIO口的电平信号,当其指定的GPIO口产生电平变化时,EXTI将立即向NVIC发出中断申请,经过NVIC裁决后即可中断CPU主程序,使CPU执行EXTI对应的中断程序。如图:
支持的触发方式:支持上升沿、下降沿、双边沿、软件触发。
支持的响应方式:事件响应 中断响应。
主要用于管理对可执行代码的存储区域的地址重映射、选择以太网 PHY 接口以及管理 GPIO 的外部中断线连接。STM32 系列的SYSCFG寄存器通常包含EXTICR寄存器用于配置外部中断线的引脚映射,可以通过引脚的高低电平变换来确定中断的来源。
我们将EXTI0 -EXTI15称为中断标志。
EXTI_Line:中断通道,根据中断源的引脚选择(EXTI_Line0~EXTI_Line15)。
EXTI_Mode:响应模式。
typedef enum
{
/*中断*/
EXTI_Mode_Interrupt = 0x00,
/*事件*/
EXTI_Mode_Event = 0x04
}EXTIMode_TypeDef;
EXTI_Trigger:触发方式。
typedef enum
{
/*上升沿*/
EXTI_Trigger_Rising = 0x08,
/*下降沿*/
EXTI_Trigger_Falling = 0x0C,
/*双边沿*/
EXTI_Trigger_Rising_Falling = 0x10
}EXTITrigger_TypeDef;
EXTI_LineCmd:中断使能,EXTI外设的时钟默认是开启的,所以大部分情况下不需要单独设置使能中断。
#include "stm32f4xx.h" // Device header
void EXTI3_IRQHandler(void)
{
if(EXTI_GetITStatus(EXTI_Line3)==SET)
{
GPIO_SetBits(GPIOF,GPIO_Pin_9);
GPIO_SetBits(GPIOF,GPIO_Pin_10);
/*这个是清除中断标志位的函数,否则CPU将陷在中断函数中出不来*/
EXTI_ClearITPendingBit(EXTI_Line3);
}
}
void EXTI4_IRQHandler(void)
{
if(EXTI_GetITStatus(EXTI_Line4)==SET)
{
GPIO_ResetBits(GPIOF,GPIO_Pin_10);
GPIO_ResetBits(GPIOF,GPIO_Pin_9);
EXTI_ClearITPendingBit(EXTI_Line4);
}
}
int main()
{
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOF,ENABLE);//点灯使能
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOE,ENABLE);//按键使能
RCC_APB2PeriphClockCmd(RCC_APB2Periph_SYSCFG,ENABLE);
//初始化LED灯
GPIO_InitTypeDef GPIO_InitTypeDefStruct;//点灯结构体
GPIO_InitTypeDefStruct.GPIO_Mode = GPIO_Mode_OUT;
GPIO_InitTypeDefStruct.GPIO_OType = GPIO_OType_PP;
GPIO_InitTypeDefStruct.GPIO_Pin = GPIO_Pin_10|GPIO_Pin_9|GPIO_Pin_10;
GPIO_InitTypeDefStruct.GPIO_Speed = GPIO_Fast_Speed;
GPIO_SetBits(GPIOF,GPIO_Pin_10);
GPIO_SetBits(GPIOF,GPIO_Pin_9);//灭
GPIO_Init(GPIOF,&GPIO_InitTypeDefStruct);
//初始化按键
GPIO_InitTypeDef GPIO_Struct2;//按键结构体1
GPIO_Struct2.GPIO_Mode = GPIO_Mode_IN;
GPIO_Struct2.GPIO_Pin = GPIO_Pin_4|GPIO_Pin_3;//按键
GPIO_Struct2.GPIO_Speed = GPIO_Speed_100MHz;
GPIO_Struct2.GPIO_PuPd = GPIO_PuPd_UP;
GPIO_Init(GPIOE,&GPIO_Struct2);
//配置SYSCFG
SYSCFG_EXTILineConfig(EXTI_PortSourceGPIOE,EXTI_PinSource3);
SYSCFG_EXTILineConfig(EXTI_PortSourceGPIOE,EXTI_PinSource4);
//配置EXTI
EXTI_InitTypeDef EXTI_InitStructure1;//EXTI1 结构体
EXTI_InitStructure1.EXTI_Line=EXTI_Line3;
EXTI_InitStructure1.EXTI_Mode=EXTI_Mode_Interrupt;
EXTI_InitStructure1.EXTI_Trigger=EXTI_Trigger_Falling;
EXTI_InitStructure1.EXTI_LineCmd=ENABLE;
EXTI_Init(&EXTI_InitStructure1);
EXTI_InitTypeDef EXTI_InitStructure2;//EXTI2 结构体
EXTI_InitStructure2.EXTI_Line=EXTI_Line4;
EXTI_InitStructure2.EXTI_Mode=EXTI_Mode_Interrupt;
EXTI_InitStructure2.EXTI_Trigger=EXTI_Trigger_Falling;
EXTI_InitStructure2.EXTI_LineCmd=ENABLE;
EXTI_Init(&EXTI_InitStructure2);
//配置NVIC
NVIC_PriorityGroupConfig( NVIC_PriorityGroup_2);
NVIC_InitTypeDef NVIC_InitStructure1;//NVIC1 结构体
NVIC_InitStructure1.NVIC_IRQChannel=EXTI3_IRQn;
NVIC_InitStructure1.NVIC_IRQChannelPreemptionPriority=2;
NVIC_InitStructure1.NVIC_IRQChannelSubPriority=1;
NVIC_Init(&NVIC_InitStructure1);
NVIC_PriorityGroupConfig( NVIC_PriorityGroup_2);
NVIC_InitTypeDef NVIC_InitStructure2;//NVIC2 结构体
NVIC_InitStructure2.NVIC_IRQChannel=EXTI4_IRQn;
NVIC_InitStructure2.NVIC_IRQChannelPreemptionPriority=1;
NVIC_InitStructure2.NVIC_IRQChannelSubPriority=1;
NVIC_Init(&NVIC_InitStructure2);
while(1)
{
EXTI3_IRQHandler();
EXTI4_IRQHandler();
}
}
运行结果:按下key0,LED灯亮,按下key1,LED灯灭。