diff options
| author | Quentin Smith <[email protected]> | 2023-07-17 21:31:43 -0400 |
|---|---|---|
| committer | Quentin Smith <[email protected]> | 2023-07-17 21:31:43 -0400 |
| commit | 6f02403184eb7fb7990fb88fc9df9c4328a690a3 (patch) | |
| tree | 748f510e190bb2724750507a6e69ed1a8e08cb20 /embassy-time/src | |
| parent | d896f80405aa8963877049ed999e4aba25d6e2bb (diff) | |
| parent | 6b5df4523aa1c4902f02e803450ae4b418e0e3ca (diff) | |
Merge remote-tracking branch 'origin/main' into nrf-pdm
Diffstat (limited to 'embassy-time/src')
| -rw-r--r-- | embassy-time/src/delay.rs | 40 | ||||
| -rw-r--r-- | embassy-time/src/driver.rs | 17 | ||||
| -rw-r--r-- | embassy-time/src/driver_std.rs | 71 | ||||
| -rw-r--r-- | embassy-time/src/driver_wasm.rs | 16 | ||||
| -rw-r--r-- | embassy-time/src/duration.rs | 54 | ||||
| -rw-r--r-- | embassy-time/src/instant.rs | 14 | ||||
| -rw-r--r-- | embassy-time/src/lib.rs | 26 | ||||
| -rw-r--r-- | embassy-time/src/queue.rs | 58 | ||||
| -rw-r--r-- | embassy-time/src/queue_generic.rs | 450 | ||||
| -rw-r--r-- | embassy-time/src/tick.rs | 239 | ||||
| -rw-r--r-- | embassy-time/src/timer.rs | 18 |
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; | |||
| 18 | mod eh1 { | 18 | mod 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 | ||
| 34 | cfg_if::cfg_if! { | 32 | #[cfg(all(feature = "unstable-traits", feature = "nightly"))] |
| 35 | if #[cfg(all(feature = "unstable-traits", feature = "nightly"))] { | 33 | mod 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 | ||
| 117 | extern "Rust" { | 118 | extern "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`] |
| 142 | pub fn set_alarm(alarm: AlarmHandle, timestamp: u64) { | 143 | pub 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 @@ | |||
| 1 | use std::cell::UnsafeCell; | 1 | use core::sync::atomic::{AtomicU8, Ordering}; |
| 2 | use std::cell::{RefCell, UnsafeCell}; | ||
| 2 | use std::mem::MaybeUninit; | 3 | use std::mem::MaybeUninit; |
| 3 | use std::sync::{Condvar, Mutex, Once}; | 4 | use std::sync::{Condvar, Mutex, Once}; |
| 4 | use std::time::{Duration as StdDuration, Instant as StdInstant}; | 5 | use std::time::{Duration as StdDuration, Instant as StdInstant}; |
| 5 | use std::{mem, ptr, thread}; | 6 | use std::{mem, ptr, thread}; |
| 6 | 7 | ||
| 7 | use atomic_polyfill::{AtomicU8, Ordering}; | 8 | use critical_section::Mutex as CsMutex; |
| 8 | 9 | ||
| 9 | use crate::driver::{AlarmHandle, Driver}; | 10 | use 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 { | |||
| 53 | impl TimeDriver { | 57 | impl 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 @@ | |||
| 1 | use core::sync::atomic::{AtomicU8, Ordering}; | ||
| 1 | use std::cell::UnsafeCell; | 2 | use std::cell::UnsafeCell; |
| 2 | use std::mem::MaybeUninit; | 3 | use std::mem::MaybeUninit; |
| 3 | use std::ptr; | 4 | use std::ptr; |
| 4 | use std::sync::{Mutex, Once}; | 5 | use std::sync::{Mutex, Once}; |
| 5 | 6 | ||
| 6 | use atomic_polyfill::{AtomicU8, Ordering}; | ||
| 7 | use wasm_bindgen::prelude::*; | 7 | use wasm_bindgen::prelude::*; |
| 8 | use wasm_timer::Instant as StdInstant; | 8 | use 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 @@ | |||
| 1 | use core::fmt; | 1 | use core::fmt; |
| 2 | use core::ops::{Add, AddAssign, Div, DivAssign, Mul, MulAssign, Sub, SubAssign}; | 2 | use core::ops::{Add, AddAssign, Div, DivAssign, Mul, MulAssign, Sub, SubAssign}; |
| 3 | 3 | ||
| 4 | use super::{GCD_1K, GCD_1M, TICKS_PER_SECOND}; | 4 | use 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 { | |||
| 182 | const fn div_ceil(num: u64, den: u64) -> u64 { | 192 | const fn div_ceil(num: u64, den: u64) -> u64 { |
| 183 | (num + den - 1) / den | 193 | (num + den - 1) / den |
| 184 | } | 194 | } |
| 195 | |||
| 196 | impl 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 | |||
| 205 | impl 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 @@ | |||
| 1 | use core::fmt; | 1 | use core::fmt; |
| 2 | use core::ops::{Add, AddAssign, Sub, SubAssign}; | 2 | use core::ops::{Add, AddAssign, Sub, SubAssign}; |
| 3 | 3 | ||
| 4 | use super::{driver, Duration, GCD_1K, GCD_1M, TICKS_PER_SECOND}; | 4 | use 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; | |||
| 11 | pub mod driver; | 11 | pub mod driver; |
| 12 | mod duration; | 12 | mod duration; |
| 13 | mod instant; | 13 | mod instant; |
| 14 | pub mod queue; | ||
| 15 | mod tick; | ||
| 14 | mod timer; | 16 | mod timer; |
| 15 | 17 | ||
| 16 | #[cfg(feature = "std")] | 18 | #[cfg(feature = "std")] |
| 17 | mod driver_std; | 19 | mod driver_std; |
| 18 | #[cfg(feature = "wasm")] | 20 | #[cfg(feature = "wasm")] |
| 19 | mod driver_wasm; | 21 | mod driver_wasm; |
| 22 | #[cfg(feature = "generic-queue")] | ||
| 23 | mod queue_generic; | ||
| 20 | 24 | ||
| 21 | pub use delay::{block_for, Delay}; | 25 | pub use delay::{block_for, Delay}; |
| 22 | pub use duration::Duration; | 26 | pub use duration::Duration; |
| 23 | pub use instant::Instant; | 27 | pub use instant::Instant; |
| 24 | pub use timer::{with_timeout, Ticker, TimeoutError, Timer}; | 28 | pub use timer::{with_timeout, Ticker, TimeoutError, Timer}; |
| 25 | 29 | ||
| 26 | #[cfg(feature = "tick-1000hz")] | ||
| 27 | const TPS: u64 = 1_000; | ||
| 28 | |||
| 29 | #[cfg(feature = "tick-32768hz")] | ||
| 30 | const TPS: u64 = 32_768; | ||
| 31 | |||
| 32 | #[cfg(feature = "tick-1mhz")] | ||
| 33 | const TPS: u64 = 1_000_000; | ||
| 34 | |||
| 35 | #[cfg(feature = "tick-16mhz")] | ||
| 36 | const 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. |
| 44 | pub const TICKS_PER_SECOND: u64 = TPS; | 36 | pub const TICK_HZ: u64 = tick::TICK_HZ; |
| 45 | 37 | ||
| 46 | const fn gcd(a: u64, b: u64) -> u64 { | 38 | const 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 | ||
| 54 | pub(crate) const GCD_1K: u64 = gcd(TICKS_PER_SECOND, 1_000); | 46 | pub(crate) const GCD_1K: u64 = gcd(TICK_HZ, 1_000); |
| 55 | pub(crate) const GCD_1M: u64 = gcd(TICKS_PER_SECOND, 1_000_000); | 47 | pub(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")] |
| 58 | defmt::timestamp! {"{=u64:us}", Instant::now().as_micros() } | 50 | defmt::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 | //! ``` | ||
| 34 | use core::task::Waker; | ||
| 35 | |||
| 36 | use crate::Instant; | ||
| 37 | |||
| 38 | /// Timer queue | ||
| 39 | pub 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] | ||
| 49 | macro_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 @@ | |||
| 1 | use core::cell::RefCell; | ||
| 2 | use core::cmp::{min, Ordering}; | ||
| 3 | use core::task::Waker; | ||
| 4 | |||
| 5 | use critical_section::Mutex; | ||
| 6 | use heapless::Vec; | ||
| 7 | |||
| 8 | use crate::driver::{allocate_alarm, set_alarm, set_alarm_callback, AlarmHandle}; | ||
| 9 | use crate::queue::TimerQueue; | ||
| 10 | use crate::Instant; | ||
| 11 | |||
| 12 | #[cfg(feature = "generic-queue-8")] | ||
| 13 | const QUEUE_SIZE: usize = 8; | ||
| 14 | #[cfg(feature = "generic-queue-16")] | ||
| 15 | const QUEUE_SIZE: usize = 16; | ||
| 16 | #[cfg(feature = "generic-queue-32")] | ||
| 17 | const QUEUE_SIZE: usize = 32; | ||
| 18 | #[cfg(feature = "generic-queue-64")] | ||
| 19 | const QUEUE_SIZE: usize = 64; | ||
| 20 | #[cfg(feature = "generic-queue-128")] | ||
| 21 | const 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 | )))] | ||
| 29 | const QUEUE_SIZE: usize = 64; | ||
| 30 | |||
| 31 | #[derive(Debug)] | ||
| 32 | struct Timer { | ||
| 33 | at: Instant, | ||
| 34 | waker: Waker, | ||
| 35 | } | ||
| 36 | |||
| 37 | impl PartialEq for Timer { | ||
| 38 | fn eq(&self, other: &Self) -> bool { | ||
| 39 | self.at == other.at | ||
| 40 | } | ||
| 41 | } | ||
| 42 | |||
| 43 | impl Eq for Timer {} | ||
| 44 | |||
| 45 | impl PartialOrd for Timer { | ||
| 46 | fn partial_cmp(&self, other: &Self) -> Option<Ordering> { | ||
| 47 | self.at.partial_cmp(&other.at) | ||
| 48 | } | ||
| 49 | } | ||
| 50 | |||
| 51 | impl Ord for Timer { | ||
| 52 | fn cmp(&self, other: &Self) -> Ordering { | ||
| 53 | self.at.cmp(&other.at) | ||
| 54 | } | ||
| 55 | } | ||
| 56 | |||
| 57 | struct InnerQueue { | ||
| 58 | queue: Vec<Timer, QUEUE_SIZE>, | ||
| 59 | alarm: AlarmHandle, | ||
| 60 | } | ||
| 61 | |||
| 62 | impl 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 | |||
| 130 | struct Queue { | ||
| 131 | inner: Mutex<RefCell<Option<InnerQueue>>>, | ||
| 132 | } | ||
| 133 | |||
| 134 | impl 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 | |||
| 169 | impl TimerQueue for Queue { | ||
| 170 | fn schedule_wake(&'static self, at: Instant, waker: &Waker) { | ||
| 171 | Queue::schedule_wake(self, at, waker); | ||
| 172 | } | ||
| 173 | } | ||
| 174 | |||
| 175 | crate::timer_queue_impl!(static QUEUE: Queue = Queue::new()); | ||
| 176 | |||
| 177 | #[cfg(test)] | ||
| 178 | mod 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")] | ||
| 4 | pub const TICK_HZ: u64 = 1; | ||
| 5 | #[cfg(feature = "tick-hz-10")] | ||
| 6 | pub const TICK_HZ: u64 = 10; | ||
| 7 | #[cfg(feature = "tick-hz-100")] | ||
| 8 | pub const TICK_HZ: u64 = 100; | ||
| 9 | #[cfg(feature = "tick-hz-1_000")] | ||
| 10 | pub const TICK_HZ: u64 = 1_000; | ||
| 11 | #[cfg(feature = "tick-hz-10_000")] | ||
| 12 | pub const TICK_HZ: u64 = 10_000; | ||
| 13 | #[cfg(feature = "tick-hz-100_000")] | ||
| 14 | pub const TICK_HZ: u64 = 100_000; | ||
| 15 | #[cfg(feature = "tick-hz-1_000_000")] | ||
| 16 | pub const TICK_HZ: u64 = 1_000_000; | ||
| 17 | #[cfg(feature = "tick-hz-10_000_000")] | ||
| 18 | pub const TICK_HZ: u64 = 10_000_000; | ||
| 19 | #[cfg(feature = "tick-hz-100_000_000")] | ||
| 20 | pub const TICK_HZ: u64 = 100_000_000; | ||
| 21 | #[cfg(feature = "tick-hz-1_000_000_000")] | ||
| 22 | pub const TICK_HZ: u64 = 1_000_000_000; | ||
| 23 | #[cfg(feature = "tick-hz-2")] | ||
| 24 | pub const TICK_HZ: u64 = 2; | ||
| 25 | #[cfg(feature = "tick-hz-4")] | ||
| 26 | pub const TICK_HZ: u64 = 4; | ||
| 27 | #[cfg(feature = "tick-hz-8")] | ||
| 28 | pub const TICK_HZ: u64 = 8; | ||
| 29 | #[cfg(feature = "tick-hz-16")] | ||
| 30 | pub const TICK_HZ: u64 = 16; | ||
| 31 | #[cfg(feature = "tick-hz-32")] | ||
| 32 | pub const TICK_HZ: u64 = 32; | ||
| 33 | #[cfg(feature = "tick-hz-64")] | ||
| 34 | pub const TICK_HZ: u64 = 64; | ||
| 35 | #[cfg(feature = "tick-hz-128")] | ||
| 36 | pub const TICK_HZ: u64 = 128; | ||
| 37 | #[cfg(feature = "tick-hz-256")] | ||
| 38 | pub const TICK_HZ: u64 = 256; | ||
| 39 | #[cfg(feature = "tick-hz-512")] | ||
| 40 | pub const TICK_HZ: u64 = 512; | ||
| 41 | #[cfg(feature = "tick-hz-1_024")] | ||
| 42 | pub const TICK_HZ: u64 = 1_024; | ||
| 43 | #[cfg(feature = "tick-hz-2_048")] | ||
| 44 | pub const TICK_HZ: u64 = 2_048; | ||
| 45 | #[cfg(feature = "tick-hz-4_096")] | ||
| 46 | pub const TICK_HZ: u64 = 4_096; | ||
| 47 | #[cfg(feature = "tick-hz-8_192")] | ||
| 48 | pub const TICK_HZ: u64 = 8_192; | ||
| 49 | #[cfg(feature = "tick-hz-16_384")] | ||
| 50 | pub const TICK_HZ: u64 = 16_384; | ||
| 51 | #[cfg(feature = "tick-hz-32_768")] | ||
| 52 | pub const TICK_HZ: u64 = 32_768; | ||
| 53 | #[cfg(feature = "tick-hz-65_536")] | ||
| 54 | pub const TICK_HZ: u64 = 65_536; | ||
| 55 | #[cfg(feature = "tick-hz-131_072")] | ||
| 56 | pub const TICK_HZ: u64 = 131_072; | ||
| 57 | #[cfg(feature = "tick-hz-262_144")] | ||
| 58 | pub const TICK_HZ: u64 = 262_144; | ||
| 59 | #[cfg(feature = "tick-hz-524_288")] | ||
| 60 | pub const TICK_HZ: u64 = 524_288; | ||
| 61 | #[cfg(feature = "tick-hz-1_048_576")] | ||
| 62 | pub const TICK_HZ: u64 = 1_048_576; | ||
| 63 | #[cfg(feature = "tick-hz-2_097_152")] | ||
| 64 | pub const TICK_HZ: u64 = 2_097_152; | ||
| 65 | #[cfg(feature = "tick-hz-4_194_304")] | ||
| 66 | pub const TICK_HZ: u64 = 4_194_304; | ||
| 67 | #[cfg(feature = "tick-hz-8_388_608")] | ||
| 68 | pub const TICK_HZ: u64 = 8_388_608; | ||
| 69 | #[cfg(feature = "tick-hz-16_777_216")] | ||
| 70 | pub const TICK_HZ: u64 = 16_777_216; | ||
| 71 | #[cfg(feature = "tick-hz-2_000")] | ||
| 72 | pub const TICK_HZ: u64 = 2_000; | ||
| 73 | #[cfg(feature = "tick-hz-4_000")] | ||
| 74 | pub const TICK_HZ: u64 = 4_000; | ||
| 75 | #[cfg(feature = "tick-hz-8_000")] | ||
| 76 | pub const TICK_HZ: u64 = 8_000; | ||
| 77 | #[cfg(feature = "tick-hz-16_000")] | ||
| 78 | pub const TICK_HZ: u64 = 16_000; | ||
| 79 | #[cfg(feature = "tick-hz-32_000")] | ||
| 80 | pub const TICK_HZ: u64 = 32_000; | ||
| 81 | #[cfg(feature = "tick-hz-64_000")] | ||
| 82 | pub const TICK_HZ: u64 = 64_000; | ||
| 83 | #[cfg(feature = "tick-hz-128_000")] | ||
| 84 | pub const TICK_HZ: u64 = 128_000; | ||
| 85 | #[cfg(feature = "tick-hz-256_000")] | ||
| 86 | pub const TICK_HZ: u64 = 256_000; | ||
| 87 | #[cfg(feature = "tick-hz-512_000")] | ||
| 88 | pub const TICK_HZ: u64 = 512_000; | ||
| 89 | #[cfg(feature = "tick-hz-1_024_000")] | ||
| 90 | pub const TICK_HZ: u64 = 1_024_000; | ||
| 91 | #[cfg(feature = "tick-hz-2_048_000")] | ||
| 92 | pub const TICK_HZ: u64 = 2_048_000; | ||
| 93 | #[cfg(feature = "tick-hz-4_096_000")] | ||
| 94 | pub const TICK_HZ: u64 = 4_096_000; | ||
| 95 | #[cfg(feature = "tick-hz-8_192_000")] | ||
| 96 | pub const TICK_HZ: u64 = 8_192_000; | ||
| 97 | #[cfg(feature = "tick-hz-16_384_000")] | ||
| 98 | pub const TICK_HZ: u64 = 16_384_000; | ||
| 99 | #[cfg(feature = "tick-hz-32_768_000")] | ||
| 100 | pub const TICK_HZ: u64 = 32_768_000; | ||
| 101 | #[cfg(feature = "tick-hz-65_536_000")] | ||
| 102 | pub const TICK_HZ: u64 = 65_536_000; | ||
| 103 | #[cfg(feature = "tick-hz-131_072_000")] | ||
| 104 | pub const TICK_HZ: u64 = 131_072_000; | ||
| 105 | #[cfg(feature = "tick-hz-262_144_000")] | ||
| 106 | pub const TICK_HZ: u64 = 262_144_000; | ||
| 107 | #[cfg(feature = "tick-hz-524_288_000")] | ||
| 108 | pub const TICK_HZ: u64 = 524_288_000; | ||
| 109 | #[cfg(feature = "tick-hz-2_000_000")] | ||
| 110 | pub const TICK_HZ: u64 = 2_000_000; | ||
| 111 | #[cfg(feature = "tick-hz-3_000_000")] | ||
| 112 | pub const TICK_HZ: u64 = 3_000_000; | ||
| 113 | #[cfg(feature = "tick-hz-4_000_000")] | ||
| 114 | pub const TICK_HZ: u64 = 4_000_000; | ||
| 115 | #[cfg(feature = "tick-hz-6_000_000")] | ||
| 116 | pub const TICK_HZ: u64 = 6_000_000; | ||
| 117 | #[cfg(feature = "tick-hz-8_000_000")] | ||
| 118 | pub const TICK_HZ: u64 = 8_000_000; | ||
| 119 | #[cfg(feature = "tick-hz-9_000_000")] | ||
| 120 | pub const TICK_HZ: u64 = 9_000_000; | ||
| 121 | #[cfg(feature = "tick-hz-12_000_000")] | ||
| 122 | pub const TICK_HZ: u64 = 12_000_000; | ||
| 123 | #[cfg(feature = "tick-hz-16_000_000")] | ||
| 124 | pub const TICK_HZ: u64 = 16_000_000; | ||
| 125 | #[cfg(feature = "tick-hz-18_000_000")] | ||
| 126 | pub const TICK_HZ: u64 = 18_000_000; | ||
| 127 | #[cfg(feature = "tick-hz-24_000_000")] | ||
| 128 | pub const TICK_HZ: u64 = 24_000_000; | ||
| 129 | #[cfg(feature = "tick-hz-32_000_000")] | ||
| 130 | pub const TICK_HZ: u64 = 32_000_000; | ||
| 131 | #[cfg(feature = "tick-hz-36_000_000")] | ||
| 132 | pub const TICK_HZ: u64 = 36_000_000; | ||
| 133 | #[cfg(feature = "tick-hz-48_000_000")] | ||
| 134 | pub const TICK_HZ: u64 = 48_000_000; | ||
| 135 | #[cfg(feature = "tick-hz-64_000_000")] | ||
| 136 | pub const TICK_HZ: u64 = 64_000_000; | ||
| 137 | #[cfg(feature = "tick-hz-72_000_000")] | ||
| 138 | pub const TICK_HZ: u64 = 72_000_000; | ||
| 139 | #[cfg(feature = "tick-hz-96_000_000")] | ||
| 140 | pub const TICK_HZ: u64 = 96_000_000; | ||
| 141 | #[cfg(feature = "tick-hz-128_000_000")] | ||
| 142 | pub const TICK_HZ: u64 = 128_000_000; | ||
| 143 | #[cfg(feature = "tick-hz-144_000_000")] | ||
| 144 | pub const TICK_HZ: u64 = 144_000_000; | ||
| 145 | #[cfg(feature = "tick-hz-192_000_000")] | ||
| 146 | pub const TICK_HZ: u64 = 192_000_000; | ||
| 147 | #[cfg(feature = "tick-hz-256_000_000")] | ||
| 148 | pub const TICK_HZ: u64 = 256_000_000; | ||
| 149 | #[cfg(feature = "tick-hz-288_000_000")] | ||
| 150 | pub const TICK_HZ: u64 = 288_000_000; | ||
| 151 | #[cfg(feature = "tick-hz-384_000_000")] | ||
| 152 | pub const TICK_HZ: u64 = 384_000_000; | ||
| 153 | #[cfg(feature = "tick-hz-512_000_000")] | ||
| 154 | pub const TICK_HZ: u64 = 512_000_000; | ||
| 155 | #[cfg(feature = "tick-hz-576_000_000")] | ||
| 156 | pub const TICK_HZ: u64 = 576_000_000; | ||
| 157 | #[cfg(feature = "tick-hz-768_000_000")] | ||
| 158 | pub 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 | )))] | ||
| 239 | pub 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 @@ | |||
| 1 | use core::future::Future; | 1 | use core::future::{poll_fn, Future}; |
| 2 | use core::pin::Pin; | 2 | use core::pin::Pin; |
| 3 | use core::task::{Context, Poll, Waker}; | 3 | use 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"] | ||
| 29 | pub struct Timer { | 30 | pub 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 | ||
| 136 | impl Unpin for Ticker {} | 150 | impl Unpin for Ticker {} |
