上一篇中,借着串口接受的问题,简要说了一下串口中断的作用和用法,本文将对STM32的中断控制体系做个介绍。
关于中断的概念,在上一篇中已经做了介绍了,说通俗点就是程序正常情况下是在while(1)内运行着相关的任务,例如下图中的任务1,任务2,在没有中断的时候,整个代码的运行流程就是完成初始化,然后在while(1)内一直循环里面的任务,此时如果任务1内有大的阻塞延时,类似串口等待接收,LED灯while死等延时这些都会阻塞整个while(1)的运行时间,导致执行效率严重低下,这种情况是不能满足日常需求的,所以在实际产品中很少有使用阻塞式的延时和while死等的情况。为了解决这个问题,这时就需要有中断的加入了。
加入中断后的运行流程图就变成了下图的情况,首先,中断是一个内核功能,不属于片上外设,但是使用过程中也需要初始化;其次,初始化后,会有一个与之对应的中断服务函数,上面的那些具有阻塞的功能通过中断服务函数内的各种标志位就可以实现变阻塞为不阻塞了;当初始化中断对应的条件满足时就会产生一个中断信号,这时CPU会停止当前任务,转而去到对应的中断服务函数里面,将中断服务函数内的任务先完成,完成后再回来继续执行原本的任务。编程时只需要判断对应的标志位即可解决上面的阻塞问题。
需要注意的是,中断是可以解决阻塞的问题,但是中断服务函数的本身也不能有延时、循环、阻塞程序,紧急事件是实时响应的,紧急事件不能执行太久。
总的来说,中断对于正常运行的CPU来说就是一个异常事件,而且异常事件的优先级高于正常运行的事件的优先级,发生异常必须先解决异常才可以重新回到正常运行。上一篇中的空闲中断和接收中断就是这样,那么,STM32的中断到底是怎么运行和管理的呢。
首先,中断的控制是在内核里面的,中断的相关内容是有ARM公司设计的,在处理器内部会有一个叫做嵌套向量中断控制器NVIC得中断处理器,他就是专门用来管理整个STM32的中断问题的,其中中断的主要来源有两个,一个是处理器的内核出现了故障会触发系统异常,进而进入中断;二是片上外设触发了其相关的中断信号也会进入中断,像上一篇的出口接收中断和空闲中断就是属于后者,是外设产生的中断。
在上一篇的配置中,已经使用到了NVIC的相关控制,关于这部分控制是官方封装好的函数,在使用过程中调用即可。也就是说,对于开发者来说,中断的控制只需要调用函数就可以了,这里借用上一篇调用的代码:
NVIC中断控制器做配置的时候流程:
第一步:配置优先级分组 (主函数配置)
第二步:合成优先级
第三步:分配优先级
第四步:使能对应的中断源 (在对应的片上外设初始化函数中)
//NVIC控制器配置
NVIC_SetPriorityGrouping(7-2);//设置中断的优先组别(111-110-101-100)
u32 pri=NVIC_EncodePriority(7-2,0,0);//设置抢占优先级为0,响应优先级为0
NVIC_SetPriority(USART1_IRQn,pri); //将对应的优先级设置映射到对应的中断
NVIC_EnableIRQ(USART1_IRQn); //使能中断源
1.中断分组函数,此函数用于对整个工程进行中断分组
void NVIC_SetPriorityGrouping(uint32_t PriorityGroup)
位置:core_cm4.h------1435行
返回值:空
参数:uint32_t PriorityGroup 分组中需要写入的值
传参:7-占先的位数
分组整个工程都要一样,可以只配置一次,只需要在主函数使用一次即可
2.使能中断源配置函数,此函数用于使能对应的中断源
void NVIC_EnableIRQ(IRQn_Type IRQn)
位置:core_cm4.h------1467行
返回值:空
形参:IRQn_Type IRQn很明显此处形参是个结构体,需要使用结构体内规定好的名称
传入对应的片上外设中断源----放中断源名字 例如:串口:USART1_IRQn
作用:使片上外设的中断能够使用 核心级别中断使能
关于此函数的形参:IRQn_Type IRQn很明显此处形参是个结构体,需要使用结构体内规定好的名称 需要用时直接跳转过去复制即可。
注意,此处的形参编程时一定要跳转到stm32f4xx.h中找到对应的,不可以自己随意修改。
3.中断优先级配置函数,此函数用于设置对应中断源的中单优先级
void NVIC_SetPriority(IRQn_Type IRQn, uint32_t priority)
位置:core_cm4.h------1550行
返回值:空
形参:IRQn_Type IRQn, uint32_t priority
IRQn_Type IRQn:中断源和上一个一样的中断源名称,需要去stm32f4xx.h复制
uint32_t priority:优先级(占先和次级一起的优先级)
4. 优先级合成函数,由于优先级写入时是一起写入的,但是分类时又是分开计算的,为了避免写入出错,官方给了一个合成函数,将分组、抢占优先级和响应优先级传入后此函数会返回需要写入的优先级值。
uint32_t NVIC_EncodePriority (uint32_t PriorityGroup, uint32_t PreemptPriority, uint32_t SubPriority)
位置:core_cm4.h------1592行
返回值:u32类型的值 优先级uint32_t priority
形参:uint32_t PriorityGroup, uint32_t PreemptPriority, uint32_t SubPriority
uint32_t PriorityGroup:优先级分组
uint32_t PreemptPriority:占先优先级
uint32_t SubPriority:次级优先级用法:u32 pri= NVIC_EncodePriority(7-2,3,2);NVIC_SetPriority(USART1_IRQn, pri);
从上面的注释中,可以清除的看出NVIC控制器的作用:
①对中断优先级做分组
② 接收来自中断的请求
③ 配置中断的优先级
④ 使能外设的中断源
具体的代码在"core_cm4.h"中,查看步骤如下图所示,点击main.c前方1号框所在位置的"+“号,然后选择"core_cm4.h”,下滑就可以找到相关函数,对应的函数描述就在其上方注释。
仔细回想上一篇中的配置过程,关于中断配置,除了上面的NVIC控制器的相关操作,还在USART的出事后配置中,使能了接收中断和空闲中断这两个控制位,那么这个使能与上面的中断源使能有什么区别呢。
中断源:一个片上外设 USART1独立的中断源 USART2也是独立的中断源
中断信号:指的是每一个中断源下,会有多个触发条件,例如串口1的接收中断和空闲中断。
也就是说,中断源相当于房子里的总电闸,而中断信号相当于房间里灯的开关,插座的开关,电视的开关。要开灯,不仅要打开总闸,还需要开启对应的分开关才行。
所以在中断配置过程中不仅需要配置对应的中断源使能,还需要使能具体的中断信号。
知道了中断的管理方式,接下来就是怎么管理的问题了。
在上述管理的相关函数中,涉及了多个中断里面的名词,像分组,占先优先级、次级优先级、占先位数等等,看起来是有些混乱,接下来就来解释一下这些东西的具体作用。
首先,需要搞清楚优先级的定义,对于Cortex-M处理器的异常是否能被处理器接受以及何时被处理器接受并执行异常处理,是由异常的优先级和处理器当前的优先级决定的。
更高优先级的异常可以抢占低优先级的异常,这就是异常/中断嵌套的情形,也就是上面提及的占先优先级。
有些异常(例如:复位,Sys_Tick等)具有固定的优先级,其优先级由负数表示,这样,它们的优先级就会比其他的异常高。
其他异常则具有可编程的优先级﹐范围为0~255。依据以上描述优先级被分为三类:
1.占先优先级:程序员进行配置的,具备抢占功能
2.次级优先级:程序员进行配置的,不具备抢占功能
3.自然优先级:系统自己配置(不需要我们管)
优先级:数字越小,优先级越高
解决了优先级,接下来就要高清楚分组的问题了。这个分组其实就是决定上面提到的占先优先级的在优先级控制寄存器中的位数和次级优先级在优先级控制寄存器的位数,先看看M3和M4的权威指南关于分组的描述:
ARM在涉及的时候一共预留了一个8位寄存器来实现优先级控制的编程,理论上占先优先级和次级优先级会有2^8=256种组合方式。
但是,由于过多的中断优先级分组会影响单片机的性能,所以ST公司在实际设计STM32芯片的时候就丢弃了LSB也就是这个八位寄存器的第四位,只保留了四位用来做优先级的控制编程,这样一来,STM32的优先级分组就只有2的四次方种,也就是16种组合。
ARM体系下优先级配置寄存器总共由8位,占先和次级同时使用这个寄存器
中断的优先级共有256种
ST体系下,ST公司对ARM公司的中断进行了裁剪,只是用寄存器的高4位
中断的优先级共有16种。
可能还是有点没说清楚,来看下表吧,由于舍去了第四位,占先优先级和次级优先级这两个一起瓜分这四位,通过这四位来配置中断的优先级,其中的配置方式如下表:
写入的分组数 | 占先在优先级寄存器中的位数 | 次级在优先级寄存器中的位数 | 此模式下中断的具体优先级数 |
---|---|---|---|
7(111) | 0位 (范围:无) | 4位(范围:0-15) | 16种 |
6(110) | 1位 (范围:0-1) | 3位(范围:0-7) | 16种 |
5(101) | 2位 (范围:0-3) | 2位(范围:0-3) | 16种 |
4(100) | 3位(范围:0-7) | 1位(范围:0-1) | 16种 |
3(011) | 4位(范围:0-15) | 0位(范围无) | 16种 |
2(011) | 4位(范围:0-15) | 0位(范围无) | 16种 |
1(011) | 4位(范围:0-15) | 0位(范围无) | 16种 |
0(011) | 4位(范围:0-15) | 0位(范围无) | 16种 |
举几个栗子吧:
1.假设在这个系统中,我们不需要占先优先级,此时就可以将占先的位置零,让优先级控制寄存器独享四位,这样的话,整个系统的中断之间就不会出现打断和抢占,当同一时刻有多个中断到来时,会优先执行次级优先级高的中断,执行完毕后才会转去下一个优先级相对高的中断。
2.假设我们在整个系统中需要各种中断嵌套,这时就可以不为次级优先级分配优先级控制寄存器的位数,让占先优先级独享四位,这样就可以有(0-15)16种中断嵌套,其中优先级最高的可以打断任意中断。
下一篇:高标准农田信息化监测系统解决方案