Implemented keyboard driver
This commit is contained in:
parent
2677534c8e
commit
4853df4cc9
3 changed files with 317 additions and 0 deletions
22
Cargo.lock
generated
22
Cargo.lock
generated
|
|
@ -70,6 +70,16 @@ version = "2.9.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "1b8e56985ec62d17e9c1001dc89c88ecd7dc08e47eba5ec7c29c7b5eeecde967"
|
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]]
|
[[package]]
|
||||||
name = "block-buffer"
|
name = "block-buffer"
|
||||||
version = "0.10.4"
|
version = "0.10.4"
|
||||||
|
|
@ -101,6 +111,8 @@ checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b"
|
||||||
name = "cardputer-bsc-nostd"
|
name = "cardputer-bsc-nostd"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
|
"bitflags",
|
||||||
|
"bitmask-enum",
|
||||||
"embedded-hal-bus",
|
"embedded-hal-bus",
|
||||||
"esp-hal",
|
"esp-hal",
|
||||||
"mipidsi",
|
"mipidsi",
|
||||||
|
|
@ -784,6 +796,16 @@ dependencies = [
|
||||||
"wasm-bindgen",
|
"wasm-bindgen",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "keyboard_test"
|
||||||
|
version = "0.1.0"
|
||||||
|
dependencies = [
|
||||||
|
"cardputer-bsc-nostd",
|
||||||
|
"esp-bootloader-esp-idf",
|
||||||
|
"esp-hal",
|
||||||
|
"esp-println",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "libc"
|
name = "libc"
|
||||||
version = "0.2.174"
|
version = "0.2.174"
|
||||||
|
|
|
||||||
|
|
@ -15,6 +15,8 @@ debug = true # Symbols are nice and they don't increase the size on Flash
|
||||||
opt-level = "z"
|
opt-level = "z"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
|
bitflags = "2.9.1"
|
||||||
|
bitmask-enum = "2.2.5"
|
||||||
embedded-hal-bus = "0.3.0"
|
embedded-hal-bus = "0.3.0"
|
||||||
esp-hal = {version = "=1.0.0-beta.1", features = ["esp32s3", "unstable"]}
|
esp-hal = {version = "=1.0.0-beta.1", features = ["esp32s3", "unstable"]}
|
||||||
mipidsi = "0.9.0"
|
mipidsi = "0.9.0"
|
||||||
|
|
|
||||||
293
src/keyboard.rs
293
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
Add table
Add a link
Reference in a new issue