中斷寄存器
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函數使用的疑惑與理解


