aboutsummaryrefslogtreecommitdiff
path: root/embassy-time/src
diff options
context:
space:
mode:
Diffstat (limited to 'embassy-time/src')
-rw-r--r--embassy-time/src/delay.rs1
-rw-r--r--embassy-time/src/driver.rs175
-rw-r--r--embassy-time/src/driver_mock.rs191
-rw-r--r--embassy-time/src/driver_std.rs5
-rw-r--r--embassy-time/src/driver_wasm.rs5
-rw-r--r--embassy-time/src/fmt.rs3
-rw-r--r--embassy-time/src/instant.rs6
-rw-r--r--embassy-time/src/lib.rs19
-rw-r--r--embassy-time/src/queue.rs58
-rw-r--r--embassy-time/src/queue_generic.rs112
-rw-r--r--embassy-time/src/tick.rs482
-rw-r--r--embassy-time/src/timer.rs50
12 files changed, 229 insertions, 878 deletions
diff --git a/embassy-time/src/delay.rs b/embassy-time/src/delay.rs
index 7ef5961f0..f77859d4a 100644
--- a/embassy-time/src/delay.rs
+++ b/embassy-time/src/delay.rs
@@ -13,6 +13,7 @@ pub fn block_for(duration: Duration) {
13/// the amount provided, but accuracy can be affected by many factors, including interrupt usage. 13/// the amount provided, but accuracy can be affected by many factors, including interrupt usage.
14/// Make sure to use a suitable tick rate for your use case. The tick rate is defined by the currently 14/// Make sure to use a suitable tick rate for your use case. The tick rate is defined by the currently
15/// active driver. 15/// active driver.
16#[derive(Clone)]
16pub struct Delay; 17pub struct Delay;
17 18
18impl embedded_hal_1::delay::DelayNs for Delay { 19impl embedded_hal_1::delay::DelayNs for Delay {
diff --git a/embassy-time/src/driver.rs b/embassy-time/src/driver.rs
deleted file mode 100644
index 5fe7becaf..000000000
--- a/embassy-time/src/driver.rs
+++ /dev/null
@@ -1,175 +0,0 @@
1//! Time driver interface
2//!
3//! This module defines the interface a driver needs to implement to power the `embassy_time` module.
4//!
5//! # Implementing a driver
6//!
7//! - Define a struct `MyDriver`
8//! - Implement [`Driver`] for it
9//! - Register it as the global driver with [`time_driver_impl`](crate::time_driver_impl).
10//! - Enable the Cargo features `embassy-executor/time` and one of `embassy-time/tick-*` corresponding to the
11//! tick rate of your driver.
12//!
13//! If you wish to make the tick rate configurable by the end user, you should do so by exposing your own
14//! Cargo features and having each enable the corresponding `embassy-time/tick-*`.
15//!
16//! # Linkage details
17//!
18//! Instead of the usual "trait + generic params" approach, calls from embassy to the driver are done via `extern` functions.
19//!
20//! `embassy` internally defines the driver functions as `extern "Rust" { fn _embassy_time_now() -> u64; }` and calls them.
21//! The driver crate defines the functions as `#[no_mangle] fn _embassy_time_now() -> u64`. The linker will resolve the
22//! calls from the `embassy` crate to call into the driver crate.
23//!
24//! If there is none or multiple drivers in the crate tree, linking will fail.
25//!
26//! This method has a few key advantages for something as foundational as timekeeping:
27//!
28//! - The time driver is available everywhere easily, without having to thread the implementation
29//! through generic parameters. This is especially helpful for libraries.
30//! - It means comparing `Instant`s will always make sense: if there were multiple drivers
31//! active, one could compare an `Instant` from driver A to an `Instant` from driver B, which
32//! would yield incorrect results.
33//!
34//! # Example
35//!
36//! ```
37//! use embassy_time::driver::{Driver, AlarmHandle};
38//!
39//! struct MyDriver{} // not public!
40//! embassy_time::time_driver_impl!(static DRIVER: MyDriver = MyDriver{});
41//!
42//! impl Driver for MyDriver {
43//! fn now(&self) -> u64 {
44//! todo!()
45//! }
46//! unsafe fn allocate_alarm(&self) -> Option<AlarmHandle> {
47//! todo!()
48//! }
49//! fn set_alarm_callback(&self, alarm: AlarmHandle, callback: fn(*mut ()), ctx: *mut ()) {
50//! todo!()
51//! }
52//! fn set_alarm(&self, alarm: AlarmHandle, timestamp: u64) -> bool {
53//! todo!()
54//! }
55//! }
56//! ```
57
58/// Alarm handle, assigned by the driver.
59#[derive(Clone, Copy)]
60pub struct AlarmHandle {
61 id: u8,
62}
63
64impl AlarmHandle {
65 /// Create an AlarmHandle
66 ///
67 /// Safety: May only be called by the current global Driver impl.
68 /// The impl is allowed to rely on the fact that all `AlarmHandle` instances
69 /// are created by itself in unsafe code (e.g. indexing operations)
70 pub unsafe fn new(id: u8) -> Self {
71 Self { id }
72 }
73
74 /// Get the ID of the AlarmHandle.
75 pub fn id(&self) -> u8 {
76 self.id
77 }
78}
79
80/// Time driver
81pub trait Driver: Send + Sync + 'static {
82 /// Return the current timestamp in ticks.
83 ///
84 /// Implementations MUST ensure that:
85 /// - This is guaranteed to be monotonic, i.e. a call to now() will always return
86 /// a greater or equal value than earler calls. Time can't "roll backwards".
87 /// - It "never" overflows. It must not overflow in a sufficiently long time frame, say
88 /// in 10_000 years (Human civilization is likely to already have self-destructed
89 /// 10_000 years from now.). This means if your hardware only has 16bit/32bit timers
90 /// you MUST extend them to 64-bit, for example by counting overflows in software,
91 /// or chaining multiple timers together.
92 fn now(&self) -> u64;
93
94 /// Try allocating an alarm handle. Returns None if no alarms left.
95 /// Initially the alarm has no callback set, and a null `ctx` pointer.
96 ///
97 /// # Safety
98 /// It is UB to make the alarm fire before setting a callback.
99 unsafe fn allocate_alarm(&self) -> Option<AlarmHandle>;
100
101 /// Sets the callback function to be called when the alarm triggers.
102 /// The callback may be called from any context (interrupt or thread mode).
103 fn set_alarm_callback(&self, alarm: AlarmHandle, callback: fn(*mut ()), ctx: *mut ());
104
105 /// Sets an alarm at the given timestamp. When the current timestamp reaches the alarm
106 /// timestamp, the provided callback function will be called.
107 ///
108 /// The `Driver` implementation should guarantee that the alarm callback is never called synchronously from `set_alarm`.
109 /// Rather - if `timestamp` is already in the past - `false` should be returned and alarm should not be set,
110 /// or alternatively, the driver should return `true` and arrange to call the alarm callback as soon as possible, but not synchronously.
111 ///
112 /// When callback is called, it is guaranteed that now() will return a value greater or equal than timestamp.
113 ///
114 /// Only one alarm can be active at a time for each AlarmHandle. This overwrites any previously-set alarm if any.
115 fn set_alarm(&self, alarm: AlarmHandle, timestamp: u64) -> bool;
116}
117
118extern "Rust" {
119 fn _embassy_time_now() -> u64;
120 fn _embassy_time_allocate_alarm() -> Option<AlarmHandle>;
121 fn _embassy_time_set_alarm_callback(alarm: AlarmHandle, callback: fn(*mut ()), ctx: *mut ());
122 fn _embassy_time_set_alarm(alarm: AlarmHandle, timestamp: u64) -> bool;
123}
124
125/// See [`Driver::now`]
126pub fn now() -> u64 {
127 unsafe { _embassy_time_now() }
128}
129
130/// See [`Driver::allocate_alarm`]
131///
132/// Safety: it is UB to make the alarm fire before setting a callback.
133pub unsafe fn allocate_alarm() -> Option<AlarmHandle> {
134 _embassy_time_allocate_alarm()
135}
136
137/// See [`Driver::set_alarm_callback`]
138pub fn set_alarm_callback(alarm: AlarmHandle, callback: fn(*mut ()), ctx: *mut ()) {
139 unsafe { _embassy_time_set_alarm_callback(alarm, callback, ctx) }
140}
141
142/// See [`Driver::set_alarm`]
143pub fn set_alarm(alarm: AlarmHandle, timestamp: u64) -> bool {
144 unsafe { _embassy_time_set_alarm(alarm, timestamp) }
145}
146
147/// Set the time Driver implementation.
148///
149/// See the module documentation for an example.
150#[macro_export]
151macro_rules! time_driver_impl {
152 (static $name:ident: $t: ty = $val:expr) => {
153 static $name: $t = $val;
154
155 #[no_mangle]
156 fn _embassy_time_now() -> u64 {
157 <$t as $crate::driver::Driver>::now(&$name)
158 }
159
160 #[no_mangle]
161 unsafe fn _embassy_time_allocate_alarm() -> Option<$crate::driver::AlarmHandle> {
162 <$t as $crate::driver::Driver>::allocate_alarm(&$name)
163 }
164
165 #[no_mangle]
166 fn _embassy_time_set_alarm_callback(alarm: $crate::driver::AlarmHandle, callback: fn(*mut ()), ctx: *mut ()) {
167 <$t as $crate::driver::Driver>::set_alarm_callback(&$name, alarm, callback, ctx)
168 }
169
170 #[no_mangle]
171 fn _embassy_time_set_alarm(alarm: $crate::driver::AlarmHandle, timestamp: u64) -> bool {
172 <$t as $crate::driver::Driver>::set_alarm(&$name, alarm, timestamp)
173 }
174 };
175}
diff --git a/embassy-time/src/driver_mock.rs b/embassy-time/src/driver_mock.rs
index c255615c7..8587f9172 100644
--- a/embassy-time/src/driver_mock.rs
+++ b/embassy-time/src/driver_mock.rs
@@ -1,14 +1,14 @@
1use core::cell::Cell; 1use core::cell::RefCell;
2 2
3use critical_section::Mutex as CsMutex; 3use critical_section::Mutex as CsMutex;
4use embassy_time_driver::{AlarmHandle, Driver};
4 5
5use crate::driver::{AlarmHandle, Driver};
6use crate::{Duration, Instant}; 6use crate::{Duration, Instant};
7 7
8/// A mock driver that can be manually advanced. 8/// A mock driver that can be manually advanced.
9/// This is useful for testing code that works with [`Instant`] and [`Duration`]. 9/// This is useful for testing code that works with [`Instant`] and [`Duration`].
10/// 10///
11/// This driver cannot currently be used to test runtime functionality, such as 11/// This driver can also be used to test runtime functionality, such as
12/// timers, delays, etc. 12/// timers, delays, etc.
13/// 13///
14/// # Example 14/// # Example
@@ -26,43 +26,196 @@ use crate::{Duration, Instant};
26/// assert_eq!(true, has_a_second_passed(reference)); 26/// assert_eq!(true, has_a_second_passed(reference));
27/// } 27/// }
28/// ``` 28/// ```
29pub struct MockDriver { 29pub struct MockDriver(CsMutex<RefCell<InnerMockDriver>>);
30 now: CsMutex<Cell<Instant>>,
31}
32 30
33crate::time_driver_impl!(static DRIVER: MockDriver = MockDriver { 31embassy_time_driver::time_driver_impl!(static DRIVER: MockDriver = MockDriver::new());
34 now: CsMutex::new(Cell::new(Instant::from_ticks(0))),
35});
36 32
37impl MockDriver { 33impl MockDriver {
34 /// Creates a new mock driver.
35 pub const fn new() -> Self {
36 Self(CsMutex::new(RefCell::new(InnerMockDriver::new())))
37 }
38
38 /// Gets a reference to the global mock driver. 39 /// Gets a reference to the global mock driver.
39 pub fn get() -> &'static MockDriver { 40 pub fn get() -> &'static MockDriver {
40 &DRIVER 41 &DRIVER
41 } 42 }
42 43
43 /// Advances the time by the specified [`Duration`]. 44 /// Resets the internal state of the mock driver
44 pub fn advance(&self, duration: Duration) { 45 /// This will clear and deallocate all alarms, and reset the current time to 0.
46 pub fn reset(&self) {
45 critical_section::with(|cs| { 47 critical_section::with(|cs| {
46 let now = self.now.borrow(cs).get().as_ticks(); 48 self.0.borrow(cs).replace(InnerMockDriver::new());
47 self.now.borrow(cs).set(Instant::from_ticks(now + duration.as_ticks()));
48 }); 49 });
49 } 50 }
51
52 /// Advances the time by the specified [`Duration`].
53 /// Calling any alarm callbacks that are due.
54 pub fn advance(&self, duration: Duration) {
55 let notify = {
56 critical_section::with(|cs| {
57 let mut inner = self.0.borrow_ref_mut(cs);
58
59 inner.now += duration;
60
61 let now = inner.now.as_ticks();
62
63 inner
64 .alarm
65 .as_mut()
66 .filter(|alarm| alarm.timestamp <= now)
67 .map(|alarm| {
68 alarm.timestamp = u64::MAX;
69
70 (alarm.callback, alarm.ctx)
71 })
72 })
73 };
74
75 if let Some((callback, ctx)) = notify {
76 (callback)(ctx);
77 }
78 }
50} 79}
51 80
52impl Driver for MockDriver { 81impl Driver for MockDriver {
53 fn now(&self) -> u64 { 82 fn now(&self) -> u64 {
54 critical_section::with(|cs| self.now.borrow(cs).get().as_ticks() as u64) 83 critical_section::with(|cs| self.0.borrow_ref(cs).now).as_ticks()
55 } 84 }
56 85
57 unsafe fn allocate_alarm(&self) -> Option<AlarmHandle> { 86 unsafe fn allocate_alarm(&self) -> Option<AlarmHandle> {
58 unimplemented!("MockDriver does not support runtime features that require an executor"); 87 critical_section::with(|cs| {
88 let mut inner = self.0.borrow_ref_mut(cs);
89
90 if inner.alarm.is_some() {
91 None
92 } else {
93 inner.alarm.replace(AlarmState::new());
94
95 Some(AlarmHandle::new(0))
96 }
97 })
59 } 98 }
60 99
61 fn set_alarm_callback(&self, _alarm: AlarmHandle, _callback: fn(*mut ()), _ctx: *mut ()) { 100 fn set_alarm_callback(&self, _alarm: AlarmHandle, callback: fn(*mut ()), ctx: *mut ()) {
62 unimplemented!("MockDriver does not support runtime features that require an executor"); 101 critical_section::with(|cs| {
102 let mut inner = self.0.borrow_ref_mut(cs);
103
104 let Some(alarm) = inner.alarm.as_mut() else {
105 panic!("Alarm not allocated");
106 };
107
108 alarm.callback = callback;
109 alarm.ctx = ctx;
110 });
63 } 111 }
64 112
65 fn set_alarm(&self, _alarm: AlarmHandle, _timestamp: u64) -> bool { 113 fn set_alarm(&self, _alarm: AlarmHandle, timestamp: u64) -> bool {
66 unimplemented!("MockDriver does not support runtime features that require an executor"); 114 critical_section::with(|cs| {
115 let mut inner = self.0.borrow_ref_mut(cs);
116
117 if timestamp <= inner.now.as_ticks() {
118 false
119 } else {
120 let Some(alarm) = inner.alarm.as_mut() else {
121 panic!("Alarm not allocated");
122 };
123
124 alarm.timestamp = timestamp;
125 true
126 }
127 })
128 }
129}
130
131struct InnerMockDriver {
132 now: Instant,
133 alarm: Option<AlarmState>,
134}
135
136impl InnerMockDriver {
137 const fn new() -> Self {
138 Self {
139 now: Instant::from_ticks(0),
140 alarm: None,
141 }
142 }
143}
144
145struct AlarmState {
146 timestamp: u64,
147 callback: fn(*mut ()),
148 ctx: *mut (),
149}
150
151impl AlarmState {
152 const fn new() -> Self {
153 Self {
154 timestamp: u64::MAX,
155 callback: Self::noop,
156 ctx: core::ptr::null_mut(),
157 }
158 }
159
160 fn noop(_ctx: *mut ()) {}
161}
162
163unsafe impl Send for AlarmState {}
164
165#[cfg(test)]
166mod tests {
167 use serial_test::serial;
168
169 use super::*;
170
171 fn setup() {
172 DRIVER.reset();
173 }
174
175 #[test]
176 #[serial]
177 fn test_advance() {
178 setup();
179
180 let driver = MockDriver::get();
181 let reference = driver.now();
182 driver.advance(Duration::from_secs(1));
183 assert_eq!(Duration::from_secs(1).as_ticks(), driver.now() - reference);
184 }
185
186 #[test]
187 #[serial]
188 fn test_set_alarm_not_in_future() {
189 setup();
190
191 let driver = MockDriver::get();
192 let alarm = unsafe { AlarmHandle::new(0) };
193 assert_eq!(false, driver.set_alarm(alarm, driver.now()));
194 }
195
196 #[test]
197 #[serial]
198 fn test_alarm() {
199 setup();
200
201 let driver = MockDriver::get();
202 let alarm = unsafe { driver.allocate_alarm() }.expect("No alarms available");
203 static mut CALLBACK_CALLED: bool = false;
204 let ctx = &mut () as *mut ();
205 driver.set_alarm_callback(alarm, |_| unsafe { CALLBACK_CALLED = true }, ctx);
206 driver.set_alarm(alarm, driver.now() + 1);
207 assert_eq!(false, unsafe { CALLBACK_CALLED });
208 driver.advance(Duration::from_secs(1));
209 assert_eq!(true, unsafe { CALLBACK_CALLED });
210 }
211
212 #[test]
213 #[serial]
214 fn test_allocate_alarm() {
215 setup();
216
217 let driver = MockDriver::get();
218 assert!(unsafe { driver.allocate_alarm() }.is_some());
219 assert!(unsafe { driver.allocate_alarm() }.is_none());
67 } 220 }
68} 221}
diff --git a/embassy-time/src/driver_std.rs b/embassy-time/src/driver_std.rs
index 32db47a37..d182f8331 100644
--- a/embassy-time/src/driver_std.rs
+++ b/embassy-time/src/driver_std.rs
@@ -6,8 +6,7 @@ use std::time::{Duration as StdDuration, Instant as StdInstant};
6use std::{mem, ptr, thread}; 6use std::{mem, ptr, thread};
7 7
8use critical_section::Mutex as CsMutex; 8use critical_section::Mutex as CsMutex;
9 9use embassy_time_driver::{AlarmHandle, Driver};
10use crate::driver::{AlarmHandle, Driver};
11 10
12const ALARM_COUNT: usize = 4; 11const ALARM_COUNT: usize = 4;
13 12
@@ -45,7 +44,7 @@ struct TimeDriver {
45} 44}
46 45
47const ALARM_NEW: AlarmState = AlarmState::new(); 46const ALARM_NEW: AlarmState = AlarmState::new();
48crate::time_driver_impl!(static DRIVER: TimeDriver = TimeDriver { 47embassy_time_driver::time_driver_impl!(static DRIVER: TimeDriver = TimeDriver {
49 alarm_count: AtomicU8::new(0), 48 alarm_count: AtomicU8::new(0),
50 49
51 once: Once::new(), 50 once: Once::new(),
diff --git a/embassy-time/src/driver_wasm.rs b/embassy-time/src/driver_wasm.rs
index 0f672dc75..ad884f060 100644
--- a/embassy-time/src/driver_wasm.rs
+++ b/embassy-time/src/driver_wasm.rs
@@ -4,11 +4,10 @@ use std::mem::MaybeUninit;
4use std::ptr; 4use std::ptr;
5use std::sync::{Mutex, Once}; 5use std::sync::{Mutex, Once};
6 6
7use embassy_time_driver::{AlarmHandle, Driver};
7use wasm_bindgen::prelude::*; 8use wasm_bindgen::prelude::*;
8use wasm_timer::Instant as StdInstant; 9use wasm_timer::Instant as StdInstant;
9 10
10use crate::driver::{AlarmHandle, Driver};
11
12const ALARM_COUNT: usize = 4; 11const ALARM_COUNT: usize = 4;
13 12
14struct AlarmState { 13struct AlarmState {
@@ -42,7 +41,7 @@ struct TimeDriver {
42} 41}
43 42
44const ALARM_NEW: AlarmState = AlarmState::new(); 43const ALARM_NEW: AlarmState = AlarmState::new();
45crate::time_driver_impl!(static DRIVER: TimeDriver = TimeDriver { 44embassy_time_driver::time_driver_impl!(static DRIVER: TimeDriver = TimeDriver {
46 alarm_count: AtomicU8::new(0), 45 alarm_count: AtomicU8::new(0),
47 once: Once::new(), 46 once: Once::new(),
48 alarms: UninitCell::uninit(), 47 alarms: UninitCell::uninit(),
diff --git a/embassy-time/src/fmt.rs b/embassy-time/src/fmt.rs
index 78e583c1c..2ac42c557 100644
--- a/embassy-time/src/fmt.rs
+++ b/embassy-time/src/fmt.rs
@@ -1,5 +1,5 @@
1#![macro_use] 1#![macro_use]
2#![allow(unused_macros)] 2#![allow(unused)]
3 3
4use core::fmt::{Debug, Display, LowerHex}; 4use core::fmt::{Debug, Display, LowerHex};
5 5
@@ -229,7 +229,6 @@ impl<T, E> Try for Result<T, E> {
229 } 229 }
230} 230}
231 231
232#[allow(unused)]
233pub(crate) struct Bytes<'a>(pub &'a [u8]); 232pub(crate) struct Bytes<'a>(pub &'a [u8]);
234 233
235impl<'a> Debug for Bytes<'a> { 234impl<'a> Debug for Bytes<'a> {
diff --git a/embassy-time/src/instant.rs b/embassy-time/src/instant.rs
index 5571cdd15..909f1b173 100644
--- a/embassy-time/src/instant.rs
+++ b/embassy-time/src/instant.rs
@@ -1,7 +1,7 @@
1use core::fmt; 1use core::fmt;
2use core::ops::{Add, AddAssign, Sub, SubAssign}; 2use core::ops::{Add, AddAssign, Sub, SubAssign};
3 3
4use super::{driver, Duration, GCD_1K, GCD_1M, TICK_HZ}; 4use super::{Duration, GCD_1K, GCD_1M, TICK_HZ};
5 5
6#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord)] 6#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord)]
7#[cfg_attr(feature = "defmt", derive(defmt::Format))] 7#[cfg_attr(feature = "defmt", derive(defmt::Format))]
@@ -18,7 +18,9 @@ impl Instant {
18 18
19 /// Returns an Instant representing the current time. 19 /// Returns an Instant representing the current time.
20 pub fn now() -> Instant { 20 pub fn now() -> Instant {
21 Instant { ticks: driver::now() } 21 Instant {
22 ticks: embassy_time_driver::now(),
23 }
22 } 24 }
23 25
24 /// Create an Instant from a tick count since system boot. 26 /// Create an Instant from a tick count since system boot.
diff --git a/embassy-time/src/lib.rs b/embassy-time/src/lib.rs
index 82a7ee0df..3c8575ee9 100644
--- a/embassy-time/src/lib.rs
+++ b/embassy-time/src/lib.rs
@@ -1,20 +1,18 @@
1#![cfg_attr(not(any(feature = "std", feature = "wasm", test)), no_std)] 1#![cfg_attr(not(any(feature = "std", feature = "wasm", test)), no_std)]
2#![cfg_attr(nightly, feature(async_fn_in_trait, impl_trait_projections))]
3#![cfg_attr(nightly, allow(stable_features, unknown_lints))]
4#![allow(async_fn_in_trait)] 2#![allow(async_fn_in_trait)]
5#![doc = include_str!("../README.md")] 3#![doc = include_str!("../README.md")]
6#![allow(clippy::new_without_default)] 4#![allow(clippy::new_without_default)]
7#![warn(missing_docs)] 5#![warn(missing_docs)]
8 6
7//! ## Feature flags
8#![doc = document_features::document_features!(feature_label = r#"<span class="stab portability"><code>{feature}</code></span>"#)]
9
9// This mod MUST go first, so that the others see its macros. 10// This mod MUST go first, so that the others see its macros.
10pub(crate) mod fmt; 11pub(crate) mod fmt;
11 12
12mod delay; 13mod delay;
13pub mod driver;
14mod duration; 14mod duration;
15mod instant; 15mod instant;
16pub mod queue;
17mod tick;
18mod timer; 16mod timer;
19 17
20#[cfg(feature = "mock-driver")] 18#[cfg(feature = "mock-driver")]
@@ -32,16 +30,9 @@ mod queue_generic;
32 30
33pub use delay::{block_for, Delay}; 31pub use delay::{block_for, Delay};
34pub use duration::Duration; 32pub use duration::Duration;
33pub use embassy_time_driver::TICK_HZ;
35pub use instant::Instant; 34pub use instant::Instant;
36pub use timer::{with_timeout, Ticker, TimeoutError, Timer}; 35pub use timer::{with_deadline, with_timeout, Ticker, TimeoutError, Timer};
37
38/// Ticks per second of the global timebase.
39///
40/// This value is specified by the `tick-*` Cargo features, which
41/// should be set by the time driver. Some drivers support a fixed tick rate, others
42/// allow you to choose a tick rate with Cargo features of their own. You should not
43/// set the `tick-*` features for embassy yourself as an end user.
44pub const TICK_HZ: u64 = tick::TICK_HZ;
45 36
46const fn gcd(a: u64, b: u64) -> u64 { 37const fn gcd(a: u64, b: u64) -> u64 {
47 if b == 0 { 38 if b == 0 {
diff --git a/embassy-time/src/queue.rs b/embassy-time/src/queue.rs
deleted file mode 100644
index c6f8b440a..000000000
--- a/embassy-time/src/queue.rs
+++ /dev/null
@@ -1,58 +0,0 @@
1//! Timer queue implementation
2//!
3//! This module defines the interface a timer queue needs to implement to power the `embassy_time` module.
4//!
5//! # Implementing a timer queue
6//!
7//! - Define a struct `MyTimerQueue`
8//! - Implement [`TimerQueue`] for it
9//! - Register it as the global timer queue with [`timer_queue_impl`](crate::timer_queue_impl).
10//!
11//! # Linkage details
12//!
13//! Check the documentation of the [`driver`](crate::driver) module for more information.
14//!
15//! Similarly to driver, if there is none or multiple timer queues in the crate tree, linking will fail.
16//!
17//! # Example
18//!
19//! ```
20//! use core::task::Waker;
21//!
22//! use embassy_time::Instant;
23//! use embassy_time::queue::{TimerQueue};
24//!
25//! struct MyTimerQueue{}; // not public!
26//! embassy_time::timer_queue_impl!(static QUEUE: MyTimerQueue = MyTimerQueue{});
27//!
28//! impl TimerQueue for MyTimerQueue {
29//! fn schedule_wake(&'static self, at: Instant, waker: &Waker) {
30//! todo!()
31//! }
32//! }
33//! ```
34use core::task::Waker;
35
36use crate::Instant;
37
38/// Timer queue
39pub trait TimerQueue {
40 /// Schedules a waker in the queue to be awoken at moment `at`.
41 /// If this moment is in the past, the waker might be awoken immediately.
42 fn schedule_wake(&'static self, at: Instant, waker: &Waker);
43}
44
45/// Set the TimerQueue implementation.
46///
47/// See the module documentation for an example.
48#[macro_export]
49macro_rules! timer_queue_impl {
50 (static $name:ident: $t: ty = $val:expr) => {
51 static $name: $t = $val;
52
53 #[no_mangle]
54 fn _embassy_time_schedule_wake(at: $crate::Instant, waker: &core::task::Waker) {
55 <$t as $crate::queue::TimerQueue>::schedule_wake(&$name, at, waker);
56 }
57 };
58}
diff --git a/embassy-time/src/queue_generic.rs b/embassy-time/src/queue_generic.rs
index 77947ab29..cf7a986d5 100644
--- a/embassy-time/src/queue_generic.rs
+++ b/embassy-time/src/queue_generic.rs
@@ -3,10 +3,10 @@ use core::cmp::{min, Ordering};
3use core::task::Waker; 3use core::task::Waker;
4 4
5use critical_section::Mutex; 5use critical_section::Mutex;
6use embassy_time_driver::{allocate_alarm, set_alarm, set_alarm_callback, AlarmHandle};
7use embassy_time_queue_driver::TimerQueue;
6use heapless::Vec; 8use heapless::Vec;
7 9
8use crate::driver::{allocate_alarm, set_alarm, set_alarm_callback, AlarmHandle};
9use crate::queue::TimerQueue;
10use crate::Instant; 10use crate::Instant;
11 11
12#[cfg(feature = "generic-queue-8")] 12#[cfg(feature = "generic-queue-8")]
@@ -167,111 +167,25 @@ impl Queue {
167} 167}
168 168
169impl TimerQueue for Queue { 169impl TimerQueue for Queue {
170 fn schedule_wake(&'static self, at: Instant, waker: &Waker) { 170 fn schedule_wake(&'static self, at: u64, waker: &Waker) {
171 Queue::schedule_wake(self, at, waker); 171 Queue::schedule_wake(self, Instant::from_ticks(at), waker);
172 } 172 }
173} 173}
174 174
175crate::timer_queue_impl!(static QUEUE: Queue = Queue::new()); 175embassy_time_queue_driver::timer_queue_impl!(static QUEUE: Queue = Queue::new());
176 176
177#[cfg(test)] 177#[cfg(test)]
178#[cfg(feature = "mock-driver")]
178mod tests { 179mod tests {
179 use core::cell::Cell; 180 use core::cell::Cell;
180 use core::task::{RawWaker, RawWakerVTable, Waker}; 181 use core::task::{RawWaker, RawWakerVTable, Waker};
181 use std::rc::Rc; 182 use std::rc::Rc;
182 use std::sync::Mutex;
183 183
184 use serial_test::serial; 184 use serial_test::serial;
185 185
186 use crate::driver::{AlarmHandle, Driver}; 186 use crate::driver_mock::MockDriver;
187 use crate::queue_generic::QUEUE; 187 use crate::queue_generic::QUEUE;
188 use crate::Instant; 188 use crate::{Duration, Instant};
189
190 struct InnerTestDriver {
191 now: u64,
192 alarm: u64,
193 callback: fn(*mut ()),
194 ctx: *mut (),
195 }
196
197 impl InnerTestDriver {
198 const fn new() -> Self {
199 Self {
200 now: 0,
201 alarm: u64::MAX,
202 callback: Self::noop,
203 ctx: core::ptr::null_mut(),
204 }
205 }
206
207 fn noop(_ctx: *mut ()) {}
208 }
209
210 unsafe impl Send for InnerTestDriver {}
211
212 struct TestDriver(Mutex<InnerTestDriver>);
213
214 impl TestDriver {
215 const fn new() -> Self {
216 Self(Mutex::new(InnerTestDriver::new()))
217 }
218
219 fn reset(&self) {
220 *self.0.lock().unwrap() = InnerTestDriver::new();
221 }
222
223 fn set_now(&self, now: u64) {
224 let notify = {
225 let mut inner = self.0.lock().unwrap();
226
227 if inner.now < now {
228 inner.now = now;
229
230 if inner.alarm <= now {
231 inner.alarm = u64::MAX;
232
233 Some((inner.callback, inner.ctx))
234 } else {
235 None
236 }
237 } else {
238 panic!("Going back in time?");
239 }
240 };
241
242 if let Some((callback, ctx)) = notify {
243 (callback)(ctx);
244 }
245 }
246 }
247
248 impl Driver for TestDriver {
249 fn now(&self) -> u64 {
250 self.0.lock().unwrap().now
251 }
252
253 unsafe fn allocate_alarm(&self) -> Option<AlarmHandle> {
254 Some(AlarmHandle::new(0))
255 }
256
257 fn set_alarm_callback(&self, _alarm: AlarmHandle, callback: fn(*mut ()), ctx: *mut ()) {
258 let mut inner = self.0.lock().unwrap();
259
260 inner.callback = callback;
261 inner.ctx = ctx;
262 }
263
264 fn set_alarm(&self, _alarm: AlarmHandle, timestamp: u64) -> bool {
265 let mut inner = self.0.lock().unwrap();
266
267 if timestamp <= inner.now {
268 false
269 } else {
270 inner.alarm = timestamp;
271 true
272 }
273 }
274 }
275 189
276 struct TestWaker { 190 struct TestWaker {
277 pub awoken: Rc<Cell<bool>>, 191 pub awoken: Rc<Cell<bool>>,
@@ -312,10 +226,8 @@ mod tests {
312 } 226 }
313 } 227 }
314 228
315 crate::time_driver_impl!(static DRIVER: TestDriver = TestDriver::new());
316
317 fn setup() { 229 fn setup() {
318 DRIVER.reset(); 230 MockDriver::get().reset();
319 critical_section::with(|cs| *QUEUE.inner.borrow_ref_mut(cs) = None); 231 critical_section::with(|cs| *QUEUE.inner.borrow_ref_mut(cs) = None);
320 } 232 }
321 233
@@ -382,13 +294,13 @@ mod tests {
382 294
383 assert!(!waker.awoken.get()); 295 assert!(!waker.awoken.get());
384 296
385 DRIVER.set_now(Instant::from_secs(99).as_ticks()); 297 MockDriver::get().advance(Duration::from_secs(99));
386 298
387 assert!(!waker.awoken.get()); 299 assert!(!waker.awoken.get());
388 300
389 assert_eq!(queue_len(), 1); 301 assert_eq!(queue_len(), 1);
390 302
391 DRIVER.set_now(Instant::from_secs(100).as_ticks()); 303 MockDriver::get().advance(Duration::from_secs(1));
392 304
393 assert!(waker.awoken.get()); 305 assert!(waker.awoken.get());
394 306
@@ -404,7 +316,7 @@ mod tests {
404 316
405 QUEUE.schedule_wake(Instant::from_secs(100), &waker.waker); 317 QUEUE.schedule_wake(Instant::from_secs(100), &waker.waker);
406 318
407 DRIVER.set_now(Instant::from_secs(50).as_ticks()); 319 MockDriver::get().advance(Duration::from_secs(50));
408 320
409 let waker2 = TestWaker::new(); 321 let waker2 = TestWaker::new();
410 322
diff --git a/embassy-time/src/tick.rs b/embassy-time/src/tick.rs
deleted file mode 100644
index 834e7c095..000000000
--- a/embassy-time/src/tick.rs
+++ /dev/null
@@ -1,482 +0,0 @@
1// Generated by gen_tick.py. DO NOT EDIT.
2
3#[cfg(feature = "tick-hz-1")]
4pub const TICK_HZ: u64 = 1;
5#[cfg(feature = "tick-hz-10")]
6pub const TICK_HZ: u64 = 10;
7#[cfg(feature = "tick-hz-100")]
8pub const TICK_HZ: u64 = 100;
9#[cfg(feature = "tick-hz-1_000")]
10pub const TICK_HZ: u64 = 1_000;
11#[cfg(feature = "tick-hz-10_000")]
12pub const TICK_HZ: u64 = 10_000;
13#[cfg(feature = "tick-hz-100_000")]
14pub const TICK_HZ: u64 = 100_000;
15#[cfg(feature = "tick-hz-1_000_000")]
16pub const TICK_HZ: u64 = 1_000_000;
17#[cfg(feature = "tick-hz-10_000_000")]
18pub const TICK_HZ: u64 = 10_000_000;
19#[cfg(feature = "tick-hz-100_000_000")]
20pub const TICK_HZ: u64 = 100_000_000;
21#[cfg(feature = "tick-hz-1_000_000_000")]
22pub const TICK_HZ: u64 = 1_000_000_000;
23#[cfg(feature = "tick-hz-2")]
24pub const TICK_HZ: u64 = 2;
25#[cfg(feature = "tick-hz-4")]
26pub const TICK_HZ: u64 = 4;
27#[cfg(feature = "tick-hz-8")]
28pub const TICK_HZ: u64 = 8;
29#[cfg(feature = "tick-hz-16")]
30pub const TICK_HZ: u64 = 16;
31#[cfg(feature = "tick-hz-32")]
32pub const TICK_HZ: u64 = 32;
33#[cfg(feature = "tick-hz-64")]
34pub const TICK_HZ: u64 = 64;
35#[cfg(feature = "tick-hz-128")]
36pub const TICK_HZ: u64 = 128;
37#[cfg(feature = "tick-hz-256")]
38pub const TICK_HZ: u64 = 256;
39#[cfg(feature = "tick-hz-512")]
40pub const TICK_HZ: u64 = 512;
41#[cfg(feature = "tick-hz-1_024")]
42pub const TICK_HZ: u64 = 1_024;
43#[cfg(feature = "tick-hz-2_048")]
44pub const TICK_HZ: u64 = 2_048;
45#[cfg(feature = "tick-hz-4_096")]
46pub const TICK_HZ: u64 = 4_096;
47#[cfg(feature = "tick-hz-8_192")]
48pub const TICK_HZ: u64 = 8_192;
49#[cfg(feature = "tick-hz-16_384")]
50pub const TICK_HZ: u64 = 16_384;
51#[cfg(feature = "tick-hz-32_768")]
52pub const TICK_HZ: u64 = 32_768;
53#[cfg(feature = "tick-hz-65_536")]
54pub const TICK_HZ: u64 = 65_536;
55#[cfg(feature = "tick-hz-131_072")]
56pub const TICK_HZ: u64 = 131_072;
57#[cfg(feature = "tick-hz-262_144")]
58pub const TICK_HZ: u64 = 262_144;
59#[cfg(feature = "tick-hz-524_288")]
60pub const TICK_HZ: u64 = 524_288;
61#[cfg(feature = "tick-hz-1_048_576")]
62pub const TICK_HZ: u64 = 1_048_576;
63#[cfg(feature = "tick-hz-2_097_152")]
64pub const TICK_HZ: u64 = 2_097_152;
65#[cfg(feature = "tick-hz-4_194_304")]
66pub const TICK_HZ: u64 = 4_194_304;
67#[cfg(feature = "tick-hz-8_388_608")]
68pub const TICK_HZ: u64 = 8_388_608;
69#[cfg(feature = "tick-hz-16_777_216")]
70pub const TICK_HZ: u64 = 16_777_216;
71#[cfg(feature = "tick-hz-2_000")]
72pub const TICK_HZ: u64 = 2_000;
73#[cfg(feature = "tick-hz-4_000")]
74pub const TICK_HZ: u64 = 4_000;
75#[cfg(feature = "tick-hz-8_000")]
76pub const TICK_HZ: u64 = 8_000;
77#[cfg(feature = "tick-hz-16_000")]
78pub const TICK_HZ: u64 = 16_000;
79#[cfg(feature = "tick-hz-32_000")]
80pub const TICK_HZ: u64 = 32_000;
81#[cfg(feature = "tick-hz-64_000")]
82pub const TICK_HZ: u64 = 64_000;
83#[cfg(feature = "tick-hz-128_000")]
84pub const TICK_HZ: u64 = 128_000;
85#[cfg(feature = "tick-hz-256_000")]
86pub const TICK_HZ: u64 = 256_000;
87#[cfg(feature = "tick-hz-512_000")]
88pub const TICK_HZ: u64 = 512_000;
89#[cfg(feature = "tick-hz-1_024_000")]
90pub const TICK_HZ: u64 = 1_024_000;
91#[cfg(feature = "tick-hz-2_048_000")]
92pub const TICK_HZ: u64 = 2_048_000;
93#[cfg(feature = "tick-hz-4_096_000")]
94pub const TICK_HZ: u64 = 4_096_000;
95#[cfg(feature = "tick-hz-8_192_000")]
96pub const TICK_HZ: u64 = 8_192_000;
97#[cfg(feature = "tick-hz-16_384_000")]
98pub const TICK_HZ: u64 = 16_384_000;
99#[cfg(feature = "tick-hz-32_768_000")]
100pub const TICK_HZ: u64 = 32_768_000;
101#[cfg(feature = "tick-hz-65_536_000")]
102pub const TICK_HZ: u64 = 65_536_000;
103#[cfg(feature = "tick-hz-131_072_000")]
104pub const TICK_HZ: u64 = 131_072_000;
105#[cfg(feature = "tick-hz-262_144_000")]
106pub const TICK_HZ: u64 = 262_144_000;
107#[cfg(feature = "tick-hz-524_288_000")]
108pub const TICK_HZ: u64 = 524_288_000;
109#[cfg(feature = "tick-hz-20_000")]
110pub const TICK_HZ: u64 = 20_000;
111#[cfg(feature = "tick-hz-40_000")]
112pub const TICK_HZ: u64 = 40_000;
113#[cfg(feature = "tick-hz-80_000")]
114pub const TICK_HZ: u64 = 80_000;
115#[cfg(feature = "tick-hz-160_000")]
116pub const TICK_HZ: u64 = 160_000;
117#[cfg(feature = "tick-hz-320_000")]
118pub const TICK_HZ: u64 = 320_000;
119#[cfg(feature = "tick-hz-640_000")]
120pub const TICK_HZ: u64 = 640_000;
121#[cfg(feature = "tick-hz-1_280_000")]
122pub const TICK_HZ: u64 = 1_280_000;
123#[cfg(feature = "tick-hz-2_560_000")]
124pub const TICK_HZ: u64 = 2_560_000;
125#[cfg(feature = "tick-hz-5_120_000")]
126pub const TICK_HZ: u64 = 5_120_000;
127#[cfg(feature = "tick-hz-10_240_000")]
128pub const TICK_HZ: u64 = 10_240_000;
129#[cfg(feature = "tick-hz-20_480_000")]
130pub const TICK_HZ: u64 = 20_480_000;
131#[cfg(feature = "tick-hz-40_960_000")]
132pub const TICK_HZ: u64 = 40_960_000;
133#[cfg(feature = "tick-hz-81_920_000")]
134pub const TICK_HZ: u64 = 81_920_000;
135#[cfg(feature = "tick-hz-163_840_000")]
136pub const TICK_HZ: u64 = 163_840_000;
137#[cfg(feature = "tick-hz-327_680_000")]
138pub const TICK_HZ: u64 = 327_680_000;
139#[cfg(feature = "tick-hz-655_360_000")]
140pub const TICK_HZ: u64 = 655_360_000;
141#[cfg(feature = "tick-hz-1_310_720_000")]
142pub const TICK_HZ: u64 = 1_310_720_000;
143#[cfg(feature = "tick-hz-2_621_440_000")]
144pub const TICK_HZ: u64 = 2_621_440_000;
145#[cfg(feature = "tick-hz-5_242_880_000")]
146pub const TICK_HZ: u64 = 5_242_880_000;
147#[cfg(feature = "tick-hz-2_000_000")]
148pub const TICK_HZ: u64 = 2_000_000;
149#[cfg(feature = "tick-hz-3_000_000")]
150pub const TICK_HZ: u64 = 3_000_000;
151#[cfg(feature = "tick-hz-4_000_000")]
152pub const TICK_HZ: u64 = 4_000_000;
153#[cfg(feature = "tick-hz-6_000_000")]
154pub const TICK_HZ: u64 = 6_000_000;
155#[cfg(feature = "tick-hz-8_000_000")]
156pub const TICK_HZ: u64 = 8_000_000;
157#[cfg(feature = "tick-hz-9_000_000")]
158pub const TICK_HZ: u64 = 9_000_000;
159#[cfg(feature = "tick-hz-12_000_000")]
160pub const TICK_HZ: u64 = 12_000_000;
161#[cfg(feature = "tick-hz-16_000_000")]
162pub const TICK_HZ: u64 = 16_000_000;
163#[cfg(feature = "tick-hz-18_000_000")]
164pub const TICK_HZ: u64 = 18_000_000;
165#[cfg(feature = "tick-hz-24_000_000")]
166pub const TICK_HZ: u64 = 24_000_000;
167#[cfg(feature = "tick-hz-32_000_000")]
168pub const TICK_HZ: u64 = 32_000_000;
169#[cfg(feature = "tick-hz-36_000_000")]
170pub const TICK_HZ: u64 = 36_000_000;
171#[cfg(feature = "tick-hz-48_000_000")]
172pub const TICK_HZ: u64 = 48_000_000;
173#[cfg(feature = "tick-hz-64_000_000")]
174pub const TICK_HZ: u64 = 64_000_000;
175#[cfg(feature = "tick-hz-72_000_000")]
176pub const TICK_HZ: u64 = 72_000_000;
177#[cfg(feature = "tick-hz-96_000_000")]
178pub const TICK_HZ: u64 = 96_000_000;
179#[cfg(feature = "tick-hz-128_000_000")]
180pub const TICK_HZ: u64 = 128_000_000;
181#[cfg(feature = "tick-hz-144_000_000")]
182pub const TICK_HZ: u64 = 144_000_000;
183#[cfg(feature = "tick-hz-192_000_000")]
184pub const TICK_HZ: u64 = 192_000_000;
185#[cfg(feature = "tick-hz-256_000_000")]
186pub const TICK_HZ: u64 = 256_000_000;
187#[cfg(feature = "tick-hz-288_000_000")]
188pub const TICK_HZ: u64 = 288_000_000;
189#[cfg(feature = "tick-hz-384_000_000")]
190pub const TICK_HZ: u64 = 384_000_000;
191#[cfg(feature = "tick-hz-512_000_000")]
192pub const TICK_HZ: u64 = 512_000_000;
193#[cfg(feature = "tick-hz-576_000_000")]
194pub const TICK_HZ: u64 = 576_000_000;
195#[cfg(feature = "tick-hz-768_000_000")]
196pub const TICK_HZ: u64 = 768_000_000;
197#[cfg(feature = "tick-hz-20_000_000")]
198pub const TICK_HZ: u64 = 20_000_000;
199#[cfg(feature = "tick-hz-30_000_000")]
200pub const TICK_HZ: u64 = 30_000_000;
201#[cfg(feature = "tick-hz-40_000_000")]
202pub const TICK_HZ: u64 = 40_000_000;
203#[cfg(feature = "tick-hz-50_000_000")]
204pub const TICK_HZ: u64 = 50_000_000;
205#[cfg(feature = "tick-hz-60_000_000")]
206pub const TICK_HZ: u64 = 60_000_000;
207#[cfg(feature = "tick-hz-70_000_000")]
208pub const TICK_HZ: u64 = 70_000_000;
209#[cfg(feature = "tick-hz-80_000_000")]
210pub const TICK_HZ: u64 = 80_000_000;
211#[cfg(feature = "tick-hz-90_000_000")]
212pub const TICK_HZ: u64 = 90_000_000;
213#[cfg(feature = "tick-hz-110_000_000")]
214pub const TICK_HZ: u64 = 110_000_000;
215#[cfg(feature = "tick-hz-120_000_000")]
216pub const TICK_HZ: u64 = 120_000_000;
217#[cfg(feature = "tick-hz-130_000_000")]
218pub const TICK_HZ: u64 = 130_000_000;
219#[cfg(feature = "tick-hz-140_000_000")]
220pub const TICK_HZ: u64 = 140_000_000;
221#[cfg(feature = "tick-hz-150_000_000")]
222pub const TICK_HZ: u64 = 150_000_000;
223#[cfg(feature = "tick-hz-160_000_000")]
224pub const TICK_HZ: u64 = 160_000_000;
225#[cfg(feature = "tick-hz-170_000_000")]
226pub const TICK_HZ: u64 = 170_000_000;
227#[cfg(feature = "tick-hz-180_000_000")]
228pub const TICK_HZ: u64 = 180_000_000;
229#[cfg(feature = "tick-hz-190_000_000")]
230pub const TICK_HZ: u64 = 190_000_000;
231#[cfg(feature = "tick-hz-200_000_000")]
232pub const TICK_HZ: u64 = 200_000_000;
233#[cfg(feature = "tick-hz-210_000_000")]
234pub const TICK_HZ: u64 = 210_000_000;
235#[cfg(feature = "tick-hz-220_000_000")]
236pub const TICK_HZ: u64 = 220_000_000;
237#[cfg(feature = "tick-hz-230_000_000")]
238pub const TICK_HZ: u64 = 230_000_000;
239#[cfg(feature = "tick-hz-240_000_000")]
240pub const TICK_HZ: u64 = 240_000_000;
241#[cfg(feature = "tick-hz-250_000_000")]
242pub const TICK_HZ: u64 = 250_000_000;
243#[cfg(feature = "tick-hz-260_000_000")]
244pub const TICK_HZ: u64 = 260_000_000;
245#[cfg(feature = "tick-hz-270_000_000")]
246pub const TICK_HZ: u64 = 270_000_000;
247#[cfg(feature = "tick-hz-280_000_000")]
248pub const TICK_HZ: u64 = 280_000_000;
249#[cfg(feature = "tick-hz-290_000_000")]
250pub const TICK_HZ: u64 = 290_000_000;
251#[cfg(feature = "tick-hz-300_000_000")]
252pub const TICK_HZ: u64 = 300_000_000;
253#[cfg(feature = "tick-hz-320_000_000")]
254pub const TICK_HZ: u64 = 320_000_000;
255#[cfg(feature = "tick-hz-340_000_000")]
256pub const TICK_HZ: u64 = 340_000_000;
257#[cfg(feature = "tick-hz-360_000_000")]
258pub const TICK_HZ: u64 = 360_000_000;
259#[cfg(feature = "tick-hz-380_000_000")]
260pub const TICK_HZ: u64 = 380_000_000;
261#[cfg(feature = "tick-hz-400_000_000")]
262pub const TICK_HZ: u64 = 400_000_000;
263#[cfg(feature = "tick-hz-420_000_000")]
264pub const TICK_HZ: u64 = 420_000_000;
265#[cfg(feature = "tick-hz-440_000_000")]
266pub const TICK_HZ: u64 = 440_000_000;
267#[cfg(feature = "tick-hz-460_000_000")]
268pub const TICK_HZ: u64 = 460_000_000;
269#[cfg(feature = "tick-hz-480_000_000")]
270pub const TICK_HZ: u64 = 480_000_000;
271#[cfg(feature = "tick-hz-500_000_000")]
272pub const TICK_HZ: u64 = 500_000_000;
273#[cfg(feature = "tick-hz-520_000_000")]
274pub const TICK_HZ: u64 = 520_000_000;
275#[cfg(feature = "tick-hz-540_000_000")]
276pub const TICK_HZ: u64 = 540_000_000;
277#[cfg(feature = "tick-hz-560_000_000")]
278pub const TICK_HZ: u64 = 560_000_000;
279#[cfg(feature = "tick-hz-580_000_000")]
280pub const TICK_HZ: u64 = 580_000_000;
281#[cfg(feature = "tick-hz-600_000_000")]
282pub const TICK_HZ: u64 = 600_000_000;
283#[cfg(feature = "tick-hz-620_000_000")]
284pub const TICK_HZ: u64 = 620_000_000;
285#[cfg(feature = "tick-hz-640_000_000")]
286pub const TICK_HZ: u64 = 640_000_000;
287#[cfg(feature = "tick-hz-660_000_000")]
288pub const TICK_HZ: u64 = 660_000_000;
289#[cfg(feature = "tick-hz-680_000_000")]
290pub const TICK_HZ: u64 = 680_000_000;
291#[cfg(feature = "tick-hz-700_000_000")]
292pub const TICK_HZ: u64 = 700_000_000;
293#[cfg(feature = "tick-hz-720_000_000")]
294pub const TICK_HZ: u64 = 720_000_000;
295#[cfg(feature = "tick-hz-740_000_000")]
296pub const TICK_HZ: u64 = 740_000_000;
297#[cfg(feature = "tick-hz-760_000_000")]
298pub const TICK_HZ: u64 = 760_000_000;
299#[cfg(feature = "tick-hz-780_000_000")]
300pub const TICK_HZ: u64 = 780_000_000;
301#[cfg(feature = "tick-hz-800_000_000")]
302pub const TICK_HZ: u64 = 800_000_000;
303#[cfg(feature = "tick-hz-820_000_000")]
304pub const TICK_HZ: u64 = 820_000_000;
305#[cfg(feature = "tick-hz-840_000_000")]
306pub const TICK_HZ: u64 = 840_000_000;
307#[cfg(feature = "tick-hz-860_000_000")]
308pub const TICK_HZ: u64 = 860_000_000;
309#[cfg(feature = "tick-hz-880_000_000")]
310pub const TICK_HZ: u64 = 880_000_000;
311#[cfg(feature = "tick-hz-900_000_000")]
312pub const TICK_HZ: u64 = 900_000_000;
313#[cfg(feature = "tick-hz-920_000_000")]
314pub const TICK_HZ: u64 = 920_000_000;
315#[cfg(feature = "tick-hz-940_000_000")]
316pub const TICK_HZ: u64 = 940_000_000;
317#[cfg(feature = "tick-hz-960_000_000")]
318pub const TICK_HZ: u64 = 960_000_000;
319#[cfg(feature = "tick-hz-980_000_000")]
320pub const TICK_HZ: u64 = 980_000_000;
321#[cfg(not(any(
322 feature = "tick-hz-1",
323 feature = "tick-hz-10",
324 feature = "tick-hz-100",
325 feature = "tick-hz-1_000",
326 feature = "tick-hz-10_000",
327 feature = "tick-hz-100_000",
328 feature = "tick-hz-1_000_000",
329 feature = "tick-hz-10_000_000",
330 feature = "tick-hz-100_000_000",
331 feature = "tick-hz-1_000_000_000",
332 feature = "tick-hz-2",
333 feature = "tick-hz-4",
334 feature = "tick-hz-8",
335 feature = "tick-hz-16",
336 feature = "tick-hz-32",
337 feature = "tick-hz-64",
338 feature = "tick-hz-128",
339 feature = "tick-hz-256",
340 feature = "tick-hz-512",
341 feature = "tick-hz-1_024",
342 feature = "tick-hz-2_048",
343 feature = "tick-hz-4_096",
344 feature = "tick-hz-8_192",
345 feature = "tick-hz-16_384",
346 feature = "tick-hz-32_768",
347 feature = "tick-hz-65_536",
348 feature = "tick-hz-131_072",
349 feature = "tick-hz-262_144",
350 feature = "tick-hz-524_288",
351 feature = "tick-hz-1_048_576",
352 feature = "tick-hz-2_097_152",
353 feature = "tick-hz-4_194_304",
354 feature = "tick-hz-8_388_608",
355 feature = "tick-hz-16_777_216",
356 feature = "tick-hz-2_000",
357 feature = "tick-hz-4_000",
358 feature = "tick-hz-8_000",
359 feature = "tick-hz-16_000",
360 feature = "tick-hz-32_000",
361 feature = "tick-hz-64_000",
362 feature = "tick-hz-128_000",
363 feature = "tick-hz-256_000",
364 feature = "tick-hz-512_000",
365 feature = "tick-hz-1_024_000",
366 feature = "tick-hz-2_048_000",
367 feature = "tick-hz-4_096_000",
368 feature = "tick-hz-8_192_000",
369 feature = "tick-hz-16_384_000",
370 feature = "tick-hz-32_768_000",
371 feature = "tick-hz-65_536_000",
372 feature = "tick-hz-131_072_000",
373 feature = "tick-hz-262_144_000",
374 feature = "tick-hz-524_288_000",
375 feature = "tick-hz-20_000",
376 feature = "tick-hz-40_000",
377 feature = "tick-hz-80_000",
378 feature = "tick-hz-160_000",
379 feature = "tick-hz-320_000",
380 feature = "tick-hz-640_000",
381 feature = "tick-hz-1_280_000",
382 feature = "tick-hz-2_560_000",
383 feature = "tick-hz-5_120_000",
384 feature = "tick-hz-10_240_000",
385 feature = "tick-hz-20_480_000",
386 feature = "tick-hz-40_960_000",
387 feature = "tick-hz-81_920_000",
388 feature = "tick-hz-163_840_000",
389 feature = "tick-hz-327_680_000",
390 feature = "tick-hz-655_360_000",
391 feature = "tick-hz-1_310_720_000",
392 feature = "tick-hz-2_621_440_000",
393 feature = "tick-hz-5_242_880_000",
394 feature = "tick-hz-2_000_000",
395 feature = "tick-hz-3_000_000",
396 feature = "tick-hz-4_000_000",
397 feature = "tick-hz-6_000_000",
398 feature = "tick-hz-8_000_000",
399 feature = "tick-hz-9_000_000",
400 feature = "tick-hz-12_000_000",
401 feature = "tick-hz-16_000_000",
402 feature = "tick-hz-18_000_000",
403 feature = "tick-hz-24_000_000",
404 feature = "tick-hz-32_000_000",
405 feature = "tick-hz-36_000_000",
406 feature = "tick-hz-48_000_000",
407 feature = "tick-hz-64_000_000",
408 feature = "tick-hz-72_000_000",
409 feature = "tick-hz-96_000_000",
410 feature = "tick-hz-128_000_000",
411 feature = "tick-hz-144_000_000",
412 feature = "tick-hz-192_000_000",
413 feature = "tick-hz-256_000_000",
414 feature = "tick-hz-288_000_000",
415 feature = "tick-hz-384_000_000",
416 feature = "tick-hz-512_000_000",
417 feature = "tick-hz-576_000_000",
418 feature = "tick-hz-768_000_000",
419 feature = "tick-hz-20_000_000",
420 feature = "tick-hz-30_000_000",
421 feature = "tick-hz-40_000_000",
422 feature = "tick-hz-50_000_000",
423 feature = "tick-hz-60_000_000",
424 feature = "tick-hz-70_000_000",
425 feature = "tick-hz-80_000_000",
426 feature = "tick-hz-90_000_000",
427 feature = "tick-hz-110_000_000",
428 feature = "tick-hz-120_000_000",
429 feature = "tick-hz-130_000_000",
430 feature = "tick-hz-140_000_000",
431 feature = "tick-hz-150_000_000",
432 feature = "tick-hz-160_000_000",
433 feature = "tick-hz-170_000_000",
434 feature = "tick-hz-180_000_000",
435 feature = "tick-hz-190_000_000",
436 feature = "tick-hz-200_000_000",
437 feature = "tick-hz-210_000_000",
438 feature = "tick-hz-220_000_000",
439 feature = "tick-hz-230_000_000",
440 feature = "tick-hz-240_000_000",
441 feature = "tick-hz-250_000_000",
442 feature = "tick-hz-260_000_000",
443 feature = "tick-hz-270_000_000",
444 feature = "tick-hz-280_000_000",
445 feature = "tick-hz-290_000_000",
446 feature = "tick-hz-300_000_000",
447 feature = "tick-hz-320_000_000",
448 feature = "tick-hz-340_000_000",
449 feature = "tick-hz-360_000_000",
450 feature = "tick-hz-380_000_000",
451 feature = "tick-hz-400_000_000",
452 feature = "tick-hz-420_000_000",
453 feature = "tick-hz-440_000_000",
454 feature = "tick-hz-460_000_000",
455 feature = "tick-hz-480_000_000",
456 feature = "tick-hz-500_000_000",
457 feature = "tick-hz-520_000_000",
458 feature = "tick-hz-540_000_000",
459 feature = "tick-hz-560_000_000",
460 feature = "tick-hz-580_000_000",
461 feature = "tick-hz-600_000_000",
462 feature = "tick-hz-620_000_000",
463 feature = "tick-hz-640_000_000",
464 feature = "tick-hz-660_000_000",
465 feature = "tick-hz-680_000_000",
466 feature = "tick-hz-700_000_000",
467 feature = "tick-hz-720_000_000",
468 feature = "tick-hz-740_000_000",
469 feature = "tick-hz-760_000_000",
470 feature = "tick-hz-780_000_000",
471 feature = "tick-hz-800_000_000",
472 feature = "tick-hz-820_000_000",
473 feature = "tick-hz-840_000_000",
474 feature = "tick-hz-860_000_000",
475 feature = "tick-hz-880_000_000",
476 feature = "tick-hz-900_000_000",
477 feature = "tick-hz-920_000_000",
478 feature = "tick-hz-940_000_000",
479 feature = "tick-hz-960_000_000",
480 feature = "tick-hz-980_000_000",
481)))]
482pub const TICK_HZ: u64 = 1_000_000;
diff --git a/embassy-time/src/timer.rs b/embassy-time/src/timer.rs
index a123c1d01..757c3ff00 100644
--- a/embassy-time/src/timer.rs
+++ b/embassy-time/src/timer.rs
@@ -1,6 +1,6 @@
1use core::future::{poll_fn, Future}; 1use core::future::{poll_fn, Future};
2use core::pin::Pin; 2use core::pin::Pin;
3use core::task::{Context, Poll, Waker}; 3use core::task::{Context, Poll};
4 4
5use futures_util::future::{select, Either}; 5use futures_util::future::{select, Either};
6use futures_util::stream::FusedStream; 6use futures_util::stream::FusedStream;
@@ -8,7 +8,7 @@ use futures_util::{pin_mut, Stream};
8 8
9use crate::{Duration, Instant}; 9use crate::{Duration, Instant};
10 10
11/// Error returned by [`with_timeout`] on timeout. 11/// Error returned by [`with_timeout`] and [`with_deadline`] on timeout.
12#[derive(Debug, Clone, PartialEq, Eq)] 12#[derive(Debug, Clone, PartialEq, Eq)]
13#[cfg_attr(feature = "defmt", derive(defmt::Format))] 13#[cfg_attr(feature = "defmt", derive(defmt::Format))]
14pub struct TimeoutError; 14pub struct TimeoutError;
@@ -26,6 +26,19 @@ pub async fn with_timeout<F: Future>(timeout: Duration, fut: F) -> Result<F::Out
26 } 26 }
27} 27}
28 28
29/// Runs a given future with a deadline time.
30///
31/// If the future completes before the deadline, its output is returned. Otherwise, on timeout,
32/// work on the future is stopped (`poll` is no longer called), the future is dropped and `Err(TimeoutError)` is returned.
33pub async fn with_deadline<F: Future>(at: Instant, fut: F) -> Result<F::Output, TimeoutError> {
34 let timeout_fut = Timer::at(at);
35 pin_mut!(fut);
36 match select(fut, timeout_fut).await {
37 Either::Left((r, _)) => Ok(r),
38 Either::Right(_) => Err(TimeoutError),
39 }
40}
41
29/// A future that completes at a specified [Instant](struct.Instant.html). 42/// A future that completes at a specified [Instant](struct.Instant.html).
30#[must_use = "futures do nothing unless you `.await` or poll them"] 43#[must_use = "futures do nothing unless you `.await` or poll them"]
31pub struct Timer { 44pub struct Timer {
@@ -47,9 +60,6 @@ impl Timer {
47 /// 60 ///
48 /// Example: 61 /// Example:
49 /// ``` no_run 62 /// ``` no_run
50 /// # #![feature(type_alias_impl_trait)]
51 /// #
52 /// # fn foo() {}
53 /// use embassy_time::{Duration, Timer}; 63 /// use embassy_time::{Duration, Timer};
54 /// 64 ///
55 /// #[embassy_executor::task] 65 /// #[embassy_executor::task]
@@ -119,7 +129,7 @@ impl Future for Timer {
119 if self.yielded_once && self.expires_at <= Instant::now() { 129 if self.yielded_once && self.expires_at <= Instant::now() {
120 Poll::Ready(()) 130 Poll::Ready(())
121 } else { 131 } else {
122 schedule_wake(self.expires_at, cx.waker()); 132 embassy_time_queue_driver::schedule_wake(self.expires_at.as_ticks(), cx.waker());
123 self.yielded_once = true; 133 self.yielded_once = true;
124 Poll::Pending 134 Poll::Pending
125 } 135 }
@@ -132,8 +142,6 @@ impl Future for Timer {
132/// 142///
133/// For instance, consider the following code fragment. 143/// For instance, consider the following code fragment.
134/// ``` no_run 144/// ``` no_run
135/// # #![feature(type_alias_impl_trait)]
136/// #
137/// use embassy_time::{Duration, Timer}; 145/// use embassy_time::{Duration, Timer};
138/// # fn foo() {} 146/// # fn foo() {}
139/// 147///
@@ -152,8 +160,6 @@ impl Future for Timer {
152/// Example using ticker, which will consistently call `foo` once a second. 160/// Example using ticker, which will consistently call `foo` once a second.
153/// 161///
154/// ``` no_run 162/// ``` no_run
155/// # #![feature(type_alias_impl_trait)]
156/// #
157/// use embassy_time::{Duration, Ticker}; 163/// use embassy_time::{Duration, Ticker};
158/// # fn foo(){} 164/// # fn foo(){}
159/// 165///
@@ -184,6 +190,18 @@ impl Ticker {
184 self.expires_at = Instant::now() + self.duration; 190 self.expires_at = Instant::now() + self.duration;
185 } 191 }
186 192
193 /// Reset the ticker at the deadline.
194 /// If the deadline is in the past, the ticker will fire instantly.
195 pub fn reset_at(&mut self, deadline: Instant) {
196 self.expires_at = deadline + self.duration;
197 }
198
199 /// Resets the ticker, after the specified duration has passed.
200 /// If the specified duration is zero, the next tick will be after the duration of the ticker.
201 pub fn reset_after(&mut self, after: Duration) {
202 self.expires_at = Instant::now() + after + self.duration;
203 }
204
187 /// Waits for the next tick. 205 /// Waits for the next tick.
188 pub fn next(&mut self) -> impl Future<Output = ()> + Send + Sync + '_ { 206 pub fn next(&mut self) -> impl Future<Output = ()> + Send + Sync + '_ {
189 poll_fn(|cx| { 207 poll_fn(|cx| {
@@ -192,7 +210,7 @@ impl Ticker {
192 self.expires_at += dur; 210 self.expires_at += dur;
193 Poll::Ready(()) 211 Poll::Ready(())
194 } else { 212 } else {
195 schedule_wake(self.expires_at, cx.waker()); 213 embassy_time_queue_driver::schedule_wake(self.expires_at.as_ticks(), cx.waker());
196 Poll::Pending 214 Poll::Pending
197 } 215 }
198 }) 216 })
@@ -209,7 +227,7 @@ impl Stream for Ticker {
209 self.expires_at += dur; 227 self.expires_at += dur;
210 Poll::Ready(Some(())) 228 Poll::Ready(Some(()))
211 } else { 229 } else {
212 schedule_wake(self.expires_at, cx.waker()); 230 embassy_time_queue_driver::schedule_wake(self.expires_at.as_ticks(), cx.waker());
213 Poll::Pending 231 Poll::Pending
214 } 232 }
215 } 233 }
@@ -221,11 +239,3 @@ impl FusedStream for Ticker {
221 false 239 false
222 } 240 }
223} 241}
224
225extern "Rust" {
226 fn _embassy_time_schedule_wake(at: Instant, waker: &Waker);
227}
228
229fn schedule_wake(at: Instant, waker: &Waker) {
230 unsafe { _embassy_time_schedule_wake(at, waker) }
231}