__disable_irq() 和 __enable_irq()定义在哪?

发布时间:2024-12-14  

前段时间一工程师向我咨询了一个问题,问我为什么他的MCU KEIL工程代码里没有找到__disable_irq() 和 __enable_irq()的具体定义,是不是有问题。

直接在工程里搜索,确实只能在cmsis_armcc.h文件里看到下面的两处注释说明,并没有这俩函数的具体定义。

可是如果直接去调用这俩函数的话,编译又不会报错,那么这俩函数的定义到底在哪呢?

__disable_irq() 和 __enable_irq() 是所谓的intrinsic函数,编译器自动识别并替换为相关的指令,它们其实是编译器的一部分,实际的定义位于arm_compat.h 文件中(位于KEIL的安装目录里),

static __inline__ unsigned int __attribute__((__always_inline__, __nodebug__))
__disable_irq(void) {
  unsigned int cpsr; #if __ARM_ARCH >= 6 #if defined(__ARM_ARCH_PROFILE) && __ARM_ARCH_PROFILE == 'M' __asm__ __volatile__("mrs %[cpsr], primask
" "cpsid i
" : [cpsr] "=r"(cpsr)); return cpsr & 0x1; #else /* !defined(__ARM_ARCH_PROFILE) || __ARM_ARCH_PROFILE != 'M' */ __asm__ __volatile__("mrs %[cpsr], cpsr
" "cpsid i
" : [cpsr] "=r"(cpsr)); return cpsr & 0x80; #endif #else /* __ARM_ARCH < 6 */ unsigned int tmp;
  __asm__ __volatile__( "mrs %[cpsr], CPSR
" "bic %[tmp], %[cpsr], #0x80
" "msr CPSR_c, %[tmp]
" : [tmp]"=r"(tmp), [cpsr]"=r"(cpsr)); return cpsr & 0x80; #endif }
#if (defined(__ARM_ARCH_PROFILE) && __ARM_ARCH_PROFILE == 'M' &&                 __ARM_ARCH == 6) || __ARM_ARCH_8M_BASE__
static __inline__ void __attribute__((unavailable( "intrinsic not supported for this architecture"))) __enable_fiq(void); #else // (!defined(__ARM_ARCH_PROFILE) || __ARM_ARCH_PROFILE != 'M' || // __ARM_ARCH != 6) && !__ARM_ARCH_8M_BASE__
static __inline__ void __attribute__((__always_inline__, __nodebug__))
__enable_fiq(void) { #if __ARM_ARCH >= 6 __asm__ __volatile__("cpsie f"); #else /* __ARM_ARCH < 6 */ unsigned int tmp;
  __asm__ __volatile__( "mrs %[tmp], CPSR
" "bic %[tmp], %[tmp], #0x40
" "msr CPSR_c, %[tmp]
" : [tmp]"=r"(tmp)); #endif } #endif 

核心是 cpsie i 和 cpsid i 这两个指令。

cps全称change processor state,即改变PRIMASK这个寄存器值

ie: interrupt enable. 中断使能,即PRIMASK.PM设置为0

id: interrupt disable. 中断关闭,即PRIMASK.PM设置为1

__enable_irq()函数调用cpsie i指令。

__disable_irq()函数除调用cpsid i 指令,同时返回了PRIMASK的值,即如果返回值为 0,则表示中断在调用该函数之前是使能的;如果返回值为1,则表示中断在调用函数之前是禁用的。

需要注意的是:如果之前开启了相关外设的中断功能,在调用__disable_irq()函数关中断后,这时如果有中断触发,那么不会去进行中断响应。但是在调用__enable_irq()开启中断后,MCU会立即处理之前触发的中断。这说明__disable_irq()只是禁止CPU去响应中断,没有真正的去屏蔽中断的触发,当中断发生后,相应的寄存器会将中断标志置位,在__enable_irq()开启中断后,由于相应的中断标志没有清空,因而还会触发中断。

以下述代码为例,程序中使用了一个GPIO中断,当按键按下时翻转一次LED。实际测试如果在调用__disable_irq()后、__enable_irq()之前的这3s时间内按下按键,并不会进入中断翻转LED,虽然这时中断标志位已经产生了。

但是调用__enable_irq()之后就会立刻进入到中断服务函数中。

int main(void)
{      
    /* 配置系统时钟 */
    system_clock_config(); 

    /* Systick初始化 */
    std_delay_init();

    /* LED初始化 */
    led_init();

    /* EXTI初始化 */
    exti_init();
    
    __disable_irq();
   
    std_delayms(3000);
        
    __enable_irq(); while (1)
    {

    } 
}

/**
* @brief  EXTI4_15中断服务函数
* @retval 无
*/
void EXTI4_15_IRQHandler(void)
{
    /* 读取EXTI通道中断挂起状态 */ if (std_exti_get_pending_status(EXTI_LINE_GPIO_PIN13))
    {
        /* 清除EXTI通道中断挂起状态 */
        std_exti_clear_pending(EXTI_LINE_GPIO_PIN13);
        LED1_TOGGLE();
    }
}

说到这里你可能还注意到还有__NVIC_DisableIRQ(IRQn_Type IRQn)、__NVIC_EnableIRQ(IRQn_Type IRQn) 这俩函数

/**
  rief   Disable Interrupt
  details Disables a device specific interrupt in the NVIC interrupt controller.
  param [in]      IRQn  Device specific interrupt number.
  
ote    IRQn must not be negative.
 */
__STATIC_INLINE void __NVIC_DisableIRQ(IRQn_Type IRQn)
{ if ((int32_t)(IRQn) >= 0)
  {
    NVIC->ICER[0U] = (uint32_t)(1UL << (((uint32_t)IRQn) & 0x1FUL)); __DSB(); __ISB(); } }
/**
  rief   Enable Interrupt
  details Enables a device specific interrupt in the NVIC interrupt controller.
  param [in]      IRQn  Device specific interrupt number.
  
ote    IRQn must not be negative.
 */
__STATIC_INLINE void __NVIC_EnableIRQ(IRQn_Type IRQn)
{ if ((int32_t)(IRQn) >= 0)
  {
    NVIC->ISER[0U] = (uint32_t)(1UL << (((uint32_t)IRQn) & 0x1FUL)); } }

这俩函数和上述函数的区别是,上面的两个函数是开关全局的中断,这俩函数是针对某特定的中断。

但是有一点相同的是,如果在调用__NVIC_DisableIRQ之后发生了中断事件,当调用__NVIC_EnableIRQ(IRQn_Type IRQn)之后还是会进入到中断处理。

综上disable函数只是不响应中断,并不会影响中断的产生,在disable状态下如果发生中断则会挂起,等到enable后满足条件还是会被执行。如果不希望此现象发生,那么需要再enable前清除掉相关外设模块中断挂起请求标志。

如果想真正禁止中断的产生的话,还得从源头上配置相关外设的寄存器关掉中断才行。


文章来源于:21IC    原文链接
本站所有转载文章系出于传递更多信息之目的,且明确注明来源,不希望被转载的媒体或个人可与我们联系,我们将立即进行删除处理。

我们与500+贴片厂合作,完美满足客户的定制需求。为品牌提供定制化的推广方案、专属产品特色页,多渠道推广,SEM/SEO精准营销以及与公众号的联合推广...详细>>

利用葫芦芯平台的卓越技术服务和新产品推广能力,原厂代理能轻松打入消费物联网(IOT)、信息与通信(ICT)、汽车及新能源汽车、工业自动化及工业物联网、装备及功率电子...详细>>

充分利用其强大的电子元器件采购流量,创新性地为这些物料提供了一个全新的窗口。我们的高效数字营销技术,不仅可以助你轻松识别与连接到需求方,更能够极大地提高“闲置物料”的处理能力,通过葫芦芯平台...详细>>

我们的目标很明确:构建一个全方位的半导体产业生态系统。成为一家全球领先的半导体互联网生态公司。目前,我们已成功打造了智能汽车、智能家居、大健康医疗、机器人和材料等五大生态领域。更为重要的是...详细>>

我们深知加工与定制类服务商的价值和重要性,因此,我们倾力为您提供最顶尖的营销资源。在我们的平台上,您可以直接接触到100万的研发工程师和采购工程师,以及10万的活跃客户群体...详细>>

凭借我们强大的专业流量和尖端的互联网数字营销技术,我们承诺为原厂提供免费的产品资料推广服务。无论是最新的资讯、技术动态还是创新产品,都可以通过我们的平台迅速传达给目标客户...详细>>

我们不止于将线索转化为潜在客户。葫芦芯平台致力于形成业务闭环,从引流、宣传到最终销售,全程跟进,确保每一个potential lead都得到妥善处理,从而大幅提高转化率。不仅如此...详细>>