STM32:NVIC

发布时间 2023-04-14 22:48:15作者: caesura_k

1 NVIC

  NVIC全称nested vectored interrupt controller,嵌套 向量 中断控制器;cm3的所有中断机制都由NVIC管理;

  1.1 "向量"

    cm3自动初始化了中断响应事件与中断服务程序入口地址相对应的向量表;

    cm3中断响应后,cm3通过nvic直接控制程序在内核层跳转至中断服务程序入口地址执行,速度快效率高;

    启动文件为每个中断事件都预先写了一个中断空函数,目的是初始化向量表地址;中断函数可统一重新编写在stm32f10x_it.c中;

编号 类型 优先级 默认地址 功能
0
\
0x0000 0000
无异常运行,当前地址数据为MSP初始值;
1 Reset_Handler -3(max) 0x0000 0004 上电复位
2 NMI -2 0x0000 0008 不可屏蔽中断(外部NMI引脚输入)
3 hard fault -1 0x0000 000C 使能所有被除能的fault中断为hard fault
4 MemManage fault 可编程 0x0000 0010
存储器管理fault,MPU 访问犯规以及访问非法位置 
5
bus fault 
0x0000 0014
总线错误(预取流产(Abort)或数据流产) 
6
usage fault 
0x0000 0018
程序错误的异常 ,通常为指令无效或非法状态转换
7-10
\
\
 
\
11
SVCall 
可编程
0x0000 002C
系统服务调用 
12 debug monitor 0x0000 0030
调试监视器 (断点,数据观察点,或者是外部调试请求)
13
\
\
0x0000 0034
\
14
PendSV 
可编程 0x0000 0038
为系统设备而设的“可悬挂请求”(pendable request)
用来实现中断嵌套时,应把PendSV的优先级编程为最低;
15
systick
0x0000 003C
cm3自带的systick中断 
16
IRQ_0   (WWDG)
0x0000 0040
外设中断0 
17
IRQ_1  (PVD)
0x0000 0044
外设中断1 
...  ...  ... ...   ...
255
IRQ_239
可编程 0x0000 03FF
外设中断239
interrupts数量由芯片厂商配置;STM32F10X_HD型号支持68个interrupts;
cm3在exception entry时,自动压栈了 R0‐R3, R12, LR, PSR 和 PC,结束异常后自动弹回,无需指令执行;
向量表地址可以通过修改向量表偏移量寄存器(0xE000_ED08)重定向至flash或rom中;

    1.1.1 NMI中断

      NMI全称Non Maskable Interrupt,不可屏蔽中断;

      大概作用相当于判断中断信号是否有效、是否抖动无效;配置程序是否由threa模式进入handler模式;

    1.1.2 内核中断

      除了NMI之外还有好几个内核中断的功能作用,具体见cm3权威指南7.5、7.6节解释,这些内核中断具体干什么和nvic关系不大我就不看了;

  1.2 “嵌套”

    cm3使用一个8bit的"优先级配置寄存器ip_reg[7:0]"来、可管理256个中断的优先级;

    cm3使用的"ip_reg[3:0]" \ 始终读为0,cm3只可以配置"ip_reg[7:4]"所以只支持16个中断优先级;

    高优先级的中断响应可以打断低优先级的中断函数执行,将低优先级的中断函数悬起,等待之后弹回继续执行;

    1.2.1 优先级

      cm3将优先级配置寄存器的bit位分为两部分,命名为抢占优先级和子优先级;

      优先级配置寄存器地址MSB对齐,目的是防止程序移植后[MSB]丢失,导致高优先级中断变成低优先级中断;

        抢占优先级pre-emption priority:包含[ MSB ]所在bit的优先级配置寄存器的地址区域;抢占优先级最多7bit,最多可管理128个中断抢占优先级;

        子优先级subpriority:                   包含[  LSB  ]所在bit的优先级配置寄存器的地址区域;子优先级最少1bit,最少可管理1个中断子优先级;

    1.2.2 优先级 “嵌套”

      如果中断函数执行时,cm3检测到抢占优先级高的中断响应,那么cm3就会pending当前中断响应,转而去执行新的中断响应函数;

      如果中断函数执行时,cm3检测到新的响应和当前响应抢占优先级相同时,那么就通过子优先级去判断是否pending当前中断响应;

2 NVIC寄存器

  NVIC一共有7个寄存器,其中STIR寄存器不理解就被我跳过了;nvic初始化函数主要配置IPR、ISER寄存器;

  

  如果外设使能了中断函数,但是该外设对应的nvic_ISER_bit未使能,那么cm3不会跳转执行中断函数;

  nvic_ICER寄存器的写bit、读bit相同数值的功能是相反的,这个rc_w1有点离谱;

  ip_reg[7:0]只支持16个优先级的意思是虽然最多有81个中断事件,但是中断优先级只能配置16个;

  2.1 中断管理

    

    

    图片来自pm0056_cm3编程手册,4.3节;

3 标准库函数

typedef struct NVIC_InitTypeDef
/****misc.h***/
typedef struct
{
  uint8_t NVIC_IRQChannel;                    /*!< Specifies the IRQ channel to be enabled or disabled.
                                                   This parameter can be a value of @ref IRQn_Type 
                                                   (For the complete STM32 Devices IRQ Channels list, please
                                                    refer to stm32f10x.h file) */

  uint8_t NVIC_IRQChannelPreemptionPriority;  /*!< Specifies the pre-emption priority for the IRQ channel
                                                   specified in NVIC_IRQChannel. This parameter can be a value
                                                   between 0 and 15 as described in the table @ref NVIC_Priority_Table */

  uint8_t NVIC_IRQChannelSubPriority;         /*!< Specifies the subpriority level for the IRQ channel specified
                                                   in NVIC_IRQChannel. This parameter can be a value
                                                   between 0 and 15 as described in the table @ref NVIC_Priority_Table */

  FunctionalState NVIC_IRQChannelCmd;         /*!< Specifies whether the IRQ channel defined in NVIC_IRQChannel
                                                   will be enabled or disabled. 
                                                   This parameter can be set either to ENABLE or DISABLE */   
} NVIC_InitTypeDef;

/** @defgroup NVIC_Priority_Table 
@code  
 The table below gives the allowed values of the pre-emption priority and subpriority according
 to the Priority Grouping configuration performed by NVIC_PriorityGroupConfig function
  ============================================================================================================================
    NVIC_PriorityGroup   | NVIC_IRQChannelPreemptionPriority | NVIC_IRQChannelSubPriority  | Description
  ============================================================================================================================
   NVIC_PriorityGroup_0  |                0                  |            0-15             |   0 bits for pre-emption priority
                         |                                   |                             |   4 bits for subpriority
  ----------------------------------------------------------------------------------------------------------------------------
   NVIC_PriorityGroup_1  |                0-1                |            0-7              |   1 bits for pre-emption priority
                         |                                   |                             |   3 bits for subpriority
  ----------------------------------------------------------------------------------------------------------------------------    
   NVIC_PriorityGroup_2  |                0-3                |            0-3              |   2 bits for pre-emption priority
                         |                                   |                             |   2 bits for subpriority
  ----------------------------------------------------------------------------------------------------------------------------    
   NVIC_PriorityGroup_3  |                0-7                |            0-1              |   3 bits for pre-emption priority
                         |                                   |                             |   1 bits for subpriority
  ----------------------------------------------------------------------------------------------------------------------------    
   NVIC_PriorityGroup_4  |                0-15               |            0                |   4 bits for pre-emption priority
                         |                                   |                             |   0 bits for subpriority                       
  ============================================================================================================================
@endcode
*/
typedef enum IRQn_Type;
 /***stm32f10x.h***/
typedef enum IRQn
{
/******  Cortex-M3 Processor Exceptions Numbers ***************************************************/
  NonMaskableInt_IRQn         = -14,    /*!< 2 Non Maskable Interrupt                             */
  MemoryManagement_IRQn       = -12,    /*!< 4 Cortex-M3 Memory Management Interrupt              */
  BusFault_IRQn               = -11,    /*!< 5 Cortex-M3 Bus Fault Interrupt                      */
  UsageFault_IRQn             = -10,    /*!< 6 Cortex-M3 Usage Fault Interrupt                    */
  SVCall_IRQn                 = -5,     /*!< 11 Cortex-M3 SV Call Interrupt                       */
  DebugMonitor_IRQn           = -4,     /*!< 12 Cortex-M3 Debug Monitor Interrupt                 */
  PendSV_IRQn                 = -2,     /*!< 14 Cortex-M3 Pend SV Interrupt                       */
  SysTick_IRQn                = -1,     /*!< 15 Cortex-M3 System Tick Interrupt                   */

/******  STM32 specific Interrupt Numbers *********************************************************/
  WWDG_IRQn                   = 0,      /*!< Window WatchDog Interrupt                            */
  PVD_IRQn                    = 1,      /*!< PVD through EXTI Line detection Interrupt            */
  TAMPER_IRQn                 = 2,      /*!< Tamper Interrupt                                     */
  RTC_IRQn                    = 3,      /*!< RTC global Interrupt                                 */
  FLASH_IRQn                  = 4,      /*!< FLASH global Interrupt                               */
  RCC_IRQn                    = 5,      /*!< RCC global Interrupt                                 */
  EXTI0_IRQn                  = 6,      /*!< EXTI Line0 Interrupt                                 */
  EXTI1_IRQn                  = 7,      /*!< EXTI Line1 Interrupt                                 */
  EXTI2_IRQn                  = 8,      /*!< EXTI Line2 Interrupt                                 */
  EXTI3_IRQn                  = 9,      /*!< EXTI Line3 Interrupt                                 */
  EXTI4_IRQn                  = 10,     /*!< EXTI Line4 Interrupt                                 */
  DMA1_Channel1_IRQn          = 11,     /*!< DMA1 Channel 1 global Interrupt                      */
  DMA1_Channel2_IRQn          = 12,     /*!< DMA1 Channel 2 global Interrupt                      */
  DMA1_Channel3_IRQn          = 13,     /*!< DMA1 Channel 3 global Interrupt                      */
  DMA1_Channel4_IRQn          = 14,     /*!< DMA1 Channel 4 global Interrupt                      */
  DMA1_Channel5_IRQn          = 15,     /*!< DMA1 Channel 5 global Interrupt                      */
  DMA1_Channel6_IRQn          = 16,     /*!< DMA1 Channel 6 global Interrupt                      */
  DMA1_Channel7_IRQn          = 17,     /*!< DMA1 Channel 7 global Interrupt                      */

#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 */  
} IRQn_Type;
NVIC_PriorityGroupConfig(uint32_t NVIC_PriorityGroup)
/***misc.c  misc.h***/
#define NVIC_PriorityGroup_0         ((uint32_t)0x700) /*!< 0 bits for pre-emption priority
                                                            4 bits for subpriority */
#define NVIC_PriorityGroup_1         ((uint32_t)0x600) /*!< 1 bits for pre-emption priority
                                                            3 bits for subpriority */
#define NVIC_PriorityGroup_2         ((uint32_t)0x500) /*!< 2 bits for pre-emption priority
                                                            2 bits for subpriority */
#define NVIC_PriorityGroup_3         ((uint32_t)0x400) /*!< 3 bits for pre-emption priority
                                                            1 bits for subpriority */
#define NVIC_PriorityGroup_4         ((uint32_t)0x300) /*!< 4 bits for pre-emption priority
                                                            0 bits for subpriority */
#define IS_NVIC_PRIORITY_GROUP(GROUP) (((GROUP) == NVIC_PriorityGroup_0) || \
                                       ((GROUP) == NVIC_PriorityGroup_1) || \
                                       ((GROUP) == NVIC_PriorityGroup_2) || \
                                       ((GROUP) == NVIC_PriorityGroup_3) || \
                                       ((GROUP) == NVIC_PriorityGroup_4))

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;
}
NVIC_Init(NVIC_InitTypeDef* NVIC_InitStruct)
/***misc.c  misc.h***/
#define IS_NVIC_PREEMPTION_PRIORITY(PRIORITY)  ((PRIORITY) < 0x10)
#define IS_NVIC_SUB_PRIORITY(PRIORITY)  ((PRIORITY) < 0x10)
#define IS_NVIC_OFFSET(OFFSET)  ((OFFSET) < 0x000FFFFF)
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);
  }
}

4 小结

  博客教程看来看去完全看不出框架头绪,建好框架之后再看别人的博客教程又很容易捋思绪;

  还是老老实实看中文参考手册、官方编程手册、数据手册然后加上源码才是正道;

  可能自己有一点强迫症,自己写的博客如果不按照自己心中既定的思路写,按着别人的思路走基本记不住;

  可是悲伤的是自己的心中常常没有既定的思路,最耗费时间精力的就是这个搭思路了,唉;

  nvic的SCB system control block系统控制单元,EXTI外部中断事件控制器的相关部分也应该捋一捋,只能先占个坑了;