diff --git a/CHANGELOG.md b/CHANGELOG.md index 9f06bfb..0687029 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,8 @@ ### Added +- A `Timer` type supporting one-shot and periodic software timers utilizing a `Clock` implementation +- `Timer` unit tests - Changelog back to v0.5.0 release ### Changed 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" diff --git a/examples/nrf52_dk/main.rs b/examples/nrf52_dk/main.rs index c5465a8..721c811 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, Clock, Instant, Period, TimeInt, Timer}; use mutex_trait::Mutex as _; pub mod nrf52 { diff --git a/src/clock.rs b/src/clock.rs index bcb7e85..eddb49b 100644 --- a/src/clock.rs +++ b/src/clock.rs @@ -1,4 +1,5 @@ -use crate::{time_int::TimeInt, Duration, Instant, Period}; +use crate::timer::param::{self, Disarmed, OneShot}; +use crate::{time_int::TimeInt, Duration, Instant, Period, Timer}; use core::convert::TryFrom; /// An abstraction for time-keeping items such as hardware timers @@ -21,4 +22,8 @@ pub trait Clock: Sized { let end = start + dur; while Self::now() < end {} } + + fn new_timer() -> Timer { + Timer::::new() + } } diff --git a/src/lib.rs b/src/lib.rs index 6fb26d2..1e4e842 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -20,6 +20,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 @@ -69,12 +71,14 @@ mod frequency; mod instant; mod period; mod time_int; +mod timer; pub use clock::Clock; 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..6b27f18 --- /dev/null +++ b/src/timer.rs @@ -0,0 +1,225 @@ +use crate::timer::param::{Armed, Disarmed, OneShot, Periodic, Running}; +use crate::{Duration, Instant, TimeInt}; +use core::convert::TryFrom; +use core::marker::PhantomData; +use core::ops::Add; + +pub 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; +} + +#[derive(Debug)] +pub struct Timer { + duration: Option, + expiration: Option>, + _type: PhantomData, + _state: PhantomData, +} + +impl Timer { + pub fn new() -> Timer { + Timer:: { + duration: None, + expiration: None, + _type: PhantomData, + _state: PhantomData, + } + } +} + +impl Timer { + pub fn into_oneshot(self) -> Timer { + Timer:: { + duration: self.duration, + expiration: self.expiration, + _type: PhantomData, + _state: PhantomData, + } + } + + pub fn into_periodic(self) -> Timer { + Timer:: { + duration: self.duration, + expiration: self.expiration, + _type: PhantomData, + _state: PhantomData, + } + } +} + +impl Timer { + pub fn set_duration(self, duration: Dur) -> Timer { + Timer:: { + duration: Some(duration), + expiration: None, + _type: PhantomData, + _state: PhantomData, + } + } +} + +impl Timer { + pub fn start(self) -> Timer + where + Instant: Add>, + { + Timer:: { + duration: self.duration, + expiration: Some(Clock::now() + self.duration.unwrap()), + _type: PhantomData, + _state: PhantomData, + } + } +} + +impl Timer { + fn _is_expired(&self) -> bool { + Clock::now() >= self.expiration.unwrap() + } +} + +impl Timer { + pub fn wait(self) { + // since the timer is running, _is_expired() will return a value + while !self._is_expired() {} + } +} + +impl Timer { + pub fn is_expired(&self) -> bool { + self._is_expired() + } +} + +impl Timer { + pub fn wait(self) -> Self + where + Instant: Add>, + { + // since the timer is running, _is_expired() will return a value + while !self._is_expired() {} + + Self { + duration: self.duration, + expiration: self + .expiration + .map(|expiration| expiration + self.duration.unwrap()), + _type: PhantomData, + _state: PhantomData, + } + } + + 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)] +mod test { + #![allow(unsafe_code)] + + use crate::{units::*, Clock as _, Duration, Instant, Period, TimeInt, Timer}; + use std::convert::{TryFrom, TryInto}; + + #[derive(Debug, Ord, PartialOrd, Eq, PartialEq)] + struct Clock; + + static mut START: Option = None; + + impl crate::Clock for Clock { + type Rep = i64; + const PERIOD: Period = Period::new(1, 64_000_000); + + fn now() -> Instant { + let since_start = unsafe { START.unwrap() }.elapsed(); + let ticks = Nanoseconds::::try_from(since_start) + .unwrap() + .into_ticks(Self::PERIOD) + .unwrap(); + Instant::new(ticks) + } + } + + fn init_start_time() { + unsafe { + START = Some(std::time::Instant::now()); + } + } + + #[test] + fn oneshot_wait() { + init_start_time(); + + Clock::new_timer().set_duration(1.seconds()).start().wait(); + + unsafe { + assert!(Seconds::::try_from(START.unwrap().elapsed()).unwrap() == 1.seconds()); + } + } + + #[test] + fn periodic_wait() { + init_start_time(); + + let timer = Clock::new_timer() + .into_periodic() + .set_duration(1.seconds()) + .start() + .wait(); + + unsafe { + assert!(Seconds::::try_from(START.unwrap().elapsed()).unwrap() == 1.seconds()); + } + + let timer = timer.wait(); + unsafe { + assert!(Seconds::::try_from(START.unwrap().elapsed()).unwrap() == 2.seconds()); + } + + let timer = timer.wait(); + unsafe { + assert!(Seconds::::try_from(START.unwrap().elapsed()).unwrap() == 3.seconds()); + } + } + + #[test] + fn periodic_expiration() { + init_start_time(); + + let mut timer = Clock::new_timer() + .into_periodic() + .set_duration(1.seconds()) + .start(); + + std::thread::sleep(std::time::Duration::from_secs(2)); + + assert!(timer.period_complete()); + assert!(timer.period_complete()); + } +}