use crate::{Control, Error};
use core::marker::PhantomData;
use switch_hal::InputSwitch;
use timestamp_source::{ElapsedTimer, Timestamp};
pub trait DebouncedInputConfig {
type Timer: ElapsedTimer;
const DEBOUNCE_TIMER: Self::Timer;
}
pub enum DebouncedInputState<T> {
FixedLow,
FixedHigh,
RiseDisturbance(T),
FallDisturbance(T),
}
pub struct DebouncedInput<Switch: InputSwitch, Config: DebouncedInputConfig> {
input_switch: Switch,
state: DebouncedInputState<<Config::Timer as ElapsedTimer>::Timestamp>,
config: PhantomData<Config>,
}
#[derive(Debug, Clone, Copy, PartialEq)]
pub enum DebouncedInputEvent {
Low,
High,
Rise,
Fall,
}
impl<Switch: InputSwitch, Config: DebouncedInputConfig> DebouncedInput<Switch, Config> {
pub fn new(input_switch: Switch) -> Self {
let init_state = if input_switch.is_active().unwrap_or(false) {
DebouncedInputState::FixedHigh
} else {
DebouncedInputState::FixedLow
};
DebouncedInput {
input_switch,
state: init_state,
config: PhantomData::<Config>,
}
}
pub fn is_high(&self) -> bool {
match self.state {
DebouncedInputState::FixedLow | DebouncedInputState::RiseDisturbance(_) => false,
DebouncedInputState::FixedHigh | DebouncedInputState::FallDisturbance(_) => true,
}
}
pub fn is_low(&self) -> bool {
!self.is_high()
}
pub fn borrow_input_switch(&self) -> &Switch {
&self.input_switch
}
pub fn release_input_switch(self) -> Switch {
self.input_switch
}
}
impl<Switch: InputSwitch, Config: DebouncedInputConfig> Control for DebouncedInput<Switch, Config> {
type Event = DebouncedInputEvent;
type Error =
Error<<<Config::Timer as ElapsedTimer>::Timestamp as Timestamp>::Error, Switch::Error>;
fn update(&mut self) -> Result<Self::Event, Self::Error> {
let now = <Config::Timer as ElapsedTimer>::Timestamp::now();
let input_switch_state = self
.input_switch
.is_active()
.map_err(|err| Error::InputSwitch(err))?;
Ok(match &self.state {
DebouncedInputState::FixedLow => {
if input_switch_state {
self.state = DebouncedInputState::RiseDisturbance(now)
}
DebouncedInputEvent::Low
}
DebouncedInputState::FixedHigh => {
if !input_switch_state {
self.state = DebouncedInputState::FallDisturbance(now)
}
DebouncedInputEvent::High
}
DebouncedInputState::RiseDisturbance(start) => {
if !input_switch_state {
self.state = DebouncedInputState::FixedLow;
DebouncedInputEvent::Low
} else if Config::DEBOUNCE_TIMER
.timeout(start, &now)
.map_err(|err| Error::ElapsedTimer(err))?
{
self.state = DebouncedInputState::FixedHigh;
DebouncedInputEvent::Rise
} else {
DebouncedInputEvent::Low
}
}
DebouncedInputState::FallDisturbance(start) => {
if input_switch_state {
self.state = DebouncedInputState::FixedHigh;
DebouncedInputEvent::High
} else if Config::DEBOUNCE_TIMER
.timeout(start, &now)
.map_err(|err| Error::ElapsedTimer(err))?
{
self.state = DebouncedInputState::FixedLow;
DebouncedInputEvent::Fall
} else {
DebouncedInputEvent::High
}
}
})
}
}