目录
一、前言
二、寄存器概述
三、NVIC寄存器组
四、SCB寄存器组
五、中断屏蔽寄存器组
六、总结
一、前言
在之前的STM32的中断系统理论基础知识之基本原理及NVIC中,分别中断的基本原理,中断的管理机制和中断的处理流程进行了较为详细的论述,读者通过全篇的阅读了解可以整体上对以围绕NVIC为管理核心的STM32的中断系统有一个初步的了解,明白中断的一些基本概念以及STM32中断系统的一个大致的工作流程。
这一篇主要对中断系统相关的寄存器进行相应的分析介绍,适当了解中断系统寄存器的相关介绍,有助于加深对STM32内核的中断及异常的认识,明白中断时的功能都是由哪些寄存器负责完成实现的,同时对后续介绍到的中断相关配置及API函数的使用有很好的帮助。
二、寄存器概述
图1为STM32中断系统寄存器的概述,总共可以分为3组不同的寄存器组。各个寄存器组下分别又有不同用于中断控制的寄存器。它们都属于STM32内核上的寄存器。
(1)、NVIC作为内嵌向量中断控制器,控制着整个芯片中断相关的功能,它跟内核紧密耦合,是内核里面的一个外设。但是各个芯片厂商在设计芯片的时候会对 Cortex-M3 内核里面的 NVIC 进行裁剪,把不需要的部分去掉,所以说 STM32的 NVIC 是Cortex-M3的 NVIC 的一个子集。它用于总体管理异常和中断,因此提供了和中断系统紧密相关的控制和状态寄存器;
(2)、SCB为系统控制块,SCB中包含了一些内核系统控制相关的寄存器,同时也包含了关于中断相关的,由于本篇主要介绍中断系统,因此这里就只针对SCB中关于中断控制的寄存器功能进行分析介绍;
(3)、而为了满足一些代码段不允许被中断打断,那么这段代码就必须用关中断的方式给保护起来的场合,STM32提供了中断屏蔽寄存器,我们可以一次性把一堆必须要屏蔽的中断进行屏蔽。
图1 中断系统寄存器概述
三、NVIC寄存器组
NVIC控制器包含一系列寄存器组,STM32为其定义了如下的结构体:
typedef struct
{__IO uint32_t ISER[8]; /*!< Offset: 0x000 (R/W) Interrupt Set Enable Register */uint32_t RESERVED0[24];__IO uint32_t ICER[8]; /*!< Offset: 0x080 (R/W) Interrupt Clear Enable Register */uint32_t RSERVED1[24];__IO uint32_t ISPR[8]; /*!< Offset: 0x100 (R/W) Interrupt Set Pending Register*/uint32_t RESERVED2[24];__IO uint32_t ICPR[8]; /*!< Offset: 0x180 (R/W) Interrupt Clear Pending Register*/uint32_t RESERVED3[24];__IO uint32_t IABR[8]; /*!< Offset: 0x200 (R/W) Interrupt Active bit Register*/uint32_t RESERVED4[56];__IO uint8_t IP[240]; /*!< Offset: 0x300 (R/W) Interrupt Priority Register (8Bit wide) */uint32_t RESERVED5[644];__IO uint32_t STIR; /*!< Offset: 0xE00 ( /W) Software Trigger Interrupt Register*/
} NVIC_Type;
(1)、中断使能寄存器ISER
Interrupt Set-Enable Registers,这是一个中断使能寄存器组。总共有8个uint32类型的寄存器,即ISER[8],上面说了 CM3 内核支持 256 个中断,这里用 8 个32位寄存器来控制,每个位控制一个中断。但是例如STM32F103 的可屏蔽中断只有 60 个,所以对我们来说,有用的就是两个(ISER[0]和 ISER[1]),总共可以表示 64 个中断。而 STM32F103 只用了其中的前 60 位。ISER[0]的 bit0 ~ bit31 分别对应中断 0 ~ 31。ISER[1]的 bit0 ~ 27 对应中断 32~59;这样总共 60 个中断就分别对应上了。你要使能某个中断,必须设置相应的 ISER 位为 1,使该中断被使能(这里仅仅是使能,还要配合中断分组、屏蔽、IO 口映射等设置才算是一个完整的中断设置)。
图2 中断使能和中断除能寄存器组
(2)、中断除能寄存器ICER
Interrupt Clear-Enable Registers,是一个中断除能寄存器组。总共有8个uint32类型的寄存器,即ICER[8],该寄存器组与 ISER 的作用恰好相反,是用来清除某个中断的使能的。其对应位的功能,也和 ICER 一样。这里要专门设置一个 ICER 来清除中断位,而不是向 ISER 写 0 来清除,是因为NVIC 的这些寄存器都是写 1 有效的,写 0 是无效的。
(3)、中断挂起控制寄存器ISPR
Interrupt Set-Pending Registers,是一个中断挂起控制寄存器组。总共有8个uint32类型的寄存器,即ISPR[8],每个位对应的中断和 ISER 是一样的。通过置 1,可以将正在进行的中断挂起,暂时停止执行该中断,而执行同级或更高级别的中断。写 0 是无效的。
图3 中断挂起和中断解挂寄存器组
(4)、中断解挂控制寄存器ICPR
Interrupt Clear-Pending Registers,是一个中断解挂控制寄存器组。总共有8个uint32类型的寄存器,即ICPR[8],其作用与 ISPR 相反,将之前暂时停止执行的中断解挂,对应位也和 ISER 是一样的。通过设置 1,可以将挂起的中断解挂。写 0 无效。
(5)、中断激活标志位寄存器IABR
Interrupt Active Bit Registers,是一个中断激活标志位寄存器组。总共有8个uint32类型的寄存器,即IABR[8],对应位所代表的中断和 ISER 一样,如果为 1,则表示该位所对应的中断正在被执行。这是一个只读寄存器,通过它可以知道当前在执行的中断是哪一个。在中断执行完了由硬件自动清零。如果在中断执行过程中,发生了更高优先级的中断抢占,那么之前被抢占的中断该标志位依然是1。
图4 中断激活标志位寄存器组
(6)、中断优先级控制寄存器IP
Interrupt Priority Registers,是一个中断优先级控制的寄存器组。总共有240个uint8类型的寄存器,即IP[240],这个寄存器组相当重要!STM32 的中断分组与这个寄存器组密切相关。IP 寄存器组由 240 个 8bit 的寄存器组成,每个可屏蔽中断占用 8bit,这样总共可以表示 240 个可屏蔽中断。而 STM32 只用到了其中的前 60 个。IP[59] ~ IP[0]分别对应中断 59 ~ 0。而每个可屏蔽中断占用的 8bit 并没有全部使用,而是只用了高 4 位。这 4 位,又分为抢占优先级和子优先级。抢占优先级在前,子优先级在后。而这两个优先级各占几个位又要根据 SCB->AIRCR 中的中断分组设置来决定。
图5 中断优先级控制寄存器组
(7)、软件触发中断寄存器STIR
这个寄存器是利用软件设置中断编号的方式来触发中断发生,是一个uint32类型的寄存器,有效位为0~8。比如设置NVIC->STIR=3,则会触发中断3,需要注意的是,该寄存器无法触发系统异常如NMI或Systick的。
图6 软件触发中断寄存器
四、SCB寄存器组
系统控制块(SCB)是内核外设的主要模块之一,提供系统控制以及系统执行信息,包括配置,控制,报告系统异常等。而SCB数据结构中也包含了一些常用于中断控制的寄存器。
(1)、中断控制和状态寄存器ICSR
该寄存器的主要作用为:
a. 设置和清除系统异常的挂起状态,异常包括Systick、PendSV、NMI。这个功能和NVIC的中断挂起控制寄存器ISPR和中断解挂控制寄存器ICPR差不多;
b.通过读取VECTACTIVE=>Vector Active域,可以确定当前执行的异常/中断编号,VECTACTIVE域和中断程序状态寄存器(IPSR)功能比较类似,IPSR包含了当前正在执行的中断服务程序(ISR)编号,只不过VECTACTIVE是用来读Systick、PendSV、NMI这些异常状态的。
图7 中断控制和状态寄存器
注意:当写ICSR时,如果:写1到PENDSVSET位和写1到PENDSVCLR位;写1到PENDSTSET位和写1到PENDSTCLR位;程序执行的效果是不可预测的。该寄存器各个位域可以通过调试器读取状态,但多数情况下只有挂起位用于应用开发。
(2)、向量偏移寄存器VTOR
由于CPU随时都可能检测到中断信息,也就是说,CPU 随时都可能执行中断处理程序,所以中断处理程序必须一直存储在内存某段空间之中。中断处理程序在内存中的入口地址称为中断向量;而要确定中断处理程序的入口地址,处理器利用了一种向量表机制:即中断向量,必须存储在对应的中断向量表表项中。采用向量表处理中断,处理器会从存储器的向量表中,自动定位中断的程序入口。
一般来说,程序启动后,将首先从“中断向量表”取出复位中断向量执行复位中断程序完成启动。而这张“中断向量表”的起始地址是0x8000004,当中断来临,STM32的内部硬件机制亦会自动将PC指针定位到“中断向量表”处,并根据中断源取出对应的中断向量执行中断服务程序。不过有些应用可能需要在运行时修改或定义向量表地址。为了进行这种处理,M3/M4处理器实现了一种名为向量表重定位的特性。向量表重定位特性提供了一个名为向量表偏移寄存器(VTOR)的可编程寄存器。该寄存器将正在使用的存储器的起始地址定义为向量表。
图8 向量偏移寄存器
中断向量表里的中断跳转地址在编译后就定下来了,SCB->VTOR向量可动态调整就是让我们的程序运行后还能改变向量的跳转地址。方法就是:在RAM重建一个中断向量表,在想改变的位置重新赋值新的跳转地址。通过赋值向量表偏移数值SCB->VTOR = (uint32_t)__VECTOR_RAM,这样下次异常发生时,就直接跳到重新指定的RAM中断向量表的首地址处,再匹配对应的中断向量。
(3)、应用中断和复位控制寄存器AIRCR
该寄存器用于:
控制异常/中断优先级管理中的优先级分组
提供系统的端信息(可以被软件或调试器使用)
提供自复位特性
图9 应用中断和复位控制寄存器
SCB->AIRCR的中断分组决定了如何去理解NVIC中断优先级控制寄存器IP的高4bit(4~7bit)。SCB->AIRCR的8~10位决定了分组的组别,可以分为0到4共5组,不同的组别决定了IP寄存器划分的抢占优先级和响应优先级各占几位。
VECTRESET和VECTCLRACTIVE位域是为调试器设计的,尽管软件可以利用VECTRESET触发一次处理器复位,不过由于它不会复位外设等系统中的其他部分,因此多数应用程序是不大会用到它的。若想产生一次系统复位,多数情况下(取决于芯片设计和应用复位需求)应该使用SYSRESETREQ。有一点要注意,VECTRESET和VECTCLRACTIVE不应同时置位,非要这么做的话会导致Cortex-M3/M4设备的复位电路出错。
在Cortex-M3中,有两种方法能够进行系统复位:
第一种方法:置位 NVIC 中应用程序中断与复位控制寄存器(AIRCR)的 VECTRESET 位(位偏移:0)。这种复位的作用为复位Cortex-M3处理器内核,除了调试逻辑之外的所有角落,但是它不会影响到Cortex-M3处理器外部的任何电路,所以STM32上的各片上外设和其它电路都不受影响。
第二种方法:置位 NVIC 中应用程序中断与复位控制寄存器(AIRCR)的 SYSRESETREQ位(位偏移:2)。系统复位是置位同一个寄存器中的 SYSRESETREQ 位。这种复位则会波及整个芯片上的电路:它会使Cortex-M3/M4处理器把送往系统复位发生器的请求线置为有效。但是系统复位发生器不是Cortex-M3/M4的这一局部,而是由芯片厂商实现,因此不同的芯片对此复位的响应也不同。因此,读者须要仔细参阅芯片规格书,明白当发生片内复位时,各外设和功能模块都会回到什么样的初始状态,或者有哪些功能模块不受影响(假如,STM32系列的芯片有后备存储区,该区就被特殊对待)。大部分情况下,复位发生器在响应SYSRESETREQ 时,它也会同时把Cortex-M3/M4处理器的系统复位信号(SYSRESETn)置为有效。通常,SYSRESETREQ不应复位调试逻辑。这里有一个要注意的问题:从SYSRESETREQ被置为有效到复位发生器执行复位命令,往往会有一个延时。在此延时期间,处理器依然能够响应中断请求。但我们的本意往往是要让此次执行到此为止,不要再做任何其它事情了。所以,最好在发出复位请求前,先把FAULTMASK置位。
这里有一个要注意的问题:从SYSRESETREQ 被置为有效,到复位发生器执行复位命令,往往会有一个延时。在此延时期间,处理器仍然可以响应中断请求。但我们的本意往往是要让此次执行到此为止,不要再做任何其它事情了。所以,最好在发出复位请求前,先把FAULTMASK 置位,才万无一失。即:
void mcuRestart(void)
{__set_FAULTMASK(1); //关闭所有中断NVIC_SystemReset(); //复位
}
(4)系统处理优先级寄存器SHP
SCB->SHP[0]到SCB->SHP[11]的位域定义和NVIC中断优先级寄存器IP的定义相同,不同之处是SHP是用于系统异常优先级的。这些寄存器并未全部实现,为了编程中断和异常的优先级,CMSIS提供了函数NVIC_SetPrioriity和NVIC_GetPriority。这两个函数位于core_cm3.h中。例如:
图10 系统处理优先级寄存器
(5)、系统处理控制和状态寄存器SHCSR
使用错误、存储器管理错误和总线错误异常的使能由该寄存器控制。错误的挂起状态和多数系统异常的活跃状态也可以从该寄存器中得到。
多数情况下,该寄存器及用于应用代码使能可配置的错误处理(MEMFAULT、 BUSFAULT、USGFAULT)。
图11 系统处理控制和状态寄存器
使能位ENA,设置1使能异常,设置0失能异常。
待定位PENDED,如果异常待定读为1,否则读为0。我们可以写这些位改变异常的待定状态。
活跃位ACT,如果异常活跃读为1,否则读为0。我们可以写这些位改变异常的活跃状态,但是看这部分的注意事项。
尽管可以写SCB->SHCSR寄存器的所有位,但建议软件只写异常使能位。写这个寄存器需要小心,确保系统异常的活跃位状态不会被意外修改,使能异常时,应该使用一次读~修改~写顺序的操作。下面的例子用于使能所有非硬Fault(存储器管理Fault、总线Fault、用法Fault异常):
不然若一个已经被激活的系统异常的活跃状态被意外清除了,当系统异常处理产生异常退出时就会出现错误异常。
五、中断屏蔽寄存器组
中断屏蔽寄存器的目的是让实时性要求高的任务或者事件能够顺利执行,因此要在需要的时候暂时屏蔽中断。
图12 异常和中断编号表
编号为 0~15 的称为内核异常,而 16 以上的则称为核外(外部)中断。
PRIMASK
PRIMASK虽然是32位的寄存器,但是只有最低位有效,这是一个只有1个bit有效的寄存器。当将寄存器的位置为1时,相当于将当前中断的优先级设为0,它只响应NMI(优先级为-2更高)和HardFault(优先级为-1更高)异常,关掉其它优先级小于0(即数值比0大)所有可屏蔽的异常或中断;寄存器的默认值是0,表示没有关异常或者中断。
PRIMASK通常用于在处理有实时性要求(time critical)的程序时关闭所有中断, 实时程序处理完毕后清除PRIMASK,重新开启中断。
FAULTMASK
FAULTMASK虽然是32位的寄存器,但是只有最低位有效,这是一个只有1个bit有效的寄存器。当将寄存器的位置为1时,当于将当前中断的优先级设为-1,它只响应NMI(优先级为-2更高),关掉其它优先级小于-1(即数值比-1大)的可屏蔽的异常或中断,甚至是HardFault异常也关闭;寄存器的默认值是0,表示没有关异常或者中断。
BASEPRI
为了提供灵活的中断屏蔽机制,内核架构提供了BASEPRI寄存器,可以根据优先级屏蔽中断或者异常,屏蔽的是抢占优先级(主优先级),它可以由用户来定义需要屏蔽中断的优先级的阈值,当设置为具体的阈值时,所有大于该数字值的中断都被关闭(优先级数字越大,表示优先级越低);这里设置的优先级针对的是抢占优先级;寄存器的默认值是0,表示没有关异常或者中断。
比如我们配置寄存器BASEPRI的数值为3,所有优先级数值大于等于3的中断都会被关闭,优先级数值小于3的中断不会被关闭。但0比较特殊,对寄存器basepri寄存器赋值0,那么被关闭的中断会被打开。
寄存器BASEPRI的有效位数受系统中表达优先级的位数影响,如果系统中只使用3个位(5~7bit)来表达优先级,则BASEPRI有意义的值仅为0x00、0x20、0x40、0x60、0x80、0xA0、0xC0和0xE0。
图12 寄存器BASEPRI
六、总结
本篇对STM32的中断系统涉及到的的寄存器分别进行了介绍,了解了各个寄存器的功能和对应寄存器位的定义可以更方便的去理解在实际使用中断配置时的配置功能,下一篇将对中断配置流程在实际开发中的设计及应用进行详细的分析。
↓↓↓更多技术内容和书籍资料获取,入群技术交流敬请关注“明解嵌入式”↓↓↓