GD32——按键外部中断

GD32——按键外部中断

以GD32F303的芯片(M4)为例。

简言:文章主要讲解,普通IO口的按键使用外部中断来配置使用,从而更快的响应按键事件,有效的提高了按键处理的实时性。

文章分为两部分:第一部分讲基本原理,第二部分讲配置。

原理参考:

1、GD32F30X用户手册:中断/事件控制器(EXTI) ;

2、gd32f30x_exti.h / .c源码;

3、GD32F303固件库开发(15)----外部中断EXTI_gd32f303 中断-CSDN博客

一、基本原理

1、EXTI有三种触发类型:上升沿触发、下降沿触发和任意沿触发

/* interrupt trigger mode */

typedef enum

{

EXTI_TRIG_RISING = 0, /*!< EXTI rising edge trigger */

EXTI_TRIG_FALLING, /*!< EXTI falling edge trigger */

EXTI_TRIG_BOTH, /*!< EXTI rising and falling edge trigger */

EXTI_TRIG_NONE /*!< without rising edge or falling edge trigger */

}exti_trig_type_enum;

2、按键---软件触发EXTI中断

每一种pin号对应不同的中断线,GPIO 口连接到 16 个外部中断 / 事件线如下图

(1)、中断线 0-4,每条线单独使用,不共用中断函数:

中断类型,(from:gd32f30x.h)

中断服务函数,startup_gd32f30x_hd.s

(2)、中断线 5-9 共用中断函数:

(3)、中断线 10-15 共用中断函数:

二、API函数

1、初始化EXTI线x

void exti_init(exti_line_enum linex, exti_mode_enum mode, exti_trig_type_enum trig_type)

(1)、参数解释:

① 中断线:EXTI_x (x=0..19): EXTI line x;

② 模式:EXTI模式

EXTI_INTERRUPT: interrupt mode

EXTI_EVENT: event mode

③ 中断触发类型

(2)、使用:

exti_init(EXTI_0, EXTI_INTERRUPT, EXTI_TRIG_BOTH);

2、设置IO口与中断线的映射关系

(选择哪个引脚作为EXTI源)

void gpio_exti_source_select(uint8_t output_port, uint8_t output_pin)

(1)、使用:

gpio_exti_source_select(GPIO_PORT_SOURCE_GPIOA, GPIO_PIN_SOURCE_0);

3、获取EXTI线x中断标志位

FlagStatus exti_interrupt_flag_get(exti_line_enum linex)

{

if(RESET != (EXTI_PD & (uint32_t)linex)) {

return SET;

} else {

return RESET;

}

}

(1)、解释:

① 读取中断状态:

EXTI_PD 是一个寄存器,存储了所有 EXTI 线的挂起状态。通过与操作 (EXTI_PD & (uint32_t)linex) 检查特定的 EXTI 线是否被置位。

② 判断标志状态:

如果结果不等于 RESET,则说明对应的中断线存在挂起的中断,返回 SET。

否则,返回 RESET,表示没有挂起的中断。

(2)、使用:

FlagStatus state = exti_interrupt_flag_get(EXTI_0);

4、清除EXTI线x中断标志位

void exti_interrupt_flag_clear(exti_line_enum linex)

{

EXTI_PD = (uint32_t)linex;

}

(1)、解释:

将 linex 的值赋给 EXTI_PD,实际上是在设置对应位为 1,从而清除该 EXTI 线的中断标志。

(2)、使用:

exti_interrupt_flag_clear(EXTI_0);

5、使能NVIC的中断,使能中断,配置中断的优先级

void nvic_irq_enable(uint8_t nvic_irq, uint8_t nvic_irq_pre_priority, uint8_t nvic_irq_sub_priority)

(1)、参数解释:

nvic_irq:NVIC中断, 参考枚举类型表

nvic_irq_pre_priority:抢占优先级(0~4)

nvic_irq_sub_priority:响应优先级(0~4)

(中断是,设置数值越小,中断优先级越大)

① 抢占式优先级:

抢占式优先级决定了某个中断是否可以打断正在执行的低优先级中断。如果一个高优先级中断到达,且当前正在服务的中断优先级低于它,系统会中断当前中断,转而服务高优先级中断。

② 响应式优先级(子优先级):

响应式优先级用于在相同抢占式优先级的中断之间进行调度。当多个中断具有相同的抢占式优先级时,响应式优先级决定了哪个中断先被处理。换句话说,它只在抢占式优先级相等的情况下影响中断的响应顺序。

三、按键外部中断配置

步骤:

使能IO口时钟,开启AFIO复用时钟;

初始化GPIO,配置I/O 口为输入模式;

使能中断,配置中断的优先级;

设置IO口与中断线的映射关系(选择中断源);

初始化EXTI,选择中断/事件模式,选择触发方式;

清除中断标志;

写中断服务函数;

四、代码

1、头文件

#ifndef __EXTI_KEY_H__

#define __EXTI_KEY_H__

#include "main.h"

/* 按键输入引脚 */

#define KEY1_S3_Pin GPIO_PIN_0 // PB0

#define KEY1_S3_GPIO_Port GPIOB

#define KEY2_S4_Pin GPIO_PIN_1 // PB1

#define KEY2_S4_GPIO_Port GPIOB

#define KEY3_S5_Pin GPIO_PIN_10 // PB10

#define KEY3_S5_GPIO_Port GPIOB

#define KEY4_S6_Pin GPIO_PIN_12 // PB12

#define KEY4_S6_GPIO_Port GPIOB

#define KEY5_S7_Pin GPIO_PIN_13 // PB13

#define KEY5_S7_GPIO_Port GPIOB

#define KEY6_S8_Pin GPIO_PIN_14 // PB14

#define KEY6_S8_GPIO_Port GPIOB

#define KEY7_S9_Pin GPIO_PIN_15 // PB15

#define KEY7_S9_GPIO_Port GPIOB

#define S3_KEY_EXTI_LINE EXTI_0

#define S3_KEY_EXTI_PORT_SOURCE GPIO_PORT_SOURCE_GPIOB

#define S3_KEY_EXTI_PIN_SOURCE GPIO_PIN_SOURCE_0

#define S3_KEY_EXTI_IRQn EXTI0_IRQn

#define S4_KEY_EXTI_LINE EXTI_1

#define S4_KEY_EXTI_PORT_SOURCE GPIO_PORT_SOURCE_GPIOB

#define S4_KEY_EXTI_PIN_SOURCE GPIO_PIN_SOURCE_1

#define S4_KEY_EXTI_IRQn EXTI1_IRQn

#define S5_KEY_EXTI_LINE EXTI_10

#define S5_KEY_EXTI_PORT_SOURCE GPIO_PORT_SOURCE_GPIOB

#define S5_KEY_EXTI_PIN_SOURCE GPIO_PIN_SOURCE_10

#define S5_KEY_EXTI_IRQn EXTI10_15_IRQn

#define S6_KEY_EXTI_LINE EXTI_12

#define S6_KEY_EXTI_PORT_SOURCE GPIO_PORT_SOURCE_GPIOB

#define S6_KEY_EXTI_PIN_SOURCE GPIO_PIN_SOURCE_12

#define S6_KEY_EXTI_IRQn EXTI10_15_IRQn

#define S7_KEY_EXTI_LINE EXTI_13

#define S7_KEY_EXTI_PORT_SOURCE GPIO_PORT_SOURCE_GPIOB

#define S7_KEY_EXTI_PIN_SOURCE GPIO_PIN_SOURCE_13

#define S7_KEY_EXTI_IRQn EXTI10_15_IRQn

#define S8_KEY_EXTI_LINE EXTI_14

#define S8_KEY_EXTI_PORT_SOURCE GPIO_PORT_SOURCE_GPIOB

#define S8_KEY_EXTI_PIN_SOURCE GPIO_PIN_SOURCE_14

#define S8_KEY_EXTI_IRQn EXTI10_15_IRQn

#define S9_KEY_EXTI_LINE EXTI_15

#define S9_KEY_EXTI_PORT_SOURCE GPIO_PORT_SOURCE_GPIOB

#define S9_KEY_EXTI_PIN_SOURCE GPIO_PIN_SOURCE_15

#define S9_KEY_EXTI_IRQn EXTI10_15_IRQn

#define LONG_PRESS_THRESHOLD 3000 // 3000毫秒为长按阈值

typedef enum

{

KEY_MODE_GPIO = 0,

KEY_MODE_EXTI = 1

} keymode_typedef_enum;

void key_exti_init(keymode_typedef_enum key_mode);

uint32_t get_current_time();

#endif

2、源文件

#include "exti_key.h"

extern volatile uint32_t key_times;

/*!

\brief configure key

\param[in] key_mode: specify button mode

\arg KEY_MODE_GPIO: key will be used as simple IO

\arg KEY_MODE_EXTI: key will be connected to EXTI line with interrupt

\param[out] none

\retval none

*/

void key_exti_init(keymode_typedef_enum key_mode){

/* enable the key clock */

// 使能IO口时钟,开启AFIO时钟

rcu_periph_clock_enable(RCU_GPIOB);

rcu_periph_clock_enable(RCU_AF);

/* configure button pin as input */

// 配置I/o 口为输入模式

gpio_init(KEY1_S3_GPIO_Port, GPIO_MODE_IPU, GPIO_OSPEED_50MHZ, KEY1_S3_Pin);

gpio_init(KEY2_S4_GPIO_Port, GPIO_MODE_IPU, GPIO_OSPEED_50MHZ, KEY2_S4_Pin);

gpio_init(KEY3_S5_GPIO_Port, GPIO_MODE_IPU, GPIO_OSPEED_50MHZ, KEY3_S5_Pin);

gpio_init(KEY4_S6_GPIO_Port, GPIO_MODE_IPU, GPIO_OSPEED_50MHZ, KEY4_S6_Pin);

gpio_init(KEY5_S7_GPIO_Port, GPIO_MODE_IPU, GPIO_OSPEED_50MHZ, KEY5_S7_Pin);

gpio_init(KEY6_S8_GPIO_Port, GPIO_MODE_IPU, GPIO_OSPEED_50MHZ, KEY6_S8_Pin);

gpio_init(KEY7_S9_GPIO_Port, GPIO_MODE_IPU, GPIO_OSPEED_50MHZ, KEY7_S9_Pin);

if (key_mode == KEY_MODE_EXTI) {

/* enable and set key EXTI interrupt to the lowest priority */

// 配置中断分组(NVIC),使能中断

nvic_irq_enable(S3_KEY_EXTI_IRQn, 2U, 0U);

nvic_irq_enable(S4_KEY_EXTI_IRQn, 2U, 0U);

nvic_irq_enable(S5_KEY_EXTI_IRQn, 2U, 0U);

nvic_irq_enable(S6_KEY_EXTI_IRQn, 2U, 0U);

nvic_irq_enable(S7_KEY_EXTI_IRQn, 2U, 0U);

nvic_irq_enable(S8_KEY_EXTI_IRQn, 2U, 0U);

nvic_irq_enable(S9_KEY_EXTI_IRQn, 2U, 0U);

/* connect key EXTI line to key GPIO pin */

// 设置IO口与中断线的映射关系

gpio_exti_source_select(S3_KEY_EXTI_PORT_SOURCE, S3_KEY_EXTI_PIN_SOURCE);

gpio_exti_source_select(S4_KEY_EXTI_PORT_SOURCE, S4_KEY_EXTI_PIN_SOURCE);

gpio_exti_source_select(S5_KEY_EXTI_PORT_SOURCE, S5_KEY_EXTI_PIN_SOURCE);

gpio_exti_source_select(S6_KEY_EXTI_PORT_SOURCE, S6_KEY_EXTI_PIN_SOURCE);

gpio_exti_source_select(S7_KEY_EXTI_PORT_SOURCE, S7_KEY_EXTI_PIN_SOURCE);

gpio_exti_source_select(S8_KEY_EXTI_PORT_SOURCE, S8_KEY_EXTI_PIN_SOURCE);

gpio_exti_source_select(S9_KEY_EXTI_PORT_SOURCE, S9_KEY_EXTI_PIN_SOURCE);

/* configure key EXTI line */

// 初始化EXTI,下降沿触发方式

exti_init(S3_KEY_EXTI_LINE, EXTI_INTERRUPT, EXTI_TRIG_FALLING);

exti_init(S4_KEY_EXTI_LINE, EXTI_INTERRUPT, EXTI_TRIG_FALLING);

exti_init(S5_KEY_EXTI_LINE, EXTI_INTERRUPT, EXTI_TRIG_BOTH);

exti_init(S6_KEY_EXTI_LINE, EXTI_INTERRUPT, EXTI_TRIG_FALLING);

exti_init(S7_KEY_EXTI_LINE, EXTI_INTERRUPT, EXTI_TRIG_FALLING);

exti_init(S8_KEY_EXTI_LINE, EXTI_INTERRUPT, EXTI_TRIG_FALLING);

exti_init(S9_KEY_EXTI_LINE, EXTI_INTERRUPT, EXTI_TRIG_FALLING);

/* clear EXTI line x interrupt pending flag */

// //清中断标志

exti_interrupt_flag_clear(S3_KEY_EXTI_LINE);

exti_interrupt_flag_clear(S4_KEY_EXTI_LINE);

exti_interrupt_flag_clear(S5_KEY_EXTI_LINE);

exti_interrupt_flag_clear(S6_KEY_EXTI_LINE);

exti_interrupt_flag_clear(S7_KEY_EXTI_LINE);

exti_interrupt_flag_clear(S8_KEY_EXTI_LINE);

exti_interrupt_flag_clear(S9_KEY_EXTI_LINE);

}

}

void EXTI0_IRQHandler(){

if (RESET != exti_interrupt_flag_get(S3_KEY_EXTI_LINE))

{

exti_interrupt_flag_clear(S3_KEY_EXTI_LINE);

LEDB_ON;

}

}

void EXTI1_IRQHandler(){

if (RESET != exti_interrupt_flag_get(S4_KEY_EXTI_LINE))

{

exti_interrupt_flag_clear(S4_KEY_EXTI_LINE);

LEDB_OFF;

}

}

void EXTI10_15_IRQHandler(){

if (RESET != exti_interrupt_flag_get(S5_KEY_EXTI_LINE))

{

FlagStatus current_button_state = gpio_input_bit_get(KEY3_S5_GPIO_Port, KEY3_S5_Pin);

if (current_button_state == 0) { // 检测到按键按下

key_times = 0;

} else { // 检测到按键释放

uint32_t duration = get_current_time(); // 计算按下持续时间

// 判断按下类型

if (duration < LONG_PRESS_THRESHOLD) { // 短按

LEDB_ON;

} else { // 长按

led_flicker(10);

}

}

exti_interrupt_flag_clear(S5_KEY_EXTI_LINE);

}

if (RESET != exti_interrupt_flag_get(S6_KEY_EXTI_LINE))

{

exti_interrupt_flag_clear(S6_KEY_EXTI_LINE);

led_flicker(6);

}

if (RESET != exti_interrupt_flag_get(S7_KEY_EXTI_LINE))

{

exti_interrupt_flag_clear(S7_KEY_EXTI_LINE);

led_flicker(7);

}

if (RESET != exti_interrupt_flag_get(S8_KEY_EXTI_LINE))

{

exti_interrupt_flag_clear(S8_KEY_EXTI_LINE);

led_flicker(8);

}

if (RESET != exti_interrupt_flag_get(S9_KEY_EXTI_LINE))

{

exti_interrupt_flag_clear(S9_KEY_EXTI_LINE);

led_flicker(9);

}

}

uint32_t get_current_time(){

return key_times; // 在systick中断函数计时,单位:ms

}