前段时间一工程师向我咨询了一个问题,问我为什么他的MCU KEIL工程代码里没有找到__disable_irq() 和 __enable_irq()的具体定义,是不是有问题。
直接在工程里搜索,确实只能在cmsis_armcc.h文件里看到下面的两处注释说明,并没有这俩函数的具体定义。
可是如果直接去调用这俩函数的话,编译又不会报错,那么这俩函数的定义到底在哪呢?
__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
__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()之后就会立刻进入到中断服务函数中。
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
+关注
关注
146文章
17141浏览量
351070 -
寄存器
+关注
关注
31文章
5342浏览量
120277 -
函数
+关注
关注
3文章
4329浏览量
62584 -
编译器
+关注
关注
1文章
1629浏览量
49115
原文标题:__disable_irq() 和 __enable_irq()定义在哪?
文章出处:【微信号:TopSemic,微信公众号:TopSemic嵌入式】欢迎添加关注!文章转载请注明出处。
发布评论请先 登录
相关推荐
评论