4. 外部中断按键
任务目标
在STM32F103上配置一个按键作为外部中断输入,当按键按下时触发EXTI中断,在中断服务函数中翻转LED的亮灭状态。通过本例学习Rust嵌入式开发中的中断配置、中断服务函数编写以及共享可变状态的处理方式。
环境与依赖
在 Cargo.toml 中添加以下依赖:
[dependencies]
cortex-m = "0.7"
cortex-m-rt = "0.7"
embedded-hal = "0.2"
stm32f1xx-hal = "0.9"
panic-halt = "0.2"
nb = "1"
硬件连接
- LED:PA5(通过限流电阻接GND,高电平点亮)
- 按键:PA0(一端接GND,另一端接PA0,内部上拉)
关键完整代码
#![no_std]
#![no_main]
use panic_halt as _;
use cortex_m::interrupt::{self, Mutex};
use cortex_m_rt::entry;
use core::cell::RefCell;
use stm32f1xx_hal::{
prelude::*,
pac,
gpio::{gpiob::PB5, Output, PushPull},
};
// 共享LED状态:使用Mutex保护,中断与主循环均可访问
static LED: Mutex<RefCell<Option<PB5<Output<PushPull>>>>> = Mutex::new(RefCell::new(None));
#[entry]
fn main() -> ! {
let dp = pac::Peripherals::take().unwrap();
let mut rcc = dp.RCC.constrain();
let mut flash = dp.FLASH.constrain();
let mut afio = dp.AFIO.constrain(&mut rcc.apb2);
// 时钟配置:8MHz外部晶振,系统时钟72MHz
let clocks = rcc.cfgr.use_hse(8.mhz()).sysclk(72.mhz()).freeze(&mut flash.acr);
// 初始化GPIO
let mut gpioa = dp.GPIOA.split(&mut rcc.apb2);
let mut gpiob = dp.GPIOB.split(&mut rcc.apb2);
// 配置LED引脚(PB5)为推挽输出
let led = gpiob.pb5.into_push_pull_output(&mut gpiob.crl);
// 将LED存入全局共享变量
interrupt::free(|cs| {
LED.borrow(cs).replace(Some(led));
});
// 配置按键引脚(PA0)为浮空输入(外部已接上拉电阻)
let button = gpioa.pa0.into_floating_input(&mut gpioa.crl);
// 启用EXTI时钟
let mut exti = dp.EXTI;
let mut nvic = dp.NVIC;
// 配置EXTI0(PA0对应EXTI线0)
afio.exti_cfg.exti0_select(&mut afio.mapr, 0); // 选择PA0作为EXTI0源
exti.rising_edge(0); // 按键按下时产生上升沿(按下接GND,释放后恢复高电平)
exti.enable(0);
// 使能EXTI0中断
nvic.enable(pac::Interrupt::EXTI0);
loop {
// 主循环空闲,等待中断
cortex_m::asm::wfi();
}
}
// EXTI0中断服务函数
#[interrupt]
fn EXTI0() {
interrupt::free(|cs| {
if let Some(led) = LED.borrow(cs).borrow_mut().as_mut() {
led.toggle();
}
});
// 清除中断标志位
let dp = unsafe { pac::Peripherals::steal() };
dp.EXTI.pr.write(|w| w.pr0().set_bit());
}
运行与验证
- 编译:在项目根目录执行
cargo build --release,生成固件。 - 烧录:使用
cargo flash --release --chip stm32f103c8或通过ST-Link等工具烧录到开发板。 - 验证步骤:
- 上电后LED保持熄灭状态(初始为低电平);
- 按下按键并释放,观察LED状态翻转(点亮或熄灭);
- 每次按下并释放按键,LED状态应切换一次;
- 连续快速按动按键,LED应跟随每次按键翻转,无卡死或异常。
通过条件:每次按键按下并释放后LED状态翻转一次,且系统运行稳定、无死机现象,即判定本例实现成功。
关键说明
- 本例使用上升沿触发:按键一端接GND,PA0内部上拉,按下时引脚为低电平,释放时恢复高电平产生上升沿。若硬件连接相反,需改为
exti.falling_edge(0)。 - 中断服务函数中通过
Mutex<RefCell<Option<...>>>安全访问共享的LED引脚,避免数据竞争。 - 中断标志位必须在ISR末尾手动清除,否则中断会持续触发。
- 若使用PA0以外的引脚,需对应修改EXTI线编号和NVIC中断名(如PA1对应EXTI1)。