From 04f5575ba64dbce5b6470b9ce2389eb81c84d04e Mon Sep 17 00:00:00 2001 From: Victoria Fischer Date: Sun, 24 Nov 2024 23:53:43 +0100 Subject: [PATCH] platform: smart_leds: simplify trait hierarchy and push pixel type conversion even closer towards the hardware --- src/main.rs | 2 +- src/platform/smart_leds_lib.rs | 204 ++++++++++++++++++++++----------- 2 files changed, 138 insertions(+), 68 deletions(-) diff --git a/src/main.rs b/src/main.rs index 482e6f7..73da8d9 100644 --- a/src/main.rs +++ b/src/main.rs @@ -19,7 +19,7 @@ use crate::platform::embedded_graphics_lib::PonderjarTarget as DisplayType; #[cfg(feature="smart-leds")] #[cfg(feature="rmt")] -use ws2812_esp32_rmt_driver::lib_smart_leds::Ws2812Esp32Rmt as DisplayType; +use crate::platform::smart_leds_lib::rmt::FastWs2812Esp32Rmt as DisplayType; #[cfg(feature="smart-leds")] #[cfg(feature="spi")] diff --git a/src/platform/smart_leds_lib.rs b/src/platform/smart_leds_lib.rs index 2bb4f83..9f8a04b 100644 --- a/src/platform/smart_leds_lib.rs +++ b/src/platform/smart_leds_lib.rs @@ -1,6 +1,7 @@ use smart_leds_trait::SmartLedsWrite; use crate::lib8::Rgb8Blend; +use crate::lib8::interpolate::Fract8Ops; use crate::render::{Framed, Surface, Display, Surfaces}; use crate::buffers::SurfacePool; use crate::power::{brightness_for_mw, AsMilliwatts}; @@ -8,19 +9,18 @@ use crate::geometry::*; use crate::mappings::*; use std::fmt::{Debug, Formatter}; -use smart_leds::brightness; - use std::io; use rgb::Rgb; use std::ops::IndexMut; -pub trait HardwarePixel: Rgb8Blend + Clone + Copy + AsMilliwatts + Default + From> {} -impl HardwarePixel for T where T: Rgb8Blend + Clone + Copy + AsMilliwatts + Default + From> {} +pub trait HardwarePixel: Send + Sync + Rgb8Blend + Copy + AsMilliwatts + Default + From> + Fract8Ops {} +impl HardwarePixel for T where T: Send + Sync + Rgb8Blend + Copy + AsMilliwatts + Default + From> + Fract8Ops {} pub trait Pixbuf: AsMilliwatts + IndexMut { fn new() -> Self; fn blank(&mut self); + fn iter_with_brightness(&self, brightness: u8) -> impl Iterator + Send; } impl Pixbuf for [T; PIXEL_NUM] { @@ -31,22 +31,22 @@ impl Pixbuf for [T; PIXEL_NUM] { fn blank(&mut self) { self.fill(T::default()) } + + fn iter_with_brightness(&self, brightness: u8) -> impl Iterator + Send { + self.iter().map(move |x| { x.scale8(brightness)}) + } } -pub struct SmartLedDisplay where -T::Color: HardwarePixel, -[T::Color; PIXEL_NUM]: Pixbuf { +struct SmartLedDisplay> { surfaces : Option>, pixmap: StrideMapping, target: T, - pixbuf: [T::Color; PIXEL_NUM], + pixbuf: P, max_mw: u32, frame: usize } -impl Debug for SmartLedDisplay where -T::Color: HardwarePixel, -[T::Color; PIXEL_NUM]: Pixbuf { +impl> Debug for SmartLedDisplay { fn fmt(&self, f: &mut Formatter) -> Result<(), std::fmt::Error> { f.debug_struct("SmartLedDisplay") .field("total_mw", &self.pixbuf.as_milliwatts()) @@ -55,36 +55,32 @@ T::Color: HardwarePixel, } } -impl SmartLedDisplay where -T::Color: HardwarePixel, -[T::Color; PIXEL_NUM]: Pixbuf { - pub fn new(target: T, max_mw: u32) -> Self { +impl> SmartLedDisplay { + fn new(target: T, max_mw: u32, pixmap: StrideMapping, pixbuf: P) -> Self { SmartLedDisplay { - pixbuf: <[T::Color; PIXEL_NUM]>::new(), + pixbuf, surfaces: Some(SurfacePool::new()), target, max_mw, - pixmap: StrideMapping::new(), + pixmap, frame: 0 } } } -impl AsMilliwatts for SmartLedDisplay where -T: SmartLedsWrite, +impl AsMilliwatts for SmartLedDisplay where +T: FastWrite, S: Surface, -T::Color: HardwarePixel, -[T::Color; PIXEL_NUM]: Pixbuf { +P: Pixbuf { fn as_milliwatts(&self) -> u32 { self.pixbuf.as_milliwatts() } } -impl Surfaces for SmartLedDisplay where -T: SmartLedsWrite, +impl Surfaces for SmartLedDisplay where +T: FastWrite, S: Surface, -T::Color: HardwarePixel, -[T::Color; PIXEL_NUM]: Pixbuf { +P: Pixbuf { fn new_surface(&mut self, area: &Rectangle) -> Result { if let Some(ref mut s) = self.surfaces { s.new_surface(area) @@ -94,38 +90,43 @@ T::Color: HardwarePixel, } } -impl Display for SmartLedDisplay where -T: SmartLedsWrite, +impl Display for SmartLedDisplay where +T: FastWrite, S: Surface, -T::Color: HardwarePixel, -[T::Color; PIXEL_NUM]: Pixbuf, -Rgb: From -{ +P: Pixbuf { fn render_frame(&mut self) { let surfaces = self.surfaces.take().unwrap(); for surface in surfaces.iter() { let rect = surface.rect().clone(); let mut sel = self.pixmap.select(&rect); - surface.with_shader(|shader| { - while let Some((virt_coords, phys_coords)) = sel.next() { - let idx = phys_coords.x as usize; - if idx >= PIXEL_NUM { - continue; + let opacity = surface.opacity(); + if opacity > 0 { + surface.with_shader(|shader| { + while let Some((virt_coords, phys_coords)) = sel.next() { + let idx = self.pixmap.to_idx(&phys_coords); + self.pixbuf[idx] = self.pixbuf[idx].blend8(shader.draw(&virt_coords, self.frame).into(), opacity); } - self.pixbuf[idx] = self.pixbuf[idx].saturating_add(shader.draw(&virt_coords, self.frame)); - } - }) + }) + } } self.surfaces = Some(surfaces); } } -impl Framed for SmartLedDisplay where -T: SmartLedsWrite, +trait FastWrite { + type Target: SmartLedsWrite; + type Color: HardwarePixel; + type Error; + fn fast_write(&mut self, iterator: T) -> Result<(), Self::Error> where + T: IntoIterator, + I: Into, + ::IntoIter: Send; +} + +impl Framed for SmartLedDisplay where +T: FastWrite, S: Surface, -T::Color: HardwarePixel, -[T::Color; PIXEL_NUM]: Pixbuf, -Rgb: From { +P: Pixbuf { fn start_frame(&mut self) { self.pixbuf.blank(); @@ -133,8 +134,8 @@ Rgb: From { fn end_frame(&mut self) { let b = brightness_for_mw(self.pixbuf.as_milliwatts(), 255, self.max_mw); - if let Err(_) = self.target.write(brightness(self.pixbuf.iter().cloned().map(|x| { x.into() }), b)) { - panic!("Could not write frame"); + if let Err(_) = self.target.fast_write(self.pixbuf.iter_with_brightness(b)) { + panic!("Could not write frame!"); } self.frame += 1; } @@ -143,43 +144,90 @@ Rgb: From { #[cfg(feature="rmt")] pub mod rmt { use esp_idf_svc::hal::prelude::Peripherals; - use ws2812_esp32_rmt_driver::lib_smart_leds::LedPixelEsp32Rmt; - use ws2812_esp32_rmt_driver::driver::color::LedPixelColor; - - use crate::render::{Display, Surface}; - use crate::platform::smart_leds_lib::{Pixbuf, SmartLedDisplay, HardwarePixel}; - use crate::platform::DisplayInit; + use ws2812_esp32_rmt_driver::driver::color::{LedPixelColor, LedPixelColorGrb24}; + use smart_leds::SmartLedsWrite; use rgb::Rgb; + use ws2812_esp32_rmt_driver::LedPixelEsp32Rmt; - impl DisplayInit for LedPixelEsp32Rmt<'_, CSmart, CDev> where - CSmart: HardwarePixel, - [CSmart; 310]: Pixbuf, - CDev: LedPixelColor + From, - Rgb: From - { + use crate::mappings::StrideMapping; + use crate::render::{Display, Surface}; + use crate::platform::DisplayInit; + + use super::{Pixbuf, FastWrite, SmartLedDisplay}; + + use crate::lib8::Rgb8Blend; + use crate::lib8::interpolate::{Fract8, Fract8Ops}; + use crate::power::AsMilliwatts; + + pub type FastWs2812Esp32Rmt<'a> = LedPixelEsp32Rmt<'a, Rgb, LedPixelColorGrb24>; + + impl Fract8Ops for LedPixelColorGrb24 { + fn blend8(self, other: Self, scale: Fract8) -> Self { + self + } + + fn scale8(self, scale: Fract8) -> Self { + self + } + } + + impl AsMilliwatts for LedPixelColorGrb24 { + fn as_milliwatts(&self) -> u32 { + Rgb::new(self.r(), self.g(), self.b()).as_milliwatts() + } + } + + impl Rgb8Blend for LedPixelColorGrb24 { + fn saturating_add>(self, b: T) -> Self where Self: Sized { + let s = b.into(); + LedPixelColorGrb24::new_with_rgb(s.r(), s.g(), s.b()) + } + } + + impl FastWrite for FastWs2812Esp32Rmt<'_> { + type Color = ::Color; + type Error = ::Error; + type Target = Self; + fn fast_write(&mut self, iterator: T) -> Result<(), Self::Error> where + T: IntoIterator, + I: Into, + ::IntoIter: Send { + self.write_nocopy(iterator) + } + } + + impl DisplayInit for FastWs2812Esp32Rmt<'_> { fn new_display() -> impl Display { let peripherals = Peripherals::take().unwrap(); let led_pin = peripherals.pins.gpio14; + //let led_pin = peripherals.pins.gpio5; let channel = peripherals.rmt.channel0; const POWER_VOLTS : u32 = 5; const POWER_MA : u32 = 500; const MAX_POWER_MW : u32 = POWER_VOLTS * POWER_MA; + //let pixbuf: [Rgb; 310] = Pixbuf::new(); + let pixbuf: [::Color; 310] = Pixbuf::new(); + let pixmap = StrideMapping::new_jar(); + + assert!(pixmap.pixel_count <= pixbuf.len(), "map needs {} pixels, I only have PIXEL_NUM={}", pixmap.pixel_count, pixbuf.len()); + let target = Self::new(channel, led_pin).unwrap(); - return SmartLedDisplay::::new(target, MAX_POWER_MW); + return SmartLedDisplay::new(target, MAX_POWER_MW, pixmap, pixbuf); } } } #[cfg(feature="spi")] pub mod spi { - use ws2812_spi::Ws2812; + use smart_leds::SmartLedsWrite; + use ws2812_spi::prerendered::Ws2812; use crate::render::{Display, Surface}; - use crate::task::Task; use crate::platform::smart_leds_lib::SmartLedDisplay; use crate::DisplayInit; + use super::FastWrite; use esp_idf_svc::hal::{ prelude::*, @@ -192,27 +240,49 @@ pub mod spi { } }; + impl<'a> FastWrite for Ws2812<'_, SpiBusDriver<'a, SpiDriver<'a>>> { + type Color = ::Color; + type Error = ::Error; + type Target = Self; + + fn fast_write(&mut self, iterator: T) -> Result<(), Self::Error> where + T: IntoIterator, + I: Into, + ::IntoIter: Send { + let resp = self.write(iterator); + if let Err(e) = resp { + panic!("Could not write SPI frame! {:?}", e) + } else { + resp + } + } + } + + static mut STATIC_BUFFER: [u8; 400 * 12] = [0; 400 * 12]; + pub struct SPIDisplay {} impl DisplayInit for SPIDisplay { - fn new_display() -> impl Display + Task { + fn new_display() -> impl Display { let peripherals = Peripherals::take().unwrap(); let driver = SpiDriver::new_without_sclk( peripherals.spi2, - peripherals.pins.gpio14, + peripherals.pins.gpio5, Option::::None, - &DriverConfig::new().dma(Dma::Auto(512)) + &DriverConfig::new().dma(Dma::Auto(4092)) ).unwrap(); - let cfg = Config::new().baudrate(3_200.kHz().into()); + let cfg = Config::new().baudrate(3.MHz().into()); let spi = SpiBusDriver::new(driver, &cfg).unwrap(); const POWER_VOLTS : u32 = 5; const POWER_MA : u32 = 500; const MAX_POWER_MW : u32 = POWER_VOLTS * POWER_MA; - let target = Ws2812::new(spi); - return SmartLedDisplay::new(target, MAX_POWER_MW) + unsafe { + let target = Ws2812::new(spi, &mut STATIC_BUFFER); + return SmartLedDisplay::>>, S, 310>::new(target, MAX_POWER_MW); + } } } }