Move away from matrix-based key layout

This commit is contained in:
2023-03-17 23:37:43 +01:00
parent 4e83970cc3
commit 5c154141d0
12 changed files with 384 additions and 269 deletions

81
Cargo.lock generated
View File

@ -70,6 +70,12 @@ dependencies = [
"rustc_version 0.2.3", "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]] [[package]]
name = "bit-set" name = "bit-set"
version = "0.5.3" version = "0.5.3"
@ -115,6 +121,12 @@ version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
[[package]]
name = "cobs"
version = "0.2.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "67ba02a97a2bd10f4b59b25c7973101c79642302776489e030cd13cdab09ed15"
[[package]] [[package]]
name = "codespan-reporting" name = "codespan-reporting"
version = "0.11.1" version = "0.11.1"
@ -418,6 +430,16 @@ dependencies = [
"usbd-hid", "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]] [[package]]
name = "embedded-hal" name = "embedded-hal"
version = "0.2.7" version = "0.2.7"
@ -609,6 +631,7 @@ dependencies = [
"atomic-polyfill 0.1.11", "atomic-polyfill 0.1.11",
"hash32", "hash32",
"rustc_version 0.4.0", "rustc_version 0.4.0",
"serde",
"spin", "spin",
"stable_deref_trait", "stable_deref_trait",
] ]
@ -661,14 +684,18 @@ dependencies = [
"embassy-usb", "embassy-usb",
"embassy-usb-driver", "embassy-usb-driver",
"embassy-usb-logger", "embassy-usb-logger",
"embedded-alloc",
"embedded-hal 0.2.7", "embedded-hal 0.2.7",
"embedded-io", "embedded-io",
"futures", "futures",
"log", "log",
"pio", "pio",
"pio-proc", "pio-proc",
"postcard",
"ron",
"smart-leds", "smart-leds",
"static_cell", "static_cell",
"tgnt",
"usb-device", "usb-device",
"usbd-hid", "usbd-hid",
] ]
@ -711,6 +738,12 @@ version = "0.2.140"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "99227334921fae1a979cf0bfdfcc6b3e5ce376ef57e16fb6fb3ea2ed6095f80c" checksum = "99227334921fae1a979cf0bfdfcc6b3e5ce376ef57e16fb6fb3ea2ed6095f80c"
[[package]]
name = "linked_list_allocator"
version = "0.10.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9afa463f5405ee81cdb9cc2baf37e08ec7e4c8209442b5d72c04cfb2cd6e6286"
[[package]] [[package]]
name = "lock_api" name = "lock_api"
version = "0.4.9" version = "0.4.9"
@ -896,6 +929,17 @@ dependencies = [
"syn", "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]] [[package]]
name = "precomputed-hash" name = "precomputed-hash"
version = "0.1.1" version = "0.1.1"
@ -996,6 +1040,17 @@ dependencies = [
"bytemuck", "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]] [[package]]
name = "rp2040-pac2" name = "rp2040-pac2"
version = "0.1.0" version = "0.1.0"
@ -1058,9 +1113,23 @@ checksum = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3"
[[package]] [[package]]
name = "serde" name = "serde"
version = "1.0.154" version = "1.0.156"
source = "registry+https://github.com/rust-lang/crates.io-index" 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]] [[package]]
name = "siphasher" name = "siphasher"
@ -1176,6 +1245,14 @@ dependencies = [
"winapi-util", "winapi-util",
] ]
[[package]]
name = "tgnt"
version = "0.1.0"
source = "git+https://git.nubo.sh/hulthe/tgnt.git#e22588c2cb139864fcd3be1087350803343c622b"
dependencies = [
"serde",
]
[[package]] [[package]]
name = "thiserror" name = "thiserror"
version = "1.0.39" version = "1.0.39"

View File

@ -4,17 +4,16 @@ version = "0.1.0"
edition = "2021" edition = "2021"
[dependencies] [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 = "0.7.6"
cortex-m-rt = "0.7" cortex-m-rt = "0.7"
embedded-hal = "0.2.5" embedded-hal = "0.2.5"
#panic-halt = "0.2.0" #panic-halt = "0.2.0"
usb-device = "*" usb-device = "0.2.9"
usbd-hid = "0.6.1" usbd-hid = "0.6.1"
static_cell = "1.0.0" static_cell = "1.0.0"
embedded-io = { version = "*", features = ["async"] } embedded-io = { version = "*", features = ["async"] }
futures = { version = "0.3", default-features = false } futures = { version = "0.3", default-features = false }
embassy-executor = { git = "https://github.com/embassy-rs/embassy.git", features = ["log", "nightly", "integrated-timers" ] } 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-sync = { git = "https://github.com/embassy-rs/embassy.git", features = ["log", "nightly"] }
embassy-time = { git = "https://github.com/embassy-rs/embassy.git", features = ["log"] } 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 = { 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-logger = { git = "https://github.com/embassy-rs/embassy.git", features = [] }
embassy-usb-driver = { 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"] } 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" log = "0.4.17"
pio = "0.2.1" pio = "0.2.1"
pio-proc = "0.2.1" pio-proc = "0.2.1"
smart-leds = "0.3.0" 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] [features]
default = ["n-key-rollover"] default = ["n-key-rollover"]

View File

@ -8,12 +8,19 @@
//! updating `memory.x` ensures a rebuild of the application with the //! updating `memory.x` ensures a rebuild of the application with the
//! new memory settings. //! new memory settings.
use std::env;
use std::fs::File; use std::fs::File;
use std::io::Write; use std::io::Write;
use std::path::PathBuf; use std::path::PathBuf;
use std::{env, fs};
use tgnt::layer::Layer;
fn main() { fn main() {
memory();
serialize_layout();
}
fn memory() {
// Put `memory.x` in our output directory and ensure it's // Put `memory.x` in our output directory and ensure it's
// on the linker search path. // on the linker search path.
let out = &PathBuf::from(env::var_os("OUT_DIR").unwrap()); 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=-Tlink-rp.x");
//println!("cargo:rustc-link-arg-bins=-Tdefmt.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<Layer> = 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");
}

59
layers.ron Normal file
View File

@ -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),
],
)
]

1
src/.gitignore vendored Normal file
View File

@ -0,0 +1 @@
layers.pc

14
src/allocator.rs Normal file
View File

@ -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<u8>; HEAP_SIZE] = [MaybeUninit::uninit(); HEAP_SIZE];
unsafe { HEAP.init(HEAP_MEM.as_ptr() as usize, HEAP_SIZE) }
}

View File

@ -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_rp::gpio::{AnyPin, Input, Pin, Pull};
use embassy_time::{Duration, Timer}; use log::{error, warn};
use log::info; use tgnt::{button::Button, layer::Layer};
pub const ROWS: usize = 3; use crate::usb::keyboard::KB_REPORT;
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];
pub static TEST_KEYMAP: [[Button; COLS]; ROWS] = [ static CURRENT_LAYER: AtomicU16 = AtomicU16::new(0);
[
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,
],
];
pub struct Switch { pub struct KeyboardConfig {
state: AtomicBool, pub pins: [AnyPin; SWITCH_COUNT],
pub layers: Vec<Layer>,
} }
#[allow(dead_code)] impl KeyboardConfig {
impl Switch { pub async fn create(self) {
pub const fn new() -> Self { let spawner = Spawner::for_current_executor().await;
Switch {
state: AtomicBool::new(false), 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(),
)
} }
} }
pub fn press(&self) { for (i, pin) in self.pins.into_iter().enumerate() {
self.state.store(true, Ordering::Relaxed); if spawner.spawn(switch_task(i, pin, layers)).is_err() {
error!("failed to spawn switch task, pool_size mismatch?");
break;
} }
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)] const SWITCH_COUNT: usize = 18;
pub enum Button { #[embassy_executor::task(pool_size = 18)]
Key { keycode: u8 }, async fn switch_task(switch_num: usize, pin: AnyPin, layers: &'static [Layer]) -> ! {
} let _pin_nr = pin.pin();
#[embassy_executor::task(pool_size = 20)]
pub async fn monitor_switch(pin: AnyPin) -> ! {
let pin_nr = pin.pin();
let mut pin = Input::new(pin, Pull::Up); let mut pin = Input::new(pin, Pull::Up);
loop { loop {
// pins are pull-up, so when the switch is pressed they are brought low.
pin.wait_for_low().await; 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; pin.wait_for_high().await;
info!("pin {pin_nr} high"); 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 => {}
} }
#[allow(dead_code)] pin.wait_for_high().await;
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 /// Random functions for testing
#[allow(dead_code)] #[allow(dead_code)]
pub mod test { pub mod test {
use super::*; use tgnt::{button::Button, keys::Key};
pub fn letter_to_key(c: char) -> Option<Button> { pub fn letter_to_key(c: char) -> Button {
if !c.is_ascii() { if !c.is_ascii() {
return None; return Button::None;
} }
let c = c.to_ascii_uppercase(); let c = c.to_ascii_uppercase();
let key = match c { let key = match c {
'A' => Button::KEY_A, 'A' => Key::A,
'B' => Button::KEY_B, 'B' => Key::B,
'C' => Button::KEY_C, 'C' => Key::C,
'D' => Button::KEY_D, 'D' => Key::D,
'E' => Button::KEY_E, 'E' => Key::E,
'F' => Button::KEY_F, 'F' => Key::F,
'G' => Button::KEY_G, 'G' => Key::G,
'H' => Button::KEY_H, 'H' => Key::H,
'I' => Button::KEY_I, 'I' => Key::I,
'J' => Button::KEY_J, 'J' => Key::J,
'K' => Button::KEY_K, 'K' => Key::K,
'L' => Button::KEY_L, 'L' => Key::L,
'M' => Button::KEY_M, 'M' => Key::M,
'N' => Button::KEY_N, 'N' => Key::N,
'O' => Button::KEY_O, 'O' => Key::O,
'P' => Button::KEY_P, 'P' => Key::P,
'Q' => Button::KEY_Q, 'Q' => Key::Q,
'R' => Button::KEY_R, 'R' => Key::R,
'S' => Button::KEY_S, 'S' => Key::S,
'T' => Button::KEY_T, 'T' => Key::T,
'U' => Button::KEY_U, 'U' => Key::U,
'V' => Button::KEY_V, 'V' => Key::V,
'W' => Button::KEY_W, 'W' => Key::W,
'X' => Button::KEY_X, 'X' => Key::X,
'Y' => Button::KEY_Y, 'Y' => Key::Y,
'Z' => Button::KEY_Z, 'Z' => Key::Z,
' ' => Button::KEY_SPACE, ' ' => Key::Space,
'\n' => Button::KEY_RETURN, '\n' => Key::Return,
_ => { _ => {
log::info!("char {c:?} -> None"); log::info!("char {c:?} -> None");
return None; return Button::None;
} }
}; };
log::info!("char {c:?} -> {key:?}"); log::info!("char {c:?} -> {key:?}");
Some(key) Button::Key(key)
}
pub async fn type_string(s: &str) {
log::info!("typing {s:?}");
for c in s.chars() {
let Some(key) = letter_to_key(c) else {
continue;
};
for col in 0..COLS {
for row in 0..ROWS {
if TEST_KEYMAP[row][col] == key {
MATRIX[row][col].press();
Timer::after(Duration::from_millis(20)).await;
MATRIX[row][col].release();
Timer::after(Duration::from_millis(5)).await;
}
}
}
}
}
/// Press all keys at once
pub async fn rollover<const N: usize>(letters: [char; N]) {
log::info!("pressing all letters in {letters:?}");
let keys = letters.map(letter_to_key);
for key in &keys {
for col in 0..COLS {
for row in 0..ROWS {
if Some(&TEST_KEYMAP[row][col]) == key.as_ref() {
MATRIX[row][col].press();
}
}
}
}
Timer::after(Duration::from_millis(200)).await;
for key in &keys {
for col in 0..COLS {
for row in 0..ROWS {
if Some(&TEST_KEYMAP[row][col]) == key.as_ref() {
MATRIX[row][col].release();
}
}
}
}
} }
} }

View File

@ -10,23 +10,32 @@
#![no_main] #![no_main]
#![feature(type_alias_impl_trait)] #![feature(type_alias_impl_trait)]
extern crate alloc;
extern crate cortex_m_rt; extern crate cortex_m_rt;
mod allocator;
mod board; mod board;
mod keyboard; mod keyboard;
mod neopixel; mod neopixel;
mod panic_handler; mod panic_handler;
mod usb; mod usb;
mod util;
mod ws2812; mod ws2812;
use alloc::vec::Vec;
use board::Board; use board::Board;
use embassy_executor::Spawner; use embassy_executor::Spawner;
use embassy_rp::gpio::{Level, Output, Pin}; use embassy_rp::gpio::{Level, Output, Pin};
use embassy_time::{Duration, Timer}; use embassy_time::{Duration, Timer};
use tgnt::layer::Layer;
use ws2812::Rgb; use ws2812::Rgb;
use crate::keyboard::KeyboardConfig;
#[embassy_executor::main] #[embassy_executor::main]
async fn main(spawner: Spawner) { async fn main(spawner: Spawner) {
allocator::init();
let p = embassy_rp::init(Default::default()); let p = embassy_rp::init(Default::default());
let board = Board { let board = Board {
@ -85,27 +94,42 @@ async fn main(spawner: Spawner) {
Timer::after(Duration::from_millis(3000)).await; Timer::after(Duration::from_millis(3000)).await;
spawner.must_spawn(keyboard::monitor_switch(board.a0.degrade())); let layers = include_bytes!("layers.pc");
spawner.must_spawn(keyboard::monitor_switch(board.a1.degrade())); let Ok(layers): Result<Vec<Layer>, _> = postcard::from_bytes(layers) else {
spawner.must_spawn(keyboard::monitor_switch(board.a2.degrade())); log::error!("Failed to deserialize layer config");
spawner.must_spawn(keyboard::monitor_switch(board.a3.degrade())); loop_forever().await
spawner.must_spawn(keyboard::monitor_switch(board.d2.degrade())); };
spawner.must_spawn(keyboard::monitor_switch(board.d3.degrade()));
spawner.must_spawn(keyboard::monitor_switch(board.d4.degrade())); let keyboard = KeyboardConfig {
spawner.must_spawn(keyboard::monitor_switch(board.d7.degrade())); layers,
spawner.must_spawn(keyboard::monitor_switch(board.d9.degrade())); pins: [
spawner.must_spawn(keyboard::monitor_switch(board.d10.degrade())); // row 1
spawner.must_spawn(keyboard::monitor_switch(board.d11.degrade())); board.d24.degrade(),
spawner.must_spawn(keyboard::monitor_switch(board.d12.degrade())); board.a3.degrade(),
spawner.must_spawn(keyboard::monitor_switch(board.d24.degrade())); board.a2.degrade(),
spawner.must_spawn(keyboard::monitor_switch(board.d25.degrade())); board.a1.degrade(),
spawner.must_spawn(keyboard::monitor_switch(board.scl.degrade())); board.a0.degrade(),
spawner.must_spawn(keyboard::monitor_switch(board.sda.degrade())); // row 2
spawner.must_spawn(keyboard::monitor_switch(board.mosi.degrade())); board.d25.degrade(),
spawner.must_spawn(keyboard::monitor_switch(board.miso.degrade())); board.sck.degrade(),
board.mosi.degrade(),
board.miso.degrade(),
board.d2.degrade(),
// row 3
board.d12.degrade(),
board.d11.degrade(),
board.d10.degrade(),
board.d9.degrade(),
board.d3.degrade(),
// thumbpad
board.d7.degrade(),
board.scl.degrade(),
board.sda.degrade(),
],
};
keyboard.create().await;
//keyboard::test::type_string("Hello there!\n").await;
//keyboard::test::rollover(['h', 'e', 'l', 'o', 't', 'r', 'a', 'b', 'c', 'd', 'i']).await;
for w in 0usize.. { for w in 0usize.. {
neopixel.write(&[wheel(w as u8)]).await; neopixel.write(&[wheel(w as u8)]).await;
neopixels_d5 neopixels_d5
@ -117,7 +141,12 @@ async fn main(spawner: Spawner) {
]) ])
.await; .await;
Timer::after(Duration::from_millis(10)).await; Timer::after(Duration::from_millis(10)).await;
//Timer::after(Duration::from_secs(10)).await; }
}
async fn loop_forever() -> ! {
loop {
Timer::after(Duration::from_secs(1)).await;
} }
} }

View File

@ -2,6 +2,7 @@ pub mod report;
use embassy_executor::Spawner; use embassy_executor::Spawner;
use embassy_rp::{peripherals::USB, usb::Driver}; use embassy_rp::{peripherals::USB, usb::Driver};
use embassy_sync::mutex::Mutex;
use embassy_time::{Duration, Timer}; use embassy_time::{Duration, Timer};
use embassy_usb::{ use embassy_usb::{
class::hid::{self, HidReaderWriter, ReadError, ReportId, RequestHandler}, class::hid::{self, HidReaderWriter, ReadError, ReportId, RequestHandler},
@ -13,8 +14,8 @@ use static_cell::StaticCell;
use usbd_hid::descriptor::{MouseReport, SerializedDescriptor}; use usbd_hid::descriptor::{MouseReport, SerializedDescriptor};
use crate::{ use crate::{
keyboard::{Button, COLS, MATRIX, ROWS, TEST_KEYMAP},
usb::keyboard::report::{KeyboardReport, EMPTY_KEYBOARD_REPORT}, usb::keyboard::report::{KeyboardReport, EMPTY_KEYBOARD_REPORT},
util::CS,
}; };
use super::MAX_PACKET_SIZE; use super::MAX_PACKET_SIZE;
@ -23,6 +24,8 @@ struct Handler;
static CONTEXT: StaticCell<Context> = StaticCell::new(); static CONTEXT: StaticCell<Context> = StaticCell::new();
pub static KB_REPORT: Mutex<CS, KeyboardReport> = Mutex::new(EMPTY_KEYBOARD_REPORT);
struct Context { struct Context {
handler: Handler, handler: Handler,
state: hid::State<'static>, state: hid::State<'static>,
@ -94,36 +97,7 @@ async fn keyboard_test(mut stream: HidStream, _handler: &'static Handler) -> Res
loop { loop {
Timer::after(Duration::from_millis(2)).await; Timer::after(Duration::from_millis(2)).await;
let keymap = &TEST_KEYMAP; let report = KB_REPORT.lock().await.clone();
let mut report = EMPTY_KEYBOARD_REPORT;
#[cfg(not(feature = "n-key-rollover"))]
let mut i = 0;
#[allow(unused_labels)]
'keyscan: for col in 0..COLS {
for row in 0..ROWS {
if !MATRIX[row][col].is_pressed() {
continue;
}
let &Button::Key { keycode } = &keymap[row][col];
// else { continue; };
#[cfg(feature = "n-key-rollover")]
report.set_key(keycode);
#[cfg(not(feature = "n-key-rollover"))]
{
report.keycodes[i] = keycode;
i += 1;
if i >= report.keycodes.len() {
break 'keyscan;
}
}
}
}
if report.keycodes != EMPTY_KEYBOARD_REPORT.keycodes { if report.keycodes != EMPTY_KEYBOARD_REPORT.keycodes {
log::debug!("keys: {:x?}", report.keycodes); log::debug!("keys: {:x?}", report.keycodes);
} }

View File

@ -5,7 +5,7 @@
/// keyboard LEDs. /// keyboard LEDs.
/// ///
/// Unlike usbd_hids KeyboardReport, this one supports N-key rollover. /// Unlike usbd_hids KeyboardReport, this one supports N-key rollover.
#[derive(PartialEq, Eq)] #[derive(Clone, PartialEq, Eq)]
#[cfg(feature = "n-key-rollover")] #[cfg(feature = "n-key-rollover")]
pub struct KeyboardReport { pub struct KeyboardReport {
pub modifier: u8, pub modifier: u8,
@ -14,6 +14,7 @@ pub struct KeyboardReport {
pub keycodes: [u8; 13], pub keycodes: [u8; 13],
} }
use tgnt::keys::Key;
#[cfg(not(feature = "n-key-rollover"))] #[cfg(not(feature = "n-key-rollover"))]
pub use usbd_hid::descriptor::KeyboardReport; pub use usbd_hid::descriptor::KeyboardReport;
@ -33,19 +34,32 @@ pub const EMPTY_KEYBOARD_REPORT: KeyboardReport = KeyboardReport {
#[cfg(feature = "n-key-rollover")] #[cfg(feature = "n-key-rollover")]
impl KeyboardReport { impl KeyboardReport {
pub fn set_key(&mut self, keycode: u8) { pub fn set_key(&mut self, key: Key, pressed: bool) {
log::info!("setting keycode: {keycode}"); let keycode = u8::from(key);
log::debug!("setting key: {key:?} ({keycode:x})");
let byte = keycode >> 3; let byte = keycode >> 3;
let bit = keycode & 0b111; let bit = keycode & 0b111;
let mask = 1 << bit; let mask = 1 << bit;
if let Some(k) = self.keycodes.get_mut(byte as usize) { if let Some(k) = self.keycodes.get_mut(byte as usize) {
if pressed {
*k |= mask; *k |= mask;
} else {
*k &= !mask;
}
} else { } else {
log::warn!("Tried to set out-of-range keycode: {keycode:x}"); log::warn!("Tried to set out-of-range keycode: {keycode:x}");
} }
} }
pub fn press_key(&mut self, key: Key) {
self.set_key(key, true)
}
pub fn release_key(&mut self, key: Key) {
self.set_key(key, false)
}
pub fn serialized(&self) -> [u8; 14] { pub fn serialized(&self) -> [u8; 14] {
let [a, b, c, d, e, f, g, h, i, j, k, l, m] = self.keycodes; let [a, b, c, d, e, f, g, h, i, j, k, l, m] = self.keycodes;
[self.modifier, a, b, c, d, e, f, g, h, i, j, k, l, m] [self.modifier, a, b, c, d, e, f, g, h, i, j, k, l, m]

View File

@ -1,8 +1,10 @@
use crate::util::CS;
use super::MAX_PACKET_SIZE; use super::MAX_PACKET_SIZE;
use core::fmt::Write as WriteFmt; use core::fmt::Write as WriteFmt;
use embassy_executor::Spawner; use embassy_executor::Spawner;
use embassy_rp::{peripherals::USB, usb::Driver}; use embassy_rp::{peripherals::USB, usb::Driver};
use embassy_sync::{blocking_mutex::raw::CriticalSectionRawMutex, pipe::Pipe}; use embassy_sync::pipe::Pipe;
use embassy_time::Instant; use embassy_time::Instant;
use embassy_usb::{ use embassy_usb::{
class::cdc_acm::{self, CdcAcmClass}, class::cdc_acm::{self, CdcAcmClass},
@ -12,7 +14,7 @@ use log::{Metadata, Record};
use static_cell::StaticCell; use static_cell::StaticCell;
pub const BUFFER_SIZE: usize = 16 * 1024; pub const BUFFER_SIZE: usize = 16 * 1024;
static BUFFER: Pipe<CriticalSectionRawMutex, BUFFER_SIZE> = Pipe::new(); static BUFFER: Pipe<CS, BUFFER_SIZE> = Pipe::new();
static STATE: StaticCell<cdc_acm::State<'static>> = StaticCell::new(); static STATE: StaticCell<cdc_acm::State<'static>> = StaticCell::new();
struct UsbLogger; struct UsbLogger;
@ -62,7 +64,7 @@ impl log::Log for UsbLogger {
let s = now.as_secs(); let s = now.as_secs();
let ms = now.as_millis() % 1000; let ms = now.as_millis() % 1000;
let level = record.metadata().level(); let level = record.metadata().level();
let _ = write!(w, "[{s}.{ms:04}] ({level}) {}\n", record.args()); let _ = writeln!(w, "[{s}.{ms:04}] ({level}) {}", record.args());
} }
} }

3
src/util.rs Normal file
View File

@ -0,0 +1,3 @@
use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex;
pub type CS = CriticalSectionRawMutex;