5.11 EXTI 使用说明
本章说明 N32H49x 标准外设库中 EXTI 驱动的文件组成、9 个公开函数,以及 KeyInterrupt 一个示例。中断线与事件线的硬件细节、GPIO 线与 EXTI 线的映射关系请配合用户手册的“EXTI”章节阅读。
5.11.1 相关文件
| 相对路径 | 作用 |
|---|---|
firmware/n32h49x_std_periph_driver/inc/n32h49x_exti.h |
定义 EXTI 模式、触发边沿枚举与 EXTI_InitType 结构体,以及 EXTI 线与时间戳线选择宏,并按初始化、软件触发、状态标志、中断、时间戳五个区段声明 9 个公开函数 |
firmware/n32h49x_std_periph_driver/src/n32h49x_exti.c |
实现上述 9 个函数:寄存器复位,屏蔽与触发边沿写入,软件中断触发,状态标志与中断挂起位读写,时间戳输入选择 |
projects/n32h49x_EVAL/examples/EXTI/ |
EXTI 示例根目录,含 KeyInterrupt 1 个示例 |
projects/n32h49x_EVAL/examples/EXTI/KeyInterrupt/ |
该示例的工程目录组成:MDK-ARM/、EWARM/、inc/、src/、readme.txt |
projects/n32h49x_EVAL/bsp/ |
该示例使用的板级支持文件:日志初始化与打印串口重映射 |
5.11.2 API 参考
以下 9 个公开函数按 n32h49x_exti.h 的声明顺序分为 5 组;每个条目给出功能摘要与 n32h49x_exti.c 中该函数正上方的注释原文。
初始化
EXTI_DeInit、EXTI_InitPeripheral、EXTI_InitStruct
软件触发
EXTI_TriggerSWInt
状态标志
EXTI_GetStatusFlag、EXTI_ClrStatusFlag
中断
EXTI_GetITStatus、EXTI_ClrITPendBit
时间戳
EXTI_RTCTimeStampSel
void EXTI_DeInit(void)将 EXTI 寄存器复位。
name EXTI_DeInit.
fun Reset the EXTI registers.
return none
void EXTI_InitPeripheral(EXTI_InitType* EXTI_InitStruct)按 EXTI_InitStruct 结构体的值初始化 EXTI。
name EXTI_InitPeripheral.
fun Initializes the EXTI according to EXTI_InitStruct.
param EXTI_InitStruct(The input parameters must be the following values) :
- EXTI_Line
- EXTI_LINE0
- EXTI_LINE1
- EXTI_LINE2
- EXTI_LINE3
- EXTI_LINE4
- EXTI_LINE5
- EXTI_LINE6
- EXTI_LINE7
- EXTI_LINE8
- EXTI_LINE9
- EXTI_LINE10
- EXTI_LINE11
- EXTI_LINE12
- EXTI_LINE13
- EXTI_LINE14
- EXTI_LINE15
- EXTI_LINE16
- EXTI_LINE17
- EXTI_LINE18
- EXTI_LINE19
- EXTI_LINE20
- EXTI_LINE21
- EXTI_LINE22
- EXTI_LINE23
- EXTI_LINE24
- EXTI_Mode
- EXTI_Mode_Event
- EXTI_Mode_Interrupt
- EXTI_Trigger
- EXTI_Trigger_Falling
- EXTI_Trigger_Rising
- EXTI_Trigger_Rising_Falling
- EXTI_LineCmd:
- ENABLE
- DISABLE
return none
void EXTI_InitStruct(EXTI_InitType* EXTI_InitStruct)将 EXTI_InitStruct 各成员填充为默认值。
name EXTI_InitStruct.
fun Fills each EXTI_InitStruct member with its default value.
param InitStruct :
- Pointer to the EXTI_InitType structure which will be initialized.
return none
void EXTI_TriggerSWInt(uint32_t EXTI_Line)产生一次软件中断。
name EXTI_TriggerSWInt.
fun Generates a Software interrupt.
param EXTI_Line(The input parameters must be the following values) :
- EXTI_LINE0
- EXTI_LINE1
- EXTI_LINE2
- EXTI_LINE3
- EXTI_LINE4
- EXTI_LINE5
- EXTI_LINE6
- EXTI_LINE7
- EXTI_LINE8
- EXTI_LINE9
- EXTI_LINE10
- EXTI_LINE11
- EXTI_LINE12
- EXTI_LINE13
- EXTI_LINE14
- EXTI_LINE15
- EXTI_LINE16
- EXTI_LINE17
- EXTI_LINE18
- EXTI_LINE19
- EXTI_LINE20
- EXTI_LINE21
- EXTI_LINE22
- EXTI_LINE23
- EXTI_LINE24
return none
FlagStatus EXTI_GetStatusFlag(uint32_t EXTI_Line)获取 EXTI 线标志位状态。
name EXTI_GetStatusFlag.
fun Get EXTI line flag status.
param EXTI_Line(The input parameters must be the following values) :
- EXTI_LINE0
- EXTI_LINE1
- EXTI_LINE2
- EXTI_LINE3
- EXTI_LINE4
- EXTI_LINE5
- EXTI_LINE6
- EXTI_LINE7
- EXTI_LINE8
- EXTI_LINE9
- EXTI_LINE10
- EXTI_LINE11
- EXTI_LINE12
- EXTI_LINE13
- EXTI_LINE14
- EXTI_LINE15
- EXTI_LINE16
- EXTI_LINE17
- EXTI_LINE18
- EXTI_LINE19
- EXTI_LINE20
- EXTI_LINE21
- EXTI_LINE22
- EXTI_LINE23
- EXTI_LINE24
return SET or RESET
void EXTI_ClrStatusFlag(uint32_t EXTI_Line)清除 EXTI 线标志位状态。
name EXTI_ClrStatusFlag.
fun Clear EXTI line flag status.
param EXTI_Line :
- EXTI_LINE0
- EXTI_LINE1
- EXTI_LINE2
- EXTI_LINE3
- EXTI_LINE4
- EXTI_LINE5
- EXTI_LINE6
- EXTI_LINE7
- EXTI_LINE8
- EXTI_LINE9
- EXTI_LINE10
- EXTI_LINE11
- EXTI_LINE12
- EXTI_LINE13
- EXTI_LINE14
- EXTI_LINE15
- EXTI_LINE16
- EXTI_LINE17
- EXTI_LINE18
- EXTI_LINE19
- EXTI_LINE20
- EXTI_LINE21
- EXTI_LINE22
- EXTI_LINE23
- EXTI_LINE24
return none
INTStatus EXTI_GetITStatus(uint32_t EXTI_Line)获取 EXTI 线中断状态。
name EXTI_GetITStatus.
fun GET EXTI line interrupt status.
param exti_line :
- EXTI_LINE0
- EXTI_LINE1
- EXTI_LINE2
- EXTI_LINE3
- EXTI_LINE4
- EXTI_LINE5
- EXTI_LINE6
- EXTI_LINE7
- EXTI_LINE8
- EXTI_LINE9
- EXTI_LINE10
- EXTI_LINE11
- EXTI_LINE12
- EXTI_LINE13
- EXTI_LINE14
- EXTI_LINE15
- EXTI_LINE16
- EXTI_LINE17
- EXTI_LINE18
- EXTI_LINE19
- EXTI_LINE20
- EXTI_LINE21
- EXTI_LINE22
- EXTI_LINE23
- EXTI_LINE24
return SET or RESET
void EXTI_ClrITPendBit(uint32_t EXTI_Line)清除 EXTI 线中断挂起位。
name EXTI_ClrITPendBit.
fun Clear EXTI line interrupt pend bit.
param exti_line :
- EXTI_LINE0
- EXTI_LINE1
- EXTI_LINE2
- EXTI_LINE3
- EXTI_LINE4
- EXTI_LINE5
- EXTI_LINE6
- EXTI_LINE7
- EXTI_LINE8
- EXTI_LINE9
- EXTI_LINE10
- EXTI_LINE11
- EXTI_LINE12
- EXTI_LINE13
- EXTI_LINE14
- EXTI_LINE15
- EXTI_LINE16
- EXTI_LINE17
- EXTI_LINE18
- EXTI_LINE19
- EXTI_LINE20
- EXTI_LINE21
- EXTI_LINE22
- EXTI_LINE23
- EXTI_LINE24
return none
void EXTI_RTCTimeStampSel(uint32_t EXTI_TSSEL_Line)选择 TimeStamp 事件的输入。
name EXTI_RTCTimeStampSel.
fun Select the input of TimeStamp event.
param EXTI_TSSEL_Line :
- EXTI_TSSEL_LINE0
- EXTI_TSSEL_LINE1
- EXTI_TSSEL_LINE2
- EXTI_TSSEL_LINE3
- EXTI_TSSEL_LINE4
- EXTI_TSSEL_LINE5
- EXTI_TSSEL_LINE6
- EXTI_TSSEL_LINE7
- EXTI_TSSEL_LINE8
- EXTI_TSSEL_LINE9
- EXTI_TSSEL_LINE10
- EXTI_TSSEL_LINE11
- EXTI_TSSEL_LINE12
- EXTI_TSSEL_LINE13
- EXTI_TSSEL_LINE14
- EXTI_TSSEL_LINE15
return none
5.11.3 Demo 示例说明
本节说明 EXTI 的 1 个示例:KeyInterrupt 用一条外部中断线在两条按键引脚之间轮换输入源,并用 LED 与串口提示当前输入源。
5.11.3.1 KeyInterrupt 按键外部中断
工程目录: projects/n32h49x_EVAL/examples/EXTI/KeyInterrupt/
初始化打印串口、两路 LED 与两路按键→把外部中断 0 配置为下降沿触发并使能其中断向量→按键在 PC13 或 PA15 上出现下降沿时进入中断→中断中熄灭当前 LED 并把中断输入源切换到另一路按键→主循环按当前输入源闪烁对应 LED→串口打印当前输入源,交替循环
| 项目 | 源码解读 |
|---|---|
| 功能 | 以 EXTI0 为例演示 GPIO 外部中断:默认由 KEY1(PC13)的下降沿触发 EXTI0 中断; 每次进入中断把 EXTI0 输入源在 KEY1 与 KEY2 之间切换一次,串口同步打印当前输入源,闪烁的 LED 随输入源变化。 |
| 源码组成 | src/main.c:main、LED 与按键的 LED_Init、KEY_Init 以及 EXTI0_Init;src/n32h49x_it.c:EXTI0_IRQHandler 中断服务函数;inc/main.h:LED 与按键的端口、引脚、时钟宏与函数声明;readme.txt:功能、环境与使用说明;MDK-ARM/、EWARM/:Keil 与 IAR 工程。 |
| 关键配置 | LED1 为 PA3、LED2 为 PB3,推挽输出; KEY1 为 PC13、KEY2 为 PA15,输入上拉。 EXTI0 配置为 EXTI_Mode_Interrupt、EXTI_Trigger_Falling;EXTI0 输入源由 GPIO_ConfigEXTILine 在 PC13 与 PA15 之间切换。系统时钟为 HSI+PLL,240 MHz。 |
| 程序与数据路径 | main 调用 log_init 并打印 EXTI key interrupt demo!,初始化两路 LED 与两路按键后调用 EXTI0_Init,把 EXTI0 输入源接到 KEY1;主循环中当 exti_int 置位时打印 Current EXTI0 input source is:%s,并按 key_cnt 闪烁对应 LED,间隔为 SystemCoreClock/8。 |
| 主要 API | EXTI_InitStruct、EXTI_InitPeripheral、GPIO_ConfigEXTILine、EXTI_GetStatusFlag、EXTI_ClrStatusFlag |
| 验证方法 | 复位运行后串口打印 EXTI key interrupt demo!;默认按下 KEY1 触发 EXTI0 中断,此后输入源切换到 KEY2,只有 KEY2 能再次触发; 每次触发后闪烁的 LED 在 LED1 与 LED2 之间切换,串口提示当前输入源。 |
| 使用提示 | 中断服务函数中先读 EXTI_GetStatusFlag 判断状态,再用 EXTI_ClrStatusFlag 清除挂起位;切换输入源后原引脚不再触发中断,需再次调用 GPIO_ConfigEXTILine 才会改用新引脚。开发环境为 KEIL MDK-ARM V5.34 或 IAR EWARM 8.50.1。 |
核心代码解读与移植
示例用一个中断服务函数在两条按键引脚之间轮换 EXTI0 的输入源,并让 LED 跟随当前输入源闪烁。
数据路径: KEY1(PC13)或 KEY2(PA15)的下降沿 → GPIO_ConfigEXTILine 选定 EXTI0 输入源 → EXTI_InitPeripheral 使能 EXTI0 下降沿中断 → 进入 EXTI0_IRQHandler → EXTI_GetStatusFlag 判状态、EXTI_ClrStatusFlag 清标志 → 把输入源切换到下一路按键 → 主循环按 key_cnt 闪烁对应 LED 并打印输入源
1. 用宏与数组集中定义 LED 与按键。 引脚与端口都在 inc/main.h 中定义,示例通过 LEDx_PORT、LEDx_PIN、KEYx_PORT、KEYx_PIN 访问,按键的端口源与引脚源另用数组登记,便于在中断里切换。
/* User LED define */
#define LED1_PORT GPIOA
#define LED1_PIN GPIO_PIN_3
#define LED1_CLOCK RCC_AHB_PERIPHEN_GPIOA
#define LED2_PORT GPIOB
#define LED2_PIN GPIO_PIN_3
#define LED2_CLOCK RCC_AHB_PERIPHEN_GPIOB
/* User KEY define */
#define KEY1_PORT GPIOC
#define KEY1_PIN GPIO_PIN_13
#define KEY1_CLOCK RCC_AHB_PERIPHEN_GPIOC
#define KEY2_PORT GPIOA
#define KEY2_PIN GPIO_PIN_15
#define KEY2_CLOCK RCC_AHB_PERIPHEN_GPIOA
更换板卡或按键引脚时,只需修改这组宏以及 src/main.c 中的 KeyPortSrc、KeyPinSrc 数组,示例逻辑不用改动。
2. 把 EXTI0 配为下降沿中断并绑定输入源。 EXTI0_Init 填写 EXTI_InitType 后使能 EXTI0 下降沿中断,同时配置 NVIC 的 EXTI0 中断通道,最后把 GPIO 引脚映射到 EXTI0。
EXTI_Struct.EXTI_Line = EXTI_LINE0;
EXTI_Struct.EXTI_Mode = EXTI_Mode_Interrupt;
EXTI_Struct.EXTI_Trigger = EXTI_Trigger_Falling;
EXTI_Struct.EXTI_LineCmd = ENABLE;
EXTI_InitPeripheral(&EXTI_Struct);
NVIC_Struct.NVIC_IRQChannel = EXTI0_IRQn;
NVIC_Struct.NVIC_IRQChannelSubPriority = NVIC_SUB_PRIORITY_0;
NVIC_Struct.NVIC_IRQChannelPreemptionPriority = NVIC_PRE_PRIORITY_0;
NVIC_Struct.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_Struct);
GPIO_ConfigEXTILine(EXTI_LINE_SOURCE0,KeyPortSrc[0],KeyPinSrc[0]);
EXTI_Trigger_Falling 决定只在下降沿触发;GPIO_ConfigEXTILine 把选定的端口源与引脚源接到 EXTI0,实现与用户手册中 EXTI 线选择寄存器的 GPIO 映射一致。
3. 中断服务函数切换输入源。 EXTI0_IRQHandler 判断 EXTI0 状态后熄灭当前 LED,把 key_cnt 在 0 与 1 之间循环,再重新映射输入源,最后清除挂起位。
if(EXTI_GetStatusFlag(EXTI_LINE0) == SET)
{
LED_Off(LedPort[key_cnt], LedPin[key_cnt]);
if(key_cnt >= (MAX_LED_KEY_CNT-1))
{
key_cnt = 0;
}
else
{
key_cnt++;
}
GPIO_ConfigEXTILine(EXTI_LINE_SOURCE0,KeyPortSrc[key_cnt],KeyPinSrc[key_cnt]);
exti_int = 1;
}
EXTI_ClrStatusFlag(EXTI_LINE0);
每次中断先由 EXTI_GetStatusFlag 确认来源,再由 GPIO_ConfigEXTILine 把 EXTI0 改接到下一路按键;末尾 EXTI_ClrStatusFlag 清除挂起位,避免中断反复进入。
4. 主循环按输入源打印并闪烁。 main 检测 exti_int,置位时打印当前输入源并把标志清零,随后始终按 key_cnt 闪烁对应 LED。
while (1)
{
if(exti_int != 0)
{
printf("Current EXTI0 input source is:%s\r\n",str[key_cnt]);
exti_int = 0;
}
LED_Blink(LedPort[key_cnt], LedPin[key_cnt]);
Delay(SystemCoreClock/8);
}
闪烁节奏由 Delay(SystemCoreClock/8) 决定;str[] 数组提供串口提示用的引脚名称,与 KeyPortSrc、KeyPinSrc 一一对应。
对应源码: src/main.c、inc/main.h、src/n32h49x_it.c
5.11.4 注意与排障
| 场景 | 检查与建议 | 来源 |
|---|---|---|
| 中断使能 | 使用 EXTI 中断前须使能 NVIC 中对应的中断通道,并正确设置 EXTI 中断屏蔽寄存器中对应线的屏蔽位; 两者缺一,边沿到来时不会进入中断服务程序。 |
用户手册 |
| 挂起位清除 | 检测到边沿后对应线的挂起位置 1,须在中断服务程序中向该位写 1 清除; 改变该线的边沿极性也会清除挂起位。 |
用户手册 |
| 事件与中断的区别 | 事件线通过事件屏蔽寄存器配置,事件产生时只输出脉冲,不会置位挂起位; 只配置事件而不配置中断时,CPU 不会进入中断服务程序。 |
用户手册 |
| 特定外设的边沿限制 | 使用 PVD、USB FS、USB HS、LPTIM1、LPTIM2 中断时,其关联的 EXTI 线只能配置为上升沿触发。 | 用户手册 |
| GPIO 线映射与 AFIO 时钟 | 通过 AFIO_EXTI_CFGy 把 GPIO 引脚映射到 EXTI 线之前,必须先使能 AFIO 时钟;同一条 EXTI 线同一时刻只能映射到一个 GPIO 引脚。 |
用户手册 |