RKTK API Docs RKTK Home Repo

rktk_drivers_common/keyscan/
matrix.rs

1use super::pressed::Pressed;
2use embedded_hal::digital::{InputPin, OutputPin};
3use embedded_hal_async::digital::Wait;
4use rktk::drivers::interface::keyscan::{KeyChangeEvent, KeyscanDriver};
5
6/// Matrix scanner
7///
8/// NOTE: `COLS` const generic is the number of columns. Though you can set this value same as
9/// specified in [`rktk::::config::static_config::Keyboard::cols`], for split keyboard, this also can be set to
10/// "columns of the half keyboard". By using this value, you can save memory usage.
11pub struct Matrix<
12    OP: OutputPin,
13    IP: InputPin + Wait,
14    T: Fn(usize, usize) -> Option<(usize, usize)>,
15    const OUTPUT_PIN_COUNT: usize,
16    const INPUT_PIN_COUNT: usize,
17    const ROWS: usize,
18    const COLS: usize,
19> {
20    output_pins: [OP; OUTPUT_PIN_COUNT],
21    input_pins: [IP; INPUT_PIN_COUNT],
22    pressed: Pressed<ROWS, COLS>,
23    map_key_pos: T,
24    scan_delay: embassy_time::Duration,
25}
26
27impl<
28    OP: OutputPin,
29    IP: InputPin + Wait,
30    T: Fn(usize, usize) -> Option<(usize, usize)>,
31    const OUTPUT_PIN_COUNT: usize,
32    const INPUT_PIN_COUNT: usize,
33    const ROWS: usize,
34    const COLS: usize,
35> Matrix<OP, IP, T, OUTPUT_PIN_COUNT, INPUT_PIN_COUNT, ROWS, COLS>
36{
37    /// Initialize the scanner.
38    ///
39    /// # Arguments
40    /// - `output_pins`: Output pins to control the matrix.
41    /// - `input_pins`: Input pins to read the matrix.
42    /// - `left_detect_key`: The (logical, not pin index) key position to detect the hand.
43    /// - `map_key`: Function to map key position from pin index. This function must return
44    ///   position within specified `ROWS` and `COLS`.
45    ///   Signature: (input_pin_idx, output_pin_idx) -> Option<(row, col)>
46    /// * `scan_delay`: Delay between output pin change and input read. This is executed for each
47    ///   col/row so, this should be short enough to scan the matrix in a reasonable time.
48    ///   Default: 5us
49    pub fn new(
50        output_pins: [OP; OUTPUT_PIN_COUNT],
51        input_pins: [IP; INPUT_PIN_COUNT],
52        map_key_pos: T,
53        scan_delay: Option<embassy_time::Duration>,
54    ) -> Self {
55        Self {
56            output_pins,
57            input_pins,
58            pressed: Pressed::new(),
59            map_key_pos,
60            scan_delay: scan_delay.unwrap_or(embassy_time::Duration::from_micros(5)),
61        }
62    }
63}
64
65impl<
66    OP: OutputPin,
67    IP: InputPin + Wait,
68    T: Fn(usize, usize) -> Option<(usize, usize)>,
69    const OUTPUT_PIN_COUNT: usize,
70    const INPUT_PIN_COUNT: usize,
71    const ROWS: usize,
72    const COLS: usize,
73> KeyscanDriver for Matrix<OP, IP, T, OUTPUT_PIN_COUNT, INPUT_PIN_COUNT, ROWS, COLS>
74{
75    // TODO: support async matrix
76    async fn scan(&mut self, mut cb: impl FnMut(KeyChangeEvent)) {
77        for output_idx in 0..OUTPUT_PIN_COUNT {
78            let _ = self.output_pins[output_idx].set_high();
79
80            embassy_time::Timer::after(self.scan_delay).await;
81
82            for (input_idx, input_pin) in self.input_pins.iter_mut().enumerate() {
83                if let Some((row, col)) = (self.map_key_pos)(input_idx, output_idx) {
84                    if let Some(change) =
85                        self.pressed
86                            .set_pressed(input_pin.is_high().unwrap(), row, col)
87                    {
88                        cb(KeyChangeEvent {
89                            row: row as u8,
90                            col: col as u8,
91                            pressed: change,
92                        });
93                    }
94                }
95            }
96
97            let _ = self.output_pins[output_idx].set_low();
98        }
99    }
100}