你們好,我是痞子衡,是正經搞技術的痞子。今天痞子衡給你們分享的是嵌入式MCU中標準的三重中斷控制設計。html
咱們知道在 MCU 裸機中程序代碼之因此能完成多任務並行實時處理功能,其實主要是靠中斷來調度的,沒有中斷,CPU 就只能按順序"呆板"地執行代碼。不少人都說是中斷能力賦予了 MCU 真正的靈魂,能正確認識和熟練使用 MCU 中斷,基本上就算玩熟了這顆 MCU。node
痞子衡以前寫過一篇 《中斷處理函數(IRQHandler)的標準流程》,裏面詳細講了中斷處理函數裏的標準代碼流程與寫法,這篇文章可以讓你們對 MCU 裏的中斷用法有個初步認識。今天痞子衡以 ARM Cortex-M 內核 MCU 爲例再來介紹下業界標準的三重中斷控制設計:微信
MCU 中最底層的中斷控制針對的是外設裏某個具體的事件,這個控制來自於外設模塊自己,以恩智浦 i.MXRT 系列 MCU 的 GPT 定時器模塊爲例。以下是 GPT 模塊寄存器列表,你能夠發現其中有經典的 IR 和 SR 寄存器,SR 是事件狀態寄存器,IR 是中斷事件控制寄存器:函數
GPT 定時器一旦被使能,其運行狀態(一共支持 6 個事件:超時、輸入捕獲 x 2ch、比較輸出 x 3ch)都會實時記錄在 SR 寄存器中,若是不在 IR 寄存器中將事件中斷開啓(默認是關閉的),那麼就須要用戶在代碼裏手動去查詢 SR 寄存器置起的事件標誌位以處理對應事件。ui
- Note:SR 寄存器中置起的事件標誌位須要在事件處理前手動清除掉。若是標誌位不及時清除,可能會遺漏下一次事件的處理(好比先處理當前事件,後清除事件標誌位,那麼處理事件期間再次發生的事件就會被漏掉)。若是標誌位忘了清除,同一次事件就會被處理兩次及以上。
固然在實際應用中,爲了節省 CPU 帶寬,咱們都是要開啓外設事件中斷的,MCU 廠商 SDK 包裏通常都會提供相應接口函數(取自 fsl_gpt.h):操作系統
typedef enum _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 事件中斷 static inline void GPT_EnableInterrupts(GPT_Type *base, uint32_t mask) { base->IR |= mask; } // 關閉 GPTx 的 xx 事件中斷 static inline void GPT_DisableInterrupts(GPT_Type *base, uint32_t mask) { base->IR &= ~mask; }
使能 GPT1 的超時事件中斷代碼示例以下:.net
void periph_int_config(void) { // 初始化 GPT1... GPT_Init(GPT1, &gptConfig); // ... // 開啓 GPT1 的超時事件中斷 GPT_EnableInterrupts(GPT1, kGPT_RollOverFlagInterruptEnable); }
MCU 中第二層的中斷控制針對的是整個外設,這個控制來自於 Cortex-M 內核的 NVIC 模塊。以下是 NVIC 模塊寄存器列表(取自 ARMv8-M 手冊,除了 IABRn 和 ITNSn 寄存器組外,其他寄存器適用所有的 Cortex-M 家族),其中跟中斷開關相關的是 ISER 和 ICER 寄存器:debug
當 MCU 中某外設(好比上一節裏的 GPT)被使能後,即便其內部事件中斷已被開啓,也不意味着系統中斷必定會被觸發,由於 NVIC 裏對於這個外設的全局中斷開關(同一外設中全部事件共享一個系統中斷資源,即一箇中斷號)尚未開啓。ARM CMSIS 包裏提供了外設全局中斷控制函數(取自 core_cm7.h 文件):設計
#define NVIC_EnableIRQ __NVIC_EnableIRQ #define NVIC_DisableIRQ __NVIC_DisableIRQ // 開啓 xx 外設的全局中斷 __STATIC_INLINE void __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(); } } // 關閉 xx 外設的全局中斷 __STATIC_INLINE void __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)裏有定義。code
void periph_int_config(void) { // 初始化 GPT1... GPT_Init(GPT1, &gptConfig); // ... // 開啓 GPT1 的超時事件中斷 GPT_EnableInterrupts(GPT1, kGPT_RollOverFlagInterruptEnable); // 開啓 GPT1 的全局中斷 NVIC_EnableIRQ(GPT1_IRQn); } // GPT1 的中斷響應函數 void GPT1_IRQHandler(void) { GPT_ClearStatusFlags(GPT1, kGPT_RollOverFlagInterruptEnable); // 中斷業務處理代碼 }
MCU 中最頂層的中斷控制針對的是整個芯片系統,這個控制來自於 Cortex-M 內核的 CPS 指令。以下是 CPS 指令用法(取自 ARMv7-M 手冊):
當你想對 MCU 整個芯片的全部中斷進行統一開關控制時,就必須藉助 CPS 指令。通常狀況下開啓芯片系統全局中斷動做在 MCU 啓動文件裏已經作好了,因此在用戶代碼環境裏經常不須要使能系統全局中斷的動做。以下是 IAR 環境下 i.MXRT1170 啓動文件中系統全局中斷操做,基於彙編指令實現:
爲了便於用戶在 C 代碼中操做系統全局中斷,各 IDE 下均按一樣的接口函數( __disable_irq / __enable_irq )作了封裝實現。IAR 環境見 \IAR Systems\Embedded Workbench 8.50.6\arm\inc\c\iccarm_builtin.h 文件,可是封裝進其 Lib 了,沒有暴露源碼:
#include "iccarm_builtin.h" #define __disable_irq __iar_builtin_disable_interrupt #define __enable_irq __iar_builtin_enable_interrupt
Keil 環境見 \Keil_v5\ARM\ARMCLANG\include\arm_compat.h 文件,咱們能夠看到源碼:
static __inline__ unsigned int __attribute__((__always_inline__, __nodebug__)) __disable_irq(void) { unsigned int cpsr; #if __ARM_ARCH >= 6 #if defined(__ARM_ARCH_PROFILE) && __ARM_ARCH_PROFILE == 'M' __asm__ __volatile__("mrs %[cpsr], primask\n" "cpsid i\n" : [cpsr] "=r"(cpsr)); return cpsr & 0x1; #endif #endif } static __inline__ void __attribute__((__always_inline__, __nodebug__)) __enable_irq(void) { #if __ARM_ARCH >= 6 __asm__ __volatile__("cpsie i"); #endif }
最終 GPT 例程裏完整的三重中斷使能代碼應以下:
void periph_int_config(void) { // 初始化 GPT1... GPT_Init(GPT1, &gptConfig); // ... // 開啓 GPT1 的超時事件中斷 GPT_EnableInterrupts(GPT1, kGPT_RollOverFlagInterruptEnable); // 開啓 GPT1 的全局中斷 NVIC_EnableIRQ(GPT1_IRQn); // 開啓芯片系統全局中斷 __enable_irq(); }
至此,嵌入式MCU中標準的三重中斷控制設計痞子衡便介紹完畢了,掌聲在哪裏~~~
文章會同時發佈到個人 博客園主頁、CSDN主頁、知乎主頁、微信公衆號 平臺上。
微信搜索"痞子衡嵌入式"或者掃描下面二維碼,就能夠在手機上第一時間看了哦。