前段時(shí)間一工程師向我咨詢了一個(gè)問(wèn)題,問(wèn)我為什么他的MCU KEIL工程代碼里沒(méi)有找到__disable_irq() 和 __enable_irq()的具體定義,是不是有問(wèn)題。
直接在工程里搜索,確實(shí)只能在cmsis_armcc.h文件里看到下面的兩處注釋說(shuō)明,并沒(méi)有這倆函數(shù)的具體定義。
可是如果直接去調(diào)用這倆函數(shù)的話,編譯又不會(huì)報(bào)錯(cuò),那么這倆函數(shù)的定義到底在哪呢?
__disable_irq() 和 __enable_irq() 是所謂的intrinsic函數(shù),編譯器自動(dòng)識(shí)別并替換為相關(guān)的指令,它們其實(shí)是編譯器的一部分,實(shí)際的定義位于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 這兩個(gè)指令。
cps全稱change processor state,即改變PRIMASK這個(gè)寄存器值
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 指令,同時(shí)返回了PRIMASK的值,即如果返回值為 0,則表示中斷在調(diào)用該函數(shù)之前是使能的;如果返回值為1,則表示中斷在調(diào)用函數(shù)之前是禁用的。
需要注意的是:如果之前開(kāi)啟了相關(guān)外設(shè)的中斷功能,在調(diào)用__disable_irq()函數(shù)關(guān)中斷后,這時(shí)如果有中斷觸發(fā),那么不會(huì)去進(jìn)行中斷響應(yīng)。但是在調(diào)用__enable_irq()開(kāi)啟中斷后,MCU會(huì)立即處理之前觸發(fā)的中斷。這說(shuō)明__disable_irq()只是禁止CPU去響應(yīng)中斷,沒(méi)有真正的去屏蔽中斷的觸發(fā),當(dāng)中斷發(fā)生后,相應(yīng)的寄存器會(huì)將中斷標(biāo)志置位,在__enable_irq()開(kāi)啟中斷后,由于相應(yīng)的中斷標(biāo)志沒(méi)有清空,因而還會(huì)觸發(fā)中斷。
以下述代碼為例,程序中使用了一個(gè)GPIO中斷,當(dāng)按鍵按下時(shí)翻轉(zhuǎn)一次LED。實(shí)際測(cè)試如果在調(diào)用__disable_irq()后、__enable_irq()之前的這3s時(shí)間內(nèi)按下按鍵,并不會(huì)進(jìn)入中斷翻轉(zhuǎn)LED,雖然這時(shí)中斷標(biāo)志位已經(jīng)產(chǎn)生了。
但是調(diào)用__enable_irq()之后就會(huì)立刻進(jìn)入到中斷服務(wù)函數(shù)中。
intmain(void) { /*配置系統(tǒng)時(shí)鐘*/ 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無(wú) */ 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(); } }
說(shuō)到這里你可能還注意到還有__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ū)別是,上面的兩個(gè)函數(shù)是開(kāi)關(guān)全局的中斷,這倆函數(shù)是針對(duì)某特定的中斷。
但是有一點(diǎn)相同的是,如果在調(diào)用__NVIC_DisableIRQ之后發(fā)生了中斷事件,當(dāng)調(diào)用__NVIC_EnableIRQ(IRQn_Type IRQn)之后還是會(huì)進(jìn)入到中斷處理。
綜上disable函數(shù)只是不響應(yīng)中斷,并不會(huì)影響中斷的產(chǎn)生,在disable狀態(tài)下如果發(fā)生中斷則會(huì)掛起,等到enable后滿足條件還是會(huì)被執(zhí)行。如果不希望此現(xiàn)象發(fā)生,那么需要再enable前清除掉相關(guān)外設(shè)模塊中斷掛起請(qǐng)求標(biāo)志。
如果想真正禁止中斷的產(chǎn)生的話,還得從源頭上配置相關(guān)外設(shè)的寄存器關(guān)掉中斷才行。
-
mcu
+關(guān)注
關(guān)注
146文章
17317瀏覽量
352638 -
寄存器
+關(guān)注
關(guān)注
31文章
5363瀏覽量
121158 -
函數(shù)
+關(guān)注
關(guān)注
3文章
4346瀏覽量
62969 -
編譯器
+關(guān)注
關(guān)注
1文章
1642瀏覽量
49284
原文標(biāo)題:__disable_irq() 和 __enable_irq()定義在哪?
文章出處:【微信號(hào):TopSemic,微信公眾號(hào):TopSemic嵌入式】歡迎添加關(guān)注!文章轉(zhuǎn)載請(qǐng)注明出處。
發(fā)布評(píng)論請(qǐng)先 登錄
相關(guān)推薦
評(píng)論