Implemented keyboard driver

This commit is contained in:
ZacJW 2025-07-13 19:19:07 +01:00
parent 2677534c8e
commit 4853df4cc9
3 changed files with 317 additions and 0 deletions

22
Cargo.lock generated
View file

@ -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"

View file

@ -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"

View file

@ -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;
}
}