RKTK API Docs RKTK Home Repo

rktk_drivers_common/keyscan/
duplex_matrix.rs

1//! (Japanese) duplex matrix scanner.
2
3use super::{
4    flex_pin::{FlexPin, Pull},
5    pressed::Pressed,
6};
7use embassy_time::Duration;
8use rktk::drivers::interface::keyscan::{KeyChangeEvent, KeyscanDriver};
9
10/// How to change the wait for output pins
11///
12/// In rp2040, wait_for_{low,high} can be used for output mode, so just use `Pin`.
13/// On the other hand, in nrf, this doesn't work and never returns.
14/// In such case, set this to `Time` and will be fallback to just wait some time.
15///
16/// Default: Time(20us)
17#[derive(Clone, Copy)]
18pub enum OutputWait {
19    Pin,
20    Time(Duration),
21}
22
23/// Implementation of keyscan driver for [duplex matrix](https://kbd.news/The-Japanese-duplex-matrix-1391.html).
24pub struct DuplexMatrixScanner<
25    F: FlexPin,
26    T: Fn(ScanDir, usize, usize) -> Option<(usize, usize)>,
27    const ROW_PIN_COUNT: usize,
28    const COL_PIN_COUNT: usize,
29    const ROWS: usize,
30    const COLS: usize,
31> {
32    rows: [F; ROW_PIN_COUNT],
33    cols: [F; COL_PIN_COUNT],
34    pressed: Pressed<ROWS, COLS>,
35    output_wait: OutputWait,
36    map_key_pos: T,
37}
38
39impl<
40    F: FlexPin,
41    T: Fn(ScanDir, usize, usize) -> Option<(usize, usize)>,
42    const ROW_PIN_COUNT: usize,
43    const COL_PIN_COUNT: usize,
44    const ROWS: usize,
45    const COLS: usize,
46> DuplexMatrixScanner<F, T, ROW_PIN_COUNT, COL_PIN_COUNT, ROWS, COLS>
47{
48    /// Detect the hand and initialize the scanner.
49    ///
50    /// # Arguments
51    /// - `rows`: Row pins of the matrix.
52    /// - `cols`: Column pins of the matrix.
53    /// - `output_awaitable`: Whether the output pins can be awaited for high/low. For more detail,
54    ///   see [`OutputWait`].
55    ///   Default: Time(20us)
56    /// - `left_detect_key`: The (logical, not pin index) key position to detect the hand.
57    /// - `translate_key_position`: Function to translate key position from pin number and scan direction to key
58    ///   (scan direction, row, col) -> Option<(row, col)>
59    pub fn new(
60        rows: [F; ROW_PIN_COUNT],
61        cols: [F; COL_PIN_COUNT],
62        output_wait: Option<OutputWait>,
63        translate_key_position: T,
64    ) -> Self {
65        Self {
66            rows,
67            cols,
68            pressed: Pressed::new(),
69            output_wait: output_wait.unwrap_or(OutputWait::Time(Duration::from_micros(20))),
70            map_key_pos: translate_key_position,
71        }
72    }
73
74    async fn wait_for_low(output_wait: OutputWait, pin: &mut F) {
75        match output_wait {
76            OutputWait::Pin => {
77                pin.wait_for_low().await;
78            }
79            OutputWait::Time(duration) => {
80                embassy_time::Timer::after(duration).await;
81            }
82        }
83    }
84
85    async fn wait_for_high(output_wait: OutputWait, pin: &mut F) {
86        match output_wait {
87            OutputWait::Pin => {
88                pin.wait_for_high().await;
89            }
90            OutputWait::Time(duration) => {
91                embassy_time::Timer::after(duration).await;
92            }
93        }
94    }
95
96    /// Scan the matrix using specific direction.
97    ///
98    /// # Arguments
99    /// - `cb`: ([output pin index], [input pin index]) -> ()
100    async fn scan_dir(
101        outputs: &mut [F],
102        inputs: &mut [F],
103        output_wait: OutputWait,
104        mut cb: impl FnMut(usize, usize, bool),
105    ) {
106        for output in outputs.iter_mut() {
107            output.set_low();
108        }
109        for inputs in inputs.iter_mut() {
110            inputs.set_pull(Pull::Down);
111            inputs.set_as_input();
112        }
113
114        embassy_time::Timer::after_micros(20).await;
115
116        for (o_i, output) in outputs.iter_mut().enumerate() {
117            output.set_high();
118            output.set_as_output();
119            Self::wait_for_high(output_wait, output).await;
120
121            for (i_i, input) in inputs.iter_mut().enumerate() {
122                cb(o_i, i_i, input.is_high());
123            }
124
125            output.set_low();
126            Self::wait_for_low(output_wait, output).await;
127            output.set_as_input();
128        }
129    }
130}
131
132impl<
133    F: FlexPin,
134    T: Fn(ScanDir, usize, usize) -> Option<(usize, usize)>,
135    const ROW_PIN_COUNT: usize,
136    const COL_PIN_COUNT: usize,
137    const ROWS: usize,
138    const COLS: usize,
139> KeyscanDriver for DuplexMatrixScanner<F, T, ROW_PIN_COUNT, COL_PIN_COUNT, ROWS, COLS>
140{
141    async fn scan(&mut self, mut cb: impl FnMut(KeyChangeEvent)) {
142        Self::scan_dir(
143            &mut self.rows,
144            &mut self.cols,
145            self.output_wait,
146            |row_pin_idx, col_pin_idx, pressed| {
147                if let Some((row, col)) =
148                    (self.map_key_pos)(ScanDir::Row2Col, row_pin_idx, col_pin_idx)
149                {
150                    if let Some(change) = self.pressed.set_pressed(pressed, row, col) {
151                        cb(KeyChangeEvent {
152                            row: row as u8,
153                            col: col as u8,
154                            pressed: change,
155                        });
156                    }
157                }
158            },
159        )
160        .await;
161
162        Self::scan_dir(
163            &mut self.cols,
164            &mut self.rows,
165            self.output_wait,
166            |col_pin_idx, row_pin_idx, pressed| {
167                if let Some((row, col)) =
168                    (self.map_key_pos)(ScanDir::Col2Row, row_pin_idx, col_pin_idx)
169                {
170                    if let Some(change) = self.pressed.set_pressed(pressed, row, col) {
171                        cb(KeyChangeEvent {
172                            row: row as u8,
173                            col: col as u8,
174                            pressed: change,
175                        });
176                    }
177                }
178            },
179        )
180        .await;
181    }
182}
183
184#[derive(Clone, Copy, PartialEq, Eq, Debug)]
185pub enum ScanDir {
186    Col2Row,
187    Row2Col,
188}