中国水土保持生态环境建设网站,大兴网站制作,万站群cms,微网页制作模板目录
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 优先…目录
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系统异常清单 在程序中体现在 stm32f10x.h 文件中可找到这些异常的编号 2.2 F103 外部中断清单 在程序中体现在 stm32f10x.h 文件中可找到这些外部中断的编号根据宏定义不同的芯片类型进行不同的中断编号 3. NVIC 简介 NVIC 是嵌套向量中断控制器控制着整个芯片中断相关的功能它跟内核紧密耦合是内核里面的一个外设。但是各个芯片厂商在设计芯片的时候会对 Cortex-M3 内核里面的 NVIC 进行裁剪把不需要的部分去掉所以说 STM32的 NVIC 是 Cortex-M3 的 NVIC 的一个子集。
3.1 NVIC 寄存器简介 在固件库中NVIC 的结构体定义可谓是颇有远虑给每个寄存器都预留了很多位恐怕为的是日后扩展功能。不过 STM32F103 可用不了这么多只是用了部分而已具体使用了多少可参考 《Cortex-M3 内核编程手册》-4.3.11:NVIC 寄存器映射。 该结构体可在 core_cm3.h 中找见 在配置中断的时候我们一般只用 ISER、ICER 和 IP 这三个寄存器ISER 用来使能中断ICER 用来失能中断IP 用来设置中断优先级。
结构体是声明了但是我们如何找到 NVIC 控制器的起始地址呢也就是说这个结构体怎么和地址联系起来 在对中断初始化时点击 NVIC_Init点击进去看到 NVIC 的指针访问
点击 NVIC 在 core_cm3.h 文件中 找到它的宏定义发现 NVIC_Type 结构体的起始地址NVIC_BASE 点击 NVIC_BASE看到 NVIC_BASE 的地址是在 SCS_BASE 地址进行偏移得到的 我们去查看 Cortex-M3 权威指南NVIC 与中断控制可以看到 NVIC 嵌套向量中断控制器的起始地址是0xE000 E1000xE000 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 用于表达优先级的这 4bit又被分组成抢占优先级和子优先级。如果有多个中断同时响应抢占优先级高的就会抢占抢占优先级低的优先得到执行如果抢占优先级相同就比较子优先级。如果抢占优先级和子优先级都相同的话就比较他们的硬件中断编号编号越小优先级越高。
注意这里的先是比较你的 抢占和子优先级数值越小优先级越高当你的外设抢占和子优先级都相同的话这时候比较的是 硬件中断编号编号越小优先级越高。这里的硬件中断编号指的是在 stmf103x.h 文件中定义好的 4.2 优先级分组 优先级的分组由内核外设 SCB 的应用程序中断及复位控制这里简单介绍一下 STM32 的中断分组STM32 将中断分为 5 个组组 0~4。该分组的设置是由 SCB-AIRCR 寄存器的 bit10~8 来定义的。具体的分配关系如表 4.5.1 所示 设置优先级分组可调用库函数 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;
} 也就是说当选择 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 简介 EXTIExternal 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(xA,B,C,D,EF,G)分别对应中断线 0~15。这样每个中断线对应了最多 7 个 IO 口以线 0 为例它对应了 GPIOA.0、GPIOB.0、GPIOC.0、GPIOD.0、GPIOE.0、GPIOF.0、GPIOG.0。而中断线每次只能连接到 1 个 IO 口上这样就需要通过配置来决定对应的中断线配置到哪个 GPIO 上了。下面我们看看 GPIO 跟中断线的映射关系图 5.2 EXTI 功能框图 EXTI 的功能框图包含了 EXTI 最核心内容掌握了功能框图对 EXTI 就有一个整体的把握在编程时思路就非常清晰。EXTI 功能框图见图 EXTI 功能框图。 在图 EXTI 功能框图 可以看到很多在信号线上打一个斜杠并标注 “20” 字样这个表示在控制器内部类似的信号线路有 20 个这与 EXTI 总共有 20 个中断/事件线是吻合的。所以我们只要明白其中一个的原理那其他 19 个线路原理也就知道了。 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-中文参考手册》中对外设的具体说明。 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 中断/事件线) 使用配置都是类似的。 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 文件中编程时我们可以结合这两个文件内注释使用。 1) EXTI_LineEXTI 中断/事件线选择可选 EXTI0 至 EXTI19可参考表 EXTI 中断 _ 事件线选择。 2) EXTI_ModeEXTI 模式选择可选为产生中断 (EXTI_Mode_Interrupt) 或者产生事件 (EXTI_Mode_Event)。 3) EXTI_TriggerEXTI 边沿触发事件可选上升沿触发 (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口映射到外部中断线0WK_UPEXTI_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_IRQHandlerEXTI10-15共用一个中断源EXTI15_10_IRQn、一个中断函数名EXTI15_10_IRQHandler但EXTI5-15都可以有属于自己的中断只是中断函数名一样而已代码中已经写的很清楚了
6. STM32 什么情况下开始外设复用 AFIO 时钟 串口、定时器等这些都是 STM32 的片上外设在使用时看作 GPIO 口的一种复用功能。可是在配置这些外设时钟的时候我们发现它们只是开启了自己外设的时钟并没有开启 AFIO 时钟这是为什么呢 但是为什么做中断配置时中断也属于外设GPIO 的复用中断除了开启对应的外设时钟还启了 AFIO 时钟了。 针对这个问题可以参考 STM32 中文参考手册 可以看到 只有当使用 事件控制寄存器、复用重映射和调试寄存器以及外部中断寄存器的时候才需要提前开启AFIO的时钟也就是说当你需要配置 AFIO 这些寄存器的时候就需要把 RCC_APB2ENR 寄存器的 AFIO 位置‘1’打开 AFIO 时钟。并不是使用到引脚复用功能 就必须开启AFIO时钟。像定时器、串口这类外设虽然不需要开启复用时钟但一定要开启他们自己的相应的外设时钟。 另外外设确实是引脚功能的一种复用针对 GPIO 的复用功能在配置 GPIO 的输出方式时一定别忘了使用 复用推挽输出 或者 复用开漏输出如下图中的 USART1_TX 的 GPIOA.9 的输出配置必须使用 复用推挽输出。