2. 按键输入与中断

任务目标

本案例使用M55开发板上的用户按键(连接至GPIO中断引脚)触发外部中断,每次按下按键时切换板载LED的亮灭状态。通过该实验掌握GPIO中断的配置流程:引脚初始化、中断触发条件设置、中断回调函数注册以及中断安全的状态变量操作。

硬件连接与前置条件

  • 开发板:M55内核开发板(含板载按键与LED)
  • 按键引脚:PB2(默认下拉,按下为高电平)
  • LED引脚:PC13(低电平点亮)
  • 依赖:HAL库(STM32CubeMX生成或手动包含),无需额外第三方库

完整代码实现

#include "main.h"

/* 中断安全的状态标志 */
static volatile uint8_t led_state = 0;

/* LED与按键引脚定义 */
#define LED_PIN       GPIO_PIN_13
#define LED_PORT      GPIOC
#define BTN_PIN       GPIO_PIN_2
#define BTN_PORT      GPIOB

void SystemClock_Config(void);
static void GPIO_Init(void);

int main(void)
{
    HAL_Init();
    SystemClock_Config();
    GPIO_Init();

    while (1)
    {
        /* 主循环空闲,等待中断触发 */
        __WFI();
    }
}

static void GPIO_Init(void)
{
    __HAL_RCC_GPIOB_CLK_ENABLE();
    __HAL_RCC_GPIOC_CLK_ENABLE();

    GPIO_InitTypeDef gpio_btn = {0};
    gpio_btn.Pin = BTN_PIN;
    gpio_btn.Mode = GPIO_MODE_IT_RISING;      /* 上升沿触发中断 */
    gpio_btn.Pull = GPIO_PULLDOWN;            /* 默认下拉,按下为高 */
    HAL_GPIO_Init(BTN_PORT, &gpio_btn);

    GPIO_InitTypeDef gpio_led = {0};
    gpio_led.Pin = LED_PIN;
    gpio_led.Mode = GPIO_MODE_OUTPUT_PP;
    gpio_led.Pull = GPIO_NOPULL;
    gpio_led.Speed = GPIO_SPEED_FREQ_LOW;
    HAL_GPIO_Init(LED_PORT, &gpio_led);

    /* 初始状态:LED熄灭(PC13高电平) */
    HAL_GPIO_WritePin(LED_PORT, LED_PIN, GPIO_PIN_SET);

    /* 使能并注册EXTI中断回调 */
    HAL_NVIC_SetPriority(EXTI2_IRQn, 2, 0);
    HAL_NVIC_EnableIRQ(EXTI2_IRQn);
}

/* EXTI2中断服务函数(在启动文件中已声明) */
void EXTI2_IRQHandler(void)
{
    HAL_GPIO_EXTI_IRQHandler(BTN_PIN);
}

/* 中断回调:按键按下时翻转LED */
void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)
{
    if (GPIO_Pin == BTN_PIN)
    {
        /* 消除抖动:简单延时(实际工程建议使用定时器消抖) */
        for (volatile uint32_t i = 0; i < 10000; i++);

        /* 确认引脚仍为高电平(有效按下) */
        if (HAL_GPIO_ReadPin(BTN_PORT, BTN_PIN) == GPIO_PIN_SET)
        {
            led_state ^= 1;
            if (led_state)
                HAL_GPIO_WritePin(LED_PORT, LED_PIN, GPIO_PIN_RESET);  /* 点亮 */
            else
                HAL_GPIO_WritePin(LED_PORT, LED_PIN, GPIO_PIN_SET);    /* 熄灭 */
        }
    }
}

编译与运行

  1. 使用STM32CubeIDE打开工程,将上述代码替换main.c中对应内容(保留SystemClock_Config函数体)。
  2. 确认启动文件(startup_stm32u5xx.s)中包含EXTI2_IRQHandler中断向量(默认已包含)。
  3. 编译并下载至开发板。

验证方法

程序运行后,观察板载LED状态:

  • 上电复位后LED保持熄灭;
  • 每次按下按键(PB2)并释放,LED状态翻转一次(亮→灭→亮→灭);
  • 长按按键不会重复触发(仅在按下瞬间翻转一次)。

若LED状态与按键动作一致,且无抖动导致的多次翻转,则本案例验证通过。

关键说明

  • 中断回调中使用了简单延时消抖,适用于教学演示;实际产品建议使用定时器或状态机消抖。
  • led_state声明为volatile,确保主循环与中断上下文访问一致性。
  • 若按键按下无反应,检查按键引脚是否与PB2一致(部分开发板使用PA0或PC13),需根据实际硬件修改BTN_PIN/BTN_PORT定义。