From c39893a067f542e06c6a4300c0e3e610816be034 Mon Sep 17 00:00:00 2001 From: Joakim Hulthe Date: Tue, 9 May 2023 18:10:43 +0200 Subject: [PATCH] Add communication between keyboard halves --- Cargo.toml | 3 + src/bin/left.rs | 24 +++++--- src/bin/right.rs | 20 +++++-- src/keyboard.rs | 141 +++++++++++++++++++++++++++++++++++++++----- src/lib.rs | 1 + src/uart.rs | 88 +++++++++++++++++++++++++++ src/usb.rs | 6 +- src/usb/keyboard.rs | 26 +++++++- 8 files changed, 278 insertions(+), 31 deletions(-) create mode 100644 src/uart.rs diff --git a/Cargo.toml b/Cargo.toml index 093f30a..6c29701 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -31,6 +31,9 @@ smart-leds = "0.3.0" embedded-alloc = "0.5.0" postcard = { version = "1.0.4", features = ["alloc"] } +#[patch."https://git.nubo.sh/hulthe/tgnt.git"] +#tgnt = { path = "../tgnt" } + [build-dependencies] tgnt = { git = "https://git.nubo.sh/hulthe/tgnt.git", default-features = false } ron = "0.8.0" diff --git a/src/bin/left.rs b/src/bin/left.rs index 8d600e3..2399396 100644 --- a/src/bin/left.rs +++ b/src/bin/left.rs @@ -11,15 +11,18 @@ use alloc::vec::Vec; use embassy_executor::Spawner; use embassy_rp::gpio::{Level, Output, Pin}; use embassy_time::{Duration, Timer}; +use log::error; use tangentbord1::board::Board; -use tangentbord1::keyboard::KeyboardConfig; +use tangentbord1::keyboard::{Half, KeyboardConfig}; use tangentbord1::util::{stall, wheel}; use tangentbord1::ws2812::{Rgb, Ws2812}; -use tangentbord1::{allocator, usb}; +use tangentbord1::{allocator, uart, usb}; use tgnt::layer::Layer; #[embassy_executor::main] async fn main(_spawner: Spawner) { + let half = Half::Left; + allocator::init(); let p = embassy_rp::init(Default::default()); @@ -32,10 +35,6 @@ async fn main(_spawner: Spawner) { let neopixels_d5 = Ws2812::new(board.PIO1, board.DMA_CH1, board.d5.degrade()); neopixel.write(&[Rgb::new(0xFF, 0x00, 0x00)]).await; - usb::setup_logger_and_keyboard(board.USB).await; - neopixel.write(&[Rgb::new(0x00, 0x00, 0xFF)]).await; - - //Timer::after(Duration::from_millis(3000)).await; let layers = include_bytes!("layers-left.pc"); let Ok(layers): Result, _> = postcard::from_bytes(layers) else { @@ -44,6 +43,7 @@ async fn main(_spawner: Spawner) { }; let keyboard = KeyboardConfig { + half, pins: [ // row 1 board.d24.degrade(), @@ -75,7 +75,17 @@ async fn main(_spawner: Spawner) { layers, }; - keyboard.create().await; + let Some([events1, events2]) = keyboard.create().await else { + error!("failed to create keyboard"); + return; + }; + + uart::start(board.tx, board.rx, board.UART0, half, events2).await; + + neopixel.write(&[Rgb::new(0x00, 0x99, 0x99)]).await; + + usb::setup_logger_and_keyboard(board.USB, events1).await; + neopixel.write(&[Rgb::new(0x00, 0x00, 0xFF)]).await; for w in 0usize.. { neopixel.write(&[wheel(w as u8)]).await; diff --git a/src/bin/right.rs b/src/bin/right.rs index 970c8f1..3b4a812 100644 --- a/src/bin/right.rs +++ b/src/bin/right.rs @@ -11,15 +11,18 @@ use alloc::vec::Vec; use embassy_executor::Spawner; use embassy_rp::gpio::{Level, Output, Pin}; use embassy_time::{Duration, Timer}; +use log::error; use tangentbord1::board::Board; -use tangentbord1::keyboard::KeyboardConfig; +use tangentbord1::keyboard::{Half, KeyboardConfig}; use tangentbord1::util::{stall, wheel}; use tangentbord1::ws2812::{Rgb, Ws2812}; -use tangentbord1::{allocator, usb}; +use tangentbord1::{allocator, uart, usb}; use tgnt::layer::Layer; #[embassy_executor::main] async fn main(_spawner: Spawner) { + let half = Half::Right; + allocator::init(); let p = embassy_rp::init(Default::default()); @@ -32,8 +35,6 @@ async fn main(_spawner: Spawner) { let neopixels_d5 = Ws2812::new(board.PIO1, board.DMA_CH1, board.d5.degrade()); neopixel.write(&[Rgb::new(0xFF, 0x00, 0x00)]).await; - usb::setup_logger_and_keyboard(board.USB).await; - neopixel.write(&[Rgb::new(0x00, 0x00, 0xFF)]).await; //Timer::after(Duration::from_millis(3000)).await; @@ -44,6 +45,7 @@ async fn main(_spawner: Spawner) { }; let keyboard = KeyboardConfig { + half, pins: [ // TODO: reconfigure these for right PCB // row 1 @@ -74,7 +76,15 @@ async fn main(_spawner: Spawner) { layers, }; - keyboard.create().await; + let Some([events1, events2]) = keyboard.create().await else { + error!("failed to create keyboard"); + return; + }; + + uart::start(board.tx, board.rx, board.UART0, half, events2).await; + + usb::setup_logger_and_keyboard(board.USB, events1).await; + neopixel.write(&[Rgb::new(0x00, 0x00, 0xFF)]).await; for w in 0usize.. { neopixel.write(&[wheel(w as u8)]).await; diff --git a/src/keyboard.rs b/src/keyboard.rs index 85d8c41..ab28bca 100644 --- a/src/keyboard.rs +++ b/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, 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 = + 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 = 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 { diff --git a/src/lib.rs b/src/lib.rs index d81c133..d9f9701 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -9,6 +9,7 @@ pub mod keyboard; pub mod lights; pub mod neopixel; pub mod panic_handler; +pub mod uart; pub mod usb; pub mod util; pub mod ws2812; diff --git a/src/uart.rs b/src/uart.rs new file mode 100644 index 0000000..c99ba89 --- /dev/null +++ b/src/uart.rs @@ -0,0 +1,88 @@ +use core::mem::{size_of, transmute}; + +use embassy_executor::Spawner; +use embassy_rp::interrupt; +use embassy_rp::peripherals::{PIN_0, PIN_1, UART0}; +use embassy_rp::uart::{self, BufferedUart, DataBits, Parity, StopBits}; +use embassy_time::{with_timeout, Duration, TimeoutError}; +use embedded_io::asynch::{Read, Write}; +use futures::{select_biased, FutureExt}; +use log::{error, info}; +use static_cell::StaticCell; + +use crate::keyboard::{self, Half, KbEvents}; + +#[derive(Clone, Debug)] +enum Message { + KeyboardEvent(keyboard::Event), +} + +pub async fn start(tx: PIN_0, rx: PIN_1, uart: UART0, board: Half, events: KbEvents) { + static TX_BUF: StaticCell<[u8; 1024]> = StaticCell::new(); + static RX_BUF: StaticCell<[u8; 1024]> = StaticCell::new(); + + let mut config = uart::Config::default(); + config.baudrate = 115200; + config.data_bits = DataBits::DataBits8; + config.stop_bits = StopBits::STOP1; + config.parity = Parity::ParityNone; + + let uart = embassy_rp::uart::BufferedUart::new( + uart, + interrupt::take!(UART0_IRQ), + tx, + rx, + TX_BUF.init_with(|| [0u8; 1024]), + RX_BUF.init_with(|| [0u8; 1024]), + config, + ); + + Spawner::for_current_executor() + .await + .must_spawn(uart_task(uart, board, events)) +} + +#[embassy_executor::task] +async fn uart_task(uart: BufferedUart<'static, UART0>, this_half: Half, mut events: KbEvents) { + let (mut rx, mut tx) = uart.split(); + let (mut events_rx, mut events_tx) = events.split(); + + let rx_task = async { + loop { + let mut buf = [0u8; size_of::()]; + // TODO: this timout thing seems sketchy + match with_timeout(Duration::from_millis(5), rx.read_exact(&mut buf)).await { + Ok(Ok(_)) => {} + Ok(Err(e)) => { + error!("uart error: {:?}", e); + continue; + } + Err(TimeoutError) => continue, + }; + + let message: Message = unsafe { transmute(buf) }; // crimes :) + info!("got msg: {:?}", message); + match message { + Message::KeyboardEvent(event) => events_tx.send(event), + } + } + }; + + let tx_task = async { + loop { + // forward messages to the other keyboard half + let event = events_rx.recv().await; + if event.source != this_half { + continue; // do not forward messages from the other half back to it + } + let message = Message::KeyboardEvent(event); + let message: [u8; size_of::()] = unsafe { transmute(message) }; // crimes :) + tx.write_all(&message).await.ok(); + } + }; + + select_biased! { + _ = tx_task.fuse() => error!("uart tx_task exited"), + _ = rx_task.fuse() => error!("eart rx_task exited"), + } +} diff --git a/src/usb.rs b/src/usb.rs index a521c77..f614b94 100644 --- a/src/usb.rs +++ b/src/usb.rs @@ -3,6 +3,8 @@ use embassy_rp::{interrupt, peripherals::USB, usb::Driver}; use embassy_usb::{Builder, Config, UsbDevice}; use static_cell::StaticCell; +use crate::keyboard::KbEvents; + pub mod keyboard; pub mod logger; @@ -17,7 +19,7 @@ struct State { static STATE: StaticCell = StaticCell::new(); -pub async fn setup_logger_and_keyboard(usb: USB) { +pub async fn setup_logger_and_keyboard(usb: USB, events: KbEvents) { let mut builder = builder(usb); logger::setup(&mut builder).await; @@ -27,7 +29,7 @@ pub async fn setup_logger_and_keyboard(usb: USB) { log::debug!("log_level: debug"); log::trace!("log_level: trace"); - keyboard::setup(&mut builder).await; + keyboard::setup(&mut builder, events).await; let usb = builder.build(); Spawner::for_current_executor().await.must_spawn(run(usb)); } diff --git a/src/usb/keyboard.rs b/src/usb/keyboard.rs index 23e8821..2830984 100644 --- a/src/usb/keyboard.rs +++ b/src/usb/keyboard.rs @@ -14,6 +14,7 @@ use static_cell::StaticCell; use usbd_hid::descriptor::{MouseReport, SerializedDescriptor}; use crate::{ + keyboard::{Event, EventKind, KbEvents}, usb::keyboard::report::{KeyboardReport, EMPTY_KEYBOARD_REPORT}, util::CS, }; @@ -31,7 +32,7 @@ struct Context { state: hid::State<'static>, } -pub async fn setup(builder: &mut Builder<'static, Driver<'static, USB>>) { +pub async fn setup(builder: &mut Builder<'static, Driver<'static, USB>>, events: KbEvents) { log::info!("setting up usb hid"); let context = CONTEXT.init(Context { @@ -52,6 +53,7 @@ pub async fn setup(builder: &mut Builder<'static, Driver<'static, USB>>) { let spawner = Spawner::for_current_executor().await; spawner.must_spawn(task(stream, &context.handler)); + spawner.must_spawn(listen_to_events(events)); log::info!("done setting up usb keyboard"); } @@ -82,9 +84,27 @@ impl RequestHandler for Handler { } type HidStream = HidReaderWriter<'static, Driver<'static, USB>, 256, 256>; +#[embassy_executor::task] +async fn listen_to_events(mut events: KbEvents) { + loop { + let event = events.recv().await; + report_event(event).await; + } +} + +pub async fn report_event(event: Event) { + match event.kind { + EventKind::PressKey(key) => KB_REPORT.lock().await.press_key(key), + EventKind::ReleaseKey(key) => KB_REPORT.lock().await.release_key(key), + EventKind::PressModifier(modifier) => KB_REPORT.lock().await.press_modifier(modifier), + EventKind::ReleaseModifier(modifier) => KB_REPORT.lock().await.release_modifier(modifier), + EventKind::SetLayer(_) => {} + } +} + #[embassy_executor::task] async fn task(stream: HidStream, handler: &'static Handler) { - if let Err(e) = keyboard_test(stream, handler).await { + if let Err(e) = keyboard_report(stream, handler).await { log::error!("keyboard error: {e:?}"); } //if let Err(e) = mouse_wiggler(stream).await { @@ -92,7 +112,7 @@ async fn task(stream: HidStream, handler: &'static Handler) { //} } -async fn keyboard_test(mut stream: HidStream, _handler: &'static Handler) -> Result<(), Error> { +async fn keyboard_report(mut stream: HidStream, _handler: &'static Handler) -> Result<(), Error> { stream.ready().await; loop { Timer::after(Duration::from_millis(2)).await;