From 8426f0b0e58710af719b1f816e12e90d7408cdf8 Mon Sep 17 00:00:00 2001 From: Victoria Fischer Date: Tue, 29 Oct 2024 01:27:20 +0100 Subject: [PATCH] src: implement first iteration of a naive smart-leds display --- Cargo.toml | 4 ++ src/main.rs | 39 ++++++++++++++++++- src/smart_leds_lib.rs | 91 +++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 133 insertions(+), 1 deletion(-) create mode 100644 src/smart_leds_lib.rs diff --git a/Cargo.toml b/Cargo.toml index 3953e8d..f1bd706 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -37,6 +37,10 @@ palette = { version = "0.7.6" } embedded-canvas = "0.3.1" embassy-executor = "0.6.0" running-average = "0.1.0" +ws2812-spi = "0.5.0" +smart-leds-trait = "0.3.0" +rgb = "0.8.50" +smart-leds = "0.4.0" [build-dependencies] embuild = "0.32.0" diff --git a/src/main.rs b/src/main.rs index ff86ab9..b4466f3 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,6 +1,5 @@ #![feature(trait_upcasting)] #![allow(arithmetic_overflow)] -use esp_idf_svc::hal::prelude::Peripherals; use ws2812_esp32_rmt_driver::lib_embedded_graphics::{LedPixelShape, Ws2812DrawTarget}; use embedded_graphics::{ prelude::*, @@ -8,6 +7,12 @@ use embedded_graphics::{ use palette::Hsv; use palette::convert::IntoColorUnclamped; +use esp_idf_svc::hal::{ + prelude::*, + spi::{config::{Config, DriverConfig}, Dma, SpiDriver, SpiBusDriver}, + gpio::AnyIOPin, +}; + mod power; mod lib8; mod render; @@ -15,13 +20,19 @@ mod task; mod time; mod geometry; mod embedded_graphics_lib; +mod smart_leds_lib; use crate::time::Periodically; use crate::geometry::{Coordinates, VirtualCoordinates}; use crate::embedded_graphics_lib::EmbeddedDisplay; +use crate::smart_leds_lib::SmartLedDisplay; use crate::render::{Surfaces, Surface, SimpleSurface, Display}; use crate::task::Task; +use ws2812_spi::Ws2812; +use smart_leds_trait::SmartLedsWrite; +use esp_idf_svc::hal::units::MegaHertz; + struct IdleTask { frame: u8, surface: T, @@ -104,6 +115,31 @@ impl DisplayInit for Ws2812DrawTarget<'_, Shape> { } } +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) + } +} + fn main() { // It is necessary to call this function once. Otherwise some patches to the runtime // implemented by esp-idf-sys might not link properly. See https://github.com/esp-rs/esp-idf-template/issues/71 @@ -113,6 +149,7 @@ fn main() { esp_idf_svc::log::EspLogger::initialize_default(); log::info!("Setting up display"); + //let mut display = SPIDisplay::new_display::(); let mut display = PonderjarTarget::new_display::(); log::info!("Creating runner"); diff --git a/src/smart_leds_lib.rs b/src/smart_leds_lib.rs new file mode 100644 index 0000000..212e3d5 --- /dev/null +++ b/src/smart_leds_lib.rs @@ -0,0 +1,91 @@ +use smart_leds_trait::SmartLedsWrite; +use running_average::RealTimeRunningAverage; + +use crate::render::{Surface, SurfacePool, Display, Surfaces}; +use crate::task::Task; +use crate::power; +use crate::time::Periodically; +use crate::lib8::RGB8; +use crate::geometry::*; +use crate::power::AsMilliwatts; + +use smart_leds::brightness; + +use std::io; + +use rgb::Rgb; + +pub struct SmartLedDisplay>, S: Surface> { + surfaces : SurfacePool, + target: T, + fps: RealTimeRunningAverage, + frame: u32, + fps_display: Periodically, + pixbuf: [T::Color; 255], + total_mw: u32, + max_mw: u32 +} + +impl>, S: Surface> SmartLedDisplay { + pub fn new(target: T, max_mw: u32) -> Self { + SmartLedDisplay { + surfaces: SurfacePool::new(), + target: target, + max_mw: max_mw, + total_mw: 0, + fps: RealTimeRunningAverage::default(), + frame: 0, + fps_display: Periodically::new_every_n_seconds(5), + pixbuf: [Rgb::new(0, 0, 0); 255] + } + } +} + +impl>, S: Surface> Task for SmartLedDisplay { + fn name(&self) -> &'static str { "Renderer" } + + fn tick(&mut self) { + self.start_frame(); + self.render_frame(); + self.end_frame(); + } +} + +impl Surfaces for SmartLedDisplay +where +T: SmartLedsWrite>, +S: Surface { + fn new_surface(&mut self) -> Result { + self.surfaces.new_surface() + } +} + +impl>, S: Surface> Display for SmartLedDisplay { + fn start_frame(&mut self) { + self.frame = self.frame.wrapping_add(1); + self.total_mw = 0; + } + + fn end_frame(&mut self) { + self.fps.insert(1); + let b = power::brightness_for_mw(self.total_mw, 255, self.max_mw); + self.fps_display.run(|| { + log::info!("FPS: {} frame={} brightness={} mw={}", self.fps.measurement(), self.frame, b, self.total_mw); + }); + self.target.write(brightness(self.pixbuf.iter().cloned(), b)); + } + + fn render_frame(&mut self) { + for x in (0..self.pixbuf.len()) { + let virtCoords = VirtualCoordinates::new(x as u8, 0); + let mut pixel = RGB8::new(0, 0, 0); + for surface in self.surfaces.iter() { + surface.with_shader(|shader| { + pixel = shader.draw(virtCoords.clone()); + }) + } + self.total_mw += pixel.as_milliwatts(); + self.pixbuf[x] = Rgb::new(pixel.red, pixel.green, pixel.blue); + }; + } +}