目录
注:本博文只是对正点原子课程的又一次重复,总结正点的教程、写一些自己的感悟,并非以原创之名做抄袭之事。
一、STM32F4中断的介绍
1. STM32F4的中断个数、类型
STM32F4的内核是ARM家的Cortex-M4,真正的Cortex-M4支持 256 个中断,其中包含了 16 个内核中断和 240 个外部中断,并且具有256 级的可编程中断设置;
但是STM32F4并没有使用Cortex-M4的全部中断,而是只用了它的一部分,总共有 92 个中断,包含10个内核中断和82个可屏蔽中断,具有 16 级可编程的中断优先级,而我们常用的就是这 82 个可屏蔽中断。
2. 什么是内核中断与外部中断?
参考:CSDN博主【dgergeg】的文章:外部中断和内部中断
外部中断是指由外部中断源执行的中断程序,主要是指INT0,INT1,通讯中断;内部中断主要是指定时器/计数器中断、装栈溢出后置位等相关特殊寄存器来执行的中断;
举一个例子,外部中断:你在吃饭,这时候电话响了,你暂时放下餐具去接听电话在这里吃饭是你目前正在执行的程序,电话响了,是一个中断源,他是随机的,不定时发生,接完电话(处理完中断事件)你回来继续吃,(继续执行中断点没有做处理完的程序)
内部中断:你正在吃饭,这时碗里的饭没有了,你要去盛饭,盛了饭回来继续吃,在这里吃饭依然是你目前正在处理的一个程序,碗里的饭没有了相当于是一个中断,这个中断是由你吃饭引起了,相当于系统中的标志位溢出;
3. 什么是不可屏蔽中断与可屏蔽中断?
参考:CSDN博主【lidandan2016】的文章:可屏蔽中断和非屏蔽中断区别
按照是否可以被屏蔽,可将中断分为两大类:不可屏蔽中断(又叫非屏蔽中断)和可屏蔽中断。
不可屏蔽中断源一旦提出请求,cpu必须无条件响应,而对于可屏蔽中断源的请求,cpu可以响应,也可以不响应。
cup一般设置两根中断请求输入线:可屏蔽中断请求INTR(Interrupt Require)和不可屏蔽中断请求NMI(Nonmaskable Interrupt)。对于可屏蔽中断,除了受本身的屏蔽位的控制外,还都要受一个总的控制,即CPU标志寄存器中的中断允许标志位IF(Interrupt Flag)的控制,IF位为1,可以得到CPU的响应,否则,得不到响应。其中,IF位可以由用户控制。
典型的非屏蔽中断源的例子是电源掉电,一旦出现,必须立即无条件地响应,否则进行其他任何工作都是没有意义的。典型的可屏蔽中断源的例子是打印机中断,CPU对打印机中断请求的响应可以快一些,也可以慢一些,因为让打印机等待会儿是完全可以的。
根据以上分类,STM32F4中共有10个内核中断和82个可屏蔽中断,具有 16 级可编程的中断优先级。至于什么是可编程的中断优先级,请往后文阅读。
二、如何配置STM32F4的中断?
1. 什么是NVIC?
NVIC的全称是Nested vectoredinterrupt controller,即嵌套向量中断控制器。对于Cortex-M3或M4内核的MCU,每个中断的优先级都是用寄存器中的8位来设置的。8位的话就可以设置2 ^ 8 =256级中断,实际中用不了这么多,所以芯片厂商根据自己生产的芯片做出了调整。比如STM32F4只使用了这个8位中的高四位[7:4],低四位取零,这样2 ^ 4=16,只能表示16级中断嵌套。对于这个NVIC,有个重要的知识点就是优先级分组,抢占优先级和响应优先级,下面就以STM32为例进行介绍。
2. NVIC中的优先级分组、抢占优先级和响应优先级
2.1 抢占优先级和响应优先级
首先明确一个概念,若程序A的抢占优先级为0,程序B的抢占优先级为1,则程序A比程序B的抢占优先级要高。即,数字越小,优先级越高。
下面来讲抢占优先级和响应优先级:
①:若程序A已经在执行过程中,这时候程序B想要开始执行,那么,如果B的抢占优先级比A高,则B可以打断A,使系统先执行B。
②:若程序A和程序B同时想要开始执行,而B的响应优先级比A高,则系统先执行B。
2.2 优先级分组
优先级可有五种分组编号,即0、1、2、3、4,不同分组代表着抢占优先级和响应优先级可以细分成多少个等级,如分组0的意义是0:4,即0 位抢占优先级,4 位响应优先级。也就是说,对系统中的全部中断程序来说,抢占优先级都一样,都是0;而响应优先级可以有2 ^ 4 = 16种不同等级。
下面展示具体的分配关系表(来自正点原子):
在写程序时,我们应当先配置优先级分组,再配置不同中断程序的抢占优先级和响应优先级。
三、如何在程序中配置STM32F4的中断?
1. 配置优先级分组
在程序中,应当先配置优先级分组,我们才能根据分组知道抢占优先级和响应优先级各有几个等级。
通过如下函数来配置优先级分组:
void NVIC_PriorityGroupConfig(uint32_t NVIC_PriorityGroup);
其中,由于优先级分组有0、1、2、3、4共五种不同选择,则参数NVIC_PriorityGroup
有如下五种选择:
NVIC_PriorityGroup_0
NVIC_PriorityGroup_1
NVIC_PriorityGroup_2
NVIC_PriorityGroup_3
NVIC_PriorityGroup_4
2. 配置抢占优先级和响应优先级
通过如下函数配置抢占优先级和响应优先级:
void NVIC_Init(NVIC_InitTypeDef* NVIC_InitStruct)
其中,形参类型NVIC_InitTypeDef
是一个结构体,它的定义如下所示:
typedef struct
{
uint8_t NVIC_IRQChannel;
uint8_t NVIC_IRQChannelPreemptionPriority;
uint8_t NVIC_IRQChannelSubPriority;
FunctionalState NVIC_IRQChannelCmd;
} NVIC_InitTypeDef;
其中,NVIC_IRQChannel
定义初始化的是哪个中断,这个我们可以在 stm32f10x.h 中找到每个中断对应的名字。例如USART1_IRQn;NVIC_IRQChannelPreemptionPriority
定义这个中断的抢占优先级;NVIC_IRQChannelSubPriority
定义这个中断的子优先级别,也叫响应优先级;NVIC_IRQChannelCmd
用来设置该中断通道的使能。
比如我们要使能串口 1 的中断,同时设置抢占优先级为 1,响应优先级位 2,初始化的方法是:
NVIC_InitTypeDef NVIC_InitStructure;
NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn;//串口 1 中断
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=1 ;// 抢占优先级为 1
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 2;// 响应优先级位 2
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //IRQ 通道使能
NVIC_Init(&NVIC_InitStructure); //根据上面指定的参数,调用函数来初始化 NVIC 寄存器,达到对中断进行设置的目的