mirror of
https://github.com/embassy-rs/embassy.git
synced 2024-11-22 14:53:03 +00:00
Merge pull request #373 from embassy-rs/docs
Time driver improvements, docs.
This commit is contained in:
commit
e56c6166dc
@ -45,7 +45,6 @@ members = [
|
|||||||
#"examples/rp",
|
#"examples/rp",
|
||||||
|
|
||||||
# std
|
# std
|
||||||
#"embassy-std",
|
|
||||||
#"examples/std",
|
#"examples/std",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
@ -82,7 +82,7 @@ impl AlarmState {
|
|||||||
|
|
||||||
const ALARM_COUNT: usize = 3;
|
const ALARM_COUNT: usize = 3;
|
||||||
|
|
||||||
struct State {
|
struct RtcDriver {
|
||||||
/// Number of 2^23 periods elapsed since boot.
|
/// Number of 2^23 periods elapsed since boot.
|
||||||
period: AtomicU32,
|
period: AtomicU32,
|
||||||
alarm_count: AtomicU8,
|
alarm_count: AtomicU8,
|
||||||
@ -91,13 +91,13 @@ struct State {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const ALARM_STATE_NEW: AlarmState = AlarmState::new();
|
const ALARM_STATE_NEW: AlarmState = AlarmState::new();
|
||||||
static STATE: State = State {
|
embassy::time_driver_impl!(static DRIVER: RtcDriver = RtcDriver {
|
||||||
period: AtomicU32::new(0),
|
period: AtomicU32::new(0),
|
||||||
alarm_count: AtomicU8::new(0),
|
alarm_count: AtomicU8::new(0),
|
||||||
alarms: Mutex::new([ALARM_STATE_NEW; ALARM_COUNT]),
|
alarms: Mutex::new([ALARM_STATE_NEW; ALARM_COUNT]),
|
||||||
};
|
});
|
||||||
|
|
||||||
impl State {
|
impl RtcDriver {
|
||||||
fn init(&'static self, irq_prio: crate::interrupt::Priority) {
|
fn init(&'static self, irq_prio: crate::interrupt::Priority) {
|
||||||
let r = rtc();
|
let r = rtc();
|
||||||
r.cc[3].write(|w| unsafe { w.bits(0x800000) });
|
r.cc[3].write(|w| unsafe { w.bits(0x800000) });
|
||||||
@ -159,14 +159,6 @@ impl State {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fn now(&self) -> u64 {
|
|
||||||
// `period` MUST be read before `counter`, see comment at the top for details.
|
|
||||||
let period = self.period.load(Ordering::Relaxed);
|
|
||||||
compiler_fence(Ordering::Acquire);
|
|
||||||
let counter = rtc().counter.read().bits();
|
|
||||||
calc_now(period, counter)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn get_alarm<'a>(&'a self, cs: CriticalSection<'a>, alarm: AlarmHandle) -> &'a AlarmState {
|
fn get_alarm<'a>(&'a self, cs: CriticalSection<'a>, alarm: AlarmHandle) -> &'a AlarmState {
|
||||||
// safety: we're allowed to assume the AlarmState is created by us, and
|
// safety: we're allowed to assume the AlarmState is created by us, and
|
||||||
// we never create one that's out of bounds.
|
// we never create one that's out of bounds.
|
||||||
@ -188,8 +180,18 @@ impl State {
|
|||||||
let f: fn(*mut ()) = unsafe { mem::transmute(alarm.callback.get()) };
|
let f: fn(*mut ()) = unsafe { mem::transmute(alarm.callback.get()) };
|
||||||
f(alarm.ctx.get());
|
f(alarm.ctx.get());
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn allocate_alarm(&self) -> Option<AlarmHandle> {
|
impl Driver for RtcDriver {
|
||||||
|
fn now(&self) -> u64 {
|
||||||
|
// `period` MUST be read before `counter`, see comment at the top for details.
|
||||||
|
let period = self.period.load(Ordering::Relaxed);
|
||||||
|
compiler_fence(Ordering::Acquire);
|
||||||
|
let counter = rtc().counter.read().bits();
|
||||||
|
calc_now(period, counter)
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe fn allocate_alarm(&self) -> Option<AlarmHandle> {
|
||||||
let id = self
|
let id = self
|
||||||
.alarm_count
|
.alarm_count
|
||||||
.fetch_update(Ordering::AcqRel, Ordering::Acquire, |x| {
|
.fetch_update(Ordering::AcqRel, Ordering::Acquire, |x| {
|
||||||
@ -201,7 +203,7 @@ impl State {
|
|||||||
});
|
});
|
||||||
|
|
||||||
match id {
|
match id {
|
||||||
Ok(id) => Some(unsafe { AlarmHandle::new(id) }),
|
Ok(id) => Some(AlarmHandle::new(id)),
|
||||||
Err(_) => None,
|
Err(_) => None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -263,32 +265,11 @@ impl State {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
struct RtcDriver;
|
|
||||||
embassy::time_driver_impl!(RtcDriver);
|
|
||||||
|
|
||||||
impl Driver for RtcDriver {
|
|
||||||
fn now() -> u64 {
|
|
||||||
STATE.now()
|
|
||||||
}
|
|
||||||
|
|
||||||
unsafe fn allocate_alarm() -> Option<AlarmHandle> {
|
|
||||||
STATE.allocate_alarm()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn set_alarm_callback(alarm: AlarmHandle, callback: fn(*mut ()), ctx: *mut ()) {
|
|
||||||
STATE.set_alarm_callback(alarm, callback, ctx)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn set_alarm(alarm: AlarmHandle, timestamp: u64) {
|
|
||||||
STATE.set_alarm(alarm, timestamp)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[interrupt]
|
#[interrupt]
|
||||||
fn RTC1() {
|
fn RTC1() {
|
||||||
STATE.on_interrupt()
|
DRIVER.on_interrupt()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn init(irq_prio: crate::interrupt::Priority) {
|
pub(crate) fn init(irq_prio: crate::interrupt::Priority) {
|
||||||
STATE.init(irq_prio)
|
DRIVER.init(irq_prio)
|
||||||
}
|
}
|
||||||
|
@ -19,38 +19,40 @@ const DUMMY_ALARM: AlarmState = AlarmState {
|
|||||||
callback: Cell::new(None),
|
callback: Cell::new(None),
|
||||||
};
|
};
|
||||||
|
|
||||||
static ALARMS: Mutex<[AlarmState; ALARM_COUNT]> = Mutex::new([DUMMY_ALARM; ALARM_COUNT]);
|
struct TimerDriver {
|
||||||
static NEXT_ALARM: AtomicU8 = AtomicU8::new(0);
|
alarms: Mutex<[AlarmState; ALARM_COUNT]>,
|
||||||
|
next_alarm: AtomicU8,
|
||||||
|
}
|
||||||
|
|
||||||
fn now() -> u64 {
|
embassy::time_driver_impl!(static DRIVER: TimerDriver = TimerDriver{
|
||||||
loop {
|
alarms: Mutex::new([DUMMY_ALARM; ALARM_COUNT]),
|
||||||
unsafe {
|
next_alarm: AtomicU8::new(0),
|
||||||
let hi = pac::TIMER.timerawh().read();
|
});
|
||||||
let lo = pac::TIMER.timerawl().read();
|
|
||||||
let hi2 = pac::TIMER.timerawh().read();
|
impl Driver for TimerDriver {
|
||||||
if hi == hi2 {
|
fn now(&self) -> u64 {
|
||||||
return (hi as u64) << 32 | (lo as u64);
|
loop {
|
||||||
|
unsafe {
|
||||||
|
let hi = pac::TIMER.timerawh().read();
|
||||||
|
let lo = pac::TIMER.timerawl().read();
|
||||||
|
let hi2 = pac::TIMER.timerawh().read();
|
||||||
|
if hi == hi2 {
|
||||||
|
return (hi as u64) << 32 | (lo as u64);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
struct TimerDriver;
|
unsafe fn allocate_alarm(&self) -> Option<AlarmHandle> {
|
||||||
embassy::time_driver_impl!(TimerDriver);
|
let id = self
|
||||||
|
.next_alarm
|
||||||
impl Driver for TimerDriver {
|
.fetch_update(Ordering::AcqRel, Ordering::Acquire, |x| {
|
||||||
fn now() -> u64 {
|
if x < ALARM_COUNT as u8 {
|
||||||
now()
|
Some(x + 1)
|
||||||
}
|
} else {
|
||||||
|
None
|
||||||
unsafe fn allocate_alarm() -> Option<AlarmHandle> {
|
}
|
||||||
let id = NEXT_ALARM.fetch_update(Ordering::AcqRel, Ordering::Acquire, |x| {
|
});
|
||||||
if x < ALARM_COUNT as u8 {
|
|
||||||
Some(x + 1)
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
match id {
|
match id {
|
||||||
Ok(id) => Some(AlarmHandle::new(id)),
|
Ok(id) => Some(AlarmHandle::new(id)),
|
||||||
@ -58,18 +60,18 @@ impl Driver for TimerDriver {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn set_alarm_callback(alarm: AlarmHandle, callback: fn(*mut ()), ctx: *mut ()) {
|
fn set_alarm_callback(&self, alarm: AlarmHandle, callback: fn(*mut ()), ctx: *mut ()) {
|
||||||
let n = alarm.id() as usize;
|
let n = alarm.id() as usize;
|
||||||
critical_section::with(|cs| {
|
critical_section::with(|cs| {
|
||||||
let alarm = &ALARMS.borrow(cs)[n];
|
let alarm = &self.alarms.borrow(cs)[n];
|
||||||
alarm.callback.set(Some((callback, ctx)));
|
alarm.callback.set(Some((callback, ctx)));
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fn set_alarm(alarm: AlarmHandle, timestamp: u64) {
|
fn set_alarm(&self, alarm: AlarmHandle, timestamp: u64) {
|
||||||
let n = alarm.id() as usize;
|
let n = alarm.id() as usize;
|
||||||
critical_section::with(|cs| {
|
critical_section::with(|cs| {
|
||||||
let alarm = &ALARMS.borrow(cs)[n];
|
let alarm = &self.alarms.borrow(cs)[n];
|
||||||
alarm.timestamp.set(timestamp);
|
alarm.timestamp.set(timestamp);
|
||||||
|
|
||||||
// Arm it.
|
// Arm it.
|
||||||
@ -78,44 +80,46 @@ impl Driver for TimerDriver {
|
|||||||
// it is checked if the alarm time has passed.
|
// it is checked if the alarm time has passed.
|
||||||
unsafe { pac::TIMER.alarm(n).write_value(timestamp as u32) };
|
unsafe { pac::TIMER.alarm(n).write_value(timestamp as u32) };
|
||||||
|
|
||||||
let now = now();
|
let now = self.now();
|
||||||
|
|
||||||
// If alarm timestamp has passed, trigger it instantly.
|
// If alarm timestamp has passed, trigger it instantly.
|
||||||
// This disarms it.
|
// This disarms it.
|
||||||
if timestamp <= now {
|
if timestamp <= now {
|
||||||
trigger_alarm(n, cs);
|
self.trigger_alarm(n, cs);
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn check_alarm(n: usize) {
|
impl TimerDriver {
|
||||||
critical_section::with(|cs| {
|
fn check_alarm(&self, n: usize) {
|
||||||
let alarm = &ALARMS.borrow(cs)[n];
|
critical_section::with(|cs| {
|
||||||
let timestamp = alarm.timestamp.get();
|
let alarm = &self.alarms.borrow(cs)[n];
|
||||||
if timestamp <= now() {
|
let timestamp = alarm.timestamp.get();
|
||||||
trigger_alarm(n, cs)
|
if timestamp <= self.now() {
|
||||||
} else {
|
self.trigger_alarm(n, cs)
|
||||||
// Not elapsed, arm it again.
|
} else {
|
||||||
// This can happen if it was set more than 2^32 us in the future.
|
// Not elapsed, arm it again.
|
||||||
unsafe { pac::TIMER.alarm(n).write_value(timestamp as u32) };
|
// This can happen if it was set more than 2^32 us in the future.
|
||||||
|
unsafe { pac::TIMER.alarm(n).write_value(timestamp as u32) };
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// clear the irq
|
||||||
|
unsafe { pac::TIMER.intr().write(|w| w.set_alarm(n, true)) }
|
||||||
|
}
|
||||||
|
|
||||||
|
fn trigger_alarm(&self, n: usize, cs: CriticalSection) {
|
||||||
|
// disarm
|
||||||
|
unsafe { pac::TIMER.armed().write(|w| w.set_armed(1 << n)) }
|
||||||
|
|
||||||
|
let alarm = &self.alarms.borrow(cs)[n];
|
||||||
|
alarm.timestamp.set(u64::MAX);
|
||||||
|
|
||||||
|
// Call after clearing alarm, so the callback can set another alarm.
|
||||||
|
if let Some((f, ctx)) = alarm.callback.get() {
|
||||||
|
f(ctx);
|
||||||
}
|
}
|
||||||
});
|
|
||||||
|
|
||||||
// clear the irq
|
|
||||||
unsafe { pac::TIMER.intr().write(|w| w.set_alarm(n, true)) }
|
|
||||||
}
|
|
||||||
|
|
||||||
fn trigger_alarm(n: usize, cs: CriticalSection) {
|
|
||||||
// disarm
|
|
||||||
unsafe { pac::TIMER.armed().write(|w| w.set_armed(1 << n)) }
|
|
||||||
|
|
||||||
let alarm = &ALARMS.borrow(cs)[n];
|
|
||||||
alarm.timestamp.set(u64::MAX);
|
|
||||||
|
|
||||||
// Call after clearing alarm, so the callback can set another alarm.
|
|
||||||
if let Some((f, ctx)) = alarm.callback.get() {
|
|
||||||
f(ctx);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -123,7 +127,7 @@ fn trigger_alarm(n: usize, cs: CriticalSection) {
|
|||||||
pub unsafe fn init() {
|
pub unsafe fn init() {
|
||||||
// init alarms
|
// init alarms
|
||||||
critical_section::with(|cs| {
|
critical_section::with(|cs| {
|
||||||
let alarms = ALARMS.borrow(cs);
|
let alarms = DRIVER.alarms.borrow(cs);
|
||||||
for a in alarms {
|
for a in alarms {
|
||||||
a.timestamp.set(u64::MAX);
|
a.timestamp.set(u64::MAX);
|
||||||
}
|
}
|
||||||
@ -144,20 +148,20 @@ pub unsafe fn init() {
|
|||||||
|
|
||||||
#[interrupt]
|
#[interrupt]
|
||||||
unsafe fn TIMER_IRQ_0() {
|
unsafe fn TIMER_IRQ_0() {
|
||||||
check_alarm(0)
|
DRIVER.check_alarm(0)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[interrupt]
|
#[interrupt]
|
||||||
unsafe fn TIMER_IRQ_1() {
|
unsafe fn TIMER_IRQ_1() {
|
||||||
check_alarm(1)
|
DRIVER.check_alarm(1)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[interrupt]
|
#[interrupt]
|
||||||
unsafe fn TIMER_IRQ_2() {
|
unsafe fn TIMER_IRQ_2() {
|
||||||
check_alarm(2)
|
DRIVER.check_alarm(2)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[interrupt]
|
#[interrupt]
|
||||||
unsafe fn TIMER_IRQ_3() {
|
unsafe fn TIMER_IRQ_3() {
|
||||||
check_alarm(3)
|
DRIVER.check_alarm(3)
|
||||||
}
|
}
|
||||||
|
@ -26,12 +26,12 @@ type T = peripherals::TIM3;
|
|||||||
#[cfg(feature = "time-driver-tim2")]
|
#[cfg(feature = "time-driver-tim2")]
|
||||||
#[interrupt]
|
#[interrupt]
|
||||||
fn TIM2() {
|
fn TIM2() {
|
||||||
STATE.on_interrupt()
|
DRIVER.on_interrupt()
|
||||||
}
|
}
|
||||||
#[cfg(feature = "time-driver-tim3")]
|
#[cfg(feature = "time-driver-tim3")]
|
||||||
#[interrupt]
|
#[interrupt]
|
||||||
fn TIM3() {
|
fn TIM3() {
|
||||||
STATE.on_interrupt()
|
DRIVER.on_interrupt()
|
||||||
}
|
}
|
||||||
|
|
||||||
// Clock timekeeping works with something we call "periods", which are time intervals
|
// Clock timekeeping works with something we call "periods", which are time intervals
|
||||||
@ -76,7 +76,7 @@ impl AlarmState {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
struct State {
|
struct RtcDriver {
|
||||||
/// Number of 2^15 periods elapsed since boot.
|
/// Number of 2^15 periods elapsed since boot.
|
||||||
period: AtomicU32,
|
period: AtomicU32,
|
||||||
alarm_count: AtomicU8,
|
alarm_count: AtomicU8,
|
||||||
@ -85,13 +85,14 @@ struct State {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const ALARM_STATE_NEW: AlarmState = AlarmState::new();
|
const ALARM_STATE_NEW: AlarmState = AlarmState::new();
|
||||||
static STATE: State = State {
|
|
||||||
|
embassy::time_driver_impl!(static DRIVER: RtcDriver = RtcDriver {
|
||||||
period: AtomicU32::new(0),
|
period: AtomicU32::new(0),
|
||||||
alarm_count: AtomicU8::new(0),
|
alarm_count: AtomicU8::new(0),
|
||||||
alarms: Mutex::new([ALARM_STATE_NEW; ALARM_COUNT]),
|
alarms: Mutex::new([ALARM_STATE_NEW; ALARM_COUNT]),
|
||||||
};
|
});
|
||||||
|
|
||||||
impl State {
|
impl RtcDriver {
|
||||||
fn init(&'static self) {
|
fn init(&'static self) {
|
||||||
let r = T::regs();
|
let r = T::regs();
|
||||||
|
|
||||||
@ -185,16 +186,6 @@ impl State {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fn now(&self) -> u64 {
|
|
||||||
let r = T::regs();
|
|
||||||
|
|
||||||
let period = self.period.load(Ordering::Relaxed);
|
|
||||||
compiler_fence(Ordering::Acquire);
|
|
||||||
// NOTE(unsafe) Atomic read with no side-effects
|
|
||||||
let counter = unsafe { r.cnt().read().cnt() };
|
|
||||||
calc_now(period, counter)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn get_alarm<'a>(&'a self, cs: CriticalSection<'a>, alarm: AlarmHandle) -> &'a AlarmState {
|
fn get_alarm<'a>(&'a self, cs: CriticalSection<'a>, alarm: AlarmHandle) -> &'a AlarmState {
|
||||||
// safety: we're allowed to assume the AlarmState is created by us, and
|
// safety: we're allowed to assume the AlarmState is created by us, and
|
||||||
// we never create one that's out of bounds.
|
// we never create one that's out of bounds.
|
||||||
@ -213,8 +204,20 @@ impl State {
|
|||||||
let f: fn(*mut ()) = unsafe { mem::transmute(alarm.callback.get()) };
|
let f: fn(*mut ()) = unsafe { mem::transmute(alarm.callback.get()) };
|
||||||
f(alarm.ctx.get());
|
f(alarm.ctx.get());
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn allocate_alarm(&self) -> Option<AlarmHandle> {
|
impl Driver for RtcDriver {
|
||||||
|
fn now(&self) -> u64 {
|
||||||
|
let r = T::regs();
|
||||||
|
|
||||||
|
let period = self.period.load(Ordering::Relaxed);
|
||||||
|
compiler_fence(Ordering::Acquire);
|
||||||
|
// NOTE(unsafe) Atomic read with no side-effects
|
||||||
|
let counter = unsafe { r.cnt().read().cnt() };
|
||||||
|
calc_now(period, counter)
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe fn allocate_alarm(&self) -> Option<AlarmHandle> {
|
||||||
let id = self
|
let id = self
|
||||||
.alarm_count
|
.alarm_count
|
||||||
.fetch_update(Ordering::AcqRel, Ordering::Acquire, |x| {
|
.fetch_update(Ordering::AcqRel, Ordering::Acquire, |x| {
|
||||||
@ -226,7 +229,7 @@ impl State {
|
|||||||
});
|
});
|
||||||
|
|
||||||
match id {
|
match id {
|
||||||
Ok(id) => Some(unsafe { AlarmHandle::new(id) }),
|
Ok(id) => Some(AlarmHandle::new(id)),
|
||||||
Err(_) => None,
|
Err(_) => None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -269,29 +272,8 @@ impl State {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
struct RtcDriver;
|
|
||||||
embassy::time_driver_impl!(RtcDriver);
|
|
||||||
|
|
||||||
impl Driver for RtcDriver {
|
|
||||||
fn now() -> u64 {
|
|
||||||
STATE.now()
|
|
||||||
}
|
|
||||||
|
|
||||||
unsafe fn allocate_alarm() -> Option<AlarmHandle> {
|
|
||||||
STATE.allocate_alarm()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn set_alarm_callback(alarm: AlarmHandle, callback: fn(*mut ()), ctx: *mut ()) {
|
|
||||||
STATE.set_alarm_callback(alarm, callback, ctx)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn set_alarm(alarm: AlarmHandle, timestamp: u64) {
|
|
||||||
STATE.set_alarm(alarm, timestamp)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) fn init() {
|
pub(crate) fn init() {
|
||||||
STATE.init()
|
DRIVER.init()
|
||||||
}
|
}
|
||||||
|
|
||||||
// ------------------------------------------------------
|
// ------------------------------------------------------
|
||||||
|
@ -7,9 +7,17 @@ resolver = "2"
|
|||||||
|
|
||||||
[features]
|
[features]
|
||||||
default = []
|
default = []
|
||||||
std = ["futures/std", "embassy-traits/std"]
|
std = ["futures/std", "embassy-traits/std", "time", "time-tick-1mhz"]
|
||||||
|
|
||||||
|
# Enable `embassy::time` module.
|
||||||
|
# NOTE: This feature is only intended to be enabled by crates providing the time driver implementation.
|
||||||
|
# Enabling it directly without supplying a time driver will fail to link.
|
||||||
time = []
|
time = []
|
||||||
|
|
||||||
|
# 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!
|
||||||
time-tick-32768hz = ["time"]
|
time-tick-32768hz = ["time"]
|
||||||
time-tick-1000hz = ["time"]
|
time-tick-1000hz = ["time"]
|
||||||
time-tick-1mhz = ["time"]
|
time-tick-1mhz = ["time"]
|
||||||
|
67
embassy/src/executor/arch/std.rs
Normal file
67
embassy/src/executor/arch/std.rs
Normal file
@ -0,0 +1,67 @@
|
|||||||
|
use std::marker::PhantomData;
|
||||||
|
use std::sync::{Condvar, Mutex};
|
||||||
|
|
||||||
|
use super::{raw, Spawner};
|
||||||
|
|
||||||
|
pub struct Executor {
|
||||||
|
inner: raw::Executor,
|
||||||
|
not_send: PhantomData<*mut ()>,
|
||||||
|
signaler: &'static Signaler,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Executor {
|
||||||
|
pub fn new() -> Self {
|
||||||
|
let signaler = &*Box::leak(Box::new(Signaler::new()));
|
||||||
|
Self {
|
||||||
|
inner: raw::Executor::new(
|
||||||
|
|p| unsafe {
|
||||||
|
let s = &*(p as *const () as *const Signaler);
|
||||||
|
s.signal()
|
||||||
|
},
|
||||||
|
signaler as *const _ as _,
|
||||||
|
),
|
||||||
|
not_send: PhantomData,
|
||||||
|
signaler,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Runs the executor.
|
||||||
|
///
|
||||||
|
/// This function never returns.
|
||||||
|
pub fn run(&'static mut self, init: impl FnOnce(Spawner)) -> ! {
|
||||||
|
init(unsafe { self.inner.spawner() });
|
||||||
|
|
||||||
|
loop {
|
||||||
|
unsafe { self.inner.run_queued() };
|
||||||
|
self.signaler.wait()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct Signaler {
|
||||||
|
mutex: Mutex<bool>,
|
||||||
|
condvar: Condvar,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Signaler {
|
||||||
|
fn new() -> Self {
|
||||||
|
Self {
|
||||||
|
mutex: Mutex::new(false),
|
||||||
|
condvar: Condvar::new(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn wait(&self) {
|
||||||
|
let mut signaled = self.mutex.lock().unwrap();
|
||||||
|
while !*signaled {
|
||||||
|
signaled = self.condvar.wait(signaled).unwrap();
|
||||||
|
}
|
||||||
|
*signaled = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn signal(&self) {
|
||||||
|
let mut signaled = self.mutex.lock().unwrap();
|
||||||
|
*signaled = true;
|
||||||
|
self.condvar.notify_one();
|
||||||
|
}
|
||||||
|
}
|
@ -1,4 +1,5 @@
|
|||||||
#[path = "arch/arm.rs"]
|
#[cfg_attr(feature = "std", path = "arch/std.rs")]
|
||||||
|
#[cfg_attr(not(feature = "std"), path = "arch/arm.rs")]
|
||||||
mod arch;
|
mod arch;
|
||||||
pub mod raw;
|
pub mod raw;
|
||||||
mod spawner;
|
mod spawner;
|
||||||
|
@ -1,7 +1,11 @@
|
|||||||
mod error;
|
mod error;
|
||||||
|
#[cfg(feature = "std")]
|
||||||
|
mod std;
|
||||||
mod traits;
|
mod traits;
|
||||||
mod util;
|
mod util;
|
||||||
|
|
||||||
pub use self::error::*;
|
pub use self::error::*;
|
||||||
|
#[cfg(feature = "std")]
|
||||||
|
pub use self::std::*;
|
||||||
pub use self::traits::*;
|
pub use self::traits::*;
|
||||||
pub use self::util::*;
|
pub use self::util::*;
|
||||||
|
35
embassy/src/io/std.rs
Normal file
35
embassy/src/io/std.rs
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
use core::pin::Pin;
|
||||||
|
use core::task::{Context, Poll};
|
||||||
|
use futures::io as std_io;
|
||||||
|
|
||||||
|
use super::{AsyncBufRead, AsyncWrite, Result};
|
||||||
|
|
||||||
|
pub struct FromStdIo<T>(T);
|
||||||
|
|
||||||
|
impl<T> FromStdIo<T> {
|
||||||
|
pub fn new(inner: T) -> Self {
|
||||||
|
Self(inner)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: std_io::AsyncBufRead> AsyncBufRead for FromStdIo<T> {
|
||||||
|
fn poll_fill_buf(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Result<&[u8]>> {
|
||||||
|
let Self(inner) = unsafe { self.get_unchecked_mut() };
|
||||||
|
unsafe { Pin::new_unchecked(inner) }
|
||||||
|
.poll_fill_buf(cx)
|
||||||
|
.map_err(|e| e.into())
|
||||||
|
}
|
||||||
|
fn consume(self: Pin<&mut Self>, amt: usize) {
|
||||||
|
let Self(inner) = unsafe { self.get_unchecked_mut() };
|
||||||
|
unsafe { Pin::new_unchecked(inner) }.consume(amt)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: std_io::AsyncWrite> AsyncWrite for FromStdIo<T> {
|
||||||
|
fn poll_write(self: Pin<&mut Self>, cx: &mut Context<'_>, buf: &[u8]) -> Poll<Result<usize>> {
|
||||||
|
let Self(inner) = unsafe { self.get_unchecked_mut() };
|
||||||
|
unsafe { Pin::new_unchecked(inner) }
|
||||||
|
.poll_write(cx, buf)
|
||||||
|
.map_err(|e| e.into())
|
||||||
|
}
|
||||||
|
}
|
@ -5,9 +5,6 @@ use core::task::{Context, Poll};
|
|||||||
#[cfg(feature = "alloc")]
|
#[cfg(feature = "alloc")]
|
||||||
use alloc::boxed::Box;
|
use alloc::boxed::Box;
|
||||||
|
|
||||||
#[cfg(feature = "std")]
|
|
||||||
use futures::io as std_io;
|
|
||||||
|
|
||||||
use super::error::Result;
|
use super::error::Result;
|
||||||
|
|
||||||
/// Read bytes asynchronously.
|
/// Read bytes asynchronously.
|
||||||
@ -159,37 +156,3 @@ where
|
|||||||
self.get_mut().as_mut().poll_write(cx, buf)
|
self.get_mut().as_mut().poll_write(cx, buf)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "std")]
|
|
||||||
pub struct FromStdIo<T>(T);
|
|
||||||
|
|
||||||
#[cfg(feature = "std")]
|
|
||||||
impl<T> FromStdIo<T> {
|
|
||||||
pub fn new(inner: T) -> Self {
|
|
||||||
Self(inner)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(feature = "std")]
|
|
||||||
impl<T: std_io::AsyncBufRead> AsyncBufRead for FromStdIo<T> {
|
|
||||||
fn poll_fill_buf(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Result<&[u8]>> {
|
|
||||||
let Self(inner) = unsafe { self.get_unchecked_mut() };
|
|
||||||
unsafe { Pin::new_unchecked(inner) }
|
|
||||||
.poll_fill_buf(cx)
|
|
||||||
.map_err(|e| e.into())
|
|
||||||
}
|
|
||||||
fn consume(self: Pin<&mut Self>, amt: usize) {
|
|
||||||
let Self(inner) = unsafe { self.get_unchecked_mut() };
|
|
||||||
unsafe { Pin::new_unchecked(inner) }.consume(amt)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(feature = "std")]
|
|
||||||
impl<T: std_io::AsyncWrite> AsyncWrite for FromStdIo<T> {
|
|
||||||
fn poll_write(self: Pin<&mut Self>, cx: &mut Context<'_>, buf: &[u8]) -> Poll<Result<usize>> {
|
|
||||||
let Self(inner) = unsafe { self.get_unchecked_mut() };
|
|
||||||
unsafe { Pin::new_unchecked(inner) }
|
|
||||||
.poll_write(cx, buf)
|
|
||||||
.map_err(|e| e.into())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
@ -4,11 +4,10 @@ use super::{Duration, Instant, Timer};
|
|||||||
|
|
||||||
/// Type implementing async delays and blocking `embedded-hal` delays.
|
/// Type implementing async delays and blocking `embedded-hal` delays.
|
||||||
///
|
///
|
||||||
/// For this interface to work, the Executor's clock must be correctly initialized before using it.
|
|
||||||
/// The delays are implemented in a "best-effort" way, meaning that the cpu will block for at least
|
/// 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.
|
/// 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 can be chosen through
|
/// Make sure to use a suitable tick rate for your use case. The tick rate is defined by the currently
|
||||||
/// features flags of this crate.
|
/// active driver.
|
||||||
pub struct Delay;
|
pub struct Delay;
|
||||||
|
|
||||||
impl crate::traits::delay::Delay for Delay {
|
impl crate::traits::delay::Delay for Delay {
|
||||||
@ -58,9 +57,7 @@ impl embedded_hal::blocking::delay::DelayUs<u32> for Delay {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Blocks the cpu for at least `duration`.
|
/// Blocks for at least `duration`.
|
||||||
///
|
|
||||||
/// For this interface to work, the Executor's clock must be correctly initialized before using it.
|
|
||||||
pub fn block_for(duration: Duration) {
|
pub fn block_for(duration: Duration) {
|
||||||
let expires_at = Instant::now() + duration;
|
let expires_at = Instant::now() + duration;
|
||||||
while Instant::now() < expires_at {}
|
while Instant::now() < expires_at {}
|
||||||
|
@ -1,3 +1,60 @@
|
|||||||
|
//! 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/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_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.
|
/// Alarm handle, assigned by the driver.
|
||||||
#[derive(Clone, Copy)]
|
#[derive(Clone, Copy)]
|
||||||
pub struct AlarmHandle {
|
pub struct AlarmHandle {
|
||||||
@ -21,22 +78,22 @@ impl AlarmHandle {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Time driver
|
/// Time driver
|
||||||
pub trait Driver {
|
pub trait Driver: Send + Sync + 'static {
|
||||||
/// Return the current timestamp in ticks.
|
/// Return the current timestamp in ticks.
|
||||||
/// This is guaranteed to be monotonic, i.e. a call to now() will always return
|
/// This is guaranteed to be monotonic, i.e. a call to now() will always return
|
||||||
/// a greater or equal value than earler calls.
|
/// a greater or equal value than earler calls.
|
||||||
fn now() -> u64;
|
fn now(&self) -> u64;
|
||||||
|
|
||||||
/// Try allocating an alarm handle. Returns None if no alarms left.
|
/// Try allocating an alarm handle. Returns None if no alarms left.
|
||||||
/// Initially the alarm has no callback set, and a null `ctx` pointer.
|
/// Initially the alarm has no callback set, and a null `ctx` pointer.
|
||||||
///
|
///
|
||||||
/// # Safety
|
/// # Safety
|
||||||
/// It is UB to make the alarm fire before setting a callback.
|
/// It is UB to make the alarm fire before setting a callback.
|
||||||
unsafe fn allocate_alarm() -> Option<AlarmHandle>;
|
unsafe fn allocate_alarm(&self) -> Option<AlarmHandle>;
|
||||||
|
|
||||||
/// Sets the callback function to be called when the alarm triggers.
|
/// Sets the callback function to be called when the alarm triggers.
|
||||||
/// The callback may be called from any context (interrupt or thread mode).
|
/// The callback may be called from any context (interrupt or thread mode).
|
||||||
fn set_alarm_callback(alarm: AlarmHandle, callback: fn(*mut ()), ctx: *mut ());
|
fn set_alarm_callback(&self, alarm: AlarmHandle, callback: fn(*mut ()), ctx: *mut ());
|
||||||
|
|
||||||
/// Sets an alarm at the given timestamp. When the current timestamp reaches that
|
/// Sets an alarm at the given timestamp. When the current timestamp reaches that
|
||||||
/// timestamp, the provided callback funcion will be called.
|
/// timestamp, the provided callback funcion will be called.
|
||||||
@ -44,7 +101,7 @@ pub trait Driver {
|
|||||||
/// When callback is called, it is guaranteed that now() will return a value greater or equal than timestamp.
|
/// 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. This overwrites any previously-set alarm if any.
|
/// Only one alarm can be active at a time. This overwrites any previously-set alarm if any.
|
||||||
fn set_alarm(alarm: AlarmHandle, timestamp: u64);
|
fn set_alarm(&self, alarm: AlarmHandle, timestamp: u64);
|
||||||
}
|
}
|
||||||
|
|
||||||
extern "Rust" {
|
extern "Rust" {
|
||||||
@ -70,49 +127,34 @@ pub(crate) fn set_alarm(alarm: AlarmHandle, timestamp: u64) {
|
|||||||
|
|
||||||
/// Set the time Driver implementation.
|
/// Set the time Driver implementation.
|
||||||
///
|
///
|
||||||
/// # Example
|
/// See the module documentation for an example.
|
||||||
///
|
|
||||||
/// ```
|
|
||||||
/// struct MyDriver;
|
|
||||||
/// embassy::time_driver_impl!(MyDriver);
|
|
||||||
///
|
|
||||||
/// unsafe impl embassy::time::driver::Driver for MyDriver {
|
|
||||||
/// fn now() -> u64 {
|
|
||||||
/// todo!()
|
|
||||||
/// }
|
|
||||||
/// unsafe fn allocate_alarm() -> Option<AlarmHandle> {
|
|
||||||
/// todo!()
|
|
||||||
/// }
|
|
||||||
/// fn set_alarm_callback(alarm: AlarmHandle, callback: fn(*mut ()), ctx: *mut ()) {
|
|
||||||
/// todo!()
|
|
||||||
/// }
|
|
||||||
/// fn set_alarm(alarm: AlarmHandle, timestamp: u64) {
|
|
||||||
/// todo!()
|
|
||||||
/// }
|
|
||||||
/// }
|
|
||||||
///
|
|
||||||
#[macro_export]
|
#[macro_export]
|
||||||
macro_rules! time_driver_impl {
|
macro_rules! time_driver_impl {
|
||||||
($t: ty) => {
|
(static $name:ident: $t: ty = $val:expr) => {
|
||||||
|
static $name: $t = $val;
|
||||||
|
|
||||||
#[no_mangle]
|
#[no_mangle]
|
||||||
fn _embassy_time_now() -> u64 {
|
fn _embassy_time_now() -> u64 {
|
||||||
<$t as $crate::time::driver::Driver>::now()
|
<$t as $crate::time::driver::Driver>::now(&$name)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[no_mangle]
|
#[no_mangle]
|
||||||
unsafe fn _embassy_time_allocate_alarm() -> Option<AlarmHandle> {
|
unsafe fn _embassy_time_allocate_alarm() -> Option<$crate::time::driver::AlarmHandle> {
|
||||||
<$t as $crate::time::driver::Driver>::allocate_alarm()
|
<$t as $crate::time::driver::Driver>::allocate_alarm(&$name)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[no_mangle]
|
#[no_mangle]
|
||||||
fn _embassy_time_set_alarm_callback(
|
fn _embassy_time_set_alarm_callback(
|
||||||
alarm: AlarmHandle,
|
alarm: $crate::time::driver::AlarmHandle,
|
||||||
callback: fn(*mut ()),
|
callback: fn(*mut ()),
|
||||||
ctx: *mut (),
|
ctx: *mut (),
|
||||||
) {
|
) {
|
||||||
<$t as $crate::time::driver::Driver>::set_alarm_callback(alarm, callback, ctx)
|
<$t as $crate::time::driver::Driver>::set_alarm_callback(&$name, alarm, callback, ctx)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[no_mangle]
|
#[no_mangle]
|
||||||
fn _embassy_time_set_alarm(alarm: AlarmHandle, timestamp: u64) {
|
fn _embassy_time_set_alarm(alarm: $crate::time::driver::AlarmHandle, timestamp: u64) {
|
||||||
<$t as $crate::time::driver::Driver>::set_alarm(alarm, timestamp)
|
<$t as $crate::time::driver::Driver>::set_alarm(&$name, alarm, timestamp)
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
208
embassy/src/time/driver_std.rs
Normal file
208
embassy/src/time/driver_std.rs
Normal file
@ -0,0 +1,208 @@
|
|||||||
|
use atomic_polyfill::{AtomicU8, Ordering};
|
||||||
|
use std::cell::UnsafeCell;
|
||||||
|
use std::mem;
|
||||||
|
use std::mem::MaybeUninit;
|
||||||
|
use std::sync::{Condvar, Mutex, Once};
|
||||||
|
use std::time::Duration as StdDuration;
|
||||||
|
use std::time::Instant as StdInstant;
|
||||||
|
use std::{ptr, thread};
|
||||||
|
|
||||||
|
use crate::time::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() {
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let until =
|
||||||
|
unsafe { DRIVER.zero_instant.read() } + StdDuration::from_micros(next_alarm);
|
||||||
|
|
||||||
|
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())
|
||||||
|
}
|
||||||
|
}
|
@ -11,18 +11,27 @@ pub struct Duration {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl Duration {
|
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 {
|
pub const fn as_ticks(&self) -> u64 {
|
||||||
self.ticks
|
self.ticks
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Convert the `Duration` to seconds, rounding down.
|
||||||
pub const fn as_secs(&self) -> u64 {
|
pub const fn as_secs(&self) -> u64 {
|
||||||
self.ticks / TICKS_PER_SECOND
|
self.ticks / TICKS_PER_SECOND
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Convert the `Duration` to milliseconds, rounding down.
|
||||||
pub const fn as_millis(&self) -> u64 {
|
pub const fn as_millis(&self) -> u64 {
|
||||||
self.ticks * 1000 / TICKS_PER_SECOND
|
self.ticks * 1000 / TICKS_PER_SECOND
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Convert the `Duration` to microseconds, rounding down.
|
||||||
pub const fn as_micros(&self) -> u64 {
|
pub const fn as_micros(&self) -> u64 {
|
||||||
self.ticks * 1_000_000 / TICKS_PER_SECOND
|
self.ticks * 1_000_000 / TICKS_PER_SECOND
|
||||||
}
|
}
|
||||||
|
@ -11,7 +11,9 @@ pub struct Instant {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl Instant {
|
impl Instant {
|
||||||
|
/// The smallest (earliest) value that can be represented by the `Instant` type.
|
||||||
pub const MIN: Instant = Instant { ticks: u64::MIN };
|
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 };
|
pub const MAX: Instant = Instant { ticks: u64::MAX };
|
||||||
|
|
||||||
/// Returns an Instant representing the current time.
|
/// Returns an Instant representing the current time.
|
||||||
@ -21,39 +23,38 @@ impl Instant {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Instant as clock ticks since MCU start.
|
/// Create an Instant from a tick count since system boot.
|
||||||
pub const fn from_ticks(ticks: u64) -> Self {
|
pub const fn from_ticks(ticks: u64) -> Self {
|
||||||
Self { ticks }
|
Self { ticks }
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Instant as milliseconds since MCU start.
|
/// Create an Instant from a millisecond count since system boot.
|
||||||
pub const fn from_millis(millis: u64) -> Self {
|
pub const fn from_millis(millis: u64) -> Self {
|
||||||
Self {
|
Self {
|
||||||
ticks: millis * TICKS_PER_SECOND as u64 / 1000,
|
ticks: millis * TICKS_PER_SECOND / 1000,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Instant representing seconds since MCU start.
|
/// Create an Instant from a second count since system boot.
|
||||||
pub const fn from_secs(seconds: u64) -> Self {
|
pub const fn from_secs(seconds: u64) -> Self {
|
||||||
Self {
|
Self {
|
||||||
ticks: seconds * TICKS_PER_SECOND as u64,
|
ticks: seconds * TICKS_PER_SECOND,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Instant as ticks since MCU start.
|
/// Tick count since system boot.
|
||||||
|
|
||||||
pub const fn as_ticks(&self) -> u64 {
|
pub const fn as_ticks(&self) -> u64 {
|
||||||
self.ticks
|
self.ticks
|
||||||
}
|
}
|
||||||
/// Instant as seconds since MCU start.
|
|
||||||
|
|
||||||
|
/// Seconds since system boot.
|
||||||
pub const fn as_secs(&self) -> u64 {
|
pub const fn as_secs(&self) -> u64 {
|
||||||
self.ticks / TICKS_PER_SECOND as u64
|
self.ticks / TICKS_PER_SECOND
|
||||||
}
|
}
|
||||||
/// Instant as miliseconds since MCU start.
|
|
||||||
|
|
||||||
|
/// Milliseconds since system boot.
|
||||||
pub const fn as_millis(&self) -> u64 {
|
pub const fn as_millis(&self) -> u64 {
|
||||||
self.ticks * 1000 / TICKS_PER_SECOND as u64
|
self.ticks * 1000 / TICKS_PER_SECOND
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Duration between this Instant and another Instant
|
/// Duration between this Instant and another Instant
|
||||||
@ -92,11 +93,14 @@ impl Instant {
|
|||||||
Instant::now() - *self
|
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> {
|
pub fn checked_add(&self, duration: Duration) -> Option<Instant> {
|
||||||
self.ticks
|
self.ticks
|
||||||
.checked_add(duration.ticks)
|
.checked_add(duration.ticks)
|
||||||
.map(|ticks| Instant { 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> {
|
pub fn checked_sub(&self, duration: Duration) -> Option<Instant> {
|
||||||
self.ticks
|
self.ticks
|
||||||
.checked_sub(duration.ticks)
|
.checked_sub(duration.ticks)
|
||||||
|
@ -1,4 +1,44 @@
|
|||||||
//! Time abstractions
|
//! 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.
|
||||||
|
|
||||||
mod delay;
|
mod delay;
|
||||||
pub mod driver;
|
pub mod driver;
|
||||||
@ -6,16 +46,27 @@ mod duration;
|
|||||||
mod instant;
|
mod instant;
|
||||||
mod timer;
|
mod timer;
|
||||||
|
|
||||||
|
#[cfg(feature = "std")]
|
||||||
|
mod driver_std;
|
||||||
|
|
||||||
pub use delay::{block_for, Delay};
|
pub use delay::{block_for, Delay};
|
||||||
pub use duration::Duration;
|
pub use duration::Duration;
|
||||||
pub use instant::Instant;
|
pub use instant::Instant;
|
||||||
pub use timer::{with_timeout, Ticker, TimeoutError, Timer};
|
pub use timer::{with_timeout, Ticker, TimeoutError, Timer};
|
||||||
|
|
||||||
#[cfg(feature = "time-tick-1000hz")]
|
#[cfg(feature = "time-tick-1000hz")]
|
||||||
pub const TICKS_PER_SECOND: u64 = 1_000;
|
const TPS: u64 = 1_000;
|
||||||
|
|
||||||
#[cfg(feature = "time-tick-32768hz")]
|
#[cfg(feature = "time-tick-32768hz")]
|
||||||
pub const TICKS_PER_SECOND: u64 = 32_768;
|
const TPS: u64 = 32_768;
|
||||||
|
|
||||||
#[cfg(feature = "time-tick-1mhz")]
|
#[cfg(feature = "time-tick-1mhz")]
|
||||||
pub const TICKS_PER_SECOND: u64 = 1_000_000;
|
const TPS: u64 = 1_000_000;
|
||||||
|
|
||||||
|
/// Ticks per second of the global timebase.
|
||||||
|
///
|
||||||
|
/// This value is specified by the `time-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 `time-tick-*` features for embassy yourself as an end user.
|
||||||
|
pub const TICKS_PER_SECOND: u64 = TPS;
|
||||||
|
@ -6,7 +6,13 @@ use futures::{future::select, future::Either, pin_mut, Stream};
|
|||||||
use crate::executor::raw;
|
use crate::executor::raw;
|
||||||
use crate::time::{Duration, Instant};
|
use crate::time::{Duration, Instant};
|
||||||
|
|
||||||
|
/// Error returned by [`with_timeout`] on timeout.
|
||||||
pub struct TimeoutError;
|
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> {
|
pub async fn with_timeout<F: Future>(timeout: Duration, fut: F) -> Result<F::Output, TimeoutError> {
|
||||||
let timeout_fut = Timer::after(timeout);
|
let timeout_fut = Timer::after(timeout);
|
||||||
pin_mut!(fut);
|
pin_mut!(fut);
|
||||||
|
@ -5,8 +5,7 @@ name = "embassy-std-examples"
|
|||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
embassy = { version = "0.1.0", path = "../../embassy", features = ["log"] }
|
embassy = { version = "0.1.0", path = "../../embassy", features = ["log", "std", "time"] }
|
||||||
embassy-std = { version = "0.1.0", path = "../../embassy-std" }
|
|
||||||
embassy-net = { version = "0.1.0", path = "../../embassy-net", features=["std", "log", "medium-ethernet", "tcp", "dhcpv4"] }
|
embassy-net = { version = "0.1.0", path = "../../embassy-net", features=["std", "log", "medium-ethernet", "tcp", "dhcpv4"] }
|
||||||
smoltcp = { git = "https://github.com/smoltcp-rs/smoltcp", rev="e4241510337e095b9d21136c5f58b2eaa1b78479", default-features = false }
|
smoltcp = { git = "https://github.com/smoltcp-rs/smoltcp", rev="e4241510337e095b9d21136c5f58b2eaa1b78479", default-features = false }
|
||||||
|
|
||||||
|
@ -2,11 +2,10 @@
|
|||||||
#![allow(incomplete_features)]
|
#![allow(incomplete_features)]
|
||||||
|
|
||||||
use clap::{AppSettings, Clap};
|
use clap::{AppSettings, Clap};
|
||||||
use embassy::executor::Spawner;
|
use embassy::executor::{Executor, Spawner};
|
||||||
use embassy::io::AsyncWriteExt;
|
use embassy::io::AsyncWriteExt;
|
||||||
use embassy::util::Forever;
|
use embassy::util::Forever;
|
||||||
use embassy_net::*;
|
use embassy_net::*;
|
||||||
use embassy_std::Executor;
|
|
||||||
use heapless::Vec;
|
use heapless::Vec;
|
||||||
use log::*;
|
use log::*;
|
||||||
|
|
||||||
|
@ -5,9 +5,9 @@
|
|||||||
mod serial_port;
|
mod serial_port;
|
||||||
|
|
||||||
use async_io::Async;
|
use async_io::Async;
|
||||||
|
use embassy::executor::Executor;
|
||||||
use embassy::io::AsyncBufReadExt;
|
use embassy::io::AsyncBufReadExt;
|
||||||
use embassy::util::Forever;
|
use embassy::util::Forever;
|
||||||
use embassy_std::Executor;
|
|
||||||
use log::*;
|
use log::*;
|
||||||
use nix::sys::termios;
|
use nix::sys::termios;
|
||||||
|
|
||||||
|
@ -1,9 +1,9 @@
|
|||||||
#![feature(type_alias_impl_trait)]
|
#![feature(type_alias_impl_trait)]
|
||||||
#![allow(incomplete_features)]
|
#![allow(incomplete_features)]
|
||||||
|
|
||||||
|
use embassy::executor::Executor;
|
||||||
use embassy::time::{Duration, Timer};
|
use embassy::time::{Duration, Timer};
|
||||||
use embassy::util::Forever;
|
use embassy::util::Forever;
|
||||||
use embassy_std::Executor;
|
|
||||||
use log::*;
|
use log::*;
|
||||||
|
|
||||||
#[embassy::task]
|
#[embassy::task]
|
||||||
|
Loading…
Reference in New Issue
Block a user