前段時間一工程師向我咨詢了一個問題,問我為什么他的MCU KEIL工程代碼里沒有找到__disable_irq() 和 __enable_irq()的具體定義,是不是有問題。

直接在工程里搜索,確實只能在cmsis_armcc.h文件里看到下面的兩處注釋說明,并沒有這倆函數(shù)的具體定義。

可是如果直接去調(diào)用這倆函數(shù)的話,編譯又不會報錯,那么這倆函數(shù)的定義到底在哪呢?
__disable_irq() 和 __enable_irq() 是所謂的intrinsic函數(shù),編譯器自動識別并替換為相關(guān)的指令,它們其實是編譯器的一部分,實際的定義位于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_ARCH6?*/ ??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_ARCH6?*/ ??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設(shè)置為0
id: interrupt disable. 中斷關(guān)閉,即PRIMASK.PM設(shè)置為1


__enable_irq()函數(shù)調(diào)用cpsie i指令。
__disable_irq()函數(shù)除調(diào)用cpsid i 指令,同時返回了PRIMASK的值,即如果返回值為 0,則表示中斷在調(diào)用該函數(shù)之前是使能的;如果返回值為1,則表示中斷在調(diào)用函數(shù)之前是禁用的。
需要注意的是:如果之前開啟了相關(guān)外設(shè)的中斷功能,在調(diào)用__disable_irq()函數(shù)關(guān)中斷后,這時如果有中斷觸發(fā),那么不會去進(jìn)行中斷響應(yīng)。但是在調(diào)用__enable_irq()開啟中斷后,MCU會立即處理之前觸發(fā)的中斷。這說明__disable_irq()只是禁止CPU去響應(yīng)中斷,沒有真正的去屏蔽中斷的觸發(fā),當(dāng)中斷發(fā)生后,相應(yīng)的寄存器會將中斷標(biāo)志置位,在__enable_irq()開啟中斷后,由于相應(yīng)的中斷標(biāo)志沒有清空,因而還會觸發(fā)中斷。
以下述代碼為例,程序中使用了一個GPIO中斷,當(dāng)按鍵按下時翻轉(zhuǎn)一次LED。實際測試如果在調(diào)用__disable_irq()后、__enable_irq()之前的這3s時間內(nèi)按下按鍵,并不會進(jìn)入中斷翻轉(zhuǎn)LED,雖然這時中斷標(biāo)志位已經(jīng)產(chǎn)生了。

但是調(diào)用__enable_irq()之后就會立刻進(jìn)入到中斷服務(wù)函數(shù)中。
intmain(void)
{
/*配置系統(tǒng)時鐘*/
system_clock_config();
/*Systick初始化*/
std_delay_init();
/*LED初始化*/
led_init();
/*EXTI初始化*/
exti_init();
__disable_irq();
std_delayms(3000);
__enable_irq();
while(1)
{
}
}
/**
*@briefEXTI4_15中斷服務(wù)函數(shù)
*@retval無
*/
voidEXTI4_15_IRQHandler(void)
{
/*讀取EXTI通道中斷掛起狀態(tài)*/
if(std_exti_get_pending_status(EXTI_LINE_GPIO_PIN13))
{
/*清除EXTI通道中斷掛起狀態(tài)*/
std_exti_clear_pending(EXTI_LINE_GPIO_PIN13);
LED1_TOGGLE();
}
}
說到這里你可能還注意到還有__NVIC_DisableIRQ(IRQn_Type IRQn)、__NVIC_EnableIRQ(IRQn_Type IRQn) 這倆函數(shù)
/** 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));
??}
}
這倆函數(shù)和上述函數(shù)的區(qū)別是,上面的兩個函數(shù)是開關(guān)全局的中斷,這倆函數(shù)是針對某特定的中斷。
但是有一點相同的是,如果在調(diào)用__NVIC_DisableIRQ之后發(fā)生了中斷事件,當(dāng)調(diào)用__NVIC_EnableIRQ(IRQn_Type IRQn)之后還是會進(jìn)入到中斷處理。
綜上disable函數(shù)只是不響應(yīng)中斷,并不會影響中斷的產(chǎn)生,在disable狀態(tài)下如果發(fā)生中斷則會掛起,等到enable后滿足條件還是會被執(zhí)行。如果不希望此現(xiàn)象發(fā)生,那么需要再enable前清除掉相關(guān)外設(shè)模塊中斷掛起請求標(biāo)志。
如果想真正禁止中斷的產(chǎn)生的話,還得從源頭上配置相關(guān)外設(shè)的寄存器關(guān)掉中斷才行。
-
mcu
+關(guān)注
關(guān)注
147文章
18946瀏覽量
398783 -
寄存器
+關(guān)注
關(guān)注
31文章
5609瀏覽量
130036 -
函數(shù)
+關(guān)注
關(guān)注
3文章
4417瀏覽量
67565 -
編譯器
+關(guān)注
關(guān)注
1文章
1672瀏覽量
51680
原文標(biāo)題:__disable_irq() 和 __enable_irq()定義在哪?
文章出處:【微信號:TopSemic,微信公眾號:TopSemic嵌入式】歡迎添加關(guān)注!文章轉(zhuǎn)載請注明出處。
發(fā)布評論請先 登錄
cmsis_armcc.h沒有實現(xiàn)__disable_irq函數(shù)接口,還有別的函數(shù)嗎?
STM32F0 IAP進(jìn)入APP后 __disable_irq() 失效了的原因?
stm32f0讀寫內(nèi)部flash和刷新液晶屏?xí)r,禁止所有中斷,__disable_irq();不起作用,還會進(jìn)入中斷響應(yīng)函數(shù)為什么?
TC387中有__disable_interrupts()關(guān)閉所有中斷和打開所有中斷的函數(shù)或宏嗎?
stm32 Cortex M3內(nèi)核 ,CPU調(diào)用__disable_irq函數(shù)關(guān)閉中斷后,為何還能接收到中斷????????
請問除了__disable_irq();__enable_irq()之外還有其他暫時屏蔽中斷嗎?
設(shè)備樹中GIC中斷控制器節(jié)點
請問ch32v103如何使用全局中斷?
STM32使用__disable_irq()后就無法使用HAL_Delay(xx),這是為什么?
逐步認(rèn)識中斷請求IRQ
Linux中斷(interrupt)子系統(tǒng)之一:驅(qū)動程序接口層和中斷通用邏輯層
6.分析request_irq和free_irq函數(shù)如何注冊注銷中斷(詳解)
2.單片機flash操作注意事項
控制IRQ和FIQ中斷的編譯器內(nèi)部函數(shù) - 基于Keil MDK
IRQ domain支持幾種映射方式
__disable_irq()和__enable_irq()函數(shù)的具體定義
評論