在Rust的arduino_hal中获取引脚编号的方法
在Rust AVR HAL中获取Firmata所需的引脚编号
你在移植Firmata到Rust AVR环境时遇到的核心问题是:pin!宏生成的每个引脚都是不同的具体类型(如Pin<_, PD0>、Pin<_, PD1>),无法直接统一处理,也没有内置方法获取对应的Arduino引脚编号。下面提供几种可行的解决思路:
1. 用枚举封装引脚与编号(类型安全首选)
创建一个枚举,每个变体对应一个引脚类型,并内置编号信息。这种方法完全类型安全,没有性能开销,适合Firmata这类需要明确引脚映射的场景:
use avr_hal_generic::port::Pin; use avr_hal_generic::port::mode::Input; // 根据你使用的MCU定义对应引脚变体,比如Arduino Uno的引脚 enum FirmataPin { D0(Pin<Input, PD0>), D1(Pin<Input, PD1>), D2(Pin<Input, PD2>), D3(Pin<Input, PD3>), // ... 依次添加所有需要的引脚(数字、模拟引脚) A0(Pin<Input, PC0>), A1(Pin<Input, PC1>), } impl FirmataPin { // 获取Firmata协议所需的引脚编号 fn get_number(&self) -> u8 { match self { FirmataPin::D0(_) => 0, FirmataPin::D1(_) => 1, FirmataPin::D2(_) => 2, FirmataPin::D3(_) => 3, // ... 对应每个变体返回正确编号 FirmataPin::A0(_) => 14, // Arduino Uno的A0对应Firmata编号14 FirmataPin::A1(_) => 15, } } // 可以扩展其他Firmata需要的操作,比如切换输入输出模式、读取值等 fn set_output(&mut self) { match self { FirmataPin::D0(pin) => *pin = pin.into_output(), FirmataPin::D1(pin) => *pin = pin.into_output(), // ... 其他引脚的模式切换逻辑 _ => {} } } } // 实例化引脚时使用枚举 let pins = arduino_uno::Pins::new( dp.PORTB, dp.PORTC, dp.PORTD, ); let target_pin = match pin_number { 0 => FirmataPin::D0(pins.d0), 1 => FirmataPin::D1(pins.d1), 14 => FirmataPin::A0(pins.a0), // ... 匹配所有需要的编号 _ => panic!("Invalid pin number"), }; // 直接获取编号用于Firmata协议运算 let firmata_cmd = REPORT_ANALOG | (target_pin.get_number() & 0xF);
2. Unsafe方式读取引脚内部信息(简洁但依赖库实现)
如果不想写大量枚举代码,可以通过unsafe方式直接读取AVR HAL引脚结构体的内部端口和位信息,再映射为Firmata编号。这种方法依赖库的内部实现,升级库时可能需要调整,但代码量少:
注意:此方法需要确认你使用的
avr_hal_generic版本中,Pin结构体是否公开了端口和引脚位的字段。如果是私有字段,需要使用unsafe配合指针访问。
use avr_hal_generic::port::Pin; unsafe fn pin_to_firmata_number<Mode, Port>(pin: &Pin<Mode, Port>) -> u8 { // 读取引脚对应的端口和位(假设Pin结构体字段为port和pin) let port = &pin.0 as *const _; let pin_bit = pin.1; // 根据Arduino Uno的端口-引脚映射转换(按需修改为你的MCU) match port { // PDx对应数字引脚0-7 ptr if ptr == &arduino_uno::port::PD0::PORT => pin_bit, // PBx对应数字引脚8-13 ptr if ptr == &arduino_uno::port::PB0::PORT => 8 + pin_bit, // PCx对应模拟引脚A0-A5(Firmata编号14-19) ptr if ptr == &arduino_uno::port::PC0::PORT => 14 + pin_bit, _ => panic!("Unsupported pin"), } } // 使用示例 let d0_pin = pins.d0; let pin_num = unsafe { pin_to_firmata_number(&d0_pin) }; let firmata_cmd = REPORT_ANALOG | (pin_num & 0xF);
3. 动态分发(Trait对象)
定义一个包含引脚编号和操作的Trait,为每个引脚类型实现该Trait,然后用Box<dyn Trait>做动态分发。这种方法代码量适中,但AVR上会有轻微的性能开销:
use avr_hal_generic::port::Pin; use avr_hal_generic::port::mode::Input; trait FirmataCompatiblePin { fn get_pin_number(&self) -> u8; // 扩展其他必要方法 } // 为每个引脚类型实现Trait impl FirmataCompatiblePin for Pin<Input, PD0> { fn get_pin_number(&self) -> u8 { 0 } } impl FirmataCompatiblePin for Pin<Input, PD1> { fn get_pin_number(&self) -> u8 { 1 } } impl FirmataCompatiblePin for Pin<Input, PC0> { fn get_pin_number(&self) -> u8 { 14 } } // 实例化时使用Trait对象 let pin: Box<dyn FirmataCompatiblePin> = match pin_number { 0 => Box::new(pins.d0), 1 => Box::new(pins.d1), 14 => Box::new(pins.a0), _ => panic!("Invalid pin number"), }; let pin_num = pin.get_pin_number();
内容的提问来源于stack exchange,提问作者will.mendil
相关产品推荐
相关产品推荐

