STM32----外部中断
1、引入
按键电灯的实现:
while(1)
{
if(key == 0)
{
//灯亮
}
else
{
//灯灭
}
}
...
这种方式称之为轮询
轮询的缺陷:
1、浪费CPU
2、占用总线(Bus is always busy)
3、轮询响应时间差,事件得不到及时响应
有没有什么办法可以不让CPU主动去询问是否有事件发生,而是让事件发生之后主动的通知CPU->中断
2、中断的概念
一般中断定义为打断CPU指令执行顺序的事件
现代CPU架构为了能及时相应外部中断和内部紧急事件,都会支持中断,并且会提供相应的中断相应机制–>中断机制
3、ARM-Cortex-M4中断机制
当满足某个条件导致特定的事件发生后,中断控制器(NVIC)通过CPU产生中断事件,此时CPU就会停止当前正在做的事,去处理这个中断事件(去执行中断对应的中断服务函数),当不同的事件发生中断时,CPU就会去执行不同的中断函数。
CPU怎么根据不同的中断事件去做处理?
1、中断编号,M4为了区分不同的中断给每一个中断一个编号,用来区分不同的事件
2、中断向量表,就是一个保存不同的中断处理函数的地址的数组,本质上就是一个函数指针数组
所以中断编号其实就是做中断向量表的下标来使用。
所以当一个事件发生,就会产生中断,CPU就会拿到对应的中断的中断编号,根据此编号去中断向量表中找对应的元素,该元素就是一个函数指针,保存了中断服务函数的地址,CPU就可以跳转到该地址上去,去执行中断服务函数
中断服务函数的格式一般为:
void xxx(void)
{
}
//中断函数无参,无返回值,不可以在中断中做复杂的计算
为了能够让CPU更快的响应中断,M4约定将中断相量表存放在一个固定的位置,也就是存储器地址为0x0800 0000出(通过映射的方式将这处空间映射到0x0000 0000)。
4、STM32F4xx的中断管理机制
任何的中断产生到CPU的相应都要经过三个阶段
1)中断源阶段
中断源是指产生中断的设备。
该设备要能够产生中断必须要有一条中断请求线(IRQ Line),并且该请求线要接到中断控制器中断输入引脚上
2)终端控制器NVIC阶段
中断控制器是对所有的引脚进行管理和控制的设备
可以根据不同的中断输入引脚信号给CPU一个中断编号,告诉CPU某设备产生中断了
3)CPU响应阶段
CPU在接受到中断编号后,会将中断编号作为下标去中断向量表中查找对应的中断服务函数的地址,然后CPU会跳转到该函数的地址上面去执行该中断服务函数
一个设备产生了中断,首先要经过中断源这一阶段,然而中断源可以屏蔽该中断
即使外部设备产生了中断事件,中断源可以不向它的上一级NVIC发起中断请求
中断控制器接受到中断之后,也可以选择屏蔽或者使能中断
即NVIC接到请求后,不向CPU报告
STM32F4XX外部中断
外部中断是指GPIO外部电路上产生的中断
EXTI 支持 23 个外部中断/事件请求,这些都是信息输入端, 具体如下:
EXTI 线 0~15:对应外部 IO 口的输入中断
EXTI 线 16:连接到 PVD 输出
EXTI 线 17:连接到 RTC 闹钟事件
EXTI 线 18:连接到 USB 唤醒事件
EXTI 线 19:连接到以太网唤醒事件
EXTI 线 20:连接到 USB OTG HS(在 FS 中配置)唤醒事件
EXTI 线 21:连接到 RTC 入侵和时间戳事件
EXTI 线 22:连接到 RTC 唤醒事件
外部中断0(EXT0)的产生来源于所有编号为0的GPIO引脚
PA0/PB0/…/PI0
…
7、外部中断编程流程
7.1、配置GPIO
7.1.1、配置GPIO分组时钟
RCC_AHB1PeriphClockCmd
7.1.2、初始化GPIO为输入模式
GPIO_Init();
7.2、配置SYSCFG选择器
7.2.1、使能SYSCFG的时钟(SYSCFG选择器处于APB2总线上)
RCC_APB2PeriphClockCmd
7.2.2、初始化SYSCFG(选择外部中断的输入信号来源)
void SYSCFG_EXTILineConfig(uint8_t EXTI_PortSourceGPIOx, uint8_t EXTI_PinSourcex)
@EXTI_PortSourceGPIOx 选择GPIO分组 EXTI_PortSourceGPIOA/EXTI_PortSourceGPIOB/...
@EXTI_PinSourcex 选择产生外部中断的引脚 EXTI_PinSource0/EXTI_PinSource1/...
7.3、配置外部中断控制器
void EXTI_Init(EXTI_InitTypeDef* EXTI_InitStruct)
@EXTI_InitStruct 指向外部中断初始化信息结构体
typedef struct
{
uint32_t EXTI_Line;
指定要初始化的外部中断线(可以位或上多个)
EXTI_Line0
EXTI_Line1
...
EXTIMode_TypeDef EXTI_Mode;
指定相应的外部中断模式(二选一)
EXTI_Mode_Interrupt(中断模式)
EXTI_Mode_event(事件模式)
EXTITrigger_TypeDef EXTI_Trigger;
指定外部中断的边沿触发模式(三选一)
EXTI_Trigger_Rising = 0x08,//在输入信号上升沿的时候触发外部中断
EXTI_Trigger_Falling = 0x0C,//在输入信号下降沿的时候触发外部中断
EXTI_Trigger_Rising_Falling = 0x10;//在输入信号上升沿和下降沿的时候都触发外部中断
FunctionalState EXTI_LineCmd;
使能/禁止相应的外部中断
ENABLE
DISABLE
}EXTI_InitTypeDef;
7.4、配置NVIC中断控制器
void NVIC_Init(NVIC_InitTypeDef* NVIC_InitStruct)
@NVIC_InitStruct 执行NVIC的初始化信息结构体
typedef struct
{
unit8_t NVIC_IRQChannel;
指定中断编号
固件库为了用户方便使用库函数,给每一个中断编号用枚举取了别名
外部中断0--->EXTI0_IRQn
串口中断1--->USART1_IRQn
...
unit8_t NVIC_IRQChannelPreemptionPriority;
指定抢占优先级
unit8_t NVIC_IRQChannelSubPriority;
指定子优先级
中断的优先级占4bit,优先级取值范围为【0~15】,数字越小优先级越高
0是最高优先级,15是最低优先级
而优先级由两部分组成
1、抢占优先级:当中断A产生并且CPU响应中断A,此时又来一个外部中断B,且此中断的抢占优先级
高于A,此时CPU就会暂停处理A中断,转而先去处理B中断
2、子优先级:当两个或两个以上的中断同时发生的时候,中断的子优先级最高的先被CPU响应
抢占优先级和子优先级分别占多少个bit通过如下函数配置:
void NVIC_PriorityGroupConfig(unit32_t NVIC_PriorityGroup)
@NVIC_PriorityGroup 指定优先级分组
NVIC_PriorityGroup_0 抢占优先级占0bit子优先级占4bit
NVIC_PriorityGroup_1 抢占优先级占1bit子优先级占3bit
NVIC_PriorityGroup_2 抢占优先级占2bit子优先级占2bit
NVIC_PriorityGroup_3 抢占优先级占3bit子优先级占1bit
NVIC_PriorityGroup_4 抢占优先级占4bit子优先级占0bit
注意:1、在利用NVIC_Init去指定优先级之前,要先用NVIC_PriorityGroupConfig去对优先级
进行分组(指定各占多少个bit)
2、中断优先级的分组一般只进行一次
3、没有设置中断优先级分组的情况下,去配置中断优先级是无意义的
更加简单的方法:
void NVIC_SetPriority(IRQn_Type IRQn,unit32_t priotrity)
@IRQn 指定要设置优先级的中断的中断编号
@priotrity 指定优先级【0~15】
uint32_t NVIC_GetPriority(IRQn_Type IRQn)
获取一个中断的优先级
FunctionalState NVIC_IRQChannelCmd;
使能/禁止相应的中断
ENABLE/DISABLE
}NVIC_InitTypeDef;
当配置好的中断产生,CPU就会自动取执行此中断对应的中断服务函数了
7.5、中断服务函数
外部中断0的中断服务函数
void EXTI0_IRQHandler(void)
{
//获取中断标志位的函数有两个
ITStatus EXTI_GetITStatus(unit32_t EXTI_Line)//在中断服务函数
FlagStatus EXTI_GetFlagStatus(unit32_t EXTI_Line)//在普通函数
@EXTI_Line 指定外部中断线
EXTI_Line0/EXTI_Line1/.../EXTI_Line15
if()//获取中断标志位,判断是什么事情引起的中断
{
//做中断处理
//清空标志位
void EXTI_ClearFlag(uint32_t EXTI_Line)
void EXTI_ClearITPendingBit(uint32_t EXTI_Line)
}
}