use alloc::{alloc::Global, boxed::Box, vec::Vec}; use embassy_time::Timer; use esp_hal::{ Blocking, dma::{BurstConfig, ExternalBurstConfig, InternalBurstConfig}, lcd_cam::lcd::dpi::Dpi, 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; use crate::{DmaBounceController, PSRAM_ALLOCATOR, peripherals::st7701s::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)] async fn test_bounce_buffers( channel: esp_hal::peripherals::DMA_CH0<'static>, peripheral: esp_hal::peripherals::SPI2<'static>, mut st7701s: St7701s<'static, Blocking>, ) { 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 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; } }