stm32的中断系统

引言

       在计算机系统中,我们的程序在执行时常常不只是一味地从上到下的执行,还可能出现一些突发事件,此时需要我们先去执行后再返回原来的位置继续执行;同时,我们写代码中在最后都是放入一个死循环中,意味着程序总是只能把主程序运行完以后才能执行其他事件,而这显然容易耽误其他事件的执行。为此,在计算机系统中引入了一种机制,能够在一定时间打断主程序的运行,优先去执行某个特定程序,这种机制就是今天我们要详细叙述的中断机制

       鉴于本次文章将比较详细的介绍中断相关知识,所以篇幅比较长,大概万字以上,希望阅读者能静下心来,建议调整速度,慢慢阅读。

一、中断概述

1.1 中断的概念

       为了更通俗地对中断概念进行解释,这里我们举一些生活中的例子。在我们日常生活中其实处处存在中断,比如我们和朋友约好过会要出去吃饭,然后我们不确定朋友什么时间去吃,然后我们每隔一段时间就问朋友去吗?去吗?就这样一直问,毕竟怕耽误忘记,但是呢这样就会造成一些问题:一方面这样一直问,朋友也会烦;另一方面,一直问的话也会影响到自己的事情,降低了效率,这显然是不合适的。那么这个时候呢,我们可以提前给朋友说好,要走时给我打电话我就出来,那么这个时候我就能安心地做自己的事情,只要电话铃响了,我就知道是要出去和朋友吃饭了,这样我就不用一直反复问朋友,同时能够高效率的处理自己的事情,一举两得,而这个处理事件的过程就是一种中断过程。

总结一下:

      在主程序进行的过程中,出现了特定事件,使得CPU暂停当前正在运行的程序,转而去处理这个事件,等这个时间处理完成之后,CPU再回到刚才被打断的位置继续处理,这就是中断。

       而这里我们所说的“朋友打来的电话”就相当于计算机系统中的中断,“朋友打来电话时的瞬间”就相当于计算机系统中的断点,这个断点可以理解为人对接电话时和之后原本该做的事进行一个记忆,方便处理完和朋友吃完饭这件事以后继续做原来的事,换句话说这个断点可以理解为一个对被中断源打断的位置的上下文进行存档的东西;而“我们和朋友去吃饭的过程”就相当于计算机中的中断处理程序

总结一下:

       那个打断CPU执行的特定事件,我们一般称之为中断源,被中断源打断的位置称为断点,处理特定事件的过程称之为中断处理事件

这里我们给一张图形象化描述一下中断在主程序中的执行效果:

        看图我们能理解到,我们计算机中程序的执行是一条一条往下执行的,也就是说,实际上我们这个中断源就是放在我们要打断的程序的位置,当执行到中断源时,程序就会自己跳转到中断处理事件所在的位置去执行,断点这个东西呢我们在调试过程中也会用到,这个断点可以理解为我们给程序添加的一个中断源所在位置,当程序执行到这里就会停住(相当于进入了中断处理程序中)

这个时候呢,我们能够感受到该过程和程序中函数调用过程极其相似,实际上,本质上,中断处理和函数调用过程是差不多的,那么这样看不就没什么区别了?那么执行中断时,在又冒出一个中断请求是不是就像函数调用一样在函数中调用函数呢,以此类推不就像套娃一样了,在函数中我们可以叫做函数的嵌套,在中断处理中也一样,我们把这种在执行中断处理程序过程中又被另一个中断源打断的模式叫做中断嵌套。

        既然出现了中断处理中打断中断的情况,那么我们难道说什么中断都会打断当前的中断处理吗?显然是不合理的。比如咱日常生活中,一般打来的电话就是一种中断源,这个时候呢我正在打游戏,然后突然来了个电话:诶是朋友的电话说是去个地方聊聊天,然后这个“和朋友在某个地方聊天”的过程就是正在执行中断处理程序的过程,这个时候呢又来了个电话,我一看是父母来的电话,那这个时候我们当然得接啊,因此我就在朋友那先暂停了聊天,接下了父母的电话,说完以后呢我再去和朋友聊,这个和朋友去某个地方聊天过程中被打断去和父母打电话,打完后再继续和朋友聊天的过程就相当于一个中断嵌套;当然如果和朋友聊的时候一个广告推销的来了电话,那显然我们没时间理他,相比和广告推销聊天,优先和朋友继续聊显然石更明智的选择,这个时候呢我并没有被“广告推销电话”给打断,仍然继续进行着“和朋友聊天”的中断程序,这个时候很显然意味着中断没有被打断,中断嵌套的过程就被阻止了,这种“优先”进行某特定事件的情况就是中断优先级。

总结一下:

       正在执行中断程序时,这个过程中可能还会被另一个中断源打断,使得CPU转而去执行另一个中断源的中断处理程序,这个过程称为中断嵌套

       同时,中断B能否打断中断A,需要看他们的中断优先级,优先级高的中断可以打断优先级低的,但是优先级低的不能打断优先级高的。关于中断优先级,后面我们会详细介绍相关内容,这里先不介绍。

这里,给一张图来形象化表达程序中的中断嵌套过程:

       解释:主程序执行过程中执行到断点处(中断源打断的位置),首先把上下文存档(便于中断处理完后返回继续执行后面的主程序),然后进入中断处理程序A中继续执行,执行过程中又执行到断点,首先比较一下中断B和中断A的优先级,发现中断B优先级高于A,因此中断处理程序A又被打断而暂停执行,断点存档后继续进入中断处理程序B执行程序,执行结束后返回中断处理程序A中暂停的位置继续执行中断程序A,中断程序A执行结束后再返回主程序中暂停的位置继续执行后面的主程序。

       既然会出现中断嵌套,意味着计算机系统中可能出现多种中断源,这里我们把中断源分为内部中断源和外部中断源两大类。

       也就是说,中断源可以是外部的,也可以是内部的。外部的叫做外部中断源,内部的叫做内部中断源(内部中断源有时候也叫异常Exception)。

       对于中断的分类也有其他的说法,即外设上发生的中断称为外中断,也叫狭义中断,另一种就被称为异常,而我们这里所说的外部和内部区分的方式可以理解为广义上的中断

1.2 为什么需要中断

        当了解完什么是中断以后,我们就想啊:为什么要有中断呢?前面我们其实已经解释了,就日常生活中来说,我们不是通过中断(打电话来知道去做特定事件)的话,就会不确定,然后一直反复去问别人或者一直等待,这样就会导致自己做事效率变低,而且还会让别人觉得烦。在单片机系统中也一样。

       对单片机系统来说,中断是至关重要的。

       因为我们现在是学习单片机中的中断处理,所以这里举一个单片机中使用中断的例子:比如我们要检测按键按下,如果没有中断,相应的我们就要用循环的方式不断去检测按键对应的IO口的电平状态,这种方式我们也称为轮询,就是一轮一轮的询问嘛。但是当这种情况多了以后,则还是非常耗费CPU系统资源的,甚至CPU会导致阻塞。

       相反,有了中断以后事情就变简单不少了,主程序中不再需要循环不断地去检测按键,而是只有当按键按下的时候,打断CPU的执行,去执行按键处理程序就行了,当没有按键按下时,CPU完全可以正常的执行主程序的代码,不会受到丝毫影响。

1.3 STM32的中断

介绍了中断的概念以后,接下来我们介绍一下在STM32中的中断。

       由于中断一般是打断CPU执行程序,而CPU又和内核相关,因此对于不同的内核架构,其中断的支持都是不尽相同的。对于我们使用的STM32单片机系统来说,其内核架构前面我们也介绍了是Cortex-M3内核架构。

       Cortex-M3内核,它支持256个中断,其中包含了16个内核中断和240个外部中断,我们可以发现这个内核中断非常少,因为内核中断也叫异常嘛,只是一些掉电时的中断、硬件导致的中断或者是一些不可屏蔽性的中断,这些都是相对比较少的。外部中断因为是关于外设的中断,而连接的外设时非常多种的,咱也不知道会出现一些什么情况,因此他提供了大量的中断源。

       同时,STM32也对这些中断源设置了中断优先级,总共有256种可编程中断设置

       当然,如果我们使用单片机的时候设置中断时如果这么多的话,那设置中断都得半天,那就很不方便了。因此呢,一般情况下,芯片厂商会对Cortex-M3内核的中断进行裁剪。也就是会让该内核的中断减少一些,以方便我们使用。

       对于STM32,它有84个中断,包括16个内核中断和68个可屏蔽中断,具有16种可编程的中断优先级。

       由此看来,过去中断优先级256种,如果用寄存器配置,那需要占8位,而现在内核经裁剪后只有16种中断优先级了,只需要4位就可以设置所有的中断优先级,显然方便了不少。

       而我们当前使用的这款STM32F103系列的芯片算是低性能的一款了,因此只有70个中断,其中10个内核中断和60个可编程的外部中断。

       这里,我们可以打开STM32F103系列的中文参考手册,进入【9 中断和事件】->【9.2 中断和异常向量】,然后就会看见两种向量表,一种是互联型产品的中断异常向量表,另一种是其它STM32F10xxx产品的中断向量表。其中这个互联型的中断会相对多一些,他还会包含以太网那些的中断。 

       而我们使用的单片机呢对应的是后者STM32F103xxx产品的中断向量表,因此我们看第二张表:

灰色表示内部中断(也叫异常),白色背景叫做外部中断

        这个表中,我们可以看见所有的中断优先级,而且这些中断向量都会在单片机启动时全部加载到单片机中。

       由图可以看出,里面每一种中断的名称都对应了一个地址,也就是说这个中断向量表主要描述的是中断名称和地址的一种映射关系,这个地址呢它不是代表了中断程序,而是表示中断处理程序的入口。

       这个表中有以上几栏,【位置】表示的就是这些中断的一个排序;【优先级】就是表示了这些中断的优先等级,其中数字越小说明中断的优先级越高;【名称】就是表示中断的名称;【说明】就是对该中断的解释;【地址】就是执行中断处理程序需要跳转的地址入口。

       其中我们可以发现前三个中断的优先级都是负数,意味着这三个中断是最先会被执行的,我们再看看名称:复位中断、不可屏蔽中断(RCC时钟安全系统...)以及硬件失效中断,这些中断都和系统密切相关,因此一定要最先执行,同时我们发现就这三的优先级类型是固定的,意味着这三个中断我们不能手动设置,实际上呢,我们发现配置中断优先级的时候一定会使用无符号整数去配置寄存器,而这里是负数,我们是不好配置的,同时由于数字越小优先级越高因此干脆就设成了负数,表示这三种中断是优先级最高且不可设置的。

       我们大致看一遍中断向量表,可以发现里面存在大量的外部中断种类,如SysTick、TIM、ADC、USART、EXTI等等,大家感兴趣的话可以去自己去阅读参考手册进行相关的了解。

1.4 STM32的中断体系架构

介绍完STM32中的中断以后,接下来就来详细的介绍一下STM32的中断体系架构。

       STM32的中断体系架构,首先肯定会涉及到不同的部件,其中最核心的部件就是内核。因为所有的中断都是将中断请求发给内核,然后打断内核执行程序,进而让内核跳转到中断处理程序中执行,因此呢内核当然就是整个中断体系架构的核心部件了,我们这里当然就是cortex-M3内核了。

       内核作为核心部件,面对大量的程序处理,显然是不可能全部交给内核一个部件来处理的,因此这里我们还会引入一个部件——NVIC嵌套向量中断控制器。通俗理解,如果我们把内核当做古代的皇上,它能够管控所有中断,那么这里的NVIC就相当于皇上旁边的宰相、大总管了,来辅助皇上管理手下的事务。

        那么。NVIC嵌套中断向量控制器是怎么样来管理这些中断的呢?首先,我们看他的名字有个嵌套,我们就能想到他肯定能管理中断嵌套吧,NVIC可以按照中断优先级高低来控制这些中断,然后依次把中断请求给到我们的内核执行中断处理程序。同时其管理的中断源会分为三种,按照离内核的远近划分,也能理解为优先级的高低吧:

       一是内核其他控件,我们能想到这个应该主要就是内部中断(即异常),如系统滴答定时器、复位等等始终内部中断;

       二是片上外设相关的中断,如串口、定时器、I2C等等,这些都算是片上外设;

       三是外部中断,也就是片外外设了,即来自芯片外部引入的一些外设,如GPIOA...G这些端口,用来连接我们的片外外设,当然我们知道GPIO端口非常多,有144个引脚,如果我们每一个引脚都来条线的话那将出现大量的线束,很显然是不太合适的

       因此我们会对这些引脚接线做一个规整,即我们对这些端口首先会每16条线做一个规整,然后接入AFIO引脚复用选择器上,之前端口是直接连上ODR,而现在我们不是直接连在ODR上,也就是使用一种复用功能,让16个引脚能同时复用一个线路,也就是AFIO。

       这里每16条线一连刚好7组GPIO端口,然后我们这里还不是直接就给到NVIC,而是先每7个引脚选一个给EXTI外部中断控制器,然后由EXTI处理后给NVIC。因为这么多片外的外设不会同时出现,所以这里我们先对这些引脚再做个7合一,即从引脚0开始到15进行七合一,比如7个0引脚就是PA0、PB0...PG0做一个合并,然后这根线我们叫做EXTI0,对应一个中断服务程序,细心的朋友在前面描述中断向量表的时候应该也注意到其中有EXTIx为名称的中断服务程序,就是这样,当我们调用片外外设的中断服务程序时只需要调用EXTI0即可调用端口对应的中断程序了。

       当然,这个中断服务程序也是7合一的,我们并不知道调用的这个到底是7个中的哪一个产生的,因此我们会需要在EXTI中做一些配置,这样EXTI才能知道到底是7合1中哪一个引脚来的中断,这就是EXTI外部中断控制器要处理的事情。

       所以,七合一连线到EXTI上会产生16条线才能把全部的引脚接完,然后他还会接另外四条线,针对其他一些比较特殊的外部中断来源,即PVC电源检测、RTC闹钟、USB检测、以太网唤醒这四种外部中断来源。也就是说,EXTI会连接20根线来控制外部中断来源。当然,我们前面说过这个以太网唤醒模块属于是互联型产品才有的功能,而我们使用的F103系列产品是没有的,因此实际上我们这款芯片上的EXTI只连了19根线

       最后,EXTI就会连到NVIC上,将外部中断来源交给NVIC,然后NVIC根据中断优先级高速这些中断源先执行谁,再执行谁就完事了。

最后,这里再给一张图来形象化描述STM32的中断体系架构:

1.5 NVIC嵌套向量中断控制器

       前面介绍了STM32的中断体系架构,我们能够明显感觉到除了内核以外有两种部件最为重要,一种就是现在要详细介绍的NVIC中断向量控制器,另一种就是EXTI外部中断控制器。

1.5.1 NVIC的介绍

       NVIC(Nested vectored interrupt controller 嵌套中断向量控制器),从名字上可以看出来,他就控制了我们所有的中断请求,无论是外部的还是内部的,无论是中断还是异常,都需要经过NVIC进行调配和中断优先级的判断,然后再交给内核进行处理,所以呢他是跟处理器的接口紧密相连的,可以实现低延时的中断请求和高效的处理中断。因此有时候因为NVIC离内核太近,所以我们把他当成内核的一部分去考虑了,即在参考手册中关于NVIC的介绍就提到“关于更多的NVIC的介绍要去参考STM32的内核的编程手册”了。

      同时,每一个外部中断都是能够做单独配置的,即都可以被使能或者禁止,也就是说,一种情况时:这个中断并不是接了那条线或者说EXTI管理的中断源来了就会马上交给NVIC去做优先级判断,然后给内核处理,而是他能在源头那边直接把他禁止掉,也就相当于这个中断请求就不在发生了;另一种情况就是:如果产生了中断请求,但是此时内核正在处理其他的中断请求,这个时候就很可能产生中断嵌套,那么如果这时候又产生的中断优先级高的话内核就会先来处理这条中断请求,但是如果后来的中断请求优先级低于正在处理的中断请求呢,很明显后来的就得等待,即没有被屏蔽也没有被禁止,这种状态我们称为挂起状态,当然,挂起的中断请求总会被处理,因此被处理完毕的状态就叫做清除状态

       处理器的中断可以是高低电平的形式,同时也能是脉冲的形式的,即上升沿或者下降沿的状态,当然这种脉冲的状态更能体现中断的一种突发的变化,这才是我们检测到的一种中断异常事件,由此中断控制器就能处理各种中断源。

       也就是说,16个IO的中断与PVD(电源检测)、RTC(实时时钟)、USB、以太网唤醒(互联型产品中才有)这20个外部中断会通过EXTI来控制,然后交给NVIC,其他中断即内部中断或异常以及片上外设相关中断会直接交给NVIC来处理,即所有中断最终都会由NVIC统一进行处理

1.5.2 中断优先级

       前面说了,NVIC主要就是管理中断的优先级,看他们是不是要进行这个中断嵌套,即已经在处理一个中断,现在又来一个中断,那么我们要不要打断这个中断呢?我们直观的一个想法就是看谁更重要,分个优先级高低嘛,然后后来的优先级高的话就打断当前正在处理的优先级低的中断呗,或者就是一堆中断来了,都在那等待呢,然后正好处理完一个中断,现在当然就是在挑一个等待的中断来执行被,那么怎么挑呢?就是根据优先级高低来选择,即便是先来等待的是优先级低的,别人后来的优先级高的也是会先去被内核执行,这样应该比较好理解。使用呢这里对优先级的理解应该有两层含义,即抢占优先级(不同的优先级时,谁能打断谁)和响应优先级(同等优先级都在等待时,先选择谁)。

       换句话说,抢占优先级是一种VIP客户和普通用户的情况,存在着绝对的优先级划分,有着明显的优先级高低之分,是直接打断别人的优先级;而响应优先级不同的是,都是VIP客户,同等的优先级中断都在那等待时,这时候就会出现VIP中P,来确定谁先去响应,但是呢你不能去打断别人,每个等待的中断都是平等的,如果实在没法确定谁先去的话呢就直接按照中断向量表中的中断排列顺序来依次去进行响应,去内核进行中断的执行。

总结一下:

       NVIC为了方便管理中断,可以通过软件给每个中断设置优先级。NVIC用4个位来控制优先级,值小的优先级高。把优先级分为两种:抢占优先级响应优先级

规则:

  • 优先级值越小,优先级越高。
  • 如果不设置优先级,则默认优先级为0。
  • 先比较抢占优先级。抢占优先级高的可以打断抢占优先级低的。
  • 若抢占优先级一样,再比较响应优先级。但是响应优先级不会导致中断嵌套
  • 若抢占优先级一样的同时挂起,则优先处理响应抢占优先级高的。
  • 若挂起的优先级(抢占和响应)都一样,则查找中断向量表,值小的先响应。

        知道优先级的两种划分后,我们就得知道,在底层我们应该怎么去配,怎么去设呢?首先就是NVIC,因为NVIC离内核很近,所以呢我们关于NVIC的相关操作也就相当于是对内核的一些操作,所以呢即便是我们使用寄存器去进行编程相关操作,也不会直接手动去配置里边的寄存器了,实际上就直接去调库就行了。那么怎么调呢?我们之前创建工程时引入的启动文件是不是有那个Core、cm.c啊?也就是说,他内核的一些相关的文件里边其实就有了NVIC相关的一些库函数,所以我们直接去掉哪里的库就可以去设置这里的优先级了。

这里我们可以扩展来叙述一些关于调用的库的底层的原理。

       首先,我们前面已经了解了两种优先级的基本规则,其中就有说优先级值越小,优先级越高,如果不设置优先级的话,默认优先级是0,意味着我们应该可以自己去设置这个优先级的值,如果不设置的话就默认给一个0,大家应该能想到我们给优先级都是无符号数,所以0应该是最高的优先级了。因此很明显我们现在底层应该就会有一个寄存器,可以针对每一个中断有那么几位0101的这样的值,来配置优先级的值,只要去写那个寄存器,就能修改对应中断优先级的值了。

       既然知道我们应该去找可以修改优先级值的几存起来,那么现在我们先取内核编程手册宏看一下它整体到底是什么情况。所以我们先进入内核编程手册中的第二章【2 The Coretex-M3 processor】,然后进入【Exception Model】异常模型这一部分,这里的异常呢是一个比较大的概念,他是把我们所说的中断和异常统称异常了。有些资料也是这么叫的,这里我们知道就可以了。

       进入【Exception Model】以后,里面2.3.5 有一个【Exception priorities】异常的优先级,我们看看就会发现,这里定义的有两个寄存器里边可以对优先级进行配置

       首先第一个就是系统级别的异常处理的优先级是在一个叫做(SHPRx)的寄存器里边进行配置的,我们点击一下这段蓝色文字就能跳转到该寄存器的相关介绍内容部分

      我们发现,这里寄存器都是对我们的内部中断(也叫异常)的优先级值进行配置的,但是这里只有6种,按理说应该是有10种,为什么呢?实际上我们前面讲中断向量表的时候说过,这个优先级最高的前三个中断也就是内部中断前三个是固定的优先级类型,也就是不能手动设置的优先级,因此这里没有显示,然后还有一个我们可以对照表中的内部中断发现,还缺一个调试监控的中断,这是因为调试相关的特殊一点,没有优先级配置,主要就是调试功能嘛。

当然,关于内部中断的优先级一般都比较高,所以我们平时也不咋需要配,一般我们主要都是去配置那些外部中断,那么外部中断在哪配呢?那很明显就是刚开看到的另外一个寄存器了,我们回到刚才看的【2.3.5 Exception priorities】部分

然后,我们看见第二个这就是NVIC模块的寄存器了,点击第二个中断优先级寄存器(NVIC_IPRx)跳转到相应寄存器部分

       这里我们可以发现,NVIC_IPRx寄存器后面有个x,也就是说他是存在多个寄存器的,点开以后就能看见这里有21个寄存器,同时我们发现,每一个寄存器都被分成了四块,和明显IP[],就表示interrupt priorities中断优先级的值嘛,然后每个寄存器都是32的,一个寄存器又可以放四个中断优先级的值,因此每一个中断优先级的值可以放8位,也就是256种优先级值,显然对于底层寄存器还是保留了内核本身的优先级数。当然,因为芯片厂商对内核进行了裁剪,也就是说实际上他应该只有4位的优先级值配置,所以我们可以看看图中表42中的描述:

        我们看看这段红线标出的话,大概意思就是我们现在做这些配置的时候,主要生效的是[7:4]尾,即高四位会生效,也就是说我们配置的时候只能配置四位的优先级值嘛,即2^4=16种,而剩下的低四位在读的时候就会作为0来处理,直接忽略我们的写入,因此我们这里配一个16种优先级就可以了。

       这里我们能够发现呢这里21个寄存器,一个寄存器配四个中断的优先级,也就是能够配置84个中断,同时我们前面也说过SMT32中一共就是有84个中断,所以理论上都是能配置的。当然了,大家会发现,这84个中断还包括了16个内核中断,而我们前面也看见了内核中断在另外的寄存器重配置,因此呢这寄存器中84个只是一个冗余的扩展而已,咱知道这是一个怎么回事就行了。然后呢,我们看这里有21个寄存器,咱配置的时候不会来这对寄存器进行配置,而是直接去调用NVIC的库就行了,标准库中也有相应的库可以调用。

       然后前面我们还说了这个优先级还分成抢占优先级和响应优先级,相当于有一个整体来划分的一个优先级,然后我们还可以进行细分,就相当于我们当时说的VIP中的优先级嘛,那么这个我们应该在哪配置呢?

        实际上呢,我们可以做【PRIGROUP】这样一个优先级组的配置,所以在底层实际上不是我们这里给的所谓抢占和响应优先级的概念,这里只是为了方便咱们理解,所以给的两种不同层次的优先级。那么他底层实际上是怎么叫的呢?他是先分一个优先级组,这个优先级组就相当于第一次的大的划分,即相当于我们所说的抢占优先级,然后呢它再对这个优先级组去细分【Subpriority bits】子优先级,那不就相当于VIP中再细分嘛,那不就是咱说的响应优先级啊。所以大家注意,底层这里不是叫的抢占和响应优先级,而是叫优先级组和子优先级。

       所以这玩意到到到底应该怎么配呢?实际上呢,我们还是要回到内核编程手册中,进入【Exception Model】异常模型中,然后进入【2.3.6 Interrupt prority grouping】中断优先级组中找对应寄存器进行配置

       也就是上图中这个蓝字部分的SCB_AIRCO寄存器里面进行配置啊,我们点击跳转到相应寄存器的介绍中看看

        实际上,这是一个系统控制模块的寄存器(SCB,即System control block),然后看他名字就是Application interrupt and reset control register应用中断和复位控制寄存器,这里我们不管别的,就只看里面跟这个优先级相关的部分

也就是上图框出的这三位,写的PRIGROUP,很明显就是优先级组的配置,描述了优先级组该怎么划分配置。

       我们看关于这三位的介绍似乎有点奇怪,我们可以往下翻,看见一张表格,里面就介绍了关于优先级组的内容

     我们来看看这张表是什么意思,首先看这里第一列,PRIGOUP[2:0]意味着这里取值是三位000-111一共就是8种取值,但是在这张表中的取值只有五种,他是从011开始取得:011-111,每一种情况代表的是我们那个优先级值,因为优先级值一共就4位有效,所以他就是在这四位里面划分优先级组和子优先级,也就是说抢占和响应优先级在这直接四位就能搞定。

       那么是怎么划分的呢?实际上这里的011-111是在划分优先级组和子优先级的分组模式,也就是优先级划分的模式。我们来以第一行为例:如果是第一种011,就相当于是模式三:第一种分组模式,然后是0bxxxx,这里的x表示的是我们配优先级组的那些位,换句话说,这些表示了我们的抢占优先级;同时我们容易发现下面也有xxxy等类似情况,这里的y就表示我们要配的子优先级的那些位,换句话说y就表示我们要划分的响应优先级。

       好,这时候我们就不难发现,这五种分组模式就是相当于全是优先级组、优先级组+子优先级以及全是子优先级这三种情况,然后在总共有效的四位中,根据子优先级或者优先级组的占比一共会产生五种情况,因此呢这里就是从011开始到111这五种划分情况了。

        现在我们继续来看第一种分组模式:也就是四位全部都是优先级组,即全部是抢占优先级,那么这个时候我们就会出现16中不同的组优先级,换句话说,所有的16种优先级彼此之间都是可以打断、可以抢占的,不存在优先级同等然后VIP中P的那种情况了,他们可以去随时打断某个中断处理程序,把别人赶出来去执行你的中断请求,因此这个时候就不含子优先级了,大概就是这个意思;然后我们看第二种模式:三位是优先级组,一位是子优先级,意味着有8种优先级组,然后每个优先级组中含两个子优先级,换句话说就是这8种优先级组之间可以相互打断或者说彼此抢占,但是细分到每一种优先级组中的子优先级之间,也就相当于VIP中P之间就不能够打断,但是可以通过设置不同响应优先级来调配进而实现提前优先响应;后面几种情况我们就能以此类推是什么意思了。

       当然了,这个关于底层调库实现修改优先级值的实现原理也不是很重要,因为我们在实际的编程写代码过程中,,我们不会单独去详细配置这些寄存器,而是直接去调用NVIC提供的库函数,也就是我们标准库中内核提供的对应库函数就可以了。然后我们只需要手动设置分组模式就行了,一般就是选择第一种分组模式,全部设成组优先级,也就是抢占优先级就即可,这样更方便。因此,我们之后写代码直接调用NVIC提供的库函数,然后设置优先级组和子优先级的分配方案,设成第一种分组模式011也就是3就行了,接着载着你对不同的中断请求设置一个不同的具体的优先级就行了,这个优先级就相当于一个抢占优先级了。

1.6 EXTI外部中断控制器

       前面我们讲述了STM32的中断体系架构中核心部件内核的“大总管”,也就是NVIC的详细介绍,现在呢我们继续介绍中断体系架构中涉及到的另一个额外的比较重要的部件——EXTI外部中断控制器(external interrupt)

        前面NVIC因为离内核很近,甚至可以看作是内核的一部分,所以对于NVIC的调用我们直接调用内核的库函数就行了。现在讲的EXTI就会偏外部一点,也是我们需要配置的,我们先来能看如下框图,来自STM32F103xxx芯片参考手册中9.2.2的关于外部中断控制器的电路框图

       现在我们来详细理解一下这个框图:首先当我们初次见这种电路框图时觉得很懵的话,不妨先找到引脚线部分,因为这里一般是整个线路的入口部分,找到了引脚口位置,我们就可以顺势把线路往里看了。

       好,我们发现,图中最右侧边上有一个输入线,这就是整个电路的输入口,信号进入外部中断控制器都会经过这个引脚向GPIO端口输入线进入;然后引脚左侧就是STM32中EXTI的组成部分了,同时呢,这个GPIO输入线不仅起到GPIO的功能,还起到了复用的功能,要我我们要做7合1的转换的对应关系,显然AFIO复用功能也要打开。然后进入后就会开始一个边沿检测,我们会发现这上面连着很多个寄存器,所以先来一个一个看一下吧

首先,这些都是中断寄存器

     【下降沿触发选择寄存器】&【上升沿触发选择寄存器】:见名知意,就相当于咱给他做配置以后,下面的边沿检测电路就会去检测下降沿或者上升沿的信号,因此·,这俩寄存器相当于一个对边沿检测电路的控制寄存器,去控制边沿检测电路检测的信号;

     【软件中断事件寄存器】:软件触发中断,这本质上是一种可选的另外的中断源,因为我们现在本身都是外部中断,那正常情况下都是硬件这边有连线,然后有信号来了之后检测到相应的变化,然后才能做终端处理,如果我们想做一些调试或者模拟这种中断的状态,这里就相当于提供了一个额外的配置,我们可以用软件来模拟中断的触发。所以这条线路上利用了一个或门(有1则1,全0则0),使得我们既能够通过边沿检测来触发相应的中断,也能由软件来产生一个中断,只需要我们给软件中断寄存器写个1就能实现了;

     【请求挂起寄存器】:是否产生中断,主要是用来判断是否产生了中断,我们观察框图会发现,无论是边沿检测的,还是软件发出的中断请求后面都会连到这个请求挂起寄存器上,所以他其实是一个状态寄存器,并不是我主动要去配置和控制的,而是当中断来了之后,这个中断请求的信号就会改变这个寄存器的值,使得中断请求处于默认的挂起状态,换句话说,中断信号来了以后,就会将这个寄存器的值置1;

     【中断屏蔽寄存器】:是否屏蔽中断,就是我们中断挂起以后,并不一定马上就会交给NVIC去处理,还可能我们会直接屏蔽这个中断,直接不选择接收这个中断,换句话说,我们如果不想把这个中断给到NVIC,不要这个中带你请求,则给中断屏蔽寄存器置0就行,相反,正常情况下我们需要把中断交给NVIC的话,直接给这个寄存器置1就可以了。因此我们会发现,这里与NVIC中断控制器用了一个与门(全1为1,有0则0)将中断屏蔽寄存器和请求挂起寄存器同时连上了,意味着需要两个寄存器同时置1才能把中断交给NVIC去处理,换句话说,只有挂起状态的中断请求不被屏蔽的情况下才能送给NVIC大总管手中;

       在一条寄存器的下方还有一个时间屏蔽寄存器,【事件屏蔽寄存器】:这个寄存器就和我们上面这排中断寄存器有点不一样了。这是对于突发情况的另外一种处理方式,上面那些处理方式叫做终端处理方式,而STM32还提供了一种对于突发状况的处理方式——事件处理方式,基本是差不多,主要区别在于我们产生信号(无论是硬件产生还是软件产生)以后,他就不会再让他先挂起,而是直接在这里看有没有被屏蔽,如果没有被屏蔽的话,就直接给到脉冲发生器,这时候他就不走NVIC那条线路进行处理了,换句话说,我这种情况是有特定的处理流程的,而不需要去麻烦大总管NVIC进行处理了。也就是说,这里脉冲发生器会捕获我们当前的事件或者信号,然后直接走硬件电路把后面所有的事情都摆平完成了。

       相对应而言,我这种方式会比前面经过一系列处理后交给NVIC去进行处理效率会高很多,因为这里是直接交给硬件处理的,而我们前面的方式将中断给到NVIC然后交给内核进行处理还需要执行中断服务程序,这个过程本质上还是软件处理的,所以效率相对就会低一些。当然了,这种通过硬件直接处理的只是一些特非常特殊的事件或者信号,那必然会需要有特定的一些电路来处理这种信号,但是我们不可能对所有事件都去作特定的电路处理,因此,这种硬件直接着手的事件处理方式不适合在大多数场景中使用,这里我们稍作了解就可以了。

      也就是说,我们这里主要还是用的框图中上方的的中断处理方式,也就是使用软件的方式处理这种突发状况。

        好,把框图一些部件和内部进行流程介绍完以后,我们用形象化的图来看看EXTI对于一般的外部中断的处理过程吧

  首先是上升沿检测处理过程:

然后是下降沿处理过程,基本相同

所以这里总结一下,我们对于EXTI需要做的配置主要体现在哪呢?我们还是最后用一张图来表示

        好了,到目前为止呢,我们对于中断的相关概念就基本了解完毕了,还想更加深入了解的朋友可以自己去相关网站或者手册进行探究,我这里就不再叙述。


二、总结

       本次呢,我们主要讲述了关于中断的相关介绍、为什么需要中断以及中断在STM32中的体现,同时针对STM32的中断体系架构我们也做了比较详细的描述,知道了Cortex-M3内核是STM32单片机中断体系架构的核心部件,理解了内核对中断源处理的管理结构,同时对其中的几大重要组件也进行了深入理解,如NVIC(嵌套向量中断控制器)EXTI(外部中断控制器)等做了较为深入的探讨,并学会了如何去对他进行相关的配置。

       后面,我们将继续通过一个案例来实际感受一下中断处理在STM32中的应用。我是一来自神秘小镇的小白,和大家一起学习,共同进步!

       鉴于以上文章篇幅较长已经笔者能力有限,难免出现一些纰漏和不足,望大家在评论区批评指正,谢谢!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值