use smart_leds_trait::SmartLedsWrite; use crate::lib8::Rgb8Blend; use crate::render::{Surface, SurfacePool, Display, Surfaces}; use crate::power::{brightness_for_mw, AsMilliwatts}; use crate::geometry::*; use crate::mappings::*; use std::fmt::{Debug, Formatter}; use smart_leds::brightness; use std::io; use rgb::Rgb; pub struct SmartLedDisplay>, S: Surface, const PIXEL_NUM: usize> { surfaces : Option>, pixmap: StrideMapping, target: T, pixbuf: [T::Color; PIXEL_NUM], max_mw: u32 } impl>, S: Surface, const PIXEL_NUM: usize> Debug for SmartLedDisplay { fn fmt(&self, f: &mut Formatter) -> Result<(), std::fmt::Error> { f.debug_struct("SmartLedDisplay") .field("total_mw", &self.pixbuf.as_milliwatts()) .field("surfaces", &self.surfaces) .finish() } } impl>, S: Surface, const PIXEL_NUM: usize> SmartLedDisplay { pub fn new(target: T, max_mw: u32) -> Self { SmartLedDisplay { pixbuf: [Rgb::new(0, 0, 0); PIXEL_NUM], surfaces: Some(SurfacePool::new()), target, max_mw, pixmap: StrideMapping::new() } } } impl>, S: Surface, const PIXEL_NUM: usize> AsMilliwatts for SmartLedDisplay { fn as_milliwatts(&self) -> u32 { self.pixbuf.as_milliwatts() } } impl Surfaces for SmartLedDisplay where T: SmartLedsWrite>, S: Surface { fn new_surface(&mut self, area: &Rectangle) -> Result { if let Some(ref mut s) = self.surfaces { s.new_surface(area) } else { panic!("Could not grab surface list") } } } impl>, S: Surface, const PIXEL_NUM: usize> Display for SmartLedDisplay { fn start_frame(&mut self) { self.pixbuf.fill(Rgb::new(0, 0, 0)); } 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(), b).map(|x| { rgb::Bgr::new(x.r, x.g, x.b)})) { panic!("Could not write frame"); } } 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; } self.pixbuf[idx] = self.pixbuf[idx].saturating_add(shader.draw(&virt_coords)); } }) } self.surfaces = Some(surfaces); } } #[cfg(feature="rmt")] pub mod rmt { use esp_idf_svc::hal::prelude::Peripherals; use ws2812_esp32_rmt_driver::lib_smart_leds::Ws2812Esp32Rmt; use crate::render::{Display, Surface}; use crate::platform::smart_leds_lib::SmartLedDisplay; use crate::platform::DisplayInit; impl DisplayInit for Ws2812Esp32Rmt<'_> { fn new_display() -> impl Display { let peripherals = Peripherals::take().unwrap(); let led_pin = peripherals.pins.gpio14; 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 target = Self::new(channel, led_pin).unwrap(); return SmartLedDisplay::::new(target, MAX_POWER_MW); } } } #[cfg(feature="spi")] pub mod spi { use ws2812_spi::Ws2812; use crate::render::{Display, Surface}; use crate::task::Task; use crate::platform::smart_leds_lib::SmartLedDisplay; use crate::DisplayInit; use esp_idf_svc::hal::{ prelude::*, gpio::AnyIOPin, spi::{ config::{Config, DriverConfig}, Dma, SpiBusDriver, SpiDriver, } }; pub struct SPIDisplay {} impl DisplayInit for SPIDisplay { fn new_display() -> impl Display + Task { let peripherals = Peripherals::take().unwrap(); let driver = SpiDriver::new_without_sclk( peripherals.spi2, peripherals.pins.gpio14, Option::::None, &DriverConfig::new().dma(Dma::Auto(512)) ).unwrap(); let cfg = Config::new().baudrate(3_200.kHz().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) } } }