中断概述
CPU执行程序时,由于发生了某种随机的事件(外部或内部),引起CPU暂时中断正在运行的程序,转去执行一段特殊的服务程序(中断服务子程序或中断处理程序),以处理该事件,该事件处理完后又返回被中断的程序继续执行,这一过程称为中断。
EXTI
EXTI:外部中断/事件控制器
外部中断/事件控制器包含多达 23 个用于产生事件/中断请求的边沿检测器。每根输入线都可单独进行配置,以选择类型(中断或事件)和相应的触发事件(上升沿触发、下降沿触发或边沿触发)。每根输入线还可单独屏蔽。挂起寄存器用于保持中断请求的状态线。
外部中断控制框图:
外部中断
STM32F4的每个IO都可以作为外部中断输入
STM32F4的中断控制器支持23个外部中断/事件请求:
- 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唤醒事件。
触发机制
下降沿:数字威廉希尔官方网站
中,数字电平从高电平(数字“1”)变为低电平(数字“0”)的那一瞬间(时刻)叫作下降沿。
上升沿:数字威廉希尔官方网站
中,数字电平从低电平(数字“0”)变为高电平(数字“1”)的那一瞬间(时刻)叫作上升沿。
外部中断~中断线的映射
GPIOx.0映射到EXTI0
GPIOx.1映射到EXTI1
…
GPIOx.15映射到EXTI15
注意:
- 一条中断线的在同一时间只能被一个IO口映射。
- 外部中断线5~9分配一个中断向量,共用一个服务函数,外部中断线10~15分配一个中断向量,共用一个中断服务函数。
中断服务函数列表:
EXTI0_IRQHandler
EXTI1_IRQHandler
EXTI2_IRQHandler
EXTI3_IRQHandler
EXTI4_IRQHandler
EXTI9_5_IRQHandler
EXTI15_10_IRQHandler
NVIC
NVIC:嵌套向量中断控制器 (NVIC)
NVIC 特性
嵌套向量中断控制器 NVIC 包含以下特性:
- STM32F405xx/07xx 和 STM32F415xx/17xx 具有 82 个可屏蔽中断通道, STM32F42xxx
- 和 STM32F43xxx 具有多达 86 个可屏蔽中断通道(不包括 Cortex™-M4F 的 16 根中断线)
- 16 个可编程优先级(使用了 4 位中断优先级)
- 低延迟异常和中断处理
- 电源管理控制
- 系统控制寄存器的实现
嵌套向量中断控制器 (NVIC) 和处理器内核接口紧密配合,可以实现低延迟的中断处理和晚到中断的高效处理。
注意:
NVIC有两个重要功能:通道的设置及优先级设置
中断优先级:数字越小,优先级别越高。
中断管理
首先,对STM32中断进行分组,组0~4。同时,对每个中断设置一个抢占优先级和一个响应优先级值。分组配置是在寄存器SCB->AIRCR中配置(注意:数字越小,优先级越高):
- 第0组:所有4位用于指定响应优先级
- 第1组:最高1位用于指定抢占式优先级,最低3位用于指定响应优先级
- 第2组:最高2位用于指定抢占式优先级,最低2位用于指定响应优先级
- 第3组:最高3位用于指定抢占式优先级,最低1位用于指定响应优先级
- 第4组:所有4位用于指定抢占式优先级
抢占优先级 & 响应优先级区别:
高优先级的抢占优先级是可以打断正在进行的低抢占优先级中断的。
抢占优先级相同的中断,高响应优先级不可以打断低响应优先级的中断。
抢占优先级相同的中断,当两个中断同时发生的情况下,哪个响应优先级高,哪个先执行。
如果两个中断的抢占优先级和响应优先级都是一样的话,则看哪个中断先发生就先执行;
NVIC中断优先级分组
中断优先级分组函数
void NVIC_PriorityGroupConfig(uint32_t NVIC_PriorityGroup);//例子:设置系统的抢占优先级分组为组2NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); 特别说明:
一般情况下,系统代码执行过程中,只设置一次中断优先级分组(只执行一次设置),比如分组2,设置好分组之后一般不会再改变分组。随意改变分组会导致中断管理混乱,程序出现意想不到的执行结果。
按键中断实现
中断实现要添加库文件:stm32f4xx_exti.c和stm32f4xx_syscfg.c
实现步骤
1、NVIC分组(一个工程当中只能配置一次分组)
void NVIC_PriorityGroupConfig(uint32_t NVIC_PriorityGroup); 2、使能SYSCFG时钟
void RCC_APB2PeriphClockCmd(RCC_APB2Periph_SYSCFG, ENABLE); 3、初始化IO口为输入
void GPIO_Init(GPIO_TypeDef* GPIOx, GPIO_InitTypeDef* GPIO_InitStruct) 4、 设置IO口与中断线的映射关系
void SYSCFG_EXTILineConfig(uint8_t EXTI_PortSourceGPIOx, uint8_t EXTI_PinSourcex) 5、初始化线上中断,设置触发条件等
void EXTI_Init(EXTI_InitTypeDef* EXTI_InitStruct) 6、 配置中断分组(NVIC),并使能中断
void NVIC_Init(NVIC_InitTypeDef* NVIC_InitStruct) 7、编写中断服务函数。(中断服务函数在startup_stm32f40_41xxx.S当中)
void EXTIx_IRQHandler();
按键中断实现例程
exti.c
#include "exti.h"
//粗延时
void delayms(int n)
{
int i, j;
for(i=0; i
for(j=0; j<40000; j++);
}
//PA0引脚按键KEY1中断初始化
void Exti_PA0_KEY1_Init(void)
{
// GPIO_InitTypeDef GPIO_InitStruct;
EXTI_InitTypeDef EXTI_InitStruct;
NVIC_InitTypeDef NVIC_InitStruct;
//使能GPIO A组时钟,
// RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA, ENABLE);
// GPIO_InitStruct.GPIO_Pin = GPIO_Pin_0; //引脚0
// GPIO_InitStruct.GPIO_Mode = GPIO_Mode_IN; //输入模式
// GPIO_InitStruct.GPIO_PuPd = GPIO_PuPd_UP; //上拉
// GPIO_Init(GPIOA, &GPIO_InitStruct);
//使能SYSCFG时钟:
RCC_APB2PeriphClockCmd(RCC_APB2Periph_SYSCFG, ENABLE);
//设置IO口与中断线的映射关系。确定什么引脚对应哪个中断线 PA0 -- EXTI0 (可变)
SYSCFG_EXTILineConfig(EXTI_PortSourceGPIOA,EXTI_PinSource0);
EXTI_InitStruct.EXTI_Line = EXTI_Line0; //中断线0 (可变)
EXTI_InitStruct.EXTI_Mode = EXTI_Mode_Interrupt; //中断模式
EXTI_InitStruct.EXTI_Trigger= EXTI_Trigger_Falling; //下降沿触发
EXTI_InitStruct.EXTI_LineCmd= ENABLE; //中断线使能
//初始化线上中断,设置触发条件等。
EXTI_Init(&EXTI_InitStruct);
NVIC_InitStruct.NVIC_IRQChannel = EXTI0_IRQn; //NVIC通道,在stm32f4xx.h可查看通道 (可变)
NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority = 0x01; //抢占优先级
NVIC_InitStruct.NVIC_IRQChannelSubPriority = 0x01; //响应优先级
NVIC_InitStruct.NVIC_IRQChannelCmd = ENABLE; //使能
//配置中断分组(NVIC),并使能中断。
NVIC_Init(&NVIC_InitStruct);
}
//编写中断服务函数。这个函数不需要程序员在主函数调用,满足条件CPU自行调用的函数
void EXTI0_IRQHandler(void)
{
//判断中断标志是否为1
if(EXTI_GetITStatus(EXTI_Line0) == SET)
{
delayms(15); //延时消抖
//判断是否按下
if(GPIO_ReadInputDataBit(GPIOA, GPIO_Pin_0) == 0)
{
//变更灯状态
GPIO_ToggleBits(GPIOF, GPIO_Pin_9);
}
}
//清空标志位
EXTI_ClearITPendingBit(EXTI_Line0);
}
main.c
#include "stm32f4xx.h"
#include "led.h"
#include "key.h"
#include "exti.h"
int main(void)
{
//NVIC分组(一个工程当中只能配置一次分组)抢占优先级2位,值范围:0~3;响应优先级2位,值范围:0~3;
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
//LED灯初始化
Led_Init();
//按键初始化
Key_Init();
//中断
Exti_PA0_KEY1_Init();
while(1)
{
GPIO_ToggleBits(GPIOE, GPIO_Pin_14);
delayms(1000);
}
return 0;
}
中断概述
CPU执行程序时,由于发生了某种随机的事件(外部或内部),引起CPU暂时中断正在运行的程序,转去执行一段特殊的服务程序(中断服务子程序或中断处理程序),以处理该事件,该事件处理完后又返回被中断的程序继续执行,这一过程称为中断。
EXTI
EXTI:外部中断/事件控制器
外部中断/事件控制器包含多达 23 个用于产生事件/中断请求的边沿检测器。每根输入线都可单独进行配置,以选择类型(中断或事件)和相应的触发事件(上升沿触发、下降沿触发或边沿触发)。每根输入线还可单独屏蔽。挂起寄存器用于保持中断请求的状态线。
外部中断控制框图:
外部中断
STM32F4的每个IO都可以作为外部中断输入
STM32F4的中断控制器支持23个外部中断/事件请求:
- 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唤醒事件。
触发机制
下降沿:数字威廉希尔官方网站
中,数字电平从高电平(数字“1”)变为低电平(数字“0”)的那一瞬间(时刻)叫作下降沿。
上升沿:数字威廉希尔官方网站
中,数字电平从低电平(数字“0”)变为高电平(数字“1”)的那一瞬间(时刻)叫作上升沿。
外部中断~中断线的映射
GPIOx.0映射到EXTI0
GPIOx.1映射到EXTI1
…
GPIOx.15映射到EXTI15
注意:
- 一条中断线的在同一时间只能被一个IO口映射。
- 外部中断线5~9分配一个中断向量,共用一个服务函数,外部中断线10~15分配一个中断向量,共用一个中断服务函数。
中断服务函数列表:
EXTI0_IRQHandler
EXTI1_IRQHandler
EXTI2_IRQHandler
EXTI3_IRQHandler
EXTI4_IRQHandler
EXTI9_5_IRQHandler
EXTI15_10_IRQHandler
NVIC
NVIC:嵌套向量中断控制器 (NVIC)
NVIC 特性
嵌套向量中断控制器 NVIC 包含以下特性:
- STM32F405xx/07xx 和 STM32F415xx/17xx 具有 82 个可屏蔽中断通道, STM32F42xxx
- 和 STM32F43xxx 具有多达 86 个可屏蔽中断通道(不包括 Cortex™-M4F 的 16 根中断线)
- 16 个可编程优先级(使用了 4 位中断优先级)
- 低延迟异常和中断处理
- 电源管理控制
- 系统控制寄存器的实现
嵌套向量中断控制器 (NVIC) 和处理器内核接口紧密配合,可以实现低延迟的中断处理和晚到中断的高效处理。
注意:
NVIC有两个重要功能:通道的设置及优先级设置
中断优先级:数字越小,优先级别越高。
中断管理
首先,对STM32中断进行分组,组0~4。同时,对每个中断设置一个抢占优先级和一个响应优先级值。分组配置是在寄存器SCB->AIRCR中配置(注意:数字越小,优先级越高):
- 第0组:所有4位用于指定响应优先级
- 第1组:最高1位用于指定抢占式优先级,最低3位用于指定响应优先级
- 第2组:最高2位用于指定抢占式优先级,最低2位用于指定响应优先级
- 第3组:最高3位用于指定抢占式优先级,最低1位用于指定响应优先级
- 第4组:所有4位用于指定抢占式优先级
抢占优先级 & 响应优先级区别:
高优先级的抢占优先级是可以打断正在进行的低抢占优先级中断的。
抢占优先级相同的中断,高响应优先级不可以打断低响应优先级的中断。
抢占优先级相同的中断,当两个中断同时发生的情况下,哪个响应优先级高,哪个先执行。
如果两个中断的抢占优先级和响应优先级都是一样的话,则看哪个中断先发生就先执行;
NVIC中断优先级分组
中断优先级分组函数
void NVIC_PriorityGroupConfig(uint32_t NVIC_PriorityGroup);//例子:设置系统的抢占优先级分组为组2NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); 特别说明:
一般情况下,系统代码执行过程中,只设置一次中断优先级分组(只执行一次设置),比如分组2,设置好分组之后一般不会再改变分组。随意改变分组会导致中断管理混乱,程序出现意想不到的执行结果。
按键中断实现
中断实现要添加库文件:stm32f4xx_exti.c和stm32f4xx_syscfg.c
实现步骤
1、NVIC分组(一个工程当中只能配置一次分组)
void NVIC_PriorityGroupConfig(uint32_t NVIC_PriorityGroup); 2、使能SYSCFG时钟
void RCC_APB2PeriphClockCmd(RCC_APB2Periph_SYSCFG, ENABLE); 3、初始化IO口为输入
void GPIO_Init(GPIO_TypeDef* GPIOx, GPIO_InitTypeDef* GPIO_InitStruct) 4、 设置IO口与中断线的映射关系
void SYSCFG_EXTILineConfig(uint8_t EXTI_PortSourceGPIOx, uint8_t EXTI_PinSourcex) 5、初始化线上中断,设置触发条件等
void EXTI_Init(EXTI_InitTypeDef* EXTI_InitStruct) 6、 配置中断分组(NVIC),并使能中断
void NVIC_Init(NVIC_InitTypeDef* NVIC_InitStruct) 7、编写中断服务函数。(中断服务函数在startup_stm32f40_41xxx.S当中)
void EXTIx_IRQHandler();
按键中断实现例程
exti.c
#include "exti.h"
//粗延时
void delayms(int n)
{
int i, j;
for(i=0; i
for(j=0; j<40000; j++);
}
//PA0引脚按键KEY1中断初始化
void Exti_PA0_KEY1_Init(void)
{
// GPIO_InitTypeDef GPIO_InitStruct;
EXTI_InitTypeDef EXTI_InitStruct;
NVIC_InitTypeDef NVIC_InitStruct;
//使能GPIO A组时钟,
// RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA, ENABLE);
// GPIO_InitStruct.GPIO_Pin = GPIO_Pin_0; //引脚0
// GPIO_InitStruct.GPIO_Mode = GPIO_Mode_IN; //输入模式
// GPIO_InitStruct.GPIO_PuPd = GPIO_PuPd_UP; //上拉
// GPIO_Init(GPIOA, &GPIO_InitStruct);
//使能SYSCFG时钟:
RCC_APB2PeriphClockCmd(RCC_APB2Periph_SYSCFG, ENABLE);
//设置IO口与中断线的映射关系。确定什么引脚对应哪个中断线 PA0 -- EXTI0 (可变)
SYSCFG_EXTILineConfig(EXTI_PortSourceGPIOA,EXTI_PinSource0);
EXTI_InitStruct.EXTI_Line = EXTI_Line0; //中断线0 (可变)
EXTI_InitStruct.EXTI_Mode = EXTI_Mode_Interrupt; //中断模式
EXTI_InitStruct.EXTI_Trigger= EXTI_Trigger_Falling; //下降沿触发
EXTI_InitStruct.EXTI_LineCmd= ENABLE; //中断线使能
//初始化线上中断,设置触发条件等。
EXTI_Init(&EXTI_InitStruct);
NVIC_InitStruct.NVIC_IRQChannel = EXTI0_IRQn; //NVIC通道,在stm32f4xx.h可查看通道 (可变)
NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority = 0x01; //抢占优先级
NVIC_InitStruct.NVIC_IRQChannelSubPriority = 0x01; //响应优先级
NVIC_InitStruct.NVIC_IRQChannelCmd = ENABLE; //使能
//配置中断分组(NVIC),并使能中断。
NVIC_Init(&NVIC_InitStruct);
}
//编写中断服务函数。这个函数不需要程序员在主函数调用,满足条件CPU自行调用的函数
void EXTI0_IRQHandler(void)
{
//判断中断标志是否为1
if(EXTI_GetITStatus(EXTI_Line0) == SET)
{
delayms(15); //延时消抖
//判断是否按下
if(GPIO_ReadInputDataBit(GPIOA, GPIO_Pin_0) == 0)
{
//变更灯状态
GPIO_ToggleBits(GPIOF, GPIO_Pin_9);
}
}
//清空标志位
EXTI_ClearITPendingBit(EXTI_Line0);
}
main.c
#include "stm32f4xx.h"
#include "led.h"
#include "key.h"
#include "exti.h"
int main(void)
{
//NVIC分组(一个工程当中只能配置一次分组)抢占优先级2位,值范围:0~3;响应优先级2位,值范围:0~3;
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
//LED灯初始化
Led_Init();
//按键初始化
Key_Init();
//中断
Exti_PA0_KEY1_Init();
while(1)
{
GPIO_ToggleBits(GPIOE, GPIO_Pin_14);
delayms(1000);
}
return 0;
}
举报