在学习SysTick时使用库函数Systick_Config()中调用了NVIC_SetPriority(),当时没有做详细研究,现在就来学习一下吧。
在SysTick_config()中调用如下所示:
NVIC_SetPriority (SysTick_IRQn, (1<<__NVIC_PRIO_BITS) - 1);/* set Priority for Cortex-M0 System Interrupts */
SysTick_IRQn 为宏定义在stm32f10x.h,其值为-1;
__NVIC_PRIO_BITS同样为宏定义在stm32f10x.h,其值为4;上述函数可以改写为:
NVIC_SetPriority (-1, 16 - 1);
下面让我们来看看 NVIC_SetPriority()函数的定义,它IR的主要作用是设置中断的优先级,
它包含2个参数,IRQn 用来指定中断源,priority指定优先级。
当IRQn<0时,配置内核中断的优先级
当IRQn>=0时,配置的是外部中断的优先级
static __INLINE void NVIC_SetPriority(IRQn_Type IRQn, uint32_t priority)
{
if(IRQn < 0) {
SCB->SHP[((uint32_t)(IRQn) & 0xF)-4] = ((priority << (8 - __NVIC_PRIO_BITS)) & 0xff); } /* set Priority for Cortex-M3 System Interrupts */
else {
NVIC->IP[(uint32_t)(IRQn)] = ((priority << (8 - __NVIC_PRIO_BITS)) & 0xff); } /* set Priority for device specific Interrupts */
}
当IRQn<0时,设置的是内核中断 ,0~15优先级可以设置,数字越小优先级越高,STM32F103共包含如下内核中断
NonMaskableInt_IRQn = -14, /*!< 2 Non Maskable Interrupt */
MemoryManagement_IRQn = -12, /*!< 4 Cortex-M3 Memory Management Interrupt */
BusFault_IRQn = -11, /*!< 5 Cortex-M3 Bus Fault Interrupt */
UsageFault_IRQn = -10, /*!< 6 Cortex-M3 Usage Fault Interrupt */
SVCall_IRQn = -5, /*!< 11 Cortex-M3 SV Call Interrupt */
DebugMonitor_IRQn = -4, /*!< 12 Cortex-M3 Debug Monitor Interrupt */
PendSV_IRQn = -2, /*!< 14 Cortex-M3 Pend SV Interrupt */
SysTick_IRQn = -1, /*!< 15 Cortex-M3 System Tick Interrupt
由上可知 NVIC_SetPriority (SysTick_IRQn, (1<<__NVIC_PRIO_BITS) - 1)是将SysTick中断的优先级设置为15,即内核优先级中最低的。
接着让我们来看看库函数是怎么一步步与寄存器对应起来的,以加深印象。
内核中断优先级设置:
首先我们来看下SCB->SHPR这个寄存器
SHPR1-SHPR3寄存器都是32位的,用来设置内核中断的优先级(0-15),并且可以以字节形式进行读写,设置每个内核中断的优先级的字段虽然是8位的,但只有高4位(7:4)用来设置优先级,所以优先级只能是0-15,低4位(3:0)在被读取时为0,写入时忽略。
SHPR1
SHPR2
SHPR3
在core_cm3.h文件中的SCB结构体中包含一个uint8_t的数组 SHP[12],如下所示:
typedef struct
{
__I uint32_t CPUID; /*!< Offset: 0x00 CPU ID Base Register */
__IO uint32_t ICSR; /*!< Offset: 0x04 Interrupt Control State Register */
__IO uint32_t VTOR; /*!< Offset: 0x08 Vector Table Offset Register */
__IO uint32_t AIRCR; /*!< Offset: 0x0C Application Interrupt / Reset Control Register */
__IO uint32_t SCR; /*!< Offset: 0x10 System Control Register */
__IO uint32_t CCR; /*!< Offset: 0x14 Configuration Control Register */
__IO uint8_t SHP[12]; /*!< Offset: 0x18 System Handlers Priority Registers (4-7, 8-11, 12-15) */
__IO uint32_t SHCSR; /*!< Offset: 0x24 System Handler Control and State Register */
__IO uint32_t CFSR; /*!< Offset: 0x28 Configurable Fault Status Register */
__IO uint32_t HFSR; /*!< Offset: 0x2C Hard Fault Status Register */
__IO uint32_t DFSR; /*!< Offset: 0x30 Debug Fault Status Register */
__IO uint32_t MMFAR; /*!< Offset: 0x34 Mem Manage Address Register */
__IO uint32_t BFAR; /*!< Offset: 0x38 Bus Fault Address Register */
__IO uint32_t AFSR; /*!< Offset: 0x3C Auxiliary Fault Status Register */
__I uint32_t PFR[2]; /*!< Offset: 0x40 Processor Feature Register */
__I uint32_t DFR; /*!< Offset: 0x48 Debug Feature Register */
__I uint32_t ADR; /*!< Offset: 0x4C Auxiliary Feature Register */
__I uint32_t MMFR[4]; /*!< Offset: 0x50 Memory Model Feature Register */
__I uint32_t ISAR[5]; /*!< Offset: 0x60 ISA Feature Register */
} SCB_Type;
SHP[0]-SHP[11]与SHPR1-SHPR3的每个字节是一一对应的,即SHP[0]对应SHPR1的低8位[7:0].
现在让我们来验证下上面结论是否正确,
SCB->SHP[((uint32_t)(IRQn) & 0xF)-4] = ((priority << (8 - __NVIC_PRIO_BITS)) & 0xff);
SysTick_IRQn对应值为-1,首先强制转化为uint32_t结果为0xffff ffff,与上0xf后结果为0xf,减去4结果为11,即SHP[11]对应SHPR3的 [31:24] 位,与实际相符。
BusFault_IRQn对应值为-11,首先强制转化为uint32_t结果为0xffff fff5,与上0xf后结果为0x5,减去4结果为1,即SHP[1]对应SHPR1的 [15:8] 位,与实际相符。
priority的设置,
__NVIC_PRIO_BITS为宏定义值为4,由于设置优先级的寄存器是8位的但只有高4位有效,所以需要将priority的值左移4位。
外部中断优先级设置:
NVIC->IP[(uint32_t)(IRQn)] = ((priority << (8 - __NVIC_PRIO_BITS)) & 0xff);
Coretex_m3 的 NVIC 用中断优先级寄存器(IPR)的 8 位字段映射到一个 8 位整数数组IP[240],数组条目 IP[n] 保存中断号为 n 的中断优先级。
例如:IP[0] 对应WWDG_IRQn的中断优先级
IP[6]对应EXTI0_IRQn的中断优先级。
Interrupt priority registers (NVIC_IPRx)中断优先级寄存器
每个IPR寄存器包含4个优先级字段,每个字段占8个bit,可以用字节的方式读取或写入,但只有[7:4] bit用来表示优先级,读取字段的[3:0]bits时值为0,写[3:0]bits时将该值忽略。
所以在库函数中要将写入的优先级的值先左移4位。
IP[n]中的内容要根据NVIC分组的不同来确定,NVIC将外设优先级分为5组,默认分组为0,
/**
* @brief Configures the priority grouping: pre-emption priority and subpriority.
* @param NVIC_PriorityGroup: specifies the priority grouping bits length.
* This parameter can be one of the following values:
* @arg NVIC_PriorityGroup_0: 0 bits for pre-emption priority //不支持抢占优先级
* 4 bits for subpriority //支持16个响应优先级
* @arg NVIC_PriorityGroup_1: 1 bits for pre-emption priority //支持2个抢占优先级
* 3 bits for subpriority //支持8个响应优先级
* @arg NVIC_PriorityGroup_2: 2 bits for pre-emption priority //支持4个抢占优先级
* 2 bits for subpriority //支持4个响应优先级
* @arg NVIC_PriorityGroup_3: 3 bits for pre-emption priority //支持8个抢占优先级
* 1 bits for subpriority //支持2个响应优先级
* @arg NVIC_PriorityGroup_4: 4 bits for pre-emption priority //支持16个抢占优先级
* 0 bits for subpriority //不支持响应优先级
* @note When the NVIC_PriorityGroup_0 is selected, IRQ pre-emption is no more possible.
* The pending IRQ priority will be managed only by the subpriority.
* @retval None
*/
void NVIC_PriorityGroupConfig(uint32_t NVIC_PriorityGroup)
例如将NVIC 优先级分组设置为组2,外部中断EXTI0的抢占优先级设置为1,响应优先级设置为1,分组2表示[7:6]2bits表示抢占优先级,[5:4]2bits表示响应优先级,EXTI0的中断号为6, IP[6]=0101 0000 ,则读取IP[6]的值为0x50。