RKTK API Docs RKTK Home Repo

rktk_drivers_common/
debounce.rs

1//! Debounce driver implementations.
2
3use rktk::{
4    config::CONST_CONFIG,
5    drivers::interface::debounce::{DebounceDriver, KeyChangeEvent},
6};
7
8/// Debounce driver that implements `Eager debouncing`
9///
10/// Debounces key using "eager debouncing" strategy.
11/// Ignores all events related to a key for a certain period of time after that event is reported.
12/// ref: [Debouncing | ZMK](https://zmk.dev/docs/features/debouncing#eager-debouncing)
13pub struct EagerDebounceDriver {
14    last: [[Option<embassy_time::Instant>; CONST_CONFIG.keyboard.cols as usize];
15        CONST_CONFIG.keyboard.rows as usize],
16    debounce_time: embassy_time::Duration,
17    deboune_only_pressed: bool,
18}
19
20impl EagerDebounceDriver {
21    /// Create a new `EagerDebounceDriver` instance.
22    ///
23    /// # Arguments
24    /// * debounce_time - The debounce time.
25    /// * deboune_only_pressed - If true, only debounce pressed events.
26    pub const fn new(debounce_time: embassy_time::Duration, deboune_only_pressed: bool) -> Self {
27        Self {
28            last: [[None; CONST_CONFIG.keyboard.cols as usize];
29                CONST_CONFIG.keyboard.rows as usize],
30            debounce_time,
31            deboune_only_pressed,
32        }
33    }
34}
35
36impl DebounceDriver for EagerDebounceDriver {
37    fn should_ignore_event(&mut self, event: &KeyChangeEvent, now: embassy_time::Instant) -> bool {
38        let last = self.last[event.row as usize][event.col as usize];
39        if let Some(last) = last {
40            if now - last < self.debounce_time {
41                if self.deboune_only_pressed {
42                    return event.pressed;
43                } else {
44                    return true;
45                }
46            }
47        }
48        self.last[event.row as usize][event.col as usize] = Some(now);
49        false
50    }
51}
52
53#[cfg(test)]
54mod tests {
55    use embassy_time::{Duration, Instant};
56    use rktk::drivers::interface::{debounce::DebounceDriver as _, keyscan::KeyChangeEvent};
57
58    use super::EagerDebounceDriver;
59
60    #[test]
61    fn eager_debouncer_only_pressed() {
62        // PRESS    Accepted
63        // 5ms
64        // RELEASE  Maybe chatter but accepted
65        // 3ms
66        // PRESS    Ignored
67        // ...
68        // 92ms
69        // ...
70        // PRESS    Accepted
71
72        let mut d = EagerDebounceDriver::new(Duration::from_millis(10), true);
73
74        assert!(
75            !d.should_ignore_event(
76                &KeyChangeEvent {
77                    row: 0,
78                    col: 0,
79                    pressed: true,
80                },
81                Instant::from_millis(0),
82            ),
83            "Key press event should not be ignored"
84        );
85
86        assert!(
87            !d.should_ignore_event(
88                &KeyChangeEvent {
89                    row: 0,
90                    col: 0,
91                    pressed: false,
92                },
93                Instant::from_millis(5),
94            ),
95            "Key release event before debounce_time should not be ignored"
96        );
97
98        assert!(
99            d.should_ignore_event(
100                &KeyChangeEvent {
101                    row: 0,
102                    col: 0,
103                    pressed: true,
104                },
105                Instant::from_millis(8),
106            ),
107            "Key press event before debounce_time should be ignored"
108        );
109
110        assert!(
111            !d.should_ignore_event(
112                &KeyChangeEvent {
113                    row: 0,
114                    col: 0,
115                    pressed: true,
116                },
117                Instant::from_millis(100),
118            ),
119            "Key press event after debounce_time should not be ignored"
120        );
121    }
122}