linux_embedded_hal/
timer.rs

1//! Implementation of [`embedded-hal`] timer traits
2//!
3//! [`embedded-hal`]: https://docs.rs/embedded-hal
4
5use core::convert::Infallible;
6use std::time::{Duration, Instant};
7
8/// Marker trait that indicates that a timer is periodic
9pub trait Periodic {}
10
11/// A count down timer
12///
13/// Note that this is borrowed from `embedded-hal` 0.2.x and will be in use until the `1.x` version provides one.
14///
15/// # Contract
16///
17/// - `self.start(count); block!(self.wait());` MUST block for AT LEAST the time specified by
18/// `count`.
19///
20/// *Note* that the implementer doesn't necessarily have to be a *downcounting* timer; it could also
21/// be an *upcounting* timer as long as the above contract is upheld.
22///
23/// # Examples
24///
25/// You can use this timer to create delays
26///
27/// ```
28/// use std::time::Duration;
29/// use nb::block;
30/// use linux_embedded_hal::{CountDown, SysTimer};
31///
32/// fn main() {
33///     let mut led: Led = {
34///         // ..
35/// #       Led
36///     };
37///     let mut timer = SysTimer::new();
38///
39///     Led.on();
40///     timer.start(Duration::from_millis(1000)).unwrap();
41///     block!(timer.wait()); // blocks for 1 second
42///     Led.off();
43/// }
44///
45/// # use core::convert::Infallible;
46/// # struct Seconds(u32);
47/// # trait U32Ext { fn s(self) -> Seconds; }
48/// # impl U32Ext for u32 { fn s(self) -> Seconds { Seconds(self) } }
49/// # struct Led;
50/// # impl Led {
51/// #     pub fn off(&mut self) {}
52/// #     pub fn on(&mut self) {}
53/// # }
54/// ```
55pub trait CountDown {
56    /// An enumeration of `CountDown` errors.
57    ///
58    /// For infallible implementations, will be `Infallible`
59    type Error: core::fmt::Debug;
60
61    /// The unit of time used by this timer
62    type Time;
63
64    /// Starts a new count down
65    fn start<T>(&mut self, count: T) -> Result<(), Self::Error>
66    where
67        T: Into<Self::Time>;
68
69    /// Non-blockingly "waits" until the count down finishes
70    ///
71    /// # Contract
72    ///
73    /// - If `Self: Periodic`, the timer will start a new count down right after the last one
74    /// finishes.
75    /// - Otherwise the behavior of calling `wait` after the last call returned `Ok` is UNSPECIFIED.
76    /// Implementers are suggested to panic on this scenario to signal a programmer error.
77    fn wait(&mut self) -> nb::Result<(), Self::Error>;
78}
79
80impl<T: CountDown> CountDown for &mut T {
81    type Error = T::Error;
82
83    type Time = T::Time;
84
85    fn start<TIME>(&mut self, count: TIME) -> Result<(), Self::Error>
86    where
87        TIME: Into<Self::Time>,
88    {
89        T::start(self, count)
90    }
91
92    fn wait(&mut self) -> nb::Result<(), Self::Error> {
93        T::wait(self)
94    }
95}
96
97/// A periodic timer based on [`std::time::Instant`][instant], which is a
98/// monotonically nondecreasing clock.
99///
100/// [instant]: https://doc.rust-lang.org/std/time/struct.Instant.html
101pub struct SysTimer {
102    start: Instant,
103    duration: Duration,
104}
105
106impl SysTimer {
107    /// Create a new timer instance.
108    ///
109    /// The `duration` will be initialized to 0, so make sure to call `start`
110    /// with your desired timer duration before calling `wait`.
111    pub fn new() -> SysTimer {
112        SysTimer {
113            start: Instant::now(),
114            duration: Duration::from_millis(0),
115        }
116    }
117}
118
119impl Default for SysTimer {
120    fn default() -> SysTimer {
121        SysTimer::new()
122    }
123}
124
125impl CountDown for SysTimer {
126    type Error = Infallible;
127    type Time = Duration;
128
129    fn start<T>(&mut self, count: T) -> Result<(), Self::Error>
130    where
131        T: Into<Self::Time>,
132    {
133        self.start = Instant::now();
134        self.duration = count.into();
135        Ok(())
136    }
137
138    fn wait(&mut self) -> nb::Result<(), Self::Error> {
139        if (Instant::now() - self.start) >= self.duration {
140            // Restart the timer to fulfill the contract by `Periodic`
141            self.start = Instant::now();
142            Ok(())
143        } else {
144            Err(nb::Error::WouldBlock)
145        }
146    }
147}
148
149impl Periodic for SysTimer {}
150
151#[cfg(test)]
152mod tests {
153    use super::*;
154
155    /// Ensure that a 100 ms delay takes at least 100 ms,
156    /// but not longer than 500 ms.
157    #[test]
158    fn test_delay() {
159        let mut timer = SysTimer::new();
160        let before = Instant::now();
161        timer.start(Duration::from_millis(100)).unwrap();
162        nb::block!(timer.wait()).unwrap();
163        let after = Instant::now();
164        let duration_ms = (after - before).as_millis();
165        assert!(duration_ms >= 100);
166        assert!(duration_ms < 500);
167    }
168
169    /// Ensure that the timer is periodic.
170    #[test]
171    fn test_periodic() {
172        let mut timer = SysTimer::new();
173        let before = Instant::now();
174        timer.start(Duration::from_millis(100)).unwrap();
175        nb::block!(timer.wait()).unwrap();
176        let after1 = Instant::now();
177        let duration_ms_1 = (after1 - before).as_millis();
178        assert!(duration_ms_1 >= 100);
179        assert!(duration_ms_1 < 500);
180        nb::block!(timer.wait()).unwrap();
181        let after2 = Instant::now();
182        let duration_ms_2 = (after2 - after1).as_millis();
183        assert!(duration_ms_2 >= 100);
184        assert!(duration_ms_2 < 500);
185    }
186}