我們知道在 MCU 裸機(jī)中程序代碼之所以能完成多任務(wù)并行實(shí)時(shí)處理功能,其實(shí)主要是靠中斷來調(diào)度的,沒有中斷,CPU 就只能按順序"呆板"地執(zhí)行代碼。很多人都說是中斷能力賦予了 MCU 真正的靈魂,能正確認(rèn)識和熟練使用 MCU 中斷,基本上就算玩熟了這顆 MCU。
之前寫過一篇《中斷處理函數(shù)(IRQHandler)的標(biāo)準(zhǔn)流程》,里面詳細(xì)講了中斷處理函數(shù)里的標(biāo)準(zhǔn)代碼流程與寫法,這篇文章可讓大家對 MCU 里的中斷用法有個初步認(rèn)識。今天以 ARM Cortex-M 內(nèi)核 MCU 為例再來介紹下業(yè)界標(biāo)準(zhǔn)的三重中斷控制設(shè)計(jì):
一、外設(shè)事件中斷控制
MCU 中最底層的中斷控制針對的是外設(shè)里某個具體的事件,這個控制來自于外設(shè)模塊本身,以恩智浦 i.MXRT 系列 MCU 的 GPT 定時(shí)器模塊為例。如下是 GPT 模塊寄存器列表,你可以發(fā)現(xiàn)其中有經(jīng)典的 IR 和 SR 寄存器,SR 是事件狀態(tài)寄存器,IR 是中斷事件控制寄存器:
GPT 定時(shí)器一旦被使能,其運(yùn)行狀態(tài)(一共支持 6 個事件:超時(shí)、輸入捕獲 x 2ch、比較輸出 x 3ch)都會實(shí)時(shí)記錄在 SR 寄存器中,如果不在 IR 寄存器中將事件中斷開啟(默認(rèn)是關(guān)閉的),那么就需要用戶在代碼里手動去查詢 SR 寄存器置起的事件標(biāo)志位以處理對應(yīng)事件。
Note:SR 寄存器中置起的事件標(biāo)志位需要在事件處理前手動清除掉。如果標(biāo)志位不及時(shí)清除,可能會遺漏下一次事件的處理(比如先處理當(dāng)前事件,后清除事件標(biāo)志位,那么處理事件期間再次發(fā)生的事件就會被漏掉)。如果標(biāo)志位忘了清除,同一次事件就會被處理兩次及以上。
當(dāng)然在實(shí)際應(yīng)用中,為了節(jié)省 CPU 帶寬,我們都是要開啟外設(shè)事件中斷的,MCU 廠商 SDK 包里一般都會提供相應(yīng)接口函數(shù)(取自 fsl_gpt.h):
typedefenum_gpt_interrupt_enable { kGPT_OutputCompare1InterruptEnable=GPT_IR_OF1IE_MASK, kGPT_OutputCompare2InterruptEnable=GPT_IR_OF2IE_MASK, kGPT_OutputCompare3InterruptEnable=GPT_IR_OF3IE_MASK, kGPT_InputCapture1InterruptEnable=GPT_IR_IF1IE_MASK, kGPT_InputCapture2InterruptEnable=GPT_IR_IF2IE_MASK, kGPT_RollOverFlagInterruptEnable=GPT_IR_ROVIE_MASK, }gpt_interrupt_enable_t; //開啟GPTx的xx事件中斷 staticinlinevoidGPT_EnableInterrupts(GPT_Type*base,uint32_tmask) { base->IR|=mask; } //關(guān)閉GPTx的xx事件中斷 staticinlinevoidGPT_DisableInterrupts(GPT_Type*base,uint32_tmask) { base->IR&=~mask; }
使能 GPT1 的超時(shí)事件中斷代碼示例如下:
voidperiph_int_config(void) { //初始化GPT1... GPT_Init(GPT1,&gptConfig); //... //開啟GPT1的超時(shí)事件中斷 GPT_EnableInterrupts(GPT1,kGPT_RollOverFlagInterruptEnable); }
二、外設(shè)全局中斷控制
MCU 中第二層的中斷控制針對的是整個外設(shè),這個控制來自于 Cortex-M 內(nèi)核的 NVIC 模塊。如下是 NVIC 模塊寄存器列表(取自 ARMv8-M 手冊,除了 IABRn 和 ITNSn 寄存器組外,其余寄存器適用全部的 Cortex-M 家族),其中跟中斷開關(guān)相關(guān)的是 ISER 和 ICER 寄存器:
當(dāng) MCU 中某外設(shè)(比如上一節(jié)里的 GPT)被使能后,即使其內(nèi)部事件中斷已被開啟,也不意味著系統(tǒng)中斷一定會被觸發(fā),因?yàn)?NVIC 里對于這個外設(shè)的全局中斷開關(guān)(同一外設(shè)中所有事件共享一個系統(tǒng)中斷資源,即一個中斷號)還沒有開啟。ARM CMSIS 包里提供了外設(shè)全局中斷控制函數(shù)(取自 core_cm7.h 文件):
#defineNVIC_EnableIRQ__NVIC_EnableIRQ #defineNVIC_DisableIRQ__NVIC_DisableIRQ //開啟xx外設(shè)的全局中斷 __STATIC_INLINEvoid__NVIC_EnableIRQ(IRQn_Type IRQn) { if((int32_t)(IRQn)>=0) { __COMPILER_BARRIER(); NVIC->ISER[(((uint32_t)IRQn)>>5UL)]=(uint32_t)(1UL<(((uint32_t)IRQn)&0x1FUL)); __COMPILER_BARRIER(); } } //關(guān)閉xx外設(shè)的全局中斷 __STATIC_INLINEvoid__NVIC_DisableIRQ(IRQn_Type IRQn) { if((int32_t)(IRQn)>=0) { NVIC->ICER[(((uint32_t)IRQn)>>5UL)]=(uint32_t)(1UL<(((uint32_t)IRQn)&0x1FUL)); __DSB(); __ISB(); } }
增加了使能 GPT1 的全局中斷代碼示例如下,其中 GPT1_IRQn 和 GPT1_IRQHandler 是固定名字,在 MCU 廠商提供的頭文件(MIMXRT1176_cm7.h)和啟動文件(startup_MIMXRT1176_cm7.s)里有定義。
voidperiph_int_config(void) { //初始化GPT1... GPT_Init(GPT1,&gptConfig); //... //開啟GPT1的超時(shí)事件中斷 GPT_EnableInterrupts(GPT1,kGPT_RollOverFlagInterruptEnable); //開啟GPT1的全局中斷 NVIC_EnableIRQ(GPT1_IRQn); } //GPT1的中斷響應(yīng)函數(shù) voidGPT1_IRQHandler(void) { GPT_ClearStatusFlags(GPT1,kGPT_RollOverFlagInterruptEnable); //中斷業(yè)務(wù)處理代碼 }
三、系統(tǒng)全局中斷控制
MCU 中最頂層的中斷控制針對的是整個芯片系統(tǒng),這個控制來自于 Cortex-M 內(nèi)核的 CPS 指令。如下是 CPS 指令用法(取自 ARMv7-M 手冊):
當(dāng)你想對 MCU 整個芯片的所有中斷進(jìn)行統(tǒng)一開關(guān)控制時(shí),就必須借助 CPS 指令。一般情況下開啟芯片系統(tǒng)全局中斷動作在 MCU 啟動文件里已經(jīng)做好了,所以在用戶代碼環(huán)境里常常不需要使能系統(tǒng)全局中斷的動作。如下是 IAR 環(huán)境下 i.MXRT1170 啟動文件中系統(tǒng)全局中斷操作,基于匯編指令實(shí)現(xiàn):
為了便于用戶在 C 代碼中操作系統(tǒng)全局中斷,各 IDE 下均按同樣的接口函數(shù)( __disable_irq / __enable_irq )做了封裝實(shí)現(xiàn)。IAR 環(huán)境見 IAR SystemsEmbedded Workbench 8.50.6armincciccarm_builtin.h 文件,但是封裝進(jìn)其 Lib 了,沒有暴露源碼:
#include"iccarm_builtin.h" #define__disable_irq__iar_builtin_disable_interrupt #define__enable_irq__iar_builtin_enable_interrupt
Keil 環(huán)境見 Keil_v5ARMARMCLANGincludearm_compat.h 文件,我們可以看到源碼:
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 " "cpsid i " :[cpsr]"=r"(cpsr)); returncpsr&0x1; #endif #endif } static__inline__void__attribute__((__always_inline__,__nodebug__)) __enable_irq(void){ #if__ARM_ARCH>=6 __asm__ __volatile__("cpsie i"); #endif }
最終 GPT 例程里完整的三重中斷使能代碼應(yīng)如下:
voidperiph_int_config(void) { //初始化GPT1... GPT_Init(GPT1,&gptConfig); //... //開啟GPT1的超時(shí)事件中斷 GPT_EnableInterrupts(GPT1,kGPT_RollOverFlagInterruptEnable); //開啟GPT1的全局中斷 NVIC_EnableIRQ(GPT1_IRQn); //開啟芯片系統(tǒng)全局中斷 __enable_irq(); }本文轉(zhuǎn)載自痞子衡嵌入式
審核編輯:湯梓紅
-
mcu
+關(guān)注
關(guān)注
146文章
16992瀏覽量
350314 -
嵌入式
+關(guān)注
關(guān)注
5068文章
19019瀏覽量
303293 -
寄存器
+關(guān)注
關(guān)注
31文章
5317瀏覽量
120008 -
cpu
+關(guān)注
關(guān)注
68文章
10825瀏覽量
211151 -
中斷
+關(guān)注
關(guān)注
5文章
895瀏覽量
41394
原文標(biāo)題:嵌入式MCU中標(biāo)準(zhǔn)的三重中斷控制設(shè)計(jì)
文章出處:【微信號:玩點(diǎn)嵌入式,微信公眾號:玩點(diǎn)嵌入式】歡迎添加關(guān)注!文章轉(zhuǎn)載請注明出處。
發(fā)布評論請先 登錄
相關(guān)推薦
評論