分享

第16章 STM32中断应用概览

 waston 2018-05-02

第16章     STM32中断应用概览

本章参考资料《STM32F4xx 中文参考手册》第十章-中断和事件、《 ARM Cortex-M4F 技术参考手册》-4.3 章节:NVIC4.4章节:SCB4.4.5AIRCR

STM32中断非常强大,每个外设都可以产生中断,所以中断的讲解放在哪一个外设里面去讲都不合适,这里单独抽出一章来做一个总结性的介绍,这样在其他章节涉及到中断部分的知识我们就不用费很大的篇幅去讲解,只要示意性带过即可。

本章如无特别说明,异常就是中断,中断就是异常,请不要刻意钻牛角尖较劲。

16.1 异常类型

F429在内核水平上搭载了一个异常响应系统,支持为数众多的系统异常和外部中断。其中系统异常有10个,外部中断有91个。除了个别异常的优先级被定死外,其它异常的优先级都是可编程的。有关具体的系统异常和外部中断可在标准库文件stm32f4xx.h这个头文件查询到,在IRQn_Type这个结构体里面包含了F4系列全部的异常声明。

表格 12 F429系统异常清单

编号

优先级

优先级类型

名称

说明

地址

 

-

-

-

保留(实际存的是MSP地址)

0X0000 0000

 

-3

固定

Reset

复位

0X0000 0004

 

-2

固定

NMI

不可屏蔽中断。 RCC 时钟安全系统(CSS) 连接到 NMI 向量

0X0000 0008

 

-1

固定

HardFault

所有类型的错误

0X0000 000C

 

0

可编程

MemManage

存储器管理

0X0000 0010

 

1

可编程

BusFault

预取指失败,存储器访问失败

0X0000 0014

 

2

可编程

UsageFault

未定义的指令或非法状态

0X0000 0018

 

-

-

-

保留

0X0000 001C-

0X0000 002B

 

3

可编程

SVCall

通过 SWI 指令调用的系统服务

0X0000 002C

 

4

可编程

Debug Monitor

调试监控器

0X0000 0030

 

-

-

-

保留

0X0000 0034

 

5

可编程

PendSV

可挂起的系统服务

0X0000 0038

 

6

可编程

SysTick

系统嘀嗒定时器

0X0000 003C

表格 13 F429外部中断清单

编号

优先级

优先级类型

名称

说明

地址

0

7

可编程

-

窗口看门狗中断

0X0000 0040

1

8

可编程

PVD

连接EXTI 线的可编程电压检测中断

0X0000 0044

2

9

可编程

TAMP_STAMP

连接EXTI 线的入侵和时间戳中断

0X0000 0048

中间部分省略,详情请参考STM32F4xx 中文参考手册》第十章-中断和事件-向量表部分

84

91

可编程

SPI4

SPI4全局中断

0X0000 0190

85

92

可编程

SPI5

SPI5全局中断

0X0000 0194

86

93

可编程

SPI6

SPI6全局中断

0X0000 0198

87

94

可编程

SAI1

SAI1全局中断

0X0000 019C

88

95

可编程

LTDC

LTDC全局中断

0X0000 01A0

89

96

可编程

LTDC_ER

LTDC_ER全局中断

0X0000 01A4

90

97

可编程

DMA2D

DMA2D全局中断

0X0000 01A8

16.2 NVIC简介

在讲如何配置中断优先级之前,我们需要先了解下NVICNVIC是嵌套向量中断控制器,控制着整个芯片中断相关的功能,它跟内核紧密耦合,是内核里面的一个外设。但是各个芯片厂商在设计芯片的时候会对Cortex-M4内核里面的NVIC进行裁剪,把不需要的部分去掉,所以说STM32NVICCortex-M4NVIC的一个子集。

16.2.1 NVIC寄存器简介

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

代码 17 NVIC结构体定义,来自固件库头文件:core_cm4.h

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

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

16.2.2 NVIC 中断配置固件库

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

表格 14 符合CMSIS标准的NVIC库函数

NVIC库函数

描述

void NVIC_EnableIRQ(IRQn_Type IRQn)

使能中断

void NVIC_DisableIRQ(IRQn_Type IRQn)

失能中断

void NVIC_SetPendingIRQ(IRQn_Type IRQn)

设置中断悬起位

void NVIC_ClearPendingIRQ(IRQn_Type IRQn)

清除中断悬起位

uint32_t NVIC_GetPendingIRQ(IRQn_Type IRQn)

获取悬起中断编号

void NVIC_SetPriority(IRQn_Type IRQn, uint32_t priority)

设置中断优先级

uint32_t NVIC_GetPriority(IRQn_Type IRQn)

获取中断优先级

void NVIC_SystemReset(void)

系统复位

这些库函数我们在编程的时候用的都比较少,甚至基本都不用。在配置中断的时候我们还有更简洁的方法,请看中断编程小节。

16.3 优先级的定义

16.3.1 优先级定义

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

表格 15 F429 使用4bit表达优先级

bit7

bit6

bit5

bit4

bit3

bit2

bit1

bit0

用于表达优先级

未使用,读回为0

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

16.3.2 优先级分组

优先级的分组由内核外设SCB的应用程序中断及复位控制寄存器AIRCRPRIGROUP[10:8]位决定,F429分为了5组,具体如下:主优先级=抢占优先级

PRIGROUP[2:0]

中断优先级值PRI_N[7:4]

级数

二进制点

主优先级位

子优先级位

主优先级

子优先级

0b 011

0b xxxx

[7:4]

None

16

None

0b 100

0b xxx.y

[7:5]

[4]

8

2

0b 101

0b xx.yy

[7:6]

[5:4]

4

4

0b 110

0b x.yyy

[7]

[6:4]

2

9

0b 111

0b .yyyy

None

[7:4]

None

16

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

代码 18 中断优先级分组库函数

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

表格 16 优先级分组真值表

优先级分组

主优先级

子优先级

描述

NVIC_PriorityGroup_0

0

0-15

-0bit,子-4bit

NVIC_PriorityGroup_1

0-1

0-7

-1bit,子-3bit

NVIC_PriorityGroup_2

0-3

0-3

-2bit,子-2bit

NVIC_PriorityGroup_3

0-7

0-1

-3bit,子-1bit

NVIC_PriorityGroup_4

0-15

0

-4bit,子-0bit

16.4 中断编程

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

1、使能外设某个中断,这个具体由每个外设的相关中断使能位控制。比如串口有发送完成中断,接收完成中断,这两个中断都由串口控制寄存器的相关中断使能位控制。

2、初始化NVIC_InitTypeDef结构体,配置中断优先级分组,设置抢占优先级和子优先级,使能中断请求。

代码 19 NVIC初始化结构体

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

有关NVIC初始化结构体的成员我们一一解释下:

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

代码 20 IRQn_Type中断源结构体

 1 typedef
							enum IRQn {
				
 2 
							//Cortex-M4 处理器异常编号
			
 3     NonMaskableInt_IRQn      = -14,
				
 4     MemoryManagement_IRQn    = -12,
				
 5     BusFault_IRQn            = -11,
				
 6     UsageFault_IRQn          = -10,
				
 7     SVCall_IRQn              = -5,
				
 8     DebugMonitor_IRQn        = -4,
				
 9     PendSV_IRQn              = -2,
				
10     SysTick_IRQn             = -1,
				
11 
							//STM32 外部中断编号
			
12     WWDG_IRQn                = 0,
				
13     PVD_IRQn                 = 1,
				
14     TAMP_STAMP_IRQn          = 2,
				
15 
			
16 
								// 限于篇幅,中间部分代码省略,具体的可查看库文件stm32f4xx.h
				
17 
			
18     SPI4_IRQn                = 84,
				
19     SPI5_IRQn                = 85,
				
20     SPI6_IRQn                = 86,
				
21     SAI1_IRQn                = 87,
				
22     LTDC_IRQn                = 88,
				
23     LTDC_ER_IRQn             = 89,
				
24     DMA2D_IRQn               = 90
				
25 } IRQn_Type;
				
			

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

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

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

3、编写中断服务函数

在启动文件startup_stm32f429_439xx.s中我们预先为每个中断都写了一个中断服务函数,只是这些中断函数都是为空,为的只是初始化中断向量表。实际的中断服务函数都需要我们重新编写,中断服务函数我们统一写在stm32f4xx_it.c这个库文件中。

关于中断服务函数的函数名必须跟启动文件里面预先设置的一样,如果写错,系统就在中断向量表中找不到中断服务函数的入口,直接跳转到启动文件里面预先写好的空函数,并且在里面无限循环,实现不了中断。

16.5 每课一问

1、库文件core_cm4.h主要实现了什么?回去认真看库的源码

2、库文件mics.c和mics.h主要实现了什么?回去认真看库的源码

3、如果实现一次软件系统复位,具体是操作哪个寄存器的哪个位实现?答案:给内核外设SCB的AIRCR寄存器的位2:SYSRESETREQ 写1即可实现一次系统复位。


第17章     EXTI—外部中断/事件控制器

本章参考资料:《STM32F4xx中文参考手册》系统配置控制器以及中断和事件章节。

上一章节我们已经详细介绍了NVIC,对STM32F4xx中断管理系统有个全局的了解,我们这章的内容是NVIC的实例应用,也是STM32F4xx控制器非常重要的一个资源。学习本章时,配合《STM32F4xx中文参考手册》系统配置控制器以及中断和事件章节一起阅读,效果会更佳,特别是涉及到寄存器说明的部分。

特别说明,本书内容是以STM32F42xxx系列控制器资源讲解。

17.1 EXTI简介

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

17.2 EXTI功能框图

EXTI的功能框图包含了EXTI最核心内容,掌握了功能框图,对EXTI就有一个整体的把握,在编程时就思路就非常清晰。EXTI功能框图见图 171

在图 171可以看到很多在信号线上打一个斜杠并标注"23"字样,这个表示在控制器内部类似的信号线路有23个,这与EXTI总共有23个中断/事件线是吻合的。所以我们只要明白其中一个的原理,那其他22个线路原理也就知道了。

171 EXTI功能框图

EXTI可分为两大部分功能,一个是产生中断,另一个是产生事件,这两个功能从硬件上就有所不同。

首先我们来看图 171中红色虚线指示的电路流程。它是一个产生中断的线路,最终信号流入到NVIC控制器内。

编号1是输入线,EXTI控制器有23个中断/事件输入线,这些输入线可以通过寄存器设置为任意一个GPIO,也可以是一些外设的事件,这部分内容我们将在后面专门讲解。输入线一般是存在电平变化的信号。

编号2是一个边沿检测电路,它会根据上升沿触发选择寄存器(EXTI_RTSR)和下降沿触发选择寄存器(EXTI_FTSR)对应位的设置来控制信号触发。边沿检测电路以输入线作为信号输入端,如果检测到有边沿跳变就输出有效信号1给编号3电路,否则输出无效信号0。而EXTI_RTSREXTI_FTSR两个寄存器可以控制器需要检测哪些类型的电平跳变过程,可以是只有上升沿触发、只有下降沿触发或者上升沿和下降沿都触发。

编号3电路实际就是一个或门电路,它一个输入来自编号2电路,另外一输入来自软件中断事件寄存器(EXTI_SWIER)EXTI_SWIER允许我们通过程序控制就可以启动中断/事件线,这在某些地方非常有用。我们知道或门的作用就是有"就为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等等。

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

另外,EXTI是在APB2总线上的,在编程时候需要注意到这点。

17.3 中断/事件线

EXTI23个中断/事件线,每个GPIO都可以被设置为输入线,占用EXTI0EXTI15,还有另外七根用于特定的外设事件,见表 171

七根特定外设中断/事件线由外设触发,具体用法参考《STM32F4xx中文参考手册》中对外设的具体说明。

171 EXTI中断/事件线

中断/事件线

输入源

EXTI0

PX0(X可为A,B,C,D,E,F,G,H,I)

EXTI1

PX1(X可为A,B,C,D,E,F,G,H,I)

EXTI2

PX2(X可为A,B,C,D,E,F,G,H,I)

EXTI3

PX3(X可为A,B,C,D,E,F,G,H,I)

EXTI4

PX4(X可为A,B,C,D,E,F,G,H,I)

EXTI5

PX5(X可为A,B,C,D,E,F,G,H,I)

EXTI6

PX6(X可为A,B,C,D,E,F,G,H,I)

EXTI7

PX7(X可为A,B,C,D,E,F,G,H,I)

EXTI8

PX8(X可为A,B,C,D,E,F,G,H,I)

EXTI9

PX9(X可为A,B,C,D,E,F,G,H,I)

EXTI10

PX10(X可为A,B,C,D,E,F,G,H,I)

EXTI11

PX11(X可为A,B,C,D,E,F,G,H,I)

EXTI12

PX12(X可为A,B,C,D,E,F,G,H,I)

EXTI13

PX13(X可为A,B,C,D,E,F,G,H,I)

EXTI14

PX14(X可为A,B,C,D,E,F,G,H,I)

EXTI15

PX15(X可为A,B,C,D,E,F,G,H)

EXTI16

可编程电压检测器(PVD)输出

EXTI17

RTC闹钟事件

EXTI18

USB OTG FS唤醒事件

EXTI19

以太网唤醒事件

EXTI20

USB OTG HS(FS中配置)唤醒事件

EXTI21

RTC入侵和时间戳事件

EXTI22

RTC唤醒事件

EXTI0EXTI15用于GPIO,通过编程控制可以实现任意一个GPIO作为EXTI的输入源。由表 171可知,EXTI0可以通过SYSCFG外部中断配置寄存器1(SYSCFG_EXTICR1)EXTI0[3:0]位选择配置为PA0PB0PC0PD0PE0PF0PG0PH0或者PI0,见图 172。其他EXTI线(EXTI中断/事件线)使用配置都是类似的。

172 EXTI0输入源选择

17.4 EXTI初始化结构体详解

标准库函数对每个外设都建立了一个初始化结构体,比如EXTI_InitTypeDef,结构体成员用于设置外设工作参数,并由外设初始化配置函数,比如EXTI_Init()调用,这些设定参数将会设置外设相应的寄存器,达到配置外设工作环境的目的。

初始化结构体和初始化库函数配合使用是标准库精髓所在,理解了初始化结构体每个成员意义基本上就可以对该外设运用自如了。初始化结构体定义在stm32f4xx_exti.h文件中,初始化库函数定义在stm32f4xx_exti.c文件中,编程时我们可以结合这两个文件内注释使用。

代码清单 171 EXTI初始化结构体

1 typedef struct {

2 uint32_t EXTI_Line; // 中断/事件线

3 EXTIMode_TypeDef EXTI_Mode; // EXTI模式

4 EXTITrigger_TypeDef EXTI_Trigger; // 触发事件

5 FunctionalState EXTI_LineCmd; // EXTI控制

6 } EXTI_InitTypeDef;

1)    EXTI_Line:EXTI中断/事件线选择,可选EXTI0至EXTI22,可参考表 171选择。

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

17.5 外部中断控制实验

中断在嵌入式应用中占有非常重要的地位,几乎每个控制器都有中断功能。中断对保证紧急事件得到第一时间处理是非常重要的

我们设计使用外接的按键来作为触发源,使得控制器产生中断,并在中断服务函数中实现控制RGB彩灯的任务。

17.5.1 硬件设计

轻触按键在按下时会使得引脚接通,通过电路设计可以使得按下时产生电平变化,见图 171

173 按键电路设计

17.5.2 软件设计

这里只讲解核心的部分代码,有些变量的设置,头文件的包含等并没有涉及到,完整的代码请参考本章配套的工程。我们创建了两个文件:bsp_exti.cbsp_exti.h文件用来存放EXTI驱动程序及相关宏定义,中断服务函数放在stm32f4xx_it.h文件中。

1.    编程要点

1)    初始化RGB彩灯的GPIO;

2)    开启按键GPIO时钟和SYSCFG时钟;

3)    配置NVIC;

4)    配置按键GPIO为输入模式;

5)    将按键GPIO连接到EXTI源输入;

6)    配置按键EXTI中断/事件线;

7)    编写EXTI中断服务函数。

2.    软件分析
按键和EXTI宏定义

代码清单 172 按键和EXTI 宏定义

1 //引脚定义

2 /*******************************************************/

3 #define KEY1_INT_GPIO_PORT GPIOA

4 #define KEY1_INT_GPIO_CLK RCC_AHB1Periph_GPIOA

5 #define KEY1_INT_GPIO_PIN GPIO_Pin_0

6 #define KEY1_INT_EXTI_PORTSOURCE EXTI_PortSourceGPIOA

7 #define KEY1_INT_EXTI_PINSOURCE EXTI_PinSource0

8 #define KEY1_INT_EXTI_LINE EXTI_Line0

9 #define KEY1_INT_EXTI_IRQ EXTI0_IRQn

10

11 #define KEY1_IRQHandler EXTI0_IRQHandler

12

13 #define KEY2_INT_GPIO_PORT GPIOC

14 #define KEY2_INT_GPIO_CLK RCC_AHB1Periph_GPIOC

15 #define KEY2_INT_GPIO_PIN GPIO_Pin_13

16 #define KEY2_INT_EXTI_PORTSOURCE EXTI_PortSourceGPIOC

17 #define KEY2_INT_EXTI_PINSOURCE EXTI_PinSource13

18 #define KEY2_INT_EXTI_LINE EXTI_Line13

19 #define KEY2_INT_EXTI_IRQ EXTI15_10_IRQn

20

21 #define KEY2_IRQHandler EXTI15_10_IRQHandler

使用宏定义方法指定与电路设计相关配置,这对于程序移植或升级非常有用的。

嵌套向量中断控制器NVIC配置

代码清单 173 NVIC配置

1 static void NVIC_Configuration(void)

2 {

3 NVIC_InitTypeDef NVIC_InitStructure;

4

5 /* 配置NVIC为优先级组1 */

6 NVIC_PriorityGroupConfig(NVIC_PriorityGroup_1);

7

8 /* 配置中断源:按键1 */

9 NVIC_InitStructure.NVIC_IRQChannel = KEY1_INT_EXTI_IRQ;

10 /* 配置抢占优先级:1 */

11 NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;

12 /* 配置子优先级:1 */

13 NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;

14 /* 使能中断通道 */

15 NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;

16 NVIC_Init(&NVIC_InitStructure);

17

18 /* 配置中断源:按键2,其他使用上面相关配置 */

19 NVIC_InitStructure.NVIC_IRQChannel = KEY2_INT_EXTI_IRQ;

20 NVIC_Init(&NVIC_InitStructure);

21 }

有关NVIC配置问题可参考上一章节内容,这里不做过多解释。

EXTI中断配置

代码清单 174 EXTI中断配置

1 void EXTI_Key_Config(void)

2 {

3 GPIO_InitTypeDef GPIO_InitStructure;

4 EXTI_InitTypeDef EXTI_InitStructure;

5

6 /*开启按键GPIO口的时钟*/

7 RCC_AHB1PeriphClockCmd(KEY1_INT_GPIO_CLK|KEY2_INT_GPIO_CLK ,ENABLE);

8

9 /* 使能 SYSCFG 时钟,使用GPIO外部中断时必须使能SYSCFG时钟*/

10 RCC_APB2PeriphClockCmd(RCC_APB2Periph_SYSCFG, ENABLE);

11

12 /* 配置 NVIC */

13 NVIC_Configuration();

14

15 /* 选择按键1的引脚 */

16 GPIO_InitStructure.GPIO_Pin = KEY1_INT_GPIO_PIN;

17 /* 设置引脚为输入模式 */

18 GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN;

19 /* 设置引脚不上拉也不下拉 */

20 GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL;

21 /* 使用上面的结构体初始化按键 */

22 GPIO_Init(KEY1_INT_GPIO_PORT, &GPIO_InitStructure);

23

24 /* 连接 EXTI 中断源key1引脚 */

25 SYSCFG_EXTILineConfig(KEY1_INT_EXTI_PORTSOURCE,

26 KEY1_INT_EXTI_PINSOURCE);

27

28 /* 选择 EXTI 中断源 */

29 EXTI_InitStructure.EXTI_Line = KEY1_INT_EXTI_LINE;

30 /* 中断模式 */

31 EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt;

32 /* 下降沿触发 */

33 EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Rising;

34 /* 使能中断/事件线 */

35 EXTI_InitStructure.EXTI_LineCmd = ENABLE;

36 EXTI_Init(&EXTI_InitStructure);

37

38 /* 选择按键2的引脚 */

39 GPIO_InitStructure.GPIO_Pin = KEY2_INT_GPIO_PIN;

40 /* 其他配置与上面相同 */

41 GPIO_Init(KEY2_INT_GPIO_PORT, &GPIO_InitStructure);

42

43 /* 连接 EXTI 中断源key2 引脚 */

44 SYSCFG_EXTILineConfig(KEY2_INT_EXTI_PORTSOURCE,

45 KEY2_INT_EXTI_PINSOURCE);

46

47 /* 选择 EXTI 中断源 */

48 EXTI_InitStructure.EXTI_Line = KEY2_INT_EXTI_LINE;

49 EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt;

50 /* 上升沿触发 */

51 EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Falling;

52 EXTI_InitStructure.EXTI_LineCmd = ENABLE;

53 EXTI_Init(&EXTI_InitStructure);

54 }

首先,使用GPIO_InitTypeDefEXTI_InitTypeDef结构体定义两个用于GPIOEXTI初始化配置的变量,关于这两个结构体前面都已经做了详细的讲解。

使用GPIO之前必须开启GPIO端口的时钟;用到EXTI必须开启SYSCFG时钟。

调用NVIC_Configuration函数完成对按键1、按键2优先级配置并使能中断通道。

作为中断/时间输入线把GPIO配置为输入模式,这里不使用上拉或下拉,有外部电路完全决定引脚的状态。

SYSCFG_EXTILineConfig函数用来指定中断/事件线的输入源,它实际是设定SYSCFG外部中断配置寄存器的值,该函数接收两个参数,第一个参数指定GPIO端口源,第二个参数为选择对应GPIO引脚源编号。

我们的目的是产生中断,执行中断服务函数,EXTI选择中断模式,按键1使用下降沿触发方式,并使能EXTI线。

按键2基本上采用与按键1相关参数配置,只是改为上升沿触发方式。

EXTI中断服务函数

代码清单 175 EXTI中断服务函数

1 void KEY1_IRQHandler(void)

2 {

3 //确保是否产生了EXTI Line中断

4 if (EXTI_GetITStatus(KEY1_INT_EXTI_LINE) != RESET) {

5 // LED1 取反

6 LED1_TOGGLE;

7 //清除中断标志位

8 EXTI_ClearITPendingBit(KEY1_INT_EXTI_LINE);

9 }

10 }

11

12 void KEY2_IRQHandler(void)

13 {

14 //确保是否产生了EXTI Line中断

15 if (EXTI_GetITStatus(KEY2_INT_EXTI_LINE) != RESET) {

16 // LED2 取反

17 LED2_TOGGLE;

18 //清除中断标志位

19 EXTI_ClearITPendingBit(KEY2_INT_EXTI_LINE);

20 }

21 }

当中断发生时,对应的中断服务函数就会被执行,我们可以在中断服务函数实现一些控制。

一般为确保中断确实发生,我们会在中断服务函数调用中断标志位状态读取函数读取外设中断标志位并判断标志位状态。

EXTI_GetITStatus函数用来获取EXTI的中断标志位状态,如果EXTI线有中断发生函数返回"SET"否则返回"RESET"。实际上,EXTI_GetITStatus函数是通过读取EXTI_PR寄存器值来判断EXTI线状态的。

按键1的中断服务函数我们让LED1翻转其状态,按键2的中断服务函数我们让LED2翻转其状态。执行任务后需要调用EXTI_ClearITPendingBit函数清除EXTI线的中断标志位。

主函数

代码清单 176 主函数

1 int main(void)

2 {

3 /* LED 端口初始化 */

4 LED_GPIO_Config();

5

6 /* 初始化EXTI中断,按下按键会触发中断,

7 * 触发中断会进入stm32f4xx_it.c文件中的函数

8 * KEY1_IRQHandlerKEY2_IRQHandler,处理中断,反转LED灯。

9 */

10 EXTI_Key_Config();

11

12 /* 等待中断,由于使用中断方式,CPU不用轮询按键 */

13 while (1) {

14 }

15 }

主函数非常简单,只有两个任务函数。LED_GPIO_Config函数定义在bsp_led.c文件内,完成RGB彩灯的GPIO初始化配置。EXTI_Key_Config函数完成两个按键的GPIOEXTI配置。

17.5.3 下载验证

保证开发板相关硬件连接正确,把编译好的程序下载到开发板。此时RGB彩色灯是暗的,如果我们按下开发板上的按键1RGB彩灯变亮,再按下按键1RGB彩灯又变暗;如果我们按下开发板上的按键2并弹开,RGB彩灯变亮,再按下开发板上的KEY2并弹开,RGB彩灯又变暗。

每课一问

1、    是否可以同时使用PA0和PB0中断?如果不可以,有什么解决方法。

2、    从硬件角度结合程序分析,为什么按下按键1RGB彩灯就马上变化,而按键2却需要按下按键再弹开之后RGB彩灯才变化?

    本站是提供个人知识管理的网络存储空间,所有内容均由用户发布,不代表本站观点。请注意甄别内容中的联系方式、诱导购买等信息,谨防诈骗。如发现有害或侵权内容,请点击一键举报。
    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多