Skip to content

Commit

Permalink
Add Timer type
Browse files Browse the repository at this point in the history
  • Loading branch information
PTaylor-us committed Jun 27, 2020
1 parent dcc88b7 commit 92786a6
Show file tree
Hide file tree
Showing 6 changed files with 244 additions and 8 deletions.
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
12 changes: 6 additions & 6 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion examples/nrf52_dk/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
7 changes: 6 additions & 1 deletion src/clock.rs
Original file line number Diff line number Diff line change
@@ -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
Expand All @@ -21,4 +22,8 @@ pub trait Clock: Sized {
let end = start + dur;
while Self::now() < end {}
}

fn new_timer<Dur: Duration>() -> Timer<OneShot, Disarmed, Self, Dur> {
Timer::<param::None, param::None, Self, Dur>::new()
}
}
4 changes: 4 additions & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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_
///
Expand Down
225 changes: 225 additions & 0 deletions src/timer.rs
Original file line number Diff line number Diff line change
@@ -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<Type, State, Clock: crate::Clock, Dur: Duration> {
duration: Option<Dur>,
expiration: Option<Instant<Clock>>,
_type: PhantomData<Type>,
_state: PhantomData<State>,
}

impl<Clock: crate::Clock, Dur: Duration> Timer<param::None, param::None, Clock, Dur> {
pub fn new() -> Timer<OneShot, Disarmed, Clock, Dur> {
Timer::<OneShot, Disarmed, Clock, Dur> {
duration: None,
expiration: None,
_type: PhantomData,
_state: PhantomData,
}
}
}

impl<Type, State, Clock: crate::Clock, Dur: Duration> Timer<Type, State, Clock, Dur> {
pub fn into_oneshot(self) -> Timer<OneShot, State, Clock, Dur> {
Timer::<OneShot, State, Clock, Dur> {
duration: self.duration,
expiration: self.expiration,
_type: PhantomData,
_state: PhantomData,
}
}

pub fn into_periodic(self) -> Timer<Periodic, State, Clock, Dur> {
Timer::<Periodic, State, Clock, Dur> {
duration: self.duration,
expiration: self.expiration,
_type: PhantomData,
_state: PhantomData,
}
}
}

impl<Type, Clock: crate::Clock, Dur: Duration> Timer<Type, Disarmed, Clock, Dur> {
pub fn set_duration(self, duration: Dur) -> Timer<Type, Armed, Clock, Dur> {
Timer::<Type, Armed, Clock, Dur> {
duration: Some(duration),
expiration: None,
_type: PhantomData,
_state: PhantomData,
}
}
}

impl<Type, Clock: crate::Clock, Dur: Duration> Timer<Type, Armed, Clock, Dur> {
pub fn start(self) -> Timer<Type, Running, Clock, Dur>
where
Instant<Clock>: Add<Dur, Output = Instant<Clock>>,
{
Timer::<Type, Running, Clock, Dur> {
duration: self.duration,
expiration: Some(Clock::now() + self.duration.unwrap()),
_type: PhantomData,
_state: PhantomData,
}
}
}

impl<Type, Clock: crate::Clock, Dur: Duration> Timer<Type, Running, Clock, Dur> {
fn _is_expired(&self) -> bool {
Clock::now() >= self.expiration.unwrap()
}
}

impl<Clock: crate::Clock, Dur: Duration> Timer<OneShot, Running, Clock, Dur> {
pub fn wait(self) {
// since the timer is running, _is_expired() will return a value
while !self._is_expired() {}
}
}

impl<Clock: crate::Clock, Dur: Duration> Timer<OneShot, Running, Clock, Dur> {
pub fn is_expired(&self) -> bool {
self._is_expired()
}
}

impl<Clock: crate::Clock, Dur: Duration> Timer<Periodic, Running, Clock, Dur> {
pub fn wait(self) -> Self
where
Instant<Clock>: Add<Dur, Output = Instant<Clock>>,
{
// 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<Clock>: Add<Dur, Output = Instant<Clock>>,
{
// 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<std::time::Instant> = None;

impl crate::Clock for Clock {
type Rep = i64;
const PERIOD: Period = Period::new(1, 64_000_000);

fn now() -> Instant<Self> {
let since_start = unsafe { START.unwrap() }.elapsed();
let ticks = Nanoseconds::<i64>::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::<i32>::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::<i32>::try_from(START.unwrap().elapsed()).unwrap() == 1.seconds());
}

let timer = timer.wait();
unsafe {
assert!(Seconds::<i32>::try_from(START.unwrap().elapsed()).unwrap() == 2.seconds());
}

let timer = timer.wait();
unsafe {
assert!(Seconds::<i32>::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());
}
}

0 comments on commit 92786a6

Please sign in to comment.