3. 外部中断触发按键响应

外部中断触发按键响应 学习卡片

任务说明

本案例在ARM Cortex-M开发板上,通过一个按键触发外部中断(EXTI),在中断服务程序中翻转LED的亮灭状态。核心目标是:掌握GPIO外部中断的配置流程(时钟使能→引脚模式→中断触发条件→NVIC使能→中断服务函数编写),并验证中断响应的实时性。

硬件连接

  • 按键K1:一端接PA0,另一端接GND(下降沿触发)
  • LED1:阳极接PC13(通过限流电阻),阴极接GND
  • 开发板:STM32F103系列(以标准外设库为例)
STM32F103 Cortex-M3 @72MHz PA0 K1 GND 上拉 3.3V PC13 LED1 R1 GND EXTI0中断 NVIC IRQ6

代码实现

以下代码使用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) {
        // 主循环空闲,等待中断
    }
}

编译与烧录

  1. 创建Keil MDK工程,选择目标芯片STM32F103C8T6;
  2. 添加标准外设库源文件(stm32f10x_gpio.c、stm32f10x_exti.c、stm32f10x_rcc.c、misc.c);
  3. 在工程选项中定义USE_STDPERIPH_DRIVER;
  4. 编译生成HEX文件,通过ST-Link烧录到开发板。

验证方法

操作步骤:给开发板上电,观察LED1初始状态为熄灭。按下按键K1并松开,观察LED1状态变化。

判定标准:

  • 每按一次按键,LED1亮灭状态翻转一次(灭→亮→灭→亮…);
  • 按键按下瞬间LED立即响应,无明显延迟;
  • 连续快速按键时,每次按键均能正确翻转(无漏响应)。

预期输出:若初始LED灭,第一次按下按键后LED亮,第二次按下后LED灭,以此类推。该结果由代码逻辑直接推出:每次中断将led_state取反并对应控制LED引脚电平。

关键配置说明

配置项值作用
触发引脚PA0对应EXTI0外部中断线
触发条件下降沿按键按下时引脚从高到低跳变
中断通道EXTI0_IRQnNVIC中对应IRQ6
抢占优先级1确保中断及时响应
LED引脚PC13推挽输出控制LED

注意事项:若按键未接上拉电阻,需将GPIO配置为内部上拉输入(GPIO_Mode_IPU),确保按键释放时引脚为高电平。本案例中按键接GND,按下时产生下降沿触发中断。