天天看點

痞子衡嵌入式:嵌入式Cortex-M中标準的三重中斷控制設計

  大家好,我是痞子衡,是正經搞技術的痞子。今天痞子衡給大家分享的是嵌入式MCU中标準的三重中斷控制設計。

  我們知道在 MCU 裸機中程式代碼之是以能完成多任務并行實時處理功能,其實主要是靠中斷來排程的,沒有中斷,CPU 就隻能按順序"呆闆"地執行代碼。很多人都說是中斷能力賦予了 MCU 真正的靈魂,能正确認識和熟練使用 MCU 中斷,基本上就算玩熟了這顆 MCU。

  痞子衡之前寫過一篇 《中斷處理函數(IRQHandler)的标準流程》,裡面詳細講了中斷處理函數裡的标準代碼流程與寫法,這篇文章可讓大家對 MCU 裡的中斷用法有個初步認識。今天痞子衡以 ARM Cortex-M 核心 MCU 為例再來介紹下業界标準的三重中斷控制設計:

一、外設事件中斷控制

  MCU 中最底層的中斷控制針對的是外設裡某個具體的事件,這個控制來自于外設子產品本身,以恩智浦 i.MXRT 系列 MCU 的 GPT 定時器子產品為例。如下是 GPT 子產品寄存器清單,你可以發現其中有經典的 IR 和 SR 寄存器,SR 是事件狀态寄存器,IR 是中斷事件控制寄存器:

痞子衡嵌入式:嵌入式Cortex-M中标準的三重中斷控制設計

  GPT 定時器一旦被使能,其運作狀态(一共支援 6 個事件:逾時、輸入捕獲 x 2ch、比較輸出 x 3ch)都會實時記錄在 SR 寄存器中,如果不在 IR 寄存器中将事件中斷開啟(預設是關閉的),那麼就需要使用者在代碼裡手動去查詢 SR 寄存器置起的事件标志位以處理對應事件。

  • 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 的逾時事件中斷代碼示例如下:

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 寄存器:

痞子衡嵌入式:嵌入式Cortex-M中标準的三重中斷控制設計

  當 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)裡有定義。

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 手冊):

痞子衡嵌入式:嵌入式Cortex-M中标準的三重中斷控制設計

  當你想對 MCU 整個晶片的所有中斷進行統一開關控制時,就必須借助 CPS 指令。一般情況下開啟晶片系統全局中斷動作在 MCU 啟動檔案裡已經做好了,是以在使用者代碼環境裡常常不需要使能系統全局中斷的動作。如下是 IAR 環境下 i.MXRT1170 啟動檔案中系統全局中斷操作,基于彙編指令實作:

痞子衡嵌入式:嵌入式Cortex-M中标準的三重中斷控制設計

  為了便于使用者在 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首頁、知乎首頁、微信公衆号 平台上。

微信搜尋"痞子衡嵌入式"或者掃描下面二維碼,就可以在手機上第一時間看了哦。

痞子衡嵌入式:嵌入式Cortex-M中标準的三重中斷控制設計

  最後歡迎關注痞子衡個人微信公衆号【痞子衡嵌入式】,一個專注嵌入式技術的公衆号,跟着痞子衡一起玩轉嵌入式。

痞子衡嵌入式:嵌入式Cortex-M中标準的三重中斷控制設計
痞子衡嵌入式:嵌入式Cortex-M中标準的三重中斷控制設計
痞子衡嵌入式:嵌入式Cortex-M中标準的三重中斷控制設計

  衡傑(痞子衡),目前就職于恩智浦MCU系統部門,擔任嵌入式系統應用工程師。

  專欄内所有文章的轉載請注明出處:http://www.cnblogs.com/henjay724/

  與痞子衡進一步交流或咨詢業務合作請發郵件至 [email protected]

  可以關注痞子衡的Github首頁 https://github.com/JayHeng,有很多好玩的嵌入式項目。

  關于專欄文章有任何疑問請直接在部落格下面留言,痞子衡會及時回複免費(劃重點)答疑。

  痞子衡郵箱已被私信擠爆,技術問題不推薦私信,堅持私信請先掃碼付款(5元起步)再發。