diff --git a/src/keypad.rs b/src/keypad.rs
new file mode 100644
index 0000000..07c604c
--- /dev/null
+++ b/src/keypad.rs
@@ -0,0 +1,351 @@
+#[doc(hidden)]
+pub extern crate core as _core;
+
+use core::cell::RefCell;
+
+use esp_hal::gpio::{Input, InputPin, OutputOpenDrain, OutputPin};
+
+/// A virtual `embedded-hal` input pin representing one key of the keypad.
+///
+/// A `KeypadInput` stores references to one row and one column pin. When you
+/// read from it with `.is_low()` or `.is_high()`, it secretly sets the column
+/// pin low, reads from the row pin, and then sets the column pin high again.
+/// The column pin is actually stored inside a `RefCell` in the keypad struct,
+/// so that multiple `KeypadInput`s can mutate the column pin's state as needed,
+/// even though they only have a shared/immutable reference to it.
+///
+/// This has several implications.
+///
+/// 1) Reading from `KeypadInput`s is not reentrant. If we were in the middle
+/// of reading a `KeypadInput` and entered an interrupt service routine that
+/// read any `KeypadInput` of the same keypad, we might read an incorrect value
+/// or cause a `panic`.
+///
+/// 2) Reading from a `KeypadInput` is slower than reading from a real input
+/// pin, because it needs to change the output pin state twice for every read.
+pub struct KeypadInput<'a> {
+ row: &'a RefCell >,
+ col: &'a RefCell>,
+}
+
+impl<'a, E> KeypadInput<'a, E> {
+ /// Create a new `KeypadInput`. For use in macros.
+ pub fn new(row: &'a dyn InputPin, col: &'a RefCell) -> Self {
+ Self { row, col }
+ }
+}
+
+impl<'a, E> InputPin for KeypadInput<'a, E> {
+ type Error = E;
+ /// Read the state of the key at this row and column. Not reentrant.
+ fn is_high(&self) -> Result {
+ Ok(!self.is_low()?)
+ }
+
+ /// Read the state of the key at this row and column. Not reentrant.
+ fn is_low(&self) -> Result {
+ self.col.borrow_mut().set_low()?;
+ let out = self.row.is_low()?;
+ self.col.borrow_mut().set_high()?;
+ Ok(out)
+ }
+}
+
+/// Define a new struct representing your keypad matrix circuit.
+///
+/// Every pin has a unique type, depending on its pin number and its current
+/// mode. This struct is where you specify which pin types will be used in the
+/// rows and columns of the keypad matrix. All the row pins must implement the
+/// `InputPin` trait, and the column pins must implement the `OutputPin` trait.
+/// The associated `Error` type of the `InputPin` and `OutputPin` traits must be
+/// the same for every row and column pin, and you must specify it after your
+/// struct name with ``
+///
+/// You can specify the visibility of the struct (eg. `pub`) as usual, and add
+/// doc comments using the `#[doc="..."]` attribute.
+///
+/// Don't access or modify the struct's fields directly. Instead, use
+/// the methods implemented by this macro, documented here:
+/// [`example_generated::ExampleKeypad`](./example_generated/struct.ExampleKeypad.html)
+///
+/// # Example
+///
+/// ```
+/// # #![cfg_attr(docs_rs_workaround, feature(macro_vis_matcher))]
+/// #[macro_use]
+/// extern crate keypad;
+///
+/// use keypad::mock_hal::{self, Input, OpenDrain, Output, PullUp};
+/// use core::convert::Infallible;
+///
+/// keypad_struct! {
+/// #[doc="My super-special keypad."]
+/// pub struct ExampleKeypad {
+/// rows: (
+/// mock_hal::gpioa::PA0 >,
+/// mock_hal::gpioa::PA1 >,
+/// mock_hal::gpioa::PA2 >,
+/// mock_hal::gpioa::PA3 >,
+/// ),
+/// columns: (
+/// mock_hal::gpioa::PA4>,
+/// mock_hal::gpioa::PA5>,
+/// mock_hal::gpioa::PA6>,
+/// mock_hal::gpioa::PA7>,
+/// mock_hal::gpioa::PA8>,
+/// ),
+/// }
+/// }
+///
+/// # fn main() {
+/// # }
+/// ```
+///
+/// # Safety
+///
+/// This macro uses `unsafe` to create an array with uninitialized memory, which
+/// is then immediately initialized in a loop. This is fine as long as there is
+/// not a bug in how the macro calculates the dimensions of the array.
+
+// There are two reasons why this big, scary macro is necessary:
+//
+// 1) Every single pin has a unique type, and we don't know which pins will be used. We know that
+// they all implement a certain trait, but that doesn't help much because it doesn't tell us the
+// size of the type, so we can't directly stick it into the struct. If we could use dynamic
+// allocation, we could just store pins on the heap as boxed trait objects. But this crate needs
+// to work on embedded platforms without an allocator! So, we use this macro to generate a
+// struct containing the exact, concrete pin types the user provides.
+//
+// 2) We don't know how many pins there will be, because the keypad could have any number of rows
+// and columns. That makes it hard to implement `decompose()`, which needs to iterate over the
+// row and column pins. We can't store pins in arrays because they all have unique types, but we
+// can store them in tuples instead. The problem is that you can't actually iterate over a tuple,
+// and even indexing into arbitrary fields of a tuple using a macro is stupidly hard. The best
+// approach I could come up with was to repeatedly destructure the tuple with different patterns,
+// like this:
+//
+// let tuple = (0, 1, 2);
+// let array = [
+// {
+// let (ref x, ..) = tuple;
+// x
+// },
+// {
+// let (_, ref x, ..) = tuple;
+// x
+// },
+// {
+// let (_, _, ref x, ..) = tuple;
+// x
+// },
+// ];
+// for reference in array.into_iter() {
+// // ...
+// }
+//
+// So that's how the `keypad_struct!()` macro iterates over tuples of pins. It counts the length of
+// the tuple (also tricky!), creates patterns with increasing numbers of underscores, and uses them
+// to build up a temporary array of references that it can iterate over. Luckily this code only
+// needs to be run once, in `decompose()`, and not every time we read from a pin.
+//
+// I can't think of any simpler design that still has a convenient API and allows the keypad struct
+// to own the row and column pins. If they weren't owned, the crate would be less convenient to use
+// but could provide a generic Keypad struct like this:
+//
+// pub struct Keypad<'a, E, const R: usize, const C: usize> {
+// rows: [&'a dyn InputPin; R],
+// columns: [&'a RefCell>; C],
+// }
+//
+#[macro_export]
+macro_rules! keypad_struct {
+ (
+ $(#[$attributes:meta])* $visibility:vis struct $struct_name:ident {
+ rows: ( $($row_type:ty),* $(,)* ),
+ columns: ( $($col_type:ty),* $(,)* ),
+ }
+ ) => {
+ compile_error!("You must specify the associated `Error` type of the row and column pins'\
+ `InputPin` and `OutputPin` traits.\n\
+ Example: `struct MyStruct { ... }`");
+ };
+ (
+ $(#[$attributes:meta])* $visibility:vis struct $struct_name:ident {
+ rows: ( $($row_type:ty),* $(,)* ),
+ columns: ( $($col_type:ty),* $(,)* ),
+ }
+ ) => {
+ $(#[$attributes])* $visibility struct $struct_name {
+ /// The input pins used for reading each row.
+ rows: ($($row_type),* ,),
+ /// The output pins used for scanning through each column. They're
+ /// wrapped in RefCells so that we can change their state even if we
+ /// only have shared/immutable reference to them. This lets us
+ /// actively scan the matrix when reading the state of a virtual
+ /// `KeypadInput` pin.
+ columns: ($($crate::_core::cell::RefCell<$col_type>),* ,),
+ }
+
+ impl $struct_name {
+ /// Get a 2d array of embedded-hal input pins, each representing one
+ /// key in the keypad matrix.
+ #[allow(dead_code)]
+ $visibility fn decompose<'a>(&'a self) ->
+ keypad_struct!(
+ @array2d_type
+ $crate::KeypadInput<'a, $error_type>,
+ ($($row_type),*)
+ ($($crate::_core::cell::RefCell<$col_type>),*)
+ )
+ {
+
+ let rows: [
+ &dyn $crate::embedded_hal::digital::v2::InputPin;
+ keypad_struct!(@count $($row_type)*)
+ ]
+ = keypad_struct!(@tuple self.rows, ($($row_type),*));
+
+ let columns: [
+ &$crate::_core::cell::RefCell>;
+ keypad_struct!(@count $($col_type)*)
+ ]
+ = keypad_struct!(@tuple self.columns, ($($col_type),*));
+
+ // Create an uninitialized 2d array of MaybeUninit.
+ let mut out: keypad_struct!(
+ @array2d_type
+ $crate::_core::mem::MaybeUninit<$crate::KeypadInput<'a, $error_type>>,
+ ($($row_type),*)
+ ($($crate::_core::cell::RefCell<$col_type>),*)
+ ) = unsafe {
+ $crate::_core::mem::MaybeUninit::uninit().assume_init()
+ };
+
+ // Initialize each element with a KeypadInput struct
+ for r in 0..rows.len() {
+ for c in 0..columns.len() {
+ out[r][c].write($crate::KeypadInput::new(rows[r], columns[c]));
+ }
+ }
+ // All elements are initialized. Transmute the array to the initialized type.
+ unsafe { $crate::_core::mem::transmute::<_, _>(out) }
+ }
+
+ /// Give back ownership of the row and column pins.
+ ///
+ /// This consumes the keypad struct. All references to its virtual
+ /// `KeypadInput` pins must have gone out of scope before you try to
+ /// call `.release()`, or it will fail to compile.
+ ///
+ /// The column pins will be returned inside of `RefCell`s (because
+ /// macros are hard). You can use `.into_inner()` to extract
+ /// each column pin from its `RefCell`.
+ #[allow(dead_code)]
+ $visibility fn release(self) ->(($($row_type),* ,), ($($crate::_core::cell::RefCell<$col_type>),* ,)) {
+ (self.rows, self.columns)
+ }
+ }
+ };
+ (@array2d_type $element_type:ty, ($($row:ty),*) ($($col:ty),*) ) => {
+ [keypad_struct!(@array1d_type $element_type, ($($col),*)) ; keypad_struct!(@count $($row)*)]
+ };
+ (@array1d_type $element_type:ty, ($($col:ty),*)) => {
+ [$element_type ; keypad_struct!(@count $($col)*)]
+ };
+ (@count $($token_trees:tt)*) => {
+ 0usize $(+ keypad_struct!(@replace $token_trees 1usize))*
+ };
+ (@replace $_t:tt $sub:expr) => {
+ $sub
+ };
+ (@underscore $unused:tt) => {
+ _
+ };
+ (@destructure_ref $tuple:expr, ($($repeat_n:ty),*)) => {
+ {
+ let (
+ $(keypad_struct!(@underscore $repeat_n),)*
+ ref nth, ..) = $tuple;
+ nth
+ }
+ };
+ (@tuple_helper $tuple:expr, ($head:ty), ($($result:expr),* $(,)*)) => {
+ [
+ keypad_struct!(@destructure_ref $tuple, ()),
+ $($result),*
+ ]
+ };
+ (@tuple_helper $tuple:expr, ($head:ty $(,$repeats:ty)* $(,)*), ($($result:expr),* $(,)*)) => {
+ keypad_struct!(
+ @tuple_helper $tuple, ($($repeats),*),
+ (
+ keypad_struct!(@destructure_ref $tuple, ($($repeats),*)),
+ $($result),*
+ )
+ )
+ };
+ (@tuple $tuple:expr, ($($repeats:ty),*)) => {
+ keypad_struct!(@tuple_helper $tuple, ($($repeats),*) , ())
+ };
+}
+
+/// Create an instance of the struct you defined with the `keypad_struct!()` macro..
+///
+/// The pin numbers and modes will need to match the ones you specified with `keypad_struct!()`.
+///
+/// ```
+/// # #![cfg_attr(docs_rs_workaround, feature(macro_vis_matcher))]
+/// # #[macro_use]
+/// # extern crate keypad;
+/// # use core::convert::Infallible;
+/// # use keypad::mock_hal::{self, Input, OpenDrain, Output, PullUp};
+/// # use keypad::mock_hal::{GpioExt, GPIOA};
+/// # keypad_struct!{
+/// # pub struct ExampleKeypad{
+/// # rows: (
+/// # mock_hal::gpioa::PA0 >,
+/// # mock_hal::gpioa::PA1 >,
+/// # mock_hal::gpioa::PA2 >,
+/// # mock_hal::gpioa::PA3 >,
+/// # ),
+/// # columns: (
+/// # mock_hal::gpioa::PA4>,
+/// # mock_hal::gpioa::PA5>,
+/// # mock_hal::gpioa::PA6>,
+/// # mock_hal::gpioa::PA7>,
+/// # mock_hal::gpioa::PA8>,
+/// # ),
+/// # }
+/// # }
+/// # fn main() {
+/// let pins = GPIOA::split();
+///
+/// let keypad = keypad_new!(ExampleKeypad {
+/// rows: (
+/// pins.pa0.into_pull_up_input(),
+/// pins.pa1.into_pull_up_input(),
+/// pins.pa2.into_pull_up_input(),
+/// pins.pa3.into_pull_up_input(),
+/// ),
+/// columns: (
+/// pins.pa4.into_open_drain_output(),
+/// pins.pa5.into_open_drain_output(),
+/// pins.pa6.into_open_drain_output(),
+/// pins.pa7.into_open_drain_output(),
+/// pins.pa8.into_open_drain_output(),
+/// ),
+/// });
+/// # }
+/// ```
+#[macro_export]
+macro_rules! keypad_new {
+ ( $struct_name:ident {
+ rows: ( $($row_val:expr),* $(,)* ),
+ columns: ( $($col_val:expr),* $(,)* ),
+ }) => {
+ $struct_name {
+ rows: ($($row_val),* ,),
+ columns: ($($crate::_core::cell::RefCell::new($col_val)),* ,),
+ }
+ };
+}
diff --git a/src/main.rs b/src/main.rs
index 03545d3..bf3118e 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -16,7 +16,7 @@ use esp_hal::{
delay::Delay,
gpio::{
Gpio10, Gpio2, Gpio3, Gpio4, Gpio5, Gpio6, Gpio7, Gpio8, Input, InputPin, Io, Level,
- Output, OutputPin, Pull, NO_PIN,
+ Output, OutputOpenDrain, OutputPin, Pull, NO_PIN,
},
peripherals::Peripherals,
prelude::*,
@@ -96,26 +96,43 @@ fn main() -> ! {
log::info!("Printing welcome msg (DONE)");
log::info!("Full update...");
- let _ = driver.full_update(&display);
+ // let _ = driver.full_update(&display);
log::info!("Full update (DONE)");
log::info!("Sleeping for 0.5s...");
- delay.delay(500.millis());
+ // delay.delay(500.millis());
- let mut kbd = Keyboard {
- rows: Rows {
- gpio2: Input::new(io.pins.gpio2, Pull::Up),
- gpio3: Input::new(io.pins.gpio3, Pull::Up),
- gpio4: Input::new(io.pins.gpio4, Pull::Up),
- gpio5: Input::new(io.pins.gpio5, Pull::Up),
- },
- cols: Cols {
- gpio6: Output::new(io.pins.gpio6, Level::Low),
- gpio7: Output::new(io.pins.gpio7, Level::Low),
- gpio8: Output::new(io.pins.gpio8, Level::Low),
- gpio10: Output::new(io.pins.gpio10, Level::Low),
- },
- };
+ let mut gpio2 = Input::new(io.pins.gpio2, Pull::Up);
+ let mut gpio3 = Input::new(io.pins.gpio3, Pull::Up);
+ let mut gpio4 = Input::new(io.pins.gpio4, Pull::Up);
+ let mut gpio5 = Input::new(io.pins.gpio5, Pull::Up);
+
+ let mut gpio6 = OutputOpenDrain::new(io.pins.gpio6, Level::Low, Pull::Up);
+ let mut gpio7 = OutputOpenDrain::new(io.pins.gpio7, Level::Low, Pull::Up);
+ let mut gpio8 = OutputOpenDrain::new(io.pins.gpio8, Level::Low, Pull::Up);
+ let mut gpio10 = OutputOpenDrain::new(io.pins.gpio10, Level::Low, Pull::Up);
+
+ let mut kbd = Keypad((
+ (&mut gpio2, &mut gpio6),
+ (&mut gpio3, &mut gpio6),
+ (&mut gpio4, &mut gpio6),
+ (&mut gpio5, &mut gpio6),
+ // --------------------------------------
+ (&mut gpio2, &mut gpio7),
+ (&mut gpio3, &mut gpio7),
+ (&mut gpio4, &mut gpio7),
+ (&mut gpio5, &mut gpio7),
+ // --------------------------------------
+ (&mut gpio2, &mut gpio8),
+ (&mut gpio3, &mut gpio8),
+ (&mut gpio4, &mut gpio8),
+ (&mut gpio5, &mut gpio8),
+ // --------------------------------------
+ (&mut gpio2, &mut gpio10),
+ (&mut gpio3, &mut gpio10),
+ (&mut gpio4, &mut gpio10),
+ (&mut gpio5, &mut gpio10),
+ ));
let mut ctx = Context {
button_pressed: None,
@@ -125,7 +142,7 @@ fn main() -> ! {
display.clear(TriColor::White);
app.draw(&mut display);
- driver.full_update(&display).unwrap();
+ // driver.full_update(&display).unwrap();
driver.sleep().unwrap();
loop {
@@ -188,79 +205,78 @@ struct Rows<'d> {
gpio5: Input<'d, Gpio5>, //
}
struct Cols<'d> {
- gpio6: Output<'d, Gpio6>, //
- gpio7: Output<'d, Gpio7>, //
- gpio8: Output<'d, Gpio8>, //
- gpio10: Output<'d, Gpio10>, //
+ gpio6: OutputOpenDrain<'d, Gpio6>, //
+ gpio7: OutputOpenDrain<'d, Gpio7>, //
+ gpio8: OutputOpenDrain<'d, Gpio8>, //
+ gpio10: OutputOpenDrain<'d, Gpio10>, //
}
-struct Keyboard<'d> {
- rows: Rows<'d>,
- cols: Cols<'d>,
-}
-impl<'d> Keyboard<'d> {
+pub type OOD<'d, T> = OutputOpenDrain<'d, T>;
+
+struct Keypad<'d>(
+ pub (
+ // --------------------------------------
+ (&'d mut Input<'d, Gpio2>, &'d mut OOD<'d, Gpio6>),
+ (&'d mut Input<'d, Gpio3>, &'d mut OOD<'d, Gpio6>),
+ (&'d mut Input<'d, Gpio4>, &'d mut OOD<'d, Gpio6>),
+ (&'d mut Input<'d, Gpio5>, &'d mut OOD<'d, Gpio6>),
+ // --------------------------------------
+ (&'d mut Input<'d, Gpio2>, &'d mut OOD<'d, Gpio7>),
+ (&'d mut Input<'d, Gpio3>, &'d mut OOD<'d, Gpio7>),
+ (&'d mut Input<'d, Gpio4>, &'d mut OOD<'d, Gpio7>),
+ (&'d mut Input<'d, Gpio5>, &'d mut OOD<'d, Gpio7>),
+ // --------------------------------------
+ (&'d mut Input<'d, Gpio2>, &'d mut OOD<'d, Gpio8>),
+ (&'d mut Input<'d, Gpio3>, &'d mut OOD<'d, Gpio8>),
+ (&'d mut Input<'d, Gpio4>, &'d mut OOD<'d, Gpio8>),
+ (&'d mut Input<'d, Gpio5>, &'d mut OOD<'d, Gpio8>),
+ // --------------------------------------
+ (&'d mut Input<'d, Gpio2>, &'d mut OOD<'d, Gpio10>),
+ (&'d mut Input<'d, Gpio3>, &'d mut OOD<'d, Gpio10>),
+ (&'d mut Input<'d, Gpio4>, &'d mut OOD<'d, Gpio10>),
+ (&'d mut Input<'d, Gpio5>, &'d mut OOD<'d, Gpio10>),
+ ),
+);
+impl<'d> Keypad<'d> {
pub fn pressed(&mut self) -> Option {
- self.cols.gpio6.set_high();
let col1 = [
- self.rows.gpio2.is_high(),
- self.rows.gpio3.is_high(),
- self.rows.gpio4.is_high(),
- self.rows.gpio5.is_high(),
+ self.0 .0.is_low(),
+ self.0 .1.is_low(),
+ self.0 .2.is_low(),
+ self.0 .3.is_low(),
];
- self.cols.gpio6.set_high();
-
- self.cols.gpio7.set_high();
let col2 = [
- self.rows.gpio2.is_high(),
- self.rows.gpio3.is_high(),
- self.rows.gpio4.is_high(),
- self.rows.gpio5.is_high(),
+ self.0 .4.is_low(),
+ self.0 .5.is_low(),
+ self.0 .6.is_low(),
+ self.0 .7.is_low(),
];
- self.cols.gpio7.set_low();
-
- self.cols.gpio8.set_high();
let col3 = [
- self.rows.gpio2.is_high(),
- self.rows.gpio3.is_high(),
- self.rows.gpio4.is_high(),
- self.rows.gpio5.is_high(),
+ self.0 .8.is_low(),
+ self.0 .9.is_low(),
+ self.0 .10.is_low(),
+ self.0 .11.is_low(),
];
- self.cols.gpio8.set_low();
-
- self.cols.gpio10.set_high();
let col4 = [
- self.rows.gpio2.is_high(),
- self.rows.gpio3.is_high(),
- self.rows.gpio4.is_high(),
- self.rows.gpio5.is_high(),
+ self.0 .12.is_low(),
+ self.0 .13.is_low(),
+ self.0 .14.is_low(),
+ self.0 .15.is_low(),
];
- self.cols.gpio10.set_low();
- println!("***************************************");
- println!("col1 {col1:?}");
- println!("col2 {col2:?}");
- println!("col3 {col3:?}");
- println!("col4 {col4:?}");
+
+ if col1.iter().any(|b| !b)
+ || col2.iter().any(|b| !b)
+ || col3.iter().any(|b| !b)
+ || col4.iter().any(|b| !b)
+ {
+ println!("***************************************");
+ println!("col1 {col1:?}");
+ println!("col2 {col2:?}");
+ println!("col3 {col3:?}");
+ println!("col4 {col4:?}");
+ }
None
}
}
-/*
-struct Keyboard<'d>(
- [(
- core::cell::RefCell,
- core::cell::RefCell,
- ); 16],
-);
-
-impl<'d> Keyboard<'d> {
- pub fn new(cols: [impl InputPin; 4], rows: [impl OutputPin; 4]) {
- core::array::from_fn(|x| )
- for (x, c) in cols.into_iter().enumerate() {
- for (y, r) in rows.into_iter().enumerate() {
- ;
- }
- }
- }
-
-}
trait KeyboardInput<'d> {
fn is_low(&mut self) -> bool;
@@ -269,11 +285,13 @@ trait KeyboardInput<'d> {
}
}
-impl<'d, C: InputPin, R: OutputPin> KeyboardInput<'d> for (Input<'d, C>, Output<'d, R>) {
+impl<'d, C: InputPin, R: OutputPin + InputPin> KeyboardInput<'d>
+ for (&'d mut Input<'d, C>, &'d mut OutputOpenDrain<'d, R>)
+{
fn is_low(&mut self) -> bool {
self.1.set_low();
let s = self.0.is_low();
self.1.set_high();
s
}
-}*/
+}