嵌入式开发十五:STM32中断系统

       STM32 中断非常强大,几乎每个外设都可以产生中断,因此这里我们单独来介绍它,为后面介绍外设中断做铺垫。

目录

一、中断的介绍

1.1 中断的概念

1.2 中断优先级:

1.3  中断的嵌套

1.4 内部中断和外部中断

1.5 中断响应和事件响应

1.6 中断的作用和意义

1.6.1 中断的作用

1.6.2 中断的意义

二、STM32 的中断资源

三、STM32中断管理控制器----NVIC

 3.1 NVIC的基本概念 

 3.2 NVIC相关寄存器介绍(理解)

 3.3 NVIC 中断配置固件库

​ 3.4 NVIC工作原理

 3.5 STM32中断优先级基本概念

 3.6 STM32中断优先级分组

 3.7  如何使用库函数实现中断分组设置以及中断优先级管理

3.7.1设置中断优先级分组函数

3.7.2 对于每个中断如何确定他的抢占优先级和响应优先级

四、 STM32 中断编程(非常重要)

五、外部中断/事件控制器—EXTI

5.1 STM32F4 GPIO外部中断简介

5.2 外部中断/事件线映射

5.3  EXTI简介

5.3.1 概念

5.3.2 组成

5.3.3 工作过程

5.3.4 支持的触发方式

5.3.5 响应的方式

5.4 EXTI 框图

5.5 SYSCFG的介绍

5.5.1 外部中断配置寄存器

5.6 EXTI 初始化结构体详解

5.7 如何使用中断

六、 使用库函数配置STM32外部中断输入的步骤(非常重要)

6.1 第一步:使能GPIO时钟和使能SYSCFG时钟

6.2  第二步:初始化 IO 口为输入模式

6.3 第三步:配置SYSCFG,设置 IO 口与中断线的映射关系。

6.4 第四步:初始化线上中断EXTI

6.5 第五步:初始化中断分组(NVIC),并使能中断。

6.6 第六步:编写中断服务函数

 七、相关补充

一、中断的介绍

1.1 中断的概念

        中断其实就是当 CPU 执行程序时, 由于发生了某种随机的事件(外部或内部),引起 CPU 暂时中断正在运行的程序, 转去执行一段特殊的服务程序(中断服务子程序或中断处理程序),以处理该事件,该事件处理完后又返回被中断的程序继续执行,这一过程就称为中断,简而言之:打断CPU执行正常的程序,转而处理紧急程序,然后返回原暂停的程序继续运行,就叫中断。引发中断的称为中断源。比如:看电视时突然门铃响,那么门铃响就相当于中断源。中断示意图如下图所示:

关键概念:

  1.     事件的发出者--------中断源
  2.     此次事件本身-------中断

中断是⼀个动作 :是由中断源向CPU发出的/申请的

中断是⼀种状态:主程序被CPU暂停了

中断是⼀个功能:⼀个实现特定功能的函数

1.2 中断优先级:

         当有多个中断源同时申请中断时,CPU会根据中断源的轻重缓急进行裁决优先响应更加紧 急的中断源。

1.3  中断的嵌套

         当一个中断程序正在运行时,又有新的更高优先级的中断源申请中断,CPU再次暂停当前中断程序,转而去处理新的中断程序,处理完成后依次进行返回。

1.4 内部中断和外部中断

  1. 内部中断:由CPU内部事件所引起的中断。也称为软件中断、异常。 系统调用。
  2. 外部中断:由外部设备事件所引起的中断。 IO中断、时钟中断....。

【注意】我们平常说的中断指的是外部中断。

1.5 中断响应和事件响应

  1. 中断响应:某个事件发生时,触发了中断,打断了正在执行的CPU,让CPU转而处理此事件。
  2. 事件响应:某个事件发生时,触发了特定的信号,此信号不经过CPU,而是触发了其他外设模块。​​​​​​​

核心区分:有无经过CPU

1.6 中断的作用和意义

1.6.1 中断的作用

  1. 精准控制:在规定的时间内做出相应的操作,如:温度监控
  2. 紧急处理:检测到故障,需要第⼀时间处理,如:电梯门夹人了
  3. 数据传输:当不确定数据何时会到来时,可采用中断的方式,如:串口数据接收

1.6.2 中断的意义

     提高CPU的工作效率、节省CPU资源、具有处理突发事件的能力-----------中断使得计算机系统具备处理突发事件的能力,提高了CPU的工作效率,如果没有中断系统,CPU就只能按照原来的程序编写的先后顺序,对各个外设进行查询和处理,即轮询的工作方式,轮询方法貌似公平,但实际工作效率却很低,浪费cpu资源,还不能及时响应紧急事件。

简而言之:高效处理紧急程序,不会一直占用CPU资源

二、STM32 的中断资源

       M4内核支持256个中断,包含16个系统(内核)中断和240个外部中断。但是芯片厂商⼀般不会把内核的这些资源全部用完。 对于STM32F407具有82个可屏蔽中断通道(外设中断)。10个系统中断。 中断通道:指的是中断源。

三、STM32中断管理控制器----NVIC

 3.1 NVIC的基本概念 

       NVIC 英文全称是 Nested Vectored Interrupt Controller,中文意思就是嵌套向量中断控制器,它属于 M4 内核的一个外设,控制着芯片的中断相关功能。 由于 ARM 给 NVIC 预留了非常多的功能,NVIC支持:256个中断(16内核 + 240外部),支持:256个优先级,允许裁剪!但对于使用 M4 内核设计芯片的公司可能就不需要这么多功能,于是就需要在 NVIC 上裁剪。ST 公司的 STM32F407 芯片内部中断数量就是 NVIC 裁剪后的结果。 Crotex-M4 内核支持 256 个中断,其中包含了 16 个内核中断和 240 个外部中断。但 STM32 并没有使用 M4 内核的全部东西,而是只用了它的一部分。 STM32F407 芯片支持 82 个可屏蔽中断通道(外部中断),除了个别异常的优先级被定死外, 其它异常的优先级都是可编程的。每个中断通道(中断源)都拥有16个可编程的优先等级(优先级),可对优先级进行分组。如下图所示:

【注意】:NVIC属于内核中的外设,默认时钟是开启的,因此不需要开启时钟;

区分内核外设与片上外设

        片上外设与内核外设他们都在芯片里面,但内核外设是在内核CPU里面,片上外设是在内核之外。

       STM32F407支持10个内部中断和82个外部中断,其实通俗理解:中断就是一个函数,只不过ST公司把这些外设对应的中断函数名已经帮我们写好了,并且这些中断通道已按照不同优先级顺序固定分配给相应的外部设备。而中断向量表就是用来存放所有函数名,我们知道函数名就是函数的入口地址,本质上就是一个地址,当中断触发后,就会去执行中断服务函数,从 STM32F4xx 中文参考手册的中断向量表可以知道具体分配到那些外设,这里只截取一部分,如需了解更详细可参考《STM32F4xx 中文参考 手册》 中断和向量章节内容,中断向量表如下图所示:表中的优先级是硬件编号,数字越小优先级越高这里复位中断的编号最小,所以一按板子上的复位键会立马执行复位程序。

        上面说到 NVIC 控制着芯片的中断相关功能,那么肯定这个外设有很多对应的寄存器, 在固件库 core_cm4.h 文件内定义了一个 NVIC 结构体,里面定义了相关寄存器, 下面就来介绍下NVIC相关寄存器。

3.2 NVIC相关寄存器介绍(理解)

       我们的STM32F407芯片的所有中断在NVIC的管理下有序的运行着。那只有了解了他的寄存器才能方便使用这些中断。在固件库中,NVIC 的结构体定义如下:

在配置中断时,我们通常使用的只有 ISER、 ICER 和 IP 这三个寄存器, ISER 是中断使能寄存器,ICER 是中断清除寄存器,IP 是中断优先级寄存器。ISER 用来使能中断,ICER 用来失能中断,IP 用来设置中断优先级。

ISER[8]:ISER 全称是:Interrupt Set Enable Registers,这是一个中断使能寄存器组。上面 说了 CM4 内核支持 256 个中断,这里用 8 个 32 位寄存器来控制,总共256位,每个位控制一个中断。但是 STM32F407 的可屏蔽中断最多只有 82 个,所以对我们来说,有用的就是三个(ISER[0~2]),总共可以表示 128 个中断。而 STM32F407 只用了其中的 82 个。ISER[0]的 bit0~31 分别对应中断 0~31;ISER[1]的 bit0~31 对应中断 32~63; ISER[2]的 bit0~16 对应中断 64~81,这样总共 82 个 中断就可以分别对应上了。你要使能某个中断,必须设置相应的 ISER 位为 1,使该中断被使能 (这里仅仅是使能,还要配合中断分组、屏蔽、IO 口映射等设置才算是一个完整的中断设置)。 具体每一位对应哪个中断,请参考 stm32f407xx.h 里面的第 68 行。 

ICER[8]:全称是:Interrupt Clear Enable Registers,是一个中断除能寄存器组。该寄存器组 与 ISER 的作用恰好相反,是用来清除某个中断的使能的,向寄存器写1清除中断,写0无效其对应位的功能,也和 ISER 一样。 这里要专门设置一个 ICER 来清除中断位,而不是向 ISER 写 0 来清除,是因为 NVIC 的这些寄存器都是写 1 有效的,写 0 是无效的。

IP [240]:全称是:Interrupt Priority Registers,是一个中断优先级控制的寄存器组。这个寄 存器组相当重要!STM32F407 的中断分组与这个寄存器组密切相关。IP 寄存器组由 240 个 8bit 的寄存器组成,每个可屏蔽中断占用 8bit,这样总共可以表示 240 个可屏蔽中断。而 STM32F407 只用到了其中的 82 个。IP[81]~IP[0]分别对应中断 81~0,也就是一个中断需要一个IP寄存器来配置优先级。而每个可屏蔽中断占用的 8bit 并没有全部使用,而是只用了高 4 位。这 4 位,又分为抢占优先级和响应优先级。抢占优先级在前,响应优先级在后。而这两个优先级各占几个位又要根据 SCB->AIRCR 中的中断分组设置来决定。关于中断优先级控制的寄存器组下面详细讲解。

 3.3 NVIC 中断配置固件库

       固件库文件 core_cm4.h 的最后,还提供了 NVIC 的一些函数,这些函数遵循 CMSI 规则,只要是 Cortex-M4 的处理器都可以使用,具体如下:

3.4 NVIC工作原理

        对于外部的每个中断,要想使用必须先使能(通过寄存器ISER/ICER控制) ,然后设置该中断的优先级(抢占优先级和响应优先级通过IPR寄存器控制)

3.5 STM32中断优先级基本概念

       在 NVIC 有一个专门的寄存器:中断优先级寄存器 NVIC_IPRx(在 F407 中,x=0...981) 用来配置外部中断的优先级,IPR 宽度为 8bit,原则上每个外部中断可配置的优先级为 0~255,数值越小,优先级越高。但是绝大多数 CM4 芯片都会精简设计,以致实际上支持的优先级数减少,在 F407 中,只使用了高 4bit,前面说了 STM32F407 芯片支持 82 个可屏蔽中断通道,每个中断通道都具备自己的中断优先级控制字节, STM32F407 用于表达优先级的高 4 位又被分组成抢占优先级和响应优先级,每个中断源都需要被指定这两种优先级。

设置优先级的意义:

3.6 STM32中断优先级分组

         对于 NVCI 的中断优先级分组:STM32F407 将中断分为 5 个组,组 0~4。该分组的设置是 由 SCB->AIRCR 寄存器的 bit10~8 (一共3个位)来定义的。具体的分配关系如表所示:

特别提示:一个工程中,一般只设置一次中断优先级分组。

       通过这个表,我们就可以清楚的看到组 0~4 对应的配置关系,例如优先级分组设置为 3, 那么此时所有的 82 个中断,每个中断的中断优先寄存器的高四位中的最高 3 位是抢占优先级, 低 1 位是响应优先级。每个中断,你可以设置抢占优先级为 0~7,响应优先级为 1 或 0。抢占优先级的级别高于响应优先级。而数值越小所代表的优先级就越高。

3.7  如何使用库函数实现中断分组设置以及中断优先级管理

       上面我们讲了中断优先级分组是通过IP寄存器进行分组的,那么,我们是基于库函数开发的,又该如何使用库函数进行中断分组设置以及中断优先级管理?NVIC 中断管理函数主要在 misc.c 文件里面。misc.h:NVIC_InitTypeDef结构体,以及库函数的参数和声明,misc.c:NVIC(嵌套向量中断控制器)、SysTick(系统滴答定时器)相关函数

3.7.1设置中断优先级分组函数

        首先要讲解的是中断优先级分组函数 NVIC_PriorityGroupConfig,其函数申明如下:这个函数的作用是对中断的优先级进行分组,这个函数在系统中只能被调用一次,一旦分组确定就最好不要更改。这个函数我们可以找到其实现:

//函数原型如下
void NVIC_PriorityGroupConfig(uint32_t NVIC_PriorityGroup);

//函数具体实现如下:
void NVIC_PriorityGroupConfig(uint32_t NVIC_PriorityGroup)
{
     assert_param(IS_NVIC_PRIORITY_GROUP(NVIC_PriorityGroup));
     SCB->AIRCR = AIRCR_VECTKEY_MASK | NVIC_PriorityGroup;
}

        从函数体可以看出,这个函数唯一目的就是通过设置 SCB->AIRCR 寄存器来设置中断优先级分组,这在前面寄存器讲解的过程中已经讲到。而其入口参数通过双击选中函数体里面的 “IS_NVIC_PRIORITY_GROUP”然后右键“Go to defition of …”可以查看到为:

#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))

这也是我们上面表 讲解的,分组范围为 0-4。比如我们设置整个系统的中断优先级分组值 为 2,那么方法如下,这样就确定了一共为“2 位抢占优先级,2 位响应优先级”。

NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);

3.7.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_InitTypeDef 结构体中间有四个成员变量,接下来我们一一来看看这些成员变量的含 义。

  1. NVIC_IRQChannel:定义初始化的是哪个中断,这个我们可以在 stm32f10x.h 中找到每个中断对应的名字。例如 USART1_IRQn。
  2. NVIC_IRQChannelPreemptionPriority:定义这个中断的抢占优先级。
  3. NVIC_IRQChannelSubPriority:定义这个中断的响应优先级。
  4. NVIC_IRQChannelCmd:该中断通道是否使能,要使用该中断必须使能。

比如我们要使能串口 1 的中断,同时设置抢占优先级为 1,响应优先级位 2,初始化的方法 是:

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 寄存器

 这里我们讲解了中断分组的概念以及设置单个中断优先级的方法。最后我们总结一下中断优先级设置的步骤:

1. 系统运行开始的时候设置中断分组。确定组号,也就是确定抢占优先级和响应优先级的分配位数。调用函数为 NVIC_PriorityGroupConfig();

2. 设置所用到的中断的中断优先级别。对每个中断调用函数为 NVIC_Init();

四、 STM32 中断编程(非常重要)

       在配置每个中断的时候一般有 3 个编程要点:

第一步:中断使能,一般中断使能一般有两个门,外设使能相应的中断然后送入NVIC再使能,外设使能中断是小门,NVIC使能中断是大门,只有都使能才能响应中断。对于外设使能中断,这个具体由每个外设的相关中断使能位控制。比如串口有发送完成中断,接收完成中断,这两个中断都由串口控制寄存器的相关中断使能位控制。而NVIC使能中断,这个由NVIC里面的结构体成员进行使能。

第二步:设置中断分组,调用库函数:NVIC_PriorityGroupConfig();

第三步:初始化NVIC,  主要是初始化 NVIC_InitTypeDef 结构体,设置中断源,设置抢占优先级和响应优先级,使能中断请求,然后调用NVIC_Init()函数进行初始化,由函数将参数写入寄存器。

有关 NVIC 初始化结构体的成员简单解释:

1)NVIC_IROChannel:用来设置中断源,不同的中断中断源不一样,且不可写错,即使写错了程序不会报错,只会导致不想要中断。具体的成员配置可参考 stm32f4xx.h 头文件里面的 IRQn_Type 结构体定义,这个结构体包含了所有的中断源。

2)NVIC_IRQChannelPreemptionPriority:抢占优先级,具体的值要根据优先级分组来确定,具体参考表格 优先级分组真值表 。

3)NVIC_IRQChannelSubPriority:子优先级,具体的值要根据优先级分组来确定,具体参考表格优先级分组真值表 。

4)NVIC_IRQChannelCmd:中断使能(ENABLE)或者失能(DISABLE)。操作的是 NVIC_ISER 和 NVIC_ICER 这两个寄存器。

第四步:编写中断服务函数:配置好中断后如果有触发,即会进入中断服务函数,那么中断服务函数也有固定的函数名,可以在 startup_stm32f40_41xxx.s 启动文件查看,只是这些中断函数都是为空,为的只是初始化中断向量表。实际的中断服务函数都需要我们重新编写。关于中断服务函数的函数名必须跟启动文件里面预先设置的一样,如果写错,系统就 在中断向量表中找不到中断服务函数的入口,直接跳转到启动文件里面预先写好的空函数, 并且在里面无限循环,实现不了中断。启动文件内中断服务函数名如下:

       如果上述的一切配置完毕,当中断来临时,CPU会根据相应的中断去中断向量表找到对应的中断函数的地址,然后调用执行中断服务函数。

五、外部中断/事件控制器—EXTI

       通过以上讲解相信已经对中断有了了解,并且掌握了如何配置一个外设的中断,接下来我们将介绍如何将 STM32F4 的 IO 口作为外部中断输入。

5.1 STM32F4 GPIO外部中断简介

      STM32F4 的每个 IO 都可以作为外部中断的中断输入口,这点也是 STM32F4 的强大之处。STM32F407 的中断控制器支持 23 个外部中断/事件请求。每个中断设有状态位,每个中断/事件都有独立的触发和屏蔽设置。GPIO作为外部中断输入口的整体框图如下 ,先整体把握,后面针对SYSCFG和EXTI会有详细讲解,NVIC上面已经详细介绍过。

        下面针对上面的每个部分进行详细介绍: 

5.2 外部中断/事件线映射

 STM32F4 的 EXTI 具有 23 个中断/事件线,对应连接的外设说明如下表所示:

         EXTI0 至 EXTI15 用于 GPIO,通过编程控制可以实现任意一个 GPIO 作为 EXTI 的输入源。从上表可知,STM32F4 的 EXTI 供外部 IO 口使用的中断线有 16 根,但是我们使用的 STM32F407 芯片却远远不止 16 个 IO 口,那么 STM32F4 芯片怎么解决这个问题的呢?因为 STM32F4 芯片每个 GPIO 端口均有 16 个管脚,因此把每个端口的 16 个 IO 对应那 16 根中断线 EXTI0-EXTI15。比如: GPIOx.0-GPIOx.15(x=A,B,C,D,E, F,G,H,I)分别对应中断线 EXTI0-EXTI15,这样一来每个中断线就对应了最多 9 个 IO 口,比如:GPIOA.0、GPIOB.0、GPIOC.0、 GPIOD.0、GPIOE.0、GPIOF.0、GPIOG.0、GPIOH.0、GPIOI.0。

        但是中断线每次只能连接一个在 IO 口上,这样就需要通过软件配置 SYSCFG 外部中断配置寄存器中的 EXTIx[3:0]位来决定对应的中断线映射到哪个 GPIO 端口上,对 SYSCFG 外部中断配置寄存器的配置函数在 stm32f4xx_syscfg.c 和 stm32f4xx_syscfg.h 中,所以使用到外部中断时需要添加到工程中,EXTI 的 GPIO 映射图如下图所示:

        16个中断线的不是每个中断都有独立的中断服务函数,IO口外部中断在中断向量表中只分配了7个中断向量,也就是只能使用7个中断服务函数,如下表所示:

从表中可以看出,外部中断线5~9分配一个中断向量,共用一个服务函数。外部中断线10~15分配一个中断向量,共用一个中断服务函数。对应的中断服务函数,直接去启动文件里面找以防写错。如下图所示:

5.3  EXTI简介

5.3.1 概念

      EXTI 即是外部中断和事件控制器, 外部中断/事件控制器(EXTI)管理了控制器的 23 个中断/事件线。每个中断/事件线都对应有一个边沿检测器,可以实现输入信号的上升沿检测和下降沿的检测。EXTI 可以实现对每个中断/事件线进行单独配置,可以单独配置为中断或者事件,以及触发事件的属性。

【注意】:EXTI也是STM32F4ZGT6的⼀个片上外设EXTI外设的时钟默认是开启的

5.3.2 组成

     它是由23个边沿检测器组成的。每条EXTI线都可以单独配置:选择类型(中断或者事件)、触发方式(上升沿,下降沿或者双边沿触发)、支持软件触发、开启/屏蔽、有挂起状态位。

5.3.3 工作过程

      EXTI可以监测指定GPIO口的电平信号,当其指定的GPIO⼝(需要配置通道)产⽣电平变化时,EXTI将⽴即向NVIC发出中断申请,经过NVIC裁决后即可中断CPU主程序,使CPU执行EXTI对应的中断程序。

5.3.4 支持的触发方式

     ⽀持上升沿、下降沿、双边沿、软件触发。

5.3.5 响应的方式

     事件响应 中断响应

5.4 EXTI 框图

         EXTI 的功能框图是最直接把有关 EXTI 的知识点连接起来的图,掌握了该图的来龙去脉, 就会对 EXTI 有了一个整体熟悉,编程时候可以得心应手。EXTI 的功能框图如图所示。可以看到很多在信号线上打一个斜杠并标注“23”字样,这个表示在控制器内部类似的信号线路有 23 个,这与 EXTI 总共有 23 个中断/事件线是吻合的。所以我们只要明白其中一个的原理,那其他 22 个线路原理也就知道了。

下面详细分析这个过程:首先整体上来看,EXTI 可分为两大部分功能,一个是产生中断,另一个是产生事件,这两个功能从硬件上就有所不同。

中断与事件的区别:

中断:需要CPU参与,需要调用软件的中断服务函数才能完成中断后产生的结果
事件:靠脉冲发生器产生一个脉冲,进而由硬件自动完成这个事件产生的结果,当然相应的联动部件需要先设置好,触发TIM计时,AD转换等,事件不要软件的参与,降低了CPU的负荷,而且硬件速度快于软件速度

首先我们来看图中蓝色指示的电路流程。它是一个产生事件的线路,最终输出一个脉冲信号。输入线一般是存在电平变化的信号。边沿检测电路以输入线作为信号输入端,如果检测到有边沿跳变就输出有效信号 1 , 否则输出无效信号 0。

        输入一个有效信号 1 时就会产生一个脉冲,如果输入端是无效信号就不会输出脉冲。这个脉冲信号可以给其他外设电路使用,比如定时器 TIM、模拟数字转换器 ADC 等等,这样的脉冲信号一般用来触发 TIM 或者 ADC 开始转换

接下来逐一介绍用到的寄存器,进一步理解框图原理:

1. 上升&下降沿触发选择寄存器

       通过上升沿触发选择寄存器和下降沿触发选择寄存器对应位的设置来控制信号触发。而上升沿和下降沿触发选择这两个寄存器可以控制需要检测哪些类型的电平跳变过程,可以是只有上升沿触发、只有下降沿触发或者上升沿和下降沿都触发。

2.软件中断事件寄存器

3. 请求挂起寄存器

4. 中断/事件屏蔽寄存器

         

      产生中断线路目的是把输入信号输入到 NVIC,进一步会运行中断服务函数,实现功能,这样是软件级的。而产生事件线路目的就是传输一个脉冲信号给其他外设使用,并且是电路级别的信号传输,属于硬件级的。

5.5 SYSCFG的介绍

         System configuration controller,即系统配置控制器,他也是一个外设,主要⽤于管理对可执⾏代码的存储区域的地址重映射、选择以太⽹ PHY 接 ⼝ 以及用于外部中断映射配置.这个外设中有一个寄存器是专门用来决定对应的中断线映射到哪个 GPIO 端口上,叫做外部中断配置寄存器,其中的 EXTIx[3:0]位来决定对应的中断线映射到哪个 GPIO 端口上,SYSCFG_EXTICR1~4,配置EXTI中断线0~15对应具体哪个IO口 .

SYSCFG它也是一个外设,配置SYSCFG寄存器之前要使能SYSCFG时钟,它是挂在APB2总线上的;

5.5.1 外部中断配置寄存器

5.6 EXTI 初始化结构体详解

         标准库函数对每个外设都建立了一个初始化结构体,比如 EXTI_InitTypeDef,结构体成员用于设置外设工作参数,并由外设初始化配置函数,比如 EXTI_Init()调用,这些设定参数将会设置外设相应的寄存器,达到配置外设工作环境的目的。 初始化结构体和初始化库函数配合使用是标准库精髓所在,理解了初始化结构体每个成员意义基本上就可以对该外设运用自如了。初始化结构体定义在 stm32f4xx_exti.h 文件中, 初始化库函数定义在 stm32f4xx_exti.c 文件中,编程时我们可以结合这两个文件内注释使用。

  1. EXTI_Line:EXTI 中断/事件线选择/中断通道/中断源,可选 EXTI0 至 EXTI22。
  2. EXTI_Mode:EXTI 模式选择,可选为产生中断(EXTI_Mode_Interrupt)或者产生事件(EXTI_Mode_Event)。
  3. EXTI_Trigger:EXTI 边沿触发事件,可选上升沿触发(EXTI_Trigger_Rising)、下 降沿触发 ( EXTI_Trigger_Falling) 或者上升沿和下降沿都触发 ( EXTI_Trigger_Rising_Falling)。
  4. EXTI_LineCmd:控制是否使能 EXTI 线,可选使能 EXTI 线(ENABLE)或禁用 (DISABLE)。

       最后调用EXTI_Init函数,将结构体配置好的参数写入对应的寄存器,这个比较简单我就不讲了,这里提一下这个结构体中断/事件线的选择并不是配置中断线/事件线,配置线的函数上面已经提及,这里选择线是为了知道中断线在寄存器哪个位置(022)对应中断线(EXTI022),好配置相应的寄存器。

 5.7 如何使用中断

六、 使用库函数配置STM32外部中断输入的步骤(非常重要)

6.1 第一步:使能GPIO时钟和使能SYSCFG时钟

      首先,我们要使用 IO 口作为中断输入,所以我们要使能相应的 IO 口时钟, EXTI和 NVIC的时钟默认是打开的,因此,我们不需要使能,但是需要使能SYSCFG的时钟。

RCC_APB2PeriphClockCmd(RCC_APB2Periph_SYSCFG, ENABLE);//使能 SYSCFG 时钟  这里大家一定要注意,只要我们使用到外部中断,就必须打开 SYSCFG 时钟。

6.2  第二步:初始化 IO 口为输入模式

         使用 IO 口作为中断输入,初始化相应的 IO 口为输入模式,具体上拉还是下拉根据具体项目进行分析确定;

6.3 第三步:配置SYSCFG,设置 IO 口与中断线的映射关系。

          配置 GPIO 与中断线的映射关系。在库函数中,配置 GPIO 与中断线的映射 关系的函数 SYSCFG_EXTILineConfig ()来实现的:

void SYSCFG_EXTILineConfig(uint8_t EXTI_PortSourceGPIOx, uint8_t EXTI_PinSourcex);

该函数将 GPIO 端口与中断线映射起来,使用范例是:

SYSCFG_EXTILineConfig(EXTI_PortSourceGPIOA, EXTI_PinSource0);

将中断线 0 与 GPIOA 映射起来,那么很显然是 GPIOA.0 与 EXTI0 中断线连接了。设置好中断 线映射之后,那么到底来自这个 IO 口的中断是通过什么方式触发的呢?接下来我们就要设置该中断线上中断的初始化参数了。

6.4 第四步:初始化线上中断EXTI

中断线上中断的初始化是通过函数 EXTI_Init()实现的。EXTI_Init()函数的定义是:

void EXTI_Init(EXTI_InitTypeDef* EXTI_InitStruct);

下面我们用一个使用范例来说明这个函数的使用:

 EXTI_InitTypeDef EXTI_InitStructure;
 EXTI_InitStructure.EXTI_Line=EXTI_Line4;
 EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt;
 EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Falling;
 EXTI_InitStructure.EXTI_LineCmd = ENABLE;
 EXTI_Init(&EXTI_InitStructure); //初始化外设 EXTI 寄存器

上面的例子设置中断线 4 上的中断为下降沿触发。STM32 的外设的初始化都是通过结构体来设 置初始值的,这里就不再讲解结构体初始化的过程了。我们来看看结构体 EXTI_InitTypeDef 的 成员变量:

typedef struct
{    
    uint32_t EXTI_Line; 
    EXTIMode_TypeDef EXTI_Mode; 
    EXTITrigger_TypeDef EXTI_Trigger; 
    FunctionalState EXTI_LineCmd; 
}EXTI_InitTypeDef;

从定义可以看出,有 4 个参数需要设置。

  1. 第一个参数是中断线的标号,对于我们的外部中断, 取值范围为 EXTI_Line0~EXTI_Line15。这个在上面已经讲过中断线的概念。也就是说,这个 函数配置的是某个中断线上的中断参数。
  2. 第二个参数是中断模式,可选值为中断 EXTI_Mode_Interrupt 和事件 EXTI_Mode_Event。
  3. 第三个参数是触发方式,可以是下降沿触发 EXTI_Trigger_Falling,上升沿触发 EXTI_Trigger_Rising,或者任意电平(上升沿和下降沿)触 发 EXTI_Trigger_Rising_Falling。
  4. 最后一个参数就是使能中断线了。

6.5 第五步:初始化中断分组(NVIC),并使能中断。

       我们设置好中断线和 GPIO 映射关系,然后又设置好了中断的触发模式等初始化参数。既然是外部中断,涉及到中断我们当然还要设置 NVIC 中断分组和优先级。这个在前面已经讲解过,这 里我们就接着上面的范例, 设置中断线 2 的中断优先级。

NVIC_InitTypeDef NVIC_InitStructure;
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); //中断优先级分组初始化

6.6 第六步:编写中断服务函数

        我们配置完中断优先级之后,接着要做的就是编写中断服务函数。中断服务函数的名字是在 MDK 中事先有定义的。这里需要说明一下,STM32F4 的 IO 口外部中断函数只有 7 个,分别为:

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。

        在编写中断服务函数的时候会经常使用到两个函数,第一个函数是判断某个中断线上的中断是否发生(标志位是否置位),这个函数一般使用在中断服务函数的开头判断中断是否发生。

ITStatus EXTI_GetITStatus(uint32_t EXTI_Line);

          另一个函数是清除某个中断线上的中断标志位:这个函数一般应用在中断服务函数结束之前,清除中断标志位。

void EXTI_ClearITPendingBit(uint32_t EXTI_Line);

  常用的中断服务函数格式为:

void EXTI3_IRQHandler(void)
{
if(EXTI_GetITStatus(EXTI_Line3)==SET)判断某个线上的中断是否发生 
   { 
        …中断逻辑…
        EXTI_ClearITPendingBit(EXTI_Line3); 清除 LINE 上的中断标志位 
   }
}

   

       通过以上几个步骤的设置,我们就可以正常使用GPIO外部中断了。

 七、相关补充

         在这里需要说明一下,固件库还提供了两个函数用来判断外部中断状态以及清除外部状态标志位的函数 EXTI_GetFlagStatusEXTI_ClearFlag,他们的作用和前面两个函数的作用类似。 只是在 EXTI_GetITStatus 函数中会先判断这种中断是否使能,使能了才去判断中断标志位,而 EXTI_GetFlagStatus 直接用来判断状态标志位。后面会用到。

        至此,我们的本次的学习就结束了。学习了 STM32F407 的 IO 作为外部输入中断的使用方法, 下一次,我们将应用我们的中断完成一个实验,加深我们对于外部中断的理解。这一节我们就讲解到这里,希望能对大家的开发有帮助。 如有兴趣,感谢点赞、关注、收藏,若有不正地方,还请各位大佬多多指教!

  • 7
    点赞
  • 28
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

未来可期,静待花开~

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值