Notes The STM32 the NVIC (Nested Vectored Interrupt Controller)

EDITORIAL:
This article aims to summarize the backup, to facilitate future inquiries, as is the personal summary, if wrong, please correct me; in addition, most of the content from the Internet, books, and various manuals, should please inform the infringement, immediately delete posts to apologize.

 

table of Contents

A, NVIC simple explanation

Second, preemption priority and the sub priority

Third, the priority and preemption priority child distinguished (vernacular)

Four, NVIC configuration analysis


 

        In  STM32 notes the EXIT (external interrupt) chapter has been learned EXIT configuration, but, as we just learned, like C51 interrupt, the interrupt is class distinction, the experiment STM32 at the same time, can only be performed a thread (because it is a single-core, here a little knowledge into the operating system, do not understand skip it), anyway, the equivalent of two cars while waiting at the entrance of the large disk single channel tunnel junction (there traffic police command), but who should enter this single channel tunnel to reach the destination of it? Then they would have man-made (police) to control which vehicles first; then change my mind, they are now to explain the NVIC control

A, NVIC simple explanation

1, there are two priority control (only one interrupt it with different grades when school C51) in the STM32 (Cortex-M3):

  • Preemptive priority (NVIC_IRQChannelPreemptionPriority)
  • Also known as sub-priority response priority (NVIC_IRQChannelSubPriority)

2, NVIC is on the M3 core processor interrupt mechanism in all of M3 by NVIC realize, see figure below

 

3, Cortex-M3 supports a number of exceptions, including 16-4-1 = 11 abnormalities, and up to 240 external interrupts - referred IRQ. Specific use this interrupt sources 240 number, by the chip manufacturer. An interrupt signal is generated by the peripheral, in addition to the SysTick, NVIC all connected to an interrupt input signal line. Typically, processors typically support the interrupt 16 to 32, of course, there is outside of this.

4、CM3 除了支持 240条中断之外,NVIC还支持16 -4 -1 = 11个内部异常源,可以实现 fault管理机制。所以,CM3一共有 256个预定义的异常类型,根据上面的图可知

5、然后 Cortex-M3在内核水平上搭载了一个异常响应系统,支持为数众多的系统异常和外部中断。其中,编号为 1-15的对应系统异常,大于等于16的则全是外部中断。除了个别异常的优先级被定死外,其它异常的优先级都是可编程的。也就是说我们用的外设中断都是基于 15之后的中断

 

二、抢占优先级与子优先级

CM3把 256级优先级按位分成高低两段,分别称为抢占优先级和子优先级

NVIC中有一个寄存器是 “ 应用程序中断及复位控制寄存器 ”,它里面有一个位段名为 “ 优先级组 ” 。

该位段的值对每一个优先级可配置的异常都有影响——把其优先级分为 2个位段:MSB所在的位段(左边的)对应抢占优先级,而 LSB所在的位段(右边的)对应子优先级

在 Cortex-M3中定义了 8个 bit用于设置中断源的优先级,这 8个 bit可以有 7处分组的分配方式,当然还有凌驾于法律之上的三位老大:复位,NMI和硬 fault。它们无论何时出现,都立即无条件抢占所有优先级可编程的“平民异常”。那么这 7处分组可以如下分配:

  • 最高 7位用于指定抢占优先级,最低 1位用于指定子优先级
  • 最高 6位用于指定抢占优先级,最低 2位用于指定子优先级
  • 最高 5位用于指定抢占优先级,最低 3位用于指定子优先级
  • 最高 4位用于指定抢占优先级,最低 4位用于指定子优先级
  • 最高 3位用于指定抢占优先级,最低 5位用于指定子优先级
  • 最高 2位用于指定抢占优先级,最低 6位用于指定子优先级
  • 最高 1位用于指定抢占优先级,最低 7位用于指定子优先级

 

了解完 CM3所分布的等级后,我们再去了解 ST把它所封装的优先级

/**
@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
*/
  • 第0组:所有 4位用于指定响应优先级
  • 第1组:最高 1位用于指定抢占式优先级,最低3位用于指定响应优先级
  • 第2组:最高 2位用于指定抢占式优先级,最低2位用于指定响应优先级
  • 第3组:最高 3位用于指定抢占式优先级,最低1位用于指定响应优先级
  • 第4组:所有 4位用于指定抢占式优先级

上面的注释代码可以在 misc.h文件中查到

 

由于 Cortex-M3允许具有较少中断源时使用较少的寄存器位指定中断源的优先级,因此 ST把指定中断优先级的寄存器位减少到 4位,所以就造成了我们所看到的上面给出的 code注释一样的等级分布

然后配置的时候,他们所对应的宏如下:

/** @defgroup Preemption_Priority_Group 
  * @{
  */

#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

 

三、抢占优先级和子优先级的区分(白话文)

在作对比之前我们假设配置了 EXIT0EXIT1的线路,毕竟对比至少需要两个,这里用两个就够了,再多也是一样的,弄懂下面的例程就会举一反三了;

继续用开篇的那个例子,只不过,我们需要补充一些情节,在来到隧道之前有 n条一样路程的独立公路可以通往隧道入口的大圆盘处(有交警指挥的),每条公路在到达隧道之前都受红绿灯影响,并且这些红绿灯比较奇葩,其中一条公路的绿灯亮起,其他公路的灯都会变红色,这些红绿灯受总部调整;好了,现在基本完善了,下面的诠释例子就各靠本事脑补了

eg 1:抢占优先级的通俗理解

假设我们使用了 NVIC_PriorityGroup_4这组优先级分配,则所有 4位用于指定抢占式优先级,共 16个(0 - 15)抢占式优先级;然后指定 EXTI0_IRQn抢占优先级为 3,EXTI1_IRQn的抢占优先级为 1,因为子优先级没有得设置就不用管了;那么根据上面的例子可以形象化理解为:

有 16条(个抢占式优先级)附带红绿灯的路程一样的公路,然后这时有两辆(EXTI0_IRQn和 EXTI1_IRQn)同时出发警车需要通往隧道,而在到达隧道之前他们需要经过各自的路程,也就是所说的独立公路;由于 EXTI1_IRQn有重大任务在身(抢占优先级越小,优先级越高),于是 EXTI1_IRQn把警灯开起来,总部得知情况就优先帮 EXTI1_IRQn所在的公路打开绿灯(),从而使 EXTI1_IRQn优先通过隧道到达目的地(优先被执行),而 EXTI0_IRQn在 EXTI1_IRQn通过隧道后继续走没走完的路(因为 EXTI1_IRQn通过了,此时 EXTI0_IRQn的道路就变回绿灯了)

 

eg 2:接着再继续在抢占优先级上理解区分子优先级

这次我们使用 NVIC_PriorityGroup_3这组优先级分配,则最高 3位用于指定抢占式优先级,最低1位用于指定响应优先级,这样就变成了共 8个(0 - 7)抢占式优先级,2个(0 - 1)子优先级;指定 EXTI0_IRQn抢占优先级为 3,EXTI1_IRQn的抢占优先级为 3,而子优先级 EXTI0_IRQn为 0,EXTI1_IRQn为 1,(因为引入了子优先级,再把这些车贴上标号,对应子优先级)EXTI0_IRQn为 0号车,EXTI1_IRQn为 1号车;那么根据上面的例子可以形象化理解为:

现在只有 8条(个抢占式优先级)附带红绿灯的路程一样的公路,然而这两辆(EXTI0_IRQn和 EXTI1_IRQn)同时出发的警车在通过了独立公路后,同时到达隧道入口处的大圆盘(因为抢占式优先级相同),这时,由于该隧道是单通道,只能一辆一辆的通过,那么谁先通过啊,这下可烦倒了这位交警;于是他为了方便,直接利用车号顺序来作为通过顺序,这样肯定是 EXTI0_IRQn(0号车)先通行(因为抢占式优先级已经不能比较了,所以只能根据子优先级判断,是 EXTI0_IRQn先通过的,再者才是 EXTI1_IRQn通过;而且算数也是从 0先算的嘛)

扩展一下:如果这两辆(EXTI0_IRQn和 EXTI1_IRQn)在通过交警指挥的地方(大圆盘)之前,再来一台 USART1_IRQn(他的抢占优先级为 2、子优先级为 0);那么因为 USART1_IRQn抢占优先级比上面的两个优先级高一个等级,按照第一个例子演化,肯定会出现 EXTI0_IRQn和 EXTI1_IRQn被红灯限制所停止,而 USART1_IRQn率先通过,后面就按照上面第二个例子一样继续;这种情况就是所谓的中断嵌套

可以简短的看成,在公路这段路程是红绿灯(抢占式优先级)控制的,而进入隧道是由交警(子优先级)控制的

 

四、NVIC配置分析

继续根据之前的 STM32笔记之 GPIO引脚中设置的 Key输入配置代码分析 NVIC的配置

/* Enable the EXTI1 Interrupt */
NVIC_InitStructure.NVIC_IRQChannel = EXTI1_IRQn;            // 选择需配置的中断通道
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 2;   // 设置抢占等级
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;          // 设置子优先级(响应等级)
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;             // 使能该中断通道
NVIC_Init(&NVIC_InitStructure);


↓↓ 函数实现 ↓↓

/**
  * @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_PriorityGroupConfig()函数配置好

NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);    // 选择中断优先级分组等级为 2,即:最高的抢占优先级和子优先级(响应优先级)皆为 3


↓↓ 函数实现 ↓↓

/**
  * @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);
  }
}

要注意的几点是:

  1. 如果指定的抢占式优先级别或响应优先级别超出了选定的优先级分组所限定的范围,将可能得到意想不到的结果(坏的比较多);
  2. 抢占式优先级别相同的中断源之间没有嵌套关系;
  3. 如果某个中断源被指定为某个抢占式优先级别,又没有其它中断源处于同一个抢占式优先级别,则可以为这个中断源指定任意有效的响应优先级别。
  4. 抢占优先级越小,优先级越高;相同抢占优先级的中断不能嵌套;
  5. 相同抢占优先级 N个中断发生时,响应优先级越小的中断首先执行(不能嵌套),如果响应优先级也均相同,则根据各中断对应向量表的位置来确定,向量表中越靠前的中断先响应。

 

 

发布了40 篇原创文章 · 获赞 14 · 访问量 1万+

Guess you like

Origin blog.csdn.net/qq_42992084/article/details/104092151