diff options
Diffstat (limited to 'embassy-time-driver/src/lib.rs')
| -rw-r--r-- | embassy-time-driver/src/lib.rs | 79 |
1 files changed, 60 insertions, 19 deletions
diff --git a/embassy-time-driver/src/lib.rs b/embassy-time-driver/src/lib.rs index 090969d8c..57a9f7587 100644 --- a/embassy-time-driver/src/lib.rs +++ b/embassy-time-driver/src/lib.rs | |||
| @@ -17,25 +17,7 @@ | |||
| 17 | //! Otherwise, don’t enable any `tick-hz-*` feature to let the user configure the tick rate themselves by | 17 | //! Otherwise, don’t enable any `tick-hz-*` feature to let the user configure the tick rate themselves by |
| 18 | //! enabling a feature on `embassy-time`. | 18 | //! enabling a feature on `embassy-time`. |
| 19 | //! | 19 | //! |
| 20 | //! # Linkage details | 20 | //! ### Example |
| 21 | //! | ||
| 22 | //! Instead of the usual "trait + generic params" approach, calls from embassy to the driver are done via `extern` functions. | ||
| 23 | //! | ||
| 24 | //! `embassy` internally defines the driver function as `extern "Rust" { fn _embassy_time_now() -> u64; }` and calls it. | ||
| 25 | //! The driver crate defines the function as `#[no_mangle] fn _embassy_time_now() -> u64`. The linker will resolve the | ||
| 26 | //! calls from the `embassy` crate to call into the driver crate. | ||
| 27 | //! | ||
| 28 | //! If there is none or multiple drivers in the crate tree, linking will fail. | ||
| 29 | //! | ||
| 30 | //! This method has a few key advantages for something as foundational as timekeeping: | ||
| 31 | //! | ||
| 32 | //! - The time driver is available everywhere easily, without having to thread the implementation | ||
| 33 | //! through generic parameters. This is especially helpful for libraries. | ||
| 34 | //! - It means comparing `Instant`s will always make sense: if there were multiple drivers | ||
| 35 | //! active, one could compare an `Instant` from driver A to an `Instant` from driver B, which | ||
| 36 | //! would yield incorrect results. | ||
| 37 | //! | ||
| 38 | //! # Example | ||
| 39 | //! | 21 | //! |
| 40 | //! ``` | 22 | //! ``` |
| 41 | //! use core::task::Waker; | 23 | //! use core::task::Waker; |
| @@ -56,6 +38,65 @@ | |||
| 56 | //! | 38 | //! |
| 57 | //! embassy_time_driver::time_driver_impl!(static DRIVER: MyDriver = MyDriver{}); | 39 | //! embassy_time_driver::time_driver_impl!(static DRIVER: MyDriver = MyDriver{}); |
| 58 | //! ``` | 40 | //! ``` |
| 41 | //! | ||
| 42 | //! ## Implementing the timer queue | ||
| 43 | //! | ||
| 44 | //! The simplest (but suboptimal) way to implement a timer queue is to define a single queue in the | ||
| 45 | //! time driver. Declare a field protected by an appropriate mutex (e.g. `critical_section::Mutex`). | ||
| 46 | //! | ||
| 47 | //! Then, you'll need to adapt the `schedule_wake` method to use this queue. | ||
| 48 | //! | ||
| 49 | //! ```ignore | ||
| 50 | //! use core::cell::RefCell; | ||
| 51 | //! use core::task::Waker; | ||
| 52 | //! | ||
| 53 | //! use embassy_time_queue_driver::Queue; | ||
| 54 | //! use embassy_time_driver::Driver; | ||
| 55 | //! | ||
| 56 | //! struct MyDriver { | ||
| 57 | //! timer_queue: critical_section::Mutex<RefCell<Queue>>, | ||
| 58 | //! } | ||
| 59 | //! | ||
| 60 | //! impl MyDriver { | ||
| 61 | //! fn set_alarm(&self, cs: &CriticalSection, at: u64) -> bool { | ||
| 62 | //! todo!() | ||
| 63 | //! } | ||
| 64 | //! } | ||
| 65 | //! | ||
| 66 | //! impl Driver for MyDriver { | ||
| 67 | //! // fn now(&self) -> u64 { ... } | ||
| 68 | //! | ||
| 69 | //! fn schedule_wake(&self, at: u64, waker: &Waker) { | ||
| 70 | //! critical_section::with(|cs| { | ||
| 71 | //! let mut queue = self.queue.borrow(cs).borrow_mut(); | ||
| 72 | //! if queue.schedule_wake(at, waker) { | ||
| 73 | //! let mut next = queue.next_expiration(self.now()); | ||
| 74 | //! while !self.set_alarm(cs, next) { | ||
| 75 | //! next = queue.next_expiration(self.now()); | ||
| 76 | //! } | ||
| 77 | //! } | ||
| 78 | //! }); | ||
| 79 | //! } | ||
| 80 | //! } | ||
| 81 | //! ``` | ||
| 82 | //! | ||
| 83 | //! # Linkage details | ||
| 84 | //! | ||
| 85 | //! Instead of the usual "trait + generic params" approach, calls from embassy to the driver are done via `extern` functions. | ||
| 86 | //! | ||
| 87 | //! `embassy` internally defines the driver function as `extern "Rust" { fn _embassy_time_now() -> u64; }` and calls it. | ||
| 88 | //! The driver crate defines the function as `#[no_mangle] fn _embassy_time_now() -> u64`. The linker will resolve the | ||
| 89 | //! calls from the `embassy` crate to call into the driver crate. | ||
| 90 | //! | ||
| 91 | //! If there is none or multiple drivers in the crate tree, linking will fail. | ||
| 92 | //! | ||
| 93 | //! This method has a few key advantages for something as foundational as timekeeping: | ||
| 94 | //! | ||
| 95 | //! - The time driver is available everywhere easily, without having to thread the implementation | ||
| 96 | //! through generic parameters. This is especially helpful for libraries. | ||
| 97 | //! - It means comparing `Instant`s will always make sense: if there were multiple drivers | ||
| 98 | //! active, one could compare an `Instant` from driver A to an `Instant` from driver B, which | ||
| 99 | //! would yield incorrect results. | ||
| 59 | 100 | ||
| 60 | //! ## Feature flags | 101 | //! ## Feature flags |
| 61 | #![doc = document_features::document_features!(feature_label = r#"<span class="stab portability"><code>{feature}</code></span>"#)] | 102 | #![doc = document_features::document_features!(feature_label = r#"<span class="stab portability"><code>{feature}</code></span>"#)] |
