標籤:
本文轉自:http://www.ichanging.org/stm32_NVIC.html
cortex-m3支援256個中端,其中包含了16個核心中斷,240個外部中斷。stm32隻有84個中斷,包括16個核心中斷和68個可屏蔽中斷。stm32f103上只有60個中斷,f107上才有68個中斷。 中斷是stm32很基礎的一個功能,學會使用中斷,才可以更好的使用其他的外設。理解stm32的中斷,必須要先從stm32的中斷優先順序分組是怎麼回事。要理解優先順序分組,就要先理解什麼是先佔優先順序,和次佔優先級。 先佔優先順序的概念等同於51單片機中的中斷。假設有兩中斷先後觸發,已經在執行的中斷先佔優先順序如果沒有後觸發的中斷 先佔優先順序更高,就會先處理先佔優先順序高的中斷。也就是說又有較高的先佔優先順序的中斷可以打斷先佔優先順序較低的中斷。這是實現中斷嵌套的基礎。 次佔優先級只在同一先佔優先順序的中斷同時觸發時起作用,先佔優先順序相同,則優先執行次佔優先級較高的中斷。次佔優先級不會造成中斷嵌套。 如果中斷的兩個優先順序都一致,則優先執行位於中斷向量表中位置較高的中斷。 還需要注意的一點是 這裡的中斷優先順序 高是指 是指是否更接近0級,0級優先順序是最高的。 那麼最低的優先順序可以是多少?這就涉及了優先順序分組的概念。 stm32 通過一個中斷向量控制器(NVIC),來分配先佔優先順序和次佔優先級的數量。 arm cortex-m3 核心中擁有一個3位寬度的的PRIGROUP資料區,用來指示一個8位元據序列中的小數點的位置從而表示中斷優先順序的分組。 舉個例子可以更好的理解: 如果PRIGROUP 資料位元000 即為0 說明8位元據序列中小數位置在第1位的左邊 為xxxxxxx.y 用於表示中斷優先順序的分組的含義就是 用7位的資料寬度來表示 先佔優先順序的數量 即為128 用1位的資料寬度來表示 次佔優先級數量 即為 2
所以arm cortex-m3中有2的三次方 即為8個優先順序分組 。 但是stm32中只有5個優先順序分組,表示方法略有不同,參照下表: MDK中定義的中斷相關的寄存器結構體為:
typedef struct{ vu32 ISER[2]; u32 RESERVED0[30]; vu32 ICER[2]; u32 RSERVED1[30]; vu32 ISPR[2]; u32 RESERVED2[30]; vu32 ICPR[2]; u32 RESERVED3[30]; vu32 IABR[2]; u32 RESERVED4[62]; vu32 IPR[15];} NVIC_TypeDef; ISER[2]:中斷使能寄存器組 stm32可屏蔽中斷共有60個,這裡用了兩個32位的寄存器,可以表示64個中斷。stm32隻用了前60位。 若要使能某個中斷,則必須設定相應的ISER位為1。具體每一位對應的中斷關係如下:(參見 MDK下的 stm32f10x_nvic.h)
#ifdef STM32F10X_HD
ADC1_2_IRQn = 18, /*!< ADC1 and ADC2 global Interrupt */
USB_HP_CAN1_TX_IRQn = 19, /*!< USB Device High Priority or CAN1 TX Interrupts */
USB_LP_CAN1_RX0_IRQn = 20, /*!< USB Device Low Priority or CAN1 RX0 Interrupts */
CAN1_RX1_IRQn = 21, /*!< CAN1 RX1 Interrupt */
CAN1_SCE_IRQn = 22, /*!< CAN1 SCE Interrupt */
EXTI9_5_IRQn = 23, /*!< External Line[9:5] Interrupts */
TIM1_BRK_IRQn = 24, /*!< TIM1 Break Interrupt */
TIM1_UP_IRQn = 25, /*!< TIM1 Update Interrupt */
TIM1_TRG_COM_IRQn = 26, /*!< TIM1 Trigger and Commutation Interrupt */
TIM1_CC_IRQn = 27, /*!< TIM1 Capture Compare Interrupt */
TIM2_IRQn = 28, /*!< TIM2 global Interrupt */
TIM3_IRQn = 29, /*!< TIM3 global Interrupt */
TIM4_IRQn = 30, /*!< TIM4 global Interrupt */
I2C1_EV_IRQn = 31, /*!< I2C1 Event Interrupt */
I2C1_ER_IRQn = 32, /*!< I2C1 Error Interrupt */
I2C2_EV_IRQn = 33, /*!< I2C2 Event Interrupt */
I2C2_ER_IRQn = 34, /*!< I2C2 Error Interrupt */
SPI1_IRQn = 35, /*!< SPI1 global Interrupt */
SPI2_IRQn = 36, /*!< SPI2 global Interrupt */
USART1_IRQn = 37, /*!< USART1 global Interrupt */
USART2_IRQn = 38, /*!< USART2 global Interrupt */
USART3_IRQn = 39, /*!< USART3 global Interrupt */
EXTI15_10_IRQn = 40, /*!< External Line[15:10] Interrupts */
RTCAlarm_IRQn = 41, /*!< RTC Alarm through EXTI Line Interrupt */
USBWakeUp_IRQn = 42, /*!< USB Device WakeUp from suspend through EXTI Line Interrupt */
TIM8_BRK_IRQn = 43, /*!< TIM8 Break Interrupt */
TIM8_UP_IRQn = 44, /*!< TIM8 Update Interrupt */
TIM8_TRG_COM_IRQn = 45, /*!< TIM8 Trigger and Commutation Interrupt */
TIM8_CC_IRQn = 46, /*!< TIM8 Capture Compare Interrupt */
ADC3_IRQn = 47, /*!< ADC3 global Interrupt */
FSMC_IRQn = 48, /*!< FSMC global Interrupt */
SDIO_IRQn = 49, /*!< SDIO global Interrupt */
TIM5_IRQn = 50, /*!< TIM5 global Interrupt */
SPI3_IRQn = 51, /*!< SPI3 global Interrupt */
UART4_IRQn = 52, /*!< UART4 global Interrupt */
UART5_IRQn = 53, /*!< UART5 global Interrupt */
TIM6_IRQn = 54, /*!< TIM6 global Interrupt */
TIM7_IRQn = 55, /*!< TIM7 global Interrupt */
DMA2_Channel1_IRQn = 56, /*!< DMA2 Channel 1 global Interrupt */
DMA2_Channel2_IRQn = 57, /*!< DMA2 Channel 2 global Interrupt */
DMA2_Channel3_IRQn = 58, /*!< DMA2 Channel 3 global Interrupt */
DMA2_Channel4_5_IRQn = 59 /*!< DMA2 Channel 4 and Channel 5 global Interrupt */
#endif /* STM32F10X_HD */
系統中斷這裡沒有申明,所以導致一些系統中斷無法使用,比如 systick的中斷 這個在 stm32上最方便的定時器Systick[操作寄存器+庫函數] 已經做了分析 ICER[2]:中斷清除寄存器組結構同ISER[2],但是作用相反。 中斷的清楚不是通過向ISER[2]中對應位寫0實現的,而是在ICER[2]對應位寫1清除的。 ISPR[2]:中斷掛起控制寄存器組每一位對應的中斷和ISER是一樣的。通過置1來掛起進行中的中斷,而執行同級或者更進階別的中斷。 ICPR[2]:中斷解掛寄存器組結構和ISPR[2]相同,作用相反。置1將相應中斷解掛。 IABR[2]:中斷啟用標誌位寄存器組中斷和ISER[2]對應,如果為1,則表示該位所對應的中斷正在執行。這是唯讀寄存器,由硬體自動清零。 IPR[15]:中斷優先順序控制的寄存器組IPR寄存器組由15個32位寄存器組成。每個可屏蔽的中斷佔用8位,這樣可以表示的可屏蔽中斷為 15*4 =60個。而每個可屏蔽中斷佔用的8位並沒有全部使用,而是只使用了高4位。這4位又分為搶佔優先順序和子優先順序。搶佔優先順序在前,子優先順序在後。而這兩個優先順序各占幾位又要根據SCB->AIRCR中中斷分組的設定來決定。 IPR寄存器描述: stm32將中斷分為5組,組0~4. 該分組由SCB->AIRCR寄存器的[10:8]三位來定義。具體關係如下:
| 組 |
AIRCR[10:8] |
分配情況 |
分配結果 |
| 0 |
111 |
.xxxx0000 |
0位 表示 搶佔優先順序,4位 表示 相應優先順序 |
| 1 |
110 |
y.xxx0000 |
1位 表示 搶佔優先順序,3位 表示 相應優先順序 |
| 2 |
101 |
yy.xx0000 |
2為 表示 搶佔優先順序,2位 表示 相應優先順序 |
| 3 |
100 |
yyy.x0000 |
3位 表示 搶佔優先順序,1位 表示 相應優先順序 |
| 4 |
011 |
yyyy.0000 |
4位 表示 搶佔優先順序,0位 表示 相應優先順序 |
中斷管理實現如下:
void NVIC_PriorityGroupConfig(uint32_t NVIC_PriorityGroup)
{
/* Check the parameters */
assert_param(IS_NVIC_PRIORITY_GROUP(NVIC_PriorityGroup));
/* Set the PRIGROUP[10:8] bits according to NVIC_PriorityGroup value */
SCB->AIRCR = AIRCR_VECTKEY_MASK | NVIC_PriorityGroup;
}
/**
* @brief Initializes the NVIC peripheral according to the specified
* parameters in the NVIC_InitStruct.
* @param NVIC_InitStruct: pointer to a NVIC_InitTypeDef structure that contains
* the configuration information for the specified NVIC peripheral.
* @retval None
*/
void NVIC_Init(NVIC_InitTypeDef* NVIC_InitStruct)
{
uint32_t tmppriority = 0x00, tmppre = 0x00, tmpsub = 0x0F;
/* Check the parameters */
assert_param(IS_FUNCTIONAL_STATE(NVIC_InitStruct->NVIC_IRQChannelCmd));
assert_param(IS_NVIC_PREEMPTION_PRIORITY(NVIC_InitStruct->NVIC_IRQChannelPreemptionPriority));
assert_param(IS_NVIC_SUB_PRIORITY(NVIC_InitStruct->NVIC_IRQChannelSubPriority));
if (NVIC_InitStruct->NVIC_IRQChannelCmd != DISABLE)
{
/* Compute the Corresponding IRQ Priority --------------------------------*/
tmppriority = (0x700 - ((SCB->AIRCR) & (uint32_t)0x700))>> 0x08;
tmppre = (0x4 - tmppriority);
tmpsub = tmpsub >> tmppriority;
tmppriority = (uint32_t)NVIC_InitStruct->NVIC_IRQChannelPreemptionPriority << tmppre;
tmppriority |= NVIC_InitStruct->NVIC_IRQChannelSubPriority & tmpsub;
tmppriority = tmppriority << 0x04;
NVIC->IP[NVIC_InitStruct->NVIC_IRQChannel] = tmppriority;
/* Enable the Selected IRQ Channels --------------------------------------*/
NVIC->ISER[NVIC_InitStruct->NVIC_IRQChannel >> 0x05] =
(uint32_t)0x01 << (NVIC_InitStruct->NVIC_IRQChannel & (uint8_t)0x1F);
}
else
{
/* Disable the Selected IRQ Channels -------------------------------------*/
NVIC->ICER[NVIC_InitStruct->NVIC_IRQChannel >> 0x05] =
(uint32_t)0x01 << (NVIC_InitStruct->NVIC_IRQChannel & (uint8_t)0x1F);
}
}
/**
* @brief Sets the vector table location and Offset.
* @param NVIC_VectTab: specifies if the vector table is in RAM or FLASH memory.
* This parameter can be one of the following values:
* @arg NVIC_VectTab_RAM
* @arg NVIC_VectTab_FLASH
* @param Offset: Vector Table base offset field. This value must be a multiple
* of 0x200.
* @retval None
*/
void NVIC_SetVectorTable(uint32_t NVIC_VectTab, uint32_t Offset)
{
/* Check the parameters */
assert_param(IS_NVIC_VECTTAB(NVIC_VectTab));
assert_param(IS_NVIC_OFFSET(Offset));
SCB->VTOR = NVIC_VectTab | (Offset & (uint32_t)0x1FFFFF80);
}
stm32 NVIC中斷管理實現[直接操作寄存器]