龙空技术网

【STM32F103ZET6开发板】第2-5讲:外部中断EXTI

电子友人章 220

前言:

此时同学们对“外部中断程序框图”大约比较着重,朋友们都想要分析一些“外部中断程序框图”的相关文章。那么小编同时在网络上汇集了一些有关“外部中断程序框图””的相关文章,希望大家能喜欢,同学们快快来学习一下吧!

外部中断EXTI

实验目的掌握STM32 EXTI外部中断原理。掌握STM32中断优先级含义。掌握GPIO口外部中断程序设计。实验内容编写程序实现单个GPIO口外部中断的触发。编写程序实现多个GPIO口外部中断的触发。硬件设计开发板IO口外部中断硬件电路

微处理器STM32的每一个IO口都可以作为外部中断输入使用。实现IO口电平变化以触发中断的方式有很多,使用外部按键改变IO口电平是最为简单直观的一种方式。

IK-ZET6开发板上设计了4个用户按键S1、S2、S3、S4,分别连接到STM32F103ZET6的PE2~PE5引脚。可以通过操作相应按键控制相应外部中断触发,进入中断服务函数。

图1:开发板IO口外部中断硬件电路

4个用户按键占用的单片机的引脚如下表:

表1:用户按键引脚分配

KEY

颜色

引脚

说明

按键S1

黄色

PE2

独立GPIO

按键S2

黄色

PE3

独立GPIO

按键S3

黄色

PE4

独立GPIO

按键S4

黄色

PE5

独立GPIO

注:独立GPIO表示开发板没有其他的电路使用这个GPIO。STM32外部中断EXTI简介

EXTI是External interrupt/event controller缩写,翻译过来是:外部中断/事件控制器。

微处理器STM32的每一个GPIO口都能配置成一个外部中断触发源,这是意法半导体STM32的强大之处。

STM32F103ZET6支持 19 个外部中断/事件请求,每个中断设有状态位,每个中断/事件都有独立的触发和屏蔽设置。下图为外部中断/事件控制器框图。

图2:外部中断/事件控制器框图

针对微处理器STM32F103外部中断/事件输入线、连接源及中断服务函数汇总如下表:

表2:EXTI相关输入线及中断服务函数

序号

输入线

连接源

中断服务函数

备注

1

输入线0

PA0 ~ PG0

EXTI0_IRQHandler

PA0 ~ PG0中选其一

2

输入线1

PA1 ~ PG1

EXTI1_IRQHandler

PA1 ~ PG1中选其一

3

输入线2

PA2 ~ PG2

EXTI2_IRQHandler

PA2 ~ PG2中选其一

4

输入线3

PA3 ~ PG3

EXTI3_IRQHandler

PA3 ~ PG3中选其一

5

输入线4

PA4 ~ PG4

EXTI4_IRQHandler

PA4 ~ PG4中选其一

6

输入线5

PA5 ~ PG5

EXTI9_5_IRQHandler

PA5 ~ PG5中选其一

7

输入线6

PA6 ~ PG6

PA6 ~ PG6中选其一

8

输入线7

PA7 ~ PG7

PA7 ~ PG7中选其一

9

输入线8

PA8 ~ PG8

PA8 ~ PG8中选其一

10

输入线9

PA9 ~ PG9

PA9 ~ PG9中选其一

11

输入线10

PA10 ~ PG10

EXTI15_10_IRQHandler

PA10 ~ PG10中选其一

12

输入线11

PA11 ~ PG11

PA11 ~ PG11中选其一

13

输入线12

PA12 ~ PG12

PA12 ~ PG12中选其一

14

输入线13

PA13 ~ PG13

PA13 ~ PG13中选其一

15

输入线14

PA14 ~ PG14

PA14 ~ PG14中选其一

16

输入线15

PA15 ~ PG15

PA15 ~ PG15中选其一

17

输入线16

PVD输出

PVD_IRQHandler

软件配置

18

输入线17

RTC闹钟事件

RTCAlarm_IRQHandler

软件配置

19

输入线18

USB唤醒事件

USBWakeUp_IRQHandler

软件配置

注:针对STM32F105和STM32F107互联型产品,输入线总数是20,除上述19个外,还有连接到以太网唤醒事件的输入线。软件设计EXTI寄存器汇集

STM32F103提供了6个用于操作EXTI的寄存器,如下表所示:

表3:EXTI相关寄存器

序号

寄存器名

读/写

功能描述

1

EXTI_IMR

读/写

中断屏蔽寄存器。

2

EXTI_EMR

读/写

事件屏蔽寄存器。

3

EXTI_RTSR

读/写

上升沿触发选择寄存器。

4

EXTI_FTSR

读/写

下降沿触发选择寄存器。

5

EXTI_SWIER

读/写

软件中断事件寄存器。

6

EXTI_PR

只写

挂起寄存器。

每一种寄存器详细的描述在这里不做具体的介绍,大家可以参考目录:“第1部分:开发板硬件资料”--->“2 - 芯片资料”中“STM32英文参考手册_V15”或“STM32中文参考手册_V10”对应的Interrupts and events章节的寄存器部分认真研读。

EXTI库函数汇集

官方提供的最终库函数版本是V3.5版本,该版本库函数提供了8个与EXTI操作有关的库函数,如下表所示:

表4:EXTI相关库函数汇集

序号

函数名

功能描述

1

EXTI_DeInit

将外设 EXTI 寄存器重设为缺省值。

2

EXTI_Init

根据EXTI_InitStruct中指定参数初始化外设EXTI寄存器。

3

EXTI_StructInit

把 EXTI_InitStruct 中的每一个参数按缺省值填入。

4

EXTI_GenerateSWInterrupt

产生一个软件中断。

5

EXTI_GetFlagStatus

检查指定的 EXTI 线路标志位设置与否。

6

EXTI_ClearFlag

清除 EXTI 线路挂起标志位。

7

EXTI_GetITStatus

检查指定的 EXTI 线路触发请求发生与否。

8

EXTI_ClearITPendingBit

清除 EXTI 线路挂起位。

每一种寄存器详细的描述在这里不一一进行介绍,大家可以参考目录:“第1部分:开发板硬件资料”--->“2 - 芯片资料”中“STM32固件库使用手册的中文翻译版”对应的EXTI章节的库函数部分认真研读。

IO口外部中断配置过程

针对STM32F103的GPIO口为外部中断时,软件的配置过程如下:

图3:外部中断软件配置步骤

注:实验例程即是按照上述配置步骤调用相关库函数实现,后有详述。外部中断2实验注:本节对应的实验源码是:“实验5-1:EXTI2外部中断2”。工程需要用到的库文件

在使用库函数建“实验5-1:EXTI2外部中断2”工程时,需要用到的c文件如下表所示。

表5:实验需要用到的C文件

序号

文件名

后缀

功能描述

1

stm32f10x_rcc

.c

复位与时钟控制器。

2

stm32f10x_gpio

.c

通用输入输出。

3

stm32f10x_exti

.c

外部中断/事件。

4

misc

.c

中断向量控制器。

注:STM32 V3.5版本库函数中没有原来版本中单独对于NVIC的外设驱动,把NVIC的外设驱动放在了misc.c中,实际上是代替原来的stm32f10x_nvic.c文件。

可按下图所示在新建工程时添加需要的c文件。

图4:在新建工程中添加所需库函数c文件

表6:实验需要用到的H文件

序号

文件名

后缀

功能描述

1

stm32f10x_rcc

.h

复位与时钟控制器。

2

stm32f10x_gpio

.h

通用输入输出。

3

stm32f10x_exti

.h

外部中断/事件。

4

misc

.h

中断向量控制器。

注:所建工程必须添加上面h文件所在的目录:..\..\ Lib\ F10x_FWLIB\ inc。

在使用该实验工程时,需要用到的h文件如表5所示。需要在MDK中点击魔术棒,打开工程配置窗口,按照下图所示添加头文件包含路径。

图5:如何添加头文件包含路径

编写代码

首先编写初始化PE2为外部中断2,完成的操作有:将PE2配置为上拉输入,使能AFIO复用时钟功能,PE2口映射到EXTI2输入线上,EXTI2初始化,EXTI2的中断优先级配置。具体代码如下。

代码清单:初始化PE2为外部中断2

/******************************************************************************* * 描 述 : 初始化PE2为外部中断2 * 参 数 : 无 * 返回值 : 无 ******************************************************************************/ void EXTI_PE2_Init(void) { GPIO_InitTypeDef GPIO_InitStructure; EXTI_InitTypeDef EXTI_InitStructure; NVIC_InitTypeDef NVIC_InitStructure;

/* Enable the EXTI_PE2 Clock */ RCC_APB2PeriphClockCmd( RCC_APB2Periph_AFIO | RCC_APB2Periph_GPIOE , ENABLE);

GPIO_InitStructure.GPIO_Pin = GPIO_Pin_2; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU; //上拉输入 GPIO_Init(GPIOE, &GPIO_InitStructure);

GPIO_EXTILineConfig(GPIO_PortSourceGPIOE, GPIO_PinSource2); //配置PE2管脚为外部中断线路用

EXTI_InitStructure.EXTI_Line = EXTI_Line2; //配置为外部中断线2 EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt; //配置为中断请求 EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Falling; //输入线路下降沿为中断请求 EXTI_InitStructure.EXTI_LineCmd = ENABLE; //使能中断 EXTI_Init(&EXTI_InitStructure);

NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); //NVIC_Group:先占优先级2位,从优先级2位 NVIC_InitStructure.NVIC_IRQChannel = EXTI2_IRQn; //配置为外部中断2中断 NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1; //先占优先级为1 NVIC_InitStructure.NVIC_IRQChannelSubPriority = 2; //从优先级为2 NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //使能中断通道 NVIC_Init(&NVIC_InitStructure); }

然后,编写外部中断2的中断服务函数。代码如下。

代码清单:EXTI2中断服务函数

/************************************************************************************** * 描 述 : 外部中断服务程序 * 入 参 : 无 * 返回值 : 无 **************************************************************************************/ void EXTI2_IRQHandler(void) { led_on(LED_1); //点亮指示灯D1 sw_delay_ms(200); //延时200ms led_off(LED_1); //熄灭指示灯D1 sw_delay_ms(200); //延时200ms led_on(LED_1); //点亮指示灯D1 sw_delay_ms(200); //延时200ms led_off(LED_1); //熄灭指示灯D1 sw_delay_ms(200); //延时200ms

EXTI_ClearITPendingBit(EXTI_Line2); //清除EXTI中断线路挂起位 }注:进中断服务程序会执行用户指示灯D1闪烁2次的任务。

最后,主函数会调用相关函数实现对PE2的配置,在while循环中没有添加任务。如下图所示。

代码清单:主函数

int main(void) { //初始化用于驱动指示灯D1、D2、D3、D4的引脚 leds_init(); //初始化PE2为外部中断2 EXTI_PE2_Init(); //主循环 while(1) { ; //无任务,说明有用户指示灯闪烁来自于外部中断服务函数 } } 实验步骤解压“…\第3部分:标准库教程和实验源码\ 1 - 基础实验程序\”目录下的压缩文件“实验5-1: EXTI2外部中断2”,打开解压后的文件夹,将“EXTI2”文件夹拷贝到合适的目录,如“D\STM32F103ZET6”。启动MDK5.23。在MDK5中执行“Project→Open Project”打开“…\ EXTI2 \projec”目录下的工程“EXTI2.uvproj”。点击编译按钮编译工程。注意查看编译输出栏,观察编译的结果,如果有错误,修改程序,直到编译成功为止。编译后生成的HEX文件“EXTI2.hex”位于工程目录下的“Objects”文件夹中。点击下载按钮下载程序 。如果需要对程序进行仿真,点击Debug按钮,即可将程序下载到STM32F103ZET6中进行仿真。程序运行后,按下开发板按键S1,可观察到指示灯D1闪烁2次。思考题1:分析下按键S1是按下时D1闪烁还是松开时D1闪烁?为什么?外部中断3实验注:本节对应的实验源码是:“实验5-2: EXTI3外部中断3”。工程需要用到的库文件

在使用库函数建“实验5-2: EXTI3外部中断3”工程时,需要用到的c文件以及添加头文件包含路径的方法与介绍“实验5-1: EXTI2外部中断2”完全一样,在此不再赘述。

编写代码

首先编写初始化PE3为外部中断3,完成的操作有:将PE3配置为上拉输入,使能AFIO复用时钟功能,PE3口映射到EXTI3输入线上,EXTI3初始化,EXTI3的中断优先级配置。具体代码如下。

代码清单:初始化PE3为外部中断3

/******************************************************************************* * 描 述 : 初始化PE3为外部中断3 * 参 数 : 无 * 返回值 : 无 ******************************************************************************/ void EXTI_PE3_Init(void) { GPIO_InitTypeDef GPIO_InitStructure; EXTI_InitTypeDef EXTI_InitStructure; NVIC_InitTypeDef NVIC_InitStructure; /* Enable the EXTI_PE3 Clock */ RCC_APB2PeriphClockCmd( RCC_APB2Periph_AFIO | RCC_APB2Periph_GPIOE , ENABLE);

GPIO_InitStructure.GPIO_Pin = GPIO_Pin_3; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU; //上拉输入 GPIO_Init(GPIOE, &GPIO_InitStructure); GPIO_EXTILineConfig(GPIO_PortSourceGPIOE, GPIO_PinSource3); //配置PE3管脚为外部中断线路用

EXTI_InitStructure.EXTI_Line = EXTI_Line3; //配置为外部中断线3 EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt; //配置为中断请求 EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Rising; //输入线路上升沿为中断请求 EXTI_InitStructure.EXTI_LineCmd = ENABLE; //使能中断 EXTI_Init(&EXTI_InitStructure);

NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); //NVIC_Group:先占优先级2位,从优先级2位 NVIC_InitStructure.NVIC_IRQChannel = EXTI3_IRQn; //配置为外部中断3中断 NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1; //先占优先级为1 NVIC_InitStructure.NVIC_IRQChannelSubPriority = 2; //从优先级为2 NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //使能中断通道 NVIC_Init(&NVIC_InitStructure); }

然后,编写外部中断3的中断服务函数。代码如下。

代码清单:EXTI3中断服务函数

/************************************************************************************** * 描 述 : 外部中断服务程序 * 入 参 : 无 * 返回值 : 无 **************************************************************************************/ void EXTI3_IRQHandler(void) { led_on(LED_2); //点亮指示灯D2 sw_delay_ms(200); //延时200ms led_off(LED_2); //熄灭指示灯D2 sw_delay_ms(200); //延时200ms led_on(LED_2); //点亮指示灯D2 sw_delay_ms(200); //延时200ms led_off(LED_2); //熄灭指示灯D2 sw_delay_ms(200); //延时200ms

EXTI_ClearITPendingBit(EXTI_Line3); //清除EXTI中断线路挂起位 } 注:进中断服务程序会执行用户指示灯D2闪烁2次的任务。

最后,主函数会调用相关函数实现对PE3的配置,在while循环中没有添加任务。如下图所示。

代码清单:主函数

int main(void) { //初始化用于驱动指示灯D1、D2、D3、D4的引脚 leds_init(); //初始化PE3为外部中断3 EXTI_PE3_Init(); //主循环 while(1) { ; //无任务,说明有用户指示灯闪烁来自于外部中断服务函数 } } 实验步骤解压“…\第3部分:标准库教程和实验源码\ 1 - 基础实验程序\”目录下的压缩文件“实验5-2: EXTI3外部中断3”,打开解压后的文件夹,将“EXTI3”文件夹拷贝到合适的目录,如“D\STM32F103ZET6”。启动MDK5.23。在MDK5中执行“Project→Open Project”打开“…\ EXTI3 \projec”目录下的工程“EXTI3.uvproj”。点击编译按钮编译工程。注意查看编译输出栏,观察编译的结果,如果有错误,修改程序,直到编译成功为止。编译后生成的HEX文件“EXTI3.hex”位于工程目录下的“Objects”文件夹中。点击下载按钮下载程序 。如果需要对程序进行仿真,点击Debug按钮,即可将程序下载到STM32F103ZET6中进行仿真。程序运行后,按下开发板按键S2,可观察到指示灯D2闪烁2次。思考题1:分析下按键S2是按下时D2闪烁还是松开时D2闪烁?为什么?外部中断4实验注:本节对应的实验源码是:“实验5-3: EXTI4外部中断4”。工程需要用到的库文件

在使用库函数建“实验5-3: EXTI4外部中断4”工程时,需要用到的c文件以及添加头文件包含路径的方法与介绍“实验5-1: EXTI2外部中断2”完全一样,在此不再赘述。

编写代码

首先编写初始化PE4为外部中断4,完成的操作有:将PE4配置为上拉输入,使能AFIO复用时钟功能,PE4口映射到EXTI4输入线上,EXT4初始化,EXTI4的中断优先级配置。具体代码如下。

代码清单:初始化PE4为外部中断4

/******************************************************************************* * 描 述 : 初始化PE4为外部中断4 * 参 数 : 无 * 返回值 : 无 ******************************************************************************/ void EXTI_PE4_Init(void) { GPIO_InitTypeDef GPIO_InitStructure; EXTI_InitTypeDef EXTI_InitStructure; NVIC_InitTypeDef NVIC_InitStructure;

/* Enable the EXTI_PE4 Clock */ RCC_APB2PeriphClockCmd( RCC_APB2Periph_AFIO | RCC_APB2Periph_GPIOE , ENABLE);

GPIO_InitStructure.GPIO_Pin = GPIO_Pin_4; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU; //上拉输入 GPIO_Init(GPIOE, &GPIO_InitStructure);

GPIO_EXTILineConfig(GPIO_PortSourceGPIOE, GPIO_PinSource4); //配置PE4管脚为外部中断线路用

EXTI_InitStructure.EXTI_Line = EXTI_Line4; //配置为外部中断线4 EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt; //配置为中断请求 EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Rising_Falling; //输入线路上升沿或下降沿为中断请求 EXTI_InitStructure.EXTI_LineCmd = ENABLE; //使能中断 EXTI_Init(&EXTI_InitStructure);

NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); //NVIC_Group:先占优先级2位,从优先级2位 NVIC_InitStructure.NVIC_IRQChannel = EXTI4_IRQn; //配置为外部中断4中断 NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1; //先占优先级为1 NVIC_InitStructure.NVIC_IRQChannelSubPriority = 2; //从优先级为2 NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //使能中断通道 NVIC_Init(&NVIC_InitStructure); }

然后,编写外部中断4的中断服务函数。代码如下。

代码清单:EXTI4中断服务函数

/************************************************************************************** * 描 述 : 外部中断服务程序 * 入 参 : 无 * 返回值 : 无 **************************************************************************************/ void EXTI4_IRQHandler(void) { led_on(LED_3); //点亮指示灯D3 sw_delay_ms(200); //延时200ms led_off(LED_3); //熄灭指示灯D3 sw_delay_ms(200); //延时200ms led_on(LED_3); //点亮指示灯D3 sw_delay_ms(200); //延时200ms led_off(LED_3); //熄灭指示灯D3 sw_delay_ms(200); //延时200ms

EXTI_ClearITPendingBit(EXTI_Line4); //清除EXTI中断线路挂起位 } 注:进中断服务程序会执行用户指示灯D3闪烁2次的任务。

最后,主函数会调用相关函数实现对PE4的配置,在while循环中没有添加任务。如下图所示。

代码清单:主函数

int main(void) { //初始化用于驱动指示灯D1、D2、D3、D4的引脚 leds_init(); //初始化PE4为外部中断4 EXTI_PE4_Init(); //主循环 while(1) { ; //无任务,说明有用户指示灯闪烁来自于外部中断服务函数 } } 实验步骤解压“…\第3部分:标准库教程和实验源码\ 1 - 基础实验程序\”目录下的压缩文件“实验5-3: EXTI4外部中断4”,打开解压后的文件夹,将“EXTI4”文件夹拷贝到合适的目录,如“D\STM32F103ZET6”。启动MDK5.23。在MDK5中执行“Project→Open Project”打开“…\ EXTI4 \projec”目录下的工程“EXTI4.uvproj”。点击编译按钮编译工程。注意查看编译输出栏,观察编译的结果,如果有错误,修改程序,直到编译成功为止。编译后生成的HEX文件“EXTI4.hex”位于工程目录下的“Objects”文件夹中。点击下载按钮下载程序 。如果需要对程序进行仿真,点击Debug按钮,即可将程序下载到STM32F103ZET6中进行仿真。程序运行后,按下开发板按键S3不松开,可观察到指示灯D3闪烁2次。将按下的按键S3松开,可观察到指示灯D3又闪烁2次。思考题1:分析下按键S3按下和松开时D3为什么都会闪烁?外部中断5实验注:本节对应的实验源码是:“实验5-4: EXTI5外部中断5”。工程需要用到的库文件

在使用库函数建“实验5-4: EXTI5外部中断5”工程时,需要用到的c文件以及添加头文件包含路径的方法与介绍“实验5-1: EXTI2外部中断2”完全一样,在此不再赘述。

编写代码

首先编写初始化PE5为外部中断5,完成的操作有:将PE5配置为上拉输入,使能AFIO复用时钟功能,PE5口映射到EXTI5输入线上,EXTI5初始化,EXTI5的中断优先级配置。具体代码如下。

代码清单:初始化PE5为外部中断5

/******************************************************************************* * 描 述 : 初始化PE5为外部中断5 * 参 数 : 无 * 返回值 : 无 ******************************************************************************/ void EXTI_PE5_Init(void) { GPIO_InitTypeDef GPIO_InitStructure; EXTI_InitTypeDef EXTI_InitStructure; NVIC_InitTypeDef NVIC_InitStructure;

/* Enable the EXTI_PE5 Clock */ RCC_APB2PeriphClockCmd( RCC_APB2Periph_AFIO | RCC_APB2Periph_GPIOE , ENABLE);

GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU; //上拉输入 GPIO_Init(GPIOE, &GPIO_InitStructure);

GPIO_EXTILineConfig(GPIO_PortSourceGPIOE, GPIO_PinSource5); //配置PE5管脚为外部中断线路用

EXTI_InitStructure.EXTI_Line = EXTI_Line5; //配置为外部中断线5 EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt; //配置为中断请求 EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Falling; //输入线路下降沿为中断请求 EXTI_InitStructure.EXTI_LineCmd = ENABLE; //使能中断 EXTI_Init(&EXTI_InitStructure);

NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); //NVIC_Group:先占优先级2位,从优先级2位 NVIC_InitStructure.NVIC_IRQChannel = EXTI9_5_IRQn; //配置为外部中断5中断 NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1; //先占优先级为1 NVIC_InitStructure.NVIC_IRQChannelSubPriority = 2; //从优先级为2 NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //使能中断通道 NVIC_Init(&NVIC_InitStructure); }

然后,编写外部中断5的中断服务函数。代码如下。

代码清单:EXTI5中断服务函数

/************************************************************************************** * 描 述 : 外部中断服务程序 * 入 参 : 无 * 返回值 : 无 **************************************************************************************/ void EXTI9_5_IRQHandler(void) { led_on(LED_4); //点亮指示灯D4 sw_delay_ms(200); //延时200ms led_off(LED_4); //熄灭指示灯D4 sw_delay_ms(200); //延时200ms led_on(LED_4); //点亮指示灯D4 sw_delay_ms(200); //延时200ms led_off(LED_4); //熄灭指示灯D4 sw_delay_ms(200); //延时200ms

EXTI_ClearITPendingBit(EXTI_Line5); //清除EXTI中断线路挂起位 } 注:进中断服务程序会执行用户指示灯D4闪烁2次的任务。此中断服务函数名已与之前中断服务函数有所不同。

最后,主函数会调用相关函数实现对PE5的配置,在while循环中没有添加任务。如下图所示。

代码清单:主函数

int main(void) { //初始化用于驱动指示灯D1、D2、D3、D4的引脚 leds_init(); //初始化PE5为外部中断5 EXTI_PE5_Init(); //主循环 while(1) { ; //无任务,说明有用户指示灯闪烁来自于外部中断服务函数 } } 实验步骤解压“…\第3部分:标准库教程和实验源码\ 1 - 基础实验程序\”目录下的压缩文件“实验5-4: EXTI5外部中断5”,打开解压后的文件夹,将“EXTI5”文件夹拷贝到合适的目录,如“D\STM32F103ZET6”。启动MDK5.23。在MDK5中执行“Project→Open Project”打开“…\ EXTI5 \projec”目录下的工程“EXTI5.uvproj”。点击编译按钮编译工程。注意查看编译输出栏,观察编译的结果,如果有错误,修改程序,直到编译成功为止。编译后生成的HEX文件“EXTI5.hex”位于工程目录下的“Objects”文件夹中。点击下载按钮下载程序 。如果需要对程序进行仿真,点击Debug按钮,即可将程序下载到STM32F103ZET6中进行仿真。程序运行后,按下开发板按键S4,可观察到指示灯D4闪烁2次。思考题1:分析下按键S4是按下时D4闪烁还是松开时D4闪烁?为什么?多个外部中断实验注:本节对应的实验源码是:“实验5-5:多个EXTI外部中断”。工程需要用到的库文件

在使用库函数建“实验5-5:多个EXTI外部中断”工程时,需要用到的c文件以及添加头文件包含路径的方法与介绍“实验5-1: EXTI2外部中断2”完全一样,在此不再赘述。

编写代码

首先编写初始化PE2为外部中断2、PE3为外部中断3、PE4为外部中断4、PE5为外部中断5。具体代码如下。

代码清单:初始化PE2为外部中断2

/******************************************************************************* * 描 述 : 初始化PE2为外部中断2 * 参 数 : 无 * 返回值 : 无 ******************************************************************************/ void EXTI_PE2_Init(void) { GPIO_InitTypeDef GPIO_InitStructure; EXTI_InitTypeDef EXTI_InitStructure;

/* Enable the EXTI_PE2 Clock */ RCC_APB2PeriphClockCmd( RCC_APB2Periph_AFIO | RCC_APB2Periph_GPIOE , ENABLE);

GPIO_InitStructure.GPIO_Pin = GPIO_Pin_2; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU; //上拉输入 GPIO_Init(GPIOE, &GPIO_InitStructure);

GPIO_EXTILineConfig(GPIO_PortSourceGPIOE, GPIO_PinSource2); //配置PE2管脚为外部中断线路用

EXTI_InitStructure.EXTI_Line = EXTI_Line2; //配置为外部中断线2 EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt; //配置为中断请求 EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Falling; //输入线路下降沿为中断请求 EXTI_InitStructure.EXTI_LineCmd = ENABLE; //使能中断 EXTI_Init(&EXTI_InitStructure); }

代码清单:初始化PE3为外部中断3

/******************************************************************************* * 描 述 : 初始化PE3为外部中断3 * 参 数 : 无 * 返回值 : 无 ******************************************************************************/ void EXTI_PE3_Init(void) { GPIO_InitTypeDef GPIO_InitStructure; EXTI_InitTypeDef EXTI_InitStructure;

/* Enable the EXTI_PE3 Clock */ RCC_APB2PeriphClockCmd( RCC_APB2Periph_AFIO | RCC_APB2Periph_GPIOE , ENABLE);

GPIO_InitStructure.GPIO_Pin = GPIO_Pin_3; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU; //上拉输入 GPIO_Init(GPIOE, &GPIO_InitStructure);

GPIO_EXTILineConfig(GPIO_PortSourceGPIOE, GPIO_PinSource3); //配置PE3管脚为外部中断线路用

EXTI_InitStructure.EXTI_Line = EXTI_Line3; //配置为外部中断线3 EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt; //配置为中断请求 EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Falling; //输入线路下降沿为中断请求 EXTI_InitStructure.EXTI_LineCmd = ENABLE; //使能中断 EXTI_Init(&EXTI_InitStructure); }

代码清单:初始化PE4为外部中断4

/ /******************************************************************************* * 描 述 : 初始化PE4为外部中断4 * 参 数 : 无 * 返回值 : 无 ******************************************************************************/ void EXTI_PE4_Init(void) { GPIO_InitTypeDef GPIO_InitStructure; EXTI_InitTypeDef EXTI_InitStructure;

/* Enable the EXTI_PE4 Clock */ RCC_APB2PeriphClockCmd( RCC_APB2Periph_AFIO | RCC_APB2Periph_GPIOE , ENABLE);

GPIO_InitStructure.GPIO_Pin = GPIO_Pin_4; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU; //上拉输入 GPIO_Init(GPIOE, &GPIO_InitStructure);

GPIO_EXTILineConfig(GPIO_PortSourceGPIOE, GPIO_PinSource4); //配置PE4管脚为外部中断线路用

EXTI_InitStructure.EXTI_Line = EXTI_Line4; //配置为外部中断线4 EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt; //配置为中断请求 EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Falling; //输入线路下降沿为中断请求 EXTI_InitStructure.EXTI_LineCmd = ENABLE; //使能中断 EXTI_Init(&EXTI_InitStructure); }

代码清单:初始化PE5为外部中断5

/******************************************************************************* * 描 述 : 初始化PE5为外部中断5 * 参 数 : 无 * 返回值 : 无 ******************************************************************************/ void EXTI_PE5_Init(void) { GPIO_InitTypeDef GPIO_InitStructure; EXTI_InitTypeDef EXTI_InitStructure;

/* Enable the EXTI_PE5 Clock */ RCC_APB2PeriphClockCmd( RCC_APB2Periph_AFIO | RCC_APB2Periph_GPIOE , ENABLE);

GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU; //上拉输入 GPIO_Init(GPIOE, &GPIO_InitStructure);

GPIO_EXTILineConfig(GPIO_PortSourceGPIOE, GPIO_PinSource5); //配置PE5管脚为外部中断线路用

EXTI_InitStructure.EXTI_Line = EXTI_Line5; //配置为外部中断线5 EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt; //配置为中断请求 EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Falling; //输入线路下降沿为中断请求 EXTI_InitStructure.EXTI_LineCmd = ENABLE; //使能中断 EXTI_Init(&EXTI_InitStructure); }

然后,配置外部中断2、外部中断3、外部中断4、外部中断5的中断优先级。代码如下。

代码清单:配置EXTI2、EXTI3、EXTI4、EXTI5中断优先级

/******************************************************************************* * 描 述 : 配置嵌套向量中断控制器 * 参 数 : 无 * 返回值 : 无 ******************************************************************************/ void NVIC_Configuration(void) { NVIC_InitTypeDef NVIC_InitStructure;

NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); //NVIC_Group:先占优先级2位,从优先级2位

NVIC_InitStructure.NVIC_IRQChannel = EXTI2_IRQn; //配置为外部中断2中断 NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1; //先占优先级为1 NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1; //从优先级为1 NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //使能中断通道 NVIC_Init(&NVIC_InitStructure);

NVIC_InitStructure.NVIC_IRQChannel = EXTI3_IRQn; //配置为外部中断3中断 NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1; //先占优先级为1 NVIC_InitStructure.NVIC_IRQChannelSubPriority = 2; //从优先级为2 NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //使能中断通道 NVIC_Init(&NVIC_InitStructure);

NVIC_InitStructure.NVIC_IRQChannel = EXTI4_IRQn; //配置为外部中断4中断 NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 2; //先占优先级为2 NVIC_InitStructure.NVIC_IRQChannelSubPriority = 2; //从优先级为2 NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //使能中断通道 NVIC_Init(&NVIC_InitStructure);

NVIC_InitStructure.NVIC_IRQChannel = EXTI9_5_IRQn; //配置为外部中断5中断 NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 2; //先占优先级为2 NVIC_InitStructure.NVIC_IRQChannelSubPriority = 3; //从优先级为3 NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //使能中断通道 NVIC_Init(&NVIC_InitStructure); }

之后,编写外部中断3的中断服务函数、外部中断3的中断服务函数、外部中断4的中断服务函数、外部中断5的中断服务函数。代码如下。

代码清单:EXTI2中断服务函数

/************************************************************************************** * 描 述 : 外部中断服务程序 * 入 参 : 无 * 返回值 : 无 **************************************************************************************/ void EXTI2_IRQHandler(void) { led_on(LED_1); //点亮指示灯D1 sw_delay_ms(200); //延时200ms led_off(LED_1); //熄灭指示灯D1 sw_delay_ms(200); //延时200ms led_on(LED_1); //点亮指示灯D1 sw_delay_ms(200); //延时200ms led_off(LED_1); //熄灭指示灯D1 sw_delay_ms(200); //延时200ms

EXTI_ClearITPendingBit(EXTI_Line2); //清除EXTI中断线路挂起位 } 注:进中断服务程序会执行用户指示灯D1闪烁2次的任务。

代码清单:EXTI3中断服务函数

/************************************************************************************** * 描 述 : 外部中断服务程序 * 入 参 : 无 * 返回值 : 无 **************************************************************************************/ void EXTI3_IRQHandler(void) { led_on(LED_2); //点亮指示灯D2 sw_delay_ms(200); //延时200ms led_off(LED_2); //熄灭指示灯D2 sw_delay_ms(200); //延时200ms led_on(LED_2); //点亮指示灯D2 sw_delay_ms(200); //延时200ms led_off(LED_2); //熄灭指示灯D2 sw_delay_ms(200); //延时200ms

EXTI_ClearITPendingBit(EXTI_Line3); //清除EXTI中断线路挂起位 } 注:进中断服务程序会执行用户指示灯D2闪烁2次的任务。

代码清单:EXTI4中断服务函数

/************************************************************************************** * 描 述 : 外部中断服务程序 * 入 参 : 无 * 返回值 : 无 **************************************************************************************/ void EXTI4_IRQHandler(void) { led_on(LED_3); //点亮指示灯D3 sw_delay_ms(200); //延时200ms led_off(LED_3); //熄灭指示灯D3 sw_delay_ms(200); //延时200ms led_on(LED_3); //点亮指示灯D3 sw_delay_ms(200); //延时200ms led_off(LED_3); //熄灭指示灯D3 sw_delay_ms(200); //延时200ms

EXTI_ClearITPendingBit(EXTI_Line4); //清除EXTI中断线路挂起位 } 注:进中断服务程序会执行用户指示灯D3闪烁2次的任务。

代码清单:EXTI5中断服务函数

/************************************************************************************** * 描 述 : 外部中断服务程序 * 入 参 : 无 * 返回值 : 无 **************************************************************************************/ void EXTI9_5_IRQHandler(void) { led_on(LED_4); //点亮指示灯D4 sw_delay_ms(200); //延时200ms led_off(LED_4); //熄灭指示灯D4 sw_delay_ms(200); //延时200ms led_on(LED_4); //点亮指示灯D4 sw_delay_ms(200); //延时200ms led_off(LED_4); //熄灭指示灯D4 sw_delay_ms(200); //延时200ms

EXTI_ClearITPendingBit(EXTI_Line5); //清除EXTI中断线路挂起位 } 注:进中断服务程序会执行用户指示灯D4闪烁2次的任务。

最后,主函数会调用相关函数实现对PE2、PE3、PE4、PE5的配置,在while循环中没有添加任务。如下图所示。

代码清单:主函数

int main(void) { //初始化用于驱动指示灯D1、D2、D3、D4的引脚 leds_init(); //初始化PE2为外部中断2 EXTI_PE2_Init(); //初始化PE3为外部中断3 EXTI_PE3_Init(); //初始化PE4为外部中断4 EXTI_PE4_Init(); //初始化PE5为外部中断5 EXTI_PE5_Init(); //配置嵌套向量中断控制器 NVIC_Configuration(); //主循环 while(1) { ; //无任务,说明有用户指示灯闪烁来自于外部中断服务函数 } } 实验步骤解压“…\第3部分:标准库教程和实验源码\ 1 - 基础实验程序\”目录下的压缩文件“实验5-5: 多个EXTI外部中断”,打开解压后的文件夹,将“EXTIs”文件夹拷贝到合适的目录,如“D\STM32F103ZET6”。启动MDK5.23。在MDK5中执行“Project→Open Project”打开“…\ EXTIs \projec”目录下的工程“EXTIs.uvproj”。点击编译按钮编译工程。注意查看编译输出栏,观察编译的结果,如果有错误,修改程序,直到编译成功为止。编译后生成的HEX文件“EXTIs.hex”位于工程目录下的“Objects”文件夹中。点击下载按钮下载程序 。如果需要对程序进行仿真,点击Debug按钮,即可将程序下载到STM32F103ZET6中进行仿真。程序运行后,可按下述操作进行实验:

1)按下开发板按键S1,可观察到指示灯D1闪烁2次。

2)按下开发板按键S2,可观察到指示灯D2闪烁2次。

3)按下开发板按键S3,可观察到指示灯D3闪烁2次。

4)按下开发板按键S4,可观察到指示灯D4闪烁2次。

5)同时按下开发板按键S1、S2时,可观察到指示灯D1最先闪烁2次,然后是D2闪烁2次。

6)同时按下开发板按键S2、S3时,可观察到指示灯D2最先闪烁2次,然后是D3闪烁2次。

7)同时按下开发板按键S3、S4时,可观察到指示灯D3最先闪烁2次,然后是D4闪烁2次。

8)同时按下开发板按键S1、S2、S3、S4时,可观察到指示灯D1最先闪烁2次,然后依次是D2闪烁2次、D3闪烁2次、D4闪烁2次。

9)先按下开发板按键S2,可观察到指示灯D2开始闪烁,待D2完成1次闪烁后,立刻按下按键S1,可观察到D2接着完成第2次闪烁,待D2闪烁结束后,D1闪烁2次。

10)先按下开发板按键S3,可观察到指示灯D3开始闪烁,待D3完成1次闪烁后,立刻按下按键S1,可观察到D3不闪烁,而是D1闪烁2次,待D1闪烁结束后,D3接着完成第2次闪烁。

11)先按下开发板按键S3,可观察到指示灯D3开始闪烁,待D3完成1次闪烁后,立刻按下按键S4,可观察到D3接着完成第2次闪烁,待D3闪烁结束后,D4闪烁2次。

思考题1:分析下上述实验现象和中断优先级的关系?如果各外部中断的中断优先级进行其他配置,又会是怎样的实验现象?

标签: #外部中断程序框图