aboutsummaryrefslogtreecommitdiff
path: root/embassy-time/src
diff options
context:
space:
mode:
authorQuentin Smith <[email protected]>2023-07-17 21:31:43 -0400
committerQuentin Smith <[email protected]>2023-07-17 21:31:43 -0400
commit6f02403184eb7fb7990fb88fc9df9c4328a690a3 (patch)
tree748f510e190bb2724750507a6e69ed1a8e08cb20 /embassy-time/src
parentd896f80405aa8963877049ed999e4aba25d6e2bb (diff)
parent6b5df4523aa1c4902f02e803450ae4b418e0e3ca (diff)
Merge remote-tracking branch 'origin/main' into nrf-pdm
Diffstat (limited to 'embassy-time/src')
-rw-r--r--embassy-time/src/delay.rs40
-rw-r--r--embassy-time/src/driver.rs17
-rw-r--r--embassy-time/src/driver_std.rs71
-rw-r--r--embassy-time/src/driver_wasm.rs16
-rw-r--r--embassy-time/src/duration.rs54
-rw-r--r--embassy-time/src/instant.rs14
-rw-r--r--embassy-time/src/lib.rs26
-rw-r--r--embassy-time/src/queue.rs58
-rw-r--r--embassy-time/src/queue_generic.rs450
-rw-r--r--embassy-time/src/tick.rs239
-rw-r--r--embassy-time/src/timer.rs18
11 files changed, 902 insertions, 101 deletions
diff --git a/embassy-time/src/delay.rs b/embassy-time/src/delay.rs
index d010fff98..cf1918724 100644
--- a/embassy-time/src/delay.rs
+++ b/embassy-time/src/delay.rs
@@ -18,39 +18,29 @@ pub struct Delay;
18mod eh1 { 18mod eh1 {
19 use super::*; 19 use super::*;
20 20
21 impl embedded_hal_1::delay::blocking::DelayUs for Delay { 21 impl embedded_hal_1::delay::DelayUs for Delay {
22 type Error = core::convert::Infallible; 22 fn delay_us(&mut self, us: u32) {
23 23 block_for(Duration::from_micros(us as u64))
24 fn delay_us(&mut self, us: u32) -> Result<(), Self::Error> {
25 Ok(block_for(Duration::from_micros(us as u64)))
26 } 24 }
27 25
28 fn delay_ms(&mut self, ms: u32) -> Result<(), Self::Error> { 26 fn delay_ms(&mut self, ms: u32) {
29 Ok(block_for(Duration::from_millis(ms as u64))) 27 block_for(Duration::from_millis(ms as u64))
30 } 28 }
31 } 29 }
32} 30}
33 31
34cfg_if::cfg_if! { 32#[cfg(all(feature = "unstable-traits", feature = "nightly"))]
35 if #[cfg(all(feature = "unstable-traits", feature = "nightly"))] { 33mod eha {
36 use crate::Timer; 34 use super::*;
37 use core::future::Future; 35 use crate::Timer;
38 use futures_util::FutureExt;
39
40 impl embedded_hal_async::delay::DelayUs for Delay {
41 type Error = core::convert::Infallible;
42
43 type DelayUsFuture<'a> = impl Future<Output = Result<(), Self::Error>> + 'a where Self: 'a;
44
45 fn delay_us(&mut self, micros: u32) -> Self::DelayUsFuture<'_> {
46 Timer::after(Duration::from_micros(micros as _)).map(Ok)
47 }
48 36
49 type DelayMsFuture<'a> = impl Future<Output = Result<(), Self::Error>> + 'a where Self: 'a; 37 impl embedded_hal_async::delay::DelayUs for Delay {
38 async fn delay_us(&mut self, micros: u32) {
39 Timer::after(Duration::from_micros(micros as _)).await
40 }
50 41
51 fn delay_ms(&mut self, millis: u32) -> Self::DelayMsFuture<'_> { 42 async fn delay_ms(&mut self, millis: u32) {
52 Timer::after(Duration::from_millis(millis as _)).map(Ok) 43 Timer::after(Duration::from_millis(millis as _)).await
53 }
54 } 44 }
55 } 45 }
56} 46}
diff --git a/embassy-time/src/driver.rs b/embassy-time/src/driver.rs
index 79ae14b91..5fe7becaf 100644
--- a/embassy-time/src/driver.rs
+++ b/embassy-time/src/driver.rs
@@ -36,7 +36,7 @@
36//! ``` 36//! ```
37//! use embassy_time::driver::{Driver, AlarmHandle}; 37//! use embassy_time::driver::{Driver, AlarmHandle};
38//! 38//!
39//! struct MyDriver{}; // not public! 39//! struct MyDriver{} // not public!
40//! embassy_time::time_driver_impl!(static DRIVER: MyDriver = MyDriver{}); 40//! embassy_time::time_driver_impl!(static DRIVER: MyDriver = MyDriver{});
41//! 41//!
42//! impl Driver for MyDriver { 42//! impl Driver for MyDriver {
@@ -49,7 +49,7 @@
49//! fn set_alarm_callback(&self, alarm: AlarmHandle, callback: fn(*mut ()), ctx: *mut ()) { 49//! fn set_alarm_callback(&self, alarm: AlarmHandle, callback: fn(*mut ()), ctx: *mut ()) {
50//! todo!() 50//! todo!()
51//! } 51//! }
52//! fn set_alarm(&self, alarm: AlarmHandle, timestamp: u64) { 52//! fn set_alarm(&self, alarm: AlarmHandle, timestamp: u64) -> bool {
53//! todo!() 53//! todo!()
54//! } 54//! }
55//! } 55//! }
@@ -105,20 +105,21 @@ pub trait Driver: Send + Sync + 'static {
105 /// Sets an alarm at the given timestamp. When the current timestamp reaches the alarm 105 /// Sets an alarm at the given timestamp. When the current timestamp reaches the alarm
106 /// timestamp, the provided callback function will be called. 106 /// timestamp, the provided callback function will be called.
107 /// 107 ///
108 /// If `timestamp` is already in the past, the alarm callback must be immediately fired. 108 /// The `Driver` implementation should guarantee that the alarm callback is never called synchronously from `set_alarm`.
109 /// In this case, it is allowed (but not mandatory) to call the alarm callback 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.
110 /// 111 ///
111 /// When callback is called, it is guaranteed that now() will return a value greater or equal than timestamp. 112 /// When callback is called, it is guaranteed that now() will return a value greater or equal than timestamp.
112 /// 113 ///
113 /// Only one alarm can be active at a time for each AlarmHandle. This overwrites any previously-set alarm if any. 114 /// Only one alarm can be active at a time for each AlarmHandle. This overwrites any previously-set alarm if any.
114 fn set_alarm(&self, alarm: AlarmHandle, timestamp: u64); 115 fn set_alarm(&self, alarm: AlarmHandle, timestamp: u64) -> bool;
115} 116}
116 117
117extern "Rust" { 118extern "Rust" {
118 fn _embassy_time_now() -> u64; 119 fn _embassy_time_now() -> u64;
119 fn _embassy_time_allocate_alarm() -> Option<AlarmHandle>; 120 fn _embassy_time_allocate_alarm() -> Option<AlarmHandle>;
120 fn _embassy_time_set_alarm_callback(alarm: AlarmHandle, callback: fn(*mut ()), ctx: *mut ()); 121 fn _embassy_time_set_alarm_callback(alarm: AlarmHandle, callback: fn(*mut ()), ctx: *mut ());
121 fn _embassy_time_set_alarm(alarm: AlarmHandle, timestamp: u64); 122 fn _embassy_time_set_alarm(alarm: AlarmHandle, timestamp: u64) -> bool;
122} 123}
123 124
124/// See [`Driver::now`] 125/// See [`Driver::now`]
@@ -139,7 +140,7 @@ pub fn set_alarm_callback(alarm: AlarmHandle, callback: fn(*mut ()), ctx: *mut (
139} 140}
140 141
141/// See [`Driver::set_alarm`] 142/// See [`Driver::set_alarm`]
142pub fn set_alarm(alarm: AlarmHandle, timestamp: u64) { 143pub fn set_alarm(alarm: AlarmHandle, timestamp: u64) -> bool {
143 unsafe { _embassy_time_set_alarm(alarm, timestamp) } 144 unsafe { _embassy_time_set_alarm(alarm, timestamp) }
144} 145}
145 146
@@ -167,7 +168,7 @@ macro_rules! time_driver_impl {
167 } 168 }
168 169
169 #[no_mangle] 170 #[no_mangle]
170 fn _embassy_time_set_alarm(alarm: $crate::driver::AlarmHandle, timestamp: u64) { 171 fn _embassy_time_set_alarm(alarm: $crate::driver::AlarmHandle, timestamp: u64) -> bool {
171 <$t as $crate::driver::Driver>::set_alarm(&$name, alarm, timestamp) 172 <$t as $crate::driver::Driver>::set_alarm(&$name, alarm, timestamp)
172 } 173 }
173 }; 174 };
diff --git a/embassy-time/src/driver_std.rs b/embassy-time/src/driver_std.rs
index 2ddb2e604..32db47a37 100644
--- a/embassy-time/src/driver_std.rs
+++ b/embassy-time/src/driver_std.rs
@@ -1,10 +1,11 @@
1use std::cell::UnsafeCell; 1use core::sync::atomic::{AtomicU8, Ordering};
2use std::cell::{RefCell, UnsafeCell};
2use std::mem::MaybeUninit; 3use std::mem::MaybeUninit;
3use std::sync::{Condvar, Mutex, Once}; 4use std::sync::{Condvar, Mutex, Once};
4use std::time::{Duration as StdDuration, Instant as StdInstant}; 5use std::time::{Duration as StdDuration, Instant as StdInstant};
5use std::{mem, ptr, thread}; 6use std::{mem, ptr, thread};
6 7
7use atomic_polyfill::{AtomicU8, Ordering}; 8use critical_section::Mutex as CsMutex;
8 9
9use crate::driver::{AlarmHandle, Driver}; 10use crate::driver::{AlarmHandle, Driver};
10 11
@@ -35,7 +36,10 @@ struct TimeDriver {
35 alarm_count: AtomicU8, 36 alarm_count: AtomicU8,
36 37
37 once: Once, 38 once: Once,
38 alarms: UninitCell<Mutex<[AlarmState; ALARM_COUNT]>>, 39 // The STD Driver implementation requires the alarms' mutex to be reentrant, which the STD Mutex isn't
40 // Fortunately, mutexes based on the `critical-section` crate are reentrant, because the critical sections
41 // themselves are reentrant
42 alarms: UninitCell<CsMutex<RefCell<[AlarmState; ALARM_COUNT]>>>,
39 zero_instant: UninitCell<StdInstant>, 43 zero_instant: UninitCell<StdInstant>,
40 signaler: UninitCell<Signaler>, 44 signaler: UninitCell<Signaler>,
41} 45}
@@ -53,7 +57,7 @@ crate::time_driver_impl!(static DRIVER: TimeDriver = TimeDriver {
53impl TimeDriver { 57impl TimeDriver {
54 fn init(&self) { 58 fn init(&self) {
55 self.once.call_once(|| unsafe { 59 self.once.call_once(|| unsafe {
56 self.alarms.write(Mutex::new([ALARM_NEW; ALARM_COUNT])); 60 self.alarms.write(CsMutex::new(RefCell::new([ALARM_NEW; ALARM_COUNT])));
57 self.zero_instant.write(StdInstant::now()); 61 self.zero_instant.write(StdInstant::now());
58 self.signaler.write(Signaler::new()); 62 self.signaler.write(Signaler::new());
59 63
@@ -66,25 +70,38 @@ impl TimeDriver {
66 loop { 70 loop {
67 let now = DRIVER.now(); 71 let now = DRIVER.now();
68 72
69 let mut next_alarm = u64::MAX; 73 let next_alarm = critical_section::with(|cs| {
70 { 74 let alarms = unsafe { DRIVER.alarms.as_ref() }.borrow(cs);
71 let alarms = &mut *unsafe { DRIVER.alarms.as_ref() }.lock().unwrap(); 75 loop {
72 for alarm in alarms { 76 let pending = alarms
73 if alarm.timestamp <= now { 77 .borrow_mut()
74 alarm.timestamp = u64::MAX; 78 .iter_mut()
79 .find(|alarm| alarm.timestamp <= now)
80 .map(|alarm| {
81 alarm.timestamp = u64::MAX;
75 82
76 // Call after clearing alarm, so the callback can set another alarm. 83 (alarm.callback, alarm.ctx)
84 });
77 85
86 if let Some((callback, ctx)) = pending {
78 // safety: 87 // safety:
79 // - we can ignore the possiblity of `f` being unset (null) because of the safety contract of `allocate_alarm`. 88 // - we can ignore the possiblity of `f` being unset (null) because of the safety contract of `allocate_alarm`.
80 // - other than that we only store valid function pointers into alarm.callback 89 // - other than that we only store valid function pointers into alarm.callback
81 let f: fn(*mut ()) = unsafe { mem::transmute(alarm.callback) }; 90 let f: fn(*mut ()) = unsafe { mem::transmute(callback) };
82 f(alarm.ctx); 91 f(ctx);
83 } else { 92 } else {
84 next_alarm = next_alarm.min(alarm.timestamp); 93 // No alarm due
94 break;
85 } 95 }
86 } 96 }
87 } 97
98 alarms
99 .borrow()
100 .iter()
101 .map(|alarm| alarm.timestamp)
102 .min()
103 .unwrap_or(u64::MAX)
104 });
88 105
89 // Ensure we don't overflow 106 // Ensure we don't overflow
90 let until = zero 107 let until = zero
@@ -121,18 +138,24 @@ impl Driver for TimeDriver {
121 138
122 fn set_alarm_callback(&self, alarm: AlarmHandle, callback: fn(*mut ()), ctx: *mut ()) { 139 fn set_alarm_callback(&self, alarm: AlarmHandle, callback: fn(*mut ()), ctx: *mut ()) {
123 self.init(); 140 self.init();
124 let mut alarms = unsafe { self.alarms.as_ref() }.lock().unwrap(); 141 critical_section::with(|cs| {
125 let alarm = &mut alarms[alarm.id() as usize]; 142 let mut alarms = unsafe { self.alarms.as_ref() }.borrow_ref_mut(cs);
126 alarm.callback = callback as *const (); 143 let alarm = &mut alarms[alarm.id() as usize];
127 alarm.ctx = ctx; 144 alarm.callback = callback as *const ();
145 alarm.ctx = ctx;
146 });
128 } 147 }
129 148
130 fn set_alarm(&self, alarm: AlarmHandle, timestamp: u64) { 149 fn set_alarm(&self, alarm: AlarmHandle, timestamp: u64) -> bool {
131 self.init(); 150 self.init();
132 let mut alarms = unsafe { self.alarms.as_ref() }.lock().unwrap(); 151 critical_section::with(|cs| {
133 let alarm = &mut alarms[alarm.id() as usize]; 152 let mut alarms = unsafe { self.alarms.as_ref() }.borrow_ref_mut(cs);
134 alarm.timestamp = timestamp; 153 let alarm = &mut alarms[alarm.id() as usize];
135 unsafe { self.signaler.as_ref() }.signal(); 154 alarm.timestamp = timestamp;
155 unsafe { self.signaler.as_ref() }.signal();
156 });
157
158 true
136 } 159 }
137} 160}
138 161
diff --git a/embassy-time/src/driver_wasm.rs b/embassy-time/src/driver_wasm.rs
index e4497e6a2..0f672dc75 100644
--- a/embassy-time/src/driver_wasm.rs
+++ b/embassy-time/src/driver_wasm.rs
@@ -1,9 +1,9 @@
1use core::sync::atomic::{AtomicU8, Ordering};
1use std::cell::UnsafeCell; 2use std::cell::UnsafeCell;
2use std::mem::MaybeUninit; 3use std::mem::MaybeUninit;
3use std::ptr; 4use std::ptr;
4use std::sync::{Mutex, Once}; 5use std::sync::{Mutex, Once};
5 6
6use atomic_polyfill::{AtomicU8, Ordering};
7use wasm_bindgen::prelude::*; 7use wasm_bindgen::prelude::*;
8use wasm_timer::Instant as StdInstant; 8use wasm_timer::Instant as StdInstant;
9 9
@@ -90,15 +90,23 @@ impl Driver for TimeDriver {
90 })); 90 }));
91 } 91 }
92 92
93 fn set_alarm(&self, alarm: AlarmHandle, timestamp: u64) { 93 fn set_alarm(&self, alarm: AlarmHandle, timestamp: u64) -> bool {
94 self.init(); 94 self.init();
95 let mut alarms = unsafe { self.alarms.as_ref() }.lock().unwrap(); 95 let mut alarms = unsafe { self.alarms.as_ref() }.lock().unwrap();
96 let alarm = &mut alarms[alarm.id() as usize]; 96 let alarm = &mut alarms[alarm.id() as usize];
97 let timeout = (timestamp - self.now()) as u32;
98 if let Some(token) = alarm.token { 97 if let Some(token) = alarm.token {
99 clearTimeout(token); 98 clearTimeout(token);
100 } 99 }
101 alarm.token = Some(setTimeout(alarm.closure.as_ref().unwrap(), timeout / 1000)); 100
101 let now = self.now();
102 if timestamp <= now {
103 false
104 } else {
105 let timeout = (timestamp - now) as u32;
106 alarm.token = Some(setTimeout(alarm.closure.as_ref().unwrap(), timeout / 1000));
107
108 true
109 }
102 } 110 }
103} 111}
104 112
diff --git a/embassy-time/src/duration.rs b/embassy-time/src/duration.rs
index dc4f16bd4..8366455be 100644
--- a/embassy-time/src/duration.rs
+++ b/embassy-time/src/duration.rs
@@ -1,7 +1,7 @@
1use core::fmt; 1use core::fmt;
2use core::ops::{Add, AddAssign, Div, DivAssign, Mul, MulAssign, Sub, SubAssign}; 2use core::ops::{Add, AddAssign, Div, DivAssign, Mul, MulAssign, Sub, SubAssign};
3 3
4use super::{GCD_1K, GCD_1M, TICKS_PER_SECOND}; 4use super::{GCD_1K, GCD_1M, TICK_HZ};
5 5
6#[derive(Debug, Default, Copy, Clone, PartialEq, Eq, PartialOrd, Ord)] 6#[derive(Debug, Default, Copy, Clone, PartialEq, Eq, PartialOrd, Ord)]
7#[cfg_attr(feature = "defmt", derive(defmt::Format))] 7#[cfg_attr(feature = "defmt", derive(defmt::Format))]
@@ -23,17 +23,17 @@ impl Duration {
23 23
24 /// Convert the `Duration` to seconds, rounding down. 24 /// Convert the `Duration` to seconds, rounding down.
25 pub const fn as_secs(&self) -> u64 { 25 pub const fn as_secs(&self) -> u64 {
26 self.ticks / TICKS_PER_SECOND 26 self.ticks / TICK_HZ
27 } 27 }
28 28
29 /// Convert the `Duration` to milliseconds, rounding down. 29 /// Convert the `Duration` to milliseconds, rounding down.
30 pub const fn as_millis(&self) -> u64 { 30 pub const fn as_millis(&self) -> u64 {
31 self.ticks * (1000 / GCD_1K) / (TICKS_PER_SECOND / GCD_1K) 31 self.ticks * (1000 / GCD_1K) / (TICK_HZ / GCD_1K)
32 } 32 }
33 33
34 /// Convert the `Duration` to microseconds, rounding down. 34 /// Convert the `Duration` to microseconds, rounding down.
35 pub const fn as_micros(&self) -> u64 { 35 pub const fn as_micros(&self) -> u64 {
36 self.ticks * (1_000_000 / GCD_1M) / (TICKS_PER_SECOND / GCD_1M) 36 self.ticks * (1_000_000 / GCD_1M) / (TICK_HZ / GCD_1M)
37 } 37 }
38 38
39 /// Creates a duration from the specified number of clock ticks 39 /// Creates a duration from the specified number of clock ticks
@@ -43,15 +43,13 @@ impl Duration {
43 43
44 /// Creates a duration from the specified number of seconds, rounding up. 44 /// Creates a duration from the specified number of seconds, rounding up.
45 pub const fn from_secs(secs: u64) -> Duration { 45 pub const fn from_secs(secs: u64) -> Duration {
46 Duration { 46 Duration { ticks: secs * TICK_HZ }
47 ticks: secs * TICKS_PER_SECOND,
48 }
49 } 47 }
50 48
51 /// Creates a duration from the specified number of milliseconds, rounding up. 49 /// Creates a duration from the specified number of milliseconds, rounding up.
52 pub const fn from_millis(millis: u64) -> Duration { 50 pub const fn from_millis(millis: u64) -> Duration {
53 Duration { 51 Duration {
54 ticks: div_ceil(millis * (TICKS_PER_SECOND / GCD_1K), 1000 / GCD_1K), 52 ticks: div_ceil(millis * (TICK_HZ / GCD_1K), 1000 / GCD_1K),
55 } 53 }
56 } 54 }
57 55
@@ -59,21 +57,19 @@ impl Duration {
59 /// NOTE: Delays this small may be inaccurate. 57 /// NOTE: Delays this small may be inaccurate.
60 pub const fn from_micros(micros: u64) -> Duration { 58 pub const fn from_micros(micros: u64) -> Duration {
61 Duration { 59 Duration {
62 ticks: div_ceil(micros * (TICKS_PER_SECOND / GCD_1M), 1_000_000 / GCD_1M), 60 ticks: div_ceil(micros * (TICK_HZ / GCD_1M), 1_000_000 / GCD_1M),
63 } 61 }
64 } 62 }
65 63
66 /// Creates a duration from the specified number of seconds, rounding down. 64 /// Creates a duration from the specified number of seconds, rounding down.
67 pub const fn from_secs_floor(secs: u64) -> Duration { 65 pub const fn from_secs_floor(secs: u64) -> Duration {
68 Duration { 66 Duration { ticks: secs * TICK_HZ }
69 ticks: secs * TICKS_PER_SECOND,
70 }
71 } 67 }
72 68
73 /// Creates a duration from the specified number of milliseconds, rounding down. 69 /// Creates a duration from the specified number of milliseconds, rounding down.
74 pub const fn from_millis_floor(millis: u64) -> Duration { 70 pub const fn from_millis_floor(millis: u64) -> Duration {
75 Duration { 71 Duration {
76 ticks: millis * (TICKS_PER_SECOND / GCD_1K) / (1000 / GCD_1K), 72 ticks: millis * (TICK_HZ / GCD_1K) / (1000 / GCD_1K),
77 } 73 }
78 } 74 }
79 75
@@ -81,10 +77,24 @@ impl Duration {
81 /// NOTE: Delays this small may be inaccurate. 77 /// NOTE: Delays this small may be inaccurate.
82 pub const fn from_micros_floor(micros: u64) -> Duration { 78 pub const fn from_micros_floor(micros: u64) -> Duration {
83 Duration { 79 Duration {
84 ticks: micros * (TICKS_PER_SECOND / GCD_1M) / (1_000_000 / GCD_1M), 80 ticks: micros * (TICK_HZ / GCD_1M) / (1_000_000 / GCD_1M),
85 } 81 }
86 } 82 }
87 83
84 /// Creates a duration corresponding to the specified Hz.
85 /// NOTE: Giving this function a hz >= the TICK_HZ of your platform will clamp the Duration to 1
86 /// tick. Doing so will not deadlock, but will certainly not produce the desired output.
87 pub const fn from_hz(hz: u64) -> Duration {
88 let ticks = {
89 if hz >= TICK_HZ {
90 1
91 } else {
92 (TICK_HZ + hz / 2) / hz
93 }
94 };
95 Duration { ticks }
96 }
97
88 /// Adds one Duration to another, returning a new Duration or None in the event of an overflow. 98 /// Adds one Duration to another, returning a new Duration or None in the event of an overflow.
89 pub fn checked_add(self, rhs: Duration) -> Option<Duration> { 99 pub fn checked_add(self, rhs: Duration) -> Option<Duration> {
90 self.ticks.checked_add(rhs.ticks).map(|ticks| Duration { ticks }) 100 self.ticks.checked_add(rhs.ticks).map(|ticks| Duration { ticks })
@@ -182,3 +192,19 @@ impl<'a> fmt::Display for Duration {
182const fn div_ceil(num: u64, den: u64) -> u64 { 192const fn div_ceil(num: u64, den: u64) -> u64 {
183 (num + den - 1) / den 193 (num + den - 1) / den
184} 194}
195
196impl TryFrom<core::time::Duration> for Duration {
197 type Error = <u64 as TryFrom<u128>>::Error;
198
199 /// Converts using [`Duration::from_micros`]. Fails if value can not be represented as u64.
200 fn try_from(value: core::time::Duration) -> Result<Self, Self::Error> {
201 Ok(Self::from_micros(value.as_micros().try_into()?))
202 }
203}
204
205impl From<Duration> for core::time::Duration {
206 /// Converts using [`Duration::as_micros`].
207 fn from(value: Duration) -> Self {
208 core::time::Duration::from_micros(value.as_micros())
209 }
210}
diff --git a/embassy-time/src/instant.rs b/embassy-time/src/instant.rs
index 6a4925f47..44f226f62 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, TICKS_PER_SECOND}; 4use super::{driver, 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))]
@@ -29,21 +29,21 @@ impl Instant {
29 /// Create an Instant from a microsecond count since system boot. 29 /// Create an Instant from a microsecond count since system boot.
30 pub const fn from_micros(micros: u64) -> Self { 30 pub const fn from_micros(micros: u64) -> Self {
31 Self { 31 Self {
32 ticks: micros * (TICKS_PER_SECOND / GCD_1M) / (1_000_000 / GCD_1M), 32 ticks: micros * (TICK_HZ / GCD_1M) / (1_000_000 / GCD_1M),
33 } 33 }
34 } 34 }
35 35
36 /// Create an Instant from a millisecond count since system boot. 36 /// Create an Instant from a millisecond count since system boot.
37 pub const fn from_millis(millis: u64) -> Self { 37 pub const fn from_millis(millis: u64) -> Self {
38 Self { 38 Self {
39 ticks: millis * (TICKS_PER_SECOND / GCD_1K) / (1000 / GCD_1K), 39 ticks: millis * (TICK_HZ / GCD_1K) / (1000 / GCD_1K),
40 } 40 }
41 } 41 }
42 42
43 /// Create an Instant from a second count since system boot. 43 /// Create an Instant from a second count since system boot.
44 pub const fn from_secs(seconds: u64) -> Self { 44 pub const fn from_secs(seconds: u64) -> Self {
45 Self { 45 Self {
46 ticks: seconds * TICKS_PER_SECOND, 46 ticks: seconds * TICK_HZ,
47 } 47 }
48 } 48 }
49 49
@@ -54,17 +54,17 @@ impl Instant {
54 54
55 /// Seconds since system boot. 55 /// Seconds since system boot.
56 pub const fn as_secs(&self) -> u64 { 56 pub const fn as_secs(&self) -> u64 {
57 self.ticks / TICKS_PER_SECOND 57 self.ticks / TICK_HZ
58 } 58 }
59 59
60 /// Milliseconds since system boot. 60 /// Milliseconds since system boot.
61 pub const fn as_millis(&self) -> u64 { 61 pub const fn as_millis(&self) -> u64 {
62 self.ticks * (1000 / GCD_1K) / (TICKS_PER_SECOND / GCD_1K) 62 self.ticks * (1000 / GCD_1K) / (TICK_HZ / GCD_1K)
63 } 63 }
64 64
65 /// Microseconds since system boot. 65 /// Microseconds since system boot.
66 pub const fn as_micros(&self) -> u64 { 66 pub const fn as_micros(&self) -> u64 {
67 self.ticks * (1_000_000 / GCD_1M) / (TICKS_PER_SECOND / GCD_1M) 67 self.ticks * (1_000_000 / GCD_1M) / (TICK_HZ / GCD_1M)
68 } 68 }
69 69
70 /// Duration between this Instant and another Instant 70 /// Duration between this Instant and another Instant
diff --git a/embassy-time/src/lib.rs b/embassy-time/src/lib.rs
index a6c5d78cc..8f57eabcb 100644
--- a/embassy-time/src/lib.rs
+++ b/embassy-time/src/lib.rs
@@ -1,5 +1,5 @@
1#![cfg_attr(not(any(feature = "std", feature = "wasm")), no_std)] 1#![cfg_attr(not(any(feature = "std", feature = "wasm", test)), no_std)]
2#![cfg_attr(feature = "nightly", feature(generic_associated_types, type_alias_impl_trait))] 2#![cfg_attr(feature = "nightly", feature(async_fn_in_trait))]
3#![doc = include_str!("../README.md")] 3#![doc = include_str!("../README.md")]
4#![allow(clippy::new_without_default)] 4#![allow(clippy::new_without_default)]
5#![warn(missing_docs)] 5#![warn(missing_docs)]
@@ -11,37 +11,29 @@ mod delay;
11pub mod driver; 11pub mod driver;
12mod duration; 12mod duration;
13mod instant; 13mod instant;
14pub mod queue;
15mod tick;
14mod timer; 16mod timer;
15 17
16#[cfg(feature = "std")] 18#[cfg(feature = "std")]
17mod driver_std; 19mod driver_std;
18#[cfg(feature = "wasm")] 20#[cfg(feature = "wasm")]
19mod driver_wasm; 21mod driver_wasm;
22#[cfg(feature = "generic-queue")]
23mod queue_generic;
20 24
21pub use delay::{block_for, Delay}; 25pub use delay::{block_for, Delay};
22pub use duration::Duration; 26pub use duration::Duration;
23pub use instant::Instant; 27pub use instant::Instant;
24pub use timer::{with_timeout, Ticker, TimeoutError, Timer}; 28pub use timer::{with_timeout, Ticker, TimeoutError, Timer};
25 29
26#[cfg(feature = "tick-1000hz")]
27const TPS: u64 = 1_000;
28
29#[cfg(feature = "tick-32768hz")]
30const TPS: u64 = 32_768;
31
32#[cfg(feature = "tick-1mhz")]
33const TPS: u64 = 1_000_000;
34
35#[cfg(feature = "tick-16mhz")]
36const TPS: u64 = 16_000_000;
37
38/// Ticks per second of the global timebase. 30/// Ticks per second of the global timebase.
39/// 31///
40/// This value is specified by the `tick-*` Cargo features, which 32/// 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 33/// 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 34/// 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. 35/// set the `tick-*` features for embassy yourself as an end user.
44pub const TICKS_PER_SECOND: u64 = TPS; 36pub const TICK_HZ: u64 = tick::TICK_HZ;
45 37
46const fn gcd(a: u64, b: u64) -> u64 { 38const fn gcd(a: u64, b: u64) -> u64 {
47 if b == 0 { 39 if b == 0 {
@@ -51,8 +43,8 @@ const fn gcd(a: u64, b: u64) -> u64 {
51 } 43 }
52} 44}
53 45
54pub(crate) const GCD_1K: u64 = gcd(TICKS_PER_SECOND, 1_000); 46pub(crate) const GCD_1K: u64 = gcd(TICK_HZ, 1_000);
55pub(crate) const GCD_1M: u64 = gcd(TICKS_PER_SECOND, 1_000_000); 47pub(crate) const GCD_1M: u64 = gcd(TICK_HZ, 1_000_000);
56 48
57#[cfg(feature = "defmt-timestamp-uptime")] 49#[cfg(feature = "defmt-timestamp-uptime")]
58defmt::timestamp! {"{=u64:us}", Instant::now().as_micros() } 50defmt::timestamp! {"{=u64:us}", Instant::now().as_micros() }
diff --git a/embassy-time/src/queue.rs b/embassy-time/src/queue.rs
new file mode 100644
index 000000000..c6f8b440a
--- /dev/null
+++ b/embassy-time/src/queue.rs
@@ -0,0 +1,58 @@
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
new file mode 100644
index 000000000..77947ab29
--- /dev/null
+++ b/embassy-time/src/queue_generic.rs
@@ -0,0 +1,450 @@
1use core::cell::RefCell;
2use core::cmp::{min, Ordering};
3use core::task::Waker;
4
5use critical_section::Mutex;
6use heapless::Vec;
7
8use crate::driver::{allocate_alarm, set_alarm, set_alarm_callback, AlarmHandle};
9use crate::queue::TimerQueue;
10use crate::Instant;
11
12#[cfg(feature = "generic-queue-8")]
13const QUEUE_SIZE: usize = 8;
14#[cfg(feature = "generic-queue-16")]
15const QUEUE_SIZE: usize = 16;
16#[cfg(feature = "generic-queue-32")]
17const QUEUE_SIZE: usize = 32;
18#[cfg(feature = "generic-queue-64")]
19const QUEUE_SIZE: usize = 64;
20#[cfg(feature = "generic-queue-128")]
21const QUEUE_SIZE: usize = 128;
22#[cfg(not(any(
23 feature = "generic-queue-8",
24 feature = "generic-queue-16",
25 feature = "generic-queue-32",
26 feature = "generic-queue-64",
27 feature = "generic-queue-128"
28)))]
29const QUEUE_SIZE: usize = 64;
30
31#[derive(Debug)]
32struct Timer {
33 at: Instant,
34 waker: Waker,
35}
36
37impl PartialEq for Timer {
38 fn eq(&self, other: &Self) -> bool {
39 self.at == other.at
40 }
41}
42
43impl Eq for Timer {}
44
45impl PartialOrd for Timer {
46 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
47 self.at.partial_cmp(&other.at)
48 }
49}
50
51impl Ord for Timer {
52 fn cmp(&self, other: &Self) -> Ordering {
53 self.at.cmp(&other.at)
54 }
55}
56
57struct InnerQueue {
58 queue: Vec<Timer, QUEUE_SIZE>,
59 alarm: AlarmHandle,
60}
61
62impl InnerQueue {
63 fn schedule_wake(&mut self, at: Instant, waker: &Waker) {
64 self.queue
65 .iter_mut()
66 .find(|timer| timer.waker.will_wake(waker))
67 .map(|timer| {
68 timer.at = min(timer.at, at);
69 })
70 .unwrap_or_else(|| {
71 let mut timer = Timer {
72 waker: waker.clone(),
73 at,
74 };
75
76 loop {
77 match self.queue.push(timer) {
78 Ok(()) => break,
79 Err(e) => timer = e,
80 }
81
82 self.queue.pop().unwrap().waker.wake();
83 }
84 });
85
86 // Don't wait for the alarm callback to trigger and directly
87 // dispatch all timers that are already due
88 //
89 // Then update the alarm if necessary
90 self.dispatch();
91 }
92
93 fn dispatch(&mut self) {
94 loop {
95 let now = Instant::now();
96
97 let mut next_alarm = Instant::MAX;
98
99 let mut i = 0;
100 while i < self.queue.len() {
101 let timer = &self.queue[i];
102 if timer.at <= now {
103 let timer = self.queue.swap_remove(i);
104 timer.waker.wake();
105 } else {
106 next_alarm = min(next_alarm, timer.at);
107 i += 1;
108 }
109 }
110
111 if self.update_alarm(next_alarm) {
112 break;
113 }
114 }
115 }
116
117 fn update_alarm(&mut self, next_alarm: Instant) -> bool {
118 if next_alarm == Instant::MAX {
119 true
120 } else {
121 set_alarm(self.alarm, next_alarm.as_ticks())
122 }
123 }
124
125 fn handle_alarm(&mut self) {
126 self.dispatch();
127 }
128}
129
130struct Queue {
131 inner: Mutex<RefCell<Option<InnerQueue>>>,
132}
133
134impl Queue {
135 const fn new() -> Self {
136 Self {
137 inner: Mutex::new(RefCell::new(None)),
138 }
139 }
140
141 fn schedule_wake(&'static self, at: Instant, waker: &Waker) {
142 critical_section::with(|cs| {
143 let mut inner = self.inner.borrow_ref_mut(cs);
144
145 if inner.is_none() {}
146
147 inner
148 .get_or_insert_with(|| {
149 let handle = unsafe { allocate_alarm() }.unwrap();
150 set_alarm_callback(handle, Self::handle_alarm_callback, self as *const _ as _);
151 InnerQueue {
152 queue: Vec::new(),
153 alarm: handle,
154 }
155 })
156 .schedule_wake(at, waker)
157 });
158 }
159
160 fn handle_alarm(&self) {
161 critical_section::with(|cs| self.inner.borrow_ref_mut(cs).as_mut().unwrap().handle_alarm())
162 }
163
164 fn handle_alarm_callback(ctx: *mut ()) {
165 unsafe { (ctx as *const Self).as_ref().unwrap() }.handle_alarm();
166 }
167}
168
169impl TimerQueue for Queue {
170 fn schedule_wake(&'static self, at: Instant, waker: &Waker) {
171 Queue::schedule_wake(self, at, waker);
172 }
173}
174
175crate::timer_queue_impl!(static QUEUE: Queue = Queue::new());
176
177#[cfg(test)]
178mod tests {
179 use core::cell::Cell;
180 use core::task::{RawWaker, RawWakerVTable, Waker};
181 use std::rc::Rc;
182 use std::sync::Mutex;
183
184 use serial_test::serial;
185
186 use crate::driver::{AlarmHandle, Driver};
187 use crate::queue_generic::QUEUE;
188 use crate::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
276 struct TestWaker {
277 pub awoken: Rc<Cell<bool>>,
278 pub waker: Waker,
279 }
280
281 impl TestWaker {
282 fn new() -> Self {
283 let flag = Rc::new(Cell::new(false));
284
285 const VTABLE: RawWakerVTable = RawWakerVTable::new(
286 |data: *const ()| {
287 unsafe {
288 Rc::increment_strong_count(data as *const Cell<bool>);
289 }
290
291 RawWaker::new(data as _, &VTABLE)
292 },
293 |data: *const ()| unsafe {
294 let data = data as *const Cell<bool>;
295 data.as_ref().unwrap().set(true);
296 Rc::decrement_strong_count(data);
297 },
298 |data: *const ()| unsafe {
299 (data as *const Cell<bool>).as_ref().unwrap().set(true);
300 },
301 |data: *const ()| unsafe {
302 Rc::decrement_strong_count(data);
303 },
304 );
305
306 let raw = RawWaker::new(Rc::into_raw(flag.clone()) as _, &VTABLE);
307
308 Self {
309 awoken: flag.clone(),
310 waker: unsafe { Waker::from_raw(raw) },
311 }
312 }
313 }
314
315 crate::time_driver_impl!(static DRIVER: TestDriver = TestDriver::new());
316
317 fn setup() {
318 DRIVER.reset();
319 critical_section::with(|cs| *QUEUE.inner.borrow_ref_mut(cs) = None);
320 }
321
322 fn queue_len() -> usize {
323 critical_section::with(|cs| {
324 QUEUE
325 .inner
326 .borrow_ref(cs)
327 .as_ref()
328 .map(|inner| inner.queue.iter().count())
329 .unwrap_or(0)
330 })
331 }
332
333 #[test]
334 #[serial]
335 fn test_schedule() {
336 setup();
337
338 assert_eq!(queue_len(), 0);
339
340 let waker = TestWaker::new();
341
342 QUEUE.schedule_wake(Instant::from_secs(1), &waker.waker);
343
344 assert!(!waker.awoken.get());
345 assert_eq!(queue_len(), 1);
346 }
347
348 #[test]
349 #[serial]
350 fn test_schedule_same() {
351 setup();
352
353 let waker = TestWaker::new();
354
355 QUEUE.schedule_wake(Instant::from_secs(1), &waker.waker);
356
357 assert_eq!(queue_len(), 1);
358
359 QUEUE.schedule_wake(Instant::from_secs(1), &waker.waker);
360
361 assert_eq!(queue_len(), 1);
362
363 QUEUE.schedule_wake(Instant::from_secs(100), &waker.waker);
364
365 assert_eq!(queue_len(), 1);
366
367 let waker2 = TestWaker::new();
368
369 QUEUE.schedule_wake(Instant::from_secs(100), &waker2.waker);
370
371 assert_eq!(queue_len(), 2);
372 }
373
374 #[test]
375 #[serial]
376 fn test_trigger() {
377 setup();
378
379 let waker = TestWaker::new();
380
381 QUEUE.schedule_wake(Instant::from_secs(100), &waker.waker);
382
383 assert!(!waker.awoken.get());
384
385 DRIVER.set_now(Instant::from_secs(99).as_ticks());
386
387 assert!(!waker.awoken.get());
388
389 assert_eq!(queue_len(), 1);
390
391 DRIVER.set_now(Instant::from_secs(100).as_ticks());
392
393 assert!(waker.awoken.get());
394
395 assert_eq!(queue_len(), 0);
396 }
397
398 #[test]
399 #[serial]
400 fn test_immediate_trigger() {
401 setup();
402
403 let waker = TestWaker::new();
404
405 QUEUE.schedule_wake(Instant::from_secs(100), &waker.waker);
406
407 DRIVER.set_now(Instant::from_secs(50).as_ticks());
408
409 let waker2 = TestWaker::new();
410
411 QUEUE.schedule_wake(Instant::from_secs(40), &waker2.waker);
412
413 assert!(!waker.awoken.get());
414 assert!(waker2.awoken.get());
415 assert_eq!(queue_len(), 1);
416 }
417
418 #[test]
419 #[serial]
420 fn test_queue_overflow() {
421 setup();
422
423 for i in 1..super::QUEUE_SIZE {
424 let waker = TestWaker::new();
425
426 QUEUE.schedule_wake(Instant::from_secs(310), &waker.waker);
427
428 assert_eq!(queue_len(), i);
429 assert!(!waker.awoken.get());
430 }
431
432 let first_waker = TestWaker::new();
433
434 QUEUE.schedule_wake(Instant::from_secs(300), &first_waker.waker);
435
436 assert_eq!(queue_len(), super::QUEUE_SIZE);
437 assert!(!first_waker.awoken.get());
438
439 let second_waker = TestWaker::new();
440
441 QUEUE.schedule_wake(Instant::from_secs(305), &second_waker.waker);
442
443 assert_eq!(queue_len(), super::QUEUE_SIZE);
444 assert!(first_waker.awoken.get());
445
446 QUEUE.schedule_wake(Instant::from_secs(320), &TestWaker::new().waker);
447 assert_eq!(queue_len(), super::QUEUE_SIZE);
448 assert!(second_waker.awoken.get());
449 }
450}
diff --git a/embassy-time/src/tick.rs b/embassy-time/src/tick.rs
new file mode 100644
index 000000000..608bc44f1
--- /dev/null
+++ b/embassy-time/src/tick.rs
@@ -0,0 +1,239 @@
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-2_000_000")]
110pub const TICK_HZ: u64 = 2_000_000;
111#[cfg(feature = "tick-hz-3_000_000")]
112pub const TICK_HZ: u64 = 3_000_000;
113#[cfg(feature = "tick-hz-4_000_000")]
114pub const TICK_HZ: u64 = 4_000_000;
115#[cfg(feature = "tick-hz-6_000_000")]
116pub const TICK_HZ: u64 = 6_000_000;
117#[cfg(feature = "tick-hz-8_000_000")]
118pub const TICK_HZ: u64 = 8_000_000;
119#[cfg(feature = "tick-hz-9_000_000")]
120pub const TICK_HZ: u64 = 9_000_000;
121#[cfg(feature = "tick-hz-12_000_000")]
122pub const TICK_HZ: u64 = 12_000_000;
123#[cfg(feature = "tick-hz-16_000_000")]
124pub const TICK_HZ: u64 = 16_000_000;
125#[cfg(feature = "tick-hz-18_000_000")]
126pub const TICK_HZ: u64 = 18_000_000;
127#[cfg(feature = "tick-hz-24_000_000")]
128pub const TICK_HZ: u64 = 24_000_000;
129#[cfg(feature = "tick-hz-32_000_000")]
130pub const TICK_HZ: u64 = 32_000_000;
131#[cfg(feature = "tick-hz-36_000_000")]
132pub const TICK_HZ: u64 = 36_000_000;
133#[cfg(feature = "tick-hz-48_000_000")]
134pub const TICK_HZ: u64 = 48_000_000;
135#[cfg(feature = "tick-hz-64_000_000")]
136pub const TICK_HZ: u64 = 64_000_000;
137#[cfg(feature = "tick-hz-72_000_000")]
138pub const TICK_HZ: u64 = 72_000_000;
139#[cfg(feature = "tick-hz-96_000_000")]
140pub const TICK_HZ: u64 = 96_000_000;
141#[cfg(feature = "tick-hz-128_000_000")]
142pub const TICK_HZ: u64 = 128_000_000;
143#[cfg(feature = "tick-hz-144_000_000")]
144pub const TICK_HZ: u64 = 144_000_000;
145#[cfg(feature = "tick-hz-192_000_000")]
146pub const TICK_HZ: u64 = 192_000_000;
147#[cfg(feature = "tick-hz-256_000_000")]
148pub const TICK_HZ: u64 = 256_000_000;
149#[cfg(feature = "tick-hz-288_000_000")]
150pub const TICK_HZ: u64 = 288_000_000;
151#[cfg(feature = "tick-hz-384_000_000")]
152pub const TICK_HZ: u64 = 384_000_000;
153#[cfg(feature = "tick-hz-512_000_000")]
154pub const TICK_HZ: u64 = 512_000_000;
155#[cfg(feature = "tick-hz-576_000_000")]
156pub const TICK_HZ: u64 = 576_000_000;
157#[cfg(feature = "tick-hz-768_000_000")]
158pub const TICK_HZ: u64 = 768_000_000;
159#[cfg(not(any(
160 feature = "tick-hz-1",
161 feature = "tick-hz-10",
162 feature = "tick-hz-100",
163 feature = "tick-hz-1_000",
164 feature = "tick-hz-10_000",
165 feature = "tick-hz-100_000",
166 feature = "tick-hz-1_000_000",
167 feature = "tick-hz-10_000_000",
168 feature = "tick-hz-100_000_000",
169 feature = "tick-hz-1_000_000_000",
170 feature = "tick-hz-2",
171 feature = "tick-hz-4",
172 feature = "tick-hz-8",
173 feature = "tick-hz-16",
174 feature = "tick-hz-32",
175 feature = "tick-hz-64",
176 feature = "tick-hz-128",
177 feature = "tick-hz-256",
178 feature = "tick-hz-512",
179 feature = "tick-hz-1_024",
180 feature = "tick-hz-2_048",
181 feature = "tick-hz-4_096",
182 feature = "tick-hz-8_192",
183 feature = "tick-hz-16_384",
184 feature = "tick-hz-32_768",
185 feature = "tick-hz-65_536",
186 feature = "tick-hz-131_072",
187 feature = "tick-hz-262_144",
188 feature = "tick-hz-524_288",
189 feature = "tick-hz-1_048_576",
190 feature = "tick-hz-2_097_152",
191 feature = "tick-hz-4_194_304",
192 feature = "tick-hz-8_388_608",
193 feature = "tick-hz-16_777_216",
194 feature = "tick-hz-2_000",
195 feature = "tick-hz-4_000",
196 feature = "tick-hz-8_000",
197 feature = "tick-hz-16_000",
198 feature = "tick-hz-32_000",
199 feature = "tick-hz-64_000",
200 feature = "tick-hz-128_000",
201 feature = "tick-hz-256_000",
202 feature = "tick-hz-512_000",
203 feature = "tick-hz-1_024_000",
204 feature = "tick-hz-2_048_000",
205 feature = "tick-hz-4_096_000",
206 feature = "tick-hz-8_192_000",
207 feature = "tick-hz-16_384_000",
208 feature = "tick-hz-32_768_000",
209 feature = "tick-hz-65_536_000",
210 feature = "tick-hz-131_072_000",
211 feature = "tick-hz-262_144_000",
212 feature = "tick-hz-524_288_000",
213 feature = "tick-hz-2_000_000",
214 feature = "tick-hz-3_000_000",
215 feature = "tick-hz-4_000_000",
216 feature = "tick-hz-6_000_000",
217 feature = "tick-hz-8_000_000",
218 feature = "tick-hz-9_000_000",
219 feature = "tick-hz-12_000_000",
220 feature = "tick-hz-16_000_000",
221 feature = "tick-hz-18_000_000",
222 feature = "tick-hz-24_000_000",
223 feature = "tick-hz-32_000_000",
224 feature = "tick-hz-36_000_000",
225 feature = "tick-hz-48_000_000",
226 feature = "tick-hz-64_000_000",
227 feature = "tick-hz-72_000_000",
228 feature = "tick-hz-96_000_000",
229 feature = "tick-hz-128_000_000",
230 feature = "tick-hz-144_000_000",
231 feature = "tick-hz-192_000_000",
232 feature = "tick-hz-256_000_000",
233 feature = "tick-hz-288_000_000",
234 feature = "tick-hz-384_000_000",
235 feature = "tick-hz-512_000_000",
236 feature = "tick-hz-576_000_000",
237 feature = "tick-hz-768_000_000",
238)))]
239pub const TICK_HZ: u64 = 1_000_000;
diff --git a/embassy-time/src/timer.rs b/embassy-time/src/timer.rs
index bd791b817..d3d1f9f5f 100644
--- a/embassy-time/src/timer.rs
+++ b/embassy-time/src/timer.rs
@@ -1,4 +1,4 @@
1use core::future::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, Waker};
4 4
@@ -26,6 +26,7 @@ pub async fn with_timeout<F: Future>(timeout: Duration, fut: F) -> Result<F::Out
26} 26}
27 27
28/// A future that completes at a specified [Instant](struct.Instant.html). 28/// A future that completes at a specified [Instant](struct.Instant.html).
29#[must_use = "futures do nothing unless you `.await` or poll them"]
29pub struct Timer { 30pub struct Timer {
30 expires_at: Instant, 31 expires_at: Instant,
31 yielded_once: bool, 32 yielded_once: bool,
@@ -108,7 +109,6 @@ impl Future for Timer {
108/// # #![feature(type_alias_impl_trait)] 109/// # #![feature(type_alias_impl_trait)]
109/// # 110/// #
110/// use embassy_time::{Duration, Ticker}; 111/// use embassy_time::{Duration, Ticker};
111/// use futures::StreamExt;
112/// # fn foo(){} 112/// # fn foo(){}
113/// 113///
114/// #[embassy_executor::task] 114/// #[embassy_executor::task]
@@ -131,6 +131,20 @@ impl Ticker {
131 let expires_at = Instant::now() + duration; 131 let expires_at = Instant::now() + duration;
132 Self { expires_at, duration } 132 Self { expires_at, duration }
133 } 133 }
134
135 /// Waits for the next tick
136 pub fn next(&mut self) -> impl Future<Output = ()> + '_ {
137 poll_fn(|cx| {
138 if self.expires_at <= Instant::now() {
139 let dur = self.duration;
140 self.expires_at += dur;
141 Poll::Ready(())
142 } else {
143 schedule_wake(self.expires_at, cx.waker());
144 Poll::Pending
145 }
146 })
147 }
134} 148}
135 149
136impl Unpin for Ticker {} 150impl Unpin for Ticker {}