From fa807d2f08b69e3fe3bdf2fde3611e5292ba32bb Mon Sep 17 00:00:00 2001 From: xoviat Date: Thu, 11 Mar 2021 16:25:38 -0600 Subject: [PATCH 01/10] add qei trait and implementation --- embassy-stm32f4/src/lib.rs | 1 + embassy-stm32f4/src/qei.rs | 95 ++++++++++++++++++++++++++++++++++++++ embassy-traits/src/lib.rs | 1 + embassy-traits/src/qei.rs | 14 ++++++ 4 files changed, 111 insertions(+) create mode 100644 embassy-stm32f4/src/qei.rs create mode 100644 embassy-traits/src/qei.rs diff --git a/embassy-stm32f4/src/lib.rs b/embassy-stm32f4/src/lib.rs index aa31951d..aa99068c 100644 --- a/embassy-stm32f4/src/lib.rs +++ b/embassy-stm32f4/src/lib.rs @@ -313,5 +313,6 @@ pub(crate) mod fmt; pub mod exti; pub mod interrupt; +pub mod qei; pub mod rtc; pub mod serial; diff --git a/embassy-stm32f4/src/qei.rs b/embassy-stm32f4/src/qei.rs new file mode 100644 index 00000000..1d4689e1 --- /dev/null +++ b/embassy-stm32f4/src/qei.rs @@ -0,0 +1,95 @@ +use crate::interrupt; +use core::future::Future; +use core::pin::Pin; +use embassy::interrupt::Interrupt; +use embassy::traits::qei::WaitForRotate; +use embedded_hal::Direction; +use embedded_hal::Qei as THQei; +use stm32f4xx_hal::pac::TIM2; +use stm32f4xx_hal::qei::{Pins, Qei as HalQei}; + +pub struct Qei { + qei: HalQei, + int: T::Interrupt, +} + +impl> Qei { + pub fn tim2(tim: TIM2, pins: PINS, interrupt: interrupt::TIM2) -> Self { + let qei = HalQei::tim2(tim, pins); + + let tim = unsafe { + &mut *(stm32f4xx_hal::stm32::TIM2::ptr() + as *mut stm32f4xx_hal::stm32::tim2::RegisterBlock) + }; + /* + enable qei interrupt + */ + tim.dier.write(|w| w.uie().set_bit()); + + Qei { + qei: qei, + int: interrupt, + } + } +} + +impl + 'static> WaitForRotate for Qei { + type RotateFuture<'a> = impl Future + 'a; + + fn wait_for_rotate<'a>( + self: Pin<&'a mut Self>, + count_down: u16, + count_up: u16, + ) -> Self::RotateFuture<'a> { + let s = unsafe { self.get_unchecked_mut() }; + + let tim = unsafe { + &mut *(stm32f4xx_hal::stm32::TIM2::ptr() + as *mut stm32f4xx_hal::stm32::tim2::RegisterBlock) + }; + + /* + the interrupt will be reached at zero or the max count + write the total range to the qei. + */ + tim.arr + .write(|w| unsafe { w.bits((count_down + count_up) as u32) }); + + /* + set timer to the correct value in the range + */ + tim.cnt.write(|w| unsafe { w.bits(count_down as u32) }); + + /* + clear interrupt flag + */ + tim.sr.write(|w| w.uif().clear_bit()); + + async move { + embassy::util::InterruptFuture::new(&mut s.int).await; + + if tim.cnt.read().bits() == 0 { + Direction::Downcounting + } else if tim.cnt.read() == count_down + count_up { + Direction::Upcounting + } else { + panic!("unexpected value") + } + } + } +} + +mod sealed { + pub trait Sealed {} +} + +pub trait Instance: sealed::Sealed { + type Interrupt: interrupt::Interrupt; +} + +#[cfg(feature = "stm32f405")] +impl sealed::Sealed for TIM2 {} +#[cfg(feature = "stm32f405")] +impl Instance for TIM2 { + type Interrupt = interrupt::TIM2; +} diff --git a/embassy-traits/src/lib.rs b/embassy-traits/src/lib.rs index 10d44d9d..d8b06a09 100644 --- a/embassy-traits/src/lib.rs +++ b/embassy-traits/src/lib.rs @@ -9,5 +9,6 @@ pub mod delay; pub mod flash; pub mod gpio; +pub mod qei; pub mod i2c; pub mod uart; diff --git a/embassy-traits/src/qei.rs b/embassy-traits/src/qei.rs new file mode 100644 index 00000000..7e0a8961 --- /dev/null +++ b/embassy-traits/src/qei.rs @@ -0,0 +1,14 @@ +use core::future::Future; +use core::pin::Pin; +use embedded_hal::Direction; + +// Wait for a specified number of rotations either up or down +pub trait WaitForRotate { + type RotateFuture<'a>: Future + 'a; + + fn wait_for_rotate<'a>( + self: Pin<&'a mut Self>, + count_down: u16, + count_up: u16, + ) -> Self::RotateFuture<'a>; +} From 6cebd27cc127cdef04f14ad0ff2982714e456b8b Mon Sep 17 00:00:00 2001 From: xoviat Date: Thu, 11 Mar 2021 16:28:21 -0600 Subject: [PATCH 02/10] add embedded-hal --- embassy-traits/Cargo.toml | 1 + 1 file changed, 1 insertion(+) diff --git a/embassy-traits/Cargo.toml b/embassy-traits/Cargo.toml index 626bf9f3..737f9118 100644 --- a/embassy-traits/Cargo.toml +++ b/embassy-traits/Cargo.toml @@ -9,3 +9,4 @@ std = [] [dependencies] defmt = { version = "0.2.0", optional = true } +embedded-hal = { version = "0.2.3", features = ["unproven"] } From 5b8c18dad321785ce23e376e58af31e46e0dc27e Mon Sep 17 00:00:00 2001 From: Vadim Kaushan Date: Sun, 14 Mar 2021 23:17:45 +0300 Subject: [PATCH 03/10] Fix probe-run install command --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 3cf1b61f..29c4a221 100644 --- a/README.md +++ b/README.md @@ -45,7 +45,7 @@ GPIO pins are set up for the `nrf52840-dk` board (PCA10056) - Install `probe-run` with defmt support. ``` -cargo install --git https://github.com/knurling-rs/probe-run --branch main --features defmt +cargo install probe-run ``` - Run the example From 385895767c7d5f5c270081c5af2a0e49219a28bc Mon Sep 17 00:00:00 2001 From: Vadim Kaushan Date: Sun, 14 Mar 2021 23:18:09 +0300 Subject: [PATCH 04/10] Fix exti driver --- embassy-stm32f4/src/exti.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/embassy-stm32f4/src/exti.rs b/embassy-stm32f4/src/exti.rs index 52cf5a29..932f29b9 100644 --- a/embassy-stm32f4/src/exti.rs +++ b/embassy-stm32f4/src/exti.rs @@ -482,7 +482,7 @@ exti!(gpioj, [ feature = "stm32f469", feature = "stm32f479" ))] -exti!(gpioj, [ +exti!(gpiok, [ EXTI0 => PK0, EXTI1 => PK1, EXTI2 => PK2, From 693177ec27600f71c70f6089ff19d3f6a2a13755 Mon Sep 17 00:00:00 2001 From: Michael Beaumont Date: Sun, 14 Mar 2021 23:28:29 +0100 Subject: [PATCH 05/10] Add embedded_hal::digital::v2::* for ExtiPin --- embassy-stm32f4/src/exti.rs | 47 +++++++++++++++++++++++++++++++++++++ 1 file changed, 47 insertions(+) diff --git a/embassy-stm32f4/src/exti.rs b/embassy-stm32f4/src/exti.rs index 52cf5a29..d9a68daf 100644 --- a/embassy-stm32f4/src/exti.rs +++ b/embassy-stm32f4/src/exti.rs @@ -10,6 +10,7 @@ use crate::hal::gpio; use crate::hal::gpio::{Edge, ExtiPin as HalExtiPin}; use crate::hal::syscfg::SysCfg; use crate::pac::EXTI; +use embedded_hal::digital::v2 as digital; use crate::interrupt; @@ -42,6 +43,52 @@ pub struct ExtiPin { _mgr: &'static ExtiManager, } +impl digital::OutputPin for ExtiPin { + type Error = T::Error; + + fn set_low(&mut self) -> Result<(), Self::Error> { + self.pin.set_low() + } + + fn set_high(&mut self) -> Result<(), Self::Error> { + self.pin.set_high() + } +} + +impl digital::StatefulOutputPin + for ExtiPin +{ + fn is_set_low(&self) -> Result { + self.pin.is_set_low() + } + + fn is_set_high(&self) -> Result { + self.pin.is_set_high() + } +} + +impl digital::ToggleableOutputPin + for ExtiPin +{ + type Error = T::Error; + + fn toggle(&mut self) -> Result<(), Self::Error> { + self.pin.toggle() + } +} + +impl digital::InputPin for ExtiPin { + type Error = T::Error; + + fn is_high(&self) -> Result { + self.pin.is_high() + } + + fn is_low(&self) -> Result { + self.pin.is_low() + } +} + /* Irq Handler Description EXTI0_IRQn EXTI0_IRQHandler Handler for pins connected to line 0 From a4bee1c1d2c8b252142bed7be8c16f9d77f3d1ff Mon Sep 17 00:00:00 2001 From: xoviat Date: Sun, 14 Mar 2021 17:41:59 -0500 Subject: [PATCH 06/10] update docs --- embassy-traits/src/qei.rs | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/embassy-traits/src/qei.rs b/embassy-traits/src/qei.rs index 7e0a8961..73581256 100644 --- a/embassy-traits/src/qei.rs +++ b/embassy-traits/src/qei.rs @@ -6,6 +6,14 @@ use embedded_hal::Direction; pub trait WaitForRotate { type RotateFuture<'a>: Future + 'a; + /// Wait for a specified number of rotations, in ticks, either up or down. + /// + /// Return Direction::Upcounting if the high bound is reached. + /// Return Direction::Downcounting if the low bound is reached. + /// + /// Number of ticks is encoder dependent. As an example, if we connect + /// the Bourns PEC11H-4120F-S0020, we have 20 ticks per full rotation. + /// Other encoders may vary. fn wait_for_rotate<'a>( self: Pin<&'a mut Self>, count_down: u16, From 3fcecd076b9655f471a12d164bf1aa8aecbaeef9 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Mon, 15 Mar 2021 00:22:03 +0100 Subject: [PATCH 07/10] Rename i2c trait --- embassy-traits/src/i2c.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/embassy-traits/src/i2c.rs b/embassy-traits/src/i2c.rs index 4dc8865e..70420388 100644 --- a/embassy-traits/src/i2c.rs +++ b/embassy-traits/src/i2c.rs @@ -91,8 +91,7 @@ impl AddressMode for SevenBitAddress {} impl AddressMode for TenBitAddress {} -/// Blocking read -pub trait Read { +pub trait I2c { /// Error type type Error; From 6ed1a8713895175520de7cb9f1f1239eef57263a Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Wed, 17 Mar 2021 02:48:16 +0100 Subject: [PATCH 08/10] Update features for latest nightly. --- embassy-nrf-examples/src/bin/buffered_uart.rs | 2 ++ embassy-nrf-examples/src/bin/executor_fairness_test.rs | 2 ++ embassy-nrf-examples/src/bin/gpiote_channel.rs | 2 ++ embassy-nrf-examples/src/bin/gpiote_port.rs | 2 ++ embassy-nrf-examples/src/bin/multiprio.rs | 2 ++ embassy-nrf-examples/src/bin/qspi.rs | 2 ++ embassy-nrf-examples/src/bin/rtc_async.rs | 2 ++ embassy-nrf-examples/src/bin/rtc_raw.rs | 2 ++ embassy-nrf-examples/src/bin/spim.rs | 2 ++ embassy-nrf-examples/src/bin/uart.rs | 2 ++ embassy-nrf/src/lib.rs | 2 ++ embassy-std-examples/src/bin/serial.rs | 2 ++ embassy-std-examples/src/bin/tick.rs | 2 ++ embassy-stm32f4-examples/src/bin/exti.rs | 2 ++ embassy-stm32f4-examples/src/bin/hello.rs | 2 ++ embassy-stm32f4-examples/src/bin/rtc_async.rs | 2 ++ embassy-stm32f4-examples/src/bin/serial.rs | 2 ++ embassy-stm32f4/src/lib.rs | 2 ++ embassy-traits/src/lib.rs | 4 +++- embassy/src/lib.rs | 2 ++ 20 files changed, 41 insertions(+), 1 deletion(-) diff --git a/embassy-nrf-examples/src/bin/buffered_uart.rs b/embassy-nrf-examples/src/bin/buffered_uart.rs index 42e81229..71e9b4a7 100644 --- a/embassy-nrf-examples/src/bin/buffered_uart.rs +++ b/embassy-nrf-examples/src/bin/buffered_uart.rs @@ -1,5 +1,7 @@ #![no_std] #![no_main] +#![feature(min_type_alias_impl_trait)] +#![feature(impl_trait_in_bindings)] #![feature(type_alias_impl_trait)] #[path = "../example_common.rs"] diff --git a/embassy-nrf-examples/src/bin/executor_fairness_test.rs b/embassy-nrf-examples/src/bin/executor_fairness_test.rs index 1b995573..6d0a311d 100644 --- a/embassy-nrf-examples/src/bin/executor_fairness_test.rs +++ b/embassy-nrf-examples/src/bin/executor_fairness_test.rs @@ -1,5 +1,7 @@ #![no_std] #![no_main] +#![feature(min_type_alias_impl_trait)] +#![feature(impl_trait_in_bindings)] #![feature(type_alias_impl_trait)] #[path = "../example_common.rs"] diff --git a/embassy-nrf-examples/src/bin/gpiote_channel.rs b/embassy-nrf-examples/src/bin/gpiote_channel.rs index 4c4dabc6..3764ba1c 100644 --- a/embassy-nrf-examples/src/bin/gpiote_channel.rs +++ b/embassy-nrf-examples/src/bin/gpiote_channel.rs @@ -1,5 +1,7 @@ #![no_std] #![no_main] +#![feature(min_type_alias_impl_trait)] +#![feature(impl_trait_in_bindings)] #![feature(type_alias_impl_trait)] #[path = "../example_common.rs"] diff --git a/embassy-nrf-examples/src/bin/gpiote_port.rs b/embassy-nrf-examples/src/bin/gpiote_port.rs index ccb5bb97..36ee1c8e 100644 --- a/embassy-nrf-examples/src/bin/gpiote_port.rs +++ b/embassy-nrf-examples/src/bin/gpiote_port.rs @@ -1,5 +1,7 @@ #![no_std] #![no_main] +#![feature(min_type_alias_impl_trait)] +#![feature(impl_trait_in_bindings)] #![feature(type_alias_impl_trait)] #[path = "../example_common.rs"] diff --git a/embassy-nrf-examples/src/bin/multiprio.rs b/embassy-nrf-examples/src/bin/multiprio.rs index d652633a..f5ebf67f 100644 --- a/embassy-nrf-examples/src/bin/multiprio.rs +++ b/embassy-nrf-examples/src/bin/multiprio.rs @@ -55,6 +55,8 @@ #![no_std] #![no_main] +#![feature(min_type_alias_impl_trait)] +#![feature(impl_trait_in_bindings)] #![feature(type_alias_impl_trait)] #[path = "../example_common.rs"] diff --git a/embassy-nrf-examples/src/bin/qspi.rs b/embassy-nrf-examples/src/bin/qspi.rs index 587ed7dd..7d8a45f7 100644 --- a/embassy-nrf-examples/src/bin/qspi.rs +++ b/embassy-nrf-examples/src/bin/qspi.rs @@ -1,5 +1,7 @@ #![no_std] #![no_main] +#![feature(min_type_alias_impl_trait)] +#![feature(impl_trait_in_bindings)] #![feature(type_alias_impl_trait)] #[path = "../example_common.rs"] diff --git a/embassy-nrf-examples/src/bin/rtc_async.rs b/embassy-nrf-examples/src/bin/rtc_async.rs index 5fb68633..ec437425 100644 --- a/embassy-nrf-examples/src/bin/rtc_async.rs +++ b/embassy-nrf-examples/src/bin/rtc_async.rs @@ -1,5 +1,7 @@ #![no_std] #![no_main] +#![feature(min_type_alias_impl_trait)] +#![feature(impl_trait_in_bindings)] #![feature(type_alias_impl_trait)] #[path = "../example_common.rs"] diff --git a/embassy-nrf-examples/src/bin/rtc_raw.rs b/embassy-nrf-examples/src/bin/rtc_raw.rs index 017487da..884ca92b 100644 --- a/embassy-nrf-examples/src/bin/rtc_raw.rs +++ b/embassy-nrf-examples/src/bin/rtc_raw.rs @@ -1,5 +1,7 @@ #![no_std] #![no_main] +#![feature(min_type_alias_impl_trait)] +#![feature(impl_trait_in_bindings)] #![feature(type_alias_impl_trait)] #[path = "../example_common.rs"] diff --git a/embassy-nrf-examples/src/bin/spim.rs b/embassy-nrf-examples/src/bin/spim.rs index 0a284dc8..d6b3a5f8 100644 --- a/embassy-nrf-examples/src/bin/spim.rs +++ b/embassy-nrf-examples/src/bin/spim.rs @@ -1,5 +1,7 @@ #![no_std] #![no_main] +#![feature(min_type_alias_impl_trait)] +#![feature(impl_trait_in_bindings)] #![feature(type_alias_impl_trait)] #[path = "../example_common.rs"] diff --git a/embassy-nrf-examples/src/bin/uart.rs b/embassy-nrf-examples/src/bin/uart.rs index 41f9e127..0acd6fde 100644 --- a/embassy-nrf-examples/src/bin/uart.rs +++ b/embassy-nrf-examples/src/bin/uart.rs @@ -1,5 +1,7 @@ #![no_std] #![no_main] +#![feature(min_type_alias_impl_trait)] +#![feature(impl_trait_in_bindings)] #![feature(type_alias_impl_trait)] #[path = "../example_common.rs"] diff --git a/embassy-nrf/src/lib.rs b/embassy-nrf/src/lib.rs index bb37ec36..2c72b912 100644 --- a/embassy-nrf/src/lib.rs +++ b/embassy-nrf/src/lib.rs @@ -1,6 +1,8 @@ #![no_std] #![feature(generic_associated_types)] #![feature(asm)] +#![feature(min_type_alias_impl_trait)] +#![feature(impl_trait_in_bindings)] #![feature(type_alias_impl_trait)] #![allow(incomplete_features)] diff --git a/embassy-std-examples/src/bin/serial.rs b/embassy-std-examples/src/bin/serial.rs index cad64fb2..6d628fe1 100644 --- a/embassy-std-examples/src/bin/serial.rs +++ b/embassy-std-examples/src/bin/serial.rs @@ -1,3 +1,5 @@ +#![feature(min_type_alias_impl_trait)] +#![feature(impl_trait_in_bindings)] #![feature(type_alias_impl_trait)] #[path = "../serial_port.rs"] diff --git a/embassy-std-examples/src/bin/tick.rs b/embassy-std-examples/src/bin/tick.rs index af2305f3..96eef067 100644 --- a/embassy-std-examples/src/bin/tick.rs +++ b/embassy-std-examples/src/bin/tick.rs @@ -1,3 +1,5 @@ +#![feature(min_type_alias_impl_trait)] +#![feature(impl_trait_in_bindings)] #![feature(type_alias_impl_trait)] use embassy::executor::task; diff --git a/embassy-stm32f4-examples/src/bin/exti.rs b/embassy-stm32f4-examples/src/bin/exti.rs index 6b3568d6..0c6561cd 100644 --- a/embassy-stm32f4-examples/src/bin/exti.rs +++ b/embassy-stm32f4-examples/src/bin/exti.rs @@ -1,6 +1,8 @@ #![no_std] #![no_main] #![feature(trait_alias)] +#![feature(min_type_alias_impl_trait)] +#![feature(impl_trait_in_bindings)] #![feature(type_alias_impl_trait)] #[path = "../example_common.rs"] diff --git a/embassy-stm32f4-examples/src/bin/hello.rs b/embassy-stm32f4-examples/src/bin/hello.rs index 77c54464..8a665414 100644 --- a/embassy-stm32f4-examples/src/bin/hello.rs +++ b/embassy-stm32f4-examples/src/bin/hello.rs @@ -1,6 +1,8 @@ #![no_std] #![no_main] #![feature(trait_alias)] +#![feature(min_type_alias_impl_trait)] +#![feature(impl_trait_in_bindings)] #![feature(type_alias_impl_trait)] #[path = "../example_common.rs"] diff --git a/embassy-stm32f4-examples/src/bin/rtc_async.rs b/embassy-stm32f4-examples/src/bin/rtc_async.rs index e5321226..41ce2d4f 100644 --- a/embassy-stm32f4-examples/src/bin/rtc_async.rs +++ b/embassy-stm32f4-examples/src/bin/rtc_async.rs @@ -1,5 +1,7 @@ #![no_std] #![no_main] +#![feature(min_type_alias_impl_trait)] +#![feature(impl_trait_in_bindings)] #![feature(type_alias_impl_trait)] #[path = "../example_common.rs"] diff --git a/embassy-stm32f4-examples/src/bin/serial.rs b/embassy-stm32f4-examples/src/bin/serial.rs index 6351f728..b138bfdd 100644 --- a/embassy-stm32f4-examples/src/bin/serial.rs +++ b/embassy-stm32f4-examples/src/bin/serial.rs @@ -1,6 +1,8 @@ #![no_std] #![no_main] #![feature(trait_alias)] +#![feature(min_type_alias_impl_trait)] +#![feature(impl_trait_in_bindings)] #![feature(type_alias_impl_trait)] #[path = "../example_common.rs"] diff --git a/embassy-stm32f4/src/lib.rs b/embassy-stm32f4/src/lib.rs index aa99068c..e5e82cd5 100644 --- a/embassy-stm32f4/src/lib.rs +++ b/embassy-stm32f4/src/lib.rs @@ -1,6 +1,8 @@ #![no_std] #![feature(generic_associated_types)] #![feature(asm)] +#![feature(min_type_alias_impl_trait)] +#![feature(impl_trait_in_bindings)] #![feature(type_alias_impl_trait)] #![allow(incomplete_features)] diff --git a/embassy-traits/src/lib.rs b/embassy-traits/src/lib.rs index d8b06a09..bf5c63de 100644 --- a/embassy-traits/src/lib.rs +++ b/embassy-traits/src/lib.rs @@ -4,11 +4,13 @@ #![feature(const_fn_fn_ptr_basics)] #![feature(const_option)] #![allow(incomplete_features)] +#![feature(min_type_alias_impl_trait)] +#![feature(impl_trait_in_bindings)] #![feature(type_alias_impl_trait)] pub mod delay; pub mod flash; pub mod gpio; -pub mod qei; pub mod i2c; +pub mod qei; pub mod uart; diff --git a/embassy/src/lib.rs b/embassy/src/lib.rs index b7ed4e70..55c8d857 100644 --- a/embassy/src/lib.rs +++ b/embassy/src/lib.rs @@ -4,6 +4,8 @@ #![feature(const_fn_fn_ptr_basics)] #![feature(const_option)] #![allow(incomplete_features)] +#![feature(min_type_alias_impl_trait)] +#![feature(impl_trait_in_bindings)] #![feature(type_alias_impl_trait)] // This mod MUST go first, so that the others see its macros. From ab01e0be3b2faa320ffc169a6aa629fe44a2080c Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Wed, 17 Mar 2021 01:47:45 +0100 Subject: [PATCH 09/10] Do not reexport atomic-polyfill --- embassy-macros/src/lib.rs | 8 ++++---- embassy/src/executor/mod.rs | 2 +- embassy/src/executor/raw.rs | 2 +- embassy/src/executor/run_queue.rs | 2 +- embassy/src/executor/timer_queue.rs | 2 +- embassy/src/interrupt.rs | 2 +- embassy/src/lib.rs | 7 ++++++- embassy/src/util/forever.rs | 2 +- 8 files changed, 16 insertions(+), 11 deletions(-) diff --git a/embassy-macros/src/lib.rs b/embassy-macros/src/lib.rs index 5b834ff1..710c5a15 100644 --- a/embassy-macros/src/lib.rs +++ b/embassy-macros/src/lib.rs @@ -157,17 +157,17 @@ pub fn interrupt_take(item: TokenStream) -> TokenStream { static HANDLER: ::embassy::interrupt::Handler; } - let func = HANDLER.func.load(::embassy::atomic::Ordering::Acquire); - let ctx = HANDLER.ctx.load(::embassy::atomic::Ordering::Acquire); + let func = HANDLER.func.load(::embassy::export::atomic::Ordering::Acquire); + let ctx = HANDLER.ctx.load(::embassy::export::atomic::Ordering::Acquire); if !func.is_null() { let func: fn(*mut ()) = ::core::mem::transmute(func); func(ctx) } } - static TAKEN: ::embassy::atomic::AtomicBool = ::embassy::atomic::AtomicBool::new(false); + static TAKEN: ::embassy::export::atomic::AtomicBool = ::embassy::export::atomic::AtomicBool::new(false); - if TAKEN.compare_exchange(false, true, ::embassy::atomic::Ordering::AcqRel, ::embassy::atomic::Ordering::Acquire).is_err() { + if TAKEN.compare_exchange(false, true, ::embassy::export::atomic::Ordering::AcqRel, ::embassy::export::atomic::Ordering::Acquire).is_err() { panic!("IRQ Already taken"); } diff --git a/embassy/src/executor/mod.rs b/embassy/src/executor/mod.rs index 7a6caf70..787230c0 100644 --- a/embassy/src/executor/mod.rs +++ b/embassy/src/executor/mod.rs @@ -1,5 +1,6 @@ pub use embassy_macros::task; +use atomic_polyfill::Ordering; use core::future::Future; use core::marker::PhantomData; use core::pin::Pin; @@ -15,7 +16,6 @@ mod util; mod waker; use self::util::UninitCell; -use crate::atomic::Ordering; use crate::fmt::panic; use crate::interrupt::{Interrupt, InterruptExt}; use crate::time::Alarm; diff --git a/embassy/src/executor/raw.rs b/embassy/src/executor/raw.rs index 0c947080..84e171df 100644 --- a/embassy/src/executor/raw.rs +++ b/embassy/src/executor/raw.rs @@ -1,3 +1,4 @@ +use atomic_polyfill::{AtomicU32, Ordering}; use core::cell::Cell; use core::cmp::min; use core::marker::PhantomData; @@ -9,7 +10,6 @@ use super::run_queue::{RunQueue, RunQueueItem}; use super::timer_queue::{TimerQueue, TimerQueueItem}; use super::util::UninitCell; use super::waker; -use crate::atomic::{AtomicU32, Ordering}; use crate::time::{Alarm, Instant}; /// Task is spawned (has a future) diff --git a/embassy/src/executor/run_queue.rs b/embassy/src/executor/run_queue.rs index fb7c65e0..1d1023e5 100644 --- a/embassy/src/executor/run_queue.rs +++ b/embassy/src/executor/run_queue.rs @@ -1,8 +1,8 @@ +use atomic_polyfill::{AtomicPtr, Ordering}; use core::ptr; use core::ptr::NonNull; use super::raw::Task; -use crate::atomic::{AtomicPtr, Ordering}; pub(crate) struct RunQueueItem { next: AtomicPtr, diff --git a/embassy/src/executor/timer_queue.rs b/embassy/src/executor/timer_queue.rs index bd043aeb..d72eb93b 100644 --- a/embassy/src/executor/timer_queue.rs +++ b/embassy/src/executor/timer_queue.rs @@ -1,10 +1,10 @@ +use atomic_polyfill::{AtomicPtr, Ordering}; use core::cell::Cell; use core::cmp::min; use core::ptr; use core::ptr::NonNull; use super::raw::{Task, STATE_TIMER_QUEUED}; -use crate::atomic::{AtomicPtr, Ordering}; use crate::time::Instant; pub(crate) struct TimerQueueItem { diff --git a/embassy/src/interrupt.rs b/embassy/src/interrupt.rs index 6491a57d..013e722e 100644 --- a/embassy/src/interrupt.rs +++ b/embassy/src/interrupt.rs @@ -1,7 +1,7 @@ use core::ptr; use cortex_m::peripheral::NVIC; -use crate::atomic::{AtomicBool, AtomicPtr, Ordering}; +use atomic_polyfill::{AtomicBool, AtomicPtr, Ordering}; pub use embassy_macros::interrupt_declare as declare; pub use embassy_macros::interrupt_take as take; diff --git a/embassy/src/lib.rs b/embassy/src/lib.rs index 55c8d857..b6fefc9f 100644 --- a/embassy/src/lib.rs +++ b/embassy/src/lib.rs @@ -18,4 +18,9 @@ pub mod time; pub mod util; pub use embassy_traits as traits; -pub use atomic_polyfill as atomic; + +#[doc(hidden)] +/// Implementation details for embassy macros. DO NOT USE. +pub mod export { + pub use atomic_polyfill as atomic; +} diff --git a/embassy/src/util/forever.rs b/embassy/src/util/forever.rs index fac3c260..ac23a3ce 100644 --- a/embassy/src/util/forever.rs +++ b/embassy/src/util/forever.rs @@ -1,7 +1,7 @@ use core::cell::UnsafeCell; use core::mem::MaybeUninit; -use crate::atomic::{AtomicBool, Ordering}; +use atomic_polyfill::{AtomicBool, Ordering}; pub struct Forever { used: AtomicBool, From bb68f5d0e83eec609ef0016baa4710b30ce49d62 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Wed, 17 Mar 2021 01:55:01 +0100 Subject: [PATCH 10/10] Add optimized single-word WakerRegistration, add AtomicWakerRegistration. --- embassy/Cargo.toml | 2 + embassy/src/util/mod.rs | 2 + embassy/src/util/waker.rs | 75 +++++++++++++++++--------- embassy/src/util/waker_agnostic.rs | 87 ++++++++++++++++++++++++++++++ 4 files changed, 142 insertions(+), 24 deletions(-) create mode 100644 embassy/src/util/waker_agnostic.rs diff --git a/embassy/Cargo.toml b/embassy/Cargo.toml index c58fcbf2..39ad64a0 100644 --- a/embassy/Cargo.toml +++ b/embassy/Cargo.toml @@ -13,6 +13,8 @@ defmt-info = [] defmt-warn = [] defmt-error = [] +executor-agnostic = [] + [dependencies] defmt = { version = "0.2.0", optional = true } log = { version = "0.4.11", optional = true } diff --git a/embassy/src/util/mod.rs b/embassy/src/util/mod.rs index ae434a8b..e64e7f1f 100644 --- a/embassy/src/util/mod.rs +++ b/embassy/src/util/mod.rs @@ -3,6 +3,8 @@ mod forever; mod mutex; mod portal; mod signal; + +#[cfg_attr(feature = "executor-agnostic", path = "waker_agnostic.rs")] mod waker; pub use drop_bomb::*; diff --git a/embassy/src/util/waker.rs b/embassy/src/util/waker.rs index 68e45cf1..1f2d3a77 100644 --- a/embassy/src/util/waker.rs +++ b/embassy/src/util/waker.rs @@ -1,11 +1,14 @@ -use core::mem; -use core::task::Context; +use core::ptr::{self, NonNull}; use core::task::Waker; +use atomic_polyfill::{AtomicPtr, Ordering}; + +use crate::executor::raw::{task_from_waker, wake_task, Task}; + /// Utility struct to register and wake a waker. #[derive(Debug)] pub struct WakerRegistration { - waker: Option, + waker: Option>, } impl WakerRegistration { @@ -15,37 +18,61 @@ impl WakerRegistration { /// Register a waker. Overwrites the previous waker, if any. pub fn register(&mut self, w: &Waker) { + let w = unsafe { task_from_waker(w) }; match self.waker { - // Optimization: If both the old and new Wakers wake the same task, we can simply - // keep the old waker, skipping the clone. (In most executor implementations, - // cloning a waker is somewhat expensive, comparable to cloning an Arc). - Some(ref w2) if (w2.will_wake(w)) => {} - _ => { - // clone the new waker and store it - if let Some(old_waker) = mem::replace(&mut self.waker, Some(w.clone())) { - // We had a waker registered for another task. Wake it, so the other task can - // reregister itself if it's still interested. - // - // If two tasks are waiting on the same thing concurrently, this will cause them - // to wake each other in a loop fighting over this WakerRegistration. This wastes - // CPU but things will still work. - // - // If the user wants to have two tasks waiting on the same thing they should use - // a more appropriate primitive that can store multiple wakers. - old_waker.wake() - } + // Optimization: If both the old and new Wakers wake the same task, do nothing. + Some(w2) if w == w2 => {} + Some(w2) => { + // We had a waker registered for another task. Wake it, so the other task can + // reregister itself if it's still interested. + // + // If two tasks are waiting on the same thing concurrently, this will cause them + // to wake each other in a loop fighting over this WakerRegistration. This wastes + // CPU but things will still work. + // + // If the user wants to have two tasks waiting on the same thing they should use + // a more appropriate primitive that can store multiple wakers. + + unsafe { wake_task(w2) } + self.waker = Some(w); } + None => self.waker = Some(w), } } /// Wake the registered waker, if any. pub fn wake(&mut self) { if let Some(w) = self.waker.take() { - w.wake() + unsafe { wake_task(w) } + } + } +} + +pub struct AtomicWakerRegistration { + waker: AtomicPtr, +} + +impl AtomicWakerRegistration { + pub const fn new() -> Self { + Self { + waker: AtomicPtr::new(ptr::null_mut()), } } - pub fn context(&self) -> Option> { - self.waker.as_ref().map(|w| Context::from_waker(w)) + /// Register a waker. Overwrites the previous waker, if any. + pub fn register(&self, w: &Waker) { + let w = unsafe { task_from_waker(w) }; + let w2 = self.waker.swap(w.as_ptr(), Ordering::Relaxed); + if !w2.is_null() && w2 != w.as_ptr() { + unsafe { wake_task(NonNull::new_unchecked(w2)) }; + } + } + + /// Wake the registered waker, if any. + pub fn wake(&self) { + let w2 = self.waker.swap(ptr::null_mut(), Ordering::Relaxed); + if !w2.is_null() { + unsafe { wake_task(NonNull::new_unchecked(w2)) }; + } } } diff --git a/embassy/src/util/waker_agnostic.rs b/embassy/src/util/waker_agnostic.rs new file mode 100644 index 00000000..b4234c0f --- /dev/null +++ b/embassy/src/util/waker_agnostic.rs @@ -0,0 +1,87 @@ +use core::cell::Cell; +use core::mem; +use core::task::Waker; + +use cortex_m::interrupt::Mutex; + +/// Utility struct to register and wake a waker. +#[derive(Debug)] +pub struct WakerRegistration { + waker: Option, +} + +impl WakerRegistration { + pub const fn new() -> Self { + Self { waker: None } + } + + /// Register a waker. Overwrites the previous waker, if any. + pub fn register(&mut self, w: &Waker) { + match self.waker { + // Optimization: If both the old and new Wakers wake the same task, we can simply + // keep the old waker, skipping the clone. (In most executor implementations, + // cloning a waker is somewhat expensive, comparable to cloning an Arc). + Some(ref w2) if (w2.will_wake(w)) => {} + _ => { + // clone the new waker and store it + if let Some(old_waker) = mem::replace(&mut self.waker, Some(w.clone())) { + // We had a waker registered for another task. Wake it, so the other task can + // reregister itself if it's still interested. + // + // If two tasks are waiting on the same thing concurrently, this will cause them + // to wake each other in a loop fighting over this WakerRegistration. This wastes + // CPU but things will still work. + // + // If the user wants to have two tasks waiting on the same thing they should use + // a more appropriate primitive that can store multiple wakers. + old_waker.wake() + } + } + } + } + + /// Wake the registered waker, if any. + pub fn wake(&mut self) { + if let Some(w) = self.waker.take() { + w.wake() + } + } +} + +/// Utility struct to register and wake a waker. +pub struct AtomicWakerRegistration { + waker: Mutex>>, +} + +impl AtomicWakerRegistration { + pub const fn new() -> Self { + Self { + waker: Mutex::new(Cell::new(None)), + } + } + + /// Register a waker. Overwrites the previous waker, if any. + pub fn register(&mut self, w: &Waker) { + cortex_m::interrupt::free(|cs| { + let cell = self.waker.borrow(cs); + cell.set(match cell.replace(None) { + Some(w2) if (w2.will_wake(w)) => Some(w2), + Some(w2) => { + w2.wake(); + Some(w.clone()) + } + None => Some(w.clone()), + }) + }) + } + + /// Wake the registered waker, if any. + pub fn wake(&mut self) { + cortex_m::interrupt::free(|cs| { + let cell = self.waker.borrow(cs); + if let Some(w) = cell.replace(None) { + w.wake() + } + }) + } +}