1use embassy_futures::select::select_slice;
2use embassy_sync::{blocking_mutex::raw::RawMutex, channel::Channel};
3use embassy_time::{Instant, Timer};
4use embedded_hal::digital::{InputPin, OutputPin};
5use embedded_hal_async::digital::Wait;
6use heapless::Vec;
7
8use crate::info;
9
10const WAIT_NANOS: u64 = 100;
11
12#[derive(Debug, Clone, Copy)]
13#[cfg_attr(feature = "defmt", derive(defmt::Format))]
14pub struct ScanKey {
15 row: u8,
16 col: u8,
17}
18impl ScanKey {
19 pub fn new(row: u8, col: u8, is_down: bool) -> Self {
20 Self {
21 row: row | if is_down { 0x80 } else { 0 },
22 col,
23 }
24 }
25
26 pub fn none() -> Self {
27 Self {
28 row: 0xff,
29 col: 0xff,
30 }
31 }
32
33 pub fn is_none(&self) -> bool {
34 self.col == 0xff
35 }
36
37 pub fn row(&self) -> usize {
38 (self.row & 0x7f) as usize
39 }
40
41 pub fn column(&self) -> usize {
42 self.col as usize
43 }
44
45 pub fn is_down(&self) -> bool {
46 self.row & 0x80 == 0x80
47 }
48
49 pub fn is_same_key(&self, other: ScanKey) -> bool {
50 self.col == other.col && self.row & 0x7f == other.row & 0x7f
51 }
52
53 pub fn same_key(&self, other: ScanKey) -> bool {
54 self.col == other.col && self.row & 0x7f == other.row & 0x7f
55 }
56
57 pub fn as_memo(&self) -> u16 {
58 self.row as u16 | ((self.col as u16) << 8)
59 }
60
61 pub fn from_memo(memo: u16) -> Self {
62 Self {
63 row: memo as u8,
64 col: (memo >> 8) as u8,
65 }
66 }
67}
68
69pub struct KeyScannerChannel<M: RawMutex, const N: usize>(Channel<M, ScanKey, N>);
70
71pub struct KeyScanner<
72 'c,
73 I: InputPin + Wait,
74 O: OutputPin,
75 M: RawMutex,
76 const INPUT_N: usize,
77 const OUTPUT_N: usize,
78 const PS: usize,
79> {
80 input_pins: [I; INPUT_N],
81 output_pins: [O; OUTPUT_N],
82 state: [[u8; INPUT_N]; OUTPUT_N],
86 scan_start: Option<Instant>,
87 channel: &'c KeyScannerChannel<M, PS>,
88 debounce: usize,
89}
90
91impl<M: RawMutex, const N: usize> Default for KeyScannerChannel<M, N> {
92 fn default() -> Self {
93 Self(Channel::new())
94 }
95}
96
97impl<M: RawMutex, const N: usize> KeyScannerChannel<M, N> {
98 pub async fn receive(&self) -> ScanKey {
99 self.0.receive().await
100 }
101
102 pub fn try_send(&self, msg: ScanKey) {
103 self.0.try_send(msg).ok();
104 }
105
106 pub async fn get_offset(&self) -> u32 {
107 let key1 = self.receive().await;
108 let key2 = self.receive().await;
109 u32::from_le_bytes([key1.row, key1.col, key2.row, key2.col])
110 }
111}
112
113impl<
114 'c,
115 I: InputPin + Wait,
116 O: OutputPin,
117 M: RawMutex,
118 const INPUT_N: usize,
119 const OUTPUT_N: usize,
120 const PS: usize,
121 > KeyScanner<'c, I, O, M, INPUT_N, OUTPUT_N, PS>
122{
123 pub fn new(
124 input_pins: [I; INPUT_N],
125 output_pins: [O; OUTPUT_N],
126 channel: &'c KeyScannerChannel<M, PS>,
127 ) -> Self {
128 Self {
129 input_pins,
130 output_pins,
131 state: [[0; INPUT_N]; OUTPUT_N],
132 scan_start: None,
133 channel,
134 debounce: 0,
135 }
136 }
137
138 pub async fn run<const ROW_IS_OUTPUT: bool, const DEBOUNCE_TUNE: usize>(&mut self) {
139 assert!(DEBOUNCE_TUNE < usize::BITS as usize);
140 loop {
141 if self.scan_start.is_some_and(|s| s.elapsed().as_secs() > 1) {
143 let _waited = self.wait_for_key().await;
144 }
145
146 self.scan::<ROW_IS_OUTPUT, DEBOUNCE_TUNE>().await;
147 }
148 }
149
150 pub async fn wait_for_key(&mut self) -> bool {
151 self.scan_start = None;
152
153 for out in self.output_pins.iter_mut() {
155 let _ = out.set_low();
156 }
157 Timer::after_micros(1).await;
158 info!("Waiting for low");
159
160 let mut futs: Vec<_, INPUT_N> = self
161 .input_pins
162 .iter_mut()
163 .map(|input_pin| input_pin.wait_for_low())
164 .collect();
165 let _ = select_slice(futs.as_mut_slice()).await;
166
167 for out in self.output_pins.iter_mut() {
169 let _ = out.set_high();
170 }
171
172 true
173 }
174
175 pub async fn scan<const ROW_IS_OUTPUT: bool, const DEBOUNCE_TUNE: usize>(&mut self) {
176 let debounce = debounce_sensitivity::<DEBOUNCE_TUNE>(self.debounce);
178
179 let mut is_all_up = true;
181
182 for (output_idx, (op, s)) in self
183 .output_pins
184 .iter_mut()
185 .zip(self.state.iter_mut())
186 .enumerate()
187 {
188 let _ = op.set_low();
189 Timer::after_nanos(WAIT_NANOS).await;
190
191 for (input_idx, (ip, s)) in self.input_pins.iter_mut().zip(s.iter_mut()).enumerate() {
192 let settle = *s & !3; let is_down = if ip.is_low().unwrap_or(false) { 1 } else { 0 };
195 let mut changed = *s & 1 != is_down;
196
197 if settle != 0 {
198 if settle == debounce {
199 *s &= 3;
201 changed = matches!(*s, 1 | 2);
202 } else {
203 is_all_up = false;
205 if changed {
206 *s = debounce_start::<DEBOUNCE_TUNE>(is_down, *s & 2, self.debounce);
208 }
209 continue;
210 }
211 }
212 if is_down == 1 {
213 is_all_up = false;
214 }
215
216 if changed {
217 *s = debounce_start::<DEBOUNCE_TUNE>(is_down, is_down << 1, self.debounce);
219 self.channel
220 .0
221 .send(if ROW_IS_OUTPUT {
222 ScanKey::new(output_idx as u8, input_idx as u8, is_down == 1)
223 } else {
224 ScanKey::new(input_idx as u8, output_idx as u8, is_down == 1)
225 })
226 .await;
227 }
228 }
229
230 let _ = op.set_high();
231 }
232
233 self.debounce = self.debounce.wrapping_add(2);
234
235 if is_all_up {
236 if self.scan_start.is_none() {
237 self.scan_start = Some(Instant::now());
238 }
239 } else if self.scan_start.is_some() {
240 self.scan_start = None;
241 }
242 }
243}
244
245fn debounce_sensitivity<const DEBOUNCE_TUNE: usize>(debounce: usize) -> u8 {
246 ((if DEBOUNCE_TUNE < 4 {
247 debounce << (4 - DEBOUNCE_TUNE) } else {
249 debounce >> (DEBOUNCE_TUNE - 4) } as u8)
251 << 2)
252 | 0x80
253}
254
255fn debounce_start<const DEBOUNCE_TUNE: usize>(
256 is_down: u8,
257 reported_down: u8,
258 debounce: usize,
259) -> u8 {
260 debounce_sensitivity::<DEBOUNCE_TUNE>(debounce.wrapping_sub(if DEBOUNCE_TUNE < 4 {
261 0
262 } else {
263 1 << (DEBOUNCE_TUNE - 3)
264 })) | (is_down | reported_down)
265}
266
267#[cfg(test)]
268extern crate std;
269
270#[cfg(test)]
271#[path = "key_scanner_test.rs"]
272mod test;