【正点原子STM32连载】第十一章 外部中断实验 摘自【正点原子】APM32F407最小系统板使用指南

1)实验平台:正点原子stm32f103战舰开发板V4
2)平台购买地址:https://detail.tmall.com/item.htm?id=609294757420
3)全套实验源码+手册+视频下载地址: http://www.openedv.com/thread-340252-1-1.html#

第十二章 外部中断实验

前两章介绍了GPIO在输出模式和输入模式下最基本的的操作,本章将介绍如何将GPIO引脚作为外部中断输入来使用。通过本章的学习,读者将学习到GPIO作为外部中断输入的使用。
本章分为如下几个小节:
12.1 硬件设计
12.2 程序设计
12.3 下载验证

12.1 硬件设计
12.1.1 例程功能

  1. 按下KEY0按键可控制LED0状态翻转,按下KEY_UP按键可控制LED1翻转。
    12.1.2 硬件资源
  2. LED
    LED0 - PF9
    LED1 - PF10
  3. 按键
    KEY0 - PE4
    KEY_UP - PA0
    12.1.3 原理图
    本章实验使用的两个APM32F407最小系统板板载按键,分别为KEY0按键和KEY_UP按键,其于板载MCU的连接原理图,如下图所示:
    B站UP查找第十二章 外部中断实验

前两章介绍了GPIO在输出模式和输入模式下最基本的的操作,本章将介绍如何将GPIO引脚作为外部中断输入来使用。通过本章的学习,读者将学习到GPIO作为外部中断输入的使用。
本章分为如下几个小节:
12.1 硬件设计
12.2 程序设计
12.3 下载验证

12.1 硬件设计
12.1.1 例程功能

  1. 按下KEY0按键可控制LED0状态翻转,按下KEY_UP按键可控制LED1翻转。
    12.1.2 硬件资源
  2. LED
    LED0 - PF9
    LED1 - PF10
  3. 按键
    KEY0 - PE4
    KEY_UP - PA0
    12.1.3 原理图
    本章实验使用的两个APM32F407最小系统板板载按键,分别为KEY0按键和KEY_UP按键,其于板载MCU的连接原理图,如下图所示:

图12.1.3.1 按键与MCU的连接原理图
从上面的原理图中可以看出,KEY0按键和KEY_UP按键的一端连接到了电源正极,而另一端分别与MCU的PE4引脚和PA0引脚相连接,并且在上一小节的介绍中,也说明了对于该硬件设计,PE4引脚和PA0引脚应当配置为下拉,这样一来,在按键被按下的时候,PE4引脚或PA0引脚就会从原来的低电平状态变为高电平状态,在这期间就会有一个上升沿的跳变,因此可以使用该上升沿信号作为中断的触发源。
12.2 程序设计
12.2.1 Geehy标准库的GPIO驱动
针对本章的实验要求,仅需将对应的GPIO引脚配置为输入下拉即可,请参考上一章节中的内容。
12.2.2 Geehy标准库的SYSCFG驱动
SYSCFG(系统配置控制器)可以用于将GPIO引脚配置为其对应EINT中断线外部中断源,例如将PE4引脚配置为EINT4的外部中断源或将PA0配置为EINT0的外部中断源等,具体的步骤如下:
①:配置GPIO引脚作为EINT的外部中断源
在Geehy标准库中对应的驱动函数如下:
①:配置GPIO引脚作为EINT的外部中断源
该函数用于配置GPIO引脚作为其对应EINT中断线的外部中断源,其函数原型如下所示:
void SYSCFG_ConfigEINTLine(SYSCFG_PORT_T port, SYSCFG_PIN_T pin);
该函数的形参描述,如下表所示:
形参 描述
port SYSCFG中的GPIO端口枚举
例如:SYSCFG_PORT_GPIOA、SYSCFG_PORT_GPIOB等(在apm32f4xx_syscfg.h文件中有定义)
pin SYSCFG中的GPIO引脚枚举
例如:SYSCFG_PIN_0、SYSCFG_PIN_1等(在apm32f4xx_syscfg.h文件中有定义)
表12.2.2.1 函数SYSCFG_ConfigEINTLine()形参描述
该函数的返回值描述,如下表所示:
返回值 描述
无 无
表12.2.2.2 函数SYSCFG_ConfigEINTLint()返回值描述
该函数的使用示例,如下所示:
#include “apm32f4xx.h”
#include “apm32f4xx_syscfg.h”

void example_fun(void)
{
/* 配置PA0引脚作为EINT0的外部中断源 /
SYSCFG_ConfigEINTLine(SYSCFG_PORT_GPIOA, SYSCFG_PIN_0);
}
12.2.3 Geehy标准库的EINT驱动
EINT(外部中断/事件控制器)可以用于管理从GPIO引脚等I/O引脚输入的信号引起的中断或事件,即外部中断/事件。使用时,具体的步骤如下:
①:配置EINT线的模式、触发条件和是否使能等配置项
②:使能EINT线中断,并配置其相关的中断优先级
在Geehy标准库中对应的驱动函数如下:
①:配置EINT
该函数用于配置EINT的各项参数,其函数原型如下所示:
void EINT_Config(EINT_Config_T
eintConfig);
该函数的形参描述,如下表所示:
形参 描述
eintConfig 指向EINT配置结构体的指针
需自行定义,并根据EINT的配置参数填充结构中的成员变量
表12.2.3.1 函数EINT_Config()形参描述
该函数的返回值描述,如下所示:
返回值 描述
无 无
表12.2.3.2 函数EINT_Config()返回值描述
该函数使用EINT_Config_T类型的结构体变量传入EINT的配置参数,该结构体的定义如下所示:
typedef enum
{
EINT_LINENONE = 0x00000, /* 不选择外部中断线 /
EINT_LINE_0 = 0x00001, /
外部中断线0 /
EINT_LINE_1 = 0x00002, /
外部中断线1 /
EINT_LINE_2 = 0x00004, /
外部中断线2 /
EINT_LINE_3 = 0x00008, /
外部中断线3 /
EINT_LINE_4 = 0x00010, /
外部中断线4 /
EINT_LINE_5 = 0x00020, /
外部中断线5 /
EINT_LINE_6 = 0x00040, /
外部中断线6 /
EINT_LINE_7 = 0x00080, /
外部中断线7 /
EINT_LINE_8 = 0x00100, /
外部中断线8 /
EINT_LINE_9 = 0x00200, /
外部中断线9 /
EINT_LINE_10 = 0x00400, /
外部中断线10 /
EINT_LINE_11 = 0x00800, /
外部中断线11 /
EINT_LINE_12 = 0x01000, /
外部中断线12 /
EINT_LINE_13 = 0x02000, /
外部中断线13 /
EINT_LINE_14 = 0x04000, /
外部中断线14 /
EINT_LINE_15 = 0x08000, /
外部中断线15 /
EINT_LINE_16 = 0x10000, /
外部中断线16 /
EINT_LINE_17 = 0x20000, /
外部中断线17 /
EINT_LINE_18 = 0x40000, /
外部中断线18 /
EINT_LINE_19 = 0x80000, /
外部中断线19 /
EINT_LINE_20 = 0x00100000, /
外部中断线20 /
EINT_LINE_21 = 0x00200000, /
外部中断线21 /
EINT_LINE_22 = 0x00400000, /
外部中断线22 */
} EINT_LINE_T;

typedef enum
{
EINT_MODE_INTERRUPT = 0x00, /* 中断请求模式 /
EINT_MODE_EVENT = 0x04 /
事件请求模式 */
} EINT_MODE_T;

typedef enum
{
EINT_TRIGGER_RISING = 0x08, /* 上升沿触发 /
EINT_TRIGGER_FALLING = 0x0C, /
下降沿触发 /
EINT_TRIGGER_RISING_FALLING = 0x10 /
上边沿触发 */
} EINT_TRIGGER_T;

typedef struct
{
EINT_LINE_T line; /* 外部中断线 /
EINT_MODE_T mode; /
模式 /
EINT_TRIGGER_T trigger; /
触发模式 /
uint8_t lineCmd; /
使能或禁止 */
} EINT_Config_T;
该函数的使用示例,如下所示:
#include “apm32f4xx.h”
#include “apm32f4xx_eint.h”

void example_fun(void)
{
EINT_Config_T eint_init_struct;

/* 配置使能EINT0的上升沿信号触发中断 */
eint_init_struct.line		= EINT_LINE_0;
eint_init_struct.mode		= EINT_MODE_INTERRUPT;
eint_init_struct.trigger	= EINT_TRIGGER_RISING;
eint_init_struct.lineCmd	= ENABLE;
EINT_Config(&eint_init_struct);

}
②:配置EINT线中断
该函数用于使能NVIC(嵌套向量中断控制器)的请求,因此不仅能用于配置EINT的中断,还能配置其他外设等的中断,其函数原型如下所示:
void NVIC_EnableIRQRequest( IRQn_Type irq,
uint8_t preemptionPriority,
uint8_t subPriority);
该函数的形参描述,如下表所示:
形参 描述
irq NVIC中断请求
例如:EINT0_IRQn、USART1_IRQn等(在apm32f4xx.h文件中有定义)
preemptionPriority 抢占优先级
subPriority 子优先级
表12.2.3.3 函数NVIC_EnableIRQRequest()形参描述
该函数的返回值描述,如下表所示:
返回值 描述
无 无
表12.2.3.4 函数NVIC_EnableIRQRequest()返回值描述
该函数的使用示例,如下所示:
#include “apm32f4xx.h”

void example_fun(void)
{
/* 使能EINT0中断,并设置其抢占优先级为2,子优先级为0 */
NVIC_EnableIRQRequest(EINT0_IRQn, 2, 0);
}

/* 使能了中断,就可以实现其对应的中断回调函数 /
void EINT0_IRQHandler(void)
{
/
Do something. */
}
12.2.4 外部中断驱动
本实验的外部中断驱动主要就是配置GPIO引脚作为EINT的外部中断源,并在其对应的中断回调函数中处理按键被按下后执行的操作。
外部中断驱动中,对GPIO引脚、SYSCFG、EINT的相关定义,如下所示:
#define KEY0_INT_GPIO_PORT GPIOE
#define KEY0_INT_GPIO_PIN GPIO_PIN_4
#define KEY0_INT_GPIO_CLK_ENABLE()
do {
RCM_EnableAHB1PeriphClock(RCM_AHB1_PERIPH_GPIOE);
} while (0)
#define KEY0_INT_SYSCFG_PORT SYSCFG_PORT_GPIOE
#define KEY0_INT_SYSCFG_PIN SYSCFG_PIN_4
#define KEY0_INT_EINT_LINE EINT_LINE_4
#define KEY0_INT_IRQn EINT4_IRQn
#define KEY0_INT_IRQHandler EINT4_IRQHandler

#define WKUP_INT_GPIO_PORT GPIOA
#define WKUP_INT_GPIO_PIN GPIO_PIN_0
#define WKUP_INT_GPIO_CLK_ENABLE()
do {
RCM_EnableAHB1PeriphClock(RCM_AHB1_PERIPH_GPIOA);
} while (0)
#define WKUP_INT_SYSCFG_PORT SYSCFG_PORT_GPIOA
#define WKUP_INT_SYSCFG_PIN SYSCFG_PIN_0
#define WKUP_INT_EINT_LINE EINT_LINE_0
#define WKUP_INT_IRQn EINT0_IRQn
#define WKUP_INT_IRQHandler EINT0_IRQHandler
外部中断驱动中,外部中断的初始化函数,如下所示:
/**

  • @brief 初始化外部中断

  • @param 无

  • @retval 无
    */
    void eint_init(void)
    {
    GPIO_Config_T gpio_init_struct;
    EINT_Config_T eint_init_struct;

    /* 使能时钟 */
    KEY0_INT_GPIO_CLK_ENABLE();
    WKUP_INT_GPIO_CLK_ENABLE();
    RCM_EnableAPB2PeriphClock(RCM_APB2_PERIPH_SYSCFG);

    /* 配置KEY0引脚和外部中断 /
    gpio_init_struct.pin = KEY0_INT_GPIO_PIN; /
    KEY0引脚 /
    gpio_init_struct.mode = GPIO_MODE_IN; /
    输入模式 /
    gpio_init_struct.pupd = GPIO_PUPD_DOWN; /
    下拉 /
    GPIO_Config(KEY0_INT_GPIO_PORT, &gpio_init_struct); /
    配置KEY0引脚 /
    /
    配置引脚作为事件线 /
    SYSCFG_ConfigEINTLine(KEY0_INT_SYSCFG_PORT, KEY0_INT_SYSCFG_PIN);
    eint_init_struct.line = KEY0_INT_EINT_LINE; /
    EINT线 /
    eint_init_struct.mode = EINT_MODE_INTERRUPT; /
    中断模式 /
    eint_init_struct.trigger = EINT_TRIGGER_RISING; /
    上升沿 /
    eint_init_struct.lineCmd = ENABLE; /
    使能 /
    EINT_Config(&eint_init_struct); /
    配置EINT /
    NVIC_EnableIRQRequest(KEY0_INT_IRQn, 2, 0); /
    使能中断 */

    /* 配置KEY_UP引脚和外部中断 /
    gpio_init_struct.pin = WKUP_INT_GPIO_PIN; /
    KEY_UP引脚 /
    gpio_init_struct.mode = GPIO_MODE_IN; /
    输入模式 /
    gpio_init_struct.pupd = GPIO_PUPD_DOWN; /
    下拉 /
    GPIO_Config(WKUP_INT_GPIO_PORT, &gpio_init_struct); /
    配置KEY_UP引脚 /
    /
    配置GPIO作为EINT线 /
    SYSCFG_ConfigEINTLine(WKUP_INT_SYSCFG_PORT, WKUP_INT_SYSCFG_PIN);
    eint_init_struct.line = WKUP_INT_EINT_LINE; /
    EINT线 /
    eint_init_struct.mode = EINT_MODE_INTERRUPT; /
    中断模式 /
    eint_init_struct.trigger = EINT_TRIGGER_RISING; /
    上升沿 /
    eint_init_struct.lineCmd = ENABLE; /
    使能 /
    EINT_Config(&eint_init_struct); /
    配置EINT /
    NVIC_EnableIRQRequest(WKUP_INT_IRQn, 2, 0); /
    使能中断 /
    }
    在外部中断的初始化函数中,除了使能按键对应GPIO端口的时钟,还使能了SYSCFG的时钟,并配置了GPIO、SYSCFG和EINT。
    在外部中断的初始化函数中,开启了两个EINT的中断,因此需要编写这两个EINT中断对应的中断回调函数,如下所示:
    /
    *

  • @brief KEY0按键外部中断服务函数

  • @param 无

  • @retval 无
    /
    void KEY0_INT_IRQHandler(void)
    {
    /
    消抖,

    • 此处为了方便使用了延时函数,
    • 实际代码中应禁止在中断中使用阻塞延时函数 */
      delay_ms(20);

    if (EINT_ReadIntFlag(KEY0_INT_EINT_LINE))
    {
    LED0_TOGGLE(); /* LED0状态翻转 /
    EINT_ClearIntFlag(KEY0_INT_EINT_LINE); /
    清除中断标志 */
    }
    }

/**

  • @brief KEY_UP按键外部中断服务函数

  • @param 无

  • @retval 无
    /
    void WKUP_INT_IRQHandler(void)
    {
    /
    消抖,

    • 此处为了方便使用了延时函数,
    • 实际代码中应禁止在中断中使用阻塞延时函数 */
      delay_ms(20);

    if (EINT_ReadIntFlag(WKUP_INT_EINT_LINE))
    {
    LED1_TOGGLE(); /* LED1状态翻转 /
    EINT_ClearIntFlag(WKUP_INT_EINT_LINE); /
    清除中断标志 /
    }
    }
    在按键被按下的一瞬间,其与MCU连接的GPIO引脚会收到一个上升沿的信号,就会触发EINT的中断,并运行对应中断回调函数。
    12.2.5 实验应用代码
    本章实验的应用代码,如下所示:
    int main(void)
    {
    NVIC_ConfigPriorityGroup(NVIC_PRIORITY_GROUP_3); /
    设置中断优先级分组为组3 /
    sys_apm32_clock_init(336, 8, 2, 7); /
    配置系统时钟 /
    delay_init(168); /
    初始化延时功能 /
    usart_init(115200); /
    初始化串口 /
    led_init(); /
    初始化LED /
    eint_init(); /
    初始化外部中断 */

    while (1);
    }
    本实验的应用代码很简单,在初始化完LED和外部表中断后,就进入一个什么都不做的while循环,这是因为按键控制LED亮灭状态翻转的操作都在对应EINT的中断回调函数中完成了。
    12.3 下载验证
    在完成编译和烧录操作后,可以看到板子上的LED0和LED1默认是处于熄灭的状态,若此时按下KEY0按键,则能够看到LED0的亮灭状态发生了一次翻转,同样的,若此时按下KEY_UP按键,则能够看LED1的亮灭状态发生了一次翻转,与预期的实验现象效果相符。

图12.1.3.1 按键与MCU的连接原理图
从上面的原理图中可以看出,KEY0按键和KEY_UP按键的一端连接到了电源正极,而另一端分别与MCU的PE4引脚和PA0引脚相连接,并且在上一小节的介绍中,也说明了对于该硬件设计,PE4引脚和PA0引脚应当配置为下拉,这样一来,在按键被按下的时候,PE4引脚或PA0引脚就会从原来的低电平状态变为高电平状态,在这期间就会有一个上升沿的跳变,因此可以使用该上升沿信号作为中断的触发源。
12.2 程序设计
12.2.1 Geehy标准库的GPIO驱动
针对本章的实验要求,仅需将对应的GPIO引脚配置为输入下拉即可,请参考上一章节中的内容。
12.2.2 Geehy标准库的SYSCFG驱动
SYSCFG(系统配置控制器)可以用于将GPIO引脚配置为其对应EINT中断线外部中断源,例如将PE4引脚配置为EINT4的外部中断源或将PA0配置为EINT0的外部中断源等,具体的步骤如下:
①:配置GPIO引脚作为EINT的外部中断源
在Geehy标准库中对应的驱动函数如下:
①:配置GPIO引脚作为EINT的外部中断源
该函数用于配置GPIO引脚作为其对应EINT中断线的外部中断源,其函数原型如下所示:
void SYSCFG_ConfigEINTLine(SYSCFG_PORT_T port, SYSCFG_PIN_T pin);
该函数的形参描述,如下表所示:
在这里插入图片描述

例如:SYSCFG_PIN_0、SYSCFG_PIN_1等(在apm32f4xx_syscfg.h文件中有定义)
表12.2.2.1 函数SYSCFG_ConfigEINTLine()形参描述
该函数的返回值描述,如下表所示:
在这里插入图片描述

表12.2.2.2 函数SYSCFG_ConfigEINTLint()返回值描述
该函数的使用示例,如下所示:

#include "apm32f4xx.h"
#include "apm32f4xx_syscfg.h"

void example_fun(void)
{
    /* 配置PA0引脚作为EINT0的外部中断源 */
    SYSCFG_ConfigEINTLine(SYSCFG_PORT_GPIOA, SYSCFG_PIN_0);
}

12.2.3 Geehy标准库的EINT驱动
EINT(外部中断/事件控制器)可以用于管理从GPIO引脚等I/O引脚输入的信号引起的中断或事件,即外部中断/事件。使用时,具体的步骤如下:
①:配置EINT线的模式、触发条件和是否使能等配置项
②:使能EINT线中断,并配置其相关的中断优先级
在Geehy标准库中对应的驱动函数如下:
①:配置EINT
该函数用于配置EINT的各项参数,其函数原型如下所示:
void EINT_Config(EINT_Config_T* eintConfig);
该函数的形参描述,如下表所示:
在这里插入图片描述

表12.2.3.1 函数EINT_Config()形参描述
该函数的返回值描述,如下所示:
在这里插入图片描述

表12.2.3.2 函数EINT_Config()返回值描述
该函数使用EINT_Config_T类型的结构体变量传入EINT的配置参数,该结构体的定义如下所示:

typedef enum
{
    EINT_LINENONE				= 0x00000,		/* 不选择外部中断线 */
    EINT_LINE_0					= 0x00001,		/* 外部中断线0 */
    EINT_LINE_1					= 0x00002,		/* 外部中断线1 */
    EINT_LINE_2					= 0x00004,		/* 外部中断线2 */
    EINT_LINE_3					= 0x00008,		/* 外部中断线3 */
    EINT_LINE_4					= 0x00010,		/* 外部中断线4 */
    EINT_LINE_5					= 0x00020,		/* 外部中断线5 */
    EINT_LINE_6					= 0x00040,		/* 外部中断线6 */
    EINT_LINE_7					= 0x00080,		/* 外部中断线7 */
    EINT_LINE_8					= 0x00100,		/* 外部中断线8 */
    EINT_LINE_9					= 0x00200,		/* 外部中断线9 */
    EINT_LINE_10					= 0x00400,		/* 外部中断线10 */
    EINT_LINE_11					= 0x00800,		/* 外部中断线11 */
    EINT_LINE_12					= 0x01000,		/* 外部中断线12 */
    EINT_LINE_13					= 0x02000,		/* 外部中断线13 */
    EINT_LINE_14					= 0x04000,		/* 外部中断线14 */
    EINT_LINE_15					= 0x08000,		/* 外部中断线15 */
    EINT_LINE_16					= 0x10000,		/* 外部中断线16 */
    EINT_LINE_17					= 0x20000,		/* 外部中断线17 */
    EINT_LINE_18					= 0x40000,		/* 外部中断线18 */
    EINT_LINE_19					= 0x80000,		/* 外部中断线19 */
    EINT_LINE_20					= 0x00100000,	/* 外部中断线20 */
    EINT_LINE_21					= 0x00200000,	/* 外部中断线21 */
    EINT_LINE_22					= 0x00400000,	/* 外部中断线22 */
} EINT_LINE_T;

typedef enum
{
    EINT_MODE_INTERRUPT			= 0x00,			/* 中断请求模式 */
    EINT_MODE_EVENT				= 0x04			/* 事件请求模式 */
} EINT_MODE_T;

typedef enum
{
    EINT_TRIGGER_RISING			= 0x08,			/* 上升沿触发 */
    EINT_TRIGGER_FALLING		= 0x0C,			/* 下降沿触发 */
    EINT_TRIGGER_RISING_FALLING	= 0x10			/* 上边沿触发 */
} EINT_TRIGGER_T;

typedef struct
{
    EINT_LINE_T		line;		/* 外部中断线 */
    EINT_MODE_T		mode;		/* 模式 */
    EINT_TRIGGER_T	trigger;	/* 触发模式 */
    uint8_t			lineCmd;	/* 使能或禁止 */
} EINT_Config_T;
该函数的使用示例,如下所示:
#include "apm32f4xx.h"
#include "apm32f4xx_eint.h"

void example_fun(void)
{
    EINT_Config_T eint_init_struct;
    
    /* 配置使能EINT0的上升沿信号触发中断 */
    eint_init_struct.line		= EINT_LINE_0;
    eint_init_struct.mode		= EINT_MODE_INTERRUPT;
    eint_init_struct.trigger	= EINT_TRIGGER_RISING;
    eint_init_struct.lineCmd	= ENABLE;
    EINT_Config(&eint_init_struct);
}

②:配置EINT线中断
该函数用于使能NVIC(嵌套向量中断控制器)的请求,因此不仅能用于配置EINT的中断,还能配置其他外设等的中断,其函数原型如下所示:

void NVIC_EnableIRQRequest(	IRQn_Type irq,
    							uint8_t preemptionPriority,
    							uint8_t subPriority);

该函数的形参描述,如下表所示:
在这里插入图片描述

表12.2.3.3 函数NVIC_EnableIRQRequest()形参描述
该函数的返回值描述,如下表所示:
在这里插入图片描述

表12.2.3.4 函数NVIC_EnableIRQRequest()返回值描述
该函数的使用示例,如下所示:

#include "apm32f4xx.h"

void example_fun(void)
{
    /* 使能EINT0中断,并设置其抢占优先级为2,子优先级为0 */
    NVIC_EnableIRQRequest(EINT0_IRQn, 2, 0);
}

/* 使能了中断,就可以实现其对应的中断回调函数 */
void EINT0_IRQHandler(void)
{
    /* Do something. */
}

12.2.4 外部中断驱动
本实验的外部中断驱动主要就是配置GPIO引脚作为EINT的外部中断源,并在其对应的中断回调函数中处理按键被按下后执行的操作。
外部中断驱动中,对GPIO引脚、SYSCFG、EINT的相关定义,如下所示:

#define KEY0_INT_GPIO_PORT		GPIOE
#define KEY0_INT_GPIO_PIN		GPIO_PIN_4
#define KEY0_INT_GPIO_CLK_ENABLE()							\
    do {														\
        RCM_EnableAHB1PeriphClock(RCM_AHB1_PERIPH_GPIOE);	\
    } while (0)
#define KEY0_INT_SYSCFG_PORT	SYSCFG_PORT_GPIOE
#define KEY0_INT_SYSCFG_PIN		SYSCFG_PIN_4
#define KEY0_INT_EINT_LINE		EINT_LINE_4
#define KEY0_INT_IRQn			EINT4_IRQn
#define KEY0_INT_IRQHandler		EINT4_IRQHandler

#define WKUP_INT_GPIO_PORT		GPIOA
#define WKUP_INT_GPIO_PIN		GPIO_PIN_0
#define WKUP_INT_GPIO_CLK_ENABLE()							\
    do {														\
        RCM_EnableAHB1PeriphClock(RCM_AHB1_PERIPH_GPIOA);	\
    } while (0)
#define WKUP_INT_SYSCFG_PORT	SYSCFG_PORT_GPIOA
#define WKUP_INT_SYSCFG_PIN		SYSCFG_PIN_0
#define WKUP_INT_EINT_LINE		EINT_LINE_0
#define WKUP_INT_IRQn			EINT0_IRQn
#define WKUP_INT_IRQHandler		EINT0_IRQHandler

外部中断驱动中,外部中断的初始化函数,如下所示:

/**
 * @brief	初始化外部中断
 * @param	无
 * @retval	无
 */
void eint_init(void)
{
    GPIO_Config_T gpio_init_struct;
    EINT_Config_T eint_init_struct;
    
    /* 使能时钟 */
    KEY0_INT_GPIO_CLK_ENABLE();
    WKUP_INT_GPIO_CLK_ENABLE();
    RCM_EnableAPB2PeriphClock(RCM_APB2_PERIPH_SYSCFG);
    
    /* 配置KEY0引脚和外部中断 */
    gpio_init_struct.pin = KEY0_INT_GPIO_PIN;			/* KEY0引脚 */
    gpio_init_struct.mode = GPIO_MODE_IN;				/* 输入模式 */
    gpio_init_struct.pupd = GPIO_PUPD_DOWN;				/* 下拉 */
    GPIO_Config(KEY0_INT_GPIO_PORT, &gpio_init_struct);	/* 配置KEY0引脚 */
    /* 配置引脚作为事件线 */
    SYSCFG_ConfigEINTLine(KEY0_INT_SYSCFG_PORT, KEY0_INT_SYSCFG_PIN);
    eint_init_struct.line = KEY0_INT_EINT_LINE;			/* EINT线 */
    eint_init_struct.mode = EINT_MODE_INTERRUPT;		/* 中断模式 */
    eint_init_struct.trigger = EINT_TRIGGER_RISING;		/* 上升沿 */
    eint_init_struct.lineCmd = ENABLE;					/* 使能 */
    EINT_Config(&eint_init_struct);						/* 配置EINT */
    NVIC_EnableIRQRequest(KEY0_INT_IRQn, 2, 0);			/* 使能中断 */
    
    /* 配置KEY_UP引脚和外部中断 */
    gpio_init_struct.pin = WKUP_INT_GPIO_PIN;			/* KEY_UP引脚 */
    gpio_init_struct.mode = GPIO_MODE_IN;				/* 输入模式 */
    gpio_init_struct.pupd = GPIO_PUPD_DOWN;				/* 下拉 */
    GPIO_Config(WKUP_INT_GPIO_PORT, &gpio_init_struct);	/* 配置KEY_UP引脚 */
    /* 配置GPIO作为EINT线 */
    SYSCFG_ConfigEINTLine(WKUP_INT_SYSCFG_PORT, WKUP_INT_SYSCFG_PIN);
    eint_init_struct.line = WKUP_INT_EINT_LINE;			/* EINT线 */
    eint_init_struct.mode = EINT_MODE_INTERRUPT;		/* 中断模式 */
    eint_init_struct.trigger = EINT_TRIGGER_RISING;		/* 上升沿 */
    eint_init_struct.lineCmd = ENABLE;					/* 使能 */
    EINT_Config(&eint_init_struct);						/* 配置EINT */
    NVIC_EnableIRQRequest(WKUP_INT_IRQn, 2, 0);			/* 使能中断 */
}

在外部中断的初始化函数中,除了使能按键对应GPIO端口的时钟,还使能了SYSCFG的时钟,并配置了GPIO、SYSCFG和EINT。
在外部中断的初始化函数中,开启了两个EINT的中断,因此需要编写这两个EINT中断对应的中断回调函数,如下所示:

/**
 * @brief	KEY0按键外部中断服务函数
 * @param	无
 * @retval	无
 */
void KEY0_INT_IRQHandler(void)
{
    /* 消抖,
     * 此处为了方便使用了延时函数,
     * 实际代码中应禁止在中断中使用阻塞延时函数 */
    delay_ms(20);
    
    if (EINT_ReadIntFlag(KEY0_INT_EINT_LINE))
    {
    		LED0_TOGGLE();							/* LED0状态翻转 */
    		EINT_ClearIntFlag(KEY0_INT_EINT_LINE);	/* 清除中断标志 */
    }
}

/**
 * @brief	KEY_UP按键外部中断服务函数
 * @param	无
 * @retval	无
 */
void WKUP_INT_IRQHandler(void)
{
    /* 消抖,
     * 此处为了方便使用了延时函数,
     * 实际代码中应禁止在中断中使用阻塞延时函数 */
    delay_ms(20);
    
    if (EINT_ReadIntFlag(WKUP_INT_EINT_LINE))
    {
    		LED1_TOGGLE();							/* LED1状态翻转 */
    		EINT_ClearIntFlag(WKUP_INT_EINT_LINE);	/* 清除中断标志 */
    }
}

在按键被按下的一瞬间,其与MCU连接的GPIO引脚会收到一个上升沿的信号,就会触发EINT的中断,并运行对应中断回调函数。
12.2.5 实验应用代码
本章实验的应用代码,如下所示:

int main(void)
{
    NVIC_ConfigPriorityGroup(NVIC_PRIORITY_GROUP_3);	/* 设置中断优先级分组为组3 */
    sys_apm32_clock_init(336, 8, 2, 7);					/* 配置系统时钟 */
    delay_init(168);										/* 初始化延时功能 */
    usart_init(115200);									/* 初始化串口 */
    led_init();											/* 初始化LED */
    eint_init();											/* 初始化外部中断 */
    
    while (1);
}

本实验的应用代码很简单,在初始化完LED和外部表中断后,就进入一个什么都不做的while循环,这是因为按键控制LED亮灭状态翻转的操作都在对应EINT的中断回调函数中完成了。
12.3 下载验证
在完成编译和烧录操作后,可以看到板子上的LED0和LED1默认是处于熄灭的状态,若此时按下KEY0按键,则能够看到LED0的亮灭状态发生了一次翻转,同样的,若此时按下KEY_UP按键,则能够看LED1的亮灭状态发生了一次翻转,与预期的实验现象效果相符。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值