NVIC是与内核高度集成的外设,由ARM负责设计,与其他外设不同,因此在ST的参考手册中并为过多提及,具体的寄存器需要参考 M4内核参考手册 。
- 寄存器描述 core_cm4.h
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];
__O uint32_t STIR; /*!< Offset: 0xE00 ( /W) Software Trigger Interrupt Register */
} NVIC_Type;
ISER/ICER是中断使能/失能寄存器,是中断的开关,寄存器中的每一位对应一个中断,顺序同中断向量表,因此后面的函数中会先用中断向量号除以32,找出对应的 ISER[x]/ICER[x] ,剩下的余数即为对应寄存器中的bit位数,对应位写1即可。
IP是优先级寄存器,8位对应一个中断,故直接将中断向量号填入IP[x]即可。
- misc.h
typedef struct
{
uint8_t NVIC_IRQChannel;
uint8_t NVIC_IRQChannelPreemptionPriority;
uint8_t NVIC_IRQChannelSubPriority;
FunctionalState NVIC_IRQChannelCmd;
} NVIC_InitTypeDef;
NVIC初始化结构体
中断向量号,抢占优先级,子优先级,中断开关。
相关函数都在misc.c文件中。
优先级实现机制
每个中断的优先级由对应的NVIC->IPR设置,寄存器为8位,其中高4位有效,低4位无效。
高4位可分为两组,一组是抢占优先级,一组是子优先级;当两个中断发生时,会优先比较抢占优先级,小的先相应;抢占优先级相同时,根据子优先级判断;子优先级也相同时,中断向量号小的先相应。
具体哪几位是抢占优先级,哪几位是子优先级,由SCB->AIRCR[10:8] 设置。SCB寄存器与NVIC类似,也是与内核紧密相连的外设,也由ARM而不是ST公司设计,参考手册中未提及,具体见M4内核参考手册(或M4与M3内核权威手册)。
注意,开关中断和跳转中断处理函数由NVIC负责,但是中断标志位的置位和清除由EXTI负责。
NVIC与EXTI管理大部分外设中断,但是中断向量表中小于0的中断不受NVIC和EXTI的管理,因为这些中断的模块与内核联系紧密,并不属于一般的外设,部分中断的使能、优先级、标志位在自己的模块或者SCB模块中。