On a microcontroller¶
exact_units is no_std, needs no allocator, and has no dependencies unless a feature asks for one.
Pick the features¶
The defaults (std, shrink_wrap) suit a host. A firmware turns them off and picks its unit groups:
[dependencies]
exact_units = { version = "0.1", default-features = false, features = ["time", "electrical", "defmt"] }
| Feature | What it adds |
|---|---|
| none | Fixed, Point, Magnitude, the markers that need no group (One, Milli, ...) |
time, electrical, ... |
the unit groups, see Units and dimensions |
defmt |
defmt::Format for every type |
libm |
to_db / from_db without std, from the pure-Rust libm |
shrink_wrap |
the wire format, see The wire |
serde |
serde for the numbers, no_std without alloc, see The wire |
std |
every unit group but customary; the owned wire types |
Nothing at run time¶
Everything about units is decided at compile time:
- A scale is a type parameter.
- A conversion factor is a constant the compiler works out.
- A literal (
fixed!,db!) is a constant.
So a Fixed costs what its integer costs. Cost on Cortex-M has the instruction counts.
What CI builds¶
thumbv6m-none-eabi(Cortex-M0, no FPU, no 64-bit multiply) withoutstd.thumbv7em-none-eabihf(Cortex-M4F) withlibm, for levels in dB in hardwaref32.
A firmware that builds for an M0 builds for anything bigger.
A first firmware value¶
use exact_units::{Fixed, Micros, Millis, prefix::Micro};
/// A timer compare value from a timeout in ms: exact, or refused at the edge.
fn compare_value(timeout: Millis<u16>) -> Option<Micros<u32>> {
timeout.to::<Micro, u32>()
}
assert_eq!(compare_value(Fixed::new(5)), Some(Fixed::new(5000)));
The examples are small firmware stories in this style.