3. 按键控制LED

任务目标

本案例使用 Rust 在嵌入式开发板上实现按键控制 LED:通过轮询方式读取按键 GPIO 电平,检测到按下动作时切换 LED 的亮灭状态。核心学习点为 GPIO 输入模式配置、电平读取以及轮询逻辑。

硬件与依赖

  • 开发板:STM32F103C8T6(Blue Pill)或兼容开发板
  • 按键:一端接 PA0,另一端接 GND(按下为低电平)
  • LED:正极经 220Ω 电阻接 PC13,负极接 GND(低电平点亮)
  • Cargo 依赖:cortex-m、cortex-m-rt、panic-halt、embedded-hal、stm32f1xx-hal

完整代码

#![no_std]
#![no_main]

use panic_halt as _;
use stm32f1xx_hal::{pac, prelude::*, timer::Timer};

#[cortex_m_rt::entry]
fn main() -> ! {
    let dp = pac::Peripherals::take().unwrap();
    let cp = cortex_m::Peripherals::take().unwrap();

    let mut flash = dp.FLASH.constrain();
    let mut rcc = dp.RCC.constrain();
    let mut gpioa = dp.GPIOA.split(&mut rcc.apb2);
    let mut gpioc = dp.GPIOC.split(&mut rcc.apb2);

    // 配置 PC13 为推挽输出(LED 低电平点亮)
    let mut led = gpioc.pc13.into_push_pull_output(&mut gpioc.crh);
    led.set_high(); // 初始熄灭

    // 配置 PA0 为浮空输入(按键按下接地)
    let button = gpioa.pa0.into_floating_input(&mut gpioa.crl);

    // 简单延时定时器(用于消抖)
    let mut timer = Timer::syst(cp.SYST, &rcc.clocks).start_count_down(10.hz());

    let mut last_state = button.is_high();
    let mut led_on = false;

    loop {
        let current_state = button.is_high();

        // 检测下降沿:从高电平变为低电平(按下)
        if last_state && !current_state {
            // 消抖延时
            timer.wait();
            // 再次确认按键仍处于按下状态
            if button.is_low() {
                led_on = !led_on;
                if led_on {
                    led.set_low();   // 点亮
                } else {
                    led.set_high();  // 熄灭
                }
            }
        }

        last_state = current_state;
        timer.wait();
    }
}

运行与验证

  1. 创建新工程并添加上述依赖,将代码写入 src/main.rs。
  2. 连接硬件:按键一端接 PA0、另一端接 GND;LED 正极经电阻接 PC13、负极接 GND。
  3. 编译并烧录:cargo build --release,使用 cargo flash 或 openocd 烧录生成的 .elf 文件。
  4. 验证标准:按下按键时 LED 状态翻转(亮变灭或灭变亮);松开后状态保持;再次按下再次翻转。连续快速按动时,每次按下只翻转一次(消抖生效)。

关键说明

  • 按键按下时 PA0 被拉低,读取为 false(低电平);释放时因浮空输入状态不确定,但本案例中按键仅在按下时接地,释放时引脚悬空。若需稳定电平,可在 PA0 与 VCC 之间加 10kΩ 上拉电阻,并将引脚配置改为 into_pull_up_input。
  • 消抖采用定时器延时约 100ms(10Hz 周期),在检测到下降沿后延时并二次确认电平,避免机械抖动导致多次翻转。
  • 轮询周期由 timer.wait() 控制,每次循环约 100ms,足以响应普通按键操作。