From d7eeb67538b9a307cc7a9a4b50bab6c98e651c9d Mon Sep 17 00:00:00 2001 From: Jannik Beyerstedt Date: Mon, 20 Jul 2026 11:36:05 +0200 Subject: [PATCH] screen: Move setup to module --- src/main.rs | 79 ++++++--------------------------- src/screen.rs | 120 ++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 134 insertions(+), 65 deletions(-) diff --git a/src/main.rs b/src/main.rs index 1a5d2ef..76557b1 100644 --- a/src/main.rs +++ b/src/main.rs @@ -18,8 +18,6 @@ use embassy_gps::gps::l76k; use esp_backtrace as _; use esp_hal::clock::CpuClock; use esp_hal::timer::timg::TimerGroup; -#[cfg(feature = "screen")] -use esp_hal::{delay, gpio, spi, time}; #[cfg(all(target_arch = "riscv32", feature = "spat"))] use esp_println::println; use esp_radio::wifi; @@ -36,10 +34,6 @@ mod radio; mod screen; const WIFI_CHANNEL: radio::Channel = 180; -#[cfg(feature = "screen")] -pub const DISPLAY_WIDTH: u16 = 320; -#[cfg(feature = "screen")] -pub const DISPLAY_HEIGHT: u16 = 172; // This creates a default app-descriptor required by the esp-idf bootloader. // For more information see: @@ -84,7 +78,7 @@ async fn main(spawner: Spawner) -> ! { info!("Embassy initialized!"); - // ST7789 172*320px screen + // Setup ST7789 screen // SCL: D8/ GPIO8 (SPI_SCK) // SDA: D10/ GPIO10 (SPI_MOSI) // RES: D3/ GPIO7 @@ -95,43 +89,28 @@ async fn main(spawner: Spawner) -> ! { let mut spi_buffer = [0_u8; 512]; #[cfg(feature = "screen")] let mut screen = { - let (spi, cs) = setup_st7789_spi( + // 172*320px in landscape orientation + let (display, width, height) = screen::make_small_display( peripherals.GPIO8, peripherals.GPIO10, + peripherals.GPIO7, + peripherals.GPIO23, peripherals.GPIO24, - peripherals.GPIO9, + peripherals.GPIO0, peripherals.SPI2, + &mut spi_buffer, ) - .expect("Failed to build SPI config"); - let output_config = gpio::OutputConfig::default(); - let dc_output = gpio::Output::new(peripherals.GPIO23, gpio::Level::Low, output_config); - let rst_output = gpio::Output::new(peripherals.GPIO7, gpio::Level::High, output_config); - let _ = gpio::Output::new(peripherals.GPIO0, gpio::Level::High, output_config); // backlight + .expect("Fatal error building screen"); - let mut display_delay = delay::Delay::new(); + let mut screen = screen::Handler::new(display, width, height); - let spi_device = embedded_hal_bus::spi::ExclusiveDevice::new_no_delay(spi, cs) - .expect("Failed to initialize SPI device"); + let _ = screen + .update(screen::ScreenState::Initial) + .inspect_err(log_screen_error); - let display = mipidsi::Builder::new( - mipidsi::models::ST7789, - mipidsi::interface::SpiInterface::new(spi_device, dc_output, &mut spi_buffer), - ) - .display_size(DISPLAY_HEIGHT + 34, DISPLAY_WIDTH) // somehow this display has 34 px of invisible space on the left (in native orientation) - .reset_pin(rst_output) - .invert_colors(mipidsi::options::ColorInversion::Inverted) - .orientation(mipidsi::options::Orientation::new().rotate(mipidsi::options::Rotation::Deg90)) - .init(&mut display_delay) - .expect("Failed to initialize display"); - - screen::Handler::new(display, DISPLAY_WIDTH, DISPLAY_HEIGHT) + screen }; - #[cfg(feature = "screen")] - let _ = screen - .update(screen::ScreenState::Initial) - .inspect_err(log_screen_error); - // Setup WiFi let (mut wifi_controller, interfaces) = esp_radio::wifi::new( peripherals.WIFI, @@ -155,7 +134,7 @@ async fn main(spawner: Spawner) -> ! { let mac = esp_hal::efuse::base_mac_address(); info!("WiFi MAC: {mac}"); - // Seeed XIAO L67K: + // Setup GNSS: Seeed XIAO L67K: // RX: D7/ GPIO12 // TX: D6/ GPIO11 // WAKEUP: D0/ GPIO1 -> HIGH for active, LOW for Sleep @@ -376,36 +355,6 @@ async fn gnss_task( } } -/// Builds the SPI interface for the ST7789 display -/// -/// Returns (spi master, chip-select) tuple or SPI config error -#[cfg(feature = "screen")] -fn setup_st7789_spi( - scl: esp_hal::peripherals::GPIO8<'static>, - sda: esp_hal::peripherals::GPIO10<'static>, - cs: esp_hal::peripherals::GPIO24<'static>, - miso: esp_hal::peripherals::GPIO9<'static>, - spi: esp_hal::peripherals::SPI2<'static>, -) -> Result< - ( - spi::master::Spi<'static, esp_hal::Blocking>, - gpio::Output<'static>, - ), - spi::master::ConfigError, -> { - let spi_config = spi::master::Config::default() - .with_frequency(time::Rate::from_mhz(40)) - .with_mode(spi::Mode::_0); - let spi: spi::master::Spi<'_, esp_hal::Blocking> = spi::master::Spi::new(spi, spi_config)? - .with_sck(scl) - .with_mosi(sda) - .with_miso(miso); - - let cs_output = gpio::Output::new(cs, gpio::Level::High, gpio::OutputConfig::default()); - - Ok((spi, cs_output)) -} - #[cfg(feature = "screen")] fn log_screen_error(error: &E) where diff --git a/src/screen.rs b/src/screen.rs index d72d0f3..b2ddf90 100644 --- a/src/screen.rs +++ b/src/screen.rs @@ -348,3 +348,123 @@ fn maneuver_to_str(value: applogic::v2x::map::Maneuvers) -> alloc::string::Strin if value.right_allowed { ">" } else { " " }, ) } + +/// Creates an [`mipidsi::Display`], width, height tuple for an ST7789 172*320px screen in landscape orientation +/// +/// # Errors +/// Human-readable fatal errors +#[allow(clippy::too_many_arguments, clippy::type_complexity)] +pub fn make_small_display( + scl: IoScl, + sda: IoSda, + res: IoRes, + dc: IoDc, + cs: IoCs, + bl: IoBl, + spi: Spi, + spi_buffer: &mut [u8], +) -> Result< + ( + mipidsi::Display< + mipidsi::interface::SpiInterface< + '_, + embedded_hal_bus::spi::ExclusiveDevice< + esp_hal::spi::master::Spi<'_, esp_hal::Blocking>, + esp_hal::gpio::Output<'_>, + embedded_hal_bus::spi::NoDelay, + >, + esp_hal::gpio::Output<'_>, + >, + mipidsi::models::ST7789, + esp_hal::gpio::Output<'_>, + >, + u16, + u16, + ), + alloc::string::String, +> +where + IoScl: esp_hal::gpio::interconnect::PeripheralOutput<'static>, + IoSda: esp_hal::gpio::interconnect::PeripheralOutput<'static>, + IoRes: + esp_hal::gpio::interconnect::PeripheralOutput<'static> + esp_hal::gpio::OutputPin + 'static, + IoDc: + esp_hal::gpio::interconnect::PeripheralOutput<'static> + esp_hal::gpio::OutputPin + 'static, + IoCs: + esp_hal::gpio::interconnect::PeripheralOutput<'static> + esp_hal::gpio::OutputPin + 'static, + IoBl: + esp_hal::gpio::interconnect::PeripheralOutput<'static> + esp_hal::gpio::OutputPin + 'static, + Spi: esp_hal::spi::master::Instance + 'static, +{ + const DISPLAY_WIDTH: u16 = 320; + const DISPLAY_HEIGHT: u16 = 172; + const DISPLAY_HIDDEN_X: u16 = 34; // somehow this display has 34 px of invisible space on the left (in native orientation) + + let (spi, cs) = setup_st7789_spi(scl, sda, cs, spi) + .map_err(|err| alloc::format!("Failed to build SPI config: {err}"))?; + + let output_config = esp_hal::gpio::OutputConfig::default(); + let dc_output = esp_hal::gpio::Output::new(dc, esp_hal::gpio::Level::Low, output_config); + let rst_output = esp_hal::gpio::Output::new(res, esp_hal::gpio::Level::High, output_config); + let _ = esp_hal::gpio::Output::new(bl, esp_hal::gpio::Level::High, output_config); // enable backlight + + let mut display_delay = esp_hal::delay::Delay::new(); + + let spi_device = embedded_hal_bus::spi::ExclusiveDevice::new_no_delay(spi, cs) + .map_err(|err| alloc::format!("Failed to initialize SPI device: {err}"))?; + + let display = mipidsi::Builder::new( + mipidsi::models::ST7789, + mipidsi::interface::SpiInterface::new(spi_device, dc_output, spi_buffer), + ) + .display_size(DISPLAY_HEIGHT + DISPLAY_HIDDEN_X, DISPLAY_WIDTH) + .reset_pin(rst_output) + .invert_colors(mipidsi::options::ColorInversion::Inverted) + .orientation(mipidsi::options::Orientation::new().rotate(mipidsi::options::Rotation::Deg90)) + .init(&mut display_delay) + .map_err(|err| alloc::format!("Failed to initialize display: {err:?}"))?; + + Ok((display, DISPLAY_WIDTH, DISPLAY_HEIGHT)) +} + +/// Builds the SPI interface for the ST7789 display +/// +/// Returns (spi master, chip-select) tuple +/// +/// # Errors +/// Returns SPI config error when SPI setup failed +fn setup_st7789_spi( + scl: IoScl, + sda: IoSda, + cs: IoCs, + spi: Spi, +) -> Result< + ( + esp_hal::spi::master::Spi<'static, esp_hal::Blocking>, + esp_hal::gpio::Output<'static>, + ), + esp_hal::spi::master::ConfigError, +> +where + IoScl: esp_hal::gpio::interconnect::PeripheralOutput<'static>, + IoSda: esp_hal::gpio::interconnect::PeripheralOutput<'static>, + IoCs: + esp_hal::gpio::interconnect::PeripheralOutput<'static> + esp_hal::gpio::OutputPin + 'static, + Spi: esp_hal::spi::master::Instance + 'static, +{ + let spi_config = esp_hal::spi::master::Config::default() + .with_frequency(esp_hal::time::Rate::from_mhz(40)) + .with_mode(esp_hal::spi::Mode::_0); + let spi: esp_hal::spi::master::Spi<'_, esp_hal::Blocking> = + esp_hal::spi::master::Spi::new(spi, spi_config)? + .with_sck(scl) + .with_mosi(sda); + + let cs_output = esp_hal::gpio::Output::new( + cs, + esp_hal::gpio::Level::High, + esp_hal::gpio::OutputConfig::default(), + ); + + Ok((spi, cs_output)) +}