【初入电子坑之stm32篇(五)】理解中断与stm32中断配置

理解中断与stm32中断配置

前言

按照个人习惯,本文将从“这是啥?”“为啥需要它?”“如何操作?”三个角度展开讨论分析。

目录

  • 中断简介
  • 中断理解
  • stm32中的中断设置
    • NVIC
    • stm32中断配置
  • 总结

中断简介

中断,即机器运行过程中出现某些意外情况,需机器停止正在运行的程序并转入处理新情况的程序,处理完毕后又返回原被暂停的程序继续运行。

理解中断

想象一下这么一个场景:

你在认真的敲代码,你妈喊你出房间去客厅恰饭,并且以不出来就拔网线为威胁。这时候你能怎么办?只能乖乖保存好文档,然后去恰饭,恰完再回来打开之前的文件继续敲咯。

我们分析一下上述的场景:

  • 正在敲代码——当前执行的事件
  • 你妈喊你吃饭——中断源
  • 你听到你妈喊你吃饭——接受到中断请求
  • 威胁你——事件优先级别高
  • 保存已经写好部分——保存现场(中断响应)
  • 去恰饭——执行中断事件
  • 恰饭回房间——中断返回
  • 打开文件继续敲码——恢复现场

对于机器来说也是一样的。无论是内核出现问题(异常),还是因为存在其他比当前正在执行的程序的优先级更高的事件发生(外部中断),都会打断当前运行的程序,去处理更加重要的事。

毕竟,机器的运算速度再快,处理事件也有先后之分不是?

接下来,让我们再次回到刚刚那个场景,考虑下面两种情况:

1、当正在吃饭的时候,你忽然想去WC解决下个人问题。这时你会停止恰饭这个动作,然后去WC解决完再回来继续吃饭。吃完饭才会继续

2、当正在吃饭的时候,你收到快递小哥的短信,告诉你丰巢的验证码可以去取的时候。你肯定会先恰完饭,然后去取完快递之后(也可能先在那放着先不取)再回房间敲代码。

对于情况1而言,为了避免某些比较尴尬的事情发生,先去WC再回来吃饭几乎是必然选择。因为它比吃饭紧急。而对于情况2取快递,相对来说没那么急迫的需求,所以一般而言我们都会先吃完饭,再去拿快递,最后再回房间敲代码。

当然如果要敲的代码比较着急,那么拿快递这件事自然就得等到我们敲完代码先啦。

从上面两个常见的场景中,我们很容易发现中断是可以嵌套的,且它有一定的优先级概念。

很显然,我们的日常生活中也会有许多中断事件。我们人脑是怎么处理它们的?认真思考下下不难发现,其实对于很多事件,我们早就给它们定义了优先级。而定义这些事件的优先级并严格执行的就是我们的大脑。

对于 CPU来说也一样。当中断事件很少的时候,不需要分门别类的整理,直接比较判断优先级就可以了。

而对于中断事件较多的复杂系统,对于事件的分组和优先级设置也就成了刚需。

下面我们以stm32f10x的中断系统为例,展开学习。

stm32中的中断设置

我们知道,stm32用的是cortex-M3内核。

而CM3内核支持256个中断,256级可编程设置(即优先级设置),其中:

  • 内核中断16个
  • 外设中断240个

但stm32并没有把CM3内核的设置全部用上。
它仅有84个中断,16级可编程设置,其中:

  • 内核中断16个
  • 外设中断68个

且以上配置属于stm32中的顶配,具体到 f103系列只有:

  • 内核中断16个
  • 外设中断44个

小结下以上的数据,可以发现,实际中断事件的多少取决于芯片的定位。高端的,即外设多的,芯片中断必然多,反之必然少。

NVIC

而要处理那么多的中断事件,CM3内核给出的解决方案是——用NVIC (Nested Vectored Interrupt Controller)嵌套向量中断控制器控制。

NVIC首先定义了一段地址区域(一般从0地址开始)用于存放中断事件服务函数的地址。定义的这段地址就称为中断向量表

中断向表表1

中断向量表2

以上是从《stm32f10x 中文参考手册》中截来。

可以看到它一个地址对应着一个中断事件,其中灰色部分是内核相关的中断的位置。每当一条指令执行完之后,系统都会检查一遍看看有没有中断事件发生。如果有的话,就会在中断向量表中找到对应的中断事件,执行中断事件所在地址指向的函数(即中断函数)。

也就是因为中断事件所在的地址存的是一个地址,它指向的是该中断对应发生的事件(函数)。所以其实中断向量表这个名字起得很符合它的实际含义——表里的是一个个有指向的值

至于定义这些中断事件的优先级和它们具体是干嘛的,NVIC把设置的权利交给了开发者,它只给了可以完成相关操作的寄存器组。

NVIC结构体

从固件库头文件:core_cm3.h中,我们可以找到以上NVIC的寄存器组映射。

其中如果我们使用寄存器编程的话,最关心的寄存器一般是:

  • ISER:Interrupt Set Enable Register,中断使能设置寄存器
  • ICER:Interrupt Clear Enable Register,中断使能清除寄存器
  • IP:Interrupt Priority Register,中断优先级寄存器

在这里面,中断优先级寄存器是需要我们着重研究的。有意思的是,固件库命名的时候对于该寄存器的简写直接只用IP,而不是IPR,害我一度怀疑这两是不是同一个东西。直到我去查了手册才验明了“真身”。

IPR中断优先级寄存器

原CM3设计中,NVIC_IPRx用于配置外部中断的优先级。IPR宽度为8bit,理论上可以配置的优先级为0~255,数值越小优先级越高。但和大多数CM3芯片都会精简设计一样,stm32也做了相关的精简设计,只使用高4bit。也就是说,实际上stm32允许配置的优先级只有16个

抢占优先级与子优先级

CM3把优先级划分成了抢占优先级和子优先级(又叫响应优先级)。即,对于同一个中断事件,他有抢占优先级和子优先级两个优先级。

为什么要这么设置?

因为如果只是简单的只有一种优先级的话,太过粗线条了。像CM3内核设置最多有256级,即使是stm32也有16级,级数太多,没有主次不方便使用和管理。

类比到我们平时生活中的状态,就像社会上有256个人,每个人都不同级别一样,估计你会眼花缭乱。而解决这个问题的方法就是那就是把这256级中断归类分层,层级内再分子级的分层管理形式,达到压缩层级的效果。 如先分统治阶层、奴隶主、奴隶等,同一阶层再区分小级别。

对应于中断分组就是,把256级优先级根据本工程的需求,先规定了中断层数(即:抢占优先级)和每层有多少小级别个数。

这样抢占优先级和子优先级的概念引进我们就完全可以理解了。

抢占优先级 与 子优先级 比较

只有当抢占优先级相同的时候,子优先级才会发挥作用。若子优先级也相同,就去比较两个外设的硬件中断编号,即该中断在中断向量表中的位置,同样是编号越小,优先级越高原则。

值得注意的是 抢占优先级 与 子优先级 还有些许差别:

  • 抢占优先级:无关中断产生的先后,只比较优先级的高低。优先级高的中断信号即使比中断优先级低的中断信号后到,也可以直接中断优先级低的事件直接抢占系统资源
  • 子优先级:当两个中断信号有相同的抢占优先级,分两种情况考虑:
    • 两个信号同时到达,子优先级高的中断先响应。
    • 子优先级低的中断事件正在发生,子优先级高的中断信号刚到时,需要等到当前中断完成后,在执行玩到的中断信号。

中断分组

从前面的讨论中我们可以知道,为了中断更加可控,CM3内核做了两种优先级的分层机制。

但是对于不同的需求,我们对于阶层和小阶级的数量要求也是不同的。以身份证为例,在发放身份证之前,我们要总体考虑全国要分多少个区域,然后确定地区需要设置几位数才够。熟悉计算机网络的朋友可以发现,这里其实和子网划分是一个道理。

到这里,我们也可以看到,在一个工程中,我们一般只会设置一次,且每次只设置一个分组。还是拿身份证举例,你不可能前一秒还说身份证前3位表示A省,下一秒就说前4位表示B省吧?编码格式统一是一切的基础。

所以,我们平时在设置分组时,若不是非常特殊的需求

一个工程只设置一次分组!!!

一个工程只设置一次分组!!!

一个工程只设置一次分组!!!

重要的事情说三遍。。。

这个分组管理的设置由内核外设SCB_AIRCR(应用程序中断及复位控制寄存器)的prigroup[10:8]控制。

SCB_AIRCR

因为它通过3个bit控制,所以理论上可以分成8组(0~7组),其中每组可设置的抢占优先级和子优先级的位数是不同的。

CM3中断分组完整图

如上图,可以看到它随着组号的增大,抢占优先级可设置的位越少,子优先级越多。

上面这种分组是针对IPR里8bits都用上的情况设计的。而我们知道,stm32的NVIC是CM3的NVIC的子集,只用了IPR的高4bits,所以分组自然而然就少了。

stm32中断分组完整图

有了以上的原理性认知,我们终于进入到轻松愉快的编程配置环节~~~

配置要点

根据前面叙述的内容,辅以一些额外资料(诸如各种官方文档),我们可以得出以下配置流程:

  • 使能某个的中断
  • 中断分组
  • 初始化NVIC_InitTypeDef 结构体
    • 设置中断源
    • 设置抢占优先级和子优先级
    • 配置中断使能/失能
  • 编写中断服务函数(就是这个中断发生后,告诉机器去干嘛)

上面流程中提到的NVIC_InitTypeDef结构体,就是这哥们儿:

image-20210316182454196

它跟NVIC_Type结构体的区别在于,NVIC_Type只是寄存器组的映射,而NVIC_InitTypeDef是固件库抽象提炼出来帮助我们简化配置流程用的。

下面,我们用一个实例体会下上述过程。

EXTI

EXTI(External interrupt/event controller)—外部中断/事件控制器,用于管理了控制器的 20个中断/事件线(互联型才有20根,其他类型只有19根)。

NVIC与EXTI的关系

我们先来理一理这东西跟NVIC的关系:

  1. NVIC是CM3内核的东西,它用于整个芯片的中断控制。而EXTI是ST公司设计用于控制外设的。它不是内核里面的东西。
  2. EXTI通过魔改引出NVIC中的20根中断信号线,对外设事件的控制能力进一步加强了。

在这里,我们先理清三个概念:

  • 事件
  • 中断
  • 中断事件

比如一老师在教室里给学生们上课。课堂上的学生可能做出各种行为动作,比方做笔记、打哈气、翻书包、讲小话等,我们把这些行为统称为事件,其中有些行为老师往往只是视而不见,继续他的上课;而有些行为可能导致老师的上课中止,比方讲小话,并对学生的相关行为予以警告、批评或纠正等,然后继续上课。我们把老师因为学生的某些行为而中止授课,并产生后续动作,之后接着上课的这个过程理解为中断或中断响应。我们把可能导致老师上课中断的学生行为理解为中断事件

EXTI框图理解

下面,我们结合EXTI的框图理解上述概念。

EXTI框图1

首先,我们先对整个框图有个大概的认知:

  • 绿色+红色部分:跟外设连接在一起。绿色部分用于和蓝色部分信息交互,红色部分用于传输事件信号。
  • 蓝色部分:寄存器组,用于控制事件信号的采集和走向。
  • 黄色部分:两个输出端。用于事件信号的输出,中断信号去NVIC,非中断信号用于其他外设(如:DMA,DMA相关内容,在后续文章会详细说明)。

EXTI 可分为两大部分功能,一个是产生中断,另一个是产生事件,这两个功能从硬件上就有所不同。

信号产生过程

下面我们来分析下这两者的产生过程。

image-20210316182718811

如上图,信号首先会经过边沿检测电路,这个检测电路会通过查看 上边沿触发下边沿触发 寄存器的值,去判断信号到底在哪个边沿采集。(所谓上边沿和下边沿,即电平从低->高 or 从高->低 变化时的那条竖直的边)

经过边沿检测电路之后,就来到上图中3的位置。这里是个或门连接着输入信号和软件中断事件寄存器。也就是说,除了信号触发中断,我们还可以控制相关的寄存器从而产生中断事件。

过了步骤3或门这一步之后,产生中断和产生事件的流程就有所区别了。

我们先来看看中断的产生过程:

首先中断请求信号会先进入 请求挂起寄存器 。这个寄存器的存在意义在于,如果中断发生时,正在处理同级或高优先级中断,则中断不能立即得到响应,此时中断被悬起。悬挂意味着等待而不是舍去,当优先级高的或者同等级先发生的中断完成后,被挂起的中断才会执行。

落实到stm32这里,可以看到在标号4的位置,是一个与门,所以,中断请教什么时候推送到NVIC控制器执行,就是中断屏蔽寄存器应该控制的事了。

同理,对于事件发生来说,事件是否生成,就是事件屏蔽寄存器的事了。不同点在于,事件的产生没有优先级的概念,所有无需挂起寄存器这么个东西。

代码示例


未完待续~

参考资料

关于STM32中断优先级分组的科普,初学者常见问题解答

STM32的“外部中断”和“事件”区别和理解

浅谈中断挂起与中断标志的区别

  • Copyright: Copyright is owned by the author. For commercial reprints, please contact the author for authorization. For non-commercial reprints, please indicate the source.
  • Copyrights © 2020-2022 逸非安逸
  • Visitors: | Views:

请我喝杯咖啡吧~