(七)STM32 NVIC 中断、优先级管理及 AFIO 时钟的开启

目录

1. 中断相关知识简介

1.1 什么是中断

1.2 什么是内中断、外中断

1.3 什么是可屏蔽中断、不可屏蔽中断

2. CM3 内核中断介绍

2.1 F103系统异常清单

2.2 F103 外部中断清单

3. NVIC 简介 

3.1 NVIC 寄存器简介

3.2 NVIC 相关寄存器的介绍

4. 中断优先级

4.1 优先级定义

​编辑 4.2 优先级分组

4.3 中断编程

5. EXTI —— 外部中断/事件控制器

5.1 STM32 外部中断 EXTI 简介

 5.2 EXTI 功能框图

5.3 中断/事件线

 5.4 EXTI 初始化结构体详解

5.5 外部中断控制实验

6. STM32 什么情况下开始外设复用 AFIO 时钟


1. 中断相关知识简介

1.1 什么是中断

        中断是指处理器运行过程中,出现某些意外情况,CPU 能自动停止正在运行的程序并转入处理新情况的程序(中断服务函数),处理完毕后又返回原被暂停的程序继续运行。

1.2 什么是内中断、外中断

        CPU 内部引发的中断称作内中断,外部引发的中断称为外中断(一般就是STM32片上外设)。而外中断源分为以下两类:可屏蔽中断、不可屏蔽中断。

1.3 什么是可屏蔽中断、不可屏蔽中断

        可屏蔽中断就是 CPU 可以不响应这个中断。CPU 是否要响应这个中断要看标志寄存器中的IF 标志位的值。如果IF标志位等于0,那么 CPU 则不响应这个中断,如果IF标志位为 1 ,CPU 则响应这个中断,所以每次的中断过程中都一个把 IF 设置为 0 的动作,就是让 CPU 在进入中断处理后禁止其他的可屏蔽中断。可屏蔽中断引发的中断过程和内中断的差不多,除第一步获取中断类型码的途径有所不同,内中断的中断类型码是在CPU内部产生的,而外中断的中断类型码是在CPU外部通过数据总线传给CPU的。

我们也有可以设置IF位的指令:  sti  :设置IF为 0 ,cli : 设置IF为 1 。

        不可屏蔽中断就是CPU必须响应的外中断,它的中断类型码固定为 2 ,所以由它引发的中断过程中没有取得中断类型码的那一步。不过后面三步还是一样的。大多数由外设引发的中断都是可屏蔽中断

2. CM3 内核中断介绍

        CM3 内核支持 256 个中断,其中包含了 16 个内核中断和 240 个外部中断,并且具有 256级的可编程中断设置。但 STM32 并没有使用 CM3 内核的全部东西,而是只用了它的一部分
        STM32 有 84 个中断,包括 16 个内核中断和 68 个可屏蔽中断,具有 16 级可编程的中断优先级(为什么是16级,在 4.1 中有介绍)。
        而我们常用的就是这 68 个可屏蔽中断,但是 STM32 的 68 个可屏蔽中断,在 STM32F103 系列上面,又只有 60 个(在 107 系列才有 68 个)。因为我们开发板选择的芯片是 STM32F103 系列的所以我们就只针对 STM32F103 系列这 60 个可屏蔽中断进行介绍。

        异常就是中断,中断就是异常,请不要刻意钻牛角尖较劲。在下文中体现。

        F103 在内核水平上搭载了一个异常响应系统,支持为数众多的系统异常和外部中断。其中系统异常有 8 个(如果把 Reset 和 HardFault 也算上的话就是 10 个),外部中断有 60 个。除了个别异常的优先级被定死外,其它异常的优先级都是可编程的。有关具体的系统异常和外部中断可在标准库文件 stm32f10x.h 这个头文件查询到,在 IRQn_Type 这个结构体里面包含了 F103 系列全部的异常声明。

2.1 F103系统异常清单

51d03d3299754f74901d6529a2bb287d.png

在程序中体现,在 stm32f10x.h 文件中可找到这些异常的编号:

4d797003cdf1475988db171b28879e28.png

2.2 F103 外部中断清单

5c5c5baa0ba944589a302ec81e8d935d.png

        在程序中体现,在 stm32f10x.h 文件中可找到这些外部中断的编号,根据宏定义不同的芯片类型,进行不同的中断编号:

7ea88eb3e6544120a440c64151f25d5b.png

3. NVIC 简介 

        NVIC 是嵌套向量中断控制器,控制着整个芯片中断相关的功能,它跟内核紧密耦合,是内核里面的一个外设。但是各个芯片厂商在设计芯片的时候会对 Cortex-M3 内核里面的 NVIC 进行裁剪,把不需要的部分去掉,所以说 STM32的 NVIC 是 Cortex-M3 的 NVIC 的一个子集。

3.1 NVIC 寄存器简介

        在固件库中,NVIC 的结构体定义可谓是颇有远虑,给每个寄存器都预留了很多位,恐怕为的是日后扩展功能。不过 STM32F103 可用不了这么多,只是用了部分而已,具体使用了多少可参考
《Cortex-M3 内核编程手册》-4.3.11:NVIC 寄存器映射。

088455d8ae5547cb9c52dd8576b43800.png

该结构体可在 core_cm3.h 中找见:

9b10799cd354466a9fe91aed425f76c6.png

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

结构体是声明了,但是我们如何找到 NVIC 控制器的起始地址呢,也就是说这个结构体怎么和地址联系起来?

f93c5eb39e1748c385895939353be16d.png

在对中断初始化时,点击 NVIC_Init(),点击进去,看到 NVIC 的指针访问:

66abe8da5fa34b85ac154c9af8229abb.png点击 NVIC ,在 core_cm3.h 文件中 找到它的宏定义,发现 NVIC_Type 结构体的起始地址:NVIC_BASE

990f41d4a2a0422cb40a3fe5b19ac928.png

点击  NVIC_BASE,看到 NVIC_BASE 的地址是在 SCS_BASE 地址进行偏移得到的

0e1d59a87a6242d6b1969bd6f4f7b44f.png

我们去查看 Cortex-M3 权威指南,NVIC 与中断控制,可以看到 :

fa9ff95c3f9543e5bb40694ba2969aa8.png

         NVIC 嵌套向量中断控制器的起始地址是:0xE000 E100,0xE000 E000 + 0x0100得到。

        因为 NVIC 嵌套向量中断控制器 ,它是一个内核里的外设,所以它的起始地址是和片上外设寄存器组的起始地址是不同的。

3.2 NVIC 相关寄存器的介绍

typedef struct 
{__IO uint32_t ISER[8]; // 中断使能寄存器uint32_t RESERVED0[24];__IO uint32_t ICER[8]; // 中断清除寄存器uint32_t RSERVED1[24];__IO uint32_t ISPR[8]; // 中断使能悬起寄存器uint32_t RESERVED2[24];__IO uint32_t ICPR[8]; // 中断清除悬起寄存器uint32_t RESERVED3[24];__IO uint32_t IABR[8]; // 中断有效位寄存器uint32_t RESERVED4[56];__IO uint8_t IP[240]; // 中断优先级寄存器 (8Bit wide)uint32_t RESERVED5[644];__O uint32_t STIR; // 软件触发中断寄存器
} NVIC_Type;

        STM32 的中断在这些寄存器的控制下有序的执行的。只有了解这些中断寄存器,才能方便
的使用 STM32 的中断。下面重点介绍这几个寄存器:

        ISER[8]:ISER 全称是:Interrupt Set-Enable Registers,这是一个中断使能寄存器组。上面
说了 CM3 内核支持 256 个中断,这里用 8 个 32 位寄存器来控制,每个位控制一个中断。但是STM32F103 的可屏蔽中断只有 60 个,所以对我们来说,有用的就是两个(ISER[0]和ISER[1]),总共可以表示 64 个中断。而 STM32F103 只用了其中的前 60 位。ISER[0]的bit0~bit31 分别对应中断 0~31。ISER[1]的 bit0~27 对应中断 32~59;这样总共 60 个中断就分别对应上了。你要使能某个中断,必须设置相应的 ISER 位为 1,使该中断被使能(这里仅仅是使能,还要配合中断分组、屏蔽、IO 口映射等设置才算是一个完整的中断设置)。具体每一位对应哪个中断,请参考 stm32f10x.h 里面的第 140 行处(针对编译器 MDK5 说)。
        ICER[8]:全称是:Interrupt Clear-Enable Registers,是一个中断除能寄存器组。该寄存器组与 ISER 的作用恰好相反,是用来清除某个中断的使能的。其对应位的功能,也和 ICER 一样。这里要专门设置一个 ICER 来清除中断位,而不是向 ISER 写 0 来清除,是因为 NVIC 的这些寄存器都是写 1 有效的,写 0 是无效的。具体为什么这么设计,请看《CM3 权威指南》第 125 页,NVIC 概览一章。
        ISPR[8]:全称是:Interrupt Set-Pending Registers,是一个中断挂起控制寄存器组。每个位对应的中断和 ISER 是一样的。通过置 1,可以将正在进行的中断挂起,而执行同级或更高级别的中断。写 0 是无效的。
        ICPR[8]:全称是:Interrupt Clear-Pending Registers,是一个中断解挂控制寄存器组。其作用与 ISPR 相反,对应位也和 ISER 是一样的。通过设置 1,可以将挂起的中断接挂。写 0 无效。
        IABR[8]:全称是:Interrupt Active Bit Registers,是一个中断激活标志位寄存器组。对应位所代表的中断和 ISER 一样,如果为 1,则表示该位所对应的中断正在被执行。这是一个只读寄存器,通过它可以知道当前在执行的中断是哪一个。在中断执行完了由硬件自动清零。
        IP[240]:全称是:Interrupt Priority Registers,是一个中断优先级控制的寄存器组。这个寄存器组相当重要!STM32 的中断分组与这个寄存器组密切相关。IP 寄存器组由 240 个 8bit 的寄存器组成,每个可屏蔽中断占用 8bit,这样总共可以表示 240 个可屏蔽中断。而 STM32 只用到了其中的前 60 个。IP[59]~IP[0]分别对应中断 59~0。而每个可屏蔽中断占用的 8bit 并没有全部使用,而是只用了高 4 位。这 4 位,又分为抢占优先级和子优先级。抢占优先级在前,子优先级在后。而这两个优先级各占几个位又要根据 SCB->AIRCR 中的中断分组设置来决定。

4. 中断优先级

4.1 优先级定义

        在 NVIC 有一个专门的寄存器:中断优先级寄存器 NVIC_IPx,用来配置外部中断的优先级,IP宽度为 8bit,原则上每个外部中断可配置的优先级为 0~255,数值越小,优先级越高。但是绝大多数 CM3 芯片都会精简设计,以致实际上支持的优先级数减少,在 F103 中,只使用了高 4bit,如下所示,每个 IP宽度虽然为 8bit,但 F103 中,只使用了高 4 bit:

3fbae04f9b0d467db69e848f15b6dd6c.png

        用于表达优先级的这 4bit,又被分组成抢占优先级子优先级。如果有多个中断同时响应,抢占优先级高的就会抢占抢占优先级低的优先得到执行,如果抢占优先级相同,就比较子优先级。如果抢占优先级和子优先级都相同的话,就比较他们的硬件中断编号,编号越小,优先级越高

注意:这里的先是比较你的 抢占和子优先级,数值越小,优先级越高;当你的外设,抢占和子优先级都相同的话,这时候比较的是 硬件中断编号,编号越小,优先级越高。这里的硬件中断编号指的是在 stmf103x.h 文件中定义好的:

7ea88eb3e6544120a440c64151f25d5b.png
 4.2 优先级分组

        优先级的分组由内核外设 SCB 的应用程序中断及复位控制这里简单介绍一下 STM32 的中断分组:STM32 将中断分为 5 个组,组 0~4。该分组的设置是由 SCB->AIRCR 寄存器的 bit10~8 来定义的。具体的分配关系如表 4.5.1 所示:

13d02bc49af94b6bbdbd0b6bc8f2501c.png        设置优先级分组可调用库函数 NVIC_PriorityGroupConfig() 实现,有关 NVIC 中断相关的库函数都在库文件 misc.c 和 misc.h 中。

中断优先级分组库函数:NVIC_PriorityGroupConfig()

/**
* 配置中断优先级分组:抢占优先级和子优先级
* 形参如下:
* @arg NVIC_PriorityGroup_0: 0 bit for 抢占优先级
*                             4 bits for 子优先级
* @arg NVIC_PriorityGroup_1: 1 bit for 抢占优先级
*                             3 bits for 子优先级
* @arg NVIC_PriorityGroup_2: 2 bit for 抢占优先级
*                             2 bits for 子优先级
* @arg NVIC_PriorityGroup_3: 3 bit for 抢占优先级
*                             1 bits for 子优先级
* @arg NVIC_PriorityGroup_4: 4 bit for 抢占优先级
*                             0 bits for 子优先级
* @ 注意 如果优先级分组为 0,则抢占优先级就不存在,优先级就全部由子优先级控制
*/
void NVIC_PriorityGroupConfig(uint32_t NVIC_PriorityGroup)
{// 设置优先级分组SCB->AIRCR = AIRCR_VECTKEY_MASK | NVIC_PriorityGroup;
}

9c7b9f403d51497bb9d7ef9bf5f5341e.png

        也就是说,当选择  NVIC_PriorityGroup_0 分组时,无主优先级,优先级全部由子优先级控制,有 0-15 个等级划分;

        当选择  NVIC_PriorityGroup_1 分组时,主优先级有0-1两级划分,子优先级有 0-7 个等级划分;

        注意:NVIC_PriorityGroupConfig() 是整个程序中只需要设置一次,并且从代码布局逻辑来说,NVIC_PriorityGroupConfig() 适合放在 main() 函数中。程序在进入main函数中时,首先进行中断分组,这样只需要一次分组,后续在其他中断配置中,无需再进行分组。一般配置为:
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2)。

4.3 中断编程

在配置每个中断的时候一般有 3 个编程要点:
        1. 使能外设某个中断,这个具体由每个外设的相关中断使能位控制。比如串口有发送完成中断,接收完成中断,这两个中断都由串口控制寄存器的相关中断使能位控制。
        2. 初始化 NVIC_InitTypeDef 结构体,配置中断优先级分组,设置抢占优先级和子优先级,使能中断请求。NVIC_InitTypeDef 结构体在固件库头文件 misc.h 中定义。

typedef struct
{uint8_t NVIC_IRQChannel; // 中断源uint8_t NVIC_IRQChannelPreemptionPriority; // 抢占优先级uint8_t NVIC_IRQChannelSubPriority; // 子优先级FunctionalState NVIC_IRQChannelCmd; // 中断使能或者失能
} NVIC_InitTypeDef;

有关 NVIC 初始化结构体的成员我们一一解释下:
        a. NVIC_IROChannel:用来设置中断源,不同的中断中断源不一样,且不可写错,即使写错了程序也不会报错,只会导致不响应中断。具体的成员配置可参考 stm32f10x.h 头文件里面的 IRQn_Type 结构体定义,这个结构体包含了所有的中断源。
        b. NVIC_IRQChannelPreemptionPriority:抢占优先级,具体的值要根据优先级分组来确定,具体参考表格优先级分组真值表 优先级分组真值表。
        c. NVIC_IRQChannelSubPriority:子优先级,具体的值要根据优先级分组来确定,具体参考表格优先级分组真值表 优先级分组真值表。
        d. NVIC_IRQChannelCmd:中断使能(ENABLE)或者失能(DISABLE)。操作的是 NVIC_ISER 和 NVIC_ICER 这两个寄存器。

        3. 编写中断服务函数
        在启动文件 startup_stm32f10x_hd.s 中我们预先为每个中断都写了一个中断服务函数,只是这些中断函数都是为空,为的只是初始化中断向量表。实际的中断服务函数都需要我们重新编写,为了方便管理我们把中断服务函数统一写在 stm32f10x_it.c 这个库文件中。一般我们都直接写在初始化外设的函数后边
        关于中断服务函数的函数名必须跟启动文件里面预先设置的一样,如果写错,系统就在中断向量表中找不到中断服务函数的入口,直接跳转到启动文件里面预先写好的空函数,并且在里面无限循环,实现不了中断。

5. EXTI —— 外部中断/事件控制器

5.1 STM32 外部中断 EXTI 简介

        EXTI(External interrupt/event controller)— 外部中断/事件控制器,管理了控制器的 20 个中断/事件线。每个中断/事件线都对应有一个边沿检测器,可以实现输入信号的上升沿检测和下降沿的检测。EXTI 可以实现对每个中断/事件线进行单独配置,可以单独配置为中断或者事件,以及触发事件的属性。
        这里我们首先知道 STM32 IO 口中断的一些基础概念。STM32 的每个 IO 都可以作为外部中断的中断输入口,这点也是 STM32 的强大之处。STM32F103 的中断控制器支持 19 个外部中断/事件请求。每个中断设有状态位,每个中断/事件都有独立的触发和屏蔽设置。STM32F103 的19 个外部中断为:

  •         线 0~15:对应外部 IO 口的输入中      断。
  •         线 16:连接到 PVD 输出。
  •         线 17:连接到 RTC 闹钟事件。
  •         线 18:连接到 USB 唤醒事件。

        从上面可以看出,STM32 供 IO 口使用的中断线只有 16 个,但是 STM32 的 IO 口却远远不止 16 个,那么 STM32 是怎么把 16 个中断线和 IO 口一一对应起来的呢?于是 STM32 就这样设计,GPIO 的管教 GPIOx.0~GPIOx.15(x=A,B,C,D,E,F,G)分别对应中断线 0~15。这样每个中断线对应了最多 7 个 IO 口,以线 0 为例:它对应了 GPIOA.0、GPIOB.0、GPIOC.0、GPIOD.0、GPIOE.0、GPIOF.0、GPIOG.0。而中断线每次只能连接到 1 个 IO 口上,这样就需要通过配置来决定对应的中断线配置到哪个 GPIO 上了。下面我们看看 GPIO 跟中断线的映射关系图:

28615d5e3dfb4e48af65908a2f7c8a8b.png

 5.2 EXTI 功能框图

        EXTI 的功能框图包含了 EXTI 最核心内容,掌握了功能框图,对 EXTI 就有一个整体的把握,在编程时思路就非常清晰。EXTI 功能框图见图 EXTI 功能框图。
        在图 EXTI 功能框图 可以看到很多在信号线上打一个斜杠并标注 “20” 字样,这个表示在控制器内部类似的信号线路有 20 个,这与 EXTI 总共有 20 个中断/事件线是吻合的。所以我们只要明白其中一个的原理,那其他 19 个线路原理也就知道了。

55778ba2965049608f7ae3d9d4676b34.png

        EXTI 可分为两大部分功能,一个是产生中断,另一个是产生事件,这两个功能从硬件上就有所不同。
        首先我们来看图 EXTI 功能框图中红色虚线指示的电路流程。它是一个产生中断的线路,最终信号流入到 NVIC 控制器内。

产生中断:
        编号 1 是输入线,EXTI 控制器有 19 个中断/事件输入线(对应上边的线0-18),这些输入线可以通过寄存器设置为任意一个 GPIO,也可以是一些外设的事件,这部分内容我们将在后面专门讲解。输入线一般是存在电平变化的信号。
        编号 2 是一个边沿检测电路,它会根据上升沿触发选择寄存器 (EXTI_RTSR) 和下降沿触发选择寄存器 (EXTI_FTSR) 对应位的设置来控制信号触发。边沿检测电路以输入线作为信号输入端,如果检测到有边沿跳变就输出有效信号 1 给编号 3 电路,否则输出无效信号 0。而 EXTI_RTSR 和EXTI_FTSR 两个寄存器可以控制器需要检测哪些类型的电平跳变过程,可以是只有上升沿触发、只有下降沿触发或者上升沿和下降沿都触发。
        编号 3 电路实际就是一个或门电路,它一个输入来自编号 2 电路,另外一个输入来自软件中断事件寄存器 (EXTI_SWIER)。EXTI_SWIER 允许我们通过程序控制就可以启动中断/事件线(也就是说,不需要实际的线上产生信号跳变触发中断/事件,也可以通过程序控制触发中断/事件),这在某些地方非常有用。我们知道或门的作用就是有 1 就为 1,所以这两个输入随便一个有有效信号 1就可以输出 1 给编号 4 和编号 6 电路。
        编号 4 电路是一个与门电路,它一个输入是编号 3 电路,另外一个输入来自中断屏蔽寄存器(EXTI_IMR)。与门电路要求输入都为 1 才输出 1,导致的结果是如果 EXTI_IMR 设置为 0 时,那不管编号 3 电路的输出信号是 1 还是 0,最终编号 4 电路输出的信号都为 0;如果 EXTI_IMR设置为 1 时,最终编号 4 电路输出的信号才由编号 3 电路的输出信号决定,这样我们可以简单的控制 EXTI_IMR 来实现是否产生中断的目的。编号 4 电路输出的信号会被保存到挂起寄存器(EXTI_PR) 内,如果确定编号 4 电路输出为 1 就会把 EXTI_PR 对应位置 1。
        编号 5 是将 EXTI_PR 寄存器内容输出到 NVIC 内,从而实现系统中断事件控制。

产生事件:

        接下来我们来看看绿色虚线指示的电路流程。它是一个产生事件的线路,最终输出一个脉冲信号。产生事件线路是在编号 3 电路之后与中断线路有所不同,之前电路都是共用的。编号 6 电路是一个与门,它一个输入来自编号 3 电路,另外一个输入来自事件屏蔽寄存器 (EXTI_EMR)。如果EXTI_EMR 设置为 0 时,那不管编号 3 电路的输出信号是 1 还是 0,最终编号 6 电路输出的信号都为 0;如果EXTI_EMR 设置为 1 时,最终编号 6 电路输出的信号才由编号 3 电路的输出信号决定,这样我们可以简单的控制 EXTI_EMR 来实现是否产生事件的目的
        编号 7 是一个脉冲发生器电路,当它的输入端,即编号 6 电路的输出端,是一个有效信号 1 时就会产生一个脉冲;如果输入端是无效信号就不会输出脉冲。
        编号 8 是一个脉冲信号,就是产生事件的线路最终的产物,这个脉冲信号可以给其他外设电路使用,比如定时器 TIM、模拟数字转换器 ADC 等等,这样的脉冲信号一般用来触发 TIM 或者 ADC开始转换。
        产生中断线路目的是把输入信号输入到 NVIC,进一步会运行中断服务函数,实现功能,这样是软件级的。而产生事件线路目的就是传输一个脉冲信号给其他外设使用,并且是电路级别的信号传输,属于硬件级的。另外,EXTI 是在 APB2 总线上的,在编程时候需要注意到这点

5.3 中断/事件线

        EXTI 有 20 个中断/事件线,每个 GPIO 都可以被设置为输入线,占用 EXTI0 至 EXTI15,还有另外七根用于特定的外设事件,见表 EXTI 中断 _ 事件线。
        4 根特定外设中断/事件线由外设触发,具体用法参考《STM32F10X-中文参考手册》中对外设的具体说明。

e817ff734bf749b285579c7b00f35d61.png

        EXTI0 至 EXTI15 用于 GPIO,通过编程控制可以实现任意一个 GPIO 作为 EXTI 的输入源。由表 EXTI 中断 _ 事件线可知,EXTI0 可以通过 AFIO 的外部中断配置寄存器 1(AFIO_EXTICR1) 的 EXTI0[3:0] 位选择配置为 PA0、PB0、PC0、PD0、PE0、PF0、PG0、PH0 或者 PI0,见图 EXTI0 输入源选择。其他 EXTI 线 (EXTI 中断/事件线) 使用配置都是类似的。

a0ddcdf5b40242d794a9cf4e26daa987.png

 5.4 EXTI 初始化结构体详解

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

1467d679f28840ff8f230faaac0f5f90.png

        1) EXTI_Line:EXTI 中断/事件线选择,可选 EXTI0 至 EXTI19,可参考表 EXTI 中断 _ 事件线选择。
        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)。

5.5 外部中断控制实验

        中断管理分组管理在前面有详细的阐述。这里我们将介绍 STM32 外部 IO 口的中断功能,通过中断的功能,达到实验的效果,即:通过板载的 3 个按键,控制板载的两个 LED 的亮灭以及蜂鸣器的发声。这章的代码主要分布在固件库的 stm32f10x_exti.h 和 stm32f10x_exti.c 文件中。

main.c

/* 外部中断测试实验 */
void exit_test(void)
{//优先级分组,数值越小,优先级越高,在misc.h中最后找//优先级分组不同,抢占优先级和子优先级数目也不同,一般选2组//因为抢占优先级有2位(0-3),子优先级有2位(0-3)NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);delay_init();     //初始化延时函数beep_init();      //初始化beepkey_init();       //初始化按键led_init();       //初始化LEDexit_init();      //初始化外部中断函数usart1_init(115200);//初始化串口1函数while (1){printf("OK\r\n");delay_ms(1000);}
}

exit.c

#include "exit.h"
#include "key.h"
#include "delay.h"
#include "led.h"
#include "beep.h"
#include "sys.h"//①初始化IO口为输入。
//     GPIO_Init();
//②开启IO口复用时钟。
//     RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO,ENABLE);
//③设置IO口与中断线的映射关系。
//     void GPIO_EXTILineConfig();
//④初始化线上中断,设置触发条件等。
//     EXTI_Init();
//⑤配置中断分组(NVIC),并使能中断。
//     NVIC_Init();
//⑥编写中断服务函数。
//     EXTIx_IRQHandler();
//     EXTI_ClearITPendingBit();该函数写在中断服务函数中,中断函数执行完后,用来清除中断标志位void exit_init(void)
{EXTI_InitTypeDef  EXTI_InitStructure;NVIC_InitTypeDef  NVIC_InitStructure;/*初始化外部中断线所对应的IO配置*/key_init();/*开启外部中断配置寄存器AFIO的时钟AFIO*/RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE);/*--------------------------KEY0配置-----------------------------*//* 选择EXTI的信号源 */GPIO_EXTILineConfig(GPIO_PortSourceGPIOE, GPIO_PinSource4); //PE4  IO口映射到外部中断线4(KEYO)EXTI_InitStructure.EXTI_Line = EXTI_Line4;/* 使能中断 */EXTI_InitStructure.EXTI_LineCmd = ENABLE;/* EXTI为中断模式 */EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt;/* 下降沿中断 */EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Falling;EXTI_Init(&EXTI_InitStructure);/* 配置中断源:EXTI4 */NVIC_InitStructure.NVIC_IRQChannel = EXTI4_IRQn; //在stm32f10.h中190行/* 使能中断通道 */NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;/* 配置抢占优先级 */NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 2;/* 配置子优先级 */NVIC_InitStructure.NVIC_IRQChannelSubPriority = 2;NVIC_Init(&NVIC_InitStructure);/*--------------------------KEY1配置-----------------------------*/GPIO_EXTILineConfig(GPIO_PortSourceGPIOE, GPIO_PinSource3); //PE3  IO口映射到外部中断线3(KEY1)EXTI_InitStructure.EXTI_Line = EXTI_Line3;EXTI_InitStructure.EXTI_LineCmd = ENABLE;EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt;EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Falling;EXTI_Init(&EXTI_InitStructure);NVIC_InitStructure.NVIC_IRQChannel = EXTI3_IRQn; //在stm32f10.h中190行NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;NVIC_Init(&NVIC_InitStructure);/*--------------------------WK_UP配置-----------------------------*/GPIO_EXTILineConfig(GPIO_PortSourceGPIOA, GPIO_PinSource0); //PA0  IO口映射到外部中断线0(WK_UP)EXTI_InitStructure.EXTI_Line = EXTI_Line0;EXTI_InitStructure.EXTI_LineCmd = ENABLE;EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt;EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Rising;EXTI_Init(&EXTI_InitStructure);NVIC_InitStructure.NVIC_IRQChannel = EXTI0_IRQn; //在stm32f10.h中190行NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;NVIC_Init(&NVIC_InitStructure);/*--------------------------END OF SECTION-----------------------------*//*-------------------外部中断-- 5-7中断线及中断函数配置-------------------*//*--------------------------GPIOE.5中断配置-----------------------------*///GPIOE.5     中断线以及中断初始化配置 下降沿触发/* 选择EXTI的信号源 */GPIO_EXTILineConfig(GPIO_PortSourceGPIOE, GPIO_PinSource5);/* 选择EXTI线 */EXTI_InitStructure.EXTI_Line = EXTI_Line5;/* EXTI为中断模式 */EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt;/* 下降沿中断 */EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Falling;/* 使能中断 */EXTI_InitStructure.EXTI_LineCmd = ENABLE;EXTI_Init(&EXTI_InitStructure);/*--------------------------GPIOE.6中断配置-----------------------------*///GPIOE.6     中断线以及中断初始化配置 下降沿触发GPIO_EXTILineConfig(GPIO_PortSourceGPIOE, GPIO_PinSource6);EXTI_InitStructure.EXTI_Line = EXTI_Line6;EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt;EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Falling;EXTI_InitStructure.EXTI_LineCmd = ENABLE;EXTI_Init(&EXTI_InitStructure);/*--------------------------GPIOE.7中断配置-----------------------------*///GPIOE.7     中断线以及中断初始化配置 下降沿触发GPIO_EXTILineConfig(GPIO_PortSourceGPIOE, GPIO_PinSource7);EXTI_InitStructure.EXTI_Line = EXTI_Line7;EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt;EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Falling;EXTI_InitStructure.EXTI_LineCmd = ENABLE;EXTI_Init(&EXTI_InitStructure);/* 配置中断源:EXTI5-9 */NVIC_InitStructure.NVIC_IRQChannel = EXTI9_5_IRQn;//在stm32f10.h中190行/* 配置抢占优先级 */NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 2;/* 配置子优先级 */NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;/* 使能中断通道 */NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;NVIC_Init(&NVIC_InitStructure);}//值得注意的是EXTI0-4有自己的中断源、中断函数名
//EXTI5-9共用一个中断源EXTI9_5_IRQn、一个中断函数名EXTI9_5_IRQHandler
//EXTI10-15共用一个中断源EXTI15_10_IRQn、一个中断函数名EXTI15_10_IRQHandler
//但EXTI5-15都可以有属于自己的中断,只是中断函数名一样而已/*** @brief  KEY0中断服务函数,翻转红色绿色LED灯的状态* @param  无* @retval 无*/
void EXTI4_IRQHandler(void)//KEY0中断服务函数,中断名字最好不要修改,但可以进行宏定义
{//确保是否产生了EXTI Line中断if (EXTI_GetITStatus(EXTI_Line4) != RESET){delay_ms(20);if (KEY0 == 0){LED0_R = !LED0_R;LED1_G = !LED1_G;}//清除中断标志位EXTI_ClearITPendingBit(EXTI_Line4);}
}/*** @brief  KEY1中断服务函数,翻转红色LED灯的状态* @param  无* @retval 无*/
void EXTI3_IRQHandler(void)//KEY1中断服务函数
{//确保是否产生了EXTI Line中断if (EXTI_GetITStatus(EXTI_Line3) != RESET){delay_ms(20);if (KEY1 == 0){LED0_R = !LED0_R;}//清除中断标志位EXTI_ClearITPendingBit(EXTI_Line3);}
}/*** @brief  WK_UP中断服务函数,翻转绿色LED灯的状态* @param  无* @retval 无*/
void EXTI0_IRQHandler(void)//WK_UP中断服务函数
{//确保是否产生了EXTI Line中断if (EXTI_GetITStatus(EXTI_Line0) != RESET){delay_ms(20);if (WK_UP == 1){LED1_G = !LED1_G;}//清除中断标志位EXTI_ClearITPendingBit(EXTI_Line0);}
}void EXTI9_5_IRQHandler(void)//5-9上的中断源不管哪个触发都进入中断函数,再进行判断是哪个中断源
{if (EXTI_GetITStatus(EXTI_Line5) != RESET){delay_ms(20);EXTI_ClearITPendingBit(EXTI_Line5); //清除LINE5上的中断标志位}if (EXTI_GetITStatus(EXTI_Line6) != RESET){delay_ms(20);EXTI_ClearITPendingBit(EXTI_Line6); //清除LINE6上的中断标志位}if (EXTI_GetITStatus(EXTI_Line7) != RESET){delay_ms(20);EXTI_ClearITPendingBit(EXTI_Line7); //清除LINE7上的中断标志位}}

注意点:

1)使用外部中断初始化配置时,不仅要配置外部中断 EXTI 外部中断控制器,还要配置 NVIC 嵌套向量中断控制器;

2)每个中断源都有自己的中断函数,值得注意的是EXTI0-4有自己的中断源、中断函数名,而EXTI5-9 共用一个中断源 EXTI9_5_IRQn、一个中断函数名 EXTI9_5_IRQHandler;EXTI10-15共用一个中断源EXTI15_10_IRQn、一个中断函数名EXTI15_10_IRQHandler,但EXTI5-15都可以有属于自己的中断,只是中断函数名一样而已,代码中已经写的很清楚了;

6. STM32 什么情况下开始外设复用 AFIO 时钟

        串口、定时器等,这些都是 STM32 的片上外设,在使用时看作 GPIO 口的一种复用功能。可是在配置这些外设时钟的时候,我们发现,它们只是开启了自己外设的时钟,并没有开启 AFIO 时钟,这是为什么呢?

939d70da7f854506b11d7077a80495e1.png

b11416f7888d4861838091844df6d45b.png        但是,为什么做中断配置时,中断也属于外设,GPIO 的复用,中断除了开启对应的外设时钟,还启了 AFIO 时钟了。

5d4744fcd7f7498cb9a16a13a3e0b0e0.png

针对这个问题,可以参考 STM32 中文参考手册:

991bff1478ee453bb95647ab2fb42fd9.png

ad6c688e08e34d04b93e20005ed532e8.png

        可以看到, 只有当使用 事件控制寄存器、复用重映射和调试寄存器以及外部中断寄存器的时候,才需要提前开启AFIO的时钟,也就是说:当你需要配置 AFIO 这些寄存器的时候,就需要把 RCC_APB2ENR 寄存器的 AFIO 位置‘1’打开 AFIO 时钟。!并不是使用到引脚复用功能 就必须开启AFIO时钟。像定时器、串口这类外设,虽然不需要开启复用时钟,但一定要开启他们自己的相应的外设时钟

        另外,外设确实是引脚功能的一种复用,针对 GPIO 的复用功能,在配置 GPIO 的输出方式时,一定别忘了使用 复用推挽输出 或者 复用开漏输出!!!如下图中的, USART1_TX 的 GPIOA.9 的输出配置,必须使用 复用推挽输出。

397dbc7120364f0b8df6e79f7dfcb7b9.png

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/235907.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

2017年第六届数学建模国际赛小美赛B题电子邮件中的笔迹分析解题全过程文档及程序

2017年第六届数学建模国际赛小美赛 B题 电子邮件中的笔迹分析 原题再现: 笔迹分析是一种非常特殊的调查形式,用于将人们与书面证据联系起来。在法庭或刑事调查中,通常要求笔迹鉴定人确认笔迹样本是否来自特定的人。由于许多语言证据出现在电…

PyTorch深度学习实战(26)——卷积自编码器(Convolutional Autoencoder)

PyTorch深度学习实战(26)——卷积自编码器 0. 前言1. 卷积自编码器2. 使用 t-SNE 对相似图像进行分组小结系列链接 0. 前言 我们已经学习了自编码器 (AutoEncoder) 的原理,并使用 PyTorch 搭建了全连接自编码器,但我们使用的数据…

【PHP入门】2.2 流程控制

-流程控制- 流程控制:代码执行的方向 2.2.1控制分类 顺序结构:代码从上往下,顺序执行。(代码执行的最基本结构) 分支结构:给定一个条件,同时有多种可执行代码(块)&am…

阿里推荐 LongAdder ,不推荐 AtomicLong !

其他系列文章导航 Java基础合集数据结构与算法合集 设计模式合集 多线程合集 分布式合集 ES合集 文章目录 其他系列文章导航 文章目录 前言 一、CAS 1.1 CAS 全称 1.2 通俗理解CAS 1.3 CAS的问题 1.4 解决 ABA 问题 二、LongAdder 2.1 什么是 LongAdder 2.2 为什么推…

用JVS低代码实现业务流程的撤回和重新开始

在当今的数字化时代,业务流程的效率和准确性对于企业的运营至关重要。在实际业务场景中,我们可能需要处理一些复杂的流程,例如申请审批流程、合同签订流程等。这些流程在执行过程中可能会遇到各种情况,例如某个审批步骤需要重新审…

❀My虚拟机上的ftp服务器搭建(centos)❀

❀My虚拟机上的ftp服务器搭建(centos)❀ 在CentOS上搭建FTP服务器可以使用vsftpd软件,下面是详细的搭建教程: ①安装vsftpd软件 在终端中输入以下命令进行安装: sudo yum install vsftpd ②配置vsftpd 打开vsftpd的配置文件,…

【深度学习】序列生成模型(五):评价方法计算实例:计算BLEU-N得分【理论到程序】

文章目录 一、BLEU-N得分(Bilingual Evaluation Understudy)1. 定义2. 计算N1N2BLEU-N 得分 3. 程序 给定一个生成序列“The cat sat on the mat”和两个参考序列“The cat is on the mat”“The bird sat on the bush”分别计算BLEU-N和ROUGE-N得分(N1或…

WEB渗透—PHP反序列化(六)

Web渗透—PHP反序列化 课程学习分享(课程非本人制作,仅提供学习分享) 靶场下载地址:GitHub - mcc0624/php_ser_Class: php反序列化靶场课程,基于课程制作的靶场 课程地址:PHP反序列化漏洞学习_哔哩…

Ubuntu 22.04 禁用(彻底移除)Snap

什么是Snaps Snaps 是 Ubuntu 的母公司 Canonical 于 2016 年 4 月发布 Ubuntu 16.04 LTS(Long Term Support,长期支持版)时引入的一种容器化的软件包格式。自 Ubuntu 16.04 LTS 起,Ubuntu 操作系统可以同时支持 Snap 及 Debian …

3dsmax渲染太慢,用云渲染农场多少钱?

对于许多从事计算机图形设计的创作者来说,渲染速度慢是一个常见问题,尤其是对于那些追求极致出图效果的室内设计师和建筑可视化师,他们通常使用3ds Max这样的工具,而高质量的渲染经常意味着长时间的等待。场景复杂、细节丰富&…

APView500PV电能质量在线监测装置——安科瑞 顾烊宇

概述 APView500PV电能质量在线监测装置采用了高性能多核平台和嵌入式操作系统,遵照IEC61000-4-30《测试和测量技术-电能质量测量方法》中规定的各电能质量指标的测量方法进行测量,集谐波分析、波形采样、电压暂降/暂升/中断、闪变监测、电压不平衡度监测…

CentOS操作学习(二)

上一篇学习了CentOS的常用指令CentOS指令学习-CSDN博客 现在我们接着学习 一、Vi编辑器 这是CentOS中自带的编辑器 三种模式 进入编辑模式后 i:在光标所在字符前开始插入a:在光标所在字符串后开始插入o:在光标所在行的下面另起一新行插入…

命令执行 [SWPUCTF 2021 新生赛]easyrce

打开题目 提示要用url传参,但实际是用url进行一些系统命令执行 那我们就用whoami命令来查看用户和权限 那我们直接用ls / 去查看当下根目录下有哪些文件 我们看到根目录下有flag 直接cat读取就行 知识点: system system是一个函数 用来运行外部的程序…

4.CentOS7开启ssh

Centos7开启ssh 通过命令查看是否安装了ssh服务 rpm -qa | grep openssh 修改主配置文件 vim /etc/ssh/sshd_config 将PermitRootLogin,RSAAuthentication,PubkeyAuthentication的设置打开 RSAAuthentication yes# 启用 RSA 认证PubkeyAuthenticatio…

19_20-Golang中的切片

**Golang **中的切片 主讲教师:(大地) 合作网站:www.itying.com** **(IT 营) 我的专栏:https://www.itying.com/category-79-b0.html 1、为什么要使用切片 因为数组的长度是固定的并且数组长…

【.NET后端工具系列】MediatR实现进程内消息通讯

阅读本文你的收获 学习MediatR工具,实现进程内消息发送和处理过程的解耦学习MediatR的两种消息处理模式了解中介者模式和其好处 一、什么是MediatR? MediatR是一款基于中介者模式的思想而实现的.NET库,支持.NET Framework和跨平台 的.NET C…

aws配置以及下载 spaceNet6 数据集

一:注册亚马逊账号 注册的时候,唯一需要注意的是信用卡绑定,这个可以去淘宝买,搜索aws匿名卡。 注册完记得点击登录,记录一下自己的账户ID哦! 二:登录自己的aws账号 2.1 首先创建一个用户 首…

从YOLOv1到YOLOv8的YOLO系列最新综述【2023年4月】

作者:Juan R. Terven 、Diana M. Cordova-Esparaza 摘要:YOLO已经成为机器人、无人驾驶汽车和视频监控应用的核心实时物体检测系统。我们对YOLO的演变进行了全面的分析,研究了从最初的YOLO到YOLOv8每次迭代的创新和贡献。我们首先描述了标准…

研发管理-代码管理篇

前言: 工作了这些年,工作了三家公司,也用过主流的代码管理平台,比如SVN,git系列(gitlib,gitee),各有优点,我个人比较喜欢SVN,多人协作的代码管理难免会有代码冲突&#…