Add communication between keyboard halves
This commit is contained in:
141
src/keyboard.rs
141
src/keyboard.rs
@ -6,19 +6,26 @@ use embassy_rp::{
|
||||
gpio::{AnyPin, Input, Pin, Pull},
|
||||
pio::PioInstanceBase,
|
||||
};
|
||||
use embassy_sync::pubsub::{ImmediatePublisher, PubSubChannel, Subscriber};
|
||||
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 tgnt::{
|
||||
button::{Button, Modifier},
|
||||
keys::Key,
|
||||
layer::Layer,
|
||||
};
|
||||
|
||||
use crate::{
|
||||
lights::Lights,
|
||||
usb::keyboard::KB_REPORT,
|
||||
util::CS,
|
||||
ws2812::{Rgb, Ws2812},
|
||||
};
|
||||
|
||||
pub struct KeyboardConfig {
|
||||
/// Which board is this.
|
||||
pub half: Half,
|
||||
/// Array of input pins of each switch
|
||||
pub pins: [AnyPin; SWITCH_COUNT],
|
||||
/// Array of LED indices of each switch
|
||||
@ -28,6 +35,8 @@ pub struct KeyboardConfig {
|
||||
}
|
||||
|
||||
struct State {
|
||||
/// Which board is this.
|
||||
half: Half,
|
||||
current_layer: AtomicU16,
|
||||
layers: &'static [Layer],
|
||||
/// Array of LED indices of each switch
|
||||
@ -35,13 +44,54 @@ struct State {
|
||||
lights: Lights<PioInstanceBase<1>, SWITCH_COUNT>,
|
||||
}
|
||||
|
||||
/// A keyboard half.
|
||||
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
|
||||
pub enum Half {
|
||||
Left,
|
||||
Right,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct Event {
|
||||
pub source: Half,
|
||||
pub kind: EventKind,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub enum EventKind {
|
||||
PressKey(Key),
|
||||
ReleaseKey(Key),
|
||||
PressModifier(Modifier),
|
||||
ReleaseModifier(Modifier),
|
||||
SetLayer(u16),
|
||||
}
|
||||
|
||||
pub const KB_SUBSCRIBERS: usize = 2;
|
||||
pub const ACTUAL_KB_SUBSCRIBERS: usize = KB_SUBSCRIBERS + 1;
|
||||
const KB_EVENT_CAP: usize = 128;
|
||||
static KB_EVENTS: PubSubChannel<CS, Event, KB_EVENT_CAP, ACTUAL_KB_SUBSCRIBERS, 0> =
|
||||
PubSubChannel::new();
|
||||
pub struct KbEvents {
|
||||
subscriber: Subscriber<'static, CS, Event, KB_EVENT_CAP, ACTUAL_KB_SUBSCRIBERS, 0>,
|
||||
publisher: ImmediatePublisher<'static, CS, Event, KB_EVENT_CAP, ACTUAL_KB_SUBSCRIBERS, 0>,
|
||||
}
|
||||
|
||||
pub struct KbEventsTx<'a> {
|
||||
publisher:
|
||||
&'a mut ImmediatePublisher<'static, CS, Event, KB_EVENT_CAP, ACTUAL_KB_SUBSCRIBERS, 0>,
|
||||
}
|
||||
|
||||
pub struct KbEventsRx<'a> {
|
||||
subscriber: &'a mut Subscriber<'static, CS, Event, KB_EVENT_CAP, ACTUAL_KB_SUBSCRIBERS, 0>,
|
||||
}
|
||||
|
||||
impl KeyboardConfig {
|
||||
pub async fn create(self) {
|
||||
pub async fn create(self) -> Option<[KbEvents; KB_SUBSCRIBERS]> {
|
||||
let spawner = Spawner::for_current_executor().await;
|
||||
|
||||
if self.layers.is_empty() {
|
||||
error!("no layers defined");
|
||||
return;
|
||||
return None;
|
||||
}
|
||||
|
||||
info!(
|
||||
@ -51,6 +101,7 @@ impl KeyboardConfig {
|
||||
|
||||
static STATE: StaticCell<State> = StaticCell::new();
|
||||
let state = STATE.init_with(|| State {
|
||||
half: self.half,
|
||||
current_layer: AtomicU16::new(0),
|
||||
layers: Box::leak(self.layers.into_boxed_slice()),
|
||||
lights: Lights::new(self.led_driver),
|
||||
@ -72,6 +123,51 @@ impl KeyboardConfig {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
spawner.must_spawn(layer_switch_task(
|
||||
KbEvents {
|
||||
publisher: KB_EVENTS.immediate_publisher(),
|
||||
subscriber: KB_EVENTS.subscriber().unwrap(),
|
||||
},
|
||||
state,
|
||||
));
|
||||
|
||||
Some([(); KB_SUBSCRIBERS].map(|_| KbEvents {
|
||||
publisher: KB_EVENTS.immediate_publisher(),
|
||||
subscriber: KB_EVENTS.subscriber().unwrap(),
|
||||
}))
|
||||
}
|
||||
}
|
||||
|
||||
impl KbEvents {
|
||||
pub async fn send(&mut self, event: Event) {
|
||||
self.publisher.publish_immediate(event);
|
||||
}
|
||||
|
||||
pub async fn recv(&mut self) -> Event {
|
||||
self.subscriber.next_message_pure().await
|
||||
}
|
||||
|
||||
pub fn split(&mut self) -> (KbEventsRx, KbEventsTx) {
|
||||
let tx = KbEventsTx {
|
||||
publisher: &mut self.publisher,
|
||||
};
|
||||
let rx = KbEventsRx {
|
||||
subscriber: &mut self.subscriber,
|
||||
};
|
||||
(rx, tx)
|
||||
}
|
||||
}
|
||||
|
||||
impl KbEventsRx<'_> {
|
||||
pub async fn recv(&mut self) -> Event {
|
||||
self.subscriber.next_message_pure().await
|
||||
}
|
||||
}
|
||||
|
||||
impl KbEventsTx<'_> {
|
||||
pub fn send(&mut self, event: Event) {
|
||||
self.publisher.publish_immediate(event);
|
||||
}
|
||||
}
|
||||
|
||||
@ -83,6 +179,7 @@ const SWITCH_COUNT: usize = 18;
|
||||
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);
|
||||
let events = KB_EVENTS.immediate_publisher();
|
||||
loop {
|
||||
// pins are pull-up, so when the switch is pressed they are brought low.
|
||||
pin.wait_for_low().await;
|
||||
@ -124,20 +221,26 @@ async fn switch_task(switch_num: usize, pin: AnyPin, state: &'static State) -> !
|
||||
}
|
||||
};
|
||||
|
||||
let ev = |kind| Event {
|
||||
source: state.half,
|
||||
kind,
|
||||
};
|
||||
|
||||
use EventKind::*;
|
||||
match button {
|
||||
&Button::Key(key) => {
|
||||
KB_REPORT.lock().await.press_key(key);
|
||||
events.publish_immediate(ev(PressKey(key)));
|
||||
state.lights.update(set_led(Rgb::new(0, 150, 0))).await;
|
||||
wait_for_release.await;
|
||||
KB_REPORT.lock().await.release_key(key);
|
||||
events.publish_immediate(ev(ReleaseKey(key)));
|
||||
state.lights.update(set_led(Rgb::new(0, 0, 0))).await;
|
||||
continue;
|
||||
}
|
||||
&Button::Mod(modifier) => {
|
||||
KB_REPORT.lock().await.press_modifier(modifier);
|
||||
events.publish_immediate(ev(PressModifier(modifier)));
|
||||
state.lights.update(set_led(Rgb::new(100, 100, 0))).await;
|
||||
wait_for_release.await;
|
||||
KB_REPORT.lock().await.release_modifier(modifier);
|
||||
events.publish_immediate(ev(ReleaseModifier(modifier)));
|
||||
state.lights.update(set_led(Rgb::new(0, 0, 0))).await;
|
||||
continue;
|
||||
}
|
||||
@ -145,19 +248,19 @@ async fn switch_task(switch_num: usize, pin: AnyPin, state: &'static State) -> !
|
||||
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);
|
||||
events.publish_immediate(ev(PressModifier(modifier)));
|
||||
state.lights.update(set_led(Rgb::new(0, 0, 150))).await;
|
||||
pin.wait_for_high().await;
|
||||
KB_REPORT.lock().await.release_modifier(modifier);
|
||||
events.publish_immediate(ev(ReleaseModifier(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(key);
|
||||
events.publish_immediate(ev(PressKey(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(key);
|
||||
events.publish_immediate(ev(ReleaseKey(key)));
|
||||
state.lights.update(set_led(Rgb::new(0, 0, 0))).await;
|
||||
continue;
|
||||
}
|
||||
@ -165,12 +268,12 @@ async fn switch_task(switch_num: usize, pin: AnyPin, state: &'static State) -> !
|
||||
}
|
||||
Button::NextLayer => {
|
||||
let next_layer = (current_layer + 1) % layer_count;
|
||||
state.current_layer.store(next_layer, Ordering::Relaxed);
|
||||
events.publish_immediate(ev(SetLayer(next_layer)));
|
||||
debug!("switched to layer {next_layer}");
|
||||
}
|
||||
Button::PrevLayer => {
|
||||
let prev_layer = current_layer.checked_sub(1).unwrap_or(layer_count - 1);
|
||||
state.current_layer.store(prev_layer, Ordering::Relaxed);
|
||||
events.publish_immediate(ev(SetLayer(prev_layer)));
|
||||
debug!("switched to layer {prev_layer}");
|
||||
}
|
||||
Button::None => {}
|
||||
@ -181,6 +284,16 @@ async fn switch_task(switch_num: usize, pin: AnyPin, state: &'static State) -> !
|
||||
}
|
||||
}
|
||||
|
||||
#[embassy_executor::task]
|
||||
async fn layer_switch_task(mut events: KbEvents, state: &'static State) {
|
||||
loop {
|
||||
let event = events.recv().await;
|
||||
if let EventKind::SetLayer(new_layer) = event.kind {
|
||||
state.current_layer.store(new_layer, Ordering::Relaxed);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Random functions for testing
|
||||
#[allow(dead_code)]
|
||||
pub mod test {
|
||||
|
||||
Reference in New Issue
Block a user