[其他ST产品]

STM32中断笔记——关于NVIC的两个问题

[复制链接]
134|23
手机看帖
扫描二维码
随时随地手机跟帖
慢醇|  楼主 | 2022-6-28 14:53 | 显示全部楼层 |阅读模式
AC, AD, IO, TI, ST, ic, vi
   STM32 中断非常强大,每个外设都可以产生中断,中断也是STM32非常重要的一个内容。

       NVIC:嵌套向量中断控制器,属于内核外设,管理着包括内核和片上所有外设的中断相关的功能。

       ARM cortex_m3 内核支持 256 个中断(16 个内核+240 外部)和可编程 256 级中断优先级的设置,与其相关的中断控制和中断优先级控制寄存器(NVIC、SYSTICK 等)也都属于cortex_m3 内核的部分。STM32 采用了 cortex_m3 内核,所以这部分仍旧保留使用,但 STM32并没有使用 cortex_m3 内核全部的东西(如内存保护单元 MPU 等),因此它的 NVIC 是cortex_m3 内核的 NVIC 的子集。

使用特权

评论回复
评论
慢醇 2022-6-28 14:55 回复TA
———————————————— 版权声明:本文为CSDN博主「落子摘星」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。 原文链接:https://blog.csdn.net/wlswls1711/article/details/109502714 
慢醇|  楼主 | 2022-6-28 14:55 | 显示全部楼层
这篇文章主要记录我学习中断时关于NVIC的两个问题。

使用特权

评论回复
慢醇|  楼主 | 2022-6-28 14:56 | 显示全部楼层
问题1:NVIC_Type和NVIC_InitTypeDef结构体的关系?

       NVIC寄存器的定义是在core_cm3.h中:
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;                                               

使用特权

评论回复
慢醇|  楼主 | 2022-6-28 14:57 | 显示全部楼层
在配置中断的时候我们一般只用 ISER、ICER 和 IP 这三个寄存器,ISER 用来使能中断,ICER用来失能中断,IP 用来设置中断优先级。而初始化这个结构体的函数就是void

使用特权

评论回复
慢醇|  楼主 | 2022-6-28 14:59 | 显示全部楼层
NVIC_Init(NVIC_InitTypeDef* NVIC_InitStruct),在misc.c中定义,比较复杂,有兴趣可以看看:
/**
  * [url=home.php?mod=space&uid=247401]@brief[/url]  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);
  }
}

使用特权

评论回复
慢醇|  楼主 | 2022-6-28 15:01 | 显示全部楼层
  NVIC初始化函数是通过传递的参数NVIC_InitTypeDef* NVIC_InitStruct来初始化NVIC_Type结构体的,而对NVIC_InitTypeDef结构体的初始化就要看具体情况,比如说普通的按键外部中断等等。

使用特权

评论回复
慢醇|  楼主 | 2022-6-28 15:07 | 显示全部楼层
NVIC_InitTypeDef结构体结构体定义在misc.h中:

使用特权

评论回复
慢醇|  楼主 | 2022-6-28 15:08 | 显示全部楼层
typedef struct
{
  uint8_t NVIC_IRQChannel;                    /*!< Specifies the IRQ channel to be enabled or disabled.
                                                   This parameter can be a value of [url=home.php?mod=space&uid=144993]@ref[/url] 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;

使用特权

评论回复
慢醇|  楼主 | 2022-6-28 15:09 | 显示全部楼层
NVIC_Type和NVIC_InitTypeDef这两个结构体就是通过NVIC_Init函数来联系起来的。即NVIC_Type和NVIC_InitTypeDef的关系就是:先在具体的外部中断里将NVIC_InitTypeDef结构体初始化完成,然后通过NVIC_Init函数,把NVIC_InitTypeDef作为参数,将NVIC_Type结构体初始化。

使用特权

评论回复
慢醇|  楼主 | 2022-6-28 15:09 | 显示全部楼层
问题2:关于中断优先级和中断优先级分组的问题,这两个有什么区别?

       中断优先级分组的设定是在NVIC_Type和NVIC_InitTypeDef结构体初始化的前面。

使用特权

评论回复
慢醇|  楼主 | 2022-6-28 15:10 | 显示全部楼层
中断优先级的设定是NVIC->IPRx寄存器(共8bit,只使用高4bit):
6259762baa96170595.png

使用特权

评论回复
慢醇|  楼主 | 2022-6-28 15:11 | 显示全部楼层
     相关函数:NVIC_SetPriority(IRQn_Type IRQn, uint32_t priority) // core_cm3.h 1586行

使用特权

评论回复
慢醇|  楼主 | 2022-6-28 15:11 | 显示全部楼层
/**
* @brief  Set the priority for an interrupt
*
* @param  IRQn      The number of the interrupt for set priority
* @param  priority  The priority to set
*
* Set the priority for the specified interrupt. The interrupt
* number can be positive to specify an external (device specific)
* interrupt, or negative to specify an internal (core) interrupt.
*
* Note: The priority cannot be set for every core interrupt.
*/
static __INLINE void NVIC_SetPriority(IRQn_Type IRQn, uint32_t priority)
{
  if(IRQn < 0) {
    SCB->SHP[((uint32_t)(IRQn) & 0xF)-4] = ((priority << (8 - __NVIC_PRIO_BITS)) & 0xff); } /* set Priority for Cortex-M3 System Interrupts */
  else {
    NVIC->IP[(uint32_t)(IRQn)] = ((priority << (8 - __NVIC_PRIO_BITS)) & 0xff);    }        /* set Priority for device specific Interrupts  */
}

使用特权

评论回复
慢醇|  楼主 | 2022-6-28 15:12 | 显示全部楼层
即就是上面函数中的NVIC->IP。

       中断优先级分组的设定是SCB->AIRCR:PRIGROUP[10:8]

使用特权

评论回复
慢醇|  楼主 | 2022-6-28 15:12 | 显示全部楼层
7295362baa9e1c5b12.png

使用特权

评论回复
慢醇|  楼主 | 2022-6-28 15:13 | 显示全部楼层
优先级分组是由3个bit来控制,所以可以有2 ^ 3 = 8 个分组,但是STM32只用了五组(即0~4),如下图:
9121162baa9fed6e72.png

使用特权

评论回复
慢醇|  楼主 | 2022-6-28 15:14 | 显示全部楼层
相关函数:void NVIC_PriorityGroupConfig(uint32_t NVIC_PriorityGroup)
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;
}

使用特权

评论回复
慢醇|  楼主 | 2022-6-28 15:15 | 显示全部楼层
所以中断优先级和中断优先级分组有什么区别或关系呢?SCB->AIRCR:PRIGROUP[10:8]设置的是中断优先级分组,也就是它的值是NVIC_PriorityGroup_0、NVIC_PriorityGroup_1、NVIC_PriorityGroup_2、NVIC_PriorityGroup_3、NVIC_PriorityGroup_4这五个值,而NVIC->IP设置的是分组中主优先级和子优先级的值,STM32使用高四位,根据不同的分组,主优先级和子优先级占用这四位中不同的位数。
       要注意的就是:中断优先级的设置和中断优先级分组的设置是在不同寄存器中设置的,前者是NVIC->IPRx,后者是SCB->AIRCR。

使用特权

评论回复
慢醇|  楼主 | 2022-6-28 15:16 | 显示全部楼层
以上就是我遇到的两个问题及其答案,如有错误,欢迎指正。

       中断编程的顺序有四个步骤:
       ①使能中断请求;
       ②配置中断优先级分组;
       ③配置NVIC寄存器,初始化NVIC_InitTypeDef;
       ④编写中断服务函数。

使用特权

评论回复
慢醇|  楼主 | 2022-6-28 15:17 | 显示全部楼层
第一个步骤,使能中断请求是配置外设相应的寄存器,配置好外设的中断使能寄存器后,外设就可以触发中断,即可以向CPU发送中断请求,但是要让CPU接受中断请求,还需要配置NVIC(嵌套向量中断控制器)的中断使能位,这个使能位是NVIC结构体的一个成员,即上面结构体中的__IO uint32_t ISER[8]。可以说,外设的中断请求使能是小门,而NVIC(嵌套向量中断控制器)的中断使能是大门,一个大门对应多个小门。

使用特权

评论回复
发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则