STM32
直播中

崔映芬

7年用户 926经验值
私信 关注
[问答]

NVIC中断优先级管理简介

如何对NVIC中断管理函数进行配置?
中断优先级设置的步骤有哪些?

回帖(1)

万菲

2021-10-27 10:51:20
  NVIC 中断优先级管理
  Nested Vectored Interrupt Controller,嵌套向量中断控制器。
  CM3支持256个中断,16个内核中断,240个外部中断,256级可编程中断设置。
  STM32使用了其中一部分,16个内核中断,107系列有68个可屏蔽中断(103系列只有60个),16级可编程的中断优先级。
  中断寄存器
  ISER[8],Interrupt Set-Enable Registers,中断使能寄存器组,用8个32位寄存器控制(256个可编程中断),每个位控制一个中断。由于STM32f103只有60个可屏蔽中断,于是只用了 ISER[0] 和 ISER[1] 64个中断中的前60位。
  ISER[0] 的 bit0 至 bit31 对应中断0至31,ISER[1] 的 bit0 至 bit27 对应中断32至59,使能某个中断就将对应的 ISER 置1
  ICER[8],Interrupt Clear-Enable Register,中断除能寄存器组。中断对应与ISER相同,但作用是置1清除中断使能
  ISPR[8],Interrupt Set-Pending Register,中断挂起控制寄存器组。置1将正在进行的中断挂起,暂停等待执行,执行同级或更高级别的中断
  ICPR[8],Interrupt Clear-Pending Register,中断解挂控制寄存器组,置1将挂起的中断解挂
  IABR[8],Interrupt Active Bit Register,中断激活标志位寄存器组,只读寄存器,为1表示该位对应的中断正在被执行
  IP[240],Interrupt Priority Registers,中断优先级控制寄存器组,相当重要! STM32的中断分组与其密切相关。
  IP[240]有240个8bit寄存器组成,每个可屏蔽中断占用8bit,总共可表示240个可屏蔽中断,STM32f1使用其中60个,IP[59]至IP[0]分别对应中断59至0。每个可屏蔽中断只用高4位,分为抢占优先级和响应优先级,抢占优先级在前、响应优先级在后,各占几位由SCB-》AIRCR中的中断分组设置。采用库函数==NVIC_PriorityGroupConfig () ==配置。
  
  如果组设置为3,则每个中断的中断优先寄存器高4位中前3位是抢占优先级,优先级级别0-7;低1位是响应优先级,优先级级别0-1。
  STM32的中断向量有抢占属性和响应属性,属性编号越小,优先级别越高。
  抢占属性是打断其他中断的属性,有这个属性就有嵌套中断。由库函数NVIC_IRQChannelPreemptionPriority() 进行参数配置。
  响应属性应用在抢占属性相同时(前提),两个中断向量的抢占优先级相同时,如果同时到达,则优先处理响应优先级高的中断。由库函数NVIC_IRQChannelSubPriority() 进行参数配置。
  库函数配置
  NVIC 中断管理函数声明在 misc.c 文件里
  1. 中断优先级分组函数 NVIC_PriorityGroupConfig()
  此函数在系统工程中只能被调用一次!一旦确定好中断分组就不能更改!
  目前了解到的在主函数中调用该函数
  按 F12 查看函数定义块:
  void NVIC_PriorityGroupConfig(uint32_t NVIC_PriorityGroup)
  {
  assert_param(IS_NVIC_PRIORITY_GROUP(NVIC_PriorityGroup));
  SCB-》AIRCR = AIRCR_VECTKEY_MASK | NVIC_PriorityGroup;
  }
  可以看到,这个函数通过设置 SCB-》AIRCR 寄存器设置中断分组。
  补充:
  assert_param(),断言机制函数,在stm32f10x_conf.h文件中可以看到 assert_param() 是一个宏定义,作用就是检测传递给函数的参数是否是有效的参数,返回值是 0假 1真。
  中断优先组配置示例:分组值为2,2优先2抢占:
  NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); 暂时看到的此函数调用都是在主函数里,以后看到其他程序结构再补充:
  
  2. 中断初始化函数 NVIC_Init
  函数定义:
  void NVIC_Init(NVIC_InitTypeDef* NVIC_InitStruct) 查看结构体 NVIC_InitTypeDef 的成员变量:
  typedef struct
  {
  uint8_t NVIC_IRQChannel;
  uint8_t NVIC_IRQChannelPreemptionPriority;
  uint8_t NVIC_IRQChannelSubPriority;
  FunctionalState NVIC_IRQChannelCmd;
  }
  NVIC_InitTypeDef;
  NVIC_IRQChannel,定义初始化的是哪个中断,例如 USART1_IRQn
  NVIC_IRQChannelPreemptionPriority,定义这个中断的抢占优先级别,级别数不超过2的“抢占优先级位数”次方。(一个是位数,一个是级别数)
  NVIC_IRQChannelSubPriority,定义这个中断的响应优先级别,级别数不超过2的“响应优先级位数”次方。
  NVIC_IRQChannelCmd,该中断是否使能
  IRQ全称为Interrupt Request,即是“中断请求”的意思
  中断初始化配置示例:
  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 寄存器
  通常如果写外部中断时,此代码块放在 exti.cEXTI_Init() 函数内部,每个端口配置结束都需要一个NVIC_Init(&NVIC_InitStructure);
  小结
  中断优先级设置的步骤:
  系统运行开始的时候设置中断分组。确定组号,也就是确定抢占优先级和子优先级的分配位数。调用函数为 NVIC_PriorityGroupConfig();
  设置所用到的中断的中断优先级别,对每个中断调用函数为 NVIC_Init();
  STM32 外部中断
  基础知识
  STM32 外部 IO 口的中断功能
  文件: stm32f10x_exti.c/h
  每个 IO 口都可以作为外部中断的中断输入口,支持19个外部中断/事件请求,每个中断设有状态位、独立的触发/屏蔽设置。
  19个外部中断为:
  线 0-15:对应外部 IO 口的输入中断 (供 IO 口使用的中断线)
  线 16 :连接到PVD输出(Programmable Votage Detector,可编程电压监测器)
  线 17 :连接到RTC闹钟事件
  线 18 :连接到USB唤醒事件
  映射方式:
  
  每个中断线对应最多7个IO口,但每次只能连接到一个IO口上,利用选择器的配置来决定中断线配置到哪个IO口上去。
  库函数配置
  1. 映射关系配置 GPIO_EXTILineConfig()
  外部中断端口映射配置示例 (GPIOE_2 与 EXTI_2):
  GPIO_EXTILineConfig(GPIO_PortSourceGPIOE,GPIO_PinSource2);
  2. 中断初始化 EXTI_Init()
  void EXTI_Init(EXTI_InitTypeDef* EXTI_InitStruct);
  //结构体函数参数
  查看一下结构体:
  typedef struct
  {
  uint32_t EXTI_Line;
  EXTIMode_TypeDef EXTI_Mode;
  EXTITrigger_TypeDef EXTI_Trigger;
  FunctionalState EXTI_LineCmd;
  }EXTI_InitTypeDef;
  有4个需要设置的参数:
  中断线标号,EXTI_Line0~EXTI_Line15
  中断模式,中断 EXTI_Mode_Interrupt 和事件 EXTI_Mode_Event
  触发方式,下降沿触发 EXTI_Trigger_Falling,上升沿触发 EXTI_Trigger_Rising,任意电平(上升沿和下降沿)触发EXTI_Trigger_Rising_Falling
  使能,ENABLE
  中断和事件的区别
  简而言之,中断是需要经过芯片内部程序处理,然后跳转的中断程序
  事件是检测到某一动作(电平边沿)触发事件发生了,进而由提前设置好的相应的联动硬件自动完成产生的结果
  中断是红线
  事件是绿线
  
  外部中断初始化配置示例:
  EXTI_InitTypeDef EXTI_InitStructure;
  //定义结构体
  EXTI_InitStructure.EXTI_Line=EXTI_Line4;
  //中断线4
  EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt;
  //中断模式
  EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Falling;
  //下降沿触发
  EXTI_InitStructure.EXTI_LineCmd = ENABLE;
  //使能
  EXTI_Init(&EXTI_InitStructure);
  //根据 EXTI_InitStruct 中指定的参数初始化外设 EXTI 寄存器
  3. 设置中断线2的中断优先级
  优先级配置见第一部分NVIC的配置
  NVIC_InitTypeDef NVIC_InitStructure;
  //定义NVIC结构体
  NVIC_InitStructure.NVIC_IRQChannel = EXTI2_IRQn;
  //使能按键外部中断通道
  NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0x02;
  //抢占优先级 2,
  NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0x02;
  //响应优先级 2
  NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
  //使能外部中断通道
  NVIC_Init(&NVIC_InitStructure);
  //中断优先级分组初始化
  4. 中断服务函数
  STM32一共15个IO口外部中断线,共有6个中断函数:
  EXPORT EXTI0_IRQHandler
  EXPORT EXTI1_IRQHandler
  EXPORT EXTI2_IRQHandler
  EXPORT EXTI3_IRQHandler
  EXPORT EXTI4_IRQHandler
  EXPORT EXTI9_5_IRQHandler
  EXPORT EXTI15_10_IRQHandler
  0-4每个中断线对应一个中断函数,5-9共用EXTI9_5_IRQHandler,10-15共用EXTI15_10_IRQHandler
  当多线共用一个处理函数的时候,就必须写EXTI_GetITStatus()判断到底是哪一线中断请求
  1. ITStatus EXTI_GetITStatus();
  用在中断服务函数的开头,判断中断是否发生
  EXTI_GetFlagStatus()与之的区别
  EXTI_GetITStatus() 函数中会先判断这种中断是否使能,使能了才去判断中断标志位
  而EXTI_GetFlagStatus() 直接用来判断状态标志位
  2. EXTI_ClearITPendingBit(EXTI_Line0);
  中断服务函数结束之前调用,清除某中断线上的中断标志位
  EXTI_ClearFlag(),与之功能相同
  常用的中断服务函数格式:
  void EXTI2_IRQHandler(void)
  {
  if(EXTI_GetITStatus(EXTI_Line2)!=RESET)
  //判断某个线上的中断是否发生
  {
  中断逻辑…
  EXTI_ClearITPendingBit(EXTI_Line2);
  //清除 LINE 上的中断标志位
  }
  }
  外部中断配置示例
  1. exit.c
  包含4个函数:
  EXTI_Init()
  外部中断初始化,(以按键功能为例):
  void EXTIX_Init(void)
  {
  EXTI_InitTypeDef EXTI_InitStructure;
  NVIC_InitTypeDef NVIC_InitStructure;
  //调用结构体
  调用 KEY_Init 函数,来初始化外部中断输入的 IO 口。
  KEY_Init();//初始化按键对应 io 模式
  2.调用RCC_APB2PeriphClockCmd()函数来使能复用功能时钟。
  RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO,ENABLE);
  //外部中断,需要使能 AFIO 时钟
  4.接着配置中断线和 GPIO 的映射关系。
  //GPIOC.5 与中断线5映射
  GPIO_EXTILineConfig(GPIO_PortSourceGPIOC,GPIO_PinSource5);
  5.然后初始化中断线。
  EXTI_InitStructure.EXTI_Line=EXTI_Line5;
  EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt;
  EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Falling;
  EXTI_InitStructure.EXTI_LineCmd = ENABLE;
  EXTI_Init(&EXTI_InitStructure);
  6.中断初始化、配置优先级
  NVIC_InitStructure.NVIC_IRQChannel = EXTI0_IRQn;
  NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0x02;
  NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0x02;
  NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
  NVIC_Init(&NVIC_InitStructure);
  EXTI0_IRQHandler()
  中断处理函数
  void EXTI0_IRQHandler(void)
  {
  delay_ms(10);
  if(WK_UP==1)//WK_UP在key.h文件中定义
  {
  LED0=!LED0;//LED0在led.h文件中定义
  LED1=!LED1;
  }
  EXTI_ClearITPendingBit(EXTI_Line0);
  //清除EXTI0线路挂起位
  }
  EXTI9_5_IRQHandler()
  EXTI15_10_IRQHandler()
  小结
  使用 IO 口外部中断的一般步骤:
  初始化 IO 口为输入。
  开启 IO 口复用时钟,设置 IO 口与中断线的映射关系。
  初始化线上中断(EXTI),设置触发条件等。
  配置中断分组(NVIC),并使能中断。
  编写中断服务函数。
举报

更多回帖

发帖
×
20
完善资料,
赚取积分