Rust嵌入式开发:结构体中使用#[interrupt]宏编译报错求助
解决Rust嵌入式中
#[interrupt]宏在结构体impl块中的编译错误 错误原因
#[interrupt]宏(来自cortex-m-rt)的作用是生成全局中断向量表的入口函数,这类函数必须定义在模块的顶层(或crate根),不能作为结构体的关联函数放在impl块内部。当你把带#[interrupt]的函数放在EndOfStroke的impl里时,编译器会将其视为结构体的成员函数,但宏需要生成全局可见的符号(比如__cortex_m_rt_EXTI2),因此找不到对应的全局符号就会报错。
而放在main.rs中能正常运行,是因为main.rs是crate根目录,中断函数处于顶层作用域,宏可以正确生成全局的中断处理入口。
解决方案
将中断处理函数移到模块的顶层(当前文件的impl块外部),保持静态变量的定义不变,调整后的代码如下:
use core::cell::RefCell; use cortex_m::interrupt::Mutex; use stm32f4xx_hal::{ gpio::{gpioa::PA4, gpiod::PD2, Edge, ExtiPin, Input, PinMode}, interrupt, pac::{ Interrupt::{EXTI2, EXTI4}, EXTI, }, syscfg::SysCfg, }; type EosRightPin = PD2<Input>; type EosLeftPin = PA4<Input>; static G_EOSR: Mutex<RefCell<Option<EosRightPin>>> = Mutex::new(RefCell::new(None)); static G_EOSL: Mutex<RefCell<Option<EosLeftPin>>> = Mutex::new(RefCell::new(None)); pub struct EndOfStroke { eos_r: EosRightPin, eos_l: EosLeftPin, } impl EndOfStroke { pub fn new(pin_eos_r: PD2<impl PinMode>, pin_eos_l: PA4<impl PinMode>) -> Self { let eos_r = pin_eos_r.into_input().internal_pull_down(true); let eos_l = pin_eos_l.into_input().internal_pull_down(true); Self { eos_r, eos_l } } pub fn enable_interrupt(mut self, syscfg: &mut SysCfg, exti: &mut EXTI) { // 配置中断源与触发边沿 self.eos_r.make_interrupt_source(syscfg); self.eos_l.make_interrupt_source(syscfg); self.eos_r.trigger_on_edge(exti, Edge::Rising); self.eos_l.trigger_on_edge(exti, Edge::Rising); self.eos_r.enable_interrupt(exti); self.eos_l.enable_interrupt(exti); // 将引脚转移到全局静态变量 cortex_m::interrupt::free(|cs| { G_EOSR.borrow(cs).replace(Some(self.eos_r)); G_EOSL.borrow(cs).replace(Some(self.eos_l)); }); // 开启NVIC中断 unsafe { cortex_m::peripheral::NVIC::unmask(EXTI2); cortex_m::peripheral::NVIC::unmask(EXTI4); } } } // 中断处理函数移至模块顶层 #[interrupt] fn EXTI2() { // TODO: 停止向右运行的电机 // 清除中断挂起位 cortex_m::interrupt::free(|cs| { let mut eos_r = G_EOSR.borrow(cs).borrow_mut(); eos_r.as_mut().unwrap().clear_interrupt_pending_bit(); }); } #[interrupt] fn EXTI4() { // TODO: 停止向左运行的电机 // 清除中断挂起位 cortex_m::interrupt::free(|cs| { let mut eos_l = G_EOSL.borrow(cs).borrow_mut(); eos_l.as_mut().unwrap().clear_interrupt_pending_bit(); }); }
关键说明
- 中断函数的作用域:
#[interrupt]宏要求函数处于全局作用域,这样才能被cortex-m-rt的中断向量表正确引用,生成对应的入口符号。 - 静态变量的作用:全局静态变量
G_EOSR和G_EOSL用于在中断上下文与主上下文之间传递引脚所有权,Mutex和RefCell是为了保证中断安全的可变访问。 - 代码结构分离:将中断配置逻辑(结构体方法)与中断处理逻辑(顶层函数)分离,既符合嵌入式Rust的最佳实践,也满足宏的使用要求。
内容的提问来源于stack exchange,提问作者Furlings
相关产品推荐
相关产品推荐

