aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorCaleb Jamison <[email protected]>2024-01-31 14:04:48 -0500
committerCaleb Jamison <[email protected]>2024-01-31 14:10:31 -0500
commit1e698af05bc6e7e520d3f35ef661f34ea6ea359e (patch)
tree98ff0a085ef03dc3c1c017eba09d28ea6ead8738
parent68be63c0e8876363580f09d340a95ac35b52614f (diff)
Add timeout_at convenience function and example.
-rw-r--r--embassy-time/src/lib.rs2
-rw-r--r--embassy-time/src/timer.rs15
-rw-r--r--examples/rp/src/bin/debounce.rs80
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};
32pub use duration::Duration; 32pub use duration::Duration;
33pub use embassy_time_driver::TICK_HZ; 33pub use embassy_time_driver::TICK_HZ;
34pub use instant::Instant; 34pub use instant::Instant;
35pub use timer::{with_timeout, Ticker, TimeoutError, Timer}; 35pub use timer::{timeout_at, with_timeout, Ticker, TimeoutError, Timer};
36 36
37const fn gcd(a: u64, b: u64) -> u64 { 37const 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
9use crate::{Duration, Instant}; 9use 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))]
14pub struct TimeoutError; 14pub 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.
33pub 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"]
31pub struct Timer { 44pub 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
7use defmt::info;
8use embassy_executor::Spawner;
9use embassy_rp::gpio::{Input, Level, Pull};
10use embassy_time::{timeout_at, Duration, Instant, Timer};
11use {defmt_rtt as _, panic_probe as _};
12
13pub struct Debouncer<'a> {
14 input: Input<'a>,
15 debounce: Duration,
16}
17
18impl<'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]
40async 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}