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}