usbd_smart_keyboard/matrix.rs
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121
//! Hardware pin switch matrix handling.
use core::fmt::Debug;
use embedded_hal::delay::DelayNs;
use embedded_hal::digital::{InputPin, OutputPin};
/// Describes the hardware-level matrix of switches.
///
/// Generic parameters are in order: The type of column pins,
/// the type of row pins, the number of columns and rows.
/// **NOTE:** In order to be able to put different pin structs
/// in an array they have to be downgraded (stripped of their
/// numbers etc.). Most HAL-s have a method of downgrading pins
/// to a common (erased) struct. (for example see
/// [stm32f0xx_hal::gpio::PA0::downgrade](https://docs.rs/stm32f0xx-hal/0.17.1/stm32f0xx_hal/gpio/gpioa/struct.PA0.html#method.downgrade))
///
/// TIM5 is used to provide a delay during the matrix scanning.
pub struct Matrix<C, R, const CS: usize, const RS: usize, D>
where
C: InputPin,
R: OutputPin,
D: DelayNs,
{
cols: [C; CS],
rows: [R; RS],
delay: D,
select_delay_us: u32,
unselect_delay_us: u32,
}
impl<C, R, const CS: usize, const RS: usize, D> Matrix<C, R, CS, RS, D>
where
C: InputPin,
R: OutputPin,
D: DelayNs,
{
/// Creates a new Matrix.
///
/// Assumes columns are pull-up inputs,
/// and rows are output pins which are set high when not being scanned.
pub fn new<E>(
cols: [C; CS],
rows: [R; RS],
delay: D,
select_delay_us: u32,
unselect_delay_us: u32,
) -> Result<Self, E>
where
C: InputPin<Error = E>,
R: OutputPin<Error = E>,
{
let mut res = Self {
cols,
rows,
delay,
select_delay_us,
unselect_delay_us,
};
res.clear()?;
Ok(res)
}
fn clear<E>(&mut self) -> Result<(), E>
where
C: InputPin<Error = E>,
R: OutputPin<Error = E>,
{
for r in self.rows.iter_mut() {
r.set_high()?;
}
Ok(())
}
}
impl<C, R, const CS: usize, const RS: usize, D, E: Debug> crate::input::MatrixScanner<CS, RS, E>
for Matrix<C, R, CS, RS, D>
where
C: InputPin<Error = E>,
R: OutputPin<Error = E>,
D: DelayNs,
{
fn is_boot_key_pressed(&mut self) -> bool {
self.rows[0].set_low().unwrap();
self.delay.delay_us(self.select_delay_us);
let is_pressed = self.cols[0].is_low().unwrap();
self.rows[0].set_high().unwrap();
self.delay.delay_us(self.unselect_delay_us);
is_pressed
}
/// Scans the matrix and checks which keys are pressed.
///
/// Every row pin in order is pulled low, and then each column
/// pin is tested; if it's low, the key is marked as pressed.
///
/// Delays for a bit after setting each pin, and after clearing
/// each pin.
fn get(&mut self) -> Result<[[bool; CS]; RS], E> {
let mut keys = [[false; CS]; RS];
for (ri, row) in self.rows.iter_mut().enumerate() {
row.set_low()?;
// Delay after setting the pin low.
// Using a timer for this is probably overkill.
self.delay.delay_us(self.select_delay_us);
for (ci, col) in self.cols.iter_mut().enumerate() {
if col.is_low()? {
keys[ri][ci] = true;
}
}
row.set_high()?;
// Delay after setting the pin high.
// Using a timer for this is probably overkill.
self.delay.delay_us(self.unselect_delay_us);
}
Ok(keys)
}
}