From 4853df4cc93c8ebd3df5ffe0193dbfc6215a4f24 Mon Sep 17 00:00:00 2001 From: ZacJW Date: Sun, 13 Jul 2025 19:19:07 +0100 Subject: [PATCH] Implemented keyboard driver --- Cargo.lock | 22 ++++ Cargo.toml | 2 + src/keyboard.rs | 293 ++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 317 insertions(+) diff --git a/Cargo.lock b/Cargo.lock index 031c0f8..b034a16 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -70,6 +70,16 @@ version = "2.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1b8e56985ec62d17e9c1001dc89c88ecd7dc08e47eba5ec7c29c7b5eeecde967" +[[package]] +name = "bitmask-enum" +version = "2.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6cbbb8f56245b5a479b30a62cdc86d26e2f35c2b9f594bc4671654b03851380" +dependencies = [ + "quote", + "syn", +] + [[package]] name = "block-buffer" version = "0.10.4" @@ -101,6 +111,8 @@ checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" name = "cardputer-bsc-nostd" version = "0.1.0" dependencies = [ + "bitflags", + "bitmask-enum", "embedded-hal-bus", "esp-hal", "mipidsi", @@ -784,6 +796,16 @@ dependencies = [ "wasm-bindgen", ] +[[package]] +name = "keyboard_test" +version = "0.1.0" +dependencies = [ + "cardputer-bsc-nostd", + "esp-bootloader-esp-idf", + "esp-hal", + "esp-println", +] + [[package]] name = "libc" version = "0.2.174" diff --git a/Cargo.toml b/Cargo.toml index cf9d368..2cb70eb 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -15,6 +15,8 @@ debug = true # Symbols are nice and they don't increase the size on Flash opt-level = "z" [dependencies] +bitflags = "2.9.1" +bitmask-enum = "2.2.5" embedded-hal-bus = "0.3.0" esp-hal = {version = "=1.0.0-beta.1", features = ["esp32s3", "unstable"]} mipidsi = "0.9.0" diff --git a/src/keyboard.rs b/src/keyboard.rs index e69de29..fad3323 100644 --- a/src/keyboard.rs +++ b/src/keyboard.rs @@ -0,0 +1,293 @@ +use bitmask_enum::bitmask; + +/// A set of physical keys on the cardputer's keyboard +/// +/// This type is a bit flag (i.e. each bit corresponds to a different key). +#[bitmask(u64)] +#[bitmask_config(flags_iter)] +pub enum Key { + LeftOpt, + Z, + C, + B, + M, + Period, + Space, + _Dummy0, + LeftShift, + S, + F, + H, + K, + SemiColon, + Enter, + _Dummy1, + Q, + E, + T, + U, + O, + OpenSquareBracket, + Backslash, + _Dummy2, + One, + Three, + Five, + Seven, + Nine, + Minus, + Backspace, + _Dummy3, + LeftCtrl, + LeftAlt, + X, + V, + N, + Comma, + Slash, + _Dummy4, + LeftFn, + A, + D, + G, + J, + L, + Quote, + _Dummy5, + Tab, + W, + R, + Y, + I, + P, + CloseSquareBracket, + _Dummy6, + Backquote, + Two, + Four, + Six, + Eight, + Zero, + Equal, + _Dummy7, +} + +/// The type for controlling the cardputer's keyboard +/// +/// `Keyboard` tracks and buffers key presses and releases, +/// making it simple to react to only the changes in a key's state. +/// +/// Example usage: +/// ```rust +/// let mut keyboard = cardputer_bsc_nostd::keyboard::Keyboard::new( +/// peripherals.GPIO8, +/// peripherals.GPIO9, +/// peripherals.GPIO11, +/// peripherals.GPIO13, +/// peripherals.GPIO15, +/// peripherals.GPIO3, +/// peripherals.GPIO4, +/// peripherals.GPIO5, +/// peripherals.GPIO6, +/// peripherals.GPIO7, +/// ); +/// loop { +/// keyboard.scan(); +/// let key_set = keyboard.pressed_keys(); +/// if !key_set.is_none() { +/// for &(key_name, key) in cardputer_bsc_nostd::keyboard::Key::flags() { +/// if key_set.contains(key) { +/// println!("Pressed {key_name}"); +/// } +/// } +/// } +/// keyboard.clear_pressed_keys(); +/// let key_set = keyboard.released_keys(); +/// if !key_set.is_none() { +/// for &(key_name, key) in cardputer_bsc_nostd::keyboard::Key::flags() { +/// if key_set.contains(key) { +/// println!("Released {key_name}"); +/// } +/// } +/// } +/// keyboard.clear_released_keys(); +/// esp_hal::delay::Delay::new().delay_millis(10); +/// } +/// ``` +pub struct Keyboard<'a> { + a0: esp_hal::gpio::Output<'a>, + a1: esp_hal::gpio::Output<'a>, + a2: esp_hal::gpio::Output<'a>, + y0: esp_hal::gpio::Input<'a>, + y1: esp_hal::gpio::Input<'a>, + y2: esp_hal::gpio::Input<'a>, + y3: esp_hal::gpio::Input<'a>, + y4: esp_hal::gpio::Input<'a>, + y5: esp_hal::gpio::Input<'a>, + y6: esp_hal::gpio::Input<'a>, + last_state: Key, + pressed: Key, + released: Key, +} + +impl<'a> Keyboard<'a> { + /// Initialises the cardputer's keyboard. + pub fn new( + a0: esp_hal::peripherals::GPIO8<'a>, + a1: esp_hal::peripherals::GPIO9<'a>, + a2: esp_hal::peripherals::GPIO11<'a>, + y0: esp_hal::peripherals::GPIO13<'a>, + y1: esp_hal::peripherals::GPIO15<'a>, + y2: esp_hal::peripherals::GPIO3<'a>, + y3: esp_hal::peripherals::GPIO4<'a>, + y4: esp_hal::peripherals::GPIO5<'a>, + y5: esp_hal::peripherals::GPIO6<'a>, + y6: esp_hal::peripherals::GPIO7<'a>, + ) -> Self { + let a0 = esp_hal::gpio::Output::new( + a0, + esp_hal::gpio::Level::Low, + esp_hal::gpio::OutputConfig::default() + .with_drive_mode(esp_hal::gpio::DriveMode::PushPull), + ); + let a1 = esp_hal::gpio::Output::new( + a1, + esp_hal::gpio::Level::Low, + esp_hal::gpio::OutputConfig::default() + .with_drive_mode(esp_hal::gpio::DriveMode::PushPull), + ); + let a2 = esp_hal::gpio::Output::new( + a2, + esp_hal::gpio::Level::Low, + esp_hal::gpio::OutputConfig::default() + .with_drive_mode(esp_hal::gpio::DriveMode::PushPull), + ); + let y0 = esp_hal::gpio::Input::new( + y0, + esp_hal::gpio::InputConfig::default().with_pull(esp_hal::gpio::Pull::Up), + ); + let y1 = esp_hal::gpio::Input::new( + y1, + esp_hal::gpio::InputConfig::default().with_pull(esp_hal::gpio::Pull::Up), + ); + let y2 = esp_hal::gpio::Input::new( + y2, + esp_hal::gpio::InputConfig::default().with_pull(esp_hal::gpio::Pull::Up), + ); + let y3 = esp_hal::gpio::Input::new( + y3, + esp_hal::gpio::InputConfig::default().with_pull(esp_hal::gpio::Pull::Up), + ); + let y4 = esp_hal::gpio::Input::new( + y4, + esp_hal::gpio::InputConfig::default().with_pull(esp_hal::gpio::Pull::Up), + ); + let y5 = esp_hal::gpio::Input::new( + y5, + esp_hal::gpio::InputConfig::default().with_pull(esp_hal::gpio::Pull::Up), + ); + let y6 = esp_hal::gpio::Input::new( + y6, + esp_hal::gpio::InputConfig::default().with_pull(esp_hal::gpio::Pull::Up), + ); + + Self { + a0, + a1, + a2, + y0, + y1, + y2, + y3, + y4, + y5, + y6, + last_state: Key::none(), + pressed: Key::none(), + released: Key::none(), + } + } + + /// Scans the keyboard matrix and tracks which keys have been pressed and released. + /// + /// This function should be called often (100Hz) otherwise it may miss key presses. + pub fn scan(&mut self) { + let mut key_flags = 0_u64; + for i in 0..8 { + self.a0 + .set_level(esp_hal::gpio::Level::from(i & 0b00000001 != 0)); + self.a1 + .set_level(esp_hal::gpio::Level::from(i & 0b00000010 != 0)); + self.a2 + .set_level(esp_hal::gpio::Level::from(i & 0b00000100 != 0)); + + let inputs: [esp_hal::gpio::Level; 7] = [ + self.y0.level(), + self.y1.level(), + self.y2.level(), + self.y3.level(), + self.y4.level(), + self.y5.level(), + self.y6.level(), + ]; + + let major_offset = i * 8; + + for (minor_offset, decoded) in inputs.iter().enumerate() { + let bit = match decoded { + esp_hal::gpio::Level::Low => 1_u64, + esp_hal::gpio::Level::High => 0_u64, + }; + key_flags |= bit << (major_offset + minor_offset); + } + } + let key = Key { bits: key_flags }; + + let changes = key.xor(self.last_state); + + let new_presses = changes.and(key); + self.pressed |= new_presses; + + let new_releases = changes.and(!key); + self.released |= new_releases; + + self.last_state = key; + } + + /// Returns the keys that were held when [scan][Self::scan] was last called. + pub fn held_keys(&self) -> Key { + self.last_state + } + + /// Returns buffered key presses that haven't already been cleared with + /// [clear_pressed_keys][Self::clear_pressed_keys] or [clear_some_pressed_keys][Self::clear_some_pressed_keys] + pub fn pressed_keys(&self) -> Key { + self.pressed + } + + /// Clears the key press buffer + pub fn clear_pressed_keys(&mut self) { + self.pressed = Key::none() + } + + /// Clears specific keys from the key press buffer + pub fn clear_some_pressed_keys(&mut self, keys_to_clear: Key) { + self.pressed &= !keys_to_clear; + } + + /// Returns buffered key releases that haven't already been cleared with + /// [clear_released_keys][Self::clear_released_keys] or [clear_some_released_keys][Self::clear_some_released_keys] + pub fn released_keys(&self) -> Key { + self.released + } + + /// Clears the key release buffer + pub fn clear_released_keys(&mut self) { + self.released = Key::none() + } + + /// Clears specific keys from the key release buffer + pub fn clear_some_released_keys(&mut self, keys_to_clear: Key) { + self.released &= !keys_to_clear; + } +}