1 unstable release
0.1.0 | Jan 11, 2024 |
---|
#2077 in Embedded development
34,238 downloads per month
Used in 98 crates
(14 directly)
43KB
610 lines
embassy-time-driver
This crate contains the driver trait necessary for adding embassy-time
support
for a new hardware platform.
If you want to use embassy-time
with already made drivers, you should depend on the main embassy-time
crate, not on this crate.
If you are writing a driver, you should depend only on this crate, not on the main embassy-time
crate.
This will allow your driver to continue working for newer embassy-time
major versions, without needing an update,
if the driver trait has not had breaking changes.
How it works
embassy-time
is backed by a global "time driver" specified at build time.
Only one driver can be active in a program.
All methods and structs transparently call into the active driver. This makes it
possible for libraries to use embassy-time
in a driver-agnostic way without
requiring generic parameters.
lib.rs
:
Implementing a driver
- Define a struct
MyDriver
- Implement
Driver
for it - Register it as the global driver with
time_driver_impl
.
If your driver has a single set tick rate, enable the corresponding tick-hz-*
feature,
which will prevent users from needing to configure it themselves (or selecting an incorrect configuration).
If your driver supports a small number of set tick rates, expose your own cargo features and have each one
enable the corresponding embassy-time-driver/tick-*
.
Otherwise, don’t enable any tick-hz-*
feature to let the user configure the tick rate themselves by
enabling a feature on embassy-time
.
Linkage details
Instead of the usual "trait + generic params" approach, calls from embassy to the driver are done via extern
functions.
embassy
internally defines the driver functions as extern "Rust" { fn _embassy_time_now() -> u64; }
and calls them.
The driver crate defines the functions as #[no_mangle] fn _embassy_time_now() -> u64
. The linker will resolve the
calls from the embassy
crate to call into the driver crate.
If there is none or multiple drivers in the crate tree, linking will fail.
This method has a few key advantages for something as foundational as timekeeping:
- The time driver is available everywhere easily, without having to thread the implementation through generic parameters. This is especially helpful for libraries.
- It means comparing
Instant
s will always make sense: if there were multiple drivers active, one could compare anInstant
from driver A to anInstant
from driver B, which would yield incorrect results.
Example
use embassy_time_driver::{Driver, AlarmHandle};
struct MyDriver{} // not public!
impl Driver for MyDriver {
fn now(&self) -> u64 {
todo!()
}
unsafe fn allocate_alarm(&self) -> Option<AlarmHandle> {
todo!()
}
fn set_alarm_callback(&self, alarm: AlarmHandle, callback: fn(*mut ()), ctx: *mut ()) {
todo!()
}
fn set_alarm(&self, alarm: AlarmHandle, timestamp: u64) -> bool {
todo!()
}
}
embassy_time_driver::time_driver_impl!(static DRIVER: MyDriver = MyDriver{});
Feature flags
Dependencies
~175KB