第七章:异常和中断(Exceptions and Interrupts)

     在几乎所有的微控制器中,中断都是一个非常普遍的功能特性。中断是由硬件所产生的事件,这些事件会改变用户自己所规定的软件控制流的变化。当硬件或外设通过中断向处理器请求的服务的时候,一般会走以下流程:

  1. 硬件或外设通过中断向处理器发出中断请求。
  2. 处理器暂停当前所执行的任务。
  3. 处理器通过执行中断服务子程序, I n t e r r u p t S e r v i c e R o u t i n e , ( I S R ) Interrupt\quad Service\quad Routine, (ISR) InterruptServiceRoutine,(ISR),来完成硬件或外设的服务需求。
  4. 处理器回到之前被打断的程序逻辑处继续执行。

     所有的 C o r t e x − M Cortex-M CortexM系列处理器都提供了一个嵌套向量中断控制器, N e s t e d V e c t o r e d I n t e r r u p t C o n t r o l l e r , N V I C Nested\quad Vectored\quad Interrupt\quad Controller, NVIC NestedVectoredInterruptController,NVIC,来进行中断处理,如图1所示。除了正常的由外设或 G P I O GPIO GPIO引脚产生的中断外,还有一些比较特殊的中断,这里可以叫做异常( E x c e p t i o n Exception Exception),它们包括了故障异常和系统异常,它们主要是用来支持操作系统的,最直观的就是我们这里打开 S T M 32 F 103 STM32\quad F103 STM32F103的启动文件 s t a r t u p _ s t m 32 f 10 x _ h d . s startup\_stm32f10x\_hd.s startup_stm32f10x_hd.s,图2中所对应的就是这些特殊的中断了。

 
图1.
 
图2.

      C o r t e x − M 3 Cortex-M3 CortexM3系列和 C o r t e x − M 4 Cortex-M4 CortexM4系列处理器支持最多240个普通中断(那些特别的除外),以及一个非屏蔽中断, N o n − M a s k a b l e I n t e r r u p t , N M I Non-Maskable\quad Interrupt ,NMI NonMaskableInterrupt,NMI,一个系统滴答定时器中断, S y s t e m T i c k t i m e r i n t e r r u p t System\quad Tick\quad timer\quad interrupt SystemTicktimerinterrupt,和一些系统异常。大部分中断由外设, G P I O GPIO GPIO口以及通信接口产生。非屏蔽中断通常由看门狗定时器或 B r o w n − O u t D e t e c t o r , B O D Brown-Out\quad Detector,BOD BrownOutDetector,BOD产生。其它的异常由处理器内核产生。这些普通的中断和那些特别的异常都是有编号的,那些特别的异常的编号一般都是固定的,1到15,至于那些一般的中断,芯片设计生产上可以根据自己的需求来编排,16到255,所有的这些普通中断和部分的特别的异常的中断优先级是可以配置的,如图3和图4所示,这里的编号对应图3和图4中的第一列。每当我们进入中断函数之后 x P S R xPSR xPSR寄存器的最低9位的值就是图3和图4所示的第一列对应中断或异常的编号。但是当我们在使用 C M S I S CMSIS CMSIS N V I C NVIC NVIC驱动去配置相应的中断的时候,确定具体是哪个中断就不是采用图3和图4所示的第一列对应中断或异常的编号,而是第二列的编号,对于图4中的一般中断,编号从0开始到239,对于那些特别的异常,编号从-15到-1,这样做是为了驱动编写的方便。如图5所示。

 
图3.
 
图4.
 
图5.

     上面说到 C o r t e x − M 3 Cortex-M3 CortexM3系列和 C o r t e x − M 4 Cortex-M4 CortexM4系列处理器如果将那些特殊的异常包括在内最多可以支持到255个中断。那么如果多个中断同时发生或者一个中断正在执行而另一个中断请求又已经触发,那在这种情况下又该怎么处理,这就涉及到中断优先级的概念了。针对我们前面提到的差不多255的中断,对应于每一个中断都有一个中断优先级寄存器,除开 R e s e t Reset Reset N M I NMI NMI H a r d F a u l t Hard\quad Fault HardFault这三个有着固定优先级的异常除外,如图3所示。那些特殊的异常的中断优先级的寄存器位于 S C B SCB SCB模块的 S H P SHP SHP寄存器(这里有12个,因为已经有三个有固定的优先级),其它普通的中断的优先级寄存器位于 N V I C NVIC NVIC模块的 I P IP IP寄存器(这里有240个)。中断优先级寄存器里面的值越小,就表示这个寄存器对应的中断的优先级越高。每一个中断优先级寄存器都是8位的,但是实际的芯片制造商会根据自己的项目需求,就是项目中中断的个数来选择实际使用这个8位寄存器中的那几位,这里优先使用高位,如果止用到了8个优先级的话,那么就只使用第5,6,7个比特位就可以了,第0,1,2,3,4,5个比特位就可以不使用。优先使用高位的原因也是为了方便驱动在不同平台的移植。中断优先级寄存器最少使用3位,最多使用8位,优先级水平设计的较多的话也会增加芯片的功耗,以及设计的复杂性和研发周期。
     针对8位的优先级寄存器,又被分为了两部分,一部分叫做抢占优先级,一部分叫做子优先级,至于这两部分的大小可以通过 S C B SCB SCB模块的 A I R C R AIRCR AIRCR寄存器的 P R I G R O U P PRIGROUP PRIGROUP比特域来进行配置。 P R I G R O U P PRIGROUP PRIGROUP比特域的值的含义如图6所示。从图6中可以看到8位的优先级寄存器中的抢占优先级的比特域最多可以分配7个比特位,也就是128个抢占优先级。根据抢占优先级和子优先级的不同,我们可以分一下几种情况来说明一下两个中断的先后执行顺序:

  • 中断A和中断B同时到来:如果中断A和中断B的抢占优先级相同,则子优先级高的中断先执行。如果中断A和中断B的抢占优先级相同,子优先级也相同,则中断A和中断B中中断编号小的那个中断先执行。如果如果中断A和中断B的抢占优先级不同,那肯定是抢占优先级高的那个中断先执行。
  • 中断A先到中断B后到:如果中断A和中断B的抢占优先级相同,这时这两个中断就没有嵌套的关系,这里也就不用去关心子优先级了,就算此时中断B的子优先级高于中断A的子有年纪,中断B也要等到中断A执行完之后才能执行。如果中断B的抢占优先级高于中断A的抢占优先级,此时处理器会停止中断A的执行转而执行中断B,等到中断B执行完之后再来执行中断A。
 
图6.

     如图6所示,当我们把 S C B SCB SCB模块的 A I R C R AIRCR AIRCR寄存器的 P R I G R O U P PRIGROUP PRIGROUP比特域设置为7的时候,这时相当于没有为抢占优先级分配比特位了。这时就不存在抢占了,也就是一个后来的中断不能抢占一个已经正在执行的中断,当然前面介绍的那几个已经有固定优先级的异常还是可以抢占的。

/*----------------------------------------------------------------------中断向量表以及重映射---------------------------------------------------------------------*/

     当 C o r t e x − M Cortex-M CortexM系列处理器接收到中断或异常请求的时候会根据中断或异常的类型进入相应的中断或异常处理函数中,那处理器是如何能够准确的进入对应的中断函数,即如何知道相应的中断或异常处理函数的起始地址的,这就要归功于中断向量表,如图7所示。或者更直观一点,看一看图2,即启动文件。中断向量表的每一个元素都对应一个中断或异常处理函数的起始地址,每一个元素占用4个字节,我们前面提到每一个中断或异常都是有对应编号的,这个编号就是对应的中断或异常的中断或异常处理函数的起始地址在中断向量表中的索引。比如从图3中我们可以知道 S Y S T I C K SYSTICK SYSTICK这个中断的编号为15(编号从0开始,第0个元素存放的是堆栈指针的初始值),那么从中断向量开始存放的地址开始的第15个字里面的内容就是 S Y S T I C K SYSTICK SYSTICK这个中断的中断处理函数的起始地址。默认情况下中断向量表从地址0开始存放,用于启动的程序的中断向量表的第一个元素是主堆栈指针, M a i n S t a c k p o i n t e r Main \quad Stack\quad pointer MainStackpointer,的初始值,这是因为某些异常,比如 N M I NMI NMI,可以发生在处理器离开复位但是还没有进入任何初始化之前的操作这段时间。

 
图7.

     一般情况下中断向量表的存放起始地址0,位于启动存储区域(一般在 C o r t e x − M Cortex-M CortexM系列处理器中可能有内部 F L A S H FLASH FLASH,起始地址位0x08000000,启动, S R A M SRAM SRAM,起始地址位0x20000000,启动和 B O O T BOOT BOOT区域启动,在这几种启动方式下硬件应该会自动的将0地址映射为各种启动方式下的代码存放的开始地址),这部分代码的内容一般在运行时是不可以修改的。为了满足可以在运行时修改中断向量表的要求(注意,这里的修改不是实际的去修改之前存放到启动开始地址处的中断向量表。这里我们可以举一个简单的例子,如图8所示,这里我们在图8中的 M a i n F l a s h Main\quad Flash MainFlash空间下载了两个工程的代码,每个工程都有自己的中断向量表以及中断处理函数且中断向量表都分别从各自代码空间的起始地址开始存放,如图7中的绿色和橙色区域所示。假设现在启动方式为从 M a i n F l a s h Main\quad Flash MainFlash启动,程序上电复位之后会执行图8中绿色区域的代码,这时产生的中断会从绿色区域的起始地址去定位对应的中断函数在绿色区域中的地址的位置,此时在绿色区域的代码中我们可以修改 V T O R VTOR VTOR寄存器的值为 0 x 08005 C 00 0x08005C00 0x08005C00,之后我们可以用指针的方式跳转到橙色区域的工程里面的复位处理函数,并将堆栈指针设为地址 0 x 08005 C 00 0x08005C00 0x08005C00开始的一个字的值,这样就可以开始运行橙色区域的工程了,这时产生的中断会从橙色区域的起始地址去定位对应的中断函数在橙色区域中的地址的位置,当然通过修改 V T O R VTOR VTOR寄存器的值为 0 x 08000000 0x08000000 0x08000000,之后我们可以用指针的方式跳转到绿色区域的工程里面的复位处理函数,并将堆栈指针设为地址 0 x 08000000 0x08000000 0x08000000开始的一个字的值,这样就可以重新回到绿色区域的工程了开始执行了。所以这里的修改相当于重新建立了一套中断向量表以及对应的中断函数), C o r t e x − M 3 Cortex-M3 CortexM3 C o r t e x − M 4 Cortex-M4 CortexM4系列处理器在 S C B SCB SCB模块中提供了特殊的寄存器 V T O R , V e c t o r T a b l e O f f s e t R e g i s t e r VTOR,Vector\quad Table\quad Offset\quad Register VTOR,VectorTableOffsetRegister,这个寄存器定义了当前的程序在有中断触发的时候寻找中断处理函数起始地址的中断向量的起始地址。

 
图8.

      V T O R VTOR VTOR寄存器在 C o r t e x − M 3 Cortex-M3 CortexM3的较老版本和 C o r t e x − M 3 Cortex-M3 CortexM3的新版本, C o r t e x − M 4 Cortex-M4 CortexM4上有区别,如图9所示。 C o r t e x − M 3 Cortex-M3 CortexM3的较老版本上的 V T O R VTOR VTOR寄存器只有30个比特位可用,因此只能重映射到代码区域和 S R A M SRAM SRAM区域, C o r t e x − M 3 Cortex-M3 CortexM3的新版本, C o r t e x − M 4 Cortex-M4 CortexM4上没有这个限制。

 
图9.

      V T O R VTOR VTOR寄存器存储的是中断向量表的起始地址,对于这个起始地址也是有要求的,这个起始地址必须是大于等于中断向量表所占用的内存空间的字节数的最近的一个2的幂的数的倍数(可以时0倍,也就是起始地址从0开始)。下面我们举一个例子,假设现在中断向量表中有32个普通中断和16个异常中断(其中一个用来存储堆栈指针的初始值),那么中断向量表所占用的内存空间为 ( 16 + 32 ) × 4 = 192 (16+32)\times 4=192 (16+32)×4=192,又因为 2 7 = 128 , 2 8 = 256 2^7=128,2^8=256 27=128,28=256,因此起始地址必须是256的倍数,因此起始地址可以设置为 0 x 00000000 , 0 x 00000100 , 0 x 00000200 0x00000000,0x00000100, 0x00000200 0x00000000,0x00000100,0x00000200等。

     下面我们来讲一下 V T O R VTOR VTOR寄存器的典型应用:

  1. B O O T BOOT BOOT区域启动之后跳转到 U s e r F l a s h User\quad Flash UserFlash执行,如图10所示。假设当前系统已被配置为从 B O O T BOOT BOOT区域启动,启动并完成相应的任务之后修改 V T O R VTOR VTOR寄存器的值为 U s e r F l a s h User\quad Flash UserFlash代码区域的起始地址,然后通过指针跳转到 U s e r F l a s h User\quad Flash UserFlash代码区域的 r e s e t h a n d l e r reset\quad handler resethandler来开启 U s e r F l a s h User\quad Flash UserFlash区域代码的执行。
  2. U s e r F l a s h User\quad Flash UserFlash启动之后,接收网络或外部 S D SD SD卡的工程代码并放到 S R A M SRAM SRAM区域,然后跳转到对应的 S R A M SRAM SRAM区域执行接收的这个工程的代码。如图11所示。
 
图10.
 
图11.
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

qqssss121dfd

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值