1.引入
按键点灯的问题一般用轮询的方式去解决。
轮询天生有缺陷: 1.浪费CPU2.占用总线,Bus is always busy.3.轮询响应时差,响应不及时的。有没有办法不让CPU主动去询问事件是否发生,而是当事件发生 后,主动去通知CPU去处理。---> 中断机制
2.中断的概念
一般中断定义为打断CPU指令正常执行顺序的事件。
现代CPU架构为了能够及时的响应外部或者内部的一些紧急事件,都支持
中断,并且会提供相应的中断响应机制-->中断机制
3.ARM Cortex-M4中断机制
当NVIC(中断控制器)通过CPU产生了某个事件,此时,CPU就会停止正在做的事情,转而切换处理模式去处理这个事件(去执行中断服务函数)。
M4给不同的中断一个唯一的编号(中断编号:用来区分不同的中断事件)。
当不同的中断事件产生时,CPU会做不同的处理。
中断向量表:一个数组,保存不同的中断事件处理函数的地址。
实际上就是一个函数指针数组
中断编号:就是该数组的下标。
中断处理函数:void Handler(void)CPU被动调用,不是用户主动调用的。是当有紧急事件发生时,CPU才会
被动去执行中断函数。它可以在用户指令,的任何时刻去调用因为中断在
任何时刻都有可能发生。
4.STM32F4xx的中断管理机制
任何中断的产生到CPU的响应,都要经过以下阶段:
1)中断源阶段
中断源是指产生了中断的设备。
设备要能够产生中断,就必须要有一 根中断请求线(IRQ Line),并
且这根中断请求线必须要连接到中断控制器(NVIC)的中断输入引脚上。
2)NVIC中断控制器阶段
中断控制器是对所有中断输入引脚进行管理和控制。
可以根据输入的中断请求给CPU内核一个中断信号,通过CPU某某设备
产生了中断,外部硬件在通过INTR发送中断请求信号时,还要向CPU给出一
个8位的中断编号。CPU在响应这个中断请求的时候,同时读取到了这个由
外部硬件给出的中断编号,然后以这个中断编号为下标就会去对应的中断
向量表中找到对应的元素,将元素中的值(地址)取出来后,跳转过去执行
这个函数(中断处理函数)。
CPU响应中断汇编实现:
MRS R0,IPSR ;将中断编号给到R0
MOV R1,#0x00000000 ;中断向量表的基址
LDR R2,[R1,R0,LSL #2] ;R2 <-- R1 + (R0 << 2)
MOV PC,R2 ;跳转到中断服务函数中去执行
一个设备产生了中断首先要经过中断源这一级,而中断源可以屏蔽或者使能中断的,即使外部设备产生了中断,中断源也可以不向上一级中断控制器发送中断请求信号。
中断控制器(NVIC)它也可以控制中断,当NVIC收到中断源发起的中断请求信号之后,也可以选择ENABLE(使能)/DISABLE(禁止)这个中断,意思是NVIC收到请求后,但是不报告给CPU。
5.STM32F4xx外部中断
外部中断(EXTI:EXTernal Interrupt)是指GPIO的外部电路上产生的中断。
比如:在GPIO口的外部电路上产生了一个上升沿(或者下降沿)将可能会导致一个外部中断的产生。
F407一共有23个外部中断。记为:EXTI0/EXT01…EXTI22
EXTI0的产生来源于所有编号为0的GPIO引脚:PA0/PB0.....PI0
EXTI1的产生来源于所有编号为1的GPIO引脚:PA1/PB1.....PI1.....
EXTI15的产生来源于所有编号为15的GPIO引脚:PA15/PB15.....PI15
GPIO外部信号输入(高跳变/低跳变):
GPIO控制器(GPIO控制器应该要配置成输入模式SYSCFG选择器(选择由哪个GPIO产生EXTI)EXTI外部中断控制器(边沿触发选择/外内部中断/屏蔽和使能)NVIC中断控制器CPU停止正常执行顺序-->1.获取NVIC报告的中断编号-->2.去中断向量表中以此编号为下标去中断处理函数的地址-->3.根据此地址跳转过去执行中断处理函数(处理中断)
6.外部中断代码实现
KEY0-->PA0-->EXTI0-->NVIC->CPU->EXTI0_IRQHandlerKEY1-->PE2-->EXTI2-->NVIC->CPU->EXTI2_IRQHandlerKEY2-->PE3 ......KEY3-->PE4 ......
1)配置GPIO控制器
a. 使能GPIO分组时钟
void RCC_AHB1PeriphClockCmd(uint32_t RCC_AHB1Periph,FunctionalState NewState)
b.初始化GPIO(输入模式)
void GPIO_Init(GPIO_TypeDef* GPIOx, GPIO_InitTypeDef *GPIO_InitStruct)
2)配置SYSCFG选择器
SYSCFG选择器也是一个外设,需要使能时钟:
a.使能SYSCFG选择器时钟(SYSCFG选择器处于APB2总线上)
void RCC_APB2PeriphClockCmd(uint32_t RCC_APB2Periph, FunctionalState NewState)
b.初始化SYSCFG选择器(选择由哪个GPIO产生EXTI)
void SYSCFG_EXTILineConfig(uint8_t EXTI_PortSourceGPIOx, uint8_t EXTI_PinSourcex)
比如:配置PA0产生外部中断0 SYSCFG_EXTILineConfig(EXTI_PortSourceGPIOA,EXTI_PinSource0);
3)初始化EXTI外部中断控制器
a.使能EXTI外部中断控制器
void RCC_APB2PeriphClockCmd(uint32_t RCC_APB2Periph, FunctionalState NewState)
b.初始化外部中断控制器
void EXTI_Init(EXTI_InitTypeDef* EXTI_InitStruct)
4)配置NVIC控制器
void NVIC_Init(NVIC_InitTypeDef* NVIC_InitStruct)
按照上述流程就可以配置好外部中断,当配置好外部中断之后,此时如果满足产生外部中断的条件,触发外部中断,CPU就会自动去调用对应的中断处理函数。
因此,在配置完中断后,应该要编写对应的中断处理函数,以供CPU在产生中断之后调用。
//中断服务函数
void xxx_IRQHandler(void)
{//获取中断的中断标志位//根据中断标志位可以得知是否产生了中断//用户代码去处理中断//清除中断标志位
}
a. 获取中断标志位ITStatus EXTI_GetITStatus(uint32_t EXTI_Line)FlagStatus EXTI_GetFlagStatus(uint32_t EXTI_Line)@EXTI_Line:指定外部中断线返回值:SET 表示获取的外部中断已经产生RESET 表示获取的外部中断未产
b. 清除中断标志位void EXTI_ClearFlag(uint32_t EXTI_Line)void EXTI_ClearITPendingBit(uint32_t EXTI_Line)
下附,外部中断处理逻辑图