Cortex-M3/M4内核NVIC及HAL库函数详解(5):__disable_irq和HAL_NVIC_DisableIRQ、__enable_irq和HAL_NVIC_EnableIRQ的区别

0 工具准备

Keil uVision5
Cortex M3权威指南(中文)
Cortex M3与M4权威指南
stm32f407的HAL库工程
STM32F4xx中文参考手册

1 __disable_irq和HAL_NVIC_DisableIRQ、__enable_irq和HAL_NVIC_EnableIRQ的区别

1.1 函数分析

我们在执行某些语句时不希望被中断打断,往往会使用__disable_irq失能全局中断,在语句执行完毕后再使用__enable_irq使能全局中断。伪代码如下:

__disable_irq();
// 时间敏感语句
__enable_irq();

使用上述语句可以一步到位失能全部中断,避免被任何中断打断,但它有一个弊端,就是会影响到导致一些必须定时调用的中断(例如SysTick时基)被中断,导致一些问题的产生。为了避免上述的问题,有时候只会失能影响最大的中断请求,也就是使用HAL_NVIC_DisableIRQ失能特定中断,然后使用HAL_NVIC_EnableIRQ使能特定中断。伪代码如下:

HAL_NVIC_DisableIRQ(xxx_IRQn);
// 时间敏感语句
HAL_NVIC_EnableIRQ(xxx_IRQn);

除了失能中断的范围不一样,上述函数操作的寄存器也不一样:
(1)__disable_irq和__enable_irq
__disable_irq和__enable_irq操作的是PRIMASK寄存器,名为中断屏蔽寄存器。
Cortex-M3/M4内核NVIC及HAL库函数详解(5):__disable_irq和HAL_NVIC_DisableIRQ、__enable_irq和HAL_NVIC_EnableIRQ的区别_第1张图片
(2)HAL_NVIC_DisableIRQ和HAL_NVIC_EnableIRQ
HAL_NVIC_DisableIRQ操作的是中断失能寄存器:
Cortex-M3/M4内核NVIC及HAL库函数详解(5):__disable_irq和HAL_NVIC_DisableIRQ、__enable_irq和HAL_NVIC_EnableIRQ的区别_第2张图片
HAL_NVIC_EnableIRQ操作的是中断使能寄存器:
Cortex-M3/M4内核NVIC及HAL库函数详解(5):__disable_irq和HAL_NVIC_DisableIRQ、__enable_irq和HAL_NVIC_EnableIRQ的区别_第3张图片

1.2 测试对比

字面上理解,中断失能后这一段时间内中断都无法生成,也就无法挂起。中断屏蔽只是不响应中断但是如果屏蔽中断这一段时间产生了中断则中断会被挂起。为了验证这一想法,我们编写了2段测试程序:

int testsw11(int argc, char *argv[])
{
    __disable_irq();
    Delay_s(10);
    printf("HAL_NVIC_GetPriorityGrouping : %08X\r\n", HAL_NVIC_GetPriorityGrouping());
    printf("HAL_NVIC_GetPendingIRQ       : %08X\r\n", HAL_NVIC_GetPendingIRQ(EXTI0_IRQn));
    __enable_irq();
    return 0;
}
int testsw12(int argc, char *argv[])
{
    HAL_NVIC_DisableIRQ(EXTI0_IRQn);
    Delay_s(10);
    printf("HAL_NVIC_GetPriorityGrouping : %08X\r\n", HAL_NVIC_GetPriorityGrouping());
    printf("HAL_NVIC_GetPendingIRQ       : %08X\r\n", HAL_NVIC_GetPendingIRQ(EXTI0_IRQn));
    HAL_NVIC_EnableIRQ(EXTI0_IRQn);
    return 0;
}

在进行测试前,我们按照前面章节的操作,将PA0初始化为下降沿触发。
(1)testsw11测试
首先打印当前中断挂起状态,然后执行testsw11函数,在Delay_s(10)中按下按键,然后打印中断挂起状态。测试结果如下:
Cortex-M3/M4内核NVIC及HAL库函数详解(5):__disable_irq和HAL_NVIC_DisableIRQ、__enable_irq和HAL_NVIC_EnableIRQ的区别_第4张图片

可以看到__disable_irq仅仅是不响应中断,中断在我们按下SW1后仍然会产生,同时由于得不到响应会被挂起,因此挂起状态为1,在执行了__enable_irq后挂起的中断得到了响应,打印了中断服务函数里的信息。中断服务函数执行完毕后,相应的中断挂起位也清空为0。
(2)testsw12测试
首先打印当前中断挂起状态,然后执行testsw12函数,在Delay_s(10)中按下按键,然后打印中断挂起状态。测试结果如下:
Cortex-M3/M4内核NVIC及HAL库函数详解(5):__disable_irq和HAL_NVIC_DisableIRQ、__enable_irq和HAL_NVIC_EnableIRQ的区别_第5张图片
可以看到HAL_NVIC_DisableIRQ仅仅是不响应中断,中断在我们按下SW1后仍然会产生,同时由于得不到响应会被挂起,因此挂起状态为1,在执行了HAL_NVIC_EnableIRQ后挂起的中断得到了响应,打印了中断服务函数里的信息。中断服务函数执行完毕后,相应的中断挂起位也清空为0。

2 总结

(1)__disable_irq和HAL_NVIC_DisableIRQ在执行后都不会影响中断的生成,只是不会响应中断,在失能状态下如果发生中断则会挂起,等到使能后满足条件(优先级最高)则会被执行。因此可以认为__disable_irq和HAL_NVIC_DisableIRQ只是用来决定是否响应中断,中断的生成只和外设中断使能寄存器的配置有关。
下图就是串口的中断使能寄存器说明:
Cortex-M3/M4内核NVIC及HAL库函数详解(5):__disable_irq和HAL_NVIC_DisableIRQ、__enable_irq和HAL_NVIC_EnableIRQ的区别_第6张图片
(2)对于时间敏感语句或不可重入代码段,可以使用HAL_NVIC_DisableIRQ来替代__disable_irq,避免失能全局中断响应带来的问题
(3)如果只是需要暂时性不响应中断,使用__disable_irq之后一定要__enable_irq,使用HAL_NVIC_DisableIRQ之后一定要HAL_NVIC_EnableIRQ
(4)可以把__disable_irq和__enable_irq看做响应中断的总开关,HAL_NVIC_DisableIRQ和HAL_NVIC_EnableIRQ看做响应中断的分开关。要想响应某个中断必须保证分开关和总开关都是闭合状态的,如果不想响应某个中断必须保证总开关或分开关是断开状态
Cortex-M3/M4内核NVIC及HAL库函数详解(5):__disable_irq和HAL_NVIC_DisableIRQ、__enable_irq和HAL_NVIC_EnableIRQ的区别_第7张图片

你可能感兴趣的:(单片机开发,ARM内核,stm32,ARM,中断,HAL库)