diff options
| author | Dario Nieuwenhuis <[email protected]> | 2021-08-24 22:46:07 +0200 |
|---|---|---|
| committer | Dario Nieuwenhuis <[email protected]> | 2021-08-25 21:06:27 +0200 |
| commit | 503be494172aa75ab2804e35a3300da29916f8c0 (patch) | |
| tree | af47df7a586f054a79fb2e72a28f55bdb00ca620 | |
| parent | 09ffdf63f18e263962a8ea9f86c1caf4360cacee (diff) | |
Document embassy::time
| -rw-r--r-- | embassy/Cargo.toml | 8 | ||||
| -rw-r--r-- | embassy/src/time/delay.rs | 9 | ||||
| -rw-r--r-- | embassy/src/time/driver.rs | 59 | ||||
| -rw-r--r-- | embassy/src/time/duration.rs | 9 | ||||
| -rw-r--r-- | embassy/src/time/instant.rs | 26 | ||||
| -rw-r--r-- | embassy/src/time/mod.rs | 56 | ||||
| -rw-r--r-- | embassy/src/time/timer.rs | 6 |
7 files changed, 150 insertions, 23 deletions
diff --git a/embassy/Cargo.toml b/embassy/Cargo.toml index ca7d38a55..ec76299e1 100644 --- a/embassy/Cargo.toml +++ b/embassy/Cargo.toml | |||
| @@ -9,7 +9,15 @@ resolver = "2" | |||
| 9 | default = [] | 9 | default = [] |
| 10 | std = ["futures/std", "embassy-traits/std"] | 10 | std = ["futures/std", "embassy-traits/std"] |
| 11 | 11 | ||
| 12 | # Enable `embassy::time` module. | ||
| 13 | # NOTE: This feature is only intended to be enabled by crates providing the time driver implementation. | ||
| 14 | # Enabling it directly without supplying a time driver will fail to link. | ||
| 12 | time = [] | 15 | time = [] |
| 16 | |||
| 17 | # Set the `embassy::time` tick rate. | ||
| 18 | # NOTE: This feature is only intended to be enabled by crates providing the time driver implementation. | ||
| 19 | # If you're not writing your own driver, check the driver documentation to customize the tick rate. | ||
| 20 | # If you're writing a driver and your tick rate is not listed here, please add it and send a PR! | ||
| 13 | time-tick-32768hz = ["time"] | 21 | time-tick-32768hz = ["time"] |
| 14 | time-tick-1000hz = ["time"] | 22 | time-tick-1000hz = ["time"] |
| 15 | time-tick-1mhz = ["time"] | 23 | time-tick-1mhz = ["time"] |
diff --git a/embassy/src/time/delay.rs b/embassy/src/time/delay.rs index c97e8b5c2..a46ee3a4f 100644 --- a/embassy/src/time/delay.rs +++ b/embassy/src/time/delay.rs | |||
| @@ -4,11 +4,10 @@ use super::{Duration, Instant, Timer}; | |||
| 4 | 4 | ||
| 5 | /// Type implementing async delays and blocking `embedded-hal` delays. | 5 | /// Type implementing async delays and blocking `embedded-hal` delays. |
| 6 | /// | 6 | /// |
| 7 | /// For this interface to work, the Executor's clock must be correctly initialized before using it. | ||
| 8 | /// The delays are implemented in a "best-effort" way, meaning that the cpu will block for at least | 7 | /// The delays are implemented in a "best-effort" way, meaning that the cpu will block for at least |
| 9 | /// the amount provided, but accuracy can be affected by many factors, including interrupt usage. | 8 | /// the amount provided, but accuracy can be affected by many factors, including interrupt usage. |
| 10 | /// Make sure to use a suitable tick rate for your use case. The tick rate can be chosen through | 9 | /// Make sure to use a suitable tick rate for your use case. The tick rate is defined by the currently |
| 11 | /// features flags of this crate. | 10 | /// active driver. |
| 12 | pub struct Delay; | 11 | pub struct Delay; |
| 13 | 12 | ||
| 14 | impl crate::traits::delay::Delay for Delay { | 13 | impl crate::traits::delay::Delay for Delay { |
| @@ -58,9 +57,7 @@ impl embedded_hal::blocking::delay::DelayUs<u32> for Delay { | |||
| 58 | } | 57 | } |
| 59 | } | 58 | } |
| 60 | 59 | ||
| 61 | /// Blocks the cpu for at least `duration`. | 60 | /// Blocks for at least `duration`. |
| 62 | /// | ||
| 63 | /// For this interface to work, the Executor's clock must be correctly initialized before using it. | ||
| 64 | pub fn block_for(duration: Duration) { | 61 | pub fn block_for(duration: Duration) { |
| 65 | let expires_at = Instant::now() + duration; | 62 | let expires_at = Instant::now() + duration; |
| 66 | while Instant::now() < expires_at {} | 63 | while Instant::now() < expires_at {} |
diff --git a/embassy/src/time/driver.rs b/embassy/src/time/driver.rs index b09cffd8c..21817b92f 100644 --- a/embassy/src/time/driver.rs +++ b/embassy/src/time/driver.rs | |||
| @@ -1,3 +1,58 @@ | |||
| 1 | //! Time driver interface | ||
| 2 | //! | ||
| 3 | //! This module defines the interface a driver needs to implement to power the `embassy::time` module. | ||
| 4 | //! | ||
| 5 | //! # Implementing a driver | ||
| 6 | //! | ||
| 7 | //! - Define a struct `MyDriver` | ||
| 8 | //! - Implement [`Driver`] for it | ||
| 9 | //! - Register it as the global driver with [`time_driver_impl`]. | ||
| 10 | //! - Enable the Cargo features `embassy/time` and one of `embassy/time-tick-*` corresponding to the | ||
| 11 | //! tick rate of your driver. | ||
| 12 | //! | ||
| 13 | //! If you wish to make the tick rate configurable by the end user, you should do so by exposing your own | ||
| 14 | //! Cargo features and having each enable the corresponding `embassy/time-tick-*`. | ||
| 15 | //! | ||
| 16 | //! # Linkage details | ||
| 17 | //! | ||
| 18 | //! Instead of the usual "trait + generic params" approach, calls from embassy to the driver are done via `extern` functions. | ||
| 19 | //! | ||
| 20 | //! `embassy` internally defines the driver functions as `extern "Rust" { fn _embassy_time_now() -> u64; }` and calls them. | ||
| 21 | //! The driver crate defines the functions as `#[no_mangle] fn _embassy_time_now() -> u64`. The linker will resolve the | ||
| 22 | //! calls from the `embassy` crate to call into the driver crate. | ||
| 23 | //! | ||
| 24 | //! If there is none or multiple drivers in the crate tree, linking will fail. | ||
| 25 | //! | ||
| 26 | //! This method has a few key advantages for something as foundational as timekeeping: | ||
| 27 | //! | ||
| 28 | //! - The time driver is available everywhere easily, without having to thread the implementation | ||
| 29 | //~ through generic parameters. This is especially helpful for libraries. | ||
| 30 | //! - It means comparing `Instant`s will always make sense: if there were multiple drivers | ||
| 31 | //! active, one could compare an `Instant` from driver A to an `Instant` from driver B, which | ||
| 32 | //! would yield incorrect results. | ||
| 33 | //! | ||
| 34 | /// # Example | ||
| 35 | /// | ||
| 36 | /// ``` | ||
| 37 | /// struct MyDriver; // not public! | ||
| 38 | /// embassy::time_driver_impl!(MyDriver); | ||
| 39 | /// | ||
| 40 | /// unsafe impl embassy::time::driver::Driver for MyDriver { | ||
| 41 | /// fn now() -> u64 { | ||
| 42 | /// todo!() | ||
| 43 | /// } | ||
| 44 | /// unsafe fn allocate_alarm() -> Option<AlarmHandle> { | ||
| 45 | /// todo!() | ||
| 46 | /// } | ||
| 47 | /// fn set_alarm_callback(alarm: AlarmHandle, callback: fn(*mut ()), ctx: *mut ()) { | ||
| 48 | /// todo!() | ||
| 49 | /// } | ||
| 50 | /// fn set_alarm(alarm: AlarmHandle, timestamp: u64) { | ||
| 51 | /// todo!() | ||
| 52 | /// } | ||
| 53 | /// } | ||
| 54 | /// ``` | ||
| 55 | |||
| 1 | /// Alarm handle, assigned by the driver. | 56 | /// Alarm handle, assigned by the driver. |
| 2 | #[derive(Clone, Copy)] | 57 | #[derive(Clone, Copy)] |
| 3 | pub struct AlarmHandle { | 58 | pub struct AlarmHandle { |
| @@ -73,7 +128,7 @@ pub(crate) fn set_alarm(alarm: AlarmHandle, timestamp: u64) { | |||
| 73 | /// # Example | 128 | /// # Example |
| 74 | /// | 129 | /// |
| 75 | /// ``` | 130 | /// ``` |
| 76 | /// struct MyDriver; | 131 | /// struct MyDriver; // not public! |
| 77 | /// embassy::time_driver_impl!(MyDriver); | 132 | /// embassy::time_driver_impl!(MyDriver); |
| 78 | /// | 133 | /// |
| 79 | /// unsafe impl embassy::time::driver::Driver for MyDriver { | 134 | /// unsafe impl embassy::time::driver::Driver for MyDriver { |
| @@ -90,7 +145,7 @@ pub(crate) fn set_alarm(alarm: AlarmHandle, timestamp: u64) { | |||
| 90 | /// todo!() | 145 | /// todo!() |
| 91 | /// } | 146 | /// } |
| 92 | /// } | 147 | /// } |
| 93 | /// | 148 | /// ``` |
| 94 | #[macro_export] | 149 | #[macro_export] |
| 95 | macro_rules! time_driver_impl { | 150 | macro_rules! time_driver_impl { |
| 96 | ($t: ty) => { | 151 | ($t: ty) => { |
diff --git a/embassy/src/time/duration.rs b/embassy/src/time/duration.rs index 5157450ad..e196a00c7 100644 --- a/embassy/src/time/duration.rs +++ b/embassy/src/time/duration.rs | |||
| @@ -11,18 +11,27 @@ pub struct Duration { | |||
| 11 | } | 11 | } |
| 12 | 12 | ||
| 13 | impl Duration { | 13 | impl Duration { |
| 14 | /// The smallest value that can be represented by the `Duration` type. | ||
| 15 | pub const MIN: Duration = Duration { ticks: u64::MIN }; | ||
| 16 | /// The largest value that can be represented by the `Duration` type. | ||
| 17 | pub const MAX: Duration = Duration { ticks: u64::MAX }; | ||
| 18 | |||
| 19 | /// Tick count of the `Duration`. | ||
| 14 | pub const fn as_ticks(&self) -> u64 { | 20 | pub const fn as_ticks(&self) -> u64 { |
| 15 | self.ticks | 21 | self.ticks |
| 16 | } | 22 | } |
| 17 | 23 | ||
| 24 | /// Convert the `Duration` to seconds, rounding down. | ||
| 18 | pub const fn as_secs(&self) -> u64 { | 25 | pub const fn as_secs(&self) -> u64 { |
| 19 | self.ticks / TICKS_PER_SECOND | 26 | self.ticks / TICKS_PER_SECOND |
| 20 | } | 27 | } |
| 21 | 28 | ||
| 29 | /// Convert the `Duration` to milliseconds, rounding down. | ||
| 22 | pub const fn as_millis(&self) -> u64 { | 30 | pub const fn as_millis(&self) -> u64 { |
| 23 | self.ticks * 1000 / TICKS_PER_SECOND | 31 | self.ticks * 1000 / TICKS_PER_SECOND |
| 24 | } | 32 | } |
| 25 | 33 | ||
| 34 | /// Convert the `Duration` to microseconds, rounding down. | ||
| 26 | pub const fn as_micros(&self) -> u64 { | 35 | pub const fn as_micros(&self) -> u64 { |
| 27 | self.ticks * 1_000_000 / TICKS_PER_SECOND | 36 | self.ticks * 1_000_000 / TICKS_PER_SECOND |
| 28 | } | 37 | } |
diff --git a/embassy/src/time/instant.rs b/embassy/src/time/instant.rs index 3d430d886..36c2b2dc2 100644 --- a/embassy/src/time/instant.rs +++ b/embassy/src/time/instant.rs | |||
| @@ -11,7 +11,9 @@ pub struct Instant { | |||
| 11 | } | 11 | } |
| 12 | 12 | ||
| 13 | impl Instant { | 13 | impl Instant { |
| 14 | /// The smallest (earliest) value that can be represented by the `Instant` type. | ||
| 14 | pub const MIN: Instant = Instant { ticks: u64::MIN }; | 15 | pub const MIN: Instant = Instant { ticks: u64::MIN }; |
| 16 | /// The largest (latest) value that can be represented by the `Instant` type. | ||
| 15 | pub const MAX: Instant = Instant { ticks: u64::MAX }; | 17 | pub const MAX: Instant = Instant { ticks: u64::MAX }; |
| 16 | 18 | ||
| 17 | /// Returns an Instant representing the current time. | 19 | /// Returns an Instant representing the current time. |
| @@ -21,39 +23,38 @@ impl Instant { | |||
| 21 | } | 23 | } |
| 22 | } | 24 | } |
| 23 | 25 | ||
| 24 | /// Instant as clock ticks since MCU start. | 26 | /// Create an Instant from a tick count since system boot. |
| 25 | pub const fn from_ticks(ticks: u64) -> Self { | 27 | pub const fn from_ticks(ticks: u64) -> Self { |
| 26 | Self { ticks } | 28 | Self { ticks } |
| 27 | } | 29 | } |
| 28 | 30 | ||
| 29 | /// Instant as milliseconds since MCU start. | 31 | /// Create an Instant from a millisecond count since system boot. |
| 30 | pub const fn from_millis(millis: u64) -> Self { | 32 | pub const fn from_millis(millis: u64) -> Self { |
| 31 | Self { | 33 | Self { |
| 32 | ticks: millis * TICKS_PER_SECOND as u64 / 1000, | 34 | ticks: millis * TICKS_PER_SECOND / 1000, |
| 33 | } | 35 | } |
| 34 | } | 36 | } |
| 35 | 37 | ||
| 36 | /// Instant representing seconds since MCU start. | 38 | /// Create an Instant from a second count since system boot. |
| 37 | pub const fn from_secs(seconds: u64) -> Self { | 39 | pub const fn from_secs(seconds: u64) -> Self { |
| 38 | Self { | 40 | Self { |
| 39 | ticks: seconds * TICKS_PER_SECOND as u64, | 41 | ticks: seconds * TICKS_PER_SECOND, |
| 40 | } | 42 | } |
| 41 | } | 43 | } |
| 42 | 44 | ||
| 43 | /// Instant as ticks since MCU start. | 45 | /// Tick count since system boot. |
| 44 | |||
| 45 | pub const fn as_ticks(&self) -> u64 { | 46 | pub const fn as_ticks(&self) -> u64 { |
| 46 | self.ticks | 47 | self.ticks |
| 47 | } | 48 | } |
| 48 | /// Instant as seconds since MCU start. | ||
| 49 | 49 | ||
| 50 | /// Seconds since system boot. | ||
| 50 | pub const fn as_secs(&self) -> u64 { | 51 | pub const fn as_secs(&self) -> u64 { |
| 51 | self.ticks / TICKS_PER_SECOND as u64 | 52 | self.ticks / TICKS_PER_SECOND |
| 52 | } | 53 | } |
| 53 | /// Instant as miliseconds since MCU start. | ||
| 54 | 54 | ||
| 55 | /// Milliseconds since system boot. | ||
| 55 | pub const fn as_millis(&self) -> u64 { | 56 | pub const fn as_millis(&self) -> u64 { |
| 56 | self.ticks * 1000 / TICKS_PER_SECOND as u64 | 57 | self.ticks * 1000 / TICKS_PER_SECOND |
| 57 | } | 58 | } |
| 58 | 59 | ||
| 59 | /// Duration between this Instant and another Instant | 60 | /// Duration between this Instant and another Instant |
| @@ -92,11 +93,14 @@ impl Instant { | |||
| 92 | Instant::now() - *self | 93 | Instant::now() - *self |
| 93 | } | 94 | } |
| 94 | 95 | ||
| 96 | /// Adds one Duration to self, returning a new `Instant` or None in the event of an overflow. | ||
| 95 | pub fn checked_add(&self, duration: Duration) -> Option<Instant> { | 97 | pub fn checked_add(&self, duration: Duration) -> Option<Instant> { |
| 96 | self.ticks | 98 | self.ticks |
| 97 | .checked_add(duration.ticks) | 99 | .checked_add(duration.ticks) |
| 98 | .map(|ticks| Instant { ticks }) | 100 | .map(|ticks| Instant { ticks }) |
| 99 | } | 101 | } |
| 102 | |||
| 103 | /// Subtracts one Duration to self, returning a new `Instant` or None in the event of an overflow. | ||
| 100 | pub fn checked_sub(&self, duration: Duration) -> Option<Instant> { | 104 | pub fn checked_sub(&self, duration: Duration) -> Option<Instant> { |
| 101 | self.ticks | 105 | self.ticks |
| 102 | .checked_sub(duration.ticks) | 106 | .checked_sub(duration.ticks) |
diff --git a/embassy/src/time/mod.rs b/embassy/src/time/mod.rs index f5ad1b3ff..db48fb3ec 100644 --- a/embassy/src/time/mod.rs +++ b/embassy/src/time/mod.rs | |||
| @@ -1,4 +1,44 @@ | |||
| 1 | //! Time abstractions | 1 | //! Timekeeping, delays and timeouts. |
| 2 | //! | ||
| 3 | //! Timekeeping is done with elapsed time since system boot. Time is represented in | ||
| 4 | //! ticks, where the tick rate is defined by the current driver, usually to match | ||
| 5 | //! the tick rate of the hardware. | ||
| 6 | //! | ||
| 7 | //! Tick counts are 64 bits. At the highest supported tick rate of 1Mhz this supports | ||
| 8 | //! representing time spans of up to ~584558 years, which is big enough for all practical | ||
| 9 | //! purposes and allows not having to worry about overflows. | ||
| 10 | //! | ||
| 11 | //! [`Instant`] represents a given instant of time (relative to system boot), and [`Duration`] | ||
| 12 | //! represents the duration of a span of time. They implement the math operations you'd expect, | ||
| 13 | //! like addition and substraction. | ||
| 14 | //! | ||
| 15 | //! # Delays and timeouts | ||
| 16 | //! | ||
| 17 | //! [`Timer`] allows performing async delays. [`Ticker`] allows periodic delays without drifting over time. | ||
| 18 | //! | ||
| 19 | //! An implementation of the `embedded-hal` delay traits is provided by [`Delay`], for compatibility | ||
| 20 | //! with libraries from the ecosystem. | ||
| 21 | //! | ||
| 22 | //! # Wall-clock time | ||
| 23 | //! | ||
| 24 | //! The `time` module deals exclusively with a monotonically increasing tick count. | ||
| 25 | //! Therefore it has no direct support for wall-clock time ("real life" datetimes | ||
| 26 | //! like `2021-08-24 13:33:21`). | ||
| 27 | //! | ||
| 28 | //! If persistence across reboots is not needed, support can be built on top of | ||
| 29 | //! `embassy::time` by storing the offset between "seconds elapsed since boot" | ||
| 30 | //! and "seconds since unix epoch". | ||
| 31 | //! | ||
| 32 | //! # Time driver | ||
| 33 | //! | ||
| 34 | //! The `time` module is backed by a global "time driver" specified at build time. | ||
| 35 | //! Only one driver can be active in a program. | ||
| 36 | //! | ||
| 37 | //! All methods and structs transparently call into the active driver. This makes it | ||
| 38 | //! possible for libraries to use `embassy::time` in a driver-agnostic way without | ||
| 39 | //! requiring generic parameters. | ||
| 40 | //! | ||
| 41 | //! For more details, check the [`driver`] module. | ||
| 2 | 42 | ||
| 3 | mod delay; | 43 | mod delay; |
| 4 | pub mod driver; | 44 | pub mod driver; |
| @@ -12,10 +52,18 @@ pub use instant::Instant; | |||
| 12 | pub use timer::{with_timeout, Ticker, TimeoutError, Timer}; | 52 | pub use timer::{with_timeout, Ticker, TimeoutError, Timer}; |
| 13 | 53 | ||
| 14 | #[cfg(feature = "time-tick-1000hz")] | 54 | #[cfg(feature = "time-tick-1000hz")] |
| 15 | pub const TICKS_PER_SECOND: u64 = 1_000; | 55 | const TPS: u64 = 1_000; |
| 16 | 56 | ||
| 17 | #[cfg(feature = "time-tick-32768hz")] | 57 | #[cfg(feature = "time-tick-32768hz")] |
| 18 | pub const TICKS_PER_SECOND: u64 = 32_768; | 58 | const TPS: u64 = 32_768; |
| 19 | 59 | ||
| 20 | #[cfg(feature = "time-tick-1mhz")] | 60 | #[cfg(feature = "time-tick-1mhz")] |
| 21 | pub const TICKS_PER_SECOND: u64 = 1_000_000; | 61 | const TPS: u64 = 1_000_000; |
| 62 | |||
| 63 | /// Ticks per second of the global timebase. | ||
| 64 | /// | ||
| 65 | /// This value is specified by the `time-tick-*` Cargo features, which | ||
| 66 | /// should be set by the time driver. Some drivers support a fixed tick rate, others | ||
| 67 | /// allow you to choose a tick rate with Cargo features of their own. You should not | ||
| 68 | /// set the `time-tick-*` features for embassy yourself as an end user. | ||
| 69 | pub const TICKS_PER_SECOND: u64 = TPS; | ||
diff --git a/embassy/src/time/timer.rs b/embassy/src/time/timer.rs index 22f1ffe30..d1ed6a512 100644 --- a/embassy/src/time/timer.rs +++ b/embassy/src/time/timer.rs | |||
| @@ -6,7 +6,13 @@ use futures::{future::select, future::Either, pin_mut, Stream}; | |||
| 6 | use crate::executor::raw; | 6 | use crate::executor::raw; |
| 7 | use crate::time::{Duration, Instant}; | 7 | use crate::time::{Duration, Instant}; |
| 8 | 8 | ||
| 9 | /// Error returned by [`with_timeout`] on timeout. | ||
| 9 | pub struct TimeoutError; | 10 | pub struct TimeoutError; |
| 11 | |||
| 12 | /// Runs a given future with a timeout. | ||
| 13 | /// | ||
| 14 | /// If the future completes before the timeout, its output is returned. Otherwise, on timeout, | ||
| 15 | /// work on the future is stopped (`poll` is no longer called), the future is dropped and `Err(TimeoutError)` is returned. | ||
| 10 | pub async fn with_timeout<F: Future>(timeout: Duration, fut: F) -> Result<F::Output, TimeoutError> { | 16 | pub async fn with_timeout<F: Future>(timeout: Duration, fut: F) -> Result<F::Output, TimeoutError> { |
| 11 | let timeout_fut = Timer::after(timeout); | 17 | let timeout_fut = Timer::after(timeout); |
| 12 | pin_mut!(fut); | 18 | pin_mut!(fut); |
