From cdb3fb059f32a5669856390cfe379da71092d545 Mon Sep 17 00:00:00 2001 From: xoviat Date: Sun, 25 Jun 2023 15:33:57 -0500 Subject: [PATCH 01/68] stm32/hrtim: first draft --- embassy-stm32/build.rs | 10 ++ embassy-stm32/src/pwm/advanced_pwm.rs | 147 ++++++++++++++++++++++++++ embassy-stm32/src/pwm/mod.rs | 60 +++++++++++ embassy-stm32/src/timer/mod.rs | 39 +++++++ 4 files changed, 256 insertions(+) create mode 100644 embassy-stm32/src/pwm/advanced_pwm.rs diff --git a/embassy-stm32/build.rs b/embassy-stm32/build.rs index 40103d32..4856047c 100644 --- a/embassy-stm32/build.rs +++ b/embassy-stm32/build.rs @@ -589,6 +589,16 @@ fn main() { (("timer", "BKIN2"), quote!(crate::pwm::BreakInput2Pin)), (("timer", "BKIN2_COMP1"), quote!(crate::pwm::BreakInput2Comparator1Pin)), (("timer", "BKIN2_COMP2"), quote!(crate::pwm::BreakInput2Comparator2Pin)), + (("hrtim", "CHA1"), quote!(crate::pwm::ChannelAPin)), + (("hrtim", "CHA2"), quote!(crate::pwm::ChannelAComplementaryPin)), + (("hrtim", "CHB1"), quote!(crate::pwm::ChannelBPin)), + (("hrtim", "CHB2"), quote!(crate::pwm::ChannelBComplementaryPin)), + (("hrtim", "CHC1"), quote!(crate::pwm::ChannelCPin)), + (("hrtim", "CHC2"), quote!(crate::pwm::ChannelCComplementaryPin)), + (("hrtim", "CHD1"), quote!(crate::pwm::ChannelDPin)), + (("hrtim", "CHD2"), quote!(crate::pwm::ChannelDComplementaryPin)), + (("hrtim", "CHE1"), quote!(crate::pwm::ChannelEPin)), + (("hrtim", "CHE2"), quote!(crate::pwm::ChannelEComplementaryPin)), (("sdmmc", "CK"), quote!(crate::sdmmc::CkPin)), (("sdmmc", "CMD"), quote!(crate::sdmmc::CmdPin)), (("sdmmc", "D0"), quote!(crate::sdmmc::D0Pin)), diff --git a/embassy-stm32/src/pwm/advanced_pwm.rs b/embassy-stm32/src/pwm/advanced_pwm.rs new file mode 100644 index 00000000..39d60c50 --- /dev/null +++ b/embassy-stm32/src/pwm/advanced_pwm.rs @@ -0,0 +1,147 @@ +use core::marker::PhantomData; + +use embassy_hal_common::{into_ref, PeripheralRef}; + +use super::simple_pwm::*; +use super::*; +#[allow(unused_imports)] +use crate::gpio::sealed::{AFType, Pin}; +use crate::gpio::AnyPin; +use crate::time::Hertz; +use crate::Peripheral; + +// Re-implement the channels for hrtim +pub struct ChA; +pub struct ChB; +pub struct ChC; +pub struct ChD; +pub struct ChE; + +pub struct PwmPin<'d, Perip, Channel> { + _pin: PeripheralRef<'d, AnyPin>, + phantom: PhantomData<(Perip, Channel)>, +} + +pub struct ComplementaryPwmPin<'d, Perip, Channel> { + _pin: PeripheralRef<'d, AnyPin>, + phantom: PhantomData<(Perip, Channel)>, +} + +macro_rules! advanced_channel_impl { + ($new_chx:ident, $channel:ident, $pin_trait:ident, $complementary_pin_trait:ident) => { + impl<'d, Perip: AdvancedCaptureCompare16bitInstance> PwmPin<'d, Perip, $channel> { + pub fn $new_chx(pin: impl Peripheral

> + 'd) -> Self { + into_ref!(pin); + critical_section::with(|_| { + pin.set_low(); + pin.set_as_af(pin.af_num(), AFType::OutputPushPull); + #[cfg(gpio_v2)] + pin.set_speed(crate::gpio::Speed::VeryHigh); + }); + PwmPin { + _pin: pin.map_into(), + phantom: PhantomData, + } + } + } + + impl<'d, Perip: AdvancedCaptureCompare16bitInstance> ComplementaryPwmPin<'d, Perip, $channel> { + pub fn $new_chx(pin: impl Peripheral

> + 'd) -> Self { + into_ref!(pin); + critical_section::with(|_| { + pin.set_low(); + pin.set_as_af(pin.af_num(), AFType::OutputPushPull); + #[cfg(gpio_v2)] + pin.set_speed(crate::gpio::Speed::VeryHigh); + }); + ComplementaryPwmPin { + _pin: pin.map_into(), + phantom: PhantomData, + } + } + } + }; +} + +advanced_channel_impl!(new_cha, ChA, ChannelAPin, ChannelAComplementaryPin); +advanced_channel_impl!(new_chb, ChB, ChannelBPin, ChannelBComplementaryPin); +advanced_channel_impl!(new_chc, ChC, ChannelCPin, ChannelCComplementaryPin); +advanced_channel_impl!(new_chd, ChD, ChannelDPin, ChannelDComplementaryPin); +advanced_channel_impl!(new_che, ChE, ChannelEPin, ChannelEComplementaryPin); + +pub struct AdvancedPwm<'d, T> { + inner: PeripheralRef<'d, T>, +} + +impl<'d, T: ComplementaryCaptureCompare16bitInstance> AdvancedPwm<'d, T> { + pub fn new( + tim: impl Peripheral

+ 'd, + _ch1: Option>, + _ch1n: Option>, + _ch2: Option>, + _ch2n: Option>, + _ch3: Option>, + _ch3n: Option>, + _ch4: Option>, + _ch4n: Option>, + freq: Hertz, + ) -> Self { + Self::new_inner(tim, freq) + } + + fn new_inner(tim: impl Peripheral

+ 'd, freq: Hertz) -> Self { + into_ref!(tim); + + T::enable(); + ::reset(); + + let mut this = Self { inner: tim }; + // + // this.inner.set_frequency(freq); + // this.inner.start(); + // + // this.inner.enable_outputs(true); + // + // this.inner + // .set_output_compare_mode(Channel::Ch1, OutputCompareMode::PwmMode1); + // this.inner + // .set_output_compare_mode(Channel::Ch2, OutputCompareMode::PwmMode1); + // this.inner + // .set_output_compare_mode(Channel::Ch3, OutputCompareMode::PwmMode1); + // this.inner + // .set_output_compare_mode(Channel::Ch4, OutputCompareMode::PwmMode1); + this + } + + pub fn enable(&mut self, channel: AdvancedChannel) { + // self.inner.enable_channel(channel, true); + // self.inner.enable_complementary_channel(channel, true); + } + + pub fn disable(&mut self, channel: AdvancedChannel) { + // self.inner.enable_complementary_channel(channel, false); + // self.inner.enable_channel(channel, false); + } + + pub fn set_freq(&mut self, freq: Hertz) { + // self.inner.set_frequency(freq); + } + + pub fn get_max_duty(&self) -> u16 { + todo!() + // self.inner.get_max_compare_value() + } + + pub fn set_duty(&mut self, channel: AdvancedChannel, duty: u16) { + // assert!(duty < self.get_max_duty()); + // self.inner.set_compare_value(channel, duty) + } + + /// Set the dead time as a proportion of max_duty + pub fn set_dead_time(&mut self, value: u16) { + // let (ckd, value) = compute_dead_time_value(value); + // + // self.inner.set_dead_time_clock_division(ckd); + // self.inner.set_dead_time_value(value); + } +} diff --git a/embassy-stm32/src/pwm/mod.rs b/embassy-stm32/src/pwm/mod.rs index 5aba2663..4c203134 100644 --- a/embassy-stm32/src/pwm/mod.rs +++ b/embassy-stm32/src/pwm/mod.rs @@ -1,3 +1,5 @@ +#[cfg(hrtim_v1)] +pub mod advanced_pwm; pub mod complementary_pwm; pub mod simple_pwm; @@ -27,6 +29,29 @@ impl Channel { } } +#[cfg(hrtim_v1)] +#[derive(Clone, Copy)] +pub enum AdvancedChannel { + ChA, + ChB, + ChC, + ChD, + ChE, +} + +#[cfg(hrtim_v1)] +impl AdvancedChannel { + pub fn raw(&self) -> usize { + match self { + AdvancedChannel::ChA => 0, + AdvancedChannel::ChB => 1, + AdvancedChannel::ChC => 2, + AdvancedChannel::ChD => 3, + AdvancedChannel::ChE => 4, + } + } +} + #[derive(Clone, Copy)] pub enum OutputCompareMode { Frozen, @@ -57,6 +82,14 @@ impl From for stm32_metapac::timer::vals::Ocm { pub(crate) mod sealed { use super::*; + pub trait AdvancedCaptureCompare16bitInstance: crate::timer::sealed::HighResolutionControlInstance { + fn enable_outputs(&mut self, enable: bool); + + fn set_output_compare_mode(&mut self, channel: AdvancedChannel, mode: OutputCompareMode); + + fn enable_channel(&mut self, channel: AdvancedChannel, enable: bool); + } + pub trait CaptureCompare16bitInstance: crate::timer::sealed::GeneralPurpose16bitInstance { /// Global output enable. Does not do anything on non-advanced timers. fn enable_outputs(&mut self, enable: bool); @@ -89,6 +122,8 @@ pub(crate) mod sealed { } } +pub trait AdvancedCaptureCompare16bitInstance: sealed::AdvancedCaptureCompare16bitInstance + 'static {} + pub trait CaptureCompare16bitInstance: sealed::CaptureCompare16bitInstance + crate::timer::GeneralPurpose16bitInstance + 'static { @@ -250,6 +285,20 @@ foreach_interrupt! { } }; + + ($inst:ident, hrtim, HRTIM, MASTER, $irq:ident) => { + impl crate::pwm::sealed::AdvancedCaptureCompare16bitInstance for crate::peripherals::$inst { + fn enable_outputs(&mut self, enable: bool) { todo!() } + + fn set_output_compare_mode(&mut self, channel: AdvancedChannel, mode: OutputCompareMode) { todo!() } + + fn enable_channel(&mut self, channel: AdvancedChannel, enable: bool) { todo!() } + } + + impl AdvancedCaptureCompare16bitInstance for crate::peripherals::$inst { + + } + }; } pin_trait!(Channel1Pin, CaptureCompare16bitInstance); @@ -267,3 +316,14 @@ pin_trait!(BreakInputComparator2Pin, CaptureCompare16bitInstance); pin_trait!(BreakInput2Pin, CaptureCompare16bitInstance); pin_trait!(BreakInput2Comparator1Pin, CaptureCompare16bitInstance); pin_trait!(BreakInput2Comparator2Pin, CaptureCompare16bitInstance); + +pin_trait!(ChannelAPin, AdvancedCaptureCompare16bitInstance); +pin_trait!(ChannelAComplementaryPin, AdvancedCaptureCompare16bitInstance); +pin_trait!(ChannelBPin, AdvancedCaptureCompare16bitInstance); +pin_trait!(ChannelBComplementaryPin, AdvancedCaptureCompare16bitInstance); +pin_trait!(ChannelCPin, AdvancedCaptureCompare16bitInstance); +pin_trait!(ChannelCComplementaryPin, AdvancedCaptureCompare16bitInstance); +pin_trait!(ChannelDPin, AdvancedCaptureCompare16bitInstance); +pin_trait!(ChannelDComplementaryPin, AdvancedCaptureCompare16bitInstance); +pin_trait!(ChannelEPin, AdvancedCaptureCompare16bitInstance); +pin_trait!(ChannelEComplementaryPin, AdvancedCaptureCompare16bitInstance); diff --git a/embassy-stm32/src/timer/mod.rs b/embassy-stm32/src/timer/mod.rs index 09b7a377..34ad5db1 100644 --- a/embassy-stm32/src/timer/mod.rs +++ b/embassy-stm32/src/timer/mod.rs @@ -43,6 +43,21 @@ pub(crate) mod sealed { pub trait AdvancedControlInstance: GeneralPurpose16bitInstance { fn regs_advanced() -> crate::pac::timer::TimAdv; } + + #[cfg(hrtim_v1)] + pub trait HighResolutionControlInstance: RccPeripheral { + type Interrupt: interrupt::typelevel::Interrupt; + + fn regs_highres() -> crate::pac::hrtim::Hrtim; + + fn start(&mut self); + + fn stop(&mut self); + + fn reset(&mut self); + + fn set_frequency(&mut self, frequency: Hertz); + } } pub trait GeneralPurpose16bitInstance: sealed::GeneralPurpose16bitInstance + 'static {} @@ -51,6 +66,9 @@ pub trait GeneralPurpose32bitInstance: sealed::GeneralPurpose32bitInstance + 'st pub trait AdvancedControlInstance: sealed::AdvancedControlInstance + 'static {} +#[cfg(hrtim_v1)] +pub trait HighResolutionControlInstance: sealed::HighResolutionControlInstance + 'static {} + pub trait Basic16bitInstance: sealed::Basic16bitInstance + 'static {} #[allow(unused)] @@ -208,4 +226,25 @@ foreach_interrupt! { impl AdvancedControlInstance for crate::peripherals::$inst { } }; + + ($inst:ident, hrtim, HRTIM, MASTER, $irq:ident) => { + impl sealed::HighResolutionControlInstance for crate::peripherals::$inst { + type Interrupt = crate::interrupt::typelevel::$irq; + + fn regs_highres() -> crate::pac::hrtim::Hrtim { + crate::pac::$inst + } + + fn start(&mut self) { todo!() } + + fn stop(&mut self) { todo!() } + + fn reset(&mut self) { todo!() } + + fn set_frequency(&mut self, frequency: Hertz) { todo!() } + } + + impl HighResolutionControlInstance for crate::peripherals::$inst { + } + }; } From 71513ccb3905965aef2ae29a841dfdf4dfffe69a Mon Sep 17 00:00:00 2001 From: xoviat Date: Mon, 26 Jun 2023 19:59:10 -0500 Subject: [PATCH 02/68] stm32/hrtim: impl. draft frequency computation --- embassy-stm32/src/pwm/mod.rs | 32 +++++--- embassy-stm32/src/timer/mod.rs | 138 ++++++++++++++++++++++++++++++++- 2 files changed, 155 insertions(+), 15 deletions(-) diff --git a/embassy-stm32/src/pwm/mod.rs b/embassy-stm32/src/pwm/mod.rs index 4c203134..1838bfdf 100644 --- a/embassy-stm32/src/pwm/mod.rs +++ b/embassy-stm32/src/pwm/mod.rs @@ -29,7 +29,6 @@ impl Channel { } } -#[cfg(hrtim_v1)] #[derive(Clone, Copy)] pub enum AdvancedChannel { ChA, @@ -39,7 +38,6 @@ pub enum AdvancedChannel { ChE, } -#[cfg(hrtim_v1)] impl AdvancedChannel { pub fn raw(&self) -> usize { match self { @@ -82,6 +80,7 @@ impl From for stm32_metapac::timer::vals::Ocm { pub(crate) mod sealed { use super::*; + #[cfg(hrtim_v1)] pub trait AdvancedCaptureCompare16bitInstance: crate::timer::sealed::HighResolutionControlInstance { fn enable_outputs(&mut self, enable: bool); @@ -122,6 +121,7 @@ pub(crate) mod sealed { } } +#[cfg(hrtim_v1)] pub trait AdvancedCaptureCompare16bitInstance: sealed::AdvancedCaptureCompare16bitInstance + 'static {} pub trait CaptureCompare16bitInstance: @@ -317,13 +317,21 @@ pin_trait!(BreakInput2Pin, CaptureCompare16bitInstance); pin_trait!(BreakInput2Comparator1Pin, CaptureCompare16bitInstance); pin_trait!(BreakInput2Comparator2Pin, CaptureCompare16bitInstance); -pin_trait!(ChannelAPin, AdvancedCaptureCompare16bitInstance); -pin_trait!(ChannelAComplementaryPin, AdvancedCaptureCompare16bitInstance); -pin_trait!(ChannelBPin, AdvancedCaptureCompare16bitInstance); -pin_trait!(ChannelBComplementaryPin, AdvancedCaptureCompare16bitInstance); -pin_trait!(ChannelCPin, AdvancedCaptureCompare16bitInstance); -pin_trait!(ChannelCComplementaryPin, AdvancedCaptureCompare16bitInstance); -pin_trait!(ChannelDPin, AdvancedCaptureCompare16bitInstance); -pin_trait!(ChannelDComplementaryPin, AdvancedCaptureCompare16bitInstance); -pin_trait!(ChannelEPin, AdvancedCaptureCompare16bitInstance); -pin_trait!(ChannelEComplementaryPin, AdvancedCaptureCompare16bitInstance); +#[cfg(hrtim_v1)] +mod hrtim_pins { + use super::*; + + pin_trait!(ChannelAPin, AdvancedCaptureCompare16bitInstance); + pin_trait!(ChannelAComplementaryPin, AdvancedCaptureCompare16bitInstance); + pin_trait!(ChannelBPin, AdvancedCaptureCompare16bitInstance); + pin_trait!(ChannelBComplementaryPin, AdvancedCaptureCompare16bitInstance); + pin_trait!(ChannelCPin, AdvancedCaptureCompare16bitInstance); + pin_trait!(ChannelCComplementaryPin, AdvancedCaptureCompare16bitInstance); + pin_trait!(ChannelDPin, AdvancedCaptureCompare16bitInstance); + pin_trait!(ChannelDComplementaryPin, AdvancedCaptureCompare16bitInstance); + pin_trait!(ChannelEPin, AdvancedCaptureCompare16bitInstance); + pin_trait!(ChannelEComplementaryPin, AdvancedCaptureCompare16bitInstance); +} + +#[cfg(hrtim_v1)] +pub use hrtim_pins::*; diff --git a/embassy-stm32/src/timer/mod.rs b/embassy-stm32/src/timer/mod.rs index 34ad5db1..03b9b3cf 100644 --- a/embassy-stm32/src/timer/mod.rs +++ b/embassy-stm32/src/timer/mod.rs @@ -1,3 +1,6 @@ +#[cfg(hrtim_v1)] +use core::ops; + use stm32_metapac::timer::vals; use crate::interrupt; @@ -50,13 +53,64 @@ pub(crate) mod sealed { fn regs_highres() -> crate::pac::hrtim::Hrtim; + fn set_master_frequency(&mut self, frequency: Hertz); + + fn set_channel_frequency(&mut self, channel: usize, frequency: Hertz); + fn start(&mut self); fn stop(&mut self); fn reset(&mut self); + } +} - fn set_frequency(&mut self, frequency: Hertz); +#[cfg(hrtim_v1)] +#[derive(Clone, Copy)] +pub(crate) enum HighResolutionControlPrescaler { + Div1, + Div2, + Div4, + Div8, + Div16, + Div32, + Div64, + Div128, +} + +#[cfg(hrtim_v1)] +impl ops::Div for Hertz { + type Output = Hertz; + + fn div(self, rhs: HighResolutionControlPrescaler) -> Self::Output { + let divisor = match rhs { + HighResolutionControlPrescaler::Div1 => 1, + HighResolutionControlPrescaler::Div2 => 2, + HighResolutionControlPrescaler::Div4 => 4, + HighResolutionControlPrescaler::Div8 => 8, + HighResolutionControlPrescaler::Div16 => 16, + HighResolutionControlPrescaler::Div32 => 32, + HighResolutionControlPrescaler::Div64 => 64, + HighResolutionControlPrescaler::Div128 => 128, + }; + + Hertz(self.0 / divisor) + } +} + +#[cfg(hrtim_v1)] +impl From for u8 { + fn from(val: HighResolutionControlPrescaler) -> Self { + match val { + HighResolutionControlPrescaler::Div1 => 0b000, + HighResolutionControlPrescaler::Div2 => 0b001, + HighResolutionControlPrescaler::Div4 => 0b010, + HighResolutionControlPrescaler::Div8 => 0b011, + HighResolutionControlPrescaler::Div16 => 0b100, + HighResolutionControlPrescaler::Div32 => 0b101, + HighResolutionControlPrescaler::Div64 => 0b110, + HighResolutionControlPrescaler::Div128 => 0b111, + } } } @@ -235,13 +289,91 @@ foreach_interrupt! { crate::pac::$inst } + fn set_master_frequency(&mut self, frequency: Hertz) { + use crate::rcc::sealed::RccPeripheral; + + // TODO: fix frequency source + let f = frequency.0; + let timer_f = Self::frequency().0; + // Ratio taken from RM0364 Table 81 + let base_f = Hertz(timer_f * (70_300 / 144_000_000)); + + /* + Find the smallest prescaler that allows us to acheive our frequency + */ + let psc = [ + HighResolutionControlPrescaler::Div1, + HighResolutionControlPrescaler::Div2, + HighResolutionControlPrescaler::Div4, + HighResolutionControlPrescaler::Div8, + HighResolutionControlPrescaler::Div16, + HighResolutionControlPrescaler::Div32, + HighResolutionControlPrescaler::Div64, + HighResolutionControlPrescaler::Div128, + ] + .iter() + .skip_while(|psc| frequency < base_f / **psc) + .next() + .unwrap(); + + let psc_timer_f = Hertz(timer_f) / *psc; + let per: u16 = (psc_timer_f / f).0 as u16; + + let regs = Self::regs_highres(); + + regs.mcr().modify(|w| w.set_ckpsc(((*psc).into()))); + regs.mper().modify(|w| w.set_mper(per)); + + // regs.cr1().modify(|r| r.set_urs(vals::Urs::COUNTERONLY)); + // regs.egr().write(|r| r.set_ug(true)); + // regs.cr1().modify(|r| r.set_urs(vals::Urs::ANYEVENT)); + } + + fn set_channel_frequency(&mut self, channel: usize, frequency: Hertz) { + use crate::rcc::sealed::RccPeripheral; + + // TODO: fix frequency source + let f = frequency.0; + let timer_f = Self::frequency().0; + // Ratio taken from RM0364 Table 81 + let base_f = Hertz(timer_f * (70_300 / 144_000_000)); + + /* + Find the smallest prescaler that allows us to acheive our frequency + */ + let psc = [ + HighResolutionControlPrescaler::Div1, + HighResolutionControlPrescaler::Div2, + HighResolutionControlPrescaler::Div4, + HighResolutionControlPrescaler::Div8, + HighResolutionControlPrescaler::Div16, + HighResolutionControlPrescaler::Div32, + HighResolutionControlPrescaler::Div64, + HighResolutionControlPrescaler::Div128, + ] + .iter() + .skip_while(|psc| frequency < base_f / **psc) + .next() + .unwrap(); + + let psc_timer_f = Hertz(timer_f) / *psc; + let per: u16 = (psc_timer_f / f).0 as u16; + + let regs = Self::regs_highres(); + + regs.tim(channel).cr().modify(|w| w.set_ckpsc(((*psc).into()))); + regs.tim(channel).per().modify(|w| w.set_per(per)); + + // regs.cr1().modify(|r| r.set_urs(vals::Urs::COUNTERONLY)); + // regs.egr().write(|r| r.set_ug(true)); + // regs.cr1().modify(|r| r.set_urs(vals::Urs::ANYEVENT)); + } + fn start(&mut self) { todo!() } fn stop(&mut self) { todo!() } fn reset(&mut self) { todo!() } - - fn set_frequency(&mut self, frequency: Hertz) { todo!() } } impl HighResolutionControlInstance for crate::peripherals::$inst { From b9eb3dfad7b601a7819377c2c94369ca74efc824 Mon Sep 17 00:00:00 2001 From: xoviat Date: Mon, 26 Jun 2023 20:23:26 -0500 Subject: [PATCH 03/68] stm32/hrtim: add api concept --- embassy-stm32/src/pwm/advanced_pwm.rs | 131 +++++++++++++++++++------- 1 file changed, 98 insertions(+), 33 deletions(-) diff --git a/embassy-stm32/src/pwm/advanced_pwm.rs b/embassy-stm32/src/pwm/advanced_pwm.rs index 39d60c50..a0895224 100644 --- a/embassy-stm32/src/pwm/advanced_pwm.rs +++ b/embassy-stm32/src/pwm/advanced_pwm.rs @@ -11,11 +11,30 @@ use crate::time::Hertz; use crate::Peripheral; // Re-implement the channels for hrtim -pub struct ChA; -pub struct ChB; -pub struct ChC; -pub struct ChD; -pub struct ChE; +pub struct Master { + phantom: PhantomData, +} +pub struct ChA { + phantom: PhantomData, +} +pub struct ChB { + phantom: PhantomData, +} +pub struct ChC { + phantom: PhantomData, +} +pub struct ChD { + phantom: PhantomData, +} +pub struct ChE { + phantom: PhantomData, +} + +mod sealed { + pub trait AdvancedChannel {} +} + +pub trait AdvancedChannel: sealed::AdvancedChannel {} pub struct PwmPin<'d, Perip, Channel> { _pin: PeripheralRef<'d, AnyPin>, @@ -60,6 +79,9 @@ macro_rules! advanced_channel_impl { } } } + + impl sealed::AdvancedChannel for $channel {} + impl AdvancedChannel for $channel {} }; } @@ -69,8 +91,15 @@ advanced_channel_impl!(new_chc, ChC, ChannelCPin, ChannelCComplementaryPin); advanced_channel_impl!(new_chd, ChD, ChannelDPin, ChannelDComplementaryPin); advanced_channel_impl!(new_che, ChE, ChannelEPin, ChannelEComplementaryPin); +/// Struct used to divide a high resolution timer into multiple channels pub struct AdvancedPwm<'d, T> { inner: PeripheralRef<'d, T>, + pub master: Master, + pub ch_a: ChA, + pub ch_b: ChB, + pub ch_c: ChC, + pub ch_d: ChD, + pub ch_e: ChE, } impl<'d, T: ComplementaryCaptureCompare16bitInstance> AdvancedPwm<'d, T> { @@ -84,18 +113,25 @@ impl<'d, T: ComplementaryCaptureCompare16bitInstance> AdvancedPwm<'d, T> { _ch3n: Option>, _ch4: Option>, _ch4n: Option>, - freq: Hertz, ) -> Self { - Self::new_inner(tim, freq) + Self::new_inner(tim) } - fn new_inner(tim: impl Peripheral

+ 'd, freq: Hertz) -> Self { + fn new_inner(tim: impl Peripheral

+ 'd) -> Self { into_ref!(tim); T::enable(); ::reset(); - let mut this = Self { inner: tim }; + Self { + inner: tim, + master: Master { phantom: PhantomData }, + ch_a: ChA { phantom: PhantomData }, + ch_b: ChB { phantom: PhantomData }, + ch_c: ChC { phantom: PhantomData }, + ch_d: ChD { phantom: PhantomData }, + ch_e: ChE { phantom: PhantomData }, + } // // this.inner.set_frequency(freq); // this.inner.start(); @@ -110,32 +146,31 @@ impl<'d, T: ComplementaryCaptureCompare16bitInstance> AdvancedPwm<'d, T> { // .set_output_compare_mode(Channel::Ch3, OutputCompareMode::PwmMode1); // this.inner // .set_output_compare_mode(Channel::Ch4, OutputCompareMode::PwmMode1); - this } - pub fn enable(&mut self, channel: AdvancedChannel) { - // self.inner.enable_channel(channel, true); - // self.inner.enable_complementary_channel(channel, true); - } - - pub fn disable(&mut self, channel: AdvancedChannel) { - // self.inner.enable_complementary_channel(channel, false); - // self.inner.enable_channel(channel, false); - } - - pub fn set_freq(&mut self, freq: Hertz) { - // self.inner.set_frequency(freq); - } - - pub fn get_max_duty(&self) -> u16 { - todo!() - // self.inner.get_max_compare_value() - } - - pub fn set_duty(&mut self, channel: AdvancedChannel, duty: u16) { - // assert!(duty < self.get_max_duty()); - // self.inner.set_compare_value(channel, duty) - } + // pub fn enable(&mut self, channel: AdvancedChannel) { + // // self.inner.enable_channel(channel, true); + // // self.inner.enable_complementary_channel(channel, true); + // } + // + // pub fn disable(&mut self, channel: AdvancedChannel) { + // // self.inner.enable_complementary_channel(channel, false); + // // self.inner.enable_channel(channel, false); + // } + // + // pub fn set_freq(&mut self, freq: Hertz) { + // // self.inner.set_frequency(freq); + // } + // + // pub fn get_max_duty(&self) -> u16 { + // todo!() + // // self.inner.get_max_compare_value() + // } + // + // pub fn set_duty(&mut self, channel: AdvancedChannel, duty: u16) { + // // assert!(duty < self.get_max_duty()); + // // self.inner.set_compare_value(channel, duty) + // } /// Set the dead time as a proportion of max_duty pub fn set_dead_time(&mut self, value: u16) { @@ -145,3 +180,33 @@ impl<'d, T: ComplementaryCaptureCompare16bitInstance> AdvancedPwm<'d, T> { // self.inner.set_dead_time_value(value); } } + +// Represents a fixed-frequency bridge converter +pub struct BridgeConverter { + pub ch: T, +} + +impl BridgeConverter { + pub fn new(channel: T, frequency: Hertz) -> Self { + Self { ch: channel } + } + + pub fn set_duty(&mut self, primary: u16, secondary: u16) { + todo!() + } +} + +// Represents a variable-frequency resonant converter +pub struct ResonantConverter { + pub ch: T, +} + +impl ResonantConverter { + pub fn new(channel: T, min_frequency: Hertz) -> Self { + Self { ch: channel } + } + + pub fn set_frequency(&mut self, frequency: Hertz) { + todo!() + } +} From 348019e37f0ff5716d80199e33244c0a1a59b360 Mon Sep 17 00:00:00 2001 From: xoviat Date: Tue, 27 Jun 2023 18:24:32 -0500 Subject: [PATCH 04/68] stm32/hrtim: impl channel alloc type system --- embassy-stm32/src/pwm/advanced_pwm.rs | 167 ++++++++++++-------------- embassy-stm32/src/pwm/mod.rs | 144 +++++++++++++++++----- embassy-stm32/src/timer/mod.rs | 149 +---------------------- 3 files changed, 194 insertions(+), 266 deletions(-) diff --git a/embassy-stm32/src/pwm/advanced_pwm.rs b/embassy-stm32/src/pwm/advanced_pwm.rs index a0895224..c7b5f748 100644 --- a/embassy-stm32/src/pwm/advanced_pwm.rs +++ b/embassy-stm32/src/pwm/advanced_pwm.rs @@ -2,7 +2,6 @@ use core::marker::PhantomData; use embassy_hal_common::{into_ref, PeripheralRef}; -use super::simple_pwm::*; use super::*; #[allow(unused_imports)] use crate::gpio::sealed::{AFType, Pin}; @@ -11,30 +10,34 @@ use crate::time::Hertz; use crate::Peripheral; // Re-implement the channels for hrtim -pub struct Master { - phantom: PhantomData, +pub struct Master { + phantom: PhantomData, } -pub struct ChA { - phantom: PhantomData, +pub struct ChA { + phantom: PhantomData, } -pub struct ChB { - phantom: PhantomData, +pub struct ChB { + phantom: PhantomData, } -pub struct ChC { - phantom: PhantomData, +pub struct ChC { + phantom: PhantomData, } -pub struct ChD { - phantom: PhantomData, +pub struct ChD { + phantom: PhantomData, } -pub struct ChE { - phantom: PhantomData, +pub struct ChE { + phantom: PhantomData, } mod sealed { - pub trait AdvancedChannel {} + use crate::pwm::AdvancedCaptureCompare16bitInstance; + + pub trait AdvancedChannel {} } -pub trait AdvancedChannel: sealed::AdvancedChannel {} +pub trait AdvancedChannel: sealed::AdvancedChannel { + fn raw() -> usize; +} pub struct PwmPin<'d, Perip, Channel> { _pin: PeripheralRef<'d, AnyPin>, @@ -47,8 +50,8 @@ pub struct ComplementaryPwmPin<'d, Perip, Channel> { } macro_rules! advanced_channel_impl { - ($new_chx:ident, $channel:ident, $pin_trait:ident, $complementary_pin_trait:ident) => { - impl<'d, Perip: AdvancedCaptureCompare16bitInstance> PwmPin<'d, Perip, $channel> { + ($new_chx:ident, $channel:tt, $ch_num:expr, $pin_trait:ident, $complementary_pin_trait:ident) => { + impl<'d, Perip: AdvancedCaptureCompare16bitInstance> PwmPin<'d, Perip, $channel> { pub fn $new_chx(pin: impl Peripheral

> + 'd) -> Self { into_ref!(pin); critical_section::with(|_| { @@ -64,7 +67,7 @@ macro_rules! advanced_channel_impl { } } - impl<'d, Perip: AdvancedCaptureCompare16bitInstance> ComplementaryPwmPin<'d, Perip, $channel> { + impl<'d, Perip: AdvancedCaptureCompare16bitInstance> ComplementaryPwmPin<'d, Perip, $channel> { pub fn $new_chx(pin: impl Peripheral

> + 'd) -> Self { into_ref!(pin); critical_section::with(|_| { @@ -80,39 +83,45 @@ macro_rules! advanced_channel_impl { } } - impl sealed::AdvancedChannel for $channel {} - impl AdvancedChannel for $channel {} + impl sealed::AdvancedChannel for $channel {} + impl AdvancedChannel for $channel { + fn raw() -> usize { + $ch_num + } + } }; } -advanced_channel_impl!(new_cha, ChA, ChannelAPin, ChannelAComplementaryPin); -advanced_channel_impl!(new_chb, ChB, ChannelBPin, ChannelBComplementaryPin); -advanced_channel_impl!(new_chc, ChC, ChannelCPin, ChannelCComplementaryPin); -advanced_channel_impl!(new_chd, ChD, ChannelDPin, ChannelDComplementaryPin); -advanced_channel_impl!(new_che, ChE, ChannelEPin, ChannelEComplementaryPin); +advanced_channel_impl!(new_cha, ChA, 0, ChannelAPin, ChannelAComplementaryPin); +advanced_channel_impl!(new_chb, ChB, 1, ChannelBPin, ChannelBComplementaryPin); +advanced_channel_impl!(new_chc, ChC, 2, ChannelCPin, ChannelCComplementaryPin); +advanced_channel_impl!(new_chd, ChD, 3, ChannelDPin, ChannelDComplementaryPin); +advanced_channel_impl!(new_che, ChE, 4, ChannelEPin, ChannelEComplementaryPin); /// Struct used to divide a high resolution timer into multiple channels -pub struct AdvancedPwm<'d, T> { - inner: PeripheralRef<'d, T>, - pub master: Master, - pub ch_a: ChA, - pub ch_b: ChB, - pub ch_c: ChC, - pub ch_d: ChD, - pub ch_e: ChE, +pub struct AdvancedPwm<'d, T: AdvancedCaptureCompare16bitInstance> { + _inner: PeripheralRef<'d, T>, + pub master: Master, + pub ch_a: ChA, + pub ch_b: ChB, + pub ch_c: ChC, + pub ch_d: ChD, + pub ch_e: ChE, } -impl<'d, T: ComplementaryCaptureCompare16bitInstance> AdvancedPwm<'d, T> { +impl<'d, T: AdvancedCaptureCompare16bitInstance> AdvancedPwm<'d, T> { pub fn new( tim: impl Peripheral

+ 'd, - _ch1: Option>, - _ch1n: Option>, - _ch2: Option>, - _ch2n: Option>, - _ch3: Option>, - _ch3n: Option>, - _ch4: Option>, - _ch4n: Option>, + _cha: Option>>, + _chan: Option>>, + _chb: Option>>, + _chbn: Option>>, + _chc: Option>>, + _chcn: Option>>, + _chd: Option>>, + _chdn: Option>>, + _che: Option>>, + _chen: Option>>, ) -> Self { Self::new_inner(tim) } @@ -124,7 +133,7 @@ impl<'d, T: ComplementaryCaptureCompare16bitInstance> AdvancedPwm<'d, T> { ::reset(); Self { - inner: tim, + _inner: tim, master: Master { phantom: PhantomData }, ch_a: ChA { phantom: PhantomData }, ch_b: ChB { phantom: PhantomData }, @@ -132,48 +141,11 @@ impl<'d, T: ComplementaryCaptureCompare16bitInstance> AdvancedPwm<'d, T> { ch_d: ChD { phantom: PhantomData }, ch_e: ChE { phantom: PhantomData }, } - // - // this.inner.set_frequency(freq); - // this.inner.start(); - // - // this.inner.enable_outputs(true); - // - // this.inner - // .set_output_compare_mode(Channel::Ch1, OutputCompareMode::PwmMode1); - // this.inner - // .set_output_compare_mode(Channel::Ch2, OutputCompareMode::PwmMode1); - // this.inner - // .set_output_compare_mode(Channel::Ch3, OutputCompareMode::PwmMode1); - // this.inner - // .set_output_compare_mode(Channel::Ch4, OutputCompareMode::PwmMode1); } - // pub fn enable(&mut self, channel: AdvancedChannel) { - // // self.inner.enable_channel(channel, true); - // // self.inner.enable_complementary_channel(channel, true); - // } - // - // pub fn disable(&mut self, channel: AdvancedChannel) { - // // self.inner.enable_complementary_channel(channel, false); - // // self.inner.enable_channel(channel, false); - // } - // - // pub fn set_freq(&mut self, freq: Hertz) { - // // self.inner.set_frequency(freq); - // } - // - // pub fn get_max_duty(&self) -> u16 { - // todo!() - // // self.inner.get_max_compare_value() - // } - // - // pub fn set_duty(&mut self, channel: AdvancedChannel, duty: u16) { - // // assert!(duty < self.get_max_duty()); - // // self.inner.set_compare_value(channel, duty) - // } - /// Set the dead time as a proportion of max_duty - pub fn set_dead_time(&mut self, value: u16) { + pub fn set_dead_time(&mut self, _value: u16) { + todo!() // let (ckd, value) = compute_dead_time_value(value); // // self.inner.set_dead_time_clock_division(ckd); @@ -182,28 +154,39 @@ impl<'d, T: ComplementaryCaptureCompare16bitInstance> AdvancedPwm<'d, T> { } // Represents a fixed-frequency bridge converter -pub struct BridgeConverter { - pub ch: T, +pub struct BridgeConverter> { + phantom: PhantomData, + pub ch: C, } -impl BridgeConverter { - pub fn new(channel: T, frequency: Hertz) -> Self { - Self { ch: channel } +impl> BridgeConverter { + pub fn new(channel: C, frequency: Hertz) -> Self { + Self { + phantom: PhantomData, + ch: channel, + } } pub fn set_duty(&mut self, primary: u16, secondary: u16) { + let _ = T::regs(); + let _ = C::raw(); + todo!() } } // Represents a variable-frequency resonant converter -pub struct ResonantConverter { - pub ch: T, +pub struct ResonantConverter> { + phantom: PhantomData, + pub ch: C, } -impl ResonantConverter { - pub fn new(channel: T, min_frequency: Hertz) -> Self { - Self { ch: channel } +impl> ResonantConverter { + pub fn new(channel: C, min_frequency: Hertz) -> Self { + Self { + phantom: PhantomData, + ch: channel, + } } pub fn set_frequency(&mut self, frequency: Hertz) { diff --git a/embassy-stm32/src/pwm/mod.rs b/embassy-stm32/src/pwm/mod.rs index 1838bfdf..67009e78 100644 --- a/embassy-stm32/src/pwm/mod.rs +++ b/embassy-stm32/src/pwm/mod.rs @@ -3,8 +3,14 @@ pub mod advanced_pwm; pub mod complementary_pwm; pub mod simple_pwm; +#[cfg(hrtim_v1)] +use core::ops; + use stm32_metapac::timer::vals::Ckd; +#[cfg(hrtim_v1)] +use crate::time::Hertz; + #[cfg(feature = "unstable-pac")] pub mod low_level { pub use super::sealed::*; @@ -29,27 +35,6 @@ impl Channel { } } -#[derive(Clone, Copy)] -pub enum AdvancedChannel { - ChA, - ChB, - ChC, - ChD, - ChE, -} - -impl AdvancedChannel { - pub fn raw(&self) -> usize { - match self { - AdvancedChannel::ChA => 0, - AdvancedChannel::ChB => 1, - AdvancedChannel::ChC => 2, - AdvancedChannel::ChD => 3, - AdvancedChannel::ChE => 4, - } - } -} - #[derive(Clone, Copy)] pub enum OutputCompareMode { Frozen, @@ -77,16 +62,87 @@ impl From for stm32_metapac::timer::vals::Ocm { } } +#[cfg(hrtim_v1)] +#[derive(Clone, Copy)] +pub(crate) enum HighResolutionControlPrescaler { + Div1, + Div2, + Div4, + Div8, + Div16, + Div32, + Div64, + Div128, +} + +#[cfg(hrtim_v1)] +impl ops::Div for Hertz { + type Output = Hertz; + + fn div(self, rhs: HighResolutionControlPrescaler) -> Self::Output { + let divisor = match rhs { + HighResolutionControlPrescaler::Div1 => 1, + HighResolutionControlPrescaler::Div2 => 2, + HighResolutionControlPrescaler::Div4 => 4, + HighResolutionControlPrescaler::Div8 => 8, + HighResolutionControlPrescaler::Div16 => 16, + HighResolutionControlPrescaler::Div32 => 32, + HighResolutionControlPrescaler::Div64 => 64, + HighResolutionControlPrescaler::Div128 => 128, + }; + + Hertz(self.0 / divisor) + } +} + +#[cfg(hrtim_v1)] +impl From for u8 { + fn from(val: HighResolutionControlPrescaler) -> Self { + match val { + HighResolutionControlPrescaler::Div1 => 0b000, + HighResolutionControlPrescaler::Div2 => 0b001, + HighResolutionControlPrescaler::Div4 => 0b010, + HighResolutionControlPrescaler::Div8 => 0b011, + HighResolutionControlPrescaler::Div16 => 0b100, + HighResolutionControlPrescaler::Div32 => 0b101, + HighResolutionControlPrescaler::Div64 => 0b110, + HighResolutionControlPrescaler::Div128 => 0b111, + } + } +} + +#[cfg(hrtim_v1)] +impl HighResolutionControlPrescaler { + pub fn compute_min(base_f: Hertz, frequency: Hertz) -> Self { + *[ + HighResolutionControlPrescaler::Div1, + HighResolutionControlPrescaler::Div2, + HighResolutionControlPrescaler::Div4, + HighResolutionControlPrescaler::Div8, + HighResolutionControlPrescaler::Div16, + HighResolutionControlPrescaler::Div32, + HighResolutionControlPrescaler::Div64, + HighResolutionControlPrescaler::Div128, + ] + .iter() + .skip_while(|psc| frequency <= base_f / **psc) + .next() + .unwrap() + } +} + pub(crate) mod sealed { use super::*; #[cfg(hrtim_v1)] pub trait AdvancedCaptureCompare16bitInstance: crate::timer::sealed::HighResolutionControlInstance { - fn enable_outputs(&mut self, enable: bool); + fn set_master_frequency(frequency: Hertz); - fn set_output_compare_mode(&mut self, channel: AdvancedChannel, mode: OutputCompareMode); + fn set_channel_frequency(channnel: usize, frequency: Hertz); - fn enable_channel(&mut self, channel: AdvancedChannel, enable: bool); + // fn enable_outputs(enable: bool); + // + // fn enable_channel(&mut self, channel: usize, enable: bool); } pub trait CaptureCompare16bitInstance: crate::timer::sealed::GeneralPurpose16bitInstance { @@ -288,11 +344,45 @@ foreach_interrupt! { ($inst:ident, hrtim, HRTIM, MASTER, $irq:ident) => { impl crate::pwm::sealed::AdvancedCaptureCompare16bitInstance for crate::peripherals::$inst { - fn enable_outputs(&mut self, enable: bool) { todo!() } + fn set_master_frequency(frequency: Hertz) { + use crate::rcc::sealed::RccPeripheral; + use crate::timer::sealed::HighResolutionControlInstance; - fn set_output_compare_mode(&mut self, channel: AdvancedChannel, mode: OutputCompareMode) { todo!() } + let f = frequency.0; + // TODO: fix frequency source + // let timer_f = Self::frequency().0; + let timer_f = Hertz(144_000_000).0; + let base_f = Hertz((32 * timer_f as u64 / u16::MAX as u64) as u32); + let psc = HighResolutionControlPrescaler::compute_min(base_f, frequency); - fn enable_channel(&mut self, channel: AdvancedChannel, enable: bool) { todo!() } + let psc_timer_f = Hertz(timer_f) / psc; + let per: u16 = (psc_timer_f / f).0 as u16; + + let regs = Self::regs(); + + regs.mcr().modify(|w| w.set_ckpsc(psc.into())); + regs.mper().modify(|w| w.set_mper(per)); + } + + fn set_channel_frequency(channel: usize, frequency: Hertz) { + use crate::rcc::sealed::RccPeripheral; + use crate::timer::sealed::HighResolutionControlInstance; + + let f = frequency.0; + // TODO: fix frequency source + // let timer_f = Self::frequency().0; + let timer_f = Hertz(144_000_000).0; + let base_f = Hertz((32 * timer_f as u64 / u16::MAX as u64) as u32); + let psc = HighResolutionControlPrescaler::compute_min(base_f, frequency); + + let psc_timer_f = Hertz(timer_f) / psc; + let per: u16 = (psc_timer_f / f).0 as u16; + + let regs = Self::regs(); + + regs.tim(channel).cr().modify(|w| w.set_ckpsc(psc.into())); + regs.tim(channel).per().modify(|w| w.set_per(per)); + } } impl AdvancedCaptureCompare16bitInstance for crate::peripherals::$inst { diff --git a/embassy-stm32/src/timer/mod.rs b/embassy-stm32/src/timer/mod.rs index 03b9b3cf..89cb1666 100644 --- a/embassy-stm32/src/timer/mod.rs +++ b/embassy-stm32/src/timer/mod.rs @@ -51,66 +51,7 @@ pub(crate) mod sealed { pub trait HighResolutionControlInstance: RccPeripheral { type Interrupt: interrupt::typelevel::Interrupt; - fn regs_highres() -> crate::pac::hrtim::Hrtim; - - fn set_master_frequency(&mut self, frequency: Hertz); - - fn set_channel_frequency(&mut self, channel: usize, frequency: Hertz); - - fn start(&mut self); - - fn stop(&mut self); - - fn reset(&mut self); - } -} - -#[cfg(hrtim_v1)] -#[derive(Clone, Copy)] -pub(crate) enum HighResolutionControlPrescaler { - Div1, - Div2, - Div4, - Div8, - Div16, - Div32, - Div64, - Div128, -} - -#[cfg(hrtim_v1)] -impl ops::Div for Hertz { - type Output = Hertz; - - fn div(self, rhs: HighResolutionControlPrescaler) -> Self::Output { - let divisor = match rhs { - HighResolutionControlPrescaler::Div1 => 1, - HighResolutionControlPrescaler::Div2 => 2, - HighResolutionControlPrescaler::Div4 => 4, - HighResolutionControlPrescaler::Div8 => 8, - HighResolutionControlPrescaler::Div16 => 16, - HighResolutionControlPrescaler::Div32 => 32, - HighResolutionControlPrescaler::Div64 => 64, - HighResolutionControlPrescaler::Div128 => 128, - }; - - Hertz(self.0 / divisor) - } -} - -#[cfg(hrtim_v1)] -impl From for u8 { - fn from(val: HighResolutionControlPrescaler) -> Self { - match val { - HighResolutionControlPrescaler::Div1 => 0b000, - HighResolutionControlPrescaler::Div2 => 0b001, - HighResolutionControlPrescaler::Div4 => 0b010, - HighResolutionControlPrescaler::Div8 => 0b011, - HighResolutionControlPrescaler::Div16 => 0b100, - HighResolutionControlPrescaler::Div32 => 0b101, - HighResolutionControlPrescaler::Div64 => 0b110, - HighResolutionControlPrescaler::Div128 => 0b111, - } + fn regs() -> crate::pac::hrtim::Hrtim; } } @@ -285,95 +226,9 @@ foreach_interrupt! { impl sealed::HighResolutionControlInstance for crate::peripherals::$inst { type Interrupt = crate::interrupt::typelevel::$irq; - fn regs_highres() -> crate::pac::hrtim::Hrtim { + fn regs() -> crate::pac::hrtim::Hrtim { crate::pac::$inst } - - fn set_master_frequency(&mut self, frequency: Hertz) { - use crate::rcc::sealed::RccPeripheral; - - // TODO: fix frequency source - let f = frequency.0; - let timer_f = Self::frequency().0; - // Ratio taken from RM0364 Table 81 - let base_f = Hertz(timer_f * (70_300 / 144_000_000)); - - /* - Find the smallest prescaler that allows us to acheive our frequency - */ - let psc = [ - HighResolutionControlPrescaler::Div1, - HighResolutionControlPrescaler::Div2, - HighResolutionControlPrescaler::Div4, - HighResolutionControlPrescaler::Div8, - HighResolutionControlPrescaler::Div16, - HighResolutionControlPrescaler::Div32, - HighResolutionControlPrescaler::Div64, - HighResolutionControlPrescaler::Div128, - ] - .iter() - .skip_while(|psc| frequency < base_f / **psc) - .next() - .unwrap(); - - let psc_timer_f = Hertz(timer_f) / *psc; - let per: u16 = (psc_timer_f / f).0 as u16; - - let regs = Self::regs_highres(); - - regs.mcr().modify(|w| w.set_ckpsc(((*psc).into()))); - regs.mper().modify(|w| w.set_mper(per)); - - // regs.cr1().modify(|r| r.set_urs(vals::Urs::COUNTERONLY)); - // regs.egr().write(|r| r.set_ug(true)); - // regs.cr1().modify(|r| r.set_urs(vals::Urs::ANYEVENT)); - } - - fn set_channel_frequency(&mut self, channel: usize, frequency: Hertz) { - use crate::rcc::sealed::RccPeripheral; - - // TODO: fix frequency source - let f = frequency.0; - let timer_f = Self::frequency().0; - // Ratio taken from RM0364 Table 81 - let base_f = Hertz(timer_f * (70_300 / 144_000_000)); - - /* - Find the smallest prescaler that allows us to acheive our frequency - */ - let psc = [ - HighResolutionControlPrescaler::Div1, - HighResolutionControlPrescaler::Div2, - HighResolutionControlPrescaler::Div4, - HighResolutionControlPrescaler::Div8, - HighResolutionControlPrescaler::Div16, - HighResolutionControlPrescaler::Div32, - HighResolutionControlPrescaler::Div64, - HighResolutionControlPrescaler::Div128, - ] - .iter() - .skip_while(|psc| frequency < base_f / **psc) - .next() - .unwrap(); - - let psc_timer_f = Hertz(timer_f) / *psc; - let per: u16 = (psc_timer_f / f).0 as u16; - - let regs = Self::regs_highres(); - - regs.tim(channel).cr().modify(|w| w.set_ckpsc(((*psc).into()))); - regs.tim(channel).per().modify(|w| w.set_per(per)); - - // regs.cr1().modify(|r| r.set_urs(vals::Urs::COUNTERONLY)); - // regs.egr().write(|r| r.set_ug(true)); - // regs.cr1().modify(|r| r.set_urs(vals::Urs::ANYEVENT)); - } - - fn start(&mut self) { todo!() } - - fn stop(&mut self) { todo!() } - - fn reset(&mut self) { todo!() } } impl HighResolutionControlInstance for crate::peripherals::$inst { From 3252eaa060d8efb79f99511ac40a26be9cf287f9 Mon Sep 17 00:00:00 2001 From: xoviat Date: Tue, 27 Jun 2023 20:30:58 -0500 Subject: [PATCH 05/68] stm32/hrtim: add example impl. --- .vscode/settings.json | 1 + ci.sh | 1 + embassy-stm32/src/pwm/advanced_pwm.rs | 2 +- examples/stm32f334/.cargo/config.toml | 9 ++++ examples/stm32f334/Cargo.toml | 26 +++++++++++ examples/stm32f334/build.rs | 5 +++ examples/stm32f334/src/bin/hello.rs | 23 ++++++++++ examples/stm32f334/src/bin/pwm.rs | 63 +++++++++++++++++++++++++++ 8 files changed, 129 insertions(+), 1 deletion(-) create mode 100644 examples/stm32f334/.cargo/config.toml create mode 100644 examples/stm32f334/Cargo.toml create mode 100644 examples/stm32f334/build.rs create mode 100644 examples/stm32f334/src/bin/hello.rs create mode 100644 examples/stm32f334/src/bin/pwm.rs diff --git a/.vscode/settings.json b/.vscode/settings.json index 9ef7fe1c..fdfafafa 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -25,6 +25,7 @@ // "examples/stm32f1/Cargo.toml", // "examples/stm32f2/Cargo.toml", // "examples/stm32f3/Cargo.toml", + // "examples/stm32f334/Cargo.toml", // "examples/stm32f4/Cargo.toml", // "examples/stm32f7/Cargo.toml", // "examples/stm32g0/Cargo.toml", diff --git a/ci.sh b/ci.sh index a03efb85..ec6304f1 100755 --- a/ci.sh +++ b/ci.sh @@ -119,6 +119,7 @@ cargo batch \ --- build --release --manifest-path examples/stm32f1/Cargo.toml --target thumbv7m-none-eabi --out-dir out/examples/stm32f1 \ --- build --release --manifest-path examples/stm32f2/Cargo.toml --target thumbv7m-none-eabi --out-dir out/examples/stm32f2 \ --- build --release --manifest-path examples/stm32f3/Cargo.toml --target thumbv7em-none-eabihf --out-dir out/examples/stm32f3 \ + --- build --release --manifest-path examples/stm32f334/Cargo.toml --target thumbv7em-none-eabihf --out-dir out/examples/stm32f334 \ --- build --release --manifest-path examples/stm32f4/Cargo.toml --target thumbv7em-none-eabi --out-dir out/examples/stm32f4 \ --- build --release --manifest-path examples/stm32f7/Cargo.toml --target thumbv7em-none-eabihf --out-dir out/examples/stm32f7 \ --- build --release --manifest-path examples/stm32c0/Cargo.toml --target thumbv6m-none-eabi --out-dir out/examples/stm32c0 \ diff --git a/embassy-stm32/src/pwm/advanced_pwm.rs b/embassy-stm32/src/pwm/advanced_pwm.rs index c7b5f748..bb0e06ad 100644 --- a/embassy-stm32/src/pwm/advanced_pwm.rs +++ b/embassy-stm32/src/pwm/advanced_pwm.rs @@ -52,7 +52,7 @@ pub struct ComplementaryPwmPin<'d, Perip, Channel> { macro_rules! advanced_channel_impl { ($new_chx:ident, $channel:tt, $ch_num:expr, $pin_trait:ident, $complementary_pin_trait:ident) => { impl<'d, Perip: AdvancedCaptureCompare16bitInstance> PwmPin<'d, Perip, $channel> { - pub fn $new_chx(pin: impl Peripheral

> + 'd) -> Self { + pub fn $new_chx(pin: impl Peripheral

> + 'd) -> Self { into_ref!(pin); critical_section::with(|_| { pin.set_low(); diff --git a/examples/stm32f334/.cargo/config.toml b/examples/stm32f334/.cargo/config.toml new file mode 100644 index 00000000..7f3fda52 --- /dev/null +++ b/examples/stm32f334/.cargo/config.toml @@ -0,0 +1,9 @@ +[target.'cfg(all(target_arch = "arm", target_os = "none"))'] +# replace STM32F429ZITx with your chip as listed in `probe-rs-cli chip list` +runner = "probe-rs-cli run --chip STM32F303ZETx" + +[build] +target = "thumbv7em-none-eabihf" + +[env] +DEFMT_LOG = "trace" diff --git a/examples/stm32f334/Cargo.toml b/examples/stm32f334/Cargo.toml new file mode 100644 index 00000000..6410891a --- /dev/null +++ b/examples/stm32f334/Cargo.toml @@ -0,0 +1,26 @@ +[package] +edition = "2021" +name = "embassy-stm32f3-examples" +version = "0.1.0" +license = "MIT OR Apache-2.0" + +[dependencies] +embassy-sync = { version = "0.2.0", path = "../../embassy-sync", features = ["defmt"] } +embassy-executor = { version = "0.2.0", path = "../../embassy-executor", features = ["nightly", "arch-cortex-m", "executor-thread", "executor-interrupt", "defmt", "integrated-timers"] } +embassy-time = { version = "0.1.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } +embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["nightly", "defmt", "stm32f334r8", "unstable-pac", "memory-x", "time-driver-any", "exti"] } +embassy-usb = { version = "0.1.0", path = "../../embassy-usb", features = ["defmt"] } +embassy-futures = { version = "0.1.0", path = "../../embassy-futures" } + +defmt = "0.3" +defmt-rtt = "0.4" + +cortex-m = { version = "0.7.6", features = ["inline-asm", "critical-section-single-core"] } +cortex-m-rt = "0.7.0" +embedded-hal = "0.2.6" +panic-probe = { version = "0.3", features = ["print-defmt"] } +futures = { version = "0.3.17", default-features = false, features = ["async-await"] } +heapless = { version = "0.7.5", default-features = false } +nb = "1.0.0" +embedded-storage = "0.3.0" +static_cell = { version = "1.1", features = ["nightly"]} diff --git a/examples/stm32f334/build.rs b/examples/stm32f334/build.rs new file mode 100644 index 00000000..8cd32d7e --- /dev/null +++ b/examples/stm32f334/build.rs @@ -0,0 +1,5 @@ +fn main() { + println!("cargo:rustc-link-arg-bins=--nmagic"); + println!("cargo:rustc-link-arg-bins=-Tlink.x"); + println!("cargo:rustc-link-arg-bins=-Tdefmt.x"); +} diff --git a/examples/stm32f334/src/bin/hello.rs b/examples/stm32f334/src/bin/hello.rs new file mode 100644 index 00000000..65773210 --- /dev/null +++ b/examples/stm32f334/src/bin/hello.rs @@ -0,0 +1,23 @@ +#![no_std] +#![no_main] +#![feature(type_alias_impl_trait)] + +use defmt::info; +use embassy_executor::Spawner; +use embassy_stm32::time::Hertz; +use embassy_stm32::Config; +use embassy_time::{Duration, Timer}; +use {defmt_rtt as _, panic_probe as _}; + +#[embassy_executor::main] +async fn main(_spawner: Spawner) -> ! { + let mut config = Config::default(); + config.rcc.hse = Some(Hertz(8_000_000)); + config.rcc.sysclk = Some(Hertz(16_000_000)); + let _p = embassy_stm32::init(config); + + loop { + info!("Hello World!"); + Timer::after(Duration::from_secs(1)).await; + } +} diff --git a/examples/stm32f334/src/bin/pwm.rs b/examples/stm32f334/src/bin/pwm.rs new file mode 100644 index 00000000..cc2ea861 --- /dev/null +++ b/examples/stm32f334/src/bin/pwm.rs @@ -0,0 +1,63 @@ +#![no_std] +#![no_main] +#![feature(type_alias_impl_trait)] + +use defmt::*; +use embassy_executor::Spawner; +use embassy_stm32::pwm::advanced_pwm::*; +use embassy_stm32::pwm::Channel; +use embassy_stm32::time::khz; +use embassy_time::{Duration, Timer}; +use {defmt_rtt as _, panic_probe as _}; + +#[embassy_executor::main] +async fn main(_spawner: Spawner) { + let p = embassy_stm32::init(Default::default()); + info!("Hello World!"); + + let ch1 = PwmPin::new_cha(p.PA8); + let ch1n = ComplementaryPwmPin::new_cha(p.PA9); + let mut pwm = AdvancedPwm::new( + p.HRTIM1, + Some(ch1), + Some(ch1n), + None, + None, + None, + None, + None, + None, + None, + None, + ); + + pwm.set_dead_time(0); + + let mut buck_converter = BridgeConverter::new(pwm.ch_a, khz(100)); + + buck_converter.set_duty(0, u16::MAX); + + // note: if the pins are not passed into the advanced pwm struct, they will not be output + let mut boost_converter = BridgeConverter::new(pwm.ch_b, khz(100)); + + boost_converter.set_duty(0, 0); + + // let max = pwm.get_max_duty(); + // pwm.set_dead_time(max / 1024); + // + // pwm.enable(Channel::Ch1); + // + // info!("PWM initialized"); + // info!("PWM max duty {}", max); + // + // loop { + // pwm.set_duty(Channel::Ch1, 0); + // Timer::after(Duration::from_millis(300)).await; + // pwm.set_duty(Channel::Ch1, max / 4); + // Timer::after(Duration::from_millis(300)).await; + // pwm.set_duty(Channel::Ch1, max / 2); + // Timer::after(Duration::from_millis(300)).await; + // pwm.set_duty(Channel::Ch1, max - 1); + // Timer::after(Duration::from_millis(300)).await; + // } +} From 8c4997c5fcd8c25d68865b9e7537e0add50eac24 Mon Sep 17 00:00:00 2001 From: xoviat Date: Thu, 29 Jun 2023 21:05:41 -0500 Subject: [PATCH 06/68] stm32/hrtim: impl. bridge, dead-time part. res. --- embassy-stm32/src/pwm/advanced_pwm.rs | 193 +++++++++++++++++++++----- embassy-stm32/src/pwm/mod.rs | 117 +++++++++++----- examples/stm32f334/src/bin/pwm.rs | 9 +- 3 files changed, 241 insertions(+), 78 deletions(-) diff --git a/embassy-stm32/src/pwm/advanced_pwm.rs b/embassy-stm32/src/pwm/advanced_pwm.rs index bb0e06ad..228899c0 100644 --- a/embassy-stm32/src/pwm/advanced_pwm.rs +++ b/embassy-stm32/src/pwm/advanced_pwm.rs @@ -9,33 +9,44 @@ use crate::gpio::AnyPin; use crate::time::Hertz; use crate::Peripheral; -// Re-implement the channels for hrtim -pub struct Master { +pub enum Source { + Master, + ChA, + ChB, + ChC, + ChD, + ChE, +} + +pub struct BurstController { phantom: PhantomData, } -pub struct ChA { +pub struct Master { phantom: PhantomData, } -pub struct ChB { +pub struct ChA { phantom: PhantomData, } -pub struct ChC { +pub struct ChB { phantom: PhantomData, } -pub struct ChD { +pub struct ChC { phantom: PhantomData, } -pub struct ChE { +pub struct ChD { + phantom: PhantomData, +} +pub struct ChE { phantom: PhantomData, } mod sealed { - use crate::pwm::AdvancedCaptureCompare16bitInstance; + use crate::pwm::HighResolutionCaptureCompare16bitInstance; - pub trait AdvancedChannel {} + pub trait AdvancedChannel {} } -pub trait AdvancedChannel: sealed::AdvancedChannel { +pub trait AdvancedChannel: sealed::AdvancedChannel { fn raw() -> usize; } @@ -51,7 +62,7 @@ pub struct ComplementaryPwmPin<'d, Perip, Channel> { macro_rules! advanced_channel_impl { ($new_chx:ident, $channel:tt, $ch_num:expr, $pin_trait:ident, $complementary_pin_trait:ident) => { - impl<'d, Perip: AdvancedCaptureCompare16bitInstance> PwmPin<'d, Perip, $channel> { + impl<'d, Perip: HighResolutionCaptureCompare16bitInstance> PwmPin<'d, Perip, $channel> { pub fn $new_chx(pin: impl Peripheral

> + 'd) -> Self { into_ref!(pin); critical_section::with(|_| { @@ -67,7 +78,7 @@ macro_rules! advanced_channel_impl { } } - impl<'d, Perip: AdvancedCaptureCompare16bitInstance> ComplementaryPwmPin<'d, Perip, $channel> { + impl<'d, Perip: HighResolutionCaptureCompare16bitInstance> ComplementaryPwmPin<'d, Perip, $channel> { pub fn $new_chx(pin: impl Peripheral

> + 'd) -> Self { into_ref!(pin); critical_section::with(|_| { @@ -83,8 +94,8 @@ macro_rules! advanced_channel_impl { } } - impl sealed::AdvancedChannel for $channel {} - impl AdvancedChannel for $channel { + impl sealed::AdvancedChannel for $channel {} + impl AdvancedChannel for $channel { fn raw() -> usize { $ch_num } @@ -99,9 +110,10 @@ advanced_channel_impl!(new_chd, ChD, 3, ChannelDPin, ChannelDComplementaryPin); advanced_channel_impl!(new_che, ChE, 4, ChannelEPin, ChannelEComplementaryPin); /// Struct used to divide a high resolution timer into multiple channels -pub struct AdvancedPwm<'d, T: AdvancedCaptureCompare16bitInstance> { +pub struct AdvancedPwm<'d, T: HighResolutionCaptureCompare16bitInstance> { _inner: PeripheralRef<'d, T>, pub master: Master, + pub burst_controller: BurstController, pub ch_a: ChA, pub ch_b: ChB, pub ch_c: ChC, @@ -109,7 +121,7 @@ pub struct AdvancedPwm<'d, T: AdvancedCaptureCompare16bitInstance> { pub ch_e: ChE, } -impl<'d, T: AdvancedCaptureCompare16bitInstance> AdvancedPwm<'d, T> { +impl<'d, T: HighResolutionCaptureCompare16bitInstance> AdvancedPwm<'d, T> { pub fn new( tim: impl Peripheral

+ 'd, _cha: Option>>, @@ -135,6 +147,7 @@ impl<'d, T: AdvancedCaptureCompare16bitInstance> AdvancedPwm<'d, T> { Self { _inner: tim, master: Master { phantom: PhantomData }, + burst_controller: BurstController { phantom: PhantomData }, ch_a: ChA { phantom: PhantomData }, ch_b: ChB { phantom: PhantomData }, ch_c: ChC { phantom: PhantomData }, @@ -142,54 +155,162 @@ impl<'d, T: AdvancedCaptureCompare16bitInstance> AdvancedPwm<'d, T> { ch_e: ChE { phantom: PhantomData }, } } +} - /// Set the dead time as a proportion of max_duty - pub fn set_dead_time(&mut self, _value: u16) { - todo!() - // let (ckd, value) = compute_dead_time_value(value); - // - // self.inner.set_dead_time_clock_division(ckd); - // self.inner.set_dead_time_value(value); +impl BurstController { + pub fn set_source(&mut self, source: Source) { + let regs = T::regs(); } } -// Represents a fixed-frequency bridge converter -pub struct BridgeConverter> { +/// Represents a fixed-frequency bridge converter +/// +/// Our implementation of the bridge converter uses a single channel and two compare registers, +/// allowing implementation of a synchronous buck or boost converter in continuous or discontinuous +/// conduction mode. +/// +/// It is important to remember that in synchronous topologies, energy can flow in reverse during +/// light loading conditions, and that the low-side switch must be active for a short time to drive +/// a bootstrapped high-side switch. +pub struct BridgeConverter> { phantom: PhantomData, pub ch: C, } -impl> BridgeConverter { +impl> BridgeConverter { pub fn new(channel: C, frequency: Hertz) -> Self { + use crate::pac::hrtim::vals::{Activeeffect, Cont, Inactiveeffect}; + + T::set_channel_frequency(C::raw(), frequency); + + // Always enable preload + T::regs().tim(C::raw()).cr().modify(|w| { + w.set_preen(true); + + // TODO: fix metapac + w.set_cont(Cont(1)); + }); + + // Set output 1 to active on a period event + T::regs() + .tim(C::raw()) + .setr(0) + .modify(|w| w.set_per(Activeeffect::SETACTIVE)); + + // Set output 1 to inactive on a compare 1 event + T::regs() + .tim(C::raw()) + .rstr(0) + .modify(|w| w.set_cmp(0, Inactiveeffect::SETINACTIVE)); + + // Set output 2 to active on a compare 1 event + T::regs() + .tim(C::raw()) + .setr(1) + .modify(|w| w.set_cmp(0, Activeeffect::SETACTIVE)); + + // Set output 2 to inactive on a compare 2 event + T::regs() + .tim(C::raw()) + .rstr(1) + .modify(|w| w.set_cmp(1, Inactiveeffect::SETINACTIVE)); + Self { phantom: PhantomData, ch: channel, } } - pub fn set_duty(&mut self, primary: u16, secondary: u16) { - let _ = T::regs(); - let _ = C::raw(); + pub fn start(&mut self) { + T::regs().mcr().modify(|w| w.set_tcen(C::raw(), true)); + } - todo!() + pub fn stop(&mut self) { + T::regs().mcr().modify(|w| w.set_tcen(C::raw(), false)); + } + + /// Set the dead time as a proportion of the maximum compare value + pub fn set_dead_time(&mut self, value: u16) { + T::set_channel_dead_time(C::raw(), value); + } + + /// Get the maximum compare value of a duty cycle + pub fn get_max_compare_value(&mut self) -> u16 { + T::regs().tim(C::raw()).per().read().per() + } + + /// The primary duty is the period in which the primary switch is active + /// + /// In the case of a buck converter, this is the high-side switch + /// In the case of a boost converter, this is the low-side switch + pub fn set_primary_duty(&mut self, primary: u16) { + T::regs().tim(C::raw()).cmp(0).modify(|w| w.set_cmp(primary)); + } + + /// The primary duty is the period in any switch is active + /// + /// If less than or equal to the primary duty, the secondary switch will never be active + pub fn set_secondary_duty(&mut self, secondary: u16) { + T::regs().tim(C::raw()).cmp(1).modify(|w| w.set_cmp(secondary)); } } -// Represents a variable-frequency resonant converter -pub struct ResonantConverter> { +/// Represents a variable-frequency resonant converter +/// +/// This implementation of a resonsant converter is appropriate for a half or full bridge, +/// but does not include secondary rectification, which is appropriate for applications +/// with a low-voltage on the secondary side. +pub struct ResonantConverter> { phantom: PhantomData, + min_period: u16, + max_period: u16, pub ch: C, } -impl> ResonantConverter { - pub fn new(channel: C, min_frequency: Hertz) -> Self { +impl> ResonantConverter { + pub fn new(channel: C, min_frequency: Hertz, max_frequency: Hertz) -> Self { + use crate::pac::hrtim::vals::Cont; + + T::set_channel_frequency(C::raw(), min_frequency); + + // Always enable preload + T::regs().tim(C::raw()).cr().modify(|w| { + w.set_preen(true); + + // TODO: fix metapac + w.set_cont(Cont(1)); + w.set_half(true); + }); + + // TODO: compute min period value + Self { + min_period: 0, + max_period: T::regs().tim(C::raw()).per().read().per(), phantom: PhantomData, ch: channel, } } - pub fn set_frequency(&mut self, frequency: Hertz) { - todo!() + /// Set the dead time as a proportion of the maximum compare value + pub fn set_dead_time(&mut self, value: u16) { + T::set_channel_dead_time(C::raw(), value); + } + + pub fn set_period(&mut self, period: u16) { + assert!(period < self.max_period); + assert!(period > self.min_period); + + T::regs().tim(C::raw()).per().modify(|w| w.set_per(period)); + } + + /// Get the minimum compare value of a duty cycle + pub fn get_min_period(&mut self) -> u16 { + self.min_period + } + + /// Get the maximum compare value of a duty cycle + pub fn get_max_period(&mut self) -> u16 { + self.max_period } } diff --git a/embassy-stm32/src/pwm/mod.rs b/embassy-stm32/src/pwm/mod.rs index 67009e78..2586dc15 100644 --- a/embassy-stm32/src/pwm/mod.rs +++ b/embassy-stm32/src/pwm/mod.rs @@ -76,11 +76,9 @@ pub(crate) enum HighResolutionControlPrescaler { } #[cfg(hrtim_v1)] -impl ops::Div for Hertz { - type Output = Hertz; - - fn div(self, rhs: HighResolutionControlPrescaler) -> Self::Output { - let divisor = match rhs { +impl From for u32 { + fn from(val: HighResolutionControlPrescaler) -> Self { + match val { HighResolutionControlPrescaler::Div1 => 1, HighResolutionControlPrescaler::Div2 => 2, HighResolutionControlPrescaler::Div4 => 4, @@ -89,9 +87,7 @@ impl ops::Div for Hertz { HighResolutionControlPrescaler::Div32 => 32, HighResolutionControlPrescaler::Div64 => 64, HighResolutionControlPrescaler::Div128 => 128, - }; - - Hertz(self.0 / divisor) + } } } @@ -111,9 +107,26 @@ impl From for u8 { } } +#[cfg(hrtim_v1)] +impl From for HighResolutionControlPrescaler { + fn from(val: u8) -> Self { + match val { + 0b000 => HighResolutionControlPrescaler::Div1, + 0b001 => HighResolutionControlPrescaler::Div2, + 0b010 => HighResolutionControlPrescaler::Div4, + 0b011 => HighResolutionControlPrescaler::Div8, + 0b100 => HighResolutionControlPrescaler::Div16, + 0b101 => HighResolutionControlPrescaler::Div32, + 0b110 => HighResolutionControlPrescaler::Div64, + 0b111 => HighResolutionControlPrescaler::Div128, + _ => unreachable!(), + } + } +} + #[cfg(hrtim_v1)] impl HighResolutionControlPrescaler { - pub fn compute_min(base_f: Hertz, frequency: Hertz) -> Self { + pub fn compute_min(val: u32) -> Self { *[ HighResolutionControlPrescaler::Div1, HighResolutionControlPrescaler::Div2, @@ -125,7 +138,7 @@ impl HighResolutionControlPrescaler { HighResolutionControlPrescaler::Div128, ] .iter() - .skip_while(|psc| frequency <= base_f / **psc) + .skip_while(|psc| >::into(**psc) <= val) .next() .unwrap() } @@ -135,11 +148,14 @@ pub(crate) mod sealed { use super::*; #[cfg(hrtim_v1)] - pub trait AdvancedCaptureCompare16bitInstance: crate::timer::sealed::HighResolutionControlInstance { + pub trait HighResolutionCaptureCompare16bitInstance: crate::timer::sealed::HighResolutionControlInstance { fn set_master_frequency(frequency: Hertz); fn set_channel_frequency(channnel: usize, frequency: Hertz); + /// Set the dead time as a proportion of max_duty + fn set_channel_dead_time(channnel: usize, dead_time: u16); + // fn enable_outputs(enable: bool); // // fn enable_channel(&mut self, channel: usize, enable: bool); @@ -178,7 +194,10 @@ pub(crate) mod sealed { } #[cfg(hrtim_v1)] -pub trait AdvancedCaptureCompare16bitInstance: sealed::AdvancedCaptureCompare16bitInstance + 'static {} +pub trait HighResolutionCaptureCompare16bitInstance: + sealed::HighResolutionCaptureCompare16bitInstance + 'static +{ +} pub trait CaptureCompare16bitInstance: sealed::CaptureCompare16bitInstance + crate::timer::GeneralPurpose16bitInstance + 'static @@ -343,20 +362,19 @@ foreach_interrupt! { }; ($inst:ident, hrtim, HRTIM, MASTER, $irq:ident) => { - impl crate::pwm::sealed::AdvancedCaptureCompare16bitInstance for crate::peripherals::$inst { + impl crate::pwm::sealed::HighResolutionCaptureCompare16bitInstance for crate::peripherals::$inst { fn set_master_frequency(frequency: Hertz) { use crate::rcc::sealed::RccPeripheral; use crate::timer::sealed::HighResolutionControlInstance; let f = frequency.0; - // TODO: fix frequency source - // let timer_f = Self::frequency().0; - let timer_f = Hertz(144_000_000).0; - let base_f = Hertz((32 * timer_f as u64 / u16::MAX as u64) as u32); - let psc = HighResolutionControlPrescaler::compute_min(base_f, frequency); + let timer_f = Self::frequency().0; + let psc_min = (timer_f / f) / (u16::MAX as u32 / 32); + let psc = HighResolutionControlPrescaler::compute_min(psc_min); - let psc_timer_f = Hertz(timer_f) / psc; - let per: u16 = (psc_timer_f / f).0 as u16; + let psc_val: u32 = psc.into(); + let timer_f = timer_f / psc_val; + let per: u16 = (timer_f / f) as u16; let regs = Self::regs(); @@ -369,23 +387,46 @@ foreach_interrupt! { use crate::timer::sealed::HighResolutionControlInstance; let f = frequency.0; - // TODO: fix frequency source - // let timer_f = Self::frequency().0; - let timer_f = Hertz(144_000_000).0; - let base_f = Hertz((32 * timer_f as u64 / u16::MAX as u64) as u32); - let psc = HighResolutionControlPrescaler::compute_min(base_f, frequency); + let timer_f = Self::frequency().0; + let psc_min = (timer_f / f) / (u16::MAX as u32 / 32); + let psc = HighResolutionControlPrescaler::compute_min(psc_min); - let psc_timer_f = Hertz(timer_f) / psc; - let per: u16 = (psc_timer_f / f).0 as u16; + let psc_val: u32 = psc.into(); + let timer_f = timer_f / psc_val; + let per: u16 = (timer_f / f) as u16; let regs = Self::regs(); regs.tim(channel).cr().modify(|w| w.set_ckpsc(psc.into())); regs.tim(channel).per().modify(|w| w.set_per(per)); } + + fn set_channel_dead_time(channel: usize, dead_time: u16) { + use crate::rcc::sealed::RccPeripheral; + use crate::timer::sealed::HighResolutionControlInstance; + + let regs = Self::regs(); + + let channel_psc: HighResolutionControlPrescaler = regs.tim(channel).cr().read().ckpsc().into(); + let psc_val: u32 = channel_psc.into(); + + + // The dead-time base clock runs 4 times slower than the hrtim base clock + // u9::MAX = 511 + let psc_min = (psc_val * dead_time as u32) / (4 * 511); + let psc = HighResolutionControlPrescaler::compute_min(psc_min); + let dt_psc_val: u32 = psc.into(); + let dt_val = (dt_psc_val * dead_time as u32) / (4 * psc_val); + + regs.tim(channel).dt().modify(|w| { + w.set_dtprsc(psc.into()); + w.set_dtf(dt_val as u16); + w.set_dtr(dt_val as u16); + }); + } } - impl AdvancedCaptureCompare16bitInstance for crate::peripherals::$inst { + impl HighResolutionCaptureCompare16bitInstance for crate::peripherals::$inst { } }; @@ -411,16 +452,16 @@ pin_trait!(BreakInput2Comparator2Pin, CaptureCompare16bitInstance); mod hrtim_pins { use super::*; - pin_trait!(ChannelAPin, AdvancedCaptureCompare16bitInstance); - pin_trait!(ChannelAComplementaryPin, AdvancedCaptureCompare16bitInstance); - pin_trait!(ChannelBPin, AdvancedCaptureCompare16bitInstance); - pin_trait!(ChannelBComplementaryPin, AdvancedCaptureCompare16bitInstance); - pin_trait!(ChannelCPin, AdvancedCaptureCompare16bitInstance); - pin_trait!(ChannelCComplementaryPin, AdvancedCaptureCompare16bitInstance); - pin_trait!(ChannelDPin, AdvancedCaptureCompare16bitInstance); - pin_trait!(ChannelDComplementaryPin, AdvancedCaptureCompare16bitInstance); - pin_trait!(ChannelEPin, AdvancedCaptureCompare16bitInstance); - pin_trait!(ChannelEComplementaryPin, AdvancedCaptureCompare16bitInstance); + pin_trait!(ChannelAPin, HighResolutionCaptureCompare16bitInstance); + pin_trait!(ChannelAComplementaryPin, HighResolutionCaptureCompare16bitInstance); + pin_trait!(ChannelBPin, HighResolutionCaptureCompare16bitInstance); + pin_trait!(ChannelBComplementaryPin, HighResolutionCaptureCompare16bitInstance); + pin_trait!(ChannelCPin, HighResolutionCaptureCompare16bitInstance); + pin_trait!(ChannelCComplementaryPin, HighResolutionCaptureCompare16bitInstance); + pin_trait!(ChannelDPin, HighResolutionCaptureCompare16bitInstance); + pin_trait!(ChannelDComplementaryPin, HighResolutionCaptureCompare16bitInstance); + pin_trait!(ChannelEPin, HighResolutionCaptureCompare16bitInstance); + pin_trait!(ChannelEComplementaryPin, HighResolutionCaptureCompare16bitInstance); } #[cfg(hrtim_v1)] diff --git a/examples/stm32f334/src/bin/pwm.rs b/examples/stm32f334/src/bin/pwm.rs index cc2ea861..20a62137 100644 --- a/examples/stm32f334/src/bin/pwm.rs +++ b/examples/stm32f334/src/bin/pwm.rs @@ -31,16 +31,17 @@ async fn main(_spawner: Spawner) { None, ); - pwm.set_dead_time(0); - let mut buck_converter = BridgeConverter::new(pwm.ch_a, khz(100)); - buck_converter.set_duty(0, u16::MAX); + buck_converter.set_primary_duty(0); + buck_converter.set_secondary_duty(0); + buck_converter.set_dead_time(0); // note: if the pins are not passed into the advanced pwm struct, they will not be output let mut boost_converter = BridgeConverter::new(pwm.ch_b, khz(100)); - boost_converter.set_duty(0, 0); + boost_converter.set_primary_duty(0); + boost_converter.set_secondary_duty(0); // let max = pwm.get_max_duty(); // pwm.set_dead_time(max / 1024); From c07854fed8f6ba38d418ef63853769a9af109bff Mon Sep 17 00:00:00 2001 From: xoviat Date: Fri, 30 Jun 2023 18:20:33 -0500 Subject: [PATCH 07/68] stm32/hrtim: minor fixes --- embassy-stm32/src/pwm/advanced_pwm.rs | 49 +++++++++++++++++++++------ 1 file changed, 38 insertions(+), 11 deletions(-) diff --git a/embassy-stm32/src/pwm/advanced_pwm.rs b/embassy-stm32/src/pwm/advanced_pwm.rs index 228899c0..7e595421 100644 --- a/embassy-stm32/src/pwm/advanced_pwm.rs +++ b/embassy-stm32/src/pwm/advanced_pwm.rs @@ -43,12 +43,12 @@ pub struct ChE { mod sealed { use crate::pwm::HighResolutionCaptureCompare16bitInstance; - pub trait AdvancedChannel {} + pub trait AdvancedChannel { + fn raw() -> usize; + } } -pub trait AdvancedChannel: sealed::AdvancedChannel { - fn raw() -> usize; -} +pub trait AdvancedChannel: sealed::AdvancedChannel {} pub struct PwmPin<'d, Perip, Channel> { _pin: PeripheralRef<'d, AnyPin>, @@ -94,12 +94,12 @@ macro_rules! advanced_channel_impl { } } - impl sealed::AdvancedChannel for $channel {} - impl AdvancedChannel for $channel { + impl sealed::AdvancedChannel for $channel { fn raw() -> usize { $ch_num } } + impl AdvancedChannel for $channel {} }; } @@ -158,8 +158,8 @@ impl<'d, T: HighResolutionCaptureCompare16bitInstance> AdvancedPwm<'d, T> { } impl BurstController { - pub fn set_source(&mut self, source: Source) { - let regs = T::regs(); + pub fn set_source(&mut self, _source: Source) { + todo!("burst mode control registers not implemented") } } @@ -229,6 +229,32 @@ impl> Bridge T::regs().mcr().modify(|w| w.set_tcen(C::raw(), false)); } + pub fn enable_burst_mode(&mut self) { + use crate::pac::hrtim::vals::{Idlem, Idles}; + + // TODO: fix metapac + T::regs().tim(C::raw()).outr().modify(|w| { + w.set_idlem(0, Idlem(1)); + w.set_idlem(1, Idlem(1)); + + w.set_idles(0, Idles(1)); + w.set_idles(1, Idles(1)); + }) + } + + pub fn disable_burst_mode(&mut self) { + use crate::pac::hrtim::vals::{Idlem, Idles}; + + // TODO: fix metapac + T::regs().tim(C::raw()).outr().modify(|w| { + w.set_idlem(0, Idlem(0)); + w.set_idlem(1, Idlem(0)); + + w.set_idles(0, Idles(0)); + w.set_idles(1, Idles(0)); + }) + } + /// Set the dead time as a proportion of the maximum compare value pub fn set_dead_time(&mut self, value: u16) { T::set_channel_dead_time(C::raw(), value); @@ -282,11 +308,12 @@ impl> Resona w.set_half(true); }); - // TODO: compute min period value + let max_period = T::regs().tim(C::raw()).per().read().per(); + let min_period = max_period * (min_frequency.0 / max_frequency.0) as u16; Self { - min_period: 0, - max_period: T::regs().tim(C::raw()).per().read().per(), + min_period: min_period, + max_period: max_period, phantom: PhantomData, ch: channel, } From 6e13f5b387d8f0f948b5874bc300925b015947d9 Mon Sep 17 00:00:00 2001 From: xoviat Date: Fri, 30 Jun 2023 18:33:22 -0500 Subject: [PATCH 08/68] rustfmt --- embassy-stm32/src/pwm/mod.rs | 4 ---- embassy-stm32/src/rcc/f3.rs | 1 + embassy-stm32/src/timer/mod.rs | 3 --- examples/stm32f334/src/bin/pwm.rs | 4 +--- 4 files changed, 2 insertions(+), 10 deletions(-) diff --git a/embassy-stm32/src/pwm/mod.rs b/embassy-stm32/src/pwm/mod.rs index 2586dc15..d09e38d0 100644 --- a/embassy-stm32/src/pwm/mod.rs +++ b/embassy-stm32/src/pwm/mod.rs @@ -3,9 +3,6 @@ pub mod advanced_pwm; pub mod complementary_pwm; pub mod simple_pwm; -#[cfg(hrtim_v1)] -use core::ops; - use stm32_metapac::timer::vals::Ckd; #[cfg(hrtim_v1)] @@ -402,7 +399,6 @@ foreach_interrupt! { } fn set_channel_dead_time(channel: usize, dead_time: u16) { - use crate::rcc::sealed::RccPeripheral; use crate::timer::sealed::HighResolutionControlInstance; let regs = Self::regs(); diff --git a/embassy-stm32/src/rcc/f3.rs b/embassy-stm32/src/rcc/f3.rs index 2deee80d..321270a7 100644 --- a/embassy-stm32/src/rcc/f3.rs +++ b/embassy-stm32/src/rcc/f3.rs @@ -264,6 +264,7 @@ fn calc_pll(config: &Config, Hertz(sysclk): Hertz) -> (Hertz, PllConfig) { } #[inline] +#[allow(unused_variables)] fn get_usb_pre(config: &Config, sysclk: u32, pclk1: u32, pll_config: &Option) -> Usbpre { cfg_if::cfg_if! { // Some chips do not have USB diff --git a/embassy-stm32/src/timer/mod.rs b/embassy-stm32/src/timer/mod.rs index 89cb1666..a92f854e 100644 --- a/embassy-stm32/src/timer/mod.rs +++ b/embassy-stm32/src/timer/mod.rs @@ -1,6 +1,3 @@ -#[cfg(hrtim_v1)] -use core::ops; - use stm32_metapac::timer::vals; use crate::interrupt; diff --git a/examples/stm32f334/src/bin/pwm.rs b/examples/stm32f334/src/bin/pwm.rs index 20a62137..1b5d509e 100644 --- a/examples/stm32f334/src/bin/pwm.rs +++ b/examples/stm32f334/src/bin/pwm.rs @@ -5,9 +5,7 @@ use defmt::*; use embassy_executor::Spawner; use embassy_stm32::pwm::advanced_pwm::*; -use embassy_stm32::pwm::Channel; use embassy_stm32::time::khz; -use embassy_time::{Duration, Timer}; use {defmt_rtt as _, panic_probe as _}; #[embassy_executor::main] @@ -17,7 +15,7 @@ async fn main(_spawner: Spawner) { let ch1 = PwmPin::new_cha(p.PA8); let ch1n = ComplementaryPwmPin::new_cha(p.PA9); - let mut pwm = AdvancedPwm::new( + let pwm = AdvancedPwm::new( p.HRTIM1, Some(ch1), Some(ch1n), From 21a86531955facbdba5583b5218e20f1ca6dc23e Mon Sep 17 00:00:00 2001 From: xoviat Date: Sat, 1 Jul 2023 17:32:16 -0500 Subject: [PATCH 09/68] hrtim: minor cleanup --- embassy-stm32/Cargo.toml | 2 +- embassy-stm32/src/pwm/advanced_pwm.rs | 28 ++++++++++++++------------- examples/stm32f334/.cargo/config.toml | 2 +- 3 files changed, 17 insertions(+), 15 deletions(-) diff --git a/embassy-stm32/Cargo.toml b/embassy-stm32/Cargo.toml index 3d9ee826..551c0969 100644 --- a/embassy-stm32/Cargo.toml +++ b/embassy-stm32/Cargo.toml @@ -57,7 +57,7 @@ sdio-host = "0.5.0" embedded-sdmmc = { git = "https://github.com/embassy-rs/embedded-sdmmc-rs", rev = "a4f293d3a6f72158385f79c98634cb8a14d0d2fc", optional = true } critical-section = "1.1" atomic-polyfill = "1.0.1" -stm32-metapac = "10" +stm32-metapac = "12" vcell = "0.1.3" bxcan = "0.7.0" nb = "1.0.0" diff --git a/embassy-stm32/src/pwm/advanced_pwm.rs b/embassy-stm32/src/pwm/advanced_pwm.rs index 7e595421..65f4e7ca 100644 --- a/embassy-stm32/src/pwm/advanced_pwm.rs +++ b/embassy-stm32/src/pwm/advanced_pwm.rs @@ -187,8 +187,12 @@ impl> Bridge T::regs().tim(C::raw()).cr().modify(|w| { w.set_preen(true); - // TODO: fix metapac - w.set_cont(Cont(1)); + w.set_cont(Cont::CONTINUOUS); + }); + + T::regs().oenr().modify(|w| { + w.set_t1oen(C::raw(), true); + w.set_t2oen(C::raw(), true); }); // Set output 1 to active on a period event @@ -234,24 +238,23 @@ impl> Bridge // TODO: fix metapac T::regs().tim(C::raw()).outr().modify(|w| { - w.set_idlem(0, Idlem(1)); - w.set_idlem(1, Idlem(1)); + w.set_idlem(0, Idlem::SETIDLE); + w.set_idlem(1, Idlem::SETIDLE); - w.set_idles(0, Idles(1)); - w.set_idles(1, Idles(1)); + w.set_idles(0, Idles::INACTIVE); + w.set_idles(1, Idles::INACTIVE); }) } pub fn disable_burst_mode(&mut self) { use crate::pac::hrtim::vals::{Idlem, Idles}; - // TODO: fix metapac T::regs().tim(C::raw()).outr().modify(|w| { - w.set_idlem(0, Idlem(0)); - w.set_idlem(1, Idlem(0)); + w.set_idlem(0, Idlem::NOEFFECT); + w.set_idlem(1, Idlem::NOEFFECT); - w.set_idles(0, Idles(0)); - w.set_idles(1, Idles(0)); + w.set_idles(0, Idles::INACTIVE); + w.set_idles(1, Idles::INACTIVE); }) } @@ -303,8 +306,7 @@ impl> Resona T::regs().tim(C::raw()).cr().modify(|w| { w.set_preen(true); - // TODO: fix metapac - w.set_cont(Cont(1)); + w.set_cont(Cont::CONTINUOUS); w.set_half(true); }); diff --git a/examples/stm32f334/.cargo/config.toml b/examples/stm32f334/.cargo/config.toml index 7f3fda52..caf947be 100644 --- a/examples/stm32f334/.cargo/config.toml +++ b/examples/stm32f334/.cargo/config.toml @@ -1,6 +1,6 @@ [target.'cfg(all(target_arch = "arm", target_os = "none"))'] # replace STM32F429ZITx with your chip as listed in `probe-rs-cli chip list` -runner = "probe-rs-cli run --chip STM32F303ZETx" +runner = "probe-run --chip STM32F334R8" [build] target = "thumbv7em-none-eabihf" From aceba1c03fc45179d4e910fad254a31191cf0c44 Mon Sep 17 00:00:00 2001 From: xoviat Date: Sat, 1 Jul 2023 21:47:44 -0500 Subject: [PATCH 10/68] hrtim: fix example and auto adjust psc. --- embassy-stm32/src/pwm/advanced_pwm.rs | 87 ++++++++++++++++++++------- embassy-stm32/src/pwm/mod.rs | 37 ++++++++++-- examples/stm32f334/src/bin/button.rs | 27 +++++++++ examples/stm32f334/src/bin/pwm.rs | 65 +++++++++++--------- 4 files changed, 160 insertions(+), 56 deletions(-) create mode 100644 examples/stm32f334/src/bin/button.rs diff --git a/embassy-stm32/src/pwm/advanced_pwm.rs b/embassy-stm32/src/pwm/advanced_pwm.rs index 65f4e7ca..fa34b2e1 100644 --- a/embassy-stm32/src/pwm/advanced_pwm.rs +++ b/embassy-stm32/src/pwm/advanced_pwm.rs @@ -144,6 +144,18 @@ impl<'d, T: HighResolutionCaptureCompare16bitInstance> AdvancedPwm<'d, T> { T::enable(); ::reset(); + // // Enable and and stabilize the DLL + // T::regs().dllcr().modify(|w| { + // // w.set_calen(true); + // // w.set_calrte(11); + // w.set_cal(true); + // }); + // + // debug!("wait for dll calibration"); + // while !T::regs().isr().read().dllrdy() {} + // + // debug!("dll calibration complete"); + Self { _inner: tim, master: Master { phantom: PhantomData }, @@ -173,12 +185,14 @@ impl BurstController { /// light loading conditions, and that the low-side switch must be active for a short time to drive /// a bootstrapped high-side switch. pub struct BridgeConverter> { - phantom: PhantomData, - pub ch: C, + timer: PhantomData, + channel: PhantomData, + dead_time: u16, + primary_duty: u16, } impl> BridgeConverter { - pub fn new(channel: C, frequency: Hertz) -> Self { + pub fn new(_channel: C, frequency: Hertz) -> Self { use crate::pac::hrtim::vals::{Activeeffect, Cont, Inactiveeffect}; T::set_channel_frequency(C::raw(), frequency); @@ -186,15 +200,21 @@ impl> Bridge // Always enable preload T::regs().tim(C::raw()).cr().modify(|w| { w.set_preen(true); + w.set_repu(true); w.set_cont(Cont::CONTINUOUS); }); + // Enable timer outputs T::regs().oenr().modify(|w| { w.set_t1oen(C::raw(), true); w.set_t2oen(C::raw(), true); }); + // The dead-time generation unit cannot be used because it forces the other output + // to be completely complementary to the first output, which restricts certain waveforms + // Therefore, software-implemented dead time must be used when setting the duty cycles + // Set output 1 to active on a period event T::regs() .tim(C::raw()) @@ -207,21 +227,23 @@ impl> Bridge .rstr(0) .modify(|w| w.set_cmp(0, Inactiveeffect::SETINACTIVE)); - // Set output 2 to active on a compare 1 event + // Set output 2 to active on a compare 2 event T::regs() .tim(C::raw()) .setr(1) - .modify(|w| w.set_cmp(0, Activeeffect::SETACTIVE)); + .modify(|w| w.set_cmp(1, Activeeffect::SETACTIVE)); - // Set output 2 to inactive on a compare 2 event + // Set output 2 to inactive on a compare 3 event T::regs() .tim(C::raw()) .rstr(1) - .modify(|w| w.set_cmp(1, Inactiveeffect::SETINACTIVE)); + .modify(|w| w.set_cmp(2, Inactiveeffect::SETINACTIVE)); Self { - phantom: PhantomData, - ch: channel, + timer: PhantomData, + channel: PhantomData, + dead_time: 0, + primary_duty: 0, } } @@ -236,7 +258,6 @@ impl> Bridge pub fn enable_burst_mode(&mut self) { use crate::pac::hrtim::vals::{Idlem, Idles}; - // TODO: fix metapac T::regs().tim(C::raw()).outr().modify(|w| { w.set_idlem(0, Idlem::SETIDLE); w.set_idlem(1, Idlem::SETIDLE); @@ -258,9 +279,18 @@ impl> Bridge }) } + fn update_primary_duty_or_dead_time(&mut self) { + T::regs().tim(C::raw()).cmp(0).modify(|w| w.set_cmp(self.primary_duty)); + T::regs() + .tim(C::raw()) + .cmp(1) + .modify(|w| w.set_cmp(self.primary_duty + self.dead_time)); + } + /// Set the dead time as a proportion of the maximum compare value - pub fn set_dead_time(&mut self, value: u16) { - T::set_channel_dead_time(C::raw(), value); + pub fn set_dead_time(&mut self, dead_time: u16) { + self.dead_time = dead_time; + self.update_primary_duty_or_dead_time(); } /// Get the maximum compare value of a duty cycle @@ -272,15 +302,17 @@ impl> Bridge /// /// In the case of a buck converter, this is the high-side switch /// In the case of a boost converter, this is the low-side switch - pub fn set_primary_duty(&mut self, primary: u16) { - T::regs().tim(C::raw()).cmp(0).modify(|w| w.set_cmp(primary)); + pub fn set_primary_duty(&mut self, primary_duty: u16) { + self.primary_duty = primary_duty; + self.update_primary_duty_or_dead_time(); } - /// The primary duty is the period in any switch is active + /// The secondary duty is the period in any switch is active /// /// If less than or equal to the primary duty, the secondary switch will never be active - pub fn set_secondary_duty(&mut self, secondary: u16) { - T::regs().tim(C::raw()).cmp(1).modify(|w| w.set_cmp(secondary)); + /// If a fully complementary output is desired, the secondary duty can be set to the max compare + pub fn set_secondary_duty(&mut self, secondary_duty: u16) { + T::regs().tim(C::raw()).cmp(2).modify(|w| w.set_cmp(secondary_duty)); } } @@ -290,14 +322,14 @@ impl> Bridge /// but does not include secondary rectification, which is appropriate for applications /// with a low-voltage on the secondary side. pub struct ResonantConverter> { - phantom: PhantomData, + timer: PhantomData, + channel: PhantomData, min_period: u16, max_period: u16, - pub ch: C, } impl> ResonantConverter { - pub fn new(channel: C, min_frequency: Hertz, max_frequency: Hertz) -> Self { + pub fn new(_channel: C, min_frequency: Hertz, max_frequency: Hertz) -> Self { use crate::pac::hrtim::vals::Cont; T::set_channel_frequency(C::raw(), min_frequency); @@ -305,19 +337,30 @@ impl> Resona // Always enable preload T::regs().tim(C::raw()).cr().modify(|w| { w.set_preen(true); + w.set_repu(true); w.set_cont(Cont::CONTINUOUS); w.set_half(true); }); + // Enable timer outputs + T::regs().oenr().modify(|w| { + w.set_t1oen(C::raw(), true); + w.set_t2oen(C::raw(), true); + }); + + // Dead-time generator can be used in this case because the primary fets + // of a resonant converter are always complementary + T::regs().tim(C::raw()).outr().modify(|w| w.set_dten(true)); + let max_period = T::regs().tim(C::raw()).per().read().per(); let min_period = max_period * (min_frequency.0 / max_frequency.0) as u16; Self { + timer: PhantomData, + channel: PhantomData, min_period: min_period, max_period: max_period, - phantom: PhantomData, - ch: channel, } } diff --git a/embassy-stm32/src/pwm/mod.rs b/embassy-stm32/src/pwm/mod.rs index d09e38d0..429a290e 100644 --- a/embassy-stm32/src/pwm/mod.rs +++ b/embassy-stm32/src/pwm/mod.rs @@ -123,7 +123,7 @@ impl From for HighResolutionControlPrescaler { #[cfg(hrtim_v1)] impl HighResolutionControlPrescaler { - pub fn compute_min(val: u32) -> Self { + pub fn compute_min_high_res(val: u32) -> Self { *[ HighResolutionControlPrescaler::Div1, HighResolutionControlPrescaler::Div2, @@ -139,6 +139,18 @@ impl HighResolutionControlPrescaler { .next() .unwrap() } + + pub fn compute_min_low_res(val: u32) -> Self { + *[ + HighResolutionControlPrescaler::Div32, + HighResolutionControlPrescaler::Div64, + HighResolutionControlPrescaler::Div128, + ] + .iter() + .skip_while(|psc| >::into(**psc) <= val) + .next() + .unwrap() + } } pub(crate) mod sealed { @@ -367,10 +379,14 @@ foreach_interrupt! { let f = frequency.0; let timer_f = Self::frequency().0; let psc_min = (timer_f / f) / (u16::MAX as u32 / 32); - let psc = HighResolutionControlPrescaler::compute_min(psc_min); + let psc = if Self::regs().isr().read().dllrdy() { + HighResolutionControlPrescaler::compute_min_high_res(psc_min) + } else { + HighResolutionControlPrescaler::compute_min_low_res(psc_min) + }; let psc_val: u32 = psc.into(); - let timer_f = timer_f / psc_val; + let timer_f = 32 * (timer_f / psc_val); let per: u16 = (timer_f / f) as u16; let regs = Self::regs(); @@ -386,10 +402,14 @@ foreach_interrupt! { let f = frequency.0; let timer_f = Self::frequency().0; let psc_min = (timer_f / f) / (u16::MAX as u32 / 32); - let psc = HighResolutionControlPrescaler::compute_min(psc_min); + let psc = if Self::regs().isr().read().dllrdy() { + HighResolutionControlPrescaler::compute_min_high_res(psc_min) + } else { + HighResolutionControlPrescaler::compute_min_low_res(psc_min) + }; let psc_val: u32 = psc.into(); - let timer_f = timer_f / psc_val; + let timer_f = 32 * (timer_f / psc_val); let per: u16 = (timer_f / f) as u16; let regs = Self::regs(); @@ -410,7 +430,12 @@ foreach_interrupt! { // The dead-time base clock runs 4 times slower than the hrtim base clock // u9::MAX = 511 let psc_min = (psc_val * dead_time as u32) / (4 * 511); - let psc = HighResolutionControlPrescaler::compute_min(psc_min); + let psc = if Self::regs().isr().read().dllrdy() { + HighResolutionControlPrescaler::compute_min_high_res(psc_min) + } else { + HighResolutionControlPrescaler::compute_min_low_res(psc_min) + }; + let dt_psc_val: u32 = psc.into(); let dt_val = (dt_psc_val * dead_time as u32) / (4 * psc_val); diff --git a/examples/stm32f334/src/bin/button.rs b/examples/stm32f334/src/bin/button.rs new file mode 100644 index 00000000..599c0f27 --- /dev/null +++ b/examples/stm32f334/src/bin/button.rs @@ -0,0 +1,27 @@ +#![no_std] +#![no_main] +#![feature(type_alias_impl_trait)] + +use defmt::*; +use embassy_executor::Spawner; +use embassy_stm32::gpio::{Level, Output, Speed}; +use embassy_time::{Duration, Timer}; +use {defmt_rtt as _, panic_probe as _}; + +#[embassy_executor::main] +async fn main(_spawner: Spawner) { + info!("Hello World!"); + + let p = embassy_stm32::init(Default::default()); + + let mut out1 = Output::new(p.PA8, Level::Low, Speed::High); + + out1.set_high(); + Timer::after(Duration::from_millis(500)).await; + out1.set_low(); + + Timer::after(Duration::from_millis(500)).await; + info!("end program"); + + cortex_m::asm::bkpt(); +} diff --git a/examples/stm32f334/src/bin/pwm.rs b/examples/stm32f334/src/bin/pwm.rs index 1b5d509e..36411974 100644 --- a/examples/stm32f334/src/bin/pwm.rs +++ b/examples/stm32f334/src/bin/pwm.rs @@ -5,12 +5,20 @@ use defmt::*; use embassy_executor::Spawner; use embassy_stm32::pwm::advanced_pwm::*; -use embassy_stm32::time::khz; +use embassy_stm32::time::{khz, mhz}; +use embassy_stm32::Config; +use embassy_time::{Duration, Timer}; use {defmt_rtt as _, panic_probe as _}; #[embassy_executor::main] async fn main(_spawner: Spawner) { - let p = embassy_stm32::init(Default::default()); + let mut config: Config = Default::default(); + config.rcc.sysclk = Some(mhz(64)); + config.rcc.hclk = Some(mhz(64)); + config.rcc.pclk1 = Some(mhz(32)); + config.rcc.pclk2 = Some(mhz(64)); + + let p = embassy_stm32::init(config); info!("Hello World!"); let ch1 = PwmPin::new_cha(p.PA8); @@ -29,34 +37,35 @@ async fn main(_spawner: Spawner) { None, ); - let mut buck_converter = BridgeConverter::new(pwm.ch_a, khz(100)); + info!("pwm constructed"); - buck_converter.set_primary_duty(0); - buck_converter.set_secondary_duty(0); - buck_converter.set_dead_time(0); + let mut buck_converter = BridgeConverter::new(pwm.ch_a, khz(5)); - // note: if the pins are not passed into the advanced pwm struct, they will not be output - let mut boost_converter = BridgeConverter::new(pwm.ch_b, khz(100)); - - boost_converter.set_primary_duty(0); - boost_converter.set_secondary_duty(0); - - // let max = pwm.get_max_duty(); - // pwm.set_dead_time(max / 1024); + // embassy_stm32::pac::HRTIM1 + // .tim(0) + // .setr(0) + // .modify(|w| w.set_sst(Activeeffect::SETACTIVE)); // - // pwm.enable(Channel::Ch1); + // Timer::after(Duration::from_millis(500)).await; // - // info!("PWM initialized"); - // info!("PWM max duty {}", max); - // - // loop { - // pwm.set_duty(Channel::Ch1, 0); - // Timer::after(Duration::from_millis(300)).await; - // pwm.set_duty(Channel::Ch1, max / 4); - // Timer::after(Duration::from_millis(300)).await; - // pwm.set_duty(Channel::Ch1, max / 2); - // Timer::after(Duration::from_millis(300)).await; - // pwm.set_duty(Channel::Ch1, max - 1); - // Timer::after(Duration::from_millis(300)).await; - // } + // embassy_stm32::pac::HRTIM1 + // .tim(0) + // .rstr(0) + // .modify(|w| w.set_srt(Inactiveeffect::SETINACTIVE)); + + let max_duty = buck_converter.get_max_compare_value(); + + info!("max compare value: {}", max_duty); + + buck_converter.set_dead_time(max_duty / 20); + buck_converter.set_primary_duty(max_duty / 2); + buck_converter.set_secondary_duty(3 * max_duty / 4); + + buck_converter.start(); + + Timer::after(Duration::from_millis(500)).await; + + info!("end program"); + + cortex_m::asm::bkpt(); } From 2e6b813225450828ebeb9908048675db0e843330 Mon Sep 17 00:00:00 2001 From: xoviat Date: Sun, 2 Jul 2023 09:17:12 -0500 Subject: [PATCH 11/68] hrtim: add guardrails on bridge sec. duty --- embassy-stm32/src/pwm/advanced_pwm.rs | 21 ++++++++++++++++++--- 1 file changed, 18 insertions(+), 3 deletions(-) diff --git a/embassy-stm32/src/pwm/advanced_pwm.rs b/embassy-stm32/src/pwm/advanced_pwm.rs index fa34b2e1..07d8708d 100644 --- a/embassy-stm32/src/pwm/advanced_pwm.rs +++ b/embassy-stm32/src/pwm/advanced_pwm.rs @@ -177,7 +177,7 @@ impl BurstController { /// Represents a fixed-frequency bridge converter /// -/// Our implementation of the bridge converter uses a single channel and two compare registers, +/// Our implementation of the bridge converter uses a single channel and three compare registers, /// allowing implementation of a synchronous buck or boost converter in continuous or discontinuous /// conduction mode. /// @@ -189,6 +189,8 @@ pub struct BridgeConverter, dead_time: u16, primary_duty: u16, + min_secondary_duty: u16, + max_secondary_duty: u16, } impl> BridgeConverter { @@ -244,6 +246,8 @@ impl> Bridge channel: PhantomData, dead_time: 0, primary_duty: 0, + min_secondary_duty: 0, + max_secondary_duty: 0, } } @@ -280,16 +284,19 @@ impl> Bridge } fn update_primary_duty_or_dead_time(&mut self) { + self.min_secondary_duty = self.primary_duty + self.dead_time; + T::regs().tim(C::raw()).cmp(0).modify(|w| w.set_cmp(self.primary_duty)); T::regs() .tim(C::raw()) .cmp(1) - .modify(|w| w.set_cmp(self.primary_duty + self.dead_time)); + .modify(|w| w.set_cmp(self.min_secondary_duty)); } /// Set the dead time as a proportion of the maximum compare value pub fn set_dead_time(&mut self, dead_time: u16) { self.dead_time = dead_time; + self.max_secondary_duty = self.get_max_compare_value() - dead_time; self.update_primary_duty_or_dead_time(); } @@ -309,9 +316,17 @@ impl> Bridge /// The secondary duty is the period in any switch is active /// - /// If less than or equal to the primary duty, the secondary switch will never be active + /// If less than or equal to the primary duty, the secondary switch will be active for one tick /// If a fully complementary output is desired, the secondary duty can be set to the max compare pub fn set_secondary_duty(&mut self, secondary_duty: u16) { + let secondary_duty = if secondary_duty > self.max_secondary_duty { + self.max_secondary_duty + } else if secondary_duty <= self.min_secondary_duty { + self.min_secondary_duty + 1 + } else { + secondary_duty + }; + T::regs().tim(C::raw()).cmp(2).modify(|w| w.set_cmp(secondary_duty)); } } From 0c49e6747c02e03fc6517969105cfdf20239fc64 Mon Sep 17 00:00:00 2001 From: xoviat Date: Sun, 2 Jul 2023 22:00:50 -0500 Subject: [PATCH 12/68] wip --- embassy-stm32/src/pwm/advanced_pwm.rs | 31 +++++++++++---------------- 1 file changed, 12 insertions(+), 19 deletions(-) diff --git a/embassy-stm32/src/pwm/advanced_pwm.rs b/embassy-stm32/src/pwm/advanced_pwm.rs index 07d8708d..9e40c5bf 100644 --- a/embassy-stm32/src/pwm/advanced_pwm.rs +++ b/embassy-stm32/src/pwm/advanced_pwm.rs @@ -195,7 +195,7 @@ pub struct BridgeConverter> BridgeConverter { pub fn new(_channel: C, frequency: Hertz) -> Self { - use crate::pac::hrtim::vals::{Activeeffect, Cont, Inactiveeffect}; + use crate::pac::hrtim::vals::{Activeeffect, Inactiveeffect}; T::set_channel_frequency(C::raw(), frequency); @@ -203,8 +203,7 @@ impl> Bridge T::regs().tim(C::raw()).cr().modify(|w| { w.set_preen(true); w.set_repu(true); - - w.set_cont(Cont::CONTINUOUS); + w.set_cont(true); }); // Enable timer outputs @@ -260,26 +259,22 @@ impl> Bridge } pub fn enable_burst_mode(&mut self) { - use crate::pac::hrtim::vals::{Idlem, Idles}; - T::regs().tim(C::raw()).outr().modify(|w| { - w.set_idlem(0, Idlem::SETIDLE); - w.set_idlem(1, Idlem::SETIDLE); + // Enable Burst Mode + w.set_idlem(0, true); + w.set_idlem(1, true); - w.set_idles(0, Idles::INACTIVE); - w.set_idles(1, Idles::INACTIVE); + // Set output to active during the burst + w.set_idles(0, true); + w.set_idles(1, true); }) } pub fn disable_burst_mode(&mut self) { - use crate::pac::hrtim::vals::{Idlem, Idles}; - T::regs().tim(C::raw()).outr().modify(|w| { - w.set_idlem(0, Idlem::NOEFFECT); - w.set_idlem(1, Idlem::NOEFFECT); - - w.set_idles(0, Idles::INACTIVE); - w.set_idles(1, Idles::INACTIVE); + // Disable Burst Mode + w.set_idlem(0, false); + w.set_idlem(1, false); }) } @@ -345,8 +340,6 @@ pub struct ResonantConverter> ResonantConverter { pub fn new(_channel: C, min_frequency: Hertz, max_frequency: Hertz) -> Self { - use crate::pac::hrtim::vals::Cont; - T::set_channel_frequency(C::raw(), min_frequency); // Always enable preload @@ -354,7 +347,7 @@ impl> Resona w.set_preen(true); w.set_repu(true); - w.set_cont(Cont::CONTINUOUS); + w.set_cont(true); w.set_half(true); }); From 4d6b3c57b1a0b99409cae743a102408f23ca828b Mon Sep 17 00:00:00 2001 From: pennae Date: Thu, 20 Jul 2023 16:08:59 +0200 Subject: [PATCH 13/68] rp: fix multicore stack guard setup the region field of the register is four bits wide followed by the valid bit that causes the rnr update we rely on for the rasr write. 0x08 is just a bit short to reach the valid bit, and since rp2040 has only 8 regions it (at best) doesn't do anything at all. --- embassy-rp/src/multicore.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/embassy-rp/src/multicore.rs b/embassy-rp/src/multicore.rs index 468e8470..89a2680a 100644 --- a/embassy-rp/src/multicore.rs +++ b/embassy-rp/src/multicore.rs @@ -74,7 +74,7 @@ fn install_stack_guard(stack_bottom: *mut usize) { let subregion_select = 0xff ^ (1 << ((addr >> 5) & 7)); unsafe { core.MPU.ctrl.write(5); // enable mpu with background default map - core.MPU.rbar.write((addr & !0xff) | 0x8); + core.MPU.rbar.write((addr & !0xff) | (1 << 4)); // set address and update RNR core.MPU.rasr.write( 1 // enable region | (0x7 << 1) // size 2^(7 + 1) = 256 From e9445ec72d8713aa7705e0a727c13c259d529e71 Mon Sep 17 00:00:00 2001 From: pennae Date: Thu, 20 Jul 2023 16:57:54 +0200 Subject: [PATCH 14/68] rp: allow for MPU-based stack guards on core 0 as well using these will require some linker script intervention. setting the core0 stack needs linker intervention anyway (to provide _stack_start), having it also provide _stack_end for the guard to use is not that much of a stretch. --- embassy-rp/src/lib.rs | 68 +++++++++++++++++++++++++++++++++++++ embassy-rp/src/multicore.rs | 33 ++++-------------- 2 files changed, 74 insertions(+), 27 deletions(-) diff --git a/embassy-rp/src/lib.rs b/embassy-rp/src/lib.rs index 4f205a16..50f028d4 100644 --- a/embassy-rp/src/lib.rs +++ b/embassy-rp/src/lib.rs @@ -219,6 +219,74 @@ select_bootloader! { default => BOOT_LOADER_W25Q080 } +/// Installs a stack guard for the CORE0 stack in MPU region 0. +/// Will fail if the MPU is already confgigured. This function requires +/// a `_stack_end` symbol to be defined by the linker script, and expexcts +/// `_stack_end` to be located at the lowest address (largest depth) of +/// the stack. +/// +/// This method can *only* set up stack guards on the currently +/// executing core. Stack guards for CORE1 are set up automatically, +/// only CORE0 should ever use this. +/// +/// # Usage +/// +/// ```no_run +/// #![feature(type_alias_impl_trait)] +/// use embassy_rp::install_core0_stack_guard; +/// use embassy_executor::{Executor, Spawner}; +/// +/// #[embassy_executor::main] +/// async fn main(_spawner: Spawner) { +/// // set up by the linker as follows: +/// // +/// // MEMORY { +/// // STACK0: ORIGIN = 0x20040000, LENGTH = 4K +/// // } +/// // +/// // _stack_end = ORIGIN(STACK0); +/// // _stack_start = _stack_end + LENGTH(STACK0); +/// // +/// install_core0_stack_guard().expect("MPU already configured"); +/// let p = embassy_rp::init(Default::default()); +/// +/// // ... +/// } +/// ``` +pub fn install_core0_stack_guard() -> Result<(), ()> { + extern "C" { + static mut _stack_end: usize; + } + unsafe { install_stack_guard(&mut _stack_end as *mut usize) } +} + +#[inline(always)] +fn install_stack_guard(stack_bottom: *mut usize) -> Result<(), ()> { + let core = unsafe { cortex_m::Peripherals::steal() }; + + // Fail if MPU is already configured + if core.MPU.ctrl.read() != 0 { + return Err(()); + } + + // The minimum we can protect is 32 bytes on a 32 byte boundary, so round up which will + // just shorten the valid stack range a tad. + let addr = (stack_bottom as u32 + 31) & !31; + // Mask is 1 bit per 32 bytes of the 256 byte range... clear the bit for the segment we want + let subregion_select = 0xff ^ (1 << ((addr >> 5) & 7)); + unsafe { + core.MPU.ctrl.write(5); // enable mpu with background default map + core.MPU.rbar.write((addr & !0xff) | (1 << 4)); // set address and update RNR + core.MPU.rasr.write( + 1 // enable region + | (0x7 << 1) // size 2^(7 + 1) = 256 + | (subregion_select << 8) + | 0x10000000, // XN = disable instruction fetch; no other bits means no permissions + ); + } + Ok(()) +} + pub mod config { use crate::clocks::ClockConfig; diff --git a/embassy-rp/src/multicore.rs b/embassy-rp/src/multicore.rs index 89a2680a..91576180 100644 --- a/embassy-rp/src/multicore.rs +++ b/embassy-rp/src/multicore.rs @@ -52,41 +52,20 @@ use core::sync::atomic::{compiler_fence, AtomicBool, Ordering}; use crate::interrupt::InterruptExt; use crate::peripherals::CORE1; -use crate::{gpio, interrupt, pac}; +use crate::{gpio, install_stack_guard, interrupt, pac}; const PAUSE_TOKEN: u32 = 0xDEADBEEF; const RESUME_TOKEN: u32 = !0xDEADBEEF; static IS_CORE1_INIT: AtomicBool = AtomicBool::new(false); #[inline(always)] -fn install_stack_guard(stack_bottom: *mut usize) { - let core = unsafe { cortex_m::Peripherals::steal() }; - - // Trap if MPU is already configured - if core.MPU.ctrl.read() != 0 { +fn core1_setup(stack_bottom: *mut usize) { + if let Err(_) = install_stack_guard(stack_bottom) { + // currently only happens if the MPU was already set up, which + // would indicate that the core is already in use from outside + // embassy, somehow. trap if so since we can't deal with that. cortex_m::asm::udf(); } - - // The minimum we can protect is 32 bytes on a 32 byte boundary, so round up which will - // just shorten the valid stack range a tad. - let addr = (stack_bottom as u32 + 31) & !31; - // Mask is 1 bit per 32 bytes of the 256 byte range... clear the bit for the segment we want - let subregion_select = 0xff ^ (1 << ((addr >> 5) & 7)); - unsafe { - core.MPU.ctrl.write(5); // enable mpu with background default map - core.MPU.rbar.write((addr & !0xff) | (1 << 4)); // set address and update RNR - core.MPU.rasr.write( - 1 // enable region - | (0x7 << 1) // size 2^(7 + 1) = 256 - | (subregion_select << 8) - | 0x10000000, // XN = disable instruction fetch; no other bits means no permissions - ); - } -} - -#[inline(always)] -fn core1_setup(stack_bottom: *mut usize) { - install_stack_guard(stack_bottom); unsafe { gpio::init(); } From 4883fdd1549e8d43290b39d72c3b311d300010ea Mon Sep 17 00:00:00 2001 From: Alex Ferro Date: Sat, 22 Jul 2023 17:17:01 -0600 Subject: [PATCH 15/68] Add a STM32/DMARingBuffer::read_exact helper This provides a helper function with an async implementation, that will only return (or error) when it was able to read that many bytes, sleeping until ready. Additionally, corrected the documentation for Ringbuffer functions to use "elements" instead of "bytes" as the types were already generic over the word/element size. --- embassy-stm32/src/dma/bdma.rs | 44 +++++++++++++++++++++++++++-- embassy-stm32/src/dma/dma.rs | 44 +++++++++++++++++++++++++++-- embassy-stm32/src/dma/ringbuffer.rs | 18 ++++++------ 3 files changed, 91 insertions(+), 15 deletions(-) diff --git a/embassy-stm32/src/dma/bdma.rs b/embassy-stm32/src/dma/bdma.rs index 5a87888b..68c78123 100644 --- a/embassy-stm32/src/dma/bdma.rs +++ b/embassy-stm32/src/dma/bdma.rs @@ -466,15 +466,53 @@ impl<'a, C: Channel, W: Word> RingBuffer<'a, C, W> { self.ringbuf.clear(DmaCtrlImpl(self.channel.reborrow())); } - /// Read bytes from the ring buffer + /// Read elements from the ring buffer /// Return a tuple of the length read and the length remaining in the buffer - /// If not all of the bytes were read, then there will be some bytes in the buffer remaining - /// The length remaining is the capacity, ring_buf.len(), less the bytes remaining after the read + /// If not all of the elements were read, then there will be some elements in the buffer remaining + /// The length remaining is the capacity, ring_buf.len(), less the elements remaining after the read /// OverrunError is returned if the portion to be read was overwritten by the DMA controller. pub fn read(&mut self, buf: &mut [W]) -> Result<(usize, usize), OverrunError> { self.ringbuf.read(DmaCtrlImpl(self.channel.reborrow()), buf) } + /// Read an exact number of elements from the ringbuffer. + /// + /// Returns the remaining number of elements available for immediate reading. + /// OverrunError is returned if the portion to be read was overwritten by the DMA controller. + /// + /// Async/Wake Behavior: + /// The underlying DMA peripheral only can wake us when its buffer pointer has reached the halfway point, + /// and when it wraps around. This means that when called with a buffer of length 'M', when this + /// ring buffer was created with a buffer of size 'N': + /// - If M equals N/2 or N/2 divides evenly into M, this function will return every N/2 elements read on the DMA source. + /// - Otherwise, this function may need up to N/2 extra elements to arrive before returning. + pub async fn read_exact(&mut self, buffer: &mut [W]) -> Result { + use core::future::poll_fn; + use core::sync::atomic::compiler_fence; + + let mut read_data = 0; + let buffer_len = buffer.len(); + + poll_fn(|cx| { + self.set_waker(cx.waker()); + + compiler_fence(Ordering::SeqCst); + + match self.read(&mut buffer[read_data..buffer_len]) { + Ok((len, remaining)) => { + read_data += len; + if read_data == buffer_len { + Poll::Ready(Ok(remaining)) + } else { + Poll::Pending + } + } + Err(e) => Poll::Ready(Err(e)), + } + }) + .await + } + /// The capacity of the ringbuffer pub fn cap(&self) -> usize { self.ringbuf.cap() diff --git a/embassy-stm32/src/dma/dma.rs b/embassy-stm32/src/dma/dma.rs index 58d438af..9459fb76 100644 --- a/embassy-stm32/src/dma/dma.rs +++ b/embassy-stm32/src/dma/dma.rs @@ -696,15 +696,53 @@ impl<'a, C: Channel, W: Word> RingBuffer<'a, C, W> { self.ringbuf.clear(DmaCtrlImpl(self.channel.reborrow())); } - /// Read bytes from the ring buffer + /// Read elements from the ring buffer /// Return a tuple of the length read and the length remaining in the buffer - /// If not all of the bytes were read, then there will be some bytes in the buffer remaining - /// The length remaining is the capacity, ring_buf.len(), less the bytes remaining after the read + /// If not all of the elements were read, then there will be some elements in the buffer remaining + /// The length remaining is the capacity, ring_buf.len(), less the elements remaining after the read /// OverrunError is returned if the portion to be read was overwritten by the DMA controller. pub fn read(&mut self, buf: &mut [W]) -> Result<(usize, usize), OverrunError> { self.ringbuf.read(DmaCtrlImpl(self.channel.reborrow()), buf) } + /// Read an exact number of elements from the ringbuffer. + /// + /// Returns the remaining number of elements available for immediate reading. + /// OverrunError is returned if the portion to be read was overwritten by the DMA controller. + /// + /// Async/Wake Behavior: + /// The underlying DMA peripheral only can wake us when its buffer pointer has reached the halfway point, + /// and when it wraps around. This means that when called with a buffer of length 'M', when this + /// ring buffer was created with a buffer of size 'N': + /// - If M equals N/2 or N/2 divides evenly into M, this function will return every N/2 elements read on the DMA source. + /// - Otherwise, this function may need up to N/2 extra elements to arrive before returning. + pub async fn read_exact(&mut self, buffer: &mut [W]) -> Result { + use core::future::poll_fn; + use core::sync::atomic::compiler_fence; + + let mut read_data = 0; + let buffer_len = buffer.len(); + + poll_fn(|cx| { + self.set_waker(cx.waker()); + + compiler_fence(Ordering::SeqCst); + + match self.read(&mut buffer[read_data..buffer_len]) { + Ok((len, remaining)) => { + read_data += len; + if read_data == buffer_len { + Poll::Ready(Ok(remaining)) + } else { + Poll::Pending + } + } + Err(e) => Poll::Ready(Err(e)), + } + }) + .await + } + // The capacity of the ringbuffer pub fn cap(&self) -> usize { self.ringbuf.cap() diff --git a/embassy-stm32/src/dma/ringbuffer.rs b/embassy-stm32/src/dma/ringbuffer.rs index a2bde986..19079397 100644 --- a/embassy-stm32/src/dma/ringbuffer.rs +++ b/embassy-stm32/src/dma/ringbuffer.rs @@ -72,10 +72,10 @@ impl<'a, W: Word> DmaRingBuffer<'a, W> { self.cap() - remaining_transfers } - /// Read bytes from the ring buffer + /// Read elements from the ring buffer /// Return a tuple of the length read and the length remaining in the buffer - /// If not all of the bytes were read, then there will be some bytes in the buffer remaining - /// The length remaining is the capacity, ring_buf.len(), less the bytes remaining after the read + /// If not all of the elements were read, then there will be some elements in the buffer remaining + /// The length remaining is the capacity, ring_buf.len(), less the elements remaining after the read /// OverrunError is returned if the portion to be read was overwritten by the DMA controller. pub fn read(&mut self, mut dma: impl DmaCtrl, buf: &mut [W]) -> Result<(usize, usize), OverrunError> { /* @@ -95,11 +95,11 @@ impl<'a, W: Word> DmaRingBuffer<'a, W> { */ let end = self.pos(dma.get_remaining_transfers()); if self.start == end && dma.get_complete_count() == 0 { - // No bytes are available in the buffer + // No elements are available in the buffer Ok((0, self.cap())) } else if self.start < end { // The available, unread portion in the ring buffer DOES NOT wrap - // Copy out the bytes from the dma buffer + // Copy out the elements from the dma buffer let len = self.copy_to(buf, self.start..end); compiler_fence(Ordering::SeqCst); @@ -128,7 +128,7 @@ impl<'a, W: Word> DmaRingBuffer<'a, W> { // The DMA writer has wrapped since we last read and is currently // writing (or the next byte added will be) in the beginning of the ring buffer. - // The provided read buffer is not large enough to include all bytes from the tail of the dma buffer. + // The provided read buffer is not large enough to include all elements from the tail of the dma buffer. // Copy out from the dma buffer let len = self.copy_to(buf, self.start..self.cap()); @@ -154,8 +154,8 @@ impl<'a, W: Word> DmaRingBuffer<'a, W> { // The DMA writer has wrapped since we last read and is currently // writing (or the next byte added will be) in the beginning of the ring buffer. - // The provided read buffer is large enough to include all bytes from the tail of the dma buffer, - // so the next read will not have any unread tail bytes in the ring buffer. + // The provided read buffer is large enough to include all elements from the tail of the dma buffer, + // so the next read will not have any unread tail elements in the ring buffer. // Copy out from the dma buffer let tail = self.copy_to(buf, self.start..self.cap()); @@ -180,7 +180,7 @@ impl<'a, W: Word> DmaRingBuffer<'a, W> { } /// Copy from the dma buffer at `data_range` into `buf` fn copy_to(&mut self, buf: &mut [W], data_range: Range) -> usize { - // Limit the number of bytes that can be copied + // Limit the number of elements that can be copied let length = usize::min(data_range.len(), buf.len()); // Copy from dma buffer into read buffer From a60d92cfbbacc909ba781802cad04fe00e849026 Mon Sep 17 00:00:00 2001 From: Michael van Niekerk Date: Mon, 24 Jul 2023 22:20:00 +0200 Subject: [PATCH 16/68] Tx and Rx setup --- examples/rp/.idea/.gitignore | 8 + examples/rp/.idea/modules.xml | 8 + examples/rp/.idea/rp.iml | 12 ++ examples/rp/.idea/vcs.xml | 6 + examples/rp/src/bin/pio_uart.rs | 262 ++++++++++++++++++++++++++++++++ 5 files changed, 296 insertions(+) create mode 100644 examples/rp/.idea/.gitignore create mode 100644 examples/rp/.idea/modules.xml create mode 100644 examples/rp/.idea/rp.iml create mode 100644 examples/rp/.idea/vcs.xml create mode 100644 examples/rp/src/bin/pio_uart.rs diff --git a/examples/rp/.idea/.gitignore b/examples/rp/.idea/.gitignore new file mode 100644 index 00000000..13566b81 --- /dev/null +++ b/examples/rp/.idea/.gitignore @@ -0,0 +1,8 @@ +# Default ignored files +/shelf/ +/workspace.xml +# Editor-based HTTP Client requests +/httpRequests/ +# Datasource local storage ignored files +/dataSources/ +/dataSources.local.xml diff --git a/examples/rp/.idea/modules.xml b/examples/rp/.idea/modules.xml new file mode 100644 index 00000000..06ff4b23 --- /dev/null +++ b/examples/rp/.idea/modules.xml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/examples/rp/.idea/rp.iml b/examples/rp/.idea/rp.iml new file mode 100644 index 00000000..9b4cf845 --- /dev/null +++ b/examples/rp/.idea/rp.iml @@ -0,0 +1,12 @@ + + + + + + + + + + + + \ No newline at end of file diff --git a/examples/rp/.idea/vcs.xml b/examples/rp/.idea/vcs.xml new file mode 100644 index 00000000..b2bdec2d --- /dev/null +++ b/examples/rp/.idea/vcs.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/examples/rp/src/bin/pio_uart.rs b/examples/rp/src/bin/pio_uart.rs new file mode 100644 index 00000000..14d05f4d --- /dev/null +++ b/examples/rp/src/bin/pio_uart.rs @@ -0,0 +1,262 @@ +//! This example shows how to use USB (Universal Serial Bus) in the RP2040 chip. +//! +//! This creates a USB serial port that echos. + +#![no_std] +#![no_main] +#![feature(type_alias_impl_trait)] + +use defmt::{info, panic}; +use embassy_executor::Spawner; +use embassy_futures::join::join; +use embassy_rp::bind_interrupts; +use embassy_rp::peripherals::{PIO0, USB}; +use embassy_rp::usb::{Driver, Instance, InterruptHandler}; +use embassy_usb::class::cdc_acm::{CdcAcmClass, State}; +use embassy_usb::driver::EndpointError; +use embassy_usb::{Builder, Config}; +use embassy_rp::pio::{InterruptHandler as PioInterruptHandler}; +use {defmt_rtt as _, panic_probe as _}; + +bind_interrupts!(struct UsbIrqs { + USBCTRL_IRQ => InterruptHandler; +}); + +bind_interrupts!(struct PioIrqs { + PIO0_IRQ_0 => PioInterruptHandler; +}); + +#[embassy_executor::main] +async fn main(_spawner: Spawner) { + info!("Hello there!"); + + let p = embassy_rp::init(Default::default()); + + // Create the driver, from the HAL. + let driver = Driver::new(p.USB, UsbIrqs); + + // Create embassy-usb Config + let mut config = Config::new(0xc0de, 0xcafe); + config.manufacturer = Some("Embassy"); + config.product = Some("PIO UART example"); + config.serial_number = Some("12345678"); + config.max_power = 100; + config.max_packet_size_0 = 64; + + // Required for windows compatibility. + // https://developer.nordicsemi.com/nRF_Connect_SDK/doc/1.9.1/kconfig/CONFIG_CDC_ACM_IAD.html#help + config.device_class = 0xEF; + config.device_sub_class = 0x02; + config.device_protocol = 0x01; + config.composite_with_iads = true; + + // Create embassy-usb DeviceBuilder using the driver and config. + // It needs some buffers for building the descriptors. + let mut device_descriptor = [0; 256]; + let mut config_descriptor = [0; 256]; + let mut bos_descriptor = [0; 256]; + let mut control_buf = [0; 64]; + + let mut state = State::new(); + + let mut builder = Builder::new( + driver, + config, + &mut device_descriptor, + &mut config_descriptor, + &mut bos_descriptor, + &mut control_buf, + ); + + // Create classes on the builder. + let mut class = CdcAcmClass::new(&mut builder, &mut state, 64); + + // Build the builder. + let mut usb = builder.build(); + + // Run the USB device. + let usb_fut = usb.run(); + + // Do stuff with the class! + let echo_fut = async { + loop { + class.wait_connection().await; + info!("Connected"); + let _ = echo(&mut class).await; + info!("Disconnected"); + } + }; + + // Run everything concurrently. + // If we had made everything `'static` above instead, we could do this using separate tasks instead. + join(usb_fut, echo_fut).await; +} + +struct Disconnected {} + +impl From for Disconnected { + fn from(val: EndpointError) -> Self { + match val { + EndpointError::BufferOverflow => panic!("Buffer overflow"), + EndpointError::Disabled => Disconnected {}, + } + } +} + +async fn echo<'d, T: Instance + 'd>(class: &mut CdcAcmClass<'d, Driver<'d, T>>) -> Result<(), Disconnected> { + let mut buf = [0; 64]; + loop { + let n = class.read_packet(&mut buf).await?; + let data = &buf[..n]; + info!("data: {:x}", data); + class.write_packet(data).await?; + } +} + +mod uart { + use embassy_rp::peripherals::PIO0; + use embassy_rp::pio::{Common, Pio, PioPin, StateMachine}; + use embassy_rp::Peripheral; + + use crate::PioIrqs; + + pub struct PioUart<'a> { + baud: u64, + pio: Common<'a, PIO0>, + sm0: StateMachine<'a, PIO0, 0>, + sm1: StateMachine<'a, PIO0, 1>, + } + + impl<'a> PioUart<'a> { + pub async fn new( + baud: u64, + pio: impl Peripheral

+ 'a, + tx_pin: impl PioPin, + rx_pin: impl PioPin, + ) -> PioUart<'a> { + let Pio { + mut common, + mut sm0, + mut sm1, + .. + } = Pio::new(pio, PioIrqs); + + crate::uart_tx::setup_uart_tx_on_sm0(&mut common, &mut sm0, tx_pin, baud); + crate::uart_rx::setup_uart_rx_on_sm1(&mut common, &mut sm1, rx_pin, baud); + + PioUart { + baud, + pio: common, + sm0, + sm1, + } + } + } +} + +mod uart_tx { + use embassy_rp::gpio::Level; + use embassy_rp::peripherals::PIO0; + use embassy_rp::pio::{Common, Config, Direction, FifoJoin, PioPin, ShiftDirection, StateMachine}; + use embassy_rp::relocate::RelocatedProgram; + use fixed::traits::ToFixed; + use fixed_macro::types::U56F8; + + pub fn setup_uart_tx_on_sm0<'a>( + common: &mut Common<'a, PIO0>, + sm_tx: &mut StateMachine<'a, PIO0, 0>, + tx_pin: impl PioPin, + baud: u64, + ) { + let prg = pio_proc::pio_asm!( + r#" + ;.program uart_tx + .side_set 1 opt + + ; An 8n1 UART transmit program. + ; OUT pin 0 and side-set pin 0 are both mapped to UART TX pin. + + pull side 1 [7] ; Assert stop bit, or stall with line in idle state + set x, 7 side 0 [7] ; Preload bit counter, assert start bit for 8 clocks + bitloop: ; This loop will run 8 times (8n1 UART) + out pins, 1 ; Shift 1 bit from OSR to the first OUT pin + jmp x-- bitloop [6] ; Each loop iteration is 8 cycles. + "# + ); + let tx_pin = common.make_pio_pin(tx_pin); + sm_tx.set_pins(Level::High, &[&tx_pin]); + sm_tx.set_pin_dirs(Direction::Out, &[&tx_pin]); + + let relocated = RelocatedProgram::new(&prg.program); + let mut cfg = Config::default(); + + cfg.use_program(&common.load_program(&relocated), &[&tx_pin]); + cfg.clock_divider = (U56F8!(125_000_000) / (8 * baud)).to_fixed(); + cfg.shift_out.auto_fill = false; + cfg.shift_out.direction = ShiftDirection::Right; + cfg.fifo_join = FifoJoin::TxOnly; + cfg.set_out_pins(&[&tx_pin]); + cfg.set_set_pins(&[&tx_pin]); + sm_tx.set_config(&cfg); + sm_tx.set_enable(true) + } +} + +mod uart_rx { + use embassy_rp::gpio::Level; + use embassy_rp::peripherals::PIO0; + use embassy_rp::pio::{Common, Config, Direction, FifoJoin, PioPin, ShiftDirection, StateMachine}; + use embassy_rp::relocate::RelocatedProgram; + use fixed::traits::ToFixed; + use fixed_macro::types::U56F8; + + pub fn setup_uart_rx_on_sm1<'a>( + common: &mut Common<'a, PIO0>, + sm_rx: &mut StateMachine<'a, PIO0, 1>, + rx_pin: impl PioPin, + baud: u64, + ) { + let prg = pio_proc::pio_asm!( + r#" + ;.program uart_rx + + ; Slightly more fleshed-out 8n1 UART receiver which handles framing errors and + ; break conditions more gracefully. + ; IN pin 0 and JMP pin are both mapped to the GPIO used as UART RX. + + start: + wait 0 pin 0 ; Stall until start bit is asserted + set x, 7 [10] ; Preload bit counter, then delay until halfway through + bitloop: ; the first data bit (12 cycles incl wait, set). + in pins, 1 ; Shift data bit into ISR + jmp x-- bitloop [6] ; Loop 8 times, each loop iteration is 8 cycles + jmp pin good_stop ; Check stop bit (should be high) + + irq 4 rel ; Either a framing error or a break. Set a sticky flag, + wait 1 pin 0 ; and wait for line to return to idle state. + jmp start ; Don't push data if we didn't see good framing. + + good_stop: ; No delay before returning to start; a little slack is + push ; important in case the TX clock is slightly too fast. + "# + ); + + let rx_pin = common.make_pio_pin(rx_pin); + sm_rx.set_pins(Level::High, &[&rx_pin]); + sm_rx.set_pin_dirs(Direction::In, &[&rx_pin]); + + let relocated = RelocatedProgram::new(&prg.program); + let mut cfg = Config::default(); + + cfg.use_program(&common.load_program(&relocated), &[&rx_pin]); + cfg.clock_divider = (U56F8!(125_000_000) / (8 * baud)).to_fixed(); + cfg.shift_out.auto_fill = false; + cfg.shift_out.direction = ShiftDirection::Right; + cfg.fifo_join = FifoJoin::RxOnly; + cfg.set_in_pins(&[&rx_pin]); + cfg.set_jmp_pin(&rx_pin); + // cfg.set_set_pins(&[&rx_pin]); + sm_rx.set_config(&cfg); + sm_rx.set_enable(true) + } +} From 3c41784de87a8903ac72cea19da784c0152896fe Mon Sep 17 00:00:00 2001 From: xoviat Date: Mon, 24 Jul 2023 18:08:23 -0500 Subject: [PATCH 17/68] stm32/usart: fix error msg for lptim --- embassy-stm32/src/usart/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/embassy-stm32/src/usart/mod.rs b/embassy-stm32/src/usart/mod.rs index ea8e525e..3b9226fd 100644 --- a/embassy-stm32/src/usart/mod.rs +++ b/embassy-stm32/src/usart/mod.rs @@ -857,7 +857,7 @@ fn configure(r: Regs, config: &Config, pclk_freq: Hertz, kind: Kind, enable_rx: "Using {} oversampling, desired baudrate: {}, actual baudrate: {}", oversampling, config.baudrate, - pclk_freq.0 / div + (pclk_freq.0 * mul as u32) / div ); r.cr2().write(|w| { From 858ddf6777d6df0e8c02921d29cd6a8095a7cdad Mon Sep 17 00:00:00 2001 From: Piotr Esden-Tempski Date: Wed, 26 Jul 2023 18:32:40 -0700 Subject: [PATCH 18/68] Added debug=2 in release profile to all examples. This makes rtt output work right when using `cargo run` in release mode. Debug was already enabled for release builds in some of the examples but not all. --- examples/nrf-rtos-trace/Cargo.toml | 3 +++ examples/nrf52840-rtic/Cargo.toml | 3 +++ examples/nrf52840/Cargo.toml | 3 +++ examples/nrf5340/Cargo.toml | 3 +++ examples/rp/Cargo.toml | 3 ++- examples/std/Cargo.toml | 3 +++ examples/stm32c0/Cargo.toml | 3 +++ examples/stm32f0/Cargo.toml | 3 +++ examples/stm32f1/Cargo.toml | 3 +++ examples/stm32f2/Cargo.toml | 3 +++ examples/stm32f3/Cargo.toml | 3 +++ examples/stm32f7/Cargo.toml | 3 +++ examples/stm32g0/Cargo.toml | 3 +++ examples/stm32g4/Cargo.toml | 3 +++ examples/stm32l0/Cargo.toml | 3 +++ examples/stm32l1/Cargo.toml | 3 +++ examples/stm32l4/Cargo.toml | 3 +++ examples/stm32l5/Cargo.toml | 3 +++ examples/stm32u5/Cargo.toml | 3 +++ examples/stm32wb/Cargo.toml | 3 +++ examples/stm32wl/Cargo.toml | 3 +++ examples/wasm/Cargo.toml | 3 +++ 22 files changed, 65 insertions(+), 1 deletion(-) diff --git a/examples/nrf-rtos-trace/Cargo.toml b/examples/nrf-rtos-trace/Cargo.toml index 30b67b7b..068474e7 100644 --- a/examples/nrf-rtos-trace/Cargo.toml +++ b/examples/nrf-rtos-trace/Cargo.toml @@ -34,3 +34,6 @@ log = { version = "0.4.17", optional = true } [[bin]] name = "rtos_trace" required-features = ["nightly"] + +[profile.release] +debug = 2 diff --git a/examples/nrf52840-rtic/Cargo.toml b/examples/nrf52840-rtic/Cargo.toml index ded3b7db..715f1ecf 100644 --- a/examples/nrf52840-rtic/Cargo.toml +++ b/examples/nrf52840-rtic/Cargo.toml @@ -19,3 +19,6 @@ cortex-m = { version = "0.7.6", features = ["inline-asm", "critical-section-sing cortex-m-rt = "0.7.0" panic-probe = { version = "0.3", features = ["print-defmt"] } futures = { version = "0.3.17", default-features = false, features = ["async-await"] } + +[profile.release] +debug = 2 diff --git a/examples/nrf52840/Cargo.toml b/examples/nrf52840/Cargo.toml index 9b41ec5a..5d6bf54e 100644 --- a/examples/nrf52840/Cargo.toml +++ b/examples/nrf52840/Cargo.toml @@ -59,3 +59,6 @@ microfft = "0.5.0" [patch.crates-io] lora-phy = { git = "https://github.com/embassy-rs/lora-phy", rev = "ad289428fd44b02788e2fa2116445cc8f640a265" } + +[profile.release] +debug = 2 diff --git a/examples/nrf5340/Cargo.toml b/examples/nrf5340/Cargo.toml index f1d45f33..b0e51dcf 100644 --- a/examples/nrf5340/Cargo.toml +++ b/examples/nrf5340/Cargo.toml @@ -53,3 +53,6 @@ rand = { version = "0.8.4", default-features = false } embedded-storage = "0.3.0" usbd-hid = "0.6.0" serde = { version = "1.0.136", default-features = false } + +[profile.release] +debug = 2 diff --git a/examples/rp/Cargo.toml b/examples/rp/Cargo.toml index c812cb3e..f2fe5da4 100644 --- a/examples/rp/Cargo.toml +++ b/examples/rp/Cargo.toml @@ -53,7 +53,8 @@ pio = "0.2.1" rand = { version = "0.8.5", default-features = false } [profile.release] -debug = true +debug = 2 [patch.crates-io] lora-phy = { git = "https://github.com/embassy-rs/lora-phy", rev = "ad289428fd44b02788e2fa2116445cc8f640a265" } + diff --git a/examples/std/Cargo.toml b/examples/std/Cargo.toml index 92933ab5..42adede1 100644 --- a/examples/std/Cargo.toml +++ b/examples/std/Cargo.toml @@ -23,3 +23,6 @@ clap = { version = "3.0.0-beta.5", features = ["derive"] } rand_core = { version = "0.6.3", features = ["std"] } heapless = { version = "0.7.5", default-features = false } static_cell = { version = "1.1", features = ["nightly"]} + +[profile.release] +debug = 2 diff --git a/examples/stm32c0/Cargo.toml b/examples/stm32c0/Cargo.toml index e74c5357..8534921a 100644 --- a/examples/stm32c0/Cargo.toml +++ b/examples/stm32c0/Cargo.toml @@ -20,3 +20,6 @@ embedded-hal = "0.2.6" panic-probe = { version = "0.3", features = ["print-defmt"] } futures = { version = "0.3.17", default-features = false, features = ["async-await"] } heapless = { version = "0.7.5", default-features = false } + +[profile.release] +debug = 2 diff --git a/examples/stm32f0/Cargo.toml b/examples/stm32f0/Cargo.toml index 620a139a..46b6db45 100644 --- a/examples/stm32f0/Cargo.toml +++ b/examples/stm32f0/Cargo.toml @@ -18,3 +18,6 @@ embassy-sync = { version = "0.2.0", path = "../../embassy-sync", features = ["de embassy-executor = { version = "0.2.0", path = "../../embassy-executor", features = ["nightly", "arch-cortex-m", "executor-thread", "executor-interrupt", "defmt", "integrated-timers"] } embassy-time = { version = "0.1.2", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } static_cell = { version = "1.1", features = ["nightly"]} + +[profile.release] +debug = 2 diff --git a/examples/stm32f1/Cargo.toml b/examples/stm32f1/Cargo.toml index 8450c541..5d32992c 100644 --- a/examples/stm32f1/Cargo.toml +++ b/examples/stm32f1/Cargo.toml @@ -26,3 +26,6 @@ nb = "1.0.0" [profile.dev] opt-level = "s" + +[profile.release] +debug = 2 diff --git a/examples/stm32f2/Cargo.toml b/examples/stm32f2/Cargo.toml index 147e2ecb..9857fb63 100644 --- a/examples/stm32f2/Cargo.toml +++ b/examples/stm32f2/Cargo.toml @@ -21,3 +21,6 @@ panic-probe = { version = "0.3", features = ["print-defmt"] } futures = { version = "0.3.17", default-features = false, features = ["async-await"] } heapless = { version = "0.7.5", default-features = false } nb = "1.0.0" + +[profile.release] +debug = 2 diff --git a/examples/stm32f3/Cargo.toml b/examples/stm32f3/Cargo.toml index 6ac5d57e..bd594d16 100644 --- a/examples/stm32f3/Cargo.toml +++ b/examples/stm32f3/Cargo.toml @@ -25,3 +25,6 @@ heapless = { version = "0.7.5", default-features = false } nb = "1.0.0" embedded-storage = "0.3.0" static_cell = { version = "1.1", features = ["nightly"]} + +[profile.release] +debug = 2 diff --git a/examples/stm32f7/Cargo.toml b/examples/stm32f7/Cargo.toml index bbc99fee..a6964c7b 100644 --- a/examples/stm32f7/Cargo.toml +++ b/examples/stm32f7/Cargo.toml @@ -28,3 +28,6 @@ rand_core = "0.6.3" critical-section = "1.1" embedded-storage = "0.3.0" static_cell = { version = "1.1", features = ["nightly"]} + +[profile.release] +debug = 2 diff --git a/examples/stm32g0/Cargo.toml b/examples/stm32g0/Cargo.toml index 4a14568a..b4dfe3c6 100644 --- a/examples/stm32g0/Cargo.toml +++ b/examples/stm32g0/Cargo.toml @@ -20,3 +20,6 @@ embedded-hal = "0.2.6" panic-probe = { version = "0.3", features = ["print-defmt"] } futures = { version = "0.3.17", default-features = false, features = ["async-await"] } heapless = { version = "0.7.5", default-features = false } + +[profile.release] +debug = 2 diff --git a/examples/stm32g4/Cargo.toml b/examples/stm32g4/Cargo.toml index 935997a7..ce883860 100644 --- a/examples/stm32g4/Cargo.toml +++ b/examples/stm32g4/Cargo.toml @@ -22,3 +22,6 @@ embedded-hal = "0.2.6" panic-probe = { version = "0.3", features = ["print-defmt"] } futures = { version = "0.3.17", default-features = false, features = ["async-await"] } heapless = { version = "0.7.5", default-features = false } + +[profile.release] +debug = 2 diff --git a/examples/stm32l0/Cargo.toml b/examples/stm32l0/Cargo.toml index e6a5a4c1..f2ebae77 100644 --- a/examples/stm32l0/Cargo.toml +++ b/examples/stm32l0/Cargo.toml @@ -36,3 +36,6 @@ static_cell = "1.1" [patch.crates-io] lora-phy = { git = "https://github.com/embassy-rs/lora-phy", rev = "ad289428fd44b02788e2fa2116445cc8f640a265" } + +[profile.release] +debug = 2 diff --git a/examples/stm32l1/Cargo.toml b/examples/stm32l1/Cargo.toml index dcca1cc3..329d44ca 100644 --- a/examples/stm32l1/Cargo.toml +++ b/examples/stm32l1/Cargo.toml @@ -20,3 +20,6 @@ panic-probe = { version = "0.3", features = ["print-defmt"] } futures = { version = "0.3.17", default-features = false, features = ["async-await"] } heapless = { version = "0.7.5", default-features = false } embedded-storage = "0.3.0" + +[profile.release] +debug = 2 diff --git a/examples/stm32l4/Cargo.toml b/examples/stm32l4/Cargo.toml index 41c9869b..0f770e2f 100644 --- a/examples/stm32l4/Cargo.toml +++ b/examples/stm32l4/Cargo.toml @@ -27,3 +27,6 @@ heapless = { version = "0.7.5", default-features = false } chrono = { version = "^0.4", default-features = false } micromath = "2.0.0" + +[profile.release] +debug = 2 diff --git a/examples/stm32l5/Cargo.toml b/examples/stm32l5/Cargo.toml index 5d66c59c..1afd0039 100644 --- a/examples/stm32l5/Cargo.toml +++ b/examples/stm32l5/Cargo.toml @@ -27,3 +27,6 @@ heapless = { version = "0.7.5", default-features = false } rand_core = { version = "0.6.3", default-features = false } embedded-io = { version = "0.4.0", features = ["async"] } static_cell = { version = "1.1", features = ["nightly"]} + +[profile.release] +debug = 2 diff --git a/examples/stm32u5/Cargo.toml b/examples/stm32u5/Cargo.toml index a43a5590..db251eaf 100644 --- a/examples/stm32u5/Cargo.toml +++ b/examples/stm32u5/Cargo.toml @@ -23,3 +23,6 @@ futures = { version = "0.3.17", default-features = false, features = ["async-awa heapless = { version = "0.7.5", default-features = false } micromath = "2.0.0" + +[profile.release] +debug = 2 diff --git a/examples/stm32wb/Cargo.toml b/examples/stm32wb/Cargo.toml index 48e34026..1a5aff35 100644 --- a/examples/stm32wb/Cargo.toml +++ b/examples/stm32wb/Cargo.toml @@ -52,3 +52,6 @@ required-features = ["ble"] [[bin]] name = "gatt_server" required-features = ["ble"] + +[profile.release] +debug = 2 diff --git a/examples/stm32wl/Cargo.toml b/examples/stm32wl/Cargo.toml index 6e6f269a..3e99b101 100644 --- a/examples/stm32wl/Cargo.toml +++ b/examples/stm32wl/Cargo.toml @@ -30,3 +30,6 @@ chrono = { version = "^0.4", default-features = false } [patch.crates-io] lora-phy = { git = "https://github.com/embassy-rs/lora-phy", rev = "ad289428fd44b02788e2fa2116445cc8f640a265" } + +[profile.release] +debug = 2 diff --git a/examples/wasm/Cargo.toml b/examples/wasm/Cargo.toml index 3679e385..2791cc34 100644 --- a/examples/wasm/Cargo.toml +++ b/examples/wasm/Cargo.toml @@ -17,3 +17,6 @@ wasm-bindgen = "0.2" web-sys = { version = "0.3", features = ["Document", "Element", "HtmlElement", "Node", "Window" ] } log = "0.4.11" critical-section = { version = "1.1", features = ["std"] } + +[profile.release] +debug = 2 From c54ae73d4999fdf6243adeedcde1467493c8935a Mon Sep 17 00:00:00 2001 From: ceekdee Date: Wed, 26 Jul 2023 21:51:09 -0500 Subject: [PATCH 19/68] Use lora-phy v1.2.1; modify embassy-lora dependencies. --- embassy-lora/Cargo.toml | 13 +++---------- examples/nrf52840/Cargo.toml | 3 --- examples/rp/Cargo.toml | 3 --- examples/stm32l0/Cargo.toml | 3 --- examples/stm32wl/Cargo.toml | 3 --- 5 files changed, 3 insertions(+), 22 deletions(-) diff --git a/embassy-lora/Cargo.toml b/embassy-lora/Cargo.toml index e4524af5..c4857ace 100644 --- a/embassy-lora/Cargo.toml +++ b/embassy-lora/Cargo.toml @@ -12,7 +12,7 @@ target = "thumbv7em-none-eabi" [features] stm32wl = ["dep:embassy-stm32"] -time = [] +time = ["dep:embassy-time", "dep:lorawan-device"] defmt = ["dep:defmt", "lorawan-device/defmt"] [dependencies] @@ -20,18 +20,11 @@ defmt = ["dep:defmt", "lorawan-device/defmt"] defmt = { version = "0.3", optional = true } log = { version = "0.4.14", optional = true } -embassy-time = { version = "0.1.2", path = "../embassy-time" } +embassy-time = { version = "0.1.2", path = "../embassy-time", optional = true } embassy-sync = { version = "0.2.0", path = "../embassy-sync" } embassy-stm32 = { version = "0.1.0", path = "../embassy-stm32", default-features = false, optional = true } -embedded-hal-1 = { package = "embedded-hal", version = "=1.0.0-alpha.11" } embedded-hal-async = { version = "=0.2.0-alpha.2" } -embassy-hal-common = { version = "0.1.0", path = "../embassy-hal-common", default-features = false } -futures = { version = "0.3.17", default-features = false, features = [ "async-await" ] } embedded-hal = { version = "0.2", features = ["unproven"] } -bit_field = { version = "0.10" } lora-phy = { version = "1" } -lorawan-device = { version = "0.10.0", default-features = false, features = ["async"] } - -[patch.crates-io] -lora-phy = { git = "https://github.com/embassy-rs/lora-phy", rev = "ad289428fd44b02788e2fa2116445cc8f640a265" } +lorawan-device = { version = "0.10.0", default-features = false, features = ["async"], optional = true } diff --git a/examples/nrf52840/Cargo.toml b/examples/nrf52840/Cargo.toml index 9b41ec5a..f39f9323 100644 --- a/examples/nrf52840/Cargo.toml +++ b/examples/nrf52840/Cargo.toml @@ -56,6 +56,3 @@ serde = { version = "1.0.136", default-features = false } embedded-hal-async = { version = "0.2.0-alpha.2", optional = true } num-integer = { version = "0.1.45", default-features = false } microfft = "0.5.0" - -[patch.crates-io] -lora-phy = { git = "https://github.com/embassy-rs/lora-phy", rev = "ad289428fd44b02788e2fa2116445cc8f640a265" } diff --git a/examples/rp/Cargo.toml b/examples/rp/Cargo.toml index c812cb3e..8a675443 100644 --- a/examples/rp/Cargo.toml +++ b/examples/rp/Cargo.toml @@ -54,6 +54,3 @@ rand = { version = "0.8.5", default-features = false } [profile.release] debug = true - -[patch.crates-io] -lora-phy = { git = "https://github.com/embassy-rs/lora-phy", rev = "ad289428fd44b02788e2fa2116445cc8f640a265" } diff --git a/examples/stm32l0/Cargo.toml b/examples/stm32l0/Cargo.toml index e6a5a4c1..e794cf1e 100644 --- a/examples/stm32l0/Cargo.toml +++ b/examples/stm32l0/Cargo.toml @@ -33,6 +33,3 @@ futures = { version = "0.3.17", default-features = false, features = ["async-awa heapless = { version = "0.7.5", default-features = false } embedded-hal = "0.2.6" static_cell = "1.1" - -[patch.crates-io] -lora-phy = { git = "https://github.com/embassy-rs/lora-phy", rev = "ad289428fd44b02788e2fa2116445cc8f640a265" } diff --git a/examples/stm32wl/Cargo.toml b/examples/stm32wl/Cargo.toml index 6e6f269a..b3f57af5 100644 --- a/examples/stm32wl/Cargo.toml +++ b/examples/stm32wl/Cargo.toml @@ -27,6 +27,3 @@ panic-probe = { version = "0.3", features = ["print-defmt"] } futures = { version = "0.3.17", default-features = false, features = ["async-await"] } heapless = { version = "0.7.5", default-features = false } chrono = { version = "^0.4", default-features = false } - -[patch.crates-io] -lora-phy = { git = "https://github.com/embassy-rs/lora-phy", rev = "ad289428fd44b02788e2fa2116445cc8f640a265" } From 13acca624f4995e06cbe1606bee10015ecd31d06 Mon Sep 17 00:00:00 2001 From: ceekdee Date: Wed, 26 Jul 2023 22:23:02 -0500 Subject: [PATCH 20/68] Correct embassy-lora time feature --- embassy-lora/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/embassy-lora/Cargo.toml b/embassy-lora/Cargo.toml index c4857ace..a77ed003 100644 --- a/embassy-lora/Cargo.toml +++ b/embassy-lora/Cargo.toml @@ -12,7 +12,7 @@ target = "thumbv7em-none-eabi" [features] stm32wl = ["dep:embassy-stm32"] -time = ["dep:embassy-time", "dep:lorawan-device"] +time = ["embassy-time", "lorawan-device"] defmt = ["dep:defmt", "lorawan-device/defmt"] [dependencies] From a6543cef16e74f8e935c92f0b4f3ef12b8f72f75 Mon Sep 17 00:00:00 2001 From: goueslati Date: Thu, 27 Jul 2023 15:00:01 +0100 Subject: [PATCH 21/68] wpan: update stm32wb-hci --- embassy-stm32-wpan/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/embassy-stm32-wpan/Cargo.toml b/embassy-stm32-wpan/Cargo.toml index 6cd12220..2c6089c5 100644 --- a/embassy-stm32-wpan/Cargo.toml +++ b/embassy-stm32-wpan/Cargo.toml @@ -26,7 +26,7 @@ aligned = "0.4.1" bit_field = "0.10.2" stm32-device-signature = { version = "0.3.3", features = ["stm32wb5x"] } -stm32wb-hci = { version = "0.1.3", optional = true } +stm32wb-hci = { version = "0.1.4", optional = true } futures = { version = "0.3.17", default-features = false, features = ["async-await"] } bitflags = { version = "2.3.3", optional = true } From 93864610ce5d9127eb415f8020ec43c26fb20804 Mon Sep 17 00:00:00 2001 From: JuliDi <20155974+JuliDi@users.noreply.github.com> Date: Thu, 27 Jul 2023 19:04:43 +0200 Subject: [PATCH 22/68] Add DAC HIL test with ADC --- tests/stm32/Cargo.toml | 13 ++++-- tests/stm32/src/bin/dac.rs | 81 ++++++++++++++++++++++++++++++++++++++ tests/stm32/src/common.rs | 1 + 3 files changed, 92 insertions(+), 3 deletions(-) create mode 100644 tests/stm32/src/bin/dac.rs diff --git a/tests/stm32/Cargo.toml b/tests/stm32/Cargo.toml index 3007cd1e..17320649 100644 --- a/tests/stm32/Cargo.toml +++ b/tests/stm32/Cargo.toml @@ -7,11 +7,11 @@ autobins = false [features] stm32f103c8 = ["embassy-stm32/stm32f103c8", "not-gpdma"] # Blue Pill -stm32f429zi = ["embassy-stm32/stm32f429zi", "chrono", "can", "not-gpdma"] # Nucleo "sdmmc" -stm32g071rb = ["embassy-stm32/stm32g071rb", "not-gpdma"] # Nucleo +stm32f429zi = ["embassy-stm32/stm32f429zi", "chrono", "can", "not-gpdma", "dac-adc-pin"] # Nucleo "sdmmc" +stm32g071rb = ["embassy-stm32/stm32g071rb", "not-gpdma", "dac-adc-pin"] # Nucleo stm32c031c6 = ["embassy-stm32/stm32c031c6", "not-gpdma"] # Nucleo stm32g491re = ["embassy-stm32/stm32g491re", "not-gpdma"] # Nucleo -stm32h755zi = ["embassy-stm32/stm32h755zi-cm7", "not-gpdma"] # Nucleo +stm32h755zi = ["embassy-stm32/stm32h755zi-cm7", "not-gpdma", "dac-adc-pin"] # Nucleo stm32wb55rg = ["embassy-stm32/stm32wb55rg", "not-gpdma", "ble", "mac" ] # Nucleo stm32h563zi = ["embassy-stm32/stm32h563zi"] # Nucleo stm32u585ai = ["embassy-stm32/stm32u585ai"] # IoT board @@ -23,6 +23,7 @@ ble = ["dep:embassy-stm32-wpan", "embassy-stm32-wpan/ble"] mac = ["dep:embassy-stm32-wpan", "embassy-stm32-wpan/mac"] embassy-stm32-wpan = [] not-gpdma = [] +dac-adc-pin = [] [dependencies] teleprobe-meta = "1" @@ -42,6 +43,7 @@ cortex-m-rt = "0.7.0" embedded-hal = "0.2.6" embedded-hal-1 = { package = "embedded-hal", version = "=1.0.0-alpha.11" } embedded-hal-async = { version = "=0.2.0-alpha.2" } +micromath = "2.0.0" panic-probe = { version = "0.3.0", features = ["print-defmt"] } rand_core = { version = "0.6", default-features = false } rand_chacha = { version = "0.3", default-features = false } @@ -55,6 +57,11 @@ name = "can" path = "src/bin/can.rs" required-features = [ "can",] +[[bin]] +name = "dac" +path = "src/bin/dac.rs" +required-features = [ "dac-adc-pin",] + [[bin]] name = "gpio" path = "src/bin/gpio.rs" diff --git a/tests/stm32/src/bin/dac.rs b/tests/stm32/src/bin/dac.rs new file mode 100644 index 00000000..67a7d5b5 --- /dev/null +++ b/tests/stm32/src/bin/dac.rs @@ -0,0 +1,81 @@ +#![no_std] +#![no_main] +#![feature(type_alias_impl_trait)] + +// required-features: dac-adc-pin + +#[path = "../common.rs"] +mod common; +use common::*; +use defmt::assert; +use embassy_executor::Spawner; +use embassy_stm32::adc::Adc; +use embassy_stm32::dac::{DacCh1, DacChannel, Value}; +use embassy_stm32::dma::NoDma; +use embassy_time::{Delay, Duration, Timer}; +use {defmt_rtt as _, panic_probe as _}; + +#[embassy_executor::main] +async fn main(_spawner: Spawner) { + // Initialize the board and obtain a Peripherals instance + let p: embassy_stm32::Peripherals = embassy_stm32::init(config()); + + #[cfg(feature = "stm32f429zi")] + let dac_peripheral = p.DAC; + + #[cfg(any(feature = "stm32h755zi", feature = "stm32g071rb"))] + let dac_peripheral = p.DAC1; + + let mut dac: DacCh1<'_, _, NoDma> = DacCh1::new(dac_peripheral, NoDma, p.PA4); + unwrap!(dac.set_trigger_enable(false)); + + let mut adc = Adc::new(p.ADC1, &mut Delay); + + #[cfg(feature = "stm32h755zi")] + let normalization_factor = 256; + #[cfg(any(feature = "stm32f429zi", feature = "stm32g071rb"))] + let normalization_factor: i32 = 16; + + unwrap!(dac.set(Value::Bit8(0))); + // Now wait a little to obtain a stable value + Timer::after(Duration::from_millis(30)).await; + let offset = adc.read(&mut unsafe { embassy_stm32::Peripherals::steal() }.PA4); + + for v in 0..=255 { + // First set the DAC output value + let dac_output_val = to_sine_wave(v); + unwrap!(dac.set(Value::Bit8(dac_output_val))); + + // Now wait a little to obtain a stable value + Timer::after(Duration::from_millis(30)).await; + + // Need to steal the peripherals here because PA4 is obviously in use already + let measured = adc.read(&mut unsafe { embassy_stm32::Peripherals::steal() }.PA4); + // Calibrate and normalize the measurement to get close to the dac_output_val + let measured_normalized = ((measured as i32 - offset as i32) / normalization_factor) as i16; + + info!("value / measured: {} / {}", dac_output_val, measured_normalized); + + // The deviations are quite enormous but that does not matter since this is only a quick test + assert!((dac_output_val as i16 - measured_normalized).abs() < 15); + } + + info!("Test OK"); + cortex_m::asm::bkpt(); +} + +use core::f32::consts::PI; + +use micromath::F32Ext; + +fn to_sine_wave(v: u8) -> u8 { + if v >= 128 { + // top half + let r = PI * ((v - 128) as f32 / 128.0); + (r.sin() * 128.0 + 127.0) as u8 + } else { + // bottom half + let r = PI + PI * (v as f32 / 128.0); + (r.sin() * 128.0 + 127.0) as u8 + } +} diff --git a/tests/stm32/src/common.rs b/tests/stm32/src/common.rs index 3d2a9b8e..ca5cb43a 100644 --- a/tests/stm32/src/common.rs +++ b/tests/stm32/src/common.rs @@ -33,6 +33,7 @@ pub fn config() -> Config { { config.rcc.sys_ck = Some(Hertz(400_000_000)); config.rcc.pll1.q_ck = Some(Hertz(100_000_000)); + config.rcc.adc_clock_source = embassy_stm32::rcc::AdcClockSource::PerCk; } #[cfg(feature = "stm32u585ai")] From e947aa01533b7fd41133678ed8444a16c9c341e0 Mon Sep 17 00:00:00 2001 From: Michael van Niekerk Date: Fri, 28 Jul 2023 11:37:38 +0200 Subject: [PATCH 23/68] Comments --- examples/rp/Cargo.toml | 1 + examples/rp/src/bin/pio_uart.rs | 345 ++++++++++++++++++++++++-------- 2 files changed, 258 insertions(+), 88 deletions(-) diff --git a/examples/rp/Cargo.toml b/examples/rp/Cargo.toml index c812cb3e..2a018ad0 100644 --- a/examples/rp/Cargo.toml +++ b/examples/rp/Cargo.toml @@ -7,6 +7,7 @@ license = "MIT OR Apache-2.0" [dependencies] embassy-embedded-hal = { version = "0.1.0", path = "../../embassy-embedded-hal", features = ["defmt"] } +embassy-hal-common = { version = "0.1.0", path = "../../embassy-hal-common", features = ["defmt"] } embassy-sync = { version = "0.2.0", path = "../../embassy-sync", features = ["defmt"] } embassy-executor = { version = "0.2.0", path = "../../embassy-executor", features = ["nightly", "arch-cortex-m", "executor-thread", "executor-interrupt", "defmt", "integrated-timers"] } embassy-time = { version = "0.1.2", path = "../../embassy-time", features = ["nightly", "unstable-traits", "defmt", "defmt-timestamp-uptime"] } diff --git a/examples/rp/src/bin/pio_uart.rs b/examples/rp/src/bin/pio_uart.rs index 14d05f4d..eeb213e1 100644 --- a/examples/rp/src/bin/pio_uart.rs +++ b/examples/rp/src/bin/pio_uart.rs @@ -1,23 +1,35 @@ -//! This example shows how to use USB (Universal Serial Bus) in the RP2040 chip. +//! This example shows how to use the PIO module in the RP2040 chip to implement a duplex UART. +//! The PIO module is a very powerful peripheral that can be used to implement many different +//! protocols. It is a very flexible state machine that can be programmed to do almost anything. //! -//! This creates a USB serial port that echos. +//! This example opens up a USB device that implements a CDC ACM serial port. It then uses the +//! PIO module to implement a UART that is connected to the USB serial port. This allows you to +//! communicate with a device connected to the RP2040 over USB serial. #![no_std] #![no_main] #![feature(type_alias_impl_trait)] +#![feature(async_fn_in_trait)] -use defmt::{info, panic}; +use defmt::{info, panic, trace}; use embassy_executor::Spawner; -use embassy_futures::join::join; +use embassy_futures::join::{join, join3}; use embassy_rp::bind_interrupts; use embassy_rp::peripherals::{PIO0, USB}; +use embassy_rp::pio::InterruptHandler as PioInterruptHandler; use embassy_rp::usb::{Driver, Instance, InterruptHandler}; -use embassy_usb::class::cdc_acm::{CdcAcmClass, State}; +use embassy_sync::blocking_mutex::raw::ThreadModeRawMutex; +use embassy_sync::channel::Channel; +use embassy_usb::class::cdc_acm::{CdcAcmClass, Receiver, Sender, State}; use embassy_usb::driver::EndpointError; use embassy_usb::{Builder, Config}; -use embassy_rp::pio::{InterruptHandler as PioInterruptHandler}; +use embedded_io::asynch::{Read, Write}; use {defmt_rtt as _, panic_probe as _}; +use crate::uart::PioUart; +use crate::uart_rx::PioUartRx; +use crate::uart_tx::PioUartTx; + bind_interrupts!(struct UsbIrqs { USBCTRL_IRQ => InterruptHandler; }); @@ -69,7 +81,7 @@ async fn main(_spawner: Spawner) { ); // Create classes on the builder. - let mut class = CdcAcmClass::new(&mut builder, &mut state, 64); + let class = CdcAcmClass::new(&mut builder, &mut state, 64); // Build the builder. let mut usb = builder.build(); @@ -77,19 +89,50 @@ async fn main(_spawner: Spawner) { // Run the USB device. let usb_fut = usb.run(); - // Do stuff with the class! - let echo_fut = async { + // PIO UART setup + let uart = PioUart::new(9600, p.PIO0, p.PIN_4, p.PIN_5).await; + let (mut uart_tx, mut uart_rx) = uart.split(); + + // Channels setup + static USB_CHANNEL_TX: Channel = Channel::::new(); + let mut usb_channel_tx_send = USB_CHANNEL_TX.sender(); + let mut usb_channel_tx_recv = USB_CHANNEL_TX.receiver(); + + static UART_CHANNEL_TX: Channel = Channel::::new(); + let mut uart_channel_tx_send = UART_CHANNEL_TX.sender(); + let mut uart_channel_tx_recv = UART_CHANNEL_TX.receiver(); + + let (mut usb_tx, mut usb_rx) = class.split(); + + // Read + write from USB + let usb_future = async { loop { - class.wait_connection().await; + info!("Wait for USB connection"); + usb_rx.wait_connection().await; info!("Connected"); - let _ = echo(&mut class).await; + let _ = join( + usb_read(&mut usb_rx, &mut uart_channel_tx_send), + usb_write(&mut usb_tx, &mut usb_channel_tx_recv), + ) + .await; info!("Disconnected"); } }; + // Read + write from UART + let uart_future = async { + loop { + let _ = join( + uart_read(&mut uart_rx, &mut usb_channel_tx_send), + uart_write(&mut uart_tx, &mut uart_channel_tx_recv), + ) + .await; + } + }; + // Run everything concurrently. // If we had made everything `'static` above instead, we could do this using separate tasks instead. - join(usb_fut, echo_fut).await; + join3(usb_fut, usb_future, uart_future).await; } struct Disconnected {} @@ -103,28 +146,79 @@ impl From for Disconnected { } } -async fn echo<'d, T: Instance + 'd>(class: &mut CdcAcmClass<'d, Driver<'d, T>>) -> Result<(), Disconnected> { +/// Read from the USB and write it to the UART TX send channel +async fn usb_read<'d, T: Instance + 'd>( + usb_rx: &mut Receiver<'d, Driver<'d, T>>, + uart_tx_send: &mut embassy_sync::channel::Sender<'d, ThreadModeRawMutex, u8, 20>, +) -> Result<(), Disconnected> { let mut buf = [0; 64]; loop { - let n = class.read_packet(&mut buf).await?; + let n = usb_rx.read_packet(&mut buf).await?; let data = &buf[..n]; - info!("data: {:x}", data); - class.write_packet(data).await?; + trace!("USB IN: {:x}", data); + for byte in data { + uart_tx_send.send(*byte).await; + } + } +} + +/// Read from the USB TX receive channel and write it to the USB +async fn usb_write<'d, T: Instance + 'd>( + usb_tx: &mut Sender<'d, Driver<'d, T>>, + usb_tx_recv: &mut embassy_sync::channel::Receiver<'d, ThreadModeRawMutex, u8, 20>, +) -> Result<(), Disconnected> { + loop { + let n = usb_tx_recv.recv().await; + let data = [n]; + trace!("USB OUT: {:x}", data); + usb_tx.write_packet(&data).await?; + } +} + +/// Read from the UART and write it to the USB TX send channel +async fn uart_read<'a>( + uart_rx: &mut PioUartRx<'a>, + usb_tx_send: &mut embassy_sync::channel::Sender<'a, ThreadModeRawMutex, u8, 20>, +) -> Result<(), Disconnected> { + let mut buf = [0; 1]; + loop { + let n = uart_rx.read(&mut buf).await.expect("UART read error"); + if n == 0 { + continue; + } + trace!("UART IN: {:x}", buf); + usb_tx_send.send(buf[0]).await; + } +} + +/// Read from the UART TX receive channel and write it to the UART +async fn uart_write<'a>( + uart_tx: &mut PioUartTx<'a>, + uart_rx_recv: &mut embassy_sync::channel::Receiver<'a, ThreadModeRawMutex, u8, 20>, +) -> Result<(), Disconnected> { + loop { + let n = uart_rx_recv.recv().await; + let data = [n]; + trace!("UART OUT: {:x}", data); + let _ = uart_tx.write(&data).await; } } mod uart { - use embassy_rp::peripherals::PIO0; - use embassy_rp::pio::{Common, Pio, PioPin, StateMachine}; - use embassy_rp::Peripheral; + use core::fmt::Debug; + use embassy_rp::peripherals::PIO0; + use embassy_rp::pio::{Pio, PioPin}; + use embassy_rp::Peripheral; + use embedded_io::ErrorKind; + + use crate::uart_rx::PioUartRx; + use crate::uart_tx::PioUartTx; use crate::PioIrqs; pub struct PioUart<'a> { - baud: u64, - pio: Common<'a, PIO0>, - sm0: StateMachine<'a, PIO0, 0>, - sm1: StateMachine<'a, PIO0, 1>, + tx: PioUartTx<'a>, + rx: PioUartRx<'a>, } impl<'a> PioUart<'a> { @@ -135,21 +229,25 @@ mod uart { rx_pin: impl PioPin, ) -> PioUart<'a> { let Pio { - mut common, - mut sm0, - mut sm1, - .. + mut common, sm0, sm1, .. } = Pio::new(pio, PioIrqs); - crate::uart_tx::setup_uart_tx_on_sm0(&mut common, &mut sm0, tx_pin, baud); - crate::uart_rx::setup_uart_rx_on_sm1(&mut common, &mut sm1, rx_pin, baud); + let (tx, origin) = PioUartTx::new(&mut common, sm0, tx_pin, baud, None); + let (rx, _) = PioUartRx::new(&mut common, sm1, rx_pin, baud, Some(origin)); - PioUart { - baud, - pio: common, - sm0, - sm1, - } + PioUart { tx, rx } + } + + pub fn split(self) -> (PioUartTx<'a>, PioUartRx<'a>) { + (self.tx, self.rx) + } + } + #[derive(defmt::Format, Debug)] + pub struct PioUartError {} + + impl embedded_io::Error for PioUartError { + fn kind(&self) -> ErrorKind { + ErrorKind::Other } } } @@ -159,18 +257,27 @@ mod uart_tx { use embassy_rp::peripherals::PIO0; use embassy_rp::pio::{Common, Config, Direction, FifoJoin, PioPin, ShiftDirection, StateMachine}; use embassy_rp::relocate::RelocatedProgram; + use embedded_io::asynch::Write; + use embedded_io::Io; use fixed::traits::ToFixed; use fixed_macro::types::U56F8; - pub fn setup_uart_tx_on_sm0<'a>( - common: &mut Common<'a, PIO0>, - sm_tx: &mut StateMachine<'a, PIO0, 0>, - tx_pin: impl PioPin, - baud: u64, - ) { - let prg = pio_proc::pio_asm!( - r#" - ;.program uart_tx + use crate::uart::PioUartError; + + pub struct PioUartTx<'a> { + sm_tx: StateMachine<'a, PIO0, 0>, + } + + impl<'a> PioUartTx<'a> { + pub fn new( + common: &mut Common<'a, PIO0>, + mut sm_tx: StateMachine<'a, PIO0, 0>, + tx_pin: impl PioPin, + baud: u64, + origin: Option, + ) -> (Self, u8) { + let mut prg = pio_proc::pio_asm!( + r#" .side_set 1 opt ; An 8n1 UART transmit program. @@ -182,23 +289,55 @@ mod uart_tx { out pins, 1 ; Shift 1 bit from OSR to the first OUT pin jmp x-- bitloop [6] ; Each loop iteration is 8 cycles. "# - ); - let tx_pin = common.make_pio_pin(tx_pin); - sm_tx.set_pins(Level::High, &[&tx_pin]); - sm_tx.set_pin_dirs(Direction::Out, &[&tx_pin]); + ); + prg.program.origin = origin; + let tx_pin = common.make_pio_pin(tx_pin); + sm_tx.set_pins(Level::High, &[&tx_pin]); + sm_tx.set_pin_dirs(Direction::Out, &[&tx_pin]); - let relocated = RelocatedProgram::new(&prg.program); - let mut cfg = Config::default(); + let relocated = RelocatedProgram::new(&prg.program); - cfg.use_program(&common.load_program(&relocated), &[&tx_pin]); - cfg.clock_divider = (U56F8!(125_000_000) / (8 * baud)).to_fixed(); - cfg.shift_out.auto_fill = false; - cfg.shift_out.direction = ShiftDirection::Right; - cfg.fifo_join = FifoJoin::TxOnly; - cfg.set_out_pins(&[&tx_pin]); - cfg.set_set_pins(&[&tx_pin]); - sm_tx.set_config(&cfg); - sm_tx.set_enable(true) + let mut cfg = Config::default(); + + cfg.set_out_pins(&[&tx_pin]); + cfg.use_program(&common.load_program(&relocated), &[&tx_pin]); + cfg.shift_out.auto_fill = false; + cfg.shift_out.direction = ShiftDirection::Right; + cfg.fifo_join = FifoJoin::TxOnly; + cfg.clock_divider = (U56F8!(125_000_000) / (8 * baud)).to_fixed(); + sm_tx.set_config(&cfg); + sm_tx.set_enable(true); + + // The 4 state machines of the PIO each have their own program counter that starts taking + // instructions at an offset (origin) of the 32 instruction "space" the PIO device has. + // It is up to the programmer to sort out where to place these instructions. + // From the pio_asm! macro you get a ProgramWithDefines which has a field .program.origin + // which takes an Option. + // + // When you load more than one RelocatedProgram into the PIO, + // you load your first program at origin = 0. + // The RelocatedProgram has .code().count() which returns a usize, + // for which you can then use as your next program's origin. + let offset = relocated.code().count() as u8 + origin.unwrap_or_default(); + (Self { sm_tx }, offset) + } + + pub async fn write_u8(&mut self, data: u8) { + self.sm_tx.tx().wait_push(data as u32).await; + } + } + + impl Io for PioUartTx<'_> { + type Error = PioUartError; + } + + impl Write for PioUartTx<'_> { + async fn write(&mut self, buf: &[u8]) -> Result { + for byte in buf { + self.write_u8(*byte).await; + } + Ok(buf.len()) + } } } @@ -207,19 +346,27 @@ mod uart_rx { use embassy_rp::peripherals::PIO0; use embassy_rp::pio::{Common, Config, Direction, FifoJoin, PioPin, ShiftDirection, StateMachine}; use embassy_rp::relocate::RelocatedProgram; + use embedded_io::asynch::Read; + use embedded_io::Io; use fixed::traits::ToFixed; use fixed_macro::types::U56F8; - pub fn setup_uart_rx_on_sm1<'a>( - common: &mut Common<'a, PIO0>, - sm_rx: &mut StateMachine<'a, PIO0, 1>, - rx_pin: impl PioPin, - baud: u64, - ) { - let prg = pio_proc::pio_asm!( - r#" - ;.program uart_rx + use crate::uart::PioUartError; + pub struct PioUartRx<'a> { + sm_rx: StateMachine<'a, PIO0, 1>, + } + + impl<'a> PioUartRx<'a> { + pub fn new( + common: &mut Common<'a, PIO0>, + mut sm_rx: StateMachine<'a, PIO0, 1>, + rx_pin: impl PioPin, + baud: u64, + origin: Option, + ) -> (Self, u8) { + let mut prg = pio_proc::pio_asm!( + r#" ; Slightly more fleshed-out 8n1 UART receiver which handles framing errors and ; break conditions more gracefully. ; IN pin 0 and JMP pin are both mapped to the GPIO used as UART RX. @@ -227,36 +374,58 @@ mod uart_rx { start: wait 0 pin 0 ; Stall until start bit is asserted set x, 7 [10] ; Preload bit counter, then delay until halfway through - bitloop: ; the first data bit (12 cycles incl wait, set). + rx_bitloop: ; the first data bit (12 cycles incl wait, set). in pins, 1 ; Shift data bit into ISR - jmp x-- bitloop [6] ; Loop 8 times, each loop iteration is 8 cycles - jmp pin good_stop ; Check stop bit (should be high) + jmp x-- rx_bitloop [6] ; Loop 8 times, each loop iteration is 8 cycles + jmp pin good_rx_stop ; Check stop bit (should be high) irq 4 rel ; Either a framing error or a break. Set a sticky flag, wait 1 pin 0 ; and wait for line to return to idle state. jmp start ; Don't push data if we didn't see good framing. - good_stop: ; No delay before returning to start; a little slack is + good_rx_stop: ; No delay before returning to start; a little slack is push ; important in case the TX clock is slightly too fast. "# - ); + ); + prg.program.origin = origin; + let relocated = RelocatedProgram::new(&prg.program); + let mut cfg = Config::default(); + cfg.use_program(&common.load_program(&relocated), &[]); - let rx_pin = common.make_pio_pin(rx_pin); - sm_rx.set_pins(Level::High, &[&rx_pin]); - sm_rx.set_pin_dirs(Direction::In, &[&rx_pin]); + let rx_pin = common.make_pio_pin(rx_pin); + sm_rx.set_pins(Level::High, &[&rx_pin]); + cfg.set_in_pins(&[&rx_pin]); + cfg.set_jmp_pin(&rx_pin); + sm_rx.set_pin_dirs(Direction::In, &[&rx_pin]); - let relocated = RelocatedProgram::new(&prg.program); - let mut cfg = Config::default(); + cfg.clock_divider = (U56F8!(125_000_000) / (8 * baud)).to_fixed(); + cfg.shift_out.auto_fill = false; + cfg.shift_out.direction = ShiftDirection::Right; + cfg.fifo_join = FifoJoin::RxOnly; + sm_rx.set_config(&cfg); + sm_rx.set_enable(true); - cfg.use_program(&common.load_program(&relocated), &[&rx_pin]); - cfg.clock_divider = (U56F8!(125_000_000) / (8 * baud)).to_fixed(); - cfg.shift_out.auto_fill = false; - cfg.shift_out.direction = ShiftDirection::Right; - cfg.fifo_join = FifoJoin::RxOnly; - cfg.set_in_pins(&[&rx_pin]); - cfg.set_jmp_pin(&rx_pin); - // cfg.set_set_pins(&[&rx_pin]); - sm_rx.set_config(&cfg); - sm_rx.set_enable(true) + let offset = relocated.code().count() as u8 + origin.unwrap_or_default(); + (Self { sm_rx }, offset) + } + + pub async fn read_u8(&mut self) -> u8 { + self.sm_rx.rx().wait_pull().await as u8 + } + } + + impl Io for PioUartRx<'_> { + type Error = PioUartError; + } + + impl Read for PioUartRx<'_> { + async fn read(&mut self, buf: &mut [u8]) -> Result { + let mut i = 0; + while i < buf.len() { + buf[i] = self.read_u8().await; + i += 1; + } + Ok(i) + } } } From 0f1ff77fcc3c085f9969bac4963d784c022e6044 Mon Sep 17 00:00:00 2001 From: Michael van Niekerk Date: Fri, 28 Jul 2023 11:38:08 +0200 Subject: [PATCH 24/68] Comments --- examples/rp/.idea/.gitignore | 8 -------- examples/rp/.idea/modules.xml | 8 -------- examples/rp/.idea/rp.iml | 12 ------------ examples/rp/.idea/vcs.xml | 6 ------ 4 files changed, 34 deletions(-) delete mode 100644 examples/rp/.idea/.gitignore delete mode 100644 examples/rp/.idea/modules.xml delete mode 100644 examples/rp/.idea/rp.iml delete mode 100644 examples/rp/.idea/vcs.xml diff --git a/examples/rp/.idea/.gitignore b/examples/rp/.idea/.gitignore deleted file mode 100644 index 13566b81..00000000 --- a/examples/rp/.idea/.gitignore +++ /dev/null @@ -1,8 +0,0 @@ -# Default ignored files -/shelf/ -/workspace.xml -# Editor-based HTTP Client requests -/httpRequests/ -# Datasource local storage ignored files -/dataSources/ -/dataSources.local.xml diff --git a/examples/rp/.idea/modules.xml b/examples/rp/.idea/modules.xml deleted file mode 100644 index 06ff4b23..00000000 --- a/examples/rp/.idea/modules.xml +++ /dev/null @@ -1,8 +0,0 @@ - - - - - - - - \ No newline at end of file diff --git a/examples/rp/.idea/rp.iml b/examples/rp/.idea/rp.iml deleted file mode 100644 index 9b4cf845..00000000 --- a/examples/rp/.idea/rp.iml +++ /dev/null @@ -1,12 +0,0 @@ - - - - - - - - - - - - \ No newline at end of file diff --git a/examples/rp/.idea/vcs.xml b/examples/rp/.idea/vcs.xml deleted file mode 100644 index b2bdec2d..00000000 --- a/examples/rp/.idea/vcs.xml +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - \ No newline at end of file From 91338adc159dd026ba56dcb4e991ed9f60053bb0 Mon Sep 17 00:00:00 2001 From: Michael van Niekerk Date: Fri, 28 Jul 2023 11:56:59 +0200 Subject: [PATCH 25/68] Don't include embedded-hal-common --- examples/rp/Cargo.toml | 1 - 1 file changed, 1 deletion(-) diff --git a/examples/rp/Cargo.toml b/examples/rp/Cargo.toml index 2a018ad0..c812cb3e 100644 --- a/examples/rp/Cargo.toml +++ b/examples/rp/Cargo.toml @@ -7,7 +7,6 @@ license = "MIT OR Apache-2.0" [dependencies] embassy-embedded-hal = { version = "0.1.0", path = "../../embassy-embedded-hal", features = ["defmt"] } -embassy-hal-common = { version = "0.1.0", path = "../../embassy-hal-common", features = ["defmt"] } embassy-sync = { version = "0.2.0", path = "../../embassy-sync", features = ["defmt"] } embassy-executor = { version = "0.2.0", path = "../../embassy-executor", features = ["nightly", "arch-cortex-m", "executor-thread", "executor-interrupt", "defmt", "integrated-timers"] } embassy-time = { version = "0.1.2", path = "../../embassy-time", features = ["nightly", "unstable-traits", "defmt", "defmt-timestamp-uptime"] } From 146c744223056561c6be61dda791993d939d0ae0 Mon Sep 17 00:00:00 2001 From: Michael van Niekerk Date: Fri, 28 Jul 2023 12:56:31 +0200 Subject: [PATCH 26/68] Fixes as per PR --- examples/rp/src/bin/pio_uart.rs | 114 ++++++++++++++------------------ 1 file changed, 48 insertions(+), 66 deletions(-) diff --git a/examples/rp/src/bin/pio_uart.rs b/examples/rp/src/bin/pio_uart.rs index eeb213e1..c978f8f0 100644 --- a/examples/rp/src/bin/pio_uart.rs +++ b/examples/rp/src/bin/pio_uart.rs @@ -19,7 +19,7 @@ use embassy_rp::peripherals::{PIO0, USB}; use embassy_rp::pio::InterruptHandler as PioInterruptHandler; use embassy_rp::usb::{Driver, Instance, InterruptHandler}; use embassy_sync::blocking_mutex::raw::ThreadModeRawMutex; -use embassy_sync::channel::Channel; +use embassy_sync::pipe::Pipe; use embassy_usb::class::cdc_acm::{CdcAcmClass, Receiver, Sender, State}; use embassy_usb::driver::EndpointError; use embassy_usb::{Builder, Config}; @@ -30,11 +30,8 @@ use crate::uart::PioUart; use crate::uart_rx::PioUartRx; use crate::uart_tx::PioUartTx; -bind_interrupts!(struct UsbIrqs { +bind_interrupts!(struct Irqs { USBCTRL_IRQ => InterruptHandler; -}); - -bind_interrupts!(struct PioIrqs { PIO0_IRQ_0 => PioInterruptHandler; }); @@ -45,7 +42,7 @@ async fn main(_spawner: Spawner) { let p = embassy_rp::init(Default::default()); // Create the driver, from the HAL. - let driver = Driver::new(p.USB, UsbIrqs); + let driver = Driver::new(p.USB, Irqs); // Create embassy-usb Config let mut config = Config::new(0xc0de, 0xcafe); @@ -90,17 +87,17 @@ async fn main(_spawner: Spawner) { let usb_fut = usb.run(); // PIO UART setup - let uart = PioUart::new(9600, p.PIO0, p.PIN_4, p.PIN_5).await; + let uart = PioUart::new(9600, p.PIO0, p.PIN_4, p.PIN_5); let (mut uart_tx, mut uart_rx) = uart.split(); - // Channels setup - static USB_CHANNEL_TX: Channel = Channel::::new(); - let mut usb_channel_tx_send = USB_CHANNEL_TX.sender(); - let mut usb_channel_tx_recv = USB_CHANNEL_TX.receiver(); + // Pipe setup + static USB_PIPE: Pipe = Pipe::new(); + let mut usb_pipe_writer = USB_PIPE.writer(); + let mut usb_pipe_reader = USB_PIPE.reader(); - static UART_CHANNEL_TX: Channel = Channel::::new(); - let mut uart_channel_tx_send = UART_CHANNEL_TX.sender(); - let mut uart_channel_tx_recv = UART_CHANNEL_TX.receiver(); + static UART_PIPE: Pipe = Pipe::new(); + let mut uart_pipe_writer = UART_PIPE.writer(); + let mut uart_pipe_reader = UART_PIPE.reader(); let (mut usb_tx, mut usb_rx) = class.split(); @@ -111,8 +108,8 @@ async fn main(_spawner: Spawner) { usb_rx.wait_connection().await; info!("Connected"); let _ = join( - usb_read(&mut usb_rx, &mut uart_channel_tx_send), - usb_write(&mut usb_tx, &mut usb_channel_tx_recv), + usb_read(&mut usb_rx, &mut uart_pipe_writer), + usb_write(&mut usb_tx, &mut usb_pipe_reader), ) .await; info!("Disconnected"); @@ -120,15 +117,10 @@ async fn main(_spawner: Spawner) { }; // Read + write from UART - let uart_future = async { - loop { - let _ = join( - uart_read(&mut uart_rx, &mut usb_channel_tx_send), - uart_write(&mut uart_tx, &mut uart_channel_tx_recv), - ) - .await; - } - }; + let uart_future = join( + uart_read(&mut uart_rx, &mut usb_pipe_writer), + uart_write(&mut uart_tx, &mut uart_pipe_reader), + ); // Run everything concurrently. // If we had made everything `'static` above instead, we could do this using separate tasks instead. @@ -146,75 +138,73 @@ impl From for Disconnected { } } -/// Read from the USB and write it to the UART TX send channel +/// Read from the USB and write it to the UART TX pipe async fn usb_read<'d, T: Instance + 'd>( usb_rx: &mut Receiver<'d, Driver<'d, T>>, - uart_tx_send: &mut embassy_sync::channel::Sender<'d, ThreadModeRawMutex, u8, 20>, + uart_pipe_writer: &mut embassy_sync::pipe::Writer<'static, ThreadModeRawMutex, 20>, ) -> Result<(), Disconnected> { let mut buf = [0; 64]; loop { let n = usb_rx.read_packet(&mut buf).await?; let data = &buf[..n]; trace!("USB IN: {:x}", data); - for byte in data { - uart_tx_send.send(*byte).await; - } + uart_pipe_writer.write(data).await; } } -/// Read from the USB TX receive channel and write it to the USB +/// Read from the USB TX pipe and write it to the USB async fn usb_write<'d, T: Instance + 'd>( usb_tx: &mut Sender<'d, Driver<'d, T>>, - usb_tx_recv: &mut embassy_sync::channel::Receiver<'d, ThreadModeRawMutex, u8, 20>, + usb_pipe_reader: &mut embassy_sync::pipe::Reader<'d, ThreadModeRawMutex, 20>, ) -> Result<(), Disconnected> { + let mut buf = [0; 64]; loop { - let n = usb_tx_recv.recv().await; - let data = [n]; + let n = usb_pipe_reader.read(&mut buf).await; + let data = &buf[..n]; trace!("USB OUT: {:x}", data); usb_tx.write_packet(&data).await?; } } -/// Read from the UART and write it to the USB TX send channel +/// Read from the UART and write it to the USB TX pipe async fn uart_read<'a>( uart_rx: &mut PioUartRx<'a>, - usb_tx_send: &mut embassy_sync::channel::Sender<'a, ThreadModeRawMutex, u8, 20>, -) -> Result<(), Disconnected> { - let mut buf = [0; 1]; + usb_pipe_writer: &mut embassy_sync::pipe::Writer<'static, ThreadModeRawMutex, 20>, +) -> ! { + let mut buf = [0; 64]; loop { let n = uart_rx.read(&mut buf).await.expect("UART read error"); if n == 0 { continue; } + let data = &buf[..n]; trace!("UART IN: {:x}", buf); - usb_tx_send.send(buf[0]).await; + usb_pipe_writer.write(data).await; } } -/// Read from the UART TX receive channel and write it to the UART +/// Read from the UART TX pipe and write it to the UART async fn uart_write<'a>( uart_tx: &mut PioUartTx<'a>, - uart_rx_recv: &mut embassy_sync::channel::Receiver<'a, ThreadModeRawMutex, u8, 20>, -) -> Result<(), Disconnected> { + uart_pipe_reader: &mut embassy_sync::pipe::Reader<'a, ThreadModeRawMutex, 20>, +) -> ! { + let mut buf = [0; 64]; loop { - let n = uart_rx_recv.recv().await; - let data = [n]; + let n = uart_pipe_reader.read(&mut buf).await; + let data = &buf[..n]; trace!("UART OUT: {:x}", data); let _ = uart_tx.write(&data).await; } } mod uart { - use core::fmt::Debug; - use embassy_rp::peripherals::PIO0; use embassy_rp::pio::{Pio, PioPin}; use embassy_rp::Peripheral; - use embedded_io::ErrorKind; use crate::uart_rx::PioUartRx; use crate::uart_tx::PioUartTx; - use crate::PioIrqs; + use crate::Irqs; pub struct PioUart<'a> { tx: PioUartTx<'a>, @@ -222,7 +212,7 @@ mod uart { } impl<'a> PioUart<'a> { - pub async fn new( + pub fn new( baud: u64, pio: impl Peripheral

+ 'a, tx_pin: impl PioPin, @@ -230,7 +220,7 @@ mod uart { ) -> PioUart<'a> { let Pio { mut common, sm0, sm1, .. - } = Pio::new(pio, PioIrqs); + } = Pio::new(pio, Irqs); let (tx, origin) = PioUartTx::new(&mut common, sm0, tx_pin, baud, None); let (rx, _) = PioUartRx::new(&mut common, sm1, rx_pin, baud, Some(origin)); @@ -242,17 +232,11 @@ mod uart { (self.tx, self.rx) } } - #[derive(defmt::Format, Debug)] - pub struct PioUartError {} - - impl embedded_io::Error for PioUartError { - fn kind(&self) -> ErrorKind { - ErrorKind::Other - } - } } mod uart_tx { + use core::convert::Infallible; + use embassy_rp::gpio::Level; use embassy_rp::peripherals::PIO0; use embassy_rp::pio::{Common, Config, Direction, FifoJoin, PioPin, ShiftDirection, StateMachine}; @@ -262,8 +246,6 @@ mod uart_tx { use fixed::traits::ToFixed; use fixed_macro::types::U56F8; - use crate::uart::PioUartError; - pub struct PioUartTx<'a> { sm_tx: StateMachine<'a, PIO0, 0>, } @@ -328,11 +310,11 @@ mod uart_tx { } impl Io for PioUartTx<'_> { - type Error = PioUartError; + type Error = Infallible; } impl Write for PioUartTx<'_> { - async fn write(&mut self, buf: &[u8]) -> Result { + async fn write(&mut self, buf: &[u8]) -> Result { for byte in buf { self.write_u8(*byte).await; } @@ -342,6 +324,8 @@ mod uart_tx { } mod uart_rx { + use core::convert::Infallible; + use embassy_rp::gpio::Level; use embassy_rp::peripherals::PIO0; use embassy_rp::pio::{Common, Config, Direction, FifoJoin, PioPin, ShiftDirection, StateMachine}; @@ -351,8 +335,6 @@ mod uart_rx { use fixed::traits::ToFixed; use fixed_macro::types::U56F8; - use crate::uart::PioUartError; - pub struct PioUartRx<'a> { sm_rx: StateMachine<'a, PIO0, 1>, } @@ -415,11 +397,11 @@ mod uart_rx { } impl Io for PioUartRx<'_> { - type Error = PioUartError; + type Error = Infallible; } impl Read for PioUartRx<'_> { - async fn read(&mut self, buf: &mut [u8]) -> Result { + async fn read(&mut self, buf: &mut [u8]) -> Result { let mut i = 0; while i < buf.len() { buf[i] = self.read_u8().await; From 38b5d1ee2b1319a6f84c8894f05c650bb3630ece Mon Sep 17 00:00:00 2001 From: chemicstry Date: Fri, 28 Jul 2023 14:22:24 +0300 Subject: [PATCH 27/68] stm32/can: implement more convenience methods --- embassy-stm32/src/can/bxcan.rs | 160 ++++++++++++++++++++++++--------- 1 file changed, 119 insertions(+), 41 deletions(-) diff --git a/embassy-stm32/src/can/bxcan.rs b/embassy-stm32/src/can/bxcan.rs index 8b8244d4..d4ec2381 100644 --- a/embassy-stm32/src/can/bxcan.rs +++ b/embassy-stm32/src/can/bxcan.rs @@ -77,6 +77,7 @@ pub struct Can<'d, T: Instance> { } #[derive(Debug)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] pub enum BusError { Stuff, Form, @@ -90,6 +91,22 @@ pub enum BusError { BusWarning, } +#[derive(Debug)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +pub enum TryReadError { + /// Bus error + BusError(BusError), + /// Receive buffer is empty + Empty, +} + +#[derive(Debug)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +pub enum TryWriteError { + /// All transmit mailboxes are full + Full, +} + impl<'d, T: Instance> Can<'d, T> { /// Creates a new Bxcan instance, keeping the peripheral in sleep mode. /// You must call [Can::enable_non_blocking] to use the peripheral. @@ -175,56 +192,46 @@ impl<'d, T: Instance> Can<'d, T> { /// Queues the message to be sent but exerts backpressure pub async fn write(&mut self, frame: &Frame) -> bxcan::TransmitStatus { - poll_fn(|cx| { - T::state().tx_waker.register(cx.waker()); - if let Ok(status) = self.can.borrow_mut().transmit(frame) { - return Poll::Ready(status); - } - - Poll::Pending - }) - .await + CanTx { can: &self.can }.write(frame).await } - pub async fn flush(&self, mb: bxcan::Mailbox) { - poll_fn(|cx| { - T::state().tx_waker.register(cx.waker()); - if T::regs().tsr().read().tme(mb.index()) { - return Poll::Ready(()); - } + /// Attempts to transmit a frame without blocking. + /// + /// Returns [Err(TryWriteError::Full)] if all transmit mailboxes are full. + pub fn try_write(&mut self, frame: &Frame) -> Result { + CanTx { can: &self.can }.try_write(frame) + } - Poll::Pending - }) - .await; + /// Waits for a specific transmit mailbox to become empty + pub async fn flush(&self, mb: bxcan::Mailbox) { + CanTx { can: &self.can }.flush(mb).await + } + + /// Waits until any of the transmit mailboxes become empty + pub async fn flush_any(&self) { + CanTx { can: &self.can }.flush_any().await + } + + /// Waits until all of the transmit mailboxes become empty + pub async fn flush_all(&self) { + CanTx { can: &self.can }.flush_all().await } /// Returns a tuple of the time the message was received and the message frame pub async fn read(&mut self) -> Result<(u16, bxcan::Frame), BusError> { - poll_fn(|cx| { - T::state().err_waker.register(cx.waker()); - if let Poll::Ready((time, frame)) = T::state().rx_queue.recv().poll_unpin(cx) { - return Poll::Ready(Ok((time, frame))); - } else if let Some(err) = self.curr_error() { - return Poll::Ready(Err(err)); - } - - Poll::Pending - }) - .await + CanRx { can: &self.can }.read().await } - fn curr_error(&self) -> Option { - let err = { T::regs().esr().read() }; - if err.boff() { - return Some(BusError::BusOff); - } else if err.epvf() { - return Some(BusError::BusPassive); - } else if err.ewgf() { - return Some(BusError::BusWarning); - } else if let Some(err) = err.lec().into_bus_err() { - return Some(err); - } - None + /// Attempts to read a can frame without blocking. + /// + /// Returns [Err(TryReadError::Empty)] if there are no frames in the rx queue. + pub fn try_read(&mut self) -> Result<(u16, bxcan::Frame), TryReadError> { + CanRx { can: &self.can }.try_read() + } + + /// Waits while receive queue is empty. + pub async fn wait_not_empty(&mut self) { + CanRx { can: &self.can }.wait_not_empty().await } unsafe fn receive_fifo(fifo: RxFifo) { @@ -386,6 +393,14 @@ impl<'c, 'd, T: Instance> CanTx<'c, 'd, T> { .await } + /// Attempts to transmit a frame without blocking. + /// + /// Returns [Err(TryWriteError::Full)] if all transmit mailboxes are full. + pub fn try_write(&mut self, frame: &Frame) -> Result { + self.can.borrow_mut().transmit(frame).map_err(|_| TryWriteError::Full) + } + + /// Waits for a specific transmit mailbox to become empty pub async fn flush(&self, mb: bxcan::Mailbox) { poll_fn(|cx| { T::state().tx_waker.register(cx.waker()); @@ -397,6 +412,42 @@ impl<'c, 'd, T: Instance> CanTx<'c, 'd, T> { }) .await; } + + /// Waits until any of the transmit mailboxes become empty + pub async fn flush_any(&self) { + poll_fn(|cx| { + T::state().tx_waker.register(cx.waker()); + + let tsr = T::regs().tsr().read(); + if tsr.tme(bxcan::Mailbox::Mailbox0.index()) + || tsr.tme(bxcan::Mailbox::Mailbox1.index()) + || tsr.tme(bxcan::Mailbox::Mailbox2.index()) + { + return Poll::Ready(()); + } + + Poll::Pending + }) + .await; + } + + /// Waits until all of the transmit mailboxes become empty + pub async fn flush_all(&self) { + poll_fn(|cx| { + T::state().tx_waker.register(cx.waker()); + + let tsr = T::regs().tsr().read(); + if tsr.tme(bxcan::Mailbox::Mailbox0.index()) + && tsr.tme(bxcan::Mailbox::Mailbox1.index()) + && tsr.tme(bxcan::Mailbox::Mailbox2.index()) + { + return Poll::Ready(()); + } + + Poll::Pending + }) + .await; + } } #[allow(dead_code)] @@ -419,6 +470,33 @@ impl<'c, 'd, T: Instance> CanRx<'c, 'd, T> { .await } + /// Attempts to read a CAN frame without blocking. + /// + /// Returns [Err(TryReadError::Empty)] if there are no frames in the rx queue. + pub fn try_read(&mut self) -> Result<(u16, bxcan::Frame), TryReadError> { + if let Ok(envelope) = T::state().rx_queue.try_recv() { + return Ok(envelope); + } + + if let Some(err) = self.curr_error() { + return Err(TryReadError::BusError(err)); + } + + Err(TryReadError::Empty) + } + + /// Waits while receive queue is empty. + pub async fn wait_not_empty(&mut self) { + poll_fn(|cx| { + if T::state().rx_queue.poll_ready_to_receive(cx) { + Poll::Ready(()) + } else { + Poll::Pending + } + }) + .await + } + fn curr_error(&self) -> Option { let err = { T::regs().esr().read() }; if err.boff() { From 036e6ae30c9e772ef8ef20439f121e108b9106f0 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Fri, 28 Jul 2023 13:23:22 +0200 Subject: [PATCH 28/68] Rename embassy-hal-common to embassy-hal-internal, document it's for internal use only. (#1700) --- .github/ci/test.sh | 2 +- .../Cargo.toml | 2 +- embassy-hal-internal/README.md | 16 ++++++++++++++++ .../build.rs | 0 .../src/atomic_ring_buffer.rs | 0 .../src/drop.rs | 0 .../src/fmt.rs | 0 .../src/interrupt.rs | 0 .../src/lib.rs | 1 + .../src/macros.rs | 0 .../src/peripheral.rs | 0 .../src/ratio.rs | 0 .../src/ring_buffer.rs | 0 embassy-lora/Cargo.toml | 1 + embassy-nrf/Cargo.toml | 2 +- embassy-nrf/src/buffered_uarte.rs | 4 ++-- embassy-nrf/src/chips/nrf52805.rs | 4 ++-- embassy-nrf/src/chips/nrf52810.rs | 4 ++-- embassy-nrf/src/chips/nrf52811.rs | 4 ++-- embassy-nrf/src/chips/nrf52820.rs | 4 ++-- embassy-nrf/src/chips/nrf52832.rs | 4 ++-- embassy-nrf/src/chips/nrf52833.rs | 4 ++-- embassy-nrf/src/chips/nrf52840.rs | 4 ++-- embassy-nrf/src/chips/nrf5340_app.rs | 4 ++-- embassy-nrf/src/chips/nrf5340_net.rs | 4 ++-- embassy-nrf/src/chips/nrf9160.rs | 4 ++-- embassy-nrf/src/gpio.rs | 2 +- embassy-nrf/src/gpiote.rs | 2 +- embassy-nrf/src/i2s.rs | 4 ++-- embassy-nrf/src/lib.rs | 4 ++-- embassy-nrf/src/nvmc.rs | 2 +- embassy-nrf/src/pdm.rs | 4 ++-- embassy-nrf/src/ppi/dppi.rs | 2 +- embassy-nrf/src/ppi/mod.rs | 2 +- embassy-nrf/src/ppi/ppi.rs | 2 +- embassy-nrf/src/pwm.rs | 2 +- embassy-nrf/src/qdec.rs | 2 +- embassy-nrf/src/qspi.rs | 4 ++-- embassy-nrf/src/rng.rs | 4 ++-- embassy-nrf/src/saadc.rs | 4 ++-- embassy-nrf/src/spim.rs | 2 +- embassy-nrf/src/spis.rs | 2 +- embassy-nrf/src/temp.rs | 4 ++-- embassy-nrf/src/timer.rs | 2 +- embassy-nrf/src/twim.rs | 2 +- embassy-nrf/src/twis.rs | 2 +- embassy-nrf/src/uarte.rs | 4 ++-- embassy-nrf/src/usb/mod.rs | 2 +- embassy-rp/Cargo.toml | 4 ++-- embassy-rp/src/adc.rs | 2 +- embassy-rp/src/clocks.rs | 2 +- embassy-rp/src/dma.rs | 2 +- embassy-rp/src/flash.rs | 2 +- embassy-rp/src/gpio.rs | 2 +- embassy-rp/src/i2c.rs | 2 +- embassy-rp/src/lib.rs | 8 ++++---- embassy-rp/src/pio.rs | 2 +- embassy-rp/src/pwm.rs | 2 +- embassy-rp/src/rtc/mod.rs | 2 +- embassy-rp/src/spi.rs | 2 +- embassy-rp/src/uart/buffered.rs | 2 +- embassy-rp/src/uart/mod.rs | 2 +- embassy-stm32-wpan/Cargo.toml | 4 ++-- embassy-stm32-wpan/src/lib.rs | 2 +- embassy-stm32/Cargo.toml | 4 ++-- embassy-stm32/build.rs | 10 +++++----- embassy-stm32/src/adc/f1.rs | 2 +- embassy-stm32/src/adc/v1.rs | 2 +- embassy-stm32/src/adc/v2.rs | 2 +- embassy-stm32/src/adc/v3.rs | 2 +- embassy-stm32/src/adc/v4.rs | 2 +- embassy-stm32/src/can/bxcan.rs | 2 +- embassy-stm32/src/can/fdcan.rs | 2 +- embassy-stm32/src/crc/v1.rs | 2 +- embassy-stm32/src/crc/v2v3.rs | 2 +- embassy-stm32/src/dac/mod.rs | 2 +- embassy-stm32/src/dcmi.rs | 2 +- embassy-stm32/src/dma/bdma.rs | 2 +- embassy-stm32/src/dma/dma.rs | 2 +- embassy-stm32/src/dma/gpdma.rs | 2 +- embassy-stm32/src/dma/mod.rs | 2 +- embassy-stm32/src/eth/v1/mod.rs | 2 +- embassy-stm32/src/eth/v2/mod.rs | 2 +- embassy-stm32/src/exti.rs | 2 +- embassy-stm32/src/flash/asynch.rs | 4 ++-- embassy-stm32/src/flash/common.rs | 4 ++-- embassy-stm32/src/flash/f4.rs | 2 +- embassy-stm32/src/fmc.rs | 2 +- embassy-stm32/src/gpio.rs | 2 +- embassy-stm32/src/i2c/v1.rs | 2 +- embassy-stm32/src/i2c/v2.rs | 4 ++-- embassy-stm32/src/i2s.rs | 2 +- embassy-stm32/src/lib.rs | 4 ++-- embassy-stm32/src/pwm/complementary_pwm.rs | 2 +- embassy-stm32/src/pwm/simple_pwm.rs | 2 +- embassy-stm32/src/qspi/mod.rs | 2 +- embassy-stm32/src/rcc/f4.rs | 2 +- embassy-stm32/src/rcc/h7.rs | 2 +- embassy-stm32/src/rcc/l4.rs | 2 +- embassy-stm32/src/rng.rs | 2 +- embassy-stm32/src/rtc/mod.rs | 2 +- embassy-stm32/src/sdmmc/mod.rs | 4 ++-- embassy-stm32/src/spi/mod.rs | 2 +- embassy-stm32/src/usart/buffered.rs | 2 +- embassy-stm32/src/usart/mod.rs | 4 ++-- embassy-stm32/src/usart/ringbuffered.rs | 2 +- embassy-stm32/src/usb/usb.rs | 2 +- embassy-stm32/src/usb_otg/usb.rs | 2 +- embassy-stm32/src/wdg/mod.rs | 2 +- examples/stm32g4/Cargo.toml | 1 - 110 files changed, 150 insertions(+), 133 deletions(-) rename {embassy-hal-common => embassy-hal-internal}/Cargo.toml (95%) create mode 100644 embassy-hal-internal/README.md rename {embassy-hal-common => embassy-hal-internal}/build.rs (100%) rename {embassy-hal-common => embassy-hal-internal}/src/atomic_ring_buffer.rs (100%) rename {embassy-hal-common => embassy-hal-internal}/src/drop.rs (100%) rename {embassy-hal-common => embassy-hal-internal}/src/fmt.rs (100%) rename {embassy-hal-common => embassy-hal-internal}/src/interrupt.rs (100%) rename {embassy-hal-common => embassy-hal-internal}/src/lib.rs (89%) rename {embassy-hal-common => embassy-hal-internal}/src/macros.rs (100%) rename {embassy-hal-common => embassy-hal-internal}/src/peripheral.rs (100%) rename {embassy-hal-common => embassy-hal-internal}/src/ratio.rs (100%) rename {embassy-hal-common => embassy-hal-internal}/src/ring_buffer.rs (100%) diff --git a/.github/ci/test.sh b/.github/ci/test.sh index d014e4bd..2892bcf8 100755 --- a/.github/ci/test.sh +++ b/.github/ci/test.sh @@ -13,7 +13,7 @@ hashtime save /ci/cache/filetime.json cargo test --manifest-path ./embassy-sync/Cargo.toml cargo test --manifest-path ./embassy-embedded-hal/Cargo.toml -cargo test --manifest-path ./embassy-hal-common/Cargo.toml +cargo test --manifest-path ./embassy-hal-internal/Cargo.toml cargo test --manifest-path ./embassy-time/Cargo.toml --features generic-queue cargo test --manifest-path ./embassy-boot/boot/Cargo.toml diff --git a/embassy-hal-common/Cargo.toml b/embassy-hal-internal/Cargo.toml similarity index 95% rename from embassy-hal-common/Cargo.toml rename to embassy-hal-internal/Cargo.toml index 18c758d7..42e03199 100644 --- a/embassy-hal-common/Cargo.toml +++ b/embassy-hal-internal/Cargo.toml @@ -1,5 +1,5 @@ [package] -name = "embassy-hal-common" +name = "embassy-hal-internal" version = "0.1.0" edition = "2021" license = "MIT OR Apache-2.0" diff --git a/embassy-hal-internal/README.md b/embassy-hal-internal/README.md new file mode 100644 index 00000000..d6539701 --- /dev/null +++ b/embassy-hal-internal/README.md @@ -0,0 +1,16 @@ +# embassy-macros + +An [Embassy](https://embassy.dev) project. + +Internal implementation details for Embassy HALs. DO NOT USE DIRECTLY. Embassy HALs (`embassy-nrf`, `embassy-stm32`, `embassy-rp`) already reexport +everything you need to use them effectively. + +## License + +This work is licensed under either of + +- Apache License, Version 2.0 ([LICENSE-APACHE](LICENSE-APACHE) or + ) +- MIT license ([LICENSE-MIT](LICENSE-MIT) or ) + +at your option. diff --git a/embassy-hal-common/build.rs b/embassy-hal-internal/build.rs similarity index 100% rename from embassy-hal-common/build.rs rename to embassy-hal-internal/build.rs diff --git a/embassy-hal-common/src/atomic_ring_buffer.rs b/embassy-hal-internal/src/atomic_ring_buffer.rs similarity index 100% rename from embassy-hal-common/src/atomic_ring_buffer.rs rename to embassy-hal-internal/src/atomic_ring_buffer.rs diff --git a/embassy-hal-common/src/drop.rs b/embassy-hal-internal/src/drop.rs similarity index 100% rename from embassy-hal-common/src/drop.rs rename to embassy-hal-internal/src/drop.rs diff --git a/embassy-hal-common/src/fmt.rs b/embassy-hal-internal/src/fmt.rs similarity index 100% rename from embassy-hal-common/src/fmt.rs rename to embassy-hal-internal/src/fmt.rs diff --git a/embassy-hal-common/src/interrupt.rs b/embassy-hal-internal/src/interrupt.rs similarity index 100% rename from embassy-hal-common/src/interrupt.rs rename to embassy-hal-internal/src/interrupt.rs diff --git a/embassy-hal-common/src/lib.rs b/embassy-hal-internal/src/lib.rs similarity index 89% rename from embassy-hal-common/src/lib.rs rename to embassy-hal-internal/src/lib.rs index 235964aa..3640ea18 100644 --- a/embassy-hal-common/src/lib.rs +++ b/embassy-hal-internal/src/lib.rs @@ -1,5 +1,6 @@ #![no_std] #![allow(clippy::new_without_default)] +#![doc = include_str!("../README.md")] // This mod MUST go first, so that the others see its macros. pub(crate) mod fmt; diff --git a/embassy-hal-common/src/macros.rs b/embassy-hal-internal/src/macros.rs similarity index 100% rename from embassy-hal-common/src/macros.rs rename to embassy-hal-internal/src/macros.rs diff --git a/embassy-hal-common/src/peripheral.rs b/embassy-hal-internal/src/peripheral.rs similarity index 100% rename from embassy-hal-common/src/peripheral.rs rename to embassy-hal-internal/src/peripheral.rs diff --git a/embassy-hal-common/src/ratio.rs b/embassy-hal-internal/src/ratio.rs similarity index 100% rename from embassy-hal-common/src/ratio.rs rename to embassy-hal-internal/src/ratio.rs diff --git a/embassy-hal-common/src/ring_buffer.rs b/embassy-hal-internal/src/ring_buffer.rs similarity index 100% rename from embassy-hal-common/src/ring_buffer.rs rename to embassy-hal-internal/src/ring_buffer.rs diff --git a/embassy-lora/Cargo.toml b/embassy-lora/Cargo.toml index a77ed003..402ad2d7 100644 --- a/embassy-lora/Cargo.toml +++ b/embassy-lora/Cargo.toml @@ -26,5 +26,6 @@ embassy-stm32 = { version = "0.1.0", path = "../embassy-stm32", default-features embedded-hal-async = { version = "=0.2.0-alpha.2" } embedded-hal = { version = "0.2", features = ["unproven"] } +futures = { version = "0.3.17", default-features = false, features = [ "async-await" ] } lora-phy = { version = "1" } lorawan-device = { version = "0.10.0", default-features = false, features = ["async"], optional = true } diff --git a/embassy-nrf/Cargo.toml b/embassy-nrf/Cargo.toml index 57dd22f1..d10cd2c3 100644 --- a/embassy-nrf/Cargo.toml +++ b/embassy-nrf/Cargo.toml @@ -93,7 +93,7 @@ _gpio-p1 = [] [dependencies] embassy-time = { version = "0.1.2", path = "../embassy-time", optional = true } embassy-sync = { version = "0.2.0", path = "../embassy-sync" } -embassy-hal-common = {version = "0.1.0", path = "../embassy-hal-common", features = ["cortex-m", "prio-bits-3"] } +embassy-hal-internal = {version = "0.1.0", path = "../embassy-hal-internal", features = ["cortex-m", "prio-bits-3"] } embassy-embedded-hal = {version = "0.1.0", path = "../embassy-embedded-hal" } embassy-usb-driver = {version = "0.1.0", path = "../embassy-usb-driver", optional=true } diff --git a/embassy-nrf/src/buffered_uarte.rs b/embassy-nrf/src/buffered_uarte.rs index 9bc1c1e7..5a0a3c7c 100644 --- a/embassy-nrf/src/buffered_uarte.rs +++ b/embassy-nrf/src/buffered_uarte.rs @@ -15,8 +15,8 @@ use core::slice; use core::sync::atomic::{compiler_fence, AtomicU8, AtomicUsize, Ordering}; use core::task::Poll; -use embassy_hal_common::atomic_ring_buffer::RingBuffer; -use embassy_hal_common::{into_ref, PeripheralRef}; +use embassy_hal_internal::atomic_ring_buffer::RingBuffer; +use embassy_hal_internal::{into_ref, PeripheralRef}; use embassy_sync::waitqueue::AtomicWaker; // Re-export SVD variants to allow user to directly set values pub use pac::uarte0::{baudrate::BAUDRATE_A as Baudrate, config::PARITY_A as Parity}; diff --git a/embassy-nrf/src/chips/nrf52805.rs b/embassy-nrf/src/chips/nrf52805.rs index 8776000c..70e4b486 100644 --- a/embassy-nrf/src/chips/nrf52805.rs +++ b/embassy-nrf/src/chips/nrf52805.rs @@ -8,7 +8,7 @@ pub const FLASH_SIZE: usize = 192 * 1024; pub const RESET_PIN: u32 = 21; -embassy_hal_common::peripherals! { +embassy_hal_internal::peripherals! { // RTC RTC0, RTC1, @@ -208,7 +208,7 @@ impl_ppi_channel!(PPI_CH31, 31 => static); impl_saadc_input!(P0_04, ANALOG_INPUT2); impl_saadc_input!(P0_05, ANALOG_INPUT3); -embassy_hal_common::interrupt_mod!( +embassy_hal_internal::interrupt_mod!( POWER_CLOCK, RADIO, UARTE0_UART0, diff --git a/embassy-nrf/src/chips/nrf52810.rs b/embassy-nrf/src/chips/nrf52810.rs index 5519e895..7416d391 100644 --- a/embassy-nrf/src/chips/nrf52810.rs +++ b/embassy-nrf/src/chips/nrf52810.rs @@ -8,7 +8,7 @@ pub const FLASH_SIZE: usize = 192 * 1024; pub const RESET_PIN: u32 = 21; -embassy_hal_common::peripherals! { +embassy_hal_internal::peripherals! { // RTC RTC0, RTC1, @@ -234,7 +234,7 @@ impl_saadc_input!(P0_29, ANALOG_INPUT5); impl_saadc_input!(P0_30, ANALOG_INPUT6); impl_saadc_input!(P0_31, ANALOG_INPUT7); -embassy_hal_common::interrupt_mod!( +embassy_hal_internal::interrupt_mod!( POWER_CLOCK, RADIO, UARTE0_UART0, diff --git a/embassy-nrf/src/chips/nrf52811.rs b/embassy-nrf/src/chips/nrf52811.rs index d5367c59..58801068 100644 --- a/embassy-nrf/src/chips/nrf52811.rs +++ b/embassy-nrf/src/chips/nrf52811.rs @@ -8,7 +8,7 @@ pub const FLASH_SIZE: usize = 192 * 1024; pub const RESET_PIN: u32 = 21; -embassy_hal_common::peripherals! { +embassy_hal_internal::peripherals! { // RTC RTC0, RTC1, @@ -236,7 +236,7 @@ impl_saadc_input!(P0_29, ANALOG_INPUT5); impl_saadc_input!(P0_30, ANALOG_INPUT6); impl_saadc_input!(P0_31, ANALOG_INPUT7); -embassy_hal_common::interrupt_mod!( +embassy_hal_internal::interrupt_mod!( POWER_CLOCK, RADIO, UARTE0_UART0, diff --git a/embassy-nrf/src/chips/nrf52820.rs b/embassy-nrf/src/chips/nrf52820.rs index 78517044..0ecddaf3 100644 --- a/embassy-nrf/src/chips/nrf52820.rs +++ b/embassy-nrf/src/chips/nrf52820.rs @@ -8,7 +8,7 @@ pub const FLASH_SIZE: usize = 256 * 1024; pub const RESET_PIN: u32 = 18; -embassy_hal_common::peripherals! { +embassy_hal_internal::peripherals! { // USB USBD, @@ -224,7 +224,7 @@ impl_ppi_channel!(PPI_CH29, 29 => static); impl_ppi_channel!(PPI_CH30, 30 => static); impl_ppi_channel!(PPI_CH31, 31 => static); -embassy_hal_common::interrupt_mod!( +embassy_hal_internal::interrupt_mod!( POWER_CLOCK, RADIO, UARTE0_UART0, diff --git a/embassy-nrf/src/chips/nrf52832.rs b/embassy-nrf/src/chips/nrf52832.rs index b77564a5..ae39628d 100644 --- a/embassy-nrf/src/chips/nrf52832.rs +++ b/embassy-nrf/src/chips/nrf52832.rs @@ -12,7 +12,7 @@ pub const FLASH_SIZE: usize = 512 * 1024; pub const RESET_PIN: u32 = 21; -embassy_hal_common::peripherals! { +embassy_hal_internal::peripherals! { // RTC RTC0, RTC1, @@ -263,7 +263,7 @@ impl_saadc_input!(P0_31, ANALOG_INPUT7); impl_i2s!(I2S, I2S, I2S); -embassy_hal_common::interrupt_mod!( +embassy_hal_internal::interrupt_mod!( POWER_CLOCK, RADIO, UARTE0_UART0, diff --git a/embassy-nrf/src/chips/nrf52833.rs b/embassy-nrf/src/chips/nrf52833.rs index bff7f4eb..b8830b33 100644 --- a/embassy-nrf/src/chips/nrf52833.rs +++ b/embassy-nrf/src/chips/nrf52833.rs @@ -8,7 +8,7 @@ pub const FLASH_SIZE: usize = 512 * 1024; pub const RESET_PIN: u32 = 18; -embassy_hal_common::peripherals! { +embassy_hal_internal::peripherals! { // USB USBD, @@ -306,7 +306,7 @@ impl_saadc_input!(P0_31, ANALOG_INPUT7); impl_i2s!(I2S, I2S, I2S); -embassy_hal_common::interrupt_mod!( +embassy_hal_internal::interrupt_mod!( POWER_CLOCK, RADIO, UARTE0_UART0, diff --git a/embassy-nrf/src/chips/nrf52840.rs b/embassy-nrf/src/chips/nrf52840.rs index 9b005082..a490cb07 100644 --- a/embassy-nrf/src/chips/nrf52840.rs +++ b/embassy-nrf/src/chips/nrf52840.rs @@ -8,7 +8,7 @@ pub const FLASH_SIZE: usize = 1024 * 1024; pub const RESET_PIN: u32 = 18; -embassy_hal_common::peripherals! { +embassy_hal_internal::peripherals! { // USB USBD, @@ -311,7 +311,7 @@ impl_saadc_input!(P0_31, ANALOG_INPUT7); impl_i2s!(I2S, I2S, I2S); -embassy_hal_common::interrupt_mod!( +embassy_hal_internal::interrupt_mod!( POWER_CLOCK, RADIO, UARTE0_UART0, diff --git a/embassy-nrf/src/chips/nrf5340_app.rs b/embassy-nrf/src/chips/nrf5340_app.rs index 410ae921..afc2c4a7 100644 --- a/embassy-nrf/src/chips/nrf5340_app.rs +++ b/embassy-nrf/src/chips/nrf5340_app.rs @@ -218,7 +218,7 @@ pub const FORCE_COPY_BUFFER_SIZE: usize = 1024; pub const FLASH_SIZE: usize = 1024 * 1024; -embassy_hal_common::peripherals! { +embassy_hal_internal::peripherals! { // USB USBD, @@ -506,7 +506,7 @@ impl_saadc_input!(P0_18, ANALOG_INPUT5); impl_saadc_input!(P0_19, ANALOG_INPUT6); impl_saadc_input!(P0_20, ANALOG_INPUT7); -embassy_hal_common::interrupt_mod!( +embassy_hal_internal::interrupt_mod!( FPU, CACHE, SPU, diff --git a/embassy-nrf/src/chips/nrf5340_net.rs b/embassy-nrf/src/chips/nrf5340_net.rs index 6ac78308..dee666a6 100644 --- a/embassy-nrf/src/chips/nrf5340_net.rs +++ b/embassy-nrf/src/chips/nrf5340_net.rs @@ -109,7 +109,7 @@ pub const FORCE_COPY_BUFFER_SIZE: usize = 1024; pub const FLASH_SIZE: usize = 256 * 1024; -embassy_hal_common::peripherals! { +embassy_hal_internal::peripherals! { // RTC RTC0, RTC1, @@ -342,7 +342,7 @@ impl_ppi_channel!(PPI_CH29, 29 => configurable); impl_ppi_channel!(PPI_CH30, 30 => configurable); impl_ppi_channel!(PPI_CH31, 31 => configurable); -embassy_hal_common::interrupt_mod!( +embassy_hal_internal::interrupt_mod!( CLOCK_POWER, RADIO, RNG, diff --git a/embassy-nrf/src/chips/nrf9160.rs b/embassy-nrf/src/chips/nrf9160.rs index 67ea032f..495285ba 100644 --- a/embassy-nrf/src/chips/nrf9160.rs +++ b/embassy-nrf/src/chips/nrf9160.rs @@ -169,7 +169,7 @@ pub const FORCE_COPY_BUFFER_SIZE: usize = 1024; pub const FLASH_SIZE: usize = 1024 * 1024; -embassy_hal_common::peripherals! { +embassy_hal_internal::peripherals! { // RTC RTC0, RTC1, @@ -368,7 +368,7 @@ impl_saadc_input!(P0_18, ANALOG_INPUT5); impl_saadc_input!(P0_19, ANALOG_INPUT6); impl_saadc_input!(P0_20, ANALOG_INPUT7); -embassy_hal_common::interrupt_mod!( +embassy_hal_internal::interrupt_mod!( SPU, CLOCK_POWER, UARTE0_SPIM0_SPIS0_TWIM0_TWIS0, diff --git a/embassy-nrf/src/gpio.rs b/embassy-nrf/src/gpio.rs index 895ab934..ea2b7609 100644 --- a/embassy-nrf/src/gpio.rs +++ b/embassy-nrf/src/gpio.rs @@ -5,7 +5,7 @@ use core::convert::Infallible; use core::hint::unreachable_unchecked; use cfg_if::cfg_if; -use embassy_hal_common::{impl_peripheral, into_ref, PeripheralRef}; +use embassy_hal_internal::{impl_peripheral, into_ref, PeripheralRef}; use self::sealed::Pin as _; use crate::pac::p0 as gpio; diff --git a/embassy-nrf/src/gpiote.rs b/embassy-nrf/src/gpiote.rs index 6550f2ab..7488bc08 100644 --- a/embassy-nrf/src/gpiote.rs +++ b/embassy-nrf/src/gpiote.rs @@ -4,7 +4,7 @@ use core::convert::Infallible; use core::future::{poll_fn, Future}; use core::task::{Context, Poll}; -use embassy_hal_common::{impl_peripheral, into_ref, Peripheral, PeripheralRef}; +use embassy_hal_internal::{impl_peripheral, into_ref, Peripheral, PeripheralRef}; use embassy_sync::waitqueue::AtomicWaker; use crate::gpio::sealed::Pin as _; diff --git a/embassy-nrf/src/i2s.rs b/embassy-nrf/src/i2s.rs index fea38c4c..907acdf4 100644 --- a/embassy-nrf/src/i2s.rs +++ b/embassy-nrf/src/i2s.rs @@ -9,8 +9,8 @@ use core::ops::{Deref, DerefMut}; use core::sync::atomic::{compiler_fence, Ordering}; use core::task::Poll; -use embassy_hal_common::drop::OnDrop; -use embassy_hal_common::{into_ref, PeripheralRef}; +use embassy_hal_internal::drop::OnDrop; +use embassy_hal_internal::{into_ref, PeripheralRef}; use crate::gpio::{AnyPin, Pin as GpioPin}; use crate::interrupt::typelevel::Interrupt; diff --git a/embassy-nrf/src/lib.rs b/embassy-nrf/src/lib.rs index d23759f9..355a0049 100644 --- a/embassy-nrf/src/lib.rs +++ b/embassy-nrf/src/lib.rs @@ -98,7 +98,7 @@ mod chip; /// This defines the right interrupt handlers, and creates a unit struct (like `struct Irqs;`) /// and implements the right [`Binding`]s for it. You can pass this struct to drivers to /// prove at compile-time that the right interrupts have been bound. -// developer note: this macro can't be in `embassy-hal-common` due to the use of `$crate`. +// developer note: this macro can't be in `embassy-hal-internal` due to the use of `$crate`. #[macro_export] macro_rules! bind_interrupts { ($vis:vis struct $name:ident { $($irq:ident => $($handler:ty),*;)* }) => { @@ -127,7 +127,7 @@ pub use chip::pac; #[cfg(not(feature = "unstable-pac"))] pub(crate) use chip::pac; pub use chip::{peripherals, Peripherals, EASY_DMA_SIZE}; -pub use embassy_hal_common::{into_ref, Peripheral, PeripheralRef}; +pub use embassy_hal_internal::{into_ref, Peripheral, PeripheralRef}; pub use crate::chip::interrupt; pub use crate::pac::NVIC_PRIO_BITS; diff --git a/embassy-nrf/src/nvmc.rs b/embassy-nrf/src/nvmc.rs index 91a5a272..de840b88 100644 --- a/embassy-nrf/src/nvmc.rs +++ b/embassy-nrf/src/nvmc.rs @@ -2,7 +2,7 @@ use core::{ptr, slice}; -use embassy_hal_common::{into_ref, PeripheralRef}; +use embassy_hal_internal::{into_ref, PeripheralRef}; use embedded_storage::nor_flash::{ ErrorType, MultiwriteNorFlash, NorFlash, NorFlashError, NorFlashErrorKind, ReadNorFlash, }; diff --git a/embassy-nrf/src/pdm.rs b/embassy-nrf/src/pdm.rs index 217884d1..01f41e9f 100644 --- a/embassy-nrf/src/pdm.rs +++ b/embassy-nrf/src/pdm.rs @@ -6,8 +6,8 @@ use core::marker::PhantomData; use core::sync::atomic::{compiler_fence, Ordering}; use core::task::Poll; -use embassy_hal_common::drop::OnDrop; -use embassy_hal_common::{into_ref, PeripheralRef}; +use embassy_hal_internal::drop::OnDrop; +use embassy_hal_internal::{into_ref, PeripheralRef}; use fixed::types::I7F1; use futures::future::poll_fn; diff --git a/embassy-nrf/src/ppi/dppi.rs b/embassy-nrf/src/ppi/dppi.rs index 40ccb2f0..0bc7f821 100644 --- a/embassy-nrf/src/ppi/dppi.rs +++ b/embassy-nrf/src/ppi/dppi.rs @@ -1,4 +1,4 @@ -use embassy_hal_common::into_ref; +use embassy_hal_internal::into_ref; use super::{Channel, ConfigurableChannel, Event, Ppi, Task}; use crate::{pac, Peripheral}; diff --git a/embassy-nrf/src/ppi/mod.rs b/embassy-nrf/src/ppi/mod.rs index ff6593bd..5b4a6438 100644 --- a/embassy-nrf/src/ppi/mod.rs +++ b/embassy-nrf/src/ppi/mod.rs @@ -18,7 +18,7 @@ use core::marker::PhantomData; use core::ptr::NonNull; -use embassy_hal_common::{impl_peripheral, into_ref, PeripheralRef}; +use embassy_hal_internal::{impl_peripheral, into_ref, PeripheralRef}; use crate::{peripherals, Peripheral}; diff --git a/embassy-nrf/src/ppi/ppi.rs b/embassy-nrf/src/ppi/ppi.rs index 1fe89862..3e9e9fc8 100644 --- a/embassy-nrf/src/ppi/ppi.rs +++ b/embassy-nrf/src/ppi/ppi.rs @@ -1,4 +1,4 @@ -use embassy_hal_common::into_ref; +use embassy_hal_internal::into_ref; use super::{Channel, ConfigurableChannel, Event, Ppi, StaticChannel, Task}; use crate::{pac, Peripheral}; diff --git a/embassy-nrf/src/pwm.rs b/embassy-nrf/src/pwm.rs index c8c81fa0..2f039763 100644 --- a/embassy-nrf/src/pwm.rs +++ b/embassy-nrf/src/pwm.rs @@ -4,7 +4,7 @@ use core::sync::atomic::{compiler_fence, Ordering}; -use embassy_hal_common::{into_ref, PeripheralRef}; +use embassy_hal_internal::{into_ref, PeripheralRef}; use crate::gpio::sealed::Pin as _; use crate::gpio::{AnyPin, Pin as GpioPin, PselBits}; diff --git a/embassy-nrf/src/qdec.rs b/embassy-nrf/src/qdec.rs index 8bac87d3..2aa50a2b 100644 --- a/embassy-nrf/src/qdec.rs +++ b/embassy-nrf/src/qdec.rs @@ -6,7 +6,7 @@ use core::future::poll_fn; use core::marker::PhantomData; use core::task::Poll; -use embassy_hal_common::{into_ref, PeripheralRef}; +use embassy_hal_internal::{into_ref, PeripheralRef}; use crate::gpio::sealed::Pin as _; use crate::gpio::{AnyPin, Pin as GpioPin}; diff --git a/embassy-nrf/src/qspi.rs b/embassy-nrf/src/qspi.rs index baefc796..36ee33f6 100644 --- a/embassy-nrf/src/qspi.rs +++ b/embassy-nrf/src/qspi.rs @@ -7,8 +7,8 @@ use core::marker::PhantomData; use core::ptr; use core::task::Poll; -use embassy_hal_common::drop::OnDrop; -use embassy_hal_common::{into_ref, PeripheralRef}; +use embassy_hal_internal::drop::OnDrop; +use embassy_hal_internal::{into_ref, PeripheralRef}; use embedded_storage::nor_flash::{ErrorType, NorFlash, NorFlashError, NorFlashErrorKind, ReadNorFlash}; use crate::gpio::{self, Pin as GpioPin}; diff --git a/embassy-nrf/src/rng.rs b/embassy-nrf/src/rng.rs index 923b8b46..e2803f0d 100644 --- a/embassy-nrf/src/rng.rs +++ b/embassy-nrf/src/rng.rs @@ -8,8 +8,8 @@ use core::ptr; use core::sync::atomic::{AtomicPtr, Ordering}; use core::task::Poll; -use embassy_hal_common::drop::OnDrop; -use embassy_hal_common::{into_ref, PeripheralRef}; +use embassy_hal_internal::drop::OnDrop; +use embassy_hal_internal::{into_ref, PeripheralRef}; use embassy_sync::waitqueue::AtomicWaker; use crate::interrupt::typelevel::Interrupt; diff --git a/embassy-nrf/src/saadc.rs b/embassy-nrf/src/saadc.rs index 23292924..662b0561 100644 --- a/embassy-nrf/src/saadc.rs +++ b/embassy-nrf/src/saadc.rs @@ -6,8 +6,8 @@ use core::future::poll_fn; use core::sync::atomic::{compiler_fence, Ordering}; use core::task::Poll; -use embassy_hal_common::drop::OnDrop; -use embassy_hal_common::{impl_peripheral, into_ref, PeripheralRef}; +use embassy_hal_internal::drop::OnDrop; +use embassy_hal_internal::{impl_peripheral, into_ref, PeripheralRef}; use embassy_sync::waitqueue::AtomicWaker; use pac::{saadc, SAADC}; use saadc::ch::config::{GAIN_A, REFSEL_A, RESP_A, TACQ_A}; diff --git a/embassy-nrf/src/spim.rs b/embassy-nrf/src/spim.rs index b7dc332e..4673a017 100644 --- a/embassy-nrf/src/spim.rs +++ b/embassy-nrf/src/spim.rs @@ -8,7 +8,7 @@ use core::sync::atomic::{compiler_fence, Ordering}; use core::task::Poll; use embassy_embedded_hal::SetConfig; -use embassy_hal_common::{into_ref, PeripheralRef}; +use embassy_hal_internal::{into_ref, PeripheralRef}; pub use embedded_hal_02::spi::{Mode, Phase, Polarity, MODE_0, MODE_1, MODE_2, MODE_3}; pub use pac::spim0::frequency::FREQUENCY_A as Frequency; diff --git a/embassy-nrf/src/spis.rs b/embassy-nrf/src/spis.rs index aa438415..21282512 100644 --- a/embassy-nrf/src/spis.rs +++ b/embassy-nrf/src/spis.rs @@ -7,7 +7,7 @@ use core::sync::atomic::{compiler_fence, Ordering}; use core::task::Poll; use embassy_embedded_hal::SetConfig; -use embassy_hal_common::{into_ref, PeripheralRef}; +use embassy_hal_internal::{into_ref, PeripheralRef}; pub use embedded_hal_02::spi::{Mode, Phase, Polarity, MODE_0, MODE_1, MODE_2, MODE_3}; use crate::chip::FORCE_COPY_BUFFER_SIZE; diff --git a/embassy-nrf/src/temp.rs b/embassy-nrf/src/temp.rs index 491e92c0..cec46d8d 100644 --- a/embassy-nrf/src/temp.rs +++ b/embassy-nrf/src/temp.rs @@ -3,8 +3,8 @@ use core::future::poll_fn; use core::task::Poll; -use embassy_hal_common::drop::OnDrop; -use embassy_hal_common::{into_ref, PeripheralRef}; +use embassy_hal_internal::drop::OnDrop; +use embassy_hal_internal::{into_ref, PeripheralRef}; use embassy_sync::waitqueue::AtomicWaker; use fixed::types::I30F2; diff --git a/embassy-nrf/src/timer.rs b/embassy-nrf/src/timer.rs index 04748238..3dbfdac4 100644 --- a/embassy-nrf/src/timer.rs +++ b/embassy-nrf/src/timer.rs @@ -6,7 +6,7 @@ #![macro_use] -use embassy_hal_common::{into_ref, PeripheralRef}; +use embassy_hal_internal::{into_ref, PeripheralRef}; use crate::ppi::{Event, Task}; use crate::{pac, Peripheral}; diff --git a/embassy-nrf/src/twim.rs b/embassy-nrf/src/twim.rs index 2ad0d19b..fdea480e 100644 --- a/embassy-nrf/src/twim.rs +++ b/embassy-nrf/src/twim.rs @@ -9,7 +9,7 @@ use core::sync::atomic::Ordering::SeqCst; use core::task::Poll; use embassy_embedded_hal::SetConfig; -use embassy_hal_common::{into_ref, PeripheralRef}; +use embassy_hal_internal::{into_ref, PeripheralRef}; use embassy_sync::waitqueue::AtomicWaker; #[cfg(feature = "time")] use embassy_time::{Duration, Instant}; diff --git a/embassy-nrf/src/twis.rs b/embassy-nrf/src/twis.rs index a115d561..c6c02055 100644 --- a/embassy-nrf/src/twis.rs +++ b/embassy-nrf/src/twis.rs @@ -8,7 +8,7 @@ use core::sync::atomic::compiler_fence; use core::sync::atomic::Ordering::SeqCst; use core::task::Poll; -use embassy_hal_common::{into_ref, PeripheralRef}; +use embassy_hal_internal::{into_ref, PeripheralRef}; use embassy_sync::waitqueue::AtomicWaker; #[cfg(feature = "time")] use embassy_time::{Duration, Instant}; diff --git a/embassy-nrf/src/uarte.rs b/embassy-nrf/src/uarte.rs index 48d57fea..e79df356 100644 --- a/embassy-nrf/src/uarte.rs +++ b/embassy-nrf/src/uarte.rs @@ -18,8 +18,8 @@ use core::marker::PhantomData; use core::sync::atomic::{compiler_fence, Ordering}; use core::task::Poll; -use embassy_hal_common::drop::OnDrop; -use embassy_hal_common::{into_ref, PeripheralRef}; +use embassy_hal_internal::drop::OnDrop; +use embassy_hal_internal::{into_ref, PeripheralRef}; use pac::uarte0::RegisterBlock; // Re-export SVD variants to allow user to directly set values. pub use pac::uarte0::{baudrate::BAUDRATE_A as Baudrate, config::PARITY_A as Parity}; diff --git a/embassy-nrf/src/usb/mod.rs b/embassy-nrf/src/usb/mod.rs index 76cf40ac..e26b49db 100644 --- a/embassy-nrf/src/usb/mod.rs +++ b/embassy-nrf/src/usb/mod.rs @@ -11,7 +11,7 @@ use core::sync::atomic::{compiler_fence, AtomicU32, Ordering}; use core::task::Poll; use cortex_m::peripheral::NVIC; -use embassy_hal_common::{into_ref, PeripheralRef}; +use embassy_hal_internal::{into_ref, PeripheralRef}; use embassy_sync::waitqueue::AtomicWaker; use embassy_usb_driver as driver; use embassy_usb_driver::{Direction, EndpointAddress, EndpointError, EndpointInfo, EndpointType, Event, Unsupported}; diff --git a/embassy-rp/Cargo.toml b/embassy-rp/Cargo.toml index 8f3ed885..b53c7a01 100644 --- a/embassy-rp/Cargo.toml +++ b/embassy-rp/Cargo.toml @@ -16,7 +16,7 @@ flavors = [ default = [ "rt" ] rt = [ "rp-pac/rt" ] -defmt = ["dep:defmt", "embassy-usb-driver?/defmt", "embassy-hal-common/defmt"] +defmt = ["dep:defmt", "embassy-usb-driver?/defmt", "embassy-hal-internal/defmt"] # critical section that is safe for multicore use critical-section-impl = ["critical-section/restore-state-u8"] @@ -58,7 +58,7 @@ unstable-traits = ["embedded-hal-1", "embedded-hal-nb"] embassy-sync = { version = "0.2.0", path = "../embassy-sync" } embassy-time = { version = "0.1.2", path = "../embassy-time", features = [ "tick-hz-1_000_000" ] } embassy-futures = { version = "0.1.0", path = "../embassy-futures" } -embassy-hal-common = {version = "0.1.0", path = "../embassy-hal-common", features = ["cortex-m", "prio-bits-2"] } +embassy-hal-internal = {version = "0.1.0", path = "../embassy-hal-internal", features = ["cortex-m", "prio-bits-2"] } embassy-embedded-hal = {version = "0.1.0", path = "../embassy-embedded-hal" } embassy-usb-driver = {version = "0.1.0", path = "../embassy-usb-driver", optional = true } atomic-polyfill = "1.0.1" diff --git a/embassy-rp/src/adc.rs b/embassy-rp/src/adc.rs index 95780c06..4fba3116 100644 --- a/embassy-rp/src/adc.rs +++ b/embassy-rp/src/adc.rs @@ -3,7 +3,7 @@ use core::marker::PhantomData; use core::sync::atomic::{compiler_fence, Ordering}; use core::task::Poll; -use embassy_hal_common::{into_ref, PeripheralRef}; +use embassy_hal_internal::{into_ref, PeripheralRef}; use embassy_sync::waitqueue::AtomicWaker; use crate::gpio::sealed::Pin as GpioPin; diff --git a/embassy-rp/src/clocks.rs b/embassy-rp/src/clocks.rs index acb21dce..976d06de 100644 --- a/embassy-rp/src/clocks.rs +++ b/embassy-rp/src/clocks.rs @@ -1,7 +1,7 @@ use core::marker::PhantomData; use core::sync::atomic::{AtomicU16, AtomicU32, Ordering}; -use embassy_hal_common::{into_ref, PeripheralRef}; +use embassy_hal_internal::{into_ref, PeripheralRef}; use pac::clocks::vals::*; use crate::gpio::sealed::Pin; diff --git a/embassy-rp/src/dma.rs b/embassy-rp/src/dma.rs index 1a458778..c8f74180 100644 --- a/embassy-rp/src/dma.rs +++ b/embassy-rp/src/dma.rs @@ -4,7 +4,7 @@ use core::pin::Pin; use core::sync::atomic::{compiler_fence, Ordering}; use core::task::{Context, Poll}; -use embassy_hal_common::{impl_peripheral, into_ref, Peripheral, PeripheralRef}; +use embassy_hal_internal::{impl_peripheral, into_ref, Peripheral, PeripheralRef}; use embassy_sync::waitqueue::AtomicWaker; use pac::dma::vals::DataSize; diff --git a/embassy-rp/src/flash.rs b/embassy-rp/src/flash.rs index 96d2d454..0ed6808e 100644 --- a/embassy-rp/src/flash.rs +++ b/embassy-rp/src/flash.rs @@ -1,6 +1,6 @@ use core::marker::PhantomData; -use embassy_hal_common::Peripheral; +use embassy_hal_internal::Peripheral; use embedded_storage::nor_flash::{ check_erase, check_read, check_write, ErrorType, MultiwriteNorFlash, NorFlash, NorFlashError, NorFlashErrorKind, ReadNorFlash, diff --git a/embassy-rp/src/gpio.rs b/embassy-rp/src/gpio.rs index a3d330cd..2807eb67 100644 --- a/embassy-rp/src/gpio.rs +++ b/embassy-rp/src/gpio.rs @@ -3,7 +3,7 @@ use core::future::Future; use core::pin::Pin as FuturePin; use core::task::{Context, Poll}; -use embassy_hal_common::{impl_peripheral, into_ref, PeripheralRef}; +use embassy_hal_internal::{impl_peripheral, into_ref, PeripheralRef}; use embassy_sync::waitqueue::AtomicWaker; use crate::interrupt::InterruptExt; diff --git a/embassy-rp/src/i2c.rs b/embassy-rp/src/i2c.rs index 9b85b234..536ad747 100644 --- a/embassy-rp/src/i2c.rs +++ b/embassy-rp/src/i2c.rs @@ -2,7 +2,7 @@ use core::future; use core::marker::PhantomData; use core::task::Poll; -use embassy_hal_common::{into_ref, PeripheralRef}; +use embassy_hal_internal::{into_ref, PeripheralRef}; use embassy_sync::waitqueue::AtomicWaker; use pac::i2c; diff --git a/embassy-rp/src/lib.rs b/embassy-rp/src/lib.rs index 50f028d4..ebec9fec 100644 --- a/embassy-rp/src/lib.rs +++ b/embassy-rp/src/lib.rs @@ -36,7 +36,7 @@ pub mod pio_instr_util; pub mod relocate; // Reexports -pub use embassy_hal_common::{into_ref, Peripheral, PeripheralRef}; +pub use embassy_hal_internal::{into_ref, Peripheral, PeripheralRef}; #[cfg(feature = "unstable-pac")] pub use rp_pac as pac; #[cfg(not(feature = "unstable-pac"))] @@ -45,7 +45,7 @@ pub(crate) use rp_pac as pac; #[cfg(feature = "rt")] pub use crate::pac::NVIC_PRIO_BITS; -embassy_hal_common::interrupt_mod!( +embassy_hal_internal::interrupt_mod!( TIMER_IRQ_0, TIMER_IRQ_1, TIMER_IRQ_2, @@ -85,7 +85,7 @@ embassy_hal_common::interrupt_mod!( /// This defines the right interrupt handlers, and creates a unit struct (like `struct Irqs;`) /// and implements the right [`Binding`]s for it. You can pass this struct to drivers to /// prove at compile-time that the right interrupts have been bound. -// developer note: this macro can't be in `embassy-hal-common` due to the use of `$crate`. +// developer note: this macro can't be in `embassy-hal-internal` due to the use of `$crate`. #[macro_export] macro_rules! bind_interrupts { ($vis:vis struct $name:ident { $($irq:ident => $($handler:ty),*;)* }) => { @@ -107,7 +107,7 @@ macro_rules! bind_interrupts { }; } -embassy_hal_common::peripherals! { +embassy_hal_internal::peripherals! { PIN_0, PIN_1, PIN_2, diff --git a/embassy-rp/src/pio.rs b/embassy-rp/src/pio.rs index 72a2f44e..464988b2 100644 --- a/embassy-rp/src/pio.rs +++ b/embassy-rp/src/pio.rs @@ -5,7 +5,7 @@ use core::sync::atomic::{compiler_fence, Ordering}; use core::task::{Context, Poll}; use atomic_polyfill::{AtomicU32, AtomicU8}; -use embassy_hal_common::{into_ref, Peripheral, PeripheralRef}; +use embassy_hal_internal::{into_ref, Peripheral, PeripheralRef}; use embassy_sync::waitqueue::AtomicWaker; use fixed::types::extra::U8; use fixed::FixedU32; diff --git a/embassy-rp/src/pwm.rs b/embassy-rp/src/pwm.rs index 20bb8844..c0ddb2a9 100644 --- a/embassy-rp/src/pwm.rs +++ b/embassy-rp/src/pwm.rs @@ -1,6 +1,6 @@ //! Pulse Width Modulation (PWM) -use embassy_hal_common::{into_ref, Peripheral, PeripheralRef}; +use embassy_hal_internal::{into_ref, Peripheral, PeripheralRef}; use fixed::traits::ToFixed; use fixed::FixedU16; use pac::pwm::regs::{ChDiv, Intr}; diff --git a/embassy-rp/src/rtc/mod.rs b/embassy-rp/src/rtc/mod.rs index 1b33fdf8..60ca8627 100644 --- a/embassy-rp/src/rtc/mod.rs +++ b/embassy-rp/src/rtc/mod.rs @@ -1,6 +1,6 @@ mod filter; -use embassy_hal_common::{into_ref, Peripheral, PeripheralRef}; +use embassy_hal_internal::{into_ref, Peripheral, PeripheralRef}; pub use self::filter::DateTimeFilter; diff --git a/embassy-rp/src/spi.rs b/embassy-rp/src/spi.rs index af101cf4..544b542e 100644 --- a/embassy-rp/src/spi.rs +++ b/embassy-rp/src/spi.rs @@ -3,7 +3,7 @@ use core::marker::PhantomData; use embassy_embedded_hal::SetConfig; use embassy_futures::join::join; -use embassy_hal_common::{into_ref, PeripheralRef}; +use embassy_hal_internal::{into_ref, PeripheralRef}; pub use embedded_hal_02::spi::{Phase, Polarity}; use crate::dma::{AnyChannel, Channel}; diff --git a/embassy-rp/src/uart/buffered.rs b/embassy-rp/src/uart/buffered.rs index 30eeb547..9d96db12 100644 --- a/embassy-rp/src/uart/buffered.rs +++ b/embassy-rp/src/uart/buffered.rs @@ -3,7 +3,7 @@ use core::slice; use core::task::Poll; use atomic_polyfill::{AtomicU8, Ordering}; -use embassy_hal_common::atomic_ring_buffer::RingBuffer; +use embassy_hal_internal::atomic_ring_buffer::RingBuffer; use embassy_sync::waitqueue::AtomicWaker; use embassy_time::{Duration, Timer}; diff --git a/embassy-rp/src/uart/mod.rs b/embassy-rp/src/uart/mod.rs index 7b94bce5..69c6ac2f 100644 --- a/embassy-rp/src/uart/mod.rs +++ b/embassy-rp/src/uart/mod.rs @@ -4,7 +4,7 @@ use core::task::Poll; use atomic_polyfill::{AtomicU16, Ordering}; use embassy_futures::select::{select, Either}; -use embassy_hal_common::{into_ref, PeripheralRef}; +use embassy_hal_internal::{into_ref, PeripheralRef}; use embassy_sync::waitqueue::AtomicWaker; use embassy_time::{Duration, Timer}; use pac::uart::regs::Uartris; diff --git a/embassy-stm32-wpan/Cargo.toml b/embassy-stm32-wpan/Cargo.toml index 2c6089c5..96c47484 100644 --- a/embassy-stm32-wpan/Cargo.toml +++ b/embassy-stm32-wpan/Cargo.toml @@ -15,7 +15,7 @@ embassy-stm32 = { version = "0.1.0", path = "../embassy-stm32" } embassy-sync = { version = "0.2.0", path = "../embassy-sync" } embassy-time = { version = "0.1.2", path = "../embassy-time", optional = true } embassy-futures = { version = "0.1.0", path = "../embassy-futures" } -embassy-hal-common = { version = "0.1.0", path = "../embassy-hal-common" } +embassy-hal-internal = { version = "0.1.0", path = "../embassy-hal-internal" } embassy-embedded-hal = { version = "0.1.0", path = "../embassy-embedded-hal" } embassy-net-driver = { version = "0.1.0", path = "../embassy-net-driver", optional=true } @@ -31,7 +31,7 @@ futures = { version = "0.3.17", default-features = false, features = ["async-awa bitflags = { version = "2.3.3", optional = true } [features] -defmt = ["dep:defmt", "embassy-sync/defmt", "embassy-embedded-hal/defmt", "embassy-hal-common/defmt", "stm32wb-hci?/defmt"] +defmt = ["dep:defmt", "embassy-sync/defmt", "embassy-embedded-hal/defmt", "embassy-hal-internal/defmt", "stm32wb-hci?/defmt"] ble = ["dep:stm32wb-hci"] mac = ["dep:bitflags", "dep:embassy-net-driver" ] diff --git a/embassy-stm32-wpan/src/lib.rs b/embassy-stm32-wpan/src/lib.rs index 6836d7a8..5ecce2cc 100644 --- a/embassy-stm32-wpan/src/lib.rs +++ b/embassy-stm32-wpan/src/lib.rs @@ -8,7 +8,7 @@ pub mod fmt; use core::mem::MaybeUninit; use core::sync::atomic::{compiler_fence, Ordering}; -use embassy_hal_common::{into_ref, Peripheral, PeripheralRef}; +use embassy_hal_internal::{into_ref, Peripheral, PeripheralRef}; use embassy_stm32::interrupt; use embassy_stm32::ipcc::{Config, Ipcc, ReceiveInterruptHandler, TransmitInterruptHandler}; use embassy_stm32::peripherals::IPCC; diff --git a/embassy-stm32/Cargo.toml b/embassy-stm32/Cargo.toml index 0fb6fdb5..6f34c741 100644 --- a/embassy-stm32/Cargo.toml +++ b/embassy-stm32/Cargo.toml @@ -34,7 +34,7 @@ flavors = [ embassy-sync = { version = "0.2.0", path = "../embassy-sync" } embassy-time = { version = "0.1.2", path = "../embassy-time", optional = true } embassy-futures = { version = "0.1.0", path = "../embassy-futures" } -embassy-hal-common = {version = "0.1.0", path = "../embassy-hal-common", features = ["cortex-m", "prio-bits-4"] } +embassy-hal-internal = {version = "0.1.0", path = "../embassy-hal-internal", features = ["cortex-m", "prio-bits-4"] } embassy-embedded-hal = {version = "0.1.0", path = "../embassy-embedded-hal" } embassy-net-driver = { version = "0.1.0", path = "../embassy-net-driver" } embassy-usb-driver = {version = "0.1.0", path = "../embassy-usb-driver", optional = true } @@ -80,7 +80,7 @@ stm32-metapac = { version = "13", default-features = false, features = ["metadat default = ["rt"] rt = ["stm32-metapac/rt"] -defmt = ["dep:defmt", "bxcan/unstable-defmt", "embassy-sync/defmt", "embassy-embedded-hal/defmt", "embassy-hal-common/defmt", "embedded-io?/defmt", "embassy-usb-driver?/defmt", "embassy-net-driver/defmt"] +defmt = ["dep:defmt", "bxcan/unstable-defmt", "embassy-sync/defmt", "embassy-embedded-hal/defmt", "embassy-hal-internal/defmt", "embedded-io?/defmt", "embassy-usb-driver?/defmt", "embassy-net-driver/defmt"] memory-x = ["stm32-metapac/memory-x"] exti = [] diff --git a/embassy-stm32/build.rs b/embassy-stm32/build.rs index 995ad144..0e9606ec 100644 --- a/embassy-stm32/build.rs +++ b/embassy-stm32/build.rs @@ -138,7 +138,7 @@ fn main() { let singleton_tokens: Vec<_> = singletons.iter().map(|s| format_ident!("{}", s)).collect(); g.extend(quote! { - embassy_hal_common::peripherals_definition!(#(#singleton_tokens),*); + embassy_hal_internal::peripherals_definition!(#(#singleton_tokens),*); }); let singleton_tokens: Vec<_> = singletons @@ -148,7 +148,7 @@ fn main() { .collect(); g.extend(quote! { - embassy_hal_common::peripherals_struct!(#(#singleton_tokens),*); + embassy_hal_internal::peripherals_struct!(#(#singleton_tokens),*); }); // ======== @@ -160,7 +160,7 @@ fn main() { } g.extend(quote! { - embassy_hal_common::interrupt_mod!( + embassy_hal_internal::interrupt_mod!( #( #irqs, )* @@ -211,7 +211,7 @@ fn main() { let region_type = format_ident!("{}", get_flash_region_type_name(region.name)); flash_regions.extend(quote! { #[cfg(flash)] - pub struct #region_type<'d, MODE = crate::flash::Async>(pub &'static crate::flash::FlashRegion, pub(crate) embassy_hal_common::PeripheralRef<'d, crate::peripherals::FLASH>, pub(crate) core::marker::PhantomData); + pub struct #region_type<'d, MODE = crate::flash::Async>(pub &'static crate::flash::FlashRegion, pub(crate) embassy_hal_internal::PeripheralRef<'d, crate::peripherals::FLASH>, pub(crate) core::marker::PhantomData); }); } @@ -243,7 +243,7 @@ fn main() { #[cfg(flash)] impl<'d, MODE> FlashLayout<'d, MODE> { - pub(crate) fn new(p: embassy_hal_common::PeripheralRef<'d, crate::peripherals::FLASH>) -> Self { + pub(crate) fn new(p: embassy_hal_internal::PeripheralRef<'d, crate::peripherals::FLASH>) -> Self { Self { #(#inits),*, _mode: core::marker::PhantomData, diff --git a/embassy-stm32/src/adc/f1.rs b/embassy-stm32/src/adc/f1.rs index 2322204d..e577ec28 100644 --- a/embassy-stm32/src/adc/f1.rs +++ b/embassy-stm32/src/adc/f1.rs @@ -1,4 +1,4 @@ -use embassy_hal_common::into_ref; +use embassy_hal_internal::into_ref; use embedded_hal_02::blocking::delay::DelayUs; use crate::adc::{Adc, AdcPin, Instance, SampleTime}; diff --git a/embassy-stm32/src/adc/v1.rs b/embassy-stm32/src/adc/v1.rs index d9af0c55..e8245884 100644 --- a/embassy-stm32/src/adc/v1.rs +++ b/embassy-stm32/src/adc/v1.rs @@ -1,4 +1,4 @@ -use embassy_hal_common::into_ref; +use embassy_hal_internal::into_ref; use embedded_hal_02::blocking::delay::DelayUs; use crate::adc::{Adc, AdcPin, Instance, InternalChannel, Resolution, SampleTime}; diff --git a/embassy-stm32/src/adc/v2.rs b/embassy-stm32/src/adc/v2.rs index 091c1d44..9a7acea5 100644 --- a/embassy-stm32/src/adc/v2.rs +++ b/embassy-stm32/src/adc/v2.rs @@ -1,4 +1,4 @@ -use embassy_hal_common::into_ref; +use embassy_hal_internal::into_ref; use embedded_hal_02::blocking::delay::DelayUs; use super::InternalChannel; diff --git a/embassy-stm32/src/adc/v3.rs b/embassy-stm32/src/adc/v3.rs index 3a6e58cf..821cc7f6 100644 --- a/embassy-stm32/src/adc/v3.rs +++ b/embassy-stm32/src/adc/v3.rs @@ -1,4 +1,4 @@ -use embassy_hal_common::into_ref; +use embassy_hal_internal::into_ref; use embedded_hal_02::blocking::delay::DelayUs; use crate::adc::{Adc, AdcPin, Instance, Resolution, SampleTime}; diff --git a/embassy-stm32/src/adc/v4.rs b/embassy-stm32/src/adc/v4.rs index c51c6840..64d0f0c7 100644 --- a/embassy-stm32/src/adc/v4.rs +++ b/embassy-stm32/src/adc/v4.rs @@ -226,7 +226,7 @@ impl Prescaler { impl<'d, T: Instance> Adc<'d, T> { pub fn new(adc: impl Peripheral

+ 'd, delay: &mut impl DelayUs) -> Self { - embassy_hal_common::into_ref!(adc); + embassy_hal_internal::into_ref!(adc); T::enable(); T::reset(); diff --git a/embassy-stm32/src/can/bxcan.rs b/embassy-stm32/src/can/bxcan.rs index 8b8244d4..55d34201 100644 --- a/embassy-stm32/src/can/bxcan.rs +++ b/embassy-stm32/src/can/bxcan.rs @@ -6,7 +6,7 @@ use core::task::Poll; pub use bxcan; use bxcan::{Data, ExtendedId, Frame, Id, StandardId}; -use embassy_hal_common::{into_ref, PeripheralRef}; +use embassy_hal_internal::{into_ref, PeripheralRef}; use futures::FutureExt; use crate::gpio::sealed::AFType; diff --git a/embassy-stm32/src/can/fdcan.rs b/embassy-stm32/src/can/fdcan.rs index c31a7fc6..f77788db 100644 --- a/embassy-stm32/src/can/fdcan.rs +++ b/embassy-stm32/src/can/fdcan.rs @@ -1,5 +1,5 @@ pub use bxcan; -use embassy_hal_common::PeripheralRef; +use embassy_hal_internal::PeripheralRef; use crate::peripherals; diff --git a/embassy-stm32/src/crc/v1.rs b/embassy-stm32/src/crc/v1.rs index 3946a2d4..154f2eb9 100644 --- a/embassy-stm32/src/crc/v1.rs +++ b/embassy-stm32/src/crc/v1.rs @@ -1,4 +1,4 @@ -use embassy_hal_common::{into_ref, PeripheralRef}; +use embassy_hal_internal::{into_ref, PeripheralRef}; use crate::pac::CRC as PAC_CRC; use crate::peripherals::CRC; diff --git a/embassy-stm32/src/crc/v2v3.rs b/embassy-stm32/src/crc/v2v3.rs index f337055a..de0c0875 100644 --- a/embassy-stm32/src/crc/v2v3.rs +++ b/embassy-stm32/src/crc/v2v3.rs @@ -1,4 +1,4 @@ -use embassy_hal_common::{into_ref, PeripheralRef}; +use embassy_hal_internal::{into_ref, PeripheralRef}; use crate::pac::crc::vals; use crate::pac::CRC as PAC_CRC; diff --git a/embassy-stm32/src/dac/mod.rs b/embassy-stm32/src/dac/mod.rs index 979748bb..a2040b85 100644 --- a/embassy-stm32/src/dac/mod.rs +++ b/embassy-stm32/src/dac/mod.rs @@ -3,7 +3,7 @@ //! Provide access to the STM32 digital-to-analog converter (DAC). use core::marker::PhantomData; -use embassy_hal_common::{into_ref, PeripheralRef}; +use embassy_hal_internal::{into_ref, PeripheralRef}; use crate::pac::dac; use crate::rcc::RccPeripheral; diff --git a/embassy-stm32/src/dcmi.rs b/embassy-stm32/src/dcmi.rs index 78b026cb..7497f4aa 100644 --- a/embassy-stm32/src/dcmi.rs +++ b/embassy-stm32/src/dcmi.rs @@ -2,7 +2,7 @@ use core::future::poll_fn; use core::marker::PhantomData; use core::task::Poll; -use embassy_hal_common::{into_ref, PeripheralRef}; +use embassy_hal_internal::{into_ref, PeripheralRef}; use embassy_sync::waitqueue::AtomicWaker; use crate::dma::Transfer; diff --git a/embassy-stm32/src/dma/bdma.rs b/embassy-stm32/src/dma/bdma.rs index 68c78123..d956047d 100644 --- a/embassy-stm32/src/dma/bdma.rs +++ b/embassy-stm32/src/dma/bdma.rs @@ -6,7 +6,7 @@ use core::sync::atomic::{fence, Ordering}; use core::task::{Context, Poll, Waker}; use atomic_polyfill::AtomicUsize; -use embassy_hal_common::{into_ref, Peripheral, PeripheralRef}; +use embassy_hal_internal::{into_ref, Peripheral, PeripheralRef}; use embassy_sync::waitqueue::AtomicWaker; use super::ringbuffer::{DmaCtrl, DmaRingBuffer, OverrunError}; diff --git a/embassy-stm32/src/dma/dma.rs b/embassy-stm32/src/dma/dma.rs index c27ec7bd..219ef2eb 100644 --- a/embassy-stm32/src/dma/dma.rs +++ b/embassy-stm32/src/dma/dma.rs @@ -4,7 +4,7 @@ use core::pin::Pin; use core::sync::atomic::{fence, AtomicUsize, Ordering}; use core::task::{Context, Poll, Waker}; -use embassy_hal_common::{into_ref, Peripheral, PeripheralRef}; +use embassy_hal_internal::{into_ref, Peripheral, PeripheralRef}; use embassy_sync::waitqueue::AtomicWaker; use super::ringbuffer::{DmaCtrl, DmaRingBuffer, OverrunError}; diff --git a/embassy-stm32/src/dma/gpdma.rs b/embassy-stm32/src/dma/gpdma.rs index b7bcf779..97cc200d 100644 --- a/embassy-stm32/src/dma/gpdma.rs +++ b/embassy-stm32/src/dma/gpdma.rs @@ -5,7 +5,7 @@ use core::pin::Pin; use core::sync::atomic::{fence, Ordering}; use core::task::{Context, Poll}; -use embassy_hal_common::{into_ref, Peripheral, PeripheralRef}; +use embassy_hal_internal::{into_ref, Peripheral, PeripheralRef}; use embassy_sync::waitqueue::AtomicWaker; use super::word::{Word, WordSize}; diff --git a/embassy-stm32/src/dma/mod.rs b/embassy-stm32/src/dma/mod.rs index 0858587b..4f1a58ae 100644 --- a/embassy-stm32/src/dma/mod.rs +++ b/embassy-stm32/src/dma/mod.rs @@ -26,7 +26,7 @@ pub mod word; use core::mem; -use embassy_hal_common::impl_peripheral; +use embassy_hal_internal::impl_peripheral; #[cfg(dmamux)] pub use self::dmamux::*; diff --git a/embassy-stm32/src/eth/v1/mod.rs b/embassy-stm32/src/eth/v1/mod.rs index 2a6ea35f..a1e0240c 100644 --- a/embassy-stm32/src/eth/v1/mod.rs +++ b/embassy-stm32/src/eth/v1/mod.rs @@ -6,7 +6,7 @@ mod tx_desc; use core::marker::PhantomData; use core::sync::atomic::{fence, Ordering}; -use embassy_hal_common::{into_ref, PeripheralRef}; +use embassy_hal_internal::{into_ref, PeripheralRef}; use stm32_metapac::eth::vals::{Apcs, Cr, Dm, DmaomrSr, Fes, Ftf, Ifg, MbProgress, Mw, Pbl, Rsf, St, Tsf}; pub(crate) use self::rx_desc::{RDes, RDesRing}; diff --git a/embassy-stm32/src/eth/v2/mod.rs b/embassy-stm32/src/eth/v2/mod.rs index bb681c42..ada495fd 100644 --- a/embassy-stm32/src/eth/v2/mod.rs +++ b/embassy-stm32/src/eth/v2/mod.rs @@ -3,7 +3,7 @@ mod descriptors; use core::marker::PhantomData; use core::sync::atomic::{fence, Ordering}; -use embassy_hal_common::{into_ref, PeripheralRef}; +use embassy_hal_internal::{into_ref, PeripheralRef}; pub(crate) use self::descriptors::{RDes, RDesRing, TDes, TDesRing}; use super::*; diff --git a/embassy-stm32/src/exti.rs b/embassy-stm32/src/exti.rs index 3ff92c9e..925cf39b 100644 --- a/embassy-stm32/src/exti.rs +++ b/embassy-stm32/src/exti.rs @@ -3,7 +3,7 @@ use core::marker::PhantomData; use core::pin::Pin; use core::task::{Context, Poll}; -use embassy_hal_common::impl_peripheral; +use embassy_hal_internal::impl_peripheral; use embassy_sync::waitqueue::AtomicWaker; use crate::gpio::{AnyPin, Input, Pin as GpioPin}; diff --git a/embassy-stm32/src/flash/asynch.rs b/embassy-stm32/src/flash/asynch.rs index f175349c..e966e2a7 100644 --- a/embassy-stm32/src/flash/asynch.rs +++ b/embassy-stm32/src/flash/asynch.rs @@ -1,8 +1,8 @@ use core::marker::PhantomData; use core::sync::atomic::{fence, Ordering}; -use embassy_hal_common::drop::OnDrop; -use embassy_hal_common::into_ref; +use embassy_hal_internal::drop::OnDrop; +use embassy_hal_internal::into_ref; use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex; use embassy_sync::mutex::Mutex; diff --git a/embassy-stm32/src/flash/common.rs b/embassy-stm32/src/flash/common.rs index 2a374733..16c51129 100644 --- a/embassy-stm32/src/flash/common.rs +++ b/embassy-stm32/src/flash/common.rs @@ -1,8 +1,8 @@ use core::marker::PhantomData; use core::sync::atomic::{fence, Ordering}; -use embassy_hal_common::drop::OnDrop; -use embassy_hal_common::{into_ref, PeripheralRef}; +use embassy_hal_internal::drop::OnDrop; +use embassy_hal_internal::{into_ref, PeripheralRef}; use stm32_metapac::FLASH_BASE; use super::{ diff --git a/embassy-stm32/src/flash/f4.rs b/embassy-stm32/src/flash/f4.rs index 4cb39e03..728f6d60 100644 --- a/embassy-stm32/src/flash/f4.rs +++ b/embassy-stm32/src/flash/f4.rs @@ -14,7 +14,7 @@ use crate::pac; mod alt_regions { use core::marker::PhantomData; - use embassy_hal_common::PeripheralRef; + use embassy_hal_internal::PeripheralRef; use stm32_metapac::FLASH_SIZE; use crate::_generated::flash_regions::{OTPRegion, BANK1_REGION1, BANK1_REGION2, BANK1_REGION3, OTP_REGION}; diff --git a/embassy-stm32/src/fmc.rs b/embassy-stm32/src/fmc.rs index 60d7a00e..177e66a9 100644 --- a/embassy-stm32/src/fmc.rs +++ b/embassy-stm32/src/fmc.rs @@ -1,6 +1,6 @@ use core::marker::PhantomData; -use embassy_hal_common::into_ref; +use embassy_hal_internal::into_ref; use crate::gpio::sealed::AFType; use crate::gpio::{Pull, Speed}; diff --git a/embassy-stm32/src/gpio.rs b/embassy-stm32/src/gpio.rs index af3a8eac..cda59714 100644 --- a/embassy-stm32/src/gpio.rs +++ b/embassy-stm32/src/gpio.rs @@ -1,7 +1,7 @@ #![macro_use] use core::convert::Infallible; -use embassy_hal_common::{impl_peripheral, into_ref, PeripheralRef}; +use embassy_hal_internal::{impl_peripheral, into_ref, PeripheralRef}; use crate::pac::gpio::{self, vals}; use crate::{pac, peripherals, Peripheral}; diff --git a/embassy-stm32/src/i2c/v1.rs b/embassy-stm32/src/i2c/v1.rs index aa485cd8..e5254a8c 100644 --- a/embassy-stm32/src/i2c/v1.rs +++ b/embassy-stm32/src/i2c/v1.rs @@ -1,7 +1,7 @@ use core::marker::PhantomData; use embassy_embedded_hal::SetConfig; -use embassy_hal_common::{into_ref, PeripheralRef}; +use embassy_hal_internal::{into_ref, PeripheralRef}; use crate::dma::NoDma; use crate::gpio::sealed::AFType; diff --git a/embassy-stm32/src/i2c/v2.rs b/embassy-stm32/src/i2c/v2.rs index 208d1527..eaf980a4 100644 --- a/embassy-stm32/src/i2c/v2.rs +++ b/embassy-stm32/src/i2c/v2.rs @@ -4,8 +4,8 @@ use core::marker::PhantomData; use core::task::Poll; use embassy_embedded_hal::SetConfig; -use embassy_hal_common::drop::OnDrop; -use embassy_hal_common::{into_ref, PeripheralRef}; +use embassy_hal_internal::drop::OnDrop; +use embassy_hal_internal::{into_ref, PeripheralRef}; use embassy_sync::waitqueue::AtomicWaker; use crate::dma::{NoDma, Transfer}; diff --git a/embassy-stm32/src/i2s.rs b/embassy-stm32/src/i2s.rs index 62dda69b..1ccad732 100644 --- a/embassy-stm32/src/i2s.rs +++ b/embassy-stm32/src/i2s.rs @@ -1,4 +1,4 @@ -use embassy_hal_common::into_ref; +use embassy_hal_internal::into_ref; use crate::gpio::sealed::{AFType, Pin as _}; use crate::gpio::AnyPin; diff --git a/embassy-stm32/src/lib.rs b/embassy-stm32/src/lib.rs index 45a7b547..ebd0e7cd 100644 --- a/embassy-stm32/src/lib.rs +++ b/embassy-stm32/src/lib.rs @@ -79,7 +79,7 @@ pub use crate::_generated::interrupt; /// This defines the right interrupt handlers, and creates a unit struct (like `struct Irqs;`) /// and implements the right [`Binding`]s for it. You can pass this struct to drivers to /// prove at compile-time that the right interrupts have been bound. -// developer note: this macro can't be in `embassy-hal-common` due to the use of `$crate`. +// developer note: this macro can't be in `embassy-hal-internal` due to the use of `$crate`. #[macro_export] macro_rules! bind_interrupts { ($vis:vis struct $name:ident { $($irq:ident => $($handler:ty),*;)* }) => { @@ -103,7 +103,7 @@ macro_rules! bind_interrupts { // Reexports pub use _generated::{peripherals, Peripherals}; -pub use embassy_hal_common::{into_ref, Peripheral, PeripheralRef}; +pub use embassy_hal_internal::{into_ref, Peripheral, PeripheralRef}; #[cfg(feature = "unstable-pac")] pub use stm32_metapac as pac; #[cfg(not(feature = "unstable-pac"))] diff --git a/embassy-stm32/src/pwm/complementary_pwm.rs b/embassy-stm32/src/pwm/complementary_pwm.rs index 4d64d005..64bb32c3 100644 --- a/embassy-stm32/src/pwm/complementary_pwm.rs +++ b/embassy-stm32/src/pwm/complementary_pwm.rs @@ -1,6 +1,6 @@ use core::marker::PhantomData; -use embassy_hal_common::{into_ref, PeripheralRef}; +use embassy_hal_internal::{into_ref, PeripheralRef}; use stm32_metapac::timer::vals::Ckd; use super::simple_pwm::*; diff --git a/embassy-stm32/src/pwm/simple_pwm.rs b/embassy-stm32/src/pwm/simple_pwm.rs index 995f59c2..51479693 100644 --- a/embassy-stm32/src/pwm/simple_pwm.rs +++ b/embassy-stm32/src/pwm/simple_pwm.rs @@ -1,6 +1,6 @@ use core::marker::PhantomData; -use embassy_hal_common::{into_ref, PeripheralRef}; +use embassy_hal_internal::{into_ref, PeripheralRef}; use super::*; #[allow(unused_imports)] diff --git a/embassy-stm32/src/qspi/mod.rs b/embassy-stm32/src/qspi/mod.rs index 31b67608..32382fb2 100644 --- a/embassy-stm32/src/qspi/mod.rs +++ b/embassy-stm32/src/qspi/mod.rs @@ -2,7 +2,7 @@ pub mod enums; -use embassy_hal_common::{into_ref, PeripheralRef}; +use embassy_hal_internal::{into_ref, PeripheralRef}; use enums::*; use crate::dma::Transfer; diff --git a/embassy-stm32/src/rcc/f4.rs b/embassy-stm32/src/rcc/f4.rs index b8447044..7aa9f0fd 100644 --- a/embassy-stm32/src/rcc/f4.rs +++ b/embassy-stm32/src/rcc/f4.rs @@ -1,6 +1,6 @@ use core::marker::PhantomData; -use embassy_hal_common::into_ref; +use embassy_hal_internal::into_ref; use stm32_metapac::rcc::vals::{Mco1, Mco2, Mcopre}; use super::sealed::RccPeripheral; diff --git a/embassy-stm32/src/rcc/h7.rs b/embassy-stm32/src/rcc/h7.rs index 7e5cd0d1..bbc0e083 100644 --- a/embassy-stm32/src/rcc/h7.rs +++ b/embassy-stm32/src/rcc/h7.rs @@ -1,6 +1,6 @@ use core::marker::PhantomData; -use embassy_hal_common::into_ref; +use embassy_hal_internal::into_ref; pub use pll::PllConfig; use stm32_metapac::rcc::vals::{Mco1, Mco2}; diff --git a/embassy-stm32/src/rcc/l4.rs b/embassy-stm32/src/rcc/l4.rs index 8a9b4adb..dc5f55d0 100644 --- a/embassy-stm32/src/rcc/l4.rs +++ b/embassy-stm32/src/rcc/l4.rs @@ -1,6 +1,6 @@ use core::marker::PhantomData; -use embassy_hal_common::into_ref; +use embassy_hal_internal::into_ref; use stm32_metapac::rcc::regs::Cfgr; use stm32_metapac::rcc::vals::{Lsedrv, Mcopre, Mcosel}; diff --git a/embassy-stm32/src/rng.rs b/embassy-stm32/src/rng.rs index b2faec53..27415c2d 100644 --- a/embassy-stm32/src/rng.rs +++ b/embassy-stm32/src/rng.rs @@ -3,7 +3,7 @@ use core::future::poll_fn; use core::task::Poll; -use embassy_hal_common::{into_ref, PeripheralRef}; +use embassy_hal_internal::{into_ref, PeripheralRef}; use embassy_sync::waitqueue::AtomicWaker; use rand_core::{CryptoRng, RngCore}; diff --git a/embassy-stm32/src/rtc/mod.rs b/embassy-stm32/src/rtc/mod.rs index 12a2ac79..323be318 100644 --- a/embassy-stm32/src/rtc/mod.rs +++ b/embassy-stm32/src/rtc/mod.rs @@ -15,7 +15,7 @@ pub use self::datetime::{DateTime, DayOfWeek, Error as DateTimeError}; #[cfg_attr(any(rtc_v3, rtc_v3u5), path = "v3.rs")] mod _version; pub use _version::*; -use embassy_hal_common::Peripheral; +use embassy_hal_internal::Peripheral; /// Errors that can occur on methods on [RtcClock] #[derive(Clone, Debug, PartialEq, Eq)] diff --git a/embassy-stm32/src/sdmmc/mod.rs b/embassy-stm32/src/sdmmc/mod.rs index 434c56a4..6b532363 100644 --- a/embassy-stm32/src/sdmmc/mod.rs +++ b/embassy-stm32/src/sdmmc/mod.rs @@ -6,8 +6,8 @@ use core::marker::PhantomData; use core::ops::{Deref, DerefMut}; use core::task::Poll; -use embassy_hal_common::drop::OnDrop; -use embassy_hal_common::{into_ref, PeripheralRef}; +use embassy_hal_internal::drop::OnDrop; +use embassy_hal_internal::{into_ref, PeripheralRef}; use embassy_sync::waitqueue::AtomicWaker; use sdio_host::{BusWidth, CardCapacity, CardStatus, CurrentState, SDStatus, CID, CSD, OCR, SCR}; diff --git a/embassy-stm32/src/spi/mod.rs b/embassy-stm32/src/spi/mod.rs index d5f63f84..bdf3c85b 100644 --- a/embassy-stm32/src/spi/mod.rs +++ b/embassy-stm32/src/spi/mod.rs @@ -4,7 +4,7 @@ use core::ptr; use embassy_embedded_hal::SetConfig; use embassy_futures::join::join; -use embassy_hal_common::{into_ref, PeripheralRef}; +use embassy_hal_internal::{into_ref, PeripheralRef}; pub use embedded_hal_02::spi::{Mode, Phase, Polarity, MODE_0, MODE_1, MODE_2, MODE_3}; use crate::dma::{slice_ptr_parts, word, Transfer}; diff --git a/embassy-stm32/src/usart/buffered.rs b/embassy-stm32/src/usart/buffered.rs index 433ad299..ca117da8 100644 --- a/embassy-stm32/src/usart/buffered.rs +++ b/embassy-stm32/src/usart/buffered.rs @@ -2,7 +2,7 @@ use core::future::poll_fn; use core::slice; use core::task::Poll; -use embassy_hal_common::atomic_ring_buffer::RingBuffer; +use embassy_hal_internal::atomic_ring_buffer::RingBuffer; use embassy_sync::waitqueue::AtomicWaker; use super::*; diff --git a/embassy-stm32/src/usart/mod.rs b/embassy-stm32/src/usart/mod.rs index 3b9226fd..d99034bc 100644 --- a/embassy-stm32/src/usart/mod.rs +++ b/embassy-stm32/src/usart/mod.rs @@ -5,8 +5,8 @@ use core::marker::PhantomData; use core::sync::atomic::{compiler_fence, Ordering}; use core::task::Poll; -use embassy_hal_common::drop::OnDrop; -use embassy_hal_common::{into_ref, PeripheralRef}; +use embassy_hal_internal::drop::OnDrop; +use embassy_hal_internal::{into_ref, PeripheralRef}; use futures::future::{select, Either}; use crate::dma::{NoDma, Transfer}; diff --git a/embassy-stm32/src/usart/ringbuffered.rs b/embassy-stm32/src/usart/ringbuffered.rs index c74d7e09..80261d04 100644 --- a/embassy-stm32/src/usart/ringbuffered.rs +++ b/embassy-stm32/src/usart/ringbuffered.rs @@ -2,7 +2,7 @@ use core::future::poll_fn; use core::sync::atomic::{compiler_fence, Ordering}; use core::task::Poll; -use embassy_hal_common::PeripheralRef; +use embassy_hal_internal::PeripheralRef; use futures::future::{select, Either}; use super::{clear_interrupt_flags, rdr, sr, BasicInstance, Error, UartRx}; diff --git a/embassy-stm32/src/usb/usb.rs b/embassy-stm32/src/usb/usb.rs index ecdd1d0b..cef19635 100644 --- a/embassy-stm32/src/usb/usb.rs +++ b/embassy-stm32/src/usb/usb.rs @@ -5,7 +5,7 @@ use core::marker::PhantomData; use core::sync::atomic::{AtomicBool, Ordering}; use core::task::Poll; -use embassy_hal_common::into_ref; +use embassy_hal_internal::into_ref; use embassy_sync::waitqueue::AtomicWaker; use embassy_usb_driver as driver; use embassy_usb_driver::{ diff --git a/embassy-stm32/src/usb_otg/usb.rs b/embassy-stm32/src/usb_otg/usb.rs index fd0e22ad..348f0f79 100644 --- a/embassy-stm32/src/usb_otg/usb.rs +++ b/embassy-stm32/src/usb_otg/usb.rs @@ -3,7 +3,7 @@ use core::marker::PhantomData; use core::sync::atomic::{AtomicBool, AtomicU16, Ordering}; use core::task::Poll; -use embassy_hal_common::{into_ref, Peripheral}; +use embassy_hal_internal::{into_ref, Peripheral}; use embassy_sync::waitqueue::AtomicWaker; use embassy_usb_driver::{ self, Bus as _, Direction, EndpointAddress, EndpointAllocError, EndpointError, EndpointIn, EndpointInfo, diff --git a/embassy-stm32/src/wdg/mod.rs b/embassy-stm32/src/wdg/mod.rs index b03e81d6..eafd0336 100644 --- a/embassy-stm32/src/wdg/mod.rs +++ b/embassy-stm32/src/wdg/mod.rs @@ -1,6 +1,6 @@ use core::marker::PhantomData; -use embassy_hal_common::{into_ref, Peripheral}; +use embassy_hal_internal::{into_ref, Peripheral}; use stm32_metapac::iwdg::vals::{Key, Pr}; use crate::rcc::LSI_FREQ; diff --git a/examples/stm32g4/Cargo.toml b/examples/stm32g4/Cargo.toml index ce883860..cf3e2ce9 100644 --- a/examples/stm32g4/Cargo.toml +++ b/examples/stm32g4/Cargo.toml @@ -10,7 +10,6 @@ embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = [" embassy-sync = { version = "0.2.0", path = "../../embassy-sync", features = ["defmt"] } embassy-executor = { version = "0.2.0", path = "../../embassy-executor", features = ["nightly", "arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] } embassy-time = { version = "0.1.2", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } -embassy-hal-common = {version = "0.1.0", path = "../../embassy-hal-common" } embassy-usb = { version = "0.1.0", path = "../../embassy-usb", features = ["defmt"] } defmt = "0.3" From d5f9d17b7c12c73cf16aa7414ce819bbd06efc2e Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Fri, 28 Jul 2023 13:38:26 +0200 Subject: [PATCH 29/68] Make pipes local vars. --- examples/rp/src/bin/pio_uart.rs | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/examples/rp/src/bin/pio_uart.rs b/examples/rp/src/bin/pio_uart.rs index c978f8f0..ca1c7f39 100644 --- a/examples/rp/src/bin/pio_uart.rs +++ b/examples/rp/src/bin/pio_uart.rs @@ -18,7 +18,7 @@ use embassy_rp::bind_interrupts; use embassy_rp::peripherals::{PIO0, USB}; use embassy_rp::pio::InterruptHandler as PioInterruptHandler; use embassy_rp::usb::{Driver, Instance, InterruptHandler}; -use embassy_sync::blocking_mutex::raw::ThreadModeRawMutex; +use embassy_sync::blocking_mutex::raw::NoopRawMutex; use embassy_sync::pipe::Pipe; use embassy_usb::class::cdc_acm::{CdcAcmClass, Receiver, Sender, State}; use embassy_usb::driver::EndpointError; @@ -91,13 +91,13 @@ async fn main(_spawner: Spawner) { let (mut uart_tx, mut uart_rx) = uart.split(); // Pipe setup - static USB_PIPE: Pipe = Pipe::new(); - let mut usb_pipe_writer = USB_PIPE.writer(); - let mut usb_pipe_reader = USB_PIPE.reader(); + let usb_pipe: Pipe = Pipe::new(); + let mut usb_pipe_writer = usb_pipe.writer(); + let mut usb_pipe_reader = usb_pipe.reader(); - static UART_PIPE: Pipe = Pipe::new(); - let mut uart_pipe_writer = UART_PIPE.writer(); - let mut uart_pipe_reader = UART_PIPE.reader(); + let uart_pipe: Pipe = Pipe::new(); + let mut uart_pipe_writer = uart_pipe.writer(); + let mut uart_pipe_reader = uart_pipe.reader(); let (mut usb_tx, mut usb_rx) = class.split(); @@ -141,7 +141,7 @@ impl From for Disconnected { /// Read from the USB and write it to the UART TX pipe async fn usb_read<'d, T: Instance + 'd>( usb_rx: &mut Receiver<'d, Driver<'d, T>>, - uart_pipe_writer: &mut embassy_sync::pipe::Writer<'static, ThreadModeRawMutex, 20>, + uart_pipe_writer: &mut embassy_sync::pipe::Writer<'_, NoopRawMutex, 20>, ) -> Result<(), Disconnected> { let mut buf = [0; 64]; loop { @@ -155,7 +155,7 @@ async fn usb_read<'d, T: Instance + 'd>( /// Read from the USB TX pipe and write it to the USB async fn usb_write<'d, T: Instance + 'd>( usb_tx: &mut Sender<'d, Driver<'d, T>>, - usb_pipe_reader: &mut embassy_sync::pipe::Reader<'d, ThreadModeRawMutex, 20>, + usb_pipe_reader: &mut embassy_sync::pipe::Reader<'_, NoopRawMutex, 20>, ) -> Result<(), Disconnected> { let mut buf = [0; 64]; loop { @@ -167,9 +167,9 @@ async fn usb_write<'d, T: Instance + 'd>( } /// Read from the UART and write it to the USB TX pipe -async fn uart_read<'a>( - uart_rx: &mut PioUartRx<'a>, - usb_pipe_writer: &mut embassy_sync::pipe::Writer<'static, ThreadModeRawMutex, 20>, +async fn uart_read( + uart_rx: &mut PioUartRx<'_>, + usb_pipe_writer: &mut embassy_sync::pipe::Writer<'_, NoopRawMutex, 20>, ) -> ! { let mut buf = [0; 64]; loop { @@ -184,9 +184,9 @@ async fn uart_read<'a>( } /// Read from the UART TX pipe and write it to the UART -async fn uart_write<'a>( - uart_tx: &mut PioUartTx<'a>, - uart_pipe_reader: &mut embassy_sync::pipe::Reader<'a, ThreadModeRawMutex, 20>, +async fn uart_write( + uart_tx: &mut PioUartTx<'_>, + uart_pipe_reader: &mut embassy_sync::pipe::Reader<'_, NoopRawMutex, 20>, ) -> ! { let mut buf = [0; 64]; loop { From f81ee103bf946bc42355474d5e125d1e5ab08da8 Mon Sep 17 00:00:00 2001 From: Ruben De Smet Date: Fri, 28 Jul 2023 14:52:03 +0200 Subject: [PATCH 30/68] Allow ethernet and 802.15.4 to coexist Co-authored-by: Thibaut Vandervelden --- embassy-net/src/lib.rs | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/embassy-net/src/lib.rs b/embassy-net/src/lib.rs index 81c751a2..3f915016 100644 --- a/embassy-net/src/lib.rs +++ b/embassy-net/src/lib.rs @@ -32,12 +32,14 @@ pub use smoltcp::iface::MulticastError; use smoltcp::iface::{Interface, SocketHandle, SocketSet, SocketStorage}; #[cfg(feature = "dhcpv4")] use smoltcp::socket::dhcpv4::{self, RetryConfig}; +#[cfg(feature = "medium-ethernet")] +pub use smoltcp::wire::EthernetAddress; +#[cfg(any(feature = "medium-ethernet", feature = "medium-ieee802154"))] +pub use smoltcp::wire::HardwareAddress; #[cfg(feature = "udp")] pub use smoltcp::wire::IpListenEndpoint; -#[cfg(feature = "medium-ethernet")] -pub use smoltcp::wire::{EthernetAddress, HardwareAddress}; #[cfg(feature = "medium-ieee802154")] -pub use smoltcp::wire::{HardwareAddress, Ieee802154Address}; +pub use smoltcp::wire::{Ieee802154Address, Ieee802154Frame}; pub use smoltcp::wire::{IpAddress, IpCidr, IpEndpoint}; #[cfg(feature = "proto-ipv4")] pub use smoltcp::wire::{Ipv4Address, Ipv4Cidr}; @@ -583,7 +585,7 @@ impl SocketStack { impl Inner { #[cfg(feature = "proto-ipv4")] fn apply_config_v4(&mut self, s: &mut SocketStack, config: StaticConfigV4) { - #[cfg(feature = "medium-ethernet")] + #[cfg(any(feature = "medium-ethernet", feature = "medium-ieee802154"))] let medium = self.device.capabilities().medium; debug!("Acquired IP configuration:"); From 3690af9bea5968653780d296146a91c63994d89d Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Fri, 28 Jul 2023 15:29:27 +0200 Subject: [PATCH 31/68] stm32/timer: merge pwm module into timer. (#1703) The traits there are applicable to timer use cases other than PWM. It doesn't make sense to keep them separated. --- embassy-stm32/build.rs | 30 +- embassy-stm32/src/lib.rs | 1 - embassy-stm32/src/pwm/mod.rs | 269 ----------------- .../src/{pwm => timer}/complementary_pwm.rs | 0 embassy-stm32/src/timer/mod.rs | 271 ++++++++++++++++-- .../src/{pwm => timer}/simple_pwm.rs | 0 examples/stm32f4/src/bin/pwm.rs | 4 +- examples/stm32f4/src/bin/pwm_complementary.rs | 6 +- examples/stm32g4/src/bin/pwm.rs | 4 +- .../stm32h7/src/bin/low_level_timer_api.rs | 2 +- examples/stm32h7/src/bin/pwm.rs | 4 +- 11 files changed, 271 insertions(+), 320 deletions(-) delete mode 100644 embassy-stm32/src/pwm/mod.rs rename embassy-stm32/src/{pwm => timer}/complementary_pwm.rs (100%) rename embassy-stm32/src/{pwm => timer}/simple_pwm.rs (100%) diff --git a/embassy-stm32/build.rs b/embassy-stm32/build.rs index 0e9606ec..409a943d 100644 --- a/embassy-stm32/build.rs +++ b/embassy-stm32/build.rs @@ -572,21 +572,21 @@ fn main() { (("fmc", "Clk"), quote!(crate::fmc::ClkPin)), (("fmc", "BA0"), quote!(crate::fmc::BA0Pin)), (("fmc", "BA1"), quote!(crate::fmc::BA1Pin)), - (("timer", "CH1"), quote!(crate::pwm::Channel1Pin)), - (("timer", "CH1N"), quote!(crate::pwm::Channel1ComplementaryPin)), - (("timer", "CH2"), quote!(crate::pwm::Channel2Pin)), - (("timer", "CH2N"), quote!(crate::pwm::Channel2ComplementaryPin)), - (("timer", "CH3"), quote!(crate::pwm::Channel3Pin)), - (("timer", "CH3N"), quote!(crate::pwm::Channel3ComplementaryPin)), - (("timer", "CH4"), quote!(crate::pwm::Channel4Pin)), - (("timer", "CH4N"), quote!(crate::pwm::Channel4ComplementaryPin)), - (("timer", "ETR"), quote!(crate::pwm::ExternalTriggerPin)), - (("timer", "BKIN"), quote!(crate::pwm::BreakInputPin)), - (("timer", "BKIN_COMP1"), quote!(crate::pwm::BreakInputComparator1Pin)), - (("timer", "BKIN_COMP2"), quote!(crate::pwm::BreakInputComparator2Pin)), - (("timer", "BKIN2"), quote!(crate::pwm::BreakInput2Pin)), - (("timer", "BKIN2_COMP1"), quote!(crate::pwm::BreakInput2Comparator1Pin)), - (("timer", "BKIN2_COMP2"), quote!(crate::pwm::BreakInput2Comparator2Pin)), + (("timer", "CH1"), quote!(crate::timer::Channel1Pin)), + (("timer", "CH1N"), quote!(crate::timer::Channel1ComplementaryPin)), + (("timer", "CH2"), quote!(crate::timer::Channel2Pin)), + (("timer", "CH2N"), quote!(crate::timer::Channel2ComplementaryPin)), + (("timer", "CH3"), quote!(crate::timer::Channel3Pin)), + (("timer", "CH3N"), quote!(crate::timer::Channel3ComplementaryPin)), + (("timer", "CH4"), quote!(crate::timer::Channel4Pin)), + (("timer", "CH4N"), quote!(crate::timer::Channel4ComplementaryPin)), + (("timer", "ETR"), quote!(crate::timer::ExternalTriggerPin)), + (("timer", "BKIN"), quote!(crate::timer::BreakInputPin)), + (("timer", "BKIN_COMP1"), quote!(crate::timer::BreakInputComparator1Pin)), + (("timer", "BKIN_COMP2"), quote!(crate::timer::BreakInputComparator2Pin)), + (("timer", "BKIN2"), quote!(crate::timer::BreakInput2Pin)), + (("timer", "BKIN2_COMP1"), quote!(crate::timer::BreakInput2Comparator1Pin)), + (("timer", "BKIN2_COMP2"), quote!(crate::timer::BreakInput2Comparator2Pin)), (("sdmmc", "CK"), quote!(crate::sdmmc::CkPin)), (("sdmmc", "CMD"), quote!(crate::sdmmc::CmdPin)), (("sdmmc", "D0"), quote!(crate::sdmmc::D0Pin)), diff --git a/embassy-stm32/src/lib.rs b/embassy-stm32/src/lib.rs index ebd0e7cd..bb2ef2fc 100644 --- a/embassy-stm32/src/lib.rs +++ b/embassy-stm32/src/lib.rs @@ -43,7 +43,6 @@ pub mod flash; pub mod i2s; #[cfg(stm32wb)] pub mod ipcc; -pub mod pwm; #[cfg(quadspi)] pub mod qspi; #[cfg(rng)] diff --git a/embassy-stm32/src/pwm/mod.rs b/embassy-stm32/src/pwm/mod.rs deleted file mode 100644 index 5aba2663..00000000 --- a/embassy-stm32/src/pwm/mod.rs +++ /dev/null @@ -1,269 +0,0 @@ -pub mod complementary_pwm; -pub mod simple_pwm; - -use stm32_metapac::timer::vals::Ckd; - -#[cfg(feature = "unstable-pac")] -pub mod low_level { - pub use super::sealed::*; -} - -#[derive(Clone, Copy)] -pub enum Channel { - Ch1, - Ch2, - Ch3, - Ch4, -} - -impl Channel { - pub fn raw(&self) -> usize { - match self { - Channel::Ch1 => 0, - Channel::Ch2 => 1, - Channel::Ch3 => 2, - Channel::Ch4 => 3, - } - } -} - -#[derive(Clone, Copy)] -pub enum OutputCompareMode { - Frozen, - ActiveOnMatch, - InactiveOnMatch, - Toggle, - ForceInactive, - ForceActive, - PwmMode1, - PwmMode2, -} - -impl From for stm32_metapac::timer::vals::Ocm { - fn from(mode: OutputCompareMode) -> Self { - match mode { - OutputCompareMode::Frozen => stm32_metapac::timer::vals::Ocm::FROZEN, - OutputCompareMode::ActiveOnMatch => stm32_metapac::timer::vals::Ocm::ACTIVEONMATCH, - OutputCompareMode::InactiveOnMatch => stm32_metapac::timer::vals::Ocm::INACTIVEONMATCH, - OutputCompareMode::Toggle => stm32_metapac::timer::vals::Ocm::TOGGLE, - OutputCompareMode::ForceInactive => stm32_metapac::timer::vals::Ocm::FORCEINACTIVE, - OutputCompareMode::ForceActive => stm32_metapac::timer::vals::Ocm::FORCEACTIVE, - OutputCompareMode::PwmMode1 => stm32_metapac::timer::vals::Ocm::PWMMODE1, - OutputCompareMode::PwmMode2 => stm32_metapac::timer::vals::Ocm::PWMMODE2, - } - } -} - -pub(crate) mod sealed { - use super::*; - - pub trait CaptureCompare16bitInstance: crate::timer::sealed::GeneralPurpose16bitInstance { - /// Global output enable. Does not do anything on non-advanced timers. - fn enable_outputs(&mut self, enable: bool); - - fn set_output_compare_mode(&mut self, channel: Channel, mode: OutputCompareMode); - - fn enable_channel(&mut self, channel: Channel, enable: bool); - - fn set_compare_value(&mut self, channel: Channel, value: u16); - - fn get_max_compare_value(&self) -> u16; - } - - pub trait ComplementaryCaptureCompare16bitInstance: CaptureCompare16bitInstance { - fn set_dead_time_clock_division(&mut self, value: Ckd); - - fn set_dead_time_value(&mut self, value: u8); - - fn enable_complementary_channel(&mut self, channel: Channel, enable: bool); - } - - pub trait CaptureCompare32bitInstance: crate::timer::sealed::GeneralPurpose32bitInstance { - fn set_output_compare_mode(&mut self, channel: Channel, mode: OutputCompareMode); - - fn enable_channel(&mut self, channel: Channel, enable: bool); - - fn set_compare_value(&mut self, channel: Channel, value: u32); - - fn get_max_compare_value(&self) -> u32; - } -} - -pub trait CaptureCompare16bitInstance: - sealed::CaptureCompare16bitInstance + crate::timer::GeneralPurpose16bitInstance + 'static -{ -} - -pub trait ComplementaryCaptureCompare16bitInstance: - sealed::ComplementaryCaptureCompare16bitInstance + crate::timer::AdvancedControlInstance + 'static -{ -} - -pub trait CaptureCompare32bitInstance: - sealed::CaptureCompare32bitInstance + CaptureCompare16bitInstance + crate::timer::GeneralPurpose32bitInstance + 'static -{ -} - -#[allow(unused)] -macro_rules! impl_compare_capable_16bit { - ($inst:ident) => { - impl crate::pwm::sealed::CaptureCompare16bitInstance for crate::peripherals::$inst { - fn enable_outputs(&mut self, _enable: bool) {} - - fn set_output_compare_mode(&mut self, channel: crate::pwm::Channel, mode: OutputCompareMode) { - use crate::timer::sealed::GeneralPurpose16bitInstance; - let r = Self::regs_gp16(); - let raw_channel: usize = channel.raw(); - r.ccmr_output(raw_channel / 2) - .modify(|w| w.set_ocm(raw_channel % 2, mode.into())); - } - - fn enable_channel(&mut self, channel: Channel, enable: bool) { - use crate::timer::sealed::GeneralPurpose16bitInstance; - Self::regs_gp16() - .ccer() - .modify(|w| w.set_cce(channel.raw(), enable)); - } - - fn set_compare_value(&mut self, channel: Channel, value: u16) { - use crate::timer::sealed::GeneralPurpose16bitInstance; - Self::regs_gp16().ccr(channel.raw()).modify(|w| w.set_ccr(value)); - } - - fn get_max_compare_value(&self) -> u16 { - use crate::timer::sealed::GeneralPurpose16bitInstance; - Self::regs_gp16().arr().read().arr() - } - } - }; -} - -foreach_interrupt! { - ($inst:ident, timer, TIM_GP16, UP, $irq:ident) => { - impl_compare_capable_16bit!($inst); - - impl CaptureCompare16bitInstance for crate::peripherals::$inst { - - } - }; - - ($inst:ident, timer, TIM_GP32, UP, $irq:ident) => { - impl_compare_capable_16bit!($inst); - impl crate::pwm::sealed::CaptureCompare32bitInstance for crate::peripherals::$inst { - fn set_output_compare_mode( - &mut self, - channel: crate::pwm::Channel, - mode: OutputCompareMode, - ) { - use crate::timer::sealed::GeneralPurpose32bitInstance; - let raw_channel = channel.raw(); - Self::regs_gp32().ccmr_output(raw_channel / 2).modify(|w| w.set_ocm(raw_channel % 2, mode.into())); - } - - fn enable_channel(&mut self, channel: Channel, enable: bool) { - use crate::timer::sealed::GeneralPurpose32bitInstance; - Self::regs_gp32().ccer().modify(|w| w.set_cce(channel.raw(), enable)); - } - - fn set_compare_value(&mut self, channel: Channel, value: u32) { - use crate::timer::sealed::GeneralPurpose32bitInstance; - Self::regs_gp32().ccr(channel.raw()).modify(|w| w.set_ccr(value)); - } - - fn get_max_compare_value(&self) -> u32 { - use crate::timer::sealed::GeneralPurpose32bitInstance; - Self::regs_gp32().arr().read().arr() as u32 - } - } - impl CaptureCompare16bitInstance for crate::peripherals::$inst { - - } - impl CaptureCompare32bitInstance for crate::peripherals::$inst { - - } - }; - - ($inst:ident, timer, TIM_ADV, UP, $irq:ident) => { - impl crate::pwm::sealed::CaptureCompare16bitInstance for crate::peripherals::$inst { - fn enable_outputs(&mut self, enable: bool) { - use crate::timer::sealed::AdvancedControlInstance; - let r = Self::regs_advanced(); - r.bdtr().modify(|w| w.set_moe(enable)); - } - - fn set_output_compare_mode( - &mut self, - channel: crate::pwm::Channel, - mode: OutputCompareMode, - ) { - use crate::timer::sealed::AdvancedControlInstance; - let r = Self::regs_advanced(); - let raw_channel: usize = channel.raw(); - r.ccmr_output(raw_channel / 2) - .modify(|w| w.set_ocm(raw_channel % 2, mode.into())); - } - - fn enable_channel(&mut self, channel: Channel, enable: bool) { - use crate::timer::sealed::AdvancedControlInstance; - Self::regs_advanced() - .ccer() - .modify(|w| w.set_cce(channel.raw(), enable)); - } - - fn set_compare_value(&mut self, channel: Channel, value: u16) { - use crate::timer::sealed::AdvancedControlInstance; - Self::regs_advanced() - .ccr(channel.raw()) - .modify(|w| w.set_ccr(value)); - } - - fn get_max_compare_value(&self) -> u16 { - use crate::timer::sealed::AdvancedControlInstance; - Self::regs_advanced().arr().read().arr() - } - } - - impl CaptureCompare16bitInstance for crate::peripherals::$inst { - - } - - impl crate::pwm::sealed::ComplementaryCaptureCompare16bitInstance for crate::peripherals::$inst { - fn set_dead_time_clock_division(&mut self, value: Ckd) { - use crate::timer::sealed::AdvancedControlInstance; - Self::regs_advanced().cr1().modify(|w| w.set_ckd(value)); - } - - fn set_dead_time_value(&mut self, value: u8) { - use crate::timer::sealed::AdvancedControlInstance; - Self::regs_advanced().bdtr().modify(|w| w.set_dtg(value)); - } - - fn enable_complementary_channel(&mut self, channel: Channel, enable: bool) { - use crate::timer::sealed::AdvancedControlInstance; - Self::regs_advanced() - .ccer() - .modify(|w| w.set_ccne(channel.raw(), enable)); - } - } - - impl ComplementaryCaptureCompare16bitInstance for crate::peripherals::$inst { - - } - }; -} - -pin_trait!(Channel1Pin, CaptureCompare16bitInstance); -pin_trait!(Channel1ComplementaryPin, CaptureCompare16bitInstance); -pin_trait!(Channel2Pin, CaptureCompare16bitInstance); -pin_trait!(Channel2ComplementaryPin, CaptureCompare16bitInstance); -pin_trait!(Channel3Pin, CaptureCompare16bitInstance); -pin_trait!(Channel3ComplementaryPin, CaptureCompare16bitInstance); -pin_trait!(Channel4Pin, CaptureCompare16bitInstance); -pin_trait!(Channel4ComplementaryPin, CaptureCompare16bitInstance); -pin_trait!(ExternalTriggerPin, CaptureCompare16bitInstance); -pin_trait!(BreakInputPin, CaptureCompare16bitInstance); -pin_trait!(BreakInputComparator1Pin, CaptureCompare16bitInstance); -pin_trait!(BreakInputComparator2Pin, CaptureCompare16bitInstance); -pin_trait!(BreakInput2Pin, CaptureCompare16bitInstance); -pin_trait!(BreakInput2Comparator1Pin, CaptureCompare16bitInstance); -pin_trait!(BreakInput2Comparator2Pin, CaptureCompare16bitInstance); diff --git a/embassy-stm32/src/pwm/complementary_pwm.rs b/embassy-stm32/src/timer/complementary_pwm.rs similarity index 100% rename from embassy-stm32/src/pwm/complementary_pwm.rs rename to embassy-stm32/src/timer/complementary_pwm.rs diff --git a/embassy-stm32/src/timer/mod.rs b/embassy-stm32/src/timer/mod.rs index 09b7a377..6c2d6d82 100644 --- a/embassy-stm32/src/timer/mod.rs +++ b/embassy-stm32/src/timer/mod.rs @@ -1,3 +1,6 @@ +pub mod complementary_pwm; +pub mod simple_pwm; + use stm32_metapac::timer::vals; use crate::interrupt; @@ -43,15 +46,123 @@ pub(crate) mod sealed { pub trait AdvancedControlInstance: GeneralPurpose16bitInstance { fn regs_advanced() -> crate::pac::timer::TimAdv; } + + pub trait CaptureCompare16bitInstance: GeneralPurpose16bitInstance { + /// Global output enable. Does not do anything on non-advanced timers. + fn enable_outputs(&mut self, enable: bool); + + fn set_output_compare_mode(&mut self, channel: Channel, mode: OutputCompareMode); + + fn enable_channel(&mut self, channel: Channel, enable: bool); + + fn set_compare_value(&mut self, channel: Channel, value: u16); + + fn get_max_compare_value(&self) -> u16; + } + + pub trait ComplementaryCaptureCompare16bitInstance: CaptureCompare16bitInstance { + fn set_dead_time_clock_division(&mut self, value: vals::Ckd); + + fn set_dead_time_value(&mut self, value: u8); + + fn enable_complementary_channel(&mut self, channel: Channel, enable: bool); + } + + pub trait CaptureCompare32bitInstance: GeneralPurpose32bitInstance { + fn set_output_compare_mode(&mut self, channel: Channel, mode: OutputCompareMode); + + fn enable_channel(&mut self, channel: Channel, enable: bool); + + fn set_compare_value(&mut self, channel: Channel, value: u32); + + fn get_max_compare_value(&self) -> u32; + } } +#[derive(Clone, Copy)] +pub enum Channel { + Ch1, + Ch2, + Ch3, + Ch4, +} + +impl Channel { + pub fn raw(&self) -> usize { + match self { + Channel::Ch1 => 0, + Channel::Ch2 => 1, + Channel::Ch3 => 2, + Channel::Ch4 => 3, + } + } +} + +#[derive(Clone, Copy)] +pub enum OutputCompareMode { + Frozen, + ActiveOnMatch, + InactiveOnMatch, + Toggle, + ForceInactive, + ForceActive, + PwmMode1, + PwmMode2, +} + +impl From for stm32_metapac::timer::vals::Ocm { + fn from(mode: OutputCompareMode) -> Self { + match mode { + OutputCompareMode::Frozen => stm32_metapac::timer::vals::Ocm::FROZEN, + OutputCompareMode::ActiveOnMatch => stm32_metapac::timer::vals::Ocm::ACTIVEONMATCH, + OutputCompareMode::InactiveOnMatch => stm32_metapac::timer::vals::Ocm::INACTIVEONMATCH, + OutputCompareMode::Toggle => stm32_metapac::timer::vals::Ocm::TOGGLE, + OutputCompareMode::ForceInactive => stm32_metapac::timer::vals::Ocm::FORCEINACTIVE, + OutputCompareMode::ForceActive => stm32_metapac::timer::vals::Ocm::FORCEACTIVE, + OutputCompareMode::PwmMode1 => stm32_metapac::timer::vals::Ocm::PWMMODE1, + OutputCompareMode::PwmMode2 => stm32_metapac::timer::vals::Ocm::PWMMODE2, + } + } +} + +pub trait Basic16bitInstance: sealed::Basic16bitInstance + 'static {} + pub trait GeneralPurpose16bitInstance: sealed::GeneralPurpose16bitInstance + 'static {} pub trait GeneralPurpose32bitInstance: sealed::GeneralPurpose32bitInstance + 'static {} pub trait AdvancedControlInstance: sealed::AdvancedControlInstance + 'static {} -pub trait Basic16bitInstance: sealed::Basic16bitInstance + 'static {} +pub trait CaptureCompare16bitInstance: + sealed::CaptureCompare16bitInstance + GeneralPurpose16bitInstance + 'static +{ +} + +pub trait ComplementaryCaptureCompare16bitInstance: + sealed::ComplementaryCaptureCompare16bitInstance + AdvancedControlInstance + 'static +{ +} + +pub trait CaptureCompare32bitInstance: + sealed::CaptureCompare32bitInstance + CaptureCompare16bitInstance + GeneralPurpose32bitInstance + 'static +{ +} + +pin_trait!(Channel1Pin, CaptureCompare16bitInstance); +pin_trait!(Channel1ComplementaryPin, CaptureCompare16bitInstance); +pin_trait!(Channel2Pin, CaptureCompare16bitInstance); +pin_trait!(Channel2ComplementaryPin, CaptureCompare16bitInstance); +pin_trait!(Channel3Pin, CaptureCompare16bitInstance); +pin_trait!(Channel3ComplementaryPin, CaptureCompare16bitInstance); +pin_trait!(Channel4Pin, CaptureCompare16bitInstance); +pin_trait!(Channel4ComplementaryPin, CaptureCompare16bitInstance); +pin_trait!(ExternalTriggerPin, CaptureCompare16bitInstance); +pin_trait!(BreakInputPin, CaptureCompare16bitInstance); +pin_trait!(BreakInputComparator1Pin, CaptureCompare16bitInstance); +pin_trait!(BreakInputComparator2Pin, CaptureCompare16bitInstance); +pin_trait!(BreakInput2Pin, CaptureCompare16bitInstance); +pin_trait!(BreakInput2Comparator1Pin, CaptureCompare16bitInstance); +pin_trait!(BreakInput2Comparator2Pin, CaptureCompare16bitInstance); #[allow(unused)] macro_rules! impl_basic_16bit_timer { @@ -140,33 +251,94 @@ macro_rules! impl_32bit_timer { }; } +#[allow(unused)] +macro_rules! impl_compare_capable_16bit { + ($inst:ident) => { + impl sealed::CaptureCompare16bitInstance for crate::peripherals::$inst { + fn enable_outputs(&mut self, _enable: bool) {} + + fn set_output_compare_mode(&mut self, channel: Channel, mode: OutputCompareMode) { + use sealed::GeneralPurpose16bitInstance; + let r = Self::regs_gp16(); + let raw_channel: usize = channel.raw(); + r.ccmr_output(raw_channel / 2) + .modify(|w| w.set_ocm(raw_channel % 2, mode.into())); + } + + fn enable_channel(&mut self, channel: Channel, enable: bool) { + use sealed::GeneralPurpose16bitInstance; + Self::regs_gp16() + .ccer() + .modify(|w| w.set_cce(channel.raw(), enable)); + } + + fn set_compare_value(&mut self, channel: Channel, value: u16) { + use sealed::GeneralPurpose16bitInstance; + Self::regs_gp16().ccr(channel.raw()).modify(|w| w.set_ccr(value)); + } + + fn get_max_compare_value(&self) -> u16 { + use sealed::GeneralPurpose16bitInstance; + Self::regs_gp16().arr().read().arr() + } + } + }; +} + foreach_interrupt! { ($inst:ident, timer, TIM_BASIC, UP, $irq:ident) => { impl_basic_16bit_timer!($inst, $irq); - - impl Basic16bitInstance for crate::peripherals::$inst { - } + impl Basic16bitInstance for crate::peripherals::$inst {} }; ($inst:ident, timer, TIM_GP16, UP, $irq:ident) => { impl_basic_16bit_timer!($inst, $irq); - - impl Basic16bitInstance for crate::peripherals::$inst { - } + impl_compare_capable_16bit!($inst); + impl Basic16bitInstance for crate::peripherals::$inst {} + impl GeneralPurpose16bitInstance for crate::peripherals::$inst {} + impl CaptureCompare16bitInstance for crate::peripherals::$inst {} impl sealed::GeneralPurpose16bitInstance for crate::peripherals::$inst { fn regs_gp16() -> crate::pac::timer::TimGp16 { crate::pac::$inst } } - - impl GeneralPurpose16bitInstance for crate::peripherals::$inst { - } }; ($inst:ident, timer, TIM_GP32, UP, $irq:ident) => { impl_basic_16bit_timer!($inst, $irq); + impl_32bit_timer!($inst); + impl_compare_capable_16bit!($inst); + impl Basic16bitInstance for crate::peripherals::$inst {} + impl CaptureCompare16bitInstance for crate::peripherals::$inst {} + impl CaptureCompare32bitInstance for crate::peripherals::$inst {} + impl GeneralPurpose16bitInstance for crate::peripherals::$inst {} + impl GeneralPurpose32bitInstance for crate::peripherals::$inst {} - impl Basic16bitInstance for crate::peripherals::$inst { + impl sealed::CaptureCompare32bitInstance for crate::peripherals::$inst { + fn set_output_compare_mode( + &mut self, + channel: Channel, + mode: OutputCompareMode, + ) { + use crate::timer::sealed::GeneralPurpose32bitInstance; + let raw_channel = channel.raw(); + Self::regs_gp32().ccmr_output(raw_channel / 2).modify(|w| w.set_ocm(raw_channel % 2, mode.into())); + } + + fn enable_channel(&mut self, channel: Channel, enable: bool) { + use crate::timer::sealed::GeneralPurpose32bitInstance; + Self::regs_gp32().ccer().modify(|w| w.set_cce(channel.raw(), enable)); + } + + fn set_compare_value(&mut self, channel: Channel, value: u32) { + use crate::timer::sealed::GeneralPurpose32bitInstance; + Self::regs_gp32().ccr(channel.raw()).modify(|w| w.set_ccr(value)); + } + + fn get_max_compare_value(&self) -> u32 { + use crate::timer::sealed::GeneralPurpose32bitInstance; + Self::regs_gp32().arr().read().arr() as u32 + } } impl sealed::GeneralPurpose16bitInstance for crate::peripherals::$inst { @@ -174,21 +346,16 @@ foreach_interrupt! { unsafe { crate::pac::timer::TimGp16::from_ptr(crate::pac::$inst.as_ptr()) } } } - - impl GeneralPurpose16bitInstance for crate::peripherals::$inst { - } - - impl_32bit_timer!($inst); - - impl GeneralPurpose32bitInstance for crate::peripherals::$inst { - } }; ($inst:ident, timer, TIM_ADV, UP, $irq:ident) => { impl_basic_16bit_timer!($inst, $irq); - impl Basic16bitInstance for crate::peripherals::$inst { - } + impl Basic16bitInstance for crate::peripherals::$inst {} + impl GeneralPurpose16bitInstance for crate::peripherals::$inst {} + impl CaptureCompare16bitInstance for crate::peripherals::$inst {} + impl ComplementaryCaptureCompare16bitInstance for crate::peripherals::$inst {} + impl AdvancedControlInstance for crate::peripherals::$inst {} impl sealed::GeneralPurpose16bitInstance for crate::peripherals::$inst { fn regs_gp16() -> crate::pac::timer::TimGp16 { @@ -196,16 +363,70 @@ foreach_interrupt! { } } - impl GeneralPurpose16bitInstance for crate::peripherals::$inst { - } - impl sealed::AdvancedControlInstance for crate::peripherals::$inst { fn regs_advanced() -> crate::pac::timer::TimAdv { crate::pac::$inst } } - impl AdvancedControlInstance for crate::peripherals::$inst { + impl sealed::CaptureCompare16bitInstance for crate::peripherals::$inst { + fn enable_outputs(&mut self, enable: bool) { + use crate::timer::sealed::AdvancedControlInstance; + let r = Self::regs_advanced(); + r.bdtr().modify(|w| w.set_moe(enable)); + } + + fn set_output_compare_mode( + &mut self, + channel: Channel, + mode: OutputCompareMode, + ) { + use crate::timer::sealed::AdvancedControlInstance; + let r = Self::regs_advanced(); + let raw_channel: usize = channel.raw(); + r.ccmr_output(raw_channel / 2) + .modify(|w| w.set_ocm(raw_channel % 2, mode.into())); + } + + fn enable_channel(&mut self, channel: Channel, enable: bool) { + use crate::timer::sealed::AdvancedControlInstance; + Self::regs_advanced() + .ccer() + .modify(|w| w.set_cce(channel.raw(), enable)); + } + + fn set_compare_value(&mut self, channel: Channel, value: u16) { + use crate::timer::sealed::AdvancedControlInstance; + Self::regs_advanced() + .ccr(channel.raw()) + .modify(|w| w.set_ccr(value)); + } + + fn get_max_compare_value(&self) -> u16 { + use crate::timer::sealed::AdvancedControlInstance; + Self::regs_advanced().arr().read().arr() + } } + + impl sealed::ComplementaryCaptureCompare16bitInstance for crate::peripherals::$inst { + fn set_dead_time_clock_division(&mut self, value: vals::Ckd) { + use crate::timer::sealed::AdvancedControlInstance; + Self::regs_advanced().cr1().modify(|w| w.set_ckd(value)); + } + + fn set_dead_time_value(&mut self, value: u8) { + use crate::timer::sealed::AdvancedControlInstance; + Self::regs_advanced().bdtr().modify(|w| w.set_dtg(value)); + } + + fn enable_complementary_channel(&mut self, channel: Channel, enable: bool) { + use crate::timer::sealed::AdvancedControlInstance; + Self::regs_advanced() + .ccer() + .modify(|w| w.set_ccne(channel.raw(), enable)); + } + } + + }; } diff --git a/embassy-stm32/src/pwm/simple_pwm.rs b/embassy-stm32/src/timer/simple_pwm.rs similarity index 100% rename from embassy-stm32/src/pwm/simple_pwm.rs rename to embassy-stm32/src/timer/simple_pwm.rs diff --git a/examples/stm32f4/src/bin/pwm.rs b/examples/stm32f4/src/bin/pwm.rs index 7c590205..4f130c26 100644 --- a/examples/stm32f4/src/bin/pwm.rs +++ b/examples/stm32f4/src/bin/pwm.rs @@ -4,9 +4,9 @@ use defmt::*; use embassy_executor::Spawner; -use embassy_stm32::pwm::simple_pwm::{PwmPin, SimplePwm}; -use embassy_stm32::pwm::Channel; use embassy_stm32::time::khz; +use embassy_stm32::timer::simple_pwm::{PwmPin, SimplePwm}; +use embassy_stm32::timer::Channel; use embassy_time::{Duration, Timer}; use {defmt_rtt as _, panic_probe as _}; diff --git a/examples/stm32f4/src/bin/pwm_complementary.rs b/examples/stm32f4/src/bin/pwm_complementary.rs index a8a68ed6..8cc2a411 100644 --- a/examples/stm32f4/src/bin/pwm_complementary.rs +++ b/examples/stm32f4/src/bin/pwm_complementary.rs @@ -4,10 +4,10 @@ use defmt::*; use embassy_executor::Spawner; -use embassy_stm32::pwm::complementary_pwm::{ComplementaryPwm, ComplementaryPwmPin}; -use embassy_stm32::pwm::simple_pwm::PwmPin; -use embassy_stm32::pwm::Channel; use embassy_stm32::time::khz; +use embassy_stm32::timer::complementary_pwm::{ComplementaryPwm, ComplementaryPwmPin}; +use embassy_stm32::timer::simple_pwm::PwmPin; +use embassy_stm32::timer::Channel; use embassy_time::{Duration, Timer}; use {defmt_rtt as _, panic_probe as _}; diff --git a/examples/stm32g4/src/bin/pwm.rs b/examples/stm32g4/src/bin/pwm.rs index 8f7842ed..b5a9b995 100644 --- a/examples/stm32g4/src/bin/pwm.rs +++ b/examples/stm32g4/src/bin/pwm.rs @@ -4,9 +4,9 @@ use defmt::*; use embassy_executor::Spawner; -use embassy_stm32::pwm::simple_pwm::{PwmPin, SimplePwm}; -use embassy_stm32::pwm::Channel; use embassy_stm32::time::khz; +use embassy_stm32::timer::simple_pwm::{PwmPin, SimplePwm}; +use embassy_stm32::timer::Channel; use embassy_time::{Duration, Timer}; use {defmt_rtt as _, panic_probe as _}; diff --git a/examples/stm32h7/src/bin/low_level_timer_api.rs b/examples/stm32h7/src/bin/low_level_timer_api.rs index d360df08..45b0872b 100644 --- a/examples/stm32h7/src/bin/low_level_timer_api.rs +++ b/examples/stm32h7/src/bin/low_level_timer_api.rs @@ -6,8 +6,8 @@ use defmt::*; use embassy_executor::Spawner; use embassy_stm32::gpio::low_level::AFType; use embassy_stm32::gpio::Speed; -use embassy_stm32::pwm::*; use embassy_stm32::time::{khz, mhz, Hertz}; +use embassy_stm32::timer::*; use embassy_stm32::{into_ref, Config, Peripheral, PeripheralRef}; use embassy_time::{Duration, Timer}; use {defmt_rtt as _, panic_probe as _}; diff --git a/examples/stm32h7/src/bin/pwm.rs b/examples/stm32h7/src/bin/pwm.rs index c5c0dd29..adf2ea9c 100644 --- a/examples/stm32h7/src/bin/pwm.rs +++ b/examples/stm32h7/src/bin/pwm.rs @@ -4,9 +4,9 @@ use defmt::*; use embassy_executor::Spawner; -use embassy_stm32::pwm::simple_pwm::{PwmPin, SimplePwm}; -use embassy_stm32::pwm::Channel; use embassy_stm32::time::{khz, mhz}; +use embassy_stm32::timer::simple_pwm::{PwmPin, SimplePwm}; +use embassy_stm32::timer::Channel; use embassy_stm32::Config; use embassy_time::{Duration, Timer}; use {defmt_rtt as _, panic_probe as _}; From 973b152375d8ace2c247c602d39aea7b01fb636e Mon Sep 17 00:00:00 2001 From: Ruben De Smet Date: Fri, 28 Jul 2023 15:41:45 +0200 Subject: [PATCH 32/68] CI: ethernet and ieee802.15.4 should be able to co-exist --- ci.sh | 2 ++ 1 file changed, 2 insertions(+) diff --git a/ci.sh b/ci.sh index 376cc8f4..19628b50 100755 --- a/ci.sh +++ b/ci.sh @@ -27,6 +27,8 @@ cargo batch \ --- build --release --manifest-path embassy-net/Cargo.toml --target thumbv7em-none-eabi --features defmt,tcp,udp,dns,dhcpv4,medium-ethernet,nightly \ --- build --release --manifest-path embassy-net/Cargo.toml --target thumbv7em-none-eabi --features defmt,tcp,udp,dns,dhcpv4,medium-ethernet,unstable-traits,nightly \ --- build --release --manifest-path embassy-net/Cargo.toml --target thumbv7em-none-eabi --features defmt,tcp,udp,dns,proto-ipv6,medium-ethernet \ + --- build --release --manifest-path embassy-net/Cargo.toml --target thumbv7em-none-eabi --features defmt,tcp,udp,dns,proto-ipv6,medium-ieee802154 \ + --- build --release --manifest-path embassy-net/Cargo.toml --target thumbv7em-none-eabi --features defmt,tcp,udp,dns,proto-ipv6,medium-ethernet,medium-ieee802154 \ --- build --release --manifest-path embassy-net/Cargo.toml --target thumbv7em-none-eabi --features defmt,tcp,udp,dns,proto-ipv6,medium-ethernet,unstable-traits \ --- build --release --manifest-path embassy-net/Cargo.toml --target thumbv7em-none-eabi --features defmt,tcp,udp,dns,proto-ipv6,medium-ethernet,nightly \ --- build --release --manifest-path embassy-net/Cargo.toml --target thumbv7em-none-eabi --features defmt,tcp,udp,dns,proto-ipv6,medium-ethernet,unstable-traits,nightly \ From b57ba84da5f287d7c2d4899c485b2732ff2745a2 Mon Sep 17 00:00:00 2001 From: JuliDi <20155974+JuliDi@users.noreply.github.com> Date: Fri, 28 Jul 2023 16:34:20 +0200 Subject: [PATCH 33/68] add dac-dma example for h7, remove memory.x --- examples/stm32h7/Cargo.toml | 2 +- examples/stm32h7/memory.x | 5 - examples/stm32h7/src/bin/dac_dma.rs | 140 ++++++++++++++++++++++++++++ 3 files changed, 141 insertions(+), 6 deletions(-) delete mode 100644 examples/stm32h7/memory.x create mode 100644 examples/stm32h7/src/bin/dac_dma.rs diff --git a/examples/stm32h7/Cargo.toml b/examples/stm32h7/Cargo.toml index 2d82c0d0..3c1232e6 100644 --- a/examples/stm32h7/Cargo.toml +++ b/examples/stm32h7/Cargo.toml @@ -6,7 +6,7 @@ license = "MIT OR Apache-2.0" [dependencies] # Change stm32h743bi to your chip name, if necessary. -embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["nightly", "defmt", "stm32h743bi", "time-driver-any", "exti", "unstable-pac", "unstable-traits"] } +embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["nightly", "defmt", "stm32h743bi", "time-driver-any", "exti", "memory-x", "unstable-pac", "unstable-traits"] } embassy-sync = { version = "0.2.0", path = "../../embassy-sync", features = ["defmt"] } embassy-executor = { version = "0.2.0", path = "../../embassy-executor", features = ["nightly", "arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] } embassy-time = { version = "0.1.2", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "unstable-traits", "tick-hz-32_768"] } diff --git a/examples/stm32h7/memory.x b/examples/stm32h7/memory.x deleted file mode 100644 index 026b14b9..00000000 --- a/examples/stm32h7/memory.x +++ /dev/null @@ -1,5 +0,0 @@ -MEMORY -{ - FLASH : ORIGIN = 0x8000000, LENGTH = 1024K - RAM : ORIGIN = 0x24000000, LENGTH = 384K -} diff --git a/examples/stm32h7/src/bin/dac_dma.rs b/examples/stm32h7/src/bin/dac_dma.rs new file mode 100644 index 00000000..a9cb5d1e --- /dev/null +++ b/examples/stm32h7/src/bin/dac_dma.rs @@ -0,0 +1,140 @@ +#![no_std] +#![no_main] +#![feature(type_alias_impl_trait)] + +use defmt::*; +use embassy_executor::Spawner; +use embassy_stm32::dac::{DacChannel, ValueArray}; +use embassy_stm32::pac::timer::vals::{Mms, Opm}; +use embassy_stm32::peripherals::{TIM6, TIM7}; +use embassy_stm32::rcc::low_level::RccPeripheral; +use embassy_stm32::time::{mhz, Hertz}; +use embassy_stm32::timer::low_level::Basic16bitInstance; +use micromath::F32Ext; +use {defmt_rtt as _, panic_probe as _}; + +pub type Dac1Type = + embassy_stm32::dac::DacCh1<'static, embassy_stm32::peripherals::DAC1, embassy_stm32::peripherals::DMA1_CH3>; + +pub type Dac2Type = + embassy_stm32::dac::DacCh2<'static, embassy_stm32::peripherals::DAC1, embassy_stm32::peripherals::DMA1_CH4>; + +#[embassy_executor::main] +async fn main(spawner: Spawner) { + let mut config = embassy_stm32::Config::default(); + config.rcc.sys_ck = Some(mhz(400)); + config.rcc.hclk = Some(mhz(100)); + config.rcc.pll1.q_ck = Some(mhz(100)); + + // Initialize the board and obtain a Peripherals instance + let p: embassy_stm32::Peripherals = embassy_stm32::init(config); + + // Obtain two independent channels (p.DAC1 can only be consumed once, though!) + let (dac_ch1, dac_ch2) = embassy_stm32::dac::Dac::new(p.DAC1, p.DMA1_CH3, p.DMA1_CH4, p.PA4, p.PA5).split(); + + spawner.spawn(dac_task1(dac_ch1)).ok(); + spawner.spawn(dac_task2(dac_ch2)).ok(); +} + +#[embassy_executor::task] +async fn dac_task1(mut dac: Dac1Type) { + let data: &[u8; 256] = &calculate_array::<256>(); + + info!("TIM6 frequency is {}", TIM6::frequency()); + const FREQUENCY: Hertz = Hertz::hz(200); + + // Compute the reload value such that we obtain the FREQUENCY for the sine + let reload: u32 = (TIM6::frequency().0 / FREQUENCY.0) / data.len() as u32; + + // Depends on your clock and on the specific chip used, you may need higher or lower values here + if reload < 10 { + error!("Reload value {} below threshold!", reload); + } + + dac.select_trigger(embassy_stm32::dac::Ch1Trigger::Tim6).unwrap(); + dac.enable_channel().unwrap(); + + TIM6::enable(); + TIM6::regs().arr().modify(|w| w.set_arr(reload as u16 - 1)); + TIM6::regs().cr2().modify(|w| w.set_mms(Mms::UPDATE)); + TIM6::regs().cr1().modify(|w| { + w.set_opm(Opm::DISABLED); + w.set_cen(true); + }); + + debug!( + "TIM6 Frequency {}, Target Frequency {}, Reload {}, Reload as u16 {}, Samples {}", + TIM6::frequency(), + FREQUENCY, + reload, + reload as u16, + data.len() + ); + + // Loop technically not necessary if DMA circular mode is enabled + loop { + info!("Loop DAC1"); + if let Err(e) = dac.write(ValueArray::Bit8(data), true).await { + error!("Could not write to dac: {}", e); + } + } +} + +#[embassy_executor::task] +async fn dac_task2(mut dac: Dac2Type) { + let data: &[u8; 256] = &calculate_array::<256>(); + + info!("TIM7 frequency is {}", TIM7::frequency()); + + const FREQUENCY: Hertz = Hertz::hz(600); + let reload: u32 = (TIM7::frequency().0 / FREQUENCY.0) / data.len() as u32; + + if reload < 10 { + error!("Reload value {} below threshold!", reload); + } + + TIM7::enable(); + TIM7::regs().arr().modify(|w| w.set_arr(reload as u16 - 1)); + TIM7::regs().cr2().modify(|w| w.set_mms(Mms::UPDATE)); + TIM7::regs().cr1().modify(|w| { + w.set_opm(Opm::DISABLED); + w.set_cen(true); + }); + + dac.select_trigger(embassy_stm32::dac::Ch2Trigger::Tim7).unwrap(); + + debug!( + "TIM7 Frequency {}, Target Frequency {}, Reload {}, Reload as u16 {}, Samples {}", + TIM7::frequency(), + FREQUENCY, + reload, + reload as u16, + data.len() + ); + + if let Err(e) = dac.write(ValueArray::Bit8(data), true).await { + error!("Could not write to dac: {}", e); + } +} + +fn to_sine_wave(v: u8) -> u8 { + if v >= 128 { + // top half + let r = 3.14 * ((v - 128) as f32 / 128.0); + (r.sin() * 128.0 + 127.0) as u8 + } else { + // bottom half + let r = 3.14 + 3.14 * (v as f32 / 128.0); + (r.sin() * 128.0 + 127.0) as u8 + } +} + +fn calculate_array() -> [u8; N] { + let mut res = [0; N]; + let mut i = 0; + while i < N { + res[i] = to_sine_wave(i as u8); + i += 1; + } + res +} From 937a63ce28beee87ae78756ecf8377f465b8cf9d Mon Sep 17 00:00:00 2001 From: JuliDi <20155974+JuliDi@users.noreply.github.com> Date: Fri, 28 Jul 2023 16:38:02 +0200 Subject: [PATCH 34/68] remove memory.x files for other stm32 examples --- examples/stm32f7/Cargo.toml | 2 +- examples/stm32f7/build.rs | 40 +---------------------------- examples/stm32f7/memory.x | 12 --------- examples/stm32h5/Cargo.toml | 2 +- examples/stm32h5/memory.x | 5 ---- examples/stm32h7/.cargo/config.toml | 2 +- examples/stm32l4/Cargo.toml | 2 +- examples/stm32l4/build.rs | 30 ---------------------- examples/stm32l4/memory.x | 7 ----- 9 files changed, 5 insertions(+), 97 deletions(-) delete mode 100644 examples/stm32f7/memory.x delete mode 100644 examples/stm32h5/memory.x delete mode 100644 examples/stm32l4/memory.x diff --git a/examples/stm32f7/Cargo.toml b/examples/stm32f7/Cargo.toml index a6964c7b..a379cbbe 100644 --- a/examples/stm32f7/Cargo.toml +++ b/examples/stm32f7/Cargo.toml @@ -6,7 +6,7 @@ license = "MIT OR Apache-2.0" [dependencies] # Change stm32f767zi to your chip name, if necessary. -embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["nightly", "defmt", "stm32f767zi", "unstable-pac", "time-driver-any", "exti"] } +embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["nightly", "defmt", "stm32f767zi", "memory-x", "unstable-pac", "time-driver-any", "exti"] } embassy-sync = { version = "0.2.0", path = "../../embassy-sync", features = ["defmt"] } embassy-executor = { version = "0.2.0", path = "../../embassy-executor", features = ["nightly", "arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] } embassy-time = { version = "0.1.2", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } diff --git a/examples/stm32f7/build.rs b/examples/stm32f7/build.rs index 2b5d412a..8cd32d7e 100644 --- a/examples/stm32f7/build.rs +++ b/examples/stm32f7/build.rs @@ -1,43 +1,5 @@ -//! adapted from https://github.com/stm32-rs/stm32f7xx-hal/blob/master/build.rs -use std::fs::File; -use std::io::prelude::*; -use std::path::PathBuf; -use std::{env, io}; - -#[derive(Debug)] -enum Error { - Env(env::VarError), - Io(io::Error), -} - -impl From for Error { - fn from(error: env::VarError) -> Self { - Self::Env(error) - } -} - -impl From for Error { - fn from(error: io::Error) -> Self { - Self::Io(error) - } -} - -fn main() -> Result<(), Error> { - println!("cargo:rerun-if-changed=build.rs"); - println!("cargo:rerun-if-changed=memory.x"); - - let out_dir = env::var("OUT_DIR")?; - let out_dir = PathBuf::from(out_dir); - - let memory_x = include_bytes!("memory.x").as_ref(); - File::create(out_dir.join("memory.x"))?.write_all(memory_x)?; - - // Tell Cargo where to find the file. - println!("cargo:rustc-link-search={}", out_dir.display()); - +fn main() { println!("cargo:rustc-link-arg-bins=--nmagic"); println!("cargo:rustc-link-arg-bins=-Tlink.x"); println!("cargo:rustc-link-arg-bins=-Tdefmt.x"); - - Ok(()) } diff --git a/examples/stm32f7/memory.x b/examples/stm32f7/memory.x deleted file mode 100644 index 899f7a4b..00000000 --- a/examples/stm32f7/memory.x +++ /dev/null @@ -1,12 +0,0 @@ -/* For STM32F765,767,768,769,777,778,779 devices */ -MEMORY -{ - /* NOTE K = KiBi = 1024 bytes */ - FLASH : ORIGIN = 0x08000000, LENGTH = 2M - RAM : ORIGIN = 0x20000000, LENGTH = 368K + 16K -} - -/* This is where the call stack will be allocated. */ -/* The stack is of the full descending type. */ -/* NOTE Do NOT modify `_stack_start` unless you know what you are doing */ -_stack_start = ORIGIN(RAM) + LENGTH(RAM); diff --git a/examples/stm32h5/Cargo.toml b/examples/stm32h5/Cargo.toml index aebc795c..51d3bad1 100644 --- a/examples/stm32h5/Cargo.toml +++ b/examples/stm32h5/Cargo.toml @@ -6,7 +6,7 @@ license = "MIT OR Apache-2.0" [dependencies] # Change stm32h563zi to your chip name, if necessary. -embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["nightly", "defmt", "stm32h563zi", "time-driver-any", "exti", "unstable-pac", "unstable-traits"] } +embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["nightly", "defmt", "stm32h563zi", "memory-x", "time-driver-any", "exti", "unstable-pac", "unstable-traits"] } embassy-sync = { version = "0.2.0", path = "../../embassy-sync", features = ["defmt"] } embassy-executor = { version = "0.2.0", path = "../../embassy-executor", features = ["nightly", "arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] } embassy-time = { version = "0.1.2", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "unstable-traits", "tick-hz-32_768"] } diff --git a/examples/stm32h5/memory.x b/examples/stm32h5/memory.x deleted file mode 100644 index 45606150..00000000 --- a/examples/stm32h5/memory.x +++ /dev/null @@ -1,5 +0,0 @@ -MEMORY -{ - FLASH : ORIGIN = 0x08000000, LENGTH = 0x200000 - RAM : ORIGIN = 0x20000000, LENGTH = 0x50000 -} diff --git a/examples/stm32h7/.cargo/config.toml b/examples/stm32h7/.cargo/config.toml index 5f680dbc..4160bf85 100644 --- a/examples/stm32h7/.cargo/config.toml +++ b/examples/stm32h7/.cargo/config.toml @@ -1,5 +1,5 @@ [target.thumbv7em-none-eabihf] -runner = 'probe-rs run --chip STM32H743ZITx' +runner = 'probe-rs run --chip STM32H7A3ZITxQ' [build] target = "thumbv7em-none-eabihf" # Cortex-M4F and Cortex-M7F (with FPU) diff --git a/examples/stm32l4/Cargo.toml b/examples/stm32l4/Cargo.toml index 0f770e2f..3b27d8e8 100644 --- a/examples/stm32l4/Cargo.toml +++ b/examples/stm32l4/Cargo.toml @@ -6,7 +6,7 @@ license = "MIT OR Apache-2.0" [dependencies] # Change stm32l4s5vi to your chip name, if necessary. -embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["nightly", "defmt", "unstable-pac", "stm32l4s5vi", "time-driver-any", "exti", "unstable-traits", "chrono"] } +embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["nightly", "defmt", "unstable-pac", "stm32l4s5vi", "memory-x", "time-driver-any", "exti", "unstable-traits", "chrono"] } embassy-sync = { version = "0.2.0", path = "../../embassy-sync", features = ["defmt"] } embassy-executor = { version = "0.2.0", path = "../../embassy-executor", features = ["nightly", "arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] } embassy-time = { version = "0.1.2", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } diff --git a/examples/stm32l4/build.rs b/examples/stm32l4/build.rs index 30691aa9..8cd32d7e 100644 --- a/examples/stm32l4/build.rs +++ b/examples/stm32l4/build.rs @@ -1,34 +1,4 @@ -//! This build script copies the `memory.x` file from the crate root into -//! a directory where the linker can always find it at build time. -//! For many projects this is optional, as the linker always searches the -//! project root directory -- wherever `Cargo.toml` is. However, if you -//! are using a workspace or have a more complicated build setup, this -//! build script becomes required. Additionally, by requesting that -//! Cargo re-run the build script whenever `memory.x` is changed, -//! updating `memory.x` ensures a rebuild of the application with the -//! new memory settings. - -use std::env; -use std::fs::File; -use std::io::Write; -use std::path::PathBuf; - fn main() { - // Put `memory.x` in our output directory and ensure it's - // on the linker search path. - let out = &PathBuf::from(env::var_os("OUT_DIR").unwrap()); - File::create(out.join("memory.x")) - .unwrap() - .write_all(include_bytes!("memory.x")) - .unwrap(); - println!("cargo:rustc-link-search={}", out.display()); - - // By default, Cargo will re-run a build script whenever - // any file in the project changes. By specifying `memory.x` - // here, we ensure the build script is only re-run when - // `memory.x` is changed. - println!("cargo:rerun-if-changed=memory.x"); - println!("cargo:rustc-link-arg-bins=--nmagic"); println!("cargo:rustc-link-arg-bins=-Tlink.x"); println!("cargo:rustc-link-arg-bins=-Tdefmt.x"); diff --git a/examples/stm32l4/memory.x b/examples/stm32l4/memory.x deleted file mode 100644 index eb87d1b5..00000000 --- a/examples/stm32l4/memory.x +++ /dev/null @@ -1,7 +0,0 @@ -MEMORY -{ - /* NOTE 1 K = 1 KiBi = 1024 bytes */ - /* These values correspond to the STM32L4S5 */ - FLASH : ORIGIN = 0x08000000, LENGTH = 1024K - RAM : ORIGIN = 0x20000000, LENGTH = 128K -} From 6dd2fc59418c9d96f049f184694ddaf4845a4425 Mon Sep 17 00:00:00 2001 From: JuliDi <20155974+JuliDi@users.noreply.github.com> Date: Fri, 28 Jul 2023 16:59:13 +0200 Subject: [PATCH 35/68] add document-features --- embassy-stm32/Cargo.toml | 54 +++++++++++++++++++++++++++++----------- embassy-stm32/src/lib.rs | 3 +++ 2 files changed, 42 insertions(+), 15 deletions(-) diff --git a/embassy-stm32/Cargo.toml b/embassy-stm32/Cargo.toml index 6f34c741..ba279f79 100644 --- a/embassy-stm32/Cargo.toml +++ b/embassy-stm32/Cargo.toml @@ -67,6 +67,7 @@ cfg-if = "1.0.0" embedded-io = { version = "0.4.0", features = ["async"], optional = true } chrono = { version = "^0.4", default-features = false, optional = true} bit_field = "0.10.2" +document-features = "0.2.7" [dev-dependencies] critical-section = { version = "1.1", features = ["std"] } @@ -78,40 +79,63 @@ stm32-metapac = { version = "13", default-features = false, features = ["metadat [features] default = ["rt"] + +## Enable `stm32-metapac`'s `rt` feature rt = ["stm32-metapac/rt"] +## Use [`defmt`](https://docs.rs/defmt/latest/defmt/) for logging defmt = ["dep:defmt", "bxcan/unstable-defmt", "embassy-sync/defmt", "embassy-embedded-hal/defmt", "embassy-hal-internal/defmt", "embedded-io?/defmt", "embassy-usb-driver?/defmt", "embassy-net-driver/defmt"] -memory-x = ["stm32-metapac/memory-x"] + exti = [] -# Enables additional driver features that depend on embassy-time +## Automatically generate `memory.x` file using [`stm32-metapac`](https://docs.rs/stm32-metapac/) +memory-x = ["stm32-metapac/memory-x"] + +## Enable nightly-only features +nightly = ["embedded-hal-1", "embedded-hal-async", "embedded-storage-async", "dep:embedded-io", "dep:embassy-usb-driver", "embassy-embedded-hal/nightly"] + +## Re-export stm32-metapac at `embassy_stm32::pac`. +## This is unstable because semver-minor (non-breaking) releases of embassy-stm32 may major-bump (breaking) the stm32-metapac version. +## If this is an issue for you, you're encouraged to directly depend on a fixed version of the PAC. +## There are no plans to make this stable. +unstable-pac = [] + +## Implement embedded-hal 1.0 alpha traits. +## Implement embedded-hal-async traits if `nightly` is set as well. +unstable-traits = ["embedded-hal-1", "dep:embedded-hal-nb"] + +#! ## Time + +## Enables additional driver features that depend on embassy-time time = ["dep:embassy-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 = ["time"] + +## Use any time driver time-driver-any = ["_time-driver"] +## Use TIM2 as time driver time-driver-tim2 = ["_time-driver"] +## Use TIM3 as time driver time-driver-tim3 = ["_time-driver"] +## Use TIM4 as time driver time-driver-tim4 = ["_time-driver"] +## Use TIM5 as time driver time-driver-tim5 = ["_time-driver"] +## Use TIM12 as time driver time-driver-tim12 = ["_time-driver"] +## Use TIM15 as time driver time-driver-tim15 = ["_time-driver"] -# Enable nightly-only features -nightly = ["embedded-hal-1", "embedded-hal-async", "embedded-storage-async", "dep:embedded-io", "dep:embassy-usb-driver", "embassy-embedded-hal/nightly"] -# Reexport stm32-metapac at `embassy_stm32::pac`. -# This is unstable because semver-minor (non-breaking) releases of embassy-stm32 may major-bump (breaking) the stm32-metapac version. -# If this is an issue for you, you're encouraged to directly depend on a fixed version of the PAC. -# There are no plans to make this stable. -unstable-pac = [] +#! ## Chip-selection features +#! Select your chip by specifying the model as a feature, e.g. `stm32c011d6`. +#! Check the `Cargo.toml` for the latest list of supported chips. +#! +#! **Important:** Do not forget to adapt the target chip in your toolchain, +#! e.g. in `.cargo/config.toml`. -# Implement embedded-hal 1.0 alpha traits. -# Implement embedded-hal-async traits if `nightly` is set as well. -unstable-traits = ["embedded-hal-1", "dep:embedded-hal-nb"] - -# Chip-selection features stm32c011d6 = [ "stm32-metapac/stm32c011d6" ] stm32c011f4 = [ "stm32-metapac/stm32c011f4" ] stm32c011f6 = [ "stm32-metapac/stm32c011f6" ] @@ -1445,4 +1469,4 @@ stm32wle5cb = [ "stm32-metapac/stm32wle5cb" ] stm32wle5cc = [ "stm32-metapac/stm32wle5cc" ] stm32wle5j8 = [ "stm32-metapac/stm32wle5j8" ] stm32wle5jb = [ "stm32-metapac/stm32wle5jb" ] -stm32wle5jc = [ "stm32-metapac/stm32wle5jc" ] \ No newline at end of file +stm32wle5jc = [ "stm32-metapac/stm32wle5jc" ] diff --git a/embassy-stm32/src/lib.rs b/embassy-stm32/src/lib.rs index bb2ef2fc..9e67596b 100644 --- a/embassy-stm32/src/lib.rs +++ b/embassy-stm32/src/lib.rs @@ -1,6 +1,9 @@ #![cfg_attr(not(test), no_std)] #![cfg_attr(feature = "nightly", feature(async_fn_in_trait, impl_trait_projections))] +//! ## Feature flags +#![doc = document_features::document_features!(feature_label = r#"{feature}"#)] + // This must go FIRST so that all the other modules see its macros. pub mod fmt; include!(concat!(env!("OUT_DIR"), "/_macros.rs")); From 5a8704b4d8820fe629e9c9d586eb78d165b5f476 Mon Sep 17 00:00:00 2001 From: Matt Ickstadt Date: Fri, 28 Jul 2023 11:16:43 -0500 Subject: [PATCH 36/68] TimeoutI2c: allow ref to live shorter than peripheral --- embassy-stm32/src/i2c/timeout.rs | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/embassy-stm32/src/i2c/timeout.rs b/embassy-stm32/src/i2c/timeout.rs index 939e2750..8dc228b3 100644 --- a/embassy-stm32/src/i2c/timeout.rs +++ b/embassy-stm32/src/i2c/timeout.rs @@ -6,8 +6,8 @@ use super::{Error, I2c, Instance}; /// /// This is useful for recovering from a shorted bus or a device stuck in a clock stretching state. /// A regular [I2c] would freeze until condition is removed. -pub struct TimeoutI2c<'d, T: Instance, TXDMA, RXDMA> { - i2c: &'d mut I2c<'d, T, TXDMA, RXDMA>, +pub struct TimeoutI2c<'a, 'd: 'a, T: Instance, TXDMA, RXDMA> { + i2c: &'a mut I2c<'d, T, TXDMA, RXDMA>, timeout: Duration, } @@ -22,8 +22,8 @@ fn timeout_fn(timeout: Duration) -> impl Fn() -> Result<(), Error> { } } -impl<'d, T: Instance, TXDMA, RXDMA> TimeoutI2c<'d, T, TXDMA, RXDMA> { - pub fn new(i2c: &'d mut I2c<'d, T, TXDMA, RXDMA>, timeout: Duration) -> Self { +impl<'a, 'd, T: Instance, TXDMA, RXDMA> TimeoutI2c<'a, 'd, T, TXDMA, RXDMA> { + pub fn new(i2c: &'a mut I2c<'d, T, TXDMA, RXDMA>, timeout: Duration) -> Self { Self { i2c, timeout } } @@ -65,7 +65,7 @@ impl<'d, T: Instance, TXDMA, RXDMA> TimeoutI2c<'d, T, TXDMA, RXDMA> { } } -impl<'d, T: Instance, TXDMA, RXDMA> embedded_hal_02::blocking::i2c::Read for TimeoutI2c<'d, T, TXDMA, RXDMA> { +impl<'a, 'd, T: Instance, TXDMA, RXDMA> embedded_hal_02::blocking::i2c::Read for TimeoutI2c<'a, 'd, T, TXDMA, RXDMA> { type Error = Error; fn read(&mut self, addr: u8, read: &mut [u8]) -> Result<(), Self::Error> { @@ -73,7 +73,7 @@ impl<'d, T: Instance, TXDMA, RXDMA> embedded_hal_02::blocking::i2c::Read for Tim } } -impl<'d, T: Instance, TXDMA, RXDMA> embedded_hal_02::blocking::i2c::Write for TimeoutI2c<'d, T, TXDMA, RXDMA> { +impl<'a, 'd, T: Instance, TXDMA, RXDMA> embedded_hal_02::blocking::i2c::Write for TimeoutI2c<'a, 'd, T, TXDMA, RXDMA> { type Error = Error; fn write(&mut self, addr: u8, write: &[u8]) -> Result<(), Self::Error> { @@ -81,7 +81,9 @@ impl<'d, T: Instance, TXDMA, RXDMA> embedded_hal_02::blocking::i2c::Write for Ti } } -impl<'d, T: Instance, TXDMA, RXDMA> embedded_hal_02::blocking::i2c::WriteRead for TimeoutI2c<'d, T, TXDMA, RXDMA> { +impl<'a, 'd, T: Instance, TXDMA, RXDMA> embedded_hal_02::blocking::i2c::WriteRead + for TimeoutI2c<'a, 'd, T, TXDMA, RXDMA> +{ type Error = Error; fn write_read(&mut self, addr: u8, write: &[u8], read: &mut [u8]) -> Result<(), Self::Error> { @@ -93,11 +95,11 @@ impl<'d, T: Instance, TXDMA, RXDMA> embedded_hal_02::blocking::i2c::WriteRead fo mod eh1 { use super::*; - impl<'d, T: Instance, TXDMA, RXDMA> embedded_hal_1::i2c::ErrorType for TimeoutI2c<'d, T, TXDMA, RXDMA> { + impl<'a, 'd, T: Instance, TXDMA, RXDMA> embedded_hal_1::i2c::ErrorType for TimeoutI2c<'a, 'd, T, TXDMA, RXDMA> { type Error = Error; } - impl<'d, T: Instance, TXDMA, RXDMA> embedded_hal_1::i2c::I2c for TimeoutI2c<'d, T, TXDMA, RXDMA> { + impl<'a, 'd, T: Instance, TXDMA, RXDMA> embedded_hal_1::i2c::I2c for TimeoutI2c<'a, 'd, T, TXDMA, RXDMA> { fn read(&mut self, address: u8, read: &mut [u8]) -> Result<(), Self::Error> { self.blocking_read(address, read) } From cbc8871a0bb40eb5fec82e7c27ed4c0127844c3c Mon Sep 17 00:00:00 2001 From: pennae Date: Fri, 28 Jul 2023 18:45:57 +0200 Subject: [PATCH 37/68] rp: relocate programs implicitly during load this removed the RelocatedProgram construction step from pio uses. there's not all that much to be said for the extra step because the origin can be set on the input program itself, and the remaining information exposed by RelocatedProgram can be exposed from LoadedProgram instead (even though it's already available on the pio_asm programs, albeit perhaps less convenient). we do lose access to the relocated instruction iterator, but we also cannot think of anything this iterator would actually be useful for outside of program loading. --- cyw43-pio/src/lib.rs | 8 +- embassy-rp/src/lib.rs | 2 +- embassy-rp/src/pio.rs | 60 ++++++++++++-- embassy-rp/src/relocate.rs | 5 -- examples/rp/src/bin/pio_async.rs | 10 +-- examples/rp/src/bin/pio_dma.rs | 4 +- examples/rp/src/bin/pio_hd44780.rs | 7 +- examples/rp/src/bin/pio_uart.rs | 41 +++------- examples/rp/src/bin/pio_ws2812.rs | 4 +- tests/rp/src/bin/pio_irq.rs | 4 +- tests/rp/src/bin/pio_multi_load.rs | 126 +++++++++++++++++++++++++++++ 11 files changed, 200 insertions(+), 71 deletions(-) create mode 100644 tests/rp/src/bin/pio_multi_load.rs diff --git a/cyw43-pio/src/lib.rs b/cyw43-pio/src/lib.rs index dca30c74..830a5b44 100644 --- a/cyw43-pio/src/lib.rs +++ b/cyw43-pio/src/lib.rs @@ -8,7 +8,6 @@ use cyw43::SpiBusCyw43; use embassy_rp::dma::Channel; use embassy_rp::gpio::{Drive, Level, Output, Pin, Pull, SlewRate}; use embassy_rp::pio::{Common, Config, Direction, Instance, Irq, PioPin, ShiftDirection, StateMachine}; -use embassy_rp::relocate::RelocatedProgram; use embassy_rp::{pio_instr_util, Peripheral, PeripheralRef}; use fixed::FixedU32; use pio_proc::pio_asm; @@ -88,8 +87,6 @@ where ".wrap" ); - let relocated = RelocatedProgram::new(&program.program); - let mut pin_io: embassy_rp::pio::Pin = common.make_pio_pin(dio); pin_io.set_pull(Pull::None); pin_io.set_schmitt(true); @@ -102,7 +99,8 @@ where pin_clk.set_slew_rate(SlewRate::Fast); let mut cfg = Config::default(); - cfg.use_program(&common.load_program(&relocated), &[&pin_clk]); + let loaded_program = common.load_program(&program.program); + cfg.use_program(&loaded_program, &[&pin_clk]); cfg.set_out_pins(&[&pin_io]); cfg.set_in_pins(&[&pin_io]); cfg.set_set_pins(&[&pin_io]); @@ -142,7 +140,7 @@ where sm, irq, dma: dma.into_ref(), - wrap_target: relocated.wrap().target, + wrap_target: loaded_program.wrap.target, } } diff --git a/embassy-rp/src/lib.rs b/embassy-rp/src/lib.rs index ebec9fec..45156458 100644 --- a/embassy-rp/src/lib.rs +++ b/embassy-rp/src/lib.rs @@ -33,7 +33,7 @@ pub mod watchdog; // TODO: move `pio_instr_util` and `relocate` to inside `pio` pub mod pio; pub mod pio_instr_util; -pub mod relocate; +pub(crate) mod relocate; // Reexports pub use embassy_hal_internal::{into_ref, Peripheral, PeripheralRef}; diff --git a/embassy-rp/src/pio.rs b/embassy-rp/src/pio.rs index 464988b2..3de398af 100644 --- a/embassy-rp/src/pio.rs +++ b/embassy-rp/src/pio.rs @@ -11,7 +11,7 @@ use fixed::types::extra::U8; use fixed::FixedU32; use pac::io::vals::Gpio0ctrlFuncsel; use pac::pio::vals::SmExecctrlStatusSel; -use pio::{SideSet, Wrap}; +use pio::{Program, SideSet, Wrap}; use crate::dma::{Channel, Transfer, Word}; use crate::gpio::sealed::Pin as SealedPin; @@ -734,23 +734,67 @@ pub struct InstanceMemory<'d, PIO: Instance> { pub struct LoadedProgram<'d, PIO: Instance> { pub used_memory: InstanceMemory<'d, PIO>, - origin: u8, - wrap: Wrap, - side_set: SideSet, + pub origin: u8, + pub wrap: Wrap, + pub side_set: SideSet, +} + +#[derive(Clone, Copy, PartialEq, Eq, Debug)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +pub enum LoadError { + /// Insufficient consecutive free instruction space to load program. + InsufficientSpace, + /// Loading the program would overwrite an instruction address already + /// used by another program. + AddressInUse(usize), } impl<'d, PIO: Instance> Common<'d, PIO> { - pub fn load_program(&mut self, prog: &RelocatedProgram) -> LoadedProgram<'d, PIO> { + /// Load a PIO program. This will automatically relocate the program to + /// an available chunk of free instruction memory if the program origin + /// was not explicitly specified, otherwise it will attempt to load the + /// program only at its origin. + pub fn load_program(&mut self, prog: &Program) -> LoadedProgram<'d, PIO> { match self.try_load_program(prog) { Ok(r) => r, - Err(at) => panic!("Trying to write already used PIO instruction memory at {}", at), + Err(e) => panic!("Failed to load PIO program: {:?}", e), } } + /// Load a PIO program. This will automatically relocate the program to + /// an available chunk of free instruction memory if the program origin + /// was not explicitly specified, otherwise it will attempt to load the + /// program only at its origin. pub fn try_load_program( &mut self, - prog: &RelocatedProgram, + prog: &Program, + ) -> Result, LoadError> { + match prog.origin { + Some(origin) => self + .try_load_program_at(prog, origin) + .map_err(|a| LoadError::AddressInUse(a)), + None => { + // naively search for free space, allowing wraparound since + // PIO does support that. with only 32 instruction slots it + // doesn't make much sense to do anything more fancy. + let mut origin = 0; + while origin < 32 { + match self.try_load_program_at(prog, origin as _) { + Ok(r) => return Ok(r), + Err(a) => origin = a + 1, + } + } + Err(LoadError::InsufficientSpace) + } + } + } + + fn try_load_program_at( + &mut self, + prog: &Program, + origin: u8, ) -> Result, usize> { + let prog = RelocatedProgram::new_with_origin(prog, origin); let used_memory = self.try_write_instr(prog.origin() as _, prog.code())?; Ok(LoadedProgram { used_memory, @@ -760,7 +804,7 @@ impl<'d, PIO: Instance> Common<'d, PIO> { }) } - pub fn try_write_instr(&mut self, start: usize, instrs: I) -> Result, usize> + fn try_write_instr(&mut self, start: usize, instrs: I) -> Result, usize> where I: Iterator, { diff --git a/embassy-rp/src/relocate.rs b/embassy-rp/src/relocate.rs index 9cb279cc..b35b4ed7 100644 --- a/embassy-rp/src/relocate.rs +++ b/embassy-rp/src/relocate.rs @@ -41,11 +41,6 @@ pub struct RelocatedProgram<'a, const PROGRAM_SIZE: usize> { } impl<'a, const PROGRAM_SIZE: usize> RelocatedProgram<'a, PROGRAM_SIZE> { - pub fn new(program: &Program) -> RelocatedProgram { - let origin = program.origin.unwrap_or(0); - RelocatedProgram { program, origin } - } - pub fn new_with_origin(program: &Program, origin: u8) -> RelocatedProgram { RelocatedProgram { program, origin } } diff --git a/examples/rp/src/bin/pio_async.rs b/examples/rp/src/bin/pio_async.rs index c001d644..a6d6144b 100644 --- a/examples/rp/src/bin/pio_async.rs +++ b/examples/rp/src/bin/pio_async.rs @@ -8,7 +8,6 @@ use embassy_executor::Spawner; use embassy_rp::bind_interrupts; use embassy_rp::peripherals::PIO0; use embassy_rp::pio::{Common, Config, InterruptHandler, Irq, Pio, PioPin, ShiftDirection, StateMachine}; -use embassy_rp::relocate::RelocatedProgram; use fixed::traits::ToFixed; use fixed_macro::types::U56F8; use {defmt_rtt as _, panic_probe as _}; @@ -29,9 +28,8 @@ fn setup_pio_task_sm0<'a>(pio: &mut Common<'a, PIO0>, sm: &mut StateMachine<'a, ".wrap", ); - let relocated = RelocatedProgram::new(&prg.program); let mut cfg = Config::default(); - cfg.use_program(&pio.load_program(&relocated), &[]); + cfg.use_program(&pio.load_program(&prg.program), &[]); let out_pin = pio.make_pio_pin(pin); cfg.set_out_pins(&[&out_pin]); cfg.set_set_pins(&[&out_pin]); @@ -65,9 +63,8 @@ fn setup_pio_task_sm1<'a>(pio: &mut Common<'a, PIO0>, sm: &mut StateMachine<'a, ".wrap", ); - let relocated = RelocatedProgram::new(&prg.program); let mut cfg = Config::default(); - cfg.use_program(&pio.load_program(&relocated), &[]); + cfg.use_program(&pio.load_program(&prg.program), &[]); cfg.clock_divider = (U56F8!(125_000_000) / 2000).to_fixed(); cfg.shift_in.auto_fill = true; cfg.shift_in.direction = ShiftDirection::Right; @@ -96,9 +93,8 @@ fn setup_pio_task_sm2<'a>(pio: &mut Common<'a, PIO0>, sm: &mut StateMachine<'a, "irq 3 [15]", ".wrap", ); - let relocated = RelocatedProgram::new(&prg.program); let mut cfg = Config::default(); - cfg.use_program(&pio.load_program(&relocated), &[]); + cfg.use_program(&pio.load_program(&prg.program), &[]); cfg.clock_divider = (U56F8!(125_000_000) / 2000).to_fixed(); sm.set_config(&cfg); } diff --git a/examples/rp/src/bin/pio_dma.rs b/examples/rp/src/bin/pio_dma.rs index 9ab72e1f..86e5017a 100644 --- a/examples/rp/src/bin/pio_dma.rs +++ b/examples/rp/src/bin/pio_dma.rs @@ -8,7 +8,6 @@ use embassy_executor::Spawner; use embassy_futures::join::join; use embassy_rp::peripherals::PIO0; use embassy_rp::pio::{Config, InterruptHandler, Pio, ShiftConfig, ShiftDirection}; -use embassy_rp::relocate::RelocatedProgram; use embassy_rp::{bind_interrupts, Peripheral}; use fixed::traits::ToFixed; use fixed_macro::types::U56F8; @@ -46,9 +45,8 @@ async fn main(_spawner: Spawner) { ".wrap", ); - let relocated = RelocatedProgram::new(&prg.program); let mut cfg = Config::default(); - cfg.use_program(&common.load_program(&relocated), &[]); + cfg.use_program(&common.load_program(&prg.program), &[]); cfg.clock_divider = (U56F8!(125_000_000) / U56F8!(10_000)).to_fixed(); cfg.shift_in = ShiftConfig { auto_fill: true, diff --git a/examples/rp/src/bin/pio_hd44780.rs b/examples/rp/src/bin/pio_hd44780.rs index 8aedd24b..d80c5c24 100644 --- a/examples/rp/src/bin/pio_hd44780.rs +++ b/examples/rp/src/bin/pio_hd44780.rs @@ -14,7 +14,6 @@ use embassy_rp::pio::{ Config, Direction, FifoJoin, InterruptHandler, Pio, PioPin, ShiftConfig, ShiftDirection, StateMachine, }; use embassy_rp::pwm::{self, Pwm}; -use embassy_rp::relocate::RelocatedProgram; use embassy_rp::{bind_interrupts, into_ref, Peripheral, PeripheralRef}; use embassy_time::{Duration, Instant, Timer}; use {defmt_rtt as _, panic_probe as _}; @@ -127,9 +126,8 @@ impl<'l> HD44780<'l> { sm0.set_pin_dirs(Direction::Out, &[&rs, &rw, &e, &db4, &db5, &db6, &db7]); - let relocated = RelocatedProgram::new(&prg.program); let mut cfg = Config::default(); - cfg.use_program(&common.load_program(&relocated), &[&e]); + cfg.use_program(&common.load_program(&prg.program), &[&e]); cfg.clock_divider = 125u8.into(); cfg.set_out_pins(&[&db4, &db5, &db6, &db7]); cfg.shift_out = ShiftConfig { @@ -201,9 +199,8 @@ impl<'l> HD44780<'l> { "# ); - let relocated = RelocatedProgram::new(&prg.program); let mut cfg = Config::default(); - cfg.use_program(&common.load_program(&relocated), &[&e]); + cfg.use_program(&common.load_program(&prg.program), &[&e]); cfg.clock_divider = 8u8.into(); // ~64ns/insn cfg.set_jmp_pin(&db7); cfg.set_set_pins(&[&rs, &rw]); diff --git a/examples/rp/src/bin/pio_uart.rs b/examples/rp/src/bin/pio_uart.rs index ca1c7f39..5fddbe29 100644 --- a/examples/rp/src/bin/pio_uart.rs +++ b/examples/rp/src/bin/pio_uart.rs @@ -222,8 +222,8 @@ mod uart { mut common, sm0, sm1, .. } = Pio::new(pio, Irqs); - let (tx, origin) = PioUartTx::new(&mut common, sm0, tx_pin, baud, None); - let (rx, _) = PioUartRx::new(&mut common, sm1, rx_pin, baud, Some(origin)); + let tx = PioUartTx::new(&mut common, sm0, tx_pin, baud); + let rx = PioUartRx::new(&mut common, sm1, rx_pin, baud); PioUart { tx, rx } } @@ -240,7 +240,6 @@ mod uart_tx { use embassy_rp::gpio::Level; use embassy_rp::peripherals::PIO0; use embassy_rp::pio::{Common, Config, Direction, FifoJoin, PioPin, ShiftDirection, StateMachine}; - use embassy_rp::relocate::RelocatedProgram; use embedded_io::asynch::Write; use embedded_io::Io; use fixed::traits::ToFixed; @@ -256,9 +255,8 @@ mod uart_tx { mut sm_tx: StateMachine<'a, PIO0, 0>, tx_pin: impl PioPin, baud: u64, - origin: Option, - ) -> (Self, u8) { - let mut prg = pio_proc::pio_asm!( + ) -> Self { + let prg = pio_proc::pio_asm!( r#" .side_set 1 opt @@ -272,17 +270,14 @@ mod uart_tx { jmp x-- bitloop [6] ; Each loop iteration is 8 cycles. "# ); - prg.program.origin = origin; let tx_pin = common.make_pio_pin(tx_pin); sm_tx.set_pins(Level::High, &[&tx_pin]); sm_tx.set_pin_dirs(Direction::Out, &[&tx_pin]); - let relocated = RelocatedProgram::new(&prg.program); - let mut cfg = Config::default(); cfg.set_out_pins(&[&tx_pin]); - cfg.use_program(&common.load_program(&relocated), &[&tx_pin]); + cfg.use_program(&common.load_program(&prg.program), &[&tx_pin]); cfg.shift_out.auto_fill = false; cfg.shift_out.direction = ShiftDirection::Right; cfg.fifo_join = FifoJoin::TxOnly; @@ -290,18 +285,7 @@ mod uart_tx { sm_tx.set_config(&cfg); sm_tx.set_enable(true); - // The 4 state machines of the PIO each have their own program counter that starts taking - // instructions at an offset (origin) of the 32 instruction "space" the PIO device has. - // It is up to the programmer to sort out where to place these instructions. - // From the pio_asm! macro you get a ProgramWithDefines which has a field .program.origin - // which takes an Option. - // - // When you load more than one RelocatedProgram into the PIO, - // you load your first program at origin = 0. - // The RelocatedProgram has .code().count() which returns a usize, - // for which you can then use as your next program's origin. - let offset = relocated.code().count() as u8 + origin.unwrap_or_default(); - (Self { sm_tx }, offset) + Self { sm_tx } } pub async fn write_u8(&mut self, data: u8) { @@ -329,7 +313,6 @@ mod uart_rx { use embassy_rp::gpio::Level; use embassy_rp::peripherals::PIO0; use embassy_rp::pio::{Common, Config, Direction, FifoJoin, PioPin, ShiftDirection, StateMachine}; - use embassy_rp::relocate::RelocatedProgram; use embedded_io::asynch::Read; use embedded_io::Io; use fixed::traits::ToFixed; @@ -345,9 +328,8 @@ mod uart_rx { mut sm_rx: StateMachine<'a, PIO0, 1>, rx_pin: impl PioPin, baud: u64, - origin: Option, - ) -> (Self, u8) { - let mut prg = pio_proc::pio_asm!( + ) -> Self { + let prg = pio_proc::pio_asm!( r#" ; Slightly more fleshed-out 8n1 UART receiver which handles framing errors and ; break conditions more gracefully. @@ -369,10 +351,8 @@ mod uart_rx { push ; important in case the TX clock is slightly too fast. "# ); - prg.program.origin = origin; - let relocated = RelocatedProgram::new(&prg.program); let mut cfg = Config::default(); - cfg.use_program(&common.load_program(&relocated), &[]); + cfg.use_program(&common.load_program(&prg.program), &[]); let rx_pin = common.make_pio_pin(rx_pin); sm_rx.set_pins(Level::High, &[&rx_pin]); @@ -387,8 +367,7 @@ mod uart_rx { sm_rx.set_config(&cfg); sm_rx.set_enable(true); - let offset = relocated.code().count() as u8 + origin.unwrap_or_default(); - (Self { sm_rx }, offset) + Self { sm_rx } } pub async fn read_u8(&mut self) -> u8 { diff --git a/examples/rp/src/bin/pio_ws2812.rs b/examples/rp/src/bin/pio_ws2812.rs index 3de2bd48..bc87016e 100644 --- a/examples/rp/src/bin/pio_ws2812.rs +++ b/examples/rp/src/bin/pio_ws2812.rs @@ -12,7 +12,6 @@ use embassy_rp::peripherals::PIO0; use embassy_rp::pio::{ Common, Config, FifoJoin, Instance, InterruptHandler, Pio, PioPin, ShiftConfig, ShiftDirection, StateMachine, }; -use embassy_rp::relocate::RelocatedProgram; use embassy_rp::{bind_interrupts, clocks, into_ref, Peripheral, PeripheralRef}; use embassy_time::{Duration, Timer}; use fixed::types::U24F8; @@ -73,8 +72,7 @@ impl<'d, P: Instance, const S: usize, const N: usize> Ws2812<'d, P, S, N> { cfg.set_out_pins(&[&out_pin]); cfg.set_set_pins(&[&out_pin]); - let relocated = RelocatedProgram::new(&prg); - cfg.use_program(&pio.load_program(&relocated), &[&out_pin]); + cfg.use_program(&pio.load_program(&prg), &[&out_pin]); // Clock config, measured in kHz to avoid overflows // TODO CLOCK_FREQ should come from embassy_rp diff --git a/tests/rp/src/bin/pio_irq.rs b/tests/rp/src/bin/pio_irq.rs index 45004424..bdea63ea 100644 --- a/tests/rp/src/bin/pio_irq.rs +++ b/tests/rp/src/bin/pio_irq.rs @@ -9,7 +9,6 @@ use embassy_executor::Spawner; use embassy_rp::bind_interrupts; use embassy_rp::peripherals::PIO0; use embassy_rp::pio::{Config, InterruptHandler, Pio}; -use embassy_rp::relocate::RelocatedProgram; use {defmt_rtt as _, panic_probe as _}; bind_interrupts!(struct Irqs { @@ -35,9 +34,8 @@ async fn main(_spawner: Spawner) { "irq wait 1", ); - let relocated = RelocatedProgram::new(&prg.program); let mut cfg = Config::default(); - cfg.use_program(&common.load_program(&relocated), &[]); + cfg.use_program(&common.load_program(&prg.program), &[]); sm.set_config(&cfg); sm.set_enable(true); diff --git a/tests/rp/src/bin/pio_multi_load.rs b/tests/rp/src/bin/pio_multi_load.rs new file mode 100644 index 00000000..356f1679 --- /dev/null +++ b/tests/rp/src/bin/pio_multi_load.rs @@ -0,0 +1,126 @@ +#![no_std] +#![no_main] +#![feature(type_alias_impl_trait)] +#[path = "../common.rs"] +mod common; + +use defmt::info; +use embassy_executor::Spawner; +use embassy_rp::bind_interrupts; +use embassy_rp::peripherals::PIO0; +use embassy_rp::pio::{Config, InterruptHandler, LoadError, Pio}; +use {defmt_rtt as _, panic_probe as _}; + +bind_interrupts!(struct Irqs { + PIO0_IRQ_0 => InterruptHandler; +}); + +#[embassy_executor::main] +async fn main(_spawner: Spawner) { + let p = embassy_rp::init(Default::default()); + let pio = p.PIO0; + let Pio { + mut common, + mut sm0, + mut sm1, + mut sm2, + irq_flags, + .. + } = Pio::new(pio, Irqs); + + // load with explicit origin works + let prg1 = pio_proc::pio_asm!( + ".origin 4" + "nop", + "nop", + "nop", + "nop", + "nop", + "nop", + "nop", + "irq 0", + "nop", + "nop", + ); + let loaded1 = common.load_program(&prg1.program); + assert_eq!(loaded1.origin, 4); + assert_eq!(loaded1.wrap.source, 13); + assert_eq!(loaded1.wrap.target, 4); + + // load without origin chooses a free space + let prg2 = pio_proc::pio_asm!("nop", "nop", "nop", "nop", "nop", "nop", "nop", "irq 1", "nop", "nop",); + let loaded2 = common.load_program(&prg2.program); + assert_eq!(loaded2.origin, 14); + assert_eq!(loaded2.wrap.source, 23); + assert_eq!(loaded2.wrap.target, 14); + + // wrapping around the end of program space automatically works + let prg3 = + pio_proc::pio_asm!("nop", "nop", "nop", "nop", "nop", "nop", "nop", "nop", "nop", "nop", "nop", "irq 2",); + let loaded3 = common.load_program(&prg3.program); + assert_eq!(loaded3.origin, 24); + assert_eq!(loaded3.wrap.source, 3); + assert_eq!(loaded3.wrap.target, 24); + + // check that the programs actually work + { + let mut cfg = Config::default(); + cfg.use_program(&loaded1, &[]); + sm0.set_config(&cfg); + sm0.set_enable(true); + while !irq_flags.check(0) {} + sm0.set_enable(false); + } + { + let mut cfg = Config::default(); + cfg.use_program(&loaded2, &[]); + sm1.set_config(&cfg); + sm1.set_enable(true); + while !irq_flags.check(1) {} + sm1.set_enable(false); + } + { + let mut cfg = Config::default(); + cfg.use_program(&loaded3, &[]); + sm2.set_config(&cfg); + sm2.set_enable(true); + while !irq_flags.check(2) {} + sm2.set_enable(false); + } + + // instruction memory is full now. all loads should fail. + { + let prg = pio_proc::pio_asm!(".origin 0", "nop"); + match common.try_load_program(&prg.program) { + Err(LoadError::AddressInUse(0)) => (), + _ => panic!("program loaded when it shouldn't"), + }; + + let prg = pio_proc::pio_asm!("nop"); + match common.try_load_program(&prg.program) { + Err(LoadError::InsufficientSpace) => (), + _ => panic!("program loaded when it shouldn't"), + }; + } + + // freeing some memory should allow further loads though. + unsafe { + common.free_instr(loaded3.used_memory); + } + { + let prg = pio_proc::pio_asm!(".origin 0", "nop"); + match common.try_load_program(&prg.program) { + Ok(_) => (), + _ => panic!("program didn't loaded when it shouldn"), + }; + + let prg = pio_proc::pio_asm!("nop"); + match common.try_load_program(&prg.program) { + Ok(_) => (), + _ => panic!("program didn't loaded when it shouldn"), + }; + } + + info!("Test OK"); + cortex_m::asm::bkpt(); +} From b344c843c4ccebcacf27f68db6d335aed2615302 Mon Sep 17 00:00:00 2001 From: Brandon Ros Date: Fri, 28 Jul 2023 17:25:07 -0400 Subject: [PATCH 38/68] sync latest cyw43-firmware --- cyw43-firmware/43439A0.bin | Bin 224190 -> 230321 bytes cyw43-firmware/43439A0_clm.bin | Bin 4752 -> 4752 bytes 2 files changed, 0 insertions(+), 0 deletions(-) diff --git a/cyw43-firmware/43439A0.bin b/cyw43-firmware/43439A0.bin index b46b3beff05de7dfcbf41d53a12bf3a74381881b..017375277d77d10e198deb1c668e2e6bb074d869 100755 GIT binary patch delta 78990 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

Cch^r!c<&_I;&@39H8>@7<$fnK0 z*5cG+wg%r}E8QZDYV}1Spg9kcz5+>_6q}Q@RLDJtmxrf#qNo^>HeDFMQesA7%_&XJ z^GuyZ!^XVbXgL;=Lh;IXNQ%5c+Jr|3HUwcO$mRDg=WER+Zc^G^;7Sy!#*k7uBH*NK z;AnSev%FUC z3ZWyf)mHlxPIP)}E&00Yfi%Js1*jDw$3alI(z{7Ff^;KDpNb24Hd_ofYn_o&lQYLP zJLP$!5^I5_8kkKjnNb17MfxrfzWi@ciL1$ z_79t{hr0Xk&QneNDE}gKpRv`)w-#R?QmOt`qpRr(xKh#f+%Bg#QmR%v*GA%U*J{_S zFh0#aed8Z12??kTS=2^eIpkB}cqur{;$SUZ$-@{3AxmAaqKr_@fEx+Z9$C;swuDP^c)UvedaW zNvGiHaJGFejrSed$PYZoh6H<m|c*N1mW<(<=lO57ZM3VU;ebwg0Q8C!KG@|>RxD@lau6@X38MC zbc)gqDka(y^A>--i#QJ&?0T!3)nx(gH&{wQ?0g8BxaHCT)+=hJQh3Woe8Nb$#o9I* zZ-W^iqG)vf&MNG*qj#m6T+78M)v)l$Ty`4Two)Bk`ylpDj5kf+dSH< zZIyVgiC1Hly1k5TpNF?|zh_^}qh`x=Wx{V)=NYZjm9*cue&KXw()nHUR=XZcs}jHb zYT8X_-)81BpN4v;DwawK!lhK@)8AMEFnuf4Kp30A9g(j4U1xpxsp!}uuduNhfk{`? zg8E^d+eE@|g4w?Hq&>q=oSI%ym({c9=hI%ZM#E0h*M0C3VW;)5S`XvPaPPq3&CYnA zgBM^+%(pbro~k_co6$}&Pw@h{oWC9BVPVqBjs+QMcXy}Sx89O#ws#c}&+`g5lpK;x z6Tr;i9ji&%&}F%T-f39`-*}@nWX0=?9>>$92V7GEwEp+F;`8=rNc(Fz&gz^;?)-eO z(HYb9Kq^{^2b0Ws=2D#02%ol1#qKpO%dM(krLMpscf}+;4K!gLIf^nyi&d_xvC3DS zCd6-;l5s1oQhl_6c`cyE=;K_lW;0bAfE|s+4;76n)%FUT zwg4;rGPZ01?ViVTD0h|j4Vw3x`gNK1uTl*!aq+iLGr4863be{+Xpc#CYh0lczr*<{ zpe1#vhs*>h+@2>#0gr_5EkYgCj4$-Z&XYsH+Wr-4AK-X|nj%q03qnna`8es9@d(Hh zx}tWk+ne;I%P2C?FrDl6B)#j3_!p*q-8;ZU{R`t=w*#17|H8<1Y~e!M8O>I!vJDGy zID%$_BHuzf0RPUY?2m=e+rI+iFU&hf_`U=4%sC>)W|fW3p~>)Fpt9F<=orftb-20) za{Uode|bRZBfzAH9ES)~3s=-wmHFh-0q7w8RLx36FSk!?^-JhF^0<*GzQyfpar-va ze(4%5e7nLo*zFtU_WcvSX;K%oA-7S0I+>uTos>}QsG=p46?Z7j8qOce(eaY&#LT?! zVK5%nk?i-!w?rJ08u_|@AV9+|{t|O%j*AeY#r%LS!rtg&dXom}g zy>YbD=zL8mwtJ)Bwi5kx@7-whQ=T|K5E#L>ETTOk0>A}we#Plj+$RK8DiN8SdN?-t zTzFe$w-(XRj_oiQ5-Lm&mqpI#nN3~5wNTb&G1!m5MuLM4&hHs&W2XuStjOM zOp~RT*{~%v%!hkw3?ZMvyQ*yI5}F`A!#;wi6v2L6LW8?^!j(!h4k-AY$>DB-zHTZP z9MUOZ0tAVtSi7aPlTUCn*S64<9IDjsYT3Z0GMkc$t_!{2{#9SJqACzD{VC=i{@2**1R=BaX(!vuDm;NFvo=8`3ImfFh_4z)jCKPaQo0aw%tamp8q z{s<+7#TV863iCTmBmAmWKc{yXmq)I{8^}<`##3L1X^7ty^<#xNI>WHMct^b8`MvDv z!?cr>!?qr#&q^a%;1SwG8pjfk;EvFtvYkh0S6RBizB*3drh%=P{R9G~u^T7oX-^ZO zGpG{Omz91>r%5K}eUgs&2RZd55*W*lous4NZKOfHx)Ui-MDTrw1)idhQa}H)iKpm1 zSz6Cdou+?EoytBwL$}gS&r!&m2#utLm7fr5rn(trfJD^xg)HMMkY`~peTAKLCsy(m z-6q{*$!F<7&&yis_sTrJrjsP;Z8qZ@Iv%O+`-TpXr2XvAZ$VIaS?YI`O41sJS==O* zuw~!VV(H5=46`UHaE(NVb|f7Nq*XKYv|oWp(C6*4DL+zw8dyT?UvwaZ%H=4vyjk-s zVFeZRN!(;{RnXJY*s|k4(bZIXyDae+daqSrjda{*Hf+em2i6jIY6J$CrCp&zB;>u( zLBBzqC$7@rRC=XsXB8bIOZ!;O4SKNC2O_ZFIC)nkRSc)3)egKeu~#5FTwo*j!GY~)_3T~(!sK$cW5e4*XvKzQ68-CP*Y%uRA8g6d`8JVK()uKbT#>k1m>~q zKS6Z~tNN4LrM$Ayf6gW}K z74@9jU&@SkA(G_Y_vjOnRKs3tq@x4Bd|2>VssAcCZsbju1INlTXz>b_-$&8B!nJ~q zEt}UuGm+4iALS89uDv8zQfXdUoh-jE1Afaxo-YL+6oK2WmG;=Y$Oavix;RPTq_Qrp zWYF+&SwAm%hC!OkUh|Rhd~{ihk35Pu^(a5Nht$9p`^g)nrqzP=%H6uMAZMn}1T`m{4j((&}uo`j-UWvvs->N?4NJPlI*jIGLh ztarE^*p`ycqHml;k$iI4_;5L=YmU4TkoQeNb9*X2zEiZ%cjIcb`7)$ z{odzEQqtu|;yad<7#|_N^N0%SWmUc4Frd<aA~Dal(i$6HY}qTw^@*Ume3Z|}NA?rcV>nGFK0$1yMII3tA_~DvjY6Q* z_7NkolVrbIzE&)(?kCN!S>K76*mYzMh@CQHwDYBY^xV|vIPVJ~xD5BXzhG7FB7JGL85ZsAe& ze(SU#quH3TR4#?=Y(9uxyuhS6t`Mf2`r@A8xjlegV93`73?nFF)AW=R9 z`2a2U0`Zy6vzLBF<5Z>wmb}Ly%65pP1H*}9Mj>&!Q~3nlKqWq(P)g} z-!CHg>9qU~kmBKol+qD|qQ79`C46{%LF^y?%|DJAyGXx1WWkJLefbyc2So4sIVqX3 zei7SA7LfPIhR?`?;y%ormF1U|0$&3)jG*<5D`6^w-OT3y5j+J|KU4!1eiHv;#{c^6{VVAstC-yQe`oc_n zUq-x_%`xlPi?On))2o~aQu^ux;-$~J5Z~RjXvQbRm$7eSk^64u8z=Yke2*6L=%p+z z4&;7F977l>;oYTO8;s?JLtd+k`Gh?kCy$Q&j+9Jk$2+9GvM?&O`OAV z<{Nl4EQVx|+PYBw(uiP;(l&(FqiTH)`iM0OLkMT5;nfL`WTVFXKFO)bUekBnH z(Q#;f*+&m*n=?p$5u#cB#!*_ipZJ-GedsHsT{l4+ro43pA0h}XoY6x3jQAi3X2(ha zt%~eT{4B&C@rDS(3FJY_@DT)}U9_H-he?*R9aQEK<7wdtkQPrR9zPxv8{X=3Pi%7m z{WHxyfT*+=O+QvDa7gK`-7Ptu>cE2d4W)(;ew2TJhjB%(8 z5RH(c+b2E-_a`1R8ocNl8Iz($Ps9ZnMcPw4YsecUp zsRe%2h5COe)Y^9}j3SEhCiqdvQw6~@CEaIY)-9S*gcwxETWcl@PLN0BWs+ON zXHTBI1FNR96VGtQn!0fuIj1i9D9JuEd(z@Lfaa2j{aRx4$$~R;rEyuJKeqPK>f*ds z7uhs_1nS~E^^nk#3;EjHs(cKRZt3F0H2cMxy4Xb`p4SPgt1Ke{_B^Q&t%X-BVXv5z zCi=xIT22^LA2`Xz<-{I|8eO4f@tpL6t!?D1)9G5?6wmSwh%6rXfja=Cb+RZ8xk_4X zGa!N+iG0y^ub^8U!-wRFEY?bf4?A#1!e03|@q2+5=A*S=5!uH+uT=u35>5<|oS0Y0 ztr6>|p-c;A6xqhTBCT=orl6~5Aki%v^_vjnYXs4#*!&>E!Wl&q_U)s3l-}=S;>e3+ zB;vYOCpU>bY9#S{o!r7F($_tHKrs68Zc-BY2Ja9h7yy{_SMTjk{7|J)Yl$Ckw4%&d z_(kQ3J`?#`-8j5p?x0TRNowHHkyGSX)T>6YBVJ8XP$XvY9a2ann>Il9nz)zuvl-(w zrpNDNjhq^kN~A#1>jLa3{~i7*iTH|m9TlYtu=BCTGg-rX)KOwDd7StiBKGK+fY^}% zvT4lc)WC<%Ntz#t_Bx4TV5vVw7DiI%QajdD5QilY{p(?xTHTams5?Cuf5E%w#BY!$ zubQt|k4I!v-uGI$e?Ym{XU6`3a<4{d$b!hX>SD_^;;)P5+V``T^xyc*CtfN?O0mBR zWb4+{z)1oW^^DZIv%5rE*S(0Y%X^aOrG8a(^%qNs-*0>|s(l`I(XRhMxn#0XUVuY@HHl>OT||M6VgWn4X*KTsasshM|$iTfoqcLs?# zkH*?WI%ose--n$WC{L3@S?@=&i|E2$eH2HE;q1Vpa*wQW2V<>?81Or-^?O#=$?Sq&-Y!#^2JWF~~vU zIpo>*jwJpQc-qr8jn~qi@c*VgZT|!`lSqvhMtwqR!^z(vwdEf$DOf1|p8%J)rf%{M zeAaq;op_K1@4x;}AUe-btC=kH6=~WBzYu#Kuc1t>hF0KnlladC&uGR+OjP)IM0Nz^ z&R(N%h3J<4E5yv!{(?I?64moW9Z|24NHgk!Pp?{{{*jloJ)egphTTMMMQ2kdUC_?i6#7nlg)mm$>rmJ~V7VvygG>x>? z80VLW7q)qP+&MXEQrt#LF>Cp+;j<;Lgn|#zJvM6H_#%dMZ$Nx7r9UMGQ~IV?Ff`*p zOCIWSeR>&R4cF9-e-&@Zq7)lZw0|<(DCLWRBXaWJqRmnj(pm@XwVb3Lm;?P8mUOM{ zBAg^{yXf5Nc3Q+c=^Gr9{ei?Jzv&|h>j>g$sT?7LfR+7sVd_V|yGRM~CtfE|!aU-2 zN-OW)2N6RfI{^7`IhpY{@&ACO4FOxUm+XmphNN^B<$eCNmiAdJc*ltUQOJ!zVTqWm z{g1-$SKb7C@%KOS|C#yy%ft-me^|`?afcty{2#tnH~uIlWl_Itc>SK$di;*Z(7B5; zHl5OX^$tE_>M&^;XB1`Z{}QR?5dSZ==H*TQ0^RflpBG7_HZS^rDX5R^%*#pDvn+b3 z+@t@GbPX6Ve}`5F6`Yuv>UCOx#lYWza(3~^f-zBZepJ%6aUL>BchMI zj2`!L!x9JB>rEs&lXKb9hS}gjH`Z-Br=^K$9F4U}`;6BZfKM6lYg!iIRr>jk%P|`=*%w=|0c0}@_H9lC~~kz0eM%9h4FVG zmC|Q?+Zl7~ha}=QN80?GKnt&IwriN|pSPKSpMSY7;$w_cu`d31p(u&?kT+{d#C|@A zcvsu(E7rvTUoT5+)wVwOP!d*n_2DZz)Q8F`0v z$U~x-fCEBM7g(!9_+SEShtczL-DHim!}Ief1qeobq#g3#v@s-mlYlOi^O^YQz&ZzX zk&D4m26O?N%Hz=jjNyQOSjsx^C2-AN@yUmj1k4;ZX2&>o6~+|86mZXeG++A^@EEux zrhPcC&mry?O;=;s09wucqG^%#Dbzw1@*wcjKAhzb=qtE5dvCn$gag9_Qx-4~rJ!I2wYImjXVc_}JU2iKB#<-`k22#kj*8sJX~d7yVyBV$ zdxx4~lnYx;Da8kzC{Igpq5)%gr+mPP2BZRd$d?9;#oYL>T(!g=*AkmVqW?dsP5n=5 z)BclMI@>W)j)>9t*#xl%LY1%PRX$q`0x%(k=)R~BM`U*r@wF&l`<;wg1s?h z0rjlyNV#KP0dIfmy8wL>`_F}7-%<80T7?v9eottAGvGHLej4#FYs5oL|Nqv+3;&`0 zMK|rzI$PVGW9Ml}Z*f;0+cOf|&^MT4B&1bYXt1oAzlwE;#J|IQN67)wJ1l$@M)y9g zrB7?Qe?WEEfCcQ+62t4x*mMN!6RROUoOKE)`DfGdWnH``PYHmW@t>VYygRWY|K3mp zl>Miw54(%vq2+XvefGE<$?lGlL-Ib-^7+!;3|fG*?uK?FMKg%K4Q~xC!gqWLf3R(9 z|B>{PkVI;J75}NEpEwjbb{Z|<9kNOba#^eRUvZ)3LCy2cjn)S*{)hNWZsIl4YEeuB zD@c>WHQp(pR!i^?Ey0`rr1wvszWtvR?))e9THXsI*^FdNdszpUZJYegASSptOti(4nI&XX&#lb+ zUf$$pfp*-lSVBD%{TI=eqr{RywAJE(X)BdT0IerT^W<~>YbVr5Ui{Kf$gtIsF|szO zb+Ccuc)G}DMjC9pkvY>)fv6NNOxq846Ju$w@s8Kw4IW*?V>CJl#Th|ekbDLEqB*VDTCdinEyA? zcDJlR4YLt%CKUh6F6UJu)mubRX6A7PC1~D6)UvrN;F0QuN7Cv8dXcj4P*CyU=d=3L zCZeG%hVZ1x>$Fu?@3rYr&$1BbA@u-P`C*916mkFLJ-_oX_qd8#4d846j03Pmt$}OT zTEy6{YTgjDhV_D7ORHMVwR(urmDCQG#A_QkBRuT0<490m$-Liz$B|cwc^t`^=!p>d zQ10(s5Ho->VhkxMh@D2#%qx7s38l6TIE=84k~MKxkV9-H+WWkhyYvb>vyw)J57Csw z*tg&;`7_8RWL)!>U@h6wMow;^ngl+Z)>^9(7O;ly4nMI!1_ll zbWapQhy2U$BRs@_j2e4Z(`O=d{9m7K4Y3M!WVu!L#cKNOXgv%+6h?_2#=_F+@i6KvlpW%)P=v?< zEJcRjQW?M$yVQvs+s)##u=oL%$(shYH;WcIPQzfk2bznCab4|qy{)RN@EqI*(v&bf zjp4S#4KoZES)YCfbH&F@ccYTpMvUz=d7*SG)t^Uhdc1K@)a_5+fQ{ zyHeZx7>zh8nP?AU_U2z9=6j?I@;7VL*U{5k0+$tu*&?Qs$3-vXg1WB@oS}*l$8DOO z0Tbb$^Q%N{;w-g(m5u1<*)MKd>iABHB*omqxuW%pq+Xe==+%nBkf@Zf#bbdzy z*Nu>M?WMC1*~x6WY=$+IGMo*o1)EPGkxwhW-Y^IhIO?U&@uV-;)vq@SYSsw$WDcD; z!#YuF-Y21vz)9)XeOQW@s_~FAzF()L{Q7vmIf)hK@Xz1b{v4c&sjMRh58sLGZVn7T zE#Ol)F$#Thw=-ce@`R9nvj)zKHxifzzCrpJ_ab9Gtzf&+#1vw_pr%pX{lu&ECpWvAUn90R)imh)iz=qU7puaLH{iM1O(7?}#c7au6-ghYF4W6W;208#Uim@%YuN&>O=iFXuhk4hJA z=RN+aXzdeY=52N(AN=;Q(FF*_K@>Kl08a6b&b$Izrzkdds}NoM{z);N%p66izt*zG zB04?#h{y+{jtG)bM?kWPB=P2DlC+0)6w)c|&m#Im_%`Uhq<+IsLbg%c*^ld}Y4gd+ z@|82nhfPk5FtJITedQ^Y%-cYsYLL2m|4rJ{Iky;0c5d|2n2;$Sw#Smtf;q%e5$7dq zVwNq8ow{h61;4jVNbGN+<5=4!dax?Kyuxd^OSWy_xV1dRFlQz^XSy85v~~7`B)hA` zSdcT_SdhLT#aO@ITjWegbQ!5dJ4#ZFjGg5MK50vcwd7Ioi3NxRrDW3SYgEg delta 72844 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 Dmcqh| diff --git a/cyw43-firmware/43439A0_clm.bin b/cyw43-firmware/43439A0_clm.bin index 6e3ba786b2b496ef3d347d8f8150cb433f684692..1fedd753ac255eb8feb4b81a8214d4070a8e414c 100755 GIT binary patch delta 67 zcmbQBIzd&%$;aQxhJk^hTJ^08BamhSVh{j{1qd-P@W~3i+^8~>Nx)dwz(m);NWsY1 P%GlV-z+&@Rro;RIogxiR delta 67 zcmbQBIzd&%$;aQxhJk@WTlJ|CBamhSVh{j{1qd-P+-_tQ->5Q^Nx)Fo&`{UNQo+c` P%EZXZ*l_b%ro;RInTQQo From cffb819e61a9cc87cad9a5351669e7dd28eeeba4 Mon Sep 17 00:00:00 2001 From: Brandon Ros Date: Fri, 28 Jul 2023 17:34:07 -0400 Subject: [PATCH 39/68] changelog --- cyw43-firmware/README.md | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/cyw43-firmware/README.md b/cyw43-firmware/README.md index 7381fdc5..db3d9c9c 100644 --- a/cyw43-firmware/README.md +++ b/cyw43-firmware/README.md @@ -2,4 +2,8 @@ Firmware obtained from https://github.com/Infineon/wifi-host-driver/tree/master/WiFi_Host_Driver/resources/firmware/COMPONENT_43439 -Licensed under the [Infineon Permissive Binary License](./LICENSE-permissive-binary-license-1.0.txt) \ No newline at end of file +Licensed under the [Infineon Permissive Binary License](./LICENSE-permissive-binary-license-1.0.txt) + +## Changelog + +* 2023-07-28: synced with `ad3bad0` - Update 43439 fw from 7.95.55 ot 7.95.62 From 29acc465017ae0b09ffb9d6364c9052e8b5f3937 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Fri, 28 Jul 2023 23:47:07 +0200 Subject: [PATCH 40/68] core::fmt devours your RAM and flash and explodes your stack. (#1708) --- embassy-net-esp-hosted/src/control.rs | 2 -- 1 file changed, 2 deletions(-) diff --git a/embassy-net-esp-hosted/src/control.rs b/embassy-net-esp-hosted/src/control.rs index 79f8cde7..37f220da 100644 --- a/embassy-net-esp-hosted/src/control.rs +++ b/embassy-net-esp-hosted/src/control.rs @@ -1,5 +1,4 @@ use ch::driver::LinkState; -use defmt::Debug2Format; use embassy_net_driver_channel as ch; use heapless::String; @@ -57,7 +56,6 @@ impl<'a> Control<'a> { let proto::CtrlMsgPayload::RespConnectAp(resp) = resp.payload.unwrap() else { panic!("unexpected resp") }; - debug!("======= {:?}", Debug2Format(&resp)); assert_eq!(resp.resp, 0); self.state_ch.set_link_state(LinkState::Up); } From d39404cddaa188790040fab00fe317205d4e5ab2 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Fri, 28 Jul 2023 23:49:37 +0200 Subject: [PATCH 41/68] fix flaky test wifi_esp_hosted_perf --- tests/nrf/src/bin/wifi_esp_hosted_perf.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/nrf/src/bin/wifi_esp_hosted_perf.rs b/tests/nrf/src/bin/wifi_esp_hosted_perf.rs index 398ab9d2..e2adfe0b 100644 --- a/tests/nrf/src/bin/wifi_esp_hosted_perf.rs +++ b/tests/nrf/src/bin/wifi_esp_hosted_perf.rs @@ -118,9 +118,9 @@ const WIFI_NETWORK: &str = "EmbassyTest"; const WIFI_PASSWORD: &str = "V8YxhKt5CdIAJFud"; const TEST_DURATION: usize = 10; -const TEST_EXPECTED_DOWNLOAD_KBPS: usize = 150; -const TEST_EXPECTED_UPLOAD_KBPS: usize = 150; -const TEST_EXPECTED_UPLOAD_DOWNLOAD_KBPS: usize = 150; +const TEST_EXPECTED_DOWNLOAD_KBPS: usize = 50; +const TEST_EXPECTED_UPLOAD_KBPS: usize = 50; +const TEST_EXPECTED_UPLOAD_DOWNLOAD_KBPS: usize = 50; const RX_BUFFER_SIZE: usize = 4096; const TX_BUFFER_SIZE: usize = 4096; const SERVER_ADDRESS: Ipv4Address = Ipv4Address::new(192, 168, 2, 2); From fd47445d75eb3546b5581dbbcd9926611489b743 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Fri, 28 Jul 2023 23:58:47 +0200 Subject: [PATCH 42/68] cyw43: Update firmware in HIL test. --- tests/rp/src/bin/cyw43-perf.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/rp/src/bin/cyw43-perf.rs b/tests/rp/src/bin/cyw43-perf.rs index bc127e2e..fffdabc9 100644 --- a/tests/rp/src/bin/cyw43-perf.rs +++ b/tests/rp/src/bin/cyw43-perf.rs @@ -48,9 +48,9 @@ async fn main(spawner: Spawner) { } // cyw43 firmware needs to be flashed manually: - // probe-rs download 43439A0.bin --format bin --chip RP2040 --base-address 0x101c0000 + // probe-rs download 43439A0.bin --format bin --chip RP2040 --base-address 0x101b0000 // probe-rs download 43439A0_clm.bin --format bin --chip RP2040 --base-address 0x101f8000 - let fw = unsafe { core::slice::from_raw_parts(0x101c0000 as *const u8, 224190) }; + let fw = unsafe { core::slice::from_raw_parts(0x101b0000 as *const u8, 230321) }; let clm = unsafe { core::slice::from_raw_parts(0x101f8000 as *const u8, 4752) }; let pwr = Output::new(p.PIN_23, Level::Low); From 28136579e98ea4d9745867d7c1ffff0ad826b504 Mon Sep 17 00:00:00 2001 From: xoviat Date: Fri, 28 Jul 2023 17:07:08 -0500 Subject: [PATCH 43/68] stm32/hrtim: extract into mod --- embassy-stm32/build.rs | 20 +++++++++--------- .../src/{pwm/advanced_pwm.rs => hrtim/mod.rs} | 13 +++++++++++- embassy-stm32/src/lib.rs | 9 ++++---- embassy-stm32/src/pwm/mod.rs | 21 ------------------- examples/stm32f334/src/bin/pwm.rs | 2 +- 5 files changed, 28 insertions(+), 37 deletions(-) rename embassy-stm32/src/{pwm/advanced_pwm.rs => hrtim/mod.rs} (94%) diff --git a/embassy-stm32/build.rs b/embassy-stm32/build.rs index d2b1cfd0..c2b84797 100644 --- a/embassy-stm32/build.rs +++ b/embassy-stm32/build.rs @@ -587,16 +587,16 @@ fn main() { (("timer", "BKIN2"), quote!(crate::pwm::BreakInput2Pin)), (("timer", "BKIN2_COMP1"), quote!(crate::pwm::BreakInput2Comparator1Pin)), (("timer", "BKIN2_COMP2"), quote!(crate::pwm::BreakInput2Comparator2Pin)), - (("hrtim", "CHA1"), quote!(crate::pwm::ChannelAPin)), - (("hrtim", "CHA2"), quote!(crate::pwm::ChannelAComplementaryPin)), - (("hrtim", "CHB1"), quote!(crate::pwm::ChannelBPin)), - (("hrtim", "CHB2"), quote!(crate::pwm::ChannelBComplementaryPin)), - (("hrtim", "CHC1"), quote!(crate::pwm::ChannelCPin)), - (("hrtim", "CHC2"), quote!(crate::pwm::ChannelCComplementaryPin)), - (("hrtim", "CHD1"), quote!(crate::pwm::ChannelDPin)), - (("hrtim", "CHD2"), quote!(crate::pwm::ChannelDComplementaryPin)), - (("hrtim", "CHE1"), quote!(crate::pwm::ChannelEPin)), - (("hrtim", "CHE2"), quote!(crate::pwm::ChannelEComplementaryPin)), + (("hrtim", "CHA1"), quote!(crate::hrtim::ChannelAPin)), + (("hrtim", "CHA2"), quote!(crate::hrtim::ChannelAComplementaryPin)), + (("hrtim", "CHB1"), quote!(crate::hrtim::ChannelBPin)), + (("hrtim", "CHB2"), quote!(crate::hrtim::ChannelBComplementaryPin)), + (("hrtim", "CHC1"), quote!(crate::hrtim::ChannelCPin)), + (("hrtim", "CHC2"), quote!(crate::hrtim::ChannelCComplementaryPin)), + (("hrtim", "CHD1"), quote!(crate::hrtim::ChannelDPin)), + (("hrtim", "CHD2"), quote!(crate::hrtim::ChannelDComplementaryPin)), + (("hrtim", "CHE1"), quote!(crate::hrtim::ChannelEPin)), + (("hrtim", "CHE2"), quote!(crate::hrtim::ChannelEComplementaryPin)), (("sdmmc", "CK"), quote!(crate::sdmmc::CkPin)), (("sdmmc", "CMD"), quote!(crate::sdmmc::CmdPin)), (("sdmmc", "D0"), quote!(crate::sdmmc::D0Pin)), diff --git a/embassy-stm32/src/pwm/advanced_pwm.rs b/embassy-stm32/src/hrtim/mod.rs similarity index 94% rename from embassy-stm32/src/pwm/advanced_pwm.rs rename to embassy-stm32/src/hrtim/mod.rs index 9e40c5bf..7e3a8f14 100644 --- a/embassy-stm32/src/pwm/advanced_pwm.rs +++ b/embassy-stm32/src/hrtim/mod.rs @@ -2,10 +2,10 @@ use core::marker::PhantomData; use embassy_hal_common::{into_ref, PeripheralRef}; -use super::*; #[allow(unused_imports)] use crate::gpio::sealed::{AFType, Pin}; use crate::gpio::AnyPin; +use crate::pwm::HighResolutionCaptureCompare16bitInstance; use crate::time::Hertz; use crate::Peripheral; @@ -394,3 +394,14 @@ impl> Resona self.max_period } } + +pin_trait!(ChannelAPin, HighResolutionCaptureCompare16bitInstance); +pin_trait!(ChannelAComplementaryPin, HighResolutionCaptureCompare16bitInstance); +pin_trait!(ChannelBPin, HighResolutionCaptureCompare16bitInstance); +pin_trait!(ChannelBComplementaryPin, HighResolutionCaptureCompare16bitInstance); +pin_trait!(ChannelCPin, HighResolutionCaptureCompare16bitInstance); +pin_trait!(ChannelCComplementaryPin, HighResolutionCaptureCompare16bitInstance); +pin_trait!(ChannelDPin, HighResolutionCaptureCompare16bitInstance); +pin_trait!(ChannelDComplementaryPin, HighResolutionCaptureCompare16bitInstance); +pin_trait!(ChannelEPin, HighResolutionCaptureCompare16bitInstance); +pin_trait!(ChannelEComplementaryPin, HighResolutionCaptureCompare16bitInstance); diff --git a/embassy-stm32/src/lib.rs b/embassy-stm32/src/lib.rs index 45a7b547..8c005bfe 100644 --- a/embassy-stm32/src/lib.rs +++ b/embassy-stm32/src/lib.rs @@ -23,6 +23,8 @@ pub mod timer; pub mod adc; #[cfg(can)] pub mod can; +#[cfg(crc)] +pub mod crc; #[cfg(dac)] pub mod dac; #[cfg(dcmi)] @@ -31,14 +33,13 @@ pub mod dcmi; pub mod eth; #[cfg(feature = "exti")] pub mod exti; +pub mod flash; #[cfg(fmc)] pub mod fmc; +#[cfg(hrtim_v1)] +pub mod hrtim; #[cfg(i2c)] pub mod i2c; - -#[cfg(crc)] -pub mod crc; -pub mod flash; #[cfg(all(spi_v1, rcc_f4))] pub mod i2s; #[cfg(stm32wb)] diff --git a/embassy-stm32/src/pwm/mod.rs b/embassy-stm32/src/pwm/mod.rs index 429a290e..c4b38ebd 100644 --- a/embassy-stm32/src/pwm/mod.rs +++ b/embassy-stm32/src/pwm/mod.rs @@ -1,5 +1,3 @@ -#[cfg(hrtim_v1)] -pub mod advanced_pwm; pub mod complementary_pwm; pub mod simple_pwm; @@ -468,22 +466,3 @@ pin_trait!(BreakInputComparator2Pin, CaptureCompare16bitInstance); pin_trait!(BreakInput2Pin, CaptureCompare16bitInstance); pin_trait!(BreakInput2Comparator1Pin, CaptureCompare16bitInstance); pin_trait!(BreakInput2Comparator2Pin, CaptureCompare16bitInstance); - -#[cfg(hrtim_v1)] -mod hrtim_pins { - use super::*; - - pin_trait!(ChannelAPin, HighResolutionCaptureCompare16bitInstance); - pin_trait!(ChannelAComplementaryPin, HighResolutionCaptureCompare16bitInstance); - pin_trait!(ChannelBPin, HighResolutionCaptureCompare16bitInstance); - pin_trait!(ChannelBComplementaryPin, HighResolutionCaptureCompare16bitInstance); - pin_trait!(ChannelCPin, HighResolutionCaptureCompare16bitInstance); - pin_trait!(ChannelCComplementaryPin, HighResolutionCaptureCompare16bitInstance); - pin_trait!(ChannelDPin, HighResolutionCaptureCompare16bitInstance); - pin_trait!(ChannelDComplementaryPin, HighResolutionCaptureCompare16bitInstance); - pin_trait!(ChannelEPin, HighResolutionCaptureCompare16bitInstance); - pin_trait!(ChannelEComplementaryPin, HighResolutionCaptureCompare16bitInstance); -} - -#[cfg(hrtim_v1)] -pub use hrtim_pins::*; diff --git a/examples/stm32f334/src/bin/pwm.rs b/examples/stm32f334/src/bin/pwm.rs index 36411974..2660b10c 100644 --- a/examples/stm32f334/src/bin/pwm.rs +++ b/examples/stm32f334/src/bin/pwm.rs @@ -4,7 +4,7 @@ use defmt::*; use embassy_executor::Spawner; -use embassy_stm32::pwm::advanced_pwm::*; +use embassy_stm32::hrtim::*; use embassy_stm32::time::{khz, mhz}; use embassy_stm32::Config; use embassy_time::{Duration, Timer}; From e495d606ec62ccfd72eaf9fc868455da217e2d5c Mon Sep 17 00:00:00 2001 From: xoviat Date: Fri, 28 Jul 2023 17:16:46 -0500 Subject: [PATCH 44/68] stm32/hrtim: extract traits --- embassy-stm32/src/hrtim/mod.rs | 6 +- embassy-stm32/src/hrtim/traits.rs | 196 +++++++++++++++++++++++++++++ embassy-stm32/src/pwm/mod.rs | 199 ------------------------------ 3 files changed, 200 insertions(+), 201 deletions(-) create mode 100644 embassy-stm32/src/hrtim/traits.rs diff --git a/embassy-stm32/src/hrtim/mod.rs b/embassy-stm32/src/hrtim/mod.rs index 7e3a8f14..ddf8cc2a 100644 --- a/embassy-stm32/src/hrtim/mod.rs +++ b/embassy-stm32/src/hrtim/mod.rs @@ -1,3 +1,5 @@ +mod traits; + use core::marker::PhantomData; use embassy_hal_common::{into_ref, PeripheralRef}; @@ -5,7 +7,7 @@ use embassy_hal_common::{into_ref, PeripheralRef}; #[allow(unused_imports)] use crate::gpio::sealed::{AFType, Pin}; use crate::gpio::AnyPin; -use crate::pwm::HighResolutionCaptureCompare16bitInstance; +use crate::hrtim::traits::HighResolutionCaptureCompare16bitInstance; use crate::time::Hertz; use crate::Peripheral; @@ -41,7 +43,7 @@ pub struct ChE { } mod sealed { - use crate::pwm::HighResolutionCaptureCompare16bitInstance; + use super::HighResolutionCaptureCompare16bitInstance; pub trait AdvancedChannel { fn raw() -> usize; diff --git a/embassy-stm32/src/hrtim/traits.rs b/embassy-stm32/src/hrtim/traits.rs new file mode 100644 index 00000000..16193a45 --- /dev/null +++ b/embassy-stm32/src/hrtim/traits.rs @@ -0,0 +1,196 @@ +use crate::time::Hertz; + +#[derive(Clone, Copy)] +pub(crate) enum HighResolutionControlPrescaler { + Div1, + Div2, + Div4, + Div8, + Div16, + Div32, + Div64, + Div128, +} + +impl From for u32 { + fn from(val: HighResolutionControlPrescaler) -> Self { + match val { + HighResolutionControlPrescaler::Div1 => 1, + HighResolutionControlPrescaler::Div2 => 2, + HighResolutionControlPrescaler::Div4 => 4, + HighResolutionControlPrescaler::Div8 => 8, + HighResolutionControlPrescaler::Div16 => 16, + HighResolutionControlPrescaler::Div32 => 32, + HighResolutionControlPrescaler::Div64 => 64, + HighResolutionControlPrescaler::Div128 => 128, + } + } +} + +impl From for u8 { + fn from(val: HighResolutionControlPrescaler) -> Self { + match val { + HighResolutionControlPrescaler::Div1 => 0b000, + HighResolutionControlPrescaler::Div2 => 0b001, + HighResolutionControlPrescaler::Div4 => 0b010, + HighResolutionControlPrescaler::Div8 => 0b011, + HighResolutionControlPrescaler::Div16 => 0b100, + HighResolutionControlPrescaler::Div32 => 0b101, + HighResolutionControlPrescaler::Div64 => 0b110, + HighResolutionControlPrescaler::Div128 => 0b111, + } + } +} + +impl From for HighResolutionControlPrescaler { + fn from(val: u8) -> Self { + match val { + 0b000 => HighResolutionControlPrescaler::Div1, + 0b001 => HighResolutionControlPrescaler::Div2, + 0b010 => HighResolutionControlPrescaler::Div4, + 0b011 => HighResolutionControlPrescaler::Div8, + 0b100 => HighResolutionControlPrescaler::Div16, + 0b101 => HighResolutionControlPrescaler::Div32, + 0b110 => HighResolutionControlPrescaler::Div64, + 0b111 => HighResolutionControlPrescaler::Div128, + _ => unreachable!(), + } + } +} + +impl HighResolutionControlPrescaler { + pub fn compute_min_high_res(val: u32) -> Self { + *[ + HighResolutionControlPrescaler::Div1, + HighResolutionControlPrescaler::Div2, + HighResolutionControlPrescaler::Div4, + HighResolutionControlPrescaler::Div8, + HighResolutionControlPrescaler::Div16, + HighResolutionControlPrescaler::Div32, + HighResolutionControlPrescaler::Div64, + HighResolutionControlPrescaler::Div128, + ] + .iter() + .skip_while(|psc| >::into(**psc) <= val) + .next() + .unwrap() + } + + pub fn compute_min_low_res(val: u32) -> Self { + *[ + HighResolutionControlPrescaler::Div32, + HighResolutionControlPrescaler::Div64, + HighResolutionControlPrescaler::Div128, + ] + .iter() + .skip_while(|psc| >::into(**psc) <= val) + .next() + .unwrap() + } +} + +pub(crate) mod sealed { + use super::*; + + pub trait HighResolutionCaptureCompare16bitInstance: crate::timer::sealed::HighResolutionControlInstance { + fn set_master_frequency(frequency: Hertz); + + fn set_channel_frequency(channnel: usize, frequency: Hertz); + + /// Set the dead time as a proportion of max_duty + fn set_channel_dead_time(channnel: usize, dead_time: u16); + + // fn enable_outputs(enable: bool); + // + // fn enable_channel(&mut self, channel: usize, enable: bool); + } +} + +pub trait HighResolutionCaptureCompare16bitInstance: + sealed::HighResolutionCaptureCompare16bitInstance + 'static +{ +} + +foreach_interrupt! { + ($inst:ident, hrtim, HRTIM, MASTER, $irq:ident) => { + impl sealed::HighResolutionCaptureCompare16bitInstance for crate::peripherals::$inst { + fn set_master_frequency(frequency: Hertz) { + use crate::rcc::sealed::RccPeripheral; + use crate::timer::sealed::HighResolutionControlInstance; + + let f = frequency.0; + let timer_f = Self::frequency().0; + let psc_min = (timer_f / f) / (u16::MAX as u32 / 32); + let psc = if Self::regs().isr().read().dllrdy() { + HighResolutionControlPrescaler::compute_min_high_res(psc_min) + } else { + HighResolutionControlPrescaler::compute_min_low_res(psc_min) + }; + + let psc_val: u32 = psc.into(); + let timer_f = 32 * (timer_f / psc_val); + let per: u16 = (timer_f / f) as u16; + + let regs = Self::regs(); + + regs.mcr().modify(|w| w.set_ckpsc(psc.into())); + regs.mper().modify(|w| w.set_mper(per)); + } + + fn set_channel_frequency(channel: usize, frequency: Hertz) { + use crate::rcc::sealed::RccPeripheral; + use crate::timer::sealed::HighResolutionControlInstance; + + let f = frequency.0; + let timer_f = Self::frequency().0; + let psc_min = (timer_f / f) / (u16::MAX as u32 / 32); + let psc = if Self::regs().isr().read().dllrdy() { + HighResolutionControlPrescaler::compute_min_high_res(psc_min) + } else { + HighResolutionControlPrescaler::compute_min_low_res(psc_min) + }; + + let psc_val: u32 = psc.into(); + let timer_f = 32 * (timer_f / psc_val); + let per: u16 = (timer_f / f) as u16; + + let regs = Self::regs(); + + regs.tim(channel).cr().modify(|w| w.set_ckpsc(psc.into())); + regs.tim(channel).per().modify(|w| w.set_per(per)); + } + + fn set_channel_dead_time(channel: usize, dead_time: u16) { + use crate::timer::sealed::HighResolutionControlInstance; + + let regs = Self::regs(); + + let channel_psc: HighResolutionControlPrescaler = regs.tim(channel).cr().read().ckpsc().into(); + let psc_val: u32 = channel_psc.into(); + + + // The dead-time base clock runs 4 times slower than the hrtim base clock + // u9::MAX = 511 + let psc_min = (psc_val * dead_time as u32) / (4 * 511); + let psc = if Self::regs().isr().read().dllrdy() { + HighResolutionControlPrescaler::compute_min_high_res(psc_min) + } else { + HighResolutionControlPrescaler::compute_min_low_res(psc_min) + }; + + let dt_psc_val: u32 = psc.into(); + let dt_val = (dt_psc_val * dead_time as u32) / (4 * psc_val); + + regs.tim(channel).dt().modify(|w| { + w.set_dtprsc(psc.into()); + w.set_dtf(dt_val as u16); + w.set_dtr(dt_val as u16); + }); + } + } + + impl HighResolutionCaptureCompare16bitInstance for crate::peripherals::$inst { + + } + }; +} diff --git a/embassy-stm32/src/pwm/mod.rs b/embassy-stm32/src/pwm/mod.rs index c4b38ebd..5aba2663 100644 --- a/embassy-stm32/src/pwm/mod.rs +++ b/embassy-stm32/src/pwm/mod.rs @@ -3,9 +3,6 @@ pub mod simple_pwm; use stm32_metapac::timer::vals::Ckd; -#[cfg(hrtim_v1)] -use crate::time::Hertz; - #[cfg(feature = "unstable-pac")] pub mod low_level { pub use super::sealed::*; @@ -57,117 +54,9 @@ impl From for stm32_metapac::timer::vals::Ocm { } } -#[cfg(hrtim_v1)] -#[derive(Clone, Copy)] -pub(crate) enum HighResolutionControlPrescaler { - Div1, - Div2, - Div4, - Div8, - Div16, - Div32, - Div64, - Div128, -} - -#[cfg(hrtim_v1)] -impl From for u32 { - fn from(val: HighResolutionControlPrescaler) -> Self { - match val { - HighResolutionControlPrescaler::Div1 => 1, - HighResolutionControlPrescaler::Div2 => 2, - HighResolutionControlPrescaler::Div4 => 4, - HighResolutionControlPrescaler::Div8 => 8, - HighResolutionControlPrescaler::Div16 => 16, - HighResolutionControlPrescaler::Div32 => 32, - HighResolutionControlPrescaler::Div64 => 64, - HighResolutionControlPrescaler::Div128 => 128, - } - } -} - -#[cfg(hrtim_v1)] -impl From for u8 { - fn from(val: HighResolutionControlPrescaler) -> Self { - match val { - HighResolutionControlPrescaler::Div1 => 0b000, - HighResolutionControlPrescaler::Div2 => 0b001, - HighResolutionControlPrescaler::Div4 => 0b010, - HighResolutionControlPrescaler::Div8 => 0b011, - HighResolutionControlPrescaler::Div16 => 0b100, - HighResolutionControlPrescaler::Div32 => 0b101, - HighResolutionControlPrescaler::Div64 => 0b110, - HighResolutionControlPrescaler::Div128 => 0b111, - } - } -} - -#[cfg(hrtim_v1)] -impl From for HighResolutionControlPrescaler { - fn from(val: u8) -> Self { - match val { - 0b000 => HighResolutionControlPrescaler::Div1, - 0b001 => HighResolutionControlPrescaler::Div2, - 0b010 => HighResolutionControlPrescaler::Div4, - 0b011 => HighResolutionControlPrescaler::Div8, - 0b100 => HighResolutionControlPrescaler::Div16, - 0b101 => HighResolutionControlPrescaler::Div32, - 0b110 => HighResolutionControlPrescaler::Div64, - 0b111 => HighResolutionControlPrescaler::Div128, - _ => unreachable!(), - } - } -} - -#[cfg(hrtim_v1)] -impl HighResolutionControlPrescaler { - pub fn compute_min_high_res(val: u32) -> Self { - *[ - HighResolutionControlPrescaler::Div1, - HighResolutionControlPrescaler::Div2, - HighResolutionControlPrescaler::Div4, - HighResolutionControlPrescaler::Div8, - HighResolutionControlPrescaler::Div16, - HighResolutionControlPrescaler::Div32, - HighResolutionControlPrescaler::Div64, - HighResolutionControlPrescaler::Div128, - ] - .iter() - .skip_while(|psc| >::into(**psc) <= val) - .next() - .unwrap() - } - - pub fn compute_min_low_res(val: u32) -> Self { - *[ - HighResolutionControlPrescaler::Div32, - HighResolutionControlPrescaler::Div64, - HighResolutionControlPrescaler::Div128, - ] - .iter() - .skip_while(|psc| >::into(**psc) <= val) - .next() - .unwrap() - } -} - pub(crate) mod sealed { use super::*; - #[cfg(hrtim_v1)] - pub trait HighResolutionCaptureCompare16bitInstance: crate::timer::sealed::HighResolutionControlInstance { - fn set_master_frequency(frequency: Hertz); - - fn set_channel_frequency(channnel: usize, frequency: Hertz); - - /// Set the dead time as a proportion of max_duty - fn set_channel_dead_time(channnel: usize, dead_time: u16); - - // fn enable_outputs(enable: bool); - // - // fn enable_channel(&mut self, channel: usize, enable: bool); - } - pub trait CaptureCompare16bitInstance: crate::timer::sealed::GeneralPurpose16bitInstance { /// Global output enable. Does not do anything on non-advanced timers. fn enable_outputs(&mut self, enable: bool); @@ -200,12 +89,6 @@ pub(crate) mod sealed { } } -#[cfg(hrtim_v1)] -pub trait HighResolutionCaptureCompare16bitInstance: - sealed::HighResolutionCaptureCompare16bitInstance + 'static -{ -} - pub trait CaptureCompare16bitInstance: sealed::CaptureCompare16bitInstance + crate::timer::GeneralPurpose16bitInstance + 'static { @@ -367,88 +250,6 @@ foreach_interrupt! { } }; - - ($inst:ident, hrtim, HRTIM, MASTER, $irq:ident) => { - impl crate::pwm::sealed::HighResolutionCaptureCompare16bitInstance for crate::peripherals::$inst { - fn set_master_frequency(frequency: Hertz) { - use crate::rcc::sealed::RccPeripheral; - use crate::timer::sealed::HighResolutionControlInstance; - - let f = frequency.0; - let timer_f = Self::frequency().0; - let psc_min = (timer_f / f) / (u16::MAX as u32 / 32); - let psc = if Self::regs().isr().read().dllrdy() { - HighResolutionControlPrescaler::compute_min_high_res(psc_min) - } else { - HighResolutionControlPrescaler::compute_min_low_res(psc_min) - }; - - let psc_val: u32 = psc.into(); - let timer_f = 32 * (timer_f / psc_val); - let per: u16 = (timer_f / f) as u16; - - let regs = Self::regs(); - - regs.mcr().modify(|w| w.set_ckpsc(psc.into())); - regs.mper().modify(|w| w.set_mper(per)); - } - - fn set_channel_frequency(channel: usize, frequency: Hertz) { - use crate::rcc::sealed::RccPeripheral; - use crate::timer::sealed::HighResolutionControlInstance; - - let f = frequency.0; - let timer_f = Self::frequency().0; - let psc_min = (timer_f / f) / (u16::MAX as u32 / 32); - let psc = if Self::regs().isr().read().dllrdy() { - HighResolutionControlPrescaler::compute_min_high_res(psc_min) - } else { - HighResolutionControlPrescaler::compute_min_low_res(psc_min) - }; - - let psc_val: u32 = psc.into(); - let timer_f = 32 * (timer_f / psc_val); - let per: u16 = (timer_f / f) as u16; - - let regs = Self::regs(); - - regs.tim(channel).cr().modify(|w| w.set_ckpsc(psc.into())); - regs.tim(channel).per().modify(|w| w.set_per(per)); - } - - fn set_channel_dead_time(channel: usize, dead_time: u16) { - use crate::timer::sealed::HighResolutionControlInstance; - - let regs = Self::regs(); - - let channel_psc: HighResolutionControlPrescaler = regs.tim(channel).cr().read().ckpsc().into(); - let psc_val: u32 = channel_psc.into(); - - - // The dead-time base clock runs 4 times slower than the hrtim base clock - // u9::MAX = 511 - let psc_min = (psc_val * dead_time as u32) / (4 * 511); - let psc = if Self::regs().isr().read().dllrdy() { - HighResolutionControlPrescaler::compute_min_high_res(psc_min) - } else { - HighResolutionControlPrescaler::compute_min_low_res(psc_min) - }; - - let dt_psc_val: u32 = psc.into(); - let dt_val = (dt_psc_val * dead_time as u32) / (4 * psc_val); - - regs.tim(channel).dt().modify(|w| { - w.set_dtprsc(psc.into()); - w.set_dtf(dt_val as u16); - w.set_dtr(dt_val as u16); - }); - } - } - - impl HighResolutionCaptureCompare16bitInstance for crate::peripherals::$inst { - - } - }; } pin_trait!(Channel1Pin, CaptureCompare16bitInstance); From ec787d3518211a91cbecc4056fde88d7d9eb3a34 Mon Sep 17 00:00:00 2001 From: xoviat Date: Fri, 28 Jul 2023 17:27:15 -0500 Subject: [PATCH 45/68] stm32/hrtim: cleanup merge issues --- embassy-stm32/src/hrtim/mod.rs | 2 +- embassy-stm32/src/hrtim/traits.rs | 12 ++++++++---- embassy-stm32/src/timer/mod.rs | 13 ------------- 3 files changed, 9 insertions(+), 18 deletions(-) diff --git a/embassy-stm32/src/hrtim/mod.rs b/embassy-stm32/src/hrtim/mod.rs index ddf8cc2a..3a05719b 100644 --- a/embassy-stm32/src/hrtim/mod.rs +++ b/embassy-stm32/src/hrtim/mod.rs @@ -2,7 +2,7 @@ mod traits; use core::marker::PhantomData; -use embassy_hal_common::{into_ref, PeripheralRef}; +use embassy_hal_internal::{into_ref, PeripheralRef}; #[allow(unused_imports)] use crate::gpio::sealed::{AFType, Pin}; diff --git a/embassy-stm32/src/hrtim/traits.rs b/embassy-stm32/src/hrtim/traits.rs index 16193a45..7f2cedda 100644 --- a/embassy-stm32/src/hrtim/traits.rs +++ b/embassy-stm32/src/hrtim/traits.rs @@ -1,3 +1,4 @@ +use crate::rcc::sealed::RccPeripheral; use crate::time::Hertz; #[derive(Clone, Copy)] @@ -92,7 +93,9 @@ impl HighResolutionControlPrescaler { pub(crate) mod sealed { use super::*; - pub trait HighResolutionCaptureCompare16bitInstance: crate::timer::sealed::HighResolutionControlInstance { + pub trait HighResolutionCaptureCompare16bitInstance: RccPeripheral { + fn regs() -> crate::pac::hrtim::Hrtim; + fn set_master_frequency(frequency: Hertz); fn set_channel_frequency(channnel: usize, frequency: Hertz); @@ -114,9 +117,12 @@ pub trait HighResolutionCaptureCompare16bitInstance: foreach_interrupt! { ($inst:ident, hrtim, HRTIM, MASTER, $irq:ident) => { impl sealed::HighResolutionCaptureCompare16bitInstance for crate::peripherals::$inst { + fn regs() -> crate::pac::hrtim::Hrtim { + crate::pac::$inst + } + fn set_master_frequency(frequency: Hertz) { use crate::rcc::sealed::RccPeripheral; - use crate::timer::sealed::HighResolutionControlInstance; let f = frequency.0; let timer_f = Self::frequency().0; @@ -139,7 +145,6 @@ foreach_interrupt! { fn set_channel_frequency(channel: usize, frequency: Hertz) { use crate::rcc::sealed::RccPeripheral; - use crate::timer::sealed::HighResolutionControlInstance; let f = frequency.0; let timer_f = Self::frequency().0; @@ -161,7 +166,6 @@ foreach_interrupt! { } fn set_channel_dead_time(channel: usize, dead_time: u16) { - use crate::timer::sealed::HighResolutionControlInstance; let regs = Self::regs(); diff --git a/embassy-stm32/src/timer/mod.rs b/embassy-stm32/src/timer/mod.rs index 286ab556..6c2d6d82 100644 --- a/embassy-stm32/src/timer/mod.rs +++ b/embassy-stm32/src/timer/mod.rs @@ -429,17 +429,4 @@ foreach_interrupt! { }; - - ($inst:ident, hrtim, HRTIM, MASTER, $irq:ident) => { - impl sealed::HighResolutionControlInstance for crate::peripherals::$inst { - type Interrupt = crate::interrupt::typelevel::$irq; - - fn regs() -> crate::pac::hrtim::Hrtim { - crate::pac::$inst - } - } - - impl HighResolutionControlInstance for crate::peripherals::$inst { - } - }; } From a8d3bcbb759293f93b7a623910ead15f2cae0a95 Mon Sep 17 00:00:00 2001 From: xoviat Date: Fri, 28 Jul 2023 17:37:14 -0500 Subject: [PATCH 46/68] stm32/hrtim: shorten names --- embassy-stm32/src/hrtim/mod.rs | 64 ++++++++-------- embassy-stm32/src/hrtim/traits.rs | 119 ++++++++++++++---------------- 2 files changed, 88 insertions(+), 95 deletions(-) diff --git a/embassy-stm32/src/hrtim/mod.rs b/embassy-stm32/src/hrtim/mod.rs index 3a05719b..9ed03a21 100644 --- a/embassy-stm32/src/hrtim/mod.rs +++ b/embassy-stm32/src/hrtim/mod.rs @@ -7,7 +7,7 @@ use embassy_hal_internal::{into_ref, PeripheralRef}; #[allow(unused_imports)] use crate::gpio::sealed::{AFType, Pin}; use crate::gpio::AnyPin; -use crate::hrtim::traits::HighResolutionCaptureCompare16bitInstance; +use crate::hrtim::traits::Instance; use crate::time::Hertz; use crate::Peripheral; @@ -20,37 +20,37 @@ pub enum Source { ChE, } -pub struct BurstController { +pub struct BurstController { phantom: PhantomData, } -pub struct Master { +pub struct Master { phantom: PhantomData, } -pub struct ChA { +pub struct ChA { phantom: PhantomData, } -pub struct ChB { +pub struct ChB { phantom: PhantomData, } -pub struct ChC { +pub struct ChC { phantom: PhantomData, } -pub struct ChD { +pub struct ChD { phantom: PhantomData, } -pub struct ChE { +pub struct ChE { phantom: PhantomData, } mod sealed { - use super::HighResolutionCaptureCompare16bitInstance; + use super::Instance; - pub trait AdvancedChannel { + pub trait AdvancedChannel { fn raw() -> usize; } } -pub trait AdvancedChannel: sealed::AdvancedChannel {} +pub trait AdvancedChannel: sealed::AdvancedChannel {} pub struct PwmPin<'d, Perip, Channel> { _pin: PeripheralRef<'d, AnyPin>, @@ -64,7 +64,7 @@ pub struct ComplementaryPwmPin<'d, Perip, Channel> { macro_rules! advanced_channel_impl { ($new_chx:ident, $channel:tt, $ch_num:expr, $pin_trait:ident, $complementary_pin_trait:ident) => { - impl<'d, Perip: HighResolutionCaptureCompare16bitInstance> PwmPin<'d, Perip, $channel> { + impl<'d, Perip: Instance> PwmPin<'d, Perip, $channel> { pub fn $new_chx(pin: impl Peripheral

> + 'd) -> Self { into_ref!(pin); critical_section::with(|_| { @@ -80,7 +80,7 @@ macro_rules! advanced_channel_impl { } } - impl<'d, Perip: HighResolutionCaptureCompare16bitInstance> ComplementaryPwmPin<'d, Perip, $channel> { + impl<'d, Perip: Instance> ComplementaryPwmPin<'d, Perip, $channel> { pub fn $new_chx(pin: impl Peripheral

> + 'd) -> Self { into_ref!(pin); critical_section::with(|_| { @@ -96,12 +96,12 @@ macro_rules! advanced_channel_impl { } } - impl sealed::AdvancedChannel for $channel { + impl sealed::AdvancedChannel for $channel { fn raw() -> usize { $ch_num } } - impl AdvancedChannel for $channel {} + impl AdvancedChannel for $channel {} }; } @@ -112,7 +112,7 @@ advanced_channel_impl!(new_chd, ChD, 3, ChannelDPin, ChannelDComplementaryPin); advanced_channel_impl!(new_che, ChE, 4, ChannelEPin, ChannelEComplementaryPin); /// Struct used to divide a high resolution timer into multiple channels -pub struct AdvancedPwm<'d, T: HighResolutionCaptureCompare16bitInstance> { +pub struct AdvancedPwm<'d, T: Instance> { _inner: PeripheralRef<'d, T>, pub master: Master, pub burst_controller: BurstController, @@ -123,7 +123,7 @@ pub struct AdvancedPwm<'d, T: HighResolutionCaptureCompare16bitInstance> { pub ch_e: ChE, } -impl<'d, T: HighResolutionCaptureCompare16bitInstance> AdvancedPwm<'d, T> { +impl<'d, T: Instance> AdvancedPwm<'d, T> { pub fn new( tim: impl Peripheral

+ 'd, _cha: Option>>, @@ -171,7 +171,7 @@ impl<'d, T: HighResolutionCaptureCompare16bitInstance> AdvancedPwm<'d, T> { } } -impl BurstController { +impl BurstController { pub fn set_source(&mut self, _source: Source) { todo!("burst mode control registers not implemented") } @@ -186,7 +186,7 @@ impl BurstController { /// It is important to remember that in synchronous topologies, energy can flow in reverse during /// light loading conditions, and that the low-side switch must be active for a short time to drive /// a bootstrapped high-side switch. -pub struct BridgeConverter> { +pub struct BridgeConverter> { timer: PhantomData, channel: PhantomData, dead_time: u16, @@ -195,7 +195,7 @@ pub struct BridgeConverter> BridgeConverter { +impl> BridgeConverter { pub fn new(_channel: C, frequency: Hertz) -> Self { use crate::pac::hrtim::vals::{Activeeffect, Inactiveeffect}; @@ -333,14 +333,14 @@ impl> Bridge /// This implementation of a resonsant converter is appropriate for a half or full bridge, /// but does not include secondary rectification, which is appropriate for applications /// with a low-voltage on the secondary side. -pub struct ResonantConverter> { +pub struct ResonantConverter> { timer: PhantomData, channel: PhantomData, min_period: u16, max_period: u16, } -impl> ResonantConverter { +impl> ResonantConverter { pub fn new(_channel: C, min_frequency: Hertz, max_frequency: Hertz) -> Self { T::set_channel_frequency(C::raw(), min_frequency); @@ -397,13 +397,13 @@ impl> Resona } } -pin_trait!(ChannelAPin, HighResolutionCaptureCompare16bitInstance); -pin_trait!(ChannelAComplementaryPin, HighResolutionCaptureCompare16bitInstance); -pin_trait!(ChannelBPin, HighResolutionCaptureCompare16bitInstance); -pin_trait!(ChannelBComplementaryPin, HighResolutionCaptureCompare16bitInstance); -pin_trait!(ChannelCPin, HighResolutionCaptureCompare16bitInstance); -pin_trait!(ChannelCComplementaryPin, HighResolutionCaptureCompare16bitInstance); -pin_trait!(ChannelDPin, HighResolutionCaptureCompare16bitInstance); -pin_trait!(ChannelDComplementaryPin, HighResolutionCaptureCompare16bitInstance); -pin_trait!(ChannelEPin, HighResolutionCaptureCompare16bitInstance); -pin_trait!(ChannelEComplementaryPin, HighResolutionCaptureCompare16bitInstance); +pin_trait!(ChannelAPin, Instance); +pin_trait!(ChannelAComplementaryPin, Instance); +pin_trait!(ChannelBPin, Instance); +pin_trait!(ChannelBComplementaryPin, Instance); +pin_trait!(ChannelCPin, Instance); +pin_trait!(ChannelCComplementaryPin, Instance); +pin_trait!(ChannelDPin, Instance); +pin_trait!(ChannelDComplementaryPin, Instance); +pin_trait!(ChannelEPin, Instance); +pin_trait!(ChannelEComplementaryPin, Instance); diff --git a/embassy-stm32/src/hrtim/traits.rs b/embassy-stm32/src/hrtim/traits.rs index 7f2cedda..158a6886 100644 --- a/embassy-stm32/src/hrtim/traits.rs +++ b/embassy-stm32/src/hrtim/traits.rs @@ -2,7 +2,7 @@ use crate::rcc::sealed::RccPeripheral; use crate::time::Hertz; #[derive(Clone, Copy)] -pub(crate) enum HighResolutionControlPrescaler { +pub(crate) enum Prescaler { Div1, Div2, Div4, @@ -13,87 +13,83 @@ pub(crate) enum HighResolutionControlPrescaler { Div128, } -impl From for u32 { - fn from(val: HighResolutionControlPrescaler) -> Self { +impl From for u32 { + fn from(val: Prescaler) -> Self { match val { - HighResolutionControlPrescaler::Div1 => 1, - HighResolutionControlPrescaler::Div2 => 2, - HighResolutionControlPrescaler::Div4 => 4, - HighResolutionControlPrescaler::Div8 => 8, - HighResolutionControlPrescaler::Div16 => 16, - HighResolutionControlPrescaler::Div32 => 32, - HighResolutionControlPrescaler::Div64 => 64, - HighResolutionControlPrescaler::Div128 => 128, + Prescaler::Div1 => 1, + Prescaler::Div2 => 2, + Prescaler::Div4 => 4, + Prescaler::Div8 => 8, + Prescaler::Div16 => 16, + Prescaler::Div32 => 32, + Prescaler::Div64 => 64, + Prescaler::Div128 => 128, } } } -impl From for u8 { - fn from(val: HighResolutionControlPrescaler) -> Self { +impl From for u8 { + fn from(val: Prescaler) -> Self { match val { - HighResolutionControlPrescaler::Div1 => 0b000, - HighResolutionControlPrescaler::Div2 => 0b001, - HighResolutionControlPrescaler::Div4 => 0b010, - HighResolutionControlPrescaler::Div8 => 0b011, - HighResolutionControlPrescaler::Div16 => 0b100, - HighResolutionControlPrescaler::Div32 => 0b101, - HighResolutionControlPrescaler::Div64 => 0b110, - HighResolutionControlPrescaler::Div128 => 0b111, + Prescaler::Div1 => 0b000, + Prescaler::Div2 => 0b001, + Prescaler::Div4 => 0b010, + Prescaler::Div8 => 0b011, + Prescaler::Div16 => 0b100, + Prescaler::Div32 => 0b101, + Prescaler::Div64 => 0b110, + Prescaler::Div128 => 0b111, } } } -impl From for HighResolutionControlPrescaler { +impl From for Prescaler { fn from(val: u8) -> Self { match val { - 0b000 => HighResolutionControlPrescaler::Div1, - 0b001 => HighResolutionControlPrescaler::Div2, - 0b010 => HighResolutionControlPrescaler::Div4, - 0b011 => HighResolutionControlPrescaler::Div8, - 0b100 => HighResolutionControlPrescaler::Div16, - 0b101 => HighResolutionControlPrescaler::Div32, - 0b110 => HighResolutionControlPrescaler::Div64, - 0b111 => HighResolutionControlPrescaler::Div128, + 0b000 => Prescaler::Div1, + 0b001 => Prescaler::Div2, + 0b010 => Prescaler::Div4, + 0b011 => Prescaler::Div8, + 0b100 => Prescaler::Div16, + 0b101 => Prescaler::Div32, + 0b110 => Prescaler::Div64, + 0b111 => Prescaler::Div128, _ => unreachable!(), } } } -impl HighResolutionControlPrescaler { +impl Prescaler { pub fn compute_min_high_res(val: u32) -> Self { *[ - HighResolutionControlPrescaler::Div1, - HighResolutionControlPrescaler::Div2, - HighResolutionControlPrescaler::Div4, - HighResolutionControlPrescaler::Div8, - HighResolutionControlPrescaler::Div16, - HighResolutionControlPrescaler::Div32, - HighResolutionControlPrescaler::Div64, - HighResolutionControlPrescaler::Div128, + Prescaler::Div1, + Prescaler::Div2, + Prescaler::Div4, + Prescaler::Div8, + Prescaler::Div16, + Prescaler::Div32, + Prescaler::Div64, + Prescaler::Div128, ] .iter() - .skip_while(|psc| >::into(**psc) <= val) + .skip_while(|psc| >::into(**psc) <= val) .next() .unwrap() } pub fn compute_min_low_res(val: u32) -> Self { - *[ - HighResolutionControlPrescaler::Div32, - HighResolutionControlPrescaler::Div64, - HighResolutionControlPrescaler::Div128, - ] - .iter() - .skip_while(|psc| >::into(**psc) <= val) - .next() - .unwrap() + *[Prescaler::Div32, Prescaler::Div64, Prescaler::Div128] + .iter() + .skip_while(|psc| >::into(**psc) <= val) + .next() + .unwrap() } } pub(crate) mod sealed { use super::*; - pub trait HighResolutionCaptureCompare16bitInstance: RccPeripheral { + pub trait Instance: RccPeripheral { fn regs() -> crate::pac::hrtim::Hrtim; fn set_master_frequency(frequency: Hertz); @@ -109,14 +105,11 @@ pub(crate) mod sealed { } } -pub trait HighResolutionCaptureCompare16bitInstance: - sealed::HighResolutionCaptureCompare16bitInstance + 'static -{ -} +pub trait Instance: sealed::Instance + 'static {} foreach_interrupt! { ($inst:ident, hrtim, HRTIM, MASTER, $irq:ident) => { - impl sealed::HighResolutionCaptureCompare16bitInstance for crate::peripherals::$inst { + impl sealed::Instance for crate::peripherals::$inst { fn regs() -> crate::pac::hrtim::Hrtim { crate::pac::$inst } @@ -128,9 +121,9 @@ foreach_interrupt! { let timer_f = Self::frequency().0; let psc_min = (timer_f / f) / (u16::MAX as u32 / 32); let psc = if Self::regs().isr().read().dllrdy() { - HighResolutionControlPrescaler::compute_min_high_res(psc_min) + Prescaler::compute_min_high_res(psc_min) } else { - HighResolutionControlPrescaler::compute_min_low_res(psc_min) + Prescaler::compute_min_low_res(psc_min) }; let psc_val: u32 = psc.into(); @@ -150,9 +143,9 @@ foreach_interrupt! { let timer_f = Self::frequency().0; let psc_min = (timer_f / f) / (u16::MAX as u32 / 32); let psc = if Self::regs().isr().read().dllrdy() { - HighResolutionControlPrescaler::compute_min_high_res(psc_min) + Prescaler::compute_min_high_res(psc_min) } else { - HighResolutionControlPrescaler::compute_min_low_res(psc_min) + Prescaler::compute_min_low_res(psc_min) }; let psc_val: u32 = psc.into(); @@ -169,7 +162,7 @@ foreach_interrupt! { let regs = Self::regs(); - let channel_psc: HighResolutionControlPrescaler = regs.tim(channel).cr().read().ckpsc().into(); + let channel_psc: Prescaler = regs.tim(channel).cr().read().ckpsc().into(); let psc_val: u32 = channel_psc.into(); @@ -177,9 +170,9 @@ foreach_interrupt! { // u9::MAX = 511 let psc_min = (psc_val * dead_time as u32) / (4 * 511); let psc = if Self::regs().isr().read().dllrdy() { - HighResolutionControlPrescaler::compute_min_high_res(psc_min) + Prescaler::compute_min_high_res(psc_min) } else { - HighResolutionControlPrescaler::compute_min_low_res(psc_min) + Prescaler::compute_min_low_res(psc_min) }; let dt_psc_val: u32 = psc.into(); @@ -193,7 +186,7 @@ foreach_interrupt! { } } - impl HighResolutionCaptureCompare16bitInstance for crate::peripherals::$inst { + impl Instance for crate::peripherals::$inst { } }; From 5bb5654d847b8d7a15f242d4b4078369a1b7285f Mon Sep 17 00:00:00 2001 From: xoviat Date: Fri, 28 Jul 2023 17:39:01 -0500 Subject: [PATCH 47/68] stm32/hrtim: pub instance --- embassy-stm32/src/hrtim/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/embassy-stm32/src/hrtim/mod.rs b/embassy-stm32/src/hrtim/mod.rs index 9ed03a21..a930ff73 100644 --- a/embassy-stm32/src/hrtim/mod.rs +++ b/embassy-stm32/src/hrtim/mod.rs @@ -3,11 +3,11 @@ mod traits; use core::marker::PhantomData; use embassy_hal_internal::{into_ref, PeripheralRef}; +pub use traits::Instance; #[allow(unused_imports)] use crate::gpio::sealed::{AFType, Pin}; use crate::gpio::AnyPin; -use crate::hrtim::traits::Instance; use crate::time::Hertz; use crate::Peripheral; From 7ed9e29326e42bf286b4f7c5883ef216cfb21531 Mon Sep 17 00:00:00 2001 From: Derek Hageman Date: Tue, 25 Jul 2023 15:54:33 -0600 Subject: [PATCH 48/68] rp: add async flash Implement an async flash mode using the XIP background best effort read interface. Only reads are actually async, write and erase remain blocking. --- embassy-boot/rp/src/lib.rs | 14 +- embassy-rp/Cargo.toml | 3 +- embassy-rp/src/flash.rs | 203 +++++++++++++++++++++- examples/boot/application/rp/src/bin/a.rs | 4 +- examples/rp/src/bin/flash.rs | 42 ++++- tests/rp/src/bin/flash.rs | 14 +- 6 files changed, 254 insertions(+), 26 deletions(-) diff --git a/embassy-boot/rp/src/lib.rs b/embassy-boot/rp/src/lib.rs index 25329f9e..35fc104e 100644 --- a/embassy-boot/rp/src/lib.rs +++ b/embassy-boot/rp/src/lib.rs @@ -6,7 +6,7 @@ mod fmt; #[cfg(feature = "nightly")] pub use embassy_boot::FirmwareUpdater; pub use embassy_boot::{AlignedBuffer, BlockingFirmwareUpdater, BootLoaderConfig, FirmwareUpdaterConfig, State}; -use embassy_rp::flash::{Flash, ERASE_SIZE}; +use embassy_rp::flash::{Blocking, Flash, ERASE_SIZE}; use embassy_rp::peripherals::{FLASH, WATCHDOG}; use embassy_rp::watchdog::Watchdog; use embassy_time::Duration; @@ -58,14 +58,14 @@ impl /// A flash implementation that will feed a watchdog when touching flash. pub struct WatchdogFlash<'d, const SIZE: usize> { - flash: Flash<'d, FLASH, SIZE>, + flash: Flash<'d, FLASH, Blocking, SIZE>, watchdog: Watchdog, } impl<'d, const SIZE: usize> WatchdogFlash<'d, SIZE> { /// Start a new watchdog with a given flash and watchdog peripheral and a timeout pub fn start(flash: FLASH, watchdog: WATCHDOG, timeout: Duration) -> Self { - let flash: Flash<'_, FLASH, SIZE> = Flash::new(flash); + let flash = Flash::<_, Blocking, SIZE>::new(flash); let mut watchdog = Watchdog::new(watchdog); watchdog.start(timeout); Self { flash, watchdog } @@ -73,12 +73,12 @@ impl<'d, const SIZE: usize> WatchdogFlash<'d, SIZE> { } impl<'d, const SIZE: usize> ErrorType for WatchdogFlash<'d, SIZE> { - type Error = as ErrorType>::Error; + type Error = as ErrorType>::Error; } impl<'d, const SIZE: usize> NorFlash for WatchdogFlash<'d, SIZE> { - const WRITE_SIZE: usize = as NorFlash>::WRITE_SIZE; - const ERASE_SIZE: usize = as NorFlash>::ERASE_SIZE; + const WRITE_SIZE: usize = as NorFlash>::WRITE_SIZE; + const ERASE_SIZE: usize = as NorFlash>::ERASE_SIZE; fn erase(&mut self, from: u32, to: u32) -> Result<(), Self::Error> { self.watchdog.feed(); @@ -91,7 +91,7 @@ impl<'d, const SIZE: usize> NorFlash for WatchdogFlash<'d, SIZE> { } impl<'d, const SIZE: usize> ReadNorFlash for WatchdogFlash<'d, SIZE> { - const READ_SIZE: usize = as ReadNorFlash>::READ_SIZE; + const READ_SIZE: usize = as ReadNorFlash>::READ_SIZE; fn read(&mut self, offset: u32, data: &mut [u8]) -> Result<(), Self::Error> { self.watchdog.feed(); self.flash.read(offset, data) diff --git a/embassy-rp/Cargo.toml b/embassy-rp/Cargo.toml index b53c7a01..6310ffb6 100644 --- a/embassy-rp/Cargo.toml +++ b/embassy-rp/Cargo.toml @@ -48,7 +48,7 @@ boot2-w25x10cl = [] run-from-ram = [] # Enable nightly-only features -nightly = ["embedded-hal-1", "embedded-hal-async", "embassy-embedded-hal/nightly", "dep:embassy-usb-driver", "dep:embedded-io"] +nightly = ["embedded-hal-1", "embedded-hal-async", "embedded-storage-async", "embassy-embedded-hal/nightly", "dep:embassy-usb-driver", "dep:embedded-io"] # Implement embedded-hal 1.0 alpha traits. # Implement embedded-hal-async traits if `nightly` is set as well. @@ -73,6 +73,7 @@ futures = { version = "0.3.17", default-features = false, features = ["async-awa chrono = { version = "0.4", default-features = false, optional = true } embedded-io = { version = "0.4.0", features = ["async"], optional = true } embedded-storage = { version = "0.3" } +embedded-storage-async = { version = "0.4.0", optional = true } rand_core = "0.6.4" fixed = "1.23.1" diff --git a/embassy-rp/src/flash.rs b/embassy-rp/src/flash.rs index 0ed6808e..70d86731 100644 --- a/embassy-rp/src/flash.rs +++ b/embassy-rp/src/flash.rs @@ -1,11 +1,15 @@ +use core::future::Future; use core::marker::PhantomData; +use core::pin::Pin; +use core::task::{Context, Poll}; -use embassy_hal_internal::Peripheral; +use embassy_hal_internal::{into_ref, Peripheral, PeripheralRef}; use embedded_storage::nor_flash::{ check_erase, check_read, check_write, ErrorType, MultiwriteNorFlash, NorFlash, NorFlashError, NorFlashErrorKind, ReadNorFlash, }; +use crate::dma::{AnyChannel, Channel, Transfer}; use crate::pac; use crate::peripherals::FLASH; @@ -24,6 +28,7 @@ pub const PAGE_SIZE: usize = 256; pub const WRITE_SIZE: usize = 1; pub const READ_SIZE: usize = 1; pub const ERASE_SIZE: usize = 4096; +pub const ASYNC_READ_SIZE: usize = 4; /// Error type for NVMC operations. #[derive(Debug, Copy, Clone, PartialEq, Eq)] @@ -57,13 +62,46 @@ impl NorFlashError for Error { } } -pub struct Flash<'d, T: Instance, const FLASH_SIZE: usize>(PhantomData<&'d mut T>); +/// Future that waits for completion of a background read +#[must_use = "futures do nothing unless you `.await` or poll them"] +pub struct BackgroundRead<'a, 'd, T: Instance, const FLASH_SIZE: usize> { + flash: PhantomData<&'a mut Flash<'d, T, Async, FLASH_SIZE>>, + transfer: Transfer<'a, AnyChannel>, +} -impl<'d, T: Instance, const FLASH_SIZE: usize> Flash<'d, T, FLASH_SIZE> { - pub fn new(_flash: impl Peripheral

+ 'd) -> Self { - Self(PhantomData) +impl<'a, 'd, T: Instance, const FLASH_SIZE: usize> Future for BackgroundRead<'a, 'd, T, FLASH_SIZE> { + type Output = (); + fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { + Pin::new(&mut self.transfer).poll(cx) } +} +impl<'a, 'd, T: Instance, const FLASH_SIZE: usize> Drop for BackgroundRead<'a, 'd, T, FLASH_SIZE> { + fn drop(&mut self) { + if pac::XIP_CTRL.stream_ctr().read().0 == 0 { + return; + } + pac::XIP_CTRL + .stream_ctr() + .write_value(pac::xip_ctrl::regs::StreamCtr(0)); + core::sync::atomic::compiler_fence(core::sync::atomic::Ordering::SeqCst); + // Errata RP2040-E8: Perform an uncached read to make sure there's not a transfer in + // flight that might effect an address written to start a new transfer. This stalls + // until after any transfer is complete, so the address will not change anymore. + const XIP_NOCACHE_NOALLOC_BASE: *const u32 = 0x13000000 as *const _; + unsafe { + core::ptr::read_volatile(XIP_NOCACHE_NOALLOC_BASE); + } + core::sync::atomic::compiler_fence(core::sync::atomic::Ordering::SeqCst); + } +} + +pub struct Flash<'d, T: Instance, M: Mode, const FLASH_SIZE: usize> { + dma: Option>, + phantom: PhantomData<(&'d mut T, M)>, +} + +impl<'d, T: Instance, M: Mode, const FLASH_SIZE: usize> Flash<'d, T, M, FLASH_SIZE> { pub fn read(&mut self, offset: u32, bytes: &mut [u8]) -> Result<(), Error> { trace!( "Reading from 0x{:x} to 0x{:x}", @@ -182,6 +220,8 @@ impl<'d, T: Instance, const FLASH_SIZE: usize> Flash<'d, T, FLASH_SIZE> { let ch = crate::pac::DMA.ch(n); while ch.read_addr().read() < SRAM_LOWER && ch.ctrl_trig().read().busy() {} } + // Wait for completion of any background reads + while pac::XIP_CTRL.stream_ctr().read().0 > 0 {} // Run our flash operation in RAM operation(); @@ -210,11 +250,73 @@ impl<'d, T: Instance, const FLASH_SIZE: usize> Flash<'d, T, FLASH_SIZE> { } } -impl<'d, T: Instance, const FLASH_SIZE: usize> ErrorType for Flash<'d, T, FLASH_SIZE> { +impl<'d, T: Instance, const FLASH_SIZE: usize> Flash<'d, T, Blocking, FLASH_SIZE> { + pub fn new(_flash: impl Peripheral

+ 'd) -> Self { + Self { + dma: None, + phantom: PhantomData, + } + } +} + +impl<'d, T: Instance, const FLASH_SIZE: usize> Flash<'d, T, Async, FLASH_SIZE> { + pub fn new(_flash: impl Peripheral

+ 'd, dma: impl Peripheral

+ 'd) -> Self { + into_ref!(dma); + Self { + dma: Some(dma.map_into()), + phantom: PhantomData, + } + } + + pub fn background_read<'a>( + &'a mut self, + offset: u32, + data: &'a mut [u32], + ) -> Result, Error> { + trace!( + "Reading in background from 0x{:x} to 0x{:x}", + FLASH_BASE as u32 + offset, + FLASH_BASE as u32 + offset + (data.len() * 4) as u32 + ); + // Can't use check_read because we need to enforce 4-byte alignment + let offset = offset as usize; + let length = data.len() * 4; + if length > self.capacity() || offset > self.capacity() - length { + return Err(Error::OutOfBounds); + } + if offset % 4 != 0 { + return Err(Error::Unaligned); + } + + while !pac::XIP_CTRL.stat().read().fifo_empty() { + pac::XIP_CTRL.stream_fifo().read(); + } + + pac::XIP_CTRL + .stream_addr() + .write_value(pac::xip_ctrl::regs::StreamAddr(FLASH_BASE as u32 + offset as u32)); + pac::XIP_CTRL + .stream_ctr() + .write_value(pac::xip_ctrl::regs::StreamCtr(data.len() as u32)); + + // Use the XIP AUX bus port, rather than the FIFO register access (e.x. + // pac::XIP_CTRL.stream_fifo().as_ptr()) to avoid DMA stalling on + // general XIP access. + const XIP_AUX_BASE: *const u32 = 0x50400000 as *const _; + let transfer = unsafe { crate::dma::read(self.dma.as_mut().unwrap(), XIP_AUX_BASE, data, 37) }; + + Ok(BackgroundRead { + flash: PhantomData, + transfer, + }) + } +} + +impl<'d, T: Instance, M: Mode, const FLASH_SIZE: usize> ErrorType for Flash<'d, T, M, FLASH_SIZE> { type Error = Error; } -impl<'d, T: Instance, const FLASH_SIZE: usize> ReadNorFlash for Flash<'d, T, FLASH_SIZE> { +impl<'d, T: Instance, M: Mode, const FLASH_SIZE: usize> ReadNorFlash for Flash<'d, T, M, FLASH_SIZE> { const READ_SIZE: usize = READ_SIZE; fn read(&mut self, offset: u32, bytes: &mut [u8]) -> Result<(), Self::Error> { @@ -226,9 +328,9 @@ impl<'d, T: Instance, const FLASH_SIZE: usize> ReadNorFlash for Flash<'d, T, FLA } } -impl<'d, T: Instance, const FLASH_SIZE: usize> MultiwriteNorFlash for Flash<'d, T, FLASH_SIZE> {} +impl<'d, T: Instance, M: Mode, const FLASH_SIZE: usize> MultiwriteNorFlash for Flash<'d, T, M, FLASH_SIZE> {} -impl<'d, T: Instance, const FLASH_SIZE: usize> NorFlash for Flash<'d, T, FLASH_SIZE> { +impl<'d, T: Instance, M: Mode, const FLASH_SIZE: usize> NorFlash for Flash<'d, T, M, FLASH_SIZE> { const WRITE_SIZE: usize = WRITE_SIZE; const ERASE_SIZE: usize = ERASE_SIZE; @@ -242,6 +344,74 @@ impl<'d, T: Instance, const FLASH_SIZE: usize> NorFlash for Flash<'d, T, FLASH_S } } +#[cfg(feature = "nightly")] +impl<'d, T: Instance, const FLASH_SIZE: usize> embedded_storage_async::nor_flash::ReadNorFlash + for Flash<'d, T, Async, FLASH_SIZE> +{ + const READ_SIZE: usize = ASYNC_READ_SIZE; + + async fn read(&mut self, offset: u32, bytes: &mut [u8]) -> Result<(), Self::Error> { + use core::mem::MaybeUninit; + + // Checked early to simplify address validity checks + if bytes.len() % 4 != 0 { + return Err(Error::Unaligned); + } + + // If the destination address is already aligned, then we can just DMA directly + if (bytes.as_ptr() as u32) % 4 == 0 { + // Safety: alignment and size have been checked for compatibility + let mut buf: &mut [u32] = + unsafe { core::slice::from_raw_parts_mut(bytes.as_mut_ptr() as *mut u32, bytes.len() / 4) }; + self.background_read(offset, &mut buf)?.await; + return Ok(()); + } + + // Destination address is unaligned, so use an intermediate buffer + const REALIGN_CHUNK: usize = PAGE_SIZE; + // Safety: MaybeUninit requires no initialization + let mut buf: [MaybeUninit; REALIGN_CHUNK / 4] = unsafe { MaybeUninit::uninit().assume_init() }; + let mut chunk_offset: usize = 0; + while chunk_offset < bytes.len() { + let chunk_size = (bytes.len() - chunk_offset).min(REALIGN_CHUNK); + let buf = &mut buf[..(chunk_size / 4)]; + + // Safety: this is written to completely by DMA before any reads + let buf = unsafe { &mut *(buf as *mut [MaybeUninit] as *mut [u32]) }; + self.background_read(offset + chunk_offset as u32, buf)?.await; + + // Safety: [u8] has more relaxed alignment and size requirements than [u32], so this is just aliasing + let buf = unsafe { core::slice::from_raw_parts(buf.as_ptr() as *const _, buf.len() * 4) }; + bytes[chunk_offset..(chunk_offset + chunk_size)].copy_from_slice(&buf[..chunk_size]); + + chunk_offset += chunk_size; + } + + Ok(()) + } + + fn capacity(&self) -> usize { + self.capacity() + } +} + +#[cfg(feature = "nightly")] +impl<'d, T: Instance, const FLASH_SIZE: usize> embedded_storage_async::nor_flash::NorFlash + for Flash<'d, T, Async, FLASH_SIZE> +{ + const WRITE_SIZE: usize = WRITE_SIZE; + + const ERASE_SIZE: usize = ERASE_SIZE; + + async fn erase(&mut self, from: u32, to: u32) -> Result<(), Self::Error> { + self.erase(from, to) + } + + async fn write(&mut self, offset: u32, bytes: &[u8]) -> Result<(), Self::Error> { + self.write(offset, bytes) + } +} + #[allow(dead_code)] mod ram_helpers { use core::marker::PhantomData; @@ -699,9 +869,24 @@ mod ram_helpers { mod sealed { pub trait Instance {} + pub trait Mode {} } pub trait Instance: sealed::Instance {} +pub trait Mode: sealed::Mode {} impl sealed::Instance for FLASH {} impl Instance for FLASH {} + +macro_rules! impl_mode { + ($name:ident) => { + impl sealed::Mode for $name {} + impl Mode for $name {} + }; +} + +pub struct Blocking; +pub struct Async; + +impl_mode!(Blocking); +impl_mode!(Async); diff --git a/examples/boot/application/rp/src/bin/a.rs b/examples/boot/application/rp/src/bin/a.rs index c8497494..b5e1950c 100644 --- a/examples/boot/application/rp/src/bin/a.rs +++ b/examples/boot/application/rp/src/bin/a.rs @@ -7,7 +7,7 @@ use core::cell::RefCell; use defmt_rtt as _; use embassy_boot_rp::*; use embassy_executor::Spawner; -use embassy_rp::flash::Flash; +use embassy_rp::flash::{self, Flash}; use embassy_rp::gpio::{Level, Output}; use embassy_rp::watchdog::Watchdog; use embassy_sync::blocking_mutex::Mutex; @@ -34,7 +34,7 @@ async fn main(_s: Spawner) { let mut watchdog = Watchdog::new(p.WATCHDOG); watchdog.start(Duration::from_secs(8)); - let flash: Flash<_, FLASH_SIZE> = Flash::new(p.FLASH); + let flash = Flash::<_, flash::Blocking, FLASH_SIZE>::new(p.FLASH); let flash = Mutex::new(RefCell::new(flash)); let config = FirmwareUpdaterConfig::from_linkerfile_blocking(&flash); diff --git a/examples/rp/src/bin/flash.rs b/examples/rp/src/bin/flash.rs index 4c4982ac..88bb931d 100644 --- a/examples/rp/src/bin/flash.rs +++ b/examples/rp/src/bin/flash.rs @@ -6,7 +6,7 @@ use defmt::*; use embassy_executor::Spawner; -use embassy_rp::flash::{ERASE_SIZE, FLASH_BASE}; +use embassy_rp::flash::{Async, ERASE_SIZE, FLASH_BASE}; use embassy_rp::peripherals::FLASH; use embassy_time::{Duration, Timer}; use {defmt_rtt as _, panic_probe as _}; @@ -25,7 +25,7 @@ async fn main(_spawner: Spawner) { // https://github.com/knurling-rs/defmt/pull/683 Timer::after(Duration::from_millis(10)).await; - let mut flash = embassy_rp::flash::Flash::<_, FLASH_SIZE>::new(p.FLASH); + let mut flash = embassy_rp::flash::Flash::<_, Async, FLASH_SIZE>::new(p.FLASH, p.DMA_CH0); // Get JEDEC id let jedec = flash.jedec_id().unwrap(); @@ -40,10 +40,12 @@ async fn main(_spawner: Spawner) { multiwrite_bytes(&mut flash, ERASE_SIZE as u32); + background_read(&mut flash, (ERASE_SIZE * 2) as u32).await; + loop {} } -fn multiwrite_bytes(flash: &mut embassy_rp::flash::Flash<'_, FLASH, FLASH_SIZE>, offset: u32) { +fn multiwrite_bytes(flash: &mut embassy_rp::flash::Flash<'_, FLASH, Async, FLASH_SIZE>, offset: u32) { info!(">>>> [multiwrite_bytes]"); let mut read_buf = [0u8; ERASE_SIZE]; defmt::unwrap!(flash.read(ADDR_OFFSET + offset, &mut read_buf)); @@ -71,7 +73,7 @@ fn multiwrite_bytes(flash: &mut embassy_rp::flash::Flash<'_, FLASH, FLASH_SIZE>, } } -fn erase_write_sector(flash: &mut embassy_rp::flash::Flash<'_, FLASH, FLASH_SIZE>, offset: u32) { +fn erase_write_sector(flash: &mut embassy_rp::flash::Flash<'_, FLASH, Async, FLASH_SIZE>, offset: u32) { info!(">>>> [erase_write_sector]"); let mut buf = [0u8; ERASE_SIZE]; defmt::unwrap!(flash.read(ADDR_OFFSET + offset, &mut buf)); @@ -99,3 +101,35 @@ fn erase_write_sector(flash: &mut embassy_rp::flash::Flash<'_, FLASH, FLASH_SIZE defmt::panic!("unexpected"); } } + +async fn background_read(flash: &mut embassy_rp::flash::Flash<'_, FLASH, Async, FLASH_SIZE>, offset: u32) { + info!(">>>> [background_read]"); + + let mut buf = [0u32; 8]; + defmt::unwrap!(flash.background_read(ADDR_OFFSET + offset, &mut buf)).await; + + info!("Addr of flash block is {:x}", ADDR_OFFSET + offset + FLASH_BASE as u32); + info!("Contents start with {=u32:x}", buf[0]); + + defmt::unwrap!(flash.erase(ADDR_OFFSET + offset, ADDR_OFFSET + offset + ERASE_SIZE as u32)); + + defmt::unwrap!(flash.background_read(ADDR_OFFSET + offset, &mut buf)).await; + info!("Contents after erase starts with {=u32:x}", buf[0]); + if buf.iter().any(|x| *x != 0xFFFFFFFF) { + defmt::panic!("unexpected"); + } + + for b in buf.iter_mut() { + *b = 0xDABA1234; + } + + defmt::unwrap!(flash.write(ADDR_OFFSET + offset, unsafe { + core::slice::from_raw_parts(buf.as_ptr() as *const u8, buf.len() * 4) + })); + + defmt::unwrap!(flash.background_read(ADDR_OFFSET + offset, &mut buf)).await; + info!("Contents after write starts with {=u32:x}", buf[0]); + if buf.iter().any(|x| *x != 0xDABA1234) { + defmt::panic!("unexpected"); + } +} diff --git a/tests/rp/src/bin/flash.rs b/tests/rp/src/bin/flash.rs index cf9b86df..c31d6dec 100644 --- a/tests/rp/src/bin/flash.rs +++ b/tests/rp/src/bin/flash.rs @@ -6,11 +6,11 @@ mod common; use defmt::*; use embassy_executor::Spawner; -use embassy_rp::flash::{ERASE_SIZE, FLASH_BASE}; +use embassy_rp::flash::{Async, ERASE_SIZE, FLASH_BASE}; use embassy_time::{Duration, Timer}; use {defmt_rtt as _, panic_probe as _}; -const ADDR_OFFSET: u32 = 0x4000; +const ADDR_OFFSET: u32 = 0x8000; #[embassy_executor::main] async fn main(_spawner: Spawner) { @@ -23,7 +23,7 @@ async fn main(_spawner: Spawner) { // https://github.com/knurling-rs/defmt/pull/683 Timer::after(Duration::from_millis(10)).await; - let mut flash = embassy_rp::flash::Flash::<_, { 2 * 1024 * 1024 }>::new(p.FLASH); + let mut flash = embassy_rp::flash::Flash::<_, Async, { 2 * 1024 * 1024 }>::new(p.FLASH, p.DMA_CH0); // Get JEDEC id let jedec = defmt::unwrap!(flash.jedec_id()); @@ -60,6 +60,14 @@ async fn main(_spawner: Spawner) { defmt::panic!("unexpected"); } + let mut buf = [0u32; ERASE_SIZE / 4]; + + defmt::unwrap!(flash.background_read(ADDR_OFFSET, &mut buf)).await; + info!("Contents after write starts with {=u32:x}", buf[0]); + if buf.iter().any(|x| *x != 0xDADADADA) { + defmt::panic!("unexpected"); + } + info!("Test OK"); cortex_m::asm::bkpt(); } From 0d7b005252a0168c779292bf9457f1a654e42386 Mon Sep 17 00:00:00 2001 From: xoviat Date: Sat, 29 Jul 2023 12:01:32 -0500 Subject: [PATCH 49/68] stm32/pwm: add output type control --- embassy-stm32/src/gpio.rs | 15 +++++++++++++++ embassy-stm32/src/timer/complementary_pwm.rs | 16 ++++++++-------- embassy-stm32/src/timer/simple_pwm.rs | 6 +++--- examples/stm32f4/src/bin/pwm.rs | 3 ++- examples/stm32f4/src/bin/pwm_complementary.rs | 5 +++-- examples/stm32g4/src/bin/pwm.rs | 3 ++- examples/stm32h7/src/bin/pwm.rs | 3 ++- 7 files changed, 35 insertions(+), 16 deletions(-) diff --git a/embassy-stm32/src/gpio.rs b/embassy-stm32/src/gpio.rs index cda59714..d2e61d23 100644 --- a/embassy-stm32/src/gpio.rs +++ b/embassy-stm32/src/gpio.rs @@ -4,6 +4,7 @@ use core::convert::Infallible; use embassy_hal_internal::{impl_peripheral, into_ref, PeripheralRef}; use crate::pac::gpio::{self, vals}; +use crate::usb_otg::Out; use crate::{pac, peripherals, Peripheral}; /// GPIO flexible pin. @@ -502,6 +503,20 @@ impl<'d, T: Pin> OutputOpenDrain<'d, T> { } } +pub enum OutputType { + PushPull, + OpenDrain, +} + +impl From for sealed::AFType { + fn from(value: OutputType) -> Self { + match value { + OutputType::OpenDrain => sealed::AFType::OutputOpenDrain, + OutputType::PushPull => sealed::AFType::OutputPushPull, + } + } +} + pub(crate) mod sealed { use super::*; diff --git a/embassy-stm32/src/timer/complementary_pwm.rs b/embassy-stm32/src/timer/complementary_pwm.rs index 64bb32c3..48cb610f 100644 --- a/embassy-stm32/src/timer/complementary_pwm.rs +++ b/embassy-stm32/src/timer/complementary_pwm.rs @@ -7,7 +7,7 @@ use super::simple_pwm::*; use super::*; #[allow(unused_imports)] use crate::gpio::sealed::{AFType, Pin}; -use crate::gpio::AnyPin; +use crate::gpio::{AnyPin, OutputType}; use crate::time::Hertz; use crate::Peripheral; @@ -17,13 +17,13 @@ pub struct ComplementaryPwmPin<'d, Perip, Channel> { } macro_rules! complementary_channel_impl { - ($new_chx:ident, $channel:ident, $pin_trait:ident, $complementary_pin_trait:ident) => { + ($new_chx:ident, $channel:ident, $pin_trait:ident) => { impl<'d, Perip: CaptureCompare16bitInstance> ComplementaryPwmPin<'d, Perip, $channel> { - pub fn $new_chx(pin: impl Peripheral

> + 'd) -> Self { + pub fn $new_chx(pin: impl Peripheral

> + 'd, output_type: OutputType) -> Self { into_ref!(pin); critical_section::with(|_| { pin.set_low(); - pin.set_as_af(pin.af_num(), AFType::OutputPushPull); + pin.set_as_af(pin.af_num(), output_type.into()); #[cfg(gpio_v2)] pin.set_speed(crate::gpio::Speed::VeryHigh); }); @@ -36,10 +36,10 @@ macro_rules! complementary_channel_impl { }; } -complementary_channel_impl!(new_ch1, Ch1, Channel1Pin, Channel1ComplementaryPin); -complementary_channel_impl!(new_ch2, Ch2, Channel2Pin, Channel2ComplementaryPin); -complementary_channel_impl!(new_ch3, Ch3, Channel3Pin, Channel3ComplementaryPin); -complementary_channel_impl!(new_ch4, Ch4, Channel4Pin, Channel4ComplementaryPin); +complementary_channel_impl!(new_ch1, Ch1, Channel1ComplementaryPin); +complementary_channel_impl!(new_ch2, Ch2, Channel2ComplementaryPin); +complementary_channel_impl!(new_ch3, Ch3, Channel3ComplementaryPin); +complementary_channel_impl!(new_ch4, Ch4, Channel4ComplementaryPin); pub struct ComplementaryPwm<'d, T> { inner: PeripheralRef<'d, T>, diff --git a/embassy-stm32/src/timer/simple_pwm.rs b/embassy-stm32/src/timer/simple_pwm.rs index 51479693..e0a81792 100644 --- a/embassy-stm32/src/timer/simple_pwm.rs +++ b/embassy-stm32/src/timer/simple_pwm.rs @@ -5,7 +5,7 @@ use embassy_hal_internal::{into_ref, PeripheralRef}; use super::*; #[allow(unused_imports)] use crate::gpio::sealed::{AFType, Pin}; -use crate::gpio::AnyPin; +use crate::gpio::{AnyPin, OutputType}; use crate::time::Hertz; use crate::Peripheral; @@ -22,11 +22,11 @@ pub struct PwmPin<'d, Perip, Channel> { macro_rules! channel_impl { ($new_chx:ident, $channel:ident, $pin_trait:ident) => { impl<'d, Perip: CaptureCompare16bitInstance> PwmPin<'d, Perip, $channel> { - pub fn $new_chx(pin: impl Peripheral

> + 'd) -> Self { + pub fn $new_chx(pin: impl Peripheral

> + 'd, output_type: OutputType) -> Self { into_ref!(pin); critical_section::with(|_| { pin.set_low(); - pin.set_as_af(pin.af_num(), AFType::OutputPushPull); + pin.set_as_af(pin.af_num(), output_type.into()); #[cfg(gpio_v2)] pin.set_speed(crate::gpio::Speed::VeryHigh); }); diff --git a/examples/stm32f4/src/bin/pwm.rs b/examples/stm32f4/src/bin/pwm.rs index 4f130c26..1013a844 100644 --- a/examples/stm32f4/src/bin/pwm.rs +++ b/examples/stm32f4/src/bin/pwm.rs @@ -4,6 +4,7 @@ use defmt::*; use embassy_executor::Spawner; +use embassy_stm32::gpio::OutputType; use embassy_stm32::time::khz; use embassy_stm32::timer::simple_pwm::{PwmPin, SimplePwm}; use embassy_stm32::timer::Channel; @@ -15,7 +16,7 @@ async fn main(_spawner: Spawner) { let p = embassy_stm32::init(Default::default()); info!("Hello World!"); - let ch1 = PwmPin::new_ch1(p.PE9); + let ch1 = PwmPin::new_ch1(p.PE9, OutputType::PushPull); let mut pwm = SimplePwm::new(p.TIM1, Some(ch1), None, None, None, khz(10)); let max = pwm.get_max_duty(); pwm.enable(Channel::Ch1); diff --git a/examples/stm32f4/src/bin/pwm_complementary.rs b/examples/stm32f4/src/bin/pwm_complementary.rs index 8cc2a411..83a3c753 100644 --- a/examples/stm32f4/src/bin/pwm_complementary.rs +++ b/examples/stm32f4/src/bin/pwm_complementary.rs @@ -4,6 +4,7 @@ use defmt::*; use embassy_executor::Spawner; +use embassy_stm32::gpio::OutputType; use embassy_stm32::time::khz; use embassy_stm32::timer::complementary_pwm::{ComplementaryPwm, ComplementaryPwmPin}; use embassy_stm32::timer::simple_pwm::PwmPin; @@ -16,8 +17,8 @@ async fn main(_spawner: Spawner) { let p = embassy_stm32::init(Default::default()); info!("Hello World!"); - let ch1 = PwmPin::new_ch1(p.PE9); - let ch1n = ComplementaryPwmPin::new_ch1(p.PA7); + let ch1 = PwmPin::new_ch1(p.PE9, OutputType::PushPull); + let ch1n = ComplementaryPwmPin::new_ch1(p.PA7, OutputType::PushPull); let mut pwm = ComplementaryPwm::new( p.TIM1, Some(ch1), diff --git a/examples/stm32g4/src/bin/pwm.rs b/examples/stm32g4/src/bin/pwm.rs index b5a9b995..01e9cb47 100644 --- a/examples/stm32g4/src/bin/pwm.rs +++ b/examples/stm32g4/src/bin/pwm.rs @@ -4,6 +4,7 @@ use defmt::*; use embassy_executor::Spawner; +use embassy_stm32::gpio::OutputType; use embassy_stm32::time::khz; use embassy_stm32::timer::simple_pwm::{PwmPin, SimplePwm}; use embassy_stm32::timer::Channel; @@ -15,7 +16,7 @@ async fn main(_spawner: Spawner) { let p = embassy_stm32::init(Default::default()); info!("Hello World!"); - let ch1 = PwmPin::new_ch1(p.PC0); + let ch1 = PwmPin::new_ch1(p.PC0, OutputType::PushPull); let mut pwm = SimplePwm::new(p.TIM1, Some(ch1), None, None, None, khz(10)); let max = pwm.get_max_duty(); pwm.enable(Channel::Ch1); diff --git a/examples/stm32h7/src/bin/pwm.rs b/examples/stm32h7/src/bin/pwm.rs index adf2ea9c..aa5ec1bc 100644 --- a/examples/stm32h7/src/bin/pwm.rs +++ b/examples/stm32h7/src/bin/pwm.rs @@ -4,6 +4,7 @@ use defmt::*; use embassy_executor::Spawner; +use embassy_stm32::gpio::OutputType; use embassy_stm32::time::{khz, mhz}; use embassy_stm32::timer::simple_pwm::{PwmPin, SimplePwm}; use embassy_stm32::timer::Channel; @@ -24,7 +25,7 @@ async fn main(_spawner: Spawner) { let p = embassy_stm32::init(config); info!("Hello World!"); - let ch1 = PwmPin::new_ch1(p.PA6); + let ch1 = PwmPin::new_ch1(p.PA6, OutputType::PushPull); let mut pwm = SimplePwm::new(p.TIM3, Some(ch1), None, None, None, khz(10)); let max = pwm.get_max_duty(); pwm.enable(Channel::Ch1); From a9f6e30bcdd3d288f1fba71311c9ae1f67f2d25a Mon Sep 17 00:00:00 2001 From: xoviat Date: Sat, 29 Jul 2023 12:03:01 -0500 Subject: [PATCH 50/68] rustfmt --- embassy-stm32/src/gpio.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/embassy-stm32/src/gpio.rs b/embassy-stm32/src/gpio.rs index d2e61d23..0cc269cf 100644 --- a/embassy-stm32/src/gpio.rs +++ b/embassy-stm32/src/gpio.rs @@ -4,7 +4,6 @@ use core::convert::Infallible; use embassy_hal_internal::{impl_peripheral, into_ref, PeripheralRef}; use crate::pac::gpio::{self, vals}; -use crate::usb_otg::Out; use crate::{pac, peripherals, Peripheral}; /// GPIO flexible pin. From e0ce7fcde7906fc219d294e858388e62ab107ec3 Mon Sep 17 00:00:00 2001 From: Scott Mabin Date: Sun, 30 Jul 2023 01:00:50 +0100 Subject: [PATCH 51/68] stm32f2 pll overflow with crystal With a large enough HSE input frequency, the vco clock calculation will overflow a u32. Therefore, in this specific case we have to use the inner value and cast to u64 to ensure the mul isn't clipped before applying the divider. --- embassy-stm32/src/rcc/f2.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/embassy-stm32/src/rcc/f2.rs b/embassy-stm32/src/rcc/f2.rs index 1525cc3c..bc240fcb 100644 --- a/embassy-stm32/src/rcc/f2.rs +++ b/embassy-stm32/src/rcc/f2.rs @@ -58,7 +58,7 @@ impl Default for PLLConfig { impl PLLConfig { pub fn clocks(&self, src_freq: Hertz) -> PLLClocks { let in_freq = src_freq / self.pre_div; - let vco_freq = src_freq * self.mul / self.pre_div; + let vco_freq = Hertz((src_freq.0 as u64 * self.mul.0 as u64 / self.pre_div.0 as u64) as u32); let main_freq = vco_freq / self.main_div; let pll48_freq = vco_freq / self.pll48_div; PLLClocks { From 2f18770e276d985c16ba865636bdc641cb2cf74a Mon Sep 17 00:00:00 2001 From: xoviat Date: Sat, 22 Apr 2023 14:26:40 -0500 Subject: [PATCH 52/68] stm32/rcc: extract and combine ahb/apb prescalers --- embassy-stm32/src/rcc/c0.rs | 55 +--------- embassy-stm32/src/rcc/common.rs | 174 ++++++++++++++++++++++++++++++++ embassy-stm32/src/rcc/f2.rs | 118 ++-------------------- embassy-stm32/src/rcc/g0.rs | 76 ++------------ embassy-stm32/src/rcc/g4.rs | 36 +------ embassy-stm32/src/rcc/h5.rs | 104 ++----------------- embassy-stm32/src/rcc/h7.rs | 16 +-- embassy-stm32/src/rcc/l0.rs | 53 +--------- embassy-stm32/src/rcc/l1.rs | 53 +--------- embassy-stm32/src/rcc/l4.rs | 53 +--------- embassy-stm32/src/rcc/l5.rs | 53 +--------- embassy-stm32/src/rcc/mod.rs | 2 + embassy-stm32/src/rcc/u5.rs | 79 ++------------- embassy-stm32/src/rcc/wb.rs | 68 +------------ embassy-stm32/src/rcc/wl.rs | 87 ++-------------- 15 files changed, 229 insertions(+), 798 deletions(-) create mode 100644 embassy-stm32/src/rcc/common.rs diff --git a/embassy-stm32/src/rcc/c0.rs b/embassy-stm32/src/rcc/c0.rs index df6e9047..6a932634 100644 --- a/embassy-stm32/src/rcc/c0.rs +++ b/embassy-stm32/src/rcc/c0.rs @@ -1,5 +1,6 @@ +pub use super::common::{AHBPrescaler, APBPrescaler}; use crate::pac::flash::vals::Latency; -use crate::pac::rcc::vals::{Hpre, Hsidiv, Ppre, Sw}; +use crate::pac::rcc::vals::{Hsidiv, Ppre, Sw}; use crate::pac::{FLASH, RCC}; use crate::rcc::{set_freqs, Clocks}; use crate::time::Hertz; @@ -45,58 +46,6 @@ impl Into for HSIPrescaler { } } -/// AHB prescaler -#[derive(Clone, Copy, PartialEq)] -pub enum AHBPrescaler { - NotDivided, - Div2, - Div4, - Div8, - Div16, - Div64, - Div128, - Div256, - Div512, -} - -/// APB prescaler -#[derive(Clone, Copy)] -pub enum APBPrescaler { - NotDivided, - Div2, - Div4, - Div8, - Div16, -} - -impl Into for APBPrescaler { - fn into(self) -> Ppre { - match self { - APBPrescaler::NotDivided => Ppre::DIV1, - APBPrescaler::Div2 => Ppre::DIV2, - APBPrescaler::Div4 => Ppre::DIV4, - APBPrescaler::Div8 => Ppre::DIV8, - APBPrescaler::Div16 => Ppre::DIV16, - } - } -} - -impl Into for AHBPrescaler { - fn into(self) -> Hpre { - match self { - AHBPrescaler::NotDivided => Hpre::DIV1, - AHBPrescaler::Div2 => Hpre::DIV2, - AHBPrescaler::Div4 => Hpre::DIV4, - AHBPrescaler::Div8 => Hpre::DIV8, - AHBPrescaler::Div16 => Hpre::DIV16, - AHBPrescaler::Div64 => Hpre::DIV64, - AHBPrescaler::Div128 => Hpre::DIV128, - AHBPrescaler::Div256 => Hpre::DIV256, - AHBPrescaler::Div512 => Hpre::DIV512, - } - } -} - /// Clocks configutation pub struct Config { pub mux: ClockSrc, diff --git a/embassy-stm32/src/rcc/common.rs b/embassy-stm32/src/rcc/common.rs new file mode 100644 index 00000000..7debb143 --- /dev/null +++ b/embassy-stm32/src/rcc/common.rs @@ -0,0 +1,174 @@ +use core::ops::Div; + +#[allow(unused_imports)] +use crate::pac::rcc; +use crate::time::Hertz; + +/// Voltage Scale +/// +/// Represents the voltage range feeding the CPU core. The maximum core +/// clock frequency depends on this value. +/// +/// Scale0 represents the highest voltage range +#[derive(Copy, Clone, PartialEq)] +pub enum VoltageScale { + Scale0, + Scale1, + #[cfg(not(any(rcc_wl5, rcc_wle)))] + Scale2, + #[cfg(not(any(rcc_wl5, rcc_wle)))] + Scale3, +} + +/// AHB prescaler +#[derive(Clone, Copy, PartialEq)] +pub enum AHBPrescaler { + NotDivided, + Div2, + #[cfg(any(rcc_wb, rcc_wl5, rcc_wle))] + Div3, + Div4, + #[cfg(any(rcc_wb, rcc_wl5, rcc_wle))] + Div5, + #[cfg(any(rcc_wb, rcc_wl5, rcc_wle))] + Div6, + Div8, + #[cfg(any(rcc_wb, rcc_wl5, rcc_wle))] + Div10, + Div16, + #[cfg(any(rcc_wb, rcc_wl5, rcc_wle))] + Div32, + Div64, + Div128, + Div256, + Div512, +} + +impl Div for Hertz { + type Output = Hertz; + + fn div(self, rhs: AHBPrescaler) -> Self::Output { + let divisor = match rhs { + AHBPrescaler::NotDivided => 1, + AHBPrescaler::Div2 => 2, + #[cfg(any(rcc_wb, rcc_wl5, rcc_wle))] + AHBPrescaler::Div3 => 3, + AHBPrescaler::Div4 => 4, + #[cfg(any(rcc_wb, rcc_wl5, rcc_wle))] + AHBPrescaler::Div5 => 5, + #[cfg(any(rcc_wb, rcc_wl5, rcc_wle))] + AHBPrescaler::Div6 => 6, + AHBPrescaler::Div8 => 8, + #[cfg(any(rcc_wb, rcc_wl5, rcc_wle))] + AHBPrescaler::Div10 => 10, + AHBPrescaler::Div16 => 16, + #[cfg(any(rcc_wb, rcc_wl5, rcc_wle))] + AHBPrescaler::Div32 => 32, + AHBPrescaler::Div64 => 64, + AHBPrescaler::Div128 => 128, + AHBPrescaler::Div256 => 256, + AHBPrescaler::Div512 => 512, + }; + Hertz(self.0 / divisor) + } +} + +#[cfg(not(any(rcc_g4, rcc_wb, rcc_wl5, rcc_wle)))] +impl From for rcc::vals::Hpre { + fn from(val: AHBPrescaler) -> rcc::vals::Hpre { + use rcc::vals::Hpre; + + match val { + #[cfg(not(rcc_u5))] + AHBPrescaler::NotDivided => Hpre::DIV1, + #[cfg(rcc_u5)] + AHBPrescaler::NotDivided => Hpre::NONE, + AHBPrescaler::Div2 => Hpre::DIV2, + AHBPrescaler::Div4 => Hpre::DIV4, + AHBPrescaler::Div8 => Hpre::DIV8, + AHBPrescaler::Div16 => Hpre::DIV16, + AHBPrescaler::Div64 => Hpre::DIV64, + AHBPrescaler::Div128 => Hpre::DIV128, + AHBPrescaler::Div256 => Hpre::DIV256, + AHBPrescaler::Div512 => Hpre::DIV512, + } + } +} + +#[cfg(any(rcc_wb, rcc_wl5, rcc_wle))] +impl From for u8 { + fn from(val: AHBPrescaler) -> u8 { + match val { + AHBPrescaler::NotDivided => 0x0, + AHBPrescaler::Div2 => 0x08, + AHBPrescaler::Div3 => 0x01, + AHBPrescaler::Div4 => 0x09, + AHBPrescaler::Div5 => 0x02, + AHBPrescaler::Div6 => 0x05, + AHBPrescaler::Div8 => 0x0a, + AHBPrescaler::Div10 => 0x06, + AHBPrescaler::Div16 => 0x0b, + AHBPrescaler::Div32 => 0x07, + AHBPrescaler::Div64 => 0x0c, + AHBPrescaler::Div128 => 0x0d, + AHBPrescaler::Div256 => 0x0e, + AHBPrescaler::Div512 => 0x0f, + } + } +} + +/// APB prescaler +#[derive(Clone, Copy)] +pub enum APBPrescaler { + NotDivided, + Div2, + Div4, + Div8, + Div16, +} + +impl Div for Hertz { + type Output = Hertz; + + fn div(self, rhs: APBPrescaler) -> Self::Output { + let divisor = match rhs { + APBPrescaler::NotDivided => 1, + APBPrescaler::Div2 => 2, + APBPrescaler::Div4 => 4, + APBPrescaler::Div8 => 8, + APBPrescaler::Div16 => 16, + }; + Hertz(self.0 / divisor) + } +} + +#[cfg(not(any(rcc_f1, rcc_g4, rcc_h7, rcc_wb, rcc_wl5, rcc_wle)))] +impl From for rcc::vals::Ppre { + fn from(val: APBPrescaler) -> rcc::vals::Ppre { + use rcc::vals::Ppre; + + match val { + #[cfg(not(rcc_u5))] + APBPrescaler::NotDivided => Ppre::DIV1, + #[cfg(rcc_u5)] + APBPrescaler::NotDivided => Ppre::NONE, + APBPrescaler::Div2 => Ppre::DIV2, + APBPrescaler::Div4 => Ppre::DIV4, + APBPrescaler::Div8 => Ppre::DIV8, + APBPrescaler::Div16 => Ppre::DIV16, + } + } +} + +#[cfg(any(rcc_wb, rcc_wl5, rcc_wle))] +impl From for u8 { + fn from(val: APBPrescaler) -> u8 { + match val { + APBPrescaler::NotDivided => 1, + APBPrescaler::Div2 => 0x04, + APBPrescaler::Div4 => 0x05, + APBPrescaler::Div8 => 0x06, + APBPrescaler::Div16 => 0x07, + } + } +} diff --git a/embassy-stm32/src/rcc/f2.rs b/embassy-stm32/src/rcc/f2.rs index 1525cc3c..69e89113 100644 --- a/embassy-stm32/src/rcc/f2.rs +++ b/embassy-stm32/src/rcc/f2.rs @@ -1,8 +1,9 @@ use core::convert::TryFrom; use core::ops::{Div, Mul}; +pub use super::common::{AHBPrescaler, APBPrescaler}; use crate::pac::flash::vals::Latency; -use crate::pac::rcc::vals::{Hpre, Pllp, Pllsrc, Ppre, Sw}; +use crate::pac::rcc::vals::{Pllp, Pllsrc, Sw}; use crate::pac::{FLASH, RCC}; use crate::rcc::{set_freqs, Clocks}; use crate::time::Hertz; @@ -200,114 +201,15 @@ pub struct PLLClocks { pub pll48_freq: Hertz, } -/// AHB prescaler -#[derive(Clone, Copy, PartialEq)] -pub enum AHBPrescaler { - NotDivided, - Div2, - Div4, - Div8, - Div16, - Div64, - Div128, - Div256, - Div512, -} +pub use super::common::VoltageScale; -impl Div for Hertz { - type Output = Hertz; - - fn div(self, rhs: AHBPrescaler) -> Self::Output { - let divisor = match rhs { - AHBPrescaler::NotDivided => 1, - AHBPrescaler::Div2 => 2, - AHBPrescaler::Div4 => 4, - AHBPrescaler::Div8 => 8, - AHBPrescaler::Div16 => 16, - AHBPrescaler::Div64 => 64, - AHBPrescaler::Div128 => 128, - AHBPrescaler::Div256 => 256, - AHBPrescaler::Div512 => 512, - }; - Hertz(self.0 / divisor) - } -} - -/// APB prescaler -#[derive(Clone, Copy)] -pub enum APBPrescaler { - NotDivided, - Div2, - Div4, - Div8, - Div16, -} - -impl Div for Hertz { - type Output = Hertz; - - fn div(self, rhs: APBPrescaler) -> Self::Output { - let divisor = match rhs { - APBPrescaler::NotDivided => 1, - APBPrescaler::Div2 => 2, - APBPrescaler::Div4 => 4, - APBPrescaler::Div8 => 8, - APBPrescaler::Div16 => 16, - }; - Hertz(self.0 / divisor) - } -} - -impl Into for APBPrescaler { - fn into(self) -> Ppre { - match self { - APBPrescaler::NotDivided => Ppre::DIV1, - APBPrescaler::Div2 => Ppre::DIV2, - APBPrescaler::Div4 => Ppre::DIV4, - APBPrescaler::Div8 => Ppre::DIV8, - APBPrescaler::Div16 => Ppre::DIV16, - } - } -} - -impl Into for AHBPrescaler { - fn into(self) -> Hpre { - match self { - AHBPrescaler::NotDivided => Hpre::DIV1, - AHBPrescaler::Div2 => Hpre::DIV2, - AHBPrescaler::Div4 => Hpre::DIV4, - AHBPrescaler::Div8 => Hpre::DIV8, - AHBPrescaler::Div16 => Hpre::DIV16, - AHBPrescaler::Div64 => Hpre::DIV64, - AHBPrescaler::Div128 => Hpre::DIV128, - AHBPrescaler::Div256 => Hpre::DIV256, - AHBPrescaler::Div512 => Hpre::DIV512, - } - } -} - -/// Voltage Range -/// -/// Represents the system supply voltage range -#[derive(Copy, Clone, PartialEq)] -pub enum VoltageRange { - /// 1.8 to 3.6 V - Min1V8, - /// 2.1 to 3.6 V - Min2V1, - /// 2.4 to 3.6 V - Min2V4, - /// 2.7 to 3.6 V - Min2V7, -} - -impl VoltageRange { +impl VoltageScale { const fn wait_states(&self, ahb_freq: Hertz) -> Option { let ahb_freq = ahb_freq.0; // Reference: RM0033 - Table 3. Number of wait states according to Cortex®-M3 clock // frequency match self { - VoltageRange::Min1V8 => { + VoltageScale::Scale3 => { if ahb_freq <= 16_000_000 { Some(Latency::WS0) } else if ahb_freq <= 32_000_000 { @@ -328,7 +230,7 @@ impl VoltageRange { None } } - VoltageRange::Min2V1 => { + VoltageScale::Scale2 => { if ahb_freq <= 18_000_000 { Some(Latency::WS0) } else if ahb_freq <= 36_000_000 { @@ -347,7 +249,7 @@ impl VoltageRange { None } } - VoltageRange::Min2V4 => { + VoltageScale::Scale1 => { if ahb_freq <= 24_000_000 { Some(Latency::WS0) } else if ahb_freq <= 48_000_000 { @@ -362,7 +264,7 @@ impl VoltageRange { None } } - VoltageRange::Min2V7 => { + VoltageScale::Scale0 => { if ahb_freq <= 30_000_000 { Some(Latency::WS0) } else if ahb_freq <= 60_000_000 { @@ -386,7 +288,7 @@ pub struct Config { pub pll_mux: PLLSrc, pub pll: PLLConfig, pub mux: ClockSrc, - pub voltage: VoltageRange, + pub voltage: VoltageScale, pub ahb_pre: AHBPrescaler, pub apb1_pre: APBPrescaler, pub apb2_pre: APBPrescaler, @@ -400,7 +302,7 @@ impl Default for Config { hsi: true, pll_mux: PLLSrc::HSI, pll: PLLConfig::default(), - voltage: VoltageRange::Min1V8, + voltage: VoltageScale::Scale3, mux: ClockSrc::HSI, ahb_pre: AHBPrescaler::NotDivided, apb1_pre: APBPrescaler::NotDivided, diff --git a/embassy-stm32/src/rcc/g0.rs b/embassy-stm32/src/rcc/g0.rs index 5e3a7911..1aaf983a 100644 --- a/embassy-stm32/src/rcc/g0.rs +++ b/embassy-stm32/src/rcc/g0.rs @@ -1,5 +1,6 @@ +pub use super::common::{AHBPrescaler, APBPrescaler}; use crate::pac::flash::vals::Latency; -use crate::pac::rcc::vals::{self, Hpre, Hsidiv, Ppre, Sw}; +use crate::pac::rcc::vals::{self, Hsidiv, Ppre, Sw}; use crate::pac::{FLASH, PWR, RCC}; use crate::rcc::{set_freqs, Clocks}; use crate::time::Hertz; @@ -172,58 +173,6 @@ impl From for u32 { } } -/// AHB prescaler -#[derive(Clone, Copy, PartialEq)] -pub enum AHBPrescaler { - NotDivided, - Div2, - Div4, - Div8, - Div16, - Div64, - Div128, - Div256, - Div512, -} - -/// APB prescaler -#[derive(Clone, Copy)] -pub enum APBPrescaler { - NotDivided, - Div2, - Div4, - Div8, - Div16, -} - -impl Into for APBPrescaler { - fn into(self) -> Ppre { - match self { - APBPrescaler::NotDivided => Ppre::DIV1, - APBPrescaler::Div2 => Ppre::DIV2, - APBPrescaler::Div4 => Ppre::DIV4, - APBPrescaler::Div8 => Ppre::DIV8, - APBPrescaler::Div16 => Ppre::DIV16, - } - } -} - -impl Into for AHBPrescaler { - fn into(self) -> Hpre { - match self { - AHBPrescaler::NotDivided => Hpre::DIV1, - AHBPrescaler::Div2 => Hpre::DIV2, - AHBPrescaler::Div4 => Hpre::DIV4, - AHBPrescaler::Div8 => Hpre::DIV8, - AHBPrescaler::Div16 => Hpre::DIV16, - AHBPrescaler::Div64 => Hpre::DIV64, - AHBPrescaler::Div128 => Hpre::DIV128, - AHBPrescaler::Div256 => Hpre::DIV256, - AHBPrescaler::Div512 => Hpre::DIV512, - } - } -} - /// Clocks configutation pub struct Config { pub mux: ClockSrc, @@ -425,25 +374,14 @@ pub(crate) unsafe fn init(config: Config) { FLASH.acr().modify(|w| w.set_latency(target_flash_latency)); } - let ahb_div = match config.ahb_pre { - AHBPrescaler::NotDivided => 1, - AHBPrescaler::Div2 => 2, - AHBPrescaler::Div4 => 4, - AHBPrescaler::Div8 => 8, - AHBPrescaler::Div16 => 16, - AHBPrescaler::Div64 => 64, - AHBPrescaler::Div128 => 128, - AHBPrescaler::Div256 => 256, - AHBPrescaler::Div512 => 512, - }; - let ahb_freq = sys_clk / ahb_div; + let ahb_freq = Hertz(sys_clk) / config.ahb_pre; let (apb_freq, apb_tim_freq) = match config.apb_pre { - APBPrescaler::NotDivided => (ahb_freq, ahb_freq), + APBPrescaler::NotDivided => (ahb_freq.0, ahb_freq.0), pre => { let pre: Ppre = pre.into(); - let pre: u8 = 1 << (pre.to_bits() - 3); - let freq = ahb_freq / pre as u32; + let pre: u8 = 1 << (pre.0 - 3); + let freq = ahb_freq.0 / pre as u32; (freq, freq * 2) } }; @@ -455,7 +393,7 @@ pub(crate) unsafe fn init(config: Config) { set_freqs(Clocks { sys: Hertz(sys_clk), - ahb1: Hertz(ahb_freq), + ahb1: ahb_freq, apb1: Hertz(apb_freq), apb1_tim: Hertz(apb_tim_freq), }); diff --git a/embassy-stm32/src/rcc/g4.rs b/embassy-stm32/src/rcc/g4.rs index ff8f9754..5489b718 100644 --- a/embassy-stm32/src/rcc/g4.rs +++ b/embassy-stm32/src/rcc/g4.rs @@ -2,6 +2,7 @@ use stm32_metapac::flash::vals::Latency; use stm32_metapac::rcc::vals::{Hpre, Pllsrc, Ppre, Sw}; use stm32_metapac::FLASH; +pub use super::common::{AHBPrescaler, APBPrescaler}; use crate::pac::{PWR, RCC}; use crate::rcc::sealed::RccPeripheral; use crate::rcc::{set_freqs, Clocks}; @@ -21,39 +22,8 @@ pub enum ClockSrc { PLL, } -/// AHB prescaler -#[derive(Clone, Copy, PartialEq)] -pub enum AHBPrescaler { - NotDivided, - Div2, - Div4, - Div8, - Div16, - Div64, - Div128, - Div256, - Div512, -} - -/// APB prescaler -#[derive(Clone, Copy)] -pub enum APBPrescaler { - NotDivided, - Div2, - Div4, - Div8, - Div16, -} - -/// PLL clock input source -#[derive(Clone, Copy, Debug)] -pub enum PllSrc { - HSI16, - HSE(Hertz), -} - -impl Into for PllSrc { - fn into(self) -> Pllsrc { +impl Into for APBPrescaler { + fn into(self) -> u8 { match self { PllSrc::HSE(..) => Pllsrc::HSE, PllSrc::HSI16 => Pllsrc::HSI16, diff --git a/embassy-stm32/src/rcc/h5.rs b/embassy-stm32/src/rcc/h5.rs index 7e2f75ab..2e72b193 100644 --- a/embassy-stm32/src/rcc/h5.rs +++ b/embassy-stm32/src/rcc/h5.rs @@ -1,6 +1,6 @@ use core::marker::PhantomData; -use stm32_metapac::rcc::vals::{Hpre, Ppre, Timpre}; +use stm32_metapac::rcc::vals::Timpre; use crate::pac::pwr::vals::Vos; use crate::pac::rcc::vals::{Hseext, Hsidiv, Mco1, Mco2, Pllrge, Pllsrc, Pllvcosel, Sw}; @@ -26,21 +26,7 @@ const VCO_MAX: u32 = 420_000_000; const VCO_WIDE_MIN: u32 = 128_000_000; const VCO_WIDE_MAX: u32 = 560_000_000; -/// Voltage Scale -/// -/// Represents the voltage range feeding the CPU core. The maximum core -/// clock frequency depends on this value. -#[derive(Copy, Clone, PartialEq)] -pub enum VoltageScale { - /// VOS 0 range VCORE 1.30V - 1.40V - Scale0, - /// VOS 1 range VCORE 1.15V - 1.26V - Scale1, - /// VOS 2 range VCORE 1.05V - 1.15V - Scale2, - /// VOS 3 range VCORE 0.95V - 1.05V - Scale3, -} +pub use super::common::{AHBPrescaler, APBPrescaler, VoltageScale}; pub enum HseMode { /// crystal/ceramic oscillator (HSEBYP=0) @@ -105,57 +91,7 @@ pub struct Pll { pub divr: Option, } -/// AHB prescaler -#[derive(Clone, Copy, PartialEq)] -pub enum AHBPrescaler { - NotDivided, - Div2, - Div4, - Div8, - Div16, - Div64, - Div128, - Div256, - Div512, -} - -impl AHBPrescaler { - fn div(&self, clk: Hertz) -> Hertz { - match self { - Self::NotDivided => clk, - Self::Div2 => clk / 2u32, - Self::Div4 => clk / 4u32, - Self::Div8 => clk / 8u32, - Self::Div16 => clk / 16u32, - Self::Div64 => clk / 64u32, - Self::Div128 => clk / 128u32, - Self::Div256 => clk / 256u32, - Self::Div512 => clk / 512u32, - } - } -} - -/// APB prescaler -#[derive(Clone, Copy)] -pub enum APBPrescaler { - NotDivided, - Div2, - Div4, - Div8, - Div16, -} - impl APBPrescaler { - fn div(&self, clk: Hertz) -> Hertz { - match self { - Self::NotDivided => clk, - Self::Div2 => clk / 2u32, - Self::Div4 => clk / 4u32, - Self::Div8 => clk / 8u32, - Self::Div16 => clk / 16u32, - } - } - fn div_tim(&self, clk: Hertz, tim: TimerPrescaler) -> Hertz { match (tim, self) { // The timers kernel clock is equal to rcc_hclk1 if PPRE1 or PPRE2 corresponds to a @@ -193,34 +129,6 @@ impl From for Timpre { } } -impl From for Ppre { - fn from(val: APBPrescaler) -> Ppre { - match val { - APBPrescaler::NotDivided => Ppre::DIV1, - APBPrescaler::Div2 => Ppre::DIV2, - APBPrescaler::Div4 => Ppre::DIV4, - APBPrescaler::Div8 => Ppre::DIV8, - APBPrescaler::Div16 => Ppre::DIV16, - } - } -} - -impl From for Hpre { - fn from(val: AHBPrescaler) -> Hpre { - match val { - AHBPrescaler::NotDivided => Hpre::DIV1, - AHBPrescaler::Div2 => Hpre::DIV2, - AHBPrescaler::Div4 => Hpre::DIV4, - AHBPrescaler::Div8 => Hpre::DIV8, - AHBPrescaler::Div16 => Hpre::DIV16, - AHBPrescaler::Div64 => Hpre::DIV64, - AHBPrescaler::Div128 => Hpre::DIV128, - AHBPrescaler::Div256 => Hpre::DIV256, - AHBPrescaler::Div512 => Hpre::DIV512, - } - } -} - /// Configuration of the core clocks #[non_exhaustive] pub struct Config { @@ -406,13 +314,13 @@ pub(crate) unsafe fn init(config: Config) { }; assert!(sys <= max_clk); - let hclk = config.ahb_pre.div(sys); + let hclk = sys / config.ahb_pre; - let apb1 = config.apb1_pre.div(hclk); + let apb1 = hclk / config.apb1_pre; let apb1_tim = config.apb1_pre.div_tim(hclk, config.timer_prescaler); - let apb2 = config.apb2_pre.div(hclk); + let apb2 = hclk / config.apb2_pre; let apb2_tim = config.apb2_pre.div_tim(hclk, config.timer_prescaler); - let apb3 = config.apb3_pre.div(hclk); + let apb3 = hclk / config.apb3_pre; flash_setup(hclk, config.voltage_scale); diff --git a/embassy-stm32/src/rcc/h7.rs b/embassy-stm32/src/rcc/h7.rs index bbc0e083..0788b064 100644 --- a/embassy-stm32/src/rcc/h7.rs +++ b/embassy-stm32/src/rcc/h7.rs @@ -24,21 +24,7 @@ pub const HSI48_FREQ: Hertz = Hertz(48_000_000); /// LSI speed pub const LSI_FREQ: Hertz = Hertz(32_000); -/// Voltage Scale -/// -/// Represents the voltage range feeding the CPU core. The maximum core -/// clock frequency depends on this value. -#[derive(Copy, Clone, PartialEq)] -pub enum VoltageScale { - /// VOS 0 range VCORE 1.26V - 1.40V - Scale0, - /// VOS 1 range VCORE 1.15V - 1.26V - Scale1, - /// VOS 2 range VCORE 1.05V - 1.15V - Scale2, - /// VOS 3 range VCORE 0.95V - 1.05V - Scale3, -} +pub use super::common::VoltageScale; #[derive(Clone, Copy)] pub enum AdcClockSource { diff --git a/embassy-stm32/src/rcc/l0.rs b/embassy-stm32/src/rcc/l0.rs index 46a528e3..46b58ca7 100644 --- a/embassy-stm32/src/rcc/l0.rs +++ b/embassy-stm32/src/rcc/l0.rs @@ -1,3 +1,4 @@ +pub use super::common::{AHBPrescaler, APBPrescaler}; use crate::pac::rcc::vals::{Hpre, Msirange, Plldiv, Pllmul, Pllsrc, Ppre, Sw}; use crate::pac::RCC; #[cfg(crs)] @@ -70,30 +71,6 @@ pub enum PLLMul { Mul48, } -/// AHB prescaler -#[derive(Clone, Copy, PartialEq)] -pub enum AHBPrescaler { - NotDivided, - Div2, - Div4, - Div8, - Div16, - Div64, - Div128, - Div256, - Div512, -} - -/// APB prescaler -#[derive(Clone, Copy)] -pub enum APBPrescaler { - NotDivided, - Div2, - Div4, - Div8, - Div16, -} - /// PLL clock input source #[derive(Clone, Copy)] pub enum PLLSource { @@ -136,34 +113,6 @@ impl From for Pllsrc { } } -impl From for Ppre { - fn from(val: APBPrescaler) -> Ppre { - match val { - APBPrescaler::NotDivided => Ppre::DIV1, - APBPrescaler::Div2 => Ppre::DIV2, - APBPrescaler::Div4 => Ppre::DIV4, - APBPrescaler::Div8 => Ppre::DIV8, - APBPrescaler::Div16 => Ppre::DIV16, - } - } -} - -impl From for Hpre { - fn from(val: AHBPrescaler) -> Hpre { - match val { - AHBPrescaler::NotDivided => Hpre::DIV1, - AHBPrescaler::Div2 => Hpre::DIV2, - AHBPrescaler::Div4 => Hpre::DIV4, - AHBPrescaler::Div8 => Hpre::DIV8, - AHBPrescaler::Div16 => Hpre::DIV16, - AHBPrescaler::Div64 => Hpre::DIV64, - AHBPrescaler::Div128 => Hpre::DIV128, - AHBPrescaler::Div256 => Hpre::DIV256, - AHBPrescaler::Div512 => Hpre::DIV512, - } - } -} - impl From for Msirange { fn from(val: MSIRange) -> Msirange { match val { diff --git a/embassy-stm32/src/rcc/l1.rs b/embassy-stm32/src/rcc/l1.rs index 59a6eac8..bdfc5b87 100644 --- a/embassy-stm32/src/rcc/l1.rs +++ b/embassy-stm32/src/rcc/l1.rs @@ -1,3 +1,4 @@ +pub use super::common::{AHBPrescaler, APBPrescaler}; use crate::pac::rcc::vals::{Hpre, Msirange, Plldiv, Pllmul, Pllsrc, Ppre, Sw}; use crate::pac::{FLASH, RCC}; use crate::rcc::{set_freqs, Clocks}; @@ -68,30 +69,6 @@ pub enum PLLMul { Mul48, } -/// AHB prescaler -#[derive(Clone, Copy, PartialEq)] -pub enum AHBPrescaler { - NotDivided, - Div2, - Div4, - Div8, - Div16, - Div64, - Div128, - Div256, - Div512, -} - -/// APB prescaler -#[derive(Clone, Copy)] -pub enum APBPrescaler { - NotDivided, - Div2, - Div4, - Div8, - Div16, -} - /// PLL clock input source #[derive(Clone, Copy)] pub enum PLLSource { @@ -134,34 +111,6 @@ impl From for Pllsrc { } } -impl From for Ppre { - fn from(val: APBPrescaler) -> Ppre { - match val { - APBPrescaler::NotDivided => Ppre::DIV1, - APBPrescaler::Div2 => Ppre::DIV2, - APBPrescaler::Div4 => Ppre::DIV4, - APBPrescaler::Div8 => Ppre::DIV8, - APBPrescaler::Div16 => Ppre::DIV16, - } - } -} - -impl From for Hpre { - fn from(val: AHBPrescaler) -> Hpre { - match val { - AHBPrescaler::NotDivided => Hpre::DIV1, - AHBPrescaler::Div2 => Hpre::DIV2, - AHBPrescaler::Div4 => Hpre::DIV4, - AHBPrescaler::Div8 => Hpre::DIV8, - AHBPrescaler::Div16 => Hpre::DIV16, - AHBPrescaler::Div64 => Hpre::DIV64, - AHBPrescaler::Div128 => Hpre::DIV128, - AHBPrescaler::Div256 => Hpre::DIV256, - AHBPrescaler::Div512 => Hpre::DIV512, - } - } -} - impl From for Msirange { fn from(val: MSIRange) -> Msirange { match val { diff --git a/embassy-stm32/src/rcc/l4.rs b/embassy-stm32/src/rcc/l4.rs index dc5f55d0..237b7bc9 100644 --- a/embassy-stm32/src/rcc/l4.rs +++ b/embassy-stm32/src/rcc/l4.rs @@ -4,6 +4,7 @@ use embassy_hal_internal::into_ref; use stm32_metapac::rcc::regs::Cfgr; use stm32_metapac::rcc::vals::{Lsedrv, Mcopre, Mcosel}; +pub use super::common::{AHBPrescaler, APBPrescaler}; use crate::gpio::sealed::AFType; use crate::gpio::Speed; use crate::pac::rcc::vals::{Hpre, Msirange, Pllsrc, Ppre, Sw}; @@ -78,30 +79,6 @@ pub enum PLLDiv { Div4, } -/// AHB prescaler -#[derive(Clone, Copy, PartialEq)] -pub enum AHBPrescaler { - NotDivided, - Div2, - Div4, - Div8, - Div16, - Div64, - Div128, - Div256, - Div512, -} - -/// APB prescaler -#[derive(Clone, Copy)] -pub enum APBPrescaler { - NotDivided, - Div2, - Div4, - Div8, - Div16, -} - /// PLL clock input source #[derive(Clone, Copy)] pub enum PLLSource { @@ -209,34 +186,6 @@ impl From for Pllsrc { } } -impl From for Ppre { - fn from(val: APBPrescaler) -> Ppre { - match val { - APBPrescaler::NotDivided => Ppre::DIV1, - APBPrescaler::Div2 => Ppre::DIV2, - APBPrescaler::Div4 => Ppre::DIV4, - APBPrescaler::Div8 => Ppre::DIV8, - APBPrescaler::Div16 => Ppre::DIV16, - } - } -} - -impl From for Hpre { - fn from(val: AHBPrescaler) -> Hpre { - match val { - AHBPrescaler::NotDivided => Hpre::DIV1, - AHBPrescaler::Div2 => Hpre::DIV2, - AHBPrescaler::Div4 => Hpre::DIV4, - AHBPrescaler::Div8 => Hpre::DIV8, - AHBPrescaler::Div16 => Hpre::DIV16, - AHBPrescaler::Div64 => Hpre::DIV64, - AHBPrescaler::Div128 => Hpre::DIV128, - AHBPrescaler::Div256 => Hpre::DIV256, - AHBPrescaler::Div512 => Hpre::DIV512, - } - } -} - impl From for Msirange { fn from(val: MSIRange) -> Msirange { match val { diff --git a/embassy-stm32/src/rcc/l5.rs b/embassy-stm32/src/rcc/l5.rs index 16da65d5..a85e1488 100644 --- a/embassy-stm32/src/rcc/l5.rs +++ b/embassy-stm32/src/rcc/l5.rs @@ -1,5 +1,6 @@ use stm32_metapac::PWR; +pub use super::common::{AHBPrescaler, APBPrescaler}; use crate::pac::rcc::vals::{Hpre, Msirange, Pllsrc, Ppre, Sw}; use crate::pac::{FLASH, RCC}; use crate::rcc::{set_freqs, Clocks}; @@ -71,30 +72,6 @@ pub enum PLLDiv { Div4, } -/// AHB prescaler -#[derive(Clone, Copy, PartialEq)] -pub enum AHBPrescaler { - NotDivided, - Div2, - Div4, - Div8, - Div16, - Div64, - Div128, - Div256, - Div512, -} - -/// APB prescaler -#[derive(Clone, Copy)] -pub enum APBPrescaler { - NotDivided, - Div2, - Div4, - Div8, - Div16, -} - /// PLL clock input source #[derive(Clone, Copy)] pub enum PLLSource { @@ -202,34 +179,6 @@ impl From for Pllsrc { } } -impl From for Ppre { - fn from(val: APBPrescaler) -> Ppre { - match val { - APBPrescaler::NotDivided => Ppre::DIV1, - APBPrescaler::Div2 => Ppre::DIV2, - APBPrescaler::Div4 => Ppre::DIV4, - APBPrescaler::Div8 => Ppre::DIV8, - APBPrescaler::Div16 => Ppre::DIV16, - } - } -} - -impl From for Hpre { - fn from(val: AHBPrescaler) -> Hpre { - match val { - AHBPrescaler::NotDivided => Hpre::DIV1, - AHBPrescaler::Div2 => Hpre::DIV2, - AHBPrescaler::Div4 => Hpre::DIV4, - AHBPrescaler::Div8 => Hpre::DIV8, - AHBPrescaler::Div16 => Hpre::DIV16, - AHBPrescaler::Div64 => Hpre::DIV64, - AHBPrescaler::Div128 => Hpre::DIV128, - AHBPrescaler::Div256 => Hpre::DIV256, - AHBPrescaler::Div512 => Hpre::DIV512, - } - } -} - impl From for Msirange { fn from(val: MSIRange) -> Msirange { match val { diff --git a/embassy-stm32/src/rcc/mod.rs b/embassy-stm32/src/rcc/mod.rs index 4ae65d3e..5c69037e 100644 --- a/embassy-stm32/src/rcc/mod.rs +++ b/embassy-stm32/src/rcc/mod.rs @@ -1,5 +1,7 @@ #![macro_use] +pub mod common; + use core::mem::MaybeUninit; use crate::time::Hertz; diff --git a/embassy-stm32/src/rcc/u5.rs b/embassy-stm32/src/rcc/u5.rs index cfc07f06..b5feeb0c 100644 --- a/embassy-stm32/src/rcc/u5.rs +++ b/embassy-stm32/src/rcc/u5.rs @@ -1,5 +1,6 @@ -use stm32_metapac::rcc::vals::{Hpre, Msirange, Msirgsel, Pllm, Pllsrc, Ppre, Sw}; +use stm32_metapac::rcc::vals::{Msirange, Msirgsel, Pllm, Pllsrc, Sw}; +pub use super::common::{AHBPrescaler, APBPrescaler}; use crate::pac::{FLASH, RCC}; use crate::rcc::{set_freqs, Clocks}; use crate::time::Hertz; @@ -10,19 +11,7 @@ pub const HSI_FREQ: Hertz = Hertz(16_000_000); /// LSI speed pub const LSI_FREQ: Hertz = Hertz(32_000); -/// Voltage Scale -/// -/// Represents the voltage range feeding the CPU core. The maximum core -/// clock frequency depends on this value. -#[derive(Copy, Clone, PartialEq)] -pub enum VoltageScale { - // Highest frequency - Range1, - Range2, - Range3, - // Lowest power - Range4, -} +pub use super::common::VoltageScale; #[derive(Copy, Clone)] pub enum ClockSrc { @@ -130,36 +119,6 @@ impl Into for PllM { } } -/// AHB prescaler -#[derive(Clone, Copy, PartialEq)] -pub enum AHBPrescaler { - NotDivided, - Div2, - Div4, - Div8, - Div16, - Div64, - Div128, - Div256, - Div512, -} - -impl Into for AHBPrescaler { - fn into(self) -> Hpre { - match self { - AHBPrescaler::NotDivided => Hpre::NONE, - AHBPrescaler::Div2 => Hpre::DIV2, - AHBPrescaler::Div4 => Hpre::DIV4, - AHBPrescaler::Div8 => Hpre::DIV8, - AHBPrescaler::Div16 => Hpre::DIV16, - AHBPrescaler::Div64 => Hpre::DIV64, - AHBPrescaler::Div128 => Hpre::DIV128, - AHBPrescaler::Div256 => Hpre::DIV256, - AHBPrescaler::Div512 => Hpre::DIV512, - } - } -} - impl Into for AHBPrescaler { fn into(self) -> u8 { match self { @@ -182,28 +141,6 @@ impl Default for AHBPrescaler { } } -/// APB prescaler -#[derive(Clone, Copy)] -pub enum APBPrescaler { - NotDivided, - Div2, - Div4, - Div8, - Div16, -} - -impl Into for APBPrescaler { - fn into(self) -> Ppre { - match self { - APBPrescaler::NotDivided => Ppre::NONE, - APBPrescaler::Div2 => Ppre::DIV2, - APBPrescaler::Div4 => Ppre::DIV4, - APBPrescaler::Div8 => Ppre::DIV8, - APBPrescaler::Div16 => Ppre::DIV16, - } - } -} - impl Default for APBPrescaler { fn default() -> Self { APBPrescaler::NotDivided @@ -389,12 +326,12 @@ pub(crate) unsafe fn init(config: Config) { } // TODO make configurable - let power_vos = VoltageScale::Range4; + let power_vos = VoltageScale::Scale3; // states and programming delay let wait_states = match power_vos { // VOS 0 range VCORE 1.26V - 1.40V - VoltageScale::Range1 => { + VoltageScale::Scale0 => { if sys_clk < 32_000_000 { 0 } else if sys_clk < 64_000_000 { @@ -408,7 +345,7 @@ pub(crate) unsafe fn init(config: Config) { } } // VOS 1 range VCORE 1.15V - 1.26V - VoltageScale::Range2 => { + VoltageScale::Scale1 => { if sys_clk < 30_000_000 { 0 } else if sys_clk < 60_000_000 { @@ -420,7 +357,7 @@ pub(crate) unsafe fn init(config: Config) { } } // VOS 2 range VCORE 1.05V - 1.15V - VoltageScale::Range3 => { + VoltageScale::Scale2 => { if sys_clk < 24_000_000 { 0 } else if sys_clk < 48_000_000 { @@ -430,7 +367,7 @@ pub(crate) unsafe fn init(config: Config) { } } // VOS 3 range VCORE 0.95V - 1.05V - VoltageScale::Range4 => { + VoltageScale::Scale3 => { if sys_clk < 12_000_000 { 0 } else { diff --git a/embassy-stm32/src/rcc/wb.rs b/embassy-stm32/src/rcc/wb.rs index 4322b950..b8ef01a9 100644 --- a/embassy-stm32/src/rcc/wb.rs +++ b/embassy-stm32/src/rcc/wb.rs @@ -1,5 +1,7 @@ -use crate::rcc::Clocks; -use crate::time::{khz, mhz, Hertz}; +pub use super::common::{AHBPrescaler, APBPrescaler}; +use crate::pac::RCC; +use crate::rcc::{set_freqs, Clocks, Clocks}; +use crate::time::{khz, mhz, Hertz, Hertz}; /// Most of clock setup is copied from stm32l0xx-hal, and adopted to the generated PAC, /// and with the addition of the init function to configure a system clock. @@ -102,68 +104,6 @@ pub struct Pll { pub divr: Option, } -/// AHB prescaler -#[derive(Clone, Copy, PartialEq)] -pub enum AHBPrescaler { - NotDivided, - Div2, - Div3, - Div4, - Div5, - Div6, - Div8, - Div10, - Div16, - Div32, - Div64, - Div128, - Div256, - Div512, -} - -/// APB prescaler -#[derive(Clone, Copy)] -pub enum APBPrescaler { - NotDivided, - Div2, - Div4, - Div8, - Div16, -} - -impl Into for APBPrescaler { - fn into(self) -> u8 { - match self { - APBPrescaler::NotDivided => 1, - APBPrescaler::Div2 => 0x04, - APBPrescaler::Div4 => 0x05, - APBPrescaler::Div8 => 0x06, - APBPrescaler::Div16 => 0x07, - } - } -} - -impl Into for AHBPrescaler { - fn into(self) -> u8 { - match self { - AHBPrescaler::NotDivided => 0x0, - AHBPrescaler::Div2 => 0x08, - AHBPrescaler::Div3 => 0x01, - AHBPrescaler::Div4 => 0x09, - AHBPrescaler::Div5 => 0x02, - AHBPrescaler::Div6 => 0x05, - AHBPrescaler::Div8 => 0x0a, - AHBPrescaler::Div10 => 0x06, - AHBPrescaler::Div16 => 0x0b, - AHBPrescaler::Div32 => 0x07, - AHBPrescaler::Div64 => 0x0c, - AHBPrescaler::Div128 => 0x0d, - AHBPrescaler::Div256 => 0x0e, - AHBPrescaler::Div512 => 0x0f, - } - } -} - /// Clocks configutation pub struct Config { pub hse: Option, diff --git a/embassy-stm32/src/rcc/wl.rs b/embassy-stm32/src/rcc/wl.rs index 6b69bb1c..eca1f613 100644 --- a/embassy-stm32/src/rcc/wl.rs +++ b/embassy-stm32/src/rcc/wl.rs @@ -1,5 +1,6 @@ +pub use super::common::{AHBPrescaler, APBPrescaler, VoltageScale}; use crate::pac::pwr::vals::Dbp; -use crate::pac::{FLASH, PWR, RCC}; +use crate::pac::{FLASH, FLASH, PWR, RCC, RCC}; use crate::rcc::{set_freqs, Clocks}; use crate::time::Hertz; @@ -73,9 +74,9 @@ impl MSIRange { fn vos(&self) -> VoltageScale { if self > &MSIRange::Range8 { - VoltageScale::Range1 + VoltageScale::Scale0 } else { - VoltageScale::Range2 + VoltageScale::Scale1 } } } @@ -105,78 +106,6 @@ impl Into for MSIRange { } } -/// Voltage Scale -/// -/// Represents the voltage range feeding the CPU core. The maximum core -/// clock frequency depends on this value. -#[derive(Copy, Clone, PartialEq)] -pub enum VoltageScale { - Range1, - Range2, -} - -/// AHB prescaler -#[derive(Clone, Copy, PartialEq)] -pub enum AHBPrescaler { - NotDivided, - Div2, - Div3, - Div4, - Div5, - Div6, - Div8, - Div10, - Div16, - Div32, - Div64, - Div128, - Div256, - Div512, -} - -/// APB prescaler -#[derive(Clone, Copy)] -pub enum APBPrescaler { - NotDivided, - Div2, - Div4, - Div8, - Div16, -} - -impl Into for APBPrescaler { - fn into(self) -> u8 { - match self { - APBPrescaler::NotDivided => 1, - APBPrescaler::Div2 => 0x04, - APBPrescaler::Div4 => 0x05, - APBPrescaler::Div8 => 0x06, - APBPrescaler::Div16 => 0x07, - } - } -} - -impl Into for AHBPrescaler { - fn into(self) -> u8 { - match self { - AHBPrescaler::NotDivided => 0x0, - AHBPrescaler::Div2 => 0x08, - AHBPrescaler::Div3 => 0x01, - AHBPrescaler::Div4 => 0x09, - AHBPrescaler::Div5 => 0x02, - AHBPrescaler::Div6 => 0x05, - AHBPrescaler::Div8 => 0x0a, - AHBPrescaler::Div10 => 0x06, - AHBPrescaler::Div16 => 0x0b, - AHBPrescaler::Div32 => 0x07, - AHBPrescaler::Div64 => 0x0c, - AHBPrescaler::Div128 => 0x0d, - AHBPrescaler::Div256 => 0x0e, - AHBPrescaler::Div512 => 0x0f, - } - } -} - /// Clocks configutation pub struct Config { pub mux: ClockSrc, @@ -220,8 +149,8 @@ pub enum Lsedrv { pub(crate) unsafe fn init(config: Config) { let (sys_clk, sw, vos) = match config.mux { - ClockSrc::HSI16 => (HSI_FREQ.0, 0x01, VoltageScale::Range2), - ClockSrc::HSE32 => (HSE32_FREQ.0, 0x02, VoltageScale::Range1), + ClockSrc::HSI16 => (HSI_FREQ.0, 0x01, VoltageScale::Scale1), + ClockSrc::HSE32 => (HSE32_FREQ.0, 0x02, VoltageScale::Scale0), ClockSrc::MSI(range) => (range.freq(), 0x00, range.vos()), }; @@ -266,12 +195,12 @@ pub(crate) unsafe fn init(config: Config) { // Adjust flash latency let flash_clk_src_freq: u32 = shd_ahb_freq; let ws = match vos { - VoltageScale::Range1 => match flash_clk_src_freq { + VoltageScale::Scale0 => match flash_clk_src_freq { 0..=18_000_000 => 0b000, 18_000_001..=36_000_000 => 0b001, _ => 0b010, }, - VoltageScale::Range2 => match flash_clk_src_freq { + VoltageScale::Scale1 => match flash_clk_src_freq { 0..=6_000_000 => 0b000, 6_000_001..=12_000_000 => 0b001, _ => 0b010, From a8a491212bc0b4de0c3d5e64b3891ebf3087eb44 Mon Sep 17 00:00:00 2001 From: xoviat Date: Sun, 30 Jul 2023 10:18:54 -0500 Subject: [PATCH 53/68] stm32/rcc: cleanup merge --- embassy-stm32/src/rcc/common.rs | 2 +- embassy-stm32/src/rcc/g0.rs | 2 +- embassy-stm32/src/rcc/g4.rs | 11 +++++++++-- embassy-stm32/src/rcc/wb.rs | 5 ++--- embassy-stm32/src/rcc/wl.rs | 2 +- 5 files changed, 14 insertions(+), 8 deletions(-) diff --git a/embassy-stm32/src/rcc/common.rs b/embassy-stm32/src/rcc/common.rs index 7debb143..62736a43 100644 --- a/embassy-stm32/src/rcc/common.rs +++ b/embassy-stm32/src/rcc/common.rs @@ -142,7 +142,7 @@ impl Div for Hertz { } } -#[cfg(not(any(rcc_f1, rcc_g4, rcc_h7, rcc_wb, rcc_wl5, rcc_wle)))] +#[cfg(not(any(rcc_f1, rcc_f100, rcc_f1cl, rcc_g4, rcc_h7, rcc_h7ab, rcc_wb, rcc_wl5, rcc_wle)))] impl From for rcc::vals::Ppre { fn from(val: APBPrescaler) -> rcc::vals::Ppre { use rcc::vals::Ppre; diff --git a/embassy-stm32/src/rcc/g0.rs b/embassy-stm32/src/rcc/g0.rs index 1aaf983a..bf2d5199 100644 --- a/embassy-stm32/src/rcc/g0.rs +++ b/embassy-stm32/src/rcc/g0.rs @@ -380,7 +380,7 @@ pub(crate) unsafe fn init(config: Config) { APBPrescaler::NotDivided => (ahb_freq.0, ahb_freq.0), pre => { let pre: Ppre = pre.into(); - let pre: u8 = 1 << (pre.0 - 3); + let pre: u8 = 1 << (pre.to_bits() - 3); let freq = ahb_freq.0 / pre as u32; (freq, freq * 2) } diff --git a/embassy-stm32/src/rcc/g4.rs b/embassy-stm32/src/rcc/g4.rs index 5489b718..dff04023 100644 --- a/embassy-stm32/src/rcc/g4.rs +++ b/embassy-stm32/src/rcc/g4.rs @@ -22,8 +22,15 @@ pub enum ClockSrc { PLL, } -impl Into for APBPrescaler { - fn into(self) -> u8 { +/// PLL clock input source +#[derive(Clone, Copy, Debug)] +pub enum PllSrc { + HSI16, + HSE(Hertz), +} + +impl Into for PllSrc { + fn into(self) -> Pllsrc { match self { PllSrc::HSE(..) => Pllsrc::HSE, PllSrc::HSI16 => Pllsrc::HSI16, diff --git a/embassy-stm32/src/rcc/wb.rs b/embassy-stm32/src/rcc/wb.rs index b8ef01a9..21aacec5 100644 --- a/embassy-stm32/src/rcc/wb.rs +++ b/embassy-stm32/src/rcc/wb.rs @@ -1,7 +1,6 @@ pub use super::common::{AHBPrescaler, APBPrescaler}; -use crate::pac::RCC; -use crate::rcc::{set_freqs, Clocks, Clocks}; -use crate::time::{khz, mhz, Hertz, Hertz}; +use crate::rcc::Clocks; +use crate::time::{khz, mhz, Hertz}; /// Most of clock setup is copied from stm32l0xx-hal, and adopted to the generated PAC, /// and with the addition of the init function to configure a system clock. diff --git a/embassy-stm32/src/rcc/wl.rs b/embassy-stm32/src/rcc/wl.rs index eca1f613..ea6e8dde 100644 --- a/embassy-stm32/src/rcc/wl.rs +++ b/embassy-stm32/src/rcc/wl.rs @@ -1,6 +1,6 @@ pub use super::common::{AHBPrescaler, APBPrescaler, VoltageScale}; use crate::pac::pwr::vals::Dbp; -use crate::pac::{FLASH, FLASH, PWR, RCC, RCC}; +use crate::pac::{FLASH, PWR, RCC}; use crate::rcc::{set_freqs, Clocks}; use crate::time::Hertz; From 73057ee241faf20f4b461766239d7cd805741cd7 Mon Sep 17 00:00:00 2001 From: GhaithOueslati Date: Sun, 30 Jul 2023 16:46:33 +0100 Subject: [PATCH 54/68] wpan: fix examples --- examples/stm32wb/src/bin/eddystone_beacon.rs | 5 ++++- examples/stm32wb/src/bin/gatt_server.rs | 5 ++++- examples/stm32wb/src/bin/mac_ffd.rs | 9 ++++++--- examples/stm32wb/src/bin/mac_ffd_net.rs | 9 ++++++--- examples/stm32wb/src/bin/mac_rfd.rs | 9 ++++++--- examples/stm32wb/src/bin/tl_mbox.rs | 5 ++++- examples/stm32wb/src/bin/tl_mbox_ble.rs | 5 ++++- examples/stm32wb/src/bin/tl_mbox_mac.rs | 9 ++++++--- 8 files changed, 40 insertions(+), 16 deletions(-) diff --git a/examples/stm32wb/src/bin/eddystone_beacon.rs b/examples/stm32wb/src/bin/eddystone_beacon.rs index 451bd7d2..ea150c67 100644 --- a/examples/stm32wb/src/bin/eddystone_beacon.rs +++ b/examples/stm32wb/src/bin/eddystone_beacon.rs @@ -8,6 +8,7 @@ use defmt::*; use embassy_executor::Spawner; use embassy_stm32::bind_interrupts; use embassy_stm32::ipcc::{Config, ReceiveInterruptHandler, TransmitInterruptHandler}; +use embassy_stm32::rcc::WPAN_DEFAULT; use embassy_stm32_wpan::hci::host::uart::UartHci; use embassy_stm32_wpan::hci::host::{AdvertisingFilterPolicy, EncryptionKey, HostHci, OwnAddressType}; use embassy_stm32_wpan::hci::types::AdvertisingType; @@ -54,7 +55,9 @@ async fn main(_spawner: Spawner) { Note: extended stack versions are not supported at this time. Do not attempt to install a stack with "extended" in the name. */ - let p = embassy_stm32::init(Default::default()); + let mut config = embassy_stm32::Config::default(); + config.rcc = WPAN_DEFAULT; + let p = embassy_stm32::init(config); info!("Hello World!"); let config = Config::default(); diff --git a/examples/stm32wb/src/bin/gatt_server.rs b/examples/stm32wb/src/bin/gatt_server.rs index 0f6419d4..dd67249c 100644 --- a/examples/stm32wb/src/bin/gatt_server.rs +++ b/examples/stm32wb/src/bin/gatt_server.rs @@ -8,6 +8,7 @@ use defmt::*; use embassy_executor::Spawner; use embassy_stm32::bind_interrupts; use embassy_stm32::ipcc::{Config, ReceiveInterruptHandler, TransmitInterruptHandler}; +use embassy_stm32::rcc::WPAN_DEFAULT; use embassy_stm32_wpan::hci::event::command::{CommandComplete, ReturnParameters}; use embassy_stm32_wpan::hci::host::uart::{Packet, UartHci}; use embassy_stm32_wpan::hci::host::{AdvertisingFilterPolicy, EncryptionKey, HostHci, OwnAddressType}; @@ -62,7 +63,9 @@ async fn main(_spawner: Spawner) { Note: extended stack versions are not supported at this time. Do not attempt to install a stack with "extended" in the name. */ - let p = embassy_stm32::init(Default::default()); + let mut config = embassy_stm32::Config::default(); + config.rcc = WPAN_DEFAULT; + let p = embassy_stm32::init(config); info!("Hello World!"); let config = Config::default(); diff --git a/examples/stm32wb/src/bin/mac_ffd.rs b/examples/stm32wb/src/bin/mac_ffd.rs index 1379ac6b..881dc488 100644 --- a/examples/stm32wb/src/bin/mac_ffd.rs +++ b/examples/stm32wb/src/bin/mac_ffd.rs @@ -6,6 +6,7 @@ use defmt::*; use embassy_executor::Spawner; use embassy_stm32::bind_interrupts; use embassy_stm32::ipcc::{Config, ReceiveInterruptHandler, TransmitInterruptHandler}; +use embassy_stm32::rcc::WPAN_DEFAULT; use embassy_stm32_wpan::mac::commands::{AssociateResponse, ResetRequest, SetRequest, StartRequest}; use embassy_stm32_wpan::mac::event::MacEvent; use embassy_stm32_wpan::mac::typedefs::{MacChannel, MacStatus, PanId, PibId, SecurityLevel}; @@ -30,7 +31,7 @@ async fn main(spawner: Spawner) { - Obtain a NUCLEO-STM32WB55 from your preferred supplier. - Download and Install STM32CubeProgrammer. - - Download stm32wb5x_FUS_fw.bin, stm32wb5x_BLE_Stack_full_fw.bin, and Release_Notes.html from + - Download stm32wb5x_FUS_fw.bin, stm32wb5x_BLE_Mac_802_15_4_fw.bin, and Release_Notes.html from gh:STMicroelectronics/STM32CubeWB@2234d97/Projects/STM32WB_Copro_Wireless_Binaries/STM32WB5x - Open STM32CubeProgrammer - On the right-hand pane, click "firmware upgrade" to upgrade the st-link firmware. @@ -39,7 +40,7 @@ async fn main(spawner: Spawner) { - In the Release_Notes.html, find the memory address that corresponds to your device for the stm32wb5x_FUS_fw.bin file - Select that file, the memory address, "verify download", and then "Firmware Upgrade". - Once complete, in the Release_Notes.html, find the memory address that corresponds to your device for the - stm32wb5x_BLE_Stack_full_fw.bin file. It should not be the same memory address. + stm32wb5x_BLE_Mac_802_15_4_fw.bin file. It should not be the same memory address. - Select that file, the memory address, "verify download", and then "Firmware Upgrade". - Select "Start Wireless Stack". - Disconnect from the device. @@ -49,7 +50,9 @@ async fn main(spawner: Spawner) { Note: extended stack versions are not supported at this time. Do not attempt to install a stack with "extended" in the name. */ - let p = embassy_stm32::init(Default::default()); + let mut config = embassy_stm32::Config::default(); + config.rcc = WPAN_DEFAULT; + let p = embassy_stm32::init(config); info!("Hello World!"); let config = Config::default(); diff --git a/examples/stm32wb/src/bin/mac_ffd_net.rs b/examples/stm32wb/src/bin/mac_ffd_net.rs index bbcd0a70..f8c76b5a 100644 --- a/examples/stm32wb/src/bin/mac_ffd_net.rs +++ b/examples/stm32wb/src/bin/mac_ffd_net.rs @@ -6,6 +6,7 @@ use defmt::*; use embassy_executor::Spawner; use embassy_stm32::bind_interrupts; use embassy_stm32::ipcc::{Config, ReceiveInterruptHandler, TransmitInterruptHandler}; +use embassy_stm32::rcc::WPAN_DEFAULT; use embassy_stm32_wpan::mac::commands::{ResetRequest, SetRequest, StartRequest}; use embassy_stm32_wpan::mac::typedefs::{MacChannel, PanId, PibId}; use embassy_stm32_wpan::mac::{self, Runner}; @@ -36,7 +37,7 @@ async fn main(spawner: Spawner) { - Obtain a NUCLEO-STM32WB55 from your preferred supplier. - Download and Install STM32CubeProgrammer. - - Download stm32wb5x_FUS_fw.bin, stm32wb5x_BLE_Stack_full_fw.bin, and Release_Notes.html from + - Download stm32wb5x_FUS_fw.bin, stm32wb5x_BLE_Mac_802_15_4_fw.bin, and Release_Notes.html from gh:STMicroelectronics/STM32CubeWB@2234d97/Projects/STM32WB_Copro_Wireless_Binaries/STM32WB5x - Open STM32CubeProgrammer - On the right-hand pane, click "firmware upgrade" to upgrade the st-link firmware. @@ -45,7 +46,7 @@ async fn main(spawner: Spawner) { - In the Release_Notes.html, find the memory address that corresponds to your device for the stm32wb5x_FUS_fw.bin file - Select that file, the memory address, "verify download", and then "Firmware Upgrade". - Once complete, in the Release_Notes.html, find the memory address that corresponds to your device for the - stm32wb5x_BLE_Stack_full_fw.bin file. It should not be the same memory address. + stm32wb5x_BLE_Mac_802_15_4_fw.bin file. It should not be the same memory address. - Select that file, the memory address, "verify download", and then "Firmware Upgrade". - Select "Start Wireless Stack". - Disconnect from the device. @@ -55,7 +56,9 @@ async fn main(spawner: Spawner) { Note: extended stack versions are not supported at this time. Do not attempt to install a stack with "extended" in the name. */ - let p = embassy_stm32::init(Default::default()); + let mut config = embassy_stm32::Config::default(); + config.rcc = WPAN_DEFAULT; + let p = embassy_stm32::init(config); info!("Hello World!"); let config = Config::default(); diff --git a/examples/stm32wb/src/bin/mac_rfd.rs b/examples/stm32wb/src/bin/mac_rfd.rs index 4d8b6601..000355de 100644 --- a/examples/stm32wb/src/bin/mac_rfd.rs +++ b/examples/stm32wb/src/bin/mac_rfd.rs @@ -6,6 +6,7 @@ use defmt::*; use embassy_executor::Spawner; use embassy_stm32::bind_interrupts; use embassy_stm32::ipcc::{Config, ReceiveInterruptHandler, TransmitInterruptHandler}; +use embassy_stm32::rcc::WPAN_DEFAULT; use embassy_stm32_wpan::mac::commands::{AssociateRequest, DataRequest, GetRequest, ResetRequest, SetRequest}; use embassy_stm32_wpan::mac::event::MacEvent; use embassy_stm32_wpan::mac::typedefs::{ @@ -32,7 +33,7 @@ async fn main(spawner: Spawner) { - Obtain a NUCLEO-STM32WB55 from your preferred supplier. - Download and Install STM32CubeProgrammer. - - Download stm32wb5x_FUS_fw.bin, stm32wb5x_BLE_Stack_full_fw.bin, and Release_Notes.html from + - Download stm32wb5x_FUS_fw.bin, stm32wb5x_BLE_Mac_802_15_4_fw.bin, and Release_Notes.html from gh:STMicroelectronics/STM32CubeWB@2234d97/Projects/STM32WB_Copro_Wireless_Binaries/STM32WB5x - Open STM32CubeProgrammer - On the right-hand pane, click "firmware upgrade" to upgrade the st-link firmware. @@ -41,7 +42,7 @@ async fn main(spawner: Spawner) { - In the Release_Notes.html, find the memory address that corresponds to your device for the stm32wb5x_FUS_fw.bin file - Select that file, the memory address, "verify download", and then "Firmware Upgrade". - Once complete, in the Release_Notes.html, find the memory address that corresponds to your device for the - stm32wb5x_BLE_Stack_full_fw.bin file. It should not be the same memory address. + stm32wb5x_BLE_Mac_802_15_4_fw.bin file. It should not be the same memory address. - Select that file, the memory address, "verify download", and then "Firmware Upgrade". - Select "Start Wireless Stack". - Disconnect from the device. @@ -51,7 +52,9 @@ async fn main(spawner: Spawner) { Note: extended stack versions are not supported at this time. Do not attempt to install a stack with "extended" in the name. */ - let p = embassy_stm32::init(Default::default()); + let mut config = embassy_stm32::Config::default(); + config.rcc = WPAN_DEFAULT; + let p = embassy_stm32::init(config); info!("Hello World!"); let config = Config::default(); diff --git a/examples/stm32wb/src/bin/tl_mbox.rs b/examples/stm32wb/src/bin/tl_mbox.rs index 9fc4b8aa..fc49c3c4 100644 --- a/examples/stm32wb/src/bin/tl_mbox.rs +++ b/examples/stm32wb/src/bin/tl_mbox.rs @@ -6,6 +6,7 @@ use defmt::*; use embassy_executor::Spawner; use embassy_stm32::bind_interrupts; use embassy_stm32::ipcc::{Config, ReceiveInterruptHandler, TransmitInterruptHandler}; +use embassy_stm32::rcc::WPAN_DEFAULT; use embassy_stm32_wpan::TlMbox; use embassy_time::{Duration, Timer}; use {defmt_rtt as _, panic_probe as _}; @@ -41,7 +42,9 @@ async fn main(_spawner: Spawner) { Note: extended stack versions are not supported at this time. Do not attempt to install a stack with "extended" in the name. */ - let p = embassy_stm32::init(Default::default()); + let mut config = embassy_stm32::Config::default(); + config.rcc = WPAN_DEFAULT; + let p = embassy_stm32::init(config); info!("Hello World!"); let config = Config::default(); diff --git a/examples/stm32wb/src/bin/tl_mbox_ble.rs b/examples/stm32wb/src/bin/tl_mbox_ble.rs index 90349422..5745ebd0 100644 --- a/examples/stm32wb/src/bin/tl_mbox_ble.rs +++ b/examples/stm32wb/src/bin/tl_mbox_ble.rs @@ -6,6 +6,7 @@ use defmt::*; use embassy_executor::Spawner; use embassy_stm32::bind_interrupts; use embassy_stm32::ipcc::{Config, ReceiveInterruptHandler, TransmitInterruptHandler}; +use embassy_stm32::rcc::WPAN_DEFAULT; use embassy_stm32_wpan::TlMbox; use {defmt_rtt as _, panic_probe as _}; @@ -40,7 +41,9 @@ async fn main(_spawner: Spawner) { Note: extended stack versions are not supported at this time. Do not attempt to install a stack with "extended" in the name. */ - let p = embassy_stm32::init(Default::default()); + let mut config = embassy_stm32::Config::default(); + config.rcc = WPAN_DEFAULT; + let p = embassy_stm32::init(config); info!("Hello World!"); let config = Config::default(); diff --git a/examples/stm32wb/src/bin/tl_mbox_mac.rs b/examples/stm32wb/src/bin/tl_mbox_mac.rs index 5931c392..f32e07d9 100644 --- a/examples/stm32wb/src/bin/tl_mbox_mac.rs +++ b/examples/stm32wb/src/bin/tl_mbox_mac.rs @@ -6,6 +6,7 @@ use defmt::*; use embassy_executor::Spawner; use embassy_stm32::bind_interrupts; use embassy_stm32::ipcc::{Config, ReceiveInterruptHandler, TransmitInterruptHandler}; +use embassy_stm32::rcc::WPAN_DEFAULT; use embassy_stm32_wpan::sub::mm; use embassy_stm32_wpan::TlMbox; use {defmt_rtt as _, panic_probe as _}; @@ -27,7 +28,7 @@ async fn main(spawner: Spawner) { - Obtain a NUCLEO-STM32WB55 from your preferred supplier. - Download and Install STM32CubeProgrammer. - - Download stm32wb5x_FUS_fw.bin, stm32wb5x_BLE_Stack_full_fw.bin, and Release_Notes.html from + - Download stm32wb5x_FUS_fw.bin, stm32wb5x_BLE_Mac_802_15_4_fw.bin, and Release_Notes.html from gh:STMicroelectronics/STM32CubeWB@2234d97/Projects/STM32WB_Copro_Wireless_Binaries/STM32WB5x - Open STM32CubeProgrammer - On the right-hand pane, click "firmware upgrade" to upgrade the st-link firmware. @@ -36,7 +37,7 @@ async fn main(spawner: Spawner) { - In the Release_Notes.html, find the memory address that corresponds to your device for the stm32wb5x_FUS_fw.bin file - Select that file, the memory address, "verify download", and then "Firmware Upgrade". - Once complete, in the Release_Notes.html, find the memory address that corresponds to your device for the - stm32wb5x_BLE_Stack_full_fw.bin file. It should not be the same memory address. + stm32wb5x_BLE_Mac_802_15_4_fw.bin file. It should not be the same memory address. - Select that file, the memory address, "verify download", and then "Firmware Upgrade". - Select "Start Wireless Stack". - Disconnect from the device. @@ -46,7 +47,9 @@ async fn main(spawner: Spawner) { Note: extended stack versions are not supported at this time. Do not attempt to install a stack with "extended" in the name. */ - let p = embassy_stm32::init(Default::default()); + let mut config = embassy_stm32::Config::default(); + config.rcc = WPAN_DEFAULT; + let p = embassy_stm32::init(config); info!("Hello World!"); let config = Config::default(); From 6b1d802caa9ca5a2b6d33bf345c0599b990311fa Mon Sep 17 00:00:00 2001 From: bofh <123368+bofh@users.noreply.github.com> Date: Sun, 30 Jul 2023 18:01:34 +0200 Subject: [PATCH 55/68] Move frequency to SPI config --- embassy-stm32/src/spi/mod.rs | 66 ++++++++++++++++++++++++++---------- 1 file changed, 49 insertions(+), 17 deletions(-) diff --git a/embassy-stm32/src/spi/mod.rs b/embassy-stm32/src/spi/mod.rs index bdf3c85b..bbc7c3b9 100644 --- a/embassy-stm32/src/spi/mod.rs +++ b/embassy-stm32/src/spi/mod.rs @@ -36,6 +36,7 @@ pub enum BitOrder { pub struct Config { pub mode: Mode, pub bit_order: BitOrder, + pub frequency: Hertz, } impl Default for Config { @@ -43,6 +44,7 @@ impl Default for Config { Self { mode: MODE_0, bit_order: BitOrder::MsbFirst, + frequency: Hertz(1_000_000), } } } @@ -88,7 +90,6 @@ impl<'d, T: Instance, Tx, Rx> Spi<'d, T, Tx, Rx> { miso: impl Peripheral

> + 'd, txdma: impl Peripheral

+ 'd, rxdma: impl Peripheral

+ 'd, - freq: Hertz, config: Config, ) -> Self { into_ref!(peri, sck, mosi, miso); @@ -112,7 +113,6 @@ impl<'d, T: Instance, Tx, Rx> Spi<'d, T, Tx, Rx> { Some(miso.map_into()), txdma, rxdma, - freq, config, ) } @@ -123,7 +123,6 @@ impl<'d, T: Instance, Tx, Rx> Spi<'d, T, Tx, Rx> { miso: impl Peripheral

> + 'd, txdma: impl Peripheral

+ 'd, // TODO remove rxdma: impl Peripheral

+ 'd, - freq: Hertz, config: Config, ) -> Self { into_ref!(sck, miso); @@ -139,7 +138,6 @@ impl<'d, T: Instance, Tx, Rx> Spi<'d, T, Tx, Rx> { Some(miso.map_into()), txdma, rxdma, - freq, config, ) } @@ -150,7 +148,6 @@ impl<'d, T: Instance, Tx, Rx> Spi<'d, T, Tx, Rx> { mosi: impl Peripheral

> + 'd, txdma: impl Peripheral

+ 'd, rxdma: impl Peripheral

+ 'd, // TODO remove - freq: Hertz, config: Config, ) -> Self { into_ref!(sck, mosi); @@ -166,7 +163,6 @@ impl<'d, T: Instance, Tx, Rx> Spi<'d, T, Tx, Rx> { None, txdma, rxdma, - freq, config, ) } @@ -176,14 +172,13 @@ impl<'d, T: Instance, Tx, Rx> Spi<'d, T, Tx, Rx> { mosi: impl Peripheral

> + 'd, txdma: impl Peripheral

+ 'd, rxdma: impl Peripheral

+ 'd, // TODO: remove - freq: Hertz, config: Config, ) -> Self { into_ref!(mosi); mosi.set_as_af_pull(mosi.af_num(), AFType::OutputPushPull, Pull::Down); mosi.set_speed(crate::gpio::Speed::Medium); - Self::new_inner(peri, None, Some(mosi.map_into()), None, txdma, rxdma, freq, config) + Self::new_inner(peri, None, Some(mosi.map_into()), None, txdma, rxdma, config) } #[cfg(stm32wl)] @@ -201,7 +196,8 @@ impl<'d, T: Instance, Tx, Rx> Spi<'d, T, Tx, Rx> { let mut config = Config::default(); config.mode = MODE_0; config.bit_order = BitOrder::MsbFirst; - Self::new_inner(peri, None, None, None, txdma, rxdma, freq, config) + config.frequency = freq; + Self::new_inner(peri, None, None, None, txdma, rxdma, config) } #[allow(dead_code)] @@ -209,10 +205,9 @@ impl<'d, T: Instance, Tx, Rx> Spi<'d, T, Tx, Rx> { peri: impl Peripheral

+ 'd, txdma: impl Peripheral

+ 'd, rxdma: impl Peripheral

+ 'd, - freq: Hertz, config: Config, ) -> Self { - Self::new_inner(peri, None, None, None, txdma, rxdma, freq, config) + Self::new_inner(peri, None, None, None, txdma, rxdma, config) } fn new_inner( @@ -222,12 +217,12 @@ impl<'d, T: Instance, Tx, Rx> Spi<'d, T, Tx, Rx> { miso: Option>, txdma: impl Peripheral

+ 'd, rxdma: impl Peripheral

+ 'd, - freq: Hertz, config: Config, ) -> Self { into_ref!(peri, txdma, rxdma); let pclk = T::frequency(); + let freq = config.frequency; let br = compute_baud_rate(pclk, freq.into()); let cpha = config.raw_phase(); @@ -334,19 +329,29 @@ impl<'d, T: Instance, Tx, Rx> Spi<'d, T, Tx, Rx> { let lsbfirst = config.raw_byte_order(); + let pclk = T::frequency(); + let freq = config.frequency; + let br = compute_baud_rate(pclk, freq.into()); + #[cfg(any(spi_v1, spi_f1, spi_v2))] T::REGS.cr1().modify(|w| { w.set_cpha(cpha); w.set_cpol(cpol); + w.set_br(br); w.set_lsbfirst(lsbfirst); }); #[cfg(any(spi_v3, spi_v4, spi_v5))] - T::REGS.cfg2().modify(|w| { - w.set_cpha(cpha); - w.set_cpol(cpol); - w.set_lsbfirst(lsbfirst); - }); + { + T::REGS.cfg2().modify(|w| { + w.set_cpha(cpha); + w.set_cpol(cpol); + w.set_lsbfirst(lsbfirst); + }); + T::REGS.cfg1().modify(|w| { + w.set_mbr(br); + }); + } } pub fn get_current_config(&self) -> Config { @@ -354,6 +359,9 @@ impl<'d, T: Instance, Tx, Rx> Spi<'d, T, Tx, Rx> { let cfg = T::REGS.cr1().read(); #[cfg(any(spi_v3, spi_v4, spi_v5))] let cfg = T::REGS.cfg2().read(); + #[cfg(any(spi_v3, spi_v4, spi_v5))] + let cfg1 = T::REGS.cfg1().read(); + let polarity = if cfg.cpol() == vals::Cpol::IDLELOW { Polarity::IdleLow } else { @@ -371,9 +379,18 @@ impl<'d, T: Instance, Tx, Rx> Spi<'d, T, Tx, Rx> { BitOrder::MsbFirst }; + #[cfg(any(spi_v1, spi_f1, spi_v2))] + let br = cfg.br(); + #[cfg(any(spi_v3, spi_v4, spi_v5))] + let br = cfg1.mbr(); + + let pclk = T::frequency(); + let frequency = compute_frequency(pclk, br); + Config { mode: Mode { polarity, phase }, bit_order, + frequency, } } @@ -653,6 +670,21 @@ fn compute_baud_rate(clocks: Hertz, freq: Hertz) -> Br { Br::from_bits(val) } +fn compute_frequency(clocks: Hertz, br: Br) -> Hertz { + let div: u16 = match br { + Br::DIV2 => 2, + Br::DIV4 => 4, + Br::DIV8 => 8, + Br::DIV16 => 16, + Br::DIV32 => 32, + Br::DIV64 => 64, + Br::DIV128 => 128, + Br::DIV256 => 256, + }; + + clocks / div +} + trait RegsExt { fn tx_ptr(&self) -> *mut W; fn rx_ptr(&self) -> *mut W; From aef93246b429cf892ad212aefc8ca0e5ddc29c18 Mon Sep 17 00:00:00 2001 From: bofh <123368+bofh@users.noreply.github.com> Date: Sun, 30 Jul 2023 18:11:39 +0200 Subject: [PATCH 56/68] Fix Spi::new_internal call in i2s --- embassy-stm32/src/i2s.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/embassy-stm32/src/i2s.rs b/embassy-stm32/src/i2s.rs index 1ccad732..f5103676 100644 --- a/embassy-stm32/src/i2s.rs +++ b/embassy-stm32/src/i2s.rs @@ -165,7 +165,9 @@ impl<'d, T: Instance, Tx, Rx> I2S<'d, T, Tx, Rx> { mck.set_as_af(mck.af_num(), AFType::OutputPushPull); mck.set_speed(crate::gpio::Speed::VeryHigh); - let spi = Spi::new_internal(peri, txdma, rxdma, freq, SpiConfig::default()); + let mut spi_cfg = SpiConfig::default(); + spi_cfg.freq = freq; + let spi = Spi::new_internal(peri, txdma, rxdma, spi_cfg); #[cfg(all(rcc_f4, not(stm32f410)))] let pclk = unsafe { get_freqs() }.plli2s.unwrap(); From 1d815f4ba09b7be88e650690806e18c8a4d8e051 Mon Sep 17 00:00:00 2001 From: bofh <123368+bofh@users.noreply.github.com> Date: Sun, 30 Jul 2023 18:20:36 +0200 Subject: [PATCH 57/68] Fix typo --- embassy-stm32/src/i2s.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/embassy-stm32/src/i2s.rs b/embassy-stm32/src/i2s.rs index f5103676..8fd3a8c6 100644 --- a/embassy-stm32/src/i2s.rs +++ b/embassy-stm32/src/i2s.rs @@ -166,7 +166,7 @@ impl<'d, T: Instance, Tx, Rx> I2S<'d, T, Tx, Rx> { mck.set_speed(crate::gpio::Speed::VeryHigh); let mut spi_cfg = SpiConfig::default(); - spi_cfg.freq = freq; + spi_cfg.frequency = freq; let spi = Spi::new_internal(peri, txdma, rxdma, spi_cfg); #[cfg(all(rcc_f4, not(stm32f410)))] From d2127f6b82e7a97744c81e2a3628ced59d8f1b49 Mon Sep 17 00:00:00 2001 From: bofh <123368+bofh@users.noreply.github.com> Date: Sun, 30 Jul 2023 18:58:40 +0200 Subject: [PATCH 58/68] Fix stm32 SPI examples --- examples/stm32f3/src/bin/spi_dma.rs | 2 -- examples/stm32f4/src/bin/spi.rs | 12 +----------- examples/stm32f4/src/bin/spi_dma.rs | 2 -- examples/stm32g0/src/bin/spi_neopixel.rs | 4 +++- examples/stm32l0/src/bin/spi.rs | 2 -- examples/stm32l1/src/bin/spi.rs | 2 -- examples/stm32l4/src/bin/spi.rs | 12 +----------- examples/stm32l4/src/bin/spi_blocking_async.rs | 2 -- examples/stm32l4/src/bin/spi_dma.rs | 2 -- 9 files changed, 5 insertions(+), 35 deletions(-) diff --git a/examples/stm32f3/src/bin/spi_dma.rs b/examples/stm32f3/src/bin/spi_dma.rs index 95b2b686..d8cdb7e6 100644 --- a/examples/stm32f3/src/bin/spi_dma.rs +++ b/examples/stm32f3/src/bin/spi_dma.rs @@ -8,7 +8,6 @@ use core::str::from_utf8; use defmt::*; use embassy_executor::Spawner; use embassy_stm32::spi::{Config, Spi}; -use embassy_stm32::time::Hertz; use heapless::String; use {defmt_rtt as _, panic_probe as _}; @@ -24,7 +23,6 @@ async fn main(_spawner: Spawner) { p.PB4, p.DMA1_CH3, p.DMA1_CH2, - Hertz(1_000_000), Config::default(), ); diff --git a/examples/stm32f4/src/bin/spi.rs b/examples/stm32f4/src/bin/spi.rs index 05b48f47..b0c62b3f 100644 --- a/examples/stm32f4/src/bin/spi.rs +++ b/examples/stm32f4/src/bin/spi.rs @@ -7,7 +7,6 @@ use defmt::*; use embassy_stm32::dma::NoDma; use embassy_stm32::gpio::{Level, Output, Speed}; use embassy_stm32::spi::{Config, Spi}; -use embassy_stm32::time::Hertz; use {defmt_rtt as _, panic_probe as _}; #[entry] @@ -16,16 +15,7 @@ fn main() -> ! { let p = embassy_stm32::init(Default::default()); - let mut spi = Spi::new( - p.SPI3, - p.PC10, - p.PC12, - p.PC11, - NoDma, - NoDma, - Hertz(1_000_000), - Config::default(), - ); + let mut spi = Spi::new(p.SPI3, p.PC10, p.PC12, p.PC11, NoDma, NoDma, Config::default()); let mut cs = Output::new(p.PE0, Level::High, Speed::VeryHigh); diff --git a/examples/stm32f4/src/bin/spi_dma.rs b/examples/stm32f4/src/bin/spi_dma.rs index 3d2a1a1a..d421b9d5 100644 --- a/examples/stm32f4/src/bin/spi_dma.rs +++ b/examples/stm32f4/src/bin/spi_dma.rs @@ -8,7 +8,6 @@ use core::str::from_utf8; use defmt::*; use embassy_executor::Spawner; use embassy_stm32::spi::{Config, Spi}; -use embassy_stm32::time::Hertz; use heapless::String; use {defmt_rtt as _, panic_probe as _}; @@ -24,7 +23,6 @@ async fn main(_spawner: Spawner) { p.PB4, p.DMA2_CH3, p.DMA2_CH2, - Hertz(1_000_000), Config::default(), ); diff --git a/examples/stm32g0/src/bin/spi_neopixel.rs b/examples/stm32g0/src/bin/spi_neopixel.rs index 81fdd15c..ee7aaf33 100644 --- a/examples/stm32g0/src/bin/spi_neopixel.rs +++ b/examples/stm32g0/src/bin/spi_neopixel.rs @@ -76,7 +76,9 @@ async fn main(_spawner: Spawner) { let p = embassy_stm32::init(Default::default()); info!("Start test using spi as neopixel driver"); - let mut spi = Spi::new_txonly_nosck(p.SPI1, p.PB5, p.DMA1_CH3, NoDma, Hertz(4_000_000), Config::default()); + let mut config = Config::default(); + config.frequency = Hertz(4_000_000); + let mut spi = Spi::new_txonly_nosck(p.SPI1, p.PB5, p.DMA1_CH3, NoDma, config); let mut neopixels = Ws2812::new(); diff --git a/examples/stm32l0/src/bin/spi.rs b/examples/stm32l0/src/bin/spi.rs index 9b5b3e27..54b7a2dd 100644 --- a/examples/stm32l0/src/bin/spi.rs +++ b/examples/stm32l0/src/bin/spi.rs @@ -7,7 +7,6 @@ use embassy_executor::Spawner; use embassy_stm32::dma::NoDma; use embassy_stm32::gpio::{Level, Output, Speed}; use embassy_stm32::spi::{Config, Spi}; -use embassy_stm32::time::Hertz; use {defmt_rtt as _, panic_probe as _}; #[embassy_executor::main] @@ -22,7 +21,6 @@ async fn main(_spawner: Spawner) { p.PA6, NoDma, NoDma, - Hertz(1_000_000), Config::default(), ); diff --git a/examples/stm32l1/src/bin/spi.rs b/examples/stm32l1/src/bin/spi.rs index 0a532e8e..34faf061 100644 --- a/examples/stm32l1/src/bin/spi.rs +++ b/examples/stm32l1/src/bin/spi.rs @@ -7,7 +7,6 @@ use embassy_executor::Spawner; use embassy_stm32::dma::NoDma; use embassy_stm32::gpio::{Level, Output, Speed}; use embassy_stm32::spi::{Config, Spi}; -use embassy_stm32::time::Hertz; use {defmt_rtt as _, panic_probe as _}; #[embassy_executor::main] @@ -22,7 +21,6 @@ async fn main(_spawner: Spawner) { p.PA6, NoDma, NoDma, - Hertz(1_000_000), Config::default(), ); diff --git a/examples/stm32l4/src/bin/spi.rs b/examples/stm32l4/src/bin/spi.rs index 76e316a2..a3097fb4 100644 --- a/examples/stm32l4/src/bin/spi.rs +++ b/examples/stm32l4/src/bin/spi.rs @@ -6,7 +6,6 @@ use defmt::*; use embassy_stm32::dma::NoDma; use embassy_stm32::gpio::{Level, Output, Speed}; use embassy_stm32::spi::{Config, Spi}; -use embassy_stm32::time::Hertz; use {defmt_rtt as _, panic_probe as _}; #[cortex_m_rt::entry] @@ -15,16 +14,7 @@ fn main() -> ! { let p = embassy_stm32::init(Default::default()); - let mut spi = Spi::new( - p.SPI3, - p.PC10, - p.PC12, - p.PC11, - NoDma, - NoDma, - Hertz(1_000_000), - Config::default(), - ); + let mut spi = Spi::new(p.SPI3, p.PC10, p.PC12, p.PC11, NoDma, NoDma, Config::default()); let mut cs = Output::new(p.PE0, Level::High, Speed::VeryHigh); diff --git a/examples/stm32l4/src/bin/spi_blocking_async.rs b/examples/stm32l4/src/bin/spi_blocking_async.rs index 62ef0130..868e2211 100644 --- a/examples/stm32l4/src/bin/spi_blocking_async.rs +++ b/examples/stm32l4/src/bin/spi_blocking_async.rs @@ -8,7 +8,6 @@ use embassy_executor::Spawner; use embassy_stm32::dma::NoDma; use embassy_stm32::gpio::{Input, Level, Output, Pull, Speed}; use embassy_stm32::spi::{Config, Spi}; -use embassy_stm32::time::Hertz; use embedded_hal_async::spi::SpiBus; use {defmt_rtt as _, panic_probe as _}; @@ -24,7 +23,6 @@ async fn main(_spawner: Spawner) { p.PC11, NoDma, NoDma, - Hertz(1_000_000), Config::default(), ); diff --git a/examples/stm32l4/src/bin/spi_dma.rs b/examples/stm32l4/src/bin/spi_dma.rs index 89471db5..01265c49 100644 --- a/examples/stm32l4/src/bin/spi_dma.rs +++ b/examples/stm32l4/src/bin/spi_dma.rs @@ -6,7 +6,6 @@ use defmt::*; use embassy_executor::Spawner; use embassy_stm32::gpio::{Input, Level, Output, Pull, Speed}; use embassy_stm32::spi::{Config, Spi}; -use embassy_stm32::time::Hertz; use {defmt_rtt as _, panic_probe as _}; #[embassy_executor::main] @@ -21,7 +20,6 @@ async fn main(_spawner: Spawner) { p.PC11, p.DMA1_CH1, p.DMA1_CH2, - Hertz(1_000_000), Config::default(), ); From 4f791799a9c05e7e34c70d47ac0b2e885d5714a0 Mon Sep 17 00:00:00 2001 From: bofh <123368+bofh@users.noreply.github.com> Date: Sun, 30 Jul 2023 19:12:49 +0200 Subject: [PATCH 59/68] Fix formatting --- examples/stm32f3/src/bin/spi_dma.rs | 10 +--------- examples/stm32f4/src/bin/spi_dma.rs | 10 +--------- examples/stm32l0/src/bin/spi.rs | 10 +--------- examples/stm32l1/src/bin/spi.rs | 10 +--------- examples/stm32l4/src/bin/spi_blocking_async.rs | 10 +--------- 5 files changed, 5 insertions(+), 45 deletions(-) diff --git a/examples/stm32f3/src/bin/spi_dma.rs b/examples/stm32f3/src/bin/spi_dma.rs index d8cdb7e6..6a39097d 100644 --- a/examples/stm32f3/src/bin/spi_dma.rs +++ b/examples/stm32f3/src/bin/spi_dma.rs @@ -16,15 +16,7 @@ async fn main(_spawner: Spawner) { let p = embassy_stm32::init(Default::default()); info!("Hello World!"); - let mut spi = Spi::new( - p.SPI1, - p.PB3, - p.PB5, - p.PB4, - p.DMA1_CH3, - p.DMA1_CH2, - Config::default(), - ); + let mut spi = Spi::new(p.SPI1, p.PB3, p.PB5, p.PB4, p.DMA1_CH3, p.DMA1_CH2, Config::default()); for n in 0u32.. { let mut write: String<128> = String::new(); diff --git a/examples/stm32f4/src/bin/spi_dma.rs b/examples/stm32f4/src/bin/spi_dma.rs index d421b9d5..4edea93a 100644 --- a/examples/stm32f4/src/bin/spi_dma.rs +++ b/examples/stm32f4/src/bin/spi_dma.rs @@ -16,15 +16,7 @@ async fn main(_spawner: Spawner) { let p = embassy_stm32::init(Default::default()); info!("Hello World!"); - let mut spi = Spi::new( - p.SPI1, - p.PB3, - p.PB5, - p.PB4, - p.DMA2_CH3, - p.DMA2_CH2, - Config::default(), - ); + let mut spi = Spi::new(p.SPI1, p.PB3, p.PB5, p.PB4, p.DMA2_CH3, p.DMA2_CH2, Config::default()); for n in 0u32.. { let mut write: String<128> = String::new(); diff --git a/examples/stm32l0/src/bin/spi.rs b/examples/stm32l0/src/bin/spi.rs index 54b7a2dd..24ea9a68 100644 --- a/examples/stm32l0/src/bin/spi.rs +++ b/examples/stm32l0/src/bin/spi.rs @@ -14,15 +14,7 @@ async fn main(_spawner: Spawner) { let p = embassy_stm32::init(Default::default()); info!("Hello World, folks!"); - let mut spi = Spi::new( - p.SPI1, - p.PB3, - p.PA7, - p.PA6, - NoDma, - NoDma, - Config::default(), - ); + let mut spi = Spi::new(p.SPI1, p.PB3, p.PA7, p.PA6, NoDma, NoDma, Config::default()); let mut cs = Output::new(p.PA15, Level::High, Speed::VeryHigh); diff --git a/examples/stm32l1/src/bin/spi.rs b/examples/stm32l1/src/bin/spi.rs index 34faf061..53523140 100644 --- a/examples/stm32l1/src/bin/spi.rs +++ b/examples/stm32l1/src/bin/spi.rs @@ -14,15 +14,7 @@ async fn main(_spawner: Spawner) { let p = embassy_stm32::init(Default::default()); info!("Hello World, folks!"); - let mut spi = Spi::new( - p.SPI1, - p.PA5, - p.PA7, - p.PA6, - NoDma, - NoDma, - Config::default(), - ); + let mut spi = Spi::new(p.SPI1, p.PA5, p.PA7, p.PA6, NoDma, NoDma, Config::default()); let mut cs = Output::new(p.PA4, Level::High, Speed::VeryHigh); diff --git a/examples/stm32l4/src/bin/spi_blocking_async.rs b/examples/stm32l4/src/bin/spi_blocking_async.rs index 868e2211..45af8a9f 100644 --- a/examples/stm32l4/src/bin/spi_blocking_async.rs +++ b/examples/stm32l4/src/bin/spi_blocking_async.rs @@ -16,15 +16,7 @@ async fn main(_spawner: Spawner) { let p = embassy_stm32::init(Default::default()); info!("Hello World!"); - let spi = Spi::new( - p.SPI3, - p.PC10, - p.PC12, - p.PC11, - NoDma, - NoDma, - Config::default(), - ); + let spi = Spi::new(p.SPI3, p.PC10, p.PC12, p.PC11, NoDma, NoDma, Config::default()); let mut spi = BlockingAsync::new(spi); From 55fb1d51268e87b522ba47e4cdc04978e9880ab0 Mon Sep 17 00:00:00 2001 From: bofh <123368+bofh@users.noreply.github.com> Date: Sun, 30 Jul 2023 19:26:24 +0200 Subject: [PATCH 60/68] Fix more stm32 SPI examples --- examples/stm32h7/src/bin/spi.rs | 6 ++++-- examples/stm32h7/src/bin/spi_dma.rs | 6 ++++-- examples/stm32l0/src/bin/lora_cad.rs | 6 ++++-- examples/stm32l0/src/bin/lora_lorawan.rs | 6 ++++-- examples/stm32l0/src/bin/lora_p2p_receive.rs | 6 ++++-- examples/stm32l0/src/bin/lora_p2p_send.rs | 6 ++++-- tests/stm32/src/bin/spi.rs | 1 - tests/stm32/src/bin/spi_dma.rs | 1 - 8 files changed, 24 insertions(+), 14 deletions(-) diff --git a/examples/stm32h7/src/bin/spi.rs b/examples/stm32h7/src/bin/spi.rs index 1f407f00..051a8392 100644 --- a/examples/stm32h7/src/bin/spi.rs +++ b/examples/stm32h7/src/bin/spi.rs @@ -43,6 +43,9 @@ fn main() -> ! { config.rcc.pll1.q_ck = Some(mhz(100)); let p = embassy_stm32::init(config); + let mut spi_config = spi::Config::default(); + spi_config.frequency = mhz(1); + let spi = spi::Spi::new( p.SPI3, p.PB3, @@ -50,8 +53,7 @@ fn main() -> ! { p.PB4, NoDma, NoDma, - mhz(1), - spi::Config::default(), + spi_config, ); let executor = EXECUTOR.init(Executor::new()); diff --git a/examples/stm32h7/src/bin/spi_dma.rs b/examples/stm32h7/src/bin/spi_dma.rs index 53004fc9..5e878ee6 100644 --- a/examples/stm32h7/src/bin/spi_dma.rs +++ b/examples/stm32h7/src/bin/spi_dma.rs @@ -39,6 +39,9 @@ fn main() -> ! { config.rcc.pll1.q_ck = Some(mhz(100)); let p = embassy_stm32::init(config); + let mut spi_config = spi::Config::default(); + spi_config.frequency = mhz(1); + let spi = spi::Spi::new( p.SPI3, p.PB3, @@ -46,8 +49,7 @@ fn main() -> ! { p.PB4, p.DMA1_CH3, p.DMA1_CH4, - mhz(1), - spi::Config::default(), + spi_config, ); let executor = EXECUTOR.init(Executor::new()); diff --git a/examples/stm32l0/src/bin/lora_cad.rs b/examples/stm32l0/src/bin/lora_cad.rs index 588cea1e..ae2393e5 100644 --- a/examples/stm32l0/src/bin/lora_cad.rs +++ b/examples/stm32l0/src/bin/lora_cad.rs @@ -27,6 +27,9 @@ async fn main(_spawner: Spawner) { config.rcc.enable_hsi48 = true; let p = embassy_stm32::init(config); + let mut spi_config = spi::Config::default(); + spi_config.frequency = khz(200); + // SPI for sx1276 let spi = spi::Spi::new( p.SPI1, @@ -35,8 +38,7 @@ async fn main(_spawner: Spawner) { p.PA6, p.DMA1_CH3, p.DMA1_CH2, - khz(200), - spi::Config::default(), + spi_config, ); let nss = Output::new(p.PA15.degrade(), Level::High, Speed::Low); diff --git a/examples/stm32l0/src/bin/lora_lorawan.rs b/examples/stm32l0/src/bin/lora_lorawan.rs index c397edd5..2202deea 100644 --- a/examples/stm32l0/src/bin/lora_lorawan.rs +++ b/examples/stm32l0/src/bin/lora_lorawan.rs @@ -32,6 +32,9 @@ async fn main(_spawner: Spawner) { config.rcc.enable_hsi48 = true; let p = embassy_stm32::init(config); + let mut spi_config = spi::Config::default(); + spi_config.frequency = khz(200); + // SPI for sx1276 let spi = spi::Spi::new( p.SPI1, @@ -40,8 +43,7 @@ async fn main(_spawner: Spawner) { p.PA6, p.DMA1_CH3, p.DMA1_CH2, - khz(200), - spi::Config::default(), + spi_config, ); let nss = Output::new(p.PA15.degrade(), Level::High, Speed::Low); diff --git a/examples/stm32l0/src/bin/lora_p2p_receive.rs b/examples/stm32l0/src/bin/lora_p2p_receive.rs index bb750950..9b6b18b8 100644 --- a/examples/stm32l0/src/bin/lora_p2p_receive.rs +++ b/examples/stm32l0/src/bin/lora_p2p_receive.rs @@ -27,6 +27,9 @@ async fn main(_spawner: Spawner) { config.rcc.enable_hsi48 = true; let p = embassy_stm32::init(config); + let mut spi_config = spi::Config::default(); + spi_config.frequency = khz(200); + // SPI for sx1276 let spi = spi::Spi::new( p.SPI1, @@ -35,8 +38,7 @@ async fn main(_spawner: Spawner) { p.PA6, p.DMA1_CH3, p.DMA1_CH2, - khz(200), - spi::Config::default(), + spi_config, ); let nss = Output::new(p.PA15.degrade(), Level::High, Speed::Low); diff --git a/examples/stm32l0/src/bin/lora_p2p_send.rs b/examples/stm32l0/src/bin/lora_p2p_send.rs index e6fadc01..90bae31e 100644 --- a/examples/stm32l0/src/bin/lora_p2p_send.rs +++ b/examples/stm32l0/src/bin/lora_p2p_send.rs @@ -27,6 +27,9 @@ async fn main(_spawner: Spawner) { config.rcc.enable_hsi48 = true; let p = embassy_stm32::init(config); + let mut spi_config = spi::Config::default(); + spi_config.frequency = khz(200); + // SPI for sx1276 let spi = spi::Spi::new( p.SPI1, @@ -35,8 +38,7 @@ async fn main(_spawner: Spawner) { p.PA6, p.DMA1_CH3, p.DMA1_CH2, - khz(200), - spi::Config::default(), + spi_config, ); let nss = Output::new(p.PA15.degrade(), Level::High, Speed::Low); diff --git a/tests/stm32/src/bin/spi.rs b/tests/stm32/src/bin/spi.rs index 819ecae3..72a8c92e 100644 --- a/tests/stm32/src/bin/spi.rs +++ b/tests/stm32/src/bin/spi.rs @@ -42,7 +42,6 @@ async fn main(_spawner: Spawner) { miso, // Arduino D12 NoDma, NoDma, - Hertz(1_000_000), spi::Config::default(), ); diff --git a/tests/stm32/src/bin/spi_dma.rs b/tests/stm32/src/bin/spi_dma.rs index 78aad24e..443aab57 100644 --- a/tests/stm32/src/bin/spi_dma.rs +++ b/tests/stm32/src/bin/spi_dma.rs @@ -41,7 +41,6 @@ async fn main(_spawner: Spawner) { miso, // Arduino D12 tx_dma, rx_dma, - Hertz(1_000_000), spi::Config::default(), ); From 04ed45941a22867c8879582586677ba719bc2b4b Mon Sep 17 00:00:00 2001 From: bofh <123368+bofh@users.noreply.github.com> Date: Sun, 30 Jul 2023 19:31:22 +0200 Subject: [PATCH 61/68] Fix format in stm32 SPI examples --- examples/stm32h7/src/bin/spi.rs | 10 +--------- examples/stm32h7/src/bin/spi_dma.rs | 10 +--------- examples/stm32l0/src/bin/lora_cad.rs | 10 +--------- examples/stm32l0/src/bin/lora_lorawan.rs | 10 +--------- examples/stm32l0/src/bin/lora_p2p_receive.rs | 10 +--------- examples/stm32l0/src/bin/lora_p2p_send.rs | 10 +--------- 6 files changed, 6 insertions(+), 54 deletions(-) diff --git a/examples/stm32h7/src/bin/spi.rs b/examples/stm32h7/src/bin/spi.rs index 051a8392..28bba2b8 100644 --- a/examples/stm32h7/src/bin/spi.rs +++ b/examples/stm32h7/src/bin/spi.rs @@ -46,15 +46,7 @@ fn main() -> ! { let mut spi_config = spi::Config::default(); spi_config.frequency = mhz(1); - let spi = spi::Spi::new( - p.SPI3, - p.PB3, - p.PB5, - p.PB4, - NoDma, - NoDma, - spi_config, - ); + let spi = spi::Spi::new(p.SPI3, p.PB3, p.PB5, p.PB4, NoDma, NoDma, spi_config); let executor = EXECUTOR.init(Executor::new()); diff --git a/examples/stm32h7/src/bin/spi_dma.rs b/examples/stm32h7/src/bin/spi_dma.rs index 5e878ee6..f6e30cfa 100644 --- a/examples/stm32h7/src/bin/spi_dma.rs +++ b/examples/stm32h7/src/bin/spi_dma.rs @@ -42,15 +42,7 @@ fn main() -> ! { let mut spi_config = spi::Config::default(); spi_config.frequency = mhz(1); - let spi = spi::Spi::new( - p.SPI3, - p.PB3, - p.PB5, - p.PB4, - p.DMA1_CH3, - p.DMA1_CH4, - spi_config, - ); + let spi = spi::Spi::new(p.SPI3, p.PB3, p.PB5, p.PB4, p.DMA1_CH3, p.DMA1_CH4, spi_config); let executor = EXECUTOR.init(Executor::new()); diff --git a/examples/stm32l0/src/bin/lora_cad.rs b/examples/stm32l0/src/bin/lora_cad.rs index ae2393e5..7729b416 100644 --- a/examples/stm32l0/src/bin/lora_cad.rs +++ b/examples/stm32l0/src/bin/lora_cad.rs @@ -31,15 +31,7 @@ async fn main(_spawner: Spawner) { spi_config.frequency = khz(200); // SPI for sx1276 - let spi = spi::Spi::new( - p.SPI1, - p.PB3, - p.PA7, - p.PA6, - p.DMA1_CH3, - p.DMA1_CH2, - spi_config, - ); + let spi = spi::Spi::new(p.SPI1, p.PB3, p.PA7, p.PA6, p.DMA1_CH3, p.DMA1_CH2, spi_config); let nss = Output::new(p.PA15.degrade(), Level::High, Speed::Low); let reset = Output::new(p.PC0.degrade(), Level::High, Speed::Low); diff --git a/examples/stm32l0/src/bin/lora_lorawan.rs b/examples/stm32l0/src/bin/lora_lorawan.rs index 2202deea..620c9f4f 100644 --- a/examples/stm32l0/src/bin/lora_lorawan.rs +++ b/examples/stm32l0/src/bin/lora_lorawan.rs @@ -36,15 +36,7 @@ async fn main(_spawner: Spawner) { spi_config.frequency = khz(200); // SPI for sx1276 - let spi = spi::Spi::new( - p.SPI1, - p.PB3, - p.PA7, - p.PA6, - p.DMA1_CH3, - p.DMA1_CH2, - spi_config, - ); + let spi = spi::Spi::new(p.SPI1, p.PB3, p.PA7, p.PA6, p.DMA1_CH3, p.DMA1_CH2, spi_config); let nss = Output::new(p.PA15.degrade(), Level::High, Speed::Low); let reset = Output::new(p.PC0.degrade(), Level::High, Speed::Low); diff --git a/examples/stm32l0/src/bin/lora_p2p_receive.rs b/examples/stm32l0/src/bin/lora_p2p_receive.rs index 9b6b18b8..0f9f6095 100644 --- a/examples/stm32l0/src/bin/lora_p2p_receive.rs +++ b/examples/stm32l0/src/bin/lora_p2p_receive.rs @@ -31,15 +31,7 @@ async fn main(_spawner: Spawner) { spi_config.frequency = khz(200); // SPI for sx1276 - let spi = spi::Spi::new( - p.SPI1, - p.PB3, - p.PA7, - p.PA6, - p.DMA1_CH3, - p.DMA1_CH2, - spi_config, - ); + let spi = spi::Spi::new(p.SPI1, p.PB3, p.PA7, p.PA6, p.DMA1_CH3, p.DMA1_CH2, spi_config); let nss = Output::new(p.PA15.degrade(), Level::High, Speed::Low); let reset = Output::new(p.PC0.degrade(), Level::High, Speed::Low); diff --git a/examples/stm32l0/src/bin/lora_p2p_send.rs b/examples/stm32l0/src/bin/lora_p2p_send.rs index 90bae31e..c85c3c2b 100644 --- a/examples/stm32l0/src/bin/lora_p2p_send.rs +++ b/examples/stm32l0/src/bin/lora_p2p_send.rs @@ -31,15 +31,7 @@ async fn main(_spawner: Spawner) { spi_config.frequency = khz(200); // SPI for sx1276 - let spi = spi::Spi::new( - p.SPI1, - p.PB3, - p.PA7, - p.PA6, - p.DMA1_CH3, - p.DMA1_CH2, - spi_config, - ); + let spi = spi::Spi::new(p.SPI1, p.PB3, p.PA7, p.PA6, p.DMA1_CH3, p.DMA1_CH2, spi_config); let nss = Output::new(p.PA15.degrade(), Level::High, Speed::Low); let reset = Output::new(p.PC0.degrade(), Level::High, Speed::Low); From d8420ed5a0cd96eef86c213a40c0bd84f4da0afe Mon Sep 17 00:00:00 2001 From: bofh <123368+bofh@users.noreply.github.com> Date: Sun, 30 Jul 2023 19:34:27 +0200 Subject: [PATCH 62/68] Remove unused imports --- tests/stm32/src/bin/spi.rs | 1 - tests/stm32/src/bin/spi_dma.rs | 1 - 2 files changed, 2 deletions(-) diff --git a/tests/stm32/src/bin/spi.rs b/tests/stm32/src/bin/spi.rs index 72a8c92e..7c2425fc 100644 --- a/tests/stm32/src/bin/spi.rs +++ b/tests/stm32/src/bin/spi.rs @@ -9,7 +9,6 @@ use defmt::assert_eq; use embassy_executor::Spawner; use embassy_stm32::dma::NoDma; use embassy_stm32::spi::{self, Spi}; -use embassy_stm32::time::Hertz; #[embassy_executor::main] async fn main(_spawner: Spawner) { diff --git a/tests/stm32/src/bin/spi_dma.rs b/tests/stm32/src/bin/spi_dma.rs index 443aab57..e70c09f7 100644 --- a/tests/stm32/src/bin/spi_dma.rs +++ b/tests/stm32/src/bin/spi_dma.rs @@ -8,7 +8,6 @@ use common::*; use defmt::assert_eq; use embassy_executor::Spawner; use embassy_stm32::spi::{self, Spi}; -use embassy_stm32::time::Hertz; #[embassy_executor::main] async fn main(_spawner: Spawner) { From 56b21ad42937c450b10e26b098c0cbe2bb8b3c3e Mon Sep 17 00:00:00 2001 From: Michael van Niekerk Date: Sun, 30 Jul 2023 22:13:27 +0200 Subject: [PATCH 63/68] Uart pio fix zeros Prevent UART from only getting 0s from the output --- examples/rp/src/bin/pio_uart.rs | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/examples/rp/src/bin/pio_uart.rs b/examples/rp/src/bin/pio_uart.rs index 5fddbe29..4c382c2e 100644 --- a/examples/rp/src/bin/pio_uart.rs +++ b/examples/rp/src/bin/pio_uart.rs @@ -338,7 +338,7 @@ mod uart_rx { start: wait 0 pin 0 ; Stall until start bit is asserted set x, 7 [10] ; Preload bit counter, then delay until halfway through - rx_bitloop: ; the first data bit (12 cycles incl wait, set). + rx_bitloop: ; the first data bit (12 cycles incl wait, set). in pins, 1 ; Shift data bit into ISR jmp x-- rx_bitloop [6] ; Loop 8 times, each loop iteration is 8 cycles jmp pin good_rx_stop ; Check stop bit (should be high) @@ -347,7 +347,8 @@ mod uart_rx { wait 1 pin 0 ; and wait for line to return to idle state. jmp start ; Don't push data if we didn't see good framing. - good_rx_stop: ; No delay before returning to start; a little slack is + good_rx_stop: ; No delay before returning to start; a little slack is + in null 24 push ; important in case the TX clock is slightly too fast. "# ); @@ -361,8 +362,9 @@ mod uart_rx { sm_rx.set_pin_dirs(Direction::In, &[&rx_pin]); cfg.clock_divider = (U56F8!(125_000_000) / (8 * baud)).to_fixed(); - cfg.shift_out.auto_fill = false; - cfg.shift_out.direction = ShiftDirection::Right; + cfg.shift_in.auto_fill = false; + cfg.shift_in.direction = ShiftDirection::Right; + cfg.shift_in.threshold = 32; cfg.fifo_join = FifoJoin::RxOnly; sm_rx.set_config(&cfg); sm_rx.set_enable(true); From b65406791a718e4e15a388b20c843fff2ab88037 Mon Sep 17 00:00:00 2001 From: Jan Christoph Bernack Date: Sat, 1 Jul 2023 03:15:39 +0200 Subject: [PATCH 64/68] add RNG conditioning --- embassy-stm32/src/rng.rs | 33 +++++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/embassy-stm32/src/rng.rs b/embassy-stm32/src/rng.rs index 27415c2d..5d61d036 100644 --- a/embassy-stm32/src/rng.rs +++ b/embassy-stm32/src/rng.rs @@ -31,6 +31,7 @@ impl<'d, T: Instance> Rng<'d, T> { random } + #[cfg(rng_v1)] pub fn reset(&mut self) { // rng_v2 locks up on seed error, needs reset #[cfg(rng_v2)] @@ -49,6 +50,38 @@ impl<'d, T: Instance> Rng<'d, T> { let _ = self.next_u32(); } + #[cfg(not(rng_v1))] + pub fn reset(&mut self) { + T::regs().cr().modify(|reg| { + reg.set_rngen(false); + reg.set_condrst(true); + // set RNG config "A" according to reference manual + // this has to be written within the same write access as setting the CONDRST bit + reg.set_nistc(pac::rng::vals::Nistc::DEFAULT); + reg.set_rng_config1(pac::rng::vals::RngConfig1::CONFIGA); + reg.set_rng_config2(pac::rng::vals::RngConfig2::CONFIGA_B); + reg.set_rng_config3(pac::rng::vals::RngConfig3::CONFIGA); + reg.set_clkdiv(pac::rng::vals::Clkdiv::NODIV); + }); + // wait for CONDRST to be set + while !T::regs().cr().read().condrst() {} + // magic number must be written immediately before every read or write access to HTCR + T::regs().htcr().write(|w| w.set_htcfg(pac::rng::vals::Htcfg::MAGIC)); + // write recommended value according to reference manual + // note: HTCR can only be written during conditioning + T::regs() + .htcr() + .write(|w| w.set_htcfg(pac::rng::vals::Htcfg::RECOMMENDED)); + // finish conditioning + T::regs().cr().modify(|reg| { + reg.set_rngen(true); + reg.set_condrst(false); + reg.set_ie(true); + }); + // wait for CONDRST to be reset + while T::regs().cr().read().condrst() {} + } + pub async fn async_fill_bytes(&mut self, dest: &mut [u8]) -> Result<(), Error> { T::regs().cr().modify(|reg| { reg.set_rngen(true); From d6c5c1772cf2c6099ab08675afc0fead2e50fffb Mon Sep 17 00:00:00 2001 From: Jan Christoph Bernack Date: Sat, 1 Jul 2023 03:32:01 +0200 Subject: [PATCH 65/68] improve RNG polling --- embassy-stm32/src/rng.rs | 105 ++++++++++++++++++++++++--------------- 1 file changed, 66 insertions(+), 39 deletions(-) diff --git a/embassy-stm32/src/rng.rs b/embassy-stm32/src/rng.rs index 5d61d036..c4b77d01 100644 --- a/embassy-stm32/src/rng.rs +++ b/embassy-stm32/src/rng.rs @@ -3,11 +3,12 @@ use core::future::poll_fn; use core::task::Poll; +use embassy_hal_internal::interrupt::InterruptExt; use embassy_hal_internal::{into_ref, PeripheralRef}; use embassy_sync::waitqueue::AtomicWaker; use rand_core::{CryptoRng, RngCore}; -use crate::{pac, peripherals, Peripheral}; +use crate::{interrupt, pac, peripherals, Peripheral}; pub(crate) static RNG_WAKER: AtomicWaker = AtomicWaker::new(); @@ -28,31 +29,31 @@ impl<'d, T: Instance> Rng<'d, T> { into_ref!(inner); let mut random = Self { _inner: inner }; random.reset(); + unsafe { + interrupt::RNG.enable(); + } random } #[cfg(rng_v1)] pub fn reset(&mut self) { - // rng_v2 locks up on seed error, needs reset - #[cfg(rng_v2)] - if T::regs().sr().read().seis() { - T::reset(); - } - T::regs().cr().modify(|reg| { - reg.set_rngen(true); - reg.set_ie(true); + T::regs().cr().write(|reg| { + reg.set_rngen(false); }); T::regs().sr().modify(|reg| { reg.set_seis(false); reg.set_ceis(false); }); + T::regs().cr().modify(|reg| { + reg.set_rngen(true); + }); // Reference manual says to discard the first. let _ = self.next_u32(); } #[cfg(not(rng_v1))] pub fn reset(&mut self) { - T::regs().cr().modify(|reg| { + T::regs().cr().write(|reg| { reg.set_rngen(false); reg.set_condrst(true); // set RNG config "A" according to reference manual @@ -76,42 +77,68 @@ impl<'d, T: Instance> Rng<'d, T> { T::regs().cr().modify(|reg| { reg.set_rngen(true); reg.set_condrst(false); - reg.set_ie(true); }); // wait for CONDRST to be reset while T::regs().cr().read().condrst() {} } + pub fn recover_seed_error(&mut self) -> () { + self.reset(); + // reset should also clear the SEIS flag + if T::regs().sr().read().seis() { + warn!("recovering from seed error failed"); + return; + } + // wait for SECS to be cleared by RNG + while T::regs().sr().read().secs() {} + } + pub async fn async_fill_bytes(&mut self, dest: &mut [u8]) -> Result<(), Error> { - T::regs().cr().modify(|reg| { - reg.set_rngen(true); - }); - for chunk in dest.chunks_mut(4) { - poll_fn(|cx| { - RNG_WAKER.register(cx.waker()); - T::regs().cr().modify(|reg| { - reg.set_ie(true); - }); - - let bits = T::regs().sr().read(); - - if bits.drdy() { - Poll::Ready(Ok(())) - } else if bits.seis() { - self.reset(); - Poll::Ready(Err(Error::SeedError)) - } else if bits.ceis() { - self.reset(); - Poll::Ready(Err(Error::ClockError)) - } else { - Poll::Pending + let bits = T::regs().sr().read(); + if bits.seis() { + // in case of noise-source or seed error we try to recover here + // but we must not use the data in DR and we return an error + // to leave retry-logic to the application + self.recover_seed_error(); + return Err(Error::SeedError); + } else if bits.ceis() { + // clock error detected, DR could still be used but keep it safe, + // clear the error and abort + T::regs().sr().modify(|sr| sr.set_ceis(false)); + return Err(Error::ClockError); + } else if bits.drdy() { + // DR can be read up to four times until the output buffer is empty + // DRDY is cleared automatically when that happens + let random_word = T::regs().dr().read(); + // reference manual: always check if DR is zero + if random_word == 0 { + return Err(Error::SeedError); } - }) - .await?; - let random_bytes = T::regs().dr().read().to_be_bytes(); - for (dest, src) in chunk.iter_mut().zip(random_bytes.iter()) { - *dest = *src + // write bytes to chunk + for (dest, src) in chunk.iter_mut().zip(random_word.to_be_bytes().iter()) { + *dest = *src + } + } else { + // wait for interrupt + poll_fn(|cx| { + // quick check to avoid registration if already done. + let bits = T::regs().sr().read(); + if bits.drdy() || bits.seis() || bits.ceis() { + return Poll::Ready(()); + } + RNG_WAKER.register(cx.waker()); + T::regs().cr().modify(|reg| reg.set_ie(true)); + // Need to check condition **after** `register` to avoid a race + // condition that would result in lost notifications. + let bits = T::regs().sr().read(); + if bits.drdy() || bits.seis() || bits.ceis() { + Poll::Ready(()) + } else { + Poll::Pending + } + }) + .await; } } @@ -186,7 +213,7 @@ macro_rules! irq { unsafe fn $irq() { let bits = $crate::pac::RNG.sr().read(); if bits.drdy() || bits.seis() || bits.ceis() { - $crate::pac::RNG.cr().write(|reg| reg.set_ie(false)); + $crate::pac::RNG.cr().modify(|reg| reg.set_ie(false)); $crate::rng::RNG_WAKER.wake(); } } From e8d3e865912c304e4c495295360955707fffa07c Mon Sep 17 00:00:00 2001 From: Scott Mabin Date: Sun, 30 Jul 2023 21:22:48 +0100 Subject: [PATCH 66/68] stm32f2: Avoid resetting rtc backup domain Also ensure the pwr is enabled before trying to initialize. For the F2 series this is in a seperate clock control register. --- embassy-stm32/src/rtc/v2.rs | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/embassy-stm32/src/rtc/v2.rs b/embassy-stm32/src/rtc/v2.rs index a2eace6d..e3b9dfb8 100644 --- a/embassy-stm32/src/rtc/v2.rs +++ b/embassy-stm32/src/rtc/v2.rs @@ -39,9 +39,8 @@ impl<'d, T: Instance> super::Rtc<'d, T> { let rtcsel = reg.rtcsel().to_bits(); if !reg.rtcen() || rtcsel != clock_config { - #[cfg(not(any(rtc_v2l0, rtc_v2l1)))] + #[cfg(not(any(rtc_v2l0, rtc_v2l1, rtc_v2f2)))] crate::pac::RCC.bdcr().modify(|w| w.set_bdrst(true)); - #[cfg(not(any(rtc_v2l0, rtc_v2l1)))] let cr = crate::pac::RCC.bdcr(); #[cfg(any(rtc_v2l0, rtc_v2l1))] @@ -201,6 +200,11 @@ impl sealed::Instance for crate::peripherals::RTC { // read to allow the pwr clock to enable crate::pac::PWR.cr1().read(); } + #[cfg(any(rtc_v2f2))] + { + crate::pac::RCC.apb1enr().modify(|w| w.set_pwren(true)); + crate::pac::PWR.cr().read(); + } } fn read_backup_register(rtc: &Rtc, register: usize) -> Option { From d8f02e151b50f9d5156d5dd60911ea0f27ddedf0 Mon Sep 17 00:00:00 2001 From: bofh <123368+bofh@users.noreply.github.com> Date: Mon, 31 Jul 2023 00:02:50 +0200 Subject: [PATCH 67/68] Set frequency in stm32 SPI examples --- examples/stm32f3/src/bin/spi_dma.rs | 6 +++++- examples/stm32f4/src/bin/spi.rs | 6 +++++- examples/stm32f4/src/bin/spi_dma.rs | 6 +++++- examples/stm32l0/src/bin/spi.rs | 6 +++++- examples/stm32l1/src/bin/spi.rs | 6 +++++- examples/stm32l4/src/bin/spi.rs | 6 +++++- examples/stm32l4/src/bin/spi_blocking_async.rs | 6 +++++- examples/stm32l4/src/bin/spi_dma.rs | 14 +++++--------- tests/stm32/src/bin/spi.rs | 11 ++++++----- tests/stm32/src/bin/spi_dma.rs | 11 ++++++----- 10 files changed, 52 insertions(+), 26 deletions(-) diff --git a/examples/stm32f3/src/bin/spi_dma.rs b/examples/stm32f3/src/bin/spi_dma.rs index 6a39097d..a27c1d54 100644 --- a/examples/stm32f3/src/bin/spi_dma.rs +++ b/examples/stm32f3/src/bin/spi_dma.rs @@ -8,6 +8,7 @@ use core::str::from_utf8; use defmt::*; use embassy_executor::Spawner; use embassy_stm32::spi::{Config, Spi}; +use embassy_stm32::time::Hertz; use heapless::String; use {defmt_rtt as _, panic_probe as _}; @@ -16,7 +17,10 @@ async fn main(_spawner: Spawner) { let p = embassy_stm32::init(Default::default()); info!("Hello World!"); - let mut spi = Spi::new(p.SPI1, p.PB3, p.PB5, p.PB4, p.DMA1_CH3, p.DMA1_CH2, Config::default()); + let mut spi_config = Config::default(); + spi_config.frequency = Hertz(1_000_000); + + let mut spi = Spi::new(p.SPI1, p.PB3, p.PB5, p.PB4, p.DMA1_CH3, p.DMA1_CH2, spi_config); for n in 0u32.. { let mut write: String<128> = String::new(); diff --git a/examples/stm32f4/src/bin/spi.rs b/examples/stm32f4/src/bin/spi.rs index b0c62b3f..0919e987 100644 --- a/examples/stm32f4/src/bin/spi.rs +++ b/examples/stm32f4/src/bin/spi.rs @@ -7,6 +7,7 @@ use defmt::*; use embassy_stm32::dma::NoDma; use embassy_stm32::gpio::{Level, Output, Speed}; use embassy_stm32::spi::{Config, Spi}; +use embassy_stm32::time::Hertz; use {defmt_rtt as _, panic_probe as _}; #[entry] @@ -15,7 +16,10 @@ fn main() -> ! { let p = embassy_stm32::init(Default::default()); - let mut spi = Spi::new(p.SPI3, p.PC10, p.PC12, p.PC11, NoDma, NoDma, Config::default()); + let mut spi_config = Config::default(); + spi_config.frequency = Hertz(1_000_000); + + let mut spi = Spi::new(p.SPI3, p.PC10, p.PC12, p.PC11, NoDma, NoDma, spi_config); let mut cs = Output::new(p.PE0, Level::High, Speed::VeryHigh); diff --git a/examples/stm32f4/src/bin/spi_dma.rs b/examples/stm32f4/src/bin/spi_dma.rs index 4edea93a..f291f7db 100644 --- a/examples/stm32f4/src/bin/spi_dma.rs +++ b/examples/stm32f4/src/bin/spi_dma.rs @@ -8,6 +8,7 @@ use core::str::from_utf8; use defmt::*; use embassy_executor::Spawner; use embassy_stm32::spi::{Config, Spi}; +use embassy_stm32::time::Hertz; use heapless::String; use {defmt_rtt as _, panic_probe as _}; @@ -16,7 +17,10 @@ async fn main(_spawner: Spawner) { let p = embassy_stm32::init(Default::default()); info!("Hello World!"); - let mut spi = Spi::new(p.SPI1, p.PB3, p.PB5, p.PB4, p.DMA2_CH3, p.DMA2_CH2, Config::default()); + let mut spi_config = Config::default(); + spi_config.frequency = Hertz(1_000_000); + + let mut spi = Spi::new(p.SPI1, p.PB3, p.PB5, p.PB4, p.DMA2_CH3, p.DMA2_CH2, spi_config); for n in 0u32.. { let mut write: String<128> = String::new(); diff --git a/examples/stm32l0/src/bin/spi.rs b/examples/stm32l0/src/bin/spi.rs index 24ea9a68..583e3d12 100644 --- a/examples/stm32l0/src/bin/spi.rs +++ b/examples/stm32l0/src/bin/spi.rs @@ -7,6 +7,7 @@ use embassy_executor::Spawner; use embassy_stm32::dma::NoDma; use embassy_stm32::gpio::{Level, Output, Speed}; use embassy_stm32::spi::{Config, Spi}; +use embassy_stm32::time::Hertz; use {defmt_rtt as _, panic_probe as _}; #[embassy_executor::main] @@ -14,7 +15,10 @@ async fn main(_spawner: Spawner) { let p = embassy_stm32::init(Default::default()); info!("Hello World, folks!"); - let mut spi = Spi::new(p.SPI1, p.PB3, p.PA7, p.PA6, NoDma, NoDma, Config::default()); + let mut spi_config = Config::default(); + spi_config.frequency = Hertz(1_000_000); + + let mut spi = Spi::new(p.SPI1, p.PB3, p.PA7, p.PA6, NoDma, NoDma, spi_config); let mut cs = Output::new(p.PA15, Level::High, Speed::VeryHigh); diff --git a/examples/stm32l1/src/bin/spi.rs b/examples/stm32l1/src/bin/spi.rs index 53523140..905b4d75 100644 --- a/examples/stm32l1/src/bin/spi.rs +++ b/examples/stm32l1/src/bin/spi.rs @@ -7,6 +7,7 @@ use embassy_executor::Spawner; use embassy_stm32::dma::NoDma; use embassy_stm32::gpio::{Level, Output, Speed}; use embassy_stm32::spi::{Config, Spi}; +use embassy_stm32::time::Hertz; use {defmt_rtt as _, panic_probe as _}; #[embassy_executor::main] @@ -14,7 +15,10 @@ async fn main(_spawner: Spawner) { let p = embassy_stm32::init(Default::default()); info!("Hello World, folks!"); - let mut spi = Spi::new(p.SPI1, p.PA5, p.PA7, p.PA6, NoDma, NoDma, Config::default()); + let mut spi_config = Config::default(); + spi_config.frequency = Hertz(1_000_000); + + let mut spi = Spi::new(p.SPI1, p.PA5, p.PA7, p.PA6, NoDma, NoDma, spi_config); let mut cs = Output::new(p.PA4, Level::High, Speed::VeryHigh); diff --git a/examples/stm32l4/src/bin/spi.rs b/examples/stm32l4/src/bin/spi.rs index a3097fb4..54cf68f7 100644 --- a/examples/stm32l4/src/bin/spi.rs +++ b/examples/stm32l4/src/bin/spi.rs @@ -6,6 +6,7 @@ use defmt::*; use embassy_stm32::dma::NoDma; use embassy_stm32::gpio::{Level, Output, Speed}; use embassy_stm32::spi::{Config, Spi}; +use embassy_stm32::time::Hertz; use {defmt_rtt as _, panic_probe as _}; #[cortex_m_rt::entry] @@ -14,7 +15,10 @@ fn main() -> ! { let p = embassy_stm32::init(Default::default()); - let mut spi = Spi::new(p.SPI3, p.PC10, p.PC12, p.PC11, NoDma, NoDma, Config::default()); + let mut spi_config = Config::default(); + spi_config.frequency = Hertz(1_000_000); + + let mut spi = Spi::new(p.SPI3, p.PC10, p.PC12, p.PC11, NoDma, NoDma, spi_config); let mut cs = Output::new(p.PE0, Level::High, Speed::VeryHigh); diff --git a/examples/stm32l4/src/bin/spi_blocking_async.rs b/examples/stm32l4/src/bin/spi_blocking_async.rs index 45af8a9f..f1b80087 100644 --- a/examples/stm32l4/src/bin/spi_blocking_async.rs +++ b/examples/stm32l4/src/bin/spi_blocking_async.rs @@ -8,6 +8,7 @@ use embassy_executor::Spawner; use embassy_stm32::dma::NoDma; use embassy_stm32::gpio::{Input, Level, Output, Pull, Speed}; use embassy_stm32::spi::{Config, Spi}; +use embassy_stm32::time::Hertz; use embedded_hal_async::spi::SpiBus; use {defmt_rtt as _, panic_probe as _}; @@ -16,7 +17,10 @@ async fn main(_spawner: Spawner) { let p = embassy_stm32::init(Default::default()); info!("Hello World!"); - let spi = Spi::new(p.SPI3, p.PC10, p.PC12, p.PC11, NoDma, NoDma, Config::default()); + let mut spi_config = Config::default(); + spi_config.frequency = Hertz(1_000_000); + + let spi = Spi::new(p.SPI3, p.PC10, p.PC12, p.PC11, NoDma, NoDma, spi_config); let mut spi = BlockingAsync::new(spi); diff --git a/examples/stm32l4/src/bin/spi_dma.rs b/examples/stm32l4/src/bin/spi_dma.rs index 01265c49..ff9b5b43 100644 --- a/examples/stm32l4/src/bin/spi_dma.rs +++ b/examples/stm32l4/src/bin/spi_dma.rs @@ -6,6 +6,7 @@ use defmt::*; use embassy_executor::Spawner; use embassy_stm32::gpio::{Input, Level, Output, Pull, Speed}; use embassy_stm32::spi::{Config, Spi}; +use embassy_stm32::time::Hertz; use {defmt_rtt as _, panic_probe as _}; #[embassy_executor::main] @@ -13,15 +14,10 @@ async fn main(_spawner: Spawner) { let p = embassy_stm32::init(Default::default()); info!("Hello World!"); - let mut spi = Spi::new( - p.SPI3, - p.PC10, - p.PC12, - p.PC11, - p.DMA1_CH1, - p.DMA1_CH2, - Config::default(), - ); + let mut spi_config = Config::default(); + spi_config.frequency = Hertz(1_000_000); + + let mut spi = Spi::new(p.SPI3, p.PC10, p.PC12, p.PC11, p.DMA1_CH1, p.DMA1_CH2, spi_config); // These are the pins for the Inventek eS-Wifi SPI Wifi Adapter. diff --git a/tests/stm32/src/bin/spi.rs b/tests/stm32/src/bin/spi.rs index 7c2425fc..e51dd5bf 100644 --- a/tests/stm32/src/bin/spi.rs +++ b/tests/stm32/src/bin/spi.rs @@ -9,6 +9,7 @@ use defmt::assert_eq; use embassy_executor::Spawner; use embassy_stm32::dma::NoDma; use embassy_stm32::spi::{self, Spi}; +use embassy_stm32::time::Hertz; #[embassy_executor::main] async fn main(_spawner: Spawner) { @@ -34,14 +35,14 @@ async fn main(_spawner: Spawner) { #[cfg(feature = "stm32c031c6")] let (spi, sck, mosi, miso) = (p.SPI1, p.PA5, p.PA7, p.PA6); + let mut spi_config = spi::Config::default(); + spi_config.frequency = Hertz(1_000_000); + let mut spi = Spi::new( - spi, - sck, // Arduino D13 + spi, sck, // Arduino D13 mosi, // Arduino D11 miso, // Arduino D12 - NoDma, - NoDma, - spi::Config::default(), + NoDma, NoDma, spi_config, ); let data: [u8; 9] = [0x00, 0xFF, 0xAA, 0x55, 0xC0, 0xFF, 0xEE, 0xC0, 0xDE]; diff --git a/tests/stm32/src/bin/spi_dma.rs b/tests/stm32/src/bin/spi_dma.rs index e70c09f7..d45cbe45 100644 --- a/tests/stm32/src/bin/spi_dma.rs +++ b/tests/stm32/src/bin/spi_dma.rs @@ -8,6 +8,7 @@ use common::*; use defmt::assert_eq; use embassy_executor::Spawner; use embassy_stm32::spi::{self, Spi}; +use embassy_stm32::time::Hertz; #[embassy_executor::main] async fn main(_spawner: Spawner) { @@ -33,14 +34,14 @@ async fn main(_spawner: Spawner) { #[cfg(feature = "stm32c031c6")] let (spi, sck, mosi, miso, tx_dma, rx_dma) = (p.SPI1, p.PA5, p.PA7, p.PA6, p.DMA1_CH1, p.DMA1_CH2); + let mut spi_config = spi::Config::default(); + spi_config.frequency = Hertz(1_000_000); + let mut spi = Spi::new( - spi, - sck, // Arduino D13 + spi, sck, // Arduino D13 mosi, // Arduino D11 miso, // Arduino D12 - tx_dma, - rx_dma, - spi::Config::default(), + tx_dma, rx_dma, spi_config, ); let data: [u8; 9] = [0x00, 0xFF, 0xAA, 0x55, 0xC0, 0xFF, 0xEE, 0xC0, 0xDE]; From 4999b045df4e5956733b0341795714a9214c12d3 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Mon, 31 Jul 2023 01:41:12 +0200 Subject: [PATCH 68/68] stm32/rng: use bind_interrupts!. --- embassy-stm32/Cargo.toml | 4 +- embassy-stm32/src/rng.rs | 86 ++++++++++-------------- examples/stm32f4/src/bin/eth.rs | 6 +- examples/stm32f4/src/bin/usb_ethernet.rs | 5 +- examples/stm32f7/src/bin/eth.rs | 6 +- examples/stm32h5/src/bin/eth.rs | 6 +- examples/stm32h5/src/bin/rng.rs | 7 +- examples/stm32h7/src/bin/eth.rs | 6 +- examples/stm32h7/src/bin/eth_client.rs | 6 +- examples/stm32h7/src/bin/rng.rs | 7 +- examples/stm32l0/src/bin/lora_lorawan.rs | 8 ++- examples/stm32l4/src/bin/rng.rs | 8 ++- examples/stm32l5/src/bin/rng.rs | 8 ++- examples/stm32l5/src/bin/usb_ethernet.rs | 5 +- examples/stm32wl/src/bin/lora_lorawan.rs | 7 +- examples/stm32wl/src/bin/random.rs | 10 ++- 16 files changed, 103 insertions(+), 82 deletions(-) diff --git a/embassy-stm32/Cargo.toml b/embassy-stm32/Cargo.toml index ba279f79..a1323e85 100644 --- a/embassy-stm32/Cargo.toml +++ b/embassy-stm32/Cargo.toml @@ -57,7 +57,7 @@ sdio-host = "0.5.0" embedded-sdmmc = { git = "https://github.com/embassy-rs/embedded-sdmmc-rs", rev = "a4f293d3a6f72158385f79c98634cb8a14d0d2fc", optional = true } critical-section = "1.1" atomic-polyfill = "1.0.1" -stm32-metapac = "13" +stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-1f8ab493e029fc601edebc6bac105a63cc9858fe" } vcell = "0.1.3" bxcan = "0.7.0" nb = "1.0.0" @@ -75,7 +75,7 @@ critical-section = { version = "1.1", features = ["std"] } [build-dependencies] proc-macro2 = "1.0.36" quote = "1.0.15" -stm32-metapac = { version = "13", default-features = false, features = ["metadata"]} +stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-1f8ab493e029fc601edebc6bac105a63cc9858fe", default-features = false, features = ["metadata"]} [features] default = ["rt"] diff --git a/embassy-stm32/src/rng.rs b/embassy-stm32/src/rng.rs index c4b77d01..2a4978ec 100644 --- a/embassy-stm32/src/rng.rs +++ b/embassy-stm32/src/rng.rs @@ -1,16 +1,17 @@ #![macro_use] use core::future::poll_fn; +use core::marker::PhantomData; use core::task::Poll; -use embassy_hal_internal::interrupt::InterruptExt; use embassy_hal_internal::{into_ref, PeripheralRef}; use embassy_sync::waitqueue::AtomicWaker; use rand_core::{CryptoRng, RngCore}; +use crate::interrupt::typelevel::Interrupt; use crate::{interrupt, pac, peripherals, Peripheral}; -pub(crate) static RNG_WAKER: AtomicWaker = AtomicWaker::new(); +static RNG_WAKER: AtomicWaker = AtomicWaker::new(); #[cfg_attr(feature = "defmt", derive(defmt::Format))] pub enum Error { @@ -18,20 +19,38 @@ pub enum Error { ClockError, } +pub struct InterruptHandler { + _phantom: PhantomData, +} + +impl interrupt::typelevel::Handler for InterruptHandler { + unsafe fn on_interrupt() { + let bits = T::regs().sr().read(); + if bits.drdy() || bits.seis() || bits.ceis() { + T::regs().cr().modify(|reg| reg.set_ie(false)); + RNG_WAKER.wake(); + } + } +} + pub struct Rng<'d, T: Instance> { _inner: PeripheralRef<'d, T>, } impl<'d, T: Instance> Rng<'d, T> { - pub fn new(inner: impl Peripheral

+ 'd) -> Self { + pub fn new( + inner: impl Peripheral

+ 'd, + _irq: impl interrupt::typelevel::Binding> + 'd, + ) -> Self { T::enable(); T::reset(); into_ref!(inner); let mut random = Self { _inner: inner }; random.reset(); - unsafe { - interrupt::RNG.enable(); - } + + T::Interrupt::unpend(); + unsafe { T::Interrupt::enable() }; + random } @@ -189,57 +208,20 @@ pub(crate) mod sealed { } } -pub trait Instance: sealed::Instance + crate::rcc::RccPeripheral {} +pub trait Instance: sealed::Instance + Peripheral

+ crate::rcc::RccPeripheral + 'static + Send { + type Interrupt: interrupt::typelevel::Interrupt; +} -foreach_peripheral!( - (rng, $inst:ident) => { - impl Instance for peripherals::$inst {} +foreach_interrupt!( + ($inst:ident, rng, RNG, GLOBAL, $irq:ident) => { + impl Instance for peripherals::$inst { + type Interrupt = crate::interrupt::typelevel::$irq; + } impl sealed::Instance for peripherals::$inst { fn regs() -> crate::pac::rng::Rng { - crate::pac::RNG + crate::pac::$inst } } }; ); - -#[cfg(feature = "rt")] -macro_rules! irq { - ($irq:ident) => { - mod rng_irq { - use crate::interrupt; - - #[interrupt] - unsafe fn $irq() { - let bits = $crate::pac::RNG.sr().read(); - if bits.drdy() || bits.seis() || bits.ceis() { - $crate::pac::RNG.cr().modify(|reg| reg.set_ie(false)); - $crate::rng::RNG_WAKER.wake(); - } - } - } - }; -} - -#[cfg(feature = "rt")] -foreach_interrupt!( - (RNG) => { - irq!(RNG); - }; - - (RNG_LPUART1) => { - irq!(RNG_LPUART1); - }; - - (AES_RNG_LPUART1) => { - irq!(AES_RNG_LPUART1); - }; - - (AES_RNG) => { - irq!(AES_RNG); - }; - - (HASH_RNG) => { - irq!(HASH_RNG); - }; -); diff --git a/examples/stm32f4/src/bin/eth.rs b/examples/stm32f4/src/bin/eth.rs index d0b16439..49601668 100644 --- a/examples/stm32f4/src/bin/eth.rs +++ b/examples/stm32f4/src/bin/eth.rs @@ -11,13 +11,15 @@ use embassy_stm32::eth::{Ethernet, PacketQueue}; use embassy_stm32::peripherals::ETH; use embassy_stm32::rng::Rng; use embassy_stm32::time::mhz; -use embassy_stm32::{bind_interrupts, eth, Config}; +use embassy_stm32::{bind_interrupts, eth, peripherals, rng, Config}; use embassy_time::{Duration, Timer}; use embedded_io::asynch::Write; use static_cell::make_static; use {defmt_rtt as _, panic_probe as _}; + bind_interrupts!(struct Irqs { ETH => eth::InterruptHandler; + HASH_RNG => rng::InterruptHandler; }); type Device = Ethernet<'static, ETH, GenericSMI>; @@ -36,7 +38,7 @@ async fn main(spawner: Spawner) -> ! { info!("Hello World!"); // Generate random seed. - let mut rng = Rng::new(p.RNG); + let mut rng = Rng::new(p.RNG, Irqs); let mut seed = [0; 8]; let _ = rng.async_fill_bytes(&mut seed).await; let seed = u64::from_le_bytes(seed); diff --git a/examples/stm32f4/src/bin/usb_ethernet.rs b/examples/stm32f4/src/bin/usb_ethernet.rs index b1f01417..740d3018 100644 --- a/examples/stm32f4/src/bin/usb_ethernet.rs +++ b/examples/stm32f4/src/bin/usb_ethernet.rs @@ -6,7 +6,7 @@ use defmt::*; use embassy_executor::Spawner; use embassy_net::tcp::TcpSocket; use embassy_net::{Stack, StackResources}; -use embassy_stm32::rng::Rng; +use embassy_stm32::rng::{self, Rng}; use embassy_stm32::time::mhz; use embassy_stm32::usb_otg::Driver; use embassy_stm32::{bind_interrupts, peripherals, usb_otg, Config}; @@ -38,6 +38,7 @@ async fn net_task(stack: &'static Stack>) -> ! { bind_interrupts!(struct Irqs { OTG_FS => usb_otg::InterruptHandler; + HASH_RNG => rng::InterruptHandler; }); #[embassy_executor::main] @@ -104,7 +105,7 @@ async fn main(spawner: Spawner) { //}); // Generate random seed - let mut rng = Rng::new(p.RNG); + let mut rng = Rng::new(p.RNG, Irqs); let mut seed = [0; 8]; unwrap!(rng.async_fill_bytes(&mut seed).await); let seed = u64::from_le_bytes(seed); diff --git a/examples/stm32f7/src/bin/eth.rs b/examples/stm32f7/src/bin/eth.rs index c6b2ba45..e5abf52b 100644 --- a/examples/stm32f7/src/bin/eth.rs +++ b/examples/stm32f7/src/bin/eth.rs @@ -11,14 +11,16 @@ use embassy_stm32::eth::{Ethernet, PacketQueue}; use embassy_stm32::peripherals::ETH; use embassy_stm32::rng::Rng; use embassy_stm32::time::mhz; -use embassy_stm32::{bind_interrupts, eth, Config}; +use embassy_stm32::{bind_interrupts, eth, peripherals, rng, Config}; use embassy_time::{Duration, Timer}; use embedded_io::asynch::Write; use rand_core::RngCore; use static_cell::make_static; use {defmt_rtt as _, panic_probe as _}; + bind_interrupts!(struct Irqs { ETH => eth::InterruptHandler; + RNG => rng::InterruptHandler; }); type Device = Ethernet<'static, ETH, GenericSMI>; @@ -37,7 +39,7 @@ async fn main(spawner: Spawner) -> ! { info!("Hello World!"); // Generate random seed. - let mut rng = Rng::new(p.RNG); + let mut rng = Rng::new(p.RNG, Irqs); let mut seed = [0; 8]; rng.fill_bytes(&mut seed); let seed = u64::from_le_bytes(seed); diff --git a/examples/stm32h5/src/bin/eth.rs b/examples/stm32h5/src/bin/eth.rs index 0bff85ed..2aa2ab62 100644 --- a/examples/stm32h5/src/bin/eth.rs +++ b/examples/stm32h5/src/bin/eth.rs @@ -12,14 +12,16 @@ use embassy_stm32::peripherals::ETH; use embassy_stm32::rcc::{AHBPrescaler, APBPrescaler, Hse, HseMode, Pll, PllSource, Sysclk, VoltageScale}; use embassy_stm32::rng::Rng; use embassy_stm32::time::Hertz; -use embassy_stm32::{bind_interrupts, eth, Config}; +use embassy_stm32::{bind_interrupts, eth, peripherals, rng, Config}; use embassy_time::{Duration, Timer}; use embedded_io::asynch::Write; use rand_core::RngCore; use static_cell::make_static; use {defmt_rtt as _, panic_probe as _}; + bind_interrupts!(struct Irqs { ETH => eth::InterruptHandler; + RNG => rng::InterruptHandler; }); type Device = Ethernet<'static, ETH, GenericSMI>; @@ -56,7 +58,7 @@ async fn main(spawner: Spawner) -> ! { info!("Hello World!"); // Generate random seed. - let mut rng = Rng::new(p.RNG); + let mut rng = Rng::new(p.RNG, Irqs); let mut seed = [0; 8]; rng.fill_bytes(&mut seed); let seed = u64::from_le_bytes(seed); diff --git a/examples/stm32h5/src/bin/rng.rs b/examples/stm32h5/src/bin/rng.rs index af9be0b6..7c8c50ec 100644 --- a/examples/stm32h5/src/bin/rng.rs +++ b/examples/stm32h5/src/bin/rng.rs @@ -5,14 +5,19 @@ use defmt::*; use embassy_executor::Spawner; use embassy_stm32::rng::Rng; +use embassy_stm32::{bind_interrupts, peripherals, rng}; use {defmt_rtt as _, panic_probe as _}; +bind_interrupts!(struct Irqs { + RNG => rng::InterruptHandler; +}); + #[embassy_executor::main] async fn main(_spawner: Spawner) { let p = embassy_stm32::init(Default::default()); info!("Hello World!"); - let mut rng = Rng::new(p.RNG); + let mut rng = Rng::new(p.RNG, Irqs); let mut buf = [0u8; 16]; unwrap!(rng.async_fill_bytes(&mut buf).await); diff --git a/examples/stm32h7/src/bin/eth.rs b/examples/stm32h7/src/bin/eth.rs index cfafcaed..c93be9f0 100644 --- a/examples/stm32h7/src/bin/eth.rs +++ b/examples/stm32h7/src/bin/eth.rs @@ -11,14 +11,16 @@ use embassy_stm32::eth::{Ethernet, PacketQueue}; use embassy_stm32::peripherals::ETH; use embassy_stm32::rng::Rng; use embassy_stm32::time::mhz; -use embassy_stm32::{bind_interrupts, eth, Config}; +use embassy_stm32::{bind_interrupts, eth, peripherals, rng, Config}; use embassy_time::{Duration, Timer}; use embedded_io::asynch::Write; use rand_core::RngCore; use static_cell::make_static; use {defmt_rtt as _, panic_probe as _}; + bind_interrupts!(struct Irqs { ETH => eth::InterruptHandler; + RNG => rng::InterruptHandler; }); type Device = Ethernet<'static, ETH, GenericSMI>; @@ -38,7 +40,7 @@ async fn main(spawner: Spawner) -> ! { info!("Hello World!"); // Generate random seed. - let mut rng = Rng::new(p.RNG); + let mut rng = Rng::new(p.RNG, Irqs); let mut seed = [0; 8]; rng.fill_bytes(&mut seed); let seed = u64::from_le_bytes(seed); diff --git a/examples/stm32h7/src/bin/eth_client.rs b/examples/stm32h7/src/bin/eth_client.rs index 4ed73757..78005e91 100644 --- a/examples/stm32h7/src/bin/eth_client.rs +++ b/examples/stm32h7/src/bin/eth_client.rs @@ -11,15 +11,17 @@ use embassy_stm32::eth::{Ethernet, PacketQueue}; use embassy_stm32::peripherals::ETH; use embassy_stm32::rng::Rng; use embassy_stm32::time::mhz; -use embassy_stm32::{bind_interrupts, eth, Config}; +use embassy_stm32::{bind_interrupts, eth, peripherals, rng, Config}; use embassy_time::{Duration, Timer}; use embedded_io::asynch::Write; use embedded_nal_async::{Ipv4Addr, SocketAddr, SocketAddrV4, TcpConnect}; use rand_core::RngCore; use static_cell::make_static; use {defmt_rtt as _, panic_probe as _}; + bind_interrupts!(struct Irqs { ETH => eth::InterruptHandler; + RNG => rng::InterruptHandler; }); type Device = Ethernet<'static, ETH, GenericSMI>; @@ -39,7 +41,7 @@ async fn main(spawner: Spawner) -> ! { info!("Hello World!"); // Generate random seed. - let mut rng = Rng::new(p.RNG); + let mut rng = Rng::new(p.RNG, Irqs); let mut seed = [0; 8]; rng.fill_bytes(&mut seed); let seed = u64::from_le_bytes(seed); diff --git a/examples/stm32h7/src/bin/rng.rs b/examples/stm32h7/src/bin/rng.rs index af9be0b6..7c8c50ec 100644 --- a/examples/stm32h7/src/bin/rng.rs +++ b/examples/stm32h7/src/bin/rng.rs @@ -5,14 +5,19 @@ use defmt::*; use embassy_executor::Spawner; use embassy_stm32::rng::Rng; +use embassy_stm32::{bind_interrupts, peripherals, rng}; use {defmt_rtt as _, panic_probe as _}; +bind_interrupts!(struct Irqs { + RNG => rng::InterruptHandler; +}); + #[embassy_executor::main] async fn main(_spawner: Spawner) { let p = embassy_stm32::init(Default::default()); info!("Hello World!"); - let mut rng = Rng::new(p.RNG); + let mut rng = Rng::new(p.RNG, Irqs); let mut buf = [0u8; 16]; unwrap!(rng.async_fill_bytes(&mut buf).await); diff --git a/examples/stm32l0/src/bin/lora_lorawan.rs b/examples/stm32l0/src/bin/lora_lorawan.rs index c397edd5..03c793b3 100644 --- a/examples/stm32l0/src/bin/lora_lorawan.rs +++ b/examples/stm32l0/src/bin/lora_lorawan.rs @@ -12,8 +12,8 @@ use embassy_lora::LoraTimer; use embassy_stm32::exti::{Channel, ExtiInput}; use embassy_stm32::gpio::{Input, Level, Output, Pin, Pull, Speed}; use embassy_stm32::rng::Rng; -use embassy_stm32::spi; use embassy_stm32::time::khz; +use embassy_stm32::{bind_interrupts, peripherals, rng, spi}; use embassy_time::Delay; use lora_phy::mod_params::*; use lora_phy::sx1276_7_8_9::SX1276_7_8_9; @@ -23,6 +23,10 @@ use lorawan_device::async_device::lora_radio::LoRaRadio; use lorawan_device::async_device::{region, Device, JoinMode}; use {defmt_rtt as _, panic_probe as _}; +bind_interrupts!(struct Irqs { + RNG_LPUART1 => rng::InterruptHandler; +}); + const LORAWAN_REGION: region::Region = region::Region::EU868; // warning: set this appropriately for the region #[embassy_executor::main] @@ -66,7 +70,7 @@ async fn main(_spawner: Spawner) { let radio = LoRaRadio::new(lora); let region: region::Configuration = region::Configuration::new(LORAWAN_REGION); - let mut device: Device<_, Crypto, _, _> = Device::new(region, radio, LoraTimer::new(), Rng::new(p.RNG)); + let mut device: Device<_, Crypto, _, _> = Device::new(region, radio, LoraTimer::new(), Rng::new(p.RNG, Irqs)); defmt::info!("Joining LoRaWAN network"); diff --git a/examples/stm32l4/src/bin/rng.rs b/examples/stm32l4/src/bin/rng.rs index c9302bb9..806e49f5 100644 --- a/examples/stm32l4/src/bin/rng.rs +++ b/examples/stm32l4/src/bin/rng.rs @@ -6,9 +6,13 @@ use defmt::*; use embassy_executor::Spawner; use embassy_stm32::rcc::{ClockSrc, PLLClkDiv, PLLMul, PLLSource, PLLSrcDiv}; use embassy_stm32::rng::Rng; -use embassy_stm32::Config; +use embassy_stm32::{bind_interrupts, peripherals, rng, Config}; use {defmt_rtt as _, panic_probe as _}; +bind_interrupts!(struct Irqs { + RNG => rng::InterruptHandler; +}); + #[embassy_executor::main] async fn main(_spawner: Spawner) { let mut config = Config::default(); @@ -24,7 +28,7 @@ async fn main(_spawner: Spawner) { info!("Hello World!"); - let mut rng = Rng::new(p.RNG); + let mut rng = Rng::new(p.RNG, Irqs); let mut buf = [0u8; 16]; unwrap!(rng.async_fill_bytes(&mut buf).await); diff --git a/examples/stm32l5/src/bin/rng.rs b/examples/stm32l5/src/bin/rng.rs index d359847e..9549d64d 100644 --- a/examples/stm32l5/src/bin/rng.rs +++ b/examples/stm32l5/src/bin/rng.rs @@ -6,9 +6,13 @@ use defmt::*; use embassy_executor::Spawner; use embassy_stm32::rcc::{ClockSrc, PLLClkDiv, PLLMul, PLLSource, PLLSrcDiv}; use embassy_stm32::rng::Rng; -use embassy_stm32::Config; +use embassy_stm32::{bind_interrupts, peripherals, rng, Config}; use {defmt_rtt as _, panic_probe as _}; +bind_interrupts!(struct Irqs { + RNG => rng::InterruptHandler; +}); + #[embassy_executor::main] async fn main(_spawner: Spawner) { let mut config = Config::default(); @@ -23,7 +27,7 @@ async fn main(_spawner: Spawner) { info!("Hello World!"); - let mut rng = Rng::new(p.RNG); + let mut rng = Rng::new(p.RNG, Irqs); let mut buf = [0u8; 16]; unwrap!(rng.async_fill_bytes(&mut buf).await); diff --git a/examples/stm32l5/src/bin/usb_ethernet.rs b/examples/stm32l5/src/bin/usb_ethernet.rs index 32eba427..5e75b21c 100644 --- a/examples/stm32l5/src/bin/usb_ethernet.rs +++ b/examples/stm32l5/src/bin/usb_ethernet.rs @@ -9,7 +9,7 @@ use embassy_net::{Stack, StackResources}; use embassy_stm32::rcc::*; use embassy_stm32::rng::Rng; use embassy_stm32::usb::Driver; -use embassy_stm32::{bind_interrupts, peripherals, usb, Config}; +use embassy_stm32::{bind_interrupts, peripherals, rng, usb, Config}; use embassy_usb::class::cdc_ncm::embassy_net::{Device, Runner, State as NetState}; use embassy_usb::class::cdc_ncm::{CdcNcmClass, State}; use embassy_usb::{Builder, UsbDevice}; @@ -24,6 +24,7 @@ const MTU: usize = 1514; bind_interrupts!(struct Irqs { USB_FS => usb::InterruptHandler; + RNG => rng::InterruptHandler; }); #[embassy_executor::task] @@ -99,7 +100,7 @@ async fn main(spawner: Spawner) { //}); // Generate random seed - let mut rng = Rng::new(p.RNG); + let mut rng = Rng::new(p.RNG, Irqs); let seed = rng.next_u64(); // Init network stack diff --git a/examples/stm32wl/src/bin/lora_lorawan.rs b/examples/stm32wl/src/bin/lora_lorawan.rs index 805d2141..2c9c9886 100644 --- a/examples/stm32wl/src/bin/lora_lorawan.rs +++ b/examples/stm32wl/src/bin/lora_lorawan.rs @@ -10,9 +10,9 @@ use embassy_executor::Spawner; use embassy_lora::iv::{InterruptHandler, Stm32wlInterfaceVariant}; use embassy_lora::LoraTimer; use embassy_stm32::gpio::{Level, Output, Pin, Speed}; -use embassy_stm32::rng::Rng; +use embassy_stm32::rng::{self, Rng}; use embassy_stm32::spi::Spi; -use embassy_stm32::{bind_interrupts, pac}; +use embassy_stm32::{bind_interrupts, pac, peripherals}; use embassy_time::Delay; use lora_phy::mod_params::*; use lora_phy::sx1261_2::SX1261_2; @@ -26,6 +26,7 @@ const LORAWAN_REGION: region::Region = region::Region::EU868; // warning: set th bind_interrupts!(struct Irqs{ SUBGHZ_RADIO => InterruptHandler; + RNG => rng::InterruptHandler; }); #[embassy_executor::main] @@ -58,7 +59,7 @@ async fn main(_spawner: Spawner) { }; let radio = LoRaRadio::new(lora); let region: region::Configuration = region::Configuration::new(LORAWAN_REGION); - let mut device: Device<_, Crypto, _, _> = Device::new(region, radio, LoraTimer::new(), Rng::new(p.RNG)); + let mut device: Device<_, Crypto, _, _> = Device::new(region, radio, LoraTimer::new(), Rng::new(p.RNG, Irqs)); defmt::info!("Joining LoRaWAN network"); diff --git a/examples/stm32wl/src/bin/random.rs b/examples/stm32wl/src/bin/random.rs index d8562fca..592e65f4 100644 --- a/examples/stm32wl/src/bin/random.rs +++ b/examples/stm32wl/src/bin/random.rs @@ -4,10 +4,14 @@ use defmt::*; use embassy_executor::Spawner; -use embassy_stm32::pac; -use embassy_stm32::rng::Rng; +use embassy_stm32::rng::{self, Rng}; +use embassy_stm32::{bind_interrupts, pac, peripherals}; use {defmt_rtt as _, panic_probe as _}; +bind_interrupts!(struct Irqs{ + RNG => rng::InterruptHandler; +}); + #[embassy_executor::main] async fn main(_spawner: Spawner) { let mut config = embassy_stm32::Config::default(); @@ -21,7 +25,7 @@ async fn main(_spawner: Spawner) { info!("Hello World!"); - let mut rng = Rng::new(p.RNG); + let mut rng = Rng::new(p.RNG, Irqs); let mut buf = [0u8; 16]; unwrap!(rng.async_fill_bytes(&mut buf).await);