GPIO 流水灯与按键控制

知识库
知识库文档
/tech-stacks/stm32-hal/examples/GPIO 流水灯与按键控制.md

文档

STM32 HAL:GPIO 流水灯与按键控制

目标

基于 STM32CubeMX 生成的 HAL 工程,实现 3 个 LED 流水灯和按键切换流水方向。

硬件配置(CubeMX)

  • MCU:STM32F407VGT6(Nucleo-F407ZG,其他 F4/F1/H7 类似)
  • LED1-3:PD12、PD13、PD14(Nucleo 板载 LED 为 LD4=PD12, LD5=PD13, LD6=PD14)
  • 按键:PC13(Nucleo 蓝色用户按键),上拉输入,按下为低电平
  • 时钟:HSE 8MHz → PLL 168MHz(F407 最大)

完整代码

/* main.c — 流水灯 + 按键方向控制,非阻塞实现 */

#include "main.h"

/* 外设句柄(CubeMX 自动生成) */
extern TIM_HandleTypeDef htim2;  /* 用于非阻塞定时 */

/* LED 配置 */
#define LED_COUNT      3
const uint16_t LED_PINS[LED_COUNT] = {
    GPIO_PIN_12,  // PD12 - LED1 (绿)
    GPIO_PIN_13,  // PD13 - LED2 (橙)
    GPIO_PIN_14   // PD14 - LED3 (红)
};
GPIO_TypeDef *const LED_PORT = GPIOD;

/* 按键配置 */
#define KEY_PORT      GPIOC
#define KEY_PIN       GPIO_PIN_13

/* 全局状态 */
typedef enum { FORWARD, REVERSE } Direction;
static Direction dir = FORWARD;
static uint8_t active_led = 0;
static uint32_t last_toggle_tick = 0;
static uint32_t last_key_check = 0;

/* ── 初始化 ── */
void led_init(void) {
    for (int i = 0; i < LED_COUNT; i++) {
        HAL_GPIO_WritePin(LED_PORT, LED_PINS[i], GPIO_PIN_RESET);
    }
    HAL_TIM_Base_Start(&htim2);
}

/* ── 按键扫描(消抖 + 检测下降沿) ── */
uint8_t key_pressed(void) {
    static uint8_t last_state = 1;
    uint8_t now = HAL_GPIO_ReadPin(KEY_PORT, KEY_PIN);

    if (last_state == 1 && now == 0) {    // 下降沿
        HAL_Delay(20);                     // 消抖
        if (HAL_GPIO_ReadPin(KEY_PORT, KEY_PIN) == 0) {
            last_state = now;
            return 1;  // 有效按下
        }
    }
    last_state = now;
    return 0;
}

/* ── 流水灯更新 ── */
void led_shift(void) {
    // 先全部关闭
    for (int i = 0; i < LED_COUNT; i++) {
        HAL_GPIO_WritePin(LED_PORT, LED_PINS[i], GPIO_PIN_RESET);
    }
    // 点亮当前 LED
    HAL_GPIO_WritePin(LED_PORT, LED_PINS[active_led], GPIO_PIN_SET);

    // 根据方向移动
    if (dir == FORWARD) {
        active_led = (active_led + 1) % LED_COUNT;
    } else {
        active_led = (active_led == 0) ? (LED_COUNT - 1) : (active_led - 1);
    }
}

/* ── 主循环 ── */
int main(void) {
    HAL_Init();
    SystemClock_Config();        // CubeMX 生成
    MX_GPIO_Init();              // CubeMX 生成
    MX_TIM2_Init();              // CubeMX 生成,TIM2 用于基准计时

    led_init();

    uint32_t now;

    while (1) {
        now = HAL_GetTick();

        // 流水灯定时 (200ms)
        if (now - last_toggle_tick >= 200) {
            last_toggle_tick = now;
            led_shift();
        }

        // 按键检测 (每 10ms)
        if (now - last_key_check >= 10) {
            last_key_check = now;
            if (key_pressed()) {
                dir = (dir == FORWARD) ? REVERSE : FORWARD;

                // LED 快速闪烁指示方向切换
                for (int i = 0; i < 2; i++) {
                    HAL_GPIO_WritePin(LED_PORT, LED_PINS[1], GPIO_PIN_SET);
                    HAL_Delay(50);
                    HAL_GPIO_WritePin(LED_PORT, LED_PINS[1], GPIO_PIN_RESET);
                    HAL_Delay(50);
                }
            }
        }
    }
}

运行步骤

  1. STM32CubeMX 打开 .ioc → 引脚配置 PD12-14 为 Output,PC13 为 Input(上拉)
  2. TIM2 使能(用作 HAL_GetTick() 的时基),生成代码
  3. 将上述 main.c 替换 CubeMX 生成的 main 函数体
  4. 编译下载:arm-none-eabi-gcc 或 CubeIDE → Run
  5. 观察 LED 流水效果,按蓝色用户按键切换方向

预期行为

  • 3 个 LED 以 200ms 间隔依次点亮形成流水效果
  • 按一次按键,方向反转(逆流水)
  • 切换时中间 LED 快闪两次确认

关键点

  • HAL_GetTick() 由 SysTick 中断驱动(默认 1ms),用作非阻塞定时基准
  • 按键消抖用硬件延时 20ms + 二次确认
  • 避免在中断或主循环中使用 HAL_Delay 做长延时(阻塞型)

信息

路径
/tech-stacks/stm32-hal/examples/GPIO 流水灯与按键控制.md
更新时间
2026/5/31