前段時間一工程師向我咨詢了一個問題,問我為什么他的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_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設置為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文章
17918瀏覽量
362811 -
寄存器
+關注
關注
31文章
5430瀏覽量
123965 -
函數
+關注
關注
3文章
4377瀏覽量
64556 -
編譯器
+關注
關注
1文章
1659瀏覽量
50059
原文標題:__disable_irq() 和 __enable_irq()定義在哪?
文章出處:【微信號:TopSemic,微信公眾號:TopSemic嵌入式】歡迎添加關注!文章轉載請注明出處。
發布評論請先 登錄
cmsis_armcc.h沒有實現__disable_irq函數接口,還有別的函數嗎?
STM32F0 IAP進入APP后 __disable_irq() 失效了的原因?
stm32f0讀寫內部flash和刷新液晶屏時,禁止所有中斷,__disable_irq();不起作用,還會進入中斷響應函數為什么?
TC387中有__disable_interrupts()關閉所有中斷和打開所有中斷的函數或宏嗎?
stm32 Cortex M3內核 ,CPU調用__disable_irq函數關閉中斷后,為何還能接收到中斷????????
請問除了__disable_irq();__enable_irq()之外還有其他暫時屏蔽中斷嗎?
請問ch32v103如何使用全局中斷?
STM32使用__disable_irq()后就無法使用HAL_Delay(xx),這是為什么?
逐步認識中斷請求IRQ
Linux中斷(interrupt)子系統之一:驅動程序接口層和中斷通用邏輯層

6.分析request_irq和free_irq函數如何注冊注銷中斷(詳解)

2.單片機flash操作注意事項

控制IRQ和FIQ中斷的編譯器內部函數 - 基于Keil MDK

IRQ domain支持幾種映射方式

評論