You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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();
    });
}

关键说明

  1. 中断函数的作用域:#[interrupt]宏要求函数处于全局作用域,这样才能被cortex-m-rt的中断向量表正确引用,生成对应的入口符号。
  2. 静态变量的作用:全局静态变量G_EOSR和G_EOSL用于在中断上下文与主上下文之间传递引脚所有权,Mutex和RefCell是为了保证中断安全的可变访问。
  3. 代码结构分离:将中断配置逻辑(结构体方法)与中断处理逻辑(顶层函数)分离,既符合嵌入式Rust的最佳实践,也满足宏的使用要求。

内容的提问来源于stack exchange,提问作者Furlings

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.29 10:05:40