0
  • 聊天消息
  • 系统消息
  • 评论与回复
登录后你可以
  • 下载海量资料
  • 学习在线课程
  • 观看技术视频
  • 写文章/发帖/加入社区
会员中心
创作中心

完善资料让更多小伙伴认识你,还能领取20积分哦,立即完善>

3天内不再提示

__disable_irq()和__enable_irq()函数的具体定义

TopSemic嵌入式 来源:TopSemic嵌入式 2024-08-29 10:06 次阅读

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

wKgZombP18aAKIgJAACAC6ifUtA639.png

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

wKgaombP19iAKZCIAAAnqTdnJps986.png

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

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

static__inline__unsignedint__attribute__((__always_inline__,__nodebug__))
__disable_irq(void){
unsignedintcpsr;
#if__ARM_ARCH>=6
#ifdefined(__ARM_ARCH_PROFILE)&&__ARM_ARCH_PROFILE=='M'
__asm____volatile__("mrs%[cpsr],primask
"
"cpsidi
"
:[cpsr]"=r"(cpsr));
returncpsr&0x1;
#else/*!defined(__ARM_ARCH_PROFILE)||__ARM_ARCH_PROFILE!='M'*/
__asm____volatile__("mrs%[cpsr],cpsr
"
"cpsidi
"
:[cpsr]"=r"(cpsr));
returncpsr&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(
"intrinsicnotsupportedforthisarchitecture")))__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__("cpsief");
#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

3dd39db1f6db39a0cd121d329fe74de5.png

3dd39db1f6db39a0cd121d329fe74de5.png

__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,虽然这时中断标志位已经产生了。

e0ec29af41084f964c2f2593534e1382.png

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

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

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

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

/*EXTI初始化*/
exti_init();

__disable_irq();

std_delayms(3000);

__enable_irq();

while(1)
{

}
}

/**
*@briefEXTI4_15中断服务函数
*@retval无
*/
voidEXTI4_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) 这俩函数

/**
riefDisableInterrupt
detailsDisablesadevicespecificinterruptintheNVICinterruptcontroller.
param[in]IRQnDevicespecificinterruptnumber.

oteIRQnmustnotbenegative.
*/
__STATIC_INLINEvoid__NVIC_DisableIRQ(IRQn_TypeIRQn)
{
if((int32_t)(IRQn)>=0)
{
NVIC->ICER[0U]=(uint32_t)(1UL<< (((uint32_t)IRQn) & 0x1FUL));
    __DSB();
    __ISB();
  }
}
/**
riefEnableInterrupt
detailsEnablesadevicespecificinterruptintheNVICinterruptcontroller.
param[in]IRQnDevicespecificinterruptnumber.

oteIRQnmustnotbenegative.
*/
__STATIC_INLINEvoid__NVIC_EnableIRQ(IRQn_TypeIRQn)
{
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前清除掉相关外设模块中断挂起请求标志。

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

声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉
  • mcu
    mcu
    +关注

    关注

    146

    文章

    17141

    浏览量

    351070
  • 寄存器
    +关注

    关注

    31

    文章

    5342

    浏览量

    120277
  • 函数
    +关注

    关注

    3

    文章

    4329

    浏览量

    62584
  • 编译器
    +关注

    关注

    1

    文章

    1629

    浏览量

    49115

原文标题:__disable_irq() 和 __enable_irq()定义在哪?

文章出处:【微信号:TopSemic,微信公众号:TopSemic嵌入式】欢迎添加关注!文章转载请注明出处。

收藏 人收藏

    评论

    相关推荐

    cmsis_armcc.h没有实现__disable_irq函数接口,还有别的函数吗?

    cmsis_armcc.h没有实现__disable_irq函数接口,还有别的函数吗?除了__NVIC_DisableIRQ
    发表于 04-02 08:25

    STM32F0 IAP进入APP后 __disable_irq() 失效了的原因?

    如题,STM32F030C8T6 做 IAP 现能正确的从 bootloader 中跳转到 APP,在APP中也能正常的进入中断,为什么在APP中使用 __disable_irq();无法关闭 所有中断。 经测试 在bootloader中__disable_irq();是
    发表于 04-22 08:21

    stm32f0读写内部flash和刷新液晶屏时,禁止所有中断,__disable_irq();不起作用,还会进入中断响应函数为什么?

    读写内部flash和刷新液晶屏时,禁止所有中断,__disable_irq();不起作用,还会进入中断响应函数
    发表于 05-09 06:41

    TC387中有__disable_interrupts()关闭所有中断和打开所有中断的函数或宏吗?

    TC387 芯片中有__disable_irq()和__enable_irq()关闭所有中断和打开所有中断的函数或宏吗,请告知
    发表于 08-01 08:22

    Nios PIO中断---“implicit declaration of function 'alt_irq_register”警告

    使能中断增强版的中断API提供如下函数:alt_ic_irq_disable(),alt_ic_irq_enable(),alt_ic_irq
    发表于 03-08 20:42

    stm32 Cortex M3内核 ,CPU调用__disable_irq函数关闭中断后,为何还能接收到中断????????

    Cortex M3内核 ,CPU调用__disable_irq函数关闭中断后,进入睡眠模式,并且调用__WFI()函数,等待中断,结果当有外部中断进入时,CPU唤醒。跪求大神指教这是什么原理,关闭中断后,等待中断,然后中断来了,
    发表于 05-25 11:41

    请问除了__disable_irq();__enable_irq()之外还有其他暂时屏蔽中断吗?

    我在设计中要利用__disable_irq();__enable_irq()来实现原子操作,防止中断与正常运行程序同时访问数据造成不一致,但是这个是暂时屏蔽全局中断的。我系统中有个最高优先级的是不能
    发表于 08-27 10:15

    设备树中GIC中断控制器节点

    irq_handler_tirqreturn_t(2)flags2、free_irq()函数3、enable_irq()函数4、
    发表于 01-10 07:37

    请问ch32v103如何使用全局中断?

    在使用arm芯片时有叫做__disable_irq();和__enable_irq();的全局中断方法,请问riscv里面要如何使用这种全局中断?
    发表于 06-02 07:14

    STM32使用__disable_irq()后就无法使用HAL_Delay(xx),这是为什么?

    __enable_irq();//打开所有中断但实际应用中发现,当使用__disable_irq(); 关闭所有中断后,再用__enable_irq();打开所有中断,就会出现HAL_Delay(xx)不能再使用,所以直接用上述
    发表于 12-03 09:09

    逐步认识中断请求IRQ

    一、了解IRQ家族??IRQ全称为Interrupt Request,即是“中断请求”的意思(以下使用IRQ称呼)。IRQ的作
    发表于 04-17 22:51 2093次阅读

    Linux中断(interrupt)子系统之一:驱动程序接口层和中断通用逻辑层

    这两个API应该配对使用,disable_irq可以被多次嵌套调用,要想重新打开irqenable_irq必须也要被调用同样的次数,为此,irq_desc结构中的depth字段专门用
    发表于 05-15 11:28 501次阅读
    Linux中断(interrupt)子系统之一:驱动程序接口层和中断通用逻辑层

    6.分析request_irq和free_irq函数如何注册注销中断(详解)

    上一节讲了如何实现运行中断,这些都是系统给做好的,当我们想自己写个中断处理程序,去执行自己的代码,就需要写irq_desc->action->handler,然后通过
    发表于 11-30 18:36 13次下载
    6.分析request_<b class='flag-5'>irq</b>和free_<b class='flag-5'>irq</b><b class='flag-5'>函数</b>如何注册注销中断(详解)

    2.单片机flash操作注意事项

    (); //关闭中断//////////////////////if(a) __disable_irq(); else __enable_irq();2.创建备份区降低擦写时掉电数据丢失的风险...
    发表于 12-01 20:51 9次下载
    2.单片机flash操作注意事项

    控制IRQ和FIQ中断的编译器内部函数 - 基于Keil MDK

    编译器内部函数__disable_irq、__enable_irq、__disable_fiq和__enable_fiq用于控制
    发表于 01-26 17:16 1次下载
    控制<b class='flag-5'>IRQ</b>和FIQ中断的编译器内部<b class='flag-5'>函数</b> - 基于Keil MDK