痞子衡嵌入式:嵌入式MCU中標準的三重中斷控制設計


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

  咱們知道在 MCU 裸機中程序代碼之因此能完成多任務並行實時處理功能,其實主要是靠中斷來調度的,沒有中斷,CPU 就只能按順序"呆板"地執行代碼。不少人都說是中斷能力賦予了 MCU 真正的靈魂,能正確認識和熟練使用 MCU 中斷,基本上就算玩熟了這顆 MCU。node

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

1、外設事件中斷控制

  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);
}

2、外設全局中斷控制

  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);

    // 中斷業務處理代碼
}

3、系統全局中斷控制

  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主頁知乎主頁微信公衆號 平臺上。

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

相關文章
相關標籤/搜索