use embassy_time::Delay;
use embedded_hal_bus::spi::{DeviceError, ExclusiveDevice, NoDelay};
use esp_hal::Async;
use esp_hal::gpio::{Level, Output, OutputConfig};
use esp_hal::peripherals::{DMA_CH0, GPIO33, GPIO34, GPIO35, GPIO36, GPIO37, SPI3};
use esp_hal::spi::Mode;
use esp_hal::spi::master::{Config, Spi, SpiDma};
use esp_hal::time::Rate;
use oxstack_drivers::st7789::{self, M5_ST7789V2_INIT, Panel, Rotation, St7789};
use oxstack_gfx::Framebuffer;
use static_cell::ConstStaticCell;
pub const WIDTH: usize = 240;
pub const HEIGHT: usize = 135;
pub type Frame = Framebuffer<WIDTH, HEIGHT>;
pub const PANEL: Panel = Panel {
width: 135,
height: 240,
offset_x: 52,
offset_y: 40,
memory_width: 240,
memory_height: 320,
bgr: true,
invert: true,
};
pub const ROTATION: Rotation = Rotation::Deg90;
pub const SPI_FREQUENCY: Rate = Rate::from_mhz(40);
pub fn try_framebuffer() -> Option<&'static mut Frame> {
static FRAME: ConstStaticCell<Frame> = ConstStaticCell::new(Frame::new());
FRAME.try_take()
}
pub fn framebuffer() -> &'static mut Frame {
try_framebuffer().expect("framebuffer already taken")
}
type Bus = ExclusiveDevice<SpiDma<'static, Async>, Output<'static>, NoDelay>;
pub type Error = st7789::Error<
DeviceError<esp_hal::spi::Error, core::convert::Infallible>,
core::convert::Infallible,
>;
#[allow(missing_docs)]
pub struct DisplayResources {
pub spi: SPI3<'static>,
pub dma: DMA_CH0<'static>,
pub sck: GPIO36<'static>,
pub mosi: GPIO35<'static>,
pub cs: GPIO37<'static>,
pub dc: GPIO34<'static>,
pub rst: GPIO33<'static>,
}
impl DisplayResources {
pub async fn init(self) -> Result<Display, Error> {
let rx = esp_hal::dma_rx_buffer!(32).expect("valid DMA buffer");
let tx = esp_hal::dma_tx_buffer!(256).expect("valid DMA buffer");
let spi = Spi::new(
self.spi,
Config::default()
.with_frequency(SPI_FREQUENCY)
.with_mode(Mode::_0),
)
.expect("40 MHz mode 0 is a valid SPI configuration")
.with_sck(self.sck)
.with_mosi(self.mosi)
.with_dma(self.dma)
.with_buffers(rx, tx)
.into_async();
let out = OutputConfig::default();
let cs = Output::new(self.cs, Level::High, out);
let dc = Output::new(self.dc, Level::Low, out);
let mut rst = Output::new(self.rst, Level::High, out);
let mut delay = Delay;
let Ok(()) = st7789::hard_reset(&mut rst, &mut delay).await;
let Ok(bus) = ExclusiveDevice::new_no_delay(spi, cs);
let mut lcd = St7789::new(bus, dc, PANEL, ROTATION);
lcd.init(M5_ST7789V2_INIT, &mut delay).await?;
Ok(Display { lcd, _rst: rst })
}
}
pub struct Display {
lcd: St7789<Bus, Output<'static>>,
_rst: Output<'static>,
}
impl Display {
pub async fn flush(&mut self, frame: &mut Frame) -> Result<bool, Error> {
let Some(dirty) = frame.take_dirty() else {
return Ok(false);
};
self.lcd.write_rows(dirty.y, dirty.bytes).await?;
Ok(true)
}
pub async fn set_display_on(&mut self, on: bool) -> Result<(), Error> {
self.lcd.set_display_on(on).await
}
pub async fn set_sleep(&mut self, sleep: bool) -> Result<(), Error> {
self.lcd.set_sleep(sleep, &mut Delay).await
}
pub fn controller(&mut self) -> &mut St7789<Bus, Output<'static>> {
&mut self.lcd
}
}