RKTK API Docs RKTK Home Repo

rktk_drivers_common/keyscan/
shift_register_matrix.rs

1use super::pressed::Pressed;
2use embedded_hal::{digital::InputPin, spi::Operation};
3use embedded_hal_async::spi::SpiDevice;
4use rktk::drivers::interface::keyscan::{KeyChangeEvent, KeyscanDriver};
5
6/// Matrix scanner using spi-like shift register such as 74HC595 as output pin.
7///
8/// NOTE: Currently, chained shift register is not supported and OUTPUT_PIN_COUNT must be number of 1 to 8.
9pub struct ShiftRegisterMatrix<
10    S: SpiDevice,
11    IP: InputPin,
12    T: Fn(usize, usize) -> Option<(usize, usize)>,
13    const OUTPUT_PIN_COUNT: usize,
14    const INPUT_PIN_COUNT: usize,
15    const ROWS: usize,
16    const COLS: usize,
17> {
18    row_shift_register: S,
19    input_pins: [IP; INPUT_PIN_COUNT],
20    pressed: Pressed<ROWS, COLS>,
21    map_key_pos: T,
22    scan_delay: embassy_time::Duration,
23}
24
25impl<
26    S: SpiDevice,
27    IP: InputPin,
28    T: Fn(usize, usize) -> Option<(usize, usize)>,
29    const OUTPUT_PIN_COUNT: usize,
30    const INPUT_PIN_COUNT: usize,
31    const ROWS: usize,
32    const COLS: usize,
33> ShiftRegisterMatrix<S, IP, T, OUTPUT_PIN_COUNT, INPUT_PIN_COUNT, ROWS, COLS>
34{
35    /// Initialize the scanner.
36    ///
37    /// WARNING: Shift register is not actually spi so you should set proper spi mode.
38    /// Also, the scan direction can be different depending spi mode.
39    ///
40    /// * `row_shift_register`: SPI bus for the shift register used as output pin.
41    /// * `input_pins`: Input pins to read the matrix.
42    /// * `map_key_pos`: Function to map key position from pin number. This function must return
43    ///   position within specified `ROWS` and `COLS`.
44    ///   Signature: (row, col) -> Option<(row, col)>
45    /// * `scan_delay`: Delay between output pin change and input read. This is executed for each
46    ///   col/row so, this should be short enough to scan the matrix in a reasonable time. (Default: 5us)
47    pub fn new(
48        row_shift_register: S,
49        input_pins: [IP; INPUT_PIN_COUNT],
50        map_key_pos: T,
51        scan_delay: Option<embassy_time::Duration>,
52    ) -> Self {
53        Self {
54            row_shift_register,
55            input_pins,
56            pressed: Pressed::new(),
57            map_key_pos,
58            scan_delay: scan_delay.unwrap_or(embassy_time::Duration::from_micros(5)),
59        }
60    }
61}
62
63impl<
64    S: SpiDevice,
65    IP: InputPin,
66    T: Fn(usize, usize) -> Option<(usize, usize)>,
67    const OUTPUT_PIN_COUNT: usize,
68    const INPUT_PIN_COUNT: usize,
69    const ROWS: usize,
70    const COLS: usize,
71> KeyscanDriver for ShiftRegisterMatrix<S, IP, T, OUTPUT_PIN_COUNT, INPUT_PIN_COUNT, ROWS, COLS>
72{
73    // TODO: support async matrix
74    async fn scan(&mut self, mut cb: impl FnMut(KeyChangeEvent)) {
75        for output_idx in 0..OUTPUT_PIN_COUNT {
76            let _ = self
77                .row_shift_register
78                .transaction(&mut [
79                    Operation::DelayNs(1000),
80                    Operation::Write(&[1 << output_idx]),
81                ])
82                .await;
83
84            embassy_time::Timer::after(self.scan_delay).await;
85
86            for (input_idx, input_pin) in self.input_pins.iter_mut().enumerate() {
87                if let Some((row, col)) = (self.map_key_pos)(input_idx, output_idx) {
88                    if let Some(change) =
89                        self.pressed
90                            .set_pressed(input_pin.is_high().unwrap(), row, col)
91                    {
92                        cb(KeyChangeEvent {
93                            row: row as u8,
94                            col: col as u8,
95                            pressed: change,
96                        });
97                    }
98                }
99            }
100        }
101    }
102}