embedded_controls/
debounced_input.rs1use crate::{Control, Error};
2
3use core::marker::PhantomData;
4use switch_hal::InputSwitch;
5use timestamp_source::{ElapsedTimer, Timestamp};
6
7pub trait DebouncedInputConfig {
9 type Timer: ElapsedTimer;
11
12 const DEBOUNCE_TIMER: Self::Timer;
15}
16
17pub enum DebouncedInputState<T> {
19 FixedLow,
20 FixedHigh,
21 RiseDisturbance(T),
22 FallDisturbance(T),
23}
24
25pub struct DebouncedInput<Switch: InputSwitch, Config: DebouncedInputConfig> {
53 input_switch: Switch,
54 state: DebouncedInputState<<Config::Timer as ElapsedTimer>::Timestamp>,
55 config: PhantomData<Config>,
56}
57
58#[derive(Debug, Clone, Copy, PartialEq)]
60pub enum DebouncedInputEvent {
61 Low,
63 High,
65 Rise,
67 Fall,
69}
70
71impl<Switch: InputSwitch, Config: DebouncedInputConfig> DebouncedInput<Switch, Config> {
72 pub fn new(input_switch: Switch) -> Self {
76 let init_state = if input_switch.is_active().unwrap_or(false) {
77 DebouncedInputState::FixedHigh
78 } else {
79 DebouncedInputState::FixedLow
80 };
81
82 DebouncedInput {
83 input_switch,
84 state: init_state,
85 config: PhantomData::<Config>,
86 }
87 }
88
89 pub fn is_high(&self) -> bool {
91 match self.state {
92 DebouncedInputState::FixedLow | DebouncedInputState::RiseDisturbance(_) => false,
93 DebouncedInputState::FixedHigh | DebouncedInputState::FallDisturbance(_) => true,
94 }
95 }
96
97 pub fn is_low(&self) -> bool {
99 !self.is_high()
100 }
101
102 pub fn borrow_input_switch(&self) -> &Switch {
104 &self.input_switch
105 }
106
107 pub fn release_input_switch(self) -> Switch {
109 self.input_switch
110 }
111}
112
113impl<Switch: InputSwitch, Config: DebouncedInputConfig> Control for DebouncedInput<Switch, Config> {
114 type Event = DebouncedInputEvent;
115 type Error =
116 Error<<<Config::Timer as ElapsedTimer>::Timestamp as Timestamp>::Error, Switch::Error>;
117
118 fn update(&mut self) -> Result<Self::Event, Self::Error> {
119 let now = <Config::Timer as ElapsedTimer>::Timestamp::now();
120 let input_switch_state = self
121 .input_switch
122 .is_active()
123 .map_err(|err| Error::InputSwitch(err))?;
124
125 Ok(match &self.state {
126 DebouncedInputState::FixedLow => {
127 if input_switch_state {
128 self.state = DebouncedInputState::RiseDisturbance(now)
129 }
130 DebouncedInputEvent::Low
131 }
132 DebouncedInputState::FixedHigh => {
133 if !input_switch_state {
134 self.state = DebouncedInputState::FallDisturbance(now)
135 }
136 DebouncedInputEvent::High
137 }
138 DebouncedInputState::RiseDisturbance(start) => {
139 if !input_switch_state {
140 self.state = DebouncedInputState::FixedLow;
141 DebouncedInputEvent::Low
142 } else if Config::DEBOUNCE_TIMER
143 .timeout(start, &now)
144 .map_err(|err| Error::ElapsedTimer(err))?
145 {
146 self.state = DebouncedInputState::FixedHigh;
147 DebouncedInputEvent::Rise
148 } else {
149 DebouncedInputEvent::Low
150 }
151 }
152 DebouncedInputState::FallDisturbance(start) => {
153 if input_switch_state {
154 self.state = DebouncedInputState::FixedHigh;
155 DebouncedInputEvent::High
156 } else if Config::DEBOUNCE_TIMER
157 .timeout(start, &now)
158 .map_err(|err| Error::ElapsedTimer(err))?
159 {
160 self.state = DebouncedInputState::FixedLow;
161 DebouncedInputEvent::Fall
162 } else {
163 DebouncedInputEvent::High
164 }
165 }
166 })
167 }
168}