2. GPIO输入读取按键状态
任务说明
本案例使用 Rust Embedded HAL 编写一个最小可复现的 LED 闪烁程序,目标为:LED 以 1Hz 频率闪烁(即每 500ms 翻转一次电平)。案例基于 STM32F103 蓝色药丸开发板,使用 stm32f1xx-hal 抽象层,不直接操作寄存器。
硬件连接
板载 LED 接在 PC13 引脚(低电平点亮),因此需要配置为推挽输出,并通过翻转输出数据寄存器实现闪烁。无需额外外部元件。
实现代码
// Cargo.toml 依赖:
// [dependencies]
// stm32f1xx-hal = { version = "0.10", features = ["stm32f103", "rt"] }
// panic-halt = "0.2"
// cortex-m-rt = "0.7"
// embedded-hal = "0.2.7"
#![no_std]
#![no_main]
use panic_halt as _;
use cortex_m_rt::entry;
use stm32f1xx_hal::{pac, prelude::*, timer::Timer};
#[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 clocks = rcc.cfgr.freeze(&mut flash.acr);
// 配置 PC13 为推挽输出(低电平点亮)
let mut gpioc = dp.GPIOC.split(&mut rcc.apb2);
let mut led = gpioc.pc13.into_push_pull_output(&mut gpioc.crh);
// 初始化 500ms 周期定时器(1Hz 翻转)
let mut timer = Timer::syst(cp.SYST, &clocks).start_count_down(500.millis());
loop {
// 翻转 LED 电平
led.toggle();
// 阻塞等待定时器溢出
nb::block!(timer.wait()).unwrap();
}
}
代码说明
上述代码完成以下核心步骤:
- 冻结时钟至 8MHz 系统主频(默认配置);
- 将 PC13 配置为推挽输出模式;
- 使用 SysTick 定时器生成 500ms 周期;
- 主循环中翻转 LED 电平后阻塞等待定时器溢出,实现每 500ms 翻转一次,即 1Hz 闪烁频率。
编译与烧录
# 编译(在项目根目录执行)
cargo build --release
# 烧录(使用 stm32flash 或 OpenOCD,示例为 stm32flash)
stm32flash -w target/thumbv7m-none-eabi/release/led_blink -v /dev/ttyUSB0
目标平台为 thumbv7m-none-eabi,需在 .cargo/config.toml 中指定:
[build]
target = "thumbv7m-none-eabi"
验证方法
烧录后观察板载 LED:
- LED 应持续以 1Hz 频率闪烁(亮 500ms,灭 500ms);
- 使用秒表或手机计时器对比 10 次闪烁周期,误差应在 ±5% 以内(即 10 次闪烁约 10 秒);
- 若 LED 常亮或常灭,检查 PC13 连接或代码中引脚配置是否正确。
判定标准:LED 以稳定 1Hz 频率闪烁且无异常停顿,即视为通过。
关键时序示意
验证观测点
上电后观察 PC13 引脚电平变化:使用逻辑分析仪或万用表测量 PC13 对 GND 电压,应看到方波信号,高电平持续 500ms,低电平持续 500ms,周期为 1s。若使用示波器,可直接测量频率应为 1.00Hz ±0.05Hz。