如何在Embassy Rust中实现STM32L0定时器中断及资料查询方法
问题与需求
- 核心需求:实现STM32L0上基于Embassy框架的定时器中断逻辑,同时掌握嵌入式Rust+Embassy环境下自行查找中断相关信息的有效途径
- 背景:接触Rust仅2周,使用搭载STM32L0的自定义开发板,基于Embassy开发
目标逻辑(伪代码)
repeat{ set timer interrupt1 at 5 miliseconds; set timer interrupt2 at 10 miliseconds; wait 100 miliseconds; } interrupt1: set pin high interrupt2: set pin low
注:此逻辑可通过其他方式实现,核心目的是理解中断工作原理,为后续项目做准备
当前尝试的代码
#![no_std] #![no_main] use cortex_m::interrupt as cortex_interrupt; use embassy_executor::Spawner; use embassy_stm32::gpio::{Level, Output, Speed}; use embassy_time::Timer; use {defmt_rtt as _, panic_probe as _}; use embassy_stm32::interrupt; #[interrupt] unsafe fn TIM2(){ let pin = embassy_stm32::peripherals::PB0::steal(); let mut pin2 = Output::new(pin, Level::High, Speed::Low); pin2.set_high(); } #[interrupt] unsafe fn TIM21(){ let pin = embassy_stm32::peripherals::PB0::steal(); let mut pin2 = Output::new(pin, Level::High, Speed::Low); pin2.set_low(); } #[embassy_executor::main] async fn main(_spawner: Spawner) { let p: embassy_stm32::Peripherals = embassy_stm32::init(Default::default()); unsafe {cortex_interrupt::enable();} loop{ Timer::after_millis(1000).await; } }
自行查找时遇到的问题
- 查阅《Rust嵌入式编程书籍》中断章节,未找到如何调度/触发中断的说明
- 参考Embassy相关GitHub问题时,代码中的
InterruptExecutor出现“未解析的导入”错误 - 查看Embassy官方文档,未找到对应解决方案
- StackOverflow上的类似问题无有效回复
- AI生成的代码均无法编译
解决方案与信息查找指南
一、定时器中断实现修正
核心问题分析
- 当前代码仅定义了中断处理函数,但未对定时器进行初始化(配置定时周期、开启中断、启动定时器)
- 中断函数中重复
steal()引脚并重新初始化,会导致资源冲突,引脚应在main中初始化后通过共享方式访问
修正后的代码
#![no_std] #![no_main] use cortex_m::interrupt::{self, Mutex}; use embassy_executor::Spawner; use embassy_stm32::gpio::{Level, Output, Speed}; use embassy_stm32::interrupt::InterruptExt; use embassy_stm32::timer::{simple_timer::SimpleTimer, Timer as Stm32Timer}; use embassy_time::Timer; use {defmt_rtt as _, panic_probe as _}; use embassy_stm32::peripherals::{TIM2, TIM21, PB0}; // 用Mutex共享引脚资源,确保中断安全 static PIN: Mutex<Option<Output<'static, PB0>>> = Mutex::new(None); #[interrupt] fn TIM2() { // 获取定时器实例并清除中断标志 let tim2 = unsafe { TIM2::steal() }; Stm32Timer::<TIM2>::clear_interrupt(&tim2); // 安全访问引脚并设置高电平 interrupt::free(|cs| { if let Some(pin) = PIN.borrow(cs).as_mut() { pin.set_high(); } }); } #[interrupt] fn TIM21() { // 获取定时器实例并清除中断标志 let tim21 = unsafe { TIM21::steal() }; Stm32Timer::<TIM21>::clear_interrupt(&tim21); // 安全访问引脚并设置低电平 interrupt::free(|cs| { if let Some(pin) = PIN.borrow(cs).as_mut() { pin.set_low(); } }); } #[embassy_executor::main] async fn main(_spawner: Spawner) { let p = embassy_stm32::init(Default::default()); // 初始化引脚并存入Mutex let pin = Output::new(p.PB0, Level::Low, Speed::Low); interrupt::free(|cs| { *PIN.borrow(cs) = Some(pin); }); // 初始化TIM2:5ms定时 let mut tim2 = SimpleTimer::new(p.TIM2); tim2.set_period(5_000); // 单位:微秒 tim2.enable_interrupt(); tim2.start(); // 启用TIM2中断 unsafe { embassy_stm32::interrupt::TIM2.unmask(); } // 初始化TIM21:10ms定时 let mut tim21 = SimpleTimer::new(p.TIM21); tim21.set_period(10_000); tim21.enable_interrupt(); tim21.start(); // 启用TIM21中断 unsafe { embassy_stm32::interrupt::TIM21.unmask(); } loop { Timer::after_millis(100).await; // 每次循环重启定时器(如果需要重复触发) tim2.restart(); tim21.restart(); } }
代码说明
- 使用
Mutex实现中断安全的资源共享,避免引脚重复初始化 - 通过
SimpleTimer简化定时器配置,设置周期、开启中断并启动 - 中断处理函数中必须清除中断标志,否则会持续触发中断
- 循环中重启定时器以实现每100ms重新触发5ms和10ms中断的逻辑
二、嵌入式Rust+Embassy信息查找有效途径
- Embassy官方示例库:优先查看针对STM32的定时器、中断相关示例,示例代码是最直接的参考,涵盖了框架的标准用法
- Embassy API文档:重点关注
embassy-stm32下的timer、interrupt模块,以及对应芯片的外设文档,文档中会标注每个功能的初始化步骤和中断启用方法 - Rust Embedded社区Discord:社区内有大量经验丰富的开发者,针对Embassy的问题提问通常能得到快速回复
- STM32官方参考手册:定时器的核心工作原理(如计数模式、中断触发条件)需要参考芯片的官方RM手册,这是底层逻辑的源头
- Embassy GitHub仓库issues和PR:搜索已解决的同类问题,注意查看issue的时间,避免参考过时的API用法(Embassy迭代较快,旧代码可能不兼容新版本)
- 《Rust Embedded Book》进阶章节:书中的中断章节基础内容可结合框架文档拓展,重点理解中断安全、资源共享等核心概念
内容的提问来源于stack exchange,提问作者Wouter Van Bastelaere
相关产品推荐
相关产品推荐

