Add idle animation

This commit is contained in:
2023-05-15 22:07:15 +02:00
parent 251fe2d79b
commit 78db14f88d
5 changed files with 146 additions and 87 deletions

View File

@ -1,35 +1,12 @@
#
# Cargo Configuration for the https://github.com/rp-rs/rp-hal.git repository.
#
# Copyright (c) The RP-RS Developers, 2021
#
# You might want to make a similar file in your own repository if you are
# writing programs for Raspberry Silicon microcontrollers.
#
# This file is MIT or Apache-2.0 as per the repository README.md file
#
[build] [build]
# Set the default target to match the Cortex-M0+ in the RP2040
target = "thumbv6m-none-eabi" target = "thumbv6m-none-eabi"
# Target specific options
[target.thumbv6m-none-eabi] [target.thumbv6m-none-eabi]
# Pass some extra options to rustc, some of which get passed on to the linker.
#
# * inline-threshold=5 makes the compiler more aggressive and inlining functions
# * no-vectorize-loops turns off the loop vectorizer (seeing as the M0+ doesn't
# have SIMD)
rustflags = [ rustflags = [
"-C", "linker=flip-link", "-C", "linker=flip-link",
"-C", "inline-threshold=5", "-C", "inline-threshold=5", # inline functions more aggressively
"-C", "no-vectorize-loops", "-C", "no-vectorize-loops",
] ]
# This runner will make a UF2 file and then copy it to a mounted RP2040 in USB #runner = "elf2uf2-rs -d"
# Bootloader mode: runner = "probe-run --chip RP2040"
runner = "elf2uf2-rs -d"
# This runner will find a supported SWD debug probe and flash your RP2040 over
# SWD:
# runner = "probe-run --chip RP2040"

View File

@ -70,7 +70,7 @@ pub enum EventKind {
} }
pub const KB_SUBSCRIBERS: usize = 2; pub const KB_SUBSCRIBERS: usize = 2;
pub const ACTUAL_KB_SUBSCRIBERS: usize = KB_SUBSCRIBERS + 2; const ACTUAL_KB_SUBSCRIBERS: usize = KB_SUBSCRIBERS + 2;
const KB_EVENT_CAP: usize = 128; const KB_EVENT_CAP: usize = 128;
static KB_EVENTS: PubSubChannel<CS, Event, KB_EVENT_CAP, ACTUAL_KB_SUBSCRIBERS, 0> = static KB_EVENTS: PubSubChannel<CS, Event, KB_EVENT_CAP, ACTUAL_KB_SUBSCRIBERS, 0> =
PubSubChannel::new(); PubSubChannel::new();
@ -254,7 +254,7 @@ async fn switch_task(switch_num: usize, pin: AnyPin, state: &'static State) -> !
} }
_ = wait_for_release.fuse() => { _ = wait_for_release.fuse() => {
events.publish_immediate(ev(PressKey(key))); events.publish_immediate(ev(PressKey(key)));
Timer::after(Duration::from_millis(10)).await; Timer::after(Duration::from_millis(20)).await;
events.publish_immediate(ev(ReleaseKey(key))); events.publish_immediate(ev(ReleaseKey(key)));
continue; continue;
} }

View File

@ -1,15 +1,80 @@
use core::cmp::min;
use embassy_time::{Duration, Timer}; use embassy_time::{Duration, Timer};
use futures::{select_biased, FutureExt}; use futures::{select_biased, FutureExt};
use crate::ws2812::Rgb; use crate::{util::wheel, ws2812::Rgb};
use super::{EventKind, KbEvents, State, SWITCH_COUNT}; use super::{Event, EventKind, KbEvents, State, SWITCH_COUNT};
/// Duration until the keyboard starts the idle animation
const UNTIL_IDLE: Duration = Duration::from_secs(30);
///// Duration from idle until the keyboard goes to sleep
//const UNTIL_SLEEP: Duration = Duration::from_secs(10);
const IDLE_ANIMATION_SPEED: u64 = 3;
const IDLE_ANIMATION_KEY_OFFSET: u64 = 10;
const IDLE_BRIGHTNESS_RAMPUP: Duration = Duration::from_secs(120);
const MAX_IDLE_BRIGHTESS: f32 = 1.0;
const MIN_IDLE_BRIGHTESS: f32 = 0.05;
#[embassy_executor::task] #[embassy_executor::task]
pub(super) async fn task(mut events: KbEvents, state: &'static State) { pub(super) async fn task(mut events: KbEvents, state: &'static State) {
loop { loop {
select_biased! {
event = events.recv().fuse() => handle_event(event, state).await,
_ = Timer::after(UNTIL_IDLE).fuse() => {
select_biased! { select_biased! {
event = events.recv().fuse() => { event = events.recv().fuse() => {
state.lights.update(|lights| {
lights.iter_mut().for_each(|rgb| *rgb = Rgb::new(0, 0, 0));
}).await;
handle_event(event, state).await;
}
_ = idle_animation(state).fuse() => {}
}
}
}
}
}
async fn idle_animation(state: &'static State) {
for tick in 0.. {
const FRAMETIME: Duration = Duration::from_millis(16);
state
.lights
.update(|lights| {
const N_MAX: u64 = IDLE_BRIGHTNESS_RAMPUP.as_millis() / FRAMETIME.as_millis();
let brightness = if tick >= N_MAX {
MAX_IDLE_BRIGHTESS
} else {
((tick as f32) / N_MAX as f32).clamp(MIN_IDLE_BRIGHTESS, MAX_IDLE_BRIGHTESS)
};
for (n, &i) in state.led_map.iter().enumerate() {
let Some(light) = lights.get_mut(i) else { continue };
let (r, g, b) = wheel(
(n as u64 * IDLE_ANIMATION_KEY_OFFSET + tick * IDLE_ANIMATION_SPEED) as u8,
)
.components();
let rgb = Rgb::new(
((r as f32) * brightness) as u8,
((g as f32) * brightness) as u8,
((b as f32) * brightness) as u8,
);
*light = rgb;
}
})
.await;
Timer::after(FRAMETIME).await;
}
}
async fn handle_event(event: Event, state: &'static State) {
let set_button_led = |color: Rgb| { let set_button_led = |color: Rgb| {
let led_num = state.led_map.get(event.source_button).copied(); let led_num = state.led_map.get(event.source_button).copied();
move |leds: &mut [Rgb; SWITCH_COUNT]| { move |leds: &mut [Rgb; SWITCH_COUNT]| {
@ -25,42 +90,63 @@ pub(super) async fn task(mut events: KbEvents, state: &'static State) {
match event.kind { match event.kind {
EventKind::PressKey(_) => { EventKind::PressKey(_) => {
state.lights.update(set_button_led(Rgb::new(0, 150, 0))).await; state
.lights
.update(set_button_led(Rgb::new(0, 150, 0)))
.await;
} }
EventKind::PressModifier(_) => { EventKind::PressModifier(_) => {
state.lights.update(set_button_led(Rgb::new(0, 0, 150))).await; state
.lights
.update(set_button_led(Rgb::new(0, 0, 150)))
.await;
} }
EventKind::ReleaseKey(_) | EventKind::ReleaseModifier(_) => { EventKind::ReleaseKey(_) | EventKind::ReleaseModifier(_) => {
state.lights.update(set_button_led(Rgb::new(0, 0, 0))).await; state.lights.update(set_button_led(Rgb::new(0, 0, 0))).await;
} }
EventKind::SetLayer(layer) => { EventKind::SetLayer(layer) => {
let buttons_to_light_up = match layer { let layer = min(layer, state.layers.len().saturating_sub(1) as u16);
let buttons_to_light_up = if state.layers.len() <= 3 {
match layer {
0 => [0, 1, 2, 3, 4].as_ref(), 0 => [0, 1, 2, 3, 4].as_ref(),
1 => &[5, 6, 7, 8, 9], 1 => &[5, 6, 7, 8, 9],
2 => &[10, 11, 12, 13, 14], 2 => &[10, 11, 12, 13, 14],
_ => &[], _ => &[],
}
} else {
match layer {
0 => [0, 5, 10].as_ref(),
1 => &[1, 6, 11],
2 => &[2, 7, 12],
3 => &[3, 8, 13],
4 => &[4, 9, 14],
_ => &[],
}
}; };
state.lights.update(|leds| { state
.lights
.update(|leds| {
for &button in buttons_to_light_up { for &button in buttons_to_light_up {
let Some(&led_id) = state.led_map.get(button) else { continue; }; let Some(&led_id) = state.led_map.get(button) else { continue; };
let Some(rgb) = leds.get_mut(led_id) else { continue; }; let Some(rgb) = leds.get_mut(led_id) else { continue; };
*rgb = Rgb::new(100, 0, 100); *rgb = Rgb::new(100, 0, 100);
} }
}).await; })
.await;
Timer::after(Duration::from_millis(200)).await; Timer::after(Duration::from_millis(200)).await;
state.lights.update(|leds| { state
.lights
.update(|leds| {
for &button in buttons_to_light_up { for &button in buttons_to_light_up {
let Some(&led_id) = state.led_map.get(button) else { continue; }; let Some(&led_id) = state.led_map.get(button) else { continue; };
let Some(rgb) = leds.get_mut(led_id) else { continue; }; let Some(rgb) = leds.get_mut(led_id) else { continue; };
*rgb = Rgb::new(0, 0, 0); *rgb = Rgb::new(0, 0, 0);
} }
}).await; })
} .await;
}
}
} }
} }
} }

View File

@ -61,10 +61,10 @@ async fn uart_task(uart: BufferedUart<'static, UART0>, this_half: Half, mut even
}; };
let message: Message = unsafe { transmute(buf) }; // crimes :) let message: Message = unsafe { transmute(buf) }; // crimes :)
info!("got msg: {:?}", message); match &message {
match message { Message::KeyboardEvent(event) => events_tx.send(event.clone()),
Message::KeyboardEvent(event) => events_tx.send(event),
} }
info!("got msg: {:?}", message);
} }
}; };

View File

@ -15,7 +15,7 @@ pub async fn stall() -> ! {
// The colours are a transition r - g - b - back to r. // The colours are a transition r - g - b - back to r.
pub fn wheel(mut wheel_pos: u8) -> Rgb { pub fn wheel(mut wheel_pos: u8) -> Rgb {
wheel_pos = 255 - wheel_pos; wheel_pos = 255 - wheel_pos;
let rgb = if wheel_pos < 85 { if wheel_pos < 85 {
Rgb::new(255 - wheel_pos * 3, 0, wheel_pos * 3) Rgb::new(255 - wheel_pos * 3, 0, wheel_pos * 3)
} else if wheel_pos < 170 { } else if wheel_pos < 170 {
wheel_pos -= 85; wheel_pos -= 85;
@ -23,9 +23,5 @@ pub fn wheel(mut wheel_pos: u8) -> Rgb {
} else { } else {
wheel_pos -= 170; wheel_pos -= 170;
Rgb::new(wheel_pos * 3, 255 - wheel_pos * 3, 0) Rgb::new(wheel_pos * 3, 255 - wheel_pos * 3, 0)
}; }
// tone the brightness down a bit, sheesh.
//rgb / 4
rgb
} }