编号0 ~ 15中,除去编号为0和7 ~ 10这5个未用的异常编号,共10个系统异常。
编号16 ~ 255,共240个外部中断,简称IRQ。外部中断的编号为IRQ#0 ~ IRQ#239。
由于CM3的硬件描述代码(HDL)是可以被修改的,使用CM3核的芯片厂商可以精简外部中断的数量,如STM32 F10x根据芯片类型支持的外部中断数为60/68。
NonMaskable Interrupt,不可屏蔽中断,通过NVIC连接一条NMI输入信号线,用于紧急事件的触发,其优先级在最高优先级(-3)之后,系统启动后的任何时间都可触发。
异常向量表是一个WORD数组,数组编号为异常编号,WORD元素的值为对应的异常服务例程(ESR)的入口地址。
当一个异常发生后,硬件会根据异常编号计算出在向量表中的偏移量,然后取出ESR的入口地址,并跳转执行。
向量表在地址空间中的位置是可以设置的(0xE000ED08 向量表偏移量寄存器VTOR),如果向量表保存在ROM/Flash中,通常是在ROM/Flash的起始处保存。如果在RAM中调试系统,向量表一般保存在RAM中,VTOR要保存向量表在RAM中的起始地址。
向量表的起始地址是有对齐限制的,具体见《cortex-M3权威指南 Chapter7.3》。
TBLOFF设置异常向量表(0号异常:MSP初始值)相对起始地址(FLASH or SRAM的起始地址)的偏移。
如果需要动态的更改向量表,则对于任何器件来说,向量表的起始初都包含一下向量:
MSP的初始值会在可执行文件链接时由链接文件指定并设置在烧写地址的起始初,TBLOFF和TBLBASE必须在系统启动之后(Reset_Handle中或之后)设置好。
当发生复位异常后,CM3执行下面两个操作:
Nested Vectored Interrupt Controller,嵌套向量中断控制器。有其名可知,NVIC支持优先级、中断抢占(中断嵌套),还可在中断服务程序运行时动态更改中断的优先级而不必担心重入。
由CM3核内外设和核外外设产生的中断,除SysTick之外,全部连接到NVIC的中断输入信号线,包括NMI。
CM3中,优先级的数值越小,则优先级越高。高优先级的异常会抢占低优先级异常,即中断会发生嵌套。
有3个系统异常:复位、NMI和硬件fault的优先级是固定的,且为负值,高于其他所有异常。其他所有异常的优先级都可以设置。
每个异常的优先级都可以通过其优先级配置寄存器来设置。CM3的优先级配置寄存器有8位,也即可以支持256级优先级。但实际上芯片厂商只会使用高几位,如STM32 F10xx的异常优先级寄存器只使用了高4位,如此只能支持16种优先级。CM3允许的最少使用位数为3个位,亦即至少要支持8级优先级。如下:
(注:往优先级配置寄存器写入设置值时,要写整个字节的值,而不只是使用到的高几位的值。如上图的优先级值分为为:0x00,0x20,0x40,0x60,0x80,0xA0,0xC0,0xE0.)
所有优先级配置寄存器按功能还可以一分为二,从分割处往左的MSB部分,表示抢占优先级,往右的LSB部分,表示子优先级。
抢占优先级,也称组优先级/主优先级,决定了抢占行为:如果一个异常H的抢占优先级高于正在被响应的异常L的抢占优先级(H的抢占优先级数值小于L),则L会被H抢占,暂停执行L的ESR,开始执行H的ESR。
子优先级处理“内务”,当抢占优先级相同的异常有不止一个悬起时(等待响应),优先响应子优先级最高的异常(数值最小)。抢占优先级相同的子优先级异常之间不能抢占。
抢占和子优先级的配置通过应用程序中断及复位控制寄存器(AIRCR)的PRIGROUP位段来控制,如下图:
(注:优先级分组可影响所有优先级可设置的异常的中断优先级设置寄存器,其值描述的是中断优先级设置寄存器的所有bits的分组,而非芯片厂商启用的bits的分组。)
CM3规定,子优先级至少是1个位,因此抢占优先级最多是7个位,即最多只有128级抢占。
当所有的位都是子优先级时,不能发生抢占的情况,不同优先级异常间只有是否优先响应的区别。当然,除了复位、NMI和硬件fault之外,它们可抢占其他所有异常。
如果优先级完全相同的多个异常同时悬起,则先响应异常编号最小的那一个。
优先级分组设置最好在开机初始化时一次性设置好,以后就再也不动它了。
下面是两个优先级分组示例:
当某中断的服务例程开始执行时,就称此中断进入了“活跃”状态其悬起位会被硬件自动清除。
中断服务例程可以在 执行过程中把自己对应的中断重新悬起,退出此次中断处理后,在合适时机(优先级最高时)再次响应。
下面为中断请求的几种情况及处理:
NVIC的访问地址是0xE000 E000.除软件触发中断寄存器可以在用户级下访问以产生软件中断外,所有NVIC的中断控制/状态寄存器都只能在特权级下访问。所有中断控制/状态寄存器均可按字/半字/字节的方式访问。
每个外部中断在NVIC中都有下列寄存器:
CM3中有240对使能位/除能位(SETENA位/CLRENA位),每个外部中断对应一对。欲使能一个中断,写1到对应的SETENA的位中;欲除能一个中断,写1到对应的CLRENA位中。如果往它们中写0,不会有任何效果。
中断的悬起状态可以通过“中断设置悬起寄存器(SETPEND)”和“中断悬起清除寄存器(CLRPEND)”来读取,SETPEND置位表示对应外部中断处于悬起状态,CLRPEND置位表示对应外部中断处于解悬状态。这两个寄存器一般由硬件自动设置,也可以手工设置SETPEND来悬起中断。
每个外部中断都有一个活动状态位。在处理器执行了其ISR的第一条指令后,它的活动位就被置1,并且直到ISR返回时才硬件清零。如果中断被抢占,其活动状态也依然为1(与SETPEND不同,只要中断被抢占,SETPEND就会被硬件置1). 活动状态位为0时表示中断还未被响应或是中断未触发。
名称 | 类型 | 地址 | 复位值 | 描述 |
ACTIVE0 | R0 | 0xE000 E300 | 0 | 中断 0-31的活动状态寄存器,共32个状态位 位[n]中断#n活动状态(异常号16+n) |
ACTIVE1 | R0 | 0xE000 E304 | 0 | 中断32-63的活动状态寄存器,共32个状态位 |
…… | …… | …… | …… | …… |
ACTIVE1 | R0 | 0xE000 E31C | 0 | 中断224-239的活动状态寄存器,共16个状态位 |
PRIMASK置1时,通过把当前优先级改为0(可编程优先级中的最高优先级),来除能NMI和硬fault之外的所有异常。通过PRIMASK来实现开中断、关中断功能。
FAULTMASK把当前优先级改为-1,除能NMI之外的所有异常。FAULTMASK会在异常退出时自动清零。
PRIMASK和FAULTMASK寄存器通过MRS/MSR方式或CPS指令访问。
如果想只掩蔽优先级低于某一阈值的中断——它们的优先级在数字上大于等于某个数,可以将此优先级数值存储在BASEPRI中。如果BASEPRI为0,则不掩蔽任何中断。
BASEPRI寄存器还有另外一个名字:BASEPRI_MAX。使用此名时,只允许设置数值上比原来更小的优先级阈值,也就是说,只能一次次扩大掩蔽范围,反之则不行。BASEPRI则无此限制。
用户fault,总线fault以及存储器管理fault,它们的使能控制是通过“系统Handler控制及状态寄存器(SHCSR)”(地址:0xE0000_ED24)来实现的。各种faults的悬起状态和大多数系统异常的活动状态也都在该寄存器中。
对于NMI、SYSTICK定时器以及PendSV,可以通过中断控制及状态寄存器ICSR来手工悬起它们。
软件中断,即通过软件设置产生而非外部中断源产生的普通中断。可以通过设置相应的SETPEND寄存器,或,通过使用软件触发中断寄存器STIR实现。
SysTick定时器被捆绑在NVIC中,用于产生SysTick异常,即周期性的“滴答”中断,可用来作为整个系统的时基。
SysTick定时器的时钟源可以是内部时钟(FCLK),或者是外部时钟(STCLK)。STCLK的具体来源由芯片产生决定。
SysTick相关寄存器:
STM32 F10x系列在ARM Cortex-M3基础上,定义了:
SysTick的校准值为9000,即SysTick的时钟设为9MHz时(max HCLK/8),1ms的计数。
在STM32F10x的外部中断中,除了片上外设如WWDG、USART使用的中断号外,还有些中断号供外部输入的中断线使用,如某些GPIO口可设置为中断,这些中断号称为外部中断EXTI。
(注:此处的外部中断要区别于CM3中的外部中断,CM3中的外部中断指的是系统异常外的中断。此处的外部中断指的是STM32片外中断输入线输入的中断)。
互联产品有20个外部输入中断,其他产品有19个。所有外部中断由外部中断/事件控制器控制,每个中断输入线可单独配置类型(事件或中断)、触发事件(上升沿/下降沿/双沿)。
(注:EXTI只是在NVIC之外再增加了一层控制逻辑,CM3核上还有NVIC自己的控制逻辑,如pending寄存器等。所以,配置EXTI时,除了配置EXTI寄存器还,还应配置NVIC对应的外部中断寄存器。)
输入线配置为事件时:
112个GPIO口连接到16根外部中断/事件信号线,如下:
PAnPGn(n=015) 7个IO口中,同时只能一个连接到EXITn.
另外三根EXTI线连接到:
以配置USART中断为例。
主要配置USART在NVIC的优先级寄存器、使能寄存器:
NVIC_InitStruct.NVIC_IRQChannel = USART2_IRQn;
NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority = 0;
NVIC_InitStruct.NVIC_IRQChannelSubPriority = 0;
NVIC_InitStruct.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStruct);
NVIC_IRQChannelPreemptionPriority表示抢占优先级,如果STM32的4位优先级位都配置为抢占优先级,其取值为0~15, 0最高。
NVIC_IRQChannelSubPriority表示子优先级,如果STM32的4位优先级位全部表示子优先级,其取值为0~15, 0最高。
优先级分组位AIRCR:PRIGROUP默认值为0,对于使用高4位优先级位的STM32来说,这4位全部表示抢占优先级。如果要设置子优先级,使用函数(未设置的情况下AIRCR:PRIGROUP默认值为0):NVIC_PriorityGroupConfig(misc.c)
STM32F4x 系列也一样。
外设自身有多个中断源,这些中断源都会产生NVIC中对应的外设IRQn中断。配置外设自身中断源,就是开启哪些、关闭哪些。如USART:
/* configure usart interrupt, 一般使能发送数据寄存器为空中断和
接收数据寄存器不为空中断. */
USART_ITConfig(USART2, USART_IT_TXE|USART_IT_RXNE, ENABLE);
Reference