LPC824的中斷系統非常強大,要用好中斷,就必須先了解LPC824的整個中斷系統。下面來討論一下NVIC中斷系統。
在LPC8xx系列處理器中,有一個部分被稱為“私有外設總線”(Private peripheral bus),它位於Memory map中地址為0xE0000000~0xE0100000的地方,包含有下表中的幾個核心外設。
在LPC8xx系列處理器中,有一個部分被稱為“私有外設總線”(Private peripheral bus),它位於Memory map中地址為0xE0000000~0xE0100000的地方,包含有下表中的幾個核心外設。

其中的NVIC(Nested Vectored Interrupt Contorller)就是中斷系統,被稱為“內嵌套向量中斷控制器”。它與處理器內核緊密耦合,可實現低中斷延遲及對新中斷的有效處理。它具有以下特征:
擁有32路向量中斷;每個中斷的優先級均可編程設置為0~192(步長64),數值越小優先級越高,0級為最高優先級;支持電平和邊沿觸發中斷;支持中斷尾鏈;擁有一個外部不可屏蔽中斷NMI。
NVIC所涉及到的寄存器如下表所示。
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;
{
__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 )
#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;
/*---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();
}
}
__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();
}
}
上述就是LPC824中的整個中斷系統,即“內嵌套向量中斷控制器”。可以看出,它控制着整個處理器32路中斷源的使能與掛起等13個動作,功能確實非常強大。