From bf39822092f05dee0ab4d7efd6296e9873555da2 Mon Sep 17 00:00:00 2001 From: xoviat Date: Sat, 20 Mar 2021 11:07:16 -0500 Subject: [PATCH 01/26] consolidate ExtiPin into stm32 package --- .../src/exti.rs | 344 +++++++++++++++--- embassy-stm32/src/lib.rs | 1 + embassy-stm32f4/src/lib.rs | 3 +- embassy-stm32l0/src/exti.rs | 268 -------------- embassy-stm32l0/src/lib.rs | 4 +- 5 files changed, 296 insertions(+), 324 deletions(-) rename {embassy-stm32f4 => embassy-stm32}/src/exti.rs (56%) delete mode 100644 embassy-stm32l0/src/exti.rs diff --git a/embassy-stm32f4/src/exti.rs b/embassy-stm32/src/exti.rs similarity index 56% rename from embassy-stm32f4/src/exti.rs rename to embassy-stm32/src/exti.rs index 154ca45c..a0007945 100644 --- a/embassy-stm32f4/src/exti.rs +++ b/embassy-stm32/src/exti.rs @@ -3,35 +3,33 @@ use core::mem; use core::pin::Pin; use cortex_m; -use embassy::traits::gpio::{WaitForFallingEdge, WaitForRisingEdge}; +use crate::hal::gpio; + +use embassy::traits::gpio::{ + WaitForAnyEdge, WaitForFallingEdge, WaitForHigh, WaitForLow, WaitForRisingEdge, +}; use embassy::util::InterruptFuture; -use crate::hal::gpio; -use crate::hal::gpio::Edge; -use crate::hal::syscfg::SysCfg; -use crate::pac::EXTI; use embedded_hal::digital::v2 as digital; use crate::interrupt; -pub struct ExtiPin { +pub struct ExtiPin { pin: T, interrupt: T::Interrupt, } -impl ExtiPin { +impl ExtiPin { pub fn new(mut pin: T, interrupt: T::Interrupt) -> Self { - let mut syscfg: SysCfg = unsafe { mem::transmute(()) }; - cortex_m::interrupt::free(|_| { - pin.make_interrupt_source(&mut syscfg); + pin.make_source(); }); Self { pin, interrupt } } } -impl digital::OutputPin for ExtiPin { +impl digital::OutputPin for ExtiPin { type Error = T::Error; fn set_low(&mut self) -> Result<(), Self::Error> { @@ -43,9 +41,7 @@ impl digital::OutputPin f } } -impl digital::StatefulOutputPin - for ExtiPin -{ +impl digital::StatefulOutputPin for ExtiPin { fn is_set_low(&self) -> Result { self.pin.is_set_low() } @@ -55,9 +51,7 @@ impl digital::Sta } } -impl digital::ToggleableOutputPin - for ExtiPin -{ +impl digital::ToggleableOutputPin for ExtiPin { type Error = T::Error; fn toggle(&mut self) -> Result<(), Self::Error> { @@ -65,7 +59,7 @@ impl digital::T } } -impl digital::InputPin for ExtiPin { +impl digital::InputPin for ExtiPin { type Error = T::Error; fn is_high(&self) -> Result { @@ -77,6 +71,73 @@ impl digital::InputPin for } } +impl ExtiPin { + fn wait_for_state<'a>(self: Pin<&'a mut Self>, state: bool) -> impl Future + 'a { + let s = unsafe { self.get_unchecked_mut() }; + + s.pin.clear_pending_bit(); + async move { + let fut = InterruptFuture::new(&mut s.interrupt); + let pin = &mut s.pin; + cortex_m::interrupt::free(|_| { + pin.trigger_edge(if state { + EdgeOption::Rising + } else { + EdgeOption::Falling + }); + }); + + if (state && s.pin.is_high().unwrap_or(false)) + || (!state && s.pin.is_low().unwrap_or(false)) + { + return; + } + + fut.await; + + s.pin.clear_pending_bit(); + } + } +} + +impl ExtiPin { + fn wait_for_edge<'a>( + self: Pin<&'a mut Self>, + state: EdgeOption, + ) -> impl Future + 'a { + let s = unsafe { self.get_unchecked_mut() }; + + s.pin.clear_pending_bit(); + async move { + let fut = InterruptFuture::new(&mut s.interrupt); + let pin = &mut s.pin; + cortex_m::interrupt::free(|_| { + pin.trigger_edge(state); + }); + + fut.await; + + s.pin.clear_pending_bit(); + } + } +} + +impl WaitForHigh for ExtiPin { + type Future<'a> = impl Future + 'a; + + fn wait_for_high<'a>(self: Pin<&'a mut Self>) -> Self::Future<'a> { + self.wait_for_state(true) + } +} + +impl WaitForLow for ExtiPin { + type Future<'a> = impl Future + 'a; + + fn wait_for_low<'a>(self: Pin<&'a mut Self>) -> Self::Future<'a> { + self.wait_for_state(false) + } +} + /* Irq Handler Description EXTI0_IRQn EXTI0_IRQHandler Handler for pins connected to line 0 @@ -88,49 +149,27 @@ impl digital::InputPin for EXTI15_10_IRQn EXTI15_10_IRQHandler Handler for pins connected to line 10 to 15 */ -impl WaitForRisingEdge for ExtiPin { +impl WaitForRisingEdge for ExtiPin { type Future<'a> = impl Future + 'a; fn wait_for_rising_edge<'a>(self: Pin<&'a mut Self>) -> Self::Future<'a> { - let s = unsafe { self.get_unchecked_mut() }; - - s.pin.clear_interrupt_pending_bit(); - async move { - let fut = InterruptFuture::new(&mut s.interrupt); - let pin = &mut s.pin; - cortex_m::interrupt::free(|_| { - let mut exti: EXTI = unsafe { mem::transmute(()) }; - - pin.trigger_on_edge(&mut exti, Edge::RISING); - pin.enable_interrupt(&mut exti); - }); - fut.await; - - s.pin.clear_interrupt_pending_bit(); - } + self.wait_for_edge(EdgeOption::Rising) } } -impl WaitForFallingEdge for ExtiPin { +impl WaitForFallingEdge for ExtiPin { type Future<'a> = impl Future + 'a; fn wait_for_falling_edge<'a>(self: Pin<&'a mut Self>) -> Self::Future<'a> { - let s = unsafe { self.get_unchecked_mut() }; + self.wait_for_edge(EdgeOption::Falling) + } +} - s.pin.clear_interrupt_pending_bit(); - async move { - let fut = InterruptFuture::new(&mut s.interrupt); - let pin = &mut s.pin; - cortex_m::interrupt::free(|_| { - let mut exti: EXTI = unsafe { mem::transmute(()) }; +impl WaitForAnyEdge for ExtiPin { + type Future<'a> = impl Future + 'a; - pin.trigger_on_edge(&mut exti, Edge::FALLING); - pin.enable_interrupt(&mut exti); - }); - fut.await; - - s.pin.clear_interrupt_pending_bit(); - } + fn wait_for_any_edge<'a>(self: Pin<&'a mut Self>) -> Self::Future<'a> { + self.wait_for_edge(EdgeOption::RisingFalling) } } @@ -138,10 +177,23 @@ mod private { pub trait Sealed {} } +#[derive(Copy, Clone)] +pub enum EdgeOption { + Rising, + Falling, + RisingFalling, +} + pub trait WithInterrupt: private::Sealed { type Interrupt: interrupt::Interrupt; } +pub trait Instance: WithInterrupt { + fn make_source(&mut self); + fn clear_pending_bit(&mut self); + fn trigger_edge(&mut self, edge: EdgeOption); +} + macro_rules! exti { ($set:ident, [ $($INT:ident => $pin:ident,)+ @@ -151,8 +203,90 @@ macro_rules! exti { impl WithInterrupt for gpio::$set::$pin { type Interrupt = interrupt::$INT; } - )+ + #[cfg(any( + feature = "stm32f401", + feature = "stm32f405", + feature = "stm32f407", + feature = "stm32f410", + feature = "stm32f411", + feature = "stm32f412", + feature = "stm32f413", + feature = "stm32f415", + feature = "stm32f417", + feature = "stm32f423", + feature = "stm32f427", + feature = "stm32f429", + feature = "stm32f437", + feature = "stm32f439", + feature = "stm32f446", + feature = "stm32f469", + feature = "stm32f479", + ))] + impl Instance for gpio::$set::$pin> { + fn make_source(&mut self) { + use crate::hal::{gpio::Edge, gpio::ExtiPin, syscfg::SysCfg}; + use crate::pac::EXTI; + let mut syscfg: SysCfg = unsafe { mem::transmute(()) }; + self.make_interrupt_source(&mut syscfg); + } + + fn clear_pending_bit(&mut self) { + use crate::hal::{gpio::Edge, gpio::ExtiPin, syscfg::SysCfg}; + + self.clear_interrupt_pending_bit(); + } + + fn trigger_edge(&mut self, edge: EdgeOption) { + use crate::hal::{gpio::Edge, gpio::ExtiPin, syscfg::SysCfg}; + use crate::pac::EXTI; + let mut exti: EXTI = unsafe { mem::transmute(()) }; + let edge = match edge { + EdgeOption::Falling => Edge::FALLING, + EdgeOption::Rising => Edge::RISING, + EdgeOption::RisingFalling => Edge::RISING_FALLING, + }; + self.trigger_on_edge(&mut exti, edge); + self.enable_interrupt(&mut exti); + } + } + + #[cfg(any(feature = "stm32l0x1", feature = "stm32l0x2", feature = "stm32l0x3",))] + impl Instance for gpio::$set::$pin { + fn make_source(&mut self) {} + + fn clear_pending_bit(&mut self) { + use crate::hal::{ + exti::{Exti, ExtiLine, GpioLine, TriggerEdge}, + syscfg::SYSCFG, + }; + + Exti::unpend(GpioLine::from_raw_line(self.pin_number()).unwrap()); + } + + fn trigger_edge(&mut self, edge: EdgeOption) { + use crate::hal::{ + exti::{Exti, ExtiLine, GpioLine, TriggerEdge}, + syscfg::SYSCFG, + }; + + use crate::pac::EXTI; + + let edge = match edge { + EdgeOption::Falling => TriggerEdge::Falling, + EdgeOption::Rising => TriggerEdge::Rising, + EdgeOption::RisingFalling => TriggerEdge::Both, + }; + + let exti: EXTI = unsafe { mem::transmute(()) }; + let mut exti = Exti::new(exti); + let port = self.port(); + let mut syscfg: SYSCFG = unsafe { mem::transmute(()) }; + let line = GpioLine::from_raw_line(self.pin_number()).unwrap(); + exti.listen_gpio(&mut syscfg, port, line, edge); + } + } + )+ }; } @@ -533,3 +667,111 @@ exti!(gpiok, [ EXTI9_5 => PK6, EXTI9_5 => PK7, ]); + +#[cfg(any(feature = "stm32l0x1", feature = "stm32l0x2", feature = "stm32l0x3",))] +exti!(gpioa, [ + EXTI0_1 => PA0, + EXTI0_1 => PA1, + EXTI2_3 => PA2, + EXTI2_3 => PA3, + EXTI4_15 => PA4, + EXTI4_15 => PA5, + EXTI4_15 => PA6, + EXTI4_15 => PA7, + EXTI4_15 => PA8, + EXTI4_15 => PA9, + EXTI4_15 => PA10, + EXTI4_15 => PA11, + EXTI4_15 => PA12, + EXTI4_15 => PA13, + EXTI4_15 => PA14, + EXTI4_15 => PA15, +]); + +#[cfg(any(feature = "stm32l0x1", feature = "stm32l0x2", feature = "stm32l0x3",))] +exti!(gpiob, [ + EXTI0_1 => PB0, + EXTI0_1 => PB1, + EXTI2_3 => PB2, + EXTI2_3 => PB3, + EXTI4_15 => PB4, + EXTI4_15 => PB5, + EXTI4_15 => PB6, + EXTI4_15 => PB7, + EXTI4_15 => PB8, + EXTI4_15 => PB9, + EXTI4_15 => PB10, + EXTI4_15 => PB11, + EXTI4_15 => PB12, + EXTI4_15 => PB13, + EXTI4_15 => PB14, + EXTI4_15 => PB15, +]); + +#[cfg(any(feature = "stm32l0x1", feature = "stm32l0x2", feature = "stm32l0x3",))] +exti!(gpioc, [ + EXTI0_1 => PC0, + EXTI0_1 => PC1, + EXTI2_3 => PC2, + EXTI2_3 => PC3, + EXTI4_15 => PC4, + EXTI4_15 => PC5, + EXTI4_15 => PC6, + EXTI4_15 => PC7, + EXTI4_15 => PC8, + EXTI4_15 => PC9, + EXTI4_15 => PC10, + EXTI4_15 => PC11, + EXTI4_15 => PC12, + EXTI4_15 => PC13, + EXTI4_15 => PC14, + EXTI4_15 => PC15, +]); + +#[cfg(any(feature = "stm32l0x1", feature = "stm32l0x2", feature = "stm32l0x3",))] +exti!(gpiod, [ + EXTI0_1 => PD0, + EXTI0_1 => PD1, + EXTI2_3 => PD2, + EXTI2_3 => PD3, + EXTI4_15 => PD4, + EXTI4_15 => PD5, + EXTI4_15 => PD6, + EXTI4_15 => PD7, + EXTI4_15 => PD8, + EXTI4_15 => PD9, + EXTI4_15 => PD10, + EXTI4_15 => PD11, + EXTI4_15 => PD12, + EXTI4_15 => PD13, + EXTI4_15 => PD14, + EXTI4_15 => PD15, +]); + +#[cfg(any(feature = "stm32l0x1", feature = "stm32l0x2", feature = "stm32l0x3",))] +exti!(gpioe, [ + EXTI0_1 => PE0, + EXTI0_1 => PE1, + EXTI2_3 => PE2, + EXTI2_3 => PE3, + EXTI4_15 => PE4, + EXTI4_15 => PE5, + EXTI4_15 => PE6, + EXTI4_15 => PE7, + EXTI4_15 => PE8, + EXTI4_15 => PE9, + EXTI4_15 => PE10, + EXTI4_15 => PE11, + EXTI4_15 => PE12, + EXTI4_15 => PE13, + EXTI4_15 => PE14, + EXTI4_15 => PE15, +]); + +#[cfg(any(feature = "stm32l0x1", feature = "stm32l0x2", feature = "stm32l0x3",))] +exti!(gpioh, [ + EXTI0_1 => PH0, + EXTI0_1 => PH1, + EXTI4_15 => PH9, + EXTI4_15 => PH10, +]); diff --git a/embassy-stm32/src/lib.rs b/embassy-stm32/src/lib.rs index a1f40b2c..954067f3 100644 --- a/embassy-stm32/src/lib.rs +++ b/embassy-stm32/src/lib.rs @@ -32,4 +32,5 @@ pub use {stm32l0xx_hal as hal, stm32l0xx_hal::pac}; pub mod fmt; +pub mod exti; pub mod interrupt; diff --git a/embassy-stm32f4/src/lib.rs b/embassy-stm32f4/src/lib.rs index 0d490525..bdd4d0fd 100644 --- a/embassy-stm32f4/src/lib.rs +++ b/embassy-stm32f4/src/lib.rs @@ -307,11 +307,10 @@ compile_error!( "Multile chip features activated. You must activate exactly one of the following features: " ); -pub use embassy_stm32::{fmt, hal, interrupt, pac}; +pub use embassy_stm32::{exti, fmt, hal, interrupt, pac}; #[cfg(not(any(feature = "stm32f401", feature = "stm32f410", feature = "stm32f411",)))] pub mod can; -pub mod exti; #[cfg(not(feature = "stm32f410"))] pub mod qei; pub mod rtc; diff --git a/embassy-stm32l0/src/exti.rs b/embassy-stm32l0/src/exti.rs deleted file mode 100644 index f50c3ae8..00000000 --- a/embassy-stm32l0/src/exti.rs +++ /dev/null @@ -1,268 +0,0 @@ -use core::future::Future; -use core::mem; -use core::pin::Pin; - -use embassy::traits::gpio::{ - WaitForAnyEdge, WaitForFallingEdge, WaitForHigh, WaitForLow, WaitForRisingEdge, -}; -use embassy::util::InterruptFuture; - -use crate::hal::{ - exti::{Exti, ExtiLine, GpioLine, TriggerEdge}, - gpio, - syscfg::SYSCFG, -}; -use crate::interrupt; -use crate::pac::EXTI; -use embedded_hal::digital::v2::InputPin; - -pub struct ExtiPin { - pin: T, - interrupt: T::Interrupt, -} - -impl ExtiPin { - pub fn new(pin: T, interrupt: T::Interrupt) -> ExtiPin { - ExtiPin { pin, interrupt } - } - - fn wait_for_edge<'a>( - self: Pin<&'a mut Self>, - edge: TriggerEdge, - ) -> impl Future + 'a { - let line = self.pin.line(); - let s = unsafe { self.get_unchecked_mut() }; - - Exti::unpend(line); - - async move { - let exti: EXTI = unsafe { mem::transmute(()) }; - let mut exti = Exti::new(exti); - - let fut = InterruptFuture::new(&mut s.interrupt); - - let port = s.pin.port(); - cortex_m::interrupt::free(|_| { - let mut syscfg: SYSCFG = unsafe { mem::transmute(()) }; - exti.listen_gpio(&mut syscfg, port, line, edge); - }); - - fut.await; - - Exti::unpend(line); - } - } -} - -impl ExtiPin { - fn wait_for_state<'a>(self: Pin<&'a mut Self>, state: bool) -> impl Future + 'a { - let line = self.pin.line(); - let s = unsafe { self.get_unchecked_mut() }; - - Exti::unpend(line); - - async move { - let exti: EXTI = unsafe { mem::transmute(()) }; - let mut exti = Exti::new(exti); - - let fut = InterruptFuture::new(&mut s.interrupt); - - let port = s.pin.port(); - cortex_m::interrupt::free(|_| { - let mut syscfg: SYSCFG = unsafe { mem::transmute(()) }; - let edge = if state { - TriggerEdge::Rising - } else { - TriggerEdge::Falling - }; - exti.listen_gpio(&mut syscfg, port, line, edge); - }); - - let pin_has_state = if state { - s.pin.is_high() - } else { - s.pin.is_low() - } - .unwrap_or(false); - if pin_has_state { - return (); - } - - fut.await; - - Exti::unpend(line); - } - } -} - -impl WaitForRisingEdge for ExtiPin { - type Future<'a> = impl Future + 'a; - - fn wait_for_rising_edge<'a>(self: Pin<&'a mut Self>) -> Self::Future<'a> { - self.wait_for_edge(TriggerEdge::Rising) - } -} - -impl WaitForFallingEdge for ExtiPin { - type Future<'a> = impl Future + 'a; - - fn wait_for_falling_edge<'a>(self: Pin<&'a mut Self>) -> Self::Future<'a> { - self.wait_for_edge(TriggerEdge::Falling) - } -} - -impl WaitForAnyEdge for ExtiPin { - type Future<'a> = impl Future + 'a; - - fn wait_for_any_edge<'a>(self: Pin<&'a mut Self>) -> Self::Future<'a> { - self.wait_for_edge(TriggerEdge::Both) - } -} - -impl WaitForHigh for ExtiPin { - type Future<'a> = impl Future + 'a; - - fn wait_for_high<'a>(self: Pin<&'a mut Self>) -> Self::Future<'a> { - self.wait_for_state(true) - } -} - -impl WaitForLow for ExtiPin { - type Future<'a> = impl Future + 'a; - - fn wait_for_low<'a>(self: Pin<&'a mut Self>) -> Self::Future<'a> { - self.wait_for_state(false) - } -} - -mod private { - pub trait Sealed {} -} - -pub trait PinWithInterrupt: private::Sealed { - type Interrupt: interrupt::Interrupt; - fn port(&self) -> gpio::Port; - fn line(&self) -> GpioLine; -} - -macro_rules! exti { - ($set:ident, [ - $($INT:ident => $pin:ident,)+ - ]) => { - $( - impl private::Sealed for gpio::$set::$pin {} - impl PinWithInterrupt for gpio::$set::$pin { - type Interrupt = interrupt::$INT; - fn port(&self) -> gpio::Port { - self.port() - } - fn line(&self) -> GpioLine { - GpioLine::from_raw_line(self.pin_number()).unwrap() - } - } - )+ - - }; -} - -exti!(gpioa, [ - EXTI0_1 => PA0, - EXTI0_1 => PA1, - EXTI2_3 => PA2, - EXTI2_3 => PA3, - EXTI4_15 => PA4, - EXTI4_15 => PA5, - EXTI4_15 => PA6, - EXTI4_15 => PA7, - EXTI4_15 => PA8, - EXTI4_15 => PA9, - EXTI4_15 => PA10, - EXTI4_15 => PA11, - EXTI4_15 => PA12, - EXTI4_15 => PA13, - EXTI4_15 => PA14, - EXTI4_15 => PA15, -]); - -exti!(gpiob, [ - EXTI0_1 => PB0, - EXTI0_1 => PB1, - EXTI2_3 => PB2, - EXTI2_3 => PB3, - EXTI4_15 => PB4, - EXTI4_15 => PB5, - EXTI4_15 => PB6, - EXTI4_15 => PB7, - EXTI4_15 => PB8, - EXTI4_15 => PB9, - EXTI4_15 => PB10, - EXTI4_15 => PB11, - EXTI4_15 => PB12, - EXTI4_15 => PB13, - EXTI4_15 => PB14, - EXTI4_15 => PB15, -]); - -exti!(gpioc, [ - EXTI0_1 => PC0, - EXTI0_1 => PC1, - EXTI2_3 => PC2, - EXTI2_3 => PC3, - EXTI4_15 => PC4, - EXTI4_15 => PC5, - EXTI4_15 => PC6, - EXTI4_15 => PC7, - EXTI4_15 => PC8, - EXTI4_15 => PC9, - EXTI4_15 => PC10, - EXTI4_15 => PC11, - EXTI4_15 => PC12, - EXTI4_15 => PC13, - EXTI4_15 => PC14, - EXTI4_15 => PC15, -]); - -exti!(gpiod, [ - EXTI0_1 => PD0, - EXTI0_1 => PD1, - EXTI2_3 => PD2, - EXTI2_3 => PD3, - EXTI4_15 => PD4, - EXTI4_15 => PD5, - EXTI4_15 => PD6, - EXTI4_15 => PD7, - EXTI4_15 => PD8, - EXTI4_15 => PD9, - EXTI4_15 => PD10, - EXTI4_15 => PD11, - EXTI4_15 => PD12, - EXTI4_15 => PD13, - EXTI4_15 => PD14, - EXTI4_15 => PD15, -]); - -exti!(gpioe, [ - EXTI0_1 => PE0, - EXTI0_1 => PE1, - EXTI2_3 => PE2, - EXTI2_3 => PE3, - EXTI4_15 => PE4, - EXTI4_15 => PE5, - EXTI4_15 => PE6, - EXTI4_15 => PE7, - EXTI4_15 => PE8, - EXTI4_15 => PE9, - EXTI4_15 => PE10, - EXTI4_15 => PE11, - EXTI4_15 => PE12, - EXTI4_15 => PE13, - EXTI4_15 => PE14, - EXTI4_15 => PE15, -]); - -exti!(gpioh, [ - EXTI0_1 => PH0, - EXTI0_1 => PH1, - EXTI4_15 => PH9, - EXTI4_15 => PH10, -]); diff --git a/embassy-stm32l0/src/lib.rs b/embassy-stm32l0/src/lib.rs index e80fe6cb..a684c4e3 100644 --- a/embassy-stm32l0/src/lib.rs +++ b/embassy-stm32l0/src/lib.rs @@ -19,6 +19,4 @@ compile_error!( "Multile chip features activated. You must activate exactly one of the following features: " ); -pub use embassy_stm32::{fmt, hal, interrupt, pac}; - -pub mod exti; +pub use embassy_stm32::{exti, fmt, hal, interrupt, pac}; From be58de039b6870fcb581f83109b558f28560677f Mon Sep 17 00:00:00 2001 From: xoviat Date: Sat, 20 Mar 2021 18:13:21 -0500 Subject: [PATCH 02/26] ci: add stm32f446 --- .github/workflows/rust.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index 9b3d3b29..19499632 100644 --- a/.github/workflows/rust.yml +++ b/.github/workflows/rust.yml @@ -52,6 +52,9 @@ jobs: - package: embassy-stm32f4 target: thumbv7em-none-eabi features: stm32f405 + - package: embassy-stm32f4 + target: thumbv7em-none-eabi + features: stm32f446 - package: embassy-stm32f4 target: thumbv7em-none-eabi features: stm32f405,defmt From b5f80787fe6a8a78756585c215be58ac1d4c19d0 Mon Sep 17 00:00:00 2001 From: xoviat Date: Sat, 20 Mar 2021 18:23:20 -0500 Subject: [PATCH 03/26] stm32: remove SDIO interrupt --- embassy-stm32/src/interrupt.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/embassy-stm32/src/interrupt.rs b/embassy-stm32/src/interrupt.rs index 5ad7ef8e..262bc3d9 100644 --- a/embassy-stm32/src/interrupt.rs +++ b/embassy-stm32/src/interrupt.rs @@ -889,7 +889,7 @@ mod irqs { declare!(TIM8_CC); declare!(DMA1_STREAM7); declare!(FMC); - declare!(SDIO); + // declare!(SDIO); declare!(TIM5); declare!(SPI3); declare!(UART4); From 38cd2672d3eb25f682498689c2ea4cb247513c80 Mon Sep 17 00:00:00 2001 From: xoviat Date: Sat, 20 Mar 2021 18:27:24 -0500 Subject: [PATCH 04/26] stm32f4: remove usart7, 8 --- embassy-stm32f4/src/serial.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/embassy-stm32f4/src/serial.rs b/embassy-stm32f4/src/serial.rs index 87d0caaa..381e0f5e 100644 --- a/embassy-stm32f4/src/serial.rs +++ b/embassy-stm32f4/src/serial.rs @@ -278,6 +278,6 @@ usart! { UART4 => (UART4), UART5 => (UART5), - UART7 => (UART7), - UART8 => (UART8), +// UART7 => (UART7), +// UART8 => (UART8), } From d1ac703830bd9e69f9dddfd8b36779da2545ea4f Mon Sep 17 00:00:00 2001 From: xoviat Date: Sat, 20 Mar 2021 23:49:49 -0500 Subject: [PATCH 05/26] .vscode: add target --- .vscode/settings.json | 1 + 1 file changed, 1 insertion(+) diff --git a/.vscode/settings.json b/.vscode/settings.json index 8a292c0b..58ef9d8a 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,6 +1,7 @@ { "rust-analyzer.assist.importMergeBehavior": "last", "editor.formatOnSave": true, + "rust.target": "thumbv7em-none-eabihf", "rust-analyzer.cargo.allFeatures": false, "rust-analyzer.checkOnSave.allFeatures": false, "rust-analyzer.checkOnSave.allTargets": false, From 556942a6bf0933921f33bfcb0155fbabf33b59c6 Mon Sep 17 00:00:00 2001 From: xoviat Date: Sat, 20 Mar 2021 23:51:24 -0500 Subject: [PATCH 06/26] stm32f4: implement idle on serial --- embassy-stm32f4/Cargo.toml | 1 + embassy-stm32f4/src/serial.rs | 42 +++++++++++++++++++++++++---------- 2 files changed, 31 insertions(+), 12 deletions(-) diff --git a/embassy-stm32f4/Cargo.toml b/embassy-stm32f4/Cargo.toml index ae3273d6..c132d7c9 100644 --- a/embassy-stm32f4/Cargo.toml +++ b/embassy-stm32f4/Cargo.toml @@ -36,6 +36,7 @@ defmt = { version = "0.2.0", optional = true } log = { version = "0.4.11", optional = true } cortex-m-rt = "0.6.13" cortex-m = "0.7.1" +futures = { version = "0.3.5", default-features = false, features = ["async-await"] } embedded-hal = { version = "0.2.4" } embedded-dma = { version = "0.1.2" } stm32f4xx-hal = { version = "0.8.3", features = ["rt", "can"], git = "https://github.com/stm32-rs/stm32f4xx-hal.git"} diff --git a/embassy-stm32f4/src/serial.rs b/embassy-stm32f4/src/serial.rs index 381e0f5e..bed89288 100644 --- a/embassy-stm32f4/src/serial.rs +++ b/embassy-stm32f4/src/serial.rs @@ -7,6 +7,8 @@ use core::future::Future; use core::marker::PhantomData; +use futures::{select_biased, FutureExt}; + use embassy::interrupt::Interrupt; use embassy::traits::uart::{Error, Uart}; use embassy::util::InterruptFuture; @@ -19,7 +21,7 @@ use crate::hal::{ rcc::Clocks, serial, serial::config::{Config as SerialConfig, DmaConfig as SerialDmaConfig}, - serial::{Event as SerialEvent, Pins, Serial as HalSerial}, + serial::{Event as SerialEvent, Pins}, }; use crate::interrupt; use crate::pac; @@ -29,14 +31,14 @@ pub struct Serial< USART: PeriAddress + WithInterrupt, TSTREAM: Stream + WithInterrupt, RSTREAM: Stream + WithInterrupt, - CHANNEL: dma::traits::Channel, + CHANNEL: Channel, > { tx_stream: Option, rx_stream: Option, usart: Option, tx_int: TSTREAM::Interrupt, rx_int: RSTREAM::Interrupt, - _usart_int: USART::Interrupt, + usart_int: USART::Interrupt, channel: PhantomData, } @@ -68,12 +70,10 @@ where PINS: Pins, { config.dma = SerialDmaConfig::TxRx; - let mut serial = HalSerial::new(usart, pins, config, clocks).unwrap(); - serial.listen(SerialEvent::Idle); - // serial.listen(SerialEvent::Txe); - - let (usart, _) = serial.release(); + let (usart, _) = serial::Serial::new(usart, pins, config, clocks) + .unwrap() + .release(); let (tx_stream, rx_stream) = streams; @@ -83,8 +83,8 @@ where usart: Some(usart), tx_int: tx_int, rx_int: rx_int, - _usart_int: usart_int, - channel: core::marker::PhantomData, + usart_int: usart_int, + channel: PhantomData, } } } @@ -127,10 +127,10 @@ where let fut = InterruptFuture::new(&mut self.tx_int); tx_transfer.start(|_usart| {}); - fut.await; let (tx_stream, usart, _buf, _) = tx_transfer.free(); + self.tx_stream.replace(tx_stream); self.usart.replace(usart); @@ -151,6 +151,15 @@ where let usart = self.usart.take().unwrap(); async move { + unsafe { + /* __HAL_UART_ENABLE_IT(&uart->UartHandle, UART_IT_IDLE); */ + (*USART::ptr()).cr1.modify(|_, w| w.idleie().set_bit()); + + /* __HAL_UART_CLEAR_IDLEFLAG(&uart->UartHandle); */ + (*USART::ptr()).sr.read(); + (*USART::ptr()).dr.read(); + }; + let mut rx_transfer = Transfer::init( rx_stream, usart, @@ -163,11 +172,20 @@ where ); let fut = InterruptFuture::new(&mut self.rx_int); + let fut_idle = InterruptFuture::new(&mut self.usart_int); rx_transfer.start(|_usart| {}); - fut.await; + + select_biased! { + () = fut.fuse() => {}, + () = fut_idle.fuse() => {}, + } let (rx_stream, usart, _, _) = rx_transfer.free(); + + unsafe { + (*USART::ptr()).cr1.modify(|_, w| w.idleie().clear_bit()); + } self.rx_stream.replace(rx_stream); self.usart.replace(usart); From cf1323fb67b2841a99bb497d7947ffa772ff354d Mon Sep 17 00:00:00 2001 From: Joshua Salzedo Date: Sun, 21 Mar 2021 16:45:24 -0700 Subject: [PATCH 07/26] Add module-level documentation for embassy::time --- embassy/src/time/mod.rs | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/embassy/src/time/mod.rs b/embassy/src/time/mod.rs index b45a608d..b19ab6db 100644 --- a/embassy/src/time/mod.rs +++ b/embassy/src/time/mod.rs @@ -1,3 +1,6 @@ +/// Time abstractions +/// To use these abstractions, first call `set_clock` with an instance of an monotonic `Clock`. +/// mod duration; mod instant; mod traits; @@ -14,10 +17,16 @@ pub const TICKS_PER_SECOND: u64 = 32768; static mut CLOCK: Option<&'static dyn Clock> = None; +/// Sets the clock used for the timing abstractions +/// +/// Safety: Sets a mutable global. pub unsafe fn set_clock(clock: &'static dyn Clock) { CLOCK = Some(clock); } +/// Return the current timestamp in ticks. +/// This is guaranteed to be monotonic, i.e. a call to now() will always return +/// a greater or equal value than earler calls. pub(crate) fn now() -> u64 { unsafe { unwrap!(CLOCK, "No clock set").now() } } From d453b9dd95589d95239ea3f86f15da647d253aef Mon Sep 17 00:00:00 2001 From: Joshua Salzedo Date: Sun, 21 Mar 2021 16:45:48 -0700 Subject: [PATCH 08/26] Add Struct/impl documentation for embassy::time::Duration --- embassy/src/time/duration.rs | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/embassy/src/time/duration.rs b/embassy/src/time/duration.rs index e04afa18..c9600774 100644 --- a/embassy/src/time/duration.rs +++ b/embassy/src/time/duration.rs @@ -26,16 +26,18 @@ impl Duration { self.ticks * 1_000_000 / TICKS_PER_SECOND } + /// Creates a duration from the specified number of clock ticks pub const fn from_ticks(ticks: u64) -> Duration { Duration { ticks } } + /// Creates a duration from the specified number of seconds pub const fn from_secs(secs: u64) -> Duration { Duration { ticks: secs * TICKS_PER_SECOND, } } - + /// Creates a duration from the specified number of milliseconds pub const fn from_millis(millis: u64) -> Duration { Duration { ticks: millis * TICKS_PER_SECOND / 1000, @@ -45,30 +47,34 @@ impl Duration { /* NOTE: us delays may not be as accurate */ + /// Creates a duration from the specified number of microseconds + /// NOTE: Delays this small may be inaccurate. pub const fn from_micros(micros: u64) -> Duration { Duration { ticks: micros * TICKS_PER_SECOND / 1_000_000, } } + /// Adds one Duration to another, returning a new Duration or None in the event of an overflow. pub fn checked_add(self, rhs: Duration) -> Option { self.ticks .checked_add(rhs.ticks) .map(|ticks| Duration { ticks }) } - + /// Subtracts one Duration to another, returning a new Duration or None in the event of an overflow. pub fn checked_sub(self, rhs: Duration) -> Option { self.ticks .checked_sub(rhs.ticks) .map(|ticks| Duration { ticks }) } + /// Multiplies one Duration to another, returning a new Duration or None in the event of an overflow. pub fn checked_mul(self, rhs: u32) -> Option { self.ticks .checked_mul(rhs as _) .map(|ticks| Duration { ticks }) } - + /// Divides one Duration against another, returning a new Duration or None in the event of an overflow. pub fn checked_div(self, rhs: u32) -> Option { self.ticks .checked_div(rhs as _) From dcdd768e0360683db747906e4780e5470d1961a1 Mon Sep 17 00:00:00 2001 From: Joshua Salzedo Date: Sun, 21 Mar 2021 16:45:53 -0700 Subject: [PATCH 09/26] Add Struct/impl documentation for embassy::time::Instant --- embassy/src/time/instant.rs | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/embassy/src/time/instant.rs b/embassy/src/time/instant.rs index 06ab84c7..9a544fc4 100644 --- a/embassy/src/time/instant.rs +++ b/embassy/src/time/instant.rs @@ -6,6 +6,7 @@ use super::{now, Duration}; #[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] +/// An Instant in time, based on the MCU's clock ticks since startup. pub struct Instant { ticks: u64, } @@ -14,44 +15,54 @@ impl Instant { pub const MIN: Instant = Instant { ticks: u64::MIN }; pub const MAX: Instant = Instant { ticks: u64::MAX }; + /// Returns an Instant representing the current time. pub fn now() -> Instant { Instant { ticks: now() } } + /// Instant as clock ticks since MCU start. pub const fn from_ticks(ticks: u64) -> Self { Self { ticks } } + /// Instant as milliseconds since MCU start. pub const fn from_millis(millis: u64) -> Self { Self { ticks: millis * TICKS_PER_SECOND as u64 / 1000, } } - + /// Instant representing seconds since MCU start. pub const fn from_secs(seconds: u64) -> Self { Self { ticks: seconds * TICKS_PER_SECOND as u64, } } + /// Instant as ticks since MCU start. + pub const fn as_ticks(&self) -> u64 { self.ticks } + /// Instant as seconds since MCU start. pub const fn as_secs(&self) -> u64 { self.ticks / TICKS_PER_SECOND as u64 } + /// Instant as miliseconds since MCU start. pub const fn as_millis(&self) -> u64 { self.ticks * 1000 / TICKS_PER_SECOND as u64 } + /// Duration between this Instant and another Instant + /// Panics on over/underflow. pub fn duration_since(&self, earlier: Instant) -> Duration { Duration { ticks: self.ticks.checked_sub(earlier.ticks).unwrap(), } } + /// Duration between this Instant and another Instant pub fn checked_duration_since(&self, earlier: Instant) -> Option { if self.ticks < earlier.ticks { None @@ -62,6 +73,8 @@ impl Instant { } } + /// Returns the duration since the "earlier" Instant. + /// If the "earlier" instant is in the future, the duration is set to zero. pub fn saturating_duration_since(&self, earlier: Instant) -> Duration { Duration { ticks: if self.ticks < earlier.ticks { @@ -72,6 +85,7 @@ impl Instant { } } + /// Duration elapsed since this Instant. pub fn elapsed(&self) -> Duration { Instant::now() - *self } From f8d63279efed5dfd649118ac89903a8bcb01a09d Mon Sep 17 00:00:00 2001 From: Joshua Salzedo Date: Sun, 21 Mar 2021 17:01:13 -0700 Subject: [PATCH 10/26] Re-add erroneously removed newlines --- embassy/src/time/duration.rs | 5 ++++- embassy/src/time/instant.rs | 1 + 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/embassy/src/time/duration.rs b/embassy/src/time/duration.rs index c9600774..474d0621 100644 --- a/embassy/src/time/duration.rs +++ b/embassy/src/time/duration.rs @@ -37,6 +37,7 @@ impl Duration { ticks: secs * TICKS_PER_SECOND, } } + /// Creates a duration from the specified number of milliseconds pub const fn from_millis(millis: u64) -> Duration { Duration { @@ -61,19 +62,21 @@ impl Duration { .checked_add(rhs.ticks) .map(|ticks| Duration { ticks }) } + /// Subtracts one Duration to another, returning a new Duration or None in the event of an overflow. pub fn checked_sub(self, rhs: Duration) -> Option { self.ticks .checked_sub(rhs.ticks) .map(|ticks| Duration { ticks }) } - /// Multiplies one Duration to another, returning a new Duration or None in the event of an overflow. + /// Multiplies one Duration to another, returning a new Duration or None in the event of an overflow. pub fn checked_mul(self, rhs: u32) -> Option { self.ticks .checked_mul(rhs as _) .map(|ticks| Duration { ticks }) } + /// Divides one Duration against another, returning a new Duration or None in the event of an overflow. pub fn checked_div(self, rhs: u32) -> Option { self.ticks diff --git a/embassy/src/time/instant.rs b/embassy/src/time/instant.rs index 9a544fc4..61a61def 100644 --- a/embassy/src/time/instant.rs +++ b/embassy/src/time/instant.rs @@ -31,6 +31,7 @@ impl Instant { ticks: millis * TICKS_PER_SECOND as u64 / 1000, } } + /// Instant representing seconds since MCU start. pub const fn from_secs(seconds: u64) -> Self { Self { From e363607d70ee088d2da43ec27d4a6257069a6b06 Mon Sep 17 00:00:00 2001 From: Joshua Salzedo Date: Sun, 21 Mar 2021 17:01:52 -0700 Subject: [PATCH 11/26] Added doc to the embassy::time::Duration struct --- embassy/src/time/duration.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/embassy/src/time/duration.rs b/embassy/src/time/duration.rs index 474d0621..db0e2cf2 100644 --- a/embassy/src/time/duration.rs +++ b/embassy/src/time/duration.rs @@ -5,6 +5,7 @@ use super::TICKS_PER_SECOND; #[derive(Debug, Default, Copy, Clone, PartialEq, Eq, PartialOrd, Ord)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] +/// Represents the difference between [Instant::now()](struct.Instant.html#method.now) and some other Instant pub struct Duration { pub(crate) ticks: u64, } From 10f14747c35f5deab8240c4d792439d72f807bf9 Mon Sep 17 00:00:00 2001 From: Joshua Salzedo Date: Sun, 21 Mar 2021 17:05:22 -0700 Subject: [PATCH 12/26] Fix module-level docstring --- embassy/src/time/mod.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/embassy/src/time/mod.rs b/embassy/src/time/mod.rs index b19ab6db..4e9b5f59 100644 --- a/embassy/src/time/mod.rs +++ b/embassy/src/time/mod.rs @@ -1,6 +1,6 @@ -/// Time abstractions -/// To use these abstractions, first call `set_clock` with an instance of an monotonic `Clock`. -/// +//! Time abstractions +//! To use these abstractions, first call `set_clock` with an instance of an [Clock](trait.Clock.html). +//! mod duration; mod instant; mod traits; From 7988b78107245b33fb8241d0eb04c7110e03f213 Mon Sep 17 00:00:00 2001 From: Joshua Salzedo Date: Sun, 21 Mar 2021 17:05:36 -0700 Subject: [PATCH 13/26] remove now redundant non-doc comment --- embassy/src/time/duration.rs | 3 --- 1 file changed, 3 deletions(-) diff --git a/embassy/src/time/duration.rs b/embassy/src/time/duration.rs index db0e2cf2..38b0c083 100644 --- a/embassy/src/time/duration.rs +++ b/embassy/src/time/duration.rs @@ -46,9 +46,6 @@ impl Duration { } } - /* - NOTE: us delays may not be as accurate - */ /// Creates a duration from the specified number of microseconds /// NOTE: Delays this small may be inaccurate. pub const fn from_micros(micros: u64) -> Duration { From 42be860446856937048efa05538e43408677186e Mon Sep 17 00:00:00 2001 From: Joshua Salzedo Date: Sun, 21 Mar 2021 17:11:30 -0700 Subject: [PATCH 14/26] Correct descriptions of Duration --- embassy/src/time/duration.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/embassy/src/time/duration.rs b/embassy/src/time/duration.rs index 38b0c083..5157450a 100644 --- a/embassy/src/time/duration.rs +++ b/embassy/src/time/duration.rs @@ -5,7 +5,7 @@ use super::TICKS_PER_SECOND; #[derive(Debug, Default, Copy, Clone, PartialEq, Eq, PartialOrd, Ord)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] -/// Represents the difference between [Instant::now()](struct.Instant.html#method.now) and some other Instant +/// Represents the difference between two [Instant](struct.Instant.html)s pub struct Duration { pub(crate) ticks: u64, } @@ -68,14 +68,14 @@ impl Duration { .map(|ticks| Duration { ticks }) } - /// Multiplies one Duration to another, returning a new Duration or None in the event of an overflow. + /// Multiplies one Duration by a scalar u32, returning a new Duration or None in the event of an overflow. pub fn checked_mul(self, rhs: u32) -> Option { self.ticks .checked_mul(rhs as _) .map(|ticks| Duration { ticks }) } - /// Divides one Duration against another, returning a new Duration or None in the event of an overflow. + /// Divides one Duration a scalar u32, returning a new Duration or None in the event of an overflow. pub fn checked_div(self, rhs: u32) -> Option { self.ticks .checked_div(rhs as _) From 8fbe83ac26185482069ee7b9049879cd9be01462 Mon Sep 17 00:00:00 2001 From: Joshua Salzedo Date: Sun, 21 Mar 2021 18:30:03 -0700 Subject: [PATCH 15/26] Added remainder of the docs for embassy::time's public interface. --- embassy-traits/src/delay.rs | 3 ++ embassy/src/executor/timer.rs | 73 +++++++++++++++++++++++++++++++++++ 2 files changed, 76 insertions(+) diff --git a/embassy-traits/src/delay.rs b/embassy-traits/src/delay.rs index b893ee4a..1e763350 100644 --- a/embassy-traits/src/delay.rs +++ b/embassy-traits/src/delay.rs @@ -4,6 +4,9 @@ use core::pin::Pin; pub trait Delay { type DelayFuture<'a>: Future + 'a; + /// Future that completes after now + millis fn delay_ms<'a>(self: Pin<&'a mut Self>, millis: u64) -> Self::DelayFuture<'a>; + + /// Future that completes after now + micros fn delay_us<'a>(self: Pin<&'a mut Self>, micros: u64) -> Self::DelayFuture<'a>; } diff --git a/embassy/src/executor/timer.rs b/embassy/src/executor/timer.rs index 256ecf66..541f96f5 100644 --- a/embassy/src/executor/timer.rs +++ b/embassy/src/executor/timer.rs @@ -7,6 +7,7 @@ use futures::Stream; use super::raw; use crate::time::{Duration, Instant}; +/// Delay abstraction using embassy's clock. pub struct Delay { _data: PhantomData, } @@ -30,12 +31,14 @@ impl crate::traits::delay::Delay for Delay { } } +/// A future that completes at a specified [Instant](struct.Instant.html). pub struct Timer { expires_at: Instant, yielded_once: bool, } impl Timer { + /// Expire at specified [Instant](struct.Instant.html) pub fn at(expires_at: Instant) -> Self { Self { expires_at, @@ -43,6 +46,26 @@ impl Timer { } } + /// Expire after specified [Duration](struct.Duration.html). + /// This can be used as a `sleep` abstraction. + /// + /// Example: + /// ``` no_run + /// # #![feature(trait_alias)] + /// # #![feature(min_type_alias_impl_trait)] + /// # #![feature(impl_trait_in_bindings)] + /// # #![feature(type_alias_impl_trait)] + /// # + /// # fn foo() {} + /// use embassy::executor::task; + /// use embassy::time::{Duration, Timer}; + /// + /// #[task] + /// async fn demo_sleep_seconds() { + /// // suspend this task for one second. + /// Timer::after(Duration::from_secs(1)).await; + /// } + /// ``` pub fn after(duration: Duration) -> Self { Self { expires_at: Instant::now() + duration, @@ -66,12 +89,62 @@ impl Future for Timer { } } +/// Asynchronous stream that yields every Duration, indefinitely. +/// +/// This stream will tick at uniform intervals, even if blocking work is performed between ticks. +/// +/// For instance, consider the following code fragment. +/// ``` no_run +/// # #![feature(trait_alias)] +/// # #![feature(min_type_alias_impl_trait)] +/// # #![feature(impl_trait_in_bindings)] +/// # #![feature(type_alias_impl_trait)] +/// # +/// use embassy::executor::task; +/// use embassy::time::{Duration, Timer}; +/// # fn foo() {} +/// +/// #[task] +/// async fn ticker_example_0() { +/// loop { +/// foo(); +/// Timer::after(Duration::from_secs(1)).await; +/// } +/// } +/// ``` +/// +/// This fragment will not call `foo` every second. +/// Instead, it will call it every second + the time it took to previously call `foo`. +/// +/// Example using ticker, which will consistently call `foo` once a second. +/// +/// ``` no_run +/// # #![feature(trait_alias)] +/// # #![feature(min_type_alias_impl_trait)] +/// # #![feature(impl_trait_in_bindings)] +/// # #![feature(type_alias_impl_trait)] +/// # +/// use embassy::executor::task; +/// use embassy::time::{Duration, Ticker}; +/// use futures::StreamExt; +/// # fn foo(){} +/// +/// #[task] +/// async fn ticker_example_1() { +/// let mut ticker = Ticker::every(Duration::from_secs(1)); +/// loop { +/// foo(); +/// ticker.next().await; +/// } +/// } +/// ``` pub struct Ticker { expires_at: Instant, duration: Duration, } impl Ticker { + /// Creates a new ticker that ticks at the specified duration interval. pub fn every(duration: Duration) -> Self { let expires_at = Instant::now() + duration; Self { From b79e9c29274fb7ede9122178a757101ac4e7283e Mon Sep 17 00:00:00 2001 From: xoviat Date: Mon, 22 Mar 2021 13:04:28 -0500 Subject: [PATCH 16/26] stm32: fix exti to require SysCfg.constrain() --- embassy-stm32/src/exti.rs | 40 ++++++++++++++++++------ embassy-stm32f4-examples/src/bin/exti.rs | 3 +- 2 files changed, 33 insertions(+), 10 deletions(-) diff --git a/embassy-stm32/src/exti.rs b/embassy-stm32/src/exti.rs index a0007945..8d70defe 100644 --- a/embassy-stm32/src/exti.rs +++ b/embassy-stm32/src/exti.rs @@ -5,6 +5,30 @@ use cortex_m; use crate::hal::gpio; +#[cfg(any( + feature = "stm32f401", + feature = "stm32f405", + feature = "stm32f407", + feature = "stm32f410", + feature = "stm32f411", + feature = "stm32f412", + feature = "stm32f413", + feature = "stm32f415", + feature = "stm32f417", + feature = "stm32f423", + feature = "stm32f427", + feature = "stm32f429", + feature = "stm32f437", + feature = "stm32f439", + feature = "stm32f446", + feature = "stm32f469", + feature = "stm32f479", +))] +use crate::hal::syscfg::SysCfg; + +#[cfg(any(feature = "stm32l0x1", feature = "stm32l0x2", feature = "stm32l0x3",))] +use crate::hal::syscfg::SYSCFG as SysCfg; + use embassy::traits::gpio::{ WaitForAnyEdge, WaitForFallingEdge, WaitForHigh, WaitForLow, WaitForRisingEdge, }; @@ -20,9 +44,9 @@ pub struct ExtiPin { } impl ExtiPin { - pub fn new(mut pin: T, interrupt: T::Interrupt) -> Self { + pub fn new(mut pin: T, interrupt: T::Interrupt, syscfg: &mut SysCfg) -> Self { cortex_m::interrupt::free(|_| { - pin.make_source(); + pin.make_source(syscfg); }); Self { pin, interrupt } @@ -189,7 +213,7 @@ pub trait WithInterrupt: private::Sealed { } pub trait Instance: WithInterrupt { - fn make_source(&mut self); + fn make_source(&mut self, syscfg: &mut SysCfg); fn clear_pending_bit(&mut self); fn trigger_edge(&mut self, edge: EdgeOption); } @@ -224,11 +248,9 @@ macro_rules! exti { feature = "stm32f479", ))] impl Instance for gpio::$set::$pin> { - fn make_source(&mut self) { - use crate::hal::{gpio::Edge, gpio::ExtiPin, syscfg::SysCfg}; - use crate::pac::EXTI; - let mut syscfg: SysCfg = unsafe { mem::transmute(()) }; - self.make_interrupt_source(&mut syscfg); + fn make_source(&mut self, syscfg: &mut SysCfg) { + use crate::hal::gpio::ExtiPin; + self.make_interrupt_source(syscfg); } fn clear_pending_bit(&mut self) { @@ -253,7 +275,7 @@ macro_rules! exti { #[cfg(any(feature = "stm32l0x1", feature = "stm32l0x2", feature = "stm32l0x3",))] impl Instance for gpio::$set::$pin { - fn make_source(&mut self) {} + fn make_source(&mut self, syscfg: &mut SysCfg) {} fn clear_pending_bit(&mut self) { use crate::hal::{ diff --git a/embassy-stm32f4-examples/src/bin/exti.rs b/embassy-stm32f4-examples/src/bin/exti.rs index 2201189e..9b2535b1 100644 --- a/embassy-stm32f4-examples/src/bin/exti.rs +++ b/embassy-stm32f4-examples/src/bin/exti.rs @@ -24,8 +24,9 @@ async fn run(dp: stm32::Peripherals, _cp: cortex_m::Peripherals) { let gpioa = dp.GPIOA.split(); let button = gpioa.pa0.into_pull_up_input(); + let mut syscfg = dp.SYSCFG.constrain(); - let pin = ExtiPin::new(button, interrupt::take!(EXTI0)); + let pin = ExtiPin::new(button, interrupt::take!(EXTI0), &mut syscfg); pin_mut!(pin); info!("Starting loop"); From 639059ba33e2440027924155df5c8a2efb7f4f6d Mon Sep 17 00:00:00 2001 From: xoviat <49173759+xoviat@users.noreply.github.com> Date: Mon, 22 Mar 2021 17:21:08 -0500 Subject: [PATCH 17/26] readme: add wiki link --- README.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 29c4a221..03732306 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,7 @@ # Embassy -Embassy is a project to make async/await a first-class option for embedded development. +Embassy is a project to make async/await a first-class option for embedded development. For more information and instructions to +get started, click [here](https://github.com/embassy-rs/embassy/wiki). ## Traits and types From 3c9d5b61bbf2126c198c9a0c243fafb06118ede2 Mon Sep 17 00:00:00 2001 From: xoviat Date: Tue, 23 Mar 2021 21:04:18 -0500 Subject: [PATCH 18/26] traits: add idle trait --- embassy-traits/src/uart.rs | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/embassy-traits/src/uart.rs b/embassy-traits/src/uart.rs index b40b9e9b..44174718 100644 --- a/embassy-traits/src/uart.rs +++ b/embassy-traits/src/uart.rs @@ -10,6 +10,15 @@ pub enum Error { pub trait Uart { type ReceiveFuture<'a>: Future>; type SendFuture<'a>: Future>; + /// Receive into the buffer until the buffer is full fn receive<'a>(&'a mut self, buf: &'a mut [u8]) -> Self::ReceiveFuture<'a>; + /// Send the specified buffer, and return when the transmission has completed fn send<'a>(&'a mut self, buf: &'a [u8]) -> Self::SendFuture<'a>; } + +pub trait IdleUart { + type ReceiveFuture<'a>: Future>; + /// Receive into the buffer until the buffer is full or the line is idle after some bytes are received + /// Return the number of bytes received + fn receive_until_idle<'a>(&'a mut self, buf: &'a mut [u8]) -> Self::ReceiveFuture<'a>; +} From 095ba23ad30d0d019d3e47be59b2354133367212 Mon Sep 17 00:00:00 2001 From: xoviat Date: Tue, 23 Mar 2021 21:04:36 -0500 Subject: [PATCH 19/26] stm32: implement idle trait --- embassy-stm32f4/src/serial.rs | 58 +++++++++++++++++++++++++++++++++-- 1 file changed, 56 insertions(+), 2 deletions(-) diff --git a/embassy-stm32f4/src/serial.rs b/embassy-stm32f4/src/serial.rs index bed89288..c8dc0598 100644 --- a/embassy-stm32f4/src/serial.rs +++ b/embassy-stm32f4/src/serial.rs @@ -10,7 +10,7 @@ use core::marker::PhantomData; use futures::{select_biased, FutureExt}; use embassy::interrupt::Interrupt; -use embassy::traits::uart::{Error, Uart}; +use embassy::traits::uart::{Error, IdleUart, Uart}; use embassy::util::InterruptFuture; use crate::hal::{ @@ -150,6 +150,56 @@ where let rx_stream = self.rx_stream.take().unwrap(); let usart = self.usart.take().unwrap(); + async move { + let mut rx_transfer = Transfer::init( + rx_stream, + usart, + static_buf, + None, + DmaConfig::default() + .transfer_complete_interrupt(true) + .memory_increment(true) + .double_buffer(false), + ); + + let fut = InterruptFuture::new(&mut self.rx_int); + rx_transfer.start(|_usart| {}); + fut.await; + + let (rx_stream, usart, _, _) = rx_transfer.free(); + self.rx_stream.replace(rx_stream); + self.usart.replace(usart); + + Ok(()) + } + } +} + +impl IdleUart for Serial +where + USART: serial::Instance + + PeriAddress + + DMASet + + DMASet + + WithInterrupt + + 'static, + TSTREAM: Stream + WithInterrupt + 'static, + RSTREAM: Stream + WithInterrupt + 'static, + CHANNEL: Channel + 'static, +{ + type ReceiveFuture<'a> = impl Future> + 'a; + + /// Receives serial data. + /// + /// The future is pending until either the buffer is completely full, or the RX line falls idle after receiving some data. + /// + /// Returns the number of bytes read. + fn receive_until_idle<'a>(&'a mut self, buf: &'a mut [u8]) -> Self::ReceiveFuture<'a> { + let static_buf = unsafe { core::mem::transmute::<&'a mut [u8], &'static mut [u8]>(buf) }; + + let rx_stream = self.rx_stream.take().unwrap(); + let usart = self.usart.take().unwrap(); + async move { unsafe { /* __HAL_UART_ENABLE_IT(&uart->UartHandle, UART_IT_IDLE); */ @@ -171,6 +221,8 @@ where .double_buffer(false), ); + let total_bytes = RSTREAM::get_number_of_transfers() as usize; + let fut = InterruptFuture::new(&mut self.rx_int); let fut_idle = InterruptFuture::new(&mut self.usart_int); @@ -183,13 +235,15 @@ where let (rx_stream, usart, _, _) = rx_transfer.free(); + let remaining_bytes = RSTREAM::get_number_of_transfers() as usize; + unsafe { (*USART::ptr()).cr1.modify(|_, w| w.idleie().clear_bit()); } self.rx_stream.replace(rx_stream); self.usart.replace(usart); - Ok(()) + Ok(total_bytes - remaining_bytes) } } } From da59112e86d43efc070ae09197930023552638ca Mon Sep 17 00:00:00 2001 From: Joshua Salzedo Date: Wed, 24 Mar 2021 12:36:02 -0700 Subject: [PATCH 20/26] Document the DropBomb --- embassy/src/util/drop_bomb.rs | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/embassy/src/util/drop_bomb.rs b/embassy/src/util/drop_bomb.rs index b2b0684e..7f0cc5d9 100644 --- a/embassy/src/util/drop_bomb.rs +++ b/embassy/src/util/drop_bomb.rs @@ -1,6 +1,12 @@ use crate::fmt::panic; use core::mem; +/// An explosive ordinance that panics if it is improperly disposed of. +/// +/// This is to forbid dropping futures, when there is absolutely no other choice. +/// +/// To correctly dispose of this device, call the [defuse](struct.DropBomb.html#method.defuse) +/// method before this object is dropped. pub struct DropBomb { _private: (), } @@ -9,7 +15,7 @@ impl DropBomb { pub fn new() -> Self { Self { _private: () } } - + // Diffuses the bomb, rendering it safe to drop. pub fn defuse(self) { mem::forget(self) } From a9e099c2151221f66571ab0001b77fe639c149ee Mon Sep 17 00:00:00 2001 From: Joshua Salzedo Date: Wed, 24 Mar 2021 12:36:17 -0700 Subject: [PATCH 21/26] Document embassy::util::Forever --- embassy/src/util/forever.rs | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/embassy/src/util/forever.rs b/embassy/src/util/forever.rs index ac23a3ce..efa96f30 100644 --- a/embassy/src/util/forever.rs +++ b/embassy/src/util/forever.rs @@ -3,6 +3,25 @@ use core::mem::MaybeUninit; use atomic_polyfill::{AtomicBool, Ordering}; +/// Type with static lifetime that may be written to once at runtime. +/// +/// This may be used to initialize static objects at runtime, typically in the init routine. +/// This is useful for objects such as Embassy's RTC, which cannot be initialized in a const +/// context. +/// +/// Note: IF a global mutable variable is desired, use a CriticalSectionMutex or ThreadModeMutex instead. +/// +/// ``` +/// use embassy::util::Forever; +/// // Using an integer for the sake of keeping this example self-contained, +/// // see https://github.com/embassy-rs/embassy/wiki/Getting-Started for a more "proper" example. +/// static SOME_INT: Forever =Forever::new(); +/// +/// // put returns a mutable pointer to the object stored in the forever, which may then be passed +/// // around. +/// let mut x = SOME_INT.put(42); +/// assert_eq!(*x, 42); +/// ``` pub struct Forever { used: AtomicBool, t: UnsafeCell>, @@ -19,6 +38,11 @@ impl Forever { } } + /// Gives this `Forever` a value. + /// + /// Panics if this `Forever` already has a value. + /// + /// Returns a mutable reference to the stored value. pub fn put(&'static self, val: T) -> &'static mut T { if self .used From 62009150bd3655f36c3f6247d5d339258212cb2f Mon Sep 17 00:00:00 2001 From: Joshua Salzedo Date: Wed, 24 Mar 2021 12:36:29 -0700 Subject: [PATCH 22/26] Document embassy::util::InterruptFuture --- embassy/src/util/signal.rs | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/embassy/src/util/signal.rs b/embassy/src/util/signal.rs index 41e27d4c..e3c5fad1 100644 --- a/embassy/src/util/signal.rs +++ b/embassy/src/util/signal.rs @@ -80,6 +80,25 @@ unsafe impl cortex_m::interrupt::Nr for NrWrap { } } +/// Creates a future that completes when the specified Interrupt is triggered. +/// +/// The input handler is unregistered when this Future is dropped. +/// +/// Example: +/// ``` no_compile +/// use embassy::traits::*; +/// use embassy::util::InterruptFuture; +/// use embassy::executor::task; +/// use embassy_stm32f4::interrupt; // Adjust this to your MCU's embassy HAL. +/// #[task] +/// async fn demo_interrupt_future() { +/// // Using STM32f446 interrupt names, adjust this to your application as necessary. +/// // Wait for TIM2 to tick. +/// let mut tim2_interrupt = interrupt::take!(TIM2); +/// InterruptFuture::new(&mut tim2_interrupt).await; +/// // TIM2 interrupt went off, do something... +/// } +/// ``` pub struct InterruptFuture<'a, I: Interrupt> { interrupt: &'a mut I, } From 40617fea043538833e40bec1694c17be6faf5077 Mon Sep 17 00:00:00 2001 From: Joshua Salzedo Date: Wed, 24 Mar 2021 13:19:05 -0700 Subject: [PATCH 23/26] Document embassy::util::signal & embassy::util --- embassy/src/util/mod.rs | 1 + embassy/src/util/signal.rs | 7 ++++++- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/embassy/src/util/mod.rs b/embassy/src/util/mod.rs index e64e7f1f..6917e999 100644 --- a/embassy/src/util/mod.rs +++ b/embassy/src/util/mod.rs @@ -1,3 +1,4 @@ +//! Async utilities mod drop_bomb; mod forever; mod mutex; diff --git a/embassy/src/util/signal.rs b/embassy/src/util/signal.rs index e3c5fad1..12dc7c31 100644 --- a/embassy/src/util/signal.rs +++ b/embassy/src/util/signal.rs @@ -10,6 +10,9 @@ use crate::executor; use crate::fmt::panic; use crate::interrupt::{Interrupt, InterruptExt}; +/// Synchronization primitive. Allows creating awaitable signals that may be passed between tasks. +/// +/// For more advanced use cases, please consider [futures-intrusive](https://crates.io/crates/futures-intrusive) channels or mutexes. pub struct Signal { state: UnsafeCell>, } @@ -29,7 +32,7 @@ impl Signal { state: UnsafeCell::new(State::None), } } - + /// Mark this Signal as completed. pub fn signal(&self, val: T) { cortex_m::interrupt::free(|_| unsafe { let state = &mut *self.state.get(); @@ -64,10 +67,12 @@ impl Signal { }) } + /// Future that completes when this Signal has been signaled. pub fn wait(&self) -> impl Future + '_ { futures::future::poll_fn(move |cx| self.poll_wait(cx)) } + /// non-blocking method to check whether this signal has been signaled. pub fn signaled(&self) -> bool { cortex_m::interrupt::free(|_| matches!(unsafe { &*self.state.get() }, State::Signaled(_))) } From 18d3c803deaf004114ec5f5bfd8de66bd241d2f4 Mon Sep 17 00:00:00 2001 From: Joshua Salzedo Date: Wed, 24 Mar 2021 13:21:32 -0700 Subject: [PATCH 24/26] Add missing newlines --- embassy/src/util/drop_bomb.rs | 1 + embassy/src/util/signal.rs | 1 + 2 files changed, 2 insertions(+) diff --git a/embassy/src/util/drop_bomb.rs b/embassy/src/util/drop_bomb.rs index 7f0cc5d9..6638a09c 100644 --- a/embassy/src/util/drop_bomb.rs +++ b/embassy/src/util/drop_bomb.rs @@ -15,6 +15,7 @@ impl DropBomb { pub fn new() -> Self { Self { _private: () } } + // Diffuses the bomb, rendering it safe to drop. pub fn defuse(self) { mem::forget(self) diff --git a/embassy/src/util/signal.rs b/embassy/src/util/signal.rs index 12dc7c31..0fd5c927 100644 --- a/embassy/src/util/signal.rs +++ b/embassy/src/util/signal.rs @@ -32,6 +32,7 @@ impl Signal { state: UnsafeCell::new(State::None), } } + /// Mark this Signal as completed. pub fn signal(&self, val: T) { cortex_m::interrupt::free(|_| unsafe { From ea650b684c54fa5e1639e80e5f6300d640b3c9d3 Mon Sep 17 00:00:00 2001 From: Joshua Salzedo Date: Wed, 24 Mar 2021 13:31:08 -0700 Subject: [PATCH 25/26] its `///` not `//`. --- embassy/src/util/drop_bomb.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/embassy/src/util/drop_bomb.rs b/embassy/src/util/drop_bomb.rs index 6638a09c..388209a2 100644 --- a/embassy/src/util/drop_bomb.rs +++ b/embassy/src/util/drop_bomb.rs @@ -16,7 +16,7 @@ impl DropBomb { Self { _private: () } } - // Diffuses the bomb, rendering it safe to drop. + /// Diffuses the bomb, rendering it safe to drop. pub fn defuse(self) { mem::forget(self) } From 6f597653afff99e3da1b442354258565f06005f6 Mon Sep 17 00:00:00 2001 From: xoviat Date: Fri, 26 Mar 2021 19:34:52 -0500 Subject: [PATCH 26/26] stm32: consolidate modules --- embassy-stm32/Cargo.toml | 4 +- {embassy-stm32f4 => embassy-stm32}/src/can.rs | 19 +++++++++ embassy-stm32/src/lib.rs | 39 +++++++++++++++++++ {embassy-stm32f4 => embassy-stm32}/src/rtc.rs | 0 embassy-stm32f4/src/lib.rs | 6 +-- 5 files changed, 64 insertions(+), 4 deletions(-) rename {embassy-stm32f4 => embassy-stm32}/src/can.rs (85%) rename {embassy-stm32f4 => embassy-stm32}/src/rtc.rs (100%) diff --git a/embassy-stm32/Cargo.toml b/embassy-stm32/Cargo.toml index 6701ff8f..3fae66e9 100644 --- a/embassy-stm32/Cargo.toml +++ b/embassy-stm32/Cargo.toml @@ -42,5 +42,7 @@ cortex-m-rt = "0.6.13" cortex-m = "0.7.1" embedded-hal = { version = "0.2.4" } embedded-dma = { version = "0.1.2" } +bxcan = "0.5.0" +nb = "*" stm32f4xx-hal = { version = "0.8.3", features = ["rt", "can"], git = "https://github.com/stm32-rs/stm32f4xx-hal.git", optional = true } -stm32l0xx-hal = { version = "0.7.0", features = ["rt"], git = "https://github.com/stm32-rs/stm32l0xx-hal.git", optional = true } \ No newline at end of file +stm32l0xx-hal = { version = "0.7.0", features = ["rt"], optional = true } \ No newline at end of file diff --git a/embassy-stm32f4/src/can.rs b/embassy-stm32/src/can.rs similarity index 85% rename from embassy-stm32f4/src/can.rs rename to embassy-stm32/src/can.rs index 526d002c..f97e900e 100644 --- a/embassy-stm32f4/src/can.rs +++ b/embassy-stm32/src/can.rs @@ -95,6 +95,25 @@ macro_rules! can { } } +#[cfg(any( + feature = "stm32f401", + feature = "stm32f405", + feature = "stm32f407", + feature = "stm32f410", + feature = "stm32f411", + feature = "stm32f412", + feature = "stm32f413", + feature = "stm32f415", + feature = "stm32f417", + feature = "stm32f423", + feature = "stm32f427", + feature = "stm32f429", + feature = "stm32f437", + feature = "stm32f439", + feature = "stm32f446", + feature = "stm32f469", + feature = "stm32f479", +))] can! { CAN1 => (CAN1_TX, CAN1_RX0), CAN2 => (CAN2_TX, CAN2_RX0), diff --git a/embassy-stm32/src/lib.rs b/embassy-stm32/src/lib.rs index 954067f3..8a1fe5a3 100644 --- a/embassy-stm32/src/lib.rs +++ b/embassy-stm32/src/lib.rs @@ -34,3 +34,42 @@ pub mod fmt; pub mod exti; pub mod interrupt; + +#[cfg(any( + feature = "stm32f405", + feature = "stm32f407", + feature = "stm32f412", + feature = "stm32f413", + feature = "stm32f415", + feature = "stm32f417", + feature = "stm32f423", + feature = "stm32f427", + feature = "stm32f429", + feature = "stm32f437", + feature = "stm32f439", + feature = "stm32f446", + feature = "stm32f469", + feature = "stm32f479", +))] +pub mod can; + +#[cfg(any( + feature = "stm32f401", + feature = "stm32f405", + feature = "stm32f407", + feature = "stm32f410", + feature = "stm32f411", + feature = "stm32f412", + feature = "stm32f413", + feature = "stm32f415", + feature = "stm32f417", + feature = "stm32f423", + feature = "stm32f427", + feature = "stm32f429", + feature = "stm32f437", + feature = "stm32f439", + feature = "stm32f446", + feature = "stm32f469", + feature = "stm32f479", +))] +pub mod rtc; diff --git a/embassy-stm32f4/src/rtc.rs b/embassy-stm32/src/rtc.rs similarity index 100% rename from embassy-stm32f4/src/rtc.rs rename to embassy-stm32/src/rtc.rs diff --git a/embassy-stm32f4/src/lib.rs b/embassy-stm32f4/src/lib.rs index bdd4d0fd..eb6f04f8 100644 --- a/embassy-stm32f4/src/lib.rs +++ b/embassy-stm32f4/src/lib.rs @@ -307,11 +307,11 @@ compile_error!( "Multile chip features activated. You must activate exactly one of the following features: " ); -pub use embassy_stm32::{exti, fmt, hal, interrupt, pac}; +pub use embassy_stm32::{exti, fmt, hal, interrupt, pac, rtc}; #[cfg(not(any(feature = "stm32f401", feature = "stm32f410", feature = "stm32f411",)))] -pub mod can; +pub use embassy_stm32::can; + #[cfg(not(feature = "stm32f410"))] pub mod qei; -pub mod rtc; pub mod serial;