rust-embedded-community / ssd1306

SSD1306 OLED driver
Apache License 2.0
311 stars 70 forks source link

I2C NACK #99

Closed teburd closed 4 years ago

teburd commented 4 years ago

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" ]}
jamwaffles commented 4 years ago

Thanks for taking the time to post a complete test case! I'll take a look on my display when I can.

teburd commented 4 years ago

Thanks for making a sweet embedded library! Let me know if there's anything I can test for you

jamwaffles commented 4 years ago

Will do! I don't have a scope (which is lunacy tbh) so I'll let you know :slightly_smiling_face:

jamwaffles commented 4 years ago

I just spent the last hour in vain trying to get the vanilla schedule.rs example working on my Blue Pill (STM32F103, so not the same CPU) to no avail. I do however have an STM32F411RET6 board I'll try to reproduce your issue on which I'll try when I'm next at home.

Have you got any further on your side? Can you try running your code on a different dev board to see if it's the HAL/chip maybe? Also, have you tried running in --release mode, or with a lower I2C frequency?

teburd commented 4 years ago

I did try 100 khz i2c previously without success. I have an nrf52 board I can try and an stm32f303 nucleo board I can also try, will try and make time today for that

jamwaffles commented 4 years ago

I gave your example code a test on my STM32F411 board and it works fine in --release mode. I can't get my setup to work with a non-release build so I can't test that.

One thing I did do differently was to use the Git repo directly, not a release on crates.io. Can you give that a go with

[dependencies.stm32f4xx-hal]
git = "https://github.com/stm32-rs/stm32f4xx-hal.git"

I also found and fixed an issue I had where nothing would show on the display without adding .set_open_drain(). That might also help you. Details here: stm32-rs/stm32f4xx-hal#120.

teburd commented 4 years ago

I'll try that out, thanks!

teburd commented 4 years ago

@jamwaffles just an update from me on this, did spend a good amount of time playing with this last night, but couldn't get things to build properly with alpha.2. crates.io and cargo don't seem to obey anything after the last point release version, it automatically updated emebedded graphics to alpha.3 which broke this crate for me. I tried to do everything via git and fixing things up without much luck. I'll try again once this is updated to use the newer embedded graphics crate and will attach a logic analyzer.

Can recommend anything sigrok supports if you don't have one yourself.

jamwaffles commented 4 years ago

Cheers for the update. I released ssd1306 version 0.3.0-alpha.3 yesterday which hopefully fixes the versioning issue via #103. Can you try that version (and maybe delete Cargo.lock or do a cargo update after) and see if you get anywhere? Apologies for the breakage.

Can recommend anything sigrok supports if you don't have one yourself.

Aye I found a knockoff 8 channel one down the back of my couch lol. Haven't had time to test it yet. Let me know how you get on!

teburd commented 4 years ago

I think I'm going to chalk up the remaining issues with this display to me either destroying it by tossing it around like the aliexpress cheapo it is, I get a line down the middle of the screen but I don't believe that's a driver or e-g issue. Cheers! Thanks for the help

jamwaffles commented 4 years ago

There's a growing corpus of issues with I2C generally so yeah, not sure it's this driver.

I get a line down the middle of the screen

Is it bi-colour? IIRC you sent a gif and it was a blue/yellow display? They have a physical gap between the blue and yellow areas which is annoying.

teburd commented 4 years ago

Yes its a bi-colour display with yellow/blue