[][src]Crate embedded_time

embedded-time provides a comprehensive library of Duration and Rate types as well as a Clock abstractions for hardware timers/clocks and the associated Instant type for in embedded systems.

Additionally, an implementation of software timers is provided that work seemlessly with all the types in this crate.

use embedded_time::{duration::*, rate::*};

let micros = 200_000_u32.microseconds();                // 200_000 μs
let millis: Milliseconds = micros.into();               // 200 ms
let frequency: Result<Hertz,_> = millis.to_rate();      // 5 Hz

assert_eq!(frequency, Ok(5_u32.Hz()));

Motivation

The handling of time on embedded systems is generally much different than that of OSs. For instance, on an OS, the time is measured against an arbitrary epoch. Embedded systems generally don't know (nor do they care) what the real time is, but rather how much time has passed since the system has started.

Drawbacks of the standard library types

Duration

Instant

Drawbacks of the time crate

The time crate is a remarkable library but isn't geared for embedded systems (although it does support a subset of features in no_std contexts). It suffers from some of the same drawbacks as the core::Duration type (namely the storage format) and the Instant struct dependency on std. It also adds a lot of functionally that would seldom be useful in an embedded context. For instance it has a comprehensive date/time formatting, timezone, and calendar support.

Background

What is an Instant?

In the Rust ecosystem, it appears to be idiomatic to call a now() associated function from an Instant type. There is generally no concept of a "Clock". I believe that using the Instant in this way is a violation of the separation of concerns principle. What is an Instant? Is it a time-keeping entity from which you read the current instant in time, or is it that instant in time itself. In this case, it's both.

As an alternative, the current instant in time is read from a Clock. The Instant read from the Clock has the same precision and width (inner type) as the Clock. Requesting the difference between two Instants gives a Duration which can have different precision and/or width.

Overview

The approach taken is similar to the C++ chrono library. Durations and Rates are fixed-point values as in they are comprised of integer and scaling factor values. The scaling factor is a const Fraction. One benefit of this structure is that it avoids unnecessary arithmetic. For example, if the Duration type is Milliseconds, a call to the Duration::integer() method simply returns the integer part directly which in the case is the number of milliseconds represented by the Duration. Conversion arithmetic is only performed when explicitly converting between time units (eg. Milliseconds --> Seconds).

In addition, a wide range of rate-type types are available including Hertz, BitsPerSecond, KibibytesPerSecond, Baud, etc.

A Duration type can be converted to a Rate type and vica-versa.

Definitions

Clock: Any entity that periodically counts (ie an external or peripheral hardware timer/counter). Generally, this needs to be monotonic. A wrapping clock is considered monotonic in this context as long as it fulfills the other requirements.

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 instants. The time that has elapsed since an instant. A span of time.

Rate: A measure of events per time such as frequency, data-rate, etc.

Imports

The suggested use statements are as follows depending on what is needed:

use embedded_time::duration::*;    // imports all duration-related types and traits
use embedded_time::rate::*;        // imports all rate-related types and traits
use embedded_time::clock;
use embedded_time::Instant;
use embedded_time::Timer;

Duration Types

UnitsExtension
Hourshours
Minutesminutes
Secondsseconds
Millisecondsmilliseconds
Microsecondsmicroseconds
Nanosecondsnanoseconds
use embedded_time::{duration::*, rate::*};

Microseconds(500_u32).to_rate() == Ok(Kilohertz(2_u32))
use embedded_time::{duration::*};

Seconds(2_u64).to_generic(Fraction::new(1, 2_000)) == Ok(Generic::new(4_000_u32, Fraction::new(1, 2_000)))
Seconds::<u64>::try_from(Generic::new(2_000_u32, Fraction::new(1, 1_000))) == Ok(Seconds(2_u64))

core Compatibility

Benchmark Comparisons to core duration type

Construct and Read Milliseconds

use embedded_time::duration::*;

let duration = Milliseconds::<u64>(ms); // 8 bytes
let count = duration.integer();

(the size of embedded-time duration types is only the size of the inner type)

use std::time::Duration;

let core_duration = Duration::from_millis(ms); // 12 bytes
let count = core_duration.as_millis();

(the size of core duration type is 12 B)

Rate Types

Frequency

UnitsExtension
MebihertzMiHz
MegahertzMHz
KibihertzKiHz
KilohertzkHz
HertzHz

Data Rate

UnitsExtension
MebibytePerSecondMiBps
MegabytePerSecondMBps
KibibytePerSecondKiBps
KiloBytePerSecondKBps
BytePerSecondBps
MebibitPerSecondMibps
MegabitPerSecondMbps
KibibitPerSecondKibps
KilobitPerSecondkbps
BitPerSecondbps

Symbol Rate

UnitsExtension
MebibaudMiBd
MegabaudMBd
KibibaudKiBd
KilobaudkBd
BaudBd
use embedded_time::{duration::*, rate::*};

Kilohertz(500_u32).to_duration() == Ok(Microseconds(2_u32))
use embedded_time::rate::*;

Hertz(2_u64).to_generic(Fraction::new(1,2_000)) == Ok(Generic::new(4_000_u32, Fraction::new(1,2_000)))
Hertz::<u64>::try_from(Generic::new(2_000_u32, Fraction::new(1,1_000))) == Ok(Hertz(2_u64))

Hardware Abstraction

Timers

Reliability and Usability

Notes

Some parts of this crate were derived from various sources:

Re-exports

pub use clock::Clock;

Modules

clock

Abstraction for hardware timers/clocks

duration

Duration types/units

fixed_point

Fixed-point values

fraction

Fractional/Rational values

rate

Rate-based types/units

Structs

Instant

Represents an instant of time relative to a specific Clock

Timer

A Timer counts toward an expiration, can be polled for elapsed and remaining time, and can be one-shot or continuous/periodic.

Enums

ConversionError

Conversion errors

TimeError

Crate errors