diff --git a/src/bin/main.rs b/src/bin/main.rs index bf7600f..ef4e201 100644 --- a/src/bin/main.rs +++ b/src/bin/main.rs @@ -10,7 +10,8 @@ use alloc::boxed::Box; use creaturedex::card::decoder::split_nfc_hex; use creaturedex::card::model::Card; -use creaturedex::display::shared_bus::{self, DualSecondaryDisplay}; +use creaturedex::display::secondary_oled::DualSecondaryDisplay; +use creaturedex::display::shared_bus; use creaturedex::display::tertiary_lcd::{TertiaryI2cLcd, init_tertiary_lcd, write_wrapped}; use creaturedex::drivers::nfc_pn532::NfcPn532Driver; use creaturedex::navigation::inputs::Inputs; @@ -66,7 +67,7 @@ async fn main(spawner: Spawner) { let timg0 = TimerGroup::new(peripherals.TIMG0); esp_rtos::start(timg0.timer0, sw_interrupt.software_interrupt0); - let (display, mut oled) = shared_bus::DisplayPinConfiguration { + let (primary, mut secondaries) = shared_bus::DisplayPinConfiguration { spi_peripheral: peripherals.SPI2, sck: peripherals.GPIO36, mosi: peripherals.GPIO35, @@ -120,15 +121,20 @@ async fn main(spawner: Spawner) { ok: Input::new(peripherals.GPIO0, button_config), }; - display_shit(&mut oled); + display_shit(&mut secondaries[0], "foo 1"); + display_shit(&mut secondaries[1], "bar 1"); + display_shit(&mut secondaries[2], "baz 1"); - let outputs = Outputs { - primary_display: display, - secondary_display_1: oled, - // secondary_display_2: todo!(), - // secondary_display_3: todo!(), - tertiary_display: lcd, - _led_a: (), + let outputs = { + let [sec_1, sec_2, sec_3] = secondaries; + Outputs { + primary_display: primary, + secondary_display_1: sec_1, + secondary_display_2: sec_2, + secondary_display_3: sec_3, + tertiary_display: lcd, + _led_a: (), + } }; log::info!("Setup complete, entering main loop"); @@ -173,11 +179,11 @@ async fn nfc_driver_task( } } -fn display_shit(oled: &mut DualSecondaryDisplay<'static>) { +fn display_shit(oled: &mut DualSecondaryDisplay<'static>, text: &str) { oled.clear().unwrap(); let text_style = MonoTextStyle::new(&FONT_6X10, BinaryColor::On); - Text::new("Hello OLED", Point::new(10, 20), text_style) + Text::new(text, Point::new(10, 20), text_style) .draw(oled) .unwrap(); oled.flush().unwrap(); diff --git a/src/display/primary_lcd.rs b/src/display/primary_lcd.rs index a8a5578..8941a98 100644 --- a/src/display/primary_lcd.rs +++ b/src/display/primary_lcd.rs @@ -2,42 +2,41 @@ use display_interface_spi::SPIInterface; use embedded_graphics::draw_target::DrawTarget; use embedded_graphics::pixelcolor::Rgb565; use embedded_graphics::prelude::RgbColor; -use embedded_hal::digital::OutputPin as EhOutputPin; use embedded_hal_bus::spi::AtomicDevice; use embedded_hal_bus::util::AtomicCell; use esp_hal::delay::Delay; -use esp_hal::gpio::{Level, Output, OutputConfig, OutputPin}; +use esp_hal::gpio::Output; use ili9341::{DisplaySize240x320, Ili9341, Orientation}; -pub type PrimaryLcdDisplay<'a, BUS, DC, RES> = +use crate::display::shared_bus::{DisplaySpiBus, SharedOutput}; + +type GenericDualPrimaryDisplay<'a, BUS, DC, RES> = Ili9341, Delay>, DC>, RES>; +pub type DualPrimaryDisplay<'a> = + GenericDualPrimaryDisplay<'a, DisplaySpiBus, SharedOutput<'a>, Output<'a>>; -pub fn init_primary_lcd_on_bus<'a, BUS, CS, RES, DC>( - spi_bus: &'a AtomicCell, - cs: CS, - reset: RES, - dc: DC, - delay: &mut Delay, -) -> 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 = AtomicDevice::new(spi_bus, cs_pin, *delay).unwrap(); - let iface = SPIInterface::new(spi_dev, dc); - - let mut display = Ili9341::new( - iface, - reset, - delay, - Orientation::Portrait, - DisplaySize240x320, - ) - .unwrap(); - - display.clear(Rgb565::BLACK).unwrap(); - display +pub struct PrimaryDisplayPinConfiguration<'a> { + pub spi_bus: &'a AtomicCell, + pub cs: Output<'a>, + pub reset: Output<'a>, + pub dc: SharedOutput<'a>, +} + +impl<'a> PrimaryDisplayPinConfiguration<'a> { + pub fn build(self) -> DualPrimaryDisplay<'a> { + let spi_dev = AtomicDevice::new(self.spi_bus, self.cs, Delay::new()).unwrap(); + let iface = SPIInterface::new(spi_dev, self.dc); + + let mut display = Ili9341::new( + iface, + self.reset, + &mut Delay::new(), + Orientation::Portrait, + DisplaySize240x320, + ) + .unwrap(); + + display.clear(Rgb565::BLACK).unwrap(); + display + } } diff --git a/src/display/secondary_oled.rs b/src/display/secondary_oled.rs index b003bdb..340faee 100644 --- a/src/display/secondary_oled.rs +++ b/src/display/secondary_oled.rs @@ -1,34 +1,59 @@ +use alloc::format; use ch1115::{Ch1115, Size128x64}; use display_interface_spi::SPIInterface; -use embedded_hal::digital::OutputPin as EhOutputPin; use embedded_hal_bus::spi::AtomicDevice; use embedded_hal_bus::util::AtomicCell; use esp_hal::delay::Delay; -use esp_hal::gpio::{Level, Output, OutputConfig, OutputPin}; +use esp_hal::gpio::Output; + +use crate::display::shared_bus::{DisplaySpiBus, SharedOutput, SharedReset}; pub type SecondaryOledDisplay<'a, BUS, RST, DC> = Ch1115, Delay>, DC>, RST, Size128x64>; +pub type DualSecondaryDisplay<'a> = + SecondaryOledDisplay<'a, DisplaySpiBus, SharedReset<'a>, SharedOutput<'a>>; -pub fn init_secondary_oled_on_bus<'a, BUS, CS, RES, DC>( - spi_bus: &'a AtomicCell, - cs: CS, - res: RES, - dc: DC, - delay: &mut Delay, -) -> SecondaryOledDisplay<'a, BUS, RES, 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 = AtomicDevice::new(spi_bus, cs_pin, *delay).unwrap(); - let interface = SPIInterface::new(spi_dev, dc); - - let mut display = Ch1115::new(interface, res, Size128x64); - - display.init(&mut Delay::new()).unwrap(); - - display +pub struct SecondaryDisplayPinConfiguration<'a> { + pub spi_bus: &'a AtomicCell, + pub cs_pins: [Output<'a>; 3], + pub res: SharedReset<'a>, + pub dc: SharedOutput<'a>, +} + +impl<'a> SecondaryDisplayPinConfiguration<'a> { + pub fn build(self) -> [DualSecondaryDisplay<'a>; 3] { + let mut displays = self.cs_pins.map(|cs| { + let spi_dev = AtomicDevice::new(self.spi_bus, cs, Delay::new()).unwrap(); + let interface = SPIInterface::new(spi_dev, self.dc.clone()); + + Ch1115::new(interface, self.res.clone(), Size128x64) + }); + + init_single_reset(&mut displays, self.res); + + displays + } +} + +fn init_single_reset<'a: 'b, 'b>( + displays: impl IntoIterator>, + shared_reset: SharedReset<'a>, +) { + let mut displays = displays.into_iter().peekable(); + + // Reset all secondaries via shared reset + let Some(any_display) = displays.peek_mut() else { + // no display to initialize + return; + }; + any_display.hard_reset(&mut Delay::new()).unwrap(); + + shared_reset.disable(true); + + for (i, display) in displays.enumerate() { + display + .init(&mut Delay::new()) + .map_err(|e| format!("Failed to init secondary display {}: {e:?}", i + 1)) + .unwrap(); + } } diff --git a/src/display/shared_bus.rs b/src/display/shared_bus.rs index 37145bc..f8a55b4 100644 --- a/src/display/shared_bus.rs +++ b/src/display/shared_bus.rs @@ -3,15 +3,16 @@ use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex; use embedded_hal::digital::ErrorType; use embedded_hal::digital::OutputPin as EhOutputPin; use embedded_hal_bus::util::AtomicCell; -use esp_hal::delay::Delay; use esp_hal::gpio::interconnect::{PeripheralInput, PeripheralOutput}; use esp_hal::gpio::{Level, Output, OutputConfig, OutputPin}; use esp_hal::spi::Mode; use esp_hal::spi::master::{Config as SpiConfig, Spi}; use esp_hal::time::Rate; -use crate::display::primary_lcd::{PrimaryLcdDisplay, init_primary_lcd_on_bus}; -use crate::display::secondary_oled::{SecondaryOledDisplay, init_secondary_oled_on_bus}; +use crate::display::primary_lcd::DualPrimaryDisplay; +use crate::display::primary_lcd::PrimaryDisplayPinConfiguration; +use crate::display::secondary_oled::DualSecondaryDisplay; +use crate::display::secondary_oled::SecondaryDisplayPinConfiguration; use core::cell::RefCell; use embassy_sync::blocking_mutex::Mutex as BlockingMutex; @@ -101,13 +102,9 @@ impl<'a, P: EhOutputPin> EhOutputPin for SharedResetPin<'a, P> { } } -type SharedOutput<'a> = SharedPin<'a, Output<'a>>; -type SharedReset<'a> = SharedResetPin<'a, Output<'a>>; +pub type SharedOutput<'a> = SharedPin<'a, Output<'a>>; +pub type SharedReset<'a> = SharedResetPin<'a, Output<'a>>; pub type DisplaySpiBus = Spi<'static, esp_hal::Blocking>; -pub type DualPrimaryDisplay<'a> = - PrimaryLcdDisplay<'a, DisplaySpiBus, SharedOutput<'a>, Output<'a>>; -pub type DualSecondaryDisplay<'a> = - SecondaryOledDisplay<'a, DisplaySpiBus, SharedReset<'a>, SharedOutput<'a>>; pub struct DisplayPinConfiguration { pub spi_peripheral: SPI, @@ -138,7 +135,12 @@ where RES2: OutputPin + 'static, DC: OutputPin + 'static, { - pub fn build(self) -> (DualPrimaryDisplay<'static>, DualSecondaryDisplay<'static>) { + pub fn build( + self, + ) -> ( + DualPrimaryDisplay<'static>, + [DualSecondaryDisplay<'static>; 3], + ) { let spi = Spi::new( self.spi_peripheral, SpiConfig::default() @@ -151,7 +153,6 @@ where .with_miso(self.miso); let bus_static: &'static AtomicCell<_> = Box::leak(Box::new(AtomicCell::new(spi))); - let reset_primary = Output::new(self.reset_primary, Level::High, OutputConfig::default()); let reset_secondary = SharedResetPin::new( Output::new(self.reset_secondary, Level::High, OutputConfig::default()), false, @@ -161,31 +162,31 @@ where Level::High, OutputConfig::default(), )); - let mut delay = Delay::new(); - let primary = init_primary_lcd_on_bus( - bus_static, - self.cs_primary, - reset_primary, - dc.clone(), - &mut delay, - ); + let primary = PrimaryDisplayPinConfiguration { + spi_bus: bus_static, + cs: low_active(self.cs_primary), + reset: low_active(self.reset_primary), + dc: dc.clone(), + } + .build(); - // TODO - // Build display - // Trigger reset - // disable init - // rest of init code - - - let secondary = init_secondary_oled_on_bus( - bus_static, - self.cs_secondary_1, - reset_secondary, + let secondaries = SecondaryDisplayPinConfiguration { + spi_bus: bus_static, + cs_pins: [ + low_active(self.cs_secondary_1), + low_active(self.cs_secondary_2), + low_active(self.cs_secondary_3), + ], + res: reset_secondary, dc, - &mut delay, - ); + } + .build(); - (primary, secondary) + (primary, secondaries) } } + +fn low_active<'a, CS: OutputPin + 'a>(pin: CS) -> Output<'a> { + Output::new(pin, Level::High, OutputConfig::default()) +} diff --git a/src/display/sprite.rs b/src/display/sprite.rs index e2d6d5d..b5dec5b 100644 --- a/src/display/sprite.rs +++ b/src/display/sprite.rs @@ -47,10 +47,7 @@ where let width = u32::try_from(end_x - start_x).unwrap(); let height = u32::try_from(end_y - start_y).unwrap(); - let area = Rectangle::new( - Point::new(start_x.try_into().unwrap(), start_y.try_into().unwrap()), - Size::new(width, height), - ); + let area = Rectangle::new(Point::new(start_x, start_y), Size::new(width, height)); let _ = ili9341.fill_solid(&area, color); } diff --git a/src/navigation/outputs.rs b/src/navigation/outputs.rs index 277e478..5181a99 100644 --- a/src/navigation/outputs.rs +++ b/src/navigation/outputs.rs @@ -4,15 +4,15 @@ use embedded_graphics::{ }; use crate::display::{ - shared_bus::{DualPrimaryDisplay, DualSecondaryDisplay}, + primary_lcd::DualPrimaryDisplay, secondary_oled::DualSecondaryDisplay, tertiary_lcd::TertiaryI2cLcd, }; pub struct Outputs { pub primary_display: DualPrimaryDisplay<'static>, pub secondary_display_1: DualSecondaryDisplay<'static>, - // pub secondary_display_2: DualSecondaryDisplay<'static>, - // pub secondary_display_3: DualSecondaryDisplay<'static>, + pub secondary_display_2: DualSecondaryDisplay<'static>, + pub secondary_display_3: DualSecondaryDisplay<'static>, pub tertiary_display: TertiaryI2cLcd<'static>, pub _led_a: (), }