中断的概念
什么是中断
中断:正在进行的事务被突发事件打断,转而处理这个突发事件,突发事件处理完成后回到被打断的事务继续执行,这一处理突发事件的过程叫做中断。
对于STM32,由于中断源的触发,常规程序被打断,CPU转而运行中断响应函数,而后又回到常规程序的执行,这一过程叫做中断。
一些生活中的"中断":
为什么使用中断
突发性:随时地在任意时刻发生、没有预兆。
紧急性:需要马上被处理。
例如,电脑通过串口向STM32发送数据,但STM32不知道电脑什么时候会发送数据,无法提前做出准备;并且电脑发送数据,等不到主程序执行完再处理数据,只能打断主程序的执行。
更多关于中断的基础概念和相关术语可参考:
http://t.csdnimg.cn/LG2b9
中断优先级
表示方法
与51单片机不同(通过寄存器),STM32通过数字去表示中断的优先级(通过4个比特位表示数字),且中断优先级可分为抢占优先级和子优先级,具体如下:
中断优先级 | |||
抢占优先级 | 子优先级 | ||
bit3 | bit2 | bit1 | bit0 |
1:0 | 1:0 | 1:0 | 1:0 |
可以看出优先级一共有0000~1111,共16个优先级,其中0000优先级最高。
中断优先分组
对于抢占优先级和子优先级,不一定是2:2的数量关系。其可通过中断优先分组来进行配置。其数量组合如下:
当配置为0位抢占优先级4位子优先级时,所有的中断都不会嵌套。而 4位抢占优先级0位子优先级时,恰恰相反。
优先级规则
1、优先级的值越小,优先级越高。
2、遇到抢占优先级更高的中断,会发生"中断嵌套"。
3、未发生中断嵌套时会发生"中断排队",总优先级更高的中断排在前面。
中断源
2种中断源
"脉冲型"中断源(罕见):中断触发信号一闪而过。
"电平型"中断源(常见):中断触发信号持续,直到手动关闭,因此在中断响应函数结束前清除中断。
4种状态
不活动:中断没有被触发,或已响应结束。
活动:中断正在被响应。
挂起:中断正在排队等待处理。
活动且挂起:中断处理的过程中再次出发(仅脉冲型)。
值得一提的是,假如中断A被执行的过程中,又被中断B嵌套从而执行B,此时A、B均为活动状态。
NVIC简介
NVIC:译为"嵌套中断向量控制器",一种核心外设,位于Cortex-M3内核中,负责管理中断(例如:中断进入时保护现场、中断退出时还原现场、中断优先级的设置、中断仲裁等)
内部结构
如下图所示,对于Cortex-M3内核,其NVIC管理中断源的最大数量是81路(中断源0到中断源80)。中断源往里走,有一个开关,负责中断屏蔽。再往里走,就有一个中断仲裁的模块,用来决定中断排队的顺序和是否进行嵌套。
在中断仲裁的过程中,也需要读取每一路的中断优先级,即读取存储优先级的4个bit位。红色虚线代表优先级分组。
中断协作模型
所谓中断协作模型,就是用来表示中断处理过程中,STM32内部的一些模块,它们相互之间的一些分工关系。
主要可分为三大模块:片上外设、NVIC、Flash。
中断编程的流程也可根据此模型来进行,如下所示:
1、开启中断源(开启方法因模块而异)
2、配置NVIC
3、编写中断响应函数
中断向量表
对于中断,其中断响应函数同样存储在Flash里,但Flash里面还有别的代码,当CPU中断发生时,可以通过中断向量表定位到中断源所对应的中断响应函数。
Flash存储内部从地址0开始的一段区域,会按照中断号(0~80)依次排序,每4个字节存储一个中断响应函数的地址,这就是中断向量表(相当于中断函数的地址集合)。当某个中断发生时(假设其中断号为0),CPU会去中断向量表里面的0号区域,查看对应的地址,通过地址找到具体的中断函数。
具体模块的中断号可去参考手册里面查找。
NVIC编程接口
NVIC模块的驱动程序位于misc.c和misc.h中,其编程接口有两个,如下所示:
其中,第一个函数NVIC_PriorityGroupConfig用于配置优先级分组;第二个函数NVIC_Init用于对NVIC的某一路中断源初始化。通常由这两个函数配置NVIC。
注意,不需要对NVIC去使能时钟,因为NVIC是芯片内核的,只要一上电,芯片就会工作,默认情况下时钟就是开启的(并且内核的时钟是无法关闭的)。
函数NVIC_PriorityGroupConfig具体使用方法:
函数NVIC_Init使用方法:
实例代码,例如要使用串口中断:
NVIC_InitTypeDef NVICInitStruct;
NVICInitStruct.NVIC_IRQChannel=USART1_IRQn;
NVICInitStruct.NVIC_IRQChannelPreemptionPriorty=0;
NVICInitStruct.NVIC_IRQChannelSubPriorty=2;
NVICInitStruct.NVIC_IRQChannelCmd=ENABLE;
NVIC_Init(&NVICInitStruct);