From 2ad22a26328bd7829fc6ddceea3d9eaf003be69a Mon Sep 17 00:00:00 2001 From: Victoria Fischer Date: Sat, 14 Mar 2026 12:02:15 +0100 Subject: [PATCH] display: implement power scaling into the display APIs --- src/events.rs | 4 ++++ src/graphics/display.rs | 23 ++++++++++++++++++----- src/tasks/motion.rs | 3 +++ src/tasks/render.rs | 5 +---- 4 files changed, 26 insertions(+), 9 deletions(-) diff --git a/src/events.rs b/src/events.rs index 7ea7b1d..8cc5e93 100644 --- a/src/events.rs +++ b/src/events.rs @@ -3,6 +3,7 @@ use embassy_time::Duration; use enum_map::Enum; use enumset::EnumSetType; use figments::liber8tion::interpolate::Fract8; +use figments_render::power::Milliwatts; use nalgebra::{Vector2, Vector3}; use crate::ego::engine::MotionState; @@ -38,6 +39,8 @@ pub enum Measurement { GPS(Option>), // Accelerometer values in body frame where x=forwards IMU { accel: Vector3, gyro: Vector3 }, + // Power status + ExternalPower(Milliwatts), // Hardware status updates SensorHardwareStatus(SensorSource, SensorState), @@ -73,6 +76,7 @@ pub enum SensorSource { // Real hardware IMU, GPS, + ExternalPower, // Connectivity related Wifi, diff --git a/src/graphics/display.rs b/src/graphics/display.rs index d2fcb15..af903b9 100644 --- a/src/graphics/display.rs +++ b/src/graphics/display.rs @@ -1,10 +1,11 @@ use embassy_sync::{blocking_mutex::raw::CriticalSectionRawMutex, signal::Signal, watch::{Receiver, Watch}}; use figments::{liber8tion::interpolate::Fract8, prelude::*}; +use portable_atomic::AtomicU32; use core::{fmt::Debug, ops::Mul, sync::atomic::{AtomicBool, AtomicU8}}; use alloc::sync::Arc; use figments_render::{ - gamma::{GammaCurve, WithGamma}, output::{Brightness, GammaCorrected, Output, OutputAsync}, power::AsMilliwatts, smart_leds::PowerManagedWriter + gamma::{GammaCurve, WithGamma}, output::{Brightness, GammaCorrected, Output, OutputAsync}, power::{AsMilliwatts, Milliwatts}, smart_leds::PowerManagedWriter }; use smart_leds::{SmartLedsWrite, SmartLedsWriteAsync}; @@ -23,10 +24,10 @@ impl GammaCorrected for BikeOutput { } impl BikeOutput { - pub fn new(target: T, max_mw: u32, controls: DisplayControls) -> Self { + pub fn new(target: T, controls: DisplayControls) -> Self { Self { pixbuf: [Default::default(); NUM_PIXELS], - writer: PowerManagedWriter::new(target, max_mw), + writer: PowerManagedWriter::new(target, controls.max_power()), controls } } @@ -44,6 +45,7 @@ impl<'a, T: SmartLedsWrite + 'a> Output<'a, SegmentSpace> for BikeOutput Result<(), Self::Error> { self.writer.controls().set_brightness(self.controls.brightness()); self.writer.controls().set_on(self.controls.is_on()); + self.writer.controls().set_max_power(self.controls.max_power()); critical_section::with(|_| { self.writer.write(&self.pixbuf) }) @@ -165,7 +167,8 @@ pub const LOW_POWER_FPS: u8 = 16; struct ControlData { on: AtomicBool, brightness: AtomicU8, - fps: AtomicU8 + fps: AtomicU8, + max_power_mw: AtomicU32 } impl Default for ControlData { @@ -173,7 +176,8 @@ impl Default for ControlData { Self { on: AtomicBool::new(true), brightness: AtomicU8::new(255), - fps: AtomicU8::new(DEFAULT_FPS) + fps: AtomicU8::new(DEFAULT_FPS), + max_power_mw: AtomicU32::new(0) } } } @@ -229,6 +233,14 @@ impl DisplayControls { while !self.render_run_receiver.changed().await { log::info!("wait for render run") } log::trace!("render says run!"); } + + pub fn set_max_power(&mut self, mw: Milliwatts) { + self.data.max_power_mw.store(mw.0, core::sync::atomic::Ordering::Relaxed); + } + + pub fn max_power(&self) -> Milliwatts { + Milliwatts(self.data.max_power_mw.load(core::sync::atomic::Ordering::Relaxed)) + } } impl GammaCorrected for DisplayControls { @@ -256,6 +268,7 @@ impl core::fmt::Debug for DisplayControls { .field("brightness", &self.data.brightness) .field("fps", &self.data.fps) .field("render_pause_signaled", &self.display_is_on.signaled()) + .field("max_power_mw", &self.data.max_power_mw) .finish() } } diff --git a/src/tasks/motion.rs b/src/tasks/motion.rs index d83e262..c75415f 100644 --- a/src/tasks/motion.rs +++ b/src/tasks/motion.rs @@ -31,6 +31,9 @@ pub async fn motion_task(src: DynamicReceiver<'static, Measurement>, prediction_ warn!("Sensor {source:?} reports {state:?}!"); prediction_sink.publish(Prediction::SensorStatus(source, state)).with_timeout(TIMEOUT).await.expect("Could not update sensor status in time. Is the prediction bus stalled?"); }, + Measurement::ExternalPower(mw) => { + info!("Got external power change to {mw:?}"); + }, Measurement::SimulationProgress(source, duration, pct) => debug!("{source:?} simulation time: {} {} / 255", duration.as_secs(), pct), Measurement::Annotation => () } diff --git a/src/tasks/render.rs b/src/tasks/render.rs index 627be30..6a92d8f 100644 --- a/src/tasks/render.rs +++ b/src/tasks/render.rs @@ -12,9 +12,6 @@ use log::*; use crate::graphics::display::NUM_PIXELS; use crate::{graphics::display::{BikeOutput, DisplayControls, Uniforms}, tasks::ui::UiSurfacePool}; -const POWER_MA : u32 = 300; -const POWER_VOLTS : u32 = 5; -const MAX_POWER_MW : u32 = POWER_VOLTS * POWER_MA; static SPI_BUFFERS: static_cell::ConstStaticCell> = static_cell::ConstStaticCell::new(DmaBuffers::new(0)); #[embassy_executor::task] @@ -40,7 +37,7 @@ pub async fn render(spi: AnySpi<'static>, dma: AnyGdmaChannel<'static>, gpio: An ..Uniforms::default() }; - let mut output = BikeOutput::new(target, MAX_POWER_MW, controls.clone()); + let mut output = BikeOutput::new(target, controls.clone()); output.set_gamma(GammaCurve::new(1.3)); info!("Rendering started! {}ms since boot", Instant::now().as_millis());