3. 外部中断控制LED

外部中断控制LED 学习卡片

任务目标

使用STM32微控制器的外部中断(EXTI)功能,将按键连接到GPIO引脚,当按键按下时触发外部中断服务函数,在中断中翻转LED的亮灭状态。本案例以STM32F103系列为例,通过标准外设库实现完整功能。

硬件连接

  • LED正极通过限流电阻(约220Ω)连接到PB0,负极接地
  • 按键一端连接PA0,另一端接地(按下为低电平)
  • PA0需配置为上拉输入,确保未按下时引脚为高电平

完整代码实现

#include "stm32f10x.h"
#include "stm32f10x_gpio.h"
#include "stm32f10x_exti.h"
#include "stm32f10x_rcc.h"
#include "misc.h"

// 按键按下标志(在中断中置1,主循环中清零)
volatile uint8_t key_pressed = 0;

// LED GPIO初始化
void LED_Init(void) {
    GPIO_InitTypeDef GPIO_InitStructure;
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_Init(GPIOB, &GPIO_InitStructure);
    GPIO_ResetBits(GPIOB, GPIO_Pin_0);  // 初始LED熄灭
}

// 按键GPIO和EXTI初始化
void KEY_EXTI_Init(void) {
    GPIO_InitTypeDef GPIO_InitStructure;
    EXTI_InitTypeDef EXTI_InitStructure;
    NVIC_InitTypeDef NVIC_InitStructure;

    // 使能GPIOA和AFIO时钟
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_AFIO, ENABLE);

    // PA0配置为上拉输入
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;
    GPIO_Init(GPIOA, &GPIO_InitStructure);

    // 将PA0连接到EXTI0线
    GPIO_EXTILineConfig(GPIO_PortSourceGPIOA, GPIO_PinSource0);

    // 配置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);

    // 配置NVIC中断优先级
    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);
}

// EXTI0中断服务函数
void EXTI0_IRQHandler(void) {
    if (EXTI_GetITStatus(EXTI_Line0) != RESET) {
        // 消抖延时(简单延时约20ms)
        for (volatile uint32_t i = 0; i < 200000; i++);
        // 再次确认按键确实按下(低电平有效)
        if (GPIO_ReadInputDataBit(GPIOA, GPIO_Pin_0) == Bit_RESET) {
            key_pressed = 1;
        }
        EXTI_ClearITPendingBit(EXTI_Line0);
    }
}

int main(void) {
    LED_Init();
    KEY_EXTI_Init();

    while (1) {
        if (key_pressed) {
            key_pressed = 0;
            // 翻转LED状态
            if (GPIO_ReadOutputDataBit(GPIOB, GPIO_Pin_0) == Bit_SET) {
                GPIO_ResetBits(GPIOB, GPIO_Pin_0);
            } else {
                GPIO_SetBits(GPIOB, GPIO_Pin_0);
            }
        }
    }
}

编译与烧录

  1. 创建STM32标准外设库工程,添加上述代码到main.c文件
  2. 在工程配置中定义STM32F10X_MD(针对STM32F103中容量型号)
  3. 编译生成HEX文件,通过ST-Link或J-Link烧录到开发板

验证方法

烧录成功后,观察以下现象:

  • 初始状态:LED处于熄灭状态
  • 每次按下按键并释放,LED状态翻转一次(亮→灭或灭→亮)
  • 连续快速按键时,每次按键都能正确翻转LED状态,不会出现漏触发或误触发

若LED状态与按键动作不一致,检查PA0上拉配置是否正确、EXTI触发边沿是否设置为下降沿,以及中断服务函数中消抖延时是否合适。