use embassy_time::Timer;
use embedded_hal;
use embedded_hal::digital::InputPin;
use rmk_macro::input_device;
#[cfg(feature = "async_matrix")]
use {
core::future::pending, embassy_futures::select::select_array, embassy_time::Instant,
embedded_hal_async::digital::Wait,
};
use super::{KeyState, MatrixTrait};
use crate::debounce::{DebounceState, DebouncerTrait};
use crate::event::KeyboardEvent;
#[input_device(publish = KeyboardEvent)]
pub struct DirectPinMatrix<
#[cfg(feature = "async_matrix")] In: Wait + InputPin,
#[cfg(not(feature = "async_matrix"))] In: InputPin,
D: DebouncerTrait<ROW, COL>,
const ROW: usize,
const COL: usize,
const SIZE: usize,
const ROW_OFFSET: usize = 0,
const COL_OFFSET: usize = 0,
> {
direct_pins: [[Option<In>; COL]; ROW],
debouncer: D,
key_states: [[KeyState; COL]; ROW],
#[cfg(feature = "async_matrix")]
scan_start: Option<Instant>,
low_active: bool,
scan_pos: (usize, usize),
}
impl<
#[cfg(not(feature = "async_matrix"))] In: InputPin,
#[cfg(feature = "async_matrix")] In: Wait + InputPin,
D: DebouncerTrait<ROW, COL>,
const ROW: usize,
const COL: usize,
const SIZE: usize,
const ROW_OFFSET: usize,
const COL_OFFSET: usize,
> DirectPinMatrix<In, D, ROW, COL, SIZE, ROW_OFFSET, COL_OFFSET>
{
pub fn new(direct_pins: [[Option<In>; COL]; ROW], debouncer: D, low_active: bool) -> Self {
DirectPinMatrix {
direct_pins,
debouncer,
key_states: [[KeyState::new(); COL]; ROW],
#[cfg(feature = "async_matrix")]
scan_start: None,
low_active,
scan_pos: (0, 0),
}
}
}
impl<
#[cfg(not(feature = "async_matrix"))] In: InputPin,
#[cfg(feature = "async_matrix")] In: Wait + InputPin,
D: DebouncerTrait<ROW, COL>,
const ROW: usize,
const COL: usize,
const SIZE: usize,
const ROW_OFFSET: usize,
const COL_OFFSET: usize,
> DirectPinMatrix<In, D, ROW, COL, SIZE, ROW_OFFSET, COL_OFFSET>
{
async fn read_keyboard_event(&mut self) -> KeyboardEvent {
loop {
let (row_idx_start, col_idx_start) = self.scan_pos;
#[cfg(feature = "async_matrix")]
self.wait_for_key().await;
for row_idx in row_idx_start..self.direct_pins.len() {
let pins_row = self.direct_pins.get_mut(row_idx).unwrap();
for col_idx in col_idx_start..pins_row.len() {
let direct_pin = pins_row.get_mut(col_idx).unwrap();
if let Some(direct_pin) = direct_pin {
let pin_state = if self.low_active {
direct_pin.is_low().ok().unwrap_or_default()
} else {
direct_pin.is_high().ok().unwrap_or_default()
};
let debounce_state = self.debouncer.detect_change_with_debounce(
row_idx,
col_idx,
pin_state,
&self.key_states[row_idx][col_idx],
);
if let DebounceState::Debounced = debounce_state {
self.key_states[row_idx][col_idx].toggle_pressed();
let key_state = self.key_states[row_idx][col_idx];
self.scan_pos = (row_idx, col_idx);
return KeyboardEvent::key(
(row_idx + ROW_OFFSET) as u8,
(col_idx + COL_OFFSET) as u8,
key_state.pressed,
);
}
#[cfg(feature = "async_matrix")]
if self.key_states[row_idx][col_idx].pressed {
self.scan_start = Some(Instant::now());
}
}
}
}
self.scan_pos = (0, 0);
Timer::after_micros(100).await;
}
}
}
impl<
#[cfg(not(feature = "async_matrix"))] In: InputPin,
#[cfg(feature = "async_matrix")] In: Wait + InputPin,
D: DebouncerTrait<ROW, COL>,
const ROW: usize,
const COL: usize,
const SIZE: usize,
const ROW_OFFSET: usize,
const COL_OFFSET: usize,
> MatrixTrait<ROW, COL> for DirectPinMatrix<In, D, ROW, COL, SIZE, ROW_OFFSET, COL_OFFSET>
{
#[cfg(feature = "async_matrix")]
async fn wait_for_key(&mut self) {
if let Some(start_time) = self.scan_start {
if start_time.elapsed().as_millis() <= 1 {
return;
} else {
self.scan_start = None;
}
}
Timer::after_micros(1).await;
info!("Waiting for active level");
let low_active = self.low_active;
let futs = self.direct_pins.each_mut().map(|direct_pins_row| {
let row_futs = direct_pins_row.each_mut().map(|direct_pin| async move {
match (direct_pin, low_active) {
(Some(direct_pin), true) => {
let _ = direct_pin.wait_for_low().await;
}
(Some(direct_pin), false) => {
let _ = direct_pin.wait_for_high().await;
}
(None, _) => pending().await,
}
});
select_array(row_futs)
});
let _ = select_array(futs).await;
self.scan_start = Some(Instant::now());
}
}