use alloc::{alloc::Global, boxed::Box, vec::Vec}; use embassy_time::Timer; use esp_hal::{ Blocking, dma::{BurstConfig, ExternalBurstConfig, InternalBurstConfig}, gpio::{Flex, Level, Output, OutputConfig}, lcd_cam::{LcdCam, lcd::dpi::Dpi}, ledc::{self, LSGlobalClkSource, Ledc, LowSpeed}, spi::master::AnySpi, }; use esp_hal_bounce_buffers::{DmaBounce, Swapchain, SwapchainWriter, allocate_dma_buffer_in}; use i_slint_core::software_renderer::{Rgb565Pixel, TargetPixel}; use log::{error, info}; use crate::{DmaBounceController, PSRAM_ALLOCATOR, peripherals::st7701s::St7701s}; #[allow(non_snake_case)] pub struct DisplayPeripherals { pub DMA_CH2: esp_hal::peripherals::DMA_CH2<'static>, pub LCD_CAM: esp_hal::peripherals::LCD_CAM<'static>, pub LEDC: esp_hal::peripherals::LEDC<'static>, pub GPIO0: Output<'static>, pub GPIO1: esp_hal::peripherals::GPIO1<'static>, pub GPIO2: esp_hal::peripherals::GPIO2<'static>, pub GPIO3: esp_hal::peripherals::GPIO3<'static>, pub GPIO4: esp_hal::peripherals::GPIO4<'static>, #[cfg(not(feature = "alt-log"))] pub GPIO5: esp_hal::peripherals::GPIO5<'static>, pub GPIO6: esp_hal::peripherals::GPIO6<'static>, #[cfg(not(feature = "alt-log"))] pub GPIO12: esp_hal::peripherals::GPIO12<'static>, pub GPIO13: esp_hal::peripherals::GPIO13<'static>, pub GPIO14: esp_hal::peripherals::GPIO14<'static>, pub GPIO15: esp_hal::peripherals::GPIO15<'static>, pub GPIO16: esp_hal::peripherals::GPIO16<'static>, pub GPIO21: esp_hal::peripherals::GPIO21<'static>, pub GPIO34: esp_hal::peripherals::GPIO34<'static>, pub GPIO35: esp_hal::peripherals::GPIO35<'static>, pub GPIO36: esp_hal::peripherals::GPIO36<'static>, pub GPIO37: esp_hal::peripherals::GPIO37<'static>, pub GPIO38: esp_hal::peripherals::GPIO38<'static>, pub GPIO39: esp_hal::peripherals::GPIO39<'static>, pub GPIO40: esp_hal::peripherals::GPIO40<'static>, pub GPIO41: esp_hal::peripherals::GPIO41<'static>, pub GPIO42: esp_hal::peripherals::GPIO42<'static>, pub GPIO43: esp_hal::peripherals::GPIO43<'static>, pub GPIO44: esp_hal::peripherals::GPIO44<'static>, } impl DisplayPeripherals { pub async fn into_display(self) -> St7701s<'static, Blocking> { let mut ledc = Ledc::new(self.LEDC); ledc.set_global_slow_clock(LSGlobalClkSource::APBClk); let bl_timer = ledc.timer::(ledc::timer::Number::Timer0); let bl_channel = ledc.channel::(ledc::channel::Number::Channel0, self.GPIO21); let sck = Output::new(self.GPIO36, Level::High, OutputConfig::default()); let mosi = Flex::new(self.GPIO35); let cs = Output::new(self.GPIO6, Level::High, OutputConfig::default()); let lcd = LcdCam::new(self.LCD_CAM).lcd; #[allow(unused_mut)] let mut unconfigured_dpi = Dpi::new(lcd, self.DMA_CH2, Default::default()) .unwrap() .with_de(self.GPIO37) .with_pclk(self.GPIO34) .with_hsync(self.GPIO44) .with_vsync(self.GPIO43) // Blue .with_data0(self.GPIO38) .with_data1(self.GPIO39) .with_data2(self.GPIO40) .with_data3(self.GPIO41) .with_data4(self.GPIO42) // Green .with_data7(self.GPIO13) .with_data8(self.GPIO14) .with_data9(self.GPIO15) .with_data10(self.GPIO16) // Red .with_data11(self.GPIO0) .with_data12(self.GPIO1) .with_data13(self.GPIO2) .with_data14(self.GPIO3) .with_data15(self.GPIO4); #[cfg(not(feature = "alt-log"))] { unconfigured_dpi = unconfigured_dpi // Green .with_data5(peripherals.GPIO5) .with_data6(peripherals.GPIO12); } let st7701s = St7701s::new(sck, mosi, cs, unconfigured_dpi, bl_timer, bl_channel).await; info!("ST7701S-based LCD display initialized!"); st7701s } } // TODO: Rename or get rid of. pub struct Framebuffer { pub width: u32, pub height: u32, pub swapchain: Option, pub bounce_buffers: Option, } impl Framebuffer { pub fn new( channel: esp_hal::peripherals::DMA_CH0<'static>, peripheral_src: AnySpi<'static>, peripheral_dst: Dpi<'static, Blocking>, burst_config: BurstConfig, front_porch_pixels: u32, width_pixels: u32, height_pixels: u32, rows_per_window: usize, cyclic: bool, ) -> Self { const BYTES_PER_PIXEL: usize = core::mem::size_of::(); let buffer_size = width_pixels as usize * height_pixels as usize * BYTES_PER_PIXEL; let framebuffers = [ Box::leak(allocate_dma_buffer_in( buffer_size, burst_config, &PSRAM_ALLOCATOR, )), Box::leak(allocate_dma_buffer_in( buffer_size, burst_config, &PSRAM_ALLOCATOR, )), ]; let (swapchain_reader, swapchain_writer) = Swapchain { framebuffers }.into_reader_writer(); let bounce_buffers = DmaBounce::new( Global, channel, peripheral_src, peripheral_dst, swapchain_reader, front_porch_pixels as usize * BYTES_PER_PIXEL, width_pixels as usize * BYTES_PER_PIXEL, rows_per_window, burst_config, cyclic, ); Self { width: width_pixels, height: height_pixels, swapchain: Some(swapchain_writer), bounce_buffers: Some(bounce_buffers), } } } #[allow(unused)] pub async fn test_bounce_buffers( channel: esp_hal::peripherals::DMA_CH0<'static>, peripheral: esp_hal::peripherals::SPI2<'static>, display_peripherals: DisplayPeripherals, ) { error!("TEST BOUNCE BUFFERS SECTION ENTERED"); const BYTES_PER_PIXEL: usize = core::mem::size_of::(); // Assume highest burst config setting. const EXTERNAL_BURST_CONFIG: ExternalBurstConfig = ExternalBurstConfig::Size32; const ALIGNMENT_PIXELS: usize = EXTERNAL_BURST_CONFIG as usize / BYTES_PER_PIXEL; // The total number of pixels demanded by the DPI, per row. const WIDTH_TOTAL_PIXELS: usize = 368; // The total number of rows demanded by the DPI, per frame. const HEIGHT_PIXELS: usize = 960; // The number of unused pixels at the start of the row. const FRONT_PORCH_ACTUAL_PIXELS: usize = 120; // The number of actually visible pixels, per row. const WIDTH_VISIBLE_PIXELS: usize = 240; // The number of pixels not stored in a bounce buffer, per row. // This many arbitrary pixels are sent to the DPI. const FRONT_PORCH_SKIPPED_PIXELS: usize = (FRONT_PORCH_ACTUAL_PIXELS / ALIGNMENT_PIXELS) * ALIGNMENT_PIXELS; const WIDTH_STORED_PIXELS: usize = WIDTH_TOTAL_PIXELS - FRONT_PORCH_SKIPPED_PIXELS; const VISIBLE_OFFSET_IN_BUFFER_PIXELS: usize = FRONT_PORCH_ACTUAL_PIXELS - FRONT_PORCH_SKIPPED_PIXELS; const ROWS_PER_WINDOW: usize = 16; let burst_config = BurstConfig { internal_memory: InternalBurstConfig::Enabled, external_memory: EXTERNAL_BURST_CONFIG, }; let (swapchain_reader, mut swapchain_writer) = Swapchain { framebuffers: [ Box::leak(allocate_dma_buffer_in( HEIGHT_PIXELS * WIDTH_STORED_PIXELS * BYTES_PER_PIXEL, burst_config, &PSRAM_ALLOCATOR, )), Box::leak(allocate_dma_buffer_in( HEIGHT_PIXELS * WIDTH_STORED_PIXELS * BYTES_PER_PIXEL, burst_config, &PSRAM_ALLOCATOR, )), ], } .into_reader_writer(); { let write_guard = &mut swapchain_writer.write(); let buffer_src = bytemuck::cast_slice_mut::(write_guard); let colors = (0..WIDTH_VISIBLE_PIXELS as u8 / 2) .rev() .map(|val| Rgb565Pixel::from_rgb(0xFF, val * 2, 0)) .collect::>(); for (index, pixel) in buffer_src.iter_mut().enumerate() { let mut x = (index % WIDTH_STORED_PIXELS) as i16 - VISIBLE_OFFSET_IN_BUFFER_PIXELS as i16; let mut y = (index / WIDTH_STORED_PIXELS) as i16; if x < WIDTH_VISIBLE_PIXELS as i16 { x = core::cmp::min(x, WIDTH_VISIBLE_PIXELS as i16 - 1 - x); y = core::cmp::min(y, HEIGHT_PIXELS as i16 - 1 - y); let min = core::cmp::min(x, y); *pixel = colors[min as usize % colors.len()]; continue; } *pixel = Rgb565Pixel::default(); } } let mut st7701s = display_peripherals.into_display().await; let mut dma_bounce = DmaBounce::new( Global, channel, AnySpi::from(peripheral), st7701s.dpi, swapchain_reader, FRONT_PORCH_SKIPPED_PIXELS * BYTES_PER_PIXEL, WIDTH_STORED_PIXELS * BYTES_PER_PIXEL, ROWS_PER_WINDOW, burst_config, true, ); let mut bb_controller = DmaBounceController::new(dma_bounce); error!("TEST BOUNCE BUFFERS TASK LAUNCHED"); loop { bb_controller.start().await.unwrap(); st7701s.controller.sleep_off().await; Timer::after_secs(1).await; st7701s.controller.sleep_on().await; bb_controller.stop().await.unwrap(); Timer::after_secs(1).await; } }