前言
在使用STM32F103,GD32F303等单片机时,我们经常会使用到中断。对于几乎所有的微控制器,中断都是一种常见的特性。一般我们需要对中断进行配置。
- 使能外设某个中断
- 中断优先级分组(抢占优先级,响应优先级)
- NVIC中使能中断
- 编写中断服务函数
那么这些操作对应Cortex-M3与Cortex-M4内核内核中哪些寄存器,又有什么作用呢?
中断优先级分组
设置中断优先级分组,对应的函数为
__STATIC_INLINE void NVIC_SetPriorityGrouping(uint32_t PriorityGroup)
{
uint32_t reg_value;
uint32_t PriorityGroupTmp = (PriorityGroup & (uint32_t)0x07); /* only values 0..7 are used */
reg_value = SCB->AIRCR; /* read old register configuration */
reg_value &= ~(SCB_AIRCR_VECTKEY_Msk | SCB_AIRCR_PRIGROUP_Msk); /* clear bits to change */
reg_value = (reg_value |
((uint32_t)0x5FA << SCB_AIRCR_VECTKEY_Pos) |
(PriorityGroupTmp << 8)); /* Insert write key and priorty group */
SCB->AIRCR = reg_value;
}
主要操作的是应用中断和复位控制寄存器(AIRCR)。
中断优先级设定,使能中断
根据抢占优先级,和子优先级计算传入NVIC中的中断优先级寄存器(IP)的参数,然后设定中断优先级
__STATIC_INLINE uint32_t NVIC_EncodePriority (uint32_t PriorityGroup, uint32_t PreemptPriority, uint32_t SubPriority)
{
uint32_t PriorityGroupTmp = (PriorityGroup & 0x07); /* only values 0..7 are used */
uint32_t PreemptPriorityBits;
uint32_t SubPriorityBits;
PreemptPriorityBits = ((7 - PriorityGroupTmp) > __NVIC_PRIO_BITS) ? __NVIC_PRIO_BITS : 7 - PriorityGroupTmp;
SubPriorityBits = ((PriorityGroupTmp + __NVIC_PRIO_BITS) < 7) ? 0 : PriorityGroupTmp - 7 + __NVIC_PRIO_BITS;
return (
((PreemptPriority & ((1 << (PreemptPriorityBits)) - 1)) << SubPriorityBits) |
((SubPriority & ((1 << (SubPriorityBits )) - 1)))
);
}
__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-M System Interrupts */
else {
NVIC->IP[(uint32_t)(IRQn)] = ((priority << (8 - __NVIC_PRIO_BITS)) & 0xff); } /* set Priority for device specific Interrupts */
}
使能中断函数
__STATIC_INLINE void NVIC_EnableIRQ(IRQn_Type IRQn)
{
/* NVIC->ISER[((uint32_t)(IRQn) >> 5)] = (1 << ((uint32_t)(IRQn) & 0x1F)); enable interrupt */
NVIC->ISER[(uint32_t)((int32_t)IRQn) >> 5] = (uint32_t)(1 << ((uint32_t)((int32_t)IRQn) & (uint32_t)0x1F)); /* enable interrupt */
}
主要操作的是中断控制用的NVIC寄存器中的中断优先级寄存器(IP)和中断使能寄存器(ISER)。
每一个中断都有一个8位的寄存器,用来表示它的优先级但是这8位,是否全都实现了?取决于芯片
那么我们怎么知道他实现了多少位?
可以往任意一个优先级寄存器写入0xFF,再读出来。如果实现了三位,读出的值是:0b11100000
如果实现了4位,读出的值是: 0b11110000
例如GD32F303为四位,固件库将NVIC操作封装了,只使用IP寄存器高四位。
#define NVIC_PRIGROUP_PRE0_SUB4 ((uint32_t)0x700) /*!< 0 bits for pre-emption priority 4 bits for subpriority */
#define NVIC_PRIGROUP_PRE1_SUB3 ((uint32_t)0x600) /*!< 1 bits for pre-emption priority 3 bits for subpriority */
#define NVIC_PRIGROUP_PRE2_SUB2 ((uint32_t)0x500) /*!< 2 bits for pre-emption priority 2 bits for subpriority */
#define NVIC_PRIGROUP_PRE3_SUB1 ((uint32_t)0x400) /*!< 3 bits for pre-emption priority 1 bits for subpriority */
#define NVIC_PRIGROUP_PRE4_SUB0 ((uint32_t)0x300) /*!< 4 bits for pre-emption priority 0 bits for subpriority */
void nvic_irq_enable(uint8_t nvic_irq, uint8_t nvic_irq_pre_priority,
uint8_t nvic_irq_sub_priority)
{
uint32_t temp_priority = 0x00U, temp_pre = 0x00U, temp_sub = 0x00U;
/* use the priority group value to get the temp_pre and the temp_sub */
if(((SCB->AIRCR) & (uint32_t)0x700U)==NVIC_PRIGROUP_PRE0_SUB4){
temp_pre=0U;
temp_sub=0x4U;
}else if(((SCB->AIRCR) & (uint32_t)0x700U)==NVIC_PRIGROUP_PRE1_SUB3){
temp_pre=1U;
temp_sub=0x3U;
}else if(((SCB->AIRCR) & (uint32_t)0x700U)==NVIC_PRIGROUP_PRE2_SUB2){
temp_pre=2U;
temp_sub=0x2U;
}else if(((SCB->AIRCR) & (uint32_t)0x700U)==NVIC_PRIGROUP_PRE3_SUB1){
temp_pre=3U;
temp_sub=0x1U;
}else if(((SCB->AIRCR) & (uint32_t)0x700U)==NVIC_PRIGROUP_PRE4_SUB0){
temp_pre=4U;
temp_sub=0x0U;
}else{
nvic_priority_group_set(NVIC_PRIGROUP_PRE2_SUB2);
temp_pre=2U;
temp_sub=0x2U;
}
/* get the temp_priority to fill the NVIC->IP register */
temp_priority = (uint32_t)nvic_irq_pre_priority << (0x4U - temp_pre);
temp_priority |= nvic_irq_sub_priority &(0x0FU >> (0x4U - temp_sub));
temp_priority = temp_priority << 0x04U;
NVIC->IP[nvic_irq] = (uint8_t)temp_priority;
/* enable the selected IRQ */
NVIC->ISER[nvic_irq >> 0x05U] = (uint32_t)0x01U << (nvic_irq & (uint8_t)0x1FU);
}
中断响应顺序
- 配置在中断优先级中的数字越小,优先级越高
- 高抢占式优先级的中断可以在具有低抢占式优先级的中断处理过程中被响应,即中断嵌套。相同抢占优先级中断不能相互嵌套。
- 抢占式优先级相同,子优先级不同,中断同时发生,那么高子优先级的中断优先被响应。
- 如果抢占优先级、子优先级都相同,中断同时发生,硬件中断号更小先处理。
CPU内核中与中断相关的控制寄存器
1. PRIMASK
把PRIMASK的bit0设置为1,就可以屏蔽所有优先级可配置的中断
PRIMASK 用于禁止除 NMI和HardFault外的所有异常。
__STATIC_INLINE void __set_PRIMASK(uint32_t priMask)
{
register uint32_t __regPriMask __ASM("primask");
__regPriMask = (priMask);
}
2. FAULTMASK
只有NMI异常可以发生,其余所有中断屏蔽
__STATIC_INLINE void __set_FAULTMASK(uint32_t faultMask)
{
register uint32_t __regFaultMask __ASM("faultmask");
__regFaultMask = (faultMask & (uint32_t)1);
}
3. BASEPRI
可设置此寄存器用于屏蔽优先级低于某一优先级的中断
__STATIC_INLINE void __set_BASEPRI(uint32_t basePri)
{
register uint32_t __regBasePri __ASM("basepri");
__regBasePri = (basePri & 0xff);
}
附录
参考资料
1、ARM Cortex-M3与Cortex-M4权威指南(第3版)
2、百问网ARM架构与编程
3、core_cm4.h和core_cmFunc.h
4、DDI0403E_e_armv7m_arm
内容若有错误,敬请留言 ,指正!