Merge pull request #2505 from CBJamo/timeout_at
Add timeout_at convenience function and example.
This commit is contained in:
commit
e05f6505ae
3 changed files with 95 additions and 2 deletions
|
@ -32,7 +32,7 @@ pub use delay::{block_for, Delay};
|
||||||
pub use duration::Duration;
|
pub use duration::Duration;
|
||||||
pub use embassy_time_driver::TICK_HZ;
|
pub use embassy_time_driver::TICK_HZ;
|
||||||
pub use instant::Instant;
|
pub use instant::Instant;
|
||||||
pub use timer::{with_timeout, Ticker, TimeoutError, Timer};
|
pub use timer::{with_deadline, with_timeout, Ticker, TimeoutError, Timer};
|
||||||
|
|
||||||
const fn gcd(a: u64, b: u64) -> u64 {
|
const fn gcd(a: u64, b: u64) -> u64 {
|
||||||
if b == 0 {
|
if b == 0 {
|
||||||
|
|
|
@ -8,7 +8,7 @@ use futures_util::{pin_mut, Stream};
|
||||||
|
|
||||||
use crate::{Duration, Instant};
|
use crate::{Duration, Instant};
|
||||||
|
|
||||||
/// Error returned by [`with_timeout`] on timeout.
|
/// Error returned by [`with_timeout`] and [`with_deadline`] on timeout.
|
||||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||||
pub struct TimeoutError;
|
pub struct TimeoutError;
|
||||||
|
@ -26,6 +26,19 @@ pub async fn with_timeout<F: Future>(timeout: Duration, fut: F) -> Result<F::Out
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Runs a given future with a deadline time.
|
||||||
|
///
|
||||||
|
/// If the future completes before the deadline, its output is returned. Otherwise, on timeout,
|
||||||
|
/// work on the future is stopped (`poll` is no longer called), the future is dropped and `Err(TimeoutError)` is returned.
|
||||||
|
pub async fn with_deadline<F: Future>(at: Instant, fut: F) -> Result<F::Output, TimeoutError> {
|
||||||
|
let timeout_fut = Timer::at(at);
|
||||||
|
pin_mut!(fut);
|
||||||
|
match select(fut, timeout_fut).await {
|
||||||
|
Either::Left((r, _)) => Ok(r),
|
||||||
|
Either::Right(_) => Err(TimeoutError),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// A future that completes at a specified [Instant](struct.Instant.html).
|
/// A future that completes at a specified [Instant](struct.Instant.html).
|
||||||
#[must_use = "futures do nothing unless you `.await` or poll them"]
|
#[must_use = "futures do nothing unless you `.await` or poll them"]
|
||||||
pub struct Timer {
|
pub struct Timer {
|
||||||
|
|
80
examples/rp/src/bin/debounce.rs
Normal file
80
examples/rp/src/bin/debounce.rs
Normal file
|
@ -0,0 +1,80 @@
|
||||||
|
//! This example shows the ease of debouncing a button with async rust.
|
||||||
|
//! Hook up a button or switch between pin 9 and ground.
|
||||||
|
|
||||||
|
#![no_std]
|
||||||
|
#![no_main]
|
||||||
|
|
||||||
|
use defmt::info;
|
||||||
|
use embassy_executor::Spawner;
|
||||||
|
use embassy_rp::gpio::{Input, Level, Pull};
|
||||||
|
use embassy_time::{with_deadline, Duration, Instant, Timer};
|
||||||
|
use {defmt_rtt as _, panic_probe as _};
|
||||||
|
|
||||||
|
pub struct Debouncer<'a> {
|
||||||
|
input: Input<'a>,
|
||||||
|
debounce: Duration,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> Debouncer<'a> {
|
||||||
|
pub fn new(input: Input<'a>, debounce: Duration) -> Self {
|
||||||
|
Self { input, debounce }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn debounce(&mut self) -> Level {
|
||||||
|
loop {
|
||||||
|
let l1 = self.input.get_level();
|
||||||
|
|
||||||
|
self.input.wait_for_any_edge().await;
|
||||||
|
|
||||||
|
Timer::after(self.debounce).await;
|
||||||
|
|
||||||
|
let l2 = self.input.get_level();
|
||||||
|
if l1 != l2 {
|
||||||
|
break l2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[embassy_executor::main]
|
||||||
|
async fn main(_spawner: Spawner) {
|
||||||
|
let p = embassy_rp::init(Default::default());
|
||||||
|
let mut btn = Debouncer::new(Input::new(p.PIN_9, Pull::Up), Duration::from_millis(20));
|
||||||
|
|
||||||
|
info!("Debounce Demo");
|
||||||
|
|
||||||
|
loop {
|
||||||
|
// button pressed
|
||||||
|
btn.debounce().await;
|
||||||
|
let start = Instant::now();
|
||||||
|
info!("Button Press");
|
||||||
|
|
||||||
|
match with_deadline(start + Duration::from_secs(1), btn.debounce()).await {
|
||||||
|
// Button Released < 1s
|
||||||
|
Ok(_) => {
|
||||||
|
info!("Button pressed for: {}ms", start.elapsed().as_millis());
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
// button held for > 1s
|
||||||
|
Err(_) => {
|
||||||
|
info!("Button Held");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
match with_deadline(start + Duration::from_secs(5), btn.debounce()).await {
|
||||||
|
// Button released <5s
|
||||||
|
Ok(_) => {
|
||||||
|
info!("Button pressed for: {}ms", start.elapsed().as_millis());
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
// button held for > >5s
|
||||||
|
Err(_) => {
|
||||||
|
info!("Button Long Held");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// wait for button release before handling another press
|
||||||
|
btn.debounce().await;
|
||||||
|
info!("Button pressed for: {}ms", start.elapsed().as_millis());
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in a new issue