Skip to main content

oxstack_cardputer/
keyboard.rs

1//! The 56-key keyboard, scanned by a TCA8418 on the internal I²C bus.
2//!
3//! The scanner debounces and queues events in hardware and pulls GPIO11
4//! low while any are pending. [`Keyboard::next_event`] sleeps on that pin,
5//! so an idle keyboard costs no CPU time and no bus traffic.
6
7use esp_hal::gpio::{Input, InputConfig, Pull};
8use esp_hal::peripherals::GPIO11;
9use oxstack_drivers::tca8418::{self, Event, FIFO_DEPTH, Matrix, Tca8418};
10use oxstack_input::{KeyEvent, KeyboardState, cardputer};
11
12use crate::i2c::{SysI2cBus, SysI2cDevice, device};
13
14/// I²C error from the shared bus.
15pub type Error = embassy_embedded_hal::shared_bus::I2cDeviceError<esp_hal::i2c::master::Error>;
16
17const MATRIX: Matrix = Matrix::new(cardputer::ADV_MATRIX_ROWS, cardputer::ADV_MATRIX_COLS);
18
19/// The scanner's interrupt line.
20#[allow(missing_docs)]
21pub struct KeyboardResources<'d> {
22    pub int: GPIO11<'d>,
23}
24reborrow_group!(KeyboardResources { int });
25
26impl<'d> KeyboardResources<'d> {
27    /// Configures the scanner and returns the keyboard. The scanner's event
28    /// queue is cleared, so keys pressed before `init` are not reported.
29    pub async fn init(&mut self, bus: &'static SysI2cBus) -> Result<Keyboard<'_>, Error> {
30        // The line has a 3.3 kΩ pull-up on the board; the internal one is
31        // a harmless backup if the scanner is unpowered.
32        let int = Input::new(
33            self.int.reborrow(),
34            InputConfig::default().with_pull(Pull::Up),
35        );
36        let mut scanner = Tca8418::new(device(bus), tca8418::DEFAULT_ADDRESS);
37        scanner.configure(MATRIX).await?;
38        Ok(Keyboard {
39            scanner,
40            int,
41            state: KeyboardState::new(),
42            fifo: [0; FIFO_DEPTH],
43            pos: 0,
44            len: 0,
45        })
46    }
47}
48
49/// The keyboard driver.
50pub struct Keyboard<'d> {
51    scanner: Tca8418<SysI2cDevice>,
52    int: Input<'d>,
53    state: KeyboardState,
54    /// Raw events drained from the chip but not yet returned.
55    fifo: [u8; FIFO_DEPTH],
56    pos: u8,
57    len: u8,
58}
59
60impl Keyboard<'_> {
61    /// Waits for the next key press or release.
62    ///
63    /// Cancelling while it waits for the interrupt line (the usual case,
64    /// e.g. in a `select`) loses nothing: events stay queued in the scanner.
65    /// Cancelling during the brief I²C drain can drop up to one FIFO's worth
66    /// of events.
67    pub async fn next_event(&mut self) -> Result<KeyEvent, Error> {
68        loop {
69            while self.pos < self.len {
70                let raw = self.fifo[usize::from(self.pos)];
71                self.pos += 1;
72                if let Some(Event::Key(k)) = Event::decode(raw)
73                    && let Some(key) = cardputer::from_adv_matrix(k.row, k.col)
74                {
75                    return Ok(self.state.apply(key, k.pressed));
76                }
77            }
78            // Wait on the level, not an edge, so an interrupt that fired
79            // before we started waiting is never missed.
80            self.int.wait_for_low().await;
81            let n = self.scanner.drain(&mut self.fifo).await?.len();
82            self.scanner.clear_interrupts().await?;
83            (self.pos, self.len) = (0, n as u8);
84        }
85    }
86
87    /// Held keys and modifiers.
88    pub fn state(&self) -> &KeyboardState {
89        &self.state
90    }
91}