animation

This commit is contained in:
Adam Gausmann 2025-10-31 08:37:53 -05:00
parent b9363f1c7d
commit 8e96e70cfd
4 changed files with 125 additions and 24 deletions

2
Cargo.lock generated
View file

@ -763,9 +763,11 @@ dependencies = [
"embedded-io", "embedded-io",
"embedded-io-async", "embedded-io-async",
"embedded-storage", "embedded-storage",
"fixed",
"heapless", "heapless",
"mipidsi", "mipidsi",
"rgb", "rgb",
"smart-leds",
"tinytga", "tinytga",
] ]

View file

@ -48,3 +48,5 @@ embassy-futures = "0.1.2"
heapless = "0.8.0" heapless = "0.8.0"
rgb = "0.8.52" rgb = "0.8.52"
tinytga = "0.5.0" tinytga = "0.5.0"
smart-leds = "0.4.0"
fixed = "1.29.0"

View file

@ -1,4 +1,7 @@
use embedded_graphics::{image::Image, pixelcolor::Rgb888, prelude::*}; use embassy_time::Duration;
use embedded_graphics::{image::Image, pixelcolor::Rgb888, prelude::*, primitives::Rectangle};
use fixed::types::U0F8;
use smart_leds::hsv::{Hsv, hsv2rgb};
use tinytga::Tga; use tinytga::Tga;
pub struct Hal { pub struct Hal {
@ -15,7 +18,6 @@ impl Hal {
impl Drawable for Hal { impl Drawable for Hal {
type Color = Rgb888; type Color = Rgb888;
type Output = (); type Output = ();
fn draw<D>(&self, target: &mut D) -> Result<Self::Output, D::Error> fn draw<D>(&self, target: &mut D) -> Result<Self::Output, D::Error>
@ -40,7 +42,6 @@ impl HappyEyes {
impl Drawable for HappyEyes { impl Drawable for HappyEyes {
type Color = Rgb888; type Color = Rgb888;
type Output = (); type Output = ();
fn draw<D>(&self, target: &mut D) -> Result<Self::Output, D::Error> fn draw<D>(&self, target: &mut D) -> Result<Self::Output, D::Error>
@ -50,3 +51,86 @@ impl Drawable for HappyEyes {
self.image_data.draw(target) self.image_data.draw(target)
} }
} }
pub struct Rainbow {
pub rate: Duration,
pub width: u32,
pub elapsed: Duration,
}
impl Rainbow {
fn map(&self, x: i32) -> Rgb888 {
let hue = ((x as f32 / self.width as f32
+ self.elapsed.as_micros() as f32 / self.rate.as_micros() as f32)
* 256.0) as u32 as u8;
let color = hsv2rgb(Hsv {
hue,
sat: 255,
val: 255,
});
Rgb888::new(color.r, color.g, color.b)
}
}
impl Drawable for Rainbow {
type Color = Rgb888;
type Output = ();
fn draw<D>(&self, target: &mut D) -> Result<Self::Output, D::Error>
where
D: DrawTarget<Color = Self::Color>,
{
let bb = target.bounding_box();
for dx in 0..bb.size.width {
let color = self.map(dx as i32);
target.fill_solid(
&Rectangle::new(
bb.top_left + Point::new(dx as i32, 0),
Size::new(1, bb.size.height),
),
color,
)?;
}
Ok(())
}
}
pub struct RainbowFilter<'a, D> {
pub inner_target: &'a mut D,
pub rainbow: &'a Rainbow,
}
impl<'a, D: DrawTarget<Color = Rgb888>> DrawTarget for RainbowFilter<'a, D> {
type Color = D::Color;
type Error = D::Error;
fn draw_iter<I>(&mut self, pixels: I) -> Result<(), Self::Error>
where
I: IntoIterator<Item = Pixel<Self::Color>>,
{
self.inner_target.draw_iter(
pixels
.into_iter()
.map(|Pixel(point, color)| Pixel(point, mix(color, self.rainbow.map(point.x)))),
)
}
}
impl<'a, D: Dimensions> Dimensions for RainbowFilter<'a, D> {
fn bounding_box(&self) -> Rectangle {
self.inner_target.bounding_box()
}
}
fn mix_u8(a: u8, b: u8) -> u8 {
(a as u16 * b as u16 / 255) as u8
}
fn mix(a: Rgb888, b: Rgb888) -> Rgb888 {
Rgb888::new(
mix_u8(a.r(), b.r()),
mix_u8(a.g(), b.g()),
mix_u8(a.b(), b.b()),
)
}

View file

@ -16,7 +16,7 @@ use embassy_rp::pio_programs::ws2812::{PioWs2812, PioWs2812Program};
use embassy_rp::pwm::{self, Pwm}; use embassy_rp::pwm::{self, Pwm};
use embassy_rp::rom_data::reset_to_usb_boot; use embassy_rp::rom_data::reset_to_usb_boot;
use embassy_rp::spi::{self, Blocking, Spi}; use embassy_rp::spi::{self, Blocking, Spi};
use embassy_time::Delay; use embassy_time::{Delay, Duration, Instant, Ticker};
use embedded_graphics::image::{Image, ImageRawLE}; use embedded_graphics::image::{Image, ImageRawLE};
use embedded_graphics::mono_font::MonoTextStyle; use embedded_graphics::mono_font::MonoTextStyle;
use embedded_graphics::mono_font::ascii::FONT_10X20; use embedded_graphics::mono_font::ascii::FONT_10X20;
@ -30,7 +30,7 @@ use mipidsi::interface::SpiInterface;
use mipidsi::models::ST7789; use mipidsi::models::ST7789;
use mipidsi::options::Orientation; use mipidsi::options::Orientation;
use crate::art::{Hal, HappyEyes}; use crate::art::{Hal, HappyEyes, Rainbow, RainbowFilter};
use crate::led_matrix::{LedMatrix, Serpentine}; use crate::led_matrix::{LedMatrix, Serpentine};
use defmt_rtt as _; use defmt_rtt as _;
@ -116,8 +116,7 @@ async fn main(spawner: Spawner) -> ! {
let mut led_pio = Pio::new(p.PIO0, Irqs); let mut led_pio = Pio::new(p.PIO0, Irqs);
let led_program = PioWs2812Program::new(&mut led_pio.common); let led_program = PioWs2812Program::new(&mut led_pio.common);
let led_surface = LedMatrix::new(
let mut led_surface = LedMatrix::new(
Serpentine { Serpentine {
size: Size::new(40, 8), size: Size::new(40, 8),
}, },
@ -129,23 +128,7 @@ async fn main(spawner: Spawner) -> ! {
&led_program, &led_program,
), ),
); );
led_surface.clear(Rgb888::BLACK).ok(); spawner.spawn(led_task(led_surface)).ok();
/*
Rectangle::new(Point::zero(), led_surface.size())
.into_styled(
PrimitiveStyleBuilder::new()
.stroke_color(Rgb888::RED)
.stroke_width(1)
.fill_color(Rgb888::CSS_DARK_BLUE)
.build(),
)
.draw(&mut led_surface)
.ok();
*/
// let hal = Hal::new();
let eyes = HappyEyes::new();
eyes.draw(&mut led_surface).ok();
led_surface.sync().await;
let mut render = |f_keys: [bool; 4]| { let mut render = |f_keys: [bool; 4]| {
display.clear(Rgb565::BLACK).ok(); display.clear(Rgb565::BLACK).ok();
@ -186,6 +169,36 @@ async fn main(spawner: Spawner) -> ! {
} }
} }
#[embassy_executor::task]
async fn led_task(mut led_surface: LedMatrix<'static>) {
let hal = Hal::new();
let eyes = HappyEyes::new();
let mut rainbow = Rainbow {
// rate: Duration::from_secs(10),
// width: 300,
rate: Duration::from_secs(1),
width: 80,
elapsed: Duration::from_secs(0),
};
let mut ticker = Ticker::every(Duration::from_hz(30));
let start = Instant::now();
loop {
let elapsed = start.elapsed();
rainbow.elapsed = elapsed;
led_surface.clear(Rgb888::BLACK).ok();
eyes.draw(&mut RainbowFilter {
inner_target: &mut led_surface,
rainbow: &rainbow,
})
.ok();
led_surface.sync().await;
ticker.next().await;
}
}
#[embassy_executor::task] #[embassy_executor::task]
async fn dfu_button(mut btn_y: Input<'static>) { async fn dfu_button(mut btn_y: Input<'static>) {
btn_y.wait_for_falling_edge().await; btn_y.wait_for_falling_edge().await;