Giter Club home page Giter Club logo

ssd1306's Introduction

SSD1306 driver

Build Status Crates.io Docs.rs

CRIUS display showing the Rust logo

I2C and SPI (4 wire) driver for the SSD1306 OLED display.

This crate uses probe-run to run the examples. Once set up, it should be as simple as cargo run --example <example name> --release.

From examples/image_i2c.rs:

#![no_std]
#![no_main]

use cortex_m_rt::{entry, exception, ExceptionFrame};
use embedded_graphics::{
    image::{Image, ImageRaw},
    pixelcolor::BinaryColor,
    prelude::*,
};
use panic_halt as _;
use ssd1306::{prelude::*, I2CDisplayInterface, Ssd1306};
use stm32f1xx_hal::{
    i2c::{BlockingI2c, DutyCycle, Mode},
    prelude::*,
    stm32,
};

#[entry]
fn main() -> ! {
    let dp = stm32::Peripherals::take().unwrap();

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

    let clocks = rcc.cfgr.freeze(&mut flash.acr);

    let mut afio = dp.AFIO.constrain(&mut rcc.apb2);

    let mut gpiob = dp.GPIOB.split(&mut rcc.apb2);

    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(
        dp.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 = I2CDisplayInterface::new(i2c);
    let mut display = Ssd1306::new(interface, DisplaySize128x64, DisplayRotation::Rotate0)
        .into_buffered_graphics_mode();
    display.init().unwrap();

    let raw: ImageRaw<BinaryColor> = ImageRaw::new(include_bytes!("./rust.raw"), 64);

    let im = Image::new(&raw, Point::new(32, 0));

    im.draw(&mut display).unwrap();

    display.flush().unwrap();

    loop {}
}

#[exception]
fn HardFault(ef: &ExceptionFrame) -> ! {
    panic!("{:#?}", ef);
}

License

Licensed under either of

at your option.

Contribution

Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in the work by you, as defined in the Apache-2.0 license, shall be dual licensed as above, without any additional terms or conditions.

ssd1306's People

Contributors

alexey-medvedchikov avatar alvinhochun avatar barafael avatar bugadani avatar bzvl avatar careyk007 avatar carlossless avatar disasm avatar eldruin avatar ew1abz avatar jamwaffles avatar jamwafflesci avatar jaxter184 avatar mexus avatar mjadczak avatar mryndzionek avatar shenmian avatar spicyjack avatar thalesfragoso avatar therealprof avatar tpwrules avatar wgh- avatar zyla avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

ssd1306's Issues

Horizontal/vertical flip

On top of rotations a display mirror/flip should also be settable. I propose adding a DisplayFlip trait that allows opt-in flipping both horizontally and vertically.

Newline characters are rendered as spaces in TerminalMode

TerminalMode does not treat newline characters in any special way. Instead, they hit this case which just renders an empty bitmap for that character. I think that the TerminalMode would be much more useful if it recognised \n and skipped to the beginning of the next line.

Travis?

We should have some kind of CI process!

At minimum, right now I think it should:

  • Run rustfmt and fail if any code changes to keep code clean and consistent
  • Run cargo test to make sure the examples build*

*because there aren't any tests at the moment...

Anything else?

Setting a pixel out of bounds crashes the driver

The display buffer has a fixed length. There should be a guard that just does a noop if the pixel coordinates are out of bounds. I think this is better than returning a Result::Err because it allows the user more freedom in what they (try and) show on the display.

Squelch unused result warnings

I'm just being lazy now. The driver shold expose the result of transactions to the end user.

warning: unused `core::result::Result` which must be used
  --> src\interface\i2c.rs:23:9
   |
23 |         self.i2c.write(0x3c, &[0, cmd]);
   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
   |
   = note: #[warn(unused_must_use)] on by default

warning: unused `core::result::Result` which must be used
  --> src\interface\i2c.rs:39:9
   |
39 |         self.i2c.write(0x3c, &writebuf[0..buf.len() + 1]);
   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

warning: unused `core::result::Result` which must be used
  --> src\interface\spi.rs:29:9
   |
29 |         self.spi.write(&[cmd]);
   |         ^^^^^^^^^^^^^^^^^^^^^^^

warning: unused `core::result::Result` which must be used
  --> src\interface\spi.rs:38:9
   |
38 |         self.spi.write(&buf);
   |         ^^^^^^^^^^^^^^^^^^^^^

RTFM

RTFM was renamed to RTIC. To avoid deprecation warnings, we should update references to it.

Fail to compile

  • Version of ssd1306 in use (if applicable): master branch (or "v0.3.0-alpha.2")

Description of the problem/feature request/other

Fail to compile this project because embedded-graphics removed Drawing trait.

cargo build  --example image_i2c
   Compiling ssd1306 v0.3.0-alpha.2 (/Users/pronvis/rust/xxx/ssd1306)
error[E0432]: unresolved import `self::embedded_graphics::Drawing`
   --> src/mode/graphics.rs:267:5
    |
267 |     Drawing,
    |     ^^^^^^^ no `Drawing` in the root

I2C NACK

  • Version of ssd1306 in use (if applicable): 0.3.0-alpha.2
  • MCU/other hardware in use: stm32f446re nucleo board
  • Display resolution and interface i2c, 128x64

Description of the problem/feature request/other

Randomly gets NACK back over I2C. I didn't spend much time debugging, I was trying to get a little Rust logo to bounce back and forth between sides of the screen and noted that it would get about half way before a NACK i2c error.

I hacked this together to tinker with rust, rtfm, and this sweet little display so perhaps I just did something wrong. I did look at the i2c signaling on my scope and it looked clean.

Test case (if applicable)

//#![deny(warnings)]
#![no_std]
#![no_main]

use core::convert::TryInto;
use panic_semihosting as _;
use cortex_m::iprintln;
use rtfm::cyccnt::{Instant, U32Ext as _ };
use stm32f4xx_hal::{prelude::*, i2c::I2c, gpio::{Alternate, AF4, gpiob::{PB8, PB9}}, stm32};
use embedded_graphics::{prelude::*, pixelcolor::BinaryColor};
use ssd1306::{prelude::*, Builder as SSD1306Builder};

const PERIOD: u32 = 1_000_000;

#[rtfm::app(device = stm32f4::stm32f446, peripherals = true, monotonic = rtfm::cyccnt::CYCCNT)]
const APP: () = {

    struct Resources {
        #[init(0)]
        n: u32,
        itm: cortex_m::peripheral::ITM,
        disp: GraphicsMode<ssd1306::interface::i2c::I2cInterface<stm32f4xx_hal::i2c::I2c<stm32f4::stm32f446::I2C1, (stm32f4xx_hal::gpio::gpiob::PB8<stm32f4xx_hal::gpio::Alternate<stm32f4xx_hal::gpio::AF4>>, stm32f4xx_hal::gpio::gpiob::PB9<stm32f4xx_hal::gpio::Alternate<stm32f4xx_hal::gpio::AF4>>)>>>,
        im: Image<'static, BinaryColor>,
    }

    #[init(schedule = [tick])]
    fn init(mut cx: init::Context) -> init::LateResources {

        // Initialize (enable) the monotonic timer (CYCCNT)
        cx.core.DCB.enable_trace();
        // required on devices that software lock the DWT (e.g. STM32F7)
        unsafe { cx.core.DWT.lar.write(0xC5ACCE55) }
        cx.core.DWT.enable_cycle_counter();

        let mut itm = cx.core.ITM;

        // semantically, the monotonic timer is frozen at time "zero" during `init`
        let now = cx.start; // the start time of the system

        // Schedule `tick` to run 8e6 cycles (clock cycles) in the future
        cx.schedule.tick(now + PERIOD.cycles()).unwrap();

        //iprintln!(&mut itm.stim[0], "init @ {:?}", now);

        let rcc = cx.device.RCC.constrain();
        let clocks = rcc.cfgr.sysclk(48.mhz()).freeze();
        let gpiob = cx.device.GPIOB.split();

        let scl = gpiob.pb8.into_alternate_af4();
        let sda = gpiob.pb9.into_alternate_af4();

        let i2c = I2c::i2c1(
            cx.device.I2C1,
            (scl, sda),
            400.khz(),
            clocks
        );

        let mut disp: GraphicsMode<_> = SSD1306Builder::new().connect_i2c(i2c).into();

        let im: Image<BinaryColor> = Image::new(include_bytes!("./rust.raw"), 64, 64);

        disp.init().unwrap();
        disp.flush().unwrap();
        //disp.clear();
        //disp.flush().unwrap();
        disp.draw(im.into_iter());
        disp.flush().unwrap();

        init::LateResources {
            itm: itm,
            disp: disp,
            im: im,
        }
    }

    #[task(schedule = [tick], resources = [itm, n, im, disp])]
    fn tick(cx: tick::Context) {

        let now = Instant::now();

        *cx.resources.n += 1;
        let x_coord: i32 = (*cx.resources.n % 128).try_into().unwrap();

        cx.resources.disp.draw(cx.resources.im.translate(Point::new(x_coord, 0)).into_iter());
        cx.resources.disp.flush().unwrap();

        cx.schedule.tick(cx.scheduled + PERIOD.cycles()).unwrap();
        //iprintln!(&mut cx.resources.itm.stim[0], "tick(scheduled = {:?}, now = {:?})", cx.scheduled, now);
    }

    extern "C" {
        fn UART4();
    }
};

test case dependencies

[dependencies]
cortex-m = "0.6.1"
cortex-m-rt = "0.6.10"
cortex-m-semihosting = "0.3.3"
cortex-m-rtfm = "0.5.0"
panic-semihosting = "0.5.3"
embedded-graphics = "0.6.0-alpha.2"
ssd1306 = "0.3.0-alpha.2"

stm32f4xx-hal = {version = "0.6.0", features = ["stm32f446"]}
stm32f4 = {version = "0.8.0", features = [ "stm32f446", "rt" ]}

Re: latest build errors

I'm having the exact same issue with a lot of my projects. It's due to crates using an outdated version of proc-macro2. Most use 0.3.8 but the issues are fixed on the latest. I think the solution is a whole lot of PRs with the latest version, but its hard to replicate locally (each broken crate requires cloning then making dependancies point to a local directory).

Shall we hide the interface Error type?

Currently each communication interface uses their own interface specific Error type which is passed down all the way to the caller as associated type which requires knowledge about the interface type being used and hence making that knowledge available throughout the driver. However I don't see why the caller would be interested in handling interface (and even HAL implementation) specific errors since that is a lot of work without any particular benefit. In order to get rid of the complexity I would suggest to rewrite the error to a new custom error type.

Remove hard requirement on `embedded_graphics`

This crate is purely a driver, so should not require embeddeed_graphics as a hard dependency. It should be feature gated instead for those that do want to use it.

Also, where should examples that used embedded_graphics go? Should they stay in this crate or be moved to e_g?

Can not determin type of display interface

  • Version of ssd1306 in use (if applicable): Master branch of github.
  • MCU/other hardware in use: STM32f103 the BluePill
  • Display resolution and interface: [I2C], [128x64]

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>>

Building examples fails due to deprecated digital::OutputPin

I just tried to build the examples using a fresh clone of the repo and got a number of errors (escalated from warnings due to https://github.com/jamwaffles/ssd1306/blob/master/src/lib.rs#L160)

error: use of deprecated item 'hal::digital::OutputPin::set_low': Deprecated because the methods cannot return errors. Users should use the traits in digital::v2.

It sounds like the solution is to just update to the latest traits, so I did that in my local copy and got the examples to build. I can create a PR, but right now I'm just throwing away the result from the pin state changes (which I guess effectively matches the existing behaviour). If more thorough error handling seems appropriate I'm open to that.

Will this driver also work with stm32f7xx-hal?

Hey, I'm new to rust and wondering if this driver can also work with the stm32f7xx-hal https://github.com/stm32-rs/stm32f7xx-hal/

It exposes a similar BlockingI2c struct: https://github.com/stm32-rs/stm32f7xx-hal/blob/471ca4de7026612b28f419b10f0ec0ef77813071/src/i2c.rs but I'm struggling to glue things together.

To me it seems that the st32f7xx-hal isn't particularly stable and is a WIP. Maybe it would be better to not use it for now. Is it possible to use this library without it?

Thanks for any assistance

0.3.0 release docs

0.2.6 is still to be released but won't need more than a changelog entry to describe non-breaking changes. 0.3.0 will upgrade embedded-graphics to 0.6.0 so will be a much more substantial change. It should have either a blog post, or a migration guide, or a link to the embedded-graphics migration guide created for it.

RTFM and I2C

Hello,

I'm very sorry, but I am having trouble using cortex_m_rtfm and an I2C SSD1306. I have it working in a non-RTFM program, and the only examples of using your library with RTFM are with an SPI interface. I keep getting variant not found and proc macro panicked errors in app!.

How would I write the same blinky example using I2C?

Thanks! :)

Inconsistent Uppercase Font in Terminal Mode

  • Version of ssd1306 in use (if applicable): 0.4.0
  • MCU/other hardware in use: atmega328p
  • Display resolution and interface: [I2C], [128x64]

Description of the problem/feature request/other

Several uppercase characters in terminal mode are bold, inconsistent with the rest. The characters in question are DLORW, which use 2-3 pixel font instead of 1 pixel like the rest of the characters.

Example code and result:

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

disp.init().unwrap();

disp.clear().unwrap();

for c in "!\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|".chars() {
    disp.print_char(c);
}

oled_font

Add rotation support

It should be possible to set the rotation to one of 4 settings (0ยบ, 90ยบ, 180ยบ, 270ยบ). The builder should have an option, something like with_rotation() which accepts an enum.

Display power off

Is it possible to power off the display using command 0xAE? This would enable saving power and OLED life in long-running applications.

"no method named `draw` found for type"

I am trying to use this library in my project with little success. When I compile my code as an example it works without issue, but in a standalone project I get the following error:

error[E0599]: no method named ``draw`` found for type ``ssd1306::SSD1306<ssd1306::interface::I2cInterface<hal::i2c::I2c<hal::<unnamed>::I2C1, (hal::gpio::gpiob::PB6<hal::gpio::Alternate<hal::gpio::OpenDrain>>, hal::gpio::gpiob::PB7<hal::gpio::Alternate<hal::gpio::OpenDrain>>)>>>`` in the current scope --> src/main.rs:62:10 | 62 | disp.draw(Font6x8::render_str("Hello world!", (0, 0)).into_iter()); | ^^^^

error[E0599]: no method named ``draw`` found for type ``ssd1306::SSD1306<ssd1306::interface::I2cInterface<hal::i2c::I2c<hal::<unnamed>::I2C1, (hal::gpio::gpiob::PB6<hal::gpio::Alternate<hal::gpio::OpenDrain>>, hal::gpio::gpiob::PB7<hal::gpio::Alternate<hal::gpio::OpenDrain>>)>>>`` in the current scope --> src/main.rs:63:10 | 63 | disp.draw(Font6x8::render_str("Hello Rust!", (0, 16)).into_iter()); | ^^^^

Any idea what it could be?

Thanks!

.cargo/config lacks default target and contains Windows specific stuff

[target.thumbv7m-none-eabi]
runner = [ "arm-none-eabi-gdb.exe", "-iex", "set auto-load safe-path ." ]
rustflags = [
  "-C", "link-arg=-Tlink.x",
  "-C", "linker=arm-none-eabi-ld",
  "-Z", "linker-flavor=ld",
  "-Z", "thinlto=no",
]

If this file exists it should be useful and hopefully not contain host specific information.

Builder examples are not tested don't fully explain that order maters

Looking at the documentation for the Builder module, it states I can do the following:

use ssd1306::prelude::*;
use ssd1306::Builder;

fn main() {
    let mut disp: GraphicsMode<_> = Builder::new()
        .connect_i2c(i2c)
        .with_size(DisplaySize::Display128x32)
        .into();
}

However, I get the following error:

error[E0599]: no method named `with_size` found for type `ssd1306::mode::displaymode::DisplayMode<ssd1306::mode::raw::RawMode<ssd1306::interface::i2c::I2cInterface<hal::sercom::I2CMaster3>>>` in the current scope
  --> src/main.rs:49:10
   |
49 |         .with_size(DisplaySize::Display128x32)
   |          ^^^^^^^^^

The documentation should be fixed and enable testing on them so this doesn't happen again

Collaborate!

@therealprof @jamwaffles

At leas the three of us have some code lying around trying to accomplish different things with this little display. This repository was offered up as a forum for putting together an SSD1306 driver we can all work with at rust-embedded/wg#39.

I think we should lay down what our various goals are and decide the best path forward here.

Configurable I2C address

Either โ€œ0111100โ€ (0x3C) or โ€œ0111101โ€ (0x3D), can be selected as the slave address of SSD1306. We're hard-coded for 0x3C.

Release 0.1 milestone

@scowcron @therealprof I've made a release milestone so there's a short-term(-ish) goal to work towards. I've already assigned a couple of issues to it. I'd like to collect a set of features/requirements from you (and anybody else reading this!) as a set of issues with the Enhancement tag and the Release 0.1 milestone. Please add any desired features/bugs/requirements to the issue list so we can get something published :D

It would also be nice to be published in the embedded WG newsletter to get some people using this driver.

the trait bound ... is not satisfied

Building any example that uses i2c on both nightly-2018-05-10 and nightly-2018-04-18 results in the following errors. I am not sure if it is issue with ssd1306, my apologies if it is with another crate.

error[E0277]: the trait bound `blue_pill::i2c::I2c<blue_pill::<unnamed>::I2C1, (blue_pill::gpio::gpiob::PB8<blue_pill::gpio::Alternate<blue_pill::gpio::OpenDrain>>, blue_pill::gpio::gpiob::PB9<blue_pill::gpio::Alternate<blue_pill::gpio::OpenDrain>>)>: hal::prelude::Write` is not satisfied
  --> examples/graphics_i2c.rs:60:52
   |
60 |     let mut disp: GraphicsMode<_> = Builder::new().connect_i2c(i2c).into();
   |                                                    ^^^^^^^^^^^ the trait `hal::prelude::Write` is not implemented for `blue_pill::i2c::I2c<blue_pill::<unnamed>::I2C1, (blue_pill::gpio::gpiob::PB8<blue_pill::gpio::Alternate<blue_pill::gpio::OpenDrain>>, blue_pill::gpio::gpiob::PB9<blue_pill::gpio::Alternate<blue_pill::gpio::OpenDrain>>)>`

error[E0277]: the trait bound `blue_pill::i2c::I2c<blue_pill::<unnamed>::I2C1, (blue_pill::gpio::gpiob::PB8<blue_pill::gpio::Alternate<blue_pill::gpio::OpenDrain>>, blue_pill::gpio::gpiob::PB9<blue_pill::gpio::Alternate<blue_pill::gpio::OpenDrain>>)>: hal::prelude::Write` is not satisfied
  --> examples/graphics_i2c.rs:60:69
   |
60 |     let mut disp: GraphicsMode<_> = Builder::new().connect_i2c(i2c).into();
   |                                                                     ^^^^ the trait `hal::prelude::Write` is not implemented for `blue_pill::i2c::I2c<blue_pill::<unnamed>::I2C1, (blue_pill::gpio::gpiob::PB8<blue_pill::gpio::Alternate<blue_pill::gpio::OpenDrain>>, blue_pill::gpio::gpiob::PB9<blue_pill::gpio::Alternate<blue_pill::gpio::OpenDrain>>)>`
   |
   = note: required because of the requirements on the impl of `ssd1306::interface::DisplayInterface` for `ssd1306::interface::I2cInterface<blue_pill::i2c::I2c<blue_pill::<unnamed>::I2C1, (blue_pill::gpio::gpiob::PB8<blue_pill::gpio::Alternate<blue_pill::gpio::OpenDrain>>, blue_pill::gpio::gpiob::PB9<blue_pill::gpio::Alternate<blue_pill::gpio::OpenDrain>>)>>`

error[E0277]: the trait bound `blue_pill::i2c::I2c<blue_pill::<unnamed>::I2C1, (blue_pill::gpio::gpiob::PB8<blue_pill::gpio::Alternate<blue_pill::gpio::OpenDrain>>, blue_pill::gpio::gpiob::PB9<blue_pill::gpio::Alternate<blue_pill::gpio::OpenDrain>>)>: hal::prelude::Write` is not satisfied
  --> examples/graphics_i2c.rs:60:19
   |
60 |     let mut disp: GraphicsMode<_> = Builder::new().connect_i2c(i2c).into();
   |                   ^^^^^^^^^^^^^^^ the trait `hal::prelude::Write` is not implemented for `blue_pill::i2c::I2c<blue_pill::<unnamed>::I2C1, (blue_pill::gpio::gpiob::PB8<blue_pill::gpio::Alternate<blue_pill::gpio::OpenDrain>>, blue_pill::gpio::gpiob::PB9<blue_pill::gpio::Alternate<blue_pill::gpio::OpenDrain>>)>`
   |
   = note: required because of the requirements on the impl of `ssd1306::interface::DisplayInterface` for `ssd1306::interface::I2cInterface<blue_pill::i2c::I2c<blue_pill::<unnamed>::I2C1, (blue_pill::gpio::gpiob::PB8<blue_pill::gpio::Alternate<blue_pill::gpio::OpenDrain>>, blue_pill::gpio::gpiob::PB9<blue_pill::gpio::Alternate<blue_pill::gpio::OpenDrain>>)>>`
   = note: required by `ssd1306::mode::GraphicsMode`

error[E0599]: no method named `init` found for type `ssd1306::mode::GraphicsMode<ssd1306::interface::I2cInterface<blue_pill::i2c::I2c<blue_pill::<unnamed>::I2C1, (blue_pill::gpio::gpiob::PB8<blue_pill::gpio::Alternate<blue_pill::gpio::OpenDrain>>, blue_pill::gpio::gpiob::PB9<blue_pill::gpio::Alternate<blue_pill::gpio::OpenDrain>>)>>>` in the current scope
  --> examples/graphics_i2c.rs:62:10
   |
62 |     disp.init().unwrap();
   |          ^^^^
   |
   = note: the method `init` exists but the following trait bounds were not satisfied:
           `ssd1306::interface::I2cInterface<blue_pill::i2c::I2c<blue_pill::<unnamed>::I2C1, (blue_pill::gpio::gpiob::PB8<blue_pill::gpio::Alternate<blue_pill::gpio::OpenDrain>>, blue_pill::gpio::gpiob::PB9<blue_pill::gpio::Alternate<blue_pill::gpio::OpenDrain>>)>> : ssd1306::interface::DisplayInterface`

Print text seems to not work with stm32l432kc

  • Version of ssd1306 in use (if applicable): 0.3.0-alpha.2
  • MCU/other hardware in use: stm32l432kc
  • Display resolution and interface: I2C, 128x64

Description of the problem/feature request/other

I was trying to print some text out but with the code below I not able to see anything into the display. I've tried to change che I2C pin, frequency etc; I didn't spend much time debugging because I don't know if the problem is related to the stm32l432kc implementation, with the ssd1306 or not. How can I test this issue?

Test case (if applicable)

//! Reads data from a gyuvl53l0x sensor

#![no_main]
#![no_std]

extern crate cortex_m;
extern crate cortex_m_rt as rt;
extern crate stm32l4xx_hal as hal;
extern crate ssd1306;

use cortex_m_semihosting::hprintln;
use crate::hal::prelude::*;
use crate::hal::stm32l4::stm32l4x2;
use crate::hal::i2c::I2c;
use crate::rt::entry;
use core::panic::PanicInfo;

use ssd1306::{mode::GraphicsMode, Builder, interface::I2cInterface};

use embedded_graphics::pixelcolor::BinaryColor;
use embedded_graphics::fonts::{Font6x8, Font12x16};
use embedded_graphics::prelude::*;
use embedded_graphics::Drawing;

#[entry]
fn main() -> ! {
    hprintln!("Hello, world!").unwrap();

    let dp = stm32l4x2::Peripherals::take().unwrap();

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

    let clocks = rcc.cfgr.freeze(&mut flash.acr);

    let mut gpioa = dp.GPIOA.split(&mut rcc.ahb2);
    
    let mut gpiob = dp.GPIOB.split(&mut rcc.ahb2);
    let mut led = gpiob.pb3.into_push_pull_output(&mut gpiob.moder, &mut gpiob.otyper);

    let mut scl = gpioa
        .pa9
        .into_open_drain_output(&mut gpioa.moder, &mut gpioa.otyper);
    scl.internal_pull_up(&mut gpioa.pupdr, true);
    let scl = scl.into_af4(&mut gpioa.moder, &mut gpioa.afrh);

    let mut sda = gpioa
        .pa10
        .into_open_drain_output(&mut gpioa.moder, &mut gpioa.otyper);
    sda.internal_pull_up(&mut gpioa.pupdr, true);
    let sda = sda.into_af4(&mut gpioa.moder, &mut gpioa.afrh);

    hprintln!("Start configuring I2C").unwrap();

    let i2c = I2c::i2c1(dp.I2C1, (scl, sda), 400.khz(), clocks, &mut rcc.apb1r1);

    hprintln!("Start configuring SSD1306").unwrap();

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

    disp.init().unwrap();
    disp.flush().unwrap();

    let (a, b) = disp.get_dimensions();

    hprintln!("{:#?}", a).unwrap();
    hprintln!("{:#?}", b).unwrap();

    disp.draw(
        Font12x16::render_str("rust")
        .stroke(Some(BinaryColor::On))
        .translate(Point::new(10, 10))
            .into_iter(),
    );

    hprintln!("b").unwrap();
    disp.flush().unwrap();

    led.set_high().unwrap();
    hprintln!("c").unwrap();
    
    loop {}
}

#[panic_handler]
#[no_mangle]
pub fn panic(_info: &PanicInfo) -> ! {
    loop{}
}

test case dependencies

[package]
authors = ["Luca Zulian <[email protected]>"]
edition = "2018"
readme = "README.md"
name = "robot"
version = "0.1.1"

[dependencies]
cortex-m = "0.6.1"
cortex-m-rtfm = "0.4.3"
panic-halt = "0.2.0"
alloc-cortex-m = "0.3.5"
embedded-hal = "0.2.3"
generic-array = "0.13.2"
nb = "0.1.0"
cortex-m-semihosting = "0.3.5"

gyuvl53l0x = "0.1.3"
ssd1306 = "0.3.0-alpha.2"
embedded-graphics = "0.6.0-alpha.2"

[dependencies.stm32l4xx-hal]
git = "https://github.com/stm32-rs/stm32l4xx-hal"
branch = "master"
features = ["stm32l4x2", "rt"]

[dependencies.cortex-m-rt]
version = "0.6.10"
features = ["device"]

[dependencies.cast]
default-features = false
version = "0.2.2"

[dependencies.safe-transmute]
version =  "0.10.1"
default-features = false

[[bin]]
name = "robot"
test = false
bench = false

[profile.release]
codegen-units = 1 # better optimizations
debug = false # symbols are nice and they don't increase the size on Flash
lto = true # better optimizations
panic = "abort"
opt-level = 's'

[profile.dev]
panic = "abort"

Can someone explain me how to "solve" or debug this kind of issue?

Release builds only work with opt-level = 0/1

Hello,

I am only able to get release builds to function if opt-level = 0 or opt-level = 1 is added to Cargo.toml. If it is not added there are no errors during building and a much smaller binary is produced, but nothing is displayed. I have tested this with my own project, and both the blinky and graphics examples. I have not tested on any I2C displays.

I'm probably doing something stupid and normally I'd be fine just using opt-level = 1 and calling it a day, but unfortunately the binaries are too large for my MCU.

Thanks! :)

Edit: I was wrong to say nothing is displayed; every 4 or 5 resets sometimes the program works and stuff is displayed, but cutting power and resetting again results in no image. Debug binaries always work regardless of power loss and frequency of resets.

Second edit: I realise it is probably just the display's internal cache retaining data written with debug binaries

.draw() methods seem to not work with embedded-graphics v0.5+

I've been able to draw fonts and such with embedded-graphics v0.4.5 with this code:

use embedded_graphics::prelude::*;
use embedded_graphics::fonts;

use ssd1306::prelude::*;
use ssd1306::Builder;

#[entry]
fn main() -> ! {

    // ...snip...

    let mut disp: GraphicsMode<_> = Builder::new()
        .with_size(DisplaySize::Display128x32)
        .connect_i2c(i2c)
        .into();

    disp.init().unwrap();
    disp.flush().unwrap();

    disp.draw(
        fonts::Font12x16::render_str("Hello Rust!")
            .into_iter(),
    );
    disp.flush().unwrap();

but once I update to latest embedded-graphics, I get the following error:

error[E0599]: no method named `draw` found for type `ssd1306::mode::graphics::GraphicsMode<ssd1306::interface::i2c::I2cInterface<hal::sercom::I2CMaster3>>` in the current scope
  --> src/main.rs:55:10
   |
55 |     disp.draw(
   |          ^^^^
   |
   = help: items from traits can only be used if the trait is in scope
help: the following trait is implemented but not in scope, perhaps add a `use` for it:
   |
14 | use embedded_graphics::Drawing;
   |

Is this expected? I can't easily see why this is breaking in the first place. Explicitly calling use embedded_graphics::Drawing doesn't seem to fix it for me.

Add a prelude

There are quite a few TODOs scattered around the codebase about adding various items to a prelude to make using the crate easier. Should be easy to add a few things so that people can just add use ssd1306::prelude::*.

display + IRQ/timer example proposal

  • Version of ssd1306 in use (if applicable): 0.3.0
  • MCU/other hardware in use: STM32F0xx
  • Display resolution and interface: I2C

SSD1306 and interrupts/timers example

I believe it would be useful to add an example with the SSD1306 being used to visualize something that is updated with interrupts, e.g. a counter, timer or similar. As the F0xx crate specifically requires the critical sections to be used in peripherals setup, it is not easy to adapt existing examples made for F1 or F4 boards.
I tried to re-use and adapt the code for blinking LEDs with interrupts from the F0xx-HAL crate, but I don't know how to approach it. In case of LEDs it seems quite straightforward: they are made global variables, wrapped in Mutex. How should this be done for the display: is it the SDA and SCL pins, as well as I2C and RCC that have to be global, or is there a way to make the display instance global, while everything else is set up within the Critical Section? If the latter is true, what should the type be, and what needs to be inside mutex?

Here's my non-working code that gives an idea of what the problem is. https://github.com/nebelgrau77/STM32F0-SSD1306-IRQ_DRAFT

This is a working example on STM32F411: it is easier with that crate, as the peripherals don't need to be defined within CS:
https://github.com/nebelgrau77/STM32F4-OLED-IRQ_timecounter

I believe such example would be useful for any kind of project that needs to use interrupts AND a display, e.g. involving some ADC readings, clocks and such.

How to change addressing mode?

  • Version of ssd1306 in use: 0.4
  • MCU/other hardware in use: nRF52840
  • Display resolution and interface: I2C, 128x64

How to use addressing mode?

I'm experimenting with sending images over serial to the board. I'm using the .draw method like in the white noise example,
sending the serial RX buffer to the display. I noticed that the bits of the bytes are written top to bottom, but then the bytes are in horizontal order. So basically for each byte I get 8 vertical pixels until the end of the row, and so on. Which is different from what I imagined :) I understand this is the the default page mode? How can I change it to Horizontal, or in other words, how do I use init_with_mode or change_mode?

Different display modes

Spinoff from #2 (comment):

So thinking a bit more about this. I think we can introduce display modes by separating the current buffer initialisation from the interface initialisation. Then we could use different types for the different modes and into_...() functions to turn the initialised display interface into a specific output interface.

I'd have called @jamwaffles egfx, mine char and @scowcron pixbuf.

Comments?

Examples for stm32f30x

As mentioned, we should have a couple of examples (one with and one without graphics perhaps?) using the stm32f30x. The examples at the moment use the stm32f103 chip commonly found on "Blue Pill" dev boards so shouldn't be too difficult to port.

Use with a ssd1305 chip, and display size 132x64.

I have this display from adafruit. It's a ssd1305 but in adafruit's circuit python tutorials they use a ssd1306 driver. I ran out of memory programming the chip in python so I'm switching to rust and found this library.

In python I had to define the display size as 132x64, despite being advertised as 128x32. With this display size the top left pixel is 32x4. If I use a display size of 128x32 then every other line is disabled (looks like an old crt).

I see the same behavior with your library, if I use a TerminalMode driver with DisplaySize::Display128x32 then every other line is disabled. If I use 128x64 the display is much better, each character looks correct, but the text starts off screen. The last columns of the display also do not work, it will show random pixels when the display is initialized and they will not change or clear.

  • Would it make sense to add Display132x64?
  • I saw display.properties.set_display_area but it isn't public, is there a way to set that?

Command::send() is highly inefficient

Even the simplest blinky example compiles down to this horrorshow on ARMv7M:

File  .text    Size Name
0.0%   0.6%     74B [10 Others]
2.6%  79.4% 10.1KiB ssd1306::command::Command::send
0.2%   6.9%    900B blinky::init
0.1%   3.3%    424B cortex_m_rt::reset_handler
0.1%   2.3%    300B <ssd1306::mode::graphics::GraphicsMode<DI>>::flush
0.0%   1.3%    166B __aeabi_memset4
0.0%   1.2%    152B blinky::draw_square
0.0%   1.1%    142B cortex_m_rtfm::atomic
0.0%   1.0%    134B <ssd1306::mode::graphics::GraphicsMode<DI>>::set_pixel
0.0%   0.8%    100B memset
0.0%   0.7%     96B memcpy
0.0%   0.6%     80B blinky::idle
0.0%   0.2%     30B blinky::main
0.0%   0.1%     10B __aeabi_memset
0.0%   0.1%     10B core::result::unwrap_failed
0.0%   0.1%     10B core::panicking::panic_bounds_check
0.0%   0.1%     10B SVCALL
0.0%   0.1%     10B MEM_MANAGE
0.0%   0.1%     10B DEBUG_MONITOR
0.0%   0.1%     10B PENDSV
0.0%   0.1%     10B USAGE_FAULT
3.3% 100.0% 12.7KiB .text section size, the file size is 387.9KiB

Obviously the compiler fails to see through the nature of the call to generate straight forward output.

It's not as close as bad on ARMv6M but a trivial test application easily doubles in size mostly due to that one function:

File  .text   Size Name
0.2%  13.5%   320B [35 Others]
0.4%  27.3%   644B ssd1306::command::Command::send
0.2%  15.6%   368B main
0.2%  14.9%   352B <ssd1306::properties::DisplayProperties<DI>>::init_column_mode
0.1%   6.1%   144B <ssd1306::properties::DisplayProperties<DI>>::set_rotation
0.1%   5.1%   120B <ssd1306::interface::i2c::I2cInterface<I2C> as ssd1306::interface::DisplayInterface>::send_data
0.1%   4.2%   100B <stm32f042_hal::i2c::I2c<stm32f042::I2C1, PINS> as embedded_hal::blocking::i2c::Write>::write
0.0%   3.6%    84B <ssd1306::mode::character::CharacterMode<DI> as core::fmt::Write>::write_str
0.0%   2.7%    64B cortex_m_rt::reset_handler
0.0%   1.8%    42B <ssd1306::interface::i2c::I2cInterface<I2C> as ssd1306::interface::DisplayInterface>::send_command
0.0%   0.8%    18B __aeabi_memcpy
0.0%   0.6%    14B __aeabi_memset
0.0%   0.5%    12B __aeabi_memclr
0.0%   0.4%    10B core::result::unwrap_failed
0.0%   0.4%    10B core::panicking::panic_bounds_check
0.0%   0.4%    10B core::slice::slice_index_order_fail
0.0%   0.4%    10B core::slice::slice_index_len_fail
0.0%   0.4%    10B PVD
0.0%   0.4%    10B TIM1_BRK_UP_TRG_COM
0.0%   0.4%    10B DMA_CH4_5_6_7
0.0%   0.4%    10B EXTI0_1

Rotation is all screwy

From #27:

Rotation acts really funny for 90 and 270 degrees, it basically mirrors the characters and changes from l-t-r to r-t-l mode with character mode.

This is probably an artifact of me only testing an image (of the Rust logo). I'll do some more thorough testing taking into account LTR/RTL direction. I think it's fine for graphics where entire display is refreshed at once, but char mode will depend on fill/scroll directions.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.