4. 按键切换LED状态
任务说明
本案例实现按键控制LED翻转:每次按下按键(检测到下降沿),LED状态翻转一次。使用GPIO外部中断方式检测按键,避免轮询占用CPU。
硬件连接
代码实现
// Cargo.toml 依赖:
// [dependencies]
// embedded-hal = "0.2.7"
// nb = "1"
// cortex-m = "0.7"
// cortex-m-rt = "0.7"
// panic-halt = "0.2"
// stm32f1xx-hal = { version = "0.9", features = ["rt", "stm32f103"] }
use stm32f1xx_hal::{
prelude::*,
pac,
gpio::{gpioc::PC13, gpioa::PA0, Edge, ExtiPin, Input, PullUp, Output, PushPull},
interrupt,
timer::Timer,
};
use core::cell::RefCell;
use cortex_m::interrupt::Mutex;
use cortex_m_rt::entry;
// 全局LED状态(由中断修改,主循环读取)
static LED_ON: Mutex<RefCell<bool>> = Mutex::new(RefCell::new(false));
// 中断中使用的LED引脚句柄(通过临界区访问)
static mut LED_PIN: Option<PC13<Output<PushPull>>> = None;
#[entry]
fn main() -> ! {
let dp = pac::Peripherals::take().unwrap();
let mut rcc = dp.RCC.constrain();
let mut flash = dp.FLASH.constrain();
let mut pwr = dp.PWR.constrain();
let clocks = rcc.cfgr.freeze(&mut flash.acr, &mut pwr);
// 初始化GPIO
let mut gpioa = dp.GPIOA.split(&mut rcc.apb2);
let mut gpioc = dp.GPIOC.split(&mut rcc.apb2);
// PA0: 按键输入,上拉(默认高电平,按下接地)
let mut key = gpioa.pa0.into_pull_up_input(&mut gpioa.crl);
key.make_interrupt_source(&mut dp.AFIO);
key.trigger_on_edge(&dp.EXTI, Edge::Falling);
key.enable_interrupt(&dp.EXTI);
// PC13: LED输出(板上LED,低电平点亮)
let led = gpioc.pc13.into_push_pull_output(&mut gpioc.crh);
led.set_high(); // 初始熄灭
// 保存LED句柄到全局
unsafe {
LED_PIN = Some(led);
}
// 使能EXTI0中断
unsafe {
cortex_m::peripheral::NVIC::unmask(pac::Interrupt::EXTI0);
}
loop {
// 主循环可做其他任务
// 此处仅演示,实际可休眠或执行其他逻辑
cortex_m::asm::wfi();
}
}
// EXTI0中断服务函数
#[interrupt]
fn EXTI0() {
cortex_m::interrupt::free(|cs| {
// 清除中断标志
let dp = unsafe { pac::Peripherals::steal() };
let exti = &dp.EXTI;
exti.pr.write(|w| w.pr0().set_bit());
// 翻转LED
if let Some(led) = unsafe { LED_PIN.as_mut() } {
if led.is_set_high() {
led.set_low();
} else {
led.set_high();
}
}
// 更新全局状态(可选,供主循环查询)
let mut state = LED_ON.borrow(cs).borrow_mut();
*state = !*state;
});
}
关键说明
- 按键消抖:本案例使用硬件上拉+下降沿中断,未做软件消抖。若实际使用中出现抖动导致多次翻转,可在中断中加入延时消抖(如循环延时10ms)或使用定时器消抖。
- 中断安全:LED引脚通过
Mutex<RefCell<>>保护,确保中断与主循环之间不会产生数据竞争。 - 中断标志清除:必须在中断服务函数中清除EXTI挂起位,否则中断会反复触发。
验证方法
- 将代码编译烧录至STM32F103开发板(PA0接按键到GND,PC13接板载LED)。
- 上电后LED处于熄灭状态。
- 每次按下按键并释放,观察LED状态翻转:灭→亮→灭→亮……
- 若按下一次LED翻转多次,说明按键抖动影响,需增加消抖处理。
判定标准:每次完整按下并释放按键,LED状态恰好翻转一次,且状态变化与按键动作一一对应。