优秀的编程知识分享平台

网站首页 > 技术文章 正文

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

nanyue 2024-09-10 16:18:02 技术文章 5 ℃

外部中断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

  1. /*******************************************************************************
  2. * 描 述 : 初始化PE2为外部中断2
  3. * 参 数 : 无
  4. * 返回值 : 无
  5. ******************************************************************************/
  6. void EXTI_PE2_Init(void)
  7. {
  8. GPIO_InitTypeDef GPIO_InitStructure;
  9. EXTI_InitTypeDef EXTI_InitStructure;
  10. NVIC_InitTypeDef NVIC_InitStructure;

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

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

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

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

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

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

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

  1. /**************************************************************************************
  2. * 描 述 : 外部中断服务程序
  3. * 入 参 : 无
  4. * 返回值 : 无
  5. **************************************************************************************/
  6. void EXTI2_IRQHandler(void)
  7. {
  8. led_on(LED_1); //点亮指示灯D1
  9. sw_delay_ms(200); //延时200ms
  10. led_off(LED_1); //熄灭指示灯D1
  11. sw_delay_ms(200); //延时200ms
  12. led_on(LED_1); //点亮指示灯D1
  13. sw_delay_ms(200); //延时200ms
  14. led_off(LED_1); //熄灭指示灯D1
  15. sw_delay_ms(200); //延时200ms

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

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

代码清单:主函数

  1. int main(void)
  2. {
  3. //初始化用于驱动指示灯D1、D2、D3、D4的引脚
  4. leds_init();
  5. //初始化PE2为外部中断2
  6. EXTI_PE2_Init();
  7. //主循环
  8. while(1)
  9. {
  10. ; //无任务,说明有用户指示灯闪烁来自于外部中断服务函数
  11. }
  12. }

实验步骤

  1. 解压“…\第3部分:标准库教程和实验源码\ 1 - 基础实验程序\”目录下的压缩文件“实验5-1: EXTI2外部中断2”,打开解压后的文件夹,将“EXTI2”文件夹拷贝到合适的目录,如“D\STM32F103ZET6”。
  2. 启动MDK5.23。
  3. 在MDK5中执行“Project→Open Project”打开“…\ EXTI2 \projec”目录下的工程“EXTI2.uvproj”。
  4. 点击编译按钮编译工程。注意查看编译输出栏,观察编译的结果,如果有错误,修改程序,直到编译成功为止。编译后生成的HEX文件“EXTI2.hex”位于工程目录下的“Objects”文件夹中。
  5. 点击下载按钮下载程序 。如果需要对程序进行仿真,点击Debug按钮,即可将程序下载到STM32F103ZET6中进行仿真。
  6. 程序运行后,按下开发板按键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

  1. /*******************************************************************************
  2. * 描 述 : 初始化PE3为外部中断3
  3. * 参 数 : 无
  4. * 返回值 : 无
  5. ******************************************************************************/
  6. void EXTI_PE3_Init(void)
  7. {
  8. GPIO_InitTypeDef GPIO_InitStructure;
  9. EXTI_InitTypeDef EXTI_InitStructure;
  10. NVIC_InitTypeDef NVIC_InitStructure;
  11. /* Enable the EXTI_PE3 Clock */
  12. RCC_APB2PeriphClockCmd( RCC_APB2Periph_AFIO | RCC_APB2Periph_GPIOE , ENABLE);

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

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

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

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

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

  1. /**************************************************************************************
  2. * 描 述 : 外部中断服务程序
  3. * 入 参 : 无
  4. * 返回值 : 无
  5. **************************************************************************************/
  6. void EXTI3_IRQHandler(void)
  7. {
  8. led_on(LED_2); //点亮指示灯D2
  9. sw_delay_ms(200); //延时200ms
  10. led_off(LED_2); //熄灭指示灯D2
  11. sw_delay_ms(200); //延时200ms
  12. led_on(LED_2); //点亮指示灯D2
  13. sw_delay_ms(200); //延时200ms
  14. led_off(LED_2); //熄灭指示灯D2
  15. sw_delay_ms(200); //延时200ms

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

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

代码清单:主函数

  1. int main(void)
  2. {
  3. //初始化用于驱动指示灯D1、D2、D3、D4的引脚
  4. leds_init();
  5. //初始化PE3为外部中断3
  6. EXTI_PE3_Init();
  7. //主循环
  8. while(1)
  9. {
  10. ; //无任务,说明有用户指示灯闪烁来自于外部中断服务函数
  11. }
  12. }

实验步骤

  1. 解压“…\第3部分:标准库教程和实验源码\ 1 - 基础实验程序\”目录下的压缩文件“实验5-2: EXTI3外部中断3”,打开解压后的文件夹,将“EXTI3”文件夹拷贝到合适的目录,如“D\STM32F103ZET6”。
  2. 启动MDK5.23。
  3. 在MDK5中执行“Project→Open Project”打开“…\ EXTI3 \projec”目录下的工程“EXTI3.uvproj”。
  4. 点击编译按钮编译工程。注意查看编译输出栏,观察编译的结果,如果有错误,修改程序,直到编译成功为止。编译后生成的HEX文件“EXTI3.hex”位于工程目录下的“Objects”文件夹中。
  5. 点击下载按钮下载程序 。如果需要对程序进行仿真,点击Debug按钮,即可将程序下载到STM32F103ZET6中进行仿真。
  6. 程序运行后,按下开发板按键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

  1. /*******************************************************************************
  2. * 描 述 : 初始化PE4为外部中断4
  3. * 参 数 : 无
  4. * 返回值 : 无
  5. ******************************************************************************/
  6. void EXTI_PE4_Init(void)
  7. {
  8. GPIO_InitTypeDef GPIO_InitStructure;
  9. EXTI_InitTypeDef EXTI_InitStructure;
  10. NVIC_InitTypeDef NVIC_InitStructure;

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

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

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

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

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

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

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

  1. /**************************************************************************************
  2. * 描 述 : 外部中断服务程序
  3. * 入 参 : 无
  4. * 返回值 : 无
  5. **************************************************************************************/
  6. void EXTI4_IRQHandler(void)
  7. {
  8. led_on(LED_3); //点亮指示灯D3
  9. sw_delay_ms(200); //延时200ms
  10. led_off(LED_3); //熄灭指示灯D3
  11. sw_delay_ms(200); //延时200ms
  12. led_on(LED_3); //点亮指示灯D3
  13. sw_delay_ms(200); //延时200ms
  14. led_off(LED_3); //熄灭指示灯D3
  15. sw_delay_ms(200); //延时200ms

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

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

代码清单:主函数

  1. int main(void)
  2. {
  3. //初始化用于驱动指示灯D1、D2、D3、D4的引脚
  4. leds_init();
  5. //初始化PE4为外部中断4
  6. EXTI_PE4_Init();
  7. //主循环
  8. while(1)
  9. {
  10. ; //无任务,说明有用户指示灯闪烁来自于外部中断服务函数
  11. }
  12. }

实验步骤

  1. 解压“…\第3部分:标准库教程和实验源码\ 1 - 基础实验程序\”目录下的压缩文件“实验5-3: EXTI4外部中断4”,打开解压后的文件夹,将“EXTI4”文件夹拷贝到合适的目录,如“D\STM32F103ZET6”。
  2. 启动MDK5.23。
  3. 在MDK5中执行“Project→Open Project”打开“…\ EXTI4 \projec”目录下的工程“EXTI4.uvproj”。
  4. 点击编译按钮编译工程。注意查看编译输出栏,观察编译的结果,如果有错误,修改程序,直到编译成功为止。编译后生成的HEX文件“EXTI4.hex”位于工程目录下的“Objects”文件夹中。
  5. 点击下载按钮下载程序 。如果需要对程序进行仿真,点击Debug按钮,即可将程序下载到STM32F103ZET6中进行仿真。
  6. 程序运行后,按下开发板按键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

  1. /*******************************************************************************
  2. * 描 述 : 初始化PE5为外部中断5
  3. * 参 数 : 无
  4. * 返回值 : 无
  5. ******************************************************************************/
  6. void EXTI_PE5_Init(void)
  7. {
  8. GPIO_InitTypeDef GPIO_InitStructure;
  9. EXTI_InitTypeDef EXTI_InitStructure;
  10. NVIC_InitTypeDef NVIC_InitStructure;

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

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

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

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

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

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

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

  1. /**************************************************************************************
  2. * 描 述 : 外部中断服务程序
  3. * 入 参 : 无
  4. * 返回值 : 无
  5. **************************************************************************************/
  6. void EXTI9_5_IRQHandler(void)
  7. {
  8. led_on(LED_4); //点亮指示灯D4
  9. sw_delay_ms(200); //延时200ms
  10. led_off(LED_4); //熄灭指示灯D4
  11. sw_delay_ms(200); //延时200ms
  12. led_on(LED_4); //点亮指示灯D4
  13. sw_delay_ms(200); //延时200ms
  14. led_off(LED_4); //熄灭指示灯D4
  15. sw_delay_ms(200); //延时200ms

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

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

代码清单:主函数

  1. int main(void)
  2. {
  3. //初始化用于驱动指示灯D1、D2、D3、D4的引脚
  4. leds_init();
  5. //初始化PE5为外部中断5
  6. EXTI_PE5_Init();
  7. //主循环
  8. while(1)
  9. {
  10. ; //无任务,说明有用户指示灯闪烁来自于外部中断服务函数
  11. }
  12. }

实验步骤

  1. 解压“…\第3部分:标准库教程和实验源码\ 1 - 基础实验程序\”目录下的压缩文件“实验5-4: EXTI5外部中断5”,打开解压后的文件夹,将“EXTI5”文件夹拷贝到合适的目录,如“D\STM32F103ZET6”。
  2. 启动MDK5.23。
  3. 在MDK5中执行“Project→Open Project”打开“…\ EXTI5 \projec”目录下的工程“EXTI5.uvproj”。
  4. 点击编译按钮编译工程。注意查看编译输出栏,观察编译的结果,如果有错误,修改程序,直到编译成功为止。编译后生成的HEX文件“EXTI5.hex”位于工程目录下的“Objects”文件夹中。
  5. 点击下载按钮下载程序 。如果需要对程序进行仿真,点击Debug按钮,即可将程序下载到STM32F103ZET6中进行仿真。
  6. 程序运行后,按下开发板按键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

  1. /*******************************************************************************
  2. * 描 述 : 初始化PE2为外部中断2
  3. * 参 数 : 无
  4. * 返回值 : 无
  5. ******************************************************************************/
  6. void EXTI_PE2_Init(void)
  7. {
  8. GPIO_InitTypeDef GPIO_InitStructure;
  9. EXTI_InitTypeDef EXTI_InitStructure;

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

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

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

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

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

  1. /*******************************************************************************
  2. * 描 述 : 初始化PE3为外部中断3
  3. * 参 数 : 无
  4. * 返回值 : 无
  5. ******************************************************************************/
  6. void EXTI_PE3_Init(void)
  7. {
  8. GPIO_InitTypeDef GPIO_InitStructure;
  9. EXTI_InitTypeDef EXTI_InitStructure;

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

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

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

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


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

  1. / /*******************************************************************************
  2. * 描 述 : 初始化PE4为外部中断4
  3. * 参 数 : 无
  4. * 返回值 : 无
  5. ******************************************************************************/
  6. void EXTI_PE4_Init(void)
  7. {
  8. GPIO_InitTypeDef GPIO_InitStructure;
  9. EXTI_InitTypeDef EXTI_InitStructure;

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

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

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

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

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

  1. /*******************************************************************************
  2. * 描 述 : 初始化PE5为外部中断5
  3. * 参 数 : 无
  4. * 返回值 : 无
  5. ******************************************************************************/
  6. void EXTI_PE5_Init(void)
  7. {
  8. GPIO_InitTypeDef GPIO_InitStructure;
  9. EXTI_InitTypeDef EXTI_InitStructure;

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

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

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

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


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

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

  1. /*******************************************************************************
  2. * 描 述 : 配置嵌套向量中断控制器
  3. * 参 数 : 无
  4. * 返回值 : 无
  5. ******************************************************************************/
  6. void NVIC_Configuration(void)
  7. {
  8. NVIC_InitTypeDef NVIC_InitStructure;

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

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

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

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

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

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

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

  1. /**************************************************************************************
  2. * 描 述 : 外部中断服务程序
  3. * 入 参 : 无
  4. * 返回值 : 无
  5. **************************************************************************************/
  6. void EXTI2_IRQHandler(void)
  7. {
  8. led_on(LED_1); //点亮指示灯D1
  9. sw_delay_ms(200); //延时200ms
  10. led_off(LED_1); //熄灭指示灯D1
  11. sw_delay_ms(200); //延时200ms
  12. led_on(LED_1); //点亮指示灯D1
  13. sw_delay_ms(200); //延时200ms
  14. led_off(LED_1); //熄灭指示灯D1
  15. sw_delay_ms(200); //延时200ms

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

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

  1. /**************************************************************************************
  2. * 描 述 : 外部中断服务程序
  3. * 入 参 : 无
  4. * 返回值 : 无
  5. **************************************************************************************/
  6. void EXTI3_IRQHandler(void)
  7. {
  8. led_on(LED_2); //点亮指示灯D2
  9. sw_delay_ms(200); //延时200ms
  10. led_off(LED_2); //熄灭指示灯D2
  11. sw_delay_ms(200); //延时200ms
  12. led_on(LED_2); //点亮指示灯D2
  13. sw_delay_ms(200); //延时200ms
  14. led_off(LED_2); //熄灭指示灯D2
  15. sw_delay_ms(200); //延时200ms

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

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

  1. /**************************************************************************************
  2. * 描 述 : 外部中断服务程序
  3. * 入 参 : 无
  4. * 返回值 : 无
  5. **************************************************************************************/
  6. void EXTI4_IRQHandler(void)
  7. {
  8. led_on(LED_3); //点亮指示灯D3
  9. sw_delay_ms(200); //延时200ms
  10. led_off(LED_3); //熄灭指示灯D3
  11. sw_delay_ms(200); //延时200ms
  12. led_on(LED_3); //点亮指示灯D3
  13. sw_delay_ms(200); //延时200ms
  14. led_off(LED_3); //熄灭指示灯D3
  15. sw_delay_ms(200); //延时200ms

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

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

  1. /**************************************************************************************
  2. * 描 述 : 外部中断服务程序
  3. * 入 参 : 无
  4. * 返回值 : 无
  5. **************************************************************************************/
  6. void EXTI9_5_IRQHandler(void)
  7. {
  8. led_on(LED_4); //点亮指示灯D4
  9. sw_delay_ms(200); //延时200ms
  10. led_off(LED_4); //熄灭指示灯D4
  11. sw_delay_ms(200); //延时200ms
  12. led_on(LED_4); //点亮指示灯D4
  13. sw_delay_ms(200); //延时200ms
  14. led_off(LED_4); //熄灭指示灯D4
  15. sw_delay_ms(200); //延时200ms

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

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

代码清单:主函数

  1. int main(void)
  2. {
  3. //初始化用于驱动指示灯D1、D2、D3、D4的引脚
  4. leds_init();
  5. //初始化PE2为外部中断2
  6. EXTI_PE2_Init();
  7. //初始化PE3为外部中断3
  8. EXTI_PE3_Init();
  9. //初始化PE4为外部中断4
  10. EXTI_PE4_Init();
  11. //初始化PE5为外部中断5
  12. EXTI_PE5_Init();
  13. //配置嵌套向量中断控制器
  14. NVIC_Configuration();
  15. //主循环
  16. while(1)
  17. {
  18. ; //无任务,说明有用户指示灯闪烁来自于外部中断服务函数
  19. }
  20. }

实验步骤

  1. 解压“…\第3部分:标准库教程和实验源码\ 1 - 基础实验程序\”目录下的压缩文件“实验5-5: 多个EXTI外部中断”,打开解压后的文件夹,将“EXTIs”文件夹拷贝到合适的目录,如“D\STM32F103ZET6”。
  2. 启动MDK5.23。
  3. 在MDK5中执行“Project→Open Project”打开“…\ EXTIs \projec”目录下的工程“EXTIs.uvproj”。
  4. 点击编译按钮编译工程。注意查看编译输出栏,观察编译的结果,如果有错误,修改程序,直到编译成功为止。编译后生成的HEX文件“EXTIs.hex”位于工程目录下的“Objects”文件夹中。
  5. 点击下载按钮下载程序 。如果需要对程序进行仿真,点击Debug按钮,即可将程序下载到STM32F103ZET6中进行仿真。
  6. 程序运行后,可按下述操作进行实验:

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:分析下上述实验现象和中断优先级的关系?如果各外部中断的中断优先级进行其他配置,又会是怎样的实验现象?

Tags:

最近发表
标签列表