Move away from matrix-based key layout
This commit is contained in:
318
src/keyboard.rs
318
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<Layer>,
|
||||
}
|
||||
|
||||
#[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<Button> {
|
||||
pub fn letter_to_key(c: char) -> Button {
|
||||
if !c.is_ascii() {
|
||||
return None;
|
||||
return Button::None;
|
||||
}
|
||||
|
||||
let c = c.to_ascii_uppercase();
|
||||
|
||||
let key = match c {
|
||||
'A' => Button::KEY_A,
|
||||
'B' => Button::KEY_B,
|
||||
'C' => Button::KEY_C,
|
||||
'D' => Button::KEY_D,
|
||||
'E' => Button::KEY_E,
|
||||
'F' => Button::KEY_F,
|
||||
'G' => Button::KEY_G,
|
||||
'H' => Button::KEY_H,
|
||||
'I' => Button::KEY_I,
|
||||
'J' => Button::KEY_J,
|
||||
'K' => Button::KEY_K,
|
||||
'L' => Button::KEY_L,
|
||||
'M' => Button::KEY_M,
|
||||
'N' => Button::KEY_N,
|
||||
'O' => Button::KEY_O,
|
||||
'P' => Button::KEY_P,
|
||||
'Q' => Button::KEY_Q,
|
||||
'R' => Button::KEY_R,
|
||||
'S' => Button::KEY_S,
|
||||
'T' => Button::KEY_T,
|
||||
'U' => Button::KEY_U,
|
||||
'V' => Button::KEY_V,
|
||||
'W' => Button::KEY_W,
|
||||
'X' => Button::KEY_X,
|
||||
'Y' => Button::KEY_Y,
|
||||
'Z' => Button::KEY_Z,
|
||||
' ' => Button::KEY_SPACE,
|
||||
'\n' => Button::KEY_RETURN,
|
||||
'A' => Key::A,
|
||||
'B' => Key::B,
|
||||
'C' => Key::C,
|
||||
'D' => Key::D,
|
||||
'E' => Key::E,
|
||||
'F' => Key::F,
|
||||
'G' => Key::G,
|
||||
'H' => Key::H,
|
||||
'I' => Key::I,
|
||||
'J' => Key::J,
|
||||
'K' => Key::K,
|
||||
'L' => Key::L,
|
||||
'M' => Key::M,
|
||||
'N' => Key::N,
|
||||
'O' => Key::O,
|
||||
'P' => Key::P,
|
||||
'Q' => Key::Q,
|
||||
'R' => Key::R,
|
||||
'S' => Key::S,
|
||||
'T' => Key::T,
|
||||
'U' => Key::U,
|
||||
'V' => Key::V,
|
||||
'W' => Key::W,
|
||||
'X' => Key::X,
|
||||
'Y' => Key::Y,
|
||||
'Z' => Key::Z,
|
||||
' ' => Key::Space,
|
||||
'\n' => Key::Return,
|
||||
_ => {
|
||||
log::info!("char {c:?} -> None");
|
||||
return None;
|
||||
return Button::None;
|
||||
}
|
||||
};
|
||||
|
||||
log::info!("char {c:?} -> {key:?}");
|
||||
|
||||
Some(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();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Button::Key(key)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user