diff --git a/src/display.rs b/src/display.rs index aa8c10c..92721c2 100644 --- a/src/display.rs +++ b/src/display.rs @@ -8,7 +8,7 @@ use esp_hal::Async; use mipidsi::interface::SpiInterface; use mipidsi::models::ST7789; use mipidsi::options::{ColorInversion, ColorOrder, Orientation, Rotation}; -use mipidsi::{Builder, Display}; +use mipidsi::Builder; pub const WIDTH: u32 = 172; pub const HEIGHT: u32 = 320; @@ -25,13 +25,13 @@ pub type SpiDmaBus = SpiDma<'static, Async>; /// SPI bus + CS, presented to mipidsi as an `SpiDevice` for init, while keeping /// the raw pieces recoverable for the async framebuffer path. -pub struct LcdDevice { +struct LcdDevice { spi: SpiDmaBus, cs: Output<'static>, } impl LcdDevice { - pub fn into_inner(self) -> (SpiDmaBus, Output<'static>) { + fn into_inner(self) -> (SpiDmaBus, Output<'static>) { (self.spi, self.cs) } } @@ -60,11 +60,6 @@ impl SpiDevice for LcdDevice { } } -type Interface<'a> = SpiInterface<'a, LcdDevice, Output<'static>>; - -/// The mipidsi [`Display`] used to initialize the panel. -pub type Lcd<'a> = Display, ST7789, Output<'static>>; - /// All the peripheral resources the LCD needs. pub struct LcdPins { pub spi2: SPI2<'static>, @@ -76,7 +71,15 @@ pub struct LcdPins { pub dma_ch: DMA_CH0<'static>, } -pub fn init<'a>(pins: LcdPins, buffer: &'a mut [u8]) -> Lcd<'a> { +/// The raw LCD resources, ready to drive the async framebuffer path. +pub struct LcdParts { + pub spi: SpiDmaBus, + pub cs: Output<'static>, + pub dc: Output<'static>, +} + +pub fn init_parts(pins: LcdPins) -> LcdParts { + let mut buffer = [0u8; 4096]; let dma_tx_buf = esp_hal::dma_tx_buffer!(DMA_BUF_SIZE).expect("Failed to create DMA TX buffer"); let dma_rx_buf = esp_hal::dma_rx_buffer!(4).expect("Failed to create DMA RX buffer"); @@ -98,14 +101,14 @@ pub fn init<'a>(pins: LcdPins, buffer: &'a mut [u8]) -> Lcd<'a> { }; let dc_pin = Output::new(pins.dc, Level::Low, OutputConfig::default()); - let interface = SpiInterface::new(device, dc_pin, buffer); + let interface = SpiInterface::new(device, dc_pin, &mut buffer); let mut rst_pin = Output::new(pins.rst, Level::High, OutputConfig::default()); rst_pin.set_high(); let mut delay = esp_hal::delay::Delay::new(); - Builder::new(ST7789, interface) + let lcd = Builder::new(ST7789, interface) .display_size(WIDTH as u16, HEIGHT as u16) .display_offset(COLUMN_OFFSET, 0) .orientation(Orientation::new().rotate(Rotation::Deg0)) @@ -113,7 +116,13 @@ pub fn init<'a>(pins: LcdPins, buffer: &'a mut [u8]) -> Lcd<'a> { .invert_colors(ColorInversion::Inverted) .reset_pin(rst_pin) .init(&mut delay) - .expect("Failed to init display") + .expect("Failed to init display"); + + let (interface, _model, _rst) = lcd.release(); + let (device, dc) = interface.release(); + let (spi, cs) = device.into_inner(); + + LcdParts { spi, cs, dc } } /// Stream a full framebuffer to the panel asynchronously (DMA). diff --git a/src/main.rs b/src/main.rs index c49c90c..68c59a1 100644 --- a/src/main.rs +++ b/src/main.rs @@ -10,10 +10,9 @@ use embedded_graphics::pixelcolor::Rgb565; use embedded_graphics::prelude::*; use embedded_graphics::primitives::{PrimitiveStyle, Triangle}; use embassy_executor::Spawner; -use embassy_time::Timer; +use embassy_time::{Instant, Timer}; use esp_backtrace as _; use esp_bootloader_esp_idf::esp_app_desc; -use esp_hal::gpio::Output; use esp_hal::timer::timg::TimerGroup; use esp_hal::{init, rmt::Rmt, time::Rate, Config}; use esp_println::println; @@ -42,13 +41,14 @@ async fn led_task(mut led: led::RgbLed) { #[embassy_executor::task] async fn display_task( - mut spi: display::SpiDmaBus, - mut dc: Output<'static>, - mut cs: Output<'static>, + pins: display::LcdPins, mut fb: framebuffer::Framebuffer, timer: esp_hal::ledc::timer::Timer<'static, esp_hal::ledc::LowSpeed>, bl_pin: esp_hal::peripherals::GPIO22<'static>, ) { + let mut parts = display::init_parts(pins); + println!("LCD {}x{} initialized", display::WIDTH, display::HEIGHT); + let mut backlight = backlight::LcdBacklight::new(&timer, bl_pin).expect("Failed to init backlight"); backlight.set_level(0.5).unwrap(); @@ -62,6 +62,8 @@ async fn display_task( let mut vx: i32 = 3; let mut vy: i32 = 2; let mut frame = 0u32; + let mut fps_window_start = Instant::now(); + let mut fps_frames = 0u32; loop { let hue = (frame % 256) as u8; @@ -95,7 +97,7 @@ async fn display_task( .draw(&mut fb) .unwrap(); - display::send_framebuffer(&mut spi, &mut dc, &mut cs, fb.as_bytes()) + display::send_framebuffer(&mut parts.spi, &mut parts.dc, &mut parts.cs, fb.as_bytes()) .await .unwrap(); @@ -106,6 +108,15 @@ async fn display_task( ); } + fps_frames += 1; + if fps_frames >= 60 { + let elapsed_ms = fps_window_start.elapsed().as_millis().max(1); + let fps = (fps_frames as u64 * 1000) / elapsed_ms; + println!("fps: {fps} ({fps_frames} frames in {elapsed_ms} ms)"); + fps_frames = 0; + fps_window_start = Instant::now(); + } + frame = frame.wrapping_add(1); } } @@ -124,8 +135,8 @@ async fn main(spawner: Spawner) -> ! { let (_ledc, timer) = backlight::setup_timer(peripherals.LEDC).expect("Failed to init backlight timer"); - let mut buffer = [0u8; 4096]; - let lcd = display::init( + spawner.spawn(led_task(led).unwrap()); + spawner.spawn(display_task( display::LcdPins { spi2: peripherals.SPI2, sck: peripherals.GPIO7, @@ -135,20 +146,6 @@ async fn main(spawner: Spawner) -> ! { rst: peripherals.GPIO21, dma_ch: peripherals.DMA_CH0, }, - &mut buffer, - ); - - let (interface, _model, _rst) = lcd.release(); - let (device, dc) = interface.release(); - let (spi, cs) = device.into_inner(); - - println!("LCD {}x{} initialized", display::WIDTH, display::HEIGHT); - - spawner.spawn(led_task(led).unwrap()); - spawner.spawn(display_task( - spi, - dc, - cs, framebuffer::Framebuffer, timer, peripherals.GPIO22,