mirror of
https://github.com/embassy-rs/embassy.git
synced 2025-09-30 13:50:37 +00:00
Merge pull request #3593 from bugadani/refactor
Rework time-driver contract.
This commit is contained in:
commit
2c3bc75da6
2
.github/ci/test.sh
vendored
2
.github/ci/test.sh
vendored
@ -17,7 +17,7 @@ cargo test --manifest-path ./embassy-futures/Cargo.toml
|
||||
cargo test --manifest-path ./embassy-sync/Cargo.toml
|
||||
cargo test --manifest-path ./embassy-embedded-hal/Cargo.toml
|
||||
cargo test --manifest-path ./embassy-hal-internal/Cargo.toml
|
||||
cargo test --manifest-path ./embassy-time/Cargo.toml --features generic-queue,mock-driver
|
||||
cargo test --manifest-path ./embassy-time/Cargo.toml --features mock-driver,embassy-time-queue-driver/generic-queue-8
|
||||
cargo test --manifest-path ./embassy-time-driver/Cargo.toml
|
||||
|
||||
cargo test --manifest-path ./embassy-boot/Cargo.toml
|
||||
|
@ -13,20 +13,13 @@ cargo batch \
|
||||
--- build --release --manifest-path embassy-executor/Cargo.toml --target thumbv7em-none-eabi --features nightly,log \
|
||||
--- build --release --manifest-path embassy-executor/Cargo.toml --target thumbv7em-none-eabi --features nightly,defmt \
|
||||
--- build --release --manifest-path embassy-executor/Cargo.toml --target thumbv6m-none-eabi --features nightly,defmt \
|
||||
--- build --release --manifest-path embassy-executor/Cargo.toml --target thumbv6m-none-eabi --features nightly,defmt,arch-cortex-m,executor-thread,executor-interrupt,integrated-timers \
|
||||
--- build --release --manifest-path embassy-executor/Cargo.toml --target thumbv6m-none-eabi --features nightly,defmt,arch-cortex-m,executor-thread,executor-interrupt \
|
||||
--- build --release --manifest-path embassy-executor/Cargo.toml --target thumbv7em-none-eabi --features nightly,arch-cortex-m \
|
||||
--- build --release --manifest-path embassy-executor/Cargo.toml --target thumbv7em-none-eabi --features nightly,arch-cortex-m,integrated-timers \
|
||||
--- build --release --manifest-path embassy-executor/Cargo.toml --target thumbv7em-none-eabi --features nightly,arch-cortex-m,executor-thread \
|
||||
--- build --release --manifest-path embassy-executor/Cargo.toml --target thumbv7em-none-eabi --features nightly,arch-cortex-m,executor-thread,integrated-timers \
|
||||
--- build --release --manifest-path embassy-executor/Cargo.toml --target thumbv7em-none-eabi --features nightly,arch-cortex-m,executor-interrupt \
|
||||
--- build --release --manifest-path embassy-executor/Cargo.toml --target thumbv7em-none-eabi --features nightly,arch-cortex-m,executor-interrupt,integrated-timers \
|
||||
--- build --release --manifest-path embassy-executor/Cargo.toml --target thumbv7em-none-eabi --features nightly,arch-cortex-m,executor-thread,executor-interrupt \
|
||||
--- build --release --manifest-path embassy-executor/Cargo.toml --target thumbv7em-none-eabi --features nightly,arch-cortex-m,executor-thread,executor-interrupt,integrated-timers \
|
||||
--- build --release --manifest-path embassy-executor/Cargo.toml --target riscv32imac-unknown-none-elf --features nightly,arch-riscv32 \
|
||||
--- build --release --manifest-path embassy-executor/Cargo.toml --target riscv32imac-unknown-none-elf --features nightly,arch-riscv32,integrated-timers \
|
||||
--- build --release --manifest-path embassy-executor/Cargo.toml --target riscv32imac-unknown-none-elf --features nightly,arch-riscv32,executor-thread \
|
||||
--- build --release --manifest-path embassy-executor/Cargo.toml --target riscv32imac-unknown-none-elf --features nightly,arch-riscv32,executor-thread,integrated-timers \
|
||||
--- build --release --manifest-path examples/nrf52840-rtic/Cargo.toml --target thumbv7em-none-eabi --out-dir out/examples/nrf52840-rtic \
|
||||
|
||||
cargo build --release --manifest-path embassy-executor/Cargo.toml --target avr-unknown-gnu-atmega328 -Z build-std=core,alloc --features nightly,arch-avr,avr-device/atmega328p
|
||||
cargo build --release --manifest-path embassy-executor/Cargo.toml --target avr-unknown-gnu-atmega328 -Z build-std=core,alloc --features nightly,arch-avr,integrated-timers,avr-device/atmega328p
|
||||
|
15
ci-xtensa.sh
15
ci-xtensa.sh
@ -14,17 +14,16 @@ cargo batch \
|
||||
--- build --release --manifest-path embassy-executor/Cargo.toml --target xtensa-esp32-none-elf --features log \
|
||||
--- build --release --manifest-path embassy-executor/Cargo.toml --target xtensa-esp32-none-elf --features defmt \
|
||||
--- build --release --manifest-path embassy-executor/Cargo.toml --target xtensa-esp32s2-none-elf --features defmt \
|
||||
--- build --release --manifest-path embassy-executor/Cargo.toml --target xtensa-esp32-none-elf --features defmt,arch-spin,executor-thread,integrated-timers \
|
||||
--- build --release --manifest-path embassy-executor/Cargo.toml --target xtensa-esp32s2-none-elf --features defmt,arch-spin,executor-thread,integrated-timers \
|
||||
--- build --release --manifest-path embassy-executor/Cargo.toml --target xtensa-esp32s3-none-elf --features defmt,arch-spin,executor-thread,integrated-timers \
|
||||
--- build --release --manifest-path embassy-executor/Cargo.toml --target xtensa-esp32-none-elf --features defmt,arch-spin,executor-thread \
|
||||
--- build --release --manifest-path embassy-executor/Cargo.toml --target xtensa-esp32s2-none-elf --features defmt,arch-spin,executor-thread \
|
||||
--- build --release --manifest-path embassy-executor/Cargo.toml --target xtensa-esp32s3-none-elf --features defmt,arch-spin,executor-thread \
|
||||
--- build --release --manifest-path embassy-executor/Cargo.toml --target xtensa-esp32-none-elf --features arch-spin \
|
||||
--- build --release --manifest-path embassy-executor/Cargo.toml --target xtensa-esp32-none-elf --features arch-spin,integrated-timers \
|
||||
--- build --release --manifest-path embassy-executor/Cargo.toml --target xtensa-esp32-none-elf --features arch-spin,rtos-trace \
|
||||
--- build --release --manifest-path embassy-executor/Cargo.toml --target xtensa-esp32-none-elf --features arch-spin,integrated-timers,rtos-trace \
|
||||
--- build --release --manifest-path embassy-executor/Cargo.toml --target xtensa-esp32-none-elf --features arch-spin,executor-thread \
|
||||
--- build --release --manifest-path embassy-executor/Cargo.toml --target xtensa-esp32-none-elf --features arch-spin,executor-thread,integrated-timers \
|
||||
--- build --release --manifest-path embassy-sync/Cargo.toml --target xtensa-esp32s2-none-elf --features defmt \
|
||||
--- build --release --manifest-path embassy-time/Cargo.toml --target xtensa-esp32s2-none-elf --features defmt,defmt-timestamp-uptime,generic-queue-8,mock-driver \
|
||||
--- build --release --manifest-path embassy-time/Cargo.toml --target xtensa-esp32s2-none-elf --features defmt,defmt-timestamp-uptime,mock-driver \
|
||||
--- build --release --manifest-path embassy-time-queue-driver/Cargo.toml --target xtensa-esp32s2-none-elf \
|
||||
--- build --release --manifest-path embassy-time-queue-driver/Cargo.toml --target xtensa-esp32s2-none-elf --features generic-queue-8 \
|
||||
--- build --release --manifest-path embassy-net/Cargo.toml --target xtensa-esp32-none-elf --features defmt,tcp,udp,dns,proto-ipv4,medium-ethernet,packet-trace \
|
||||
--- build --release --manifest-path embassy-net/Cargo.toml --target xtensa-esp32-none-elf --features defmt,tcp,udp,dns,proto-ipv4,multicast,medium-ethernet \
|
||||
--- build --release --manifest-path embassy-net/Cargo.toml --target xtensa-esp32-none-elf --features defmt,tcp,udp,dns,dhcpv4,medium-ethernet \
|
||||
@ -36,4 +35,4 @@ cargo batch \
|
||||
--- build --release --manifest-path embassy-net/Cargo.toml --target xtensa-esp32-none-elf --features defmt,tcp,udp,dns,proto-ipv4,proto-ipv6,medium-ethernet \
|
||||
--- build --release --manifest-path embassy-net/Cargo.toml --target xtensa-esp32-none-elf --features defmt,tcp,udp,dns,proto-ipv4,proto-ipv6,medium-ip \
|
||||
--- build --release --manifest-path embassy-net/Cargo.toml --target xtensa-esp32-none-elf --features defmt,tcp,udp,dns,proto-ipv4,proto-ipv6,medium-ip,medium-ethernet \
|
||||
--- build --release --manifest-path embassy-net/Cargo.toml --target xtensa-esp32-none-elf --features defmt,tcp,udp,dns,proto-ipv4,proto-ipv6,medium-ip,medium-ethernet,medium-ieee802154 \
|
||||
--- build --release --manifest-path embassy-net/Cargo.toml --target xtensa-esp32-none-elf --features defmt,tcp,udp,dns,proto-ipv4,proto-ipv6,medium-ip,medium-ethernet,medium-ieee802154 \
|
||||
|
16
ci.sh
16
ci.sh
@ -29,23 +29,18 @@ cargo batch \
|
||||
--- build --release --manifest-path embassy-executor/Cargo.toml --target thumbv7em-none-eabi --features log \
|
||||
--- build --release --manifest-path embassy-executor/Cargo.toml --target thumbv7em-none-eabi --features defmt \
|
||||
--- build --release --manifest-path embassy-executor/Cargo.toml --target thumbv6m-none-eabi --features defmt \
|
||||
--- build --release --manifest-path embassy-executor/Cargo.toml --target thumbv6m-none-eabi --features defmt,arch-cortex-m,executor-thread,executor-interrupt,integrated-timers \
|
||||
--- build --release --manifest-path embassy-executor/Cargo.toml --target thumbv6m-none-eabi --features defmt,arch-cortex-m,executor-thread,executor-interrupt \
|
||||
--- build --release --manifest-path embassy-executor/Cargo.toml --target thumbv7em-none-eabi --features arch-cortex-m \
|
||||
--- build --release --manifest-path embassy-executor/Cargo.toml --target thumbv7em-none-eabi --features arch-cortex-m,integrated-timers \
|
||||
--- build --release --manifest-path embassy-executor/Cargo.toml --target thumbv7em-none-eabi --features arch-cortex-m,rtos-trace \
|
||||
--- build --release --manifest-path embassy-executor/Cargo.toml --target thumbv7em-none-eabi --features arch-cortex-m,integrated-timers,rtos-trace \
|
||||
--- build --release --manifest-path embassy-executor/Cargo.toml --target thumbv7em-none-eabi --features arch-cortex-m,executor-thread \
|
||||
--- build --release --manifest-path embassy-executor/Cargo.toml --target thumbv7em-none-eabi --features arch-cortex-m,executor-thread,integrated-timers \
|
||||
--- build --release --manifest-path embassy-executor/Cargo.toml --target thumbv7em-none-eabi --features arch-cortex-m,executor-interrupt \
|
||||
--- build --release --manifest-path embassy-executor/Cargo.toml --target thumbv7em-none-eabi --features arch-cortex-m,executor-interrupt,integrated-timers \
|
||||
--- build --release --manifest-path embassy-executor/Cargo.toml --target thumbv7em-none-eabi --features arch-cortex-m,executor-thread,executor-interrupt \
|
||||
--- build --release --manifest-path embassy-executor/Cargo.toml --target thumbv7em-none-eabi --features arch-cortex-m,executor-thread,executor-interrupt,integrated-timers \
|
||||
--- build --release --manifest-path embassy-executor/Cargo.toml --target riscv32imac-unknown-none-elf --features arch-riscv32 \
|
||||
--- build --release --manifest-path embassy-executor/Cargo.toml --target riscv32imac-unknown-none-elf --features arch-riscv32,integrated-timers \
|
||||
--- build --release --manifest-path embassy-executor/Cargo.toml --target riscv32imac-unknown-none-elf --features arch-riscv32,executor-thread \
|
||||
--- build --release --manifest-path embassy-executor/Cargo.toml --target riscv32imac-unknown-none-elf --features arch-riscv32,executor-thread,integrated-timers \
|
||||
--- build --release --manifest-path embassy-sync/Cargo.toml --target thumbv6m-none-eabi --features defmt \
|
||||
--- build --release --manifest-path embassy-time/Cargo.toml --target thumbv6m-none-eabi --features defmt,defmt-timestamp-uptime,generic-queue-8,mock-driver \
|
||||
--- build --release --manifest-path embassy-time/Cargo.toml --target thumbv6m-none-eabi --features defmt,defmt-timestamp-uptime,mock-driver \
|
||||
--- build --release --manifest-path embassy-time-queue-driver/Cargo.toml --target thumbv6m-none-eabi \
|
||||
--- build --release --manifest-path embassy-time-queue-driver/Cargo.toml --target thumbv6m-none-eabi --features generic-queue-8 \
|
||||
--- build --release --manifest-path embassy-net/Cargo.toml --target thumbv7em-none-eabi --features defmt,tcp,udp,dns,proto-ipv4,medium-ethernet,packet-trace \
|
||||
--- build --release --manifest-path embassy-net/Cargo.toml --target thumbv7em-none-eabi --features defmt,tcp,udp,dns,proto-ipv4,multicast,medium-ethernet \
|
||||
--- build --release --manifest-path embassy-net/Cargo.toml --target thumbv7em-none-eabi --features defmt,tcp,udp,dns,dhcpv4,medium-ethernet \
|
||||
@ -307,6 +302,9 @@ rm out/tests/stm32wb55rg/wpan_ble
|
||||
# unstable, I think it's running out of RAM?
|
||||
rm out/tests/stm32f207zg/eth
|
||||
|
||||
# temporarily disabled, hard faults for unknown reasons
|
||||
rm out/tests/stm32f207zg/usart_rx_ringbuffered
|
||||
|
||||
# doesn't work, gives "noise error", no idea why. usart_dma does pass.
|
||||
rm out/tests/stm32u5a5zj/usart
|
||||
|
||||
|
@ -6,7 +6,7 @@ version = "0.1.0"
|
||||
license = "MIT OR Apache-2.0"
|
||||
|
||||
[dependencies]
|
||||
embassy-executor = { version = "0.6.3", path = "../../../embassy-executor", features = ["defmt", "integrated-timers", "arch-cortex-m", "executor-thread"] }
|
||||
embassy-executor = { version = "0.6.3", path = "../../../embassy-executor", features = ["defmt", "arch-cortex-m", "executor-thread"] }
|
||||
embassy-time = { version = "0.3.2", path = "../../../embassy-time", features = ["defmt"] }
|
||||
embassy-nrf = { version = "0.2.0", path = "../../../embassy-nrf", features = ["defmt", "nrf52840", "time-driver-rtc1", "gpiote"] }
|
||||
|
||||
|
@ -80,7 +80,7 @@ At the time of writing, embassy is already published to crates.io. Therefore, de
|
||||
----
|
||||
[dependencies]
|
||||
embassy-stm32 = { version = "0.1.0", features = ["defmt", "time-driver-any", "stm32g474re", "memory-x", "unstable-pac", "exti"] }
|
||||
embassy-executor = { version = "0.6.3", features = ["nightly", "arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] }
|
||||
embassy-executor = { version = "0.6.3", features = ["nightly", "arch-cortex-m", "executor-thread", "defmt"] }
|
||||
embassy-time = { version = "0.3.2", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] }
|
||||
----
|
||||
|
||||
@ -100,7 +100,7 @@ An example Cargo.toml file might look as follows:
|
||||
----
|
||||
[dependencies]
|
||||
embassy-stm32 = {version = "0.1.0", features = ["defmt", "time-driver-any", "stm32g474re", "memory-x", "unstable-pac", "exti"]}
|
||||
embassy-executor = { version = "0.3.3", features = ["nightly", "arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] }
|
||||
embassy-executor = { version = "0.3.3", features = ["nightly", "arch-cortex-m", "executor-thread", "defmt"] }
|
||||
embassy-time = { version = "0.2", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] }
|
||||
|
||||
[patch.crates-io]
|
||||
|
@ -7,7 +7,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
||||
|
||||
## Unreleased
|
||||
|
||||
- `raw::Executor` now has an `fn initialize` that must be called once before starting to poll it.
|
||||
- embassy-executor no longer provides an `embassy-time-queue-driver` implementation
|
||||
- Added `TaskRef::executor` to obtain a reference to a task's executor
|
||||
- integrated-timers are no longer processed when polling the executor.
|
||||
|
||||
## 0.6.3 - 2024-11-12
|
||||
|
||||
|
@ -35,7 +35,6 @@ rtos-trace = { version = "0.1.3", optional = true }
|
||||
|
||||
embassy-executor-macros = { version = "0.6.2", path = "../embassy-executor-macros" }
|
||||
embassy-time-driver = { version = "0.1.0", path = "../embassy-time-driver", optional = true }
|
||||
embassy-time-queue-driver = { version = "0.1.0", path = "../embassy-time-queue-driver", optional = true }
|
||||
critical-section = "1.1"
|
||||
|
||||
document-features = "0.2.7"
|
||||
@ -67,9 +66,6 @@ nightly = ["embassy-executor-macros/nightly"]
|
||||
# See: https://github.com/embassy-rs/embassy/pull/1263
|
||||
turbowakers = []
|
||||
|
||||
## Use the executor-integrated `embassy-time` timer queue.
|
||||
integrated-timers = ["dep:embassy-time-driver", "dep:embassy-time-queue-driver"]
|
||||
|
||||
#! ### Architecture
|
||||
_arch = [] # some arch was picked
|
||||
## std
|
||||
@ -94,7 +90,7 @@ executor-interrupt = []
|
||||
## Enable tracing support (adds some overhead)
|
||||
trace = []
|
||||
## Enable support for rtos-trace framework
|
||||
rtos-trace = ["dep:rtos-trace", "trace"]
|
||||
rtos-trace = ["dep:rtos-trace", "trace", "dep:embassy-time-driver"]
|
||||
|
||||
#! ### Task Arena Size
|
||||
#! Sets the [task arena](#task-arena) size. Necessary if you’re not using `nightly`.
|
||||
|
@ -53,10 +53,6 @@ mod thread {
|
||||
///
|
||||
/// This function never returns.
|
||||
pub fn run(&'static mut self, init: impl FnOnce(Spawner)) -> ! {
|
||||
unsafe {
|
||||
self.inner.initialize();
|
||||
}
|
||||
|
||||
init(self.inner.spawner());
|
||||
|
||||
loop {
|
||||
|
@ -98,9 +98,6 @@ mod thread {
|
||||
///
|
||||
/// This function never returns.
|
||||
pub fn run(&'static mut self, init: impl FnOnce(Spawner)) -> ! {
|
||||
unsafe {
|
||||
self.inner.initialize();
|
||||
}
|
||||
init(self.inner.spawner());
|
||||
|
||||
loop {
|
||||
@ -210,9 +207,6 @@ mod interrupt {
|
||||
}
|
||||
|
||||
let executor = unsafe { (&*self.executor.get()).assume_init_ref() };
|
||||
unsafe {
|
||||
executor.initialize();
|
||||
}
|
||||
|
||||
unsafe { NVIC::unmask(irq) }
|
||||
|
||||
|
@ -54,10 +54,6 @@ mod thread {
|
||||
///
|
||||
/// This function never returns.
|
||||
pub fn run(&'static mut self, init: impl FnOnce(Spawner)) -> ! {
|
||||
unsafe {
|
||||
self.inner.initialize();
|
||||
}
|
||||
|
||||
init(self.inner.spawner());
|
||||
|
||||
loop {
|
||||
|
@ -48,10 +48,6 @@ mod thread {
|
||||
///
|
||||
/// This function never returns.
|
||||
pub fn run(&'static mut self, init: impl FnOnce(Spawner)) -> ! {
|
||||
unsafe {
|
||||
self.inner.initialize();
|
||||
}
|
||||
|
||||
init(self.inner.spawner());
|
||||
|
||||
loop {
|
||||
|
@ -55,10 +55,6 @@ mod thread {
|
||||
///
|
||||
/// This function never returns.
|
||||
pub fn run(&'static mut self, init: impl FnOnce(Spawner)) -> ! {
|
||||
unsafe {
|
||||
self.inner.initialize();
|
||||
}
|
||||
|
||||
init(self.inner.spawner());
|
||||
|
||||
loop {
|
||||
|
@ -70,10 +70,6 @@ mod thread {
|
||||
/// - a `static mut` (unsafe)
|
||||
/// - a local variable in a function you know never returns (like `fn main() -> !`), upgrading its lifetime with `transmute`. (unsafe)
|
||||
pub fn start(&'static mut self, init: impl FnOnce(Spawner)) {
|
||||
unsafe {
|
||||
self.inner.initialize();
|
||||
}
|
||||
|
||||
unsafe {
|
||||
let executor = &self.inner;
|
||||
let future = Closure::new(move |_| {
|
||||
|
@ -16,8 +16,7 @@ mod run_queue;
|
||||
#[cfg_attr(not(target_has_atomic = "8"), path = "state_critical_section.rs")]
|
||||
mod state;
|
||||
|
||||
#[cfg(feature = "integrated-timers")]
|
||||
mod timer_queue;
|
||||
pub mod timer_queue;
|
||||
#[cfg(feature = "trace")]
|
||||
mod trace;
|
||||
pub(crate) mod util;
|
||||
@ -31,9 +30,6 @@ use core::pin::Pin;
|
||||
use core::ptr::NonNull;
|
||||
use core::task::{Context, Poll};
|
||||
|
||||
#[cfg(feature = "integrated-timers")]
|
||||
use embassy_time_driver::AlarmHandle;
|
||||
|
||||
use self::run_queue::{RunQueue, RunQueueItem};
|
||||
use self::state::State;
|
||||
use self::util::{SyncUnsafeCell, UninitCell};
|
||||
@ -47,14 +43,12 @@ pub(crate) struct TaskHeader {
|
||||
pub(crate) executor: SyncUnsafeCell<Option<&'static SyncExecutor>>,
|
||||
poll_fn: SyncUnsafeCell<Option<unsafe fn(TaskRef)>>,
|
||||
|
||||
#[cfg(feature = "integrated-timers")]
|
||||
pub(crate) expires_at: SyncUnsafeCell<u64>,
|
||||
#[cfg(feature = "integrated-timers")]
|
||||
/// Integrated timer queue storage. This field should not be accessed outside of the timer queue.
|
||||
pub(crate) timer_queue_item: timer_queue::TimerQueueItem,
|
||||
}
|
||||
|
||||
/// This is essentially a `&'static TaskStorage<F>` where the type of the future has been erased.
|
||||
#[derive(Clone, Copy)]
|
||||
#[derive(Clone, Copy, PartialEq)]
|
||||
pub struct TaskRef {
|
||||
ptr: NonNull<TaskHeader>,
|
||||
}
|
||||
@ -76,10 +70,53 @@ impl TaskRef {
|
||||
}
|
||||
}
|
||||
|
||||
/// # Safety
|
||||
///
|
||||
/// The result of this function must only be compared
|
||||
/// for equality, or stored, but not used.
|
||||
pub const unsafe fn dangling() -> Self {
|
||||
Self {
|
||||
ptr: NonNull::dangling(),
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn header(self) -> &'static TaskHeader {
|
||||
unsafe { self.ptr.as_ref() }
|
||||
}
|
||||
|
||||
/// Returns a reference to the executor that the task is currently running on.
|
||||
pub unsafe fn executor(self) -> Option<&'static Executor> {
|
||||
self.header().executor.get().map(|e| Executor::wrap(e))
|
||||
}
|
||||
|
||||
/// Returns a reference to the timer queue item.
|
||||
pub fn timer_queue_item(&self) -> &'static timer_queue::TimerQueueItem {
|
||||
&self.header().timer_queue_item
|
||||
}
|
||||
|
||||
/// Mark the task as timer-queued. Return whether it should be actually enqueued
|
||||
/// using `_embassy_time_schedule_wake`.
|
||||
///
|
||||
/// Entering this state prevents the task from being respawned while in a timer queue.
|
||||
///
|
||||
/// Safety:
|
||||
///
|
||||
/// This functions should only be called by the timer queue driver, before
|
||||
/// enqueueing the timer item.
|
||||
pub unsafe fn timer_enqueue(&self) -> timer_queue::TimerEnqueueOperation {
|
||||
self.header().state.timer_enqueue()
|
||||
}
|
||||
|
||||
/// Unmark the task as timer-queued.
|
||||
///
|
||||
/// Safety:
|
||||
///
|
||||
/// This functions should only be called by the timer queue implementation, after the task has
|
||||
/// been removed from the timer queue.
|
||||
pub unsafe fn timer_dequeue(&self) {
|
||||
self.header().state.timer_dequeue()
|
||||
}
|
||||
|
||||
/// The returned pointer is valid for the entire TaskStorage.
|
||||
pub(crate) fn as_ptr(self) -> *const TaskHeader {
|
||||
self.ptr.as_ptr()
|
||||
@ -120,9 +157,6 @@ impl<F: Future + 'static> TaskStorage<F> {
|
||||
// Note: this is lazily initialized so that a static `TaskStorage` will go in `.bss`
|
||||
poll_fn: SyncUnsafeCell::new(None),
|
||||
|
||||
#[cfg(feature = "integrated-timers")]
|
||||
expires_at: SyncUnsafeCell::new(0),
|
||||
#[cfg(feature = "integrated-timers")]
|
||||
timer_queue_item: timer_queue::TimerQueueItem::new(),
|
||||
},
|
||||
future: UninitCell::uninit(),
|
||||
@ -159,10 +193,25 @@ impl<F: Future + 'static> TaskStorage<F> {
|
||||
match future.poll(&mut cx) {
|
||||
Poll::Ready(_) => {
|
||||
this.future.drop_in_place();
|
||||
|
||||
// Mark this task to be timer queued.
|
||||
// We're splitting the enqueue in two parts, so that we can change task state
|
||||
// to something that prevent re-queueing.
|
||||
let op = this.raw.state.timer_enqueue();
|
||||
|
||||
// Now mark the task as not spawned, so that
|
||||
// - it can be spawned again once it has been removed from the timer queue
|
||||
// - it can not be timer-queued again
|
||||
// We must do this before scheduling the wake, to prevent the task from being
|
||||
// dequeued by the time driver while it's still SPAWNED.
|
||||
this.raw.state.despawn();
|
||||
|
||||
#[cfg(feature = "integrated-timers")]
|
||||
this.raw.expires_at.set(u64::MAX);
|
||||
// Now let's finish enqueueing. While we shouldn't get an `Ignore` here, it's
|
||||
// better to be safe.
|
||||
if op == timer_queue::TimerEnqueueOperation::Enqueue {
|
||||
// Schedule the task in the past, so it gets dequeued ASAP.
|
||||
unsafe { _embassy_time_schedule_wake(0, &waker) }
|
||||
}
|
||||
}
|
||||
Poll::Pending => {}
|
||||
}
|
||||
@ -181,6 +230,10 @@ impl<F: Future + 'static> TaskStorage<F> {
|
||||
}
|
||||
}
|
||||
|
||||
extern "Rust" {
|
||||
fn _embassy_time_schedule_wake(at: u64, waker: &core::task::Waker);
|
||||
}
|
||||
|
||||
/// An uninitialized [`TaskStorage`].
|
||||
pub struct AvailableTask<F: Future + 'static> {
|
||||
task: &'static TaskStorage<F>,
|
||||
@ -316,34 +369,16 @@ impl Pender {
|
||||
pub(crate) struct SyncExecutor {
|
||||
run_queue: RunQueue,
|
||||
pender: Pender,
|
||||
|
||||
#[cfg(feature = "integrated-timers")]
|
||||
pub(crate) timer_queue: timer_queue::TimerQueue,
|
||||
#[cfg(feature = "integrated-timers")]
|
||||
alarm: AlarmHandle,
|
||||
}
|
||||
|
||||
impl SyncExecutor {
|
||||
pub(crate) fn new(pender: Pender) -> Self {
|
||||
#[cfg(feature = "integrated-timers")]
|
||||
let alarm = unsafe { unwrap!(embassy_time_driver::allocate_alarm()) };
|
||||
|
||||
Self {
|
||||
run_queue: RunQueue::new(),
|
||||
pender,
|
||||
|
||||
#[cfg(feature = "integrated-timers")]
|
||||
timer_queue: timer_queue::TimerQueue::new(),
|
||||
#[cfg(feature = "integrated-timers")]
|
||||
alarm,
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) unsafe fn initialize(&'static self) {
|
||||
#[cfg(feature = "integrated-timers")]
|
||||
embassy_time_driver::set_alarm_callback(self.alarm, Self::alarm_callback, self as *const _ as *mut ());
|
||||
}
|
||||
|
||||
/// Enqueue a task in the task queue
|
||||
///
|
||||
/// # Safety
|
||||
@ -360,12 +395,6 @@ impl SyncExecutor {
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "integrated-timers")]
|
||||
fn alarm_callback(ctx: *mut ()) {
|
||||
let this: &Self = unsafe { &*(ctx as *const Self) };
|
||||
this.pender.pend();
|
||||
}
|
||||
|
||||
pub(super) unsafe fn spawn(&'static self, task: TaskRef) {
|
||||
task.header().executor.set(Some(self));
|
||||
|
||||
@ -379,56 +408,27 @@ impl SyncExecutor {
|
||||
///
|
||||
/// Same as [`Executor::poll`], plus you must only call this on the thread this executor was created.
|
||||
pub(crate) unsafe fn poll(&'static self) {
|
||||
#[allow(clippy::never_loop)]
|
||||
loop {
|
||||
#[cfg(feature = "integrated-timers")]
|
||||
self.timer_queue
|
||||
.dequeue_expired(embassy_time_driver::now(), wake_task_no_pend);
|
||||
self.run_queue.dequeue_all(|p| {
|
||||
let task = p.header();
|
||||
|
||||
self.run_queue.dequeue_all(|p| {
|
||||
let task = p.header();
|
||||
|
||||
#[cfg(feature = "integrated-timers")]
|
||||
task.expires_at.set(u64::MAX);
|
||||
|
||||
if !task.state.run_dequeue() {
|
||||
// If task is not running, ignore it. This can happen in the following scenario:
|
||||
// - Task gets dequeued, poll starts
|
||||
// - While task is being polled, it gets woken. It gets placed in the queue.
|
||||
// - Task poll finishes, returning done=true
|
||||
// - RUNNING bit is cleared, but the task is already in the queue.
|
||||
return;
|
||||
}
|
||||
|
||||
#[cfg(feature = "trace")]
|
||||
trace::task_exec_begin(self, &p);
|
||||
|
||||
// Run the task
|
||||
task.poll_fn.get().unwrap_unchecked()(p);
|
||||
|
||||
#[cfg(feature = "trace")]
|
||||
trace::task_exec_end(self, &p);
|
||||
|
||||
// Enqueue or update into timer_queue
|
||||
#[cfg(feature = "integrated-timers")]
|
||||
self.timer_queue.update(p);
|
||||
});
|
||||
|
||||
#[cfg(feature = "integrated-timers")]
|
||||
{
|
||||
// If this is already in the past, set_alarm might return false
|
||||
// In that case do another poll loop iteration.
|
||||
let next_expiration = self.timer_queue.next_expiration();
|
||||
if embassy_time_driver::set_alarm(self.alarm, next_expiration) {
|
||||
break;
|
||||
}
|
||||
if !task.state.run_dequeue() {
|
||||
// If task is not running, ignore it. This can happen in the following scenario:
|
||||
// - Task gets dequeued, poll starts
|
||||
// - While task is being polled, it gets woken. It gets placed in the queue.
|
||||
// - Task poll finishes, returning done=true
|
||||
// - RUNNING bit is cleared, but the task is already in the queue.
|
||||
return;
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "integrated-timers"))]
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
#[cfg(feature = "trace")]
|
||||
trace::task_exec_begin(self, &p);
|
||||
|
||||
// Run the task
|
||||
task.poll_fn.get().unwrap_unchecked()(p);
|
||||
|
||||
#[cfg(feature = "trace")]
|
||||
trace::task_exec_end(self, &p);
|
||||
});
|
||||
|
||||
#[cfg(feature = "trace")]
|
||||
trace::executor_idle(self)
|
||||
@ -494,15 +494,6 @@ impl Executor {
|
||||
}
|
||||
}
|
||||
|
||||
/// Initializes the executor.
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// This function must be called once before any other method is called.
|
||||
pub unsafe fn initialize(&'static self) {
|
||||
self.inner.initialize();
|
||||
}
|
||||
|
||||
/// Spawn a task in this executor.
|
||||
///
|
||||
/// # Safety
|
||||
@ -575,21 +566,3 @@ pub fn wake_task_no_pend(task: TaskRef) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "integrated-timers")]
|
||||
struct TimerQueue;
|
||||
|
||||
#[cfg(feature = "integrated-timers")]
|
||||
impl embassy_time_queue_driver::TimerQueue for TimerQueue {
|
||||
fn schedule_wake(&'static self, at: u64, waker: &core::task::Waker) {
|
||||
let task = waker::task_from_waker(waker);
|
||||
let task = task.header();
|
||||
unsafe {
|
||||
let expires_at = task.expires_at.get();
|
||||
task.expires_at.set(expires_at.min(at));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "integrated-timers")]
|
||||
embassy_time_queue_driver::timer_queue_impl!(static TIMER_QUEUE: TimerQueue = TimerQueue);
|
||||
|
@ -1,11 +1,12 @@
|
||||
use core::sync::atomic::{AtomicU32, Ordering};
|
||||
|
||||
use super::timer_queue::TimerEnqueueOperation;
|
||||
|
||||
/// Task is spawned (has a future)
|
||||
pub(crate) const STATE_SPAWNED: u32 = 1 << 0;
|
||||
/// Task is in the executor run queue
|
||||
pub(crate) const STATE_RUN_QUEUED: u32 = 1 << 1;
|
||||
/// Task is in the executor timer queue
|
||||
#[cfg(feature = "integrated-timers")]
|
||||
pub(crate) const STATE_TIMER_QUEUED: u32 = 1 << 2;
|
||||
|
||||
pub(crate) struct State {
|
||||
@ -56,18 +57,31 @@ impl State {
|
||||
state & STATE_SPAWNED != 0
|
||||
}
|
||||
|
||||
/// Mark the task as timer-queued. Return whether it was newly queued (i.e. not queued before)
|
||||
#[cfg(feature = "integrated-timers")]
|
||||
/// Mark the task as timer-queued. Return whether it can be enqueued.
|
||||
#[inline(always)]
|
||||
pub fn timer_enqueue(&self) -> bool {
|
||||
let old_state = self.state.fetch_or(STATE_TIMER_QUEUED, Ordering::AcqRel);
|
||||
old_state & STATE_TIMER_QUEUED == 0
|
||||
pub fn timer_enqueue(&self) -> TimerEnqueueOperation {
|
||||
if self
|
||||
.state
|
||||
.fetch_update(Ordering::SeqCst, Ordering::SeqCst, |state| {
|
||||
// If not started, ignore it
|
||||
if state & STATE_SPAWNED == 0 {
|
||||
None
|
||||
} else {
|
||||
// Mark it as enqueued
|
||||
Some(state | STATE_TIMER_QUEUED)
|
||||
}
|
||||
})
|
||||
.is_ok()
|
||||
{
|
||||
TimerEnqueueOperation::Enqueue
|
||||
} else {
|
||||
TimerEnqueueOperation::Ignore
|
||||
}
|
||||
}
|
||||
|
||||
/// Unmark the task as timer-queued.
|
||||
#[cfg(feature = "integrated-timers")]
|
||||
#[inline(always)]
|
||||
pub fn timer_dequeue(&self) {
|
||||
self.state.fetch_and(!STATE_TIMER_QUEUED, Ordering::AcqRel);
|
||||
self.state.fetch_and(!STATE_TIMER_QUEUED, Ordering::Relaxed);
|
||||
}
|
||||
}
|
||||
|
@ -1,9 +1,12 @@
|
||||
use core::arch::asm;
|
||||
use core::sync::atomic::{compiler_fence, AtomicBool, AtomicU32, Ordering};
|
||||
|
||||
use super::timer_queue::TimerEnqueueOperation;
|
||||
|
||||
// Must be kept in sync with the layout of `State`!
|
||||
pub(crate) const STATE_SPAWNED: u32 = 1 << 0;
|
||||
pub(crate) const STATE_RUN_QUEUED: u32 = 1 << 8;
|
||||
pub(crate) const STATE_TIMER_QUEUED: u32 = 1 << 16;
|
||||
|
||||
#[repr(C, align(4))]
|
||||
pub(crate) struct State {
|
||||
@ -87,15 +90,29 @@ impl State {
|
||||
r
|
||||
}
|
||||
|
||||
/// Mark the task as timer-queued. Return whether it was newly queued (i.e. not queued before)
|
||||
#[cfg(feature = "integrated-timers")]
|
||||
/// Mark the task as timer-queued. Return whether it can be enqueued.
|
||||
#[inline(always)]
|
||||
pub fn timer_enqueue(&self) -> bool {
|
||||
!self.timer_queued.swap(true, Ordering::Relaxed)
|
||||
pub fn timer_enqueue(&self) -> TimerEnqueueOperation {
|
||||
if self
|
||||
.as_u32()
|
||||
.fetch_update(Ordering::SeqCst, Ordering::SeqCst, |state| {
|
||||
// If not started, ignore it
|
||||
if state & STATE_SPAWNED == 0 {
|
||||
None
|
||||
} else {
|
||||
// Mark it as enqueued
|
||||
Some(state | STATE_TIMER_QUEUED)
|
||||
}
|
||||
})
|
||||
.is_ok()
|
||||
{
|
||||
TimerEnqueueOperation::Enqueue
|
||||
} else {
|
||||
TimerEnqueueOperation::Ignore
|
||||
}
|
||||
}
|
||||
|
||||
/// Unmark the task as timer-queued.
|
||||
#[cfg(feature = "integrated-timers")]
|
||||
#[inline(always)]
|
||||
pub fn timer_dequeue(&self) {
|
||||
self.timer_queued.store(false, Ordering::Relaxed);
|
||||
|
@ -2,12 +2,13 @@ use core::cell::Cell;
|
||||
|
||||
use critical_section::Mutex;
|
||||
|
||||
use super::timer_queue::TimerEnqueueOperation;
|
||||
|
||||
/// Task is spawned (has a future)
|
||||
pub(crate) const STATE_SPAWNED: u32 = 1 << 0;
|
||||
/// Task is in the executor run queue
|
||||
pub(crate) const STATE_RUN_QUEUED: u32 = 1 << 1;
|
||||
/// Task is in the executor timer queue
|
||||
#[cfg(feature = "integrated-timers")]
|
||||
pub(crate) const STATE_TIMER_QUEUED: u32 = 1 << 2;
|
||||
|
||||
pub(crate) struct State {
|
||||
@ -73,19 +74,22 @@ impl State {
|
||||
})
|
||||
}
|
||||
|
||||
/// Mark the task as timer-queued. Return whether it was newly queued (i.e. not queued before)
|
||||
#[cfg(feature = "integrated-timers")]
|
||||
/// Mark the task as timer-queued. Return whether it can be enqueued.
|
||||
#[inline(always)]
|
||||
pub fn timer_enqueue(&self) -> bool {
|
||||
pub fn timer_enqueue(&self) -> TimerEnqueueOperation {
|
||||
self.update(|s| {
|
||||
let ok = *s & STATE_TIMER_QUEUED == 0;
|
||||
*s |= STATE_TIMER_QUEUED;
|
||||
ok
|
||||
// FIXME: we need to split SPAWNED into two phases, to prevent enqueueing a task that is
|
||||
// just being spawned, because its executor pointer may still be changing.
|
||||
if *s & STATE_SPAWNED == STATE_SPAWNED {
|
||||
*s |= STATE_TIMER_QUEUED;
|
||||
TimerEnqueueOperation::Enqueue
|
||||
} else {
|
||||
TimerEnqueueOperation::Ignore
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
/// Unmark the task as timer-queued.
|
||||
#[cfg(feature = "integrated-timers")]
|
||||
#[inline(always)]
|
||||
pub fn timer_dequeue(&self) {
|
||||
self.update(|s| *s &= !STATE_TIMER_QUEUED);
|
||||
|
@ -1,76 +1,39 @@
|
||||
use core::cmp::min;
|
||||
//! Timer queue operations.
|
||||
|
||||
use core::cell::Cell;
|
||||
|
||||
use super::TaskRef;
|
||||
use crate::raw::util::SyncUnsafeCell;
|
||||
|
||||
pub(crate) struct TimerQueueItem {
|
||||
next: SyncUnsafeCell<Option<TaskRef>>,
|
||||
/// An item in the timer queue.
|
||||
pub struct TimerQueueItem {
|
||||
/// The next item in the queue.
|
||||
///
|
||||
/// If this field contains `Some`, the item is in the queue. The last item in the queue has a
|
||||
/// value of `Some(dangling_pointer)`
|
||||
pub next: Cell<Option<TaskRef>>,
|
||||
|
||||
/// The time at which this item expires.
|
||||
pub expires_at: Cell<u64>,
|
||||
}
|
||||
|
||||
unsafe impl Sync for TimerQueueItem {}
|
||||
|
||||
impl TimerQueueItem {
|
||||
pub const fn new() -> Self {
|
||||
pub(crate) const fn new() -> Self {
|
||||
Self {
|
||||
next: SyncUnsafeCell::new(None),
|
||||
next: Cell::new(None),
|
||||
expires_at: Cell::new(0),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) struct TimerQueue {
|
||||
head: SyncUnsafeCell<Option<TaskRef>>,
|
||||
}
|
||||
|
||||
impl TimerQueue {
|
||||
pub const fn new() -> Self {
|
||||
Self {
|
||||
head: SyncUnsafeCell::new(None),
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) unsafe fn update(&self, p: TaskRef) {
|
||||
let task = p.header();
|
||||
if task.expires_at.get() != u64::MAX {
|
||||
if task.state.timer_enqueue() {
|
||||
task.timer_queue_item.next.set(self.head.get());
|
||||
self.head.set(Some(p));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) unsafe fn next_expiration(&self) -> u64 {
|
||||
let mut res = u64::MAX;
|
||||
self.retain(|p| {
|
||||
let task = p.header();
|
||||
let expires = task.expires_at.get();
|
||||
res = min(res, expires);
|
||||
expires != u64::MAX
|
||||
});
|
||||
res
|
||||
}
|
||||
|
||||
pub(crate) unsafe fn dequeue_expired(&self, now: u64, on_task: impl Fn(TaskRef)) {
|
||||
self.retain(|p| {
|
||||
let task = p.header();
|
||||
if task.expires_at.get() <= now {
|
||||
on_task(p);
|
||||
false
|
||||
} else {
|
||||
true
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
pub(crate) unsafe fn retain(&self, mut f: impl FnMut(TaskRef) -> bool) {
|
||||
let mut prev = &self.head;
|
||||
while let Some(p) = prev.get() {
|
||||
let task = p.header();
|
||||
if f(p) {
|
||||
// Skip to next
|
||||
prev = &task.timer_queue_item.next;
|
||||
} else {
|
||||
// Remove it
|
||||
prev.set(task.timer_queue_item.next.get());
|
||||
task.state.timer_dequeue();
|
||||
}
|
||||
}
|
||||
}
|
||||
/// The operation to perform after `timer_enqueue` is called.
|
||||
#[derive(Debug, Copy, Clone, PartialEq)]
|
||||
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||
#[must_use]
|
||||
pub enum TimerEnqueueOperation {
|
||||
/// Enqueue the task (or update its expiration time).
|
||||
Enqueue,
|
||||
/// The task must not be enqueued in the timer queue.
|
||||
Ignore,
|
||||
}
|
||||
|
@ -61,29 +61,23 @@ pub(crate) fn executor_idle(executor: &SyncExecutor) {
|
||||
rtos_trace::trace::system_idle();
|
||||
}
|
||||
|
||||
#[cfg(all(feature = "rtos-trace", feature = "integrated-timers"))]
|
||||
const fn gcd(a: u64, b: u64) -> u64 {
|
||||
if b == 0 {
|
||||
a
|
||||
} else {
|
||||
gcd(b, a % b)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "rtos-trace")]
|
||||
impl rtos_trace::RtosTraceOSCallbacks for crate::raw::SyncExecutor {
|
||||
fn task_list() {
|
||||
// We don't know what tasks exist, so we can't send them.
|
||||
}
|
||||
#[cfg(feature = "integrated-timers")]
|
||||
fn time() -> u64 {
|
||||
const fn gcd(a: u64, b: u64) -> u64 {
|
||||
if b == 0 {
|
||||
a
|
||||
} else {
|
||||
gcd(b, a % b)
|
||||
}
|
||||
}
|
||||
|
||||
const GCD_1M: u64 = gcd(embassy_time_driver::TICK_HZ, 1_000_000);
|
||||
embassy_time_driver::now() * (1_000_000 / GCD_1M) / (embassy_time_driver::TICK_HZ / GCD_1M)
|
||||
}
|
||||
#[cfg(not(feature = "integrated-timers"))]
|
||||
fn time() -> u64 {
|
||||
0
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "rtos-trace")]
|
||||
|
@ -40,9 +40,6 @@ fn setup() -> (&'static Executor, Trace) {
|
||||
let trace = Trace::new();
|
||||
let context = Box::leak(Box::new(trace.clone())) as *mut _ as *mut ();
|
||||
let executor = &*Box::leak(Box::new(Executor::new(context)));
|
||||
unsafe {
|
||||
executor.initialize();
|
||||
}
|
||||
|
||||
(executor, trace)
|
||||
}
|
||||
@ -153,3 +150,7 @@ fn executor_task_cfg_args() {
|
||||
let (_, _, _) = (a, b, c);
|
||||
}
|
||||
}
|
||||
|
||||
// We need this for the test to compile, even though we don't want to use timers at the moment.
|
||||
#[no_mangle]
|
||||
fn _embassy_time_schedule_wake(_at: u64, _waker: &core::task::Waker) {}
|
||||
|
@ -119,7 +119,7 @@ _nrf52 = ["_ppi"]
|
||||
_nrf51 = ["_ppi"]
|
||||
_nrf91 = []
|
||||
|
||||
_time-driver = ["dep:embassy-time-driver", "embassy-time-driver?/tick-hz-32_768"]
|
||||
_time-driver = ["dep:embassy-time-driver", "embassy-time-driver?/tick-hz-32_768", "dep:embassy-time-queue-driver"]
|
||||
|
||||
# trustzone state.
|
||||
_s = []
|
||||
@ -135,6 +135,7 @@ _nrf52832_anomaly_109 = []
|
||||
|
||||
[dependencies]
|
||||
embassy-time-driver = { version = "0.1", path = "../embassy-time-driver", optional = true }
|
||||
embassy-time-queue-driver = { version = "0.1", path = "../embassy-time-queue-driver", optional = true }
|
||||
embassy-time = { version = "0.3.2", path = "../embassy-time", optional = true }
|
||||
embassy-sync = { version = "0.6.1", path = "../embassy-sync" }
|
||||
embassy-hal-internal = {version = "0.2.0", path = "../embassy-hal-internal", features = ["cortex-m", "prio-bits-3"] }
|
||||
|
@ -1,11 +1,11 @@
|
||||
use core::cell::Cell;
|
||||
use core::sync::atomic::{compiler_fence, AtomicU32, AtomicU8, Ordering};
|
||||
use core::{mem, ptr};
|
||||
use core::cell::{Cell, RefCell};
|
||||
use core::sync::atomic::{compiler_fence, AtomicU32, Ordering};
|
||||
|
||||
use critical_section::CriticalSection;
|
||||
use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex;
|
||||
use embassy_sync::blocking_mutex::CriticalSectionMutex as Mutex;
|
||||
use embassy_time_driver::{AlarmHandle, Driver};
|
||||
use embassy_time_driver::Driver;
|
||||
use embassy_time_queue_driver::Queue;
|
||||
|
||||
use crate::interrupt::InterruptExt;
|
||||
use crate::{interrupt, pac};
|
||||
@ -94,11 +94,6 @@ mod test {
|
||||
|
||||
struct AlarmState {
|
||||
timestamp: Cell<u64>,
|
||||
|
||||
// This is really a Option<(fn(*mut ()), *mut ())>
|
||||
// but fn pointers aren't allowed in const yet
|
||||
callback: Cell<*const ()>,
|
||||
ctx: Cell<*mut ()>,
|
||||
}
|
||||
|
||||
unsafe impl Send for AlarmState {}
|
||||
@ -107,26 +102,22 @@ impl AlarmState {
|
||||
const fn new() -> Self {
|
||||
Self {
|
||||
timestamp: Cell::new(u64::MAX),
|
||||
callback: Cell::new(ptr::null()),
|
||||
ctx: Cell::new(ptr::null_mut()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const ALARM_COUNT: usize = 3;
|
||||
|
||||
struct RtcDriver {
|
||||
/// Number of 2^23 periods elapsed since boot.
|
||||
period: AtomicU32,
|
||||
alarm_count: AtomicU8,
|
||||
/// Timestamp at which to fire alarm. u64::MAX if no alarm is scheduled.
|
||||
alarms: Mutex<[AlarmState; ALARM_COUNT]>,
|
||||
alarms: Mutex<AlarmState>,
|
||||
queue: Mutex<RefCell<Queue>>,
|
||||
}
|
||||
|
||||
embassy_time_driver::time_driver_impl!(static DRIVER: RtcDriver = RtcDriver {
|
||||
period: AtomicU32::new(0),
|
||||
alarm_count: AtomicU8::new(0),
|
||||
alarms: Mutex::const_new(CriticalSectionRawMutex::new(), [const {AlarmState::new()}; ALARM_COUNT]),
|
||||
alarms: Mutex::const_new(CriticalSectionRawMutex::new(), AlarmState::new()),
|
||||
queue: Mutex::new(RefCell::new(Queue::new())),
|
||||
});
|
||||
|
||||
impl RtcDriver {
|
||||
@ -169,13 +160,12 @@ impl RtcDriver {
|
||||
self.next_period();
|
||||
}
|
||||
|
||||
for n in 0..ALARM_COUNT {
|
||||
if r.events_compare(n).read() == 1 {
|
||||
r.events_compare(n).write_value(0);
|
||||
critical_section::with(|cs| {
|
||||
self.trigger_alarm(n, cs);
|
||||
})
|
||||
}
|
||||
let n = 0;
|
||||
if r.events_compare(n).read() == 1 {
|
||||
r.events_compare(n).write_value(0);
|
||||
critical_section::with(|cs| {
|
||||
self.trigger_alarm(cs);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@ -186,38 +176,80 @@ impl RtcDriver {
|
||||
self.period.store(period, Ordering::Relaxed);
|
||||
let t = (period as u64) << 23;
|
||||
|
||||
for n in 0..ALARM_COUNT {
|
||||
let alarm = &self.alarms.borrow(cs)[n];
|
||||
let at = alarm.timestamp.get();
|
||||
let n = 0;
|
||||
let alarm = &self.alarms.borrow(cs);
|
||||
let at = alarm.timestamp.get();
|
||||
|
||||
if at < t + 0xc00000 {
|
||||
// just enable it. `set_alarm` has already set the correct CC val.
|
||||
r.intenset().write(|w| w.0 = compare_n(n));
|
||||
}
|
||||
if at < t + 0xc00000 {
|
||||
// just enable it. `set_alarm` has already set the correct CC val.
|
||||
r.intenset().write(|w| w.0 = compare_n(n));
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
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
|
||||
// we never create one that's out of bounds.
|
||||
unsafe { self.alarms.borrow(cs).get_unchecked(alarm.id() as usize) }
|
||||
}
|
||||
|
||||
fn trigger_alarm(&self, n: usize, cs: CriticalSection) {
|
||||
fn trigger_alarm(&self, cs: CriticalSection) {
|
||||
let n = 0;
|
||||
let r = rtc();
|
||||
r.intenclr().write(|w| w.0 = compare_n(n));
|
||||
|
||||
let alarm = &self.alarms.borrow(cs)[n];
|
||||
let alarm = &self.alarms.borrow(cs);
|
||||
alarm.timestamp.set(u64::MAX);
|
||||
|
||||
// Call after clearing alarm, so the callback can set another alarm.
|
||||
let mut next = self.queue.borrow(cs).borrow_mut().next_expiration(self.now());
|
||||
while !self.set_alarm(cs, next) {
|
||||
next = self.queue.borrow(cs).borrow_mut().next_expiration(self.now());
|
||||
}
|
||||
}
|
||||
|
||||
// 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.get()) };
|
||||
f(alarm.ctx.get());
|
||||
fn set_alarm(&self, cs: CriticalSection, timestamp: u64) -> bool {
|
||||
let n = 0;
|
||||
let alarm = &self.alarms.borrow(cs);
|
||||
alarm.timestamp.set(timestamp);
|
||||
|
||||
let r = rtc();
|
||||
|
||||
let t = self.now();
|
||||
if timestamp <= t {
|
||||
// If alarm timestamp has passed the alarm will not fire.
|
||||
// Disarm the alarm and return `false` to indicate that.
|
||||
r.intenclr().write(|w| w.0 = compare_n(n));
|
||||
|
||||
alarm.timestamp.set(u64::MAX);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// If it hasn't triggered yet, setup it in the compare channel.
|
||||
|
||||
// Write the CC value regardless of whether we're going to enable it now or not.
|
||||
// This way, when we enable it later, the right value is already set.
|
||||
|
||||
// nrf52 docs say:
|
||||
// If the COUNTER is N, writing N or N+1 to a CC register may not trigger a COMPARE event.
|
||||
// To workaround this, we never write a timestamp smaller than N+3.
|
||||
// N+2 is not safe because rtc can tick from N to N+1 between calling now() and writing cc.
|
||||
//
|
||||
// It is impossible for rtc to tick more than once because
|
||||
// - this code takes less time than 1 tick
|
||||
// - it runs with interrupts disabled so nothing else can preempt it.
|
||||
//
|
||||
// This means that an alarm can be delayed for up to 2 ticks (from t+1 to t+3), but this is allowed
|
||||
// by the Alarm trait contract. What's not allowed is triggering alarms *before* their scheduled time,
|
||||
// and we don't do that here.
|
||||
let safe_timestamp = timestamp.max(t + 3);
|
||||
r.cc(n).write(|w| w.set_compare(safe_timestamp as u32 & 0xFFFFFF));
|
||||
|
||||
let diff = timestamp - t;
|
||||
if diff < 0xc00000 {
|
||||
r.intenset().write(|w| w.0 = compare_n(n));
|
||||
} else {
|
||||
// If it's too far in the future, don't setup the compare channel yet.
|
||||
// It will be setup later by `next_period`.
|
||||
r.intenclr().write(|w| w.0 = compare_n(n));
|
||||
}
|
||||
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
@ -230,76 +262,16 @@ impl Driver for RtcDriver {
|
||||
calc_now(period, counter)
|
||||
}
|
||||
|
||||
unsafe fn allocate_alarm(&self) -> Option<AlarmHandle> {
|
||||
critical_section::with(|_| {
|
||||
let id = self.alarm_count.load(Ordering::Relaxed);
|
||||
if id < ALARM_COUNT as u8 {
|
||||
self.alarm_count.store(id + 1, Ordering::Relaxed);
|
||||
Some(AlarmHandle::new(id))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
fn set_alarm_callback(&self, alarm: AlarmHandle, callback: fn(*mut ()), ctx: *mut ()) {
|
||||
fn schedule_wake(&self, at: u64, waker: &core::task::Waker) {
|
||||
critical_section::with(|cs| {
|
||||
let alarm = self.get_alarm(cs, alarm);
|
||||
let mut queue = self.queue.borrow(cs).borrow_mut();
|
||||
|
||||
alarm.callback.set(callback as *const ());
|
||||
alarm.ctx.set(ctx);
|
||||
})
|
||||
}
|
||||
|
||||
fn set_alarm(&self, alarm: AlarmHandle, timestamp: u64) -> bool {
|
||||
critical_section::with(|cs| {
|
||||
let n = alarm.id() as _;
|
||||
let alarm = self.get_alarm(cs, alarm);
|
||||
alarm.timestamp.set(timestamp);
|
||||
|
||||
let r = rtc();
|
||||
|
||||
let t = self.now();
|
||||
if timestamp <= t {
|
||||
// If alarm timestamp has passed the alarm will not fire.
|
||||
// Disarm the alarm and return `false` to indicate that.
|
||||
r.intenclr().write(|w| w.0 = compare_n(n));
|
||||
|
||||
alarm.timestamp.set(u64::MAX);
|
||||
|
||||
return false;
|
||||
if queue.schedule_wake(at, waker) {
|
||||
let mut next = queue.next_expiration(self.now());
|
||||
while !self.set_alarm(cs, next) {
|
||||
next = queue.next_expiration(self.now());
|
||||
}
|
||||
}
|
||||
|
||||
// If it hasn't triggered yet, setup it in the compare channel.
|
||||
|
||||
// Write the CC value regardless of whether we're going to enable it now or not.
|
||||
// This way, when we enable it later, the right value is already set.
|
||||
|
||||
// nrf52 docs say:
|
||||
// If the COUNTER is N, writing N or N+1 to a CC register may not trigger a COMPARE event.
|
||||
// To workaround this, we never write a timestamp smaller than N+3.
|
||||
// N+2 is not safe because rtc can tick from N to N+1 between calling now() and writing cc.
|
||||
//
|
||||
// It is impossible for rtc to tick more than once because
|
||||
// - this code takes less time than 1 tick
|
||||
// - it runs with interrupts disabled so nothing else can preempt it.
|
||||
//
|
||||
// This means that an alarm can be delayed for up to 2 ticks (from t+1 to t+3), but this is allowed
|
||||
// by the Alarm trait contract. What's not allowed is triggering alarms *before* their scheduled time,
|
||||
// and we don't do that here.
|
||||
let safe_timestamp = timestamp.max(t + 3);
|
||||
r.cc(n).write(|w| w.set_compare(safe_timestamp as u32 & 0xFFFFFF));
|
||||
|
||||
let diff = timestamp - t;
|
||||
if diff < 0xc00000 {
|
||||
r.intenset().write(|w| w.0 = compare_n(n));
|
||||
} else {
|
||||
// If it's too far in the future, don't setup the compare channel yet.
|
||||
// It will be setup later by `next_period`.
|
||||
r.intenclr().write(|w| w.0 = compare_n(n));
|
||||
}
|
||||
|
||||
true
|
||||
})
|
||||
}
|
||||
}
|
||||
|
@ -40,7 +40,7 @@ critical-section-impl = ["critical-section/restore-state-u8"]
|
||||
unstable-pac = []
|
||||
|
||||
## Enable the timer for use with `embassy-time` with a 1MHz tick rate.
|
||||
time-driver = ["dep:embassy-time-driver", "embassy-time-driver?/tick-hz-1_000_000"]
|
||||
time-driver = ["dep:embassy-time-driver", "embassy-time-driver?/tick-hz-1_000_000", "dep:embassy-time-queue-driver"]
|
||||
|
||||
## Enable ROM function cache. This will store the address of a ROM function when first used, improving performance of subsequent calls.
|
||||
rom-func-cache = []
|
||||
@ -110,6 +110,7 @@ binary-info = ["rt", "dep:rp-binary-info", "rp-binary-info?/binary-info"]
|
||||
[dependencies]
|
||||
embassy-sync = { version = "0.6.1", path = "../embassy-sync" }
|
||||
embassy-time-driver = { version = "0.1", path = "../embassy-time-driver", optional = true }
|
||||
embassy-time-queue-driver = { version = "0.1", path = "../embassy-time-queue-driver", optional = true }
|
||||
embassy-time = { version = "0.3.2", path = "../embassy-time" }
|
||||
embassy-futures = { version = "0.1.0", path = "../embassy-futures" }
|
||||
embassy-hal-internal = {version = "0.2.0", path = "../embassy-hal-internal", features = ["cortex-m", "prio-bits-2"] }
|
||||
|
@ -1,11 +1,11 @@
|
||||
//! Timer driver.
|
||||
use core::cell::Cell;
|
||||
use core::cell::{Cell, RefCell};
|
||||
|
||||
use atomic_polyfill::{AtomicU8, Ordering};
|
||||
use critical_section::CriticalSection;
|
||||
use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex;
|
||||
use embassy_sync::blocking_mutex::Mutex;
|
||||
use embassy_time_driver::{AlarmHandle, Driver};
|
||||
use embassy_time_driver::Driver;
|
||||
use embassy_time_queue_driver::Queue;
|
||||
#[cfg(feature = "rp2040")]
|
||||
use pac::TIMER;
|
||||
#[cfg(feature = "_rp235x")]
|
||||
@ -16,23 +16,19 @@ use crate::{interrupt, pac};
|
||||
|
||||
struct AlarmState {
|
||||
timestamp: Cell<u64>,
|
||||
callback: Cell<Option<(fn(*mut ()), *mut ())>>,
|
||||
}
|
||||
unsafe impl Send for AlarmState {}
|
||||
|
||||
const ALARM_COUNT: usize = 4;
|
||||
|
||||
struct TimerDriver {
|
||||
alarms: Mutex<CriticalSectionRawMutex, [AlarmState; ALARM_COUNT]>,
|
||||
next_alarm: AtomicU8,
|
||||
alarms: Mutex<CriticalSectionRawMutex, AlarmState>,
|
||||
queue: Mutex<CriticalSectionRawMutex, RefCell<Queue>>,
|
||||
}
|
||||
|
||||
embassy_time_driver::time_driver_impl!(static DRIVER: TimerDriver = TimerDriver{
|
||||
alarms: Mutex::const_new(CriticalSectionRawMutex::new(), [const{AlarmState {
|
||||
alarms: Mutex::const_new(CriticalSectionRawMutex::new(), AlarmState {
|
||||
timestamp: Cell::new(0),
|
||||
callback: Cell::new(None),
|
||||
}}; ALARM_COUNT]),
|
||||
next_alarm: AtomicU8::new(0),
|
||||
}),
|
||||
queue: Mutex::new(RefCell::new(Queue::new()))
|
||||
});
|
||||
|
||||
impl Driver for TimerDriver {
|
||||
@ -47,64 +43,53 @@ impl Driver for TimerDriver {
|
||||
}
|
||||
}
|
||||
|
||||
unsafe fn allocate_alarm(&self) -> Option<AlarmHandle> {
|
||||
let id = self.next_alarm.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 ()) {
|
||||
let n = alarm.id() as usize;
|
||||
fn schedule_wake(&self, at: u64, waker: &core::task::Waker) {
|
||||
critical_section::with(|cs| {
|
||||
let alarm = &self.alarms.borrow(cs)[n];
|
||||
alarm.callback.set(Some((callback, ctx)));
|
||||
})
|
||||
}
|
||||
let mut queue = self.queue.borrow(cs).borrow_mut();
|
||||
|
||||
fn set_alarm(&self, alarm: AlarmHandle, timestamp: u64) -> bool {
|
||||
let n = alarm.id() as usize;
|
||||
critical_section::with(|cs| {
|
||||
let alarm = &self.alarms.borrow(cs)[n];
|
||||
alarm.timestamp.set(timestamp);
|
||||
|
||||
// Arm it.
|
||||
// Note that we're not checking the high bits at all. This means the irq may fire early
|
||||
// if the alarm is more than 72 minutes (2^32 us) in the future. This is OK, since on irq fire
|
||||
// it is checked if the alarm time has passed.
|
||||
TIMER.alarm(n).write_value(timestamp as u32);
|
||||
|
||||
let now = self.now();
|
||||
if timestamp <= now {
|
||||
// If alarm timestamp has passed the alarm will not fire.
|
||||
// Disarm the alarm and return `false` to indicate that.
|
||||
TIMER.armed().write(|w| w.set_armed(1 << n));
|
||||
|
||||
alarm.timestamp.set(u64::MAX);
|
||||
|
||||
false
|
||||
} else {
|
||||
true
|
||||
if queue.schedule_wake(at, waker) {
|
||||
let mut next = queue.next_expiration(self.now());
|
||||
while !self.set_alarm(cs, next) {
|
||||
next = queue.next_expiration(self.now());
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl TimerDriver {
|
||||
fn check_alarm(&self, n: usize) {
|
||||
fn set_alarm(&self, cs: CriticalSection, timestamp: u64) -> bool {
|
||||
let n = 0;
|
||||
let alarm = &self.alarms.borrow(cs);
|
||||
alarm.timestamp.set(timestamp);
|
||||
|
||||
// Arm it.
|
||||
// Note that we're not checking the high bits at all. This means the irq may fire early
|
||||
// if the alarm is more than 72 minutes (2^32 us) in the future. This is OK, since on irq fire
|
||||
// it is checked if the alarm time has passed.
|
||||
TIMER.alarm(n).write_value(timestamp as u32);
|
||||
|
||||
let now = self.now();
|
||||
if timestamp <= now {
|
||||
// If alarm timestamp has passed the alarm will not fire.
|
||||
// Disarm the alarm and return `false` to indicate that.
|
||||
TIMER.armed().write(|w| w.set_armed(1 << n));
|
||||
|
||||
alarm.timestamp.set(u64::MAX);
|
||||
|
||||
false
|
||||
} else {
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
fn check_alarm(&self) {
|
||||
let n = 0;
|
||||
critical_section::with(|cs| {
|
||||
let alarm = &self.alarms.borrow(cs)[n];
|
||||
let alarm = &self.alarms.borrow(cs);
|
||||
let timestamp = alarm.timestamp.get();
|
||||
if timestamp <= self.now() {
|
||||
self.trigger_alarm(n, cs)
|
||||
self.trigger_alarm(cs)
|
||||
} else {
|
||||
// Not elapsed, arm it again.
|
||||
// This can happen if it was set more than 2^32 us in the future.
|
||||
@ -116,16 +101,10 @@ impl TimerDriver {
|
||||
TIMER.intr().write(|w| w.set_alarm(n, true));
|
||||
}
|
||||
|
||||
fn trigger_alarm(&self, n: usize, cs: CriticalSection) {
|
||||
// disarm
|
||||
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);
|
||||
fn trigger_alarm(&self, cs: CriticalSection) {
|
||||
let mut next = self.queue.borrow(cs).borrow_mut().next_expiration(self.now());
|
||||
while !self.set_alarm(cs, next) {
|
||||
next = self.queue.borrow(cs).borrow_mut().next_expiration(self.now());
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -134,79 +113,32 @@ impl TimerDriver {
|
||||
pub unsafe fn init() {
|
||||
// init alarms
|
||||
critical_section::with(|cs| {
|
||||
let alarms = DRIVER.alarms.borrow(cs);
|
||||
for a in alarms {
|
||||
a.timestamp.set(u64::MAX);
|
||||
}
|
||||
let alarm = DRIVER.alarms.borrow(cs);
|
||||
alarm.timestamp.set(u64::MAX);
|
||||
});
|
||||
|
||||
// enable all irqs
|
||||
// enable irq
|
||||
TIMER.inte().write(|w| {
|
||||
w.set_alarm(0, true);
|
||||
w.set_alarm(1, true);
|
||||
w.set_alarm(2, true);
|
||||
w.set_alarm(3, true);
|
||||
});
|
||||
#[cfg(feature = "rp2040")]
|
||||
{
|
||||
interrupt::TIMER_IRQ_0.enable();
|
||||
interrupt::TIMER_IRQ_1.enable();
|
||||
interrupt::TIMER_IRQ_2.enable();
|
||||
interrupt::TIMER_IRQ_3.enable();
|
||||
}
|
||||
#[cfg(feature = "_rp235x")]
|
||||
{
|
||||
interrupt::TIMER0_IRQ_0.enable();
|
||||
interrupt::TIMER0_IRQ_1.enable();
|
||||
interrupt::TIMER0_IRQ_2.enable();
|
||||
interrupt::TIMER0_IRQ_3.enable();
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(all(feature = "rt", feature = "rp2040"))]
|
||||
#[interrupt]
|
||||
fn TIMER_IRQ_0() {
|
||||
DRIVER.check_alarm(0)
|
||||
}
|
||||
|
||||
#[cfg(all(feature = "rt", feature = "rp2040"))]
|
||||
#[interrupt]
|
||||
fn TIMER_IRQ_1() {
|
||||
DRIVER.check_alarm(1)
|
||||
}
|
||||
|
||||
#[cfg(all(feature = "rt", feature = "rp2040"))]
|
||||
#[interrupt]
|
||||
fn TIMER_IRQ_2() {
|
||||
DRIVER.check_alarm(2)
|
||||
}
|
||||
|
||||
#[cfg(all(feature = "rt", feature = "rp2040"))]
|
||||
#[interrupt]
|
||||
fn TIMER_IRQ_3() {
|
||||
DRIVER.check_alarm(3)
|
||||
DRIVER.check_alarm()
|
||||
}
|
||||
|
||||
#[cfg(all(feature = "rt", feature = "_rp235x"))]
|
||||
#[interrupt]
|
||||
fn TIMER0_IRQ_0() {
|
||||
DRIVER.check_alarm(0)
|
||||
}
|
||||
|
||||
#[cfg(all(feature = "rt", feature = "_rp235x"))]
|
||||
#[interrupt]
|
||||
fn TIMER0_IRQ_1() {
|
||||
DRIVER.check_alarm(1)
|
||||
}
|
||||
|
||||
#[cfg(all(feature = "rt", feature = "_rp235x"))]
|
||||
#[interrupt]
|
||||
fn TIMER0_IRQ_2() {
|
||||
DRIVER.check_alarm(2)
|
||||
}
|
||||
|
||||
#[cfg(all(feature = "rt", feature = "_rp235x"))]
|
||||
#[interrupt]
|
||||
fn TIMER0_IRQ_3() {
|
||||
DRIVER.check_alarm(3)
|
||||
DRIVER.check_alarm()
|
||||
}
|
||||
|
@ -45,6 +45,7 @@ rustdoc-args = ["--cfg", "docsrs"]
|
||||
embassy-sync = { version = "0.6.1", path = "../embassy-sync" }
|
||||
embassy-time = { version = "0.3.2", path = "../embassy-time", optional = true }
|
||||
embassy-time-driver = { version = "0.1", path = "../embassy-time-driver", optional = true }
|
||||
embassy-time-queue-driver = { version = "0.1", path = "../embassy-time-queue-driver", optional = true }
|
||||
embassy-futures = { version = "0.1.0", path = "../embassy-futures" }
|
||||
embassy-hal-internal = {version = "0.2.0", path = "../embassy-hal-internal", features = ["cortex-m", "prio-bits-4"] }
|
||||
embassy-embedded-hal = {version = "0.2.0", path = "../embassy-embedded-hal", default-features = false }
|
||||
@ -149,7 +150,7 @@ time = ["dep:embassy-time", "embassy-embedded-hal/time"]
|
||||
|
||||
# Features starting with `_` are for internal use only. They're not intended
|
||||
# to be enabled by other crates, and are not covered by semver guarantees.
|
||||
_time-driver = ["dep:embassy-time-driver", "time"]
|
||||
_time-driver = ["dep:embassy-time-driver", "time", "dep:embassy-time-queue-driver"]
|
||||
|
||||
## Use any time driver
|
||||
time-driver-any = ["_time-driver"]
|
||||
|
@ -256,9 +256,6 @@ impl Executor {
|
||||
/// This function never returns.
|
||||
pub fn run(&'static mut self, init: impl FnOnce(Spawner)) -> ! {
|
||||
let executor = unsafe { EXECUTOR.as_mut().unwrap() };
|
||||
unsafe {
|
||||
executor.inner.initialize();
|
||||
}
|
||||
init(executor.inner.spawner());
|
||||
|
||||
loop {
|
||||
|
@ -1,13 +1,13 @@
|
||||
#![allow(non_snake_case)]
|
||||
|
||||
use core::cell::Cell;
|
||||
use core::sync::atomic::{compiler_fence, AtomicU32, AtomicU8, Ordering};
|
||||
use core::{mem, ptr};
|
||||
use core::cell::{Cell, RefCell};
|
||||
use core::sync::atomic::{compiler_fence, AtomicU32, Ordering};
|
||||
|
||||
use critical_section::CriticalSection;
|
||||
use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex;
|
||||
use embassy_sync::blocking_mutex::Mutex;
|
||||
use embassy_time_driver::{AlarmHandle, Driver, TICK_HZ};
|
||||
use embassy_time_driver::{Driver, TICK_HZ};
|
||||
use embassy_time_queue_driver::Queue;
|
||||
use stm32_metapac::timer::{regs, TimGp16};
|
||||
|
||||
use crate::interrupt::typelevel::Interrupt;
|
||||
@ -24,18 +24,6 @@ use crate::{interrupt, peripherals};
|
||||
// additional CC capabilities to provide timer alarms to embassy-time. embassy-time requires AT LEAST
|
||||
// one alarm to be allocatable, which means timers that only have CC1, such as TIM16/TIM17, are not
|
||||
// candidates for use as an embassy-time driver provider. (a.k.a 1CH and 1CH_CMP are not, others are good.)
|
||||
//
|
||||
// The values of ALARM_COUNT below are not the TOTAL CC registers available, but rather the number
|
||||
// available after reserving CC1 for regular time keeping. For example, TIM2 has four CC registers:
|
||||
// CC1, CC2, CC3, and CC4, so it can provide ALARM_COUNT = 3.
|
||||
|
||||
cfg_if::cfg_if! {
|
||||
if #[cfg(any(time_driver_tim9, time_driver_tim12, time_driver_tim15, time_driver_tim21, time_driver_tim22))] {
|
||||
const ALARM_COUNT: usize = 1;
|
||||
} else {
|
||||
const ALARM_COUNT: usize = 3;
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(time_driver_tim1)]
|
||||
type T = peripherals::TIM1;
|
||||
@ -208,11 +196,6 @@ fn calc_now(period: u32, counter: u16) -> u64 {
|
||||
|
||||
struct AlarmState {
|
||||
timestamp: Cell<u64>,
|
||||
|
||||
// This is really a Option<(fn(*mut ()), *mut ())>
|
||||
// but fn pointers aren't allowed in const yet
|
||||
callback: Cell<*const ()>,
|
||||
ctx: Cell<*mut ()>,
|
||||
}
|
||||
|
||||
unsafe impl Send for AlarmState {}
|
||||
@ -221,8 +204,6 @@ impl AlarmState {
|
||||
const fn new() -> Self {
|
||||
Self {
|
||||
timestamp: Cell::new(u64::MAX),
|
||||
callback: Cell::new(ptr::null()),
|
||||
ctx: Cell::new(ptr::null_mut()),
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -230,19 +211,18 @@ impl AlarmState {
|
||||
pub(crate) struct RtcDriver {
|
||||
/// Number of 2^15 periods elapsed since boot.
|
||||
period: AtomicU32,
|
||||
alarm_count: AtomicU8,
|
||||
/// Timestamp at which to fire alarm. u64::MAX if no alarm is scheduled.
|
||||
alarms: Mutex<CriticalSectionRawMutex, [AlarmState; ALARM_COUNT]>,
|
||||
alarm: Mutex<CriticalSectionRawMutex, AlarmState>,
|
||||
#[cfg(feature = "low-power")]
|
||||
rtc: Mutex<CriticalSectionRawMutex, Cell<Option<&'static Rtc>>>,
|
||||
queue: Mutex<CriticalSectionRawMutex, RefCell<Queue>>,
|
||||
}
|
||||
|
||||
embassy_time_driver::time_driver_impl!(static DRIVER: RtcDriver = RtcDriver {
|
||||
period: AtomicU32::new(0),
|
||||
alarm_count: AtomicU8::new(0),
|
||||
alarms: Mutex::const_new(CriticalSectionRawMutex::new(), [const{AlarmState::new()}; ALARM_COUNT]),
|
||||
alarm: Mutex::const_new(CriticalSectionRawMutex::new(), AlarmState::new()),
|
||||
#[cfg(feature = "low-power")]
|
||||
rtc: Mutex::const_new(CriticalSectionRawMutex::new(), Cell::new(None)),
|
||||
queue: Mutex::new(RefCell::new(Queue::new()))
|
||||
});
|
||||
|
||||
impl RtcDriver {
|
||||
@ -288,7 +268,6 @@ impl RtcDriver {
|
||||
fn on_interrupt(&self) {
|
||||
let r = regs_gp16();
|
||||
|
||||
// XXX: reduce the size of this critical section ?
|
||||
critical_section::with(|cs| {
|
||||
let sr = r.sr().read();
|
||||
let dier = r.dier().read();
|
||||
@ -308,10 +287,9 @@ impl RtcDriver {
|
||||
self.next_period();
|
||||
}
|
||||
|
||||
for n in 0..ALARM_COUNT {
|
||||
if sr.ccif(n + 1) && dier.ccie(n + 1) {
|
||||
self.trigger_alarm(n, cs);
|
||||
}
|
||||
let n = 0;
|
||||
if sr.ccif(n + 1) && dier.ccie(n + 1) {
|
||||
self.trigger_alarm(cs);
|
||||
}
|
||||
})
|
||||
}
|
||||
@ -326,36 +304,23 @@ impl RtcDriver {
|
||||
|
||||
critical_section::with(move |cs| {
|
||||
r.dier().modify(move |w| {
|
||||
for n in 0..ALARM_COUNT {
|
||||
let alarm = &self.alarms.borrow(cs)[n];
|
||||
let at = alarm.timestamp.get();
|
||||
let n = 0;
|
||||
let alarm = self.alarm.borrow(cs);
|
||||
let at = alarm.timestamp.get();
|
||||
|
||||
if at < t + 0xc000 {
|
||||
// just enable it. `set_alarm` has already set the correct CCR val.
|
||||
w.set_ccie(n + 1, true);
|
||||
}
|
||||
if at < t + 0xc000 {
|
||||
// just enable it. `set_alarm` has already set the correct CCR val.
|
||||
w.set_ccie(n + 1, true);
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
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
|
||||
// we never create one that's out of bounds.
|
||||
unsafe { self.alarms.borrow(cs).get_unchecked(alarm.id() as usize) }
|
||||
}
|
||||
|
||||
fn trigger_alarm(&self, n: usize, cs: CriticalSection) {
|
||||
let alarm = &self.alarms.borrow(cs)[n];
|
||||
alarm.timestamp.set(u64::MAX);
|
||||
|
||||
// Call after clearing alarm, so the callback can set another alarm.
|
||||
|
||||
// safety:
|
||||
// - we can ignore the possibility 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.get()) };
|
||||
f(alarm.ctx.get());
|
||||
fn trigger_alarm(&self, cs: CriticalSection) {
|
||||
let mut next = self.queue.borrow(cs).borrow_mut().next_expiration(self.now());
|
||||
while !self.set_alarm(cs, next) {
|
||||
next = self.queue.borrow(cs).borrow_mut().next_expiration(self.now());
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
@ -367,14 +332,7 @@ impl RtcDriver {
|
||||
fn time_until_next_alarm(&self, cs: CriticalSection) -> embassy_time::Duration {
|
||||
let now = self.now() + 32;
|
||||
|
||||
embassy_time::Duration::from_ticks(
|
||||
self.alarms
|
||||
.borrow(cs)
|
||||
.iter()
|
||||
.map(|alarm: &AlarmState| alarm.timestamp.get().saturating_sub(now))
|
||||
.min()
|
||||
.unwrap_or(u64::MAX),
|
||||
)
|
||||
embassy_time::Duration::from_ticks(self.alarm.borrow(cs).timestamp.get().saturating_sub(now))
|
||||
}
|
||||
|
||||
#[cfg(feature = "low-power")]
|
||||
@ -409,15 +367,12 @@ impl RtcDriver {
|
||||
self.period.store(period, Ordering::SeqCst);
|
||||
regs_gp16().cnt().write(|w| w.set_cnt(cnt as u16));
|
||||
|
||||
// Now, recompute all alarms
|
||||
for i in 0..self.alarm_count.load(Ordering::Relaxed) as usize {
|
||||
let alarm_handle = unsafe { AlarmHandle::new(i as u8) };
|
||||
let alarm = self.get_alarm(cs, alarm_handle);
|
||||
// Now, recompute alarm
|
||||
let alarm = self.alarm.borrow(cs);
|
||||
|
||||
if !self.set_alarm(alarm_handle, alarm.timestamp.get()) {
|
||||
// If the alarm timestamp has passed, we need to trigger it
|
||||
self.trigger_alarm(i, cs);
|
||||
}
|
||||
if !self.set_alarm(cs, alarm.timestamp.get()) {
|
||||
// If the alarm timestamp has passed, we need to trigger it
|
||||
self.trigger_alarm(cs);
|
||||
}
|
||||
}
|
||||
|
||||
@ -489,6 +444,49 @@ impl RtcDriver {
|
||||
regs_gp16().cr1().modify(|w| w.set_cen(true));
|
||||
})
|
||||
}
|
||||
|
||||
fn set_alarm(&self, cs: CriticalSection, timestamp: u64) -> bool {
|
||||
let r = regs_gp16();
|
||||
|
||||
let n = 0;
|
||||
self.alarm.borrow(cs).timestamp.set(timestamp);
|
||||
|
||||
let t = self.now();
|
||||
if timestamp <= t {
|
||||
// If alarm timestamp has passed the alarm will not fire.
|
||||
// Disarm the alarm and return `false` to indicate that.
|
||||
r.dier().modify(|w| w.set_ccie(n + 1, false));
|
||||
|
||||
self.alarm.borrow(cs).timestamp.set(u64::MAX);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// Write the CCR value regardless of whether we're going to enable it now or not.
|
||||
// This way, when we enable it later, the right value is already set.
|
||||
r.ccr(n + 1).write(|w| w.set_ccr(timestamp as u16));
|
||||
|
||||
// Enable it if it'll happen soon. Otherwise, `next_period` will enable it.
|
||||
let diff = timestamp - t;
|
||||
r.dier().modify(|w| w.set_ccie(n + 1, diff < 0xc000));
|
||||
|
||||
// Reevaluate if the alarm timestamp is still in the future
|
||||
let t = self.now();
|
||||
if timestamp <= t {
|
||||
// If alarm timestamp has passed since we set it, we have a race condition and
|
||||
// the alarm may or may not have fired.
|
||||
// Disarm the alarm and return `false` to indicate that.
|
||||
// It is the caller's responsibility to handle this ambiguity.
|
||||
r.dier().modify(|w| w.set_ccie(n + 1, false));
|
||||
|
||||
self.alarm.borrow(cs).timestamp.set(u64::MAX);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// We're confident the alarm will ring in the future.
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
impl Driver for RtcDriver {
|
||||
@ -501,70 +499,16 @@ impl Driver for RtcDriver {
|
||||
calc_now(period, counter)
|
||||
}
|
||||
|
||||
unsafe fn allocate_alarm(&self) -> Option<AlarmHandle> {
|
||||
critical_section::with(|_| {
|
||||
let id = self.alarm_count.load(Ordering::Relaxed);
|
||||
if id < ALARM_COUNT as u8 {
|
||||
self.alarm_count.store(id + 1, Ordering::Relaxed);
|
||||
Some(AlarmHandle::new(id))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
fn set_alarm_callback(&self, alarm: AlarmHandle, callback: fn(*mut ()), ctx: *mut ()) {
|
||||
fn schedule_wake(&self, at: u64, waker: &core::task::Waker) {
|
||||
critical_section::with(|cs| {
|
||||
let alarm = self.get_alarm(cs, alarm);
|
||||
let mut queue = self.queue.borrow(cs).borrow_mut();
|
||||
|
||||
alarm.callback.set(callback as *const ());
|
||||
alarm.ctx.set(ctx);
|
||||
})
|
||||
}
|
||||
|
||||
fn set_alarm(&self, alarm: AlarmHandle, timestamp: u64) -> bool {
|
||||
critical_section::with(|cs| {
|
||||
let r = regs_gp16();
|
||||
|
||||
let n = alarm.id() as usize;
|
||||
let alarm = self.get_alarm(cs, alarm);
|
||||
alarm.timestamp.set(timestamp);
|
||||
|
||||
let t = self.now();
|
||||
if timestamp <= t {
|
||||
// If alarm timestamp has passed the alarm will not fire.
|
||||
// Disarm the alarm and return `false` to indicate that.
|
||||
r.dier().modify(|w| w.set_ccie(n + 1, false));
|
||||
|
||||
alarm.timestamp.set(u64::MAX);
|
||||
|
||||
return false;
|
||||
if queue.schedule_wake(at, waker) {
|
||||
let mut next = queue.next_expiration(self.now());
|
||||
while !self.set_alarm(cs, next) {
|
||||
next = queue.next_expiration(self.now());
|
||||
}
|
||||
}
|
||||
|
||||
// Write the CCR value regardless of whether we're going to enable it now or not.
|
||||
// This way, when we enable it later, the right value is already set.
|
||||
r.ccr(n + 1).write(|w| w.set_ccr(timestamp as u16));
|
||||
|
||||
// Enable it if it'll happen soon. Otherwise, `next_period` will enable it.
|
||||
let diff = timestamp - t;
|
||||
r.dier().modify(|w| w.set_ccie(n + 1, diff < 0xc000));
|
||||
|
||||
// Reevaluate if the alarm timestamp is still in the future
|
||||
let t = self.now();
|
||||
if timestamp <= t {
|
||||
// If alarm timestamp has passed since we set it, we have a race condition and
|
||||
// the alarm may or may not have fired.
|
||||
// Disarm the alarm and return `false` to indicate that.
|
||||
// It is the caller's responsibility to handle this ambiguity.
|
||||
r.dier().modify(|w| w.set_ccie(n + 1, false));
|
||||
|
||||
alarm.timestamp.set(u64::MAX);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// We're confident the alarm will ring in the future.
|
||||
true
|
||||
})
|
||||
}
|
||||
}
|
||||
|
15
embassy-time-driver/CHANGELOG.md
Normal file
15
embassy-time-driver/CHANGELOG.md
Normal file
@ -0,0 +1,15 @@
|
||||
# Changelog for embassy-time-driver
|
||||
|
||||
All notable changes to this project will be documented in this file.
|
||||
|
||||
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
||||
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
||||
|
||||
## Unreleased
|
||||
|
||||
- The `allocate_alarm`, `set_alarm_callback`, `set_alarm` functions have been removed.
|
||||
- `schedule_wake` has been added to the `Driver` trait.
|
||||
|
||||
## 0.1.0 - 2024-01-11
|
||||
|
||||
Initial release
|
@ -17,12 +17,75 @@
|
||||
//! Otherwise, don’t enable any `tick-hz-*` feature to let the user configure the tick rate themselves by
|
||||
//! enabling a feature on `embassy-time`.
|
||||
//!
|
||||
//! ### Example
|
||||
//!
|
||||
//! ```
|
||||
//! use core::task::Waker;
|
||||
//!
|
||||
//! use embassy_time_driver::Driver;
|
||||
//!
|
||||
//! struct MyDriver{} // not public!
|
||||
//!
|
||||
//! impl Driver for MyDriver {
|
||||
//! fn now(&self) -> u64 {
|
||||
//! todo!()
|
||||
//! }
|
||||
//!
|
||||
//! fn schedule_wake(&self, at: u64, waker: &Waker) {
|
||||
//! todo!()
|
||||
//! }
|
||||
//! }
|
||||
//!
|
||||
//! embassy_time_driver::time_driver_impl!(static DRIVER: MyDriver = MyDriver{});
|
||||
//! ```
|
||||
//!
|
||||
//! ## Implementing the timer queue
|
||||
//!
|
||||
//! The simplest (but suboptimal) way to implement a timer queue is to define a single queue in the
|
||||
//! time driver. Declare a field protected by an appropriate mutex (e.g. `critical_section::Mutex`).
|
||||
//!
|
||||
//! Then, you'll need to adapt the `schedule_wake` method to use this queue.
|
||||
//!
|
||||
//! ```ignore
|
||||
//! use core::cell::RefCell;
|
||||
//! use core::task::Waker;
|
||||
//!
|
||||
//! use embassy_time_queue_driver::Queue;
|
||||
//! use embassy_time_driver::Driver;
|
||||
//!
|
||||
//! struct MyDriver {
|
||||
//! timer_queue: critical_section::Mutex<RefCell<Queue>>,
|
||||
//! }
|
||||
//!
|
||||
//! impl MyDriver {
|
||||
//! fn set_alarm(&self, cs: &CriticalSection, at: u64) -> bool {
|
||||
//! todo!()
|
||||
//! }
|
||||
//! }
|
||||
//!
|
||||
//! impl Driver for MyDriver {
|
||||
//! // fn now(&self) -> u64 { ... }
|
||||
//!
|
||||
//! fn schedule_wake(&self, at: u64, waker: &Waker) {
|
||||
//! critical_section::with(|cs| {
|
||||
//! let mut queue = self.queue.borrow(cs).borrow_mut();
|
||||
//! if queue.schedule_wake(at, waker) {
|
||||
//! let mut next = queue.next_expiration(self.now());
|
||||
//! while !self.set_alarm(cs, next) {
|
||||
//! next = queue.next_expiration(self.now());
|
||||
//! }
|
||||
//! }
|
||||
//! });
|
||||
//! }
|
||||
//! }
|
||||
//! ```
|
||||
//!
|
||||
//! # 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
|
||||
//! `embassy` internally defines the driver function as `extern "Rust" { fn _embassy_time_now() -> u64; }` and calls it.
|
||||
//! The driver crate defines the function 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.
|
||||
@ -34,35 +97,12 @@
|
||||
//! - 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!
|
||||
//!
|
||||
//! 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) -> bool {
|
||||
//! todo!()
|
||||
//! }
|
||||
//! }
|
||||
//!
|
||||
//! embassy_time_driver::time_driver_impl!(static DRIVER: MyDriver = MyDriver{});
|
||||
//! ```
|
||||
|
||||
//! ## Feature flags
|
||||
#![doc = document_features::document_features!(feature_label = r#"<span class="stab portability"><code>{feature}</code></span>"#)]
|
||||
|
||||
use core::task::Waker;
|
||||
|
||||
mod tick;
|
||||
|
||||
/// Ticks per second of the global timebase.
|
||||
@ -70,28 +110,6 @@ mod tick;
|
||||
/// This value is specified by the [`tick-*` Cargo features](crate#tick-rate)
|
||||
pub const TICK_HZ: u64 = tick::TICK_HZ;
|
||||
|
||||
/// 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.
|
||||
@ -106,75 +124,13 @@ pub trait Driver: Send + Sync + 'static {
|
||||
/// 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.
|
||||
///
|
||||
/// The allocated alarm is a reusable resource and can be used multiple times.
|
||||
/// Once the alarm has fired, it remains allocated and can be set again without needing
|
||||
/// to be reallocated.
|
||||
///
|
||||
/// # Safety
|
||||
/// It is UB to make the alarm fire before setting a callback.
|
||||
unsafe fn allocate_alarm(&self) -> Option<AlarmHandle>;
|
||||
|
||||
/// Set the callback function to be called when the alarm triggers.
|
||||
/// The callback may be called from any context (interrupt or thread mode).
|
||||
///
|
||||
/// The callback is maintained after the alarm has fired. Callers do not need
|
||||
/// to set a callback again before setting another alarm, unless they want to
|
||||
/// change the callback function or context.
|
||||
fn set_alarm_callback(&self, alarm: AlarmHandle, callback: fn(*mut ()), ctx: *mut ());
|
||||
|
||||
/// Set an alarm at the given timestamp.
|
||||
///
|
||||
/// ## Behavior
|
||||
///
|
||||
/// If `timestamp` is in the future, `set_alarm` schedules calling the callback function
|
||||
/// at that time, and returns `true`.
|
||||
///
|
||||
/// If `timestamp` is in the past, `set_alarm` has two allowed behaviors. Implementations can pick whether to:
|
||||
///
|
||||
/// - Schedule calling the callback function "immediately", as if the requested timestamp was "now+epsilon" and return `true`, or
|
||||
/// - Not schedule the callback, and return `false`.
|
||||
///
|
||||
/// Callers must ensure to behave correctly with either behavior.
|
||||
///
|
||||
/// When callback is called, it is guaranteed that `now()` will return a value greater than or equal to `timestamp`.
|
||||
///
|
||||
/// ## Reentrancy
|
||||
///
|
||||
/// Calling the callback from `set_alarm` synchronously is not allowed. If the implementation chooses the first option above,
|
||||
/// it must still call the callback from another context (i.e. an interrupt handler or background thread), it's not allowed
|
||||
/// to call it synchronously in the context `set_alarm` is running.
|
||||
///
|
||||
/// The reason for the above is callers are explicitly permitted to do both of:
|
||||
/// - Lock a mutex in the alarm callback.
|
||||
/// - Call `set_alarm` while having that mutex locked.
|
||||
///
|
||||
/// If `set_alarm` called the callback synchronously, it'd cause a deadlock or panic because it'd cause the
|
||||
/// mutex to be locked twice reentrantly in the same context.
|
||||
///
|
||||
/// ## Overwriting alarms
|
||||
///
|
||||
/// Only one alarm can be active at a time for each `AlarmHandle`. This overwrites any previously-set alarm if any.
|
||||
///
|
||||
/// ## Unsetting the alarm
|
||||
///
|
||||
/// There is no `unset_alarm` API. Instead, callers can call `set_alarm` with `timestamp` set to `u64::MAX`.
|
||||
///
|
||||
/// This allows for more efficient implementations, since they don't need to distinguish between the "alarm set" and
|
||||
/// "alarm not set" cases, thanks to the fact "Alarm set for u64::MAX" is effectively equivalent for "alarm not set".
|
||||
///
|
||||
/// This means implementations need to be careful to avoid timestamp overflows. The recommendation is to make `timestamp`
|
||||
/// be in the same units as hardware ticks to avoid any conversions, which makes avoiding overflow easier.
|
||||
fn set_alarm(&self, alarm: AlarmHandle, timestamp: u64) -> bool;
|
||||
/// Schedules a waker to be awoken at moment `at`.
|
||||
/// If this moment is in the past, the waker might be awoken immediately.
|
||||
fn schedule_wake(&self, at: u64, waker: &Waker);
|
||||
}
|
||||
|
||||
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) -> bool;
|
||||
}
|
||||
|
||||
/// See [`Driver::now`]
|
||||
@ -182,23 +138,6 @@ 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) -> bool {
|
||||
unsafe { _embassy_time_set_alarm(alarm, timestamp) }
|
||||
}
|
||||
|
||||
/// Set the time Driver implementation.
|
||||
///
|
||||
/// See the module documentation for an example.
|
||||
@ -213,18 +152,8 @@ macro_rules! time_driver_impl {
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
unsafe fn _embassy_time_allocate_alarm() -> Option<$crate::AlarmHandle> {
|
||||
<$t as $crate::Driver>::allocate_alarm(&$name)
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
fn _embassy_time_set_alarm_callback(alarm: $crate::AlarmHandle, callback: fn(*mut ()), ctx: *mut ()) {
|
||||
<$t as $crate::Driver>::set_alarm_callback(&$name, alarm, callback, ctx)
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
fn _embassy_time_set_alarm(alarm: $crate::AlarmHandle, timestamp: u64) -> bool {
|
||||
<$t as $crate::Driver>::set_alarm(&$name, alarm, timestamp)
|
||||
fn _embassy_time_schedule_wake(at: u64, waker: &core::task::Waker) {
|
||||
<$t as $crate::Driver>::schedule_wake(&$name, at, waker);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
15
embassy-time-queue-driver/CHANGELOG.md
Normal file
15
embassy-time-queue-driver/CHANGELOG.md
Normal file
@ -0,0 +1,15 @@
|
||||
# Changelog for embassy-time-queue-driver
|
||||
|
||||
All notable changes to this project will be documented in this file.
|
||||
|
||||
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
||||
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
||||
|
||||
## Unreleased
|
||||
|
||||
- Added `generic-queue-N` features.
|
||||
- Added `embassy_time_queue_driver::Queue` struct which uses integrated or a generic storage (configured using `generic-queue-N`).
|
||||
|
||||
## 0.1.0 - 2024-01-11
|
||||
|
||||
Initial release
|
@ -20,6 +20,38 @@ categories = [
|
||||
# This is especially common when mixing crates from crates.io and git.
|
||||
links = "embassy-time-queue"
|
||||
|
||||
[dependencies]
|
||||
heapless = "0.8"
|
||||
embassy-executor = { version = "0.6.3", path = "../embassy-executor" }
|
||||
|
||||
[features]
|
||||
#! ### Generic Queue
|
||||
|
||||
#! By default this crate uses a timer queue implementation that is faster but depends on `embassy-executor`.
|
||||
#! It will panic if you try to await any timer when using another executor.
|
||||
#!
|
||||
#! Alternatively, you can choose to use a "generic" timer queue implementation that works on any executor.
|
||||
#! To enable it, enable any of the features below.
|
||||
#!
|
||||
#! The features also set how many timers are used for the generic queue. At most one
|
||||
#! `generic-queue-*` feature can be enabled. If none is enabled, a default of 64 timers is used.
|
||||
#!
|
||||
#! When using embassy-time from libraries, you should *not* enable any `generic-queue-*` feature, to allow the
|
||||
#! end user to pick.
|
||||
|
||||
## Generic Queue with 8 timers
|
||||
generic-queue-8 = ["_generic-queue"]
|
||||
## Generic Queue with 16 timers
|
||||
generic-queue-16 = ["_generic-queue"]
|
||||
## Generic Queue with 32 timers
|
||||
generic-queue-32 = ["_generic-queue"]
|
||||
## Generic Queue with 64 timers
|
||||
generic-queue-64 = ["_generic-queue"]
|
||||
## Generic Queue with 128 timers
|
||||
generic-queue-128 = ["_generic-queue"]
|
||||
|
||||
_generic-queue = []
|
||||
|
||||
[package.metadata.embassy_docs]
|
||||
src_base = "https://github.com/embassy-rs/embassy/blob/embassy-time-queue-driver-v$VERSION/embassy-time-queue-driver/src/"
|
||||
src_base_git = "https://github.com/embassy-rs/embassy/blob/$COMMIT/embassy-time-queue-driver/src/"
|
||||
|
@ -2,38 +2,25 @@
|
||||
#![doc = include_str!("../README.md")]
|
||||
#![warn(missing_docs)]
|
||||
|
||||
//! ## Implementing a timer queue
|
||||
//! This crate is an implementation detail of `embassy-time-driver`.
|
||||
//!
|
||||
//! - Define a struct `MyTimerQueue`
|
||||
//! - Implement [`TimerQueue`] for it
|
||||
//! - Register it as the global timer queue with [`timer_queue_impl`](crate::timer_queue_impl).
|
||||
//! As a HAL user, you should only depend on this crate if your application does not use
|
||||
//! `embassy-executor` and your HAL does not configure a generic queue by itself.
|
||||
//!
|
||||
//! ## Example
|
||||
//!
|
||||
//! ```
|
||||
//! use core::task::Waker;
|
||||
//!
|
||||
//! use embassy_time::Instant;
|
||||
//! use embassy_time::queue::{TimerQueue};
|
||||
//!
|
||||
//! struct MyTimerQueue{}; // not public!
|
||||
//!
|
||||
//! impl TimerQueue for MyTimerQueue {
|
||||
//! fn schedule_wake(&'static self, at: u64, waker: &Waker) {
|
||||
//! todo!()
|
||||
//! }
|
||||
//! }
|
||||
//!
|
||||
//! embassy_time_queue_driver::timer_queue_impl!(static QUEUE: MyTimerQueue = MyTimerQueue{});
|
||||
//! ```
|
||||
//! As a HAL implementer, you need to depend on this crate if you want to implement a time driver,
|
||||
//! but how you should do so is documented in `embassy-time-driver`.
|
||||
|
||||
use core::task::Waker;
|
||||
|
||||
/// Timer queue
|
||||
pub trait TimerQueue {
|
||||
/// Schedules a waker in the queue to be awoken at moment `at`.
|
||||
/// If this moment is in the past, the waker might be awoken immediately.
|
||||
fn schedule_wake(&'static self, at: u64, waker: &Waker);
|
||||
}
|
||||
#[cfg(feature = "_generic-queue")]
|
||||
pub mod queue_generic;
|
||||
#[cfg(not(feature = "_generic-queue"))]
|
||||
pub mod queue_integrated;
|
||||
|
||||
#[cfg(feature = "_generic-queue")]
|
||||
pub use queue_generic::Queue;
|
||||
#[cfg(not(feature = "_generic-queue"))]
|
||||
pub use queue_integrated::Queue;
|
||||
|
||||
extern "Rust" {
|
||||
fn _embassy_time_schedule_wake(at: u64, waker: &Waker);
|
||||
@ -41,20 +28,22 @@ extern "Rust" {
|
||||
|
||||
/// Schedule the given waker to be woken at `at`.
|
||||
pub fn schedule_wake(at: u64, waker: &Waker) {
|
||||
// This function is not implemented in embassy-time-driver because it needs access to executor
|
||||
// internals. The function updates task state, then delegates to the implementation provided
|
||||
// by the time driver.
|
||||
#[cfg(not(feature = "_generic-queue"))]
|
||||
{
|
||||
use embassy_executor::raw::task_from_waker;
|
||||
use embassy_executor::raw::timer_queue::TimerEnqueueOperation;
|
||||
// The very first thing we must do, before we even access the timer queue, is to
|
||||
// mark the task a TIMER_QUEUED. This ensures that the task that is being scheduled
|
||||
// can not be respawn while we are accessing the timer queue.
|
||||
let task = task_from_waker(waker);
|
||||
if unsafe { task.timer_enqueue() } == TimerEnqueueOperation::Ignore {
|
||||
// We are not allowed to enqueue the task in the timer queue. This is because the
|
||||
// task is not spawned, and so it makes no sense to schedule it.
|
||||
return;
|
||||
}
|
||||
}
|
||||
unsafe { _embassy_time_schedule_wake(at, waker) }
|
||||
}
|
||||
|
||||
/// Set the TimerQueue implementation.
|
||||
///
|
||||
/// See the module documentation for an example.
|
||||
#[macro_export]
|
||||
macro_rules! timer_queue_impl {
|
||||
(static $name:ident: $t: ty = $val:expr) => {
|
||||
static $name: $t = $val;
|
||||
|
||||
#[no_mangle]
|
||||
fn _embassy_time_schedule_wake(at: u64, waker: &core::task::Waker) {
|
||||
<$t as $crate::TimerQueue>::schedule_wake(&$name, at, waker);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
146
embassy-time-queue-driver/src/queue_generic.rs
Normal file
146
embassy-time-queue-driver/src/queue_generic.rs
Normal file
@ -0,0 +1,146 @@
|
||||
//! Generic timer queue implementations.
|
||||
//!
|
||||
//! Time queue drivers may use this to simplify their implementation.
|
||||
|
||||
use core::cmp::{min, Ordering};
|
||||
use core::task::Waker;
|
||||
|
||||
use heapless::Vec;
|
||||
|
||||
#[derive(Debug)]
|
||||
struct Timer {
|
||||
at: u64,
|
||||
waker: Waker,
|
||||
}
|
||||
|
||||
impl PartialEq for Timer {
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
self.at == other.at
|
||||
}
|
||||
}
|
||||
|
||||
impl Eq for Timer {}
|
||||
|
||||
impl PartialOrd for Timer {
|
||||
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
|
||||
self.at.partial_cmp(&other.at)
|
||||
}
|
||||
}
|
||||
|
||||
impl Ord for Timer {
|
||||
fn cmp(&self, other: &Self) -> Ordering {
|
||||
self.at.cmp(&other.at)
|
||||
}
|
||||
}
|
||||
|
||||
/// A timer queue with a pre-determined capacity.
|
||||
pub struct ConstGenericQueue<const QUEUE_SIZE: usize> {
|
||||
queue: Vec<Timer, QUEUE_SIZE>,
|
||||
}
|
||||
|
||||
impl<const QUEUE_SIZE: usize> ConstGenericQueue<QUEUE_SIZE> {
|
||||
/// Creates a new timer queue.
|
||||
pub const fn new() -> Self {
|
||||
Self { queue: Vec::new() }
|
||||
}
|
||||
|
||||
/// Schedules a task to run at a specific time, and returns whether any changes were made.
|
||||
///
|
||||
/// If this function returns `true`, the called should find the next expiration time and set
|
||||
/// a new alarm for that time.
|
||||
pub fn schedule_wake(&mut self, at: u64, waker: &Waker) -> bool {
|
||||
self.queue
|
||||
.iter_mut()
|
||||
.find(|timer| timer.waker.will_wake(waker))
|
||||
.map(|timer| {
|
||||
if timer.at > at {
|
||||
timer.at = at;
|
||||
true
|
||||
} else {
|
||||
false
|
||||
}
|
||||
})
|
||||
.unwrap_or_else(|| {
|
||||
let mut timer = Timer {
|
||||
waker: waker.clone(),
|
||||
at,
|
||||
};
|
||||
|
||||
loop {
|
||||
match self.queue.push(timer) {
|
||||
Ok(()) => break,
|
||||
Err(e) => timer = e,
|
||||
}
|
||||
|
||||
self.queue.pop().unwrap().waker.wake();
|
||||
}
|
||||
|
||||
true
|
||||
})
|
||||
}
|
||||
|
||||
/// Dequeues expired timers and returns the next alarm time.
|
||||
pub fn next_expiration(&mut self, now: u64) -> u64 {
|
||||
let mut next_alarm = u64::MAX;
|
||||
|
||||
let mut i = 0;
|
||||
while i < self.queue.len() {
|
||||
let timer = &self.queue[i];
|
||||
if timer.at <= now {
|
||||
let timer = self.queue.swap_remove(i);
|
||||
timer.waker.wake();
|
||||
} else {
|
||||
next_alarm = min(next_alarm, timer.at);
|
||||
i += 1;
|
||||
}
|
||||
}
|
||||
|
||||
next_alarm
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "generic-queue-8")]
|
||||
const QUEUE_SIZE: usize = 8;
|
||||
#[cfg(feature = "generic-queue-16")]
|
||||
const QUEUE_SIZE: usize = 16;
|
||||
#[cfg(feature = "generic-queue-32")]
|
||||
const QUEUE_SIZE: usize = 32;
|
||||
#[cfg(feature = "generic-queue-64")]
|
||||
const QUEUE_SIZE: usize = 64;
|
||||
#[cfg(feature = "generic-queue-128")]
|
||||
const QUEUE_SIZE: usize = 128;
|
||||
#[cfg(not(any(
|
||||
feature = "generic-queue-8",
|
||||
feature = "generic-queue-16",
|
||||
feature = "generic-queue-32",
|
||||
feature = "generic-queue-64",
|
||||
feature = "generic-queue-128"
|
||||
)))]
|
||||
const QUEUE_SIZE: usize = 64;
|
||||
|
||||
/// A timer queue with a pre-determined capacity.
|
||||
pub struct Queue {
|
||||
queue: ConstGenericQueue<QUEUE_SIZE>,
|
||||
}
|
||||
|
||||
impl Queue {
|
||||
/// Creates a new timer queue.
|
||||
pub const fn new() -> Self {
|
||||
Self {
|
||||
queue: ConstGenericQueue::new(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Schedules a task to run at a specific time, and returns whether any changes were made.
|
||||
///
|
||||
/// If this function returns `true`, the called should find the next expiration time and set
|
||||
/// a new alarm for that time.
|
||||
pub fn schedule_wake(&mut self, at: u64, waker: &Waker) -> bool {
|
||||
self.queue.schedule_wake(at, waker)
|
||||
}
|
||||
|
||||
/// Dequeues expired timers and returns the next alarm time.
|
||||
pub fn next_expiration(&mut self, now: u64) -> u64 {
|
||||
self.queue.next_expiration(now)
|
||||
}
|
||||
}
|
90
embassy-time-queue-driver/src/queue_integrated.rs
Normal file
90
embassy-time-queue-driver/src/queue_integrated.rs
Normal file
@ -0,0 +1,90 @@
|
||||
//! Timer queue operations.
|
||||
use core::cell::Cell;
|
||||
use core::cmp::min;
|
||||
use core::task::Waker;
|
||||
|
||||
use embassy_executor::raw::TaskRef;
|
||||
|
||||
/// A timer queue, with items integrated into tasks.
|
||||
pub struct Queue {
|
||||
head: Cell<Option<TaskRef>>,
|
||||
}
|
||||
|
||||
impl Queue {
|
||||
/// Creates a new timer queue.
|
||||
pub const fn new() -> Self {
|
||||
Self { head: Cell::new(None) }
|
||||
}
|
||||
|
||||
/// Schedules a task to run at a specific time.
|
||||
///
|
||||
/// If this function returns `true`, the called should find the next expiration time and set
|
||||
/// a new alarm for that time.
|
||||
pub fn schedule_wake(&mut self, at: u64, waker: &Waker) -> bool {
|
||||
let task = embassy_executor::raw::task_from_waker(waker);
|
||||
let item = task.timer_queue_item();
|
||||
if item.next.get().is_none() {
|
||||
// If not in the queue, add it and update.
|
||||
let prev = self.head.replace(Some(task));
|
||||
item.next.set(if prev.is_none() {
|
||||
Some(unsafe { TaskRef::dangling() })
|
||||
} else {
|
||||
prev
|
||||
});
|
||||
item.expires_at.set(at);
|
||||
true
|
||||
} else if at <= item.expires_at.get() {
|
||||
// If expiration is sooner than previously set, update.
|
||||
item.expires_at.set(at);
|
||||
true
|
||||
} else {
|
||||
// Task does not need to be updated.
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
/// Dequeues expired timers and returns the next alarm time.
|
||||
///
|
||||
/// The provided callback will be called for each expired task. Tasks that never expire
|
||||
/// will be removed, but the callback will not be called.
|
||||
pub fn next_expiration(&mut self, now: u64) -> u64 {
|
||||
let mut next_expiration = u64::MAX;
|
||||
|
||||
self.retain(|p| {
|
||||
let item = p.timer_queue_item();
|
||||
let expires = item.expires_at.get();
|
||||
|
||||
if expires <= now {
|
||||
// Timer expired, process task.
|
||||
embassy_executor::raw::wake_task(p);
|
||||
false
|
||||
} else {
|
||||
// Timer didn't yet expire, or never expires.
|
||||
next_expiration = min(next_expiration, expires);
|
||||
expires != u64::MAX
|
||||
}
|
||||
});
|
||||
|
||||
next_expiration
|
||||
}
|
||||
|
||||
fn retain(&self, mut f: impl FnMut(TaskRef) -> bool) {
|
||||
let mut prev = &self.head;
|
||||
while let Some(p) = prev.get() {
|
||||
if unsafe { p == TaskRef::dangling() } {
|
||||
// prev was the last item, stop
|
||||
break;
|
||||
}
|
||||
let item = p.timer_queue_item();
|
||||
if f(p) {
|
||||
// Skip to next
|
||||
prev = &item.next;
|
||||
} else {
|
||||
// Remove it
|
||||
prev.set(item.next.get());
|
||||
item.next.set(None);
|
||||
unsafe { p.timer_dequeue() };
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -7,10 +7,13 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
||||
|
||||
## Unreleased
|
||||
|
||||
- The `generic-queue` and related features have been removed (moved to embassy-time-queue-driver)
|
||||
- embassy-time no longer provides an `embassy-time-queue-driver` implementation
|
||||
|
||||
## 0.3.2 - 2024-08-05
|
||||
|
||||
- Implement with_timeout()/with_deadline() method style call on Future
|
||||
- Add collapse_debuginfo to fmt.rs macros.
|
||||
- Add collapse_debuginfo to fmt.rs macros.
|
||||
|
||||
## 0.3.1 - 2024-01-11
|
||||
|
||||
|
@ -42,29 +42,6 @@ defmt-timestamp-uptime-tus = ["defmt"]
|
||||
## Create a `MockDriver` that can be manually advanced for testing purposes.
|
||||
mock-driver = ["tick-hz-1_000_000"]
|
||||
|
||||
#! ### Generic Queue
|
||||
|
||||
## Create a global, generic queue that can be used with any executor.
|
||||
## To use this you must have a time driver provided.
|
||||
generic-queue = []
|
||||
|
||||
#! The following features set how many timers are used for the generic queue. At most one
|
||||
#! `generic-queue-*` feature can be enabled. If none is enabled, a default of 64 timers is used.
|
||||
#!
|
||||
#! When using embassy-time from libraries, you should *not* enable any `generic-queue-*` feature, to allow the
|
||||
#! end user to pick.
|
||||
|
||||
## Generic Queue with 8 timers
|
||||
generic-queue-8 = ["generic-queue"]
|
||||
## Generic Queue with 16 timers
|
||||
generic-queue-16 = ["generic-queue"]
|
||||
## Generic Queue with 32 timers
|
||||
generic-queue-32 = ["generic-queue"]
|
||||
## Generic Queue with 64 timers
|
||||
generic-queue-64 = ["generic-queue"]
|
||||
## Generic Queue with 128 timers
|
||||
generic-queue-128 = ["generic-queue"]
|
||||
|
||||
#! ### Tick Rate
|
||||
#!
|
||||
#! At most 1 `tick-*` feature can be enabled. If none is enabled, a default of 1MHz is used.
|
||||
@ -419,7 +396,6 @@ embedded-hal-async = { version = "1.0" }
|
||||
futures-util = { version = "0.3.17", default-features = false }
|
||||
critical-section = "1.1"
|
||||
cfg-if = "1.0.0"
|
||||
heapless = "0.8"
|
||||
|
||||
document-features = "0.2.7"
|
||||
|
||||
|
@ -1,7 +1,9 @@
|
||||
use core::cell::RefCell;
|
||||
use core::task::Waker;
|
||||
|
||||
use critical_section::Mutex as CsMutex;
|
||||
use embassy_time_driver::{AlarmHandle, Driver};
|
||||
use embassy_time_driver::Driver;
|
||||
use embassy_time_queue_driver::Queue;
|
||||
|
||||
use crate::{Duration, Instant};
|
||||
|
||||
@ -52,29 +54,13 @@ impl MockDriver {
|
||||
/// Advances the time by the specified [`Duration`].
|
||||
/// Calling any alarm callbacks that are due.
|
||||
pub fn advance(&self, duration: Duration) {
|
||||
let notify = {
|
||||
critical_section::with(|cs| {
|
||||
let mut inner = self.0.borrow_ref_mut(cs);
|
||||
critical_section::with(|cs| {
|
||||
let inner = &mut *self.0.borrow_ref_mut(cs);
|
||||
|
||||
inner.now += duration;
|
||||
|
||||
let now = inner.now.as_ticks();
|
||||
|
||||
inner
|
||||
.alarm
|
||||
.as_mut()
|
||||
.filter(|alarm| alarm.timestamp <= now)
|
||||
.map(|alarm| {
|
||||
alarm.timestamp = u64::MAX;
|
||||
|
||||
(alarm.callback, alarm.ctx)
|
||||
})
|
||||
})
|
||||
};
|
||||
|
||||
if let Some((callback, ctx)) = notify {
|
||||
(callback)(ctx);
|
||||
}
|
||||
inner.now += duration;
|
||||
// wake expired tasks.
|
||||
inner.queue.next_expiration(inner.now.as_ticks());
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@ -83,87 +69,37 @@ impl Driver for MockDriver {
|
||||
critical_section::with(|cs| self.0.borrow_ref(cs).now).as_ticks()
|
||||
}
|
||||
|
||||
unsafe fn allocate_alarm(&self) -> Option<AlarmHandle> {
|
||||
fn schedule_wake(&self, at: u64, waker: &Waker) {
|
||||
critical_section::with(|cs| {
|
||||
let mut inner = self.0.borrow_ref_mut(cs);
|
||||
|
||||
if inner.alarm.is_some() {
|
||||
None
|
||||
} else {
|
||||
inner.alarm.replace(AlarmState::new());
|
||||
|
||||
Some(AlarmHandle::new(0))
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
fn set_alarm_callback(&self, _alarm: AlarmHandle, callback: fn(*mut ()), ctx: *mut ()) {
|
||||
critical_section::with(|cs| {
|
||||
let mut inner = self.0.borrow_ref_mut(cs);
|
||||
|
||||
let Some(alarm) = inner.alarm.as_mut() else {
|
||||
panic!("Alarm not allocated");
|
||||
};
|
||||
|
||||
alarm.callback = callback;
|
||||
alarm.ctx = ctx;
|
||||
});
|
||||
}
|
||||
|
||||
fn set_alarm(&self, _alarm: AlarmHandle, timestamp: u64) -> bool {
|
||||
critical_section::with(|cs| {
|
||||
let mut inner = self.0.borrow_ref_mut(cs);
|
||||
|
||||
if timestamp <= inner.now.as_ticks() {
|
||||
false
|
||||
} else {
|
||||
let Some(alarm) = inner.alarm.as_mut() else {
|
||||
panic!("Alarm not allocated");
|
||||
};
|
||||
|
||||
alarm.timestamp = timestamp;
|
||||
true
|
||||
}
|
||||
let inner = &mut *self.0.borrow_ref_mut(cs);
|
||||
// enqueue it
|
||||
inner.queue.schedule_wake(at, waker);
|
||||
// wake it if it's in the past.
|
||||
inner.queue.next_expiration(inner.now.as_ticks());
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
struct InnerMockDriver {
|
||||
now: Instant,
|
||||
alarm: Option<AlarmState>,
|
||||
queue: Queue,
|
||||
}
|
||||
|
||||
impl InnerMockDriver {
|
||||
const fn new() -> Self {
|
||||
Self {
|
||||
now: Instant::from_ticks(0),
|
||||
alarm: None,
|
||||
queue: Queue::new(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct AlarmState {
|
||||
timestamp: u64,
|
||||
callback: fn(*mut ()),
|
||||
ctx: *mut (),
|
||||
}
|
||||
|
||||
impl AlarmState {
|
||||
const fn new() -> Self {
|
||||
Self {
|
||||
timestamp: u64::MAX,
|
||||
callback: Self::noop,
|
||||
ctx: core::ptr::null_mut(),
|
||||
}
|
||||
}
|
||||
|
||||
fn noop(_ctx: *mut ()) {}
|
||||
}
|
||||
|
||||
unsafe impl Send for AlarmState {}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use core::sync::atomic::{AtomicBool, Ordering};
|
||||
use std::sync::Arc;
|
||||
use std::task::Wake;
|
||||
|
||||
use serial_test::serial;
|
||||
|
||||
use super::*;
|
||||
@ -185,37 +121,25 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
#[serial]
|
||||
fn test_set_alarm_not_in_future() {
|
||||
fn test_schedule_wake() {
|
||||
setup();
|
||||
|
||||
let driver = MockDriver::get();
|
||||
let alarm = unsafe { AlarmHandle::new(0) };
|
||||
assert_eq!(false, driver.set_alarm(alarm, driver.now()));
|
||||
}
|
||||
static CALLBACK_CALLED: AtomicBool = AtomicBool::new(false);
|
||||
|
||||
#[test]
|
||||
#[serial]
|
||||
fn test_alarm() {
|
||||
setup();
|
||||
struct MockWaker;
|
||||
|
||||
impl Wake for MockWaker {
|
||||
fn wake(self: Arc<Self>) {
|
||||
CALLBACK_CALLED.store(true, Ordering::Relaxed);
|
||||
}
|
||||
}
|
||||
let waker = Arc::new(MockWaker).into();
|
||||
|
||||
let driver = MockDriver::get();
|
||||
let alarm = unsafe { driver.allocate_alarm() }.expect("No alarms available");
|
||||
static mut CALLBACK_CALLED: bool = false;
|
||||
let ctx = &mut () as *mut ();
|
||||
driver.set_alarm_callback(alarm, |_| unsafe { CALLBACK_CALLED = true }, ctx);
|
||||
driver.set_alarm(alarm, driver.now() + 1);
|
||||
assert_eq!(false, unsafe { CALLBACK_CALLED });
|
||||
|
||||
driver.schedule_wake(driver.now() + 1, &waker);
|
||||
assert_eq!(false, CALLBACK_CALLED.load(Ordering::Relaxed));
|
||||
driver.advance(Duration::from_secs(1));
|
||||
assert_eq!(true, unsafe { CALLBACK_CALLED });
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[serial]
|
||||
fn test_allocate_alarm() {
|
||||
setup();
|
||||
|
||||
let driver = MockDriver::get();
|
||||
assert!(unsafe { driver.allocate_alarm() }.is_some());
|
||||
assert!(unsafe { driver.allocate_alarm() }.is_none());
|
||||
assert_eq!(true, CALLBACK_CALLED.load(Ordering::Relaxed));
|
||||
}
|
||||
}
|
||||
|
@ -1,160 +1,66 @@
|
||||
use core::sync::atomic::{AtomicU8, Ordering};
|
||||
use std::cell::{RefCell, UnsafeCell};
|
||||
use std::mem::MaybeUninit;
|
||||
use std::sync::{Condvar, Mutex, Once};
|
||||
use std::sync::{Condvar, Mutex};
|
||||
use std::thread;
|
||||
use std::time::{Duration as StdDuration, Instant as StdInstant};
|
||||
use std::{mem, ptr, thread};
|
||||
|
||||
use critical_section::Mutex as CsMutex;
|
||||
use embassy_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(),
|
||||
}
|
||||
}
|
||||
}
|
||||
use embassy_time_driver::Driver;
|
||||
use embassy_time_queue_driver::Queue;
|
||||
|
||||
struct TimeDriver {
|
||||
alarm_count: AtomicU8,
|
||||
signaler: Signaler,
|
||||
inner: Mutex<Inner>,
|
||||
}
|
||||
|
||||
once: Once,
|
||||
// The STD Driver implementation requires the alarms' mutex to be reentrant, which the STD Mutex isn't
|
||||
// Fortunately, mutexes based on the `critical-section` crate are reentrant, because the critical sections
|
||||
// themselves are reentrant
|
||||
alarms: UninitCell<CsMutex<RefCell<[AlarmState; ALARM_COUNT]>>>,
|
||||
zero_instant: UninitCell<StdInstant>,
|
||||
signaler: UninitCell<Signaler>,
|
||||
struct Inner {
|
||||
zero_instant: Option<StdInstant>,
|
||||
queue: Queue,
|
||||
}
|
||||
|
||||
embassy_time_driver::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(),
|
||||
inner: Mutex::new(Inner{
|
||||
zero_instant: None,
|
||||
queue: Queue::new(),
|
||||
}),
|
||||
signaler: Signaler::new(),
|
||||
});
|
||||
|
||||
impl TimeDriver {
|
||||
fn init(&self) {
|
||||
self.once.call_once(|| unsafe {
|
||||
self.alarms
|
||||
.write(CsMutex::new(RefCell::new([const { AlarmState::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 next_alarm = critical_section::with(|cs| {
|
||||
let alarms = unsafe { DRIVER.alarms.as_ref() }.borrow(cs);
|
||||
loop {
|
||||
let pending = alarms
|
||||
.borrow_mut()
|
||||
.iter_mut()
|
||||
.find(|alarm| alarm.timestamp <= now)
|
||||
.map(|alarm| {
|
||||
alarm.timestamp = u64::MAX;
|
||||
|
||||
(alarm.callback, alarm.ctx)
|
||||
});
|
||||
|
||||
if let Some((callback, ctx)) = pending {
|
||||
// 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(callback) };
|
||||
f(ctx);
|
||||
} else {
|
||||
// No alarm due
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
alarms
|
||||
.borrow()
|
||||
.iter()
|
||||
.map(|alarm| alarm.timestamp)
|
||||
.min()
|
||||
.unwrap_or(u64::MAX)
|
||||
});
|
||||
|
||||
// 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 Inner {
|
||||
fn init(&mut self) -> StdInstant {
|
||||
*self.zero_instant.get_or_insert_with(|| {
|
||||
thread::spawn(alarm_thread);
|
||||
StdInstant::now()
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl Driver for TimeDriver {
|
||||
fn now(&self) -> u64 {
|
||||
self.init();
|
||||
|
||||
let zero = unsafe { self.zero_instant.read() };
|
||||
let mut inner = self.inner.lock().unwrap();
|
||||
let zero = inner.init();
|
||||
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 schedule_wake(&self, at: u64, waker: &core::task::Waker) {
|
||||
let mut inner = self.inner.lock().unwrap();
|
||||
inner.init();
|
||||
if inner.queue.schedule_wake(at, waker) {
|
||||
self.signaler.signal();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn set_alarm_callback(&self, alarm: AlarmHandle, callback: fn(*mut ()), ctx: *mut ()) {
|
||||
self.init();
|
||||
critical_section::with(|cs| {
|
||||
let mut alarms = unsafe { self.alarms.as_ref() }.borrow_ref_mut(cs);
|
||||
let alarm = &mut alarms[alarm.id() as usize];
|
||||
alarm.callback = callback as *const ();
|
||||
alarm.ctx = ctx;
|
||||
});
|
||||
}
|
||||
fn alarm_thread() {
|
||||
let zero = DRIVER.inner.lock().unwrap().zero_instant.unwrap();
|
||||
loop {
|
||||
let now = DRIVER.now();
|
||||
|
||||
fn set_alarm(&self, alarm: AlarmHandle, timestamp: u64) -> bool {
|
||||
self.init();
|
||||
critical_section::with(|cs| {
|
||||
let mut alarms = unsafe { self.alarms.as_ref() }.borrow_ref_mut(cs);
|
||||
let alarm = &mut alarms[alarm.id() as usize];
|
||||
alarm.timestamp = timestamp;
|
||||
unsafe { self.signaler.as_ref() }.signal();
|
||||
});
|
||||
let next_alarm = DRIVER.inner.lock().unwrap().queue.next_expiration(now);
|
||||
|
||||
true
|
||||
// Ensure we don't overflow
|
||||
let until = zero
|
||||
.checked_add(StdDuration::from_micros(next_alarm))
|
||||
.unwrap_or_else(|| StdInstant::now() + StdDuration::from_secs(1));
|
||||
|
||||
DRIVER.signaler.wait_until(until);
|
||||
}
|
||||
}
|
||||
|
||||
@ -164,7 +70,7 @@ struct Signaler {
|
||||
}
|
||||
|
||||
impl Signaler {
|
||||
fn new() -> Self {
|
||||
const fn new() -> Self {
|
||||
Self {
|
||||
mutex: Mutex::new(false),
|
||||
condvar: Condvar::new(),
|
||||
@ -196,35 +102,3 @@ impl Signaler {
|
||||
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())
|
||||
}
|
||||
}
|
||||
|
@ -1,28 +1,17 @@
|
||||
use core::sync::atomic::{AtomicU8, Ordering};
|
||||
use std::cell::UnsafeCell;
|
||||
use std::mem::MaybeUninit;
|
||||
use std::ptr;
|
||||
use std::sync::{Mutex, Once};
|
||||
use std::sync::Mutex;
|
||||
|
||||
use embassy_time_driver::{AlarmHandle, Driver};
|
||||
use embassy_time_driver::Driver;
|
||||
use embassy_time_queue_driver::Queue;
|
||||
use wasm_bindgen::prelude::*;
|
||||
use wasm_timer::Instant as StdInstant;
|
||||
|
||||
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,
|
||||
}
|
||||
Self { token: None }
|
||||
}
|
||||
}
|
||||
|
||||
@ -33,67 +22,38 @@ extern "C" {
|
||||
}
|
||||
|
||||
struct TimeDriver {
|
||||
alarm_count: AtomicU8,
|
||||
|
||||
once: Once,
|
||||
alarms: UninitCell<Mutex<[AlarmState; ALARM_COUNT]>>,
|
||||
zero_instant: UninitCell<StdInstant>,
|
||||
inner: Mutex<Inner>,
|
||||
}
|
||||
|
||||
struct Inner {
|
||||
alarm: AlarmState,
|
||||
zero_instant: Option<StdInstant>,
|
||||
queue: Queue,
|
||||
closure: Option<Closure<dyn FnMut()>>,
|
||||
}
|
||||
|
||||
unsafe impl Send for Inner {}
|
||||
|
||||
embassy_time_driver::time_driver_impl!(static DRIVER: TimeDriver = TimeDriver {
|
||||
alarm_count: AtomicU8::new(0),
|
||||
once: Once::new(),
|
||||
alarms: UninitCell::uninit(),
|
||||
zero_instant: UninitCell::uninit(),
|
||||
inner: Mutex::new(Inner{
|
||||
zero_instant: None,
|
||||
queue: Queue::new(),
|
||||
alarm: AlarmState::new(),
|
||||
closure: None,
|
||||
}),
|
||||
});
|
||||
|
||||
impl TimeDriver {
|
||||
fn init(&self) {
|
||||
self.once.call_once(|| unsafe {
|
||||
self.alarms
|
||||
.write(Mutex::new([const { AlarmState::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
|
||||
impl Inner {
|
||||
fn init(&mut self) -> StdInstant {
|
||||
*self.zero_instant.get_or_insert_with(StdInstant::now)
|
||||
}
|
||||
|
||||
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 now(&mut self) -> u64 {
|
||||
StdInstant::now().duration_since(self.zero_instant.unwrap()).as_micros() as u64
|
||||
}
|
||||
|
||||
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) -> bool {
|
||||
self.init();
|
||||
let mut alarms = unsafe { self.alarms.as_ref() }.lock().unwrap();
|
||||
let alarm = &mut alarms[alarm.id() as usize];
|
||||
if let Some(token) = alarm.token {
|
||||
fn set_alarm(&mut self, timestamp: u64) -> bool {
|
||||
if let Some(token) = self.alarm.token {
|
||||
clearTimeout(token);
|
||||
}
|
||||
|
||||
@ -102,40 +62,42 @@ impl Driver for TimeDriver {
|
||||
false
|
||||
} else {
|
||||
let timeout = (timestamp - now) as u32;
|
||||
alarm.token = Some(setTimeout(alarm.closure.as_ref().unwrap(), timeout / 1000));
|
||||
let closure = self.closure.get_or_insert_with(|| Closure::new(dispatch));
|
||||
self.alarm.token = Some(setTimeout(closure, timeout / 1000));
|
||||
|
||||
true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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()
|
||||
impl Driver for TimeDriver {
|
||||
fn now(&self) -> u64 {
|
||||
let mut inner = self.inner.lock().unwrap();
|
||||
let zero = inner.init();
|
||||
StdInstant::now().duration_since(zero).as_micros() as u64
|
||||
}
|
||||
|
||||
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)
|
||||
fn schedule_wake(&self, at: u64, waker: &core::task::Waker) {
|
||||
let mut inner = self.inner.lock().unwrap();
|
||||
inner.init();
|
||||
if inner.queue.schedule_wake(at, waker) {
|
||||
let now = inner.now();
|
||||
let mut next = inner.queue.next_expiration(now);
|
||||
while !inner.set_alarm(next) {
|
||||
let now = inner.now();
|
||||
next = inner.queue.next_expiration(now);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Copy> UninitCell<T> {
|
||||
pub unsafe fn read(&self) -> T {
|
||||
ptr::read(self.as_mut_ptr())
|
||||
fn dispatch() {
|
||||
let inner = &mut *DRIVER.inner.lock().unwrap();
|
||||
|
||||
let now = inner.now();
|
||||
let mut next = inner.queue.next_expiration(now);
|
||||
while !inner.set_alarm(next) {
|
||||
let now = inner.now();
|
||||
next = inner.queue.next_expiration(now);
|
||||
}
|
||||
}
|
||||
|
@ -25,8 +25,6 @@ pub use driver_mock::MockDriver;
|
||||
mod driver_std;
|
||||
#[cfg(feature = "wasm")]
|
||||
mod driver_wasm;
|
||||
#[cfg(feature = "generic-queue")]
|
||||
mod queue_generic;
|
||||
|
||||
pub use delay::{block_for, Delay};
|
||||
pub use duration::Duration;
|
||||
|
@ -1,346 +0,0 @@
|
||||
use core::cell::RefCell;
|
||||
use core::cmp::{min, Ordering};
|
||||
use core::task::Waker;
|
||||
|
||||
use critical_section::Mutex;
|
||||
use embassy_time_driver::{allocate_alarm, set_alarm, set_alarm_callback, AlarmHandle};
|
||||
use embassy_time_queue_driver::TimerQueue;
|
||||
use heapless::Vec;
|
||||
|
||||
use crate::Instant;
|
||||
|
||||
#[cfg(feature = "generic-queue-8")]
|
||||
const QUEUE_SIZE: usize = 8;
|
||||
#[cfg(feature = "generic-queue-16")]
|
||||
const QUEUE_SIZE: usize = 16;
|
||||
#[cfg(feature = "generic-queue-32")]
|
||||
const QUEUE_SIZE: usize = 32;
|
||||
#[cfg(feature = "generic-queue-64")]
|
||||
const QUEUE_SIZE: usize = 64;
|
||||
#[cfg(feature = "generic-queue-128")]
|
||||
const QUEUE_SIZE: usize = 128;
|
||||
#[cfg(not(any(
|
||||
feature = "generic-queue-8",
|
||||
feature = "generic-queue-16",
|
||||
feature = "generic-queue-32",
|
||||
feature = "generic-queue-64",
|
||||
feature = "generic-queue-128"
|
||||
)))]
|
||||
const QUEUE_SIZE: usize = 64;
|
||||
|
||||
#[derive(Debug)]
|
||||
struct Timer {
|
||||
at: Instant,
|
||||
waker: Waker,
|
||||
}
|
||||
|
||||
impl PartialEq for Timer {
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
self.at == other.at
|
||||
}
|
||||
}
|
||||
|
||||
impl Eq for Timer {}
|
||||
|
||||
impl PartialOrd for Timer {
|
||||
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
|
||||
self.at.partial_cmp(&other.at)
|
||||
}
|
||||
}
|
||||
|
||||
impl Ord for Timer {
|
||||
fn cmp(&self, other: &Self) -> Ordering {
|
||||
self.at.cmp(&other.at)
|
||||
}
|
||||
}
|
||||
|
||||
struct InnerQueue {
|
||||
queue: Vec<Timer, QUEUE_SIZE>,
|
||||
alarm: AlarmHandle,
|
||||
}
|
||||
|
||||
impl InnerQueue {
|
||||
fn schedule_wake(&mut self, at: Instant, waker: &Waker) {
|
||||
self.queue
|
||||
.iter_mut()
|
||||
.find(|timer| timer.waker.will_wake(waker))
|
||||
.map(|timer| {
|
||||
timer.at = min(timer.at, at);
|
||||
})
|
||||
.unwrap_or_else(|| {
|
||||
let mut timer = Timer {
|
||||
waker: waker.clone(),
|
||||
at,
|
||||
};
|
||||
|
||||
loop {
|
||||
match self.queue.push(timer) {
|
||||
Ok(()) => break,
|
||||
Err(e) => timer = e,
|
||||
}
|
||||
|
||||
self.queue.pop().unwrap().waker.wake();
|
||||
}
|
||||
});
|
||||
|
||||
// Don't wait for the alarm callback to trigger and directly
|
||||
// dispatch all timers that are already due
|
||||
//
|
||||
// Then update the alarm if necessary
|
||||
self.dispatch();
|
||||
}
|
||||
|
||||
fn dispatch(&mut self) {
|
||||
loop {
|
||||
let now = Instant::now();
|
||||
|
||||
let mut next_alarm = Instant::MAX;
|
||||
|
||||
let mut i = 0;
|
||||
while i < self.queue.len() {
|
||||
let timer = &self.queue[i];
|
||||
if timer.at <= now {
|
||||
let timer = self.queue.swap_remove(i);
|
||||
timer.waker.wake();
|
||||
} else {
|
||||
next_alarm = min(next_alarm, timer.at);
|
||||
i += 1;
|
||||
}
|
||||
}
|
||||
|
||||
if self.update_alarm(next_alarm) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn update_alarm(&mut self, next_alarm: Instant) -> bool {
|
||||
if next_alarm == Instant::MAX {
|
||||
true
|
||||
} else {
|
||||
set_alarm(self.alarm, next_alarm.as_ticks())
|
||||
}
|
||||
}
|
||||
|
||||
fn handle_alarm(&mut self) {
|
||||
self.dispatch();
|
||||
}
|
||||
}
|
||||
|
||||
struct Queue {
|
||||
inner: Mutex<RefCell<Option<InnerQueue>>>,
|
||||
}
|
||||
|
||||
impl Queue {
|
||||
const fn new() -> Self {
|
||||
Self {
|
||||
inner: Mutex::new(RefCell::new(None)),
|
||||
}
|
||||
}
|
||||
|
||||
fn schedule_wake(&'static self, at: Instant, waker: &Waker) {
|
||||
critical_section::with(|cs| {
|
||||
let mut inner = self.inner.borrow_ref_mut(cs);
|
||||
|
||||
inner
|
||||
.get_or_insert_with(|| {
|
||||
let handle = unsafe { allocate_alarm() }.unwrap();
|
||||
set_alarm_callback(handle, Self::handle_alarm_callback, self as *const _ as _);
|
||||
InnerQueue {
|
||||
queue: Vec::new(),
|
||||
alarm: handle,
|
||||
}
|
||||
})
|
||||
.schedule_wake(at, waker)
|
||||
});
|
||||
}
|
||||
|
||||
fn handle_alarm(&self) {
|
||||
critical_section::with(|cs| self.inner.borrow_ref_mut(cs).as_mut().unwrap().handle_alarm())
|
||||
}
|
||||
|
||||
fn handle_alarm_callback(ctx: *mut ()) {
|
||||
unsafe { (ctx as *const Self).as_ref().unwrap() }.handle_alarm();
|
||||
}
|
||||
}
|
||||
|
||||
impl TimerQueue for Queue {
|
||||
fn schedule_wake(&'static self, at: u64, waker: &Waker) {
|
||||
Queue::schedule_wake(self, Instant::from_ticks(at), waker);
|
||||
}
|
||||
}
|
||||
|
||||
embassy_time_queue_driver::timer_queue_impl!(static QUEUE: Queue = Queue::new());
|
||||
|
||||
#[cfg(test)]
|
||||
#[cfg(feature = "mock-driver")]
|
||||
mod tests {
|
||||
use core::sync::atomic::{AtomicBool, Ordering};
|
||||
use core::task::Waker;
|
||||
use std::sync::Arc;
|
||||
use std::task::Wake;
|
||||
|
||||
use serial_test::serial;
|
||||
|
||||
use crate::driver_mock::MockDriver;
|
||||
use crate::queue_generic::QUEUE;
|
||||
use crate::{Duration, Instant};
|
||||
|
||||
struct TestWaker {
|
||||
pub awoken: AtomicBool,
|
||||
}
|
||||
|
||||
impl Wake for TestWaker {
|
||||
fn wake(self: Arc<Self>) {
|
||||
self.awoken.store(true, Ordering::Relaxed);
|
||||
}
|
||||
|
||||
fn wake_by_ref(self: &Arc<Self>) {
|
||||
self.awoken.store(true, Ordering::Relaxed);
|
||||
}
|
||||
}
|
||||
|
||||
fn test_waker() -> (Arc<TestWaker>, Waker) {
|
||||
let arc = Arc::new(TestWaker {
|
||||
awoken: AtomicBool::new(false),
|
||||
});
|
||||
let waker = Waker::from(arc.clone());
|
||||
|
||||
(arc, waker)
|
||||
}
|
||||
|
||||
fn setup() {
|
||||
MockDriver::get().reset();
|
||||
critical_section::with(|cs| *QUEUE.inner.borrow_ref_mut(cs) = None);
|
||||
}
|
||||
|
||||
fn queue_len() -> usize {
|
||||
critical_section::with(|cs| {
|
||||
QUEUE
|
||||
.inner
|
||||
.borrow_ref(cs)
|
||||
.as_ref()
|
||||
.map(|inner| inner.queue.iter().count())
|
||||
.unwrap_or(0)
|
||||
})
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[serial]
|
||||
fn test_schedule() {
|
||||
setup();
|
||||
|
||||
assert_eq!(queue_len(), 0);
|
||||
|
||||
let (flag, waker) = test_waker();
|
||||
|
||||
QUEUE.schedule_wake(Instant::from_secs(1), &waker);
|
||||
|
||||
assert!(!flag.awoken.load(Ordering::Relaxed));
|
||||
assert_eq!(queue_len(), 1);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[serial]
|
||||
fn test_schedule_same() {
|
||||
setup();
|
||||
|
||||
let (_flag, waker) = test_waker();
|
||||
|
||||
QUEUE.schedule_wake(Instant::from_secs(1), &waker);
|
||||
|
||||
assert_eq!(queue_len(), 1);
|
||||
|
||||
QUEUE.schedule_wake(Instant::from_secs(1), &waker);
|
||||
|
||||
assert_eq!(queue_len(), 1);
|
||||
|
||||
QUEUE.schedule_wake(Instant::from_secs(100), &waker);
|
||||
|
||||
assert_eq!(queue_len(), 1);
|
||||
|
||||
let (_flag2, waker2) = test_waker();
|
||||
|
||||
QUEUE.schedule_wake(Instant::from_secs(100), &waker2);
|
||||
|
||||
assert_eq!(queue_len(), 2);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[serial]
|
||||
fn test_trigger() {
|
||||
setup();
|
||||
|
||||
let (flag, waker) = test_waker();
|
||||
|
||||
QUEUE.schedule_wake(Instant::from_secs(100), &waker);
|
||||
|
||||
assert!(!flag.awoken.load(Ordering::Relaxed));
|
||||
|
||||
MockDriver::get().advance(Duration::from_secs(99));
|
||||
|
||||
assert!(!flag.awoken.load(Ordering::Relaxed));
|
||||
|
||||
assert_eq!(queue_len(), 1);
|
||||
|
||||
MockDriver::get().advance(Duration::from_secs(1));
|
||||
|
||||
assert!(flag.awoken.load(Ordering::Relaxed));
|
||||
|
||||
assert_eq!(queue_len(), 0);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[serial]
|
||||
fn test_immediate_trigger() {
|
||||
setup();
|
||||
|
||||
let (flag, waker) = test_waker();
|
||||
|
||||
QUEUE.schedule_wake(Instant::from_secs(100), &waker);
|
||||
|
||||
MockDriver::get().advance(Duration::from_secs(50));
|
||||
|
||||
let (flag2, waker2) = test_waker();
|
||||
|
||||
QUEUE.schedule_wake(Instant::from_secs(40), &waker2);
|
||||
|
||||
assert!(!flag.awoken.load(Ordering::Relaxed));
|
||||
assert!(flag2.awoken.load(Ordering::Relaxed));
|
||||
assert_eq!(queue_len(), 1);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[serial]
|
||||
fn test_queue_overflow() {
|
||||
setup();
|
||||
|
||||
for i in 1..super::QUEUE_SIZE {
|
||||
let (flag, waker) = test_waker();
|
||||
|
||||
QUEUE.schedule_wake(Instant::from_secs(310), &waker);
|
||||
|
||||
assert_eq!(queue_len(), i);
|
||||
assert!(!flag.awoken.load(Ordering::Relaxed));
|
||||
}
|
||||
|
||||
let (flag, waker) = test_waker();
|
||||
|
||||
QUEUE.schedule_wake(Instant::from_secs(300), &waker);
|
||||
|
||||
assert_eq!(queue_len(), super::QUEUE_SIZE);
|
||||
assert!(!flag.awoken.load(Ordering::Relaxed));
|
||||
|
||||
let (flag2, waker2) = test_waker();
|
||||
|
||||
QUEUE.schedule_wake(Instant::from_secs(305), &waker2);
|
||||
|
||||
assert_eq!(queue_len(), super::QUEUE_SIZE);
|
||||
assert!(flag.awoken.load(Ordering::Relaxed));
|
||||
|
||||
let (_flag3, waker3) = test_waker();
|
||||
QUEUE.schedule_wake(Instant::from_secs(320), &waker3);
|
||||
assert_eq!(queue_len(), super::QUEUE_SIZE);
|
||||
assert!(flag2.awoken.load(Ordering::Relaxed));
|
||||
}
|
||||
}
|
@ -6,7 +6,7 @@ license = "MIT OR Apache-2.0"
|
||||
|
||||
[dependencies]
|
||||
embassy-sync = { version = "0.6.1", path = "../../../../embassy-sync" }
|
||||
embassy-executor = { version = "0.6.3", path = "../../../../embassy-executor", features = ["task-arena-size-16384", "arch-cortex-m", "executor-thread", "integrated-timers", "arch-cortex-m", "executor-thread"] }
|
||||
embassy-executor = { version = "0.6.3", path = "../../../../embassy-executor", features = ["task-arena-size-16384", "arch-cortex-m", "executor-thread", "arch-cortex-m", "executor-thread"] }
|
||||
embassy-time = { version = "0.3.2", path = "../../../../embassy-time", features = [] }
|
||||
embassy-nrf = { version = "0.2.0", path = "../../../../embassy-nrf", features = ["time-driver-rtc1", "gpiote", ] }
|
||||
embassy-boot = { version = "0.3.0", path = "../../../../embassy-boot", features = [] }
|
||||
|
@ -6,7 +6,7 @@ license = "MIT OR Apache-2.0"
|
||||
|
||||
[dependencies]
|
||||
embassy-sync = { version = "0.6.1", path = "../../../../embassy-sync" }
|
||||
embassy-executor = { version = "0.6.3", path = "../../../../embassy-executor", features = ["task-arena-size-16384", "arch-cortex-m", "executor-thread", "integrated-timers", "arch-cortex-m", "executor-thread"] }
|
||||
embassy-executor = { version = "0.6.3", path = "../../../../embassy-executor", features = ["task-arena-size-16384", "arch-cortex-m", "executor-thread", "arch-cortex-m", "executor-thread"] }
|
||||
embassy-time = { version = "0.3.2", path = "../../../../embassy-time", features = [] }
|
||||
embassy-rp = { version = "0.2.0", path = "../../../../embassy-rp", features = ["time-driver", "rp2040"] }
|
||||
embassy-boot-rp = { version = "0.3.0", path = "../../../../embassy-boot-rp", features = [] }
|
||||
|
@ -6,7 +6,7 @@ license = "MIT OR Apache-2.0"
|
||||
|
||||
[dependencies]
|
||||
embassy-sync = { version = "0.6.1", path = "../../../../embassy-sync" }
|
||||
embassy-executor = { version = "0.6.3", path = "../../../../embassy-executor", features = ["task-arena-size-8192", "arch-cortex-m", "executor-thread", "integrated-timers"] }
|
||||
embassy-executor = { version = "0.6.3", path = "../../../../embassy-executor", features = ["task-arena-size-8192", "arch-cortex-m", "executor-thread"] }
|
||||
embassy-time = { version = "0.3.2", path = "../../../../embassy-time", features = [ "tick-hz-32_768"] }
|
||||
embassy-stm32 = { version = "0.1.0", path = "../../../../embassy-stm32", features = ["stm32f303re", "time-driver-any", "exti"] }
|
||||
embassy-boot-stm32 = { version = "0.2.0", path = "../../../../embassy-boot-stm32" }
|
||||
|
@ -6,7 +6,7 @@ license = "MIT OR Apache-2.0"
|
||||
|
||||
[dependencies]
|
||||
embassy-sync = { version = "0.6.1", path = "../../../../embassy-sync" }
|
||||
embassy-executor = { version = "0.6.3", path = "../../../../embassy-executor", features = ["task-arena-size-8192", "arch-cortex-m", "executor-thread", "integrated-timers"] }
|
||||
embassy-executor = { version = "0.6.3", path = "../../../../embassy-executor", features = ["task-arena-size-8192", "arch-cortex-m", "executor-thread"] }
|
||||
embassy-time = { version = "0.3.2", path = "../../../../embassy-time", features = [ "tick-hz-32_768"] }
|
||||
embassy-stm32 = { version = "0.1.0", path = "../../../../embassy-stm32", features = ["stm32f767zi", "time-driver-any", "exti"] }
|
||||
embassy-boot-stm32 = { version = "0.2.0", path = "../../../../embassy-boot-stm32", features = [] }
|
||||
|
@ -6,7 +6,7 @@ license = "MIT OR Apache-2.0"
|
||||
|
||||
[dependencies]
|
||||
embassy-sync = { version = "0.6.1", path = "../../../../embassy-sync" }
|
||||
embassy-executor = { version = "0.6.3", path = "../../../../embassy-executor", features = ["task-arena-size-8192", "arch-cortex-m", "executor-thread", "integrated-timers"] }
|
||||
embassy-executor = { version = "0.6.3", path = "../../../../embassy-executor", features = ["task-arena-size-8192", "arch-cortex-m", "executor-thread"] }
|
||||
embassy-time = { version = "0.3.2", path = "../../../../embassy-time", features = [ "tick-hz-32_768"] }
|
||||
embassy-stm32 = { version = "0.1.0", path = "../../../../embassy-stm32", features = ["stm32h743zi", "time-driver-any", "exti"] }
|
||||
embassy-boot-stm32 = { version = "0.2.0", path = "../../../../embassy-boot-stm32", features = [] }
|
||||
|
@ -6,7 +6,7 @@ license = "MIT OR Apache-2.0"
|
||||
|
||||
[dependencies]
|
||||
embassy-sync = { version = "0.6.1", path = "../../../../embassy-sync" }
|
||||
embassy-executor = { version = "0.6.3", path = "../../../../embassy-executor", features = ["task-arena-size-8192", "arch-cortex-m", "executor-thread", "integrated-timers"] }
|
||||
embassy-executor = { version = "0.6.3", path = "../../../../embassy-executor", features = ["task-arena-size-8192", "arch-cortex-m", "executor-thread"] }
|
||||
embassy-time = { version = "0.3.2", path = "../../../../embassy-time", features = [ "tick-hz-32_768"] }
|
||||
embassy-stm32 = { version = "0.1.0", path = "../../../../embassy-stm32", features = ["stm32l072cz", "time-driver-any", "exti", "memory-x"] }
|
||||
embassy-boot-stm32 = { version = "0.2.0", path = "../../../../embassy-boot-stm32", features = [] }
|
||||
|
@ -6,7 +6,7 @@ license = "MIT OR Apache-2.0"
|
||||
|
||||
[dependencies]
|
||||
embassy-sync = { version = "0.6.1", path = "../../../../embassy-sync" }
|
||||
embassy-executor = { version = "0.6.3", path = "../../../../embassy-executor", features = ["task-arena-size-8192", "arch-cortex-m", "executor-thread", "integrated-timers"] }
|
||||
embassy-executor = { version = "0.6.3", path = "../../../../embassy-executor", features = ["task-arena-size-8192", "arch-cortex-m", "executor-thread"] }
|
||||
embassy-time = { version = "0.3.2", path = "../../../../embassy-time", features = [ "tick-hz-32_768"] }
|
||||
embassy-stm32 = { version = "0.1.0", path = "../../../../embassy-stm32", features = ["stm32l151cb-a", "time-driver-any", "exti"] }
|
||||
embassy-boot-stm32 = { version = "0.2.0", path = "../../../../embassy-boot-stm32", features = [] }
|
||||
|
@ -6,7 +6,7 @@ license = "MIT OR Apache-2.0"
|
||||
|
||||
[dependencies]
|
||||
embassy-sync = { version = "0.6.1", path = "../../../../embassy-sync" }
|
||||
embassy-executor = { version = "0.6.3", path = "../../../../embassy-executor", features = ["task-arena-size-8192", "arch-cortex-m", "executor-thread", "integrated-timers"] }
|
||||
embassy-executor = { version = "0.6.3", path = "../../../../embassy-executor", features = ["task-arena-size-8192", "arch-cortex-m", "executor-thread"] }
|
||||
embassy-time = { version = "0.3.2", path = "../../../../embassy-time", features = [ "tick-hz-32_768"] }
|
||||
embassy-stm32 = { version = "0.1.0", path = "../../../../embassy-stm32", features = ["stm32l475vg", "time-driver-any", "exti"] }
|
||||
embassy-boot-stm32 = { version = "0.2.0", path = "../../../../embassy-boot-stm32", features = [] }
|
||||
|
@ -6,7 +6,7 @@ license = "MIT OR Apache-2.0"
|
||||
|
||||
[dependencies]
|
||||
embassy-sync = { version = "0.6.1", path = "../../../../embassy-sync" }
|
||||
embassy-executor = { version = "0.6.3", path = "../../../../embassy-executor", features = ["task-arena-size-8192", "arch-cortex-m", "executor-thread", "integrated-timers"] }
|
||||
embassy-executor = { version = "0.6.3", path = "../../../../embassy-executor", features = ["task-arena-size-8192", "arch-cortex-m", "executor-thread"] }
|
||||
embassy-time = { version = "0.3.2", path = "../../../../embassy-time", features = [ "tick-hz-32_768"] }
|
||||
embassy-stm32 = { version = "0.1.0", path = "../../../../embassy-stm32", features = ["stm32wb55rg", "time-driver-any", "exti"] }
|
||||
embassy-boot-stm32 = { version = "0.2.0", path = "../../../../embassy-boot-stm32", features = [] }
|
||||
|
@ -6,7 +6,7 @@ license = "MIT OR Apache-2.0"
|
||||
|
||||
[dependencies]
|
||||
embassy-sync = { version = "0.6.1", path = "../../../../embassy-sync" }
|
||||
embassy-executor = { version = "0.6.3", path = "../../../../embassy-executor", features = ["task-arena-size-8192", "arch-cortex-m", "executor-thread", "integrated-timers"] }
|
||||
embassy-executor = { version = "0.6.3", path = "../../../../embassy-executor", features = ["task-arena-size-8192", "arch-cortex-m", "executor-thread"] }
|
||||
embassy-time = { version = "0.3.2", path = "../../../../embassy-time", features = [ "tick-hz-32_768"] }
|
||||
embassy-stm32 = { version = "0.1.0", path = "../../../../embassy-stm32", features = ["stm32wl55jc-cm4", "time-driver-any", "exti"] }
|
||||
embassy-boot-stm32 = { version = "0.2.0", path = "../../../../embassy-boot-stm32", features = [] }
|
||||
|
@ -16,7 +16,7 @@ log = [
|
||||
|
||||
[dependencies]
|
||||
embassy-sync = { version = "0.6.1", path = "../../embassy-sync" }
|
||||
embassy-executor = { version = "0.6.3", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "rtos-trace", "integrated-timers"] }
|
||||
embassy-executor = { version = "0.6.3", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "rtos-trace"] }
|
||||
embassy-time = { version = "0.3.2", path = "../../embassy-time" }
|
||||
embassy-nrf = { version = "0.2.0", path = "../../embassy-nrf", features = ["nrf52840", "time-driver-rtc1", "gpiote", "unstable-pac"] }
|
||||
|
||||
|
@ -5,7 +5,7 @@ version = "0.1.0"
|
||||
license = "MIT OR Apache-2.0"
|
||||
|
||||
[dependencies]
|
||||
embassy-executor = { version = "0.6.3", path = "../../embassy-executor", features = ["task-arena-size-4096", "arch-cortex-m", "executor-thread", "executor-interrupt", "defmt", "integrated-timers"] }
|
||||
embassy-executor = { version = "0.6.3", path = "../../embassy-executor", features = ["task-arena-size-4096", "arch-cortex-m", "executor-thread", "executor-interrupt", "defmt"] }
|
||||
embassy-time = { version = "0.3.2", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime"] }
|
||||
embassy-nrf = { version = "0.2.0", path = "../../embassy-nrf", features = ["defmt", "nrf51", "gpiote", "time-driver-rtc1", "unstable-pac", "time", "rt"] }
|
||||
|
||||
|
@ -7,7 +7,7 @@ license = "MIT OR Apache-2.0"
|
||||
[dependencies]
|
||||
embassy-futures = { version = "0.1.0", path = "../../embassy-futures" }
|
||||
embassy-sync = { version = "0.6.1", path = "../../embassy-sync", features = ["defmt"] }
|
||||
embassy-executor = { version = "0.6.3", path = "../../embassy-executor", features = ["task-arena-size-8192", "arch-cortex-m", "executor-thread", "executor-interrupt", "defmt", "integrated-timers"] }
|
||||
embassy-executor = { version = "0.6.3", path = "../../embassy-executor", features = ["task-arena-size-8192", "arch-cortex-m", "executor-thread", "executor-interrupt", "defmt"] }
|
||||
embassy-time = { version = "0.3.2", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime"] }
|
||||
embassy-nrf = { version = "0.2.0", path = "../../embassy-nrf", features = ["defmt", "nrf52810", "time-driver-rtc1", "gpiote", "unstable-pac", "time"] }
|
||||
|
||||
|
@ -9,7 +9,8 @@ rtic = { version = "2", features = ["thumbv7-backend"] }
|
||||
|
||||
embassy-futures = { version = "0.1.0", path = "../../embassy-futures" }
|
||||
embassy-sync = { version = "0.6.1", path = "../../embassy-sync", features = ["defmt"] }
|
||||
embassy-time = { version = "0.3.2", path = "../../embassy-time", features = [ "defmt", "defmt-timestamp-uptime", "generic-queue"] }
|
||||
embassy-time = { version = "0.3.2", path = "../../embassy-time", features = [ "defmt", "defmt-timestamp-uptime"] }
|
||||
embassy-time-queue-driver = { version = "0.1.0", path = "../../embassy-time-queue-driver", features = ["generic-queue-8"] }
|
||||
embassy-nrf = { version = "0.2.0", path = "../../embassy-nrf", features = [ "defmt", "nrf52840", "time-driver-rtc1", "gpiote", "unstable-pac", "time"] }
|
||||
|
||||
defmt = "0.3"
|
||||
|
@ -7,7 +7,7 @@ license = "MIT OR Apache-2.0"
|
||||
[dependencies]
|
||||
embassy-futures = { version = "0.1.0", path = "../../embassy-futures" }
|
||||
embassy-sync = { version = "0.6.1", path = "../../embassy-sync", features = ["defmt"] }
|
||||
embassy-executor = { version = "0.6.3", path = "../../embassy-executor", features = ["task-arena-size-32768", "arch-cortex-m", "executor-thread", "executor-interrupt", "defmt", "integrated-timers"] }
|
||||
embassy-executor = { version = "0.6.3", path = "../../embassy-executor", features = ["task-arena-size-32768", "arch-cortex-m", "executor-thread", "executor-interrupt", "defmt"] }
|
||||
embassy-time = { version = "0.3.2", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime"] }
|
||||
embassy-nrf = { version = "0.2.0", path = "../../embassy-nrf", features = ["defmt", "nrf52840", "time-driver-rtc1", "gpiote", "unstable-pac", "time"] }
|
||||
embassy-net = { version = "0.5.0", path = "../../embassy-net", features = ["defmt", "tcp", "dhcpv4", "medium-ethernet"] }
|
||||
|
@ -7,7 +7,7 @@ license = "MIT OR Apache-2.0"
|
||||
[dependencies]
|
||||
embassy-futures = { version = "0.1.0", path = "../../embassy-futures" }
|
||||
embassy-sync = { version = "0.6.1", path = "../../embassy-sync", features = ["defmt"] }
|
||||
embassy-executor = { version = "0.6.3", path = "../../embassy-executor", features = ["task-arena-size-32768", "arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] }
|
||||
embassy-executor = { version = "0.6.3", path = "../../embassy-executor", features = ["task-arena-size-32768", "arch-cortex-m", "executor-thread", "defmt"] }
|
||||
embassy-time = { version = "0.3.2", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime"] }
|
||||
embassy-nrf = { version = "0.2.0", path = "../../embassy-nrf", features = ["defmt", "nrf5340-app-s", "time-driver-rtc1", "gpiote", "unstable-pac"] }
|
||||
embassy-net = { version = "0.5.0", path = "../../embassy-net", features = ["defmt", "tcp", "dhcpv4", "medium-ethernet"] }
|
||||
|
@ -5,7 +5,7 @@ version = "0.1.0"
|
||||
license = "MIT OR Apache-2.0"
|
||||
|
||||
[dependencies]
|
||||
embassy-executor = { version = "0.6.3", path = "../../embassy-executor", features = ["task-arena-size-32768", "arch-cortex-m", "executor-thread", "executor-interrupt", "defmt", "integrated-timers"] }
|
||||
embassy-executor = { version = "0.6.3", path = "../../embassy-executor", features = ["task-arena-size-32768", "arch-cortex-m", "executor-thread", "executor-interrupt", "defmt"] }
|
||||
embassy-time = { version = "0.3.2", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime"] }
|
||||
embassy-nrf = { version = "0.2.0", path = "../../embassy-nrf", features = ["defmt", "nrf54l15-app-s", "time-driver-rtc1", "gpiote", "unstable-pac", "time"] }
|
||||
|
||||
|
@ -5,7 +5,7 @@ version = "0.1.0"
|
||||
license = "MIT OR Apache-2.0"
|
||||
|
||||
[dependencies]
|
||||
embassy-executor = { version = "0.6.3", path = "../../../embassy-executor", features = ["task-arena-size-32768", "arch-cortex-m", "executor-thread", "executor-interrupt", "defmt", "integrated-timers"] }
|
||||
embassy-executor = { version = "0.6.3", path = "../../../embassy-executor", features = ["task-arena-size-32768", "arch-cortex-m", "executor-thread", "executor-interrupt", "defmt"] }
|
||||
embassy-time = { version = "0.3.2", path = "../../../embassy-time", features = ["defmt", "defmt-timestamp-uptime"] }
|
||||
embassy-nrf = { version = "0.2.0", path = "../../../embassy-nrf", features = ["defmt", "nrf9120-ns", "time-driver-rtc1", "gpiote", "unstable-pac", "time"] }
|
||||
|
||||
|
@ -5,7 +5,7 @@ version = "0.1.0"
|
||||
license = "MIT OR Apache-2.0"
|
||||
|
||||
[dependencies]
|
||||
embassy-executor = { version = "0.6.3", path = "../../../embassy-executor", features = ["task-arena-size-32768", "arch-cortex-m", "executor-thread", "executor-interrupt", "defmt", "integrated-timers"] }
|
||||
embassy-executor = { version = "0.6.3", path = "../../../embassy-executor", features = ["task-arena-size-32768", "arch-cortex-m", "executor-thread", "executor-interrupt", "defmt"] }
|
||||
embassy-time = { version = "0.3.2", path = "../../../embassy-time", features = ["defmt", "defmt-timestamp-uptime"] }
|
||||
embassy-nrf = { version = "0.2.0", path = "../../../embassy-nrf", features = ["defmt", "nrf9120-s", "time-driver-rtc1", "gpiote", "unstable-pac", "time"] }
|
||||
|
||||
|
@ -5,7 +5,7 @@ version = "0.1.0"
|
||||
license = "MIT OR Apache-2.0"
|
||||
|
||||
[dependencies]
|
||||
embassy-executor = { version = "0.6.3", path = "../../embassy-executor", features = ["task-arena-size-32768", "arch-cortex-m", "executor-thread", "executor-interrupt", "defmt", "integrated-timers"] }
|
||||
embassy-executor = { version = "0.6.3", path = "../../embassy-executor", features = ["task-arena-size-32768", "arch-cortex-m", "executor-thread", "executor-interrupt", "defmt"] }
|
||||
embassy-time = { version = "0.3.2", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime"] }
|
||||
embassy-nrf = { version = "0.2.0", path = "../../embassy-nrf", features = ["defmt", "nrf9160-s", "time-driver-rtc1", "gpiote", "unstable-pac", "time"] }
|
||||
embassy-net-nrf91 = { version = "0.1.0", path = "../../embassy-net-nrf91", features = ["defmt"] }
|
||||
|
@ -8,7 +8,7 @@ license = "MIT OR Apache-2.0"
|
||||
[dependencies]
|
||||
embassy-embedded-hal = { version = "0.2.0", path = "../../embassy-embedded-hal", features = ["defmt"] }
|
||||
embassy-sync = { version = "0.6.1", path = "../../embassy-sync", features = ["defmt"] }
|
||||
embassy-executor = { version = "0.6.3", path = "../../embassy-executor", features = ["task-arena-size-98304", "arch-cortex-m", "executor-thread", "executor-interrupt", "defmt", "integrated-timers"] }
|
||||
embassy-executor = { version = "0.6.3", path = "../../embassy-executor", features = ["task-arena-size-98304", "arch-cortex-m", "executor-thread", "executor-interrupt", "defmt"] }
|
||||
embassy-time = { version = "0.3.2", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime"] }
|
||||
embassy-rp = { version = "0.2.0", path = "../../embassy-rp", features = ["defmt", "unstable-pac", "time-driver", "critical-section-impl", "rp2040"] }
|
||||
embassy-usb = { version = "0.3.0", path = "../../embassy-usb", features = ["defmt"] }
|
||||
|
@ -8,7 +8,7 @@ license = "MIT OR Apache-2.0"
|
||||
[dependencies]
|
||||
embassy-embedded-hal = { version = "0.2.0", path = "../../embassy-embedded-hal", features = ["defmt"] }
|
||||
embassy-sync = { version = "0.6.1", path = "../../embassy-sync", features = ["defmt"] }
|
||||
embassy-executor = { version = "0.6.3", path = "../../embassy-executor", features = ["task-arena-size-98304", "arch-cortex-m", "executor-thread", "executor-interrupt", "defmt", "integrated-timers"] }
|
||||
embassy-executor = { version = "0.6.3", path = "../../embassy-executor", features = ["task-arena-size-98304", "arch-cortex-m", "executor-thread", "executor-interrupt", "defmt"] }
|
||||
embassy-time = { version = "0.3.2", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime"] }
|
||||
embassy-rp = { version = "0.2.0", path = "../../embassy-rp", features = ["defmt", "unstable-pac", "time-driver", "critical-section-impl", "rp235xa", "binary-info"] }
|
||||
embassy-usb = { version = "0.3.0", path = "../../embassy-usb", features = ["defmt"] }
|
||||
|
@ -6,7 +6,7 @@ license = "MIT OR Apache-2.0"
|
||||
|
||||
[dependencies]
|
||||
embassy-sync = { version = "0.6.1", path = "../../embassy-sync", features = ["log"] }
|
||||
embassy-executor = { version = "0.6.3", path = "../../embassy-executor", features = ["task-arena-size-32768", "arch-std", "executor-thread", "log", "integrated-timers"] }
|
||||
embassy-executor = { version = "0.6.3", path = "../../embassy-executor", features = ["task-arena-size-32768", "arch-std", "executor-thread", "log"] }
|
||||
embassy-time = { version = "0.3.2", path = "../../embassy-time", features = ["log", "std", ] }
|
||||
embassy-net = { version = "0.5.0", path = "../../embassy-net", features=[ "std", "log", "medium-ethernet", "medium-ip", "tcp", "udp", "dns", "dhcpv4", "proto-ipv6"] }
|
||||
embassy-net-tuntap = { version = "0.1.0", path = "../../embassy-net-tuntap" }
|
||||
|
@ -8,7 +8,7 @@ license = "MIT OR Apache-2.0"
|
||||
# Change stm32c031c6 to your chip name, if necessary.
|
||||
embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = [ "defmt", "time-driver-any", "stm32c031c6", "memory-x", "unstable-pac", "exti"] }
|
||||
embassy-sync = { version = "0.6.1", path = "../../embassy-sync", features = ["defmt"] }
|
||||
embassy-executor = { version = "0.6.3", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] }
|
||||
embassy-executor = { version = "0.6.3", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "defmt"] }
|
||||
embassy-time = { version = "0.3.2", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] }
|
||||
|
||||
defmt = "0.3"
|
||||
|
@ -13,7 +13,7 @@ defmt = "0.3"
|
||||
defmt-rtt = "0.4"
|
||||
panic-probe = { version = "0.3", features = ["print-defmt"] }
|
||||
embassy-sync = { version = "0.6.1", path = "../../embassy-sync", features = ["defmt"] }
|
||||
embassy-executor = { version = "0.6.3", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "executor-interrupt", "defmt", "integrated-timers"] }
|
||||
embassy-executor = { version = "0.6.3", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "executor-interrupt", "defmt"] }
|
||||
embassy-time = { version = "0.3.2", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] }
|
||||
static_cell = "2"
|
||||
portable-atomic = { version = "1.5", features = ["unsafe-assume-single-core"] }
|
||||
|
@ -8,7 +8,7 @@ license = "MIT OR Apache-2.0"
|
||||
# Change stm32f103c8 to your chip name, if necessary.
|
||||
embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = [ "defmt", "stm32f103c8", "unstable-pac", "memory-x", "time-driver-any" ] }
|
||||
embassy-sync = { version = "0.6.1", path = "../../embassy-sync", features = ["defmt"] }
|
||||
embassy-executor = { version = "0.6.3", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] }
|
||||
embassy-executor = { version = "0.6.3", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "defmt"] }
|
||||
embassy-time = { version = "0.3.2", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] }
|
||||
embassy-usb = { version = "0.3.0", path = "../../embassy-usb", features = ["defmt"] }
|
||||
embassy-futures = { version = "0.1.0", path = "../../embassy-futures" }
|
||||
|
@ -8,7 +8,7 @@ license = "MIT OR Apache-2.0"
|
||||
# Change stm32f207zg to your chip name, if necessary.
|
||||
embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = [ "defmt", "stm32f207zg", "unstable-pac", "memory-x", "time-driver-any", "exti"] }
|
||||
embassy-sync = { version = "0.6.1", path = "../../embassy-sync", features = ["defmt"] }
|
||||
embassy-executor = { version = "0.6.3", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] }
|
||||
embassy-executor = { version = "0.6.3", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "defmt"] }
|
||||
embassy-time = { version = "0.3.2", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] }
|
||||
|
||||
defmt = "0.3"
|
||||
|
@ -8,7 +8,7 @@ license = "MIT OR Apache-2.0"
|
||||
# Change stm32f303ze to your chip name, if necessary.
|
||||
embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = [ "defmt", "stm32f303ze", "unstable-pac", "memory-x", "time-driver-tim2", "exti"] }
|
||||
embassy-sync = { version = "0.6.1", path = "../../embassy-sync", features = ["defmt"] }
|
||||
embassy-executor = { version = "0.6.3", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "executor-interrupt", "defmt", "integrated-timers"] }
|
||||
embassy-executor = { version = "0.6.3", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "executor-interrupt", "defmt"] }
|
||||
embassy-time = { version = "0.3.2", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] }
|
||||
embassy-usb = { version = "0.3.0", path = "../../embassy-usb", features = ["defmt"] }
|
||||
embassy-futures = { version = "0.1.0", path = "../../embassy-futures" }
|
||||
|
@ -6,7 +6,7 @@ license = "MIT OR Apache-2.0"
|
||||
|
||||
[dependencies]
|
||||
embassy-sync = { version = "0.6.1", path = "../../embassy-sync", features = ["defmt"] }
|
||||
embassy-executor = { version = "0.6.3", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "executor-interrupt", "defmt", "integrated-timers"] }
|
||||
embassy-executor = { version = "0.6.3", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "executor-interrupt", "defmt"] }
|
||||
embassy-time = { version = "0.3.2", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] }
|
||||
embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = [ "defmt", "stm32f334r8", "unstable-pac", "memory-x", "time-driver-any", "exti"] }
|
||||
embassy-usb = { version = "0.3.0", path = "../../embassy-usb", features = ["defmt"] }
|
||||
|
@ -8,7 +8,7 @@ license = "MIT OR Apache-2.0"
|
||||
# Change stm32f429zi to your chip name, if necessary.
|
||||
embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["defmt", "stm32f429zi", "unstable-pac", "memory-x", "time-driver-tim4", "exti", "chrono"] }
|
||||
embassy-sync = { version = "0.6.1", path = "../../embassy-sync", features = ["defmt"] }
|
||||
embassy-executor = { version = "0.6.3", path = "../../embassy-executor", features = ["task-arena-size-32768", "arch-cortex-m", "executor-thread", "executor-interrupt", "defmt", "integrated-timers"] }
|
||||
embassy-executor = { version = "0.6.3", path = "../../embassy-executor", features = ["task-arena-size-32768", "arch-cortex-m", "executor-thread", "executor-interrupt", "defmt"] }
|
||||
embassy-time = { version = "0.3.2", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] }
|
||||
embassy-usb = { version = "0.3.0", path = "../../embassy-usb", features = ["defmt" ] }
|
||||
embassy-net = { version = "0.5.0", path = "../../embassy-net", features = ["defmt", "tcp", "dhcpv4", "medium-ethernet", ] }
|
||||
|
@ -7,7 +7,7 @@ license = "MIT OR Apache-2.0"
|
||||
[dependencies]
|
||||
# Specific examples only for stm32f469
|
||||
embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["defmt", "stm32f469ni", "unstable-pac", "memory-x", "time-driver-any", "exti", "chrono"] }
|
||||
embassy-executor = { version = "0.6.3", path = "../../embassy-executor", features = ["task-arena-size-32768", "arch-cortex-m", "executor-thread", "executor-interrupt", "defmt", "integrated-timers"] }
|
||||
embassy-executor = { version = "0.6.3", path = "../../embassy-executor", features = ["task-arena-size-32768", "arch-cortex-m", "executor-thread", "executor-interrupt", "defmt"] }
|
||||
embassy-time = { version = "0.3.2", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] }
|
||||
|
||||
defmt = "0.3"
|
||||
|
@ -8,7 +8,7 @@ license = "MIT OR Apache-2.0"
|
||||
# Change stm32f777zi to your chip name, if necessary.
|
||||
embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["defmt", "stm32f777zi", "memory-x", "unstable-pac", "time-driver-any", "exti"] }
|
||||
embassy-sync = { version = "0.6.1", path = "../../embassy-sync", features = ["defmt"] }
|
||||
embassy-executor = { version = "0.6.3", path = "../../embassy-executor", features = ["task-arena-size-32768", "arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] }
|
||||
embassy-executor = { version = "0.6.3", path = "../../embassy-executor", features = ["task-arena-size-32768", "arch-cortex-m", "executor-thread", "defmt"] }
|
||||
embassy-time = { version = "0.3.2", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] }
|
||||
embassy-net = { version = "0.5.0", path = "../../embassy-net", features = ["defmt", "tcp", "dhcpv4", "medium-ethernet"] }
|
||||
embedded-io-async = { version = "0.6.1" }
|
||||
|
@ -8,7 +8,7 @@ license = "MIT OR Apache-2.0"
|
||||
# Change stm32g0b1re to your chip name, if necessary.
|
||||
embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = [ "defmt", "time-driver-any", "stm32g0b1re", "memory-x", "unstable-pac", "exti"] }
|
||||
embassy-sync = { version = "0.6.1", path = "../../embassy-sync", features = ["defmt"] }
|
||||
embassy-executor = { version = "0.6.3", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] }
|
||||
embassy-executor = { version = "0.6.3", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "defmt"] }
|
||||
embassy-time = { version = "0.3.2", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] }
|
||||
embassy-usb = { version = "0.3.0", path = "../../embassy-usb", default-features = false, features = ["defmt"] }
|
||||
embassy-futures = { version = "0.1.0", path = "../../embassy-futures" }
|
||||
|
@ -8,7 +8,7 @@ license = "MIT OR Apache-2.0"
|
||||
# Change stm32g491re to your chip name, if necessary.
|
||||
embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = [ "defmt", "time-driver-any", "stm32g491re", "memory-x", "unstable-pac", "exti"] }
|
||||
embassy-sync = { version = "0.6.1", path = "../../embassy-sync", features = ["defmt"] }
|
||||
embassy-executor = { version = "0.6.3", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] }
|
||||
embassy-executor = { version = "0.6.3", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "defmt"] }
|
||||
embassy-time = { version = "0.3.2", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] }
|
||||
embassy-usb = { version = "0.3.0", path = "../../embassy-usb", features = ["defmt"] }
|
||||
embassy-futures = { version = "0.1.0", path = "../../embassy-futures" }
|
||||
|
@ -8,7 +8,7 @@ license = "MIT OR Apache-2.0"
|
||||
# Change stm32h563zi to your chip name, if necessary.
|
||||
embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["defmt", "stm32h563zi", "memory-x", "time-driver-any", "exti", "unstable-pac", "low-power"] }
|
||||
embassy-sync = { version = "0.6.1", path = "../../embassy-sync", features = ["defmt"] }
|
||||
embassy-executor = { version = "0.6.3", path = "../../embassy-executor", features = ["task-arena-size-32768", "arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] }
|
||||
embassy-executor = { version = "0.6.3", path = "../../embassy-executor", features = ["task-arena-size-32768", "arch-cortex-m", "executor-thread", "defmt"] }
|
||||
embassy-time = { version = "0.3.2", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] }
|
||||
embassy-net = { version = "0.5.0", path = "../../embassy-net", features = ["defmt", "tcp", "dhcpv4", "medium-ethernet", "proto-ipv6"] }
|
||||
embassy-usb = { version = "0.3.0", path = "../../embassy-usb", features = ["defmt"] }
|
||||
|
@ -9,7 +9,7 @@ license = "MIT OR Apache-2.0"
|
||||
embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["defmt", "stm32h743bi", "time-driver-tim2", "exti", "memory-x", "unstable-pac", "chrono"] }
|
||||
embassy-sync = { version = "0.6.1", path = "../../embassy-sync", features = ["defmt"] }
|
||||
embassy-embedded-hal = { version = "0.2.0", path = "../../embassy-embedded-hal" }
|
||||
embassy-executor = { version = "0.6.3", path = "../../embassy-executor", features = ["task-arena-size-32768", "arch-cortex-m", "executor-thread", "executor-interrupt", "defmt", "integrated-timers"] }
|
||||
embassy-executor = { version = "0.6.3", path = "../../embassy-executor", features = ["task-arena-size-32768", "arch-cortex-m", "executor-thread", "executor-interrupt", "defmt"] }
|
||||
embassy-time = { version = "0.3.2", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] }
|
||||
embassy-net = { version = "0.5.0", path = "../../embassy-net", features = ["defmt", "tcp", "dhcpv4", "medium-ethernet", "proto-ipv6", "dns"] }
|
||||
embassy-usb = { version = "0.3.0", path = "../../embassy-usb", features = ["defmt"] }
|
||||
|
@ -8,7 +8,7 @@ license = "MIT OR Apache-2.0"
|
||||
# Change stm32h723zg to your chip name, if necessary.
|
||||
embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["defmt", "stm32h723zg", "time-driver-tim2", "exti", "memory-x", "unstable-pac", "chrono"] }
|
||||
embassy-sync = { version = "0.6.1", path = "../../embassy-sync", features = ["defmt"] }
|
||||
embassy-executor = { version = "0.6.2", path = "../../embassy-executor", features = ["task-arena-size-32768", "arch-cortex-m", "executor-thread", "executor-interrupt", "defmt", "integrated-timers"] }
|
||||
embassy-executor = { version = "0.6.2", path = "../../embassy-executor", features = ["task-arena-size-32768", "arch-cortex-m", "executor-thread", "executor-interrupt", "defmt"] }
|
||||
embassy-time = { version = "0.3.2", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] }
|
||||
embassy-futures = { version = "0.1.0", path = "../../embassy-futures" }
|
||||
|
||||
|
@ -8,7 +8,7 @@ license = "MIT OR Apache-2.0"
|
||||
embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["defmt", "stm32h735ig", "time-driver-tim2", "exti", "memory-x", "unstable-pac", "chrono"] }
|
||||
embassy-sync = { version = "0.6.1", path = "../../embassy-sync", features = ["defmt"] }
|
||||
embassy-embedded-hal = { version = "0.2.0", path = "../../embassy-embedded-hal" }
|
||||
embassy-executor = { version = "0.6.3", path = "../../embassy-executor", features = ["task-arena-size-32768", "arch-cortex-m", "executor-thread", "executor-interrupt", "defmt", "integrated-timers"] }
|
||||
embassy-executor = { version = "0.6.3", path = "../../embassy-executor", features = ["task-arena-size-32768", "arch-cortex-m", "executor-thread", "executor-interrupt", "defmt"] }
|
||||
embassy-time = { version = "0.3.2", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] }
|
||||
embassy-futures = { version = "0.1.0", path = "../../embassy-futures" }
|
||||
|
||||
|
@ -9,7 +9,7 @@ license = "MIT OR Apache-2.0"
|
||||
embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["defmt", "stm32h755zi-cm4", "time-driver-tim2", "exti", "memory-x", "unstable-pac", "chrono"] }
|
||||
embassy-sync = { version = "0.6.1", path = "../../embassy-sync", features = ["defmt"] }
|
||||
embassy-embedded-hal = { version = "0.2.0", path = "../../embassy-embedded-hal" }
|
||||
embassy-executor = { version = "0.6.3", path = "../../embassy-executor", features = ["task-arena-size-32768", "arch-cortex-m", "executor-thread", "executor-interrupt", "defmt", "integrated-timers"] }
|
||||
embassy-executor = { version = "0.6.3", path = "../../embassy-executor", features = ["task-arena-size-32768", "arch-cortex-m", "executor-thread", "executor-interrupt", "defmt"] }
|
||||
embassy-time = { version = "0.3.1", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] }
|
||||
embassy-net = { version = "0.5.0", path = "../../embassy-net", features = ["defmt", "tcp", "dhcpv4", "medium-ethernet", "proto-ipv6", "dns"] }
|
||||
embassy-usb = { version = "0.3.0", path = "../../embassy-usb", features = ["defmt"] }
|
||||
|
@ -9,7 +9,7 @@ license = "MIT OR Apache-2.0"
|
||||
embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["defmt", "stm32h755zi-cm7", "time-driver-tim3", "exti", "memory-x", "unstable-pac", "chrono"] }
|
||||
embassy-sync = { version = "0.6.1", path = "../../embassy-sync", features = ["defmt"] }
|
||||
embassy-embedded-hal = { version = "0.2.0", path = "../../embassy-embedded-hal" }
|
||||
embassy-executor = { version = "0.6.3", path = "../../embassy-executor", features = ["task-arena-size-32768", "arch-cortex-m", "executor-thread", "executor-interrupt", "defmt", "integrated-timers"] }
|
||||
embassy-executor = { version = "0.6.3", path = "../../embassy-executor", features = ["task-arena-size-32768", "arch-cortex-m", "executor-thread", "executor-interrupt", "defmt"] }
|
||||
embassy-time = { version = "0.3.1", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] }
|
||||
embassy-net = { version = "0.5.0", path = "../../embassy-net", features = ["defmt", "tcp", "dhcpv4", "medium-ethernet", "proto-ipv6", "dns"] }
|
||||
embassy-usb = { version = "0.3.0", path = "../../embassy-usb", features = ["defmt"] }
|
||||
|
@ -8,7 +8,7 @@ license = "MIT OR Apache-2.0"
|
||||
embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["defmt", "stm32h7b0vb", "time-driver-tim2", "exti", "memory-x", "unstable-pac", "chrono"] }
|
||||
embassy-sync = { version = "0.6.1", path = "../../embassy-sync", features = ["defmt"] }
|
||||
embassy-embedded-hal = { version = "0.2.0", path = "../../embassy-embedded-hal" }
|
||||
embassy-executor = { version = "0.6.3", path = "../../embassy-executor", features = ["task-arena-size-32768", "arch-cortex-m", "executor-thread", "executor-interrupt", "defmt", "integrated-timers"] }
|
||||
embassy-executor = { version = "0.6.3", path = "../../embassy-executor", features = ["task-arena-size-32768", "arch-cortex-m", "executor-thread", "executor-interrupt", "defmt"] }
|
||||
embassy-time = { version = "0.3.2", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] }
|
||||
embassy-net = { version = "0.5.0", path = "../../embassy-net", features = ["defmt", "tcp", "dhcpv4", "medium-ethernet", "proto-ipv6", "dns"] }
|
||||
embassy-usb = { version = "0.3.0", path = "../../embassy-usb", features = ["defmt"] }
|
||||
|
@ -8,7 +8,7 @@ license = "MIT OR Apache-2.0"
|
||||
# Change stm32h743bi to your chip name, if necessary.
|
||||
embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["defmt", "stm32h7s3l8", "time-driver-tim2", "exti", "memory-x", "unstable-pac", "chrono"] }
|
||||
embassy-sync = { version = "0.6.1", path = "../../embassy-sync", features = ["defmt"] }
|
||||
embassy-executor = { version = "0.6.3", path = "../../embassy-executor", features = ["task-arena-size-32768", "arch-cortex-m", "executor-thread", "executor-interrupt", "defmt", "integrated-timers"] }
|
||||
embassy-executor = { version = "0.6.3", path = "../../embassy-executor", features = ["task-arena-size-32768", "arch-cortex-m", "executor-thread", "executor-interrupt", "defmt"] }
|
||||
embassy-time = { version = "0.3.2", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] }
|
||||
embassy-net = { version = "0.5.0", path = "../../embassy-net", features = ["defmt", "tcp", "dhcpv4", "medium-ethernet", "proto-ipv6", "dns"] }
|
||||
embassy-usb = { version = "0.3.0", path = "../../embassy-usb", features = ["defmt"] }
|
||||
|
@ -8,7 +8,7 @@ license = "MIT OR Apache-2.0"
|
||||
# Change stm32l072cz to your chip name, if necessary.
|
||||
embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["defmt", "stm32l073rz", "unstable-pac", "time-driver-any", "exti", "memory-x"] }
|
||||
embassy-sync = { version = "0.6.1", path = "../../embassy-sync", features = ["defmt"] }
|
||||
embassy-executor = { version = "0.6.3", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] }
|
||||
embassy-executor = { version = "0.6.3", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "defmt"] }
|
||||
embassy-time = { version = "0.3.2", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] }
|
||||
|
||||
defmt = "0.3"
|
||||
|
@ -6,7 +6,7 @@ license = "MIT OR Apache-2.0"
|
||||
|
||||
[dependencies]
|
||||
embassy-sync = { version = "0.6.1", path = "../../embassy-sync", features = ["defmt"] }
|
||||
embassy-executor = { version = "0.6.3", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] }
|
||||
embassy-executor = { version = "0.6.3", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "defmt"] }
|
||||
embassy-time = { version = "0.3.2", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] }
|
||||
embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = [ "defmt", "stm32l151cb-a", "time-driver-any", "memory-x"] }
|
||||
embassy-usb = { version = "0.3.0", path = "../../embassy-usb", features = ["defmt"] }
|
||||
|
@ -8,7 +8,7 @@ license = "MIT OR Apache-2.0"
|
||||
# Change stm32l4s5vi to your chip name, if necessary.
|
||||
embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = [ "defmt", "unstable-pac", "stm32l4r5zi", "memory-x", "time-driver-any", "exti", "chrono"] }
|
||||
embassy-sync = { version = "0.6.1", path = "../../embassy-sync", features = ["defmt"] }
|
||||
embassy-executor = { version = "0.6.3", path = "../../embassy-executor", features = ["task-arena-size-32768", "arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] }
|
||||
embassy-executor = { version = "0.6.3", path = "../../embassy-executor", features = ["task-arena-size-32768", "arch-cortex-m", "executor-thread", "defmt"] }
|
||||
embassy-time = { version = "0.3.2", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768", ] }
|
||||
embassy-embedded-hal = { version = "0.2.0", path = "../../embassy-embedded-hal" }
|
||||
embassy-usb = { version = "0.3.0", path = "../../embassy-usb", features = ["defmt"] }
|
||||
|
@ -8,7 +8,7 @@ license = "MIT OR Apache-2.0"
|
||||
# Change stm32l552ze to your chip name, if necessary.
|
||||
embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = [ "defmt", "unstable-pac", "stm32l552ze", "time-driver-any", "exti", "memory-x", "low-power"] }
|
||||
embassy-sync = { version = "0.6.1", path = "../../embassy-sync", features = ["defmt"] }
|
||||
embassy-executor = { version = "0.6.3", path = "../../embassy-executor", features = ["task-arena-size-32768", "arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] }
|
||||
embassy-executor = { version = "0.6.3", path = "../../embassy-executor", features = ["task-arena-size-32768", "arch-cortex-m", "executor-thread", "defmt"] }
|
||||
embassy-time = { version = "0.3.2", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] }
|
||||
embassy-usb = { version = "0.3.0", path = "../../embassy-usb", features = ["defmt"] }
|
||||
embassy-net = { version = "0.5.0", path = "../../embassy-net", features = ["defmt", "tcp", "dhcpv4", "medium-ethernet"] }
|
||||
|
@ -8,7 +8,7 @@ license = "MIT OR Apache-2.0"
|
||||
# Change stm32u083rc to your chip name, if necessary.
|
||||
embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = [ "defmt", "time-driver-any", "stm32u083rc", "memory-x", "unstable-pac", "exti", "chrono"] }
|
||||
embassy-sync = { version = "0.6.1", path = "../../embassy-sync", features = ["defmt"] }
|
||||
embassy-executor = { version = "0.6.3", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] }
|
||||
embassy-executor = { version = "0.6.3", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "defmt"] }
|
||||
embassy-time = { version = "0.3.2", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] }
|
||||
embassy-usb = { version = "0.3.0", path = "../../embassy-usb", default-features = false, features = ["defmt"] }
|
||||
embassy-futures = { version = "0.1.0", path = "../../embassy-futures" }
|
||||
|
@ -8,7 +8,7 @@ license = "MIT OR Apache-2.0"
|
||||
# Change stm32u5g9zj to your chip name, if necessary.
|
||||
embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["defmt", "unstable-pac", "stm32u5g9zj", "time-driver-any", "memory-x" ] }
|
||||
embassy-sync = { version = "0.6.1", path = "../../embassy-sync", features = ["defmt"] }
|
||||
embassy-executor = { version = "0.6.3", path = "../../embassy-executor", features = ["task-arena-size-32768", "arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] }
|
||||
embassy-executor = { version = "0.6.3", path = "../../embassy-executor", features = ["task-arena-size-32768", "arch-cortex-m", "executor-thread", "defmt"] }
|
||||
embassy-time = { version = "0.3.2", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] }
|
||||
embassy-usb = { version = "0.3.0", path = "../../embassy-usb", features = ["defmt"] }
|
||||
embassy-futures = { version = "0.1.0", path = "../../embassy-futures" }
|
||||
|
@ -9,7 +9,7 @@ license = "MIT OR Apache-2.0"
|
||||
embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = [ "defmt", "stm32wb55rg", "time-driver-any", "memory-x", "exti"] }
|
||||
embassy-stm32-wpan = { version = "0.1.0", path = "../../embassy-stm32-wpan", features = ["defmt", "stm32wb55rg"] }
|
||||
embassy-sync = { version = "0.6.1", path = "../../embassy-sync", features = ["defmt"] }
|
||||
embassy-executor = { version = "0.6.3", path = "../../embassy-executor", features = ["task-arena-size-32768", "arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] }
|
||||
embassy-executor = { version = "0.6.3", path = "../../embassy-executor", features = ["task-arena-size-32768", "arch-cortex-m", "executor-thread", "defmt"] }
|
||||
embassy-time = { version = "0.3.2", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] }
|
||||
embassy-net = { version = "0.5.0", path = "../../embassy-net", features = ["defmt", "udp", "proto-ipv6", "medium-ieee802154", ], optional=true }
|
||||
|
||||
|
@ -7,7 +7,7 @@ license = "MIT OR Apache-2.0"
|
||||
[dependencies]
|
||||
embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = [ "defmt", "stm32wba52cg", "time-driver-any", "memory-x", "exti"] }
|
||||
embassy-sync = { version = "0.6.1", path = "../../embassy-sync", features = ["defmt"] }
|
||||
embassy-executor = { version = "0.6.3", path = "../../embassy-executor", features = ["task-arena-size-32768", "arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] }
|
||||
embassy-executor = { version = "0.6.3", path = "../../embassy-executor", features = ["task-arena-size-32768", "arch-cortex-m", "executor-thread", "defmt"] }
|
||||
embassy-time = { version = "0.3.2", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] }
|
||||
embassy-net = { version = "0.5.0", path = "../../embassy-net", features = ["defmt", "udp", "proto-ipv6", "medium-ieee802154", ], optional=true }
|
||||
|
||||
|
@ -8,7 +8,7 @@ license = "MIT OR Apache-2.0"
|
||||
# Change stm32wl55jc-cm4 to your chip name, if necessary.
|
||||
embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["defmt", "stm32wl55jc-cm4", "time-driver-any", "memory-x", "unstable-pac", "exti", "chrono"] }
|
||||
embassy-sync = { version = "0.6.1", path = "../../embassy-sync", features = ["defmt"] }
|
||||
embassy-executor = { version = "0.6.3", path = "../../embassy-executor", features = ["task-arena-size-4096", "arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] }
|
||||
embassy-executor = { version = "0.6.3", path = "../../embassy-executor", features = ["task-arena-size-4096", "arch-cortex-m", "executor-thread", "defmt"] }
|
||||
embassy-time = { version = "0.3.2", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] }
|
||||
embassy-embedded-hal = { version = "0.2.0", path = "../../embassy-embedded-hal" }
|
||||
|
||||
|
@ -9,7 +9,7 @@ crate-type = ["cdylib"]
|
||||
|
||||
[dependencies]
|
||||
embassy-sync = { version = "0.6.1", path = "../../embassy-sync", features = ["log"] }
|
||||
embassy-executor = { version = "0.6.3", path = "../../embassy-executor", features = ["arch-wasm", "executor-thread", "log", "integrated-timers"] }
|
||||
embassy-executor = { version = "0.6.3", path = "../../embassy-executor", features = ["arch-wasm", "executor-thread", "log"] }
|
||||
embassy-time = { version = "0.3.2", path = "../../embassy-time", features = ["log", "wasm", ] }
|
||||
|
||||
wasm-logger = "0.2.0"
|
||||
|
@ -9,7 +9,7 @@ teleprobe-meta = "1"
|
||||
|
||||
embassy-futures = { version = "0.1.0", path = "../../embassy-futures" }
|
||||
embassy-sync = { version = "0.6.1", path = "../../embassy-sync", features = ["defmt", ] }
|
||||
embassy-executor = { version = "0.6.3", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] }
|
||||
embassy-executor = { version = "0.6.3", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "defmt"] }
|
||||
embassy-time = { version = "0.3.2", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime"] }
|
||||
embassy-nrf = { version = "0.2.0", path = "../../embassy-nrf", features = ["defmt", "time-driver-rtc1", "gpiote", "unstable-pac"] }
|
||||
embedded-io-async = { version = "0.6.1", features = ["defmt-03"] }
|
||||
|
@ -8,7 +8,7 @@ license = "MIT OR Apache-2.0"
|
||||
teleprobe-meta = "1.1"
|
||||
|
||||
embassy-sync = { version = "0.6.1", path = "../../embassy-sync", features = ["defmt"] }
|
||||
embassy-executor = { version = "0.6.3", path = "../../embassy-executor", features = ["task-arena-size-32768", "arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] }
|
||||
embassy-executor = { version = "0.6.3", path = "../../embassy-executor", features = ["task-arena-size-32768", "arch-cortex-m", "executor-thread", "defmt"] }
|
||||
embassy-time = { version = "0.3.2", path = "../../embassy-time", features = ["defmt", ] }
|
||||
embassy-rp = { version = "0.2.0", path = "../../embassy-rp", features = [ "defmt", "unstable-pac", "time-driver", "critical-section-impl", "intrinsics", "rom-v2-intrinsics", "run-from-ram", "rp2040"] }
|
||||
embassy-futures = { version = "0.1.0", path = "../../embassy-futures" }
|
||||
|
@ -60,7 +60,7 @@ cm0 = ["portable-atomic/unsafe-assume-single-core"]
|
||||
teleprobe-meta = "1"
|
||||
|
||||
embassy-sync = { version = "0.6.1", path = "../../embassy-sync", features = ["defmt"] }
|
||||
embassy-executor = { version = "0.6.3", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] }
|
||||
embassy-executor = { version = "0.6.3", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "defmt"] }
|
||||
embassy-time = { version = "0.3.2", path = "../../embassy-time", features = ["defmt", "tick-hz-131_072", "defmt-timestamp-uptime"] }
|
||||
embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = [ "defmt", "unstable-pac", "memory-x", "time-driver-any"] }
|
||||
embassy-futures = { version = "0.1.0", path = "../../embassy-futures" }
|
||||
|
Loading…
x
Reference in New Issue
Block a user