rust-embedded-community / ssd1306

SSD1306 OLED driver
Apache License 2.0
283 stars 69 forks source link

Can not determin type of display interface #131

Closed lll9p closed 3 years ago

lll9p commented 3 years ago

Description of the problem/feature request/other

Can not determin type of display interface, I want to use display in critical sections, But not working, what can I do about it ,Thanks!

......

type DISP = ssd1306::mode::graphics::GraphicsMode<
     stm32f1xx_hal::i2c::BlockingI2c<
         stm32f1::stm32f103::I2C1,
         (
             stm32f1xx_hal::gpio::gpiob::PB8<stm32f1xx_hal::gpio::Alternate<OpenDrain>>,
             stm32f1xx_hal::gpio::gpiob::PB9<stm32f1xx_hal::gpio::Alternate<OpenDrain>>,
         ),
     >,
 >;

......


let i2c = BlockingI2c::i2c1(
        device.I2C1,
        (scl, sda),
        &mut afio.mapr,
        Mode::Fast {
            frequency: 400_000.hz(),
            duty_cycle: DutyCycle::Ratio2to1,
        },
        clocks,
        &mut rcc.apb1,
        1000,
        10,
        1000,
        1000,
    );
let interface = I2CDIBuilder::new().init(i2c);

// The working code.
// let mut disp: GraphicsMode<_> = Builder::new().connect(interface).into();

// **Code Not Working.
let mut disp: DISP = Builder::new().connect(interface).into();

Compile appears errors.

error[E0277]: the trait bound `ssd1306::mode::graphics::GraphicsMode<stm32f1xx_hal::i2c::BlockingI2c<stm32f1::stm32f103::I2C1, (stm32f1xx_hal::gpio::gpiob::PB8<stm32f1xx_hal::gpio::Alternate<stm32f1xx_hal::gpio::OpenDrain>>, stm32f1xx_hal::gpio::gpiob::PB9<stm32f1xx_hal::gpio::Alternate<stm32f1xx_hal::gpio::OpenDrain>>)>>: ssd1306::mode::displaymode::DisplayModeTrait<impl display_interface::WriteOnlyDataCommand, ssd1306::displaysize::DisplaySize128x64>` is not satisfied
   --> src\main.rs:183:60
    |
183 |     let mut disp: DISP = Builder::new().connect(interface).into();
    |                                                            ^^^^ the trait `ssd1306::mode::displaymode::DisplayModeTrait<impl display_interface::WriteOnlyDataCommand, ssd1306::displaysize::DisplaySize128x64>` is not implemented for `ssd1306::mode::graphics::GraphicsMode<stm32f1xx_hal::i2c::BlockingI2c<stm32f1::stm32f103::I2C1, (stm32f1xx_hal::gpio::gpiob::PB8<stm32f1xx_hal::gpio::Alternate<stm32f1xx_hal::gpio::OpenDrain>>, stm32f1xx_hal::gpio::gpiob::PB9<stm32f1xx_hal::gpio::Alternate<stm32f1xx_hal::gpio::OpenDrain>>)>>`
    |
    = help: the following implementations were found:
              <ssd1306::mode::graphics::GraphicsMode<DI, DSIZE> as ssd1306::mode::displaymode::DisplayModeTrait<DI, DSIZE>>
jamwaffles commented 3 years ago

I believe you're missing a type in the definition of DISP:

use display_interface_i2c::I2CInterface;

type Display = ssd1306::mode::graphics::GraphicsMode<
    I2CInterface<
        stm32f1xx_hal::i2c::BlockingI2c<
            stm32f1xx_hal::pac::I2C1,
            (
                stm32f1xx_hal::gpio::gpiob::PB8<stm32f1xx_hal::gpio::Alternate<OpenDrain>>,
                stm32f1xx_hal::gpio::gpiob::PB9<stm32f1xx_hal::gpio::Alternate<OpenDrain>>,
            ),
        >,
    >,
>;

The display mode structs don't take a raw interface anymore - they're wrapped in implementors of the display_interface crate like display-interface-i2c.

I haven't tested this on hardware, but it does compile. You'll need to add the display_interface_i2c crate to your Cargo.toml if you haven't already.

lll9p commented 3 years ago

Thank you for your reply! @jamwaffles

Follow your description, I make a minimal example about it.

The codes below does compile.

// #![deny(unsafe_code)]
#![allow(unused)]
#![no_main]
#![no_std]
use panic_halt as _;

use crate::hal::{
    gpio::OpenDrain,
    i2c::{BlockingI2c, DutyCycle, Mode},
    prelude::*,
};
use core::{
    cell::{Cell, RefCell},
    fmt::Write,
};
use cortex_m::interrupt::{free, Mutex};
use cortex_m_rt::{entry, exception, ExceptionFrame};

use display_interface_i2c::I2CInterface;
use embedded_graphics::{
    fonts::{Font6x12, Font6x6, Font6x8, Font8x16, Text},
    pixelcolor::BinaryColor,
    prelude::*,
    style::TextStyleBuilder,
};
use ssd1306::{prelude::*, Builder, I2CDIBuilder};
use stm32f1xx_hal as hal;

type DISP = ssd1306::mode::graphics::GraphicsMode<
    I2CInterface<
        stm32f1xx_hal::i2c::BlockingI2c<
            stm32f1xx_hal::pac::I2C1,
            (
                stm32f1xx_hal::gpio::gpiob::PB8<stm32f1xx_hal::gpio::Alternate<OpenDrain>>,
                stm32f1xx_hal::gpio::gpiob::PB9<stm32f1xx_hal::gpio::Alternate<OpenDrain>>,
            ),
        >,
    >,
>;

// static _DISP: Mutex<RefCell<Option<DISP>>> = Mutex::new(RefCell::new(None));

#[entry]
fn main() -> ! {
    let device = stm32f1::stm32f103::Peripherals::take().unwrap();
    let mut core = cortex_m::peripheral::Peripherals::take().unwrap();
    core.DCB.enable_trace();
    core.DWT.enable_cycle_counter();

    let mut flash = device.FLASH.constrain();
    let mut rcc = device.RCC.constrain();

    let clocks = rcc.cfgr.sysclk(32.mhz()).freeze(&mut flash.acr);
    // Prepare the alternate function I/O registers
    let mut afio = device.AFIO.constrain(&mut rcc.apb2);

    let mut gpioa = device.GPIOA.split(&mut rcc.apb2);
    let mut gpiob = device.GPIOB.split(&mut rcc.apb2);

    // I2C Config
    let scl = gpiob.pb8.into_alternate_open_drain(&mut gpiob.crh);
    let sda = gpiob.pb9.into_alternate_open_drain(&mut gpiob.crh);

    let i2c = BlockingI2c::i2c1(
        device.I2C1,
        (scl, sda),
        &mut afio.mapr,
        Mode::Fast {
            frequency: 400_000.hz(),
            duty_cycle: DutyCycle::Ratio2to1,
        },
        clocks,
        &mut rcc.apb1,
        1000,
        10,
        1000,
        1000,
    );

    let interface = I2CDIBuilder::new().init(i2c);
    let mut disp: GraphicsMode<_> = Builder::new().connect(interface).into();
    disp.init().unwrap();

    // make Mutexes
    // free(|cs| {
    //     _DISP.borrow(cs).replace(Some(disp));
    // });

    loop {}
}

But after I changed

    let mut disp: GraphicsMode<_> = Builder::new().connect(interface).into();

to

    let mut disp: DISP = Builder::new().connect(interface).into();

And the code dosent compile, how do I fix this? Thanks again!

jamwaffles commented 3 years ago

Hm... that's strange. The error I get is this:

   |
81 |     let mut disp: DISP = Builder::new().connect(interface).into();
   |                                                            ^^^^ the trait `ssd1306::mode::displaymode::DisplayModeTrait<impl display_interface::WriteOnlyDataCommand, ssd1306::displaysize::DisplaySize128x64>` is not implemented for `ssd1306::mode::graphics::GraphicsMode<display_interface_i2c::I2CInterface<stm32f1xx_hal::i2c::BlockingI2c<stm32f1::stm32f103::I2C1, (stm32f1xx_hal::gpio::gpiob::PB8<stm32f1xx_hal::gpio::Alternate<stm32f1xx_hal::gpio::OpenDrain>>, stm32f1xx_hal::gpio::gpiob::PB9<stm32f1xx_hal::gpio::Alternate<stm32f1xx_hal::gpio::OpenDrain>>)>>>`
   |
   = help: the following implementations were found:
             <ssd1306::mode::graphics::GraphicsMode<DI, DSIZE> as ssd1306::mode::displaymode::DisplayModeTrait<DI, DSIZE>>

Is that the same as yours? I'm not sure how to fix this, but in the meantime I think it's fine to just do let mut disp: GraphicsMode<_> = Builder::new().connect(interface).into();. What's stopping you doing that?

@therealprof Any ideas here? I don't think it's related to the display-interface crates but might be worth checking.

therealprof commented 3 years ago

It seems auto-coercion doesn't work with impl type returns.

After this change it seems to work:

diff --git a/src/builder.rs b/src/builder.rs
index 79794c2..87e7f7c 100644
--- a/src/builder.rs
+++ b/src/builder.rs
@@ -160,7 +160,7 @@ impl I2CDIBuilder {
     /// Finish the builder and return an initialised display interface for further use
     ///
     /// This method consumes the builder and must come last in the method call chain
-    pub fn init<I: hal::blocking::i2c::Write>(self, i2c: I) -> impl WriteOnlyDataCommand {
+    pub fn init<I: hal::blocking::i2c::Write>(self, i2c: I) -> display_interface_i2c::I2CInterface<I> {
         display_interface_i2c::I2CInterface::new(i2c, self.i2c_addr, 0x40)
     }
 }
lll9p commented 3 years ago

@jamwaffles @therealprof Yes, it is impl type issue, after changing code in builder, example code work as expect, thank you.

therealprof commented 3 years ago

Funny overlap, just issued a PR with that change. ;)

therealprof commented 3 years ago

Thanks for confirming.