https://blog.csdn.net/u012864480/article/details/86214026?utm_medium=distribute.pc_relevant_t0.none-task-blog-2%7Edefault%7EBlogCommendFromMachineLearnPai2%7Edefault-1.control&dist_request_id=1328767.11964.16173528597000401&depth_1-utm_source=distribute.pc_relevant_t0.none-task-blog-2%7Edefault%7EBlogCommendFromMachineLearnPai2%7Edefault-1.control
异常类型
原话:
Cortex‐M3 在内核水平上搭载了一个异常响应系统,支持为数众多的系统异常和外部中断。
其中,编号为 1-15 的对应系统异常,大于等于 16 的则全是外部中断。
除了个别异常的优先级被定死外,其它异常的优先级都是可编程的。
所有能打断正常执行流的事件都称为异常。
- 异常的概念包含中断的概念,即中断是异常的子集。
- 异常与中断都是硬件支持的
异常表:
外部中断表:
这里的“外部中断”不是指STM32中的EXTI中断,而是所有中断。
NMI
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)的初始值;
- 复位向量;
- NMI;
- 硬Fault服务例程。
MSP的初始值会在可执行文件链接时由链接文件指定并设置在烧写地址的起始初,TBLOFF和TBLBASE必须在系统启动之后(Reset_Handle中或之后)设置好。
复位序列
当发生复位异常后,CM3执行下面两个操作:
从地址0x0000 0000处取出MSP的初始值;
从地址0x0000 0000处取出PC的初始值,这个值是复位向量的入口地址,然后从这个值所对应的地址处取指。
(STM32可以配置喂从User FLASH、SYSTEM FLASH或者Sram启动。congUser Flash启动时,从User Flash对应的起始地址0x0800 0000处取出MSP,0x08000 0004取出PC的初始值,指向Reset_handler的第一条指令所在地址,然后指向Reset_Handler。从Sram启动时,也是从其对应处取MSP和PC的初始值)。
为什么在运行复位ESR之前要初始化MSP呢?
因为可能第一条指令还没来得及执行,就发生了NMI或其他fault。MSP初始化好后就以及为它们的服务例程准备好了堆栈。
NVIC
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之外,它们可抢占其他所有异常。
如果优先级完全相同的多个异常同时悬起,则先响应异常编号最小的那一个。
优先级分组设置最好在开机初始化时一次性设置好,以后就再也不动它了。
下面是两个优先级分组示例:
- 示例1
- 示例2
可用优先级的具体情况:
中断悬起、活跃状态
- 中断悬起
当中断输入脚被置位有效(assert)后,如果系统正在处理比它优先级更高的异常,或该优先级异常被掩蔽(见BASEPRI、PRIMASK、FAULTMASK寄存器),该中断就被悬起。即使后来中断源撤销了中断请求,已被标记成悬起的中断也不会丢失。直到系统中所有被悬起的中断/异常中,它的优先级最高时,才会得到响应。
但是某个中断得到形影之前,其悬起状态被清除了(如在PRIMASK或FAULTMASK置位时软件清除了悬起状态标识),则中断被取消,不再被响应,如图:
活跃状态
当某中断的服务例程开始执行时,就称此中断进入了“活跃”状态其悬起位会被硬件自动清除。
中断服务例程可以在 执行过程中把自己对应的中断重新悬起,退出此次中断处理后,在合适时机(优先级最高时)再次响应。
下面为中断请求的几种情况及处理:
- 中断请求信号一直保持;
- 在某个中断得到响应前,其发生多次中断请求;
- 中断氢气在其服务例程返回前重置为有效;
NVIC外部中断设置
NVIC的访问地址是0xE000 E000.除软件触发中断寄存器可以在用户级下访问以产生软件中断外,所有NVIC的中断控制/状态寄存器都只能在特权级下访问。所有中断控制/状态寄存器均可按字/半字/字节的方式访问。
中断配置基础
每个外部中断在NVIC中都有下列寄存器:
- 使能与除能寄存器
- 悬起与解悬寄存器
- 优先级寄存器(即上面所说的优先级配置寄存器)
- 活动状态寄存器
- 另外,下列寄存器也对中断处理有重大影响:
- 异常掩蔽寄存器(PRIMASK, FAULTMASK, BASEPRI)
- 向量表偏移量寄存器(即VTOR)
- 软件触发中断寄存器
- 优先级分组位段(如上所述的AIRCR:PRIGROUP)
中断的使能与除能
CM3中有240对使能位/除能位(SETENA位/CLRENA位),每个外部中断对应一对。欲使能一个中断,写1到对应的SETENA的位中;欲除能一个中断,写1到对应的CLRENA位中。如果往它们中写0,不会有任何效果。
中断的悬起与解悬
中断的悬起状态可以通过“中断设置悬起寄存器(SETPEND)”和“中断悬起清除寄存器(CLRPEND)”来读取,SETPEND置位表示对应外部中断处于悬起状态,CLRPEND置位表示对应外部中断处于解悬状态。这两个寄存器一般由硬件自动设置,也可以手工设置SETPEND来悬起中断。
优先级寄存器
活动状态
每个外部中断都有一个活动状态位。在处理器执行了其ISR的第一条指令后,它的活动位就被置1,并且直到ISR返回时才硬件清零。如果中断被抢占,其活动状态也依然为1(与SETPEND不同,只要中断被抢占,SETPEND就会被硬件置1). 活动状态位为0时表示中断还未被响应或是中断未触发。
### 特殊功能寄存器PRIMASK、FAULTMASK
PRIMASK置1时,通过把当前优先级改为0(可编程优先级中的最高优先级),来除能NMI和硬fault之外的所有异常。通过PRIMASK来实现开中断、关中断功能。
FAULTMASK把当前优先级改为-1,除能NMI之外的所有异常。FAULTMASK会在异常退出时自动清零。
PRIMASK和FAULTMASK寄存器通过MRS/MSR方式或CPS指令访问。
BASEPRI寄存器
如果想只掩蔽优先级低于某一阈值的中断——它们的优先级在数字上大于等于某个数,可以将此优先级数值存储在BASEPRI中。如果BASEPRI为0,则不掩蔽任何中断。
BASEPRI寄存器还有另外一个名字:BASEPRI_MAX。使用此名时,只允许设置数值上比原来更小的优先级阈值,也就是说,只能一次次扩大掩蔽范围,反之则不行。BASEPRI则无此限制。
其他异常的配置寄存器
用户fault,总线fault以及存储器管理fault,它们的使能控制是通过“系统Handler控制及状态寄存器(SHCSR)”(地址:0xE0000_ED24)来实现的。各种faults的悬起状态和大多数系统异常的活动状态也都在该寄存器中。
对于NMI、SYSTICK定时器以及PendSV,可以通过中断控制及状态寄存器ICSR来手工悬起它们。
软件中断
软件中断,即通过软件设置产生而非外部中断源产生的普通中断。可以通过设置相应的SETPEND寄存器,或,通过使用软件触发中断寄存器STIR实现。
SysTick定时器
SysTick定时器被捆绑在NVIC中,用于产生SysTick异常,即周期性的“滴答”中断,可用来作为整个系统的时基。
SysTick定时器的时钟源可以是内部时钟(FCLK),或者是外部时钟(STCLK)。STCLK的具体来源由芯片产生决定。
SysTick相关寄存器:
STM32F10X中断系统
特性
STM32 F10x系列在ARM Cortex-M3基础上,定义了:
- 外部中断类型及数量:Connectivity line devices支持68个外部中断,XL-density devices及L/M/H-density devices支持60个外部中断。(CM3支持240个外部中断)
- 使用4个优先级设置位,共支持16级可编程优先级。
SysTick校准值寄存器
SysTick的校准值为9000,即SysTick的时钟设为9MHz时(max HCLK/8),1ms的计数。
外部中断/事件控制器(EXTI)
在STM32F10x的外部中断中,除了片上外设如WWDG、USART使用的中断号外,还有些中断号供外部输入的中断线使用,如某些GPIO口可设置为中断,这些中断号称为外部中断EXTI。
(注:此处的外部中断要区别于CM3中的外部中断,CM3中的外部中断指的是系统异常外的中断。此处的外部中断指的是STM32片外中断输入线输入的中断)。
互联产品有20个外部输入中断,其他产品有19个。所有外部中断由外部中断/事件控制器控制,每个中断输入线可单独配置类型(事件或中断)、触发事件(上升沿/下降沿/双沿)。
EXIT控制器框图
由上图知,输入线配置为中断时:
- 通过Falling/Rising trigger selection register可配置输入线的触发方式(上升沿/下降沿/双沿);
- 通过Software interrupt event register可产生软件触发。(见CM3的软件中断);
- 中断产生时,置位相应pending位。通过往pending reg写1,将清除对应的中断请求;
- 通过Interrupt mask register可屏蔽此中断,不让中断事件产生pending位;
- Pending request register记录断,与CM3的NVIC连接,以通知NVIC产生的中断事件。
(注:EXTI只是在NVIC之外再增加了一层控制逻辑,CM3核上还有NVIC自己的控制逻辑,如pending寄存器等。所以,配置EXTI时,除了配置EXTI寄存器还,还应配置NVIC对应的外部中断寄存器。)
输入线配置为事件时:
- 其他功能与配置为中断一样。但事件产生时不会置位pending位;
- 最终会通过Pulse generator产生一个脉冲;
- 如连接到EXTI的PVD中断,EXTI配置为事件,PVD内部与某根InputLine的Pulse generator连接,InputLine再与外部电路连接,当外部电路产生中断时,Pulse generator产生一个脉冲,再产生PVD中断。
外部中断/事件线路映像
112个GPIO口连接到16根外部中断/事件信号线,如下:
另外三根EXTI线连接到:
- EXTI line 16 is connected to the PVD output
- EXTI line 17 is connected to the RTC Alarm event
- EXTI line 18 is connected to the USB Wakeup event
配置外设的NVIC中断
以配置USART中断为例。
- 配置NVIC
主要配置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);