什么是中断和异常:
首先要了解什么是内核,什么是外设。内核就如Cortex_M4,是ARM公司开发的。ST公司在拿到这个内核后,做了一些东西,如GPIO和RTC以及USART等等等等,都是外设。
那么异常是对所有能打断程序正常执行的统称,中断属于一种异常。在内核中,异常就叫异常,而在外设中,异常被称为中断。(下面统称中断);
中断的概念与组成:
现在我们把单片机看做是一个正在做饭的少妇。他正在做饭(去除食物,放入锅里,煎炒...),就在这时,他的孩子发出了一声啼哭,并且这时她发现家里的盐所剩不多了,他不得不放下手头的铲子去哄孩子睡觉,并记住她做饭做到哪里了。等她哄完孩子后,再去超市买盐回来,又继续做饭。
如果把这个少妇看作是一个单片机,
1:那么她做饭的过程我们称为程序正常的运行流程
2:孩子的啼哭和她发现盐不够了被称为中断触发。
3:《哄孩子》,《买盐》被称为中断向量
4:她准备处理孩子的啼哭和盐不够这两个事件,被称为中断响应
5:她先哄孩子,再去买盐就是处理多个中断的优先级。
6:当她哄孩子时,她必须记得接下来去买盐。那么买盐这个事情就称为中断悬空
总结一下:程序运行流程,中断触发,中断响应,中断向量,中断优先级,中断悬空
中断的管理:
在单片机中,对于中断的管理是由NVIC实现的,也就是一个中断管理器。它负责管理如下事务
1:中断的使能(我是否对这个事件进行中断响应,也就是我是否将这个中断送入内核处理)
2:中断优先级选择(我该先选择处理那个中断?)
3:中断源的选择(中断向量是什么)
通过对这三点进行配置,即可对一个中断向量进行管理
NVIC优先级:
在Cortex内核中,由八个位决定中断优先级,但是在Cortex低版本如Cortex_M4中,只使用了4个位。共0-15个优先级。因为低版本内核如Cortex_M4根本用不了这么多的优先级。但是还有一个问题出现了,当一个中断服务函数正在执行的时候,又来了个中断,那么内核是先把当前中断服务函数执行完了呢,还是先去执行新来的中断呢?
这时就提出了中断分组的概念。及将现有的表示优先级的4位化为两个部分,高位部分用于表示抢占优先级(主优先级),低位部分用于表示响应优先级(子优先级)。高位部分和地位部分的长度由优先级组号表示。优先级组号在寄存器钟用了三个位表示。8组,但是实际上在低版本Cortex中,只用了0-5个组。这个优先级组号决定了抢占优先级在优先级位数中所占的长度,如3组,就是抢占优先级占高三位,响应优先级剩下1位。如果是组0,那么不使用抢占优先级,只使用响应优先级。
在实际运行过程中,先响应抢占优先级低的(注意,这里是数值越低,优先级越大),如果抢占优先级相同,则处理响应优先级低的,如果都相同,则处理中断向量号低的(实际上,中断向量存储在一个中断数组中,中断向量号就是它的数组下标。)。
当一个中断服务函数A正在运行时,出现一个抢占优先级高(值低)于于A的中断B,则先去处理中断B,处理完成后,再处理A没完成的部分。如果A正在运行,那么出现的B的抢占优先级相同或者小A,则先处理完A,再去响应B。
EXTI外部中断以及事件控制器
提到NVIC用于管理中断,中断的形式有很多种,有定时器中断和外部中断等等。那么用于管理外部中断的东西就叫做EXTI,它和NVIC的关系可以看作是上下级的关系,EXTI检测到GPIO等外设发生的变化来决定是否触发中断或事件。
我们如何来定义NVIC和EXTI的关系?
EXTI统筹外部中断,并将这些中断发送给NVIC处理。
来看一组GPIO中断发生的流程
GPIO口的电平发生变化,EXTI检测到了这个事件,由EXTI向NVIC发送一个中断请求。
这就是外部中断发生的流程,外部的事件本质并不会向NVIC产生中断信号,而是由EXTI检测到这个事件从而向NVIC发送中断请求。
祭出官方框图
这里我分为了几个部分,
先解决7这个,EXTI是挂在APB2上的,APB2的时钟就是PLCK2。EXTI所谓片上外设,无非就是一些挂在总线上的控制寄存器与一些实现电路。EXTI分为1-23给中断线.图上的23就是表示其为23号中断线,中断线的作用就是EXTI连接总线时钟和NVIC以及边上外设的。
1:首先输入线检测到跳变电平。
2:边沿检测电路检测这是个啥电平,通过上面两个寄存器决定上升沿有效还是下降沿有效,也可以都设置,上升下降都触发。
3:是个门电路,软件中断和硬件中断都可通过
然后就到了事件和中断的分界点了,先看中断
4:又是个门电路,中断信号来了并没有进行中断屏蔽中断信号才能继续
5:进入中断悬空,之后交由NVIC处理,所谓EXTI挂起就是把寄存器的某个位设1,标记该中断线处于中断中,执行完中断服务函数后需要软件复位。
6:产生事件,就是产生个电平啥的。
EXTI中断线:
EXTI中断线共23条,从0-22号。前16条连接GPIOPin,无论GPIO_ABCDEF..其对应的GPIOPin号就等于中断线号。后几条连了一些外设啥的,现在不用管
来自野火手册
EXTI中断向量有如下几条
中断线0-4分别有各自的中断向量,5-9用一条,10-15用一条,其他的先不管。
以下是配置部分
NVIC配置
//配置NVIC
typedef struct {
uint8_t NVIC_IRQChannel; // 中断源
uint8_t NVIC_IRQChannelPreemptionPriority; // 抢占优先级
uint8_t NVIC_IRQChannelSubPriority; // 子优先级
FunctionalState NVIC_IRQChannelCmd; // 中断使能或者失能
} NVIC_InitTypeDef;
NVIC_Init(&NVIC_InitTypeDef);
//设置NVIC优先级组
void NVIC_PriorityGroupConfig(uint32_t NVIC_PriorityGroup);
EXTI配置
第一步,开启SYSCFG时钟(这玩意就是EXTI的时钟,也有一些其他作用,挂在APB2上)
RCC_APB2PeriphClockCmd(RCC_APB2Periph_SYSCFG,ENABLE);//使能EXTI时钟
第二步,设置中断线与外设之间的映射,(简单理解为把中断线和GPIO连上)
void SYSCFG_EXTILineConfig(uint8_t EXTI_PortSourceGPIOx, uint8_t EXTI_PinSourcex);//输入的参数从stm32f4xx_syscfg.h的58行开始
第三步,设置EXTI与写中断服务函数
typedef struct
{
uint32_t EXTI_Line;
EXTIMode_TypeDef EXTI_Mode;
EXTITrigger_TypeDef EXTI_Trigger;
FunctionalState EXTI_LineCmd;
}EXTI_InitTypeDef;
void EXTI_Init(EXTI_InitTypeDef* EXTI_InitStruct);//这个函数用于配置EXTI
FlagStatus EXTI_GetFlagStatus(uint32_t EXTI_Line);//该函数的返回值为是否为参数EXTI_Line产生了中断,也就是检测中断线是否标记为挂起状态
void EXTI_ClearITPendingBit(uint32_t EXTI_Line);//清除中断线挂起标志
创建中断服务函数两个注意
//首先中断服务函数名在.s文件的80行左右
//其次中断向量在stm32f4xx.h的172行,如果你用的是f407的话
如果配置了EXTI但是没有中断服务函数的话,发生中断会卡死
EXTI中断服务函数实例
#define KeyUp_Hander EXTI0_IRQHandler
void KeyUp_Hander(void)
{
delay_ms(300);
if (EXTI_GetITStatus(KeyUp_Line) != RESET)
{
LED_0 = !LED_0;
}
EXTI_ClearITPendingBit(KeyUp_Line);
}
各位观众老爷打个赏,不多,一瓶护发素即可