use core::{cell::RefCell, fmt};
use embassy_embedded_hal::shared_bus::blocking::spi::SpiDeviceWithConfig;
use embassy_sync::blocking_mutex::{Mutex, raw::NoopRawMutex};
use embedded_graphics::{mono_font::MonoFont, pixelcolor::Rgb888};
use esp_hal::{
gpio::{Output, OutputConfig},
spi,
};
use static_cell::StaticCell;
use device_envoy_core::button::Button;
use device_envoy_core::cyd::{Cyd, backend};
use super::{
CydDisplayEsp, CydStaticEsp, CydTouchEsp, CydTouchUncalibratedEsp, Error, Orientation,
TOUCH_SPI_HZ, buffer::PixelBuffer,
};
use crate::flash_block::FlashBlockEsp;
type SharedSpiBus = spi::master::Spi<'static, esp_hal::Blocking>;
type SharedSpiMutex = Mutex<NoopRawMutex, RefCell<SharedSpiBus>>;
type SharedSpiDevice = SpiDeviceWithConfig<'static, NoopRawMutex, SharedSpiBus, Output<'static>>;
pub struct CydEspOneSpi {
display: CydDisplayEsp<SharedSpiDevice>,
touch: CydTouchEsp<SharedSpiDevice>,
}
impl CydEspOneSpi {
pub const SCREEN_PIXELS: usize = device_envoy_core::cyd::SCREEN_PIXELS;
#[must_use]
pub const fn new_static<const PIXEL_COUNT: usize>() -> CydStaticEsp<PIXEL_COUNT> {
CydStaticEsp::new()
}
#[allow(clippy::too_many_arguments)]
pub async fn new<const PIXEL_COUNT: usize, R: Button>(
statics: &'static CydStaticEsp<PIXEL_COUNT>,
spi: impl spi::master::Instance + 'static,
sck_pin: impl esp_hal::gpio::interconnect::PeripheralOutput<'static>,
mosi_pin: impl esp_hal::gpio::interconnect::PeripheralOutput<'static>,
miso_pin: impl esp_hal::gpio::interconnect::PeripheralInput<'static>,
lcd_cs_pin: impl esp_hal::gpio::OutputPin + 'static,
lcd_dc_pin: impl esp_hal::gpio::OutputPin + 'static,
lcd_rst_pin: impl esp_hal::gpio::OutputPin + 'static,
lcd_backlight_pin: impl esp_hal::gpio::OutputPin + 'static,
display_spi_hz: u32,
touch_cs_pin: impl esp_hal::gpio::OutputPin + 'static,
touch_irq_pin: impl esp_hal::gpio::InputPin + 'static,
orientation: Orientation,
background_color: Rgb888,
foreground_color: Rgb888,
font: &'static MonoFont<'static>,
calibration_flash_block: &mut FlashBlockEsp,
recalibration_button: &mut R,
) -> crate::Result<Self> {
let spi_config = spi::master::Config::default()
.with_frequency(esp_hal::time::Rate::from_hz(TOUCH_SPI_HZ))
.with_mode(spi::Mode::_0);
let spi = spi::master::Spi::new(spi, spi_config)?
.with_sck(sck_pin)
.with_mosi(mosi_pin)
.with_miso(miso_pin);
static SHARED_SPI: StaticCell<SharedSpiMutex> = StaticCell::new();
let shared_spi: &'static SharedSpiMutex = SHARED_SPI.init(Mutex::new(RefCell::new(spi)));
let lcd_cs = Output::new(
lcd_cs_pin,
esp_hal::gpio::Level::High,
OutputConfig::default(),
);
let touch_cs = Output::new(
touch_cs_pin,
esp_hal::gpio::Level::High,
OutputConfig::default(),
);
let lcd_spi_config = spi::master::Config::default()
.with_frequency(esp_hal::time::Rate::from_hz(display_spi_hz))
.with_mode(spi::Mode::_0);
let touch_spi_config = spi::master::Config::default()
.with_frequency(esp_hal::time::Rate::from_hz(TOUCH_SPI_HZ))
.with_mode(spi::Mode::_0);
let lcd_spi_device = SpiDeviceWithConfig::new(shared_spi, lcd_cs, lcd_spi_config);
let touch_spi_device = SpiDeviceWithConfig::new(shared_spi, touch_cs, touch_spi_config);
let pixel_buffer = PixelBuffer::init_static(&statics.pixel_buffer);
let mut display = CydDisplayEsp::new_from_device(
lcd_spi_device,
lcd_dc_pin,
lcd_rst_pin,
lcd_backlight_pin,
Orientation::Landscape,
background_color,
foreground_color,
font,
pixel_buffer,
)?;
let touch = CydTouchUncalibratedEsp::from_device(touch_spi_device, touch_irq_pin);
let touch = backend::ensure_calibration(
&mut display,
touch,
calibration_flash_block,
recalibration_button,
None,
orientation,
)
.await
.map_err(|error| match error {
backend::Error::Device(cyd_error) => crate::Error::from(cyd_error),
backend::Error::Flash(flash_error) => flash_error,
})?;
display.set_orientation(orientation)?;
Ok(Self { display, touch })
}
}
impl Cyd for CydEspOneSpi {
type Error = Error;
type Display = CydDisplayEsp<SharedSpiDevice>;
type Touch = CydTouchEsp<SharedSpiDevice>;
fn parts(&mut self) -> (&mut Self::Display, &mut Self::Touch) {
(&mut self.display, &mut self.touch)
}
fn orientation(&self) -> Orientation {
self.display.orientation
}
}
impl fmt::Debug for CydEspOneSpi {
fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
formatter
.debug_struct("CydEspOneSpi")
.field("orientation", &self.display.orientation)
.finish_non_exhaustive()
}
}