diff options
| -rw-r--r-- | embassy-time/src/lib.rs | 2 | ||||
| -rw-r--r-- | embassy-time/src/timer.rs | 15 | ||||
| -rw-r--r-- | examples/rp/src/bin/debounce.rs | 80 |
3 files changed, 95 insertions, 2 deletions
diff --git a/embassy-time/src/lib.rs b/embassy-time/src/lib.rs index d27eb92f6..e7efce49c 100644 --- a/embassy-time/src/lib.rs +++ b/embassy-time/src/lib.rs | |||
| @@ -32,7 +32,7 @@ pub use delay::{block_for, Delay}; | |||
| 32 | pub use duration::Duration; | 32 | pub use duration::Duration; |
| 33 | pub use embassy_time_driver::TICK_HZ; | 33 | pub use embassy_time_driver::TICK_HZ; |
| 34 | pub use instant::Instant; | 34 | pub use instant::Instant; |
| 35 | pub use timer::{with_timeout, Ticker, TimeoutError, Timer}; | 35 | pub use timer::{timeout_at, with_timeout, Ticker, TimeoutError, Timer}; |
| 36 | 36 | ||
| 37 | const fn gcd(a: u64, b: u64) -> u64 { | 37 | const fn gcd(a: u64, b: u64) -> u64 { |
| 38 | if b == 0 { | 38 | if b == 0 { |
diff --git a/embassy-time/src/timer.rs b/embassy-time/src/timer.rs index 565a65cb8..dbce9297c 100644 --- a/embassy-time/src/timer.rs +++ b/embassy-time/src/timer.rs | |||
| @@ -8,7 +8,7 @@ use futures_util::{pin_mut, Stream}; | |||
| 8 | 8 | ||
| 9 | use crate::{Duration, Instant}; | 9 | use crate::{Duration, Instant}; |
| 10 | 10 | ||
| 11 | /// Error returned by [`with_timeout`] on timeout. | 11 | /// Error returned by [`with_timeout`] and [`timeout_at`] on timeout. |
| 12 | #[derive(Debug, Clone, PartialEq, Eq)] | 12 | #[derive(Debug, Clone, PartialEq, Eq)] |
| 13 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] | 13 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] |
| 14 | pub struct TimeoutError; | 14 | pub struct TimeoutError; |
| @@ -26,6 +26,19 @@ pub async fn with_timeout<F: Future>(timeout: Duration, fut: F) -> Result<F::Out | |||
| 26 | } | 26 | } |
| 27 | } | 27 | } |
| 28 | 28 | ||
| 29 | /// Runs a given future with a deadline time. | ||
| 30 | /// | ||
| 31 | /// If the future completes before the deadline, its output is returned. Otherwise, on timeout, | ||
| 32 | /// work on the future is stopped (`poll` is no longer called), the future is dropped and `Err(TimeoutError)` is returned. | ||
| 33 | pub async fn timeout_at<F: Future>(at: Instant, fut: F) -> Result<F::Output, TimeoutError> { | ||
| 34 | let timeout_fut = Timer::at(at); | ||
| 35 | pin_mut!(fut); | ||
| 36 | match select(fut, timeout_fut).await { | ||
| 37 | Either::Left((r, _)) => Ok(r), | ||
| 38 | Either::Right(_) => Err(TimeoutError), | ||
| 39 | } | ||
| 40 | } | ||
| 41 | |||
| 29 | /// A future that completes at a specified [Instant](struct.Instant.html). | 42 | /// A future that completes at a specified [Instant](struct.Instant.html). |
| 30 | #[must_use = "futures do nothing unless you `.await` or poll them"] | 43 | #[must_use = "futures do nothing unless you `.await` or poll them"] |
| 31 | pub struct Timer { | 44 | pub struct Timer { |
diff --git a/examples/rp/src/bin/debounce.rs b/examples/rp/src/bin/debounce.rs new file mode 100644 index 000000000..bf1579091 --- /dev/null +++ b/examples/rp/src/bin/debounce.rs | |||
| @@ -0,0 +1,80 @@ | |||
| 1 | //! This example shows the ease of debouncing a button with async rust. | ||
| 2 | //! Hook up a button or switch between pin 9 and ground. | ||
| 3 | |||
| 4 | #![no_std] | ||
| 5 | #![no_main] | ||
| 6 | |||
| 7 | use defmt::info; | ||
| 8 | use embassy_executor::Spawner; | ||
| 9 | use embassy_rp::gpio::{Input, Level, Pull}; | ||
| 10 | use embassy_time::{timeout_at, Duration, Instant, Timer}; | ||
| 11 | use {defmt_rtt as _, panic_probe as _}; | ||
| 12 | |||
| 13 | pub struct Debouncer<'a> { | ||
| 14 | input: Input<'a>, | ||
| 15 | debounce: Duration, | ||
| 16 | } | ||
| 17 | |||
| 18 | impl<'a> Debouncer<'a> { | ||
| 19 | pub fn new(input: Input<'a>, debounce: Duration) -> Self { | ||
| 20 | Self { input, debounce } | ||
| 21 | } | ||
| 22 | |||
| 23 | pub async fn debounce(&mut self) -> Level { | ||
| 24 | loop { | ||
| 25 | let l1 = self.input.get_level(); | ||
| 26 | |||
| 27 | self.input.wait_for_any_edge().await; | ||
| 28 | |||
| 29 | Timer::after(self.debounce).await; | ||
| 30 | |||
| 31 | let l2 = self.input.get_level(); | ||
| 32 | if l1 != l2 { | ||
| 33 | break l2; | ||
| 34 | } | ||
| 35 | } | ||
| 36 | } | ||
| 37 | } | ||
| 38 | |||
| 39 | #[embassy_executor::main] | ||
| 40 | async fn main(_spawner: Spawner) { | ||
| 41 | let p = embassy_rp::init(Default::default()); | ||
| 42 | let mut btn = Debouncer::new(Input::new(p.PIN_9, Pull::Up), Duration::from_millis(20)); | ||
| 43 | |||
| 44 | info!("Debounce Demo"); | ||
| 45 | |||
| 46 | loop { | ||
| 47 | // button pressed | ||
| 48 | btn.debounce().await; | ||
| 49 | let start = Instant::now(); | ||
| 50 | info!("Button Press"); | ||
| 51 | |||
| 52 | match timeout_at(start + Duration::from_secs(1), btn.debounce()).await { | ||
| 53 | // Button Released < 1s | ||
| 54 | Ok(_) => { | ||
| 55 | info!("Button pressed for: {}ms", start.elapsed().as_millis()); | ||
| 56 | continue; | ||
| 57 | } | ||
| 58 | // button held for > 1s | ||
| 59 | Err(_) => { | ||
| 60 | info!("Button Held"); | ||
| 61 | } | ||
| 62 | } | ||
| 63 | |||
| 64 | match timeout_at(start + Duration::from_secs(5), btn.debounce()).await { | ||
| 65 | // Button released <5s | ||
| 66 | Ok(_) => { | ||
| 67 | info!("Button pressed for: {}ms", start.elapsed().as_millis()); | ||
| 68 | continue; | ||
| 69 | } | ||
| 70 | // button held for > >5s | ||
| 71 | Err(_) => { | ||
| 72 | info!("Button Long Held"); | ||
| 73 | } | ||
| 74 | } | ||
| 75 | |||
| 76 | // wait for button release before handling another press | ||
| 77 | btn.debounce().await; | ||
| 78 | info!("Button pressed for: {}ms", start.elapsed().as_millis()); | ||
| 79 | } | ||
| 80 | } | ||
