diff options
| author | Dario Nieuwenhuis <[email protected]> | 2021-08-26 23:37:37 +0200 |
|---|---|---|
| committer | GitHub <[email protected]> | 2021-08-26 23:37:37 +0200 |
| commit | e56c6166dcac9132cde1769e5ef8d60e03963329 (patch) | |
| tree | 81c076e27a66a3d825bfc19b08a45ab0035bf271 | |
| parent | 37e0ad299c027785f90bba4ffdb86d6c91a83633 (diff) | |
| parent | c4b9c8ac8762fdca52b6c37dd99346b2371387ab (diff) | |
Merge pull request #373 from embassy-rs/docs
Time driver improvements, docs.
| -rw-r--r-- | Cargo.example.toml | 1 | ||||
| -rw-r--r-- | embassy-nrf/src/time_driver.rs | 55 | ||||
| -rw-r--r-- | embassy-rp/src/timer.rs | 124 | ||||
| -rw-r--r-- | embassy-stm32/src/time_driver.rs | 62 | ||||
| -rw-r--r-- | embassy/Cargo.toml | 10 | ||||
| -rw-r--r-- | embassy/src/executor/arch/std.rs | 67 | ||||
| -rw-r--r-- | embassy/src/executor/mod.rs | 3 | ||||
| -rw-r--r-- | embassy/src/io/mod.rs | 4 | ||||
| -rw-r--r-- | embassy/src/io/std.rs | 35 | ||||
| -rw-r--r-- | embassy/src/io/traits.rs | 37 | ||||
| -rw-r--r-- | embassy/src/time/delay.rs | 9 | ||||
| -rw-r--r-- | embassy/src/time/driver.rs | 110 | ||||
| -rw-r--r-- | embassy/src/time/driver_std.rs | 208 | ||||
| -rw-r--r-- | embassy/src/time/duration.rs | 9 | ||||
| -rw-r--r-- | embassy/src/time/instant.rs | 26 | ||||
| -rw-r--r-- | embassy/src/time/mod.rs | 59 | ||||
| -rw-r--r-- | embassy/src/time/timer.rs | 6 | ||||
| -rw-r--r-- | examples/std/Cargo.toml | 3 | ||||
| -rw-r--r-- | examples/std/src/bin/net.rs | 3 | ||||
| -rw-r--r-- | examples/std/src/bin/serial.rs | 2 | ||||
| -rw-r--r-- | examples/std/src/bin/tick.rs | 2 |
21 files changed, 597 insertions, 238 deletions
diff --git a/Cargo.example.toml b/Cargo.example.toml index f072c2f96..0e9d1e32d 100644 --- a/Cargo.example.toml +++ b/Cargo.example.toml | |||
| @@ -45,7 +45,6 @@ members = [ | |||
| 45 | #"examples/rp", | 45 | #"examples/rp", |
| 46 | 46 | ||
| 47 | # std | 47 | # std |
| 48 | #"embassy-std", | ||
| 49 | #"examples/std", | 48 | #"examples/std", |
| 50 | ] | 49 | ] |
| 51 | 50 | ||
diff --git a/embassy-nrf/src/time_driver.rs b/embassy-nrf/src/time_driver.rs index 30461633a..f93ebb54a 100644 --- a/embassy-nrf/src/time_driver.rs +++ b/embassy-nrf/src/time_driver.rs | |||
| @@ -82,7 +82,7 @@ impl AlarmState { | |||
| 82 | 82 | ||
| 83 | const ALARM_COUNT: usize = 3; | 83 | const ALARM_COUNT: usize = 3; |
| 84 | 84 | ||
| 85 | struct State { | 85 | struct RtcDriver { |
| 86 | /// Number of 2^23 periods elapsed since boot. | 86 | /// Number of 2^23 periods elapsed since boot. |
| 87 | period: AtomicU32, | 87 | period: AtomicU32, |
| 88 | alarm_count: AtomicU8, | 88 | alarm_count: AtomicU8, |
| @@ -91,13 +91,13 @@ struct State { | |||
| 91 | } | 91 | } |
| 92 | 92 | ||
| 93 | const ALARM_STATE_NEW: AlarmState = AlarmState::new(); | 93 | const ALARM_STATE_NEW: AlarmState = AlarmState::new(); |
| 94 | static STATE: State = State { | 94 | embassy::time_driver_impl!(static DRIVER: RtcDriver = RtcDriver { |
| 95 | period: AtomicU32::new(0), | 95 | period: AtomicU32::new(0), |
| 96 | alarm_count: AtomicU8::new(0), | 96 | alarm_count: AtomicU8::new(0), |
| 97 | alarms: Mutex::new([ALARM_STATE_NEW; ALARM_COUNT]), | 97 | alarms: Mutex::new([ALARM_STATE_NEW; ALARM_COUNT]), |
| 98 | }; | 98 | }); |
| 99 | 99 | ||
| 100 | impl State { | 100 | impl RtcDriver { |
| 101 | fn init(&'static self, irq_prio: crate::interrupt::Priority) { | 101 | fn init(&'static self, irq_prio: crate::interrupt::Priority) { |
| 102 | let r = rtc(); | 102 | let r = rtc(); |
| 103 | r.cc[3].write(|w| unsafe { w.bits(0x800000) }); | 103 | r.cc[3].write(|w| unsafe { w.bits(0x800000) }); |
| @@ -159,14 +159,6 @@ impl State { | |||
| 159 | }) | 159 | }) |
| 160 | } | 160 | } |
| 161 | 161 | ||
| 162 | fn now(&self) -> u64 { | ||
| 163 | // `period` MUST be read before `counter`, see comment at the top for details. | ||
| 164 | let period = self.period.load(Ordering::Relaxed); | ||
| 165 | compiler_fence(Ordering::Acquire); | ||
| 166 | let counter = rtc().counter.read().bits(); | ||
| 167 | calc_now(period, counter) | ||
| 168 | } | ||
| 169 | |||
| 170 | fn get_alarm<'a>(&'a self, cs: CriticalSection<'a>, alarm: AlarmHandle) -> &'a AlarmState { | 162 | fn get_alarm<'a>(&'a self, cs: CriticalSection<'a>, alarm: AlarmHandle) -> &'a AlarmState { |
| 171 | // safety: we're allowed to assume the AlarmState is created by us, and | 163 | // safety: we're allowed to assume the AlarmState is created by us, and |
| 172 | // we never create one that's out of bounds. | 164 | // we never create one that's out of bounds. |
| @@ -188,8 +180,18 @@ impl State { | |||
| 188 | let f: fn(*mut ()) = unsafe { mem::transmute(alarm.callback.get()) }; | 180 | let f: fn(*mut ()) = unsafe { mem::transmute(alarm.callback.get()) }; |
| 189 | f(alarm.ctx.get()); | 181 | f(alarm.ctx.get()); |
| 190 | } | 182 | } |
| 183 | } | ||
| 191 | 184 | ||
| 192 | fn allocate_alarm(&self) -> Option<AlarmHandle> { | 185 | impl Driver for RtcDriver { |
| 186 | fn now(&self) -> u64 { | ||
| 187 | // `period` MUST be read before `counter`, see comment at the top for details. | ||
| 188 | let period = self.period.load(Ordering::Relaxed); | ||
| 189 | compiler_fence(Ordering::Acquire); | ||
| 190 | let counter = rtc().counter.read().bits(); | ||
| 191 | calc_now(period, counter) | ||
| 192 | } | ||
| 193 | |||
| 194 | unsafe fn allocate_alarm(&self) -> Option<AlarmHandle> { | ||
| 193 | let id = self | 195 | let id = self |
| 194 | .alarm_count | 196 | .alarm_count |
| 195 | .fetch_update(Ordering::AcqRel, Ordering::Acquire, |x| { | 197 | .fetch_update(Ordering::AcqRel, Ordering::Acquire, |x| { |
| @@ -201,7 +203,7 @@ impl State { | |||
| 201 | }); | 203 | }); |
| 202 | 204 | ||
| 203 | match id { | 205 | match id { |
| 204 | Ok(id) => Some(unsafe { AlarmHandle::new(id) }), | 206 | Ok(id) => Some(AlarmHandle::new(id)), |
| 205 | Err(_) => None, | 207 | Err(_) => None, |
| 206 | } | 208 | } |
| 207 | } | 209 | } |
| @@ -263,32 +265,11 @@ impl State { | |||
| 263 | } | 265 | } |
| 264 | } | 266 | } |
| 265 | 267 | ||
| 266 | struct RtcDriver; | ||
| 267 | embassy::time_driver_impl!(RtcDriver); | ||
| 268 | |||
| 269 | impl Driver for RtcDriver { | ||
| 270 | fn now() -> u64 { | ||
| 271 | STATE.now() | ||
| 272 | } | ||
| 273 | |||
| 274 | unsafe fn allocate_alarm() -> Option<AlarmHandle> { | ||
| 275 | STATE.allocate_alarm() | ||
| 276 | } | ||
| 277 | |||
| 278 | fn set_alarm_callback(alarm: AlarmHandle, callback: fn(*mut ()), ctx: *mut ()) { | ||
| 279 | STATE.set_alarm_callback(alarm, callback, ctx) | ||
| 280 | } | ||
| 281 | |||
| 282 | fn set_alarm(alarm: AlarmHandle, timestamp: u64) { | ||
| 283 | STATE.set_alarm(alarm, timestamp) | ||
| 284 | } | ||
| 285 | } | ||
| 286 | |||
| 287 | #[interrupt] | 268 | #[interrupt] |
| 288 | fn RTC1() { | 269 | fn RTC1() { |
| 289 | STATE.on_interrupt() | 270 | DRIVER.on_interrupt() |
| 290 | } | 271 | } |
| 291 | 272 | ||
| 292 | pub(crate) fn init(irq_prio: crate::interrupt::Priority) { | 273 | pub(crate) fn init(irq_prio: crate::interrupt::Priority) { |
| 293 | STATE.init(irq_prio) | 274 | DRIVER.init(irq_prio) |
| 294 | } | 275 | } |
diff --git a/embassy-rp/src/timer.rs b/embassy-rp/src/timer.rs index 71c59ec8d..ed265c47f 100644 --- a/embassy-rp/src/timer.rs +++ b/embassy-rp/src/timer.rs | |||
| @@ -19,38 +19,40 @@ const DUMMY_ALARM: AlarmState = AlarmState { | |||
| 19 | callback: Cell::new(None), | 19 | callback: Cell::new(None), |
| 20 | }; | 20 | }; |
| 21 | 21 | ||
| 22 | static ALARMS: Mutex<[AlarmState; ALARM_COUNT]> = Mutex::new([DUMMY_ALARM; ALARM_COUNT]); | 22 | struct TimerDriver { |
| 23 | static NEXT_ALARM: AtomicU8 = AtomicU8::new(0); | 23 | alarms: Mutex<[AlarmState; ALARM_COUNT]>, |
| 24 | 24 | next_alarm: AtomicU8, | |
| 25 | fn now() -> u64 { | ||
| 26 | loop { | ||
| 27 | unsafe { | ||
| 28 | let hi = pac::TIMER.timerawh().read(); | ||
| 29 | let lo = pac::TIMER.timerawl().read(); | ||
| 30 | let hi2 = pac::TIMER.timerawh().read(); | ||
| 31 | if hi == hi2 { | ||
| 32 | return (hi as u64) << 32 | (lo as u64); | ||
| 33 | } | ||
| 34 | } | ||
| 35 | } | ||
| 36 | } | 25 | } |
| 37 | 26 | ||
| 38 | struct TimerDriver; | 27 | embassy::time_driver_impl!(static DRIVER: TimerDriver = TimerDriver{ |
| 39 | embassy::time_driver_impl!(TimerDriver); | 28 | alarms: Mutex::new([DUMMY_ALARM; ALARM_COUNT]), |
| 29 | next_alarm: AtomicU8::new(0), | ||
| 30 | }); | ||
| 40 | 31 | ||
| 41 | impl Driver for TimerDriver { | 32 | impl Driver for TimerDriver { |
| 42 | fn now() -> u64 { | 33 | fn now(&self) -> u64 { |
| 43 | now() | 34 | loop { |
| 35 | unsafe { | ||
| 36 | let hi = pac::TIMER.timerawh().read(); | ||
| 37 | let lo = pac::TIMER.timerawl().read(); | ||
| 38 | let hi2 = pac::TIMER.timerawh().read(); | ||
| 39 | if hi == hi2 { | ||
| 40 | return (hi as u64) << 32 | (lo as u64); | ||
| 41 | } | ||
| 42 | } | ||
| 43 | } | ||
| 44 | } | 44 | } |
| 45 | 45 | ||
| 46 | unsafe fn allocate_alarm() -> Option<AlarmHandle> { | 46 | unsafe fn allocate_alarm(&self) -> Option<AlarmHandle> { |
| 47 | let id = NEXT_ALARM.fetch_update(Ordering::AcqRel, Ordering::Acquire, |x| { | 47 | let id = self |
| 48 | if x < ALARM_COUNT as u8 { | 48 | .next_alarm |
| 49 | Some(x + 1) | 49 | .fetch_update(Ordering::AcqRel, Ordering::Acquire, |x| { |
| 50 | } else { | 50 | if x < ALARM_COUNT as u8 { |
| 51 | None | 51 | Some(x + 1) |
| 52 | } | 52 | } else { |
| 53 | }); | 53 | None |
| 54 | } | ||
| 55 | }); | ||
| 54 | 56 | ||
| 55 | match id { | 57 | match id { |
| 56 | Ok(id) => Some(AlarmHandle::new(id)), | 58 | Ok(id) => Some(AlarmHandle::new(id)), |
| @@ -58,18 +60,18 @@ impl Driver for TimerDriver { | |||
| 58 | } | 60 | } |
| 59 | } | 61 | } |
| 60 | 62 | ||
| 61 | fn set_alarm_callback(alarm: AlarmHandle, callback: fn(*mut ()), ctx: *mut ()) { | 63 | fn set_alarm_callback(&self, alarm: AlarmHandle, callback: fn(*mut ()), ctx: *mut ()) { |
| 62 | let n = alarm.id() as usize; | 64 | let n = alarm.id() as usize; |
| 63 | critical_section::with(|cs| { | 65 | critical_section::with(|cs| { |
| 64 | let alarm = &ALARMS.borrow(cs)[n]; | 66 | let alarm = &self.alarms.borrow(cs)[n]; |
| 65 | alarm.callback.set(Some((callback, ctx))); | 67 | alarm.callback.set(Some((callback, ctx))); |
| 66 | }) | 68 | }) |
| 67 | } | 69 | } |
| 68 | 70 | ||
| 69 | fn set_alarm(alarm: AlarmHandle, timestamp: u64) { | 71 | fn set_alarm(&self, alarm: AlarmHandle, timestamp: u64) { |
| 70 | let n = alarm.id() as usize; | 72 | let n = alarm.id() as usize; |
| 71 | critical_section::with(|cs| { | 73 | critical_section::with(|cs| { |
| 72 | let alarm = &ALARMS.borrow(cs)[n]; | 74 | let alarm = &self.alarms.borrow(cs)[n]; |
| 73 | alarm.timestamp.set(timestamp); | 75 | alarm.timestamp.set(timestamp); |
| 74 | 76 | ||
| 75 | // Arm it. | 77 | // Arm it. |
| @@ -78,44 +80,46 @@ impl Driver for TimerDriver { | |||
| 78 | // it is checked if the alarm time has passed. | 80 | // it is checked if the alarm time has passed. |
| 79 | unsafe { pac::TIMER.alarm(n).write_value(timestamp as u32) }; | 81 | unsafe { pac::TIMER.alarm(n).write_value(timestamp as u32) }; |
| 80 | 82 | ||
| 81 | let now = now(); | 83 | let now = self.now(); |
| 82 | 84 | ||
| 83 | // If alarm timestamp has passed, trigger it instantly. | 85 | // If alarm timestamp has passed, trigger it instantly. |
| 84 | // This disarms it. | 86 | // This disarms it. |
| 85 | if timestamp <= now { | 87 | if timestamp <= now { |
| 86 | trigger_alarm(n, cs); | 88 | self.trigger_alarm(n, cs); |
| 87 | } | 89 | } |
| 88 | }) | 90 | }) |
| 89 | } | 91 | } |
| 90 | } | 92 | } |
| 91 | 93 | ||
| 92 | fn check_alarm(n: usize) { | 94 | impl TimerDriver { |
| 93 | critical_section::with(|cs| { | 95 | fn check_alarm(&self, n: usize) { |
| 94 | let alarm = &ALARMS.borrow(cs)[n]; | 96 | critical_section::with(|cs| { |
| 95 | let timestamp = alarm.timestamp.get(); | 97 | let alarm = &self.alarms.borrow(cs)[n]; |
| 96 | if timestamp <= now() { | 98 | let timestamp = alarm.timestamp.get(); |
| 97 | trigger_alarm(n, cs) | 99 | if timestamp <= self.now() { |
| 98 | } else { | 100 | self.trigger_alarm(n, cs) |
| 99 | // Not elapsed, arm it again. | 101 | } else { |
| 100 | // This can happen if it was set more than 2^32 us in the future. | 102 | // Not elapsed, arm it again. |
| 101 | unsafe { pac::TIMER.alarm(n).write_value(timestamp as u32) }; | 103 | // This can happen if it was set more than 2^32 us in the future. |
| 102 | } | 104 | unsafe { pac::TIMER.alarm(n).write_value(timestamp as u32) }; |
| 103 | }); | 105 | } |
| 106 | }); | ||
| 104 | 107 | ||
| 105 | // clear the irq | 108 | // clear the irq |
| 106 | unsafe { pac::TIMER.intr().write(|w| w.set_alarm(n, true)) } | 109 | unsafe { pac::TIMER.intr().write(|w| w.set_alarm(n, true)) } |
| 107 | } | 110 | } |
| 108 | 111 | ||
| 109 | fn trigger_alarm(n: usize, cs: CriticalSection) { | 112 | fn trigger_alarm(&self, n: usize, cs: CriticalSection) { |
| 110 | // disarm | 113 | // disarm |
| 111 | unsafe { pac::TIMER.armed().write(|w| w.set_armed(1 << n)) } | 114 | unsafe { pac::TIMER.armed().write(|w| w.set_armed(1 << n)) } |
| 112 | 115 | ||
| 113 | let alarm = &ALARMS.borrow(cs)[n]; | 116 | let alarm = &self.alarms.borrow(cs)[n]; |
| 114 | alarm.timestamp.set(u64::MAX); | 117 | alarm.timestamp.set(u64::MAX); |
| 115 | 118 | ||
| 116 | // Call after clearing alarm, so the callback can set another alarm. | 119 | // Call after clearing alarm, so the callback can set another alarm. |
| 117 | if let Some((f, ctx)) = alarm.callback.get() { | 120 | if let Some((f, ctx)) = alarm.callback.get() { |
| 118 | f(ctx); | 121 | f(ctx); |
| 122 | } | ||
| 119 | } | 123 | } |
| 120 | } | 124 | } |
| 121 | 125 | ||
| @@ -123,7 +127,7 @@ fn trigger_alarm(n: usize, cs: CriticalSection) { | |||
| 123 | pub unsafe fn init() { | 127 | pub unsafe fn init() { |
| 124 | // init alarms | 128 | // init alarms |
| 125 | critical_section::with(|cs| { | 129 | critical_section::with(|cs| { |
| 126 | let alarms = ALARMS.borrow(cs); | 130 | let alarms = DRIVER.alarms.borrow(cs); |
| 127 | for a in alarms { | 131 | for a in alarms { |
| 128 | a.timestamp.set(u64::MAX); | 132 | a.timestamp.set(u64::MAX); |
| 129 | } | 133 | } |
| @@ -144,20 +148,20 @@ pub unsafe fn init() { | |||
| 144 | 148 | ||
| 145 | #[interrupt] | 149 | #[interrupt] |
| 146 | unsafe fn TIMER_IRQ_0() { | 150 | unsafe fn TIMER_IRQ_0() { |
| 147 | check_alarm(0) | 151 | DRIVER.check_alarm(0) |
| 148 | } | 152 | } |
| 149 | 153 | ||
| 150 | #[interrupt] | 154 | #[interrupt] |
| 151 | unsafe fn TIMER_IRQ_1() { | 155 | unsafe fn TIMER_IRQ_1() { |
| 152 | check_alarm(1) | 156 | DRIVER.check_alarm(1) |
| 153 | } | 157 | } |
| 154 | 158 | ||
| 155 | #[interrupt] | 159 | #[interrupt] |
| 156 | unsafe fn TIMER_IRQ_2() { | 160 | unsafe fn TIMER_IRQ_2() { |
| 157 | check_alarm(2) | 161 | DRIVER.check_alarm(2) |
| 158 | } | 162 | } |
| 159 | 163 | ||
| 160 | #[interrupt] | 164 | #[interrupt] |
| 161 | unsafe fn TIMER_IRQ_3() { | 165 | unsafe fn TIMER_IRQ_3() { |
| 162 | check_alarm(3) | 166 | DRIVER.check_alarm(3) |
| 163 | } | 167 | } |
diff --git a/embassy-stm32/src/time_driver.rs b/embassy-stm32/src/time_driver.rs index 91b8525ae..4e1eb7aaa 100644 --- a/embassy-stm32/src/time_driver.rs +++ b/embassy-stm32/src/time_driver.rs | |||
| @@ -26,12 +26,12 @@ type T = peripherals::TIM3; | |||
| 26 | #[cfg(feature = "time-driver-tim2")] | 26 | #[cfg(feature = "time-driver-tim2")] |
| 27 | #[interrupt] | 27 | #[interrupt] |
| 28 | fn TIM2() { | 28 | fn TIM2() { |
| 29 | STATE.on_interrupt() | 29 | DRIVER.on_interrupt() |
| 30 | } | 30 | } |
| 31 | #[cfg(feature = "time-driver-tim3")] | 31 | #[cfg(feature = "time-driver-tim3")] |
| 32 | #[interrupt] | 32 | #[interrupt] |
| 33 | fn TIM3() { | 33 | fn TIM3() { |
| 34 | STATE.on_interrupt() | 34 | DRIVER.on_interrupt() |
| 35 | } | 35 | } |
| 36 | 36 | ||
| 37 | // Clock timekeeping works with something we call "periods", which are time intervals | 37 | // Clock timekeeping works with something we call "periods", which are time intervals |
| @@ -76,7 +76,7 @@ impl AlarmState { | |||
| 76 | } | 76 | } |
| 77 | } | 77 | } |
| 78 | 78 | ||
| 79 | struct State { | 79 | struct RtcDriver { |
| 80 | /// Number of 2^15 periods elapsed since boot. | 80 | /// Number of 2^15 periods elapsed since boot. |
| 81 | period: AtomicU32, | 81 | period: AtomicU32, |
| 82 | alarm_count: AtomicU8, | 82 | alarm_count: AtomicU8, |
| @@ -85,13 +85,14 @@ struct State { | |||
| 85 | } | 85 | } |
| 86 | 86 | ||
| 87 | const ALARM_STATE_NEW: AlarmState = AlarmState::new(); | 87 | const ALARM_STATE_NEW: AlarmState = AlarmState::new(); |
| 88 | static STATE: State = State { | 88 | |
| 89 | embassy::time_driver_impl!(static DRIVER: RtcDriver = RtcDriver { | ||
| 89 | period: AtomicU32::new(0), | 90 | period: AtomicU32::new(0), |
| 90 | alarm_count: AtomicU8::new(0), | 91 | alarm_count: AtomicU8::new(0), |
| 91 | alarms: Mutex::new([ALARM_STATE_NEW; ALARM_COUNT]), | 92 | alarms: Mutex::new([ALARM_STATE_NEW; ALARM_COUNT]), |
| 92 | }; | 93 | }); |
| 93 | 94 | ||
| 94 | impl State { | 95 | impl RtcDriver { |
| 95 | fn init(&'static self) { | 96 | fn init(&'static self) { |
| 96 | let r = T::regs(); | 97 | let r = T::regs(); |
| 97 | 98 | ||
| @@ -185,16 +186,6 @@ impl State { | |||
| 185 | }) | 186 | }) |
| 186 | } | 187 | } |
| 187 | 188 | ||
| 188 | fn now(&self) -> u64 { | ||
| 189 | let r = T::regs(); | ||
| 190 | |||
| 191 | let period = self.period.load(Ordering::Relaxed); | ||
| 192 | compiler_fence(Ordering::Acquire); | ||
| 193 | // NOTE(unsafe) Atomic read with no side-effects | ||
| 194 | let counter = unsafe { r.cnt().read().cnt() }; | ||
| 195 | calc_now(period, counter) | ||
| 196 | } | ||
| 197 | |||
| 198 | fn get_alarm<'a>(&'a self, cs: CriticalSection<'a>, alarm: AlarmHandle) -> &'a AlarmState { | 189 | fn get_alarm<'a>(&'a self, cs: CriticalSection<'a>, alarm: AlarmHandle) -> &'a AlarmState { |
| 199 | // safety: we're allowed to assume the AlarmState is created by us, and | 190 | // safety: we're allowed to assume the AlarmState is created by us, and |
| 200 | // we never create one that's out of bounds. | 191 | // we never create one that's out of bounds. |
| @@ -213,8 +204,20 @@ impl State { | |||
| 213 | let f: fn(*mut ()) = unsafe { mem::transmute(alarm.callback.get()) }; | 204 | let f: fn(*mut ()) = unsafe { mem::transmute(alarm.callback.get()) }; |
| 214 | f(alarm.ctx.get()); | 205 | f(alarm.ctx.get()); |
| 215 | } | 206 | } |
| 207 | } | ||
| 208 | |||
| 209 | impl Driver for RtcDriver { | ||
| 210 | fn now(&self) -> u64 { | ||
| 211 | let r = T::regs(); | ||
| 212 | |||
| 213 | let period = self.period.load(Ordering::Relaxed); | ||
| 214 | compiler_fence(Ordering::Acquire); | ||
| 215 | // NOTE(unsafe) Atomic read with no side-effects | ||
| 216 | let counter = unsafe { r.cnt().read().cnt() }; | ||
| 217 | calc_now(period, counter) | ||
| 218 | } | ||
| 216 | 219 | ||
| 217 | fn allocate_alarm(&self) -> Option<AlarmHandle> { | 220 | unsafe fn allocate_alarm(&self) -> Option<AlarmHandle> { |
| 218 | let id = self | 221 | let id = self |
| 219 | .alarm_count | 222 | .alarm_count |
| 220 | .fetch_update(Ordering::AcqRel, Ordering::Acquire, |x| { | 223 | .fetch_update(Ordering::AcqRel, Ordering::Acquire, |x| { |
| @@ -226,7 +229,7 @@ impl State { | |||
| 226 | }); | 229 | }); |
| 227 | 230 | ||
| 228 | match id { | 231 | match id { |
| 229 | Ok(id) => Some(unsafe { AlarmHandle::new(id) }), | 232 | Ok(id) => Some(AlarmHandle::new(id)), |
| 230 | Err(_) => None, | 233 | Err(_) => None, |
| 231 | } | 234 | } |
| 232 | } | 235 | } |
| @@ -269,29 +272,8 @@ impl State { | |||
| 269 | } | 272 | } |
| 270 | } | 273 | } |
| 271 | 274 | ||
| 272 | struct RtcDriver; | ||
| 273 | embassy::time_driver_impl!(RtcDriver); | ||
| 274 | |||
| 275 | impl Driver for RtcDriver { | ||
| 276 | fn now() -> u64 { | ||
| 277 | STATE.now() | ||
| 278 | } | ||
| 279 | |||
| 280 | unsafe fn allocate_alarm() -> Option<AlarmHandle> { | ||
| 281 | STATE.allocate_alarm() | ||
| 282 | } | ||
| 283 | |||
| 284 | fn set_alarm_callback(alarm: AlarmHandle, callback: fn(*mut ()), ctx: *mut ()) { | ||
| 285 | STATE.set_alarm_callback(alarm, callback, ctx) | ||
| 286 | } | ||
| 287 | |||
| 288 | fn set_alarm(alarm: AlarmHandle, timestamp: u64) { | ||
| 289 | STATE.set_alarm(alarm, timestamp) | ||
| 290 | } | ||
| 291 | } | ||
| 292 | |||
| 293 | pub(crate) fn init() { | 275 | pub(crate) fn init() { |
| 294 | STATE.init() | 276 | DRIVER.init() |
| 295 | } | 277 | } |
| 296 | 278 | ||
| 297 | // ------------------------------------------------------ | 279 | // ------------------------------------------------------ |
diff --git a/embassy/Cargo.toml b/embassy/Cargo.toml index ca7d38a55..0ff71ce1d 100644 --- a/embassy/Cargo.toml +++ b/embassy/Cargo.toml | |||
| @@ -7,9 +7,17 @@ resolver = "2" | |||
| 7 | 7 | ||
| 8 | [features] | 8 | [features] |
| 9 | default = [] | 9 | default = [] |
| 10 | std = ["futures/std", "embassy-traits/std"] | 10 | std = ["futures/std", "embassy-traits/std", "time", "time-tick-1mhz"] |
| 11 | 11 | ||
| 12 | # Enable `embassy::time` module. | ||
| 13 | # NOTE: This feature is only intended to be enabled by crates providing the time driver implementation. | ||
| 14 | # Enabling it directly without supplying a time driver will fail to link. | ||
| 12 | time = [] | 15 | time = [] |
| 16 | |||
| 17 | # Set the `embassy::time` tick rate. | ||
| 18 | # NOTE: This feature is only intended to be enabled by crates providing the time driver implementation. | ||
| 19 | # If you're not writing your own driver, check the driver documentation to customize the tick rate. | ||
| 20 | # If you're writing a driver and your tick rate is not listed here, please add it and send a PR! | ||
| 13 | time-tick-32768hz = ["time"] | 21 | time-tick-32768hz = ["time"] |
| 14 | time-tick-1000hz = ["time"] | 22 | time-tick-1000hz = ["time"] |
| 15 | time-tick-1mhz = ["time"] | 23 | time-tick-1mhz = ["time"] |
diff --git a/embassy/src/executor/arch/std.rs b/embassy/src/executor/arch/std.rs new file mode 100644 index 000000000..fb7880542 --- /dev/null +++ b/embassy/src/executor/arch/std.rs | |||
| @@ -0,0 +1,67 @@ | |||
| 1 | use std::marker::PhantomData; | ||
| 2 | use std::sync::{Condvar, Mutex}; | ||
| 3 | |||
| 4 | use super::{raw, Spawner}; | ||
| 5 | |||
| 6 | pub struct Executor { | ||
| 7 | inner: raw::Executor, | ||
| 8 | not_send: PhantomData<*mut ()>, | ||
| 9 | signaler: &'static Signaler, | ||
| 10 | } | ||
| 11 | |||
| 12 | impl Executor { | ||
| 13 | pub fn new() -> Self { | ||
| 14 | let signaler = &*Box::leak(Box::new(Signaler::new())); | ||
| 15 | Self { | ||
| 16 | inner: raw::Executor::new( | ||
| 17 | |p| unsafe { | ||
| 18 | let s = &*(p as *const () as *const Signaler); | ||
| 19 | s.signal() | ||
| 20 | }, | ||
| 21 | signaler as *const _ as _, | ||
| 22 | ), | ||
| 23 | not_send: PhantomData, | ||
| 24 | signaler, | ||
| 25 | } | ||
| 26 | } | ||
| 27 | |||
| 28 | /// Runs the executor. | ||
| 29 | /// | ||
| 30 | /// This function never returns. | ||
| 31 | pub fn run(&'static mut self, init: impl FnOnce(Spawner)) -> ! { | ||
| 32 | init(unsafe { self.inner.spawner() }); | ||
| 33 | |||
| 34 | loop { | ||
| 35 | unsafe { self.inner.run_queued() }; | ||
| 36 | self.signaler.wait() | ||
| 37 | } | ||
| 38 | } | ||
| 39 | } | ||
| 40 | |||
| 41 | struct Signaler { | ||
| 42 | mutex: Mutex<bool>, | ||
| 43 | condvar: Condvar, | ||
| 44 | } | ||
| 45 | |||
| 46 | impl Signaler { | ||
| 47 | fn new() -> Self { | ||
| 48 | Self { | ||
| 49 | mutex: Mutex::new(false), | ||
| 50 | condvar: Condvar::new(), | ||
| 51 | } | ||
| 52 | } | ||
| 53 | |||
| 54 | fn wait(&self) { | ||
| 55 | let mut signaled = self.mutex.lock().unwrap(); | ||
| 56 | while !*signaled { | ||
| 57 | signaled = self.condvar.wait(signaled).unwrap(); | ||
| 58 | } | ||
| 59 | *signaled = false; | ||
| 60 | } | ||
| 61 | |||
| 62 | fn signal(&self) { | ||
| 63 | let mut signaled = self.mutex.lock().unwrap(); | ||
| 64 | *signaled = true; | ||
| 65 | self.condvar.notify_one(); | ||
| 66 | } | ||
| 67 | } | ||
diff --git a/embassy/src/executor/mod.rs b/embassy/src/executor/mod.rs index e0ac566f1..5ffbe689e 100644 --- a/embassy/src/executor/mod.rs +++ b/embassy/src/executor/mod.rs | |||
| @@ -1,4 +1,5 @@ | |||
| 1 | #[path = "arch/arm.rs"] | 1 | #[cfg_attr(feature = "std", path = "arch/std.rs")] |
| 2 | #[cfg_attr(not(feature = "std"), path = "arch/arm.rs")] | ||
| 2 | mod arch; | 3 | mod arch; |
| 3 | pub mod raw; | 4 | pub mod raw; |
| 4 | mod spawner; | 5 | mod spawner; |
diff --git a/embassy/src/io/mod.rs b/embassy/src/io/mod.rs index 8445f6e80..52b050971 100644 --- a/embassy/src/io/mod.rs +++ b/embassy/src/io/mod.rs | |||
| @@ -1,7 +1,11 @@ | |||
| 1 | mod error; | 1 | mod error; |
| 2 | #[cfg(feature = "std")] | ||
| 3 | mod std; | ||
| 2 | mod traits; | 4 | mod traits; |
| 3 | mod util; | 5 | mod util; |
| 4 | 6 | ||
| 5 | pub use self::error::*; | 7 | pub use self::error::*; |
| 8 | #[cfg(feature = "std")] | ||
| 9 | pub use self::std::*; | ||
| 6 | pub use self::traits::*; | 10 | pub use self::traits::*; |
| 7 | pub use self::util::*; | 11 | pub use self::util::*; |
diff --git a/embassy/src/io/std.rs b/embassy/src/io/std.rs new file mode 100644 index 000000000..ddec8d56d --- /dev/null +++ b/embassy/src/io/std.rs | |||
| @@ -0,0 +1,35 @@ | |||
| 1 | use core::pin::Pin; | ||
| 2 | use core::task::{Context, Poll}; | ||
| 3 | use futures::io as std_io; | ||
| 4 | |||
| 5 | use super::{AsyncBufRead, AsyncWrite, Result}; | ||
| 6 | |||
| 7 | pub struct FromStdIo<T>(T); | ||
| 8 | |||
| 9 | impl<T> FromStdIo<T> { | ||
| 10 | pub fn new(inner: T) -> Self { | ||
| 11 | Self(inner) | ||
| 12 | } | ||
| 13 | } | ||
| 14 | |||
| 15 | impl<T: std_io::AsyncBufRead> AsyncBufRead for FromStdIo<T> { | ||
| 16 | fn poll_fill_buf(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Result<&[u8]>> { | ||
| 17 | let Self(inner) = unsafe { self.get_unchecked_mut() }; | ||
| 18 | unsafe { Pin::new_unchecked(inner) } | ||
| 19 | .poll_fill_buf(cx) | ||
| 20 | .map_err(|e| e.into()) | ||
| 21 | } | ||
| 22 | fn consume(self: Pin<&mut Self>, amt: usize) { | ||
| 23 | let Self(inner) = unsafe { self.get_unchecked_mut() }; | ||
| 24 | unsafe { Pin::new_unchecked(inner) }.consume(amt) | ||
| 25 | } | ||
| 26 | } | ||
| 27 | |||
| 28 | impl<T: std_io::AsyncWrite> AsyncWrite for FromStdIo<T> { | ||
| 29 | fn poll_write(self: Pin<&mut Self>, cx: &mut Context<'_>, buf: &[u8]) -> Poll<Result<usize>> { | ||
| 30 | let Self(inner) = unsafe { self.get_unchecked_mut() }; | ||
| 31 | unsafe { Pin::new_unchecked(inner) } | ||
| 32 | .poll_write(cx, buf) | ||
| 33 | .map_err(|e| e.into()) | ||
| 34 | } | ||
| 35 | } | ||
diff --git a/embassy/src/io/traits.rs b/embassy/src/io/traits.rs index b59cdc0d4..8e4a981da 100644 --- a/embassy/src/io/traits.rs +++ b/embassy/src/io/traits.rs | |||
| @@ -5,9 +5,6 @@ use core::task::{Context, Poll}; | |||
| 5 | #[cfg(feature = "alloc")] | 5 | #[cfg(feature = "alloc")] |
| 6 | use alloc::boxed::Box; | 6 | use alloc::boxed::Box; |
| 7 | 7 | ||
| 8 | #[cfg(feature = "std")] | ||
| 9 | use futures::io as std_io; | ||
| 10 | |||
| 11 | use super::error::Result; | 8 | use super::error::Result; |
| 12 | 9 | ||
| 13 | /// Read bytes asynchronously. | 10 | /// Read bytes asynchronously. |
| @@ -159,37 +156,3 @@ where | |||
| 159 | self.get_mut().as_mut().poll_write(cx, buf) | 156 | self.get_mut().as_mut().poll_write(cx, buf) |
| 160 | } | 157 | } |
| 161 | } | 158 | } |
| 162 | |||
| 163 | #[cfg(feature = "std")] | ||
| 164 | pub struct FromStdIo<T>(T); | ||
| 165 | |||
| 166 | #[cfg(feature = "std")] | ||
| 167 | impl<T> FromStdIo<T> { | ||
| 168 | pub fn new(inner: T) -> Self { | ||
| 169 | Self(inner) | ||
| 170 | } | ||
| 171 | } | ||
| 172 | |||
| 173 | #[cfg(feature = "std")] | ||
| 174 | impl<T: std_io::AsyncBufRead> AsyncBufRead for FromStdIo<T> { | ||
| 175 | fn poll_fill_buf(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Result<&[u8]>> { | ||
| 176 | let Self(inner) = unsafe { self.get_unchecked_mut() }; | ||
| 177 | unsafe { Pin::new_unchecked(inner) } | ||
| 178 | .poll_fill_buf(cx) | ||
| 179 | .map_err(|e| e.into()) | ||
| 180 | } | ||
| 181 | fn consume(self: Pin<&mut Self>, amt: usize) { | ||
| 182 | let Self(inner) = unsafe { self.get_unchecked_mut() }; | ||
| 183 | unsafe { Pin::new_unchecked(inner) }.consume(amt) | ||
| 184 | } | ||
| 185 | } | ||
| 186 | |||
| 187 | #[cfg(feature = "std")] | ||
| 188 | impl<T: std_io::AsyncWrite> AsyncWrite for FromStdIo<T> { | ||
| 189 | fn poll_write(self: Pin<&mut Self>, cx: &mut Context<'_>, buf: &[u8]) -> Poll<Result<usize>> { | ||
| 190 | let Self(inner) = unsafe { self.get_unchecked_mut() }; | ||
| 191 | unsafe { Pin::new_unchecked(inner) } | ||
| 192 | .poll_write(cx, buf) | ||
| 193 | .map_err(|e| e.into()) | ||
| 194 | } | ||
| 195 | } | ||
diff --git a/embassy/src/time/delay.rs b/embassy/src/time/delay.rs index c97e8b5c2..a46ee3a4f 100644 --- a/embassy/src/time/delay.rs +++ b/embassy/src/time/delay.rs | |||
| @@ -4,11 +4,10 @@ use super::{Duration, Instant, Timer}; | |||
| 4 | 4 | ||
| 5 | /// Type implementing async delays and blocking `embedded-hal` delays. | 5 | /// Type implementing async delays and blocking `embedded-hal` delays. |
| 6 | /// | 6 | /// |
| 7 | /// For this interface to work, the Executor's clock must be correctly initialized before using it. | ||
| 8 | /// The delays are implemented in a "best-effort" way, meaning that the cpu will block for at least | 7 | /// The delays are implemented in a "best-effort" way, meaning that the cpu will block for at least |
| 9 | /// the amount provided, but accuracy can be affected by many factors, including interrupt usage. | 8 | /// the amount provided, but accuracy can be affected by many factors, including interrupt usage. |
| 10 | /// Make sure to use a suitable tick rate for your use case. The tick rate can be chosen through | 9 | /// Make sure to use a suitable tick rate for your use case. The tick rate is defined by the currently |
| 11 | /// features flags of this crate. | 10 | /// active driver. |
| 12 | pub struct Delay; | 11 | pub struct Delay; |
| 13 | 12 | ||
| 14 | impl crate::traits::delay::Delay for Delay { | 13 | impl crate::traits::delay::Delay for Delay { |
| @@ -58,9 +57,7 @@ impl embedded_hal::blocking::delay::DelayUs<u32> for Delay { | |||
| 58 | } | 57 | } |
| 59 | } | 58 | } |
| 60 | 59 | ||
| 61 | /// Blocks the cpu for at least `duration`. | 60 | /// Blocks for at least `duration`. |
| 62 | /// | ||
| 63 | /// For this interface to work, the Executor's clock must be correctly initialized before using it. | ||
| 64 | pub fn block_for(duration: Duration) { | 61 | pub fn block_for(duration: Duration) { |
| 65 | let expires_at = Instant::now() + duration; | 62 | let expires_at = Instant::now() + duration; |
| 66 | while Instant::now() < expires_at {} | 63 | while Instant::now() < expires_at {} |
diff --git a/embassy/src/time/driver.rs b/embassy/src/time/driver.rs index b09cffd8c..1b8949ae1 100644 --- a/embassy/src/time/driver.rs +++ b/embassy/src/time/driver.rs | |||
| @@ -1,3 +1,60 @@ | |||
| 1 | //! Time driver interface | ||
| 2 | //! | ||
| 3 | //! This module defines the interface a driver needs to implement to power the `embassy::time` module. | ||
| 4 | //! | ||
| 5 | //! # Implementing a driver | ||
| 6 | //! | ||
| 7 | //! - Define a struct `MyDriver` | ||
| 8 | //! - Implement [`Driver`] for it | ||
| 9 | //! - Register it as the global driver with [`time_driver_impl`]. | ||
| 10 | //! - Enable the Cargo features `embassy/time` and one of `embassy/time-tick-*` corresponding to the | ||
| 11 | //! tick rate of your driver. | ||
| 12 | //! | ||
| 13 | //! If you wish to make the tick rate configurable by the end user, you should do so by exposing your own | ||
| 14 | //! Cargo features and having each enable the corresponding `embassy/time-tick-*`. | ||
| 15 | //! | ||
| 16 | //! # Linkage details | ||
| 17 | //! | ||
| 18 | //! Instead of the usual "trait + generic params" approach, calls from embassy to the driver are done via `extern` functions. | ||
| 19 | //! | ||
| 20 | //! `embassy` internally defines the driver functions as `extern "Rust" { fn _embassy_time_now() -> u64; }` and calls them. | ||
| 21 | //! The driver crate defines the functions as `#[no_mangle] fn _embassy_time_now() -> u64`. The linker will resolve the | ||
| 22 | //! calls from the `embassy` crate to call into the driver crate. | ||
| 23 | //! | ||
| 24 | //! If there is none or multiple drivers in the crate tree, linking will fail. | ||
| 25 | //! | ||
| 26 | //! This method has a few key advantages for something as foundational as timekeeping: | ||
| 27 | //! | ||
| 28 | //! - The time driver is available everywhere easily, without having to thread the implementation | ||
| 29 | //! through generic parameters. This is especially helpful for libraries. | ||
| 30 | //! - It means comparing `Instant`s will always make sense: if there were multiple drivers | ||
| 31 | //! active, one could compare an `Instant` from driver A to an `Instant` from driver B, which | ||
| 32 | //! would yield incorrect results. | ||
| 33 | //! | ||
| 34 | //! # Example | ||
| 35 | //! | ||
| 36 | //! ``` | ||
| 37 | //! use embassy::time::driver::{Driver, AlarmHandle}; | ||
| 38 | //! | ||
| 39 | //! struct MyDriver{}; // not public! | ||
| 40 | //! embassy::time_driver_impl!(static DRIVER: MyDriver = MyDriver{}); | ||
| 41 | //! | ||
| 42 | //! impl Driver for MyDriver { | ||
| 43 | //! fn now(&self) -> u64 { | ||
| 44 | //! todo!() | ||
| 45 | //! } | ||
| 46 | //! unsafe fn allocate_alarm(&self) -> Option<AlarmHandle> { | ||
| 47 | //! todo!() | ||
| 48 | //! } | ||
| 49 | //! fn set_alarm_callback(&self, alarm: AlarmHandle, callback: fn(*mut ()), ctx: *mut ()) { | ||
| 50 | //! todo!() | ||
| 51 | //! } | ||
| 52 | //! fn set_alarm(&self, alarm: AlarmHandle, timestamp: u64) { | ||
| 53 | //! todo!() | ||
| 54 | //! } | ||
| 55 | //! } | ||
| 56 | //! ``` | ||
| 57 | |||
| 1 | /// Alarm handle, assigned by the driver. | 58 | /// Alarm handle, assigned by the driver. |
| 2 | #[derive(Clone, Copy)] | 59 | #[derive(Clone, Copy)] |
| 3 | pub struct AlarmHandle { | 60 | pub struct AlarmHandle { |
| @@ -21,22 +78,22 @@ impl AlarmHandle { | |||
| 21 | } | 78 | } |
| 22 | 79 | ||
| 23 | /// Time driver | 80 | /// Time driver |
| 24 | pub trait Driver { | 81 | pub trait Driver: Send + Sync + 'static { |
| 25 | /// Return the current timestamp in ticks. | 82 | /// Return the current timestamp in ticks. |
| 26 | /// This is guaranteed to be monotonic, i.e. a call to now() will always return | 83 | /// This is guaranteed to be monotonic, i.e. a call to now() will always return |
| 27 | /// a greater or equal value than earler calls. | 84 | /// a greater or equal value than earler calls. |
| 28 | fn now() -> u64; | 85 | fn now(&self) -> u64; |
| 29 | 86 | ||
| 30 | /// Try allocating an alarm handle. Returns None if no alarms left. | 87 | /// Try allocating an alarm handle. Returns None if no alarms left. |
| 31 | /// Initially the alarm has no callback set, and a null `ctx` pointer. | 88 | /// Initially the alarm has no callback set, and a null `ctx` pointer. |
| 32 | /// | 89 | /// |
| 33 | /// # Safety | 90 | /// # Safety |
| 34 | /// It is UB to make the alarm fire before setting a callback. | 91 | /// It is UB to make the alarm fire before setting a callback. |
| 35 | unsafe fn allocate_alarm() -> Option<AlarmHandle>; | 92 | unsafe fn allocate_alarm(&self) -> Option<AlarmHandle>; |
| 36 | 93 | ||
| 37 | /// Sets the callback function to be called when the alarm triggers. | 94 | /// Sets the callback function to be called when the alarm triggers. |
| 38 | /// The callback may be called from any context (interrupt or thread mode). | 95 | /// The callback may be called from any context (interrupt or thread mode). |
| 39 | fn set_alarm_callback(alarm: AlarmHandle, callback: fn(*mut ()), ctx: *mut ()); | 96 | fn set_alarm_callback(&self, alarm: AlarmHandle, callback: fn(*mut ()), ctx: *mut ()); |
| 40 | 97 | ||
| 41 | /// Sets an alarm at the given timestamp. When the current timestamp reaches that | 98 | /// Sets an alarm at the given timestamp. When the current timestamp reaches that |
| 42 | /// timestamp, the provided callback funcion will be called. | 99 | /// timestamp, the provided callback funcion will be called. |
| @@ -44,7 +101,7 @@ pub trait Driver { | |||
| 44 | /// When callback is called, it is guaranteed that now() will return a value greater or equal than timestamp. | 101 | /// When callback is called, it is guaranteed that now() will return a value greater or equal than timestamp. |
| 45 | /// | 102 | /// |
| 46 | /// Only one alarm can be active at a time. This overwrites any previously-set alarm if any. | 103 | /// Only one alarm can be active at a time. This overwrites any previously-set alarm if any. |
| 47 | fn set_alarm(alarm: AlarmHandle, timestamp: u64); | 104 | fn set_alarm(&self, alarm: AlarmHandle, timestamp: u64); |
| 48 | } | 105 | } |
| 49 | 106 | ||
| 50 | extern "Rust" { | 107 | extern "Rust" { |
| @@ -70,49 +127,34 @@ pub(crate) fn set_alarm(alarm: AlarmHandle, timestamp: u64) { | |||
| 70 | 127 | ||
| 71 | /// Set the time Driver implementation. | 128 | /// Set the time Driver implementation. |
| 72 | /// | 129 | /// |
| 73 | /// # Example | 130 | /// See the module documentation for an example. |
| 74 | /// | ||
| 75 | /// ``` | ||
| 76 | /// struct MyDriver; | ||
| 77 | /// embassy::time_driver_impl!(MyDriver); | ||
| 78 | /// | ||
| 79 | /// unsafe impl embassy::time::driver::Driver for MyDriver { | ||
| 80 | /// fn now() -> u64 { | ||
| 81 | /// todo!() | ||
| 82 | /// } | ||
| 83 | /// unsafe fn allocate_alarm() -> Option<AlarmHandle> { | ||
| 84 | /// todo!() | ||
| 85 | /// } | ||
| 86 | /// fn set_alarm_callback(alarm: AlarmHandle, callback: fn(*mut ()), ctx: *mut ()) { | ||
| 87 | /// todo!() | ||
| 88 | /// } | ||
| 89 | /// fn set_alarm(alarm: AlarmHandle, timestamp: u64) { | ||
| 90 | /// todo!() | ||
| 91 | /// } | ||
| 92 | /// } | ||
| 93 | /// | ||
| 94 | #[macro_export] | 131 | #[macro_export] |
| 95 | macro_rules! time_driver_impl { | 132 | macro_rules! time_driver_impl { |
| 96 | ($t: ty) => { | 133 | (static $name:ident: $t: ty = $val:expr) => { |
| 134 | static $name: $t = $val; | ||
| 135 | |||
| 97 | #[no_mangle] | 136 | #[no_mangle] |
| 98 | fn _embassy_time_now() -> u64 { | 137 | fn _embassy_time_now() -> u64 { |
| 99 | <$t as $crate::time::driver::Driver>::now() | 138 | <$t as $crate::time::driver::Driver>::now(&$name) |
| 100 | } | 139 | } |
| 140 | |||
| 101 | #[no_mangle] | 141 | #[no_mangle] |
| 102 | unsafe fn _embassy_time_allocate_alarm() -> Option<AlarmHandle> { | 142 | unsafe fn _embassy_time_allocate_alarm() -> Option<$crate::time::driver::AlarmHandle> { |
| 103 | <$t as $crate::time::driver::Driver>::allocate_alarm() | 143 | <$t as $crate::time::driver::Driver>::allocate_alarm(&$name) |
| 104 | } | 144 | } |
| 145 | |||
| 105 | #[no_mangle] | 146 | #[no_mangle] |
| 106 | fn _embassy_time_set_alarm_callback( | 147 | fn _embassy_time_set_alarm_callback( |
| 107 | alarm: AlarmHandle, | 148 | alarm: $crate::time::driver::AlarmHandle, |
| 108 | callback: fn(*mut ()), | 149 | callback: fn(*mut ()), |
| 109 | ctx: *mut (), | 150 | ctx: *mut (), |
| 110 | ) { | 151 | ) { |
| 111 | <$t as $crate::time::driver::Driver>::set_alarm_callback(alarm, callback, ctx) | 152 | <$t as $crate::time::driver::Driver>::set_alarm_callback(&$name, alarm, callback, ctx) |
| 112 | } | 153 | } |
| 154 | |||
| 113 | #[no_mangle] | 155 | #[no_mangle] |
| 114 | fn _embassy_time_set_alarm(alarm: AlarmHandle, timestamp: u64) { | 156 | fn _embassy_time_set_alarm(alarm: $crate::time::driver::AlarmHandle, timestamp: u64) { |
| 115 | <$t as $crate::time::driver::Driver>::set_alarm(alarm, timestamp) | 157 | <$t as $crate::time::driver::Driver>::set_alarm(&$name, alarm, timestamp) |
| 116 | } | 158 | } |
| 117 | }; | 159 | }; |
| 118 | } | 160 | } |
diff --git a/embassy/src/time/driver_std.rs b/embassy/src/time/driver_std.rs new file mode 100644 index 000000000..29911c4d2 --- /dev/null +++ b/embassy/src/time/driver_std.rs | |||
| @@ -0,0 +1,208 @@ | |||
| 1 | use atomic_polyfill::{AtomicU8, Ordering}; | ||
| 2 | use std::cell::UnsafeCell; | ||
| 3 | use std::mem; | ||
| 4 | use std::mem::MaybeUninit; | ||
| 5 | use std::sync::{Condvar, Mutex, Once}; | ||
| 6 | use std::time::Duration as StdDuration; | ||
| 7 | use std::time::Instant as StdInstant; | ||
| 8 | use std::{ptr, thread}; | ||
| 9 | |||
| 10 | use crate::time::driver::{AlarmHandle, Driver}; | ||
| 11 | |||
| 12 | const ALARM_COUNT: usize = 4; | ||
| 13 | |||
| 14 | struct AlarmState { | ||
| 15 | timestamp: u64, | ||
| 16 | |||
| 17 | // This is really a Option<(fn(*mut ()), *mut ())> | ||
| 18 | // but fn pointers aren't allowed in const yet | ||
| 19 | callback: *const (), | ||
| 20 | ctx: *mut (), | ||
| 21 | } | ||
| 22 | |||
| 23 | unsafe impl Send for AlarmState {} | ||
| 24 | |||
| 25 | impl AlarmState { | ||
| 26 | const fn new() -> Self { | ||
| 27 | Self { | ||
| 28 | timestamp: u64::MAX, | ||
| 29 | callback: ptr::null(), | ||
| 30 | ctx: ptr::null_mut(), | ||
| 31 | } | ||
| 32 | } | ||
| 33 | } | ||
| 34 | |||
| 35 | struct TimeDriver { | ||
| 36 | alarm_count: AtomicU8, | ||
| 37 | |||
| 38 | once: Once, | ||
| 39 | alarms: UninitCell<Mutex<[AlarmState; ALARM_COUNT]>>, | ||
| 40 | zero_instant: UninitCell<StdInstant>, | ||
| 41 | signaler: UninitCell<Signaler>, | ||
| 42 | } | ||
| 43 | |||
| 44 | const ALARM_NEW: AlarmState = AlarmState::new(); | ||
| 45 | crate::time_driver_impl!(static DRIVER: TimeDriver = TimeDriver { | ||
| 46 | alarm_count: AtomicU8::new(0), | ||
| 47 | |||
| 48 | once: Once::new(), | ||
| 49 | alarms: UninitCell::uninit(), | ||
| 50 | zero_instant: UninitCell::uninit(), | ||
| 51 | signaler: UninitCell::uninit(), | ||
| 52 | }); | ||
| 53 | |||
| 54 | impl TimeDriver { | ||
| 55 | fn init(&self) { | ||
| 56 | self.once.call_once(|| unsafe { | ||
| 57 | self.alarms.write(Mutex::new([ALARM_NEW; ALARM_COUNT])); | ||
| 58 | self.zero_instant.write(StdInstant::now()); | ||
| 59 | self.signaler.write(Signaler::new()); | ||
| 60 | |||
| 61 | thread::spawn(Self::alarm_thread); | ||
| 62 | }); | ||
| 63 | } | ||
| 64 | |||
| 65 | fn alarm_thread() { | ||
| 66 | loop { | ||
| 67 | let now = DRIVER.now(); | ||
| 68 | |||
| 69 | let mut next_alarm = u64::MAX; | ||
| 70 | { | ||
| 71 | let alarms = &mut *unsafe { DRIVER.alarms.as_ref() }.lock().unwrap(); | ||
| 72 | for alarm in alarms { | ||
| 73 | if alarm.timestamp <= now { | ||
| 74 | alarm.timestamp = u64::MAX; | ||
| 75 | |||
| 76 | // Call after clearing alarm, so the callback can set another alarm. | ||
| 77 | |||
| 78 | // safety: | ||
| 79 | // - 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 | ||
| 81 | let f: fn(*mut ()) = unsafe { mem::transmute(alarm.callback) }; | ||
| 82 | f(alarm.ctx); | ||
| 83 | } else { | ||
| 84 | next_alarm = next_alarm.min(alarm.timestamp); | ||
| 85 | } | ||
| 86 | } | ||
| 87 | } | ||
| 88 | |||
| 89 | let until = | ||
| 90 | unsafe { DRIVER.zero_instant.read() } + StdDuration::from_micros(next_alarm); | ||
| 91 | |||
| 92 | unsafe { DRIVER.signaler.as_ref() }.wait_until(until); | ||
| 93 | } | ||
| 94 | } | ||
| 95 | } | ||
| 96 | |||
| 97 | impl Driver for TimeDriver { | ||
| 98 | fn now(&self) -> u64 { | ||
| 99 | self.init(); | ||
| 100 | |||
| 101 | let zero = unsafe { self.zero_instant.read() }; | ||
| 102 | StdInstant::now().duration_since(zero).as_micros() as u64 | ||
| 103 | } | ||
| 104 | |||
| 105 | unsafe fn allocate_alarm(&self) -> Option<AlarmHandle> { | ||
| 106 | let id = self | ||
| 107 | .alarm_count | ||
| 108 | .fetch_update(Ordering::AcqRel, Ordering::Acquire, |x| { | ||
| 109 | if x < ALARM_COUNT as u8 { | ||
| 110 | Some(x + 1) | ||
| 111 | } else { | ||
| 112 | None | ||
| 113 | } | ||
| 114 | }); | ||
| 115 | |||
| 116 | match id { | ||
| 117 | Ok(id) => Some(AlarmHandle::new(id)), | ||
| 118 | Err(_) => None, | ||
| 119 | } | ||
| 120 | } | ||
| 121 | |||
| 122 | fn set_alarm_callback(&self, alarm: AlarmHandle, callback: fn(*mut ()), ctx: *mut ()) { | ||
| 123 | self.init(); | ||
| 124 | let mut alarms = unsafe { self.alarms.as_ref() }.lock().unwrap(); | ||
| 125 | let alarm = &mut alarms[alarm.id() as usize]; | ||
| 126 | alarm.callback = callback as *const (); | ||
| 127 | alarm.ctx = ctx; | ||
| 128 | } | ||
| 129 | |||
| 130 | fn set_alarm(&self, alarm: AlarmHandle, timestamp: u64) { | ||
| 131 | self.init(); | ||
| 132 | let mut alarms = unsafe { self.alarms.as_ref() }.lock().unwrap(); | ||
| 133 | let alarm = &mut alarms[alarm.id() as usize]; | ||
| 134 | alarm.timestamp = timestamp; | ||
| 135 | unsafe { self.signaler.as_ref() }.signal(); | ||
| 136 | } | ||
| 137 | } | ||
| 138 | |||
| 139 | struct Signaler { | ||
| 140 | mutex: Mutex<bool>, | ||
| 141 | condvar: Condvar, | ||
| 142 | } | ||
| 143 | |||
| 144 | impl Signaler { | ||
| 145 | fn new() -> Self { | ||
| 146 | Self { | ||
| 147 | mutex: Mutex::new(false), | ||
| 148 | condvar: Condvar::new(), | ||
| 149 | } | ||
| 150 | } | ||
| 151 | |||
| 152 | fn wait_until(&self, until: StdInstant) { | ||
| 153 | let mut signaled = self.mutex.lock().unwrap(); | ||
| 154 | while !*signaled { | ||
| 155 | let now = StdInstant::now(); | ||
| 156 | |||
| 157 | if now >= until { | ||
| 158 | break; | ||
| 159 | } | ||
| 160 | |||
| 161 | let dur = until - now; | ||
| 162 | let (signaled2, timeout) = self.condvar.wait_timeout(signaled, dur).unwrap(); | ||
| 163 | signaled = signaled2; | ||
| 164 | if timeout.timed_out() { | ||
| 165 | break; | ||
| 166 | } | ||
| 167 | } | ||
| 168 | *signaled = false; | ||
| 169 | } | ||
| 170 | |||
| 171 | fn signal(&self) { | ||
| 172 | let mut signaled = self.mutex.lock().unwrap(); | ||
| 173 | *signaled = true; | ||
| 174 | self.condvar.notify_one(); | ||
| 175 | } | ||
| 176 | } | ||
| 177 | |||
| 178 | pub(crate) struct UninitCell<T>(MaybeUninit<UnsafeCell<T>>); | ||
| 179 | unsafe impl<T> Send for UninitCell<T> {} | ||
| 180 | unsafe impl<T> Sync for UninitCell<T> {} | ||
| 181 | |||
| 182 | impl<T> UninitCell<T> { | ||
| 183 | pub const fn uninit() -> Self { | ||
| 184 | Self(MaybeUninit::uninit()) | ||
| 185 | } | ||
| 186 | |||
| 187 | pub unsafe fn as_ptr(&self) -> *const T { | ||
| 188 | (*self.0.as_ptr()).get() | ||
| 189 | } | ||
| 190 | |||
| 191 | pub unsafe fn as_mut_ptr(&self) -> *mut T { | ||
| 192 | (*self.0.as_ptr()).get() | ||
| 193 | } | ||
| 194 | |||
| 195 | pub unsafe fn as_ref(&self) -> &T { | ||
| 196 | &*self.as_ptr() | ||
| 197 | } | ||
| 198 | |||
| 199 | pub unsafe fn write(&self, val: T) { | ||
| 200 | ptr::write(self.as_mut_ptr(), val) | ||
| 201 | } | ||
| 202 | } | ||
| 203 | |||
| 204 | impl<T: Copy> UninitCell<T> { | ||
| 205 | pub unsafe fn read(&self) -> T { | ||
| 206 | ptr::read(self.as_mut_ptr()) | ||
| 207 | } | ||
| 208 | } | ||
diff --git a/embassy/src/time/duration.rs b/embassy/src/time/duration.rs index 5157450ad..e196a00c7 100644 --- a/embassy/src/time/duration.rs +++ b/embassy/src/time/duration.rs | |||
| @@ -11,18 +11,27 @@ pub struct Duration { | |||
| 11 | } | 11 | } |
| 12 | 12 | ||
| 13 | impl Duration { | 13 | impl Duration { |
| 14 | /// The smallest value that can be represented by the `Duration` type. | ||
| 15 | pub const MIN: Duration = Duration { ticks: u64::MIN }; | ||
| 16 | /// The largest value that can be represented by the `Duration` type. | ||
| 17 | pub const MAX: Duration = Duration { ticks: u64::MAX }; | ||
| 18 | |||
| 19 | /// Tick count of the `Duration`. | ||
| 14 | pub const fn as_ticks(&self) -> u64 { | 20 | pub const fn as_ticks(&self) -> u64 { |
| 15 | self.ticks | 21 | self.ticks |
| 16 | } | 22 | } |
| 17 | 23 | ||
| 24 | /// Convert the `Duration` to seconds, rounding down. | ||
| 18 | pub const fn as_secs(&self) -> u64 { | 25 | pub const fn as_secs(&self) -> u64 { |
| 19 | self.ticks / TICKS_PER_SECOND | 26 | self.ticks / TICKS_PER_SECOND |
| 20 | } | 27 | } |
| 21 | 28 | ||
| 29 | /// Convert the `Duration` to milliseconds, rounding down. | ||
| 22 | pub const fn as_millis(&self) -> u64 { | 30 | pub const fn as_millis(&self) -> u64 { |
| 23 | self.ticks * 1000 / TICKS_PER_SECOND | 31 | self.ticks * 1000 / TICKS_PER_SECOND |
| 24 | } | 32 | } |
| 25 | 33 | ||
| 34 | /// Convert the `Duration` to microseconds, rounding down. | ||
| 26 | pub const fn as_micros(&self) -> u64 { | 35 | pub const fn as_micros(&self) -> u64 { |
| 27 | self.ticks * 1_000_000 / TICKS_PER_SECOND | 36 | self.ticks * 1_000_000 / TICKS_PER_SECOND |
| 28 | } | 37 | } |
diff --git a/embassy/src/time/instant.rs b/embassy/src/time/instant.rs index 3d430d886..36c2b2dc2 100644 --- a/embassy/src/time/instant.rs +++ b/embassy/src/time/instant.rs | |||
| @@ -11,7 +11,9 @@ pub struct Instant { | |||
| 11 | } | 11 | } |
| 12 | 12 | ||
| 13 | impl Instant { | 13 | impl Instant { |
| 14 | /// The smallest (earliest) value that can be represented by the `Instant` type. | ||
| 14 | pub const MIN: Instant = Instant { ticks: u64::MIN }; | 15 | pub const MIN: Instant = Instant { ticks: u64::MIN }; |
| 16 | /// The largest (latest) value that can be represented by the `Instant` type. | ||
| 15 | pub const MAX: Instant = Instant { ticks: u64::MAX }; | 17 | pub const MAX: Instant = Instant { ticks: u64::MAX }; |
| 16 | 18 | ||
| 17 | /// Returns an Instant representing the current time. | 19 | /// Returns an Instant representing the current time. |
| @@ -21,39 +23,38 @@ impl Instant { | |||
| 21 | } | 23 | } |
| 22 | } | 24 | } |
| 23 | 25 | ||
| 24 | /// Instant as clock ticks since MCU start. | 26 | /// Create an Instant from a tick count since system boot. |
| 25 | pub const fn from_ticks(ticks: u64) -> Self { | 27 | pub const fn from_ticks(ticks: u64) -> Self { |
| 26 | Self { ticks } | 28 | Self { ticks } |
| 27 | } | 29 | } |
| 28 | 30 | ||
| 29 | /// Instant as milliseconds since MCU start. | 31 | /// Create an Instant from a millisecond count since system boot. |
| 30 | pub const fn from_millis(millis: u64) -> Self { | 32 | pub const fn from_millis(millis: u64) -> Self { |
| 31 | Self { | 33 | Self { |
| 32 | ticks: millis * TICKS_PER_SECOND as u64 / 1000, | 34 | ticks: millis * TICKS_PER_SECOND / 1000, |
| 33 | } | 35 | } |
| 34 | } | 36 | } |
| 35 | 37 | ||
| 36 | /// Instant representing seconds since MCU start. | 38 | /// Create an Instant from a second count since system boot. |
| 37 | pub const fn from_secs(seconds: u64) -> Self { | 39 | pub const fn from_secs(seconds: u64) -> Self { |
| 38 | Self { | 40 | Self { |
| 39 | ticks: seconds * TICKS_PER_SECOND as u64, | 41 | ticks: seconds * TICKS_PER_SECOND, |
| 40 | } | 42 | } |
| 41 | } | 43 | } |
| 42 | 44 | ||
| 43 | /// Instant as ticks since MCU start. | 45 | /// Tick count since system boot. |
| 44 | |||
| 45 | pub const fn as_ticks(&self) -> u64 { | 46 | pub const fn as_ticks(&self) -> u64 { |
| 46 | self.ticks | 47 | self.ticks |
| 47 | } | 48 | } |
| 48 | /// Instant as seconds since MCU start. | ||
| 49 | 49 | ||
| 50 | /// Seconds since system boot. | ||
| 50 | pub const fn as_secs(&self) -> u64 { | 51 | pub const fn as_secs(&self) -> u64 { |
| 51 | self.ticks / TICKS_PER_SECOND as u64 | 52 | self.ticks / TICKS_PER_SECOND |
| 52 | } | 53 | } |
| 53 | /// Instant as miliseconds since MCU start. | ||
| 54 | 54 | ||
| 55 | /// Milliseconds since system boot. | ||
| 55 | pub const fn as_millis(&self) -> u64 { | 56 | pub const fn as_millis(&self) -> u64 { |
| 56 | self.ticks * 1000 / TICKS_PER_SECOND as u64 | 57 | self.ticks * 1000 / TICKS_PER_SECOND |
| 57 | } | 58 | } |
| 58 | 59 | ||
| 59 | /// Duration between this Instant and another Instant | 60 | /// Duration between this Instant and another Instant |
| @@ -92,11 +93,14 @@ impl Instant { | |||
| 92 | Instant::now() - *self | 93 | Instant::now() - *self |
| 93 | } | 94 | } |
| 94 | 95 | ||
| 96 | /// Adds one Duration to self, returning a new `Instant` or None in the event of an overflow. | ||
| 95 | pub fn checked_add(&self, duration: Duration) -> Option<Instant> { | 97 | pub fn checked_add(&self, duration: Duration) -> Option<Instant> { |
| 96 | self.ticks | 98 | self.ticks |
| 97 | .checked_add(duration.ticks) | 99 | .checked_add(duration.ticks) |
| 98 | .map(|ticks| Instant { ticks }) | 100 | .map(|ticks| Instant { ticks }) |
| 99 | } | 101 | } |
| 102 | |||
| 103 | /// Subtracts one Duration to self, returning a new `Instant` or None in the event of an overflow. | ||
| 100 | pub fn checked_sub(&self, duration: Duration) -> Option<Instant> { | 104 | pub fn checked_sub(&self, duration: Duration) -> Option<Instant> { |
| 101 | self.ticks | 105 | self.ticks |
| 102 | .checked_sub(duration.ticks) | 106 | .checked_sub(duration.ticks) |
diff --git a/embassy/src/time/mod.rs b/embassy/src/time/mod.rs index f5ad1b3ff..e50742040 100644 --- a/embassy/src/time/mod.rs +++ b/embassy/src/time/mod.rs | |||
| @@ -1,4 +1,44 @@ | |||
| 1 | //! Time abstractions | 1 | //! Timekeeping, delays and timeouts. |
| 2 | //! | ||
| 3 | //! Timekeeping is done with elapsed time since system boot. Time is represented in | ||
| 4 | //! ticks, where the tick rate is defined by the current driver, usually to match | ||
| 5 | //! the tick rate of the hardware. | ||
| 6 | //! | ||
| 7 | //! Tick counts are 64 bits. At the highest supported tick rate of 1Mhz this supports | ||
| 8 | //! representing time spans of up to ~584558 years, which is big enough for all practical | ||
| 9 | //! purposes and allows not having to worry about overflows. | ||
| 10 | //! | ||
| 11 | //! [`Instant`] represents a given instant of time (relative to system boot), and [`Duration`] | ||
| 12 | //! represents the duration of a span of time. They implement the math operations you'd expect, | ||
| 13 | //! like addition and substraction. | ||
| 14 | //! | ||
| 15 | //! # Delays and timeouts | ||
| 16 | //! | ||
| 17 | //! [`Timer`] allows performing async delays. [`Ticker`] allows periodic delays without drifting over time. | ||
| 18 | //! | ||
| 19 | //! An implementation of the `embedded-hal` delay traits is provided by [`Delay`], for compatibility | ||
| 20 | //! with libraries from the ecosystem. | ||
| 21 | //! | ||
| 22 | //! # Wall-clock time | ||
| 23 | //! | ||
| 24 | //! The `time` module deals exclusively with a monotonically increasing tick count. | ||
| 25 | //! Therefore it has no direct support for wall-clock time ("real life" datetimes | ||
| 26 | //! like `2021-08-24 13:33:21`). | ||
| 27 | //! | ||
| 28 | //! If persistence across reboots is not needed, support can be built on top of | ||
| 29 | //! `embassy::time` by storing the offset between "seconds elapsed since boot" | ||
| 30 | //! and "seconds since unix epoch". | ||
| 31 | //! | ||
| 32 | //! # Time driver | ||
| 33 | //! | ||
| 34 | //! The `time` module is backed by a global "time driver" specified at build time. | ||
| 35 | //! Only one driver can be active in a program. | ||
| 36 | //! | ||
| 37 | //! All methods and structs transparently call into the active driver. This makes it | ||
| 38 | //! possible for libraries to use `embassy::time` in a driver-agnostic way without | ||
| 39 | //! requiring generic parameters. | ||
| 40 | //! | ||
| 41 | //! For more details, check the [`driver`] module. | ||
| 2 | 42 | ||
| 3 | mod delay; | 43 | mod delay; |
| 4 | pub mod driver; | 44 | pub mod driver; |
| @@ -6,16 +46,27 @@ mod duration; | |||
| 6 | mod instant; | 46 | mod instant; |
| 7 | mod timer; | 47 | mod timer; |
| 8 | 48 | ||
| 49 | #[cfg(feature = "std")] | ||
| 50 | mod driver_std; | ||
| 51 | |||
| 9 | pub use delay::{block_for, Delay}; | 52 | pub use delay::{block_for, Delay}; |
| 10 | pub use duration::Duration; | 53 | pub use duration::Duration; |
| 11 | pub use instant::Instant; | 54 | pub use instant::Instant; |
| 12 | pub use timer::{with_timeout, Ticker, TimeoutError, Timer}; | 55 | pub use timer::{with_timeout, Ticker, TimeoutError, Timer}; |
| 13 | 56 | ||
| 14 | #[cfg(feature = "time-tick-1000hz")] | 57 | #[cfg(feature = "time-tick-1000hz")] |
| 15 | pub const TICKS_PER_SECOND: u64 = 1_000; | 58 | const TPS: u64 = 1_000; |
| 16 | 59 | ||
| 17 | #[cfg(feature = "time-tick-32768hz")] | 60 | #[cfg(feature = "time-tick-32768hz")] |
| 18 | pub const TICKS_PER_SECOND: u64 = 32_768; | 61 | const TPS: u64 = 32_768; |
| 19 | 62 | ||
| 20 | #[cfg(feature = "time-tick-1mhz")] | 63 | #[cfg(feature = "time-tick-1mhz")] |
| 21 | pub const TICKS_PER_SECOND: u64 = 1_000_000; | 64 | const TPS: u64 = 1_000_000; |
| 65 | |||
| 66 | /// Ticks per second of the global timebase. | ||
| 67 | /// | ||
| 68 | /// This value is specified by the `time-tick-*` Cargo features, which | ||
| 69 | /// should be set by the time driver. Some drivers support a fixed tick rate, others | ||
| 70 | /// allow you to choose a tick rate with Cargo features of their own. You should not | ||
| 71 | /// set the `time-tick-*` features for embassy yourself as an end user. | ||
| 72 | pub const TICKS_PER_SECOND: u64 = TPS; | ||
diff --git a/embassy/src/time/timer.rs b/embassy/src/time/timer.rs index 22f1ffe30..d1ed6a512 100644 --- a/embassy/src/time/timer.rs +++ b/embassy/src/time/timer.rs | |||
| @@ -6,7 +6,13 @@ use futures::{future::select, future::Either, pin_mut, Stream}; | |||
| 6 | use crate::executor::raw; | 6 | use crate::executor::raw; |
| 7 | use crate::time::{Duration, Instant}; | 7 | use crate::time::{Duration, Instant}; |
| 8 | 8 | ||
| 9 | /// Error returned by [`with_timeout`] on timeout. | ||
| 9 | pub struct TimeoutError; | 10 | pub struct TimeoutError; |
| 11 | |||
| 12 | /// Runs a given future with a timeout. | ||
| 13 | /// | ||
| 14 | /// If the future completes before the timeout, its output is returned. Otherwise, on timeout, | ||
| 15 | /// work on the future is stopped (`poll` is no longer called), the future is dropped and `Err(TimeoutError)` is returned. | ||
| 10 | pub async fn with_timeout<F: Future>(timeout: Duration, fut: F) -> Result<F::Output, TimeoutError> { | 16 | pub async fn with_timeout<F: Future>(timeout: Duration, fut: F) -> Result<F::Output, TimeoutError> { |
| 11 | let timeout_fut = Timer::after(timeout); | 17 | let timeout_fut = Timer::after(timeout); |
| 12 | pin_mut!(fut); | 18 | pin_mut!(fut); |
diff --git a/examples/std/Cargo.toml b/examples/std/Cargo.toml index 466814850..34b8ebb67 100644 --- a/examples/std/Cargo.toml +++ b/examples/std/Cargo.toml | |||
| @@ -5,8 +5,7 @@ name = "embassy-std-examples" | |||
| 5 | version = "0.1.0" | 5 | version = "0.1.0" |
| 6 | 6 | ||
| 7 | [dependencies] | 7 | [dependencies] |
| 8 | embassy = { version = "0.1.0", path = "../../embassy", features = ["log"] } | 8 | embassy = { version = "0.1.0", path = "../../embassy", features = ["log", "std", "time"] } |
| 9 | embassy-std = { version = "0.1.0", path = "../../embassy-std" } | ||
| 10 | embassy-net = { version = "0.1.0", path = "../../embassy-net", features=["std", "log", "medium-ethernet", "tcp", "dhcpv4"] } | 9 | embassy-net = { version = "0.1.0", path = "../../embassy-net", features=["std", "log", "medium-ethernet", "tcp", "dhcpv4"] } |
| 11 | smoltcp = { git = "https://github.com/smoltcp-rs/smoltcp", rev="e4241510337e095b9d21136c5f58b2eaa1b78479", default-features = false } | 10 | smoltcp = { git = "https://github.com/smoltcp-rs/smoltcp", rev="e4241510337e095b9d21136c5f58b2eaa1b78479", default-features = false } |
| 12 | 11 | ||
diff --git a/examples/std/src/bin/net.rs b/examples/std/src/bin/net.rs index 323d711a8..b98b97090 100644 --- a/examples/std/src/bin/net.rs +++ b/examples/std/src/bin/net.rs | |||
| @@ -2,11 +2,10 @@ | |||
| 2 | #![allow(incomplete_features)] | 2 | #![allow(incomplete_features)] |
| 3 | 3 | ||
| 4 | use clap::{AppSettings, Clap}; | 4 | use clap::{AppSettings, Clap}; |
| 5 | use embassy::executor::Spawner; | 5 | use embassy::executor::{Executor, Spawner}; |
| 6 | use embassy::io::AsyncWriteExt; | 6 | use embassy::io::AsyncWriteExt; |
| 7 | use embassy::util::Forever; | 7 | use embassy::util::Forever; |
| 8 | use embassy_net::*; | 8 | use embassy_net::*; |
| 9 | use embassy_std::Executor; | ||
| 10 | use heapless::Vec; | 9 | use heapless::Vec; |
| 11 | use log::*; | 10 | use log::*; |
| 12 | 11 | ||
diff --git a/examples/std/src/bin/serial.rs b/examples/std/src/bin/serial.rs index ca596a34c..181c5dfaa 100644 --- a/examples/std/src/bin/serial.rs +++ b/examples/std/src/bin/serial.rs | |||
| @@ -5,9 +5,9 @@ | |||
| 5 | mod serial_port; | 5 | mod serial_port; |
| 6 | 6 | ||
| 7 | use async_io::Async; | 7 | use async_io::Async; |
| 8 | use embassy::executor::Executor; | ||
| 8 | use embassy::io::AsyncBufReadExt; | 9 | use embassy::io::AsyncBufReadExt; |
| 9 | use embassy::util::Forever; | 10 | use embassy::util::Forever; |
| 10 | use embassy_std::Executor; | ||
| 11 | use log::*; | 11 | use log::*; |
| 12 | use nix::sys::termios; | 12 | use nix::sys::termios; |
| 13 | 13 | ||
diff --git a/examples/std/src/bin/tick.rs b/examples/std/src/bin/tick.rs index 16f54b2c6..385b317d4 100644 --- a/examples/std/src/bin/tick.rs +++ b/examples/std/src/bin/tick.rs | |||
| @@ -1,9 +1,9 @@ | |||
| 1 | #![feature(type_alias_impl_trait)] | 1 | #![feature(type_alias_impl_trait)] |
| 2 | #![allow(incomplete_features)] | 2 | #![allow(incomplete_features)] |
| 3 | 3 | ||
| 4 | use embassy::executor::Executor; | ||
| 4 | use embassy::time::{Duration, Timer}; | 5 | use embassy::time::{Duration, Timer}; |
| 5 | use embassy::util::Forever; | 6 | use embassy::util::Forever; |
| 6 | use embassy_std::Executor; | ||
| 7 | use log::*; | 7 | use log::*; |
| 8 | 8 | ||
| 9 | #[embassy::task] | 9 | #[embassy::task] |
