acid/firmware/acid-firmware/src/dpi.rs

257 lines
9.5 KiB
Rust
Raw Normal View History

2026-02-27 23:54:29 +01:00
use alloc::{alloc::Global, boxed::Box, vec::Vec};
use embassy_time::Timer;
2026-02-14 20:03:32 +01:00
use esp_hal::{
2026-02-27 23:54:29 +01:00
Blocking,
dma::{BurstConfig, ExternalBurstConfig, InternalBurstConfig},
2026-02-28 01:34:59 +01:00
gpio::{Flex, Level, Output, OutputConfig},
lcd_cam::{LcdCam, lcd::dpi::Dpi},
ledc::{self, LSGlobalClkSource, Ledc, LowSpeed},
2026-02-27 23:54:29 +01:00
spi::master::AnySpi,
2026-02-14 20:03:32 +01:00
};
use esp_hal_bounce_buffers::{DmaBounce, Swapchain, SwapchainWriter, allocate_dma_buffer_in};
2026-02-27 23:54:29 +01:00
use i_slint_core::software_renderer::{Rgb565Pixel, TargetPixel};
2026-02-28 01:34:59 +01:00
use log::{error, info};
2026-02-14 20:03:32 +01:00
2026-02-27 23:54:29 +01:00
use crate::{DmaBounceController, PSRAM_ALLOCATOR, peripherals::st7701s::St7701s};
2026-02-14 20:03:32 +01:00
2026-02-28 01:34:59 +01:00
#[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::<LowSpeed>(ledc::timer::Number::Timer0);
let bl_channel = ledc.channel::<LowSpeed>(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.
2026-02-22 00:59:01 +01:00
pub struct Framebuffer {
pub width: u32,
pub height: u32,
pub swapchain: Option<SwapchainWriter>,
pub bounce_buffers: Option<DmaBounce>,
2026-02-22 00:59:01 +01:00
}
2026-02-14 20:03:32 +01:00
impl Framebuffer {
2026-02-22 00:59:01 +01:00
pub fn new(
2026-02-27 23:54:29 +01:00
channel: esp_hal::peripherals::DMA_CH0<'static>,
2026-02-22 00:59:01 +01:00
peripheral_src: AnySpi<'static>,
peripheral_dst: Dpi<'static, Blocking>,
burst_config: BurstConfig,
front_porch_pixels: u32,
width_pixels: u32,
height_pixels: u32,
2026-02-22 00:59:01 +01:00
rows_per_window: usize,
cyclic: bool,
) -> Self {
const BYTES_PER_PIXEL: usize = core::mem::size_of::<u16>();
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();
2026-02-22 00:59:01 +01:00
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,
2026-02-22 00:59:01 +01:00
burst_config,
cyclic,
2026-02-14 20:03:32 +01:00
);
Self {
width: width_pixels,
height: height_pixels,
swapchain: Some(swapchain_writer),
bounce_buffers: Some(bounce_buffers),
2026-02-14 20:03:32 +01:00
}
}
}
2026-02-27 23:54:29 +01:00
#[allow(unused)]
2026-02-28 01:34:59 +01:00
pub async fn test_bounce_buffers(
2026-02-27 23:54:29 +01:00
channel: esp_hal::peripherals::DMA_CH0<'static>,
peripheral: esp_hal::peripherals::SPI2<'static>,
2026-02-28 01:34:59 +01:00
display_peripherals: DisplayPeripherals,
2026-02-27 23:54:29 +01:00
) {
error!("TEST BOUNCE BUFFERS SECTION ENTERED");
const BYTES_PER_PIXEL: usize = core::mem::size_of::<u16>();
// 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::<u8, Rgb565Pixel>(write_guard);
let colors = (0..WIDTH_VISIBLE_PIXELS as u8 / 2)
.rev()
.map(|val| Rgb565Pixel::from_rgb(0xFF, val * 2, 0))
.collect::<Vec<_>>();
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();
}
}
2026-02-28 01:34:59 +01:00
let mut st7701s = display_peripherals.into_display().await;
2026-02-27 23:54:29 +01:00
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;
}
}