From 5f366957f31d87030182f80dd2d39dc8a8496883 Mon Sep 17 00:00:00 2001 From: James Munns Date: Fri, 12 Dec 2025 20:45:15 +0100 Subject: Add SOSC support --- embassy-mcxa/src/clkout.rs | 8 +++ embassy-mcxa/src/clocks/config.rs | 23 +++++++ embassy-mcxa/src/clocks/mod.rs | 128 +++++++++++++++++++++++++++++++++++++- embassy-mcxa/src/gpio.rs | 20 +++--- embassy-mcxa/src/i2c/mod.rs | 7 ++- embassy-mcxa/src/lib.rs | 24 ++++++- examples/mcxa/src/bin/clkout.rs | 85 +++++++++++++------------ 7 files changed, 240 insertions(+), 55 deletions(-) diff --git a/embassy-mcxa/src/clkout.rs b/embassy-mcxa/src/clkout.rs index 5b21f24b0..3495eb886 100644 --- a/embassy-mcxa/src/clkout.rs +++ b/embassy-mcxa/src/clkout.rs @@ -20,6 +20,7 @@ pub struct ClockOut<'a> { } /// Selected clock source to output +#[derive(Copy, Clone)] pub enum ClockOutSel { /// 12MHz Internal Oscillator Fro12M, @@ -36,6 +37,7 @@ pub enum ClockOutSel { } /// Configuration for the ClockOut +#[derive(Copy, Clone)] pub struct Config { /// Selected Source Clock pub sel: ClockOutSel, @@ -157,6 +159,12 @@ mod sealed { fn mux(&self) { self.set_function(crate::pac::port0::pcr0::Mux::$func); self.set_pull(Pull::Disabled); + + // TODO: we may want to expose these as options to allow the slew rate + // and drive strength for clocks if they are particularly high speed. + // + // self.set_drive_strength(crate::pac::port0::pcr0::Dse::Dse1); + // self.set_slew_rate(crate::pac::port0::pcr0::Sre::Sre0); } } }; diff --git a/embassy-mcxa/src/clocks/config.rs b/embassy-mcxa/src/clocks/config.rs index 0563b8917..9f97160ff 100644 --- a/embassy-mcxa/src/clocks/config.rs +++ b/embassy-mcxa/src/clocks/config.rs @@ -119,6 +119,28 @@ pub struct ClocksConfig { pub sirc: SircConfig, /// FRO16K clock source pub fro16k: Option, + /// SOSC, clk_in clock source + pub sosc: Option, +} + +/// The mode of the external reference clock +#[derive(Copy, Clone)] +pub enum SoscMode { + /// Passive crystal oscillators + CrystalOscillator, + /// Active external reference clock + ActiveClock, +} + +// SOSC/clk_in configuration +#[derive(Copy, Clone)] +pub struct SoscConfig { + /// Mode of the external reference clock + pub mode: SoscMode, + /// Specific frequency of the external reference clock + pub frequency: u32, + /// Power state of the external reference clock + pub power: PoweredClock, } // FIRC/FRO180M @@ -199,6 +221,7 @@ impl Default for ClocksConfig { vsys_domain_active: true, vdd_core_domain_active: true, }), + sosc: None, } } } diff --git a/embassy-mcxa/src/clocks/mod.rs b/embassy-mcxa/src/clocks/mod.rs index 667d79536..ceb1e2a50 100644 --- a/embassy-mcxa/src/clocks/mod.rs +++ b/embassy-mcxa/src/clocks/mod.rs @@ -87,6 +87,7 @@ pub fn init(settings: ClocksConfig) -> Result<(), ClockError> { operator.configure_firc_clocks()?; operator.configure_sirc_clocks()?; operator.configure_fro16k_clocks()?; + operator.configure_sosc()?; // For now, just use FIRC as the main/cpu clock, which should already be // the case on reset @@ -136,6 +137,7 @@ pub fn with_clocks R>(f: F) -> Option { #[non_exhaustive] pub struct Clocks { /// The `clk_in` is a clock provided by an external oscillator + /// AKA SOSC pub clk_in: Option, // FRO180M stuff @@ -485,8 +487,20 @@ impl Clocks { } /// Ensure the `clk_in` clock is active and valid at the given power state. - pub fn ensure_clk_in_active(&self, _at_level: &PoweredClock) -> Result { - Err(ClockError::NotImplemented { clock: "clk_in" }) + pub fn ensure_clk_in_active(&self, at_level: &PoweredClock) -> Result { + let Some(clk) = self.clk_in.as_ref() else { + return Err(ClockError::BadConfig { + clock: "clk_in", + reason: "required but not active", + }); + }; + if !clk.power.meets_requirement_of(at_level) { + return Err(ClockError::BadConfig { + clock: "clk_in", + reason: "not low power active", + }); + } + Ok(clk.frequency) } /// Ensure the `clk_16k_vsys` clock is active and valid at the given power state. @@ -851,6 +865,116 @@ impl ClockOperator<'_> { Ok(()) } + + /// Configure the SOSC/clk_in oscillator + fn configure_sosc(&mut self) -> Result<(), ClockError> { + let Some(parts) = self.config.sosc.as_ref() else { + return Ok(()); + }; + + let scg0 = unsafe { pac::Scg0::steal() }; + + // TODO: Config for the LDO? For now, if we have Sosc, just enable + // using the default settings: + // LDOBYPASS: 0/not bypassed + // VOUT_SEL: 0b100: 1.1v + // LDOEN: 0/Disabled + scg0.ldocsr().modify(|_r, w| w.ldoen().enabled()); + while scg0.ldocsr().read().vout_ok().is_disabled() {} + + // TODO: something something pins? This seems to work when the pins are + // not enabled, even if GPIO hasn't been initialized at all yet. + let eref = match parts.mode { + config::SoscMode::CrystalOscillator => pac::scg0::sosccfg::Erefs::Internal, + config::SoscMode::ActiveClock => pac::scg0::sosccfg::Erefs::External, + }; + let freq = parts.frequency; + + // TODO: Fix PAC names here + // + // #[doc = "0: Frequency range select of 8-16 MHz."] + // Freq16to20mhz = 0, + // #[doc = "1: Frequency range select of 16-25 MHz."] + // LowFreq = 1, + // #[doc = "2: Frequency range select of 25-40 MHz."] + // MediumFreq = 2, + // #[doc = "3: Frequency range select of 40-50 MHz."] + // HighFreq = 3, + let range = match freq { + 0..8_000_000 => { + return Err(ClockError::BadConfig { + clock: "clk_in", + reason: "freq too low", + }); + } + 8_000_000..16_000_000 => pac::scg0::sosccfg::Range::Freq16to20mhz, + 16_000_000..25_000_000 => pac::scg0::sosccfg::Range::LowFreq, + 25_000_000..40_000_000 => pac::scg0::sosccfg::Range::MediumFreq, + 40_000_000..50_000_001 => pac::scg0::sosccfg::Range::HighFreq, + 50_000_001.. => { + return Err(ClockError::BadConfig { + clock: "clk_in", + reason: "freq too high", + }); + } + }; + + // Set source/erefs and range + scg0.sosccfg().modify(|_r, w| { + w.erefs().variant(eref); + w.range().variant(range); + w + }); + + // Disable lock + scg0.sosccsr().modify(|_r, w| w.lk().clear_bit()); + + // TODO: We could enable the SOSC clock monitor. There are some things to + // figure out first: + // + // * This requires SIRC to be enabled, not sure which branch. Maybe fro12m_root? + // * If SOSC needs to work in deep sleep, AND the monitor is enabled: + // * SIRC also need needs to be low power + // * We need to decide if we need an interrupt or a reset if the monitor trips + + // Apply remaining config + scg0.sosccsr().modify(|_r, w| { + // For now, just disable the monitor. See above. + w.sosccm().disabled(); + + // Set deep sleep mode + match parts.power { + PoweredClock::NormalEnabledDeepSleepDisabled => { + w.soscsten().clear_bit(); + } + PoweredClock::AlwaysEnabled => { + w.soscsten().set_bit(); + } + } + + // Enable SOSC + w.soscen().enabled() + }); + + // Wait for SOSC to be valid, check for errors + while !scg0.sosccsr().read().soscvld().bit_is_set() {} + if scg0.sosccsr().read().soscerr().is_enabled_and_error() { + return Err(ClockError::BadConfig { + clock: "clk_in", + reason: "soscerr is set", + }); + } + + // Re-lock the sosc + scg0.sosccsr().modify(|_r, w| w.lk().set_bit()); + + self.clocks.clk_in = Some(Clock { + frequency: freq, + power: parts.power, + }); + + Ok(()) + } } // diff --git a/embassy-mcxa/src/gpio.rs b/embassy-mcxa/src/gpio.rs index 65f8df985..29d66656d 100644 --- a/embassy-mcxa/src/gpio.rs +++ b/embassy-mcxa/src/gpio.rs @@ -81,7 +81,7 @@ fn GPIO4() { irq_handler(4, crate::pac::Gpio4::ptr()); } -pub(crate) unsafe fn init() { +pub(crate) unsafe fn interrupt_init() { use embassy_hal_internal::interrupt::InterruptExt; crate::pac::interrupt::GPIO0.enable(); @@ -320,8 +320,12 @@ impl GpioPin for AnyPin {} macro_rules! impl_pin { ($peri:ident, $port:expr, $pin:expr, $block:ident) => { + impl_pin!(crate::peripherals, $peri, $port, $pin, $block); + }; + + ($perip:path, $peri:ident, $port:expr, $pin:expr, $block:ident) => { paste! { - impl SealedPin for crate::peripherals::$peri { + impl SealedPin for $perip::$peri { fn pin_port(&self) -> usize { $port * 32 + $pin } @@ -372,15 +376,15 @@ macro_rules! impl_pin { } } - impl GpioPin for crate::peripherals::$peri {} + impl GpioPin for $perip::$peri {} - impl From for AnyPin { - fn from(value: crate::peripherals::$peri) -> Self { + impl From<$perip::$peri> for AnyPin { + fn from(value: $perip::$peri) -> Self { value.degrade() } } - impl crate::peripherals::$peri { + impl $perip::$peri { /// Convenience helper to obtain a type-erased handle to this pin. pub fn degrade(&self) -> AnyPin { AnyPin::new(self.port(), self.pin(), self.gpio(), self.port_reg(), self.pcr_reg()) @@ -453,8 +457,8 @@ impl_pin!(P1_26, 1, 26, Gpio1); impl_pin!(P1_27, 1, 27, Gpio1); impl_pin!(P1_28, 1, 28, Gpio1); impl_pin!(P1_29, 1, 29, Gpio1); -impl_pin!(P1_30, 1, 30, Gpio1); -impl_pin!(P1_31, 1, 31, Gpio1); +impl_pin!(crate::internal_peripherals, P1_30, 1, 30, Gpio1); +impl_pin!(crate::internal_peripherals, P1_31, 1, 31, Gpio1); impl_pin!(P2_0, 2, 0, Gpio2); impl_pin!(P2_1, 2, 1, Gpio2); diff --git a/embassy-mcxa/src/i2c/mod.rs b/embassy-mcxa/src/i2c/mod.rs index 9a014224a..55c933f71 100644 --- a/embassy-mcxa/src/i2c/mod.rs +++ b/embassy-mcxa/src/i2c/mod.rs @@ -180,8 +180,11 @@ impl_pin!(P1_12, LPI2C1, Mux2, SdaPin); impl_pin!(P1_13, LPI2C1, Mux2, SclPin); impl_pin!(P1_14, LPI2C1, Mux2, SclPin); impl_pin!(P1_15, LPI2C1, Mux2, SdaPin); -impl_pin!(P1_30, LPI2C0, Mux3, SdaPin); -impl_pin!(P1_31, LPI2C0, Mux3, SclPin); +// NOTE: P1_30 and P1_31 are typically used for the external oscillator +// For now, we just don't give users these pins. +// +// impl_pin!(P1_30, LPI2C0, Mux3, SdaPin); +// impl_pin!(P1_31, LPI2C0, Mux3, SclPin); impl_pin!(P3_27, LPI2C3, Mux2, SclPin); impl_pin!(P3_28, LPI2C3, Mux2, SdaPin); // impl_pin!(P3_29, LPI2C3, Mux2, HreqPin); What is this HREQ pin? diff --git a/embassy-mcxa/src/lib.rs b/embassy-mcxa/src/lib.rs index 22bc40e35..8d39728a7 100644 --- a/embassy-mcxa/src/lib.rs +++ b/embassy-mcxa/src/lib.rs @@ -174,8 +174,18 @@ embassy_hal_internal::peripherals!( P1_27, P1_28, P1_29, - P1_30, - P1_31, + // TODO: These pins are optionally used as the clock sources for SOSC. + // Ideally, we'd want to have a custom version of the `peripheral!` macro + // that presented these as `Option>` instead of `Peri<'_, P1_30>` + // when the user DOES enable the external SOSC. For now, I'm guessing MOST designs + // will have an external clock sitting on these pins anyway, so we just notch them + // out from the `Peripherals` struct given to users. + // + // If you find this and want your extra two pins to be available: please open an + // embassy issue to discuss how we could do this. + // + // P1_30, + // P1_31, P2_0, P2_1, @@ -336,6 +346,14 @@ embassy_hal_internal::peripherals!( WWDT0, ); +// See commented out items above to understand why we create the instances +// here but don't give them to the user. +pub(crate) mod internal_peripherals { + embassy_hal_internal::peripherals_definition!(P1_30, P1_31,); + + pub(crate) use peripherals::*; +} + // Use cortex-m-rt's #[interrupt] attribute directly; PAC does not re-export it. // Re-export interrupt traits and types @@ -368,7 +386,7 @@ pub fn init(cfg: crate::config::Config) -> Peripherals { crate::clocks::init(cfg.clock_cfg).unwrap(); unsafe { - crate::gpio::init(); + crate::gpio::interrupt_init(); } // Initialize DMA controller (clock, reset, configuration) diff --git a/examples/mcxa/src/bin/clkout.rs b/examples/mcxa/src/bin/clkout.rs index 1e52912d3..ba7c8185e 100644 --- a/examples/mcxa/src/bin/clkout.rs +++ b/examples/mcxa/src/bin/clkout.rs @@ -4,6 +4,7 @@ use embassy_executor::Spawner; use embassy_mcxa::clkout::{ClockOut, ClockOutSel, Config, Div4}; use embassy_mcxa::clocks::PoweredClock; +use embassy_mcxa::clocks::config::{SoscConfig, SoscMode, SoscRange}; use embassy_mcxa::gpio::{DriveStrength, Level, Output, SlewRate}; use embassy_time::Timer; use {defmt_rtt as _, embassy_mcxa as hal, panic_probe as _}; @@ -11,58 +12,62 @@ use {defmt_rtt as _, embassy_mcxa as hal, panic_probe as _}; /// Demonstrate CLKOUT, using Pin P4.2 #[embassy_executor::main] async fn main(_spawner: Spawner) { - let p = hal::init(hal::config::Config::default()); + let mut cfg = hal::config::Config::default(); + cfg.clock_cfg.sosc = Some(SoscConfig { + mode: SoscMode::CrystalOscillator, + frequency: 8_000_000, + range: SoscRange::Mhz8To16, + power: PoweredClock::NormalEnabledDeepSleepDisabled, + }); + + let p = hal::init(cfg); + let mut pin = p.P4_2; let mut clkout = p.CLKOUT; - loop { - defmt::info!("Set Low..."); - let mut output = Output::new(pin.reborrow(), Level::Low, DriveStrength::Normal, SlewRate::Slow); - Timer::after_millis(500).await; + const K16_CONFIG: Config = Config { + sel: ClockOutSel::Clk16K, + div: Div4::no_div(), + level: PoweredClock::NormalEnabledDeepSleepDisabled, + }; + const M4_CONFIG: Config = Config { + sel: ClockOutSel::Fro12M, + div: const { Div4::from_divisor(3).unwrap() }, + level: PoweredClock::NormalEnabledDeepSleepDisabled, + }; + const K512_CONFIG: Config = Config { + sel: ClockOutSel::ClkIn, + div: const { Div4::from_divisor(16).unwrap() }, + level: PoweredClock::NormalEnabledDeepSleepDisabled, + }; + let configs = [ + ("16K -> /1 = 16K", K16_CONFIG), + ("12M -> /3 = 4M", M4_CONFIG), + ("8M -> /16 = 512K", K512_CONFIG), + ]; + + loop { defmt::info!("Set High..."); - output.set_high(); - Timer::after_millis(400).await; + let mut output = Output::new(pin.reborrow(), Level::High, DriveStrength::Normal, SlewRate::Slow); + Timer::after_millis(250).await; defmt::info!("Set Low..."); output.set_low(); - Timer::after_millis(500).await; + Timer::after_millis(750).await; - defmt::info!("16k..."); - // Run Clock Out with the 16K clock - let _clock_out = ClockOut::new( - clkout.reborrow(), - pin.reborrow(), - Config { - sel: ClockOutSel::Clk16K, - div: Div4::no_div(), - level: PoweredClock::NormalEnabledDeepSleepDisabled, - }, - ) - .unwrap(); + for (name, conf) in configs.iter() { + defmt::info!("Running {=str}", name); - Timer::after_millis(3000).await; - - defmt::info!("Set Low..."); - drop(_clock_out); + let _clock_out = ClockOut::new(clkout.reborrow(), pin.reborrow(), *conf).unwrap(); - let _output = Output::new(pin.reborrow(), Level::Low, DriveStrength::Normal, SlewRate::Slow); - Timer::after_millis(500).await; + Timer::after_millis(3000).await; - // Run Clock Out with the 12M clock, divided by 3 - defmt::info!("4M..."); - let _clock_out = ClockOut::new( - clkout.reborrow(), - pin.reborrow(), - Config { - sel: ClockOutSel::Fro12M, - div: const { Div4::from_divisor(3).unwrap() }, - level: PoweredClock::NormalEnabledDeepSleepDisabled, - }, - ) - .unwrap(); + defmt::info!("Set Low..."); + drop(_clock_out); - // Let it run for 3 seconds... - Timer::after_millis(3000).await; + let _output = Output::new(pin.reborrow(), Level::Low, DriveStrength::Normal, SlewRate::Slow); + Timer::after_millis(500).await; + } } } -- cgit From d6c65cd0e4b651b1b07e1583562dfccfd5db22b1 Mon Sep 17 00:00:00 2001 From: James Munns Date: Mon, 15 Dec 2025 14:33:47 +0100 Subject: Update example to not explicitly configure range --- examples/mcxa/src/bin/clkout.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/examples/mcxa/src/bin/clkout.rs b/examples/mcxa/src/bin/clkout.rs index ba7c8185e..e6e6a2d3d 100644 --- a/examples/mcxa/src/bin/clkout.rs +++ b/examples/mcxa/src/bin/clkout.rs @@ -4,7 +4,7 @@ use embassy_executor::Spawner; use embassy_mcxa::clkout::{ClockOut, ClockOutSel, Config, Div4}; use embassy_mcxa::clocks::PoweredClock; -use embassy_mcxa::clocks::config::{SoscConfig, SoscMode, SoscRange}; +use embassy_mcxa::clocks::config::{SoscConfig, SoscMode}; use embassy_mcxa::gpio::{DriveStrength, Level, Output, SlewRate}; use embassy_time::Timer; use {defmt_rtt as _, embassy_mcxa as hal, panic_probe as _}; @@ -16,7 +16,6 @@ async fn main(_spawner: Spawner) { cfg.clock_cfg.sosc = Some(SoscConfig { mode: SoscMode::CrystalOscillator, frequency: 8_000_000, - range: SoscRange::Mhz8To16, power: PoweredClock::NormalEnabledDeepSleepDisabled, }); -- cgit From 14d847e1b26ec35b91af50c573af32551437456c Mon Sep 17 00:00:00 2001 From: Lucas Kent Date: Wed, 17 Dec 2025 17:41:53 +1100 Subject: Fix rp webusb example on windows --- embassy-usb/src/msos.rs | 4 ---- examples/rp/src/bin/usb_webusb.rs | 11 ++++++++++- 2 files changed, 10 insertions(+), 5 deletions(-) diff --git a/embassy-usb/src/msos.rs b/embassy-usb/src/msos.rs index 66689871e..6d7f87061 100644 --- a/embassy-usb/src/msos.rs +++ b/embassy-usb/src/msos.rs @@ -110,10 +110,6 @@ impl<'d> MsOsDescriptorWriter<'d> { !self.is_empty(), "device features may only be added after the header is written" ); - assert!( - self.config_mark.is_none(), - "device features must be added before the first configuration subset" - ); self.write(desc); } diff --git a/examples/rp/src/bin/usb_webusb.rs b/examples/rp/src/bin/usb_webusb.rs index 5cecb92f0..edc9a0c52 100644 --- a/examples/rp/src/bin/usb_webusb.rs +++ b/examples/rp/src/bin/usb_webusb.rs @@ -26,6 +26,7 @@ use embassy_rp::usb::{Driver as UsbDriver, InterruptHandler}; use embassy_usb::class::web_usb::{Config as WebUsbConfig, State, Url, WebUsb}; use embassy_usb::driver::{Driver, Endpoint, EndpointIn, EndpointOut}; use embassy_usb::msos::{self, windows_version}; +use embassy_usb::types::InterfaceNumber; use embassy_usb::{Builder, Config}; use {defmt_rtt as _, panic_probe as _}; @@ -56,7 +57,7 @@ async fn main(_spawner: Spawner) { let mut config_descriptor = [0; 256]; let mut bos_descriptor = [0; 256]; let mut control_buf = [0; 64]; - let mut msos_descriptor = [0; 256]; + let mut msos_descriptor = [0; 512]; let webusb_config = WebUsbConfig { max_packet_size: 64, @@ -83,6 +84,14 @@ async fn main(_spawner: Spawner) { // In principle you might want to call msos_feature() just on a specific function, // if your device also has other functions that still use standard class drivers. builder.msos_descriptor(windows_version::WIN8_1, 0); + builder.msos_writer().configuration(0); + builder.msos_writer().function(InterfaceNumber(0)); + builder.msos_feature(msos::CompatibleIdFeatureDescriptor::new("WINUSB", "")); + builder.msos_feature(msos::RegistryPropertyFeatureDescriptor::new( + "DeviceInterfaceGUIDs", + msos::PropertyData::RegMultiSz(DEVICE_INTERFACE_GUIDS), + )); + builder.msos_writer().function(InterfaceNumber(1)); builder.msos_feature(msos::CompatibleIdFeatureDescriptor::new("WINUSB", "")); builder.msos_feature(msos::RegistryPropertyFeatureDescriptor::new( "DeviceInterfaceGUIDs", -- cgit From d113772136548e2bb50cecf1749f73bef72a0fe9 Mon Sep 17 00:00:00 2001 From: xoviat Date: Thu, 18 Dec 2025 07:00:59 -0600 Subject: stm32: cleanup low-power features --- embassy-stm32/Cargo.toml | 4 ++-- embassy-stm32/src/lib.rs | 3 +++ embassy-stm32/src/low_power.rs | 29 ++++++++++++++-------------- examples/stm32wb/Cargo.toml | 4 ++-- examples/stm32wb/src/bin/blinky.rs | 2 +- examples/stm32wb/src/bin/button_exti.rs | 2 +- examples/stm32wb/src/bin/eddystone_beacon.rs | 2 +- examples/stm32wb/src/bin/gatt_server.rs | 2 +- examples/stm32wb/src/bin/mac_ffd.rs | 2 +- examples/stm32wb/src/bin/mac_ffd_net.rs | 2 +- examples/stm32wb/src/bin/mac_rfd.rs | 2 +- examples/stm32wb/src/bin/tl_mbox.rs | 2 +- examples/stm32wb/src/bin/tl_mbox_ble.rs | 2 +- examples/stm32wb/src/bin/tl_mbox_mac.rs | 2 +- 14 files changed, 32 insertions(+), 28 deletions(-) diff --git a/embassy-stm32/Cargo.toml b/embassy-stm32/Cargo.toml index e96933b78..880df5f33 100644 --- a/embassy-stm32/Cargo.toml +++ b/embassy-stm32/Cargo.toml @@ -242,8 +242,8 @@ chrono = ["dep:chrono"] exti = [] low-power = [ "dep:embassy-executor", "time" ] -low-power-pender = [ ] -low-power-debug-with-sleep = [] +low-power-pender = [ "low-power" ] +low-power-debug-with-sleep = [ "low-power" ] ## Automatically generate `memory.x` file based on the memory map from [`stm32-metapac`](https://docs.rs/stm32-metapac/) memory-x = [] diff --git a/embassy-stm32/src/lib.rs b/embassy-stm32/src/lib.rs index 2f783bf64..a0b2f045c 100644 --- a/embassy-stm32/src/lib.rs +++ b/embassy-stm32/src/lib.rs @@ -138,6 +138,9 @@ pub mod wdg; #[cfg(xspi)] pub mod xspi; +#[cfg(feature = "low-power")] +pub use low_power::Executor; + // This must go last, so that it sees all the impl_foo! macros defined earlier. pub(crate) mod _generated { #![allow(dead_code)] diff --git a/embassy-stm32/src/low_power.rs b/embassy-stm32/src/low_power.rs index 5c10c1a5d..02116e08a 100644 --- a/embassy-stm32/src/low_power.rs +++ b/embassy-stm32/src/low_power.rs @@ -3,31 +3,31 @@ //! The STM32 line of microcontrollers support various deep-sleep modes which exploit clock-gating //! to reduce power consumption. `embassy-stm32` provides a low-power executor, [`Executor`] which //! can use knowledge of which peripherals are currently blocked upon to transparently and safely -//! enter such low-power modes (currently, only `STOP2`) when idle. +//! enter such low-power modes including `STOP1` and `STOP2` when idle. //! //! The executor determines which peripherals are active by their RCC state; consequently, -//! low-power states can only be entered if all peripherals have been `drop`'d. There are a few -//! exceptions to this rule: +//! low-power states can only be entered if peripherals which block stop have been `drop`'d and if +//! peripherals that do not block stop are busy. Peripherals which never block stop include: //! //! * `GPIO` //! * `RTC` //! +//! Other peripherals which block stop when busy include (this list may be stale): +//! +//! * `I2C` +//! * `USART` +//! //! Since entering and leaving low-power modes typically incurs a significant latency, the //! low-power executor will only attempt to enter when the next timer event is at least -//! [`time_driver::min_stop_pause`] in the future. +//! [`config.min_stop_pause`] in the future. //! -//! Currently there is no macro analogous to `embassy_executor::main` for this executor; -//! consequently one must define their entrypoint manually. Moreover, you must relinquish control -//! of the `RTC` peripheral to the executor. This will typically look like //! //! ```rust,no_run //! use embassy_executor::Spawner; -//! use embassy_stm32::low_power; -//! use embassy_stm32::rtc::{Rtc, RtcConfig}; //! use embassy_time::Duration; //! -//! #[embassy_executor::main(executor = "low_power::Executor")] -//! async fn async_main(spawner: Spawner) { +//! #[embassy_executor::main(executor = "embassy_stm32::Executor", entry = "cortex_m_rt::entry")] +//! async fn main(spawner: Spawner) { //! // initialize the platform... //! let mut config = embassy_stm32::Config::default(); //! // the default value, but can be adjusted @@ -210,7 +210,7 @@ impl Executor { w.set_c1cssf(false); }); - let has_stopped2 = { + let _has_stopped2 = { #[cfg(stm32wb)] { es.c2stopf() @@ -222,10 +222,11 @@ impl Executor { } }; - if es.c1stopf() || has_stopped2 { + #[cfg(not(stm32wb))] + if es.c1stopf() || _has_stopped2 { // when we wake from any stop mode we need to re-initialize the rcc crate::rcc::init(RCC_CONFIG.unwrap()); - if has_stopped2 { + if _has_stopped2 { // when we wake from STOP2, we need to re-initialize the time driver get_driver().init_timer(cs); // reset the refcounts for STOP2 and STOP1 (initializing the time driver will increment one of them for the timer) diff --git a/examples/stm32wb/Cargo.toml b/examples/stm32wb/Cargo.toml index 83f7cb56b..496500f75 100644 --- a/examples/stm32wb/Cargo.toml +++ b/examples/stm32wb/Cargo.toml @@ -7,10 +7,10 @@ publish = false [dependencies] # Change stm32wb55rg to your chip name in both dependencies, if necessary. -embassy-stm32 = { version = "0.4.0", path = "../../embassy-stm32", features = [ "defmt", "stm32wb55rg", "time-driver-any", "memory-x", "exti", "low-power"] } +embassy-stm32 = { version = "0.4.0", path = "../../embassy-stm32", features = [ "defmt", "stm32wb55rg", "time-driver-any", "memory-x", "exti", "low-power-pender"] } embassy-stm32-wpan = { version = "0.1.0", path = "../../embassy-stm32-wpan", features = ["defmt", "stm32wb55rg"] } embassy-sync = { version = "0.7.2", path = "../../embassy-sync", features = ["defmt"] } -embassy-executor = { version = "0.9.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "defmt"] } +embassy-executor = { version = "0.9.0", path = "../../embassy-executor", features = ["defmt"] } embassy-time = { version = "0.5.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } embassy-net = { version = "0.7.1", path = "../../embassy-net", features = ["defmt", "udp", "proto-ipv6", "medium-ieee802154", ], optional = true } diff --git a/examples/stm32wb/src/bin/blinky.rs b/examples/stm32wb/src/bin/blinky.rs index f37e8b1d8..e2737fcd5 100644 --- a/examples/stm32wb/src/bin/blinky.rs +++ b/examples/stm32wb/src/bin/blinky.rs @@ -7,7 +7,7 @@ use embassy_stm32::gpio::{Level, Output, Speed}; use embassy_time::Timer; use {defmt_rtt as _, panic_probe as _}; -#[embassy_executor::main] +#[embassy_executor::main(executor = "embassy_stm32::Executor", entry = "cortex_m_rt::entry")] async fn main(_spawner: Spawner) { let p = embassy_stm32::init(Default::default()); info!("Hello World!"); diff --git a/examples/stm32wb/src/bin/button_exti.rs b/examples/stm32wb/src/bin/button_exti.rs index 3c58eb556..37a207519 100644 --- a/examples/stm32wb/src/bin/button_exti.rs +++ b/examples/stm32wb/src/bin/button_exti.rs @@ -13,7 +13,7 @@ bind_interrupts!( EXTI4 => exti::InterruptHandler; }); -#[embassy_executor::main] +#[embassy_executor::main(executor = "embassy_stm32::Executor", entry = "cortex_m_rt::entry")] async fn main(_spawner: Spawner) { let p = embassy_stm32::init(Default::default()); info!("Hello World!"); diff --git a/examples/stm32wb/src/bin/eddystone_beacon.rs b/examples/stm32wb/src/bin/eddystone_beacon.rs index 413b1ac8f..a679e6fb1 100644 --- a/examples/stm32wb/src/bin/eddystone_beacon.rs +++ b/examples/stm32wb/src/bin/eddystone_beacon.rs @@ -26,7 +26,7 @@ bind_interrupts!(struct Irqs{ const BLE_GAP_DEVICE_NAME_LENGTH: u8 = 7; -#[embassy_executor::main] +#[embassy_executor::main(executor = "embassy_stm32::Executor", entry = "cortex_m_rt::entry")] async fn main(_spawner: Spawner) { /* How to make this work: diff --git a/examples/stm32wb/src/bin/gatt_server.rs b/examples/stm32wb/src/bin/gatt_server.rs index 3484f1844..10c7fd0ba 100644 --- a/examples/stm32wb/src/bin/gatt_server.rs +++ b/examples/stm32wb/src/bin/gatt_server.rs @@ -38,7 +38,7 @@ bind_interrupts!(struct Irqs{ const BLE_GAP_DEVICE_NAME_LENGTH: u8 = 7; -#[embassy_executor::main] +#[embassy_executor::main(executor = "embassy_stm32::Executor", entry = "cortex_m_rt::entry")] async fn main(spawner: Spawner) { /* How to make this work: diff --git a/examples/stm32wb/src/bin/mac_ffd.rs b/examples/stm32wb/src/bin/mac_ffd.rs index 4bab6ea9f..cd15968d2 100644 --- a/examples/stm32wb/src/bin/mac_ffd.rs +++ b/examples/stm32wb/src/bin/mac_ffd.rs @@ -23,7 +23,7 @@ async fn run_mm_queue(mut memory_manager: mm::MemoryManager<'static>) { memory_manager.run_queue().await; } -#[embassy_executor::main] +#[embassy_executor::main(executor = "embassy_stm32::Executor", entry = "cortex_m_rt::entry")] async fn main(spawner: Spawner) { /* How to make this work: diff --git a/examples/stm32wb/src/bin/mac_ffd_net.rs b/examples/stm32wb/src/bin/mac_ffd_net.rs index b4789e3ee..244b35243 100644 --- a/examples/stm32wb/src/bin/mac_ffd_net.rs +++ b/examples/stm32wb/src/bin/mac_ffd_net.rs @@ -41,7 +41,7 @@ async fn run_net(mut runner: embassy_net::Runner<'static, Driver<'static>>) -> ! runner.run().await } -#[embassy_executor::main] +#[embassy_executor::main(executor = "embassy_stm32::Executor", entry = "cortex_m_rt::entry")] async fn main(spawner: Spawner) { /* How to make this work: diff --git a/examples/stm32wb/src/bin/mac_rfd.rs b/examples/stm32wb/src/bin/mac_rfd.rs index dae3c5200..f3e65c66b 100644 --- a/examples/stm32wb/src/bin/mac_rfd.rs +++ b/examples/stm32wb/src/bin/mac_rfd.rs @@ -25,7 +25,7 @@ async fn run_mm_queue(mut memory_manager: mm::MemoryManager<'static>) { memory_manager.run_queue().await; } -#[embassy_executor::main] +#[embassy_executor::main(executor = "embassy_stm32::Executor", entry = "cortex_m_rt::entry")] async fn main(spawner: Spawner) { /* How to make this work: diff --git a/examples/stm32wb/src/bin/tl_mbox.rs b/examples/stm32wb/src/bin/tl_mbox.rs index 0902e28e8..adb6eff7b 100644 --- a/examples/stm32wb/src/bin/tl_mbox.rs +++ b/examples/stm32wb/src/bin/tl_mbox.rs @@ -15,7 +15,7 @@ bind_interrupts!(struct Irqs{ IPCC_C1_TX => TransmitInterruptHandler; }); -#[embassy_executor::main] +#[embassy_executor::main(executor = "embassy_stm32::Executor", entry = "cortex_m_rt::entry")] async fn main(_spawner: Spawner) { /* How to make this work: diff --git a/examples/stm32wb/src/bin/tl_mbox_ble.rs b/examples/stm32wb/src/bin/tl_mbox_ble.rs index 763dc32cd..376b808de 100644 --- a/examples/stm32wb/src/bin/tl_mbox_ble.rs +++ b/examples/stm32wb/src/bin/tl_mbox_ble.rs @@ -20,7 +20,7 @@ async fn run_mm_queue(mut memory_manager: mm::MemoryManager<'static>) { memory_manager.run_queue().await; } -#[embassy_executor::main] +#[embassy_executor::main(executor = "embassy_stm32::Executor", entry = "cortex_m_rt::entry")] async fn main(spawner: Spawner) { /* How to make this work: diff --git a/examples/stm32wb/src/bin/tl_mbox_mac.rs b/examples/stm32wb/src/bin/tl_mbox_mac.rs index 235a48241..697e061c1 100644 --- a/examples/stm32wb/src/bin/tl_mbox_mac.rs +++ b/examples/stm32wb/src/bin/tl_mbox_mac.rs @@ -20,7 +20,7 @@ async fn run_mm_queue(mut memory_manager: mm::MemoryManager<'static>) { memory_manager.run_queue().await; } -#[embassy_executor::main] +#[embassy_executor::main(executor = "embassy_stm32::Executor", entry = "cortex_m_rt::entry")] async fn main(spawner: Spawner) { /* How to make this work: -- cgit From 1c1c99c6bd5caa61f8bd9e52d0966e6166d53464 Mon Sep 17 00:00:00 2001 From: xoviat Date: Thu, 18 Dec 2025 07:01:12 -0600 Subject: ci: remove mac and ble test --- ci.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ci.sh b/ci.sh index e489982a7..1eecd158f 100755 --- a/ci.sh +++ b/ci.sh @@ -35,8 +35,8 @@ rm -rf out/tests/nrf5340-dk # disabled because these boards are not on the shelf rm -rf out/tests/mspm0g3507 -# rm out/tests/stm32wb55rg/wpan_mac -# rm out/tests/stm32wb55rg/wpan_ble +rm out/tests/stm32wb55rg/wpan_mac +rm out/tests/stm32wb55rg/wpan_ble # unstable, I think it's running out of RAM? rm out/tests/stm32f207zg/eth -- cgit