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

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.03 04:06:35