掌握外部中断基于GD32F407VE的天空星的配置
掌握外部中断基于GD32F407VE的天空星的配置
一、中断概述
什么是中断?
想象这样一个场景:
⏰ 闹钟中断
场景:清晨深度睡眠中
正常执行:睡眠状态
中断发生:闹钟响起
中断处理:醒来关闹钟,起床洗漱
返回继续:不返回睡眠,开始新任务
特点:任务切换型中断
在计算机系统中,中断正是这样一种机制:
- 中断概念:计算机响应外部或内部事件的机制,使单片机暂时中断正在执行的程序,转去执行中断处理函数,处理完毕后再返回原程序继续执行
- 中断意义:提高了计算机的实时性和可靠性,使其能够及时响应各种事件
中断分类与触发条件
中断类型 | 描述 | 实例 |
---|---|---|
外部设备请求 | 外部设备请求主机处理事件 | 键盘按键、鼠标点击触发中断 |
时钟中断 | 系统计时器周期性触发中断 | 操作系统定时任务切换 |
硬件故障 | 硬件错误触发中断保护系统 | 内存错误触发中断 |
软件中断 | 软件通过指令请求系统功能 | 应用程序调用系统服务 |
异常 | 执行过程发生异常触发中断 | 除零错误触发异常中断 |
中断向量表
中断向量表是一块固定的内存区域(4字节对齐),存放各个中断服务函数的首地址。当发生中断时,CPU会自动执行对应的中断服务函数。
中断向量对应函数
二、外部中断EXTI
2.1 外部中断概念
外部中断(EXTI,External Interrupt)是芯片引脚电平变化触发的中断。
触发机制:
-
硬件触发:引脚电平变化触发
- 上升沿触发
- 下降沿触发
- 任意沿触发
-
软件触发:手动触发中断事件
重要特性:
- 总共16个外部中断线(0-15)
- 同一PIN编号的引脚(如PA0和PB0)不能同时触发中断
2.2 硬件外部中断实现
基本配置步骤
#define EXTI2_RCU RCU_GPIOC
#define EXTI2_PORT GPIOC
#define EXTI2_PUPD GPIO_PUPD_PULLUP
#define EXTI2_SOURCE_PORT EXTI_SOURCE_GPIOC
#define EXTI2_TRIG_TYPE EXTI_TRIG_BOTH
#define EXTI2_PRIORITY 2, 2void EXTI2_config() { // ================== EXTI2 配置// 时钟使能 syscfgrcu_periph_clock_enable(RCU_SYSCFG);// 中断线连接 syscfgsyscfg_exti_line_config(EXTI2_SOURCE_PORT, EXTI_SOURCE_PIN2);// exti 初始化// 中断线 模式:中断 触发类型(条件): 上升或下降沿exti_init(EXTI_2, EXTI_INTERRUPT, EXTI2_TRIG_TYPE);// 使能外部中断exti_interrupt_enable(EXTI_2);// 中断优先级,使能中断请求 nvic_irq_enable在misc.h中// 参数1: 中断类型枚举常量, 在 gd32f4xx.h第128行// 参数2: 抢占优先级// 参数3: 响应优先级nvic_irq_enable(EXTI2_IRQn, EXTI2_PRIORITY);
}
实际应用示例
示例1:PA0按键中断
#include "gd32f4xx.h"
#include "systick.h"
#include "USART0.h"// 收到串口0数据,回调函数
void USART0_on_recv(uint8_t* data, uint32_t len) {printf("recv[%d] = %s\n", len, data);
}#define EXTI0_RCU RCU_GPIOA
#define EXTI0_PORT GPIOA
#define EXTI0_PUPD GPIO_PUPD_NONE
#define EXTI0_SOURCE_PORT EXTI_SOURCE_GPIOA
#define EXTI0_TRIG_TYPE EXTI_TRIG_BOTH
#define EXTI0_PRIORITY 2, 2void EXTI0_config() {// ================== PA0 初始化 浮空输入GPIO_input(EXTI0_RCU, EXTI0_PORT, GPIO_PIN_0, EXTI0_PUPD);// ================== EXTI0 配置// 时钟使能 syscfgrcu_periph_clock_enable(RCU_SYSCFG);// 中断线连接 syscfgsyscfg_exti_line_config(EXTI0_SOURCE_PORT, EXTI_SOURCE_PIN0);// exti 初始化// 中断线 模式:中断 触发类型(条件): 上升或下降沿exti_init(EXTI_0, EXTI_INTERRUPT, EXTI0_TRIG_TYPE);// 使能外部中断exti_interrupt_enable(EXTI_0);// 中断优先级,使能中断请求 nvic_irq_enable在misc.h中// 参数1: 中断类型枚举常量, 在 gd32f4xx.h第128行// 参数2: 抢占优先级// 参数3: 响应优先级nvic_irq_enable(EXTI0_IRQn, EXTI0_PRIORITY);
}#define KEY GPIOA, GPIO_PIN_0
#define DOWN SET
#define UP RESETvolatile static uint64_t start_tick = 0;// 外部中断0处理函数 启动汇编文件的第93行拷贝名字
void EXTI0_IRQHandler() {if (SET == exti_interrupt_flag_get(EXTI_0)) {exti_interrupt_flag_clear(EXTI_0); // 软件清零FlagStatus state = gpio_input_bit_get(KEY);if (DOWN == state) {// 获取起点时间start_tick = get_tick_us();// printf("按下\n");} else {
// printf("抬起\n");// 获取终点时间uint64_t duration = get_tick_us() - start_tick; // us为单位duration = duration / 1000; // 转换为msprintf("duration = %llu ms\n", duration);if (duration <= 50) { // < 50 ms, 不处理,忽略} else if (duration <= 500) {printf("short press\n");} else if (duration <= 2000) {printf("long press\n");}}}
}
int main(void) {// 系统滴答定时器初始化systick_config();USART0_init(); // 串口初始化调用EXTI0_config(); // 外部中断配置while(1) {
// printf("========while(1)========\n");delay_1ms(1000);}
}
示例2:PC0按键中断
#include "gd32f4xx.h"
#include "systick.h"
#include "USART0.h"
// 收到串口0数据,回调函数
void USART0_on_recv(uint8_t* data, uint32_t len) {printf("recv[%d] = %s\n", len, data);
}
#define EXTI0_RCU RCU_GPIOC
#define EXTI0_PORT GPIOC
#define EXTI0_PUPD GPIO_PUPD_PULLUP
#define EXTI0_SOURCE_PORT EXTI_SOURCE_GPIOC
#define EXTI0_TRIG_TYPE EXTI_TRIG_BOTH
#define EXTI0_PRIORITY 2, 2
void EXTI0_config() {// ================== PA0 初始化 浮空输入GPIO_input(EXTI0_RCU, EXTI0_PORT, GPIO_PIN_0, EXTI0_PUPD);// ================== EXTI0 配置// 时钟使能 syscfgrcu_periph_clock_enable(RCU_SYSCFG);// 中断线连接 syscfgsyscfg_exti_line_config(EXTI0_SOURCE_PORT, EXTI_SOURCE_PIN0);// exti 初始化// 中断线 模式:中断 触发类型(条件): 上升或下降沿exti_init(EXTI_0, EXTI_INTERRUPT, EXTI0_TRIG_TYPE);// 使能外部中断exti_interrupt_enable(EXTI_0);// 中断优先级,使能中断请求 nvic_irq_enable在misc.h中// 参数1: 中断类型枚举常量, 在 gd32f4xx.h第128行// 参数2: 抢占优先级// 参数3: 响应优先级nvic_irq_enable(EXTI0_IRQn, EXTI0_PRIORITY);
}#define KEY GPIOC, GPIO_PIN_0
#define DOWN RESET
#define UP SETvolatile static uint64_t start_tick = 0;
volatile static uint64_t last_tick = 0; // 上一次的时间// 外部中断0处理函数 启动汇编文件的第93行拷贝名字
void EXTI0_IRQHandler() {if (SET == exti_interrupt_flag_get(EXTI_0)) {exti_interrupt_flag_clear(EXTI_0); // 软件清零// 获取当前的时间uint64_t cur_tick = get_tick_us();uint64_t shake_dura = (cur_tick - last_tick) / 1000; // 转为mslast_tick = cur_tick; // 保存上一次的状态if (shake_dura < 20) return; // 少于30ms,不能往下执行,去抖动FlagStatus state = gpio_input_bit_get(KEY);if (DOWN == state) {// 获取起点时间start_tick = get_tick_us();// printf("按下\n");} else {
// printf("抬起\n");// 获取终点时间uint64_t duration = get_tick_us() - start_tick; // us为单位duration = duration / 1000; // 转换为ms
// printf("duration = %llu ms\n", duration);if (duration <= 30) { // < 30 ms, 不处理,忽略} else if (duration <= 500) {printf("short press\n");} else if (duration <= 2000) {printf("long press\n");}}}
}int main(void) {// 系统滴答定时器初始化systick_config();USART0_init(); // 串口初始化调用EXTI0_config(); // 外部中断配置while(1) {
// printf("========while(1)========\n");delay_1ms(1000);}
}
中断消抖处理
Winvalid-source-encoding警告消除
-Wno-invalid-source-encoding
滴答定时器中断消抖处理思路
- SysTick 定时器,是硬件级别的计时器
systick.c 文件中添加一个函数
#include "gd32f4xx.h"
#include "systick.h"volatile static uint32_t delay;
//按键按下的时间获取
volatile static uint64_t tick_us = 0;/*!\brief configure systick\param[in] none\param[out] none\retval none
*/
void systick_config(void)
{/* setup systick timer for 1000000Hz interrupts */if(SysTick_Config(SystemCoreClock / 1000000U)) {/* capture error */while(1) {}}/* configure the systick handler priority */NVIC_SetPriority(SysTick_IRQn, 0x00U);
}/*!\brief delay a time in milliseconds\param[in] count: count in milliseconds\param[out] none\retval none
*/
void delay_1ms(uint32_t count)
{delay = count * 1000;while(0U != delay) {}
}void delay_1us(uint32_t count)
{delay = count;while(0U != delay) {}
}
/*!\brief delay decrement\param[in] none\param[out] none\retval none
*/
void delay_decrement(void)
{//累加时间tick_us ++;if(0U != delay) {delay--;}
}
//获取按键按下的时间
uint64_t get_tick_us() {return tick_us;
}
systick.h 文件中添加一个函数
#ifndef SYS_TICK_H
#define SYS_TICK_H#include <stdint.h>/* configure systick */
void systick_config(void);
/* delay a time in milliseconds */
void delay_1ms(uint32_t count);
/* delay a time in microseconds */
void delay_1us(uint32_t count);
/* delay decrement */
void delay_decrement(void);uint64_t get_tick_us();#endif /* SYS_TICK_H */
机械按键存在抖动问题,消抖处理,用按下的时间判断:
volatile static uint64_t last_tick = 0; // 上一次的时间
// 外部中断0处理函数 启动汇编文件的第93行拷贝名字
void EXTI0_IRQHandler() {if (SET == exti_interrupt_flag_get(EXTI_0)) {exti_interrupt_flag_clear(EXTI_0); // 软件清零// 获取当前的时间uint64_t cur_tick = get_tick_us();uint64_t shake_dura = (cur_tick - last_tick) / 1000; // 转为mslast_tick = cur_tick; // 保存上一次的状态if (shake_dura < 20) return; // 少于30ms,不能往下执行,去抖动FlagStatus state = gpio_input_bit_get(KEY);if (DOWN == state) {// 获取起点时间start_tick = get_tick_us();
// printf("按下\n");} else {
// printf("抬起\n");// 获取终点时间uint64_t duration = get_tick_us() - start_tick; // us为单位duration = duration / 1000; // 转换为ms
// printf("duration = %llu ms\n", duration);if (duration <= 30) { // < 30 ms, 不处理,忽略} else if (duration <= 500) {printf("short press\n");} else if (duration <= 2000) {printf("long press\n");}}}
}
2.3 软件外部中断
软件中断无需硬件引脚变化,通过代码手动触发:
#include "gd32f4xx.h"
#include "systick.h"
#include "USART0.h"
#include "EXTI.h"// 收到串口0数据,回调函数
void USART0_on_recv(uint8_t* data, uint32_t len) {printf("recv[%d] = %#x\n", len, data[0]);switch(data[0]){case 0x0: exti_software_interrupt_enable(EXTI_0); break; case 0x1: exti_software_interrupt_enable(EXTI_1); break; case 0x2: exti_software_interrupt_enable(EXTI_2); break; case 0x3: exti_software_interrupt_enable(EXTI_3); break; case 0x4: exti_software_interrupt_enable(EXTI_4); break; case 0x5: exti_software_interrupt_enable(EXTI_5); break; case 0x6: exti_software_interrupt_enable(EXTI_6); break; case 0x7: exti_software_interrupt_enable(EXTI_7); break; case 0x8: exti_software_interrupt_enable(EXTI_8); break; case 0x9: exti_software_interrupt_enable(EXTI_9); break; case 0xA: exti_software_interrupt_enable(EXTI_10); break; case 0xB: exti_software_interrupt_enable(EXTI_11); break; case 0xC: exti_software_interrupt_enable(EXTI_12); break; case 0xD: exti_software_interrupt_enable(EXTI_13); break; case 0xE: exti_software_interrupt_enable(EXTI_14); break; case 0xF: exti_software_interrupt_enable(EXTI_15); break; default: break; }
}void EXTI0_on_trig() {printf("===============EXTI0_on_trig================\n");
}void EXTI1_on_trig() {printf("===============EXTI1_on_trig================\n");
}void EXTI2_on_trig() {printf("===============EXTI2_on_trig================\n");
}void EXTI3_on_trig() {printf("===============EXTI3_on_trig================\n");
}void EXTI4_on_trig() {printf("===============EXTI4_on_trig================\n");
}void EXTI5_on_trig() {printf("===============EXTI5_on_trig================\n");
}void EXTI6_on_trig() {printf("===============EXTI6_on_trig================\n");
}void EXTI7_on_trig() {printf("===============EXTI7_on_trig================\n");
}void EXTI8_on_trig() {printf("===============EXTI8_on_trig================\n");
}void EXTI9_on_trig() {printf("===============EXTI9_on_trig================\n");
}void EXTI10_on_trig() {printf("===============EXTI10_on_trig================\n");
}void EXTI11_on_trig() {printf("===============EXTI11_on_trig================\n");
}void EXTI12_on_trig() {printf("===============EXTI12_on_trig================\n");
}void EXTI13_on_trig() {printf("===============EXTI13_on_trig================\n");
}void EXTI14_on_trig() {printf("===============EXTI14_on_trig================\n");
}void EXTI15_on_trig() {printf("===============EXTI15_on_trig================\n");
}int main(void) {// 抢占 [0, 3] 响应 [0, 3]nvic_priority_group_set(NVIC_PRIGROUP_PRE2_SUB2); // 优先级分组// 系统滴答定时器初始化systick_config();USART0_init(); // 串口初始化调用EXTI_init(); // 外部中断初始化, 包含所有,有宏定义开关while(1) {
// printf("========while(1)========\n");delay_1ms(1000);}
}
三、中断优先级
3.1 优先级理论
NVIC(嵌套向量中断控制器)管理中断优先级:
- 抢占优先级:高优先级可打断低优先级任务(大老板)
- 响应优先级:同抢占级时决定执行顺序(正式工)
- 自然优先级:中断向量表序号,序号越小优先级越高(临时工)
优先级分组:
nvic_priority_group_set(NVIC_PRIGROUP_PRE4_SUB0); // 4位抢占,0位响应
nvic_priority_group_set(NVIC_PRIGROUP_PRE3_SUB1); // 3位抢占,1位响应
nvic_priority_group_set(NVIC_PRIGROUP_PRE2_SUB2); // 2位抢占,2位响应
nvic_priority_group_set(NVIC_PRIGROUP_PRE1_SUB3); // 1位抢占,3位响应
nvic_priority_group_set(NVIC_PRIGROUP_PRE0_SUB4); // 0位抢占,4位响应
3.2 优先级规则
- 抢占不同,响应相同:高抢占级可打断低抢占级任务
- 抢占相同,响应不同:高响应级排前面,不打断只重排序
- 抢占相同,响应相同:比较自然优先级,不打断只重排序
注意:串口中断优先级应设置较高,否则可能无法正常触发接收中断。
四、Library封装
4.1 统一配置接口
EXTI.h
#ifndef __EXTI_H__
#define __EXTI_H__#include "gd32f4xx.h"#include "EXTI_config.h"void EXTI_init();// 非函数指针的回调函数
void EXTI0_on_trig();
void EXTI1_on_trig();
void EXTI2_on_trig();
void EXTI3_on_trig();
void EXTI4_on_trig();
void EXTI5_on_trig();
void EXTI6_on_trig();
void EXTI7_on_trig();
void EXTI8_on_trig();
void EXTI9_on_trig();
void EXTI10_on_trig();
void EXTI11_on_trig();
void EXTI12_on_trig();
void EXTI13_on_trig();
void EXTI14_on_trig();
void EXTI15_on_trig();#endif
EXTI.c
#include "EXTI.h"
#include "gpio_cfg.h"
#include <stdio.h>/*********************************************************** @brief 配置外部中断引脚* @param rcu 外设时钟控制器,指定要启用的外设时钟* @param port GPIO端口,指定要配置的GPIO端口* @param pin GPIO引脚,指定要配置的GPIO引脚* @param pull_up_down 上拉/下拉配置,指定GPIO引脚的上拉或下拉电阻配置* @param exti_port EXTI端口,指定要配置的外部中断端口* @param exti_pin EXTI引脚,指定要配置的外部中断引脚* @param linex EXTI线,指定要配置的外部中断线* @param trig_type 触发类型,指定中断触发的类型(上升沿、下降沿、双边沿)* @param nvic_irq NVIC中断,指定要启用的NVIC中断* @param nvic_irq_pre_priority NVIC抢占优先级,指定NVIC中断的抢占优先级* @param nvic_irq_sub_priority NVIC子优先级,指定NVIC中断的子优先级* @return 无**********************************************************/
static void EXTI_config(rcu_periph_enum rcu, uint32_t port, uint32_t pin, uint32_t pull_up_down, uint8_t exti_port, uint8_t exti_pin, exti_line_enum linex, exti_trig_type_enum trig_type,uint8_t nvic_irq, uint8_t nvic_irq_pre_priority, uint8_t nvic_irq_sub_priority) {// ================== 引脚初始化 浮空输入GPIO_input(rcu, port, pin, pull_up_down);// ================== EXTI 配置// 时钟使能 syscfgrcu_periph_clock_enable(RCU_SYSCFG);// 中断线连接 syscfgsyscfg_exti_line_config(exti_port, exti_pin);// exti 初始化// 中断线 模式:中断 触发类型(条件)exti_init(linex, EXTI_INTERRUPT, trig_type);// 使能外部中断exti_interrupt_enable(linex);// 中断优先级,使能中断请求 nvic_irq_enable在misc.h中// 参数1: 中断类型枚举常量, 在 gd32f4xx.h 拷贝// 参数2: 抢占优先级// 参数3: 响应优先级nvic_irq_enable(nvic_irq, nvic_irq_pre_priority, nvic_irq_sub_priority);}void EXTI_init(void) {
#if USE_EXTI_0EXTI_config(EXTI0_RCU, EXTI0_PORT, GPIO_PIN_0, EXTI0_PUPD,EXTI0_SOURCE_PORT, EXTI_SOURCE_PIN0, EXTI_0, EXTI0_TRIG_TYPE,EXTI0_IRQn, EXTI0_PRIORITY);
#endif#if USE_EXTI_1EXTI_config(EXTI1_RCU, EXTI1_PORT, GPIO_PIN_1, EXTI1_PUPD,EXTI1_SOURCE_PORT, EXTI_SOURCE_PIN1, EXTI_1, EXTI1_TRIG_TYPE,EXTI1_IRQn, EXTI1_PRIORITY);
#endif#if USE_EXTI_2EXTI_config(EXTI2_RCU, EXTI2_PORT, GPIO_PIN_2, EXTI2_PUPD,EXTI2_SOURCE_PORT, EXTI_SOURCE_PIN2, EXTI_2, EXTI2_TRIG_TYPE,EXTI2_IRQn, EXTI2_PRIORITY);
#endif#if USE_EXTI_3EXTI_config(EXTI3_RCU, EXTI3_PORT, GPIO_PIN_3, EXTI3_PUPD,EXTI3_SOURCE_PORT, EXTI_SOURCE_PIN3, EXTI_3, EXTI3_TRIG_TYPE,EXTI3_IRQn, EXTI3_PRIORITY);
#endif#if USE_EXTI_4EXTI_config(EXTI4_RCU, EXTI4_PORT, GPIO_PIN_4, EXTI4_PUPD,EXTI4_SOURCE_PORT, EXTI_SOURCE_PIN4, EXTI_4, EXTI4_TRIG_TYPE,EXTI4_IRQn, EXTI4_PRIORITY);
#endif#if USE_EXTI_5EXTI_config(EXTI5_RCU, EXTI5_PORT, GPIO_PIN_5, EXTI5_PUPD,EXTI5_SOURCE_PORT, EXTI_SOURCE_PIN5, EXTI_5, EXTI5_TRIG_TYPE,EXTI5_9_IRQn, EXTI5_PRIORITY);
#endif#if USE_EXTI_6EXTI_config(EXTI6_RCU, EXTI6_PORT, GPIO_PIN_6, EXTI6_PUPD,EXTI6_SOURCE_PORT, EXTI_SOURCE_PIN6, EXTI_6, EXTI6_TRIG_TYPE,EXTI5_9_IRQn, EXTI6_PRIORITY);
#endif#if USE_EXTI_7EXTI_config(EXTI7_RCU, EXTI7_PORT, GPIO_PIN_7, EXTI7_PUPD,EXTI7_SOURCE_PORT, EXTI_SOURCE_PIN7, EXTI_7, EXTI7_TRIG_TYPE,EXTI5_9_IRQn, EXTI7_PRIORITY);
#endif#if USE_EXTI_8EXTI_config(EXTI8_RCU, EXTI8_PORT, GPIO_PIN_8, EXTI8_PUPD,EXTI8_SOURCE_PORT, EXTI_SOURCE_PIN8, EXTI_8, EXTI8_TRIG_TYPE,EXTI5_9_IRQn, EXTI8_PRIORITY);
#endif#if USE_EXTI_9EXTI_config(EXTI9_RCU, EXTI9_PORT, GPIO_PIN_9, EXTI9_PUPD,EXTI9_SOURCE_PORT, EXTI_SOURCE_PIN9, EXTI_9, EXTI9_TRIG_TYPE,EXTI5_9_IRQn, EXTI9_PRIORITY);
#endif#if USE_EXTI_10EXTI_config(EXTI10_RCU, EXTI10_PORT, GPIO_PIN_10, EXTI10_PUPD,EXTI10_SOURCE_PORT, EXTI_SOURCE_PIN10, EXTI_10, EXTI10_TRIG_TYPE,EXTI10_15_IRQn, EXTI10_PRIORITY);
#endif#if USE_EXTI_11EXTI_config(EXTI11_RCU, EXTI11_PORT, GPIO_PIN_11, EXTI11_PUPD,EXTI11_SOURCE_PORT, EXTI_SOURCE_PIN11, EXTI_11, EXTI11_TRIG_TYPE,EXTI10_15_IRQn, EXTI11_PRIORITY);
#endif#if USE_EXTI_12EXTI_config(EXTI12_RCU, EXTI12_PORT, GPIO_PIN_12, EXTI12_PUPD,EXTI12_SOURCE_PORT, EXTI_SOURCE_PIN12, EXTI_12, EXTI12_TRIG_TYPE,EXTI10_15_IRQn, EXTI12_PRIORITY);
#endif#if USE_EXTI_13EXTI_config(EXTI13_RCU, EXTI13_PORT, GPIO_PIN_13, EXTI13_PUPD,EXTI13_SOURCE_PORT, EXTI_SOURCE_PIN13, EXTI_13, EXTI13_TRIG_TYPE,EXTI10_15_IRQn, EXTI13_PRIORITY);
#endif#if USE_EXTI_14EXTI_config(EXTI14_RCU, EXTI14_PORT, GPIO_PIN_14, EXTI14_PUPD,EXTI14_SOURCE_PORT, EXTI_SOURCE_PIN14, EXTI_14, EXTI14_TRIG_TYPE,EXTI10_15_IRQn, EXTI14_PRIORITY);
#endif#if USE_EXTI_15EXTI_config(EXTI15_RCU, EXTI15_PORT, GPIO_PIN_15, EXTI15_PUPD,EXTI15_SOURCE_PORT, EXTI_SOURCE_PIN15, EXTI_15, EXTI15_TRIG_TYPE,EXTI10_15_IRQn, EXTI15_PRIORITY);
#endif
}#if USE_EXTI_0
// 外部中断0处理函数 启动汇编文件的第93行拷贝名字
void EXTI0_IRQHandler() {if (SET == exti_interrupt_flag_get(EXTI_0)) {exti_interrupt_flag_clear(EXTI_0); // 软件清零EXTI0_on_trig();}
}
#endif#if USE_EXTI_1
// 外部中断1处理函数
void EXTI1_IRQHandler() {if (SET == exti_interrupt_flag_get(EXTI_1)) {exti_interrupt_flag_clear(EXTI_1); // 软件清零EXTI1_on_trig();}
}
#endif#if USE_EXTI_2
// 外部中断2处理函数
void EXTI2_IRQHandler() {if (SET == exti_interrupt_flag_get(EXTI_2)) {exti_interrupt_flag_clear(EXTI_2); // 软件清零EXTI2_on_trig();}
}
#endif#if USE_EXTI_3
// 外部中断3处理函数
void EXTI3_IRQHandler() {if (SET == exti_interrupt_flag_get(EXTI_3)) {exti_interrupt_flag_clear(EXTI_3); // 软件清零EXTI3_on_trig();}
}
#endif#if USE_EXTI_4
// 外部中断4处理函数
void EXTI4_IRQHandler() {if (SET == exti_interrupt_flag_get(EXTI_4)) {exti_interrupt_flag_clear(EXTI_4); // 软件清零EXTI4_on_trig();}
}
#endif#if USE_EXTI_5 || USE_EXTI_6 || USE_EXTI_7 || USE_EXTI_8 || USE_EXTI_9
// 外部中断5~9处理函数
void EXTI5_9_IRQHandler() {#if USE_EXTI_5if (SET == exti_interrupt_flag_get(EXTI_5)) {exti_interrupt_flag_clear(EXTI_5); // 软件清零EXTI5_on_trig();}#endif#if USE_EXTI_6if (SET == exti_interrupt_flag_get(EXTI_6)) {exti_interrupt_flag_clear(EXTI_6); // 软件清零EXTI6_on_trig();}#endif#if USE_EXTI_7if (SET == exti_interrupt_flag_get(EXTI_7)) {exti_interrupt_flag_clear(EXTI_7); // 软件清零EXTI7_on_trig();}#endif#if USE_EXTI_8if (SET == exti_interrupt_flag_get(EXTI_8)) {exti_interrupt_flag_clear(EXTI_8); // 软件清零EXTI8_on_trig();}#endif#if USE_EXTI_9if (SET == exti_interrupt_flag_get(EXTI_9)) {exti_interrupt_flag_clear(EXTI_9); // 软件清零EXTI9_on_trig();}#endif
}
#endif#if USE_EXTI_10 || USE_EXTI_11 || USE_EXTI_12 || USE_EXTI_13 || USE_EXTI_14 || USE_EXTI_15
// 外部中断10~15处理函数
void EXTI10_15_IRQHandler() {#if USE_EXTI_10if (SET == exti_interrupt_flag_get(EXTI_10)) {exti_interrupt_flag_clear(EXTI_10); // 软件清零EXTI10_on_trig();}#endif#if USE_EXTI_11if (SET == exti_interrupt_flag_get(EXTI_11)) {exti_interrupt_flag_clear(EXTI_11); // 软件清零EXTI11_on_trig();}#endif#if USE_EXTI_12if (SET == exti_interrupt_flag_get(EXTI_12)) {exti_interrupt_flag_clear(EXTI_12); // 软件清零EXTI12_on_trig();}#endif#if USE_EXTI_13if (SET == exti_interrupt_flag_get(EXTI_13)) {exti_interrupt_flag_clear(EXTI_13); // 软件清零EXTI13_on_trig();}#endif#if USE_EXTI_14if (SET == exti_interrupt_flag_get(EXTI_14)) {exti_interrupt_flag_clear(EXTI_14); // 软件清零EXTI14_on_trig();}#endif#if USE_EXTI_15if (SET == exti_interrupt_flag_get(EXTI_15)) {exti_interrupt_flag_clear(EXTI_15); // 软件清零EXTI15_on_trig();}#endif
}
#endif
4.2 配置信息提取
通过配置文件管理所有外部中断参数:
#ifndef __EXTI_CONFIG_H__
#define __EXTI_CONFIG_H__#include "gd32f4xx.h"
//中断的开关1是开,0是关
#define USE_EXTI_0 1
#define USE_EXTI_1 0
#define USE_EXTI_2 0
#define USE_EXTI_3 1
#define USE_EXTI_4 0
#define USE_EXTI_5 0
#define USE_EXTI_6 0
#define USE_EXTI_7 0
#define USE_EXTI_8 0
#define USE_EXTI_9 0
#define USE_EXTI_10 0
#define USE_EXTI_11 0
#define USE_EXTI_12 0
#define USE_EXTI_13 0
#define USE_EXTI_14 0
#define USE_EXTI_15 0#if USE_EXTI_0#define EXTI0_RCU RCU_GPIOA#define EXTI0_PORT GPIOA#define EXTI0_PUPD GPIO_PUPD_NONE#define EXTI0_SOURCE_PORT EXTI_SOURCE_GPIOA#define EXTI0_TRIG_TYPE EXTI_TRIG_BOTH#define EXTI0_PRIORITY 2, 2
#endif#if USE_EXTI_1#define EXTI1_RCU RCU_GPIOC#define EXTI1_PORT GPIOC #define EXTI1_PUPD GPIO_PUPD_PULLUP#define EXTI1_SOURCE_PORT EXTI_SOURCE_GPIOC#define EXTI1_TRIG_TYPE EXTI_TRIG_BOTH#define EXTI1_PRIORITY 2, 2
#endif#if USE_EXTI_2#define EXTI2_RCU RCU_GPIOC#define EXTI2_PORT GPIOC#define EXTI2_PUPD GPIO_PUPD_PULLUP#define EXTI2_SOURCE_PORT EXTI_SOURCE_GPIOC#define EXTI2_TRIG_TYPE EXTI_TRIG_BOTH#define EXTI2_PRIORITY 2, 2
#endif#if USE_EXTI_3#define EXTI3_RCU RCU_GPIOC#define EXTI3_PORT GPIOC#define EXTI3_PUPD GPIO_PUPD_PULLUP#define EXTI3_SOURCE_PORT EXTI_SOURCE_GPIOC#define EXTI3_TRIG_TYPE EXTI_TRIG_BOTH#define EXTI3_PRIORITY 2, 2
#endif#if USE_EXTI_4#define EXTI4_RCU RCU_GPIOC#define EXTI4_PORT GPIOC#define EXTI4_PUPD GPIO_PUPD_PULLUP#define EXTI4_SOURCE_PORT EXTI_SOURCE_GPIOC#define EXTI4_TRIG_TYPE EXTI_TRIG_BOTH#define EXTI4_PRIORITY 2, 2
#endif#if USE_EXTI_5#define EXTI5_RCU RCU_GPIOC#define EXTI5_PORT GPIOC#define EXTI5_PUPD GPIO_PUPD_PULLUP#define EXTI5_SOURCE_PORT EXTI_SOURCE_GPIOC#define EXTI5_TRIG_TYPE EXTI_TRIG_BOTH#define EXTI5_PRIORITY 2, 2
#endif#if USE_EXTI_6#define EXTI6_RCU RCU_GPIOC#define EXTI6_PORT GPIOC#define EXTI6_PUPD GPIO_PUPD_PULLUP#define EXTI6_SOURCE_PORT EXTI_SOURCE_GPIOC#define EXTI6_TRIG_TYPE EXTI_TRIG_BOTH#define EXTI6_PRIORITY 2, 2
#endif#if USE_EXTI_7#define EXTI7_RCU RCU_GPIOC#define EXTI7_PORT GPIOC#define EXTI7_PUPD GPIO_PUPD_PULLUP#define EXTI7_SOURCE_PORT EXTI_SOURCE_GPIOC#define EXTI7_TRIG_TYPE EXTI_TRIG_BOTH#define EXTI7_PRIORITY 2, 2
#endif#if USE_EXTI_8#define EXTI8_RCU RCU_GPIOC#define EXTI8_PORT GPIOC#define EXTI8_PUPD GPIO_PUPD_PULLUP#define EXTI8_SOURCE_PORT EXTI_SOURCE_GPIOC#define EXTI8_TRIG_TYPE EXTI_TRIG_BOTH#define EXTI8_PRIORITY 2, 2
#endif#if USE_EXTI_9#define EXTI9_RCU RCU_GPIOC#define EXTI9_PORT GPIOC#define EXTI9_PUPD GPIO_PUPD_PULLUP#define EXTI9_SOURCE_PORT EXTI_SOURCE_GPIOC#define EXTI9_TRIG_TYPE EXTI_TRIG_BOTH#define EXTI9_PRIORITY 2, 2
#endif#if USE_EXTI_10#define EXTI10_RCU RCU_GPIOC#define EXTI10_PORT GPIOC#define EXTI10_PUPD GPIO_PUPD_PULLUP#define EXTI10_SOURCE_PORT EXTI_SOURCE_GPIOC#define EXTI10_TRIG_TYPE EXTI_TRIG_BOTH#define EXTI10_PRIORITY 2, 2
#endif#if USE_EXTI_11#define EXTI11_RCU RCU_GPIOC#define EXTI11_PORT GPIOC#define EXTI11_PUPD GPIO_PUPD_PULLUP#define EXTI11_SOURCE_PORT EXTI_SOURCE_GPIOC#define EXTI11_TRIG_TYPE EXTI_TRIG_BOTH#define EXTI11_PRIORITY 2, 2
#endif#if USE_EXTI_12#define EXTI12_RCU RCU_GPIOC#define EXTI12_PORT GPIOC#define EXTI12_PUPD GPIO_PUPD_PULLUP#define EXTI12_SOURCE_PORT EXTI_SOURCE_GPIOC#define EXTI12_TRIG_TYPE EXTI_TRIG_BOTH#define EXTI12_PRIORITY 2, 2
#endif#if USE_EXTI_13#define EXTI13_RCU RCU_GPIOC#define EXTI13_PORT GPIOC#define EXTI13_PUPD GPIO_PUPD_PULLUP#define EXTI13_SOURCE_PORT EXTI_SOURCE_GPIOC#define EXTI13_TRIG_TYPE EXTI_TRIG_BOTH#define EXTI13_PRIORITY 2, 2
#endif#if USE_EXTI_14#define EXTI14_RCU RCU_GPIOC#define EXTI14_PORT GPIOC#define EXTI14_PUPD GPIO_PUPD_PULLUP#define EXTI14_SOURCE_PORT EXTI_SOURCE_GPIOC#define EXTI14_TRIG_TYPE EXTI_TRIG_BOTH#define EXTI14_PRIORITY 2, 2
#endif#if USE_EXTI_15#define EXTI15_RCU RCU_GPIOC#define EXTI15_PORT GPIOC#define EXTI15_PUPD GPIO_PUPD_PULLUP#define EXTI15_SOURCE_PORT EXTI_SOURCE_GPIOC#define EXTI15_TRIG_TYPE EXTI_TRIG_BOTH#define EXTI15_PRIORITY 2, 2
#endif
#endif
函数解读
/* 将EXTI恢复为默认状态(复位) */
void exti_deinit(void);
/* 初始化EXTI线x */
void exti_init(exti_line_enum linex, exti_mode_enum mode, exti_trig_type_enum trig_type);
/* 使能EXTI线x的中断 */
void exti_interrupt_enable(exti_line_enum linex);
/* 禁用EXTI线x的中断 */
void exti_interrupt_disable(exti_line_enum linex);
/* 使能EXTI线x的事件 */
void exti_event_enable(exti_line_enum linex);
/* 禁用EXTI线x的事件 */
void exti_event_disable(exti_line_enum linex);
/* 使能EXTI线x的软件中断事件 */
void exti_software_interrupt_enable(exti_line_enum linex);
/* 禁用EXTI线x的软件中断事件 */
void exti_software_interrupt_disable(exti_line_enum linex);/* 中断与标志函数 */
/* 获取EXTI线x的中断挂起标志 */
FlagStatus exti_flag_get(exti_line_enum linex);
/* 清除EXTI线x的中断挂起标志 */
void exti_flag_clear(exti_line_enum linex);
/* 获取EXTI线x的中断挂起标志 */
FlagStatus exti_interrupt_flag_get(exti_line_enum linex);
/* 清除EXTI线x的中断挂起标志 */
void exti_interrupt_flag_clear(exti_line_enum linex);
函数作用详解
EXTI(External Interrupt/Event Controller,外部中断/事件控制器)是MCU用于处理外部引脚触发信号的核心外设,可实现“外部信号→中断/事件”的转换。以下是各函数的具体作用:
一、初始化与复位函数(基础配置)
-
exti_deinit
- 作用:将EXTI控制器的所有配置恢复为复位默认状态(清除所有中断线的模式、触发方式等配置)。
- 用途:初始化EXTI前的“清零操作”,确保配置从默认状态开始,避免残留配置影响新功能。
-
exti_init
- 作用:配置指定EXTI线(
linex
)的工作模式和触发类型,是EXTI功能的核心配置函数。 - 参数说明:
linex
:指定要配置的中断线(如EXTI_Line0
对应GPIO引脚的第0号线,需与GPIO复用映射配合);mode
:工作模式(EXTI_MODE_INTERRUPT
中断模式/EXTI_MODE_EVENT
事件模式);trig_type
:触发类型(EXTI_TRIG_RISING
上升沿/EXTI_TRIG_FALLING
下降沿/EXTI_TRIG_BOTH
双边沿)。
- 示例:配置EXTI线0为“上升沿触发的中断模式”,用于检测按键按下(按键按下时引脚电平从低到高跳变)。
- 作用:配置指定EXTI线(
二、中断与事件控制函数(功能开关)
EXTI的“中断”和“事件”是两个核心概念:
- 中断:触发后会向CPU发送中断请求,CPU会暂停当前任务转而执行中断服务函数(需配置NVIC);
- 事件:触发后仅会产生一个内部脉冲信号,可用于触发其他外设(如DMA、定时器),不会打断CPU运行。
-
exti_interrupt_enable / disable
- 作用:单独使能/禁用指定EXTI线的“中断功能”(即使
exti_init
配置了中断模式,也需调用此函数使能才会生效)。
- 作用:单独使能/禁用指定EXTI线的“中断功能”(即使
-
exti_event_enable / disable
- 作用:单独使能/禁用指定EXTI线的“事件功能”(与中断功能独立,可同时开启或仅开启其一)。
- 应用场景:如通过EXTI事件触发ADC采样(无需CPU干预,提高效率)。
三、软件中断控制函数(软件触发)
除了外部引脚信号,EXTI还支持通过软件主动触发中断/事件,用于模拟外部信号或软件控制流程。
- exti_software_interrupt_enable / disable
- 作用:通过软件主动触发/取消触发指定EXTI线的中断/事件(无论外部引脚是否有信号)。
- 用途:调试时模拟外部触发信号,或在特定软件逻辑中主动触发中断(如定时检查外部设备状态)。
四、标志与中断函数(状态查询与清除)
EXTI触发后会设置对应的“挂起标志位”,需通过函数查询状态或清除标志,避免重复触发。
-
exti_flag_get
- 作用:获取指定EXTI线的“事件标志位”状态(
SET
表示已触发事件,RESET
表示未触发)。 - 用途:查询方式判断事件是否发生(适用于事件模式)。
- 作用:获取指定EXTI线的“事件标志位”状态(
-
exti_flag_clear
- 作用:清除指定EXTI线的“事件标志位”(事件触发后标志位会保持
SET
,需手动清除才能检测下一次触发)。
- 作用:清除指定EXTI线的“事件标志位”(事件触发后标志位会保持
-
exti_interrupt_flag_get
- 作用:获取指定EXTI线的“中断标志位”状态(
SET
表示已触发中断,RESET
表示未触发)。 - 用途:在中断服务函数中判断是否是当前EXTI线触发的中断(避免误处理)。
- 作用:获取指定EXTI线的“中断标志位”状态(
-
exti_interrupt_flag_clear
- 作用:清除指定EXTI线的“中断标志位”(中断触发后标志位会保持
SET
,若不清除,会重复进入中断服务函数)。 - 注意:必须在中断服务函数中调用,否则中断会无限触发。
- 作用:清除指定EXTI线的“中断标志位”(中断触发后标志位会保持
总结
EXTI函数的核心逻辑是“配置→使能→触发→查询/清除标志”,关键是区分“中断”和“事件”的不同用途:
- 需CPU处理的场景(如按键响应、异常处理)用中断模式,配合
exti_interrupt_enable
和中断标志函数; - 无需CPU干预的外设联动(如信号触发采样)用事件模式,配合
exti_event_enable
和事件标志函数; - 软件模拟外部信号用软件中断函数,灵活控制触发时机。
掌握这些函数可实现对外部信号的高效响应,是嵌入式开发中处理按键、传感器等外设的基础。