From d1ae2c22e8e2d79da81c7d2fdd58fbfb8b33209f Mon Sep 17 00:00:00 2001 From: Peter Taylor Date: Thu, 25 Jun 2020 13:21:52 -0600 Subject: [PATCH 01/23] refactoring --- examples/nrf52_dk/main.rs | 42 ++++++++++++++------------------------- src/clock.rs | 4 +--- src/lib.rs | 2 +- 3 files changed, 17 insertions(+), 31 deletions(-) diff --git a/examples/nrf52_dk/main.rs b/examples/nrf52_dk/main.rs index d147614..c5465a8 100644 --- a/examples/nrf52_dk/main.rs +++ b/examples/nrf52_dk/main.rs @@ -3,7 +3,6 @@ extern crate panic_rtt; -use core::borrow::Borrow; use cortex_m::mutex::CriticalSectionMutex as Mutex; use cortex_m_rt::entry; use embedded_time::{self as time, Clock, Instant, Period, TimeInt}; @@ -16,13 +15,13 @@ pub mod nrf52 { target::{self as pac, Peripherals}, }; - pub struct Timer64 { + pub struct Clock64 { low: pac::TIMER0, high: pac::TIMER1, capture_task: pac::EGU0, } - impl Timer64 { + impl Clock64 { pub fn take(low: pac::TIMER0, high: pac::TIMER1, capture_task: pac::EGU0) -> Self { Self { low, @@ -45,8 +44,8 @@ impl time::Clock for SysClock { const PERIOD: Period = Period::new(1, 16_000_000); fn now() -> Instant { - let ticks = (&SYSTEM_TICKS).lock(|timer64| match timer64 { - Some(timer64) => timer64.read(), + let ticks = (&CLOCK64).lock(|clock| match clock { + Some(clock) => clock.read(), None => 0, }); @@ -54,7 +53,7 @@ impl time::Clock for SysClock { } } -static SYSTEM_TICKS: Mutex> = Mutex::new(None); +static CLOCK64: Mutex> = Mutex::new(None); #[entry] fn main() -> ! { @@ -76,35 +75,21 @@ fn main() -> ! { unsafe { device.PPI.ch[0].eep.write(|w| { - w.bits( - device.TIMER0.events_compare[1].borrow() as *const nrf52::pac::generic::Reg<_, _> - as u32, - ) + w.bits(&device.TIMER0.events_compare[1] as *const nrf52::pac::generic::Reg<_, _> as u32) }); device.PPI.ch[0].tep.write(|w| { - w.bits( - device.TIMER1.tasks_count.borrow() as *const nrf52::pac::generic::Reg<_, _> as u32, - ) + w.bits(&device.TIMER1.tasks_count as *const nrf52::pac::generic::Reg<_, _> as u32) }); device.PPI.chen.modify(|_, w| w.ch0().enabled()); device.PPI.ch[1].eep.write(|w| { - w.bits( - device.EGU0.events_triggered[0].borrow() as *const nrf52::pac::generic::Reg<_, _> - as u32, - ) + w.bits(&device.EGU0.events_triggered[0] as *const nrf52::pac::generic::Reg<_, _> as u32) }); device.PPI.ch[1].tep.write(|w| { - w.bits( - device.TIMER0.tasks_capture[0].borrow() as *const nrf52::pac::generic::Reg<_, _> - as u32, - ) + w.bits(&device.TIMER0.tasks_capture[0] as *const nrf52::pac::generic::Reg<_, _> as u32) }); device.PPI.fork[1].tep.write(|w| { - w.bits( - device.TIMER1.tasks_capture[0].borrow() as *const nrf52::pac::generic::Reg<_, _> - as u32, - ) + w.bits(&device.TIMER1.tasks_capture[0] as *const nrf52::pac::generic::Reg<_, _> as u32) }); device.PPI.chen.modify(|_, w| w.ch1().enabled()); } @@ -118,8 +103,11 @@ fn main() -> ! { .tasks_start .write(|write| unsafe { write.bits(1) }); - let timer64 = nrf52::Timer64::take(device.TIMER0, device.TIMER1, device.EGU0); - (&SYSTEM_TICKS).lock(|ticks| *ticks = Some(timer64)); + // This moves these peripherals to prevent conflicting usage, however not the entire EGU0 is + // used. A ref to EGU0 could be sent instead, although that provides no protection for the + // fields that are being used by Clock64. + let clock64 = nrf52::Clock64::take(device.TIMER0, device.TIMER1, device.EGU0); + (&CLOCK64).lock(|ticks| *ticks = Some(clock64)); let port0 = nrf52::gpio::p0::Parts::new(device.P0); diff --git a/src/clock.rs b/src/clock.rs index a14386f..bcb7e85 100644 --- a/src/clock.rs +++ b/src/clock.rs @@ -13,10 +13,8 @@ pub trait Clock: Sized { fn now() -> Instant; /// Blocking delay - fn delay(dur: Dur) + fn delay(dur: Dur) where - Dur: Duration, - Dur::Rep: TimeInt, Self::Rep: TryFrom, { let start = Self::now(); diff --git a/src/lib.rs b/src/lib.rs index 38b3d9a..6fb26d2 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -13,7 +13,7 @@ //! [`Milliseconds`]: units::Milliseconds //! //! ## Definitions -//! **Clock**: Any entity that periodically counts (ie a hardware timer peripheral). Generally, +//! **Clock**: Any entity that periodically counts (ie a hardware timer/counter peripheral). Generally, //! this needs to be monotonic. A wrapping clock is considered monotonic in this context as long as //! it fulfills the other requirements. //! From dcc88b7d92f3cfd4c051b421d8d84ef18cfa1748 Mon Sep 17 00:00:00 2001 From: Peter Taylor Date: Fri, 26 Jun 2020 18:26:39 -0600 Subject: [PATCH 02/23] docs: Add changelog back to v0.5.0 release --- CHANGELOG.md | 63 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 63 insertions(+) create mode 100644 CHANGELOG.md diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..9f06bfb --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,63 @@ +# Changelog + +## [Unreleased] + +### Added + +- Changelog back to v0.5.0 release + +### Changed + +- Refactor `examples/nrf52_dk` + +[unreleased]: https://github.com/FluenTech/embedded-time/compare/v0.5.2...HEAD + +## [0.5.2] - 2020-06-21 + +### Added + +- Ability to convert to/from [`core::time::Duration`](https://doc.rust-lang.org/stable/core/time/struct.Duration.html) +- Missing documentation + +### Changed + +- Moved majority of `Duration`-related documentation to `Duration` trait +- Minor refactoring + +[0.5.2]: https://github.com/FluenTech/embedded-time/compare/v0.5.1...v0.5.2 + + +## [0.5.1] - 2020-06-21 + +### Changed + +- Repository location + +### Removed + +- `Period` from `prelude` mod as it is no longer a trait + +[0.5.1]: https://github.com/FluenTech/embedded-time/compare/v0.5.0...v0.5.1 + + +## [0.5.0] - 2020-06-17 + +### Added + +- `cargo doc` CI test +- Frequency-based type (`Hertz`) with conversion to/from `Period` +- CI tests for `stable` + +### Changed + +- Rename `duration::time_units` to `duration::units` (`units` is also re-exported) +- Rename `TimeRep` to `TimeInt` +- Update `num` to v0.3.x +- Make `Period` a struct that wraps a `Ratio`, rather than a trait + +### Removed + +- `associated_type_bounds` feature flag to allow `stable` build +- Re-export of the `duration` module (wasn't useful) + +[0.5.0]: https://github.com/FluenTech/embedded-time/compare/v0.4.0...v0.5.0 From 72d18a1be51b0a59dfb4c37d91fa0d0a495e26ca Mon Sep 17 00:00:00 2001 From: Peter Taylor Date: Tue, 30 Jun 2020 16:55:46 -0600 Subject: [PATCH 03/23] chore: Update dependency versions --- Cargo.lock | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 63e4771..050557d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -64,9 +64,9 @@ dependencies = [ [[package]] name = "embedded-hal" -version = "0.2.3" +version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ee4908a155094da7723c2d60d617b820061e3b4efcc3d9e293d206a5a76c170b" +checksum = "fa998ce59ec9765d15216393af37a58961ddcefb14c753b4816ba2191d865fcb" dependencies = [ "nb", "void", @@ -284,9 +284,9 @@ checksum = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3" [[package]] name = "syn" -version = "1.0.31" +version = "1.0.33" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b5304cfdf27365b7585c25d4af91b35016ed21ef88f17ced89c7093b43dba8b6" +checksum = "e8d5d96e8cbb005d6959f119f773bfaebb5684296108fb32600c00cde305b2cd" dependencies = [ "proc-macro2", "quote", @@ -301,9 +301,9 @@ checksum = "373c8a200f9e67a0c95e62a4f52fbf80c23b4381c05a17845531982fa99e6b33" [[package]] name = "unicode-xid" -version = "0.2.0" +version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "826e7639553986605ec5979c7dd957c7895e93eabed50ab2ffa7f6128a75097c" +checksum = "f7fe0bb3479651439c9112f72b6c505038574c9fbb575ed1bf3b797fa39dd564" [[package]] name = "vcell" From 522c03dc47c1f6a56062127ba64dcba84c938947 Mon Sep 17 00:00:00 2001 From: Peter Taylor Date: Tue, 30 Jun 2020 16:57:51 -0600 Subject: [PATCH 04/23] docs: Cleanup - wrap comments - fix documentation links - add to crate-level documentation --- src/duration.rs | 3 ++- src/instant.rs | 5 +++-- src/lib.rs | 24 +++++++++++++++--------- 3 files changed, 20 insertions(+), 12 deletions(-) diff --git a/src/duration.rs b/src/duration.rs index 9948946..028c167 100644 --- a/src/duration.rs +++ b/src/duration.rs @@ -210,7 +210,8 @@ pub trait Duration: Sized + Copy + fmt::Display { /// The inner type of the `Duration` representing the count of the implementation unit type Rep: TimeInt; - /// A fraction/ratio representing the period of the count's LSbit. The precision of the `Duration`. + /// A fraction/ratio representing the period of the count's LSbit. The precision of the + /// `Duration`. const PERIOD: Period; /// Not generally useful or needed as the duration can be constructed like this: diff --git a/src/instant.rs b/src/instant.rs index 452b0ba..5055863 100644 --- a/src/instant.rs +++ b/src/instant.rs @@ -4,7 +4,7 @@ use crate::Duration; use core::{cmp::Ordering, convert::TryFrom, ops}; use num::traits::{WrappingAdd, WrappingSub}; -/// Represents an instant of time relative to a specific [`Clock`](crate::clock::Clock) +/// Represents an instant of time relative to a specific [`Clock`](trait.Clock.html) /// /// # Example /// Create an `Instant` that is `23 * SomeClock::PERIOD` seconds since the clock's epoch: @@ -55,7 +55,8 @@ impl Instant { Dur::from_ticks(self.ticks.wrapping_sub(&other.ticks), Clock::PERIOD) } - /// Returns the [`Duration`] (in the provided units) since the beginning of time (or the [`Clock`](crate::clock::Clock)'s 0) + /// Returns the [`Duration`](trait.Duration.html) (in the provided units) since the beginning of + /// time (or the [`Clock`](trait.Clock.html)'s 0) pub fn duration_since_epoch(&self) -> Option where Dur::Rep: TryFrom, diff --git a/src/lib.rs b/src/lib.rs index 6fb26d2..3f73df2 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -2,20 +2,26 @@ //! hardware to generate [`Instant`]s and using [`Duration`]s ([`Seconds`], [`Milliseconds`], etc) //! in embedded systems. The approach is similar to the C++ `chrono` library. A [`Duration`] //! consists of an integer (whose type is chosen by the user to be either [`i32`] or [`i64`]) as -//! well as a `const` ratio where the integer value multiplied by the ratio is the [`Duration`] in -//! seconds. Put another way, the ratio is the precision of the LSbit of the integer. This structure -//! avoids unnecessary arithmetic. For example, if the [`Duration`] type is [`Milliseconds`], a call -//! to the [`Duration::count()`] method simply returns the stored integer value directly which is -//! the number of milliseconds being represented. Conversion arithmetic is only performed when -//! explicitly converting between time units. +//! well as a `const` fraction ([`Period`]) where the integer value multiplied by the fraction is +//! the [`Duration`] in seconds. Put another way, the ratio is the precision of the LSbit of the +//! integer. This structure avoids unnecessary arithmetic. For example, if the [`Duration`] type is +//! [`Milliseconds`], a call to the [`Duration::count()`] method simply returns the stored integer +//! value directly which is the number of milliseconds being represented. Conversion arithmetic is +//! only performed when explicitly converting between time units. +//! +//! In addition frequency-type types are available including [`Hertz`] ([`i32`]) and it's reciprocal +//! [`Period`] ([`i32`]/[`i32`] seconds). //! //! [`Seconds`]: units::Seconds //! [`Milliseconds`]: units::Milliseconds +//! [`Hertz`]: units::Hertz +//! [`Duration`]: trait.Duration.html +//! [`Clock`]: trait.Clock.html //! //! ## Definitions -//! **Clock**: Any entity that periodically counts (ie a hardware timer/counter peripheral). Generally, -//! this needs to be monotonic. A wrapping clock is considered monotonic in this context as long as -//! it fulfills the other requirements. +//! **Clock**: Any entity that periodically counts (ie a hardware timer/counter peripheral). +//! Generally, this needs to be monotonic. A wrapping clock is considered monotonic in this context +//! as long as it fulfills the other requirements. //! //! **Wrapping Clock**: A clock that when at its maximum value, the next count is the minimum //! value. From 909d5a50b259bd9f9aaeb24de9286e660b6139df Mon Sep 17 00:00:00 2001 From: Peter Taylor Date: Wed, 1 Jul 2020 22:22:18 -0600 Subject: [PATCH 05/23] Create CODE_OF_CONDUCT.md --- CODE_OF_CONDUCT.md | 76 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 76 insertions(+) create mode 100644 CODE_OF_CONDUCT.md diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md new file mode 100644 index 0000000..3cad194 --- /dev/null +++ b/CODE_OF_CONDUCT.md @@ -0,0 +1,76 @@ +# Contributor Covenant Code of Conduct + +## Our Pledge + +In the interest of fostering an open and welcoming environment, we as +contributors and maintainers pledge to making participation in our project and +our community a harassment-free experience for everyone, regardless of age, body +size, disability, ethnicity, sex characteristics, gender identity and expression, +level of experience, education, socio-economic status, nationality, personal +appearance, race, religion, or sexual identity and orientation. + +## Our Standards + +Examples of behavior that contributes to creating a positive environment +include: + +* Using welcoming and inclusive language +* Being respectful of differing viewpoints and experiences +* Gracefully accepting constructive criticism +* Focusing on what is best for the community +* Showing empathy towards other community members + +Examples of unacceptable behavior by participants include: + +* The use of sexualized language or imagery and unwelcome sexual attention or + advances +* Trolling, insulting/derogatory comments, and personal or political attacks +* Public or private harassment +* Publishing others' private information, such as a physical or electronic + address, without explicit permission +* Other conduct which could reasonably be considered inappropriate in a + professional setting + +## Our Responsibilities + +Project maintainers are responsible for clarifying the standards of acceptable +behavior and are expected to take appropriate and fair corrective action in +response to any instances of unacceptable behavior. + +Project maintainers have the right and responsibility to remove, edit, or +reject comments, commits, code, wiki edits, issues, and other contributions +that are not aligned to this Code of Conduct, or to ban temporarily or +permanently any contributor for other behaviors that they deem inappropriate, +threatening, offensive, or harmful. + +## Scope + +This Code of Conduct applies both within project spaces and in public spaces +when an individual is representing the project or its community. Examples of +representing a project or community include using an official project e-mail +address, posting via an official social media account, or acting as an appointed +representative at an online or offline event. Representation of a project may be +further defined and clarified by project maintainers. + +## Enforcement + +Instances of abusive, harassing, or otherwise unacceptable behavior may be +reported by contacting the project team at ptaylor@fluentech.info. All +complaints will be reviewed and investigated and will result in a response that +is deemed necessary and appropriate to the circumstances. The project team is +obligated to maintain confidentiality with regard to the reporter of an incident. +Further details of specific enforcement policies may be posted separately. + +Project maintainers who do not follow or enforce the Code of Conduct in good +faith may face temporary or permanent repercussions as determined by other +members of the project's leadership. + +## Attribution + +This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, +available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html + +[homepage]: https://www.contributor-covenant.org + +For answers to common questions about this code of conduct, see +https://www.contributor-covenant.org/faq From f3e05c978c0f2d2f223a07198f9330e03b1d38a7 Mon Sep 17 00:00:00 2001 From: Peter Taylor Date: Thu, 25 Jun 2020 20:38:24 -0600 Subject: [PATCH 06/23] Change all inner types to unsigned --- CHANGELOG.md | 5 + examples/nrf52_dk/main.rs | 10 +- src/duration.rs | 308 ++++++++++++++++++-------------------- src/frequency.rs | 10 +- src/instant.rs | 202 +++++++++++++++++++------ src/lib.rs | 63 ++++---- src/period.rs | 28 ++-- src/time_int.rs | 92 ++++++------ 8 files changed, 412 insertions(+), 306 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9f06bfb..b2e4dc4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,10 +4,15 @@ ### Added +- `Instant::duration_until()` with order checking +- Order checking to `Instant::duration_since()` +- Bounds checking on `Instant` impls of Add/Sub - Changelog back to v0.5.0 release ### Changed +- All time-type inner types from signed to unsigned +- `Instant::duration_since()` return type to `Result` - Refactor `examples/nrf52_dk` [unreleased]: https://github.com/FluenTech/embedded-time/compare/v0.5.2...HEAD diff --git a/examples/nrf52_dk/main.rs b/examples/nrf52_dk/main.rs index c5465a8..a47af02 100644 --- a/examples/nrf52_dk/main.rs +++ b/examples/nrf52_dk/main.rs @@ -5,7 +5,7 @@ extern crate panic_rtt; use cortex_m::mutex::CriticalSectionMutex as Mutex; use cortex_m_rt::entry; -use embedded_time::{self as time, Clock, Instant, Period, TimeInt}; +use embedded_time::{self as time, traits::*, Clock, Instant, Period}; use mutex_trait::Mutex as _; pub mod nrf52 { @@ -40,8 +40,8 @@ pub mod nrf52 { pub struct SysClock; impl time::Clock for SysClock { - type Rep = i64; - const PERIOD: Period = Period::new(1, 16_000_000); + type Rep = u64; + const PERIOD: Period = ::new(1, 16_000_000); fn now() -> Instant { let ticks = (&CLOCK64).lock(|clock| match clock { @@ -156,12 +156,12 @@ where led2.set_high()?; led3.set_high()?; led4.set_low()?; - SysClock::delay(250.milliseconds()); + SysClock::delay(250_u32.milliseconds()); led1.set_high()?; led2.set_low()?; led3.set_low()?; led4.set_high()?; - SysClock::delay(250.milliseconds()); + SysClock::delay(250_u32.milliseconds()); } } diff --git a/src/duration.rs b/src/duration.rs index 028c167..c1cb02b 100644 --- a/src/duration.rs +++ b/src/duration.rs @@ -1,10 +1,10 @@ //! Duration types/units creation and conversion. -use crate::{time_int::TimeInt, Period}; +use crate::{period::Period, time_int::TimeInt}; use core::{convert::TryFrom, fmt, mem::size_of, prelude::v1::*}; use num::Bounded; -/// A duration of time with signed, generic storage +/// An unsigned duration of time /// /// Each implementation defines a constant [`Period`] which is a fraction/ratio representing the /// period of the count's LSbit @@ -21,7 +21,7 @@ use num::Bounded; /// type Rep = T; // set the storage type /// /// // set LSbit period to 1 millisecond -/// const PERIOD: Period = Period::new(1, 1_000); +/// const PERIOD: Period = ::new(1, 1_000); /// /// fn new(value: Self::Rep) -> Self { /// Self(value) @@ -42,96 +42,84 @@ use num::Bounded; /// /// # Constructing a duration /// ```rust -/// # use embedded_time::{prelude::*, units::*}; +/// # use embedded_time::{traits::*, units::*}; /// # -/// assert_eq!(Milliseconds::::new(23), Milliseconds(23_i32)); -/// assert_eq!(Milliseconds(23), 23.milliseconds()); +/// assert_eq!(Milliseconds::::new(23), Milliseconds(23_u32)); +/// assert_eq!(Milliseconds(23_u32), 23_u32.milliseconds()); /// ``` /// /// # Get the integer count /// ```rust -/// # use embedded_time::{prelude::*, units::*}; +/// # use embedded_time::{traits::*, units::*}; /// # -/// assert_eq!(Milliseconds(23).count(), 23); +/// assert_eq!(Milliseconds(23_u32).count(), 23_u32); /// ``` /// /// # Formatting /// Just forwards the underlying integer to [`core::fmt::Display::fmt()`] /// ```rust -/// # use embedded_time::{prelude::*, units::*}; +/// # use embedded_time::{traits::*, units::*}; /// # -/// assert_eq!(format!("{}", Seconds(123)), "123"); +/// assert_eq!(format!("{}", Seconds(123_u32)), "123"); /// ``` /// /// # Getting H:M:S.MS... Components /// ```rust -/// # use embedded_time::{prelude::*, units::*}; +/// # use embedded_time::{traits::*, units::*}; /// # -/// let duration = 38_238_479.microseconds(); -/// let hours = Hours::::try_convert_from(duration).unwrap(); -/// let minutes = Minutes::::try_convert_from(duration).unwrap() % Hours(1); -/// let seconds = Seconds::::try_convert_from(duration).unwrap() % Minutes(1); -/// let milliseconds = Milliseconds::::try_convert_from(duration).unwrap() % Seconds(1); +/// let duration = 38_238_479_u32.microseconds(); +/// let hours = Hours::::try_convert_from(duration).unwrap(); +/// let minutes = Minutes::::try_convert_from(duration).unwrap() % Hours(1_u32); +/// let seconds = Seconds::::try_convert_from(duration).unwrap() % Minutes(1_u32); +/// let milliseconds = Milliseconds::::try_convert_from(duration).unwrap() % Seconds(1_u32); /// // ... /// ``` /// /// # Converting to [`core::time::Duration`] /// ## Examples /// ```rust -/// # use embedded_time::prelude::*; +/// # use embedded_time::traits::*; /// # use core::convert::TryFrom; /// # -/// let core_duration = core::time::Duration::try_from(2_569.milliseconds()).unwrap(); +/// let core_duration = core::time::Duration::try_from(2_569_u32.milliseconds()).unwrap(); /// assert_eq!(2, core_duration.as_secs()); /// assert_eq!(569_000_000, core_duration.subsec_nanos()); /// ``` /// ```rust -/// # use embedded_time::prelude::*; +/// # use embedded_time::traits::*; /// # use core::convert::TryInto; /// # -/// let core_duration: core::time::Duration = 2_569.milliseconds().try_into().unwrap(); +/// let core_duration: core::time::Duration = 2_569_u32.milliseconds().try_into().unwrap(); /// assert_eq!(2, core_duration.as_secs()); /// assert_eq!(569_000_000, core_duration.subsec_nanos()); /// ``` /// -/// ## Errors -/// Attempting to convert from a _negative_ duration will fail -/// ```rust -/// # use embedded_time::prelude::*; -/// # use core::convert::{TryFrom, TryInto}; -/// # -/// assert!(core::time::Duration::try_from((-2_569).milliseconds()).is_err()); -/// -/// let core_duration: Result = (-2_569).milliseconds().try_into(); -/// assert!(core_duration.is_err()); -/// ``` -/// /// # Converting from [`core::time::Duration`] /// ## Examples /// ```rust -/// # use embedded_time::{prelude::*, units::*}; +/// # use embedded_time::{traits::*, units::*}; /// # use core::convert::TryFrom; /// # /// let core_duration = core::time::Duration::new(5, 730023852); -/// assert_eq!(Milliseconds::::try_from(core_duration), Ok(5_730.milliseconds())); +/// assert_eq!(Milliseconds::::try_from(core_duration), Ok(5_730.milliseconds())); /// ``` /// ```rust -/// # use embedded_time::{prelude::*, units::*}; +/// # use embedded_time::{traits::*, units::*}; /// # use core::convert::TryInto; /// # -/// let duration: Result, _> = core::time::Duration::new(5, 730023852).try_into(); +/// let duration: Result, _> = core::time::Duration::new(5, 730023852).try_into(); /// assert_eq!(duration, Ok(5_730.milliseconds())); /// ``` /// /// ## Errors /// The duration doesn't fit in the type specified /// ```rust -/// # use embedded_time::{prelude::*, units::*}; +/// # use embedded_time::{traits::*, units::*}; /// # use core::convert::{TryFrom, TryInto}; /// # -/// assert!(Milliseconds::::try_from(core::time::Duration::from_millis((i32::MAX as u64) + 1)).is_err()); +/// assert!(Milliseconds::::try_from(core::time::Duration::from_millis((u32::MAX as u64) + 1)).is_err()); /// -/// let duration: Result, _> = core::time::Duration::from_millis((i32::MAX as u64) + 1).try_into(); +/// let duration: Result, _> = core::time::Duration::from_millis((u32::MAX as u64) + 1).try_into(); /// assert!(duration.is_err()); /// ``` /// @@ -140,71 +128,71 @@ use num::Bounded; /// ## Panics /// Panics if the rhs duration cannot be converted into the lhs duration type /// -/// In this example, the maximum `i32` value of seconds is stored as `i32` and -/// converting that value to milliseconds (with `i32` storage type) causes an overflow. +/// In this example, the maximum `u32` value of seconds is stored as `u32` and +/// converting that value to milliseconds (with `u32` storage type) causes an overflow. /// ```rust,should_panic -/// # use embedded_time::{prelude::*, units::*}; +/// # use embedded_time::{traits::*, units::*}; /// # -/// let _ = Milliseconds(24) + Seconds(i32::MAX); +/// let _ = Milliseconds(24_u32) + Seconds(u32::MAX); /// ``` /// -/// This example works just fine as the seconds value is first cast to `i64`, then +/// This example works just fine as the seconds value is first cast to `u64`, then /// converted to milliseconds. /// ```rust -/// # use embedded_time::{prelude::*, units::*}; +/// # use embedded_time::{traits::*, units::*}; /// # -/// let _ = Milliseconds(24_i64) + Seconds(i32::MAX); +/// let _ = Milliseconds(24_u64) + Seconds(u32::MAX); /// ``` /// -/// Here, there is no units conversion to worry about, but `i32::MAX + 1` cannot be -/// cast to an `i32`. +/// Here, there is no units conversion to worry about, but `u32::MAX + 1` cannot be +/// cast to an `u32`. /// ```rust,should_panic -/// # use embedded_time::{prelude::*, units::*}; +/// # use embedded_time::{traits::*, units::*}; /// # -/// let _ = Seconds(i32::MAX) - Seconds(i32::MAX as i64 + 1); +/// let _ = Seconds(u32::MAX) - Seconds(u32::MAX as u64 + 1); /// ``` /// /// ## Examples /// ```rust -/// # use embedded_time::{prelude::*, units::*}; +/// # use embedded_time::{traits::*, units::*}; /// # -/// assert_eq!((Milliseconds(3_234) - Seconds(2)), Milliseconds(1_234)); -/// assert_eq!((Milliseconds(3_234_i64) - Seconds(2_i32)), Milliseconds(1_234_i64)); -/// assert_eq!((Seconds(i32::MAX) - Milliseconds((i32::MAX as i64) + 1)), -/// Seconds(2_145_336_164_i32)); +/// assert_eq!((Milliseconds(3_234_u32) - Seconds(2_u32)), Milliseconds(1_234_u32)); +/// assert_eq!((Milliseconds(3_234_u64) - Seconds(2_u32)), Milliseconds(1_234_u64)); +/// assert_eq!((Seconds(u32::MAX) - Milliseconds((u32::MAX as u64) + 1)), +/// Seconds(4_290_672_328_u32)); /// ``` /// /// # Equality /// ```rust -/// # use embedded_time::{prelude::*, units::*}; +/// # use embedded_time::{traits::*, units::*}; /// # -/// assert_eq!(Seconds(123), Seconds(123)); -/// assert_eq!(Seconds(123), Milliseconds(123_000)); -/// -/// assert_ne!(Seconds(123), Milliseconds(123_001)); -/// assert_ne!(Milliseconds(123_001), Seconds(123)); -/// assert_ne!(Milliseconds(123_001_i64), Seconds(123_i64)); -/// assert_ne!(Seconds(123_i64), Milliseconds(123_001_i64)); -/// assert_ne!(Seconds(123_i64), Milliseconds(123_001_i32)); +/// assert_eq!(Seconds(123_u32), Seconds(123_u32)); +/// assert_eq!(Seconds(123_u32), Milliseconds(123_000_u32)); +/// +/// assert_ne!(Seconds(123_u32), Milliseconds(123_001_u32)); +/// assert_ne!(Milliseconds(123_001_u32), Seconds(123_u32)); +/// assert_ne!(Milliseconds(123_001_u64), Seconds(123_u64)); +/// assert_ne!(Seconds(123_u64), Milliseconds(123_001_u64)); +/// assert_ne!(Seconds(123_u64), Milliseconds(123_001_u32)); /// ``` /// /// # Comparisons /// ```rust -/// # use embedded_time::{prelude::*, units::*}; +/// # use embedded_time::{traits::*, units::*}; /// # -/// assert!(Seconds(2) < Seconds(3)); -/// assert!(Seconds(2) < Milliseconds(2_001)); -/// assert!(Seconds(2) == Milliseconds(2_000)); -/// assert!(Seconds(2) > Milliseconds(1_999)); -/// assert!(Seconds(2_i32) < Milliseconds(2_001_i64)); -/// assert!(Seconds(2_i64) < Milliseconds(2_001_i32)); +/// assert!(Seconds(2_u32) < Seconds(3_u32)); +/// assert!(Seconds(2_u32) < Milliseconds(2_001_u32)); +/// assert!(Seconds(2_u32) == Milliseconds(2_000_u32)); +/// assert!(Seconds(2_u32) > Milliseconds(1_999_u32)); +/// assert!(Seconds(2_u32) < Milliseconds(2_001_u64)); +/// assert!(Seconds(2_u64) < Milliseconds(2_001_u32)); /// ``` /// /// # Remainder /// ```rust -/// # use embedded_time::{prelude::*, units::*}; +/// # use embedded_time::{traits::*, units::*}; /// # -/// assert_eq!(Minutes(62) % Hours(1), Minutes(2)); +/// assert_eq!(Minutes(62_u32) % Hours(1_u32), Minutes(2_u32)); /// ``` pub trait Duration: Sized + Copy + fmt::Display { /// The inner type of the `Duration` representing the count of the implementation unit @@ -216,9 +204,9 @@ pub trait Duration: Sized + Copy + fmt::Display { /// Not generally useful or needed as the duration can be constructed like this: /// ```no_run - /// # use embedded_time::{prelude::*, units::*}; - /// Seconds(123); - /// 123.seconds(); + /// # use embedded_time::{traits::*, units::*}; + /// Seconds(123_u32); + /// 123_u32.seconds(); /// ``` /// It only exists to allow Duration methods with default definitions to create a /// new duration @@ -228,8 +216,8 @@ pub trait Duration: Sized + Copy + fmt::Display { /// /// # Examples /// ```rust - /// # use embedded_time::{prelude::*, units::*}; - /// assert_eq!(Seconds(123).count(), 123); + /// # use embedded_time::{traits::*, units::*}; + /// assert_eq!(Seconds(123_u32).count(), 123_u32); /// ``` fn count(self) -> Self::Rep; @@ -237,41 +225,40 @@ pub trait Duration: Sized + Copy + fmt::Display { /// /// # Examples /// ```rust - /// # use embedded_time::{prelude::*, units::*, Period}; - /// assert_eq!(Microseconds::::from_ticks(5_i64, Period::new(1, 1_000)), - /// Some(Microseconds(5_000_i32))); + /// # use embedded_time::{traits::*, units::*, Period}; + /// assert_eq!(Microseconds::::from_ticks(5_u64, ::new(1, 1_000)), + /// Some(Microseconds(5_000_u32))); /// /// // the conversion arithmetic will not cause overflow - /// assert_eq!(Milliseconds::::from_ticks((i32::MAX as i64) + 1, Period::new(1, 1_000_000)), - /// Some(Milliseconds((((i32::MAX as i64) + 1) / 1_000) as i32))); + /// assert_eq!(Milliseconds::::from_ticks((u32::MAX as u64) + 1, ::new(1, 1_000_000)), + /// Some(Milliseconds((((u32::MAX as u64) + 1) / 1_000) as u32))); /// ``` /// /// # Errors /// the conversion of periods causes an overflow: /// ```rust - /// # use embedded_time::{prelude::*, units::*, Period}; - /// assert_eq!(Milliseconds::::from_ticks(i32::MAX, Period::new(1, 1)), + /// # use embedded_time::{traits::*, units::*, Period}; + /// assert_eq!(Milliseconds::::from_ticks(u32::MAX, ::new(1, 1)), /// None); /// ``` /// /// the Self integer cast to that of the provided type fails /// ```rust - /// # use embedded_time::{prelude::*, units::*, Period}; - /// assert_eq!(Seconds::::from_ticks(i32::MAX as i64 + 1, Period::new(1, 1)), + /// # use embedded_time::{traits::*, units::*, Period}; + /// assert_eq!(Seconds::::from_ticks(u32::MAX as u64 + 1, ::new(1, 1)), /// None); /// ``` /// /// # Returns /// [`None`] if the result of the conversion does not fit in the requested integer size - fn from_ticks(ticks: Rep, period: Period) -> Option + fn from_ticks(ticks: Rep, period: Period) -> Option where - Self::Rep: TimeInt + TryFrom, - Rep: TimeInt, + Self::Rep: TryFrom, { if size_of::() > size_of::() { let converted_ticks = Self::Rep::try_from(ticks).ok()?; - if period > Period::new(1, 1) { + if period > ::new(1, 1) { Some(Self::new(TimeInt::checked_div_period( &TimeInt::checked_mul_period(&converted_ticks, &period)?, &Self::PERIOD, @@ -283,12 +270,12 @@ pub trait Duration: Sized + Copy + fmt::Display { )?)) } } else { - let ticks = if period > Period::new(1, 1) { + let ticks = if period > ::new(1, 1) { TimeInt::checked_div_period( &TimeInt::checked_mul_period(&ticks, &period)?, &Self::PERIOD, )? - } else if Self::PERIOD > Period::new(1, 1) { + } else if Self::PERIOD > ::new(1, 1) { TimeInt::checked_mul_period( &TimeInt::checked_div_period(&ticks, &Self::PERIOD)?, &period, @@ -309,28 +296,28 @@ pub trait Duration: Sized + Copy + fmt::Display { /// /// # Examples /// ```rust - /// # use embedded_time::{prelude::*, units::*, Period}; - /// assert_eq!(Microseconds(5_000_i32).into_ticks::(Period::new(1, 1_000)), Some(5_i32)); + /// # use embedded_time::{traits::*, units::*, Period}; + /// assert_eq!(Microseconds(5_000_u32).into_ticks::(Period::new(1, 1_000)), Some(5_u32)); /// /// // the _into_ period can be any value - /// assert_eq!(Microseconds(5_000_i32).into_ticks::(Period::new(1, 200)), Some(1_i32)); + /// assert_eq!(Microseconds(5_000_u32).into_ticks::(Period::new(1, 200)), Some(1_u32)); /// /// // as long as the result fits in the provided integer, it will succeed - /// assert_eq!(Microseconds::(i32::MAX).into_ticks::(Period::new(1, 2_000_000)), - /// Some((i32::MAX as i64) * 2)); + /// assert_eq!(Microseconds::(u32::MAX).into_ticks::(Period::new(1, 2_000_000)), + /// Some((u32::MAX as u64) * 2)); /// ``` /// /// # Errors /// the conversion of periods causes an overflow: /// ```rust - /// # use embedded_time::{prelude::*, units::*, Period}; - /// assert_eq!(Seconds(i32::MAX).into_ticks::(Period::new(1, 1_000)), None); + /// # use embedded_time::{traits::*, units::*, Period}; + /// assert_eq!(Seconds(u32::MAX).into_ticks::(Period::new(1, 1_000)), None); /// ``` /// /// the Self integer cast to that of the provided type fails /// ```rust - /// # use embedded_time::{prelude::*, units::*, Period}; - /// assert_eq!(Seconds(i32::MAX as i64 + 1).into_ticks::(Period::new(1, 1)), None); + /// # use embedded_time::{traits::*, units::*, Period}; + /// assert_eq!(Seconds(u32::MAX as u64 + 1).into_ticks::(Period::new(1, 1)), None); /// ``` /// /// # Returns @@ -343,7 +330,7 @@ pub trait Duration: Sized + Copy + fmt::Display { if size_of::() > size_of::() { let ticks = Rep::try_from(self.count()).ok()?; - if period > Period::new(1, 1) { + if period > ::new(1, 1) { Some(TimeInt::checked_div_period( &TimeInt::checked_mul_period(&ticks, &Self::PERIOD)?, &period, @@ -355,7 +342,7 @@ pub trait Duration: Sized + Copy + fmt::Display { )?) } } else { - let ticks = if Self::PERIOD > Period::new(1, 1) { + let ticks = if Self::PERIOD > ::new(1, 1) { TimeInt::checked_div_period( &TimeInt::checked_mul_period(&self.count(), &Self::PERIOD)?, &period, @@ -372,8 +359,8 @@ pub trait Duration: Sized + Copy + fmt::Display { } /// ```rust - /// # use embedded_time::{prelude::*, units::*}; - /// assert_eq!(Seconds::::min_value(), i32::MIN); + /// # use embedded_time::{traits::*, units::*}; + /// assert_eq!(Seconds::::min_value(), u32::MIN); /// ``` #[must_use] fn min_value() -> Self::Rep { @@ -381,8 +368,8 @@ pub trait Duration: Sized + Copy + fmt::Display { } /// ```rust - /// # use embedded_time::{prelude::*, units::*}; - /// assert_eq!(Seconds::::max_value(), i32::MAX); + /// # use embedded_time::{traits::*, units::*}; + /// assert_eq!(Seconds::::max_value(), u32::MAX); /// ``` #[must_use] fn max_value() -> Self::Rep { @@ -406,12 +393,9 @@ pub trait TryConvertInto { fn try_convert_into(self) -> Option; } -impl TryConvertFrom for Dest +impl TryConvertFrom for Dest where - Dest: Duration, - Dest::Rep: TimeInt + TryFrom, - Source: Duration, - Source::Rep: TimeInt, + Dest::Rep: TryFrom, { /// Attempt to convert from one duration type to another /// @@ -419,33 +403,33 @@ where /// /// # Examples /// ```rust - /// # use embedded_time::{prelude::*, units::*}; - /// assert_eq!(Seconds::::try_convert_from(Milliseconds(23_000_i64)), Some(Seconds(23_i32))); - /// assert_eq!(Seconds::::try_convert_from(Milliseconds(23_000_i32)), Some(Seconds(23_i64))); - /// assert_eq!(Seconds::::try_convert_from(Milliseconds(230_i32)), Some(Seconds(0))); + /// # use embedded_time::{traits::*, units::*}; + /// assert_eq!(Seconds::::try_convert_from(Milliseconds(23_000_u64)), Some(Seconds(23_u32))); + /// assert_eq!(Seconds::::try_convert_from(Milliseconds(23_000_u32)), Some(Seconds(23_u64))); + /// assert_eq!(Seconds::::try_convert_from(Milliseconds(230_u32)), Some(Seconds(0))); /// ``` /// /// # Errors /// the conversion of periods causes an overflow: /// ```rust - /// # use embedded_time::{prelude::*, units::*}; - /// assert_eq!(Milliseconds::::try_convert_from(Seconds(i32::MAX)), None); + /// # use embedded_time::{traits::*, units::*}; + /// assert_eq!(Milliseconds::::try_convert_from(Seconds(u32::MAX)), None); /// ``` /// /// the Self integer cast to that of the provided type fails /// ```rust - /// # use embedded_time::{prelude::*, units::*}; - /// assert_eq!(Seconds::::try_convert_from(Seconds(i32::MAX as i64 + 1)), None); + /// # use embedded_time::{traits::*, units::*}; + /// assert_eq!(Seconds::::try_convert_from(Seconds(u32::MAX as u64 + 1)), None); /// ``` /// /// However, these work because the sequence of cast/conversion adapts /// ```rust - /// # use embedded_time::{prelude::*, units::*}; + /// # use embedded_time::{traits::*, units::*}; /// // period conversion applied first - /// assert_eq!(Hours::::try_convert_from(Microseconds(3_600_000_000_i64)), Some(Hours(1_i32))); + /// assert_eq!(Hours::::try_convert_from(Microseconds(3_600_000_000_u64)), Some(Hours(1_u32))); /// /// // cast applied first - /// assert_eq!(Microseconds::::try_convert_from(Hours(1_i32)), Some(Microseconds(3_600_000_000_i64))); + /// assert_eq!(Microseconds::::try_convert_from(Hours(1_u32)), Some(Microseconds(3_600_000_000_u64))); /// ``` /// /// # Returns @@ -459,33 +443,33 @@ where /// /// # Examples /// ```rust -/// # use embedded_time::{prelude::*, units::*}; -/// assert_eq!(Seconds(23_i64).try_convert_into(), Some(Seconds(23_i32))); -/// assert_eq!(Some(Seconds(23_i64)), (Seconds(23_i32).try_convert_into())); -/// assert_eq!(Milliseconds(23_000_i64).try_convert_into(), Some(Seconds(23_i32))); +/// # use embedded_time::{traits::*, units::*}; +/// assert_eq!(Seconds(23_u64).try_convert_into(), Some(Seconds(23_u32))); +/// assert_eq!(Some(Seconds(23_u64)), (Seconds(23_u32).try_convert_into())); +/// assert_eq!(Milliseconds(23_000_u64).try_convert_into(), Some(Seconds(23_u32))); /// ``` /// /// # Errors /// the conversion of periods causes an overflow: /// ```rust -/// # use embedded_time::{prelude::*, units::*}; -/// assert_eq!(Seconds(i32::MAX).try_convert_into(), None::>); +/// # use embedded_time::{traits::*, units::*}; +/// assert_eq!(Seconds(u32::MAX).try_convert_into(), None::>); /// ``` /// /// the Self integer cast to that of the destination type fails /// ```rust -/// # use embedded_time::{prelude::*, units::*}; -/// assert_eq!(Seconds(i32::MAX as i64 + 1).try_convert_into(), None::>); +/// # use embedded_time::{traits::*, units::*}; +/// assert_eq!(Seconds(u32::MAX as u64 + 1).try_convert_into(), None::>); /// ``` /// /// However, these work because the sequence of cast/conversion adapts /// ```rust -/// # use embedded_time::{prelude::*, units::*}; +/// # use embedded_time::{traits::*, units::*}; /// // period conversion applied first -/// assert_eq!(Microseconds(3_600_000_000_i64).try_convert_into(), Some(Hours(1_i32))); +/// assert_eq!(Microseconds(3_600_000_000_u64).try_convert_into(), Some(Hours(1_u32))); /// /// // cast applied first -/// assert_eq!(Hours(1_i32).try_convert_into(), Some(Microseconds(3_600_000_000_i64))); +/// assert_eq!(Hours(1_u32).try_convert_into(), Some(Microseconds(3_600_000_000_u64))); /// ``` /// /// # Returns @@ -517,11 +501,11 @@ pub mod units { ( $name:ident, ($numer:expr, $denom:expr) ) => { /// A duration unit type #[derive(Copy, Clone, Debug, Eq, Ord)] - pub struct $name(pub T); + pub struct $name(pub T); impl Duration for $name { type Rep = Rep; - const PERIOD: Period = Period::new($numer, $denom); + const PERIOD: Period = ::new($numer, $denom); fn new(value: Self::Rep) -> Self { Self(value) @@ -636,8 +620,8 @@ pub mod units { /// Convert an embedded_time::[`Duration`] into a [`core::time::Duration`] fn try_from(duration: $name) -> Result { - let seconds = Seconds::::try_convert_from(duration).ok_or(())?; - Ok(Self::from_secs(seconds.count().try_into().or(Err(()))?)) + let seconds = Seconds::::try_convert_from(duration).ok_or(())?; + Ok(Self::from_secs(seconds.count())) } } @@ -646,7 +630,7 @@ pub mod units { /// Convert a [`core::time::Duration`] into an embedded_time::[`Duration`] fn try_from(core_duration: core::time::Duration) -> Result { - let seconds = Seconds::(core_duration.as_secs().try_into().or(Err(()))?); + let seconds = Seconds(core_duration.as_secs()); Ok(Self::try_convert_from(seconds).ok_or(())?) } } @@ -659,7 +643,7 @@ pub mod units { /// Convert an embedded_time::[`Duration`] into a [`core::time::Duration`] fn try_from(duration: $name) -> Result { - Ok(Self::$from_core_dur(duration.count().try_into()?)) + Ok(Self::$from_core_dur(duration.count().into())) } } @@ -689,24 +673,24 @@ mod tests { #[test] fn check_for_overflows() { - let mut time = 1_i64; + let mut time = 1_u64; time *= 60; - assert_eq!(Hours(1), Minutes(time)); + assert_eq!(Hours(1_u32), Minutes(time)); time *= 60; - assert_eq!(Hours(1), Seconds(time)); + assert_eq!(Hours(1_u32), Seconds(time)); time *= 1000; - assert_eq!(Hours(1), Milliseconds(time)); + assert_eq!(Hours(1_u32), Milliseconds(time)); time *= 1000; - assert_eq!(Hours(1), Microseconds(time)); + assert_eq!(Hours(1_u32), Microseconds(time)); time *= 1000; - assert_eq!(Hours(1), Nanoseconds(time)); + assert_eq!(Hours(1_u32), Nanoseconds(time)); } #[test] fn remainder() { - assert_eq!(Minutes(62) % Hours(1), Minutes(2)); - assert_eq!(Minutes(62) % Milliseconds(1), Minutes(0)); - assert_eq!(Minutes(62) % Minutes(60), Minutes(2)); + assert_eq!(Minutes(62_u32) % Hours(1_u32), Minutes(2_u32)); + assert_eq!(Minutes(62_u32) % Milliseconds(1_u32), Minutes(0_u32)); + assert_eq!(Minutes(62_u32) % Minutes(60_u32), Minutes(2_u32)); } #[test] @@ -714,42 +698,42 @@ mod tests { let core_duration = core::time::Duration::from_nanos(5_025_678_901_234); assert_eq!( core_duration.try_into(), - Ok(Nanoseconds::(5_025_678_901_234)) + Ok(Nanoseconds::(5_025_678_901_234)) ); assert_eq!( core_duration.try_into(), - Ok(Microseconds::(5_025_678_901)) + Ok(Microseconds::(5_025_678_901)) ); - assert_eq!(core_duration.try_into(), Ok(Milliseconds::(5_025_678))); - assert_eq!(core_duration.try_into(), Ok(Seconds::(5_025))); - assert_eq!(core_duration.try_into(), Ok(Minutes::(83))); - assert_eq!(core_duration.try_into(), Ok(Hours::(1))); + assert_eq!(core_duration.try_into(), Ok(Milliseconds::(5_025_678))); + assert_eq!(core_duration.try_into(), Ok(Seconds::(5_025))); + assert_eq!(core_duration.try_into(), Ok(Minutes::(83))); + assert_eq!(core_duration.try_into(), Ok(Hours::(1))); } #[test] fn convert_to_core_duration() { assert_eq!( - Nanoseconds(123).try_into(), + Nanoseconds(123_u32).try_into(), Ok(core::time::Duration::from_nanos(123)) ); assert_eq!( - Microseconds(123).try_into(), + Microseconds(123_u32).try_into(), Ok(core::time::Duration::from_micros(123)) ); assert_eq!( - Milliseconds(123).try_into(), + Milliseconds(123_u32).try_into(), Ok(core::time::Duration::from_millis(123)) ); assert_eq!( - Seconds(123).try_into(), + Seconds(123_u32).try_into(), Ok(core::time::Duration::from_secs(123)) ); assert_eq!( - Minutes(123).try_into(), + Minutes(123_u32).try_into(), Ok(core::time::Duration::from_secs(123 * 60)) ); assert_eq!( - Hours(123).try_into(), + Hours(123_u32).try_into(), Ok(core::time::Duration::from_secs(123 * 3600)) ); } diff --git a/src/frequency.rs b/src/frequency.rs index 6ea8223..4048870 100644 --- a/src/frequency.rs +++ b/src/frequency.rs @@ -6,12 +6,12 @@ pub(crate) mod units { /// A frequency unit type #[derive(Ord, PartialOrd, Eq, PartialEq, Debug)] - pub struct Hertz(pub T); + pub struct Hertz(pub T); impl Hertz { /// ```rust /// # use embedded_time::{Period, units::*}; - /// assert_eq!(Hertz(1_000).into_period(), Period::new(1, 1_000)); + /// assert_eq!(Hertz(1_000_u32).into_period(), ::new(1, 1_000)); /// ``` pub fn into_period(self) -> Period { Period::from_frequency(self) @@ -19,7 +19,7 @@ pub(crate) mod units { /// ```rust /// # use embedded_time::{Period, units::*}; - /// assert_eq!(Hertz::from_period(Period::new(1, 1_000)), Hertz(1_000)); + /// assert_eq!(Hertz::from_period(Period::new(1, 1_000)), Hertz(1_000_u32)); /// ``` pub fn from_period(period: Period) -> Self { period.to_frequency() @@ -34,7 +34,7 @@ pub(crate) mod units { /// ```rust /// # use embedded_time::units::*; - /// assert_eq!(Hertz(100) * 3, Hertz(300)); + /// assert_eq!(Hertz(100_u32) * 3_u32, Hertz(300_u32)); /// ``` fn mul(self, rhs: Rhs) -> Self::Output { Self(self.0 * >::from(rhs)) @@ -49,7 +49,7 @@ pub(crate) mod units { /// ```rust /// # use embedded_time::units::*; - /// assert_eq!(Hertz(300) / 3, Hertz(100)); + /// assert_eq!(Hertz(300_u32) / 3_u32, Hertz(100_u32)); /// ``` fn div(self, rhs: Rhs) -> Self::Output { Self(self.0 / >::from(rhs)) diff --git a/src/instant.rs b/src/instant.rs index 5055863..b684711 100644 --- a/src/instant.rs +++ b/src/instant.rs @@ -22,50 +22,88 @@ impl Instant { Self { ticks } } - /// Calculates the difference between two `Instance`s resulting in a [`Duration`] + /// Returns the [`Duration`] since the given `Instant` + /// + /// # Errors + /// - `()`: RHS is a future `Instant` + /// - `()`: problem coverting to desired [`Duration`] /// /// # Examples /// ```rust /// # use embedded_time::{Period, units::*, Instant}; /// # #[derive(Debug, Eq, PartialEq, Ord, PartialOrd)] + /// # /// struct Clock; /// impl embedded_time::Clock for Clock { - /// type Rep = i32; - /// const PERIOD: Period = Period::new(1, 1_000); + /// type Rep = u32; + /// const PERIOD: Period = ::new(1, 1_000); /// // ... /// # fn now() -> Instant {unimplemented!()} /// } /// - /// let diff: Option> = Instant::::new(5).duration_since(&Instant::::new(3)); - /// assert_eq!(diff, Some(Milliseconds(2_i32))); + /// assert_eq!(Instant::::new(5).duration_since::>(&Instant::::new(3)), + /// Ok(Microseconds(2_000_u64))); /// - /// let diff: Option> = Instant::::new(5).duration_since(&Instant::::new(3)); - /// assert_eq!(diff, Some(Microseconds(2_000_i64))); + /// assert_eq!(Instant::::new(3).duration_since::>(&Instant::::new(5)), + /// Err(())); + /// ``` + pub fn duration_since(&self, other: &Self) -> Result + where + Dur::Rep: TryFrom, + { + if self < other { + Err(()) + } else { + Dur::from_ticks(self.ticks.wrapping_sub(&other.ticks), Clock::PERIOD).ok_or(()) + } + } + + /// Returns the [`Duration`] until the given `Instant` + /// + /// # Errors + /// - `()`: RHS is a past `Instant` + /// - `()`: problem coverting to desired [`Duration`] + /// + /// # Examples + /// ```rust + /// # use embedded_time::{Period, units::*, Instant}; + /// # #[derive(Debug, Eq, PartialEq, Ord, PartialOrd)] + /// # + /// struct Clock; + /// impl embedded_time::Clock for Clock { + /// type Rep = u32; + /// const PERIOD: Period =::new(1, 1_000); + /// // ... + /// # fn now() -> Instant {unimplemented!()} + /// } /// - /// let diff: Option> = Instant::::new(i32::MIN).duration_since(&Instant::::new(i32::MAX)); - /// assert_eq!(diff, Some(Microseconds(1_000_i64))); + /// assert_eq!(Instant::::new(5).duration_until::>(&Instant::::new(7)), + /// Ok(Microseconds(2_000_u64))); /// - /// let diff: Option> = Instant::::new(1_000).duration_since(&Instant::::new(-1_000)); - /// assert_eq!(diff, Some(Seconds(2_i64))); + /// assert_eq!(Instant::::new(7).duration_until::>(&Instant::::new(5)), + /// Err(())); /// ``` - pub fn duration_since(&self, other: &Self) -> Option + pub fn duration_until(&self, other: &Self) -> Result where Dur::Rep: TryFrom, { - Dur::from_ticks(self.ticks.wrapping_sub(&other.ticks), Clock::PERIOD) + if self > other { + Err(()) + } else { + Dur::from_ticks(other.ticks.wrapping_sub(&self.ticks), Clock::PERIOD).ok_or(()) + } } /// Returns the [`Duration`](trait.Duration.html) (in the provided units) since the beginning of /// time (or the [`Clock`](trait.Clock.html)'s 0) - pub fn duration_since_epoch(&self) -> Option + pub fn duration_since_epoch(&self) -> Result where Dur::Rep: TryFrom, - Clock::Rep: From, { Self::duration_since::( &self, &Self { - ticks: Clock::Rep::from(0_i32), + ticks: Clock::Rep::from(0), }, ) } @@ -96,15 +134,15 @@ impl PartialOrd for Instant { /// # #[derive(Debug, Eq, PartialEq, Ord, PartialOrd)] /// struct Clock; /// impl embedded_time::Clock for Clock { - /// type Rep = i32; - /// const PERIOD: Period = Period::new(1, 1_000); + /// type Rep = u32; + /// const PERIOD: Period =::new(1, 1_000); /// // ... /// # fn now() -> Instant {unimplemented!()} /// } /// /// assert!(Instant::::new(5) > Instant::::new(3)); /// assert!(Instant::::new(5) == Instant::::new(5)); - /// assert!(Instant::::new(i32::MAX) < Instant::::new(i32::MIN)); + /// assert!(Instant::::new(u32::MAX) < Instant::::new(u32::MIN)); /// ``` fn partial_cmp(&self, other: &Self) -> Option { Some(self.cmp(&other)) @@ -115,7 +153,8 @@ impl Ord for Instant { fn cmp(&self, other: &Self) -> Ordering { self.ticks .wrapping_sub(&other.ticks) - .cmp(&Clock::Rep::from(0)) + .cmp(&(::max_value() / 2.into())) + .reverse() } } @@ -128,22 +167,39 @@ where /// Add a duration to an instant resulting in a new, later instance /// /// # Panics - /// If [`Duration::into_ticks()`] returns [`None`]. In this case, `i32::MAX` of seconds - /// cannot be converted to the clock precision of milliseconds with i32 storage. + /// If [`Duration::into_ticks()`] returns [`None`]. In this case, `u32::MAX` of seconds + /// cannot be converted to the clock precision of milliseconds with u32 storage. + /// ```rust,should_panic + /// # use embedded_time::{Period, units::*, Instant}; + /// # #[derive(Debug, Eq, PartialEq, Ord, PartialOrd)] + /// struct Clock; + /// impl embedded_time::Clock for Clock { + /// type Rep = u32; + /// const PERIOD: Period =::new(1, 1_000); + /// // ... + /// # fn now() -> Instant {unimplemented!()} + /// } + /// + /// Instant::::new(1) + Seconds(u32::MAX); + /// ``` + /// + /// If the the [`Duration`] is greater than the signed Clock::Rep max value which would cause + /// the result to (logically) overflow /// ```rust,should_panic /// # use embedded_time::{Period, units::*, Instant}; /// # #[derive(Debug, Eq, PartialEq, Ord, PartialOrd)] /// struct Clock; /// impl embedded_time::Clock for Clock { - /// type Rep = i32; - /// const PERIOD: Period = Period::new(1, 1_000); + /// type Rep = u32; + /// const PERIOD: Period =::new(1, 1_000); /// // ... /// # fn now() -> Instant {unimplemented!()} /// } /// - /// Instant::::new(1) + Seconds(i32::MAX); + /// let _ = Instant::::new(0) + Milliseconds(i32::MAX as u32 + 1); /// ``` - /// See also: [`impl Add for Duration`](duration/units/index.html#addsub) + /// + /// See also [`impl Sub for Duration`](duration/units/index.html#addsub) /// /// # Examples /// ```rust @@ -151,19 +207,23 @@ where /// # #[derive(Debug, Eq, PartialEq, Ord, PartialOrd)] /// struct Clock; /// impl embedded_time::Clock for Clock { - /// type Rep = i32; - /// const PERIOD: Period = Period::new(1, 1_000); + /// type Rep = u32; + /// const PERIOD: Period =::new(1, 1_000); /// // ... /// # fn now() -> Instant {unimplemented!()} /// } /// - /// assert_eq!(Instant::::new(1) + Seconds(3), Instant::::new(3_001)); - /// assert_eq!(Instant::::new(-1) + Milliseconds(5_123), Instant::::new(5_122)); - /// assert_eq!(Instant::::new(1) + Milliseconds(700), Instant::::new(701)); - /// assert_eq!(Instant::::new(1_i32) + Milliseconds(700_i64), Instant::::new(701_i32)); + /// assert_eq!(Instant::::new(1) + Seconds(3_u32), Instant::::new(3_001)); + /// assert_eq!(Instant::::new(1) + Milliseconds(700_u32), Instant::::new(701)); + /// assert_eq!(Instant::::new(1) + Milliseconds(700_u64), Instant::::new(701)); + /// + /// // maximum duration allowed + /// assert_eq!(Instant::::new(0) + Milliseconds(i32::MAX as u32), + /// Instant::::new(u32::MAX/2)); /// ``` fn add(self, rhs: Dur) -> Self::Output { let add_ticks: Clock::Rep = rhs.into_ticks(Clock::PERIOD).unwrap(); + debug_assert!(add_ticks < ::max_value() / 2.into() + 1.into()); Self { ticks: self.ticks.wrapping_add(&add_ticks), @@ -180,21 +240,38 @@ where /// Subtract a duration from an instant resulting in a new, earlier instance /// /// # Panics - /// If [`Duration::into_ticks()`] returns [`None`]. In this case, `i32::MAX` of seconds - /// cannot be converted to the clock precision of milliseconds with i32 storage. + /// If [`Duration::into_ticks()`] returns [`None`]. In this case, `u32::MAX` of seconds + /// cannot be converted to the clock precision of milliseconds with u32 storage. + /// ```rust,should_panic + /// # use embedded_time::{Period, units::*, Instant}; + /// # #[derive(Debug, Eq, PartialEq, Ord, PartialOrd)] + /// struct Clock; + /// impl embedded_time::Clock for Clock { + /// type Rep = u32; + /// const PERIOD: Period =::new(1, 1_000); + /// // ... + /// # fn now() -> Instant {unimplemented!()} + /// } + /// + /// Instant::::new(1) - Seconds(u32::MAX); + /// ``` + /// + /// If the the [`Duration`] is greater than the signed Clock::Rep max value which would cause + /// the result to (logically) underflow /// ```rust,should_panic /// # use embedded_time::{Period, units::*, Instant}; /// # #[derive(Debug, Eq, PartialEq, Ord, PartialOrd)] /// struct Clock; /// impl embedded_time::Clock for Clock { - /// type Rep = i32; - /// const PERIOD: Period = Period::new(1, 1_000); + /// type Rep = u32; + /// const PERIOD: Period = ::new(1, 1_000); /// // ... /// # fn now() -> Instant {unimplemented!()} /// } /// - /// Instant::::new(1) - Seconds(i32::MAX); + /// let _ = Instant::::new(u32::MAX) - Milliseconds(i32::MAX as u32 + 1); /// ``` + /// /// See also [`impl Sub for Duration`](duration/units/index.html#addsub) /// /// # Examples @@ -203,22 +280,61 @@ where /// # #[derive(Debug, Eq, PartialEq, Ord, PartialOrd)] /// struct Clock; /// impl embedded_time::Clock for Clock { - /// type Rep = i32; - /// const PERIOD: Period = Period::new(1, 1_000); + /// type Rep = u32; + /// const PERIOD: Period =::new(1, 1_000); /// // ... /// # fn now() -> Instant {unimplemented!()} /// } /// - /// assert_eq!(Instant::::new(1) - Seconds(3), Instant::::new(-2_999)); - /// assert_eq!(Instant::::new(-1) - Milliseconds(5_123), Instant::::new(-5_124)); - /// assert_eq!(Instant::::new(800) - Milliseconds(700), Instant::::new(100)); - /// assert_eq!(Instant::::new(5_000_i32) - Milliseconds(700_i64), Instant::::new(4_300_i32)); + /// assert_eq!(Instant::::new(800) - Milliseconds(700_u32), Instant::::new(100)); + /// assert_eq!(Instant::::new(5_000) - Milliseconds(700_u64), Instant::::new(4_300)); + /// + /// // maximum duration allowed + /// assert_eq!(Instant::::new(u32::MAX) - Milliseconds(i32::MAX as u32), + /// Instant::::new(u32::MAX/2 + 1)); /// ``` fn sub(self, rhs: Dur) -> Self::Output { let sub_ticks: Clock::Rep = rhs.into_ticks(Clock::PERIOD).unwrap(); + debug_assert!(sub_ticks < ::max_value() / 2.into() + 1.into()); Self { ticks: self.ticks.wrapping_sub(&sub_ticks), } } } + +#[cfg(test)] +mod tests { + use crate::{self as time, units::*, Instant, Period}; + + #[derive(Debug, Eq, PartialEq, Ord, PartialOrd)] + struct Clock; + + impl time::Clock for Clock { + type Rep = u32; + const PERIOD: Period = ::new(1, 1_000); + + fn now() -> Instant { + unimplemented!() + } + } + + #[test] + fn duration_since() { + let diff: Result, _> = + Instant::::new(5).duration_since(&Instant::::new(3)); + assert_eq!(diff, Ok(Milliseconds(2_u32))); + + let diff: Result, _> = + Instant::::new(5).duration_since(&Instant::::new(3)); + assert_eq!(diff, Ok(Microseconds(2_000_u64))); + + let diff: Result, _> = + Instant::::new(u32::MIN).duration_since(&Instant::::new(u32::MAX)); + assert_eq!(diff, Ok(Microseconds(1_000_u64))); + + let diff: Result, _> = + Instant::::new(5).duration_since(&Instant::::new(6)); + assert_eq!(diff, Err(())); + } +} diff --git a/src/lib.rs b/src/lib.rs index 3f73df2..81bcc89 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,7 +1,7 @@ //! `embedded-time` provides a comprehensive library for implementing [`Clock`] abstractions over //! hardware to generate [`Instant`]s and using [`Duration`]s ([`Seconds`], [`Milliseconds`], etc) //! in embedded systems. The approach is similar to the C++ `chrono` library. A [`Duration`] -//! consists of an integer (whose type is chosen by the user to be either [`i32`] or [`i64`]) as +//! consists of an integer (whose type is chosen by the user to be either [`u32`] or [`u64`]) as //! well as a `const` fraction ([`Period`]) where the integer value multiplied by the fraction is //! the [`Duration`] in seconds. Put another way, the ratio is the precision of the LSbit of the //! integer. This structure avoids unnecessary arithmetic. For example, if the [`Duration`] type is @@ -9,8 +9,8 @@ //! value directly which is the number of milliseconds being represented. Conversion arithmetic is //! only performed when explicitly converting between time units. //! -//! In addition frequency-type types are available including [`Hertz`] ([`i32`]) and it's reciprocal -//! [`Period`] ([`i32`]/[`i32`] seconds). +//! In addition frequency-type types are available including [`Hertz`] ([`u32`]) and it's reciprocal +//! [`Period`] ([`u32`]/[`u32`] seconds). //! //! [`Seconds`]: units::Seconds //! [`Milliseconds`]: units::Milliseconds @@ -39,12 +39,12 @@ //! //! # Example Usage //! ```rust,no_run -//! # use embedded_time::{prelude::*, units::*, Instant, Period}; +//! # use embedded_time::{traits::*, units::*, Instant, Period}; //! # #[derive(Debug)] //! struct SomeClock; //! impl embedded_time::Clock for SomeClock { -//! type Rep = i64; -//! const PERIOD: Period = Period::new(1, 16_000_000); +//! type Rep = u64; +//! const PERIOD: Period = ::new(1, 16_000_000); //! //! fn now() -> Instant { //! // ... @@ -58,9 +58,9 @@ //! assert!(instant1 < instant2); // instant1 is *before* instant2 //! //! // duration is the difference between the instances -//! let duration: Option> = instant2.duration_since(&instant1); +//! let duration: Result, _> = instant2.duration_since(&instant1); //! -//! assert!(duration.is_some()); +//! assert!(duration.is_ok()); //! assert_eq!(instant1 + duration.unwrap(), instant2); //! ``` @@ -85,15 +85,16 @@ pub use time_int::TimeInt; /// Public _traits_ /// /// ```rust,no_run -/// use embedded_time::prelude::*; +/// use embedded_time::traits::*; /// ``` -pub mod prelude { +pub mod traits { // Rename traits to `_` to avoid any potential name conflicts. + pub use crate::clock::Clock as _; pub use crate::duration::Duration as _; pub use crate::duration::TryConvertFrom as _; pub use crate::duration::TryConvertInto as _; + pub use crate::time_int::NumericConstructor as _; pub use crate::time_int::TimeInt as _; - pub use crate::Clock as _; } pub mod units { @@ -106,16 +107,19 @@ pub mod units { #[allow(unused_imports)] mod tests { use crate as time; - use crate::prelude::*; - use crate::units::*; - use core::fmt::{self, Formatter}; + use core::{ + convert::TryFrom, + convert::TryInto, + fmt::{self, Formatter}, + }; + use time::{traits::*, units::*}; #[derive(Debug, Ord, PartialOrd, Eq, PartialEq)] struct MockClock64; impl time::Clock for MockClock64 { - type Rep = i64; - const PERIOD: time::Period = time::Period::new(1, 64_000_000); + type Rep = u64; + const PERIOD: time::Period = ::new(1, 64_000_000); fn now() -> time::Instant { time::Instant::new(128_000_000) @@ -126,19 +130,19 @@ mod tests { struct MockClock32; impl time::Clock for MockClock32 { - type Rep = i32; - const PERIOD: time::Period = time::Period::new(1, 16_000_000); + type Rep = u32; + const PERIOD: time::Period = ::new(1, 16_000_000); fn now() -> time::Instant { time::Instant::new(32_000_000) } } - fn get_time() + fn get_time() where - M: time::Clock, + u32: TryFrom, { - assert_eq!(M::now().duration_since_epoch(), Some(Seconds(2))); + assert_eq!(M::now().duration_since_epoch(), Ok(Seconds(2_u32))); } #[test] @@ -149,7 +153,7 @@ mod tests { get_time::(); get_time::(); - let then = then - Seconds(1); + let then = then - Seconds(1_u32); assert_ne!(then, now); assert!(then < now); } @@ -174,16 +178,17 @@ mod tests { fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { let duration = self .0 - .duration_since_epoch::>() - .ok_or(fmt::Error {})?; + .duration_since_epoch::>() + .map_err(|_| fmt::Error {})?; - let hours = Hours::::try_convert_from(duration).ok_or(fmt::Error {})?; + let hours = Hours::::try_convert_from(duration).ok_or(fmt::Error {})?; let minutes = - Minutes::::try_convert_from(duration).ok_or(fmt::Error {})? % Hours(1); + Minutes::::try_convert_from(duration).ok_or(fmt::Error {})? % Hours(1_u32); let seconds = - Seconds::::try_convert_from(duration).ok_or(fmt::Error {})? % Minutes(1); - let milliseconds = - Milliseconds::::try_convert_from(duration).ok_or(fmt::Error {})? % Seconds(1); + Seconds::::try_convert_from(duration).ok_or(fmt::Error {})? % Minutes(1_u32); + let milliseconds = Milliseconds::::try_convert_from(duration) + .ok_or(fmt::Error {})? + % Seconds(1_u32); f.write_fmt(format_args!( "{}:{:02}:{:02}.{:03}", diff --git a/src/period.rs b/src/period.rs index 2b68977..a95c426 100644 --- a/src/period.rs +++ b/src/period.rs @@ -7,7 +7,7 @@ use num::{rational::Ratio, CheckedDiv, CheckedMul}; /// /// Used primarily to define the period of one count of a [`crate::Duration`] type #[derive(Debug)] -pub struct Period(Ratio); +pub struct Period(Ratio); impl Period { /// Construct a new fractional `Period`. @@ -46,9 +46,7 @@ impl Period { Self(Ratio::from_integer(freq.0).recip()) } - /// Returns an integer approximation of the Period - /// - /// Any remainder is truncated. + /// Returns the value truncated to an integer pub fn to_integer(&self) -> T { self.0.to_integer() } @@ -62,9 +60,9 @@ impl Period { /// ```rust /// # use embedded_time::Period; - /// assert_eq!(Period::new(1000, 1).checked_mul_integer(5), Some(Period::new(5_000, 1))); + /// assert_eq!(Period::new(1000, 1).checked_mul_integer(5_u32), Some(Period::new(5_000, 1))); /// - /// assert_eq!(Period::new(i32::MAX, 1).checked_mul_integer(2), None); + /// assert_eq!(Period::new(u32::MAX, 1).checked_mul_integer(2_u32), None); /// ``` pub fn checked_mul_integer(&self, multiplier: T) -> Option { Some(Self(Ratio::checked_mul( @@ -75,10 +73,10 @@ impl Period { /// ```rust /// # use embedded_time::Period; - /// assert_eq!(Period::new(1000, 1).checked_div_integer(5), Some(Period::new(200, 1))); - /// assert_eq!(Period::new(1, 1000).checked_div_integer(5), Some(Period::new(1, 5000))); + /// assert_eq!(Period::new(1000, 1).checked_div_integer(5_u32), Some(Period::new(200, 1))); + /// assert_eq!(Period::new(1, 1000).checked_div_integer(5_u32), Some(Period::new(1, 5000))); /// - /// assert_eq!(Period::new(1, i32::MAX).checked_div_integer(2), None); + /// assert_eq!(Period::new(1, u32::MAX).checked_div_integer(2_u32), None); /// ``` pub fn checked_div_integer(&self, divisor: T) -> Option { Some(Self(Ratio::checked_div( @@ -96,8 +94,8 @@ where /// ```rust /// # use embedded_time::Period; - /// assert_eq!(Period::new(1000, 1) * Period::new(5,5), - /// Period::new(5_000, 5)); + /// assert_eq!(Period::::new(1000, 1) * ::new(5,5), + /// ::new(5_000, 5)); /// ``` fn mul(self, rhs: Self) -> Self::Output { Self(self.0 * rhs.0) @@ -113,7 +111,7 @@ where /// assert_eq!(::checked_mul(&Period::new(1000, 1), /// &Period::new(5,5)), Some(Period::new(5_000, 5))); /// - /// assert_eq!(::checked_mul(&Period::new(i32::MAX, 1), + /// assert_eq!(::checked_mul(&Period::new(u32::MAX, 1), /// &Period::new(2,1)), None); /// ``` fn checked_mul(&self, v: &Self) -> Option { @@ -129,8 +127,8 @@ where /// ```rust /// # use embedded_time::Period; - /// assert_eq!(Period::new(1000, 1) / Period::new(10, 1_000), - /// Period::new(1_000_000, 10)); + /// assert_eq!(Period::::new(1000, 1) / ::new(10, 1_000), + /// ::new(1_000_000, 10)); /// ``` fn div(self, rhs: Self) -> Self::Output { Self(self.0 / rhs.0) @@ -146,7 +144,7 @@ where /// assert_eq!(::checked_div(&Period::new(1000, 1), /// &Period::new(10, 1000)), Some(Period::new(1_000_000, 10))); /// - /// assert_eq!(::checked_div(&Period::new(1, i32::MAX), + /// assert_eq!(::checked_div(&Period::new(1, u32::MAX), /// &Period::new(2,1)), None); /// ``` fn checked_div(&self, v: &Self) -> Option { diff --git a/src/time_int.rs b/src/time_int.rs index 62e5ef3..f36d6f7 100644 --- a/src/time_int.rs +++ b/src/time_int.rs @@ -9,25 +9,15 @@ use core::{convert::TryFrom, convert::TryInto, fmt}; /// # Examples /// Basic construction of time-based values. /// ```rust -/// # use embedded_time::{prelude::*, units::*}; -/// assert_eq!(5.nanoseconds(), Nanoseconds(5)); -/// assert_eq!(5.microseconds(), Microseconds(5)); -/// assert_eq!(5.milliseconds(), Milliseconds(5)); -/// assert_eq!(5.seconds(), Seconds(5)); -/// assert_eq!(5.minutes(), Minutes(5)); -/// assert_eq!(5.hours(), Hours(5)); -/// assert_eq!(5.hertz(), Hertz(5)); -/// ``` +/// # use embedded_time::{traits::*, units::*}; +/// assert_eq!(5_u32.nanoseconds(), Nanoseconds(5_u32)); +/// assert_eq!(5_u32.microseconds(), Microseconds(5_u32)); +/// assert_eq!(5_u32.milliseconds(), Milliseconds(5_u32)); +/// assert_eq!(5_u32.seconds(), Seconds(5_u32)); +/// assert_eq!(5_u32.minutes(), Minutes(5_u32)); +/// assert_eq!(5_u32.hours(), Hours(5_u32)); /// -/// Signed integers work as well! -/// ```rust -/// # use embedded_time::{prelude::*, units::*}; -/// assert_eq!((-5).nanoseconds(), Nanoseconds(-5)); -/// assert_eq!((-5).microseconds(), Microseconds(-5)); -/// assert_eq!((-5).milliseconds(), Milliseconds(-5)); -/// assert_eq!((-5).seconds(), Seconds(-5)); -/// assert_eq!((-5).minutes(), Minutes(-5)); -/// assert_eq!((-5).hours(), Hours(-5)); +/// assert_eq!(5_u32.hertz(), Hertz(5_u32)); /// ``` pub trait TimeInt: Copy @@ -37,37 +27,22 @@ pub trait TimeInt: + num::traits::WrappingSub + num::CheckedMul + num::CheckedDiv - + From - + TryInto - + TryFrom + + From + + TryFrom + + TryInto + + TryFrom + TryInto - + Into + + Into + TryFrom + fmt::Display + fmt::Debug { - /// Construct the [`Duration`](crate::duration::Duration) implementation - fn nanoseconds(self) -> Nanoseconds; - /// Construct the [`Duration`](crate::duration::Duration) implementation - fn microseconds(self) -> Microseconds; - /// Construct the [`Duration`](crate::duration::Duration) implementation - fn milliseconds(self) -> Milliseconds; - /// Construct the [`Duration`](crate::duration::Duration) implementation - fn seconds(self) -> Seconds; - /// Construct the [`Duration`](crate::duration::Duration) implementation - fn minutes(self) -> Minutes; - /// Construct the [`Duration`](crate::duration::Duration) implementation - fn hours(self) -> Hours; - - /// Construct the frequency type - fn hertz(self) -> Hertz; - /// ```rust - /// # use embedded_time::{Period, prelude::*}; - /// assert_eq!(8_i32.checked_mul_period(&Period::new(1,2)), Some(4_i32)); + /// # use embedded_time::{Period, traits::*}; + /// assert_eq!(8_u32.checked_mul_period(&Period::new(1,2)), Some(4_u32)); /// /// // the result is not rounded, but truncated - /// assert_eq!(8_i32.checked_mul_period(&Period::new(1,3)), Some(2_i32)); + /// assert_eq!(8_u32.checked_mul_period(&Period::new(1,3)), Some(2_u32)); /// ``` fn checked_mul_period(&self, period: &Period) -> Option { Some(::checked_div( @@ -77,9 +52,9 @@ pub trait TimeInt: } /// ```rust - /// # use embedded_time::{Period, prelude::*}; - /// assert_eq!(8_i32.checked_div_period(&Period::new(1,2)), Some(16_i32)); - /// assert_eq!(8_i32.checked_div_period(&Period::new(3,2)), Some(5_i32)); + /// # use embedded_time::{Period, traits::*}; + /// assert_eq!(8_u32.checked_div_period(&Period::new(1,2)), Some(16_u32)); + /// assert_eq!(8_u32.checked_div_period(&Period::new(3,2)), Some(5_u32)); /// ``` fn checked_div_period(&self, period: &Period) -> Option { Some(::checked_div( @@ -89,10 +64,29 @@ pub trait TimeInt: } } -macro_rules! impl_time_ints { +/// Construct a time-related type with suffix units +pub trait NumericConstructor: TimeInt { + /// Construct the [`Duration`](crate::duration::Duration) implementation + fn nanoseconds(self) -> Nanoseconds; + /// Construct the [`Duration`](crate::duration::Duration) implementation + fn microseconds(self) -> Microseconds; + /// Construct the [`Duration`](crate::duration::Duration) implementation + fn milliseconds(self) -> Milliseconds; + /// Construct the [`Duration`](crate::duration::Duration) implementation + fn seconds(self) -> Seconds; + /// Construct the [`Duration`](crate::duration::Duration) implementation + fn minutes(self) -> Minutes; + /// Construct the [`Duration`](crate::duration::Duration) implementation + fn hours(self) -> Hours; + + /// Construct the frequency type + fn hertz(self) -> Hertz; +} + +macro_rules! impl_numeric_constructors { ($($type:ty),* $(,)?) => { $( - impl TimeInt for $type { + impl NumericConstructor for $type { #[inline(always)] fn nanoseconds(self) -> Nanoseconds<$type> { Nanoseconds(self) @@ -132,4 +126,8 @@ macro_rules! impl_time_ints { }; } -impl_time_ints![i32, i64]; +impl_numeric_constructors![u32, u64]; + +impl TimeInt for u32 {} + +impl TimeInt for u64 {} From c1debd21eb0ac6870f0818d7ef720d6ab8f12a8c Mon Sep 17 00:00:00 2001 From: Peter Taylor Date: Tue, 30 Jun 2020 17:38:57 -0600 Subject: [PATCH 07/23] feat(Clock): Allow `Clock` impls to be stateful --- CHANGELOG.md | 1 + examples/nrf52_dk/main.rs | 64 ++++++++++++++++++--------------------- src/clock.rs | 8 ++--- src/instant.rs | 21 +++++++------ src/lib.rs | 29 ++++++++++-------- 5 files changed, 62 insertions(+), 61 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index b2e4dc4..f7906b5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,7 @@ ### Changed +- Add `&mut self` to `Clock` functions (make stateful, or at least allow stateful implementations) - All time-type inner types from signed to unsigned - `Instant::duration_since()` return type to `Result` - Refactor `examples/nrf52_dk` diff --git a/examples/nrf52_dk/main.rs b/examples/nrf52_dk/main.rs index a47af02..cf6fbee 100644 --- a/examples/nrf52_dk/main.rs +++ b/examples/nrf52_dk/main.rs @@ -3,10 +3,8 @@ extern crate panic_rtt; -use cortex_m::mutex::CriticalSectionMutex as Mutex; use cortex_m_rt::entry; -use embedded_time::{self as time, traits::*, Clock, Instant, Period}; -use mutex_trait::Mutex as _; +use embedded_time::{self as time, traits::*}; pub mod nrf52 { pub use nrf52832_hal::{ @@ -14,47 +12,42 @@ pub mod nrf52 { prelude::*, target::{self as pac, Peripherals}, }; +} - pub struct Clock64 { - low: pac::TIMER0, - high: pac::TIMER1, - capture_task: pac::EGU0, - } - - impl Clock64 { - pub fn take(low: pac::TIMER0, high: pac::TIMER1, capture_task: pac::EGU0) -> Self { - Self { - low, - high, - capture_task, - } - } +pub struct SysClock { + low: nrf52::pac::TIMER0, + high: nrf52::pac::TIMER1, + capture_task: nrf52::pac::EGU0, +} - pub(crate) fn read(&mut self) -> u64 { - self.capture_task.tasks_trigger[0].write(|write| unsafe { write.bits(1) }); - self.low.cc[0].read().bits() as u64 | ((self.high.cc[0].read().bits() as u64) << 32) +impl SysClock { + pub fn take( + low: nrf52::pac::TIMER0, + high: nrf52::pac::TIMER1, + capture_task: nrf52::pac::EGU0, + ) -> Self { + Self { + low, + high, + capture_task, } } } -pub struct SysClock; - impl time::Clock for SysClock { type Rep = u64; - const PERIOD: Period = ::new(1, 16_000_000); + const PERIOD: time::Period = ::new(1, 16_000_000); - fn now() -> Instant { - let ticks = (&CLOCK64).lock(|clock| match clock { - Some(clock) => clock.read(), - None => 0, - }); + fn now(&mut self) -> time::Instant { + self.capture_task.tasks_trigger[0].write(|write| unsafe { write.bits(1) }); - Instant::new(ticks as Self::Rep) + let ticks = + self.low.cc[0].read().bits() as u64 | ((self.high.cc[0].read().bits() as u64) << 32); + + time::Instant::new(ticks as Self::Rep) } } -static CLOCK64: Mutex> = Mutex::new(None); - #[entry] fn main() -> ! { let device = nrf52::pac::Peripherals::take().unwrap(); @@ -106,8 +99,7 @@ fn main() -> ! { // This moves these peripherals to prevent conflicting usage, however not the entire EGU0 is // used. A ref to EGU0 could be sent instead, although that provides no protection for the // fields that are being used by Clock64. - let clock64 = nrf52::Clock64::take(device.TIMER0, device.TIMER1, device.EGU0); - (&CLOCK64).lock(|ticks| *ticks = Some(clock64)); + let mut clock = SysClock::take(device.TIMER0, device.TIMER1, device.EGU0); let port0 = nrf52::gpio::p0::Parts::new(device.P0); @@ -136,6 +128,7 @@ fn main() -> ! { &mut led2.degrade(), &mut led3.degrade(), &mut led4.degrade(), + &mut clock, ) .unwrap(); @@ -147,6 +140,7 @@ fn run( led2: &mut Led, led3: &mut Led, led4: &mut Led, + clock: &mut SysClock, ) -> Result<(), ::Error> where Led: nrf52::OutputPin, @@ -156,12 +150,12 @@ where led2.set_high()?; led3.set_high()?; led4.set_low()?; - SysClock::delay(250_u32.milliseconds()); + clock.delay(250_u32.milliseconds()); led1.set_high()?; led2.set_low()?; led3.set_low()?; led4.set_high()?; - SysClock::delay(250_u32.milliseconds()); + clock.delay(250_u32.milliseconds()); } } diff --git a/src/clock.rs b/src/clock.rs index bcb7e85..9a14faf 100644 --- a/src/clock.rs +++ b/src/clock.rs @@ -10,15 +10,15 @@ pub trait Clock: Sized { const PERIOD: Period; /// Get the current Instant - fn now() -> Instant; + fn now(&mut self) -> Instant; /// Blocking delay - fn delay(dur: Dur) + fn delay(&mut self, dur: Dur) where Self::Rep: TryFrom, { - let start = Self::now(); + let start = self.now(); let end = start + dur; - while Self::now() < end {} + while self.now() < end {} } } diff --git a/src/instant.rs b/src/instant.rs index b684711..b56a9a5 100644 --- a/src/instant.rs +++ b/src/instant.rs @@ -38,7 +38,7 @@ impl Instant { /// type Rep = u32; /// const PERIOD: Period = ::new(1, 1_000); /// // ... - /// # fn now() -> Instant {unimplemented!()} + /// # fn now(&mut self) -> Instant {unimplemented!()} /// } /// /// assert_eq!(Instant::::new(5).duration_since::>(&Instant::::new(3)), @@ -74,7 +74,7 @@ impl Instant { /// type Rep = u32; /// const PERIOD: Period =::new(1, 1_000); /// // ... - /// # fn now() -> Instant {unimplemented!()} + /// # fn now(&mut self) -> Instant {unimplemented!()} /// } /// /// assert_eq!(Instant::::new(5).duration_until::>(&Instant::::new(7)), @@ -99,6 +99,7 @@ impl Instant { pub fn duration_since_epoch(&self) -> Result where Dur::Rep: TryFrom, + Clock::Rep: TryFrom, { Self::duration_since::( &self, @@ -137,7 +138,7 @@ impl PartialOrd for Instant { /// type Rep = u32; /// const PERIOD: Period =::new(1, 1_000); /// // ... - /// # fn now() -> Instant {unimplemented!()} + /// # fn now(&mut self) -> Instant {unimplemented!()} /// } /// /// assert!(Instant::::new(5) > Instant::::new(3)); @@ -177,7 +178,7 @@ where /// type Rep = u32; /// const PERIOD: Period =::new(1, 1_000); /// // ... - /// # fn now() -> Instant {unimplemented!()} + /// # fn now(&mut self) -> Instant {unimplemented!()} /// } /// /// Instant::::new(1) + Seconds(u32::MAX); @@ -193,7 +194,7 @@ where /// type Rep = u32; /// const PERIOD: Period =::new(1, 1_000); /// // ... - /// # fn now() -> Instant {unimplemented!()} + /// # fn now(&mut self) -> Instant {unimplemented!()} /// } /// /// let _ = Instant::::new(0) + Milliseconds(i32::MAX as u32 + 1); @@ -210,7 +211,7 @@ where /// type Rep = u32; /// const PERIOD: Period =::new(1, 1_000); /// // ... - /// # fn now() -> Instant {unimplemented!()} + /// # fn now(&mut self) -> Instant {unimplemented!()} /// } /// /// assert_eq!(Instant::::new(1) + Seconds(3_u32), Instant::::new(3_001)); @@ -250,7 +251,7 @@ where /// type Rep = u32; /// const PERIOD: Period =::new(1, 1_000); /// // ... - /// # fn now() -> Instant {unimplemented!()} + /// # fn now(&mut self) -> Instant {unimplemented!()} /// } /// /// Instant::::new(1) - Seconds(u32::MAX); @@ -266,7 +267,7 @@ where /// type Rep = u32; /// const PERIOD: Period = ::new(1, 1_000); /// // ... - /// # fn now() -> Instant {unimplemented!()} + /// # fn now(&mut self) -> Instant {unimplemented!()} /// } /// /// let _ = Instant::::new(u32::MAX) - Milliseconds(i32::MAX as u32 + 1); @@ -283,7 +284,7 @@ where /// type Rep = u32; /// const PERIOD: Period =::new(1, 1_000); /// // ... - /// # fn now() -> Instant {unimplemented!()} + /// # fn now(&mut self) -> Instant {unimplemented!()} /// } /// /// assert_eq!(Instant::::new(800) - Milliseconds(700_u32), Instant::::new(100)); @@ -314,7 +315,7 @@ mod tests { type Rep = u32; const PERIOD: Period = ::new(1, 1_000); - fn now() -> Instant { + fn now(&mut self) -> Instant { unimplemented!() } } diff --git a/src/lib.rs b/src/lib.rs index 81bcc89..54000a9 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -46,15 +46,16 @@ //! type Rep = u64; //! const PERIOD: Period = ::new(1, 16_000_000); //! -//! fn now() -> Instant { +//! fn now(&mut self) -> Instant { //! // ... //! # unimplemented!() //! } //! } //! -//! let instant1 = SomeClock::now(); +//! let mut clock = SomeClock; +//! let instant1 = clock.now(); //! // ... -//! let instant2 = SomeClock::now(); +//! let instant2 = clock.now(); //! assert!(instant1 < instant2); // instant1 is *before* instant2 //! //! // duration is the difference between the instances @@ -121,7 +122,7 @@ mod tests { type Rep = u64; const PERIOD: time::Period = ::new(1, 64_000_000); - fn now() -> time::Instant { + fn now(&mut self) -> time::Instant { time::Instant::new(128_000_000) } } @@ -133,25 +134,29 @@ mod tests { type Rep = u32; const PERIOD: time::Period = ::new(1, 16_000_000); - fn now() -> time::Instant { + fn now(&mut self) -> time::Instant { time::Instant::new(32_000_000) } } - fn get_time() + fn get_time(clock: &mut Clock) where - u32: TryFrom, + u32: TryFrom, + Clock::Rep: TryFrom, { - assert_eq!(M::now().duration_since_epoch(), Ok(Seconds(2_u32))); + assert_eq!(clock.now().duration_since_epoch(), Ok(Seconds(2_u32))); } #[test] fn common_types() { - let then = MockClock32::now(); - let now = MockClock32::now(); + let then = MockClock32.now(); + let now = MockClock32.now(); - get_time::(); - get_time::(); + let mut clock64 = MockClock64 {}; + let mut clock32 = MockClock32 {}; + + get_time(&mut clock64); + get_time(&mut clock32); let then = then - Seconds(1_u32); assert_ne!(then, now); From 4a859dae354fb23cc2d4bc00dffb31bb2917836c Mon Sep 17 00:00:00 2001 From: Peter Taylor Date: Thu, 2 Jul 2020 15:14:00 -0600 Subject: [PATCH 08/23] refactor(Clock): Allow immutable `Clock` objects Changed `now(&mut self)` to `now(&self)` --- examples/nrf52_dk/main.rs | 2 +- src/clock.rs | 6 ++++-- src/instant.rs | 20 ++++++++++---------- src/lib.rs | 6 +++--- 4 files changed, 18 insertions(+), 16 deletions(-) diff --git a/examples/nrf52_dk/main.rs b/examples/nrf52_dk/main.rs index cf6fbee..c3ba7c8 100644 --- a/examples/nrf52_dk/main.rs +++ b/examples/nrf52_dk/main.rs @@ -38,7 +38,7 @@ impl time::Clock for SysClock { type Rep = u64; const PERIOD: time::Period = ::new(1, 16_000_000); - fn now(&mut self) -> time::Instant { + fn now(&self) -> time::Instant { self.capture_task.tasks_trigger[0].write(|write| unsafe { write.bits(1) }); let ticks = diff --git a/src/clock.rs b/src/clock.rs index 9a14faf..7e55c13 100644 --- a/src/clock.rs +++ b/src/clock.rs @@ -1,3 +1,5 @@ +//! The `Clock` trait can be implemented over hardware timers or other time-keeping device + use crate::{time_int::TimeInt, Duration, Instant, Period}; use core::convert::TryFrom; @@ -10,10 +12,10 @@ pub trait Clock: Sized { const PERIOD: Period; /// Get the current Instant - fn now(&mut self) -> Instant; + fn now(&self) -> Instant; /// Blocking delay - fn delay(&mut self, dur: Dur) + fn delay(&self, dur: Dur) where Self::Rep: TryFrom, { diff --git a/src/instant.rs b/src/instant.rs index b56a9a5..378e707 100644 --- a/src/instant.rs +++ b/src/instant.rs @@ -38,7 +38,7 @@ impl Instant { /// type Rep = u32; /// const PERIOD: Period = ::new(1, 1_000); /// // ... - /// # fn now(&mut self) -> Instant {unimplemented!()} + /// # fn now(&self) -> Instant {unimplemented!()} /// } /// /// assert_eq!(Instant::::new(5).duration_since::>(&Instant::::new(3)), @@ -74,7 +74,7 @@ impl Instant { /// type Rep = u32; /// const PERIOD: Period =::new(1, 1_000); /// // ... - /// # fn now(&mut self) -> Instant {unimplemented!()} + /// # fn now(&self) -> Instant {unimplemented!()} /// } /// /// assert_eq!(Instant::::new(5).duration_until::>(&Instant::::new(7)), @@ -138,7 +138,7 @@ impl PartialOrd for Instant { /// type Rep = u32; /// const PERIOD: Period =::new(1, 1_000); /// // ... - /// # fn now(&mut self) -> Instant {unimplemented!()} + /// # fn now(&self) -> Instant {unimplemented!()} /// } /// /// assert!(Instant::::new(5) > Instant::::new(3)); @@ -178,7 +178,7 @@ where /// type Rep = u32; /// const PERIOD: Period =::new(1, 1_000); /// // ... - /// # fn now(&mut self) -> Instant {unimplemented!()} + /// # fn now(&self) -> Instant {unimplemented!()} /// } /// /// Instant::::new(1) + Seconds(u32::MAX); @@ -194,7 +194,7 @@ where /// type Rep = u32; /// const PERIOD: Period =::new(1, 1_000); /// // ... - /// # fn now(&mut self) -> Instant {unimplemented!()} + /// # fn now(&self) -> Instant {unimplemented!()} /// } /// /// let _ = Instant::::new(0) + Milliseconds(i32::MAX as u32 + 1); @@ -211,7 +211,7 @@ where /// type Rep = u32; /// const PERIOD: Period =::new(1, 1_000); /// // ... - /// # fn now(&mut self) -> Instant {unimplemented!()} + /// # fn now(&self) -> Instant {unimplemented!()} /// } /// /// assert_eq!(Instant::::new(1) + Seconds(3_u32), Instant::::new(3_001)); @@ -251,7 +251,7 @@ where /// type Rep = u32; /// const PERIOD: Period =::new(1, 1_000); /// // ... - /// # fn now(&mut self) -> Instant {unimplemented!()} + /// # fn now(&self) -> Instant {unimplemented!()} /// } /// /// Instant::::new(1) - Seconds(u32::MAX); @@ -267,7 +267,7 @@ where /// type Rep = u32; /// const PERIOD: Period = ::new(1, 1_000); /// // ... - /// # fn now(&mut self) -> Instant {unimplemented!()} + /// # fn now(&self) -> Instant {unimplemented!()} /// } /// /// let _ = Instant::::new(u32::MAX) - Milliseconds(i32::MAX as u32 + 1); @@ -284,7 +284,7 @@ where /// type Rep = u32; /// const PERIOD: Period =::new(1, 1_000); /// // ... - /// # fn now(&mut self) -> Instant {unimplemented!()} + /// # fn now(&self) -> Instant {unimplemented!()} /// } /// /// assert_eq!(Instant::::new(800) - Milliseconds(700_u32), Instant::::new(100)); @@ -315,7 +315,7 @@ mod tests { type Rep = u32; const PERIOD: Period = ::new(1, 1_000); - fn now(&mut self) -> Instant { + fn now(&self) -> Instant { unimplemented!() } } diff --git a/src/lib.rs b/src/lib.rs index 54000a9..ecedf99 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -46,7 +46,7 @@ //! type Rep = u64; //! const PERIOD: Period = ::new(1, 16_000_000); //! -//! fn now(&mut self) -> Instant { +//! fn now(&self) -> Instant { //! // ... //! # unimplemented!() //! } @@ -122,7 +122,7 @@ mod tests { type Rep = u64; const PERIOD: time::Period = ::new(1, 64_000_000); - fn now(&mut self) -> time::Instant { + fn now(&self) -> time::Instant { time::Instant::new(128_000_000) } } @@ -134,7 +134,7 @@ mod tests { type Rep = u32; const PERIOD: time::Period = ::new(1, 16_000_000); - fn now(&mut self) -> time::Instant { + fn now(&self) -> time::Instant { time::Instant::new(32_000_000) } } From 6df560fecd5cafccfa5a6ef90e4a0144b7ffa80d Mon Sep 17 00:00:00 2001 From: Peter Taylor Date: Tue, 30 Jun 2020 18:57:30 -0600 Subject: [PATCH 09/23] feat(Clock): Add `Result` return to `Clock` methods BREAKING CHANGE: The returned `Result` must now be handled --- CHANGELOG.md | 1 + examples/nrf52_dk/main.rs | 8 +++---- src/clock.rs | 17 ++++++++++---- src/instant.rs | 20 ++++++++-------- src/lib.rs | 49 ++++++++++++++++++++++++++++----------- 5 files changed, 63 insertions(+), 32 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index f7906b5..11fc3a1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,7 @@ ### Added +- Add fallibility to `Clock` methods - `Instant::duration_until()` with order checking - Order checking to `Instant::duration_since()` - Bounds checking on `Instant` impls of Add/Sub diff --git a/examples/nrf52_dk/main.rs b/examples/nrf52_dk/main.rs index c3ba7c8..6bc6ed6 100644 --- a/examples/nrf52_dk/main.rs +++ b/examples/nrf52_dk/main.rs @@ -38,13 +38,13 @@ impl time::Clock for SysClock { type Rep = u64; const PERIOD: time::Period = ::new(1, 16_000_000); - fn now(&self) -> time::Instant { + fn now(&self) -> Result, time::Error> { self.capture_task.tasks_trigger[0].write(|write| unsafe { write.bits(1) }); let ticks = self.low.cc[0].read().bits() as u64 | ((self.high.cc[0].read().bits() as u64) << 32); - time::Instant::new(ticks as Self::Rep) + Ok(time::Instant::new(ticks as Self::Rep)) } } @@ -150,12 +150,12 @@ where led2.set_high()?; led3.set_high()?; led4.set_low()?; - clock.delay(250_u32.milliseconds()); + clock.delay(250_u32.milliseconds()).unwrap(); led1.set_high()?; led2.set_low()?; led3.set_low()?; led4.set_high()?; - clock.delay(250_u32.milliseconds()); + clock.delay(250_u32.milliseconds()).unwrap(); } } diff --git a/src/clock.rs b/src/clock.rs index 7e55c13..a9f2ccc 100644 --- a/src/clock.rs +++ b/src/clock.rs @@ -1,6 +1,6 @@ //! The `Clock` trait can be implemented over hardware timers or other time-keeping device -use crate::{time_int::TimeInt, Duration, Instant, Period}; +use crate::{time_int::TimeInt, Duration, Error, Instant, Period}; use core::convert::TryFrom; /// An abstraction for time-keeping items such as hardware timers @@ -12,15 +12,22 @@ pub trait Clock: Sized { const PERIOD: Period; /// Get the current Instant - fn now(&self) -> Instant; + /// + /// # Errors + /// - Error: The current instant was not readable + fn now(&self) -> Result, Error>; /// Blocking delay - fn delay(&self, dur: Dur) + /// + /// # Errors + /// - Error: The current instant was not readable, actual delay (if any) is unknown + fn delay(&self, dur: Dur) -> Result<(), Error> where Self::Rep: TryFrom, { - let start = self.now(); + let start = self.now()?; let end = start + dur; - while self.now() < end {} + while self.now()? < end {} + Ok(()) } } diff --git a/src/instant.rs b/src/instant.rs index 378e707..c1a384d 100644 --- a/src/instant.rs +++ b/src/instant.rs @@ -38,7 +38,7 @@ impl Instant { /// type Rep = u32; /// const PERIOD: Period = ::new(1, 1_000); /// // ... - /// # fn now(&self) -> Instant {unimplemented!()} + /// # fn now(&self) -> Result, embedded_time::Error> {unimplemented!()} /// } /// /// assert_eq!(Instant::::new(5).duration_since::>(&Instant::::new(3)), @@ -74,7 +74,7 @@ impl Instant { /// type Rep = u32; /// const PERIOD: Period =::new(1, 1_000); /// // ... - /// # fn now(&self) -> Instant {unimplemented!()} + /// # fn now(&self) -> Result, embedded_time::Error> {unimplemented!()} /// } /// /// assert_eq!(Instant::::new(5).duration_until::>(&Instant::::new(7)), @@ -138,7 +138,7 @@ impl PartialOrd for Instant { /// type Rep = u32; /// const PERIOD: Period =::new(1, 1_000); /// // ... - /// # fn now(&self) -> Instant {unimplemented!()} + /// # fn now(&self) -> Result, embedded_time::Error> {unimplemented!()} /// } /// /// assert!(Instant::::new(5) > Instant::::new(3)); @@ -178,7 +178,7 @@ where /// type Rep = u32; /// const PERIOD: Period =::new(1, 1_000); /// // ... - /// # fn now(&self) -> Instant {unimplemented!()} + /// # fn now(&self) -> Result, embedded_time::Error> {unimplemented!()} /// } /// /// Instant::::new(1) + Seconds(u32::MAX); @@ -194,7 +194,7 @@ where /// type Rep = u32; /// const PERIOD: Period =::new(1, 1_000); /// // ... - /// # fn now(&self) -> Instant {unimplemented!()} + /// # fn now(&self) -> Result, embedded_time::Error> {unimplemented!()} /// } /// /// let _ = Instant::::new(0) + Milliseconds(i32::MAX as u32 + 1); @@ -211,7 +211,7 @@ where /// type Rep = u32; /// const PERIOD: Period =::new(1, 1_000); /// // ... - /// # fn now(&self) -> Instant {unimplemented!()} + /// # fn now(&self) -> Result, embedded_time::Error> {unimplemented!()} /// } /// /// assert_eq!(Instant::::new(1) + Seconds(3_u32), Instant::::new(3_001)); @@ -251,7 +251,7 @@ where /// type Rep = u32; /// const PERIOD: Period =::new(1, 1_000); /// // ... - /// # fn now(&self) -> Instant {unimplemented!()} + /// # fn now(&self) -> Result, embedded_time::Error> {unimplemented!()} /// } /// /// Instant::::new(1) - Seconds(u32::MAX); @@ -267,7 +267,7 @@ where /// type Rep = u32; /// const PERIOD: Period = ::new(1, 1_000); /// // ... - /// # fn now(&self) -> Instant {unimplemented!()} + /// # fn now(&self) -> Result, embedded_time::Error> {unimplemented!()} /// } /// /// let _ = Instant::::new(u32::MAX) - Milliseconds(i32::MAX as u32 + 1); @@ -284,7 +284,7 @@ where /// type Rep = u32; /// const PERIOD: Period =::new(1, 1_000); /// // ... - /// # fn now(&self) -> Instant {unimplemented!()} + /// # fn now(&self) -> Result, embedded_time::Error> {unimplemented!()} /// } /// /// assert_eq!(Instant::::new(800) - Milliseconds(700_u32), Instant::::new(100)); @@ -315,7 +315,7 @@ mod tests { type Rep = u32; const PERIOD: Period = ::new(1, 1_000); - fn now(&self) -> Instant { + fn now(&self) -> Result, time::Error> { unimplemented!() } } diff --git a/src/lib.rs b/src/lib.rs index ecedf99..eff314d 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -46,16 +46,16 @@ //! type Rep = u64; //! const PERIOD: Period = ::new(1, 16_000_000); //! -//! fn now(&self) -> Instant { +//! fn now(&self) -> Result, embedded_time::Error> { //! // ... //! # unimplemented!() //! } //! } //! //! let mut clock = SomeClock; -//! let instant1 = clock.now(); +//! let instant1 = clock.now().unwrap(); //! // ... -//! let instant2 = clock.now(); +//! let instant2 = clock.now().unwrap(); //! assert!(instant1 < instant2); // instant1 is *before* instant2 //! //! // duration is the difference between the instances @@ -70,7 +70,7 @@ #![warn(missing_docs)] #![deny(intra_doc_link_resolution_failure)] -mod clock; +pub mod clock; mod duration; mod frequency; mod instant; @@ -104,6 +104,10 @@ pub mod units { pub use crate::frequency::units::*; } +/// Time-related error +#[derive(Debug, Eq, PartialEq)] +pub struct Error; + #[cfg(test)] #[allow(unused_imports)] mod tests { @@ -115,27 +119,38 @@ mod tests { }; use time::{traits::*, units::*}; - #[derive(Debug, Ord, PartialOrd, Eq, PartialEq)] struct MockClock64; impl time::Clock for MockClock64 { type Rep = u64; const PERIOD: time::Period = ::new(1, 64_000_000); - fn now(&self) -> time::Instant { - time::Instant::new(128_000_000) + fn now(&self) -> Result, time::Error> { + Ok(time::Instant::new(128_000_000)) } } - #[derive(Debug, Ord, PartialOrd, Eq, PartialEq)] + #[derive(Debug)] struct MockClock32; impl time::Clock for MockClock32 { type Rep = u32; const PERIOD: time::Period = ::new(1, 16_000_000); - fn now(&self) -> time::Instant { - time::Instant::new(32_000_000) + fn now(&self) -> Result, time::Error> { + Ok(time::Instant::new(32_000_000)) + } + } + + #[derive(Debug)] + struct BadClock; + + impl time::Clock for BadClock { + type Rep = u32; + const PERIOD: time::Period = time::Period::new(1, 16_000_000); + + fn now(&mut self) -> Result, time::Error> { + Err(time::Error) } } @@ -144,13 +159,16 @@ mod tests { u32: TryFrom, Clock::Rep: TryFrom, { - assert_eq!(clock.now().duration_since_epoch(), Ok(Seconds(2_u32))); + assert_eq!( + clock.now().unwrap().duration_since_epoch(), + Ok(Seconds(2_u32)) + ); } #[test] fn common_types() { - let then = MockClock32.now(); - let now = MockClock32.now(); + let then = MockClock32.now().unwrap(); + let now = MockClock32.now().unwrap(); let mut clock64 = MockClock64 {}; let mut clock32 = MockClock32 {}; @@ -163,6 +181,11 @@ mod tests { assert!(then < now); } + #[test] + fn clock_error() { + assert_eq!(BadClock.now(), Err(time::Error)); + } + struct Timestamp(time::Instant) where Clock: time::Clock; From 4c8a09e97c9fdf47388eb2c8fdcebd622d1e261e Mon Sep 17 00:00:00 2001 From: Peter Taylor Date: Wed, 1 Jul 2020 09:59:13 -0600 Subject: [PATCH 10/23] feat(Clock): Add Error variants --- src/lib.rs | 22 +++++++++++++++++++--- 1 file changed, 19 insertions(+), 3 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index eff314d..5a3af98 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -105,8 +105,24 @@ pub mod units { } /// Time-related error +#[non_exhaustive] #[derive(Debug, Eq, PartialEq)] -pub struct Error; +pub enum Error { + /// Communication with the clock failed + ClockInterfaceFailure, + /// The clock has not been started + ClockNotStarted, + /// Reading an `Instant` from the clock failed for some reason + UnableToReadFromClock, + /// Negative `Duration`s are not supported + NegativeDurationNotAllowed, + /// An integer overflow was detected + Overflow, + /// An integer underflow was detected + Underflow, + /// A divide-by-zero was detected + DivByZero, +} #[cfg(test)] #[allow(unused_imports)] @@ -150,7 +166,7 @@ mod tests { const PERIOD: time::Period = time::Period::new(1, 16_000_000); fn now(&mut self) -> Result, time::Error> { - Err(time::Error) + Err(time::Error::UnableToReadFromClock) } } @@ -183,7 +199,7 @@ mod tests { #[test] fn clock_error() { - assert_eq!(BadClock.now(), Err(time::Error)); + assert_eq!(BadClock.now(), Err(time::Error::UnableToReadFromClock)); } struct Timestamp(time::Instant) From db779d0f69bc93afab7fddc4803eb2559f6fa54c Mon Sep 17 00:00:00 2001 From: Peter Taylor Date: Wed, 1 Jul 2020 22:04:33 -0600 Subject: [PATCH 11/23] refactor(Clock): Changed error handling scheme - Remove the crate-level, monolithic `Error` enum - Add crate-level `Error` trait - Add associated error type to `Clock` trait for implementation-specific errors - Add clock::Error enum with a single variant, `Other(E)` where E is an impl-specific error type --- examples/nrf52_dk/main.rs | 3 +- src/clock.rs | 21 ++++++++++--- src/instant.rs | 30 ++++++++++++------ src/lib.rs | 65 +++++++++++++++++++-------------------- 4 files changed, 69 insertions(+), 50 deletions(-) diff --git a/examples/nrf52_dk/main.rs b/examples/nrf52_dk/main.rs index 6bc6ed6..82410c4 100644 --- a/examples/nrf52_dk/main.rs +++ b/examples/nrf52_dk/main.rs @@ -37,8 +37,9 @@ impl SysClock { impl time::Clock for SysClock { type Rep = u64; const PERIOD: time::Period = ::new(1, 16_000_000); + type ImplError = (); - fn now(&self) -> Result, time::Error> { + fn now(&self) -> Result, time::clock::Error> { self.capture_task.tasks_trigger[0].write(|write| unsafe { write.bits(1) }); let ticks = diff --git a/src/clock.rs b/src/clock.rs index a9f2ccc..4fcc263 100644 --- a/src/clock.rs +++ b/src/clock.rs @@ -1,8 +1,16 @@ //! The `Clock` trait can be implemented over hardware timers or other time-keeping device -use crate::{time_int::TimeInt, Duration, Error, Instant, Period}; +use crate::{time_int::TimeInt, Duration, Instant, Period}; use core::convert::TryFrom; +/// Potential `Clock` errors +#[non_exhaustive] +#[derive(Debug, Eq, PartialEq)] +pub enum Error { + /// specific implementation error + Other(E), +} + /// An abstraction for time-keeping items such as hardware timers pub trait Clock: Sized { /// The type to hold the tick count @@ -11,17 +19,20 @@ pub trait Clock: Sized { /// The duration of one clock tick in seconds, AKA the clock precision. const PERIOD: Period; + /// Implementation-specific error type + type ImplError: crate::Error; + /// Get the current Instant /// /// # Errors - /// - Error: The current instant was not readable - fn now(&self) -> Result, Error>; + /// Implementation-specific error returned through [`Error::Other(ImplError)`] + fn now(&self) -> Result, Error>; /// Blocking delay /// /// # Errors - /// - Error: The current instant was not readable, actual delay (if any) is unknown - fn delay(&self, dur: Dur) -> Result<(), Error> + /// Implementation-specific error returned through [`Error::Other(ImplError)`] + fn delay(&self, dur: Dur) -> Result<(), Error> where Self::Rep: TryFrom, { diff --git a/src/instant.rs b/src/instant.rs index c1a384d..381301f 100644 --- a/src/instant.rs +++ b/src/instant.rs @@ -38,7 +38,8 @@ impl Instant { /// type Rep = u32; /// const PERIOD: Period = ::new(1, 1_000); /// // ... - /// # fn now(&self) -> Result, embedded_time::Error> {unimplemented!()} + /// # type ImplError = (); + /// # fn now(&self) -> Result, embedded_time::clock::Error> {unimplemented!()} /// } /// /// assert_eq!(Instant::::new(5).duration_since::>(&Instant::::new(3)), @@ -74,7 +75,8 @@ impl Instant { /// type Rep = u32; /// const PERIOD: Period =::new(1, 1_000); /// // ... - /// # fn now(&self) -> Result, embedded_time::Error> {unimplemented!()} + /// # type ImplError = (); + /// # fn now(&self) -> Result, embedded_time::clock::Error> {unimplemented!()} /// } /// /// assert_eq!(Instant::::new(5).duration_until::>(&Instant::::new(7)), @@ -138,7 +140,8 @@ impl PartialOrd for Instant { /// type Rep = u32; /// const PERIOD: Period =::new(1, 1_000); /// // ... - /// # fn now(&self) -> Result, embedded_time::Error> {unimplemented!()} + /// # type ImplError = (); + /// # fn now(&self) -> Result, embedded_time::clock::Error> {unimplemented!()} /// } /// /// assert!(Instant::::new(5) > Instant::::new(3)); @@ -178,7 +181,8 @@ where /// type Rep = u32; /// const PERIOD: Period =::new(1, 1_000); /// // ... - /// # fn now(&self) -> Result, embedded_time::Error> {unimplemented!()} + /// # type ImplError = (); + /// # fn now(&self) -> Result, embedded_time::clock::Error> {unimplemented!()} /// } /// /// Instant::::new(1) + Seconds(u32::MAX); @@ -194,7 +198,8 @@ where /// type Rep = u32; /// const PERIOD: Period =::new(1, 1_000); /// // ... - /// # fn now(&self) -> Result, embedded_time::Error> {unimplemented!()} + /// # type ImplError = (); + /// # fn now(&self) -> Result, embedded_time::clock::Error> {unimplemented!()} /// } /// /// let _ = Instant::::new(0) + Milliseconds(i32::MAX as u32 + 1); @@ -211,7 +216,8 @@ where /// type Rep = u32; /// const PERIOD: Period =::new(1, 1_000); /// // ... - /// # fn now(&self) -> Result, embedded_time::Error> {unimplemented!()} + /// # type ImplError = (); + /// # fn now(&self) -> Result, embedded_time::clock::Error> {unimplemented!()} /// } /// /// assert_eq!(Instant::::new(1) + Seconds(3_u32), Instant::::new(3_001)); @@ -251,7 +257,8 @@ where /// type Rep = u32; /// const PERIOD: Period =::new(1, 1_000); /// // ... - /// # fn now(&self) -> Result, embedded_time::Error> {unimplemented!()} + /// # type ImplError = (); + /// # fn now(&self) -> Result, embedded_time::clock::Error> {unimplemented!()} /// } /// /// Instant::::new(1) - Seconds(u32::MAX); @@ -267,7 +274,8 @@ where /// type Rep = u32; /// const PERIOD: Period = ::new(1, 1_000); /// // ... - /// # fn now(&self) -> Result, embedded_time::Error> {unimplemented!()} + /// # type ImplError = (); + /// # fn now(&self) -> Result, embedded_time::clock::Error> {unimplemented!()} /// } /// /// let _ = Instant::::new(u32::MAX) - Milliseconds(i32::MAX as u32 + 1); @@ -284,7 +292,8 @@ where /// type Rep = u32; /// const PERIOD: Period =::new(1, 1_000); /// // ... - /// # fn now(&self) -> Result, embedded_time::Error> {unimplemented!()} + /// # type ImplError = (); + /// # fn now(&self) -> Result, embedded_time::clock::Error> {unimplemented!()} /// } /// /// assert_eq!(Instant::::new(800) - Milliseconds(700_u32), Instant::::new(100)); @@ -314,8 +323,9 @@ mod tests { impl time::Clock for Clock { type Rep = u32; const PERIOD: Period = ::new(1, 1_000); + type ImplError = (); - fn now(&self) -> Result, time::Error> { + fn now(&self) -> Result, time::clock::Error> { unimplemented!() } } diff --git a/src/lib.rs b/src/lib.rs index 5a3af98..78ba27a 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -45,8 +45,9 @@ //! impl embedded_time::Clock for SomeClock { //! type Rep = u64; //! const PERIOD: Period = ::new(1, 16_000_000); -//! -//! fn now(&self) -> Result, embedded_time::Error> { +//! type ImplError = (); +//! +//! fn now(&self) -> Result, embedded_time::clock::Error> { //! // ... //! # unimplemented!() //! } @@ -78,6 +79,7 @@ mod period; mod time_int; pub use clock::Clock; +use core::fmt; pub use duration::Duration; pub use instant::Instant; pub use period::Period; @@ -104,44 +106,27 @@ pub mod units { pub use crate::frequency::units::*; } -/// Time-related error -#[non_exhaustive] -#[derive(Debug, Eq, PartialEq)] -pub enum Error { - /// Communication with the clock failed - ClockInterfaceFailure, - /// The clock has not been started - ClockNotStarted, - /// Reading an `Instant` from the clock failed for some reason - UnableToReadFromClock, - /// Negative `Duration`s are not supported - NegativeDurationNotAllowed, - /// An integer overflow was detected - Overflow, - /// An integer underflow was detected - Underflow, - /// A divide-by-zero was detected - DivByZero, -} +/// An implementation-specific error +pub trait Error: fmt::Debug {} +impl Error for () {} #[cfg(test)] #[allow(unused_imports)] mod tests { - use crate as time; + use crate::{self as time, clock, traits::*, units::*}; use core::{ convert::TryFrom, convert::TryInto, fmt::{self, Formatter}, }; - use time::{traits::*, units::*}; struct MockClock64; - impl time::Clock for MockClock64 { type Rep = u64; const PERIOD: time::Period = ::new(1, 64_000_000); + type ImplError = ClockImplError; - fn now(&self) -> Result, time::Error> { + fn now(&self) -> Result, time::clock::Error> { Ok(time::Instant::new(128_000_000)) } } @@ -152,25 +137,34 @@ mod tests { impl time::Clock for MockClock32 { type Rep = u32; const PERIOD: time::Period = ::new(1, 16_000_000); + type ImplError = ClockImplError; - fn now(&self) -> Result, time::Error> { + fn now(&self) -> Result, time::clock::Error> { Ok(time::Instant::new(32_000_000)) } } + #[non_exhaustive] + #[derive(Debug, Eq, PartialEq)] + pub enum ClockImplError { + NotStarted, + } + impl crate::Error for ClockImplError {} + #[derive(Debug)] struct BadClock; impl time::Clock for BadClock { type Rep = u32; const PERIOD: time::Period = time::Period::new(1, 16_000_000); + type ImplError = ClockImplError; - fn now(&mut self) -> Result, time::Error> { - Err(time::Error::UnableToReadFromClock) + fn now(&self) -> Result, time::clock::Error> { + Err(time::clock::Error::Other(ClockImplError::NotStarted)) } } - fn get_time(clock: &mut Clock) + fn get_time(clock: &Clock) where u32: TryFrom, Clock::Rep: TryFrom, @@ -186,11 +180,11 @@ mod tests { let then = MockClock32.now().unwrap(); let now = MockClock32.now().unwrap(); - let mut clock64 = MockClock64 {}; - let mut clock32 = MockClock32 {}; + let clock64 = MockClock64 {}; + let clock32 = MockClock32 {}; - get_time(&mut clock64); - get_time(&mut clock32); + get_time(&clock64); + get_time(&clock32); let then = then - Seconds(1_u32); assert_ne!(then, now); @@ -199,7 +193,10 @@ mod tests { #[test] fn clock_error() { - assert_eq!(BadClock.now(), Err(time::Error::UnableToReadFromClock)); + assert_eq!( + BadClock.now(), + Err(time::clock::Error::Other(ClockImplError::NotStarted)) + ); } struct Timestamp(time::Instant) From c47b8ef9ecfa0c15af4d9f0eaa19f5fde65cd4ed Mon Sep 17 00:00:00 2001 From: Peter Taylor Date: Thu, 2 Jul 2020 15:09:19 -0600 Subject: [PATCH 12/23] feat: Add `Infallible` error type --- examples/nrf52_dk/main.rs | 3 ++- src/lib.rs | 10 +++++----- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/examples/nrf52_dk/main.rs b/examples/nrf52_dk/main.rs index 82410c4..a450522 100644 --- a/examples/nrf52_dk/main.rs +++ b/examples/nrf52_dk/main.rs @@ -3,6 +3,7 @@ extern crate panic_rtt; +use core::convert::Infallible; use cortex_m_rt::entry; use embedded_time::{self as time, traits::*}; @@ -37,7 +38,7 @@ impl SysClock { impl time::Clock for SysClock { type Rep = u64; const PERIOD: time::Period = ::new(1, 16_000_000); - type ImplError = (); + type ImplError = Infallible; fn now(&self) -> Result, time::clock::Error> { self.capture_task.tasks_trigger[0].write(|write| unsafe { write.bits(1) }); diff --git a/src/lib.rs b/src/lib.rs index 78ba27a..e8c9f1e 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -79,7 +79,7 @@ mod period; mod time_int; pub use clock::Clock; -use core::fmt; +use core::{convert::Infallible, fmt}; pub use duration::Duration; pub use instant::Instant; pub use period::Period; @@ -109,14 +109,14 @@ pub mod units { /// An implementation-specific error pub trait Error: fmt::Debug {} impl Error for () {} +impl Error for Infallible {} #[cfg(test)] #[allow(unused_imports)] mod tests { use crate::{self as time, clock, traits::*, units::*}; use core::{ - convert::TryFrom, - convert::TryInto, + convert::{Infallible, TryFrom, TryInto}, fmt::{self, Formatter}, }; @@ -124,7 +124,7 @@ mod tests { impl time::Clock for MockClock64 { type Rep = u64; const PERIOD: time::Period = ::new(1, 64_000_000); - type ImplError = ClockImplError; + type ImplError = Infallible; fn now(&self) -> Result, time::clock::Error> { Ok(time::Instant::new(128_000_000)) @@ -137,7 +137,7 @@ mod tests { impl time::Clock for MockClock32 { type Rep = u32; const PERIOD: time::Period = ::new(1, 16_000_000); - type ImplError = ClockImplError; + type ImplError = Infallible; fn now(&self) -> Result, time::clock::Error> { Ok(time::Instant::new(32_000_000)) From 407addde610cdbe08818e1474f22483e48c1e7a6 Mon Sep 17 00:00:00 2001 From: Peter Taylor Date: Thu, 25 Jun 2020 13:13:53 -0600 Subject: [PATCH 13/23] Add `Timer` type --- CHANGELOG.md | 6 +- src/clock.rs | 8 +- src/lib.rs | 4 + src/timer.rs | 257 +++++++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 272 insertions(+), 3 deletions(-) create mode 100644 src/timer.rs diff --git a/CHANGELOG.md b/CHANGELOG.md index 11fc3a1..dcde591 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,7 +4,9 @@ ### Added -- Add fallibility to `Clock` methods +- A `Timer` type supporting one-shot and periodic software timers utilizing a `Clock` implementation +- `Timer` unit tests +- Fallibility to `Clock` methods - `Instant::duration_until()` with order checking - Order checking to `Instant::duration_since()` - Bounds checking on `Instant` impls of Add/Sub @@ -12,7 +14,7 @@ ### Changed -- Add `&mut self` to `Clock` functions (make stateful, or at least allow stateful implementations) +- Add `&self` to `Clock` functions (make stateful, or at least allow stateful implementations) - All time-type inner types from signed to unsigned - `Instant::duration_since()` return type to `Result` - Refactor `examples/nrf52_dk` diff --git a/src/clock.rs b/src/clock.rs index 4fcc263..4ceb23c 100644 --- a/src/clock.rs +++ b/src/clock.rs @@ -1,6 +1,7 @@ //! The `Clock` trait can be implemented over hardware timers or other time-keeping device -use crate::{time_int::TimeInt, Duration, Instant, Period}; +use crate::timer::param; +use crate::{time_int::TimeInt, Duration, Instant, Period, Timer}; use core::convert::TryFrom; /// Potential `Clock` errors @@ -41,4 +42,9 @@ pub trait Clock: Sized { while self.now()? < end {} Ok(()) } + + /// Spawn a new, `OneShot` [`Timer`] from this clock + fn new_timer(&self) -> Timer { + Timer::::new(&self) + } } diff --git a/src/lib.rs b/src/lib.rs index e8c9f1e..83c2e5f 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -26,6 +26,8 @@ //! **Wrapping Clock**: A clock that when at its maximum value, the next count is the minimum //! value. //! +//! **Timer**: An entity that counts toward an expiration. +//! //! **Instant**: A specific instant in time ("time-point") read from a clock. //! //! **Duration**: The difference of two instances. The time that has elapsed since an instant. A @@ -77,6 +79,7 @@ mod frequency; mod instant; mod period; mod time_int; +mod timer; pub use clock::Clock; use core::{convert::Infallible, fmt}; @@ -84,6 +87,7 @@ pub use duration::Duration; pub use instant::Instant; pub use period::Period; pub use time_int::TimeInt; +pub use timer::Timer; /// Public _traits_ /// diff --git a/src/timer.rs b/src/timer.rs new file mode 100644 index 0000000..6a83c3c --- /dev/null +++ b/src/timer.rs @@ -0,0 +1,257 @@ +use crate::timer::param::*; +use crate::{traits::*, Duration, Instant}; +use core::{marker::PhantomData, ops::Add, prelude::v1::*}; + +pub(crate) mod param { + #[derive(Debug)] + pub struct None; + + #[derive(Debug)] + pub struct Disarmed; + + #[derive(Debug)] + pub struct Armed; + + #[derive(Debug)] + pub struct Running; + + #[derive(Debug)] + pub struct Periodic; + + #[derive(Debug)] + pub struct OneShot; +} + +/// A `Timer` counts toward an expiration, can be polled for elapsed and remaining time, as +/// well as optionally execute a task upon expiration. +#[derive(Debug)] +pub struct Timer<'a, Type, State, Clock: crate::Clock, Dur: Duration> { + clock: &'a Clock, + duration: Option, + expiration: Option>, + _type: PhantomData, + _state: PhantomData, +} + +impl<'a, Clock: crate::Clock, Dur: Duration> Timer<'_, param::None, param::None, Clock, Dur> { + /// Construct a new, `OneShot` `Timer` + #[allow(clippy::new_ret_no_self)] + pub fn new(clock: &Clock) -> Timer { + Timer:: { + clock, + duration: Option::None, + expiration: Option::None, + _type: PhantomData, + _state: PhantomData, + } + } +} + +impl<'a, Type, State, Clock: crate::Clock, Dur: Duration> Timer<'a, Type, State, Clock, Dur> { + /// Change timer type to one-shot + pub fn into_oneshot(self) -> Timer<'a, OneShot, State, Clock, Dur> { + Timer:: { + clock: self.clock, + duration: self.duration, + expiration: self.expiration, + _type: PhantomData, + _state: PhantomData, + } + } + + /// Change timer type into periodic + pub fn into_periodic(self) -> Timer<'a, Periodic, State, Clock, Dur> { + Timer:: { + clock: self.clock, + duration: self.duration, + expiration: self.expiration, + _type: PhantomData, + _state: PhantomData, + } + } +} + +impl<'a, Type, Clock: crate::Clock, Dur: Duration> Timer<'a, Type, Disarmed, Clock, Dur> { + /// Set the [`Duration`](trait.Duration.html) of the timer + /// + /// This _arms_ the timer (makes it ready to run). + pub fn set_duration(self, duration: Dur) -> Timer<'a, Type, Armed, Clock, Dur> { + Timer:: { + clock: self.clock, + duration: Some(duration), + expiration: Option::None, + _type: PhantomData, + _state: PhantomData, + } + } +} +impl<'a, Type, Clock: crate::Clock, Dur: Duration> Timer<'a, Type, Armed, Clock, Dur> { + /// Start the _armed_ timer from this instant + pub fn start(self) -> Timer<'a, Type, Running, Clock, Dur> + where + Instant: Add>, + { + Timer:: { + clock: self.clock, + duration: self.duration, + expiration: Some(self.clock.now().unwrap() + self.duration.unwrap()), + _type: PhantomData, + _state: PhantomData, + } + } +} + +impl Timer<'_, Type, Running, Clock, Dur> { + fn _is_expired(&self) -> bool { + self.clock.now().unwrap() >= self.expiration.unwrap() + } +} + +impl<'a, Clock: crate::Clock, Dur: Duration> Timer<'a, OneShot, Running, Clock, Dur> { + /// Block until the timer has expired + pub fn wait(self) { + // since the timer is running, _is_expired() will return a value + while !self._is_expired() {} + } + + /// Check whether the timer has expired + /// + /// The timer is not restarted + pub fn is_expired(&self) -> bool { + self._is_expired() + } +} + +impl Timer<'_, Periodic, Running, Clock, Dur> { + /// Block until the timer has expired + /// + /// The timer is restarted + pub fn wait(self) -> Self + where + Instant: Add>, + { + // since the timer is running, _is_expired() will return a value + while !self._is_expired() {} + + Self { + clock: self.clock, + duration: self.duration, + expiration: self + .expiration + .map(|expiration| expiration + self.duration.unwrap()), + _type: PhantomData, + _state: PhantomData, + } + } + + /// Check whether a _periodic_ timer has elapsed + /// + /// The timer is restarted if it has elapsed. + pub fn period_complete(&mut self) -> bool + where + Instant: Add>, + { + // since the timer is running, _is_expired() will return a value + if self._is_expired() { + self.expiration = Some(self.expiration.unwrap() + self.duration.unwrap()); + + true + } else { + false + } + } +} + +#[cfg(test)] +#[allow(unused_imports)] +#[allow(unsafe_code)] +mod test { + use crate::{traits::*, units::*, Duration, Error, Instant, Period}; + use std::convert::{Infallible, TryFrom, TryInto}; + + #[derive(Debug, Ord, PartialOrd, Eq, PartialEq)] + struct Clock; + + static mut START: Option = None; + + impl crate::Clock for Clock { + type Rep = u64; + const PERIOD: Period = ::new(1, 64_000_000); + type ImplError = Infallible; + + fn now(&self) -> Result, crate::clock::Error> { + let since_start = unsafe { START.unwrap() }.elapsed(); + let ticks = Nanoseconds::::try_from(since_start) + .unwrap() + .into_ticks(Self::PERIOD) + .unwrap(); + Ok(Instant::new(ticks)) + } + } + + fn init_start_time() { + unsafe { + START = Some(std::time::Instant::now()); + } + } + + #[test] + fn oneshot_wait() { + init_start_time(); + let clock = Clock; + + clock + .new_timer() + .set_duration(1_u32.seconds()) + .start() + .wait(); + + unsafe { + assert!(Seconds::::try_from(START.unwrap().elapsed()).unwrap() == 1_u32.seconds()); + } + } + + #[test] + fn periodic_wait() { + init_start_time(); + let clock = Clock; + + let timer = clock + .new_timer() + .into_periodic() + .set_duration(1_u32.seconds()) + .start() + .wait(); + + unsafe { + assert!(Seconds::::try_from(START.unwrap().elapsed()).unwrap() == 1_u32.seconds()); + } + + let timer = timer.wait(); + unsafe { + assert!(Seconds::::try_from(START.unwrap().elapsed()).unwrap() == 2_u32.seconds()); + } + + let timer = timer.wait(); + unsafe { + assert!(Seconds::::try_from(START.unwrap().elapsed()).unwrap() == 3_u32.seconds()); + } + } + + #[test] + fn periodic_expiration() { + init_start_time(); + let clock = Clock; + + let mut timer = clock + .new_timer() + .into_periodic() + .set_duration(1_u32.seconds()) + .start(); + + std::thread::sleep(std::time::Duration::from_secs(2)); + + assert!(timer.period_complete()); + assert!(timer.period_complete()); + } +} From e6883ced9db5cc204a6ccad4afa91758da9ae229 Mon Sep 17 00:00:00 2001 From: Peter Taylor Date: Sun, 28 Jun 2020 21:01:05 -0600 Subject: [PATCH 14/23] refactor(Clock): Remove `Clock::delay()` This functionality is now handled by `Timer`. BREAKING CHANGE: `Clock::delay()` replaced with `Timer::wait()` --- examples/nrf52_dk/main.rs | 12 ++++++++++-- src/clock.rs | 15 --------------- 2 files changed, 10 insertions(+), 17 deletions(-) diff --git a/examples/nrf52_dk/main.rs b/examples/nrf52_dk/main.rs index a450522..e85144e 100644 --- a/examples/nrf52_dk/main.rs +++ b/examples/nrf52_dk/main.rs @@ -152,12 +152,20 @@ where led2.set_high()?; led3.set_high()?; led4.set_low()?; - clock.delay(250_u32.milliseconds()).unwrap(); + clock + .new_timer() + .set_duration(250_u32.milliseconds()) + .start() + .wait(); led1.set_high()?; led2.set_low()?; led3.set_low()?; led4.set_high()?; - clock.delay(250_u32.milliseconds()).unwrap(); + clock + .new_timer() + .set_duration(250_u32.milliseconds()) + .start() + .wait(); } } diff --git a/src/clock.rs b/src/clock.rs index 4ceb23c..fd3e5e1 100644 --- a/src/clock.rs +++ b/src/clock.rs @@ -2,7 +2,6 @@ use crate::timer::param; use crate::{time_int::TimeInt, Duration, Instant, Period, Timer}; -use core::convert::TryFrom; /// Potential `Clock` errors #[non_exhaustive] @@ -29,20 +28,6 @@ pub trait Clock: Sized { /// Implementation-specific error returned through [`Error::Other(ImplError)`] fn now(&self) -> Result, Error>; - /// Blocking delay - /// - /// # Errors - /// Implementation-specific error returned through [`Error::Other(ImplError)`] - fn delay(&self, dur: Dur) -> Result<(), Error> - where - Self::Rep: TryFrom, - { - let start = self.now()?; - let end = start + dur; - while self.now()? < end {} - Ok(()) - } - /// Spawn a new, `OneShot` [`Timer`] from this clock fn new_timer(&self) -> Timer { Timer::::new(&self) From d5a5ba23f9acb9eb65cbcff59fdbdf5bff6230b3 Mon Sep 17 00:00:00 2001 From: Peter Taylor Date: Sun, 28 Jun 2020 21:03:08 -0600 Subject: [PATCH 15/23] feat(Timer): Add functions to read elapsed and remaining time from a timer docs(Clock,Timer): Remove links to private items --- src/timer.rs | 83 ++++++++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 74 insertions(+), 9 deletions(-) diff --git a/src/timer.rs b/src/timer.rs index 6a83c3c..6e97cbb 100644 --- a/src/timer.rs +++ b/src/timer.rs @@ -1,6 +1,5 @@ -use crate::timer::param::*; -use crate::{traits::*, Duration, Instant}; -use core::{marker::PhantomData, ops::Add, prelude::v1::*}; +use crate::{duration::TryConvertFrom, timer::param::*, traits::*, units::*, Duration, Instant}; +use core::{convert::TryFrom, marker::PhantomData, ops::Add, prelude::v1::*}; pub(crate) mod param { #[derive(Debug)] @@ -105,13 +104,53 @@ impl Timer<'_, Type, Running, Clock, D fn _is_expired(&self) -> bool { self.clock.now().unwrap() >= self.expiration.unwrap() } + + /// Returns the [`Duration`](trait.Duration.html) of time elapsed since it was started + /// + /// The units of the [`Duration`](trait.Duration.html) are the same as that used with + /// [`set_duration()`](struct.Timer.html#method.set_duration). + pub fn elapsed(&self) -> Dur + where + Dur::Rep: TryFrom, + Clock::Rep: TryFrom, + { + self.clock + .now() + .unwrap() + .duration_since(&(self.expiration.unwrap() - self.duration.unwrap())) + .unwrap() + } + + /// Returns the [`Duration`](trait.Duration.html) until the expiration of the timer + /// + /// The units of the [`Duration`](trait.Duration.html) are the same as that used with + /// [`set_duration()`](struct.Timer.html#method.set_duration). + pub fn remaining(&self) -> Dur + where + Dur::Rep: TryFrom, + Clock::Rep: TryFrom, + Dur: TryConvertFrom>, + { + if let Ok(duration) = self + .expiration + .unwrap() + .duration_since(&self.clock.now().unwrap()) + { + duration + } else { + 0.seconds().try_convert_into().unwrap() + } + } } impl<'a, Clock: crate::Clock, Dur: Duration> Timer<'a, OneShot, Running, Clock, Dur> { /// Block until the timer has expired - pub fn wait(self) { + pub fn wait(self) -> Timer<'a, OneShot, Armed, Clock, Dur> { // since the timer is running, _is_expired() will return a value while !self._is_expired() {} + + Timer::::new(self.clock) + .set_duration(self.duration.unwrap()) } /// Check whether the timer has expired @@ -207,7 +246,7 @@ mod test { .wait(); unsafe { - assert!(Seconds::::try_from(START.unwrap().elapsed()).unwrap() == 1_u32.seconds()); + assert!(Seconds::::try_from(START.unwrap().elapsed()).unwrap() >= 1_u32.seconds()); } } @@ -224,17 +263,17 @@ mod test { .wait(); unsafe { - assert!(Seconds::::try_from(START.unwrap().elapsed()).unwrap() == 1_u32.seconds()); + assert!(Seconds::::try_from(START.unwrap().elapsed()).unwrap() >= 1_u32.seconds()); } let timer = timer.wait(); unsafe { - assert!(Seconds::::try_from(START.unwrap().elapsed()).unwrap() == 2_u32.seconds()); + assert!(Seconds::::try_from(START.unwrap().elapsed()).unwrap() >= 2_u32.seconds()); } - let timer = timer.wait(); + timer.wait(); unsafe { - assert!(Seconds::::try_from(START.unwrap().elapsed()).unwrap() == 3_u32.seconds()); + assert!(Seconds::::try_from(START.unwrap().elapsed()).unwrap() >= 3_u32.seconds()); } } @@ -254,4 +293,30 @@ mod test { assert!(timer.period_complete()); assert!(timer.period_complete()); } + + #[test] + fn read_timer() { + init_start_time(); + let clock = Clock; + + let timer = clock.new_timer().set_duration(2_u32.seconds()).start(); + + assert_eq!(timer.elapsed(), 0_u32.seconds()); + assert_eq!(timer.remaining(), 1_u32.seconds()); + + std::thread::sleep(std::time::Duration::from_secs(1)); + + assert_eq!(timer.elapsed(), 1_u32.seconds()); + assert_eq!(timer.remaining(), 0_u32.seconds()); + + std::thread::sleep(std::time::Duration::from_secs(1)); + + assert_eq!(timer.elapsed(), 2_u32.seconds()); + assert_eq!(timer.remaining(), 0_u32.seconds()); + + std::thread::sleep(std::time::Duration::from_secs(1)); + + assert_eq!(timer.elapsed(), 3_u32.seconds()); + assert_eq!(timer.remaining(), 0_u32.seconds()); + } } From 24ba400bdf59a6d2606f9d91fdbf741f257bf57c Mon Sep 17 00:00:00 2001 From: Peter Taylor Date: Mon, 29 Jun 2020 14:46:47 -0600 Subject: [PATCH 16/23] feat(Timer): Add return from OneShot Timer::wait() Return a TimerBuilder --- src/timer.rs | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/timer.rs b/src/timer.rs index 6e97cbb..2c4997d 100644 --- a/src/timer.rs +++ b/src/timer.rs @@ -239,7 +239,7 @@ mod test { init_start_time(); let clock = Clock; - clock + let timer = clock .new_timer() .set_duration(1_u32.seconds()) .start() @@ -248,6 +248,14 @@ mod test { unsafe { assert!(Seconds::::try_from(START.unwrap().elapsed()).unwrap() >= 1_u32.seconds()); } + + // WHEN blocking on a timer + timer.start().wait(); + + // THEN the block occurs for _at least_ the given duration + unsafe { + assert!(Seconds::::try_from(START.unwrap().elapsed()).unwrap() >= 2_u32.seconds()); + } } #[test] From 8b7fe88b17143f6f41ca33c7661eede652569ddf Mon Sep 17 00:00:00 2001 From: Peter Taylor Date: Mon, 29 Jun 2020 17:08:21 -0600 Subject: [PATCH 17/23] test(Timer): Stabilize tests Switch from using system clock for delays to manually advancing clock ticks using multiple threads --- Cargo.lock | 24 +++++++++++ Cargo.toml | 3 ++ src/timer.rs | 119 ++++++++++++++++++++++++++++----------------------- 3 files changed, 93 insertions(+), 53 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 050557d..0d34995 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -30,6 +30,12 @@ dependencies = [ "rustc_version", ] +[[package]] +name = "cfg-if" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822" + [[package]] name = "cortex-m" version = "0.6.2" @@ -62,6 +68,17 @@ dependencies = [ "syn", ] +[[package]] +name = "crossbeam-utils" +version = "0.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3c7c73a2d1e9fc0886a08b93e98eb643461230d5f1925e4036204d5f2e261a8" +dependencies = [ + "autocfg", + "cfg-if", + "lazy_static", +] + [[package]] name = "embedded-hal" version = "0.2.4" @@ -78,6 +95,7 @@ version = "0.5.2" dependencies = [ "cortex-m", "cortex-m-rt", + "crossbeam-utils", "mutex-trait", "nrf52832-hal", "num", @@ -100,6 +118,12 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "42f6af40154f64bd7095b5245d6933fef99b0a28c432b38a676cfafaa0aedfc2" +[[package]] +name = "lazy_static" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" + [[package]] name = "mutex-trait" version = "0.2.0" diff --git a/Cargo.toml b/Cargo.toml index 91319f3..437bcce 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -22,5 +22,8 @@ panic_rtt = "0.3.0" nrf52832-hal = { version = "0.10.0", default-features = false, features = ["rt", "xxAA-package"] } mutex-trait = "0.2.0" +[target.'cfg(not(target_arch = "arm"))'.dev-dependencies] +crossbeam-utils = "0.7.2" + [patch.crates-io] cortex-m = { git = "https://github.com/rust-embedded/cortex-m", branch = "mutex_add" } diff --git a/src/timer.rs b/src/timer.rs index 2c4997d..b23f150 100644 --- a/src/timer.rs +++ b/src/timer.rs @@ -206,88 +206,88 @@ impl Timer<'_, Periodic, Running, Clock, Dur #[allow(unsafe_code)] mod test { use crate::{traits::*, units::*, Duration, Error, Instant, Period}; - use std::convert::{Infallible, TryFrom, TryInto}; + use core::convert::{Infallible, TryFrom}; + use crossbeam_utils::thread; + use std::sync::atomic::{AtomicU64, Ordering}; - #[derive(Debug, Ord, PartialOrd, Eq, PartialEq)] - struct Clock; - - static mut START: Option = None; + static TICKS: AtomicU64 = AtomicU64::new(0); + #[derive(Debug)] + struct Clock; impl crate::Clock for Clock { type Rep = u64; - const PERIOD: Period = ::new(1, 64_000_000); + const PERIOD: Period = ::new(1, 1_000); type ImplError = Infallible; fn now(&self) -> Result, crate::clock::Error> { - let since_start = unsafe { START.unwrap() }.elapsed(); - let ticks = Nanoseconds::::try_from(since_start) - .unwrap() - .into_ticks(Self::PERIOD) - .unwrap(); - Ok(Instant::new(ticks)) - } - } - - fn init_start_time() { - unsafe { - START = Some(std::time::Instant::now()); + Ok(Instant::new(TICKS.load(Ordering::Acquire))) } } #[test] fn oneshot_wait() { - init_start_time(); + init_ticks(); let clock = Clock; - let timer = clock - .new_timer() - .set_duration(1_u32.seconds()) - .start() - .wait(); + let timer = clock.new_timer().set_duration(1_u32.seconds()).start(); - unsafe { - assert!(Seconds::::try_from(START.unwrap().elapsed()).unwrap() >= 1_u32.seconds()); - } + thread::scope(|s| { + let timer_handle = s.spawn(|_| timer.wait()); - // WHEN blocking on a timer - timer.start().wait(); + add_to_ticks(1_u32.seconds()); - // THEN the block occurs for _at least_ the given duration - unsafe { - assert!(Seconds::::try_from(START.unwrap().elapsed()).unwrap() >= 2_u32.seconds()); - } + let result = timer_handle.join(); + + assert!(result.is_ok()); + + add_to_ticks(1_u32.seconds()); + + let timer = result.unwrap().start(); + assert!(!timer.is_expired()); + + let timer_handle = s.spawn(|_| timer.wait()); + add_to_ticks(1_u32.seconds()); + + assert!(timer_handle.join().is_ok()); + }) + .unwrap(); } #[test] fn periodic_wait() { - init_start_time(); + init_ticks(); let clock = Clock; let timer = clock .new_timer() .into_periodic() .set_duration(1_u32.seconds()) - .start() - .wait(); + .start(); - unsafe { - assert!(Seconds::::try_from(START.unwrap().elapsed()).unwrap() >= 1_u32.seconds()); - } + thread::scope(|s| { + let timer_handle = s.spawn(|_| timer.wait()); - let timer = timer.wait(); - unsafe { - assert!(Seconds::::try_from(START.unwrap().elapsed()).unwrap() >= 2_u32.seconds()); - } + add_to_ticks(1_u32.seconds()); - timer.wait(); - unsafe { - assert!(Seconds::::try_from(START.unwrap().elapsed()).unwrap() >= 3_u32.seconds()); - } + let result = timer_handle.join(); + + assert!(result.is_ok()); + + let timer = result.unwrap(); + + // WHEN blocking on a timer + let timer_handle = s.spawn(|_| timer.wait()); + + add_to_ticks(1_u32.seconds()); + + assert!(timer_handle.join().is_ok()); + }) + .unwrap(); } #[test] fn periodic_expiration() { - init_start_time(); + init_ticks(); let clock = Clock; let mut timer = clock @@ -296,7 +296,7 @@ mod test { .set_duration(1_u32.seconds()) .start(); - std::thread::sleep(std::time::Duration::from_secs(2)); + add_to_ticks(2_u32.seconds()); assert!(timer.period_complete()); assert!(timer.period_complete()); @@ -304,27 +304,40 @@ mod test { #[test] fn read_timer() { - init_start_time(); + init_ticks(); let clock = Clock; let timer = clock.new_timer().set_duration(2_u32.seconds()).start(); + add_to_ticks(1_u32.milliseconds()); + assert_eq!(timer.elapsed(), 0_u32.seconds()); assert_eq!(timer.remaining(), 1_u32.seconds()); - std::thread::sleep(std::time::Duration::from_secs(1)); + add_to_ticks(1_u32.seconds()); assert_eq!(timer.elapsed(), 1_u32.seconds()); assert_eq!(timer.remaining(), 0_u32.seconds()); - std::thread::sleep(std::time::Duration::from_secs(1)); + add_to_ticks(1_u32.seconds()); assert_eq!(timer.elapsed(), 2_u32.seconds()); assert_eq!(timer.remaining(), 0_u32.seconds()); - std::thread::sleep(std::time::Duration::from_secs(1)); + add_to_ticks(1_u32.seconds()); assert_eq!(timer.elapsed(), 3_u32.seconds()); assert_eq!(timer.remaining(), 0_u32.seconds()); } + + fn init_ticks() {} + + fn add_to_ticks(duration: Dur) { + let ticks = TICKS.load(Ordering::Acquire); + let ticks = ticks + + duration + .into_ticks::<::Rep>(Clock::PERIOD) + .unwrap(); + TICKS.store(ticks, Ordering::Release); + } } From 665f868f6cc5adc02fdbd0adcd841acb43843dd2 Mon Sep 17 00:00:00 2001 From: Peter Taylor Date: Fri, 3 Jul 2020 10:39:53 -0600 Subject: [PATCH 18/23] refactor(duration): Public Interface - Make `duration` mod public - Remove re-export of `Duration` trait (but it's still public) BREAKING CHANGE: The `Duration` trait access changed from `embedded_time::Duration` to `embedded_time::duration::Duration` --- src/clock.rs | 6 ++- src/instant.rs | 2 +- src/lib.rs | 12 +++--- src/numeric_constructor.rs | 83 +++++++++++++++++++++++++++++++++++++ src/time_int.rs | 85 +------------------------------------- src/timer.rs | 6 ++- 6 files changed, 100 insertions(+), 94 deletions(-) create mode 100644 src/numeric_constructor.rs diff --git a/src/clock.rs b/src/clock.rs index fd3e5e1..d1aee2e 100644 --- a/src/clock.rs +++ b/src/clock.rs @@ -1,7 +1,9 @@ //! The `Clock` trait can be implemented over hardware timers or other time-keeping device -use crate::timer::param; -use crate::{time_int::TimeInt, Duration, Instant, Period, Timer}; +use crate::{ + duration::Duration, instant::Instant, period::Period, time_int::TimeInt, timer::param, + timer::Timer, +}; /// Potential `Clock` errors #[non_exhaustive] diff --git a/src/instant.rs b/src/instant.rs index 381301f..a5d964a 100644 --- a/src/instant.rs +++ b/src/instant.rs @@ -1,6 +1,6 @@ //! An instant of time -use crate::Duration; +use crate::duration::Duration; use core::{cmp::Ordering, convert::TryFrom, ops}; use num::traits::{WrappingAdd, WrappingSub}; diff --git a/src/lib.rs b/src/lib.rs index 83c2e5f..3a22a7b 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -15,8 +15,8 @@ //! [`Seconds`]: units::Seconds //! [`Milliseconds`]: units::Milliseconds //! [`Hertz`]: units::Hertz -//! [`Duration`]: trait.Duration.html -//! [`Clock`]: trait.Clock.html +//! [`Duration`]: duration/trait.Duration.html +//! [`Duration::count()`]: duration/trait.Duration.html#tymethod.count //! //! ## Definitions //! **Clock**: Any entity that periodically counts (ie a hardware timer/counter peripheral). @@ -74,19 +74,19 @@ #![deny(intra_doc_link_resolution_failure)] pub mod clock; -mod duration; +pub mod duration; mod frequency; mod instant; +mod numeric_constructor; mod period; mod time_int; mod timer; pub use clock::Clock; use core::{convert::Infallible, fmt}; -pub use duration::Duration; pub use instant::Instant; pub use period::Period; -pub use time_int::TimeInt; +pub(crate) use time_int::TimeInt; pub use timer::Timer; /// Public _traits_ @@ -100,7 +100,7 @@ pub mod traits { pub use crate::duration::Duration as _; pub use crate::duration::TryConvertFrom as _; pub use crate::duration::TryConvertInto as _; - pub use crate::time_int::NumericConstructor as _; + pub use crate::numeric_constructor::NumericConstructor as _; pub use crate::time_int::TimeInt as _; } diff --git a/src/numeric_constructor.rs b/src/numeric_constructor.rs new file mode 100644 index 0000000..dfea9ed --- /dev/null +++ b/src/numeric_constructor.rs @@ -0,0 +1,83 @@ +//! Construction of time-based types from integers + +use crate::{duration::units::*, frequency::units::*, time_int::TimeInt}; + +/// Create time-based values from primitive and core numeric types. +/// +/// This trait is anonomously re-exported in [`traits`](crate::traits) +/// +/// # Examples +/// Basic construction of time-based values. +/// ```rust +/// # use embedded_time::{traits::*, units::*}; +/// assert_eq!(5_u32.nanoseconds(), Nanoseconds(5_u32)); +/// assert_eq!(5_u32.microseconds(), Microseconds(5_u32)); +/// assert_eq!(5_u32.milliseconds(), Milliseconds(5_u32)); +/// assert_eq!(5_u32.seconds(), Seconds(5_u32)); +/// assert_eq!(5_u32.minutes(), Minutes(5_u32)); +/// assert_eq!(5_u32.hours(), Hours(5_u32)); +/// +/// assert_eq!(5_u32.hertz(), Hertz(5_u32)); +/// ``` +pub trait NumericConstructor: TimeInt { + /// Construct the duration implementation + fn nanoseconds(self) -> Nanoseconds; + /// Construct the duration implementation + fn microseconds(self) -> Microseconds; + /// Construct the duration implementation + fn milliseconds(self) -> Milliseconds; + /// Construct the duration implementation + fn seconds(self) -> Seconds; + /// Construct the duration implementation + fn minutes(self) -> Minutes; + /// Construct the duration implementation + fn hours(self) -> Hours; + + /// Construct the frequency type + fn hertz(self) -> Hertz; +} + +macro_rules! impl_numeric_constructors { + ($($type:ty),* $(,)?) => { + $( + impl NumericConstructor for $type { + #[inline(always)] + fn nanoseconds(self) -> Nanoseconds<$type> { + Nanoseconds(self) + } + + #[inline(always)] + fn microseconds(self) -> Microseconds<$type> { + Microseconds(self) + } + + #[inline(always)] + fn milliseconds(self) -> Milliseconds<$type> { + Milliseconds(self) + } + + #[inline(always)] + fn seconds(self) -> Seconds<$type> { + Seconds(self) + } + + #[inline(always)] + fn minutes(self) -> Minutes<$type> { + Minutes(self) + } + + #[inline(always)] + fn hours(self) -> Hours<$type> { + Hours(self) + } + + #[inline(always)] + fn hertz(self) -> Hertz<$type> { + Hertz(self) + } + } + )* + }; + } + +impl_numeric_constructors![u32, u64]; diff --git a/src/time_int.rs b/src/time_int.rs index f36d6f7..de87bb9 100644 --- a/src/time_int.rs +++ b/src/time_int.rs @@ -1,24 +1,7 @@ -use crate::frequency::units::Hertz; -use crate::{duration::units::*, Period}; +use crate::Period; use core::{convert::TryFrom, convert::TryInto, fmt}; -/// Create time-based values from primitive and core numeric types. -/// -/// This trait can be imported with `use embedded-time::prelude::*`. -/// -/// # Examples -/// Basic construction of time-based values. -/// ```rust -/// # use embedded_time::{traits::*, units::*}; -/// assert_eq!(5_u32.nanoseconds(), Nanoseconds(5_u32)); -/// assert_eq!(5_u32.microseconds(), Microseconds(5_u32)); -/// assert_eq!(5_u32.milliseconds(), Milliseconds(5_u32)); -/// assert_eq!(5_u32.seconds(), Seconds(5_u32)); -/// assert_eq!(5_u32.minutes(), Minutes(5_u32)); -/// assert_eq!(5_u32.hours(), Hours(5_u32)); -/// -/// assert_eq!(5_u32.hertz(), Hertz(5_u32)); -/// ``` +/// The core inner-type trait for time-related types pub trait TimeInt: Copy + num::Integer @@ -64,70 +47,6 @@ pub trait TimeInt: } } -/// Construct a time-related type with suffix units -pub trait NumericConstructor: TimeInt { - /// Construct the [`Duration`](crate::duration::Duration) implementation - fn nanoseconds(self) -> Nanoseconds; - /// Construct the [`Duration`](crate::duration::Duration) implementation - fn microseconds(self) -> Microseconds; - /// Construct the [`Duration`](crate::duration::Duration) implementation - fn milliseconds(self) -> Milliseconds; - /// Construct the [`Duration`](crate::duration::Duration) implementation - fn seconds(self) -> Seconds; - /// Construct the [`Duration`](crate::duration::Duration) implementation - fn minutes(self) -> Minutes; - /// Construct the [`Duration`](crate::duration::Duration) implementation - fn hours(self) -> Hours; - - /// Construct the frequency type - fn hertz(self) -> Hertz; -} - -macro_rules! impl_numeric_constructors { - ($($type:ty),* $(,)?) => { - $( - impl NumericConstructor for $type { - #[inline(always)] - fn nanoseconds(self) -> Nanoseconds<$type> { - Nanoseconds(self) - } - - #[inline(always)] - fn microseconds(self) -> Microseconds<$type> { - Microseconds(self) - } - - #[inline(always)] - fn milliseconds(self) -> Milliseconds<$type> { - Milliseconds(self) - } - - #[inline(always)] - fn seconds(self) -> Seconds<$type> { - Seconds(self) - } - - #[inline(always)] - fn minutes(self) -> Minutes<$type> { - Minutes(self) - } - - #[inline(always)] - fn hours(self) -> Hours<$type> { - Hours(self) - } - - #[inline(always)] - fn hertz(self) -> Hertz<$type> { - Hertz(self) - } - } - )* - }; -} - -impl_numeric_constructors![u32, u64]; - impl TimeInt for u32 {} impl TimeInt for u64 {} diff --git a/src/timer.rs b/src/timer.rs index b23f150..7d555b3 100644 --- a/src/timer.rs +++ b/src/timer.rs @@ -1,4 +1,6 @@ -use crate::{duration::TryConvertFrom, timer::param::*, traits::*, units::*, Duration, Instant}; +use crate::{ + duration::Duration, duration::TryConvertFrom, timer::param::*, traits::*, units::*, Instant, +}; use core::{convert::TryFrom, marker::PhantomData, ops::Add, prelude::v1::*}; pub(crate) mod param { @@ -205,7 +207,7 @@ impl Timer<'_, Periodic, Running, Clock, Dur #[allow(unused_imports)] #[allow(unsafe_code)] mod test { - use crate::{traits::*, units::*, Duration, Error, Instant, Period}; + use crate::{duration::Duration, traits::*, units::*, Error, Instant, Period}; use core::convert::{Infallible, TryFrom}; use crossbeam_utils::thread; use std::sync::atomic::{AtomicU64, Ordering}; From c607e81a1410f78190460ebac9aefdfd063c1bab Mon Sep 17 00:00:00 2001 From: Peter Taylor Date: Fri, 3 Jul 2020 10:43:16 -0600 Subject: [PATCH 19/23] docs: Extensive documenation additions and changes --- src/clock.rs | 20 ++++++++++++++++---- src/duration.rs | 43 +++++++++---------------------------------- src/frequency.rs | 2 ++ src/instant.rs | 33 +++++++++++++++++++++++++++++---- src/lib.rs | 11 +++++++---- src/period.rs | 7 ++++++- src/time_int.rs | 8 ++++++++ src/timer.rs | 14 +++++++------- 8 files changed, 84 insertions(+), 54 deletions(-) diff --git a/src/clock.rs b/src/clock.rs index d1aee2e..8b19e8f 100644 --- a/src/clock.rs +++ b/src/clock.rs @@ -1,4 +1,4 @@ -//! The `Clock` trait can be implemented over hardware timers or other time-keeping device +//! Clock abstractions use crate::{ duration::Duration, instant::Instant, period::Period, time_int::TimeInt, timer::param, @@ -9,11 +9,20 @@ use crate::{ #[non_exhaustive] #[derive(Debug, Eq, PartialEq)] pub enum Error { - /// specific implementation error + /// implementation-specific error Other(E), } -/// An abstraction for time-keeping items such as hardware timers +/// The `Clock` trait provides an abstraction of hardware-specific timer peripherals, external timer +/// devices, RTCs, etc. +/// +/// The `Clock` is characterized by an inner unsigned integer storage type (either [`u32`] or +/// [`u64`]), a [`u32`]/[`u32`] fraction ([`Period`]) defining the duration (in seconds) of one +/// count of this `Clock`, and a custom error type (must implement [`crate::Error`]) representing +/// errors that may be generated by the implementation. +/// +/// In addition to the [`Clock::now()`] method which returns an [`Instant`], an unlimited number of +/// software [`Timer`]s can be spawned from a single `Clock` instance. pub trait Clock: Sized { /// The type to hold the tick count type Rep: TimeInt; @@ -22,12 +31,15 @@ pub trait Clock: Sized { const PERIOD: Period; /// Implementation-specific error type + /// + /// This type can be returned using the [`Error::Other(E)`](enum.Error.html#variant.Other) type ImplError: crate::Error; /// Get the current Instant /// /// # Errors - /// Implementation-specific error returned through [`Error::Other(ImplError)`] + /// Implementation-specific error returned as + /// [`Error::Other(Self::ImplError)`](enum.Error.html#variant.Other) fn now(&self) -> Result, Error>; /// Spawn a new, `OneShot` [`Timer`] from this clock diff --git a/src/duration.rs b/src/duration.rs index c1cb02b..222b2c4 100644 --- a/src/duration.rs +++ b/src/duration.rs @@ -9,36 +9,6 @@ use num::Bounded; /// Each implementation defines a constant [`Period`] which is a fraction/ratio representing the /// period of the count's LSbit /// -/// # Implementation Example -/// ```rust,no_run -/// # use embedded_time::{Duration, Period, TimeInt}; -/// # use core::{fmt, fmt::Formatter}; -/// # -/// #[derive(Copy, Clone)] -/// struct Milliseconds(pub T); -/// -/// impl Duration for Milliseconds { -/// type Rep = T; // set the storage type -/// -/// // set LSbit period to 1 millisecond -/// const PERIOD: Period = ::new(1, 1_000); -/// -/// fn new(value: Self::Rep) -> Self { -/// Self(value) -/// } -/// -/// fn count(self) -> Self::Rep { -/// self.0 -/// } -/// } -/// -/// impl fmt::Display for Milliseconds { -/// fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), fmt::Error> { -/// unimplemented!() -/// } -/// -/// } -/// ``` /// /// # Constructing a duration /// ```rust @@ -75,7 +45,9 @@ use num::Bounded; /// // ... /// ``` /// -/// # Converting to [`core::time::Duration`] +/// # Converting to `core` types +/// [`core::time::Duration`] +/// /// ## Examples /// ```rust /// # use embedded_time::traits::*; @@ -94,7 +66,9 @@ use num::Bounded; /// assert_eq!(569_000_000, core_duration.subsec_nanos()); /// ``` /// -/// # Converting from [`core::time::Duration`] +/// # Converting from `core` types +/// [`core::time::Duration`] +/// /// ## Examples /// ```rust /// # use embedded_time::{traits::*, units::*}; @@ -212,7 +186,7 @@ pub trait Duration: Sized + Copy + fmt::Display { /// new duration fn new(value: Self::Rep) -> Self; - /// Returns the integer value of the [`Duration`] + /// Returns the integer value of the `Duration` /// /// # Examples /// ```rust @@ -221,7 +195,7 @@ pub trait Duration: Sized + Copy + fmt::Display { /// ``` fn count(self) -> Self::Rep; - /// Constructs a [`Duration`] from a value of ticks and a period + /// Constructs a `Duration` from a value of ticks and a period /// /// # Examples /// ```rust @@ -484,6 +458,7 @@ where } } +#[doc(hidden)] pub mod units { use crate::{ duration::{Duration, TryConvertFrom}, diff --git a/src/frequency.rs b/src/frequency.rs index 4048870..db547da 100644 --- a/src/frequency.rs +++ b/src/frequency.rs @@ -5,6 +5,8 @@ pub(crate) mod units { use core::{convert, ops}; /// A frequency unit type + /// + /// Convertible to/from [`Period`]. #[derive(Ord, PartialOrd, Eq, PartialEq, Debug)] pub struct Hertz(pub T); diff --git a/src/instant.rs b/src/instant.rs index a5d964a..d323cea 100644 --- a/src/instant.rs +++ b/src/instant.rs @@ -4,11 +4,36 @@ use crate::duration::Duration; use core::{cmp::Ordering, convert::TryFrom, ops}; use num::traits::{WrappingAdd, WrappingSub}; -/// Represents an instant of time relative to a specific [`Clock`](trait.Clock.html) +/// Represents an instant of time relative to a specific [`Clock`](crate::clock::Clock) /// /// # Example -/// Create an `Instant` that is `23 * SomeClock::PERIOD` seconds since the clock's epoch: -/// ```rust,ignore +/// Typically an `Instant` will be obtained from a [`Clock`](crate::clock::Clock) +/// ```rust +/// # use embedded_time::{Period, traits::*, Instant}; +/// # #[derive(Debug)] +/// # struct SomeClock; +/// # impl embedded_time::Clock for SomeClock { +/// # type Rep = u32; +/// # const PERIOD: Period = ::new(1, 1_000); +/// # type ImplError = (); +/// # fn now(&self) -> Result, embedded_time::clock::Error> {Ok(Instant::::new(23))} +/// # } +/// let some_clock = SomeClock; +/// let some_instant = some_clock.now().unwrap(); +/// ``` +/// +/// However, an `Instant` can also be constructed directly. In this case the constructed `Instant` +/// is `23 * SomeClock::PERIOD` seconds since the clock's epoch +/// ```rust,no_run +/// # use embedded_time::{Period, Instant}; +/// # #[derive(Debug)] +/// # struct SomeClock; +/// # impl embedded_time::Clock for SomeClock { +/// # type Rep = u32; +/// # const PERIOD: Period = ::new(1, 1_000); +/// # type ImplError = (); +/// # fn now(&self) -> Result, embedded_time::clock::Error> {unimplemented!()} +/// # } /// Instant::::new(23); /// ``` #[derive(Debug)] @@ -96,7 +121,7 @@ impl Instant { } } - /// Returns the [`Duration`](trait.Duration.html) (in the provided units) since the beginning of + /// Returns the [`Duration`] (in the provided units) since the beginning of /// time (or the [`Clock`](trait.Clock.html)'s 0) pub fn duration_since_epoch(&self) -> Result where diff --git a/src/lib.rs b/src/lib.rs index 3a22a7b..f93db9e 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -19,9 +19,9 @@ //! [`Duration::count()`]: duration/trait.Duration.html#tymethod.count //! //! ## Definitions -//! **Clock**: Any entity that periodically counts (ie a hardware timer/counter peripheral). -//! Generally, this needs to be monotonic. A wrapping clock is considered monotonic in this context -//! as long as it fulfills the other requirements. +//! **Clock**: Any entity that periodically counts (ie an external or peripheral hardware +//! timer/counter). Generally, this needs to be monotonic. A wrapping clock is considered monotonic +//! in this context as long as it fulfills the other requirements. //! //! **Wrapping Clock**: A clock that when at its maximum value, the next count is the minimum //! value. @@ -94,6 +94,7 @@ pub use timer::Timer; /// ```rust,no_run /// use embedded_time::traits::*; /// ``` +#[doc(hidden)] pub mod traits { // Rename traits to `_` to avoid any potential name conflicts. pub use crate::clock::Clock as _; @@ -106,11 +107,13 @@ pub mod traits { pub mod units { //! Time-based units of measure ([`Milliseconds`], [`Hertz`], etc) + #[doc(inline)] pub use crate::duration::units::*; + #[doc(inline)] pub use crate::frequency::units::*; } -/// An implementation-specific error +/// General error-type trait implemented for all error types in this crate pub trait Error: fmt::Debug {} impl Error for () {} impl Error for Infallible {} diff --git a/src/period.rs b/src/period.rs index a95c426..442b718 100644 --- a/src/period.rs +++ b/src/period.rs @@ -5,7 +5,12 @@ use num::{rational::Ratio, CheckedDiv, CheckedMul}; /// A fractional time period /// -/// Used primarily to define the period of one count of a [`crate::Duration`] type +/// Used primarily to define the period of one count of a [`Duration`], [`Instant`] and [`Clock`] +/// impl types but also convertable to/from [`Hertz`]. +/// +/// [`Duration`]: duration/trait.Duration.html +/// [`Clock`]: clock/trait.Clock.html +/// [`Instant`]: instant/struct.Instant.html #[derive(Debug)] pub struct Period(Ratio); diff --git a/src/time_int.rs b/src/time_int.rs index de87bb9..6cdb089 100644 --- a/src/time_int.rs +++ b/src/time_int.rs @@ -20,6 +20,10 @@ pub trait TimeInt: + fmt::Display + fmt::Debug { + /// A checked multiplication with a [`Period`] + /// + /// # Examples + /// /// ```rust /// # use embedded_time::{Period, traits::*}; /// assert_eq!(8_u32.checked_mul_period(&Period::new(1,2)), Some(4_u32)); @@ -34,6 +38,10 @@ pub trait TimeInt: )?) } + /// A checked division with a [`Period`] + /// + /// # Examples + /// /// ```rust /// # use embedded_time::{Period, traits::*}; /// assert_eq!(8_u32.checked_div_period(&Period::new(1,2)), Some(16_u32)); diff --git a/src/timer.rs b/src/timer.rs index 7d555b3..b5f750e 100644 --- a/src/timer.rs +++ b/src/timer.rs @@ -23,8 +23,8 @@ pub(crate) mod param { pub struct OneShot; } -/// A `Timer` counts toward an expiration, can be polled for elapsed and remaining time, as -/// well as optionally execute a task upon expiration. +/// A `Timer` counts toward an expiration, can be polled for elapsed and remaining time, and can be +/// one-shot or continuous/periodic. #[derive(Debug)] pub struct Timer<'a, Type, State, Clock: crate::Clock, Dur: Duration> { clock: &'a Clock, @@ -73,7 +73,7 @@ impl<'a, Type, State, Clock: crate::Clock, Dur: Duration> Timer<'a, Type, State, } impl<'a, Type, Clock: crate::Clock, Dur: Duration> Timer<'a, Type, Disarmed, Clock, Dur> { - /// Set the [`Duration`](trait.Duration.html) of the timer + /// Set the [`Duration`] of the timer /// /// This _arms_ the timer (makes it ready to run). pub fn set_duration(self, duration: Dur) -> Timer<'a, Type, Armed, Clock, Dur> { @@ -107,9 +107,9 @@ impl Timer<'_, Type, Running, Clock, D self.clock.now().unwrap() >= self.expiration.unwrap() } - /// Returns the [`Duration`](trait.Duration.html) of time elapsed since it was started + /// Returns the [`Duration`] of time elapsed since it was started /// - /// The units of the [`Duration`](trait.Duration.html) are the same as that used with + /// The units of the [`Duration`] are the same as that used with /// [`set_duration()`](struct.Timer.html#method.set_duration). pub fn elapsed(&self) -> Dur where @@ -123,9 +123,9 @@ impl Timer<'_, Type, Running, Clock, D .unwrap() } - /// Returns the [`Duration`](trait.Duration.html) until the expiration of the timer + /// Returns the [`Duration`] until the expiration of the timer /// - /// The units of the [`Duration`](trait.Duration.html) are the same as that used with + /// The units of the [`Duration`] are the same as that used with /// [`set_duration()`](struct.Timer.html#method.set_duration). pub fn remaining(&self) -> Dur where From 6f9d560fb3515a5965227d80c3762b7dbaa970f2 Mon Sep 17 00:00:00 2001 From: Peter Taylor Date: Fri, 3 Jul 2020 10:44:45 -0600 Subject: [PATCH 20/23] refactor(clock): Remove default from clock::Error type parameter --- src/clock.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/clock.rs b/src/clock.rs index 8b19e8f..864a5d8 100644 --- a/src/clock.rs +++ b/src/clock.rs @@ -8,7 +8,7 @@ use crate::{ /// Potential `Clock` errors #[non_exhaustive] #[derive(Debug, Eq, PartialEq)] -pub enum Error { +pub enum Error { /// implementation-specific error Other(E), } From 9d76e9474e02ac816216222f7465d76ca488728d Mon Sep 17 00:00:00 2001 From: Peter Taylor Date: Fri, 3 Jul 2020 12:32:42 -0600 Subject: [PATCH 21/23] build(examples): Remove cortex-m patch and mutex-trait dependency These are no longer needed due to changes made in examples/nrf52_dk. --- Cargo.lock | 65 ++++++++++++++++++++++++++++++++++++++++-------------- Cargo.toml | 6 +---- 2 files changed, 49 insertions(+), 22 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 0d34995..64167e5 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1,5 +1,25 @@ # This file is automatically @generated by Cargo. # It is not intended for manual editing. +[[package]] +name = "aligned" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eb1ce8b3382016136ab1d31a1b5ce807144f8b7eb2d5f16b2108f0f07edceb94" +dependencies = [ + "as-slice", +] + +[[package]] +name = "as-slice" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "37dfb65bc03b2bc85ee827004f14a6817e04160e3b1a28931986a666a9290e70" +dependencies = [ + "generic-array 0.12.3", + "generic-array 0.13.2", + "stable_deref_trait", +] + [[package]] name = "autocfg" version = "1.0.0" @@ -15,12 +35,6 @@ dependencies = [ "rustc_version", ] -[[package]] -name = "bitfield" -version = "0.13.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "46afbd2983a5d5a7bd740ccb198caf5b82f45c40c09c0eed36052d91cb92e719" - [[package]] name = "cast" version = "0.2.3" @@ -39,11 +53,11 @@ checksum = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822" [[package]] name = "cortex-m" version = "0.6.2" -source = "git+https://github.com/rust-embedded/cortex-m?branch=mutex_add#2dca8c948f47a1c23e9f33bff26721f5a33f2fd8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2954942fbbdd49996704e6f048ce57567c3e1a4e2dc59b41ae9fde06a01fc763" dependencies = [ + "aligned", "bare-metal", - "bitfield", - "mutex-trait", "volatile-register", ] @@ -91,12 +105,11 @@ dependencies = [ [[package]] name = "embedded-time" -version = "0.5.2" +version = "0.6.0" dependencies = [ "cortex-m", "cortex-m-rt", "crossbeam-utils", - "mutex-trait", "nrf52832-hal", "num", "panic_rtt", @@ -112,6 +125,24 @@ dependencies = [ "typenum", ] +[[package]] +name = "generic-array" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c68f0274ae0e023facc3c97b2e00f076be70e254bc851d972503b328db79b2ec" +dependencies = [ + "typenum", +] + +[[package]] +name = "generic-array" +version = "0.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ed1e761351b56f54eb9dcd0cfaca9fd0daecf93918e1cfc01c8a3d26ee7adcd" +dependencies = [ + "typenum", +] + [[package]] name = "jlink_rtt" version = "0.2.0" @@ -124,12 +155,6 @@ version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" -[[package]] -name = "mutex-trait" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b4bb1638d419e12f8b1c43d9e639abd0d1424285bdea2f76aa231e233c63cd3a" - [[package]] name = "nb" version = "0.1.2" @@ -306,6 +331,12 @@ version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3" +[[package]] +name = "stable_deref_trait" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dba1a27d3efae4351c8051072d619e3ade2820635c3958d826bfea39d59b54c8" + [[package]] name = "syn" version = "1.0.33" diff --git a/Cargo.toml b/Cargo.toml index 437bcce..a8b5e16 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "embedded-time" -version = "0.5.2" +version = "0.6.0" authors = ["Peter Taylor "] edition = "2018" description = "A library for abstracting clocks and handling time(ing) in embedded systems" @@ -20,10 +20,6 @@ cortex-m = "0.6.2" cortex-m-rt = "0.6.12" panic_rtt = "0.3.0" nrf52832-hal = { version = "0.10.0", default-features = false, features = ["rt", "xxAA-package"] } -mutex-trait = "0.2.0" [target.'cfg(not(target_arch = "arm"))'.dev-dependencies] crossbeam-utils = "0.7.2" - -[patch.crates-io] -cortex-m = { git = "https://github.com/rust-embedded/cortex-m", branch = "mutex_add" } From ca7bff417614c938383315c1c1935c4fb0b81744 Mon Sep 17 00:00:00 2001 From: Peter Taylor Date: Fri, 3 Jul 2020 13:05:18 -0600 Subject: [PATCH 22/23] refactor(Timer): Set duration upon construction - Add duration to `Clock::new_timer()` and `Timer::new()` functions - Remove `Timer::set_duration()` method - Remove `Disarmed` `Timer` state BREAKING CHANGE: `Timer` interface --- examples/nrf52_dk/main.rs | 12 ++----- src/clock.rs | 7 +++-- src/timer.rs | 66 ++++++++++++--------------------------- 3 files changed, 27 insertions(+), 58 deletions(-) diff --git a/examples/nrf52_dk/main.rs b/examples/nrf52_dk/main.rs index e85144e..036cd66 100644 --- a/examples/nrf52_dk/main.rs +++ b/examples/nrf52_dk/main.rs @@ -152,20 +152,12 @@ where led2.set_high()?; led3.set_high()?; led4.set_low()?; - clock - .new_timer() - .set_duration(250_u32.milliseconds()) - .start() - .wait(); + clock.new_timer(250_u32.milliseconds()).start().wait(); led1.set_high()?; led2.set_low()?; led3.set_low()?; led4.set_high()?; - clock - .new_timer() - .set_duration(250_u32.milliseconds()) - .start() - .wait(); + clock.new_timer(250_u32.milliseconds()).start().wait(); } } diff --git a/src/clock.rs b/src/clock.rs index 864a5d8..0d5a1c9 100644 --- a/src/clock.rs +++ b/src/clock.rs @@ -43,7 +43,10 @@ pub trait Clock: Sized { fn now(&self) -> Result, Error>; /// Spawn a new, `OneShot` [`Timer`] from this clock - fn new_timer(&self) -> Timer { - Timer::::new(&self) + fn new_timer( + &self, + duration: Dur, + ) -> Timer { + Timer::::new(&self, duration) } } diff --git a/src/timer.rs b/src/timer.rs index b5f750e..47bc613 100644 --- a/src/timer.rs +++ b/src/timer.rs @@ -7,9 +7,6 @@ pub(crate) mod param { #[derive(Debug)] pub struct None; - #[derive(Debug)] - pub struct Disarmed; - #[derive(Debug)] pub struct Armed; @@ -28,7 +25,7 @@ pub(crate) mod param { #[derive(Debug)] pub struct Timer<'a, Type, State, Clock: crate::Clock, Dur: Duration> { clock: &'a Clock, - duration: Option, + duration: Dur, expiration: Option>, _type: PhantomData, _state: PhantomData, @@ -37,10 +34,10 @@ pub struct Timer<'a, Type, State, Clock: crate::Clock, Dur: Duration> { impl<'a, Clock: crate::Clock, Dur: Duration> Timer<'_, param::None, param::None, Clock, Dur> { /// Construct a new, `OneShot` `Timer` #[allow(clippy::new_ret_no_self)] - pub fn new(clock: &Clock) -> Timer { - Timer:: { + pub fn new(clock: &Clock, duration: Dur) -> Timer { + Timer:: { clock, - duration: Option::None, + duration, expiration: Option::None, _type: PhantomData, _state: PhantomData, @@ -72,22 +69,8 @@ impl<'a, Type, State, Clock: crate::Clock, Dur: Duration> Timer<'a, Type, State, } } -impl<'a, Type, Clock: crate::Clock, Dur: Duration> Timer<'a, Type, Disarmed, Clock, Dur> { - /// Set the [`Duration`] of the timer - /// - /// This _arms_ the timer (makes it ready to run). - pub fn set_duration(self, duration: Dur) -> Timer<'a, Type, Armed, Clock, Dur> { - Timer:: { - clock: self.clock, - duration: Some(duration), - expiration: Option::None, - _type: PhantomData, - _state: PhantomData, - } - } -} impl<'a, Type, Clock: crate::Clock, Dur: Duration> Timer<'a, Type, Armed, Clock, Dur> { - /// Start the _armed_ timer from this instant + /// Start the timer from this instant pub fn start(self) -> Timer<'a, Type, Running, Clock, Dur> where Instant: Add>, @@ -95,7 +78,7 @@ impl<'a, Type, Clock: crate::Clock, Dur: Duration> Timer<'a, Type, Armed, Clock, Timer:: { clock: self.clock, duration: self.duration, - expiration: Some(self.clock.now().unwrap() + self.duration.unwrap()), + expiration: Some(self.clock.now().unwrap() + self.duration), _type: PhantomData, _state: PhantomData, } @@ -109,8 +92,9 @@ impl Timer<'_, Type, Running, Clock, D /// Returns the [`Duration`] of time elapsed since it was started /// - /// The units of the [`Duration`] are the same as that used with - /// [`set_duration()`](struct.Timer.html#method.set_duration). + /// **The duration is truncated, not rounded**. + /// + /// The units of the [`Duration`] are the same as that used to construct the `Timer`. pub fn elapsed(&self) -> Dur where Dur::Rep: TryFrom, @@ -119,14 +103,15 @@ impl Timer<'_, Type, Running, Clock, D self.clock .now() .unwrap() - .duration_since(&(self.expiration.unwrap() - self.duration.unwrap())) + .duration_since(&(self.expiration.unwrap() - self.duration)) .unwrap() } /// Returns the [`Duration`] until the expiration of the timer /// - /// The units of the [`Duration`] are the same as that used with - /// [`set_duration()`](struct.Timer.html#method.set_duration). + /// **The duration is truncated, not rounded**. + /// + /// The units of the [`Duration`] are the same as that used to construct the `Timer`. pub fn remaining(&self) -> Dur where Dur::Rep: TryFrom, @@ -151,8 +136,7 @@ impl<'a, Clock: crate::Clock, Dur: Duration> Timer<'a, OneShot, Running, Clock, // since the timer is running, _is_expired() will return a value while !self._is_expired() {} - Timer::::new(self.clock) - .set_duration(self.duration.unwrap()) + Timer::::new(self.clock, self.duration) } /// Check whether the timer has expired @@ -177,9 +161,7 @@ impl Timer<'_, Periodic, Running, Clock, Dur Self { clock: self.clock, duration: self.duration, - expiration: self - .expiration - .map(|expiration| expiration + self.duration.unwrap()), + expiration: self.expiration.map(|expiration| expiration + self.duration), _type: PhantomData, _state: PhantomData, } @@ -194,7 +176,7 @@ impl Timer<'_, Periodic, Running, Clock, Dur { // since the timer is running, _is_expired() will return a value if self._is_expired() { - self.expiration = Some(self.expiration.unwrap() + self.duration.unwrap()); + self.expiration = Some(self.expiration.unwrap() + self.duration); true } else { @@ -231,7 +213,7 @@ mod test { init_ticks(); let clock = Clock; - let timer = clock.new_timer().set_duration(1_u32.seconds()).start(); + let timer = clock.new_timer(1_u32.seconds()).start(); thread::scope(|s| { let timer_handle = s.spawn(|_| timer.wait()); @@ -260,11 +242,7 @@ mod test { init_ticks(); let clock = Clock; - let timer = clock - .new_timer() - .into_periodic() - .set_duration(1_u32.seconds()) - .start(); + let timer = clock.new_timer(1_u32.seconds()).into_periodic().start(); thread::scope(|s| { let timer_handle = s.spawn(|_| timer.wait()); @@ -292,11 +270,7 @@ mod test { init_ticks(); let clock = Clock; - let mut timer = clock - .new_timer() - .into_periodic() - .set_duration(1_u32.seconds()) - .start(); + let mut timer = clock.new_timer(1_u32.seconds()).into_periodic().start(); add_to_ticks(2_u32.seconds()); @@ -309,7 +283,7 @@ mod test { init_ticks(); let clock = Clock; - let timer = clock.new_timer().set_duration(2_u32.seconds()).start(); + let timer = clock.new_timer(2_u32.seconds()).start(); add_to_ticks(1_u32.milliseconds()); From c04a9011929bb37deb0b314d9e978de82821e183 Mon Sep 17 00:00:00 2001 From: Peter Taylor Date: Fri, 3 Jul 2020 13:36:52 -0600 Subject: [PATCH 23/23] docs(crate): Pre-release updating - Update both readme's - Update changelog - Bump version to 0.6.0 --- CHANGELOG.md | 11 ++++++++--- README.md | 40 +++++++++++++++++++++++++++++++++++----- crates-io.md | 39 ++++++++++++++++++++++++++++++++++----- 3 files changed, 77 insertions(+), 13 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index dcde591..30c4b9f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,15 +2,20 @@ ## [Unreleased] + +[unreleased]: https://github.com/FluenTech/embedded-time/compare/v0.6.0...HEAD + +## [0.6.0] - 2020-07-03 + ### Added - A `Timer` type supporting one-shot and periodic software timers utilizing a `Clock` implementation -- `Timer` unit tests -- Fallibility to `Clock` methods +- Fallibility and error handling for `Clock` methods - `Instant::duration_until()` with order checking - Order checking to `Instant::duration_since()` - Bounds checking on `Instant` impls of Add/Sub - Changelog back to v0.5.0 release +- [`crossbeam-utils`](https://crates.io/crates/crossbeam-utils) dev-dependency for scoped threads in tests ### Changed @@ -19,7 +24,7 @@ - `Instant::duration_since()` return type to `Result` - Refactor `examples/nrf52_dk` -[unreleased]: https://github.com/FluenTech/embedded-time/compare/v0.5.2...HEAD +[0.6.0]: https://github.com/FluenTech/embedded-time/compare/v0.5.2...v0.6.0 ## [0.5.2] - 2020-06-21 diff --git a/README.md b/README.md index 807e2cc..8418b8a 100644 --- a/README.md +++ b/README.md @@ -5,12 +5,42 @@ [docs.rs]: https://docs.rs/embedded-time/badge.svg `embedded-time` provides a comprehensive library for implementing abstractions over -hardware and work with _clocks_, _instants_, _durations_, _periods_, and _frequencies_ in a more intuitive way. - +hardware and work with _clocks_, _timers_, _instants_, _durations_, _periods_, and _frequencies_ in a more intuitive way. + +## Hardware Abstraction + - `Clock` trait allowing abstraction of hardware timers for timekeeping. -- Work with time using _milliseconds_, _seconds_, _hertz_, etc. rather than _cycles_ or _ticks_. -- Includes example for the nRF52_DK board -- Conversion to/from core::time::Duration + +## Timers + +- Software timers spawned from a `Clock` impl object. +- One-shot or periodic/continuous +- Blocking delay +- Poll for expiration +- Read elapsed/remaining duration + +## Duration Types + +- Nanoseconds +- Microseconds +- Milliseconds +- Seconds +- Minutes +- Hours + +## Frequency Type + +- Hertz + +## `core` Compatibility + +- Conversion to/from `core::time::Duration` + +## Reliability and Usability +- Extensive tests +- Thorough documentation with examples +- Example for the nRF52_DK board + ## License This project is licensed under either of diff --git a/crates-io.md b/crates-io.md index 273f3bc..b2ce535 100644 --- a/crates-io.md +++ b/crates-io.md @@ -1,12 +1,41 @@ # embedded-time `embedded-time` provides a comprehensive library for implementing abstractions over -hardware and work with _clocks_, _instants_, _durations_, _periods_, and _frequencies_ in a more intuitive way. - +hardware and work with _clocks_, _timers_, _instants_, _durations_, _periods_, and _frequencies_ in a more intuitive way. + +## Hardware Abstraction + - `Clock` trait allowing abstraction of hardware timers for timekeeping. -- Work with time using _milliseconds_, _seconds_, _hertz_, etc. rather than _cycles_ or _ticks_. -- Includes example for the nRF52_DK board -- Conversion to/from core::time::Duration + +## Timers + +- Software timers spawned from a `Clock` impl object. +- One-shot or periodic/continuous +- Blocking delay +- Poll for expiration +- Read elapsed/remaining duration + +## Duration Types + +- Nanoseconds +- Microseconds +- Milliseconds +- Seconds +- Minutes +- Hours + +## Frequency Type + +- Hertz + +## `core` Compatibility + +- Conversion to/from `core::time::Duration` + +## Reliability and Usability +- Extensive tests +- Thorough documentation with examples +- Example for the nRF52_DK board ## Motivation The handling of time on embedded systems is generally much different than that of OSs. For instance, on an OS, the time is measured against an arbitrary epoch. Embedded systems generally don't know (nor do they care) what the *real* time is, but rather how much time has passed since the system has started.