中斷系統NVIC


LPC824的中斷系統非常強大,要用好中斷,就必須先了解LPC824的整個中斷系統。下面來討論一下NVIC中斷系統。
在LPC8xx系列處理器中,有一個部分被稱為“私有外設總線”(Private peripheral bus),它位於Memory map中地址為0xE0000000~0xE0100000的地方,包含有下表中的幾個核心外設。
其中的NVIC(Nested Vectored Interrupt Contorller)就是中斷系統,被稱為“內嵌套向量中斷控制器”。它與處理器內核緊密耦合,可實現低中斷延遲及對新中斷的有效處理。它具有以下特征:
擁有32路向量中斷;每個中斷的優先級均可編程設置為0~192(步長64),數值越小優先級越高,0級為最高優先級;支持電平和邊沿觸發中斷;支持中斷尾鏈;擁有一個外部不可屏蔽中斷NMI。
NVIC所涉及到的寄存器如下表所示。
從表中可以看出,每個寄存器都是32位的結構,都具有可讀可寫的屬性,復位值都為全0。其中ISER0寄存器是設置中斷的使能,32位對應32路中斷,值為1使能中斷,值為0不使能中斷。ICER0寄存器是設置中斷的禁能,32位對應32路中斷,值為1禁能中斷,值為0不禁能。ISPR0寄存器是設置中斷的掛起,32位對應32路中斷,值為1掛起,值為0不掛起。ICPR0寄存器是清除中斷的掛起,32位對應32路中斷,值為1清除掛起,值為0不清除掛起。IPR0~7寄存器是設置中斷優先級。
下面是NVIC寄存器組所對應的結構體形式(位於頭文件core_cm0plus.h中)。
typedef struct
{
  __IOM uint32_t ISER[1U];     
        uint32_t RESERVED0[31U];
  __IOM uint32_t ICER[1U];     
        uint32_t RSERVED1[31U];
  __IOM uint32_t ISPR[1U];     
        uint32_t RESERVED2[31U];
  __IOM uint32_t ICPR[1U];     
        uint32_t RESERVED3[31U];
        uint32_t RESERVED4[64U];
  __IOM uint32_t IP[8U];       
}  NVIC_Type;
因NVIC寄存器組的基址為0xE000E100,所以要將基址指針強制轉換為上述結構體,還必須要加上下面的定義。
#define SCS_BASE            (0xE000E000UL)
#define NVIC_BASE           (SCS_BASE + 0x0100UL)
#define NVIC                ((NVIC_Type *) NVIC_BASE )
接下來給出的是上面NVIC32位寄存器所對應的32路中斷向量的中斷源。
 
為了能描述上面的32路中斷源,在C語言中運用了枚舉類型,代碼如下所示(位於頭文件lpc82x.h中)。
typedef enum {
/*---Cortex-M0PLUS Processor Exceptions Numbers---*/
  Reset_IRQn                    = -15,
  NonMaskableInt_IRQn           = -14,
  HardFault_IRQn                = -13,
  SVCall_IRQn                   =  -5,
  DebugMonitor_IRQn             =  -4,
  PendSV_IRQn                   =  -2,
  SysTick_IRQn                  =  -1,
/*---LPC82x Specific Interrupt Numbers---*/
  SPI0_IRQn                     =   0,
  SPI1_IRQn                     =   1,
  UART0_IRQn                    =   3,
  UART1_IRQn                    =   4,
  UART2_IRQn                    =   5,
  I2C1_IRQn                     =   7,
  I2C0_IRQn                     =   8,
  SCT_IRQn                      =   9,
  MRT_IRQn                      =  10,
  CMP_IRQn                      =  11,
  WDT_IRQn                      =  12,
  BOD_IRQn                      =  13,
  FLASH_IRQn                    =  14,
  WKT_IRQn                      =  15,
  ADC_SEQA_IRQn                 =  16,
  ADC_SEQB_IRQn                 =  17,
  ADC_THCMP_IRQn                =  18,
  ADC_OVR_IRQn                  =  19,
  DMA_IRQn                      =  20,
  I2C2_IRQn                     =  21,
  I2C3_IRQn                     =  22,
  PIN_INT0_IRQn                 =  24,
  PIN_INT1_IRQn                 =  25,
  PIN_INT2_IRQn                 =  26,
  PIN_INT3_IRQn                 =  27,
  PIN_INT4_IRQn                 =  28,
  PIN_INT5_IRQn                 =  29,
  PIN_INT6_IRQn                 =  30,
  PIN_INT7_IRQn                 =  31
} IRQn_Type;
從上述代碼中可以看出,除了32路中斷源外,還加入了編號為負數的、優先級更高的7個中斷源。這里先不進行說明,在后面用到時再來討論。定義好上述代碼后,就可以來寫中斷所需要的函數了。下面就是依據CMSIS規范所定義的13個中斷操作函數(位於頭文件core_cm0plus.h中)。 
1.允許某個中斷
__STATIC_INLINE void __NVIC_EnableIRQ(IRQn_Type IRQn)
{
  if ((int32_t)(IRQn) >= 0)
  {
    NVIC->ISER[0U] = (uint32_t)(1UL << (((uint32_t)IRQn) & 0x1FUL));
  }
}
2.讀取某個中斷的使能狀態
__STATIC_INLINE uint32_t __NVIC_GetEnableIRQ(IRQn_Type IRQn)
{
  if ((int32_t)(IRQn) >= 0)
  {
    return((uint32_t)(((NVIC->ISER[0U] & (1UL << (((uint32_t)IRQn) & 0x1FUL))) != 0UL) ? 1UL : 0UL));
  }
  else
  {
    return(0U);
  }
}
3.禁止某個中斷
__STATIC_INLINE void __NVIC_DisableIRQ(IRQn_Type IRQn)
{
  if ((int32_t)(IRQn) >= 0)
  {
    NVIC->ICER[0U] = (uint32_t)(1UL << (((uint32_t)IRQn) & 0x1FUL));
    __DSB();
    __ISB();
  }
}
4.讀取某個中斷的掛起狀態
__STATIC_INLINE uint32_t __NVIC_GetPendingIRQ(IRQn_Type IRQn)
{
  if ((int32_t)(IRQn) >= 0)
  {
    return((uint32_t)(((NVIC->ISPR[0U] & (1UL << (((uint32_t)IRQn) & 0x1FUL))) != 0UL) ? 1UL : 0UL));
  }
  else
  {
    return(0U);
  }
}
5.把某個中斷的掛起狀態設為1
__STATIC_INLINE void __NVIC_SetPendingIRQ(IRQn_Type IRQn)
{
  if ((int32_t)(IRQn) >= 0)
  {
    NVIC->ISPR[0U] = (uint32_t)(1UL << (((uint32_t)IRQn) & 0x1FUL));
  }
}
6.把某個中斷的掛起狀態清為0
__STATIC_INLINE void __NVIC_ClearPendingIRQ(IRQn_Type IRQn)
{
  if ((int32_t)(IRQn) >= 0)
  {
    NVIC->ICPR[0U] = (uint32_t)(1UL << (((uint32_t)IRQn) & 0x1FUL));
  }
}
7.把某個中斷的可配置優先級設為1
__STATIC_INLINE void __NVIC_SetPriority(IRQn_Type IRQn, uint32_t priority)
{
  if ((int32_t)(IRQn) >= 0)
  {
    NVIC->IP[_IP_IDX(IRQn)]  = ((uint32_t)(NVIC->IP[_IP_IDX(IRQn)]  & ~(0xFFUL << _BIT_SHIFT(IRQn))) |
       (((priority << (8U - __NVIC_PRIO_BITS)) & (uint32_t)0xFFUL) << _BIT_SHIFT(IRQn)));
  }
  else
  {
    SCB->SHP[_SHP_IDX(IRQn)] = ((uint32_t)(SCB->SHP[_SHP_IDX(IRQn)] & ~(0xFFUL << _BIT_SHIFT(IRQn))) |
       (((priority << (8U - __NVIC_PRIO_BITS)) & (uint32_t)0xFFUL) << _BIT_SHIFT(IRQn)));
  }
}
8.讀取某個中斷的優先級
__STATIC_INLINE uint32_t __NVIC_GetPriority(IRQn_Type IRQn)
{
  if ((int32_t)(IRQn) >= 0)
  {
    return((uint32_t)(((NVIC->IP[ _IP_IDX(IRQn)] >> _BIT_SHIFT(IRQn) ) & (uint32_t)0xFFUL) >> (8U - __NVIC_PRIO_BITS)));
  }
  else
  {
    return((uint32_t)(((SCB->SHP[_SHP_IDX(IRQn)] >> _BIT_SHIFT(IRQn) ) & (uint32_t)0xFFUL) >> (8U - __NVIC_PRIO_BITS)));
  }
}
9.編碼優先級
__STATIC_INLINE uint32_t NVIC_EncodePriority (uint32_t PriorityGroup, uint32_t PreemptPriority, uint32_t SubPriority)
{
  uint32_t PriorityGroupTmp = (PriorityGroup & (uint32_t)0x07UL);
  uint32_t PreemptPriorityBits;
  uint32_t SubPriorityBits;
  PreemptPriorityBits = ((7UL - PriorityGroupTmp) > (uint32_t)(__NVIC_PRIO_BITS)) ? (uint32_t)(__NVIC_PRIO_BITS) : (uint32_t)(7UL - PriorityGroupTmp);
  SubPriorityBits     = ((PriorityGroupTmp + (uint32_t)(__NVIC_PRIO_BITS)) < (uint32_t)7UL) ? (uint32_t)0UL : (uint32_t)((PriorityGroupTmp - 7UL) + (uint32_t)(__NVIC_PRIO_BITS));
  return (
           ((PreemptPriority & (uint32_t)((1UL << (PreemptPriorityBits)) - 1UL)) << SubPriorityBits) |
           ((SubPriority     & (uint32_t)((1UL << (SubPriorityBits    )) - 1UL)))
         );
}
10.解碼優先級
__STATIC_INLINE void NVIC_DecodePriority (uint32_t Priority, uint32_t PriorityGroup, uint32_t* const pPreemptPriority, uint32_t* const pSubPriority)
{
  uint32_t PriorityGroupTmp = (PriorityGroup & (uint32_t)0x07UL);
  uint32_t PreemptPriorityBits;
  uint32_t SubPriorityBits;
  PreemptPriorityBits = ((7UL - PriorityGroupTmp) > (uint32_t)(__NVIC_PRIO_BITS)) ? (uint32_t)(__NVIC_PRIO_BITS) : (uint32_t)(7UL - PriorityGroupTmp);
  SubPriorityBits     = ((PriorityGroupTmp + (uint32_t)(__NVIC_PRIO_BITS)) < (uint32_t)7UL) ? (uint32_t)0UL : (uint32_t)((PriorityGroupTmp - 7UL) + (uint32_t)(__NVIC_PRIO_BITS));
  *pPreemptPriority = (Priority >> SubPriorityBits) & (uint32_t)((1UL << (PreemptPriorityBits)) - 1UL);
  *pSubPriority     = (Priority                   ) & (uint32_t)((1UL << (SubPriorityBits    )) - 1UL);
}
11.設置中斷向量
__STATIC_INLINE void __NVIC_SetVector(IRQn_Type IRQn, uint32_t vector)
{
#if defined (__VTOR_PRESENT) && (__VTOR_PRESENT == 1U)
  uint32_t *vectors = (uint32_t *)SCB->VTOR;
#else
    uint32_t *vectors = (uint32_t *)0x0U;
#endif
  vectors[(int32_t)IRQn + NVIC_USER_IRQ_OFFSET] = vector;
}
12.讀取中斷向量
__STATIC_INLINE uint32_t __NVIC_GetVector(IRQn_Type IRQn)
{
#if defined (__VTOR_PRESENT) && (__VTOR_PRESENT == 1U)
  uint32_t *vectors = (uint32_t *)SCB->VTOR;
#else
  uint32_t *vectors = (uint32_t *)0x0U;
#endif
  return vectors[(int32_t)IRQn + NVIC_USER_IRQ_OFFSET];
}
13.復位NVIC
__NO_RETURN __STATIC_INLINE void __NVIC_SystemReset(void)
{
  __DSB();
  SCB->AIRCR  = ((0x5FAUL << SCB_AIRCR_VECTKEY_Pos) | SCB_AIRCR_SYSRESETREQ_Msk);
  __DSB();
  for(;;)
  {
    __NOP();
  }
}
在上述函數中有幾點要說明一下,一是數組的引用其取值只能是0(即第一個元素),這是因為在結構體定義中只定義了一個數組元素,且由於需要利用數組的地址連續性來對映CPU物理地址,所以也不能將其定義為一個普通變量;二是關鍵字“__STATIC_INLINE”在頭文件cmsis_armcc.h中已做了宏定義“#define __STATIC_INLINE static __inline”,__inline是通知編譯器其后面的函數為內聯形式;三是中斷源IRQn要與0x1F“與”一下,是為了屏蔽高27位的值,因為中斷源的最大值只到31,所以只用了32位中的低5位(31的二進制是11111,十六進制是0x1F);四是在函數的參數中,由於引入了枚舉類型,所以可以在調用函數的時候,在參數部分可直接使用枚舉中的名稱,這樣就可以省去記憶32個中斷源在32位寄存器中的對應位置,便於書寫和閱讀。例如,要開啟端口0的外部中斷,執行程序“NVIC_EnableIRQ(PIN_INT0_IRQn)”即可。
上述就是LPC824中的整個中斷系統,即“內嵌套向量中斷控制器”。可以看出,它控制着整個處理器32路中斷源的使能與掛起等13個動作,功能確實非常強大。
 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM