参考:
正点原子EXTI

源码下载

功能说明

EXTI

支持的外部请求

与引脚的对应关系

EXTI配置步骤

hal库EXTI配置步骤

hal库EXTI配置中自动配置好afio时钟、中断线映射和EXTI屏蔽

EXTI中断号

EXTI中断函数位置

常用函数

开启时钟

1
__HAL_RCC_GPIOX_CLK_ENABLE()     // 使能GPIOX时钟

初始化引脚

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
GPIO_InitTypeDef gpio_init_struct;
gpio_init_struct.Pin = GPIO_PIN_X; /* GPIO引脚 */
gpio_init_struct.Mode = GPIO_MODE_OUTPUT_PP; /* 推挽输出 */
gpio_init_struct.Pull = GPIO_PULLUP; /* 上拉 */
gpio_init_struct.Speed = GPIO_SPEED_FREQ_HIGH; /* 高速 */
HAL_GPIO_Init(GPIOX, &gpio_init_struct); /* 初始化GPIO引脚 */

/*
gpio_init_struct.Mode
GPIO_MODE_INPUT 输入模式
GPIO_MODE_OUTPUT_PP 推挽输出模式
GPIO_MODE_OUTPUT_OD 开漏输出模式
GPIO_MODE_AF_PP 复用推挽输出模式
GPIO_MODE_AF_OD 复用开漏输出模式
GPIO_MODE_AF_INPUT 复用输入模式
GPIO_MODE_ANALOG 模拟输入模式

GPIO_MODE_IT_RISING 上升沿触发中断
GPIO_MODE_IT_FALLING 下降沿触发中断
GPIO_MODE_IT_RISING_FALLING 上升沿和下降沿触发中断

GPIO_MODE_EVT_RISING 上升沿触发事件
GPIO_MODE_EVT_FALLING 下降沿触发事件
GPIO_MODE_EVT_RISING_FALLING 上升沿和下降沿触发事件
*/

/*
gpio_init_struct.Pull
GPIO_PULLUP 上拉
GPIO_PULLDOWN 下拉
GPIO_NOPULL 无拉
*/

/*
gpio_init_struct.Speed
GPIO_SPEED_FREQ_LOW 低速
GPIO_SPEED_FREQ_MEDIUM 中速
GPIO_SPEED_FREQ_HIGH 高速
*/

改写引脚

1
2
3
4
HAL_GPIO_WritePin(GPIOX, GPIO_PIN_X, GPIO_PIN_SET);     // 设置引脚为高电平
HAL_GPIO_WritePin(GPIOX, GPIO_PIN_X, GPIO_PIN_RESET); // 设置引脚为低电平

HAL_GPIO_TogglePin(GPIOX, GPIO_PIN_X); // 切换引脚状态

读取引脚

1
HAL_GPIO_ReadPin(GPIOX, GPIO_PIN_X);     // 读取引脚状态

应用示例

控制led闪烁

宏定义:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
/******************************************************************************************/
/* 引脚 定义 */

#define LED0_GPIO_PORT GPIOA
#define LED0_GPIO_PIN GPIO_PIN_0
#define LED0_GPIO_CLK_ENABLE() do{ __HAL_RCC_GPIOA_CLK_ENABLE(); }while(0) /* PB口时钟使能 */

#define LED1_GPIO_PORT GPIOA
#define LED1_GPIO_PIN GPIO_PIN_1
#define LED1_GPIO_CLK_ENABLE() do{ __HAL_RCC_GPIOA_CLK_ENABLE(); }while(0) /* PE口时钟使能 */

/******************************************************************************************/
/* LED端口定义 */
#define LED0(x) do{ x ? \
HAL_GPIO_WritePin(LED0_GPIO_PORT, LED0_GPIO_PIN, GPIO_PIN_SET) : \
HAL_GPIO_WritePin(LED0_GPIO_PORT, LED0_GPIO_PIN, GPIO_PIN_RESET); \
}while(0) /* LED0翻转 */

#define LED1(x) do{ x ? \
HAL_GPIO_WritePin(LED1_GPIO_PORT, LED1_GPIO_PIN, GPIO_PIN_SET) : \
HAL_GPIO_WritePin(LED1_GPIO_PORT, LED1_GPIO_PIN, GPIO_PIN_RESET); \
}while(0) /* LED1翻转 */

/* LED取反定义 */
#define LED0_TOGGLE() do{ HAL_GPIO_TogglePin(LED0_GPIO_PORT, LED0_GPIO_PIN); }while(0) /* 翻转LED0 */
#define LED1_TOGGLE() do{ HAL_GPIO_TogglePin(LED1_GPIO_PORT, LED1_GPIO_PIN); }while(0) /* 翻转LED1 */

/******************************************************************************************/

初始化:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
void led_init(void)
{
GPIO_InitTypeDef gpio_init_struct;
LED0_GPIO_CLK_ENABLE(); /* LED0时钟使能 */
LED1_GPIO_CLK_ENABLE(); /* LED1时钟使能 */

gpio_init_struct.Pin = LED0_GPIO_PIN; /* LED0引脚 */
gpio_init_struct.Mode = GPIO_MODE_OUTPUT_PP; /* 推挽输出 */
gpio_init_struct.Pull = GPIO_PULLUP; /* 上拉 */
gpio_init_struct.Speed = GPIO_SPEED_FREQ_HIGH; /* 高速 */
HAL_GPIO_Init(LED0_GPIO_PORT, &gpio_init_struct); /* 初始化LED0引脚 */

gpio_init_struct.Pin = LED1_GPIO_PIN; /* LED1引脚 */
HAL_GPIO_Init(LED1_GPIO_PORT, &gpio_init_struct); /* 初始化LED1引脚 */


LED0(1); /* 关闭 LED0 */
LED1(1); /* 关闭 LED1 */
}

主函数:

1
2
3
4
5
6
while(1)
{
delay_ms(500);
LED0_TOGGLE();
LED1_TOGGLE();
}

按键控制led

宏定义:

1
2
3
4
5
6
7
8
9
10
11
12
#define KEY0_GPIO_PORT                  GPIOB
#define KEY0_GPIO_PIN GPIO_PIN_1
#define KEY0_GPIO_CLK_ENABLE() do{ __HAL_RCC_GPIOB_CLK_ENABLE(); }while(0)
#define KEY1_GPIO_PORT GPIOB
#define KEY1_GPIO_PIN GPIO_PIN_11
#define KEY1_GPIO_CLK_ENABLE() do{ __HAL_RCC_GPIOB_CLK_ENABLE(); }while(0)

#define KEY0 HAL_GPIO_ReadPin(KEY0_GPIO_PORT, KEY0_GPIO_PIN) /* 读取KEY0引脚 */
#define KEY1 HAL_GPIO_ReadPin(KEY1_GPIO_PORT, KEY1_GPIO_PIN) /* 读取KEY1引脚 */

#define KEY0_PRES 1 /* KEY0按下 */
#define KEY1_PRES 2 /* KEY1按下 */

初始化:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
void key_init(void)
{
GPIO_InitTypeDef gpio_init_struct;
KEY0_GPIO_CLK_ENABLE(); /* KEY0时钟使能 */
KEY1_GPIO_CLK_ENABLE(); /* KEY1时钟使能 */

gpio_init_struct.Pin = KEY0_GPIO_PIN; /* KEY0引脚 */
gpio_init_struct.Mode = GPIO_MODE_INPUT; /* 输入 */
gpio_init_struct.Pull = GPIO_PULLUP; /* 上拉 */
gpio_init_struct.Speed = GPIO_SPEED_FREQ_HIGH; /* 高速 */
HAL_GPIO_Init(KEY0_GPIO_PORT, &gpio_init_struct); /* KEY0引脚模式设置,上拉输入 */

gpio_init_struct.Pin = KEY1_GPIO_PIN; /* KEY1引脚 */
gpio_init_struct.Mode = GPIO_MODE_INPUT; /* 输入 */
gpio_init_struct.Pull = GPIO_PULLUP; /* 上拉 */
gpio_init_struct.Speed = GPIO_SPEED_FREQ_HIGH; /* 高速 */
HAL_GPIO_Init(KEY1_GPIO_PORT, &gpio_init_struct); /* KEY1引脚模式设置,上拉输入 */
}

按键扫描函数:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
/**
* @brief 按键扫描函数
* @note 该函数有响应优先级(同时按下多个按键): WK_UP > KEY1 > KEY0!!
* @param mode:0 / 1, 具体含义如下:
* @arg 0, 不支持连续按(当按键按下不放时, 只有第一次调用会返回键值,
* 必须松开以后, 再次按下才会返回其他键值)
* @arg 1, 支持连续按(当按键按下不放时, 每次调用该函数都会返回键值)
* @retval 键值, 定义如下:
* KEY0_PRES, 1, KEY0按下
* KEY1_PRES, 2, KEY1按下
*/
uint8_t key_scan(uint8_t mode)
{
static uint8_t key_up = 1; /* 按键按松开标志 */
uint8_t keyval = 0;

if (mode) key_up = 1; /* 支持连按 */

if (key_up && (KEY0 == 0 || KEY1 == 0)) /* 按键松开标志为1, 且有任意一个按键按下了 */
{
delay_ms(10); /* 去抖动 */
key_up = 0;

if (KEY0 == 0) keyval = KEY0_PRES;

if (KEY1 == 0) keyval = KEY1_PRES;
}
else if (KEY0 == 1 && KEY1 == 1) /* 没有任何按键按下, 标记按键松开 */
{
key_up = 1;
}

return keyval; /* 返回键值 */
}

主函数:

1
2
3
4
5
6
while(1)
{
uint8_t keyval = key_scan(0);
if(keyval == KEY0_PRES) LED0_TOGGLE();
else if(keyval == KEY1_PRES) LED1_TOGGLE();
}

EXTI外部中断控制led

宏定义:

1
2
3
4
5
6
7
8
9
10
11
#define KEY0_INT_GPIO_PORT              GPIOB
#define KEY0_INT_GPIO_PIN GPIO_PIN_1
#define KEY0_INT_GPIO_CLK_ENABLE() do{ __HAL_RCC_GPIOB_CLK_ENABLE(); }while(0) /* PE口时钟使能 */
#define KEY0_INT_IRQn EXTI1_IRQn
#define KEY0_INT_IRQHandler EXTI1_IRQHandler

#define KEY1_INT_GPIO_PORT GPIOB
#define KEY1_INT_GPIO_PIN GPIO_PIN_11
#define KEY1_INT_GPIO_CLK_ENABLE() do{ __HAL_RCC_GPIOB_CLK_ENABLE(); }while(0) /* PE口时钟使能 */
#define KEY1_INT_IRQn EXTI15_10_IRQn
#define KEY1_INT_IRQHandler EXTI15_10_IRQHandler

初始化:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
void extix_init(void)
{
GPIO_InitTypeDef gpio_init_struct;

KEY0_GPIO_CLK_ENABLE(); /* KEY0时钟使能 */
KEY1_GPIO_CLK_ENABLE(); /* KEY1时钟使能 */

gpio_init_struct.Pin = KEY0_INT_GPIO_PIN;
gpio_init_struct.Mode = GPIO_MODE_IT_FALLING; /* 下升沿触发 */
gpio_init_struct.Pull = GPIO_PULLUP;
HAL_GPIO_Init(KEY0_INT_GPIO_PORT, &gpio_init_struct); /* KEY0配置为下降沿触发中断 */

gpio_init_struct.Pin = KEY1_INT_GPIO_PIN;
gpio_init_struct.Mode = GPIO_MODE_IT_FALLING; /* 下升沿触发 */
gpio_init_struct.Pull = GPIO_PULLUP;
HAL_GPIO_Init(KEY1_INT_GPIO_PORT, &gpio_init_struct); /* KEY1配置为下降沿触发中断 */

// 配置中断优先级
HAL_NVIC_SetPriority(KEY0_INT_IRQn, 0, 2); /* 抢占0,子优先级2 */
HAL_NVIC_EnableIRQ(KEY0_INT_IRQn); /* 使能中断线4 */

HAL_NVIC_SetPriority(KEY1_INT_IRQn, 1, 2); /* 抢占1,子优先级2 */
HAL_NVIC_EnableIRQ(KEY1_INT_IRQn); /* 使能中断线3 */
}

中断函数:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
/**
* @brief KEY0 外部中断服务程序
* @param 无
* @retval 无
*/
void KEY0_INT_IRQHandler(void)
{
HAL_GPIO_EXTI_IRQHandler(KEY0_INT_GPIO_PIN); /* 调用中断处理公用函数 清除KEY0所在中断线 的中断标志位 */
__HAL_GPIO_EXTI_CLEAR_IT(KEY0_INT_GPIO_PIN); /* HAL库默认先清中断再处理回调,退出时再清一次中断,避免按键抖动误触发 */
}

/**
* @brief KEY1 外部中断服务程序
* @param 无
* @retval 无
*/
void KEY1_INT_IRQHandler(void)
{
HAL_GPIO_EXTI_IRQHandler(KEY1_INT_GPIO_PIN); /* 调用中断处理公用函数 清除KEY1所在中断线 的中断标志位,中断下半部在HAL_GPIO_EXTI_Callback执行 */
__HAL_GPIO_EXTI_CLEAR_IT(KEY1_INT_GPIO_PIN); /* HAL库默认先清中断再处理回调,退出时再清一次中断,避免按键抖动误触发 */
}

回调函数:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
/**
* @brief 中断服务程序中需要做的事情
在HAL库中所有的外部中断服务函数都会调用此函数
* @param GPIO_Pin:中断引脚号
* @retval 无
*/
void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)
{
delay_ms(20); /* 消抖 */
switch(GPIO_Pin)
{
case KEY0_INT_GPIO_PIN:
if (KEY0 == 0)
{
LED0_TOGGLE(); /* LED0 状态取反 */
LED1_TOGGLE(); /* LED1 状态取反 */
}
break;
case KEY1_INT_GPIO_PIN:
if (KEY1 == 0)
{
LED0_TOGGLE(); /* LED0 状态取反 */
}
break;
}
}