summaryrefslogtreecommitdiff
path: root/embassy-time
diff options
context:
space:
mode:
authorDario Nieuwenhuis <dirbaio@dirbaio.net>2022-08-17 23:40:16 +0200
committerDario Nieuwenhuis <dirbaio@dirbaio.net>2022-08-18 01:22:30 +0200
commit5daa173ce4b153a532b4daa9e94c7a248231f25b (patch)
tree2ef0b4d6f9b1c02dac2589e7b57982c20cbc0e66 /embassy-time
parent1c5b54a4823d596db730eb476c3ab78110557214 (diff)
downloadembassy-5daa173ce4b153a532b4daa9e94c7a248231f25b.zip
Split embassy-time from embassy-executor.
Diffstat (limited to 'embassy-time')
-rw-r--r--embassy-time/Cargo.toml54
-rw-r--r--embassy-time/src/delay.rs98
-rw-r--r--embassy-time/src/driver.rs174
-rw-r--r--embassy-time/src/driver_std.rs208
-rw-r--r--embassy-time/src/driver_wasm.rs134
-rw-r--r--embassy-time/src/duration.rs184
-rw-r--r--embassy-time/src/fmt.rs225
-rw-r--r--embassy-time/src/instant.rs159
-rw-r--r--embassy-time/src/lib.rs99
-rw-r--r--embassy-time/src/timer.rs158
10 files changed, 1493 insertions, 0 deletions
diff --git a/embassy-time/Cargo.toml b/embassy-time/Cargo.toml
new file mode 100644
index 00000000..161c101f
--- /dev/null
+++ b/embassy-time/Cargo.toml
@@ -0,0 +1,54 @@
+[package]
+name = "embassy-time"
+version = "0.1.0"
+edition = "2021"
+
+
+[package.metadata.embassy_docs]
+src_base = "https://github.com/embassy-rs/embassy/blob/embassy-time-v$VERSION/embassy-time/src/"
+src_base_git = "https://github.com/embassy-rs/embassy/blob/$COMMIT/embassy-time/src/"
+features = ["nightly", "defmt", "unstable-traits", "std"]
+target = "x86_64-unknown-linux-gnu"
+
+[features]
+std = ["tick-1mhz"]
+wasm = ["dep:wasm-bindgen", "dep:js-sys", "dep:wasm-timer", "tick-1mhz"]
+
+# Enable nightly-only features
+nightly = ["embedded-hal-async"]
+
+# Implement embedded-hal 1.0 alpha and embedded-hal-async traits.
+# Implement embedded-hal-async traits if `nightly` is set as well.
+unstable-traits = ["embedded-hal-1"]
+
+# Display a timestamp of the number of seconds since startup next to defmt log messages
+# To use this you must have a time driver provided.
+defmt-timestamp-uptime = ["defmt"]
+
+# Set the `embassy_time` tick rate.
+# NOTE: This feature is only intended to be enabled by crates providing the time driver implementation.
+# If you're not writing your own driver, check the driver documentation to customize the tick rate.
+# If you're writing a driver and your tick rate is not listed here, please add it and send a PR!
+tick-32768hz = []
+tick-1000hz = []
+tick-1mhz = []
+tick-16mhz = []
+
+[dependencies]
+defmt = { version = "0.3", optional = true }
+log = { version = "0.4.14", optional = true }
+
+embedded-hal-02 = { package = "embedded-hal", version = "0.2.6" }
+embedded-hal-1 = { package = "embedded-hal", version = "1.0.0-alpha.8", optional = true}
+embedded-hal-async = { version = "0.1.0-alpha.1", optional = true}
+
+futures-util = { version = "0.3.17", default-features = false }
+embassy-macros = { version = "0.1.0", path = "../embassy-macros"}
+atomic-polyfill = "1.0.1"
+critical-section = "1.1"
+cfg-if = "1.0.0"
+
+# WASM dependencies
+wasm-bindgen = { version = "0.2.76", features = ["nightly"], optional = true }
+js-sys = { version = "0.3", optional = true }
+wasm-timer = { version = "0.2.5", optional = true } \ No newline at end of file
diff --git a/embassy-time/src/delay.rs b/embassy-time/src/delay.rs
new file mode 100644
index 00000000..d010fff9
--- /dev/null
+++ b/embassy-time/src/delay.rs
@@ -0,0 +1,98 @@
+use super::{Duration, Instant};
+
+/// Blocks for at least `duration`.
+pub fn block_for(duration: Duration) {
+ let expires_at = Instant::now() + duration;
+ while Instant::now() < expires_at {}
+}
+
+/// Type implementing async delays and blocking `embedded-hal` delays.
+///
+/// The delays are implemented in a "best-effort" way, meaning that the cpu will block for at least
+/// the amount provided, but accuracy can be affected by many factors, including interrupt usage.
+/// Make sure to use a suitable tick rate for your use case. The tick rate is defined by the currently
+/// active driver.
+pub struct Delay;
+
+#[cfg(feature = "unstable-traits")]
+mod eh1 {
+ use super::*;
+
+ impl embedded_hal_1::delay::blocking::DelayUs for Delay {
+ type Error = core::convert::Infallible;
+
+ fn delay_us(&mut self, us: u32) -> Result<(), Self::Error> {
+ Ok(block_for(Duration::from_micros(us as u64)))
+ }
+
+ fn delay_ms(&mut self, ms: u32) -> Result<(), Self::Error> {
+ Ok(block_for(Duration::from_millis(ms as u64)))
+ }
+ }
+}
+
+cfg_if::cfg_if! {
+ if #[cfg(all(feature = "unstable-traits", feature = "nightly"))] {
+ use crate::Timer;
+ use core::future::Future;
+ use futures_util::FutureExt;
+
+ impl embedded_hal_async::delay::DelayUs for Delay {
+ type Error = core::convert::Infallible;
+
+ type DelayUsFuture<'a> = impl Future<Output = Result<(), Self::Error>> + 'a where Self: 'a;
+
+ fn delay_us(&mut self, micros: u32) -> Self::DelayUsFuture<'_> {
+ Timer::after(Duration::from_micros(micros as _)).map(Ok)
+ }
+
+ type DelayMsFuture<'a> = impl Future<Output = Result<(), Self::Error>> + 'a where Self: 'a;
+
+ fn delay_ms(&mut self, millis: u32) -> Self::DelayMsFuture<'_> {
+ Timer::after(Duration::from_millis(millis as _)).map(Ok)
+ }
+ }
+ }
+}
+
+mod eh02 {
+ use embedded_hal_02::blocking::delay::{DelayMs, DelayUs};
+
+ use super::*;
+
+ impl DelayMs<u8> for Delay {
+ fn delay_ms(&mut self, ms: u8) {
+ block_for(Duration::from_millis(ms as u64))
+ }
+ }
+
+ impl DelayMs<u16> for Delay {
+ fn delay_ms(&mut self, ms: u16) {
+ block_for(Duration::from_millis(ms as u64))
+ }
+ }
+
+ impl DelayMs<u32> for Delay {
+ fn delay_ms(&mut self, ms: u32) {
+ block_for(Duration::from_millis(ms as u64))
+ }
+ }
+
+ impl DelayUs<u8> for Delay {
+ fn delay_us(&mut self, us: u8) {
+ block_for(Duration::from_micros(us as u64))
+ }
+ }
+
+ impl DelayUs<u16> for Delay {
+ fn delay_us(&mut self, us: u16) {
+ block_for(Duration::from_micros(us as u64))
+ }
+ }
+
+ impl DelayUs<u32> for Delay {
+ fn delay_us(&mut self, us: u32) {
+ block_for(Duration::from_micros(us as u64))
+ }
+ }
+}
diff --git a/embassy-time/src/driver.rs b/embassy-time/src/driver.rs
new file mode 100644
index 00000000..216b2740
--- /dev/null
+++ b/embassy-time/src/driver.rs
@@ -0,0 +1,174 @@
+//! Time driver interface
+//!
+//! This module defines the interface a driver needs to implement to power the `embassy_time` module.
+//!
+//! # Implementing a driver
+//!
+//! - Define a struct `MyDriver`
+//! - Implement [`Driver`] for it
+//! - Register it as the global driver with [`time_driver_impl`].
+//! - Enable the Cargo features `embassy-executor/time` and one of `embassy-time/tick-*` corresponding to the
+//! tick rate of your driver.
+//!
+//! If you wish to make the tick rate configurable by the end user, you should do so by exposing your own
+//! Cargo features and having each enable the corresponding `embassy-time/tick-*`.
+//!
+//! # Linkage details
+//!
+//! Instead of the usual "trait + generic params" approach, calls from embassy to the driver are done via `extern` functions.
+//!
+//! `embassy` internally defines the driver functions as `extern "Rust" { fn _embassy_time_now() -> u64; }` and calls them.
+//! The driver crate defines the functions as `#[no_mangle] fn _embassy_time_now() -> u64`. The linker will resolve the
+//! calls from the `embassy` crate to call into the driver crate.
+//!
+//! If there is none or multiple drivers in the crate tree, linking will fail.
+//!
+//! This method has a few key advantages for something as foundational as timekeeping:
+//!
+//! - The time driver is available everywhere easily, without having to thread the implementation
+//! through generic parameters. This is especially helpful for libraries.
+//! - It means comparing `Instant`s will always make sense: if there were multiple drivers
+//! active, one could compare an `Instant` from driver A to an `Instant` from driver B, which
+//! would yield incorrect results.
+//!
+//! # Example
+//!
+//! ```
+//! use embassy_time::driver::{Driver, AlarmHandle};
+//!
+//! struct MyDriver{}; // not public!
+//! embassy_time::time_driver_impl!(static DRIVER: MyDriver = MyDriver{});
+//!
+//! impl Driver for MyDriver {
+//! fn now(&self) -> u64 {
+//! todo!()
+//! }
+//! unsafe fn allocate_alarm(&self) -> Option<AlarmHandle> {
+//! todo!()
+//! }
+//! fn set_alarm_callback(&self, alarm: AlarmHandle, callback: fn(*mut ()), ctx: *mut ()) {
+//! todo!()
+//! }
+//! fn set_alarm(&self, alarm: AlarmHandle, timestamp: u64) {
+//! todo!()
+//! }
+//! }
+//! ```
+
+/// Alarm handle, assigned by the driver.
+#[derive(Clone, Copy)]
+pub struct AlarmHandle {
+ id: u8,
+}
+
+impl AlarmHandle {
+ /// Create an AlarmHandle
+ ///
+ /// Safety: May only be called by the current global Driver impl.
+ /// The impl is allowed to rely on the fact that all `AlarmHandle` instances
+ /// are created by itself in unsafe code (e.g. indexing operations)
+ pub unsafe fn new(id: u8) -> Self {
+ Self { id }
+ }
+
+ /// Get the ID of the AlarmHandle.
+ pub fn id(&self) -> u8 {
+ self.id
+ }
+}
+
+/// Time driver
+pub trait Driver: Send + Sync + 'static {
+ /// Return the current timestamp in ticks.
+ ///
+ /// Implementations MUST ensure that:
+ /// - This is guaranteed to be monotonic, i.e. a call to now() will always return
+ /// a greater or equal value than earler calls. Time can't "roll backwards".
+ /// - It "never" overflows. It must not overflow in a sufficiently long time frame, say
+ /// in 10_000 years (Human civilization is likely to already have self-destructed
+ /// 10_000 years from now.). This means if your hardware only has 16bit/32bit timers
+ /// you MUST extend them to 64-bit, for example by counting overflows in software,
+ /// or chaining multiple timers together.
+ fn now(&self) -> u64;
+
+ /// Try allocating an alarm handle. Returns None if no alarms left.
+ /// Initially the alarm has no callback set, and a null `ctx` pointer.
+ ///
+ /// # Safety
+ /// It is UB to make the alarm fire before setting a callback.
+ unsafe fn allocate_alarm(&self) -> Option<AlarmHandle>;
+
+ /// Sets the callback function to be called when the alarm triggers.
+ /// The callback may be called from any context (interrupt or thread mode).
+ fn set_alarm_callback(&self, alarm: AlarmHandle, callback: fn(*mut ()), ctx: *mut ());
+
+ /// Sets an alarm at the given timestamp. When the current timestamp reaches the alarm
+ /// timestamp, the provided callback function will be called.
+ ///
+ /// If `timestamp` is already in the past, the alarm callback must be immediately fired.
+ /// In this case, it is allowed (but not mandatory) to call the alarm callback synchronously from `set_alarm`.
+ ///
+ /// When callback is called, it is guaranteed that now() will return a value greater or equal than timestamp.
+ ///
+ /// Only one alarm can be active at a time for each AlarmHandle. This overwrites any previously-set alarm if any.
+ fn set_alarm(&self, alarm: AlarmHandle, timestamp: u64);
+}
+
+extern "Rust" {
+ fn _embassy_time_now() -> u64;
+ fn _embassy_time_allocate_alarm() -> Option<AlarmHandle>;
+ fn _embassy_time_set_alarm_callback(alarm: AlarmHandle, callback: fn(*mut ()), ctx: *mut ());
+ fn _embassy_time_set_alarm(alarm: AlarmHandle, timestamp: u64);
+}
+
+/// See [`Driver::now`]
+pub fn now() -> u64 {
+ unsafe { _embassy_time_now() }
+}
+
+/// See [`Driver::allocate_alarm`]
+///
+/// Safety: it is UB to make the alarm fire before setting a callback.
+pub unsafe fn allocate_alarm() -> Option<AlarmHandle> {
+ _embassy_time_allocate_alarm()
+}
+
+/// See [`Driver::set_alarm_callback`]
+pub fn set_alarm_callback(alarm: AlarmHandle, callback: fn(*mut ()), ctx: *mut ()) {
+ unsafe { _embassy_time_set_alarm_callback(alarm, callback, ctx) }
+}
+
+/// See [`Driver::set_alarm`]
+pub fn set_alarm(alarm: AlarmHandle, timestamp: u64) {
+ unsafe { _embassy_time_set_alarm(alarm, timestamp) }
+}
+
+/// Set the time Driver implementation.
+///
+/// See the module documentation for an example.
+#[macro_export]
+macro_rules! time_driver_impl {
+ (static $name:ident: $t: ty = $val:expr) => {
+ static $name: $t = $val;
+
+ #[no_mangle]
+ fn _embassy_time_now() -> u64 {
+ <$t as $crate::driver::Driver>::now(&$name)
+ }
+
+ #[no_mangle]
+ unsafe fn _embassy_time_allocate_alarm() -> Option<$crate::driver::AlarmHandle> {
+ <$t as $crate::driver::Driver>::allocate_alarm(&$name)
+ }
+
+ #[no_mangle]
+ fn _embassy_time_set_alarm_callback(alarm: $crate::driver::AlarmHandle, callback: fn(*mut ()), ctx: *mut ()) {
+ <$t as $crate::driver::Driver>::set_alarm_callback(&$name, alarm, callback, ctx)
+ }
+
+ #[no_mangle]
+ fn _embassy_time_set_alarm(alarm: $crate::driver::AlarmHandle, timestamp: u64) {
+ <$t as $crate::driver::Driver>::set_alarm(&$name, alarm, timestamp)
+ }
+ };
+}
diff --git a/embassy-time/src/driver_std.rs b/embassy-time/src/driver_std.rs
new file mode 100644
index 00000000..2ddb2e60
--- /dev/null
+++ b/embassy-time/src/driver_std.rs
@@ -0,0 +1,208 @@
+use std::cell::UnsafeCell;
+use std::mem::MaybeUninit;
+use std::sync::{Condvar, Mutex, Once};
+use std::time::{Duration as StdDuration, Instant as StdInstant};
+use std::{mem, ptr, thread};
+
+use atomic_polyfill::{AtomicU8, Ordering};
+
+use crate::driver::{AlarmHandle, Driver};
+
+const ALARM_COUNT: usize = 4;
+
+struct AlarmState {
+ timestamp: u64,
+
+ // This is really a Option<(fn(*mut ()), *mut ())>
+ // but fn pointers aren't allowed in const yet
+ callback: *const (),
+ ctx: *mut (),
+}
+
+unsafe impl Send for AlarmState {}
+
+impl AlarmState {
+ const fn new() -> Self {
+ Self {
+ timestamp: u64::MAX,
+ callback: ptr::null(),
+ ctx: ptr::null_mut(),
+ }
+ }
+}
+
+struct TimeDriver {
+ alarm_count: AtomicU8,
+
+ once: Once,
+ alarms: UninitCell<Mutex<[AlarmState; ALARM_COUNT]>>,
+ zero_instant: UninitCell<StdInstant>,
+ signaler: UninitCell<Signaler>,
+}
+
+const ALARM_NEW: AlarmState = AlarmState::new();
+crate::time_driver_impl!(static DRIVER: TimeDriver = TimeDriver {
+ alarm_count: AtomicU8::new(0),
+
+ once: Once::new(),
+ alarms: UninitCell::uninit(),
+ zero_instant: UninitCell::uninit(),
+ signaler: UninitCell::uninit(),
+});
+
+impl TimeDriver {
+ fn init(&self) {
+ self.once.call_once(|| unsafe {
+ self.alarms.write(Mutex::new([ALARM_NEW; ALARM_COUNT]));
+ self.zero_instant.write(StdInstant::now());
+ self.signaler.write(Signaler::new());
+
+ thread::spawn(Self::alarm_thread);
+ });
+ }
+
+ fn alarm_thread() {
+ let zero = unsafe { DRIVER.zero_instant.read() };
+ loop {
+ let now = DRIVER.now();
+
+ let mut next_alarm = u64::MAX;
+ {
+ let alarms = &mut *unsafe { DRIVER.alarms.as_ref() }.lock().unwrap();
+ for alarm in alarms {
+ if alarm.timestamp <= now {
+ alarm.timestamp = u64::MAX;
+
+ // Call after clearing alarm, so the callback can set another alarm.
+
+ // safety:
+ // - we can ignore the possiblity of `f` being unset (null) because of the safety contract of `allocate_alarm`.
+ // - other than that we only store valid function pointers into alarm.callback
+ let f: fn(*mut ()) = unsafe { mem::transmute(alarm.callback) };
+ f(alarm.ctx);
+ } else {
+ next_alarm = next_alarm.min(alarm.timestamp);
+ }
+ }
+ }
+
+ // Ensure we don't overflow
+ let until = zero
+ .checked_add(StdDuration::from_micros(next_alarm))
+ .unwrap_or_else(|| StdInstant::now() + StdDuration::from_secs(1));
+
+ unsafe { DRIVER.signaler.as_ref() }.wait_until(until);
+ }
+ }
+}
+
+impl Driver for TimeDriver {
+ fn now(&self) -> u64 {
+ self.init();
+
+ let zero = unsafe { self.zero_instant.read() };
+ StdInstant::now().duration_since(zero).as_micros() as u64
+ }
+
+ unsafe fn allocate_alarm(&self) -> Option<AlarmHandle> {
+ let id = self.alarm_count.fetch_update(Ordering::AcqRel, Ordering::Acquire, |x| {
+ if x < ALARM_COUNT as u8 {
+ Some(x + 1)
+ } else {
+ None
+ }
+ });
+
+ match id {
+ Ok(id) => Some(AlarmHandle::new(id)),
+ Err(_) => None,
+ }
+ }
+
+ fn set_alarm_callback(&self, alarm: AlarmHandle, callback: fn(*mut ()), ctx: *mut ()) {
+ self.init();
+ let mut alarms = unsafe { self.alarms.as_ref() }.lock().unwrap();
+ let alarm = &mut alarms[alarm.id() as usize];
+ alarm.callback = callback as *const ();
+ alarm.ctx = ctx;
+ }
+
+ fn set_alarm(&self, alarm: AlarmHandle, timestamp: u64) {
+ self.init();
+ let mut alarms = unsafe { self.alarms.as_ref() }.lock().unwrap();
+ let alarm = &mut alarms[alarm.id() as usize];
+ alarm.timestamp = timestamp;
+ unsafe { self.signaler.as_ref() }.signal();
+ }
+}
+
+struct Signaler {
+ mutex: Mutex<bool>,
+ condvar: Condvar,
+}
+
+impl Signaler {
+ fn new() -> Self {
+ Self {
+ mutex: Mutex::new(false),
+ condvar: Condvar::new(),
+ }
+ }
+
+ fn wait_until(&self, until: StdInstant) {
+ let mut signaled = self.mutex.lock().unwrap();
+ while !*signaled {
+ let now = StdInstant::now();
+
+ if now >= until {
+ break;
+ }
+
+ let dur = until - now;
+ let (signaled2, timeout) = self.condvar.wait_timeout(signaled, dur).unwrap();
+ signaled = signaled2;
+ if timeout.timed_out() {
+ break;
+ }
+ }
+ *signaled = false;
+ }
+
+ fn signal(&self) {
+ let mut signaled = self.mutex.lock().unwrap();
+ *signaled = true;
+ self.condvar.notify_one();
+ }
+}
+
+pub(crate) struct UninitCell<T>(MaybeUninit<UnsafeCell<T>>);
+unsafe impl<T> Send for UninitCell<T> {}
+unsafe impl<T> Sync for UninitCell<T> {}
+
+impl<T> UninitCell<T> {
+ pub const fn uninit() -> Self {
+ Self(MaybeUninit::uninit())
+ }
+
+ pub unsafe fn as_ptr(&self) -> *const T {
+ (*self.0.as_ptr()).get()
+ }
+
+ pub unsafe fn as_mut_ptr(&self) -> *mut T {
+ (*self.0.as_ptr()).get()
+ }
+
+ pub unsafe fn as_ref(&self) -> &T {
+ &*self.as_ptr()
+ }
+
+ pub unsafe fn write(&self, val: T) {
+ ptr::write(self.as_mut_ptr(), val)
+ }
+}
+
+impl<T: Copy> UninitCell<T> {
+ pub unsafe fn read(&self) -> T {
+ ptr::read(self.as_mut_ptr())
+ }
+}
diff --git a/embassy-time/src/driver_wasm.rs b/embassy-time/src/driver_wasm.rs
new file mode 100644
index 00000000..e4497e6a
--- /dev/null
+++ b/embassy-time/src/driver_wasm.rs
@@ -0,0 +1,134 @@
+use std::cell::UnsafeCell;
+use std::mem::MaybeUninit;
+use std::ptr;
+use std::sync::{Mutex, Once};
+
+use atomic_polyfill::{AtomicU8, Ordering};
+use wasm_bindgen::prelude::*;
+use wasm_timer::Instant as StdInstant;
+
+use crate::driver::{AlarmHandle, Driver};
+
+const ALARM_COUNT: usize = 4;
+
+struct AlarmState {
+ token: Option<f64>,
+ closure: Option<Closure<dyn FnMut() + 'static>>,
+}
+
+unsafe impl Send for AlarmState {}
+
+impl AlarmState {
+ const fn new() -> Self {
+ Self {
+ token: None,
+ closure: None,
+ }
+ }
+}
+
+#[wasm_bindgen]
+extern "C" {
+ fn setTimeout(closure: &Closure<dyn FnMut()>, millis: u32) -> f64;
+ fn clearTimeout(token: f64);
+}
+
+struct TimeDriver {
+ alarm_count: AtomicU8,
+
+ once: Once,
+ alarms: UninitCell<Mutex<[AlarmState; ALARM_COUNT]>>,
+ zero_instant: UninitCell<StdInstant>,
+}
+
+const ALARM_NEW: AlarmState = AlarmState::new();
+crate::time_driver_impl!(static DRIVER: TimeDriver = TimeDriver {
+ alarm_count: AtomicU8::new(0),
+ once: Once::new(),
+ alarms: UninitCell::uninit(),
+ zero_instant: UninitCell::uninit(),
+});
+
+impl TimeDriver {
+ fn init(&self) {
+ self.once.call_once(|| unsafe {
+ self.alarms.write(Mutex::new([ALARM_NEW; ALARM_COUNT]));
+ self.zero_instant.write(StdInstant::now());
+ });
+ }
+}
+
+impl Driver for TimeDriver {
+ fn now(&self) -> u64 {
+ self.init();
+
+ let zero = unsafe { self.zero_instant.read() };
+ StdInstant::now().duration_since(zero).as_micros() as u64
+ }
+
+ unsafe fn allocate_alarm(&self) -> Option<AlarmHandle> {
+ let id = self.alarm_count.fetch_update(Ordering::AcqRel, Ordering::Acquire, |x| {
+ if x < ALARM_COUNT as u8 {
+ Some(x + 1)
+ } else {
+ None
+ }
+ });
+
+ match id {
+ Ok(id) => Some(AlarmHandle::new(id)),
+ Err(_) => None,
+ }
+ }
+
+ fn set_alarm_callback(&self, alarm: AlarmHandle, callback: fn(*mut ()), ctx: *mut ()) {
+ self.init();
+ let mut alarms = unsafe { self.alarms.as_ref() }.lock().unwrap();
+ let alarm = &mut alarms[alarm.id() as usize];
+ alarm.closure.replace(Closure::new(move || {
+ callback(ctx);
+ }));
+ }
+
+ fn set_alarm(&self, alarm: AlarmHandle, timestamp: u64) {
+ self.init();
+ let mut alarms = unsafe { self.alarms.as_ref() }.lock().unwrap();
+ let alarm = &mut alarms[alarm.id() as usize];
+ let timeout = (timestamp - self.now()) as u32;
+ if let Some(token) = alarm.token {
+ clearTimeout(token);
+ }
+ alarm.token = Some(setTimeout(alarm.closure.as_ref().unwrap(), timeout / 1000));
+ }
+}
+
+pub(crate) struct UninitCell<T>(MaybeUninit<UnsafeCell<T>>);
+unsafe impl<T> Send for UninitCell<T> {}
+unsafe impl<T> Sync for UninitCell<T> {}
+
+impl<T> UninitCell<T> {
+ pub const fn uninit() -> Self {
+ Self(MaybeUninit::uninit())
+ }
+ unsafe fn as_ptr(&self) -> *const T {
+ (*self.0.as_ptr()).get()
+ }
+
+ pub unsafe fn as_mut_ptr(&self) -> *mut T {
+ (*self.0.as_ptr()).get()
+ }
+
+ pub unsafe fn as_ref(&self) -> &T {
+ &*self.as_ptr()
+ }
+
+ pub unsafe fn write(&self, val: T) {
+ ptr::write(self.as_mut_ptr(), val)
+ }
+}
+
+impl<T: Copy> UninitCell<T> {
+ pub unsafe fn read(&self) -> T {
+ ptr::read(self.as_mut_ptr())
+ }
+}
diff --git a/embassy-time/src/duration.rs b/embassy-time/src/duration.rs
new file mode 100644
index 00000000..dc4f16bd
--- /dev/null
+++ b/embassy-time/src/duration.rs
@@ -0,0 +1,184 @@
+use core::fmt;
+use core::ops::{Add, AddAssign, Div, DivAssign, Mul, MulAssign, Sub, SubAssign};
+
+use super::{GCD_1K, GCD_1M, TICKS_PER_SECOND};
+
+#[derive(Debug, Default, Copy, Clone, PartialEq, Eq, PartialOrd, Ord)]
+#[cfg_attr(feature = "defmt", derive(defmt::Format))]
+/// Represents the difference between two [Instant](struct.Instant.html)s
+pub struct Duration {
+ pub(crate) ticks: u64,
+}
+
+impl Duration {
+ /// The smallest value that can be represented by the `Duration` type.
+ pub const MIN: Duration = Duration { ticks: u64::MIN };
+ /// The largest value that can be represented by the `Duration` type.
+ pub const MAX: Duration = Duration { ticks: u64::MAX };
+
+ /// Tick count of the `Duration`.
+ pub const fn as_ticks(&self) -> u64 {
+ self.ticks
+ }
+
+ /// Convert the `Duration` to seconds, rounding down.
+ pub const fn as_secs(&self) -> u64 {
+ self.ticks / TICKS_PER_SECOND
+ }
+
+ /// Convert the `Duration` to milliseconds, rounding down.
+ pub const fn as_millis(&self) -> u64 {
+ self.ticks * (1000 / GCD_1K) / (TICKS_PER_SECOND / GCD_1K)
+ }
+
+ /// Convert the `Duration` to microseconds, rounding down.
+ pub const fn as_micros(&self) -> u64 {
+ self.ticks * (1_000_000 / GCD_1M) / (TICKS_PER_SECOND / GCD_1M)
+ }
+
+ /// Creates a duration from the specified number of clock ticks
+ pub const fn from_ticks(ticks: u64) -> Duration {
+ Duration { ticks }
+ }
+
+ /// Creates a duration from the specified number of seconds, rounding up.
+ pub const fn from_secs(secs: u64) -> Duration {
+ Duration {
+ ticks: secs * TICKS_PER_SECOND,
+ }
+ }
+
+ /// Creates a duration from the specified number of milliseconds, rounding up.
+ pub const fn from_millis(millis: u64) -> Duration {
+ Duration {
+ ticks: div_ceil(millis * (TICKS_PER_SECOND / GCD_1K), 1000 / GCD_1K),
+ }
+ }
+
+ /// Creates a duration from the specified number of microseconds, rounding up.
+ /// NOTE: Delays this small may be inaccurate.
+ pub const fn from_micros(micros: u64) -> Duration {
+ Duration {
+ ticks: div_ceil(micros * (TICKS_PER_SECOND / GCD_1M), 1_000_000 / GCD_1M),
+ }
+ }
+
+ /// Creates a duration from the specified number of seconds, rounding down.
+ pub const fn from_secs_floor(secs: u64) -> Duration {
+ Duration {
+ ticks: secs * TICKS_PER_SECOND,
+ }
+ }
+
+ /// Creates a duration from the specified number of milliseconds, rounding down.
+ pub const fn from_millis_floor(millis: u64) -> Duration {
+ Duration {
+ ticks: millis * (TICKS_PER_SECOND / GCD_1K) / (1000 / GCD_1K),
+ }
+ }
+
+ /// Creates a duration from the specified number of microseconds, rounding down.
+ /// NOTE: Delays this small may be inaccurate.
+ pub const fn from_micros_floor(micros: u64) -> Duration {
+ Duration {
+ ticks: micros * (TICKS_PER_SECOND / GCD_1M) / (1_000_000 / GCD_1M),
+ }
+ }
+
+ /// Adds one Duration to another, returning a new Duration or None in the event of an overflow.
+ pub fn checked_add(self, rhs: Duration) -> Option<Duration> {
+ self.ticks.checked_add(rhs.ticks).map(|ticks| Duration { ticks })
+ }
+
+ /// Subtracts one Duration to another, returning a new Duration or None in the event of an overflow.
+ pub fn checked_sub(self, rhs: Duration) -> Option<Duration> {
+ self.ticks.checked_sub(rhs.ticks).map(|ticks| Duration { ticks })
+ }
+
+ /// Multiplies one Duration by a scalar u32, returning a new Duration or None in the event of an overflow.
+ pub fn checked_mul(self, rhs: u32) -> Option<Duration> {
+ self.ticks.checked_mul(rhs as _).map(|ticks| Duration { ticks })
+ }
+
+ /// Divides one Duration a scalar u32, returning a new Duration or None in the event of an overflow.
+ pub fn checked_div(self, rhs: u32) -> Option<Duration> {
+ self.ticks.checked_div(rhs as _).map(|ticks| Duration { ticks })
+ }
+}
+
+impl Add for Duration {
+ type Output = Duration;
+
+ fn add(self, rhs: Duration) -> Duration {
+ self.checked_add(rhs).expect("overflow when adding durations")
+ }
+}
+
+impl AddAssign for Duration {
+ fn add_assign(&mut self, rhs: Duration) {
+ *self = *self + rhs;
+ }
+}
+
+impl Sub for Duration {
+ type Output = Duration;
+
+ fn sub(self, rhs: Duration) -> Duration {
+ self.checked_sub(rhs).expect("overflow when subtracting durations")
+ }
+}
+
+impl SubAssign for Duration {
+ fn sub_assign(&mut self, rhs: Duration) {
+ *self = *self - rhs;
+ }
+}
+
+impl Mul<u32> for Duration {
+ type Output = Duration;
+
+ fn mul(self, rhs: u32) -> Duration {
+ self.checked_mul(rhs)
+ .expect("overflow when multiplying duration by scalar")
+ }
+}
+
+impl Mul<Duration> for u32 {
+ type Output = Duration;
+
+ fn mul(self, rhs: Duration) -> Duration {
+ rhs * self
+ }
+}
+
+impl MulAssign<u32> for Duration {
+ fn mul_assign(&mut self, rhs: u32) {
+ *self = *self * rhs;
+ }
+}
+
+impl Div<u32> for Duration {
+ type Output = Duration;
+
+ fn div(self, rhs: u32) -> Duration {
+ self.checked_div(rhs)
+ .expect("divide by zero error when dividing duration by scalar")
+ }
+}
+
+impl DivAssign<u32> for Duration {
+ fn div_assign(&mut self, rhs: u32) {
+ *self = *self / rhs;
+ }
+}
+
+impl<'a> fmt::Display for Duration {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ write!(f, "{} ticks", self.ticks)
+ }
+}
+
+#[inline]
+const fn div_ceil(num: u64, den: u64) -> u64 {
+ (num + den - 1) / den
+}
diff --git a/embassy-time/src/fmt.rs b/embassy-time/src/fmt.rs
new file mode 100644
index 00000000..06697081
--- /dev/null
+++ b/embassy-time/src/fmt.rs
@@ -0,0 +1,225 @@
+#![macro_use]
+#![allow(unused_macros)]
+
+#[cfg(all(feature = "defmt", feature = "log"))]
+compile_error!("You may not enable both `defmt` and `log` features.");
+
+macro_rules! assert {
+ ($($x:tt)*) => {
+ {
+ #[cfg(not(feature = "defmt"))]
+ ::core::assert!($($x)*);
+ #[cfg(feature = "defmt")]
+ ::defmt::assert!($($x)*);
+ }
+ };
+}
+
+macro_rules! assert_eq {
+ ($($x:tt)*) => {
+ {
+ #[cfg(not(feature = "defmt"))]
+ ::core::assert_eq!($($x)*);
+ #[cfg(feature = "defmt")]
+ ::defmt::assert_eq!($($x)*);
+ }
+ };
+}
+
+macro_rules! assert_ne {
+ ($($x:tt)*) => {
+ {
+ #[cfg(not(feature = "defmt"))]
+ ::core::assert_ne!($($x)*);
+ #[cfg(feature = "defmt")]
+ ::defmt::assert_ne!($($x)*);
+ }
+ };
+}
+
+macro_rules! debug_assert {
+ ($($x:tt)*) => {
+ {
+ #[cfg(not(feature = "defmt"))]
+ ::core::debug_assert!($($x)*);
+ #[cfg(feature = "defmt")]
+ ::defmt::debug_assert!($($x)*);
+ }
+ };
+}
+
+macro_rules! debug_assert_eq {
+ ($($x:tt)*) => {
+ {
+ #[cfg(not(feature = "defmt"))]
+ ::core::debug_assert_eq!($($x)*);
+ #[cfg(feature = "defmt")]
+ ::defmt::debug_assert_eq!($($x)*);
+ }
+ };
+}
+
+macro_rules! debug_assert_ne {
+ ($($x:tt)*) => {
+ {
+ #[cfg(not(feature = "defmt"))]
+ ::core::debug_assert_ne!($($x)*);
+ #[cfg(feature = "defmt")]
+ ::defmt::debug_assert_ne!($($x)*);
+ }
+ };
+}
+
+macro_rules! todo {
+ ($($x:tt)*) => {
+ {
+ #[cfg(not(feature = "defmt"))]
+ ::core::todo!($($x)*);
+ #[cfg(feature = "defmt")]
+ ::defmt::todo!($($x)*);
+ }
+ };
+}
+
+macro_rules! unreachable {
+ ($($x:tt)*) => {
+ {
+ #[cfg(not(feature = "defmt"))]
+ ::core::unreachable!($($x)*);
+ #[cfg(feature = "defmt")]
+ ::defmt::unreachable!($($x)*);
+ }
+ };
+}
+
+macro_rules! panic {
+ ($($x:tt)*) => {
+ {
+ #[cfg(not(feature = "defmt"))]
+ ::core::panic!($($x)*);
+ #[cfg(feature = "defmt")]
+ ::defmt::panic!($($x)*);
+ }
+ };
+}
+
+macro_rules! trace {
+ ($s:literal $(, $x:expr)* $(,)?) => {
+ {
+ #[cfg(feature = "log")]
+ ::log::trace!($s $(, $x)*);
+ #[cfg(feature = "defmt")]
+ ::defmt::trace!($s $(, $x)*);
+ #[cfg(not(any(feature = "log", feature="defmt")))]
+ let _ = ($( & $x ),*);
+ }
+ };
+}
+
+macro_rules! debug {
+ ($s:literal $(, $x:expr)* $(,)?) => {
+ {
+ #[cfg(feature = "log")]
+ ::log::debug!($s $(, $x)*);
+ #[cfg(feature = "defmt")]
+ ::defmt::debug!($s $(, $x)*);
+ #[cfg(not(any(feature = "log", feature="defmt")))]
+ let _ = ($( & $x ),*);
+ }
+ };
+}
+
+macro_rules! info {
+ ($s:literal $(, $x:expr)* $(,)?) => {
+ {
+ #[cfg(feature = "log")]
+ ::log::info!($s $(, $x)*);
+ #[cfg(feature = "defmt")]
+ ::defmt::info!($s $(, $x)*);
+ #[cfg(not(any(feature = "log", feature="defmt")))]
+ let _ = ($( & $x ),*);
+ }
+ };
+}
+
+macro_rules! warn {
+ ($s:literal $(, $x:expr)* $(,)?) => {
+ {
+ #[cfg(feature = "log")]
+ ::log::warn!($s $(, $x)*);
+ #[cfg(feature = "defmt")]
+ ::defmt::warn!($s $(, $x)*);
+ #[cfg(not(any(feature = "log", feature="defmt")))]
+ let _ = ($( & $x ),*);
+ }
+ };
+}
+
+macro_rules! error {
+ ($s:literal $(, $x:expr)* $(,)?) => {
+ {
+ #[cfg(feature = "log")]
+ ::log::error!($s $(, $x)*);
+ #[cfg(feature = "defmt")]
+ ::defmt::error!($s $(, $x)*);
+ #[cfg(not(any(feature = "log", feature="defmt")))]
+ let _ = ($( & $x ),*);
+ }
+ };
+}
+
+#[cfg(feature = "defmt")]
+macro_rules! unwrap {
+ ($($x:tt)*) => {
+ ::defmt::unwrap!($($x)*)
+ };
+}
+
+#[cfg(not(feature = "defmt"))]
+macro_rules! unwrap {
+ ($arg:expr) => {
+ match $crate::fmt::Try::into_result($arg) {
+ ::core::result::Result::Ok(t) => t,
+ ::core::result::Result::Err(e) => {
+ ::core::panic!("unwrap of `{}` failed: {:?}", ::core::stringify!($arg), e);
+ }
+ }
+ };
+ ($arg:expr, $($msg:expr),+ $(,)? ) => {
+ match $crate::fmt::Try::into_result($arg) {
+ ::core::result::Result::Ok(t) => t,
+ ::core::result::Result::Err(e) => {
+ ::core::panic!("unwrap of `{}` failed: {}: {:?}", ::core::stringify!($arg), ::core::format_args!($($msg,)*), e);
+ }
+ }
+ }
+}
+
+#[derive(Debug, Copy, Clone, Eq, PartialEq)]
+pub struct NoneError;
+
+pub trait Try {
+ type Ok;
+ type Error;
+ fn into_result(self) -> Result<Self::Ok, Self::Error>;
+}
+
+impl<T> Try for Option<T> {
+ type Ok = T;
+ type Error = NoneError;
+
+ #[inline]
+ fn into_result(self) -> Result<T, NoneError> {
+ self.ok_or(NoneError)
+ }
+}
+
+impl<T, E> Try for Result<T, E> {
+ type Ok = T;
+ type Error = E;
+
+ #[inline]
+ fn into_result(self) -> Self {
+ self
+ }
+}
diff --git a/embassy-time/src/instant.rs b/embassy-time/src/instant.rs
new file mode 100644
index 00000000..6a4925f4
--- /dev/null
+++ b/embassy-time/src/instant.rs
@@ -0,0 +1,159 @@
+use core::fmt;
+use core::ops::{Add, AddAssign, Sub, SubAssign};
+
+use super::{driver, Duration, GCD_1K, GCD_1M, TICKS_PER_SECOND};
+
+#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord)]
+#[cfg_attr(feature = "defmt", derive(defmt::Format))]
+/// An Instant in time, based on the MCU's clock ticks since startup.
+pub struct Instant {
+ ticks: u64,
+}
+
+impl Instant {
+ /// The smallest (earliest) value that can be represented by the `Instant` type.
+ pub const MIN: Instant = Instant { ticks: u64::MIN };
+ /// The largest (latest) value that can be represented by the `Instant` type.
+ pub const MAX: Instant = Instant { ticks: u64::MAX };
+
+ /// Returns an Instant representing the current time.
+ pub fn now() -> Instant {
+ Instant { ticks: driver::now() }
+ }
+
+ /// Create an Instant from a tick count since system boot.
+ pub const fn from_ticks(ticks: u64) -> Self {
+ Self { ticks }
+ }
+
+ /// Create an Instant from a microsecond count since system boot.
+ pub const fn from_micros(micros: u64) -> Self {
+ Self {
+ ticks: micros * (TICKS_PER_SECOND / GCD_1M) / (1_000_000 / GCD_1M),
+ }
+ }
+
+ /// Create an Instant from a millisecond count since system boot.
+ pub const fn from_millis(millis: u64) -> Self {
+ Self {
+ ticks: millis * (TICKS_PER_SECOND / GCD_1K) / (1000 / GCD_1K),
+ }
+ }
+
+ /// Create an Instant from a second count since system boot.
+ pub const fn from_secs(seconds: u64) -> Self {
+ Self {
+ ticks: seconds * TICKS_PER_SECOND,
+ }
+ }
+
+ /// Tick count since system boot.
+ pub const fn as_ticks(&self) -> u64 {
+ self.ticks
+ }
+
+ /// Seconds since system boot.
+ pub const fn as_secs(&self) -> u64 {
+ self.ticks / TICKS_PER_SECOND
+ }
+
+ /// Milliseconds since system boot.
+ pub const fn as_millis(&self) -> u64 {
+ self.ticks * (1000 / GCD_1K) / (TICKS_PER_SECOND / GCD_1K)
+ }
+
+ /// Microseconds since system boot.
+ pub const fn as_micros(&self) -> u64 {
+ self.ticks * (1_000_000 / GCD_1M) / (TICKS_PER_SECOND / GCD_1M)
+ }
+
+ /// Duration between this Instant and another Instant
+ /// Panics on over/underflow.
+ pub fn duration_since(&self, earlier: Instant) -> Duration {
+ Duration {
+ ticks: self.ticks.checked_sub(earlier.ticks).unwrap(),
+ }
+ }
+
+ /// Duration between this Instant and another Instant
+ pub fn checked_duration_since(&self, earlier: Instant) -> Option<Duration> {
+ if self.ticks < earlier.ticks {
+ None
+ } else {
+ Some(Duration {
+ ticks: self.ticks - earlier.ticks,
+ })
+ }
+ }
+
+ /// Returns the duration since the "earlier" Instant.
+ /// If the "earlier" instant is in the future, the duration is set to zero.
+ pub fn saturating_duration_since(&self, earlier: Instant) -> Duration {
+ Duration {
+ ticks: if self.ticks < earlier.ticks {
+ 0
+ } else {
+ self.ticks - earlier.ticks
+ },
+ }
+ }
+
+ /// Duration elapsed since this Instant.
+ pub fn elapsed(&self) -> Duration {
+ Instant::now() - *self
+ }
+
+ /// Adds one Duration to self, returning a new `Instant` or None in the event of an overflow.
+ pub fn checked_add(&self, duration: Duration) -> Option<Instant> {
+ self.ticks.checked_add(duration.ticks).map(|ticks| Instant { ticks })
+ }
+
+ /// Subtracts one Duration to self, returning a new `Instant` or None in the event of an overflow.
+ pub fn checked_sub(&self, duration: Duration) -> Option<Instant> {
+ self.ticks.checked_sub(duration.ticks).map(|ticks| Instant { ticks })
+ }
+}
+
+impl Add<Duration> for Instant {
+ type Output = Instant;
+
+ fn add(self, other: Duration) -> Instant {
+ self.checked_add(other)
+ .expect("overflow when adding duration to instant")
+ }
+}
+
+impl AddAssign<Duration> for Instant {
+ fn add_assign(&mut self, other: Duration) {
+ *self = *self + other;
+ }
+}
+
+impl Sub<Duration> for Instant {
+ type Output = Instant;
+
+ fn sub(self, other: Duration) -> Instant {
+ self.checked_sub(other)
+ .expect("overflow when subtracting duration from instant")
+ }
+}
+
+impl SubAssign<Duration> for Instant {
+ fn sub_assign(&mut self, other: Duration) {
+ *self = *self - other;
+ }
+}
+
+impl Sub<Instant> for Instant {
+ type Output = Duration;
+
+ fn sub(self, other: Instant) -> Duration {
+ self.duration_since(other)
+ }
+}
+
+impl<'a> fmt::Display for Instant {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ write!(f, "{} ticks", self.ticks)
+ }
+}
diff --git a/embassy-time/src/lib.rs b/embassy-time/src/lib.rs
new file mode 100644
index 00000000..a6454d55
--- /dev/null
+++ b/embassy-time/src/lib.rs
@@ -0,0 +1,99 @@
+#![cfg_attr(not(any(feature = "std", feature = "wasm")), no_std)]
+#![cfg_attr(feature = "nightly", feature(generic_associated_types, type_alias_impl_trait))]
+#![allow(clippy::new_without_default)]
+#![warn(missing_docs)]
+
+//! Timekeeping, delays and timeouts.
+//!
+//! Timekeeping is done with elapsed time since system boot. Time is represented in
+//! ticks, where the tick rate is defined by the current driver, usually to match
+//! the tick rate of the hardware.
+//!
+//! Tick counts are 64 bits. At the highest supported tick rate of 1Mhz this supports
+//! representing time spans of up to ~584558 years, which is big enough for all practical
+//! purposes and allows not having to worry about overflows.
+//!
+//! [`Instant`] represents a given instant of time (relative to system boot), and [`Duration`]
+//! represents the duration of a span of time. They implement the math operations you'd expect,
+//! like addition and substraction.
+//!
+//! # Delays and timeouts
+//!
+//! [`Timer`] allows performing async delays. [`Ticker`] allows periodic delays without drifting over time.
+//!
+//! An implementation of the `embedded-hal` delay traits is provided by [`Delay`], for compatibility
+//! with libraries from the ecosystem.
+//!
+//! # Wall-clock time
+//!
+//! The `time` module deals exclusively with a monotonically increasing tick count.
+//! Therefore it has no direct support for wall-clock time ("real life" datetimes
+//! like `2021-08-24 13:33:21`).
+//!
+//! If persistence across reboots is not needed, support can be built on top of
+//! `embassy_time` by storing the offset between "seconds elapsed since boot"
+//! and "seconds since unix epoch".
+//!
+//! # Time driver
+//!
+//! The `time` module is backed by a global "time driver" specified at build time.
+//! Only one driver can be active in a program.
+//!
+//! All methods and structs transparently call into the active driver. This makes it
+//! possible for libraries to use `embassy_time` in a driver-agnostic way without
+//! requiring generic parameters.
+//!
+//! For more details, check the [`driver`] module.
+
+// This mod MUST go first, so that the others see its macros.
+pub(crate) mod fmt;
+
+mod delay;
+pub mod driver;
+mod duration;
+mod instant;
+mod timer;
+
+#[cfg(feature = "std")]
+mod driver_std;
+#[cfg(feature = "wasm")]
+mod driver_wasm;
+
+pub use delay::{block_for, Delay};
+pub use duration::Duration;
+pub use instant::Instant;
+pub use timer::{with_timeout, Ticker, TimeoutError, Timer};
+
+#[cfg(feature = "tick-1000hz")]
+const TPS: u64 = 1_000;
+
+#[cfg(feature = "tick-32768hz")]
+const TPS: u64 = 32_768;
+
+#[cfg(feature = "tick-1mhz")]
+const TPS: u64 = 1_000_000;
+
+#[cfg(feature = "tick-16mhz")]
+const TPS: u64 = 16_000_000;
+
+/// Ticks per second of the global timebase.
+///
+/// This value is specified by the `tick-*` Cargo features, which
+/// should be set by the time driver. Some drivers support a fixed tick rate, others
+/// allow you to choose a tick rate with Cargo features of their own. You should not
+/// set the `tick-*` features for embassy yourself as an end user.
+pub const TICKS_PER_SECOND: u64 = TPS;
+
+const fn gcd(a: u64, b: u64) -> u64 {
+ if b == 0 {
+ a
+ } else {
+ gcd(b, a % b)
+ }
+}
+
+pub(crate) const GCD_1K: u64 = gcd(TICKS_PER_SECOND, 1_000);
+pub(crate) const GCD_1M: u64 = gcd(TICKS_PER_SECOND, 1_000_000);
+
+#[cfg(feature = "defmt-timestamp-uptime")]
+defmt::timestamp! {"{=u64:us}", Instant::now().as_micros() }
diff --git a/embassy-time/src/timer.rs b/embassy-time/src/timer.rs
new file mode 100644
index 00000000..bd791b81
--- /dev/null
+++ b/embassy-time/src/timer.rs
@@ -0,0 +1,158 @@
+use core::future::Future;
+use core::pin::Pin;
+use core::task::{Context, Poll, Waker};
+
+use futures_util::future::{select, Either};
+use futures_util::{pin_mut, Stream};
+
+use crate::{Duration, Instant};
+
+/// Error returned by [`with_timeout`] on timeout.
+#[derive(Debug, Clone, PartialEq, Eq)]
+#[cfg_attr(feature = "defmt", derive(defmt::Format))]
+pub struct TimeoutError;
+
+/// Runs a given future with a timeout.
+///
+/// If the future completes before the timeout, its output is returned. Otherwise, on timeout,
+/// work on the future is stopped (`poll` is no longer called), the future is dropped and `Err(TimeoutError)` is returned.
+pub async fn with_timeout<F: Future>(timeout: Duration, fut: F) -> Result<F::Output, TimeoutError> {
+ let timeout_fut = Timer::after(timeout);
+ pin_mut!(fut);
+ match select(fut, timeout_fut).await {
+ Either::Left((r, _)) => Ok(r),
+ Either::Right(_) => Err(TimeoutError),
+ }
+}
+
+/// A future that completes at a specified [Instant](struct.Instant.html).
+pub struct Timer {
+ expires_at: Instant,
+ yielded_once: bool,
+}
+
+impl Timer {
+ /// Expire at specified [Instant](struct.Instant.html)
+ pub fn at(expires_at: Instant) -> Self {
+ Self {
+ expires_at,
+ yielded_once: false,
+ }
+ }
+
+ /// Expire after specified [Duration](struct.Duration.html).
+ /// This can be used as a `sleep` abstraction.
+ ///
+ /// Example:
+ /// ``` no_run
+ /// # #![feature(type_alias_impl_trait)]
+ /// #
+ /// # fn foo() {}
+ /// use embassy_time::{Duration, Timer};
+ ///
+ /// #[embassy_executor::task]
+ /// async fn demo_sleep_seconds() {
+ /// // suspend this task for one second.
+ /// Timer::after(Duration::from_secs(1)).await;
+ /// }
+ /// ```
+ pub fn after(duration: Duration) -> Self {
+ Self {
+ expires_at: Instant::now() + duration,
+ yielded_once: false,
+ }
+ }
+}
+
+impl Unpin for Timer {}
+
+impl Future for Timer {
+ type Output = ();
+ fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
+ if self.yielded_once && self.expires_at <= Instant::now() {
+ Poll::Ready(())
+ } else {
+ schedule_wake(self.expires_at, cx.waker());
+ self.yielded_once = true;
+ Poll::Pending
+ }
+ }
+}
+
+/// Asynchronous stream that yields every Duration, indefinitely.
+///
+/// This stream will tick at uniform intervals, even if blocking work is performed between ticks.
+///
+/// For instance, consider the following code fragment.
+/// ``` no_run
+/// # #![feature(type_alias_impl_trait)]
+/// #
+/// use embassy_time::{Duration, Timer};
+/// # fn foo() {}
+///
+/// #[embassy_executor::task]
+/// async fn ticker_example_0() {
+/// loop {
+/// foo();
+/// Timer::after(Duration::from_secs(1)).await;
+/// }
+/// }
+/// ```
+///
+/// This fragment will not call `foo` every second.
+/// Instead, it will call it every second + the time it took to previously call `foo`.
+///
+/// Example using ticker, which will consistently call `foo` once a second.
+///
+/// ``` no_run
+/// # #![feature(type_alias_impl_trait)]
+/// #
+/// use embassy_time::{Duration, Ticker};
+/// use futures::StreamExt;
+/// # fn foo(){}
+///
+/// #[embassy_executor::task]
+/// async fn ticker_example_1() {
+/// let mut ticker = Ticker::every(Duration::from_secs(1));
+/// loop {
+/// foo();
+/// ticker.next().await;
+/// }
+/// }
+/// ```
+pub struct Ticker {
+ expires_at: Instant,
+ duration: Duration,
+}
+
+impl Ticker {
+ /// Creates a new ticker that ticks at the specified duration interval.
+ pub fn every(duration: Duration) -> Self {
+ let expires_at = Instant::now() + duration;
+ Self { expires_at, duration }
+ }
+}
+
+impl Unpin for Ticker {}
+
+impl Stream for Ticker {
+ type Item = ();
+ fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
+ if self.expires_at <= Instant::now() {
+ let dur = self.duration;
+ self.expires_at += dur;
+ Poll::Ready(Some(()))
+ } else {
+ schedule_wake(self.expires_at, cx.waker());
+ Poll::Pending
+ }
+ }
+}
+
+extern "Rust" {
+ fn _embassy_time_schedule_wake(at: Instant, waker: &Waker);
+}
+
+fn schedule_wake(at: Instant, waker: &Waker) {
+ unsafe { _embassy_time_schedule_wake(at, waker) }
+}