2. LED闪烁

任务目标

在嵌入式开发板上控制一个LED以固定周期交替亮灭,通过延时函数实现循环闪烁。本案例聚焦于GPIO输出控制与延时循环的基本用法,不涉及中断或定时器外设。

环境与依赖

  • 硬件:任意带LED的STM32开发板(如STM32F103C8T6 Blue Pill),LED接PC13(板上自带)或PA5
  • 软件:Rust工具链(含thumbv7m-none-eabi目标)、cargo-flash或OpenOCD用于烧录
  • Cargo依赖:cortex-m、cortex-m-rt、panic-halt、embedded-hal、stm32f1xx-hal

完整实现代码

#![no_std]
#![no_main]

use panic_halt as _;
use cortex_m::peripheral::syst::SystClkSource;
use cortex_m_rt::entry;
use embedded_hal::digital::v2::OutputPin;
use stm32f1xx_hal::{pac, prelude::*, timer::Timer};

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

    // 时钟配置:外部8MHz晶振,系统时钟72MHz
    let mut flash = dp.FLASH.constrain();
    let mut rcc = dp.RCC.constrain();
    let clocks = rcc.cfgr.use_hse(8.mhz()).sysclk(72.mhz()).freeze(&mut flash.acr);

    // 初始化GPIO:PC13推挽输出(Blue Pill板上LED)
    let mut gpioc = dp.GPIOC.split(&mut rcc.apb2);
    let mut led = gpioc.pc13.into_push_pull_output(&mut gpioc.crh);

    // 配置SysTick作为延时源:1ms中断周期
    let mut syst = cp.SYST;
    syst.set_clock_source(SystClkSource::Core);
    syst.set_reload(72_000_000 / 1000); // 72MHz / 1000 = 1ms
    syst.clear_current();
    syst.enable_counter();
    syst.enable_interrupt();

    // 使用TIM2作为阻塞延时(更精确,不依赖中断)
    let mut timer = Timer::tim2(dp.TIM2, &clocks, &mut rcc.apb1);
    timer.start(500.millis());

    loop {
        led.set_high().unwrap();
        timer.wait();
        led.set_low().unwrap();
        timer.wait();
    }
}

运行与烧录

  1. 创建新项目并添加依赖:cargo init --name led_blink,在Cargo.toml中配置对应依赖及[profile.release]优化选项
  2. 编译:cargo build --release
  3. 烧录:cargo flash --chip stm32f103c8 --release(需安装cargo-flash)

验证方法

烧录成功后,观察板上LED(PC13)是否以约1Hz频率(亮500ms、灭500ms)交替闪烁。若LED不亮或常亮,检查GPIO引脚编号是否与硬件连接一致;若闪烁频率异常,确认时钟配置中晶振频率是否与板载晶振匹配。

关键说明

本案例使用TIM2定时器实现阻塞延时,相比纯软件循环延时更精确且不占用CPU空转。SysTick部分代码仅作初始化示例,实际延时由TIM2完成。若需调整闪烁速度,修改timer.start()中的时长参数即可。LED引脚定义在gpioc.pc13处,更换引脚时同步修改此行及对应的GPIO端口时钟使能。