中斷寄存器
1)ISER[8](Interrupt Set-Enable Registers):中斷使能寄存器--void NVIC_Init(NVIC_InitTypeDef* NVIC_InitStruct);。其
中斷使能寄存器共有8個,ISER[0]設置0~31號中斷的使能,ISER[1]設置32~63號中斷的使能,如此類推。以下以ISER[0]為例:
[31:0] SETENA中斷設置使能位。
寫:
0 =無影響
1 =使能中斷。
讀:
0 =中斷是禁止的
1=中斷已經被使能
如果要使能0號中斷,就向該寄存器的0位寫1,如果要使能38號中斷,就向NVIC_ISER[1]的6位寫1,如此類推,至於哪個中斷對應哪個中斷號
2)ICER[8](Interrupt Clear-Enable Registers):中斷移除寄存器--void NVIC_Init(NVIC_InitTypeDef* NVIC_InitStruct);。該寄存器的作用於ISER相反。這里專門設置一個ICER來清除中斷位,而不是向ISER位寫0,是因為NVIC的寄存器寫1有效,寫0無效。
3)ISPR[8](Interrupt Set-Pending Registers):中斷掛起控制寄存器--static __INLINE void NVIC_SetPendingIRQ(IRQn_Type IRQn);。通過置1可以將正在進行的中斷掛起,執行同級或者更高級別的中斷。寫0無效。
4)ICPR[8](Interrupt Clear-Pending Registers):中斷解掛控制寄存器--static __INLINE void NVIC_ClearPendingIRQ(IRQn_Type IRQn);。通過置1可以將正在掛起的中斷解掛。寫0無效。
5)IABR[8](Interrupt Active-Bit Registers):中斷激活標志位寄存器--static __INLINE uint32_t NVIC_GetActive(IRQn_Type IRQn);。這是一個只讀寄存器,可以知道當前在執行的中斷是哪一個(為1),在中斷執行完后硬件自動清零。
6)IP[240](Interrupt Priority Registers):中斷優先級控制的寄存器--void NVIC_Init(NVIC_InitTypeDef* NVIC_InitStruct);。這是用來控制每個中斷的優先級。由於STM32F10x系列一共60個可屏蔽中斷,故使用IP[59]~IP[0]。其中每個IP寄存器的高4位[7:4]用來設置搶占和響應優先級(根據分組),低4位沒有用到。而兩個優先級各占幾個位又要由上面講到的中斷優先級分組決定。
STM32F0xx 實現中斷向量表重定義
在STM32F103等cortex-m3/m4內核的單片機上可以通過設置SCB->VTOR = FLASH_BASE | VECT_TAB_OFFSET;
該寄存器的值來實現中斷向量表的重定義。
在Coretext-M3與M4核中,在System Control Block中存在一個向量表偏移量寄存器 VTOR(
0xE000ED08
),系統產生中斷后,內核通過這個寄存器的值來找到中斷向量表的地址,進而執行中斷例程代碼。當然,此寄存器的值是可以修改的,它的默認值為0。實際上在大部分的M3和M4的工程中,一般都是在
SystemInit
函數中對此寄存器的值進行設置。
由於STM32F0XX采用的是M0核,它是沒有這個VTOR寄存器的,那么它又是怎么找到中斷向量表的地址的呢?
如何將中斷向量表的尋找位置從0x0800 0000
修改到0x0800 3000
(假設為APP的地址)? 我們重新回顧之前的分析,可以得出有2種方法:
- 修改寄存器VTOR的值(M3/M4 使用)
- 內存重映射(M0使用)
通過將SRAM重映射到地址0x0000 0000
,那么,M0系統產生中斷后,CPU還是從地址0x0000 0000
尋找中斷入口,但是,實際上不再是尋址0x0800 0000
,而是尋址0x2000 0000
,這么一來,接下來我們就只需要將中斷向量表整個拷貝到SRAM上,也就是0x2000 0000
上,就這樣,CPU就可以正常尋址中斷向量表了。
/* Memory mapping of Cortex-M3 Hardware */ #define SCS_BASE (0xE000E000) /*!< System Control Space Base Address */ #define ITM_BASE (0xE0000000) /*!< ITM Base Address */ #define CoreDebug_BASE (0xE000EDF0) /*!< Core Debug Base Address */ #define SysTick_BASE (SCS_BASE + 0x0010) /*!< SysTick Base Address */ #define NVIC_BASE (SCS_BASE + 0x0100) /*!< NVIC Base Address */ #define SCB_BASE (SCS_BASE + 0x0D00) /*!< System Control Block Base Address */ #define InterruptType ((InterruptType_Type *) SCS_BASE) /*!< Interrupt Type Register */ #define SCB ((SCB_Type *) SCB_BASE) /*!< SCB configuration struct */ #define SysTick ((SysTick_Type *) SysTick_BASE) /*!< SysTick configuration struct */ #define NVIC ((NVIC_Type *) NVIC_BASE) /*!< NVIC configuration struct */ #define ITM ((ITM_Type *) ITM_BASE) /*!< ITM configuration struct */ #define CoreDebug ((CoreDebug_Type *) CoreDebug_BASE) /*!< Core Debug configuration struct *
1. 中斷分組
分組函數:void NVIC_PriorityGroupConfig(uint32_t NVIC_PriorityGroup);
假設,要使中斷分組為2
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
#define AIRCR_VECTKEY_MASK ((uint32_t)0x05FA0000) #define NVIC_PriorityGroup_2 ((uint32_t)0x500) /*!< 2 bits for pre-emption priority
/** * @brief Configures the priority grouping: pre-emption priority and subpriority. * @param NVIC_PriorityGroup: specifies the priority grouping bits length. * This parameter can be one of the following values: * @arg NVIC_PriorityGroup_0: 0 bits for pre-emption priority * 4 bits for subpriority * @arg NVIC_PriorityGroup_1: 1 bits for pre-emption priority * 3 bits for subpriority * @arg NVIC_PriorityGroup_2: 2 bits for pre-emption priority * 2 bits for subpriority * @arg NVIC_PriorityGroup_3: 3 bits for pre-emption priority * 1 bits for subpriority * @arg NVIC_PriorityGroup_4: 4 bits for pre-emption priority * 0 bits for subpriority * @retval None */ 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; }
分組配置是在寄存器SCB->AIRCR中配置,具體的分配關系如下所示:
STM32有16個級別(4-bit)優先級可使用:
其中AIRCR寄存器來確定是用哪種分組,IP寄存器是相對應於那種分組搶占優先級和響應優先級的分配比例。例如組設置成3,那么此時所有的60個中斷優先寄存器高4位中的最高3位是搶占優先級,低1位為響應優先級。CM3中定義了8個Bit用於設置中斷源的優先級,而STM32只選用其中的4個Bit。
搶占優先級的級別高於響應優先級,而數值越小所代表的的優先級越高。
介紹一下搶占優先級、響應優先級的區別:
1)高優先級的搶占優先級是可以打斷正在進行的低搶占優先級中斷的;
2)搶占優先級相同的中斷,高響應優先級不可以打斷低響應優先級的中斷;
3)搶占優先級相同的中斷,當兩個中斷同時發生的情況下,哪個響應優先級高,哪個先執行;
4)如果兩個中斷的搶占優先級和響應優先級都是一樣的話,則看哪個中斷先發生就先執行;
除此之外有兩點需要注意:
1)打斷的情況只會與搶占優先級有關, 和響應優先級無關!
2)一般情況下,系統代碼執行過程中,只設置一次中斷優先級分組,比如分組2,設置好分組之后一般不會再改變分組。隨意改變分組會導致中斷管理混亂,程序出現意想不到的執行結果。
優先級舉例說明:假定設置中斷優先級組為2,然后設置中斷3(RTC中斷)的搶占優先級為2,響應優先級為1。中斷6(外部中斷0)的搶占優先級為3,響應優先級為0。中斷7(外部中斷1)的搶占優先級為2,響應優先級為0。那么這3個中斷的優先級順序為:中斷7>中斷3>中斷6
2.設置中斷的優先級別(搶占優先級和子優先級)
中斷初始化函數:void NVIC_Init(NVIC_InitTypeDef* NVIC_InitStruct)
假設,要設置串口1的中斷,同時設置搶占優先級為1,子優先級為2
NVIC_InitTypeDef NVIC_InitStructure; NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn;//串口 1 中斷 NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=1 ;// 搶占優先級為 1 NVIC_InitStructure.NVIC_IRQChannelSubPriority = 2;// 子優先級位 2 NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //IRQ 通道使能 NVIC_Init(&NVIC_InitStructure); //根據上面指定的參數初始化 NVIC 寄存器
NVIC_Type為:
/** @addtogroup CMSIS_CM3_NVIC CMSIS CM3 NVIC memory mapped structure for Nested Vectored Interrupt Controller (NVIC) @{ */ typedef struct { __IO uint32_t ISER[8]; /*!< Offset: 0x000 Interrupt Set Enable Register */ uint32_t RESERVED0[24]; __IO uint32_t ICER[8]; /*!< Offset: 0x080 Interrupt Clear Enable Register */ uint32_t RSERVED1[24]; __IO uint32_t ISPR[8]; /*!< Offset: 0x100 Interrupt Set Pending Register */ uint32_t RESERVED2[24]; __IO uint32_t ICPR[8]; /*!< Offset: 0x180 Interrupt Clear Pending Register */ uint32_t RESERVED3[24]; __IO uint32_t IABR[8]; /*!< Offset: 0x200 Interrupt Active bit Register */ uint32_t RESERVED4[56]; __IO uint8_t IP[240]; /*!< Offset: 0x300 Interrupt Priority Register (8Bit wide) */ uint32_t RESERVED5[644]; __O uint32_t STIR; /*!< Offset: 0xE00 Software Trigger Interrupt Register */ } NVIC_Type; /*@}*/ /* end of group CMSIS_CM3_NVIC */
NVIC基址
#define SCS_BASE (0xE000E000) /*!< System Control Space Base Address */
#define NVIC_BASE (SCS_BASE + 0x0100) /*!< NVIC Base Address */
#define SCB_BASE (SCS_BASE + 0x0D00) /*!< System Control Block Base Address */
#define NVIC ((NVIC_Type *) NVIC_BASE) /*!< NVIC configuration struct */
NVIC_Init()這個函數:
/** * @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); } }
可以看到,它會去設置幾個寄存器:NVIC的IP,ISER,ICER等等,
STM32 學習:IAP 簡單的IAP例子
stm32中關於NVIC_SetVectorTable函數使用的疑惑與理解