Skip to main content

keyberon_smart_keyboard/
matrix.rs

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