3. 外部中断触发按键响应
任务说明
本案例在ARM Cortex-M开发板上,通过一个按键触发外部中断(EXTI),在中断服务程序中翻转LED的亮灭状态。核心目标是:掌握GPIO外部中断的配置流程(时钟使能→引脚模式→中断触发条件→NVIC使能→中断服务函数编写),并验证中断响应的实时性。
硬件连接
- 按键K1:一端接PA0,另一端接GND(下降沿触发)
- LED1:阳极接PC13(通过限流电阻),阴极接GND
- 开发板:STM32F103系列(以标准外设库为例)
代码实现
以下代码使用STM32标准外设库,完整实现按键外部中断触发LED翻转功能。代码包含GPIO初始化、EXTI配置、NVIC设置和中断服务函数。
// main.c - 外部中断按键响应
#include "stm32f10x.h"
#include "stm32f10x_gpio.h"
#include "stm32f10x_exti.h"
#include "stm32f10x_rcc.h"
#include "misc.h"
// LED翻转标志
volatile uint8_t led_state = 0;
// 按键外部中断初始化
void EXTI_Key_Init(void) {
GPIO_InitTypeDef GPIO_InitStructure;
EXTI_InitTypeDef EXTI_InitStructure;
NVIC_InitTypeDef NVIC_InitStructure;
// 1. 使能GPIOA和AFIO时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_AFIO, ENABLE);
// 2. 配置PA0为浮空输入(按键接GND,内部上拉可选)
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
GPIO_Init(GPIOA, &GPIO_InitStructure);
// 3. 将PA0连接到EXTI0线
GPIO_EXTILineConfig(GPIO_PortSourceGPIOA, GPIO_PinSource0);
// 4. 配置EXTI0:下降沿触发
EXTI_InitStructure.EXTI_Line = EXTI_Line0;
EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt;
EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Falling;
EXTI_InitStructure.EXTI_LineCmd = ENABLE;
EXTI_Init(&EXTI_InitStructure);
// 5. 配置NVIC:EXTI0中断通道
NVIC_InitStructure.NVIC_IRQChannel = EXTI0_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
}
// LED GPIO初始化
void LED_Init(void) {
GPIO_InitTypeDef GPIO_InitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC, ENABLE);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_13;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOC, &GPIO_InitStructure);
GPIO_SetBits(GPIOC, GPIO_Pin_13); // 初始LED灭
}
// EXTI0中断服务函数
void EXTI0_IRQHandler(void) {
if (EXTI_GetITStatus(EXTI_Line0) != RESET) {
// 消抖延时(简单延时,实际可用定时器)
volatile uint32_t delay = 0xFFFF;
while (delay--);
// 翻转LED状态
if (led_state == 0) {
GPIO_ResetBits(GPIOC, GPIO_Pin_13); // LED亮
led_state = 1;
} else {
GPIO_SetBits(GPIOC, GPIO_Pin_13); // LED灭
led_state = 0;
}
// 清除中断标志
EXTI_ClearITPendingBit(EXTI_Line0);
}
}
int main(void) {
LED_Init();
EXTI_Key_Init();
while (1) {
// 主循环空闲,等待中断
}
}
编译与烧录
- 创建Keil MDK工程,选择目标芯片STM32F103C8T6;
- 添加标准外设库源文件(stm32f10x_gpio.c、stm32f10x_exti.c、stm32f10x_rcc.c、misc.c);
- 在工程选项中定义USE_STDPERIPH_DRIVER;
- 编译生成HEX文件,通过ST-Link烧录到开发板。
验证方法
操作步骤:给开发板上电,观察LED1初始状态为熄灭。按下按键K1并松开,观察LED1状态变化。
判定标准:
- 每按一次按键,LED1亮灭状态翻转一次(灭→亮→灭→亮…);
- 按键按下瞬间LED立即响应,无明显延迟;
- 连续快速按键时,每次按键均能正确翻转(无漏响应)。
预期输出:若初始LED灭,第一次按下按键后LED亮,第二次按下后LED灭,以此类推。该结果由代码逻辑直接推出:每次中断将led_state取反并对应控制LED引脚电平。
关键配置说明
| 配置项 | 值 | 作用 |
|---|---|---|
| 触发引脚 | PA0 | 对应EXTI0外部中断线 |
| 触发条件 | 下降沿 | 按键按下时引脚从高到低跳变 |
| 中断通道 | EXTI0_IRQn | NVIC中对应IRQ6 |
| 抢占优先级 | 1 | 确保中断及时响应 |
| LED引脚 | PC13 | 推挽输出控制LED |
注意事项:若按键未接上拉电阻,需将GPIO配置为内部上拉输入(GPIO_Mode_IPU),确保按键释放时引脚为高电平。本案例中按键接GND,按下时产生下降沿触发中断。