rp-hal icon indicating copy to clipboard operation
rp-hal copied to clipboard

PRI PICO with SH1106 128x64 pixel Display - i2c issue

Open ds2k5 opened this issue 1 year ago • 17 comments

Hello, I try to get output at the Display. But have error at compile with i2c

What did I wrong ?

//! # GPIO 'Blinky' Example
//!
//! This application demonstrates how to control a GPIO pin on the RP2040.
//!
//! It may need to be adapted to your particular board layout and/or pin assignment.
//!
//! See the `Cargo.toml` file for Copyright and license details.

#![no_std]
#![no_main]
#![allow(unreachable_code, unused_labels)]


// Ensure we halt the program on panic (if we don't mention this crate it won't
// be linked)
use panic_halt as _;

// Alias for our HAL crate
use rp2040_hal as hal;

use hal::i2c::*;
use hal::prelude::*;
use hal::peripherals::Peripherals;
use hal::delay::FreeRtos;

use sh1106::{prelude::*, I2CDisplayInterface, Builder};

use embedded_graphics::{
    mono_font::{ascii::FONT_6X10, MonoTextStyleBuilder},
    pixelcolor::BinaryColor,
    prelude::*,
    text::{Baseline, Text},
    primitives::{Circle, PrimitiveStyleBuilder}
};


// A shorter alias for the Peripheral Access Crate, which provides low-level
// register access
use hal::pac;

// Some traits we need
use embedded_hal::blocking::delay::DelayMs;
use embedded_hal::digital::v2::OutputPin;

/// The linker will place this boot block at the start of our program image. We
/// need this to help the ROM bootloader get our code up and running.
/// Note: This boot block is not necessary when using a rp-hal based BSP
/// as the BSPs already perform this step.
#[link_section = ".boot2"]
#[used]
pub static BOOT2: [u8; 256] = rp2040_boot2::BOOT_LOADER_GENERIC_03H;

/// External high-speed crystal on the Raspberry Pi Pico board is 12 MHz. Adjust
/// if your board has a different frequency
const XTAL_FREQ_HZ: u32 = 12_000_000u32;

/// Entry point to our bare-metal application.
///
/// The `#[rp2040_hal::entry]` macro ensures the Cortex-M start-up code calls this function
/// as soon as all global variables and the spinlock are initialised.
///
/// The function configures the RP2040 peripherals, then toggles a GPIO pin in
/// an infinite loop. If there is an LED connected to that pin, it will blink.
#[rp2040_hal::entry]
//fn main() -> ! {
fn main() -> Result<()> {

    // Grab our singleton objects
    let mut pac = pac::Peripherals::take().unwrap();

    // Set up the watchdog driver - needed by the clock setup code
    let mut watchdog = hal::Watchdog::new(pac.WATCHDOG);

    // Configure the clocks
    let clocks = hal::clocks::init_clocks_and_plls(
        XTAL_FREQ_HZ,
        pac.XOSC,
        pac.CLOCKS,
        pac.PLL_SYS,
        pac.PLL_USB,
        &mut pac.RESETS,
        &mut watchdog,
    )
    .ok()
    .unwrap();

    let mut timer = rp2040_hal::Timer::new(pac.TIMER, &mut pac.RESETS, &clocks);

    // The single-cycle I/O block controls our GPIO pins
    let sio = hal::Sio::new(pac.SIO);


    let peripherals = Peripherals::take().unwrap();
    let i2c = peripherals.i2c0;
    let sda = peripherals.pins.gpio18;
    let scl = peripherals.pins.gpio19;

    let config = I2cConfig::new().baudrate(100.kHz().into());
    let i2c = I2cDriver::new(i2c, sda, scl, &config)?;

    let interface = I2CDisplayInterface::new(i2c);
    let mut display = sh1106::new(interface, DisplaySize128x64, DisplayRotation::Rotate0)
        .into_buffered_graphics_mode();
    display.init().unwrap();


    let text_style = MonoTextStyleBuilder::new()
    .font(&FONT_6X10)
    .text_color(BinaryColor::On)
    .build();

let off = PrimitiveStyleBuilder::new()
    .stroke_width(1)
    .stroke_color(BinaryColor::Off)
    .build();

let on = PrimitiveStyleBuilder::new()
    .stroke_width(1)
    .stroke_color(BinaryColor::On)
    .build();


Text::with_baseline("Picoboy with", Point::new(30, 0), text_style, Baseline::Top)
    .draw(&mut display)
    .unwrap();

Text::with_baseline("SH1106 in Rust!", Point::new(30, 16), text_style, Baseline::Top)
    .draw(&mut display)
    .unwrap();




    // Set the pins to their default state
    let pins = hal::gpio::Pins::new(
        pac.IO_BANK0,
        pac.PADS_BANK0,
        sio.gpio_bank0,
        &mut pac.RESETS,
    );

    // Configure GPIO5 as an output - LED red
    let mut led_pin = pins.gpio5.into_push_pull_output();

        'outer: loop {
            led_pin.set_high().unwrap();
            timer.delay_ms(500);
            led_pin.set_low().unwrap();
            timer.delay_ms(500);

            //LED yellow
            let mut led_pin = pins.gpio6.into_push_pull_output();

            'inner: loop {
                led_pin.set_high().unwrap();
                timer.delay_ms(500);
                led_pin.set_low().unwrap();
                timer.delay_ms(500);
    
                //LED green
                let mut led_pin = pins.gpio7.into_push_pull_output();

                'inner2: loop {
                    led_pin.set_high().unwrap();
                    timer.delay_ms(500);
                    led_pin.set_low().unwrap();
                    timer.delay_ms(500);

                    
                    'inner3: loop {
                        led_pin.set_high().unwrap();
                // This would break only the inner loop
                break;

                // This breaks the outer loop
                //break 'outer;
            }
          }
        }
      }
    }


// End of file


Error:

  --> /home/developer/.cargo/registry/src/index.crates.io-6f17d22bba15001f/i2c-0.1.0/src/smbus.rs:82:17
   |
82 |                 unimplemented!()
   |                 ^^^^^^^^^^^^^
   |
help: consider importing this macro
   |
1  + use bitflags::_core::unimplemented;
   |

error: cannot find macro `unimplemented` in this scope
  --> /home/developer/.cargo/registry/src/index.crates.io-6f17d22bba15001f/i2c-0.1.0/src/smbus.rs:97:17
   |
97 |                 unimplemented!()
   |                 ^^^^^^^^^^^^^
   |
help: consider importing this macro
   |
1  + use bitflags::_core::unimplemented;
   |

error: cannot find macro `format` in this scope
  --> /home/developer/.cargo/registry/src/index.crates.io-6f17d22bba15001f/i2c-0.1.0/src/lib.rs:55:64
   |
55 |                 Err(io::Error::new(io::ErrorKind::Interrupted, format!("I2C write was truncated to {} bytes", len)))
   |                                                                ^^^^^^

error: cannot find derive macro `Copy` in this scope
   --> /home/developer/.cargo/registry/src/index.crates.io-6f17d22bba15001f/i2c-0.1.0/src/lib.rs:187:1

ds2k5 avatar Dec 24 '23 09:12 ds2k5

i don't know what the i2c crate is, but you don't need to import that to use i2c on a pico. what is in your Cargo.toml?

9names avatar Dec 24 '23 09:12 9names

@9names thanks for your time - here is the snip of Cargo.toml

run with:

cargo check --release --example blinky3 --features="critical-section-impl"


[dependencies]
cortex-m = "0.7.2"
embedded-hal = { version = "0.2.5", features = ["unproven"] }
eh1_0_alpha = { package = "embedded-hal", version = "=1.0.0-rc.3",  optional = true }
eh_nb_1_0_alpha = { package = "embedded-hal-nb", version = "=1.0.0-rc.3",  optional = true }
embedded-dma = "0.2.0"
embedded-io = "0.6.1"
fugit = "0.3.6"
itertools = { version = "0.10.1", default-features = false }
nb = "1.0"
rp2040-pac = { version = "0.5.0", features = ["critical-section"] }
paste = "1.0"
pio = "0.2.0"
rp2040-hal-macros = { version = "0.1.0", path = "../rp2040-hal-macros" }
usb-device = "0.3"
vcell = "0.1"
void = { version = "1.0.2", default-features = false }
rand_core = "0.6.3"
critical-section = { version = "1.0.0" }

chrono = { version = "0.4", default-features = false, optional = true }

defmt = { version = ">=0.2.0, <0.4", optional = true }

rtic-monotonic = { version = "1.0.0", optional = true }

frunk = { version = "0.4.1", default-features = false }

bitfield = { version = "0.14.0" }
sh1106 = "0.5.0"
i2c = "0.1.0"
embedded-graphics = "0.8.1"

ds2k5 avatar Dec 24 '23 10:12 ds2k5

I'll take a look. In the meantime, remove the line

i2c = "0.1.0"

and see if it's still broken

9names avatar Dec 24 '23 10:12 9names

remove the line

I did

and see if it's still broken

yes it is:

   |
12 |         let mut buffer = vec![0u8; value.len() + 1];
   |                          ^^^

error: cannot find macro `unimplemented` in this scope
  --> /home/developer/.cargo/registry/src/index.crates.io-6f17d22bba15001f/i2c-0.1.0/src/smbus.rs:58:17
   |
58 |                 unimplemented!()
   |                 ^^^^^^^^^^^^^
   |

ds2k5 avatar Dec 24 '23 10:12 ds2k5

can i suggest starting with https://github.com/rp-rs/rp2040-project-template/

check to make sure it builds and runs before you change it at all.

then use the following to add the sh1106 and embedded-graphics crates to that template

cargo add embedded-graphics
cargo add sh1106

check again that it builds and runs before moving forward.

once you've done that, slowly add in code to make your graphics work. keep checking that it compiles after you add a line or two of code so you know when you're breaking things. don't add anything else to your dependencies. i don't know where you got hal::delay::FreeRtos and hal::peripherals::Peripherals from but they are not helping here.

9names avatar Dec 24 '23 10:12 9names

@9names

thanks a lot.... will try later this day

cargo generate --git https://github.com/rp-rs/rp2040-project-template/

if add i2c ( cargo add i2c ) then the issue is there....

marry xmas

ds2k5 avatar Dec 24 '23 10:12 ds2k5

The I2C crate is not built to be usable on a no_std target like the rp2040. Also it is a fairly old crate (5yo).

no_std system lack several services available by default on a regular system such as a global allocator (required for Vec, String etc) and threads. Some dedicated crates provide alternatives solutions like heapless for the common containers. Threads are a different story I won't go into here :)

Nowadays, virtually the entire embedded rust ecosystem relies on the embedded-hal (& al.) Crates to provide abstractions for buses typically found on microcontrollers (including I2C).

This is what this hal implements. If your need for the I2C crate comes from your sh1106 driver, I'd recommend having a look on crates.io, there should be no_std compatible alternatives.

edit: ~~https://crates.io/crates/sh1106 seems to be a good candidate using the embedded-hal~~

Never mind, you're already using it. What you need to use is https://docs.rs/rp2040-hal/latest/rp2040_hal/i2c/struct.I2C.html instead of the I2C driver you instantiate.

https://github.com/rp-rs/rp-hal/blob/9c4eea7eba2eedb48c306428b165cec51311166b/rp2040-hal/examples/i2c.rs#L80-L95 are what you need instead (+/- the I2c instance as the associated pins).

ithinuel avatar Dec 24 '23 11:12 ithinuel

@ithinuel

thanks for the information

ds2k5 avatar Dec 24 '23 15:12 ds2k5

@ithinuel

what did I wrong ?

grafik

Error:


error[E0277]: the trait bound `Rate<u32, 1, 1>: From<u32>` is not satisfied
   --> src/main.rs:101:9
    |
97  |     let mut i2c = hal::I2C::i2c1(
    |                   -------------- required by a bound introduced by this call
...
101 |         fq,
    |         ^^ the trait `From<u32>` is not implemented for `Rate<u32, 1, 1>`
    |
    = help: the following other types implement trait `From<T>`:
              <Rate<u32, 1, 1> as From<&GpioOutput0Clock>>
              <Rate<u32, 1, 1> as From<&GpioOutput1Clock>>
              <Rate<u32, 1, 1> as From<&GpioOutput2Clock>>
              <Rate<u32, 1, 1> as From<&GpioOutput3Clock>>
              <Rate<u32, 1, 1> as From<&ReferenceClock>>
              <Rate<u32, 1, 1> as From<&SystemClock>>
              <Rate<u32, 1, 1> as From<&PeripheralClock>>
              <Rate<u32, 1, 1> as From<&UsbClock>>
            and 2 others
    = note: required for `u32` to implement `Into<Rate<u32, 1, 1>>`
note: required by a bound in `rp2040_hal::I2C::<I2C1, (Sda, Scl)>::i2c1`
   --> /home/developer/.cargo/registry/src/index.crates.io-6f17d22bba15001f/rp2040-hal-0.9.1/src/i2c.rs:344:1
    |
344 | / hal! {
345 | |     I2C0: (i2c0),
346 | |     I2C1: (i2c1),
    | |            ---- required by a bound in this associated function
347 | | }
    | |_^ required by this bound in `I2C::<I2C1, (Sda, Scl)>::i2c1`
    = note: this error originates in the macro `hal` (in Nightly builds, run with -Z macro-backtrace for more info)


the Code:

//! # GPIO 'Blinky' Example
//!
//! This application demonstrates how to control a GPIO pin on the RP2040.
//!
//! It may need to be adapted to your particular board layout and/or pin assignment.
//!
//! See the `Cargo.toml` file for Copyright and license details.

#![no_std]
#![no_main]
#![allow(unreachable_code, unused_labels)]

use hal::Clock;
use pac::i2c0;
// Ensure we halt the program on panic (if we don't mention this crate it won't
// be linked)
use panic_halt as _;

// Alias for our HAL crate
use rp2040_hal as hal;

use embedded_hal::blocking::i2c::Write;

// A shorter alias for the Peripheral Access Crate, which provides low-level
// register access
use hal::gpio::FunctionI2C;
use hal::gpio::Pin;
use hal::gpio::PullUp;
use hal::i2c::*;
use hal::pac;

// Some traits we need
use embedded_hal::blocking::delay::DelayMs;
use embedded_hal::digital::v2::OutputPin;

/// The linker will place this boot block at the start of our program image. We
/// need this to help the ROM bootloader get our code up and running.
/// Note: This boot block is not necessary when using a rp-hal based BSP
/// as the BSPs already perform this step.
#[link_section = ".boot2"]
#[used]
pub static BOOT2: [u8; 256] = rp2040_boot2::BOOT_LOADER_GENERIC_03H;

/// External high-speed crystal on the Raspberry Pi Pico board is 12 MHz. Adjust
/// if your board has a different frequency
const XTAL_FREQ_HZ: u32 = 12_000_000u32;

/// Entry point to our bare-metal application.
///
/// The `#[rp2040_hal::entry]` macro ensures the Cortex-M start-up code calls this function
/// as soon as all global variables and the spinlock are initialised.
///
/// The function configures the RP2040 peripherals, then toggles a GPIO pin in
/// an infinite loop. If there is an LED connected to that pin, it will blink.
#[rp2040_hal::entry]
fn main() -> ! {
    // Grab our singleton objects
    let mut pac = pac::Peripherals::take().unwrap();

    // Set up the watchdog driver - needed by the clock setup code
    let mut watchdog = hal::Watchdog::new(pac.WATCHDOG);

    // Configure the clocks
    let clocks = hal::clocks::init_clocks_and_plls(
        XTAL_FREQ_HZ,
        pac.XOSC,
        pac.CLOCKS,
        pac.PLL_SYS,
        pac.PLL_USB,
        &mut pac.RESETS,
        &mut watchdog,
    )
    .ok()
    .unwrap();

    let mut timer = rp2040_hal::Timer::new(pac.TIMER, &mut pac.RESETS, &clocks);

    // The single-cycle I/O block controls our GPIO pins
    let sio = hal::Sio::new(pac.SIO);

    // Set the pins to their default state
    let pins = hal::gpio::Pins::new(
        pac.IO_BANK0,
        pac.PADS_BANK0,
        sio.gpio_bank0,
        &mut pac.RESETS,
    );

    // Configure two pins as being I²C, not GPIO
    let sda_pin: Pin<_, FunctionI2C, PullUp> = pins.gpio18.reconfigure();
    let scl_pin: Pin<_, FunctionI2C, PullUp> = pins.gpio19.reconfigure();
    let fq: u32 = 100;

    // Create the I²C drive, using the two pre-configured pins. This will fail
    // at compile time if the pins are in the wrong mode, or if this I²C
    // peripheral isn't available on these pins!
    let mut i2c = hal::I2C::i2c1(
        pac.I2C1,
        sda_pin,
        scl_pin, // Try `not_an_scl_pin` here
        fq,
        &mut pac.RESETS,
        &clocks.system_clock,
    );

    // Configure GPIO5 as an output - LED red
    let mut led_pin = pins.gpio5.into_push_pull_output();

    'outer: loop {
        led_pin.set_high().unwrap();
        timer.delay_ms(500);
        led_pin.set_low().unwrap();
        timer.delay_ms(500);

        //LED yellow
        let mut led_pin = pins.gpio6.into_push_pull_output();

        'inner: loop {
            led_pin.set_high().unwrap();
            timer.delay_ms(500);
            led_pin.set_low().unwrap();
            timer.delay_ms(500);

            //LED green
            let mut led_pin = pins.gpio7.into_push_pull_output();

            'inner2: loop {
                led_pin.set_high().unwrap();
                timer.delay_ms(500);
                led_pin.set_low().unwrap();
                timer.delay_ms(500);

                'inner3: loop {
                    led_pin.set_high().unwrap();
                    // This would break only the inner loop
                    break;

                    // This breaks the outer loop
                    //break 'outer;
                }
            }
        }
    }
}

// End of file

ds2k5 avatar Dec 24 '23 17:12 ds2k5

If you look more closely to the example, you'll see it uses 400.kHz().

This .kHz() method comes from the trait RateExtU32 imported line 21.

But you define fq as a plain u32.

ithinuel avatar Dec 24 '23 18:12 ithinuel

@ithinuel the Version with .kHz() did not work for me

grafik

ds2k5 avatar Dec 24 '23 19:12 ds2k5

Did you import the trait as it is done in the example ?

If not, I invite you to read the rust book to get more familiar with the language's mechanics :)

ithinuel avatar Dec 24 '23 19:12 ithinuel

sorry my english is not so good

could you explain another way or show me the line of import you mean please ?

ds2k5 avatar Dec 24 '23 19:12 ds2k5

No worries, I am not native English speaker either :)

The line is use hal::fugit::RateExtU32;.

It is on line 21 in the example

ithinuel avatar Dec 24 '23 20:12 ithinuel

no was not in the code I use add it but...

grafik

relpaced it ( quick an dirty ) with use hal::*;

now it compile

thank you!

ds2k5 avatar Dec 24 '23 20:12 ds2k5

Merry end of year celebrations :) 🎊

ithinuel avatar Dec 24 '23 21:12 ithinuel

thanks to you too!

ds2k5 avatar Dec 24 '23 21:12 ds2k5

Looks like this was resolved? I'm going to close this issue, if it is not resolved please re-open this issue @ds2k5

9names avatar Apr 28 '24 03:04 9names