diff options
| author | Dario Nieuwenhuis <[email protected]> | 2020-09-25 03:25:06 +0200 |
|---|---|---|
| committer | Dario Nieuwenhuis <[email protected]> | 2020-09-25 03:25:06 +0200 |
| commit | 4333105341072bc5391809906c5c68fee1a31a8e (patch) | |
| tree | 1628046ad9b452bc253ac289a4ec3af5998a21a9 | |
| parent | 05ca563e7d61eb13e3a136092177986623d68617 (diff) | |
Add Executor with timer queue, Timer, Instant, Duration, Alarm.
| -rw-r--r-- | .gitignore | 1 | ||||
| -rw-r--r-- | Cargo.toml | 5 | ||||
| -rw-r--r-- | embassy-nrf/src/rtc.rs | 35 | ||||
| -rw-r--r-- | embassy/Cargo.toml | 1 | ||||
| -rw-r--r-- | embassy/src/clock.rs | 21 | ||||
| -rw-r--r-- | embassy/src/executor.rs | 72 | ||||
| -rw-r--r-- | embassy/src/lib.rs | 3 | ||||
| -rw-r--r-- | embassy/src/time.rs | 287 | ||||
| -rw-r--r-- | examples/Cargo.toml | 3 | ||||
| -rw-r--r-- | examples/src/bin/rtc_async.rs | 61 | ||||
| -rw-r--r-- | examples/src/bin/rtc_raw.rs | 7 |
11 files changed, 413 insertions, 83 deletions
diff --git a/.gitignore b/.gitignore index 96ef6c0b9..25d18d5aa 100644 --- a/.gitignore +++ b/.gitignore | |||
| @@ -1,2 +1,3 @@ | |||
| 1 | /target | 1 | /target |
| 2 | Cargo.lock | 2 | Cargo.lock |
| 3 | third_party | ||
diff --git a/Cargo.toml b/Cargo.toml index 25e30eff1..abce83ec6 100644 --- a/Cargo.toml +++ b/Cargo.toml | |||
| @@ -6,11 +6,16 @@ members = [ | |||
| 6 | "examples", | 6 | "examples", |
| 7 | ] | 7 | ] |
| 8 | 8 | ||
| 9 | exclude = [ | ||
| 10 | "third_party" | ||
| 11 | ] | ||
| 12 | |||
| 9 | [patch.crates-io] | 13 | [patch.crates-io] |
| 10 | panic-probe = { git = "https://github.com/knurling-rs/probe-run", branch="main" } | 14 | panic-probe = { git = "https://github.com/knurling-rs/probe-run", branch="main" } |
| 11 | defmt-rtt = { git = "https://github.com/knurling-rs/defmt", branch="cursed-symbol-names-linkers-must-repent-for-their-sins" } | 15 | defmt-rtt = { git = "https://github.com/knurling-rs/defmt", branch="cursed-symbol-names-linkers-must-repent-for-their-sins" } |
| 12 | defmt = { git = "https://github.com/knurling-rs/defmt", branch="cursed-symbol-names-linkers-must-repent-for-their-sins" } | 16 | defmt = { git = "https://github.com/knurling-rs/defmt", branch="cursed-symbol-names-linkers-must-repent-for-their-sins" } |
| 13 | static-executor = { git = "https://github.com/Dirbaio/static-executor", branch="multi"} | 17 | static-executor = { git = "https://github.com/Dirbaio/static-executor", branch="multi"} |
| 18 | futures-intrusive = { git = "https://github.com/Dirbaio/futures-intrusive", branch="master"} | ||
| 14 | 19 | ||
| 15 | [profile.dev] | 20 | [profile.dev] |
| 16 | codegen-units = 1 | 21 | codegen-units = 1 |
diff --git a/embassy-nrf/src/rtc.rs b/embassy-nrf/src/rtc.rs index 55869c6e5..ddb4fd367 100644 --- a/embassy-nrf/src/rtc.rs +++ b/embassy-nrf/src/rtc.rs | |||
| @@ -1,8 +1,6 @@ | |||
| 1 | use core::cell::Cell; | 1 | use core::cell::Cell; |
| 2 | use core::ops::Deref; | 2 | use core::ops::Deref; |
| 3 | use core::sync::atomic::{AtomicU32, Ordering}; | 3 | use core::sync::atomic::{AtomicU32, Ordering}; |
| 4 | use defmt::trace; | ||
| 5 | use embassy::clock::Monotonic; | ||
| 6 | 4 | ||
| 7 | use crate::interrupt; | 5 | use crate::interrupt; |
| 8 | use crate::interrupt::Mutex; | 6 | use crate::interrupt::Mutex; |
| @@ -86,22 +84,25 @@ impl<T: Instance> RTC<T> { | |||
| 86 | interrupt::enable(T::INTERRUPT); | 84 | interrupt::enable(T::INTERRUPT); |
| 87 | } | 85 | } |
| 88 | 86 | ||
| 87 | pub fn now(&self) -> u64 { | ||
| 88 | let counter = self.rtc.counter.read().bits(); | ||
| 89 | let period = self.period.load(Ordering::Relaxed); | ||
| 90 | calc_now(period, counter) | ||
| 91 | } | ||
| 92 | |||
| 89 | fn on_interrupt(&self) { | 93 | fn on_interrupt(&self) { |
| 90 | if self.rtc.events_ovrflw.read().bits() == 1 { | 94 | if self.rtc.events_ovrflw.read().bits() == 1 { |
| 91 | self.rtc.events_ovrflw.write(|w| w); | 95 | self.rtc.events_ovrflw.write(|w| w); |
| 92 | trace!("rtc overflow"); | ||
| 93 | self.next_period(); | 96 | self.next_period(); |
| 94 | } | 97 | } |
| 95 | 98 | ||
| 96 | if self.rtc.events_compare[0].read().bits() == 1 { | 99 | if self.rtc.events_compare[0].read().bits() == 1 { |
| 97 | self.rtc.events_compare[0].write(|w| w); | 100 | self.rtc.events_compare[0].write(|w| w); |
| 98 | trace!("rtc compare0"); | ||
| 99 | self.next_period(); | 101 | self.next_period(); |
| 100 | } | 102 | } |
| 101 | 103 | ||
| 102 | if self.rtc.events_compare[1].read().bits() == 1 { | 104 | if self.rtc.events_compare[1].read().bits() == 1 { |
| 103 | self.rtc.events_compare[1].write(|w| w); | 105 | self.rtc.events_compare[1].write(|w| w); |
| 104 | trace!("rtc compare1"); | ||
| 105 | self.trigger_alarm(); | 106 | self.trigger_alarm(); |
| 106 | } | 107 | } |
| 107 | } | 108 | } |
| @@ -162,26 +163,28 @@ impl<T: Instance> RTC<T> { | |||
| 162 | } | 163 | } |
| 163 | }) | 164 | }) |
| 164 | } | 165 | } |
| 165 | } | ||
| 166 | 166 | ||
| 167 | impl<T: Instance> Monotonic for RTC<T> { | 167 | pub fn alarm0(&'static self) -> Alarm<T> { |
| 168 | fn now(&self) -> u64 { | 168 | Alarm { rtc: self } |
| 169 | let counter = self.rtc.counter.read().bits(); | ||
| 170 | let period = self.period.load(Ordering::Relaxed); | ||
| 171 | calc_now(period, counter) | ||
| 172 | } | 169 | } |
| 170 | } | ||
| 171 | |||
| 172 | pub struct Alarm<T: Instance> { | ||
| 173 | rtc: &'static RTC<T>, | ||
| 174 | } | ||
| 173 | 175 | ||
| 174 | fn set_alarm(&self, timestamp: u64, callback: fn()) { | 176 | impl<T: Instance> embassy::time::Alarm for Alarm<T> { |
| 175 | self.do_set_alarm(timestamp, Some(callback)); | 177 | fn set(&self, timestamp: u64, callback: fn()) { |
| 178 | self.rtc.do_set_alarm(timestamp, Some(callback)); | ||
| 176 | } | 179 | } |
| 177 | 180 | ||
| 178 | fn clear_alarm(&self) { | 181 | fn clear(&self) { |
| 179 | self.do_set_alarm(u64::MAX, None); | 182 | self.rtc.do_set_alarm(u64::MAX, None); |
| 180 | } | 183 | } |
| 181 | } | 184 | } |
| 182 | 185 | ||
| 183 | /// Implemented by all RTC instances. | 186 | /// Implemented by all RTC instances. |
| 184 | pub trait Instance: Deref<Target = rtc0::RegisterBlock> + Sized { | 187 | pub trait Instance: Deref<Target = rtc0::RegisterBlock> + Sized + 'static { |
| 185 | /// The interrupt associated with this RTC instance. | 188 | /// The interrupt associated with this RTC instance. |
| 186 | const INTERRUPT: Interrupt; | 189 | const INTERRUPT: Interrupt; |
| 187 | 190 | ||
diff --git a/embassy/Cargo.toml b/embassy/Cargo.toml index 5826e8ba6..60428f10d 100644 --- a/embassy/Cargo.toml +++ b/embassy/Cargo.toml | |||
| @@ -13,3 +13,4 @@ cortex-m = "0.6.3" | |||
| 13 | futures = { version = "0.3.5", default-features = false, features = [ "async-await" ] } | 13 | futures = { version = "0.3.5", default-features = false, features = [ "async-await" ] } |
| 14 | pin-project = { version = "0.4.23", default-features = false } | 14 | pin-project = { version = "0.4.23", default-features = false } |
| 15 | futures-intrusive = { version = "0.3.1", default-features = false } | 15 | futures-intrusive = { version = "0.3.1", default-features = false } |
| 16 | static-executor = { version = "0.1.0", features=["defmt"]} | ||
diff --git a/embassy/src/clock.rs b/embassy/src/clock.rs deleted file mode 100644 index ca5f26a05..000000000 --- a/embassy/src/clock.rs +++ /dev/null | |||
| @@ -1,21 +0,0 @@ | |||
| 1 | /// Monotonic clock with support for setting an alarm. | ||
| 2 | /// | ||
| 3 | /// The clock uses a "tick" time unit, whose length is an implementation-dependent constant. | ||
| 4 | pub trait Monotonic { | ||
| 5 | /// Returns the current timestamp in ticks. | ||
| 6 | /// This is guaranteed to be monotonic, i.e. a call to now() will always return | ||
| 7 | /// a greater or equal value than earler calls. | ||
| 8 | fn now(&self) -> u64; | ||
| 9 | |||
| 10 | /// Sets an alarm at the given timestamp. When the clock reaches that | ||
| 11 | /// timestamp, the provided callback funcion will be called. | ||
| 12 | /// | ||
| 13 | /// When callback is called, it is guaranteed that now() will return a value greater or equal than timestamp. | ||
| 14 | /// | ||
| 15 | /// Only one alarm can be active at a time. This overwrites any previously-set alarm if any. | ||
| 16 | fn set_alarm(&self, timestamp: u64, callback: fn()); | ||
| 17 | |||
| 18 | /// Clears the previously-set alarm. | ||
| 19 | /// If no alarm was set, this is a noop. | ||
| 20 | fn clear_alarm(&self); | ||
| 21 | } | ||
diff --git a/embassy/src/executor.rs b/embassy/src/executor.rs new file mode 100644 index 000000000..c5d024f7b --- /dev/null +++ b/embassy/src/executor.rs | |||
| @@ -0,0 +1,72 @@ | |||
| 1 | use core::marker::PhantomData; | ||
| 2 | use static_executor as se; | ||
| 3 | |||
| 4 | use crate::time; | ||
| 5 | use crate::time::Alarm; | ||
| 6 | |||
| 7 | pub use se::{task, SpawnError, SpawnToken}; | ||
| 8 | |||
| 9 | pub trait Model { | ||
| 10 | fn signal(); | ||
| 11 | } | ||
| 12 | |||
| 13 | pub struct WfeModel; | ||
| 14 | |||
| 15 | impl Model for WfeModel { | ||
| 16 | fn signal() { | ||
| 17 | cortex_m::asm::sev() | ||
| 18 | } | ||
| 19 | } | ||
| 20 | |||
| 21 | pub struct Executor<M, A: Alarm> { | ||
| 22 | inner: se::Executor, | ||
| 23 | alarm: A, | ||
| 24 | timer: time::TimerService, | ||
| 25 | _phantom: PhantomData<M>, | ||
| 26 | } | ||
| 27 | |||
| 28 | impl<M: Model, A: Alarm> Executor<M, A> { | ||
| 29 | pub fn new(alarm: A) -> Self { | ||
| 30 | Self { | ||
| 31 | inner: se::Executor::new(M::signal), | ||
| 32 | alarm, | ||
| 33 | timer: time::TimerService::new(time::IntrusiveClock), | ||
| 34 | _phantom: PhantomData, | ||
| 35 | } | ||
| 36 | } | ||
| 37 | |||
| 38 | /// Spawn a future on this executor. | ||
| 39 | /// | ||
| 40 | /// safety: can only be called from the executor thread | ||
| 41 | pub unsafe fn spawn(&'static self, token: SpawnToken) -> Result<(), SpawnError> { | ||
| 42 | self.inner.spawn(token) | ||
| 43 | } | ||
| 44 | |||
| 45 | /// Runs the executor until the queue is empty. | ||
| 46 | /// | ||
| 47 | /// safety: can only be called from the executor thread | ||
| 48 | pub unsafe fn run_once(&'static self) { | ||
| 49 | time::with_timer_service(&self.timer, || { | ||
| 50 | self.timer.check_expirations(); | ||
| 51 | self.inner.run(); | ||
| 52 | |||
| 53 | match self.timer.next_expiration() { | ||
| 54 | // If this is in the past, set_alarm will immediately trigger the alarm, | ||
| 55 | // which will make the wfe immediately return so we do another loop iteration. | ||
| 56 | Some(at) => self.alarm.set(at, M::signal), | ||
| 57 | None => self.alarm.clear(), | ||
| 58 | } | ||
| 59 | }) | ||
| 60 | } | ||
| 61 | } | ||
| 62 | |||
| 63 | impl<A: Alarm> Executor<WfeModel, A> { | ||
| 64 | /// Runs the executor forever | ||
| 65 | /// safety: can only be called from the executor thread | ||
| 66 | pub unsafe fn run(&'static self) -> ! { | ||
| 67 | loop { | ||
| 68 | self.run_once(); | ||
| 69 | cortex_m::asm::wfe() | ||
| 70 | } | ||
| 71 | } | ||
| 72 | } | ||
diff --git a/embassy/src/lib.rs b/embassy/src/lib.rs index 89eef3f4c..92ad77424 100644 --- a/embassy/src/lib.rs +++ b/embassy/src/lib.rs | |||
| @@ -3,7 +3,8 @@ | |||
| 3 | #![feature(generic_associated_types)] | 3 | #![feature(generic_associated_types)] |
| 4 | #![feature(const_fn)] | 4 | #![feature(const_fn)] |
| 5 | 5 | ||
| 6 | pub mod clock; | 6 | pub mod executor; |
| 7 | pub mod flash; | 7 | pub mod flash; |
| 8 | pub mod io; | 8 | pub mod io; |
| 9 | pub mod time; | ||
| 9 | pub mod util; | 10 | pub mod util; |
diff --git a/embassy/src/time.rs b/embassy/src/time.rs new file mode 100644 index 000000000..30fd1b680 --- /dev/null +++ b/embassy/src/time.rs | |||
| @@ -0,0 +1,287 @@ | |||
| 1 | use core::cell::Cell; | ||
| 2 | use core::convert::TryInto; | ||
| 3 | use core::future::Future; | ||
| 4 | use core::ops::{Add, AddAssign, Div, DivAssign, Mul, MulAssign, Sub, SubAssign}; | ||
| 5 | use core::pin::Pin; | ||
| 6 | use core::ptr; | ||
| 7 | use core::sync::atomic::{AtomicPtr, Ordering}; | ||
| 8 | use core::task::{Context, Poll}; | ||
| 9 | |||
| 10 | use fi::LocalTimer; | ||
| 11 | use futures_intrusive::timer as fi; | ||
| 12 | |||
| 13 | static mut CLOCK: fn() -> u64 = clock_not_set; | ||
| 14 | |||
| 15 | fn clock_not_set() -> u64 { | ||
| 16 | panic!("No clock set. You must call embassy::time::set_clock() before trying to use the clock") | ||
| 17 | } | ||
| 18 | |||
| 19 | pub unsafe fn set_clock(clock: fn() -> u64) { | ||
| 20 | CLOCK = clock; | ||
| 21 | } | ||
| 22 | |||
| 23 | fn now() -> u64 { | ||
| 24 | unsafe { CLOCK() } | ||
| 25 | } | ||
| 26 | |||
| 27 | #[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord)] | ||
| 28 | pub struct Instant { | ||
| 29 | ticks: u64, | ||
| 30 | } | ||
| 31 | |||
| 32 | impl Instant { | ||
| 33 | pub fn now() -> Instant { | ||
| 34 | Instant { ticks: now() } | ||
| 35 | } | ||
| 36 | |||
| 37 | pub fn into_ticks(&self) -> u64 { | ||
| 38 | self.ticks | ||
| 39 | } | ||
| 40 | |||
| 41 | pub fn duration_since(&self, earlier: Instant) -> Duration { | ||
| 42 | Duration { | ||
| 43 | ticks: (self.ticks - earlier.ticks).try_into().unwrap(), | ||
| 44 | } | ||
| 45 | } | ||
| 46 | |||
| 47 | pub fn checked_duration_since(&self, earlier: Instant) -> Option<Duration> { | ||
| 48 | if self.ticks < earlier.ticks { | ||
| 49 | None | ||
| 50 | } else { | ||
| 51 | Some(Duration { | ||
| 52 | ticks: (self.ticks - earlier.ticks).try_into().unwrap(), | ||
| 53 | }) | ||
| 54 | } | ||
| 55 | } | ||
| 56 | |||
| 57 | pub fn saturating_duration_since(&self, earlier: Instant) -> Duration { | ||
| 58 | Duration { | ||
| 59 | ticks: if self.ticks < earlier.ticks { | ||
| 60 | 0 | ||
| 61 | } else { | ||
| 62 | (self.ticks - earlier.ticks).try_into().unwrap() | ||
| 63 | }, | ||
| 64 | } | ||
| 65 | } | ||
| 66 | |||
| 67 | pub fn elapsed(&self) -> Duration { | ||
| 68 | Instant::now() - *self | ||
| 69 | } | ||
| 70 | |||
| 71 | pub fn checked_add(&self, duration: Duration) -> Option<Instant> { | ||
| 72 | self.ticks | ||
| 73 | .checked_add(duration.ticks.into()) | ||
| 74 | .map(|ticks| Instant { ticks }) | ||
| 75 | } | ||
| 76 | pub fn checked_sub(&self, duration: Duration) -> Option<Instant> { | ||
| 77 | self.ticks | ||
| 78 | .checked_sub(duration.ticks.into()) | ||
| 79 | .map(|ticks| Instant { ticks }) | ||
| 80 | } | ||
| 81 | } | ||
| 82 | |||
| 83 | impl Add<Duration> for Instant { | ||
| 84 | type Output = Instant; | ||
| 85 | |||
| 86 | fn add(self, other: Duration) -> Instant { | ||
| 87 | self.checked_add(other) | ||
| 88 | .expect("overflow when adding duration to instant") | ||
| 89 | } | ||
| 90 | } | ||
| 91 | |||
| 92 | impl AddAssign<Duration> for Instant { | ||
| 93 | fn add_assign(&mut self, other: Duration) { | ||
| 94 | *self = *self + other; | ||
| 95 | } | ||
| 96 | } | ||
| 97 | |||
| 98 | impl Sub<Duration> for Instant { | ||
| 99 | type Output = Instant; | ||
| 100 | |||
| 101 | fn sub(self, other: Duration) -> Instant { | ||
| 102 | self.checked_sub(other) | ||
| 103 | .expect("overflow when subtracting duration from instant") | ||
| 104 | } | ||
| 105 | } | ||
| 106 | |||
| 107 | impl SubAssign<Duration> for Instant { | ||
| 108 | fn sub_assign(&mut self, other: Duration) { | ||
| 109 | *self = *self - other; | ||
| 110 | } | ||
| 111 | } | ||
| 112 | |||
| 113 | impl Sub<Instant> for Instant { | ||
| 114 | type Output = Duration; | ||
| 115 | |||
| 116 | fn sub(self, other: Instant) -> Duration { | ||
| 117 | self.duration_since(other) | ||
| 118 | } | ||
| 119 | } | ||
| 120 | |||
| 121 | #[derive(Debug, Default, Copy, Clone, PartialEq, Eq, PartialOrd, Ord)] | ||
| 122 | pub struct Duration { | ||
| 123 | ticks: u32, | ||
| 124 | } | ||
| 125 | |||
| 126 | impl Duration { | ||
| 127 | pub const fn from_ticks(ticks: u32) -> Duration { | ||
| 128 | Duration { ticks } | ||
| 129 | } | ||
| 130 | |||
| 131 | pub fn checked_add(self, rhs: Duration) -> Option<Duration> { | ||
| 132 | self.ticks | ||
| 133 | .checked_add(rhs.ticks) | ||
| 134 | .map(|ticks| Duration { ticks }) | ||
| 135 | } | ||
| 136 | |||
| 137 | pub fn checked_sub(self, rhs: Duration) -> Option<Duration> { | ||
| 138 | self.ticks | ||
| 139 | .checked_sub(rhs.ticks) | ||
| 140 | .map(|ticks| Duration { ticks }) | ||
| 141 | } | ||
| 142 | |||
| 143 | pub fn checked_mul(self, rhs: u32) -> Option<Duration> { | ||
| 144 | self.ticks.checked_mul(rhs).map(|ticks| Duration { ticks }) | ||
| 145 | } | ||
| 146 | |||
| 147 | pub fn checked_div(self, rhs: u32) -> Option<Duration> { | ||
| 148 | self.ticks.checked_div(rhs).map(|ticks| Duration { ticks }) | ||
| 149 | } | ||
| 150 | } | ||
| 151 | |||
| 152 | impl Add for Duration { | ||
| 153 | type Output = Duration; | ||
| 154 | |||
| 155 | fn add(self, rhs: Duration) -> Duration { | ||
| 156 | self.checked_add(rhs) | ||
| 157 | .expect("overflow when adding durations") | ||
| 158 | } | ||
| 159 | } | ||
| 160 | |||
| 161 | impl AddAssign for Duration { | ||
| 162 | fn add_assign(&mut self, rhs: Duration) { | ||
| 163 | *self = *self + rhs; | ||
| 164 | } | ||
| 165 | } | ||
| 166 | |||
| 167 | impl Sub for Duration { | ||
| 168 | type Output = Duration; | ||
| 169 | |||
| 170 | fn sub(self, rhs: Duration) -> Duration { | ||
| 171 | self.checked_sub(rhs) | ||
| 172 | .expect("overflow when subtracting durations") | ||
| 173 | } | ||
| 174 | } | ||
| 175 | |||
| 176 | impl SubAssign for Duration { | ||
| 177 | fn sub_assign(&mut self, rhs: Duration) { | ||
| 178 | *self = *self - rhs; | ||
| 179 | } | ||
| 180 | } | ||
| 181 | |||
| 182 | impl Mul<u32> for Duration { | ||
| 183 | type Output = Duration; | ||
| 184 | |||
| 185 | fn mul(self, rhs: u32) -> Duration { | ||
| 186 | self.checked_mul(rhs) | ||
| 187 | .expect("overflow when multiplying duration by scalar") | ||
| 188 | } | ||
| 189 | } | ||
| 190 | |||
| 191 | impl Mul<Duration> for u32 { | ||
| 192 | type Output = Duration; | ||
| 193 | |||
| 194 | fn mul(self, rhs: Duration) -> Duration { | ||
| 195 | rhs * self | ||
| 196 | } | ||
| 197 | } | ||
| 198 | |||
| 199 | impl MulAssign<u32> for Duration { | ||
| 200 | fn mul_assign(&mut self, rhs: u32) { | ||
| 201 | *self = *self * rhs; | ||
| 202 | } | ||
| 203 | } | ||
| 204 | |||
| 205 | impl Div<u32> for Duration { | ||
| 206 | type Output = Duration; | ||
| 207 | |||
| 208 | fn div(self, rhs: u32) -> Duration { | ||
| 209 | self.checked_div(rhs) | ||
| 210 | .expect("divide by zero error when dividing duration by scalar") | ||
| 211 | } | ||
| 212 | } | ||
| 213 | |||
| 214 | impl DivAssign<u32> for Duration { | ||
| 215 | fn div_assign(&mut self, rhs: u32) { | ||
| 216 | *self = *self / rhs; | ||
| 217 | } | ||
| 218 | } | ||
| 219 | |||
| 220 | pub(crate) struct IntrusiveClock; | ||
| 221 | |||
| 222 | impl fi::Clock for IntrusiveClock { | ||
| 223 | fn now(&self) -> u64 { | ||
| 224 | now() | ||
| 225 | } | ||
| 226 | } | ||
| 227 | |||
| 228 | pub(crate) type TimerService = fi::LocalTimerService<IntrusiveClock>; | ||
| 229 | |||
| 230 | static CURRENT_TIMER_SERVICE: AtomicPtr<TimerService> = AtomicPtr::new(ptr::null_mut()); | ||
| 231 | |||
| 232 | pub(crate) fn with_timer_service<R>(svc: &'static TimerService, f: impl FnOnce() -> R) -> R { | ||
| 233 | let svc = svc as *const _ as *mut _; | ||
| 234 | let prev_svc = CURRENT_TIMER_SERVICE.swap(svc, Ordering::Relaxed); | ||
| 235 | let r = f(); | ||
| 236 | let svc2 = CURRENT_TIMER_SERVICE.swap(prev_svc, Ordering::Relaxed); | ||
| 237 | assert_eq!(svc, svc2); | ||
| 238 | r | ||
| 239 | } | ||
| 240 | |||
| 241 | fn current_timer_service() -> &'static TimerService { | ||
| 242 | unsafe { | ||
| 243 | CURRENT_TIMER_SERVICE | ||
| 244 | .load(Ordering::Relaxed) | ||
| 245 | .as_ref() | ||
| 246 | .unwrap() | ||
| 247 | } | ||
| 248 | } | ||
| 249 | |||
| 250 | pub struct Timer { | ||
| 251 | inner: fi::LocalTimerFuture<'static>, | ||
| 252 | } | ||
| 253 | |||
| 254 | impl Timer { | ||
| 255 | pub fn at(when: Instant) -> Self { | ||
| 256 | let svc: &TimerService = current_timer_service(); | ||
| 257 | Self { | ||
| 258 | inner: svc.deadline(when.into_ticks()), | ||
| 259 | } | ||
| 260 | } | ||
| 261 | |||
| 262 | pub fn after(dur: Duration) -> Self { | ||
| 263 | Self::at(Instant::now() + dur) | ||
| 264 | } | ||
| 265 | } | ||
| 266 | |||
| 267 | impl Future for Timer { | ||
| 268 | type Output = (); | ||
| 269 | fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> { | ||
| 270 | unsafe { Pin::new_unchecked(&mut self.get_unchecked_mut().inner) }.poll(cx) | ||
| 271 | } | ||
| 272 | } | ||
| 273 | |||
| 274 | /// Trait to register a callback at a given timestamp. | ||
| 275 | pub trait Alarm { | ||
| 276 | /// Sets an alarm at the given timestamp. When the clock reaches that | ||
| 277 | /// timestamp, the provided callback funcion will be called. | ||
| 278 | /// | ||
| 279 | /// When callback is called, it is guaranteed that now() will return a value greater or equal than timestamp. | ||
| 280 | /// | ||
| 281 | /// Only one alarm can be active at a time. This overwrites any previously-set alarm if any. | ||
| 282 | fn set(&self, timestamp: u64, callback: fn()); | ||
| 283 | |||
| 284 | /// Clears the previously-set alarm. | ||
| 285 | /// If no alarm was set, this is a noop. | ||
| 286 | fn clear(&self); | ||
| 287 | } | ||
diff --git a/examples/Cargo.toml b/examples/Cargo.toml index 4e81d1daa..7b49ffb1f 100644 --- a/examples/Cargo.toml +++ b/examples/Cargo.toml | |||
| @@ -27,5 +27,4 @@ nrf52840-hal = { version = "0.11.0" } | |||
| 27 | embassy = { version = "0.1.0", path = "../embassy" } | 27 | embassy = { version = "0.1.0", path = "../embassy" } |
| 28 | embassy-nrf = { version = "0.1.0", path = "../embassy-nrf", features = ["defmt-trace", "52840"] } | 28 | embassy-nrf = { version = "0.1.0", path = "../embassy-nrf", features = ["defmt-trace", "52840"] } |
| 29 | static-executor = { version = "0.1.0", features=["defmt"]} | 29 | static-executor = { version = "0.1.0", features=["defmt"]} |
| 30 | futures = { version = "0.3.5", default-features = false } | 30 | futures = { version = "0.3.5", default-features = false } \ No newline at end of file |
| 31 | futures-intrusive = { version = "0.3.1", default-features = false } \ No newline at end of file | ||
diff --git a/examples/src/bin/rtc_async.rs b/examples/src/bin/rtc_async.rs index 977621d4c..3bffabf0f 100644 --- a/examples/src/bin/rtc_async.rs +++ b/examples/src/bin/rtc_async.rs | |||
| @@ -8,39 +8,30 @@ use example_common::*; | |||
| 8 | 8 | ||
| 9 | use core::mem::MaybeUninit; | 9 | use core::mem::MaybeUninit; |
| 10 | use cortex_m_rt::entry; | 10 | use cortex_m_rt::entry; |
| 11 | use embassy::clock::Monotonic; | 11 | use embassy::executor::{task, Executor, WfeModel}; |
| 12 | use embassy::time::{Duration, Instant, Timer}; | ||
| 13 | use embassy_nrf::pac; | ||
| 12 | use embassy_nrf::rtc; | 14 | use embassy_nrf::rtc; |
| 13 | use futures_intrusive::timer::{Clock, LocalTimer, LocalTimerService}; | ||
| 14 | use nrf52840_hal::clocks; | 15 | use nrf52840_hal::clocks; |
| 15 | use static_executor::{task, Executor}; | ||
| 16 | |||
| 17 | struct RtcClock<T>(rtc::RTC<T>); | ||
| 18 | |||
| 19 | impl<T: rtc::Instance> Clock for RtcClock<T> { | ||
| 20 | fn now(&self) -> u64 { | ||
| 21 | self.0.now() | ||
| 22 | } | ||
| 23 | } | ||
| 24 | 16 | ||
| 25 | #[task] | 17 | #[task] |
| 26 | async fn run1(rtc: &'static rtc::RTC<embassy_nrf::pac::RTC1>, timer: &'static LocalTimerService) { | 18 | async fn run1() { |
| 27 | loop { | 19 | loop { |
| 28 | info!("tick 1"); | 20 | info!("BIG INFREQUENT TICK"); |
| 29 | timer.deadline(rtc.now() + 64000).await; | 21 | Timer::after(Duration::from_ticks(64000)).await; |
| 30 | } | 22 | } |
| 31 | } | 23 | } |
| 32 | 24 | ||
| 33 | #[task] | 25 | #[task] |
| 34 | async fn run2(rtc: &'static rtc::RTC<embassy_nrf::pac::RTC1>, timer: &'static LocalTimerService) { | 26 | async fn run2() { |
| 35 | loop { | 27 | loop { |
| 36 | info!("tick 2"); | 28 | info!("tick"); |
| 37 | timer.deadline(rtc.now() + 23000).await; | 29 | Timer::after(Duration::from_ticks(13000)).await; |
| 38 | } | 30 | } |
| 39 | } | 31 | } |
| 40 | 32 | ||
| 41 | static EXECUTOR: Executor = Executor::new(cortex_m::asm::sev); | 33 | static mut RTC: MaybeUninit<rtc::RTC<pac::RTC1>> = MaybeUninit::uninit(); |
| 42 | static mut RTC: MaybeUninit<RtcClock<embassy_nrf::pac::RTC1>> = MaybeUninit::uninit(); | 34 | static mut EXECUTOR: MaybeUninit<Executor<WfeModel, rtc::Alarm<pac::RTC1>>> = MaybeUninit::uninit(); |
| 43 | static mut TIMER: MaybeUninit<LocalTimerService> = MaybeUninit::uninit(); | ||
| 44 | 35 | ||
| 45 | #[entry] | 36 | #[entry] |
| 46 | fn main() -> ! { | 37 | fn main() -> ! { |
| @@ -55,35 +46,23 @@ fn main() -> ! { | |||
| 55 | 46 | ||
| 56 | let rtc: &'static _ = unsafe { | 47 | let rtc: &'static _ = unsafe { |
| 57 | let ptr = RTC.as_mut_ptr(); | 48 | let ptr = RTC.as_mut_ptr(); |
| 58 | ptr.write(RtcClock(rtc::RTC::new(p.RTC1))); | 49 | ptr.write(rtc::RTC::new(p.RTC1)); |
| 59 | &*ptr | 50 | &*ptr |
| 60 | }; | 51 | }; |
| 61 | 52 | ||
| 62 | rtc.0.start(); | 53 | rtc.start(); |
| 54 | unsafe { embassy::time::set_clock(|| RTC.as_ptr().as_ref().unwrap().now()) }; | ||
| 63 | 55 | ||
| 64 | let timer: &'static _ = unsafe { | 56 | let executor: &'static _ = unsafe { |
| 65 | let ptr = TIMER.as_mut_ptr(); | 57 | let ptr = EXECUTOR.as_mut_ptr(); |
| 66 | ptr.write(LocalTimerService::new(rtc)); | 58 | ptr.write(Executor::new(rtc.alarm0())); |
| 67 | &*ptr | 59 | &*ptr |
| 68 | }; | 60 | }; |
| 69 | 61 | ||
| 70 | unsafe { | 62 | unsafe { |
| 71 | EXECUTOR.spawn(run1(&rtc.0, timer)).dewrap(); | 63 | executor.spawn(run1()).dewrap(); |
| 72 | EXECUTOR.spawn(run2(&rtc.0, timer)).dewrap(); | 64 | executor.spawn(run2()).dewrap(); |
| 73 | |||
| 74 | loop { | ||
| 75 | timer.check_expirations(); | ||
| 76 | |||
| 77 | EXECUTOR.run(); | ||
| 78 | |||
| 79 | match timer.next_expiration() { | ||
| 80 | // If this is in the past, set_alarm will immediately trigger the alarm, | ||
| 81 | // which will make the wfe immediately return so we do another loop iteration. | ||
| 82 | Some(at) => rtc.0.set_alarm(at, cortex_m::asm::sev), | ||
| 83 | None => rtc.0.clear_alarm(), | ||
| 84 | } | ||
| 85 | 65 | ||
| 86 | cortex_m::asm::wfe(); | 66 | executor.run() |
| 87 | } | ||
| 88 | } | 67 | } |
| 89 | } | 68 | } |
diff --git a/examples/src/bin/rtc_raw.rs b/examples/src/bin/rtc_raw.rs index aa98409c4..2e4b28653 100644 --- a/examples/src/bin/rtc_raw.rs +++ b/examples/src/bin/rtc_raw.rs | |||
| @@ -8,7 +8,7 @@ use example_common::*; | |||
| 8 | 8 | ||
| 9 | use core::mem::MaybeUninit; | 9 | use core::mem::MaybeUninit; |
| 10 | use cortex_m_rt::entry; | 10 | use cortex_m_rt::entry; |
| 11 | use embassy::clock::Monotonic; | 11 | use embassy::time::Alarm; |
| 12 | use embassy_nrf::rtc; | 12 | use embassy_nrf::rtc; |
| 13 | use nrf52840_hal::clocks; | 13 | use nrf52840_hal::clocks; |
| 14 | 14 | ||
| @@ -31,8 +31,11 @@ fn main() -> ! { | |||
| 31 | &*ptr | 31 | &*ptr |
| 32 | }; | 32 | }; |
| 33 | 33 | ||
| 34 | let alarm = rtc.alarm0(); | ||
| 35 | |||
| 34 | rtc.start(); | 36 | rtc.start(); |
| 35 | rtc.set_alarm(53719, || info!("ALARM TRIGGERED")); | 37 | |
| 38 | alarm.set(53719, || info!("ALARM TRIGGERED")); | ||
| 36 | 39 | ||
| 37 | info!("initialized!"); | 40 | info!("initialized!"); |
| 38 | 41 | ||
