Make keys light up when pressed

This commit is contained in:
2023-04-26 18:39:47 +02:00
parent 2fc050eb4f
commit 4f59a59693
9 changed files with 245 additions and 95 deletions

View File

@ -2,21 +2,39 @@ use core::sync::atomic::{AtomicU16, Ordering};
use alloc::{boxed::Box, vec::Vec};
use embassy_executor::Spawner;
use embassy_rp::gpio::{AnyPin, Input, Pin, Pull};
use embassy_rp::{
gpio::{AnyPin, Input, Pin, Pull},
pio::PioInstanceBase,
};
use embassy_time::{Duration, Timer};
use futures::{select_biased, FutureExt};
use log::{debug, error, info, warn};
use static_cell::StaticCell;
use tgnt::{button::Button, layer::Layer};
use crate::usb::keyboard::KB_REPORT;
static CURRENT_LAYER: AtomicU16 = AtomicU16::new(0);
use crate::{
lights::Lights,
usb::keyboard::KB_REPORT,
ws2812::{Rgb, Ws2812},
};
pub struct KeyboardConfig {
/// Array of input pins of each switch
pub pins: [AnyPin; SWITCH_COUNT],
/// Array of LED indices of each switch
pub led_map: [usize; SWITCH_COUNT],
pub led_driver: Ws2812<PioInstanceBase<1>>,
pub layers: Vec<Layer>,
}
struct State {
current_layer: AtomicU16,
layers: &'static [Layer],
/// Array of LED indices of each switch
led_map: [usize; SWITCH_COUNT],
lights: Lights<PioInstanceBase<1>, SWITCH_COUNT>,
}
impl KeyboardConfig {
pub async fn create(self) {
let spawner = Spawner::for_current_executor().await;
@ -31,8 +49,15 @@ impl KeyboardConfig {
self.layers.len()
);
let layers = Box::leak(self.layers.into_boxed_slice());
for (i, layer) in layers.iter().enumerate() {
static STATE: StaticCell<State> = StaticCell::new();
let state = STATE.init_with(|| State {
current_layer: AtomicU16::new(0),
layers: Box::leak(self.layers.into_boxed_slice()),
lights: Lights::new(self.led_driver),
led_map: self.led_map,
});
for (i, layer) in state.layers.iter().enumerate() {
if layer.buttons.len() != SWITCH_COUNT {
warn!(
"layer {i} defines {} buttons, but there are {SWITCH_COUNT} switches",
@ -42,7 +67,7 @@ impl KeyboardConfig {
}
for (i, pin) in self.pins.into_iter().enumerate() {
if spawner.spawn(switch_task(i, pin, layers)).is_err() {
if spawner.spawn(switch_task(i, pin, state)).is_err() {
error!("failed to spawn switch task, pool_size mismatch?");
break;
}
@ -50,12 +75,12 @@ impl KeyboardConfig {
}
}
const MOD_TAP_TIME: Duration = Duration::from_millis(100);
const MOD_TAP_TIME: Duration = Duration::from_millis(150);
const SWITCH_COUNT: usize = 18;
/// Task for monitoring a single switch pin, and handling button presses.
#[embassy_executor::task(pool_size = 18)]
async fn switch_task(switch_num: usize, pin: AnyPin, layers: &'static [Layer]) -> ! {
async fn switch_task(switch_num: usize, pin: AnyPin, state: &'static State) -> ! {
let _pin_nr = pin.pin();
let mut pin = Input::new(pin, Pull::Up);
loop {
@ -65,15 +90,15 @@ async fn switch_task(switch_num: usize, pin: AnyPin, layers: &'static [Layer]) -
// TODO: do we need debouncing?
// get current layer
let mut current_layer = CURRENT_LAYER.load(Ordering::Relaxed);
let layer_count = layers.len() as u16;
let mut current_layer = state.current_layer.load(Ordering::Relaxed);
let layer_count = state.layers.len() as u16;
if current_layer >= layer_count {
error!("current layer was out of bounds for some reason ({current_layer})");
current_layer = 0;
}
let Some(Layer { buttons }) = layers.get(usize::from(current_layer)) else {
let Some(Layer { buttons }) = state.layers.get(usize::from(current_layer)) else {
error!("current layer was out of bounds for some reason ({current_layer})");
CURRENT_LAYER.store(0, Ordering::Relaxed);
state.current_layer.store(0, Ordering::Relaxed);
continue;
};
@ -90,50 +115,69 @@ async fn switch_task(switch_num: usize, pin: AnyPin, layers: &'static [Layer]) -
debug!("switch {switch_num} button {button:?} released");
};
let set_led = |color: Rgb| {
let led_num = state.led_map.get(switch_num).copied();
move |leds: &mut [Rgb; SWITCH_COUNT]| {
if let Some(led) = led_num.and_then(|i| leds.get_mut(i)) {
*led = color;
}
}
};
match button {
&Button::Key(key) => {
KB_REPORT.lock().await.press_key(key);
state.lights.update(set_led(Rgb::new(0, 150, 0))).await;
wait_for_release.await;
KB_REPORT.lock().await.release_key(key);
state.lights.update(set_led(Rgb::new(0, 0, 0))).await;
continue;
}
&Button::Mod(modifier) => {
KB_REPORT.lock().await.press_modifier(modifier);
state.lights.update(set_led(Rgb::new(100, 100, 0))).await;
wait_for_release.await;
KB_REPORT.lock().await.release_modifier(modifier);
state.lights.update(set_led(Rgb::new(0, 0, 0))).await;
continue;
}
&Button::ModTap { keycode, modifier } => {
&Button::ModTap(key, modifier) => {
state.lights.update(set_led(Rgb::new(100, 100, 0))).await;
select_biased! {
_ = Timer::after(MOD_TAP_TIME).fuse() => {
KB_REPORT.lock().await.press_modifier(modifier);
state.lights.update(set_led(Rgb::new(0, 0, 150))).await;
pin.wait_for_high().await;
KB_REPORT.lock().await.release_modifier(modifier);
state.lights.update(set_led(Rgb::new(0, 0, 0))).await;
debug!("switch {switch_num} button {button:?} released");
continue;
}
_ = wait_for_release.fuse() => {
KB_REPORT.lock().await.press_key(keycode);
KB_REPORT.lock().await.press_key(key);
state.lights.update(set_led(Rgb::new(0, 150, 0))).await;
Timer::after(Duration::from_millis(10)).await;
KB_REPORT.lock().await.release_key(keycode);
KB_REPORT.lock().await.release_key(key);
state.lights.update(set_led(Rgb::new(0, 0, 0))).await;
continue;
}
}
}
Button::NextLayer => {
let next_layer = (current_layer + 1) % layer_count;
CURRENT_LAYER.store(next_layer, Ordering::Relaxed);
state.current_layer.store(next_layer, Ordering::Relaxed);
debug!("switched to layer {next_layer}");
}
Button::PrevLayer => {
let prev_layer = current_layer.checked_sub(1).unwrap_or(layer_count - 1);
CURRENT_LAYER.store(prev_layer, Ordering::Relaxed);
state.current_layer.store(prev_layer, Ordering::Relaxed);
debug!("switched to layer {prev_layer}");
}
Button::None => {}
}
wait_for_release.await;
state.lights.update(set_led(Rgb::new(0, 0, 0))).await;
}
}