0 工具准备
Keil uVision5
Cortex M3权威指南(中文)
Cortex M3与M4权威指南
stm32f407的HAL库工程
STM32F4xx中文参考手册
1 HAL库中断底层函数实现
打开stm32f407的HAL库工程,可以在CMSIS->Include->core_cm4.h内找到有关NVIC寄存器设置的相关函数:
#define NVIC_SetPriorityGrouping __NVIC_SetPriorityGrouping
#define NVIC_GetPriorityGrouping __NVIC_GetPriorityGrouping
#define NVIC_EnableIRQ __NVIC_EnableIRQ
#define NVIC_GetEnableIRQ __NVIC_GetEnableIRQ
#define NVIC_DisableIRQ __NVIC_DisableIRQ
#define NVIC_GetPendingIRQ __NVIC_GetPendingIRQ
#define NVIC_SetPendingIRQ __NVIC_SetPendingIRQ
#define NVIC_ClearPendingIRQ __NVIC_ClearPendingIRQ
#define NVIC_GetActive __NVIC_GetActive
#define NVIC_SetPriority __NVIC_SetPriority
#define NVIC_GetPriority __NVIC_GetPriority
#define NVIC_SystemReset __NVIC_SystemReset
#define NVIC_SetVector __NVIC_SetVector
#define NVIC_GetVector __NVIC_GetVector
1.1 __NVIC_SetPriorityGrouping(设置优先级分组)函数
__STATIC_INLINE void __NVIC_SetPriorityGrouping(uint32_t PriorityGroup)
{
uint32_t reg_value;
uint32_t PriorityGroupTmp = (PriorityGroup & (uint32_t)0x07UL); /* only values 0..7 are used */
reg_value = SCB->AIRCR; /* read old register configuration */
reg_value &= ~((uint32_t)(SCB_AIRCR_VECTKEY_Msk | SCB_AIRCR_PRIGROUP_Msk)); /* clear bits to change */
reg_value = (reg_value |
((uint32_t)0x5FAUL << SCB_AIRCR_VECTKEY_Pos) |
(PriorityGroupTmp << SCB_AIRCR_PRIGROUP_Pos) ); /* Insert write key and priority group */
SCB->AIRCR = reg_value;
}
该函数操作步骤如下:
(1)读取SCB->AIRCR旧值,保存到reg_value
(2)将reg_value的bit31-16(访问钥匙)、bit10-8(优先级分组)设置为0
(3)将reg_value的bit31-16(访问钥匙)设置为0x05FA,同时将优先级分组值写到bit10-8(优先级分组)
(4)将SCB->AIRCR的值设置为reg_value
相关寄存器如下:
1.2 __NVIC_GetPriorityGrouping(获取优先级分组)函数
__STATIC_INLINE uint32_t __NVIC_GetPriorityGrouping(void)
{
return ((uint32_t)((SCB->AIRCR & SCB_AIRCR_PRIGROUP_Msk) >> SCB_AIRCR_PRIGROUP_Pos));
}
这个函数读取SCB->AIRCR的bit10-bit8获取优先级分组设置的值。
相关寄存器如下:
1.3 __NVIC_EnableIRQ(使能中断请求)函数
__STATIC_INLINE void __NVIC_EnableIRQ(IRQn_Type IRQn)
{
if ((int32_t)(IRQn) >= 0)
{
NVIC->ISER[(((uint32_t)IRQn) >> 5UL)] = (uint32_t)(1UL << (((uint32_t)IRQn) & 0x1FUL));
}
}
该函数通过中断请求编号得到在其在中断使能寄存器中的对应位,然后将该位置1使能中断。
相关寄存器如下:
1.4 __NVIC_GetEnableIRQ(获取中断请求使能状态)函数
__STATIC_INLINE uint32_t __NVIC_GetEnableIRQ(IRQn_Type IRQn)
{
if ((int32_t)(IRQn) >= 0)
{
return((uint32_t)(((NVIC->ISER[(((uint32_t)IRQn) >> 5UL)] & (1UL << (((uint32_t)IRQn) & 0x1FUL))) != 0UL) ? 1UL : 0UL));
}
else
{
return(0U);
}
}
该函数通过中断请求编号得到在其在中断使能寄存器中的对应位,然后读取对应位的值并返回。
相关寄存器如下:
1.5 __NVIC_DisableIRQ(失能中断请求)函数
__STATIC_INLINE void __NVIC_DisableIRQ(IRQn_Type IRQn)
{
if ((int32_t)(IRQn) >= 0)
{
NVIC->ICER[(((uint32_t)IRQn) >> 5UL)] = (uint32_t)(1UL << (((uint32_t)IRQn) & 0x1FUL));
__DSB();
__ISB();
}
}
该函数通过中断请求编号得到在其在中断失能寄存器中的对应位,然后将该位置1失能中断。
相关寄存器如下:
这里还有__DSB()、__ISB()这2个函数,分别是数据同步屏障、指令同步屏障,目的是保证前面对寄存器的操作被执行完成。
1.6 __NVIC_GetPendingIRQ(获取中断请求挂起状态)函数
__STATIC_INLINE uint32_t __NVIC_GetPendingIRQ(IRQn_Type IRQn)
{
if ((int32_t)(IRQn) >= 0)
{
return((uint32_t)(((NVIC->ISPR[(((uint32_t)IRQn) >> 5UL)] & (1UL << (((uint32_t)IRQn) & 0x1FUL))) != 0UL) ? 1UL : 0UL));
}
else
{
return(0U);
}
}
该函数通过中断请求编号得到在其在中断挂起寄存器中的对应位,然后返回对应位的值。
相关寄存器如下:
1.7 __NVIC_SetPendingIRQ(设置中断请求挂起状态)函数
__STATIC_INLINE void __NVIC_SetPendingIRQ(IRQn_Type IRQn)
{
if ((int32_t)(IRQn) >= 0)
{
NVIC->ISPR[(((uint32_t)IRQn) >> 5UL)] = (uint32_t)(1UL << (((uint32_t)IRQn) & 0x1FUL));
}
}
该函数通过中断请求编号得到在其在中断挂起寄存器中的对应位,然后设置对应位为1。
相关寄存器如下:
1.8 __NVIC_ClearPendingIRQ(清除中断请求挂起)函数
__STATIC_INLINE void __NVIC_ClearPendingIRQ(IRQn_Type IRQn)
{
if ((int32_t)(IRQn) >= 0)
{
NVIC->ICPR[(((uint32_t)IRQn) >> 5UL)] = (uint32_t)(1UL << (((uint32_t)IRQn) & 0x1FUL));
}
}
该函数通过中断请求编号得到在其在中断解挂寄存器中的对应位,然后设置对应位为1。
相关寄存器如下:
1.9 __NVIC_GetActive(获取中断激活状态)函数
__STATIC_INLINE uint32_t __NVIC_GetActive(IRQn_Type IRQn)
{
if ((int32_t)(IRQn) >= 0)
{
return((uint32_t)(((NVIC->IABR[(((uint32_t)IRQn) >> 5UL)] & (1UL << (((uint32_t)IRQn) & 0x1FUL))) != 0UL) ? 1UL : 0UL));
}
else
{
return(0U);
}
}
该函数通过中断请求编号得到在其在中断激活寄存器中的对应位,然后读取并返回对应位的值。
相关寄存器如下:
1.20 __NVIC_SetPriority(设置中断优先级)函数
__STATIC_INLINE void __NVIC_SetPriority(IRQn_Type IRQn, uint32_t priority)
{
if ((int32_t)(IRQn) >= 0)
{
NVIC->IP[((uint32_t)IRQn)] = (uint8_t)((priority << (8U - __NVIC_PRIO_BITS)) & (uint32_t)0xFFUL);
}
else
{
SCB->SHP[(((uint32_t)IRQn) & 0xFUL)-4UL] = (uint8_t)((priority << (8U - __NVIC_PRIO_BITS)) & (uint32_t)0xFFUL);
}
}
该函数可以分为外部中断和系统异常2个部分:
(1)外部中断(中断请求序号≥0)
通过中断请求编号找到对应的中断优先级寄存器,然后设置中断优先级寄存器即可。
相关寄存器如下:
注:stm32f407仅使用高4bit设置中断优先级
(2)系统异常(中断请求序号<0)
通过系统异常请求编号找到对应的异常优先级寄存器,然后设置系统异常优先级寄存器即可。
相关寄存器如下:
注:stm32f407仅使用高4bit设置中断优先级
1.21 __NVIC_GetPriority(获取中断优先级)函数
__STATIC_INLINE uint32_t __NVIC_GetPriority(IRQn_Type IRQn)
{
if ((int32_t)(IRQn) >= 0)
{
return(((uint32_t)NVIC->IP[((uint32_t)IRQn)] >> (8U - __NVIC_PRIO_BITS)));
}
else
{
return(((uint32_t)SCB->SHP[(((uint32_t)IRQn) & 0xFUL)-4UL] >> (8U - __NVIC_PRIO_BITS)));
}
}
该函数可以分为外部中断和系统异常2个部分:
(1)外部中断(中断请求序号≥0)
通过中断请求编号找到对应的中断优先级寄存器,然后读取中断优先级寄存器即可。
相关寄存器如下:
注:stm32f407的高4bit为中断优先级
(2)系统异常(中断请求序号<0)
通过系统异常请求编号找到对应的异常优先级寄存器,然后读取系统异常优先级寄存器即可。
相关寄存器如下:
注:stm32f407的高4bit为中断优先级
1.22 __NVIC_SystemReset(系统复位)函数
__NO_RETURN __STATIC_INLINE void __NVIC_SystemReset(void)
{
__DSB(); /* Ensure all outstanding memory accesses included
buffered write are completed before reset */
SCB->AIRCR = (uint32_t)((0x5FAUL << SCB_AIRCR_VECTKEY_Pos) |
(SCB->AIRCR & SCB_AIRCR_PRIGROUP_Msk) |
SCB_AIRCR_SYSRESETREQ_Msk ); /* Keep priority group unchanged */
__DSB(); /* Ensure completion of memory access */
for(;;) /* wait until reset */
{
__NOP();
}
}
系统复位函数实际上就是写AIRCR(应用程序中断及复位控制寄存器)的bit2为1使MCU复位。
相关寄存器如下:
1.23 __NVIC_SetVector(设置中断向量偏移量)函数
__STATIC_INLINE void __NVIC_SetVector(IRQn_Type IRQn, uint32_t vector)
{
uint32_t *vectors = (uint32_t *)SCB->VTOR;
vectors[(int32_t)IRQn + NVIC_USER_IRQ_OFFSET] = vector;
}
这个函数实际上就是设置外部中断向量偏移量,用得很少。
相关寄存器如下:
1.24 __NVIC_GetVector(获取中断向量偏移量)函数
__STATIC_INLINE uint32_t __NVIC_GetVector(IRQn_Type IRQn)
{
uint32_t *vectors = (uint32_t *)SCB->VTOR;
return vectors[(int32_t)IRQn + NVIC_USER_IRQ_OFFSET];
}
这个函数实际上就是获取外部中断向量偏移量寄存器的值,用得很少。
相关寄存器如下: