我們在使用ARM Cortex-M內(nèi)核芯片進(jìn)行產(chǎn)品開發(fā)時,有時可能需要暫時開辟一個相對清靜、不被打擾的程序執(zhí)行環(huán)境,以確保某些操作可靠順利完成。這時我們往往會使用所謂開、關(guān)總中斷的指令代碼來完成。我們可以對特殊功能寄存器PRIMASK寫1來關(guān)閉/屏蔽優(yōu)先級不高于0【數(shù)字大于0】的所有可配置中斷的中斷響應(yīng)。對其寫0,放棄關(guān)閉/屏蔽功能,即所謂的開總中斷。
關(guān)于使用PRIMASK寄存器關(guān)閉/屏蔽所有可配置中斷的做法還有其它等效操作,比如使用CPSID指令和CPSIE指令或調(diào)用相關(guān)CMSIS函數(shù)。【如下圖所示,左邊是匯編指令,右邊是對應(yīng)CMSIS函數(shù)】
不過,在實際應(yīng)用中很多人對這這個關(guān)、開總中斷操作有些誤解,尤其對關(guān)總中斷誤解更甚。以為關(guān)總中斷就在NVIC那里關(guān)閉了所有中斷響應(yīng)允許,或者說連外設(shè)端的中斷請求的使能也關(guān)閉了,中斷請求事件標(biāo)志也無效了,其實并不是這樣的。若出于誤解而使用開、關(guān)總中斷指令或函數(shù)往往就得不到自己想要的結(jié)果、或者結(jié)果讓人困惑不解。
下面基于Cortex -M4內(nèi)核的STM32L4芯片做些驗證。這里我們拿STM32L476開發(fā)板驗證下相關(guān)內(nèi)容【具體使用哪款STM32芯片不重要】。
我這里開啟片內(nèi)TIM1/TIM2/TIM3/TIM4四個定時器的更新事件中斷,其中TIM1/TIM2/TIM3的時間參數(shù)完全一樣,而TIM4跟前三者相比,除了溢出時間參數(shù)【就是ARR】稍微小一點外,其它配置一樣,四者同時啟動,并確保讓TIM4一定先進(jìn)入中斷服務(wù)程序[即ISR]。
它們的中斷優(yōu)先級設(shè)置各不相同,TIM4的最低。現(xiàn)在假設(shè)TIM4中斷服務(wù)程序里要執(zhí)行一段特別重要的事情,通過代碼設(shè)計保證CPU剛進(jìn)入TIM4中斷服務(wù)程序時另外3個定時器一定還沒有觸發(fā)更新事件。為了便于測試,我也確保啟動程序后每個TIMER都只產(chǎn)生一次更新事件。他們的優(yōu)先級配置如下圖所示【此處優(yōu)先級高4位全部用作搶占優(yōu)先級,子優(yōu)先級都一樣,不做配置】:
上面各中斷優(yōu)先級的數(shù)字是基于優(yōu)先級寄存器的高4位而言的,顯然TIM4的優(yōu)先級最低【數(shù)字越大優(yōu)先級越低】。我在CPU進(jìn)入TIM4 ISR時立即調(diào)用所謂的關(guān)閉總中斷函數(shù),然后一段時間后才調(diào)用所謂的打開總中斷的函數(shù),確保TIM4 ISR執(zhí)行完畢之前另外3個TIMER的更新中斷請求都產(chǎn)生了。
我在每個中斷ISR里執(zhí)行一條輸提示,基于上面的測試代碼我們看看輸出結(jié)果【我借助于RTC把執(zhí)行各中斷的時間點也輸出了】:
盡管在TIM4中斷里一開始就調(diào)用了disable_irq()函數(shù),可是,它并沒有關(guān)閉TIM1/TIM2/TIM3的中斷請求和后續(xù)響應(yīng)。從上圖不難看出,TIM4/TIM1/TIM2/TIM3依次得到響應(yīng)執(zhí)行。怎么感覺那個關(guān)閉總中斷的函數(shù)啥也沒關(guān)掉呢?這也正是被很多人誤會的地方。
其實,這里調(diào)用disable_irq()函數(shù)的目的就是關(guān)閉/屏蔽所有優(yōu)先級不高于0的可配置中斷的響應(yīng),以保障當(dāng)前TIM4 ISR的順暢執(zhí)行。這就相當(dāng)于將當(dāng)前TIM4 的中斷優(yōu)先級從4提升到0了。因此,讓原本優(yōu)先級高于TIM4的TIM1/TIM2/TIM3中斷優(yōu)先級反而低于當(dāng)前TIM4ISR的執(zhí)行優(yōu)先級,即使在TIM4 ISR過程中發(fā)生了TIM1/TIM2/TIM3中斷請求也沒法得到響應(yīng)。
而且,調(diào)用disable_irq()函數(shù)并不對被關(guān)閉/屏蔽中斷的原有參數(shù)和配置做任何改變。什么意思呢?那些暫時被屏蔽中斷的中斷響應(yīng)允許位、中斷請求使能位以及觸發(fā)事件標(biāo)志等都不會因暫時被屏蔽而發(fā)生改變。同樣,enable_irq()函數(shù)也不改變之前被屏蔽中斷的原有參數(shù)和配置,它只是放棄剛才的屏蔽功能【或說優(yōu)先級提升功能】。所以,當(dāng)執(zhí)行enable_irq()函數(shù)后,那些一度被屏蔽/關(guān)閉的中斷請求都會按照之前各自優(yōu)先級而被CPU響應(yīng)執(zhí)行。
我們可以在TIM4 ISR的適當(dāng)位置后打上斷點【斷點處4個TIMER的更新事件一定都產(chǎn)生了】看看各個中斷的響應(yīng)情況:【詳見下圖】
圖中E、P、A是下方Eable/Pending/Active單詞的首字母。Enable表示中斷請求是否在NVIC端得到響應(yīng)允許;Pending表示中斷請求等待CPU的響應(yīng)執(zhí)行;Active表示中斷服務(wù)程序正在被執(zhí)行。
顯然,只有TIM4服務(wù)程序在被執(zhí)行中,其它3個都是掛起狀態(tài),等待被響應(yīng)。也就是說disable_irq()函數(shù)并不直接影響別的中斷請求的產(chǎn)生和響應(yīng)掛起狀態(tài),它通過提升當(dāng)前執(zhí)行程序的優(yōu)先級變相地實現(xiàn)了屏蔽/關(guān)閉其它優(yōu)先級不高于0的中斷請求的響應(yīng),即這個關(guān)閉是從執(zhí)行效果上來說的,相當(dāng)于變相提升了中斷響應(yīng)門檻;enable_irq()函數(shù)相當(dāng)于將提升的門檻拿走恢復(fù)原貌。
如果其它參數(shù)都不變,我注釋掉TIM4 ISR里的關(guān)閉總中斷的代碼,看看運行結(jié)果會怎么樣呢?【見下圖中TIM4 ISR 代碼和輸出結(jié)果】
雖然代碼保障了TIM4最先進(jìn)入中斷服務(wù)程序,但由于其它幾個更高優(yōu)先級的更新事件隨即產(chǎn)生而發(fā)生搶占,反而TIM4最后完成中斷執(zhí)行。我們可以觀察上面輸出順序及記錄的時間點,請?zhí)貏e留意TIM3 ISR與 TIM4 ISR的輸出時間點是相同的,因分辨率問題時間太接近沒區(qū)分開來。不過這反而可以更清晰地看出TIM4被搶占了,TIM3一執(zhí)行完馬上回到TIM4的輸出【這個結(jié)果跟TIM4的被搶占時間點密切相關(guān)】。
如果說,我在上面配置和代碼基礎(chǔ)上,進(jìn)入TIM4 ISR后依然先關(guān)閉總中斷,在打開總中斷前對TIM1/TIM2/TIM3的幾個更新事件標(biāo)志做清零,結(jié)果會怎么樣呢?【相信很多人想看看這個結(jié)果,不妨先心里猜測下。】
基于上面調(diào)整后的代碼,編譯運行。不論我怎么反復(fù)點擊運行,上圖右邊的輸出結(jié)果紋絲不動?這個結(jié)果是正確、合理的嗎?符合你心里預(yù)期否?
我這里清除的只是外設(shè)端的中斷請求事件標(biāo)志,怎么早已生效的中斷請求就消失了呢?結(jié)合前面的分析,在TIM4 ISR里運行延時程序就是為了確保另外的TIM1/TIM2/TIM3的中斷請求得以生效,請求生效后并會在NVIC的中斷響應(yīng)掛起寄存器的相應(yīng)位置1,等待執(zhí)行。
這是怎么回事呢?原理上說不通啊?
其實,我們看到的只是表象。真相是TIM1/TIM2/TIM3的ISR都得到執(zhí)行了,為什么沒看到TIM1/TIM2/TIM3 ISR運行的結(jié)果輸出呢?這跟我們ISR代碼處理有關(guān)。
首先可以肯定,這里做中斷請求事件標(biāo)志清零時,它們3個中斷請求早就生效并處于響應(yīng)掛起狀態(tài)。對事件標(biāo)志清零也不會影響到NVIC的中斷掛起位的。當(dāng)TIM4 ISR執(zhí)行完畢后,TIM1/TIM2/TIM3照樣基于優(yōu)先級高低相繼運行各自ISR。但有個問題,就是各自的ISR代碼里都會檢測更新事件標(biāo)志,由于該標(biāo)志在TIM4 ISR早就被做了清除而成為無效標(biāo)志,所以在它們3個各自ISR里因檢測到事件標(biāo)志無效就都沒有繼續(xù)往下運行而提前退場,說直接點就是沒有運行到結(jié)果輸出代碼就返回了。
既然這樣,我不妨將輸出結(jié)果的代碼放在各自ISR的入口處,免掉檢查標(biāo)志位這個環(huán)節(jié),然后我們繼續(xù)看看結(jié)果。【結(jié)果輸出在各自更新中斷回調(diào)函數(shù)里完成。修改后的中斷代碼如下圖所示:】
基于上面的代碼調(diào)整,再看看結(jié)果輸出:
這個輸出結(jié)果就跟我們分析和預(yù)期的一致。通過這個實驗表明,對于在NVIC端生效處于響應(yīng)掛起的中斷請求,只是清除相應(yīng)的事件標(biāo)志是不會影響它的后續(xù)執(zhí)行的。如果我在上面TIM4 ISR里面執(zhí)行開啟總中斷操作前希望處于掛起狀態(tài)的TIM1/TIM2/TIM3中斷請求不要再被執(zhí)行了,那要如何操作呢?
首先,清除請求事件標(biāo)志是應(yīng)該的,而且還要清除它們各自的NVIC端的中斷響應(yīng)掛起位。我們可以通過調(diào)用__NVIC_ClearPendingIRQ(IRQn)函數(shù)對特定中斷響應(yīng)掛起位清零。
對TIM4ISR代碼再稍加調(diào)整【見上圖】,運行后就只能看到TIM4 ISR的輸出結(jié)果了。其它3個中斷請求半路被TIM4 ISR給清除了,即使再開總中斷也于事無補(bǔ)。
關(guān)于被誤解的開、關(guān)總中斷的話題,拉拉扯扯不知不覺也聊了這么多。所謂關(guān)總中斷,實質(zhì)上將當(dāng)前執(zhí)行程序的優(yōu)先級提升到0,變相提高了期望打斷當(dāng)前執(zhí)行程序的中斷事件的響應(yīng)門檻,開總中斷就是取消對當(dāng)前執(zhí)行程序的優(yōu)先級提升,恢復(fù)原貌。這里順便貼出幾個常用的有關(guān)中斷響應(yīng)的CMSIS函數(shù)供參考備忘。
今天的分享就到這里,愿本文的分享能帶給您一些收獲。祝君好運。下次再聊。
哦,最后補(bǔ)充下,文中的串口終端輸出結(jié)果,我是通過CPU直接向UART外設(shè)的數(shù)據(jù)寄存器輪詢式寫數(shù)據(jù)實現(xiàn)的。本公眾號文字力求不拖泥帶水,特意提醒這句給有心人。
審核編輯:湯梓紅
-
ARM
+關(guān)注
關(guān)注
134文章
9084瀏覽量
367384 -
寄存器
+關(guān)注
關(guān)注
31文章
5336瀏覽量
120230 -
內(nèi)核
+關(guān)注
關(guān)注
3文章
1372瀏覽量
40277 -
中斷
+關(guān)注
關(guān)注
5文章
898瀏覽量
41470 -
Cortex-M
+關(guān)注
關(guān)注
2文章
229瀏覽量
29752
原文標(biāo)題:常被誤解的開、關(guān)總中斷話題
文章出處:【微信號:stmcu832,微信公眾號:茶話MCU】歡迎添加關(guān)注!文章轉(zhuǎn)載請注明出處。
發(fā)布評論請先 登錄
相關(guān)推薦
評論