From 020ca5cd4bc8c905c0b16e361f4f04801c44758f Mon Sep 17 00:00:00 2001 From: lukas Date: Sun, 2 Aug 2026 17:20:40 +0200 Subject: [PATCH 1/3] Initial implementation of OLED driver with shared SPI bus --- Cargo.lock | 91 ++++++++++++++++++++++++++++++++ Cargo.toml | 1 + src/bin/main.rs | 40 ++++++++++---- src/display.rs | 2 + src/display/primary_lcd.rs | 62 ++++++++++------------ src/display/secondary_oled.rs | 55 +++++++++++++++++++- src/display/shared_bus.rs | 98 +++++++++++++++++++++++++++++++++++ 7 files changed, 301 insertions(+), 48 deletions(-) create mode 100644 src/display/shared_bus.rs diff --git a/Cargo.lock b/Cargo.lock index 19bc895..a49367c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -322,6 +322,7 @@ dependencies = [ "log", "ndef", "pn532", + "ssd1306", ] [[package]] @@ -606,6 +607,17 @@ version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7ba2aab1ef3793e6f7804162debb5ac5edb93b3d650fbcc5aeb72fcd0e6c03a0" +[[package]] +name = "display-interface-i2c" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0d964fa85bbbb5a6ecd06e58699407ac5dc3e3ad72dac0ab7e6b0d00a1cd262d" +dependencies = [ + "display-interface", + "embedded-hal 1.0.0", + "embedded-hal-async", +] + [[package]] name = "display-interface-spi" version = "0.5.0" @@ -1916,6 +1928,30 @@ version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0ca88d725a0a943b096803bd34e73a4437208b6077654cc4ecb2947a5f91618d" +[[package]] +name = "manyhow" +version = "0.11.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b33efb3ca6d3b07393750d4030418d594ab1139cee518f0dc88db70fec873587" +dependencies = [ + "manyhow-macros", + "proc-macro2", + "quote", + "syn 1.0.109", + "syn 2.0.119", +] + +[[package]] +name = "manyhow-macros" +version = "0.11.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "46fce34d199b78b6e6073abf984c9cf5fd3e9330145a93ee0738a7443e371495" +dependencies = [ + "proc-macro-utils", + "proc-macro2", + "quote", +] + [[package]] name = "matchers" version = "0.2.0" @@ -1931,6 +1967,19 @@ version = "0.1.10" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2532096657941c2fea9c289d370a250971c689d4f143798ff67113ec042024a5" +[[package]] +name = "maybe-async-cfg" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8dbfaa67a76e2623580df07d6bb5e7956c0a4bae4b418314083a9c619bd66627" +dependencies = [ + "manyhow", + "proc-macro2", + "pulldown-cmark", + "quote", + "syn 1.0.109", +] + [[package]] name = "memchr" version = "2.8.3" @@ -2690,6 +2739,17 @@ dependencies = [ "toml_edit", ] +[[package]] +name = "proc-macro-utils" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eeaf08a13de400bc215877b5bdc088f241b12eb42f0a548d3390dc1c56bb7071" +dependencies = [ + "proc-macro2", + "quote", + "smallvec", +] + [[package]] name = "proc-macro2" version = "1.0.107" @@ -2719,6 +2779,17 @@ dependencies = [ "syn 1.0.109", ] +[[package]] +name = "pulldown-cmark" +version = "0.11.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "679341d22c78c6c649893cbd6c3278dcbe9fc4faa62fea3a9296ae2b50c14625" +dependencies = [ + "bitflags 2.13.1", + "memchr", + "unicase", +] + [[package]] name = "quote" version = "1.0.47" @@ -3133,6 +3204,20 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a0f368519fc6c85fc1afdb769fb5a51123f6158013e143656e25a3485a0d401c" +[[package]] +name = "ssd1306" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ed51935bdc6e9e976d3d9a747e768589eaea0cf26f483213bfededc947c8624" +dependencies = [ + "display-interface", + "display-interface-i2c", + "display-interface-spi", + "embedded-graphics-core", + "embedded-hal 1.0.0", + "maybe-async-cfg", +] + [[package]] name = "stable_deref_trait" version = "1.2.1" @@ -3400,6 +3485,12 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e87a2ed6b42ec5e28cc3b94c09982969e9227600b2e3dcbc1db927a84c06bd69" +[[package]] +name = "unicase" +version = "2.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dbc4bc3a9f746d862c45cb89d705aa10f187bb96c76001afab07a0d35ce60142" + [[package]] name = "unicode-id-start" version = "1.4.0" diff --git a/Cargo.toml b/Cargo.toml index eeaf461..b731e3e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -34,6 +34,7 @@ esp-hal-wifimanager = { git = "https://github.com/InvisibleUSA/esp-hal-wifimanag esp-println = { version = "0.17.0", features = ["esp32s3", "log-04"] } display-interface-spi = "0.5.0" ili9341 = "0.6.0" +ssd1306 = "0.9.0" embedded-hal = "1.0.0" embedded-hal-bus = "0.3.0" embedded-graphics = "0.8.2" diff --git a/src/bin/main.rs b/src/bin/main.rs index e09f649..79bcf8e 100644 --- a/src/bin/main.rs +++ b/src/bin/main.rs @@ -8,7 +8,9 @@ #![deny(clippy::large_stack_frames)] use creaturedex::card::mock::{MOCK_PAYLOAD, MOCK_PAYLOAD2}; -use creaturedex::display::primary_lcd::init_primary_lcd; +use creaturedex::display::shared_bus::init_dual_displays; + + use creaturedex::display::views::card_view::render_nfc_payload; use creaturedex::drivers::nfc_pn532::NfcPn532Driver; use creaturedex::navigation::input::init_navigation_button; @@ -16,8 +18,13 @@ use creaturedex::navigation::state::NavigationState; use creaturedex::network::wifi::setup_wifi; use embassy_executor::Spawner; use embassy_time::Timer; +use embedded_graphics::{ + mono_font::{ascii::FONT_6X10, MonoTextStyle}, + prelude::*, + text::Text, + }; use embedded_graphics::draw_target::DrawTarget; -use embedded_graphics::pixelcolor::Rgb565; +use embedded_graphics::pixelcolor::{BinaryColor, Rgb565}; use embedded_graphics::prelude::RgbColor; use esp_hal::time::Duration; use esp_hal::clock::CpuClock; @@ -68,23 +75,32 @@ async fn main(spawner: Spawner) { esp_hal::interrupt::software::SoftwareInterruptControl::new(peripherals.SW_INTERRUPT); esp_rtos::start(timg0.timer0, sw_interrupt.software_interrupt0); - let mut display = init_primary_lcd( + let (mut display, mut oled) = init_dual_displays( peripherals.SPI2, - peripherals.GPIO36, - peripherals.GPIO35, - peripherals.GPIO37, - peripherals.GPIO11, - peripherals.GPIO10, - peripherals.GPIO12, + peripherals.GPIO36, // sck + peripherals.GPIO35, // mosi + peripherals.GPIO37, // miso + peripherals.GPIO11, // primary cs + peripherals.GPIO5, // oled cs + peripherals.GPIO10, // shared reset + peripherals.GPIO12, // shared dc &mut delay, ); setup_wifi(peripherals.WIFI, peripherals.FLASH, &spawner).await; - log::info!("Setup complete, entering main loop"); - let mut nav_state = NavigationState::new(); + oled.clear(BinaryColor::Off).unwrap(); + + let text_style = MonoTextStyle::new(&FONT_6X10, BinaryColor::On); + Text::new("Hello OLED", Point::new(10, 20), text_style) + .draw(&mut oled) + .unwrap(); + + oled.flush().unwrap(); + + let mut nfc_driver = NfcPn532Driver::new(peripherals.I2C0, peripherals.GPIO17, peripherals.GPIO18); let timeout = Duration::from_millis(1000); if nfc_driver.configure_sam(timeout).is_ok() { @@ -93,6 +109,8 @@ async fn main(spawner: Spawner) { log::error!("NFC SAM configuration failed"); } + + log::info!("Setup complete, entering main loop"); loop { if let Ok(card_data) = nfc_driver.read_card_data() { log::info!("NFC Card Read: {:?}", card_data); diff --git a/src/display.rs b/src/display.rs index 8e7dab1..d1d34f0 100644 --- a/src/display.rs +++ b/src/display.rs @@ -1,6 +1,8 @@ pub mod palette; pub mod primary_lcd; pub mod secondary_oled; +pub mod shared_bus; pub mod sprite; pub mod tertiary_lcd; pub mod views; + diff --git a/src/display/primary_lcd.rs b/src/display/primary_lcd.rs index efebe50..73df719 100644 --- a/src/display/primary_lcd.rs +++ b/src/display/primary_lcd.rs @@ -1,54 +1,42 @@ +use core::cell::RefCell; use display_interface_spi::SPIInterface; use embedded_graphics::draw_target::DrawTarget; use embedded_graphics::pixelcolor::Rgb565; use embedded_graphics::prelude::RgbColor; -use embedded_hal_bus::spi::ExclusiveDevice; +use embedded_hal::digital::OutputPin as EhOutputPin; +use embedded_hal_bus::spi::RefCellDevice; use esp_hal::delay::Delay; -use esp_hal::gpio::{Level, Output, OutputConfig}; -use esp_hal::peripherals::{GPIO10, GPIO11, GPIO12, GPIO35, GPIO36, GPIO37, SPI2}; -use esp_hal::spi::{master::{Config, Spi}, Mode}; -use esp_hal::time::Rate; +use esp_hal::gpio::{Level, Output, OutputConfig, OutputPin}; use ili9341::{DisplaySize240x320, Ili9341, Orientation}; -pub type PrimaryLcdDisplay<'a> = Ili9341< +pub type PrimaryLcdDisplay<'a, BUS, DC, RES> = Ili9341< SPIInterface< - ExclusiveDevice, Output<'a>, Delay>, - Output<'a>, + RefCellDevice<'a, BUS, Output<'a>, Delay>, + DC, >, - Output<'a>, + RES, >; -pub fn init_primary_lcd( - spi2: SPI2<'static>, - sck: GPIO36<'static>, - mosi: GPIO35<'static>, - miso: GPIO37<'static>, - cs: GPIO11<'static>, - reset: GPIO10<'static>, - dc: GPIO12<'static>, +pub fn init_primary_lcd_on_bus<'a, BUS, CS, RES, DC>( + spi_bus: &'a RefCell, + cs: CS, + reset: RES, + dc: DC, delay: &mut Delay, -) -> PrimaryLcdDisplay<'static> { - let freq = Rate::from_khz(60_000); - log::info!("Display freq: {freq}"); - - let spi_bus = Spi::new( - spi2, - Config::default().with_frequency(freq).with_mode(Mode::_0), - ) - .unwrap() - .with_sck(sck) - .with_mosi(mosi) - .with_miso(miso); - - let cs_main = Output::new(cs, Level::High, OutputConfig::default()); - let spi_dev = ExclusiveDevice::new(spi_bus, cs_main, *delay).unwrap(); - let reset_main = Output::new(reset, Level::High, OutputConfig::default()); - let spi_dc = Output::new(dc, Level::High, OutputConfig::default()); - let iface = SPIInterface::new(spi_dev, spi_dc); +) -> PrimaryLcdDisplay<'a, BUS, DC, RES> +where + BUS: embedded_hal::spi::SpiBus, + CS: OutputPin + 'static, + RES: EhOutputPin, + DC: EhOutputPin, +{ + let cs_pin = Output::new(cs, Level::High, OutputConfig::default()); + let spi_dev = RefCellDevice::new(spi_bus, cs_pin, *delay).unwrap(); + let iface = SPIInterface::new(spi_dev, dc); let mut display = Ili9341::new( iface, - reset_main, + reset, delay, Orientation::Portrait, DisplaySize240x320, @@ -58,3 +46,5 @@ pub fn init_primary_lcd( display.clear(Rgb565::BLACK).unwrap(); display } + + diff --git a/src/display/secondary_oled.rs b/src/display/secondary_oled.rs index 668cfb6..d7ff951 100644 --- a/src/display/secondary_oled.rs +++ b/src/display/secondary_oled.rs @@ -1 +1,54 @@ -// Secondary OLED module +use core::cell::RefCell; +use display_interface_spi::SPIInterface; +use embedded_graphics::pixelcolor::BinaryColor; +use embedded_graphics::prelude::*; +use embedded_hal::digital::OutputPin as EhOutputPin; +use embedded_hal_bus::spi::RefCellDevice; +use esp_hal::delay::Delay; +use esp_hal::gpio::{Level, Output, OutputConfig, OutputPin}; +use ssd1306::mode::BufferedGraphicsMode; +use ssd1306::prelude::*; +use ssd1306::Ssd1306; + +pub type SecondaryOledDisplay<'a, BUS, DC> = Ssd1306< + SPIInterface< + RefCellDevice<'a, BUS, Output<'a>, Delay>, + DC, + >, + DisplaySize128x64, + BufferedGraphicsMode, +>; + +pub fn init_secondary_oled_on_bus<'a, BUS, CS, RES, DC>( + spi_bus: &'a RefCell, + cs: CS, + mut res: RES, + dc: DC, + delay: &mut Delay, +) -> SecondaryOledDisplay<'a, BUS, DC> +where + BUS: embedded_hal::spi::SpiBus, + CS: OutputPin + 'static, + RES: EhOutputPin, + DC: EhOutputPin, +{ + let cs_pin = Output::new(cs, Level::High, OutputConfig::default()); + let spi_dev = RefCellDevice::new(spi_bus, cs_pin, *delay).unwrap(); + let interface = SPIInterface::new(spi_dev, dc); + + let mut display = Ssd1306::new( + interface, + DisplaySize128x64, + DisplayRotation::Rotate0, + ) + .into_buffered_graphics_mode(); + + display.reset(&mut res, delay).unwrap(); + display.init().unwrap(); + display.clear(BinaryColor::Off).unwrap(); + display.flush().unwrap(); + + display +} + + diff --git a/src/display/shared_bus.rs b/src/display/shared_bus.rs new file mode 100644 index 0000000..68d5145 --- /dev/null +++ b/src/display/shared_bus.rs @@ -0,0 +1,98 @@ +use alloc::boxed::Box; +use core::cell::RefCell; +use embedded_hal::digital::{ErrorType, OutputPin as EhOutputPin, PinState}; +use esp_hal::delay::Delay; +use esp_hal::gpio::interconnect::{PeripheralInput, PeripheralOutput}; +use esp_hal::gpio::{Level, Output, OutputConfig, OutputPin}; +use esp_hal::spi::master::{Config as SpiConfig, Spi}; +use esp_hal::spi::Mode; +use esp_hal::time::Rate; +use crate::display::primary_lcd::{init_primary_lcd_on_bus, PrimaryLcdDisplay}; + +use crate::display::secondary_oled::{init_secondary_oled_on_bus, SecondaryOledDisplay}; + +pub struct SharedPin<'a>(pub &'a RefCell>); + +impl<'a> ErrorType for SharedPin<'a> { + type Error = core::convert::Infallible; +} + +impl<'a> EhOutputPin for SharedPin<'a> { + fn set_low(&mut self) -> Result<(), Self::Error> { + self.0.borrow_mut().set_low(); + Ok(()) + } + + fn set_high(&mut self) -> Result<(), Self::Error> { + self.0.borrow_mut().set_high(); + Ok(()) + } + + fn set_state(&mut self, state: PinState) -> Result<(), Self::Error> { + self.0.borrow_mut().set_state(state); + Ok(()) + } +} + +pub type DisplaySpiBus = Spi<'static, esp_hal::Blocking>; +pub type DualPrimaryDisplay<'a> = PrimaryLcdDisplay<'a, DisplaySpiBus, SharedPin<'a>, SharedPin<'a>>; +pub type DualSecondaryDisplay<'a> = SecondaryOledDisplay<'a, DisplaySpiBus, SharedPin<'a>>; + +pub fn init_dual_displays( + spi_peripheral: SPI, + sck: SCK, + mosi: MOSI, + miso: MISO, + cs_primary: CS1, + cs_secondary: CS2, + reset_pin: RES, + dc_pin: DC, + delay: &mut Delay, +) -> (DualPrimaryDisplay<'static>, DualSecondaryDisplay<'static>) + +where + SPI: esp_hal::spi::master::Instance + 'static, + SCK: PeripheralOutput<'static>, + MOSI: PeripheralOutput<'static>, + MISO: PeripheralInput<'static>, + CS1: OutputPin + 'static, + CS2: OutputPin + 'static, + RES: OutputPin + 'static, + DC: OutputPin + 'static, +{ + let spi = Spi::new( + spi_peripheral, + SpiConfig::default().with_frequency(Rate::from_khz(20_000)).with_mode(Mode::_0), + ) + .unwrap() + .with_sck(sck) + .with_mosi(mosi) + .with_miso(miso); + + let bus_static: &'static RefCell = Box::leak(Box::new(RefCell::new(spi))); + let reset_static: &'static RefCell> = Box::leak(Box::new(RefCell::new( + Output::new(reset_pin, Level::High, OutputConfig::default()), + ))); + let dc_static: &'static RefCell> = Box::leak(Box::new(RefCell::new( + Output::new(dc_pin, Level::High, OutputConfig::default()), + ))); + + let primary = init_primary_lcd_on_bus( + bus_static, + cs_primary, + SharedPin(reset_static), + SharedPin(dc_static), + delay, + ); + + let secondary = init_secondary_oled_on_bus( + bus_static, + cs_secondary, + SharedPin(reset_static), + SharedPin(dc_static), + delay, + ); + + (primary, secondary) +} + -- 2.39.5 From e4774627838c1b3febf61c0073751180e1998f48 Mon Sep 17 00:00:00 2001 From: ede1998 Date: Wed, 5 Aug 2026 18:28:28 +0200 Subject: [PATCH 2/3] try different crates --- Cargo.lock | 33 +++++++++ Cargo.toml | 8 ++- src/bin/experiment_ch1115.rs | 106 +++++++++++++++++++++++++++++ src/bin/experiment_sh1106.rs | 121 +++++++++++++++++++++++++++++++++ src/bin/experiment_ssd1306.rs | 122 ++++++++++++++++++++++++++++++++++ src/bin/main.rs | 39 ++++------- 6 files changed, 401 insertions(+), 28 deletions(-) create mode 100644 src/bin/experiment_ch1115.rs create mode 100644 src/bin/experiment_sh1106.rs create mode 100644 src/bin/experiment_ssd1306.rs diff --git a/Cargo.lock b/Cargo.lock index a49367c..ed56a59 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -229,6 +229,15 @@ version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801" +[[package]] +name = "ch1115" +version = "0.1.2" +dependencies = [ + "display-interface", + "embedded-graphics-core", + "embedded-hal 1.0.0", +] + [[package]] name = "cobs" version = "0.3.0" @@ -303,6 +312,7 @@ name = "creaturedex" version = "0.1.0" dependencies = [ "base64 0.22.1", + "ch1115", "critical-section", "display-interface-spi", "embassy-executor", @@ -311,6 +321,7 @@ dependencies = [ "embedded-graphics-core", "embedded-hal 1.0.0", "embedded-hal-bus", + "embedded-hal-compat", "embedded-io 0.7.1", "esp-alloc", "esp-bootloader-esp-idf", @@ -322,6 +333,7 @@ dependencies = [ "log", "ndef", "pn532", + "sh1106", "ssd1306", ] @@ -957,6 +969,17 @@ dependencies = [ "embedded-hal 1.0.0", ] +[[package]] +name = "embedded-hal-compat" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08ecac72f6be75ad2d4809c9f07d45502e2e5c9a4e9579077f28d7135de81df3" +dependencies = [ + "embedded-hal 0.2.7", + "embedded-hal 1.0.0", + "nb 1.1.0", +] + [[package]] name = "embedded-io" version = "0.4.0" @@ -3116,6 +3139,16 @@ dependencies = [ "unsafe-libyaml", ] +[[package]] +name = "sh1106" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc12a0563ec9b3b1f99b1f64613581ebbade1347416a34766564f406c15d1bf3" +dependencies = [ + "embedded-graphics-core", + "embedded-hal 0.2.7", +] + [[package]] name = "sharded-slab" version = "0.1.7" diff --git a/Cargo.toml b/Cargo.toml index b731e3e..2725dce 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -42,8 +42,14 @@ embedded-graphics-core = "0.4.1" base64 = { version = "0.22", default-features = false, features = ["alloc"] } pn532 = "0.5.0" ndef = "0.5.0" +ch1115 = { version = "0.1.2", features = ["graphics"] } +sh1106 = "0.5.0" +embedded-hal-compat = "0.13.0" +[patch.crates-io] +ch1115 = { path = "./ch1115" } + # For fine tuning these settings, please refer to https://doc.rust-lang.org/cargo/reference/profiles.html @@ -60,4 +66,4 @@ opt-level = 's' [features] default = [] -wokwi = [] \ No newline at end of file +wokwi = [] diff --git a/src/bin/experiment_ch1115.rs b/src/bin/experiment_ch1115.rs new file mode 100644 index 0000000..f3ed3cc --- /dev/null +++ b/src/bin/experiment_ch1115.rs @@ -0,0 +1,106 @@ +#![no_std] +#![no_main] +#![deny( + clippy::mem_forget, + reason = "mem::forget is generally not safe to do with esp_hal types, especially those \ + holding buffers for the duration of a data transfer." +)] + +use ch1115::Ch1115; +use core::cell::RefCell; +use creaturedex::display::shared_bus::SharedPin; +use embassy_executor::Spawner; +use embassy_time::Timer; +use embedded_graphics::pixelcolor::BinaryColor; +use embedded_graphics::{ + mono_font::{MonoTextStyle, ascii::FONT_6X10}, + prelude::*, + text::Text, +}; +use esp_hal::clock::CpuClock; +use esp_hal::delay::Delay; +use esp_hal::gpio::{Level, Output, OutputConfig}; +use esp_hal::spi::Mode; +use esp_hal::spi::master::{Config as SpiConfig, Spi}; +use esp_hal::time::Rate; +use esp_hal::timer::timg::TimerGroup; +use log::error; + +#[panic_handler] +fn panic(panic_info: &core::panic::PanicInfo) -> ! { + error!("{}", panic_info); + loop {} +} + +extern crate alloc; + +esp_bootloader_esp_idf::esp_app_desc!(); + +#[esp_rtos::main] +async fn main(_spawner: Spawner) { + esp_println::logger::init_logger_from_env(); + + let config = esp_hal::Config::default().with_cpu_clock(CpuClock::max()); + let peripherals = esp_hal::init(config); + + // The following GPIO pins are in use by some feature of the module and should not be used. + let _ = peripherals.GPIO27; + let _ = peripherals.GPIO28; + let _ = peripherals.GPIO29; + let _ = peripherals.GPIO30; + let _ = peripherals.GPIO31; + let _ = peripherals.GPIO32; + let _ = peripherals.GPIO33; + let _ = peripherals.GPIO34; + + esp_alloc::heap_allocator!(#[esp_hal::ram(reclaimed)] size: 73744); + + let mut delay = Delay::new(); + + let timg0 = TimerGroup::new(peripherals.TIMG0); + let sw_interrupt = + esp_hal::interrupt::software::SoftwareInterruptControl::new(peripherals.SW_INTERRUPT); + esp_rtos::start(timg0.timer0, sw_interrupt.software_interrupt0); + + let spi = peripherals.SPI2; + let sck = peripherals.GPIO36; + let mosi = peripherals.GPIO35; + let cs = peripherals.GPIO13; + let reset = peripherals.GPIO10; + let dc = peripherals.GPIO12; + let miso = peripherals.GPIO37; + + let cs = Output::new(cs, Level::High, OutputConfig::default()); + let dc = Output::new(dc, Level::Low, OutputConfig::default()); + let reset = Output::new(reset, Level::High, OutputConfig::default()); + + let spi = Spi::new( + spi, + SpiConfig::default() + .with_frequency(Rate::from_khz(5000)) + .with_mode(Mode::_0), + ) + .unwrap() + .with_sck(sck) + .with_mosi(mosi) + .with_miso(miso); + + let spi = RefCell::new(spi); + let spi_dev = embedded_hal_bus::spi::RefCellDevice::new(&spi, cs, delay).unwrap(); + let interface = display_interface_spi::SPIInterface::new(spi_dev, dc); + + let mut display = Ch1115::new(interface, reset, ch1115::Size128x64); + display.init(&mut delay).unwrap(); + + + loop { + log::info!("Init finished"); + // let text_style = MonoTextStyle::new(&FONT_6X10, BinaryColor::On); + // Text::new("Hello OLED", Point::new(10, 20), text_style) + // .draw(&mut display) + // .unwrap(); + + // display.flush().unwrap(); + Timer::after_millis(1000).await; + } +} diff --git a/src/bin/experiment_sh1106.rs b/src/bin/experiment_sh1106.rs new file mode 100644 index 0000000..68744a8 --- /dev/null +++ b/src/bin/experiment_sh1106.rs @@ -0,0 +1,121 @@ +#![no_std] +#![no_main] +#![deny( + clippy::mem_forget, + reason = "mem::forget is generally not safe to do with esp_hal types, especially those \ + holding buffers for the duration of a data transfer." +)] + +use embassy_executor::Spawner; +use embassy_time::Timer; +use embedded_graphics::pixelcolor::BinaryColor; +use embedded_graphics::{ + mono_font::{MonoTextStyle, ascii::FONT_6X10}, + prelude::*, + text::Text, +}; +use embedded_hal_compat::ReverseCompat; +use esp_hal::clock::CpuClock; +use esp_hal::delay::Delay; +use esp_hal::gpio::{Level, Output, OutputConfig}; +use esp_hal::spi::Mode; +use esp_hal::spi::master::{Config as SpiConfig, Spi}; +use esp_hal::time::Rate; +use esp_hal::timer::timg::TimerGroup; +use log::error; + +#[panic_handler] +fn panic(panic_info: &core::panic::PanicInfo) -> ! { + error!("{}", panic_info); + loop {} +} + +extern crate alloc; + +esp_bootloader_esp_idf::esp_app_desc!(); + +// This creates a defaembedded_hal_compatedReverseCompat::reverse(Output::new(cs,Level::High,OutputConfig::default()))vel::High,OutputConfig::default())) more information see: embedded_hal_compat::ForwardCompat::forward(Spi::new(spi,SpiConfig::default().with_frequency(Rate::from_khz(100)).with_mode(Mode::_0),).unwrap().with_sck(sck).with_mosi(mosi))rward(Spi::new(spi,SpiConfig::default().with_frequency(Rate::from_khz(100)).with_mode(Mode::_0),).unwrap().with_sck(sck).with_mosi(mosi)) to allocate larger buffers etc. in main" +#[esp_rtos::main] +async fn main(_spawner: Spawner) { + esp_println::logger::init_logger_from_env(); + + let config = esp_hal::Config::default().with_cpu_clock(CpuClock::max()); + let peripherals = esp_hal::init(config); + + // The following GPIO pins are in use by some feature of the module and should not be used. + let _ = peripherals.GPIO27; + let _ = peripherals.GPIO28; + let _ = peripherals.GPIO29; + let _ = peripherals.GPIO30; + let _ = peripherals.GPIO31; + let _ = peripherals.GPIO32; + let _ = peripherals.GPIO33; + let _ = peripherals.GPIO34; + + esp_alloc::heap_allocator!(#[esp_hal::ram(reclaimed)] size: 73744); + + let delay = Delay::new(); + + let timg0 = TimerGroup::new(peripherals.TIMG0); + let sw_interrupt = + esp_hal::interrupt::software::SoftwareInterruptControl::new(peripherals.SW_INTERRUPT); + esp_rtos::start(timg0.timer0, sw_interrupt.software_interrupt0); + + let spi = peripherals.SPI2; + let sck = peripherals.GPIO36; + let mosi = peripherals.GPIO35; + let cs = peripherals.GPIO13; + let reset = peripherals.GPIO10; + let dc = peripherals.GPIO12; + let miso = peripherals.GPIO37; + + let cs = Output::new(cs, Level::High, OutputConfig::default()).reverse_cell(); + let dc = Output::new(dc, Level::Low, OutputConfig::default()).reverse_cell(); + let mut reset = Output::new(reset, Level::High, OutputConfig::default()); + + let spi = Spi::new( + spi, + SpiConfig::default() + .with_frequency(Rate::from_khz(5000)) + .with_mode(Mode::_0), + ) + .unwrap() + .with_sck(sck) + .with_mosi(mosi) + .with_miso(miso) + .reverse(); + + let mut display: sh1106::mode::GraphicsMode<_> = + sh1106::Builder::new().connect_spi(spi, dc, cs).into(); + + display.init().unwrap(); + + // Hard reset the OLED + reset.set_high(); + delay.delay_millis(1); + reset.set_low(); + delay.delay_millis(10); + reset.set_high(); + + display.flush().unwrap(); + + display.set_pixel(10, 20, 1); + + display.flush().unwrap(); + + let mut oled = display; + + oled.clear(); + + let text_style = MonoTextStyle::new(&FONT_6X10, BinaryColor::On); + Text::new("Hello OLED", Point::new(10, 20), text_style) + .draw(&mut oled) + .unwrap(); + + oled.flush().unwrap(); + + loop { + log::info!("Init finished"); + Timer::after_millis(1000).await; + } +} diff --git a/src/bin/experiment_ssd1306.rs b/src/bin/experiment_ssd1306.rs new file mode 100644 index 0000000..05f5e96 --- /dev/null +++ b/src/bin/experiment_ssd1306.rs @@ -0,0 +1,122 @@ +#![no_std] +#![no_main] +#![deny( + clippy::mem_forget, + reason = "mem::forget is generally not safe to do with esp_hal types, especially those \ + holding buffers for the duration of a data transfer." +)] +#![deny(clippy::large_stack_frames)] + +use embassy_executor::Spawner; +use embassy_time::Timer; +use embedded_graphics::draw_target::DrawTarget; +use embedded_graphics::pixelcolor::BinaryColor; +use embedded_graphics::text::Text; +use embedded_graphics::{ + mono_font::{MonoTextStyle, ascii::FONT_6X10}, + prelude::*, +}; + +use core::cell::RefCell; +use esp_hal::clock::CpuClock; +use esp_hal::delay::Delay; +use esp_hal::gpio::{Level, Output, OutputConfig}; +use esp_hal::spi::Mode; +use esp_hal::spi::master::{Config as SpiConfig, Spi}; +use esp_hal::time::Rate; +use esp_hal::timer::timg::TimerGroup; +use log::error; +use ssd1306::Ssd1306; +use ssd1306::mode::DisplayConfig; +use ssd1306::rotation::DisplayRotation; +use ssd1306::size::DisplaySize128x64; + +#[panic_handler] +fn panic(panic_info: &core::panic::PanicInfo) -> ! { + error!("{}", panic_info); + loop {} +} + +extern crate alloc; + +esp_bootloader_esp_idf::esp_app_desc!(); + +// This creates a defaembedded_hal_compatedReverseCompat::reverse(Output::new(cs,Level::High,OutputConfig::default()))vel::High,OutputConfig::default())) more information see: embedded_hal_compat::ForwardCompat::forward(Spi::new(spi,SpiConfig::default().with_frequency(Rate::from_khz(100)).with_mode(Mode::_0),).unwrap().with_sck(sck).with_mosi(mosi))rward(Spi::new(spi,SpiConfig::default().with_frequency(Rate::from_khz(100)).with_mode(Mode::_0),).unwrap().with_sck(sck).with_mosi(mosi)) to allocate larger buffers etc. in main" +#[esp_rtos::main] +async fn main(_spawner: Spawner) { + esp_println::logger::init_logger_from_env(); + + let config = esp_hal::Config::default().with_cpu_clock(CpuClock::max()); + let peripherals = esp_hal::init(config); + + // The following GPIO pins are in use by some feature of the module and should not be used. + let _ = peripherals.GPIO27; + let _ = peripherals.GPIO28; + let _ = peripherals.GPIO29; + let _ = peripherals.GPIO30; + let _ = peripherals.GPIO31; + let _ = peripherals.GPIO32; + let _ = peripherals.GPIO33; + let _ = peripherals.GPIO34; + + esp_alloc::heap_allocator!(#[esp_hal::ram(reclaimed)] size: 73744); + + let mut delay = Delay::new(); + + let timg0 = TimerGroup::new(peripherals.TIMG0); + let sw_interrupt = + esp_hal::interrupt::software::SoftwareInterruptControl::new(peripherals.SW_INTERRUPT); + esp_rtos::start(timg0.timer0, sw_interrupt.software_interrupt0); + + let spi = peripherals.SPI2; + let sck = peripherals.GPIO36; + let mosi = peripherals.GPIO35; + let cs = peripherals.GPIO13; + let reset = peripherals.GPIO10; + let dc = peripherals.GPIO12; + let miso = peripherals.GPIO37; + + let cs = Output::new(cs, Level::High, OutputConfig::default()); + let dc = Output::new(dc, Level::Low, OutputConfig::default()); + let mut reset = Output::new(reset, Level::High, OutputConfig::default()); + + let spi = Spi::new( + spi, + SpiConfig::default() + .with_frequency(Rate::from_khz(5000)) + .with_mode(Mode::_0), + ) + .unwrap() + .with_sck(sck) + .with_mosi(mosi) + .with_miso(miso); + + let spi = RefCell::new(spi); + let spi_dev = embedded_hal_bus::spi::RefCellDevice::new(&spi, cs, delay).unwrap(); + let interface = display_interface_spi::SPIInterface::new(spi_dev, dc); + let mut display = Ssd1306::new( + interface, + DisplaySize128x64, + DisplayRotation::Rotate0, + ) + .into_buffered_graphics_mode(); + + display.reset(&mut reset, &mut delay).unwrap(); + display.init().unwrap(); + display.clear(BinaryColor::Off).unwrap(); + + + let mut oled = display; + + let text_style = MonoTextStyle::new(&FONT_6X10, BinaryColor::On); + Text::new("Hello OLED", Point::new(10, 20), text_style) + .draw(&mut oled) + .unwrap(); + + oled.flush().unwrap(); + + loop { + log::info!("Init finished"); + Timer::after_millis(1000).await; + } +} diff --git a/src/bin/main.rs b/src/bin/main.rs index 79bcf8e..5f47555 100644 --- a/src/bin/main.rs +++ b/src/bin/main.rs @@ -10,7 +10,6 @@ use creaturedex::card::mock::{MOCK_PAYLOAD, MOCK_PAYLOAD2}; use creaturedex::display::shared_bus::init_dual_displays; - use creaturedex::display::views::card_view::render_nfc_payload; use creaturedex::drivers::nfc_pn532::NfcPn532Driver; use creaturedex::navigation::input::init_navigation_button; @@ -18,17 +17,17 @@ use creaturedex::navigation::state::NavigationState; use creaturedex::network::wifi::setup_wifi; use embassy_executor::Spawner; use embassy_time::Timer; -use embedded_graphics::{ - mono_font::{ascii::FONT_6X10, MonoTextStyle}, - prelude::*, - text::Text, - }; use embedded_graphics::draw_target::DrawTarget; use embedded_graphics::pixelcolor::{BinaryColor, Rgb565}; use embedded_graphics::prelude::RgbColor; -use esp_hal::time::Duration; +use embedded_graphics::{ + mono_font::{MonoTextStyle, ascii::FONT_6X10}, + prelude::*, + text::Text, +}; use esp_hal::clock::CpuClock; use esp_hal::delay::Delay; +use esp_hal::time::Duration; use esp_hal::timer::timg::TimerGroup; use log::error; @@ -81,7 +80,7 @@ async fn main(spawner: Spawner) { peripherals.GPIO35, // mosi peripherals.GPIO37, // miso peripherals.GPIO11, // primary cs - peripherals.GPIO5, // oled cs + peripherals.GPIO13, // oled cs peripherals.GPIO10, // shared reset peripherals.GPIO12, // shared dc &mut delay, @@ -97,11 +96,14 @@ async fn main(spawner: Spawner) { Text::new("Hello OLED", Point::new(10, 20), text_style) .draw(&mut oled) .unwrap(); - oled.flush().unwrap(); + let text_style = MonoTextStyle::new(&FONT_6X10, Rgb565::WHITE); + display.clear_screen(0).unwrap(); + Text::new("Hello OLED", Point::new(10, 20), text_style).draw(&mut display).unwrap(); - let mut nfc_driver = NfcPn532Driver::new(peripherals.I2C0, peripherals.GPIO17, peripherals.GPIO18); + let mut nfc_driver = + NfcPn532Driver::new(peripherals.I2C0, peripherals.GPIO17, peripherals.GPIO18); let timeout = Duration::from_millis(1000); if nfc_driver.configure_sam(timeout).is_ok() { log::info!("NFC SAM configuration successful"); @@ -109,24 +111,7 @@ async fn main(spawner: Spawner) { log::error!("NFC SAM configuration failed"); } - - log::info!("Setup complete, entering main loop"); loop { - if let Ok(card_data) = nfc_driver.read_card_data() { - log::info!("NFC Card Read: {:?}", card_data); - } - - if button1.is_low() { - log::info!("Button pressed!"); - display.clear(Rgb565::BLACK).unwrap(); - let payload = if nav_state.toggle_payload() { - &MOCK_PAYLOAD - } else { - &MOCK_PAYLOAD2 - }; - - render_nfc_payload(&mut display, payload); - } Timer::after_millis(10).await; } } -- 2.39.5 From cfe1c6b11ad2b5f1e021f80f78802be4cfa2ecbb Mon Sep 17 00:00:00 2001 From: ede1998 Date: Wed, 5 Aug 2026 20:33:00 +0200 Subject: [PATCH 3/3] working mini display example --- Cargo.lock | 1 + Cargo.toml | 4 +--- src/bin/experiment_ch1115.rs | 10 +++++----- 3 files changed, 7 insertions(+), 8 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index ed56a59..9700615 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -232,6 +232,7 @@ checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801" [[package]] name = "ch1115" version = "0.1.2" +source = "git+https://github.com/ede1998/ch1115.git?rev=2d3a3ba38050efe012e6a210afe3e125ec280c37#2d3a3ba38050efe012e6a210afe3e125ec280c37" dependencies = [ "display-interface", "embedded-graphics-core", diff --git a/Cargo.toml b/Cargo.toml index 2725dce..ca491db 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -48,9 +48,7 @@ embedded-hal-compat = "0.13.0" [patch.crates-io] -ch1115 = { path = "./ch1115" } - - +ch1115 = { git = "https://github.com/ede1998/ch1115.git", rev = "2d3a3ba38050efe012e6a210afe3e125ec280c37" } # For fine tuning these settings, please refer to https://doc.rust-lang.org/cargo/reference/profiles.html [profile.dev] diff --git a/src/bin/experiment_ch1115.rs b/src/bin/experiment_ch1115.rs index f3ed3cc..6b7d7a7 100644 --- a/src/bin/experiment_ch1115.rs +++ b/src/bin/experiment_ch1115.rs @@ -95,12 +95,12 @@ async fn main(_spawner: Spawner) { loop { log::info!("Init finished"); - // let text_style = MonoTextStyle::new(&FONT_6X10, BinaryColor::On); - // Text::new("Hello OLED", Point::new(10, 20), text_style) - // .draw(&mut display) - // .unwrap(); + let text_style = MonoTextStyle::new(&FONT_6X10, BinaryColor::On); + Text::new("Hello OLED", Point::new(10, 20), text_style) + .draw(&mut display) + .unwrap(); - // display.flush().unwrap(); + display.flush().unwrap(); Timer::after_millis(1000).await; } } -- 2.39.5