diff --git a/Cargo.lock b/Cargo.lock index fd99808..4854f90 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -70,6 +70,12 @@ dependencies = [ "rustc_version 0.2.3", ] +[[package]] +name = "base64" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e1b586273c5702936fe7b7d6896644d8be71e6314cfe09d3167c95f712589e8" + [[package]] name = "bit-set" version = "0.5.3" @@ -115,6 +121,12 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" +[[package]] +name = "cobs" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67ba02a97a2bd10f4b59b25c7973101c79642302776489e030cd13cdab09ed15" + [[package]] name = "codespan-reporting" version = "0.11.1" @@ -418,6 +430,16 @@ dependencies = [ "usbd-hid", ] +[[package]] +name = "embedded-alloc" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8931e47e33c5d3194fbcf9cc82df0919193bd2fa40008f388eb1d28fd9c9ea6b" +dependencies = [ + "critical-section", + "linked_list_allocator", +] + [[package]] name = "embedded-hal" version = "0.2.7" @@ -609,6 +631,7 @@ dependencies = [ "atomic-polyfill 0.1.11", "hash32", "rustc_version 0.4.0", + "serde", "spin", "stable_deref_trait", ] @@ -661,14 +684,18 @@ dependencies = [ "embassy-usb", "embassy-usb-driver", "embassy-usb-logger", + "embedded-alloc", "embedded-hal 0.2.7", "embedded-io", "futures", "log", "pio", "pio-proc", + "postcard", + "ron", "smart-leds", "static_cell", + "tgnt", "usb-device", "usbd-hid", ] @@ -711,6 +738,12 @@ version = "0.2.140" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "99227334921fae1a979cf0bfdfcc6b3e5ce376ef57e16fb6fb3ea2ed6095f80c" +[[package]] +name = "linked_list_allocator" +version = "0.10.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9afa463f5405ee81cdb9cc2baf37e08ec7e4c8209442b5d72c04cfb2cd6e6286" + [[package]] name = "lock_api" version = "0.4.9" @@ -896,6 +929,17 @@ dependencies = [ "syn", ] +[[package]] +name = "postcard" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cfa512cd0d087cc9f99ad30a1bf64795b67871edbead083ffc3a4dfafa59aa00" +dependencies = [ + "cobs", + "heapless", + "serde", +] + [[package]] name = "precomputed-hash" version = "0.1.1" @@ -996,6 +1040,17 @@ dependencies = [ "bytemuck", ] +[[package]] +name = "ron" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "300a51053b1cb55c80b7a9fde4120726ddf25ca241a1cbb926626f62fb136bff" +dependencies = [ + "base64", + "bitflags", + "serde", +] + [[package]] name = "rp2040-pac2" version = "0.1.0" @@ -1058,9 +1113,23 @@ checksum = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3" [[package]] name = "serde" -version = "1.0.154" +version = "1.0.156" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8cdd151213925e7f1ab45a9bbfb129316bd00799784b174b7cc7bcd16961c49e" +checksum = "314b5b092c0ade17c00142951e50ced110ec27cea304b1037c6969246c2469a4" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.156" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d7e29c4601e36bcec74a223228dce795f4cd3616341a4af93520ca1a837c087d" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] [[package]] name = "siphasher" @@ -1176,6 +1245,14 @@ dependencies = [ "winapi-util", ] +[[package]] +name = "tgnt" +version = "0.1.0" +source = "git+https://git.nubo.sh/hulthe/tgnt.git#e22588c2cb139864fcd3be1087350803343c622b" +dependencies = [ + "serde", +] + [[package]] name = "thiserror" version = "1.0.39" diff --git a/Cargo.toml b/Cargo.toml index 72ad90d..4a596df 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -4,17 +4,16 @@ version = "0.1.0" edition = "2021" [dependencies] -#adafruit-itsy-bitsy-rp2040 = "0.6.0" +tgnt = { git = "https://git.nubo.sh/hulthe/tgnt.git", default-features = false } cortex-m = "0.7.6" cortex-m-rt = "0.7" -embedded-hal ="0.2.5" -#panic-halt= "0.2.0" -usb-device = "*" +embedded-hal = "0.2.5" +#panic-halt = "0.2.0" +usb-device = "0.2.9" usbd-hid = "0.6.1" static_cell = "1.0.0" embedded-io = { version = "*", features = ["async"] } futures = { version = "0.3", default-features = false } - embassy-executor = { git = "https://github.com/embassy-rs/embassy.git", features = ["log", "nightly", "integrated-timers" ] } embassy-sync = { git = "https://github.com/embassy-rs/embassy.git", features = ["log", "nightly"] } embassy-time = { git = "https://github.com/embassy-rs/embassy.git", features = ["log"] } @@ -22,12 +21,18 @@ embassy-futures = { git = "https://github.com/embassy-rs/embassy.git", features embassy-usb = { git = "https://github.com/embassy-rs/embassy.git", features = ["usbd-hid"] } embassy-usb-logger = { git = "https://github.com/embassy-rs/embassy.git", features = [] } embassy-usb-driver = { git = "https://github.com/embassy-rs/embassy.git", features = [] } - embassy-rp = { git = "https://github.com/embassy-rs/embassy.git", features = ["log", "nightly", "unstable-traits", "unstable-pac", "time-driver", "pio", "critical-section-impl"] } log = "0.4.17" pio = "0.2.1" pio-proc = "0.2.1" smart-leds = "0.3.0" +embedded-alloc = "0.5.0" +postcard = { version = "1.0.4", features = ["alloc"] } + +[build-dependencies] +tgnt = { git = "https://git.nubo.sh/hulthe/tgnt.git", default-features = false } +ron = "0.8.0" +postcard = { version = "1", features = ["use-std"] } [features] default = ["n-key-rollover"] diff --git a/build.rs b/build.rs index 20b5a0f..3e917fe 100644 --- a/build.rs +++ b/build.rs @@ -8,12 +8,19 @@ //! updating `memory.x` ensures a rebuild of the application with the //! new memory settings. -use std::env; use std::fs::File; use std::io::Write; use std::path::PathBuf; +use std::{env, fs}; + +use tgnt::layer::Layer; fn main() { + memory(); + serialize_layout(); +} + +fn memory() { // Put `memory.x` in our output directory and ensure it's // on the linker search path. let out = &PathBuf::from(env::var_os("OUT_DIR").unwrap()); @@ -36,3 +43,17 @@ fn main() { println!("cargo:rustc-link-arg-bins=-Tlink-rp.x"); //println!("cargo:rustc-link-arg-bins=-Tdefmt.x"); } + +fn serialize_layout() { + println!("cargo:rerun-if-changed=layers.ron"); + + let layers = fs::read_to_string("layers.ron").expect("Failed to read layers.ron"); + let layers: Vec = ron::from_str(&layers).expect("Failed to deserialize layers.ron"); + + let serialized = postcard::to_stdvec(&layers).expect("Failed to serialize layers"); + + File::create("./src/layers.pc") + .expect("Failed to create layers.pc") + .write_all(&serialized) + .expect("Failed to write layers.pc"); +} diff --git a/layers.ron b/layers.ron new file mode 100644 index 0000000..41cf069 --- /dev/null +++ b/layers.ron @@ -0,0 +1,59 @@ +[ + Layer( + buttons: [ + /* Left */ + // Row 1 + Key(Apostrophe), + Key(Comma), + Key(Period), + Key(P), + Key(Y), + + // Row 2 + Key(A), + Key(O), + Key(E), + Key(U), + Key(I), + + // Row 3 + Key(Colon), + Key(Q), + Key(J), + Key(K), + Key(X), + + // Thumbpad + Mod(LShift), + Mod(LCtrl), + NextLayer, + + /* Right */ + // Row 1 + Key(F), + Key(G), + Key(C), + Key(R), + Key(L), + + // Row 2 + Key(D), + Key(H), + Key(T), + Key(N), + Key(S), + + // Row 3 + Key(B), + Key(M), + Key(W), + Key(V), + Key(Z), + + // Thumbpad + PrevLayer, + Mod(RAlt), + Mod(RMod), + ], + ) +] diff --git a/src/.gitignore b/src/.gitignore new file mode 100644 index 0000000..2456870 --- /dev/null +++ b/src/.gitignore @@ -0,0 +1 @@ +layers.pc diff --git a/src/allocator.rs b/src/allocator.rs new file mode 100644 index 0000000..d01e72a --- /dev/null +++ b/src/allocator.rs @@ -0,0 +1,14 @@ +extern crate alloc; + +use core::mem::MaybeUninit; + +use embedded_alloc::Heap; + +#[global_allocator] +static HEAP: Heap = Heap::empty(); + +pub fn init() { + const HEAP_SIZE: usize = 2048; + static mut HEAP_MEM: [MaybeUninit; HEAP_SIZE] = [MaybeUninit::uninit(); HEAP_SIZE]; + unsafe { HEAP.init(HEAP_MEM.as_ptr() as usize, HEAP_SIZE) } +} diff --git a/src/keyboard.rs b/src/keyboard.rs index d911953..9f5d78f 100644 --- a/src/keyboard.rs +++ b/src/keyboard.rs @@ -1,238 +1,154 @@ -use core::sync::atomic::{AtomicBool, Ordering}; +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_time::{Duration, Timer}; -use log::info; +use log::{error, warn}; +use tgnt::{button::Button, layer::Layer}; -pub const ROWS: usize = 3; -pub const COLS: usize = 5; -const NEW_SWITCH: Switch = Switch::new(); -const NEW_ROW: [Switch; COLS] = [NEW_SWITCH; COLS]; -pub static MATRIX: [[Switch; COLS]; ROWS] = [NEW_ROW; ROWS]; +use crate::usb::keyboard::KB_REPORT; -pub static TEST_KEYMAP: [[Button; COLS]; ROWS] = [ - [ - Button::KEY_A, - Button::KEY_B, - Button::KEY_C, - Button::KEY_D, - Button::KEY_E, - ], - [ - Button::KEY_T, - Button::KEY_R, - Button::KEY_H, - Button::KEY_I, - Button::KEY_SPACE, - ], - [ - Button::KEY_O, - Button::KEY_L, - Button::KEY_LSHIFT, - Button::KEY_LCTRL, - Button::KEY_LALT, - ], -]; +static CURRENT_LAYER: AtomicU16 = AtomicU16::new(0); -pub struct Switch { - state: AtomicBool, +pub struct KeyboardConfig { + pub pins: [AnyPin; SWITCH_COUNT], + pub layers: Vec, } -#[allow(dead_code)] -impl Switch { - pub const fn new() -> Self { - Switch { - state: AtomicBool::new(false), +impl KeyboardConfig { + pub async fn create(self) { + let spawner = Spawner::for_current_executor().await; + + if self.layers.is_empty() { + error!("no layers defined"); + return; + } + + let layers = Box::leak(self.layers.into_boxed_slice()); + for (i, layer) in layers.iter().enumerate() { + if layer.buttons.len() != SWITCH_COUNT { + warn!( + "layer {i} defines {} buttons, but there are {SWITCH_COUNT} switches", + layer.buttons.len(), + ) + } + } + + for (i, pin) in self.pins.into_iter().enumerate() { + if spawner.spawn(switch_task(i, pin, layers)).is_err() { + error!("failed to spawn switch task, pool_size mismatch?"); + break; + } } } - - pub fn press(&self) { - self.state.store(true, Ordering::Relaxed); - } - - pub fn release(&self) { - self.state.store(false, Ordering::Relaxed); - } - - pub fn is_pressed(&self) -> bool { - self.state.load(Ordering::Relaxed) - } } -#[derive(Debug, PartialEq, Eq)] -pub enum Button { - Key { keycode: u8 }, -} - -#[embassy_executor::task(pool_size = 20)] -pub async fn monitor_switch(pin: AnyPin) -> ! { - let pin_nr = pin.pin(); +const SWITCH_COUNT: usize = 18; +#[embassy_executor::task(pool_size = 18)] +async fn switch_task(switch_num: usize, pin: AnyPin, layers: &'static [Layer]) -> ! { + let _pin_nr = pin.pin(); let mut pin = Input::new(pin, Pull::Up); loop { + // pins are pull-up, so when the switch is pressed they are brought low. pin.wait_for_low().await; - info!("pin {pin_nr} low"); + let mut current_layer = CURRENT_LAYER.load(Ordering::Relaxed); + let layer_count = 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 { + error!("current layer was out of bounds for some reason ({current_layer})"); + CURRENT_LAYER.store(0, Ordering::Relaxed); + continue; + }; + + let Some(button) = buttons.get(switch_num) else { + continue; + }; + + match button { + &Button::Key(key) => { + KB_REPORT.lock().await.press_key(key); + pin.wait_for_high().await; + KB_REPORT.lock().await.release_key(key); + continue; + } + Button::Mod(modifier) => { + // TODO + //KB_REPORT.lock().await.press_mod(modifier); + //pin.wait_for_high().await; + //KB_REPORT.lock().await.release_mod(modifier); + //continue; + } + Button::ModTap { keycode, modifier } => { + // TODO + } + Button::NextLayer => { + let next_layer = (current_layer + 1) % layer_count; + CURRENT_LAYER.store(next_layer, Ordering::Relaxed); + } + Button::PrevLayer => { + let prev_layer = current_layer.checked_sub(1).unwrap_or(layer_count - 1); + CURRENT_LAYER.store(prev_layer, Ordering::Relaxed); + } + Button::None => {} + } + pin.wait_for_high().await; - info!("pin {pin_nr} high"); } } -#[allow(dead_code)] -impl Button { - pub const fn key(keycode: u8) -> Self { - Button::Key { keycode } - } - - // https://usb.org/sites/default/files/hut1_3_0.pdf - pub const KEY_A: Button = Button::key(0x04); - pub const KEY_B: Button = Button::key(0x05); - pub const KEY_C: Button = Button::key(0x06); - pub const KEY_D: Button = Button::key(0x07); - pub const KEY_E: Button = Button::key(0x08); - pub const KEY_F: Button = Button::key(0x09); - pub const KEY_G: Button = Button::key(0x0A); - pub const KEY_H: Button = Button::key(0x0B); - pub const KEY_I: Button = Button::key(0x0C); - pub const KEY_J: Button = Button::key(0x0D); - pub const KEY_K: Button = Button::key(0x0E); - pub const KEY_L: Button = Button::key(0x0F); - pub const KEY_M: Button = Button::key(0x10); - pub const KEY_N: Button = Button::key(0x11); - pub const KEY_O: Button = Button::key(0x12); - pub const KEY_P: Button = Button::key(0x13); - pub const KEY_Q: Button = Button::key(0x14); - pub const KEY_R: Button = Button::key(0x15); - pub const KEY_S: Button = Button::key(0x16); - pub const KEY_T: Button = Button::key(0x17); - pub const KEY_U: Button = Button::key(0x18); - pub const KEY_V: Button = Button::key(0x19); - pub const KEY_W: Button = Button::key(0x1A); - pub const KEY_X: Button = Button::key(0x1B); - pub const KEY_Y: Button = Button::key(0x1C); - pub const KEY_Z: Button = Button::key(0x1D); - - pub const KEY_1: Button = Button::key(0x1E); - pub const KEY_2: Button = Button::key(0x1F); - pub const KEY_3: Button = Button::key(0x20); - pub const KEY_4: Button = Button::key(0x21); - pub const KEY_5: Button = Button::key(0x22); - pub const KEY_6: Button = Button::key(0x23); - pub const KEY_7: Button = Button::key(0x24); - pub const KEY_8: Button = Button::key(0x25); - pub const KEY_9: Button = Button::key(0x26); - pub const KEY_0: Button = Button::key(0x27); - - pub const KEY_SPACE: Button = Button::key(0x2C); - pub const KEY_RETURN: Button = Button::key(0x28); - - pub const KEY_LCTRL: Button = Button::key(0xE0); - pub const KEY_RCTRL: Button = Button::key(0xE4); - - pub const KEY_LSHIFT: Button = Button::key(0xE1); - pub const KEY_RSHIFT: Button = Button::key(0xE5); - - pub const KEY_LALT: Button = Button::key(0xE2); - pub const KEY_RALT: Button = Button::key(0xE6); -} - /// Random functions for testing #[allow(dead_code)] pub mod test { - use super::*; + use tgnt::{button::Button, keys::Key}; - pub fn letter_to_key(c: char) -> Option