一、外部中断核心概念
-
EXTI (External Interrupt/Event Controller)
STM32 的 GPIO 引脚可配置为外部中断源,通过 EXTI 模块管理中断触发。 -
NVIC (Nested Vectored Interrupt Controller)
负责中断优先级管理和中断服务函数(ISR)的路由。 -
中断服务函数 (ISR)
中断触发后自动调用的函数,需用户实现。
二、中断号与管脚的映射关系
STM32 的 EXTI 线(0-15) 对应 GPIO 引脚号,但同一时刻每个 EXTI 线只能绑定到一个 GPIO 端口。
EXTI 线 | 支持的 GPIO 引脚 | 中断通道 (IRQn) | 中断号 |
---|---|---|---|
EXTI0 | PA0, PB0, PC0, …, Px0 | EXTI0_IRQn | 6 |
EXTI1 | PA1, PB1, PC1, …, Px1 | EXTI1_IRQn | 7 |
… | … | … | … |
EXTI15 | PA15, PB15, PC15, …, Px15 | EXTI15_10_IRQn | 40 |
关键规则:
- EXTI 线编号 = GPIO 引脚号(如 PA3 使用 EXTI3)。
- 同一 EXTI 线可被多个 GPIO 端口复用(但需通过配置选择具体端口)。
- 不同 EXTI 线对应不同的中断通道(IRQn)和中断号。
三、按键中断配置步骤(以 PA0 为例)
1. GPIO 初始化
GPIO_InitTypeDef GPIO_InitStruct;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_AFIO, ENABLE);
GPIO_InitStruct.GPIO_Pin = GPIO_Pin_0;
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_IPU; // 上拉输入(按键接地时使用)
// GPIO_InitStruct.GPIO_Mode = GPIO_Mode_IPD; // 下拉输入(按键接电源时使用)
GPIO_Init(GPIOA, &GPIO_InitStruct);
2. 配置 EXTI 中断线
EXTI_InitTypeDef EXTI_InitStruct;
// 绑定 PA0 到 EXTI0 线
GPIO_EXTILineConfig(GPIO_PortSourceGPIOA, GPIO_PinSource0);
EXTI_InitStruct.EXTI_Line = EXTI_Line0;
EXTI_InitStruct.EXTI_Mode = EXTI_Mode_Interrupt;
EXTI_InitStruct.EXTI_Trigger = EXTI_Trigger_Falling; // 下降沿触发(按键按下)
// EXTI_InitStruct.EXTI_Trigger = EXTI_Trigger_Rising; // 上升沿触发(按键释放)
EXTI_InitStruct.EXTI_LineCmd = ENABLE;
EXTI_Init(&EXTI_InitStruct);
3. 配置 NVIC 中断优先级
NVIC_InitTypeDef NVIC_InitStruct;
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); // 优先级分组(根据需求选择)
NVIC_InitStruct.NVIC_IRQChannel = EXTI0_IRQn; // 中断通道为 EXTI0
NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority = 1; // 抢占优先级
NVIC_InitStruct.NVIC_IRQChannelSubPriority = 1; // 子优先级
NVIC_InitStruct.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStruct);
4. 编写中断服务函数 (ISR)
void EXTI0_IRQHandler(void) {
if (EXTI_GetITStatus(EXTI_Line0) != RESET) {
// 处理按键中断逻辑
EXTI_ClearITPendingBit(EXTI_Line0); // 清除中断标志
}
}
四、中断号与管脚关系详解
-
EXTI 线选择
- 每个 GPIO 引脚号对应一个 EXTI 线(如 PA5 使用 EXTI5)。
- 通过
GPIO_EXTILineConfig()
选择具体 GPIO 端口(PA/PB/PC…)。
-
中断通道 (IRQn)
- EXTI0-4 有独立的中断通道(如
EXTI0_IRQn
)。 - EXTI5-9 共享
EXTI9_5_IRQn
,EXTI10-15 共享EXTI15_10_IRQn
。
- EXTI0-4 有独立的中断通道(如
-
中断服务函数名称
- 必须与启动文件 (
startup_stm32fxxx.s
) 中的中断向量名称严格一致。
- 必须与启动文件 (
五、常见问题
-
中断未触发
- 检查 GPIO 时钟和 AFIO 时钟是否使能。
- 确认中断服务函数名称是否正确。
- 确保中断标志位被清除。
-
多引脚共享同一 EXTI 线
- 若 PA0 和 PB0 同时使用 EXTI0,需在 ISR 中判断具体引脚:
if (GPIO_ReadInputDataBit(GPIOA, GPIO_Pin_0) == 0) { // PA0 触发中断 }
- 若 PA0 和 PB0 同时使用 EXTI0,需在 ISR 中判断具体引脚:
-
按键消抖
- 硬件消抖:并联 0.1μF 电容。
- 软件消抖:在 ISR 中延时 10-20ms 后再次检测电平。
六、完整示例代码
#include "stm32f10x.h"
void EXTI0_IRQHandler(void) {
if (EXTI_GetITStatus(EXTI_Line0) != RESET) {
if (GPIO_ReadInputDataBit(GPIOA, GPIO_Pin_0) == 0) {
// 处理按键按下事件
}
EXTI_ClearITPendingBit(EXTI_Line0);
}
}
int main(void) {
// GPIO 初始化
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_AFIO, ENABLE);
GPIO_InitTypeDef GPIO_InitStruct;
GPIO_InitStruct.GPIO_Pin = GPIO_Pin_0;
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_IPU;
GPIO_Init(GPIOA, &GPIO_InitStruct);
// EXTI 配置
GPIO_EXTILineConfig(GPIO_PortSourceGPIOA, GPIO_PinSource0);
EXTI_InitTypeDef EXTI_InitStruct;
EXTI_InitStruct.EXTI_Line = EXTI_Line0;
EXTI_InitStruct.EXTI_Mode = EXTI_Mode_Interrupt;
EXTI_InitStruct.EXTI_Trigger = EXTI_Trigger_Falling;
EXTI_InitStruct.EXTI_LineCmd = ENABLE;
EXTI_Init(&EXTI_InitStruct);
// NVIC 配置
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
NVIC_InitTypeDef NVIC_InitStruct;
NVIC_InitStruct.NVIC_IRQChannel = EXTI0_IRQn;
NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority = 1;
NVIC_InitStruct.NVIC_IRQChannelSubPriority = 1;
NVIC_InitStruct.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStruct);
while(1) {
// 主循环
}
}
通过以上步骤,您可快速掌握 STM32 标准库的外部中断配置方法,并理解中断号与 GPIO 管脚的对应关系。实际开发中需根据具体型号调整引脚和时钟配置。