Rust中使用SH1106驱动时,如何避免I2C实例被转移以复用总线?
解决I2C总线在多设备间的复用问题(Rust Embassy + SH1106 + MCP3421)
问题核心
你的代码中connect_i2c(i2c)会将I2C实例的所有权转移给SH1106驱动,这是Rust所有权机制的正常表现,导致后续无法直接通过原变量调用read_async。以下是针对你疑问的具体解答:
1. 复用I2C总线的Rust特性
有,核心是通过共享所有权+异步同步锁实现总线安全复用:
- 用
Arc(原子引用计数)实现多所有者共享实例 - 用嵌入式异步Mutex(如
embassy_sync::mutex::Mutex)保证同一时间仅一个设备访问I2C总线,避免总线冲突
修改后的代码示例:
use embassy_sync::mutex::Mutex; use core::sync::atomic::Ordering; use alloc::sync::Arc; #[embassy_executor::main] async fn main(_spawner: Spawner) { let p = embassy_rp::init(Default::default()); let used_i2c = p.I2C0; let sda = p.PIN_12; let scl = p.PIN_13; let mut data = [0u8; 4]; info!("set up i2c "); let i2c = i2c::I2c::new_async(used_i2c, scl, sda, Irqs, Config::default()); // 将I2C包在Arc与异步Mutex中实现共享 let shared_i2c = Arc::new(Mutex::new(i2c)); // 给驱动传递Arc的克隆实例 let mut display: GraphicsMode<_> = Builder::new() .connect_i2c(shared_i2c.clone()) .into(); // 读取MCP3421前先获取总线锁 let mut locked_i2c = shared_i2c.lock().await; locked_i2c.read_async(ADDRESS_16, &mut data).await.unwrap(); // 锁会在locked_i2c离开作用域时自动释放 ... }
注:需在Cargo.toml中添加embassy-sync依赖,并开启Rust的alloc功能(针对Raspberry Pi Pico,需在内存配置中启用)。
2. 显示驱动占用整个I2C总线的合理原因
这是简化单设备场景的设计取舍:
- 所有权转移可保证驱动对总线的独占控制,避免其他代码误操作总线导致通信错误,适合仅接单个显示屏的简单场景
- 无需额外同步逻辑,驱动代码更简洁,降低初学者使用门槛
3. SH1106驱动是否存在设计问题
不算设计问题,属于场景适配的设计选择:
- 驱动本身支持灵活的总线类型,不仅接收所有权转移的I2C实例,也支持包裹共享引用/同步锁的类型(只要实现驱动要求的I2C trait)
- 它并未强制独占总线,只是默认提供了最简单的所有权传递方式,用户可根据多设备场景自行封装共享逻辑
内容的提问来源于stack exchange,提问作者d850b
相关产品推荐
相关产品推荐

