Move away from matrix-based key layout
This commit is contained in:
1
src/.gitignore
vendored
Normal file
1
src/.gitignore
vendored
Normal file
@ -0,0 +1 @@
|
||||
layers.pc
|
||||
14
src/allocator.rs
Normal file
14
src/allocator.rs
Normal 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) }
|
||||
}
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
||||
71
src/main.rs
71
src/main.rs
@ -10,23 +10,32 @@
|
||||
#![no_main]
|
||||
#![feature(type_alias_impl_trait)]
|
||||
|
||||
extern crate alloc;
|
||||
extern crate cortex_m_rt;
|
||||
|
||||
mod allocator;
|
||||
mod board;
|
||||
mod keyboard;
|
||||
mod neopixel;
|
||||
mod panic_handler;
|
||||
mod usb;
|
||||
mod util;
|
||||
mod ws2812;
|
||||
|
||||
use alloc::vec::Vec;
|
||||
use board::Board;
|
||||
use embassy_executor::Spawner;
|
||||
use embassy_rp::gpio::{Level, Output, Pin};
|
||||
use embassy_time::{Duration, Timer};
|
||||
use tgnt::layer::Layer;
|
||||
use ws2812::Rgb;
|
||||
|
||||
use crate::keyboard::KeyboardConfig;
|
||||
|
||||
#[embassy_executor::main]
|
||||
async fn main(spawner: Spawner) {
|
||||
allocator::init();
|
||||
|
||||
let p = embassy_rp::init(Default::default());
|
||||
|
||||
let board = Board {
|
||||
@ -85,27 +94,42 @@ async fn main(spawner: Spawner) {
|
||||
|
||||
Timer::after(Duration::from_millis(3000)).await;
|
||||
|
||||
spawner.must_spawn(keyboard::monitor_switch(board.a0.degrade()));
|
||||
spawner.must_spawn(keyboard::monitor_switch(board.a1.degrade()));
|
||||
spawner.must_spawn(keyboard::monitor_switch(board.a2.degrade()));
|
||||
spawner.must_spawn(keyboard::monitor_switch(board.a3.degrade()));
|
||||
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()));
|
||||
spawner.must_spawn(keyboard::monitor_switch(board.d7.degrade()));
|
||||
spawner.must_spawn(keyboard::monitor_switch(board.d9.degrade()));
|
||||
spawner.must_spawn(keyboard::monitor_switch(board.d10.degrade()));
|
||||
spawner.must_spawn(keyboard::monitor_switch(board.d11.degrade()));
|
||||
spawner.must_spawn(keyboard::monitor_switch(board.d12.degrade()));
|
||||
spawner.must_spawn(keyboard::monitor_switch(board.d24.degrade()));
|
||||
spawner.must_spawn(keyboard::monitor_switch(board.d25.degrade()));
|
||||
spawner.must_spawn(keyboard::monitor_switch(board.scl.degrade()));
|
||||
spawner.must_spawn(keyboard::monitor_switch(board.sda.degrade()));
|
||||
spawner.must_spawn(keyboard::monitor_switch(board.mosi.degrade()));
|
||||
spawner.must_spawn(keyboard::monitor_switch(board.miso.degrade()));
|
||||
let layers = include_bytes!("layers.pc");
|
||||
let Ok(layers): Result<Vec<Layer>, _> = postcard::from_bytes(layers) else {
|
||||
log::error!("Failed to deserialize layer config");
|
||||
loop_forever().await
|
||||
};
|
||||
|
||||
let keyboard = KeyboardConfig {
|
||||
layers,
|
||||
pins: [
|
||||
// row 1
|
||||
board.d24.degrade(),
|
||||
board.a3.degrade(),
|
||||
board.a2.degrade(),
|
||||
board.a1.degrade(),
|
||||
board.a0.degrade(),
|
||||
// row 2
|
||||
board.d25.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.. {
|
||||
neopixel.write(&[wheel(w as u8)]).await;
|
||||
neopixels_d5
|
||||
@ -117,7 +141,12 @@ async fn main(spawner: Spawner) {
|
||||
])
|
||||
.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;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -2,6 +2,7 @@ pub mod report;
|
||||
|
||||
use embassy_executor::Spawner;
|
||||
use embassy_rp::{peripherals::USB, usb::Driver};
|
||||
use embassy_sync::mutex::Mutex;
|
||||
use embassy_time::{Duration, Timer};
|
||||
use embassy_usb::{
|
||||
class::hid::{self, HidReaderWriter, ReadError, ReportId, RequestHandler},
|
||||
@ -13,8 +14,8 @@ use static_cell::StaticCell;
|
||||
use usbd_hid::descriptor::{MouseReport, SerializedDescriptor};
|
||||
|
||||
use crate::{
|
||||
keyboard::{Button, COLS, MATRIX, ROWS, TEST_KEYMAP},
|
||||
usb::keyboard::report::{KeyboardReport, EMPTY_KEYBOARD_REPORT},
|
||||
util::CS,
|
||||
};
|
||||
|
||||
use super::MAX_PACKET_SIZE;
|
||||
@ -23,6 +24,8 @@ struct Handler;
|
||||
|
||||
static CONTEXT: StaticCell<Context> = StaticCell::new();
|
||||
|
||||
pub static KB_REPORT: Mutex<CS, KeyboardReport> = Mutex::new(EMPTY_KEYBOARD_REPORT);
|
||||
|
||||
struct Context {
|
||||
handler: Handler,
|
||||
state: hid::State<'static>,
|
||||
@ -94,36 +97,7 @@ async fn keyboard_test(mut stream: HidStream, _handler: &'static Handler) -> Res
|
||||
loop {
|
||||
Timer::after(Duration::from_millis(2)).await;
|
||||
|
||||
let keymap = &TEST_KEYMAP;
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let report = KB_REPORT.lock().await.clone();
|
||||
if report.keycodes != EMPTY_KEYBOARD_REPORT.keycodes {
|
||||
log::debug!("keys: {:x?}", report.keycodes);
|
||||
}
|
||||
|
||||
@ -5,7 +5,7 @@
|
||||
/// keyboard LEDs.
|
||||
///
|
||||
/// Unlike usbd_hids KeyboardReport, this one supports N-key rollover.
|
||||
#[derive(PartialEq, Eq)]
|
||||
#[derive(Clone, PartialEq, Eq)]
|
||||
#[cfg(feature = "n-key-rollover")]
|
||||
pub struct KeyboardReport {
|
||||
pub modifier: u8,
|
||||
@ -14,6 +14,7 @@ pub struct KeyboardReport {
|
||||
pub keycodes: [u8; 13],
|
||||
}
|
||||
|
||||
use tgnt::keys::Key;
|
||||
#[cfg(not(feature = "n-key-rollover"))]
|
||||
pub use usbd_hid::descriptor::KeyboardReport;
|
||||
|
||||
@ -33,19 +34,32 @@ pub const EMPTY_KEYBOARD_REPORT: KeyboardReport = KeyboardReport {
|
||||
|
||||
#[cfg(feature = "n-key-rollover")]
|
||||
impl KeyboardReport {
|
||||
pub fn set_key(&mut self, keycode: u8) {
|
||||
log::info!("setting keycode: {keycode}");
|
||||
pub fn set_key(&mut self, key: Key, pressed: bool) {
|
||||
let keycode = u8::from(key);
|
||||
log::debug!("setting key: {key:?} ({keycode:x})");
|
||||
let byte = keycode >> 3;
|
||||
let bit = keycode & 0b111;
|
||||
let mask = 1 << bit;
|
||||
|
||||
if let Some(k) = self.keycodes.get_mut(byte as usize) {
|
||||
*k |= mask;
|
||||
if pressed {
|
||||
*k |= mask;
|
||||
} else {
|
||||
*k &= !mask;
|
||||
}
|
||||
} else {
|
||||
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] {
|
||||
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]
|
||||
|
||||
@ -1,8 +1,10 @@
|
||||
use crate::util::CS;
|
||||
|
||||
use super::MAX_PACKET_SIZE;
|
||||
use core::fmt::Write as WriteFmt;
|
||||
use embassy_executor::Spawner;
|
||||
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_usb::{
|
||||
class::cdc_acm::{self, CdcAcmClass},
|
||||
@ -12,7 +14,7 @@ use log::{Metadata, Record};
|
||||
use static_cell::StaticCell;
|
||||
|
||||
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();
|
||||
|
||||
struct UsbLogger;
|
||||
@ -62,7 +64,7 @@ impl log::Log for UsbLogger {
|
||||
let s = now.as_secs();
|
||||
let ms = now.as_millis() % 1000;
|
||||
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
3
src/util.rs
Normal file
@ -0,0 +1,3 @@
|
||||
use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex;
|
||||
|
||||
pub type CS = CriticalSectionRawMutex;
|
||||
Reference in New Issue
Block a user