embedded_timers/
lib.rs

1// Copyright Open Logistics Foundation
2//
3// Licensed under the Open Logistics Foundation License 1.3.
4// For details on the licensing terms, see the LICENSE file.
5// SPDX-License-Identifier: OLFL-1.3
6
7//! This crate provides a common base for monotonically nondecreasing clocks in `no_std`
8//! environments. It defines the [`Clock`](clock::Clock) and [`Instant`](instant::Instant) traits
9//! as interface and implements timers and delays based on these traits.
10//!
11//! By using the `Clock` trait as dependency, device drivers and applications can be developed in
12//! a platform-independent way. Therefore, this crate serves the same purpose for clocks as the
13//! [`embedded-hal`](https://docs.rs/embedded-hal/latest/embedded_hal/) or
14//! [`embedded-nal`](https://docs.rs/embedded-nal/latest/embedded_nal/) crates for peripheral or
15//! network abstraction, respectively.
16//!
17//! This crate is _only_ concerned with monotonically nondecreasing clocks, i.e. clocks which serve
18//! the same purpose as the
19//! [`std::time::Instant`](https://doc.rust-lang.org/stable/std/time/struct.Instant.html) type.
20//! These can be used for measuring durations between instants, creating timers/timeouts or just
21//! wait/delay program execution. Anything else which might be part of a time library is a
22//! _non-goal_ of this crate: This crate is not concerned with system time or wall clock time
23//! which might jump when the clock is synchronized. Furthermore, this crate does not cover time
24//! zone handling or calendars.
25//!
26//! # Design Decisions
27//!
28//! To create a common interface for handling clocks in `no_std` environments, this crate aims to
29//! make decisions which are acceptable for many use cases and many users. In this regard, it tries
30//! to be as straightforward (boring) as possible and to avoid too opinionated or debatable
31//! decisions. For example:
32//! - Use whatever is already defined in `core`. Specifically, the
33//!   [`core::time::Duration`](https://doc.rust-lang.org/stable/core/time/struct.Duration.html) type
34//!   is used for durations although its 12 byte memory layout seems like overkill for many embedded
35//!   applications. But it covers all use cases from high-precision to multi-year timing and it is
36//!   already agreed upon in the community.
37//! - The `Clock` and `Instant` traits are inspired by `std::time::Instant` which should be
38//!   familiar for Rust developers. But different from `std::time::Instant`, no assumption of a
39//!   globally available clock is made. Therefore, the functionality is split in two different
40//!   traits.
41//!
42//! # Usage
43//!
44//! Most users (application, library or device driver developers) will depend on the `Clock` trait.
45//! Then, the clock can be used for timers or delays without being concerned with the underlying
46//! `Instant` type:
47//!
48//! ```
49//! fn application(clock: &impl embedded_timers::clock::Clock) {
50//!     // Get the current instant, the instant type is inferred from the generic Clock
51//!     let earlier = clock.now();
52//!     let later = clock.now();
53//!     // The instant type is guaranteed to support calculations due to the Instant trait
54//!     let time_passed: core::time::Duration = later - earlier;
55//!
56//!     // Timers and delays can easily be written manually
57//!     let deadline = clock.now() + core::time::Duration::from_secs(1); // 1 second timer/delay
58//!     loop {
59//!         // By comparing clock.now() with the deadline, we determine if the timer has expired.
60//!         // When doing nothing in the loop, this is a simple busy wait delay.
61//!         if clock.now() > deadline {
62//!             break;
63//!         }
64//!     }
65//!
66//!     // Alternatively, the provided helper types for timer and delay can be used
67//!     let mut timer = embedded_timers::timer::Timer::new(clock);
68//!     timer.try_start(core::time::Duration::from_secs(1)).unwrap();
69//!     let is_expired = timer.is_expired().unwrap();
70//! }
71//! ```
72//!
73//! On the platform level, the `Clock` trait has to be implemented for a clock type. The associated
74//! `Instant` type needs to implement the `Instant` trait. If the `std` feature is enabled,
75//! `std::time::Instant` implements the `Instant` trait and can be used for a very simple clock:
76//!
77//! ```
78//! struct StdClock;
79//!
80//! impl embedded_timers::clock::Clock for StdClock {
81//!     type Instant = std::time::Instant;
82//!     fn now(&self) -> Self::Instant {
83//!         std::time::Instant::now()
84//!     }
85//! }
86//! ```
87//!
88//! In an actual `no_std` environment, a clock needs to be implemented manually. For the associated
89//! `Instant` type, it may use one of the types defined in the [`instant`] module. It needs to take
90//! care of setting up an appropriate tick interrupt handler and to synchronize accesses to the
91//! tick variable. This may look somewhat like:
92//! ```
93//! // Global tick counter variable which is incremented in the tick interrupt handler. By using an
94//! // atomic variable, we can do this without unsafe code. Note that using a 32 bit counter for
95//! // milliseconds will wrap around after around 50 days so this might not be feasible in a real
96//! // scenario.
97//! static TICKS: core::sync::atomic::AtomicU32 = core::sync::atomic::AtomicU32::new(0);
98//!
99//! // This tick interrupt handler is assumed to be called once per millisecond
100//! fn tick_handler() {
101//!     TICKS.fetch_add(1, core::sync::atomic::Ordering::Relaxed);
102//! }
103//!
104//! struct MilliSecondClock32;
105//!
106//! impl embedded_timers::clock::Clock for MilliSecondClock32 {
107//!     type Instant = embedded_timers::instant::Instant32<1000>;
108//!     fn now(&self) -> Self::Instant {
109//!         let ticks = TICKS.load(core::sync::atomic::Ordering::Relaxed);
110//!         embedded_timers::instant::Instant32::<1000>::new(ticks)
111//!     }
112//! }
113//! ```
114//!
115//! ## Delay
116//!
117//! From the clock, a delay can be created. This will perform a busy waiting delay.
118//!
119//! ```rust,no_run
120//! use embedded_timers::clock::Clock;
121//! use embedded_hal::delay::DelayNs;
122//! #[derive(Debug)]
123//! pub struct MilliSecondClock;
124//! # impl embedded_timers::clock::Clock for MilliSecondClock {
125//! #     type Instant = std::time::Instant;
126//! #     fn now(&self) -> Self::Instant {
127//! #         std::time::Instant::now()
128//! #     }
129//! # }
130//!
131//! let clock = MilliSecondClock;
132//! let mut delay = embedded_timers::delay::Delay::new(&clock);
133//!
134//! loop {
135//!     println!("This shows every second");
136//!     delay.delay_ms(1000_u32);
137//! }
138//! ```
139//!
140//! ## Timer
141//!
142//! The crate provides a convenient timer interface with functionality to check if the timer
143//! `is_running` or `is_expired` and how much `duration_left`.
144//!
145//! ```rust,no_run
146//! use embedded_timers::clock::Clock;
147//! use embedded_timers::timer::Timer;
148//! #[derive(Debug)]
149//! pub struct MilliSecondClock;
150//! # impl embedded_timers::clock::Clock for MilliSecondClock {
151//! #     type Instant = std::time::Instant;
152//! #     fn now(&self) -> Self::Instant {
153//! #         std::time::Instant::now()
154//! #     }
155//! # }
156//!
157//! let clock = MilliSecondClock;
158//! let mut timer = embedded_timers::timer::Timer::new(&clock);
159//!
160//! timer.start(core::time::Duration::from_secs(1));
161//!
162//! loop {
163//!     if let Ok(expired) = timer.is_expired() {
164//!         if expired {
165//!             println!("This shows every second");
166//!             timer.start(core::time::Duration::from_secs(1));
167//!         }
168//!     }
169//! }
170//! ```
171//!
172//! The `embedded_timers::Timer` also implements a non-blocking _count-down_
173//! which utilizes [nb](https://docs.rs/nb/latest/nb/).
174//!
175//! ```rust,no_run
176//! use embedded_timers::clock::Clock;
177//! use embedded_timers::timer::Timer;
178//! use embedded_hal::delay::DelayNs;
179//! #[derive(Debug)]
180//! pub struct MilliSecondClock;
181//! # impl embedded_timers::clock::Clock for MilliSecondClock {
182//! #     type Instant = std::time::Instant;
183//! #     fn now(&self) -> Self::Instant {
184//! #         std::time::Instant::now()
185//! #     }
186//! # }
187//!
188//! let clock = MilliSecondClock;
189//! let mut timer = embedded_timers::timer::Timer::new(&clock);
190//! let mut delay = embedded_timers::delay::Delay::new(&clock);
191//!
192//! timer.start(core::time::Duration::from_secs(1));
193//!
194//! loop {
195//!     match timer.wait() {
196//!         Err(nb::Error::WouldBlock) => {
197//!             println!("Timer still running");
198//!             delay.delay_ms(50_u32);
199//!         }
200//!         Err(_) => panic!("TIMER ERROR"),
201//!         Ok(_) => {
202//!             println!("This shows after one second");
203//!             timer.start(core::time::Duration::from_secs(1));
204//!         }
205//!     }
206//! }
207//! ```
208//!
209//! # License
210//!
211//! Open Logistics Foundation License\
212//! Version 1.3, January 2023
213//!
214//! See the LICENSE file in the top-level directory.
215//!
216//! # Contact
217//!
218//! Fraunhofer IML Embedded Rust Group - <embedded-rust@iml.fraunhofer.de>
219
220#![cfg_attr(all(not(test), not(feature = "std")), no_std)]
221#![warn(missing_docs)]
222
223/// Defines the [`Clock`](clock::Clock) trait for clocks which are expected to be always running
224/// and never fail
225pub mod clock;
226
227pub mod instant;
228
229/// Timer implementation based on Clocks
230pub mod timer;
231
232/// Delays based on Clocks
233pub mod delay;