STM32——中断、EXTI、按键中断实验

一、中断是什么?

1.1 中断的含义

说道中断,首先需要对中断这个概念有所了解。中断是指计算机运行过程中,出现某些意外情况需主机干预时,机器能自动停止正在运行的程序并转入处理新情况的程序,处理完毕后又返回原被暂停的程序继续运行。举个例子就比如你在工作的时候,突然打进来一个电话,这时你放下手中的针线活,接了电话,挂断电话之后再继续之前的工作,这就是一次中断,而打断你工作状态的电话就是中断源。而且有的中断还能被更高优先级的中断打断,就比如你上班接领导电话的时候,突然火灾警报响了,这是你必须要尽快离开,这就涉及到中断嵌套和优先级的问题了。
在这里插入图片描述

1.2 中断的作用(了解即可)

1.速度匹配,提高机器系统效率。 系统中处理机的工作速度远高于外围设备的工作速度。通过中断可以协调它们之间的工作。当外围设备需要与处理机交换信息时,由外围设备向处理机发出中断请求,处理机及时响应并作相应处理。不交换信息时,处理机和外围设备处于各自独立的并行工作状态。
2. 分时操作,维持系统可靠正常工作。现代计算机中,程序员不能直接干预和操纵机器,必须通过中断系统向操作系统发出请求,由操作系统来实现人为干预。主存储器中往往有多道程序和各自的存储空间。在程序运行过程中,如出现越界访问,有可能引起程序混乱或相互破坏信息。为避免这类事件的发生,由存储管理部件进行监测,一旦发生越界访问,向处理机发出中断请求,处理机立即采取保护措施。
3. 实时响应,满足实时处理要求。 在实时系统中,各种监测和控制装置随机地向处理机发出中断请求,处理机随时响应并进行处理。
4. 提供故障现场处理手段,可靠性高。 处理机中设有各种故障检测和错误诊断的部件,一旦发现故障或错误,立即发出中断请求,进行故障现场记录和隔离,为进一步处理提供必要的依据。

1.3 中断的流程

在这里插入图片描述

二、中断资源

2.1 NVIC中断控制器

NVIC英文全称是Nested Vectored Interrupt Controller,中文意思就是嵌套向量中断控制器,它属于M3内核的一个外设,控制着芯片的中断相关功能。

中断向量就是中断处理函数的入口地址,在stm32fxxx.s文件中已经定义好了,所有中断向量在一起就构成了中断向量表。NVIC把所有的外设中断和系统异常用一张向量表来管理,每个系统异常和外设中断都被分配相应的地址,除了一些系统异常的优先级不能改变外,其余的系统异常和中断的优先级都可变化。

STM32F10x芯片有84个中断通道,包括 16 个内核中断和 68 个可屏蔽中断,这些中断通道已按照不同优先级顺序固定分配给相应的外部设备。大家可以在《STM32F10x参考手册》第九章查找详细数据。
在这里插入图片描述

2.2 NVIC寄存器

在NVIC中定义了以下寄存器:

typedef struct
{
    
    
  __IO uint32_t ISER[8];                      /*!< Offset: 0x000  中断使能寄存器          */
       uint32_t RESERVED0[24];                                   
  __IO uint32_t ICER[8];                      /*!< Offset: 0x080  中断清除寄存器         */
       uint32_t RSERVED1[24];                                    
  __IO uint32_t ISPR[8];                      /*!< Offset: 0x100  中断使能挂起寄存器        */
       uint32_t RESERVED2[24];                                   
  __IO uint32_t ICPR[8];                      /*!< Offset: 0x180  中断清除挂起寄存器       */
       uint32_t RESERVED3[24];                                   
  __IO uint32_t IABR[8];                      /*!< Offset: 0x200  中断有效位寄存器           */
       uint32_t RESERVED4[56];                                   
  __IO uint8_t  IP[240];                      /*!< Offset: 0x300  中断优先级寄存器(8Bit wide) */
       uint32_t RESERVED5[644];                                  
  __O  uint32_t STIR;                         /*!< Offset: 0xE00  软件触发中断寄存器     */
}  NVIC_Type;  

在配置中断的时候我们一般只用到ISER、ICER、IP这三个寄存器,ISER寄存器用来使能中断,ICER寄存器用来清除中断,IP用来设置中断优先级

三、优先级的概念

当我们接到了两个电话,两个电话都安排你去做别的事,这时你需要先完成比较急的事,然后再完成不是太急的事,这就是优先级的问题。当有多个中断时,我们需要根据中断优先级判断先响应优先级高的中断,然后再响应优先级低的中断。

在NVIC中有一个专门的寄存器:中断优先级寄存器NVIC_IPRx,用来配置外部中断的优先级。IPR有8位,如果8位全使用,则可配置的优先级为0-255,数值小的优先级越高,但在STM32F373中,只是使用了高4位,可配置的优先级为0-15。

STM32 将中断分为 5 个组,组 0 ~ 4,该分组的设置是由 SCB->AIRCR 寄存器的 bit10 ~ 8 来定义的对这4位有5种搭配方式:
在这里插入图片描述
可以通过调用STM32的固件库中的函数NVIC_PriorityGroupConfig()选择使用哪种优先级分组方式,这个函数的作用是对中断的优先级进行分组,这个函数在系统中只能被调用一次,一旦分组确定就最好不要更改,其参数有下列5种:

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

例如组设置为 2,那么此时每个中断的中断优先寄存器的高四位中的最高 2位是用来设置抢占优先级,低 2 位设置响应优先级。每个中断,你可以设置抢占优先级为 0 ~ 3,响应优先级为 0 ~ 3。抢占优先级的级别高于响应优先级。而数值越小所代表的优先级就越高。

关于中断的相关库函数都在库文件misc.cmisc.h中。

优先级分为抢占式优先级响应式优先级
1、具有高抢占式优先级的中断可以在低抢占式优先级中断处理过程中被响应,即中断嵌套。
2、当两个中断源的抢占式优先级相同时,这两个中断将没有嵌套关系,当一个中断到来后,如果正在处理另一个中断,这个后到来的中断就要等到前一个中断处理完之后才能被处理。
3、如果两同等优先级的中断同时到达,则中断控制器根据他们的响应优先级高低来决定先处理哪一个;
4、如果他们的抢占式优先级和响应优先级都相等,则根据他们在中断表中的排位顺序决定先处理哪一个。
每一个中断源都必须定义2个优先级。

四、配置中断流程

在配置每个中断的时候,一般有四个编程要点:
1、使能外设某个中断,这个具体由每个外设的相关中断使能位控制。
注意:这里的使能中断请求,并不是之前NVIC中说到的中断使能,而是外设的中断使能。比如串口有发送完成中断,接收完成中断,这两个中断都是串口控制寄存器的相关中断使能位控制。
在这里插入图片描述
2、通过SCB_AIRCR寄存器配置中断优先级分组,通过函数NVIC_PriorityGroupConfig()完成。
在这里插入图片描述

3、初始化NVIC_InitTypeDef结构体,设置抢占优先级和和子优先级,使能中断请求。NVIC_InitTypeDef结构体在固件库头文件misc.h中定义,具体代码如下:

typedef struct
{
    
    
  uint8_t NVIC_IRQChannel;                    
  /*定义初始化的是哪个中断,这个我们可以在 stm32f10x.h 中找到每个中断对应的名字。例如 USART1_IRQn。 */

  uint8_t NVIC_IRQChannelPreemptionPriority;  
  /*定义这个中断的抢占优先级别。 */

  uint8_t NVIC_IRQChannelSubPriority;         
  /*定义这个中断的子优先级别。*/

  FunctionalState NVIC_IRQChannelCmd;         
  /*该中断是否使能。*/   
} NVIC_InitTypeDef;

中断源枚举:
在这里插入图片描述
在配置完结构体成员之后,我们调用初始化函数:void NVIC_Init(NVIC_InitTypeDef* NVIC_InitStruct)将配置好的值写入相应寄存器即可。

4、初始化外设中断以及NVIC之后,我们就可以编写中断服务函数了。编写中断服务函数有两个需要注意的地方:

①:中断服务函数名能不能随便写?
答案是不可以!在启动文件startup_stm32f10x_hd.s中,已经将中断向量表进行了初始化,在中断向量表中每一个中断都对应一个名称,启动文件根据这些名称对其进行了初始化工作。我们的中断服务函数的名称也要跟其一一对应。如果名称写错了,编译器并不会报错,但是却无法执行功能。

②:中断服务函数在哪里写?
为了方便管理和查看,中断服务函数一般都是放在stm32f10x_it.c文件中。

五、EXTI(外部的中断事件控制器)

外部中断指的是在中断向量表中EXTI的外部中断,STM32的每个IO都可以作为外部中断输入。
在这里插入图片描述
首先我们从框图的右下角开始看,输入线一般是存在电平变化的IO口,它可以是任意一个GPIO也可以是一些外设的事件。
然后就到了边沿检测电路,这里主要是选择触发方式是上升沿还是下降沿或者是都可以触发中断。
再往后是一个或门电路,这里有两个输入,一个来自边沿检测电路,一个来自软件中断事件寄存器。也即是说我们不光可以通过外设来产生中断,我们也可以通过程序来设置中断。
再往后信号分成了两个部分:
一部分进入请求挂起寄存器,这时单片机系统已经接收到了中断请求,只有在中断屏蔽寄存器确认该路允许中断的情况下,才会将中断请求发送到NVIC,然后调用相关的中断程序。
另一部分和时间屏蔽寄存器的信号一起经过一个与门,如果两者同为1,那么就会产生一个脉冲信号。一般是用来触发TIM或者ADC转换。

5.1 中断\事件 输入线

STM32的中断控制器支持20个输入线,每个GPIO都可被设置为输入线,占用EXTI0~EXTI15。以PA0为例,EXTI0通过AFIO的外部中断配置寄存器(AFIO_EXTICR1)的EXTI0[3:0]位选择配置为PA0,其他的输入线也是如此配置。
在这里插入图片描述
此外还有四根特定的外设输入线:
线16:连接到PVD输出;
线17:连接到RTC闹钟事件;
线18:连接到USB唤醒事件;
线19:以太网换唤醒事件(STM32F10x没有这一路)。

5.2 EXTI初始化结构体

结构体一般用于设置外设的工作参数,并有外设初始化函数进行调用,将参数配置到外设相关的寄存器中,比如EXTI_Init()。EXTI初始化结构体在文件stm32f10x_exti.h中,初始化库函数定义在stm32f10x_exti.c文件中。
在这里插入图片描述
EXTI_Line:EXTI输入线选择,可以选择EXTI0 - 19。

EXTI_Mode:选择EXTI模式,可选择为产生中断(EXTI_Mode_Interrupt)或者是产生事件(EXTI_Mode_Event)。

EXTI_Trigger:选择EXTI边沿触发事件,可选为EXTI_Trigger_Rising(上升沿触发),EXTI_Trigger_Falling(下降沿触发),EXTI_Trigger_Rising_Falling(上升沿下降沿都触发)。

EXTI_LineCmd:选择是否使能EXTI线,可选ENABLE或者是DISABLE。

六、按键中断实验

实验目的:通过按键产生中断改变RGB灯状态。初始状态下,RGB彩灯是暗的,此时按下开发板按键1,灯亮。再次按下按键1,灯灭。按下按键2并弹开,灯亮,再按下按键2并弹开,灯灭。

说明:本实验采用野火指南者开发板完成,对于其他STM32开发板,大家修改一下相关的Pin即可。
按键中断实验主要有以下几个部分:

1) 初始化 IO 口为输入。
这一步设置你要作为外部中断输入的 IO 口的状态,可以设置为上拉/下拉输入,也可以设置为浮空输入,但浮空的时候外部一定要带上拉,或者下拉电阻。否则可能导致中断不停的触发。在干扰较大的地方,就算使用了上拉/下拉,也建议使用外部上拉/下拉电阻,这样可以一定程度防止外部干扰带来的影响。

2) 开启 IO 口复用时钟,设置 IO 口与中断线的映射关系。
STM32 的 IO 口与中断线的对应关系需要配置外部中断配置寄存器 EXTICR,这样我们要先开启复用时钟,然后配置 IO 口与中断线的对应关系。才能把外部中断与中断线连接起来。

3) 开启与该 IO 口相对的线上中断/事件,设置触发条件。
这一步,我们要配置中断产生的条件, STM32 可以配置成上升沿触发,下降沿触发,或者任意电平变化触发,但是不能配置成高电平触发和低电平触发。这里根据自己的实际情况来配置,同时要开启中断线上的中断。 这里需要注意的是:如果使用外部中断,并设置该中断的 EMR位的话,会引起软件仿真不能跳到中断,而硬件上是可以的。而不设置 EMR,软件仿真就可以进入中断服务函数,并且硬件上也是可以的。建议不要配置 EMR 位。

4) 配置中断分组(NVIC),并使能中断。
这一步,我们就是配置中断的分组,以及使能,对 STM32 的中断来说,只有配置了 NVIC的设置,并开启才能被执行,否则是不会执行到中断服务函数里面去的。

5) 编写中断服务函数。
这是中断设置的最后一步,中断服务函数,是必不可少的,如果在代码里面开启了中断,但是没编写中断服务函数,就可能引起硬件错误,从而导致程序崩溃!所以在开启了某个中断后,一定要记得为该中断编写服务函数。在中断服务函数里面编写你要执行的中断后的操作。
通过以上几个步骤的设置,我们就可以正常使用外部中断了。

6.1 新建工程

找一个空白的工程模板
在这里插入图片描述
然后在User文件夹中新建LED和EXTI两个文件夹,将我们需要的文件放在里面,然后在Keil中添加到工程中。

猜你喜欢

转载自blog.csdn.net/qq_36535414/article/details/120349005