rp-rs / rp-hal

A Rust Embedded-HAL for the rp series microcontrollers
https://crates.io/crates/rp2040-hal
Apache License 2.0
1.45k stars 234 forks source link

PRI PICO with SH1106 128x64 pixel Display - i2c issue #742

Closed ds2k5 closed 6 months ago

ds2k5 commented 10 months ago

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
9names commented 10 months ago

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?

ds2k5 commented 10 months ago

@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"
9names commented 10 months ago

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

i2c = "0.1.0"

and see if it's still broken

ds2k5 commented 10 months ago

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!()
   |                 ^^^^^^^^^^^^^
   |
9names commented 10 months ago

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.

ds2k5 commented 10 months ago

@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

ithinuel commented 10 months ago

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

ds2k5 commented 10 months ago

@ithinuel

thanks for the information

ds2k5 commented 10 months ago

@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
ithinuel commented 10 months ago

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.

ds2k5 commented 10 months ago

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

grafik

ithinuel commented 10 months ago

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

ds2k5 commented 10 months ago

sorry my english is not so good

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

ithinuel commented 10 months ago

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

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

It is on line 21 in the example

ds2k5 commented 10 months ago

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!

ithinuel commented 10 months ago

Merry end of year celebrations :) 🎊

ds2k5 commented 10 months ago

thanks to you too!

9names commented 6 months ago

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