From f758c4b3910e8cb4d09c284db245e66de8cb5e5e Mon Sep 17 00:00:00 2001 From: Dion Dokter Date: Fri, 14 Jun 2024 16:55:05 +0200 Subject: Start implementing lcd --- embassy-stm32/Cargo.toml | 4 +- embassy-stm32/build.rs | 18 +++- embassy-stm32/src/lcd.rs | 176 ++++++++++++++++++++++++++++++++++++ embassy-stm32/src/lib.rs | 2 + examples/stm32u0/.cargo/config.toml | 2 +- examples/stm32u0/Cargo.toml | 2 +- examples/stm32u0/src/bin/lcd.rs | 76 ++++++++++++++++ 7 files changed, 275 insertions(+), 5 deletions(-) create mode 100644 embassy-stm32/src/lcd.rs create mode 100644 examples/stm32u0/src/bin/lcd.rs diff --git a/embassy-stm32/Cargo.toml b/embassy-stm32/Cargo.toml index d06d8af03..79d79c5a3 100644 --- a/embassy-stm32/Cargo.toml +++ b/embassy-stm32/Cargo.toml @@ -72,7 +72,7 @@ rand_core = "0.6.3" sdio-host = "0.5.0" critical-section = "1.1" #stm32-metapac = { version = "15" } -stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-cdd0f8e7cb79cbd126e2480f1b747fb01c901910" } +stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-b5202878509d8d82913bd969ba4137b7958139aa" } vcell = "0.1.3" nb = "1.0.0" @@ -97,7 +97,7 @@ proc-macro2 = "1.0.36" quote = "1.0.15" #stm32-metapac = { version = "15", default-features = false, features = ["metadata"]} -stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-cdd0f8e7cb79cbd126e2480f1b747fb01c901910", default-features = false, features = ["metadata"] } +stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-b5202878509d8d82913bd969ba4137b7958139aa", default-features = false, features = ["metadata"] } [features] default = ["rt"] diff --git a/embassy-stm32/build.rs b/embassy-stm32/build.rs index 6aedcc228..b7c00f8f7 100644 --- a/embassy-stm32/build.rs +++ b/embassy-stm32/build.rs @@ -1072,12 +1072,28 @@ fn main() { (("tsc", "G8_IO2"), quote!(crate::tsc::G8IO2Pin)), (("tsc", "G8_IO3"), quote!(crate::tsc::G8IO3Pin)), (("tsc", "G8_IO4"), quote!(crate::tsc::G8IO4Pin)), + (("lcd", "SEG"), quote!(crate::lcd::SegComPin)), + (("lcd", "COM"), quote!(crate::lcd::SegComPin)), ].into(); + let mut seen_lcd_pins = HashSet::new(); + for p in METADATA.peripherals { if let Some(regs) = &p.registers { for pin in p.pins { - let key = (regs.kind, pin.signal); + let mut key = (regs.kind, pin.signal); + + // LCD is special + if regs.kind == "lcd" { + key.1 = pin.signal.trim_end_matches(char::is_numeric); + + // Some lcd pins have multiple lcd functions + // Dedup so they don't get the trait implemented twice + if !seen_lcd_pins.insert(pin.pin) { + continue; + } + } + if let Some(tr) = signals.get(&key) { let mut peri = format_ident!("{}", p.name); diff --git a/embassy-stm32/src/lcd.rs b/embassy-stm32/src/lcd.rs new file mode 100644 index 000000000..89fca416f --- /dev/null +++ b/embassy-stm32/src/lcd.rs @@ -0,0 +1,176 @@ +//! LCD +use core::marker::PhantomData; + +use embassy_hal_internal::{into_ref, PeripheralRef}; + +use crate::gpio::{AFType, AnyPin, SealedPin}; +use crate::rcc::{self, RccPeripheral}; +use crate::{peripherals, Peripheral}; + +#[non_exhaustive] +#[derive(Debug, Default, Clone, Copy)] +pub struct Config { + pub use_voltage_output_buffer: bool, + pub use_segment_muxing: bool, + pub bias: Bias, + pub duty: Duty, + pub voltage_source: VoltageSource, + pub high_drive: bool, +} + +#[repr(u8)] +#[derive(Debug, Default, Clone, Copy)] +pub enum Bias { + #[default] + Quarter = 0b00, + Half = 0b01, + Third = 0b10, +} + +#[repr(u8)] +#[derive(Debug, Default, Clone, Copy)] +pub enum Duty { + #[default] + Static = 0b000, + Half = 0b001, + Third = 0b010, + Quarter = 0b011, + /// In this mode, `COM[7:4]` outputs are available on `SEG[51:48]`. + /// This allows reducing the number of available segments. + Eigth = 0b100, +} + +#[repr(u8)] +#[derive(Debug, Default, Clone, Copy)] +pub enum VoltageSource { + #[default] + /// Voltage stepup converter + Internal, + /// VLCD pin + External, +} + +/// LCD driver. +pub struct Lcd<'d, T: Instance> { + _peri: PhantomData<&'d mut T>, +} + +impl<'d, T: Instance> Lcd<'d, T> { + /// Initialize the lcd driver + pub fn new(_peri: impl Peripheral

+ 'd, config: Config, pins: [LcdPin<'d, T>; N]) -> Self { + rcc::enable_and_reset::(); + + for pin in pins { + pin.pin.set_as_af(pin.af_num, AFType::OutputPushPull); + } + + T::regs().cr().write(|w| { + w.set_bufen(config.use_voltage_output_buffer); + w.set_mux_seg(config.use_segment_muxing); + w.set_bias(config.bias as u8); + w.set_duty(config.duty as u8); + w.set_vsel(matches!(config.voltage_source, VoltageSource::External)); + }); + + while !T::regs().sr().read().fcrsf() { } + + T::regs().fcr().modify(|w| { + w.set_dead(0); + w.set_pon(0b111); + // w.set_hd(config.high_drive); + }); + while !T::regs().sr().read().fcrsf() { } + + for i in 0..8 { + T::regs().ram_com(i).low().write_value(0); + T::regs().ram_com(i).high().write_value(0); + } + T::regs().sr().write(|w| w.set_udr(true)); + + while !T::regs().sr().read().fcrsf() { } + + T::regs().fcr().modify(|w| { + w.set_ps(2); + w.set_div(4); + }); + while !T::regs().sr().read().fcrsf() { } + + T::regs().fcr().modify(|w| { + w.set_cc(7); + }); + while !T::regs().sr().read().fcrsf() { } + + T::regs().cr().modify(|w| w.set_lcden(true)); + + while !T::regs().sr().read().rdy() { } + + Self { _peri: PhantomData } + } + + pub fn write_frame(&mut self, data: &[u32; 16]) { + defmt::info!("{:06b}", T::regs().sr().read().0); + + // Wait until the last update is done + while T::regs().sr().read().udr() { } + + for i in 0..8 { + T::regs().ram_com(i).low().write_value(data[i * 2]); + T::regs().ram_com(i).low().write_value(data[i * 2 + 1]); + } + T::regs().sr().write(|w| w.set_udr(true)); + } +} + +impl<'d, T: Instance> Drop for Lcd<'d, T> { + fn drop(&mut self) { + rcc::disable::(); + } +} + +pub struct LcdPin<'d, T: Instance> { + pin: PeripheralRef<'d, AnyPin>, + af_num: u8, + _phantom: PhantomData, +} + +impl<'d, T: Instance, Pin: Peripheral> + 'd> From for LcdPin<'d, T> { + fn from(value: Pin) -> Self { + Self::new(value) + } +} + +impl<'d, T: Instance> LcdPin<'d, T> { + pub fn new(pin: impl Peripheral

> + 'd) -> Self { + into_ref!(pin); + + let af = pin.af_num(); + + Self { + pin: pin.map_into(), + af_num: af, + _phantom: PhantomData, + } + } +} + +trait SealedInstance: crate::rcc::SealedRccPeripheral { + fn regs() -> crate::pac::lcd::Lcd; +} + +/// DSI instance trait. +#[allow(private_bounds)] +pub trait Instance: SealedInstance + RccPeripheral + 'static {} + +pin_trait!(SegComPin, Instance); + +foreach_peripheral!( + (lcd, $inst:ident) => { + impl crate::lcd::SealedInstance for peripherals::$inst { + fn regs() -> crate::pac::lcd::Lcd { + crate::pac::$inst + } + } + + impl crate::lcd::Instance for peripherals::$inst {} + }; +); diff --git a/embassy-stm32/src/lib.rs b/embassy-stm32/src/lib.rs index 95f59360a..334a0d717 100644 --- a/embassy-stm32/src/lib.rs +++ b/embassy-stm32/src/lib.rs @@ -89,6 +89,8 @@ pub mod i2s; pub mod ipcc; #[cfg(feature = "low-power")] pub mod low_power; +#[cfg(lcd)] +pub mod lcd; #[cfg(ltdc)] pub mod ltdc; #[cfg(opamp)] diff --git a/examples/stm32u0/.cargo/config.toml b/examples/stm32u0/.cargo/config.toml index 688347084..06eed6c8f 100644 --- a/examples/stm32u0/.cargo/config.toml +++ b/examples/stm32u0/.cargo/config.toml @@ -1,6 +1,6 @@ [target.'cfg(all(target_arch = "arm", target_os = "none"))'] # replace stm32u083rctx with your chip as listed in `probe-rs chip list` -runner = "probe-rs run --chip stm32u083rctx" +runner = "probe-rs run --chip stm32u083rctx --catch-hardfault" [build] target = "thumbv6m-none-eabi" diff --git a/examples/stm32u0/Cargo.toml b/examples/stm32u0/Cargo.toml index afeb4dc34..6c310b0f6 100644 --- a/examples/stm32u0/Cargo.toml +++ b/examples/stm32u0/Cargo.toml @@ -6,7 +6,7 @@ license = "MIT OR Apache-2.0" [dependencies] # Change stm32u083rc to your chip name, if necessary. -embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = [ "defmt", "time-driver-any", "stm32u083rc", "memory-x", "unstable-pac", "exti", "chrono"] } +embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = [ "defmt", "time-driver-any", "stm32u083mc", "memory-x", "unstable-pac", "exti", "chrono"] } embassy-sync = { version = "0.6.0", path = "../../embassy-sync", features = ["defmt"] } embassy-executor = { version = "0.5.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] } embassy-time = { version = "0.3.1", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } diff --git a/examples/stm32u0/src/bin/lcd.rs b/examples/stm32u0/src/bin/lcd.rs new file mode 100644 index 000000000..8612c3dfc --- /dev/null +++ b/examples/stm32u0/src/bin/lcd.rs @@ -0,0 +1,76 @@ +#![no_std] +#![no_main] + +use defmt::*; +use embassy_executor::Spawner; +use embassy_stm32::lcd::{Bias, Config, Duty, Lcd, VoltageSource}; +use {defmt_rtt as _, panic_probe as _}; + +#[embassy_executor::main] +async fn main(_spawner: Spawner) { + let mut config = embassy_stm32::Config::default(); + // The RTC clock = the LCD clock and must be running + { + use embassy_stm32::rcc::*; + config.rcc.sys = Sysclk::PLL1_R; + config.rcc.hsi = true; + config.rcc.pll = Some(Pll { + source: PllSource::HSI, // 16 MHz + prediv: PllPreDiv::DIV1, + mul: PllMul::MUL7, // 16 * 7 = 112 MHz + divp: None, + divq: None, + divr: Some(PllRDiv::DIV2), // 112 / 2 = 56 MHz + }); + config.rcc.ls = LsConfig::default(); + } + + let p = embassy_stm32::init(config); + info!("Hello World!"); + + let mut config = Config::default(); + config.bias = Bias::Third; + config.duty = Duty::Quarter; + + let mut lcd = Lcd::new( + p.LCD, + config, + [ + p.PC4.into(), + p.PC5.into(), + p.PB1.into(), + p.PE7.into(), + p.PE8.into(), + p.PE9.into(), + p.PB11.into(), + p.PB14.into(), + p.PB15.into(), + p.PD8.into(), + p.PD9.into(), + p.PD12.into(), + p.PB9.into(), + p.PA10.into(), + p.PA9.into(), + p.PA8.into(), + p.PD13.into(), + p.PC6.into(), + p.PC8.into(), + p.PC9.into(), + p.PC10.into(), + p.PD0.into(), + p.PD1.into(), + p.PD3.into(), + p.PD4.into(), + p.PD5.into(), + p.PD6.into(), + p.PC11.into(), + ], + ); + + loop { + defmt::info!("Writing frame"); + lcd.write_frame(&[0xAAAAAAAA; 16]); + defmt::info!("Writing frame"); + lcd.write_frame(&[!0xAAAAAAAA; 16]); + } +} -- cgit From 10a1a19e9470b96664f94cb9fc3aa53ea51695a4 Mon Sep 17 00:00:00 2001 From: Dion Dokter Date: Sat, 15 Jun 2024 11:46:22 +0200 Subject: More debugging --- embassy-stm32/src/lcd.rs | 120 ++++++++++++++++++++++++++++++---------- examples/stm32u0/src/bin/lcd.rs | 2 +- 2 files changed, 92 insertions(+), 30 deletions(-) diff --git a/embassy-stm32/src/lcd.rs b/embassy-stm32/src/lcd.rs index 89fca416f..5c5f46861 100644 --- a/embassy-stm32/src/lcd.rs +++ b/embassy-stm32/src/lcd.rs @@ -5,10 +5,11 @@ use embassy_hal_internal::{into_ref, PeripheralRef}; use crate::gpio::{AFType, AnyPin, SealedPin}; use crate::rcc::{self, RccPeripheral}; +use crate::time::Hertz; use crate::{peripherals, Peripheral}; #[non_exhaustive] -#[derive(Debug, Default, Clone, Copy)] +#[derive(Debug, Clone, Copy)] pub struct Config { pub use_voltage_output_buffer: bool, pub use_segment_muxing: bool, @@ -16,6 +17,21 @@ pub struct Config { pub duty: Duty, pub voltage_source: VoltageSource, pub high_drive: bool, + pub target_fps: Hertz, +} + +impl Default for Config { + fn default() -> Self { + Self { + use_voltage_output_buffer: Default::default(), + use_segment_muxing: Default::default(), + bias: Default::default(), + duty: Default::default(), + voltage_source: Default::default(), + high_drive: Default::default(), + target_fps: Hertz(30), + } + } } #[repr(u8)] @@ -60,49 +76,91 @@ impl<'d, T: Instance> Lcd<'d, T> { pub fn new(_peri: impl Peripheral

+ 'd, config: Config, pins: [LcdPin<'d, T>; N]) -> Self { rcc::enable_and_reset::(); + // Set the pins for pin in pins { pin.pin.set_as_af(pin.af_num, AFType::OutputPushPull); + pin.pin.set_speed(crate::gpio::Speed::VeryHigh); } - T::regs().cr().write(|w| { - w.set_bufen(config.use_voltage_output_buffer); - w.set_mux_seg(config.use_segment_muxing); - w.set_bias(config.bias as u8); - w.set_duty(config.duty as u8); - w.set_vsel(matches!(config.voltage_source, VoltageSource::External)); - }); - - while !T::regs().sr().read().fcrsf() { } - - T::regs().fcr().modify(|w| { - w.set_dead(0); - w.set_pon(0b111); - // w.set_hd(config.high_drive); - }); - while !T::regs().sr().read().fcrsf() { } - + // Initialize the display ram to 0 for i in 0..8 { T::regs().ram_com(i).low().write_value(0); T::regs().ram_com(i).high().write_value(0); } - T::regs().sr().write(|w| w.set_udr(true)); - while !T::regs().sr().read().fcrsf() { } + // Calculate the clock dividers + let Some(lcd_clk) = (unsafe { rcc::get_freqs().rtc }) else { + panic!("The LCD driver needs the RTC/LCD clock to be running"); + }; + let duty_divider = match config.duty { + Duty::Static => 1, + Duty::Half => 2, + Duty::Third => 3, + Duty::Quarter => 4, + Duty::Eigth => 8, + }; + let target_clock = config.target_fps.0 * duty_divider; + let target_division = lcd_clk.0 / target_clock; + + let mut ps = 0; + let mut div = 0; + let mut best_fps_match = u32::MAX; + + for trial_div in 0..0xF { + let trial_ps = (target_division / (trial_div + 16)) + .next_power_of_two() + .trailing_zeros(); + let fps = lcd_clk.0 / ((1 << trial_ps) * (trial_div + 16)) / duty_divider; + + if fps < config.target_fps.0 { + continue; + } + + if fps < best_fps_match { + ps = trial_ps; + div = trial_div; + best_fps_match = fps; + } + } + + trace!("lcd_clk: {}, fps: {}, ps: {}, div: {}", lcd_clk, best_fps_match, ps, div); + if best_fps_match == u32::MAX || ps > 0xF { + panic!("Lcd clock error"); + } + + // Set the frame control T::regs().fcr().modify(|w| { - w.set_ps(2); - w.set_div(4); + w.0 = 0x7C5C41; + // w.set_ps(ps as u8); + // w.set_div(div as u8); + // w.set_cc(0); + // w.set_dead(0); + // w.set_pon(0); + // // w.set_hd(config.high_drive); }); - while !T::regs().sr().read().fcrsf() { } - T::regs().fcr().modify(|w| { - w.set_cc(7); + // Wait for the frame control to synchronize + while !T::regs().sr().read().fcrsf() {} + + // Set the control register values + T::regs().cr().modify(|w| { + w.0 = 0x4E; + // w.set_bufen(config.use_voltage_output_buffer); + // w.set_mux_seg(config.use_segment_muxing); + // w.set_bias(config.bias as u8); + // w.set_duty(config.duty as u8); + // w.set_vsel(matches!(config.voltage_source, VoltageSource::External)); }); - while !T::regs().sr().read().fcrsf() { } + // Enable the lcd T::regs().cr().modify(|w| w.set_lcden(true)); - while !T::regs().sr().read().rdy() { } + // Wait for the lcd to be enabled + while !T::regs().sr().read().ens() {} + + // Wait for the stepup converter to be ready + while !T::regs().sr().read().rdy() {} Self { _peri: PhantomData } } @@ -111,12 +169,16 @@ impl<'d, T: Instance> Lcd<'d, T> { defmt::info!("{:06b}", T::regs().sr().read().0); // Wait until the last update is done - while T::regs().sr().read().udr() { } + while T::regs().sr().read().udr() {} - for i in 0..8 { + for i in 0..8 { T::regs().ram_com(i).low().write_value(data[i * 2]); T::regs().ram_com(i).low().write_value(data[i * 2 + 1]); } + + // Clear the update done flag + T::regs().sr().write(|w| w.set_udd(true)); + // Set the update request flag T::regs().sr().write(|w| w.set_udr(true)); } } diff --git a/examples/stm32u0/src/bin/lcd.rs b/examples/stm32u0/src/bin/lcd.rs index 8612c3dfc..7a7c3a8a1 100644 --- a/examples/stm32u0/src/bin/lcd.rs +++ b/examples/stm32u0/src/bin/lcd.rs @@ -22,7 +22,7 @@ async fn main(_spawner: Spawner) { divq: None, divr: Some(PllRDiv::DIV2), // 112 / 2 = 56 MHz }); - config.rcc.ls = LsConfig::default(); + config.rcc.ls = LsConfig::default_lse(); } let p = embassy_stm32::init(config); -- cgit From 663732d85abbae400f2dbab2c411802a5b60e9b1 Mon Sep 17 00:00:00 2001 From: Dion Dokter Date: Sat, 15 Jun 2024 13:51:16 +0200 Subject: Things work! --- embassy-stm32/src/lcd.rs | 129 +++++++++++++++++++++++++++++++++------- examples/stm32u0/src/bin/lcd.rs | 39 +++++++----- 2 files changed, 130 insertions(+), 38 deletions(-) diff --git a/embassy-stm32/src/lcd.rs b/embassy-stm32/src/lcd.rs index 5c5f46861..15d3e8fbc 100644 --- a/embassy-stm32/src/lcd.rs +++ b/embassy-stm32/src/lcd.rs @@ -16,46 +16,61 @@ pub struct Config { pub bias: Bias, pub duty: Duty, pub voltage_source: VoltageSource, - pub high_drive: bool, pub target_fps: Hertz, + pub drive: Drive, } impl Default for Config { fn default() -> Self { Self { - use_voltage_output_buffer: Default::default(), - use_segment_muxing: Default::default(), + use_voltage_output_buffer: false, + use_segment_muxing: false, bias: Default::default(), duty: Default::default(), voltage_source: Default::default(), - high_drive: Default::default(), target_fps: Hertz(30), + drive: Drive::Medium, } } } +/// The number of voltage levels used when driving an LCD. +/// Your LCD datasheet should tell you what to use. #[repr(u8)] #[derive(Debug, Default, Clone, Copy)] pub enum Bias { + /// 1/4 bias #[default] Quarter = 0b00, + /// 1/2 bias Half = 0b01, + /// 1/3 bias Third = 0b10, } +/// The duty used by the LCD driver. +/// +/// This is essentially how many COM pins you're using. #[repr(u8)] #[derive(Debug, Default, Clone, Copy)] pub enum Duty { #[default] + /// Use a single COM pin Static = 0b000, + /// Use two COM pins Half = 0b001, + /// Use three COM pins Third = 0b010, + /// Use four COM pins Quarter = 0b011, + /// Use eight COM pins. + /// /// In this mode, `COM[7:4]` outputs are available on `SEG[51:48]`. /// This allows reducing the number of available segments. Eigth = 0b100, } +/// Whether to use the internal or external voltage source to drive the LCD #[repr(u8)] #[derive(Debug, Default, Clone, Copy)] pub enum VoltageSource { @@ -66,6 +81,40 @@ pub enum VoltageSource { External, } +/// Defines the pulse duration in terms of ck_ps pulses. +/// +/// A short pulse leads to lower power consumption, but displays with high internal resistance +/// may need a longer pulse to achieve satisfactory contrast. +/// Note that the pulse is never longer than one half prescaled LCD clock period. +/// +/// Displays with high internal resistance may need a longer drive time to achieve satisfactory contrast. +/// `PermanentHighDrive` is useful in this case if some additional power consumption can be tolerated. +/// +/// Basically, for power usage, you want this as low as possible while still being able to use the LCD +/// with a good enough contrast. +#[repr(u8)] +#[derive(Debug, Clone, Copy)] +pub enum Drive { + /// Zero clock pulse on duration + Lowest = 0x00, + /// One clock pulse on duration + VeryLow = 0x01, + /// Two clock pulse on duration + Low = 0x02, + /// Three clock pulse on duration + Medium = 0x03, + /// Four clock pulse on duration + MediumHigh = 0x04, + /// Five clock pulse on duration + High = 0x05, + /// Six clock pulse on duration + VeryHigh = 0x06, + /// Seven clock pulse on duration + Highest = 0x07, + /// Enables the highdrive bit of the hardware + PermanentHighDrive = 0x09, +} + /// LCD driver. pub struct Lcd<'d, T: Instance> { _peri: PhantomData<&'d mut T>, @@ -73,9 +122,18 @@ pub struct Lcd<'d, T: Instance> { impl<'d, T: Instance> Lcd<'d, T> { /// Initialize the lcd driver - pub fn new(_peri: impl Peripheral

+ 'd, config: Config, pins: [LcdPin<'d, T>; N]) -> Self { + pub fn new( + _peri: impl Peripheral

+ 'd, + config: Config, + vlcd_pin: impl Peripheral

> + 'd, + pins: [LcdPin<'d, T>; N], + ) -> Self { rcc::enable_and_reset::(); + into_ref!(vlcd_pin); + vlcd_pin.set_as_af(vlcd_pin.af_num(), AFType::OutputPushPull); + vlcd_pin.set_speed(crate::gpio::Speed::VeryHigh); + // Set the pins for pin in pins { pin.pin.set_as_af(pin.af_num, AFType::OutputPushPull); @@ -123,7 +181,13 @@ impl<'d, T: Instance> Lcd<'d, T> { } } - trace!("lcd_clk: {}, fps: {}, ps: {}, div: {}", lcd_clk, best_fps_match, ps, div); + trace!( + "lcd_clk: {}, fps: {}, ps: {}, div: {}", + lcd_clk, + best_fps_match, + ps, + div + ); if best_fps_match == u32::MAX || ps > 0xF { panic!("Lcd clock error"); @@ -131,13 +195,12 @@ impl<'d, T: Instance> Lcd<'d, T> { // Set the frame control T::regs().fcr().modify(|w| { - w.0 = 0x7C5C41; - // w.set_ps(ps as u8); - // w.set_div(div as u8); - // w.set_cc(0); - // w.set_dead(0); - // w.set_pon(0); - // // w.set_hd(config.high_drive); + w.set_ps(ps as u8); + w.set_div(div as u8); + w.set_cc(0b100); // Init in the middle-ish + w.set_dead(0b000); + w.set_pon(config.drive as u8 & 0x07); + w.set_hd((config.drive as u8 & !0x07) != 0); }); // Wait for the frame control to synchronize @@ -145,12 +208,11 @@ impl<'d, T: Instance> Lcd<'d, T> { // Set the control register values T::regs().cr().modify(|w| { - w.0 = 0x4E; - // w.set_bufen(config.use_voltage_output_buffer); - // w.set_mux_seg(config.use_segment_muxing); - // w.set_bias(config.bias as u8); - // w.set_duty(config.duty as u8); - // w.set_vsel(matches!(config.voltage_source, VoltageSource::External)); + w.set_bufen(config.use_voltage_output_buffer); + w.set_mux_seg(config.use_segment_muxing); + w.set_bias(config.bias as u8); + w.set_duty(config.duty as u8); + w.set_vsel(matches!(config.voltage_source, VoltageSource::External)); }); // Enable the lcd @@ -165,10 +227,28 @@ impl<'d, T: Instance> Lcd<'d, T> { Self { _peri: PhantomData } } - pub fn write_frame(&mut self, data: &[u32; 16]) { - defmt::info!("{:06b}", T::regs().sr().read().0); + /// Change the contrast by changing the voltage being used. + /// + /// This from low at 0 to high at 7. + pub fn set_contrast_control(&mut self, value: u8) { + T::regs().fcr().modify(|w| w.set_cc(value)); + } + + /// Change the contrast by introducing a deadtime to the signals + /// where the voltages are held at 0V. + /// + /// This from no dead time at 0 to high dead time at 7. + pub fn set_dead_time(&mut self, value: u8) { + T::regs().fcr().modify(|w: &mut stm32_metapac::lcd::regs::Fcr| w.set_dead(value)); + } - // Wait until the last update is done + /// Write frame data to the peripheral. + /// + /// What each bit means depends on the exact microcontroller you use, + /// which pins are connected to your LCD and also the LCD layout itself. + /// + /// This function blocks until the last update display request has been processed. + pub fn write_frame(&mut self, data: &[u32; 16]) { while T::regs().sr().read().udr() {} for i in 0..8 { @@ -224,6 +304,11 @@ trait SealedInstance: crate::rcc::SealedRccPeripheral { pub trait Instance: SealedInstance + RccPeripheral + 'static {} pin_trait!(SegComPin, Instance); +pin_trait!(VlcdPin, Instance); + +// TODO: pull into build.rs, but the metapack doesn't have this info +pin_trait_impl!(crate::lcd::VlcdPin, LCD, PC3, 11); +pin_trait_impl!(crate::lcd::VlcdPin, LCD, PB2, 11); foreach_peripheral!( (lcd, $inst:ident) => { diff --git a/examples/stm32u0/src/bin/lcd.rs b/examples/stm32u0/src/bin/lcd.rs index 7a7c3a8a1..5551dd819 100644 --- a/examples/stm32u0/src/bin/lcd.rs +++ b/examples/stm32u0/src/bin/lcd.rs @@ -3,7 +3,7 @@ use defmt::*; use embassy_executor::Spawner; -use embassy_stm32::lcd::{Bias, Config, Duty, Lcd, VoltageSource}; +use embassy_stm32::{lcd::{Bias, Config, Duty, Lcd, VoltageSource}, time::Hertz}; use {defmt_rtt as _, panic_probe as _}; #[embassy_executor::main] @@ -22,7 +22,7 @@ async fn main(_spawner: Spawner) { divq: None, divr: Some(PllRDiv::DIV2), // 112 / 2 = 56 MHz }); - config.rcc.ls = LsConfig::default_lse(); + config.rcc.ls = LsConfig::default_lsi(); } let p = embassy_stm32::init(config); @@ -31,46 +31,53 @@ async fn main(_spawner: Spawner) { let mut config = Config::default(); config.bias = Bias::Third; config.duty = Duty::Quarter; + config.target_fps = Hertz(60); let mut lcd = Lcd::new( p.LCD, config, + p.PC3, [ - p.PC4.into(), - p.PC5.into(), + p.PA8.into(), + p.PA9.into(), + p.PA10.into(), p.PB1.into(), - p.PE7.into(), - p.PE8.into(), - p.PE9.into(), + p.PB9.into(), p.PB11.into(), p.PB14.into(), p.PB15.into(), - p.PD8.into(), - p.PD9.into(), - p.PD12.into(), - p.PB9.into(), - p.PA10.into(), - p.PA9.into(), - p.PA8.into(), - p.PD13.into(), + p.PC4.into(), + p.PC5.into(), p.PC6.into(), p.PC8.into(), p.PC9.into(), p.PC10.into(), + p.PC11.into(), + p.PD8.into(), + p.PD9.into(), + p.PD12.into(), + p.PD13.into(), p.PD0.into(), p.PD1.into(), p.PD3.into(), p.PD4.into(), p.PD5.into(), p.PD6.into(), - p.PC11.into(), + p.PE7.into(), + p.PE8.into(), + p.PE9.into(), ], ); loop { defmt::info!("Writing frame"); lcd.write_frame(&[0xAAAAAAAA; 16]); + + embassy_time::Timer::after_secs(1).await; + defmt::info!("Writing frame"); lcd.write_frame(&[!0xAAAAAAAA; 16]); + + embassy_time::Timer::after_secs(1).await; } } -- cgit From 5f5f71efd09266eccb0ab7e4048994f244eed5ae Mon Sep 17 00:00:00 2001 From: matteo Date: Sun, 21 Sep 2025 17:52:14 +0200 Subject: Add support for changing hid protocol mode --- embassy-usb/CHANGELOG.md | 4 ++++ embassy-usb/Cargo.toml | 2 +- embassy-usb/src/class/hid.rs | 39 ++++++++++++++++++++++++++++++++------- 3 files changed, 37 insertions(+), 8 deletions(-) diff --git a/embassy-usb/CHANGELOG.md b/embassy-usb/CHANGELOG.md index 0a30bc24b..891388122 100644 --- a/embassy-usb/CHANGELOG.md +++ b/embassy-usb/CHANGELOG.md @@ -8,6 +8,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## Unreleased - ReleaseDate +## 0.5.2 - Unreleased + +- `hid`: Add USB HID Boot Protocol Mode support + ## 0.5.1 - 2025-08-26 ## 0.5.0 - 2025-07-16 diff --git a/embassy-usb/Cargo.toml b/embassy-usb/Cargo.toml index e309eec93..fde603df0 100644 --- a/embassy-usb/Cargo.toml +++ b/embassy-usb/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "embassy-usb" -version = "0.5.1" +version = "0.5.2" edition = "2021" license = "MIT OR Apache-2.0" description = "Async USB device stack for embedded devices in Rust." diff --git a/embassy-usb/src/class/hid.rs b/embassy-usb/src/class/hid.rs index 182e1f83f..8df23b384 100644 --- a/embassy-usb/src/class/hid.rs +++ b/embassy-usb/src/class/hid.rs @@ -8,6 +8,7 @@ use core::sync::atomic::{AtomicUsize, Ordering}; use ssmarshal::serialize; #[cfg(feature = "usbd-hid")] use usbd_hid::descriptor::AsInputReport; +use usbd_hid::hid_class::HidProtocolMode; use crate::control::{InResponse, OutResponse, Recipient, Request, RequestType}; use crate::driver::{Driver, Endpoint, EndpointError, EndpointIn, EndpointOut}; @@ -389,6 +390,23 @@ pub trait RequestHandler { OutResponse::Rejected } + /// Gets the current hid protocol. + /// + /// Returns `Report` protocol by default. + fn get_protocol(&self) -> HidProtocolMode { + HidProtocolMode::Report + } + + /// Sets the current hid protocol to `protocol`. + /// + /// Accepts only `Report` protocol by default. + fn set_protocol(&mut self, protocol: HidProtocolMode) -> OutResponse { + match protocol { + HidProtocolMode::Report => OutResponse::Accepted, + HidProtocolMode::Boot => OutResponse::Rejected, + } + } + /// Get the idle rate for `id`. /// /// If `id` is `None`, get the idle rate for all reports. Returning `None` @@ -482,11 +500,14 @@ impl<'d> Handler for Control<'d> { _ => Some(OutResponse::Rejected), }, HID_REQ_SET_PROTOCOL => { - if req.value == 1 { - Some(OutResponse::Accepted) - } else { - warn!("HID Boot Protocol is unsupported."); - Some(OutResponse::Rejected) // UNSUPPORTED: Boot Protocol + let hid_protocol = HidProtocolMode::from(req.value as u8); + match (self.request_handler.as_mut(), hid_protocol) { + (Some(request_handler), hid_protocol) => Some(request_handler.set_protocol(hid_protocol)), + (None, HidProtocolMode::Report) => Some(OutResponse::Accepted), + (None, HidProtocolMode::Boot) => { + warn!("HID Boot Protocol is unsupported."); + Some(OutResponse::Rejected) // UNSUPPORTED: Boot Protocol + } } } _ => Some(OutResponse::Rejected), @@ -539,8 +560,12 @@ impl<'d> Handler for Control<'d> { } } HID_REQ_GET_PROTOCOL => { - // UNSUPPORTED: Boot Protocol - buf[0] = 1; + if let Some(request_handler) = self.request_handler.as_mut() { + buf[0] = request_handler.get_protocol() as u8; + } else { + // Return `Report` protocol by default + buf[0] = HidProtocolMode::Report as u8; + } Some(InResponse::Accepted(&buf[0..1])) } _ => Some(InResponse::Rejected), -- cgit From 2cf7cc4c92fe0f7897513286e4bce944870e1bad Mon Sep 17 00:00:00 2001 From: matteo Date: Sun, 21 Sep 2025 18:00:32 +0200 Subject: fix compilation with usbd-hid feature off --- embassy-usb/src/class/hid.rs | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/embassy-usb/src/class/hid.rs b/embassy-usb/src/class/hid.rs index 8df23b384..f97bcdfac 100644 --- a/embassy-usb/src/class/hid.rs +++ b/embassy-usb/src/class/hid.rs @@ -8,6 +8,7 @@ use core::sync::atomic::{AtomicUsize, Ordering}; use ssmarshal::serialize; #[cfg(feature = "usbd-hid")] use usbd_hid::descriptor::AsInputReport; +#[cfg(feature = "usbd-hid")] use usbd_hid::hid_class::HidProtocolMode; use crate::control::{InResponse, OutResponse, Recipient, Request, RequestType}; @@ -32,6 +33,30 @@ const HID_REQ_SET_REPORT: u8 = 0x09; const HID_REQ_GET_PROTOCOL: u8 = 0x03; const HID_REQ_SET_PROTOCOL: u8 = 0x0b; +/// Get/Set Protocol mapping +/// See (7.2.5 and 7.2.6): +#[cfg(not(feature = "usbd-hid"))] +#[derive(Copy, Clone, Debug, PartialEq, Eq)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +#[repr(u8)] +pub enum HidProtocolMode { + /// Hid Boot Protocol Mode + Boot = 0, + /// Hid Report Protocol Mode + Report = 1, +} + +#[cfg(not(feature = "usbd-hid"))] +impl From for HidProtocolMode { + fn from(mode: u8) -> HidProtocolMode { + if mode == HidProtocolMode::Boot as u8 { + HidProtocolMode::Boot + } else { + HidProtocolMode::Report + } + } +} + /// Configuration for the HID class. pub struct Config<'d> { /// HID report descriptor. -- cgit From 1688a3c393375790b223da70d41106fd59431878 Mon Sep 17 00:00:00 2001 From: matteo Date: Sun, 21 Sep 2025 18:08:42 +0200 Subject: update comments --- embassy-usb/src/class/hid.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/embassy-usb/src/class/hid.rs b/embassy-usb/src/class/hid.rs index f97bcdfac..c8875435f 100644 --- a/embassy-usb/src/class/hid.rs +++ b/embassy-usb/src/class/hid.rs @@ -530,8 +530,8 @@ impl<'d> Handler for Control<'d> { (Some(request_handler), hid_protocol) => Some(request_handler.set_protocol(hid_protocol)), (None, HidProtocolMode::Report) => Some(OutResponse::Accepted), (None, HidProtocolMode::Boot) => { - warn!("HID Boot Protocol is unsupported."); - Some(OutResponse::Rejected) // UNSUPPORTED: Boot Protocol + info!("Received request to switch to Boot protocol mode, but it is disabled by default."); + Some(OutResponse::Rejected) } } } @@ -588,7 +588,7 @@ impl<'d> Handler for Control<'d> { if let Some(request_handler) = self.request_handler.as_mut() { buf[0] = request_handler.get_protocol() as u8; } else { - // Return `Report` protocol by default + // Return `Report` protocol mode by default buf[0] = HidProtocolMode::Report as u8; } Some(InResponse::Accepted(&buf[0..1])) -- cgit From 10397e9f3c4a224c38caa81f1f1d72c0aa96fcdf Mon Sep 17 00:00:00 2001 From: matteo Date: Sun, 21 Sep 2025 18:26:26 +0200 Subject: fix changelog --- embassy-usb/CHANGELOG.md | 4 +--- embassy-usb/Cargo.toml | 2 +- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/embassy-usb/CHANGELOG.md b/embassy-usb/CHANGELOG.md index 891388122..cfb1bf021 100644 --- a/embassy-usb/CHANGELOG.md +++ b/embassy-usb/CHANGELOG.md @@ -8,9 +8,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## Unreleased - ReleaseDate -## 0.5.2 - Unreleased - -- `hid`: Add USB HID Boot Protocol Mode support +- Add support for USB HID Boot Protocol Mode ## 0.5.1 - 2025-08-26 diff --git a/embassy-usb/Cargo.toml b/embassy-usb/Cargo.toml index fde603df0..e309eec93 100644 --- a/embassy-usb/Cargo.toml +++ b/embassy-usb/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "embassy-usb" -version = "0.5.2" +version = "0.5.1" edition = "2021" license = "MIT OR Apache-2.0" description = "Async USB device stack for embedded devices in Rust." -- cgit From d72e8d9af921bfd5ddc25a17933e16b2132386b8 Mon Sep 17 00:00:00 2001 From: matteo Date: Tue, 23 Sep 2025 20:08:11 +0200 Subject: add usb subclass boot and usb protocol mouse and keyboard --- embassy-usb/src/class/hid.rs | 95 +++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 90 insertions(+), 5 deletions(-) diff --git a/embassy-usb/src/class/hid.rs b/embassy-usb/src/class/hid.rs index c8875435f..b9830baeb 100644 --- a/embassy-usb/src/class/hid.rs +++ b/embassy-usb/src/class/hid.rs @@ -17,8 +17,13 @@ use crate::types::InterfaceNumber; use crate::{Builder, Handler}; const USB_CLASS_HID: u8 = 0x03; -const USB_SUBCLASS_NONE: u8 = 0x00; + +const USB_SUBCLASS_REPORT_ONLY: u8 = 0x00; +const USB_SUBCLASS_BOOT_OR_REPORT: u8 = 0x01; + const USB_PROTOCOL_NONE: u8 = 0x00; +const USB_PROTOCOL_KEYBOARD: u8 = 0x01; +const USB_PROTOCOL_MOUSE: u8 = 0x02; // HID const HID_DESC_DESCTYPE_HID: u8 = 0x21; @@ -132,13 +137,15 @@ fn build<'d, D: Driver<'d>>( state: &'d mut State<'d>, config: Config<'d>, with_out_endpoint: bool, + usb_subclass: u8, + usb_protocol: u8, ) -> (Option, D::EndpointIn, &'d AtomicUsize) { let len = config.report_descriptor.len(); - let mut func = builder.function(USB_CLASS_HID, USB_SUBCLASS_NONE, USB_PROTOCOL_NONE); + let mut func = builder.function(USB_CLASS_HID, usb_subclass, usb_protocol); let mut iface = func.interface(); let if_num = iface.interface_number(); - let mut alt = iface.alt_setting(USB_CLASS_HID, USB_SUBCLASS_NONE, USB_PROTOCOL_NONE, None); + let mut alt = iface.alt_setting(USB_CLASS_HID, usb_subclass, usb_protocol, None); // HID descriptor alt.descriptor( @@ -186,7 +193,42 @@ impl<'d, D: Driver<'d>, const READ_N: usize, const WRITE_N: usize> HidReaderWrit /// HID reports, consider using [`HidWriter::new`] instead, which allocates an IN endpoint only. /// pub fn new(builder: &mut Builder<'d, D>, state: &'d mut State<'d>, config: Config<'d>) -> Self { - let (ep_out, ep_in, offset) = build(builder, state, config, true); + HidReaderWriter::_new(builder, state, config, USB_SUBCLASS_REPORT_ONLY, USB_PROTOCOL_NONE) + } + + /// Creates a new `HidReaderWriter` for a HID Mouse, with support for the BOOT protocol mode. + /// + /// This will allocate one IN and one OUT endpoints. If you only need writing (sending) + /// HID reports, consider using [`HidWriter::new`] instead, which allocates an IN endpoint only. + /// + pub fn new_mouse(builder: &mut Builder<'d, D>, state: &'d mut State<'d>, config: Config<'d>) -> Self { + HidReaderWriter::_new(builder, state, config, USB_SUBCLASS_BOOT_OR_REPORT, USB_PROTOCOL_MOUSE) + } + + /// Creates a new `HidReaderWriter` for a HID Keyboard, with support for the BOOT protocol mode. + /// + /// This will allocate one IN and one OUT endpoints. If you only need writing (sending) + /// HID reports, consider using [`HidWriter::new`] instead, which allocates an IN endpoint only. + /// + pub fn new_keyboard(builder: &mut Builder<'d, D>, state: &'d mut State<'d>, config: Config<'d>) -> Self { + HidReaderWriter::_new( + builder, + state, + config, + USB_SUBCLASS_BOOT_OR_REPORT, + USB_PROTOCOL_KEYBOARD, + ) + } + + /// Private helper function to create a new `HidReaderWriter`. + fn _new( + builder: &mut Builder<'d, D>, + state: &'d mut State<'d>, + config: Config<'d>, + usb_subclass: u8, + usb_protocol: u8, + ) -> Self { + let (ep_out, ep_in, offset) = build(builder, state, config, true, usb_subclass, usb_protocol); Self { reader: HidReader { @@ -275,7 +317,50 @@ impl<'d, D: Driver<'d>, const N: usize> HidWriter<'d, D, N> { /// of CPU on the device & bandwidth on the bus. A value of 10 is reasonable for /// high performance uses, and a value of 255 is good for best-effort usecases. pub fn new(builder: &mut Builder<'d, D>, state: &'d mut State<'d>, config: Config<'d>) -> Self { - let (ep_out, ep_in, _offset) = build(builder, state, config, false); + HidWriter::_new(builder, state, config, USB_SUBCLASS_REPORT_ONLY, USB_PROTOCOL_NONE) + } + + /// Creates a new `HidWriter` for a HID Mouse, with support for the BOOT protocol mode. + /// + /// This will allocate one IN endpoint only, so the host won't be able to send + /// reports to us. If you need that, consider using [`HidReaderWriter::new`] instead. + /// + /// poll_ms configures how frequently the host should poll for reading/writing + /// HID reports. A lower value means better throughput & latency, at the expense + /// of CPU on the device & bandwidth on the bus. A value of 10 is reasonable for + /// high performance uses, and a value of 255 is good for best-effort usecases. + pub fn new_mouse(builder: &mut Builder<'d, D>, state: &'d mut State<'d>, config: Config<'d>) -> Self { + HidWriter::_new(builder, state, config, USB_SUBCLASS_BOOT_OR_REPORT, USB_PROTOCOL_MOUSE) + } + + /// Creates a new `HidWriter` for a HID Keyboard, with support for the BOOT protocol mode. + /// + /// This will allocate one IN endpoint only, so the host won't be able to send + /// reports to us. If you need that, consider using [`HidReaderWriter::new`] instead. + /// + /// poll_ms configures how frequently the host should poll for reading/writing + /// HID reports. A lower value means better throughput & latency, at the expense + /// of CPU on the device & bandwidth on the bus. A value of 10 is reasonable for + /// high performance uses, and a value of 255 is good for best-effort usecases. + pub fn new_keyboard(builder: &mut Builder<'d, D>, state: &'d mut State<'d>, config: Config<'d>) -> Self { + HidWriter::_new( + builder, + state, + config, + USB_SUBCLASS_BOOT_OR_REPORT, + USB_PROTOCOL_KEYBOARD, + ) + } + + /// Private helper function to create a new `HidWriter`. + pub fn _new( + builder: &mut Builder<'d, D>, + state: &'d mut State<'d>, + config: Config<'d>, + usb_subclass: u8, + usb_protocol: u8, + ) -> Self { + let (ep_out, ep_in, _offset) = build(builder, state, config, false, usb_subclass, usb_protocol); assert!(ep_out.is_none()); -- cgit From 2e303c995c53a97a1c2eaecf77827f02567b8417 Mon Sep 17 00:00:00 2001 From: matteo Date: Tue, 23 Sep 2025 20:20:43 +0200 Subject: update hid examples --- examples/nrf52840/src/bin/usb_hid_keyboard.rs | 6 +++++- examples/nrf52840/src/bin/usb_hid_mouse.rs | 6 +++++- examples/rp/src/bin/usb_hid_keyboard.rs | 6 +++++- examples/rp/src/bin/usb_hid_mouse.rs | 6 +++++- examples/rp235x/src/bin/usb_hid_keyboard.rs | 6 +++++- examples/stm32f4/src/bin/usb_hid_keyboard.rs | 6 +++++- examples/stm32f4/src/bin/usb_hid_mouse.rs | 6 +++++- examples/stm32l5/src/bin/usb_hid_mouse.rs | 6 +++++- 8 files changed, 40 insertions(+), 8 deletions(-) diff --git a/examples/nrf52840/src/bin/usb_hid_keyboard.rs b/examples/nrf52840/src/bin/usb_hid_keyboard.rs index 5a9dc90a2..0f0d830c6 100644 --- a/examples/nrf52840/src/bin/usb_hid_keyboard.rs +++ b/examples/nrf52840/src/bin/usb_hid_keyboard.rs @@ -45,6 +45,10 @@ async fn main(_spawner: Spawner) { config.max_power = 100; config.max_packet_size_0 = 64; config.supports_remote_wakeup = true; + config.composite_with_iads = false; + config.device_class = 0; + config.device_sub_class = 0; + config.device_protocol = 0; // Create embassy-usb DeviceBuilder using the driver and config. // It needs some buffers for building the descriptors. @@ -75,7 +79,7 @@ async fn main(_spawner: Spawner) { poll_ms: 60, max_packet_size: 64, }; - let hid = HidReaderWriter::<_, 1, 8>::new(&mut builder, &mut state, config); + let hid = HidReaderWriter::<_, 1, 8>::new_keyboard(&mut builder, &mut state, config); // Build the builder. let mut usb = builder.build(); diff --git a/examples/nrf52840/src/bin/usb_hid_mouse.rs b/examples/nrf52840/src/bin/usb_hid_mouse.rs index 80cda70e3..b3d90354b 100644 --- a/examples/nrf52840/src/bin/usb_hid_mouse.rs +++ b/examples/nrf52840/src/bin/usb_hid_mouse.rs @@ -37,6 +37,10 @@ async fn main(_spawner: Spawner) { config.serial_number = Some("12345678"); config.max_power = 100; config.max_packet_size_0 = 64; + config.composite_with_iads = false; + config.device_class = 0; + config.device_sub_class = 0; + config.device_protocol = 0; // Create embassy-usb DeviceBuilder using the driver and config. // It needs some buffers for building the descriptors. @@ -65,7 +69,7 @@ async fn main(_spawner: Spawner) { max_packet_size: 8, }; - let mut writer = HidWriter::<_, 5>::new(&mut builder, &mut state, config); + let mut writer = HidWriter::<_, 5>::new_mouse(&mut builder, &mut state, config); // Build the builder. let mut usb = builder.build(); diff --git a/examples/rp/src/bin/usb_hid_keyboard.rs b/examples/rp/src/bin/usb_hid_keyboard.rs index a7cb322d8..8658da6b5 100644 --- a/examples/rp/src/bin/usb_hid_keyboard.rs +++ b/examples/rp/src/bin/usb_hid_keyboard.rs @@ -33,6 +33,10 @@ async fn main(_spawner: Spawner) { config.serial_number = Some("12345678"); config.max_power = 100; config.max_packet_size_0 = 64; + config.composite_with_iads = false; + config.device_class = 0; + config.device_sub_class = 0; + config.device_protocol = 0; // Create embassy-usb DeviceBuilder using the driver and config. // It needs some buffers for building the descriptors. @@ -64,7 +68,7 @@ async fn main(_spawner: Spawner) { poll_ms: 60, max_packet_size: 64, }; - let hid = HidReaderWriter::<_, 1, 8>::new(&mut builder, &mut state, config); + let hid = HidReaderWriter::<_, 1, 8>::new_keyboard(&mut builder, &mut state, config); // Build the builder. let mut usb = builder.build(); diff --git a/examples/rp/src/bin/usb_hid_mouse.rs b/examples/rp/src/bin/usb_hid_mouse.rs index 4454c593c..4d8fc354e 100755 --- a/examples/rp/src/bin/usb_hid_mouse.rs +++ b/examples/rp/src/bin/usb_hid_mouse.rs @@ -35,6 +35,10 @@ async fn main(_spawner: Spawner) { config.serial_number = Some("12345678"); config.max_power = 100; config.max_packet_size_0 = 64; + config.composite_with_iads = false; + config.device_class = 0; + config.device_sub_class = 0; + config.device_protocol = 0; // Create embassy-usb DeviceBuilder using the driver and config. // It needs some buffers for building the descriptors. @@ -66,7 +70,7 @@ async fn main(_spawner: Spawner) { poll_ms: 60, max_packet_size: 64, }; - let hid = HidReaderWriter::<_, 1, 8>::new(&mut builder, &mut state, config); + let hid = HidReaderWriter::<_, 1, 8>::new_keyboard(&mut builder, &mut state, config); // Build the builder. let mut usb = builder.build(); diff --git a/examples/rp235x/src/bin/usb_hid_keyboard.rs b/examples/rp235x/src/bin/usb_hid_keyboard.rs index 6f496e23a..fa9eaa863 100644 --- a/examples/rp235x/src/bin/usb_hid_keyboard.rs +++ b/examples/rp235x/src/bin/usb_hid_keyboard.rs @@ -33,6 +33,10 @@ async fn main(_spawner: Spawner) { config.serial_number = Some("12345678"); config.max_power = 100; config.max_packet_size_0 = 64; + config.composite_with_iads = false; + config.device_class = 0; + config.device_sub_class = 0; + config.device_protocol = 0; // Create embassy-usb DeviceBuilder using the driver and config. // It needs some buffers for building the descriptors. @@ -64,7 +68,7 @@ async fn main(_spawner: Spawner) { poll_ms: 60, max_packet_size: 64, }; - let hid = HidReaderWriter::<_, 1, 8>::new(&mut builder, &mut state, config); + let hid = HidReaderWriter::<_, 1, 8>::new_keyboard(&mut builder, &mut state, config); // Build the builder. let mut usb = builder.build(); diff --git a/examples/stm32f4/src/bin/usb_hid_keyboard.rs b/examples/stm32f4/src/bin/usb_hid_keyboard.rs index d6b4a9bc9..6ddfba83a 100644 --- a/examples/stm32f4/src/bin/usb_hid_keyboard.rs +++ b/examples/stm32f4/src/bin/usb_hid_keyboard.rs @@ -70,6 +70,10 @@ async fn main(_spawner: Spawner) { config.serial_number = Some("12345678"); config.max_power = 100; config.max_packet_size_0 = 64; + config.composite_with_iads = false; + config.device_class = 0; + config.device_sub_class = 0; + config.device_protocol = 0; // Create embassy-usb DeviceBuilder using the driver and config. // It needs some buffers for building the descriptors. @@ -103,7 +107,7 @@ async fn main(_spawner: Spawner) { max_packet_size: 8, }; - let hid = HidReaderWriter::<_, 1, 8>::new(&mut builder, &mut state, config); + let hid = HidReaderWriter::<_, 1, 8>::new_keyboard(&mut builder, &mut state, config); // Build the builder. let mut usb = builder.build(); diff --git a/examples/stm32f4/src/bin/usb_hid_mouse.rs b/examples/stm32f4/src/bin/usb_hid_mouse.rs index badb65e98..8d035d0d5 100644 --- a/examples/stm32f4/src/bin/usb_hid_mouse.rs +++ b/examples/stm32f4/src/bin/usb_hid_mouse.rs @@ -65,6 +65,10 @@ async fn main(_spawner: Spawner) { config.manufacturer = Some("Embassy"); config.product = Some("HID mouse example"); config.serial_number = Some("12345678"); + config.composite_with_iads = false; + config.device_class = 0; + config.device_sub_class = 0; + config.device_protocol = 0; // Create embassy-usb DeviceBuilder using the driver and config. // It needs some buffers for building the descriptors. @@ -93,7 +97,7 @@ async fn main(_spawner: Spawner) { max_packet_size: 8, }; - let mut writer = HidWriter::<_, 5>::new(&mut builder, &mut state, config); + let mut writer = HidWriter::<_, 5>::new_mouse(&mut builder, &mut state, config); // Build the builder. let mut usb = builder.build(); diff --git a/examples/stm32l5/src/bin/usb_hid_mouse.rs b/examples/stm32l5/src/bin/usb_hid_mouse.rs index 3f8c52b82..6f9200548 100644 --- a/examples/stm32l5/src/bin/usb_hid_mouse.rs +++ b/examples/stm32l5/src/bin/usb_hid_mouse.rs @@ -48,6 +48,10 @@ async fn main(_spawner: Spawner) { config.serial_number = Some("12345678"); config.max_power = 100; config.max_packet_size_0 = 64; + config.composite_with_iads = false; + config.device_class = 0; + config.device_sub_class = 0; + config.device_protocol = 0; // Create embassy-usb DeviceBuilder using the driver and config. // It needs some buffers for building the descriptors. @@ -75,7 +79,7 @@ async fn main(_spawner: Spawner) { max_packet_size: 8, }; - let mut writer = HidWriter::<_, 5>::new(&mut builder, &mut state, config); + let mut writer = HidWriter::<_, 5>::new_mouse(&mut builder, &mut state, config); // Build the builder. let mut usb = builder.build(); -- cgit From 8eebeceb16fc5ef15285c62d21b8ea65b9baf6ee Mon Sep 17 00:00:00 2001 From: matteo Date: Tue, 23 Sep 2025 20:51:58 +0200 Subject: working nrf52840 examples --- examples/nrf52840/Cargo.toml | 2 +- examples/nrf52840/src/bin/usb_hid_keyboard.rs | 49 +++++++++++++++++++++------ examples/nrf52840/src/bin/usb_hid_mouse.rs | 47 +++++++++++++++++++------ 3 files changed, 76 insertions(+), 22 deletions(-) diff --git a/examples/nrf52840/Cargo.toml b/examples/nrf52840/Cargo.toml index 452e83b7e..9a1fc080e 100644 --- a/examples/nrf52840/Cargo.toml +++ b/examples/nrf52840/Cargo.toml @@ -28,7 +28,7 @@ cortex-m-rt = "0.7.0" panic-probe = { version = "1.0.0", features = ["print-defmt"] } rand = { version = "0.9.0", default-features = false } embedded-storage = "0.3.1" -usbd-hid = "0.8.1" +usbd-hid = { version = "0.8.1", features = ["defmt"] } serde = { version = "1.0.136", default-features = false } embedded-hal = { version = "1.0" } embedded-hal-async = { version = "1.0" } diff --git a/examples/nrf52840/src/bin/usb_hid_keyboard.rs b/examples/nrf52840/src/bin/usb_hid_keyboard.rs index 0f0d830c6..a4931099a 100644 --- a/examples/nrf52840/src/bin/usb_hid_keyboard.rs +++ b/examples/nrf52840/src/bin/usb_hid_keyboard.rs @@ -1,7 +1,7 @@ #![no_std] #![no_main] -use core::sync::atomic::{AtomicBool, Ordering}; +use core::sync::atomic::{AtomicBool, AtomicU8, Ordering}; use defmt::*; use embassy_executor::Spawner; @@ -17,6 +17,7 @@ use embassy_usb::class::hid::{HidReaderWriter, ReportId, RequestHandler, State}; use embassy_usb::control::OutResponse; use embassy_usb::{Builder, Config, Handler}; use usbd_hid::descriptor::{KeyboardReport, SerializedDescriptor}; +use usbd_hid::hid_class::HidProtocolMode; use {defmt_rtt as _, panic_probe as _}; bind_interrupts!(struct Irqs { @@ -26,6 +27,8 @@ bind_interrupts!(struct Irqs { static SUSPENDED: AtomicBool = AtomicBool::new(false); +static HID_PROTOCOL_MODE: AtomicU8 = AtomicU8::new(HidProtocolMode::Boot as u8); + #[embassy_executor::main] async fn main(_spawner: Spawner) { let p = embassy_nrf::init(Default::default()); @@ -110,6 +113,11 @@ async fn main(_spawner: Spawner) { if SUSPENDED.load(Ordering::Acquire) { info!("Triggering remote wakeup"); remote_wakeup.signal(()); + } else if HID_PROTOCOL_MODE.load(Ordering::Relaxed) == HidProtocolMode::Boot as u8 { + match writer.write(&[0, 0, 4, 0, 0, 0, 0, 0]).await { + Ok(()) => {} + Err(e) => warn!("Failed to send boot report: {:?}", e), + }; } else { let report = KeyboardReport { keycodes: [4, 0, 0, 0, 0, 0], @@ -125,16 +133,23 @@ async fn main(_spawner: Spawner) { button.wait_for_high().await; info!("RELEASED"); - let report = KeyboardReport { - keycodes: [0, 0, 0, 0, 0, 0], - leds: 0, - modifier: 0, - reserved: 0, - }; - match writer.write_serialize(&report).await { - Ok(()) => {} - Err(e) => warn!("Failed to send report: {:?}", e), - }; + if HID_PROTOCOL_MODE.load(Ordering::Relaxed) == HidProtocolMode::Boot as u8 { + match writer.write(&[0, 0, 0, 0, 0, 0, 0, 0]).await { + Ok(()) => {} + Err(e) => warn!("Failed to send boot report: {:?}", e), + }; + } else { + let report = KeyboardReport { + keycodes: [0, 0, 0, 0, 0, 0], + leds: 0, + modifier: 0, + reserved: 0, + }; + match writer.write_serialize(&report).await { + Ok(()) => {} + Err(e) => warn!("Failed to send report: {:?}", e), + }; + } } }; @@ -160,6 +175,18 @@ impl RequestHandler for MyRequestHandler { OutResponse::Accepted } + fn get_protocol(&self) -> HidProtocolMode { + let protocol = HidProtocolMode::from(HID_PROTOCOL_MODE.load(Ordering::Relaxed)); + info!("The current HID protocol mode is: {}", protocol); + protocol + } + + fn set_protocol(&mut self, protocol: HidProtocolMode) -> OutResponse { + info!("Switching to HID protocol mode: {}", protocol); + HID_PROTOCOL_MODE.store(protocol as u8, Ordering::Relaxed); + OutResponse::Accepted + } + fn set_idle_ms(&mut self, id: Option, dur: u32) { info!("Set idle rate for {:?} to {:?}", id, dur); } diff --git a/examples/nrf52840/src/bin/usb_hid_mouse.rs b/examples/nrf52840/src/bin/usb_hid_mouse.rs index b3d90354b..6ec8a2d33 100644 --- a/examples/nrf52840/src/bin/usb_hid_mouse.rs +++ b/examples/nrf52840/src/bin/usb_hid_mouse.rs @@ -1,6 +1,8 @@ #![no_std] #![no_main] +use core::sync::atomic::{AtomicU8, Ordering}; + use defmt::*; use embassy_executor::Spawner; use embassy_futures::join::join; @@ -12,6 +14,7 @@ use embassy_usb::class::hid::{HidWriter, ReportId, RequestHandler, State}; use embassy_usb::control::OutResponse; use embassy_usb::{Builder, Config}; use usbd_hid::descriptor::{MouseReport, SerializedDescriptor}; +use usbd_hid::hid_class::HidProtocolMode; use {defmt_rtt as _, panic_probe as _}; bind_interrupts!(struct Irqs { @@ -19,6 +22,8 @@ bind_interrupts!(struct Irqs { CLOCK_POWER => usb::vbus_detect::InterruptHandler; }); +static HID_PROTOCOL_MODE: AtomicU8 = AtomicU8::new(HidProtocolMode::Boot as u8); + #[embassy_executor::main] async fn main(_spawner: Spawner) { let p = embassy_nrf::init(Default::default()); @@ -84,16 +89,26 @@ async fn main(_spawner: Spawner) { Timer::after_millis(500).await; y = -y; - let report = MouseReport { - buttons: 0, - x: 0, - y, - wheel: 0, - pan: 0, - }; - match writer.write_serialize(&report).await { - Ok(()) => {} - Err(e) => warn!("Failed to send report: {:?}", e), + + if HID_PROTOCOL_MODE.load(Ordering::Relaxed) == HidProtocolMode::Boot as u8 { + let buttons = 0u8; + let x = 0i8; + match writer.write(&[buttons, x as u8, y as u8]).await { + Ok(()) => {} + Err(e) => warn!("Failed to send boot report: {:?}", e), + } + } else { + let report = MouseReport { + buttons: 0, + x: 0, + y, + wheel: 0, + pan: 0, + }; + match writer.write_serialize(&report).await { + Ok(()) => {} + Err(e) => warn!("Failed to send report: {:?}", e), + } } } }; @@ -116,6 +131,18 @@ impl RequestHandler for MyRequestHandler { OutResponse::Accepted } + fn get_protocol(&self) -> HidProtocolMode { + let protocol = HidProtocolMode::from(HID_PROTOCOL_MODE.load(Ordering::Relaxed)); + info!("The current HID protocol mode is: {}", protocol); + protocol + } + + fn set_protocol(&mut self, protocol: HidProtocolMode) -> OutResponse { + info!("Switching to HID protocol mode: {}", protocol); + HID_PROTOCOL_MODE.store(protocol as u8, Ordering::Relaxed); + OutResponse::Accepted + } + fn set_idle_ms(&mut self, id: Option, dur: u32) { info!("Set idle rate for {:?} to {:?}", id, dur); } -- cgit From 5c8218b8750bed3f4bef7973e250aa830d8c2fe3 Mon Sep 17 00:00:00 2001 From: matteo Date: Tue, 30 Sep 2025 18:38:43 +0200 Subject: review comments --- embassy-usb/src/class/hid.rs | 134 ++++++++------------------ examples/nrf52840/Cargo.toml | 2 +- examples/nrf52840/src/bin/usb_hid_keyboard.rs | 9 +- examples/nrf52840/src/bin/usb_hid_mouse.rs | 9 +- examples/rp/src/bin/usb_hid_keyboard.rs | 6 +- examples/rp/src/bin/usb_hid_mouse.rs | 6 +- examples/rp235x/src/bin/usb_hid_keyboard.rs | 8 +- examples/stm32f4/src/bin/usb_hid_keyboard.rs | 6 +- examples/stm32f4/src/bin/usb_hid_mouse.rs | 6 +- examples/stm32l5/src/bin/usb_hid_mouse.rs | 6 +- 10 files changed, 78 insertions(+), 114 deletions(-) diff --git a/embassy-usb/src/class/hid.rs b/embassy-usb/src/class/hid.rs index b9830baeb..6723afbbc 100644 --- a/embassy-usb/src/class/hid.rs +++ b/embassy-usb/src/class/hid.rs @@ -8,8 +8,6 @@ use core::sync::atomic::{AtomicUsize, Ordering}; use ssmarshal::serialize; #[cfg(feature = "usbd-hid")] use usbd_hid::descriptor::AsInputReport; -#[cfg(feature = "usbd-hid")] -use usbd_hid::hid_class::HidProtocolMode; use crate::control::{InResponse, OutResponse, Recipient, Request, RequestType}; use crate::driver::{Driver, Endpoint, EndpointError, EndpointIn, EndpointOut}; @@ -18,13 +16,6 @@ use crate::{Builder, Handler}; const USB_CLASS_HID: u8 = 0x03; -const USB_SUBCLASS_REPORT_ONLY: u8 = 0x00; -const USB_SUBCLASS_BOOT_OR_REPORT: u8 = 0x01; - -const USB_PROTOCOL_NONE: u8 = 0x00; -const USB_PROTOCOL_KEYBOARD: u8 = 0x01; -const USB_PROTOCOL_MOUSE: u8 = 0x02; - // HID const HID_DESC_DESCTYPE_HID: u8 = 0x21; const HID_DESC_DESCTYPE_HID_REPORT: u8 = 0x22; @@ -40,7 +31,6 @@ const HID_REQ_SET_PROTOCOL: u8 = 0x0b; /// Get/Set Protocol mapping /// See (7.2.5 and 7.2.6): -#[cfg(not(feature = "usbd-hid"))] #[derive(Copy, Clone, Debug, PartialEq, Eq)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] #[repr(u8)] @@ -51,7 +41,6 @@ pub enum HidProtocolMode { Report = 1, } -#[cfg(not(feature = "usbd-hid"))] impl From for HidProtocolMode { fn from(mode: u8) -> HidProtocolMode { if mode == HidProtocolMode::Boot as u8 { @@ -62,6 +51,30 @@ impl From for HidProtocolMode { } } +/// USB HID interface subclass values. +#[derive(Copy, Clone, Debug, PartialEq, Eq)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +#[repr(u8)] +pub enum HidSubclass { + /// Only report mode is supported. + ReportOnly = 0, + /// Both boot and report mode are supported. + ReportOrBoot = 1, +} + +/// USB HID protocol values. +#[derive(Copy, Clone, Debug, PartialEq, Eq)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +#[repr(u8)] +pub enum HidBootProtocol { + /// No specific boot protocol. + None = 0, + /// Boot protocol keyboard. + Keyboard = 1, + /// Boot protocol mouse. + Mouse = 2, +} + /// Configuration for the HID class. pub struct Config<'d> { /// HID report descriptor. @@ -79,6 +92,12 @@ pub struct Config<'d> { /// Max packet size for both the IN and OUT endpoints. pub max_packet_size: u16, + + /// The HID subclass of this interface + pub hid_subclass: HidSubclass, + + /// The HID boot protocol of this interface + pub hid_boot_protocol: HidBootProtocol, } /// Report ID @@ -137,15 +156,18 @@ fn build<'d, D: Driver<'d>>( state: &'d mut State<'d>, config: Config<'d>, with_out_endpoint: bool, - usb_subclass: u8, - usb_protocol: u8, ) -> (Option, D::EndpointIn, &'d AtomicUsize) { let len = config.report_descriptor.len(); - let mut func = builder.function(USB_CLASS_HID, usb_subclass, usb_protocol); + let mut func = builder.function(USB_CLASS_HID, config.hid_subclass as u8, config.hid_boot_protocol as u8); let mut iface = func.interface(); let if_num = iface.interface_number(); - let mut alt = iface.alt_setting(USB_CLASS_HID, usb_subclass, usb_protocol, None); + let mut alt = iface.alt_setting( + USB_CLASS_HID, + config.hid_subclass as u8, + config.hid_boot_protocol as u8, + None, + ); // HID descriptor alt.descriptor( @@ -193,42 +215,7 @@ impl<'d, D: Driver<'d>, const READ_N: usize, const WRITE_N: usize> HidReaderWrit /// HID reports, consider using [`HidWriter::new`] instead, which allocates an IN endpoint only. /// pub fn new(builder: &mut Builder<'d, D>, state: &'d mut State<'d>, config: Config<'d>) -> Self { - HidReaderWriter::_new(builder, state, config, USB_SUBCLASS_REPORT_ONLY, USB_PROTOCOL_NONE) - } - - /// Creates a new `HidReaderWriter` for a HID Mouse, with support for the BOOT protocol mode. - /// - /// This will allocate one IN and one OUT endpoints. If you only need writing (sending) - /// HID reports, consider using [`HidWriter::new`] instead, which allocates an IN endpoint only. - /// - pub fn new_mouse(builder: &mut Builder<'d, D>, state: &'d mut State<'d>, config: Config<'d>) -> Self { - HidReaderWriter::_new(builder, state, config, USB_SUBCLASS_BOOT_OR_REPORT, USB_PROTOCOL_MOUSE) - } - - /// Creates a new `HidReaderWriter` for a HID Keyboard, with support for the BOOT protocol mode. - /// - /// This will allocate one IN and one OUT endpoints. If you only need writing (sending) - /// HID reports, consider using [`HidWriter::new`] instead, which allocates an IN endpoint only. - /// - pub fn new_keyboard(builder: &mut Builder<'d, D>, state: &'d mut State<'d>, config: Config<'d>) -> Self { - HidReaderWriter::_new( - builder, - state, - config, - USB_SUBCLASS_BOOT_OR_REPORT, - USB_PROTOCOL_KEYBOARD, - ) - } - - /// Private helper function to create a new `HidReaderWriter`. - fn _new( - builder: &mut Builder<'d, D>, - state: &'d mut State<'d>, - config: Config<'d>, - usb_subclass: u8, - usb_protocol: u8, - ) -> Self { - let (ep_out, ep_in, offset) = build(builder, state, config, true, usb_subclass, usb_protocol); + let (ep_out, ep_in, offset) = build(builder, state, config, true); Self { reader: HidReader { @@ -317,50 +304,7 @@ impl<'d, D: Driver<'d>, const N: usize> HidWriter<'d, D, N> { /// of CPU on the device & bandwidth on the bus. A value of 10 is reasonable for /// high performance uses, and a value of 255 is good for best-effort usecases. pub fn new(builder: &mut Builder<'d, D>, state: &'d mut State<'d>, config: Config<'d>) -> Self { - HidWriter::_new(builder, state, config, USB_SUBCLASS_REPORT_ONLY, USB_PROTOCOL_NONE) - } - - /// Creates a new `HidWriter` for a HID Mouse, with support for the BOOT protocol mode. - /// - /// This will allocate one IN endpoint only, so the host won't be able to send - /// reports to us. If you need that, consider using [`HidReaderWriter::new`] instead. - /// - /// poll_ms configures how frequently the host should poll for reading/writing - /// HID reports. A lower value means better throughput & latency, at the expense - /// of CPU on the device & bandwidth on the bus. A value of 10 is reasonable for - /// high performance uses, and a value of 255 is good for best-effort usecases. - pub fn new_mouse(builder: &mut Builder<'d, D>, state: &'d mut State<'d>, config: Config<'d>) -> Self { - HidWriter::_new(builder, state, config, USB_SUBCLASS_BOOT_OR_REPORT, USB_PROTOCOL_MOUSE) - } - - /// Creates a new `HidWriter` for a HID Keyboard, with support for the BOOT protocol mode. - /// - /// This will allocate one IN endpoint only, so the host won't be able to send - /// reports to us. If you need that, consider using [`HidReaderWriter::new`] instead. - /// - /// poll_ms configures how frequently the host should poll for reading/writing - /// HID reports. A lower value means better throughput & latency, at the expense - /// of CPU on the device & bandwidth on the bus. A value of 10 is reasonable for - /// high performance uses, and a value of 255 is good for best-effort usecases. - pub fn new_keyboard(builder: &mut Builder<'d, D>, state: &'d mut State<'d>, config: Config<'d>) -> Self { - HidWriter::_new( - builder, - state, - config, - USB_SUBCLASS_BOOT_OR_REPORT, - USB_PROTOCOL_KEYBOARD, - ) - } - - /// Private helper function to create a new `HidWriter`. - pub fn _new( - builder: &mut Builder<'d, D>, - state: &'d mut State<'d>, - config: Config<'d>, - usb_subclass: u8, - usb_protocol: u8, - ) -> Self { - let (ep_out, ep_in, _offset) = build(builder, state, config, false, usb_subclass, usb_protocol); + let (ep_out, ep_in, _offset) = build(builder, state, config, false); assert!(ep_out.is_none()); diff --git a/examples/nrf52840/Cargo.toml b/examples/nrf52840/Cargo.toml index 9a1fc080e..452e83b7e 100644 --- a/examples/nrf52840/Cargo.toml +++ b/examples/nrf52840/Cargo.toml @@ -28,7 +28,7 @@ cortex-m-rt = "0.7.0" panic-probe = { version = "1.0.0", features = ["print-defmt"] } rand = { version = "0.9.0", default-features = false } embedded-storage = "0.3.1" -usbd-hid = { version = "0.8.1", features = ["defmt"] } +usbd-hid = "0.8.1" serde = { version = "1.0.136", default-features = false } embedded-hal = { version = "1.0" } embedded-hal-async = { version = "1.0" } diff --git a/examples/nrf52840/src/bin/usb_hid_keyboard.rs b/examples/nrf52840/src/bin/usb_hid_keyboard.rs index a4931099a..8649d5667 100644 --- a/examples/nrf52840/src/bin/usb_hid_keyboard.rs +++ b/examples/nrf52840/src/bin/usb_hid_keyboard.rs @@ -13,11 +13,12 @@ use embassy_nrf::usb::Driver; use embassy_nrf::{bind_interrupts, pac, peripherals, usb}; use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex; use embassy_sync::signal::Signal; -use embassy_usb::class::hid::{HidReaderWriter, ReportId, RequestHandler, State}; +use embassy_usb::class::hid::{ + HidBootProtocol, HidProtocolMode, HidReaderWriter, HidSubclass, ReportId, RequestHandler, State, +}; use embassy_usb::control::OutResponse; use embassy_usb::{Builder, Config, Handler}; use usbd_hid::descriptor::{KeyboardReport, SerializedDescriptor}; -use usbd_hid::hid_class::HidProtocolMode; use {defmt_rtt as _, panic_probe as _}; bind_interrupts!(struct Irqs { @@ -81,8 +82,10 @@ async fn main(_spawner: Spawner) { request_handler: None, poll_ms: 60, max_packet_size: 64, + hid_subclass: HidSubclass::ReportOrBoot, + hid_boot_protocol: HidBootProtocol::Keyboard, }; - let hid = HidReaderWriter::<_, 1, 8>::new_keyboard(&mut builder, &mut state, config); + let hid = HidReaderWriter::<_, 1, 8>::new(&mut builder, &mut state, config); // Build the builder. let mut usb = builder.build(); diff --git a/examples/nrf52840/src/bin/usb_hid_mouse.rs b/examples/nrf52840/src/bin/usb_hid_mouse.rs index 6ec8a2d33..4baf2e814 100644 --- a/examples/nrf52840/src/bin/usb_hid_mouse.rs +++ b/examples/nrf52840/src/bin/usb_hid_mouse.rs @@ -10,11 +10,12 @@ use embassy_nrf::usb::vbus_detect::HardwareVbusDetect; use embassy_nrf::usb::Driver; use embassy_nrf::{bind_interrupts, pac, peripherals, usb}; use embassy_time::Timer; -use embassy_usb::class::hid::{HidWriter, ReportId, RequestHandler, State}; +use embassy_usb::class::hid::{ + HidBootProtocol, HidProtocolMode, HidSubclass, HidWriter, ReportId, RequestHandler, State, +}; use embassy_usb::control::OutResponse; use embassy_usb::{Builder, Config}; use usbd_hid::descriptor::{MouseReport, SerializedDescriptor}; -use usbd_hid::hid_class::HidProtocolMode; use {defmt_rtt as _, panic_probe as _}; bind_interrupts!(struct Irqs { @@ -72,9 +73,11 @@ async fn main(_spawner: Spawner) { request_handler: Some(&mut request_handler), poll_ms: 60, max_packet_size: 8, + hid_subclass: HidSubclass::ReportOrBoot, + hid_boot_protocol: HidBootProtocol::Mouse, }; - let mut writer = HidWriter::<_, 5>::new_mouse(&mut builder, &mut state, config); + let mut writer = HidWriter::<_, 5>::new(&mut builder, &mut state, config); // Build the builder. let mut usb = builder.build(); diff --git a/examples/rp/src/bin/usb_hid_keyboard.rs b/examples/rp/src/bin/usb_hid_keyboard.rs index 8658da6b5..fa78d0c2e 100644 --- a/examples/rp/src/bin/usb_hid_keyboard.rs +++ b/examples/rp/src/bin/usb_hid_keyboard.rs @@ -10,7 +10,7 @@ use embassy_rp::bind_interrupts; use embassy_rp::gpio::{Input, Pull}; use embassy_rp::peripherals::USB; use embassy_rp::usb::{Driver, InterruptHandler}; -use embassy_usb::class::hid::{HidReaderWriter, ReportId, RequestHandler, State}; +use embassy_usb::class::hid::{HidBootProtocol, HidReaderWriter, HidSubclass, ReportId, RequestHandler, State}; use embassy_usb::control::OutResponse; use embassy_usb::{Builder, Config, Handler}; use usbd_hid::descriptor::{KeyboardReport, SerializedDescriptor}; @@ -67,8 +67,10 @@ async fn main(_spawner: Spawner) { request_handler: None, poll_ms: 60, max_packet_size: 64, + hid_subclass: HidSubclass::ReportOrBoot, + hid_boot_protocol: HidBootProtocol::Keyboard, }; - let hid = HidReaderWriter::<_, 1, 8>::new_keyboard(&mut builder, &mut state, config); + let hid = HidReaderWriter::<_, 1, 8>::new(&mut builder, &mut state, config); // Build the builder. let mut usb = builder.build(); diff --git a/examples/rp/src/bin/usb_hid_mouse.rs b/examples/rp/src/bin/usb_hid_mouse.rs index 4d8fc354e..100e6048a 100755 --- a/examples/rp/src/bin/usb_hid_mouse.rs +++ b/examples/rp/src/bin/usb_hid_mouse.rs @@ -11,7 +11,7 @@ use embassy_rp::clocks::RoscRng; use embassy_rp::peripherals::USB; use embassy_rp::usb::{Driver, InterruptHandler}; use embassy_time::Timer; -use embassy_usb::class::hid::{HidReaderWriter, ReportId, RequestHandler, State}; +use embassy_usb::class::hid::{HidBootProtocol, HidReaderWriter, HidSubclass, ReportId, RequestHandler, State}; use embassy_usb::control::OutResponse; use embassy_usb::{Builder, Config, Handler}; use rand::Rng; @@ -69,8 +69,10 @@ async fn main(_spawner: Spawner) { request_handler: None, poll_ms: 60, max_packet_size: 64, + hid_subclass: HidSubclass::ReportOrBoot, + hid_boot_protocol: HidBootProtocol::Mouse, }; - let hid = HidReaderWriter::<_, 1, 8>::new_keyboard(&mut builder, &mut state, config); + let hid = HidReaderWriter::<_, 1, 8>::new(&mut builder, &mut state, config); // Build the builder. let mut usb = builder.build(); diff --git a/examples/rp235x/src/bin/usb_hid_keyboard.rs b/examples/rp235x/src/bin/usb_hid_keyboard.rs index fa9eaa863..3203176cb 100644 --- a/examples/rp235x/src/bin/usb_hid_keyboard.rs +++ b/examples/rp235x/src/bin/usb_hid_keyboard.rs @@ -10,7 +10,9 @@ use embassy_rp::bind_interrupts; use embassy_rp::gpio::{Input, Pull}; use embassy_rp::peripherals::USB; use embassy_rp::usb::{Driver as UsbDriver, InterruptHandler}; -use embassy_usb::class::hid::{HidReaderWriter, ReportId, RequestHandler, State as HidState}; +use embassy_usb::class::hid::{ + HidBootProtocol, HidReaderWriter, HidSubclass, ReportId, RequestHandler, State as HidState, +}; use embassy_usb::control::OutResponse; use embassy_usb::{Builder, Config, Handler}; use usbd_hid::descriptor::{KeyboardReport, SerializedDescriptor}; @@ -67,8 +69,10 @@ async fn main(_spawner: Spawner) { request_handler: None, poll_ms: 60, max_packet_size: 64, + hid_subclass: HidSubclass::ReportOrBoot, + hid_boot_protocol: HidBootProtocol::Keyboard, }; - let hid = HidReaderWriter::<_, 1, 8>::new_keyboard(&mut builder, &mut state, config); + let hid = HidReaderWriter::<_, 1, 8>::new(&mut builder, &mut state, config); // Build the builder. let mut usb = builder.build(); diff --git a/examples/stm32f4/src/bin/usb_hid_keyboard.rs b/examples/stm32f4/src/bin/usb_hid_keyboard.rs index 6ddfba83a..740fbcaef 100644 --- a/examples/stm32f4/src/bin/usb_hid_keyboard.rs +++ b/examples/stm32f4/src/bin/usb_hid_keyboard.rs @@ -11,7 +11,7 @@ use embassy_stm32::gpio::Pull; use embassy_stm32::time::Hertz; use embassy_stm32::usb::Driver; use embassy_stm32::{bind_interrupts, peripherals, usb, Config}; -use embassy_usb::class::hid::{HidReaderWriter, ReportId, RequestHandler, State}; +use embassy_usb::class::hid::{HidBootProtocol, HidReaderWriter, HidSubclass, ReportId, RequestHandler, State}; use embassy_usb::control::OutResponse; use embassy_usb::{Builder, Handler}; use usbd_hid::descriptor::{KeyboardReport, SerializedDescriptor}; @@ -105,9 +105,11 @@ async fn main(_spawner: Spawner) { request_handler: None, poll_ms: 60, max_packet_size: 8, + hid_subclass: HidSubclass::ReportOrBoot, + hid_boot_protocol: HidBootProtocol::Keyboard, }; - let hid = HidReaderWriter::<_, 1, 8>::new_keyboard(&mut builder, &mut state, config); + let hid = HidReaderWriter::<_, 1, 8>::new(&mut builder, &mut state, config); // Build the builder. let mut usb = builder.build(); diff --git a/examples/stm32f4/src/bin/usb_hid_mouse.rs b/examples/stm32f4/src/bin/usb_hid_mouse.rs index 8d035d0d5..09af204c4 100644 --- a/examples/stm32f4/src/bin/usb_hid_mouse.rs +++ b/examples/stm32f4/src/bin/usb_hid_mouse.rs @@ -8,7 +8,7 @@ use embassy_stm32::time::Hertz; use embassy_stm32::usb::Driver; use embassy_stm32::{bind_interrupts, peripherals, usb, Config}; use embassy_time::Timer; -use embassy_usb::class::hid::{HidWriter, ReportId, RequestHandler, State}; +use embassy_usb::class::hid::{HidBootProtocol, HidSubclass, HidWriter, ReportId, RequestHandler, State}; use embassy_usb::control::OutResponse; use embassy_usb::Builder; use usbd_hid::descriptor::{MouseReport, SerializedDescriptor}; @@ -95,9 +95,11 @@ async fn main(_spawner: Spawner) { request_handler: Some(&mut request_handler), poll_ms: 60, max_packet_size: 8, + hid_subclass: HidSubclass::ReportOrBoot, + hid_boot_protocol: HidBootProtocol::Mouse, }; - let mut writer = HidWriter::<_, 5>::new_mouse(&mut builder, &mut state, config); + let mut writer = HidWriter::<_, 5>::new(&mut builder, &mut state, config); // Build the builder. let mut usb = builder.build(); diff --git a/examples/stm32l5/src/bin/usb_hid_mouse.rs b/examples/stm32l5/src/bin/usb_hid_mouse.rs index 6f9200548..30dbd2698 100644 --- a/examples/stm32l5/src/bin/usb_hid_mouse.rs +++ b/examples/stm32l5/src/bin/usb_hid_mouse.rs @@ -7,7 +7,7 @@ use embassy_futures::join::join; use embassy_stm32::usb::Driver; use embassy_stm32::{bind_interrupts, peripherals, usb, Config}; use embassy_time::Timer; -use embassy_usb::class::hid::{HidWriter, ReportId, RequestHandler, State}; +use embassy_usb::class::hid::{HidBootProtocol, HidSubclass, HidWriter, ReportId, RequestHandler, State}; use embassy_usb::control::OutResponse; use embassy_usb::Builder; use usbd_hid::descriptor::{MouseReport, SerializedDescriptor}; @@ -77,9 +77,11 @@ async fn main(_spawner: Spawner) { request_handler: Some(&mut request_handler), poll_ms: 60, max_packet_size: 8, + hid_subclass: HidSubclass::ReportOrBoot, + hid_boot_protocol: HidBootProtocol::Mouse, }; - let mut writer = HidWriter::<_, 5>::new_mouse(&mut builder, &mut state, config); + let mut writer = HidWriter::<_, 5>::new(&mut builder, &mut state, config); // Build the builder. let mut usb = builder.build(); -- cgit From d79d433d02ab154e5f8570392fd0ca1ffdf9cac1 Mon Sep 17 00:00:00 2001 From: matteo Date: Wed, 1 Oct 2025 18:30:15 +0200 Subject: rename HidSubclass to match hid spec --- embassy-usb/src/class/hid.rs | 14 +++++++------- examples/nrf52840/src/bin/usb_hid_keyboard.rs | 2 +- examples/nrf52840/src/bin/usb_hid_mouse.rs | 2 +- examples/rp/src/bin/usb_hid_keyboard.rs | 2 +- examples/rp/src/bin/usb_hid_mouse.rs | 2 +- examples/rp235x/src/bin/usb_hid_keyboard.rs | 2 +- examples/stm32f4/src/bin/usb_hid_keyboard.rs | 2 +- examples/stm32f4/src/bin/usb_hid_mouse.rs | 2 +- examples/stm32l5/src/bin/usb_hid_mouse.rs | 2 +- 9 files changed, 15 insertions(+), 15 deletions(-) diff --git a/embassy-usb/src/class/hid.rs b/embassy-usb/src/class/hid.rs index 6723afbbc..64e8fd59f 100644 --- a/embassy-usb/src/class/hid.rs +++ b/embassy-usb/src/class/hid.rs @@ -56,10 +56,10 @@ impl From for HidProtocolMode { #[cfg_attr(feature = "defmt", derive(defmt::Format))] #[repr(u8)] pub enum HidSubclass { - /// Only report mode is supported. - ReportOnly = 0, - /// Both boot and report mode are supported. - ReportOrBoot = 1, + /// No subclass, standard HID device. + No = 0, + /// Boot interface subclass, supports BIOS boot protocol. + Boot = 1, } /// USB HID protocol values. @@ -67,11 +67,11 @@ pub enum HidSubclass { #[cfg_attr(feature = "defmt", derive(defmt::Format))] #[repr(u8)] pub enum HidBootProtocol { - /// No specific boot protocol. + /// No boot protocol. None = 0, - /// Boot protocol keyboard. + /// Keyboard boot protocol. Keyboard = 1, - /// Boot protocol mouse. + /// Mouse boot protocol. Mouse = 2, } diff --git a/examples/nrf52840/src/bin/usb_hid_keyboard.rs b/examples/nrf52840/src/bin/usb_hid_keyboard.rs index 8649d5667..540580c31 100644 --- a/examples/nrf52840/src/bin/usb_hid_keyboard.rs +++ b/examples/nrf52840/src/bin/usb_hid_keyboard.rs @@ -82,7 +82,7 @@ async fn main(_spawner: Spawner) { request_handler: None, poll_ms: 60, max_packet_size: 64, - hid_subclass: HidSubclass::ReportOrBoot, + hid_subclass: HidSubclass::Boot, hid_boot_protocol: HidBootProtocol::Keyboard, }; let hid = HidReaderWriter::<_, 1, 8>::new(&mut builder, &mut state, config); diff --git a/examples/nrf52840/src/bin/usb_hid_mouse.rs b/examples/nrf52840/src/bin/usb_hid_mouse.rs index 4baf2e814..efc28203c 100644 --- a/examples/nrf52840/src/bin/usb_hid_mouse.rs +++ b/examples/nrf52840/src/bin/usb_hid_mouse.rs @@ -73,7 +73,7 @@ async fn main(_spawner: Spawner) { request_handler: Some(&mut request_handler), poll_ms: 60, max_packet_size: 8, - hid_subclass: HidSubclass::ReportOrBoot, + hid_subclass: HidSubclass::Boot, hid_boot_protocol: HidBootProtocol::Mouse, }; diff --git a/examples/rp/src/bin/usb_hid_keyboard.rs b/examples/rp/src/bin/usb_hid_keyboard.rs index fa78d0c2e..adf91439e 100644 --- a/examples/rp/src/bin/usb_hid_keyboard.rs +++ b/examples/rp/src/bin/usb_hid_keyboard.rs @@ -67,7 +67,7 @@ async fn main(_spawner: Spawner) { request_handler: None, poll_ms: 60, max_packet_size: 64, - hid_subclass: HidSubclass::ReportOrBoot, + hid_subclass: HidSubclass::Boot, hid_boot_protocol: HidBootProtocol::Keyboard, }; let hid = HidReaderWriter::<_, 1, 8>::new(&mut builder, &mut state, config); diff --git a/examples/rp/src/bin/usb_hid_mouse.rs b/examples/rp/src/bin/usb_hid_mouse.rs index 100e6048a..3e62e8891 100755 --- a/examples/rp/src/bin/usb_hid_mouse.rs +++ b/examples/rp/src/bin/usb_hid_mouse.rs @@ -69,7 +69,7 @@ async fn main(_spawner: Spawner) { request_handler: None, poll_ms: 60, max_packet_size: 64, - hid_subclass: HidSubclass::ReportOrBoot, + hid_subclass: HidSubclass::Boot, hid_boot_protocol: HidBootProtocol::Mouse, }; let hid = HidReaderWriter::<_, 1, 8>::new(&mut builder, &mut state, config); diff --git a/examples/rp235x/src/bin/usb_hid_keyboard.rs b/examples/rp235x/src/bin/usb_hid_keyboard.rs index 3203176cb..b740a07b3 100644 --- a/examples/rp235x/src/bin/usb_hid_keyboard.rs +++ b/examples/rp235x/src/bin/usb_hid_keyboard.rs @@ -69,7 +69,7 @@ async fn main(_spawner: Spawner) { request_handler: None, poll_ms: 60, max_packet_size: 64, - hid_subclass: HidSubclass::ReportOrBoot, + hid_subclass: HidSubclass::Boot, hid_boot_protocol: HidBootProtocol::Keyboard, }; let hid = HidReaderWriter::<_, 1, 8>::new(&mut builder, &mut state, config); diff --git a/examples/stm32f4/src/bin/usb_hid_keyboard.rs b/examples/stm32f4/src/bin/usb_hid_keyboard.rs index 740fbcaef..5521a8240 100644 --- a/examples/stm32f4/src/bin/usb_hid_keyboard.rs +++ b/examples/stm32f4/src/bin/usb_hid_keyboard.rs @@ -105,7 +105,7 @@ async fn main(_spawner: Spawner) { request_handler: None, poll_ms: 60, max_packet_size: 8, - hid_subclass: HidSubclass::ReportOrBoot, + hid_subclass: HidSubclass::Boot, hid_boot_protocol: HidBootProtocol::Keyboard, }; diff --git a/examples/stm32f4/src/bin/usb_hid_mouse.rs b/examples/stm32f4/src/bin/usb_hid_mouse.rs index 09af204c4..5cfa0aec4 100644 --- a/examples/stm32f4/src/bin/usb_hid_mouse.rs +++ b/examples/stm32f4/src/bin/usb_hid_mouse.rs @@ -95,7 +95,7 @@ async fn main(_spawner: Spawner) { request_handler: Some(&mut request_handler), poll_ms: 60, max_packet_size: 8, - hid_subclass: HidSubclass::ReportOrBoot, + hid_subclass: HidSubclass::Boot, hid_boot_protocol: HidBootProtocol::Mouse, }; diff --git a/examples/stm32l5/src/bin/usb_hid_mouse.rs b/examples/stm32l5/src/bin/usb_hid_mouse.rs index 30dbd2698..f64fde3cb 100644 --- a/examples/stm32l5/src/bin/usb_hid_mouse.rs +++ b/examples/stm32l5/src/bin/usb_hid_mouse.rs @@ -77,7 +77,7 @@ async fn main(_spawner: Spawner) { request_handler: Some(&mut request_handler), poll_ms: 60, max_packet_size: 8, - hid_subclass: HidSubclass::ReportOrBoot, + hid_subclass: HidSubclass::Boot, hid_boot_protocol: HidBootProtocol::Mouse, }; -- cgit From 176649e71ad442ca9856af6c11989b0b2f228c4b Mon Sep 17 00:00:00 2001 From: matteo Date: Wed, 1 Oct 2025 18:56:38 +0200 Subject: update hid mouse and keyboard examples --- examples/rp/src/bin/usb_hid_keyboard.rs | 80 +++++++++++++++++++--------- examples/rp/src/bin/usb_hid_mouse.rs | 54 ++++++++++++++----- examples/rp235x/src/bin/usb_hid_keyboard.rs | 77 +++++++++++++++++--------- examples/stm32f4/src/bin/usb_hid_keyboard.rs | 78 ++++++++++++++++++--------- examples/stm32f4/src/bin/usb_hid_mouse.rs | 50 +++++++++++++---- examples/stm32l5/src/bin/usb_hid_mouse.rs | 50 +++++++++++++---- 6 files changed, 282 insertions(+), 107 deletions(-) diff --git a/examples/rp/src/bin/usb_hid_keyboard.rs b/examples/rp/src/bin/usb_hid_keyboard.rs index adf91439e..2f6d169bf 100644 --- a/examples/rp/src/bin/usb_hid_keyboard.rs +++ b/examples/rp/src/bin/usb_hid_keyboard.rs @@ -1,7 +1,7 @@ #![no_std] #![no_main] -use core::sync::atomic::{AtomicBool, Ordering}; +use core::sync::atomic::{AtomicBool, AtomicU8, Ordering}; use defmt::*; use embassy_executor::Spawner; @@ -10,7 +10,9 @@ use embassy_rp::bind_interrupts; use embassy_rp::gpio::{Input, Pull}; use embassy_rp::peripherals::USB; use embassy_rp::usb::{Driver, InterruptHandler}; -use embassy_usb::class::hid::{HidBootProtocol, HidReaderWriter, HidSubclass, ReportId, RequestHandler, State}; +use embassy_usb::class::hid::{ + HidBootProtocol, HidProtocolMode, HidReaderWriter, HidSubclass, ReportId, RequestHandler, State, +}; use embassy_usb::control::OutResponse; use embassy_usb::{Builder, Config, Handler}; use usbd_hid::descriptor::{KeyboardReport, SerializedDescriptor}; @@ -20,6 +22,8 @@ bind_interrupts!(struct Irqs { USBCTRL_IRQ => InterruptHandler; }); +static HID_PROTOCOL_MODE: AtomicU8 = AtomicU8::new(HidProtocolMode::Boot as u8); + #[embassy_executor::main] async fn main(_spawner: Spawner) { let p = embassy_rp::init(Default::default()); @@ -92,30 +96,46 @@ async fn main(_spawner: Spawner) { info!("Waiting for HIGH on pin 16"); signal_pin.wait_for_high().await; info!("HIGH DETECTED"); - // Create a report with the A key pressed. (no shift modifier) - let report = KeyboardReport { - keycodes: [4, 0, 0, 0, 0, 0], - leds: 0, - modifier: 0, - reserved: 0, - }; - // Send the report. - match writer.write_serialize(&report).await { - Ok(()) => {} - Err(e) => warn!("Failed to send report: {:?}", e), - }; + + if HID_PROTOCOL_MODE.load(Ordering::Relaxed) == HidProtocolMode::Boot as u8 { + match writer.write(&[0, 0, 4, 0, 0, 0, 0, 0]).await { + Ok(()) => {} + Err(e) => warn!("Failed to send boot report: {:?}", e), + }; + } else { + // Create a report with the A key pressed. (no shift modifier) + let report = KeyboardReport { + keycodes: [4, 0, 0, 0, 0, 0], + leds: 0, + modifier: 0, + reserved: 0, + }; + // Send the report. + match writer.write_serialize(&report).await { + Ok(()) => {} + Err(e) => warn!("Failed to send report: {:?}", e), + }; + } + signal_pin.wait_for_low().await; info!("LOW DETECTED"); - let report = KeyboardReport { - keycodes: [0, 0, 0, 0, 0, 0], - leds: 0, - modifier: 0, - reserved: 0, - }; - match writer.write_serialize(&report).await { - Ok(()) => {} - Err(e) => warn!("Failed to send report: {:?}", e), - }; + if HID_PROTOCOL_MODE.load(Ordering::Relaxed) == HidProtocolMode::Boot as u8 { + match writer.write(&[0, 0, 0, 0, 0, 0, 0, 0]).await { + Ok(()) => {} + Err(e) => warn!("Failed to send boot report: {:?}", e), + }; + } else { + let report = KeyboardReport { + keycodes: [0, 0, 0, 0, 0, 0], + leds: 0, + modifier: 0, + reserved: 0, + }; + match writer.write_serialize(&report).await { + Ok(()) => {} + Err(e) => warn!("Failed to send report: {:?}", e), + }; + } } }; @@ -141,6 +161,18 @@ impl RequestHandler for MyRequestHandler { OutResponse::Accepted } + fn get_protocol(&self) -> HidProtocolMode { + let protocol = HidProtocolMode::from(HID_PROTOCOL_MODE.load(Ordering::Relaxed)); + info!("The current HID protocol mode is: {}", protocol); + protocol + } + + fn set_protocol(&mut self, protocol: HidProtocolMode) -> OutResponse { + info!("Switching to HID protocol mode: {}", protocol); + HID_PROTOCOL_MODE.store(protocol as u8, Ordering::Relaxed); + OutResponse::Accepted + } + fn set_idle_ms(&mut self, id: Option, dur: u32) { info!("Set idle rate for {:?} to {:?}", id, dur); } diff --git a/examples/rp/src/bin/usb_hid_mouse.rs b/examples/rp/src/bin/usb_hid_mouse.rs index 3e62e8891..dc331cbdd 100755 --- a/examples/rp/src/bin/usb_hid_mouse.rs +++ b/examples/rp/src/bin/usb_hid_mouse.rs @@ -1,7 +1,7 @@ #![no_std] #![no_main] -use core::sync::atomic::{AtomicBool, Ordering}; +use core::sync::atomic::{AtomicBool, AtomicU8, Ordering}; use defmt::*; use embassy_executor::Spawner; @@ -11,7 +11,9 @@ use embassy_rp::clocks::RoscRng; use embassy_rp::peripherals::USB; use embassy_rp::usb::{Driver, InterruptHandler}; use embassy_time::Timer; -use embassy_usb::class::hid::{HidBootProtocol, HidReaderWriter, HidSubclass, ReportId, RequestHandler, State}; +use embassy_usb::class::hid::{ + HidBootProtocol, HidProtocolMode, HidReaderWriter, HidSubclass, ReportId, RequestHandler, State, +}; use embassy_usb::control::OutResponse; use embassy_usb::{Builder, Config, Handler}; use rand::Rng; @@ -22,6 +24,8 @@ bind_interrupts!(struct Irqs { USBCTRL_IRQ => InterruptHandler; }); +static HID_PROTOCOL_MODE: AtomicU8 = AtomicU8::new(HidProtocolMode::Boot as u8); + #[embassy_executor::main] async fn main(_spawner: Spawner) { let p = embassy_rp::init(Default::default()); @@ -89,17 +93,29 @@ async fn main(_spawner: Spawner) { loop { // every 1 second _ = Timer::after_secs(1).await; - let report = MouseReport { - buttons: 0, - x: rng.random_range(-100..100), // random small x movement - y: rng.random_range(-100..100), // random small y movement - wheel: 0, - pan: 0, - }; - // Send the report. - match writer.write_serialize(&report).await { - Ok(()) => {} - Err(e) => warn!("Failed to send report: {:?}", e), + + let x = rng.random_range(-100..100); // random small x movement + let y = rng.random_range(-100..100); // random small y movement + + if HID_PROTOCOL_MODE.load(Ordering::Relaxed) == HidProtocolMode::Boot as u8 { + let buttons = 0u8; + match writer.write(&[buttons, x as u8, y as u8]).await { + Ok(()) => {} + Err(e) => warn!("Failed to send boot report: {:?}", e), + } + } else { + let report = MouseReport { + buttons: 0, + x, + y, + wheel: 0, + pan: 0, + }; + // Send the report. + match writer.write_serialize(&report).await { + Ok(()) => {} + Err(e) => warn!("Failed to send report: {:?}", e), + } } } }; @@ -126,6 +142,18 @@ impl RequestHandler for MyRequestHandler { OutResponse::Accepted } + fn get_protocol(&self) -> HidProtocolMode { + let protocol = HidProtocolMode::from(HID_PROTOCOL_MODE.load(Ordering::Relaxed)); + info!("The current HID protocol mode is: {}", protocol); + protocol + } + + fn set_protocol(&mut self, protocol: HidProtocolMode) -> OutResponse { + info!("Switching to HID protocol mode: {}", protocol); + HID_PROTOCOL_MODE.store(protocol as u8, Ordering::Relaxed); + OutResponse::Accepted + } + fn set_idle_ms(&mut self, id: Option, dur: u32) { info!("Set idle rate for {:?} to {:?}", id, dur); } diff --git a/examples/rp235x/src/bin/usb_hid_keyboard.rs b/examples/rp235x/src/bin/usb_hid_keyboard.rs index b740a07b3..d8f64c470 100644 --- a/examples/rp235x/src/bin/usb_hid_keyboard.rs +++ b/examples/rp235x/src/bin/usb_hid_keyboard.rs @@ -1,7 +1,7 @@ #![no_std] #![no_main] -use core::sync::atomic::{AtomicBool, Ordering}; +use core::sync::atomic::{AtomicBool, AtomicU8, Ordering}; use defmt::*; use embassy_executor::Spawner; @@ -11,7 +11,7 @@ use embassy_rp::gpio::{Input, Pull}; use embassy_rp::peripherals::USB; use embassy_rp::usb::{Driver as UsbDriver, InterruptHandler}; use embassy_usb::class::hid::{ - HidBootProtocol, HidReaderWriter, HidSubclass, ReportId, RequestHandler, State as HidState, + HidBootProtocol, HidProtocolMode, HidReaderWriter, HidSubclass, ReportId, RequestHandler, State as HidState, }; use embassy_usb::control::OutResponse; use embassy_usb::{Builder, Config, Handler}; @@ -22,6 +22,8 @@ bind_interrupts!(struct Irqs { USBCTRL_IRQ => InterruptHandler; }); +static HID_PROTOCOL_MODE: AtomicU8 = AtomicU8::new(HidProtocolMode::Boot as u8); + #[embassy_executor::main] async fn main(_spawner: Spawner) { let p = embassy_rp::init(Default::default()); @@ -94,30 +96,45 @@ async fn main(_spawner: Spawner) { info!("Waiting for HIGH on pin 16"); signal_pin.wait_for_high().await; info!("HIGH DETECTED"); - // Create a report with the A key pressed. (no shift modifier) - let report = KeyboardReport { - keycodes: [4, 0, 0, 0, 0, 0], - leds: 0, - modifier: 0, - reserved: 0, - }; - // Send the report. - match writer.write_serialize(&report).await { - Ok(()) => {} - Err(e) => warn!("Failed to send report: {:?}", e), - }; + + if HID_PROTOCOL_MODE.load(Ordering::Relaxed) == HidProtocolMode::Boot as u8 { + match writer.write(&[0, 0, 4, 0, 0, 0, 0, 0]).await { + Ok(()) => {} + Err(e) => warn!("Failed to send boot report: {:?}", e), + }; + } else { + // Create a report with the A key pressed. (no shift modifier) + let report = KeyboardReport { + keycodes: [4, 0, 0, 0, 0, 0], + leds: 0, + modifier: 0, + reserved: 0, + }; + // Send the report. + match writer.write_serialize(&report).await { + Ok(()) => {} + Err(e) => warn!("Failed to send report: {:?}", e), + }; + } signal_pin.wait_for_low().await; info!("LOW DETECTED"); - let report = KeyboardReport { - keycodes: [0, 0, 0, 0, 0, 0], - leds: 0, - modifier: 0, - reserved: 0, - }; - match writer.write_serialize(&report).await { - Ok(()) => {} - Err(e) => warn!("Failed to send report: {:?}", e), - }; + if HID_PROTOCOL_MODE.load(Ordering::Relaxed) == HidProtocolMode::Boot as u8 { + match writer.write(&[0, 0, 0, 0, 0, 0, 0, 0]).await { + Ok(()) => {} + Err(e) => warn!("Failed to send boot report: {:?}", e), + }; + } else { + let report = KeyboardReport { + keycodes: [0, 0, 0, 0, 0, 0], + leds: 0, + modifier: 0, + reserved: 0, + }; + match writer.write_serialize(&report).await { + Ok(()) => {} + Err(e) => warn!("Failed to send report: {:?}", e), + }; + } } }; @@ -143,6 +160,18 @@ impl RequestHandler for MyRequestHandler { OutResponse::Accepted } + fn get_protocol(&self) -> HidProtocolMode { + let protocol = HidProtocolMode::from(HID_PROTOCOL_MODE.load(Ordering::Relaxed)); + info!("The current HID protocol mode is: {}", protocol); + protocol + } + + fn set_protocol(&mut self, protocol: HidProtocolMode) -> OutResponse { + info!("Switching to HID protocol mode: {}", protocol); + HID_PROTOCOL_MODE.store(protocol as u8, Ordering::Relaxed); + OutResponse::Accepted + } + fn set_idle_ms(&mut self, id: Option, dur: u32) { info!("Set idle rate for {:?} to {:?}", id, dur); } diff --git a/examples/stm32f4/src/bin/usb_hid_keyboard.rs b/examples/stm32f4/src/bin/usb_hid_keyboard.rs index 5521a8240..86b6fa95f 100644 --- a/examples/stm32f4/src/bin/usb_hid_keyboard.rs +++ b/examples/stm32f4/src/bin/usb_hid_keyboard.rs @@ -1,7 +1,7 @@ #![no_std] #![no_main] -use core::sync::atomic::{AtomicBool, Ordering}; +use core::sync::atomic::{AtomicBool, AtomicU8, Ordering}; use defmt::*; use embassy_executor::Spawner; @@ -11,7 +11,9 @@ use embassy_stm32::gpio::Pull; use embassy_stm32::time::Hertz; use embassy_stm32::usb::Driver; use embassy_stm32::{bind_interrupts, peripherals, usb, Config}; -use embassy_usb::class::hid::{HidBootProtocol, HidReaderWriter, HidSubclass, ReportId, RequestHandler, State}; +use embassy_usb::class::hid::{ + HidBootProtocol, HidProtocolMode, HidReaderWriter, HidSubclass, ReportId, RequestHandler, State, +}; use embassy_usb::control::OutResponse; use embassy_usb::{Builder, Handler}; use usbd_hid::descriptor::{KeyboardReport, SerializedDescriptor}; @@ -21,6 +23,8 @@ bind_interrupts!(struct Irqs { OTG_FS => usb::InterruptHandler; }); +static HID_PROTOCOL_MODE: AtomicU8 = AtomicU8::new(HidProtocolMode::Boot as u8); + // If you are trying this and your USB device doesn't connect, the most // common issues are the RCC config and vbus_detection // @@ -127,32 +131,46 @@ async fn main(_spawner: Spawner) { button.wait_for_rising_edge().await; // signal_pin.wait_for_high().await; info!("Button pressed!"); - // Create a report with the A key pressed. (no shift modifier) - let report = KeyboardReport { - keycodes: [4, 0, 0, 0, 0, 0], - leds: 0, - modifier: 0, - reserved: 0, - }; - // Send the report. - match writer.write_serialize(&report).await { - Ok(()) => {} - Err(e) => warn!("Failed to send report: {:?}", e), - }; + if HID_PROTOCOL_MODE.load(Ordering::Relaxed) == HidProtocolMode::Boot as u8 { + match writer.write(&[0, 0, 4, 0, 0, 0, 0, 0]).await { + Ok(()) => {} + Err(e) => warn!("Failed to send boot report: {:?}", e), + }; + } else { + // Create a report with the A key pressed. (no shift modifier) + let report = KeyboardReport { + keycodes: [4, 0, 0, 0, 0, 0], + leds: 0, + modifier: 0, + reserved: 0, + }; + // Send the report. + match writer.write_serialize(&report).await { + Ok(()) => {} + Err(e) => warn!("Failed to send report: {:?}", e), + }; + } button.wait_for_falling_edge().await; // signal_pin.wait_for_low().await; info!("Button released!"); - let report = KeyboardReport { - keycodes: [0, 0, 0, 0, 0, 0], - leds: 0, - modifier: 0, - reserved: 0, - }; - match writer.write_serialize(&report).await { - Ok(()) => {} - Err(e) => warn!("Failed to send report: {:?}", e), - }; + if HID_PROTOCOL_MODE.load(Ordering::Relaxed) == HidProtocolMode::Boot as u8 { + match writer.write(&[0, 0, 0, 0, 0, 0, 0, 0]).await { + Ok(()) => {} + Err(e) => warn!("Failed to send boot report: {:?}", e), + }; + } else { + let report = KeyboardReport { + keycodes: [0, 0, 0, 0, 0, 0], + leds: 0, + modifier: 0, + reserved: 0, + }; + match writer.write_serialize(&report).await { + Ok(()) => {} + Err(e) => warn!("Failed to send report: {:?}", e), + }; + } } }; @@ -178,6 +196,18 @@ impl RequestHandler for MyRequestHandler { OutResponse::Accepted } + fn get_protocol(&self) -> HidProtocolMode { + let protocol = HidProtocolMode::from(HID_PROTOCOL_MODE.load(Ordering::Relaxed)); + info!("The current HID protocol mode is: {}", protocol); + protocol + } + + fn set_protocol(&mut self, protocol: HidProtocolMode) -> OutResponse { + info!("Switching to HID protocol mode: {}", protocol); + HID_PROTOCOL_MODE.store(protocol as u8, Ordering::Relaxed); + OutResponse::Accepted + } + fn set_idle_ms(&mut self, id: Option, dur: u32) { info!("Set idle rate for {:?} to {:?}", id, dur); } diff --git a/examples/stm32f4/src/bin/usb_hid_mouse.rs b/examples/stm32f4/src/bin/usb_hid_mouse.rs index 5cfa0aec4..977db4c15 100644 --- a/examples/stm32f4/src/bin/usb_hid_mouse.rs +++ b/examples/stm32f4/src/bin/usb_hid_mouse.rs @@ -1,6 +1,8 @@ #![no_std] #![no_main] +use core::sync::atomic::{AtomicU8, Ordering}; + use defmt::*; use embassy_executor::Spawner; use embassy_futures::join::join; @@ -8,7 +10,9 @@ use embassy_stm32::time::Hertz; use embassy_stm32::usb::Driver; use embassy_stm32::{bind_interrupts, peripherals, usb, Config}; use embassy_time::Timer; -use embassy_usb::class::hid::{HidBootProtocol, HidSubclass, HidWriter, ReportId, RequestHandler, State}; +use embassy_usb::class::hid::{ + HidBootProtocol, HidProtocolMode, HidSubclass, HidWriter, ReportId, RequestHandler, State, +}; use embassy_usb::control::OutResponse; use embassy_usb::Builder; use usbd_hid::descriptor::{MouseReport, SerializedDescriptor}; @@ -18,6 +22,8 @@ bind_interrupts!(struct Irqs { OTG_FS => usb::InterruptHandler; }); +static HID_PROTOCOL_MODE: AtomicU8 = AtomicU8::new(HidProtocolMode::Boot as u8); + // If you are trying this and your USB device doesn't connect, the most // common issues are the RCC config and vbus_detection // @@ -114,16 +120,26 @@ async fn main(_spawner: Spawner) { Timer::after_millis(500).await; y = -y; - let report = MouseReport { - buttons: 0, - x: 0, - y, - wheel: 0, - pan: 0, - }; - match writer.write_serialize(&report).await { - Ok(()) => {} - Err(e) => warn!("Failed to send report: {:?}", e), + + if HID_PROTOCOL_MODE.load(Ordering::Relaxed) == HidProtocolMode::Boot as u8 { + let buttons = 0u8; + let x = 0i8; + match writer.write(&[buttons, x as u8, y as u8]).await { + Ok(()) => {} + Err(e) => warn!("Failed to send boot report: {:?}", e), + } + } else { + let report = MouseReport { + buttons: 0, + x: 0, + y, + wheel: 0, + pan: 0, + }; + match writer.write_serialize(&report).await { + Ok(()) => {} + Err(e) => warn!("Failed to send report: {:?}", e), + } } } }; @@ -146,6 +162,18 @@ impl RequestHandler for MyRequestHandler { OutResponse::Accepted } + fn get_protocol(&self) -> HidProtocolMode { + let protocol = HidProtocolMode::from(HID_PROTOCOL_MODE.load(Ordering::Relaxed)); + info!("The current HID protocol mode is: {}", protocol); + protocol + } + + fn set_protocol(&mut self, protocol: HidProtocolMode) -> OutResponse { + info!("Switching to HID protocol mode: {}", protocol); + HID_PROTOCOL_MODE.store(protocol as u8, Ordering::Relaxed); + OutResponse::Accepted + } + fn set_idle_ms(&mut self, id: Option, dur: u32) { info!("Set idle rate for {:?} to {:?}", id, dur); } diff --git a/examples/stm32l5/src/bin/usb_hid_mouse.rs b/examples/stm32l5/src/bin/usb_hid_mouse.rs index f64fde3cb..8c7cdbef5 100644 --- a/examples/stm32l5/src/bin/usb_hid_mouse.rs +++ b/examples/stm32l5/src/bin/usb_hid_mouse.rs @@ -1,13 +1,17 @@ #![no_std] #![no_main] +use core::sync::atomic::{AtomicU8, Ordering}; + use defmt::*; use embassy_executor::Spawner; use embassy_futures::join::join; use embassy_stm32::usb::Driver; use embassy_stm32::{bind_interrupts, peripherals, usb, Config}; use embassy_time::Timer; -use embassy_usb::class::hid::{HidBootProtocol, HidSubclass, HidWriter, ReportId, RequestHandler, State}; +use embassy_usb::class::hid::{ + HidBootProtocol, HidProtocolMode, HidSubclass, HidWriter, ReportId, RequestHandler, State, +}; use embassy_usb::control::OutResponse; use embassy_usb::Builder; use usbd_hid::descriptor::{MouseReport, SerializedDescriptor}; @@ -17,6 +21,8 @@ bind_interrupts!(struct Irqs { USB_FS => usb::InterruptHandler; }); +static HID_PROTOCOL_MODE: AtomicU8 = AtomicU8::new(HidProtocolMode::Boot as u8); + #[embassy_executor::main] async fn main(_spawner: Spawner) { let mut config = Config::default(); @@ -96,16 +102,26 @@ async fn main(_spawner: Spawner) { Timer::after_millis(500).await; y = -y; - let report = MouseReport { - buttons: 0, - x: 0, - y, - wheel: 0, - pan: 0, - }; - match writer.write_serialize(&report).await { - Ok(()) => {} - Err(e) => warn!("Failed to send report: {:?}", e), + + if HID_PROTOCOL_MODE.load(Ordering::Relaxed) == HidProtocolMode::Boot as u8 { + let buttons = 0u8; + let x = 0i8; + match writer.write(&[buttons, x as u8, y as u8]).await { + Ok(()) => {} + Err(e) => warn!("Failed to send boot report: {:?}", e), + } + } else { + let report = MouseReport { + buttons: 0, + x: 0, + y, + wheel: 0, + pan: 0, + }; + match writer.write_serialize(&report).await { + Ok(()) => {} + Err(e) => warn!("Failed to send report: {:?}", e), + } } } }; @@ -128,6 +144,18 @@ impl RequestHandler for MyRequestHandler { OutResponse::Accepted } + fn get_protocol(&self) -> HidProtocolMode { + let protocol = HidProtocolMode::from(HID_PROTOCOL_MODE.load(Ordering::Relaxed)); + info!("The current HID protocol mode is: {}", protocol); + protocol + } + + fn set_protocol(&mut self, protocol: HidProtocolMode) -> OutResponse { + info!("Switching to HID protocol mode: {}", protocol); + HID_PROTOCOL_MODE.store(protocol as u8, Ordering::Relaxed); + OutResponse::Accepted + } + fn set_idle_ms(&mut self, id: Option, dur: u32) { info!("Set idle rate for {:?} to {:?}", id, dur); } -- cgit From 617dd353637d5d99e47c357f9115f6f96144ed6b Mon Sep 17 00:00:00 2001 From: Brian Schwind Date: Sat, 4 Oct 2025 13:04:42 +0900 Subject: ospi: use a named enum variant in place of a literal zero --- embassy-stm32/src/ospi/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/embassy-stm32/src/ospi/mod.rs b/embassy-stm32/src/ospi/mod.rs index d93cecb69..76e6b46eb 100644 --- a/embassy-stm32/src/ospi/mod.rs +++ b/embassy-stm32/src/ospi/mod.rs @@ -451,7 +451,7 @@ impl<'d, T: Instance, M: PeriMode> Ospi<'d, T, M> { } T::REGS.cr().modify(|w| { - w.set_fmode(0.into()); + w.set_fmode(vals::FunctionalMode::INDIRECT_WRITE); }); // Configure alternate bytes -- cgit From 5e89631367c88e636899ddc3b3d333c92d0a983e Mon Sep 17 00:00:00 2001 From: Brian Schwind Date: Sat, 4 Oct 2025 13:05:26 +0900 Subject: ospi: properly configure the transfer size --- embassy-stm32/src/ospi/mod.rs | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/embassy-stm32/src/ospi/mod.rs b/embassy-stm32/src/ospi/mod.rs index 76e6b46eb..a1f3c8b03 100644 --- a/embassy-stm32/src/ospi/mod.rs +++ b/embassy-stm32/src/ospi/mod.rs @@ -577,7 +577,8 @@ impl<'d, T: Instance, M: PeriMode> Ospi<'d, T, M> { w.set_dmaen(false); }); - self.configure_command(&transaction, Some(buf.len()))?; + let transfer_size_bytes = buf.len() * W::size().bytes(); + self.configure_command(&transaction, Some(transfer_size_bytes))?; let current_address = T::REGS.ar().read().address(); let current_instruction = T::REGS.ir().read().instruction(); @@ -616,7 +617,8 @@ impl<'d, T: Instance, M: PeriMode> Ospi<'d, T, M> { w.set_dmaen(false); }); - self.configure_command(&transaction, Some(buf.len()))?; + let transfer_size_bytes = buf.len() * W::size().bytes(); + self.configure_command(&transaction, Some(transfer_size_bytes))?; T::REGS .cr() @@ -1153,7 +1155,8 @@ impl<'d, T: Instance> Ospi<'d, T, Async> { // Wait for peripheral to be free while T::REGS.sr().read().busy() {} - self.configure_command(&transaction, Some(buf.len()))?; + let transfer_size_bytes = buf.len() * W::size().bytes(); + self.configure_command(&transaction, Some(transfer_size_bytes))?; let current_address = T::REGS.ar().read().address(); let current_instruction = T::REGS.ir().read().instruction(); @@ -1193,7 +1196,8 @@ impl<'d, T: Instance> Ospi<'d, T, Async> { // Wait for peripheral to be free while T::REGS.sr().read().busy() {} - self.configure_command(&transaction, Some(buf.len()))?; + let transfer_size_bytes = buf.len() * W::size().bytes(); + self.configure_command(&transaction, Some(transfer_size_bytes))?; T::REGS .cr() .modify(|v| v.set_fmode(vals::FunctionalMode::INDIRECT_WRITE)); @@ -1226,7 +1230,8 @@ impl<'d, T: Instance> Ospi<'d, T, Async> { // Wait for peripheral to be free while T::REGS.sr().read().busy() {} - self.configure_command(&transaction, Some(buf.len()))?; + let transfer_size_bytes = buf.len() * W::size().bytes(); + self.configure_command(&transaction, Some(transfer_size_bytes))?; let current_address = T::REGS.ar().read().address(); let current_instruction = T::REGS.ir().read().instruction(); @@ -1266,7 +1271,8 @@ impl<'d, T: Instance> Ospi<'d, T, Async> { // Wait for peripheral to be free while T::REGS.sr().read().busy() {} - self.configure_command(&transaction, Some(buf.len()))?; + let transfer_size_bytes = buf.len() * W::size().bytes(); + self.configure_command(&transaction, Some(transfer_size_bytes))?; T::REGS .cr() .modify(|v| v.set_fmode(vals::FunctionalMode::INDIRECT_WRITE)); -- cgit From 5220a76e5f71c4e44c1e2f023df5ea7feb4d4370 Mon Sep 17 00:00:00 2001 From: Brian Schwind Date: Sat, 4 Oct 2025 13:13:16 +0900 Subject: ospi: properly respect the max DMA transfer size when writing --- embassy-stm32/src/ospi/mod.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/embassy-stm32/src/ospi/mod.rs b/embassy-stm32/src/ospi/mod.rs index a1f3c8b03..2e4943a1b 100644 --- a/embassy-stm32/src/ospi/mod.rs +++ b/embassy-stm32/src/ospi/mod.rs @@ -1203,7 +1203,7 @@ impl<'d, T: Instance> Ospi<'d, T, Async> { .modify(|v| v.set_fmode(vals::FunctionalMode::INDIRECT_WRITE)); // TODO: implement this using a LinkedList DMA to offload the whole transfer off the CPU. - for chunk in buf.chunks(0xFFFF) { + for chunk in buf.chunks(0xFFFF / W::size().bytes()) { let transfer = unsafe { self.dma .as_mut() @@ -1278,7 +1278,7 @@ impl<'d, T: Instance> Ospi<'d, T, Async> { .modify(|v| v.set_fmode(vals::FunctionalMode::INDIRECT_WRITE)); // TODO: implement this using a LinkedList DMA to offload the whole transfer off the CPU. - for chunk in buf.chunks(0xFFFF) { + for chunk in buf.chunks(0xFFFF / W::size().bytes()) { let transfer = unsafe { self.dma .as_mut() -- cgit From ed527e659e0e5d729f9d0ee2f6f15019a144b68a Mon Sep 17 00:00:00 2001 From: Brian Schwind Date: Sat, 4 Oct 2025 13:18:10 +0900 Subject: ospi: properly respect the max DMA transfer size when reading --- embassy-stm32/src/ospi/mod.rs | 36 ++++++++++++++++++++---------------- 1 file changed, 20 insertions(+), 16 deletions(-) diff --git a/embassy-stm32/src/ospi/mod.rs b/embassy-stm32/src/ospi/mod.rs index 2e4943a1b..dbcf07469 100644 --- a/embassy-stm32/src/ospi/mod.rs +++ b/embassy-stm32/src/ospi/mod.rs @@ -1171,16 +1171,18 @@ impl<'d, T: Instance> Ospi<'d, T, Async> { T::REGS.ar().write(|v| v.set_address(current_address)); } - let transfer = unsafe { - self.dma - .as_mut() - .unwrap() - .read(T::REGS.dr().as_ptr() as *mut W, buf, Default::default()) - }; + for chunk in buf.chunks_mut(0xFFFF / W::size().bytes()) { + let transfer = unsafe { + self.dma + .as_mut() + .unwrap() + .read(T::REGS.dr().as_ptr() as *mut W, chunk, Default::default()) + }; - T::REGS.cr().modify(|w| w.set_dmaen(true)); + T::REGS.cr().modify(|w| w.set_dmaen(true)); - transfer.blocking_wait(); + transfer.blocking_wait(); + } finish_dma(T::REGS); @@ -1246,16 +1248,18 @@ impl<'d, T: Instance> Ospi<'d, T, Async> { T::REGS.ar().write(|v| v.set_address(current_address)); } - let transfer = unsafe { - self.dma - .as_mut() - .unwrap() - .read(T::REGS.dr().as_ptr() as *mut W, buf, Default::default()) - }; + for chunk in buf.chunks_mut(0xFFFF / W::size().bytes()) { + let transfer = unsafe { + self.dma + .as_mut() + .unwrap() + .read(T::REGS.dr().as_ptr() as *mut W, chunk, Default::default()) + }; - T::REGS.cr().modify(|w| w.set_dmaen(true)); + T::REGS.cr().modify(|w| w.set_dmaen(true)); - transfer.await; + transfer.await; + } finish_dma(T::REGS); -- cgit From 2ecb06bf9b7d59ec85793228fefa01c70f3c432c Mon Sep 17 00:00:00 2001 From: Brian Schwind Date: Sat, 4 Oct 2025 13:21:55 +0900 Subject: hspi: use a named enum variant in place of a literal zero --- embassy-stm32/src/hspi/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/embassy-stm32/src/hspi/mod.rs b/embassy-stm32/src/hspi/mod.rs index 95d9e5099..277f69496 100644 --- a/embassy-stm32/src/hspi/mod.rs +++ b/embassy-stm32/src/hspi/mod.rs @@ -391,7 +391,7 @@ impl<'d, T: Instance, M: PeriMode> Hspi<'d, T, M> { while T::REGS.sr().read().busy() {} T::REGS.cr().modify(|w| { - w.set_fmode(0.into()); + w.set_fmode(FunctionalMode::IndirectWrite.into()); }); // Configure alternate bytes -- cgit From 7e3ca6067be7e2361ccd75f9712a0d08fa7d2d6a Mon Sep 17 00:00:00 2001 From: Brian Schwind Date: Sat, 4 Oct 2025 13:22:35 +0900 Subject: hspi: properly configure the transfer size --- embassy-stm32/src/hspi/mod.rs | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/embassy-stm32/src/hspi/mod.rs b/embassy-stm32/src/hspi/mod.rs index 277f69496..b64a6b62c 100644 --- a/embassy-stm32/src/hspi/mod.rs +++ b/embassy-stm32/src/hspi/mod.rs @@ -498,7 +498,8 @@ impl<'d, T: Instance, M: PeriMode> Hspi<'d, T, M> { w.set_dmaen(false); }); - self.configure_command(&transaction, Some(buf.len()))?; + let transfer_size_bytes = buf.len() * W::size().bytes(); + self.configure_command(&transaction, Some(transfer_size_bytes))?; let current_address = T::REGS.ar().read().address(); let current_instruction = T::REGS.ir().read().instruction(); @@ -537,7 +538,8 @@ impl<'d, T: Instance, M: PeriMode> Hspi<'d, T, M> { w.set_dmaen(false); }); - self.configure_command(&transaction, Some(buf.len()))?; + let transfer_size_bytes = buf.len() * W::size().bytes(); + self.configure_command(&transaction, Some(transfer_size_bytes))?; T::REGS .cr() @@ -767,7 +769,8 @@ impl<'d, T: Instance> Hspi<'d, T, Async> { // Wait for peripheral to be free while T::REGS.sr().read().busy() {} - self.configure_command(&transaction, Some(buf.len()))?; + let transfer_size_bytes = buf.len() * W::size().bytes(); + self.configure_command(&transaction, Some(transfer_size_bytes))?; let current_address = T::REGS.ar().read().address(); let current_instruction = T::REGS.ir().read().instruction(); @@ -807,7 +810,8 @@ impl<'d, T: Instance> Hspi<'d, T, Async> { // Wait for peripheral to be free while T::REGS.sr().read().busy() {} - self.configure_command(&transaction, Some(buf.len()))?; + let transfer_size_bytes = buf.len() * W::size().bytes(); + self.configure_command(&transaction, Some(transfer_size_bytes))?; T::REGS .cr() .modify(|v| v.set_fmode(FunctionalMode::IndirectWrite.into())); @@ -837,7 +841,8 @@ impl<'d, T: Instance> Hspi<'d, T, Async> { // Wait for peripheral to be free while T::REGS.sr().read().busy() {} - self.configure_command(&transaction, Some(buf.len()))?; + let transfer_size_bytes = buf.len() * W::size().bytes(); + self.configure_command(&transaction, Some(transfer_size_bytes))?; let current_address = T::REGS.ar().read().address(); let current_instruction = T::REGS.ir().read().instruction(); @@ -877,7 +882,8 @@ impl<'d, T: Instance> Hspi<'d, T, Async> { // Wait for peripheral to be free while T::REGS.sr().read().busy() {} - self.configure_command(&transaction, Some(buf.len()))?; + let transfer_size_bytes = buf.len() * W::size().bytes(); + self.configure_command(&transaction, Some(transfer_size_bytes))?; T::REGS .cr() .modify(|v| v.set_fmode(FunctionalMode::IndirectWrite.into())); -- cgit From b0a62ddaaaa3e8d89b7af47d904365e6285f97cb Mon Sep 17 00:00:00 2001 From: Brian Schwind Date: Sat, 4 Oct 2025 13:26:11 +0900 Subject: hspi: properly respect the max DMA transfer size when reading and writing --- embassy-stm32/src/hspi/mod.rs | 73 ++++++++++++++++++++++++------------------- 1 file changed, 41 insertions(+), 32 deletions(-) diff --git a/embassy-stm32/src/hspi/mod.rs b/embassy-stm32/src/hspi/mod.rs index b64a6b62c..59dd7ca16 100644 --- a/embassy-stm32/src/hspi/mod.rs +++ b/embassy-stm32/src/hspi/mod.rs @@ -785,16 +785,18 @@ impl<'d, T: Instance> Hspi<'d, T, Async> { T::REGS.ar().write(|v| v.set_address(current_address)); } - let transfer = unsafe { - self.dma - .as_mut() - .unwrap() - .read(T::REGS.dr().as_ptr() as *mut W, buf, Default::default()) - }; + for chunk in buf.chunks_mut(0xFFFF / W::size().bytes()) { + let transfer = unsafe { + self.dma + .as_mut() + .unwrap() + .read(T::REGS.dr().as_ptr() as *mut W, chunk, Default::default()) + }; - T::REGS.cr().modify(|w| w.set_dmaen(true)); + T::REGS.cr().modify(|w| w.set_dmaen(true)); - transfer.blocking_wait(); + transfer.blocking_wait(); + } finish_dma(T::REGS); @@ -816,16 +818,18 @@ impl<'d, T: Instance> Hspi<'d, T, Async> { .cr() .modify(|v| v.set_fmode(FunctionalMode::IndirectWrite.into())); - let transfer = unsafe { - self.dma - .as_mut() - .unwrap() - .write(buf, T::REGS.dr().as_ptr() as *mut W, Default::default()) - }; + for chunk in buf.chunks(0xFFFF / W::size().bytes()) { + let transfer = unsafe { + self.dma + .as_mut() + .unwrap() + .write(chunk, T::REGS.dr().as_ptr() as *mut W, Default::default()) + }; - T::REGS.cr().modify(|w| w.set_dmaen(true)); + T::REGS.cr().modify(|w| w.set_dmaen(true)); - transfer.blocking_wait(); + transfer.blocking_wait(); + } finish_dma(T::REGS); @@ -857,16 +861,18 @@ impl<'d, T: Instance> Hspi<'d, T, Async> { T::REGS.ar().write(|v| v.set_address(current_address)); } - let transfer = unsafe { - self.dma - .as_mut() - .unwrap() - .read(T::REGS.dr().as_ptr() as *mut W, buf, Default::default()) - }; + for chunk in buf.chunks_mut(0xFFFF / W::size().bytes()) { + let transfer = unsafe { + self.dma + .as_mut() + .unwrap() + .read(T::REGS.dr().as_ptr() as *mut W, chunk, Default::default()) + }; - T::REGS.cr().modify(|w| w.set_dmaen(true)); + T::REGS.cr().modify(|w| w.set_dmaen(true)); - transfer.await; + transfer.await; + } finish_dma(T::REGS); @@ -888,16 +894,19 @@ impl<'d, T: Instance> Hspi<'d, T, Async> { .cr() .modify(|v| v.set_fmode(FunctionalMode::IndirectWrite.into())); - let transfer = unsafe { - self.dma - .as_mut() - .unwrap() - .write(buf, T::REGS.dr().as_ptr() as *mut W, Default::default()) - }; + // TODO: implement this using a LinkedList DMA to offload the whole transfer off the CPU. + for chunk in buf.chunks(0xFFFF / W::size().bytes()) { + let transfer = unsafe { + self.dma + .as_mut() + .unwrap() + .write(chunk, T::REGS.dr().as_ptr() as *mut W, Default::default()) + }; - T::REGS.cr().modify(|w| w.set_dmaen(true)); + T::REGS.cr().modify(|w| w.set_dmaen(true)); - transfer.await; + transfer.await; + } finish_dma(T::REGS); -- cgit From 2e46fbf3c94b226ffd07a8c3d0730e138f4f168e Mon Sep 17 00:00:00 2001 From: Brian Schwind Date: Sat, 4 Oct 2025 13:27:37 +0900 Subject: xspi: use a named enum variant in place of a literal zero --- embassy-stm32/src/xspi/mod.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/embassy-stm32/src/xspi/mod.rs b/embassy-stm32/src/xspi/mod.rs index 901569f64..cfc24422a 100644 --- a/embassy-stm32/src/xspi/mod.rs +++ b/embassy-stm32/src/xspi/mod.rs @@ -420,9 +420,9 @@ impl<'d, T: Instance, M: PeriMode> Xspi<'d, T, M> { return Err(XspiError::InvalidCommand); } - T::REGS.cr().modify(|w| { - w.set_fmode(0.into()); - }); + T::REGS + .cr() + .modify(|w| w.set_fmode(Fmode::from_bits(XspiMode::IndirectWrite.into()))); // Configure alternate bytes if let Some(ab) = command.alternate_bytes { -- cgit From f471f72d3173f91ebbc1b6eb797278e7ff988e4e Mon Sep 17 00:00:00 2001 From: Brian Schwind Date: Sat, 4 Oct 2025 13:30:06 +0900 Subject: xspi: properly configure the transfer size --- embassy-stm32/src/xspi/mod.rs | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/embassy-stm32/src/xspi/mod.rs b/embassy-stm32/src/xspi/mod.rs index cfc24422a..1f051bffe 100644 --- a/embassy-stm32/src/xspi/mod.rs +++ b/embassy-stm32/src/xspi/mod.rs @@ -538,8 +538,8 @@ impl<'d, T: Instance, M: PeriMode> Xspi<'d, T, M> { w.set_dmaen(false); }); - // self.configure_command(&transaction, Some(buf.len()))?; - self.configure_command(&transaction, Some(buf.len())).unwrap(); + let transfer_size_bytes = buf.len() * W::size().bytes(); + self.configure_command(&transaction, Some(transfer_size_bytes))?; let current_address = T::REGS.ar().read().address(); let current_instruction = T::REGS.ir().read().instruction(); @@ -578,7 +578,8 @@ impl<'d, T: Instance, M: PeriMode> Xspi<'d, T, M> { w.set_dmaen(false); }); - self.configure_command(&transaction, Some(buf.len()))?; + let transfer_size_bytes = buf.len() * W::size().bytes(); + self.configure_command(&transaction, Some(transfer_size_bytes))?; T::REGS .cr() @@ -1145,7 +1146,8 @@ impl<'d, T: Instance> Xspi<'d, T, Async> { // Wait for peripheral to be free while T::REGS.sr().read().busy() {} - self.configure_command(&transaction, Some(buf.len()))?; + let transfer_size_bytes = buf.len() * W::size().bytes(); + self.configure_command(&transaction, Some(transfer_size_bytes))?; let current_address = T::REGS.ar().read().address(); let current_instruction = T::REGS.ir().read().instruction(); @@ -1185,7 +1187,8 @@ impl<'d, T: Instance> Xspi<'d, T, Async> { // Wait for peripheral to be free while T::REGS.sr().read().busy() {} - self.configure_command(&transaction, Some(buf.len()))?; + let transfer_size_bytes = buf.len() * W::size().bytes(); + self.configure_command(&transaction, Some(transfer_size_bytes))?; T::REGS .cr() .modify(|v| v.set_fmode(Fmode::from_bits(XspiMode::IndirectWrite.into()))); @@ -1215,7 +1218,8 @@ impl<'d, T: Instance> Xspi<'d, T, Async> { // Wait for peripheral to be free while T::REGS.sr().read().busy() {} - self.configure_command(&transaction, Some(buf.len()))?; + let transfer_size_bytes = buf.len() * W::size().bytes(); + self.configure_command(&transaction, Some(transfer_size_bytes))?; let current_address = T::REGS.ar().read().address(); let current_instruction = T::REGS.ir().read().instruction(); @@ -1255,7 +1259,8 @@ impl<'d, T: Instance> Xspi<'d, T, Async> { // Wait for peripheral to be free while T::REGS.sr().read().busy() {} - self.configure_command(&transaction, Some(buf.len()))?; + let transfer_size_bytes = buf.len() * W::size().bytes(); + self.configure_command(&transaction, Some(transfer_size_bytes))?; T::REGS .cr() .modify(|v| v.set_fmode(Fmode::from_bits(XspiMode::IndirectWrite.into()))); -- cgit From 64d0fdf1d1b2ce1a3d90b312e009dfc17171086a Mon Sep 17 00:00:00 2001 From: Brian Schwind Date: Sat, 4 Oct 2025 13:32:46 +0900 Subject: xspi: properly respect the max DMA transfer size when reading and writing --- embassy-stm32/src/xspi/mod.rs | 73 ++++++++++++++++++++++++------------------- 1 file changed, 41 insertions(+), 32 deletions(-) diff --git a/embassy-stm32/src/xspi/mod.rs b/embassy-stm32/src/xspi/mod.rs index 1f051bffe..6f224ab99 100644 --- a/embassy-stm32/src/xspi/mod.rs +++ b/embassy-stm32/src/xspi/mod.rs @@ -1162,16 +1162,18 @@ impl<'d, T: Instance> Xspi<'d, T, Async> { T::REGS.ar().write(|v| v.set_address(current_address)); } - let transfer = unsafe { - self.dma - .as_mut() - .unwrap() - .read(T::REGS.dr().as_ptr() as *mut W, buf, Default::default()) - }; + for chunk in buf.chunks_mut(0xFFFF / W::size().bytes()) { + let transfer = unsafe { + self.dma + .as_mut() + .unwrap() + .read(T::REGS.dr().as_ptr() as *mut W, chunk, Default::default()) + }; - T::REGS.cr().modify(|w| w.set_dmaen(true)); + T::REGS.cr().modify(|w| w.set_dmaen(true)); - transfer.blocking_wait(); + transfer.blocking_wait(); + } finish_dma(T::REGS); @@ -1193,16 +1195,18 @@ impl<'d, T: Instance> Xspi<'d, T, Async> { .cr() .modify(|v| v.set_fmode(Fmode::from_bits(XspiMode::IndirectWrite.into()))); - let transfer = unsafe { - self.dma - .as_mut() - .unwrap() - .write(buf, T::REGS.dr().as_ptr() as *mut W, Default::default()) - }; + for chunk in buf.chunks(0xFFFF / W::size().bytes()) { + let transfer = unsafe { + self.dma + .as_mut() + .unwrap() + .write(chunk, T::REGS.dr().as_ptr() as *mut W, Default::default()) + }; - T::REGS.cr().modify(|w| w.set_dmaen(true)); + T::REGS.cr().modify(|w| w.set_dmaen(true)); - transfer.blocking_wait(); + transfer.blocking_wait(); + } finish_dma(T::REGS); @@ -1234,16 +1238,18 @@ impl<'d, T: Instance> Xspi<'d, T, Async> { T::REGS.ar().write(|v| v.set_address(current_address)); } - let transfer = unsafe { - self.dma - .as_mut() - .unwrap() - .read(T::REGS.dr().as_ptr() as *mut W, buf, Default::default()) - }; + for chunk in buf.chunks_mut(0xFFFF / W::size().bytes()) { + let transfer = unsafe { + self.dma + .as_mut() + .unwrap() + .read(T::REGS.dr().as_ptr() as *mut W, chunk, Default::default()) + }; - T::REGS.cr().modify(|w| w.set_dmaen(true)); + T::REGS.cr().modify(|w| w.set_dmaen(true)); - transfer.await; + transfer.await; + } finish_dma(T::REGS); @@ -1265,16 +1271,19 @@ impl<'d, T: Instance> Xspi<'d, T, Async> { .cr() .modify(|v| v.set_fmode(Fmode::from_bits(XspiMode::IndirectWrite.into()))); - let transfer = unsafe { - self.dma - .as_mut() - .unwrap() - .write(buf, T::REGS.dr().as_ptr() as *mut W, Default::default()) - }; + // TODO: implement this using a LinkedList DMA to offload the whole transfer off the CPU. + for chunk in buf.chunks(0xFFFF / W::size().bytes()) { + let transfer = unsafe { + self.dma + .as_mut() + .unwrap() + .write(chunk, T::REGS.dr().as_ptr() as *mut W, Default::default()) + }; - T::REGS.cr().modify(|w| w.set_dmaen(true)); + T::REGS.cr().modify(|w| w.set_dmaen(true)); - transfer.await; + transfer.await; + } finish_dma(T::REGS); -- cgit From 461681028681930e50f41ee00154ac3e1886ebca Mon Sep 17 00:00:00 2001 From: Brian Schwind Date: Sat, 4 Oct 2025 16:54:40 +0900 Subject: Update CHANGELOG.md --- embassy-stm32/CHANGELOG.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/embassy-stm32/CHANGELOG.md b/embassy-stm32/CHANGELOG.md index a6ee5c4b8..2005128d5 100644 --- a/embassy-stm32/CHANGELOG.md +++ b/embassy-stm32/CHANGELOG.md @@ -20,7 +20,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - feat: Configurable gpio speed for QSPI - feat: derive Clone, Copy and defmt::Format for all *SPI-related configs - fix: handle address and data-length errors in OSPI -- feat: Allow OSPI DMA writes larger than 64kB using chunking +- feat: Allow OSPI/HSPI/XSPI DMA writes larger than 64kB using chunking - feat: More ADC enums for g0 PAC, API change for oversampling, allow separate sample times - feat: Add USB CRS sync support for STM32C071 - fix: RTC register definition for STM32L4P5 and L4Q5 as they use v3 register map. @@ -31,6 +31,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - feat: stm32/usart: add `eager_reads` option to control if buffered readers return as soon as possible or after more data is available ([#4668](https://github.com/embassy-rs/embassy/pull/4668)) - feat: stm32/usart: add `de_assertion_time` and `de_deassertion_time` config options - change: stm32/uart: BufferedUartRx now returns all available bytes from the internal buffer +- fix: Properly set the transfer size for OSPI/HSPI/XSPI transfers with word sizes other than 8 bits. ## 0.4.0 - 2025-08-26 -- cgit From a9727a17b593f7328f721e8905b7fc8dab9ae7ff Mon Sep 17 00:00:00 2001 From: Piotr Esden-Tempski Date: Sun, 5 Oct 2025 15:49:05 -0700 Subject: stm32/ADC: Fix prescaler calculation to include max frequency. Due to the integer rounding rules one has to subtract 1 from the numerator. For example: Let max clock be 55 and supplied clock be 110 110/55 = 2 which results in the divider being set to 4 and the clock after division ends up being 27 instead of 55 Subtracting 1 to the numerator get around the rounding issue 109/55 = 1 which results in the divider being set to 2 and the clock after division ends up being 55 which is exactly max clock --- embassy-stm32/CHANGELOG.md | 1 + embassy-stm32/src/adc/adc4.rs | 3 ++- embassy-stm32/src/adc/c0.rs | 3 ++- embassy-stm32/src/adc/g4.rs | 3 ++- embassy-stm32/src/adc/v2.rs | 3 ++- embassy-stm32/src/adc/v4.rs | 3 ++- 6 files changed, 11 insertions(+), 5 deletions(-) diff --git a/embassy-stm32/CHANGELOG.md b/embassy-stm32/CHANGELOG.md index a6ee5c4b8..716c169e1 100644 --- a/embassy-stm32/CHANGELOG.md +++ b/embassy-stm32/CHANGELOG.md @@ -31,6 +31,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - feat: stm32/usart: add `eager_reads` option to control if buffered readers return as soon as possible or after more data is available ([#4668](https://github.com/embassy-rs/embassy/pull/4668)) - feat: stm32/usart: add `de_assertion_time` and `de_deassertion_time` config options - change: stm32/uart: BufferedUartRx now returns all available bytes from the internal buffer +- fix: stm32/adc: Calculate the ADC prescaler in a way that it allows for the max frequency to be reached ## 0.4.0 - 2025-08-26 diff --git a/embassy-stm32/src/adc/adc4.rs b/embassy-stm32/src/adc/adc4.rs index 255dc7956..1302dffb8 100644 --- a/embassy-stm32/src/adc/adc4.rs +++ b/embassy-stm32/src/adc/adc4.rs @@ -128,7 +128,8 @@ enum Prescaler { impl Prescaler { fn from_ker_ck(frequency: Hertz) -> Self { - let raw_prescaler = frequency.0 / MAX_ADC_CLK_FREQ.0; + // Calculate prescaler in a way where the clock can hit MAX CLK + let raw_prescaler = frequency.0.saturating_sub(1) / MAX_ADC_CLK_FREQ.0; match raw_prescaler { 0 => Self::NotDivided, 1 => Self::DividedBy2, diff --git a/embassy-stm32/src/adc/c0.rs b/embassy-stm32/src/adc/c0.rs index f2837a8f1..bd9a3e2c6 100644 --- a/embassy-stm32/src/adc/c0.rs +++ b/embassy-stm32/src/adc/c0.rs @@ -66,7 +66,8 @@ pub enum Prescaler { impl Prescaler { fn from_ker_ck(frequency: Hertz) -> Self { - let raw_prescaler = frequency.0 / MAX_ADC_CLK_FREQ.0; + // Calculate prescaler in a way where the clock can hit MAX CLK + let raw_prescaler = frequency.0.saturating_sub(1) / MAX_ADC_CLK_FREQ.0; match raw_prescaler { 0 => Self::NotDivided, 1 => Self::DividedBy2, diff --git a/embassy-stm32/src/adc/g4.rs b/embassy-stm32/src/adc/g4.rs index 43498966f..ac0a6196f 100644 --- a/embassy-stm32/src/adc/g4.rs +++ b/embassy-stm32/src/adc/g4.rs @@ -72,7 +72,8 @@ enum Prescaler { impl Prescaler { fn from_ker_ck(frequency: Hertz) -> Self { - let raw_prescaler = frequency.0 / MAX_ADC_CLK_FREQ.0; + // Calculate prescaler in a way where the clock can hit MAX CLK + let raw_prescaler = frequency.0.saturating_sub(1) / MAX_ADC_CLK_FREQ.0; match raw_prescaler { 0 => Self::NotDivided, 1 => Self::DividedBy2, diff --git a/embassy-stm32/src/adc/v2.rs b/embassy-stm32/src/adc/v2.rs index e94a25b24..57f252e13 100644 --- a/embassy-stm32/src/adc/v2.rs +++ b/embassy-stm32/src/adc/v2.rs @@ -71,7 +71,8 @@ impl Prescaler { // Datasheet for both F4 and F7 specifies min frequency 0.6 MHz, typ freq. 30 MHz and max 36 MHz. #[cfg(not(stm32f2))] const MAX_FREQUENCY: Hertz = Hertz(36_000_000); - let raw_div = freq.0 / MAX_FREQUENCY.0; + // Calculate prescaler divider including MAX_FREQ + let raw_div = freq.0.saturating_sub(1) / MAX_FREQUENCY.0; match raw_div { 0..=1 => Self::Div2, 2..=3 => Self::Div4, diff --git a/embassy-stm32/src/adc/v4.rs b/embassy-stm32/src/adc/v4.rs index b66437e6e..c68684cb2 100644 --- a/embassy-stm32/src/adc/v4.rs +++ b/embassy-stm32/src/adc/v4.rs @@ -93,7 +93,8 @@ enum Prescaler { impl Prescaler { fn from_ker_ck(frequency: Hertz) -> Self { - let raw_prescaler = frequency.0 / MAX_ADC_CLK_FREQ.0; + // Calculate prescaler in a way where the clock can hit MAX CLK + let raw_prescaler = frequency.0.saturating_sub(1) / MAX_ADC_CLK_FREQ.0; match raw_prescaler { 0 => Self::NotDivided, 1 => Self::DividedBy2, -- cgit From 6831fdbfe896e9f848f93c31473703fa1c767198 Mon Sep 17 00:00:00 2001 From: Piotr Esden-Tempski Date: Sun, 5 Oct 2025 19:41:49 -0700 Subject: stm32: Add raw_prescaler function to make it more reusable. This also puts the explanation why the calculation has to be done that way into one place so it does not need to be copied all over the codebase. --- embassy-stm32/src/adc/adc4.rs | 3 +-- embassy-stm32/src/adc/c0.rs | 3 +-- embassy-stm32/src/adc/g4.rs | 3 +-- embassy-stm32/src/adc/v2.rs | 3 +-- embassy-stm32/src/adc/v4.rs | 3 +-- embassy-stm32/src/rcc/mod.rs | 30 ++++++++++++++++++++++++++++++ 6 files changed, 35 insertions(+), 10 deletions(-) diff --git a/embassy-stm32/src/adc/adc4.rs b/embassy-stm32/src/adc/adc4.rs index 1302dffb8..0b442330a 100644 --- a/embassy-stm32/src/adc/adc4.rs +++ b/embassy-stm32/src/adc/adc4.rs @@ -128,8 +128,7 @@ enum Prescaler { impl Prescaler { fn from_ker_ck(frequency: Hertz) -> Self { - // Calculate prescaler in a way where the clock can hit MAX CLK - let raw_prescaler = frequency.0.saturating_sub(1) / MAX_ADC_CLK_FREQ.0; + let raw_prescaler = rcc::raw_prescaler(frequency.0, MAX_ADC_CLK_FREQ.0); match raw_prescaler { 0 => Self::NotDivided, 1 => Self::DividedBy2, diff --git a/embassy-stm32/src/adc/c0.rs b/embassy-stm32/src/adc/c0.rs index bd9a3e2c6..5b3438ea1 100644 --- a/embassy-stm32/src/adc/c0.rs +++ b/embassy-stm32/src/adc/c0.rs @@ -66,8 +66,7 @@ pub enum Prescaler { impl Prescaler { fn from_ker_ck(frequency: Hertz) -> Self { - // Calculate prescaler in a way where the clock can hit MAX CLK - let raw_prescaler = frequency.0.saturating_sub(1) / MAX_ADC_CLK_FREQ.0; + let raw_prescaler = rcc::raw_prescaler(frequency.0, MAX_ADC_CLK_FREQ.0); match raw_prescaler { 0 => Self::NotDivided, 1 => Self::DividedBy2, diff --git a/embassy-stm32/src/adc/g4.rs b/embassy-stm32/src/adc/g4.rs index ac0a6196f..6c7789c0e 100644 --- a/embassy-stm32/src/adc/g4.rs +++ b/embassy-stm32/src/adc/g4.rs @@ -72,8 +72,7 @@ enum Prescaler { impl Prescaler { fn from_ker_ck(frequency: Hertz) -> Self { - // Calculate prescaler in a way where the clock can hit MAX CLK - let raw_prescaler = frequency.0.saturating_sub(1) / MAX_ADC_CLK_FREQ.0; + let raw_prescaler = rcc::raw_prescaler(frequency.0, MAX_ADC_CLK_FREQ.0); match raw_prescaler { 0 => Self::NotDivided, 1 => Self::DividedBy2, diff --git a/embassy-stm32/src/adc/v2.rs b/embassy-stm32/src/adc/v2.rs index 57f252e13..09e0f4d11 100644 --- a/embassy-stm32/src/adc/v2.rs +++ b/embassy-stm32/src/adc/v2.rs @@ -71,8 +71,7 @@ impl Prescaler { // Datasheet for both F4 and F7 specifies min frequency 0.6 MHz, typ freq. 30 MHz and max 36 MHz. #[cfg(not(stm32f2))] const MAX_FREQUENCY: Hertz = Hertz(36_000_000); - // Calculate prescaler divider including MAX_FREQ - let raw_div = freq.0.saturating_sub(1) / MAX_FREQUENCY.0; + let raw_div = rcc::raw_prescaler(freq.0, MAX_FREQUENCY.0); match raw_div { 0..=1 => Self::Div2, 2..=3 => Self::Div4, diff --git a/embassy-stm32/src/adc/v4.rs b/embassy-stm32/src/adc/v4.rs index c68684cb2..6c8ce7b64 100644 --- a/embassy-stm32/src/adc/v4.rs +++ b/embassy-stm32/src/adc/v4.rs @@ -93,8 +93,7 @@ enum Prescaler { impl Prescaler { fn from_ker_ck(frequency: Hertz) -> Self { - // Calculate prescaler in a way where the clock can hit MAX CLK - let raw_prescaler = frequency.0.saturating_sub(1) / MAX_ADC_CLK_FREQ.0; + let raw_prescaler = rcc::raw_prescaler(frequency.0, MAX_ADC_CLK_FREQ.0); match raw_prescaler { 0 => Self::NotDivided, 1 => Self::DividedBy2, diff --git a/embassy-stm32/src/rcc/mod.rs b/embassy-stm32/src/rcc/mod.rs index c41f81816..8509838ed 100644 --- a/embassy-stm32/src/rcc/mod.rs +++ b/embassy-stm32/src/rcc/mod.rs @@ -409,3 +409,33 @@ pub(crate) fn init_rcc(_cs: CriticalSection, config: Config) { } } } + +/// Calculate intermediate prescaler number used to calculate peripheral prescalers +/// +/// This function is intended to calculate a number indicating a minimum division +/// necessary to result in a frequency lower than the provided `freq_max`. +/// +/// The returned value indicates the `val + 1` divider is necessary to result in +/// the output frequency that is below the maximum provided. +/// +/// For example: +/// 0 = divider of 1 => no division necessary as the input frequency is below max +/// 1 = divider of 2 => division by 2 necessary +/// ... +/// +/// The provided max frequency is inclusive. So if `freq_in == freq_max` the result +/// will be 0, indicating that no division is necessary. To accomplish that we subtract +/// 1 from the input frequency so that the integer rounding plays in our favor. +/// +/// For example: +/// Let the input frequency be 110 and the max frequency be 55. +/// If we naiively do `110/55 = 2` the renult will indicate that we need a divider by 3 +/// which in reality will be rounded up to 4 as usually a 3 division is not available. +/// In either case the resulting frequency will be either 36 or 27 which is lower than +/// what we would want. The result should be 1. +/// If we do the following instead `109/55 = 1` indicating that we need a divide by 2 +/// which will result in the correct 55. +#[allow(unused)] +pub(crate) fn raw_prescaler(freq_in: u32, freq_max: u32) -> u32 { + freq_in.saturating_sub(1) / freq_max +} -- cgit From c4b7fde3bc44a0b87b29eb048b76445fa2177e93 Mon Sep 17 00:00:00 2001 From: everdrone Date: Wed, 22 Oct 2025 20:01:15 +0200 Subject: allow setting stm32 SAI frame_length to 256 --- embassy-stm32/src/sai/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/embassy-stm32/src/sai/mod.rs b/embassy-stm32/src/sai/mod.rs index 726d1729a..08aebfb11 100644 --- a/embassy-stm32/src/sai/mod.rs +++ b/embassy-stm32/src/sai/mod.rs @@ -391,7 +391,7 @@ pub struct Config { pub frame_sync_polarity: FrameSyncPolarity, pub frame_sync_active_level_length: word::U7, pub frame_sync_definition: FrameSyncDefinition, - pub frame_length: u8, + pub frame_length: u16, pub clock_strobe: ClockStrobe, pub output_drive: OutputDrive, pub master_clock_divider: Option, -- cgit From 86c32c8d7ce6100e7b18413efd3e13932cbd9157 Mon Sep 17 00:00:00 2001 From: everdrone Date: Thu, 23 Oct 2025 12:04:51 +0200 Subject: Add changelog entry --- embassy-stm32/CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/embassy-stm32/CHANGELOG.md b/embassy-stm32/CHANGELOG.md index 9848daf49..d2a1b9161 100644 --- a/embassy-stm32/CHANGELOG.md +++ b/embassy-stm32/CHANGELOG.md @@ -9,6 +9,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### [Unreleased] * **Fix(stm32h5):** Prevent a HardFault crash on STM32H5 devices by changing `uid()` to return `[u8; 12]` by value instead of a reference. (Fixes #2696) +- fix: Allow setting SAI peripheral `frame_length` to `256` + ## Unreleased - ReleaseDate - fix flash erase on L4 & L5 -- cgit From 23833b1716e2de6ac18db23521073e870c13e009 Mon Sep 17 00:00:00 2001 From: everdrone Date: Thu, 23 Oct 2025 12:43:58 +0200 Subject: add error message and convert to u8 --- embassy-stm32/src/sai/mod.rs | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/embassy-stm32/src/sai/mod.rs b/embassy-stm32/src/sai/mod.rs index 08aebfb11..58e3b832a 100644 --- a/embassy-stm32/src/sai/mod.rs +++ b/embassy-stm32/src/sai/mod.rs @@ -696,7 +696,12 @@ impl<'d, T: Instance, W: word::Word> Sai<'d, T, W> { w.set_fspol(config.frame_sync_polarity.fspol()); w.set_fsdef(config.frame_sync_definition.fsdef()); w.set_fsall(config.frame_sync_active_level_length.0 as u8 - 1); - w.set_frl(config.frame_length - 1); + + if config.frame_length > 256 { + panic!("Frame length cannot be greater than 256"); + } + + w.set_frl((config.frame_length - 1) as u8); }); ch.slotr().modify(|w| { -- cgit From e2807058ffc73bd0fc2f4ce9f29e5a56f3e5a18e Mon Sep 17 00:00:00 2001 From: everdrone Date: Thu, 23 Oct 2025 12:49:47 +0200 Subject: fix stm32h723 example --- examples/stm32h723/src/bin/spdifrx.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/stm32h723/src/bin/spdifrx.rs b/examples/stm32h723/src/bin/spdifrx.rs index cdbd69b89..5c29602c6 100644 --- a/examples/stm32h723/src/bin/spdifrx.rs +++ b/examples/stm32h723/src/bin/spdifrx.rs @@ -167,7 +167,7 @@ fn new_sai_transmitter<'d>( sai_config.slot_count = hal::sai::word::U4(CHANNEL_COUNT as u8); sai_config.slot_enable = 0xFFFF; // All slots sai_config.data_size = sai::DataSize::Data32; - sai_config.frame_length = (CHANNEL_COUNT * 32) as u8; + sai_config.frame_length = (CHANNEL_COUNT * 32) as u16; sai_config.master_clock_divider = None; let (sub_block_tx, _) = hal::sai::split_subblocks(sai); -- cgit From 7976f950b0de72c521f92efa350c67ccd197fab9 Mon Sep 17 00:00:00 2001 From: Matteo Meluzzi Date: Fri, 24 Oct 2025 15:48:34 +0200 Subject: Merge branch 'main' into 17-add-support-for-boot-protocol --- .github/ci/build-nightly.sh | 2 +- .github/ci/build-xtensa.sh | 2 +- .github/ci/build.sh | 2 +- .github/ci/doc.sh | 2 +- .github/ci/janitor.sh | 2 +- .github/ci/rustfmt.sh | 2 +- cyw43-pio/Cargo.toml | 2 +- cyw43-pio/src/lib.rs | 4 +- cyw43/Cargo.toml | 2 +- cyw43/src/bluetooth.rs | 2 +- cyw43/src/bus.rs | 6 +- cyw43/src/control.rs | 2 +- cyw43/src/ioctl.rs | 2 +- cyw43/src/lib.rs | 1 + cyw43/src/runner.rs | 6 +- .../layer-by-layer/blinky-async/Cargo.toml | 2 +- docs/examples/layer-by-layer/blinky-hal/Cargo.toml | 2 +- docs/examples/layer-by-layer/blinky-irq/Cargo.toml | 2 +- docs/examples/layer-by-layer/blinky-pac/Cargo.toml | 2 +- docs/pages/embassy_in_the_wild.adoc | 7 +- embassy-boot-nrf/Cargo.toml | 2 +- embassy-boot-nrf/src/lib.rs | 3 +- embassy-boot-rp/Cargo.toml | 2 +- embassy-boot-rp/src/lib.rs | 5 +- embassy-boot-stm32/Cargo.toml | 2 +- embassy-boot-stm32/src/lib.rs | 1 + embassy-boot/Cargo.toml | 2 +- embassy-boot/src/boot_loader.rs | 6 +- embassy-boot/src/firmware_updater/asynch.rs | 4 +- embassy-boot/src/firmware_updater/blocking.rs | 6 +- embassy-boot/src/lib.rs | 15 +- embassy-boot/src/test_flash/blocking.rs | 2 +- embassy-embedded-hal/Cargo.toml | 2 +- .../src/flash/partition/blocking.rs | 2 +- embassy-embedded-hal/src/shared_bus/asynch/i2c.rs | 2 +- embassy-embedded-hal/src/shared_bus/asynch/spi.rs | 2 +- .../src/shared_bus/blocking/i2c.rs | 4 +- .../src/shared_bus/blocking/spi.rs | 4 +- embassy-executor-macros/Cargo.toml | 2 +- embassy-executor-macros/src/macros/main.rs | 4 +- embassy-executor-macros/src/macros/task.rs | 8 +- embassy-executor-macros/src/util.rs | 2 +- embassy-executor-timer-queue/Cargo.toml | 2 +- embassy-executor/CHANGELOG.md | 1 + embassy-executor/Cargo.toml | 6 +- embassy-executor/src/arch/avr.rs | 4 +- embassy-executor/src/arch/cortex_ar.rs | 4 +- embassy-executor/src/arch/cortex_m.rs | 4 +- embassy-executor/src/arch/riscv32.rs | 4 +- embassy-executor/src/arch/spin.rs | 4 +- embassy-executor/src/arch/std.rs | 4 +- embassy-executor/src/arch/wasm.rs | 4 +- embassy-executor/src/lib.rs | 1 + embassy-executor/src/metadata.rs | 2 +- embassy-executor/src/raw/mod.rs | 6 +- embassy-executor/src/raw/run_queue.rs | 4 +- embassy-executor/src/raw/state_atomics_arm.rs | 2 +- embassy-executor/src/raw/state_critical_section.rs | 2 +- embassy-executor/src/raw/trace.rs | 6 +- embassy-executor/src/raw/waker.rs | 6 +- embassy-executor/src/raw/waker_turbo.rs | 4 +- embassy-executor/src/spawner.rs | 12 +- embassy-executor/tests/test.rs | 6 +- .../tests/ui/nonstatic_struct_elided.stderr | 12 +- embassy-executor/tests/ui/task_safety_attribute.rs | 2 +- embassy-futures/Cargo.toml | 2 +- embassy-hal-internal/Cargo.toml | 2 +- embassy-hal-internal/src/interrupt.rs | 2 +- embassy-hal-internal/src/lib.rs | 1 + embassy-hal-internal/src/macros.rs | 2 +- embassy-imxrt/Cargo.toml | 2 +- embassy-imxrt/src/clocks.rs | 2 +- embassy-imxrt/src/crc.rs | 4 +- embassy-imxrt/src/dma.rs | 6 +- embassy-imxrt/src/flexcomm/mod.rs | 18 +- embassy-imxrt/src/flexcomm/uart.rs | 4 +- embassy-imxrt/src/gpio.rs | 2 +- embassy-imxrt/src/iopctl.rs | 2 +- embassy-imxrt/src/lib.rs | 5 +- embassy-imxrt/src/rng.rs | 4 +- embassy-imxrt/src/time_driver.rs | 4 +- embassy-mspm0/CHANGELOG.md | 4 + embassy-mspm0/Cargo.toml | 20 +- embassy-mspm0/build.rs | 52 ++- embassy-mspm0/src/adc.rs | 6 +- embassy-mspm0/src/dma.rs | 8 +- embassy-mspm0/src/gpio.rs | 32 +- embassy-mspm0/src/i2c.rs | 21 +- embassy-mspm0/src/i2c_target.rs | 509 +++++++++++++++++++++ embassy-mspm0/src/lib.rs | 6 +- embassy-mspm0/src/time_driver.rs | 2 +- embassy-mspm0/src/uart/buffered.rs | 4 +- embassy-mspm0/src/uart/mod.rs | 13 +- embassy-mspm0/src/wwdt.rs | 4 +- embassy-net-adin1110/Cargo.toml | 2 +- embassy-net-adin1110/src/crc8.rs | 2 +- embassy-net-adin1110/src/lib.rs | 4 +- embassy-net-driver-channel/Cargo.toml | 2 +- embassy-net-driver-channel/src/lib.rs | 2 +- embassy-net-driver/Cargo.toml | 2 +- embassy-net-driver/src/lib.rs | 1 + embassy-net-enc28j60/Cargo.toml | 2 +- embassy-net-esp-hosted/Cargo.toml | 2 +- embassy-net-esp-hosted/src/control.rs | 3 +- embassy-net-esp-hosted/src/ioctl.rs | 2 +- embassy-net-esp-hosted/src/lib.rs | 2 +- embassy-net-nrf91/Cargo.toml | 2 +- embassy-net-nrf91/src/lib.rs | 12 +- embassy-net-ppp/Cargo.toml | 2 +- embassy-net-ppp/src/lib.rs | 2 +- embassy-net-tuntap/Cargo.toml | 2 +- embassy-net-wiznet/Cargo.toml | 2 +- embassy-net-wiznet/src/chip/mod.rs | 2 +- embassy-net-wiznet/src/lib.rs | 2 +- embassy-net/Cargo.toml | 2 +- embassy-net/src/icmp.rs | 2 +- embassy-net/src/lib.rs | 3 +- embassy-net/src/raw.rs | 2 +- embassy-net/src/tcp.rs | 4 +- embassy-net/src/udp.rs | 2 +- embassy-nrf/CHANGELOG.md | 6 + embassy-nrf/Cargo.toml | 4 +- embassy-nrf/src/buffered_uarte.rs | 32 +- embassy-nrf/src/chips/nrf54l15_app.rs | 43 ++ embassy-nrf/src/egu.rs | 2 +- embassy-nrf/src/embassy_net_802154_driver.rs | 4 +- embassy-nrf/src/gpio.rs | 4 +- embassy-nrf/src/gpiote.rs | 12 +- embassy-nrf/src/i2s.rs | 4 +- embassy-nrf/src/lib.rs | 70 ++- embassy-nrf/src/nfct.rs | 6 +- 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 | 4 +- embassy-nrf/src/ppi/ppi.rs | 2 +- embassy-nrf/src/pwm.rs | 4 +- embassy-nrf/src/qspi.rs | 2 +- embassy-nrf/src/radio/ieee802154.rs | 21 +- embassy-nrf/src/rramc.rs | 2 +- embassy-nrf/src/saadc.rs | 4 +- embassy-nrf/src/spim.rs | 6 +- embassy-nrf/src/spis.rs | 6 +- embassy-nrf/src/temp.rs | 2 +- embassy-nrf/src/time_driver.rs | 4 +- embassy-nrf/src/twim.rs | 2 +- embassy-nrf/src/twis.rs | 4 +- embassy-nrf/src/uarte.rs | 4 +- embassy-nrf/src/usb/mod.rs | 10 +- embassy-nrf/src/usb/vbus_detect.rs | 2 +- embassy-nrf/src/util.rs | 6 +- embassy-nrf/src/wdt.rs | 2 +- embassy-nxp/CHANGELOG.md | 2 + embassy-nxp/Cargo.toml | 2 +- embassy-nxp/src/dma/lpc55.rs | 6 +- embassy-nxp/src/gpio/lpc55.rs | 70 +-- embassy-nxp/src/gpio/rt1xxx.rs | 4 +- embassy-nxp/src/lib.rs | 5 +- embassy-nxp/src/pint.rs | 4 +- embassy-nxp/src/time_driver/rtc.rs | 4 +- embassy-nxp/src/usart/lpc55.rs | 177 ++++--- embassy-rp/Cargo.toml | 6 +- embassy-rp/src/adc.rs | 8 +- embassy-rp/src/bootsel.rs | 4 +- embassy-rp/src/clocks.rs | 4 +- embassy-rp/src/dma.rs | 4 +- embassy-rp/src/flash.rs | 10 +- embassy-rp/src/float/cmp.rs | 12 +- embassy-rp/src/float/functions.rs | 12 +- embassy-rp/src/gpio.rs | 6 +- embassy-rp/src/i2c_slave.rs | 4 +- embassy-rp/src/intrinsics.rs | 6 +- embassy-rp/src/lib.rs | 14 +- embassy-rp/src/multicore.rs | 9 +- embassy-rp/src/pio/mod.rs | 14 +- embassy-rp/src/pio_programs/hd44780.rs | 2 +- embassy-rp/src/pio_programs/i2s.rs | 2 +- embassy-rp/src/pio_programs/onewire.rs | 8 +- embassy-rp/src/pio_programs/pwm.rs | 2 +- embassy-rp/src/pio_programs/rotary_encoder.rs | 2 +- embassy-rp/src/pio_programs/stepper.rs | 2 +- embassy-rp/src/pio_programs/uart.rs | 2 +- embassy-rp/src/pio_programs/ws2812.rs | 2 +- embassy-rp/src/psram.rs | 10 +- embassy-rp/src/pwm.rs | 4 +- embassy-rp/src/rtc/mod.rs | 2 +- embassy-rp/src/time_driver.rs | 2 +- embassy-rp/src/uart/mod.rs | 16 +- embassy-rp/src/usb.rs | 16 +- embassy-rp/src/watchdog.rs | 2 +- embassy-stm32-wpan/Cargo.toml | 2 +- embassy-stm32-wpan/src/cmd.rs | 2 +- embassy-stm32-wpan/src/consts.rs | 2 +- embassy-stm32-wpan/src/lhci.rs | 4 +- embassy-stm32-wpan/src/lib.rs | 5 +- embassy-stm32-wpan/src/mac/driver.rs | 2 +- embassy-stm32-wpan/src/mac/runner.rs | 2 +- embassy-stm32-wpan/src/sub/ble.rs | 4 +- embassy-stm32-wpan/src/sub/mm.rs | 4 +- embassy-stm32-wpan/src/sub/sys.rs | 12 +- embassy-stm32-wpan/src/tables.rs | 52 +-- embassy-stm32/CHANGELOG.md | 4 + embassy-stm32/Cargo.toml | 2 +- embassy-stm32/build.rs | 8 +- embassy-stm32/src/adc/adc4.rs | 9 +- embassy-stm32/src/adc/c0.rs | 9 +- embassy-stm32/src/adc/f1.rs | 2 +- embassy-stm32/src/adc/f3.rs | 2 +- embassy-stm32/src/adc/f3_v1_1.rs | 2 +- embassy-stm32/src/adc/g4.rs | 9 +- embassy-stm32/src/adc/mod.rs | 2 +- embassy-stm32/src/adc/ringbuffered_v2.rs | 4 +- embassy-stm32/src/adc/v1.rs | 8 +- embassy-stm32/src/adc/v2.rs | 2 +- embassy-stm32/src/adc/v3.rs | 4 +- embassy-stm32/src/adc/v4.rs | 9 +- embassy-stm32/src/can/bxcan/mod.rs | 12 +- embassy-stm32/src/can/fd/config.rs | 2 +- embassy-stm32/src/can/fdcan.rs | 12 +- embassy-stm32/src/can/util.rs | 2 +- embassy-stm32/src/crc/v1.rs | 2 +- embassy-stm32/src/crc/v2v3.rs | 4 +- embassy-stm32/src/cryp/mod.rs | 10 +- embassy-stm32/src/dac/mod.rs | 2 +- embassy-stm32/src/dcmi.rs | 2 +- embassy-stm32/src/dma/dma_bdma.rs | 4 +- embassy-stm32/src/dma/gpdma/mod.rs | 2 +- embassy-stm32/src/dma/gpdma/ringbuffered.rs | 4 +- embassy-stm32/src/dma/mod.rs | 2 +- .../src/dma/ringbuffer/tests/prop_test/mod.rs | 2 +- embassy-stm32/src/dsihost.rs | 2 +- embassy-stm32/src/dts/mod.rs | 2 +- embassy-stm32/src/eth/v1/mod.rs | 8 +- embassy-stm32/src/eth/v1/rx_desc.rs | 2 +- embassy-stm32/src/eth/v1/tx_desc.rs | 2 +- embassy-stm32/src/eth/v2/descriptors.rs | 2 +- embassy-stm32/src/eth/v2/mod.rs | 6 +- embassy-stm32/src/exti.rs | 6 +- embassy-stm32/src/flash/asynch.rs | 8 +- embassy-stm32/src/flash/common.rs | 8 +- embassy-stm32/src/flash/eeprom.rs | 2 +- embassy-stm32/src/flash/f0.rs | 2 +- embassy-stm32/src/flash/f1f3.rs | 2 +- embassy-stm32/src/flash/f2.rs | 4 +- embassy-stm32/src/flash/f4.rs | 24 +- embassy-stm32/src/flash/f7.rs | 12 +- embassy-stm32/src/flash/g.rs | 18 +- embassy-stm32/src/flash/h5.rs | 2 +- embassy-stm32/src/flash/h50.rs | 2 +- embassy-stm32/src/flash/h7.rs | 4 +- embassy-stm32/src/flash/l.rs | 30 +- embassy-stm32/src/flash/u0.rs | 2 +- embassy-stm32/src/flash/u5.rs | 2 +- embassy-stm32/src/fmc.rs | 2 +- embassy-stm32/src/gpio.rs | 2 +- embassy-stm32/src/hash/mod.rs | 8 +- embassy-stm32/src/hsem/mod.rs | 4 +- embassy-stm32/src/hspi/mod.rs | 2 +- embassy-stm32/src/i2c/mod.rs | 4 +- embassy-stm32/src/i2c/v1.rs | 8 +- embassy-stm32/src/i2c/v2.rs | 2 +- embassy-stm32/src/i2s.rs | 4 +- embassy-stm32/src/ipcc.rs | 2 +- embassy-stm32/src/lib.rs | 5 +- embassy-stm32/src/low_power.rs | 4 +- embassy-stm32/src/lptim/pwm.rs | 6 +- embassy-stm32/src/ltdc.rs | 2 +- embassy-stm32/src/opamp.rs | 2 +- embassy-stm32/src/ospi/mod.rs | 6 +- embassy-stm32/src/qspi/mod.rs | 2 +- embassy-stm32/src/rcc/bd.rs | 4 +- embassy-stm32/src/rcc/f247.rs | 4 +- embassy-stm32/src/rcc/h.rs | 5 +- embassy-stm32/src/rcc/l.rs | 8 +- embassy-stm32/src/rcc/mco.rs | 4 +- embassy-stm32/src/rcc/mod.rs | 2 +- embassy-stm32/src/rcc/u5.rs | 9 +- embassy-stm32/src/rcc/wba.rs | 9 +- embassy-stm32/src/rng.rs | 2 +- embassy-stm32/src/rtc/low_power.rs | 2 +- embassy-stm32/src/rtc/mod.rs | 8 +- embassy-stm32/src/rtc/v3.rs | 2 +- embassy-stm32/src/sai/mod.rs | 6 +- embassy-stm32/src/sdmmc/mod.rs | 6 +- embassy-stm32/src/spdifrx/mod.rs | 2 +- embassy-stm32/src/spi/mod.rs | 8 +- embassy-stm32/src/time_driver.rs | 6 +- embassy-stm32/src/timer/complementary_pwm.rs | 6 +- embassy-stm32/src/timer/input_capture.rs | 2 +- embassy-stm32/src/timer/one_pulse.rs | 2 +- embassy-stm32/src/timer/pwm_input.rs | 2 +- embassy-stm32/src/timer/qei.rs | 2 +- embassy-stm32/src/timer/simple_pwm.rs | 2 +- embassy-stm32/src/tsc/acquisition_banks.rs | 4 +- embassy-stm32/src/tsc/pin_groups.rs | 4 +- embassy-stm32/src/ucpd.rs | 4 +- embassy-stm32/src/uid.rs | 4 +- embassy-stm32/src/usart/buffered.rs | 6 +- embassy-stm32/src/usart/mod.rs | 8 +- embassy-stm32/src/usart/ringbuffered.rs | 8 +- embassy-stm32/src/usb/otg.rs | 10 +- embassy-stm32/src/usb/usb.rs | 4 +- embassy-stm32/src/vrefbuf/mod.rs | 3 +- embassy-stm32/src/wdg/mod.rs | 2 +- embassy-stm32/src/xspi/mod.rs | 6 +- embassy-sync/Cargo.toml | 2 +- embassy-sync/src/channel.rs | 30 +- embassy-sync/src/lib.rs | 1 + embassy-sync/src/mutex.rs | 4 +- embassy-sync/src/once_lock.rs | 2 +- embassy-sync/src/pipe.rs | 2 +- embassy-sync/src/priority_channel.rs | 32 +- embassy-sync/src/pubsub/mod.rs | 2 +- embassy-sync/src/ring_buffer.rs | 6 +- embassy-sync/src/rwlock.rs | 4 +- embassy-sync/src/semaphore.rs | 4 +- embassy-sync/src/signal.rs | 4 +- embassy-sync/src/waitqueue/atomic_waker.rs | 2 +- embassy-sync/src/watch.rs | 4 +- embassy-sync/src/zerocopy_channel.rs | 10 +- embassy-time-driver/Cargo.toml | 2 +- embassy-time-driver/src/lib.rs | 8 +- embassy-time-queue-utils/Cargo.toml | 2 +- embassy-time-queue-utils/src/queue_generic.rs | 2 +- embassy-time/Cargo.toml | 2 +- embassy-time/src/duration.rs | 8 +- embassy-time/src/lib.rs | 11 +- embassy-time/src/timer.rs | 4 +- embassy-usb-dfu/Cargo.toml | 2 +- embassy-usb-dfu/src/application.rs | 4 +- embassy-usb-dfu/src/dfu.rs | 4 +- embassy-usb-driver/Cargo.toml | 2 +- embassy-usb-driver/src/lib.rs | 1 + embassy-usb-logger/Cargo.toml | 2 +- embassy-usb-synopsys-otg/Cargo.toml | 2 +- embassy-usb-synopsys-otg/src/lib.rs | 17 +- embassy-usb/Cargo.toml | 2 +- embassy-usb/src/builder.rs | 9 +- embassy-usb/src/class/cdc_acm.rs | 2 +- embassy-usb/src/class/cdc_ncm/embassy_net.rs | 2 +- embassy-usb/src/class/cdc_ncm/mod.rs | 2 +- embassy-usb/src/class/cmsis_dap_v2.rs | 2 +- embassy-usb/src/class/midi.rs | 2 +- embassy-usb/src/class/uac1/speaker.rs | 4 +- embassy-usb/src/descriptor.rs | 2 +- embassy-usb/src/lib.rs | 3 +- embassy-usb/src/msos.rs | 2 +- examples/boot/application/nrf/Cargo.toml | 2 +- examples/boot/application/rp/Cargo.toml | 2 +- examples/boot/application/stm32f3/Cargo.toml | 2 +- examples/boot/application/stm32f7/Cargo.toml | 2 +- examples/boot/application/stm32h7/Cargo.toml | 2 +- examples/boot/application/stm32l0/Cargo.toml | 2 +- examples/boot/application/stm32l1/Cargo.toml | 2 +- examples/boot/application/stm32l4/Cargo.toml | 2 +- examples/boot/application/stm32wb-dfu/Cargo.toml | 2 +- examples/boot/application/stm32wb-dfu/src/main.rs | 4 +- examples/boot/application/stm32wba-dfu/Cargo.toml | 2 +- examples/boot/application/stm32wba-dfu/src/main.rs | 4 +- examples/boot/application/stm32wl/Cargo.toml | 2 +- examples/boot/application/stm32wl/src/bin/a.rs | 2 +- examples/boot/application/stm32wl/src/bin/b.rs | 2 +- examples/boot/bootloader/nrf/Cargo.toml | 2 +- examples/boot/bootloader/nrf/src/main.rs | 6 +- examples/boot/bootloader/rp/Cargo.toml | 2 +- examples/boot/bootloader/rp/src/main.rs | 6 +- .../boot/bootloader/stm32-dual-bank/Cargo.toml | 2 +- .../boot/bootloader/stm32-dual-bank/src/main.rs | 8 +- examples/boot/bootloader/stm32/Cargo.toml | 2 +- examples/boot/bootloader/stm32/src/main.rs | 8 +- examples/boot/bootloader/stm32wb-dfu/Cargo.toml | 2 +- examples/boot/bootloader/stm32wb-dfu/src/main.rs | 12 +- examples/boot/bootloader/stm32wba-dfu/Cargo.toml | 2 +- examples/boot/bootloader/stm32wba-dfu/src/main.rs | 14 +- examples/lpc55s69/Cargo.toml | 2 +- examples/mimxrt1011/Cargo.toml | 2 +- examples/mimxrt1011/src/lib.rs | 2 +- examples/mimxrt1062-evk/Cargo.toml | 2 +- examples/mimxrt1062-evk/src/lib.rs | 4 +- examples/mimxrt6/Cargo.toml | 2 +- examples/mimxrt6/src/bin/button.rs | 2 +- examples/mspm0c1104/Cargo.toml | 2 +- examples/mspm0c1104/src/bin/blinky.rs | 2 +- examples/mspm0c1104/src/bin/button.rs | 2 +- examples/mspm0g3507/Cargo.toml | 2 +- examples/mspm0g3507/src/bin/adc.rs | 2 +- examples/mspm0g3507/src/bin/blinky.rs | 2 +- examples/mspm0g3507/src/bin/button.rs | 2 +- examples/mspm0g3507/src/bin/i2c_target.rs | 63 +++ examples/mspm0g3519/Cargo.toml | 2 +- examples/mspm0g3519/src/bin/blinky.rs | 2 +- examples/mspm0g3519/src/bin/button.rs | 2 +- examples/mspm0l1306/Cargo.toml | 2 +- examples/mspm0l1306/src/bin/adc.rs | 2 +- examples/mspm0l1306/src/bin/blinky.rs | 2 +- examples/mspm0l1306/src/bin/button.rs | 2 +- examples/mspm0l1306/src/bin/i2c_target.rs | 63 +++ examples/mspm0l2228/Cargo.toml | 2 +- examples/mspm0l2228/src/bin/blinky.rs | 2 +- examples/mspm0l2228/src/bin/button.rs | 2 +- examples/nrf-rtos-trace/Cargo.toml | 2 +- examples/nrf51/Cargo.toml | 2 +- examples/nrf52810/Cargo.toml | 2 +- examples/nrf52840-edf/Cargo.toml | 2 +- examples/nrf52840-edf/src/bin/basic.rs | 2 +- examples/nrf52840-rtic/Cargo.toml | 2 +- examples/nrf52840-rtic/src/bin/blinky.rs | 2 +- examples/nrf52840/Cargo.toml | 2 +- .../nrf52840/src/bin/channel_sender_receiver.rs | 2 +- examples/nrf52840/src/bin/ethernet_enc28j60.rs | 2 +- examples/nrf52840/src/bin/i2s_effect.rs | 8 +- examples/nrf52840/src/bin/i2s_monitor.rs | 2 +- examples/nrf52840/src/bin/i2s_waveform.rs | 8 +- examples/nrf52840/src/bin/multiprio.rs | 4 +- examples/nrf52840/src/bin/raw_spawn.rs | 4 +- examples/nrf52840/src/bin/rtc.rs | 2 +- examples/nrf52840/src/bin/usb_ethernet.rs | 4 +- examples/nrf52840/src/bin/usb_hid_keyboard.rs | 8 +- examples/nrf52840/src/bin/usb_hid_mouse.rs | 2 +- examples/nrf52840/src/bin/usb_serial.rs | 2 +- examples/nrf52840/src/bin/usb_serial_multitask.rs | 2 +- examples/nrf52840/src/bin/usb_serial_winusb.rs | 2 +- examples/nrf52840/src/bin/wifi_esp_hosted.rs | 2 +- examples/nrf5340/Cargo.toml | 2 +- .../nrf5340/src/bin/nrf5340dk_internal_caps.rs | 30 ++ examples/nrf54l15/Cargo.toml | 4 +- examples/nrf54l15/src/bin/rtc.rs | 56 +++ examples/nrf9151/ns/Cargo.toml | 2 +- examples/nrf9151/s/Cargo.toml | 2 +- examples/nrf9160/Cargo.toml | 2 +- examples/nrf9160/src/bin/modem_tcp_client.rs | 6 +- examples/rp/Cargo.toml | 2 +- examples/rp/src/bin/assign_resources.rs | 2 +- examples/rp/src/bin/debounce.rs | 2 +- examples/rp/src/bin/ethernet_w5500_icmp_ping.rs | 4 +- examples/rp/src/bin/ethernet_w55rp20_tcp_server.rs | 2 +- examples/rp/src/bin/interrupt.rs | 2 +- examples/rp/src/bin/multicore.rs | 2 +- examples/rp/src/bin/multiprio.rs | 6 +- examples/rp/src/bin/orchestrate_tasks.rs | 4 +- examples/rp/src/bin/overclock.rs | 2 +- examples/rp/src/bin/overclock_manual.rs | 2 +- examples/rp/src/bin/pio_async.rs | 2 +- examples/rp/src/bin/pio_stepper.rs | 2 +- examples/rp/src/bin/pwm.rs | 4 +- examples/rp/src/bin/rtc_alarm.rs | 2 +- examples/rp/src/bin/sharing.rs | 2 +- examples/rp/src/bin/spi_display.rs | 12 +- examples/rp/src/bin/spi_gc9a01.rs | 4 +- examples/rp/src/bin/uart_r503.rs | 2 +- examples/rp/src/bin/usb_ethernet.rs | 2 +- examples/rp/src/bin/usb_serial.rs | 2 +- examples/rp/src/bin/wifi_ap_tcp_server.rs | 2 +- examples/rp/src/bin/wifi_blinky.rs | 2 +- examples/rp/src/bin/wifi_scan.rs | 2 +- examples/rp/src/bin/wifi_tcp_server.rs | 2 +- examples/rp/src/bin/wifi_webrequest.rs | 2 +- examples/rp/src/bin/zerocopy.rs | 2 +- examples/rp235x/Cargo.toml | 2 +- examples/rp235x/src/bin/assign_resources.rs | 2 +- examples/rp235x/src/bin/debounce.rs | 2 +- .../rp235x/src/bin/ethernet_w5500_icmp_ping.rs | 4 +- examples/rp235x/src/bin/interrupt.rs | 2 +- examples/rp235x/src/bin/multicore.rs | 2 +- .../rp235x/src/bin/multicore_stack_overflow.rs | 2 +- examples/rp235x/src/bin/multiprio.rs | 6 +- examples/rp235x/src/bin/overclock.rs | 2 +- examples/rp235x/src/bin/pio_async.rs | 2 +- examples/rp235x/src/bin/pio_i2s_rx.rs | 2 +- examples/rp235x/src/bin/pio_rotary_encoder_rxf.rs | 2 +- examples/rp235x/src/bin/pio_stepper.rs | 2 +- examples/rp235x/src/bin/pwm.rs | 4 +- .../rp235x/src/bin/pwm_tb6612fng_motor_driver.rs | 2 +- examples/rp235x/src/bin/sharing.rs | 2 +- examples/rp235x/src/bin/spi_display.rs | 12 +- examples/rp235x/src/bin/uart_r503.rs | 2 +- examples/rp235x/src/bin/zerocopy.rs | 2 +- examples/std/Cargo.toml | 2 +- examples/stm32c0/Cargo.toml | 2 +- examples/stm32c0/src/bin/rtc.rs | 2 +- examples/stm32f0/Cargo.toml | 2 +- .../stm32f0/src/bin/button_controlled_blink.rs | 2 +- examples/stm32f0/src/bin/multiprio.rs | 4 +- examples/stm32f1/Cargo.toml | 2 +- examples/stm32f1/src/bin/can.rs | 6 +- examples/stm32f1/src/bin/input_capture.rs | 2 +- examples/stm32f1/src/bin/pwm_input.rs | 2 +- examples/stm32f1/src/bin/usb_serial.rs | 4 +- examples/stm32f2/Cargo.toml | 2 +- examples/stm32f2/src/bin/pll.rs | 2 +- examples/stm32f3/Cargo.toml | 2 +- examples/stm32f3/src/bin/button_events.rs | 2 +- examples/stm32f3/src/bin/multiprio.rs | 4 +- examples/stm32f3/src/bin/usb_serial.rs | 4 +- examples/stm32f334/Cargo.toml | 2 +- examples/stm32f334/src/bin/adc.rs | 2 +- examples/stm32f334/src/bin/opamp.rs | 2 +- examples/stm32f334/src/bin/pwm.rs | 2 +- examples/stm32f4/Cargo.toml | 2 +- examples/stm32f4/src/bin/adc_dma.rs | 2 +- examples/stm32f4/src/bin/eth.rs | 2 +- examples/stm32f4/src/bin/eth_compliance_test.rs | 2 +- examples/stm32f4/src/bin/eth_w5500.rs | 2 +- examples/stm32f4/src/bin/flash_async.rs | 2 +- examples/stm32f4/src/bin/input_capture.rs | 2 +- examples/stm32f4/src/bin/multiprio.rs | 4 +- examples/stm32f4/src/bin/pwm_complementary.rs | 2 +- examples/stm32f4/src/bin/pwm_input.rs | 2 +- examples/stm32f4/src/bin/rtc.rs | 2 +- examples/stm32f4/src/bin/sdmmc.rs | 4 +- examples/stm32f4/src/bin/usb_ethernet.rs | 4 +- examples/stm32f4/src/bin/usb_hid_keyboard.rs | 2 +- examples/stm32f4/src/bin/usb_hid_mouse.rs | 4 +- examples/stm32f4/src/bin/usb_raw.rs | 2 +- examples/stm32f4/src/bin/usb_serial.rs | 4 +- examples/stm32f4/src/bin/usb_uac_speaker.rs | 4 +- examples/stm32f4/src/bin/ws2812_pwm.rs | 2 +- examples/stm32f469/Cargo.toml | 2 +- examples/stm32f469/src/bin/dsi_bsp.rs | 6 +- examples/stm32f7/Cargo.toml | 2 +- examples/stm32f7/src/bin/can.rs | 2 +- examples/stm32f7/src/bin/cryp.rs | 4 +- examples/stm32f7/src/bin/eth.rs | 2 +- examples/stm32f7/src/bin/hash.rs | 2 +- examples/stm32f7/src/bin/qspi.rs | 2 +- examples/stm32f7/src/bin/sdmmc.rs | 4 +- examples/stm32f7/src/bin/usb_serial.rs | 4 +- examples/stm32g0/Cargo.toml | 2 +- examples/stm32g0/src/bin/hf_timer.rs | 4 +- examples/stm32g0/src/bin/input_capture.rs | 4 +- examples/stm32g0/src/bin/pwm_complementary.rs | 2 +- examples/stm32g0/src/bin/pwm_input.rs | 2 +- examples/stm32g0/src/bin/rtc.rs | 2 +- examples/stm32g0/src/bin/usb_serial.rs | 4 +- examples/stm32g4/Cargo.toml | 2 +- examples/stm32g4/src/bin/adc.rs | 2 +- examples/stm32g4/src/bin/adc_differential.rs | 2 +- examples/stm32g4/src/bin/adc_dma.rs | 2 +- examples/stm32g4/src/bin/adc_oversampling.rs | 2 +- examples/stm32g4/src/bin/can.rs | 2 +- examples/stm32g4/src/bin/usb_c_pd.rs | 6 +- examples/stm32g4/src/bin/usb_serial.rs | 4 +- examples/stm32h5/Cargo.toml | 2 +- examples/stm32h5/src/bin/adc.rs | 2 +- examples/stm32h5/src/bin/can.rs | 2 +- examples/stm32h5/src/bin/dts.rs | 2 +- examples/stm32h5/src/bin/eth.rs | 2 +- examples/stm32h5/src/bin/sai.rs | 2 +- examples/stm32h5/src/bin/usb_c_pd.rs | 6 +- examples/stm32h5/src/bin/usb_serial.rs | 4 +- examples/stm32h5/src/bin/usb_uac_speaker.rs | 4 +- examples/stm32h7/Cargo.toml | 2 +- examples/stm32h7/src/bin/adc.rs | 2 +- examples/stm32h7/src/bin/adc_dma.rs | 2 +- examples/stm32h7/src/bin/camera.rs | 2 +- examples/stm32h7/src/bin/can.rs | 2 +- examples/stm32h7/src/bin/dac.rs | 2 +- examples/stm32h7/src/bin/dac_dma.rs | 2 +- examples/stm32h7/src/bin/eth.rs | 2 +- examples/stm32h7/src/bin/eth_client.rs | 4 +- examples/stm32h7/src/bin/eth_client_mii.rs | 4 +- examples/stm32h7/src/bin/fmc.rs | 2 +- examples/stm32h7/src/bin/i2c_shared.rs | 2 +- examples/stm32h7/src/bin/low_level_timer_api.rs | 2 +- examples/stm32h7/src/bin/multiprio.rs | 4 +- examples/stm32h7/src/bin/pwm.rs | 2 +- examples/stm32h7/src/bin/rng.rs | 2 +- examples/stm32h7/src/bin/rtc.rs | 2 +- examples/stm32h7/src/bin/sdmmc.rs | 2 +- examples/stm32h7/src/bin/spi.rs | 2 +- examples/stm32h7/src/bin/spi_bdma.rs | 2 +- examples/stm32h7/src/bin/spi_dma.rs | 2 +- examples/stm32h7/src/bin/usb_serial.rs | 4 +- examples/stm32h723/Cargo.toml | 2 +- examples/stm32h723/src/bin/spdifrx.rs | 4 +- examples/stm32h735/Cargo.toml | 2 +- examples/stm32h735/src/bin/ltdc.rs | 4 +- examples/stm32h742/Cargo.toml | 2 +- examples/stm32h742/src/bin/qspi.rs | 2 +- examples/stm32h755cm4/Cargo.toml | 2 +- examples/stm32h755cm4/src/bin/blinky.rs | 2 +- examples/stm32h755cm4/src/bin/intercore.rs | 6 +- examples/stm32h755cm7/Cargo.toml | 2 +- examples/stm32h755cm7/src/bin/blinky.rs | 2 +- examples/stm32h755cm7/src/bin/intercore.rs | 4 +- examples/stm32h7b0/Cargo.toml | 2 +- examples/stm32h7b0/src/bin/ospi_memory_mapped.rs | 2 +- examples/stm32h7rs/Cargo.toml | 2 +- examples/stm32h7rs/src/bin/blinky.rs | 2 +- examples/stm32h7rs/src/bin/can.rs | 2 +- examples/stm32h7rs/src/bin/eth.rs | 2 +- examples/stm32h7rs/src/bin/multiprio.rs | 4 +- examples/stm32h7rs/src/bin/rng.rs | 2 +- examples/stm32h7rs/src/bin/rtc.rs | 2 +- examples/stm32h7rs/src/bin/usb_serial.rs | 4 +- examples/stm32h7rs/src/bin/xspi_memory_mapped.rs | 2 +- examples/stm32l0/Cargo.toml | 2 +- examples/stm32l0/src/bin/button_exti.rs | 2 +- examples/stm32l0/src/bin/dds.rs | 2 +- examples/stm32l0/src/bin/eeprom.rs | 2 +- examples/stm32l0/src/bin/raw_spawn.rs | 4 +- examples/stm32l0/src/bin/usb_serial.rs | 2 +- examples/stm32l1/Cargo.toml | 2 +- examples/stm32l1/src/bin/eeprom.rs | 2 +- examples/stm32l1/src/bin/usb_serial.rs | 2 +- examples/stm32l4/Cargo.toml | 2 +- examples/stm32l4/src/bin/adc.rs | 2 +- examples/stm32l4/src/bin/can.rs | 2 +- examples/stm32l4/src/bin/dac_dma.rs | 2 +- examples/stm32l4/src/bin/rng.rs | 2 +- examples/stm32l4/src/bin/rtc.rs | 2 +- .../stm32l4/src/bin/spe_adin1110_http_server.rs | 10 +- examples/stm32l4/src/bin/usb_serial.rs | 4 +- examples/stm32l432/Cargo.toml | 2 +- examples/stm32l5/Cargo.toml | 2 +- examples/stm32l5/src/bin/rng.rs | 2 +- examples/stm32l5/src/bin/usb_ethernet.rs | 4 +- examples/stm32l5/src/bin/usb_hid_mouse.rs | 4 +- examples/stm32l5/src/bin/usb_serial.rs | 4 +- examples/stm32u0/Cargo.toml | 2 +- examples/stm32u0/src/bin/adc.rs | 2 +- examples/stm32u0/src/bin/rng.rs | 2 +- examples/stm32u0/src/bin/rtc.rs | 2 +- examples/stm32u0/src/bin/usb_serial.rs | 4 +- examples/stm32u5/Cargo.toml | 2 +- examples/stm32u5/src/bin/adc.rs | 2 +- examples/stm32u5/src/bin/ltdc.rs | 6 +- examples/stm32u5/src/bin/usb_hs_serial.rs | 4 +- examples/stm32u5/src/bin/usb_serial.rs | 4 +- examples/stm32wb/Cargo.toml | 2 +- examples/stm32wb/src/bin/eddystone_beacon.rs | 4 +- 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_mac.rs | 2 +- examples/stm32wba/Cargo.toml | 2 +- examples/stm32wba/src/bin/adc.rs | 2 +- examples/stm32wba/src/bin/pwm.rs | 2 +- examples/stm32wba6/Cargo.toml | 2 +- examples/stm32wba6/src/bin/adc.rs | 2 +- examples/stm32wba6/src/bin/pwm.rs | 2 +- examples/stm32wba6/src/bin/usb_hs_serial.rs | 4 +- examples/stm32wl/Cargo.toml | 2 +- examples/stm32wl/src/bin/adc.rs | 2 +- examples/stm32wl/src/bin/blinky.rs | 2 +- examples/stm32wl/src/bin/button.rs | 2 +- examples/stm32wl/src/bin/button_exti.rs | 2 +- examples/stm32wl/src/bin/flash.rs | 2 +- examples/stm32wl/src/bin/random.rs | 2 +- examples/stm32wl/src/bin/uart_async.rs | 2 +- examples/wasm/Cargo.toml | 2 +- rustfmt.toml | 2 +- tests/mspm0/Cargo.toml | 2 +- tests/mspm0/src/bin/dma.rs | 2 +- tests/nrf/Cargo.toml | 2 +- tests/perf-client/Cargo.toml | 2 +- tests/perf-client/src/lib.rs | 2 +- tests/perf-server/Cargo.toml | 2 +- tests/riscv32/Cargo.toml | 2 +- tests/rp/Cargo.toml | 2 +- tests/rp/src/bin/cyw43-perf.rs | 2 +- tests/rp/src/bin/gpio_multicore.rs | 4 +- tests/rp/src/bin/multicore.rs | 2 +- tests/rp/src/bin/overclock.rs | 2 +- tests/rp/src/bin/pio_multi_load.rs | 4 +- tests/rp/src/bin/rtc.rs | 2 +- tests/rp/src/bin/spinlock_mutex_multicore.rs | 2 +- tests/stm32/Cargo.toml | 2 +- tests/stm32/src/bin/afio.rs | 2 +- tests/stm32/src/bin/cryp.rs | 2 +- tests/stm32/src/bin/fdcan.rs | 2 +- tests/stm32/src/bin/stop.rs | 4 +- tests/stm32/src/bin/ucpd.rs | 2 +- tests/stm32/src/bin/usart.rs | 2 +- tests/stm32/src/bin/wpan_ble.rs | 4 +- tests/stm32/src/bin/wpan_mac.rs | 2 +- tests/stm32/src/common.rs | 2 +- tests/utils/Cargo.toml | 2 +- tests/utils/src/bin/saturate_serial.rs | 2 +- 680 files changed, 2326 insertions(+), 1395 deletions(-) create mode 100644 embassy-mspm0/src/i2c_target.rs create mode 100644 examples/mspm0g3507/src/bin/i2c_target.rs create mode 100644 examples/mspm0l1306/src/bin/i2c_target.rs create mode 100644 examples/nrf5340/src/bin/nrf5340dk_internal_caps.rs create mode 100644 examples/nrf54l15/src/bin/rtc.rs diff --git a/.github/ci/build-nightly.sh b/.github/ci/build-nightly.sh index 8cca1b445..82e9436f3 100755 --- a/.github/ci/build-nightly.sh +++ b/.github/ci/build-nightly.sh @@ -23,7 +23,7 @@ fi hashtime restore /ci/cache/filetime.json || true hashtime save /ci/cache/filetime.json -cargo install --git https://github.com/embassy-rs/cargo-embassy-devtool --locked --rev 676e6d602bf016dc71f1e98f2c7f191d7bd20707 +cargo install --git https://github.com/embassy-rs/cargo-embassy-devtool --locked --rev d015fd5e972a3e550ebef0da6748099b88a93ba6 ./ci-nightly.sh diff --git a/.github/ci/build-xtensa.sh b/.github/ci/build-xtensa.sh index dbd2f7ffc..3f74b4a5a 100755 --- a/.github/ci/build-xtensa.sh +++ b/.github/ci/build-xtensa.sh @@ -25,7 +25,7 @@ fi hashtime restore /ci/cache/filetime.json || true hashtime save /ci/cache/filetime.json -cargo install --git https://github.com/embassy-rs/cargo-embassy-devtool --locked --rev 676e6d602bf016dc71f1e98f2c7f191d7bd20707 +cargo install --git https://github.com/embassy-rs/cargo-embassy-devtool --locked --rev d015fd5e972a3e550ebef0da6748099b88a93ba6 ./ci-xtensa.sh diff --git a/.github/ci/build.sh b/.github/ci/build.sh index d5e0e0bd2..3c196f72b 100755 --- a/.github/ci/build.sh +++ b/.github/ci/build.sh @@ -28,7 +28,7 @@ fi hashtime restore /ci/cache/filetime.json || true hashtime save /ci/cache/filetime.json -cargo install --git https://github.com/embassy-rs/cargo-embassy-devtool --locked --rev 676e6d602bf016dc71f1e98f2c7f191d7bd20707 +cargo install --git https://github.com/embassy-rs/cargo-embassy-devtool --locked --rev d015fd5e972a3e550ebef0da6748099b88a93ba6 ./ci.sh diff --git a/.github/ci/doc.sh b/.github/ci/doc.sh index dab47e86d..535fc5262 100755 --- a/.github/ci/doc.sh +++ b/.github/ci/doc.sh @@ -12,7 +12,7 @@ export CARGO_TARGET_DIR=/ci/cache/target export PATH=$CARGO_HOME/bin:$PATH mv rust-toolchain-nightly.toml rust-toolchain.toml -cargo install --git https://github.com/embassy-rs/cargo-embassy-devtool --locked --rev 676e6d602bf016dc71f1e98f2c7f191d7bd20707 +cargo install --git https://github.com/embassy-rs/cargo-embassy-devtool --locked --rev d015fd5e972a3e550ebef0da6748099b88a93ba6 cargo embassy-devtool doc -o webroot diff --git a/.github/ci/janitor.sh b/.github/ci/janitor.sh index 305c6b227..bc43075bd 100755 --- a/.github/ci/janitor.sh +++ b/.github/ci/janitor.sh @@ -9,7 +9,7 @@ export CARGO_HOME=/ci/cache/cargo export CARGO_TARGET_DIR=/ci/cache/target export PATH=$CARGO_HOME/bin:$PATH -cargo install --git https://github.com/embassy-rs/cargo-embassy-devtool --locked --rev 676e6d602bf016dc71f1e98f2c7f191d7bd20707 +cargo install --git https://github.com/embassy-rs/cargo-embassy-devtool --locked --rev d015fd5e972a3e550ebef0da6748099b88a93ba6 cargo embassy-devtool check-crlf cargo embassy-devtool check-manifest diff --git a/.github/ci/rustfmt.sh b/.github/ci/rustfmt.sh index 369239cfe..7aaf93234 100755 --- a/.github/ci/rustfmt.sh +++ b/.github/ci/rustfmt.sh @@ -9,4 +9,4 @@ export CARGO_HOME=/ci/cache/cargo export CARGO_TARGET_DIR=/ci/cache/target mv rust-toolchain-nightly.toml rust-toolchain.toml -find . -name '*.rs' -not -path '*target*' | xargs rustfmt --check --skip-children --unstable-features --edition 2021 +find . -name '*.rs' -not -path '*target*' | xargs rustfmt --check --skip-children --unstable-features --edition 2024 diff --git a/cyw43-pio/Cargo.toml b/cyw43-pio/Cargo.toml index 6ab5c453e..a10a091e9 100644 --- a/cyw43-pio/Cargo.toml +++ b/cyw43-pio/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "cyw43-pio" version = "0.8.0" -edition = "2021" +edition = "2024" description = "RP2040 PIO SPI implementation for cyw43" keywords = ["embedded", "cyw43", "embassy-net", "embedded-hal-async", "wifi"] categories = ["embedded", "hardware-support", "no-std", "network-programming", "asynchronous"] diff --git a/cyw43-pio/src/lib.rs b/cyw43-pio/src/lib.rs index b0be19358..41ac6816d 100644 --- a/cyw43-pio/src/lib.rs +++ b/cyw43-pio/src/lib.rs @@ -6,13 +6,13 @@ use core::slice; use cyw43::SpiBusCyw43; +use embassy_rp::Peri; use embassy_rp::dma::Channel; use embassy_rp::gpio::{Drive, Level, Output, Pull, SlewRate}; use embassy_rp::pio::program::pio_asm; use embassy_rp::pio::{Common, Config, Direction, Instance, Irq, PioPin, ShiftDirection, StateMachine}; -use embassy_rp::Peri; -use fixed::types::extra::U8; use fixed::FixedU32; +use fixed::types::extra::U8; /// SPI comms driven by PIO. pub struct PioSpi<'d, PIO: Instance, const SM: usize, DMA: Channel> { diff --git a/cyw43/Cargo.toml b/cyw43/Cargo.toml index c59c15a71..314427611 100644 --- a/cyw43/Cargo.toml +++ b/cyw43/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "cyw43" version = "0.5.0" -edition = "2021" +edition = "2024" description = "Rust driver for the CYW43439 WiFi chip, used in the Raspberry Pi Pico W." keywords = ["embedded", "cyw43", "embassy-net", "embedded-hal-async", "wifi"] categories = ["embedded", "hardware-support", "no-std", "network-programming", "asynchronous"] diff --git a/cyw43/src/bluetooth.rs b/cyw43/src/bluetooth.rs index d176c4b09..332b7048d 100644 --- a/cyw43/src/bluetooth.rs +++ b/cyw43/src/bluetooth.rs @@ -15,7 +15,7 @@ use crate::bus::Bus; pub use crate::bus::SpiBusCyw43; use crate::consts::*; use crate::util::round_up; -use crate::{util, CHIP}; +use crate::{CHIP, util}; pub(crate) struct BtState { rx: [BtPacketBuf; 4], diff --git a/cyw43/src/bus.rs b/cyw43/src/bus.rs index 8a53484d5..aa2b66a40 100644 --- a/cyw43/src/bus.rs +++ b/cyw43/src/bus.rs @@ -340,11 +340,7 @@ where self.status = self.spi.cmd_read(cmd, &mut buf[..len]).await; // if we read from the backplane, the result is in the second word, after the response delay - if func == FUNC_BACKPLANE { - buf[1] - } else { - buf[0] - } + if func == FUNC_BACKPLANE { buf[1] } else { buf[0] } } async fn writen(&mut self, func: u32, addr: u32, val: u32, len: u32) { diff --git a/cyw43/src/control.rs b/cyw43/src/control.rs index fd0d4d532..49e3faee4 100644 --- a/cyw43/src/control.rs +++ b/cyw43/src/control.rs @@ -10,7 +10,7 @@ use crate::events::{Event, EventSubscriber, Events}; use crate::fmt::Bytes; use crate::ioctl::{IoctlState, IoctlType}; use crate::structs::*; -use crate::{countries, events, PowerManagementMode}; +use crate::{PowerManagementMode, countries, events}; /// Control errors. #[derive(Debug)] diff --git a/cyw43/src/ioctl.rs b/cyw43/src/ioctl.rs index 35135e296..deccc945d 100644 --- a/cyw43/src/ioctl.rs +++ b/cyw43/src/ioctl.rs @@ -1,5 +1,5 @@ use core::cell::{Cell, RefCell}; -use core::future::{poll_fn, Future}; +use core::future::{Future, poll_fn}; use core::task::{Poll, Waker}; use embassy_sync::waitqueue::WakerRegistration; diff --git a/cyw43/src/lib.rs b/cyw43/src/lib.rs index 16b436e66..82c636346 100644 --- a/cyw43/src/lib.rs +++ b/cyw43/src/lib.rs @@ -1,6 +1,7 @@ #![no_std] #![no_main] #![allow(async_fn_in_trait)] +#![allow(unsafe_op_in_unsafe_fn)] #![deny(unused_must_use)] #![doc = include_str!("../README.md")] #![warn(missing_docs)] diff --git a/cyw43/src/runner.rs b/cyw43/src/runner.rs index 77910b281..7c38be24a 100644 --- a/cyw43/src/runner.rs +++ b/cyw43/src/runner.rs @@ -1,6 +1,6 @@ -use embassy_futures::select::{select4, Either4}; +use embassy_futures::select::{Either4, select4}; use embassy_net_driver_channel as ch; -use embassy_time::{block_for, Duration, Timer}; +use embassy_time::{Duration, Timer, block_for}; use embedded_hal_1::digital::OutputPin; use crate::bus::Bus; @@ -12,7 +12,7 @@ use crate::ioctl::{IoctlState, IoctlType, PendingIoctl}; use crate::nvram::NVRAM; use crate::structs::*; use crate::util::slice8_mut; -use crate::{events, Core, CHIP, MTU}; +use crate::{CHIP, Core, MTU, events}; #[cfg(feature = "firmware-logs")] struct LogState { diff --git a/docs/examples/layer-by-layer/blinky-async/Cargo.toml b/docs/examples/layer-by-layer/blinky-async/Cargo.toml index ec718022c..797ae3097 100644 --- a/docs/examples/layer-by-layer/blinky-async/Cargo.toml +++ b/docs/examples/layer-by-layer/blinky-async/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "blinky-async" version = "0.1.0" -edition = "2021" +edition = "2024" license = "MIT OR Apache-2.0" publish = false diff --git a/docs/examples/layer-by-layer/blinky-hal/Cargo.toml b/docs/examples/layer-by-layer/blinky-hal/Cargo.toml index 4a0b03a83..802b8b32e 100644 --- a/docs/examples/layer-by-layer/blinky-hal/Cargo.toml +++ b/docs/examples/layer-by-layer/blinky-hal/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "blinky-hal" version = "0.1.0" -edition = "2021" +edition = "2024" license = "MIT OR Apache-2.0" publish = false diff --git a/docs/examples/layer-by-layer/blinky-irq/Cargo.toml b/docs/examples/layer-by-layer/blinky-irq/Cargo.toml index 3c4bf8446..d1b893a1e 100644 --- a/docs/examples/layer-by-layer/blinky-irq/Cargo.toml +++ b/docs/examples/layer-by-layer/blinky-irq/Cargo.toml @@ -3,7 +3,7 @@ [package] name = "blinky-irq" version = "0.1.0" -edition = "2021" +edition = "2024" license = "MIT OR Apache-2.0" publish = false diff --git a/docs/examples/layer-by-layer/blinky-pac/Cargo.toml b/docs/examples/layer-by-layer/blinky-pac/Cargo.toml index 0d4711da2..fa093a3af 100644 --- a/docs/examples/layer-by-layer/blinky-pac/Cargo.toml +++ b/docs/examples/layer-by-layer/blinky-pac/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "blinky-pac" version = "0.1.0" -edition = "2021" +edition = "2024" license = "MIT OR Apache-2.0" publish = false diff --git a/docs/pages/embassy_in_the_wild.adoc b/docs/pages/embassy_in_the_wild.adoc index cedbedada..3b58bb9b4 100644 --- a/docs/pages/embassy_in_the_wild.adoc +++ b/docs/pages/embassy_in_the_wild.adoc @@ -4,6 +4,8 @@ Here are known examples of real-world projects which make use of Embassy. Feel f _newer entries at the top_ +* link:https://github.com/1-rafael-1/air-quality-monitor[Air Quality Monitor] +** Air Quality Monitor based on rp2350 board, ens160 and aht21 sensors and ssd1306 display. Code and 3D printable enclosure included. * link:https://github.com/CarlKCarlK/clock[Embassy Clock: Layered, modular bare-metal clock with emulation] ** A `no_std` Raspberry Pi Pico clock demonstrating layered Embassy tasks (Display->Blinker->Clock) for clean separation of multiplexing, blinking, and UI logic. Features single-button HH:MM/MM:SS time-set UI, heapless data structures, and a Renode emulator for hardware-free testing. See link:https://medium.com/@carlmkadie/how-rust-embassy-shine-on-embedded-devices-part-2-aad1adfccf72[this article] for details. * link:https://github.com/1-rafael-1/simple-robot[A simple tracked robot based on Raspberry Pi Pico 2] @@ -11,7 +13,7 @@ _newer entries at the top_ * link:https://github.com/1-rafael-1/pi-pico-alarmclock-rust[A Raspberry Pi Pico W Alarmclock] ** A hobbyist project building an alarm clock around a Pi Pico W complete with code, components list and enclosure design files. * link:https://github.com/haobogu/rmk/[RMK: A feature-rich Rust keyboard firmware] -** RMK has built-in layer support, wireless(BLE) support, real-time key editing support using vial, and more! +** RMK has built-in layer support, wireless(BLE) support, real-time key editing support using vial, and more! ** Targets STM32, RP2040, nRF52 and ESP32 MCUs * link:https://github.com/cbruiz/printhor/[Printhor: The highly reliable but not necessarily functional 3D printer firmware] ** Targets some STM32 MCUs @@ -21,10 +23,9 @@ _newer entries at the top_ * link:https://github.com/matoushybl/air-force-one[Air force one: A simple air quality monitoring system] ** Targets nRF52 and uses nrf-softdevice -* link:https://github.com/schmettow/ylab-edge-go[YLab Edge Go] and link:https://github.com/schmettow/ylab-edge-pro[YLab Edge Pro] projects develop +* link:https://github.com/schmettow/ylab-edge-go[YLab Edge Go] and link:https://github.com/schmettow/ylab-edge-pro[YLab Edge Pro] projects develop firmware (RP2040, STM32) for capturing physiological data in behavioural science research. Included so far are: ** biopotentials (analog ports) ** motion capture (6-axis accelerometers) ** air quality (CO2, Temp, Humidity) ** comes with an app for capturing and visualizing data [link:https://github.com/schmettow/ystudio-zero[Ystudio]] - diff --git a/embassy-boot-nrf/Cargo.toml b/embassy-boot-nrf/Cargo.toml index 466f18631..787a28d70 100644 --- a/embassy-boot-nrf/Cargo.toml +++ b/embassy-boot-nrf/Cargo.toml @@ -1,5 +1,5 @@ [package] -edition = "2021" +edition = "2024" name = "embassy-boot-nrf" version = "0.9.0" description = "Bootloader lib for nRF chips" diff --git a/embassy-boot-nrf/src/lib.rs b/embassy-boot-nrf/src/lib.rs index f1c9da080..b4c3c1151 100644 --- a/embassy-boot-nrf/src/lib.rs +++ b/embassy-boot-nrf/src/lib.rs @@ -1,4 +1,5 @@ #![no_std] +#![allow(unsafe_op_in_unsafe_fn)] #![warn(missing_docs)] #![doc = include_str!("../README.md")] mod fmt; @@ -8,7 +9,7 @@ pub use embassy_boot::{ FirmwareUpdater, FirmwareUpdaterConfig, }; use embassy_nrf::nvmc::PAGE_SIZE; -use embassy_nrf::{wdt, Peri}; +use embassy_nrf::{Peri, wdt}; use embedded_storage::nor_flash::{ErrorType, NorFlash, ReadNorFlash}; /// A bootloader for nRF devices. diff --git a/embassy-boot-rp/Cargo.toml b/embassy-boot-rp/Cargo.toml index 894b77595..e80c79374 100644 --- a/embassy-boot-rp/Cargo.toml +++ b/embassy-boot-rp/Cargo.toml @@ -1,5 +1,5 @@ [package] -edition = "2021" +edition = "2024" name = "embassy-boot-rp" version = "0.8.0" description = "Bootloader lib for RP2040 chips" diff --git a/embassy-boot-rp/src/lib.rs b/embassy-boot-rp/src/lib.rs index f704380ef..d090aee49 100644 --- a/embassy-boot-rp/src/lib.rs +++ b/embassy-boot-rp/src/lib.rs @@ -1,4 +1,5 @@ #![no_std] +#![allow(unsafe_op_in_unsafe_fn)] #![warn(missing_docs)] #![doc = include_str!("../README.md")] mod fmt; @@ -7,10 +8,10 @@ pub use embassy_boot::{ AlignedBuffer, BlockingFirmwareState, BlockingFirmwareUpdater, BootError, BootLoaderConfig, FirmwareState, FirmwareUpdater, FirmwareUpdaterConfig, State, }; -use embassy_rp::flash::{Blocking, Flash, ERASE_SIZE}; +use embassy_rp::Peri; +use embassy_rp::flash::{Blocking, ERASE_SIZE, Flash}; use embassy_rp::peripherals::{FLASH, WATCHDOG}; use embassy_rp::watchdog::Watchdog; -use embassy_rp::Peri; use embassy_time::Duration; use embedded_storage::nor_flash::{ErrorType, NorFlash, ReadNorFlash}; diff --git a/embassy-boot-stm32/Cargo.toml b/embassy-boot-stm32/Cargo.toml index 24eafcdbf..c8c7f4409 100644 --- a/embassy-boot-stm32/Cargo.toml +++ b/embassy-boot-stm32/Cargo.toml @@ -1,5 +1,5 @@ [package] -edition = "2021" +edition = "2024" name = "embassy-boot-stm32" version = "0.6.0" description = "Bootloader lib for STM32 chips" diff --git a/embassy-boot-stm32/src/lib.rs b/embassy-boot-stm32/src/lib.rs index 387cc0ce5..ee6ae41b7 100644 --- a/embassy-boot-stm32/src/lib.rs +++ b/embassy-boot-stm32/src/lib.rs @@ -1,4 +1,5 @@ #![no_std] +#![allow(unsafe_op_in_unsafe_fn)] #![warn(missing_docs)] #![doc = include_str!("../README.md")] mod fmt; diff --git a/embassy-boot/Cargo.toml b/embassy-boot/Cargo.toml index a18438c81..8c5c1f633 100644 --- a/embassy-boot/Cargo.toml +++ b/embassy-boot/Cargo.toml @@ -1,5 +1,5 @@ [package] -edition = "2021" +edition = "2024" name = "embassy-boot" version = "0.6.1" description = "A lightweight bootloader supporting firmware updates in a power-fail-safe way, with trial boots and rollbacks." diff --git a/embassy-boot/src/boot_loader.rs b/embassy-boot/src/boot_loader.rs index 5bffdc5ea..c38940d6e 100644 --- a/embassy-boot/src/boot_loader.rs +++ b/embassy-boot/src/boot_loader.rs @@ -1,11 +1,11 @@ use core::cell::RefCell; use embassy_embedded_hal::flash::partition::BlockingPartition; -use embassy_sync::blocking_mutex::raw::NoopRawMutex; use embassy_sync::blocking_mutex::Mutex; +use embassy_sync::blocking_mutex::raw::NoopRawMutex; use embedded_storage::nor_flash::{NorFlash, NorFlashError, NorFlashErrorKind}; -use crate::{State, DFU_DETACH_MAGIC, REVERT_MAGIC, STATE_ERASE_VALUE, SWAP_MAGIC}; +use crate::{DFU_DETACH_MAGIC, REVERT_MAGIC, STATE_ERASE_VALUE, SWAP_MAGIC, State}; /// Errors returned by bootloader #[derive(PartialEq, Eq, Debug)] @@ -94,7 +94,7 @@ impl<'a, ACTIVE: NorFlash, DFU: NorFlash, STATE: NorFlash> dfu_flash: &'a Mutex>, state_flash: &'a Mutex>, ) -> Self { - extern "C" { + unsafe extern "C" { static __bootloader_state_start: u32; static __bootloader_state_end: u32; static __bootloader_active_start: u32; diff --git a/embassy-boot/src/firmware_updater/asynch.rs b/embassy-boot/src/firmware_updater/asynch.rs index 66e311e38..26d4d39bd 100644 --- a/embassy-boot/src/firmware_updater/asynch.rs +++ b/embassy-boot/src/firmware_updater/asynch.rs @@ -6,7 +6,7 @@ use embassy_sync::blocking_mutex::raw::NoopRawMutex; use embedded_storage_async::nor_flash::NorFlash; use super::FirmwareUpdaterConfig; -use crate::{FirmwareUpdaterError, State, BOOT_MAGIC, DFU_DETACH_MAGIC, STATE_ERASE_VALUE, SWAP_MAGIC}; +use crate::{BOOT_MAGIC, DFU_DETACH_MAGIC, FirmwareUpdaterError, STATE_ERASE_VALUE, SWAP_MAGIC, State}; /// FirmwareUpdater is an application API for interacting with the BootLoader without the ability to /// 'mess up' the internal bootloader state @@ -25,7 +25,7 @@ impl<'a, DFU: NorFlash, STATE: NorFlash> dfu_flash: &'a embassy_sync::mutex::Mutex, state_flash: &'a embassy_sync::mutex::Mutex, ) -> Self { - extern "C" { + unsafe extern "C" { static __bootloader_state_start: u32; static __bootloader_state_end: u32; static __bootloader_dfu_start: u32; diff --git a/embassy-boot/src/firmware_updater/blocking.rs b/embassy-boot/src/firmware_updater/blocking.rs index 0fedac1ea..5554025fc 100644 --- a/embassy-boot/src/firmware_updater/blocking.rs +++ b/embassy-boot/src/firmware_updater/blocking.rs @@ -6,7 +6,7 @@ use embassy_sync::blocking_mutex::raw::NoopRawMutex; use embedded_storage::nor_flash::NorFlash; use super::FirmwareUpdaterConfig; -use crate::{FirmwareUpdaterError, State, BOOT_MAGIC, DFU_DETACH_MAGIC, STATE_ERASE_VALUE, SWAP_MAGIC}; +use crate::{BOOT_MAGIC, DFU_DETACH_MAGIC, FirmwareUpdaterError, STATE_ERASE_VALUE, SWAP_MAGIC, State}; /// Blocking FirmwareUpdater is an application API for interacting with the BootLoader without the ability to /// 'mess up' the internal bootloader state @@ -55,7 +55,7 @@ impl<'a, DFU: NorFlash, STATE: NorFlash> dfu_flash: &'a embassy_sync::blocking_mutex::Mutex>, state_flash: &'a embassy_sync::blocking_mutex::Mutex>, ) -> Self { - extern "C" { + unsafe extern "C" { static __bootloader_state_start: u32; static __bootloader_state_end: u32; static __bootloader_dfu_start: u32; @@ -399,8 +399,8 @@ mod tests { use core::cell::RefCell; use embassy_embedded_hal::flash::partition::BlockingPartition; - use embassy_sync::blocking_mutex::raw::NoopRawMutex; use embassy_sync::blocking_mutex::Mutex; + use embassy_sync::blocking_mutex::raw::NoopRawMutex; use sha1::{Digest, Sha1}; use super::*; diff --git a/embassy-boot/src/lib.rs b/embassy-boot/src/lib.rs index e2c4cf771..7dc811f66 100644 --- a/embassy-boot/src/lib.rs +++ b/embassy-boot/src/lib.rs @@ -1,5 +1,6 @@ #![no_std] #![allow(async_fn_in_trait)] +#![allow(unsafe_op_in_unsafe_fn)] #![warn(missing_docs)] #![doc = include_str!("../README.md")] mod fmt; @@ -341,11 +342,13 @@ mod tests { &mut aligned, ); - assert!(block_on(updater.verify_and_mark_updated( - &public_key.to_bytes(), - &signature.to_bytes(), - firmware_len as u32, - )) - .is_ok()); + assert!( + block_on(updater.verify_and_mark_updated( + &public_key.to_bytes(), + &signature.to_bytes(), + firmware_len as u32, + )) + .is_ok() + ); } } diff --git a/embassy-boot/src/test_flash/blocking.rs b/embassy-boot/src/test_flash/blocking.rs index 5ec476c65..7334346fd 100644 --- a/embassy-boot/src/test_flash/blocking.rs +++ b/embassy-boot/src/test_flash/blocking.rs @@ -1,8 +1,8 @@ use core::cell::RefCell; use embassy_embedded_hal::flash::partition::BlockingPartition; -use embassy_sync::blocking_mutex::raw::NoopRawMutex; use embassy_sync::blocking_mutex::Mutex; +use embassy_sync::blocking_mutex::raw::NoopRawMutex; use embedded_storage::nor_flash::NorFlash; use crate::BootLoaderConfig; diff --git a/embassy-embedded-hal/Cargo.toml b/embassy-embedded-hal/Cargo.toml index 8b8122567..c9551257a 100644 --- a/embassy-embedded-hal/Cargo.toml +++ b/embassy-embedded-hal/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "embassy-embedded-hal" version = "0.5.0" -edition = "2021" +edition = "2024" license = "MIT OR Apache-2.0" description = "Collection of utilities to use `embedded-hal` and `embedded-storage` traits with Embassy." repository = "https://github.com/embassy-rs/embassy" diff --git a/embassy-embedded-hal/src/flash/partition/blocking.rs b/embassy-embedded-hal/src/flash/partition/blocking.rs index cb30290a8..bd9329e2d 100644 --- a/embassy-embedded-hal/src/flash/partition/blocking.rs +++ b/embassy-embedded-hal/src/flash/partition/blocking.rs @@ -1,7 +1,7 @@ use core::cell::RefCell; -use embassy_sync::blocking_mutex::raw::RawMutex; use embassy_sync::blocking_mutex::Mutex; +use embassy_sync::blocking_mutex::raw::RawMutex; use embedded_storage::nor_flash::{ErrorType, MultiwriteNorFlash, NorFlash, ReadNorFlash}; use super::Error; diff --git a/embassy-embedded-hal/src/shared_bus/asynch/i2c.rs b/embassy-embedded-hal/src/shared_bus/asynch/i2c.rs index 48246270e..9c7d3ee71 100644 --- a/embassy-embedded-hal/src/shared_bus/asynch/i2c.rs +++ b/embassy-embedded-hal/src/shared_bus/asynch/i2c.rs @@ -26,8 +26,8 @@ use embassy_sync::blocking_mutex::raw::RawMutex; use embassy_sync::mutex::Mutex; use embedded_hal_async::i2c; -use crate::shared_bus::I2cDeviceError; use crate::SetConfig; +use crate::shared_bus::I2cDeviceError; /// I2C device on a shared bus. pub struct I2cDevice<'a, M: RawMutex, BUS> { diff --git a/embassy-embedded-hal/src/shared_bus/asynch/spi.rs b/embassy-embedded-hal/src/shared_bus/asynch/spi.rs index 0faefbc1e..869a78164 100644 --- a/embassy-embedded-hal/src/shared_bus/asynch/spi.rs +++ b/embassy-embedded-hal/src/shared_bus/asynch/spi.rs @@ -32,8 +32,8 @@ use embedded_hal_1::digital::OutputPin; use embedded_hal_1::spi::Operation; use embedded_hal_async::spi; -use crate::shared_bus::SpiDeviceError; use crate::SetConfig; +use crate::shared_bus::SpiDeviceError; /// SPI device on a shared bus. pub struct SpiDevice<'a, M: RawMutex, BUS, CS> { diff --git a/embassy-embedded-hal/src/shared_bus/blocking/i2c.rs b/embassy-embedded-hal/src/shared_bus/blocking/i2c.rs index dc634a209..49b50f8c1 100644 --- a/embassy-embedded-hal/src/shared_bus/blocking/i2c.rs +++ b/embassy-embedded-hal/src/shared_bus/blocking/i2c.rs @@ -17,12 +17,12 @@ use core::cell::RefCell; -use embassy_sync::blocking_mutex::raw::RawMutex; use embassy_sync::blocking_mutex::Mutex; +use embassy_sync::blocking_mutex::raw::RawMutex; use embedded_hal_1::i2c::{ErrorType, I2c, Operation}; -use crate::shared_bus::I2cDeviceError; use crate::SetConfig; +use crate::shared_bus::I2cDeviceError; /// I2C device on a shared bus. pub struct I2cDevice<'a, M: RawMutex, BUS> { diff --git a/embassy-embedded-hal/src/shared_bus/blocking/spi.rs b/embassy-embedded-hal/src/shared_bus/blocking/spi.rs index ffe2aa1c6..48fe2f4c4 100644 --- a/embassy-embedded-hal/src/shared_bus/blocking/spi.rs +++ b/embassy-embedded-hal/src/shared_bus/blocking/spi.rs @@ -19,13 +19,13 @@ use core::cell::RefCell; -use embassy_sync::blocking_mutex::raw::RawMutex; use embassy_sync::blocking_mutex::Mutex; +use embassy_sync::blocking_mutex::raw::RawMutex; use embedded_hal_1::digital::OutputPin; use embedded_hal_1::spi::{self, Operation, SpiBus}; -use crate::shared_bus::SpiDeviceError; use crate::SetConfig; +use crate::shared_bus::SpiDeviceError; /// SPI device on a shared bus. pub struct SpiDevice<'a, M: RawMutex, BUS, CS> { diff --git a/embassy-executor-macros/Cargo.toml b/embassy-executor-macros/Cargo.toml index 9c2b40d03..3eeed5e4d 100644 --- a/embassy-executor-macros/Cargo.toml +++ b/embassy-executor-macros/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "embassy-executor-macros" version = "0.7.0" -edition = "2021" +edition = "2024" license = "MIT OR Apache-2.0" description = "macros for creating the entry point and tasks for embassy-executor" repository = "https://github.com/embassy-rs/embassy" diff --git a/embassy-executor-macros/src/macros/main.rs b/embassy-executor-macros/src/macros/main.rs index dc470e51c..c259c003f 100644 --- a/embassy-executor-macros/src/macros/main.rs +++ b/embassy-executor-macros/src/macros/main.rs @@ -1,7 +1,7 @@ use std::str::FromStr; -use darling::export::NestedMeta; use darling::FromMeta; +use darling::export::NestedMeta; use proc_macro2::TokenStream; use quote::quote; use syn::{ReturnType, Type}; @@ -183,7 +183,7 @@ For example: `#[embassy_executor::main(entry = ..., executor = \"some_crate::Exe quote!(!), quote! { unsafe fn __make_static(t: &mut T) -> &'static mut T { - ::core::mem::transmute(t) + unsafe { ::core::mem::transmute(t) } } let mut executor = #executor::new(); diff --git a/embassy-executor-macros/src/macros/task.rs b/embassy-executor-macros/src/macros/task.rs index 755948882..8ce8d6726 100644 --- a/embassy-executor-macros/src/macros/task.rs +++ b/embassy-executor-macros/src/macros/task.rs @@ -1,7 +1,7 @@ use std::str::FromStr; -use darling::export::NestedMeta; use darling::FromMeta; +use darling::export::NestedMeta; use proc_macro2::{Span, TokenStream}; use quote::{format_ident, quote}; use syn::visit::{self, Visit}; @@ -287,7 +287,11 @@ fn check_arg_ty(errors: &mut TokenStream, ty: &Type) { } fn visit_type_impl_trait(&mut self, i: &'ast syn::TypeImplTrait) { - error(self.errors, i, "`impl Trait` is not allowed in task arguments. It is syntax sugar for generics, and tasks can't be generic."); + error( + self.errors, + i, + "`impl Trait` is not allowed in task arguments. It is syntax sugar for generics, and tasks can't be generic.", + ); } } diff --git a/embassy-executor-macros/src/util.rs b/embassy-executor-macros/src/util.rs index ebd032a62..5a13f2121 100644 --- a/embassy-executor-macros/src/util.rs +++ b/embassy-executor-macros/src/util.rs @@ -3,7 +3,7 @@ use std::fmt::Display; use proc_macro2::{TokenStream, TokenTree}; use quote::{ToTokens, TokenStreamExt}; use syn::parse::{Parse, ParseStream}; -use syn::{braced, bracketed, token, AttrStyle, Attribute, Signature, Token, Visibility}; +use syn::{AttrStyle, Attribute, Signature, Token, Visibility, braced, bracketed, token}; pub fn token_stream_with_error(mut tokens: TokenStream, error: syn::Error) -> TokenStream { tokens.extend(error.into_compile_error()); diff --git a/embassy-executor-timer-queue/Cargo.toml b/embassy-executor-timer-queue/Cargo.toml index a0ac44420..6770ab26e 100644 --- a/embassy-executor-timer-queue/Cargo.toml +++ b/embassy-executor-timer-queue/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "embassy-executor-timer-queue" version = "0.1.0" -edition = "2021" +edition = "2024" description = "Timer queue item and interface between embassy-executor and timer queues" repository = "https://github.com/embassy-rs/embassy" documentation = "https://docs.embassy.dev/embassy-executor-timer-queue" diff --git a/embassy-executor/CHANGELOG.md b/embassy-executor/CHANGELOG.md index 6f079a11a..47a8ae995 100644 --- a/embassy-executor/CHANGELOG.md +++ b/embassy-executor/CHANGELOG.md @@ -13,6 +13,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Upgraded rtos-trace - Added optional "highest priority" scheduling - Added optional "earliest deadline first" EDF scheduling +- Bump `cortex-ar` to v0.3 ## 0.9.1 - 2025-08-31 diff --git a/embassy-executor/Cargo.toml b/embassy-executor/Cargo.toml index 61d060630..ecc4b6338 100644 --- a/embassy-executor/Cargo.toml +++ b/embassy-executor/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "embassy-executor" version = "0.9.1" -edition = "2021" +edition = "2024" license = "MIT OR Apache-2.0" description = "async/await executor designed for embedded usage" repository = "https://github.com/embassy-rs/embassy" @@ -30,6 +30,8 @@ build = [ {target = "thumbv7em-none-eabi", features = ["arch-cortex-m", "executor-interrupt", "executor-thread", "embassy-time-driver", "scheduler-deadline"]}, {target = "thumbv7em-none-eabi", features = ["arch-cortex-m", "executor-interrupt", "executor-thread", "scheduler-priority", "scheduler-deadline"]}, {target = "thumbv7em-none-eabi", features = ["arch-cortex-m", "executor-interrupt", "executor-thread", "scheduler-deadline"]}, + {target = "thumbv7em-none-eabi", features = ["arch-spin"]}, + {target = "thumbv7em-none-eabi", features = ["arch-spin", "scheduler-deadline"]}, {target = "armv7a-none-eabi", features = ["arch-cortex-ar", "executor-thread"]}, {target = "armv7r-none-eabi", features = ["arch-cortex-ar", "executor-thread"]}, {target = "armv7r-none-eabihf", features = ["arch-cortex-ar", "executor-thread"]}, @@ -98,7 +100,7 @@ portable-atomic = { version = "1.5", optional = true } cortex-m = { version = "0.7.6", optional = true } # arch-cortex-ar dependencies -cortex-ar = { version = "0.2", optional = true } +cortex-ar = { version = "0.3", optional = true } # arch-wasm dependencies wasm-bindgen = { version = "0.2.82", optional = true } diff --git a/embassy-executor/src/arch/avr.rs b/embassy-executor/src/arch/avr.rs index 70085d04d..a841afe15 100644 --- a/embassy-executor/src/arch/avr.rs +++ b/embassy-executor/src/arch/avr.rs @@ -10,11 +10,11 @@ mod thread { pub use embassy_executor_macros::main_avr as main; use portable_atomic::{AtomicBool, Ordering}; - use crate::{raw, Spawner}; + use crate::{Spawner, raw}; static SIGNAL_WORK_THREAD_MODE: AtomicBool = AtomicBool::new(false); - #[export_name = "__pender"] + #[unsafe(export_name = "__pender")] fn __pender(_context: *mut ()) { SIGNAL_WORK_THREAD_MODE.store(true, Ordering::SeqCst); } diff --git a/embassy-executor/src/arch/cortex_ar.rs b/embassy-executor/src/arch/cortex_ar.rs index f9e2f3f7c..a9be3d323 100644 --- a/embassy-executor/src/arch/cortex_ar.rs +++ b/embassy-executor/src/arch/cortex_ar.rs @@ -1,7 +1,7 @@ #[cfg(feature = "executor-interrupt")] compile_error!("`executor-interrupt` is not supported with `arch-cortex-ar`."); -#[export_name = "__pender"] +#[unsafe(export_name = "__pender")] #[cfg(any(feature = "executor-thread", feature = "executor-interrupt"))] fn __pender(context: *mut ()) { // `context` is always `usize::MAX` created by `Executor::run`. @@ -26,7 +26,7 @@ mod thread { use cortex_ar::asm::wfe; pub use embassy_executor_macros::main_cortex_ar as main; - use crate::{raw, Spawner}; + use crate::{Spawner, raw}; /// Thread mode executor, using WFE/SEV. /// diff --git a/embassy-executor/src/arch/cortex_m.rs b/embassy-executor/src/arch/cortex_m.rs index 1c9ddd8a0..1ce96d1d5 100644 --- a/embassy-executor/src/arch/cortex_m.rs +++ b/embassy-executor/src/arch/cortex_m.rs @@ -1,4 +1,4 @@ -#[export_name = "__pender"] +#[unsafe(export_name = "__pender")] #[cfg(any(feature = "executor-thread", feature = "executor-interrupt"))] fn __pender(context: *mut ()) { unsafe { @@ -53,7 +53,7 @@ mod thread { pub use embassy_executor_macros::main_cortex_m as main; - use crate::{raw, Spawner}; + use crate::{Spawner, raw}; /// Thread mode executor, using WFE/SEV. /// diff --git a/embassy-executor/src/arch/riscv32.rs b/embassy-executor/src/arch/riscv32.rs index 01e63a9fd..c70c1344a 100644 --- a/embassy-executor/src/arch/riscv32.rs +++ b/embassy-executor/src/arch/riscv32.rs @@ -10,12 +10,12 @@ mod thread { pub use embassy_executor_macros::main_riscv as main; - use crate::{raw, Spawner}; + use crate::{Spawner, raw}; /// global atomic used to keep track of whether there is work to do since sev() is not available on RISCV static SIGNAL_WORK_THREAD_MODE: AtomicBool = AtomicBool::new(false); - #[export_name = "__pender"] + #[unsafe(export_name = "__pender")] fn __pender(_context: *mut ()) { SIGNAL_WORK_THREAD_MODE.store(true, Ordering::SeqCst); } diff --git a/embassy-executor/src/arch/spin.rs b/embassy-executor/src/arch/spin.rs index 340023620..49f3356a6 100644 --- a/embassy-executor/src/arch/spin.rs +++ b/embassy-executor/src/arch/spin.rs @@ -9,9 +9,9 @@ mod thread { pub use embassy_executor_macros::main_spin as main; - use crate::{raw, Spawner}; + use crate::{Spawner, raw}; - #[export_name = "__pender"] + #[unsafe(export_name = "__pender")] fn __pender(_context: *mut ()) {} /// Spin Executor diff --git a/embassy-executor/src/arch/std.rs b/embassy-executor/src/arch/std.rs index b02b15988..c62ab723b 100644 --- a/embassy-executor/src/arch/std.rs +++ b/embassy-executor/src/arch/std.rs @@ -10,9 +10,9 @@ mod thread { pub use embassy_executor_macros::main_std as main; - use crate::{raw, Spawner}; + use crate::{Spawner, raw}; - #[export_name = "__pender"] + #[unsafe(export_name = "__pender")] fn __pender(context: *mut ()) { let signaler: &'static Signaler = unsafe { std::mem::transmute(context) }; signaler.signal() diff --git a/embassy-executor/src/arch/wasm.rs b/embassy-executor/src/arch/wasm.rs index f9d0f935c..d2ff2fe51 100644 --- a/embassy-executor/src/arch/wasm.rs +++ b/embassy-executor/src/arch/wasm.rs @@ -13,9 +13,9 @@ mod thread { use wasm_bindgen::prelude::*; use crate::raw::util::UninitCell; - use crate::{raw, Spawner}; + use crate::{Spawner, raw}; - #[export_name = "__pender"] + #[unsafe(export_name = "__pender")] fn __pender(context: *mut ()) { let signaler: &'static WasmContext = unsafe { std::mem::transmute(context) }; let _ = signaler.promise.then(unsafe { signaler.closure.as_mut() }); diff --git a/embassy-executor/src/lib.rs b/embassy-executor/src/lib.rs index e47b8eb9f..cffc76699 100644 --- a/embassy-executor/src/lib.rs +++ b/embassy-executor/src/lib.rs @@ -1,5 +1,6 @@ #![cfg_attr(not(any(feature = "arch-std", feature = "arch-wasm")), no_std)] #![allow(clippy::new_without_default)] +#![allow(unsafe_op_in_unsafe_fn)] #![doc = include_str!("../README.md")] #![warn(missing_docs)] diff --git a/embassy-executor/src/metadata.rs b/embassy-executor/src/metadata.rs index bc0df0f83..76504ab0b 100644 --- a/embassy-executor/src/metadata.rs +++ b/embassy-executor/src/metadata.rs @@ -1,6 +1,6 @@ #[cfg(feature = "metadata-name")] use core::cell::Cell; -use core::future::{poll_fn, Future}; +use core::future::{Future, poll_fn}; #[cfg(feature = "scheduler-priority")] use core::sync::atomic::{AtomicU8, Ordering}; use core::task::Poll; diff --git a/embassy-executor/src/raw/mod.rs b/embassy-executor/src/raw/mod.rs index dbd70cbf4..ab845ed3b 100644 --- a/embassy-executor/src/raw/mod.rs +++ b/embassy-executor/src/raw/mod.rs @@ -52,7 +52,7 @@ pub use self::waker::task_from_waker; use super::SpawnToken; use crate::{Metadata, SpawnError}; -#[no_mangle] +#[unsafe(no_mangle)] extern "Rust" fn __embassy_time_queue_item_from_waker(waker: &Waker) -> &'static mut TimerQueueItem { unsafe { task_from_waker(waker).timer_queue_item() } } @@ -407,7 +407,7 @@ unsafe impl Sync for Pender {} impl Pender { pub(crate) fn pend(self) { - extern "Rust" { + unsafe extern "Rust" { fn __pender(context: *mut ()); } unsafe { __pender(self.0) }; @@ -507,7 +507,7 @@ impl SyncExecutor { /// The pender function must be exported with the name `__pender` and have the following signature: /// /// ```rust -/// #[export_name = "__pender"] +/// #[unsafe(export_name = "__pender")] /// fn pender(context: *mut ()) { /// // schedule `poll()` to be called /// } diff --git a/embassy-executor/src/raw/run_queue.rs b/embassy-executor/src/raw/run_queue.rs index b8b052310..6f2abdbd0 100644 --- a/embassy-executor/src/raw/run_queue.rs +++ b/embassy-executor/src/raw/run_queue.rs @@ -1,9 +1,9 @@ -use core::ptr::{addr_of_mut, NonNull}; +use core::ptr::{NonNull, addr_of_mut}; -use cordyceps::sorted_list::Links; use cordyceps::Linked; #[cfg(any(feature = "scheduler-priority", feature = "scheduler-deadline"))] use cordyceps::SortedList; +use cordyceps::sorted_list::Links; #[cfg(target_has_atomic = "ptr")] type TransferStack = cordyceps::TransferStack; diff --git a/embassy-executor/src/raw/state_atomics_arm.rs b/embassy-executor/src/raw/state_atomics_arm.rs index b743dcc2c..f68de955f 100644 --- a/embassy-executor/src/raw/state_atomics_arm.rs +++ b/embassy-executor/src/raw/state_atomics_arm.rs @@ -1,4 +1,4 @@ -use core::sync::atomic::{compiler_fence, AtomicBool, AtomicU32, Ordering}; +use core::sync::atomic::{AtomicBool, AtomicU32, Ordering, compiler_fence}; #[derive(Clone, Copy)] pub(crate) struct Token(()); diff --git a/embassy-executor/src/raw/state_critical_section.rs b/embassy-executor/src/raw/state_critical_section.rs index b69a6ac66..8d7ef2892 100644 --- a/embassy-executor/src/raw/state_critical_section.rs +++ b/embassy-executor/src/raw/state_critical_section.rs @@ -1,7 +1,7 @@ use core::cell::Cell; -pub(crate) use critical_section::{with as locked, CriticalSection as Token}; use critical_section::{CriticalSection, Mutex}; +pub(crate) use critical_section::{CriticalSection as Token, with as locked}; #[cfg(target_arch = "avr")] type StateBits = u8; diff --git a/embassy-executor/src/raw/trace.rs b/embassy-executor/src/raw/trace.rs index b3086948c..74519b927 100644 --- a/embassy-executor/src/raw/trace.rs +++ b/embassy-executor/src/raw/trace.rs @@ -368,11 +368,7 @@ impl rtos_trace::RtosTraceOSCallbacks for crate::raw::SyncExecutor { } fn time() -> u64 { const fn gcd(a: u64, b: u64) -> u64 { - if b == 0 { - a - } else { - gcd(b, a % b) - } + if b == 0 { a } else { gcd(b, a % b) } } const GCD_1M: u64 = gcd(embassy_time_driver::TICK_HZ, 1_000_000); diff --git a/embassy-executor/src/raw/waker.rs b/embassy-executor/src/raw/waker.rs index d0d7b003d..2706f0fdf 100644 --- a/embassy-executor/src/raw/waker.rs +++ b/embassy-executor/src/raw/waker.rs @@ -1,6 +1,6 @@ use core::task::{RawWaker, RawWakerVTable, Waker}; -use super::{wake_task, TaskHeader, TaskRef}; +use super::{TaskHeader, TaskRef, wake_task}; static VTABLE: RawWakerVTable = RawWakerVTable::new(clone, wake, wake, drop); @@ -35,7 +35,9 @@ pub fn task_from_waker(waker: &Waker) -> TaskRef { // make sure to compare vtable addresses. Doing `==` on the references // will compare the contents, which is slower. if waker.vtable() as *const _ != &VTABLE as *const _ { - panic!("Found waker not created by the Embassy executor. `embassy_time::Timer` only works with the Embassy executor.") + panic!( + "Found waker not created by the Embassy executor. `embassy_time::Timer` only works with the Embassy executor." + ) } // safety: our wakers are always created with `TaskRef::as_ptr` unsafe { TaskRef::from_ptr(waker.data() as *const TaskHeader) } diff --git a/embassy-executor/src/raw/waker_turbo.rs b/embassy-executor/src/raw/waker_turbo.rs index 435a0ff7e..919bcc61a 100644 --- a/embassy-executor/src/raw/waker_turbo.rs +++ b/embassy-executor/src/raw/waker_turbo.rs @@ -1,7 +1,7 @@ use core::ptr::NonNull; use core::task::Waker; -use super::{wake_task, TaskHeader, TaskRef}; +use super::{TaskHeader, TaskRef, wake_task}; pub(crate) unsafe fn from_task(p: TaskRef) -> Waker { Waker::from_turbo_ptr(NonNull::new_unchecked(p.as_ptr() as _)) @@ -26,7 +26,7 @@ pub fn task_from_waker(waker: &Waker) -> TaskRef { } #[inline(never)] -#[no_mangle] +#[unsafe(no_mangle)] fn _turbo_wake(ptr: NonNull<()>) { // safety: our wakers are always created with `TaskRef::as_ptr` let task = unsafe { TaskRef::from_ptr(ptr.as_ptr() as *const TaskHeader) }; diff --git a/embassy-executor/src/spawner.rs b/embassy-executor/src/spawner.rs index 83d896b76..b73a1e7c6 100644 --- a/embassy-executor/src/spawner.rs +++ b/embassy-executor/src/spawner.rs @@ -1,4 +1,4 @@ -use core::future::{poll_fn, Future}; +use core::future::{Future, poll_fn}; use core::marker::PhantomData; use core::mem; use core::sync::atomic::Ordering; @@ -75,7 +75,10 @@ impl core::fmt::Debug for SpawnError { impl core::fmt::Display for SpawnError { fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { match self { - SpawnError::Busy => write!(f, "Busy - Too many instances of this task are already running. Check the `pool_size` attribute of the task."), + SpawnError::Busy => write!( + f, + "Busy - Too many instances of this task are already running. Check the `pool_size` attribute of the task." + ), } } } @@ -84,7 +87,10 @@ impl core::fmt::Display for SpawnError { impl defmt::Format for SpawnError { fn format(&self, f: defmt::Formatter) { match self { - SpawnError::Busy => defmt::write!(f, "Busy - Too many instances of this task are already running. Check the `pool_size` attribute of the task."), + SpawnError::Busy => defmt::write!( + f, + "Busy - Too many instances of this task are already running. Check the `pool_size` attribute of the task." + ), } } } diff --git a/embassy-executor/tests/test.rs b/embassy-executor/tests/test.rs index 6baf3dc21..a99976168 100644 --- a/embassy-executor/tests/test.rs +++ b/embassy-executor/tests/test.rs @@ -2,14 +2,14 @@ #![cfg_attr(feature = "nightly", feature(never_type))] use std::boxed::Box; -use std::future::{poll_fn, Future}; +use std::future::{Future, poll_fn}; use std::sync::{Arc, Mutex}; use std::task::Poll; use embassy_executor::raw::Executor; -use embassy_executor::{task, Spawner}; +use embassy_executor::{Spawner, task}; -#[export_name = "__pender"] +#[unsafe(export_name = "__pender")] fn __pender(context: *mut ()) { unsafe { let trace = &*(context as *const Trace); diff --git a/embassy-executor/tests/ui/nonstatic_struct_elided.stderr b/embassy-executor/tests/ui/nonstatic_struct_elided.stderr index 0ee1bfe0c..e6829bf5d 100644 --- a/embassy-executor/tests/ui/nonstatic_struct_elided.stderr +++ b/embassy-executor/tests/ui/nonstatic_struct_elided.stderr @@ -9,16 +9,16 @@ help: indicate the anonymous lifetime 6 | async fn task(_x: Foo<'_>) {} | ++++ -error[E0700]: hidden type for `impl Sized` captures lifetime that does not appear in bounds +error: lifetime may not live long enough --> tests/ui/nonstatic_struct_elided.rs:5:1 | 5 | #[embassy_executor::task] - | ^^^^^^^^^^^^^^^^^^^^^^^^^ opaque type defined here + | ^^^^^^^^^^^^^^^^^^^^^^^^^ returning this value requires that `'1` must outlive `'static` 6 | async fn task(_x: Foo) {} - | --- hidden type `impl Sized` captures the anonymous lifetime defined here + | -- has type `Foo<'1>` | = note: this error originates in the attribute macro `embassy_executor::task` (in Nightly builds, run with -Z macro-backtrace for more info) -help: add a `use<...>` bound to explicitly capture `'_` +help: to declare that `impl Sized` captures data from argument `_x`, you can add an explicit `'_` lifetime bound | -5 | #[embassy_executor::task] + use<'_> - | +++++++++ +5 | #[embassy_executor::task] + '_ + | ++++ diff --git a/embassy-executor/tests/ui/task_safety_attribute.rs b/embassy-executor/tests/ui/task_safety_attribute.rs index ab5a2f99f..46a5c665f 100644 --- a/embassy-executor/tests/ui/task_safety_attribute.rs +++ b/embassy-executor/tests/ui/task_safety_attribute.rs @@ -9,7 +9,7 @@ async fn safe() {} #[embassy_executor::task] async unsafe fn not_safe() {} -#[export_name = "__pender"] +#[unsafe(export_name = "__pender")] fn pender(_: *mut ()) { // The test doesn't link if we don't include this. // We never call this anyway. diff --git a/embassy-futures/Cargo.toml b/embassy-futures/Cargo.toml index 07d5219cf..d344e5c4b 100644 --- a/embassy-futures/Cargo.toml +++ b/embassy-futures/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "embassy-futures" version = "0.1.2" -edition = "2021" +edition = "2024" description = "no-std, no-alloc utilities for working with futures" repository = "https://github.com/embassy-rs/embassy" documentation = "https://docs.embassy.dev/embassy-futures" diff --git a/embassy-hal-internal/Cargo.toml b/embassy-hal-internal/Cargo.toml index 11dcc2466..c2e0bd30f 100644 --- a/embassy-hal-internal/Cargo.toml +++ b/embassy-hal-internal/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "embassy-hal-internal" version = "0.3.0" -edition = "2021" +edition = "2024" license = "MIT OR Apache-2.0" description = "Internal implementation details for Embassy HALs. DO NOT USE DIRECTLY." repository = "https://github.com/embassy-rs/embassy" diff --git a/embassy-hal-internal/src/interrupt.rs b/embassy-hal-internal/src/interrupt.rs index 5e64dce9d..ce6057e2d 100644 --- a/embassy-hal-internal/src/interrupt.rs +++ b/embassy-hal-internal/src/interrupt.rs @@ -1,6 +1,6 @@ //! Interrupt handling for cortex-m devices. use core::mem; -use core::sync::atomic::{compiler_fence, Ordering}; +use core::sync::atomic::{Ordering, compiler_fence}; use cortex_m::interrupt::InterruptNumber; use cortex_m::peripheral::NVIC; diff --git a/embassy-hal-internal/src/lib.rs b/embassy-hal-internal/src/lib.rs index 7addb71e2..729f97f0c 100644 --- a/embassy-hal-internal/src/lib.rs +++ b/embassy-hal-internal/src/lib.rs @@ -1,5 +1,6 @@ #![no_std] #![allow(clippy::new_without_default)] +#![allow(unsafe_op_in_unsafe_fn)] #![doc = include_str!("../README.md")] #![warn(missing_docs)] diff --git a/embassy-hal-internal/src/macros.rs b/embassy-hal-internal/src/macros.rs index ce72ded5c..0fcca2baf 100644 --- a/embassy-hal-internal/src/macros.rs +++ b/embassy-hal-internal/src/macros.rs @@ -58,7 +58,7 @@ macro_rules! peripherals_struct { ///Returns all the peripherals *once* #[inline] pub(crate) fn take_with_cs(_cs: critical_section::CriticalSection) -> Self { - #[no_mangle] + #[unsafe(no_mangle)] static mut _EMBASSY_DEVICE_PERIPHERALS: bool = false; // safety: OK because we're inside a CS. diff --git a/embassy-imxrt/Cargo.toml b/embassy-imxrt/Cargo.toml index 7561640dd..c47756f10 100644 --- a/embassy-imxrt/Cargo.toml +++ b/embassy-imxrt/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "embassy-imxrt" version = "0.1.0" -edition = "2021" +edition = "2024" license = "MIT OR Apache-2.0" description = "Embassy Hardware Abstraction Layer (HAL) for the IMXRT microcontroller" keywords = ["embedded", "async", "imxrt", "rt600", "embedded-hal"] diff --git a/embassy-imxrt/src/clocks.rs b/embassy-imxrt/src/clocks.rs index 39c3e6238..22df2686f 100644 --- a/embassy-imxrt/src/clocks.rs +++ b/embassy-imxrt/src/clocks.rs @@ -1,5 +1,5 @@ //! Clock configuration for the `RT6xx` -use core::sync::atomic::{AtomicU32, AtomicU8, Ordering}; +use core::sync::atomic::{AtomicU8, AtomicU32, Ordering}; use paste::paste; diff --git a/embassy-imxrt/src/crc.rs b/embassy-imxrt/src/crc.rs index 24d6ba5bd..d6f0419d0 100644 --- a/embassy-imxrt/src/crc.rs +++ b/embassy-imxrt/src/crc.rs @@ -2,9 +2,9 @@ use core::marker::PhantomData; -use crate::clocks::{enable_and_reset, SysconPeripheral}; +use crate::clocks::{SysconPeripheral, enable_and_reset}; pub use crate::pac::crc_engine::mode::CrcPolynomial as Polynomial; -use crate::{peripherals, Peri, PeripheralType}; +use crate::{Peri, PeripheralType, peripherals}; /// CRC driver. pub struct Crc<'d> { diff --git a/embassy-imxrt/src/dma.rs b/embassy-imxrt/src/dma.rs index e141447f3..e71a27e0e 100644 --- a/embassy-imxrt/src/dma.rs +++ b/embassy-imxrt/src/dma.rs @@ -2,10 +2,10 @@ use core::future::Future; use core::pin::Pin; -use core::sync::atomic::{compiler_fence, Ordering}; +use core::sync::atomic::{Ordering, compiler_fence}; use core::task::{Context, Poll}; -use embassy_hal_internal::{impl_peripheral, Peri, PeripheralType}; +use embassy_hal_internal::{Peri, PeripheralType, impl_peripheral}; use embassy_sync::waitqueue::AtomicWaker; use pac::dma0::channel::cfg::Periphreqen; use pac::dma0::channel::xfercfg::{Dstinc, Srcinc, Width}; @@ -14,7 +14,7 @@ use crate::clocks::enable_and_reset; use crate::interrupt::InterruptExt; use crate::peripherals::DMA0; use crate::sealed::Sealed; -use crate::{interrupt, pac, peripherals, BitIter}; +use crate::{BitIter, interrupt, pac, peripherals}; #[cfg(feature = "rt")] #[interrupt] diff --git a/embassy-imxrt/src/flexcomm/mod.rs b/embassy-imxrt/src/flexcomm/mod.rs index 4473c9a77..27794042b 100644 --- a/embassy-imxrt/src/flexcomm/mod.rs +++ b/embassy-imxrt/src/flexcomm/mod.rs @@ -4,11 +4,11 @@ pub mod uart; use paste::paste; -use crate::clocks::{enable_and_reset, SysconPeripheral}; +use crate::clocks::{SysconPeripheral, enable_and_reset}; use crate::peripherals::{ - FLEXCOMM0, FLEXCOMM1, FLEXCOMM14, FLEXCOMM15, FLEXCOMM2, FLEXCOMM3, FLEXCOMM4, FLEXCOMM5, FLEXCOMM6, FLEXCOMM7, + FLEXCOMM0, FLEXCOMM1, FLEXCOMM2, FLEXCOMM3, FLEXCOMM4, FLEXCOMM5, FLEXCOMM6, FLEXCOMM7, FLEXCOMM14, FLEXCOMM15, }; -use crate::{pac, PeripheralType}; +use crate::{PeripheralType, pac}; /// clock selection option #[derive(Copy, Clone, Debug)] @@ -223,9 +223,15 @@ macro_rules! into_mode { } } -into_mode!(usart, FLEXCOMM0, FLEXCOMM1, FLEXCOMM2, FLEXCOMM3, FLEXCOMM4, FLEXCOMM5, FLEXCOMM6, FLEXCOMM7); -into_mode!(spi, FLEXCOMM0, FLEXCOMM1, FLEXCOMM2, FLEXCOMM3, FLEXCOMM4, FLEXCOMM5, FLEXCOMM6, FLEXCOMM7, FLEXCOMM14); -into_mode!(i2c, FLEXCOMM0, FLEXCOMM1, FLEXCOMM2, FLEXCOMM3, FLEXCOMM4, FLEXCOMM5, FLEXCOMM6, FLEXCOMM7, FLEXCOMM15); +into_mode!( + usart, FLEXCOMM0, FLEXCOMM1, FLEXCOMM2, FLEXCOMM3, FLEXCOMM4, FLEXCOMM5, FLEXCOMM6, FLEXCOMM7 +); +into_mode!( + spi, FLEXCOMM0, FLEXCOMM1, FLEXCOMM2, FLEXCOMM3, FLEXCOMM4, FLEXCOMM5, FLEXCOMM6, FLEXCOMM7, FLEXCOMM14 +); +into_mode!( + i2c, FLEXCOMM0, FLEXCOMM1, FLEXCOMM2, FLEXCOMM3, FLEXCOMM4, FLEXCOMM5, FLEXCOMM6, FLEXCOMM7, FLEXCOMM15 +); into_mode!( i2s_transmit, diff --git a/embassy-imxrt/src/flexcomm/uart.rs b/embassy-imxrt/src/flexcomm/uart.rs index 230b30d43..2b759ba84 100644 --- a/embassy-imxrt/src/flexcomm/uart.rs +++ b/embassy-imxrt/src/flexcomm/uart.rs @@ -2,10 +2,10 @@ use core::future::poll_fn; use core::marker::PhantomData; -use core::sync::atomic::{compiler_fence, AtomicU8, Ordering}; +use core::sync::atomic::{AtomicU8, Ordering, compiler_fence}; use core::task::Poll; -use embassy_futures::select::{select, Either}; +use embassy_futures::select::{Either, select}; use embassy_hal_internal::drop::OnDrop; use embassy_hal_internal::{Peri, PeripheralType}; use embassy_sync::waitqueue::AtomicWaker; diff --git a/embassy-imxrt/src/gpio.rs b/embassy-imxrt/src/gpio.rs index e62fde8b1..4a0608e76 100644 --- a/embassy-imxrt/src/gpio.rs +++ b/embassy-imxrt/src/gpio.rs @@ -13,7 +13,7 @@ use crate::clocks::enable_and_reset; use crate::iopctl::IopctlPin; pub use crate::iopctl::{AnyPin, DriveMode, DriveStrength, Function, Inverter, Pull, SlewRate}; use crate::sealed::Sealed; -use crate::{interrupt, peripherals, BitIter, Peri, PeripheralType}; +use crate::{BitIter, Peri, PeripheralType, interrupt, peripherals}; // This should be unique per IMXRT package const PORT_COUNT: usize = 8; diff --git a/embassy-imxrt/src/iopctl.rs b/embassy-imxrt/src/iopctl.rs index a3b8b14d6..805bf2f1b 100644 --- a/embassy-imxrt/src/iopctl.rs +++ b/embassy-imxrt/src/iopctl.rs @@ -2,7 +2,7 @@ //! //! Also known as IO Pin Configuration (IOCON) -use crate::pac::{iopctl, Iopctl}; +use crate::pac::{Iopctl, iopctl}; // A generic pin of any type. // diff --git a/embassy-imxrt/src/lib.rs b/embassy-imxrt/src/lib.rs index a3437c655..643dd0c8a 100644 --- a/embassy-imxrt/src/lib.rs +++ b/embassy-imxrt/src/lib.rs @@ -1,5 +1,6 @@ #![no_std] #![allow(async_fn_in_trait)] +#![allow(unsafe_op_in_unsafe_fn)] #![doc = include_str!("../README.md")] #![warn(missing_docs)] @@ -39,7 +40,7 @@ pub use chip::interrupts::*; pub use chip::pac; #[cfg(not(feature = "unstable-pac"))] pub(crate) use chip::pac; -pub use chip::{peripherals, Peripherals}; +pub use chip::{Peripherals, peripherals}; pub use embassy_hal_internal::{Peri, PeripheralType}; #[cfg(feature = "rt")] @@ -74,7 +75,7 @@ macro_rules! bind_interrupts { $( #[allow(non_snake_case)] - #[no_mangle] + #[unsafe(no_mangle)] unsafe extern "C" fn $irq() { unsafe { $( diff --git a/embassy-imxrt/src/rng.rs b/embassy-imxrt/src/rng.rs index 75f243df9..094418e41 100644 --- a/embassy-imxrt/src/rng.rs +++ b/embassy-imxrt/src/rng.rs @@ -7,9 +7,9 @@ use core::task::Poll; use embassy_futures::block_on; use embassy_sync::waitqueue::AtomicWaker; -use crate::clocks::{enable_and_reset, SysconPeripheral}; +use crate::clocks::{SysconPeripheral, enable_and_reset}; use crate::interrupt::typelevel::Interrupt; -use crate::{interrupt, peripherals, Peri, PeripheralType}; +use crate::{Peri, PeripheralType, interrupt, peripherals}; static RNG_WAKER: AtomicWaker = AtomicWaker::new(); diff --git a/embassy-imxrt/src/time_driver.rs b/embassy-imxrt/src/time_driver.rs index f127609c8..fbd935b70 100644 --- a/embassy-imxrt/src/time_driver.rs +++ b/embassy-imxrt/src/time_driver.rs @@ -1,11 +1,11 @@ //! Time Driver. use core::cell::{Cell, RefCell}; #[cfg(feature = "time-driver-rtc")] -use core::sync::atomic::{compiler_fence, AtomicU32, Ordering}; +use core::sync::atomic::{AtomicU32, Ordering, compiler_fence}; use critical_section::CriticalSection; -use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex; use embassy_sync::blocking_mutex::Mutex; +use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex; use embassy_time_driver::Driver; use embassy_time_queue_utils::Queue; diff --git a/embassy-mspm0/CHANGELOG.md b/embassy-mspm0/CHANGELOG.md index b846f21b1..fcb0f9dbd 100644 --- a/embassy-mspm0/CHANGELOG.md +++ b/embassy-mspm0/CHANGELOG.md @@ -13,3 +13,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - feat: Add window watchdog implementation based on WWDT0, WWDT1 peripherals (#4574) - feat: Add MSPM0C1105/C1106 support - feat: Add adc implementation (#4646) +- fix: gpio OutputOpenDrain config (#4735) +- fix: add MSPM0C1106 to build test matrix +- feat: add MSPM0H3216 support +- feat: Add i2c target implementation (#4605) diff --git a/embassy-mspm0/Cargo.toml b/embassy-mspm0/Cargo.toml index 1b32c4d43..df6176ff6 100644 --- a/embassy-mspm0/Cargo.toml +++ b/embassy-mspm0/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "embassy-mspm0" version = "0.1.0" -edition = "2021" +edition = "2024" license = "MIT OR Apache-2.0" description = "Embassy Hardware Abstraction Layer (HAL) for Texas Instruments MSPM0 series microcontrollers" keywords = ["embedded", "async", "mspm0", "hal", "embedded-hal"] @@ -15,17 +15,19 @@ publish = false [package.metadata.embassy] build = [ {target = "thumbv6m-none-eabi", features = ["defmt", "mspm0c1104dgs20", "time-driver-any"]}, + {target = "thumbv6m-none-eabi", features = ["defmt", "mspm0c1106rgz", "time-driver-any"]}, + {target = "thumbv6m-none-eabi", features = ["defmt", "mspm0g1107ycj", "time-driver-any"]}, + {target = "thumbv6m-none-eabi", features = ["defmt", "mspm0g1505pt", "time-driver-any"]}, + {target = "thumbv6m-none-eabi", features = ["defmt", "mspm0g1519rhb", "time-driver-any"]}, + {target = "thumbv6m-none-eabi", features = ["defmt", "mspm0g3105rhb", "time-driver-any"]}, {target = "thumbv6m-none-eabi", features = ["defmt", "mspm0g3507pm", "time-driver-any"]}, {target = "thumbv6m-none-eabi", features = ["defmt", "mspm0g3519pz", "time-driver-any"]}, + {target = "thumbv6m-none-eabi", features = ["defmt", "mspm0h3216pt", "time-driver-any"]}, {target = "thumbv6m-none-eabi", features = ["defmt", "mspm0l1306rhb", "time-driver-any"]}, {target = "thumbv6m-none-eabi", features = ["defmt", "mspm0l2228pn", "time-driver-any"]}, {target = "thumbv6m-none-eabi", features = ["defmt", "mspm0l1345dgs28", "time-driver-any"]}, {target = "thumbv6m-none-eabi", features = ["defmt", "mspm0l1106dgs28", "time-driver-any"]}, - {target = "thumbv6m-none-eabi", features = ["defmt", "mspm0l1228pm", "time-driver-any"]}, - {target = "thumbv6m-none-eabi", features = ["defmt", "mspm0g1107ycj", "time-driver-any"]}, - {target = "thumbv6m-none-eabi", features = ["defmt", "mspm0g3105rhb", "time-driver-any"]}, - {target = "thumbv6m-none-eabi", features = ["defmt", "mspm0g1505pt", "time-driver-any"]}, - {target = "thumbv6m-none-eabi", features = ["defmt", "mspm0g1519rhb", "time-driver-any"]}, + {target = "thumbv6m-none-eabi", features = ["defmt", "mspm0l1228pt", "time-driver-any"]}, ] [package.metadata.embassy_docs] @@ -37,6 +39,7 @@ flavors = [ { regex_feature = "mspm0c.*", target = "thumbv6m-none-eabi" }, { regex_feature = "mspm0l.*", target = "thumbv6m-none-eabi" }, { regex_feature = "mspm0g.*", target = "thumbv6m-none-eabi" }, + { regex_feature = "mspm0h.*", target = "thumbv6m-none-eabi" }, ] [package.metadata.docs.rs] @@ -69,7 +72,7 @@ cortex-m = "0.7.6" critical-section = "1.2.0" # mspm0-metapac = { version = "" } -mspm0-metapac = { git = "https://github.com/mspm0-rs/mspm0-data-generated/", tag = "mspm0-data-d7bf3d01ac0780e716a45b0474234d39443dc5cf" } +mspm0-metapac = { git = "https://github.com/mspm0-rs/mspm0-data-generated/", tag = "mspm0-data-e7de4103a0713772695ffcad52c3c2f07414dc29" } [build-dependencies] proc-macro2 = "1.0.94" @@ -77,7 +80,7 @@ quote = "1.0.40" cfg_aliases = "0.2.1" # mspm0-metapac = { version = "", default-features = false, features = ["metadata"] } -mspm0-metapac = { git = "https://github.com/mspm0-rs/mspm0-data-generated/", tag = "mspm0-data-d7bf3d01ac0780e716a45b0474234d39443dc5cf", default-features = false, features = ["metadata"] } +mspm0-metapac = { git = "https://github.com/mspm0-rs/mspm0-data-generated/", tag = "mspm0-data-e7de4103a0713772695ffcad52c3c2f07414dc29", default-features = false, features = ["metadata"] } [features] default = ["rt"] @@ -243,6 +246,7 @@ mspm0g3519pn = ["mspm0-metapac/mspm0g3519pn"] mspm0g3519pz = ["mspm0-metapac/mspm0g3519pz"] mspm0g3519rgz = ["mspm0-metapac/mspm0g3519rgz"] mspm0g3519rhb = ["mspm0-metapac/mspm0g3519rhb"] +mspm0h3216pt = ["mspm0-metapac/mspm0h3216pt"] mspm0l1105dgs20 = ["mspm0-metapac/mspm0l1105dgs20"] mspm0l1105dgs28 = ["mspm0-metapac/mspm0l1105dgs28"] mspm0l1105dyy = ["mspm0-metapac/mspm0l1105dyy"] diff --git a/embassy-mspm0/build.rs b/embassy-mspm0/build.rs index d294bc422..1d118ad66 100644 --- a/embassy-mspm0/build.rs +++ b/embassy-mspm0/build.rs @@ -16,14 +16,15 @@ use quote::{format_ident, quote}; mod common; fn main() { - generate_code(); - interrupt_group_linker_magic(); -} - -fn generate_code() { let mut cfgs = common::CfgSet::new(); common::set_target_cfgs(&mut cfgs); + generate_code(&mut cfgs); + select_gpio_features(&mut cfgs); + interrupt_group_linker_magic(); +} + +fn generate_code(cfgs: &mut CfgSet) { #[cfg(any(feature = "rt"))] println!( "cargo:rustc-link-search={}", @@ -53,9 +54,9 @@ fn generate_code() { cfgs.declare_all(&get_chip_cfgs(&chip)); } - let mut singletons = get_singletons(&mut cfgs); + let mut singletons = get_singletons(cfgs); - time_driver(&mut singletons, &mut cfgs); + time_driver(&mut singletons, cfgs); let mut g = TokenStream::new(); @@ -68,7 +69,7 @@ fn generate_code() { g.extend(generate_pin_trait_impls()); g.extend(generate_groups()); g.extend(generate_dma_channel_count()); - g.extend(generate_adc_constants(&mut cfgs)); + g.extend(generate_adc_constants(cfgs)); let out_dir = &PathBuf::from(env::var_os("OUT_DIR").unwrap()); let out_file = out_dir.join("_generated.rs").to_string_lossy().to_string(); @@ -115,6 +116,10 @@ fn get_chip_cfgs(chip_name: &str) -> Vec { cfgs.push("mspm0g351x".to_string()); } + if chip_name.starts_with("mspm0h321") { + cfgs.push("mspm0h321x".to_string()); + } + if chip_name.starts_with("mspm0l110") { cfgs.push("mspm0l110x".to_string()); } @@ -208,7 +213,7 @@ fn generate_groups() -> TokenStream { #[cfg(feature = "rt")] mod group_vectors { - extern "Rust" { + unsafe extern "Rust" { #(#group_vectors)* } } @@ -646,6 +651,35 @@ fn generate_pin_trait_impls() -> TokenStream { } } +fn select_gpio_features(cfgs: &mut CfgSet) { + cfgs.declare_all(&[ + "gpioa_interrupt", + "gpioa_group", + "gpiob_interrupt", + "gpiob_group", + "gpioc_group", + ]); + + for interrupt in METADATA.interrupts.iter() { + match interrupt.name { + "GPIOA" => cfgs.enable("gpioa_interrupt"), + "GPIOB" => cfgs.enable("gpiob_interrupt"), + _ => (), + } + } + + for group in METADATA.interrupt_groups.iter() { + for interrupt in group.interrupts { + match interrupt.name { + "GPIOA" => cfgs.enable("gpioa_group"), + "GPIOB" => cfgs.enable("gpiob_group"), + "GPIOC" => cfgs.enable("gpioc_group"), + _ => (), + } + } + } +} + /// rustfmt a given path. /// Failures are logged to stderr and ignored. fn rustfmt(path: impl AsRef) { diff --git a/embassy-mspm0/src/adc.rs b/embassy-mspm0/src/adc.rs index 5b93e9a6e..948801679 100644 --- a/embassy-mspm0/src/adc.rs +++ b/embassy-mspm0/src/adc.rs @@ -4,13 +4,13 @@ use core::future::poll_fn; use core::marker::PhantomData; use core::task::Poll; -use embassy_hal_internal::{impl_peripheral, PeripheralType}; +use embassy_hal_internal::{PeripheralType, impl_peripheral}; use embassy_sync::waitqueue::AtomicWaker; use crate::interrupt::{Interrupt, InterruptExt}; use crate::mode::{Async, Blocking, Mode}; -use crate::pac::adc::{vals, Adc as Regs}; -use crate::{interrupt, Peri}; +use crate::pac::adc::{Adc as Regs, vals}; +use crate::{Peri, interrupt}; /// Interrupt handler. pub struct InterruptHandler { diff --git a/embassy-mspm0/src/dma.rs b/embassy-mspm0/src/dma.rs index 66b79709c..58b087761 100644 --- a/embassy-mspm0/src/dma.rs +++ b/embassy-mspm0/src/dma.rs @@ -5,18 +5,18 @@ use core::future::Future; use core::mem; use core::pin::Pin; -use core::sync::atomic::{compiler_fence, Ordering}; +use core::sync::atomic::{Ordering, compiler_fence}; use core::task::{Context, Poll}; use critical_section::CriticalSection; use embassy_hal_internal::interrupt::InterruptExt; -use embassy_hal_internal::{impl_peripheral, PeripheralType}; +use embassy_hal_internal::{PeripheralType, impl_peripheral}; use embassy_sync::waitqueue::AtomicWaker; -use mspm0_metapac::common::{Reg, RW}; +use mspm0_metapac::common::{RW, Reg}; use mspm0_metapac::dma::regs; use mspm0_metapac::dma::vals::{self, Autoen, Em, Incr, Preirq, Wdth}; -use crate::{interrupt, pac, Peri}; +use crate::{Peri, interrupt, pac}; /// The burst size of a DMA transfer. #[derive(Debug, Clone, Copy, PartialEq, Eq)] diff --git a/embassy-mspm0/src/gpio.rs b/embassy-mspm0/src/gpio.rs index d5fd36dbf..d8eb42dc2 100644 --- a/embassy-mspm0/src/gpio.rs +++ b/embassy-mspm0/src/gpio.rs @@ -5,12 +5,12 @@ use core::future::Future; use core::pin::Pin as FuturePin; use core::task::{Context, Poll}; -use embassy_hal_internal::{impl_peripheral, Peri, PeripheralType}; +use embassy_hal_internal::{Peri, PeripheralType, impl_peripheral}; use embassy_sync::waitqueue::AtomicWaker; use crate::pac::gpio::vals::*; use crate::pac::gpio::{self}; -#[cfg(all(feature = "rt", any(mspm0c110x, mspm0c1105_c1106, mspm0l110x)))] +#[cfg(all(feature = "rt", any(gpioa_interrupt, gpiob_interrupt)))] use crate::pac::interrupt; use crate::pac::{self}; @@ -156,7 +156,12 @@ impl<'d> Flex<'d> { w.set_pf(GPIO_PF); w.set_hiz1(true); w.set_pc(true); - w.set_inena(false); + w.set_inena(true); + }); + + // Enable output driver (DOE) - required for open-drain to drive low + self.pin.block().doeset31_0().write(|w| { + w.set_dio(self.pin.bit_index(), true); }); self.set_pull(Pull::None); @@ -1105,16 +1110,21 @@ fn irq_handler(gpio: gpio::Gpio, wakers: &[AtomicWaker; 32]) { } } +#[cfg(all(gpioa_interrupt, gpioa_group))] +compile_error!("gpioa_interrupt and gpioa_group are mutually exclusive cfgs"); +#[cfg(all(gpiob_interrupt, gpiob_group))] +compile_error!("gpiob_interrupt and gpiob_group are mutually exclusive cfgs"); + // C110x and L110x have a dedicated interrupts just for GPIOA. // // These chips do not have a GROUP1 interrupt. -#[cfg(all(feature = "rt", any(mspm0c110x, mspm0c1105_c1106, mspm0l110x)))] +#[cfg(all(feature = "rt", gpioa_interrupt))] #[interrupt] fn GPIOA() { irq_handler(pac::GPIOA, &PORTA_WAKERS); } -#[cfg(all(feature = "rt", mspm0c1105_c1106))] +#[cfg(all(feature = "rt", gpiob_interrupt))] #[interrupt] fn GPIOB() { irq_handler(pac::GPIOB, &PORTB_WAKERS); @@ -1124,23 +1134,23 @@ fn GPIOB() { // // Defining these as no_mangle is required so that the linker will pick these over the default handler. -#[cfg(all(feature = "rt", not(any(mspm0c110x, mspm0c1105_c1106, mspm0l110x))))] -#[no_mangle] +#[cfg(all(feature = "rt", gpioa_group))] +#[unsafe(no_mangle)] #[allow(non_snake_case)] fn GPIOA() { irq_handler(pac::GPIOA, &PORTA_WAKERS); } -#[cfg(all(feature = "rt", gpio_pb, not(mspm0c1105_c1106)))] -#[no_mangle] +#[cfg(all(feature = "rt", gpiob_group))] +#[unsafe(no_mangle)] #[allow(non_snake_case)] fn GPIOB() { irq_handler(pac::GPIOB, &PORTB_WAKERS); } -#[cfg(all(feature = "rt", gpio_pc))] +#[cfg(all(feature = "rt", gpioc_group))] #[allow(non_snake_case)] -#[no_mangle] +#[unsafe(no_mangle)] fn GPIOC() { irq_handler(pac::GPIOC, &PORTC_WAKERS); } diff --git a/embassy-mspm0/src/i2c.rs b/embassy-mspm0/src/i2c.rs index 1906e37ba..3067f4833 100644 --- a/embassy-mspm0/src/i2c.rs +++ b/embassy-mspm0/src/i2c.rs @@ -10,13 +10,13 @@ use embassy_hal_internal::PeripheralType; use embassy_sync::waitqueue::AtomicWaker; use mspm0_metapac::i2c; +use crate::Peri; use crate::gpio::{AnyPin, PfType, Pull, SealedPin}; use crate::interrupt::typelevel::Binding; use crate::interrupt::{Interrupt, InterruptExt}; use crate::mode::{Async, Blocking, Mode}; -use crate::pac::i2c::{vals, I2c as Regs}; +use crate::pac::i2c::{I2c as Regs, vals}; use crate::pac::{self}; -use crate::Peri; /// The clock source for the I2C. #[derive(Clone, Copy, PartialEq, Eq, Debug)] @@ -56,7 +56,7 @@ pub enum ClockDiv { } impl ClockDiv { - fn into(self) -> vals::Ratio { + pub(crate) fn into(self) -> vals::Ratio { match self { Self::DivBy1 => vals::Ratio::DIV_BY_1, Self::DivBy2 => vals::Ratio::DIV_BY_2, @@ -133,6 +133,11 @@ pub enum ConfigError { /// /// The clock soure is not enabled is SYSCTL. ClockSourceNotEnabled, + + /// Invalid target address. + /// + /// The target address is not 7-bit. + InvalidTargetAddress, } #[non_exhaustive] @@ -140,7 +145,7 @@ pub enum ConfigError { /// Config pub struct Config { /// I2C clock source. - clock_source: ClockSel, + pub(crate) clock_source: ClockSel, /// I2C clock divider. pub clock_div: ClockDiv, @@ -196,7 +201,7 @@ impl Config { } #[cfg(any(mspm0c110x, mspm0c1105_c1106))] - fn calculate_clock_source(&self) -> u32 { + pub(crate) fn calculate_clock_source(&self) -> u32 { // Assume that BusClk has default value. // TODO: calculate BusClk more precisely. match self.clock_source { @@ -206,10 +211,10 @@ impl Config { } #[cfg(any( - mspm0g110x, mspm0g150x, mspm0g151x, mspm0g310x, mspm0g350x, mspm0g351x, mspm0l110x, mspm0l122x, mspm0l130x, - mspm0l134x, mspm0l222x + mspm0g110x, mspm0g150x, mspm0g151x, mspm0g310x, mspm0g350x, mspm0g351x, mspm0h321x, mspm0l110x, mspm0l122x, + mspm0l130x, mspm0l134x, mspm0l222x ))] - fn calculate_clock_source(&self) -> u32 { + pub(crate) fn calculate_clock_source(&self) -> u32 { // Assume that BusClk has default value. // TODO: calculate BusClk more precisely. match self.clock_source { diff --git a/embassy-mspm0/src/i2c_target.rs b/embassy-mspm0/src/i2c_target.rs new file mode 100644 index 000000000..86be91415 --- /dev/null +++ b/embassy-mspm0/src/i2c_target.rs @@ -0,0 +1,509 @@ +//! Inter-Integrated-Circuit (I2C) Target +// The following code is modified from embassy-stm32 and embassy-rp +// https://github.com/embassy-rs/embassy/tree/main/embassy-stm32 +// https://github.com/embassy-rs/embassy/tree/main/embassy-rp + +use core::future::poll_fn; +use core::marker::PhantomData; +use core::sync::atomic::Ordering; +use core::task::Poll; + +use embassy_embedded_hal::SetConfig; +use mspm0_metapac::i2c::vals::CpuIntIidxStat; + +use crate::gpio::{AnyPin, SealedPin}; +use crate::interrupt::InterruptExt; +use crate::mode::{Async, Blocking, Mode}; +use crate::pac::{self, i2c::vals}; +use crate::{i2c, i2c_target, interrupt, Peri}; +// Re-use I2c controller types +use crate::i2c::{ClockSel, ConfigError, Info, Instance, InterruptHandler, SclPin, SdaPin, State}; + +#[non_exhaustive] +#[derive(Clone, Copy, PartialEq, Eq, Debug)] +/// Config +pub struct Config { + /// 7-bit Target Address + pub target_addr: u8, + + /// Control if the target should ack to and report general calls. + pub general_call: bool, +} + +impl Default for Config { + fn default() -> Self { + Self { + target_addr: 0x48, + general_call: false, + } + } +} + +/// I2C error +#[derive(Debug, PartialEq, Eq, Clone, Copy)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +#[non_exhaustive] +pub enum Error { + /// User passed in a response buffer that was 0 length + InvalidResponseBufferLength, + /// The response buffer length was too short to contain the message + /// + /// The length parameter will always be the length of the buffer, and is + /// provided as a convenience for matching alongside `Command::Write`. + PartialWrite(usize), + /// The response buffer length was too short to contain the message + /// + /// The length parameter will always be the length of the buffer, and is + /// provided as a convenience for matching alongside `Command::GeneralCall`. + PartialGeneralCall(usize), +} + +/// Received command from the controller. +#[derive(Debug, Copy, Clone, Eq, PartialEq)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +pub enum Command { + /// General Call Write: Controller sent the General Call address (0x00) followed by data. + /// Contains the number of bytes written by the controller. + GeneralCall(usize), + /// Read: Controller wants to read data from the target. + Read, + /// Write: Controller sent the target's address followed by data. + /// Contains the number of bytes written by the controller. + Write(usize), + /// Write followed by Read (Repeated Start): Controller wrote data, then issued a repeated + /// start and wants to read data. Contains the number of bytes written before the read. + WriteRead(usize), +} + +/// Status after responding to a controller read request. +#[derive(Debug, Copy, Clone, Eq, PartialEq)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +pub enum ReadStatus { + /// Transaction completed successfully. The controller either NACKed the last byte + /// or sent a STOP condition. + Done, + /// Transaction incomplete, controller trying to read more bytes than were provided + NeedMoreBytes, + /// Transaction complete, but controller stopped reading bytes before we ran out + LeftoverBytes(u16), +} + +/// I2C Target driver. +// Use the same Instance, SclPin, SdaPin traits as the controller +pub struct I2cTarget<'d, M: Mode> { + info: &'static Info, + state: &'static State, + scl: Option>, + sda: Option>, + config: i2c::Config, + target_config: i2c_target::Config, + _phantom: PhantomData, +} + +impl<'d> SetConfig for I2cTarget<'d, Async> { + type Config = (i2c::Config, i2c_target::Config); + type ConfigError = ConfigError; + + fn set_config(&mut self, config: &Self::Config) -> Result<(), Self::ConfigError> { + self.info.interrupt.disable(); + + if let Some(ref sda) = self.sda { + sda.update_pf(config.0.sda_pf()); + } + + if let Some(ref scl) = self.scl { + scl.update_pf(config.0.scl_pf()); + } + + self.config = config.0.clone(); + self.target_config = config.1.clone(); + + self.reset() + } +} + +impl<'d> SetConfig for I2cTarget<'d, Blocking> { + type Config = (i2c::Config, i2c_target::Config); + type ConfigError = ConfigError; + + fn set_config(&mut self, config: &Self::Config) -> Result<(), Self::ConfigError> { + if let Some(ref sda) = self.sda { + sda.update_pf(config.0.sda_pf()); + } + + if let Some(ref scl) = self.scl { + scl.update_pf(config.0.scl_pf()); + } + + self.config = config.0.clone(); + self.target_config = config.1.clone(); + + self.reset() + } +} + +impl<'d> I2cTarget<'d, Async> { + /// Create a new asynchronous I2C target driver using interrupts + /// The `config` reuses the i2c controller config to setup the clock while `target_config` + /// configures i2c target specific parameters. + pub fn new( + peri: Peri<'d, T>, + scl: Peri<'d, impl SclPin>, + sda: Peri<'d, impl SdaPin>, + _irq: impl interrupt::typelevel::Binding> + 'd, + config: i2c::Config, + target_config: i2c_target::Config, + ) -> Result { + let mut this = Self::new_inner( + peri, + new_pin!(scl, config.scl_pf()), + new_pin!(sda, config.sda_pf()), + config, + target_config, + ); + this.reset()?; + Ok(this) + } + + /// Reset the i2c peripheral. If you cancel a respond_to_read, you may stall the bus. + /// You can recover the bus by calling this function, but doing so will almost certainly cause + /// an i/o error in the controller. + pub fn reset(&mut self) -> Result<(), ConfigError> { + self.init()?; + unsafe { self.info.interrupt.enable() }; + Ok(()) + } +} + +impl<'d> I2cTarget<'d, Blocking> { + /// Create a new blocking I2C target driver. + /// The `config` reuses the i2c controller config to setup the clock while `target_config` + /// configures i2c target specific parameters. + pub fn new_blocking( + peri: Peri<'d, T>, + scl: Peri<'d, impl SclPin>, + sda: Peri<'d, impl SdaPin>, + config: i2c::Config, + target_config: i2c_target::Config, + ) -> Result { + let mut this = Self::new_inner( + peri, + new_pin!(scl, config.scl_pf()), + new_pin!(sda, config.sda_pf()), + config, + target_config, + ); + this.reset()?; + Ok(this) + } + + /// Reset the i2c peripheral. If you cancel a respond_to_read, you may stall the bus. + /// You can recover the bus by calling this function, but doing so will almost certainly cause + /// an i/o error in the controller. + pub fn reset(&mut self) -> Result<(), ConfigError> { + self.init()?; + Ok(()) + } +} + +impl<'d, M: Mode> I2cTarget<'d, M> { + fn new_inner( + _peri: Peri<'d, T>, + scl: Option>, + sda: Option>, + config: i2c::Config, + target_config: i2c_target::Config, + ) -> Self { + if let Some(ref scl) = scl { + let pincm = pac::IOMUX.pincm(scl._pin_cm() as usize); + pincm.modify(|w| { + w.set_hiz1(true); + }); + } + if let Some(ref sda) = sda { + let pincm = pac::IOMUX.pincm(sda._pin_cm() as usize); + pincm.modify(|w| { + w.set_hiz1(true); + }); + } + + Self { + info: T::info(), + state: T::state(), + scl, + sda, + config, + target_config, + _phantom: PhantomData, + } + } + + fn init(&mut self) -> Result<(), ConfigError> { + let mut config = self.config; + let target_config = self.target_config; + let regs = self.info.regs; + + config.check_config()?; + // Target address must be 7-bit + if !(target_config.target_addr < 0x80) { + return Err(ConfigError::InvalidTargetAddress); + } + + regs.target(0).tctr().modify(|w| { + w.set_active(false); + }); + + // Init power for I2C + regs.gprcm(0).rstctl().write(|w| { + w.set_resetstkyclr(true); + w.set_resetassert(true); + w.set_key(vals::ResetKey::KEY); + }); + + regs.gprcm(0).pwren().write(|w| { + w.set_enable(true); + w.set_key(vals::PwrenKey::KEY); + }); + + self.info.interrupt.disable(); + + // Init delay from the M0 examples by TI in CCStudio (16 cycles) + cortex_m::asm::delay(16); + + // Select and configure the I2C clock using the CLKSEL and CLKDIV registers + regs.clksel().write(|w| match config.clock_source { + ClockSel::BusClk => { + w.set_mfclk_sel(false); + w.set_busclk_sel(true); + } + ClockSel::MfClk => { + w.set_mfclk_sel(true); + w.set_busclk_sel(false); + } + }); + regs.clkdiv().write(|w| w.set_ratio(config.clock_div.into())); + + // Configure at least one target address by writing the 7-bit address to I2Cx.SOAR register. The additional + // target address can be enabled and configured by using I2Cx.TOAR2 register. + regs.target(0).toar().modify(|w| { + w.set_oaren(true); + w.set_oar(target_config.target_addr as u16); + }); + + self.state + .clock + .store(config.calculate_clock_source(), Ordering::Relaxed); + + regs.target(0).tctr().modify(|w| { + w.set_gencall(target_config.general_call); + w.set_tclkstretch(true); + // Disable target wakeup, follow TI example. (TI note: Workaround for errata I2C_ERR_04.) + w.set_twuen(false); + w.set_txempty_on_treq(true); + }); + + // Enable the I2C target mode by setting the ACTIVE bit in I2Cx.TCTR register. + regs.target(0).tctr().modify(|w| { + w.set_active(true); + }); + + Ok(()) + } + + #[inline(always)] + fn drain_fifo(&mut self, buffer: &mut [u8], offset: &mut usize) { + let regs = self.info.regs; + + for b in &mut buffer[*offset..] { + if regs.target(0).tfifosr().read().rxfifocnt() == 0 { + break; + } + + *b = regs.target(0).trxdata().read().value(); + *offset += 1; + } + } + + /// Blocking function to empty the tx fifo + /// + /// This function can be used to empty the transmit FIFO if data remains after handling a 'read' command (LeftoverBytes). + pub fn flush_tx_fifo(&mut self) { + self.info.regs.target(0).tfifoctl().modify(|w| { + w.set_txflush(true); + }); + while self.info.regs.target(0).tfifosr().read().txfifocnt() as usize != self.info.fifo_size {} + self.info.regs.target(0).tfifoctl().modify(|w| { + w.set_txflush(false); + }); + } +} + +impl<'d> I2cTarget<'d, Async> { + /// Wait asynchronously for commands from an I2C controller. + /// `buffer` is provided in case controller does a 'write', 'write read', or 'general call' and is unused for 'read'. + pub async fn listen(&mut self, buffer: &mut [u8]) -> Result { + let regs = self.info.regs; + + let mut len = 0; + + // Set the rx fifo interrupt to avoid a fifo overflow + regs.target(0).tfifoctl().modify(|r| { + r.set_rxtrig(vals::TfifoctlRxtrig::LEVEL_6); + }); + + self.wait_on( + |me| { + // Check if address matches the General Call address (0x00) + let is_gencall = regs.target(0).tsr().read().addrmatch() == 0; + + if regs.target(0).tfifosr().read().rxfifocnt() > 0 { + me.drain_fifo(buffer, &mut len); + } + + if buffer.len() == len && regs.target(0).tfifosr().read().rxfifocnt() > 0 { + if is_gencall { + return Poll::Ready(Err(Error::PartialGeneralCall(buffer.len()))); + } else { + return Poll::Ready(Err(Error::PartialWrite(buffer.len()))); + } + } + + let iidx = regs.cpu_int(0).iidx().read().stat(); + trace!("ls:{} len:{}", iidx as u8, len); + let result = match iidx { + CpuIntIidxStat::TTXEMPTY => match len { + 0 => Poll::Ready(Ok(Command::Read)), + w => Poll::Ready(Ok(Command::WriteRead(w))), + }, + CpuIntIidxStat::TSTOPFG => match (is_gencall, len) { + (_, 0) => Poll::Pending, + (true, w) => Poll::Ready(Ok(Command::GeneralCall(w))), + (false, w) => Poll::Ready(Ok(Command::Write(w))), + }, + _ => Poll::Pending, + }; + if !result.is_pending() { + regs.cpu_int(0).imask().write(|_| {}); + } + result + }, + |_me| { + regs.cpu_int(0).imask().write(|_| {}); + regs.cpu_int(0).imask().modify(|w| { + w.set_tgencall(true); + w.set_trxfifotrg(true); + w.set_tstop(true); + w.set_ttxempty(true); + }); + }, + ) + .await + } + + /// Respond to an I2C controller 'read' command, asynchronously. + pub async fn respond_to_read(&mut self, buffer: &[u8]) -> Result { + if buffer.is_empty() { + return Err(Error::InvalidResponseBufferLength); + } + + let regs = self.info.regs; + let fifo_size = self.info.fifo_size; + let mut chunks = buffer.chunks(self.info.fifo_size); + + self.wait_on( + |_me| { + if let Some(chunk) = chunks.next() { + for byte in chunk { + regs.target(0).ttxdata().write(|w| w.set_value(*byte)); + } + + return Poll::Pending; + } + + let iidx = regs.cpu_int(0).iidx().read().stat(); + let fifo_bytes = fifo_size - regs.target(0).tfifosr().read().txfifocnt() as usize; + trace!("rs:{}, fifo:{}", iidx as u8, fifo_bytes); + + let result = match iidx { + CpuIntIidxStat::TTXEMPTY => Poll::Ready(Ok(ReadStatus::NeedMoreBytes)), + CpuIntIidxStat::TSTOPFG => match fifo_bytes { + 0 => Poll::Ready(Ok(ReadStatus::Done)), + w => Poll::Ready(Ok(ReadStatus::LeftoverBytes(w as u16))), + }, + _ => Poll::Pending, + }; + if !result.is_pending() { + regs.cpu_int(0).imask().write(|_| {}); + } + result + }, + |_me| { + regs.cpu_int(0).imask().write(|_| {}); + regs.cpu_int(0).imask().modify(|w| { + w.set_ttxempty(true); + w.set_tstop(true); + }); + }, + ) + .await + } + + /// Respond to reads with the fill byte until the controller stops asking + pub async fn respond_till_stop(&mut self, fill: u8) -> Result<(), Error> { + // The buffer size could be increased to reduce interrupt noise but has higher probability + // of LeftoverBytes + let buff = [fill]; + loop { + match self.respond_to_read(&buff).await { + Ok(ReadStatus::NeedMoreBytes) => (), + Ok(_) => break Ok(()), + Err(e) => break Err(e), + } + } + } + + /// Respond to a controller read, then fill any remaining read bytes with `fill` + pub async fn respond_and_fill(&mut self, buffer: &[u8], fill: u8) -> Result { + let resp_stat = self.respond_to_read(buffer).await?; + + if resp_stat == ReadStatus::NeedMoreBytes { + self.respond_till_stop(fill).await?; + Ok(ReadStatus::Done) + } else { + Ok(resp_stat) + } + } + + /// Calls `f` to check if we are ready or not. + /// If not, `g` is called once(to eg enable the required interrupts). + /// The waker will always be registered prior to calling `f`. + #[inline(always)] + async fn wait_on(&mut self, mut f: F, mut g: G) -> U + where + F: FnMut(&mut Self) -> Poll, + G: FnMut(&mut Self), + { + poll_fn(|cx| { + // Register prior to checking the condition + self.state.waker.register(cx.waker()); + let r = f(self); + + if r.is_pending() { + g(self); + } + + r + }) + .await + } +} + +impl<'d, M: Mode> Drop for I2cTarget<'d, M> { + fn drop(&mut self) { + // Ensure peripheral is disabled and pins are reset + self.info.regs.target(0).tctr().modify(|w| w.set_active(false)); + + self.scl.as_ref().map(|x| x.set_as_disconnected()); + self.sda.as_ref().map(|x| x.set_as_disconnected()); + } +} diff --git a/embassy-mspm0/src/lib.rs b/embassy-mspm0/src/lib.rs index 13f0ce662..7135dd9f0 100644 --- a/embassy-mspm0/src/lib.rs +++ b/embassy-mspm0/src/lib.rs @@ -1,4 +1,5 @@ #![no_std] +#![allow(unsafe_op_in_unsafe_fn)] // Doc feature labels can be tested locally by running RUSTDOCFLAGS="--cfg=docsrs" cargo +nightly doc #![cfg_attr(docsrs, feature(doc_auto_cfg, doc_cfg_hide), doc(cfg_hide(doc, docsrs)))] #![cfg_attr( @@ -17,6 +18,7 @@ pub mod adc; pub mod dma; pub mod gpio; pub mod i2c; +pub mod i2c_target; pub mod timer; pub mod uart; pub mod wwdt; @@ -54,7 +56,7 @@ pub(crate) mod _generated { // Reexports pub(crate) use _generated::gpio_pincm; -pub use _generated::{peripherals, Peripherals}; +pub use _generated::{Peripherals, peripherals}; pub use embassy_hal_internal::Peri; #[cfg(feature = "unstable-pac")] pub use mspm0_metapac as pac; @@ -111,7 +113,7 @@ macro_rules! bind_interrupts { $( #[allow(non_snake_case)] - #[no_mangle] + #[unsafe(no_mangle)] $(#[cfg($cond_irq)])? unsafe extern "C" fn $irq() { unsafe { diff --git a/embassy-mspm0/src/time_driver.rs b/embassy-mspm0/src/time_driver.rs index e80e89e55..0743c667b 100644 --- a/embassy-mspm0/src/time_driver.rs +++ b/embassy-mspm0/src/time_driver.rs @@ -1,5 +1,5 @@ use core::cell::{Cell, RefCell}; -use core::sync::atomic::{compiler_fence, AtomicU32, Ordering}; +use core::sync::atomic::{AtomicU32, Ordering, compiler_fence}; use core::task::Waker; use critical_section::{CriticalSection, Mutex}; diff --git a/embassy-mspm0/src/uart/buffered.rs b/embassy-mspm0/src/uart/buffered.rs index cbc0b6c80..89e6bcc7b 100644 --- a/embassy-mspm0/src/uart/buffered.rs +++ b/embassy-mspm0/src/uart/buffered.rs @@ -1,4 +1,4 @@ -use core::future::{poll_fn, Future}; +use core::future::{Future, poll_fn}; use core::marker::PhantomData; use core::slice; use core::sync::atomic::{AtomicU8, Ordering}; @@ -14,7 +14,7 @@ use crate::gpio::{AnyPin, SealedPin}; use crate::interrupt::typelevel::Binding; use crate::pac::uart::Uart as Regs; use crate::uart::{Config, ConfigError, CtsPin, Error, Info, Instance, RtsPin, RxPin, State, TxPin}; -use crate::{interrupt, Peri}; +use crate::{Peri, interrupt}; /// Interrupt handler. pub struct BufferedInterruptHandler { diff --git a/embassy-mspm0/src/uart/mod.rs b/embassy-mspm0/src/uart/mod.rs index 6599cea06..03e68d297 100644 --- a/embassy-mspm0/src/uart/mod.rs +++ b/embassy-mspm0/src/uart/mod.rs @@ -3,17 +3,17 @@ mod buffered; use core::marker::PhantomData; -use core::sync::atomic::{compiler_fence, AtomicU32, Ordering}; +use core::sync::atomic::{AtomicU32, Ordering, compiler_fence}; pub use buffered::*; use embassy_embedded_hal::SetConfig; use embassy_hal_internal::PeripheralType; +use crate::Peri; use crate::gpio::{AnyPin, PfType, Pull, SealedPin}; use crate::interrupt::{Interrupt, InterruptExt}; use crate::mode::{Blocking, Mode}; -use crate::pac::uart::{vals, Uart as Regs}; -use crate::Peri; +use crate::pac::uart::{Uart as Regs, vals}; /// The clock source for the UART. #[derive(Clone, Copy, PartialEq, Eq, Debug)] @@ -931,8 +931,7 @@ fn set_baudrate_inner(regs: Regs, clock: u32, baudrate: u32) -> Result<(), Confi let Some(min_clock) = baudrate.checked_mul(oversampling as u32) else { trace!( "{}x oversampling would cause overflow for clock: {} Hz", - oversampling, - clock + oversampling, clock ); continue; }; @@ -945,9 +944,7 @@ fn set_baudrate_inner(regs: Regs, clock: u32, baudrate: u32) -> Result<(), Confi for &(div, div_value) in &DIVS { trace!( "Trying div: {}, oversampling {} for {} baud", - div, - oversampling, - baudrate + div, oversampling, baudrate ); let Some((ibrd, fbrd)) = calculate_brd(clock, div, baudrate, oversampling) else { diff --git a/embassy-mspm0/src/wwdt.rs b/embassy-mspm0/src/wwdt.rs index e5c62c660..92aeb8b40 100644 --- a/embassy-mspm0/src/wwdt.rs +++ b/embassy-mspm0/src/wwdt.rs @@ -6,9 +6,9 @@ use embassy_hal_internal::PeripheralType; -use crate::pac::wwdt::{vals, Wwdt as Regs}; -use crate::pac::{self}; use crate::Peri; +use crate::pac::wwdt::{Wwdt as Regs, vals}; +use crate::pac::{self}; /// Possible watchdog timeout values. #[derive(Clone, Copy, PartialEq, Eq, Debug)] diff --git a/embassy-net-adin1110/Cargo.toml b/embassy-net-adin1110/Cargo.toml index 587c69eb7..a5655870f 100644 --- a/embassy-net-adin1110/Cargo.toml +++ b/embassy-net-adin1110/Cargo.toml @@ -5,7 +5,7 @@ description = "embassy-net driver for the ADIN1110 ethernet chip" keywords = ["embedded", "ADIN1110", "embassy-net", "embedded-hal-async", "ethernet"] categories = ["embedded", "hardware-support", "no-std", "network-programming", "asynchronous"] license = "MIT OR Apache-2.0" -edition = "2021" +edition = "2024" repository = "https://github.com/embassy-rs/embassy" documentation = "https://docs.embassy.dev/embassy-net-adin1110" diff --git a/embassy-net-adin1110/src/crc8.rs b/embassy-net-adin1110/src/crc8.rs index 321983e64..a51353aab 100644 --- a/embassy-net-adin1110/src/crc8.rs +++ b/embassy-net-adin1110/src/crc8.rs @@ -23,7 +23,7 @@ pub fn crc8(data: &[u8]) -> u8 { #[cfg(test)] mod tests { - use ::crc::{Crc, CRC_8_SMBUS}; + use ::crc::{CRC_8_SMBUS, Crc}; use super::crc8; diff --git a/embassy-net-adin1110/src/lib.rs b/embassy-net-adin1110/src/lib.rs index 7f1c772e2..90ac242bd 100644 --- a/embassy-net-adin1110/src/lib.rs +++ b/embassy-net-adin1110/src/lib.rs @@ -17,9 +17,9 @@ mod phy; mod regs; use ch::driver::LinkState; -pub use crc32::ETH_FCS; use crc8::crc8; -use embassy_futures::select::{select, Either}; +pub use crc32::ETH_FCS; +use embassy_futures::select::{Either, select}; use embassy_net_driver_channel as ch; use embassy_time::Timer; use embedded_hal_1::digital::OutputPin; diff --git a/embassy-net-driver-channel/Cargo.toml b/embassy-net-driver-channel/Cargo.toml index 1e40c2d87..116057d6e 100644 --- a/embassy-net-driver-channel/Cargo.toml +++ b/embassy-net-driver-channel/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "embassy-net-driver-channel" version = "0.3.2" -edition = "2021" +edition = "2024" license = "MIT OR Apache-2.0" description = "High-level channel-based driver for the `embassy-net` async TCP/IP network stack." repository = "https://github.com/embassy-rs/embassy" diff --git a/embassy-net-driver-channel/src/lib.rs b/embassy-net-driver-channel/src/lib.rs index 600efd9e5..d0a14aa52 100644 --- a/embassy-net-driver-channel/src/lib.rs +++ b/embassy-net-driver-channel/src/lib.rs @@ -11,8 +11,8 @@ use core::task::{Context, Poll}; pub use embassy_net_driver as driver; use embassy_net_driver::{Capabilities, LinkState}; -use embassy_sync::blocking_mutex::raw::NoopRawMutex; use embassy_sync::blocking_mutex::Mutex; +use embassy_sync::blocking_mutex::raw::NoopRawMutex; use embassy_sync::waitqueue::WakerRegistration; use embassy_sync::zerocopy_channel; diff --git a/embassy-net-driver/Cargo.toml b/embassy-net-driver/Cargo.toml index a36e412ad..948fb2667 100644 --- a/embassy-net-driver/Cargo.toml +++ b/embassy-net-driver/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "embassy-net-driver" version = "0.2.0" -edition = "2021" +edition = "2024" license = "MIT OR Apache-2.0" description = "Driver trait for the `embassy-net` async TCP/IP network stack." repository = "https://github.com/embassy-rs/embassy" diff --git a/embassy-net-driver/src/lib.rs b/embassy-net-driver/src/lib.rs index 4c847718d..47babe34a 100644 --- a/embassy-net-driver/src/lib.rs +++ b/embassy-net-driver/src/lib.rs @@ -1,4 +1,5 @@ #![no_std] +#![allow(unsafe_op_in_unsafe_fn)] #![warn(missing_docs)] #![doc = include_str!("../README.md")] diff --git a/embassy-net-enc28j60/Cargo.toml b/embassy-net-enc28j60/Cargo.toml index e7bad118b..7c3e05922 100644 --- a/embassy-net-enc28j60/Cargo.toml +++ b/embassy-net-enc28j60/Cargo.toml @@ -5,7 +5,7 @@ description = "embassy-net driver for the ENC28J60 ethernet chip" keywords = ["embedded", "enc28j60", "embassy-net", "embedded-hal-async", "ethernet"] categories = ["embedded", "hardware-support", "no-std", "network-programming", "asynchronous"] license = "MIT OR Apache-2.0" -edition = "2021" +edition = "2024" repository = "https://github.com/embassy-rs/embassy" documentation = "https://docs.embassy.dev/embassy-net-enc28j60" diff --git a/embassy-net-esp-hosted/Cargo.toml b/embassy-net-esp-hosted/Cargo.toml index 149ff2bb2..f148f4762 100644 --- a/embassy-net-esp-hosted/Cargo.toml +++ b/embassy-net-esp-hosted/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "embassy-net-esp-hosted" version = "0.2.1" -edition = "2021" +edition = "2024" description = "embassy-net driver for ESP-Hosted" keywords = ["embedded", "esp-hosted", "embassy-net", "embedded-hal-async", "wifi"] categories = ["embedded", "hardware-support", "no-std", "network-programming", "asynchronous"] diff --git a/embassy-net-esp-hosted/src/control.rs b/embassy-net-esp-hosted/src/control.rs index b1838a425..cbc194877 100644 --- a/embassy-net-esp-hosted/src/control.rs +++ b/embassy-net-esp-hosted/src/control.rs @@ -65,8 +65,7 @@ macro_rules! ioctl { }; $self.ioctl(&mut msg).await?; #[allow(unused_mut)] - let Some(proto::CtrlMsgPayload::$resp_variant(mut $resp)) = msg.payload - else { + let Some(proto::CtrlMsgPayload::$resp_variant(mut $resp)) = msg.payload else { warn!("unexpected response variant"); return Err(Error::Internal); }; diff --git a/embassy-net-esp-hosted/src/ioctl.rs b/embassy-net-esp-hosted/src/ioctl.rs index 512023206..a516f80c7 100644 --- a/embassy-net-esp-hosted/src/ioctl.rs +++ b/embassy-net-esp-hosted/src/ioctl.rs @@ -1,5 +1,5 @@ use core::cell::RefCell; -use core::future::{poll_fn, Future}; +use core::future::{Future, poll_fn}; use core::task::Poll; use embassy_sync::waitqueue::WakerRegistration; diff --git a/embassy-net-esp-hosted/src/lib.rs b/embassy-net-esp-hosted/src/lib.rs index f05e2a70a..4405d9f77 100644 --- a/embassy-net-esp-hosted/src/lib.rs +++ b/embassy-net-esp-hosted/src/lib.rs @@ -2,7 +2,7 @@ #![doc = include_str!("../README.md")] #![warn(missing_docs)] -use embassy_futures::select::{select4, Either4}; +use embassy_futures::select::{Either4, select4}; use embassy_net_driver_channel as ch; use embassy_net_driver_channel::driver::LinkState; use embassy_time::{Duration, Instant, Timer}; diff --git a/embassy-net-nrf91/Cargo.toml b/embassy-net-nrf91/Cargo.toml index 35fbef3f6..ecb10246a 100644 --- a/embassy-net-nrf91/Cargo.toml +++ b/embassy-net-nrf91/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "embassy-net-nrf91" version = "0.1.1" -edition = "2021" +edition = "2024" description = "embassy-net driver for Nordic nRF91-series cellular modems" keywords = ["embedded", "nrf91", "embassy-net", "cellular"] categories = ["embedded", "hardware-support", "no-std", "network-programming", "asynchronous"] diff --git a/embassy-net-nrf91/src/lib.rs b/embassy-net-nrf91/src/lib.rs index 0bd9be0d9..dd4812aae 100644 --- a/embassy-net-nrf91/src/lib.rs +++ b/embassy-net-nrf91/src/lib.rs @@ -1,4 +1,5 @@ #![no_std] +#![allow(unsafe_op_in_unsafe_fn)] #![doc = include_str!("../README.md")] #![warn(missing_docs)] #![deny(unused_must_use)] @@ -9,12 +10,12 @@ mod fmt; pub mod context; use core::cell::RefCell; -use core::future::{poll_fn, Future}; +use core::future::{Future, poll_fn}; use core::marker::PhantomData; use core::mem::{self, MaybeUninit}; use core::ptr::{self, addr_of, addr_of_mut, copy_nonoverlapping}; use core::slice; -use core::sync::atomic::{compiler_fence, fence, Ordering}; +use core::sync::atomic::{Ordering, compiler_fence, fence}; use core::task::{Poll, Waker}; use cortex_m::peripheral::NVIC; @@ -139,9 +140,7 @@ async fn new_internal<'a>( debug!("Setting IPC RAM as nonsecure..."); trace!( " SPU_REGION_SIZE={}, shmem_ptr=0x{:08X}, shmem_len={}", - SPU_REGION_SIZE, - shmem_ptr as usize, - shmem_len + SPU_REGION_SIZE, shmem_ptr as usize, shmem_len ); let region_start = (shmem_ptr as usize - 0x2000_0000) / SPU_REGION_SIZE; let region_end = region_start + shmem_len / SPU_REGION_SIZE; @@ -165,8 +164,7 @@ async fn new_internal<'a>( }; trace!( " Allocator: start=0x{:08X}, end=0x{:08X}", - alloc.start as usize, - alloc.end as usize + alloc.start as usize, alloc.end as usize ); let cb: &mut ControlBlock = alloc.alloc().write(unsafe { mem::zeroed() }); diff --git a/embassy-net-ppp/Cargo.toml b/embassy-net-ppp/Cargo.toml index 644f5b827..45ee2f6b5 100644 --- a/embassy-net-ppp/Cargo.toml +++ b/embassy-net-ppp/Cargo.toml @@ -5,7 +5,7 @@ description = "embassy-net driver for PPP over Serial" keywords = ["embedded", "ppp", "embassy-net", "embedded-hal-async", "async"] categories = ["embedded", "hardware-support", "no-std", "network-programming", "asynchronous"] license = "MIT OR Apache-2.0" -edition = "2021" +edition = "2024" repository = "https://github.com/embassy-rs/embassy" documentation = "https://docs.embassy.dev/embassy-net-ppp" diff --git a/embassy-net-ppp/src/lib.rs b/embassy-net-ppp/src/lib.rs index 54a98c95f..ab42ecd26 100644 --- a/embassy-net-ppp/src/lib.rs +++ b/embassy-net-ppp/src/lib.rs @@ -8,7 +8,7 @@ mod fmt; use core::convert::Infallible; use core::mem::MaybeUninit; -use embassy_futures::select::{select, Either}; +use embassy_futures::select::{Either, select}; use embassy_net_driver_channel as ch; use embassy_net_driver_channel::driver::LinkState; use embedded_io_async::{BufRead, Write}; diff --git a/embassy-net-tuntap/Cargo.toml b/embassy-net-tuntap/Cargo.toml index 2cd0c0f7e..77668b445 100644 --- a/embassy-net-tuntap/Cargo.toml +++ b/embassy-net-tuntap/Cargo.toml @@ -5,7 +5,7 @@ description = "embassy-net driver for Linux TUN/TAP interfaces." keywords = ["embedded", "tuntap", "embassy-net", "ethernet", "async"] categories = ["embedded", "hardware-support", "network-programming", "asynchronous"] license = "MIT OR Apache-2.0" -edition = "2021" +edition = "2024" repository = "https://github.com/embassy-rs/embassy" documentation = "https://docs.embassy.dev/embassy-net-tuntap" diff --git a/embassy-net-wiznet/Cargo.toml b/embassy-net-wiznet/Cargo.toml index 6e6dccebd..069f46fc3 100644 --- a/embassy-net-wiznet/Cargo.toml +++ b/embassy-net-wiznet/Cargo.toml @@ -5,7 +5,7 @@ description = "embassy-net driver for WIZnet SPI Ethernet chips" keywords = ["embedded", "embassy-net", "embedded-hal-async", "ethernet", "async"] categories = ["embedded", "hardware-support", "no-std", "network-programming", "asynchronous"] license = "MIT OR Apache-2.0" -edition = "2021" +edition = "2024" repository = "https://github.com/embassy-rs/embassy" documentation = "https://docs.embassy.dev/embassy-net-wiznet" diff --git a/embassy-net-wiznet/src/chip/mod.rs b/embassy-net-wiznet/src/chip/mod.rs index 47d7c5dc3..04346bb21 100644 --- a/embassy-net-wiznet/src/chip/mod.rs +++ b/embassy-net-wiznet/src/chip/mod.rs @@ -43,7 +43,7 @@ pub(crate) trait SealedChip { fn tx_addr(addr: u16) -> Self::Address; async fn bus_read(spi: &mut SPI, address: Self::Address, data: &mut [u8]) - -> Result<(), SPI::Error>; + -> Result<(), SPI::Error>; async fn bus_write(spi: &mut SPI, address: Self::Address, data: &[u8]) -> Result<(), SPI::Error>; } diff --git a/embassy-net-wiznet/src/lib.rs b/embassy-net-wiznet/src/lib.rs index 3fbd4c741..30a5db9f6 100644 --- a/embassy-net-wiznet/src/lib.rs +++ b/embassy-net-wiznet/src/lib.rs @@ -6,7 +6,7 @@ pub mod chip; mod device; -use embassy_futures::select::{select3, Either3}; +use embassy_futures::select::{Either3, select3}; use embassy_net_driver_channel as ch; use embassy_net_driver_channel::driver::LinkState; use embassy_time::{Duration, Ticker, Timer}; diff --git a/embassy-net/Cargo.toml b/embassy-net/Cargo.toml index 31ce7e9a6..4c8075c43 100644 --- a/embassy-net/Cargo.toml +++ b/embassy-net/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "embassy-net" version = "0.7.1" -edition = "2021" +edition = "2024" license = "MIT OR Apache-2.0" description = "Async TCP/IP network stack for embedded systems" repository = "https://github.com/embassy-rs/embassy" diff --git a/embassy-net/src/icmp.rs b/embassy-net/src/icmp.rs index 22c31a589..09e91a1ae 100644 --- a/embassy-net/src/icmp.rs +++ b/embassy-net/src/icmp.rs @@ -1,6 +1,6 @@ //! ICMP sockets. -use core::future::{poll_fn, Future}; +use core::future::{Future, poll_fn}; use core::mem; use core::task::{Context, Poll}; diff --git a/embassy-net/src/lib.rs b/embassy-net/src/lib.rs index 3f0634849..a49955c96 100644 --- a/embassy-net/src/lib.rs +++ b/embassy-net/src/lib.rs @@ -1,5 +1,6 @@ #![no_std] #![allow(async_fn_in_trait)] +#![allow(unsafe_op_in_unsafe_fn)] #![warn(missing_docs)] #![doc = include_str!("../README.md")] @@ -26,7 +27,7 @@ mod time; pub mod udp; use core::cell::RefCell; -use core::future::{poll_fn, Future}; +use core::future::{Future, poll_fn}; use core::mem::MaybeUninit; use core::pin::pin; use core::task::{Context, Poll}; diff --git a/embassy-net/src/raw.rs b/embassy-net/src/raw.rs index c9f753f13..89d2dd350 100644 --- a/embassy-net/src/raw.rs +++ b/embassy-net/src/raw.rs @@ -1,6 +1,6 @@ //! Raw sockets. -use core::future::{poll_fn, Future}; +use core::future::{Future, poll_fn}; use core::mem; use core::task::{Context, Poll}; diff --git a/embassy-net/src/tcp.rs b/embassy-net/src/tcp.rs index d0230b581..6792c5526 100644 --- a/embassy-net/src/tcp.rs +++ b/embassy-net/src/tcp.rs @@ -8,7 +8,7 @@ //! Incoming connections when no socket is listening are rejected. To accept many incoming //! connections, create many sockets and put them all into listening mode. -use core::future::{poll_fn, Future}; +use core::future::{Future, poll_fn}; use core::mem; use core::task::{Context, Poll}; @@ -18,8 +18,8 @@ use smoltcp::socket::tcp; pub use smoltcp::socket::tcp::State; use smoltcp::wire::{IpEndpoint, IpListenEndpoint}; -use crate::time::duration_to_smoltcp; use crate::Stack; +use crate::time::duration_to_smoltcp; /// Error returned by TcpSocket read/write functions. #[derive(PartialEq, Eq, Clone, Copy, Debug)] diff --git a/embassy-net/src/udp.rs b/embassy-net/src/udp.rs index 482eb0e56..448c25ecc 100644 --- a/embassy-net/src/udp.rs +++ b/embassy-net/src/udp.rs @@ -1,6 +1,6 @@ //! UDP sockets. -use core::future::{poll_fn, Future}; +use core::future::{Future, poll_fn}; use core::mem; use core::task::{Context, Poll}; diff --git a/embassy-nrf/CHANGELOG.md b/embassy-nrf/CHANGELOG.md index b3d4045fa..0280e2730 100644 --- a/embassy-nrf/CHANGELOG.md +++ b/embassy-nrf/CHANGELOG.md @@ -8,6 +8,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## Unreleased - ReleaseDate +- added: Add basic RTC support for nRF54L +- changed: apply trimming values from FICR.TRIMCNF on nrf53/54l +- changed: do not panic on BufferedUarte overrun +- added: allow direct access to the input pin of `gpiote::InputChannel` +- bugfix: use DETECTMODE_SEC in GPIOTE in secure mode + ## 0.8.0 - 2025-09-30 - changed: Remove `T: Instance` generic params in all drivers. diff --git a/embassy-nrf/Cargo.toml b/embassy-nrf/Cargo.toml index 362fabcf7..28f137d5c 100644 --- a/embassy-nrf/Cargo.toml +++ b/embassy-nrf/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "embassy-nrf" version = "0.8.0" -edition = "2021" +edition = "2024" license = "MIT OR Apache-2.0" description = "Embassy Hardware Abstraction Layer (HAL) for nRF series microcontrollers" keywords = ["embedded", "async", "nordic", "nrf", "embedded-hal"] @@ -80,6 +80,8 @@ unstable-pac = [] gpiote = [] ## Use RTC1 as the time driver for `embassy-time`, with a tick rate of 32.768khz +## +## Note: For nRF54L, it's actually RTC30 time-driver-rtc1 = ["_time-driver"] ## Enable embassy-net 802.15.4 driver diff --git a/embassy-nrf/src/buffered_uarte.rs b/embassy-nrf/src/buffered_uarte.rs index 40c679190..b1eb5c81a 100644 --- a/embassy-nrf/src/buffered_uarte.rs +++ b/embassy-nrf/src/buffered_uarte.rs @@ -9,27 +9,27 @@ //! Please also see [crate::uarte] to understand when [BufferedUarte] should be used. use core::cmp::min; -use core::future::{poll_fn, Future}; +use core::future::{Future, poll_fn}; use core::marker::PhantomData; use core::slice; -use core::sync::atomic::{compiler_fence, AtomicBool, AtomicU8, AtomicUsize, Ordering}; +use core::sync::atomic::{AtomicBool, AtomicU8, AtomicUsize, Ordering, compiler_fence}; use core::task::Poll; -use embassy_hal_internal::atomic_ring_buffer::RingBuffer; use embassy_hal_internal::Peri; +use embassy_hal_internal::atomic_ring_buffer::RingBuffer; use pac::uarte::vals; // Re-export SVD variants to allow user to directly set values pub use pac::uarte::vals::{Baudrate, ConfigParity as Parity}; use crate::gpio::{AnyPin, Pin as GpioPin}; -use crate::interrupt::typelevel::Interrupt; use crate::interrupt::InterruptExt; +use crate::interrupt::typelevel::Interrupt; use crate::ppi::{ self, AnyConfigurableChannel, AnyGroup, Channel, ConfigurableChannel, Event, Group, Ppi, PpiGroup, Task, }; use crate::timer::{Instance as TimerInstance, Timer}; -use crate::uarte::{configure, configure_rx_pins, configure_tx_pins, drop_tx_rx, Config, Instance as UarteInstance}; -use crate::{interrupt, pac, EASY_DMA_SIZE}; +use crate::uarte::{Config, Instance as UarteInstance, configure, configure_rx_pins, configure_tx_pins, drop_tx_rx}; +use crate::{EASY_DMA_SIZE, interrupt, pac}; pub(crate) struct State { tx_buf: RingBuffer, @@ -40,6 +40,7 @@ pub(crate) struct State { rx_started_count: AtomicU8, rx_ended_count: AtomicU8, rx_ppi_ch: AtomicU8, + rx_overrun: AtomicBool, } /// UART error. @@ -47,7 +48,8 @@ pub(crate) struct State { #[cfg_attr(feature = "defmt", derive(defmt::Format))] #[non_exhaustive] pub enum Error { - // No errors for now + /// Buffer Overrun + Overrun, } impl State { @@ -61,6 +63,7 @@ impl State { rx_started_count: AtomicU8::new(0), rx_ended_count: AtomicU8::new(0), rx_ppi_ch: AtomicU8::new(0), + rx_overrun: AtomicBool::new(false), } } } @@ -87,7 +90,8 @@ impl interrupt::typelevel::Handler for Interrupt r.errorsrc().write_value(errs); if errs.overrun() { - panic!("BufferedUarte overrun"); + s.rx_overrun.store(true, Ordering::Release); + ss.rx_waker.wake(); } } @@ -689,6 +693,7 @@ impl<'d> BufferedUarteRx<'d> { buffered_state.rx_started_count.store(0, Ordering::Relaxed); buffered_state.rx_ended_count.store(0, Ordering::Relaxed); buffered_state.rx_started.store(false, Ordering::Relaxed); + buffered_state.rx_overrun.store(false, Ordering::Relaxed); let rx_len = rx_buffer.len().min(EASY_DMA_SIZE * 2); unsafe { buffered_state.rx_buf.init(rx_buffer.as_mut_ptr(), rx_len) }; @@ -762,6 +767,10 @@ impl<'d> BufferedUarteRx<'d> { compiler_fence(Ordering::SeqCst); //trace!("poll_read"); + if s.rx_overrun.swap(false, Ordering::Acquire) { + return Poll::Ready(Err(Error::Overrun)); + } + // Read the RXDRDY counter. timer.cc(0).capture(); let mut end = timer.cc(0).read() as usize; @@ -820,6 +829,9 @@ impl<'d> BufferedUarteRx<'d> { /// we are ready to read if there is data in the buffer fn read_ready(&self) -> Result { let state = self.buffered_state; + if state.rx_overrun.swap(false, Ordering::Acquire) { + return Err(Error::Overrun); + } Ok(!state.rx_buf.is_empty()) } } @@ -854,7 +866,9 @@ mod _embedded_io { impl embedded_io_async::Error for Error { fn kind(&self) -> embedded_io_async::ErrorKind { - match *self {} + match *self { + Error::Overrun => embedded_io_async::ErrorKind::OutOfMemory, + } } } diff --git a/embassy-nrf/src/chips/nrf54l15_app.rs b/embassy-nrf/src/chips/nrf54l15_app.rs index ff05bbec0..901c5e7fc 100644 --- a/embassy-nrf/src/chips/nrf54l15_app.rs +++ b/embassy-nrf/src/chips/nrf54l15_app.rs @@ -94,6 +94,7 @@ pub mod pac { #[cfg(feature = "_s")] #[doc(no_inline)] pub use nrf_pac::{ + FICR_NS as FICR, SICR_S as SICR, ICACHEDATA_S as ICACHEDATA, ICACHEINFO_S as ICACHEINFO, @@ -248,6 +249,45 @@ embassy_hal_internal::peripherals! { P2_09, P2_10, + // RTC + RTC10, + RTC30, + + // SERIAL + SERIAL00, + SERIAL20, + SERIAL21, + SERIAL22, + SERIAL30, + + // SAADC + SAADC, + + // RADIO + RADIO, + + // TIMER + TIMER00, + TIMER10, + TIMER20, + + // PPI BRIDGE + PPIB00, + PPIB01, + PPIB10, + PPIB11, + PPIB20, + PPIB21, + PPIB22, + PPIB30, + + // GPIOTE + GPIOTE20, + GPIOTE30, + + // CRACEN + CRACEN, + #[cfg(feature = "_s")] // RRAMC RRAMC, @@ -302,6 +342,9 @@ impl_pin!(P2_08, 2, 8); impl_pin!(P2_09, 2, 9); impl_pin!(P2_10, 2, 10); +impl_rtc!(RTC10, RTC10, RTC10); +impl_rtc!(RTC30, RTC30, RTC30); + #[cfg(feature = "_ns")] impl_wdt!(WDT, WDT31, WDT31, 0); #[cfg(feature = "_s")] diff --git a/embassy-nrf/src/egu.rs b/embassy-nrf/src/egu.rs index f7372fca1..666986115 100644 --- a/embassy-nrf/src/egu.rs +++ b/embassy-nrf/src/egu.rs @@ -10,7 +10,7 @@ use core::marker::PhantomData; use embassy_hal_internal::PeripheralType; use crate::ppi::{Event, Task}; -use crate::{interrupt, pac, Peri}; +use crate::{Peri, interrupt, pac}; /// An instance of the EGU. pub struct Egu<'d> { diff --git a/embassy-nrf/src/embassy_net_802154_driver.rs b/embassy-nrf/src/embassy_net_802154_driver.rs index b3fc5df2c..4c47b7cbd 100644 --- a/embassy-nrf/src/embassy_net_802154_driver.rs +++ b/embassy-nrf/src/embassy_net_802154_driver.rs @@ -1,12 +1,12 @@ //! embassy-net IEEE 802.15.4 driver -use embassy_futures::select::{select3, Either3}; +use embassy_futures::select::{Either3, select3}; use embassy_net_driver_channel::driver::LinkState; use embassy_net_driver_channel::{self as ch}; use embassy_time::{Duration, Ticker}; -use crate::radio::ieee802154::{Packet, Radio}; use crate::radio::InterruptHandler; +use crate::radio::ieee802154::{Packet, Radio}; use crate::{self as nrf, interrupt}; /// MTU for the nrf radio. diff --git a/embassy-nrf/src/gpio.rs b/embassy-nrf/src/gpio.rs index ab5e7ed4b..7ed3a7927 100644 --- a/embassy-nrf/src/gpio.rs +++ b/embassy-nrf/src/gpio.rs @@ -5,10 +5,10 @@ use core::convert::Infallible; use core::hint::unreachable_unchecked; use cfg_if::cfg_if; -use embassy_hal_internal::{impl_peripheral, Peri, PeripheralType}; +use embassy_hal_internal::{Peri, PeripheralType, impl_peripheral}; use crate::pac; -use crate::pac::common::{Reg, RW}; +use crate::pac::common::{RW, Reg}; use crate::pac::gpio; use crate::pac::gpio::vals; #[cfg(not(feature = "_nrf51"))] diff --git a/embassy-nrf/src/gpiote.rs b/embassy-nrf/src/gpiote.rs index 43e43f0bf..3658657c0 100644 --- a/embassy-nrf/src/gpiote.rs +++ b/embassy-nrf/src/gpiote.rs @@ -1,10 +1,10 @@ //! GPIO task/event (GPIOTE) driver. use core::convert::Infallible; -use core::future::{poll_fn, Future}; +use core::future::{Future, poll_fn}; use core::task::{Context, Poll}; -use embassy_hal_internal::{impl_peripheral, Peri, PeripheralType}; +use embassy_hal_internal::{Peri, PeripheralType, impl_peripheral}; use embassy_sync::waitqueue::AtomicWaker; use crate::gpio::{AnyPin, Flex, Input, Output, Pin as GpioPin, SealedPin as _}; @@ -77,6 +77,9 @@ pub(crate) fn init(irq_prio: crate::interrupt::Priority) { for &p in ports { // Enable latched detection + #[cfg(feature = "_s")] + p.detectmode_sec().write(|w| w.set_detectmode(Detectmode::LDETECT)); + #[cfg(not(feature = "_s"))] p.detectmode().write(|w| w.set_detectmode(Detectmode::LDETECT)); // Clear latch p.latch().write(|w| w.0 = 0xFFFFFFFF) @@ -259,6 +262,11 @@ impl<'d> InputChannel<'d> { .await; } + /// Get the associated input pin. + pub fn pin(&self) -> &Input<'_> { + &self.pin + } + /// Returns the IN event, for use with PPI. pub fn event_in(&self) -> Event<'d> { let g = regs(); diff --git a/embassy-nrf/src/i2s.rs b/embassy-nrf/src/i2s.rs index 1bfa18491..9cce9f1e8 100644 --- a/embassy-nrf/src/i2s.rs +++ b/embassy-nrf/src/i2s.rs @@ -6,7 +6,7 @@ use core::future::poll_fn; use core::marker::PhantomData; use core::mem::size_of; use core::ops::{Deref, DerefMut}; -use core::sync::atomic::{compiler_fence, AtomicBool, Ordering}; +use core::sync::atomic::{AtomicBool, Ordering, compiler_fence}; use core::task::Poll; use embassy_hal_internal::drop::OnDrop; @@ -17,7 +17,7 @@ use crate::gpio::{AnyPin, Pin as GpioPin, PselBits}; use crate::interrupt::typelevel::Interrupt; use crate::pac::i2s::vals; use crate::util::slice_in_ram_or; -use crate::{interrupt, pac, EASY_DMA_SIZE}; +use crate::{EASY_DMA_SIZE, interrupt, pac}; /// Type alias for `MultiBuffering` with 2 buffers. pub type DoubleBuffering = MultiBuffering; diff --git a/embassy-nrf/src/lib.rs b/embassy-nrf/src/lib.rs index 7c26a6184..705c77453 100644 --- a/embassy-nrf/src/lib.rs +++ b/embassy-nrf/src/lib.rs @@ -1,5 +1,6 @@ #![no_std] #![allow(async_fn_in_trait)] +#![allow(unsafe_op_in_unsafe_fn)] #![cfg_attr( docsrs, doc = "

You might want to browse the `embassy-nrf` documentation on the Embassy website instead.

The documentation here on `docs.rs` is built for a single chip only (nRF52840 in particular), while on the Embassy website you can pick your exact chip from the top menu. Available peripherals and their APIs change depending on the chip.

\n\n" @@ -154,7 +155,6 @@ pub mod reset; #[cfg(not(feature = "_nrf54l"))] // TODO #[cfg(not(any(feature = "_nrf5340-app", feature = "_nrf91")))] pub mod rng; -#[cfg(not(feature = "_nrf54l"))] // TODO pub mod rtc; #[cfg(not(feature = "_nrf54l"))] // TODO #[cfg(not(any(feature = "_nrf51", feature = "nrf52820", feature = "_nrf5340-net")))] @@ -252,7 +252,7 @@ macro_rules! bind_interrupts { $( #[allow(non_snake_case)] - #[no_mangle] + #[unsafe(no_mangle)] $(#[cfg($cond_irq)])? unsafe extern "C" fn $irq() { unsafe { @@ -284,7 +284,7 @@ macro_rules! bind_interrupts { pub use chip::pac; #[cfg(not(feature = "unstable-pac"))] pub(crate) use chip::pac; -pub use chip::{peripherals, Peripherals, EASY_DMA_SIZE}; +pub use chip::{EASY_DMA_SIZE, Peripherals, peripherals}; pub use embassy_hal_internal::{Peri, PeripheralType}; pub use crate::chip::interrupt; @@ -406,9 +406,10 @@ pub mod config { /// Settings for the internal capacitors. #[cfg(feature = "nrf5340-app-s")] pub struct InternalCapacitors { - /// Config for the internal capacitors on pins XC1 and XC2. + /// Config for the internal capacitors on pins XC1 and XC2. Pass `None` to not touch it. pub hfxo: Option, - /// Config for the internal capacitors between pins XL1 and XL2. + /// Config for the internal capacitors between pins XL1 and XL2. Pass `None` to not touch + /// it. pub lfxo: Option, } @@ -416,6 +417,8 @@ pub mod config { #[cfg(feature = "nrf5340-app-s")] #[derive(Copy, Clone)] pub enum HfxoCapacitance { + /// Use external capacitors + External, /// 7.0 pF _7_0pF, /// 7.5 pF @@ -475,8 +478,9 @@ pub mod config { #[cfg(feature = "nrf5340-app-s")] impl HfxoCapacitance { /// The capacitance value times two. - pub(crate) const fn value2(self) -> i32 { + pub(crate) fn value2(self) -> i32 { match self { + HfxoCapacitance::External => unreachable!(), HfxoCapacitance::_7_0pF => 14, HfxoCapacitance::_7_5pF => 15, HfxoCapacitance::_8_0pF => 16, @@ -506,11 +510,17 @@ pub mod config { HfxoCapacitance::_20_0pF => 40, } } + + pub(crate) fn external(self) -> bool { + matches!(self, Self::External) + } } /// Internal capacitance value for the LFXO. #[cfg(feature = "nrf5340-app-s")] pub enum LfxoCapacitance { + /// Use external capacitors + External = 0, /// 6 pF _6pF = 1, /// 7 pF @@ -523,6 +533,7 @@ pub mod config { impl From for super::pac::oscillators::vals::Intcap { fn from(t: LfxoCapacitance) -> Self { match t { + LfxoCapacitance::External => Self::EXTERNAL, LfxoCapacitance::_6pF => Self::C6PF, LfxoCapacitance::_7pF => Self::C7PF, LfxoCapacitance::_9pF => Self::C9PF, @@ -720,6 +731,29 @@ pub fn init(config: config::Config) -> Peripherals { } } + // Apply trimming values from the FICR. + #[cfg(any( + all(feature = "_nrf5340-app", feature = "_s"), + all(feature = "_nrf54l", feature = "_s"), + feature = "_nrf5340-net", + ))] + { + #[cfg(feature = "_nrf5340")] + let n = 32; + #[cfg(feature = "_nrf54l")] + let n = 64; + for i in 0..n { + let info = pac::FICR.trimcnf(i); + let addr = info.addr().read(); + if addr == 0 || addr == 0xFFFF_FFFF { + break; + } + unsafe { + (addr as *mut u32).write_volatile(info.data().read()); + } + } + } + // GLITCHDET is only accessible for secure code #[cfg(all(feature = "_nrf54l", feature = "_s"))] { @@ -953,17 +987,21 @@ pub fn init(config: config::Config) -> Peripherals { #[cfg(feature = "nrf5340-app-s")] { if let Some(cap) = config.internal_capacitors.hfxo { - let mut slope = pac::FICR.xosc32mtrim().read().slope() as i32; - let offset = pac::FICR.xosc32mtrim().read().offset() as i32; - // slope is a signed 5-bit integer - if slope >= 16 { - slope -= 32; + if cap.external() { + pac::OSCILLATORS.xosc32mcaps().write(|w| w.set_enable(false)); + } else { + let mut slope = pac::FICR.xosc32mtrim().read().slope() as i32; + let offset = pac::FICR.xosc32mtrim().read().offset() as i32; + // slope is a signed 5-bit integer + if slope >= 16 { + slope -= 32; + } + let capvalue = (((slope + 56) * (cap.value2() - 14)) + ((offset - 8) << 4) + 32) >> 6; + pac::OSCILLATORS.xosc32mcaps().write(|w| { + w.set_capvalue(capvalue as u8); + w.set_enable(true); + }); } - let capvalue = (((slope + 56) * (cap.value2() - 14)) + ((offset - 8) << 4) + 32) >> 6; - pac::OSCILLATORS.xosc32mcaps().write(|w| { - w.set_capvalue(capvalue as u8); - w.set_enable(true); - }); } if let Some(cap) = config.internal_capacitors.lfxo { pac::OSCILLATORS.xosc32ki().intcap().write(|w| w.set_intcap(cap.into())); diff --git a/embassy-nrf/src/nfct.rs b/embassy-nrf/src/nfct.rs index 8d70ec954..bfbdc2906 100644 --- a/embassy-nrf/src/nfct.rs +++ b/embassy-nrf/src/nfct.rs @@ -10,18 +10,18 @@ #![macro_use] use core::future::poll_fn; -use core::sync::atomic::{compiler_fence, Ordering}; +use core::sync::atomic::{Ordering, compiler_fence}; use core::task::Poll; use embassy_sync::waitqueue::AtomicWaker; pub use vals::{Bitframesdd as SddPat, Discardmode as DiscardMode}; use crate::interrupt::InterruptExt; -use crate::pac::nfct::vals; use crate::pac::NFCT; +use crate::pac::nfct::vals; use crate::peripherals::NFCT; use crate::util::slice_in_ram; -use crate::{interrupt, pac, Peri}; +use crate::{Peri, interrupt, pac}; /// NFCID1 (aka UID) of different sizes. #[derive(Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug)] diff --git a/embassy-nrf/src/nvmc.rs b/embassy-nrf/src/nvmc.rs index c46af0b34..3f38cd0f5 100644 --- a/embassy-nrf/src/nvmc.rs +++ b/embassy-nrf/src/nvmc.rs @@ -8,7 +8,7 @@ use embedded_storage::nor_flash::{ use crate::pac::nvmc::vals; use crate::peripherals::NVMC; -use crate::{pac, Peri}; +use crate::{Peri, pac}; #[cfg(not(feature = "_nrf5340-net"))] /// Erase size of NVMC flash in bytes. diff --git a/embassy-nrf/src/pdm.rs b/embassy-nrf/src/pdm.rs index b6ee52850..bc28f5c8a 100644 --- a/embassy-nrf/src/pdm.rs +++ b/embassy-nrf/src/pdm.rs @@ -4,7 +4,7 @@ use core::future::poll_fn; use core::marker::PhantomData; -use core::sync::atomic::{compiler_fence, Ordering}; +use core::sync::atomic::{Ordering, compiler_fence}; use core::task::Poll; use embassy_hal_internal::drop::OnDrop; @@ -13,7 +13,7 @@ use embassy_sync::waitqueue::AtomicWaker; use fixed::types::I7F1; use crate::chip::EASY_DMA_SIZE; -use crate::gpio::{AnyPin, Pin as GpioPin, SealedPin, DISCONNECTED}; +use crate::gpio::{AnyPin, DISCONNECTED, Pin as GpioPin, SealedPin}; use crate::interrupt::typelevel::Interrupt; use crate::pac::gpio::vals as gpiovals; use crate::pac::pdm::vals; diff --git a/embassy-nrf/src/ppi/dppi.rs b/embassy-nrf/src/ppi/dppi.rs index 078d2fd1c..168647be3 100644 --- a/embassy-nrf/src/ppi/dppi.rs +++ b/embassy-nrf/src/ppi/dppi.rs @@ -1,5 +1,5 @@ use super::{Channel, ConfigurableChannel, Event, Ppi, Task}; -use crate::{pac, Peri}; +use crate::{Peri, pac}; const DPPI_ENABLE_BIT: u32 = 0x8000_0000; const DPPI_CHANNEL_MASK: u32 = 0x0000_00FF; diff --git a/embassy-nrf/src/ppi/mod.rs b/embassy-nrf/src/ppi/mod.rs index 2bcf72e9c..f30c2218d 100644 --- a/embassy-nrf/src/ppi/mod.rs +++ b/embassy-nrf/src/ppi/mod.rs @@ -18,9 +18,9 @@ use core::marker::PhantomData; use core::ptr::NonNull; -use embassy_hal_internal::{impl_peripheral, Peri, PeripheralType}; +use embassy_hal_internal::{Peri, PeripheralType, impl_peripheral}; -use crate::pac::common::{Reg, RW, W}; +use crate::pac::common::{RW, Reg, W}; use crate::peripherals; #[cfg_attr(feature = "_dppi", path = "dppi.rs")] diff --git a/embassy-nrf/src/ppi/ppi.rs b/embassy-nrf/src/ppi/ppi.rs index 531c25444..18bc8b8db 100644 --- a/embassy-nrf/src/ppi/ppi.rs +++ b/embassy-nrf/src/ppi/ppi.rs @@ -1,5 +1,5 @@ use super::{Channel, ConfigurableChannel, Event, Ppi, Task}; -use crate::{pac, Peri}; +use crate::{Peri, pac}; impl<'d> Task<'d> { fn reg_val(&self) -> u32 { diff --git a/embassy-nrf/src/pwm.rs b/embassy-nrf/src/pwm.rs index d67cb546b..e038f44b8 100644 --- a/embassy-nrf/src/pwm.rs +++ b/embassy-nrf/src/pwm.rs @@ -2,11 +2,11 @@ #![macro_use] -use core::sync::atomic::{compiler_fence, Ordering}; +use core::sync::atomic::{Ordering, compiler_fence}; use embassy_hal_internal::{Peri, PeripheralType}; -use crate::gpio::{convert_drive, AnyPin, OutputDrive, Pin as GpioPin, PselBits, SealedPin as _, DISCONNECTED}; +use crate::gpio::{AnyPin, DISCONNECTED, OutputDrive, Pin as GpioPin, PselBits, SealedPin as _, convert_drive}; use crate::pac::gpio::vals as gpiovals; use crate::pac::pwm::vals; use crate::ppi::{Event, Task}; diff --git a/embassy-nrf/src/qspi.rs b/embassy-nrf/src/qspi.rs index 6f4524716..6bb7c033e 100755 --- a/embassy-nrf/src/qspi.rs +++ b/embassy-nrf/src/qspi.rs @@ -2,7 +2,7 @@ #![macro_use] -use core::future::{poll_fn, Future}; +use core::future::{Future, poll_fn}; use core::marker::PhantomData; use core::ptr; use core::task::Poll; diff --git a/embassy-nrf/src/radio/ieee802154.rs b/embassy-nrf/src/radio/ieee802154.rs index 62af03a5a..54b463343 100644 --- a/embassy-nrf/src/radio/ieee802154.rs +++ b/embassy-nrf/src/radio/ieee802154.rs @@ -1,18 +1,18 @@ //! IEEE 802.15.4 radio driver use core::marker::PhantomData; -use core::sync::atomic::{compiler_fence, Ordering}; +use core::sync::atomic::{Ordering, compiler_fence}; use core::task::Poll; use embassy_hal_internal::drop::OnDrop; use super::{Error, InterruptHandler, TxPower}; +use crate::Peri; use crate::interrupt::typelevel::Interrupt; use crate::interrupt::{self}; use crate::pac::radio::vals; pub use crate::pac::radio::vals::State as RadioState; use crate::radio::Instance; -use crate::Peri; /// Default (IEEE compliant) Start of Frame Delimiter pub const DEFAULT_SFD: u8 = 0xA7; @@ -52,6 +52,7 @@ impl<'d> Radio<'d> { // Disable and enable to reset peripheral r.power().write(|w| w.set_power(false)); r.power().write(|w| w.set_power(true)); + errata::post_power(); // Enable 802.15.4 mode r.mode().write(|w| w.set_mode(vals::Mode::IEEE802154_250KBIT)); @@ -541,3 +542,19 @@ fn dma_start_fence() { fn dma_end_fence() { compiler_fence(Ordering::Acquire); } + +mod errata { + pub fn post_power() { + // Workaround for anomaly 158 + #[cfg(feature = "_nrf5340-net")] + for i in 0..32 { + let info = crate::pac::FICR.trimcnf(i); + let addr = info.addr().read(); + if addr & 0xFFFF_F000 == crate::pac::RADIO.as_ptr() as u32 { + unsafe { + (addr as *mut u32).write_volatile(info.data().read()); + } + } + } + } +} diff --git a/embassy-nrf/src/rramc.rs b/embassy-nrf/src/rramc.rs index 7cb5660cb..521ac4ee7 100644 --- a/embassy-nrf/src/rramc.rs +++ b/embassy-nrf/src/rramc.rs @@ -7,7 +7,7 @@ use embedded_storage::nor_flash::{ }; use crate::peripherals::RRAMC; -use crate::{pac, Peri}; +use crate::{Peri, pac}; // // Export Nvmc alias and page size for downstream compatibility diff --git a/embassy-nrf/src/saadc.rs b/embassy-nrf/src/saadc.rs index fd48faabb..a199c1c4d 100644 --- a/embassy-nrf/src/saadc.rs +++ b/embassy-nrf/src/saadc.rs @@ -4,11 +4,11 @@ use core::future::poll_fn; use core::marker::PhantomData; -use core::sync::atomic::{compiler_fence, Ordering}; +use core::sync::atomic::{Ordering, compiler_fence}; use core::task::Poll; use embassy_hal_internal::drop::OnDrop; -use embassy_hal_internal::{impl_peripheral, Peri}; +use embassy_hal_internal::{Peri, impl_peripheral}; use embassy_sync::waitqueue::AtomicWaker; pub(crate) use vals::Psel as InputChannel; diff --git a/embassy-nrf/src/spim.rs b/embassy-nrf/src/spim.rs index c410e49fd..ce994dbc9 100644 --- a/embassy-nrf/src/spim.rs +++ b/embassy-nrf/src/spim.rs @@ -6,17 +6,17 @@ use core::future::poll_fn; use core::marker::PhantomData; #[cfg(feature = "_nrf52832_anomaly_109")] use core::sync::atomic::AtomicU8; -use core::sync::atomic::{compiler_fence, Ordering}; +use core::sync::atomic::{Ordering, compiler_fence}; use core::task::Poll; use embassy_embedded_hal::SetConfig; use embassy_hal_internal::{Peri, PeripheralType}; use embassy_sync::waitqueue::AtomicWaker; -pub use embedded_hal_02::spi::{Mode, Phase, Polarity, MODE_0, MODE_1, MODE_2, MODE_3}; +pub use embedded_hal_02::spi::{MODE_0, MODE_1, MODE_2, MODE_3, Mode, Phase, Polarity}; pub use pac::spim::vals::{Frequency, Order as BitOrder}; use crate::chip::{EASY_DMA_SIZE, FORCE_COPY_BUFFER_SIZE}; -use crate::gpio::{self, convert_drive, AnyPin, OutputDrive, Pin as GpioPin, PselBits, SealedPin as _}; +use crate::gpio::{self, AnyPin, OutputDrive, Pin as GpioPin, PselBits, SealedPin as _, convert_drive}; use crate::interrupt::typelevel::Interrupt; use crate::pac::gpio::vals as gpiovals; use crate::pac::spim::vals; diff --git a/embassy-nrf/src/spis.rs b/embassy-nrf/src/spis.rs index 713163a55..885821146 100644 --- a/embassy-nrf/src/spis.rs +++ b/embassy-nrf/src/spis.rs @@ -3,17 +3,17 @@ #![macro_use] use core::future::poll_fn; use core::marker::PhantomData; -use core::sync::atomic::{compiler_fence, Ordering}; +use core::sync::atomic::{Ordering, compiler_fence}; use core::task::Poll; use embassy_embedded_hal::SetConfig; use embassy_hal_internal::{Peri, PeripheralType}; use embassy_sync::waitqueue::AtomicWaker; -pub use embedded_hal_02::spi::{Mode, Phase, Polarity, MODE_0, MODE_1, MODE_2, MODE_3}; +pub use embedded_hal_02::spi::{MODE_0, MODE_1, MODE_2, MODE_3, Mode, Phase, Polarity}; pub use pac::spis::vals::Order as BitOrder; use crate::chip::{EASY_DMA_SIZE, FORCE_COPY_BUFFER_SIZE}; -use crate::gpio::{self, convert_drive, AnyPin, OutputDrive, Pin as GpioPin, SealedPin as _}; +use crate::gpio::{self, AnyPin, OutputDrive, Pin as GpioPin, SealedPin as _, convert_drive}; use crate::interrupt::typelevel::Interrupt; use crate::pac::gpio::vals as gpiovals; use crate::pac::spis::vals; diff --git a/embassy-nrf/src/temp.rs b/embassy-nrf/src/temp.rs index 44be0f6d1..a20e300b7 100644 --- a/embassy-nrf/src/temp.rs +++ b/embassy-nrf/src/temp.rs @@ -9,7 +9,7 @@ use fixed::types::I30F2; use crate::interrupt::InterruptExt; use crate::peripherals::TEMP; -use crate::{interrupt, pac, Peri}; +use crate::{Peri, interrupt, pac}; /// Interrupt handler. pub struct InterruptHandler { diff --git a/embassy-nrf/src/time_driver.rs b/embassy-nrf/src/time_driver.rs index 03f4c2e2b..b723e2334 100644 --- a/embassy-nrf/src/time_driver.rs +++ b/embassy-nrf/src/time_driver.rs @@ -1,9 +1,9 @@ use core::cell::{Cell, RefCell}; -use core::sync::atomic::{compiler_fence, AtomicU32, Ordering}; +use core::sync::atomic::{AtomicU32, Ordering, compiler_fence}; use critical_section::CriticalSection; -use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex; use embassy_sync::blocking_mutex::CriticalSectionMutex as Mutex; +use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex; use embassy_time_driver::Driver; use embassy_time_queue_utils::Queue; diff --git a/embassy-nrf/src/twim.rs b/embassy-nrf/src/twim.rs index 943ea9d31..93255c832 100644 --- a/embassy-nrf/src/twim.rs +++ b/embassy-nrf/src/twim.rs @@ -4,8 +4,8 @@ use core::future::poll_fn; use core::marker::PhantomData; -use core::sync::atomic::compiler_fence; use core::sync::atomic::Ordering::SeqCst; +use core::sync::atomic::compiler_fence; use core::task::Poll; use embassy_embedded_hal::SetConfig; diff --git a/embassy-nrf/src/twis.rs b/embassy-nrf/src/twis.rs index dd4978b3e..2bc0a5c13 100644 --- a/embassy-nrf/src/twis.rs +++ b/embassy-nrf/src/twis.rs @@ -2,10 +2,10 @@ #![macro_use] -use core::future::{poll_fn, Future}; +use core::future::{Future, poll_fn}; use core::marker::PhantomData; -use core::sync::atomic::compiler_fence; use core::sync::atomic::Ordering::SeqCst; +use core::sync::atomic::compiler_fence; use core::task::Poll; use embassy_hal_internal::{Peri, PeripheralType}; diff --git a/embassy-nrf/src/uarte.rs b/embassy-nrf/src/uarte.rs index 66fb3b3f2..1ee452173 100644 --- a/embassy-nrf/src/uarte.rs +++ b/embassy-nrf/src/uarte.rs @@ -15,7 +15,7 @@ use core::future::poll_fn; use core::marker::PhantomData; -use core::sync::atomic::{compiler_fence, AtomicU8, Ordering}; +use core::sync::atomic::{AtomicU8, Ordering, compiler_fence}; use core::task::Poll; use embassy_hal_internal::drop::OnDrop; @@ -25,7 +25,7 @@ use embassy_sync::waitqueue::AtomicWaker; pub use pac::uarte::vals::{Baudrate, ConfigParity as Parity}; use crate::chip::{EASY_DMA_SIZE, FORCE_COPY_BUFFER_SIZE}; -use crate::gpio::{self, AnyPin, Pin as GpioPin, PselBits, SealedPin as _, DISCONNECTED}; +use crate::gpio::{self, AnyPin, DISCONNECTED, Pin as GpioPin, PselBits, SealedPin as _}; use crate::interrupt::typelevel::Interrupt; use crate::pac::gpio::vals as gpiovals; use crate::pac::uarte::vals; diff --git a/embassy-nrf/src/usb/mod.rs b/embassy-nrf/src/usb/mod.rs index 2a32fe922..07cf2578a 100644 --- a/embassy-nrf/src/usb/mod.rs +++ b/embassy-nrf/src/usb/mod.rs @@ -4,10 +4,10 @@ pub mod vbus_detect; -use core::future::{poll_fn, Future}; +use core::future::{Future, poll_fn}; use core::marker::PhantomData; use core::mem::MaybeUninit; -use core::sync::atomic::{compiler_fence, AtomicU32, Ordering}; +use core::sync::atomic::{AtomicU32, Ordering, compiler_fence}; use core::task::Poll; use cortex_m::peripheral::NVIC; @@ -330,11 +330,7 @@ impl<'d, V: VbusDetect> driver::Bus for Bus<'d, V> { let mut was_enabled = false; regs.epinen().modify(|w| { was_enabled = (w.0 & mask) != 0; - if enabled { - w.0 |= mask - } else { - w.0 &= !mask - } + if enabled { w.0 |= mask } else { w.0 &= !mask } }); let ready_mask = In::mask(i); diff --git a/embassy-nrf/src/usb/vbus_detect.rs b/embassy-nrf/src/usb/vbus_detect.rs index 33cf91ee2..f24a7bff5 100644 --- a/embassy-nrf/src/usb/vbus_detect.rs +++ b/embassy-nrf/src/usb/vbus_detect.rs @@ -1,6 +1,6 @@ //! Trait and implementations for performing VBUS detection. -use core::future::{poll_fn, Future}; +use core::future::{Future, poll_fn}; use core::sync::atomic::{AtomicBool, Ordering}; use core::task::Poll; diff --git a/embassy-nrf/src/util.rs b/embassy-nrf/src/util.rs index 78f71719f..87118d347 100644 --- a/embassy-nrf/src/util.rs +++ b/embassy-nrf/src/util.rs @@ -15,9 +15,5 @@ pub(crate) fn slice_in_ram(slice: *const [T]) -> bool { /// Return an error if slice is not in RAM. Skips check if slice is zero-length. pub(crate) fn slice_in_ram_or(slice: *const [T], err: E) -> Result<(), E> { - if slice_in_ram(slice) { - Ok(()) - } else { - Err(err) - } + if slice_in_ram(slice) { Ok(()) } else { Err(err) } } diff --git a/embassy-nrf/src/wdt.rs b/embassy-nrf/src/wdt.rs index dc99a16f5..6afd73431 100644 --- a/embassy-nrf/src/wdt.rs +++ b/embassy-nrf/src/wdt.rs @@ -11,7 +11,7 @@ use embassy_hal_internal::PeripheralType; use crate::pac::wdt::vals; pub use crate::pac::wdt::vals::{Halt as HaltConfig, Sleep as SleepConfig}; -use crate::{interrupt, pac, peripherals, Peri}; +use crate::{Peri, interrupt, pac, peripherals}; const MIN_TICKS: u32 = 15; diff --git a/embassy-nxp/CHANGELOG.md b/embassy-nxp/CHANGELOG.md index 0fb677cd8..ad8670854 100644 --- a/embassy-nxp/CHANGELOG.md +++ b/embassy-nxp/CHANGELOG.md @@ -7,6 +7,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## Unreleased - ReleaseDate +- LPC55: Move ALT definitions for USART to TX/RX pin impls. +- LPC55: Remove internal match_iocon macro - LPC55: DMA Controller and asynchronous version of USART - Moved NXP LPC55S69 from `lpc55-pac` to `nxp-pac` - First release with changelog. diff --git a/embassy-nxp/Cargo.toml b/embassy-nxp/Cargo.toml index f3c828313..33f0f2dff 100644 --- a/embassy-nxp/Cargo.toml +++ b/embassy-nxp/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "embassy-nxp" version = "0.1.0" -edition = "2021" +edition = "2024" license = "MIT OR Apache-2.0" publish = false diff --git a/embassy-nxp/src/dma/lpc55.rs b/embassy-nxp/src/dma/lpc55.rs index 578d1fd88..5bd763f03 100644 --- a/embassy-nxp/src/dma/lpc55.rs +++ b/embassy-nxp/src/dma/lpc55.rs @@ -1,16 +1,16 @@ use core::cell::RefCell; use core::future::Future; use core::pin::Pin; -use core::sync::atomic::{compiler_fence, Ordering}; +use core::sync::atomic::{Ordering, compiler_fence}; use core::task::{Context, Poll}; use critical_section::Mutex; use embassy_hal_internal::interrupt::InterruptExt; -use embassy_hal_internal::{impl_peripheral, PeripheralType}; +use embassy_hal_internal::{PeripheralType, impl_peripheral}; use embassy_sync::waitqueue::AtomicWaker; use crate::pac::{DMA0, SYSCON, *}; -use crate::{peripherals, Peri}; +use crate::{Peri, peripherals}; #[interrupt] fn DMA0() { diff --git a/embassy-nxp/src/gpio/lpc55.rs b/embassy-nxp/src/gpio/lpc55.rs index 36ea99d21..6039d8ca8 100644 --- a/embassy-nxp/src/gpio/lpc55.rs +++ b/embassy-nxp/src/gpio/lpc55.rs @@ -1,8 +1,9 @@ -use embassy_hal_internal::{impl_peripheral, PeripheralType}; +use embassy_hal_internal::{PeripheralType, impl_peripheral}; +use crate::pac::common::{RW, Reg}; use crate::pac::iocon::vals::{PioDigimode, PioMode}; -use crate::pac::{GPIO, IOCON, SYSCON}; -use crate::{peripherals, Peri}; +use crate::pac::{GPIO, IOCON, SYSCON, iocon}; +use crate::{Peri, peripherals}; pub(crate) fn init() { // Enable clocks for GPIO, PINT, and IOCON @@ -109,13 +110,7 @@ impl<'d> Input<'d> { /// Set the pull configuration for the pin. To disable the pull, use [Pull::None]. pub fn set_pull(&mut self, pull: Pull) { - match_iocon!(register, self.pin.pin_bank(), self.pin.pin_number(), { - register.modify(|w| match pull { - Pull::None => w.set_mode(PioMode::INACTIVE), - Pull::Up => w.set_mode(PioMode::PULL_UP), - Pull::Down => w.set_mode(PioMode::PULL_DOWN), - }); - }); + self.pin.set_pull(pull); } /// Get the current input level of the pin. @@ -193,11 +188,20 @@ impl<'d> Flex<'d> { 1 << self.pin.pin_number() } + /// Set the pull configuration for the pin. To disable the pull, use [Pull::None]. + pub fn set_pull(&mut self, pull: Pull) { + self.pin.pio().modify(|w| match pull { + Pull::None => w.set_mode(PioMode::INACTIVE), + Pull::Up => w.set_mode(PioMode::PULL_UP), + Pull::Down => w.set_mode(PioMode::PULL_DOWN), + }); + } + /// Set the pin to digital mode. This is required for using a pin as a GPIO pin. The default /// setting for pins is (usually) non-digital. fn set_as_digital(&mut self) { - match_iocon!(register, self.pin_bank(), self.pin_number(), { - register.modify(|w| w.set_digimode(PioDigimode::DIGITAL)); + self.pin.pio().modify(|w| { + w.set_digimode(PioDigimode::DIGITAL); }); } @@ -220,6 +224,14 @@ impl<'d> Flex<'d> { pub(crate) trait SealedPin: Sized { fn pin_bank(&self) -> Bank; fn pin_number(&self) -> u8; + + #[inline] + fn pio(&self) -> Reg { + match self.pin_bank() { + Bank::Bank0 => IOCON.pio0(self.pin_number() as usize), + Bank::Bank1 => IOCON.pio1(self.pin_number() as usize), + } + } } /// Interface for a Pin that can be configured by an [Input] or [Output] driver, or converted to an @@ -272,40 +284,6 @@ impl SealedPin for AnyPin { } } -/// Match the pin bank and number of a pin to the corresponding IOCON register. -/// -/// # Example -/// ``` -/// use embassy_nxp::gpio::Bank; -/// use embassy_nxp::pac_utils::{iocon_reg, match_iocon}; -/// -/// // Make pin PIO1_6 digital and set it to pull-down mode. -/// match_iocon!(register, Bank::Bank1, 6, { -/// register.modify(|w|{ -/// w.set_mode(PioMode::PULL_DOWN); -/// w.set_digimode(PioDigimode::DIGITAL); -/// -/// } -/// }); -/// ``` -macro_rules! match_iocon { - ($register:ident, $pin_bank:expr, $pin_number:expr, $action:expr) => { - match $pin_bank { - Bank::Bank0 => { - let $register = IOCON.pio0($pin_number as usize); - $action; - } - - Bank::Bank1 => { - let $register = IOCON.pio1($pin_number as usize); - $action; - } - } - }; -} - -pub(crate) use match_iocon; - macro_rules! impl_pin { ($name:ident, $bank:expr, $pin_num:expr) => { impl Pin for peripherals::$name {} diff --git a/embassy-nxp/src/gpio/rt1xxx.rs b/embassy-nxp/src/gpio/rt1xxx.rs index 1d60a0d51..c4dc110ff 100644 --- a/embassy-nxp/src/gpio/rt1xxx.rs +++ b/embassy-nxp/src/gpio/rt1xxx.rs @@ -5,13 +5,13 @@ use core::ops::Not; use core::pin::Pin as FuturePin; use core::task::{Context, Poll}; -use embassy_hal_internal::{impl_peripheral, Peri, PeripheralType}; +use embassy_hal_internal::{Peri, PeripheralType, impl_peripheral}; use embassy_sync::waitqueue::AtomicWaker; use nxp_pac::gpio::vals::Icr; use nxp_pac::iomuxc::vals::Pus; use crate::chip::{mux_address, pad_address}; -use crate::pac::common::{Reg, RW}; +use crate::pac::common::{RW, Reg}; use crate::pac::gpio::Gpio; #[cfg(feature = "rt")] use crate::pac::interrupt; diff --git a/embassy-nxp/src/lib.rs b/embassy-nxp/src/lib.rs index f0f0afb6c..9576f02b1 100644 --- a/embassy-nxp/src/lib.rs +++ b/embassy-nxp/src/lib.rs @@ -1,4 +1,5 @@ #![no_std] +#![allow(unsafe_op_in_unsafe_fn)] // This mod MUST go first, so that the others see its macros. pub(crate) mod fmt; @@ -29,7 +30,7 @@ pub use chip::interrupt; pub use chip::pac; #[cfg(not(feature = "unstable-pac"))] pub(crate) use chip::pac; -pub use chip::{peripherals, Peripherals}; +pub use chip::{Peripherals, peripherals}; pub use embassy_hal_internal::{Peri, PeripheralType}; /// Macro to bind interrupts to handlers. @@ -67,7 +68,7 @@ macro_rules! bind_interrupts { $( #[allow(non_snake_case)] - #[no_mangle] + #[unsafe(no_mangle)] $(#[cfg($cond_irq)])? unsafe extern "C" fn $irq() { unsafe { diff --git a/embassy-nxp/src/pint.rs b/embassy-nxp/src/pint.rs index e594aaa6a..5b10b4540 100644 --- a/embassy-nxp/src/pint.rs +++ b/embassy-nxp/src/pint.rs @@ -8,9 +8,9 @@ use critical_section::Mutex; use embassy_hal_internal::interrupt::InterruptExt; use embassy_sync::waitqueue::AtomicWaker; -use crate::gpio::{self, AnyPin, Level, SealedPin}; -use crate::pac::{interrupt, INPUTMUX, PINT, SYSCON}; use crate::Peri; +use crate::gpio::{self, AnyPin, Level, SealedPin}; +use crate::pac::{INPUTMUX, PINT, SYSCON, interrupt}; struct PinInterrupt { assigned: bool, diff --git a/embassy-nxp/src/time_driver/rtc.rs b/embassy-nxp/src/time_driver/rtc.rs index fb6de6a5e..0883fa2e8 100644 --- a/embassy-nxp/src/time_driver/rtc.rs +++ b/embassy-nxp/src/time_driver/rtc.rs @@ -4,10 +4,10 @@ use core::task::Waker; use critical_section::CriticalSection; use embassy_hal_internal::interrupt::{InterruptExt, Priority}; use embassy_sync::blocking_mutex::CriticalSectionMutex as Mutex; -use embassy_time_driver::{time_driver_impl, Driver}; +use embassy_time_driver::{Driver, time_driver_impl}; use embassy_time_queue_utils::Queue; -use crate::pac::{interrupt, pmc, rtc, PMC, RTC, SYSCON}; +use crate::pac::{PMC, RTC, SYSCON, interrupt, pmc, rtc}; struct AlarmState { timestamp: Cell, diff --git a/embassy-nxp/src/usart/lpc55.rs b/embassy-nxp/src/usart/lpc55.rs index 9034ed429..d54927b25 100644 --- a/embassy-nxp/src/usart/lpc55.rs +++ b/embassy-nxp/src/usart/lpc55.rs @@ -4,16 +4,16 @@ use core::marker::PhantomData; use core::sync::atomic::{AtomicBool, Ordering}; use core::task::Poll; -use embassy_futures::select::{select, Either}; +use embassy_futures::select::{Either, select}; use embassy_hal_internal::interrupt::InterruptExt; use embassy_hal_internal::{Peri, PeripheralType}; use embassy_sync::waitqueue::AtomicWaker; use embedded_io::{self, ErrorKind}; use crate::dma::{AnyChannel, Channel}; -use crate::gpio::{match_iocon, AnyPin, Bank, SealedPin}; -use crate::interrupt::typelevel::{Binding, Interrupt as _}; +use crate::gpio::{AnyPin, SealedPin}; use crate::interrupt::Interrupt; +use crate::interrupt::typelevel::{Binding, Interrupt as _}; use crate::pac::flexcomm::Flexcomm as FlexcommReg; use crate::pac::iocon::vals::PioFunc; use crate::pac::usart::Usart as UsartReg; @@ -146,7 +146,8 @@ impl<'d, M: Mode> UsartTx<'d, M> { tx_dma: Peri<'d, impl Channel>, config: Config, ) -> Self { - Usart::::init::(Some(tx.into()), None, config); + let tx_func = tx.pin_func(); + Usart::::init::(Some((tx.into(), tx_func)), None, config); Self::new_inner(T::info(), Some(tx_dma.into())) } @@ -179,7 +180,8 @@ impl<'d, M: Mode> UsartTx<'d, M> { impl<'d> UsartTx<'d, Blocking> { pub fn new_blocking(_usart: Peri<'d, T>, tx: Peri<'d, impl TxPin>, config: Config) -> Self { - Usart::::init::(Some(tx.into()), None, config); + let tx_func = tx.pin_func(); + Usart::::init::(Some((tx.into(), tx_func)), None, config); Self::new_inner(T::info(), None) } } @@ -208,7 +210,8 @@ impl<'d, M: Mode> UsartRx<'d, M> { rx_dma: Peri<'d, impl Channel>, config: Config, ) -> Self { - Usart::::init::(None, Some(rx.into()), config); + let rx_func = rx.pin_func(); + Usart::::init::(None, Some((rx.into(), rx_func)), config); Self::new_inner(T::info(), T::dma_state(), has_irq, Some(rx_dma.into())) } @@ -280,7 +283,8 @@ impl<'d, M: Mode> UsartRx<'d, M> { impl<'d> UsartRx<'d, Blocking> { pub fn new_blocking(_usart: Peri<'d, T>, rx: Peri<'d, impl RxPin>, config: Config) -> Self { - Usart::::init::(None, Some(rx.into()), config); + let rx_func = rx.pin_func(); + Usart::::init::(None, Some((rx.into(), rx_func)), config); Self::new_inner(T::info(), T::dma_state(), false, None) } } @@ -405,7 +409,10 @@ impl<'d> Usart<'d, Blocking> { rx: Peri<'d, impl RxPin>, config: Config, ) -> Self { - Self::new_inner(usart, tx.into(), rx.into(), false, None, None, config) + let tx_func = tx.pin_func(); + let rx_func = rx.pin_func(); + + Self::new_inner(usart, tx.into(), tx_func, rx.into(), rx_func, false, None, None, config) } } @@ -419,10 +426,15 @@ impl<'d> Usart<'d, Async> { rx_dma: Peri<'d, impl RxChannel>, config: Config, ) -> Self { + let tx_func = tx.pin_func(); + let rx_func = rx.pin_func(); + Self::new_inner( uart, tx.into(), + tx_func, rx.into(), + rx_func, true, Some(tx_dma.into()), Some(rx_dma.into()), @@ -435,20 +447,26 @@ impl<'d, M: Mode> Usart<'d, M> { fn new_inner( _usart: Peri<'d, T>, mut tx: Peri<'d, AnyPin>, + tx_func: PioFunc, mut rx: Peri<'d, AnyPin>, + rx_func: PioFunc, has_irq: bool, tx_dma: Option>, rx_dma: Option>, config: Config, ) -> Self { - Self::init::(Some(tx.reborrow()), Some(rx.reborrow()), config); + Self::init::(Some((tx.reborrow(), tx_func)), Some((rx.reborrow(), rx_func)), config); Self { tx: UsartTx::new_inner(T::info(), tx_dma), rx: UsartRx::new_inner(T::info(), T::dma_state(), has_irq, rx_dma), } } - fn init(tx: Option>, rx: Option>, config: Config) { + fn init( + tx: Option<(Peri<'_, AnyPin>, PioFunc)>, + rx: Option<(Peri<'_, AnyPin>, PioFunc)>, + config: Config, + ) { Self::configure_flexcomm(T::info().fc_reg, T::instance_number()); Self::configure_clock::(&config); Self::pin_config::(tx, rx); @@ -553,31 +571,27 @@ impl<'d, M: Mode> Usart<'d, M> { .modify(|w| w.set_brgval((brg_value - 1) as u16)); } - fn pin_config(tx: Option>, rx: Option>) { - if let Some(tx_pin) = tx { - match_iocon!(register, tx_pin.pin_bank(), tx_pin.pin_number(), { - register.modify(|w| { - w.set_func(T::tx_pin_func()); - w.set_mode(iocon::vals::PioMode::INACTIVE); - w.set_slew(iocon::vals::PioSlew::STANDARD); - w.set_invert(false); - w.set_digimode(iocon::vals::PioDigimode::DIGITAL); - w.set_od(iocon::vals::PioOd::NORMAL); - }); - }) + fn pin_config(tx: Option<(Peri<'_, AnyPin>, PioFunc)>, rx: Option<(Peri<'_, AnyPin>, PioFunc)>) { + if let Some((tx_pin, func)) = tx { + tx_pin.pio().modify(|w| { + w.set_func(func); + w.set_mode(iocon::vals::PioMode::INACTIVE); + w.set_slew(iocon::vals::PioSlew::STANDARD); + w.set_invert(false); + w.set_digimode(iocon::vals::PioDigimode::DIGITAL); + w.set_od(iocon::vals::PioOd::NORMAL); + }); } - if let Some(rx_pin) = rx { - match_iocon!(register, rx_pin.pin_bank(), rx_pin.pin_number(), { - register.modify(|w| { - w.set_func(T::rx_pin_func()); - w.set_mode(iocon::vals::PioMode::INACTIVE); - w.set_slew(iocon::vals::PioSlew::STANDARD); - w.set_invert(false); - w.set_digimode(iocon::vals::PioDigimode::DIGITAL); - w.set_od(iocon::vals::PioOd::NORMAL); - }); - }) + if let Some((rx_pin, func)) = rx { + rx_pin.pio().modify(|w| { + w.set_func(func); + w.set_mode(iocon::vals::PioMode::INACTIVE); + w.set_slew(iocon::vals::PioSlew::STANDARD); + w.set_invert(false); + w.set_digimode(iocon::vals::PioDigimode::DIGITAL); + w.set_od(iocon::vals::PioOd::NORMAL); + }); }; } @@ -814,8 +828,6 @@ trait SealedInstance { fn info() -> &'static Info; fn dma_state() -> &'static DmaState; fn instance_number() -> usize; - fn tx_pin_func() -> PioFunc; - fn rx_pin_func() -> PioFunc; } /// UART instance. @@ -826,7 +838,7 @@ pub trait Instance: SealedInstance + PeripheralType { } macro_rules! impl_instance { - ($inst:ident, $fc:ident, $tx_pin:ident, $rx_pin:ident, $fc_num:expr) => { + ($inst:ident, $fc:ident, $fc_num:expr) => { impl $crate::usart::inner::SealedInstance for $crate::peripherals::$inst { fn info() -> &'static Info { static INFO: Info = Info { @@ -848,14 +860,6 @@ macro_rules! impl_instance { fn instance_number() -> usize { $fc_num } - #[inline] - fn tx_pin_func() -> PioFunc { - PioFunc::$tx_pin - } - #[inline] - fn rx_pin_func() -> PioFunc { - PioFunc::$rx_pin - } } impl $crate::usart::Instance for $crate::peripherals::$inst { type Interrupt = crate::interrupt::typelevel::$fc; @@ -863,45 +867,72 @@ macro_rules! impl_instance { }; } -impl_instance!(USART0, FLEXCOMM0, ALT1, ALT1, 0); -impl_instance!(USART1, FLEXCOMM1, ALT2, ALT2, 1); -impl_instance!(USART2, FLEXCOMM2, ALT1, ALT1, 2); -impl_instance!(USART3, FLEXCOMM3, ALT1, ALT1, 3); -impl_instance!(USART4, FLEXCOMM4, ALT1, ALT2, 4); -impl_instance!(USART5, FLEXCOMM5, ALT3, ALT3, 5); -impl_instance!(USART6, FLEXCOMM6, ALT2, ALT2, 6); -impl_instance!(USART7, FLEXCOMM7, ALT7, ALT7, 7); +impl_instance!(USART0, FLEXCOMM0, 0); +impl_instance!(USART1, FLEXCOMM1, 1); +impl_instance!(USART2, FLEXCOMM2, 2); +impl_instance!(USART3, FLEXCOMM3, 3); +impl_instance!(USART4, FLEXCOMM4, 4); +impl_instance!(USART5, FLEXCOMM5, 5); +impl_instance!(USART6, FLEXCOMM6, 6); +impl_instance!(USART7, FLEXCOMM7, 7); + +pub(crate) trait SealedTxPin: crate::gpio::Pin { + fn pin_func(&self) -> PioFunc; +} + +pub(crate) trait SealedRxPin: crate::gpio::Pin { + fn pin_func(&self) -> PioFunc; +} /// Trait for TX pins. -pub trait TxPin: crate::gpio::Pin {} +#[allow(private_bounds)] +pub trait TxPin: SealedTxPin + crate::gpio::Pin {} + /// Trait for RX pins. -pub trait RxPin: crate::gpio::Pin {} +#[allow(private_bounds)] +pub trait RxPin: SealedRxPin + crate::gpio::Pin {} + +macro_rules! impl_tx_pin { + ($pin:ident, $instance:ident, $func: ident) => { + impl SealedTxPin for crate::peripherals::$pin { + fn pin_func(&self) -> PioFunc { + PioFunc::$func + } + } -macro_rules! impl_pin { - ($pin:ident, $instance:ident, Tx) => { impl TxPin for crate::peripherals::$pin {} }; - ($pin:ident, $instance:ident, Rx) => { +} + +macro_rules! impl_rx_pin { + ($pin:ident, $instance:ident, $func: ident) => { + impl SealedRxPin for crate::peripherals::$pin { + fn pin_func(&self) -> PioFunc { + PioFunc::$func + } + } + impl RxPin for crate::peripherals::$pin {} }; } -impl_pin!(PIO1_6, USART0, Tx); -impl_pin!(PIO1_5, USART0, Rx); -impl_pin!(PIO1_11, USART1, Tx); -impl_pin!(PIO1_10, USART1, Rx); -impl_pin!(PIO0_27, USART2, Tx); -impl_pin!(PIO1_24, USART2, Rx); -impl_pin!(PIO0_2, USART3, Tx); -impl_pin!(PIO0_3, USART3, Rx); -impl_pin!(PIO0_16, USART4, Tx); -impl_pin!(PIO0_5, USART4, Rx); -impl_pin!(PIO0_9, USART5, Tx); -impl_pin!(PIO0_8, USART5, Rx); -impl_pin!(PIO1_16, USART6, Tx); -impl_pin!(PIO1_13, USART6, Rx); -impl_pin!(PIO0_19, USART7, Tx); -impl_pin!(PIO0_20, USART7, Rx); +impl_tx_pin!(PIO1_6, USART0, ALT1); +impl_tx_pin!(PIO1_11, USART1, ALT2); +impl_tx_pin!(PIO0_27, USART2, ALT1); +impl_tx_pin!(PIO0_2, USART3, ALT1); +impl_tx_pin!(PIO0_16, USART4, ALT1); +impl_tx_pin!(PIO0_9, USART5, ALT3); +impl_tx_pin!(PIO1_16, USART6, ALT2); +impl_tx_pin!(PIO0_19, USART7, ALT7); + +impl_rx_pin!(PIO1_5, USART0, ALT1); +impl_rx_pin!(PIO1_10, USART1, ALT2); +impl_rx_pin!(PIO1_24, USART2, ALT1); +impl_rx_pin!(PIO0_3, USART3, ALT1); +impl_rx_pin!(PIO0_5, USART4, ALT2); +impl_rx_pin!(PIO0_8, USART5, ALT3); +impl_rx_pin!(PIO1_13, USART6, ALT2); +impl_rx_pin!(PIO0_20, USART7, ALT7); /// Trait for TX DMA channels. pub trait TxChannel: crate::dma::Channel {} diff --git a/embassy-rp/Cargo.toml b/embassy-rp/Cargo.toml index f6b0900f2..9ad4b47a3 100644 --- a/embassy-rp/Cargo.toml +++ b/embassy-rp/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "embassy-rp" version = "0.8.0" -edition = "2021" +edition = "2024" license = "MIT OR Apache-2.0" description = "Embassy Hardware Abstraction Layer (HAL) for the Raspberry Pi RP2040 or RP235x microcontroller" keywords = ["embedded", "async", "rp235x", "rp2040", "embedded-hal"] @@ -104,7 +104,7 @@ boot2-w25x10cl = [] ## Have embassy-rp not provide the boot2 so you can use your own. ## Place your own in the ".boot2" section like: ## ``` -## #[link_section = ".boot2"] +## #[unsafe(link_section = ".boot2")] ## #[used] ## static BOOT2: [u8; 256] = [0; 256]; // Provide your own with e.g. include_bytes! ## ``` @@ -127,7 +127,7 @@ imagedef-nonsecure-exe = [] ## ```ignore ## use embassy_rp::block::ImageDef; ## -## #[link_section = ".start_block"] +## #[unsafe(link_section = ".start_block")] ## #[used] ## static IMAGE_DEF: ImageDef = ImageDef::secure_exe(); // Update this with your own implementation. ## ``` diff --git a/embassy-rp/src/adc.rs b/embassy-rp/src/adc.rs index 2db8e63d7..d16779e01 100644 --- a/embassy-rp/src/adc.rs +++ b/embassy-rp/src/adc.rs @@ -1,18 +1,18 @@ //! ADC driver. -use core::future::{poll_fn, Future}; +use core::future::{Future, poll_fn}; use core::marker::PhantomData; use core::mem; -use core::sync::atomic::{compiler_fence, Ordering}; +use core::sync::atomic::{Ordering, compiler_fence}; use core::task::Poll; use embassy_sync::waitqueue::AtomicWaker; use crate::gpio::{self, AnyPin, Pull, SealedPin as GpioPin}; -use crate::interrupt::typelevel::Binding; use crate::interrupt::InterruptExt; +use crate::interrupt::typelevel::Binding; use crate::pac::dma::vals::TreqSel; use crate::peripherals::{ADC, ADC_TEMP_SENSOR}; -use crate::{dma, interrupt, pac, peripherals, Peri, RegExt}; +use crate::{Peri, RegExt, dma, interrupt, pac, peripherals}; static WAKER: AtomicWaker = AtomicWaker::new(); diff --git a/embassy-rp/src/bootsel.rs b/embassy-rp/src/bootsel.rs index 14f9e46aa..b24b98cd5 100644 --- a/embassy-rp/src/bootsel.rs +++ b/embassy-rp/src/bootsel.rs @@ -7,8 +7,8 @@ //! //! This module provides functionality to poll BOOTSEL from an embassy application. -use crate::flash::in_ram; use crate::Peri; +use crate::flash::in_ram; /// Reads the BOOTSEL button. Returns true if the button is pressed. /// @@ -36,7 +36,7 @@ mod ram_helpers { /// This function must live in ram. It uses inline asm to avoid any /// potential calls to ABI functions that might be in flash. #[inline(never)] - #[link_section = ".data.ram_func"] + #[unsafe(link_section = ".data.ram_func")] #[cfg(target_arch = "arm")] pub unsafe fn read_cs_status() -> GpioStatus { let result: u32; diff --git a/embassy-rp/src/clocks.rs b/embassy-rp/src/clocks.rs index 2eddc0bcc..56892d7a2 100644 --- a/embassy-rp/src/clocks.rs +++ b/embassy-rp/src/clocks.rs @@ -72,8 +72,8 @@ use core::sync::atomic::{AtomicU32, Ordering}; use pac::clocks::vals::*; use crate::gpio::{AnyPin, SealedPin}; -use crate::pac::common::{Reg, RW}; -use crate::{pac, reset, Peri}; +use crate::pac::common::{RW, Reg}; +use crate::{Peri, pac, reset}; // NOTE: all gpin handling is commented out for future reference. // gpin is not usually safe to use during the boot init() call, so it won't diff --git a/embassy-rp/src/dma.rs b/embassy-rp/src/dma.rs index d31d1e159..18aec60a5 100644 --- a/embassy-rp/src/dma.rs +++ b/embassy-rp/src/dma.rs @@ -1,10 +1,10 @@ //! Direct Memory Access (DMA) use core::future::Future; use core::pin::Pin; -use core::sync::atomic::{compiler_fence, Ordering}; +use core::sync::atomic::{Ordering, compiler_fence}; use core::task::{Context, Poll}; -use embassy_hal_internal::{impl_peripheral, Peri, PeripheralType}; +use embassy_hal_internal::{Peri, PeripheralType, impl_peripheral}; 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 8c809090e..7cc8f0c1d 100644 --- a/embassy-rp/src/flash.rs +++ b/embassy-rp/src/flash.rs @@ -6,8 +6,8 @@ use core::task::{Context, Poll}; use embassy_hal_internal::{Peri, PeripheralType}; use embedded_storage::nor_flash::{ - check_erase, check_read, check_write, ErrorType, MultiwriteNorFlash, NorFlash, NorFlashError, NorFlashErrorKind, - ReadNorFlash, + ErrorType, MultiwriteNorFlash, NorFlash, NorFlashError, NorFlashErrorKind, ReadNorFlash, check_erase, check_read, + check_write, }; use crate::dma::{AnyChannel, Channel, Transfer}; @@ -627,7 +627,7 @@ mod ram_helpers { /// Length of data must be a multiple of 4096 /// addr must be aligned to 4096 #[inline(never)] - #[link_section = ".data.ram_func"] + #[unsafe(link_section = ".data.ram_func")] #[cfg(feature = "rp2040")] unsafe fn write_flash_inner(addr: u32, len: u32, data: Option<&[u8]>, ptrs: *const FlashFunctionPointers) { #[cfg(target_arch = "arm")] @@ -692,7 +692,7 @@ mod ram_helpers { /// Length of data must be a multiple of 4096 /// addr must be aligned to 4096 #[inline(never)] - #[link_section = ".data.ram_func"] + #[unsafe(link_section = ".data.ram_func")] #[cfg(feature = "_rp235x")] unsafe fn write_flash_inner(addr: u32, len: u32, data: Option<&[u8]>, ptrs: *const FlashFunctionPointers) { let data = data.map(|d| d.as_ptr()).unwrap_or(core::ptr::null()); @@ -811,7 +811,7 @@ mod ram_helpers { /// /// Credit: taken from `rp2040-flash` (also licensed Apache+MIT) #[inline(never)] - #[link_section = ".data.ram_func"] + #[unsafe(link_section = ".data.ram_func")] #[cfg(feature = "rp2040")] unsafe fn read_flash_inner(cmd: FlashCommand, ptrs: *const FlashFunctionPointers) { #[cfg(target_arch = "arm")] diff --git a/embassy-rp/src/float/cmp.rs b/embassy-rp/src/float/cmp.rs index e540e3918..f917eb9b3 100644 --- a/embassy-rp/src/float/cmp.rs +++ b/embassy-rp/src/float/cmp.rs @@ -21,19 +21,11 @@ impl ROMCmp for f64 { } fn le_abi(a: F, b: F) -> i32 { - if a.is_nan() || b.is_nan() { - 1 - } else { - a.rom_cmp(b) - } + if a.is_nan() || b.is_nan() { 1 } else { a.rom_cmp(b) } } fn ge_abi(a: F, b: F) -> i32 { - if a.is_nan() || b.is_nan() { - -1 - } else { - a.rom_cmp(b) - } + if a.is_nan() || b.is_nan() { -1 } else { a.rom_cmp(b) } } intrinsics! { diff --git a/embassy-rp/src/float/functions.rs b/embassy-rp/src/float/functions.rs index de29ce336..170168237 100644 --- a/embassy-rp/src/float/functions.rs +++ b/embassy-rp/src/float/functions.rs @@ -114,19 +114,11 @@ fn sqrt(f: F) -> F { } fn ln(f: F) -> F { - if is_negative_nonzero_or_nan(f) { - F::NAN - } else { - f.ln() - } + if is_negative_nonzero_or_nan(f) { F::NAN } else { f.ln() } } fn exp(f: F) -> F { - if f.is_nan() { - F::NAN - } else { - f.exp() - } + if f.is_nan() { F::NAN } else { f.exp() } } fn sin(f: F) -> F { diff --git a/embassy-rp/src/gpio.rs b/embassy-rp/src/gpio.rs index f79bf8948..c15e0e41b 100644 --- a/embassy-rp/src/gpio.rs +++ b/embassy-rp/src/gpio.rs @@ -5,13 +5,13 @@ use core::future::Future; use core::pin::Pin as FuturePin; use core::task::{Context, Poll}; -use embassy_hal_internal::{impl_peripheral, Peri, PeripheralType}; +use embassy_hal_internal::{Peri, PeripheralType, impl_peripheral}; use embassy_sync::waitqueue::AtomicWaker; use crate::interrupt::InterruptExt; -use crate::pac::common::{Reg, RW}; use crate::pac::SIO; -use crate::{interrupt, pac, peripherals, RegExt}; +use crate::pac::common::{RW, Reg}; +use crate::{RegExt, interrupt, pac, peripherals}; #[cfg(any(feature = "rp2040", feature = "rp235xa"))] pub(crate) const BANK0_PIN_COUNT: usize = 30; diff --git a/embassy-rp/src/i2c_slave.rs b/embassy-rp/src/i2c_slave.rs index c263047ad..770087bc8 100644 --- a/embassy-rp/src/i2c_slave.rs +++ b/embassy-rp/src/i2c_slave.rs @@ -5,9 +5,9 @@ use core::task::Poll; use pac::i2c; -use crate::i2c::{set_up_i2c_pin, AbortReason, Instance, InterruptHandler, SclPin, SdaPin, FIFO_SIZE}; +use crate::i2c::{AbortReason, FIFO_SIZE, Instance, InterruptHandler, SclPin, SdaPin, set_up_i2c_pin}; use crate::interrupt::typelevel::{Binding, Interrupt}; -use crate::{pac, Peri}; +use crate::{Peri, pac}; /// I2C error #[derive(Debug, PartialEq, Eq, Clone, Copy)] diff --git a/embassy-rp/src/intrinsics.rs b/embassy-rp/src/intrinsics.rs index 69d5d92de..aed8a3227 100644 --- a/embassy-rp/src/intrinsics.rs +++ b/embassy-rp/src/intrinsics.rs @@ -223,7 +223,7 @@ macro_rules! intrinsics { #[cfg(all(target_arch = "arm", feature = "intrinsics"))] mod $name { - #[no_mangle] + #[unsafe(no_mangle)] $(#[$($attr)*])* pub extern $abi fn $name( $($argname: $ty),* ) -> $ret { super::$name($($argname),*) @@ -257,7 +257,7 @@ macro_rules! intrinsics { #[cfg(all(target_arch = "arm", feature = "intrinsics"))] mod $name { - #[no_mangle] + #[unsafe(no_mangle)] $(#[$($attr)*])* pub unsafe extern $abi fn $name( $($argname: $ty),* ) -> $ret { super::$name($($argname),*) @@ -392,7 +392,7 @@ macro_rules! division_function { ); #[cfg(target_arch = "arm")] - extern "aapcs" { + unsafe extern "aapcs" { // Connect a local name to global symbol above through FFI. #[link_name = concat!("_erphal_", stringify!($name)) ] fn $name(n: $argty, d: $argty) -> u64; diff --git a/embassy-rp/src/lib.rs b/embassy-rp/src/lib.rs index d03ba1fef..4cb1a0912 100644 --- a/embassy-rp/src/lib.rs +++ b/embassy-rp/src/lib.rs @@ -1,5 +1,7 @@ #![no_std] #![allow(async_fn_in_trait)] +#![allow(unsafe_op_in_unsafe_fn)] +#![allow(unused_unsafe)] #![doc = include_str!("../README.md")] #![warn(missing_docs)] @@ -190,7 +192,7 @@ macro_rules! bind_interrupts { $( #[allow(non_snake_case)] - #[no_mangle] + #[unsafe(no_mangle)] $(#[cfg($cond_irq)])? unsafe extern "C" fn $irq() { unsafe { @@ -446,13 +448,13 @@ macro_rules! select_bootloader { ( $( $feature:literal => $loader:ident, )+ default => $default:ident ) => { $( #[cfg(feature = $feature)] - #[link_section = ".boot2"] + #[unsafe(link_section = ".boot2")] #[used] static BOOT2: [u8; 256] = rp2040_boot2::$loader; )* #[cfg(not(any( $( feature = $feature),* )))] - #[link_section = ".boot2"] + #[unsafe(link_section = ".boot2")] #[used] static BOOT2: [u8; 256] = rp2040_boot2::$default; } @@ -475,13 +477,13 @@ macro_rules! select_imagedef { ( $( $feature:literal => $imagedef:ident, )+ default => $default:ident ) => { $( #[cfg(feature = $feature)] - #[link_section = ".start_block"] + #[unsafe(link_section = ".start_block")] #[used] static IMAGE_DEF: crate::block::ImageDef = crate::block::ImageDef::$imagedef(); )* #[cfg(not(any( $( feature = $feature),* )))] - #[link_section = ".start_block"] + #[unsafe(link_section = ".start_block")] #[used] static IMAGE_DEF: crate::block::ImageDef = crate::block::ImageDef::$default(); } @@ -528,7 +530,7 @@ select_imagedef! { /// } /// ``` pub fn install_core0_stack_guard() -> Result<(), ()> { - extern "C" { + unsafe extern "C" { static mut _stack_end: usize; } unsafe { install_stack_guard(core::ptr::addr_of_mut!(_stack_end)) } diff --git a/embassy-rp/src/multicore.rs b/embassy-rp/src/multicore.rs index adedc98ad..3b120e349 100644 --- a/embassy-rp/src/multicore.rs +++ b/embassy-rp/src/multicore.rs @@ -14,6 +14,7 @@ //! use embassy_rp::multicore::Stack; //! use static_cell::StaticCell; //! use embassy_executor::Executor; +//! use core::ptr::addr_of_mut; //! //! static mut CORE1_STACK: Stack<4096> = Stack::new(); //! static EXECUTOR0: StaticCell = StaticCell::new(); @@ -36,7 +37,7 @@ //! fn main() -> ! { //! let p = embassy_rp::init(Default::default()); //! -//! embassy_rp::multicore::spawn_core1(p.CORE1, unsafe { &mut CORE1_STACK }, move || { +//! embassy_rp::multicore::spawn_core1(p.CORE1, unsafe { &mut *addr_of_mut!(CORE1_STACK) }, move || { //! let executor1 = EXECUTOR1.init(Executor::new()); //! executor1.run(|spawner| spawner.spawn(core1_task().unwrap())); //! }); @@ -47,11 +48,11 @@ //! ``` use core::mem::ManuallyDrop; -use core::sync::atomic::{compiler_fence, AtomicBool, Ordering}; +use core::sync::atomic::{AtomicBool, Ordering, compiler_fence}; use crate::interrupt::InterruptExt; use crate::peripherals::CORE1; -use crate::{gpio, install_stack_guard, interrupt, pac, Peri}; +use crate::{Peri, gpio, install_stack_guard, interrupt, pac}; const PAUSE_TOKEN: u32 = 0xDEADBEEF; const RESUME_TOKEN: u32 = !0xDEADBEEF; @@ -110,7 +111,6 @@ impl Stack { #[cfg(all(feature = "rt", feature = "rp2040"))] #[interrupt] -#[link_section = ".data.ram_func"] unsafe fn SIO_IRQ_PROC1() { let sio = pac::SIO; // Clear IRQ @@ -135,7 +135,6 @@ unsafe fn SIO_IRQ_PROC1() { #[cfg(all(feature = "rt", feature = "_rp235x"))] #[interrupt] -#[link_section = ".data.ram_func"] unsafe fn SIO_IRQ_FIFO() { let sio = pac::SIO; // Clear IRQ diff --git a/embassy-rp/src/pio/mod.rs b/embassy-rp/src/pio/mod.rs index 5f554dfe3..38ee1f97c 100644 --- a/embassy-rp/src/pio/mod.rs +++ b/embassy-rp/src/pio/mod.rs @@ -2,21 +2,21 @@ use core::future::Future; use core::marker::PhantomData; use core::pin::Pin as FuturePin; -use core::sync::atomic::{compiler_fence, Ordering}; +use core::sync::atomic::{Ordering, compiler_fence}; use core::task::{Context, Poll}; -use atomic_polyfill::{AtomicU64, AtomicU8}; +use atomic_polyfill::{AtomicU8, AtomicU64}; use embassy_hal_internal::{Peri, PeripheralType}; use embassy_sync::waitqueue::AtomicWaker; -use fixed::types::extra::U8; use fixed::FixedU32; +use fixed::types::extra::U8; use pio::{Program, SideSet, Wrap}; use crate::dma::{self, Channel, Transfer, Word}; use crate::gpio::{self, AnyPin, Drive, Level, Pull, SealedPin, SlewRate}; use crate::interrupt::typelevel::{Binding, Handler, Interrupt}; use crate::relocate::RelocatedProgram; -use crate::{pac, peripherals, RegExt}; +use crate::{RegExt, pac, peripherals}; mod instr; @@ -984,11 +984,7 @@ impl<'d, PIO: Instance + 'd, const SM: usize> StateMachine<'d, PIO, SM> { #[cfg(feature = "_rp235x")] fn pin_base() -> u8 { - if PIO::PIO.gpiobase().read().gpiobase() { - 16 - } else { - 0 - } + if PIO::PIO.gpiobase().read().gpiobase() { 16 } else { 0 } } /// Sets pin directions. This pauses the current state machine to run `SET` commands diff --git a/embassy-rp/src/pio_programs/hd44780.rs b/embassy-rp/src/pio_programs/hd44780.rs index 546c85a89..78281ddd4 100644 --- a/embassy-rp/src/pio_programs/hd44780.rs +++ b/embassy-rp/src/pio_programs/hd44780.rs @@ -1,12 +1,12 @@ //! [HD44780 display driver](https://www.sparkfun.com/datasheets/LCD/HD44780.pdf) +use crate::Peri; use crate::dma::{AnyChannel, Channel}; use crate::pio::{ Common, Config, Direction, FifoJoin, Instance, Irq, LoadedProgram, PioPin, ShiftConfig, ShiftDirection, StateMachine, }; use crate::pio_programs::clock_divider::calculate_pio_clock_divider; -use crate::Peri; /// This struct represents a HD44780 program that takes command words ( <0:4>) pub struct PioHD44780CommandWordProgram<'a, PIO: Instance> { diff --git a/embassy-rp/src/pio_programs/i2s.rs b/embassy-rp/src/pio_programs/i2s.rs index 2382a3f9f..7e5f68ad6 100644 --- a/embassy-rp/src/pio_programs/i2s.rs +++ b/embassy-rp/src/pio_programs/i2s.rs @@ -2,12 +2,12 @@ use fixed::traits::ToFixed; +use crate::Peri; use crate::dma::{AnyChannel, Channel, Transfer}; use crate::gpio::Pull; use crate::pio::{ Common, Config, Direction, FifoJoin, Instance, LoadedProgram, PioPin, ShiftConfig, ShiftDirection, StateMachine, }; -use crate::Peri; /// This struct represents an i2s receiver & controller driver program pub struct PioI2sInProgram<'d, PIO: Instance> { diff --git a/embassy-rp/src/pio_programs/onewire.rs b/embassy-rp/src/pio_programs/onewire.rs index 980d0fe5f..09babc229 100644 --- a/embassy-rp/src/pio_programs/onewire.rs +++ b/embassy-rp/src/pio_programs/onewire.rs @@ -1,11 +1,11 @@ //! OneWire pio driver +use crate::Peri; use crate::clocks::clk_sys_freq; use crate::gpio::Level; use crate::pio::{ Common, Config, Direction, Instance, LoadedProgram, PioPin, ShiftConfig, ShiftDirection, StateMachine, }; -use crate::Peri; /// This struct represents a onewire driver program pub struct PioOneWireProgram<'a, PIO: Instance> { @@ -321,11 +321,7 @@ impl PioOneWireSearch { /// Search for the next address on the bus pub async fn next(&mut self, pio: &mut PioOneWire<'_, PIO, SM>) -> Option { - if self.finished { - None - } else { - pio.search(self).await - } + if self.finished { None } else { pio.search(self).await } } /// Is finished when all devices have been found diff --git a/embassy-rp/src/pio_programs/pwm.rs b/embassy-rp/src/pio_programs/pwm.rs index f0f837bc5..ba06bb3c1 100644 --- a/embassy-rp/src/pio_programs/pwm.rs +++ b/embassy-rp/src/pio_programs/pwm.rs @@ -6,7 +6,7 @@ use pio::InstructionOperands; use crate::gpio::Level; use crate::pio::{Common, Config, Direction, Instance, LoadedProgram, Pin, PioPin, StateMachine}; -use crate::{clocks, Peri}; +use crate::{Peri, clocks}; /// This converts the duration provided into the number of cycles the PIO needs to run to make it take the same time fn to_pio_cycles(duration: Duration) -> u32 { diff --git a/embassy-rp/src/pio_programs/rotary_encoder.rs b/embassy-rp/src/pio_programs/rotary_encoder.rs index 70b3795e9..6347527e6 100644 --- a/embassy-rp/src/pio_programs/rotary_encoder.rs +++ b/embassy-rp/src/pio_programs/rotary_encoder.rs @@ -1,11 +1,11 @@ //! PIO backed quadrature encoder +use crate::Peri; use crate::gpio::Pull; use crate::pio::{ Common, Config, Direction as PioDirection, FifoJoin, Instance, LoadedProgram, PioPin, ShiftDirection, StateMachine, }; use crate::pio_programs::clock_divider::calculate_pio_clock_divider; -use crate::Peri; /// This struct represents an Encoder program loaded into pio instruction memory. pub struct PioEncoderProgram<'a, PIO: Instance> { diff --git a/embassy-rp/src/pio_programs/stepper.rs b/embassy-rp/src/pio_programs/stepper.rs index 0e9a8daf9..5762ee189 100644 --- a/embassy-rp/src/pio_programs/stepper.rs +++ b/embassy-rp/src/pio_programs/stepper.rs @@ -2,9 +2,9 @@ use core::mem::{self, MaybeUninit}; +use crate::Peri; use crate::pio::{Common, Config, Direction, Instance, Irq, LoadedProgram, PioPin, StateMachine}; use crate::pio_programs::clock_divider::calculate_pio_clock_divider; -use crate::Peri; /// This struct represents a Stepper driver program loaded into pio instruction memory. pub struct PioStepperProgram<'a, PIO: Instance> { diff --git a/embassy-rp/src/pio_programs/uart.rs b/embassy-rp/src/pio_programs/uart.rs index 04e39a571..444efb5db 100644 --- a/embassy-rp/src/pio_programs/uart.rs +++ b/embassy-rp/src/pio_programs/uart.rs @@ -5,12 +5,12 @@ use core::convert::Infallible; use embedded_io_async::{ErrorType, Read, Write}; use fixed::traits::ToFixed; +use crate::Peri; use crate::clocks::clk_sys_freq; use crate::gpio::Level; use crate::pio::{ Common, Config, Direction as PioDirection, FifoJoin, Instance, LoadedProgram, PioPin, ShiftDirection, StateMachine, }; -use crate::Peri; /// This struct represents a uart tx program loaded into pio instruction memory. pub struct PioUartTxProgram<'d, PIO: Instance> { diff --git a/embassy-rp/src/pio_programs/ws2812.rs b/embassy-rp/src/pio_programs/ws2812.rs index 37dd1c4e0..e6851b1a6 100644 --- a/embassy-rp/src/pio_programs/ws2812.rs +++ b/embassy-rp/src/pio_programs/ws2812.rs @@ -4,12 +4,12 @@ use embassy_time::Timer; use fixed::types::U24F8; use smart_leds::{RGB8, RGBW}; +use crate::Peri; use crate::clocks::clk_sys_freq; use crate::dma::{AnyChannel, Channel}; use crate::pio::{ Common, Config, FifoJoin, Instance, LoadedProgram, PioPin, ShiftConfig, ShiftDirection, StateMachine, }; -use crate::Peri; const T1: u8 = 2; // start bit const T2: u8 = 5; // data bit diff --git a/embassy-rp/src/psram.rs b/embassy-rp/src/psram.rs index ae43dd5aa..25185ead2 100644 --- a/embassy-rp/src/psram.rs +++ b/embassy-rp/src/psram.rs @@ -10,7 +10,7 @@ #![cfg(feature = "_rp235x")] -use critical_section::{acquire, release, CriticalSection, RestoreState}; +use critical_section::{CriticalSection, RestoreState, acquire, release}; use crate::pac; use crate::qmi_cs1::QmiCs1; @@ -251,7 +251,7 @@ impl<'d> Psram<'d> { } /// Verify APS6404L PSRAM device matches expected configuration. - #[link_section = ".data.ram_func"] + #[unsafe(link_section = ".data.ram_func")] #[inline(never)] fn verify_aps6404l(qmi: &pac::qmi::Qmi, expected_size: usize) -> Result<(), Error> { // APS6404L-specific constants @@ -306,7 +306,7 @@ impl<'d> Psram<'d> { Ok(()) } - #[link_section = ".data.ram_func"] + #[unsafe(link_section = ".data.ram_func")] #[inline(never)] unsafe fn read_aps6404l_kgd_eid(qmi: &pac::qmi::Qmi) -> (u32, u32) { const RESET_ENABLE_CMD: u8 = 0xf5; @@ -435,7 +435,7 @@ impl<'d> Psram<'d> { } /// Initialize PSRAM with proper timing. - #[link_section = ".data.ram_func"] + #[unsafe(link_section = ".data.ram_func")] #[inline(never)] fn init_psram(qmi: &pac::qmi::Qmi, xip_ctrl: &pac::xip_ctrl::XipCtrl, config: &Config) -> Result<(), Error> { // Set PSRAM timing for APS6404 @@ -610,7 +610,7 @@ impl<'d> Psram<'d> { Ok(()) } - #[link_section = ".data.ram_func"] + #[unsafe(link_section = ".data.ram_func")] #[inline(never)] unsafe fn direct_csr_send_init_command(config: &Config, init_cmd: u8) { #[cfg(target_arch = "arm")] diff --git a/embassy-rp/src/pwm.rs b/embassy-rp/src/pwm.rs index 1e1ccc4c6..59a3fc9a2 100644 --- a/embassy-rp/src/pwm.rs +++ b/embassy-rp/src/pwm.rs @@ -3,13 +3,13 @@ use embassy_hal_internal::{Peri, PeripheralType}; pub use embedded_hal_1::pwm::SetDutyCycle; use embedded_hal_1::pwm::{Error, ErrorKind, ErrorType}; -use fixed::traits::ToFixed; use fixed::FixedU16; +use fixed::traits::ToFixed; use pac::pwm::regs::{ChDiv, Intr}; use pac::pwm::vals::Divmode; use crate::gpio::{AnyPin, Pin as GpioPin, Pull, SealedPin as _}; -use crate::{pac, peripherals, RegExt}; +use crate::{RegExt, pac, peripherals}; /// The configuration of a PWM slice. /// Note the period in clock cycles of a slice can be computed as: diff --git a/embassy-rp/src/rtc/mod.rs b/embassy-rp/src/rtc/mod.rs index 8b0deed21..68fb3b765 100644 --- a/embassy-rp/src/rtc/mod.rs +++ b/embassy-rp/src/rtc/mod.rs @@ -2,7 +2,7 @@ mod filter; use core::future::poll_fn; -use core::sync::atomic::{compiler_fence, AtomicBool, Ordering}; +use core::sync::atomic::{AtomicBool, Ordering, compiler_fence}; use core::task::Poll; use embassy_hal_internal::{Peri, PeripheralType}; diff --git a/embassy-rp/src/time_driver.rs b/embassy-rp/src/time_driver.rs index d598287a9..ec1c17ed5 100644 --- a/embassy-rp/src/time_driver.rs +++ b/embassy-rp/src/time_driver.rs @@ -2,8 +2,8 @@ use core::cell::{Cell, RefCell}; use critical_section::CriticalSection; -use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex; use embassy_sync::blocking_mutex::Mutex; +use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex; use embassy_time_driver::Driver; use embassy_time_queue_utils::Queue; #[cfg(feature = "rp2040")] diff --git a/embassy-rp/src/uart/mod.rs b/embassy-rp/src/uart/mod.rs index 6f4e2ee07..43187df2d 100644 --- a/embassy-rp/src/uart/mod.rs +++ b/embassy-rp/src/uart/mod.rs @@ -4,7 +4,7 @@ use core::marker::PhantomData; use core::task::Poll; use atomic_polyfill::{AtomicU16, Ordering}; -use embassy_futures::select::{select, Either}; +use embassy_futures::select::{Either, select}; use embassy_hal_internal::{Peri, PeripheralType}; use embassy_sync::waitqueue::AtomicWaker; use embassy_time::{Delay, Timer}; @@ -16,7 +16,7 @@ use crate::gpio::{AnyPin, SealedPin}; use crate::interrupt::typelevel::{Binding, Interrupt as _}; use crate::interrupt::{Interrupt, InterruptExt}; use crate::pac::io::vals::{Inover, Outover}; -use crate::{interrupt, pac, peripherals, RegExt}; +use crate::{RegExt, interrupt, pac, peripherals}; mod buffered; pub use buffered::{BufferedInterruptHandler, BufferedUart, BufferedUartRx, BufferedUartTx}; @@ -863,11 +863,7 @@ impl<'d, M: Mode> Uart<'d, M> { if let Some(pin) = &tx { let funcsel = { let pin_number = ((pin.gpio().as_ptr() as u32) & 0x1FF) / 8; - if (pin_number % 4) == 0 { - 2 - } else { - 11 - } + if (pin_number % 4) == 0 { 2 } else { 11 } }; pin.gpio().ctrl().write(|w| { w.set_funcsel(funcsel); @@ -886,11 +882,7 @@ impl<'d, M: Mode> Uart<'d, M> { if let Some(pin) = &rx { let funcsel = { let pin_number = ((pin.gpio().as_ptr() as u32) & 0x1FF) / 8; - if ((pin_number - 1) % 4) == 0 { - 2 - } else { - 11 - } + if ((pin_number - 1) % 4) == 0 { 2 } else { 11 } }; pin.gpio().ctrl().write(|w| { w.set_funcsel(funcsel); diff --git a/embassy-rp/src/usb.rs b/embassy-rp/src/usb.rs index 671ecbd32..e8273c3f2 100644 --- a/embassy-rp/src/usb.rs +++ b/embassy-rp/src/usb.rs @@ -2,7 +2,7 @@ use core::future::poll_fn; use core::marker::PhantomData; use core::slice; -use core::sync::atomic::{compiler_fence, Ordering}; +use core::sync::atomic::{Ordering, compiler_fence}; use core::task::Poll; use embassy_hal_internal::PeripheralType; @@ -13,7 +13,7 @@ use embassy_usb_driver::{ }; use crate::interrupt::typelevel::{Binding, Interrupt}; -use crate::{interrupt, pac, peripherals, Peri, RegExt}; +use crate::{Peri, RegExt, interrupt, pac, peripherals}; trait SealedInstance { fn regs() -> crate::pac::usb::Usb; @@ -545,11 +545,7 @@ impl<'d, T: Instance> driver::Endpoint for Endpoint<'d, T, In> { poll_fn(|cx| { EP_IN_WAKERS[index].register(cx.waker()); let val = T::dpram().ep_in_control(self.info.addr.index() - 1).read(); - if val.enable() { - Poll::Ready(()) - } else { - Poll::Pending - } + if val.enable() { Poll::Ready(()) } else { Poll::Pending } }) .await; trace!("wait_enabled IN OK"); @@ -567,11 +563,7 @@ impl<'d, T: Instance> driver::Endpoint for Endpoint<'d, T, Out> { poll_fn(|cx| { EP_OUT_WAKERS[index].register(cx.waker()); let val = T::dpram().ep_out_control(self.info.addr.index() - 1).read(); - if val.enable() { - Poll::Ready(()) - } else { - Poll::Pending - } + if val.enable() { Poll::Ready(()) } else { Poll::Pending } }) .await; trace!("wait_enabled OUT OK"); diff --git a/embassy-rp/src/watchdog.rs b/embassy-rp/src/watchdog.rs index 49cf03850..d42601745 100644 --- a/embassy-rp/src/watchdog.rs +++ b/embassy-rp/src/watchdog.rs @@ -11,7 +11,7 @@ use core::marker::PhantomData; use embassy_time::Duration; use crate::peripherals::WATCHDOG; -use crate::{pac, Peri}; +use crate::{Peri, pac}; /// The reason for a system reset from the watchdog. #[derive(Debug, Copy, Clone, PartialEq, Eq)] diff --git a/embassy-stm32-wpan/Cargo.toml b/embassy-stm32-wpan/Cargo.toml index d7c7a284c..0802b7328 100644 --- a/embassy-stm32-wpan/Cargo.toml +++ b/embassy-stm32-wpan/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "embassy-stm32-wpan" version = "0.1.0" -edition = "2021" +edition = "2024" license = "MIT OR Apache-2.0" description = "Async STM32 WPAN stack for embedded devices in Rust." keywords = ["embedded", "async", "stm32", "ble", "wpan"] diff --git a/embassy-stm32-wpan/src/cmd.rs b/embassy-stm32-wpan/src/cmd.rs index 928357384..5c81a4aa7 100644 --- a/embassy-stm32-wpan/src/cmd.rs +++ b/embassy-stm32-wpan/src/cmd.rs @@ -1,7 +1,7 @@ use core::ptr; -use crate::consts::TlPacketType; use crate::PacketHeader; +use crate::consts::TlPacketType; #[derive(Copy, Clone)] #[repr(C, packed)] diff --git a/embassy-stm32-wpan/src/consts.rs b/embassy-stm32-wpan/src/consts.rs index e2ae6ca86..7ecb22974 100644 --- a/embassy-stm32-wpan/src/consts.rs +++ b/embassy-stm32-wpan/src/consts.rs @@ -1,5 +1,5 @@ -use crate::evt::CsEvt; use crate::PacketHeader; +use crate::evt::CsEvt; #[derive(Debug)] #[repr(C)] diff --git a/embassy-stm32-wpan/src/lhci.rs b/embassy-stm32-wpan/src/lhci.rs index 89f204f99..59c8bfb5d 100644 --- a/embassy-stm32-wpan/src/lhci.rs +++ b/embassy-stm32-wpan/src/lhci.rs @@ -1,9 +1,9 @@ use core::ptr; use crate::cmd::CmdPacket; -use crate::consts::{TlPacketType, TL_EVT_HEADER_SIZE}; +use crate::consts::{TL_EVT_HEADER_SIZE, TlPacketType}; use crate::evt::{CcEvt, EvtPacket, EvtSerial}; -use crate::tables::{DeviceInfoTable, RssInfoTable, SafeBootInfoTable, WirelessFwInfoTable, TL_DEVICE_INFO_TABLE}; +use crate::tables::{DeviceInfoTable, RssInfoTable, SafeBootInfoTable, TL_DEVICE_INFO_TABLE, WirelessFwInfoTable}; const TL_BLEEVT_CC_OPCODE: u8 = 0x0e; const LHCI_OPCODE_C1_DEVICE_INF: u16 = 0xfd62; diff --git a/embassy-stm32-wpan/src/lib.rs b/embassy-stm32-wpan/src/lib.rs index 40ff14795..f6b1f6021 100644 --- a/embassy-stm32-wpan/src/lib.rs +++ b/embassy-stm32-wpan/src/lib.rs @@ -13,6 +13,7 @@ #![no_std] #![allow(async_fn_in_trait)] +#![allow(unsafe_op_in_unsafe_fn)] #![doc = include_str!("../README.md")] // #![warn(missing_docs)] #![allow(static_mut_refs)] // TODO: Fix @@ -21,7 +22,7 @@ mod fmt; use core::mem::MaybeUninit; -use core::sync::atomic::{compiler_fence, Ordering}; +use core::sync::atomic::{Ordering, compiler_fence}; use embassy_hal_internal::Peri; use embassy_stm32::interrupt; @@ -94,7 +95,7 @@ impl<'d> TlMbox<'d> { pub fn init( ipcc: Peri<'d, IPCC>, _irqs: impl interrupt::typelevel::Binding - + interrupt::typelevel::Binding, + + interrupt::typelevel::Binding, config: Config, ) -> Self { // this is an inlined version of TL_Init from the STM32WB firmware as requested by AN5289. diff --git a/embassy-stm32-wpan/src/mac/driver.rs b/embassy-stm32-wpan/src/mac/driver.rs index 41cca09e3..480ac3790 100644 --- a/embassy-stm32-wpan/src/mac/driver.rs +++ b/embassy-stm32-wpan/src/mac/driver.rs @@ -6,9 +6,9 @@ use embassy_net_driver::{Capabilities, HardwareAddress, LinkState}; use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex; use embassy_sync::channel::Channel; +use crate::mac::MTU; use crate::mac::event::MacEvent; use crate::mac::runner::Runner; -use crate::mac::MTU; pub struct Driver<'d> { runner: &'d Runner<'d>, diff --git a/embassy-stm32-wpan/src/mac/runner.rs b/embassy-stm32-wpan/src/mac/runner.rs index d3099b6b7..2409f994d 100644 --- a/embassy-stm32-wpan/src/mac/runner.rs +++ b/embassy-stm32-wpan/src/mac/runner.rs @@ -7,10 +7,10 @@ use embassy_sync::channel::Channel; use embassy_sync::mutex::Mutex; use embassy_sync::signal::Signal; +use crate::mac::MTU; use crate::mac::commands::DataRequest; use crate::mac::event::MacEvent; use crate::mac::typedefs::{AddressMode, MacAddress, PanId, SecurityLevel}; -use crate::mac::MTU; use crate::sub::mac::Mac; type ZeroCopyPubSub = blocking_mutex::Mutex>>>; diff --git a/embassy-stm32-wpan/src/sub/ble.rs b/embassy-stm32-wpan/src/sub/ble.rs index 0f770d92c..cd69a0479 100644 --- a/embassy-stm32-wpan/src/sub/ble.rs +++ b/embassy-stm32-wpan/src/sub/ble.rs @@ -4,10 +4,10 @@ use embassy_stm32::ipcc::Ipcc; use hci::Opcode; use crate::cmd::CmdPacket; -use crate::consts::{TlPacketType, TL_BLEEVT_CC_OPCODE, TL_BLEEVT_CS_OPCODE}; +use crate::consts::{TL_BLEEVT_CC_OPCODE, TL_BLEEVT_CS_OPCODE, TlPacketType}; use crate::evt::{EvtBox, EvtPacket, EvtStub}; use crate::sub::mm; -use crate::tables::{BleTable, BLE_CMD_BUFFER, CS_BUFFER, EVT_QUEUE, HCI_ACL_DATA_BUFFER, TL_BLE_TABLE}; +use crate::tables::{BLE_CMD_BUFFER, BleTable, CS_BUFFER, EVT_QUEUE, HCI_ACL_DATA_BUFFER, TL_BLE_TABLE}; use crate::unsafe_linked_list::LinkedListNode; use crate::{channels, evt}; diff --git a/embassy-stm32-wpan/src/sub/mm.rs b/embassy-stm32-wpan/src/sub/mm.rs index 4e4d2f854..62d0de8bd 100644 --- a/embassy-stm32-wpan/src/sub/mm.rs +++ b/embassy-stm32-wpan/src/sub/mm.rs @@ -3,7 +3,7 @@ use core::future::poll_fn; use core::mem::MaybeUninit; use core::task::Poll; -use aligned::{Aligned, A4}; +use aligned::{A4, Aligned}; use cortex_m::interrupt; use embassy_stm32::ipcc::Ipcc; use embassy_sync::waitqueue::AtomicWaker; @@ -12,7 +12,7 @@ use crate::consts::POOL_SIZE; use crate::evt::EvtPacket; #[cfg(feature = "ble")] use crate::tables::BLE_SPARE_EVT_BUF; -use crate::tables::{MemManagerTable, EVT_POOL, FREE_BUF_QUEUE, SYS_SPARE_EVT_BUF, TL_MEM_MANAGER_TABLE}; +use crate::tables::{EVT_POOL, FREE_BUF_QUEUE, MemManagerTable, SYS_SPARE_EVT_BUF, TL_MEM_MANAGER_TABLE}; use crate::unsafe_linked_list::LinkedListNode; use crate::{channels, evt}; diff --git a/embassy-stm32-wpan/src/sub/sys.rs b/embassy-stm32-wpan/src/sub/sys.rs index cf6df58bf..8a3382f86 100644 --- a/embassy-stm32-wpan/src/sub/sys.rs +++ b/embassy-stm32-wpan/src/sub/sys.rs @@ -8,7 +8,7 @@ use crate::shci::{SchiCommandStatus, ShciBleInitCmdParam, ShciOpcode}; use crate::sub::mm; use crate::tables::{SysTable, WirelessFwInfoTable}; use crate::unsafe_linked_list::LinkedListNode; -use crate::{channels, Ipcc, SYSTEM_EVT_QUEUE, SYS_CMD_BUF, TL_DEVICE_INFO_TABLE, TL_SYS_TABLE}; +use crate::{Ipcc, SYS_CMD_BUF, SYSTEM_EVT_QUEUE, TL_DEVICE_INFO_TABLE, TL_SYS_TABLE, channels}; /// A guard that, once constructed, allows for sys commands to be sent to CPU2. pub struct Sys { @@ -35,11 +35,7 @@ impl Sys { let info = unsafe { TL_DEVICE_INFO_TABLE.as_mut_ptr().read_volatile().wireless_fw_info_table }; // Zero version indicates that CPU2 wasn't active and didn't fill the information table - if info.version != 0 { - Some(info) - } else { - None - } + if info.version != 0 { Some(info) } else { None } } pub async fn write(&self, opcode: ShciOpcode, payload: &[u8]) { @@ -66,8 +62,8 @@ impl Sys { #[cfg(feature = "mac")] pub async fn shci_c2_mac_802_15_4_init(&self) -> Result { use crate::tables::{ - Mac802_15_4Table, TracesTable, MAC_802_15_4_CMD_BUFFER, MAC_802_15_4_NOTIF_RSP_EVT_BUFFER, - TL_MAC_802_15_4_TABLE, TL_TRACES_TABLE, TRACES_EVT_QUEUE, + MAC_802_15_4_CMD_BUFFER, MAC_802_15_4_NOTIF_RSP_EVT_BUFFER, Mac802_15_4Table, TL_MAC_802_15_4_TABLE, + TL_TRACES_TABLE, TRACES_EVT_QUEUE, TracesTable, }; unsafe { diff --git a/embassy-stm32-wpan/src/tables.rs b/embassy-stm32-wpan/src/tables.rs index fe6fc47a3..1dafed159 100644 --- a/embassy-stm32-wpan/src/tables.rs +++ b/embassy-stm32-wpan/src/tables.rs @@ -1,6 +1,6 @@ use core::mem::MaybeUninit; -use aligned::{Aligned, A4}; +use aligned::{A4, Aligned}; use bit_field::BitField; use crate::cmd::{AclDataPacket, CmdPacket}; @@ -190,94 +190,94 @@ pub struct RefTable { } // --------------------- ref table --------------------- -#[link_section = "TL_REF_TABLE"] +#[unsafe(link_section = "TL_REF_TABLE")] pub static mut TL_REF_TABLE: MaybeUninit = MaybeUninit::uninit(); -#[link_section = "MB_MEM1"] +#[unsafe(link_section = "MB_MEM1")] pub static mut TL_DEVICE_INFO_TABLE: Aligned> = Aligned(MaybeUninit::uninit()); -#[link_section = "MB_MEM1"] +#[unsafe(link_section = "MB_MEM1")] pub static mut TL_BLE_TABLE: Aligned> = Aligned(MaybeUninit::uninit()); -#[link_section = "MB_MEM1"] +#[unsafe(link_section = "MB_MEM1")] pub static mut TL_THREAD_TABLE: Aligned> = Aligned(MaybeUninit::uninit()); -#[link_section = "MB_MEM1"] +#[unsafe(link_section = "MB_MEM1")] pub static mut TL_LLD_TESTS_TABLE: Aligned> = Aligned(MaybeUninit::uninit()); -#[link_section = "MB_MEM1"] +#[unsafe(link_section = "MB_MEM1")] pub static mut TL_BLE_LLD_TABLE: Aligned> = Aligned(MaybeUninit::uninit()); -#[link_section = "MB_MEM1"] +#[unsafe(link_section = "MB_MEM1")] pub static mut TL_SYS_TABLE: Aligned> = Aligned(MaybeUninit::uninit()); -#[link_section = "MB_MEM1"] +#[unsafe(link_section = "MB_MEM1")] pub static mut TL_MEM_MANAGER_TABLE: Aligned> = Aligned(MaybeUninit::uninit()); -#[link_section = "MB_MEM1"] +#[unsafe(link_section = "MB_MEM1")] pub static mut TL_TRACES_TABLE: Aligned> = Aligned(MaybeUninit::uninit()); -#[link_section = "MB_MEM1"] +#[unsafe(link_section = "MB_MEM1")] pub static mut TL_MAC_802_15_4_TABLE: Aligned> = Aligned(MaybeUninit::uninit()); -#[link_section = "MB_MEM1"] +#[unsafe(link_section = "MB_MEM1")] pub static mut TL_ZIGBEE_TABLE: Aligned> = Aligned(MaybeUninit::uninit()); // --------------------- tables --------------------- -#[link_section = "MB_MEM1"] +#[unsafe(link_section = "MB_MEM1")] pub static mut FREE_BUF_QUEUE: Aligned> = Aligned(MaybeUninit::uninit()); #[allow(dead_code)] -#[link_section = "MB_MEM1"] +#[unsafe(link_section = "MB_MEM1")] pub static mut TRACES_EVT_QUEUE: Aligned> = Aligned(MaybeUninit::uninit()); -#[link_section = "MB_MEM2"] +#[unsafe(link_section = "MB_MEM2")] pub static mut CS_BUFFER: Aligned> = Aligned(MaybeUninit::uninit()); -#[link_section = "MB_MEM2"] +#[unsafe(link_section = "MB_MEM2")] pub static mut EVT_QUEUE: Aligned> = Aligned(MaybeUninit::uninit()); -#[link_section = "MB_MEM2"] +#[unsafe(link_section = "MB_MEM2")] pub static mut SYSTEM_EVT_QUEUE: Aligned> = Aligned(MaybeUninit::uninit()); // --------------------- app tables --------------------- #[cfg(feature = "mac")] -#[link_section = "MB_MEM2"] +#[unsafe(link_section = "MB_MEM2")] pub static mut MAC_802_15_4_CMD_BUFFER: Aligned> = Aligned(MaybeUninit::uninit()); #[cfg(feature = "mac")] -#[link_section = "MB_MEM2"] +#[unsafe(link_section = "MB_MEM2")] pub static mut MAC_802_15_4_NOTIF_RSP_EVT_BUFFER: MaybeUninit< Aligned, > = MaybeUninit::uninit(); -#[link_section = "MB_MEM2"] +#[unsafe(link_section = "MB_MEM2")] pub static mut EVT_POOL: Aligned> = Aligned(MaybeUninit::uninit()); -#[link_section = "MB_MEM2"] +#[unsafe(link_section = "MB_MEM2")] pub static mut SYS_CMD_BUF: Aligned> = Aligned(MaybeUninit::uninit()); -#[link_section = "MB_MEM2"] +#[unsafe(link_section = "MB_MEM2")] pub static mut SYS_SPARE_EVT_BUF: Aligned> = Aligned(MaybeUninit::uninit()); #[cfg(feature = "mac")] -#[link_section = "MB_MEM2"] +#[unsafe(link_section = "MB_MEM2")] pub static mut MAC_802_15_4_CNFINDNOT: Aligned> = Aligned(MaybeUninit::uninit()); #[cfg(feature = "ble")] -#[link_section = "MB_MEM1"] +#[unsafe(link_section = "MB_MEM1")] pub static mut BLE_CMD_BUFFER: Aligned> = Aligned(MaybeUninit::uninit()); #[cfg(feature = "ble")] -#[link_section = "MB_MEM2"] +#[unsafe(link_section = "MB_MEM2")] pub static mut BLE_SPARE_EVT_BUF: Aligned> = Aligned(MaybeUninit::uninit()); #[cfg(feature = "ble")] -#[link_section = "MB_MEM2"] +#[unsafe(link_section = "MB_MEM2")] // fuck these "magic" numbers from ST ---v---v pub static mut HCI_ACL_DATA_BUFFER: Aligned> = Aligned(MaybeUninit::uninit()); diff --git a/embassy-stm32/CHANGELOG.md b/embassy-stm32/CHANGELOG.md index a6ee5c4b8..9848daf49 100644 --- a/embassy-stm32/CHANGELOG.md +++ b/embassy-stm32/CHANGELOG.md @@ -6,8 +6,12 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +### [Unreleased] + +* **Fix(stm32h5):** Prevent a HardFault crash on STM32H5 devices by changing `uid()` to return `[u8; 12]` by value instead of a reference. (Fixes #2696) ## Unreleased - ReleaseDate +- fix flash erase on L4 & L5 - fix: Fixed STM32H5 builds requiring time feature - feat: Derive Clone, Copy for QSPI Config - fix: stm32/i2c in master mode (blocking): subsequent transmissions failed after a NACK was received diff --git a/embassy-stm32/Cargo.toml b/embassy-stm32/Cargo.toml index 82bc73708..7c243b350 100644 --- a/embassy-stm32/Cargo.toml +++ b/embassy-stm32/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "embassy-stm32" version = "0.4.0" -edition = "2021" +edition = "2024" license = "MIT OR Apache-2.0" description = "Embassy Hardware Abstraction Layer (HAL) for ST STM32 series microcontrollers" keywords = ["embedded", "async", "stm32", "hal", "embedded-hal"] diff --git a/embassy-stm32/build.rs b/embassy-stm32/build.rs index b5f1261fe..eea1acf68 100644 --- a/embassy-stm32/build.rs +++ b/embassy-stm32/build.rs @@ -9,8 +9,8 @@ use proc_macro2::{Ident, TokenStream}; use quote::{format_ident, quote}; use stm32_metapac::metadata::ir::BitOffset; use stm32_metapac::metadata::{ - MemoryRegion, MemoryRegionKind, PeripheralRccKernelClock, PeripheralRccRegister, PeripheralRegisters, StopMode, - ALL_CHIPS, ALL_PERIPHERAL_VERSIONS, METADATA, + ALL_CHIPS, ALL_PERIPHERAL_VERSIONS, METADATA, MemoryRegion, MemoryRegionKind, PeripheralRccKernelClock, + PeripheralRccRegister, PeripheralRegisters, StopMode, }; #[path = "./build_common.rs"] @@ -105,7 +105,9 @@ fn main() { } (false, false) => { if METADATA.memory.len() != 1 { - panic!("Chip supports single and dual bank configuration. No Cargo feature to select one is enabled. Use the 'single-bank' or 'dual-bank' feature to make your selection") + panic!( + "Chip supports single and dual bank configuration. No Cargo feature to select one is enabled. Use the 'single-bank' or 'dual-bank' feature to make your selection" + ) } METADATA.memory[0] } diff --git a/embassy-stm32/src/adc/adc4.rs b/embassy-stm32/src/adc/adc4.rs index 255dc7956..2608160a3 100644 --- a/embassy-stm32/src/adc/adc4.rs +++ b/embassy-stm32/src/adc/adc4.rs @@ -4,7 +4,7 @@ use pac::adc::vals::{Adc4Dmacfg as Dmacfg, Adc4Exten as Exten, Adc4OversamplingR #[cfg(stm32wba)] use pac::adc::vals::{Chselrmod, Cont, Dmacfg, Exten, OversamplingRatio, Ovss, Smpsel}; -use super::{blocking_delay_us, AdcChannel, AnyAdcChannel, RxDma4, SealedAdcChannel}; +use super::{AdcChannel, AnyAdcChannel, RxDma4, SealedAdcChannel, blocking_delay_us}; use crate::dma::Transfer; #[cfg(stm32u5)] pub use crate::pac::adc::regs::Adc4Chselrmod0 as Chselr; @@ -15,7 +15,7 @@ pub use crate::pac::adc::vals::{Adc4Presc as Presc, Adc4Res as Resolution, Adc4S #[cfg(stm32wba)] pub use crate::pac::adc::vals::{Presc, Res as Resolution, SampleTime}; use crate::time::Hertz; -use crate::{pac, rcc, Peri}; +use crate::{Peri, pac, rcc}; const MAX_ADC_CLK_FREQ: Hertz = Hertz::mhz(55); @@ -208,7 +208,10 @@ impl<'d, T: Instance> Adc4<'d, T> { info!("ADC4 frequency set to {}", frequency); if frequency > MAX_ADC_CLK_FREQ { - panic!("Maximal allowed frequency for ADC4 is {} MHz and it varies with different packages, refer to ST docs for more information.", MAX_ADC_CLK_FREQ.0 / 1_000_000 ); + panic!( + "Maximal allowed frequency for ADC4 is {} MHz and it varies with different packages, refer to ST docs for more information.", + MAX_ADC_CLK_FREQ.0 / 1_000_000 + ); } let mut s = Self { adc }; diff --git a/embassy-stm32/src/adc/c0.rs b/embassy-stm32/src/adc/c0.rs index f2837a8f1..fc28df346 100644 --- a/embassy-stm32/src/adc/c0.rs +++ b/embassy-stm32/src/adc/c0.rs @@ -4,11 +4,11 @@ use pac::adc::vals::{Adstp, Align, Ckmode, Dmacfg, Exten, Ovrmod, Ovsr}; use pac::adccommon::vals::Presc; use super::{ - blocking_delay_us, Adc, AdcChannel, AnyAdcChannel, Instance, Resolution, RxDma, SampleTime, SealedAdcChannel, + Adc, AdcChannel, AnyAdcChannel, Instance, Resolution, RxDma, SampleTime, SealedAdcChannel, blocking_delay_us, }; use crate::dma::Transfer; use crate::time::Hertz; -use crate::{pac, rcc, Peri}; +use crate::{Peri, pac, rcc}; /// Default VREF voltage used for sample conversion to millivolts. pub const VREF_DEFAULT_MV: u32 = 3300; @@ -168,7 +168,10 @@ impl<'d, T: Instance> Adc<'d, T> { debug!("ADC frequency set to {}", frequency); if frequency > MAX_ADC_CLK_FREQ { - panic!("Maximal allowed frequency for the ADC is {} MHz and it varies with different packages, refer to ST docs for more information.", MAX_ADC_CLK_FREQ.0 / 1_000_000 ); + panic!( + "Maximal allowed frequency for the ADC is {} MHz and it varies with different packages, refer to ST docs for more information.", + MAX_ADC_CLK_FREQ.0 / 1_000_000 + ); } let mut s = Self { diff --git a/embassy-stm32/src/adc/f1.rs b/embassy-stm32/src/adc/f1.rs index 3cdc9d8fb..f9c23d72b 100644 --- a/embassy-stm32/src/adc/f1.rs +++ b/embassy-stm32/src/adc/f1.rs @@ -7,7 +7,7 @@ use crate::adc::{Adc, AdcChannel, Instance, SampleTime}; use crate::interrupt::typelevel::Interrupt; use crate::interrupt::{self}; use crate::time::Hertz; -use crate::{rcc, Peri}; +use crate::{Peri, rcc}; pub const VDDA_CALIB_MV: u32 = 3300; pub const ADC_MAX: u32 = (1 << 12) - 1; diff --git a/embassy-stm32/src/adc/f3.rs b/embassy-stm32/src/adc/f3.rs index 3aeb6f2c7..73ceb087a 100644 --- a/embassy-stm32/src/adc/f3.rs +++ b/embassy-stm32/src/adc/f3.rs @@ -6,7 +6,7 @@ use super::blocking_delay_us; use crate::adc::{Adc, AdcChannel, Instance, SampleTime}; use crate::interrupt::typelevel::Interrupt; use crate::time::Hertz; -use crate::{interrupt, rcc, Peri}; +use crate::{Peri, interrupt, rcc}; pub const VDDA_CALIB_MV: u32 = 3300; pub const ADC_MAX: u32 = (1 << 12) - 1; diff --git a/embassy-stm32/src/adc/f3_v1_1.rs b/embassy-stm32/src/adc/f3_v1_1.rs index 84613078c..cd5de54f5 100644 --- a/embassy-stm32/src/adc/f3_v1_1.rs +++ b/embassy-stm32/src/adc/f3_v1_1.rs @@ -9,7 +9,7 @@ use super::Resolution; use crate::adc::{Adc, AdcChannel, Instance, SampleTime}; use crate::interrupt::typelevel::Interrupt; use crate::time::Hertz; -use crate::{interrupt, rcc, Peri}; +use crate::{Peri, interrupt, rcc}; const ADC_FREQ: Hertz = crate::rcc::HSI_FREQ; diff --git a/embassy-stm32/src/adc/g4.rs b/embassy-stm32/src/adc/g4.rs index 43498966f..5098aadd8 100644 --- a/embassy-stm32/src/adc/g4.rs +++ b/embassy-stm32/src/adc/g4.rs @@ -7,11 +7,11 @@ use pac::adc::vals::{Adcaldif, Difsel, Exten, Rovsm, Trovs}; use pac::adccommon::vals::Presc; use stm32_metapac::adc::vals::{Adstp, Dmacfg, Dmaen}; -use super::{blocking_delay_us, Adc, AdcChannel, AnyAdcChannel, Instance, Resolution, RxDma, SampleTime}; +use super::{Adc, AdcChannel, AnyAdcChannel, Instance, Resolution, RxDma, SampleTime, blocking_delay_us}; use crate::adc::SealedAdcChannel; use crate::dma::Transfer; use crate::time::Hertz; -use crate::{pac, rcc, Peri}; +use crate::{Peri, pac, rcc}; /// Default VREF voltage used for sample conversion to millivolts. pub const VREF_DEFAULT_MV: u32 = 3300; @@ -133,7 +133,10 @@ impl<'d, T: Instance> Adc<'d, T> { trace!("ADC frequency set to {}", frequency); if frequency > MAX_ADC_CLK_FREQ { - panic!("Maximal allowed frequency for the ADC is {} MHz and it varies with different packages, refer to ST docs for more information.", MAX_ADC_CLK_FREQ.0 / 1_000_000 ); + panic!( + "Maximal allowed frequency for the ADC is {} MHz and it varies with different packages, refer to ST docs for more information.", + MAX_ADC_CLK_FREQ.0 / 1_000_000 + ); } let mut s = Self { diff --git a/embassy-stm32/src/adc/mod.rs b/embassy-stm32/src/adc/mod.rs index ea986f4cf..22ed8295f 100644 --- a/embassy-stm32/src/adc/mod.rs +++ b/embassy-stm32/src/adc/mod.rs @@ -22,7 +22,7 @@ use core::marker::PhantomData; #[allow(unused)] #[cfg(not(any(adc_f3v3, adc_wba)))] pub use _version::*; -use embassy_hal_internal::{impl_peripheral, PeripheralType}; +use embassy_hal_internal::{PeripheralType, impl_peripheral}; #[cfg(any(adc_f1, adc_f3v1, adc_v1, adc_l0, adc_f3v2))] use embassy_sync::waitqueue::AtomicWaker; diff --git a/embassy-stm32/src/adc/ringbuffered_v2.rs b/embassy-stm32/src/adc/ringbuffered_v2.rs index 6f69e8486..9b2e5b8fe 100644 --- a/embassy-stm32/src/adc/ringbuffered_v2.rs +++ b/embassy-stm32/src/adc/ringbuffered_v2.rs @@ -1,13 +1,13 @@ use core::marker::PhantomData; use core::mem; -use core::sync::atomic::{compiler_fence, Ordering}; +use core::sync::atomic::{Ordering, compiler_fence}; use stm32_metapac::adc::vals::SampleTime; use crate::adc::{Adc, AdcChannel, Instance, RxDma}; use crate::dma::{Priority, ReadableRingBuffer, TransferOptions}; use crate::pac::adc::vals; -use crate::{rcc, Peri}; +use crate::{Peri, rcc}; #[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct OverrunError; diff --git a/embassy-stm32/src/adc/v1.rs b/embassy-stm32/src/adc/v1.rs index d09374876..a5869d110 100644 --- a/embassy-stm32/src/adc/v1.rs +++ b/embassy-stm32/src/adc/v1.rs @@ -9,7 +9,7 @@ use super::blocking_delay_us; use crate::adc::{Adc, AdcChannel, Instance, Resolution, SampleTime}; use crate::interrupt::typelevel::Interrupt; use crate::peripherals::ADC1; -use crate::{interrupt, rcc, Peri}; +use crate::{Peri, interrupt, rcc}; mod watchdog_v1; pub use watchdog_v1::WatchdogChannels; @@ -66,11 +66,7 @@ pub struct Temperature; impl AdcChannel for Temperature {} impl super::SealedAdcChannel for Temperature { fn channel(&self) -> u8 { - if cfg!(adc_l0) { - 18 - } else { - 16 - } + if cfg!(adc_l0) { 18 } else { 16 } } } diff --git a/embassy-stm32/src/adc/v2.rs b/embassy-stm32/src/adc/v2.rs index e94a25b24..93ec78548 100644 --- a/embassy-stm32/src/adc/v2.rs +++ b/embassy-stm32/src/adc/v2.rs @@ -2,7 +2,7 @@ use super::blocking_delay_us; use crate::adc::{Adc, AdcChannel, Instance, Resolution, SampleTime}; use crate::peripherals::ADC1; use crate::time::Hertz; -use crate::{rcc, Peri}; +use crate::{Peri, rcc}; mod ringbuffered_v2; pub use ringbuffered_v2::{RingBufferedAdc, Sequence}; diff --git a/embassy-stm32/src/adc/v3.rs b/embassy-stm32/src/adc/v3.rs index 16063ce4d..47632263b 100644 --- a/embassy-stm32/src/adc/v3.rs +++ b/embassy-stm32/src/adc/v3.rs @@ -10,10 +10,10 @@ use pac::adc::vals::{OversamplingRatio, OversamplingShift, Rovsm, Trovs}; pub use pac::adc::vals::{Ovsr, Ovss, Presc}; use super::{ - blocking_delay_us, Adc, AdcChannel, AnyAdcChannel, Instance, Resolution, RxDma, SampleTime, SealedAdcChannel, + Adc, AdcChannel, AnyAdcChannel, Instance, Resolution, RxDma, SampleTime, SealedAdcChannel, blocking_delay_us, }; use crate::dma::Transfer; -use crate::{pac, rcc, Peri}; +use crate::{Peri, pac, rcc}; /// Default VREF voltage used for sample conversion to millivolts. pub const VREF_DEFAULT_MV: u32 = 3300; diff --git a/embassy-stm32/src/adc/v4.rs b/embassy-stm32/src/adc/v4.rs index b66437e6e..c7d0103a6 100644 --- a/embassy-stm32/src/adc/v4.rs +++ b/embassy-stm32/src/adc/v4.rs @@ -5,11 +5,11 @@ use pac::adc::vals::{Adstp, Difsel, Dmngt, Exten, Pcsel}; use pac::adccommon::vals::Presc; use super::{ - blocking_delay_us, Adc, AdcChannel, AnyAdcChannel, Instance, Resolution, RxDma, SampleTime, SealedAdcChannel, + Adc, AdcChannel, AnyAdcChannel, Instance, Resolution, RxDma, SampleTime, SealedAdcChannel, blocking_delay_us, }; use crate::dma::Transfer; use crate::time::Hertz; -use crate::{pac, rcc, Peri}; +use crate::{Peri, pac, rcc}; /// Default VREF voltage used for sample conversion to millivolts. pub const VREF_DEFAULT_MV: u32 = 3300; @@ -171,7 +171,10 @@ impl<'d, T: Instance> Adc<'d, T> { info!("ADC frequency set to {}", frequency); if frequency > MAX_ADC_CLK_FREQ { - panic!("Maximal allowed frequency for the ADC is {} MHz and it varies with different packages, refer to ST docs for more information.", MAX_ADC_CLK_FREQ.0 / 1_000_000 ); + panic!( + "Maximal allowed frequency for the ADC is {} MHz and it varies with different packages, refer to ST docs for more information.", + MAX_ADC_CLK_FREQ.0 / 1_000_000 + ); } #[cfg(stm32h7)] diff --git a/embassy-stm32/src/can/bxcan/mod.rs b/embassy-stm32/src/can/bxcan/mod.rs index 8eb188560..507350c42 100644 --- a/embassy-stm32/src/can/bxcan/mod.rs +++ b/embassy-stm32/src/can/bxcan/mod.rs @@ -5,8 +5,8 @@ use core::future::poll_fn; use core::marker::PhantomData; use core::task::Poll; -use embassy_hal_internal::interrupt::InterruptExt; use embassy_hal_internal::PeripheralType; +use embassy_hal_internal::interrupt::InterruptExt; use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex; use embassy_sync::channel::Channel; use embassy_sync::waitqueue::AtomicWaker; @@ -22,7 +22,7 @@ use crate::can::enums::{BusError, RefCountOp, TryReadError}; use crate::gpio::{AfType, OutputType, Pull, Speed}; use crate::interrupt::typelevel::Interrupt; use crate::rcc::{self, RccPeripheral}; -use crate::{interrupt, peripherals, Peri}; +use crate::{Peri, interrupt, peripherals}; /// Interrupt handler. pub struct TxInterruptHandler { @@ -186,10 +186,10 @@ impl<'d> Can<'d> { rx: Peri<'d, if_afio!(impl RxPin)>, tx: Peri<'d, if_afio!(impl TxPin)>, _irqs: impl interrupt::typelevel::Binding> - + interrupt::typelevel::Binding> - + interrupt::typelevel::Binding> - + interrupt::typelevel::Binding> - + 'd, + + interrupt::typelevel::Binding> + + interrupt::typelevel::Binding> + + interrupt::typelevel::Binding> + + 'd, ) -> Self { let info = T::info(); let regs = &T::info().regs; diff --git a/embassy-stm32/src/can/fd/config.rs b/embassy-stm32/src/can/fd/config.rs index c6a66b469..e08349f02 100644 --- a/embassy-stm32/src/can/fd/config.rs +++ b/embassy-stm32/src/can/fd/config.rs @@ -1,7 +1,7 @@ //! Configuration for FDCAN Module // Note: This file is copied and modified from fdcan crate by Richard Meadows -use core::num::{NonZeroU16, NonZeroU8}; +use core::num::{NonZeroU8, NonZeroU16}; /// Configures the bit timings. /// diff --git a/embassy-stm32/src/can/fdcan.rs b/embassy-stm32/src/can/fdcan.rs index d8f71e03e..a142a6d63 100644 --- a/embassy-stm32/src/can/fdcan.rs +++ b/embassy-stm32/src/can/fdcan.rs @@ -3,8 +3,8 @@ use core::future::poll_fn; use core::marker::PhantomData; use core::task::Poll; -use embassy_hal_internal::interrupt::InterruptExt; use embassy_hal_internal::PeripheralType; +use embassy_hal_internal::interrupt::InterruptExt; use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex; use embassy_sync::channel::Channel; use embassy_sync::waitqueue::AtomicWaker; @@ -13,7 +13,7 @@ use crate::can::fd::peripheral::Registers; use crate::gpio::{AfType, OutputType, Pull, SealedPin as _, Speed}; use crate::interrupt::typelevel::Interrupt; use crate::rcc::{self, RccPeripheral}; -use crate::{interrupt, peripherals, Peri}; +use crate::{Peri, interrupt, peripherals}; pub(crate) mod fd; @@ -182,8 +182,8 @@ impl<'d> CanConfigurator<'d> { rx: Peri<'d, impl RxPin>, tx: Peri<'d, impl TxPin>, _irqs: impl interrupt::typelevel::Binding> - + interrupt::typelevel::Binding> - + 'd, + + interrupt::typelevel::Binding> + + 'd, ) -> CanConfigurator<'d> { set_as_af!(rx, AfType::input(Pull::None)); set_as_af!(tx, AfType::output(OutputType::PushPull, Speed::VeryHigh)); @@ -459,7 +459,7 @@ impl<'c, 'd, const TX_BUF_SIZE: usize, const RX_BUF_SIZE: usize> BufferedCan<'d, pub async fn write(&mut self, frame: Frame) { self.tx_buf.send(frame).await; self.info.interrupt0.pend(); // Wake for Tx - //T::IT0Interrupt::pend(); // Wake for Tx + //T::IT0Interrupt::pend(); // Wake for Tx } /// Async read frame from RX buffer. @@ -548,7 +548,7 @@ impl<'c, 'd, const TX_BUF_SIZE: usize, const RX_BUF_SIZE: usize> BufferedCanFd<' pub async fn write(&mut self, frame: FdFrame) { self.tx_buf.send(frame).await; self.info.interrupt0.pend(); // Wake for Tx - //T::IT0Interrupt::pend(); // Wake for Tx + //T::IT0Interrupt::pend(); // Wake for Tx } /// Async read frame from RX buffer. diff --git a/embassy-stm32/src/can/util.rs b/embassy-stm32/src/can/util.rs index fcdbbad62..6d7f0c16a 100644 --- a/embassy-stm32/src/can/util.rs +++ b/embassy-stm32/src/can/util.rs @@ -1,6 +1,6 @@ //! Utility functions shared between CAN controller types. -use core::num::{NonZeroU16, NonZeroU8}; +use core::num::{NonZeroU8, NonZeroU16}; /// Shared struct to represent bit timings used by calc_can_timings. #[derive(Clone, Copy, Debug)] diff --git a/embassy-stm32/src/crc/v1.rs b/embassy-stm32/src/crc/v1.rs index 13e5263de..836228599 100644 --- a/embassy-stm32/src/crc/v1.rs +++ b/embassy-stm32/src/crc/v1.rs @@ -1,6 +1,6 @@ use crate::pac::CRC as PAC_CRC; use crate::peripherals::CRC; -use crate::{rcc, Peri}; +use crate::{Peri, rcc}; /// CRC driver. pub struct Crc<'d> { diff --git a/embassy-stm32/src/crc/v2v3.rs b/embassy-stm32/src/crc/v2v3.rs index d834d0971..a566a2e04 100644 --- a/embassy-stm32/src/crc/v2v3.rs +++ b/embassy-stm32/src/crc/v2v3.rs @@ -1,7 +1,7 @@ -use crate::pac::crc::vals; use crate::pac::CRC as PAC_CRC; +use crate::pac::crc::vals; use crate::peripherals::CRC; -use crate::{rcc, Peri}; +use crate::{Peri, rcc}; /// CRC driver. pub struct Crc<'d> { diff --git a/embassy-stm32/src/cryp/mod.rs b/embassy-stm32/src/cryp/mod.rs index 0173b2b5d..4f1115fb7 100644 --- a/embassy-stm32/src/cryp/mod.rs +++ b/embassy-stm32/src/cryp/mod.rs @@ -1236,7 +1236,10 @@ impl<'d, T: Instance, M: Mode> Cryp<'d, T, M> { } if C::REQUIRES_PADDING { if last_block_remainder != 0 { - panic!("Input must be a multiple of {} bytes in ECB and CBC modes. Consider padding or ciphertext stealing.", C::BLOCK_SIZE); + panic!( + "Input must be a multiple of {} bytes in ECB and CBC modes. Consider padding or ciphertext stealing.", + C::BLOCK_SIZE + ); } } if last_block { @@ -1703,7 +1706,10 @@ impl<'d, T: Instance> Cryp<'d, T, Async> { } if C::REQUIRES_PADDING { if last_block_remainder != 0 { - panic!("Input must be a multiple of {} bytes in ECB and CBC modes. Consider padding or ciphertext stealing.", C::BLOCK_SIZE); + panic!( + "Input must be a multiple of {} bytes in ECB and CBC modes. Consider padding or ciphertext stealing.", + C::BLOCK_SIZE + ); } } if last_block { diff --git a/embassy-stm32/src/dac/mod.rs b/embassy-stm32/src/dac/mod.rs index 08e001337..d74d4a4be 100644 --- a/embassy-stm32/src/dac/mod.rs +++ b/embassy-stm32/src/dac/mod.rs @@ -8,7 +8,7 @@ use crate::mode::{Async, Blocking, Mode as PeriMode}; #[cfg(any(dac_v3, dac_v4, dac_v5, dac_v6, dac_v7))] use crate::pac::dac; use crate::rcc::{self, RccPeripheral}; -use crate::{peripherals, Peri}; +use crate::{Peri, peripherals}; mod tsel; use embassy_hal_internal::PeripheralType; diff --git a/embassy-stm32/src/dcmi.rs b/embassy-stm32/src/dcmi.rs index bd03f1e00..dcae9f298 100644 --- a/embassy-stm32/src/dcmi.rs +++ b/embassy-stm32/src/dcmi.rs @@ -9,7 +9,7 @@ use embassy_sync::waitqueue::AtomicWaker; use crate::dma::Transfer; use crate::gpio::{AfType, Pull}; use crate::interrupt::typelevel::Interrupt; -use crate::{interrupt, rcc, Peri}; +use crate::{Peri, interrupt, rcc}; /// Interrupt handler. pub struct InterruptHandler { diff --git a/embassy-stm32/src/dma/dma_bdma.rs b/embassy-stm32/src/dma/dma_bdma.rs index 73ecab070..90dbf4f09 100644 --- a/embassy-stm32/src/dma/dma_bdma.rs +++ b/embassy-stm32/src/dma/dma_bdma.rs @@ -1,6 +1,6 @@ -use core::future::{poll_fn, Future}; +use core::future::{Future, poll_fn}; use core::pin::Pin; -use core::sync::atomic::{fence, AtomicUsize, Ordering}; +use core::sync::atomic::{AtomicUsize, Ordering, fence}; use core::task::{Context, Poll, Waker}; use embassy_hal_internal::Peri; diff --git a/embassy-stm32/src/dma/gpdma/mod.rs b/embassy-stm32/src/dma/gpdma/mod.rs index 4a14c2a8e..3e117c331 100644 --- a/embassy-stm32/src/dma/gpdma/mod.rs +++ b/embassy-stm32/src/dma/gpdma/mod.rs @@ -2,7 +2,7 @@ use core::future::Future; use core::pin::Pin; -use core::sync::atomic::{fence, AtomicUsize, Ordering}; +use core::sync::atomic::{AtomicUsize, Ordering, fence}; use core::task::{Context, Poll}; use embassy_hal_internal::Peri; diff --git a/embassy-stm32/src/dma/gpdma/ringbuffered.rs b/embassy-stm32/src/dma/gpdma/ringbuffered.rs index 9ee52193b..94c597e0d 100644 --- a/embassy-stm32/src/dma/gpdma/ringbuffered.rs +++ b/embassy-stm32/src/dma/gpdma/ringbuffered.rs @@ -3,12 +3,12 @@ //! FIXME: Add request_pause functionality? //! FIXME: Stop the DMA, if a user does not queue new transfers (chain of linked-list items ends automatically). use core::future::poll_fn; -use core::sync::atomic::{fence, Ordering}; +use core::sync::atomic::{Ordering, fence}; use core::task::Waker; use embassy_hal_internal::Peri; -use super::{AnyChannel, TransferOptions, STATE}; +use super::{AnyChannel, STATE, TransferOptions}; use crate::dma::gpdma::linked_list::{RunMode, Table}; use crate::dma::ringbuffer::{DmaCtrl, Error, ReadableDmaRingBuffer, WritableDmaRingBuffer}; use crate::dma::word::Word; diff --git a/embassy-stm32/src/dma/mod.rs b/embassy-stm32/src/dma/mod.rs index 5989bfd7c..297fa3674 100644 --- a/embassy-stm32/src/dma/mod.rs +++ b/embassy-stm32/src/dma/mod.rs @@ -24,7 +24,7 @@ pub(crate) use util::*; pub(crate) mod ringbuffer; pub mod word; -use embassy_hal_internal::{impl_peripheral, PeripheralType}; +use embassy_hal_internal::{PeripheralType, impl_peripheral}; use crate::interrupt; diff --git a/embassy-stm32/src/dma/ringbuffer/tests/prop_test/mod.rs b/embassy-stm32/src/dma/ringbuffer/tests/prop_test/mod.rs index 661fb1728..eff5b4058 100644 --- a/embassy-stm32/src/dma/ringbuffer/tests/prop_test/mod.rs +++ b/embassy-stm32/src/dma/ringbuffer/tests/prop_test/mod.rs @@ -2,7 +2,7 @@ use std::task::Waker; use proptest::prop_oneof; use proptest::strategy::{self, BoxedStrategy, Strategy as _}; -use proptest_state_machine::{prop_state_machine, ReferenceStateMachine, StateMachineTest}; +use proptest_state_machine::{ReferenceStateMachine, StateMachineTest, prop_state_machine}; use super::*; diff --git a/embassy-stm32/src/dsihost.rs b/embassy-stm32/src/dsihost.rs index deda956af..fd1682d2b 100644 --- a/embassy-stm32/src/dsihost.rs +++ b/embassy-stm32/src/dsihost.rs @@ -7,7 +7,7 @@ use embassy_hal_internal::PeripheralType; //use crate::gpio::{AnyPin, SealedPin}; use crate::gpio::{AfType, AnyPin, OutputType, Speed}; use crate::rcc::{self, RccPeripheral}; -use crate::{peripherals, Peri}; +use crate::{Peri, peripherals}; /// Performs a busy-wait delay for a specified number of microseconds. pub fn blocking_delay_ms(ms: u32) { diff --git a/embassy-stm32/src/dts/mod.rs b/embassy-stm32/src/dts/mod.rs index 1f39c8db5..a75ae0560 100644 --- a/embassy-stm32/src/dts/mod.rs +++ b/embassy-stm32/src/dts/mod.rs @@ -1,7 +1,7 @@ //! Digital Temperature Sensor (DTS) use core::future::poll_fn; -use core::sync::atomic::{compiler_fence, Ordering}; +use core::sync::atomic::{Ordering, compiler_fence}; use core::task::Poll; use embassy_hal_internal::Peri; diff --git a/embassy-stm32/src/eth/v1/mod.rs b/embassy-stm32/src/eth/v1/mod.rs index 5be1c9739..a77eb8719 100644 --- a/embassy-stm32/src/eth/v1/mod.rs +++ b/embassy-stm32/src/eth/v1/mod.rs @@ -4,7 +4,7 @@ mod rx_desc; mod tx_desc; use core::marker::PhantomData; -use core::sync::atomic::{fence, Ordering}; +use core::sync::atomic::{Ordering, fence}; use embassy_hal_internal::Peri; use stm32_metapac::eth::vals::{Apcs, Cr, Dm, DmaomrSr, Fes, Ftf, Ifg, MbProgress, Mw, Pbl, Rsf, St, Tsf}; @@ -190,7 +190,7 @@ impl<'d, T: Instance, P: Phy> Ethernet<'d, T, P> { w.set_apcs(Apcs::STRIP); // automatic padding and crc stripping w.set_fes(Fes::FES100); // fast ethernet speed w.set_dm(Dm::FULL_DUPLEX); // full duplex - // TODO: Carrier sense ? ECRSFD + // TODO: Carrier sense ? ECRSFD }); // Set the mac to pass all multicast packets @@ -350,7 +350,9 @@ impl<'d, T: Instance, P: Phy> Ethernet<'d, T, P> { } #[cfg(any(eth_v1b, eth_v1c))] - config_pins!(rx_clk, tx_clk, mdio, mdc, rxdv, rx_d0, rx_d1, rx_d2, rx_d3, tx_d0, tx_d1, tx_d2, tx_d3, tx_en); + config_pins!( + rx_clk, tx_clk, mdio, mdc, rxdv, rx_d0, rx_d1, rx_d2, rx_d3, tx_d0, tx_d1, tx_d2, tx_d3, tx_en + ); let pins = Pins::Mii([ rx_clk.into(), diff --git a/embassy-stm32/src/eth/v1/rx_desc.rs b/embassy-stm32/src/eth/v1/rx_desc.rs index 2a46c1895..6ade1f29c 100644 --- a/embassy-stm32/src/eth/v1/rx_desc.rs +++ b/embassy-stm32/src/eth/v1/rx_desc.rs @@ -1,4 +1,4 @@ -use core::sync::atomic::{compiler_fence, fence, Ordering}; +use core::sync::atomic::{Ordering, compiler_fence, fence}; use stm32_metapac::eth::vals::{Rpd, Rps}; use vcell::VolatileCell; diff --git a/embassy-stm32/src/eth/v1/tx_desc.rs b/embassy-stm32/src/eth/v1/tx_desc.rs index 1317d20f4..ba99b66cb 100644 --- a/embassy-stm32/src/eth/v1/tx_desc.rs +++ b/embassy-stm32/src/eth/v1/tx_desc.rs @@ -1,4 +1,4 @@ -use core::sync::atomic::{compiler_fence, fence, Ordering}; +use core::sync::atomic::{Ordering, compiler_fence, fence}; use vcell::VolatileCell; diff --git a/embassy-stm32/src/eth/v2/descriptors.rs b/embassy-stm32/src/eth/v2/descriptors.rs index 645bfdb14..e335ed8f3 100644 --- a/embassy-stm32/src/eth/v2/descriptors.rs +++ b/embassy-stm32/src/eth/v2/descriptors.rs @@ -1,4 +1,4 @@ -use core::sync::atomic::{fence, Ordering}; +use core::sync::atomic::{Ordering, fence}; use vcell::VolatileCell; diff --git a/embassy-stm32/src/eth/v2/mod.rs b/embassy-stm32/src/eth/v2/mod.rs index cf7a9901b..39a6e8b0f 100644 --- a/embassy-stm32/src/eth/v2/mod.rs +++ b/embassy-stm32/src/eth/v2/mod.rs @@ -1,7 +1,7 @@ mod descriptors; use core::marker::PhantomData; -use core::sync::atomic::{fence, Ordering}; +use core::sync::atomic::{Ordering, fence}; use embassy_hal_internal::Peri; use stm32_metapac::syscfg::vals::EthSelPhy; @@ -144,7 +144,9 @@ impl<'d, T: Instance, P: Phy> Ethernet<'d, T, P> { .modify(|w| w.set_eth_sel_phy(EthSelPhy::MII_GMII)); }); - config_pins!(rx_clk, tx_clk, mdio, mdc, rxdv, rx_d0, rx_d1, rx_d2, rx_d3, tx_d0, tx_d1, tx_d2, tx_d3, tx_en); + config_pins!( + rx_clk, tx_clk, mdio, mdc, rxdv, rx_d0, rx_d1, rx_d2, rx_d3, tx_d0, tx_d1, tx_d2, tx_d3, tx_en + ); let pins = Pins::Mii([ rx_clk.into(), diff --git a/embassy-stm32/src/exti.rs b/embassy-stm32/src/exti.rs index 9fce78f95..12600d4eb 100644 --- a/embassy-stm32/src/exti.rs +++ b/embassy-stm32/src/exti.rs @@ -5,13 +5,13 @@ use core::marker::PhantomData; use core::pin::Pin; use core::task::{Context, Poll}; -use embassy_hal_internal::{impl_peripheral, PeripheralType}; +use embassy_hal_internal::{PeripheralType, impl_peripheral}; use embassy_sync::waitqueue::AtomicWaker; use crate::gpio::{AnyPin, Input, Level, Pin as GpioPin, Pull}; -use crate::pac::exti::regs::Lines; use crate::pac::EXTI; -use crate::{interrupt, pac, peripherals, Peri}; +use crate::pac::exti::regs::Lines; +use crate::{Peri, interrupt, pac, peripherals}; const EXTI_COUNT: usize = 16; static EXTI_WAKERS: [AtomicWaker; EXTI_COUNT] = [const { AtomicWaker::new() }; EXTI_COUNT]; diff --git a/embassy-stm32/src/flash/asynch.rs b/embassy-stm32/src/flash/asynch.rs index 006dcddeb..a131217b7 100644 --- a/embassy-stm32/src/flash/asynch.rs +++ b/embassy-stm32/src/flash/asynch.rs @@ -1,17 +1,17 @@ use core::marker::PhantomData; -use core::sync::atomic::{fence, Ordering}; +use core::sync::atomic::{Ordering, fence}; use embassy_hal_internal::drop::OnDrop; use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex; use embassy_sync::mutex::Mutex; use super::{ - blocking_read, ensure_sector_aligned, family, get_flash_regions, get_sector, Async, Error, Flash, FlashLayout, - FLASH_BASE, FLASH_SIZE, WRITE_SIZE, + Async, Error, FLASH_BASE, FLASH_SIZE, Flash, FlashLayout, WRITE_SIZE, blocking_read, ensure_sector_aligned, family, + get_flash_regions, get_sector, }; use crate::interrupt::InterruptExt; use crate::peripherals::FLASH; -use crate::{interrupt, Peri}; +use crate::{Peri, interrupt}; pub(super) static REGION_ACCESS: Mutex = Mutex::new(()); diff --git a/embassy-stm32/src/flash/common.rs b/embassy-stm32/src/flash/common.rs index 10023e637..b595938a6 100644 --- a/embassy-stm32/src/flash/common.rs +++ b/embassy-stm32/src/flash/common.rs @@ -1,14 +1,14 @@ use core::marker::PhantomData; -use core::sync::atomic::{fence, Ordering}; +use core::sync::atomic::{Ordering, fence}; use embassy_hal_internal::drop::OnDrop; use super::{ - family, get_flash_regions, Async, Blocking, Error, FlashBank, FlashLayout, FlashRegion, FlashSector, FLASH_SIZE, - MAX_ERASE_SIZE, READ_SIZE, WRITE_SIZE, + Async, Blocking, Error, FLASH_SIZE, FlashBank, FlashLayout, FlashRegion, FlashSector, MAX_ERASE_SIZE, READ_SIZE, + WRITE_SIZE, family, get_flash_regions, }; -use crate::Peri; use crate::_generated::FLASH_BASE; +use crate::Peri; use crate::peripherals::FLASH; /// Internal flash memory driver. diff --git a/embassy-stm32/src/flash/eeprom.rs b/embassy-stm32/src/flash/eeprom.rs index cc3529eb9..39c497e3f 100644 --- a/embassy-stm32/src/flash/eeprom.rs +++ b/embassy-stm32/src/flash/eeprom.rs @@ -1,6 +1,6 @@ use embassy_hal_internal::drop::OnDrop; -use super::{family, Blocking, Error, Flash, EEPROM_BASE, EEPROM_SIZE}; +use super::{Blocking, EEPROM_BASE, EEPROM_SIZE, Error, Flash, family}; #[cfg(eeprom)] impl<'d> Flash<'d, Blocking> { diff --git a/embassy-stm32/src/flash/f0.rs b/embassy-stm32/src/flash/f0.rs index 3f9dbe945..5c01fce9c 100644 --- a/embassy-stm32/src/flash/f0.rs +++ b/embassy-stm32/src/flash/f0.rs @@ -1,5 +1,5 @@ use core::ptr::write_volatile; -use core::sync::atomic::{fence, Ordering}; +use core::sync::atomic::{Ordering, fence}; use super::{FlashSector, WRITE_SIZE}; use crate::flash::Error; diff --git a/embassy-stm32/src/flash/f1f3.rs b/embassy-stm32/src/flash/f1f3.rs index bf9ad2893..9e469ffbc 100644 --- a/embassy-stm32/src/flash/f1f3.rs +++ b/embassy-stm32/src/flash/f1f3.rs @@ -1,5 +1,5 @@ use core::ptr::write_volatile; -use core::sync::atomic::{fence, Ordering}; +use core::sync::atomic::{Ordering, fence}; use super::{FlashSector, WRITE_SIZE}; use crate::flash::Error; diff --git a/embassy-stm32/src/flash/f2.rs b/embassy-stm32/src/flash/f2.rs index 67e380619..b48ab3b76 100644 --- a/embassy-stm32/src/flash/f2.rs +++ b/embassy-stm32/src/flash/f2.rs @@ -1,9 +1,9 @@ use core::ptr::write_volatile; -use core::sync::atomic::{fence, AtomicBool, Ordering}; +use core::sync::atomic::{AtomicBool, Ordering, fence}; use pac::flash::regs::Sr; -use super::{get_flash_regions, FlashBank, FlashSector, WRITE_SIZE}; +use super::{FlashBank, FlashSector, WRITE_SIZE, get_flash_regions}; use crate::flash::Error; use crate::pac; diff --git a/embassy-stm32/src/flash/f4.rs b/embassy-stm32/src/flash/f4.rs index 62e0492b5..9c5051492 100644 --- a/embassy-stm32/src/flash/f4.rs +++ b/embassy-stm32/src/flash/f4.rs @@ -1,10 +1,10 @@ use core::ptr::write_volatile; -use core::sync::atomic::{fence, AtomicBool, Ordering}; +use core::sync::atomic::{AtomicBool, Ordering, fence}; use embassy_sync::waitqueue::AtomicWaker; use pac::flash::regs::Sr; -use super::{get_flash_regions, FlashBank, FlashSector, WRITE_SIZE}; +use super::{FlashBank, FlashSector, WRITE_SIZE, get_flash_regions}; use crate::_generated::FLASH_SIZE; use crate::flash::Error; use crate::pac; @@ -246,7 +246,9 @@ pub(crate) fn assert_not_corrupted_read(end_address: u32) { feature = "stm32f439zi", ))] if second_bank_read && pac::DBGMCU.idcode().read().rev_id() < REVISION_3 && !pa12_is_output_pull_low() { - panic!("Read corruption for stm32f42xxI and stm32f43xxI when PA12 is in use for chips below revision 3, see errata 2.2.11"); + panic!( + "Read corruption for stm32f42xxI and stm32f43xxI when PA12 is in use for chips below revision 3, see errata 2.2.11" + ); } #[cfg(any( @@ -270,14 +272,16 @@ pub(crate) fn assert_not_corrupted_read(end_address: u32) { feature = "stm32f439zg", ))] if second_bank_read && pac::DBGMCU.idcode().read().rev_id() < REVISION_3 && !pa12_is_output_pull_low() { - panic!("Read corruption for stm32f42xxG and stm32f43xxG in dual bank mode when PA12 is in use for chips below revision 3, see errata 2.2.11"); + panic!( + "Read corruption for stm32f42xxG and stm32f43xxG in dual bank mode when PA12 is in use for chips below revision 3, see errata 2.2.11" + ); } } #[allow(unused)] fn pa12_is_output_pull_low() -> bool { - use pac::gpio::vals; use pac::GPIOA; + use pac::gpio::vals; const PIN: usize = 12; GPIOA.moder().read().moder(PIN) == vals::Moder::OUTPUT && GPIOA.pupdr().read().pupdr(PIN) == vals::Pupdr::PULL_DOWN @@ -287,7 +291,7 @@ fn pa12_is_output_pull_low() -> bool { #[cfg(test)] mod tests { use super::*; - use crate::flash::{get_sector, FlashBank}; + use crate::flash::{FlashBank, get_sector}; #[test] #[cfg(stm32f429)] @@ -370,9 +374,13 @@ mod tests { #[cfg(all(bank_setup_configurable))] pub(crate) fn check_bank_setup() { if cfg!(feature = "single-bank") && pac::FLASH.optcr().read().db1m() { - panic!("Embassy is configured as single-bank, but the hardware is running in dual-bank mode. Change the hardware by changing the db1m value in the user option bytes or configure embassy to use dual-bank config"); + panic!( + "Embassy is configured as single-bank, but the hardware is running in dual-bank mode. Change the hardware by changing the db1m value in the user option bytes or configure embassy to use dual-bank config" + ); } if cfg!(feature = "dual-bank") && !pac::FLASH.optcr().read().db1m() { - panic!("Embassy is configured as dual-bank, but the hardware is running in single-bank mode. Change the hardware by changing the db1m value in the user option bytes or configure embassy to use single-bank config"); + panic!( + "Embassy is configured as dual-bank, but the hardware is running in single-bank mode. Change the hardware by changing the db1m value in the user option bytes or configure embassy to use single-bank config" + ); } } diff --git a/embassy-stm32/src/flash/f7.rs b/embassy-stm32/src/flash/f7.rs index 0547c747a..09389c417 100644 --- a/embassy-stm32/src/flash/f7.rs +++ b/embassy-stm32/src/flash/f7.rs @@ -1,5 +1,5 @@ use core::ptr::write_volatile; -use core::sync::atomic::{fence, Ordering}; +use core::sync::atomic::{Ordering, fence}; use super::{FlashSector, WRITE_SIZE}; use crate::flash::Error; @@ -99,7 +99,7 @@ unsafe fn blocking_wait_ready() -> Result<(), Error> { #[cfg(test)] mod tests { use super::*; - use crate::flash::{get_sector, FlashBank}; + use crate::flash::{FlashBank, get_sector}; #[test] #[cfg(stm32f732)] @@ -218,9 +218,13 @@ mod tests { #[cfg(all(bank_setup_configurable))] pub(crate) fn check_bank_setup() { if cfg!(feature = "single-bank") && !pac::FLASH.optcr().read().n_dbank() { - panic!("Embassy is configured as single-bank, but the hardware is running in dual-bank mode. Change the hardware by changing the ndbank value in the user option bytes or configure embassy to use dual-bank config"); + panic!( + "Embassy is configured as single-bank, but the hardware is running in dual-bank mode. Change the hardware by changing the ndbank value in the user option bytes or configure embassy to use dual-bank config" + ); } if cfg!(feature = "dual-bank") && pac::FLASH.optcr().read().n_dbank() { - panic!("Embassy is configured as dual-bank, but the hardware is running in single-bank mode. Change the hardware by changing the ndbank value in the user option bytes or configure embassy to use single-bank config"); + panic!( + "Embassy is configured as dual-bank, but the hardware is running in single-bank mode. Change the hardware by changing the ndbank value in the user option bytes or configure embassy to use single-bank config" + ); } } diff --git a/embassy-stm32/src/flash/g.rs b/embassy-stm32/src/flash/g.rs index bc1fd360c..d026541a4 100644 --- a/embassy-stm32/src/flash/g.rs +++ b/embassy-stm32/src/flash/g.rs @@ -1,5 +1,5 @@ use core::ptr::write_volatile; -use core::sync::atomic::{fence, Ordering}; +use core::sync::atomic::{Ordering, fence}; use cortex_m::interrupt; @@ -105,19 +105,27 @@ fn wait_busy() { #[cfg(all(bank_setup_configurable, any(flash_g4c2, flash_g4c3, flash_g4c4)))] pub(crate) fn check_bank_setup() { if cfg!(feature = "single-bank") && pac::FLASH.optr().read().dbank() { - panic!("Embassy is configured as single-bank, but the hardware is running in dual-bank mode. Change the hardware by changing the dbank value in the user option bytes or configure embassy to use dual-bank config"); + panic!( + "Embassy is configured as single-bank, but the hardware is running in dual-bank mode. Change the hardware by changing the dbank value in the user option bytes or configure embassy to use dual-bank config" + ); } if cfg!(feature = "dual-bank") && !pac::FLASH.optr().read().dbank() { - panic!("Embassy is configured as dual-bank, but the hardware is running in single-bank mode. Change the hardware by changing the dbank value in the user option bytes or configure embassy to use single-bank config"); + panic!( + "Embassy is configured as dual-bank, but the hardware is running in single-bank mode. Change the hardware by changing the dbank value in the user option bytes or configure embassy to use single-bank config" + ); } } #[cfg(all(bank_setup_configurable, flash_g0x1))] pub(crate) fn check_bank_setup() { if cfg!(feature = "single-bank") && pac::FLASH.optr().read().dual_bank() { - panic!("Embassy is configured as single-bank, but the hardware is running in dual-bank mode. Change the hardware by changing the dual_bank value in the user option bytes or configure embassy to use dual-bank config"); + panic!( + "Embassy is configured as single-bank, but the hardware is running in dual-bank mode. Change the hardware by changing the dual_bank value in the user option bytes or configure embassy to use dual-bank config" + ); } if cfg!(feature = "dual-bank") && !pac::FLASH.optr().read().dual_bank() { - panic!("Embassy is configured as dual-bank, but the hardware is running in single-bank mode. Change the hardware by changing the dual_bank value in the user option bytes or configure embassy to use single-bank config"); + panic!( + "Embassy is configured as dual-bank, but the hardware is running in single-bank mode. Change the hardware by changing the dual_bank value in the user option bytes or configure embassy to use single-bank config" + ); } } diff --git a/embassy-stm32/src/flash/h5.rs b/embassy-stm32/src/flash/h5.rs index fd9bfcc75..88f247879 100644 --- a/embassy-stm32/src/flash/h5.rs +++ b/embassy-stm32/src/flash/h5.rs @@ -1,5 +1,5 @@ use core::ptr::write_volatile; -use core::sync::atomic::{fence, Ordering}; +use core::sync::atomic::{Ordering, fence}; use super::{FlashSector, WRITE_SIZE}; use crate::flash::Error; diff --git a/embassy-stm32/src/flash/h50.rs b/embassy-stm32/src/flash/h50.rs index f8e210556..91d5da4d6 100644 --- a/embassy-stm32/src/flash/h50.rs +++ b/embassy-stm32/src/flash/h50.rs @@ -1,7 +1,7 @@ /// STM32H50 series flash impl. See RM0492 use core::{ ptr::write_volatile, - sync::atomic::{fence, Ordering}, + sync::atomic::{Ordering, fence}, }; use cortex_m::interrupt; diff --git a/embassy-stm32/src/flash/h7.rs b/embassy-stm32/src/flash/h7.rs index f1d84101c..8a43cce3f 100644 --- a/embassy-stm32/src/flash/h7.rs +++ b/embassy-stm32/src/flash/h7.rs @@ -1,7 +1,7 @@ use core::ptr::write_volatile; -use core::sync::atomic::{fence, Ordering}; +use core::sync::atomic::{Ordering, fence}; -use super::{FlashSector, BANK1_REGION, FLASH_REGIONS, WRITE_SIZE}; +use super::{BANK1_REGION, FLASH_REGIONS, FlashSector, WRITE_SIZE}; use crate::flash::Error; use crate::pac; diff --git a/embassy-stm32/src/flash/l.rs b/embassy-stm32/src/flash/l.rs index 1b82704ec..b3281f2d5 100644 --- a/embassy-stm32/src/flash/l.rs +++ b/embassy-stm32/src/flash/l.rs @@ -1,5 +1,5 @@ use core::ptr::write_volatile; -use core::sync::atomic::{fence, Ordering}; +use core::sync::atomic::{Ordering, fence}; use super::{FlashSector, WRITE_SIZE}; use crate::flash::Error; @@ -96,14 +96,20 @@ pub(crate) unsafe fn blocking_erase_sector(sector: &FlashSector) -> Result<(), E #[cfg(any(flash_wl, flash_wb, flash_l4, flash_l5))] { let idx = (sector.start - super::FLASH_BASE as u32) / super::BANK1_REGION.erase_size as u32; + #[cfg(any(flash_l4, flash_l5))] + let pgn = super::BANK1_REGION.size as u32 / super::BANK1_REGION.erase_size as u32; #[cfg(flash_l4)] - let (idx, bank) = if idx > 255 { (idx - 256, true) } else { (idx, false) }; + let (idx, bank) = if idx > (pgn - 1) { + (idx - pgn, true) + } else { + (idx, false) + }; #[cfg(flash_l5)] let (idx, bank) = if pac::FLASH.optr().read().dbank() { - if idx > 255 { - (idx - 256, Some(true)) + if idx > (pgn - 1) { + (idx - pgn, Some(true)) } else { (idx, Some(false)) } @@ -234,19 +240,27 @@ pub(crate) unsafe fn wait_ready_blocking() -> Result<(), Error> { #[cfg(all(bank_setup_configurable, flash_l5))] pub(crate) fn check_bank_setup() { if cfg!(feature = "single-bank") && pac::FLASH.optr().read().dbank() { - panic!("Embassy is configured as single-bank, but the hardware is running in dual-bank mode. Change the hardware by changing the dbank value in the user option bytes or configure embassy to use dual-bank config"); + panic!( + "Embassy is configured as single-bank, but the hardware is running in dual-bank mode. Change the hardware by changing the dbank value in the user option bytes or configure embassy to use dual-bank config" + ); } if cfg!(feature = "dual-bank") && !pac::FLASH.optr().read().dbank() { - panic!("Embassy is configured as dual-bank, but the hardware is running in single-bank mode. Change the hardware by changing the dbank value in the user option bytes or configure embassy to use single-bank config"); + panic!( + "Embassy is configured as dual-bank, but the hardware is running in single-bank mode. Change the hardware by changing the dbank value in the user option bytes or configure embassy to use single-bank config" + ); } } #[cfg(all(bank_setup_configurable, flash_l4))] pub(crate) fn check_bank_setup() { if cfg!(feature = "single-bank") && pac::FLASH.optr().read().dualbank() { - panic!("Embassy is configured as single-bank, but the hardware is running in dual-bank mode. Change the hardware by changing the dualbank value in the user option bytes or configure embassy to use dual-bank config"); + panic!( + "Embassy is configured as single-bank, but the hardware is running in dual-bank mode. Change the hardware by changing the dualbank value in the user option bytes or configure embassy to use dual-bank config" + ); } if cfg!(feature = "dual-bank") && !pac::FLASH.optr().read().dualbank() { - panic!("Embassy is configured as dual-bank, but the hardware is running in single-bank mode. Change the hardware by changing the dualbank value in the user option bytes or configure embassy to use single-bank config"); + panic!( + "Embassy is configured as dual-bank, but the hardware is running in single-bank mode. Change the hardware by changing the dualbank value in the user option bytes or configure embassy to use single-bank config" + ); } } diff --git a/embassy-stm32/src/flash/u0.rs b/embassy-stm32/src/flash/u0.rs index 68d847eca..a64f6c492 100644 --- a/embassy-stm32/src/flash/u0.rs +++ b/embassy-stm32/src/flash/u0.rs @@ -1,5 +1,5 @@ use core::ptr::write_volatile; -use core::sync::atomic::{fence, Ordering}; +use core::sync::atomic::{Ordering, fence}; use cortex_m::interrupt; diff --git a/embassy-stm32/src/flash/u5.rs b/embassy-stm32/src/flash/u5.rs index 6c3d4b422..5f1f562c0 100644 --- a/embassy-stm32/src/flash/u5.rs +++ b/embassy-stm32/src/flash/u5.rs @@ -1,5 +1,5 @@ use core::ptr::write_volatile; -use core::sync::atomic::{fence, Ordering}; +use core::sync::atomic::{Ordering, fence}; use super::{FlashBank, FlashSector, WRITE_SIZE}; use crate::flash::Error; diff --git a/embassy-stm32/src/fmc.rs b/embassy-stm32/src/fmc.rs index ff18a8bee..8ecfbc522 100644 --- a/embassy-stm32/src/fmc.rs +++ b/embassy-stm32/src/fmc.rs @@ -4,7 +4,7 @@ use core::marker::PhantomData; use embassy_hal_internal::PeripheralType; use crate::gpio::{AfType, OutputType, Pull, Speed}; -use crate::{rcc, Peri}; +use crate::{Peri, rcc}; /// FMC driver pub struct Fmc<'d, T: Instance> { diff --git a/embassy-stm32/src/gpio.rs b/embassy-stm32/src/gpio.rs index 5a8d23183..b55baffdc 100644 --- a/embassy-stm32/src/gpio.rs +++ b/embassy-stm32/src/gpio.rs @@ -4,7 +4,7 @@ use core::convert::Infallible; use critical_section::CriticalSection; -use embassy_hal_internal::{impl_peripheral, Peri, PeripheralType}; +use embassy_hal_internal::{Peri, PeripheralType, impl_peripheral}; use crate::pac::gpio::{self, vals}; use crate::peripherals; diff --git a/embassy-stm32/src/hash/mod.rs b/embassy-stm32/src/hash/mod.rs index 90c06c0d8..ba573267c 100644 --- a/embassy-stm32/src/hash/mod.rs +++ b/embassy-stm32/src/hash/mod.rs @@ -19,7 +19,7 @@ use crate::interrupt::typelevel::Interrupt; use crate::mode::Async; use crate::mode::{Blocking, Mode}; use crate::peripherals::HASH; -use crate::{interrupt, pac, peripherals, rcc, Peri}; +use crate::{Peri, interrupt, pac, peripherals, rcc}; #[cfg(hash_v1)] const NUM_CONTEXT_REGS: usize = 51; @@ -514,11 +514,7 @@ impl<'d, T: Instance> Hash<'d, T, Async> { T::regs().imr().modify(|reg| reg.set_dcie(true)); // Check for completion. let bits = T::regs().sr().read(); - if bits.dcis() { - Poll::Ready(()) - } else { - Poll::Pending - } + if bits.dcis() { Poll::Ready(()) } else { Poll::Pending } }) .await; diff --git a/embassy-stm32/src/hsem/mod.rs b/embassy-stm32/src/hsem/mod.rs index 573a1851d..4d3a5d68d 100644 --- a/embassy-stm32/src/hsem/mod.rs +++ b/embassy-stm32/src/hsem/mod.rs @@ -2,13 +2,13 @@ use embassy_hal_internal::PeripheralType; -use crate::pac; -use crate::rcc::{self, RccPeripheral}; // TODO: This code works for all HSEM implemenations except for the STM32WBA52/4/5xx MCUs. // Those MCUs have a different HSEM implementation (Secure semaphore lock support, // Privileged / unprivileged semaphore lock support, Semaphore lock protection via semaphore attribute), // which is not yet supported by this code. use crate::Peri; +use crate::pac; +use crate::rcc::{self, RccPeripheral}; /// HSEM error. #[derive(Debug)] diff --git a/embassy-stm32/src/hspi/mod.rs b/embassy-stm32/src/hspi/mod.rs index 95d9e5099..69baa708e 100644 --- a/embassy-stm32/src/hspi/mod.rs +++ b/embassy-stm32/src/hspi/mod.rs @@ -16,7 +16,7 @@ use embassy_embedded_hal::{GetConfig, SetConfig}; use embassy_hal_internal::{Peri, PeripheralType}; pub use enums::*; -use crate::dma::{word, ChannelAndRequest}; +use crate::dma::{ChannelAndRequest, word}; use crate::gpio::{AfType, AnyPin, OutputType, Pull, SealedPin as _, Speed}; use crate::mode::{Async, Blocking, Mode as PeriMode}; use crate::pac::hspi::Hspi as Regs; diff --git a/embassy-stm32/src/i2c/mod.rs b/embassy-stm32/src/i2c/mod.rs index 249bac41c..f4bf55d34 100644 --- a/embassy-stm32/src/i2c/mod.rs +++ b/embassy-stm32/src/i2c/mod.rs @@ -154,8 +154,8 @@ impl<'d> I2c<'d, Async, Master> { scl: Peri<'d, if_afio!(impl SclPin)>, sda: Peri<'d, if_afio!(impl SdaPin)>, _irq: impl interrupt::typelevel::Binding> - + interrupt::typelevel::Binding> - + 'd, + + interrupt::typelevel::Binding> + + 'd, tx_dma: Peri<'d, impl TxDma>, rx_dma: Peri<'d, impl RxDma>, config: Config, diff --git a/embassy-stm32/src/i2c/v1.rs b/embassy-stm32/src/i2c/v1.rs index 081eb1191..e6b6c7c42 100644 --- a/embassy-stm32/src/i2c/v1.rs +++ b/embassy-stm32/src/i2c/v1.rs @@ -8,7 +8,7 @@ use core::future::poll_fn; use core::task::Poll; use embassy_embedded_hal::SetConfig; -use embassy_futures::select::{select, Either}; +use embassy_futures::select::{Either, select}; use embassy_hal_internal::drop::OnDrop; use embedded_hal_1::i2c::Operation; use mode::Master; @@ -762,11 +762,7 @@ impl Timings { mode = Mode::Standard; ccr = { let ccr = clock / (frequency * 2); - if ccr < 4 { - 4 - } else { - ccr - } + if ccr < 4 { 4 } else { ccr } }; } else { const DUTYCYCLE: u8 = 0; diff --git a/embassy-stm32/src/i2c/v2.rs b/embassy-stm32/src/i2c/v2.rs index 0bfc795ac..01b6b8800 100644 --- a/embassy-stm32/src/i2c/v2.rs +++ b/embassy-stm32/src/i2c/v2.rs @@ -2,7 +2,7 @@ use core::cmp; use core::future::poll_fn; use core::task::Poll; -use config::{Address, OwnAddresses, OA2}; +use config::{Address, OA2, OwnAddresses}; use embassy_embedded_hal::SetConfig; use embassy_hal_internal::drop::OnDrop; use embedded_hal_1::i2c::Operation; diff --git a/embassy-stm32/src/i2s.rs b/embassy-stm32/src/i2s.rs index b6d3daf54..db22cfa11 100644 --- a/embassy-stm32/src/i2s.rs +++ b/embassy-stm32/src/i2s.rs @@ -3,12 +3,12 @@ use embassy_futures::join::join; use stm32_metapac::spi::vals; -use crate::dma::{ringbuffer, ChannelAndRequest, ReadableRingBuffer, TransferOptions, WritableRingBuffer}; +use crate::Peri; +use crate::dma::{ChannelAndRequest, ReadableRingBuffer, TransferOptions, WritableRingBuffer, ringbuffer}; use crate::gpio::{AfType, AnyPin, OutputType, SealedPin, Speed}; use crate::mode::Async; use crate::spi::{Config as SpiConfig, RegsExt as _, *}; use crate::time::Hertz; -use crate::Peri; /// I2S mode #[derive(Copy, Clone)] diff --git a/embassy-stm32/src/ipcc.rs b/embassy-stm32/src/ipcc.rs index 670d8332c..e1d8b1c2a 100644 --- a/embassy-stm32/src/ipcc.rs +++ b/embassy-stm32/src/ipcc.rs @@ -1,7 +1,7 @@ //! Inter-Process Communication Controller (IPCC) use core::future::poll_fn; -use core::sync::atomic::{compiler_fence, Ordering}; +use core::sync::atomic::{Ordering, compiler_fence}; use core::task::Poll; use embassy_sync::waitqueue::AtomicWaker; diff --git a/embassy-stm32/src/lib.rs b/embassy-stm32/src/lib.rs index 7e0f7884e..dbf0fe620 100644 --- a/embassy-stm32/src/lib.rs +++ b/embassy-stm32/src/lib.rs @@ -1,5 +1,6 @@ #![cfg_attr(not(test), no_std)] #![allow(async_fn_in_trait)] +#![allow(unsafe_op_in_unsafe_fn)] #![cfg_attr( docsrs, doc = "

You might want to browse the `embassy-stm32` documentation on the Embassy website instead.

The documentation here on `docs.rs` is built for a single chip only (stm32h7, stm32h7rs55 in particular), while on the Embassy website you can pick your exact chip from the top menu. Available peripherals and their APIs change depending on the chip.

\n\n" @@ -194,7 +195,7 @@ macro_rules! bind_interrupts { $( #[allow(non_snake_case)] - #[no_mangle] + #[unsafe(no_mangle)] $(#[cfg($cond_irq)])? $(#[doc = $doc])* unsafe extern "C" fn $irq() { @@ -222,7 +223,7 @@ macro_rules! bind_interrupts { } // Reexports -pub use _generated::{peripherals, Peripherals}; +pub use _generated::{Peripherals, peripherals}; pub use embassy_hal_internal::{Peri, PeripheralType}; #[cfg(feature = "unstable-pac")] pub use stm32_metapac as pac; diff --git a/embassy-stm32/src/low_power.rs b/embassy-stm32/src/low_power.rs index 342f73bc8..1b66ca680 100644 --- a/embassy-stm32/src/low_power.rs +++ b/embassy-stm32/src/low_power.rs @@ -56,13 +56,13 @@ use core::arch::asm; use core::marker::PhantomData; -use core::sync::atomic::{compiler_fence, Ordering}; +use core::sync::atomic::{Ordering, compiler_fence}; use cortex_m::peripheral::SCB; use embassy_executor::*; use crate::interrupt; -use crate::time_driver::{get_driver, RtcDriver}; +use crate::time_driver::{RtcDriver, get_driver}; const THREAD_PENDER: usize = usize::MAX; diff --git a/embassy-stm32/src/lptim/pwm.rs b/embassy-stm32/src/lptim/pwm.rs index 96af9f4d9..a69db3caf 100644 --- a/embassy-stm32/src/lptim/pwm.rs +++ b/embassy-stm32/src/lptim/pwm.rs @@ -4,12 +4,12 @@ use core::marker::PhantomData; use embassy_hal_internal::Peri; -use super::timer::Timer; #[cfg(not(any(lptim_v2a, lptim_v2b)))] use super::OutputPin; -#[cfg(any(lptim_v2a, lptim_v2b))] -use super::{channel::Channel, timer::ChannelDirection, Channel1Pin, Channel2Pin}; +use super::timer::Timer; use super::{BasicInstance, Instance}; +#[cfg(any(lptim_v2a, lptim_v2b))] +use super::{Channel1Pin, Channel2Pin, channel::Channel, timer::ChannelDirection}; #[cfg(gpio_v2)] use crate::gpio::Pull; use crate::gpio::{AfType, AnyPin, OutputType, Speed}; diff --git a/embassy-stm32/src/ltdc.rs b/embassy-stm32/src/ltdc.rs index 0f6ef569c..de2db9872 100644 --- a/embassy-stm32/src/ltdc.rs +++ b/embassy-stm32/src/ltdc.rs @@ -14,7 +14,7 @@ use stm32_metapac::ltdc::vals::{Bf1, Bf2, Cfuif, Clif, Crrif, Cterrif, Pf, Vbr}; use crate::gpio::{AfType, OutputType, Speed}; use crate::interrupt::typelevel::Interrupt; use crate::interrupt::{self}; -use crate::{peripherals, rcc, Peri}; +use crate::{Peri, peripherals, rcc}; static LTDC_WAKER: AtomicWaker = AtomicWaker::new(); diff --git a/embassy-stm32/src/opamp.rs b/embassy-stm32/src/opamp.rs index e36719ef3..ac8d5de21 100644 --- a/embassy-stm32/src/opamp.rs +++ b/embassy-stm32/src/opamp.rs @@ -3,10 +3,10 @@ use embassy_hal_internal::PeripheralType; +use crate::Peri; use crate::pac::opamp::vals::*; #[cfg(not(any(stm32g4, stm32f3)))] use crate::rcc::RccInfo; -use crate::Peri; /// Performs a busy-wait delay for a specified number of microseconds. #[cfg(opamp_v5)] diff --git a/embassy-stm32/src/ospi/mod.rs b/embassy-stm32/src/ospi/mod.rs index d93cecb69..592a8594a 100644 --- a/embassy-stm32/src/ospi/mod.rs +++ b/embassy-stm32/src/ospi/mod.rs @@ -12,14 +12,14 @@ use embassy_hal_internal::PeripheralType; pub use enums::*; use stm32_metapac::octospi::vals::{PhaseMode, SizeInBits}; -use crate::dma::{word, ChannelAndRequest}; +use crate::dma::{ChannelAndRequest, word}; use crate::gpio::{AfType, AnyPin, OutputType, Pull, SealedPin as _, Speed}; use crate::mode::{Async, Blocking, Mode as PeriMode}; -use crate::pac::octospi::{vals, Octospi as Regs}; +use crate::pac::octospi::{Octospi as Regs, vals}; #[cfg(octospim_v1)] use crate::pac::octospim::Octospim; use crate::rcc::{self, RccPeripheral}; -use crate::{peripherals, Peri}; +use crate::{Peri, peripherals}; /// OPSI driver config. #[derive(Clone, Copy)] diff --git a/embassy-stm32/src/qspi/mod.rs b/embassy-stm32/src/qspi/mod.rs index b03cd9009..bb4f4f1d0 100644 --- a/embassy-stm32/src/qspi/mod.rs +++ b/embassy-stm32/src/qspi/mod.rs @@ -14,7 +14,7 @@ use crate::gpio::{AfType, AnyPin, OutputType, Pull, Speed}; use crate::mode::{Async, Blocking, Mode as PeriMode}; use crate::pac::quadspi::Quadspi as Regs; use crate::rcc::{self, RccPeripheral}; -use crate::{peripherals, Peri}; +use crate::{Peri, peripherals}; /// QSPI transfer configuration. #[derive(Clone, Copy)] diff --git a/embassy-stm32/src/rcc/bd.rs b/embassy-stm32/src/rcc/bd.rs index 63fc195dd..3b2a10581 100644 --- a/embassy-stm32/src/rcc/bd.rs +++ b/embassy-stm32/src/rcc/bd.rs @@ -1,6 +1,6 @@ -use core::sync::atomic::{compiler_fence, Ordering}; +use core::sync::atomic::{Ordering, compiler_fence}; -use crate::pac::common::{Reg, RW}; +use crate::pac::common::{RW, Reg}; pub use crate::pac::rcc::vals::Rtcsel as RtcClockSource; use crate::time::Hertz; diff --git a/embassy-stm32/src/rcc/f247.rs b/embassy-stm32/src/rcc/f247.rs index 8f2e8db5f..d941054cd 100644 --- a/embassy-stm32/src/rcc/f247.rs +++ b/embassy-stm32/src/rcc/f247.rs @@ -1,13 +1,13 @@ use stm32_metapac::flash::vals::Latency; +#[cfg(any(stm32f4, stm32f7))] +use crate::pac::PWR; #[cfg(any(stm32f413, stm32f423, stm32f412))] pub use crate::pac::rcc::vals::Plli2ssrc as Plli2sSource; pub use crate::pac::rcc::vals::{ Hpre as AHBPrescaler, Pllm as PllPreDiv, Plln as PllMul, Pllp as PllPDiv, Pllq as PllQDiv, Pllr as PllRDiv, Pllsrc as PllSource, Ppre as APBPrescaler, Sw as Sysclk, }; -#[cfg(any(stm32f4, stm32f7))] -use crate::pac::PWR; use crate::pac::{FLASH, RCC}; use crate::time::Hertz; diff --git a/embassy-stm32/src/rcc/h.rs b/embassy-stm32/src/rcc/h.rs index 331bab7a0..485edd390 100644 --- a/embassy-stm32/src/rcc/h.rs +++ b/embassy-stm32/src/rcc/h.rs @@ -597,7 +597,10 @@ pub(crate) unsafe fn init(config: Config) { Hertz(24_000_000) => Usbrefcksel::MHZ24, Hertz(26_000_000) => Usbrefcksel::MHZ26, Hertz(32_000_000) => Usbrefcksel::MHZ32, - _ => panic!("cannot select USBPHYC reference clock with source frequency of {}, must be one of 16, 19.2, 20, 24, 26, 32 MHz", clk_val), + _ => panic!( + "cannot select USBPHYC reference clock with source frequency of {}, must be one of 16, 19.2, 20, 24, 26, 32 MHz", + clk_val + ), }, None => Usbrefcksel::MHZ24, }; diff --git a/embassy-stm32/src/rcc/l.rs b/embassy-stm32/src/rcc/l.rs index 81b89046e..2e1cbd702 100644 --- a/embassy-stm32/src/rcc/l.rs +++ b/embassy-stm32/src/rcc/l.rs @@ -499,9 +499,9 @@ pub use pll::*; #[cfg(any(stm32l0, stm32l1))] mod pll { - use super::{pll_enable, PllInstance}; - pub use crate::pac::rcc::vals::{Plldiv as PllDiv, Pllmul as PllMul, Pllsrc as PllSource}; + use super::{PllInstance, pll_enable}; use crate::pac::RCC; + pub use crate::pac::rcc::vals::{Plldiv as PllDiv, Pllmul as PllMul, Pllsrc as PllSource}; use crate::time::Hertz; #[derive(Clone, Copy)] @@ -563,11 +563,11 @@ mod pll { #[cfg(any(stm32l4, stm32l5, stm32wb, stm32wl, stm32u0))] mod pll { - use super::{pll_enable, PllInstance}; + use super::{PllInstance, pll_enable}; + use crate::pac::RCC; pub use crate::pac::rcc::vals::{ Pllm as PllPreDiv, Plln as PllMul, Pllp as PllPDiv, Pllq as PllQDiv, Pllr as PllRDiv, Pllsrc as PllSource, }; - use crate::pac::RCC; use crate::time::Hertz; #[derive(Clone, Copy)] diff --git a/embassy-stm32/src/rcc/mco.rs b/embassy-stm32/src/rcc/mco.rs index fa4b45a20..3d961df03 100644 --- a/embassy-stm32/src/rcc/mco.rs +++ b/embassy-stm32/src/rcc/mco.rs @@ -3,6 +3,7 @@ use core::marker::PhantomData; use embassy_hal_internal::PeripheralType; use crate::gpio::{AfType, OutputType, Speed}; +use crate::pac::RCC; #[cfg(not(any(stm32f1, rcc_f0v1, rcc_f3v1, rcc_f37)))] pub use crate::pac::rcc::vals::Mcopre as McoPrescaler; #[cfg(not(any( @@ -31,8 +32,7 @@ pub use crate::pac::rcc::vals::Mcosel as McoSource; rcc_h7rs ))] pub use crate::pac::rcc::vals::{Mco1sel as Mco1Source, Mco2sel as Mco2Source}; -use crate::pac::RCC; -use crate::{peripherals, Peri}; +use crate::{Peri, peripherals}; #[cfg(any(stm32f1, rcc_f0v1, rcc_f3v1, rcc_f37))] #[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd)] diff --git a/embassy-stm32/src/rcc/mod.rs b/embassy-stm32/src/rcc/mod.rs index c41f81816..addfca3c3 100644 --- a/embassy-stm32/src/rcc/mod.rs +++ b/embassy-stm32/src/rcc/mod.rs @@ -33,7 +33,7 @@ mod _version; pub use _version::*; use stm32_metapac::RCC; -pub use crate::_generated::{mux, Clocks}; +pub use crate::_generated::{Clocks, mux}; use crate::time::Hertz; #[cfg(feature = "low-power")] diff --git a/embassy-stm32/src/rcc/u5.rs b/embassy-stm32/src/rcc/u5.rs index 06895a99a..7b0dcb63f 100644 --- a/embassy-stm32/src/rcc/u5.rs +++ b/embassy-stm32/src/rcc/u5.rs @@ -6,9 +6,9 @@ pub use crate::pac::rcc::vals::{ Pllsrc as PllSource, Ppre as APBPrescaler, Sw as Sysclk, }; use crate::pac::rcc::vals::{Hseext, Msipllfast, Msipllsel, Msirgsel, Pllmboost, Pllrge}; -#[cfg(all(peri_usb_otg_hs))] -pub use crate::pac::{syscfg::vals::Usbrefcksel, SYSCFG}; use crate::pac::{FLASH, PWR, RCC}; +#[cfg(all(peri_usb_otg_hs))] +pub use crate::pac::{SYSCFG, syscfg::vals::Usbrefcksel}; use crate::rcc::LSI_FREQ; use crate::time::Hertz; @@ -442,7 +442,10 @@ pub(crate) unsafe fn init(config: Config) { Hertz(24_000_000) => Usbrefcksel::MHZ24, Hertz(26_000_000) => Usbrefcksel::MHZ26, Hertz(32_000_000) => Usbrefcksel::MHZ32, - _ => panic!("cannot select OTG_HS reference clock with source frequency of {}, must be one of 16, 19.2, 20, 24, 26, 32 MHz", clk_val), + _ => panic!( + "cannot select OTG_HS reference clock with source frequency of {}, must be one of 16, 19.2, 20, 24, 26, 32 MHz", + clk_val + ), }, None => Usbrefcksel::MHZ24, }; diff --git a/embassy-stm32/src/rcc/wba.rs b/embassy-stm32/src/rcc/wba.rs index 481437939..2528996d5 100644 --- a/embassy-stm32/src/rcc/wba.rs +++ b/embassy-stm32/src/rcc/wba.rs @@ -7,9 +7,9 @@ pub use crate::pac::rcc::vals::{ Hdiv5, Hpre as AHBPrescaler, Hpre5 as AHB5Prescaler, Hsepre as HsePrescaler, Plldiv as PllDiv, Pllm as PllPreDiv, Plln as PllMul, Pllsrc as PllSource, Ppre as APBPrescaler, Sai1sel, Sw as Sysclk, }; -#[cfg(all(peri_usb_otg_hs))] -pub use crate::pac::{syscfg::vals::Usbrefcksel, SYSCFG}; use crate::pac::{FLASH, RCC}; +#[cfg(all(peri_usb_otg_hs))] +pub use crate::pac::{SYSCFG, syscfg::vals::Usbrefcksel}; use crate::rcc::LSI_FREQ; use crate::time::Hertz; @@ -245,7 +245,10 @@ pub(crate) unsafe fn init(config: Config) { Hertz(24_000_000) => Usbrefcksel::MHZ24, Hertz(26_000_000) => Usbrefcksel::MHZ26, Hertz(32_000_000) => Usbrefcksel::MHZ32, - _ => panic!("cannot select OTG_HS reference clock with source frequency of {}, must be one of 16, 19.2, 20, 24, 26, 32 MHz", clk_val), + _ => panic!( + "cannot select OTG_HS reference clock with source frequency of {}, must be one of 16, 19.2, 20, 24, 26, 32 MHz", + clk_val + ), }, None => Usbrefcksel::MHZ24, }; diff --git a/embassy-stm32/src/rng.rs b/embassy-stm32/src/rng.rs index 63654639e..dada9bda1 100644 --- a/embassy-stm32/src/rng.rs +++ b/embassy-stm32/src/rng.rs @@ -9,7 +9,7 @@ use embassy_hal_internal::PeripheralType; use embassy_sync::waitqueue::AtomicWaker; use crate::interrupt::typelevel::Interrupt; -use crate::{interrupt, pac, peripherals, rcc, Peri}; +use crate::{Peri, interrupt, pac, peripherals, rcc}; static RNG_WAKER: AtomicWaker = AtomicWaker::new(); diff --git a/embassy-stm32/src/rtc/low_power.rs b/embassy-stm32/src/rtc/low_power.rs index a81ac6746..999f24714 100644 --- a/embassy-stm32/src/rtc/low_power.rs +++ b/embassy-stm32/src/rtc/low_power.rs @@ -1,7 +1,7 @@ #[cfg(feature = "time")] use embassy_time::{Duration, TICK_HZ}; -use super::{bcd2_to_byte, DateTimeError, Rtc, RtcError}; +use super::{DateTimeError, Rtc, RtcError, bcd2_to_byte}; use crate::interrupt::typelevel::Interrupt; use crate::peripherals::RTC; use crate::rtc::SealedInstance; diff --git a/embassy-stm32/src/rtc/mod.rs b/embassy-stm32/src/rtc/mod.rs index 92dec0960..bc6df528b 100644 --- a/embassy-stm32/src/rtc/mod.rs +++ b/embassy-stm32/src/rtc/mod.rs @@ -7,13 +7,13 @@ mod low_power; #[cfg(feature = "low-power")] use core::cell::Cell; -#[cfg(feature = "low-power")] -use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex; #[cfg(feature = "low-power")] use embassy_sync::blocking_mutex::Mutex; +#[cfg(feature = "low-power")] +use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex; -use self::datetime::{day_of_week_from_u8, day_of_week_to_u8}; pub use self::datetime::{DateTime, DayOfWeek, Error as DateTimeError}; +use self::datetime::{day_of_week_from_u8, day_of_week_to_u8}; use crate::pac::rtc::regs::{Dr, Tr}; use crate::time::Hertz; @@ -25,8 +25,8 @@ mod _version; #[allow(unused_imports)] pub use _version::*; -use crate::peripherals::RTC; use crate::Peri; +use crate::peripherals::RTC; /// Errors that can occur on methods on [RtcClock] #[non_exhaustive] diff --git a/embassy-stm32/src/rtc/v3.rs b/embassy-stm32/src/rtc/v3.rs index d0b52049e..01da5d70a 100644 --- a/embassy-stm32/src/rtc/v3.rs +++ b/embassy-stm32/src/rtc/v3.rs @@ -1,4 +1,4 @@ -use stm32_metapac::rtc::vals::{Calp, Calw16, Calw8, Fmt, Key, Osel, Pol, TampalrmType}; +use stm32_metapac::rtc::vals::{Calp, Calw8, Calw16, Fmt, Key, Osel, Pol, TampalrmType}; use super::RtcCalibrationCyclePeriod; use crate::pac::rtc::Rtc; diff --git a/embassy-stm32/src/sai/mod.rs b/embassy-stm32/src/sai/mod.rs index fb8b23b79..726d1729a 100644 --- a/embassy-stm32/src/sai/mod.rs +++ b/embassy-stm32/src/sai/mod.rs @@ -6,12 +6,12 @@ use core::marker::PhantomData; use embassy_hal_internal::PeripheralType; pub use crate::dma::word; -use crate::dma::{ringbuffer, Channel, ReadableRingBuffer, Request, TransferOptions, WritableRingBuffer}; +use crate::dma::{Channel, ReadableRingBuffer, Request, TransferOptions, WritableRingBuffer, ringbuffer}; use crate::gpio::{AfType, AnyPin, OutputType, Pull, SealedPin as _, Speed}; pub use crate::pac::sai::vals::Mckdiv as MasterClockDivider; -use crate::pac::sai::{vals, Sai as Regs}; +use crate::pac::sai::{Sai as Regs, vals}; use crate::rcc::{self, RccPeripheral}; -use crate::{peripherals, Peri}; +use crate::{Peri, peripherals}; /// SAI error #[derive(Debug, PartialEq, Eq, Clone, Copy)] diff --git a/embassy-stm32/src/sdmmc/mod.rs b/embassy-stm32/src/sdmmc/mod.rs index ccbd16cbf..408d1b764 100644 --- a/embassy-stm32/src/sdmmc/mod.rs +++ b/embassy-stm32/src/sdmmc/mod.rs @@ -11,9 +11,9 @@ use embassy_hal_internal::drop::OnDrop; use embassy_hal_internal::{Peri, PeripheralType}; use embassy_sync::waitqueue::AtomicWaker; use sdio_host::common_cmd::{self, Resp, ResponseLen}; -use sdio_host::emmc::{ExtCSD, EMMC}; -use sdio_host::sd::{BusWidth, CardCapacity, CardStatus, CurrentState, SDStatus, CIC, CID, CSD, OCR, RCA, SCR, SD}; -use sdio_host::{emmc_cmd, sd_cmd, Cmd}; +use sdio_host::emmc::{EMMC, ExtCSD}; +use sdio_host::sd::{BusWidth, CIC, CID, CSD, CardCapacity, CardStatus, CurrentState, OCR, RCA, SCR, SD, SDStatus}; +use sdio_host::{Cmd, emmc_cmd, sd_cmd}; #[cfg(sdmmc_v1)] use crate::dma::ChannelAndRequest; diff --git a/embassy-stm32/src/spdifrx/mod.rs b/embassy-stm32/src/spdifrx/mod.rs index b0a32d5d1..6f2d24560 100644 --- a/embassy-stm32/src/spdifrx/mod.rs +++ b/embassy-stm32/src/spdifrx/mod.rs @@ -13,7 +13,7 @@ use crate::gpio::{AfType, AnyPin, Pull, SealedPin as _}; use crate::interrupt::typelevel::Interrupt; use crate::pac::spdifrx::Spdifrx as Regs; use crate::rcc::{RccInfo, SealedRccPeripheral}; -use crate::{interrupt, peripherals, Peri}; +use crate::{Peri, interrupt, peripherals}; /// Possible S/PDIF preamble types. #[allow(dead_code)] diff --git a/embassy-stm32/src/spi/mod.rs b/embassy-stm32/src/spi/mod.rs index c5373a54d..c27d09ea7 100644 --- a/embassy-stm32/src/spi/mod.rs +++ b/embassy-stm32/src/spi/mod.rs @@ -6,15 +6,15 @@ use core::ptr; use embassy_embedded_hal::SetConfig; use embassy_futures::join::join; -pub use embedded_hal_02::spi::{Mode, Phase, Polarity, MODE_0, MODE_1, MODE_2, MODE_3}; +pub use embedded_hal_02::spi::{MODE_0, MODE_1, MODE_2, MODE_3, Mode, Phase, Polarity}; -use crate::dma::{word, ChannelAndRequest}; +use crate::Peri; +use crate::dma::{ChannelAndRequest, word}; use crate::gpio::{AfType, AnyPin, OutputType, Pull, SealedPin as _, Speed}; use crate::mode::{Async, Blocking, Mode as PeriMode}; -use crate::pac::spi::{regs, vals, Spi as Regs}; +use crate::pac::spi::{Spi as Regs, regs, vals}; use crate::rcc::{RccInfo, SealedRccPeripheral}; use crate::time::Hertz; -use crate::Peri; /// SPI error. #[derive(Debug, PartialEq, Eq, Clone, Copy)] diff --git a/embassy-stm32/src/time_driver.rs b/embassy-stm32/src/time_driver.rs index 7db74bdf6..74b10a183 100644 --- a/embassy-stm32/src/time_driver.rs +++ b/embassy-stm32/src/time_driver.rs @@ -1,14 +1,14 @@ #![allow(non_snake_case)] use core::cell::{Cell, RefCell}; -use core::sync::atomic::{compiler_fence, AtomicU32, Ordering}; +use core::sync::atomic::{AtomicU32, Ordering, compiler_fence}; use critical_section::CriticalSection; -use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex; use embassy_sync::blocking_mutex::Mutex; +use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex; use embassy_time_driver::{Driver, TICK_HZ}; use embassy_time_queue_utils::Queue; -use stm32_metapac::timer::{regs, TimGp16}; +use stm32_metapac::timer::{TimGp16, regs}; use crate::interrupt::typelevel::Interrupt; use crate::pac::timer::vals; diff --git a/embassy-stm32/src/timer/complementary_pwm.rs b/embassy-stm32/src/timer/complementary_pwm.rs index 484aae1d0..75a83629c 100644 --- a/embassy-stm32/src/timer/complementary_pwm.rs +++ b/embassy-stm32/src/timer/complementary_pwm.rs @@ -7,11 +7,11 @@ pub use stm32_metapac::timer::vals::{Ckd, Ossi, Ossr}; use super::low_level::{CountingMode, OutputPolarity, Timer}; use super::simple_pwm::PwmPin; use super::{AdvancedInstance4Channel, Ch1, Ch2, Ch3, Ch4, Channel, TimerComplementaryPin}; +use crate::Peri; use crate::gpio::{AnyPin, OutputType}; use crate::time::Hertz; -use crate::timer::low_level::OutputCompareMode; use crate::timer::TimerChannel; -use crate::Peri; +use crate::timer::low_level::OutputCompareMode; /// Complementary PWM pin wrapper. /// @@ -388,7 +388,7 @@ fn compute_dead_time_value(value: u16) -> (Ckd, u8) { #[cfg(test)] mod tests { - use super::{compute_dead_time_value, Ckd}; + use super::{Ckd, compute_dead_time_value}; #[test] fn test_compute_dead_time_value() { diff --git a/embassy-stm32/src/timer/input_capture.rs b/embassy-stm32/src/timer/input_capture.rs index 7a25e6c21..2a4ec2db0 100644 --- a/embassy-stm32/src/timer/input_capture.rs +++ b/embassy-stm32/src/timer/input_capture.rs @@ -8,11 +8,11 @@ use core::task::{Context, Poll}; use super::low_level::{CountingMode, FilterValue, InputCaptureMode, InputTISelection, Timer}; use super::{CaptureCompareInterruptHandler, Channel, GeneralInstance4Channel, TimerPin}; pub use super::{Ch1, Ch2, Ch3, Ch4}; +use crate::Peri; use crate::gpio::{AfType, AnyPin, Pull}; use crate::interrupt::typelevel::{Binding, Interrupt}; use crate::time::Hertz; use crate::timer::TimerChannel; -use crate::Peri; /// Capture pin wrapper. /// diff --git a/embassy-stm32/src/timer/one_pulse.rs b/embassy-stm32/src/timer/one_pulse.rs index a75b41bd7..fe8681356 100644 --- a/embassy-stm32/src/timer/one_pulse.rs +++ b/embassy-stm32/src/timer/one_pulse.rs @@ -11,12 +11,12 @@ use super::low_level::{ }; use super::{CaptureCompareInterruptHandler, Channel, ExternalTriggerPin, GeneralInstance4Channel, TimerPin}; pub use super::{Ch1, Ch2}; +use crate::Peri; use crate::gpio::{AfType, AnyPin, Pull}; use crate::interrupt::typelevel::{Binding, Interrupt}; use crate::pac::timer::vals::Etp; use crate::time::Hertz; use crate::timer::TimerChannel; -use crate::Peri; /// External input marker type. pub enum Ext {} diff --git a/embassy-stm32/src/timer/pwm_input.rs b/embassy-stm32/src/timer/pwm_input.rs index 159b5a177..da8a79b09 100644 --- a/embassy-stm32/src/timer/pwm_input.rs +++ b/embassy-stm32/src/timer/pwm_input.rs @@ -2,9 +2,9 @@ use super::low_level::{CountingMode, InputCaptureMode, InputTISelection, SlaveMode, Timer, TriggerSource}; use super::{Ch1, Ch2, Channel, GeneralInstance4Channel, TimerPin}; +use crate::Peri; use crate::gpio::{AfType, Pull}; use crate::time::Hertz; -use crate::Peri; /// PWM Input driver. /// diff --git a/embassy-stm32/src/timer/qei.rs b/embassy-stm32/src/timer/qei.rs index bb152731c..a547a2a19 100644 --- a/embassy-stm32/src/timer/qei.rs +++ b/embassy-stm32/src/timer/qei.rs @@ -5,9 +5,9 @@ use stm32_metapac::timer::vals::{self, Sms}; use super::low_level::Timer; pub use super::{Ch1, Ch2}; use super::{GeneralInstance4Channel, TimerPin}; +use crate::Peri; use crate::gpio::{AfType, AnyPin, Pull}; use crate::timer::TimerChannel; -use crate::Peri; /// Qei driver config. #[cfg_attr(feature = "defmt", derive(defmt::Format))] diff --git a/embassy-stm32/src/timer/simple_pwm.rs b/embassy-stm32/src/timer/simple_pwm.rs index e6165e42b..06315d7f3 100644 --- a/embassy-stm32/src/timer/simple_pwm.rs +++ b/embassy-stm32/src/timer/simple_pwm.rs @@ -5,11 +5,11 @@ use core::mem::ManuallyDrop; use super::low_level::{CountingMode, OutputCompareMode, OutputPolarity, Timer}; use super::{Ch1, Ch2, Ch3, Ch4, Channel, GeneralInstance4Channel, TimerBits, TimerChannel, TimerPin}; +use crate::Peri; #[cfg(gpio_v2)] use crate::gpio::Pull; use crate::gpio::{AfType, AnyPin, OutputType, Speed}; use crate::time::Hertz; -use crate::Peri; /// PWM pin wrapper. /// diff --git a/embassy-stm32/src/tsc/acquisition_banks.rs b/embassy-stm32/src/tsc/acquisition_banks.rs index 7d6442b48..097cf7942 100644 --- a/embassy-stm32/src/tsc/acquisition_banks.rs +++ b/embassy-stm32/src/tsc/acquisition_banks.rs @@ -1,11 +1,11 @@ +use super::TSC_NUM_GROUPS; use super::io_pin::*; #[cfg(any(tsc_v2, tsc_v3))] use super::pin_groups::G7; #[cfg(tsc_v3)] use super::pin_groups::G8; -use super::pin_groups::{pin_roles, G1, G2, G3, G4, G5, G6}; +use super::pin_groups::{G1, G2, G3, G4, G5, G6, pin_roles}; use super::types::{Group, GroupStatus}; -use super::TSC_NUM_GROUPS; /// Represents a collection of TSC (Touch Sensing Controller) pins for an acquisition bank. /// diff --git a/embassy-stm32/src/tsc/pin_groups.rs b/embassy-stm32/src/tsc/pin_groups.rs index 84421f7ff..9347e6bc0 100644 --- a/embassy-stm32/src/tsc/pin_groups.rs +++ b/embassy-stm32/src/tsc/pin_groups.rs @@ -1,11 +1,11 @@ use core::marker::PhantomData; use core::ops::BitOr; +use super::Instance; use super::errors::GroupError; use super::io_pin::*; -use super::Instance; -use crate::gpio::{AfType, AnyPin, OutputType, Speed}; use crate::Peri; +use crate::gpio::{AfType, AnyPin, OutputType, Speed}; /// Pin type definition to control IO parameters #[derive(PartialEq, Clone, Copy)] diff --git a/embassy-stm32/src/ucpd.rs b/embassy-stm32/src/ucpd.rs index 18aff4fbd..8f259a917 100644 --- a/embassy-stm32/src/ucpd.rs +++ b/embassy-stm32/src/ucpd.rs @@ -19,8 +19,8 @@ use core::marker::PhantomData; use core::sync::atomic::{AtomicBool, Ordering}; use core::task::Poll; -use embassy_hal_internal::drop::OnDrop; use embassy_hal_internal::PeripheralType; +use embassy_hal_internal::drop::OnDrop; use embassy_sync::waitqueue::AtomicWaker; use crate::dma::{ChannelAndRequest, TransferOptions}; @@ -28,7 +28,7 @@ use crate::interrupt::typelevel::Interrupt; use crate::pac::ucpd::vals::{Anamode, Ccenable, PscUsbpdclk, Txmode}; pub use crate::pac::ucpd::vals::{Phyccsel as CcSel, Rxordset, TypecVstateCc as CcVState}; use crate::rcc::{self, RccPeripheral}; -use crate::{interrupt, Peri}; +use crate::{Peri, interrupt}; pub(crate) fn init( _cs: critical_section::CriticalSection, diff --git a/embassy-stm32/src/uid.rs b/embassy-stm32/src/uid.rs index 5e38532bd..2d3e2b972 100644 --- a/embassy-stm32/src/uid.rs +++ b/embassy-stm32/src/uid.rs @@ -1,8 +1,8 @@ //! Unique ID (UID) /// Get this device's unique 96-bit ID. -pub fn uid() -> &'static [u8; 12] { - unsafe { &*crate::pac::UID.uid(0).as_ptr().cast::<[u8; 12]>() } +pub fn uid() -> [u8; 12] { + unsafe { *crate::pac::UID.uid(0).as_ptr().cast::<[u8; 12]>() } } /// Get this device's unique 96-bit ID, encoded into a string of 24 hexadecimal ASCII digits. diff --git a/embassy-stm32/src/usart/buffered.rs b/embassy-stm32/src/usart/buffered.rs index 10dc02334..69c3a740f 100644 --- a/embassy-stm32/src/usart/buffered.rs +++ b/embassy-stm32/src/usart/buffered.rs @@ -5,16 +5,16 @@ use core::sync::atomic::{AtomicBool, AtomicU8, AtomicUsize, Ordering}; use core::task::Poll; use embassy_embedded_hal::SetConfig; -use embassy_hal_internal::atomic_ring_buffer::RingBuffer; use embassy_hal_internal::Peri; +use embassy_hal_internal::atomic_ring_buffer::RingBuffer; use embassy_sync::waitqueue::AtomicWaker; #[cfg(not(any(usart_v1, usart_v2)))] use super::DePin; use super::{ + Config, ConfigError, CtsPin, Duplex, Error, HalfDuplexReadback, Info, Instance, Regs, RtsPin, RxPin, TxPin, clear_interrupt_flags, configure, half_duplex_set_rx_tx_before_write, rdr, reconfigure, send_break, set_baudrate, - sr, tdr, Config, ConfigError, CtsPin, Duplex, Error, HalfDuplexReadback, Info, Instance, Regs, RtsPin, RxPin, - TxPin, + sr, tdr, }; use crate::gpio::{AfType, AnyPin, Pull, SealedPin as _}; use crate::interrupt::{self, InterruptExt}; diff --git a/embassy-stm32/src/usart/mod.rs b/embassy-stm32/src/usart/mod.rs index 0d2d86aca..0e7da634d 100644 --- a/embassy-stm32/src/usart/mod.rs +++ b/embassy-stm32/src/usart/mod.rs @@ -4,15 +4,16 @@ use core::future::poll_fn; use core::marker::PhantomData; -use core::sync::atomic::{compiler_fence, AtomicU8, AtomicUsize, Ordering}; +use core::sync::atomic::{AtomicU8, AtomicUsize, Ordering, compiler_fence}; use core::task::Poll; use embassy_embedded_hal::SetConfig; -use embassy_hal_internal::drop::OnDrop; use embassy_hal_internal::PeripheralType; +use embassy_hal_internal::drop::OnDrop; use embassy_sync::waitqueue::AtomicWaker; -use futures_util::future::{select, Either}; +use futures_util::future::{Either, select}; +use crate::Peri; use crate::dma::ChannelAndRequest; use crate::gpio::{AfType, AnyPin, OutputType, Pull, SealedPin as _, Speed}; use crate::interrupt::typelevel::Interrupt as _; @@ -25,7 +26,6 @@ use crate::pac::usart::Usart as Regs; use crate::pac::usart::{regs, vals}; use crate::rcc::{RccInfo, SealedRccPeripheral}; use crate::time::Hertz; -use crate::Peri; /// Interrupt handler. pub struct InterruptHandler { diff --git a/embassy-stm32/src/usart/ringbuffered.rs b/embassy-stm32/src/usart/ringbuffered.rs index 27071fb31..20bfefd9e 100644 --- a/embassy-stm32/src/usart/ringbuffered.rs +++ b/embassy-stm32/src/usart/ringbuffered.rs @@ -1,19 +1,19 @@ use core::future::poll_fn; use core::mem; -use core::sync::atomic::{compiler_fence, Ordering}; +use core::sync::atomic::{Ordering, compiler_fence}; use core::task::Poll; use embassy_embedded_hal::SetConfig; use embedded_io_async::ReadReady; -use futures_util::future::{select, Either}; +use futures_util::future::{Either, select}; -use super::{rdr, reconfigure, set_baudrate, sr, Config, ConfigError, Error, Info, State, UartRx}; +use super::{Config, ConfigError, Error, Info, State, UartRx, rdr, reconfigure, set_baudrate, sr}; +use crate::Peri; use crate::dma::ReadableRingBuffer; use crate::gpio::{AnyPin, SealedPin as _}; use crate::mode::Async; use crate::time::Hertz; use crate::usart::Regs; -use crate::Peri; /// Rx-only Ring-buffered UART Driver /// diff --git a/embassy-stm32/src/usb/otg.rs b/embassy-stm32/src/usb/otg.rs index 5ce81b131..f6b1a81db 100644 --- a/embassy-stm32/src/usb/otg.rs +++ b/embassy-stm32/src/usb/otg.rs @@ -2,18 +2,18 @@ use core::marker::PhantomData; use embassy_hal_internal::PeripheralType; use embassy_usb_driver::{EndpointAddress, EndpointAllocError, EndpointType, Event, Unsupported}; -use embassy_usb_synopsys_otg::otg_v1::vals::Dspd; -use embassy_usb_synopsys_otg::otg_v1::Otg; pub use embassy_usb_synopsys_otg::Config; +use embassy_usb_synopsys_otg::otg_v1::Otg; +use embassy_usb_synopsys_otg::otg_v1::vals::Dspd; use embassy_usb_synopsys_otg::{ - on_interrupt as on_interrupt_impl, Bus as OtgBus, ControlPipe, Driver as OtgDriver, Endpoint, In, OtgInstance, Out, - PhyType, State, + Bus as OtgBus, ControlPipe, Driver as OtgDriver, Endpoint, In, OtgInstance, Out, PhyType, State, + on_interrupt as on_interrupt_impl, }; use crate::gpio::{AfType, OutputType, Speed}; use crate::interrupt::typelevel::Interrupt; use crate::rcc::{self, RccPeripheral}; -use crate::{interrupt, Peri}; +use crate::{Peri, interrupt}; const MAX_EP_COUNT: usize = 9; diff --git a/embassy-stm32/src/usb/usb.rs b/embassy-stm32/src/usb/usb.rs index 9e08d99b3..d405e4802 100644 --- a/embassy-stm32/src/usb/usb.rs +++ b/embassy-stm32/src/usb/usb.rs @@ -12,11 +12,11 @@ use embassy_usb_driver::{ Direction, EndpointAddress, EndpointAllocError, EndpointError, EndpointInfo, EndpointType, Event, Unsupported, }; +use crate::pac::USBRAM; use crate::pac::usb::regs; use crate::pac::usb::vals::{EpType, Stat}; -use crate::pac::USBRAM; use crate::rcc::RccPeripheral; -use crate::{interrupt, Peri}; +use crate::{Peri, interrupt}; /// Interrupt handler. pub struct InterruptHandler { diff --git a/embassy-stm32/src/vrefbuf/mod.rs b/embassy-stm32/src/vrefbuf/mod.rs index ccbd748d5..b061306a0 100644 --- a/embassy-stm32/src/vrefbuf/mod.rs +++ b/embassy-stm32/src/vrefbuf/mod.rs @@ -62,8 +62,7 @@ impl<'d, T: Instance> VoltageReferenceBuffer<'d, T> { } trace!( "Vrefbuf configured with voltage scale {} and impedance mode {}", - voltage_scale as u8, - impedance_mode as u8, + voltage_scale as u8, impedance_mode as u8, ); VoltageReferenceBuffer { vrefbuf: PhantomData } } diff --git a/embassy-stm32/src/wdg/mod.rs b/embassy-stm32/src/wdg/mod.rs index fb5c3d930..1164739ff 100644 --- a/embassy-stm32/src/wdg/mod.rs +++ b/embassy-stm32/src/wdg/mod.rs @@ -4,8 +4,8 @@ use core::marker::PhantomData; use embassy_hal_internal::PeripheralType; use stm32_metapac::iwdg::vals::{Key, Pr}; -use crate::rcc::LSI_FREQ; use crate::Peri; +use crate::rcc::LSI_FREQ; /// Independent watchdog (IWDG) driver. pub struct IndependentWatchdog<'d, T: Instance> { diff --git a/embassy-stm32/src/xspi/mod.rs b/embassy-stm32/src/xspi/mod.rs index 901569f64..a80a2692b 100644 --- a/embassy-stm32/src/xspi/mod.rs +++ b/embassy-stm32/src/xspi/mod.rs @@ -11,15 +11,15 @@ use embassy_embedded_hal::{GetConfig, SetConfig}; use embassy_hal_internal::PeripheralType; pub use enums::*; -use crate::dma::{word, ChannelAndRequest}; +use crate::dma::{ChannelAndRequest, word}; use crate::gpio::{AfType, AnyPin, OutputType, Pull, SealedPin as _, Speed}; use crate::mode::{Async, Blocking, Mode as PeriMode}; -use crate::pac::xspi::vals::*; use crate::pac::xspi::Xspi as Regs; +use crate::pac::xspi::vals::*; #[cfg(xspim_v1)] use crate::pac::xspim::Xspim; use crate::rcc::{self, RccPeripheral}; -use crate::{peripherals, Peri}; +use crate::{Peri, peripherals}; /// XPSI driver config. #[derive(Clone, Copy)] diff --git a/embassy-sync/Cargo.toml b/embassy-sync/Cargo.toml index 64d76baba..0c28cec7d 100644 --- a/embassy-sync/Cargo.toml +++ b/embassy-sync/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "embassy-sync" version = "0.7.2" -edition = "2021" +edition = "2024" description = "no-std, no-alloc synchronization primitives with async support" repository = "https://github.com/embassy-rs/embassy" documentation = "https://docs.embassy.dev/embassy-sync" diff --git a/embassy-sync/src/channel.rs b/embassy-sync/src/channel.rs index de437cc52..dbd24a6c7 100644 --- a/embassy-sync/src/channel.rs +++ b/embassy-sync/src/channel.rs @@ -50,8 +50,8 @@ use core::task::{Context, Poll}; use heapless::Deque; -use crate::blocking_mutex::raw::RawMutex; use crate::blocking_mutex::Mutex; +use crate::blocking_mutex::raw::RawMutex; use crate::waitqueue::WakerRegistration; /// Send-only access to a [`Channel`]. @@ -1112,11 +1112,13 @@ mod tests { static CHANNEL: StaticCell> = StaticCell::new(); let c = &*CHANNEL.init(Channel::new()); let c2 = c; - assert!(executor - .spawn(async move { - assert!(c2.try_send(1).is_ok()); - }) - .is_ok()); + assert!( + executor + .spawn(async move { + assert!(c2.try_send(1).is_ok()); + }) + .is_ok() + ); assert_eq!(c.receive().await, 1); } @@ -1143,13 +1145,15 @@ mod tests { // However, I've used the debugger to observe that the send does indeed wait. Delay::new(Duration::from_millis(500)).await; assert_eq!(c.receive().await, 1); - assert!(executor - .spawn(async move { - loop { - c.receive().await; - } - }) - .is_ok()); + assert!( + executor + .spawn(async move { + loop { + c.receive().await; + } + }) + .is_ok() + ); send_task_1.unwrap().await; send_task_2.unwrap().await; } diff --git a/embassy-sync/src/lib.rs b/embassy-sync/src/lib.rs index 5d91b4d9c..1cfde8b10 100644 --- a/embassy-sync/src/lib.rs +++ b/embassy-sync/src/lib.rs @@ -1,6 +1,7 @@ #![cfg_attr(not(feature = "std"), no_std)] #![allow(async_fn_in_trait)] #![allow(clippy::new_without_default)] +#![allow(unsafe_op_in_unsafe_fn)] #![doc = include_str!("../README.md")] #![warn(missing_docs)] diff --git a/embassy-sync/src/mutex.rs b/embassy-sync/src/mutex.rs index aea682899..96b834f02 100644 --- a/embassy-sync/src/mutex.rs +++ b/embassy-sync/src/mutex.rs @@ -2,13 +2,13 @@ //! //! This module provides a mutex that can be used to synchronize data between asynchronous tasks. use core::cell::{RefCell, UnsafeCell}; -use core::future::{poll_fn, Future}; +use core::future::{Future, poll_fn}; use core::ops::{Deref, DerefMut}; use core::task::Poll; use core::{fmt, mem}; -use crate::blocking_mutex::raw::RawMutex; use crate::blocking_mutex::Mutex as BlockingMutex; +use crate::blocking_mutex::raw::RawMutex; use crate::waitqueue::WakerRegistration; /// Error returned by [`Mutex::try_lock`] diff --git a/embassy-sync/src/once_lock.rs b/embassy-sync/src/once_lock.rs index 73edfea9a..2af19ca20 100644 --- a/embassy-sync/src/once_lock.rs +++ b/embassy-sync/src/once_lock.rs @@ -2,7 +2,7 @@ use core::cell::Cell; use core::fmt::{Debug, Formatter}; -use core::future::{poll_fn, Future}; +use core::future::{Future, poll_fn}; use core::mem::MaybeUninit; use core::sync::atomic::{AtomicBool, Ordering}; use core::task::Poll; diff --git a/embassy-sync/src/pipe.rs b/embassy-sync/src/pipe.rs index 6d624979a..215a556d9 100644 --- a/embassy-sync/src/pipe.rs +++ b/embassy-sync/src/pipe.rs @@ -7,8 +7,8 @@ use core::ops::Range; use core::pin::Pin; use core::task::{Context, Poll}; -use crate::blocking_mutex::raw::RawMutex; use crate::blocking_mutex::Mutex; +use crate::blocking_mutex::raw::RawMutex; use crate::ring_buffer::RingBuffer; use crate::waitqueue::WakerRegistration; diff --git a/embassy-sync/src/priority_channel.rs b/embassy-sync/src/priority_channel.rs index 715a20e86..1af7d9221 100644 --- a/embassy-sync/src/priority_channel.rs +++ b/embassy-sync/src/priority_channel.rs @@ -8,11 +8,11 @@ use core::future::Future; use core::pin::Pin; use core::task::{Context, Poll}; -pub use heapless::binary_heap::{Kind, Max, Min}; use heapless::BinaryHeap; +pub use heapless::binary_heap::{Kind, Max, Min}; -use crate::blocking_mutex::raw::RawMutex; use crate::blocking_mutex::Mutex; +use crate::blocking_mutex::raw::RawMutex; use crate::channel::{DynamicChannel, DynamicReceiver, DynamicSender, TryReceiveError, TrySendError}; use crate::waitqueue::WakerRegistration; @@ -799,11 +799,13 @@ mod tests { static CHANNEL: StaticCell> = StaticCell::new(); let c = &*CHANNEL.init(PriorityChannel::new()); let c2 = c; - assert!(executor - .spawn(async move { - assert!(c2.try_send(1).is_ok()); - }) - .is_ok()); + assert!( + executor + .spawn(async move { + assert!(c2.try_send(1).is_ok()); + }) + .is_ok() + ); assert_eq!(c.receive().await, 1); } @@ -830,13 +832,15 @@ mod tests { // However, I've used the debugger to observe that the send does indeed wait. Delay::new(Duration::from_millis(500)).await; assert_eq!(c.receive().await, 1); - assert!(executor - .spawn(async move { - loop { - c.receive().await; - } - }) - .is_ok()); + assert!( + executor + .spawn(async move { + loop { + c.receive().await; + } + }) + .is_ok() + ); send_task_1.unwrap().await; send_task_2.unwrap().await; } diff --git a/embassy-sync/src/pubsub/mod.rs b/embassy-sync/src/pubsub/mod.rs index ad9402f5a..127a208f1 100644 --- a/embassy-sync/src/pubsub/mod.rs +++ b/embassy-sync/src/pubsub/mod.rs @@ -10,8 +10,8 @@ use heapless::Deque; use self::publisher::{ImmediatePub, Pub}; use self::subscriber::Sub; -use crate::blocking_mutex::raw::RawMutex; use crate::blocking_mutex::Mutex; +use crate::blocking_mutex::raw::RawMutex; use crate::waitqueue::MultiWakerRegistration; pub mod publisher; diff --git a/embassy-sync/src/ring_buffer.rs b/embassy-sync/src/ring_buffer.rs index f03b7dd8f..608447cd6 100644 --- a/embassy-sync/src/ring_buffer.rs +++ b/embassy-sync/src/ring_buffer.rs @@ -95,11 +95,7 @@ impl RingBuffer { fn wrap(&self, n: usize) -> usize { assert!(n <= N); - if n == N { - 0 - } else { - n - } + if n == N { 0 } else { n } } } diff --git a/embassy-sync/src/rwlock.rs b/embassy-sync/src/rwlock.rs index e43388c4d..918a6aa41 100644 --- a/embassy-sync/src/rwlock.rs +++ b/embassy-sync/src/rwlock.rs @@ -3,12 +3,12 @@ //! This module provides a read-write lock that can be used to synchronize data between asynchronous tasks. use core::cell::{RefCell, UnsafeCell}; use core::fmt; -use core::future::{poll_fn, Future}; +use core::future::{Future, poll_fn}; use core::ops::{Deref, DerefMut}; use core::task::Poll; -use crate::blocking_mutex::raw::RawMutex; use crate::blocking_mutex::Mutex as BlockingMutex; +use crate::blocking_mutex::raw::RawMutex; use crate::waitqueue::WakerRegistration; /// Error returned by [`RwLock::try_read`] and [`RwLock::try_write`] when the lock is already held. diff --git a/embassy-sync/src/semaphore.rs b/embassy-sync/src/semaphore.rs index 4e82b0fcd..8d2413931 100644 --- a/embassy-sync/src/semaphore.rs +++ b/embassy-sync/src/semaphore.rs @@ -1,13 +1,13 @@ //! A synchronization primitive for controlling access to a pool of resources. use core::cell::{Cell, RefCell}; use core::convert::Infallible; -use core::future::{poll_fn, Future}; +use core::future::{Future, poll_fn}; use core::task::{Poll, Waker}; use heapless::Deque; -use crate::blocking_mutex::raw::RawMutex; use crate::blocking_mutex::Mutex; +use crate::blocking_mutex::raw::RawMutex; use crate::waitqueue::WakerRegistration; /// An asynchronous semaphore. diff --git a/embassy-sync/src/signal.rs b/embassy-sync/src/signal.rs index 229b1fa99..cc02228cf 100644 --- a/embassy-sync/src/signal.rs +++ b/embassy-sync/src/signal.rs @@ -1,10 +1,10 @@ //! A synchronization primitive for passing the latest value to a task. use core::cell::Cell; -use core::future::{poll_fn, Future}; +use core::future::{Future, poll_fn}; use core::task::{Context, Poll, Waker}; -use crate::blocking_mutex::raw::RawMutex; use crate::blocking_mutex::Mutex; +use crate::blocking_mutex::raw::RawMutex; /// Single-slot signaling primitive for a _single_ consumer. /// diff --git a/embassy-sync/src/waitqueue/atomic_waker.rs b/embassy-sync/src/waitqueue/atomic_waker.rs index 5a9910e7f..d2bf890e5 100644 --- a/embassy-sync/src/waitqueue/atomic_waker.rs +++ b/embassy-sync/src/waitqueue/atomic_waker.rs @@ -1,8 +1,8 @@ use core::cell::Cell; use core::task::Waker; -use crate::blocking_mutex::raw::{CriticalSectionRawMutex, RawMutex}; use crate::blocking_mutex::Mutex; +use crate::blocking_mutex::raw::{CriticalSectionRawMutex, RawMutex}; /// Utility struct to register and wake a waker. /// If a waker is registered, registering another waker will replace the previous one without waking it. diff --git a/embassy-sync/src/watch.rs b/embassy-sync/src/watch.rs index 332ab5405..0f8a8d679 100644 --- a/embassy-sync/src/watch.rs +++ b/embassy-sync/src/watch.rs @@ -1,13 +1,13 @@ //! A synchronization primitive for passing the latest value to **multiple** receivers. use core::cell::RefCell; -use core::future::{poll_fn, Future}; +use core::future::{Future, poll_fn}; use core::marker::PhantomData; use core::ops::{Deref, DerefMut}; use core::task::{Context, Poll}; -use crate::blocking_mutex::raw::RawMutex; use crate::blocking_mutex::Mutex; +use crate::blocking_mutex::raw::RawMutex; use crate::waitqueue::MultiWakerRegistration; /// The `Watch` is a single-slot signaling primitive that allows _multiple_ (`N`) receivers to concurrently await diff --git a/embassy-sync/src/zerocopy_channel.rs b/embassy-sync/src/zerocopy_channel.rs index b3f7dbe8c..c572592b8 100644 --- a/embassy-sync/src/zerocopy_channel.rs +++ b/embassy-sync/src/zerocopy_channel.rs @@ -15,12 +15,12 @@ //! another message will result in an error being returned. use core::cell::RefCell; -use core::future::{poll_fn, Future}; +use core::future::{Future, poll_fn}; use core::marker::PhantomData; use core::task::{Context, Poll}; -use crate::blocking_mutex::raw::RawMutex; use crate::blocking_mutex::Mutex; +use crate::blocking_mutex::raw::RawMutex; use crate::waitqueue::WakerRegistration; /// A bounded zero-copy channel for communicating between asynchronous tasks @@ -296,11 +296,7 @@ struct State { impl State { fn increment(&self, i: usize) -> usize { - if i + 1 == self.capacity { - 0 - } else { - i + 1 - } + if i + 1 == self.capacity { 0 } else { i + 1 } } fn clear(&mut self) { diff --git a/embassy-time-driver/Cargo.toml b/embassy-time-driver/Cargo.toml index 56ef3d15f..a52e82433 100644 --- a/embassy-time-driver/Cargo.toml +++ b/embassy-time-driver/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "embassy-time-driver" version = "0.2.1" -edition = "2021" +edition = "2024" description = "Driver trait for embassy-time" repository = "https://github.com/embassy-rs/embassy" documentation = "https://docs.embassy.dev/embassy-time-driver" diff --git a/embassy-time-driver/src/lib.rs b/embassy-time-driver/src/lib.rs index 44d9a156a..f3e8e0153 100644 --- a/embassy-time-driver/src/lib.rs +++ b/embassy-time-driver/src/lib.rs @@ -89,7 +89,7 @@ //! Instead of the usual "trait + generic params" approach, calls from embassy to the driver are done via `extern` functions. //! //! `embassy` internally defines the driver function as `extern "Rust" { fn _embassy_time_now() -> u64; }` and calls it. -//! The driver crate defines the function as `#[no_mangle] fn _embassy_time_now() -> u64`. The linker will resolve the +//! The driver crate defines the function as `#[unsafe(no_mangle)] fn _embassy_time_now() -> u64`. The linker will resolve the //! calls from the `embassy` crate to call into the driver crate. //! //! If there is none or multiple drivers in the crate tree, linking will fail. @@ -133,7 +133,7 @@ pub trait Driver: Send + Sync + 'static { fn schedule_wake(&self, at: u64, waker: &Waker); } -extern "Rust" { +unsafe extern "Rust" { fn _embassy_time_now() -> u64; fn _embassy_time_schedule_wake(at: u64, waker: &Waker); } @@ -158,13 +158,13 @@ macro_rules! time_driver_impl { (static $name:ident: $t: ty = $val:expr) => { static $name: $t = $val; - #[no_mangle] + #[unsafe(no_mangle)] #[inline] fn _embassy_time_now() -> u64 { <$t as $crate::Driver>::now(&$name) } - #[no_mangle] + #[unsafe(no_mangle)] #[inline] fn _embassy_time_schedule_wake(at: u64, waker: &core::task::Waker) { <$t as $crate::Driver>::schedule_wake(&$name, at, waker); diff --git a/embassy-time-queue-utils/Cargo.toml b/embassy-time-queue-utils/Cargo.toml index 13da62874..8da30c544 100644 --- a/embassy-time-queue-utils/Cargo.toml +++ b/embassy-time-queue-utils/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "embassy-time-queue-utils" version = "0.3.0" -edition = "2021" +edition = "2024" description = "Timer queue driver trait for embassy-time" repository = "https://github.com/embassy-rs/embassy" documentation = "https://docs.embassy.dev/embassy-time-queue-utils" diff --git a/embassy-time-queue-utils/src/queue_generic.rs b/embassy-time-queue-utils/src/queue_generic.rs index bff7a4735..88986953d 100644 --- a/embassy-time-queue-utils/src/queue_generic.rs +++ b/embassy-time-queue-utils/src/queue_generic.rs @@ -2,7 +2,7 @@ //! //! Time queue drivers may use this to simplify their implementation. -use core::cmp::{min, Ordering}; +use core::cmp::{Ordering, min}; use core::task::Waker; use heapless::Vec; diff --git a/embassy-time/Cargo.toml b/embassy-time/Cargo.toml index bad6ecf97..05614dbf5 100644 --- a/embassy-time/Cargo.toml +++ b/embassy-time/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "embassy-time" version = "0.5.0" -edition = "2021" +edition = "2024" description = "Instant and Duration for embedded no-std systems, with async timer support" repository = "https://github.com/embassy-rs/embassy" documentation = "https://docs.embassy.dev/embassy-time" diff --git a/embassy-time/src/duration.rs b/embassy-time/src/duration.rs index b3ea0468d..b2bfd6de9 100644 --- a/embassy-time/src/duration.rs +++ b/embassy-time/src/duration.rs @@ -175,13 +175,7 @@ impl Duration { /// NOTE: Giving this function a hz >= the TICK_HZ of your platform will clamp the Duration to 1 /// tick. Doing so will not deadlock, but will certainly not produce the desired output. pub const fn from_hz(hz: u64) -> Duration { - let ticks = { - if hz >= TICK_HZ { - 1 - } else { - (TICK_HZ + hz / 2) / hz - } - }; + let ticks = { if hz >= TICK_HZ { 1 } else { (TICK_HZ + hz / 2) / hz } }; Duration { ticks } } diff --git a/embassy-time/src/lib.rs b/embassy-time/src/lib.rs index 77f4b344d..e375fe93e 100644 --- a/embassy-time/src/lib.rs +++ b/embassy-time/src/lib.rs @@ -1,5 +1,6 @@ #![cfg_attr(not(any(feature = "std", feature = "wasm", test)), no_std)] #![allow(async_fn_in_trait)] +#![allow(unsafe_op_in_unsafe_fn)] #![doc = include_str!("../README.md")] #![allow(clippy::new_without_default)] #![warn(missing_docs)] @@ -27,18 +28,14 @@ mod driver_std; #[cfg(feature = "wasm")] mod driver_wasm; -pub use delay::{block_for, Delay}; +pub use delay::{Delay, block_for}; pub use duration::Duration; pub use embassy_time_driver::TICK_HZ; pub use instant::Instant; -pub use timer::{with_deadline, with_timeout, Ticker, TimeoutError, Timer, WithTimeout}; +pub use timer::{Ticker, TimeoutError, Timer, WithTimeout, with_deadline, with_timeout}; const fn gcd(a: u64, b: u64) -> u64 { - if b == 0 { - a - } else { - gcd(b, a % b) - } + if b == 0 { a } else { gcd(b, a % b) } } pub(crate) const GCD_1K: u64 = gcd(TICK_HZ, 1_000); diff --git a/embassy-time/src/timer.rs b/embassy-time/src/timer.rs index fcf79f58e..2f5967c63 100644 --- a/embassy-time/src/timer.rs +++ b/embassy-time/src/timer.rs @@ -1,9 +1,9 @@ -use core::future::{poll_fn, Future}; +use core::future::{Future, poll_fn}; use core::pin::Pin; use core::task::{Context, Poll}; -use futures_core::stream::FusedStream; use futures_core::Stream; +use futures_core::stream::FusedStream; use crate::{Duration, Instant}; diff --git a/embassy-usb-dfu/Cargo.toml b/embassy-usb-dfu/Cargo.toml index e70ab970e..8b32582c0 100644 --- a/embassy-usb-dfu/Cargo.toml +++ b/embassy-usb-dfu/Cargo.toml @@ -1,5 +1,5 @@ [package] -edition = "2021" +edition = "2024" name = "embassy-usb-dfu" version = "0.2.0" description = "An implementation of the USB DFU 1.1 protocol, using embassy-boot" diff --git a/embassy-usb-dfu/src/application.rs b/embassy-usb-dfu/src/application.rs index 78eb2c083..1ea1a74fe 100644 --- a/embassy-usb-dfu/src/application.rs +++ b/embassy-usb-dfu/src/application.rs @@ -6,11 +6,11 @@ use embassy_usb::driver::Driver; use embassy_usb::{Builder, FunctionBuilder, Handler}; use embedded_storage::nor_flash::NorFlash; +use crate::Reset; use crate::consts::{ - DfuAttributes, Request, State, Status, APPN_SPEC_SUBCLASS_DFU, DESC_DFU_FUNCTIONAL, DFU_PROTOCOL_RT, + APPN_SPEC_SUBCLASS_DFU, DESC_DFU_FUNCTIONAL, DFU_PROTOCOL_RT, DfuAttributes, Request, State, Status, USB_CLASS_APPN_SPEC, }; -use crate::Reset; /// Generic interface for a system that can signal to the bootloader that USB DFU mode is needed on the next boot. /// diff --git a/embassy-usb-dfu/src/dfu.rs b/embassy-usb-dfu/src/dfu.rs index 7c28d04cf..2ed4511ce 100644 --- a/embassy-usb-dfu/src/dfu.rs +++ b/embassy-usb-dfu/src/dfu.rs @@ -5,11 +5,11 @@ use embassy_usb::driver::Driver; use embassy_usb::{Builder, FunctionBuilder, Handler}; use embedded_storage::nor_flash::{NorFlash, NorFlashErrorKind}; +use crate::Reset; use crate::consts::{ - DfuAttributes, Request, State, Status, APPN_SPEC_SUBCLASS_DFU, DESC_DFU_FUNCTIONAL, DFU_PROTOCOL_DFU, + APPN_SPEC_SUBCLASS_DFU, DESC_DFU_FUNCTIONAL, DFU_PROTOCOL_DFU, DfuAttributes, Request, State, Status, USB_CLASS_APPN_SPEC, }; -use crate::Reset; /// Internal state for USB DFU pub struct Control<'d, DFU: NorFlash, STATE: NorFlash, RST: Reset, const BLOCK_SIZE: usize> { diff --git a/embassy-usb-driver/Cargo.toml b/embassy-usb-driver/Cargo.toml index 6e4c31273..3f43f60a3 100644 --- a/embassy-usb-driver/Cargo.toml +++ b/embassy-usb-driver/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "embassy-usb-driver" version = "0.2.0" -edition = "2021" +edition = "2024" license = "MIT OR Apache-2.0" description = "Driver trait for `embassy-usb`, an async USB device stack for embedded devices." keywords = ["embedded", "async", "usb", "hal", "embedded-hal"] diff --git a/embassy-usb-driver/src/lib.rs b/embassy-usb-driver/src/lib.rs index 59845a268..f19e0a401 100644 --- a/embassy-usb-driver/src/lib.rs +++ b/embassy-usb-driver/src/lib.rs @@ -1,5 +1,6 @@ #![no_std] #![allow(async_fn_in_trait)] +#![allow(unsafe_op_in_unsafe_fn)] #![doc = include_str!("../README.md")] #![warn(missing_docs)] diff --git a/embassy-usb-logger/Cargo.toml b/embassy-usb-logger/Cargo.toml index 6d13653bf..683821759 100644 --- a/embassy-usb-logger/Cargo.toml +++ b/embassy-usb-logger/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "embassy-usb-logger" version = "0.5.1" -edition = "2021" +edition = "2024" license = "MIT OR Apache-2.0" description = "`log` implementation for USB serial using `embassy-usb`." keywords = ["embedded", "log", "usb", "hal", "serial"] diff --git a/embassy-usb-synopsys-otg/Cargo.toml b/embassy-usb-synopsys-otg/Cargo.toml index 61b14a215..eca68095d 100644 --- a/embassy-usb-synopsys-otg/Cargo.toml +++ b/embassy-usb-synopsys-otg/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "embassy-usb-synopsys-otg" version = "0.3.1" -edition = "2021" +edition = "2024" license = "MIT OR Apache-2.0" description = "`embassy-usb-driver` implementation for Synopsys OTG USB controllers" keywords = ["embedded", "async", "usb", "hal", "embedded-hal"] diff --git a/embassy-usb-synopsys-otg/src/lib.rs b/embassy-usb-synopsys-otg/src/lib.rs index 6b4a87bdf..d94209f45 100644 --- a/embassy-usb-synopsys-otg/src/lib.rs +++ b/embassy-usb-synopsys-otg/src/lib.rs @@ -1,5 +1,6 @@ #![cfg_attr(not(test), no_std)] #![allow(async_fn_in_trait)] +#![allow(unsafe_op_in_unsafe_fn)] #![doc = include_str!("../README.md")] #![warn(missing_docs)] @@ -22,7 +23,7 @@ use crate::fmt::Bytes; pub mod otg_v1; -use otg_v1::{regs, vals, Otg}; +use otg_v1::{Otg, regs, vals}; /// Handle interrupts. pub unsafe fn on_interrupt(r: Otg, state: &State, ep_count: usize) { @@ -679,9 +680,7 @@ impl<'d, const MAX_EP_COUNT: usize> Bus<'d, MAX_EP_COUNT> { if let Some(ep) = self.ep_in[i] { trace!( "configuring tx fifo ep={}, offset={}, size={}", - i, - fifo_top, - ep.fifo_size_words + i, fifo_top, ep.fifo_size_words ); let dieptxf = if i == 0 { regs.dieptxf0() } else { regs.dieptxf(i - 1) }; @@ -1158,9 +1157,7 @@ impl<'d> embassy_usb_driver::EndpointIn for Endpoint<'d, In> { let dtxfsts = self.regs.dtxfsts(index).read(); trace!( "write ep={:?}: diepctl {:08x} ftxfsts {:08x}", - self.info.addr, - diepctl.0, - dtxfsts.0 + self.info.addr, diepctl.0, dtxfsts.0 ); if !diepctl.usbaep() { trace!("write ep={:?} wait for prev: error disabled", self.info.addr); @@ -1375,11 +1372,7 @@ fn ep_irq_mask(eps: &[Option]) -> u16 { eps.iter().enumerate().fold( 0, |mask, (index, ep)| { - if ep.is_some() { - mask | (1 << index) - } else { - mask - } + if ep.is_some() { mask | (1 << index) } else { mask } }, ) } diff --git a/embassy-usb/Cargo.toml b/embassy-usb/Cargo.toml index bff48c4a6..aeb7392f1 100644 --- a/embassy-usb/Cargo.toml +++ b/embassy-usb/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "embassy-usb" version = "0.5.1" -edition = "2021" +edition = "2024" license = "MIT OR Apache-2.0" description = "Async USB device stack for embedded devices in Rust." keywords = ["embedded", "async", "usb", "hal", "embedded-hal"] diff --git a/embassy-usb/src/builder.rs b/embassy-usb/src/builder.rs index 8d7abe46c..6fc2a5a22 100644 --- a/embassy-usb/src/builder.rs +++ b/embassy-usb/src/builder.rs @@ -5,7 +5,7 @@ use crate::descriptor::{BosWriter, DescriptorWriter, SynchronizationType, UsageT use crate::driver::{Driver, Endpoint, EndpointAddress, EndpointInfo, EndpointType}; use crate::msos::{DeviceLevelDescriptor, FunctionLevelDescriptor, MsOsDescriptorWriter}; use crate::types::{InterfaceNumber, StringIndex}; -use crate::{Handler, Interface, UsbDevice, MAX_INTERFACE_COUNT, STRING_INDEX_CUSTOM_START}; +use crate::{Handler, Interface, MAX_INTERFACE_COUNT, STRING_INDEX_CUSTOM_START, UsbDevice}; #[derive(Debug, Copy, Clone)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] @@ -176,7 +176,9 @@ impl<'d, D: Driver<'d>> Builder<'d, D> { if config.composite_with_iads && (config.device_class != 0xEF || config.device_sub_class != 0x02 || config.device_protocol != 0x01) { - panic!("if composite_with_iads is set, you must set device_class = 0xEF, device_sub_class = 0x02, device_protocol = 0x01"); + panic!( + "if composite_with_iads is set, you must set device_class = 0xEF, device_sub_class = 0x02, device_protocol = 0x01" + ); } assert!( @@ -337,7 +339,8 @@ impl<'a, 'd, D: Driver<'d>> FunctionBuilder<'a, 'd, D> { num_alt_settings: 0, }; - assert!(self.builder.interfaces.push(iface).is_ok(), + assert!( + self.builder.interfaces.push(iface).is_ok(), "embassy-usb: interface list full. Increase the `max_interface_count` compile-time setting. Current value: {}", MAX_INTERFACE_COUNT ); diff --git a/embassy-usb/src/class/cdc_acm.rs b/embassy-usb/src/class/cdc_acm.rs index 0a1a5e64f..388e21fbd 100644 --- a/embassy-usb/src/class/cdc_acm.rs +++ b/embassy-usb/src/class/cdc_acm.rs @@ -1,7 +1,7 @@ //! CDC-ACM class implementation, aka Serial over USB. use core::cell::{Cell, RefCell}; -use core::future::{poll_fn, Future}; +use core::future::{Future, poll_fn}; use core::mem::{self, MaybeUninit}; use core::sync::atomic::{AtomicBool, Ordering}; use core::task::Poll; diff --git a/embassy-usb/src/class/cdc_ncm/embassy_net.rs b/embassy-usb/src/class/cdc_ncm/embassy_net.rs index 57d322946..c83ff468a 100644 --- a/embassy-usb/src/class/cdc_ncm/embassy_net.rs +++ b/embassy-usb/src/class/cdc_ncm/embassy_net.rs @@ -1,6 +1,6 @@ //! [`embassy-net`](https://crates.io/crates/embassy-net) driver for the CDC-NCM class. -use embassy_futures::select::{select, Either}; +use embassy_futures::select::{Either, select}; use embassy_net_driver_channel as ch; use embassy_net_driver_channel::driver::LinkState; use embassy_usb_driver::Driver; diff --git a/embassy-usb/src/class/cdc_ncm/mod.rs b/embassy-usb/src/class/cdc_ncm/mod.rs index 3af853091..9b6dd9f21 100644 --- a/embassy-usb/src/class/cdc_ncm/mod.rs +++ b/embassy-usb/src/class/cdc_ncm/mod.rs @@ -14,7 +14,7 @@ //! This is due to regex spaghetti: //! and this nonsense in the linux kernel: -use core::mem::{size_of, MaybeUninit}; +use core::mem::{MaybeUninit, size_of}; use core::ptr::{addr_of, copy_nonoverlapping}; use crate::control::{self, InResponse, OutResponse, Recipient, Request, RequestType}; diff --git a/embassy-usb/src/class/cmsis_dap_v2.rs b/embassy-usb/src/class/cmsis_dap_v2.rs index a9fd9cdf0..0e2356f17 100644 --- a/embassy-usb/src/class/cmsis_dap_v2.rs +++ b/embassy-usb/src/class/cmsis_dap_v2.rs @@ -4,7 +4,7 @@ use core::mem::MaybeUninit; use crate::driver::{Driver, Endpoint, EndpointError, EndpointIn, EndpointOut}; use crate::types::StringIndex; -use crate::{msos, Builder, Handler}; +use crate::{Builder, Handler, msos}; /// State for the CMSIS-DAP v2 USB class. pub struct State { diff --git a/embassy-usb/src/class/midi.rs b/embassy-usb/src/class/midi.rs index 1d152ca44..d29172be1 100644 --- a/embassy-usb/src/class/midi.rs +++ b/embassy-usb/src/class/midi.rs @@ -1,7 +1,7 @@ //! MIDI class implementation. -use crate::driver::{Driver, Endpoint, EndpointError, EndpointIn, EndpointOut}; use crate::Builder; +use crate::driver::{Driver, Endpoint, EndpointError, EndpointIn, EndpointOut}; /// This should be used as `device_class` when building the `UsbDevice`. pub const USB_AUDIO_CLASS: u8 = 0x01; diff --git a/embassy-usb/src/class/uac1/speaker.rs b/embassy-usb/src/class/uac1/speaker.rs index 9565e2a25..46e0420d5 100644 --- a/embassy-usb/src/class/uac1/speaker.rs +++ b/embassy-usb/src/class/uac1/speaker.rs @@ -11,7 +11,7 @@ //! The class provides volume and mute controls for each channel. use core::cell::{Cell, RefCell}; -use core::future::{poll_fn, Future}; +use core::future::{Future, poll_fn}; use core::marker::PhantomData; use core::sync::atomic::{AtomicBool, AtomicU32, Ordering}; use core::task::Poll; @@ -22,7 +22,7 @@ use heapless::Vec; use super::class_codes::*; use super::terminal_type::TerminalType; -use super::{Channel, ChannelConfig, FeedbackRefresh, SampleWidth, MAX_AUDIO_CHANNEL_COUNT, MAX_AUDIO_CHANNEL_INDEX}; +use super::{Channel, ChannelConfig, FeedbackRefresh, MAX_AUDIO_CHANNEL_COUNT, MAX_AUDIO_CHANNEL_INDEX, SampleWidth}; use crate::control::{self, InResponse, OutResponse, Recipient, Request, RequestType}; use crate::descriptor::{SynchronizationType, UsageType}; use crate::driver::{Driver, Endpoint, EndpointError, EndpointIn, EndpointOut, EndpointType}; diff --git a/embassy-usb/src/descriptor.rs b/embassy-usb/src/descriptor.rs index e9a6fd79a..c79dd02eb 100644 --- a/embassy-usb/src/descriptor.rs +++ b/embassy-usb/src/descriptor.rs @@ -1,10 +1,10 @@ //! Utilities for writing USB descriptors. use embassy_usb_driver::EndpointType; +use crate::CONFIGURATION_VALUE; use crate::builder::Config; use crate::driver::EndpointInfo; use crate::types::{InterfaceNumber, StringIndex}; -use crate::CONFIGURATION_VALUE; /// Standard descriptor types #[allow(missing_docs)] diff --git a/embassy-usb/src/lib.rs b/embassy-usb/src/lib.rs index 0638fd0a2..0c10c08df 100644 --- a/embassy-usb/src/lib.rs +++ b/embassy-usb/src/lib.rs @@ -1,4 +1,5 @@ #![no_std] +#![allow(unsafe_op_in_unsafe_fn)] #![doc = include_str!("../README.md")] #![warn(missing_docs)] @@ -20,7 +21,7 @@ mod config { include!(concat!(env!("OUT_DIR"), "/config.rs")); } -use embassy_futures::select::{select, Either}; +use embassy_futures::select::{Either, select}; use heapless::Vec; pub use crate::builder::{Builder, Config, FunctionBuilder, InterfaceAltBuilder, InterfaceBuilder, UsbVersion}; diff --git a/embassy-usb/src/msos.rs b/embassy-usb/src/msos.rs index 9f4e1a57b..66689871e 100644 --- a/embassy-usb/src/msos.rs +++ b/embassy-usb/src/msos.rs @@ -4,7 +4,7 @@ use core::mem::size_of; -use crate::descriptor::{capability_type, BosWriter}; +use crate::descriptor::{BosWriter, capability_type}; use crate::types::InterfaceNumber; /// A serialized Microsoft OS 2.0 Descriptor set. diff --git a/examples/boot/application/nrf/Cargo.toml b/examples/boot/application/nrf/Cargo.toml index f5f89ecb5..55053bc33 100644 --- a/examples/boot/application/nrf/Cargo.toml +++ b/examples/boot/application/nrf/Cargo.toml @@ -1,5 +1,5 @@ [package] -edition = "2021" +edition = "2024" name = "embassy-boot-nrf-examples" version = "0.1.0" license = "MIT OR Apache-2.0" diff --git a/examples/boot/application/rp/Cargo.toml b/examples/boot/application/rp/Cargo.toml index d86386b00..70a2c28c3 100644 --- a/examples/boot/application/rp/Cargo.toml +++ b/examples/boot/application/rp/Cargo.toml @@ -1,5 +1,5 @@ [package] -edition = "2021" +edition = "2024" name = "embassy-boot-rp-examples" version = "0.1.0" license = "MIT OR Apache-2.0" diff --git a/examples/boot/application/stm32f3/Cargo.toml b/examples/boot/application/stm32f3/Cargo.toml index cd5f422fc..2dc75d939 100644 --- a/examples/boot/application/stm32f3/Cargo.toml +++ b/examples/boot/application/stm32f3/Cargo.toml @@ -1,5 +1,5 @@ [package] -edition = "2021" +edition = "2024" name = "embassy-boot-stm32f3-examples" version = "0.1.0" license = "MIT OR Apache-2.0" diff --git a/examples/boot/application/stm32f7/Cargo.toml b/examples/boot/application/stm32f7/Cargo.toml index c3921a166..5c372fb19 100644 --- a/examples/boot/application/stm32f7/Cargo.toml +++ b/examples/boot/application/stm32f7/Cargo.toml @@ -1,5 +1,5 @@ [package] -edition = "2021" +edition = "2024" name = "embassy-boot-stm32f7-examples" version = "0.1.0" license = "MIT OR Apache-2.0" diff --git a/examples/boot/application/stm32h7/Cargo.toml b/examples/boot/application/stm32h7/Cargo.toml index ca186d4d9..641a2ba96 100644 --- a/examples/boot/application/stm32h7/Cargo.toml +++ b/examples/boot/application/stm32h7/Cargo.toml @@ -1,5 +1,5 @@ [package] -edition = "2021" +edition = "2024" name = "embassy-boot-stm32h7-examples" version = "0.1.0" license = "MIT OR Apache-2.0" diff --git a/examples/boot/application/stm32l0/Cargo.toml b/examples/boot/application/stm32l0/Cargo.toml index be08956f1..4a168be15 100644 --- a/examples/boot/application/stm32l0/Cargo.toml +++ b/examples/boot/application/stm32l0/Cargo.toml @@ -1,5 +1,5 @@ [package] -edition = "2021" +edition = "2024" name = "embassy-boot-stm32l0-examples" version = "0.1.0" license = "MIT OR Apache-2.0" diff --git a/examples/boot/application/stm32l1/Cargo.toml b/examples/boot/application/stm32l1/Cargo.toml index 207eed733..af2cb3881 100644 --- a/examples/boot/application/stm32l1/Cargo.toml +++ b/examples/boot/application/stm32l1/Cargo.toml @@ -1,5 +1,5 @@ [package] -edition = "2021" +edition = "2024" name = "embassy-boot-stm32l1-examples" version = "0.1.0" license = "MIT OR Apache-2.0" diff --git a/examples/boot/application/stm32l4/Cargo.toml b/examples/boot/application/stm32l4/Cargo.toml index 22b9642d8..032e934aa 100644 --- a/examples/boot/application/stm32l4/Cargo.toml +++ b/examples/boot/application/stm32l4/Cargo.toml @@ -1,5 +1,5 @@ [package] -edition = "2021" +edition = "2024" name = "embassy-boot-stm32l4-examples" version = "0.1.0" license = "MIT OR Apache-2.0" diff --git a/examples/boot/application/stm32wb-dfu/Cargo.toml b/examples/boot/application/stm32wb-dfu/Cargo.toml index e2be4f470..ea4c26681 100644 --- a/examples/boot/application/stm32wb-dfu/Cargo.toml +++ b/examples/boot/application/stm32wb-dfu/Cargo.toml @@ -1,5 +1,5 @@ [package] -edition = "2021" +edition = "2024" name = "embassy-boot-stm32wb-dfu-examples" version = "0.1.0" license = "MIT OR Apache-2.0" diff --git a/examples/boot/application/stm32wb-dfu/src/main.rs b/examples/boot/application/stm32wb-dfu/src/main.rs index 5e7b71f5a..1ae28bf3a 100644 --- a/examples/boot/application/stm32wb-dfu/src/main.rs +++ b/examples/boot/application/stm32wb-dfu/src/main.rs @@ -13,9 +13,9 @@ use embassy_stm32::usb::{self, Driver}; use embassy_stm32::{bind_interrupts, peripherals}; use embassy_sync::blocking_mutex::Mutex; use embassy_time::Duration; -use embassy_usb::{msos, Builder}; +use embassy_usb::{Builder, msos}; use embassy_usb_dfu::consts::DfuAttributes; -use embassy_usb_dfu::{usb_dfu, Control, ResetImmediate}; +use embassy_usb_dfu::{Control, ResetImmediate, usb_dfu}; use panic_reset as _; bind_interrupts!(struct Irqs { diff --git a/examples/boot/application/stm32wba-dfu/Cargo.toml b/examples/boot/application/stm32wba-dfu/Cargo.toml index 6f4213b2c..d6f7dc3b6 100644 --- a/examples/boot/application/stm32wba-dfu/Cargo.toml +++ b/examples/boot/application/stm32wba-dfu/Cargo.toml @@ -1,5 +1,5 @@ [package] -edition = "2021" +edition = "2024" name = "embassy-boot-stm32wba-dfu-examples" version = "0.1.0" license = "MIT OR Apache-2.0" diff --git a/examples/boot/application/stm32wba-dfu/src/main.rs b/examples/boot/application/stm32wba-dfu/src/main.rs index bf17a7150..8adb2e7c0 100644 --- a/examples/boot/application/stm32wba-dfu/src/main.rs +++ b/examples/boot/application/stm32wba-dfu/src/main.rs @@ -12,9 +12,9 @@ use embassy_stm32::usb::{self, Driver}; use embassy_stm32::{bind_interrupts, peripherals}; use embassy_sync::blocking_mutex::Mutex; use embassy_time::Duration; -use embassy_usb::{msos, Builder}; +use embassy_usb::{Builder, msos}; use embassy_usb_dfu::consts::DfuAttributes; -use embassy_usb_dfu::{usb_dfu, Control, ResetImmediate}; +use embassy_usb_dfu::{Control, ResetImmediate, usb_dfu}; use panic_reset as _; bind_interrupts!(struct Irqs { diff --git a/examples/boot/application/stm32wl/Cargo.toml b/examples/boot/application/stm32wl/Cargo.toml index 8d1446ba9..c7fa811c9 100644 --- a/examples/boot/application/stm32wl/Cargo.toml +++ b/examples/boot/application/stm32wl/Cargo.toml @@ -1,5 +1,5 @@ [package] -edition = "2021" +edition = "2024" name = "embassy-boot-stm32wl-examples" version = "0.1.0" license = "MIT OR Apache-2.0" diff --git a/examples/boot/application/stm32wl/src/bin/a.rs b/examples/boot/application/stm32wl/src/bin/a.rs index e4526927f..3f381fd80 100644 --- a/examples/boot/application/stm32wl/src/bin/a.rs +++ b/examples/boot/application/stm32wl/src/bin/a.rs @@ -8,10 +8,10 @@ use defmt_rtt::*; use embassy_boot_stm32::{AlignedBuffer, FirmwareUpdater, FirmwareUpdaterConfig}; use embassy_embedded_hal::adapter::BlockingAsync; use embassy_executor::Spawner; +use embassy_stm32::SharedData; use embassy_stm32::exti::ExtiInput; use embassy_stm32::flash::{Flash, WRITE_SIZE}; use embassy_stm32::gpio::{Level, Output, Pull, Speed}; -use embassy_stm32::SharedData; use embassy_sync::mutex::Mutex; use panic_reset as _; diff --git a/examples/boot/application/stm32wl/src/bin/b.rs b/examples/boot/application/stm32wl/src/bin/b.rs index 6016a9555..952e94a58 100644 --- a/examples/boot/application/stm32wl/src/bin/b.rs +++ b/examples/boot/application/stm32wl/src/bin/b.rs @@ -6,8 +6,8 @@ use core::mem::MaybeUninit; #[cfg(feature = "defmt")] use defmt_rtt::*; use embassy_executor::Spawner; -use embassy_stm32::gpio::{Level, Output, Speed}; use embassy_stm32::SharedData; +use embassy_stm32::gpio::{Level, Output, Speed}; use embassy_time::Timer; use panic_reset as _; diff --git a/examples/boot/bootloader/nrf/Cargo.toml b/examples/boot/bootloader/nrf/Cargo.toml index 72b7114d4..1fea2b7d7 100644 --- a/examples/boot/bootloader/nrf/Cargo.toml +++ b/examples/boot/bootloader/nrf/Cargo.toml @@ -1,5 +1,5 @@ [package] -edition = "2021" +edition = "2024" name = "nrf-bootloader-example" version = "0.1.0" description = "Bootloader for nRF chips" diff --git a/examples/boot/bootloader/nrf/src/main.rs b/examples/boot/bootloader/nrf/src/main.rs index b849a0df3..76c4c1048 100644 --- a/examples/boot/bootloader/nrf/src/main.rs +++ b/examples/boot/bootloader/nrf/src/main.rs @@ -38,8 +38,8 @@ fn main() -> ! { unsafe { bl.load(active_offset) } } -#[no_mangle] -#[cfg_attr(target_os = "none", link_section = ".HardFault.user")] +#[unsafe(no_mangle)] +#[cfg_attr(target_os = "none", unsafe(link_section = ".HardFault.user"))] unsafe extern "C" fn HardFault() { cortex_m::peripheral::SCB::sys_reset(); } @@ -47,7 +47,7 @@ unsafe extern "C" fn HardFault() { #[exception] unsafe fn DefaultHandler(_: i16) -> ! { const SCB_ICSR: *const u32 = 0xE000_ED04 as *const u32; - let irqn = core::ptr::read_volatile(SCB_ICSR) as u8 as i16 - 16; + let irqn = unsafe { core::ptr::read_volatile(SCB_ICSR) } as u8 as i16 - 16; panic!("DefaultHandler #{:?}", irqn); } diff --git a/examples/boot/bootloader/rp/Cargo.toml b/examples/boot/bootloader/rp/Cargo.toml index 93a1c4edf..188bcab36 100644 --- a/examples/boot/bootloader/rp/Cargo.toml +++ b/examples/boot/bootloader/rp/Cargo.toml @@ -1,5 +1,5 @@ [package] -edition = "2021" +edition = "2024" name = "rp-bootloader-example" version = "0.1.0" description = "Example bootloader for RP2040 chips" diff --git a/examples/boot/bootloader/rp/src/main.rs b/examples/boot/bootloader/rp/src/main.rs index 25b1657b8..7ebefd374 100644 --- a/examples/boot/bootloader/rp/src/main.rs +++ b/examples/boot/bootloader/rp/src/main.rs @@ -34,8 +34,8 @@ fn main() -> ! { unsafe { bl.load(embassy_rp::flash::FLASH_BASE as u32 + active_offset) } } -#[no_mangle] -#[cfg_attr(target_os = "none", link_section = ".HardFault.user")] +#[unsafe(no_mangle)] +#[cfg_attr(target_os = "none", unsafe(link_section = ".HardFault.user"))] unsafe extern "C" fn HardFault() { cortex_m::peripheral::SCB::sys_reset(); } @@ -43,7 +43,7 @@ unsafe extern "C" fn HardFault() { #[exception] unsafe fn DefaultHandler(_: i16) -> ! { const SCB_ICSR: *const u32 = 0xE000_ED04 as *const u32; - let irqn = core::ptr::read_volatile(SCB_ICSR) as u8 as i16 - 16; + let irqn = unsafe { core::ptr::read_volatile(SCB_ICSR) } as u8 as i16 - 16; panic!("DefaultHandler #{:?}", irqn); } diff --git a/examples/boot/bootloader/stm32-dual-bank/Cargo.toml b/examples/boot/bootloader/stm32-dual-bank/Cargo.toml index 95ca20a59..cf68921dc 100644 --- a/examples/boot/bootloader/stm32-dual-bank/Cargo.toml +++ b/examples/boot/bootloader/stm32-dual-bank/Cargo.toml @@ -1,5 +1,5 @@ [package] -edition = "2021" +edition = "2024" name = "stm32-bootloader-dual-bank-flash-example" version = "0.1.0" description = "Example bootloader for dual-bank flash STM32 chips" diff --git a/examples/boot/bootloader/stm32-dual-bank/src/main.rs b/examples/boot/bootloader/stm32-dual-bank/src/main.rs index 4d2e82d26..f0063fb5c 100644 --- a/examples/boot/bootloader/stm32-dual-bank/src/main.rs +++ b/examples/boot/bootloader/stm32-dual-bank/src/main.rs @@ -7,7 +7,7 @@ use cortex_m_rt::{entry, exception}; #[cfg(feature = "defmt")] use defmt_rtt as _; use embassy_boot_stm32::*; -use embassy_stm32::flash::{Flash, BANK1_REGION}; +use embassy_stm32::flash::{BANK1_REGION, Flash}; use embassy_sync::blocking_mutex::Mutex; #[entry] @@ -33,8 +33,8 @@ fn main() -> ! { unsafe { bl.load(BANK1_REGION.base + active_offset) } } -#[no_mangle] -#[cfg_attr(target_os = "none", link_section = ".HardFault.user")] +#[unsafe(no_mangle)] +#[cfg_attr(target_os = "none", unsafe(link_section = ".HardFault.user"))] unsafe extern "C" fn HardFault() { cortex_m::peripheral::SCB::sys_reset(); } @@ -42,7 +42,7 @@ unsafe extern "C" fn HardFault() { #[exception] unsafe fn DefaultHandler(_: i16) -> ! { const SCB_ICSR: *const u32 = 0xE000_ED04 as *const u32; - let irqn = core::ptr::read_volatile(SCB_ICSR) as u8 as i16 - 16; + let irqn = unsafe { core::ptr::read_volatile(SCB_ICSR) } as u8 as i16 - 16; panic!("DefaultHandler #{:?}", irqn); } diff --git a/examples/boot/bootloader/stm32/Cargo.toml b/examples/boot/bootloader/stm32/Cargo.toml index 526637f37..e457310b9 100644 --- a/examples/boot/bootloader/stm32/Cargo.toml +++ b/examples/boot/bootloader/stm32/Cargo.toml @@ -1,5 +1,5 @@ [package] -edition = "2021" +edition = "2024" name = "stm32-bootloader-example" version = "0.1.0" description = "Example bootloader for STM32 chips" diff --git a/examples/boot/bootloader/stm32/src/main.rs b/examples/boot/bootloader/stm32/src/main.rs index 99a7a6a6b..383ad912d 100644 --- a/examples/boot/bootloader/stm32/src/main.rs +++ b/examples/boot/bootloader/stm32/src/main.rs @@ -7,7 +7,7 @@ use cortex_m_rt::{entry, exception}; #[cfg(feature = "defmt")] use defmt_rtt as _; use embassy_boot_stm32::*; -use embassy_stm32::flash::{Flash, BANK1_REGION}; +use embassy_stm32::flash::{BANK1_REGION, Flash}; use embassy_sync::blocking_mutex::Mutex; #[entry] @@ -32,8 +32,8 @@ fn main() -> ! { unsafe { bl.load(BANK1_REGION.base + active_offset) } } -#[no_mangle] -#[cfg_attr(target_os = "none", link_section = ".HardFault.user")] +#[unsafe(no_mangle)] +#[cfg_attr(target_os = "none", unsafe(link_section = ".HardFault.user"))] unsafe extern "C" fn HardFault() { cortex_m::peripheral::SCB::sys_reset(); } @@ -41,7 +41,7 @@ unsafe extern "C" fn HardFault() { #[exception] unsafe fn DefaultHandler(_: i16) -> ! { const SCB_ICSR: *const u32 = 0xE000_ED04 as *const u32; - let irqn = core::ptr::read_volatile(SCB_ICSR) as u8 as i16 - 16; + let irqn = unsafe { core::ptr::read_volatile(SCB_ICSR) } as u8 as i16 - 16; panic!("DefaultHandler #{:?}", irqn); } diff --git a/examples/boot/bootloader/stm32wb-dfu/Cargo.toml b/examples/boot/bootloader/stm32wb-dfu/Cargo.toml index ef10aeabf..75b7081df 100644 --- a/examples/boot/bootloader/stm32wb-dfu/Cargo.toml +++ b/examples/boot/bootloader/stm32wb-dfu/Cargo.toml @@ -1,5 +1,5 @@ [package] -edition = "2021" +edition = "2024" name = "stm32wb-dfu-bootloader-example" version = "0.1.0" description = "Example USB DFUbootloader for the STM32WB series of chips" diff --git a/examples/boot/bootloader/stm32wb-dfu/src/main.rs b/examples/boot/bootloader/stm32wb-dfu/src/main.rs index 107f243fd..9ee82846d 100644 --- a/examples/boot/bootloader/stm32wb-dfu/src/main.rs +++ b/examples/boot/bootloader/stm32wb-dfu/src/main.rs @@ -7,14 +7,14 @@ use cortex_m_rt::{entry, exception}; #[cfg(feature = "defmt")] use defmt_rtt as _; use embassy_boot_stm32::*; -use embassy_stm32::flash::{Flash, BANK1_REGION, WRITE_SIZE}; +use embassy_stm32::flash::{BANK1_REGION, Flash, WRITE_SIZE}; use embassy_stm32::rcc::WPAN_DEFAULT; use embassy_stm32::usb::Driver; use embassy_stm32::{bind_interrupts, peripherals, usb}; use embassy_sync::blocking_mutex::Mutex; -use embassy_usb::{msos, Builder}; +use embassy_usb::{Builder, msos}; use embassy_usb_dfu::consts::DfuAttributes; -use embassy_usb_dfu::{usb_dfu, Control, ResetImmediate}; +use embassy_usb_dfu::{Control, ResetImmediate, usb_dfu}; bind_interrupts!(struct Irqs { USB_LP => usb::InterruptHandler; @@ -109,8 +109,8 @@ fn main() -> ! { unsafe { bl.load(BANK1_REGION.base + active_offset) } } -#[no_mangle] -#[cfg_attr(target_os = "none", link_section = ".HardFault.user")] +#[unsafe(no_mangle)] +#[cfg_attr(target_os = "none", unsafe(link_section = ".HardFault.user"))] unsafe extern "C" fn HardFault() { cortex_m::peripheral::SCB::sys_reset(); } @@ -118,7 +118,7 @@ unsafe extern "C" fn HardFault() { #[exception] unsafe fn DefaultHandler(_: i16) -> ! { const SCB_ICSR: *const u32 = 0xE000_ED04 as *const u32; - let irqn = core::ptr::read_volatile(SCB_ICSR) as u8 as i16 - 16; + let irqn = unsafe { core::ptr::read_volatile(SCB_ICSR) } as u8 as i16 - 16; panic!("DefaultHandler #{:?}", irqn); } diff --git a/examples/boot/bootloader/stm32wba-dfu/Cargo.toml b/examples/boot/bootloader/stm32wba-dfu/Cargo.toml index 16de7684e..eee2b2f71 100644 --- a/examples/boot/bootloader/stm32wba-dfu/Cargo.toml +++ b/examples/boot/bootloader/stm32wba-dfu/Cargo.toml @@ -1,5 +1,5 @@ [package] -edition = "2021" +edition = "2024" name = "stm32wba6-dfu-bootloader-example" version = "0.1.0" description = "Example USB DFUbootloader for the STM32WBA series of chips" diff --git a/examples/boot/bootloader/stm32wba-dfu/src/main.rs b/examples/boot/bootloader/stm32wba-dfu/src/main.rs index 75d8d4199..b33a75d95 100644 --- a/examples/boot/bootloader/stm32wba-dfu/src/main.rs +++ b/examples/boot/bootloader/stm32wba-dfu/src/main.rs @@ -7,13 +7,13 @@ use cortex_m_rt::{entry, exception}; #[cfg(feature = "defmt")] use defmt_rtt as _; use embassy_boot_stm32::*; -use embassy_stm32::flash::{Flash, BANK1_REGION, WRITE_SIZE}; +use embassy_stm32::flash::{BANK1_REGION, Flash, WRITE_SIZE}; use embassy_stm32::usb::Driver; -use embassy_stm32::{bind_interrupts, peripherals, usb, Config}; +use embassy_stm32::{Config, bind_interrupts, peripherals, usb}; use embassy_sync::blocking_mutex::Mutex; -use embassy_usb::{msos, Builder}; +use embassy_usb::{Builder, msos}; use embassy_usb_dfu::consts::DfuAttributes; -use embassy_usb_dfu::{usb_dfu, Control, ResetImmediate}; +use embassy_usb_dfu::{Control, ResetImmediate, usb_dfu}; bind_interrupts!(struct Irqs { USB_OTG_HS => usb::InterruptHandler; @@ -138,8 +138,8 @@ fn main() -> ! { unsafe { bl.load(BANK1_REGION.base + active_offset) } } -#[no_mangle] -#[cfg_attr(target_os = "none", link_section = ".HardFault.user")] +#[unsafe(no_mangle)] +#[cfg_attr(target_os = "none", unsafe(link_section = ".HardFault.user"))] unsafe extern "C" fn HardFault() { cortex_m::peripheral::SCB::sys_reset(); } @@ -147,7 +147,7 @@ unsafe extern "C" fn HardFault() { #[exception] unsafe fn DefaultHandler(_: i16) -> ! { const SCB_ICSR: *const u32 = 0xE000_ED04 as *const u32; - let irqn = core::ptr::read_volatile(SCB_ICSR) as u8 as i16 - 16; + let irqn = unsafe { core::ptr::read_volatile(SCB_ICSR) } as u8 as i16 - 16; panic!("DefaultHandler #{:?}", irqn); } diff --git a/examples/lpc55s69/Cargo.toml b/examples/lpc55s69/Cargo.toml index 579748595..94903b3f8 100644 --- a/examples/lpc55s69/Cargo.toml +++ b/examples/lpc55s69/Cargo.toml @@ -1,5 +1,5 @@ [package] -edition = "2021" +edition = "2024" name = "embassy-nxp-lpc55s69-examples" version = "0.1.0" license = "MIT OR Apache-2.0" diff --git a/examples/mimxrt1011/Cargo.toml b/examples/mimxrt1011/Cargo.toml index 3038f5d4d..d784ce729 100644 --- a/examples/mimxrt1011/Cargo.toml +++ b/examples/mimxrt1011/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "embassy-imxrt1011-examples" version = "0.1.0" -edition = "2021" +edition = "2024" license = "MIT OR Apache-2.0" publish = false diff --git a/examples/mimxrt1011/src/lib.rs b/examples/mimxrt1011/src/lib.rs index f0391ef57..36d3e2fb3 100644 --- a/examples/mimxrt1011/src/lib.rs +++ b/examples/mimxrt1011/src/lib.rs @@ -71,5 +71,5 @@ pub const SERIAL_NOR_CONFIGURATION_BLOCK: nor::ConfigurationBlock = .ip_cmd_serial_clk_freq(nor::SerialClockFrequency::MHz30); #[unsafe(no_mangle)] -#[cfg_attr(all(target_arch = "arm", target_os = "none"), link_section = ".fcb")] +#[cfg_attr(all(target_arch = "arm", target_os = "none"), unsafe(link_section = ".fcb"))] pub static FLEXSPI_CONFIGURATION_BLOCK: nor::ConfigurationBlock = SERIAL_NOR_CONFIGURATION_BLOCK; diff --git a/examples/mimxrt1062-evk/Cargo.toml b/examples/mimxrt1062-evk/Cargo.toml index 82a24490d..29a80db12 100644 --- a/examples/mimxrt1062-evk/Cargo.toml +++ b/examples/mimxrt1062-evk/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "embassy-imxrt1062-evk-examples" version = "0.1.0" -edition = "2021" +edition = "2024" license = "MIT OR Apache-2.0" publish = false diff --git a/examples/mimxrt1062-evk/src/lib.rs b/examples/mimxrt1062-evk/src/lib.rs index 3f99f9db3..e952b91ec 100644 --- a/examples/mimxrt1062-evk/src/lib.rs +++ b/examples/mimxrt1062-evk/src/lib.rs @@ -55,6 +55,6 @@ pub const SERIAL_NOR_CONFIGURATION_BLOCK: nor::ConfigurationBlock = .sector_size(4096) .ip_cmd_serial_clk_freq(nor::SerialClockFrequency::MHz30); -#[no_mangle] -#[cfg_attr(all(target_arch = "arm", target_os = "none"), link_section = ".fcb")] +#[unsafe(no_mangle)] +#[cfg_attr(all(target_arch = "arm", target_os = "none"), unsafe(link_section = ".fcb"))] pub static FLEXSPI_CONFIGURATION_BLOCK: nor::ConfigurationBlock = SERIAL_NOR_CONFIGURATION_BLOCK; diff --git a/examples/mimxrt6/Cargo.toml b/examples/mimxrt6/Cargo.toml index 3f7ad8485..dc09e97e7 100644 --- a/examples/mimxrt6/Cargo.toml +++ b/examples/mimxrt6/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "embassy-imxrt-examples" version = "0.1.0" -edition = "2021" +edition = "2024" license = "MIT OR Apache-2.0" publish = false diff --git a/examples/mimxrt6/src/bin/button.rs b/examples/mimxrt6/src/bin/button.rs index efb7f14af..a9bdde98e 100644 --- a/examples/mimxrt6/src/bin/button.rs +++ b/examples/mimxrt6/src/bin/button.rs @@ -3,7 +3,7 @@ use defmt::info; use embassy_executor::Spawner; -use embassy_futures::select::{select, Either}; +use embassy_futures::select::{Either, select}; use embassy_imxrt::gpio; use {defmt_rtt as _, embassy_imxrt_examples as _, panic_probe as _}; diff --git a/examples/mspm0c1104/Cargo.toml b/examples/mspm0c1104/Cargo.toml index 21434106a..74301bc9c 100644 --- a/examples/mspm0c1104/Cargo.toml +++ b/examples/mspm0c1104/Cargo.toml @@ -1,5 +1,5 @@ [package] -edition = "2021" +edition = "2024" name = "embassy-mspm0-c1104-examples" version = "0.1.0" license = "MIT OR Apache-2.0" diff --git a/examples/mspm0c1104/src/bin/blinky.rs b/examples/mspm0c1104/src/bin/blinky.rs index 0d974cc5e..345077b37 100644 --- a/examples/mspm0c1104/src/bin/blinky.rs +++ b/examples/mspm0c1104/src/bin/blinky.rs @@ -3,8 +3,8 @@ use defmt::*; use embassy_executor::Spawner; -use embassy_mspm0::gpio::{Level, Output}; use embassy_mspm0::Config; +use embassy_mspm0::gpio::{Level, Output}; use embassy_time::Timer; use {defmt_rtt as _, panic_halt as _}; diff --git a/examples/mspm0c1104/src/bin/button.rs b/examples/mspm0c1104/src/bin/button.rs index 7face1618..557d997cd 100644 --- a/examples/mspm0c1104/src/bin/button.rs +++ b/examples/mspm0c1104/src/bin/button.rs @@ -3,8 +3,8 @@ use defmt::*; use embassy_executor::Spawner; -use embassy_mspm0::gpio::{Input, Level, Output, Pull}; use embassy_mspm0::Config; +use embassy_mspm0::gpio::{Input, Level, Output, Pull}; use {defmt_rtt as _, panic_halt as _}; #[embassy_executor::main] diff --git a/examples/mspm0g3507/Cargo.toml b/examples/mspm0g3507/Cargo.toml index 616b82adb..8c230f038 100644 --- a/examples/mspm0g3507/Cargo.toml +++ b/examples/mspm0g3507/Cargo.toml @@ -1,5 +1,5 @@ [package] -edition = "2021" +edition = "2024" name = "embassy-mspm0-g3507-examples" version = "0.1.0" license = "MIT OR Apache-2.0" diff --git a/examples/mspm0g3507/src/bin/adc.rs b/examples/mspm0g3507/src/bin/adc.rs index ceccc7c02..cf1abb471 100644 --- a/examples/mspm0g3507/src/bin/adc.rs +++ b/examples/mspm0g3507/src/bin/adc.rs @@ -4,7 +4,7 @@ use defmt::*; use embassy_executor::Spawner; use embassy_mspm0::adc::{self, Adc, Vrsel}; -use embassy_mspm0::{bind_interrupts, peripherals, Config}; +use embassy_mspm0::{Config, bind_interrupts, peripherals}; use embassy_time::Timer; use {defmt_rtt as _, panic_halt as _}; diff --git a/examples/mspm0g3507/src/bin/blinky.rs b/examples/mspm0g3507/src/bin/blinky.rs index 055a5cd81..47eaf1535 100644 --- a/examples/mspm0g3507/src/bin/blinky.rs +++ b/examples/mspm0g3507/src/bin/blinky.rs @@ -3,8 +3,8 @@ use defmt::*; use embassy_executor::Spawner; -use embassy_mspm0::gpio::{Level, Output}; use embassy_mspm0::Config; +use embassy_mspm0::gpio::{Level, Output}; use embassy_time::Timer; use {defmt_rtt as _, panic_halt as _}; diff --git a/examples/mspm0g3507/src/bin/button.rs b/examples/mspm0g3507/src/bin/button.rs index cde1f2892..76f3a1aba 100644 --- a/examples/mspm0g3507/src/bin/button.rs +++ b/examples/mspm0g3507/src/bin/button.rs @@ -3,8 +3,8 @@ use defmt::*; use embassy_executor::Spawner; -use embassy_mspm0::gpio::{Input, Level, Output, Pull}; use embassy_mspm0::Config; +use embassy_mspm0::gpio::{Input, Level, Output, Pull}; use {defmt_rtt as _, panic_halt as _}; #[embassy_executor::main] diff --git a/examples/mspm0g3507/src/bin/i2c_target.rs b/examples/mspm0g3507/src/bin/i2c_target.rs new file mode 100644 index 000000000..5dd718eaf --- /dev/null +++ b/examples/mspm0g3507/src/bin/i2c_target.rs @@ -0,0 +1,63 @@ +//! Example of using async I2C target +//! +//! This uses the virtual COM port provided on the LP-MSPM0G3507 board. + +#![no_std] +#![no_main] + +use defmt::*; +use embassy_executor::Spawner; +use embassy_mspm0::i2c::Config; +use embassy_mspm0::i2c_target::{Command, Config as TargetConfig, I2cTarget, ReadStatus}; +use embassy_mspm0::peripherals::I2C1; +use embassy_mspm0::{bind_interrupts, i2c}; +use {defmt_rtt as _, panic_halt as _}; + +bind_interrupts!(struct Irqs { + I2C1 => i2c::InterruptHandler; +}); + +#[embassy_executor::main] +async fn main(_spawner: Spawner) -> ! { + let p = embassy_mspm0::init(Default::default()); + + let instance = p.I2C1; + let scl = p.PB2; + let sda = p.PB3; + + let config = Config::default(); + let mut target_config = TargetConfig::default(); + target_config.target_addr = 0x48; + target_config.general_call = true; + let mut i2c = I2cTarget::new(instance, scl, sda, Irqs, config, target_config).unwrap(); + + let mut read = [0u8; 8]; + let data = [8u8; 2]; + let data_wr = [9u8; 2]; + + loop { + match i2c.listen(&mut read).await { + Ok(Command::GeneralCall(_)) => info!("General call received"), + Ok(Command::Read) => { + info!("Read command received"); + match i2c.respond_to_read(&data).await.unwrap() { + ReadStatus::Done => info!("Finished reading"), + ReadStatus::NeedMoreBytes => { + info!("Read needs more bytes - will reset"); + i2c.reset().unwrap(); + } + ReadStatus::LeftoverBytes(_) => { + info!("Leftover bytes received"); + i2c.flush_tx_fifo(); + } + } + } + Ok(Command::Write(_)) => info!("Write command received"), + Ok(Command::WriteRead(_)) => { + info!("Write-Read command received"); + i2c.respond_and_fill(&data_wr, 0xFE).await.unwrap(); + } + Err(e) => info!("Got error {}", e), + } + } +} diff --git a/examples/mspm0g3519/Cargo.toml b/examples/mspm0g3519/Cargo.toml index ae699d6f4..0f5e58343 100644 --- a/examples/mspm0g3519/Cargo.toml +++ b/examples/mspm0g3519/Cargo.toml @@ -1,5 +1,5 @@ [package] -edition = "2021" +edition = "2024" name = "embassy-mspm0-g3519-examples" version = "0.1.0" license = "MIT OR Apache-2.0" diff --git a/examples/mspm0g3519/src/bin/blinky.rs b/examples/mspm0g3519/src/bin/blinky.rs index 055a5cd81..47eaf1535 100644 --- a/examples/mspm0g3519/src/bin/blinky.rs +++ b/examples/mspm0g3519/src/bin/blinky.rs @@ -3,8 +3,8 @@ use defmt::*; use embassy_executor::Spawner; -use embassy_mspm0::gpio::{Level, Output}; use embassy_mspm0::Config; +use embassy_mspm0::gpio::{Level, Output}; use embassy_time::Timer; use {defmt_rtt as _, panic_halt as _}; diff --git a/examples/mspm0g3519/src/bin/button.rs b/examples/mspm0g3519/src/bin/button.rs index c81cc2918..21e7873d8 100644 --- a/examples/mspm0g3519/src/bin/button.rs +++ b/examples/mspm0g3519/src/bin/button.rs @@ -3,8 +3,8 @@ use defmt::*; use embassy_executor::Spawner; -use embassy_mspm0::gpio::{Input, Level, Output, Pull}; use embassy_mspm0::Config; +use embassy_mspm0::gpio::{Input, Level, Output, Pull}; use {defmt_rtt as _, panic_halt as _}; #[embassy_executor::main] diff --git a/examples/mspm0l1306/Cargo.toml b/examples/mspm0l1306/Cargo.toml index 8100e11da..d5b5e9d3e 100644 --- a/examples/mspm0l1306/Cargo.toml +++ b/examples/mspm0l1306/Cargo.toml @@ -1,5 +1,5 @@ [package] -edition = "2021" +edition = "2024" name = "embassy-mspm0-l1306-examples" version = "0.1.0" license = "MIT OR Apache-2.0" diff --git a/examples/mspm0l1306/src/bin/adc.rs b/examples/mspm0l1306/src/bin/adc.rs index 2806b98cc..235396b8a 100644 --- a/examples/mspm0l1306/src/bin/adc.rs +++ b/examples/mspm0l1306/src/bin/adc.rs @@ -4,7 +4,7 @@ use defmt::*; use embassy_executor::Spawner; use embassy_mspm0::adc::{self, Adc, Vrsel}; -use embassy_mspm0::{bind_interrupts, peripherals, Config}; +use embassy_mspm0::{Config, bind_interrupts, peripherals}; use embassy_time::Timer; use {defmt_rtt as _, panic_halt as _}; diff --git a/examples/mspm0l1306/src/bin/blinky.rs b/examples/mspm0l1306/src/bin/blinky.rs index 055a5cd81..47eaf1535 100644 --- a/examples/mspm0l1306/src/bin/blinky.rs +++ b/examples/mspm0l1306/src/bin/blinky.rs @@ -3,8 +3,8 @@ use defmt::*; use embassy_executor::Spawner; -use embassy_mspm0::gpio::{Level, Output}; use embassy_mspm0::Config; +use embassy_mspm0::gpio::{Level, Output}; use embassy_time::Timer; use {defmt_rtt as _, panic_halt as _}; diff --git a/examples/mspm0l1306/src/bin/button.rs b/examples/mspm0l1306/src/bin/button.rs index d8c85947f..33e682272 100644 --- a/examples/mspm0l1306/src/bin/button.rs +++ b/examples/mspm0l1306/src/bin/button.rs @@ -3,8 +3,8 @@ use defmt::*; use embassy_executor::Spawner; -use embassy_mspm0::gpio::{Input, Level, Output, Pull}; use embassy_mspm0::Config; +use embassy_mspm0::gpio::{Input, Level, Output, Pull}; use {defmt_rtt as _, panic_halt as _}; #[embassy_executor::main] diff --git a/examples/mspm0l1306/src/bin/i2c_target.rs b/examples/mspm0l1306/src/bin/i2c_target.rs new file mode 100644 index 000000000..4d147d08b --- /dev/null +++ b/examples/mspm0l1306/src/bin/i2c_target.rs @@ -0,0 +1,63 @@ +//! Example of using async I2C target +//! +//! This uses the virtual COM port provided on the LP-MSPM0L1306 board. + +#![no_std] +#![no_main] + +use defmt::*; +use embassy_executor::Spawner; +use embassy_mspm0::i2c::Config; +use embassy_mspm0::i2c_target::{Command, Config as TargetConfig, I2cTarget, ReadStatus}; +use embassy_mspm0::peripherals::I2C0; +use embassy_mspm0::{bind_interrupts, i2c}; +use {defmt_rtt as _, panic_halt as _}; + +bind_interrupts!(struct Irqs { + I2C0 => i2c::InterruptHandler; +}); + +#[embassy_executor::main] +async fn main(_spawner: Spawner) -> ! { + let p = embassy_mspm0::init(Default::default()); + + let instance = p.I2C0; + let scl = p.PA1; + let sda = p.PA0; + + let config = Config::default(); + let mut target_config = TargetConfig::default(); + target_config.target_addr = 0x48; + target_config.general_call = true; + let mut i2c = I2cTarget::new(instance, scl, sda, Irqs, config, target_config).unwrap(); + + let mut read = [0u8; 8]; + let data = [8u8; 2]; + let data_wr = [9u8; 2]; + + loop { + match i2c.listen(&mut read).await { + Ok(Command::GeneralCall(_)) => info!("General call received"), + Ok(Command::Read) => { + info!("Read command received"); + match i2c.respond_to_read(&data).await.unwrap() { + ReadStatus::Done => info!("Finished reading"), + ReadStatus::NeedMoreBytes => { + info!("Read needs more bytes - will reset"); + i2c.reset().unwrap(); + } + ReadStatus::LeftoverBytes(_) => { + info!("Leftover bytes received"); + i2c.flush_tx_fifo(); + } + } + } + Ok(Command::Write(_)) => info!("Write command received"), + Ok(Command::WriteRead(_)) => { + info!("Write-Read command received"); + i2c.respond_and_fill(&data_wr, 0xFE).await.unwrap(); + } + Err(e) => info!("Got error {}", e), + } + } +} diff --git a/examples/mspm0l2228/Cargo.toml b/examples/mspm0l2228/Cargo.toml index 3add7b8e8..1d27ae64a 100644 --- a/examples/mspm0l2228/Cargo.toml +++ b/examples/mspm0l2228/Cargo.toml @@ -1,5 +1,5 @@ [package] -edition = "2021" +edition = "2024" name = "embassy-mspm0-l2228-examples" version = "0.1.0" license = "MIT OR Apache-2.0" diff --git a/examples/mspm0l2228/src/bin/blinky.rs b/examples/mspm0l2228/src/bin/blinky.rs index 055a5cd81..47eaf1535 100644 --- a/examples/mspm0l2228/src/bin/blinky.rs +++ b/examples/mspm0l2228/src/bin/blinky.rs @@ -3,8 +3,8 @@ use defmt::*; use embassy_executor::Spawner; -use embassy_mspm0::gpio::{Level, Output}; use embassy_mspm0::Config; +use embassy_mspm0::gpio::{Level, Output}; use embassy_time::Timer; use {defmt_rtt as _, panic_halt as _}; diff --git a/examples/mspm0l2228/src/bin/button.rs b/examples/mspm0l2228/src/bin/button.rs index 47bfd274b..bad1cb138 100644 --- a/examples/mspm0l2228/src/bin/button.rs +++ b/examples/mspm0l2228/src/bin/button.rs @@ -3,8 +3,8 @@ use defmt::*; use embassy_executor::Spawner; -use embassy_mspm0::gpio::{Input, Level, Output, Pull}; use embassy_mspm0::Config; +use embassy_mspm0::gpio::{Input, Level, Output, Pull}; use {defmt_rtt as _, panic_halt as _}; #[embassy_executor::main] diff --git a/examples/nrf-rtos-trace/Cargo.toml b/examples/nrf-rtos-trace/Cargo.toml index d2d0ae093..5caabf228 100644 --- a/examples/nrf-rtos-trace/Cargo.toml +++ b/examples/nrf-rtos-trace/Cargo.toml @@ -1,5 +1,5 @@ [package] -edition = "2021" +edition = "2024" name = "embassy-nrf-rtos-trace-examples" version = "0.1.0" license = "MIT OR Apache-2.0" diff --git a/examples/nrf51/Cargo.toml b/examples/nrf51/Cargo.toml index 082d85e5b..c7492f562 100644 --- a/examples/nrf51/Cargo.toml +++ b/examples/nrf51/Cargo.toml @@ -1,5 +1,5 @@ [package] -edition = "2021" +edition = "2024" name = "embassy-nrf51-examples" version = "0.1.0" license = "MIT OR Apache-2.0" diff --git a/examples/nrf52810/Cargo.toml b/examples/nrf52810/Cargo.toml index 7835320e5..1711a3d8d 100644 --- a/examples/nrf52810/Cargo.toml +++ b/examples/nrf52810/Cargo.toml @@ -1,5 +1,5 @@ [package] -edition = "2021" +edition = "2024" name = "embassy-nrf52810-examples" version = "0.1.0" license = "MIT OR Apache-2.0" diff --git a/examples/nrf52840-edf/Cargo.toml b/examples/nrf52840-edf/Cargo.toml index 67a624d6d..8b1db4652 100644 --- a/examples/nrf52840-edf/Cargo.toml +++ b/examples/nrf52840-edf/Cargo.toml @@ -1,5 +1,5 @@ [package] -edition = "2021" +edition = "2024" name = "embassy-nrf52840-edf-examples" version = "0.1.0" license = "MIT OR Apache-2.0" diff --git a/examples/nrf52840-edf/src/bin/basic.rs b/examples/nrf52840-edf/src/bin/basic.rs index d888e17d1..f7214790d 100644 --- a/examples/nrf52840-edf/src/bin/basic.rs +++ b/examples/nrf52840-edf/src/bin/basic.rs @@ -12,7 +12,7 @@ #![no_std] #![no_main] -use core::sync::atomic::{compiler_fence, Ordering}; +use core::sync::atomic::{Ordering, compiler_fence}; use defmt::unwrap; use embassy_executor::Spawner; diff --git a/examples/nrf52840-rtic/Cargo.toml b/examples/nrf52840-rtic/Cargo.toml index d860626a1..26b21598f 100644 --- a/examples/nrf52840-rtic/Cargo.toml +++ b/examples/nrf52840-rtic/Cargo.toml @@ -1,5 +1,5 @@ [package] -edition = "2021" +edition = "2024" name = "embassy-nrf52840-rtic-examples" version = "0.1.0" license = "MIT OR Apache-2.0" diff --git a/examples/nrf52840-rtic/src/bin/blinky.rs b/examples/nrf52840-rtic/src/bin/blinky.rs index 2adac7e0a..671082117 100644 --- a/examples/nrf52840-rtic/src/bin/blinky.rs +++ b/examples/nrf52840-rtic/src/bin/blinky.rs @@ -7,7 +7,7 @@ use {defmt_rtt as _, panic_probe as _}; mod app { use defmt::info; use embassy_nrf::gpio::{Level, Output, OutputDrive}; - use embassy_nrf::{peripherals, Peri}; + use embassy_nrf::{Peri, peripherals}; use embassy_time::Timer; #[shared] diff --git a/examples/nrf52840/Cargo.toml b/examples/nrf52840/Cargo.toml index 5b3e176c0..a026d6352 100644 --- a/examples/nrf52840/Cargo.toml +++ b/examples/nrf52840/Cargo.toml @@ -1,5 +1,5 @@ [package] -edition = "2021" +edition = "2024" name = "embassy-nrf52840-examples" version = "0.1.0" license = "MIT OR Apache-2.0" diff --git a/examples/nrf52840/src/bin/channel_sender_receiver.rs b/examples/nrf52840/src/bin/channel_sender_receiver.rs index 09050db68..de694eaa0 100644 --- a/examples/nrf52840/src/bin/channel_sender_receiver.rs +++ b/examples/nrf52840/src/bin/channel_sender_receiver.rs @@ -3,8 +3,8 @@ use defmt::unwrap; use embassy_executor::Spawner; -use embassy_nrf::gpio::{AnyPin, Level, Output, OutputDrive}; use embassy_nrf::Peri; +use embassy_nrf::gpio::{AnyPin, Level, Output, OutputDrive}; use embassy_sync::blocking_mutex::raw::NoopRawMutex; use embassy_sync::channel::{Channel, Receiver, Sender}; use embassy_time::Timer; diff --git a/examples/nrf52840/src/bin/ethernet_enc28j60.rs b/examples/nrf52840/src/bin/ethernet_enc28j60.rs index e59afd37f..5a988d89b 100644 --- a/examples/nrf52840/src/bin/ethernet_enc28j60.rs +++ b/examples/nrf52840/src/bin/ethernet_enc28j60.rs @@ -3,8 +3,8 @@ use defmt::*; use embassy_executor::Spawner; -use embassy_net::tcp::TcpSocket; use embassy_net::StackResources; +use embassy_net::tcp::TcpSocket; use embassy_net_enc28j60::Enc28j60; use embassy_nrf::gpio::{Level, Output, OutputDrive}; use embassy_nrf::rng::Rng; diff --git a/examples/nrf52840/src/bin/i2s_effect.rs b/examples/nrf52840/src/bin/i2s_effect.rs index 9eadeb4e4..c31b78614 100644 --- a/examples/nrf52840/src/bin/i2s_effect.rs +++ b/examples/nrf52840/src/bin/i2s_effect.rs @@ -5,7 +5,7 @@ use core::f32::consts::PI; use defmt::{error, info}; use embassy_executor::Spawner; -use embassy_nrf::i2s::{self, Channels, Config, MasterClock, MultiBuffering, Sample as _, SampleWidth, I2S}; +use embassy_nrf::i2s::{self, Channels, Config, I2S, MasterClock, MultiBuffering, Sample as _, SampleWidth}; use embassy_nrf::{bind_interrupts, peripherals}; use {defmt_rtt as _, panic_probe as _}; @@ -102,11 +102,7 @@ impl SineOsc { #[inline] fn abs(value: f32) -> f32 { - if value < 0.0 { - -value - } else { - value - } + if value < 0.0 { -value } else { value } } #[inline] diff --git a/examples/nrf52840/src/bin/i2s_monitor.rs b/examples/nrf52840/src/bin/i2s_monitor.rs index 799be351f..66b429b09 100644 --- a/examples/nrf52840/src/bin/i2s_monitor.rs +++ b/examples/nrf52840/src/bin/i2s_monitor.rs @@ -3,7 +3,7 @@ use defmt::{debug, error, info}; use embassy_executor::Spawner; -use embassy_nrf::i2s::{self, Channels, Config, DoubleBuffering, MasterClock, Sample as _, SampleWidth, I2S}; +use embassy_nrf::i2s::{self, Channels, Config, DoubleBuffering, I2S, MasterClock, Sample as _, SampleWidth}; use embassy_nrf::pwm::{Prescaler, SimplePwm}; use embassy_nrf::{bind_interrupts, peripherals}; use {defmt_rtt as _, panic_probe as _}; diff --git a/examples/nrf52840/src/bin/i2s_waveform.rs b/examples/nrf52840/src/bin/i2s_waveform.rs index 137d82840..ce7a68d3a 100644 --- a/examples/nrf52840/src/bin/i2s_waveform.rs +++ b/examples/nrf52840/src/bin/i2s_waveform.rs @@ -5,7 +5,7 @@ use core::f32::consts::PI; use defmt::{error, info}; use embassy_executor::Spawner; -use embassy_nrf::i2s::{self, Channels, Config, DoubleBuffering, MasterClock, Sample as _, SampleWidth, I2S}; +use embassy_nrf::i2s::{self, Channels, Config, DoubleBuffering, I2S, MasterClock, Sample as _, SampleWidth}; use embassy_nrf::{bind_interrupts, peripherals}; use {defmt_rtt as _, panic_probe as _}; @@ -140,11 +140,7 @@ impl SineOsc { #[inline] fn abs(value: f32) -> f32 { - if value < 0.0 { - -value - } else { - value - } + if value < 0.0 { -value } else { value } } #[inline] diff --git a/examples/nrf52840/src/bin/multiprio.rs b/examples/nrf52840/src/bin/multiprio.rs index 4d9b986d4..dc566adee 100644 --- a/examples/nrf52840/src/bin/multiprio.rs +++ b/examples/nrf52840/src/bin/multiprio.rs @@ -113,12 +113,12 @@ static EXECUTOR_LOW: StaticCell = StaticCell::new(); #[interrupt] unsafe fn EGU1_SWI1() { - EXECUTOR_HIGH.on_interrupt() + unsafe { EXECUTOR_HIGH.on_interrupt() } } #[interrupt] unsafe fn EGU0_SWI0() { - EXECUTOR_MED.on_interrupt() + unsafe { EXECUTOR_MED.on_interrupt() } } #[entry] diff --git a/examples/nrf52840/src/bin/raw_spawn.rs b/examples/nrf52840/src/bin/raw_spawn.rs index b80954408..783be763d 100644 --- a/examples/nrf52840/src/bin/raw_spawn.rs +++ b/examples/nrf52840/src/bin/raw_spawn.rs @@ -5,8 +5,8 @@ use core::mem; use cortex_m_rt::entry; use defmt::{info, unwrap}; -use embassy_executor::raw::TaskStorage; use embassy_executor::Executor; +use embassy_executor::raw::TaskStorage; use embassy_time::Timer; use static_cell::StaticCell; use {defmt_rtt as _, panic_probe as _}; @@ -48,5 +48,5 @@ fn main() -> ! { } unsafe fn make_static(t: &T) -> &'static T { - mem::transmute(t) + unsafe { mem::transmute(t) } } diff --git a/examples/nrf52840/src/bin/rtc.rs b/examples/nrf52840/src/bin/rtc.rs index 9d475df7f..56a0c25f4 100644 --- a/examples/nrf52840/src/bin/rtc.rs +++ b/examples/nrf52840/src/bin/rtc.rs @@ -7,8 +7,8 @@ use embassy_executor::Spawner; use embassy_nrf::gpio::{Level, Output, OutputDrive}; use embassy_nrf::interrupt; use embassy_nrf::rtc::Rtc; -use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex; use embassy_sync::blocking_mutex::Mutex; +use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex; use portable_atomic::AtomicU64; use {defmt_rtt as _, panic_probe as _}; diff --git a/examples/nrf52840/src/bin/usb_ethernet.rs b/examples/nrf52840/src/bin/usb_ethernet.rs index a75b967b4..14a1004d7 100644 --- a/examples/nrf52840/src/bin/usb_ethernet.rs +++ b/examples/nrf52840/src/bin/usb_ethernet.rs @@ -3,11 +3,11 @@ use defmt::*; use embassy_executor::Spawner; -use embassy_net::tcp::TcpSocket; use embassy_net::StackResources; +use embassy_net::tcp::TcpSocket; use embassy_nrf::rng::Rng; -use embassy_nrf::usb::vbus_detect::HardwareVbusDetect; use embassy_nrf::usb::Driver; +use embassy_nrf::usb::vbus_detect::HardwareVbusDetect; use embassy_nrf::{bind_interrupts, pac, peripherals, rng, usb}; use embassy_usb::class::cdc_ncm::embassy_net::{Device, Runner, State as NetState}; use embassy_usb::class::cdc_ncm::{CdcNcmClass, State}; diff --git a/examples/nrf52840/src/bin/usb_hid_keyboard.rs b/examples/nrf52840/src/bin/usb_hid_keyboard.rs index 540580c31..7b7303526 100644 --- a/examples/nrf52840/src/bin/usb_hid_keyboard.rs +++ b/examples/nrf52840/src/bin/usb_hid_keyboard.rs @@ -6,10 +6,10 @@ use core::sync::atomic::{AtomicBool, AtomicU8, Ordering}; use defmt::*; use embassy_executor::Spawner; use embassy_futures::join::join; -use embassy_futures::select::{select, Either}; +use embassy_futures::select::{Either, select}; use embassy_nrf::gpio::{Input, Pull}; -use embassy_nrf::usb::vbus_detect::HardwareVbusDetect; use embassy_nrf::usb::Driver; +use embassy_nrf::usb::vbus_detect::HardwareVbusDetect; use embassy_nrf::{bind_interrupts, pac, peripherals, usb}; use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex; use embassy_sync::signal::Signal; @@ -244,7 +244,9 @@ impl Handler for MyDeviceHandler { fn suspended(&mut self, suspended: bool) { if suspended { - info!("Device suspended, the Vbus current limit is 500µA (or 2.5mA for high-power devices with remote wakeup enabled)."); + info!( + "Device suspended, the Vbus current limit is 500µA (or 2.5mA for high-power devices with remote wakeup enabled)." + ); SUSPENDED.store(true, Ordering::Release); } else { SUSPENDED.store(false, Ordering::Release); diff --git a/examples/nrf52840/src/bin/usb_hid_mouse.rs b/examples/nrf52840/src/bin/usb_hid_mouse.rs index efc28203c..6bee4546b 100644 --- a/examples/nrf52840/src/bin/usb_hid_mouse.rs +++ b/examples/nrf52840/src/bin/usb_hid_mouse.rs @@ -6,8 +6,8 @@ use core::sync::atomic::{AtomicU8, Ordering}; use defmt::*; use embassy_executor::Spawner; use embassy_futures::join::join; -use embassy_nrf::usb::vbus_detect::HardwareVbusDetect; use embassy_nrf::usb::Driver; +use embassy_nrf::usb::vbus_detect::HardwareVbusDetect; use embassy_nrf::{bind_interrupts, pac, peripherals, usb}; use embassy_time::Timer; use embassy_usb::class::hid::{ diff --git a/examples/nrf52840/src/bin/usb_serial.rs b/examples/nrf52840/src/bin/usb_serial.rs index e7c2d0854..469002bc7 100644 --- a/examples/nrf52840/src/bin/usb_serial.rs +++ b/examples/nrf52840/src/bin/usb_serial.rs @@ -4,8 +4,8 @@ use defmt::{info, panic}; use embassy_executor::Spawner; use embassy_futures::join::join; -use embassy_nrf::usb::vbus_detect::{HardwareVbusDetect, VbusDetect}; use embassy_nrf::usb::Driver; +use embassy_nrf::usb::vbus_detect::{HardwareVbusDetect, VbusDetect}; use embassy_nrf::{bind_interrupts, pac, peripherals, usb}; use embassy_usb::class::cdc_acm::{CdcAcmClass, State}; use embassy_usb::driver::EndpointError; diff --git a/examples/nrf52840/src/bin/usb_serial_multitask.rs b/examples/nrf52840/src/bin/usb_serial_multitask.rs index b6a983854..67b2bccbb 100644 --- a/examples/nrf52840/src/bin/usb_serial_multitask.rs +++ b/examples/nrf52840/src/bin/usb_serial_multitask.rs @@ -3,8 +3,8 @@ use defmt::{info, panic, unwrap}; use embassy_executor::Spawner; -use embassy_nrf::usb::vbus_detect::HardwareVbusDetect; use embassy_nrf::usb::Driver; +use embassy_nrf::usb::vbus_detect::HardwareVbusDetect; use embassy_nrf::{bind_interrupts, pac, peripherals, usb}; use embassy_usb::class::cdc_acm::{CdcAcmClass, State}; use embassy_usb::driver::EndpointError; diff --git a/examples/nrf52840/src/bin/usb_serial_winusb.rs b/examples/nrf52840/src/bin/usb_serial_winusb.rs index e30e08a01..cd4d5bca1 100644 --- a/examples/nrf52840/src/bin/usb_serial_winusb.rs +++ b/examples/nrf52840/src/bin/usb_serial_winusb.rs @@ -4,8 +4,8 @@ use defmt::{info, panic}; use embassy_executor::Spawner; use embassy_futures::join::join; -use embassy_nrf::usb::vbus_detect::{HardwareVbusDetect, VbusDetect}; use embassy_nrf::usb::Driver; +use embassy_nrf::usb::vbus_detect::{HardwareVbusDetect, VbusDetect}; use embassy_nrf::{bind_interrupts, pac, peripherals, usb}; use embassy_usb::class::cdc_acm::{CdcAcmClass, State}; use embassy_usb::driver::EndpointError; diff --git a/examples/nrf52840/src/bin/wifi_esp_hosted.rs b/examples/nrf52840/src/bin/wifi_esp_hosted.rs index 1bc35746a..07752ffc4 100644 --- a/examples/nrf52840/src/bin/wifi_esp_hosted.rs +++ b/examples/nrf52840/src/bin/wifi_esp_hosted.rs @@ -3,8 +3,8 @@ use defmt::{info, unwrap, warn}; use embassy_executor::Spawner; -use embassy_net::tcp::TcpSocket; use embassy_net::StackResources; +use embassy_net::tcp::TcpSocket; use embassy_nrf::gpio::{Input, Level, Output, OutputDrive, Pull}; use embassy_nrf::rng::Rng; use embassy_nrf::spim::{self, Spim}; diff --git a/examples/nrf5340/Cargo.toml b/examples/nrf5340/Cargo.toml index 256fee08d..4dcbdd715 100644 --- a/examples/nrf5340/Cargo.toml +++ b/examples/nrf5340/Cargo.toml @@ -1,5 +1,5 @@ [package] -edition = "2021" +edition = "2024" name = "embassy-nrf5340-examples" version = "0.1.0" license = "MIT OR Apache-2.0" diff --git a/examples/nrf5340/src/bin/nrf5340dk_internal_caps.rs b/examples/nrf5340/src/bin/nrf5340dk_internal_caps.rs new file mode 100644 index 000000000..0b1fb852e --- /dev/null +++ b/examples/nrf5340/src/bin/nrf5340dk_internal_caps.rs @@ -0,0 +1,30 @@ +#![no_std] +#![no_main] + +use defmt::info; +use embassy_executor::Spawner; +use embassy_nrf::config::{Config, HfclkSource, LfclkSource, LfxoCapacitance}; +use embassy_nrf::pac; +use {defmt_rtt as _, panic_probe as _}; + +fn print_xosc32mcaps() { + let value = pac::OSCILLATORS.xosc32mcaps().read(); + info!("XOSC32MCAPS.ENABLE = {}", value.enable()); + info!("XOSC32MCAPS.CAPVALUE = {}", value.capvalue()); +} + +#[embassy_executor::main] +async fn main(_spawner: Spawner) { + info!("Before init:"); + print_xosc32mcaps(); + + let mut config = Config::default(); + config.hfclk_source = HfclkSource::Internal; + config.lfclk_source = LfclkSource::ExternalXtal; + config.internal_capacitors.hfxo = None; // keep the value from the FICR + config.internal_capacitors.lfxo = Some(LfxoCapacitance::_7pF); + let _p = embassy_nrf::init(config); + + info!("After init:"); + print_xosc32mcaps(); +} diff --git a/examples/nrf54l15/Cargo.toml b/examples/nrf54l15/Cargo.toml index 9c24cdab4..541e79fcb 100644 --- a/examples/nrf54l15/Cargo.toml +++ b/examples/nrf54l15/Cargo.toml @@ -1,5 +1,5 @@ [package] -edition = "2021" +edition = "2024" name = "embassy-nrf54l15-examples" version = "0.1.0" license = "MIT OR Apache-2.0" @@ -8,6 +8,7 @@ publish = false [dependencies] embassy-executor = { version = "0.9.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "executor-interrupt", "defmt"] } embassy-time = { version = "0.5.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime"] } +embassy-sync = { version = "0.7.2", path = "../../embassy-sync", features = ["defmt"] } embassy-nrf = { version = "0.8.0", path = "../../embassy-nrf", features = ["defmt", "nrf54l15-app-s", "time-driver-rtc1", "gpiote", "unstable-pac", "time"] } defmt = "1.0.1" @@ -18,6 +19,7 @@ cortex-m = { version = "0.7.6", features = ["inline-asm", "critical-section-sing cortex-m-rt = "0.7.0" embedded-storage = "0.3.1" +portable-atomic = "1" [profile.release] debug = 2 diff --git a/examples/nrf54l15/src/bin/rtc.rs b/examples/nrf54l15/src/bin/rtc.rs new file mode 100644 index 000000000..a45aaca52 --- /dev/null +++ b/examples/nrf54l15/src/bin/rtc.rs @@ -0,0 +1,56 @@ +#![no_std] +#![no_main] + +use core::cell::RefCell; + +use embassy_executor::Spawner; +use embassy_nrf::gpio::{Level, Output, OutputDrive}; +use embassy_nrf::interrupt; +use embassy_nrf::rtc::Rtc; +use embassy_sync::blocking_mutex::Mutex; +use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex; +use portable_atomic::AtomicU64; +use {defmt_rtt as _, panic_probe as _}; + +// 64 bit counter which will never overflow. +static TICK_COUNTER: AtomicU64 = AtomicU64::new(0); +static RTC: Mutex>>> = Mutex::new(RefCell::new(None)); + +#[embassy_executor::main] +async fn main(_spawner: Spawner) { + defmt::println!("nRF54L15 RTC example"); + let p = embassy_nrf::init(Default::default()); + let mut led = Output::new(p.P2_09, Level::High, OutputDrive::Standard); + // Counter resolution is 125 ms. + let mut rtc = Rtc::new(p.RTC10, (1 << 12) - 1).unwrap(); + rtc.enable_interrupt(embassy_nrf::rtc::Interrupt::Tick, true); + rtc.enable_event(embassy_nrf::rtc::Interrupt::Tick); + rtc.enable(); + RTC.lock(|r| { + let mut rtc_borrow = r.borrow_mut(); + *rtc_borrow = Some(rtc); + }); + + let mut last_counter_val = 0; + loop { + let current = TICK_COUNTER.load(core::sync::atomic::Ordering::Relaxed); + if current != last_counter_val { + led.toggle(); + last_counter_val = current; + } + } +} + +#[interrupt] +fn RTC10() { + // For 64-bit, we do not need to worry about overflowing, at least not for realistic program + // lifetimes. + TICK_COUNTER.fetch_add(1, core::sync::atomic::Ordering::Relaxed); + RTC.lock(|r| { + let mut rtc_borrow = r.borrow_mut(); + rtc_borrow + .as_mut() + .unwrap() + .reset_event(embassy_nrf::rtc::Interrupt::Tick); + }); +} diff --git a/examples/nrf9151/ns/Cargo.toml b/examples/nrf9151/ns/Cargo.toml index 89a6c7c94..7f1f5239a 100644 --- a/examples/nrf9151/ns/Cargo.toml +++ b/examples/nrf9151/ns/Cargo.toml @@ -1,5 +1,5 @@ [package] -edition = "2021" +edition = "2024" name = "embassy-nrf9151-non-secure-examples" version = "0.1.0" license = "MIT OR Apache-2.0" diff --git a/examples/nrf9151/s/Cargo.toml b/examples/nrf9151/s/Cargo.toml index 870311c5d..ce71cc456 100644 --- a/examples/nrf9151/s/Cargo.toml +++ b/examples/nrf9151/s/Cargo.toml @@ -1,5 +1,5 @@ [package] -edition = "2021" +edition = "2024" name = "embassy-nrf9151-secure-examples" version = "0.1.0" license = "MIT OR Apache-2.0" diff --git a/examples/nrf9160/Cargo.toml b/examples/nrf9160/Cargo.toml index 274e26dd6..ae3b2eeb1 100644 --- a/examples/nrf9160/Cargo.toml +++ b/examples/nrf9160/Cargo.toml @@ -1,5 +1,5 @@ [package] -edition = "2021" +edition = "2024" name = "embassy-nrf9160-examples" version = "0.1.0" license = "MIT OR Apache-2.0" diff --git a/examples/nrf9160/src/bin/modem_tcp_client.rs b/examples/nrf9160/src/bin/modem_tcp_client.rs index 460a4cfae..07fa57e63 100644 --- a/examples/nrf9160/src/bin/modem_tcp_client.rs +++ b/examples/nrf9160/src/bin/modem_tcp_client.rs @@ -11,11 +11,11 @@ use defmt::{info, unwrap, warn}; use embassy_executor::Spawner; use embassy_net::{Ipv4Cidr, Stack, StackResources}; use embassy_net_nrf91::context::Status; -use embassy_net_nrf91::{context, Runner, State, TraceBuffer, TraceReader}; +use embassy_net_nrf91::{Runner, State, TraceBuffer, TraceReader, context}; use embassy_nrf::buffered_uarte::{self, BufferedUarteTx}; use embassy_nrf::gpio::{AnyPin, Level, Output, OutputDrive}; use embassy_nrf::uarte::Baudrate; -use embassy_nrf::{bind_interrupts, interrupt, peripherals, uarte, Peri}; +use embassy_nrf::{Peri, bind_interrupts, interrupt, peripherals, uarte}; use embassy_time::{Duration, Timer}; use embedded_io_async::Write; use heapless::Vec; @@ -101,7 +101,7 @@ async fn blink_task(pin: Peri<'static, AnyPin>) { } } -extern "C" { +unsafe extern "C" { static __start_ipc: u8; static __end_ipc: u8; } diff --git a/examples/rp/Cargo.toml b/examples/rp/Cargo.toml index 97e019cdf..640addb28 100644 --- a/examples/rp/Cargo.toml +++ b/examples/rp/Cargo.toml @@ -1,5 +1,5 @@ [package] -edition = "2021" +edition = "2024" name = "embassy-rp-examples" version = "0.1.0" license = "MIT OR Apache-2.0" diff --git a/examples/rp/src/bin/assign_resources.rs b/examples/rp/src/bin/assign_resources.rs index 4ee4278b5..aaa134768 100644 --- a/examples/rp/src/bin/assign_resources.rs +++ b/examples/rp/src/bin/assign_resources.rs @@ -14,9 +14,9 @@ use assign_resources::assign_resources; use defmt::*; use embassy_executor::Spawner; +use embassy_rp::Peri; use embassy_rp::gpio::{Level, Output}; use embassy_rp::peripherals::{self, PIN_20, PIN_21}; -use embassy_rp::Peri; use embassy_time::Timer; use {defmt_rtt as _, panic_probe as _}; diff --git a/examples/rp/src/bin/debounce.rs b/examples/rp/src/bin/debounce.rs index 0077f19fc..6eeb01d0a 100644 --- a/examples/rp/src/bin/debounce.rs +++ b/examples/rp/src/bin/debounce.rs @@ -7,7 +7,7 @@ use defmt::info; use embassy_executor::Spawner; use embassy_rp::gpio::{Input, Level, Pull}; -use embassy_time::{with_deadline, Duration, Instant, Timer}; +use embassy_time::{Duration, Instant, Timer, with_deadline}; use {defmt_rtt as _, panic_probe as _}; pub struct Debouncer<'a> { diff --git a/examples/rp/src/bin/ethernet_w5500_icmp_ping.rs b/examples/rp/src/bin/ethernet_w5500_icmp_ping.rs index 49d28071a..cb667f24f 100644 --- a/examples/rp/src/bin/ethernet_w5500_icmp_ping.rs +++ b/examples/rp/src/bin/ethernet_w5500_icmp_ping.rs @@ -12,8 +12,8 @@ use core::str::FromStr; use defmt::*; use embassy_executor::Spawner; use embassy_futures::yield_now; -use embassy_net::icmp::ping::{PingManager, PingParams}; use embassy_net::icmp::PacketMetadata; +use embassy_net::icmp::ping::{PingManager, PingParams}; use embassy_net::{Ipv4Cidr, Stack, StackResources}; use embassy_net_wiznet::chip::W5500; use embassy_net_wiznet::*; @@ -99,7 +99,7 @@ async fn main(spawner: Spawner) { // Create the ping manager instance let mut ping_manager = PingManager::new(stack, &mut rx_meta, &mut rx_buffer, &mut tx_meta, &mut tx_buffer); let addr = "192.168.8.1"; // Address to ping to - // Create the PingParams with the target address + // Create the PingParams with the target address let mut ping_params = PingParams::new(Ipv4Addr::from_str(addr).unwrap()); // (optional) Set custom properties of the ping ping_params.set_payload(b"Hello, Ping!"); // custom payload diff --git a/examples/rp/src/bin/ethernet_w55rp20_tcp_server.rs b/examples/rp/src/bin/ethernet_w55rp20_tcp_server.rs index f51df2df9..b402029b5 100644 --- a/examples/rp/src/bin/ethernet_w55rp20_tcp_server.rs +++ b/examples/rp/src/bin/ethernet_w55rp20_tcp_server.rs @@ -65,7 +65,7 @@ async fn main(spawner: Spawner) { // Construct an SPI driver backed by a PIO state machine let mut spi_cfg = SpiConfig::default(); spi_cfg.frequency = 12_500_000; // The PIO SPI program is much less stable than the actual SPI - // peripheral, use higher speeds at your peril + // peripheral, use higher speeds at your peril let spi = Spi::new(&mut common, sm0, clk, mosi, miso, p.DMA_CH0, p.DMA_CH1, spi_cfg); // Further control pins diff --git a/examples/rp/src/bin/interrupt.rs b/examples/rp/src/bin/interrupt.rs index 2748f778a..2605622ab 100644 --- a/examples/rp/src/bin/interrupt.rs +++ b/examples/rp/src/bin/interrupt.rs @@ -16,8 +16,8 @@ use embassy_rp::adc::{self, Adc, Blocking}; use embassy_rp::gpio::Pull; use embassy_rp::interrupt; use embassy_rp::pwm::{Config, Pwm}; -use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex; use embassy_sync::blocking_mutex::Mutex; +use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex; use embassy_sync::channel::Channel; use embassy_time::{Duration, Ticker}; use portable_atomic::{AtomicU32, Ordering}; diff --git a/examples/rp/src/bin/multicore.rs b/examples/rp/src/bin/multicore.rs index 3a6367420..d289f8020 100644 --- a/examples/rp/src/bin/multicore.rs +++ b/examples/rp/src/bin/multicore.rs @@ -8,7 +8,7 @@ use defmt::*; use embassy_executor::Executor; use embassy_rp::gpio::{Level, Output}; -use embassy_rp::multicore::{spawn_core1, Stack}; +use embassy_rp::multicore::{Stack, spawn_core1}; use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex; use embassy_sync::channel::Channel; use embassy_time::Timer; diff --git a/examples/rp/src/bin/multiprio.rs b/examples/rp/src/bin/multiprio.rs index 96cdf8fb1..310047505 100644 --- a/examples/rp/src/bin/multiprio.rs +++ b/examples/rp/src/bin/multiprio.rs @@ -61,7 +61,7 @@ use defmt::{info, unwrap}; use embassy_executor::{Executor, InterruptExecutor}; use embassy_rp::interrupt; use embassy_rp::interrupt::{InterruptExt, Priority}; -use embassy_time::{Instant, Timer, TICK_HZ}; +use embassy_time::{Instant, TICK_HZ, Timer}; use static_cell::StaticCell; use {defmt_rtt as _, panic_probe as _}; @@ -113,12 +113,12 @@ static EXECUTOR_LOW: StaticCell = StaticCell::new(); #[interrupt] unsafe fn SWI_IRQ_1() { - EXECUTOR_HIGH.on_interrupt() + unsafe { EXECUTOR_HIGH.on_interrupt() } } #[interrupt] unsafe fn SWI_IRQ_0() { - EXECUTOR_MED.on_interrupt() + unsafe { EXECUTOR_MED.on_interrupt() } } #[entry] diff --git a/examples/rp/src/bin/orchestrate_tasks.rs b/examples/rp/src/bin/orchestrate_tasks.rs index 9f25e1087..cd26a5371 100644 --- a/examples/rp/src/bin/orchestrate_tasks.rs +++ b/examples/rp/src/bin/orchestrate_tasks.rs @@ -20,11 +20,11 @@ use assign_resources::assign_resources; use defmt::*; use embassy_executor::Spawner; -use embassy_futures::select::{select, Either}; +use embassy_futures::select::{Either, select}; use embassy_rp::adc::{Adc, Channel, Config, InterruptHandler}; use embassy_rp::clocks::RoscRng; use embassy_rp::gpio::{Input, Pull}; -use embassy_rp::{bind_interrupts, peripherals, Peri}; +use embassy_rp::{Peri, bind_interrupts, peripherals}; use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex; use embassy_sync::mutex::Mutex; use embassy_sync::{channel, signal}; diff --git a/examples/rp/src/bin/overclock.rs b/examples/rp/src/bin/overclock.rs index 83b17308b..a98185a8e 100644 --- a/examples/rp/src/bin/overclock.rs +++ b/examples/rp/src/bin/overclock.rs @@ -7,7 +7,7 @@ use defmt::*; use embassy_executor::Spawner; -use embassy_rp::clocks::{clk_sys_freq, core_voltage, ClockConfig}; +use embassy_rp::clocks::{ClockConfig, clk_sys_freq, core_voltage}; use embassy_rp::config::Config; use embassy_rp::gpio::{Level, Output}; use embassy_time::{Duration, Instant, Timer}; diff --git a/examples/rp/src/bin/overclock_manual.rs b/examples/rp/src/bin/overclock_manual.rs index dea5cfb3c..18397f9a8 100644 --- a/examples/rp/src/bin/overclock_manual.rs +++ b/examples/rp/src/bin/overclock_manual.rs @@ -7,7 +7,7 @@ use defmt::*; use embassy_executor::Spawner; -use embassy_rp::clocks::{clk_sys_freq, core_voltage, ClockConfig, CoreVoltage, PllConfig}; +use embassy_rp::clocks::{ClockConfig, CoreVoltage, PllConfig, clk_sys_freq, core_voltage}; use embassy_rp::config::Config; use embassy_rp::gpio::{Level, Output}; use embassy_time::{Duration, Instant, Timer}; diff --git a/examples/rp/src/bin/pio_async.rs b/examples/rp/src/bin/pio_async.rs index 1743a417e..55e983c36 100644 --- a/examples/rp/src/bin/pio_async.rs +++ b/examples/rp/src/bin/pio_async.rs @@ -7,7 +7,7 @@ use embassy_executor::Spawner; use embassy_rp::peripherals::PIO0; use embassy_rp::pio::program::pio_asm; use embassy_rp::pio::{Common, Config, InterruptHandler, Irq, Pio, PioPin, ShiftDirection, StateMachine}; -use embassy_rp::{bind_interrupts, Peri}; +use embassy_rp::{Peri, bind_interrupts}; use fixed::traits::ToFixed; use fixed_macro::types::U56F8; use {defmt_rtt as _, panic_probe as _}; diff --git a/examples/rp/src/bin/pio_stepper.rs b/examples/rp/src/bin/pio_stepper.rs index 3862c248b..e8f203990 100644 --- a/examples/rp/src/bin/pio_stepper.rs +++ b/examples/rp/src/bin/pio_stepper.rs @@ -10,7 +10,7 @@ use embassy_rp::bind_interrupts; use embassy_rp::peripherals::PIO0; use embassy_rp::pio::{InterruptHandler, Pio}; use embassy_rp::pio_programs::stepper::{PioStepper, PioStepperProgram}; -use embassy_time::{with_timeout, Duration, Timer}; +use embassy_time::{Duration, Timer, with_timeout}; use {defmt_rtt as _, panic_probe as _}; bind_interrupts!(struct Irqs { diff --git a/examples/rp/src/bin/pwm.rs b/examples/rp/src/bin/pwm.rs index 9dd07ab6e..f985bf7cf 100644 --- a/examples/rp/src/bin/pwm.rs +++ b/examples/rp/src/bin/pwm.rs @@ -9,9 +9,9 @@ use defmt::*; use embassy_executor::Spawner; -use embassy_rp::peripherals::{PIN_25, PIN_4, PWM_SLICE2, PWM_SLICE4}; -use embassy_rp::pwm::{Config, Pwm, SetDutyCycle}; use embassy_rp::Peri; +use embassy_rp::peripherals::{PIN_4, PIN_25, PWM_SLICE2, PWM_SLICE4}; +use embassy_rp::pwm::{Config, Pwm, SetDutyCycle}; use embassy_time::Timer; use {defmt_rtt as _, panic_probe as _}; diff --git a/examples/rp/src/bin/rtc_alarm.rs b/examples/rp/src/bin/rtc_alarm.rs index 94b5fbd27..bde49ccd5 100644 --- a/examples/rp/src/bin/rtc_alarm.rs +++ b/examples/rp/src/bin/rtc_alarm.rs @@ -5,7 +5,7 @@ use defmt::*; use embassy_executor::Spawner; -use embassy_futures::select::{select, Either}; +use embassy_futures::select::{Either, select}; use embassy_rp::bind_interrupts; use embassy_rp::rtc::{DateTime, DateTimeFilter, DayOfWeek, Rtc}; use embassy_time::Timer; diff --git a/examples/rp/src/bin/sharing.rs b/examples/rp/src/bin/sharing.rs index d4c89946b..618ab9117 100644 --- a/examples/rp/src/bin/sharing.rs +++ b/examples/rp/src/bin/sharing.rs @@ -52,7 +52,7 @@ bind_interrupts!(struct Irqs { #[interrupt] unsafe fn SWI_IRQ_0() { - EXECUTOR_HI.on_interrupt() + unsafe { EXECUTOR_HI.on_interrupt() } } #[entry] diff --git a/examples/rp/src/bin/spi_display.rs b/examples/rp/src/bin/spi_display.rs index dd114a4ae..4bf924e56 100644 --- a/examples/rp/src/bin/spi_display.rs +++ b/examples/rp/src/bin/spi_display.rs @@ -15,19 +15,19 @@ use embassy_executor::Spawner; use embassy_rp::gpio::{Level, Output}; use embassy_rp::spi; use embassy_rp::spi::Spi; -use embassy_sync::blocking_mutex::raw::NoopRawMutex; use embassy_sync::blocking_mutex::Mutex; +use embassy_sync::blocking_mutex::raw::NoopRawMutex; use embassy_time::Delay; use embedded_graphics::image::{Image, ImageRawLE}; -use embedded_graphics::mono_font::ascii::FONT_10X20; use embedded_graphics::mono_font::MonoTextStyle; +use embedded_graphics::mono_font::ascii::FONT_10X20; use embedded_graphics::pixelcolor::Rgb565; use embedded_graphics::prelude::*; use embedded_graphics::primitives::{PrimitiveStyleBuilder, Rectangle}; use embedded_graphics::text::Text; +use mipidsi::Builder; use mipidsi::models::ST7789; use mipidsi::options::{Orientation, Rotation}; -use mipidsi::Builder; use {defmt_rtt as _, panic_probe as _}; use crate::touch::Touch; @@ -167,11 +167,7 @@ mod touch { let x = ((x - cal.x1) * cal.sx / (cal.x2 - cal.x1)).clamp(0, cal.sx); let y = ((y - cal.y1) * cal.sy / (cal.y2 - cal.y1)).clamp(0, cal.sy); - if x == 0 && y == 0 { - None - } else { - Some((x, y)) - } + if x == 0 && y == 0 { None } else { Some((x, y)) } } } } diff --git a/examples/rp/src/bin/spi_gc9a01.rs b/examples/rp/src/bin/spi_gc9a01.rs index fdef09d4b..fd007b9bd 100644 --- a/examples/rp/src/bin/spi_gc9a01.rs +++ b/examples/rp/src/bin/spi_gc9a01.rs @@ -16,16 +16,16 @@ use embassy_rp::clocks::RoscRng; use embassy_rp::gpio::{Level, Output}; use embassy_rp::spi; use embassy_rp::spi::{Blocking, Spi}; -use embassy_sync::blocking_mutex::raw::NoopRawMutex; use embassy_sync::blocking_mutex::Mutex; +use embassy_sync::blocking_mutex::raw::NoopRawMutex; use embassy_time::{Delay, Duration, Timer}; use embedded_graphics::image::{Image, ImageRawLE}; use embedded_graphics::pixelcolor::Rgb565; use embedded_graphics::prelude::*; use embedded_graphics::primitives::{PrimitiveStyleBuilder, Rectangle}; +use mipidsi::Builder; use mipidsi::models::GC9A01; use mipidsi::options::{ColorInversion, ColorOrder}; -use mipidsi::Builder; use {defmt_rtt as _, panic_probe as _}; const DISPLAY_FREQ: u32 = 64_000_000; diff --git a/examples/rp/src/bin/uart_r503.rs b/examples/rp/src/bin/uart_r503.rs index 085be280b..a25d45b18 100644 --- a/examples/rp/src/bin/uart_r503.rs +++ b/examples/rp/src/bin/uart_r503.rs @@ -6,7 +6,7 @@ use embassy_executor::Spawner; use embassy_rp::bind_interrupts; use embassy_rp::peripherals::UART0; use embassy_rp::uart::{Config, DataBits, InterruptHandler as UARTInterruptHandler, Parity, StopBits, Uart}; -use embassy_time::{with_timeout, Duration, Timer}; +use embassy_time::{Duration, Timer, with_timeout}; use heapless::Vec; use {defmt_rtt as _, panic_probe as _}; diff --git a/examples/rp/src/bin/usb_ethernet.rs b/examples/rp/src/bin/usb_ethernet.rs index 912e52e96..b62a602b1 100644 --- a/examples/rp/src/bin/usb_ethernet.rs +++ b/examples/rp/src/bin/usb_ethernet.rs @@ -7,8 +7,8 @@ use defmt::*; use embassy_executor::Spawner; -use embassy_net::tcp::TcpSocket; use embassy_net::StackResources; +use embassy_net::tcp::TcpSocket; use embassy_rp::clocks::RoscRng; use embassy_rp::peripherals::USB; use embassy_rp::usb::{Driver, InterruptHandler}; diff --git a/examples/rp/src/bin/usb_serial.rs b/examples/rp/src/bin/usb_serial.rs index b79012acb..23d0c9e8b 100644 --- a/examples/rp/src/bin/usb_serial.rs +++ b/examples/rp/src/bin/usb_serial.rs @@ -10,9 +10,9 @@ use embassy_executor::Spawner; use embassy_rp::bind_interrupts; use embassy_rp::peripherals::USB; use embassy_rp::usb::{Driver, Instance, InterruptHandler}; +use embassy_usb::UsbDevice; use embassy_usb::class::cdc_acm::{CdcAcmClass, State}; use embassy_usb::driver::EndpointError; -use embassy_usb::UsbDevice; use static_cell::StaticCell; use {defmt_rtt as _, panic_probe as _}; diff --git a/examples/rp/src/bin/wifi_ap_tcp_server.rs b/examples/rp/src/bin/wifi_ap_tcp_server.rs index 128599e0d..0828dbbb9 100644 --- a/examples/rp/src/bin/wifi_ap_tcp_server.rs +++ b/examples/rp/src/bin/wifi_ap_tcp_server.rs @@ -7,7 +7,7 @@ use core::str::from_utf8; -use cyw43_pio::{PioSpi, DEFAULT_CLOCK_DIVIDER}; +use cyw43_pio::{DEFAULT_CLOCK_DIVIDER, PioSpi}; use defmt::*; use embassy_executor::Spawner; use embassy_net::tcp::TcpSocket; diff --git a/examples/rp/src/bin/wifi_blinky.rs b/examples/rp/src/bin/wifi_blinky.rs index b2e08c517..aa6ee4df0 100644 --- a/examples/rp/src/bin/wifi_blinky.rs +++ b/examples/rp/src/bin/wifi_blinky.rs @@ -5,7 +5,7 @@ #![no_std] #![no_main] -use cyw43_pio::{PioSpi, DEFAULT_CLOCK_DIVIDER}; +use cyw43_pio::{DEFAULT_CLOCK_DIVIDER, PioSpi}; use defmt::*; use embassy_executor::Spawner; use embassy_rp::bind_interrupts; diff --git a/examples/rp/src/bin/wifi_scan.rs b/examples/rp/src/bin/wifi_scan.rs index c884aa2ba..7e3de1db9 100644 --- a/examples/rp/src/bin/wifi_scan.rs +++ b/examples/rp/src/bin/wifi_scan.rs @@ -7,7 +7,7 @@ use core::str; -use cyw43_pio::{PioSpi, DEFAULT_CLOCK_DIVIDER}; +use cyw43_pio::{DEFAULT_CLOCK_DIVIDER, PioSpi}; use defmt::*; use embassy_executor::Spawner; use embassy_rp::bind_interrupts; diff --git a/examples/rp/src/bin/wifi_tcp_server.rs b/examples/rp/src/bin/wifi_tcp_server.rs index 126475779..e39de4902 100644 --- a/examples/rp/src/bin/wifi_tcp_server.rs +++ b/examples/rp/src/bin/wifi_tcp_server.rs @@ -8,7 +8,7 @@ use core::str::from_utf8; use cyw43::JoinOptions; -use cyw43_pio::{PioSpi, DEFAULT_CLOCK_DIVIDER}; +use cyw43_pio::{DEFAULT_CLOCK_DIVIDER, PioSpi}; use defmt::*; use embassy_executor::Spawner; use embassy_net::tcp::TcpSocket; diff --git a/examples/rp/src/bin/wifi_webrequest.rs b/examples/rp/src/bin/wifi_webrequest.rs index 079def370..b618d2b38 100644 --- a/examples/rp/src/bin/wifi_webrequest.rs +++ b/examples/rp/src/bin/wifi_webrequest.rs @@ -8,7 +8,7 @@ use core::str::from_utf8; use cyw43::JoinOptions; -use cyw43_pio::{PioSpi, DEFAULT_CLOCK_DIVIDER}; +use cyw43_pio::{DEFAULT_CLOCK_DIVIDER, PioSpi}; use defmt::*; use embassy_executor::Spawner; use embassy_net::dns::DnsSocket; diff --git a/examples/rp/src/bin/zerocopy.rs b/examples/rp/src/bin/zerocopy.rs index d603e1ed3..fc5f95e6e 100644 --- a/examples/rp/src/bin/zerocopy.rs +++ b/examples/rp/src/bin/zerocopy.rs @@ -11,7 +11,7 @@ use embassy_executor::Spawner; use embassy_rp::adc::{self, Adc, Async, Config, InterruptHandler}; use embassy_rp::gpio::Pull; use embassy_rp::peripherals::DMA_CH0; -use embassy_rp::{bind_interrupts, Peri}; +use embassy_rp::{Peri, bind_interrupts}; use embassy_sync::blocking_mutex::raw::NoopRawMutex; use embassy_sync::zerocopy_channel::{Channel, Receiver, Sender}; use embassy_time::{Duration, Ticker, Timer}; diff --git a/examples/rp235x/Cargo.toml b/examples/rp235x/Cargo.toml index 40fbb5798..39a4f421a 100644 --- a/examples/rp235x/Cargo.toml +++ b/examples/rp235x/Cargo.toml @@ -1,5 +1,5 @@ [package] -edition = "2021" +edition = "2024" name = "embassy-rp2350-examples" version = "0.1.0" license = "MIT OR Apache-2.0" diff --git a/examples/rp235x/src/bin/assign_resources.rs b/examples/rp235x/src/bin/assign_resources.rs index 4ee4278b5..aaa134768 100644 --- a/examples/rp235x/src/bin/assign_resources.rs +++ b/examples/rp235x/src/bin/assign_resources.rs @@ -14,9 +14,9 @@ use assign_resources::assign_resources; use defmt::*; use embassy_executor::Spawner; +use embassy_rp::Peri; use embassy_rp::gpio::{Level, Output}; use embassy_rp::peripherals::{self, PIN_20, PIN_21}; -use embassy_rp::Peri; use embassy_time::Timer; use {defmt_rtt as _, panic_probe as _}; diff --git a/examples/rp235x/src/bin/debounce.rs b/examples/rp235x/src/bin/debounce.rs index 0077f19fc..6eeb01d0a 100644 --- a/examples/rp235x/src/bin/debounce.rs +++ b/examples/rp235x/src/bin/debounce.rs @@ -7,7 +7,7 @@ use defmt::info; use embassy_executor::Spawner; use embassy_rp::gpio::{Input, Level, Pull}; -use embassy_time::{with_deadline, Duration, Instant, Timer}; +use embassy_time::{Duration, Instant, Timer, with_deadline}; use {defmt_rtt as _, panic_probe as _}; pub struct Debouncer<'a> { diff --git a/examples/rp235x/src/bin/ethernet_w5500_icmp_ping.rs b/examples/rp235x/src/bin/ethernet_w5500_icmp_ping.rs index 309d3e4f7..227e68029 100644 --- a/examples/rp235x/src/bin/ethernet_w5500_icmp_ping.rs +++ b/examples/rp235x/src/bin/ethernet_w5500_icmp_ping.rs @@ -12,8 +12,8 @@ use core::str::FromStr; use defmt::*; use embassy_executor::Spawner; use embassy_futures::yield_now; -use embassy_net::icmp::ping::{PingManager, PingParams}; use embassy_net::icmp::PacketMetadata; +use embassy_net::icmp::ping::{PingManager, PingParams}; use embassy_net::{Ipv4Cidr, Stack, StackResources}; use embassy_net_wiznet::chip::W5500; use embassy_net_wiznet::*; @@ -99,7 +99,7 @@ async fn main(spawner: Spawner) { // Create the ping manager instance let mut ping_manager = PingManager::new(stack, &mut rx_meta, &mut rx_buffer, &mut tx_meta, &mut tx_buffer); let addr = "192.168.8.1"; // Address to ping to - // Create the PingParams with the target address + // Create the PingParams with the target address let mut ping_params = PingParams::new(Ipv4Addr::from_str(addr).unwrap()); // (optional) Set custom properties of the ping ping_params.set_payload(b"Hello, Ping!"); // custom payload diff --git a/examples/rp235x/src/bin/interrupt.rs b/examples/rp235x/src/bin/interrupt.rs index 88513180c..1b18f6931 100644 --- a/examples/rp235x/src/bin/interrupt.rs +++ b/examples/rp235x/src/bin/interrupt.rs @@ -16,8 +16,8 @@ use embassy_rp::adc::{self, Adc, Blocking}; use embassy_rp::gpio::Pull; use embassy_rp::interrupt; use embassy_rp::pwm::{Config, Pwm}; -use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex; use embassy_sync::blocking_mutex::Mutex; +use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex; use embassy_sync::channel::Channel; use embassy_time::{Duration, Ticker}; use portable_atomic::{AtomicU32, Ordering}; diff --git a/examples/rp235x/src/bin/multicore.rs b/examples/rp235x/src/bin/multicore.rs index 4f82801d6..9b61fdbca 100644 --- a/examples/rp235x/src/bin/multicore.rs +++ b/examples/rp235x/src/bin/multicore.rs @@ -8,7 +8,7 @@ use defmt::*; use embassy_executor::Executor; use embassy_rp::gpio::{Level, Output}; -use embassy_rp::multicore::{spawn_core1, Stack}; +use embassy_rp::multicore::{Stack, spawn_core1}; use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex; use embassy_sync::channel::Channel; use embassy_time::Timer; diff --git a/examples/rp235x/src/bin/multicore_stack_overflow.rs b/examples/rp235x/src/bin/multicore_stack_overflow.rs index dba44aa23..9efe89318 100644 --- a/examples/rp235x/src/bin/multicore_stack_overflow.rs +++ b/examples/rp235x/src/bin/multicore_stack_overflow.rs @@ -6,7 +6,7 @@ use defmt::*; use embassy_executor::Executor; use embassy_rp::gpio::{Level, Output}; -use embassy_rp::multicore::{spawn_core1, Stack}; +use embassy_rp::multicore::{Stack, spawn_core1}; use embassy_time::Timer; use static_cell::StaticCell; use {defmt_rtt as _, panic_probe as _}; diff --git a/examples/rp235x/src/bin/multiprio.rs b/examples/rp235x/src/bin/multiprio.rs index 96cdf8fb1..310047505 100644 --- a/examples/rp235x/src/bin/multiprio.rs +++ b/examples/rp235x/src/bin/multiprio.rs @@ -61,7 +61,7 @@ use defmt::{info, unwrap}; use embassy_executor::{Executor, InterruptExecutor}; use embassy_rp::interrupt; use embassy_rp::interrupt::{InterruptExt, Priority}; -use embassy_time::{Instant, Timer, TICK_HZ}; +use embassy_time::{Instant, TICK_HZ, Timer}; use static_cell::StaticCell; use {defmt_rtt as _, panic_probe as _}; @@ -113,12 +113,12 @@ static EXECUTOR_LOW: StaticCell = StaticCell::new(); #[interrupt] unsafe fn SWI_IRQ_1() { - EXECUTOR_HIGH.on_interrupt() + unsafe { EXECUTOR_HIGH.on_interrupt() } } #[interrupt] unsafe fn SWI_IRQ_0() { - EXECUTOR_MED.on_interrupt() + unsafe { EXECUTOR_MED.on_interrupt() } } #[entry] diff --git a/examples/rp235x/src/bin/overclock.rs b/examples/rp235x/src/bin/overclock.rs index 5fd97ef97..cba137f3a 100644 --- a/examples/rp235x/src/bin/overclock.rs +++ b/examples/rp235x/src/bin/overclock.rs @@ -12,7 +12,7 @@ use defmt::*; use embassy_executor::Spawner; -use embassy_rp::clocks::{clk_sys_freq, core_voltage, ClockConfig, CoreVoltage}; +use embassy_rp::clocks::{ClockConfig, CoreVoltage, clk_sys_freq, core_voltage}; use embassy_rp::config::Config; use embassy_rp::gpio::{Level, Output}; use embassy_time::{Duration, Instant, Timer}; diff --git a/examples/rp235x/src/bin/pio_async.rs b/examples/rp235x/src/bin/pio_async.rs index d76930f5c..a392fe37e 100644 --- a/examples/rp235x/src/bin/pio_async.rs +++ b/examples/rp235x/src/bin/pio_async.rs @@ -7,7 +7,7 @@ use embassy_executor::Spawner; use embassy_rp::peripherals::PIO0; use embassy_rp::pio::program::pio_asm; use embassy_rp::pio::{Common, Config, InterruptHandler, Irq, Pio, PioPin, ShiftDirection, StateMachine}; -use embassy_rp::{bind_interrupts, Peri}; +use embassy_rp::{Peri, bind_interrupts}; use fixed::traits::ToFixed; use fixed_macro::types::U56F8; use {defmt_rtt as _, panic_probe as _}; diff --git a/examples/rp235x/src/bin/pio_i2s_rx.rs b/examples/rp235x/src/bin/pio_i2s_rx.rs index c3f505b13..6735c402f 100644 --- a/examples/rp235x/src/bin/pio_i2s_rx.rs +++ b/examples/rp235x/src/bin/pio_i2s_rx.rs @@ -34,7 +34,7 @@ const SAMPLE_RATE: u32 = 48_000; const BIT_DEPTH: u32 = 16; const CHANNELS: u32 = 2; const USE_ONBOARD_PULLDOWN: bool = false; // whether or not to use the onboard pull-down resistor, - // which has documented issues on many RP235x boards +// which has documented issues on many RP235x boards #[embassy_executor::main] async fn main(_spawner: Spawner) { let p = embassy_rp::init(Default::default()); diff --git a/examples/rp235x/src/bin/pio_rotary_encoder_rxf.rs b/examples/rp235x/src/bin/pio_rotary_encoder_rxf.rs index 61af94560..948699e40 100644 --- a/examples/rp235x/src/bin/pio_rotary_encoder_rxf.rs +++ b/examples/rp235x/src/bin/pio_rotary_encoder_rxf.rs @@ -9,7 +9,7 @@ use embassy_executor::Spawner; use embassy_rp::gpio::Pull; use embassy_rp::peripherals::PIO0; use embassy_rp::pio::program::pio_asm; -use embassy_rp::{bind_interrupts, pio, Peri}; +use embassy_rp::{Peri, bind_interrupts, pio}; use embassy_time::Timer; use fixed::traits::ToFixed; use pio::{Common, Config, FifoJoin, Instance, InterruptHandler, Pio, PioPin, ShiftDirection, StateMachine}; diff --git a/examples/rp235x/src/bin/pio_stepper.rs b/examples/rp235x/src/bin/pio_stepper.rs index 931adbeda..9b33710ad 100644 --- a/examples/rp235x/src/bin/pio_stepper.rs +++ b/examples/rp235x/src/bin/pio_stepper.rs @@ -10,7 +10,7 @@ use embassy_rp::bind_interrupts; use embassy_rp::peripherals::PIO0; use embassy_rp::pio::{InterruptHandler, Pio}; use embassy_rp::pio_programs::stepper::{PioStepper, PioStepperProgram}; -use embassy_time::{with_timeout, Duration, Timer}; +use embassy_time::{Duration, Timer, with_timeout}; use {defmt_rtt as _, panic_probe as _}; bind_interrupts!(struct Irqs { diff --git a/examples/rp235x/src/bin/pwm.rs b/examples/rp235x/src/bin/pwm.rs index 289480c85..971e86aa7 100644 --- a/examples/rp235x/src/bin/pwm.rs +++ b/examples/rp235x/src/bin/pwm.rs @@ -9,9 +9,9 @@ use defmt::*; use embassy_executor::Spawner; -use embassy_rp::peripherals::{PIN_25, PIN_4, PWM_SLICE2, PWM_SLICE4}; -use embassy_rp::pwm::{Config, Pwm, SetDutyCycle}; use embassy_rp::Peri; +use embassy_rp::peripherals::{PIN_4, PIN_25, PWM_SLICE2, PWM_SLICE4}; +use embassy_rp::pwm::{Config, Pwm, SetDutyCycle}; use embassy_time::Timer; use {defmt_rtt as _, panic_probe as _}; diff --git a/examples/rp235x/src/bin/pwm_tb6612fng_motor_driver.rs b/examples/rp235x/src/bin/pwm_tb6612fng_motor_driver.rs index 2cfb2038d..670f302a4 100644 --- a/examples/rp235x/src/bin/pwm_tb6612fng_motor_driver.rs +++ b/examples/rp235x/src/bin/pwm_tb6612fng_motor_driver.rs @@ -10,7 +10,7 @@ use defmt::*; use embassy_executor::Spawner; use embassy_rp::config::Config; use embassy_rp::gpio::Output; -use embassy_rp::{gpio, peripherals, pwm, Peri}; +use embassy_rp::{Peri, gpio, peripherals, pwm}; use embassy_time::{Duration, Timer}; use tb6612fng::{DriveCommand, Motor, Tb6612fng}; use {defmt_rtt as _, panic_probe as _}; diff --git a/examples/rp235x/src/bin/sharing.rs b/examples/rp235x/src/bin/sharing.rs index d4c89946b..618ab9117 100644 --- a/examples/rp235x/src/bin/sharing.rs +++ b/examples/rp235x/src/bin/sharing.rs @@ -52,7 +52,7 @@ bind_interrupts!(struct Irqs { #[interrupt] unsafe fn SWI_IRQ_0() { - EXECUTOR_HI.on_interrupt() + unsafe { EXECUTOR_HI.on_interrupt() } } #[entry] diff --git a/examples/rp235x/src/bin/spi_display.rs b/examples/rp235x/src/bin/spi_display.rs index 9967abefd..3cef93f62 100644 --- a/examples/rp235x/src/bin/spi_display.rs +++ b/examples/rp235x/src/bin/spi_display.rs @@ -15,19 +15,19 @@ use embassy_executor::Spawner; use embassy_rp::gpio::{Level, Output}; use embassy_rp::spi; use embassy_rp::spi::{Blocking, Spi}; -use embassy_sync::blocking_mutex::raw::NoopRawMutex; use embassy_sync::blocking_mutex::Mutex; +use embassy_sync::blocking_mutex::raw::NoopRawMutex; use embassy_time::Delay; use embedded_graphics::image::{Image, ImageRawLE}; -use embedded_graphics::mono_font::ascii::FONT_10X20; use embedded_graphics::mono_font::MonoTextStyle; +use embedded_graphics::mono_font::ascii::FONT_10X20; use embedded_graphics::pixelcolor::Rgb565; use embedded_graphics::prelude::*; use embedded_graphics::primitives::{PrimitiveStyleBuilder, Rectangle}; use embedded_graphics::text::Text; +use mipidsi::Builder; use mipidsi::models::ST7789; use mipidsi::options::{Orientation, Rotation}; -use mipidsi::Builder; use {defmt_rtt as _, panic_probe as _}; use crate::touch::Touch; @@ -167,11 +167,7 @@ mod touch { let x = ((x - cal.x1) * cal.sx / (cal.x2 - cal.x1)).clamp(0, cal.sx); let y = ((y - cal.y1) * cal.sy / (cal.y2 - cal.y1)).clamp(0, cal.sy); - if x == 0 && y == 0 { - None - } else { - Some((x, y)) - } + if x == 0 && y == 0 { None } else { Some((x, y)) } } } } diff --git a/examples/rp235x/src/bin/uart_r503.rs b/examples/rp235x/src/bin/uart_r503.rs index 085be280b..a25d45b18 100644 --- a/examples/rp235x/src/bin/uart_r503.rs +++ b/examples/rp235x/src/bin/uart_r503.rs @@ -6,7 +6,7 @@ use embassy_executor::Spawner; use embassy_rp::bind_interrupts; use embassy_rp::peripherals::UART0; use embassy_rp::uart::{Config, DataBits, InterruptHandler as UARTInterruptHandler, Parity, StopBits, Uart}; -use embassy_time::{with_timeout, Duration, Timer}; +use embassy_time::{Duration, Timer, with_timeout}; use heapless::Vec; use {defmt_rtt as _, panic_probe as _}; diff --git a/examples/rp235x/src/bin/zerocopy.rs b/examples/rp235x/src/bin/zerocopy.rs index 62ba4cfb8..55deffd5f 100644 --- a/examples/rp235x/src/bin/zerocopy.rs +++ b/examples/rp235x/src/bin/zerocopy.rs @@ -11,7 +11,7 @@ use embassy_executor::Spawner; use embassy_rp::adc::{self, Adc, Async, Config, InterruptHandler}; use embassy_rp::gpio::Pull; use embassy_rp::peripherals::DMA_CH0; -use embassy_rp::{bind_interrupts, Peri}; +use embassy_rp::{Peri, bind_interrupts}; use embassy_sync::blocking_mutex::raw::NoopRawMutex; use embassy_sync::zerocopy_channel::{Channel, Receiver, Sender}; use embassy_time::{Duration, Ticker, Timer}; diff --git a/examples/std/Cargo.toml b/examples/std/Cargo.toml index 449c5ddca..6dc6a353d 100644 --- a/examples/std/Cargo.toml +++ b/examples/std/Cargo.toml @@ -1,5 +1,5 @@ [package] -edition = "2021" +edition = "2024" name = "embassy-std-examples" version = "0.1.0" license = "MIT OR Apache-2.0" diff --git a/examples/stm32c0/Cargo.toml b/examples/stm32c0/Cargo.toml index bb7b57496..696a95854 100644 --- a/examples/stm32c0/Cargo.toml +++ b/examples/stm32c0/Cargo.toml @@ -1,5 +1,5 @@ [package] -edition = "2021" +edition = "2024" name = "embassy-stm32c0-examples" version = "0.1.0" license = "MIT OR Apache-2.0" diff --git a/examples/stm32c0/src/bin/rtc.rs b/examples/stm32c0/src/bin/rtc.rs index 82d8a37ba..feb27f6d9 100644 --- a/examples/stm32c0/src/bin/rtc.rs +++ b/examples/stm32c0/src/bin/rtc.rs @@ -4,8 +4,8 @@ use chrono::{NaiveDate, NaiveDateTime}; use defmt::*; use embassy_executor::Spawner; -use embassy_stm32::rtc::{Rtc, RtcConfig}; use embassy_stm32::Config; +use embassy_stm32::rtc::{Rtc, RtcConfig}; use embassy_time::Timer; use {defmt_rtt as _, panic_probe as _}; diff --git a/examples/stm32f0/Cargo.toml b/examples/stm32f0/Cargo.toml index 11ecbe3c2..a78873d21 100644 --- a/examples/stm32f0/Cargo.toml +++ b/examples/stm32f0/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "embassy-stm32f0-examples" version = "0.1.0" -edition = "2021" +edition = "2024" license = "MIT OR Apache-2.0" publish = false diff --git a/examples/stm32f0/src/bin/button_controlled_blink.rs b/examples/stm32f0/src/bin/button_controlled_blink.rs index f232e3290..0b678af01 100644 --- a/examples/stm32f0/src/bin/button_controlled_blink.rs +++ b/examples/stm32f0/src/bin/button_controlled_blink.rs @@ -7,9 +7,9 @@ use core::sync::atomic::{AtomicU32, Ordering}; use defmt::info; use embassy_executor::Spawner; +use embassy_stm32::Peri; use embassy_stm32::exti::ExtiInput; use embassy_stm32::gpio::{AnyPin, Level, Output, Pull, Speed}; -use embassy_stm32::Peri; use embassy_time::Timer; use {defmt_rtt as _, panic_probe as _}; diff --git a/examples/stm32f0/src/bin/multiprio.rs b/examples/stm32f0/src/bin/multiprio.rs index b5244afc8..9a8dc5685 100644 --- a/examples/stm32f0/src/bin/multiprio.rs +++ b/examples/stm32f0/src/bin/multiprio.rs @@ -113,12 +113,12 @@ static EXECUTOR_LOW: StaticCell = StaticCell::new(); #[interrupt] unsafe fn USART1() { - EXECUTOR_HIGH.on_interrupt() + unsafe { EXECUTOR_HIGH.on_interrupt() } } #[interrupt] unsafe fn USART2() { - EXECUTOR_MED.on_interrupt() + unsafe { EXECUTOR_MED.on_interrupt() } } #[entry] diff --git a/examples/stm32f1/Cargo.toml b/examples/stm32f1/Cargo.toml index dcb58796b..5714b149a 100644 --- a/examples/stm32f1/Cargo.toml +++ b/examples/stm32f1/Cargo.toml @@ -1,5 +1,5 @@ [package] -edition = "2021" +edition = "2024" name = "embassy-stm32f1-examples" version = "0.1.0" license = "MIT OR Apache-2.0" diff --git a/examples/stm32f1/src/bin/can.rs b/examples/stm32f1/src/bin/can.rs index ad0c8a5a5..cbe13b206 100644 --- a/examples/stm32f1/src/bin/can.rs +++ b/examples/stm32f1/src/bin/can.rs @@ -5,11 +5,11 @@ use defmt::*; use embassy_executor::Spawner; use embassy_stm32::can::frame::Envelope; use embassy_stm32::can::{ - filter, Can, Fifo, Frame, Id, Rx0InterruptHandler, Rx1InterruptHandler, SceInterruptHandler, StandardId, - TxInterruptHandler, + Can, Fifo, Frame, Id, Rx0InterruptHandler, Rx1InterruptHandler, SceInterruptHandler, StandardId, + TxInterruptHandler, filter, }; use embassy_stm32::peripherals::CAN; -use embassy_stm32::{bind_interrupts, Config}; +use embassy_stm32::{Config, bind_interrupts}; use static_cell::StaticCell; use {defmt_rtt as _, panic_probe as _}; diff --git a/examples/stm32f1/src/bin/input_capture.rs b/examples/stm32f1/src/bin/input_capture.rs index b5b26938d..6232d8c17 100644 --- a/examples/stm32f1/src/bin/input_capture.rs +++ b/examples/stm32f1/src/bin/input_capture.rs @@ -7,7 +7,7 @@ use embassy_stm32::gpio::{AfioRemap, Level, Output, Pull, Speed}; use embassy_stm32::time::khz; use embassy_stm32::timer::input_capture::{CapturePin, InputCapture}; use embassy_stm32::timer::{self, Channel}; -use embassy_stm32::{bind_interrupts, peripherals, Peri}; +use embassy_stm32::{Peri, bind_interrupts, peripherals}; use embassy_time::Timer; use {defmt_rtt as _, panic_probe as _}; diff --git a/examples/stm32f1/src/bin/pwm_input.rs b/examples/stm32f1/src/bin/pwm_input.rs index 9ae747018..136c5c555 100644 --- a/examples/stm32f1/src/bin/pwm_input.rs +++ b/examples/stm32f1/src/bin/pwm_input.rs @@ -6,7 +6,7 @@ use embassy_executor::Spawner; use embassy_stm32::gpio::{AfioRemap, Level, Output, Pull, Speed}; use embassy_stm32::time::khz; use embassy_stm32::timer::pwm_input::PwmInput; -use embassy_stm32::{bind_interrupts, peripherals, timer, Peri}; +use embassy_stm32::{Peri, bind_interrupts, peripherals, timer}; use embassy_time::Timer; use {defmt_rtt as _, panic_probe as _}; diff --git a/examples/stm32f1/src/bin/usb_serial.rs b/examples/stm32f1/src/bin/usb_serial.rs index 77ec307b9..5ff54a521 100644 --- a/examples/stm32f1/src/bin/usb_serial.rs +++ b/examples/stm32f1/src/bin/usb_serial.rs @@ -7,11 +7,11 @@ use embassy_futures::join::join; use embassy_stm32::gpio::{Level, Output, Speed}; use embassy_stm32::time::Hertz; use embassy_stm32::usb::{Driver, Instance}; -use embassy_stm32::{bind_interrupts, peripherals, usb, Config}; +use embassy_stm32::{Config, bind_interrupts, peripherals, usb}; use embassy_time::Timer; +use embassy_usb::Builder; use embassy_usb::class::cdc_acm::{CdcAcmClass, State}; use embassy_usb::driver::EndpointError; -use embassy_usb::Builder; use {defmt_rtt as _, panic_probe as _}; bind_interrupts!(struct Irqs { diff --git a/examples/stm32f2/Cargo.toml b/examples/stm32f2/Cargo.toml index 498c20d84..f726018c3 100644 --- a/examples/stm32f2/Cargo.toml +++ b/examples/stm32f2/Cargo.toml @@ -1,5 +1,5 @@ [package] -edition = "2021" +edition = "2024" name = "embassy-stm32f2-examples" version = "0.1.0" license = "MIT OR Apache-2.0" diff --git a/examples/stm32f2/src/bin/pll.rs b/examples/stm32f2/src/bin/pll.rs index e39e2daec..4418bf502 100644 --- a/examples/stm32f2/src/bin/pll.rs +++ b/examples/stm32f2/src/bin/pll.rs @@ -3,8 +3,8 @@ use defmt::*; use embassy_executor::Spawner; -use embassy_stm32::time::Hertz; use embassy_stm32::Config; +use embassy_stm32::time::Hertz; use embassy_time::Timer; use {defmt_rtt as _, panic_probe as _}; diff --git a/examples/stm32f3/Cargo.toml b/examples/stm32f3/Cargo.toml index 23025ef0b..4349e8055 100644 --- a/examples/stm32f3/Cargo.toml +++ b/examples/stm32f3/Cargo.toml @@ -1,5 +1,5 @@ [package] -edition = "2021" +edition = "2024" name = "embassy-stm32f3-examples" version = "0.1.0" license = "MIT OR Apache-2.0" diff --git a/examples/stm32f3/src/bin/button_events.rs b/examples/stm32f3/src/bin/button_events.rs index a54d03212..99957a641 100644 --- a/examples/stm32f3/src/bin/button_events.rs +++ b/examples/stm32f3/src/bin/button_events.rs @@ -15,7 +15,7 @@ use embassy_stm32::exti::ExtiInput; use embassy_stm32::gpio::{Level, Output, Pull, Speed}; use embassy_sync::blocking_mutex::raw::ThreadModeRawMutex; use embassy_sync::channel::Channel; -use embassy_time::{with_timeout, Duration, Timer}; +use embassy_time::{Duration, Timer, with_timeout}; use {defmt_rtt as _, panic_probe as _}; struct Leds<'a> { diff --git a/examples/stm32f3/src/bin/multiprio.rs b/examples/stm32f3/src/bin/multiprio.rs index 2f2ffdea2..8375e0e8e 100644 --- a/examples/stm32f3/src/bin/multiprio.rs +++ b/examples/stm32f3/src/bin/multiprio.rs @@ -113,12 +113,12 @@ static EXECUTOR_LOW: StaticCell = StaticCell::new(); #[interrupt] unsafe fn UART4() { - EXECUTOR_HIGH.on_interrupt() + unsafe { EXECUTOR_HIGH.on_interrupt() } } #[interrupt] unsafe fn UART5() { - EXECUTOR_MED.on_interrupt() + unsafe { EXECUTOR_MED.on_interrupt() } } #[entry] diff --git a/examples/stm32f3/src/bin/usb_serial.rs b/examples/stm32f3/src/bin/usb_serial.rs index 5760f2c1c..58b801c36 100644 --- a/examples/stm32f3/src/bin/usb_serial.rs +++ b/examples/stm32f3/src/bin/usb_serial.rs @@ -7,11 +7,11 @@ use embassy_futures::join::join; use embassy_stm32::gpio::{Level, Output, Speed}; use embassy_stm32::time::mhz; use embassy_stm32::usb::{Driver, Instance}; -use embassy_stm32::{bind_interrupts, peripherals, usb, Config}; +use embassy_stm32::{Config, bind_interrupts, peripherals, usb}; use embassy_time::Timer; +use embassy_usb::Builder; use embassy_usb::class::cdc_acm::{CdcAcmClass, State}; use embassy_usb::driver::EndpointError; -use embassy_usb::Builder; use {defmt_rtt as _, panic_probe as _}; bind_interrupts!(struct Irqs { diff --git a/examples/stm32f334/Cargo.toml b/examples/stm32f334/Cargo.toml index 3495b118c..cf22633dd 100644 --- a/examples/stm32f334/Cargo.toml +++ b/examples/stm32f334/Cargo.toml @@ -1,5 +1,5 @@ [package] -edition = "2021" +edition = "2024" name = "embassy-stm32f334-examples" version = "0.1.0" license = "MIT OR Apache-2.0" diff --git a/examples/stm32f334/src/bin/adc.rs b/examples/stm32f334/src/bin/adc.rs index 0528a9637..a993b00ca 100644 --- a/examples/stm32f334/src/bin/adc.rs +++ b/examples/stm32f334/src/bin/adc.rs @@ -6,7 +6,7 @@ use embassy_executor::Spawner; use embassy_stm32::adc::{Adc, SampleTime}; use embassy_stm32::peripherals::ADC1; use embassy_stm32::time::mhz; -use embassy_stm32::{adc, bind_interrupts, Config}; +use embassy_stm32::{Config, adc, bind_interrupts}; use embassy_time::Timer; use {defmt_rtt as _, panic_probe as _}; diff --git a/examples/stm32f334/src/bin/opamp.rs b/examples/stm32f334/src/bin/opamp.rs index c344935d7..3e621f2a1 100644 --- a/examples/stm32f334/src/bin/opamp.rs +++ b/examples/stm32f334/src/bin/opamp.rs @@ -7,7 +7,7 @@ use embassy_stm32::adc::{Adc, SampleTime}; use embassy_stm32::opamp::OpAmp; use embassy_stm32::peripherals::ADC2; use embassy_stm32::time::mhz; -use embassy_stm32::{adc, bind_interrupts, Config}; +use embassy_stm32::{Config, adc, bind_interrupts}; use embassy_time::Timer; use {defmt_rtt as _, panic_probe as _}; diff --git a/examples/stm32f334/src/bin/pwm.rs b/examples/stm32f334/src/bin/pwm.rs index 2b0686121..68a61ae22 100644 --- a/examples/stm32f334/src/bin/pwm.rs +++ b/examples/stm32f334/src/bin/pwm.rs @@ -3,9 +3,9 @@ use defmt::*; use embassy_executor::Spawner; +use embassy_stm32::Config; use embassy_stm32::hrtim::*; use embassy_stm32::time::{khz, mhz}; -use embassy_stm32::Config; use embassy_time::Timer; use {defmt_rtt as _, panic_probe as _}; diff --git a/examples/stm32f4/Cargo.toml b/examples/stm32f4/Cargo.toml index fb5f86aac..d06b7505c 100644 --- a/examples/stm32f4/Cargo.toml +++ b/examples/stm32f4/Cargo.toml @@ -1,5 +1,5 @@ [package] -edition = "2021" +edition = "2024" name = "embassy-stm32f4-examples" version = "0.1.0" license = "MIT OR Apache-2.0" diff --git a/examples/stm32f4/src/bin/adc_dma.rs b/examples/stm32f4/src/bin/adc_dma.rs index 2ec48640e..c24f01753 100644 --- a/examples/stm32f4/src/bin/adc_dma.rs +++ b/examples/stm32f4/src/bin/adc_dma.rs @@ -3,8 +3,8 @@ use cortex_m::singleton; use defmt::*; use embassy_executor::Spawner; -use embassy_stm32::adc::{Adc, RingBufferedAdc, SampleTime, Sequence}; use embassy_stm32::Peripherals; +use embassy_stm32::adc::{Adc, RingBufferedAdc, SampleTime, Sequence}; use embassy_time::Instant; use {defmt_rtt as _, panic_probe as _}; diff --git a/examples/stm32f4/src/bin/eth.rs b/examples/stm32f4/src/bin/eth.rs index f41a60529..2d72b6b0b 100644 --- a/examples/stm32f4/src/bin/eth.rs +++ b/examples/stm32f4/src/bin/eth.rs @@ -9,7 +9,7 @@ use embassy_stm32::eth::{Ethernet, GenericPhy, PacketQueue}; use embassy_stm32::peripherals::ETH; use embassy_stm32::rng::Rng; use embassy_stm32::time::Hertz; -use embassy_stm32::{bind_interrupts, eth, peripherals, rng, Config}; +use embassy_stm32::{Config, bind_interrupts, eth, peripherals, rng}; use embassy_time::Timer; use embedded_io_async::Write; use static_cell::StaticCell; diff --git a/examples/stm32f4/src/bin/eth_compliance_test.rs b/examples/stm32f4/src/bin/eth_compliance_test.rs index 52f9d57f6..734a14c2c 100644 --- a/examples/stm32f4/src/bin/eth_compliance_test.rs +++ b/examples/stm32f4/src/bin/eth_compliance_test.rs @@ -5,7 +5,7 @@ use defmt::*; use embassy_executor::Spawner; use embassy_stm32::eth::{Ethernet, GenericPhy, PacketQueue, StationManagement}; use embassy_stm32::time::Hertz; -use embassy_stm32::{bind_interrupts, eth, peripherals, rng, Config}; +use embassy_stm32::{Config, bind_interrupts, eth, peripherals, rng}; use embassy_time::Timer; use static_cell::StaticCell; use {defmt_rtt as _, panic_probe as _}; diff --git a/examples/stm32f4/src/bin/eth_w5500.rs b/examples/stm32f4/src/bin/eth_w5500.rs index 7ce3bfe75..cccf20949 100644 --- a/examples/stm32f4/src/bin/eth_w5500.rs +++ b/examples/stm32f4/src/bin/eth_w5500.rs @@ -13,7 +13,7 @@ use embassy_stm32::mode::Async; use embassy_stm32::rng::Rng; use embassy_stm32::spi::Spi; use embassy_stm32::time::Hertz; -use embassy_stm32::{bind_interrupts, peripherals, rng, spi, Config}; +use embassy_stm32::{Config, bind_interrupts, peripherals, rng, spi}; use embassy_time::{Delay, Timer}; use embedded_hal_bus::spi::ExclusiveDevice; use embedded_io_async::Write; diff --git a/examples/stm32f4/src/bin/flash_async.rs b/examples/stm32f4/src/bin/flash_async.rs index 2feb9de09..14f029747 100644 --- a/examples/stm32f4/src/bin/flash_async.rs +++ b/examples/stm32f4/src/bin/flash_async.rs @@ -5,7 +5,7 @@ use defmt::{info, unwrap}; use embassy_executor::Spawner; use embassy_stm32::flash::{Flash, InterruptHandler}; use embassy_stm32::gpio::{AnyPin, Level, Output, Speed}; -use embassy_stm32::{bind_interrupts, Peri}; +use embassy_stm32::{Peri, bind_interrupts}; use embassy_time::Timer; use {defmt_rtt as _, panic_probe as _}; diff --git a/examples/stm32f4/src/bin/input_capture.rs b/examples/stm32f4/src/bin/input_capture.rs index 9998c4733..3ff96584d 100644 --- a/examples/stm32f4/src/bin/input_capture.rs +++ b/examples/stm32f4/src/bin/input_capture.rs @@ -7,7 +7,7 @@ use embassy_stm32::gpio::{Level, Output, Pull, Speed}; use embassy_stm32::time::khz; use embassy_stm32::timer::input_capture::{CapturePin, InputCapture}; use embassy_stm32::timer::{self, Channel}; -use embassy_stm32::{bind_interrupts, peripherals, Peri}; +use embassy_stm32::{Peri, bind_interrupts, peripherals}; use embassy_time::Timer; use {defmt_rtt as _, panic_probe as _}; diff --git a/examples/stm32f4/src/bin/multiprio.rs b/examples/stm32f4/src/bin/multiprio.rs index 2f2ffdea2..8375e0e8e 100644 --- a/examples/stm32f4/src/bin/multiprio.rs +++ b/examples/stm32f4/src/bin/multiprio.rs @@ -113,12 +113,12 @@ static EXECUTOR_LOW: StaticCell = StaticCell::new(); #[interrupt] unsafe fn UART4() { - EXECUTOR_HIGH.on_interrupt() + unsafe { EXECUTOR_HIGH.on_interrupt() } } #[interrupt] unsafe fn UART5() { - EXECUTOR_MED.on_interrupt() + unsafe { EXECUTOR_MED.on_interrupt() } } #[entry] diff --git a/examples/stm32f4/src/bin/pwm_complementary.rs b/examples/stm32f4/src/bin/pwm_complementary.rs index c981f1a76..50008a37b 100644 --- a/examples/stm32f4/src/bin/pwm_complementary.rs +++ b/examples/stm32f4/src/bin/pwm_complementary.rs @@ -5,9 +5,9 @@ use defmt::*; use embassy_executor::Spawner; use embassy_stm32::gpio::OutputType; use embassy_stm32::time::khz; +use embassy_stm32::timer::Channel; use embassy_stm32::timer::complementary_pwm::{ComplementaryPwm, ComplementaryPwmPin}; use embassy_stm32::timer::simple_pwm::PwmPin; -use embassy_stm32::timer::Channel; use embassy_time::Timer; use {defmt_rtt as _, panic_probe as _}; diff --git a/examples/stm32f4/src/bin/pwm_input.rs b/examples/stm32f4/src/bin/pwm_input.rs index e8bfa524f..d8ea56a34 100644 --- a/examples/stm32f4/src/bin/pwm_input.rs +++ b/examples/stm32f4/src/bin/pwm_input.rs @@ -6,7 +6,7 @@ use embassy_executor::Spawner; use embassy_stm32::gpio::{Level, Output, Pull, Speed}; use embassy_stm32::time::khz; use embassy_stm32::timer::pwm_input::PwmInput; -use embassy_stm32::{bind_interrupts, peripherals, timer, Peri}; +use embassy_stm32::{Peri, bind_interrupts, peripherals, timer}; use embassy_time::Timer; use {defmt_rtt as _, panic_probe as _}; diff --git a/examples/stm32f4/src/bin/rtc.rs b/examples/stm32f4/src/bin/rtc.rs index 82d8a37ba..feb27f6d9 100644 --- a/examples/stm32f4/src/bin/rtc.rs +++ b/examples/stm32f4/src/bin/rtc.rs @@ -4,8 +4,8 @@ use chrono::{NaiveDate, NaiveDateTime}; use defmt::*; use embassy_executor::Spawner; -use embassy_stm32::rtc::{Rtc, RtcConfig}; use embassy_stm32::Config; +use embassy_stm32::rtc::{Rtc, RtcConfig}; use embassy_time::Timer; use {defmt_rtt as _, panic_probe as _}; diff --git a/examples/stm32f4/src/bin/sdmmc.rs b/examples/stm32f4/src/bin/sdmmc.rs index e97b63925..fe0f887bf 100644 --- a/examples/stm32f4/src/bin/sdmmc.rs +++ b/examples/stm32f4/src/bin/sdmmc.rs @@ -4,8 +4,8 @@ use defmt::*; use embassy_executor::Spawner; use embassy_stm32::sdmmc::{DataBlock, Sdmmc}; -use embassy_stm32::time::{mhz, Hertz}; -use embassy_stm32::{bind_interrupts, peripherals, sdmmc, Config}; +use embassy_stm32::time::{Hertz, mhz}; +use embassy_stm32::{Config, bind_interrupts, peripherals, sdmmc}; use {defmt_rtt as _, panic_probe as _}; /// This is a safeguard to not overwrite any data on the SD card. diff --git a/examples/stm32f4/src/bin/usb_ethernet.rs b/examples/stm32f4/src/bin/usb_ethernet.rs index 7abbe8719..a5e625edd 100644 --- a/examples/stm32f4/src/bin/usb_ethernet.rs +++ b/examples/stm32f4/src/bin/usb_ethernet.rs @@ -3,12 +3,12 @@ use defmt::*; use embassy_executor::Spawner; -use embassy_net::tcp::TcpSocket; use embassy_net::StackResources; +use embassy_net::tcp::TcpSocket; use embassy_stm32::rng::{self, Rng}; use embassy_stm32::time::Hertz; use embassy_stm32::usb::Driver; -use embassy_stm32::{bind_interrupts, peripherals, usb, Config}; +use embassy_stm32::{Config, bind_interrupts, peripherals, usb}; 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}; diff --git a/examples/stm32f4/src/bin/usb_hid_keyboard.rs b/examples/stm32f4/src/bin/usb_hid_keyboard.rs index 86b6fa95f..9971e43f5 100644 --- a/examples/stm32f4/src/bin/usb_hid_keyboard.rs +++ b/examples/stm32f4/src/bin/usb_hid_keyboard.rs @@ -10,7 +10,7 @@ use embassy_stm32::exti::ExtiInput; use embassy_stm32::gpio::Pull; use embassy_stm32::time::Hertz; use embassy_stm32::usb::Driver; -use embassy_stm32::{bind_interrupts, peripherals, usb, Config}; +use embassy_stm32::{Config, bind_interrupts, peripherals, usb}; use embassy_usb::class::hid::{ HidBootProtocol, HidProtocolMode, HidReaderWriter, HidSubclass, ReportId, RequestHandler, State, }; diff --git a/examples/stm32f4/src/bin/usb_hid_mouse.rs b/examples/stm32f4/src/bin/usb_hid_mouse.rs index 977db4c15..e83d01f88 100644 --- a/examples/stm32f4/src/bin/usb_hid_mouse.rs +++ b/examples/stm32f4/src/bin/usb_hid_mouse.rs @@ -8,13 +8,13 @@ use embassy_executor::Spawner; use embassy_futures::join::join; use embassy_stm32::time::Hertz; use embassy_stm32::usb::Driver; -use embassy_stm32::{bind_interrupts, peripherals, usb, Config}; +use embassy_stm32::{Config, bind_interrupts, peripherals, usb}; use embassy_time::Timer; +use embassy_usb::Builder; use embassy_usb::class::hid::{ HidBootProtocol, HidProtocolMode, HidSubclass, HidWriter, ReportId, RequestHandler, State, }; use embassy_usb::control::OutResponse; -use embassy_usb::Builder; use usbd_hid::descriptor::{MouseReport, SerializedDescriptor}; use {defmt_rtt as _, panic_probe as _}; diff --git a/examples/stm32f4/src/bin/usb_raw.rs b/examples/stm32f4/src/bin/usb_raw.rs index bbbcc082b..511f0b281 100644 --- a/examples/stm32f4/src/bin/usb_raw.rs +++ b/examples/stm32f4/src/bin/usb_raw.rs @@ -53,7 +53,7 @@ use defmt::*; use embassy_executor::Spawner; use embassy_stm32::time::Hertz; use embassy_stm32::usb::Driver; -use embassy_stm32::{bind_interrupts, peripherals, usb, Config}; +use embassy_stm32::{Config, bind_interrupts, peripherals, usb}; use embassy_usb::control::{InResponse, OutResponse, Recipient, Request, RequestType}; use embassy_usb::msos::{self, windows_version}; use embassy_usb::types::InterfaceNumber; diff --git a/examples/stm32f4/src/bin/usb_serial.rs b/examples/stm32f4/src/bin/usb_serial.rs index e62b2d8d6..2e81e0a59 100644 --- a/examples/stm32f4/src/bin/usb_serial.rs +++ b/examples/stm32f4/src/bin/usb_serial.rs @@ -6,10 +6,10 @@ use embassy_executor::Spawner; use embassy_futures::join::join; use embassy_stm32::time::Hertz; use embassy_stm32::usb::{Driver, Instance}; -use embassy_stm32::{bind_interrupts, peripherals, usb, Config}; +use embassy_stm32::{Config, bind_interrupts, peripherals, usb}; +use embassy_usb::Builder; use embassy_usb::class::cdc_acm::{CdcAcmClass, State}; use embassy_usb::driver::EndpointError; -use embassy_usb::Builder; use {defmt_rtt as _, panic_probe as _}; bind_interrupts!(struct Irqs { diff --git a/examples/stm32f4/src/bin/usb_uac_speaker.rs b/examples/stm32f4/src/bin/usb_uac_speaker.rs index 79bd2d914..b92f4531e 100644 --- a/examples/stm32f4/src/bin/usb_uac_speaker.rs +++ b/examples/stm32f4/src/bin/usb_uac_speaker.rs @@ -6,9 +6,9 @@ use core::cell::{Cell, RefCell}; use defmt::{panic, *}; use embassy_executor::Spawner; use embassy_stm32::time::Hertz; -use embassy_stm32::{bind_interrupts, interrupt, peripherals, timer, usb, Config}; -use embassy_sync::blocking_mutex::raw::{CriticalSectionRawMutex, NoopRawMutex}; +use embassy_stm32::{Config, bind_interrupts, interrupt, peripherals, timer, usb}; use embassy_sync::blocking_mutex::Mutex; +use embassy_sync::blocking_mutex::raw::{CriticalSectionRawMutex, NoopRawMutex}; use embassy_sync::signal::Signal; use embassy_sync::zerocopy_channel; use embassy_usb::class::uac1; diff --git a/examples/stm32f4/src/bin/ws2812_pwm.rs b/examples/stm32f4/src/bin/ws2812_pwm.rs index 5153e1cfd..ccfd0661e 100644 --- a/examples/stm32f4/src/bin/ws2812_pwm.rs +++ b/examples/stm32f4/src/bin/ws2812_pwm.rs @@ -15,9 +15,9 @@ use embassy_executor::Spawner; use embassy_stm32::gpio::OutputType; use embassy_stm32::time::khz; +use embassy_stm32::timer::Channel; use embassy_stm32::timer::low_level::CountingMode; use embassy_stm32::timer::simple_pwm::{PwmPin, SimplePwm}; -use embassy_stm32::timer::Channel; use embassy_time::{Duration, Ticker, Timer}; use {defmt_rtt as _, panic_probe as _}; diff --git a/examples/stm32f469/Cargo.toml b/examples/stm32f469/Cargo.toml index f1d0e411a..5216e19b4 100644 --- a/examples/stm32f469/Cargo.toml +++ b/examples/stm32f469/Cargo.toml @@ -1,5 +1,5 @@ [package] -edition = "2021" +edition = "2024" name = "embassy-stm32f469-examples" version = "0.1.0" license = "MIT OR Apache-2.0" diff --git a/examples/stm32f469/src/bin/dsi_bsp.rs b/examples/stm32f469/src/bin/dsi_bsp.rs index 3a24d5dcf..d659291ff 100644 --- a/examples/stm32f469/src/bin/dsi_bsp.rs +++ b/examples/stm32f469/src/bin/dsi_bsp.rs @@ -3,7 +3,7 @@ use defmt::*; use embassy_executor::Spawner; -use embassy_stm32::dsihost::{blocking_delay_ms, DsiHost, PacketType}; +use embassy_stm32::dsihost::{DsiHost, PacketType, blocking_delay_ms}; use embassy_stm32::gpio::{Level, Output, Speed}; use embassy_stm32::ltdc::Ltdc; use embassy_stm32::pac::dsihost::regs::{Ier0, Ier1}; @@ -211,7 +211,7 @@ async fn main(_spawner: Spawner) { const HORIZONTAL_SYNC_ACTIVE: u16 = 4; // ((HSA as u32 * LANE_BYTE_CLK_K_HZ as u32 ) / LCD_CLOCK as u32 ) as u16; const HORIZONTAL_BACK_PORCH: u16 = 77; //((HBP as u32 * LANE_BYTE_CLK_K_HZ as u32 ) / LCD_CLOCK as u32) as u16; const HORIZONTAL_LINE: u16 = 1982; //(((HACT + HSA + HBP + HFP) as u32 * LANE_BYTE_CLK_K_HZ as u32 ) / LCD_CLOCK as u32 ) as u16; /* Value depending on display orientation choice portrait/landscape */ - // FIXME: Make depend on orientation + // FIXME: Make depend on orientation const VERTICAL_SYNC_ACTIVE: u16 = VSA; const VERTICAL_BACK_PORCH: u16 = VBP; const VERTICAL_FRONT_PORCH: u16 = VFP; @@ -658,7 +658,7 @@ const NT35510_RASET_LANDSCAPE: &[u8] = &[NT35510_CMD_RASET, 0x00, 0x00, 0x01, 0x const NT35510_WRITES_26: &[u8] = &[NT35510_CMD_TEEON, 0x00]; // Tear on const NT35510_WRITES_27: &[u8] = &[NT35510_CMD_SLPOUT, 0x00]; // Sleep out - // 28,29 missing +// 28,29 missing const NT35510_WRITES_30: &[u8] = &[NT35510_CMD_DISPON, 0x00]; // Display on const NT35510_WRITES_31: &[u8] = &[NT35510_CMD_WRDISBV, 0x7F]; diff --git a/examples/stm32f7/Cargo.toml b/examples/stm32f7/Cargo.toml index 5d7763334..565277394 100644 --- a/examples/stm32f7/Cargo.toml +++ b/examples/stm32f7/Cargo.toml @@ -1,5 +1,5 @@ [package] -edition = "2021" +edition = "2024" name = "embassy-stm32f7-examples" version = "0.1.0" license = "MIT OR Apache-2.0" diff --git a/examples/stm32f7/src/bin/can.rs b/examples/stm32f7/src/bin/can.rs index 9a91ac814..2f3f6db84 100644 --- a/examples/stm32f7/src/bin/can.rs +++ b/examples/stm32f7/src/bin/can.rs @@ -1,7 +1,7 @@ #![no_std] #![no_main] -use core::num::{NonZeroU16, NonZeroU8}; +use core::num::{NonZeroU8, NonZeroU16}; use defmt::*; use embassy_executor::Spawner; diff --git a/examples/stm32f7/src/bin/cryp.rs b/examples/stm32f7/src/bin/cryp.rs index a31e9b4f2..9ccef0b82 100644 --- a/examples/stm32f7/src/bin/cryp.rs +++ b/examples/stm32f7/src/bin/cryp.rs @@ -1,13 +1,13 @@ #![no_std] #![no_main] +use aes_gcm::Aes128Gcm; use aes_gcm::aead::heapless::Vec; use aes_gcm::aead::{AeadInPlace, KeyInit}; -use aes_gcm::Aes128Gcm; use defmt::info; use embassy_executor::Spawner; use embassy_stm32::cryp::{self, *}; -use embassy_stm32::{bind_interrupts, peripherals, Config}; +use embassy_stm32::{Config, bind_interrupts, peripherals}; use embassy_time::Instant; use {defmt_rtt as _, panic_probe as _}; diff --git a/examples/stm32f7/src/bin/eth.rs b/examples/stm32f7/src/bin/eth.rs index b13b7bdda..f8a129239 100644 --- a/examples/stm32f7/src/bin/eth.rs +++ b/examples/stm32f7/src/bin/eth.rs @@ -9,7 +9,7 @@ use embassy_stm32::eth::{Ethernet, GenericPhy, PacketQueue}; use embassy_stm32::peripherals::ETH; use embassy_stm32::rng::Rng; use embassy_stm32::time::Hertz; -use embassy_stm32::{bind_interrupts, eth, peripherals, rng, Config}; +use embassy_stm32::{Config, bind_interrupts, eth, peripherals, rng}; use embassy_time::Timer; use embedded_io_async::Write; use static_cell::StaticCell; diff --git a/examples/stm32f7/src/bin/hash.rs b/examples/stm32f7/src/bin/hash.rs index c2d1a7158..4fd465df6 100644 --- a/examples/stm32f7/src/bin/hash.rs +++ b/examples/stm32f7/src/bin/hash.rs @@ -4,7 +4,7 @@ use defmt::info; use embassy_executor::Spawner; use embassy_stm32::hash::*; -use embassy_stm32::{bind_interrupts, hash, peripherals, Config}; +use embassy_stm32::{Config, bind_interrupts, hash, peripherals}; use embassy_time::Instant; use hmac::{Hmac, Mac}; use sha2::{Digest, Sha256}; diff --git a/examples/stm32f7/src/bin/qspi.rs b/examples/stm32f7/src/bin/qspi.rs index 80652b865..e8ef3ad81 100644 --- a/examples/stm32f7/src/bin/qspi.rs +++ b/examples/stm32f7/src/bin/qspi.rs @@ -4,11 +4,11 @@ use defmt::info; use embassy_executor::Spawner; +use embassy_stm32::Config as StmCfg; use embassy_stm32::mode::Async; use embassy_stm32::qspi::enums::{AddressSize, ChipSelectHighTime, FIFOThresholdLevel, MemorySize, *}; use embassy_stm32::qspi::{Config as QspiCfg, Instance, Qspi, TransferConfig}; use embassy_stm32::time::mhz; -use embassy_stm32::Config as StmCfg; use {defmt_rtt as _, panic_probe as _}; const MEMORY_PAGE_SIZE: usize = 256; diff --git a/examples/stm32f7/src/bin/sdmmc.rs b/examples/stm32f7/src/bin/sdmmc.rs index 787bef25e..8809b5d0c 100644 --- a/examples/stm32f7/src/bin/sdmmc.rs +++ b/examples/stm32f7/src/bin/sdmmc.rs @@ -4,8 +4,8 @@ use defmt::*; use embassy_executor::Spawner; use embassy_stm32::sdmmc::Sdmmc; -use embassy_stm32::time::{mhz, Hertz}; -use embassy_stm32::{bind_interrupts, peripherals, sdmmc, Config}; +use embassy_stm32::time::{Hertz, mhz}; +use embassy_stm32::{Config, bind_interrupts, peripherals, sdmmc}; use {defmt_rtt as _, panic_probe as _}; bind_interrupts!(struct Irqs { diff --git a/examples/stm32f7/src/bin/usb_serial.rs b/examples/stm32f7/src/bin/usb_serial.rs index 349012888..9a30b2c20 100644 --- a/examples/stm32f7/src/bin/usb_serial.rs +++ b/examples/stm32f7/src/bin/usb_serial.rs @@ -6,10 +6,10 @@ use embassy_executor::Spawner; use embassy_futures::join::join; use embassy_stm32::time::Hertz; use embassy_stm32::usb::{Driver, Instance}; -use embassy_stm32::{bind_interrupts, peripherals, usb, Config}; +use embassy_stm32::{Config, bind_interrupts, peripherals, usb}; +use embassy_usb::Builder; use embassy_usb::class::cdc_acm::{CdcAcmClass, State}; use embassy_usb::driver::EndpointError; -use embassy_usb::Builder; use {defmt_rtt as _, panic_probe as _}; bind_interrupts!(struct Irqs { diff --git a/examples/stm32g0/Cargo.toml b/examples/stm32g0/Cargo.toml index 1c9451469..16f28500d 100644 --- a/examples/stm32g0/Cargo.toml +++ b/examples/stm32g0/Cargo.toml @@ -1,5 +1,5 @@ [package] -edition = "2021" +edition = "2024" name = "embassy-stm32g0-examples" version = "0.1.0" license = "MIT OR Apache-2.0" diff --git a/examples/stm32g0/src/bin/hf_timer.rs b/examples/stm32g0/src/bin/hf_timer.rs index 705905f01..88ee7ea86 100644 --- a/examples/stm32g0/src/bin/hf_timer.rs +++ b/examples/stm32g0/src/bin/hf_timer.rs @@ -3,12 +3,12 @@ use defmt::info; use embassy_executor::Spawner; +use embassy_stm32::Config as PeripheralConfig; use embassy_stm32::gpio::OutputType; use embassy_stm32::time::khz; +use embassy_stm32::timer::Channel; use embassy_stm32::timer::complementary_pwm::{ComplementaryPwm, ComplementaryPwmPin}; use embassy_stm32::timer::simple_pwm::PwmPin; -use embassy_stm32::timer::Channel; -use embassy_stm32::Config as PeripheralConfig; use {defmt_rtt as _, panic_probe as _}; #[embassy_executor::main] diff --git a/examples/stm32g0/src/bin/input_capture.rs b/examples/stm32g0/src/bin/input_capture.rs index 5501a6941..ca3e8eb41 100644 --- a/examples/stm32g0/src/bin/input_capture.rs +++ b/examples/stm32g0/src/bin/input_capture.rs @@ -13,10 +13,10 @@ use defmt::*; use embassy_executor::Spawner; use embassy_stm32::gpio::{Level, Output, OutputType, Pull, Speed}; use embassy_stm32::time::khz; +use embassy_stm32::timer::Channel; use embassy_stm32::timer::input_capture::{CapturePin, InputCapture}; use embassy_stm32::timer::simple_pwm::{PwmPin, SimplePwm}; -use embassy_stm32::timer::Channel; -use embassy_stm32::{bind_interrupts, peripherals, timer, Peri}; +use embassy_stm32::{Peri, bind_interrupts, peripherals, timer}; use embassy_time::Timer; use {defmt_rtt as _, panic_probe as _}; diff --git a/examples/stm32g0/src/bin/pwm_complementary.rs b/examples/stm32g0/src/bin/pwm_complementary.rs index dbd9194c9..9856dd953 100644 --- a/examples/stm32g0/src/bin/pwm_complementary.rs +++ b/examples/stm32g0/src/bin/pwm_complementary.rs @@ -17,9 +17,9 @@ use defmt::info; use embassy_executor::Spawner; use embassy_stm32::gpio::OutputType; use embassy_stm32::time::khz; +use embassy_stm32::timer::Channel; use embassy_stm32::timer::complementary_pwm::{ComplementaryPwm, ComplementaryPwmPin}; use embassy_stm32::timer::simple_pwm::PwmPin; -use embassy_stm32::timer::Channel; use {defmt_rtt as _, panic_probe as _}; #[embassy_executor::main] diff --git a/examples/stm32g0/src/bin/pwm_input.rs b/examples/stm32g0/src/bin/pwm_input.rs index 72aa07c03..5da19e077 100644 --- a/examples/stm32g0/src/bin/pwm_input.rs +++ b/examples/stm32g0/src/bin/pwm_input.rs @@ -14,7 +14,7 @@ use embassy_stm32::gpio::{Level, Output, OutputType, Pull, Speed}; use embassy_stm32::time::khz; use embassy_stm32::timer::pwm_input::PwmInput; use embassy_stm32::timer::simple_pwm::{PwmPin, SimplePwm}; -use embassy_stm32::{bind_interrupts, peripherals, timer, Peri}; +use embassy_stm32::{Peri, bind_interrupts, peripherals, timer}; use embassy_time::Timer; use {defmt_rtt as _, panic_probe as _}; diff --git a/examples/stm32g0/src/bin/rtc.rs b/examples/stm32g0/src/bin/rtc.rs index 50fb6398e..21da204cc 100644 --- a/examples/stm32g0/src/bin/rtc.rs +++ b/examples/stm32g0/src/bin/rtc.rs @@ -3,8 +3,8 @@ use defmt::*; use embassy_executor::Spawner; -use embassy_stm32::rtc::{DateTime, DayOfWeek, Rtc, RtcConfig}; use embassy_stm32::Config; +use embassy_stm32::rtc::{DateTime, DayOfWeek, Rtc, RtcConfig}; use embassy_time::Timer; use {defmt_rtt as _, panic_probe as _}; diff --git a/examples/stm32g0/src/bin/usb_serial.rs b/examples/stm32g0/src/bin/usb_serial.rs index 162dfd86b..7dab393ac 100644 --- a/examples/stm32g0/src/bin/usb_serial.rs +++ b/examples/stm32g0/src/bin/usb_serial.rs @@ -5,10 +5,10 @@ use defmt::{panic, *}; use embassy_executor::Spawner; use embassy_futures::join::join; use embassy_stm32::usb::{Driver, Instance}; -use embassy_stm32::{bind_interrupts, peripherals, usb, Config}; +use embassy_stm32::{Config, bind_interrupts, peripherals, usb}; +use embassy_usb::Builder; use embassy_usb::class::cdc_acm::{CdcAcmClass, State}; use embassy_usb::driver::EndpointError; -use embassy_usb::Builder; use {defmt_rtt as _, panic_probe as _}; bind_interrupts!(struct Irqs { diff --git a/examples/stm32g4/Cargo.toml b/examples/stm32g4/Cargo.toml index 102960980..6fd282d6d 100644 --- a/examples/stm32g4/Cargo.toml +++ b/examples/stm32g4/Cargo.toml @@ -1,5 +1,5 @@ [package] -edition = "2021" +edition = "2024" name = "embassy-stm32g4-examples" version = "0.1.0" license = "MIT OR Apache-2.0" diff --git a/examples/stm32g4/src/bin/adc.rs b/examples/stm32g4/src/bin/adc.rs index adca846d8..920142a18 100644 --- a/examples/stm32g4/src/bin/adc.rs +++ b/examples/stm32g4/src/bin/adc.rs @@ -3,8 +3,8 @@ use defmt::*; use embassy_executor::Spawner; -use embassy_stm32::adc::{Adc, SampleTime}; use embassy_stm32::Config; +use embassy_stm32::adc::{Adc, SampleTime}; use embassy_time::Timer; use {defmt_rtt as _, panic_probe as _}; diff --git a/examples/stm32g4/src/bin/adc_differential.rs b/examples/stm32g4/src/bin/adc_differential.rs index 78d071d45..301f0da84 100644 --- a/examples/stm32g4/src/bin/adc_differential.rs +++ b/examples/stm32g4/src/bin/adc_differential.rs @@ -8,8 +8,8 @@ use defmt::*; use embassy_executor::Spawner; -use embassy_stm32::adc::{Adc, SampleTime}; use embassy_stm32::Config; +use embassy_stm32::adc::{Adc, SampleTime}; use embassy_time::Timer; use {defmt_rtt as _, panic_probe as _}; diff --git a/examples/stm32g4/src/bin/adc_dma.rs b/examples/stm32g4/src/bin/adc_dma.rs index 202704085..a82067049 100644 --- a/examples/stm32g4/src/bin/adc_dma.rs +++ b/examples/stm32g4/src/bin/adc_dma.rs @@ -3,8 +3,8 @@ use defmt::*; use embassy_executor::Spawner; -use embassy_stm32::adc::{Adc, AdcChannel as _, SampleTime}; use embassy_stm32::Config; +use embassy_stm32::adc::{Adc, AdcChannel as _, SampleTime}; use embassy_time::Timer; use {defmt_rtt as _, panic_probe as _}; diff --git a/examples/stm32g4/src/bin/adc_oversampling.rs b/examples/stm32g4/src/bin/adc_oversampling.rs index d31eb20f8..1e464183a 100644 --- a/examples/stm32g4/src/bin/adc_oversampling.rs +++ b/examples/stm32g4/src/bin/adc_oversampling.rs @@ -7,9 +7,9 @@ use defmt::*; use embassy_executor::Spawner; +use embassy_stm32::Config; use embassy_stm32::adc::vals::{Rovsm, Trovs}; use embassy_stm32::adc::{Adc, SampleTime}; -use embassy_stm32::Config; use embassy_time::Timer; use {defmt_rtt as _, panic_probe as _}; diff --git a/examples/stm32g4/src/bin/can.rs b/examples/stm32g4/src/bin/can.rs index 90004f874..7ff7bd7b4 100644 --- a/examples/stm32g4/src/bin/can.rs +++ b/examples/stm32g4/src/bin/can.rs @@ -4,7 +4,7 @@ use defmt::*; use embassy_executor::Spawner; use embassy_stm32::peripherals::*; use embassy_stm32::time::Hertz; -use embassy_stm32::{bind_interrupts, can, Config}; +use embassy_stm32::{Config, bind_interrupts, can}; use embassy_time::Timer; use static_cell::StaticCell; use {defmt_rtt as _, panic_probe as _}; diff --git a/examples/stm32g4/src/bin/usb_c_pd.rs b/examples/stm32g4/src/bin/usb_c_pd.rs index 2e87d3931..b23984b3a 100644 --- a/examples/stm32g4/src/bin/usb_c_pd.rs +++ b/examples/stm32g4/src/bin/usb_c_pd.rs @@ -1,11 +1,11 @@ #![no_std] #![no_main] -use defmt::{error, info, Format}; +use defmt::{Format, error, info}; use embassy_executor::Spawner; use embassy_stm32::ucpd::{self, CcPhy, CcPull, CcSel, CcVState, Ucpd}; -use embassy_stm32::{bind_interrupts, peripherals, Config}; -use embassy_time::{with_timeout, Duration}; +use embassy_stm32::{Config, bind_interrupts, peripherals}; +use embassy_time::{Duration, with_timeout}; use {defmt_rtt as _, panic_probe as _}; bind_interrupts!(struct Irqs { diff --git a/examples/stm32g4/src/bin/usb_serial.rs b/examples/stm32g4/src/bin/usb_serial.rs index 9f66f0c53..a62da6d97 100644 --- a/examples/stm32g4/src/bin/usb_serial.rs +++ b/examples/stm32g4/src/bin/usb_serial.rs @@ -6,10 +6,10 @@ use embassy_executor::Spawner; use embassy_futures::join::join; use embassy_stm32::time::Hertz; use embassy_stm32::usb::{self, Driver, Instance}; -use embassy_stm32::{bind_interrupts, peripherals, Config}; +use embassy_stm32::{Config, bind_interrupts, peripherals}; +use embassy_usb::Builder; use embassy_usb::class::cdc_acm::{CdcAcmClass, State}; use embassy_usb::driver::EndpointError; -use embassy_usb::Builder; use {defmt_rtt as _, panic_probe as _}; bind_interrupts!(struct Irqs { diff --git a/examples/stm32h5/Cargo.toml b/examples/stm32h5/Cargo.toml index 66680c027..475ba7e8a 100644 --- a/examples/stm32h5/Cargo.toml +++ b/examples/stm32h5/Cargo.toml @@ -1,5 +1,5 @@ [package] -edition = "2021" +edition = "2024" name = "embassy-stm32h5-examples" version = "0.1.0" license = "MIT OR Apache-2.0" diff --git a/examples/stm32h5/src/bin/adc.rs b/examples/stm32h5/src/bin/adc.rs index c5d508ece..0566320d4 100644 --- a/examples/stm32h5/src/bin/adc.rs +++ b/examples/stm32h5/src/bin/adc.rs @@ -3,8 +3,8 @@ use defmt::*; use embassy_executor::Spawner; -use embassy_stm32::adc::{Adc, SampleTime}; use embassy_stm32::Config; +use embassy_stm32::adc::{Adc, SampleTime}; use embassy_time::Timer; use {defmt_rtt as _, panic_probe as _}; diff --git a/examples/stm32h5/src/bin/can.rs b/examples/stm32h5/src/bin/can.rs index 194239d47..b1923547e 100644 --- a/examples/stm32h5/src/bin/can.rs +++ b/examples/stm32h5/src/bin/can.rs @@ -4,7 +4,7 @@ use defmt::*; use embassy_executor::Spawner; use embassy_stm32::peripherals::*; -use embassy_stm32::{bind_interrupts, can, rcc, Config}; +use embassy_stm32::{Config, bind_interrupts, can, rcc}; use embassy_time::Timer; use {defmt_rtt as _, panic_probe as _}; diff --git a/examples/stm32h5/src/bin/dts.rs b/examples/stm32h5/src/bin/dts.rs index 8c18fafea..7c856b5b3 100644 --- a/examples/stm32h5/src/bin/dts.rs +++ b/examples/stm32h5/src/bin/dts.rs @@ -6,7 +6,7 @@ use embassy_executor::Spawner; use embassy_stm32::dts::{Dts, InterruptHandler, SampleTime}; use embassy_stm32::peripherals::DTS; use embassy_stm32::rcc::frequency; -use embassy_stm32::{bind_interrupts, dts, Config}; +use embassy_stm32::{Config, bind_interrupts, dts}; use embassy_time::Timer; use {defmt_rtt as _, panic_probe as _}; diff --git a/examples/stm32h5/src/bin/eth.rs b/examples/stm32h5/src/bin/eth.rs index a84fe358b..a5c6cee26 100644 --- a/examples/stm32h5/src/bin/eth.rs +++ b/examples/stm32h5/src/bin/eth.rs @@ -12,7 +12,7 @@ use embassy_stm32::rcc::{ }; use embassy_stm32::rng::Rng; use embassy_stm32::time::Hertz; -use embassy_stm32::{bind_interrupts, eth, peripherals, rng, Config}; +use embassy_stm32::{Config, bind_interrupts, eth, peripherals, rng}; use embassy_time::Timer; use embedded_io_async::Write; use static_cell::StaticCell; diff --git a/examples/stm32h5/src/bin/sai.rs b/examples/stm32h5/src/bin/sai.rs index 0e182f9cf..6632a7f98 100644 --- a/examples/stm32h5/src/bin/sai.rs +++ b/examples/stm32h5/src/bin/sai.rs @@ -3,7 +3,7 @@ use defmt::info; use embassy_executor::Spawner; -use embassy_stm32::{sai, Config}; +use embassy_stm32::{Config, sai}; use {defmt_rtt as _, panic_probe as _}; #[embassy_executor::main] diff --git a/examples/stm32h5/src/bin/usb_c_pd.rs b/examples/stm32h5/src/bin/usb_c_pd.rs index acb03e498..ab6efff32 100644 --- a/examples/stm32h5/src/bin/usb_c_pd.rs +++ b/examples/stm32h5/src/bin/usb_c_pd.rs @@ -3,12 +3,12 @@ #![no_std] #![no_main] -use defmt::{error, info, Format}; +use defmt::{Format, error, info}; use embassy_executor::Spawner; use embassy_stm32::gpio::Output; use embassy_stm32::ucpd::{self, CcPhy, CcPull, CcSel, CcVState, Ucpd}; -use embassy_stm32::{bind_interrupts, peripherals, Config}; -use embassy_time::{with_timeout, Duration}; +use embassy_stm32::{Config, bind_interrupts, peripherals}; +use embassy_time::{Duration, with_timeout}; use {defmt_rtt as _, panic_probe as _}; bind_interrupts!(struct Irqs { diff --git a/examples/stm32h5/src/bin/usb_serial.rs b/examples/stm32h5/src/bin/usb_serial.rs index e8f536133..f72851ed7 100644 --- a/examples/stm32h5/src/bin/usb_serial.rs +++ b/examples/stm32h5/src/bin/usb_serial.rs @@ -6,10 +6,10 @@ use embassy_executor::Spawner; use embassy_futures::join::join; use embassy_stm32::time::Hertz; use embassy_stm32::usb::{Driver, Instance}; -use embassy_stm32::{bind_interrupts, peripherals, usb, Config}; +use embassy_stm32::{Config, bind_interrupts, peripherals, usb}; +use embassy_usb::Builder; use embassy_usb::class::cdc_acm::{CdcAcmClass, State}; use embassy_usb::driver::EndpointError; -use embassy_usb::Builder; use {defmt_rtt as _, panic_probe as _}; bind_interrupts!(struct Irqs { diff --git a/examples/stm32h5/src/bin/usb_uac_speaker.rs b/examples/stm32h5/src/bin/usb_uac_speaker.rs index 86873cabd..f75b1fd8a 100644 --- a/examples/stm32h5/src/bin/usb_uac_speaker.rs +++ b/examples/stm32h5/src/bin/usb_uac_speaker.rs @@ -6,9 +6,9 @@ use core::cell::{Cell, RefCell}; use defmt::{panic, *}; use embassy_executor::Spawner; use embassy_stm32::time::Hertz; -use embassy_stm32::{bind_interrupts, interrupt, peripherals, timer, usb, Config}; -use embassy_sync::blocking_mutex::raw::{CriticalSectionRawMutex, NoopRawMutex}; +use embassy_stm32::{Config, bind_interrupts, interrupt, peripherals, timer, usb}; use embassy_sync::blocking_mutex::Mutex; +use embassy_sync::blocking_mutex::raw::{CriticalSectionRawMutex, NoopRawMutex}; use embassy_sync::signal::Signal; use embassy_sync::zerocopy_channel; use embassy_usb::class::uac1; diff --git a/examples/stm32h7/Cargo.toml b/examples/stm32h7/Cargo.toml index 9a2080013..5993110de 100644 --- a/examples/stm32h7/Cargo.toml +++ b/examples/stm32h7/Cargo.toml @@ -1,5 +1,5 @@ [package] -edition = "2021" +edition = "2024" name = "embassy-stm32h7-examples" version = "0.1.0" license = "MIT OR Apache-2.0" diff --git a/examples/stm32h7/src/bin/adc.rs b/examples/stm32h7/src/bin/adc.rs index 98504ddf6..a53c9d8d5 100644 --- a/examples/stm32h7/src/bin/adc.rs +++ b/examples/stm32h7/src/bin/adc.rs @@ -3,8 +3,8 @@ use defmt::*; use embassy_executor::Spawner; -use embassy_stm32::adc::{Adc, SampleTime}; use embassy_stm32::Config; +use embassy_stm32::adc::{Adc, SampleTime}; use embassy_time::Timer; use {defmt_rtt as _, panic_probe as _}; diff --git a/examples/stm32h7/src/bin/adc_dma.rs b/examples/stm32h7/src/bin/adc_dma.rs index f06b5d06e..cedb32e47 100644 --- a/examples/stm32h7/src/bin/adc_dma.rs +++ b/examples/stm32h7/src/bin/adc_dma.rs @@ -3,8 +3,8 @@ use defmt::*; use embassy_executor::Spawner; -use embassy_stm32::adc::{Adc, AdcChannel as _, SampleTime}; use embassy_stm32::Config; +use embassy_stm32::adc::{Adc, AdcChannel as _, SampleTime}; use embassy_time::Timer; use {defmt_rtt as _, panic_probe as _}; diff --git a/examples/stm32h7/src/bin/camera.rs b/examples/stm32h7/src/bin/camera.rs index 039008d17..c593d5e0b 100644 --- a/examples/stm32h7/src/bin/camera.rs +++ b/examples/stm32h7/src/bin/camera.rs @@ -6,7 +6,7 @@ use embassy_stm32::dcmi::{self, *}; use embassy_stm32::gpio::{Level, Output, Speed}; use embassy_stm32::i2c::I2c; use embassy_stm32::rcc::{Mco, Mco1Source, McoConfig, McoPrescaler}; -use embassy_stm32::{bind_interrupts, i2c, peripherals, Config}; +use embassy_stm32::{Config, bind_interrupts, i2c, peripherals}; use embassy_time::Timer; use ov7725::*; use {defmt_rtt as _, panic_probe as _}; diff --git a/examples/stm32h7/src/bin/can.rs b/examples/stm32h7/src/bin/can.rs index 0af11ef3e..49830f9b7 100644 --- a/examples/stm32h7/src/bin/can.rs +++ b/examples/stm32h7/src/bin/can.rs @@ -4,7 +4,7 @@ use defmt::*; use embassy_executor::Spawner; use embassy_stm32::peripherals::*; -use embassy_stm32::{bind_interrupts, can, rcc, Config}; +use embassy_stm32::{Config, bind_interrupts, can, rcc}; use embassy_time::Timer; use {defmt_rtt as _, panic_probe as _}; diff --git a/examples/stm32h7/src/bin/dac.rs b/examples/stm32h7/src/bin/dac.rs index 27df80336..fa22837c5 100644 --- a/examples/stm32h7/src/bin/dac.rs +++ b/examples/stm32h7/src/bin/dac.rs @@ -3,8 +3,8 @@ use cortex_m_rt::entry; use defmt::*; -use embassy_stm32::dac::{DacCh1, Value}; use embassy_stm32::Config; +use embassy_stm32::dac::{DacCh1, Value}; use {defmt_rtt as _, panic_probe as _}; #[entry] diff --git a/examples/stm32h7/src/bin/dac_dma.rs b/examples/stm32h7/src/bin/dac_dma.rs index df37e9d78..9ccefa761 100644 --- a/examples/stm32h7/src/bin/dac_dma.rs +++ b/examples/stm32h7/src/bin/dac_dma.rs @@ -3,6 +3,7 @@ use defmt::*; use embassy_executor::Spawner; +use embassy_stm32::Peri; use embassy_stm32::dac::{DacCh1, DacCh2, ValueArray}; use embassy_stm32::mode::Async; use embassy_stm32::pac::timer::vals::Mms; @@ -10,7 +11,6 @@ use embassy_stm32::peripherals::{DAC1, TIM6, TIM7}; use embassy_stm32::rcc::frequency; use embassy_stm32::time::Hertz; use embassy_stm32::timer::low_level::Timer; -use embassy_stm32::Peri; use micromath::F32Ext; use {defmt_rtt as _, panic_probe as _}; diff --git a/examples/stm32h7/src/bin/eth.rs b/examples/stm32h7/src/bin/eth.rs index 6c215362d..589f4426e 100644 --- a/examples/stm32h7/src/bin/eth.rs +++ b/examples/stm32h7/src/bin/eth.rs @@ -8,7 +8,7 @@ use embassy_net::{Ipv4Address, StackResources}; use embassy_stm32::eth::{Ethernet, GenericPhy, PacketQueue}; use embassy_stm32::peripherals::ETH; use embassy_stm32::rng::Rng; -use embassy_stm32::{bind_interrupts, eth, peripherals, rng, Config}; +use embassy_stm32::{Config, bind_interrupts, eth, peripherals, rng}; use embassy_time::Timer; use embedded_io_async::Write; use static_cell::StaticCell; diff --git a/examples/stm32h7/src/bin/eth_client.rs b/examples/stm32h7/src/bin/eth_client.rs index 10ac57fc9..fed8f1a9c 100644 --- a/examples/stm32h7/src/bin/eth_client.rs +++ b/examples/stm32h7/src/bin/eth_client.rs @@ -5,12 +5,12 @@ use core::net::{Ipv4Addr, SocketAddr, SocketAddrV4}; use defmt::*; use embassy_executor::Spawner; -use embassy_net::tcp::client::{TcpClient, TcpClientState}; use embassy_net::StackResources; +use embassy_net::tcp::client::{TcpClient, TcpClientState}; use embassy_stm32::eth::{Ethernet, GenericPhy, PacketQueue}; use embassy_stm32::peripherals::ETH; use embassy_stm32::rng::Rng; -use embassy_stm32::{bind_interrupts, eth, peripherals, rng, Config}; +use embassy_stm32::{Config, bind_interrupts, eth, peripherals, rng}; use embassy_time::Timer; use embedded_io_async::Write; use embedded_nal_async::TcpConnect; diff --git a/examples/stm32h7/src/bin/eth_client_mii.rs b/examples/stm32h7/src/bin/eth_client_mii.rs index c6a108471..c3c631f0f 100644 --- a/examples/stm32h7/src/bin/eth_client_mii.rs +++ b/examples/stm32h7/src/bin/eth_client_mii.rs @@ -5,12 +5,12 @@ use core::net::{Ipv4Addr, SocketAddr, SocketAddrV4}; use defmt::*; use embassy_executor::Spawner; -use embassy_net::tcp::client::{TcpClient, TcpClientState}; use embassy_net::StackResources; +use embassy_net::tcp::client::{TcpClient, TcpClientState}; use embassy_stm32::eth::{Ethernet, GenericPhy, PacketQueue}; use embassy_stm32::peripherals::ETH; use embassy_stm32::rng::Rng; -use embassy_stm32::{bind_interrupts, eth, peripherals, rng, Config}; +use embassy_stm32::{Config, bind_interrupts, eth, peripherals, rng}; use embassy_time::Timer; use embedded_io_async::Write; use embedded_nal_async::TcpConnect; diff --git a/examples/stm32h7/src/bin/fmc.rs b/examples/stm32h7/src/bin/fmc.rs index 5e5e6ccc8..b65d50443 100644 --- a/examples/stm32h7/src/bin/fmc.rs +++ b/examples/stm32h7/src/bin/fmc.rs @@ -3,8 +3,8 @@ use defmt::*; use embassy_executor::Spawner; -use embassy_stm32::fmc::Fmc; use embassy_stm32::Config; +use embassy_stm32::fmc::Fmc; use embassy_time::{Delay, Timer}; use {defmt_rtt as _, panic_probe as _}; diff --git a/examples/stm32h7/src/bin/i2c_shared.rs b/examples/stm32h7/src/bin/i2c_shared.rs index 9e45d845f..08ff812f2 100644 --- a/examples/stm32h7/src/bin/i2c_shared.rs +++ b/examples/stm32h7/src/bin/i2c_shared.rs @@ -9,8 +9,8 @@ use embassy_executor::Spawner; use embassy_stm32::i2c::{self, I2c}; use embassy_stm32::mode::Async; use embassy_stm32::{bind_interrupts, peripherals}; -use embassy_sync::blocking_mutex::raw::NoopRawMutex; use embassy_sync::blocking_mutex::NoopMutex; +use embassy_sync::blocking_mutex::raw::NoopRawMutex; use embassy_time::{Duration, Timer}; use embedded_hal_1::i2c::I2c as _; use static_cell::StaticCell; diff --git a/examples/stm32h7/src/bin/low_level_timer_api.rs b/examples/stm32h7/src/bin/low_level_timer_api.rs index 12abb8693..f17fb2aaa 100644 --- a/examples/stm32h7/src/bin/low_level_timer_api.rs +++ b/examples/stm32h7/src/bin/low_level_timer_api.rs @@ -4,7 +4,7 @@ use defmt::*; use embassy_executor::Spawner; use embassy_stm32::gpio::{AfType, Flex, OutputType, Speed}; -use embassy_stm32::time::{khz, Hertz}; +use embassy_stm32::time::{Hertz, khz}; use embassy_stm32::timer::low_level::{OutputCompareMode, Timer as LLTimer}; use embassy_stm32::timer::{Ch1, Ch2, Ch3, Ch4, Channel, GeneralInstance32bit4Channel, TimerPin}; use embassy_stm32::{Config, Peri}; diff --git a/examples/stm32h7/src/bin/multiprio.rs b/examples/stm32h7/src/bin/multiprio.rs index 2f2ffdea2..8375e0e8e 100644 --- a/examples/stm32h7/src/bin/multiprio.rs +++ b/examples/stm32h7/src/bin/multiprio.rs @@ -113,12 +113,12 @@ static EXECUTOR_LOW: StaticCell = StaticCell::new(); #[interrupt] unsafe fn UART4() { - EXECUTOR_HIGH.on_interrupt() + unsafe { EXECUTOR_HIGH.on_interrupt() } } #[interrupt] unsafe fn UART5() { - EXECUTOR_MED.on_interrupt() + unsafe { EXECUTOR_MED.on_interrupt() } } #[entry] diff --git a/examples/stm32h7/src/bin/pwm.rs b/examples/stm32h7/src/bin/pwm.rs index 73b43be69..ffd117580 100644 --- a/examples/stm32h7/src/bin/pwm.rs +++ b/examples/stm32h7/src/bin/pwm.rs @@ -3,10 +3,10 @@ use defmt::*; use embassy_executor::Spawner; +use embassy_stm32::Config; use embassy_stm32::gpio::OutputType; use embassy_stm32::time::khz; use embassy_stm32::timer::simple_pwm::{PwmPin, SimplePwm}; -use embassy_stm32::Config; use embassy_time::Timer; use {defmt_rtt as _, panic_probe as _}; diff --git a/examples/stm32h7/src/bin/rng.rs b/examples/stm32h7/src/bin/rng.rs index a9ef7200d..489747678 100644 --- a/examples/stm32h7/src/bin/rng.rs +++ b/examples/stm32h7/src/bin/rng.rs @@ -4,7 +4,7 @@ use defmt::*; use embassy_executor::Spawner; use embassy_stm32::rng::Rng; -use embassy_stm32::{bind_interrupts, peripherals, rng, Config}; +use embassy_stm32::{Config, bind_interrupts, peripherals, rng}; use {defmt_rtt as _, panic_probe as _}; bind_interrupts!(struct Irqs { diff --git a/examples/stm32h7/src/bin/rtc.rs b/examples/stm32h7/src/bin/rtc.rs index 0adb48877..1bd71637b 100644 --- a/examples/stm32h7/src/bin/rtc.rs +++ b/examples/stm32h7/src/bin/rtc.rs @@ -4,9 +4,9 @@ use chrono::{NaiveDate, NaiveDateTime}; use defmt::*; use embassy_executor::Spawner; +use embassy_stm32::Config; use embassy_stm32::rcc::LsConfig; use embassy_stm32::rtc::{Rtc, RtcConfig}; -use embassy_stm32::Config; use embassy_time::Timer; use {defmt_rtt as _, panic_probe as _}; diff --git a/examples/stm32h7/src/bin/sdmmc.rs b/examples/stm32h7/src/bin/sdmmc.rs index 96840d8ff..4977fec79 100644 --- a/examples/stm32h7/src/bin/sdmmc.rs +++ b/examples/stm32h7/src/bin/sdmmc.rs @@ -5,7 +5,7 @@ use defmt::*; use embassy_executor::Spawner; use embassy_stm32::sdmmc::Sdmmc; use embassy_stm32::time::mhz; -use embassy_stm32::{bind_interrupts, peripherals, sdmmc, Config}; +use embassy_stm32::{Config, bind_interrupts, peripherals, sdmmc}; use {defmt_rtt as _, panic_probe as _}; bind_interrupts!(struct Irqs { diff --git a/examples/stm32h7/src/bin/spi.rs b/examples/stm32h7/src/bin/spi.rs index dce30a4a7..61f31be24 100644 --- a/examples/stm32h7/src/bin/spi.rs +++ b/examples/stm32h7/src/bin/spi.rs @@ -9,7 +9,7 @@ use defmt::*; use embassy_executor::Executor; use embassy_stm32::mode::Blocking; use embassy_stm32::time::mhz; -use embassy_stm32::{spi, Config}; +use embassy_stm32::{Config, spi}; use heapless::String; use static_cell::StaticCell; use {defmt_rtt as _, panic_probe as _}; diff --git a/examples/stm32h7/src/bin/spi_bdma.rs b/examples/stm32h7/src/bin/spi_bdma.rs index 828f687b8..be6a26d82 100644 --- a/examples/stm32h7/src/bin/spi_bdma.rs +++ b/examples/stm32h7/src/bin/spi_bdma.rs @@ -9,7 +9,7 @@ use defmt::*; use embassy_executor::Executor; use embassy_stm32::mode::Async; use embassy_stm32::time::mhz; -use embassy_stm32::{spi, Config}; +use embassy_stm32::{Config, spi}; use grounded::uninit::GroundedArrayCell; use heapless::String; use static_cell::StaticCell; diff --git a/examples/stm32h7/src/bin/spi_dma.rs b/examples/stm32h7/src/bin/spi_dma.rs index 2197fabce..20cb67ba0 100644 --- a/examples/stm32h7/src/bin/spi_dma.rs +++ b/examples/stm32h7/src/bin/spi_dma.rs @@ -9,7 +9,7 @@ use defmt::*; use embassy_executor::Executor; use embassy_stm32::mode::Async; use embassy_stm32::time::mhz; -use embassy_stm32::{spi, Config}; +use embassy_stm32::{Config, spi}; use heapless::String; use static_cell::StaticCell; use {defmt_rtt as _, panic_probe as _}; diff --git a/examples/stm32h7/src/bin/usb_serial.rs b/examples/stm32h7/src/bin/usb_serial.rs index 50bb964da..d0470101b 100644 --- a/examples/stm32h7/src/bin/usb_serial.rs +++ b/examples/stm32h7/src/bin/usb_serial.rs @@ -5,10 +5,10 @@ use defmt::{panic, *}; use embassy_executor::Spawner; use embassy_futures::join::join; use embassy_stm32::usb::{Driver, Instance}; -use embassy_stm32::{bind_interrupts, peripherals, usb, Config}; +use embassy_stm32::{Config, bind_interrupts, peripherals, usb}; +use embassy_usb::Builder; use embassy_usb::class::cdc_acm::{CdcAcmClass, State}; use embassy_usb::driver::EndpointError; -use embassy_usb::Builder; use {defmt_rtt as _, panic_probe as _}; bind_interrupts!(struct Irqs { diff --git a/examples/stm32h723/Cargo.toml b/examples/stm32h723/Cargo.toml index 7e4ccc528..93a5109e2 100644 --- a/examples/stm32h723/Cargo.toml +++ b/examples/stm32h723/Cargo.toml @@ -1,5 +1,5 @@ [package] -edition = "2021" +edition = "2024" name = "embassy-stm32h723-examples" version = "0.1.0" license = "MIT OR Apache-2.0" diff --git a/examples/stm32h723/src/bin/spdifrx.rs b/examples/stm32h723/src/bin/spdifrx.rs index b75a03ae8..cdbd69b89 100644 --- a/examples/stm32h723/src/bin/spdifrx.rs +++ b/examples/stm32h723/src/bin/spdifrx.rs @@ -7,9 +7,9 @@ use defmt::{info, trace}; use embassy_executor::Spawner; -use embassy_futures::select::{select, Either}; +use embassy_futures::select::{Either, select}; use embassy_stm32::spdifrx::{self, Spdifrx}; -use embassy_stm32::{bind_interrupts, peripherals, sai, Peri}; +use embassy_stm32::{Peri, bind_interrupts, peripherals, sai}; use grounded::uninit::GroundedArrayCell; use hal::sai::*; use {defmt_rtt as _, embassy_stm32 as hal, panic_probe as _}; diff --git a/examples/stm32h735/Cargo.toml b/examples/stm32h735/Cargo.toml index 22b7ad96a..1ad2eeb2e 100644 --- a/examples/stm32h735/Cargo.toml +++ b/examples/stm32h735/Cargo.toml @@ -1,5 +1,5 @@ [package] -edition = "2021" +edition = "2024" name = "embassy-stm32h735-examples" version = "0.1.0" license = "MIT OR Apache-2.0" diff --git a/examples/stm32h735/src/bin/ltdc.rs b/examples/stm32h735/src/bin/ltdc.rs index 8a99f745d..f042e04c2 100644 --- a/examples/stm32h735/src/bin/ltdc.rs +++ b/examples/stm32h735/src/bin/ltdc.rs @@ -15,14 +15,14 @@ use embassy_stm32::gpio::{Level, Output, Speed}; use embassy_stm32::ltdc::{self, Ltdc, LtdcConfiguration, LtdcLayer, LtdcLayerConfig, PolarityActive, PolarityEdge}; use embassy_stm32::{bind_interrupts, peripherals}; use embassy_time::{Duration, Timer}; +use embedded_graphics::Pixel; use embedded_graphics::draw_target::DrawTarget; use embedded_graphics::geometry::{OriginDimensions, Point, Size}; use embedded_graphics::image::Image; -use embedded_graphics::pixelcolor::raw::RawU24; use embedded_graphics::pixelcolor::Rgb888; +use embedded_graphics::pixelcolor::raw::RawU24; use embedded_graphics::prelude::*; use embedded_graphics::primitives::Rectangle; -use embedded_graphics::Pixel; use heapless::{Entry, FnvIndexMap}; use tinybmp::Bmp; use {defmt_rtt as _, panic_probe as _}; diff --git a/examples/stm32h742/Cargo.toml b/examples/stm32h742/Cargo.toml index c76340b5f..9b5e5d93d 100644 --- a/examples/stm32h742/Cargo.toml +++ b/examples/stm32h742/Cargo.toml @@ -1,5 +1,5 @@ [package] -edition = "2021" +edition = "2024" name = "embassy-stm32h742-examples" version = "0.1.0" license = "MIT OR Apache-2.0" diff --git a/examples/stm32h742/src/bin/qspi.rs b/examples/stm32h742/src/bin/qspi.rs index 9e79d7089..a88c8f249 100644 --- a/examples/stm32h742/src/bin/qspi.rs +++ b/examples/stm32h742/src/bin/qspi.rs @@ -4,10 +4,10 @@ use defmt::info; use embassy_executor::Spawner; +use embassy_stm32::Config as StmCfg; use embassy_stm32::mode::Blocking; use embassy_stm32::qspi::enums::{AddressSize, ChipSelectHighTime, FIFOThresholdLevel, MemorySize, *}; use embassy_stm32::qspi::{Config as QspiCfg, Instance, Qspi, TransferConfig}; -use embassy_stm32::Config as StmCfg; use {defmt_rtt as _, panic_probe as _}; const MEMORY_PAGE_SIZE: usize = 256; diff --git a/examples/stm32h755cm4/Cargo.toml b/examples/stm32h755cm4/Cargo.toml index c73f9df79..b5c313523 100644 --- a/examples/stm32h755cm4/Cargo.toml +++ b/examples/stm32h755cm4/Cargo.toml @@ -1,5 +1,5 @@ [package] -edition = "2021" +edition = "2024" name = "embassy-stm32h755cm4-examples" version = "0.1.0" license = "MIT OR Apache-2.0" diff --git a/examples/stm32h755cm4/src/bin/blinky.rs b/examples/stm32h755cm4/src/bin/blinky.rs index 39112c1f5..0ee3c68dc 100644 --- a/examples/stm32h755cm4/src/bin/blinky.rs +++ b/examples/stm32h755cm4/src/bin/blinky.rs @@ -5,8 +5,8 @@ use core::mem::MaybeUninit; use defmt::*; use embassy_executor::Spawner; -use embassy_stm32::gpio::{Level, Output, Speed}; use embassy_stm32::SharedData; +use embassy_stm32::gpio::{Level, Output, Speed}; use embassy_time::Timer; use {defmt_rtt as _, panic_probe as _}; diff --git a/examples/stm32h755cm4/src/bin/intercore.rs b/examples/stm32h755cm4/src/bin/intercore.rs index f584e31e9..c0db8cdd3 100644 --- a/examples/stm32h755cm4/src/bin/intercore.rs +++ b/examples/stm32h755cm4/src/bin/intercore.rs @@ -85,7 +85,7 @@ mod shared { } } - #[link_section = ".ram_d3"] + #[unsafe(link_section = ".ram_d3")] pub static SHARED_LED_STATE: SharedLedState = SharedLedState::new(); } @@ -93,13 +93,13 @@ use core::mem::MaybeUninit; use defmt::*; use embassy_executor::Spawner; -use embassy_stm32::gpio::{Level, Output, Speed}; use embassy_stm32::SharedData; +use embassy_stm32::gpio::{Level, Output, Speed}; use embassy_time::Timer; use shared::SHARED_LED_STATE; use {defmt_rtt as _, panic_probe as _}; -#[link_section = ".ram_d3"] +#[unsafe(link_section = ".ram_d3")] static SHARED_DATA: MaybeUninit = MaybeUninit::uninit(); /// Task that continuously blinks the red LED as a heartbeat indicator diff --git a/examples/stm32h755cm7/Cargo.toml b/examples/stm32h755cm7/Cargo.toml index c34d4e45c..7a1519aae 100644 --- a/examples/stm32h755cm7/Cargo.toml +++ b/examples/stm32h755cm7/Cargo.toml @@ -1,5 +1,5 @@ [package] -edition = "2021" +edition = "2024" name = "embassy-stm32h755cm7-examples" version = "0.1.0" license = "MIT OR Apache-2.0" diff --git a/examples/stm32h755cm7/src/bin/blinky.rs b/examples/stm32h755cm7/src/bin/blinky.rs index b30bf4de8..e8f5a1c43 100644 --- a/examples/stm32h755cm7/src/bin/blinky.rs +++ b/examples/stm32h755cm7/src/bin/blinky.rs @@ -5,8 +5,8 @@ use core::mem::MaybeUninit; use defmt::*; use embassy_executor::Spawner; -use embassy_stm32::gpio::{Level, Output, Speed}; use embassy_stm32::SharedData; +use embassy_stm32::gpio::{Level, Output, Speed}; use embassy_time::Timer; use {defmt_rtt as _, panic_probe as _}; diff --git a/examples/stm32h755cm7/src/bin/intercore.rs b/examples/stm32h755cm7/src/bin/intercore.rs index a4e1b5ff4..3df0b26d7 100644 --- a/examples/stm32h755cm7/src/bin/intercore.rs +++ b/examples/stm32h755cm7/src/bin/intercore.rs @@ -97,7 +97,7 @@ mod shared { } } - #[link_section = ".ram_d3"] + #[unsafe(link_section = ".ram_d3")] pub static SHARED_LED_STATE: SharedLedState = SharedLedState::new(); // Memory region constants for MPU configuration @@ -106,7 +106,7 @@ mod shared { pub const SRAM4_REGION_NUMBER: u8 = 0; } -#[link_section = ".ram_d3"] +#[unsafe(link_section = ".ram_d3")] static SHARED_DATA: MaybeUninit = MaybeUninit::uninit(); /// Configure MPU to make SRAM4 region non-cacheable diff --git a/examples/stm32h7b0/Cargo.toml b/examples/stm32h7b0/Cargo.toml index 1917749c5..4cd7b84e5 100644 --- a/examples/stm32h7b0/Cargo.toml +++ b/examples/stm32h7b0/Cargo.toml @@ -1,5 +1,5 @@ [package] -edition = "2021" +edition = "2024" name = "embassy-stm32h7b0-examples" version = "0.1.0" license = "MIT OR Apache-2.0" diff --git a/examples/stm32h7b0/src/bin/ospi_memory_mapped.rs b/examples/stm32h7b0/src/bin/ospi_memory_mapped.rs index dffb740a9..865062f4b 100644 --- a/examples/stm32h7b0/src/bin/ospi_memory_mapped.rs +++ b/examples/stm32h7b0/src/bin/ospi_memory_mapped.rs @@ -5,6 +5,7 @@ use defmt::info; use embassy_executor::Spawner; +use embassy_stm32::Config; use embassy_stm32::gpio::{Level, Output, Speed}; use embassy_stm32::mode::Blocking; use embassy_stm32::ospi::{ @@ -12,7 +13,6 @@ use embassy_stm32::ospi::{ OspiWidth, TransferConfig, WrapSize, }; use embassy_stm32::time::Hertz; -use embassy_stm32::Config; use embassy_time::Timer; use {defmt_rtt as _, panic_probe as _}; diff --git a/examples/stm32h7rs/Cargo.toml b/examples/stm32h7rs/Cargo.toml index bfe59b68d..445916972 100644 --- a/examples/stm32h7rs/Cargo.toml +++ b/examples/stm32h7rs/Cargo.toml @@ -1,5 +1,5 @@ [package] -edition = "2021" +edition = "2024" name = "embassy-stm32h7rs-examples" version = "0.1.0" license = "MIT OR Apache-2.0" diff --git a/examples/stm32h7rs/src/bin/blinky.rs b/examples/stm32h7rs/src/bin/blinky.rs index 5fd50fb15..4c0864ff6 100644 --- a/examples/stm32h7rs/src/bin/blinky.rs +++ b/examples/stm32h7rs/src/bin/blinky.rs @@ -3,9 +3,9 @@ use defmt::*; use embassy_executor::Spawner; +use embassy_stm32::Config; use embassy_stm32::gpio::{Level, Output, Speed}; use embassy_stm32::time::Hertz; -use embassy_stm32::Config; use embassy_time::Timer; use {defmt_rtt as _, panic_probe as _}; diff --git a/examples/stm32h7rs/src/bin/can.rs b/examples/stm32h7rs/src/bin/can.rs index 0af11ef3e..49830f9b7 100644 --- a/examples/stm32h7rs/src/bin/can.rs +++ b/examples/stm32h7rs/src/bin/can.rs @@ -4,7 +4,7 @@ use defmt::*; use embassy_executor::Spawner; use embassy_stm32::peripherals::*; -use embassy_stm32::{bind_interrupts, can, rcc, Config}; +use embassy_stm32::{Config, bind_interrupts, can, rcc}; use embassy_time::Timer; use {defmt_rtt as _, panic_probe as _}; diff --git a/examples/stm32h7rs/src/bin/eth.rs b/examples/stm32h7rs/src/bin/eth.rs index 67f541564..5ce1d4765 100644 --- a/examples/stm32h7rs/src/bin/eth.rs +++ b/examples/stm32h7rs/src/bin/eth.rs @@ -8,7 +8,7 @@ use embassy_net::{Ipv4Address, Ipv4Cidr, StackResources}; use embassy_stm32::eth::{Ethernet, GenericPhy, PacketQueue}; use embassy_stm32::peripherals::ETH; use embassy_stm32::rng::Rng; -use embassy_stm32::{bind_interrupts, eth, peripherals, rng, Config}; +use embassy_stm32::{Config, bind_interrupts, eth, peripherals, rng}; use embassy_time::Timer; use heapless::Vec; use static_cell::StaticCell; diff --git a/examples/stm32h7rs/src/bin/multiprio.rs b/examples/stm32h7rs/src/bin/multiprio.rs index 2f2ffdea2..8375e0e8e 100644 --- a/examples/stm32h7rs/src/bin/multiprio.rs +++ b/examples/stm32h7rs/src/bin/multiprio.rs @@ -113,12 +113,12 @@ static EXECUTOR_LOW: StaticCell = StaticCell::new(); #[interrupt] unsafe fn UART4() { - EXECUTOR_HIGH.on_interrupt() + unsafe { EXECUTOR_HIGH.on_interrupt() } } #[interrupt] unsafe fn UART5() { - EXECUTOR_MED.on_interrupt() + unsafe { EXECUTOR_MED.on_interrupt() } } #[entry] diff --git a/examples/stm32h7rs/src/bin/rng.rs b/examples/stm32h7rs/src/bin/rng.rs index a9ef7200d..489747678 100644 --- a/examples/stm32h7rs/src/bin/rng.rs +++ b/examples/stm32h7rs/src/bin/rng.rs @@ -4,7 +4,7 @@ use defmt::*; use embassy_executor::Spawner; use embassy_stm32::rng::Rng; -use embassy_stm32::{bind_interrupts, peripherals, rng, Config}; +use embassy_stm32::{Config, bind_interrupts, peripherals, rng}; use {defmt_rtt as _, panic_probe as _}; bind_interrupts!(struct Irqs { diff --git a/examples/stm32h7rs/src/bin/rtc.rs b/examples/stm32h7rs/src/bin/rtc.rs index 0adb48877..1bd71637b 100644 --- a/examples/stm32h7rs/src/bin/rtc.rs +++ b/examples/stm32h7rs/src/bin/rtc.rs @@ -4,9 +4,9 @@ use chrono::{NaiveDate, NaiveDateTime}; use defmt::*; use embassy_executor::Spawner; +use embassy_stm32::Config; use embassy_stm32::rcc::LsConfig; use embassy_stm32::rtc::{Rtc, RtcConfig}; -use embassy_stm32::Config; use embassy_time::Timer; use {defmt_rtt as _, panic_probe as _}; diff --git a/examples/stm32h7rs/src/bin/usb_serial.rs b/examples/stm32h7rs/src/bin/usb_serial.rs index 23abc3e2f..3e295dd51 100644 --- a/examples/stm32h7rs/src/bin/usb_serial.rs +++ b/examples/stm32h7rs/src/bin/usb_serial.rs @@ -6,10 +6,10 @@ use embassy_executor::Spawner; use embassy_futures::join::join; use embassy_stm32::time::Hertz; use embassy_stm32::usb::{Driver, Instance}; -use embassy_stm32::{bind_interrupts, peripherals, usb, Config}; +use embassy_stm32::{Config, bind_interrupts, peripherals, usb}; +use embassy_usb::Builder; use embassy_usb::class::cdc_acm::{CdcAcmClass, State}; use embassy_usb::driver::EndpointError; -use embassy_usb::Builder; use {defmt_rtt as _, panic_probe as _}; bind_interrupts!(struct Irqs { diff --git a/examples/stm32h7rs/src/bin/xspi_memory_mapped.rs b/examples/stm32h7rs/src/bin/xspi_memory_mapped.rs index 4c1b450b4..d91ae9de0 100644 --- a/examples/stm32h7rs/src/bin/xspi_memory_mapped.rs +++ b/examples/stm32h7rs/src/bin/xspi_memory_mapped.rs @@ -8,6 +8,7 @@ use core::cmp::min; use defmt::info; use embassy_executor::Spawner; +use embassy_stm32::Config; use embassy_stm32::gpio::{Level, Output, Speed}; use embassy_stm32::mode::Blocking; use embassy_stm32::time::Hertz; @@ -15,7 +16,6 @@ use embassy_stm32::xspi::{ AddressSize, ChipSelectHighTime, DummyCycles, FIFOThresholdLevel, Instance, MemorySize, MemoryType, TransferConfig, WrapSize, Xspi, XspiWidth, }; -use embassy_stm32::Config; use embassy_time::Timer; use {defmt_rtt as _, panic_probe as _}; diff --git a/examples/stm32l0/Cargo.toml b/examples/stm32l0/Cargo.toml index d42cdac15..a9c71d655 100644 --- a/examples/stm32l0/Cargo.toml +++ b/examples/stm32l0/Cargo.toml @@ -1,5 +1,5 @@ [package] -edition = "2021" +edition = "2024" name = "embassy-stm32l0-examples" version = "0.1.0" license = "MIT OR Apache-2.0" diff --git a/examples/stm32l0/src/bin/button_exti.rs b/examples/stm32l0/src/bin/button_exti.rs index 4945da7ce..7ff4a7d52 100644 --- a/examples/stm32l0/src/bin/button_exti.rs +++ b/examples/stm32l0/src/bin/button_exti.rs @@ -3,9 +3,9 @@ use defmt::*; use embassy_executor::Spawner; +use embassy_stm32::Config; use embassy_stm32::exti::ExtiInput; use embassy_stm32::gpio::Pull; -use embassy_stm32::Config; use {defmt_rtt as _, panic_probe as _}; #[embassy_executor::main] diff --git a/examples/stm32l0/src/bin/dds.rs b/examples/stm32l0/src/bin/dds.rs index eaa7a61a8..d8f9020d4 100644 --- a/examples/stm32l0/src/bin/dds.rs +++ b/examples/stm32l0/src/bin/dds.rs @@ -12,7 +12,7 @@ use embassy_stm32::time::hz; use embassy_stm32::timer::low_level::{Timer as LLTimer, *}; use embassy_stm32::timer::simple_pwm::PwmPin; use embassy_stm32::timer::{Ch3, Channel}; -use embassy_stm32::{interrupt, pac, Config}; +use embassy_stm32::{Config, interrupt, pac}; use panic_probe as _; const DDS_SINE_DATA: [u8; 256] = [ diff --git a/examples/stm32l0/src/bin/eeprom.rs b/examples/stm32l0/src/bin/eeprom.rs index 370246644..a33088f36 100644 --- a/examples/stm32l0/src/bin/eeprom.rs +++ b/examples/stm32l0/src/bin/eeprom.rs @@ -3,7 +3,7 @@ use defmt::{info, unwrap}; use embassy_executor::Spawner; -use embassy_stm32::flash::{Flash, EEPROM_BASE, EEPROM_SIZE}; +use embassy_stm32::flash::{EEPROM_BASE, EEPROM_SIZE, Flash}; use {defmt_rtt as _, panic_probe as _}; #[embassy_executor::main] diff --git a/examples/stm32l0/src/bin/raw_spawn.rs b/examples/stm32l0/src/bin/raw_spawn.rs index 6385e3c8f..8e8fe0240 100644 --- a/examples/stm32l0/src/bin/raw_spawn.rs +++ b/examples/stm32l0/src/bin/raw_spawn.rs @@ -5,8 +5,8 @@ use core::mem; use cortex_m_rt::entry; use defmt::*; -use embassy_executor::raw::TaskStorage; use embassy_executor::Executor; +use embassy_executor::raw::TaskStorage; use embassy_time::Timer; use static_cell::StaticCell; use {defmt_rtt as _, panic_probe as _}; @@ -48,5 +48,5 @@ fn main() -> ! { } unsafe fn make_static(t: &T) -> &'static T { - mem::transmute(t) + unsafe { mem::transmute(t) } } diff --git a/examples/stm32l0/src/bin/usb_serial.rs b/examples/stm32l0/src/bin/usb_serial.rs index fdb1aeb59..612082f29 100644 --- a/examples/stm32l0/src/bin/usb_serial.rs +++ b/examples/stm32l0/src/bin/usb_serial.rs @@ -6,9 +6,9 @@ use embassy_executor::Spawner; use embassy_futures::join::join; use embassy_stm32::usb::{self, Driver, Instance}; use embassy_stm32::{bind_interrupts, peripherals}; +use embassy_usb::Builder; use embassy_usb::class::cdc_acm::{CdcAcmClass, State}; use embassy_usb::driver::EndpointError; -use embassy_usb::Builder; use {defmt_rtt as _, panic_probe as _}; bind_interrupts!(struct Irqs { diff --git a/examples/stm32l1/Cargo.toml b/examples/stm32l1/Cargo.toml index 76ceade9c..7f6b31ed5 100644 --- a/examples/stm32l1/Cargo.toml +++ b/examples/stm32l1/Cargo.toml @@ -1,5 +1,5 @@ [package] -edition = "2021" +edition = "2024" name = "embassy-stm32l1-examples" version = "0.1.0" license = "MIT OR Apache-2.0" diff --git a/examples/stm32l1/src/bin/eeprom.rs b/examples/stm32l1/src/bin/eeprom.rs index 370246644..a33088f36 100644 --- a/examples/stm32l1/src/bin/eeprom.rs +++ b/examples/stm32l1/src/bin/eeprom.rs @@ -3,7 +3,7 @@ use defmt::{info, unwrap}; use embassy_executor::Spawner; -use embassy_stm32::flash::{Flash, EEPROM_BASE, EEPROM_SIZE}; +use embassy_stm32::flash::{EEPROM_BASE, EEPROM_SIZE, Flash}; use {defmt_rtt as _, panic_probe as _}; #[embassy_executor::main] diff --git a/examples/stm32l1/src/bin/usb_serial.rs b/examples/stm32l1/src/bin/usb_serial.rs index a35f1d7a7..c54d37f9d 100644 --- a/examples/stm32l1/src/bin/usb_serial.rs +++ b/examples/stm32l1/src/bin/usb_serial.rs @@ -6,9 +6,9 @@ use embassy_executor::Spawner; use embassy_futures::join::join; use embassy_stm32::usb::{self, Driver, Instance}; use embassy_stm32::{bind_interrupts, peripherals}; +use embassy_usb::Builder; use embassy_usb::class::cdc_acm::{CdcAcmClass, State}; use embassy_usb::driver::EndpointError; -use embassy_usb::Builder; use {defmt_rtt as _, panic_probe as _}; bind_interrupts!(struct Irqs { diff --git a/examples/stm32l4/Cargo.toml b/examples/stm32l4/Cargo.toml index 1b7f15b1d..936472199 100644 --- a/examples/stm32l4/Cargo.toml +++ b/examples/stm32l4/Cargo.toml @@ -1,5 +1,5 @@ [package] -edition = "2021" +edition = "2024" name = "embassy-stm32l4-examples" version = "0.1.1" license = "MIT OR Apache-2.0" diff --git a/examples/stm32l4/src/bin/adc.rs b/examples/stm32l4/src/bin/adc.rs index c557ac6d7..40e907940 100644 --- a/examples/stm32l4/src/bin/adc.rs +++ b/examples/stm32l4/src/bin/adc.rs @@ -2,8 +2,8 @@ #![no_main] use defmt::*; -use embassy_stm32::adc::{Adc, Resolution}; use embassy_stm32::Config; +use embassy_stm32::adc::{Adc, Resolution}; use {defmt_rtt as _, panic_probe as _}; #[cortex_m_rt::entry] diff --git a/examples/stm32l4/src/bin/can.rs b/examples/stm32l4/src/bin/can.rs index 3c4cdac24..bd361417e 100644 --- a/examples/stm32l4/src/bin/can.rs +++ b/examples/stm32l4/src/bin/can.rs @@ -8,7 +8,7 @@ use embassy_stm32::can::{ Can, Fifo, Frame, Rx0InterruptHandler, Rx1InterruptHandler, SceInterruptHandler, TxInterruptHandler, }; use embassy_stm32::peripherals::CAN1; -use embassy_stm32::{bind_interrupts, Config}; +use embassy_stm32::{Config, bind_interrupts}; use embassy_time::Timer; use {defmt_rtt as _, panic_probe as _}; diff --git a/examples/stm32l4/src/bin/dac_dma.rs b/examples/stm32l4/src/bin/dac_dma.rs index 44edec728..bfdf858c5 100644 --- a/examples/stm32l4/src/bin/dac_dma.rs +++ b/examples/stm32l4/src/bin/dac_dma.rs @@ -3,6 +3,7 @@ use defmt::*; use embassy_executor::Spawner; +use embassy_stm32::Peri; use embassy_stm32::dac::{DacCh1, DacCh2, ValueArray}; use embassy_stm32::mode::Async; use embassy_stm32::pac::timer::vals::Mms; @@ -10,7 +11,6 @@ use embassy_stm32::peripherals::{DAC1, TIM6, TIM7}; use embassy_stm32::rcc::frequency; use embassy_stm32::time::Hertz; use embassy_stm32::timer::low_level::Timer; -use embassy_stm32::Peri; use micromath::F32Ext; use {defmt_rtt as _, panic_probe as _}; diff --git a/examples/stm32l4/src/bin/rng.rs b/examples/stm32l4/src/bin/rng.rs index 14d0e3c1e..4d9f83ad8 100644 --- a/examples/stm32l4/src/bin/rng.rs +++ b/examples/stm32l4/src/bin/rng.rs @@ -5,7 +5,7 @@ use defmt::*; use embassy_executor::Spawner; use embassy_stm32::rcc::{Pll, PllMul, PllPreDiv, PllQDiv, PllRDiv, PllSource, Sysclk}; use embassy_stm32::rng::Rng; -use embassy_stm32::{bind_interrupts, peripherals, rng, Config}; +use embassy_stm32::{Config, bind_interrupts, peripherals, rng}; use {defmt_rtt as _, panic_probe as _}; bind_interrupts!(struct Irqs { diff --git a/examples/stm32l4/src/bin/rtc.rs b/examples/stm32l4/src/bin/rtc.rs index f554f0f78..1d26cd008 100644 --- a/examples/stm32l4/src/bin/rtc.rs +++ b/examples/stm32l4/src/bin/rtc.rs @@ -4,9 +4,9 @@ use chrono::{NaiveDate, NaiveDateTime}; use defmt::*; use embassy_executor::Spawner; +use embassy_stm32::Config; use embassy_stm32::rtc::{Rtc, RtcConfig}; use embassy_stm32::time::Hertz; -use embassy_stm32::Config; use embassy_time::Timer; use {defmt_rtt as _, panic_probe as _}; diff --git a/examples/stm32l4/src/bin/spe_adin1110_http_server.rs b/examples/stm32l4/src/bin/spe_adin1110_http_server.rs index 24efe526f..8e54938d1 100644 --- a/examples/stm32l4/src/bin/spe_adin1110_http_server.rs +++ b/examples/stm32l4/src/bin/spe_adin1110_http_server.rs @@ -16,14 +16,14 @@ use core::marker::PhantomData; use core::sync::atomic::{AtomicI32, Ordering}; -use defmt::{error, info, println, unwrap, Format}; +use defmt::{Format, error, info, println, unwrap}; use defmt_rtt as _; // global logger use embassy_executor::Spawner; -use embassy_futures::select::{select, Either}; +use embassy_futures::select::{Either, select}; use embassy_futures::yield_now; use embassy_net::tcp::TcpSocket; use embassy_net::{Ipv4Address, Ipv4Cidr, Stack, StackResources, StaticConfigV4}; -use embassy_net_adin1110::{Device, Runner, ADIN1110}; +use embassy_net_adin1110::{ADIN1110, Device, Runner}; use embassy_stm32::gpio::{Input, Level, Output, Pull, Speed}; use embassy_stm32::i2c::{self, Config as I2C_Config, I2c}; use embassy_stm32::mode::Async; @@ -159,7 +159,9 @@ async fn main(spawner: Spawner) { // Check the SPI mode selected with the "HW CFG" dip-switch if !cfg1_spi_mode { - error!("Driver doesn´t support SPI Protolcol \"OPEN Alliance\".\nplease use the \"Generic SPI\"! Turn On \"HW CFG\": \"SPI_CFG1\""); + error!( + "Driver doesn´t support SPI Protolcol \"OPEN Alliance\".\nplease use the \"Generic SPI\"! Turn On \"HW CFG\": \"SPI_CFG1\"" + ); loop { led_uc2_red.toggle(); Timer::after(Duration::from_hz(10)).await; diff --git a/examples/stm32l4/src/bin/usb_serial.rs b/examples/stm32l4/src/bin/usb_serial.rs index af90e297e..17cd107f6 100644 --- a/examples/stm32l4/src/bin/usb_serial.rs +++ b/examples/stm32l4/src/bin/usb_serial.rs @@ -6,10 +6,10 @@ use defmt_rtt as _; // global logger use embassy_executor::Spawner; use embassy_futures::join::join; use embassy_stm32::usb::{Driver, Instance}; -use embassy_stm32::{bind_interrupts, peripherals, usb, Config}; +use embassy_stm32::{Config, bind_interrupts, peripherals, usb}; +use embassy_usb::Builder; use embassy_usb::class::cdc_acm::{CdcAcmClass, State}; use embassy_usb::driver::EndpointError; -use embassy_usb::Builder; use panic_probe as _; bind_interrupts!(struct Irqs { diff --git a/examples/stm32l432/Cargo.toml b/examples/stm32l432/Cargo.toml index f173c651e..14f41992d 100644 --- a/examples/stm32l432/Cargo.toml +++ b/examples/stm32l432/Cargo.toml @@ -1,5 +1,5 @@ [package] -edition = "2021" +edition = "2024" name = "embassy-stm32l432-examples" version = "0.1.1" license = "MIT OR Apache-2.0" diff --git a/examples/stm32l5/Cargo.toml b/examples/stm32l5/Cargo.toml index 9999300b8..b6158c854 100644 --- a/examples/stm32l5/Cargo.toml +++ b/examples/stm32l5/Cargo.toml @@ -1,5 +1,5 @@ [package] -edition = "2021" +edition = "2024" name = "embassy-stm32l5-examples" version = "0.1.0" license = "MIT OR Apache-2.0" diff --git a/examples/stm32l5/src/bin/rng.rs b/examples/stm32l5/src/bin/rng.rs index 0a644e73d..d6302e106 100644 --- a/examples/stm32l5/src/bin/rng.rs +++ b/examples/stm32l5/src/bin/rng.rs @@ -5,7 +5,7 @@ use defmt::*; use embassy_executor::Spawner; use embassy_stm32::rcc::{Pll, PllMul, PllPreDiv, PllRDiv, PllSource, Sysclk}; use embassy_stm32::rng::Rng; -use embassy_stm32::{bind_interrupts, peripherals, rng, Config}; +use embassy_stm32::{Config, bind_interrupts, peripherals, rng}; use {defmt_rtt as _, panic_probe as _}; bind_interrupts!(struct Irqs { diff --git a/examples/stm32l5/src/bin/usb_ethernet.rs b/examples/stm32l5/src/bin/usb_ethernet.rs index 25aa9ef69..d2cbeb550 100644 --- a/examples/stm32l5/src/bin/usb_ethernet.rs +++ b/examples/stm32l5/src/bin/usb_ethernet.rs @@ -3,11 +3,11 @@ use defmt::*; use embassy_executor::Spawner; -use embassy_net::tcp::TcpSocket; use embassy_net::StackResources; +use embassy_net::tcp::TcpSocket; use embassy_stm32::rng::Rng; use embassy_stm32::usb::Driver; -use embassy_stm32::{bind_interrupts, peripherals, rng, usb, Config}; +use embassy_stm32::{Config, bind_interrupts, peripherals, rng, usb}; 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}; diff --git a/examples/stm32l5/src/bin/usb_hid_mouse.rs b/examples/stm32l5/src/bin/usb_hid_mouse.rs index 8c7cdbef5..d8f2de941 100644 --- a/examples/stm32l5/src/bin/usb_hid_mouse.rs +++ b/examples/stm32l5/src/bin/usb_hid_mouse.rs @@ -7,13 +7,13 @@ use defmt::*; use embassy_executor::Spawner; use embassy_futures::join::join; use embassy_stm32::usb::Driver; -use embassy_stm32::{bind_interrupts, peripherals, usb, Config}; +use embassy_stm32::{Config, bind_interrupts, peripherals, usb}; use embassy_time::Timer; +use embassy_usb::Builder; use embassy_usb::class::hid::{ HidBootProtocol, HidProtocolMode, HidSubclass, HidWriter, ReportId, RequestHandler, State, }; use embassy_usb::control::OutResponse; -use embassy_usb::Builder; use usbd_hid::descriptor::{MouseReport, SerializedDescriptor}; use {defmt_rtt as _, panic_probe as _}; diff --git a/examples/stm32l5/src/bin/usb_serial.rs b/examples/stm32l5/src/bin/usb_serial.rs index a64bda31b..4f77fc1d7 100644 --- a/examples/stm32l5/src/bin/usb_serial.rs +++ b/examples/stm32l5/src/bin/usb_serial.rs @@ -5,10 +5,10 @@ use defmt::{panic, *}; use embassy_executor::Spawner; use embassy_futures::join::join; use embassy_stm32::usb::{Driver, Instance}; -use embassy_stm32::{bind_interrupts, peripherals, usb, Config}; +use embassy_stm32::{Config, bind_interrupts, peripherals, usb}; +use embassy_usb::Builder; use embassy_usb::class::cdc_acm::{CdcAcmClass, State}; use embassy_usb::driver::EndpointError; -use embassy_usb::Builder; use {defmt_rtt as _, panic_probe as _}; bind_interrupts!(struct Irqs { diff --git a/examples/stm32u0/Cargo.toml b/examples/stm32u0/Cargo.toml index 9318414a5..9f5227e3f 100644 --- a/examples/stm32u0/Cargo.toml +++ b/examples/stm32u0/Cargo.toml @@ -1,5 +1,5 @@ [package] -edition = "2021" +edition = "2024" name = "embassy-stm32u0-examples" version = "0.1.0" license = "MIT OR Apache-2.0" diff --git a/examples/stm32u0/src/bin/adc.rs b/examples/stm32u0/src/bin/adc.rs index c8252e4e1..32a54299d 100644 --- a/examples/stm32u0/src/bin/adc.rs +++ b/examples/stm32u0/src/bin/adc.rs @@ -2,8 +2,8 @@ #![no_main] use defmt::*; -use embassy_stm32::adc::{Adc, Resolution}; use embassy_stm32::Config; +use embassy_stm32::adc::{Adc, Resolution}; use embassy_time::Duration; use {defmt_rtt as _, panic_probe as _}; diff --git a/examples/stm32u0/src/bin/rng.rs b/examples/stm32u0/src/bin/rng.rs index 89445b042..07deda94c 100644 --- a/examples/stm32u0/src/bin/rng.rs +++ b/examples/stm32u0/src/bin/rng.rs @@ -5,7 +5,7 @@ use defmt::*; use embassy_executor::Spawner; use embassy_stm32::rcc::mux::Clk48sel; use embassy_stm32::rng::Rng; -use embassy_stm32::{bind_interrupts, peripherals, rng, Config}; +use embassy_stm32::{Config, bind_interrupts, peripherals, rng}; use {defmt_rtt as _, panic_probe as _}; bind_interrupts!(struct Irqs { diff --git a/examples/stm32u0/src/bin/rtc.rs b/examples/stm32u0/src/bin/rtc.rs index 72fa0fde4..d071cfbc7 100644 --- a/examples/stm32u0/src/bin/rtc.rs +++ b/examples/stm32u0/src/bin/rtc.rs @@ -4,8 +4,8 @@ use chrono::{NaiveDate, NaiveDateTime}; use defmt::*; use embassy_executor::Spawner; -use embassy_stm32::rtc::{Rtc, RtcConfig}; use embassy_stm32::Config; +use embassy_stm32::rtc::{Rtc, RtcConfig}; use embassy_time::Timer; use {defmt_rtt as _, panic_probe as _}; diff --git a/examples/stm32u0/src/bin/usb_serial.rs b/examples/stm32u0/src/bin/usb_serial.rs index 273f40643..77d0640f6 100644 --- a/examples/stm32u0/src/bin/usb_serial.rs +++ b/examples/stm32u0/src/bin/usb_serial.rs @@ -6,10 +6,10 @@ use defmt_rtt as _; // global logger use embassy_executor::Spawner; use embassy_futures::join::join; use embassy_stm32::usb::{Driver, Instance}; -use embassy_stm32::{bind_interrupts, peripherals, usb, Config}; +use embassy_stm32::{Config, bind_interrupts, peripherals, usb}; +use embassy_usb::Builder; use embassy_usb::class::cdc_acm::{CdcAcmClass, State}; use embassy_usb::driver::EndpointError; -use embassy_usb::Builder; use panic_probe as _; bind_interrupts!(struct Irqs { diff --git a/examples/stm32u5/Cargo.toml b/examples/stm32u5/Cargo.toml index f2ffe52c5..7a1e62406 100644 --- a/examples/stm32u5/Cargo.toml +++ b/examples/stm32u5/Cargo.toml @@ -1,5 +1,5 @@ [package] -edition = "2021" +edition = "2024" name = "embassy-stm32u5-examples" version = "0.1.0" license = "MIT OR Apache-2.0" diff --git a/examples/stm32u5/src/bin/adc.rs b/examples/stm32u5/src/bin/adc.rs index d2aa28087..91e33053e 100644 --- a/examples/stm32u5/src/bin/adc.rs +++ b/examples/stm32u5/src/bin/adc.rs @@ -3,7 +3,7 @@ use defmt::*; use embassy_stm32::adc; -use embassy_stm32::adc::{adc4, AdcChannel}; +use embassy_stm32::adc::{AdcChannel, adc4}; use {defmt_rtt as _, panic_probe as _}; #[embassy_executor::main] diff --git a/examples/stm32u5/src/bin/ltdc.rs b/examples/stm32u5/src/bin/ltdc.rs index 46d1c120f..d1fddb679 100644 --- a/examples/stm32u5/src/bin/ltdc.rs +++ b/examples/stm32u5/src/bin/ltdc.rs @@ -13,14 +13,14 @@ use embassy_stm32::gpio::{Level, Output, Speed}; use embassy_stm32::ltdc::{self, Ltdc, LtdcConfiguration, LtdcLayer, LtdcLayerConfig, PolarityActive, PolarityEdge}; use embassy_stm32::{bind_interrupts, peripherals}; use embassy_time::{Duration, Timer}; +use embedded_graphics::Pixel; use embedded_graphics::draw_target::DrawTarget; use embedded_graphics::geometry::{OriginDimensions, Point, Size}; use embedded_graphics::image::Image; -use embedded_graphics::pixelcolor::raw::RawU24; use embedded_graphics::pixelcolor::Rgb888; +use embedded_graphics::pixelcolor::raw::RawU24; use embedded_graphics::prelude::*; use embedded_graphics::primitives::Rectangle; -use embedded_graphics::Pixel; use heapless::{Entry, FnvIndexMap}; use tinybmp::Bmp; use {defmt_rtt as _, panic_probe as _}; @@ -317,7 +317,7 @@ impl OriginDimensions for DoubleBuffer { mod rcc_setup { use embassy_stm32::time::Hertz; - use embassy_stm32::{rcc, Config, Peripherals}; + use embassy_stm32::{Config, Peripherals, rcc}; /// Sets up clocks for the stm32u5g9zj mcu /// change this if you plan to use a different microcontroller diff --git a/examples/stm32u5/src/bin/usb_hs_serial.rs b/examples/stm32u5/src/bin/usb_hs_serial.rs index d37e7777b..c444d3479 100644 --- a/examples/stm32u5/src/bin/usb_hs_serial.rs +++ b/examples/stm32u5/src/bin/usb_hs_serial.rs @@ -6,10 +6,10 @@ use defmt_rtt as _; // global logger use embassy_executor::Spawner; use embassy_futures::join::join; use embassy_stm32::usb::{Driver, Instance}; -use embassy_stm32::{bind_interrupts, peripherals, usb, Config}; +use embassy_stm32::{Config, bind_interrupts, peripherals, usb}; +use embassy_usb::Builder; use embassy_usb::class::cdc_acm::{CdcAcmClass, State}; use embassy_usb::driver::EndpointError; -use embassy_usb::Builder; use panic_probe as _; bind_interrupts!(struct Irqs { diff --git a/examples/stm32u5/src/bin/usb_serial.rs b/examples/stm32u5/src/bin/usb_serial.rs index ff7f4e5be..c0a768cbb 100644 --- a/examples/stm32u5/src/bin/usb_serial.rs +++ b/examples/stm32u5/src/bin/usb_serial.rs @@ -6,10 +6,10 @@ use defmt_rtt as _; // global logger use embassy_executor::Spawner; use embassy_futures::join::join; use embassy_stm32::usb::{Driver, Instance}; -use embassy_stm32::{bind_interrupts, peripherals, usb, Config}; +use embassy_stm32::{Config, bind_interrupts, peripherals, usb}; +use embassy_usb::Builder; use embassy_usb::class::cdc_acm::{CdcAcmClass, State}; use embassy_usb::driver::EndpointError; -use embassy_usb::Builder; use panic_probe as _; bind_interrupts!(struct Irqs { diff --git a/examples/stm32wb/Cargo.toml b/examples/stm32wb/Cargo.toml index 7ab13c290..783690c11 100644 --- a/examples/stm32wb/Cargo.toml +++ b/examples/stm32wb/Cargo.toml @@ -1,5 +1,5 @@ [package] -edition = "2021" +edition = "2024" name = "embassy-stm32wb-examples" version = "0.1.0" license = "MIT OR Apache-2.0" diff --git a/examples/stm32wb/src/bin/eddystone_beacon.rs b/examples/stm32wb/src/bin/eddystone_beacon.rs index 3bd8b4a63..f309ca3a2 100644 --- a/examples/stm32wb/src/bin/eddystone_beacon.rs +++ b/examples/stm32wb/src/bin/eddystone_beacon.rs @@ -8,15 +8,15 @@ 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_stm32_wpan::hci::BdAddr; 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; use embassy_stm32_wpan::hci::vendor::command::gap::{AdvertisingDataType, DiscoverableParameters, GapCommands, Role}; use embassy_stm32_wpan::hci::vendor::command::gatt::GattCommands; use embassy_stm32_wpan::hci::vendor::command::hal::{ConfigData, HalCommands, PowerLevel}; -use embassy_stm32_wpan::hci::BdAddr; use embassy_stm32_wpan::lhci::LhciC1DeviceInformationCcrp; -use embassy_stm32_wpan::TlMbox; use {defmt_rtt as _, panic_probe as _}; bind_interrupts!(struct Irqs{ diff --git a/examples/stm32wb/src/bin/gatt_server.rs b/examples/stm32wb/src/bin/gatt_server.rs index 5d927bc00..2ed257566 100644 --- a/examples/stm32wb/src/bin/gatt_server.rs +++ b/examples/stm32wb/src/bin/gatt_server.rs @@ -8,6 +8,7 @@ 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_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}; @@ -28,7 +29,6 @@ use embassy_stm32_wpan::hci::{BdAddr, Event}; use embassy_stm32_wpan::lhci::LhciC1DeviceInformationCcrp; use embassy_stm32_wpan::sub::ble::Ble; use embassy_stm32_wpan::sub::mm; -use embassy_stm32_wpan::TlMbox; use {defmt_rtt as _, panic_probe as _}; bind_interrupts!(struct Irqs{ diff --git a/examples/stm32wb/src/bin/mac_ffd.rs b/examples/stm32wb/src/bin/mac_ffd.rs index ede6cf4b9..18a52e162 100644 --- a/examples/stm32wb/src/bin/mac_ffd.rs +++ b/examples/stm32wb/src/bin/mac_ffd.rs @@ -6,11 +6,11 @@ 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_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}; use embassy_stm32_wpan::sub::mm; -use embassy_stm32_wpan::TlMbox; use {defmt_rtt as _, panic_probe as _}; bind_interrupts!(struct Irqs{ diff --git a/examples/stm32wb/src/bin/mac_ffd_net.rs b/examples/stm32wb/src/bin/mac_ffd_net.rs index cc3b21e2e..5296943a1 100644 --- a/examples/stm32wb/src/bin/mac_ffd_net.rs +++ b/examples/stm32wb/src/bin/mac_ffd_net.rs @@ -6,11 +6,11 @@ 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_stm32_wpan::mac::commands::{ResetRequest, SetRequest, StartRequest}; use embassy_stm32_wpan::mac::typedefs::{MacChannel, PanId, PibId}; use embassy_stm32_wpan::mac::{self, Runner}; use embassy_stm32_wpan::sub::mm; -use embassy_stm32_wpan::TlMbox; use static_cell::StaticCell; use {defmt_rtt as _, panic_probe as _}; diff --git a/examples/stm32wb/src/bin/mac_rfd.rs b/examples/stm32wb/src/bin/mac_rfd.rs index d872104a8..883179023 100644 --- a/examples/stm32wb/src/bin/mac_rfd.rs +++ b/examples/stm32wb/src/bin/mac_rfd.rs @@ -6,13 +6,13 @@ 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_stm32_wpan::mac::commands::{AssociateRequest, DataRequest, GetRequest, ResetRequest, SetRequest}; use embassy_stm32_wpan::mac::event::MacEvent; use embassy_stm32_wpan::mac::typedefs::{ AddressMode, Capabilities, KeyIdMode, MacAddress, MacChannel, PanId, PibId, SecurityLevel, }; use embassy_stm32_wpan::sub::mm; -use embassy_stm32_wpan::TlMbox; use {defmt_rtt as _, panic_probe as _}; bind_interrupts!(struct Irqs{ diff --git a/examples/stm32wb/src/bin/tl_mbox_mac.rs b/examples/stm32wb/src/bin/tl_mbox_mac.rs index 95c73872b..16d0a1527 100644 --- a/examples/stm32wb/src/bin/tl_mbox_mac.rs +++ b/examples/stm32wb/src/bin/tl_mbox_mac.rs @@ -6,8 +6,8 @@ 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 embassy_stm32_wpan::sub::mm; use {defmt_rtt as _, panic_probe as _}; bind_interrupts!(struct Irqs{ diff --git a/examples/stm32wba/Cargo.toml b/examples/stm32wba/Cargo.toml index e1196614a..3496b41b0 100644 --- a/examples/stm32wba/Cargo.toml +++ b/examples/stm32wba/Cargo.toml @@ -1,5 +1,5 @@ [package] -edition = "2021" +edition = "2024" name = "embassy-stm32wba-examples" version = "0.1.0" license = "MIT OR Apache-2.0" diff --git a/examples/stm32wba/src/bin/adc.rs b/examples/stm32wba/src/bin/adc.rs index a9651d57e..8c80470b8 100644 --- a/examples/stm32wba/src/bin/adc.rs +++ b/examples/stm32wba/src/bin/adc.rs @@ -2,7 +2,7 @@ #![no_main] use defmt::*; -use embassy_stm32::adc::{adc4, AdcChannel}; +use embassy_stm32::adc::{AdcChannel, adc4}; use {defmt_rtt as _, panic_probe as _}; #[embassy_executor::main] diff --git a/examples/stm32wba/src/bin/pwm.rs b/examples/stm32wba/src/bin/pwm.rs index de690fda0..f20c77a7c 100644 --- a/examples/stm32wba/src/bin/pwm.rs +++ b/examples/stm32wba/src/bin/pwm.rs @@ -4,13 +4,13 @@ use defmt::*; use defmt_rtt as _; // global logger use embassy_executor::Spawner; +use embassy_stm32::Config; use embassy_stm32::gpio::OutputType; use embassy_stm32::rcc::{ AHB5Prescaler, AHBPrescaler, APBPrescaler, PllDiv, PllMul, PllPreDiv, PllSource, Sysclk, VoltageScale, }; use embassy_stm32::time::khz; use embassy_stm32::timer::simple_pwm::{PwmPin, SimplePwm}; -use embassy_stm32::Config; use embassy_time::Timer; use panic_probe as _; diff --git a/examples/stm32wba6/Cargo.toml b/examples/stm32wba6/Cargo.toml index f98317846..04bb27cb0 100644 --- a/examples/stm32wba6/Cargo.toml +++ b/examples/stm32wba6/Cargo.toml @@ -1,5 +1,5 @@ [package] -edition = "2021" +edition = "2024" name = "embassy-stm32wba6-examples" version = "0.1.0" license = "MIT OR Apache-2.0" diff --git a/examples/stm32wba6/src/bin/adc.rs b/examples/stm32wba6/src/bin/adc.rs index a9651d57e..8c80470b8 100644 --- a/examples/stm32wba6/src/bin/adc.rs +++ b/examples/stm32wba6/src/bin/adc.rs @@ -2,7 +2,7 @@ #![no_main] use defmt::*; -use embassy_stm32::adc::{adc4, AdcChannel}; +use embassy_stm32::adc::{AdcChannel, adc4}; use {defmt_rtt as _, panic_probe as _}; #[embassy_executor::main] diff --git a/examples/stm32wba6/src/bin/pwm.rs b/examples/stm32wba6/src/bin/pwm.rs index 2c696834a..64ae01945 100644 --- a/examples/stm32wba6/src/bin/pwm.rs +++ b/examples/stm32wba6/src/bin/pwm.rs @@ -4,13 +4,13 @@ use defmt::*; use defmt_rtt as _; // global logger use embassy_executor::Spawner; +use embassy_stm32::Config; use embassy_stm32::gpio::OutputType; use embassy_stm32::rcc::{ AHB5Prescaler, AHBPrescaler, APBPrescaler, PllDiv, PllMul, PllPreDiv, PllSource, Sysclk, VoltageScale, }; use embassy_stm32::time::khz; use embassy_stm32::timer::simple_pwm::{PwmPin, SimplePwm}; -use embassy_stm32::Config; use embassy_time::Timer; use panic_probe as _; diff --git a/examples/stm32wba6/src/bin/usb_hs_serial.rs b/examples/stm32wba6/src/bin/usb_hs_serial.rs index 20bdeaac3..a60eeb480 100644 --- a/examples/stm32wba6/src/bin/usb_hs_serial.rs +++ b/examples/stm32wba6/src/bin/usb_hs_serial.rs @@ -5,10 +5,10 @@ use defmt::{panic, *}; use embassy_executor::Spawner; use embassy_futures::join::join; use embassy_stm32::usb::{Driver, Instance}; -use embassy_stm32::{bind_interrupts, peripherals, usb, Config}; +use embassy_stm32::{Config, bind_interrupts, peripherals, usb}; +use embassy_usb::Builder; use embassy_usb::class::cdc_acm::{CdcAcmClass, State}; use embassy_usb::driver::EndpointError; -use embassy_usb::Builder; use {defmt_rtt as _, panic_probe as _}; bind_interrupts!(struct Irqs { diff --git a/examples/stm32wl/Cargo.toml b/examples/stm32wl/Cargo.toml index 825d25c0d..1754aa0b8 100644 --- a/examples/stm32wl/Cargo.toml +++ b/examples/stm32wl/Cargo.toml @@ -1,5 +1,5 @@ [package] -edition = "2021" +edition = "2024" name = "embassy-stm32wl-examples" version = "0.1.0" license = "MIT OR Apache-2.0" diff --git a/examples/stm32wl/src/bin/adc.rs b/examples/stm32wl/src/bin/adc.rs index 118f02ae1..6b21b086b 100644 --- a/examples/stm32wl/src/bin/adc.rs +++ b/examples/stm32wl/src/bin/adc.rs @@ -5,8 +5,8 @@ use core::mem::MaybeUninit; use defmt::*; use embassy_executor::Spawner; -use embassy_stm32::adc::{Adc, CkModePclk, Clock, SampleTime}; use embassy_stm32::SharedData; +use embassy_stm32::adc::{Adc, CkModePclk, Clock, SampleTime}; use embassy_time::Timer; use {defmt_rtt as _, panic_probe as _}; diff --git a/examples/stm32wl/src/bin/blinky.rs b/examples/stm32wl/src/bin/blinky.rs index a2a90871d..f7f57e35c 100644 --- a/examples/stm32wl/src/bin/blinky.rs +++ b/examples/stm32wl/src/bin/blinky.rs @@ -5,8 +5,8 @@ use core::mem::MaybeUninit; use defmt::*; use embassy_executor::Spawner; -use embassy_stm32::gpio::{Level, Output, Speed}; use embassy_stm32::SharedData; +use embassy_stm32::gpio::{Level, Output, Speed}; use embassy_time::Timer; use {defmt_rtt as _, panic_probe as _}; diff --git a/examples/stm32wl/src/bin/button.rs b/examples/stm32wl/src/bin/button.rs index 21bcd2ac6..07bc95ad7 100644 --- a/examples/stm32wl/src/bin/button.rs +++ b/examples/stm32wl/src/bin/button.rs @@ -5,8 +5,8 @@ use core::mem::MaybeUninit; use cortex_m_rt::entry; use defmt::*; -use embassy_stm32::gpio::{Input, Level, Output, Pull, Speed}; use embassy_stm32::SharedData; +use embassy_stm32::gpio::{Input, Level, Output, Pull, Speed}; use {defmt_rtt as _, panic_probe as _}; #[unsafe(link_section = ".shared_data")] diff --git a/examples/stm32wl/src/bin/button_exti.rs b/examples/stm32wl/src/bin/button_exti.rs index 0a8aece34..953b13bac 100644 --- a/examples/stm32wl/src/bin/button_exti.rs +++ b/examples/stm32wl/src/bin/button_exti.rs @@ -5,9 +5,9 @@ use core::mem::MaybeUninit; use defmt::*; use embassy_executor::Spawner; +use embassy_stm32::SharedData; use embassy_stm32::exti::ExtiInput; use embassy_stm32::gpio::Pull; -use embassy_stm32::SharedData; use {defmt_rtt as _, panic_probe as _}; #[unsafe(link_section = ".shared_data")] diff --git a/examples/stm32wl/src/bin/flash.rs b/examples/stm32wl/src/bin/flash.rs index 320a9723a..bc707820d 100644 --- a/examples/stm32wl/src/bin/flash.rs +++ b/examples/stm32wl/src/bin/flash.rs @@ -5,8 +5,8 @@ use core::mem::MaybeUninit; use defmt::{info, unwrap}; use embassy_executor::Spawner; -use embassy_stm32::flash::Flash; use embassy_stm32::SharedData; +use embassy_stm32::flash::Flash; use {defmt_rtt as _, panic_probe as _}; #[unsafe(link_section = ".shared_data")] diff --git a/examples/stm32wl/src/bin/random.rs b/examples/stm32wl/src/bin/random.rs index 68b9d7d00..931f9819a 100644 --- a/examples/stm32wl/src/bin/random.rs +++ b/examples/stm32wl/src/bin/random.rs @@ -7,7 +7,7 @@ use defmt::*; use embassy_executor::Spawner; use embassy_stm32::rng::{self, Rng}; use embassy_stm32::time::Hertz; -use embassy_stm32::{bind_interrupts, peripherals, SharedData}; +use embassy_stm32::{SharedData, bind_interrupts, peripherals}; use {defmt_rtt as _, panic_probe as _}; bind_interrupts!(struct Irqs{ diff --git a/examples/stm32wl/src/bin/uart_async.rs b/examples/stm32wl/src/bin/uart_async.rs index 505a85f47..829ea2de7 100644 --- a/examples/stm32wl/src/bin/uart_async.rs +++ b/examples/stm32wl/src/bin/uart_async.rs @@ -6,7 +6,7 @@ use core::mem::MaybeUninit; use defmt::*; use embassy_executor::Spawner; use embassy_stm32::usart::{Config, InterruptHandler, Uart}; -use embassy_stm32::{bind_interrupts, peripherals, SharedData}; +use embassy_stm32::{SharedData, bind_interrupts, peripherals}; use {defmt_rtt as _, panic_probe as _}; bind_interrupts!(struct Irqs{ diff --git a/examples/wasm/Cargo.toml b/examples/wasm/Cargo.toml index e8897506c..79d50b584 100644 --- a/examples/wasm/Cargo.toml +++ b/examples/wasm/Cargo.toml @@ -1,5 +1,5 @@ [package] -edition = "2021" +edition = "2024" name = "embassy-wasm-example" version = "0.1.0" license = "MIT OR Apache-2.0" diff --git a/rustfmt.toml b/rustfmt.toml index 2561562fd..3d5eeed93 100644 --- a/rustfmt.toml +++ b/rustfmt.toml @@ -1,4 +1,4 @@ group_imports = "StdExternalCrate" imports_granularity = "Module" -edition = "2021" +edition = "2024" max_width = 120 diff --git a/tests/mspm0/Cargo.toml b/tests/mspm0/Cargo.toml index 227d898d5..df52b538d 100644 --- a/tests/mspm0/Cargo.toml +++ b/tests/mspm0/Cargo.toml @@ -1,5 +1,5 @@ [package] -edition = "2021" +edition = "2024" name = "embassy-mspm0-tests" version = "0.1.0" license = "MIT OR Apache-2.0" diff --git a/tests/mspm0/src/bin/dma.rs b/tests/mspm0/src/bin/dma.rs index 6fd973a18..9c56acadc 100644 --- a/tests/mspm0/src/bin/dma.rs +++ b/tests/mspm0/src/bin/dma.rs @@ -11,8 +11,8 @@ use core::slice; use defmt::{assert, assert_eq, *}; use embassy_executor::Spawner; -use embassy_mspm0::dma::{Channel, Transfer, TransferMode, TransferOptions, Word}; use embassy_mspm0::Peri; +use embassy_mspm0::dma::{Channel, Transfer, TransferMode, TransferOptions, Word}; use {defmt_rtt as _, panic_probe as _}; #[embassy_executor::main] diff --git a/tests/nrf/Cargo.toml b/tests/nrf/Cargo.toml index 8acf27ce7..3a9b86cef 100644 --- a/tests/nrf/Cargo.toml +++ b/tests/nrf/Cargo.toml @@ -1,5 +1,5 @@ [package] -edition = "2021" +edition = "2024" name = "embassy-nrf-examples" version = "0.1.0" license = "MIT OR Apache-2.0" diff --git a/tests/perf-client/Cargo.toml b/tests/perf-client/Cargo.toml index c426fdd74..3756046fa 100644 --- a/tests/perf-client/Cargo.toml +++ b/tests/perf-client/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "perf-client" version = "0.1.0" -edition = "2021" +edition = "2024" license = "MIT OR Apache-2.0" publish = false diff --git a/tests/perf-client/src/lib.rs b/tests/perf-client/src/lib.rs index 4bd9e5674..1ba071e8d 100644 --- a/tests/perf-client/src/lib.rs +++ b/tests/perf-client/src/lib.rs @@ -4,7 +4,7 @@ use defmt::{assert, *}; use embassy_futures::join::join; use embassy_net::tcp::TcpSocket; use embassy_net::{Ipv4Address, Stack}; -use embassy_time::{with_timeout, Duration, Timer}; +use embassy_time::{Duration, Timer, with_timeout}; pub struct Expected { pub down_kbps: usize, diff --git a/tests/perf-server/Cargo.toml b/tests/perf-server/Cargo.toml index f048eade2..72f92ed8d 100644 --- a/tests/perf-server/Cargo.toml +++ b/tests/perf-server/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "perf-server" version = "0.1.0" -edition = "2021" +edition = "2024" license = "MIT OR Apache-2.0" publish = false diff --git a/tests/riscv32/Cargo.toml b/tests/riscv32/Cargo.toml index c441e8ed3..935c6a2ee 100644 --- a/tests/riscv32/Cargo.toml +++ b/tests/riscv32/Cargo.toml @@ -1,5 +1,5 @@ [package] -edition = "2021" +edition = "2024" name = "embassy-riscv-tests" version = "0.1.0" license = "MIT OR Apache-2.0" diff --git a/tests/rp/Cargo.toml b/tests/rp/Cargo.toml index 19461520a..640e58f11 100644 --- a/tests/rp/Cargo.toml +++ b/tests/rp/Cargo.toml @@ -1,5 +1,5 @@ [package] -edition = "2021" +edition = "2024" name = "embassy-rp-tests" version = "0.1.0" license = "MIT OR Apache-2.0" diff --git a/tests/rp/src/bin/cyw43-perf.rs b/tests/rp/src/bin/cyw43-perf.rs index 555134ffd..9487f5e1a 100644 --- a/tests/rp/src/bin/cyw43-perf.rs +++ b/tests/rp/src/bin/cyw43-perf.rs @@ -3,7 +3,7 @@ teleprobe_meta::target!(b"rpi-pico"); use cyw43::JoinOptions; -use cyw43_pio::{PioSpi, DEFAULT_CLOCK_DIVIDER}; +use cyw43_pio::{DEFAULT_CLOCK_DIVIDER, PioSpi}; use defmt::{panic, *}; use embassy_executor::Spawner; use embassy_net::{Config, StackResources}; diff --git a/tests/rp/src/bin/gpio_multicore.rs b/tests/rp/src/bin/gpio_multicore.rs index f48dd207b..6abcba590 100644 --- a/tests/rp/src/bin/gpio_multicore.rs +++ b/tests/rp/src/bin/gpio_multicore.rs @@ -7,10 +7,10 @@ teleprobe_meta::target!(b"pimoroni-pico-plus-2"); use defmt::{info, unwrap}; use embassy_executor::Executor; +use embassy_rp::Peri; use embassy_rp::gpio::{Input, Level, Output, Pull}; -use embassy_rp::multicore::{spawn_core1, Stack}; +use embassy_rp::multicore::{Stack, spawn_core1}; use embassy_rp::peripherals::{PIN_0, PIN_1}; -use embassy_rp::Peri; use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex; use embassy_sync::channel::Channel; use static_cell::StaticCell; diff --git a/tests/rp/src/bin/multicore.rs b/tests/rp/src/bin/multicore.rs index 11b03cfea..7e34bc778 100644 --- a/tests/rp/src/bin/multicore.rs +++ b/tests/rp/src/bin/multicore.rs @@ -7,7 +7,7 @@ teleprobe_meta::target!(b"pimoroni-pico-plus-2"); use defmt::{info, unwrap}; use embassy_executor::Executor; -use embassy_rp::multicore::{spawn_core1, Stack}; +use embassy_rp::multicore::{Stack, spawn_core1}; use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex; use embassy_sync::channel::Channel; use static_cell::StaticCell; diff --git a/tests/rp/src/bin/overclock.rs b/tests/rp/src/bin/overclock.rs index 167a26eb2..87a03b5e2 100644 --- a/tests/rp/src/bin/overclock.rs +++ b/tests/rp/src/bin/overclock.rs @@ -8,7 +8,7 @@ teleprobe_meta::target!(b"pimoroni-pico-plus-2"); use defmt::info; use embassy_executor::Spawner; -use embassy_rp::clocks::{clk_sys_freq, core_voltage, ClockConfig, CoreVoltage}; +use embassy_rp::clocks::{ClockConfig, CoreVoltage, clk_sys_freq, core_voltage}; use embassy_rp::config::Config; use embassy_time::Instant; use {defmt_rtt as _, panic_probe as _}; diff --git a/tests/rp/src/bin/pio_multi_load.rs b/tests/rp/src/bin/pio_multi_load.rs index aca476d56..82bbab272 100644 --- a/tests/rp/src/bin/pio_multi_load.rs +++ b/tests/rp/src/bin/pio_multi_load.rs @@ -57,7 +57,9 @@ async fn main(_spawner: Spawner) { assert_eq!(loaded2.wrap.target, 14); // wrapping around the end of program space automatically works - let prg3 = pio_asm!("nop", "nop", "nop", "nop", "nop", "nop", "nop", "nop", "nop", "nop", "nop", "irq 2",); + let prg3 = 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); diff --git a/tests/rp/src/bin/rtc.rs b/tests/rp/src/bin/rtc.rs index c66981d95..e1def7b5b 100644 --- a/tests/rp/src/bin/rtc.rs +++ b/tests/rp/src/bin/rtc.rs @@ -5,7 +5,7 @@ teleprobe_meta::target!(b"rpi-pico"); use defmt::{assert, *}; use embassy_executor::Spawner; -use embassy_futures::select::{select, Either}; +use embassy_futures::select::{Either, select}; use embassy_rp::bind_interrupts; use embassy_rp::rtc::{DateTime, DateTimeFilter, DayOfWeek, Rtc}; use embassy_time::{Duration, Instant, Timer}; diff --git a/tests/rp/src/bin/spinlock_mutex_multicore.rs b/tests/rp/src/bin/spinlock_mutex_multicore.rs index c56d43ade..25c4faf37 100644 --- a/tests/rp/src/bin/spinlock_mutex_multicore.rs +++ b/tests/rp/src/bin/spinlock_mutex_multicore.rs @@ -7,7 +7,7 @@ teleprobe_meta::target!(b"pimoroni-pico-plus-2"); use defmt::{info, unwrap}; use embassy_executor::Executor; -use embassy_rp::multicore::{spawn_core1, Stack}; +use embassy_rp::multicore::{Stack, spawn_core1}; use embassy_rp::spinlock_mutex::SpinlockRawMutex; use embassy_sync::channel::Channel; use static_cell::StaticCell; diff --git a/tests/stm32/Cargo.toml b/tests/stm32/Cargo.toml index 891ec93fd..1161e827b 100644 --- a/tests/stm32/Cargo.toml +++ b/tests/stm32/Cargo.toml @@ -1,5 +1,5 @@ [package] -edition = "2021" +edition = "2024" name = "embassy-stm32-tests" version = "0.1.0" license = "MIT OR Apache-2.0" diff --git a/tests/stm32/src/bin/afio.rs b/tests/stm32/src/bin/afio.rs index 81d50874b..d88765717 100644 --- a/tests/stm32/src/bin/afio.rs +++ b/tests/stm32/src/bin/afio.rs @@ -16,7 +16,7 @@ use embassy_stm32::timer::qei::Qei; use embassy_stm32::timer::simple_pwm::{PwmPin, SimplePwm}; use embassy_stm32::timer::{Ch1, Ch2}; use embassy_stm32::usart::{Uart, UartRx, UartTx}; -use embassy_stm32::{bind_interrupts, Peripherals}; +use embassy_stm32::{Peripherals, bind_interrupts}; #[cfg(not(feature = "afio-connectivity-line"))] bind_interrupts!(struct Irqs { diff --git a/tests/stm32/src/bin/cryp.rs b/tests/stm32/src/bin/cryp.rs index f54c99cc3..640de50e3 100644 --- a/tests/stm32/src/bin/cryp.rs +++ b/tests/stm32/src/bin/cryp.rs @@ -5,9 +5,9 @@ #[path = "../common.rs"] mod common; +use aes_gcm::Aes128Gcm; use aes_gcm::aead::heapless::Vec; use aes_gcm::aead::{AeadInPlace, KeyInit}; -use aes_gcm::Aes128Gcm; use common::*; use embassy_executor::Spawner; use embassy_stm32::cryp::{self, *}; diff --git a/tests/stm32/src/bin/fdcan.rs b/tests/stm32/src/bin/fdcan.rs index c2a1a7bb8..d97f493df 100644 --- a/tests/stm32/src/bin/fdcan.rs +++ b/tests/stm32/src/bin/fdcan.rs @@ -8,7 +8,7 @@ mod common; use common::*; use embassy_executor::Spawner; use embassy_stm32::peripherals::*; -use embassy_stm32::{bind_interrupts, can, Config}; +use embassy_stm32::{Config, bind_interrupts, can}; use embassy_time::Duration; use {defmt_rtt as _, panic_probe as _}; diff --git a/tests/stm32/src/bin/stop.rs b/tests/stm32/src/bin/stop.rs index 8119c1f39..833ca05d0 100644 --- a/tests/stm32/src/bin/stop.rs +++ b/tests/stm32/src/bin/stop.rs @@ -9,10 +9,10 @@ use chrono::NaiveDate; use common::*; use cortex_m_rt::entry; use embassy_executor::Spawner; -use embassy_stm32::low_power::{stop_ready, stop_with_rtc, Executor, StopMode}; +use embassy_stm32::Config; +use embassy_stm32::low_power::{Executor, StopMode, stop_ready, stop_with_rtc}; use embassy_stm32::rcc::LsConfig; use embassy_stm32::rtc::{Rtc, RtcConfig}; -use embassy_stm32::Config; use embassy_time::Timer; use static_cell::StaticCell; diff --git a/tests/stm32/src/bin/ucpd.rs b/tests/stm32/src/bin/ucpd.rs index 97aefe1a0..c794afff8 100644 --- a/tests/stm32/src/bin/ucpd.rs +++ b/tests/stm32/src/bin/ucpd.rs @@ -9,7 +9,7 @@ use defmt::{assert, assert_eq}; use embassy_executor::Spawner; use embassy_futures::join::join; use embassy_stm32::ucpd::{self, CcPhy, CcPull, CcSel, CcVState, RxError, Ucpd}; -use embassy_stm32::{bind_interrupts, peripherals, Peri}; +use embassy_stm32::{Peri, bind_interrupts, peripherals}; use embassy_time::Timer; bind_interrupts!(struct Irqs { diff --git a/tests/stm32/src/bin/usart.rs b/tests/stm32/src/bin/usart.rs index 129c7b692..0b98d3eeb 100644 --- a/tests/stm32/src/bin/usart.rs +++ b/tests/stm32/src/bin/usart.rs @@ -7,7 +7,7 @@ use common::*; use defmt::{assert, assert_eq, unreachable}; use embassy_executor::Spawner; use embassy_stm32::usart::{Config, ConfigError, Error, Uart}; -use embassy_time::{block_for, Duration, Instant}; +use embassy_time::{Duration, Instant, block_for}; #[embassy_executor::main] async fn main(_spawner: Spawner) { diff --git a/tests/stm32/src/bin/wpan_ble.rs b/tests/stm32/src/bin/wpan_ble.rs index 8957bfc04..0f396b848 100644 --- a/tests/stm32/src/bin/wpan_ble.rs +++ b/tests/stm32/src/bin/wpan_ble.rs @@ -12,16 +12,16 @@ 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_stm32_wpan::hci::BdAddr; 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; use embassy_stm32_wpan::hci::vendor::command::gap::{AdvertisingDataType, DiscoverableParameters, GapCommands, Role}; use embassy_stm32_wpan::hci::vendor::command::gatt::GattCommands; use embassy_stm32_wpan::hci::vendor::command::hal::{ConfigData, HalCommands, PowerLevel}; -use embassy_stm32_wpan::hci::BdAddr; use embassy_stm32_wpan::lhci::LhciC1DeviceInformationCcrp; use embassy_stm32_wpan::sub::mm; -use embassy_stm32_wpan::TlMbox; use {defmt_rtt as _, panic_probe as _}; bind_interrupts!(struct Irqs{ diff --git a/tests/stm32/src/bin/wpan_mac.rs b/tests/stm32/src/bin/wpan_mac.rs index 79e13d524..f27146c44 100644 --- a/tests/stm32/src/bin/wpan_mac.rs +++ b/tests/stm32/src/bin/wpan_mac.rs @@ -10,13 +10,13 @@ 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_stm32_wpan::mac::commands::{AssociateRequest, GetRequest, ResetRequest, SetRequest}; use embassy_stm32_wpan::mac::event::MacEvent; use embassy_stm32_wpan::mac::typedefs::{ AddressMode, Capabilities, KeyIdMode, MacAddress, MacChannel, PanId, PibId, SecurityLevel, }; use embassy_stm32_wpan::sub::mm; -use embassy_stm32_wpan::TlMbox; use {defmt_rtt as _, panic_probe as _}; bind_interrupts!(struct Irqs{ diff --git a/tests/stm32/src/common.rs b/tests/stm32/src/common.rs index f800769ab..2bd934d6f 100644 --- a/tests/stm32/src/common.rs +++ b/tests/stm32/src/common.rs @@ -1,11 +1,11 @@ #![macro_use] pub use defmt::*; +use embassy_stm32::Config; #[allow(unused)] use embassy_stm32::rcc::*; #[allow(unused)] use embassy_stm32::time::Hertz; -use embassy_stm32::Config; use {defmt_rtt as _, panic_probe as _}; #[cfg(feature = "stm32f103c8")] diff --git a/tests/utils/Cargo.toml b/tests/utils/Cargo.toml index ddb990e0f..da04a1f5d 100644 --- a/tests/utils/Cargo.toml +++ b/tests/utils/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "test-utils" version = "0.1.0" -edition = "2021" +edition = "2024" license = "MIT OR Apache-2.0" publish = false diff --git a/tests/utils/src/bin/saturate_serial.rs b/tests/utils/src/bin/saturate_serial.rs index 85676b106..1c8a8b322 100644 --- a/tests/utils/src/bin/saturate_serial.rs +++ b/tests/utils/src/bin/saturate_serial.rs @@ -2,7 +2,7 @@ use std::path::Path; use std::time::Duration; use std::{env, io, process, thread}; -use rand::{rng, Rng}; +use rand::{Rng, rng}; use serial::SerialPort; pub fn main() { -- cgit From 3949a8601f293856df326ccc21252cb5f1518c5c Mon Sep 17 00:00:00 2001 From: Maarten de Vries Date: Thu, 30 Oct 2025 11:52:53 +0100 Subject: embassy-nrf: add gpiote::InputChannel::wait_for_high/low() Also catch GPIOTE events directly when wait() is called, even before polling the future. --- embassy-nrf/CHANGELOG.md | 3 ++ embassy-nrf/src/gpiote.rs | 68 ++++++++++++++++++++++++++--- examples/nrf52840/src/bin/gpiote_channel.rs | 8 ++-- examples/nrf5340/src/bin/gpiote_channel.rs | 8 ++-- examples/nrf54l15/src/bin/gpiote_channel.rs | 8 ++-- 5 files changed, 77 insertions(+), 18 deletions(-) diff --git a/embassy-nrf/CHANGELOG.md b/embassy-nrf/CHANGELOG.md index a0668c495..98f40f9a9 100644 --- a/embassy-nrf/CHANGELOG.md +++ b/embassy-nrf/CHANGELOG.md @@ -22,6 +22,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - bugfix: Do not write to UICR from non-secure code on nrf53 - bugfix: Add delay to uart init anomaly fix - changed: `BufferedUarte::read_ready` now uses the same definition for 'empty' so following read calls will not block when true is returned +- added: add `gpiote::InputChannel::wait_for_high()` and `wait_for_low()` to wait for specific signal level +- changed: `gpiote::InputChannel::wait()` now takes a mutable reference to `self` to avoid interference from concurrent calls +- changed: `gpiote::InputChannel::wait()` now ensures events are seen as soon as the function is called, even if the future is not polled ## 0.8.0 - 2025-09-30 diff --git a/embassy-nrf/src/gpiote.rs b/embassy-nrf/src/gpiote.rs index 91944d8cd..d4f6668f3 100644 --- a/embassy-nrf/src/gpiote.rs +++ b/embassy-nrf/src/gpiote.rs @@ -349,16 +349,73 @@ impl<'d> InputChannel<'d> { } /// Asynchronously wait for an event in this channel. - pub async fn wait(&self) { - let g = self.ch.regs(); - let num = self.ch.number(); - let waker = self.ch.waker(); + /// + /// It is possible to call this function and await the returned future later. + /// If an even occurs in the mean time, the future will immediately report ready. + pub fn wait(&mut self) -> impl Future { + // NOTE: This is `-> impl Future` and not an `async fn` on purpose. + // Otherwise, events will only be detected starting at the first poll of the returned future. + Self::wait_internal(&mut self.ch) + } + + /// Asynchronously wait for the pin to become high. + /// + /// The channel must be configured with [`InputChannelPolarity::LoToHi`] or [`InputChannelPolarity::Toggle`]. + /// If the channel is not configured to detect rising edges, it is unspecified when the returned future completes. + /// + /// It is possible to call this function and await the returned future later. + /// If an even occurs in the mean time, the future will immediately report ready. + pub fn wait_for_high(&mut self) -> impl Future { + // NOTE: This is `-> impl Future` and not an `async fn` on purpose. + // Otherwise, events will only be detected starting at the first poll of the returned future. + + // Subscribe to the event before checking the pin level. + let wait = Self::wait_internal(&mut self.ch); + let pin = &self.pin; + async move { + if pin.is_high() { + return; + } + wait.await; + } + } + + /// Asynchronously wait for the pin to become low. + /// + /// The channel must be configured with [`InputChannelPolarity::HiToLo`] or [`InputChannelPolarity::Toggle`]. + /// If the channel is not configured to detect falling edges, it is unspecified when the returned future completes. + /// + /// It is possible to call this function and await the returned future later. + /// If an even occurs in the mean time, the future will immediately report ready. + pub fn wait_for_low(&mut self) -> impl Future { + // NOTE: This is `-> impl Future` and not an `async fn` on purpose. + // Otherwise, events will only be detected starting at the first poll of the returned future. + + // Subscribe to the event before checking the pin level. + let wait = Self::wait_internal(&mut self.ch); + let pin = &self.pin; + async move { + if pin.is_low() { + return; + } + wait.await; + } + } + + /// Internal implementation for `wait()` and friends. + fn wait_internal(channel: &mut Peri<'_, AnyChannel>) -> impl Future { + // NOTE: This is `-> impl Future` and not an `async fn` on purpose. + // Otherwise, events will only be detected starting at the first poll of the returned future. + + let g = channel.regs(); + let num = channel.number(); + let waker = channel.waker(); // Enable interrupt g.events_in(num).write_value(0); g.intenset(INTNUM).write(|w| w.0 = 1 << num); - poll_fn(|cx| { + poll_fn(move |cx| { CHANNEL_WAKERS[waker].register(cx.waker()); if g.events_in(num).read() != 0 { @@ -367,7 +424,6 @@ impl<'d> InputChannel<'d> { Poll::Pending } }) - .await; } /// Get the associated input pin. diff --git a/examples/nrf52840/src/bin/gpiote_channel.rs b/examples/nrf52840/src/bin/gpiote_channel.rs index c7ddc1d8d..e358779b2 100644 --- a/examples/nrf52840/src/bin/gpiote_channel.rs +++ b/examples/nrf52840/src/bin/gpiote_channel.rs @@ -12,10 +12,10 @@ async fn main(_spawner: Spawner) { let p = embassy_nrf::init(Default::default()); info!("Starting!"); - let ch1 = InputChannel::new(p.GPIOTE_CH0, p.P0_11, Pull::Up, InputChannelPolarity::HiToLo); - let ch2 = InputChannel::new(p.GPIOTE_CH1, p.P0_12, Pull::Up, InputChannelPolarity::LoToHi); - let ch3 = InputChannel::new(p.GPIOTE_CH2, p.P0_24, Pull::Up, InputChannelPolarity::Toggle); - let ch4 = InputChannel::new(p.GPIOTE_CH3, p.P0_25, Pull::Up, InputChannelPolarity::Toggle); + let mut ch1 = InputChannel::new(p.GPIOTE_CH0, p.P0_11, Pull::Up, InputChannelPolarity::HiToLo); + let mut ch2 = InputChannel::new(p.GPIOTE_CH1, p.P0_12, Pull::Up, InputChannelPolarity::LoToHi); + let mut ch3 = InputChannel::new(p.GPIOTE_CH2, p.P0_24, Pull::Up, InputChannelPolarity::Toggle); + let mut ch4 = InputChannel::new(p.GPIOTE_CH3, p.P0_25, Pull::Up, InputChannelPolarity::Toggle); let button1 = async { loop { diff --git a/examples/nrf5340/src/bin/gpiote_channel.rs b/examples/nrf5340/src/bin/gpiote_channel.rs index a085310ce..41ee732c3 100644 --- a/examples/nrf5340/src/bin/gpiote_channel.rs +++ b/examples/nrf5340/src/bin/gpiote_channel.rs @@ -12,10 +12,10 @@ async fn main(_spawner: Spawner) { let p = embassy_nrf::init(Default::default()); info!("Starting!"); - let ch1 = InputChannel::new(p.GPIOTE_CH0, p.P0_23, Pull::Up, InputChannelPolarity::HiToLo); - let ch2 = InputChannel::new(p.GPIOTE_CH1, p.P0_24, Pull::Up, InputChannelPolarity::LoToHi); - let ch3 = InputChannel::new(p.GPIOTE_CH2, p.P0_08, Pull::Up, InputChannelPolarity::Toggle); - let ch4 = InputChannel::new(p.GPIOTE_CH3, p.P0_09, Pull::Up, InputChannelPolarity::Toggle); + let mut ch1 = InputChannel::new(p.GPIOTE_CH0, p.P0_23, Pull::Up, InputChannelPolarity::HiToLo); + let mut ch2 = InputChannel::new(p.GPIOTE_CH1, p.P0_24, Pull::Up, InputChannelPolarity::LoToHi); + let mut ch3 = InputChannel::new(p.GPIOTE_CH2, p.P0_08, Pull::Up, InputChannelPolarity::Toggle); + let mut ch4 = InputChannel::new(p.GPIOTE_CH3, p.P0_09, Pull::Up, InputChannelPolarity::Toggle); let button1 = async { loop { diff --git a/examples/nrf54l15/src/bin/gpiote_channel.rs b/examples/nrf54l15/src/bin/gpiote_channel.rs index 6333250ba..cac8823f8 100644 --- a/examples/nrf54l15/src/bin/gpiote_channel.rs +++ b/examples/nrf54l15/src/bin/gpiote_channel.rs @@ -12,10 +12,10 @@ async fn main(_spawner: Spawner) { let p = embassy_nrf::init(Default::default()); info!("Starting!"); - let ch1 = InputChannel::new(p.GPIOTE20_CH0, p.P1_13, Pull::Up, InputChannelPolarity::HiToLo); - let ch2 = InputChannel::new(p.GPIOTE20_CH1, p.P1_09, Pull::Up, InputChannelPolarity::LoToHi); - let ch3 = InputChannel::new(p.GPIOTE20_CH2, p.P1_08, Pull::Up, InputChannelPolarity::Toggle); - let ch4 = InputChannel::new(p.GPIOTE30_CH0, p.P0_04, Pull::Up, InputChannelPolarity::Toggle); + let mut ch1 = InputChannel::new(p.GPIOTE20_CH0, p.P1_13, Pull::Up, InputChannelPolarity::HiToLo); + let mut ch2 = InputChannel::new(p.GPIOTE20_CH1, p.P1_09, Pull::Up, InputChannelPolarity::LoToHi); + let mut ch3 = InputChannel::new(p.GPIOTE20_CH2, p.P1_08, Pull::Up, InputChannelPolarity::Toggle); + let mut ch4 = InputChannel::new(p.GPIOTE30_CH0, p.P0_04, Pull::Up, InputChannelPolarity::Toggle); let button1 = async { loop { -- cgit From f440a3e19584aa8c1c5df742b964ae417cf705a1 Mon Sep 17 00:00:00 2001 From: Priit Laes Date: Mon, 3 Nov 2025 23:08:15 +0200 Subject: stm32/timer/simplepwm: Fix docs formatting and clarify timer usage --- embassy-stm32/src/timer/simple_pwm.rs | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/embassy-stm32/src/timer/simple_pwm.rs b/embassy-stm32/src/timer/simple_pwm.rs index 06315d7f3..7597c0eee 100644 --- a/embassy-stm32/src/timer/simple_pwm.rs +++ b/embassy-stm32/src/timer/simple_pwm.rs @@ -309,7 +309,9 @@ impl<'d, T: GeneralInstance4Channel> SimplePwm<'d, T> { /// Generate a sequence of PWM waveform /// /// Note: - /// you will need to provide corresponding TIMx_UP DMA channel to use this method. + /// You will need to provide corresponding `TIMx_UP` DMA channel to use this method. + /// Also be aware that embassy timers use one of timers internally. It is possible to + /// switch this timer by using `time-driver-timX` feature. pub async fn waveform_up(&mut self, dma: Peri<'_, impl super::UpDma>, channel: Channel, duty: &[u16]) { #[allow(clippy::let_unit_value)] // eg. stm32f334 let req = dma.request(); @@ -378,18 +380,23 @@ impl<'d, T: GeneralInstance4Channel> SimplePwm<'d, T> { /// /// For example, if using channels 1 through 4, a buffer of 4 update steps might look like: /// + /// ```rust,ignore /// let dma_buf: [u16; 16] = [ /// ch1_duty_1, ch2_duty_1, ch3_duty_1, ch4_duty_1, // update 1 /// ch1_duty_2, ch2_duty_2, ch3_duty_2, ch4_duty_2, // update 2 /// ch1_duty_3, ch2_duty_3, ch3_duty_3, ch4_duty_3, // update 3 /// ch1_duty_4, ch2_duty_4, ch3_duty_4, ch4_duty_4, // update 4 /// ]; + /// ``` /// - /// Each group of N values (where N = number of channels) is transferred on one update event, + /// Each group of `N` values (where `N` is number of channels) is transferred on one update event, /// updating the duty cycles of all selected channels simultaneously. /// /// Note: - /// you will need to provide corresponding TIMx_UP DMA channel to use this method. + /// You will need to provide corresponding `TIMx_UP` DMA channel to use this method. + /// Also be aware that embassy timers use one of timers internally. It is possible to + /// switch this timer by using `time-driver-timX` feature. + /// pub async fn waveform_up_multi_channel( &mut self, dma: Peri<'_, impl super::UpDma>, -- cgit From 5e76be83cf693d2de4608fec4ef11fbeb32722d4 Mon Sep 17 00:00:00 2001 From: HybridChild Date: Tue, 11 Nov 2025 20:33:00 +0100 Subject: stm32/i2c_v2: Add initial transaction implementation --- embassy-stm32/src/i2c/v2.rs | 502 +++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 474 insertions(+), 28 deletions(-) diff --git a/embassy-stm32/src/i2c/v2.rs b/embassy-stm32/src/i2c/v2.rs index 4527e55b9..061f4ff3a 100644 --- a/embassy-stm32/src/i2c/v2.rs +++ b/embassy-stm32/src/i2c/v2.rs @@ -172,20 +172,23 @@ impl<'d, M: Mode, IM: MasterMode> I2c<'d, M, IM> { length: usize, stop: Stop, reload: bool, + restart: bool, timeout: Timeout, ) -> Result<(), Error> { assert!(length < 256); - // Wait for any previous address sequence to end - // automatically. This could be up to 50% of a bus - // cycle (ie. up to 0.5/freq) - while info.regs.cr2().read().start() { - timeout.check()?; - } + if !restart { + // Wait for any previous address sequence to end + // automatically. This could be up to 50% of a bus + // cycle (ie. up to 0.5/freq) + while info.regs.cr2().read().start() { + timeout.check()?; + } - // Wait for the bus to be free - while info.regs.isr().read().busy() { - timeout.check()?; + // Wait for the bus to be free + while info.regs.isr().read().busy() { + timeout.check()?; + } } let reload = if reload { @@ -210,7 +213,7 @@ impl<'d, M: Mode, IM: MasterMode> I2c<'d, M, IM> { Ok(()) } - fn reload(info: &'static Info, length: usize, will_reload: bool, timeout: Timeout) -> Result<(), Error> { + fn reload(info: &'static Info, length: usize, will_reload: bool, stop: Stop, timeout: Timeout) -> Result<(), Error> { assert!(length < 256 && length > 0); while !info.regs.isr().read().tcr() { @@ -226,6 +229,7 @@ impl<'d, M: Mode, IM: MasterMode> I2c<'d, M, IM> { info.regs.cr2().modify(|w| { w.set_nbytes(length as u8); w.set_reload(will_reload); + w.set_autoend(stop.autoend()); }); Ok(()) @@ -403,7 +407,7 @@ impl<'d, M: Mode, IM: MasterMode> I2c<'d, M, IM> { for (number, chunk) in read.chunks_mut(255).enumerate() { if number != 0 { - Self::reload(self.info, chunk.len(), number != last_chunk_idx, timeout)?; + Self::reload(self.info, chunk.len(), number != last_chunk_idx, Stop::Automatic, timeout)?; } for byte in chunk { @@ -441,6 +445,7 @@ impl<'d, M: Mode, IM: MasterMode> I2c<'d, M, IM> { write.len().min(255), Stop::Software, last_chunk_idx != 0, + false, // restart timeout, ) { if send_stop { @@ -451,7 +456,7 @@ impl<'d, M: Mode, IM: MasterMode> I2c<'d, M, IM> { for (number, chunk) in write.chunks(255).enumerate() { if number != 0 { - Self::reload(self.info, chunk.len(), number != last_chunk_idx, timeout)?; + Self::reload(self.info, chunk.len(), number != last_chunk_idx, Stop::Software, timeout)?; } for byte in chunk { @@ -507,9 +512,219 @@ impl<'d, M: Mode, IM: MasterMode> I2c<'d, M, IM> { /// /// [transaction contract]: embedded_hal_1::i2c::I2c::transaction pub fn blocking_transaction(&mut self, addr: u8, operations: &mut [Operation<'_>]) -> Result<(), Error> { - let _ = addr; - let _ = operations; - todo!() + if operations.is_empty() { + return Err(Error::ZeroLengthTransfer); + } + + let address = addr.into(); + let timeout = self.timeout(); + + // Group consecutive operations of the same type + let mut op_idx = 0; + let mut is_first_group = true; + + while op_idx < operations.len() { + // Determine the type of current group and find all consecutive operations of same type + let is_read = matches!(operations[op_idx], Operation::Read(_)); + let group_start = op_idx; + + // Find end of this group (consecutive operations of same type) + while op_idx < operations.len() && matches!(operations[op_idx], Operation::Read(_)) == is_read { + op_idx += 1; + } + let group_end = op_idx; + let is_last_group = op_idx >= operations.len(); + + // Execute this group of operations + if is_read { + self.execute_read_group( + address, + &mut operations[group_start..group_end], + is_first_group, + is_last_group, + timeout, + )?; + } else { + self.execute_write_group( + address, + &operations[group_start..group_end], + is_first_group, + is_last_group, + timeout, + )?; + } + + is_first_group = false; + } + + Ok(()) + } + + fn execute_write_group( + &mut self, + address: Address, + operations: &[Operation<'_>], + is_first_group: bool, + is_last_group: bool, + timeout: Timeout, + ) -> Result<(), Error> { + // Calculate total bytes across all operations in this group + let total_bytes: usize = operations + .iter() + .map(|op| match op { + Operation::Write(buf) => buf.len(), + _ => 0, + }) + .sum(); + + if total_bytes == 0 { + // Handle empty write group - just send address + if is_first_group { + Self::master_write(self.info, address, 0, Stop::Software, false, !is_first_group, timeout)?; + } + if is_last_group { + self.master_stop(); + } + return Ok(()); + } + + let mut total_remaining = total_bytes; + let mut first_chunk = true; + + for operation in operations { + if let Operation::Write(buffer) = operation { + for chunk in buffer.chunks(255) { + let chunk_len = chunk.len(); + total_remaining -= chunk_len; + let is_last_chunk = total_remaining == 0; + let will_reload = !is_last_chunk; + + if first_chunk { + // First chunk: initiate transfer + // If not first group, use RESTART instead of START + Self::master_write(self.info, address, chunk_len, Stop::Software, will_reload, !is_first_group, timeout)?; + first_chunk = false; + } else { + // Subsequent chunks: use reload + // Always use Software stop for writes + Self::reload(self.info, chunk_len, will_reload, Stop::Software, timeout)?; + } + + // Send data bytes + for byte in chunk { + self.wait_txis(timeout)?; + self.info.regs.txdr().write(|w| w.set_txdata(*byte)); + } + } + } + } + + // Wait for transfer to complete + if is_last_group { + self.wait_tc(timeout)?; + self.master_stop(); + self.wait_stop(timeout)?; + } else { + // Wait for TC before next group (enables RESTART) + self.wait_tc(timeout)?; + } + + Ok(()) + } + + fn execute_read_group( + &mut self, + address: Address, + operations: &mut [Operation<'_>], + is_first_group: bool, + is_last_group: bool, + timeout: Timeout, + ) -> Result<(), Error> { + // Calculate total bytes across all operations in this group + let total_bytes: usize = operations + .iter() + .map(|op| match op { + Operation::Read(buf) => buf.len(), + _ => 0, + }) + .sum(); + + if total_bytes == 0 { + // Handle empty read group + if is_first_group { + Self::master_read( + self.info, + address, + 0, + if is_last_group { Stop::Automatic } else { Stop::Software }, + false, + !is_first_group, + timeout, + )?; + } + if is_last_group { + self.wait_stop(timeout)?; + } + return Ok(()); + } + + let mut total_remaining = total_bytes; + let mut first_chunk = true; + + for operation in operations { + if let Operation::Read(buffer) = operation { + for chunk in buffer.chunks_mut(255) { + let chunk_len = chunk.len(); + total_remaining -= chunk_len; + let is_last_chunk = total_remaining == 0; + let will_reload = !is_last_chunk; + + if first_chunk { + // First chunk: initiate transfer + let stop = if is_last_group && is_last_chunk { + Stop::Automatic + } else { + Stop::Software + }; + + Self::master_read( + self.info, + address, + chunk_len, + stop, + will_reload, + !is_first_group, // restart if not first group + timeout, + )?; + first_chunk = false; + } else { + // Subsequent chunks: use reload + let stop = if is_last_group && is_last_chunk { + Stop::Automatic + } else { + Stop::Software + }; + Self::reload(self.info, chunk_len, will_reload, stop, timeout)?; + } + + // Receive data bytes + for byte in chunk { + self.wait_rxne(timeout)?; + *byte = self.info.regs.rxdr().read().rxdata(); + } + } + } + } + + // Wait for transfer to complete + if is_last_group { + self.wait_stop(timeout)?; + } + // For non-last read groups, don't wait for TC - the peripheral may hold SCL low + // in Software AUTOEND mode until the next START is issued. Just proceed directly + // to the next group which will issue RESTART and release the clock. + + Ok(()) } /// Blocking write multiple buffers. @@ -531,6 +746,7 @@ impl<'d, M: Mode, IM: MasterMode> I2c<'d, M, IM> { first_length.min(255), Stop::Software, (first_length > 255) || (last_slice_index != 0), + false, // restart timeout, ) { self.master_stop(); @@ -552,6 +768,7 @@ impl<'d, M: Mode, IM: MasterMode> I2c<'d, M, IM> { self.info, slice_len.min(255), (idx != last_slice_index) || (slice_len > 255), + Stop::Software, timeout, ) { if err != Error::Nack { @@ -567,6 +784,7 @@ impl<'d, M: Mode, IM: MasterMode> I2c<'d, M, IM> { self.info, chunk.len(), (number != last_chunk_idx) || (idx != last_slice_index), + Stop::Software, timeout, ) { if err != Error::Nack { @@ -610,6 +828,7 @@ impl<'d, IM: MasterMode> I2c<'d, Async, IM> { first_slice: bool, last_slice: bool, send_stop: bool, + restart: bool, timeout: Timeout, ) -> Result<(), Error> { let total_len = write.len(); @@ -676,10 +895,11 @@ impl<'d, IM: MasterMode> I2c<'d, Async, IM> { total_len.min(255), Stop::Software, (total_len > 255) || !last_slice, + restart, timeout, )?; } else { - Self::reload(self.info, total_len.min(255), (total_len > 255) || !last_slice, timeout)?; + Self::reload(self.info, total_len.min(255), (total_len > 255) || !last_slice, Stop::Software, timeout)?; self.info.regs.cr1().modify(|w| w.set_tcie(true)); } } else if !(isr.tcr() || isr.tc()) { @@ -690,7 +910,7 @@ impl<'d, IM: MasterMode> I2c<'d, Async, IM> { } else { let last_piece = (remaining_len <= 255) && last_slice; - if let Err(e) = Self::reload(self.info, remaining_len.min(255), !last_piece, timeout) { + if let Err(e) = Self::reload(self.info, remaining_len.min(255), !last_piece, Stop::Software, timeout) { return Poll::Ready(Err(e)); } self.info.regs.cr1().modify(|w| w.set_tcie(true)); @@ -793,7 +1013,7 @@ impl<'d, IM: MasterMode> I2c<'d, Async, IM> { } else { let last_piece = remaining_len <= 255; - if let Err(e) = Self::reload(self.info, remaining_len.min(255), !last_piece, timeout) { + if let Err(e) = Self::reload(self.info, remaining_len.min(255), !last_piece, Stop::Automatic, timeout) { return Poll::Ready(Err(e)); } // Return here if we are on last chunk, @@ -826,7 +1046,7 @@ impl<'d, IM: MasterMode> I2c<'d, Async, IM> { self.write_internal(address.into(), write, true, timeout) } else { timeout - .with(self.write_dma_internal(address.into(), write, true, true, true, timeout)) + .with(self.write_dma_internal(address.into(), write, true, true, true, false, timeout)) .await } } @@ -850,7 +1070,7 @@ impl<'d, IM: MasterMode> I2c<'d, Async, IM> { let next = iter.next(); let is_last = next.is_none(); - let fut = self.write_dma_internal(address, c, first, is_last, is_last, timeout); + let fut = self.write_dma_internal(address, c, first, is_last, is_last, false, timeout); timeout.with(fut).await?; first = false; current = next; @@ -881,7 +1101,7 @@ impl<'d, IM: MasterMode> I2c<'d, Async, IM> { if write.is_empty() { self.write_internal(address.into(), write, false, timeout)?; } else { - let fut = self.write_dma_internal(address.into(), write, true, true, false, timeout); + let fut = self.write_dma_internal(address.into(), write, true, true, false, false, timeout); timeout.with(fut).await?; } @@ -903,9 +1123,235 @@ impl<'d, IM: MasterMode> I2c<'d, Async, IM> { pub async fn transaction(&mut self, addr: u8, operations: &mut [Operation<'_>]) -> Result<(), Error> { #[cfg(all(feature = "low-power", stm32wlex))] let _device_busy = crate::low_power::DeviceBusy::new_stop1(); - let _ = addr; - let _ = operations; - todo!() + + if operations.is_empty() { + return Err(Error::ZeroLengthTransfer); + } + + let address = addr.into(); + let timeout = self.timeout(); + + // Group consecutive operations of the same type + let mut op_idx = 0; + let mut is_first_group = true; + + while op_idx < operations.len() { + // Determine the type of current group and find all consecutive operations of same type + let is_read = matches!(operations[op_idx], Operation::Read(_)); + let group_start = op_idx; + + // Find end of this group (consecutive operations of same type) + while op_idx < operations.len() && matches!(operations[op_idx], Operation::Read(_)) == is_read { + op_idx += 1; + } + let group_end = op_idx; + let is_last_group = op_idx >= operations.len(); + + // Execute this group of operations + if is_read { + self.execute_read_group_async( + address, + &mut operations[group_start..group_end], + is_first_group, + is_last_group, + timeout, + ) + .await?; + } else { + self.execute_write_group_async( + address, + &operations[group_start..group_end], + is_first_group, + is_last_group, + timeout, + ) + .await?; + } + + is_first_group = false; + } + + Ok(()) + } + + async fn execute_write_group_async( + &mut self, + address: Address, + operations: &[Operation<'_>], + is_first_group: bool, + is_last_group: bool, + timeout: Timeout, + ) -> Result<(), Error> { + // Calculate total bytes across all operations in this group + let total_bytes: usize = operations + .iter() + .map(|op| match op { + Operation::Write(buf) => buf.len(), + _ => 0, + }) + .sum(); + + if total_bytes == 0 { + // Handle empty write group using blocking call + if is_first_group { + Self::master_write(self.info, address, 0, Stop::Software, false, !is_first_group, timeout)?; + } + if is_last_group { + self.master_stop(); + } + return Ok(()); + } + + let send_stop = is_last_group; + + // Use DMA for each operation in the group + let mut first_in_group = true; + let mut remaining_operations = operations.len(); + + for operation in operations { + if let Operation::Write(buffer) = operation { + remaining_operations -= 1; + let is_last_in_group = remaining_operations == 0; + + if buffer.is_empty() { + // Skip empty buffers + continue; + } + + let fut = self.write_dma_internal( + address, + buffer, + first_in_group && is_first_group, // first_slice + is_last_in_group && is_last_group, // last_slice + send_stop && is_last_in_group, // send_stop + !is_first_group && first_in_group, // restart + timeout, + ); + timeout.with(fut).await?; + first_in_group = false; + } + } + + // If not last group, wait for TC to enable RESTART + if !is_last_group { + self.wait_tc(timeout)?; + } + + Ok(()) + } + + async fn execute_read_group_async( + &mut self, + address: Address, + operations: &mut [Operation<'_>], + is_first_group: bool, + is_last_group: bool, + timeout: Timeout, + ) -> Result<(), Error> { + // Calculate total bytes across all operations in this group + let total_bytes: usize = operations + .iter() + .map(|op| match op { + Operation::Read(buf) => buf.len(), + _ => 0, + }) + .sum(); + + if total_bytes == 0 { + // Handle empty read group using blocking call + if is_first_group { + Self::master_read( + self.info, + address, + 0, + if is_last_group { Stop::Automatic } else { Stop::Software }, + false, + !is_first_group, + timeout, + )?; + } + if is_last_group { + self.wait_stop(timeout)?; + } + return Ok(()); + } + + // For read operations, we need to handle them differently since read_dma_internal + // expects a single buffer. We'll iterate through operations and use DMA for each. + let mut total_remaining = total_bytes; + let restart = !is_first_group; + + for operation in operations { + if let Operation::Read(buffer) = operation { + if buffer.is_empty() { + // Skip empty buffers + continue; + } + + let is_first_read = total_remaining == total_bytes; + let buf_len = buffer.len(); + total_remaining -= buf_len; + let is_last_read = total_remaining == 0; + + if is_first_read { + // First read in the group + let completed_chunks = buf_len / 255; + let total_chunks = if completed_chunks * 255 == buf_len { + completed_chunks + } else { + completed_chunks + 1 + }; + let last_chunk_idx = total_chunks.saturating_sub(1); + + // Use master_read to initiate, then DMA for data + Self::master_read( + self.info, + address, + buf_len.min(255), + if is_last_group && is_last_read { + Stop::Automatic + } else { + Stop::Software + }, + last_chunk_idx != 0 || !is_last_read, + restart, + timeout, + )?; + } + + // Use the existing read_dma_internal, but we need to handle the reload logic ourselves + // For simplicity with consecutive reads, fall back to blocking for now + // This maintains correctness while keeping complexity manageable + for (chunk_idx, chunk) in buffer.chunks_mut(255).enumerate() { + let chunk_len = chunk.len(); + let is_very_last = total_remaining == 0 && chunk_len == chunk.len(); + + if !is_first_read || chunk_idx != 0 { + let stop = if is_last_group && is_very_last { + Stop::Automatic + } else { + Stop::Software + }; + Self::reload(self.info, chunk_len, !(is_last_group && is_very_last), stop, timeout)?; + } + + for byte in chunk { + self.wait_rxne(timeout)?; + *byte = self.info.regs.rxdr().read().rxdata(); + } + } + } + } + + // Wait for transfer to complete + if is_last_group { + self.wait_stop(timeout)?; + } + // For non-last read groups, don't wait for TC - the peripheral may hold SCL low + // in Software AUTOEND mode until the next START is issued. Just proceed directly + // to the next group which will issue RESTART and release the clock. + + Ok(()) } } @@ -1043,7 +1489,7 @@ impl<'d, M: Mode> I2c<'d, M, MultiMaster> { if number == 0 { Self::slave_start(self.info, chunk.len(), number != last_chunk_idx); } else { - Self::reload(self.info, chunk.len(), number != last_chunk_idx, timeout)?; + Self::reload(self.info, chunk.len(), number != last_chunk_idx, Stop::Software, timeout)?; } let mut index = 0; @@ -1092,7 +1538,7 @@ impl<'d, M: Mode> I2c<'d, M, MultiMaster> { if number == 0 { Self::slave_start(self.info, chunk.len(), number != last_chunk_idx); } else { - Self::reload(self.info, chunk.len(), number != last_chunk_idx, timeout)?; + Self::reload(self.info, chunk.len(), number != last_chunk_idx, Stop::Software, timeout)?; } let mut index = 0; @@ -1228,7 +1674,7 @@ impl<'d> I2c<'d, Async, MultiMaster> { Poll::Pending } else if isr.tcr() { let is_last_slice = remaining_len <= 255; - if let Err(e) = Self::reload(self.info, remaining_len.min(255), !is_last_slice, timeout) { + if let Err(e) = Self::reload(self.info, remaining_len.min(255), !is_last_slice, Stop::Software, timeout) { return Poll::Ready(Err(e)); } remaining_len = remaining_len.saturating_sub(255); @@ -1292,7 +1738,7 @@ impl<'d> I2c<'d, Async, MultiMaster> { Poll::Pending } else if isr.tcr() { let is_last_slice = remaining_len <= 255; - if let Err(e) = Self::reload(self.info, remaining_len.min(255), !is_last_slice, timeout) { + if let Err(e) = Self::reload(self.info, remaining_len.min(255), !is_last_slice, Stop::Software, timeout) { return Poll::Ready(Err(e)); } remaining_len = remaining_len.saturating_sub(255); -- cgit From aa5c0c02425104fceea9e5dc773e3f5c346e9656 Mon Sep 17 00:00:00 2001 From: HybridChild Date: Wed, 12 Nov 2025 09:49:25 +0100 Subject: stm32/i2c_v2: Add transaction test --- examples/stm32f0/Cargo.toml | 1 + examples/stm32f0/src/bin/i2c_transaction_test.rs | 219 +++++++++++++++++++++++ 2 files changed, 220 insertions(+) create mode 100644 examples/stm32f0/src/bin/i2c_transaction_test.rs diff --git a/examples/stm32f0/Cargo.toml b/examples/stm32f0/Cargo.toml index a78873d21..177dd0ac2 100644 --- a/examples/stm32f0/Cargo.toml +++ b/examples/stm32f0/Cargo.toml @@ -16,6 +16,7 @@ panic-probe = { version = "1.0.0", features = ["print-defmt"] } 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", "executor-interrupt", "defmt"] } embassy-time = { version = "0.5.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } +embedded-hal-1 = { package = "embedded-hal", version = "1.0" } static_cell = "2" portable-atomic = { version = "1.5", features = ["unsafe-assume-single-core"] } diff --git a/examples/stm32f0/src/bin/i2c_transaction_test.rs b/examples/stm32f0/src/bin/i2c_transaction_test.rs new file mode 100644 index 000000000..0ecc3e8b1 --- /dev/null +++ b/examples/stm32f0/src/bin/i2c_transaction_test.rs @@ -0,0 +1,219 @@ +#![no_std] +#![no_main] + +use defmt::*; +use embassy_executor::Spawner; +use embassy_stm32::i2c::{Config, I2c, Master}; +use embassy_stm32::mode::Blocking; +use embassy_stm32::time::Hertz; +use embassy_stm32::{bind_interrupts, i2c, peripherals}; +use embassy_time::Timer; +use embedded_hal_1::i2c::Operation; +use {defmt_rtt as _, panic_probe as _}; + +bind_interrupts!(struct Irqs { + I2C1 => i2c::EventInterruptHandler, i2c::ErrorInterruptHandler; +}); + +#[embassy_executor::main] +async fn main(_spawner: Spawner) { + // For STM32F072RB on NUCLEO board + let p = embassy_stm32::init(Default::default()); + + info!("I2C Transaction Test Starting..."); + + // Initialize I2C1: PB6=SCL, PB7=SDA + let mut config = Config::default(); + config.frequency = Hertz(100_000); + let mut i2c = I2c::new_blocking( + p.I2C1, + p.PB8, // SCL + p.PB9, // SDA + config, + ); + + let slave_addr = 0x50u8; + + // Wait for devices to initialize + Timer::after_millis(100).await; + + info!("=== Test 1: Consecutive Writes (Should Merge) ==="); + test_consecutive_writes(&mut i2c, slave_addr); + Timer::after_millis(500).await; + + info!("=== Test 2: Consecutive Reads (Should Merge) ==="); + test_consecutive_reads(&mut i2c, slave_addr); + Timer::after_millis(500).await; + + info!("=== Test 3: Write then Read (RESTART) ==="); + test_write_then_read(&mut i2c, slave_addr); + Timer::after_millis(500).await; + + info!("=== Test 4: Read then Write (RESTART) ==="); + test_read_then_write(&mut i2c, slave_addr); + Timer::after_millis(500).await; + + info!("=== Test 5: Complex Mixed Sequence ==="); + test_mixed_sequence(&mut i2c, slave_addr); + Timer::after_millis(500).await; + + info!("=== Test 6: Single Operations ==="); + test_single_operations(&mut i2c, slave_addr); + + info!("All tests complete!"); + + loop { + Timer::after_secs(1).await; + } +} + +fn test_consecutive_writes(i2c: &mut I2c<'static, Blocking, Master>, addr: u8) { + // Expected on bus: START, ADDR+W, data1, data2, data3, STOP + // NO intermediate RESTART/STOP between writes + let data1 = [0x10, 0x11, 0x12]; + let data2 = [0x20, 0x21]; + let data3 = [0x30, 0x31, 0x32, 0x33]; + + let mut ops = [ + Operation::Write(&data1), + Operation::Write(&data2), + Operation::Write(&data3), + ]; + + match i2c.blocking_transaction(addr, &mut ops) { + Ok(_) => info!("✓ Consecutive writes succeeded"), + Err(e) => warn!("✗ Consecutive writes failed: {:?}", e), + } + + info!("Expected: START, ADDR+W, [9 bytes], STOP"); + info!("Check Analog Discovery: No RESTART between writes"); +} + +fn test_consecutive_reads(i2c: &mut I2c<'static, Blocking, Master>, addr: u8) { + // Expected on bus: START, ADDR+R, data1, data2, data3, NACK, STOP + // NO intermediate RESTART/STOP between reads + let mut buf1 = [0u8; 4]; + let mut buf2 = [0u8; 3]; + let mut buf3 = [0u8; 2]; + + let mut ops = [ + Operation::Read(&mut buf1), + Operation::Read(&mut buf2), + Operation::Read(&mut buf3), + ]; + + match i2c.blocking_transaction(addr, &mut ops) { + Ok(_) => { + info!("✓ Consecutive reads succeeded"); + info!(" buf1: {:02x}", buf1); + info!(" buf2: {:02x}", buf2); + info!(" buf3: {:02x}", buf3); + } + Err(e) => warn!("✗ Consecutive reads failed: {:?}", e), + } + + info!("Expected: START, ADDR+R, [9 bytes], NACK on last, STOP"); + info!("Check Analog Discovery: No RESTART between reads"); +} + +fn test_write_then_read(i2c: &mut I2c<'static, Blocking, Master>, addr: u8) { + // Expected: START, ADDR+W, data, RESTART, ADDR+R, data, NACK, STOP + let write_data = [0xAA, 0xBB]; + let mut read_buf = [0u8; 4]; + + let mut ops = [ + Operation::Write(&write_data), + Operation::Read(&mut read_buf), + ]; + + match i2c.blocking_transaction(addr, &mut ops) { + Ok(_) => { + info!("✓ Write-then-read succeeded"); + info!(" Written: {:02x}", write_data); + info!(" Read: {:02x}", read_buf); + } + Err(e) => warn!("✗ Write-then-read failed: {:?}", e), + } + + info!("Expected: START, ADDR+W, [2 bytes], RESTART, ADDR+R, [4 bytes], NACK, STOP"); + info!("Check Analog Discovery: RESTART between write and read"); +} + +fn test_read_then_write(i2c: &mut I2c<'static, Blocking, Master>, addr: u8) { + // Expected: START, ADDR+R, data, NACK, RESTART, ADDR+W, data, STOP + let mut read_buf = [0u8; 3]; + let write_data = [0xCC, 0xDD, 0xEE]; + + let mut ops = [ + Operation::Read(&mut read_buf), + Operation::Write(&write_data), + ]; + + match i2c.blocking_transaction(addr, &mut ops) { + Ok(_) => { + info!("✓ Read-then-write succeeded"); + info!(" Read: {:02x}", read_buf); + info!(" Written: {:02x}", write_data); + } + Err(e) => warn!("✗ Read-then-write failed: {:?}", e), + } + + info!("Expected: START, ADDR+R, [3 bytes], NACK, RESTART, ADDR+W, [3 bytes], STOP"); + info!("Check Analog Discovery: RESTART between read and write"); +} + +fn test_mixed_sequence(i2c: &mut I2c<'static, Blocking, Master>, addr: u8) { + // Complex: W, W, R, R, W, R + // Groups: [W,W] RESTART [R,R] RESTART [W] RESTART [R] + let w1 = [0x01, 0x02]; + let w2 = [0x03, 0x04]; + let mut r1 = [0u8; 2]; + let mut r2 = [0u8; 2]; + let w3 = [0x05]; + let mut r3 = [0u8; 1]; + + let mut ops = [ + Operation::Write(&w1), + Operation::Write(&w2), + Operation::Read(&mut r1), + Operation::Read(&mut r2), + Operation::Write(&w3), + Operation::Read(&mut r3), + ]; + + match i2c.blocking_transaction(addr, &mut ops) { + Ok(_) => { + info!("✓ Mixed sequence succeeded"); + info!(" r1: {:02x}", r1); + info!(" r2: {:02x}", r2); + info!(" r3: {:02x}", r3); + } + Err(e) => warn!("✗ Mixed sequence failed: {:?}", e), + } + + info!("Expected sequence:"); + info!(" START, ADDR+W, [4 bytes merged], RESTART,"); + info!(" ADDR+R, [4 bytes merged], NACK, RESTART,"); + info!(" ADDR+W, [1 byte], RESTART,"); + info!(" ADDR+R, [1 byte], NACK, STOP"); +} + +fn test_single_operations(i2c: &mut I2c<'static, Blocking, Master>, addr: u8) { + // Test single write + let write_data = [0xFF]; + let mut ops = [Operation::Write(&write_data)]; + + match i2c.blocking_transaction(addr, &mut ops) { + Ok(_) => info!("✓ Single write succeeded"), + Err(e) => warn!("✗ Single write failed: {:?}", e), + } + + // Test single read + let mut read_buf = [0u8; 1]; + let mut ops = [Operation::Read(&mut read_buf)]; + + match i2c.blocking_transaction(addr, &mut ops) { + Ok(_) => info!("✓ Single read succeeded, data: 0x{:02x}", read_buf[0]), + Err(e) => warn!("✗ Single read failed: {:?}", e), + } +} -- cgit From d4e2caab31c1e9802120141cebc0a00b04471c16 Mon Sep 17 00:00:00 2001 From: Valentin Trophime Date: Wed, 12 Nov 2025 13:19:42 +0100 Subject: Add docs for embassy-rp::pio::get_x assumption about autopush. --- embassy-rp/CHANGELOG.md | 2 ++ embassy-rp/src/pio/instr.rs | 16 ++++++++++++++++ 2 files changed, 18 insertions(+) diff --git a/embassy-rp/CHANGELOG.md b/embassy-rp/CHANGELOG.md index 3b3cb5351..4b0d738a7 100644 --- a/embassy-rp/CHANGELOG.md +++ b/embassy-rp/CHANGELOG.md @@ -8,6 +8,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## Unreleased - ReleaseDate +- Add documentation for pio `get_x` about autopush. - Fix several minor typos in documentation - Add PIO SPI - Add PIO I2S input @@ -114,3 +115,4 @@ Small release fixing a few gnarly bugs, upgrading is strongly recommended. - rename the Channel trait to Slice and the PwmPin to PwmChannel - i2c: Fix race condition that appears on fast repeated transfers. - Add a basic "read to break" function + diff --git a/embassy-rp/src/pio/instr.rs b/embassy-rp/src/pio/instr.rs index b15d507de..304ddb20a 100644 --- a/embassy-rp/src/pio/instr.rs +++ b/embassy-rp/src/pio/instr.rs @@ -5,6 +5,10 @@ use crate::pio::{Instance, StateMachine}; impl<'d, PIO: Instance, const SM: usize> StateMachine<'d, PIO, SM> { /// Set value of scratch register X. + /// + /// SAFETY: autopull enabled else it will write undefined data. + /// Make sure to have setup the according configuration see + /// [shift_out](crate::pio::Config::shift_out) and [auto_fill](crate::pio::ShiftConfig::auto_fill). pub unsafe fn set_x(&mut self, value: u32) { const OUT: u16 = InstructionOperands::OUT { destination: OutDestination::X, @@ -16,6 +20,10 @@ impl<'d, PIO: Instance, const SM: usize> StateMachine<'d, PIO, SM> { } /// Get value of scratch register X. + /// + /// SAFETY: autopush enabled else it will read undefined data. + /// Make sure to have setup the according configurations see + /// [shift_in](crate::pio::Config::shift_in) and [auto_fill](crate::pio::ShiftConfig::auto_fill). pub unsafe fn get_x(&mut self) -> u32 { const IN: u16 = InstructionOperands::IN { source: InSource::X, @@ -27,6 +35,10 @@ impl<'d, PIO: Instance, const SM: usize> StateMachine<'d, PIO, SM> { } /// Set value of scratch register Y. + /// + /// SAFETY: autopull enabled else it will write undefined data. + /// Make sure to have setup the according configuration see + /// [shift_out](crate::pio::Config::shift_out) and [auto_fill](crate::pio::ShiftConfig::auto_fill). pub unsafe fn set_y(&mut self, value: u32) { const OUT: u16 = InstructionOperands::OUT { destination: OutDestination::Y, @@ -38,6 +50,10 @@ impl<'d, PIO: Instance, const SM: usize> StateMachine<'d, PIO, SM> { } /// Get value of scratch register Y. + /// + /// SAFETY: autopush enabled else it will read undefined data. + /// Make sure to have setup the according configurations see + /// [shift_in](crate::pio::Config::shift_in) and [auto_fill](crate::pio::ShiftConfig::auto_fill). pub unsafe fn get_y(&mut self) -> u32 { const IN: u16 = InstructionOperands::IN { source: InSource::Y, -- cgit From 2d73fe88935bcc42452907b42a7de5a9fa5ab1f8 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Wed, 12 Nov 2025 13:49:17 +0100 Subject: stm32: add all N6 chips, add N6 to docs build. --- embassy-stm32/Cargo.toml | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/embassy-stm32/Cargo.toml b/embassy-stm32/Cargo.toml index a722d2379..ec49924a2 100644 --- a/embassy-stm32/Cargo.toml +++ b/embassy-stm32/Cargo.toml @@ -139,6 +139,7 @@ flavors = [ { regex_feature = "stm32wb.*", target = "thumbv7em-none-eabi" }, { regex_feature = "stm32wba.*", target = "thumbv8m.main-none-eabihf" }, { regex_feature = "stm32wl.*", target = "thumbv7em-none-eabi" }, + { regex_feature = "stm32n6.*", target = "thumbv8m.main-none-eabihf" }, ] [package.metadata.docs.rs] @@ -1642,7 +1643,30 @@ stm32l562qe = [ "stm32-metapac/stm32l562qe" ] stm32l562re = [ "stm32-metapac/stm32l562re" ] stm32l562ve = [ "stm32-metapac/stm32l562ve" ] stm32l562ze = [ "stm32-metapac/stm32l562ze" ] +stm32n645a0 = [ "stm32-metapac/stm32n645a0" ] +stm32n645b0 = [ "stm32-metapac/stm32n645b0" ] +stm32n645i0 = [ "stm32-metapac/stm32n645i0" ] +stm32n645l0 = [ "stm32-metapac/stm32n645l0" ] +stm32n645x0 = [ "stm32-metapac/stm32n645x0" ] +stm32n645z0 = [ "stm32-metapac/stm32n645z0" ] +stm32n647a0 = [ "stm32-metapac/stm32n647a0" ] +stm32n647b0 = [ "stm32-metapac/stm32n647b0" ] +stm32n647i0 = [ "stm32-metapac/stm32n647i0" ] +stm32n647l0 = [ "stm32-metapac/stm32n647l0" ] +stm32n647x0 = [ "stm32-metapac/stm32n647x0" ] +stm32n647z0 = [ "stm32-metapac/stm32n647z0" ] +stm32n655a0 = [ "stm32-metapac/stm32n655a0" ] +stm32n655b0 = [ "stm32-metapac/stm32n655b0" ] +stm32n655i0 = [ "stm32-metapac/stm32n655i0" ] +stm32n655l0 = [ "stm32-metapac/stm32n655l0" ] +stm32n655x0 = [ "stm32-metapac/stm32n655x0" ] +stm32n655z0 = [ "stm32-metapac/stm32n655z0" ] +stm32n657a0 = [ "stm32-metapac/stm32n657a0" ] +stm32n657b0 = [ "stm32-metapac/stm32n657b0" ] +stm32n657i0 = [ "stm32-metapac/stm32n657i0" ] +stm32n657l0 = [ "stm32-metapac/stm32n657l0" ] stm32n657x0 = [ "stm32-metapac/stm32n657x0" ] +stm32n657z0 = [ "stm32-metapac/stm32n657z0" ] stm32u031c6 = [ "stm32-metapac/stm32u031c6" ] stm32u031c8 = [ "stm32-metapac/stm32u031c8" ] stm32u031f4 = [ "stm32-metapac/stm32u031f4" ] -- cgit From 74b5f14ede9a6b4349932bff41dac077afc47fa2 Mon Sep 17 00:00:00 2001 From: HybridChild Date: Wed, 12 Nov 2025 19:31:21 +0100 Subject: stm32/i2c_v2: Refactor transaction implementation --- embassy-stm32/src/i2c/v2.rs | 87 +++++++++++++++++---------------------------- 1 file changed, 33 insertions(+), 54 deletions(-) diff --git a/embassy-stm32/src/i2c/v2.rs b/embassy-stm32/src/i2c/v2.rs index 061f4ff3a..3c43887c0 100644 --- a/embassy-stm32/src/i2c/v2.rs +++ b/embassy-stm32/src/i2c/v2.rs @@ -98,6 +98,27 @@ pub(crate) unsafe fn on_interrupt() { } impl<'d, M: Mode, IM: MasterMode> I2c<'d, M, IM> { + #[inline] + fn to_reload(reload: bool) -> i2c::vals::Reload { + if reload { + i2c::vals::Reload::NOT_COMPLETED + } else { + i2c::vals::Reload::COMPLETED + } + } + + /// Calculate total bytes in a group of operations + #[inline] + fn total_operation_bytes(operations: &[Operation<'_>]) -> usize { + operations + .iter() + .map(|op| match op { + Operation::Write(buf) => buf.len(), + Operation::Read(buf) => buf.len(), + }) + .sum() + } + pub(crate) fn init(&mut self, config: Config) { self.info.regs.cr1().modify(|reg| { reg.set_pe(false); @@ -147,12 +168,6 @@ impl<'d, M: Mode, IM: MasterMode> I2c<'d, M, IM> { // `buffer`. The START bit can be set even if the bus // is BUSY or I2C is in slave mode. - let reload = if reload { - i2c::vals::Reload::NOT_COMPLETED - } else { - i2c::vals::Reload::COMPLETED - }; - info.regs.cr2().modify(|w| { w.set_sadd(address.addr() << 1); w.set_add10(address.add_mode()); @@ -160,7 +175,7 @@ impl<'d, M: Mode, IM: MasterMode> I2c<'d, M, IM> { w.set_nbytes(length as u8); w.set_start(true); w.set_autoend(stop.autoend()); - w.set_reload(reload); + w.set_reload(Self::to_reload(reload)); }); Ok(()) @@ -191,12 +206,6 @@ impl<'d, M: Mode, IM: MasterMode> I2c<'d, M, IM> { } } - let reload = if reload { - i2c::vals::Reload::NOT_COMPLETED - } else { - i2c::vals::Reload::COMPLETED - }; - // Set START and prepare to send `bytes`. The // START bit can be set even if the bus is BUSY or // I2C is in slave mode. @@ -207,7 +216,7 @@ impl<'d, M: Mode, IM: MasterMode> I2c<'d, M, IM> { w.set_nbytes(length as u8); w.set_start(true); w.set_autoend(stop.autoend()); - w.set_reload(reload); + w.set_reload(Self::to_reload(reload)); }); Ok(()) @@ -220,15 +229,9 @@ impl<'d, M: Mode, IM: MasterMode> I2c<'d, M, IM> { timeout.check()?; } - let will_reload = if will_reload { - i2c::vals::Reload::NOT_COMPLETED - } else { - i2c::vals::Reload::COMPLETED - }; - info.regs.cr2().modify(|w| { w.set_nbytes(length as u8); - w.set_reload(will_reload); + w.set_reload(Self::to_reload(will_reload)); w.set_autoend(stop.autoend()); }); @@ -400,7 +403,7 @@ impl<'d, M: Mode, IM: MasterMode> I2c<'d, M, IM> { address, read.len().min(255), Stop::Automatic, - last_chunk_idx != 0, + last_chunk_idx != 0, // reload restart, timeout, )?; @@ -569,13 +572,7 @@ impl<'d, M: Mode, IM: MasterMode> I2c<'d, M, IM> { timeout: Timeout, ) -> Result<(), Error> { // Calculate total bytes across all operations in this group - let total_bytes: usize = operations - .iter() - .map(|op| match op { - Operation::Write(buf) => buf.len(), - _ => 0, - }) - .sum(); + let total_bytes = Self::total_operation_bytes(operations); if total_bytes == 0 { // Handle empty write group - just send address @@ -641,13 +638,7 @@ impl<'d, M: Mode, IM: MasterMode> I2c<'d, M, IM> { timeout: Timeout, ) -> Result<(), Error> { // Calculate total bytes across all operations in this group - let total_bytes: usize = operations - .iter() - .map(|op| match op { - Operation::Read(buf) => buf.len(), - _ => 0, - }) - .sum(); + let total_bytes = Self::total_operation_bytes(operations); if total_bytes == 0 { // Handle empty read group @@ -657,7 +648,7 @@ impl<'d, M: Mode, IM: MasterMode> I2c<'d, M, IM> { address, 0, if is_last_group { Stop::Automatic } else { Stop::Software }, - false, + false, // reload !is_first_group, timeout, )?; @@ -1000,7 +991,7 @@ impl<'d, IM: MasterMode> I2c<'d, Async, IM> { address, total_len.min(255), Stop::Automatic, - total_len > 255, + total_len > 255, // reload restart, timeout, )?; @@ -1183,13 +1174,7 @@ impl<'d, IM: MasterMode> I2c<'d, Async, IM> { timeout: Timeout, ) -> Result<(), Error> { // Calculate total bytes across all operations in this group - let total_bytes: usize = operations - .iter() - .map(|op| match op { - Operation::Write(buf) => buf.len(), - _ => 0, - }) - .sum(); + let total_bytes = Self::total_operation_bytes(operations); if total_bytes == 0 { // Handle empty write group using blocking call @@ -1249,13 +1234,7 @@ impl<'d, IM: MasterMode> I2c<'d, Async, IM> { timeout: Timeout, ) -> Result<(), Error> { // Calculate total bytes across all operations in this group - let total_bytes: usize = operations - .iter() - .map(|op| match op { - Operation::Read(buf) => buf.len(), - _ => 0, - }) - .sum(); + let total_bytes = Self::total_operation_bytes(operations); if total_bytes == 0 { // Handle empty read group using blocking call @@ -1265,7 +1244,7 @@ impl<'d, IM: MasterMode> I2c<'d, Async, IM> { address, 0, if is_last_group { Stop::Automatic } else { Stop::Software }, - false, + false, // reload !is_first_group, timeout, )?; @@ -1313,7 +1292,7 @@ impl<'d, IM: MasterMode> I2c<'d, Async, IM> { } else { Stop::Software }, - last_chunk_idx != 0 || !is_last_read, + last_chunk_idx != 0 || !is_last_read, // reload restart, timeout, )?; -- cgit From 1c94d27a147035dfe40d33bae85be0308394dc53 Mon Sep 17 00:00:00 2001 From: HybridChild Date: Wed, 12 Nov 2025 20:13:48 +0100 Subject: stm32: Add entry about i2c v2 transaction implementation --- embassy-stm32/CHANGELOG.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/embassy-stm32/CHANGELOG.md b/embassy-stm32/CHANGELOG.md index 3431848d3..666ee1714 100644 --- a/embassy-stm32/CHANGELOG.md +++ b/embassy-stm32/CHANGELOG.md @@ -43,7 +43,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - fix: sdmmc: don't wait for DBCKEND flag on sdmmc_v2 devices as it never fires (Fixes #4723) - fix: usart: fix race condition in ringbuffered usart - feat: Add backup_sram::init() for H5 devices to access BKPSRAM -- feat: Add I2C MultiMaster (Slave) support for I2C v1 +- feat: stm32/i2c v1: Add I2C MultiMaster (Slave) support +- feat: stm32/i2c v2: Add transaction() and blocking_transaction() methods with contract-compliant operation merging - feat: stm32/fdcan: add ability to control automatic recovery from bus off ([#4821](https://github.com/embassy-rs/embassy/pull/4821)) - low-power: update rtc api to allow reconfig - adc: consolidate ringbuffer -- cgit From e32f78fde6f8130f1eb3effa131e42b7ca153ba6 Mon Sep 17 00:00:00 2001 From: xoviat Date: Wed, 12 Nov 2025 13:14:15 -0600 Subject: stm32/adc: extract into common add common low-level interface for adc --- embassy-stm32/src/adc/adc4.rs | 110 +-- embassy-stm32/src/adc/g4.rs | 459 ++++--------- embassy-stm32/src/adc/injected.rs | 2 +- embassy-stm32/src/adc/mod.rs | 173 +++++ embassy-stm32/src/adc/ringbuffered.rs | 2 +- embassy-stm32/src/adc/v2.rs | 233 +++---- embassy-stm32/src/adc/v3.rs | 738 +++++++-------------- embassy-stm32/src/adc/v4.rs | 327 ++++----- examples/stm32f4/src/bin/adc.rs | 2 +- examples/stm32f4/src/bin/adc_dma.rs | 8 +- examples/stm32g0/src/bin/adc_oversampling.rs | 14 +- examples/stm32g4/src/bin/adc.rs | 4 +- examples/stm32g4/src/bin/adc_differential.rs | 2 +- examples/stm32g4/src/bin/adc_dma.rs | 2 +- .../stm32g4/src/bin/adc_injected_and_regular.rs | 2 +- examples/stm32g4/src/bin/adc_oversampling.rs | 13 +- examples/stm32l4/src/bin/adc.rs | 9 +- examples/stm32l4/src/bin/adc_dma.rs | 5 +- examples/stm32u0/src/bin/adc.rs | 7 +- examples/stm32u5/src/bin/adc.rs | 16 +- 20 files changed, 869 insertions(+), 1259 deletions(-) diff --git a/embassy-stm32/src/adc/adc4.rs b/embassy-stm32/src/adc/adc4.rs index befa8ed4a..04d976513 100644 --- a/embassy-stm32/src/adc/adc4.rs +++ b/embassy-stm32/src/adc/adc4.rs @@ -4,7 +4,7 @@ use pac::adc::vals::{Adc4Dmacfg as Dmacfg, Adc4Exten as Exten, Adc4OversamplingR #[cfg(stm32wba)] use pac::adc::vals::{Chselrmod, Cont, Dmacfg, Exten, OversamplingRatio, Ovss, Smpsel}; -use super::{AdcChannel, AnyAdcChannel, RxDma4, SealedAdcChannel, blocking_delay_us}; +use super::{AdcChannel, AnyAdcChannel, RxDma4, blocking_delay_us}; use crate::dma::Transfer; #[cfg(stm32u5)] pub use crate::pac::adc::regs::Adc4Chselrmod0 as Chselr; @@ -24,56 +24,24 @@ pub const VREF_DEFAULT_MV: u32 = 3300; /// VREF voltage used for factory calibration of VREFINTCAL register. pub const VREF_CALIB_MV: u32 = 3300; -const VREF_CHANNEL: u8 = 0; -const VCORE_CHANNEL: u8 = 12; -const TEMP_CHANNEL: u8 = 13; -const VBAT_CHANNEL: u8 = 14; -const DAC_CHANNEL: u8 = 21; - -// NOTE: Vrefint/Temperature/Vbat are not available on all ADCs, this currently cannot be modeled with stm32-data, so these are available from the software on all ADCs -/// Internal voltage reference channel. -pub struct VrefInt; -impl AdcChannel for VrefInt {} -impl SealedAdcChannel for VrefInt { - fn channel(&self) -> u8 { - VREF_CHANNEL - } +impl<'d, T: Instance> super::SealedSpecialConverter for Adc4<'d, T> { + const CHANNEL: u8 = 0; } -/// Internal temperature channel. -pub struct Temperature; -impl AdcChannel for Temperature {} -impl SealedAdcChannel for Temperature { - fn channel(&self) -> u8 { - TEMP_CHANNEL - } +impl<'d, T: Instance> super::SealedSpecialConverter for Adc4<'d, T> { + const CHANNEL: u8 = 13; } -/// Internal battery voltage channel. -pub struct Vbat; -impl AdcChannel for Vbat {} -impl SealedAdcChannel for Vbat { - fn channel(&self) -> u8 { - VBAT_CHANNEL - } +impl<'d, T: Instance> super::SealedSpecialConverter for Adc4<'d, T> { + const CHANNEL: u8 = 12; } -/// Internal DAC channel. -pub struct Dac; -impl AdcChannel for Dac {} -impl SealedAdcChannel for Dac { - fn channel(&self) -> u8 { - DAC_CHANNEL - } +impl<'d, T: Instance> super::SealedSpecialConverter for Adc4<'d, T> { + const CHANNEL: u8 = 14; } -/// Internal Vcore channel. -pub struct Vcore; -impl AdcChannel for Vcore {} -impl SealedAdcChannel for Vcore { - fn channel(&self) -> u8 { - VCORE_CHANNEL - } +impl<'d, T: Instance> super::SealedSpecialConverter for Adc4<'d, T> { + const CHANNEL: u8 = 21; } #[derive(Copy, Clone)] @@ -214,20 +182,6 @@ impl<'d, T: Instance> Adc4<'d, T> { ); } - let mut s = Self { adc }; - - s.power_up(); - - s.calibrate(); - blocking_delay_us(1); - - s.enable(); - s.configure(); - - s - } - - fn power_up(&mut self) { T::regs().isr().modify(|w| { w.set_ldordy(true); }); @@ -239,22 +193,15 @@ impl<'d, T: Instance> Adc4<'d, T> { T::regs().isr().modify(|w| { w.set_ldordy(true); }); - } - fn calibrate(&mut self) { T::regs().cr().modify(|w| w.set_adcal(true)); while T::regs().cr().read().adcal() {} T::regs().isr().modify(|w| w.set_eocal(true)); - } - fn enable(&mut self) { - T::regs().isr().write(|w| w.set_adrdy(true)); - T::regs().cr().modify(|w| w.set_aden(true)); - while !T::regs().isr().read().adrdy() {} - T::regs().isr().write(|w| w.set_adrdy(true)); - } + blocking_delay_us(1); + + Self::enable(); - fn configure(&mut self) { // single conversion mode, software trigger T::regs().cfgr1().modify(|w| { #[cfg(stm32u5)] @@ -280,51 +227,60 @@ impl<'d, T: Instance> Adc4<'d, T> { w.set_smpsel(i, Smpsel::SMP1); } }); + + Self { adc } + } + + fn enable() { + T::regs().isr().write(|w| w.set_adrdy(true)); + T::regs().cr().modify(|w| w.set_aden(true)); + while !T::regs().isr().read().adrdy() {} + T::regs().isr().write(|w| w.set_adrdy(true)); } /// Enable reading the voltage reference internal channel. - pub fn enable_vrefint(&self) -> VrefInt { + pub fn enable_vrefint(&self) -> super::VrefInt { T::regs().ccr().modify(|w| { w.set_vrefen(true); }); - VrefInt {} + super::VrefInt {} } /// Enable reading the temperature internal channel. - pub fn enable_temperature(&self) -> Temperature { + pub fn enable_temperature(&self) -> super::Temperature { T::regs().ccr().modify(|w| { w.set_vsensesel(true); }); - Temperature {} + super::Temperature {} } /// Enable reading the vbat internal channel. #[cfg(stm32u5)] - pub fn enable_vbat(&self) -> Vbat { + pub fn enable_vbat(&self) -> super::Vbat { T::regs().ccr().modify(|w| { w.set_vbaten(true); }); - Vbat {} + super::Vbat {} } /// Enable reading the vbat internal channel. - pub fn enable_vcore(&self) -> Vcore { - Vcore {} + pub fn enable_vcore(&self) -> super::Vcore { + super::Vcore {} } /// Enable reading the vbat internal channel. #[cfg(stm32u5)] - pub fn enable_dac_channel(&self, dac: DacChannel) -> Dac { + pub fn enable_dac_channel(&self, dac: DacChannel) -> super::Dac { let mux; match dac { DacChannel::OUT1 => mux = false, DacChannel::OUT2 => mux = true, } T::regs().or().modify(|w| w.set_chn21sel(mux)); - Dac {} + super::Dac {} } /// Set the ADC resolution. diff --git a/embassy-stm32/src/adc/g4.rs b/embassy-stm32/src/adc/g4.rs index 6430b0243..0a9f35a5b 100644 --- a/embassy-stm32/src/adc/g4.rs +++ b/embassy-stm32/src/adc/g4.rs @@ -1,5 +1,3 @@ -use core::mem; - #[allow(unused)] #[cfg(stm32h7)] use pac::adc::vals::{Adcaldif, Difsel, Exten}; @@ -10,15 +8,14 @@ pub use pac::adccommon::vals::Presc; pub use stm32_metapac::adc::vals::{Adstp, Dmacfg, Dmaen}; pub use stm32_metapac::adccommon::vals::Dual; -use super::{Adc, AdcChannel, AnyAdcChannel, Instance, Resolution, RxDma, SampleTime, blocking_delay_us}; +use super::{ + Adc, AnyAdcChannel, ConversionMode, Instance, RegularConversionMode, Resolution, RxDma, SampleTime, + blocking_delay_us, +}; use crate::adc::SealedAdcChannel; -use crate::dma::Transfer; use crate::time::Hertz; use crate::{Peri, pac, rcc}; -mod ringbuffered; -pub use ringbuffered::RingBufferedAdc; - mod injected; pub use injected::InjectedAdc; @@ -103,6 +100,19 @@ impl Prescaler { } } +/// ADC configuration +#[derive(Default)] +pub struct AdcConfig { + pub dual_mode: Option, + pub resolution: Option, + #[cfg(stm32g4)] + pub oversampling_shift: Option, + #[cfg(stm32g4)] + pub oversampling_ratio: Option, + #[cfg(stm32g4)] + pub oversampling_mode: Option<(Rovsm, Trovs, bool)>, +} + // Trigger source for ADC conversions¨ #[derive(Copy, Clone)] pub struct ConversionTrigger { @@ -112,18 +122,9 @@ pub struct ConversionTrigger { pub edge: Exten, } -// Conversion mode for regular ADC channels -#[derive(Copy, Clone)] -pub enum RegularConversionMode { - // Samples as fast as possible - Continuous, - // Sample at rate determined by external trigger - Triggered(ConversionTrigger), -} - impl<'d, T: Instance> Adc<'d, T> { /// Create a new ADC driver. - pub fn new(adc: Peri<'d, T>) -> Self { + pub fn new(adc: Peri<'d, T>, config: AdcConfig) -> Self { rcc::enable_and_reset::(); let prescaler = Prescaler::from_ker_ck(T::frequency()); @@ -181,10 +182,38 @@ impl<'d, T: Instance> Adc<'d, T> { w.set_exten(Exten::DISABLED); }); + if let Some(dual) = config.dual_mode { + T::common_regs().ccr().modify(|reg| { + reg.set_dual(dual); + }) + } + + if let Some(resolution) = config.resolution { + T::regs().cfgr().modify(|reg| reg.set_res(resolution.into())); + } + + #[cfg(stm32g4)] + if let Some(shift) = config.oversampling_shift { + T::regs().cfgr2().modify(|reg| reg.set_ovss(shift)); + } + + #[cfg(stm32g4)] + if let Some(ratio) = config.oversampling_ratio { + T::regs().cfgr2().modify(|reg| reg.set_ovsr(ratio)); + } + + #[cfg(stm32g4)] + if let Some((mode, trig_mode, enable)) = config.oversampling_mode { + T::regs().cfgr2().modify(|reg| reg.set_trovs(trig_mode)); + T::regs().cfgr2().modify(|reg| reg.set_rovsm(mode)); + T::regs().cfgr2().modify(|reg| reg.set_rovse(enable)); + } + Self { adc } } - fn enable() { + /// Enable the ADC + pub(super) fn enable() { // Make sure bits are off while T::regs().cr().read().addis() { // spin @@ -205,82 +234,33 @@ impl<'d, T: Instance> Adc<'d, T> { } } - /// Enable reading the voltage reference internal channel. - pub fn enable_vrefint(&self) -> super::VrefInt - where - T: super::SpecialConverter, - { - T::common_regs().ccr().modify(|reg| { - reg.set_vrefen(true); + /// Start regular adc conversion + pub(super) fn start() { + T::regs().cr().modify(|reg| { + reg.set_adstart(true); }); - - super::VrefInt {} } - /// Enable reading the temperature internal channel. - pub fn enable_temperature(&self) -> super::Temperature - where - T: super::SpecialConverter, - { - T::common_regs().ccr().modify(|reg| { - reg.set_vsenseen(true); - }); - - super::Temperature {} - } + /// Stop regular conversions and disable DMA + pub(super) fn stop() { + if T::regs().cr().read().adstart() && !T::regs().cr().read().addis() { + T::regs().cr().modify(|reg| { + reg.set_adstp(Adstp::STOP); + }); + // The software must poll ADSTART until the bit is reset before assuming the + // ADC is completely stopped + while T::regs().cr().read().adstart() {} + } - /// Enable reading the vbat internal channel. - pub fn enable_vbat(&self) -> super::Vbat - where - T: super::SpecialConverter, - { - T::common_regs().ccr().modify(|reg| { - reg.set_vbaten(true); + // Disable dma control and continuous conversion, if enabled + T::regs().cfgr().modify(|reg| { + reg.set_cont(false); + reg.set_dmaen(Dmaen::DISABLE); }); - - super::Vbat {} - } - - /// Set oversampling shift. - #[cfg(stm32g4)] - pub fn set_oversampling_shift(&mut self, shift: u8) { - T::regs().cfgr2().modify(|reg| reg.set_ovss(shift)); - } - - /// Set oversampling ratio. - #[cfg(stm32g4)] - pub fn set_oversampling_ratio(&mut self, ratio: u8) { - T::regs().cfgr2().modify(|reg| reg.set_ovsr(ratio)); - } - - /// Enable oversampling in regular mode. - #[cfg(stm32g4)] - pub fn enable_regular_oversampling_mode(&mut self, mode: Rovsm, trig_mode: Trovs, enable: bool) { - T::regs().cfgr2().modify(|reg| reg.set_trovs(trig_mode)); - T::regs().cfgr2().modify(|reg| reg.set_rovsm(mode)); - T::regs().cfgr2().modify(|reg| reg.set_rovse(enable)); - } - - // Reads that are not implemented as INJECTED in "blocking_read" - // #[cfg(stm32g4)] - // pub fn enalble_injected_oversampling_mode(&mut self, enable: bool) { - // T::regs().cfgr2().modify(|reg| reg.set_jovse(enable)); - // } - - // #[cfg(stm32g4)] - // pub fn enable_oversampling_regular_injected_mode(&mut self, enable: bool) { - // // the regularoversampling mode is forced to resumed mode (ROVSM bit ignored), - // T::regs().cfgr2().modify(|reg| reg.set_rovse(enable)); - // T::regs().cfgr2().modify(|reg| reg.set_jovse(enable)); - // } - - /// Set the ADC resolution. - pub fn set_resolution(&mut self, resolution: Resolution) { - T::regs().cfgr().modify(|reg| reg.set_res(resolution.into())); } /// Perform a single conversion. - fn convert(&mut self) -> u16 { + pub(super) fn convert() -> u16 { T::regs().isr().modify(|reg| { reg.set_eos(true); reg.set_eoc(true); @@ -298,136 +278,42 @@ impl<'d, T: Instance> Adc<'d, T> { T::regs().dr().read().0 as u16 } - /// Read an ADC pin. - pub fn blocking_read(&mut self, channel: &mut impl AdcChannel, sample_time: SampleTime) -> u16 { - channel.setup(); - - Self::configure_sequence([((channel.channel(), channel.is_differential()), sample_time)].into_iter()); - - #[cfg(stm32h7)] - { - T::regs().cfgr2().modify(|w| w.set_lshift(0)); - T::regs() - .pcsel() - .write(|w| w.set_pcsel(channel.channel() as _, Pcsel::PRESELECTED)); - } - - self.convert() - } - - /// Start regular adc conversion - pub(super) fn start() { - T::regs().cr().modify(|reg| { - reg.set_adstart(true); - }); - } - - /// Stop regular conversions - pub(super) fn stop() { - Self::stop_regular_conversions(); - } - - /// Teardown method for stopping regular ADC conversions - pub(super) fn teardown_dma() { - Self::stop_regular_conversions(); - - // Disable dma control - T::regs().cfgr().modify(|reg| { - reg.set_dmaen(Dmaen::DISABLE); - }); - } - - /// Read one or multiple ADC regular channels using DMA. - /// - /// `sequence` iterator and `readings` must have the same length. - /// - /// Example - /// ```rust,ignore - /// use embassy_stm32::adc::{Adc, AdcChannel} - /// - /// let mut adc = Adc::new(p.ADC1); - /// let mut adc_pin0 = p.PA0.into(); - /// let mut adc_pin1 = p.PA1.into(); - /// let mut measurements = [0u16; 2]; - /// - /// adc.read( - /// p.DMA1_CH2.reborrow(), - /// [ - /// (&mut *adc_pin0, SampleTime::CYCLES160_5), - /// (&mut *adc_pin1, SampleTime::CYCLES160_5), - /// ] - /// .into_iter(), - /// &mut measurements, - /// ) - /// .await; - /// defmt::info!("measurements: {}", measurements); - /// ``` - /// - /// Note: This is not very efficient as the ADC needs to be reconfigured for each read. Use - /// `into_ring_buffered`, `into_ring_buffered_and_injected` - pub async fn read( - &mut self, - rx_dma: Peri<'_, impl RxDma>, - sequence: impl ExactSizeIterator, SampleTime)>, - readings: &mut [u16], - ) { - assert!(sequence.len() != 0, "Asynchronous read sequence cannot be empty"); - assert!( - sequence.len() == readings.len(), - "Sequence length must be equal to readings length" - ); - assert!( - sequence.len() <= 16, - "Asynchronous read sequence cannot be more than 16 in length" - ); - - // Ensure no conversions are ongoing and ADC is enabled. - Self::stop_regular_conversions(); - Self::enable(); - - Self::configure_sequence( - sequence.map(|(channel, sample_time)| ((channel.channel, channel.is_differential), sample_time)), - ); - - // Set continuous mode with oneshot dma. - // Clear overrun flag before starting transfer. + pub(super) fn configure_dma(conversion_mode: ConversionMode) { T::regs().isr().modify(|reg| { reg.set_ovr(true); }); T::regs().cfgr().modify(|reg| { - reg.set_discen(false); - reg.set_cont(true); - reg.set_dmacfg(Dmacfg::ONE_SHOT); + reg.set_discen(false); // Convert all channels for each trigger + reg.set_dmacfg(match conversion_mode { + ConversionMode::Singular => Dmacfg::ONE_SHOT, + ConversionMode::Repeated(_) => Dmacfg::CIRCULAR, + }); reg.set_dmaen(Dmaen::ENABLE); }); - let request = rx_dma.request(); - let transfer = unsafe { - Transfer::new_read( - rx_dma, - request, - T::regs().dr().as_ptr() as *mut u16, - readings, - Default::default(), - ) - }; - - // Start conversion - T::regs().cr().modify(|reg| { - reg.set_adstart(true); - }); - - // Wait for conversion sequence to finish. - transfer.await; + if let ConversionMode::Repeated(mode) = conversion_mode { + match mode { + RegularConversionMode::Continuous => { + T::regs().cfgr().modify(|reg| { + reg.set_cont(true); + }); + } + RegularConversionMode::Triggered(trigger) => { + T::regs().cfgr().modify(|r| { + r.set_cont(false); // New trigger is neede for each sample to be read + }); - // Ensure conversions are finished. - Self::stop_regular_conversions(); + T::regs().cfgr().modify(|r| { + r.set_extsel(trigger.channel); + r.set_exten(trigger.edge); + }); - // Reset configuration. - T::regs().cfgr().modify(|reg| { - reg.set_cont(false); - }); + // Regular conversions uses DMA so no need to generate interrupt + T::regs().ier().modify(|r| r.set_eosie(false)); + } + } + } } pub(super) fn configure_sequence(sequence: impl ExactSizeIterator) { @@ -489,95 +375,54 @@ impl<'d, T: Instance> Adc<'d, T> { } } - /// Set external trigger for regular conversion sequence - fn set_regular_conversion_trigger(&mut self, trigger: ConversionTrigger) { - T::regs().cfgr().modify(|r| { - r.set_extsel(trigger.channel); - r.set_exten(trigger.edge); + /// Enable reading the voltage reference internal channel. + pub fn enable_vrefint(&self) -> super::VrefInt + where + T: super::SpecialConverter, + { + T::common_regs().ccr().modify(|reg| { + reg.set_vrefen(true); }); - // Regular conversions uses DMA so no need to generate interrupt - T::regs().ier().modify(|r| r.set_eosie(false)); - } - // Dual ADC mode selection - pub fn configure_dual_mode(&mut self, val: Dual) { - T::common_regs().ccr().modify(|reg| { - reg.set_dual(val); - }) + super::VrefInt {} } - /// Configures the ADC to use a DMA ring buffer for continuous data acquisition. - /// - /// Use the [`read`] method to retrieve measurements from the DMA ring buffer. The read buffer - /// should be exactly half the size of `dma_buf`. When using triggered mode, it is recommended - /// to configure `dma_buf` as a double buffer so that one half can be read while the other half - /// is being filled by the DMA, preventing data loss. The trigger period of the ADC effectively - /// defines the period at which the buffer should be read. - /// - /// If continous conversion mode is selected, the provided `dma_buf` must be large enough to prevent - /// DMA buffer overruns. Its length should be a multiple of the number of ADC channels being measured. - /// For example, if 3 channels are measured and you want to store 40 samples per channel, - /// the buffer length should be `3 * 40 = 120`. - /// - /// # Parameters - /// - `dma`: The DMA peripheral used to transfer ADC data into the buffer. - /// - `dma_buf`: The buffer where DMA stores ADC samples. - /// - `regular_sequence`: Sequence of channels and sample times for regular ADC conversions. - /// - `regular_conversion_mode`: Mode for regular conversions (continuous or triggered). - /// - /// # Returns - /// A `RingBufferedAdc<'a, T>` instance configured for continuous DMA-based sampling. - pub fn into_ring_buffered<'a>( - mut self, - dma: Peri<'a, impl RxDma>, - dma_buf: &'a mut [u16], - sequence: impl ExactSizeIterator, SampleTime)>, - mode: RegularConversionMode, - ) -> RingBufferedAdc<'a, T> { - assert!(!dma_buf.is_empty() && dma_buf.len() <= 0xFFFF); - assert!(sequence.len() != 0, "Asynchronous read sequence cannot be empty"); - assert!( - sequence.len() <= 16, - "Asynchronous read sequence cannot be more than 16 in length" - ); - // reset conversions and enable the adc - Self::stop_regular_conversions(); - Self::enable(); - - //adc side setup - Self::configure_sequence( - sequence.map(|(channel, sample_time)| ((channel.channel, channel.is_differential), sample_time)), - ); - - // Clear overrun flag before starting transfer. - T::regs().isr().modify(|reg| { - reg.set_ovr(true); + /// Enable reading the temperature internal channel. + pub fn enable_temperature(&self) -> super::Temperature + where + T: super::SpecialConverter, + { + T::common_regs().ccr().modify(|reg| { + reg.set_vsenseen(true); }); - T::regs().cfgr().modify(|reg| { - reg.set_discen(false); // Convert all channels for each trigger - reg.set_dmacfg(Dmacfg::CIRCULAR); - reg.set_dmaen(Dmaen::ENABLE); + super::Temperature {} + } + + /// Enable reading the vbat internal channel. + pub fn enable_vbat(&self) -> super::Vbat + where + T: super::SpecialConverter, + { + T::common_regs().ccr().modify(|reg| { + reg.set_vbaten(true); }); - match mode { - RegularConversionMode::Continuous => { - T::regs().cfgr().modify(|reg| { - reg.set_cont(true); - }); - } - RegularConversionMode::Triggered(trigger) => { - T::regs().cfgr().modify(|r| { - r.set_cont(false); // New trigger is neede for each sample to be read - }); - self.set_regular_conversion_trigger(trigger); - } - } + super::Vbat {} + } - mem::forget(self); + // Reads that are not implemented as INJECTED in "blocking_read" + // #[cfg(stm32g4)] + // pub fn enalble_injected_oversampling_mode(&mut self, enable: bool) { + // T::regs().cfgr2().modify(|reg| reg.set_jovse(enable)); + // } - RingBufferedAdc::new(dma, dma_buf) - } + // #[cfg(stm32g4)] + // pub fn enable_oversampling_regular_injected_mode(&mut self, enable: bool) { + // // the regularoversampling mode is forced to resumed mode (ROVSM bit ignored), + // T::regs().cfgr2().modify(|reg| reg.set_rovse(enable)); + // T::regs().cfgr2().modify(|reg| reg.set_jovse(enable)); + // } /// Configures the ADC for injected conversions. /// @@ -607,7 +452,7 @@ impl<'d, T: Instance> Adc<'d, T> { /// - Accessing samples beyond `N` will result in a panic; use the returned type /// `InjectedAdc` to enforce bounds at compile time. pub fn setup_injected_conversions<'a, const N: usize>( - mut self, + self, sequence: [(AnyAdcChannel, SampleTime); N], trigger: ConversionTrigger, interrupt: bool, @@ -619,7 +464,7 @@ impl<'d, T: Instance> Adc<'d, T> { NR_INJECTED_RANKS ); - Self::stop_regular_conversions(); + Self::stop(); Self::enable(); T::regs().jsqr().modify(|w| w.set_jl(N as u8 - 1)); @@ -649,8 +494,16 @@ impl<'d, T: Instance> Adc<'d, T> { T::regs().cfgr().modify(|reg| reg.set_jdiscen(false)); - self.set_injected_conversion_trigger(trigger); - self.enable_injected_eos_interrupt(interrupt); + // Set external trigger for injected conversion sequence + // Possible trigger values are seen in Table 167 in RM0440 Rev 9 + T::regs().jsqr().modify(|r| { + r.set_jextsel(trigger.channel); + r.set_jexten(trigger.edge); + }); + + // Enable end of injected sequence interrupt + T::regs().ier().modify(|r| r.set_jeosie(interrupt)); + Self::start_injected_conversions(); InjectedAdc::new(sequence) // InjectedAdc<'a, T, N> now borrows the channels @@ -688,7 +541,7 @@ impl<'d, T: Instance> Adc<'d, T> { injected_sequence: [(AnyAdcChannel, SampleTime); N], injected_trigger: ConversionTrigger, injected_interrupt: bool, - ) -> (RingBufferedAdc<'a, T>, InjectedAdc) { + ) -> (super::RingBufferedAdc<'a, T>, InjectedAdc) { unsafe { ( Self { @@ -721,32 +574,6 @@ impl<'d, T: Instance> Adc<'d, T> { reg.set_jadstart(true); }); } - - /// Set external trigger for injected conversion sequence - /// Possible trigger values are seen in Table 167 in RM0440 Rev 9 - fn set_injected_conversion_trigger(&mut self, trigger: ConversionTrigger) { - T::regs().jsqr().modify(|r| { - r.set_jextsel(trigger.channel); - r.set_jexten(trigger.edge); - }); - } - - /// Enable end of injected sequence interrupt - fn enable_injected_eos_interrupt(&mut self, enable: bool) { - T::regs().ier().modify(|r| r.set_jeosie(enable)); - } - - // Stop regular conversions - fn stop_regular_conversions() { - if T::regs().cr().read().adstart() && !T::regs().cr().read().addis() { - T::regs().cr().modify(|reg| { - reg.set_adstp(Adstp::STOP); - }); - // The software must poll ADSTART until the bit is reset before assuming the - // ADC is completely stopped - while T::regs().cr().read().adstart() {} - } - } } impl InjectedAdc { diff --git a/embassy-stm32/src/adc/injected.rs b/embassy-stm32/src/adc/injected.rs index f9f1bba2a..7bb3a541c 100644 --- a/embassy-stm32/src/adc/injected.rs +++ b/embassy-stm32/src/adc/injected.rs @@ -38,7 +38,7 @@ impl InjectedAdc { impl Drop for InjectedAdc { fn drop(&mut self) { - Adc::::teardown_dma(); + Adc::::stop(); compiler_fence(Ordering::SeqCst); } } diff --git a/embassy-stm32/src/adc/mod.rs b/embassy-stm32/src/adc/mod.rs index e321c4fa1..bf404d6ef 100644 --- a/embassy-stm32/src/adc/mod.rs +++ b/embassy-stm32/src/adc/mod.rs @@ -17,6 +17,9 @@ #[cfg_attr(adc_c0, path = "c0.rs")] mod _version; +#[cfg(any(adc_v2, adc_g4, adc_v3, adc_g0, adc_u0))] +mod ringbuffered; + use core::marker::PhantomData; #[allow(unused)] @@ -25,6 +28,8 @@ pub use _version::*; use embassy_hal_internal::{PeripheralType, impl_peripheral}; #[cfg(any(adc_f1, adc_f3v1, adc_v1, adc_l0, adc_f3v2))] use embassy_sync::waitqueue::AtomicWaker; +#[cfg(any(adc_v2, adc_g4, adc_v3, adc_g0, adc_u0))] +pub use ringbuffered::RingBufferedAdc; #[cfg(any(adc_u5, adc_wba))] #[path = "adc4.rs"] @@ -105,6 +110,166 @@ pub(crate) fn blocking_delay_us(us: u32) { } } +#[cfg(any(adc_v2, adc_g4, adc_v3, adc_g0, adc_h5, adc_h7rs, adc_u0, adc_v4, adc_u5))] +pub(self) enum ConversionMode { + #[cfg(any(adc_g4, adc_v3, adc_g0, adc_h5, adc_h7rs, adc_u0, adc_v4, adc_u5))] + Singular, + #[allow(dead_code)] + Repeated(RegularConversionMode), +} + +#[cfg(any(adc_v2, adc_g4, adc_v3, adc_g0, adc_h5, adc_h7rs, adc_u0, adc_v4, adc_u5))] +// Conversion mode for regular ADC channels +#[derive(Copy, Clone)] +pub enum RegularConversionMode { + // Samples as fast as possible + Continuous, + #[cfg(adc_g4)] + // Sample at rate determined by external trigger + Triggered(ConversionTrigger), +} + +impl<'d, T: Instance> Adc<'d, T> { + #[cfg(any(adc_v2, adc_g4, adc_v3, adc_g0, adc_h5, adc_h7rs, adc_u0, adc_u5, adc_v4))] + /// Read an ADC pin. + pub fn blocking_read(&mut self, channel: &mut impl AdcChannel, sample_time: SampleTime) -> u16 { + #[cfg(any(adc_v1, adc_c0, adc_l0, adc_v2, adc_g4, adc_v4, adc_u5, adc_wba))] + channel.setup(); + + #[cfg(not(adc_v4))] + Self::enable(); + Self::configure_sequence([((channel.channel(), channel.is_differential()), sample_time)].into_iter()); + + Self::convert() + } + + #[cfg(any(adc_g4, adc_v3, adc_g0, adc_h5, adc_h7rs, adc_u0, adc_v4, adc_u5))] + /// Read one or multiple ADC regular channels using DMA. + /// + /// `sequence` iterator and `readings` must have the same length. + /// + /// Example + /// ```rust,ignore + /// use embassy_stm32::adc::{Adc, AdcChannel} + /// + /// let mut adc = Adc::new(p.ADC1); + /// let mut adc_pin0 = p.PA0.into(); + /// let mut adc_pin1 = p.PA1.into(); + /// let mut measurements = [0u16; 2]; + /// + /// adc.read( + /// p.DMA1_CH2.reborrow(), + /// [ + /// (&mut *adc_pin0, SampleTime::CYCLES160_5), + /// (&mut *adc_pin1, SampleTime::CYCLES160_5), + /// ] + /// .into_iter(), + /// &mut measurements, + /// ) + /// .await; + /// defmt::info!("measurements: {}", measurements); + /// ``` + /// + /// Note: This is not very efficient as the ADC needs to be reconfigured for each read. Use + /// `into_ring_buffered`, `into_ring_buffered_and_injected` + pub async fn read( + &mut self, + rx_dma: embassy_hal_internal::Peri<'_, impl RxDma>, + sequence: impl ExactSizeIterator, SampleTime)>, + readings: &mut [u16], + ) { + assert!(sequence.len() != 0, "Asynchronous read sequence cannot be empty"); + assert!( + sequence.len() == readings.len(), + "Sequence length must be equal to readings length" + ); + assert!( + sequence.len() <= 16, + "Asynchronous read sequence cannot be more than 16 in length" + ); + + // Ensure no conversions are ongoing and ADC is enabled. + Self::stop(); + Self::enable(); + + Self::configure_sequence( + sequence.map(|(channel, sample_time)| ((channel.channel, channel.is_differential), sample_time)), + ); + + Self::configure_dma(ConversionMode::Singular); + + let request = rx_dma.request(); + let transfer = unsafe { + crate::dma::Transfer::new_read( + rx_dma, + request, + T::regs().dr().as_ptr() as *mut u16, + readings, + Default::default(), + ) + }; + + Self::start(); + + // Wait for conversion sequence to finish. + transfer.await; + + // Ensure conversions are finished. + Self::stop(); + } + + #[cfg(any(adc_v2, adc_g4, adc_v3, adc_g0, adc_u0))] + /// Configures the ADC to use a DMA ring buffer for continuous data acquisition. + /// + /// Use the [`read`] method to retrieve measurements from the DMA ring buffer. The read buffer + /// should be exactly half the size of `dma_buf`. When using triggered mode, it is recommended + /// to configure `dma_buf` as a double buffer so that one half can be read while the other half + /// is being filled by the DMA, preventing data loss. The trigger period of the ADC effectively + /// defines the period at which the buffer should be read. + /// + /// If continous conversion mode is selected, the provided `dma_buf` must be large enough to prevent + /// DMA buffer overruns. Its length should be a multiple of the number of ADC channels being measured. + /// For example, if 3 channels are measured and you want to store 40 samples per channel, + /// the buffer length should be `3 * 40 = 120`. + /// + /// # Parameters + /// - `dma`: The DMA peripheral used to transfer ADC data into the buffer. + /// - `dma_buf`: The buffer where DMA stores ADC samples. + /// - `regular_sequence`: Sequence of channels and sample times for regular ADC conversions. + /// - `regular_conversion_mode`: Mode for regular conversions (continuous or triggered). + /// + /// # Returns + /// A `RingBufferedAdc<'a, T>` instance configured for continuous DMA-based sampling. + pub fn into_ring_buffered<'a>( + self, + dma: embassy_hal_internal::Peri<'a, impl RxDma>, + dma_buf: &'a mut [u16], + sequence: impl ExactSizeIterator, SampleTime)>, + mode: RegularConversionMode, + ) -> RingBufferedAdc<'a, T> { + assert!(!dma_buf.is_empty() && dma_buf.len() <= 0xFFFF); + assert!(sequence.len() != 0, "Asynchronous read sequence cannot be empty"); + assert!( + sequence.len() <= 16, + "Asynchronous read sequence cannot be more than 16 in length" + ); + // reset conversions and enable the adc + Self::stop(); + Self::enable(); + + //adc side setup + Self::configure_sequence( + sequence.map(|(channel, sample_time)| ((channel.channel, channel.is_differential), sample_time)), + ); + + Self::configure_dma(ConversionMode::Repeated(mode)); + + core::mem::forget(self); + + RingBufferedAdc::new(dma, dma_buf) + } +} + pub(self) trait SpecialChannel {} /// Implemented for ADCs that have a special channel @@ -143,6 +308,14 @@ impl SpecialChannel for Temperature {} pub struct Vbat; impl SpecialChannel for Vbat {} +/// Vcore channel. +pub struct Vcore; +impl SpecialChannel for Vcore {} + +/// Internal dac channel. +pub struct Dac; +impl SpecialChannel for Dac {} + /// ADC instance. #[cfg(not(any( adc_f1, adc_v1, adc_l0, adc_v2, adc_v3, adc_v4, adc_g4, adc_f3v1, adc_f3v2, adc_g0, adc_u0, adc_h5, adc_h7rs, diff --git a/embassy-stm32/src/adc/ringbuffered.rs b/embassy-stm32/src/adc/ringbuffered.rs index 024c6acdc..62ea0d3a2 100644 --- a/embassy-stm32/src/adc/ringbuffered.rs +++ b/embassy-stm32/src/adc/ringbuffered.rs @@ -172,7 +172,7 @@ impl<'d, T: Instance> RingBufferedAdc<'d, T> { impl Drop for RingBufferedAdc<'_, T> { fn drop(&mut self) { - Adc::::teardown_dma(); + Adc::::stop(); compiler_fence(Ordering::SeqCst); diff --git a/embassy-stm32/src/adc/v2.rs b/embassy-stm32/src/adc/v2.rs index efa1cc68c..2f9fabafb 100644 --- a/embassy-stm32/src/adc/v2.rs +++ b/embassy-stm32/src/adc/v2.rs @@ -1,15 +1,11 @@ -use core::mem; use core::sync::atomic::{Ordering, compiler_fence}; -use super::{Temperature, Vbat, VrefInt, blocking_delay_us}; -use crate::adc::{Adc, AdcChannel, AnyAdcChannel, Instance, Resolution, RxDma, SampleTime, SealedAdcChannel}; +use super::{ConversionMode, Temperature, Vbat, VrefInt, blocking_delay_us}; +use crate::adc::{Adc, Instance, Resolution, SampleTime}; use crate::pac::adc::vals; use crate::time::Hertz; use crate::{Peri, rcc}; -mod ringbuffered; -pub use ringbuffered::RingBufferedAdc; - fn clear_interrupt_flags(r: crate::pac::adc::Adc) { r.sr().modify(|regs| { regs.set_eoc(false); @@ -89,11 +85,21 @@ impl Prescaler { } } +/// ADC configuration +#[derive(Default)] +pub struct AdcConfig { + resolution: Option, +} + impl<'d, T> Adc<'d, T> where T: Instance, { pub fn new(adc: Peri<'d, T>) -> Self { + Self::new_with_config(adc, Default::default()) + } + + pub fn new_with_config(adc: Peri<'d, T>, config: AdcConfig) -> Self { rcc::enable_and_reset::(); let presc = Prescaler::from_pclk2(T::frequency()); @@ -104,84 +110,14 @@ where blocking_delay_us(3); - Self { adc } - } - - /// Configures the ADC to use a DMA ring buffer for continuous data acquisition. - /// - /// The `dma_buf` should be large enough to prevent DMA buffer overrun. - /// The length of the `dma_buf` should be a multiple of the ADC channel count. - /// For example, if 3 channels are measured, its length can be 3 * 40 = 120 measurements. - /// - /// `read` method is used to read out measurements from the DMA ring buffer, and its buffer should be exactly half of the `dma_buf` length. - /// It is critical to call `read` frequently to prevent DMA buffer overrun. - /// - /// [`read`]: #method.read - pub fn into_ring_buffered<'a>( - self, - dma: Peri<'d, impl RxDma>, - dma_buf: &'d mut [u16], - sequence: impl ExactSizeIterator, SampleTime)>, - ) -> RingBufferedAdc<'d, T> { - assert!(!dma_buf.is_empty() && dma_buf.len() <= 0xFFFF); - - Self::configure_sequence(sequence.map(|(mut channel, sample_time)| { - channel.setup(); - - (channel.channel, sample_time) - })); - compiler_fence(Ordering::SeqCst); - - Self::setup_dma(); - - // Don't disable the clock - mem::forget(self); - - RingBufferedAdc::new(dma, dma_buf) - } - - pub fn blocking_read(&mut self, channel: &mut impl AdcChannel, sample_time: SampleTime) -> u16 { - channel.setup(); - - // Configure ADC - let channel = channel.channel(); - - Self::configure_sequence([(channel, sample_time)].into_iter()); - Self::blocking_convert() - } - - /// Enables internal voltage reference and returns [VrefInt], which can be used in - /// [Adc::read_internal()] to perform conversion. - pub fn enable_vrefint(&self) -> VrefInt { - T::common_regs().ccr().modify(|reg| { - reg.set_tsvrefe(true); - }); - - VrefInt {} - } - - /// Enables internal temperature sensor and returns [Temperature], which can be used in - /// [Adc::read_internal()] to perform conversion. - /// - /// On STM32F42 and STM32F43 this can not be used together with [Vbat]. If both are enabled, - /// temperature sensor will return vbat value. - pub fn enable_temperature(&self) -> Temperature { - T::common_regs().ccr().modify(|reg| { - reg.set_tsvrefe(true); - }); + if let Some(resolution) = config.resolution { + T::regs().cr1().modify(|reg| reg.set_res(resolution.into())); + } - Temperature {} + Self { adc } } - /// Enables vbat input and returns [Vbat], which can be used in - /// [Adc::read_internal()] to perform conversion. - pub fn enable_vbat(&self) -> Vbat { - T::common_regs().ccr().modify(|reg| { - reg.set_vbate(true); - }); - - Vbat {} - } + pub(super) fn enable() {} pub(super) fn start() { // Begin ADC conversions @@ -192,18 +128,31 @@ where } pub(super) fn stop() { + let r = T::regs(); + // Stop ADC - T::regs().cr2().modify(|reg| { + r.cr2().modify(|reg| { // Stop ADC reg.set_swstart(false); + // Stop ADC + reg.set_adon(false); + // Stop DMA + reg.set_dma(false); }); - } - pub fn set_resolution(&mut self, resolution: Resolution) { - T::regs().cr1().modify(|reg| reg.set_res(resolution.into())); + r.cr1().modify(|w| { + // Disable interrupt for end of conversion + w.set_eocie(false); + // Disable interrupt for overrun + w.set_ovrie(false); + }); + + clear_interrupt_flags(r); + + compiler_fence(Ordering::SeqCst); } - pub(super) fn blocking_convert() -> u16 { + pub(super) fn convert() -> u16 { // clear end of conversion flag T::regs().sr().modify(|reg| { reg.set_eoc(false); @@ -224,7 +173,44 @@ where T::regs().dr().read().0 as u16 } - pub(super) fn configure_sequence(sequence: impl ExactSizeIterator) { + pub(super) fn configure_dma(conversion_mode: ConversionMode) { + match conversion_mode { + ConversionMode::Repeated(_) => { + let r = T::regs(); + + // Clear all interrupts + r.sr().modify(|regs| { + regs.set_eoc(false); + regs.set_ovr(false); + regs.set_strt(false); + }); + + r.cr1().modify(|w| { + // Enable interrupt for end of conversion + w.set_eocie(true); + // Enable interrupt for overrun + w.set_ovrie(true); + // Scanning converisons of multiple channels + w.set_scan(true); + // Continuous conversion mode + w.set_discen(false); + }); + + r.cr2().modify(|w| { + // Enable DMA mode + w.set_dma(true); + // Enable continuous conversions + w.set_cont(true); + // DMA requests are issues as long as DMA=1 and data are converted. + w.set_dds(vals::Dds::CONTINUOUS); + // EOC flag is set at the end of each conversion. + w.set_eocs(vals::Eocs::EACH_CONVERSION); + }); + } + } + } + + pub(super) fn configure_sequence(sequence: impl ExactSizeIterator) { T::regs().cr2().modify(|reg| { reg.set_adon(true); }); @@ -234,7 +220,7 @@ where r.set_l((sequence.len() - 1).try_into().unwrap()); }); - for (i, (ch, sample_time)) in sequence.enumerate() { + for (i, ((ch, _), sample_time)) in sequence.enumerate() { // Set the channel in the right sequence field. T::regs().sqr3().modify(|w| w.set_sq(i, ch)); @@ -247,62 +233,37 @@ where } } - pub(super) fn setup_dma() { - let r = T::regs(); - - // Clear all interrupts - r.sr().modify(|regs| { - regs.set_eoc(false); - regs.set_ovr(false); - regs.set_strt(false); - }); - - r.cr1().modify(|w| { - // Enable interrupt for end of conversion - w.set_eocie(true); - // Enable interrupt for overrun - w.set_ovrie(true); - // Scanning converisons of multiple channels - w.set_scan(true); - // Continuous conversion mode - w.set_discen(false); + /// Enables internal voltage reference and returns [VrefInt], which can be used in + /// [Adc::read_internal()] to perform conversion. + pub fn enable_vrefint(&self) -> VrefInt { + T::common_regs().ccr().modify(|reg| { + reg.set_tsvrefe(true); }); - r.cr2().modify(|w| { - // Enable DMA mode - w.set_dma(true); - // Enable continuous conversions - w.set_cont(true); - // DMA requests are issues as long as DMA=1 and data are converted. - w.set_dds(vals::Dds::CONTINUOUS); - // EOC flag is set at the end of each conversion. - w.set_eocs(vals::Eocs::EACH_CONVERSION); - }); + VrefInt {} } - pub(super) fn teardown_dma() { - let r = T::regs(); - - // Stop ADC - r.cr2().modify(|reg| { - // Stop ADC - reg.set_swstart(false); - // Stop ADC - reg.set_adon(false); - // Stop DMA - reg.set_dma(false); + /// Enables internal temperature sensor and returns [Temperature], which can be used in + /// [Adc::read_internal()] to perform conversion. + /// + /// On STM32F42 and STM32F43 this can not be used together with [Vbat]. If both are enabled, + /// temperature sensor will return vbat value. + pub fn enable_temperature(&self) -> Temperature { + T::common_regs().ccr().modify(|reg| { + reg.set_tsvrefe(true); }); - r.cr1().modify(|w| { - // Disable interrupt for end of conversion - w.set_eocie(false); - // Disable interrupt for overrun - w.set_ovrie(false); - }); + Temperature {} + } - clear_interrupt_flags(r); + /// Enables vbat input and returns [Vbat], which can be used in + /// [Adc::read_internal()] to perform conversion. + pub fn enable_vbat(&self) -> Vbat { + T::common_regs().ccr().modify(|reg| { + reg.set_vbate(true); + }); - compiler_fence(Ordering::SeqCst); + Vbat {} } } diff --git a/embassy-stm32/src/adc/v3.rs b/embassy-stm32/src/adc/v3.rs index cbc217545..62b5043ee 100644 --- a/embassy-stm32/src/adc/v3.rs +++ b/embassy-stm32/src/adc/v3.rs @@ -1,9 +1,9 @@ use cfg_if::cfg_if; #[cfg(adc_g0)] use heapless::Vec; -use pac::adc::vals::Dmacfg; #[cfg(adc_g0)] -use pac::adc::vals::{Ckmode, Smpsel}; +use pac::adc::vals::Ckmode; +use pac::adc::vals::Dmacfg; #[cfg(adc_v3)] use pac::adc::vals::{OversamplingRatio, OversamplingShift, Rovsm, Trovs}; #[cfg(adc_g0)] @@ -11,18 +11,8 @@ pub use pac::adc::vals::{Ovsr, Ovss, Presc}; #[allow(unused_imports)] use super::SealedAdcChannel; -use super::{ - Adc, AdcChannel, AnyAdcChannel, Instance, Resolution, RxDma, SampleTime, Temperature, Vbat, VrefInt, - blocking_delay_us, -}; - -#[cfg(any(adc_v3, adc_g0, adc_u0))] -mod ringbuffered; - -#[cfg(any(adc_v3, adc_g0, adc_u0))] -use ringbuffered::RingBufferedAdc; - -use crate::dma::Transfer; +use super::{Adc, Instance, Resolution, SampleTime, Temperature, Vbat, VrefInt, blocking_delay_us}; +use crate::adc::ConversionMode; use crate::{Peri, pac, rcc}; /// Default VREF voltage used for sample conversion to millivolts. @@ -89,7 +79,7 @@ impl super::SealedSpecialConverter for T { cfg_if! { if #[cfg(any(adc_h5, adc_h7rs))] { pub struct VddCore; - impl AdcChannel for VddCore {} + impl super::AdcChannel for VddCore {} impl super::SealedAdcChannel for VddCore { fn channel(&self) -> u8 { 6 @@ -101,7 +91,7 @@ cfg_if! { cfg_if! { if #[cfg(adc_u0)] { pub struct DacOut; - impl AdcChannel for DacOut {} + impl super::AdcChannel for DacOut {} impl super::SealedAdcChannel for DacOut { fn channel(&self) -> u8 { 19 @@ -145,6 +135,32 @@ pub enum Clock { }} +#[cfg(adc_u0)] +type Ovss = u8; +#[cfg(adc_u0)] +type Ovsr = u8; +#[cfg(adc_v3)] +type Ovss = OversamplingShift; +#[cfg(adc_v3)] +type Ovsr = OversamplingRatio; + +/// Adc configuration +#[derive(Default)] +pub struct AdcConfig { + #[cfg(any(adc_u0, adc_g0, adc_v3))] + pub oversampling_shift: Option, + #[cfg(any(adc_u0, adc_g0, adc_v3))] + pub oversampling_ratio: Option, + #[cfg(any(adc_u0, adc_g0))] + pub oversampling_enable: Option, + #[cfg(adc_v3)] + pub oversampling_mode: Option<(Rovsm, Trovs, bool)>, + #[cfg(adc_g0)] + pub clock: Option, + pub resolution: Option, + pub averaging: Option, +} + impl<'d, T: Instance> Adc<'d, T> { /// Enable the voltage regulator fn init_regulator() { @@ -178,38 +194,6 @@ impl<'d, T: Instance> Adc<'d, T> { blocking_delay_us(1); } - #[cfg(any(adc_v3, adc_g0, adc_u0))] - pub(super) fn start() { - // Start adc conversion - T::regs().cr().modify(|reg| { - reg.set_adstart(true); - }); - } - - #[cfg(any(adc_v3, adc_g0, adc_u0))] - pub(super) fn stop() { - // Stop adc conversion - if T::regs().cr().read().adstart() && !T::regs().cr().read().addis() { - T::regs().cr().modify(|reg| { - reg.set_adstp(true); - }); - while T::regs().cr().read().adstart() {} - } - } - - #[cfg(any(adc_v3, adc_g0, adc_u0))] - pub(super) fn teardown_dma() { - //disable dma control - #[cfg(not(any(adc_g0, adc_u0)))] - T::regs().cfgr().modify(|reg| { - reg.set_dmaen(false); - }); - #[cfg(any(adc_g0, adc_u0))] - T::regs().cfgr1().modify(|reg| { - reg.set_dmaen(false); - }); - } - /// Initialize the ADC leaving any analog clock at reset value. /// For G0 and WL, this is the async clock without prescaler. pub fn new(adc: Peri<'d, T>) -> Self { @@ -218,6 +202,73 @@ impl<'d, T: Instance> Adc<'d, T> { Self { adc } } + pub fn new_with_config(adc: Peri<'d, T>, config: AdcConfig) -> Self { + #[cfg(not(adc_g0))] + let s = Self::new(adc); + + #[cfg(adc_g0)] + let s = match config.clock { + Some(clock) => Self::new_with_clock(adc, clock), + None => Self::new(adc), + }; + + #[cfg(any(adc_g0, adc_u0, adc_v3))] + if let Some(shift) = config.oversampling_shift { + T::regs().cfgr2().modify(|reg| reg.set_ovss(shift)); + } + + #[cfg(any(adc_g0, adc_u0, adc_v3))] + if let Some(ratio) = config.oversampling_ratio { + T::regs().cfgr2().modify(|reg| reg.set_ovsr(ratio)); + } + + #[cfg(any(adc_g0, adc_u0))] + if let Some(enable) = config.oversampling_enable { + T::regs().cfgr2().modify(|reg| reg.set_ovse(enable)); + } + + #[cfg(adc_v3)] + if let Some((mode, trig_mode, enable)) = config.oversampling_mode { + T::regs().cfgr2().modify(|reg| reg.set_trovs(trig_mode)); + T::regs().cfgr2().modify(|reg| reg.set_rovsm(mode)); + T::regs().cfgr2().modify(|reg| reg.set_rovse(enable)); + } + + if let Some(resolution) = config.resolution { + #[cfg(not(any(adc_g0, adc_u0)))] + T::regs().cfgr().modify(|reg| reg.set_res(resolution.into())); + #[cfg(any(adc_g0, adc_u0))] + T::regs().cfgr1().modify(|reg| reg.set_res(resolution.into())); + } + + if let Some(averaging) = config.averaging { + let (enable, samples, right_shift) = match averaging { + Averaging::Disabled => (false, 0, 0), + Averaging::Samples2 => (true, 0, 1), + Averaging::Samples4 => (true, 1, 2), + Averaging::Samples8 => (true, 2, 3), + Averaging::Samples16 => (true, 3, 4), + Averaging::Samples32 => (true, 4, 5), + Averaging::Samples64 => (true, 5, 6), + Averaging::Samples128 => (true, 6, 7), + Averaging::Samples256 => (true, 7, 8), + }; + T::regs().cfgr2().modify(|reg| { + #[cfg(not(any(adc_g0, adc_u0)))] + reg.set_rovse(enable); + #[cfg(any(adc_g0, adc_u0))] + reg.set_ovse(enable); + #[cfg(any(adc_h5, adc_h7rs))] + reg.set_ovsr(samples.into()); + #[cfg(not(any(adc_h5, adc_h7rs)))] + reg.set_ovsr(samples.into()); + reg.set_ovss(right_shift.into()); + }) + } + + s + } + #[cfg(adc_g0)] /// Initialize ADC with explicit clock for the analog ADC pub fn new_with_clock(adc: Peri<'d, T>, clock: Clock) -> Self { @@ -255,7 +306,7 @@ impl<'d, T: Instance> Adc<'d, T> { } // Enable ADC only when it is not already running. - fn enable(&mut self) { + pub(super) fn enable() { // Make sure bits are off while T::regs().cr().read().addis() { // spin @@ -276,258 +327,75 @@ impl<'d, T: Instance> Adc<'d, T> { } } - pub fn enable_vrefint(&self) -> VrefInt { - #[cfg(not(any(adc_g0, adc_u0)))] - T::common_regs().ccr().modify(|reg| { - reg.set_vrefen(true); - }); - #[cfg(any(adc_g0, adc_u0))] - T::regs().ccr().modify(|reg| { - reg.set_vrefen(true); - }); - - // "Table 24. Embedded internal voltage reference" states that it takes a maximum of 12 us - // to stabilize the internal voltage reference. - blocking_delay_us(15); - - VrefInt {} - } - - pub fn enable_temperature(&self) -> Temperature { - cfg_if! { - if #[cfg(any(adc_g0, adc_u0))] { - T::regs().ccr().modify(|reg| { - reg.set_tsen(true); - }); - } else if #[cfg(any(adc_h5, adc_h7rs))] { - T::common_regs().ccr().modify(|reg| { - reg.set_tsen(true); - }); - } else { - T::common_regs().ccr().modify(|reg| { - reg.set_ch17sel(true); - }); - } + pub(super) fn start() { + #[cfg(any(adc_v3, adc_g0, adc_u0))] + { + // Start adc conversion + T::regs().cr().modify(|reg| { + reg.set_adstart(true); + }); } - - Temperature {} } - pub fn enable_vbat(&self) -> Vbat { - cfg_if! { - if #[cfg(any(adc_g0, adc_u0))] { - T::regs().ccr().modify(|reg| { - reg.set_vbaten(true); - }); - } else if #[cfg(any(adc_h5, adc_h7rs))] { - T::common_regs().ccr().modify(|reg| { - reg.set_vbaten(true); - }); - } else { - T::common_regs().ccr().modify(|reg| { - reg.set_ch18sel(true); + pub(super) fn stop() { + #[cfg(any(adc_v3, adc_g0, adc_u0))] + { + // Ensure conversions are finished. + if T::regs().cr().read().adstart() && !T::regs().cr().read().addis() { + T::regs().cr().modify(|reg| { + reg.set_adstp(true); }); + while T::regs().cr().read().adstart() {} } - } - - Vbat {} - } - - /// Set the ADC resolution. - pub fn set_resolution(&mut self, resolution: Resolution) { - #[cfg(not(any(adc_g0, adc_u0)))] - T::regs().cfgr().modify(|reg| reg.set_res(resolution.into())); - #[cfg(any(adc_g0, adc_u0))] - T::regs().cfgr1().modify(|reg| reg.set_res(resolution.into())); - } - pub fn set_averaging(&mut self, averaging: Averaging) { - let (enable, samples, right_shift) = match averaging { - Averaging::Disabled => (false, 0, 0), - Averaging::Samples2 => (true, 0, 1), - Averaging::Samples4 => (true, 1, 2), - Averaging::Samples8 => (true, 2, 3), - Averaging::Samples16 => (true, 3, 4), - Averaging::Samples32 => (true, 4, 5), - Averaging::Samples64 => (true, 5, 6), - Averaging::Samples128 => (true, 6, 7), - Averaging::Samples256 => (true, 7, 8), - }; - T::regs().cfgr2().modify(|reg| { + // Reset configuration. #[cfg(not(any(adc_g0, adc_u0)))] - reg.set_rovse(enable); + T::regs().cfgr().modify(|reg| { + reg.set_cont(false); + reg.set_dmaen(false); + }); #[cfg(any(adc_g0, adc_u0))] - reg.set_ovse(enable); - #[cfg(any(adc_h5, adc_h7rs))] - reg.set_ovsr(samples.into()); - #[cfg(not(any(adc_h5, adc_h7rs)))] - reg.set_ovsr(samples.into()); - reg.set_ovss(right_shift.into()); - }) - } - /* - /// Convert a raw sample from the `Temperature` to deg C - pub fn to_degrees_centigrade(sample: u16) -> f32 { - (130.0 - 30.0) / (VtempCal130::get().read() as f32 - VtempCal30::get().read() as f32) - * (sample as f32 - VtempCal30::get().read() as f32) - + 30.0 - } - */ - - /// Perform a single conversion. - fn convert(&mut self) -> u16 { - T::regs().isr().modify(|reg| { - reg.set_eos(true); - reg.set_eoc(true); - }); - - // Start conversion - T::regs().cr().modify(|reg| { - reg.set_adstart(true); - }); - - while !T::regs().isr().read().eos() { - // spin + T::regs().cfgr1().modify(|reg| { + reg.set_cont(false); + reg.set_dmaen(false); + }); } - - T::regs().dr().read().0 as u16 } - /// Read an ADC channel. - pub fn blocking_read(&mut self, channel: &mut impl AdcChannel, sample_time: SampleTime) -> u16 { - self.read_channel(channel, sample_time) - } - - /// Read one or multiple ADC channels using DMA. - /// - /// `readings` must have a length that is a multiple of the length of the - /// `sequence` iterator. - /// - /// Note: The order of values in `readings` is defined by the pin ADC - /// channel number and not the pin order in `sequence`. - /// - /// Example - /// ```rust,ignore - /// use embassy_stm32::adc::{Adc, AdcChannel} - /// - /// let mut adc = Adc::new(p.ADC1); - /// let mut adc_pin0 = p.PA0.degrade_adc(); - /// let mut adc_pin1 = p.PA1.degrade_adc(); - /// let mut measurements = [0u16; 2]; - /// - /// adc.read( - /// p.DMA1_CH2.reborrow(), - /// [ - /// (&mut *adc_pin0, SampleTime::CYCLES160_5), - /// (&mut *adc_pin1, SampleTime::CYCLES160_5), - /// ] - /// .into_iter(), - /// &mut measurements, - /// ) - /// .await; - /// defmt::info!("measurements: {}", measurements); - /// ``` - pub async fn read( - &mut self, - rx_dma: Peri<'_, impl RxDma>, - sequence: impl ExactSizeIterator, SampleTime)>, - readings: &mut [u16], - ) { - assert!(sequence.len() != 0, "Asynchronous read sequence cannot be empty"); - assert!( - readings.len() % sequence.len() == 0, - "Readings length must be a multiple of sequence length" - ); - assert!( - sequence.len() <= 16, - "Asynchronous read sequence cannot be more than 16 in length" - ); - - #[cfg(all(feature = "low-power", stm32wlex))] - let _device_busy = crate::low_power::DeviceBusy::new_stop1(); - - // Ensure no conversions are ongoing and ADC is enabled. - Self::cancel_conversions(); - self.enable(); + /// Perform a single conversion. + pub(super) fn convert() -> u16 { + // Some models are affected by an erratum: + // If we perform conversions slower than 1 kHz, the first read ADC value can be + // corrupted, so we discard it and measure again. + // + // STM32L471xx: Section 2.7.3 + // STM32G4: Section 2.7.3 + #[cfg(any(rcc_l4, rcc_g4))] + let len = 2; - // Set sequence length - #[cfg(not(any(adc_g0, adc_u0)))] - T::regs().sqr1().modify(|w| { - w.set_l(sequence.len() as u8 - 1); - }); + #[cfg(not(any(rcc_l4, rcc_g4)))] + let len = 1; - #[cfg(adc_g0)] - { - let mut sample_times = Vec::::new(); - - T::regs().chselr().write(|chselr| { - T::regs().smpr().write(|smpr| { - for (channel, sample_time) in sequence { - chselr.set_chsel(channel.channel.into(), true); - if let Some(i) = sample_times.iter().position(|&t| t == sample_time) { - smpr.set_smpsel(channel.channel.into(), (i as u8).into()); - } else { - smpr.set_sample_time(sample_times.len(), sample_time); - if let Err(_) = sample_times.push(sample_time) { - panic!( - "Implementation is limited to {} unique sample times among all channels.", - SAMPLE_TIMES_CAPACITY - ); - } - } - } - }) + for _ in 0..len { + T::regs().isr().modify(|reg| { + reg.set_eos(true); + reg.set_eoc(true); }); - } - #[cfg(not(adc_g0))] - { - #[cfg(adc_u0)] - let mut channel_mask = 0; - - // Configure channels and ranks - for (_i, (channel, sample_time)) in sequence.enumerate() { - Self::configure_channel(channel, sample_time); - // Each channel is sampled according to sequence - #[cfg(not(any(adc_g0, adc_u0)))] - match _i { - 0..=3 => { - T::regs().sqr1().modify(|w| { - w.set_sq(_i, channel.channel()); - }); - } - 4..=8 => { - T::regs().sqr2().modify(|w| { - w.set_sq(_i - 4, channel.channel()); - }); - } - 9..=13 => { - T::regs().sqr3().modify(|w| { - w.set_sq(_i - 9, channel.channel()); - }); - } - 14..=15 => { - T::regs().sqr4().modify(|w| { - w.set_sq(_i - 14, channel.channel()); - }); - } - _ => unreachable!(), - } + // Start conversion + T::regs().cr().modify(|reg| { + reg.set_adstart(true); + }); - #[cfg(adc_u0)] - { - channel_mask |= 1 << channel.channel(); - } + while !T::regs().isr().read().eos() { + // spin } - - // On G0 and U0 enabled channels are sampled from 0 to last channel. - // It is possible to add up to 8 sequences if CHSELRMOD = 1. - // However for supporting more than 8 channels alternative CHSELRMOD = 0 approach is used. - #[cfg(adc_u0)] - T::regs().chselr().modify(|reg| { - reg.set_chsel(channel_mask); - }); } + + T::regs().dr().read().0 as u16 + } + + pub(super) fn configure_dma(conversion_mode: ConversionMode) { // Set continuous mode with oneshot dma. // Clear overrun flag before starting transfer. T::regs().isr().modify(|reg| { @@ -535,82 +403,23 @@ impl<'d, T: Instance> Adc<'d, T> { }); #[cfg(not(any(adc_g0, adc_u0)))] - T::regs().cfgr().modify(|reg| { - reg.set_discen(false); - reg.set_cont(true); - reg.set_dmacfg(Dmacfg::ONE_SHOT); - reg.set_dmaen(true); - }); + let regs = T::regs().cfgr(); + #[cfg(any(adc_g0, adc_u0))] - T::regs().cfgr1().modify(|reg| { + let regs = T::regs().cfgr1(); + + regs.modify(|reg| { reg.set_discen(false); reg.set_cont(true); - reg.set_dmacfg(Dmacfg::ONE_SHOT); + reg.set_dmacfg(match conversion_mode { + ConversionMode::Singular => Dmacfg::ONE_SHOT, + ConversionMode::Repeated(_) => Dmacfg::CIRCULAR, + }); reg.set_dmaen(true); }); - - let request = rx_dma.request(); - let transfer = unsafe { - Transfer::new_read( - rx_dma, - request, - T::regs().dr().as_ptr() as *mut u16, - readings, - Default::default(), - ) - }; - - // Start conversion - T::regs().cr().modify(|reg| { - reg.set_adstart(true); - }); - - // Wait for conversion sequence to finish. - transfer.await; - - // Ensure conversions are finished. - Self::cancel_conversions(); - - // Reset configuration. - #[cfg(not(any(adc_g0, adc_u0)))] - T::regs().cfgr().modify(|reg| { - reg.set_cont(false); - }); - #[cfg(any(adc_g0, adc_u0))] - T::regs().cfgr1().modify(|reg| { - reg.set_cont(false); - }); } - /// Configures the ADC to use a DMA ring buffer for continuous data acquisition. - /// - /// The `dma_buf` should be large enough to prevent DMA buffer overrun. - /// The length of the `dma_buf` should be a multiple of the ADC channel count. - /// For example, if 3 channels are measured, its length can be 3 * 40 = 120 measurements. - /// - /// `read` method is used to read out measurements from the DMA ring buffer, and its buffer should be exactly half of the `dma_buf` length. - /// It is critical to call `read` frequently to prevent DMA buffer overrun. - /// - /// [`read`]: #method.read - #[cfg(any(adc_v3, adc_g0, adc_u0))] - pub fn into_ring_buffered<'a>( - &mut self, - dma: Peri<'a, impl RxDma>, - dma_buf: &'a mut [u16], - sequence: impl ExactSizeIterator, SampleTime)>, - ) -> RingBufferedAdc<'a, T> { - assert!(!dma_buf.is_empty() && dma_buf.len() <= 0xFFFF); - assert!(sequence.len() != 0, "Asynchronous read sequence cannot be empty"); - assert!( - sequence.len() <= 16, - "Asynchronous read sequence cannot be more than 16 in length" - ); - // reset conversions and enable the adc - Self::cancel_conversions(); - self.enable(); - - //adc side setup - + pub(super) fn configure_sequence(sequence: impl ExactSizeIterator) { // Set sequence length #[cfg(not(any(adc_g0, adc_u0)))] T::regs().sqr1().modify(|w| { @@ -623,10 +432,10 @@ impl<'d, T: Instance> Adc<'d, T> { T::regs().chselr().write(|chselr| { T::regs().smpr().write(|smpr| { - for (channel, sample_time) in sequence { - chselr.set_chsel(channel.channel.into(), true); + for ((channel, _), sample_time) in sequence { + chselr.set_chsel(channel.into(), true); if let Some(i) = sample_times.iter().position(|&t| t == sample_time) { - smpr.set_smpsel(channel.channel.into(), (i as u8).into()); + smpr.set_smpsel(channel.into(), (i as u8).into()); } else { smpr.set_sample_time(sample_times.len(), sample_time); if let Err(_) = sample_times.push(sample_time) { @@ -646,30 +455,63 @@ impl<'d, T: Instance> Adc<'d, T> { let mut channel_mask = 0; // Configure channels and ranks - for (_i, (mut channel, sample_time)) in sequence.enumerate() { - Self::configure_channel(&mut channel, sample_time); + for (_i, ((channel, _), sample_time)) in sequence.enumerate() { + // RM0492, RM0481, etc. + // "This option bit must be set to 1 when ADCx_INP0 or ADCx_INN1 channel is selected." + #[cfg(any(adc_h5, adc_h7rs))] + if channel == 0 { + T::regs().or().modify(|reg| reg.set_op0(true)); + } + + // Configure channel + cfg_if! { + if #[cfg(adc_u0)] { + // On G0 and U6 all channels use the same sampling time. + T::regs().smpr().modify(|reg| reg.set_smp1(sample_time.into())); + } else if #[cfg(any(adc_h5, adc_h7rs))] { + match channel { + 0..=9 => T::regs().smpr1().modify(|w| w.set_smp(channel as usize % 10, sample_time.into())), + _ => T::regs().smpr2().modify(|w| w.set_smp(channel as usize % 10, sample_time.into())), + } + } else { + let sample_time = sample_time.into(); + T::regs() + .smpr(channel as usize / 10) + .modify(|reg| reg.set_smp(channel as usize % 10, sample_time)); + } + } + + #[cfg(stm32h7)] + { + use crate::pac::adc::vals::Pcsel; + + T::regs().cfgr2().modify(|w| w.set_lshift(0)); + T::regs() + .pcsel() + .write(|w| w.set_pcsel(channel.channel() as _, Pcsel::PRESELECTED)); + } // Each channel is sampled according to sequence #[cfg(not(any(adc_g0, adc_u0)))] match _i { 0..=3 => { T::regs().sqr1().modify(|w| { - w.set_sq(_i, channel.channel()); + w.set_sq(_i, channel); }); } 4..=8 => { T::regs().sqr2().modify(|w| { - w.set_sq(_i - 4, channel.channel()); + w.set_sq(_i - 4, channel); }); } 9..=13 => { T::regs().sqr3().modify(|w| { - w.set_sq(_i - 9, channel.channel()); + w.set_sq(_i - 9, channel); }); } 14..=15 => { T::regs().sqr4().modify(|w| { - w.set_sq(_i - 14, channel.channel()); + w.set_sq(_i - 14, channel); }); } _ => unreachable!(), @@ -677,7 +519,7 @@ impl<'d, T: Instance> Adc<'d, T> { #[cfg(adc_u0)] { - channel_mask |= 1 << channel.channel(); + channel_mask |= 1 << channel; } } @@ -689,151 +531,71 @@ impl<'d, T: Instance> Adc<'d, T> { reg.set_chsel(channel_mask); }); } - // Set continuous mode with Circular dma. - // Clear overrun flag before starting transfer. - T::regs().isr().modify(|reg| { - reg.set_ovr(true); - }); + } + pub fn enable_vrefint(&self) -> VrefInt { #[cfg(not(any(adc_g0, adc_u0)))] - T::regs().cfgr().modify(|reg| { - reg.set_discen(false); - reg.set_cont(true); - reg.set_dmacfg(Dmacfg::CIRCULAR); - reg.set_dmaen(true); + T::common_regs().ccr().modify(|reg| { + reg.set_vrefen(true); }); #[cfg(any(adc_g0, adc_u0))] - T::regs().cfgr1().modify(|reg| { - reg.set_discen(false); - reg.set_cont(true); - reg.set_dmacfg(Dmacfg::CIRCULAR); - reg.set_dmaen(true); + T::regs().ccr().modify(|reg| { + reg.set_vrefen(true); }); - RingBufferedAdc::new(dma, dma_buf) - } - - #[cfg(not(adc_g0))] - fn configure_channel(channel: &mut impl AdcChannel, sample_time: SampleTime) { - // RM0492, RM0481, etc. - // "This option bit must be set to 1 when ADCx_INP0 or ADCx_INN1 channel is selected." - #[cfg(any(adc_h5, adc_h7rs))] - if channel.channel() == 0 { - T::regs().or().modify(|reg| reg.set_op0(true)); - } + // "Table 24. Embedded internal voltage reference" states that it takes a maximum of 12 us + // to stabilize the internal voltage reference. + blocking_delay_us(15); - // Configure channel - Self::set_channel_sample_time(channel.channel(), sample_time); + VrefInt {} } - fn read_channel(&mut self, channel: &mut impl AdcChannel, sample_time: SampleTime) -> u16 { - self.enable(); - #[cfg(not(adc_g0))] - Self::configure_channel(channel, sample_time); - #[cfg(adc_g0)] - T::regs().smpr().write(|reg| { - reg.set_sample_time(0, sample_time); - reg.set_smpsel(channel.channel().into(), Smpsel::SMP1); - }); - // Select channel - #[cfg(not(any(adc_g0, adc_u0)))] - T::regs().sqr1().write(|reg| reg.set_sq(0, channel.channel())); - #[cfg(any(adc_g0, adc_u0))] - T::regs().chselr().write(|reg| { - #[cfg(adc_g0)] - reg.set_chsel(channel.channel().into(), true); - #[cfg(adc_u0)] - reg.set_chsel(1 << channel.channel()); - }); - - // Some models are affected by an erratum: - // If we perform conversions slower than 1 kHz, the first read ADC value can be - // corrupted, so we discard it and measure again. - // - // STM32L471xx: Section 2.7.3 - // STM32G4: Section 2.7.3 - #[cfg(any(rcc_l4, rcc_g4))] - let _ = self.convert(); - let val = self.convert(); - - T::regs().cr().modify(|reg| reg.set_addis(true)); - - // RM0492, RM0481, etc. - // "This option bit must be set to 1 when ADCx_INP0 or ADCx_INN1 channel is selected." - #[cfg(any(adc_h5, adc_h7rs))] - if channel.channel() == 0 { - T::regs().or().modify(|reg| reg.set_op0(false)); + pub fn enable_temperature(&self) -> Temperature { + cfg_if! { + if #[cfg(any(adc_g0, adc_u0))] { + T::regs().ccr().modify(|reg| { + reg.set_tsen(true); + }); + } else if #[cfg(any(adc_h5, adc_h7rs))] { + T::common_regs().ccr().modify(|reg| { + reg.set_tsen(true); + }); + } else { + T::common_regs().ccr().modify(|reg| { + reg.set_ch17sel(true); + }); + } } - val - } - - #[cfg(adc_g0)] - pub fn set_oversampling_shift(&mut self, shift: Ovss) { - T::regs().cfgr2().modify(|reg| reg.set_ovss(shift)); - } - #[cfg(adc_u0)] - pub fn set_oversampling_shift(&mut self, shift: u8) { - T::regs().cfgr2().modify(|reg| reg.set_ovss(shift)); - } - - #[cfg(adc_g0)] - pub fn set_oversampling_ratio(&mut self, ratio: Ovsr) { - T::regs().cfgr2().modify(|reg| reg.set_ovsr(ratio)); - } - #[cfg(adc_u0)] - pub fn set_oversampling_ratio(&mut self, ratio: u8) { - T::regs().cfgr2().modify(|reg| reg.set_ovsr(ratio)); - } - - #[cfg(any(adc_g0, adc_u0))] - pub fn oversampling_enable(&mut self, enable: bool) { - T::regs().cfgr2().modify(|reg| reg.set_ovse(enable)); - } - - #[cfg(adc_v3)] - pub fn enable_regular_oversampling_mode(&mut self, mode: Rovsm, trig_mode: Trovs, enable: bool) { - T::regs().cfgr2().modify(|reg| reg.set_trovs(trig_mode)); - T::regs().cfgr2().modify(|reg| reg.set_rovsm(mode)); - T::regs().cfgr2().modify(|reg| reg.set_rovse(enable)); - } - - #[cfg(adc_v3)] - pub fn set_oversampling_ratio(&mut self, ratio: OversamplingRatio) { - T::regs().cfgr2().modify(|reg| reg.set_ovsr(ratio)); - } - - #[cfg(adc_v3)] - pub fn set_oversampling_shift(&mut self, shift: OversamplingShift) { - T::regs().cfgr2().modify(|reg| reg.set_ovss(shift)); + Temperature {} } - #[cfg(not(adc_g0))] - fn set_channel_sample_time(_ch: u8, sample_time: SampleTime) { + pub fn enable_vbat(&self) -> Vbat { cfg_if! { - if #[cfg(adc_u0)] { - // On G0 and U6 all channels use the same sampling time. - T::regs().smpr().modify(|reg| reg.set_smp1(sample_time.into())); + if #[cfg(any(adc_g0, adc_u0))] { + T::regs().ccr().modify(|reg| { + reg.set_vbaten(true); + }); } else if #[cfg(any(adc_h5, adc_h7rs))] { - match _ch { - 0..=9 => T::regs().smpr1().modify(|w| w.set_smp(_ch as usize % 10, sample_time.into())), - _ => T::regs().smpr2().modify(|w| w.set_smp(_ch as usize % 10, sample_time.into())), - } + T::common_regs().ccr().modify(|reg| { + reg.set_vbaten(true); + }); } else { - let sample_time = sample_time.into(); - T::regs() - .smpr(_ch as usize / 10) - .modify(|reg| reg.set_smp(_ch as usize % 10, sample_time)); + T::common_regs().ccr().modify(|reg| { + reg.set_ch18sel(true); + }); } } + + Vbat {} } - fn cancel_conversions() { - if T::regs().cr().read().adstart() && !T::regs().cr().read().addis() { - T::regs().cr().modify(|reg| { - reg.set_adstp(true); - }); - while T::regs().cr().read().adstart() {} - } + /* + /// Convert a raw sample from the `Temperature` to deg C + pub fn to_degrees_centigrade(sample: u16) -> f32 { + (130.0 - 30.0) / (VtempCal130::get().read() as f32 - VtempCal30::get().read() as f32) + * (sample as f32 - VtempCal30::get().read() as f32) + + 30.0 } + */ } diff --git a/embassy-stm32/src/adc/v4.rs b/embassy-stm32/src/adc/v4.rs index 1d5d3fb92..9be6bcd0b 100644 --- a/embassy-stm32/src/adc/v4.rs +++ b/embassy-stm32/src/adc/v4.rs @@ -4,11 +4,8 @@ use pac::adc::vals::{Adcaldif, Boost}; use pac::adc::vals::{Adstp, Difsel, Dmngt, Exten, Pcsel}; use pac::adccommon::vals::Presc; -use super::{ - Adc, AdcChannel, AnyAdcChannel, Instance, Resolution, RxDma, SampleTime, SealedAdcChannel, Temperature, Vbat, - VrefInt, blocking_delay_us, -}; -use crate::dma::Transfer; +use super::{Adc, Instance, Resolution, SampleTime, Temperature, Vbat, VrefInt, blocking_delay_us}; +use crate::adc::ConversionMode; use crate::time::Hertz; use crate::{Peri, pac, rcc}; @@ -147,7 +144,48 @@ pub enum Averaging { Samples1024, } +/// Adc configuration +#[derive(Default)] +pub struct AdcConfig { + pub resolution: Option, + pub averaging: Option, +} + impl<'d, T: Instance> Adc<'d, T> { + pub fn new_with_config(adc: Peri<'d, T>, config: AdcConfig) -> Self { + let s = Self::new(adc); + + // Set the ADC resolution. + if let Some(resolution) = config.resolution { + T::regs().cfgr().modify(|reg| reg.set_res(resolution.into())); + } + + // Set hardware averaging. + if let Some(averaging) = config.averaging { + let (enable, samples, right_shift) = match averaging { + Averaging::Disabled => (false, 0, 0), + Averaging::Samples2 => (true, 1, 1), + Averaging::Samples4 => (true, 3, 2), + Averaging::Samples8 => (true, 7, 3), + Averaging::Samples16 => (true, 15, 4), + Averaging::Samples32 => (true, 31, 5), + Averaging::Samples64 => (true, 63, 6), + Averaging::Samples128 => (true, 127, 7), + Averaging::Samples256 => (true, 255, 8), + Averaging::Samples512 => (true, 511, 9), + Averaging::Samples1024 => (true, 1023, 10), + }; + + T::regs().cfgr2().modify(|reg| { + reg.set_rovse(enable); + reg.set_ovsr(samples); + reg.set_ovss(right_shift); + }) + } + + s + } + /// Create a new ADC driver. pub fn new(adc: Peri<'d, T>) -> Self { rcc::enable_and_reset::(); @@ -179,37 +217,20 @@ impl<'d, T: Instance> Adc<'d, T> { }; T::regs().cr().modify(|w| w.set_boost(boost)); } - let mut s = Self { adc }; - s.power_up(); - s.configure_differential_inputs(); - - s.calibrate(); - blocking_delay_us(1); - - s.enable(); - s.configure(); - - s - } - fn power_up(&mut self) { T::regs().cr().modify(|reg| { reg.set_deeppwd(false); reg.set_advregen(true); }); blocking_delay_us(10); - } - fn configure_differential_inputs(&mut self) { T::regs().difsel().modify(|w| { for n in 0..20 { w.set_difsel(n, Difsel::SINGLE_ENDED); } }); - } - fn calibrate(&mut self) { T::regs().cr().modify(|w| { #[cfg(not(adc_u5))] w.set_adcaldif(Adcaldif::SINGLE_ENDED); @@ -219,80 +240,50 @@ impl<'d, T: Instance> Adc<'d, T> { T::regs().cr().modify(|w| w.set_adcal(true)); while T::regs().cr().read().adcal() {} - } - fn enable(&mut self) { - T::regs().isr().write(|w| w.set_adrdy(true)); - T::regs().cr().modify(|w| w.set_aden(true)); - while !T::regs().isr().read().adrdy() {} - T::regs().isr().write(|w| w.set_adrdy(true)); - } + blocking_delay_us(1); + + Self::enable(); - fn configure(&mut self) { // single conversion mode, software trigger T::regs().cfgr().modify(|w| { w.set_cont(false); w.set_exten(Exten::DISABLED); }); - } - /// Enable reading the voltage reference internal channel. - pub fn enable_vrefint(&self) -> VrefInt { - T::common_regs().ccr().modify(|reg| { - reg.set_vrefen(true); - }); - - VrefInt {} + Self { adc } } - /// Enable reading the temperature internal channel. - pub fn enable_temperature(&self) -> Temperature { - T::common_regs().ccr().modify(|reg| { - reg.set_vsenseen(true); - }); - - Temperature {} + pub(super) fn enable() { + T::regs().isr().write(|w| w.set_adrdy(true)); + T::regs().cr().modify(|w| w.set_aden(true)); + while !T::regs().isr().read().adrdy() {} + T::regs().isr().write(|w| w.set_adrdy(true)); } - /// Enable reading the vbat internal channel. - pub fn enable_vbat(&self) -> Vbat { - T::common_regs().ccr().modify(|reg| { - reg.set_vbaten(true); + pub(super) fn start() { + // Start conversion + T::regs().cr().modify(|reg| { + reg.set_adstart(true); }); - - Vbat {} } - /// Set the ADC resolution. - pub fn set_resolution(&mut self, resolution: Resolution) { - T::regs().cfgr().modify(|reg| reg.set_res(resolution.into())); - } + pub(super) fn stop() { + if T::regs().cr().read().adstart() && !T::regs().cr().read().addis() { + T::regs().cr().modify(|reg| { + reg.set_adstp(Adstp::STOP); + }); + while T::regs().cr().read().adstart() {} + } - /// Set hardware averaging. - pub fn set_averaging(&mut self, averaging: Averaging) { - let (enable, samples, right_shift) = match averaging { - Averaging::Disabled => (false, 0, 0), - Averaging::Samples2 => (true, 1, 1), - Averaging::Samples4 => (true, 3, 2), - Averaging::Samples8 => (true, 7, 3), - Averaging::Samples16 => (true, 15, 4), - Averaging::Samples32 => (true, 31, 5), - Averaging::Samples64 => (true, 63, 6), - Averaging::Samples128 => (true, 127, 7), - Averaging::Samples256 => (true, 255, 8), - Averaging::Samples512 => (true, 511, 9), - Averaging::Samples1024 => (true, 1023, 10), - }; - - T::regs().cfgr2().modify(|reg| { - reg.set_rovse(enable); - reg.set_ovsr(samples); - reg.set_ovss(right_shift); - }) + // Reset configuration. + T::regs().cfgr().modify(|reg| { + reg.set_cont(false); + reg.set_dmngt(Dmngt::from_bits(0)); + }); } - /// Perform a single conversion. - fn convert(&mut self) -> u16 { + pub(super) fn convert() -> u16 { T::regs().isr().modify(|reg| { reg.set_eos(true); reg.set_eoc(true); @@ -310,170 +301,96 @@ impl<'d, T: Instance> Adc<'d, T> { T::regs().dr().read().0 as u16 } - /// Read an ADC channel. - pub fn blocking_read(&mut self, channel: &mut impl AdcChannel, sample_time: SampleTime) -> u16 { - self.read_channel(channel, sample_time) + pub(super) fn configure_dma(conversion_mode: ConversionMode) { + match conversion_mode { + ConversionMode::Singular => { + T::regs().isr().modify(|reg| { + reg.set_ovr(true); + }); + T::regs().cfgr().modify(|reg| { + reg.set_cont(true); + reg.set_dmngt(Dmngt::DMA_ONE_SHOT); + }); + } + _ => unreachable!(), + } } - /// Read one or multiple ADC channels using DMA. - /// - /// `sequence` iterator and `readings` must have the same length. - /// - /// Example - /// ```rust,ignore - /// use embassy_stm32::adc::{Adc, AdcChannel} - /// - /// let mut adc = Adc::new(p.ADC1); - /// let mut adc_pin0 = p.PA0.into(); - /// let mut adc_pin2 = p.PA2.into(); - /// let mut measurements = [0u16; 2]; - /// - /// adc.read( - /// p.DMA2_CH0.reborrow(), - /// [ - /// (&mut *adc_pin0, SampleTime::CYCLES112), - /// (&mut *adc_pin2, SampleTime::CYCLES112), - /// ] - /// .into_iter(), - /// &mut measurements, - /// ) - /// .await; - /// defmt::info!("measurements: {}", measurements); - /// ``` - pub async fn read( - &mut self, - rx_dma: Peri<'_, impl RxDma>, - sequence: impl ExactSizeIterator, SampleTime)>, - readings: &mut [u16], - ) { - assert!(sequence.len() != 0, "Asynchronous read sequence cannot be empty"); - assert!( - sequence.len() == readings.len(), - "Sequence length must be equal to readings length" - ); - assert!( - sequence.len() <= 16, - "Asynchronous read sequence cannot be more than 16 in length" - ); - - // Ensure no conversions are ongoing - Self::cancel_conversions(); - + pub(super) fn configure_sequence(sequence: impl ExactSizeIterator) { // Set sequence length T::regs().sqr1().modify(|w| { w.set_l(sequence.len() as u8 - 1); }); // Configure channels and ranks - for (i, (channel, sample_time)) in sequence.enumerate() { - Self::configure_channel(channel, sample_time); + for (i, ((channel, _), sample_time)) in sequence.enumerate() { + let sample_time = sample_time.into(); + if channel <= 9 { + T::regs().smpr(0).modify(|reg| reg.set_smp(channel as _, sample_time)); + } else { + T::regs() + .smpr(1) + .modify(|reg| reg.set_smp((channel - 10) as _, sample_time)); + } + + #[cfg(any(stm32h7, stm32u5))] + { + T::regs().cfgr2().modify(|w| w.set_lshift(0)); + T::regs() + .pcsel() + .modify(|w| w.set_pcsel(channel as _, Pcsel::PRESELECTED)); + } + match i { 0..=3 => { T::regs().sqr1().modify(|w| { - w.set_sq(i, channel.channel()); + w.set_sq(i, channel); }); } 4..=8 => { T::regs().sqr2().modify(|w| { - w.set_sq(i - 4, channel.channel()); + w.set_sq(i - 4, channel); }); } 9..=13 => { T::regs().sqr3().modify(|w| { - w.set_sq(i - 9, channel.channel()); + w.set_sq(i - 9, channel); }); } 14..=15 => { T::regs().sqr4().modify(|w| { - w.set_sq(i - 14, channel.channel()); + w.set_sq(i - 14, channel); }); } _ => unreachable!(), } } - - // Set continuous mode with oneshot dma. - // Clear overrun flag before starting transfer. - - T::regs().isr().modify(|reg| { - reg.set_ovr(true); - }); - T::regs().cfgr().modify(|reg| { - reg.set_cont(true); - reg.set_dmngt(Dmngt::DMA_ONE_SHOT); - }); - - let request = rx_dma.request(); - let transfer = unsafe { - Transfer::new_read( - rx_dma, - request, - T::regs().dr().as_ptr() as *mut u16, - readings, - Default::default(), - ) - }; - - // Start conversion - T::regs().cr().modify(|reg| { - reg.set_adstart(true); - }); - - // Wait for conversion sequence to finish. - transfer.await; - - // Ensure conversions are finished. - Self::cancel_conversions(); - - // Reset configuration. - T::regs().cfgr().modify(|reg| { - reg.set_cont(false); - reg.set_dmngt(Dmngt::from_bits(0)); - }); } - fn configure_channel(channel: &mut impl AdcChannel, sample_time: SampleTime) { - channel.setup(); - - let channel = channel.channel(); - - Self::set_channel_sample_time(channel, sample_time); + /// Enable reading the voltage reference internal channel. + pub fn enable_vrefint(&self) -> VrefInt { + T::common_regs().ccr().modify(|reg| { + reg.set_vrefen(true); + }); - #[cfg(any(stm32h7, stm32u5))] - { - T::regs().cfgr2().modify(|w| w.set_lshift(0)); - T::regs() - .pcsel() - .modify(|w| w.set_pcsel(channel as _, Pcsel::PRESELECTED)); - } + VrefInt {} } - fn read_channel(&mut self, channel: &mut impl AdcChannel, sample_time: SampleTime) -> u16 { - Self::configure_channel(channel, sample_time); - - T::regs().sqr1().modify(|reg| { - reg.set_sq(0, channel.channel()); - reg.set_l(0); + /// Enable reading the temperature internal channel. + pub fn enable_temperature(&self) -> Temperature { + T::common_regs().ccr().modify(|reg| { + reg.set_vsenseen(true); }); - self.convert() + Temperature {} } - fn set_channel_sample_time(ch: u8, sample_time: SampleTime) { - let sample_time = sample_time.into(); - if ch <= 9 { - T::regs().smpr(0).modify(|reg| reg.set_smp(ch as _, sample_time)); - } else { - T::regs().smpr(1).modify(|reg| reg.set_smp((ch - 10) as _, sample_time)); - } - } + /// Enable reading the vbat internal channel. + pub fn enable_vbat(&self) -> Vbat { + T::common_regs().ccr().modify(|reg| { + reg.set_vbaten(true); + }); - fn cancel_conversions() { - if T::regs().cr().read().adstart() && !T::regs().cr().read().addis() { - T::regs().cr().modify(|reg| { - reg.set_adstp(Adstp::STOP); - }); - while T::regs().cr().read().adstart() {} - } + Vbat {} } } diff --git a/examples/stm32f4/src/bin/adc.rs b/examples/stm32f4/src/bin/adc.rs index 5628cb827..694e85657 100644 --- a/examples/stm32f4/src/bin/adc.rs +++ b/examples/stm32f4/src/bin/adc.rs @@ -14,7 +14,7 @@ async fn main(_spawner: Spawner) { info!("Hello World!"); let mut delay = Delay; - let mut adc = Adc::new(p.ADC1); + let mut adc = Adc::new_with_config(p.ADC1, Default::default()); let mut pin = p.PC1; let mut vrefint = adc.enable_vrefint(); diff --git a/examples/stm32f4/src/bin/adc_dma.rs b/examples/stm32f4/src/bin/adc_dma.rs index 01b881c79..d61b1b2eb 100644 --- a/examples/stm32f4/src/bin/adc_dma.rs +++ b/examples/stm32f4/src/bin/adc_dma.rs @@ -4,7 +4,7 @@ use cortex_m::singleton; use defmt::*; use embassy_executor::Spawner; use embassy_stm32::Peripherals; -use embassy_stm32::adc::{Adc, AdcChannel, RingBufferedAdc, SampleTime}; +use embassy_stm32::adc::{Adc, AdcChannel, RegularConversionMode, RingBufferedAdc, SampleTime}; use embassy_time::Instant; use {defmt_rtt as _, panic_probe as _}; @@ -20,8 +20,8 @@ async fn adc_task(p: Peripherals) { let adc_data: &mut [u16; ADC_BUF_SIZE] = singleton!(ADCDAT : [u16; ADC_BUF_SIZE] = [0u16; ADC_BUF_SIZE]).unwrap(); let adc_data2: &mut [u16; ADC_BUF_SIZE] = singleton!(ADCDAT2 : [u16; ADC_BUF_SIZE] = [0u16; ADC_BUF_SIZE]).unwrap(); - let adc = Adc::new(p.ADC1); - let adc2 = Adc::new(p.ADC2); + let adc = Adc::new_with_config(p.ADC1, Default::default()); + let adc2 = Adc::new_with_config(p.ADC2, Default::default()); let mut adc: RingBufferedAdc = adc.into_ring_buffered( p.DMA2_CH0, @@ -31,6 +31,7 @@ async fn adc_task(p: Peripherals) { (p.PA2.degrade_adc(), SampleTime::CYCLES112), ] .into_iter(), + RegularConversionMode::Continuous, ); let mut adc2: RingBufferedAdc = adc2.into_ring_buffered( p.DMA2_CH2, @@ -40,6 +41,7 @@ async fn adc_task(p: Peripherals) { (p.PA3.degrade_adc(), SampleTime::CYCLES112), ] .into_iter(), + RegularConversionMode::Continuous, ); // Note that overrun is a big consideration in this implementation. Whatever task is running the adc.read() calls absolutely must circle back around diff --git a/examples/stm32g0/src/bin/adc_oversampling.rs b/examples/stm32g0/src/bin/adc_oversampling.rs index f6979889d..aa8b1771b 100644 --- a/examples/stm32g0/src/bin/adc_oversampling.rs +++ b/examples/stm32g0/src/bin/adc_oversampling.rs @@ -7,7 +7,7 @@ use defmt::*; use embassy_executor::Spawner; -use embassy_stm32::adc::{Adc, Clock, Ovsr, Ovss, Presc, SampleTime}; +use embassy_stm32::adc::{Adc, AdcConfig, Clock, Ovsr, Ovss, Presc, SampleTime}; use embassy_time::Timer; use {defmt_rtt as _, panic_probe as _}; @@ -16,12 +16,14 @@ async fn main(_spawner: Spawner) { let p = embassy_stm32::init(Default::default()); info!("Adc oversample test"); - let mut adc = Adc::new_with_clock(p.ADC1, Clock::Async { div: Presc::DIV1 }); - let mut pin = p.PA1; + let mut config = AdcConfig::default(); + config.clock = Some(Clock::Async { div: Presc::DIV1 }); + config.oversampling_ratio = Some(Ovsr::MUL16); + config.oversampling_shift = Some(Ovss::NO_SHIFT); + config.oversampling_enable = Some(true); - adc.set_oversampling_ratio(Ovsr::MUL16); - adc.set_oversampling_shift(Ovss::NO_SHIFT); - adc.oversampling_enable(true); + let mut adc = Adc::new_with_config(p.ADC1, config); + let mut pin = p.PA1; loop { let v = adc.blocking_read(&mut pin, SampleTime::CYCLES1_5); diff --git a/examples/stm32g4/src/bin/adc.rs b/examples/stm32g4/src/bin/adc.rs index 94315141c..2149e0748 100644 --- a/examples/stm32g4/src/bin/adc.rs +++ b/examples/stm32g4/src/bin/adc.rs @@ -28,9 +28,9 @@ async fn main(_spawner: Spawner) { let mut p = embassy_stm32::init(config); info!("Hello World!"); - let mut adc = Adc::new(p.ADC2); + let mut adc = Adc::new(p.ADC2, Default::default()); - let mut adc_temp = Adc::new(p.ADC1); + let mut adc_temp = Adc::new(p.ADC1, Default::default()); let mut temperature = adc_temp.enable_temperature(); loop { diff --git a/examples/stm32g4/src/bin/adc_differential.rs b/examples/stm32g4/src/bin/adc_differential.rs index 2773723e9..6dedf88d6 100644 --- a/examples/stm32g4/src/bin/adc_differential.rs +++ b/examples/stm32g4/src/bin/adc_differential.rs @@ -32,7 +32,7 @@ async fn main(_spawner: Spawner) { } let p = embassy_stm32::init(config); - let mut adc = Adc::new(p.ADC1); + let mut adc = Adc::new(p.ADC1, Default::default()); let mut differential_channel = (p.PA0, p.PA1); // can also use diff --git a/examples/stm32g4/src/bin/adc_dma.rs b/examples/stm32g4/src/bin/adc_dma.rs index ef8b0c3c2..478b6b2ca 100644 --- a/examples/stm32g4/src/bin/adc_dma.rs +++ b/examples/stm32g4/src/bin/adc_dma.rs @@ -33,7 +33,7 @@ async fn main(_spawner: Spawner) { info!("Hello World!"); - let mut adc = Adc::new(p.ADC1); + let mut adc = Adc::new(p.ADC1, Default::default()); let mut dma = p.DMA1_CH1; let mut vrefint_channel = adc.enable_vrefint().degrade_adc(); diff --git a/examples/stm32g4/src/bin/adc_injected_and_regular.rs b/examples/stm32g4/src/bin/adc_injected_and_regular.rs index 3ae2ff064..1e97fa925 100644 --- a/examples/stm32g4/src/bin/adc_injected_and_regular.rs +++ b/examples/stm32g4/src/bin/adc_injected_and_regular.rs @@ -77,7 +77,7 @@ async fn main(_spawner: embassy_executor::Spawner) { pwm.set_mms2(Mms2::UPDATE); // Configure regular conversions with DMA - let adc1 = Adc::new(p.ADC1); + let adc1 = Adc::new(p.ADC1, Default::default()); let vrefint_channel = adc1.enable_vrefint().degrade_adc(); let pa0 = p.PC1.degrade_adc(); diff --git a/examples/stm32g4/src/bin/adc_oversampling.rs b/examples/stm32g4/src/bin/adc_oversampling.rs index cb99ab2a7..87ffea4be 100644 --- a/examples/stm32g4/src/bin/adc_oversampling.rs +++ b/examples/stm32g4/src/bin/adc_oversampling.rs @@ -9,7 +9,7 @@ use defmt::*; use embassy_executor::Spawner; use embassy_stm32::Config; use embassy_stm32::adc::vals::{Rovsm, Trovs}; -use embassy_stm32::adc::{Adc, SampleTime}; +use embassy_stm32::adc::{Adc, AdcConfig, SampleTime}; use embassy_time::Timer; use {defmt_rtt as _, panic_probe as _}; @@ -32,7 +32,8 @@ async fn main(_spawner: Spawner) { } let mut p = embassy_stm32::init(config); - let mut adc = Adc::new(p.ADC1); + let mut config = AdcConfig::default(); + // From https://www.st.com/resource/en/reference_manual/rm0440-stm32g4-series-advanced-armbased-32bit-mcus-stmicroelectronics.pdf // page652 Oversampler // Table 172. Maximum output results vs N and M. Grayed values indicates truncation @@ -44,9 +45,11 @@ async fn main(_spawner: Spawner) { // 0x05 oversampling ratio X64 // 0x06 oversampling ratio X128 // 0x07 oversampling ratio X256 - adc.set_oversampling_ratio(0x03); // ratio X3 - adc.set_oversampling_shift(0b0000); // no shift - adc.enable_regular_oversampling_mode(Rovsm::RESUMED, Trovs::AUTOMATIC, true); + config.oversampling_ratio = Some(0x03); // ratio X3 + config.oversampling_shift = Some(0b0000); // no shift + config.oversampling_mode = Some((Rovsm::RESUMED, Trovs::AUTOMATIC, true)); + + let mut adc = Adc::new(p.ADC1, config); loop { let measured = adc.blocking_read(&mut p.PA0, SampleTime::CYCLES6_5); diff --git a/examples/stm32l4/src/bin/adc.rs b/examples/stm32l4/src/bin/adc.rs index 835bf5411..42766a5e3 100644 --- a/examples/stm32l4/src/bin/adc.rs +++ b/examples/stm32l4/src/bin/adc.rs @@ -3,7 +3,7 @@ use defmt::*; use embassy_stm32::Config; -use embassy_stm32::adc::{Adc, Resolution, SampleTime}; +use embassy_stm32::adc::{Adc, AdcConfig, Resolution, SampleTime}; use {defmt_rtt as _, panic_probe as _}; #[cortex_m_rt::entry] @@ -17,9 +17,12 @@ fn main() -> ! { } let p = embassy_stm32::init(config); - let mut adc = Adc::new(p.ADC1); + let mut config = AdcConfig::default(); + config.resolution = Some(Resolution::BITS8); + + let mut adc = Adc::new_with_config(p.ADC1, config); //adc.enable_vref(); - adc.set_resolution(Resolution::BITS8); + let mut channel = p.PC0; loop { diff --git a/examples/stm32l4/src/bin/adc_dma.rs b/examples/stm32l4/src/bin/adc_dma.rs index ab1e9d2e9..550da95a4 100644 --- a/examples/stm32l4/src/bin/adc_dma.rs +++ b/examples/stm32l4/src/bin/adc_dma.rs @@ -4,7 +4,7 @@ use defmt::*; use embassy_executor::Spawner; use embassy_stm32::Config; -use embassy_stm32::adc::{Adc, AdcChannel, SampleTime}; +use embassy_stm32::adc::{Adc, AdcChannel, RegularConversionMode, SampleTime}; use {defmt_rtt as _, panic_probe as _}; const DMA_BUF_LEN: usize = 512; @@ -20,7 +20,7 @@ async fn main(_spawner: Spawner) { } let p = embassy_stm32::init(config); - let mut adc = Adc::new(p.ADC1); + let adc = Adc::new(p.ADC1); let adc_pin0 = p.PA0.degrade_adc(); let adc_pin1 = p.PA1.degrade_adc(); let mut adc_dma_buf = [0u16; DMA_BUF_LEN]; @@ -29,6 +29,7 @@ async fn main(_spawner: Spawner) { p.DMA1_CH1, &mut adc_dma_buf, [(adc_pin0, SampleTime::CYCLES640_5), (adc_pin1, SampleTime::CYCLES640_5)].into_iter(), + RegularConversionMode::Continuous, ); info!("starting measurement loop"); diff --git a/examples/stm32u0/src/bin/adc.rs b/examples/stm32u0/src/bin/adc.rs index 4fbc6f17f..53bd37303 100644 --- a/examples/stm32u0/src/bin/adc.rs +++ b/examples/stm32u0/src/bin/adc.rs @@ -3,7 +3,7 @@ use defmt::*; use embassy_stm32::Config; -use embassy_stm32::adc::{Adc, Resolution, SampleTime}; +use embassy_stm32::adc::{Adc, AdcConfig, Resolution, SampleTime}; use embassy_time::Duration; use {defmt_rtt as _, panic_probe as _}; @@ -18,8 +18,9 @@ fn main() -> ! { } let p = embassy_stm32::init(config); - let mut adc = Adc::new(p.ADC1); - adc.set_resolution(Resolution::BITS8); + let mut config = AdcConfig::default(); + config.resolution = Some(Resolution::BITS8); + let mut adc = Adc::new_with_config(p.ADC1, config); let mut channel = p.PC0; loop { diff --git a/examples/stm32u5/src/bin/adc.rs b/examples/stm32u5/src/bin/adc.rs index 99944f7c7..6b9a91d6e 100644 --- a/examples/stm32u5/src/bin/adc.rs +++ b/examples/stm32u5/src/bin/adc.rs @@ -2,7 +2,7 @@ #![no_main] use defmt::*; -use embassy_stm32::adc::{self, AdcChannel, SampleTime, adc4}; +use embassy_stm32::adc::{self, AdcChannel, AdcConfig, SampleTime, adc4}; use {defmt_rtt as _, panic_probe as _}; #[embassy_executor::main] @@ -12,19 +12,21 @@ async fn main(_spawner: embassy_executor::Spawner) { let mut p = embassy_stm32::init(config); // **** ADC1 init **** - let mut adc1 = adc::Adc::new(p.ADC1); + let mut config = AdcConfig::default(); + config.averaging = Some(adc::Averaging::Samples1024); + config.resolution = Some(adc::Resolution::BITS14); + let mut adc1 = adc::Adc::new_with_config(p.ADC1, config); let mut adc1_pin1 = p.PA3; // A0 on nucleo u5a5 let mut adc1_pin2 = p.PA2; // A1 - adc1.set_resolution(adc::Resolution::BITS14); - adc1.set_averaging(adc::Averaging::Samples1024); let max1 = adc::resolution_to_max_count(adc::Resolution::BITS14); // **** ADC2 init **** - let mut adc2 = adc::Adc::new(p.ADC2); + let mut config = AdcConfig::default(); + config.averaging = Some(adc::Averaging::Samples1024); + config.resolution = Some(adc::Resolution::BITS14); + let mut adc2 = adc::Adc::new_with_config(p.ADC2, config); let mut adc2_pin1 = p.PC3; // A2 let mut adc2_pin2 = p.PB0; // A3 - adc2.set_resolution(adc::Resolution::BITS14); - adc2.set_averaging(adc::Averaging::Samples1024); let max2 = adc::resolution_to_max_count(adc::Resolution::BITS14); // **** ADC4 init **** -- cgit From 4efd9fccf4259779d96c5d1a4829a90bda1a5def Mon Sep 17 00:00:00 2001 From: Wouter Geraedts Date: Wed, 12 Nov 2025 20:48:03 +0100 Subject: Fix flash erase on dualbank STM32Gxxx --- embassy-stm32/CHANGELOG.md | 1 + embassy-stm32/src/flash/g.rs | 5 ++--- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/embassy-stm32/CHANGELOG.md b/embassy-stm32/CHANGELOG.md index 3431848d3..b418faee6 100644 --- a/embassy-stm32/CHANGELOG.md +++ b/embassy-stm32/CHANGELOG.md @@ -7,6 +7,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## Unreleased - ReleaseDate +- fix: flash erase on dual-bank STM32Gxxx - feat: Add support for STM32N657X0 - feat: timer: Add 32-bit timer support to SimplePwm waveform_up method following waveform pattern ([#4717](https://github.com/embassy-rs/embassy/pull/4717)) - feat: Add support for injected ADC measurements for g4 ([#4840](https://github.com/embassy-rs/embassy/pull/4840)) diff --git a/embassy-stm32/src/flash/g.rs b/embassy-stm32/src/flash/g.rs index d026541a4..d7ba2f571 100644 --- a/embassy-stm32/src/flash/g.rs +++ b/embassy-stm32/src/flash/g.rs @@ -44,7 +44,6 @@ pub(crate) unsafe fn blocking_write(start_address: u32, buf: &[u8; WRITE_SIZE]) } pub(crate) unsafe fn blocking_erase_sector(sector: &FlashSector) -> Result<(), Error> { - let idx = (sector.start - super::FLASH_BASE as u32) / super::BANK1_REGION.erase_size as u32; wait_busy(); clear_all_err(); @@ -54,9 +53,9 @@ pub(crate) unsafe fn blocking_erase_sector(sector: &FlashSector) -> Result<(), E #[cfg(any(flash_g0x0, flash_g0x1, flash_g4c3))] w.set_bker(sector.bank == crate::flash::FlashBank::Bank2); #[cfg(flash_g0x0)] - w.set_pnb(idx as u16); + w.set_pnb(sector.index_in_bank as u16); #[cfg(not(flash_g0x0))] - w.set_pnb(idx as u8); + w.set_pnb(sector.index_in_bank as u8); w.set_strt(true); }); }); -- cgit From 973fdb6b222a24e881c722b33767aab76ab92896 Mon Sep 17 00:00:00 2001 From: HybridChild Date: Wed, 12 Nov 2025 21:02:10 +0100 Subject: stm32: Add i2c v2 transaction test --- examples/stm32f0/Cargo.toml | 1 - examples/stm32f0/src/bin/i2c_transaction_test.rs | 219 ---------------------- tests/stm32/Cargo.toml | 7 + tests/stm32/src/bin/i2c_v2.rs | 220 +++++++++++++++++++++++ tests/stm32/src/common.rs | 25 +++ 5 files changed, 252 insertions(+), 220 deletions(-) delete mode 100644 examples/stm32f0/src/bin/i2c_transaction_test.rs create mode 100644 tests/stm32/src/bin/i2c_v2.rs diff --git a/examples/stm32f0/Cargo.toml b/examples/stm32f0/Cargo.toml index 177dd0ac2..a78873d21 100644 --- a/examples/stm32f0/Cargo.toml +++ b/examples/stm32f0/Cargo.toml @@ -16,7 +16,6 @@ panic-probe = { version = "1.0.0", features = ["print-defmt"] } 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", "executor-interrupt", "defmt"] } embassy-time = { version = "0.5.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } -embedded-hal-1 = { package = "embedded-hal", version = "1.0" } static_cell = "2" portable-atomic = { version = "1.5", features = ["unsafe-assume-single-core"] } diff --git a/examples/stm32f0/src/bin/i2c_transaction_test.rs b/examples/stm32f0/src/bin/i2c_transaction_test.rs deleted file mode 100644 index 0ecc3e8b1..000000000 --- a/examples/stm32f0/src/bin/i2c_transaction_test.rs +++ /dev/null @@ -1,219 +0,0 @@ -#![no_std] -#![no_main] - -use defmt::*; -use embassy_executor::Spawner; -use embassy_stm32::i2c::{Config, I2c, Master}; -use embassy_stm32::mode::Blocking; -use embassy_stm32::time::Hertz; -use embassy_stm32::{bind_interrupts, i2c, peripherals}; -use embassy_time::Timer; -use embedded_hal_1::i2c::Operation; -use {defmt_rtt as _, panic_probe as _}; - -bind_interrupts!(struct Irqs { - I2C1 => i2c::EventInterruptHandler, i2c::ErrorInterruptHandler; -}); - -#[embassy_executor::main] -async fn main(_spawner: Spawner) { - // For STM32F072RB on NUCLEO board - let p = embassy_stm32::init(Default::default()); - - info!("I2C Transaction Test Starting..."); - - // Initialize I2C1: PB6=SCL, PB7=SDA - let mut config = Config::default(); - config.frequency = Hertz(100_000); - let mut i2c = I2c::new_blocking( - p.I2C1, - p.PB8, // SCL - p.PB9, // SDA - config, - ); - - let slave_addr = 0x50u8; - - // Wait for devices to initialize - Timer::after_millis(100).await; - - info!("=== Test 1: Consecutive Writes (Should Merge) ==="); - test_consecutive_writes(&mut i2c, slave_addr); - Timer::after_millis(500).await; - - info!("=== Test 2: Consecutive Reads (Should Merge) ==="); - test_consecutive_reads(&mut i2c, slave_addr); - Timer::after_millis(500).await; - - info!("=== Test 3: Write then Read (RESTART) ==="); - test_write_then_read(&mut i2c, slave_addr); - Timer::after_millis(500).await; - - info!("=== Test 4: Read then Write (RESTART) ==="); - test_read_then_write(&mut i2c, slave_addr); - Timer::after_millis(500).await; - - info!("=== Test 5: Complex Mixed Sequence ==="); - test_mixed_sequence(&mut i2c, slave_addr); - Timer::after_millis(500).await; - - info!("=== Test 6: Single Operations ==="); - test_single_operations(&mut i2c, slave_addr); - - info!("All tests complete!"); - - loop { - Timer::after_secs(1).await; - } -} - -fn test_consecutive_writes(i2c: &mut I2c<'static, Blocking, Master>, addr: u8) { - // Expected on bus: START, ADDR+W, data1, data2, data3, STOP - // NO intermediate RESTART/STOP between writes - let data1 = [0x10, 0x11, 0x12]; - let data2 = [0x20, 0x21]; - let data3 = [0x30, 0x31, 0x32, 0x33]; - - let mut ops = [ - Operation::Write(&data1), - Operation::Write(&data2), - Operation::Write(&data3), - ]; - - match i2c.blocking_transaction(addr, &mut ops) { - Ok(_) => info!("✓ Consecutive writes succeeded"), - Err(e) => warn!("✗ Consecutive writes failed: {:?}", e), - } - - info!("Expected: START, ADDR+W, [9 bytes], STOP"); - info!("Check Analog Discovery: No RESTART between writes"); -} - -fn test_consecutive_reads(i2c: &mut I2c<'static, Blocking, Master>, addr: u8) { - // Expected on bus: START, ADDR+R, data1, data2, data3, NACK, STOP - // NO intermediate RESTART/STOP between reads - let mut buf1 = [0u8; 4]; - let mut buf2 = [0u8; 3]; - let mut buf3 = [0u8; 2]; - - let mut ops = [ - Operation::Read(&mut buf1), - Operation::Read(&mut buf2), - Operation::Read(&mut buf3), - ]; - - match i2c.blocking_transaction(addr, &mut ops) { - Ok(_) => { - info!("✓ Consecutive reads succeeded"); - info!(" buf1: {:02x}", buf1); - info!(" buf2: {:02x}", buf2); - info!(" buf3: {:02x}", buf3); - } - Err(e) => warn!("✗ Consecutive reads failed: {:?}", e), - } - - info!("Expected: START, ADDR+R, [9 bytes], NACK on last, STOP"); - info!("Check Analog Discovery: No RESTART between reads"); -} - -fn test_write_then_read(i2c: &mut I2c<'static, Blocking, Master>, addr: u8) { - // Expected: START, ADDR+W, data, RESTART, ADDR+R, data, NACK, STOP - let write_data = [0xAA, 0xBB]; - let mut read_buf = [0u8; 4]; - - let mut ops = [ - Operation::Write(&write_data), - Operation::Read(&mut read_buf), - ]; - - match i2c.blocking_transaction(addr, &mut ops) { - Ok(_) => { - info!("✓ Write-then-read succeeded"); - info!(" Written: {:02x}", write_data); - info!(" Read: {:02x}", read_buf); - } - Err(e) => warn!("✗ Write-then-read failed: {:?}", e), - } - - info!("Expected: START, ADDR+W, [2 bytes], RESTART, ADDR+R, [4 bytes], NACK, STOP"); - info!("Check Analog Discovery: RESTART between write and read"); -} - -fn test_read_then_write(i2c: &mut I2c<'static, Blocking, Master>, addr: u8) { - // Expected: START, ADDR+R, data, NACK, RESTART, ADDR+W, data, STOP - let mut read_buf = [0u8; 3]; - let write_data = [0xCC, 0xDD, 0xEE]; - - let mut ops = [ - Operation::Read(&mut read_buf), - Operation::Write(&write_data), - ]; - - match i2c.blocking_transaction(addr, &mut ops) { - Ok(_) => { - info!("✓ Read-then-write succeeded"); - info!(" Read: {:02x}", read_buf); - info!(" Written: {:02x}", write_data); - } - Err(e) => warn!("✗ Read-then-write failed: {:?}", e), - } - - info!("Expected: START, ADDR+R, [3 bytes], NACK, RESTART, ADDR+W, [3 bytes], STOP"); - info!("Check Analog Discovery: RESTART between read and write"); -} - -fn test_mixed_sequence(i2c: &mut I2c<'static, Blocking, Master>, addr: u8) { - // Complex: W, W, R, R, W, R - // Groups: [W,W] RESTART [R,R] RESTART [W] RESTART [R] - let w1 = [0x01, 0x02]; - let w2 = [0x03, 0x04]; - let mut r1 = [0u8; 2]; - let mut r2 = [0u8; 2]; - let w3 = [0x05]; - let mut r3 = [0u8; 1]; - - let mut ops = [ - Operation::Write(&w1), - Operation::Write(&w2), - Operation::Read(&mut r1), - Operation::Read(&mut r2), - Operation::Write(&w3), - Operation::Read(&mut r3), - ]; - - match i2c.blocking_transaction(addr, &mut ops) { - Ok(_) => { - info!("✓ Mixed sequence succeeded"); - info!(" r1: {:02x}", r1); - info!(" r2: {:02x}", r2); - info!(" r3: {:02x}", r3); - } - Err(e) => warn!("✗ Mixed sequence failed: {:?}", e), - } - - info!("Expected sequence:"); - info!(" START, ADDR+W, [4 bytes merged], RESTART,"); - info!(" ADDR+R, [4 bytes merged], NACK, RESTART,"); - info!(" ADDR+W, [1 byte], RESTART,"); - info!(" ADDR+R, [1 byte], NACK, STOP"); -} - -fn test_single_operations(i2c: &mut I2c<'static, Blocking, Master>, addr: u8) { - // Test single write - let write_data = [0xFF]; - let mut ops = [Operation::Write(&write_data)]; - - match i2c.blocking_transaction(addr, &mut ops) { - Ok(_) => info!("✓ Single write succeeded"), - Err(e) => warn!("✗ Single write failed: {:?}", e), - } - - // Test single read - let mut read_buf = [0u8; 1]; - let mut ops = [Operation::Read(&mut read_buf)]; - - match i2c.blocking_transaction(addr, &mut ops) { - Ok(_) => info!("✓ Single read succeeded, data: 0x{:02x}", read_buf[0]), - Err(e) => warn!("✗ Single read failed: {:?}", e), - } -} diff --git a/tests/stm32/Cargo.toml b/tests/stm32/Cargo.toml index b92b47be2..fa757e276 100644 --- a/tests/stm32/Cargo.toml +++ b/tests/stm32/Cargo.toml @@ -35,6 +35,7 @@ stm32wb55rg = ["embassy-stm32/stm32wb55rg", "chrono", "not-gpdma", "ble", "mac" stm32wba52cg = ["embassy-stm32/stm32wba52cg", "spi-v345", "chrono", "rng", "hash"] stm32wl55jc = ["embassy-stm32/stm32wl55jc-cm4", "not-gpdma", "rng", "chrono"] stm32f091rc = ["embassy-stm32/stm32f091rc", "cm0", "not-gpdma", "chrono"] +stm32f072rb = ["embassy-stm32/stm32f072rb", "cm0", "not-gpdma", "chrono"] stm32h503rb = ["embassy-stm32/stm32h503rb", "spi-v345", "rng", "stop"] stm32h7s3l8 = ["embassy-stm32/stm32h7s3l8", "spi-v345", "rng", "cordic", "hash-v34"] # TODO: fdcan crashes, cryp dma hangs. stm32u083rc = ["embassy-stm32/stm32u083rc", "cm0", "rng", "chrono"] @@ -159,6 +160,11 @@ name = "hash" path = "src/bin/hash.rs" required-features = [ "hash",] +[[bin]] +name = "i2c_v2" +path = "src/bin/i2c_v2.rs" +required-features = [ "stm32f072rb",] + [[bin]] name = "rng" path = "src/bin/rng.rs" @@ -285,6 +291,7 @@ build = [ { target = "thumbv7em-none-eabi", features = ["stm32wl55jc"], artifact-dir = "out/tests/stm32wl55jc" }, { target = "thumbv7em-none-eabi", features = ["stm32h7s3l8"], artifact-dir = "out/tests/stm32h7s3l8" }, { target = "thumbv6m-none-eabi", features = ["stm32f091rc"], artifact-dir = "out/tests/stm32f091rc" }, + { target = "thumbv6m-none-eabi", features = ["stm32f072rb"], artifact-dir = "out/tests/stm32f072rb" }, { target = "thumbv8m.main-none-eabihf", features = ["stm32h503rb"], artifact-dir = "out/tests/stm32h503rb" }, { target = "thumbv6m-none-eabi", features = ["stm32u083rc"], artifact-dir = "out/tests/stm32u083rc" } ] diff --git a/tests/stm32/src/bin/i2c_v2.rs b/tests/stm32/src/bin/i2c_v2.rs new file mode 100644 index 000000000..9a23e28e1 --- /dev/null +++ b/tests/stm32/src/bin/i2c_v2.rs @@ -0,0 +1,220 @@ +#![no_std] +#![no_main] +// required-features: stm32f072rb +// +// Hardware Setup for NUCLEO-F072RB: +// - I2C1 pins: PB8 (SCL), PB9 (SDA) on CN5 connector +// - Connect to I2C slave device (e.g., Digilent Analog Discovery I2C slave, or EEPROM) +// - Default slave address: 0x50 +// - Pull-up resistors: 4.7kΩ on both SCL and SDA +// - CN5 Pin 5 (PB8/SCL) and CN5 Pin 9 (PB9/SDA) +// +// Analog Discovery Setup: +// - Configure as I2C Slave at address 0x50 +// - DIO 0: SCL +// - DIO 1: SDA +// - Enable pull-ups or use external 4.7kΩ pull-up resistors + +#[path = "../common.rs"] +mod common; + +use common::*; +use embassy_executor::Spawner; +use embassy_stm32::i2c::{Config, I2c, Master}; +use embassy_stm32::mode::Blocking; +use embassy_stm32::time::Hertz; +use embassy_time::block_for; +use embedded_hal_1::i2c::Operation; + +#[embassy_executor::main] +async fn main(_spawner: Spawner) { + let p = init(); + info!("I2C v2 Transaction Test Starting..."); + + let mut i2c_peri = peri!(p, I2C); + let mut scl = peri!(p, I2C_SCL); + let mut sda = peri!(p, I2C_SDA); + + let mut config = Config::default(); + config.frequency = Hertz(100_000); + + let mut i2c = I2c::new_blocking( + i2c_peri.reborrow(), + scl.reborrow(), + sda.reborrow(), + config, + ); + + // I2C slave address for Analog Discovery or test EEPROM + let slave_addr = 0x50u8; + + // Wait for slave device to be ready + block_for(embassy_time::Duration::from_millis(100)); + + info!("=== Test 1: Consecutive Writes (Should Merge) ==="); + test_consecutive_writes(&mut i2c, slave_addr); + + info!("=== Test 2: Consecutive Reads (Should Merge) ==="); + test_consecutive_reads(&mut i2c, slave_addr); + + info!("=== Test 3: Write then Read (RESTART) ==="); + test_write_then_read(&mut i2c, slave_addr); + + info!("=== Test 4: Read then Write (RESTART) ==="); + test_read_then_write(&mut i2c, slave_addr); + + info!("=== Test 5: Complex Mixed Sequence ==="); + test_mixed_sequence(&mut i2c, slave_addr); + + info!("=== Test 6: Single Operations ==="); + test_single_operations(&mut i2c, slave_addr); + + info!("Test OK"); + cortex_m::asm::bkpt(); +} + +fn test_consecutive_writes(i2c: &mut I2c<'_, Blocking, Master>, addr: u8) { + // Expected on bus: START, ADDR+W, data1, data2, data3, STOP + // NO intermediate RESTART/STOP between writes - they should be merged + let data1 = [0x10, 0x11, 0x12]; + let data2 = [0x20, 0x21]; + let data3 = [0x30, 0x31, 0x32, 0x33]; + + let mut ops = [ + Operation::Write(&data1), + Operation::Write(&data2), + Operation::Write(&data3), + ]; + + match i2c.blocking_transaction(addr, &mut ops) { + Ok(_) => info!("✓ Consecutive writes succeeded (merged 9 bytes)"), + Err(e) => { + error!("✗ Consecutive writes failed: {:?}", e); + defmt::panic!("Test failed: consecutive writes"); + } + } +} + +fn test_consecutive_reads(i2c: &mut I2c<'_, Blocking, Master>, addr: u8) { + // Expected on bus: START, ADDR+R, data1, data2, data3, NACK, STOP + // NO intermediate RESTART/STOP between reads - they should be merged + let mut buf1 = [0u8; 4]; + let mut buf2 = [0u8; 3]; + let mut buf3 = [0u8; 2]; + + let mut ops = [ + Operation::Read(&mut buf1), + Operation::Read(&mut buf2), + Operation::Read(&mut buf3), + ]; + + match i2c.blocking_transaction(addr, &mut ops) { + Ok(_) => { + info!("✓ Consecutive reads succeeded (merged 9 bytes)"); + info!(" buf1: {:02x}", buf1); + info!(" buf2: {:02x}", buf2); + info!(" buf3: {:02x}", buf3); + } + Err(e) => { + error!("✗ Consecutive reads failed: {:?}", e); + defmt::panic!("Test failed: consecutive reads"); + } + } +} + +fn test_write_then_read(i2c: &mut I2c<'_, Blocking, Master>, addr: u8) { + // Expected: START, ADDR+W, data, RESTART, ADDR+R, data, NACK, STOP + let write_data = [0xAA, 0xBB]; + let mut read_buf = [0u8; 4]; + + let mut ops = [Operation::Write(&write_data), Operation::Read(&mut read_buf)]; + + match i2c.blocking_transaction(addr, &mut ops) { + Ok(_) => { + info!("✓ Write-then-read succeeded with RESTART"); + info!(" Written: {:02x}", write_data); + info!(" Read: {:02x}", read_buf); + } + Err(e) => { + error!("✗ Write-then-read failed: {:?}", e); + defmt::panic!("Test failed: write-then-read"); + } + } +} + +fn test_read_then_write(i2c: &mut I2c<'_, Blocking, Master>, addr: u8) { + // Expected: START, ADDR+R, data, NACK, RESTART, ADDR+W, data, STOP + let mut read_buf = [0u8; 3]; + let write_data = [0xCC, 0xDD, 0xEE]; + + let mut ops = [Operation::Read(&mut read_buf), Operation::Write(&write_data)]; + + match i2c.blocking_transaction(addr, &mut ops) { + Ok(_) => { + info!("✓ Read-then-write succeeded with RESTART"); + info!(" Read: {:02x}", read_buf); + info!(" Written: {:02x}", write_data); + } + Err(e) => { + error!("✗ Read-then-write failed: {:?}", e); + defmt::panic!("Test failed: read-then-write"); + } + } +} + +fn test_mixed_sequence(i2c: &mut I2c<'_, Blocking, Master>, addr: u8) { + // Complex: W, W, R, R, W, R + // Groups: [W,W] RESTART [R,R] RESTART [W] RESTART [R] + let w1 = [0x01, 0x02]; + let w2 = [0x03, 0x04]; + let mut r1 = [0u8; 2]; + let mut r2 = [0u8; 2]; + let w3 = [0x05]; + let mut r3 = [0u8; 1]; + + let mut ops = [ + Operation::Write(&w1), + Operation::Write(&w2), + Operation::Read(&mut r1), + Operation::Read(&mut r2), + Operation::Write(&w3), + Operation::Read(&mut r3), + ]; + + match i2c.blocking_transaction(addr, &mut ops) { + Ok(_) => { + info!("✓ Mixed sequence succeeded"); + info!(" Groups: [W4] RESTART [R4] RESTART [W1] RESTART [R1]"); + } + Err(e) => { + error!("✗ Mixed sequence failed: {:?}", e); + defmt::panic!("Test failed: mixed sequence"); + } + } +} + +fn test_single_operations(i2c: &mut I2c<'_, Blocking, Master>, addr: u8) { + // Test single write + let write_data = [0xFF]; + let mut ops = [Operation::Write(&write_data)]; + + match i2c.blocking_transaction(addr, &mut ops) { + Ok(_) => info!("✓ Single write succeeded"), + Err(e) => { + error!("✗ Single write failed: {:?}", e); + defmt::panic!("Test failed: single write"); + } + } + + // Test single read + let mut read_buf = [0u8; 1]; + let mut ops = [Operation::Read(&mut read_buf)]; + + match i2c.blocking_transaction(addr, &mut ops) { + Ok(_) => info!("✓ Single read succeeded, data: 0x{:02x}", read_buf[0]), + Err(e) => { + error!("✗ Single read failed: {:?}", e); + defmt::panic!("Test failed: single read"); + } + } +} diff --git a/tests/stm32/src/common.rs b/tests/stm32/src/common.rs index 096cce947..07b667ade 100644 --- a/tests/stm32/src/common.rs +++ b/tests/stm32/src/common.rs @@ -60,6 +60,8 @@ teleprobe_meta::target!(b"nucleo-stm32wl55jc"); teleprobe_meta::target!(b"nucleo-stm32wba52cg"); #[cfg(feature = "stm32f091rc")] teleprobe_meta::target!(b"nucleo-stm32f091rc"); +#[cfg(feature = "stm32f072rb")] +teleprobe_meta::target!(b"nucleo-stm32f072rb"); #[cfg(feature = "stm32h503rb")] teleprobe_meta::target!(b"nucleo-stm32h503rb"); #[cfg(feature = "stm32h7s3l8")] @@ -103,6 +105,14 @@ define_peris!( SPI = SPI1, SPI_SCK = PA5, SPI_MOSI = PA7, SPI_MISO = PA6, SPI_TX_DMA = DMA1_CH3, SPI_RX_DMA = DMA1_CH2, @irq UART = {USART1 => embassy_stm32::usart::InterruptHandler;}, ); +#[cfg(feature = "stm32f072rb")] +define_peris!( + UART = USART1, UART_TX = PA9, UART_RX = PA10, UART_TX_DMA = DMA1_CH4, UART_RX_DMA = DMA1_CH5, + SPI = SPI1, SPI_SCK = PA5, SPI_MOSI = PA7, SPI_MISO = PA6, SPI_TX_DMA = DMA1_CH3, SPI_RX_DMA = DMA1_CH2, + I2C = I2C1, I2C_SCL = PB8, I2C_SDA = PB9, + @irq UART = {USART1 => embassy_stm32::usart::InterruptHandler;}, + @irq I2C = {I2C1 => embassy_stm32::i2c::EventInterruptHandler, embassy_stm32::i2c::ErrorInterruptHandler;}, +); #[cfg(any(feature = "stm32f100rd", feature = "stm32f103c8", feature = "stm32f107vc"))] define_peris!( UART = USART1, UART_TX = PA9, UART_RX = PA10, UART_TX_DMA = DMA1_CH4, UART_RX_DMA = DMA1_CH5, @@ -325,6 +335,21 @@ pub fn config() -> Config { config.rcc.ahb_pre = AHBPrescaler::DIV1; config.rcc.apb1_pre = APBPrescaler::DIV1; } + #[cfg(feature = "stm32f072rb")] + { + config.rcc.hse = Some(Hse { + freq: Hertz(8_000_000), + mode: HseMode::Bypass, + }); + config.rcc.pll = Some(Pll { + src: PllSource::HSE, + prediv: PllPreDiv::DIV1, + mul: PllMul::MUL6, + }); + config.rcc.sys = Sysclk::PLL1_P; + config.rcc.ahb_pre = AHBPrescaler::DIV1; + config.rcc.apb1_pre = APBPrescaler::DIV1; + } #[cfg(feature = "stm32f103c8")] { config.rcc.hse = Some(Hse { -- cgit From 9e2a4161b32154f19963a222b3eb8f956b60d820 Mon Sep 17 00:00:00 2001 From: HybridChild Date: Wed, 12 Nov 2025 21:55:20 +0100 Subject: stm32/i2c: Fix v2 async transaction implementation --- embassy-stm32/src/i2c/v2.rs | 261 +++++++++++++++++++++++++----------------- tests/stm32/build.rs | 1 + tests/stm32/src/bin/i2c_v2.rs | 238 +++++++++++++++++++++++++++++++++----- tests/stm32/src/common.rs | 2 +- 4 files changed, 363 insertions(+), 139 deletions(-) diff --git a/embassy-stm32/src/i2c/v2.rs b/embassy-stm32/src/i2c/v2.rs index 3c43887c0..c9656d2c2 100644 --- a/embassy-stm32/src/i2c/v2.rs +++ b/embassy-stm32/src/i2c/v2.rs @@ -225,7 +225,14 @@ impl<'d, M: Mode, IM: MasterMode> I2c<'d, M, IM> { fn reload(info: &'static Info, length: usize, will_reload: bool, stop: Stop, timeout: Timeout) -> Result<(), Error> { assert!(length < 256 && length > 0); - while !info.regs.isr().read().tcr() { + // Wait for either TCR (Transfer Complete Reload) or TC (Transfer Complete) + // TCR occurs when RELOAD=1, TC occurs when RELOAD=0 + // Both indicate the peripheral is ready for the next transfer + loop { + let isr = info.regs.isr().read(); + if isr.tcr() || isr.tc() { + break; + } timeout.check()?; } @@ -885,12 +892,12 @@ impl<'d, IM: MasterMode> I2c<'d, Async, IM> { address, total_len.min(255), Stop::Software, - (total_len > 255) || !last_slice, + total_len > 255, restart, timeout, )?; } else { - Self::reload(self.info, total_len.min(255), (total_len > 255) || !last_slice, Stop::Software, timeout)?; + Self::reload(self.info, total_len.min(255), total_len > 255, Stop::Software, timeout)?; self.info.regs.cr1().modify(|w| w.set_tcie(true)); } } else if !(isr.tcr() || isr.tc()) { @@ -899,9 +906,7 @@ impl<'d, IM: MasterMode> I2c<'d, Async, IM> { } else if remaining_len == 0 { return Poll::Ready(Ok(())); } else { - let last_piece = (remaining_len <= 255) && last_slice; - - if let Err(e) = Self::reload(self.info, remaining_len.min(255), !last_piece, Stop::Software, timeout) { + if let Err(e) = Self::reload(self.info, remaining_len.min(255), remaining_len > 255, Stop::Software, timeout) { return Poll::Ready(Err(e)); } self.info.regs.cr1().modify(|w| w.set_tcie(true)); @@ -913,10 +918,9 @@ impl<'d, IM: MasterMode> I2c<'d, Async, IM> { .await?; dma_transfer.await; - if last_slice { - // This should be done already - self.wait_tc(timeout)?; - } + + // Always wait for TC after DMA completes - needed for consecutive buffers + self.wait_tc(timeout)?; if last_slice & send_stop { self.master_stop(); @@ -1173,56 +1177,9 @@ impl<'d, IM: MasterMode> I2c<'d, Async, IM> { is_last_group: bool, timeout: Timeout, ) -> Result<(), Error> { - // Calculate total bytes across all operations in this group - let total_bytes = Self::total_operation_bytes(operations); - - if total_bytes == 0 { - // Handle empty write group using blocking call - if is_first_group { - Self::master_write(self.info, address, 0, Stop::Software, false, !is_first_group, timeout)?; - } - if is_last_group { - self.master_stop(); - } - return Ok(()); - } - - let send_stop = is_last_group; - - // Use DMA for each operation in the group - let mut first_in_group = true; - let mut remaining_operations = operations.len(); - - for operation in operations { - if let Operation::Write(buffer) = operation { - remaining_operations -= 1; - let is_last_in_group = remaining_operations == 0; - - if buffer.is_empty() { - // Skip empty buffers - continue; - } - - let fut = self.write_dma_internal( - address, - buffer, - first_in_group && is_first_group, // first_slice - is_last_in_group && is_last_group, // last_slice - send_stop && is_last_in_group, // send_stop - !is_first_group && first_in_group, // restart - timeout, - ); - timeout.with(fut).await?; - first_in_group = false; - } - } - - // If not last group, wait for TC to enable RESTART - if !is_last_group { - self.wait_tc(timeout)?; - } - - Ok(()) + // For now, use blocking implementation for write groups + // This avoids complexity of handling multiple non-contiguous buffers with DMA + self.execute_write_group(address, operations, is_first_group, is_last_group, timeout) } async fn execute_read_group_async( @@ -1255,10 +1212,10 @@ impl<'d, IM: MasterMode> I2c<'d, Async, IM> { return Ok(()); } - // For read operations, we need to handle them differently since read_dma_internal - // expects a single buffer. We'll iterate through operations and use DMA for each. - let mut total_remaining = total_bytes; + // Use DMA for read operations - need to handle multiple buffers let restart = !is_first_group; + let mut total_remaining = total_bytes; + let mut is_first_in_group = true; for operation in operations { if let Operation::Read(buffer) = operation { @@ -1267,57 +1224,46 @@ impl<'d, IM: MasterMode> I2c<'d, Async, IM> { continue; } - let is_first_read = total_remaining == total_bytes; let buf_len = buffer.len(); total_remaining -= buf_len; - let is_last_read = total_remaining == 0; - - if is_first_read { - // First read in the group - let completed_chunks = buf_len / 255; - let total_chunks = if completed_chunks * 255 == buf_len { - completed_chunks + let is_last_in_group = total_remaining == 0; + + // Perform DMA read + if is_first_in_group { + // First buffer: use read_dma_internal which handles restart properly + // Only use Automatic stop if this is the last buffer in the last group + let stop_mode = if is_last_group && is_last_in_group { + Stop::Automatic } else { - completed_chunks + 1 + Stop::Software }; - let last_chunk_idx = total_chunks.saturating_sub(1); - // Use master_read to initiate, then DMA for data - Self::master_read( - self.info, + // We need a custom DMA read that respects our stop mode + self.read_dma_group_internal( address, - buf_len.min(255), - if is_last_group && is_last_read { - Stop::Automatic - } else { - Stop::Software - }, - last_chunk_idx != 0 || !is_last_read, // reload + buffer, restart, + stop_mode, timeout, - )?; - } - - // Use the existing read_dma_internal, but we need to handle the reload logic ourselves - // For simplicity with consecutive reads, fall back to blocking for now - // This maintains correctness while keeping complexity manageable - for (chunk_idx, chunk) in buffer.chunks_mut(255).enumerate() { - let chunk_len = chunk.len(); - let is_very_last = total_remaining == 0 && chunk_len == chunk.len(); - - if !is_first_read || chunk_idx != 0 { - let stop = if is_last_group && is_very_last { - Stop::Automatic - } else { - Stop::Software - }; - Self::reload(self.info, chunk_len, !(is_last_group && is_very_last), stop, timeout)?; - } + ) + .await?; + is_first_in_group = false; + } else { + // Subsequent buffers: need to reload and continue + let stop_mode = if is_last_group && is_last_in_group { + Stop::Automatic + } else { + Stop::Software + }; - for byte in chunk { - self.wait_rxne(timeout)?; - *byte = self.info.regs.rxdr().read().rxdata(); - } + self.read_dma_group_internal( + address, + buffer, + false, // no restart for subsequent buffers in same group + stop_mode, + timeout, + ) + .await?; } } } @@ -1326,9 +1272,108 @@ impl<'d, IM: MasterMode> I2c<'d, Async, IM> { if is_last_group { self.wait_stop(timeout)?; } - // For non-last read groups, don't wait for TC - the peripheral may hold SCL low - // in Software AUTOEND mode until the next START is issued. Just proceed directly - // to the next group which will issue RESTART and release the clock. + + Ok(()) + } + + /// Internal DMA read helper for transaction groups + async fn read_dma_group_internal( + &mut self, + address: Address, + buffer: &mut [u8], + restart: bool, + stop_mode: Stop, + timeout: Timeout, + ) -> Result<(), Error> { + let total_len = buffer.len(); + + let dma_transfer = unsafe { + let regs = self.info.regs; + regs.cr1().modify(|w| { + w.set_rxdmaen(true); + w.set_tcie(true); + w.set_nackie(true); + w.set_errie(true); + }); + let src = regs.rxdr().as_ptr() as *mut u8; + + self.rx_dma.as_mut().unwrap().read(src, buffer, Default::default()) + }; + + let mut remaining_len = total_len; + + let on_drop = OnDrop::new(|| { + let regs = self.info.regs; + regs.cr1().modify(|w| { + w.set_rxdmaen(false); + w.set_tcie(false); + w.set_nackie(false); + w.set_errie(false); + }); + regs.icr().write(|w| { + w.set_nackcf(true); + w.set_berrcf(true); + w.set_arlocf(true); + w.set_ovrcf(true); + }); + }); + + poll_fn(|cx| { + self.state.waker.register(cx.waker()); + + let isr = self.info.regs.isr().read(); + + if isr.nackf() { + return Poll::Ready(Err(Error::Nack)); + } + if isr.arlo() { + return Poll::Ready(Err(Error::Arbitration)); + } + if isr.berr() { + return Poll::Ready(Err(Error::Bus)); + } + if isr.ovr() { + return Poll::Ready(Err(Error::Overrun)); + } + + if remaining_len == total_len { + Self::master_read( + self.info, + address, + total_len.min(255), + stop_mode, + total_len > 255, // reload + restart, + timeout, + )?; + if total_len <= 255 { + return Poll::Ready(Ok(())); + } + } else if isr.tcr() { + // Transfer Complete Reload - need to set up next chunk + let last_piece = remaining_len <= 255; + + if let Err(e) = Self::reload(self.info, remaining_len.min(255), !last_piece, stop_mode, timeout) { + return Poll::Ready(Err(e)); + } + // Return here if we are on last chunk, + // end of transfer will be awaited with the DMA below + if last_piece { + return Poll::Ready(Ok(())); + } + self.info.regs.cr1().modify(|w| w.set_tcie(true)); + } else { + // poll_fn was woken without TCR interrupt + return Poll::Pending; + } + + remaining_len = remaining_len.saturating_sub(255); + Poll::Pending + }) + .await?; + + dma_transfer.await; + drop(on_drop); Ok(()) } diff --git a/tests/stm32/build.rs b/tests/stm32/build.rs index 556d77a20..34030c206 100644 --- a/tests/stm32/build.rs +++ b/tests/stm32/build.rs @@ -15,6 +15,7 @@ fn main() -> Result<(), Box> { feature = "stm32c071rb", // 24 kb feature = "stm32l073rz", // 20 kb feature = "stm32h503rb", // 32 kb + feature = "stm32f072rb", // 16 kb - I2C v2 test with async too large for RAM // no VTOR, so interrupts can't work when running from RAM feature = "stm32f091rc", )) { diff --git a/tests/stm32/src/bin/i2c_v2.rs b/tests/stm32/src/bin/i2c_v2.rs index 9a23e28e1..087b8bbd9 100644 --- a/tests/stm32/src/bin/i2c_v2.rs +++ b/tests/stm32/src/bin/i2c_v2.rs @@ -7,7 +7,7 @@ // - Connect to I2C slave device (e.g., Digilent Analog Discovery I2C slave, or EEPROM) // - Default slave address: 0x50 // - Pull-up resistors: 4.7kΩ on both SCL and SDA -// - CN5 Pin 5 (PB8/SCL) and CN5 Pin 9 (PB9/SDA) +// - CN5 Pin 10 (PB8/SCL) and CN5 Pin 9 (PB9/SDA) // // Analog Discovery Setup: // - Configure as I2C Slave at address 0x50 @@ -21,9 +21,9 @@ mod common; use common::*; use embassy_executor::Spawner; use embassy_stm32::i2c::{Config, I2c, Master}; -use embassy_stm32::mode::Blocking; +use embassy_stm32::mode::{Async, Blocking}; use embassy_stm32::time::Hertz; -use embassy_time::block_for; +use embassy_time::Timer; use embedded_hal_1::i2c::Operation; #[embassy_executor::main] @@ -38,42 +38,80 @@ async fn main(_spawner: Spawner) { let mut config = Config::default(); config.frequency = Hertz(100_000); - let mut i2c = I2c::new_blocking( - i2c_peri.reborrow(), - scl.reborrow(), - sda.reborrow(), - config, - ); - // I2C slave address for Analog Discovery or test EEPROM let slave_addr = 0x50u8; // Wait for slave device to be ready - block_for(embassy_time::Duration::from_millis(100)); + Timer::after_millis(100).await; + + // ========== BLOCKING TESTS ========== + info!("========== BLOCKING TRANSACTION TESTS =========="); + { + let mut i2c = I2c::new_blocking( + i2c_peri.reborrow(), + scl.reborrow(), + sda.reborrow(), + config, + ); + + info!("=== Test 1: Consecutive Writes (Should Merge) ==="); + test_consecutive_writes_blocking(&mut i2c, slave_addr); + + info!("=== Test 2: Consecutive Reads (Should Merge) ==="); + test_consecutive_reads_blocking(&mut i2c, slave_addr); + + info!("=== Test 3: Write then Read (RESTART) ==="); + test_write_then_read_blocking(&mut i2c, slave_addr); + + info!("=== Test 4: Read then Write (RESTART) ==="); + test_read_then_write_blocking(&mut i2c, slave_addr); + + info!("=== Test 5: Complex Mixed Sequence ==="); + test_mixed_sequence_blocking(&mut i2c, slave_addr); + + info!("=== Test 6: Single Operations ==="); + test_single_operations_blocking(&mut i2c, slave_addr); + + info!("Blocking tests OK"); + } + + Timer::after_millis(100).await; + + // ========== ASYNC TESTS ========== + info!("========== ASYNC TRANSACTION TESTS (DMA) =========="); + { + let tx_dma = peri!(p, I2C_TX_DMA); + let rx_dma = peri!(p, I2C_RX_DMA); + let irq = irqs!(I2C); - info!("=== Test 1: Consecutive Writes (Should Merge) ==="); - test_consecutive_writes(&mut i2c, slave_addr); + let mut i2c = I2c::new(i2c_peri, scl, sda, irq, tx_dma, rx_dma, config); - info!("=== Test 2: Consecutive Reads (Should Merge) ==="); - test_consecutive_reads(&mut i2c, slave_addr); + info!("=== Test 1: Consecutive Writes (Should Merge) ==="); + test_consecutive_writes_async(&mut i2c, slave_addr).await; - info!("=== Test 3: Write then Read (RESTART) ==="); - test_write_then_read(&mut i2c, slave_addr); + info!("=== Test 2: Consecutive Reads (Should Merge) ==="); + test_consecutive_reads_async(&mut i2c, slave_addr).await; - info!("=== Test 4: Read then Write (RESTART) ==="); - test_read_then_write(&mut i2c, slave_addr); + info!("=== Test 3: Write then Read (RESTART) ==="); + test_write_then_read_async(&mut i2c, slave_addr).await; - info!("=== Test 5: Complex Mixed Sequence ==="); - test_mixed_sequence(&mut i2c, slave_addr); + info!("=== Test 4: Read then Write (RESTART) ==="); + test_read_then_write_async(&mut i2c, slave_addr).await; - info!("=== Test 6: Single Operations ==="); - test_single_operations(&mut i2c, slave_addr); + info!("=== Test 5: Complex Mixed Sequence ==="); + test_mixed_sequence_async(&mut i2c, slave_addr).await; - info!("Test OK"); + info!("=== Test 6: Single Operations ==="); + test_single_operations_async(&mut i2c, slave_addr).await; + + info!("Async tests OK"); + } + + info!("All tests OK"); cortex_m::asm::bkpt(); } -fn test_consecutive_writes(i2c: &mut I2c<'_, Blocking, Master>, addr: u8) { +fn test_consecutive_writes_blocking(i2c: &mut I2c<'_, Blocking, Master>, addr: u8) { // Expected on bus: START, ADDR+W, data1, data2, data3, STOP // NO intermediate RESTART/STOP between writes - they should be merged let data1 = [0x10, 0x11, 0x12]; @@ -95,7 +133,7 @@ fn test_consecutive_writes(i2c: &mut I2c<'_, Blocking, Master>, addr: u8) { } } -fn test_consecutive_reads(i2c: &mut I2c<'_, Blocking, Master>, addr: u8) { +fn test_consecutive_reads_blocking(i2c: &mut I2c<'_, Blocking, Master>, addr: u8) { // Expected on bus: START, ADDR+R, data1, data2, data3, NACK, STOP // NO intermediate RESTART/STOP between reads - they should be merged let mut buf1 = [0u8; 4]; @@ -122,7 +160,7 @@ fn test_consecutive_reads(i2c: &mut I2c<'_, Blocking, Master>, addr: u8) { } } -fn test_write_then_read(i2c: &mut I2c<'_, Blocking, Master>, addr: u8) { +fn test_write_then_read_blocking(i2c: &mut I2c<'_, Blocking, Master>, addr: u8) { // Expected: START, ADDR+W, data, RESTART, ADDR+R, data, NACK, STOP let write_data = [0xAA, 0xBB]; let mut read_buf = [0u8; 4]; @@ -142,7 +180,7 @@ fn test_write_then_read(i2c: &mut I2c<'_, Blocking, Master>, addr: u8) { } } -fn test_read_then_write(i2c: &mut I2c<'_, Blocking, Master>, addr: u8) { +fn test_read_then_write_blocking(i2c: &mut I2c<'_, Blocking, Master>, addr: u8) { // Expected: START, ADDR+R, data, NACK, RESTART, ADDR+W, data, STOP let mut read_buf = [0u8; 3]; let write_data = [0xCC, 0xDD, 0xEE]; @@ -162,7 +200,7 @@ fn test_read_then_write(i2c: &mut I2c<'_, Blocking, Master>, addr: u8) { } } -fn test_mixed_sequence(i2c: &mut I2c<'_, Blocking, Master>, addr: u8) { +fn test_mixed_sequence_blocking(i2c: &mut I2c<'_, Blocking, Master>, addr: u8) { // Complex: W, W, R, R, W, R // Groups: [W,W] RESTART [R,R] RESTART [W] RESTART [R] let w1 = [0x01, 0x02]; @@ -193,7 +231,7 @@ fn test_mixed_sequence(i2c: &mut I2c<'_, Blocking, Master>, addr: u8) { } } -fn test_single_operations(i2c: &mut I2c<'_, Blocking, Master>, addr: u8) { +fn test_single_operations_blocking(i2c: &mut I2c<'_, Blocking, Master>, addr: u8) { // Test single write let write_data = [0xFF]; let mut ops = [Operation::Write(&write_data)]; @@ -218,3 +256,143 @@ fn test_single_operations(i2c: &mut I2c<'_, Blocking, Master>, addr: u8) { } } } + +// ==================== ASYNC TEST FUNCTIONS ==================== + +async fn test_consecutive_writes_async(i2c: &mut I2c<'_, Async, Master>, addr: u8) { + let data1 = [0x10, 0x11, 0x12]; + let data2 = [0x20, 0x21]; + let data3 = [0x30, 0x31, 0x32, 0x33]; + + let mut ops = [ + Operation::Write(&data1), + Operation::Write(&data2), + Operation::Write(&data3), + ]; + + match i2c.transaction(addr, &mut ops).await { + Ok(_) => info!("✓ Consecutive writes succeeded (merged 9 bytes)"), + Err(e) => { + error!("✗ Consecutive writes failed: {:?}", e); + defmt::panic!("Test failed: consecutive writes"); + } + } +} + +async fn test_consecutive_reads_async(i2c: &mut I2c<'_, Async, Master>, addr: u8) { + let mut buf1 = [0u8; 4]; + let mut buf2 = [0u8; 3]; + let mut buf3 = [0u8; 2]; + + let mut ops = [ + Operation::Read(&mut buf1), + Operation::Read(&mut buf2), + Operation::Read(&mut buf3), + ]; + + match i2c.transaction(addr, &mut ops).await { + Ok(_) => { + info!("✓ Consecutive reads succeeded (merged 9 bytes)"); + info!(" buf1: {:02x}", buf1); + info!(" buf2: {:02x}", buf2); + info!(" buf3: {:02x}", buf3); + } + Err(e) => { + error!("✗ Consecutive reads failed: {:?}", e); + defmt::panic!("Test failed: consecutive reads"); + } + } +} + +async fn test_write_then_read_async(i2c: &mut I2c<'_, Async, Master>, addr: u8) { + let write_data = [0xAA, 0xBB]; + let mut read_buf = [0u8; 4]; + + let mut ops = [Operation::Write(&write_data), Operation::Read(&mut read_buf)]; + + match i2c.transaction(addr, &mut ops).await { + Ok(_) => { + info!("✓ Write-then-read succeeded with RESTART"); + info!(" Written: {:02x}", write_data); + info!(" Read: {:02x}", read_buf); + } + Err(e) => { + error!("✗ Write-then-read failed: {:?}", e); + defmt::panic!("Test failed: write-then-read"); + } + } +} + +async fn test_read_then_write_async(i2c: &mut I2c<'_, Async, Master>, addr: u8) { + let mut read_buf = [0u8; 3]; + let write_data = [0xCC, 0xDD, 0xEE]; + + let mut ops = [Operation::Read(&mut read_buf), Operation::Write(&write_data)]; + + match i2c.transaction(addr, &mut ops).await { + Ok(_) => { + info!("✓ Read-then-write succeeded with RESTART"); + info!(" Read: {:02x}", read_buf); + info!(" Written: {:02x}", write_data); + } + Err(e) => { + error!("✗ Read-then-write failed: {:?}", e); + defmt::panic!("Test failed: read-then-write"); + } + } +} + +async fn test_mixed_sequence_async(i2c: &mut I2c<'_, Async, Master>, addr: u8) { + let w1 = [0x01, 0x02]; + let w2 = [0x03, 0x04]; + let mut r1 = [0u8; 2]; + let mut r2 = [0u8; 2]; + let w3 = [0x05]; + let mut r3 = [0u8; 1]; + + let mut ops = [ + Operation::Write(&w1), + Operation::Write(&w2), + Operation::Read(&mut r1), + Operation::Read(&mut r2), + Operation::Write(&w3), + Operation::Read(&mut r3), + ]; + + match i2c.transaction(addr, &mut ops).await { + Ok(_) => { + info!("✓ Mixed sequence succeeded"); + info!(" Groups: [W4] RESTART [R4] RESTART [W1] RESTART [R1]"); + } + Err(e) => { + error!("✗ Mixed sequence failed: {:?}", e); + defmt::panic!("Test failed: mixed sequence"); + } + } +} + +async fn test_single_operations_async(i2c: &mut I2c<'_, Async, Master>, addr: u8) { + // Test single write + let write_data = [0xFF]; + let mut ops = [Operation::Write(&write_data)]; + + match i2c.transaction(addr, &mut ops).await { + Ok(_) => info!("✓ Single write succeeded"), + Err(e) => { + error!("✗ Single write failed: {:?}", e); + defmt::panic!("Test failed: single write"); + } + } + + // Test single read + let mut read_buf = [0u8; 1]; + let mut ops = [Operation::Read(&mut read_buf)]; + + match i2c.transaction(addr, &mut ops).await { + Ok(_) => info!("✓ Single read succeeded, data: 0x{:02x}", read_buf[0]), + Err(e) => { + error!("✗ Single read failed: {:?}", e); + defmt::panic!("Test failed: single read"); + } + } +} diff --git a/tests/stm32/src/common.rs b/tests/stm32/src/common.rs index 07b667ade..de06cb267 100644 --- a/tests/stm32/src/common.rs +++ b/tests/stm32/src/common.rs @@ -109,7 +109,7 @@ define_peris!( define_peris!( UART = USART1, UART_TX = PA9, UART_RX = PA10, UART_TX_DMA = DMA1_CH4, UART_RX_DMA = DMA1_CH5, SPI = SPI1, SPI_SCK = PA5, SPI_MOSI = PA7, SPI_MISO = PA6, SPI_TX_DMA = DMA1_CH3, SPI_RX_DMA = DMA1_CH2, - I2C = I2C1, I2C_SCL = PB8, I2C_SDA = PB9, + I2C = I2C1, I2C_SCL = PB8, I2C_SDA = PB9, I2C_TX_DMA = DMA1_CH6, I2C_RX_DMA = DMA1_CH7, @irq UART = {USART1 => embassy_stm32::usart::InterruptHandler;}, @irq I2C = {I2C1 => embassy_stm32::i2c::EventInterruptHandler, embassy_stm32::i2c::ErrorInterruptHandler;}, ); -- cgit From f0506252e21c96ce3b03e0d1c061a831d7dfe3c3 Mon Sep 17 00:00:00 2001 From: xoviat Date: Wed, 12 Nov 2025 20:06:00 -0600 Subject: stm32: extract adc4 extract adc4 into common adc system and add anyInstance trait to cover adc4 and not adc4 --- embassy-stm32/Cargo.toml | 1 + embassy-stm32/build.rs | 4 +- embassy-stm32/src/adc/adc4.rs | 312 ++++++++++++++-------------------- embassy-stm32/src/adc/g4.rs | 206 +++++++++++----------- embassy-stm32/src/adc/injected.rs | 6 +- embassy-stm32/src/adc/mod.rs | 111 +++++++----- embassy-stm32/src/adc/ringbuffered.rs | 12 +- embassy-stm32/src/adc/v2.rs | 66 +++---- embassy-stm32/src/adc/v3.rs | 302 ++++++++++++++++---------------- embassy-stm32/src/adc/v4.rs | 220 ++++++++++++------------ examples/stm32u5/src/bin/adc.rs | 21 ++- examples/stm32wba/src/bin/adc.rs | 17 +- examples/stm32wba6/src/bin/adc.rs | 17 +- 13 files changed, 651 insertions(+), 644 deletions(-) diff --git a/embassy-stm32/Cargo.toml b/embassy-stm32/Cargo.toml index ec49924a2..2f4f2ce51 100644 --- a/embassy-stm32/Cargo.toml +++ b/embassy-stm32/Cargo.toml @@ -188,6 +188,7 @@ embedded-io = { version = "0.6.0" } embedded-io-async = { version = "0.6.1" } chrono = { version = "^0.4", default-features = false, optional = true } bit_field = "0.10.2" +trait-set = "0.3.0" document-features = "0.2.7" static_assertions = { version = "1.1" } diff --git a/embassy-stm32/build.rs b/embassy-stm32/build.rs index 48da475df..ad6743f96 100644 --- a/embassy-stm32/build.rs +++ b/embassy-stm32/build.rs @@ -1602,13 +1602,13 @@ fn main() { .into(); if chip_name.starts_with("stm32u5") { - signals.insert(("adc", "ADC4"), quote!(crate::adc::RxDma4)); + signals.insert(("adc", "ADC4"), quote!(crate::adc::RxDma)); } else { signals.insert(("adc", "ADC4"), quote!(crate::adc::RxDma)); } if chip_name.starts_with("stm32wba") { - signals.insert(("adc", "ADC4"), quote!(crate::adc::RxDma4)); + signals.insert(("adc", "ADC4"), quote!(crate::adc::RxDma)); } if chip_name.starts_with("stm32g4") { diff --git a/embassy-stm32/src/adc/adc4.rs b/embassy-stm32/src/adc/adc4.rs index 04d976513..52678d1b6 100644 --- a/embassy-stm32/src/adc/adc4.rs +++ b/embassy-stm32/src/adc/adc4.rs @@ -4,8 +4,8 @@ use pac::adc::vals::{Adc4Dmacfg as Dmacfg, Adc4Exten as Exten, Adc4OversamplingR #[cfg(stm32wba)] use pac::adc::vals::{Chselrmod, Cont, Dmacfg, Exten, OversamplingRatio, Ovss, Smpsel}; -use super::{AdcChannel, AnyAdcChannel, RxDma4, blocking_delay_us}; -use crate::dma::Transfer; +use super::blocking_delay_us; +use crate::adc::ConversionMode; #[cfg(stm32u5)] pub use crate::pac::adc::regs::Adc4Chselrmod0 as Chselr; #[cfg(stm32wba)] @@ -153,6 +153,121 @@ pub trait Instance: SealedInstance + crate::PeripheralType + crate::rcc::RccPeri type Interrupt: crate::interrupt::typelevel::Interrupt; } +foreach_adc!( + (ADC4, $common_inst:ident, $clock:ident) => { + use crate::peripherals::ADC4; + + impl super::BasicAnyInstance for ADC4 { + type SampleTime = SampleTime; + } + + impl super::SealedAnyInstance for ADC4 { + fn dr() -> *mut u16 { + ADC4::regs().dr().as_ptr() as *mut u16 + } + + fn enable() { + ADC4::regs().isr().write(|w| w.set_adrdy(true)); + ADC4::regs().cr().modify(|w| w.set_aden(true)); + while !ADC4::regs().isr().read().adrdy() {} + ADC4::regs().isr().write(|w| w.set_adrdy(true)); + } + + fn start() { + // Start conversion + ADC4::regs().cr().modify(|reg| { + reg.set_adstart(true); + }); + } + + fn stop() { + if ADC4::regs().cr().read().adstart() && !ADC4::regs().cr().read().addis() { + ADC4::regs().cr().modify(|reg| { + reg.set_adstp(true); + }); + while ADC4::regs().cr().read().adstart() {} + } + + // Reset configuration. + ADC4::regs().cfgr1().modify(|reg| { + reg.set_dmaen(false); + }); + } + + fn configure_dma(conversion_mode: ConversionMode) { + match conversion_mode { + ConversionMode::Singular => { + ADC4::regs().isr().modify(|reg| { + reg.set_ovr(true); + reg.set_eos(true); + reg.set_eoc(true); + }); + + ADC4::regs().cfgr1().modify(|reg| { + reg.set_dmaen(true); + reg.set_dmacfg(Dmacfg::ONE_SHOT); + #[cfg(stm32u5)] + reg.set_chselrmod(false); + #[cfg(stm32wba)] + reg.set_chselrmod(Chselrmod::ENABLE_INPUT) + }); + } + _ => unreachable!(), + } + } + + fn configure_sequence(sequence: impl ExactSizeIterator) { + let mut prev_channel: i16 = -1; + #[cfg(stm32wba)] + ADC4::regs().chselr().write_value(Chselr(0_u32)); + #[cfg(stm32u5)] + ADC4::regs().chselrmod0().write_value(Chselr(0_u32)); + for (_i, ((channel, _), sample_time)) in sequence.enumerate() { + ADC4::regs().smpr().modify(|w| { + w.set_smp(_i, sample_time); + }); + + let channel_num = channel; + if channel_num as i16 <= prev_channel { + return; + }; + prev_channel = channel_num as i16; + + #[cfg(stm32wba)] + ADC4::regs().chselr().modify(|w| { + w.set_chsel0(channel as usize, true); + }); + #[cfg(stm32u5)] + ADC4::regs().chselrmod0().modify(|w| { + w.set_chsel(channel as usize, true); + }); + } + } + + fn convert() -> u16 { + // Reset interrupts + ADC4::regs().isr().modify(|reg| { + reg.set_eos(true); + reg.set_eoc(true); + }); + + // Start conversion + ADC4::regs().cr().modify(|reg| { + reg.set_adstart(true); + }); + + while !ADC4::regs().isr().read().eos() { + // spin + } + + ADC4::regs().dr().read().0 as u16 + } + } + + impl super::AnyInstance for ADC4 {} + }; +); + pub struct Adc4<'d, T: Instance> { #[allow(unused)] adc: crate::Peri<'d, T>, @@ -164,9 +279,9 @@ pub enum Adc4Error { DMAError, } -impl<'d, T: Instance> Adc4<'d, T> { +impl<'d, T: Instance + super::AnyInstance> super::Adc<'d, T> { /// Create a new ADC driver. - pub fn new(adc: Peri<'d, T>) -> Self { + pub fn new_adc4(adc: Peri<'d, T>) -> Self { rcc::enable_and_reset::(); let prescaler = Prescaler::from_ker_ck(T::frequency()); @@ -200,7 +315,7 @@ impl<'d, T: Instance> Adc4<'d, T> { blocking_delay_us(1); - Self::enable(); + T::enable(); // single conversion mode, software trigger T::regs().cfgr1().modify(|w| { @@ -231,15 +346,8 @@ impl<'d, T: Instance> Adc4<'d, T> { Self { adc } } - fn enable() { - T::regs().isr().write(|w| w.set_adrdy(true)); - T::regs().cr().modify(|w| w.set_aden(true)); - while !T::regs().isr().read().adrdy() {} - T::regs().isr().write(|w| w.set_adrdy(true)); - } - /// Enable reading the voltage reference internal channel. - pub fn enable_vrefint(&self) -> super::VrefInt { + pub fn enable_vrefint_adc4(&self) -> super::VrefInt { T::regs().ccr().modify(|w| { w.set_vrefen(true); }); @@ -248,7 +356,7 @@ impl<'d, T: Instance> Adc4<'d, T> { } /// Enable reading the temperature internal channel. - pub fn enable_temperature(&self) -> super::Temperature { + pub fn enable_temperature_adc4(&self) -> super::Temperature { T::regs().ccr().modify(|w| { w.set_vsensesel(true); }); @@ -258,7 +366,7 @@ impl<'d, T: Instance> Adc4<'d, T> { /// Enable reading the vbat internal channel. #[cfg(stm32u5)] - pub fn enable_vbat(&self) -> super::Vbat { + pub fn enable_vbat_adc4(&self) -> super::Vbat { T::regs().ccr().modify(|w| { w.set_vbaten(true); }); @@ -267,13 +375,13 @@ impl<'d, T: Instance> Adc4<'d, T> { } /// Enable reading the vbat internal channel. - pub fn enable_vcore(&self) -> super::Vcore { + pub fn enable_vcore_adc4(&self) -> super::Vcore { super::Vcore {} } /// Enable reading the vbat internal channel. #[cfg(stm32u5)] - pub fn enable_dac_channel(&self, dac: DacChannel) -> super::Dac { + pub fn enable_dac_channel_adc4(&self, dac: DacChannel) -> super::Dac { let mux; match dac { DacChannel::OUT1 => mux = false, @@ -284,13 +392,13 @@ impl<'d, T: Instance> Adc4<'d, T> { } /// Set the ADC resolution. - pub fn set_resolution(&mut self, resolution: Resolution) { + pub fn set_resolution_adc4(&mut self, resolution: Resolution) { T::regs().cfgr1().modify(|w| w.set_res(resolution.into())); } /// Set hardware averaging. #[cfg(stm32u5)] - pub fn set_averaging(&mut self, averaging: Averaging) { + pub fn set_averaging_adc4(&mut self, averaging: Averaging) { let (enable, samples, right_shift) = match averaging { Averaging::Disabled => (false, OversamplingRatio::OVERSAMPLE2X, 0), Averaging::Samples2 => (true, OversamplingRatio::OVERSAMPLE2X, 1), @@ -310,7 +418,7 @@ impl<'d, T: Instance> Adc4<'d, T> { }) } #[cfg(stm32wba)] - pub fn set_averaging(&mut self, averaging: Averaging) { + pub fn set_averaging_adc4(&mut self, averaging: Averaging) { let (enable, samples, right_shift) = match averaging { Averaging::Disabled => (false, OversamplingRatio::OVERSAMPLE2X, Ovss::SHIFT0), Averaging::Samples2 => (true, OversamplingRatio::OVERSAMPLE2X, Ovss::SHIFT1), @@ -329,168 +437,4 @@ impl<'d, T: Instance> Adc4<'d, T> { w.set_ovse(enable) }) } - - /// Read an ADC channel. - pub fn blocking_read(&mut self, channel: &mut impl AdcChannel, sample_time: SampleTime) -> u16 { - T::regs().smpr().modify(|w| { - w.set_smp(0, sample_time); - }); - - channel.setup(); - - // Select channel - #[cfg(stm32wba)] - { - T::regs().chselr().write_value(Chselr(0_u32)); - T::regs().chselr().modify(|w| { - w.set_chsel0(channel.channel() as usize, true); - }); - } - #[cfg(stm32u5)] - { - T::regs().chselrmod0().write_value(Chselr(0_u32)); - T::regs().chselrmod0().modify(|w| { - w.set_chsel(channel.channel() as usize, true); - }); - } - - // Reset interrupts - T::regs().isr().modify(|reg| { - reg.set_eos(true); - reg.set_eoc(true); - }); - - // Start conversion - T::regs().cr().modify(|reg| { - reg.set_adstart(true); - }); - - while !T::regs().isr().read().eos() { - // spin - } - - T::regs().dr().read().0 as u16 - } - - /// Read one or multiple ADC channels using DMA. - /// - /// `sequence` iterator and `readings` must have the same length. - /// The channels in `sequence` must be in ascending order. - /// - /// Example - /// ```rust,ignore - /// use embassy_stm32::adc::adc4; - /// use embassy_stm32::adc::AdcChannel; - /// - /// let mut adc4 = adc4::Adc4::new(p.ADC4); - /// let mut adc4_pin1 = p.PC1; - /// let mut adc4_pin2 = p.PC0; - /// let mut.into()d41 = adc4_pin1.into(); - /// let mut.into()d42 = adc4_pin2.into(); - /// let mut measurements = [0u16; 2]; - /// // not that the channels must be in ascending order - /// adc4.read( - /// &mut p.GPDMA1_CH1, - /// [ - /// &mut.into()d42, - /// &mut.into()d41, - /// ] - /// .into_iter(), - /// &mut measurements, - /// ).await.unwrap(); - /// ``` - pub async fn read( - &mut self, - rx_dma: Peri<'_, impl RxDma4>, - sequence: impl ExactSizeIterator>, - readings: &mut [u16], - ) -> Result<(), Adc4Error> { - assert!(sequence.len() != 0, "Asynchronous read sequence cannot be empty"); - assert!( - sequence.len() == readings.len(), - "Sequence length must be equal to readings length" - ); - - // Ensure no conversions are ongoing - Self::cancel_conversions(); - - T::regs().isr().modify(|reg| { - reg.set_ovr(true); - reg.set_eos(true); - reg.set_eoc(true); - }); - - T::regs().cfgr1().modify(|reg| { - reg.set_dmaen(true); - reg.set_dmacfg(Dmacfg::ONE_SHOT); - #[cfg(stm32u5)] - reg.set_chselrmod(false); - #[cfg(stm32wba)] - reg.set_chselrmod(Chselrmod::ENABLE_INPUT) - }); - - // Verify and activate sequence - let mut prev_channel: i16 = -1; - #[cfg(stm32wba)] - T::regs().chselr().write_value(Chselr(0_u32)); - #[cfg(stm32u5)] - T::regs().chselrmod0().write_value(Chselr(0_u32)); - for channel in sequence { - let channel_num = channel.channel; - if channel_num as i16 <= prev_channel { - return Err(Adc4Error::InvalidSequence); - }; - prev_channel = channel_num as i16; - - #[cfg(stm32wba)] - T::regs().chselr().modify(|w| { - w.set_chsel0(channel.channel as usize, true); - }); - #[cfg(stm32u5)] - T::regs().chselrmod0().modify(|w| { - w.set_chsel(channel.channel as usize, true); - }); - } - - let request = rx_dma.request(); - let transfer = unsafe { - Transfer::new_read( - rx_dma, - request, - T::regs().dr().as_ptr() as *mut u16, - readings, - Default::default(), - ) - }; - - // Start conversion - T::regs().cr().modify(|reg| { - reg.set_adstart(true); - }); - - transfer.await; - - // Ensure conversions are finished. - Self::cancel_conversions(); - - // Reset configuration. - T::regs().cfgr1().modify(|reg| { - reg.set_dmaen(false); - }); - - if T::regs().isr().read().ovr() { - Err(Adc4Error::DMAError) - } else { - Ok(()) - } - } - - fn cancel_conversions() { - if T::regs().cr().read().adstart() && !T::regs().cr().read().addis() { - T::regs().cr().modify(|reg| { - reg.set_adstp(true); - }); - while T::regs().cr().read().adstart() {} - } - } } diff --git a/embassy-stm32/src/adc/g4.rs b/embassy-stm32/src/adc/g4.rs index 0a9f35a5b..71dc8acc0 100644 --- a/embassy-stm32/src/adc/g4.rs +++ b/embassy-stm32/src/adc/g4.rs @@ -12,7 +12,7 @@ use super::{ Adc, AnyAdcChannel, ConversionMode, Instance, RegularConversionMode, Resolution, RxDma, SampleTime, blocking_delay_us, }; -use crate::adc::SealedAdcChannel; +use crate::adc::{AnyInstance, SealedAdcChannel}; use crate::time::Hertz; use crate::{Peri, pac, rcc}; @@ -122,98 +122,12 @@ pub struct ConversionTrigger { pub edge: Exten, } -impl<'d, T: Instance> Adc<'d, T> { - /// Create a new ADC driver. - pub fn new(adc: Peri<'d, T>, config: AdcConfig) -> Self { - rcc::enable_and_reset::(); - - let prescaler = Prescaler::from_ker_ck(T::frequency()); - - T::common_regs().ccr().modify(|w| w.set_presc(prescaler.presc())); - - let frequency = Hertz(T::frequency().0 / prescaler.divisor()); - trace!("ADC frequency set to {}", frequency); - - if frequency > MAX_ADC_CLK_FREQ { - panic!( - "Maximal allowed frequency for the ADC is {} MHz and it varies with different packages, refer to ST docs for more information.", - MAX_ADC_CLK_FREQ.0 / 1_000_000 - ); - } - - T::regs().cr().modify(|reg| { - reg.set_deeppwd(false); - reg.set_advregen(true); - }); - - blocking_delay_us(20); - - T::regs().difsel().modify(|w| { - for n in 0..18 { - w.set_difsel(n, Difsel::SINGLE_ENDED); - } - }); - - T::regs().cr().modify(|w| { - w.set_adcaldif(Adcaldif::SINGLE_ENDED); - }); - - T::regs().cr().modify(|w| w.set_adcal(true)); - - while T::regs().cr().read().adcal() {} - - blocking_delay_us(20); - - T::regs().cr().modify(|w| { - w.set_adcaldif(Adcaldif::DIFFERENTIAL); - }); - - T::regs().cr().modify(|w| w.set_adcal(true)); - - while T::regs().cr().read().adcal() {} - - blocking_delay_us(20); - - Self::enable(); - - // single conversion mode, software trigger - T::regs().cfgr().modify(|w| { - w.set_cont(false); - w.set_exten(Exten::DISABLED); - }); - - if let Some(dual) = config.dual_mode { - T::common_regs().ccr().modify(|reg| { - reg.set_dual(dual); - }) - } - - if let Some(resolution) = config.resolution { - T::regs().cfgr().modify(|reg| reg.set_res(resolution.into())); - } - - #[cfg(stm32g4)] - if let Some(shift) = config.oversampling_shift { - T::regs().cfgr2().modify(|reg| reg.set_ovss(shift)); - } - - #[cfg(stm32g4)] - if let Some(ratio) = config.oversampling_ratio { - T::regs().cfgr2().modify(|reg| reg.set_ovsr(ratio)); - } - - #[cfg(stm32g4)] - if let Some((mode, trig_mode, enable)) = config.oversampling_mode { - T::regs().cfgr2().modify(|reg| reg.set_trovs(trig_mode)); - T::regs().cfgr2().modify(|reg| reg.set_rovsm(mode)); - T::regs().cfgr2().modify(|reg| reg.set_rovse(enable)); - } - - Self { adc } +impl super::SealedAnyInstance for T { + fn dr() -> *mut u16 { + T::regs().dr().as_ptr() as *mut u16 } - /// Enable the ADC - pub(super) fn enable() { + fn enable() { // Make sure bits are off while T::regs().cr().read().addis() { // spin @@ -234,15 +148,13 @@ impl<'d, T: Instance> Adc<'d, T> { } } - /// Start regular adc conversion - pub(super) fn start() { + fn start() { T::regs().cr().modify(|reg| { reg.set_adstart(true); }); } - /// Stop regular conversions and disable DMA - pub(super) fn stop() { + fn stop() { if T::regs().cr().read().adstart() && !T::regs().cr().read().addis() { T::regs().cr().modify(|reg| { reg.set_adstp(Adstp::STOP); @@ -259,8 +171,7 @@ impl<'d, T: Instance> Adc<'d, T> { }); } - /// Perform a single conversion. - pub(super) fn convert() -> u16 { + fn convert() -> u16 { T::regs().isr().modify(|reg| { reg.set_eos(true); reg.set_eoc(true); @@ -278,7 +189,7 @@ impl<'d, T: Instance> Adc<'d, T> { T::regs().dr().read().0 as u16 } - pub(super) fn configure_dma(conversion_mode: ConversionMode) { + fn configure_dma(conversion_mode: ConversionMode) { T::regs().isr().modify(|reg| { reg.set_ovr(true); }); @@ -316,7 +227,7 @@ impl<'d, T: Instance> Adc<'d, T> { } } - pub(super) fn configure_sequence(sequence: impl ExactSizeIterator) { + fn configure_sequence(sequence: impl ExactSizeIterator) { // Set sequence length T::regs().sqr1().modify(|w| { w.set_l(sequence.len() as u8 - 1); @@ -374,6 +285,97 @@ impl<'d, T: Instance> Adc<'d, T> { } } } +} + +impl<'d, T: Instance + AnyInstance> Adc<'d, T> { + /// Create a new ADC driver. + pub fn new(adc: Peri<'d, T>, config: AdcConfig) -> Self { + rcc::enable_and_reset::(); + + let prescaler = Prescaler::from_ker_ck(T::frequency()); + + T::common_regs().ccr().modify(|w| w.set_presc(prescaler.presc())); + + let frequency = Hertz(T::frequency().0 / prescaler.divisor()); + trace!("ADC frequency set to {}", frequency); + + if frequency > MAX_ADC_CLK_FREQ { + panic!( + "Maximal allowed frequency for the ADC is {} MHz and it varies with different packages, refer to ST docs for more information.", + MAX_ADC_CLK_FREQ.0 / 1_000_000 + ); + } + + T::regs().cr().modify(|reg| { + reg.set_deeppwd(false); + reg.set_advregen(true); + }); + + blocking_delay_us(20); + + T::regs().difsel().modify(|w| { + for n in 0..18 { + w.set_difsel(n, Difsel::SINGLE_ENDED); + } + }); + + T::regs().cr().modify(|w| { + w.set_adcaldif(Adcaldif::SINGLE_ENDED); + }); + + T::regs().cr().modify(|w| w.set_adcal(true)); + + while T::regs().cr().read().adcal() {} + + blocking_delay_us(20); + + T::regs().cr().modify(|w| { + w.set_adcaldif(Adcaldif::DIFFERENTIAL); + }); + + T::regs().cr().modify(|w| w.set_adcal(true)); + + while T::regs().cr().read().adcal() {} + + blocking_delay_us(20); + + T::enable(); + + // single conversion mode, software trigger + T::regs().cfgr().modify(|w| { + w.set_cont(false); + w.set_exten(Exten::DISABLED); + }); + + if let Some(dual) = config.dual_mode { + T::common_regs().ccr().modify(|reg| { + reg.set_dual(dual); + }) + } + + if let Some(resolution) = config.resolution { + T::regs().cfgr().modify(|reg| reg.set_res(resolution.into())); + } + + #[cfg(stm32g4)] + if let Some(shift) = config.oversampling_shift { + T::regs().cfgr2().modify(|reg| reg.set_ovss(shift)); + } + + #[cfg(stm32g4)] + if let Some(ratio) = config.oversampling_ratio { + T::regs().cfgr2().modify(|reg| reg.set_ovsr(ratio)); + } + + #[cfg(stm32g4)] + if let Some((mode, trig_mode, enable)) = config.oversampling_mode { + T::regs().cfgr2().modify(|reg| reg.set_trovs(trig_mode)); + T::regs().cfgr2().modify(|reg| reg.set_rovsm(mode)); + T::regs().cfgr2().modify(|reg| reg.set_rovse(enable)); + } + + Self { adc } + } /// Enable reading the voltage reference internal channel. pub fn enable_vrefint(&self) -> super::VrefInt @@ -464,8 +466,8 @@ impl<'d, T: Instance> Adc<'d, T> { NR_INJECTED_RANKS ); - Self::stop(); - Self::enable(); + T::stop(); + T::enable(); T::regs().jsqr().modify(|w| w.set_jl(N as u8 - 1)); @@ -536,7 +538,7 @@ impl<'d, T: Instance> Adc<'d, T> { self, dma: Peri<'a, impl RxDma>, dma_buf: &'a mut [u16], - regular_sequence: impl ExactSizeIterator, SampleTime)>, + regular_sequence: impl ExactSizeIterator, T::SampleTime)>, regular_conversion_mode: RegularConversionMode, injected_sequence: [(AnyAdcChannel, SampleTime); N], injected_trigger: ConversionTrigger, diff --git a/embassy-stm32/src/adc/injected.rs b/embassy-stm32/src/adc/injected.rs index 7bb3a541c..ccaa5d1b2 100644 --- a/embassy-stm32/src/adc/injected.rs +++ b/embassy-stm32/src/adc/injected.rs @@ -5,9 +5,9 @@ use core::sync::atomic::{Ordering, compiler_fence}; use embassy_hal_internal::Peri; use super::{AnyAdcChannel, SampleTime}; -use crate::adc::Adc; #[allow(unused_imports)] use crate::adc::Instance; +use crate::adc::{Adc, AnyInstance}; /// Injected ADC sequence with owned channels. pub struct InjectedAdc { @@ -36,9 +36,9 @@ impl InjectedAdc { } } -impl Drop for InjectedAdc { +impl Drop for InjectedAdc { fn drop(&mut self) { - Adc::::stop(); + T::stop(); compiler_fence(Ordering::SeqCst); } } diff --git a/embassy-stm32/src/adc/mod.rs b/embassy-stm32/src/adc/mod.rs index bf404d6ef..856c2e61e 100644 --- a/embassy-stm32/src/adc/mod.rs +++ b/embassy-stm32/src/adc/mod.rs @@ -41,15 +41,10 @@ pub use crate::pac::adc::vals::Res as Resolution; pub use crate::pac::adc::vals::SampleTime; use crate::peripherals; -#[cfg(not(adc_wba))] -dma_trait!(RxDma, Instance); -#[cfg(adc_u5)] -dma_trait!(RxDma4, adc4::Instance); -#[cfg(adc_wba)] -dma_trait!(RxDma4, adc4::Instance); +dma_trait!(RxDma, AnyInstance); /// Analog to Digital driver. -pub struct Adc<'d, T: Instance> { +pub struct Adc<'d, T: AnyInstance> { #[allow(unused)] adc: crate::Peri<'d, T>, } @@ -92,6 +87,49 @@ pub(crate) trait SealedAdcChannel { } } +// Temporary patch for ADCs that have not implemented the standard iface yet +#[cfg(not(any(adc_v2, adc_v3, adc_g0, adc_h5, adc_h7rs, adc_u0, adc_v4, adc_u5, adc_wba, adc_g4)))] +trait_set::trait_set! { + pub trait AnyInstance = Instance; +} + +#[cfg(any(adc_v2, adc_v3, adc_g0, adc_h5, adc_h7rs, adc_u0, adc_v4, adc_u5, adc_wba, adc_g4))] +#[allow(dead_code)] +pub trait BasicAnyInstance { + type SampleTime; +} + +#[cfg(any(adc_v2, adc_v3, adc_g0, adc_h5, adc_h7rs, adc_u0, adc_v4, adc_u5, adc_wba, adc_g4))] +#[allow(dead_code)] +pub(self) trait SealedAnyInstance: BasicAnyInstance { + fn enable(); + fn start(); + fn stop(); + fn convert() -> u16; + fn configure_dma(conversion_mode: ConversionMode); + fn configure_sequence(sequence: impl ExactSizeIterator); + fn dr() -> *mut u16; +} + +// On chips without ADC4, AnyInstance is an Instance +#[cfg(any(adc_v2, adc_v3, adc_g0, adc_h5, adc_h7rs, adc_u0, adc_g4))] +#[allow(private_bounds)] +pub trait AnyInstance: SealedAnyInstance + Instance {} + +// On chips with ADC4, AnyInstance is an Instance or adc4::Instance +#[cfg(any(adc_v4, adc_u5, adc_wba))] +#[allow(private_bounds)] +pub trait AnyInstance: SealedAnyInstance + crate::PeripheralType + crate::rcc::RccPeripheral {} + +// Implement AnyInstance automatically for SealedAnyInstance +#[cfg(any(adc_v2, adc_v3, adc_g0, adc_h5, adc_h7rs, adc_u0, adc_v4, adc_u5, adc_wba, adc_g4))] +impl BasicAnyInstance for T { + type SampleTime = SampleTime; +} + +#[cfg(any(adc_v2, adc_v3, adc_g0, adc_h5, adc_h7rs, adc_u0, adc_v4, adc_u5, adc_wba, adc_g4))] +impl AnyInstance for T {} + /// Performs a busy-wait delay for a specified number of microseconds. #[allow(unused)] pub(crate) fn blocking_delay_us(us: u32) { @@ -110,16 +148,18 @@ pub(crate) fn blocking_delay_us(us: u32) { } } -#[cfg(any(adc_v2, adc_g4, adc_v3, adc_g0, adc_h5, adc_h7rs, adc_u0, adc_v4, adc_u5))] -pub(self) enum ConversionMode { - #[cfg(any(adc_g4, adc_v3, adc_g0, adc_h5, adc_h7rs, adc_u0, adc_v4, adc_u5))] +#[cfg(any(adc_v2, adc_g4, adc_v3, adc_g0, adc_h5, adc_h7rs, adc_u0, adc_v4, adc_u5, adc_wba))] +#[allow(dead_code)] +pub(crate) enum ConversionMode { + #[cfg(any(adc_g4, adc_v3, adc_g0, adc_h5, adc_h7rs, adc_u0, adc_v4, adc_u5, adc_wba))] Singular, #[allow(dead_code)] Repeated(RegularConversionMode), } -#[cfg(any(adc_v2, adc_g4, adc_v3, adc_g0, adc_h5, adc_h7rs, adc_u0, adc_v4, adc_u5))] +#[cfg(any(adc_v2, adc_g4, adc_v3, adc_g0, adc_h5, adc_h7rs, adc_u0, adc_v4, adc_u5, adc_wba))] // Conversion mode for regular ADC channels +#[allow(dead_code)] #[derive(Copy, Clone)] pub enum RegularConversionMode { // Samples as fast as possible @@ -129,21 +169,21 @@ pub enum RegularConversionMode { Triggered(ConversionTrigger), } -impl<'d, T: Instance> Adc<'d, T> { - #[cfg(any(adc_v2, adc_g4, adc_v3, adc_g0, adc_h5, adc_h7rs, adc_u0, adc_u5, adc_v4))] +impl<'d, T: AnyInstance> Adc<'d, T> { + #[cfg(any(adc_v2, adc_g4, adc_v3, adc_g0, adc_h5, adc_h7rs, adc_u0, adc_u5, adc_v4, adc_wba))] /// Read an ADC pin. - pub fn blocking_read(&mut self, channel: &mut impl AdcChannel, sample_time: SampleTime) -> u16 { + pub fn blocking_read(&mut self, channel: &mut impl AdcChannel, sample_time: T::SampleTime) -> u16 { #[cfg(any(adc_v1, adc_c0, adc_l0, adc_v2, adc_g4, adc_v4, adc_u5, adc_wba))] channel.setup(); #[cfg(not(adc_v4))] - Self::enable(); - Self::configure_sequence([((channel.channel(), channel.is_differential()), sample_time)].into_iter()); + T::enable(); + T::configure_sequence([((channel.channel(), channel.is_differential()), sample_time)].into_iter()); - Self::convert() + T::convert() } - #[cfg(any(adc_g4, adc_v3, adc_g0, adc_h5, adc_h7rs, adc_u0, adc_v4, adc_u5))] + #[cfg(any(adc_g4, adc_v3, adc_g0, adc_h5, adc_h7rs, adc_u0, adc_v4, adc_u5, adc_wba))] /// Read one or multiple ADC regular channels using DMA. /// /// `sequence` iterator and `readings` must have the same length. @@ -175,7 +215,7 @@ impl<'d, T: Instance> Adc<'d, T> { pub async fn read( &mut self, rx_dma: embassy_hal_internal::Peri<'_, impl RxDma>, - sequence: impl ExactSizeIterator, SampleTime)>, + sequence: impl ExactSizeIterator, T::SampleTime)>, readings: &mut [u16], ) { assert!(sequence.len() != 0, "Asynchronous read sequence cannot be empty"); @@ -189,33 +229,26 @@ impl<'d, T: Instance> Adc<'d, T> { ); // Ensure no conversions are ongoing and ADC is enabled. - Self::stop(); - Self::enable(); + T::stop(); + T::enable(); - Self::configure_sequence( + T::configure_sequence( sequence.map(|(channel, sample_time)| ((channel.channel, channel.is_differential), sample_time)), ); - Self::configure_dma(ConversionMode::Singular); + T::configure_dma(ConversionMode::Singular); let request = rx_dma.request(); - let transfer = unsafe { - crate::dma::Transfer::new_read( - rx_dma, - request, - T::regs().dr().as_ptr() as *mut u16, - readings, - Default::default(), - ) - }; + let transfer = + unsafe { crate::dma::Transfer::new_read(rx_dma, request, T::dr(), readings, Default::default()) }; - Self::start(); + T::start(); // Wait for conversion sequence to finish. transfer.await; // Ensure conversions are finished. - Self::stop(); + T::stop(); } #[cfg(any(adc_v2, adc_g4, adc_v3, adc_g0, adc_u0))] @@ -244,7 +277,7 @@ impl<'d, T: Instance> Adc<'d, T> { self, dma: embassy_hal_internal::Peri<'a, impl RxDma>, dma_buf: &'a mut [u16], - sequence: impl ExactSizeIterator, SampleTime)>, + sequence: impl ExactSizeIterator, T::SampleTime)>, mode: RegularConversionMode, ) -> RingBufferedAdc<'a, T> { assert!(!dma_buf.is_empty() && dma_buf.len() <= 0xFFFF); @@ -254,15 +287,15 @@ impl<'d, T: Instance> Adc<'d, T> { "Asynchronous read sequence cannot be more than 16 in length" ); // reset conversions and enable the adc - Self::stop(); - Self::enable(); + T::stop(); + T::enable(); //adc side setup - Self::configure_sequence( + T::configure_sequence( sequence.map(|(channel, sample_time)| ((channel.channel, channel.is_differential), sample_time)), ); - Self::configure_dma(ConversionMode::Repeated(mode)); + T::configure_dma(ConversionMode::Repeated(mode)); core::mem::forget(self); diff --git a/embassy-stm32/src/adc/ringbuffered.rs b/embassy-stm32/src/adc/ringbuffered.rs index 62ea0d3a2..a56f8ca0b 100644 --- a/embassy-stm32/src/adc/ringbuffered.rs +++ b/embassy-stm32/src/adc/ringbuffered.rs @@ -4,7 +4,7 @@ use core::sync::atomic::{Ordering, compiler_fence}; #[allow(unused_imports)] use embassy_hal_internal::Peri; -use crate::adc::Adc; +use crate::adc::AnyInstance; #[allow(unused_imports)] use crate::adc::{Instance, RxDma}; #[allow(unused_imports)] @@ -19,7 +19,7 @@ pub struct RingBufferedAdc<'d, T: Instance> { ring_buf: ReadableRingBuffer<'d, u16>, } -impl<'d, T: Instance> RingBufferedAdc<'d, T> { +impl<'d, T: Instance + AnyInstance> RingBufferedAdc<'d, T> { pub(crate) fn new(dma: Peri<'d, impl RxDma>, dma_buf: &'d mut [u16]) -> Self { //dma side setup let opts = TransferOptions { @@ -45,11 +45,11 @@ impl<'d, T: Instance> RingBufferedAdc<'d, T> { compiler_fence(Ordering::SeqCst); self.ring_buf.start(); - Adc::::start(); + T::start(); } pub fn stop(&mut self) { - Adc::::stop(); + T::stop(); self.ring_buf.request_pause(); @@ -170,9 +170,9 @@ impl<'d, T: Instance> RingBufferedAdc<'d, T> { } } -impl Drop for RingBufferedAdc<'_, T> { +impl Drop for RingBufferedAdc<'_, T> { fn drop(&mut self) { - Adc::::stop(); + T::stop(); compiler_fence(Ordering::SeqCst); diff --git a/embassy-stm32/src/adc/v2.rs b/embassy-stm32/src/adc/v2.rs index 2f9fabafb..4065f89a7 100644 --- a/embassy-stm32/src/adc/v2.rs +++ b/embassy-stm32/src/adc/v2.rs @@ -91,35 +91,14 @@ pub struct AdcConfig { resolution: Option, } -impl<'d, T> Adc<'d, T> -where - T: Instance, -{ - pub fn new(adc: Peri<'d, T>) -> Self { - Self::new_with_config(adc, Default::default()) - } - - pub fn new_with_config(adc: Peri<'d, T>, config: AdcConfig) -> Self { - rcc::enable_and_reset::(); - - let presc = Prescaler::from_pclk2(T::frequency()); - T::common_regs().ccr().modify(|w| w.set_adcpre(presc.adcpre())); - T::regs().cr2().modify(|reg| { - reg.set_adon(true); - }); - - blocking_delay_us(3); - - if let Some(resolution) = config.resolution { - T::regs().cr1().modify(|reg| reg.set_res(resolution.into())); - } - - Self { adc } +impl super::SealedAnyInstance for T { + fn dr() -> *mut u16 { + T::regs().dr().as_ptr() as *mut u16 } - pub(super) fn enable() {} + fn enable() {} - pub(super) fn start() { + fn start() { // Begin ADC conversions T::regs().cr2().modify(|reg| { reg.set_adon(true); @@ -127,7 +106,7 @@ where }); } - pub(super) fn stop() { + fn stop() { let r = T::regs(); // Stop ADC @@ -152,7 +131,7 @@ where compiler_fence(Ordering::SeqCst); } - pub(super) fn convert() -> u16 { + fn convert() -> u16 { // clear end of conversion flag T::regs().sr().modify(|reg| { reg.set_eoc(false); @@ -173,7 +152,7 @@ where T::regs().dr().read().0 as u16 } - pub(super) fn configure_dma(conversion_mode: ConversionMode) { + fn configure_dma(conversion_mode: ConversionMode) { match conversion_mode { ConversionMode::Repeated(_) => { let r = T::regs(); @@ -210,7 +189,7 @@ where } } - pub(super) fn configure_sequence(sequence: impl ExactSizeIterator) { + fn configure_sequence(sequence: impl ExactSizeIterator) { T::regs().cr2().modify(|reg| { reg.set_adon(true); }); @@ -232,6 +211,33 @@ where } } } +} + +impl<'d, T> Adc<'d, T> +where + T: Instance, +{ + pub fn new(adc: Peri<'d, T>) -> Self { + Self::new_with_config(adc, Default::default()) + } + + pub fn new_with_config(adc: Peri<'d, T>, config: AdcConfig) -> Self { + rcc::enable_and_reset::(); + + let presc = Prescaler::from_pclk2(T::frequency()); + T::common_regs().ccr().modify(|w| w.set_adcpre(presc.adcpre())); + T::regs().cr2().modify(|reg| { + reg.set_adon(true); + }); + + blocking_delay_us(3); + + if let Some(resolution) = config.resolution { + T::regs().cr1().modify(|reg| reg.set_res(resolution.into())); + } + + Self { adc } + } /// Enables internal voltage reference and returns [VrefInt], which can be used in /// [Adc::read_internal()] to perform conversion. diff --git a/embassy-stm32/src/adc/v3.rs b/embassy-stm32/src/adc/v3.rs index 62b5043ee..4cce1dac3 100644 --- a/embassy-stm32/src/adc/v3.rs +++ b/embassy-stm32/src/adc/v3.rs @@ -161,152 +161,13 @@ pub struct AdcConfig { pub averaging: Option, } -impl<'d, T: Instance> Adc<'d, T> { - /// Enable the voltage regulator - fn init_regulator() { - rcc::enable_and_reset::(); - T::regs().cr().modify(|reg| { - #[cfg(not(any(adc_g0, adc_u0)))] - reg.set_deeppwd(false); - reg.set_advregen(true); - }); - - // If this is false then each ADC_CHSELR bit enables an input channel. - // This is the reset value, so has no effect. - #[cfg(any(adc_g0, adc_u0))] - T::regs().cfgr1().modify(|reg| { - reg.set_chselrmod(false); - }); - - blocking_delay_us(20); - } - - /// Calibrate to remove conversion offset - fn init_calibrate() { - T::regs().cr().modify(|reg| { - reg.set_adcal(true); - }); - - while T::regs().cr().read().adcal() { - // spin - } - - blocking_delay_us(1); - } - - /// Initialize the ADC leaving any analog clock at reset value. - /// For G0 and WL, this is the async clock without prescaler. - pub fn new(adc: Peri<'d, T>) -> Self { - Self::init_regulator(); - Self::init_calibrate(); - Self { adc } - } - - pub fn new_with_config(adc: Peri<'d, T>, config: AdcConfig) -> Self { - #[cfg(not(adc_g0))] - let s = Self::new(adc); - - #[cfg(adc_g0)] - let s = match config.clock { - Some(clock) => Self::new_with_clock(adc, clock), - None => Self::new(adc), - }; - - #[cfg(any(adc_g0, adc_u0, adc_v3))] - if let Some(shift) = config.oversampling_shift { - T::regs().cfgr2().modify(|reg| reg.set_ovss(shift)); - } - - #[cfg(any(adc_g0, adc_u0, adc_v3))] - if let Some(ratio) = config.oversampling_ratio { - T::regs().cfgr2().modify(|reg| reg.set_ovsr(ratio)); - } - - #[cfg(any(adc_g0, adc_u0))] - if let Some(enable) = config.oversampling_enable { - T::regs().cfgr2().modify(|reg| reg.set_ovse(enable)); - } - - #[cfg(adc_v3)] - if let Some((mode, trig_mode, enable)) = config.oversampling_mode { - T::regs().cfgr2().modify(|reg| reg.set_trovs(trig_mode)); - T::regs().cfgr2().modify(|reg| reg.set_rovsm(mode)); - T::regs().cfgr2().modify(|reg| reg.set_rovse(enable)); - } - - if let Some(resolution) = config.resolution { - #[cfg(not(any(adc_g0, adc_u0)))] - T::regs().cfgr().modify(|reg| reg.set_res(resolution.into())); - #[cfg(any(adc_g0, adc_u0))] - T::regs().cfgr1().modify(|reg| reg.set_res(resolution.into())); - } - - if let Some(averaging) = config.averaging { - let (enable, samples, right_shift) = match averaging { - Averaging::Disabled => (false, 0, 0), - Averaging::Samples2 => (true, 0, 1), - Averaging::Samples4 => (true, 1, 2), - Averaging::Samples8 => (true, 2, 3), - Averaging::Samples16 => (true, 3, 4), - Averaging::Samples32 => (true, 4, 5), - Averaging::Samples64 => (true, 5, 6), - Averaging::Samples128 => (true, 6, 7), - Averaging::Samples256 => (true, 7, 8), - }; - T::regs().cfgr2().modify(|reg| { - #[cfg(not(any(adc_g0, adc_u0)))] - reg.set_rovse(enable); - #[cfg(any(adc_g0, adc_u0))] - reg.set_ovse(enable); - #[cfg(any(adc_h5, adc_h7rs))] - reg.set_ovsr(samples.into()); - #[cfg(not(any(adc_h5, adc_h7rs)))] - reg.set_ovsr(samples.into()); - reg.set_ovss(right_shift.into()); - }) - } - - s - } - - #[cfg(adc_g0)] - /// Initialize ADC with explicit clock for the analog ADC - pub fn new_with_clock(adc: Peri<'d, T>, clock: Clock) -> Self { - Self::init_regulator(); - - #[cfg(any(stm32wl5x))] - { - // Reset value 0 is actually _No clock selected_ in the STM32WL5x reference manual - let async_clock_available = pac::RCC.ccipr().read().adcsel() != pac::rcc::vals::Adcsel::_RESERVED_0; - match clock { - Clock::Async { div: _ } => { - assert!(async_clock_available); - } - Clock::Sync { div: _ } => { - if async_clock_available { - warn!("Not using configured ADC clock"); - } - } - } - } - match clock { - Clock::Async { div } => T::regs().ccr().modify(|reg| reg.set_presc(div)), - Clock::Sync { div } => T::regs().cfgr2().modify(|reg| { - reg.set_ckmode(match div { - CkModePclk::DIV1 => Ckmode::PCLK, - CkModePclk::DIV2 => Ckmode::PCLK_DIV2, - CkModePclk::DIV4 => Ckmode::PCLK_DIV4, - }) - }), - } - - Self::init_calibrate(); - - Self { adc } +impl super::SealedAnyInstance for T { + fn dr() -> *mut u16 { + T::regs().dr().as_ptr() as *mut u16 } // Enable ADC only when it is not already running. - pub(super) fn enable() { + fn enable() { // Make sure bits are off while T::regs().cr().read().addis() { // spin @@ -327,7 +188,7 @@ impl<'d, T: Instance> Adc<'d, T> { } } - pub(super) fn start() { + fn start() { #[cfg(any(adc_v3, adc_g0, adc_u0))] { // Start adc conversion @@ -337,7 +198,7 @@ impl<'d, T: Instance> Adc<'d, T> { } } - pub(super) fn stop() { + fn stop() { #[cfg(any(adc_v3, adc_g0, adc_u0))] { // Ensure conversions are finished. @@ -363,7 +224,7 @@ impl<'d, T: Instance> Adc<'d, T> { } /// Perform a single conversion. - pub(super) fn convert() -> u16 { + fn convert() -> u16 { // Some models are affected by an erratum: // If we perform conversions slower than 1 kHz, the first read ADC value can be // corrupted, so we discard it and measure again. @@ -395,7 +256,7 @@ impl<'d, T: Instance> Adc<'d, T> { T::regs().dr().read().0 as u16 } - pub(super) fn configure_dma(conversion_mode: ConversionMode) { + fn configure_dma(conversion_mode: ConversionMode) { // Set continuous mode with oneshot dma. // Clear overrun flag before starting transfer. T::regs().isr().modify(|reg| { @@ -419,7 +280,7 @@ impl<'d, T: Instance> Adc<'d, T> { }); } - pub(super) fn configure_sequence(sequence: impl ExactSizeIterator) { + fn configure_sequence(sequence: impl ExactSizeIterator) { // Set sequence length #[cfg(not(any(adc_g0, adc_u0)))] T::regs().sqr1().modify(|w| { @@ -532,6 +393,151 @@ impl<'d, T: Instance> Adc<'d, T> { }); } } +} + +impl<'d, T: Instance> Adc<'d, T> { + /// Enable the voltage regulator + fn init_regulator() { + rcc::enable_and_reset::(); + T::regs().cr().modify(|reg| { + #[cfg(not(any(adc_g0, adc_u0)))] + reg.set_deeppwd(false); + reg.set_advregen(true); + }); + + // If this is false then each ADC_CHSELR bit enables an input channel. + // This is the reset value, so has no effect. + #[cfg(any(adc_g0, adc_u0))] + T::regs().cfgr1().modify(|reg| { + reg.set_chselrmod(false); + }); + + blocking_delay_us(20); + } + + /// Calibrate to remove conversion offset + fn init_calibrate() { + T::regs().cr().modify(|reg| { + reg.set_adcal(true); + }); + + while T::regs().cr().read().adcal() { + // spin + } + + blocking_delay_us(1); + } + + /// Initialize the ADC leaving any analog clock at reset value. + /// For G0 and WL, this is the async clock without prescaler. + pub fn new(adc: Peri<'d, T>) -> Self { + Self::init_regulator(); + Self::init_calibrate(); + Self { adc } + } + + pub fn new_with_config(adc: Peri<'d, T>, config: AdcConfig) -> Self { + #[cfg(not(adc_g0))] + let s = Self::new(adc); + + #[cfg(adc_g0)] + let s = match config.clock { + Some(clock) => Self::new_with_clock(adc, clock), + None => Self::new(adc), + }; + + #[cfg(any(adc_g0, adc_u0, adc_v3))] + if let Some(shift) = config.oversampling_shift { + T::regs().cfgr2().modify(|reg| reg.set_ovss(shift)); + } + + #[cfg(any(adc_g0, adc_u0, adc_v3))] + if let Some(ratio) = config.oversampling_ratio { + T::regs().cfgr2().modify(|reg| reg.set_ovsr(ratio)); + } + + #[cfg(any(adc_g0, adc_u0))] + if let Some(enable) = config.oversampling_enable { + T::regs().cfgr2().modify(|reg| reg.set_ovse(enable)); + } + + #[cfg(adc_v3)] + if let Some((mode, trig_mode, enable)) = config.oversampling_mode { + T::regs().cfgr2().modify(|reg| reg.set_trovs(trig_mode)); + T::regs().cfgr2().modify(|reg| reg.set_rovsm(mode)); + T::regs().cfgr2().modify(|reg| reg.set_rovse(enable)); + } + + if let Some(resolution) = config.resolution { + #[cfg(not(any(adc_g0, adc_u0)))] + T::regs().cfgr().modify(|reg| reg.set_res(resolution.into())); + #[cfg(any(adc_g0, adc_u0))] + T::regs().cfgr1().modify(|reg| reg.set_res(resolution.into())); + } + + if let Some(averaging) = config.averaging { + let (enable, samples, right_shift) = match averaging { + Averaging::Disabled => (false, 0, 0), + Averaging::Samples2 => (true, 0, 1), + Averaging::Samples4 => (true, 1, 2), + Averaging::Samples8 => (true, 2, 3), + Averaging::Samples16 => (true, 3, 4), + Averaging::Samples32 => (true, 4, 5), + Averaging::Samples64 => (true, 5, 6), + Averaging::Samples128 => (true, 6, 7), + Averaging::Samples256 => (true, 7, 8), + }; + T::regs().cfgr2().modify(|reg| { + #[cfg(not(any(adc_g0, adc_u0)))] + reg.set_rovse(enable); + #[cfg(any(adc_g0, adc_u0))] + reg.set_ovse(enable); + #[cfg(any(adc_h5, adc_h7rs))] + reg.set_ovsr(samples.into()); + #[cfg(not(any(adc_h5, adc_h7rs)))] + reg.set_ovsr(samples.into()); + reg.set_ovss(right_shift.into()); + }) + } + + s + } + + #[cfg(adc_g0)] + /// Initialize ADC with explicit clock for the analog ADC + pub fn new_with_clock(adc: Peri<'d, T>, clock: Clock) -> Self { + Self::init_regulator(); + + #[cfg(any(stm32wl5x))] + { + // Reset value 0 is actually _No clock selected_ in the STM32WL5x reference manual + let async_clock_available = pac::RCC.ccipr().read().adcsel() != pac::rcc::vals::Adcsel::_RESERVED_0; + match clock { + Clock::Async { div: _ } => { + assert!(async_clock_available); + } + Clock::Sync { div: _ } => { + if async_clock_available { + warn!("Not using configured ADC clock"); + } + } + } + } + match clock { + Clock::Async { div } => T::regs().ccr().modify(|reg| reg.set_presc(div)), + Clock::Sync { div } => T::regs().cfgr2().modify(|reg| { + reg.set_ckmode(match div { + CkModePclk::DIV1 => Ckmode::PCLK, + CkModePclk::DIV2 => Ckmode::PCLK_DIV2, + CkModePclk::DIV4 => Ckmode::PCLK_DIV4, + }) + }), + } + + Self::init_calibrate(); + + Self { adc } + } pub fn enable_vrefint(&self) -> VrefInt { #[cfg(not(any(adc_g0, adc_u0)))] diff --git a/embassy-stm32/src/adc/v4.rs b/embassy-stm32/src/adc/v4.rs index 9be6bcd0b..43eb16fd5 100644 --- a/embassy-stm32/src/adc/v4.rs +++ b/embassy-stm32/src/adc/v4.rs @@ -151,124 +151,26 @@ pub struct AdcConfig { pub averaging: Option, } -impl<'d, T: Instance> Adc<'d, T> { - pub fn new_with_config(adc: Peri<'d, T>, config: AdcConfig) -> Self { - let s = Self::new(adc); - - // Set the ADC resolution. - if let Some(resolution) = config.resolution { - T::regs().cfgr().modify(|reg| reg.set_res(resolution.into())); - } - - // Set hardware averaging. - if let Some(averaging) = config.averaging { - let (enable, samples, right_shift) = match averaging { - Averaging::Disabled => (false, 0, 0), - Averaging::Samples2 => (true, 1, 1), - Averaging::Samples4 => (true, 3, 2), - Averaging::Samples8 => (true, 7, 3), - Averaging::Samples16 => (true, 15, 4), - Averaging::Samples32 => (true, 31, 5), - Averaging::Samples64 => (true, 63, 6), - Averaging::Samples128 => (true, 127, 7), - Averaging::Samples256 => (true, 255, 8), - Averaging::Samples512 => (true, 511, 9), - Averaging::Samples1024 => (true, 1023, 10), - }; - - T::regs().cfgr2().modify(|reg| { - reg.set_rovse(enable); - reg.set_ovsr(samples); - reg.set_ovss(right_shift); - }) - } - - s +impl super::SealedAnyInstance for T { + fn dr() -> *mut u16 { + T::regs().dr().as_ptr() as *mut u16 } - /// Create a new ADC driver. - pub fn new(adc: Peri<'d, T>) -> Self { - rcc::enable_and_reset::(); - - let prescaler = Prescaler::from_ker_ck(T::frequency()); - - T::common_regs().ccr().modify(|w| w.set_presc(prescaler.presc())); - - let frequency = Hertz(T::frequency().0 / prescaler.divisor()); - info!("ADC frequency set to {}", frequency); - - if frequency > MAX_ADC_CLK_FREQ { - panic!( - "Maximal allowed frequency for the ADC is {} MHz and it varies with different packages, refer to ST docs for more information.", - MAX_ADC_CLK_FREQ.0 / 1_000_000 - ); - } - - #[cfg(stm32h7)] - { - let boost = if frequency < Hertz::khz(6_250) { - Boost::LT6_25 - } else if frequency < Hertz::khz(12_500) { - Boost::LT12_5 - } else if frequency < Hertz::mhz(25) { - Boost::LT25 - } else { - Boost::LT50 - }; - T::regs().cr().modify(|w| w.set_boost(boost)); - } - - T::regs().cr().modify(|reg| { - reg.set_deeppwd(false); - reg.set_advregen(true); - }); - - blocking_delay_us(10); - - T::regs().difsel().modify(|w| { - for n in 0..20 { - w.set_difsel(n, Difsel::SINGLE_ENDED); - } - }); - - T::regs().cr().modify(|w| { - #[cfg(not(adc_u5))] - w.set_adcaldif(Adcaldif::SINGLE_ENDED); - w.set_adcallin(true); - }); - - T::regs().cr().modify(|w| w.set_adcal(true)); - - while T::regs().cr().read().adcal() {} - - blocking_delay_us(1); - - Self::enable(); - - // single conversion mode, software trigger - T::regs().cfgr().modify(|w| { - w.set_cont(false); - w.set_exten(Exten::DISABLED); - }); - - Self { adc } - } - - pub(super) fn enable() { + fn enable() { T::regs().isr().write(|w| w.set_adrdy(true)); T::regs().cr().modify(|w| w.set_aden(true)); while !T::regs().isr().read().adrdy() {} T::regs().isr().write(|w| w.set_adrdy(true)); } - pub(super) fn start() { + fn start() { // Start conversion T::regs().cr().modify(|reg| { reg.set_adstart(true); }); } - pub(super) fn stop() { + fn stop() { if T::regs().cr().read().adstart() && !T::regs().cr().read().addis() { T::regs().cr().modify(|reg| { reg.set_adstp(Adstp::STOP); @@ -283,7 +185,7 @@ impl<'d, T: Instance> Adc<'d, T> { }); } - pub(super) fn convert() -> u16 { + fn convert() -> u16 { T::regs().isr().modify(|reg| { reg.set_eos(true); reg.set_eoc(true); @@ -301,7 +203,7 @@ impl<'d, T: Instance> Adc<'d, T> { T::regs().dr().read().0 as u16 } - pub(super) fn configure_dma(conversion_mode: ConversionMode) { + fn configure_dma(conversion_mode: ConversionMode) { match conversion_mode { ConversionMode::Singular => { T::regs().isr().modify(|reg| { @@ -316,7 +218,7 @@ impl<'d, T: Instance> Adc<'d, T> { } } - pub(super) fn configure_sequence(sequence: impl ExactSizeIterator) { + fn configure_sequence(sequence: impl ExactSizeIterator) { // Set sequence length T::regs().sqr1().modify(|w| { w.set_l(sequence.len() as u8 - 1); @@ -366,6 +268,110 @@ impl<'d, T: Instance> Adc<'d, T> { } } } +} + +impl<'d, T: Instance + super::AnyInstance> Adc<'d, T> { + pub fn new_with_config(adc: Peri<'d, T>, config: AdcConfig) -> Self { + let s = Self::new(adc); + + // Set the ADC resolution. + if let Some(resolution) = config.resolution { + T::regs().cfgr().modify(|reg| reg.set_res(resolution.into())); + } + + // Set hardware averaging. + if let Some(averaging) = config.averaging { + let (enable, samples, right_shift) = match averaging { + Averaging::Disabled => (false, 0, 0), + Averaging::Samples2 => (true, 1, 1), + Averaging::Samples4 => (true, 3, 2), + Averaging::Samples8 => (true, 7, 3), + Averaging::Samples16 => (true, 15, 4), + Averaging::Samples32 => (true, 31, 5), + Averaging::Samples64 => (true, 63, 6), + Averaging::Samples128 => (true, 127, 7), + Averaging::Samples256 => (true, 255, 8), + Averaging::Samples512 => (true, 511, 9), + Averaging::Samples1024 => (true, 1023, 10), + }; + + T::regs().cfgr2().modify(|reg| { + reg.set_rovse(enable); + reg.set_ovsr(samples); + reg.set_ovss(right_shift); + }) + } + + s + } + + /// Create a new ADC driver. + pub fn new(adc: Peri<'d, T>) -> Self { + rcc::enable_and_reset::(); + + let prescaler = Prescaler::from_ker_ck(T::frequency()); + + T::common_regs().ccr().modify(|w| w.set_presc(prescaler.presc())); + + let frequency = Hertz(T::frequency().0 / prescaler.divisor()); + info!("ADC frequency set to {}", frequency); + + if frequency > MAX_ADC_CLK_FREQ { + panic!( + "Maximal allowed frequency for the ADC is {} MHz and it varies with different packages, refer to ST docs for more information.", + MAX_ADC_CLK_FREQ.0 / 1_000_000 + ); + } + + #[cfg(stm32h7)] + { + let boost = if frequency < Hertz::khz(6_250) { + Boost::LT6_25 + } else if frequency < Hertz::khz(12_500) { + Boost::LT12_5 + } else if frequency < Hertz::mhz(25) { + Boost::LT25 + } else { + Boost::LT50 + }; + T::regs().cr().modify(|w| w.set_boost(boost)); + } + + T::regs().cr().modify(|reg| { + reg.set_deeppwd(false); + reg.set_advregen(true); + }); + + blocking_delay_us(10); + + T::regs().difsel().modify(|w| { + for n in 0..20 { + w.set_difsel(n, Difsel::SINGLE_ENDED); + } + }); + + T::regs().cr().modify(|w| { + #[cfg(not(adc_u5))] + w.set_adcaldif(Adcaldif::SINGLE_ENDED); + w.set_adcallin(true); + }); + + T::regs().cr().modify(|w| w.set_adcal(true)); + + while T::regs().cr().read().adcal() {} + + blocking_delay_us(1); + + T::enable(); + + // single conversion mode, software trigger + T::regs().cfgr().modify(|w| { + w.set_cont(false); + w.set_exten(Exten::DISABLED); + }); + + Self { adc } + } /// Enable reading the voltage reference internal channel. pub fn enable_vrefint(&self) -> VrefInt { diff --git a/examples/stm32u5/src/bin/adc.rs b/examples/stm32u5/src/bin/adc.rs index 6b9a91d6e..ad59c0bea 100644 --- a/examples/stm32u5/src/bin/adc.rs +++ b/examples/stm32u5/src/bin/adc.rs @@ -2,7 +2,7 @@ #![no_main] use defmt::*; -use embassy_stm32::adc::{self, AdcChannel, AdcConfig, SampleTime, adc4}; +use embassy_stm32::adc::{self, Adc, AdcChannel, AdcConfig, SampleTime, adc4}; use {defmt_rtt as _, panic_probe as _}; #[embassy_executor::main] @@ -15,7 +15,7 @@ async fn main(_spawner: embassy_executor::Spawner) { let mut config = AdcConfig::default(); config.averaging = Some(adc::Averaging::Samples1024); config.resolution = Some(adc::Resolution::BITS14); - let mut adc1 = adc::Adc::new_with_config(p.ADC1, config); + let mut adc1 = Adc::new_with_config(p.ADC1, config); let mut adc1_pin1 = p.PA3; // A0 on nucleo u5a5 let mut adc1_pin2 = p.PA2; // A1 let max1 = adc::resolution_to_max_count(adc::Resolution::BITS14); @@ -24,17 +24,17 @@ async fn main(_spawner: embassy_executor::Spawner) { let mut config = AdcConfig::default(); config.averaging = Some(adc::Averaging::Samples1024); config.resolution = Some(adc::Resolution::BITS14); - let mut adc2 = adc::Adc::new_with_config(p.ADC2, config); + let mut adc2 = Adc::new_with_config(p.ADC2, config); let mut adc2_pin1 = p.PC3; // A2 let mut adc2_pin2 = p.PB0; // A3 let max2 = adc::resolution_to_max_count(adc::Resolution::BITS14); // **** ADC4 init **** - let mut adc4 = adc4::Adc4::new(p.ADC4); + let mut adc4 = Adc::new_adc4(p.ADC4); let mut adc4_pin1 = p.PC1; // A4 let mut adc4_pin2 = p.PC0; // A5 - adc4.set_resolution(adc4::Resolution::BITS12); - adc4.set_averaging(adc4::Averaging::Samples256); + adc4.set_resolution_adc4(adc4::Resolution::BITS12); + adc4.set_averaging_adc4(adc4::Averaging::Samples256); let max4 = adc4::resolution_to_max_count(adc4::Resolution::BITS12); // **** ADC1 blocking read **** @@ -95,11 +95,14 @@ async fn main(_spawner: embassy_executor::Spawner) { // The channels must be in ascending order and can't repeat for ADC4 adc4.read( p.GPDMA1_CH1.reborrow(), - [&mut degraded42, &mut degraded41].into_iter(), + [ + (&mut degraded42, adc4::SampleTime::CYCLES1_5), + (&mut degraded41, adc4::SampleTime::CYCLES1_5), + ] + .into_iter(), &mut measurements, ) - .await - .unwrap(); + .await; let volt2: f32 = 3.3 * measurements[0] as f32 / max4 as f32; let volt1: f32 = 3.3 * measurements[1] as f32 / max4 as f32; info!("Async read 4 pin 1 {}", volt1); diff --git a/examples/stm32wba/src/bin/adc.rs b/examples/stm32wba/src/bin/adc.rs index 177aab3f3..ade3f5d6a 100644 --- a/examples/stm32wba/src/bin/adc.rs +++ b/examples/stm32wba/src/bin/adc.rs @@ -2,7 +2,7 @@ #![no_main] use defmt::*; -use embassy_stm32::adc::{AdcChannel, adc4}; +use embassy_stm32::adc::{Adc, AdcChannel, SampleTime, adc4}; use {defmt_rtt as _, panic_probe as _}; #[embassy_executor::main] @@ -12,11 +12,11 @@ async fn main(_spawner: embassy_executor::Spawner) { let mut p = embassy_stm32::init(config); // **** ADC4 init **** - let mut adc4 = adc4::Adc4::new(p.ADC4); + let mut adc4 = Adc::new_adc4(p.ADC4); let mut adc4_pin1 = p.PA0; // A4 let mut adc4_pin2 = p.PA1; // A5 - adc4.set_resolution(adc4::Resolution::BITS12); - adc4.set_averaging(adc4::Averaging::Samples256); + adc4.set_resolution_adc4(adc4::Resolution::BITS12); + adc4.set_averaging_adc4(adc4::Averaging::Samples256); let max4 = adc4::resolution_to_max_count(adc4::Resolution::BITS12); @@ -37,11 +37,14 @@ async fn main(_spawner: embassy_executor::Spawner) { // The channels must be in ascending order and can't repeat for ADC4 adc4.read( p.GPDMA1_CH1.reborrow(), - [&mut degraded42, &mut degraded41].into_iter(), + [ + (&mut degraded42, SampleTime::CYCLES12_5), + (&mut degraded41, SampleTime::CYCLES12_5), + ] + .into_iter(), &mut measurements, ) - .await - .unwrap(); + .await; let volt2: f32 = 3.3 * measurements[0] as f32 / max4 as f32; let volt1: f32 = 3.0 * measurements[1] as f32 / max4 as f32; info!("Async read 4 pin 1 {}", volt1); diff --git a/examples/stm32wba6/src/bin/adc.rs b/examples/stm32wba6/src/bin/adc.rs index 0887e124c..9d1f39419 100644 --- a/examples/stm32wba6/src/bin/adc.rs +++ b/examples/stm32wba6/src/bin/adc.rs @@ -2,7 +2,7 @@ #![no_main] use defmt::*; -use embassy_stm32::adc::{AdcChannel, adc4}; +use embassy_stm32::adc::{Adc, AdcChannel, SampleTime, adc4}; use {defmt_rtt as _, panic_probe as _}; #[embassy_executor::main] @@ -12,11 +12,11 @@ async fn main(_spawner: embassy_executor::Spawner) { let mut p = embassy_stm32::init(config); // **** ADC4 init **** - let mut adc4 = adc4::Adc4::new(p.ADC4); + let mut adc4 = Adc::new_adc4(p.ADC4); let mut adc4_pin1 = p.PA0; // A4 let mut adc4_pin2 = p.PA1; // A5 - adc4.set_resolution(adc4::Resolution::BITS12); - adc4.set_averaging(adc4::Averaging::Samples256); + adc4.set_resolution_adc4(adc4::Resolution::BITS12); + adc4.set_averaging_adc4(adc4::Averaging::Samples256); let max4 = adc4::resolution_to_max_count(adc4::Resolution::BITS12); // **** ADC4 blocking read **** @@ -36,11 +36,14 @@ async fn main(_spawner: embassy_executor::Spawner) { // The channels must be in ascending order and can't repeat for ADC4 adc4.read( p.GPDMA1_CH1.reborrow(), - [&mut degraded42, &mut degraded41].into_iter(), + [ + (&mut degraded42, SampleTime::CYCLES12_5), + (&mut degraded41, SampleTime::CYCLES12_5), + ] + .into_iter(), &mut measurements, ) - .await - .unwrap(); + .await; let volt2: f32 = 3.3 * measurements[0] as f32 / max4 as f32; let volt1: f32 = 3.0 * measurements[1] as f32 / max4 as f32; info!("Async read 4 pin 1 {}", volt1); -- cgit From 03050a369befb7aeed88079626de21b4055ebccb Mon Sep 17 00:00:00 2001 From: HybridChild Date: Thu, 13 Nov 2025 09:14:03 +0100 Subject: stm32/i2c: Add comprehensive v2 Master API tests and fix async issues --- embassy-stm32/src/i2c/v2.rs | 28 +- tests/stm32/Cargo.toml | 4 +- tests/stm32/src/bin/i2c_v2.rs | 398 ---------------------- tests/stm32/src/bin/i2c_v2_master.rs | 618 +++++++++++++++++++++++++++++++++++ 4 files changed, 627 insertions(+), 421 deletions(-) delete mode 100644 tests/stm32/src/bin/i2c_v2.rs create mode 100644 tests/stm32/src/bin/i2c_v2_master.rs diff --git a/embassy-stm32/src/i2c/v2.rs b/embassy-stm32/src/i2c/v2.rs index c9656d2c2..9771d7c98 100644 --- a/embassy-stm32/src/i2c/v2.rs +++ b/embassy-stm32/src/i2c/v2.rs @@ -1003,9 +1003,7 @@ impl<'d, IM: MasterMode> I2c<'d, Async, IM> { return Poll::Ready(Ok(())); } } else if isr.tcr() { - // poll_fn was woken without an interrupt present - return Poll::Pending; - } else { + // Transfer Complete Reload - need to set up next chunk let last_piece = remaining_len <= 255; if let Err(e) = Self::reload(self.info, remaining_len.min(255), !last_piece, Stop::Automatic, timeout) { @@ -1017,6 +1015,9 @@ impl<'d, IM: MasterMode> I2c<'d, Async, IM> { return Poll::Ready(Ok(())); } self.info.regs.cr1().modify(|w| w.set_tcie(true)); + } else { + // poll_fn was woken without TCR interrupt + return Poll::Pending; } remaining_len = remaining_len.saturating_sub(255); @@ -1052,25 +1053,10 @@ impl<'d, IM: MasterMode> I2c<'d, Async, IM> { pub async fn write_vectored(&mut self, address: Address, write: &[&[u8]]) -> Result<(), Error> { #[cfg(all(feature = "low-power", stm32wlex))] let _device_busy = crate::low_power::DeviceBusy::new_stop1(); - let timeout = self.timeout(); - if write.is_empty() { - return Err(Error::ZeroLengthTransfer); - } - let mut iter = write.iter(); - - let mut first = true; - let mut current = iter.next(); - while let Some(c) = current { - let next = iter.next(); - let is_last = next.is_none(); - - let fut = self.write_dma_internal(address, c, first, is_last, is_last, false, timeout); - timeout.with(fut).await?; - first = false; - current = next; - } - Ok(()) + // For now, use blocking implementation for write_vectored + // This avoids complexity of handling multiple non-contiguous buffers with DMA + self.blocking_write_vectored((address.addr() & 0xFF) as u8, write) } /// Read. diff --git a/tests/stm32/Cargo.toml b/tests/stm32/Cargo.toml index fa757e276..1912a772c 100644 --- a/tests/stm32/Cargo.toml +++ b/tests/stm32/Cargo.toml @@ -161,8 +161,8 @@ path = "src/bin/hash.rs" required-features = [ "hash",] [[bin]] -name = "i2c_v2" -path = "src/bin/i2c_v2.rs" +name = "i2c_v2_master" +path = "src/bin/i2c_v2_master.rs" required-features = [ "stm32f072rb",] [[bin]] diff --git a/tests/stm32/src/bin/i2c_v2.rs b/tests/stm32/src/bin/i2c_v2.rs deleted file mode 100644 index 087b8bbd9..000000000 --- a/tests/stm32/src/bin/i2c_v2.rs +++ /dev/null @@ -1,398 +0,0 @@ -#![no_std] -#![no_main] -// required-features: stm32f072rb -// -// Hardware Setup for NUCLEO-F072RB: -// - I2C1 pins: PB8 (SCL), PB9 (SDA) on CN5 connector -// - Connect to I2C slave device (e.g., Digilent Analog Discovery I2C slave, or EEPROM) -// - Default slave address: 0x50 -// - Pull-up resistors: 4.7kΩ on both SCL and SDA -// - CN5 Pin 10 (PB8/SCL) and CN5 Pin 9 (PB9/SDA) -// -// Analog Discovery Setup: -// - Configure as I2C Slave at address 0x50 -// - DIO 0: SCL -// - DIO 1: SDA -// - Enable pull-ups or use external 4.7kΩ pull-up resistors - -#[path = "../common.rs"] -mod common; - -use common::*; -use embassy_executor::Spawner; -use embassy_stm32::i2c::{Config, I2c, Master}; -use embassy_stm32::mode::{Async, Blocking}; -use embassy_stm32::time::Hertz; -use embassy_time::Timer; -use embedded_hal_1::i2c::Operation; - -#[embassy_executor::main] -async fn main(_spawner: Spawner) { - let p = init(); - info!("I2C v2 Transaction Test Starting..."); - - let mut i2c_peri = peri!(p, I2C); - let mut scl = peri!(p, I2C_SCL); - let mut sda = peri!(p, I2C_SDA); - - let mut config = Config::default(); - config.frequency = Hertz(100_000); - - // I2C slave address for Analog Discovery or test EEPROM - let slave_addr = 0x50u8; - - // Wait for slave device to be ready - Timer::after_millis(100).await; - - // ========== BLOCKING TESTS ========== - info!("========== BLOCKING TRANSACTION TESTS =========="); - { - let mut i2c = I2c::new_blocking( - i2c_peri.reborrow(), - scl.reborrow(), - sda.reborrow(), - config, - ); - - info!("=== Test 1: Consecutive Writes (Should Merge) ==="); - test_consecutive_writes_blocking(&mut i2c, slave_addr); - - info!("=== Test 2: Consecutive Reads (Should Merge) ==="); - test_consecutive_reads_blocking(&mut i2c, slave_addr); - - info!("=== Test 3: Write then Read (RESTART) ==="); - test_write_then_read_blocking(&mut i2c, slave_addr); - - info!("=== Test 4: Read then Write (RESTART) ==="); - test_read_then_write_blocking(&mut i2c, slave_addr); - - info!("=== Test 5: Complex Mixed Sequence ==="); - test_mixed_sequence_blocking(&mut i2c, slave_addr); - - info!("=== Test 6: Single Operations ==="); - test_single_operations_blocking(&mut i2c, slave_addr); - - info!("Blocking tests OK"); - } - - Timer::after_millis(100).await; - - // ========== ASYNC TESTS ========== - info!("========== ASYNC TRANSACTION TESTS (DMA) =========="); - { - let tx_dma = peri!(p, I2C_TX_DMA); - let rx_dma = peri!(p, I2C_RX_DMA); - let irq = irqs!(I2C); - - let mut i2c = I2c::new(i2c_peri, scl, sda, irq, tx_dma, rx_dma, config); - - info!("=== Test 1: Consecutive Writes (Should Merge) ==="); - test_consecutive_writes_async(&mut i2c, slave_addr).await; - - info!("=== Test 2: Consecutive Reads (Should Merge) ==="); - test_consecutive_reads_async(&mut i2c, slave_addr).await; - - info!("=== Test 3: Write then Read (RESTART) ==="); - test_write_then_read_async(&mut i2c, slave_addr).await; - - info!("=== Test 4: Read then Write (RESTART) ==="); - test_read_then_write_async(&mut i2c, slave_addr).await; - - info!("=== Test 5: Complex Mixed Sequence ==="); - test_mixed_sequence_async(&mut i2c, slave_addr).await; - - info!("=== Test 6: Single Operations ==="); - test_single_operations_async(&mut i2c, slave_addr).await; - - info!("Async tests OK"); - } - - info!("All tests OK"); - cortex_m::asm::bkpt(); -} - -fn test_consecutive_writes_blocking(i2c: &mut I2c<'_, Blocking, Master>, addr: u8) { - // Expected on bus: START, ADDR+W, data1, data2, data3, STOP - // NO intermediate RESTART/STOP between writes - they should be merged - let data1 = [0x10, 0x11, 0x12]; - let data2 = [0x20, 0x21]; - let data3 = [0x30, 0x31, 0x32, 0x33]; - - let mut ops = [ - Operation::Write(&data1), - Operation::Write(&data2), - Operation::Write(&data3), - ]; - - match i2c.blocking_transaction(addr, &mut ops) { - Ok(_) => info!("✓ Consecutive writes succeeded (merged 9 bytes)"), - Err(e) => { - error!("✗ Consecutive writes failed: {:?}", e); - defmt::panic!("Test failed: consecutive writes"); - } - } -} - -fn test_consecutive_reads_blocking(i2c: &mut I2c<'_, Blocking, Master>, addr: u8) { - // Expected on bus: START, ADDR+R, data1, data2, data3, NACK, STOP - // NO intermediate RESTART/STOP between reads - they should be merged - let mut buf1 = [0u8; 4]; - let mut buf2 = [0u8; 3]; - let mut buf3 = [0u8; 2]; - - let mut ops = [ - Operation::Read(&mut buf1), - Operation::Read(&mut buf2), - Operation::Read(&mut buf3), - ]; - - match i2c.blocking_transaction(addr, &mut ops) { - Ok(_) => { - info!("✓ Consecutive reads succeeded (merged 9 bytes)"); - info!(" buf1: {:02x}", buf1); - info!(" buf2: {:02x}", buf2); - info!(" buf3: {:02x}", buf3); - } - Err(e) => { - error!("✗ Consecutive reads failed: {:?}", e); - defmt::panic!("Test failed: consecutive reads"); - } - } -} - -fn test_write_then_read_blocking(i2c: &mut I2c<'_, Blocking, Master>, addr: u8) { - // Expected: START, ADDR+W, data, RESTART, ADDR+R, data, NACK, STOP - let write_data = [0xAA, 0xBB]; - let mut read_buf = [0u8; 4]; - - let mut ops = [Operation::Write(&write_data), Operation::Read(&mut read_buf)]; - - match i2c.blocking_transaction(addr, &mut ops) { - Ok(_) => { - info!("✓ Write-then-read succeeded with RESTART"); - info!(" Written: {:02x}", write_data); - info!(" Read: {:02x}", read_buf); - } - Err(e) => { - error!("✗ Write-then-read failed: {:?}", e); - defmt::panic!("Test failed: write-then-read"); - } - } -} - -fn test_read_then_write_blocking(i2c: &mut I2c<'_, Blocking, Master>, addr: u8) { - // Expected: START, ADDR+R, data, NACK, RESTART, ADDR+W, data, STOP - let mut read_buf = [0u8; 3]; - let write_data = [0xCC, 0xDD, 0xEE]; - - let mut ops = [Operation::Read(&mut read_buf), Operation::Write(&write_data)]; - - match i2c.blocking_transaction(addr, &mut ops) { - Ok(_) => { - info!("✓ Read-then-write succeeded with RESTART"); - info!(" Read: {:02x}", read_buf); - info!(" Written: {:02x}", write_data); - } - Err(e) => { - error!("✗ Read-then-write failed: {:?}", e); - defmt::panic!("Test failed: read-then-write"); - } - } -} - -fn test_mixed_sequence_blocking(i2c: &mut I2c<'_, Blocking, Master>, addr: u8) { - // Complex: W, W, R, R, W, R - // Groups: [W,W] RESTART [R,R] RESTART [W] RESTART [R] - let w1 = [0x01, 0x02]; - let w2 = [0x03, 0x04]; - let mut r1 = [0u8; 2]; - let mut r2 = [0u8; 2]; - let w3 = [0x05]; - let mut r3 = [0u8; 1]; - - let mut ops = [ - Operation::Write(&w1), - Operation::Write(&w2), - Operation::Read(&mut r1), - Operation::Read(&mut r2), - Operation::Write(&w3), - Operation::Read(&mut r3), - ]; - - match i2c.blocking_transaction(addr, &mut ops) { - Ok(_) => { - info!("✓ Mixed sequence succeeded"); - info!(" Groups: [W4] RESTART [R4] RESTART [W1] RESTART [R1]"); - } - Err(e) => { - error!("✗ Mixed sequence failed: {:?}", e); - defmt::panic!("Test failed: mixed sequence"); - } - } -} - -fn test_single_operations_blocking(i2c: &mut I2c<'_, Blocking, Master>, addr: u8) { - // Test single write - let write_data = [0xFF]; - let mut ops = [Operation::Write(&write_data)]; - - match i2c.blocking_transaction(addr, &mut ops) { - Ok(_) => info!("✓ Single write succeeded"), - Err(e) => { - error!("✗ Single write failed: {:?}", e); - defmt::panic!("Test failed: single write"); - } - } - - // Test single read - let mut read_buf = [0u8; 1]; - let mut ops = [Operation::Read(&mut read_buf)]; - - match i2c.blocking_transaction(addr, &mut ops) { - Ok(_) => info!("✓ Single read succeeded, data: 0x{:02x}", read_buf[0]), - Err(e) => { - error!("✗ Single read failed: {:?}", e); - defmt::panic!("Test failed: single read"); - } - } -} - -// ==================== ASYNC TEST FUNCTIONS ==================== - -async fn test_consecutive_writes_async(i2c: &mut I2c<'_, Async, Master>, addr: u8) { - let data1 = [0x10, 0x11, 0x12]; - let data2 = [0x20, 0x21]; - let data3 = [0x30, 0x31, 0x32, 0x33]; - - let mut ops = [ - Operation::Write(&data1), - Operation::Write(&data2), - Operation::Write(&data3), - ]; - - match i2c.transaction(addr, &mut ops).await { - Ok(_) => info!("✓ Consecutive writes succeeded (merged 9 bytes)"), - Err(e) => { - error!("✗ Consecutive writes failed: {:?}", e); - defmt::panic!("Test failed: consecutive writes"); - } - } -} - -async fn test_consecutive_reads_async(i2c: &mut I2c<'_, Async, Master>, addr: u8) { - let mut buf1 = [0u8; 4]; - let mut buf2 = [0u8; 3]; - let mut buf3 = [0u8; 2]; - - let mut ops = [ - Operation::Read(&mut buf1), - Operation::Read(&mut buf2), - Operation::Read(&mut buf3), - ]; - - match i2c.transaction(addr, &mut ops).await { - Ok(_) => { - info!("✓ Consecutive reads succeeded (merged 9 bytes)"); - info!(" buf1: {:02x}", buf1); - info!(" buf2: {:02x}", buf2); - info!(" buf3: {:02x}", buf3); - } - Err(e) => { - error!("✗ Consecutive reads failed: {:?}", e); - defmt::panic!("Test failed: consecutive reads"); - } - } -} - -async fn test_write_then_read_async(i2c: &mut I2c<'_, Async, Master>, addr: u8) { - let write_data = [0xAA, 0xBB]; - let mut read_buf = [0u8; 4]; - - let mut ops = [Operation::Write(&write_data), Operation::Read(&mut read_buf)]; - - match i2c.transaction(addr, &mut ops).await { - Ok(_) => { - info!("✓ Write-then-read succeeded with RESTART"); - info!(" Written: {:02x}", write_data); - info!(" Read: {:02x}", read_buf); - } - Err(e) => { - error!("✗ Write-then-read failed: {:?}", e); - defmt::panic!("Test failed: write-then-read"); - } - } -} - -async fn test_read_then_write_async(i2c: &mut I2c<'_, Async, Master>, addr: u8) { - let mut read_buf = [0u8; 3]; - let write_data = [0xCC, 0xDD, 0xEE]; - - let mut ops = [Operation::Read(&mut read_buf), Operation::Write(&write_data)]; - - match i2c.transaction(addr, &mut ops).await { - Ok(_) => { - info!("✓ Read-then-write succeeded with RESTART"); - info!(" Read: {:02x}", read_buf); - info!(" Written: {:02x}", write_data); - } - Err(e) => { - error!("✗ Read-then-write failed: {:?}", e); - defmt::panic!("Test failed: read-then-write"); - } - } -} - -async fn test_mixed_sequence_async(i2c: &mut I2c<'_, Async, Master>, addr: u8) { - let w1 = [0x01, 0x02]; - let w2 = [0x03, 0x04]; - let mut r1 = [0u8; 2]; - let mut r2 = [0u8; 2]; - let w3 = [0x05]; - let mut r3 = [0u8; 1]; - - let mut ops = [ - Operation::Write(&w1), - Operation::Write(&w2), - Operation::Read(&mut r1), - Operation::Read(&mut r2), - Operation::Write(&w3), - Operation::Read(&mut r3), - ]; - - match i2c.transaction(addr, &mut ops).await { - Ok(_) => { - info!("✓ Mixed sequence succeeded"); - info!(" Groups: [W4] RESTART [R4] RESTART [W1] RESTART [R1]"); - } - Err(e) => { - error!("✗ Mixed sequence failed: {:?}", e); - defmt::panic!("Test failed: mixed sequence"); - } - } -} - -async fn test_single_operations_async(i2c: &mut I2c<'_, Async, Master>, addr: u8) { - // Test single write - let write_data = [0xFF]; - let mut ops = [Operation::Write(&write_data)]; - - match i2c.transaction(addr, &mut ops).await { - Ok(_) => info!("✓ Single write succeeded"), - Err(e) => { - error!("✗ Single write failed: {:?}", e); - defmt::panic!("Test failed: single write"); - } - } - - // Test single read - let mut read_buf = [0u8; 1]; - let mut ops = [Operation::Read(&mut read_buf)]; - - match i2c.transaction(addr, &mut ops).await { - Ok(_) => info!("✓ Single read succeeded, data: 0x{:02x}", read_buf[0]), - Err(e) => { - error!("✗ Single read failed: {:?}", e); - defmt::panic!("Test failed: single read"); - } - } -} diff --git a/tests/stm32/src/bin/i2c_v2_master.rs b/tests/stm32/src/bin/i2c_v2_master.rs new file mode 100644 index 000000000..b841d556a --- /dev/null +++ b/tests/stm32/src/bin/i2c_v2_master.rs @@ -0,0 +1,618 @@ +#![no_std] +#![no_main] +// required-features: stm32f072rb +// +// Hardware Setup for NUCLEO-F072RB: +// - I2C1 pins: PB8 (SCL), PB9 (SDA) on CN5 connector +// - Connect to I2C slave device (e.g., Digilent Analog Discovery I2C slave, or EEPROM) +// - Default slave address: 0x50 +// - Pull-up resistors: 4.7kΩ on both SCL and SDA +// - CN5 Pin 10 (PB8/SCL) and CN5 Pin 9 (PB9/SDA) +// +// Analog Discovery - Waveforms Setup: +// - Increase buffer size: Settings -> Device Manager -> Option 4 +// - Run Protocol Analyzer +// - Configure as I2C Slave at address 0x50 +// - Connect and configure DIO pins for SCL and SDA +// - Frequency: 100kHz - [✓] Clock Stretching + +#[path = "../common.rs"] +mod common; + +use common::*; +use embassy_executor::Spawner; +use embassy_stm32::i2c::{Config, I2c, Master}; +use embassy_stm32::mode::{Async, Blocking}; +use embassy_stm32::time::Hertz; +use embassy_time::Timer; +use embedded_hal_1::i2c::Operation; + +#[embassy_executor::main] +async fn main(_spawner: Spawner) { + let p = init(); + info!("Run stm32 I2C v2 Master Tests..."); + + let mut i2c_peri = peri!(p, I2C); + let mut scl = peri!(p, I2C_SCL); + let mut sda = peri!(p, I2C_SDA); + + let mut config = Config::default(); + config.frequency = Hertz(100_000); + + // I2C slave address for Analog Discovery or test EEPROM + let slave_addr = 0x50u8; + + // Wait for slave device to be ready + Timer::after_millis(100).await; + + // ========== BLOCKING DIRECT API TESTS ========== + info!("========== BLOCKING DIRECT API TESTS =========="); + { + let mut i2c = I2c::new_blocking( + i2c_peri.reborrow(), + scl.reborrow(), + sda.reborrow(), + config, + ); + + info!("=== Test 1: Direct blocking_write ==="); + test_blocking_write(&mut i2c, slave_addr); + + info!("=== Test 2: Direct blocking_read ==="); + test_blocking_read(&mut i2c, slave_addr); + + info!("=== Test 3: Direct blocking_write_read ==="); + test_blocking_write_read(&mut i2c, slave_addr); + + info!("=== Test 4: Direct blocking_write_vectored ==="); + test_blocking_write_vectored(&mut i2c, slave_addr); + + info!("=== Test 5: Large buffer (>255 bytes) ==="); + test_blocking_large_buffer(&mut i2c, slave_addr); + + info!("Blocking direct API tests OK"); + } + + Timer::after_millis(100).await; + + // ========== BLOCKING TRANSACTION TESTS ========== + info!("========== BLOCKING TRANSACTION TESTS =========="); + { + let mut i2c = I2c::new_blocking( + i2c_peri.reborrow(), + scl.reborrow(), + sda.reborrow(), + config, + ); + + info!("=== Test 6: Consecutive Writes (Should Merge) ==="); + test_consecutive_writes_blocking(&mut i2c, slave_addr); + + info!("=== Test 7: Consecutive Reads (Should Merge) ==="); + test_consecutive_reads_blocking(&mut i2c, slave_addr); + + info!("=== Test 8: Write then Read (RESTART) ==="); + test_write_then_read_blocking(&mut i2c, slave_addr); + + info!("=== Test 9: Read then Write (RESTART) ==="); + test_read_then_write_blocking(&mut i2c, slave_addr); + + info!("=== Test 10: Complex Mixed Sequence ==="); + test_mixed_sequence_blocking(&mut i2c, slave_addr); + + info!("=== Test 11: Single Operations ==="); + test_single_operations_blocking(&mut i2c, slave_addr); + + info!("Blocking transaction tests OK"); + } + + Timer::after_millis(100).await; + + // ========== ASYNC TESTS (DMA) ========== + info!("========== ASYNC TESTS (DMA) =========="); + { + let tx_dma = peri!(p, I2C_TX_DMA); + let rx_dma = peri!(p, I2C_RX_DMA); + let irq = irqs!(I2C); + + let mut i2c = I2c::new(i2c_peri, scl, sda, irq, tx_dma, rx_dma, config); + + // Direct API tests (reusing same I2C instance) + info!("=== Direct API Test 1: write() ==="); + test_async_write(&mut i2c, slave_addr).await; + + info!("=== Direct API Test 2: read() ==="); + test_async_read(&mut i2c, slave_addr).await; + + info!("=== Direct API Test 3: write_read() ==="); + test_async_write_read(&mut i2c, slave_addr).await; + + info!("=== Direct API Test 4: write_vectored() ==="); + test_async_write_vectored(&mut i2c, slave_addr).await; + + info!("=== Direct API Test 5: Large buffer (>255 bytes) ==="); + test_async_large_buffer(&mut i2c, slave_addr).await; + + info!("Async Direct API tests OK"); + + // Transaction tests + info!("=== Transaction Test 6: Consecutive Writes (Should Merge) ==="); + test_consecutive_writes_async(&mut i2c, slave_addr).await; + + info!("=== Transaction Test 7: Consecutive Reads (Should Merge) ==="); + test_consecutive_reads_async(&mut i2c, slave_addr).await; + + info!("=== Transaction Test 8: Write then Read (RESTART) ==="); + test_write_then_read_async(&mut i2c, slave_addr).await; + + info!("=== Transaction Test 9: Read then Write (RESTART) ==="); + test_read_then_write_async(&mut i2c, slave_addr).await; + + info!("=== Transaction Test 10: Complex Mixed Sequence ==="); + test_mixed_sequence_async(&mut i2c, slave_addr).await; + + info!("=== Transaction Test 11: Single Operations ==="); + test_single_operations_async(&mut i2c, slave_addr).await; + + info!("Async transaction tests OK"); + } + + info!("All tests OK"); + cortex_m::asm::bkpt(); +} + +// ==================== BLOCKING DIRECT API TEST FUNCTIONS ==================== + +fn test_blocking_write(i2c: &mut I2c<'_, Blocking, Master>, addr: u8) { + let write_data = [0x42, 0x43, 0x44, 0x45]; + + match i2c.blocking_write(addr, &write_data) { + Ok(_) => info!("✓ blocking_write succeeded: {:02x}", write_data), + Err(e) => { + error!("✗ blocking_write failed: {:?}", e); + defmt::panic!("Test failed: blocking_write"); + } + } +} + +fn test_blocking_read(i2c: &mut I2c<'_, Blocking, Master>, addr: u8) { + let mut read_buf = [0u8; 8]; + + match i2c.blocking_read(addr, &mut read_buf) { + Ok(_) => info!("✓ blocking_read succeeded: {:02x}", read_buf), + Err(e) => { + error!("✗ blocking_read failed: {:?}", e); + defmt::panic!("Test failed: blocking_read"); + } + } +} + +fn test_blocking_write_read(i2c: &mut I2c<'_, Blocking, Master>, addr: u8) { + let write_data = [0x50, 0x51]; + let mut read_buf = [0u8; 6]; + + match i2c.blocking_write_read(addr, &write_data, &mut read_buf) { + Ok(_) => { + info!("✓ blocking_write_read succeeded"); + info!(" Written: {:02x}", write_data); + info!(" Read: {:02x}", read_buf); + } + Err(e) => { + error!("✗ blocking_write_read failed: {:?}", e); + defmt::panic!("Test failed: blocking_write_read"); + } + } +} + +fn test_blocking_write_vectored(i2c: &mut I2c<'_, Blocking, Master>, addr: u8) { + let buf1 = [0x60, 0x61, 0x62]; + let buf2 = [0x70, 0x71]; + let buf3 = [0x80, 0x81, 0x82, 0x83]; + let bufs = [&buf1[..], &buf2[..], &buf3[..]]; + + match i2c.blocking_write_vectored(addr, &bufs) { + Ok(_) => info!("✓ blocking_write_vectored succeeded (9 bytes total)"), + Err(e) => { + error!("✗ blocking_write_vectored failed: {:?}", e); + defmt::panic!("Test failed: blocking_write_vectored"); + } + } +} + +fn test_blocking_large_buffer(i2c: &mut I2c<'_, Blocking, Master>, addr: u8) { + // Test with 300 bytes to verify RELOAD mechanism works (needs chunking at 255 bytes) + let mut write_buf = [0u8; 300]; + for (i, byte) in write_buf.iter_mut().enumerate() { + *byte = (i & 0xFF) as u8; + } + + match i2c.blocking_write(addr, &write_buf) { + Ok(_) => info!("✓ Large buffer write succeeded (300 bytes, tests RELOAD)"), + Err(e) => { + error!("✗ Large buffer write failed: {:?}", e); + defmt::panic!("Test failed: large buffer write"); + } + } + + // Test large read + let mut read_buf = [0u8; 300]; + match i2c.blocking_read(addr, &mut read_buf) { + Ok(_) => info!("✓ Large buffer read succeeded (300 bytes, tests RELOAD)"), + Err(e) => { + error!("✗ Large buffer read failed: {:?}", e); + defmt::panic!("Test failed: large buffer read"); + } + } +} + +// ==================== BLOCKING TRANSACTION TEST FUNCTIONS ==================== + +fn test_consecutive_writes_blocking(i2c: &mut I2c<'_, Blocking, Master>, addr: u8) { + // Expected on bus: START, ADDR+W, data1, data2, data3, STOP + // NO intermediate RESTART/STOP between writes - they should be merged + let data1 = [0x10, 0x11, 0x12]; + let data2 = [0x20, 0x21]; + let data3 = [0x30, 0x31, 0x32, 0x33]; + + let mut ops = [ + Operation::Write(&data1), + Operation::Write(&data2), + Operation::Write(&data3), + ]; + + match i2c.blocking_transaction(addr, &mut ops) { + Ok(_) => info!("✓ Consecutive writes succeeded (merged 9 bytes)"), + Err(e) => { + error!("✗ Consecutive writes failed: {:?}", e); + defmt::panic!("Test failed: consecutive writes"); + } + } +} + +fn test_consecutive_reads_blocking(i2c: &mut I2c<'_, Blocking, Master>, addr: u8) { + // Expected on bus: START, ADDR+R, data1, data2, data3, NACK, STOP + // NO intermediate RESTART/STOP between reads - they should be merged + let mut buf1 = [0u8; 4]; + let mut buf2 = [0u8; 3]; + let mut buf3 = [0u8; 2]; + + let mut ops = [ + Operation::Read(&mut buf1), + Operation::Read(&mut buf2), + Operation::Read(&mut buf3), + ]; + + match i2c.blocking_transaction(addr, &mut ops) { + Ok(_) => { + info!("✓ Consecutive reads succeeded (merged 9 bytes)"); + info!(" buf1: {:02x}", buf1); + info!(" buf2: {:02x}", buf2); + info!(" buf3: {:02x}", buf3); + } + Err(e) => { + error!("✗ Consecutive reads failed: {:?}", e); + defmt::panic!("Test failed: consecutive reads"); + } + } +} + +fn test_write_then_read_blocking(i2c: &mut I2c<'_, Blocking, Master>, addr: u8) { + // Expected: START, ADDR+W, data, RESTART, ADDR+R, data, NACK, STOP + let write_data = [0xAA, 0xBB]; + let mut read_buf = [0u8; 4]; + + let mut ops = [Operation::Write(&write_data), Operation::Read(&mut read_buf)]; + + match i2c.blocking_transaction(addr, &mut ops) { + Ok(_) => { + info!("✓ Write-then-read succeeded with RESTART"); + info!(" Written: {:02x}", write_data); + info!(" Read: {:02x}", read_buf); + } + Err(e) => { + error!("✗ Write-then-read failed: {:?}", e); + defmt::panic!("Test failed: write-then-read"); + } + } +} + +fn test_read_then_write_blocking(i2c: &mut I2c<'_, Blocking, Master>, addr: u8) { + // Expected: START, ADDR+R, data, NACK, RESTART, ADDR+W, data, STOP + let mut read_buf = [0u8; 3]; + let write_data = [0xCC, 0xDD, 0xEE]; + + let mut ops = [Operation::Read(&mut read_buf), Operation::Write(&write_data)]; + + match i2c.blocking_transaction(addr, &mut ops) { + Ok(_) => { + info!("✓ Read-then-write succeeded with RESTART"); + info!(" Read: {:02x}", read_buf); + info!(" Written: {:02x}", write_data); + } + Err(e) => { + error!("✗ Read-then-write failed: {:?}", e); + defmt::panic!("Test failed: read-then-write"); + } + } +} + +fn test_mixed_sequence_blocking(i2c: &mut I2c<'_, Blocking, Master>, addr: u8) { + // Complex: W, W, R, R, W, R + // Groups: [W,W] RESTART [R,R] RESTART [W] RESTART [R] + let w1 = [0x01, 0x02]; + let w2 = [0x03, 0x04]; + let mut r1 = [0u8; 2]; + let mut r2 = [0u8; 2]; + let w3 = [0x05]; + let mut r3 = [0u8; 1]; + + let mut ops = [ + Operation::Write(&w1), + Operation::Write(&w2), + Operation::Read(&mut r1), + Operation::Read(&mut r2), + Operation::Write(&w3), + Operation::Read(&mut r3), + ]; + + match i2c.blocking_transaction(addr, &mut ops) { + Ok(_) => { + info!("✓ Mixed sequence succeeded"); + info!(" Groups: [W4] RESTART [R4] RESTART [W1] RESTART [R1]"); + } + Err(e) => { + error!("✗ Mixed sequence failed: {:?}", e); + defmt::panic!("Test failed: mixed sequence"); + } + } +} + +fn test_single_operations_blocking(i2c: &mut I2c<'_, Blocking, Master>, addr: u8) { + // Test single write + let write_data = [0xFF]; + let mut ops = [Operation::Write(&write_data)]; + + match i2c.blocking_transaction(addr, &mut ops) { + Ok(_) => info!("✓ Single write succeeded"), + Err(e) => { + error!("✗ Single write failed: {:?}", e); + defmt::panic!("Test failed: single write"); + } + } + + // Test single read + let mut read_buf = [0u8; 1]; + let mut ops = [Operation::Read(&mut read_buf)]; + + match i2c.blocking_transaction(addr, &mut ops) { + Ok(_) => info!("✓ Single read succeeded, data: 0x{:02x}", read_buf[0]), + Err(e) => { + error!("✗ Single read failed: {:?}", e); + defmt::panic!("Test failed: single read"); + } + } +} + +// ==================== ASYNC DIRECT API TEST FUNCTIONS ==================== + +async fn test_async_write(i2c: &mut I2c<'_, Async, Master>, addr: u8) { + let write_data = [0x42, 0x43, 0x44, 0x45]; + + match i2c.write(addr, &write_data).await { + Ok(_) => info!("✓ async write succeeded: {:02x}", write_data), + Err(e) => { + error!("✗ async write failed: {:?}", e); + defmt::panic!("Test failed: async write"); + } + } +} + +async fn test_async_read(i2c: &mut I2c<'_, Async, Master>, addr: u8) { + let mut read_buf = [0u8; 8]; + + match i2c.read(addr, &mut read_buf).await { + Ok(_) => info!("✓ async read succeeded: {:02x}", read_buf), + Err(e) => { + error!("✗ async read failed: {:?}", e); + defmt::panic!("Test failed: async read"); + } + } +} + +async fn test_async_write_read(i2c: &mut I2c<'_, Async, Master>, addr: u8) { + let write_data = [0x50, 0x51]; + let mut read_buf = [0u8; 6]; + + match i2c.write_read(addr, &write_data, &mut read_buf).await { + Ok(_) => { + info!("✓ async write_read succeeded"); + info!(" Written: {:02x}", write_data); + info!(" Read: {:02x}", read_buf); + } + Err(e) => { + error!("✗ async write_read failed: {:?}", e); + defmt::panic!("Test failed: async write_read"); + } + } +} + +async fn test_async_write_vectored(i2c: &mut I2c<'_, Async, Master>, addr: u8) { + let buf1 = [0x60, 0x61, 0x62]; + let buf2 = [0x70, 0x71]; + let buf3 = [0x80, 0x81, 0x82, 0x83]; + let bufs = [&buf1[..], &buf2[..], &buf3[..]]; + + match i2c.write_vectored(addr.into(), &bufs).await { + Ok(_) => info!("✓ async write_vectored succeeded (9 bytes total)"), + Err(e) => { + error!("✗ async write_vectored failed: {:?}", e); + defmt::panic!("Test failed: async write_vectored"); + } + } +} + +async fn test_async_large_buffer(i2c: &mut I2c<'_, Async, Master>, addr: u8) { + // Test with 300 bytes to verify RELOAD mechanism works with DMA (needs chunking at 255 bytes) + let mut write_buf = [0u8; 300]; + for (i, byte) in write_buf.iter_mut().enumerate() { + *byte = (i & 0xFF) as u8; + } + + match i2c.write(addr, &write_buf).await { + Ok(_) => info!("✓ Large buffer async write succeeded (300 bytes, tests RELOAD with DMA)"), + Err(e) => { + error!("✗ Large buffer async write failed: {:?}", e); + defmt::panic!("Test failed: large buffer async write"); + } + } + + // Test large read + let mut read_buf = [0u8; 300]; + match i2c.read(addr, &mut read_buf).await { + Ok(_) => info!("✓ Large buffer async read succeeded (300 bytes, tests RELOAD with DMA)"), + Err(e) => { + error!("✗ Large buffer async read failed: {:?}", e); + defmt::panic!("Test failed: large buffer async read"); + } + } +} + +// ==================== ASYNC TRANSACTION TEST FUNCTIONS ==================== + +async fn test_consecutive_writes_async(i2c: &mut I2c<'_, Async, Master>, addr: u8) { + let data1 = [0x10, 0x11, 0x12]; + let data2 = [0x20, 0x21]; + let data3 = [0x30, 0x31, 0x32, 0x33]; + + let mut ops = [ + Operation::Write(&data1), + Operation::Write(&data2), + Operation::Write(&data3), + ]; + + match i2c.transaction(addr, &mut ops).await { + Ok(_) => info!("✓ Consecutive writes succeeded (merged 9 bytes)"), + Err(e) => { + error!("✗ Consecutive writes failed: {:?}", e); + defmt::panic!("Test failed: consecutive writes"); + } + } +} + +async fn test_consecutive_reads_async(i2c: &mut I2c<'_, Async, Master>, addr: u8) { + let mut buf1 = [0u8; 4]; + let mut buf2 = [0u8; 3]; + let mut buf3 = [0u8; 2]; + + let mut ops = [ + Operation::Read(&mut buf1), + Operation::Read(&mut buf2), + Operation::Read(&mut buf3), + ]; + + match i2c.transaction(addr, &mut ops).await { + Ok(_) => { + info!("✓ Consecutive reads succeeded (merged 9 bytes)"); + info!(" buf1: {:02x}", buf1); + info!(" buf2: {:02x}", buf2); + info!(" buf3: {:02x}", buf3); + } + Err(e) => { + error!("✗ Consecutive reads failed: {:?}", e); + defmt::panic!("Test failed: consecutive reads"); + } + } +} + +async fn test_write_then_read_async(i2c: &mut I2c<'_, Async, Master>, addr: u8) { + let write_data = [0xAA, 0xBB]; + let mut read_buf = [0u8; 4]; + + let mut ops = [Operation::Write(&write_data), Operation::Read(&mut read_buf)]; + + match i2c.transaction(addr, &mut ops).await { + Ok(_) => { + info!("✓ Write-then-read succeeded with RESTART"); + info!(" Written: {:02x}", write_data); + info!(" Read: {:02x}", read_buf); + } + Err(e) => { + error!("✗ Write-then-read failed: {:?}", e); + defmt::panic!("Test failed: write-then-read"); + } + } +} + +async fn test_read_then_write_async(i2c: &mut I2c<'_, Async, Master>, addr: u8) { + let mut read_buf = [0u8; 3]; + let write_data = [0xCC, 0xDD, 0xEE]; + + let mut ops = [Operation::Read(&mut read_buf), Operation::Write(&write_data)]; + + match i2c.transaction(addr, &mut ops).await { + Ok(_) => { + info!("✓ Read-then-write succeeded with RESTART"); + info!(" Read: {:02x}", read_buf); + info!(" Written: {:02x}", write_data); + } + Err(e) => { + error!("✗ Read-then-write failed: {:?}", e); + defmt::panic!("Test failed: read-then-write"); + } + } +} + +async fn test_mixed_sequence_async(i2c: &mut I2c<'_, Async, Master>, addr: u8) { + let w1 = [0x01, 0x02]; + let w2 = [0x03, 0x04]; + let mut r1 = [0u8; 2]; + let mut r2 = [0u8; 2]; + let w3 = [0x05]; + let mut r3 = [0u8; 1]; + + let mut ops = [ + Operation::Write(&w1), + Operation::Write(&w2), + Operation::Read(&mut r1), + Operation::Read(&mut r2), + Operation::Write(&w3), + Operation::Read(&mut r3), + ]; + + match i2c.transaction(addr, &mut ops).await { + Ok(_) => { + info!("✓ Mixed sequence succeeded"); + info!(" Groups: [W4] RESTART [R4] RESTART [W1] RESTART [R1]"); + } + Err(e) => { + error!("✗ Mixed sequence failed: {:?}", e); + defmt::panic!("Test failed: mixed sequence"); + } + } +} + +async fn test_single_operations_async(i2c: &mut I2c<'_, Async, Master>, addr: u8) { + // Test single write + let write_data = [0xFF]; + let mut ops = [Operation::Write(&write_data)]; + + match i2c.transaction(addr, &mut ops).await { + Ok(_) => info!("✓ Single write succeeded"), + Err(e) => { + error!("✗ Single write failed: {:?}", e); + defmt::panic!("Test failed: single write"); + } + } + + // Test single read + let mut read_buf = [0u8; 1]; + let mut ops = [Operation::Read(&mut read_buf)]; + + match i2c.transaction(addr, &mut ops).await { + Ok(_) => info!("✓ Single read succeeded, data: 0x{:02x}", read_buf[0]), + Err(e) => { + error!("✗ Single read failed: {:?}", e); + defmt::panic!("Test failed: single read"); + } + } +} -- cgit From dbd4c384f94044505917295145c25777768a3081 Mon Sep 17 00:00:00 2001 From: HybridChild Date: Thu, 13 Nov 2025 10:50:48 +0100 Subject: stm32/i2c: Run cargo fmt --- embassy-stm32/src/i2c/v2.rs | 91 ++++++++++++++++++++++++++++++++++----------- 1 file changed, 69 insertions(+), 22 deletions(-) diff --git a/embassy-stm32/src/i2c/v2.rs b/embassy-stm32/src/i2c/v2.rs index 9771d7c98..7bcfa00b0 100644 --- a/embassy-stm32/src/i2c/v2.rs +++ b/embassy-stm32/src/i2c/v2.rs @@ -222,7 +222,13 @@ impl<'d, M: Mode, IM: MasterMode> I2c<'d, M, IM> { Ok(()) } - fn reload(info: &'static Info, length: usize, will_reload: bool, stop: Stop, timeout: Timeout) -> Result<(), Error> { + fn reload( + info: &'static Info, + length: usize, + will_reload: bool, + stop: Stop, + timeout: Timeout, + ) -> Result<(), Error> { assert!(length < 256 && length > 0); // Wait for either TCR (Transfer Complete Reload) or TC (Transfer Complete) @@ -417,7 +423,13 @@ impl<'d, M: Mode, IM: MasterMode> I2c<'d, M, IM> { for (number, chunk) in read.chunks_mut(255).enumerate() { if number != 0 { - Self::reload(self.info, chunk.len(), number != last_chunk_idx, Stop::Automatic, timeout)?; + Self::reload( + self.info, + chunk.len(), + number != last_chunk_idx, + Stop::Automatic, + timeout, + )?; } for byte in chunk { @@ -466,7 +478,13 @@ impl<'d, M: Mode, IM: MasterMode> I2c<'d, M, IM> { for (number, chunk) in write.chunks(255).enumerate() { if number != 0 { - Self::reload(self.info, chunk.len(), number != last_chunk_idx, Stop::Software, timeout)?; + Self::reload( + self.info, + chunk.len(), + number != last_chunk_idx, + Stop::Software, + timeout, + )?; } for byte in chunk { @@ -606,7 +624,15 @@ impl<'d, M: Mode, IM: MasterMode> I2c<'d, M, IM> { if first_chunk { // First chunk: initiate transfer // If not first group, use RESTART instead of START - Self::master_write(self.info, address, chunk_len, Stop::Software, will_reload, !is_first_group, timeout)?; + Self::master_write( + self.info, + address, + chunk_len, + Stop::Software, + will_reload, + !is_first_group, + timeout, + )?; first_chunk = false; } else { // Subsequent chunks: use reload @@ -906,7 +932,13 @@ impl<'d, IM: MasterMode> I2c<'d, Async, IM> { } else if remaining_len == 0 { return Poll::Ready(Ok(())); } else { - if let Err(e) = Self::reload(self.info, remaining_len.min(255), remaining_len > 255, Stop::Software, timeout) { + if let Err(e) = Self::reload( + self.info, + remaining_len.min(255), + remaining_len > 255, + Stop::Software, + timeout, + ) { return Poll::Ready(Err(e)); } self.info.regs.cr1().modify(|w| w.set_tcie(true)); @@ -1225,14 +1257,8 @@ impl<'d, IM: MasterMode> I2c<'d, Async, IM> { }; // We need a custom DMA read that respects our stop mode - self.read_dma_group_internal( - address, - buffer, - restart, - stop_mode, - timeout, - ) - .await?; + self.read_dma_group_internal(address, buffer, restart, stop_mode, timeout) + .await?; is_first_in_group = false; } else { // Subsequent buffers: need to reload and continue @@ -1243,11 +1269,8 @@ impl<'d, IM: MasterMode> I2c<'d, Async, IM> { }; self.read_dma_group_internal( - address, - buffer, - false, // no restart for subsequent buffers in same group - stop_mode, - timeout, + address, buffer, false, // no restart for subsequent buffers in same group + stop_mode, timeout, ) .await?; } @@ -1499,7 +1522,13 @@ impl<'d, M: Mode> I2c<'d, M, MultiMaster> { if number == 0 { Self::slave_start(self.info, chunk.len(), number != last_chunk_idx); } else { - Self::reload(self.info, chunk.len(), number != last_chunk_idx, Stop::Software, timeout)?; + Self::reload( + self.info, + chunk.len(), + number != last_chunk_idx, + Stop::Software, + timeout, + )?; } let mut index = 0; @@ -1548,7 +1577,13 @@ impl<'d, M: Mode> I2c<'d, M, MultiMaster> { if number == 0 { Self::slave_start(self.info, chunk.len(), number != last_chunk_idx); } else { - Self::reload(self.info, chunk.len(), number != last_chunk_idx, Stop::Software, timeout)?; + Self::reload( + self.info, + chunk.len(), + number != last_chunk_idx, + Stop::Software, + timeout, + )?; } let mut index = 0; @@ -1684,7 +1719,13 @@ impl<'d> I2c<'d, Async, MultiMaster> { Poll::Pending } else if isr.tcr() { let is_last_slice = remaining_len <= 255; - if let Err(e) = Self::reload(self.info, remaining_len.min(255), !is_last_slice, Stop::Software, timeout) { + if let Err(e) = Self::reload( + self.info, + remaining_len.min(255), + !is_last_slice, + Stop::Software, + timeout, + ) { return Poll::Ready(Err(e)); } remaining_len = remaining_len.saturating_sub(255); @@ -1748,7 +1789,13 @@ impl<'d> I2c<'d, Async, MultiMaster> { Poll::Pending } else if isr.tcr() { let is_last_slice = remaining_len <= 255; - if let Err(e) = Self::reload(self.info, remaining_len.min(255), !is_last_slice, Stop::Software, timeout) { + if let Err(e) = Self::reload( + self.info, + remaining_len.min(255), + !is_last_slice, + Stop::Software, + timeout, + ) { return Poll::Ready(Err(e)); } remaining_len = remaining_len.saturating_sub(255); -- cgit From 260a3fdc530ef430956ed313811efea94e1dff5c Mon Sep 17 00:00:00 2001 From: HybridChild Date: Thu, 13 Nov 2025 10:57:41 +0100 Subject: stm32: Run cargo fmt for tests/ --- tests/stm32/src/bin/i2c_v2_master.rs | 14 ++------------ 1 file changed, 2 insertions(+), 12 deletions(-) diff --git a/tests/stm32/src/bin/i2c_v2_master.rs b/tests/stm32/src/bin/i2c_v2_master.rs index b841d556a..34f9b48d3 100644 --- a/tests/stm32/src/bin/i2c_v2_master.rs +++ b/tests/stm32/src/bin/i2c_v2_master.rs @@ -48,12 +48,7 @@ async fn main(_spawner: Spawner) { // ========== BLOCKING DIRECT API TESTS ========== info!("========== BLOCKING DIRECT API TESTS =========="); { - let mut i2c = I2c::new_blocking( - i2c_peri.reborrow(), - scl.reborrow(), - sda.reborrow(), - config, - ); + let mut i2c = I2c::new_blocking(i2c_peri.reborrow(), scl.reborrow(), sda.reborrow(), config); info!("=== Test 1: Direct blocking_write ==="); test_blocking_write(&mut i2c, slave_addr); @@ -78,12 +73,7 @@ async fn main(_spawner: Spawner) { // ========== BLOCKING TRANSACTION TESTS ========== info!("========== BLOCKING TRANSACTION TESTS =========="); { - let mut i2c = I2c::new_blocking( - i2c_peri.reborrow(), - scl.reborrow(), - sda.reborrow(), - config, - ); + let mut i2c = I2c::new_blocking(i2c_peri.reborrow(), scl.reborrow(), sda.reborrow(), config); info!("=== Test 6: Consecutive Writes (Should Merge) ==="); test_consecutive_writes_blocking(&mut i2c, slave_addr); -- cgit From dccf185e1489c0055fcacdea59ce7837cc4d076d Mon Sep 17 00:00:00 2001 From: "Andreas Lindahl Flåten (ALF)" Date: Wed, 5 Nov 2025 16:47:09 +0100 Subject: Add c.rs flash for the stm32c0 family This is basically a copy of the `g.rs` file, with multi bank support removed (c0 is single bank only). --- embassy-stm32/src/flash/c.rs | 122 ++++++++++++++++++++++++++++++++++++++ embassy-stm32/src/flash/common.rs | 2 +- embassy-stm32/src/flash/mod.rs | 3 +- 3 files changed, 125 insertions(+), 2 deletions(-) create mode 100644 embassy-stm32/src/flash/c.rs diff --git a/embassy-stm32/src/flash/c.rs b/embassy-stm32/src/flash/c.rs new file mode 100644 index 000000000..af3d07ac6 --- /dev/null +++ b/embassy-stm32/src/flash/c.rs @@ -0,0 +1,122 @@ +use core::ptr::write_volatile; +use core::sync::atomic::{Ordering, fence}; + +use cortex_m::interrupt; + +use super::{FlashSector, WRITE_SIZE}; +use crate::flash::Error; +use crate::pac; + +pub(crate) unsafe fn lock() { + pac::FLASH.cr().modify(|w| w.set_lock(true)); +} +pub(crate) unsafe fn unlock() { + // Wait, while the memory interface is busy. + wait_busy(); + + // Unlock flash + if pac::FLASH.cr().read().lock() { + pac::FLASH.keyr().write_value(0x4567_0123); + pac::FLASH.keyr().write_value(0xCDEF_89AB); + } +} + +pub(crate) unsafe fn enable_blocking_write() { + assert_eq!(0, WRITE_SIZE % 4); + pac::FLASH.cr().write(|w| w.set_pg(true)); +} + +pub(crate) unsafe fn disable_blocking_write() { + pac::FLASH.cr().write(|w| w.set_pg(false)); +} + +pub(crate) unsafe fn blocking_write(start_address: u32, buf: &[u8; WRITE_SIZE]) -> Result<(), Error> { + let mut address = start_address; + for val in buf.chunks(4) { + write_volatile(address as *mut u32, u32::from_le_bytes(unwrap!(val.try_into()))); + address += val.len() as u32; + + // prevents parallelism errors + fence(Ordering::SeqCst); + } + + wait_ready_blocking() +} + +pub(crate) unsafe fn blocking_erase_sector(sector: &FlashSector) -> Result<(), Error> { + let idx = (sector.start - super::FLASH_BASE as u32) / super::BANK1_REGION.erase_size as u32; + + #[cfg(feature = "defmt")] + defmt::trace!("STM32C0 Erase: addr=0x{:08x}, idx={}, erase_size={}", sector.start, idx, super::BANK1_REGION.erase_size); + + + wait_busy(); + clear_all_err(); + + // Explicitly unlock before erase + unlock(); + + interrupt::free(|_| { + #[cfg(feature = "defmt")] + { + let cr_before = pac::FLASH.cr().read(); + defmt::trace!("FLASH_CR before: 0x{:08x}", cr_before.0); + } + + pac::FLASH.cr().modify(|w| { + w.set_per(true); + w.set_pnb(idx as u8); + w.set_strt(true); + }); + + #[cfg(feature = "defmt")] + { + let cr_after = pac::FLASH.cr().read(); + defmt::trace!("FLASH_CR after: 0x{:08x}, PER={}, PNB={}, STRT={}", + cr_after.0, cr_after.per(), cr_after.pnb(), cr_after.strt()); + } + }); + + let ret: Result<(), Error> = wait_ready_blocking(); + + // Clear erase bit + pac::FLASH.cr().modify(|w| w.set_per(false)); + + // Explicitly lock after erase + lock(); + + // Extra wait to ensure operation completes + wait_busy(); + + ret +} + +pub(crate) unsafe fn wait_ready_blocking() -> Result<(), Error> { + while pac::FLASH.sr().read().bsy() {} + + let sr = pac::FLASH.sr().read(); + + if sr.progerr() { + return Err(Error::Prog); + } + + if sr.wrperr() { + return Err(Error::Protected); + } + + if sr.pgaerr() { + return Err(Error::Unaligned); + } + + Ok(()) +} + +pub(crate) unsafe fn clear_all_err() { + // read and write back the same value. + // This clears all "write 1 to clear" bits. + pac::FLASH.sr().modify(|_| {}); +} + +fn wait_busy() { + while pac::FLASH.sr().read().bsy() {} +} diff --git a/embassy-stm32/src/flash/common.rs b/embassy-stm32/src/flash/common.rs index b595938a6..508bb2548 100644 --- a/embassy-stm32/src/flash/common.rs +++ b/embassy-stm32/src/flash/common.rs @@ -102,7 +102,7 @@ pub(super) unsafe fn blocking_write( } let mut address = base + offset; - trace!("Writing {} bytes at 0x{:x}", bytes.len(), address); + trace!("Writing {} bytes at 0x{:x} (base=0x{:x}, offset=0x{:x})", bytes.len(), address, base, offset); for chunk in bytes.chunks(WRITE_SIZE) { write_chunk(address, chunk)?; diff --git a/embassy-stm32/src/flash/mod.rs b/embassy-stm32/src/flash/mod.rs index 3e74d857a..39cd9b3a9 100644 --- a/embassy-stm32/src/flash/mod.rs +++ b/embassy-stm32/src/flash/mod.rs @@ -99,6 +99,7 @@ compile_error!("The 'eeprom' cfg is enabled for a non-L0/L1 chip family. This is #[cfg_attr(flash_f4, path = "f4.rs")] #[cfg_attr(flash_f7, path = "f7.rs")] #[cfg_attr(any(flash_g0x0, flash_g0x1, flash_g4c2, flash_g4c3, flash_g4c4), path = "g.rs")] +#[cfg_attr(flash_c0, path = "c.rs")] #[cfg_attr(flash_h7, path = "h7.rs")] #[cfg_attr(flash_h7ab, path = "h7.rs")] #[cfg_attr(any(flash_u5, flash_wba), path = "u5.rs")] @@ -108,7 +109,7 @@ compile_error!("The 'eeprom' cfg is enabled for a non-L0/L1 chip family. This is #[cfg_attr( not(any( flash_l0, flash_l1, flash_l4, flash_l5, flash_wl, flash_wb, flash_f0, flash_f1, flash_f2, flash_f3, flash_f4, - flash_f7, flash_g0x0, flash_g0x1, flash_g4c2, flash_g4c3, flash_g4c4, flash_h7, flash_h7ab, flash_u5, + flash_f7, flash_g0x0, flash_g0x1, flash_g4c2, flash_g4c3, flash_g4c4, flash_c0, flash_h7, flash_h7ab, flash_u5, flash_wba, flash_h50, flash_u0, flash_h5, )), path = "other.rs" -- cgit From f72349660eb30f6fc32104db60c33a732a99f6b5 Mon Sep 17 00:00:00 2001 From: "Andreas Lindahl Flåten (ALF)" Date: Thu, 13 Nov 2025 11:24:43 +0100 Subject: add changelog and fix rustfmt errors --- embassy-stm32/CHANGELOG.md | 1 + embassy-stm32/src/flash/c.rs | 17 +++++++++++++---- embassy-stm32/src/flash/common.rs | 8 +++++++- 3 files changed, 21 insertions(+), 5 deletions(-) diff --git a/embassy-stm32/CHANGELOG.md b/embassy-stm32/CHANGELOG.md index 3431848d3..7586861ef 100644 --- a/embassy-stm32/CHANGELOG.md +++ b/embassy-stm32/CHANGELOG.md @@ -55,6 +55,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - chore: Updated stm32-metapac and stm32-data dependencies - adc: reogranize and cleanup somewhat. require sample_time to be passed on conversion - fix: stm32/i2c v2 slave: prevent misaligned reads, error false positives, and incorrect counts of bytes read/written +- feat: add flash support for c0 family ([#4874](https://github.com/embassy-rs/embassy/pull/4874)) ## 0.4.0 - 2025-08-26 diff --git a/embassy-stm32/src/flash/c.rs b/embassy-stm32/src/flash/c.rs index af3d07ac6..0ad1002b0 100644 --- a/embassy-stm32/src/flash/c.rs +++ b/embassy-stm32/src/flash/c.rs @@ -47,8 +47,12 @@ pub(crate) unsafe fn blocking_erase_sector(sector: &FlashSector) -> Result<(), E let idx = (sector.start - super::FLASH_BASE as u32) / super::BANK1_REGION.erase_size as u32; #[cfg(feature = "defmt")] - defmt::trace!("STM32C0 Erase: addr=0x{:08x}, idx={}, erase_size={}", sector.start, idx, super::BANK1_REGION.erase_size); - + defmt::trace!( + "STM32C0 Erase: addr=0x{:08x}, idx={}, erase_size={}", + sector.start, + idx, + super::BANK1_REGION.erase_size + ); wait_busy(); clear_all_err(); @@ -72,8 +76,13 @@ pub(crate) unsafe fn blocking_erase_sector(sector: &FlashSector) -> Result<(), E #[cfg(feature = "defmt")] { let cr_after = pac::FLASH.cr().read(); - defmt::trace!("FLASH_CR after: 0x{:08x}, PER={}, PNB={}, STRT={}", - cr_after.0, cr_after.per(), cr_after.pnb(), cr_after.strt()); + defmt::trace!( + "FLASH_CR after: 0x{:08x}, PER={}, PNB={}, STRT={}", + cr_after.0, + cr_after.per(), + cr_after.pnb(), + cr_after.strt() + ); } }); diff --git a/embassy-stm32/src/flash/common.rs b/embassy-stm32/src/flash/common.rs index 508bb2548..60d00e766 100644 --- a/embassy-stm32/src/flash/common.rs +++ b/embassy-stm32/src/flash/common.rs @@ -102,7 +102,13 @@ pub(super) unsafe fn blocking_write( } let mut address = base + offset; - trace!("Writing {} bytes at 0x{:x} (base=0x{:x}, offset=0x{:x})", bytes.len(), address, base, offset); + trace!( + "Writing {} bytes at 0x{:x} (base=0x{:x}, offset=0x{:x})", + bytes.len(), + address, + base, + offset + ); for chunk in bytes.chunks(WRITE_SIZE) { write_chunk(address, chunk)?; -- cgit From c17d24d0cc8fedbe69b22032ea7323997ddfe4dc Mon Sep 17 00:00:00 2001 From: xoviat Date: Thu, 13 Nov 2025 07:38:06 -0600 Subject: extract averaging enum --- embassy-stm32/src/adc/c0.rs | 19 ------------------- embassy-stm32/src/adc/mod.rs | 20 ++++++++++++++++++++ embassy-stm32/src/adc/v3.rs | 17 +---------------- embassy-stm32/src/adc/v4.rs | 19 +------------------ 4 files changed, 22 insertions(+), 53 deletions(-) diff --git a/embassy-stm32/src/adc/c0.rs b/embassy-stm32/src/adc/c0.rs index bc97a7c4b..8992d6e6e 100644 --- a/embassy-stm32/src/adc/c0.rs +++ b/embassy-stm32/src/adc/c0.rs @@ -119,25 +119,6 @@ impl<'a> defmt::Format for Prescaler { } } -/// Number of samples used for averaging. -/// TODO: Implement hardware averaging setting. -#[allow(unused)] -#[derive(Copy, Clone, Debug)] -#[cfg_attr(feature = "defmt", derive(defmt::Format))] -pub enum Averaging { - Disabled, - Samples2, - Samples4, - Samples8, - Samples16, - Samples32, - Samples64, - Samples128, - Samples256, - Samples512, - Samples1024, -} - impl<'d, T: Instance> Adc<'d, T> { /// Create a new ADC driver. pub fn new(adc: Peri<'d, T>, resolution: Resolution) -> Self { diff --git a/embassy-stm32/src/adc/mod.rs b/embassy-stm32/src/adc/mod.rs index 856c2e61e..c91d68e87 100644 --- a/embassy-stm32/src/adc/mod.rs +++ b/embassy-stm32/src/adc/mod.rs @@ -148,6 +148,26 @@ pub(crate) fn blocking_delay_us(us: u32) { } } +#[cfg(any(adc_c0, adc_v3, adc_g0, adc_h5, adc_h7rs, adc_u0, adc_v4, adc_u5))] +/// Number of samples used for averaging. +#[derive(Copy, Clone, Debug)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +pub enum Averaging { + Disabled, + Samples2, + Samples4, + Samples8, + Samples16, + Samples32, + Samples64, + Samples128, + Samples256, + #[cfg(any(adc_c0, adc_v4, adc_u5))] + Samples512, + #[cfg(any(adc_c0, adc_v4, adc_u5))] + Samples1024, +} + #[cfg(any(adc_v2, adc_g4, adc_v3, adc_g0, adc_h5, adc_h7rs, adc_u0, adc_v4, adc_u5, adc_wba))] #[allow(dead_code)] pub(crate) enum ConversionMode { diff --git a/embassy-stm32/src/adc/v3.rs b/embassy-stm32/src/adc/v3.rs index 4cce1dac3..fa191c663 100644 --- a/embassy-stm32/src/adc/v3.rs +++ b/embassy-stm32/src/adc/v3.rs @@ -11,7 +11,7 @@ pub use pac::adc::vals::{Ovsr, Ovss, Presc}; #[allow(unused_imports)] use super::SealedAdcChannel; -use super::{Adc, Instance, Resolution, SampleTime, Temperature, Vbat, VrefInt, blocking_delay_us}; +use super::{Adc, Averaging, Instance, Resolution, SampleTime, Temperature, Vbat, VrefInt, blocking_delay_us}; use crate::adc::ConversionMode; use crate::{Peri, pac, rcc}; @@ -100,21 +100,6 @@ cfg_if! { } } -/// Number of samples used for averaging. -#[derive(Copy, Clone, Debug)] -#[cfg_attr(feature = "defmt", derive(defmt::Format))] -pub enum Averaging { - Disabled, - Samples2, - Samples4, - Samples8, - Samples16, - Samples32, - Samples64, - Samples128, - Samples256, -} - cfg_if! { if #[cfg(adc_g0)] { /// Synchronous PCLK prescaler diff --git a/embassy-stm32/src/adc/v4.rs b/embassy-stm32/src/adc/v4.rs index 43eb16fd5..1b17b744f 100644 --- a/embassy-stm32/src/adc/v4.rs +++ b/embassy-stm32/src/adc/v4.rs @@ -4,7 +4,7 @@ use pac::adc::vals::{Adcaldif, Boost}; use pac::adc::vals::{Adstp, Difsel, Dmngt, Exten, Pcsel}; use pac::adccommon::vals::Presc; -use super::{Adc, Instance, Resolution, SampleTime, Temperature, Vbat, VrefInt, blocking_delay_us}; +use super::{Adc, Averaging, Instance, Resolution, SampleTime, Temperature, Vbat, VrefInt, blocking_delay_us}; use crate::adc::ConversionMode; use crate::time::Hertz; use crate::{Peri, pac, rcc}; @@ -127,23 +127,6 @@ impl Prescaler { } } -/// Number of samples used for averaging. -#[derive(Copy, Clone, Debug)] -#[cfg_attr(feature = "defmt", derive(defmt::Format))] -pub enum Averaging { - Disabled, - Samples2, - Samples4, - Samples8, - Samples16, - Samples32, - Samples64, - Samples128, - Samples256, - Samples512, - Samples1024, -} - /// Adc configuration #[derive(Default)] pub struct AdcConfig { -- cgit From 2553ced205d49d2890e000069f5a170b75d267a9 Mon Sep 17 00:00:00 2001 From: HybridChild Date: Thu, 13 Nov 2025 14:36:31 +0100 Subject: stm32: Move i2c_master test to examples --- examples/stm32f0/Cargo.toml | 1 + examples/stm32f0/src/bin/i2c_master.rs | 609 +++++++++++++++++++++++++++++++++ tests/stm32/Cargo.toml | 7 - tests/stm32/build.rs | 1 - tests/stm32/src/bin/i2c_v2_master.rs | 608 -------------------------------- tests/stm32/src/common.rs | 25 -- 6 files changed, 610 insertions(+), 641 deletions(-) create mode 100644 examples/stm32f0/src/bin/i2c_master.rs delete mode 100644 tests/stm32/src/bin/i2c_v2_master.rs diff --git a/examples/stm32f0/Cargo.toml b/examples/stm32f0/Cargo.toml index a78873d21..177dd0ac2 100644 --- a/examples/stm32f0/Cargo.toml +++ b/examples/stm32f0/Cargo.toml @@ -16,6 +16,7 @@ panic-probe = { version = "1.0.0", features = ["print-defmt"] } 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", "executor-interrupt", "defmt"] } embassy-time = { version = "0.5.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } +embedded-hal-1 = { package = "embedded-hal", version = "1.0" } static_cell = "2" portable-atomic = { version = "1.5", features = ["unsafe-assume-single-core"] } diff --git a/examples/stm32f0/src/bin/i2c_master.rs b/examples/stm32f0/src/bin/i2c_master.rs new file mode 100644 index 000000000..2e61ecdf7 --- /dev/null +++ b/examples/stm32f0/src/bin/i2c_master.rs @@ -0,0 +1,609 @@ +#![no_std] +#![no_main] + +// Hardware Setup for NUCLEO-F072RB: +// - I2C1 pins: PB8 (SCL), PB9 (SDA) on CN5 connector +// - Connect to I2C slave device (e.g., Digilent Analog Discovery I2C slave, or EEPROM) +// - Default slave address: 0x50 +// - Pull-up resistors: 4.7kΩ on both SCL and SDA +// - CN5 Pin 10 (PB8/SCL) and CN5 Pin 9 (PB9/SDA) +// +// Analog Discovery - Waveforms Setup: +// - Increase buffer size: Settings -> Device Manager -> Option 4 +// - Run Protocol Analyzer +// - Configure as I2C Slave at address 0x50 +// - Connect and configure DIO pins for SCL and SDA +// - Frequency: 100kHz - [✓] Clock Stretching + +use defmt::*; +use embassy_executor::Spawner; +use embassy_stm32::i2c::{Config, I2c, Master}; +use embassy_stm32::mode::{Async, Blocking}; +use embassy_stm32::time::Hertz; +use embassy_stm32::{bind_interrupts, i2c, peripherals}; +use embassy_time::Timer; +use embedded_hal_1::i2c::Operation; +use {defmt_rtt as _, panic_probe as _}; + +bind_interrupts!(struct Irqs { + I2C1 => i2c::EventInterruptHandler, i2c::ErrorInterruptHandler; +}); + +#[embassy_executor::main] +async fn main(_spawner: Spawner) { + let p = embassy_stm32::init(Default::default()); + info!("Run stm32 I2C v2 Master Tests..."); + + let mut i2c_peri = p.I2C1; + let mut scl = p.PB8; + let mut sda = p.PB9; + + let mut config = Config::default(); + config.frequency = Hertz(100_000); + + // I2C slave address for Analog Discovery or test EEPROM + let slave_addr = 0x50u8; + + // Wait for slave device to be ready + Timer::after_millis(100).await; + + // ========== BLOCKING DIRECT API TESTS ========== + info!("========== BLOCKING DIRECT API TESTS =========="); + { + let mut i2c = I2c::new_blocking(i2c_peri.reborrow(), scl.reborrow(), sda.reborrow(), config); + + info!("=== Test 1: Direct blocking_write ==="); + test_blocking_write(&mut i2c, slave_addr); + + info!("=== Test 2: Direct blocking_read ==="); + test_blocking_read(&mut i2c, slave_addr); + + info!("=== Test 3: Direct blocking_write_read ==="); + test_blocking_write_read(&mut i2c, slave_addr); + + info!("=== Test 4: Direct blocking_write_vectored ==="); + test_blocking_write_vectored(&mut i2c, slave_addr); + + info!("=== Test 5: Large buffer (>255 bytes) ==="); + test_blocking_large_buffer(&mut i2c, slave_addr); + + info!("Blocking direct API tests OK"); + } + + Timer::after_millis(100).await; + + // ========== BLOCKING TRANSACTION TESTS ========== + info!("========== BLOCKING TRANSACTION TESTS =========="); + { + let mut i2c = I2c::new_blocking(i2c_peri.reborrow(), scl.reborrow(), sda.reborrow(), config); + + info!("=== Test 6: Consecutive Writes (Should Merge) ==="); + test_consecutive_writes_blocking(&mut i2c, slave_addr); + + info!("=== Test 7: Consecutive Reads (Should Merge) ==="); + test_consecutive_reads_blocking(&mut i2c, slave_addr); + + info!("=== Test 8: Write then Read (RESTART) ==="); + test_write_then_read_blocking(&mut i2c, slave_addr); + + info!("=== Test 9: Read then Write (RESTART) ==="); + test_read_then_write_blocking(&mut i2c, slave_addr); + + info!("=== Test 10: Complex Mixed Sequence ==="); + test_mixed_sequence_blocking(&mut i2c, slave_addr); + + info!("=== Test 11: Single Operations ==="); + test_single_operations_blocking(&mut i2c, slave_addr); + + info!("Blocking transaction tests OK"); + } + + Timer::after_millis(100).await; + + // ========== ASYNC TESTS (DMA) ========== + info!("========== ASYNC TESTS (DMA) =========="); + { + let tx_dma = p.DMA1_CH2; + let rx_dma = p.DMA1_CH3; + + let mut i2c = I2c::new(i2c_peri, scl, sda, Irqs, tx_dma, rx_dma, config); + + // Direct API tests (reusing same I2C instance) + info!("=== Direct API Test 1: write() ==="); + test_async_write(&mut i2c, slave_addr).await; + + info!("=== Direct API Test 2: read() ==="); + test_async_read(&mut i2c, slave_addr).await; + + info!("=== Direct API Test 3: write_read() ==="); + test_async_write_read(&mut i2c, slave_addr).await; + + info!("=== Direct API Test 4: write_vectored() ==="); + test_async_write_vectored(&mut i2c, slave_addr).await; + + info!("=== Direct API Test 5: Large buffer (>255 bytes) ==="); + test_async_large_buffer(&mut i2c, slave_addr).await; + + info!("Async Direct API tests OK"); + + // Transaction tests + info!("=== Transaction Test 6: Consecutive Writes (Should Merge) ==="); + test_consecutive_writes_async(&mut i2c, slave_addr).await; + + info!("=== Transaction Test 7: Consecutive Reads (Should Merge) ==="); + test_consecutive_reads_async(&mut i2c, slave_addr).await; + + info!("=== Transaction Test 8: Write then Read (RESTART) ==="); + test_write_then_read_async(&mut i2c, slave_addr).await; + + info!("=== Transaction Test 9: Read then Write (RESTART) ==="); + test_read_then_write_async(&mut i2c, slave_addr).await; + + info!("=== Transaction Test 10: Complex Mixed Sequence ==="); + test_mixed_sequence_async(&mut i2c, slave_addr).await; + + info!("=== Transaction Test 11: Single Operations ==="); + test_single_operations_async(&mut i2c, slave_addr).await; + + info!("Async transaction tests OK"); + } + + info!("All tests OK"); + cortex_m::asm::bkpt(); +} + +// ==================== BLOCKING DIRECT API TEST FUNCTIONS ==================== + +fn test_blocking_write(i2c: &mut I2c<'_, Blocking, Master>, addr: u8) { + let write_data = [0x42, 0x43, 0x44, 0x45]; + + match i2c.blocking_write(addr, &write_data) { + Ok(_) => info!("✓ blocking_write succeeded: {:02x}", write_data), + Err(e) => { + error!("✗ blocking_write failed: {:?}", e); + defmt::panic!("Test failed: blocking_write"); + } + } +} + +fn test_blocking_read(i2c: &mut I2c<'_, Blocking, Master>, addr: u8) { + let mut read_buf = [0u8; 8]; + + match i2c.blocking_read(addr, &mut read_buf) { + Ok(_) => info!("✓ blocking_read succeeded: {:02x}", read_buf), + Err(e) => { + error!("✗ blocking_read failed: {:?}", e); + defmt::panic!("Test failed: blocking_read"); + } + } +} + +fn test_blocking_write_read(i2c: &mut I2c<'_, Blocking, Master>, addr: u8) { + let write_data = [0x50, 0x51]; + let mut read_buf = [0u8; 6]; + + match i2c.blocking_write_read(addr, &write_data, &mut read_buf) { + Ok(_) => { + info!("✓ blocking_write_read succeeded"); + info!(" Written: {:02x}", write_data); + info!(" Read: {:02x}", read_buf); + } + Err(e) => { + error!("✗ blocking_write_read failed: {:?}", e); + defmt::panic!("Test failed: blocking_write_read"); + } + } +} + +fn test_blocking_write_vectored(i2c: &mut I2c<'_, Blocking, Master>, addr: u8) { + let buf1 = [0x60, 0x61, 0x62]; + let buf2 = [0x70, 0x71]; + let buf3 = [0x80, 0x81, 0x82, 0x83]; + let bufs = [&buf1[..], &buf2[..], &buf3[..]]; + + match i2c.blocking_write_vectored(addr, &bufs) { + Ok(_) => info!("✓ blocking_write_vectored succeeded (9 bytes total)"), + Err(e) => { + error!("✗ blocking_write_vectored failed: {:?}", e); + defmt::panic!("Test failed: blocking_write_vectored"); + } + } +} + +fn test_blocking_large_buffer(i2c: &mut I2c<'_, Blocking, Master>, addr: u8) { + // Test with 300 bytes to verify RELOAD mechanism works (needs chunking at 255 bytes) + let mut write_buf = [0u8; 300]; + for (i, byte) in write_buf.iter_mut().enumerate() { + *byte = (i & 0xFF) as u8; + } + + match i2c.blocking_write(addr, &write_buf) { + Ok(_) => info!("✓ Large buffer write succeeded (300 bytes, tests RELOAD)"), + Err(e) => { + error!("✗ Large buffer write failed: {:?}", e); + defmt::panic!("Test failed: large buffer write"); + } + } + + // Test large read + let mut read_buf = [0u8; 300]; + match i2c.blocking_read(addr, &mut read_buf) { + Ok(_) => info!("✓ Large buffer read succeeded (300 bytes, tests RELOAD)"), + Err(e) => { + error!("✗ Large buffer read failed: {:?}", e); + defmt::panic!("Test failed: large buffer read"); + } + } +} + +// ==================== BLOCKING TRANSACTION TEST FUNCTIONS ==================== + +fn test_consecutive_writes_blocking(i2c: &mut I2c<'_, Blocking, Master>, addr: u8) { + // Expected on bus: START, ADDR+W, data1, data2, data3, STOP + // NO intermediate RESTART/STOP between writes - they should be merged + let data1 = [0x10, 0x11, 0x12]; + let data2 = [0x20, 0x21]; + let data3 = [0x30, 0x31, 0x32, 0x33]; + + let mut ops = [ + Operation::Write(&data1), + Operation::Write(&data2), + Operation::Write(&data3), + ]; + + match i2c.blocking_transaction(addr, &mut ops) { + Ok(_) => info!("✓ Consecutive writes succeeded (merged 9 bytes)"), + Err(e) => { + error!("✗ Consecutive writes failed: {:?}", e); + defmt::panic!("Test failed: consecutive writes"); + } + } +} + +fn test_consecutive_reads_blocking(i2c: &mut I2c<'_, Blocking, Master>, addr: u8) { + // Expected on bus: START, ADDR+R, data1, data2, data3, NACK, STOP + // NO intermediate RESTART/STOP between reads - they should be merged + let mut buf1 = [0u8; 4]; + let mut buf2 = [0u8; 3]; + let mut buf3 = [0u8; 2]; + + let mut ops = [ + Operation::Read(&mut buf1), + Operation::Read(&mut buf2), + Operation::Read(&mut buf3), + ]; + + match i2c.blocking_transaction(addr, &mut ops) { + Ok(_) => { + info!("✓ Consecutive reads succeeded (merged 9 bytes)"); + info!(" buf1: {:02x}", buf1); + info!(" buf2: {:02x}", buf2); + info!(" buf3: {:02x}", buf3); + } + Err(e) => { + error!("✗ Consecutive reads failed: {:?}", e); + defmt::panic!("Test failed: consecutive reads"); + } + } +} + +fn test_write_then_read_blocking(i2c: &mut I2c<'_, Blocking, Master>, addr: u8) { + // Expected: START, ADDR+W, data, RESTART, ADDR+R, data, NACK, STOP + let write_data = [0xAA, 0xBB]; + let mut read_buf = [0u8; 4]; + + let mut ops = [Operation::Write(&write_data), Operation::Read(&mut read_buf)]; + + match i2c.blocking_transaction(addr, &mut ops) { + Ok(_) => { + info!("✓ Write-then-read succeeded with RESTART"); + info!(" Written: {:02x}", write_data); + info!(" Read: {:02x}", read_buf); + } + Err(e) => { + error!("✗ Write-then-read failed: {:?}", e); + defmt::panic!("Test failed: write-then-read"); + } + } +} + +fn test_read_then_write_blocking(i2c: &mut I2c<'_, Blocking, Master>, addr: u8) { + // Expected: START, ADDR+R, data, NACK, RESTART, ADDR+W, data, STOP + let mut read_buf = [0u8; 3]; + let write_data = [0xCC, 0xDD, 0xEE]; + + let mut ops = [Operation::Read(&mut read_buf), Operation::Write(&write_data)]; + + match i2c.blocking_transaction(addr, &mut ops) { + Ok(_) => { + info!("✓ Read-then-write succeeded with RESTART"); + info!(" Read: {:02x}", read_buf); + info!(" Written: {:02x}", write_data); + } + Err(e) => { + error!("✗ Read-then-write failed: {:?}", e); + defmt::panic!("Test failed: read-then-write"); + } + } +} + +fn test_mixed_sequence_blocking(i2c: &mut I2c<'_, Blocking, Master>, addr: u8) { + // Complex: W, W, R, R, W, R + // Groups: [W,W] RESTART [R,R] RESTART [W] RESTART [R] + let w1 = [0x01, 0x02]; + let w2 = [0x03, 0x04]; + let mut r1 = [0u8; 2]; + let mut r2 = [0u8; 2]; + let w3 = [0x05]; + let mut r3 = [0u8; 1]; + + let mut ops = [ + Operation::Write(&w1), + Operation::Write(&w2), + Operation::Read(&mut r1), + Operation::Read(&mut r2), + Operation::Write(&w3), + Operation::Read(&mut r3), + ]; + + match i2c.blocking_transaction(addr, &mut ops) { + Ok(_) => { + info!("✓ Mixed sequence succeeded"); + info!(" Groups: [W4] RESTART [R4] RESTART [W1] RESTART [R1]"); + } + Err(e) => { + error!("✗ Mixed sequence failed: {:?}", e); + defmt::panic!("Test failed: mixed sequence"); + } + } +} + +fn test_single_operations_blocking(i2c: &mut I2c<'_, Blocking, Master>, addr: u8) { + // Test single write + let write_data = [0xFF]; + let mut ops = [Operation::Write(&write_data)]; + + match i2c.blocking_transaction(addr, &mut ops) { + Ok(_) => info!("✓ Single write succeeded"), + Err(e) => { + error!("✗ Single write failed: {:?}", e); + defmt::panic!("Test failed: single write"); + } + } + + // Test single read + let mut read_buf = [0u8; 1]; + let mut ops = [Operation::Read(&mut read_buf)]; + + match i2c.blocking_transaction(addr, &mut ops) { + Ok(_) => info!("✓ Single read succeeded, data: 0x{:02x}", read_buf[0]), + Err(e) => { + error!("✗ Single read failed: {:?}", e); + defmt::panic!("Test failed: single read"); + } + } +} + +// ==================== ASYNC DIRECT API TEST FUNCTIONS ==================== + +async fn test_async_write(i2c: &mut I2c<'_, Async, Master>, addr: u8) { + let write_data = [0x42, 0x43, 0x44, 0x45]; + + match i2c.write(addr, &write_data).await { + Ok(_) => info!("✓ async write succeeded: {:02x}", write_data), + Err(e) => { + error!("✗ async write failed: {:?}", e); + defmt::panic!("Test failed: async write"); + } + } +} + +async fn test_async_read(i2c: &mut I2c<'_, Async, Master>, addr: u8) { + let mut read_buf = [0u8; 8]; + + match i2c.read(addr, &mut read_buf).await { + Ok(_) => info!("✓ async read succeeded: {:02x}", read_buf), + Err(e) => { + error!("✗ async read failed: {:?}", e); + defmt::panic!("Test failed: async read"); + } + } +} + +async fn test_async_write_read(i2c: &mut I2c<'_, Async, Master>, addr: u8) { + let write_data = [0x50, 0x51]; + let mut read_buf = [0u8; 6]; + + match i2c.write_read(addr, &write_data, &mut read_buf).await { + Ok(_) => { + info!("✓ async write_read succeeded"); + info!(" Written: {:02x}", write_data); + info!(" Read: {:02x}", read_buf); + } + Err(e) => { + error!("✗ async write_read failed: {:?}", e); + defmt::panic!("Test failed: async write_read"); + } + } +} + +async fn test_async_write_vectored(i2c: &mut I2c<'_, Async, Master>, addr: u8) { + let buf1 = [0x60, 0x61, 0x62]; + let buf2 = [0x70, 0x71]; + let buf3 = [0x80, 0x81, 0x82, 0x83]; + let bufs = [&buf1[..], &buf2[..], &buf3[..]]; + + match i2c.write_vectored(addr.into(), &bufs).await { + Ok(_) => info!("✓ async write_vectored succeeded (9 bytes total)"), + Err(e) => { + error!("✗ async write_vectored failed: {:?}", e); + defmt::panic!("Test failed: async write_vectored"); + } + } +} + +async fn test_async_large_buffer(i2c: &mut I2c<'_, Async, Master>, addr: u8) { + // Test with 300 bytes to verify RELOAD mechanism works with DMA (needs chunking at 255 bytes) + let mut write_buf = [0u8; 300]; + for (i, byte) in write_buf.iter_mut().enumerate() { + *byte = (i & 0xFF) as u8; + } + + match i2c.write(addr, &write_buf).await { + Ok(_) => info!("✓ Large buffer async write succeeded (300 bytes, tests RELOAD with DMA)"), + Err(e) => { + error!("✗ Large buffer async write failed: {:?}", e); + defmt::panic!("Test failed: large buffer async write"); + } + } + + // Test large read + let mut read_buf = [0u8; 300]; + match i2c.read(addr, &mut read_buf).await { + Ok(_) => info!("✓ Large buffer async read succeeded (300 bytes, tests RELOAD with DMA)"), + Err(e) => { + error!("✗ Large buffer async read failed: {:?}", e); + defmt::panic!("Test failed: large buffer async read"); + } + } +} + +// ==================== ASYNC TRANSACTION TEST FUNCTIONS ==================== + +async fn test_consecutive_writes_async(i2c: &mut I2c<'_, Async, Master>, addr: u8) { + let data1 = [0x10, 0x11, 0x12]; + let data2 = [0x20, 0x21]; + let data3 = [0x30, 0x31, 0x32, 0x33]; + + let mut ops = [ + Operation::Write(&data1), + Operation::Write(&data2), + Operation::Write(&data3), + ]; + + match i2c.transaction(addr, &mut ops).await { + Ok(_) => info!("✓ Consecutive writes succeeded (merged 9 bytes)"), + Err(e) => { + error!("✗ Consecutive writes failed: {:?}", e); + defmt::panic!("Test failed: consecutive writes"); + } + } +} + +async fn test_consecutive_reads_async(i2c: &mut I2c<'_, Async, Master>, addr: u8) { + let mut buf1 = [0u8; 4]; + let mut buf2 = [0u8; 3]; + let mut buf3 = [0u8; 2]; + + let mut ops = [ + Operation::Read(&mut buf1), + Operation::Read(&mut buf2), + Operation::Read(&mut buf3), + ]; + + match i2c.transaction(addr, &mut ops).await { + Ok(_) => { + info!("✓ Consecutive reads succeeded (merged 9 bytes)"); + info!(" buf1: {:02x}", buf1); + info!(" buf2: {:02x}", buf2); + info!(" buf3: {:02x}", buf3); + } + Err(e) => { + error!("✗ Consecutive reads failed: {:?}", e); + defmt::panic!("Test failed: consecutive reads"); + } + } +} + +async fn test_write_then_read_async(i2c: &mut I2c<'_, Async, Master>, addr: u8) { + let write_data = [0xAA, 0xBB]; + let mut read_buf = [0u8; 4]; + + let mut ops = [Operation::Write(&write_data), Operation::Read(&mut read_buf)]; + + match i2c.transaction(addr, &mut ops).await { + Ok(_) => { + info!("✓ Write-then-read succeeded with RESTART"); + info!(" Written: {:02x}", write_data); + info!(" Read: {:02x}", read_buf); + } + Err(e) => { + error!("✗ Write-then-read failed: {:?}", e); + defmt::panic!("Test failed: write-then-read"); + } + } +} + +async fn test_read_then_write_async(i2c: &mut I2c<'_, Async, Master>, addr: u8) { + let mut read_buf = [0u8; 3]; + let write_data = [0xCC, 0xDD, 0xEE]; + + let mut ops = [Operation::Read(&mut read_buf), Operation::Write(&write_data)]; + + match i2c.transaction(addr, &mut ops).await { + Ok(_) => { + info!("✓ Read-then-write succeeded with RESTART"); + info!(" Read: {:02x}", read_buf); + info!(" Written: {:02x}", write_data); + } + Err(e) => { + error!("✗ Read-then-write failed: {:?}", e); + defmt::panic!("Test failed: read-then-write"); + } + } +} + +async fn test_mixed_sequence_async(i2c: &mut I2c<'_, Async, Master>, addr: u8) { + let w1 = [0x01, 0x02]; + let w2 = [0x03, 0x04]; + let mut r1 = [0u8; 2]; + let mut r2 = [0u8; 2]; + let w3 = [0x05]; + let mut r3 = [0u8; 1]; + + let mut ops = [ + Operation::Write(&w1), + Operation::Write(&w2), + Operation::Read(&mut r1), + Operation::Read(&mut r2), + Operation::Write(&w3), + Operation::Read(&mut r3), + ]; + + match i2c.transaction(addr, &mut ops).await { + Ok(_) => { + info!("✓ Mixed sequence succeeded"); + info!(" Groups: [W4] RESTART [R4] RESTART [W1] RESTART [R1]"); + } + Err(e) => { + error!("✗ Mixed sequence failed: {:?}", e); + defmt::panic!("Test failed: mixed sequence"); + } + } +} + +async fn test_single_operations_async(i2c: &mut I2c<'_, Async, Master>, addr: u8) { + // Test single write + let write_data = [0xFF]; + let mut ops = [Operation::Write(&write_data)]; + + match i2c.transaction(addr, &mut ops).await { + Ok(_) => info!("✓ Single write succeeded"), + Err(e) => { + error!("✗ Single write failed: {:?}", e); + defmt::panic!("Test failed: single write"); + } + } + + // Test single read + let mut read_buf = [0u8; 1]; + let mut ops = [Operation::Read(&mut read_buf)]; + + match i2c.transaction(addr, &mut ops).await { + Ok(_) => info!("✓ Single read succeeded, data: 0x{:02x}", read_buf[0]), + Err(e) => { + error!("✗ Single read failed: {:?}", e); + defmt::panic!("Test failed: single read"); + } + } +} diff --git a/tests/stm32/Cargo.toml b/tests/stm32/Cargo.toml index 1912a772c..b92b47be2 100644 --- a/tests/stm32/Cargo.toml +++ b/tests/stm32/Cargo.toml @@ -35,7 +35,6 @@ stm32wb55rg = ["embassy-stm32/stm32wb55rg", "chrono", "not-gpdma", "ble", "mac" stm32wba52cg = ["embassy-stm32/stm32wba52cg", "spi-v345", "chrono", "rng", "hash"] stm32wl55jc = ["embassy-stm32/stm32wl55jc-cm4", "not-gpdma", "rng", "chrono"] stm32f091rc = ["embassy-stm32/stm32f091rc", "cm0", "not-gpdma", "chrono"] -stm32f072rb = ["embassy-stm32/stm32f072rb", "cm0", "not-gpdma", "chrono"] stm32h503rb = ["embassy-stm32/stm32h503rb", "spi-v345", "rng", "stop"] stm32h7s3l8 = ["embassy-stm32/stm32h7s3l8", "spi-v345", "rng", "cordic", "hash-v34"] # TODO: fdcan crashes, cryp dma hangs. stm32u083rc = ["embassy-stm32/stm32u083rc", "cm0", "rng", "chrono"] @@ -160,11 +159,6 @@ name = "hash" path = "src/bin/hash.rs" required-features = [ "hash",] -[[bin]] -name = "i2c_v2_master" -path = "src/bin/i2c_v2_master.rs" -required-features = [ "stm32f072rb",] - [[bin]] name = "rng" path = "src/bin/rng.rs" @@ -291,7 +285,6 @@ build = [ { target = "thumbv7em-none-eabi", features = ["stm32wl55jc"], artifact-dir = "out/tests/stm32wl55jc" }, { target = "thumbv7em-none-eabi", features = ["stm32h7s3l8"], artifact-dir = "out/tests/stm32h7s3l8" }, { target = "thumbv6m-none-eabi", features = ["stm32f091rc"], artifact-dir = "out/tests/stm32f091rc" }, - { target = "thumbv6m-none-eabi", features = ["stm32f072rb"], artifact-dir = "out/tests/stm32f072rb" }, { target = "thumbv8m.main-none-eabihf", features = ["stm32h503rb"], artifact-dir = "out/tests/stm32h503rb" }, { target = "thumbv6m-none-eabi", features = ["stm32u083rc"], artifact-dir = "out/tests/stm32u083rc" } ] diff --git a/tests/stm32/build.rs b/tests/stm32/build.rs index 34030c206..556d77a20 100644 --- a/tests/stm32/build.rs +++ b/tests/stm32/build.rs @@ -15,7 +15,6 @@ fn main() -> Result<(), Box> { feature = "stm32c071rb", // 24 kb feature = "stm32l073rz", // 20 kb feature = "stm32h503rb", // 32 kb - feature = "stm32f072rb", // 16 kb - I2C v2 test with async too large for RAM // no VTOR, so interrupts can't work when running from RAM feature = "stm32f091rc", )) { diff --git a/tests/stm32/src/bin/i2c_v2_master.rs b/tests/stm32/src/bin/i2c_v2_master.rs deleted file mode 100644 index 34f9b48d3..000000000 --- a/tests/stm32/src/bin/i2c_v2_master.rs +++ /dev/null @@ -1,608 +0,0 @@ -#![no_std] -#![no_main] -// required-features: stm32f072rb -// -// Hardware Setup for NUCLEO-F072RB: -// - I2C1 pins: PB8 (SCL), PB9 (SDA) on CN5 connector -// - Connect to I2C slave device (e.g., Digilent Analog Discovery I2C slave, or EEPROM) -// - Default slave address: 0x50 -// - Pull-up resistors: 4.7kΩ on both SCL and SDA -// - CN5 Pin 10 (PB8/SCL) and CN5 Pin 9 (PB9/SDA) -// -// Analog Discovery - Waveforms Setup: -// - Increase buffer size: Settings -> Device Manager -> Option 4 -// - Run Protocol Analyzer -// - Configure as I2C Slave at address 0x50 -// - Connect and configure DIO pins for SCL and SDA -// - Frequency: 100kHz - [✓] Clock Stretching - -#[path = "../common.rs"] -mod common; - -use common::*; -use embassy_executor::Spawner; -use embassy_stm32::i2c::{Config, I2c, Master}; -use embassy_stm32::mode::{Async, Blocking}; -use embassy_stm32::time::Hertz; -use embassy_time::Timer; -use embedded_hal_1::i2c::Operation; - -#[embassy_executor::main] -async fn main(_spawner: Spawner) { - let p = init(); - info!("Run stm32 I2C v2 Master Tests..."); - - let mut i2c_peri = peri!(p, I2C); - let mut scl = peri!(p, I2C_SCL); - let mut sda = peri!(p, I2C_SDA); - - let mut config = Config::default(); - config.frequency = Hertz(100_000); - - // I2C slave address for Analog Discovery or test EEPROM - let slave_addr = 0x50u8; - - // Wait for slave device to be ready - Timer::after_millis(100).await; - - // ========== BLOCKING DIRECT API TESTS ========== - info!("========== BLOCKING DIRECT API TESTS =========="); - { - let mut i2c = I2c::new_blocking(i2c_peri.reborrow(), scl.reborrow(), sda.reborrow(), config); - - info!("=== Test 1: Direct blocking_write ==="); - test_blocking_write(&mut i2c, slave_addr); - - info!("=== Test 2: Direct blocking_read ==="); - test_blocking_read(&mut i2c, slave_addr); - - info!("=== Test 3: Direct blocking_write_read ==="); - test_blocking_write_read(&mut i2c, slave_addr); - - info!("=== Test 4: Direct blocking_write_vectored ==="); - test_blocking_write_vectored(&mut i2c, slave_addr); - - info!("=== Test 5: Large buffer (>255 bytes) ==="); - test_blocking_large_buffer(&mut i2c, slave_addr); - - info!("Blocking direct API tests OK"); - } - - Timer::after_millis(100).await; - - // ========== BLOCKING TRANSACTION TESTS ========== - info!("========== BLOCKING TRANSACTION TESTS =========="); - { - let mut i2c = I2c::new_blocking(i2c_peri.reborrow(), scl.reborrow(), sda.reborrow(), config); - - info!("=== Test 6: Consecutive Writes (Should Merge) ==="); - test_consecutive_writes_blocking(&mut i2c, slave_addr); - - info!("=== Test 7: Consecutive Reads (Should Merge) ==="); - test_consecutive_reads_blocking(&mut i2c, slave_addr); - - info!("=== Test 8: Write then Read (RESTART) ==="); - test_write_then_read_blocking(&mut i2c, slave_addr); - - info!("=== Test 9: Read then Write (RESTART) ==="); - test_read_then_write_blocking(&mut i2c, slave_addr); - - info!("=== Test 10: Complex Mixed Sequence ==="); - test_mixed_sequence_blocking(&mut i2c, slave_addr); - - info!("=== Test 11: Single Operations ==="); - test_single_operations_blocking(&mut i2c, slave_addr); - - info!("Blocking transaction tests OK"); - } - - Timer::after_millis(100).await; - - // ========== ASYNC TESTS (DMA) ========== - info!("========== ASYNC TESTS (DMA) =========="); - { - let tx_dma = peri!(p, I2C_TX_DMA); - let rx_dma = peri!(p, I2C_RX_DMA); - let irq = irqs!(I2C); - - let mut i2c = I2c::new(i2c_peri, scl, sda, irq, tx_dma, rx_dma, config); - - // Direct API tests (reusing same I2C instance) - info!("=== Direct API Test 1: write() ==="); - test_async_write(&mut i2c, slave_addr).await; - - info!("=== Direct API Test 2: read() ==="); - test_async_read(&mut i2c, slave_addr).await; - - info!("=== Direct API Test 3: write_read() ==="); - test_async_write_read(&mut i2c, slave_addr).await; - - info!("=== Direct API Test 4: write_vectored() ==="); - test_async_write_vectored(&mut i2c, slave_addr).await; - - info!("=== Direct API Test 5: Large buffer (>255 bytes) ==="); - test_async_large_buffer(&mut i2c, slave_addr).await; - - info!("Async Direct API tests OK"); - - // Transaction tests - info!("=== Transaction Test 6: Consecutive Writes (Should Merge) ==="); - test_consecutive_writes_async(&mut i2c, slave_addr).await; - - info!("=== Transaction Test 7: Consecutive Reads (Should Merge) ==="); - test_consecutive_reads_async(&mut i2c, slave_addr).await; - - info!("=== Transaction Test 8: Write then Read (RESTART) ==="); - test_write_then_read_async(&mut i2c, slave_addr).await; - - info!("=== Transaction Test 9: Read then Write (RESTART) ==="); - test_read_then_write_async(&mut i2c, slave_addr).await; - - info!("=== Transaction Test 10: Complex Mixed Sequence ==="); - test_mixed_sequence_async(&mut i2c, slave_addr).await; - - info!("=== Transaction Test 11: Single Operations ==="); - test_single_operations_async(&mut i2c, slave_addr).await; - - info!("Async transaction tests OK"); - } - - info!("All tests OK"); - cortex_m::asm::bkpt(); -} - -// ==================== BLOCKING DIRECT API TEST FUNCTIONS ==================== - -fn test_blocking_write(i2c: &mut I2c<'_, Blocking, Master>, addr: u8) { - let write_data = [0x42, 0x43, 0x44, 0x45]; - - match i2c.blocking_write(addr, &write_data) { - Ok(_) => info!("✓ blocking_write succeeded: {:02x}", write_data), - Err(e) => { - error!("✗ blocking_write failed: {:?}", e); - defmt::panic!("Test failed: blocking_write"); - } - } -} - -fn test_blocking_read(i2c: &mut I2c<'_, Blocking, Master>, addr: u8) { - let mut read_buf = [0u8; 8]; - - match i2c.blocking_read(addr, &mut read_buf) { - Ok(_) => info!("✓ blocking_read succeeded: {:02x}", read_buf), - Err(e) => { - error!("✗ blocking_read failed: {:?}", e); - defmt::panic!("Test failed: blocking_read"); - } - } -} - -fn test_blocking_write_read(i2c: &mut I2c<'_, Blocking, Master>, addr: u8) { - let write_data = [0x50, 0x51]; - let mut read_buf = [0u8; 6]; - - match i2c.blocking_write_read(addr, &write_data, &mut read_buf) { - Ok(_) => { - info!("✓ blocking_write_read succeeded"); - info!(" Written: {:02x}", write_data); - info!(" Read: {:02x}", read_buf); - } - Err(e) => { - error!("✗ blocking_write_read failed: {:?}", e); - defmt::panic!("Test failed: blocking_write_read"); - } - } -} - -fn test_blocking_write_vectored(i2c: &mut I2c<'_, Blocking, Master>, addr: u8) { - let buf1 = [0x60, 0x61, 0x62]; - let buf2 = [0x70, 0x71]; - let buf3 = [0x80, 0x81, 0x82, 0x83]; - let bufs = [&buf1[..], &buf2[..], &buf3[..]]; - - match i2c.blocking_write_vectored(addr, &bufs) { - Ok(_) => info!("✓ blocking_write_vectored succeeded (9 bytes total)"), - Err(e) => { - error!("✗ blocking_write_vectored failed: {:?}", e); - defmt::panic!("Test failed: blocking_write_vectored"); - } - } -} - -fn test_blocking_large_buffer(i2c: &mut I2c<'_, Blocking, Master>, addr: u8) { - // Test with 300 bytes to verify RELOAD mechanism works (needs chunking at 255 bytes) - let mut write_buf = [0u8; 300]; - for (i, byte) in write_buf.iter_mut().enumerate() { - *byte = (i & 0xFF) as u8; - } - - match i2c.blocking_write(addr, &write_buf) { - Ok(_) => info!("✓ Large buffer write succeeded (300 bytes, tests RELOAD)"), - Err(e) => { - error!("✗ Large buffer write failed: {:?}", e); - defmt::panic!("Test failed: large buffer write"); - } - } - - // Test large read - let mut read_buf = [0u8; 300]; - match i2c.blocking_read(addr, &mut read_buf) { - Ok(_) => info!("✓ Large buffer read succeeded (300 bytes, tests RELOAD)"), - Err(e) => { - error!("✗ Large buffer read failed: {:?}", e); - defmt::panic!("Test failed: large buffer read"); - } - } -} - -// ==================== BLOCKING TRANSACTION TEST FUNCTIONS ==================== - -fn test_consecutive_writes_blocking(i2c: &mut I2c<'_, Blocking, Master>, addr: u8) { - // Expected on bus: START, ADDR+W, data1, data2, data3, STOP - // NO intermediate RESTART/STOP between writes - they should be merged - let data1 = [0x10, 0x11, 0x12]; - let data2 = [0x20, 0x21]; - let data3 = [0x30, 0x31, 0x32, 0x33]; - - let mut ops = [ - Operation::Write(&data1), - Operation::Write(&data2), - Operation::Write(&data3), - ]; - - match i2c.blocking_transaction(addr, &mut ops) { - Ok(_) => info!("✓ Consecutive writes succeeded (merged 9 bytes)"), - Err(e) => { - error!("✗ Consecutive writes failed: {:?}", e); - defmt::panic!("Test failed: consecutive writes"); - } - } -} - -fn test_consecutive_reads_blocking(i2c: &mut I2c<'_, Blocking, Master>, addr: u8) { - // Expected on bus: START, ADDR+R, data1, data2, data3, NACK, STOP - // NO intermediate RESTART/STOP between reads - they should be merged - let mut buf1 = [0u8; 4]; - let mut buf2 = [0u8; 3]; - let mut buf3 = [0u8; 2]; - - let mut ops = [ - Operation::Read(&mut buf1), - Operation::Read(&mut buf2), - Operation::Read(&mut buf3), - ]; - - match i2c.blocking_transaction(addr, &mut ops) { - Ok(_) => { - info!("✓ Consecutive reads succeeded (merged 9 bytes)"); - info!(" buf1: {:02x}", buf1); - info!(" buf2: {:02x}", buf2); - info!(" buf3: {:02x}", buf3); - } - Err(e) => { - error!("✗ Consecutive reads failed: {:?}", e); - defmt::panic!("Test failed: consecutive reads"); - } - } -} - -fn test_write_then_read_blocking(i2c: &mut I2c<'_, Blocking, Master>, addr: u8) { - // Expected: START, ADDR+W, data, RESTART, ADDR+R, data, NACK, STOP - let write_data = [0xAA, 0xBB]; - let mut read_buf = [0u8; 4]; - - let mut ops = [Operation::Write(&write_data), Operation::Read(&mut read_buf)]; - - match i2c.blocking_transaction(addr, &mut ops) { - Ok(_) => { - info!("✓ Write-then-read succeeded with RESTART"); - info!(" Written: {:02x}", write_data); - info!(" Read: {:02x}", read_buf); - } - Err(e) => { - error!("✗ Write-then-read failed: {:?}", e); - defmt::panic!("Test failed: write-then-read"); - } - } -} - -fn test_read_then_write_blocking(i2c: &mut I2c<'_, Blocking, Master>, addr: u8) { - // Expected: START, ADDR+R, data, NACK, RESTART, ADDR+W, data, STOP - let mut read_buf = [0u8; 3]; - let write_data = [0xCC, 0xDD, 0xEE]; - - let mut ops = [Operation::Read(&mut read_buf), Operation::Write(&write_data)]; - - match i2c.blocking_transaction(addr, &mut ops) { - Ok(_) => { - info!("✓ Read-then-write succeeded with RESTART"); - info!(" Read: {:02x}", read_buf); - info!(" Written: {:02x}", write_data); - } - Err(e) => { - error!("✗ Read-then-write failed: {:?}", e); - defmt::panic!("Test failed: read-then-write"); - } - } -} - -fn test_mixed_sequence_blocking(i2c: &mut I2c<'_, Blocking, Master>, addr: u8) { - // Complex: W, W, R, R, W, R - // Groups: [W,W] RESTART [R,R] RESTART [W] RESTART [R] - let w1 = [0x01, 0x02]; - let w2 = [0x03, 0x04]; - let mut r1 = [0u8; 2]; - let mut r2 = [0u8; 2]; - let w3 = [0x05]; - let mut r3 = [0u8; 1]; - - let mut ops = [ - Operation::Write(&w1), - Operation::Write(&w2), - Operation::Read(&mut r1), - Operation::Read(&mut r2), - Operation::Write(&w3), - Operation::Read(&mut r3), - ]; - - match i2c.blocking_transaction(addr, &mut ops) { - Ok(_) => { - info!("✓ Mixed sequence succeeded"); - info!(" Groups: [W4] RESTART [R4] RESTART [W1] RESTART [R1]"); - } - Err(e) => { - error!("✗ Mixed sequence failed: {:?}", e); - defmt::panic!("Test failed: mixed sequence"); - } - } -} - -fn test_single_operations_blocking(i2c: &mut I2c<'_, Blocking, Master>, addr: u8) { - // Test single write - let write_data = [0xFF]; - let mut ops = [Operation::Write(&write_data)]; - - match i2c.blocking_transaction(addr, &mut ops) { - Ok(_) => info!("✓ Single write succeeded"), - Err(e) => { - error!("✗ Single write failed: {:?}", e); - defmt::panic!("Test failed: single write"); - } - } - - // Test single read - let mut read_buf = [0u8; 1]; - let mut ops = [Operation::Read(&mut read_buf)]; - - match i2c.blocking_transaction(addr, &mut ops) { - Ok(_) => info!("✓ Single read succeeded, data: 0x{:02x}", read_buf[0]), - Err(e) => { - error!("✗ Single read failed: {:?}", e); - defmt::panic!("Test failed: single read"); - } - } -} - -// ==================== ASYNC DIRECT API TEST FUNCTIONS ==================== - -async fn test_async_write(i2c: &mut I2c<'_, Async, Master>, addr: u8) { - let write_data = [0x42, 0x43, 0x44, 0x45]; - - match i2c.write(addr, &write_data).await { - Ok(_) => info!("✓ async write succeeded: {:02x}", write_data), - Err(e) => { - error!("✗ async write failed: {:?}", e); - defmt::panic!("Test failed: async write"); - } - } -} - -async fn test_async_read(i2c: &mut I2c<'_, Async, Master>, addr: u8) { - let mut read_buf = [0u8; 8]; - - match i2c.read(addr, &mut read_buf).await { - Ok(_) => info!("✓ async read succeeded: {:02x}", read_buf), - Err(e) => { - error!("✗ async read failed: {:?}", e); - defmt::panic!("Test failed: async read"); - } - } -} - -async fn test_async_write_read(i2c: &mut I2c<'_, Async, Master>, addr: u8) { - let write_data = [0x50, 0x51]; - let mut read_buf = [0u8; 6]; - - match i2c.write_read(addr, &write_data, &mut read_buf).await { - Ok(_) => { - info!("✓ async write_read succeeded"); - info!(" Written: {:02x}", write_data); - info!(" Read: {:02x}", read_buf); - } - Err(e) => { - error!("✗ async write_read failed: {:?}", e); - defmt::panic!("Test failed: async write_read"); - } - } -} - -async fn test_async_write_vectored(i2c: &mut I2c<'_, Async, Master>, addr: u8) { - let buf1 = [0x60, 0x61, 0x62]; - let buf2 = [0x70, 0x71]; - let buf3 = [0x80, 0x81, 0x82, 0x83]; - let bufs = [&buf1[..], &buf2[..], &buf3[..]]; - - match i2c.write_vectored(addr.into(), &bufs).await { - Ok(_) => info!("✓ async write_vectored succeeded (9 bytes total)"), - Err(e) => { - error!("✗ async write_vectored failed: {:?}", e); - defmt::panic!("Test failed: async write_vectored"); - } - } -} - -async fn test_async_large_buffer(i2c: &mut I2c<'_, Async, Master>, addr: u8) { - // Test with 300 bytes to verify RELOAD mechanism works with DMA (needs chunking at 255 bytes) - let mut write_buf = [0u8; 300]; - for (i, byte) in write_buf.iter_mut().enumerate() { - *byte = (i & 0xFF) as u8; - } - - match i2c.write(addr, &write_buf).await { - Ok(_) => info!("✓ Large buffer async write succeeded (300 bytes, tests RELOAD with DMA)"), - Err(e) => { - error!("✗ Large buffer async write failed: {:?}", e); - defmt::panic!("Test failed: large buffer async write"); - } - } - - // Test large read - let mut read_buf = [0u8; 300]; - match i2c.read(addr, &mut read_buf).await { - Ok(_) => info!("✓ Large buffer async read succeeded (300 bytes, tests RELOAD with DMA)"), - Err(e) => { - error!("✗ Large buffer async read failed: {:?}", e); - defmt::panic!("Test failed: large buffer async read"); - } - } -} - -// ==================== ASYNC TRANSACTION TEST FUNCTIONS ==================== - -async fn test_consecutive_writes_async(i2c: &mut I2c<'_, Async, Master>, addr: u8) { - let data1 = [0x10, 0x11, 0x12]; - let data2 = [0x20, 0x21]; - let data3 = [0x30, 0x31, 0x32, 0x33]; - - let mut ops = [ - Operation::Write(&data1), - Operation::Write(&data2), - Operation::Write(&data3), - ]; - - match i2c.transaction(addr, &mut ops).await { - Ok(_) => info!("✓ Consecutive writes succeeded (merged 9 bytes)"), - Err(e) => { - error!("✗ Consecutive writes failed: {:?}", e); - defmt::panic!("Test failed: consecutive writes"); - } - } -} - -async fn test_consecutive_reads_async(i2c: &mut I2c<'_, Async, Master>, addr: u8) { - let mut buf1 = [0u8; 4]; - let mut buf2 = [0u8; 3]; - let mut buf3 = [0u8; 2]; - - let mut ops = [ - Operation::Read(&mut buf1), - Operation::Read(&mut buf2), - Operation::Read(&mut buf3), - ]; - - match i2c.transaction(addr, &mut ops).await { - Ok(_) => { - info!("✓ Consecutive reads succeeded (merged 9 bytes)"); - info!(" buf1: {:02x}", buf1); - info!(" buf2: {:02x}", buf2); - info!(" buf3: {:02x}", buf3); - } - Err(e) => { - error!("✗ Consecutive reads failed: {:?}", e); - defmt::panic!("Test failed: consecutive reads"); - } - } -} - -async fn test_write_then_read_async(i2c: &mut I2c<'_, Async, Master>, addr: u8) { - let write_data = [0xAA, 0xBB]; - let mut read_buf = [0u8; 4]; - - let mut ops = [Operation::Write(&write_data), Operation::Read(&mut read_buf)]; - - match i2c.transaction(addr, &mut ops).await { - Ok(_) => { - info!("✓ Write-then-read succeeded with RESTART"); - info!(" Written: {:02x}", write_data); - info!(" Read: {:02x}", read_buf); - } - Err(e) => { - error!("✗ Write-then-read failed: {:?}", e); - defmt::panic!("Test failed: write-then-read"); - } - } -} - -async fn test_read_then_write_async(i2c: &mut I2c<'_, Async, Master>, addr: u8) { - let mut read_buf = [0u8; 3]; - let write_data = [0xCC, 0xDD, 0xEE]; - - let mut ops = [Operation::Read(&mut read_buf), Operation::Write(&write_data)]; - - match i2c.transaction(addr, &mut ops).await { - Ok(_) => { - info!("✓ Read-then-write succeeded with RESTART"); - info!(" Read: {:02x}", read_buf); - info!(" Written: {:02x}", write_data); - } - Err(e) => { - error!("✗ Read-then-write failed: {:?}", e); - defmt::panic!("Test failed: read-then-write"); - } - } -} - -async fn test_mixed_sequence_async(i2c: &mut I2c<'_, Async, Master>, addr: u8) { - let w1 = [0x01, 0x02]; - let w2 = [0x03, 0x04]; - let mut r1 = [0u8; 2]; - let mut r2 = [0u8; 2]; - let w3 = [0x05]; - let mut r3 = [0u8; 1]; - - let mut ops = [ - Operation::Write(&w1), - Operation::Write(&w2), - Operation::Read(&mut r1), - Operation::Read(&mut r2), - Operation::Write(&w3), - Operation::Read(&mut r3), - ]; - - match i2c.transaction(addr, &mut ops).await { - Ok(_) => { - info!("✓ Mixed sequence succeeded"); - info!(" Groups: [W4] RESTART [R4] RESTART [W1] RESTART [R1]"); - } - Err(e) => { - error!("✗ Mixed sequence failed: {:?}", e); - defmt::panic!("Test failed: mixed sequence"); - } - } -} - -async fn test_single_operations_async(i2c: &mut I2c<'_, Async, Master>, addr: u8) { - // Test single write - let write_data = [0xFF]; - let mut ops = [Operation::Write(&write_data)]; - - match i2c.transaction(addr, &mut ops).await { - Ok(_) => info!("✓ Single write succeeded"), - Err(e) => { - error!("✗ Single write failed: {:?}", e); - defmt::panic!("Test failed: single write"); - } - } - - // Test single read - let mut read_buf = [0u8; 1]; - let mut ops = [Operation::Read(&mut read_buf)]; - - match i2c.transaction(addr, &mut ops).await { - Ok(_) => info!("✓ Single read succeeded, data: 0x{:02x}", read_buf[0]), - Err(e) => { - error!("✗ Single read failed: {:?}", e); - defmt::panic!("Test failed: single read"); - } - } -} diff --git a/tests/stm32/src/common.rs b/tests/stm32/src/common.rs index de06cb267..096cce947 100644 --- a/tests/stm32/src/common.rs +++ b/tests/stm32/src/common.rs @@ -60,8 +60,6 @@ teleprobe_meta::target!(b"nucleo-stm32wl55jc"); teleprobe_meta::target!(b"nucleo-stm32wba52cg"); #[cfg(feature = "stm32f091rc")] teleprobe_meta::target!(b"nucleo-stm32f091rc"); -#[cfg(feature = "stm32f072rb")] -teleprobe_meta::target!(b"nucleo-stm32f072rb"); #[cfg(feature = "stm32h503rb")] teleprobe_meta::target!(b"nucleo-stm32h503rb"); #[cfg(feature = "stm32h7s3l8")] @@ -105,14 +103,6 @@ define_peris!( SPI = SPI1, SPI_SCK = PA5, SPI_MOSI = PA7, SPI_MISO = PA6, SPI_TX_DMA = DMA1_CH3, SPI_RX_DMA = DMA1_CH2, @irq UART = {USART1 => embassy_stm32::usart::InterruptHandler;}, ); -#[cfg(feature = "stm32f072rb")] -define_peris!( - UART = USART1, UART_TX = PA9, UART_RX = PA10, UART_TX_DMA = DMA1_CH4, UART_RX_DMA = DMA1_CH5, - SPI = SPI1, SPI_SCK = PA5, SPI_MOSI = PA7, SPI_MISO = PA6, SPI_TX_DMA = DMA1_CH3, SPI_RX_DMA = DMA1_CH2, - I2C = I2C1, I2C_SCL = PB8, I2C_SDA = PB9, I2C_TX_DMA = DMA1_CH6, I2C_RX_DMA = DMA1_CH7, - @irq UART = {USART1 => embassy_stm32::usart::InterruptHandler;}, - @irq I2C = {I2C1 => embassy_stm32::i2c::EventInterruptHandler, embassy_stm32::i2c::ErrorInterruptHandler;}, -); #[cfg(any(feature = "stm32f100rd", feature = "stm32f103c8", feature = "stm32f107vc"))] define_peris!( UART = USART1, UART_TX = PA9, UART_RX = PA10, UART_TX_DMA = DMA1_CH4, UART_RX_DMA = DMA1_CH5, @@ -335,21 +325,6 @@ pub fn config() -> Config { config.rcc.ahb_pre = AHBPrescaler::DIV1; config.rcc.apb1_pre = APBPrescaler::DIV1; } - #[cfg(feature = "stm32f072rb")] - { - config.rcc.hse = Some(Hse { - freq: Hertz(8_000_000), - mode: HseMode::Bypass, - }); - config.rcc.pll = Some(Pll { - src: PllSource::HSE, - prediv: PllPreDiv::DIV1, - mul: PllMul::MUL6, - }); - config.rcc.sys = Sysclk::PLL1_P; - config.rcc.ahb_pre = AHBPrescaler::DIV1; - config.rcc.apb1_pre = APBPrescaler::DIV1; - } #[cfg(feature = "stm32f103c8")] { config.rcc.hse = Some(Hse { -- cgit From de582f7d5ed9d4903fb5f07d7816f67a907c1d47 Mon Sep 17 00:00:00 2001 From: xoviat Date: Thu, 13 Nov 2025 08:51:14 -0600 Subject: adc: extract prescalers --- embassy-stm32/build.rs | 124 ++++++++++++++++++++++++------------------ embassy-stm32/src/adc/adc4.rs | 82 +++++----------------------- embassy-stm32/src/adc/g4.rs | 82 +++++----------------------- embassy-stm32/src/adc/v4.rs | 82 +++++----------------------- 4 files changed, 113 insertions(+), 257 deletions(-) diff --git a/embassy-stm32/build.rs b/embassy-stm32/build.rs index ad6743f96..67041a9e0 100644 --- a/embassy-stm32/build.rs +++ b/embassy-stm32/build.rs @@ -1695,70 +1695,88 @@ fn main() { } // ======== - // Generate Div/Mul impls for RCC prescalers/dividers/multipliers. - for e in rcc_registers.ir.enums { - fn is_rcc_name(e: &str) -> bool { - match e { - "Pllp" | "Pllq" | "Pllr" | "Plldivst" | "Pllm" | "Plln" | "Prediv1" | "Prediv2" | "Hpre5" => true, - "Timpre" | "Pllrclkpre" => false, - e if e.ends_with("pre") || e.ends_with("pres") || e.ends_with("div") || e.ends_with("mul") => true, - _ => false, + // Generate Div/Mul impls for RCC and ADC prescalers/dividers/multipliers. + for (kind, psc_enums) in ["rcc", "adc", "adccommon"].iter().filter_map(|kind| { + METADATA + .peripherals + .iter() + .filter_map(|p| p.registers.as_ref()) + .find(|r| r.kind == *kind) + .map(|r| (*kind, r.ir.enums)) + }) { + for e in psc_enums.iter() { + fn is_adc_name(e: &str) -> bool { + match e { + "Presc" | "Adc4Presc" => true, + _ => false, + } } - } - fn parse_num(n: &str) -> Result { - for prefix in ["DIV", "MUL"] { - if let Some(n) = n.strip_prefix(prefix) { - let exponent = n.find('_').map(|e| n.len() - 1 - e).unwrap_or(0) as u32; - let mantissa = n.replace('_', "").parse().map_err(|_| ())?; - let f = Frac { - num: mantissa, - denom: 10u32.pow(exponent), - }; - return Ok(f.simplify()); + fn is_rcc_name(e: &str) -> bool { + match e { + "Pllp" | "Pllq" | "Pllr" | "Plldivst" | "Pllm" | "Plln" | "Prediv1" | "Prediv2" | "Hpre5" => true, + "Timpre" | "Pllrclkpre" => false, + e if e.ends_with("pre") || e.ends_with("pres") || e.ends_with("div") || e.ends_with("mul") => true, + _ => false, } } - Err(()) - } - if is_rcc_name(e.name) { - let enum_name = format_ident!("{}", e.name); - let mut muls = Vec::new(); - let mut divs = Vec::new(); - for v in e.variants { - let Ok(val) = parse_num(v.name) else { - panic!("could not parse mul/div. enum={} variant={}", e.name, v.name) - }; - let variant_name = format_ident!("{}", v.name); - let variant = quote!(crate::pac::rcc::vals::#enum_name::#variant_name); - let num = val.num; - let denom = val.denom; - muls.push(quote!(#variant => self * #num / #denom,)); - divs.push(quote!(#variant => self * #denom / #num,)); + fn parse_num(n: &str) -> Result { + for prefix in ["DIV", "MUL"] { + if let Some(n) = n.strip_prefix(prefix) { + let exponent = n.find('_').map(|e| n.len() - 1 - e).unwrap_or(0) as u32; + let mantissa = n.replace('_', "").parse().map_err(|_| ())?; + let f = Frac { + num: mantissa, + denom: 10u32.pow(exponent), + }; + return Ok(f.simplify()); + } + } + Err(()) } - g.extend(quote! { - impl core::ops::Div for crate::time::Hertz { - type Output = crate::time::Hertz; - fn div(self, rhs: crate::pac::rcc::vals::#enum_name) -> Self::Output { - match rhs { - #(#divs)* - #[allow(unreachable_patterns)] - _ => unreachable!(), + if (kind == "rcc" && is_rcc_name(e.name)) || ((kind == "adccommon" || kind == "adc") && is_adc_name(e.name)) + { + let kind = format_ident!("{}", kind); + let enum_name = format_ident!("{}", e.name); + let mut muls = Vec::new(); + let mut divs = Vec::new(); + for v in e.variants { + let Ok(val) = parse_num(v.name) else { + panic!("could not parse mul/div. enum={} variant={}", e.name, v.name) + }; + let variant_name = format_ident!("{}", v.name); + let variant = quote!(crate::pac::#kind::vals::#enum_name::#variant_name); + let num = val.num; + let denom = val.denom; + muls.push(quote!(#variant => self * #num / #denom,)); + divs.push(quote!(#variant => self * #denom / #num,)); + } + + g.extend(quote! { + impl core::ops::Div for crate::time::Hertz { + type Output = crate::time::Hertz; + fn div(self, rhs: crate::pac::#kind::vals::#enum_name) -> Self::Output { + match rhs { + #(#divs)* + #[allow(unreachable_patterns)] + _ => unreachable!(), + } } } - } - impl core::ops::Mul for crate::time::Hertz { - type Output = crate::time::Hertz; - fn mul(self, rhs: crate::pac::rcc::vals::#enum_name) -> Self::Output { - match rhs { - #(#muls)* - #[allow(unreachable_patterns)] - _ => unreachable!(), + impl core::ops::Mul for crate::time::Hertz { + type Output = crate::time::Hertz; + fn mul(self, rhs: crate::pac::#kind::vals::#enum_name) -> Self::Output { + match rhs { + #(#muls)* + #[allow(unreachable_patterns)] + _ => unreachable!(), + } } } - } - }); + }); + } } } diff --git a/embassy-stm32/src/adc/adc4.rs b/embassy-stm32/src/adc/adc4.rs index 52678d1b6..d816eea57 100644 --- a/embassy-stm32/src/adc/adc4.rs +++ b/embassy-stm32/src/adc/adc4.rs @@ -76,71 +76,17 @@ pub const fn resolution_to_max_count(res: Resolution) -> u32 { } } -// NOTE (unused): The prescaler enum closely copies the hardware capabilities, -// but high prescaling doesn't make a lot of sense in the current implementation and is ommited. -#[allow(unused)] -enum Prescaler { - NotDivided, - DividedBy2, - DividedBy4, - DividedBy6, - DividedBy8, - DividedBy10, - DividedBy12, - DividedBy16, - DividedBy32, - DividedBy64, - DividedBy128, - DividedBy256, -} - -impl Prescaler { - fn from_ker_ck(frequency: Hertz) -> Self { - let raw_prescaler = frequency.0 / MAX_ADC_CLK_FREQ.0; - match raw_prescaler { - 0 => Self::NotDivided, - 1 => Self::DividedBy2, - 2..=3 => Self::DividedBy4, - 4..=5 => Self::DividedBy6, - 6..=7 => Self::DividedBy8, - 8..=9 => Self::DividedBy10, - 10..=11 => Self::DividedBy12, - _ => unimplemented!(), - } - } - - fn divisor(&self) -> u32 { - match self { - Prescaler::NotDivided => 1, - Prescaler::DividedBy2 => 2, - Prescaler::DividedBy4 => 4, - Prescaler::DividedBy6 => 6, - Prescaler::DividedBy8 => 8, - Prescaler::DividedBy10 => 10, - Prescaler::DividedBy12 => 12, - Prescaler::DividedBy16 => 16, - Prescaler::DividedBy32 => 32, - Prescaler::DividedBy64 => 64, - Prescaler::DividedBy128 => 128, - Prescaler::DividedBy256 => 256, - } - } - - fn presc(&self) -> Presc { - match self { - Prescaler::NotDivided => Presc::DIV1, - Prescaler::DividedBy2 => Presc::DIV2, - Prescaler::DividedBy4 => Presc::DIV4, - Prescaler::DividedBy6 => Presc::DIV6, - Prescaler::DividedBy8 => Presc::DIV8, - Prescaler::DividedBy10 => Presc::DIV10, - Prescaler::DividedBy12 => Presc::DIV12, - Prescaler::DividedBy16 => Presc::DIV16, - Prescaler::DividedBy32 => Presc::DIV32, - Prescaler::DividedBy64 => Presc::DIV64, - Prescaler::DividedBy128 => Presc::DIV128, - Prescaler::DividedBy256 => Presc::DIV256, - } +fn from_ker_ck(frequency: Hertz) -> Presc { + let raw_prescaler = frequency.0 / MAX_ADC_CLK_FREQ.0; + match raw_prescaler { + 0 => Presc::DIV1, + 1 => Presc::DIV2, + 2..=3 => Presc::DIV4, + 4..=5 => Presc::DIV6, + 6..=7 => Presc::DIV8, + 8..=9 => Presc::DIV10, + 10..=11 => Presc::DIV12, + _ => unimplemented!(), } } @@ -283,11 +229,11 @@ impl<'d, T: Instance + super::AnyInstance> super::Adc<'d, T> { /// Create a new ADC driver. pub fn new_adc4(adc: Peri<'d, T>) -> Self { rcc::enable_and_reset::(); - let prescaler = Prescaler::from_ker_ck(T::frequency()); + let prescaler = from_ker_ck(T::frequency()); - T::regs().ccr().modify(|w| w.set_presc(prescaler.presc())); + T::regs().ccr().modify(|w| w.set_presc(prescaler)); - let frequency = Hertz(T::frequency().0 / prescaler.divisor()); + let frequency = T::frequency() / prescaler; info!("ADC4 frequency set to {}", frequency); if frequency > MAX_ADC_CLK_FREQ { diff --git a/embassy-stm32/src/adc/g4.rs b/embassy-stm32/src/adc/g4.rs index 71dc8acc0..514734017 100644 --- a/embassy-stm32/src/adc/g4.rs +++ b/embassy-stm32/src/adc/g4.rs @@ -32,71 +32,17 @@ const MAX_ADC_CLK_FREQ: Hertz = Hertz::mhz(60); #[cfg(stm32h7)] const MAX_ADC_CLK_FREQ: Hertz = Hertz::mhz(50); -// NOTE (unused): The prescaler enum closely copies the hardware capabilities, -// but high prescaling doesn't make a lot of sense in the current implementation and is ommited. -#[allow(unused)] -enum Prescaler { - NotDivided, - DividedBy2, - DividedBy4, - DividedBy6, - DividedBy8, - DividedBy10, - DividedBy12, - DividedBy16, - DividedBy32, - DividedBy64, - DividedBy128, - DividedBy256, -} - -impl Prescaler { - fn from_ker_ck(frequency: Hertz) -> Self { - let raw_prescaler = frequency.0 / MAX_ADC_CLK_FREQ.0; - match raw_prescaler { - 0 => Self::NotDivided, - 1 => Self::DividedBy2, - 2..=3 => Self::DividedBy4, - 4..=5 => Self::DividedBy6, - 6..=7 => Self::DividedBy8, - 8..=9 => Self::DividedBy10, - 10..=11 => Self::DividedBy12, - _ => unimplemented!(), - } - } - - fn divisor(&self) -> u32 { - match self { - Prescaler::NotDivided => 1, - Prescaler::DividedBy2 => 2, - Prescaler::DividedBy4 => 4, - Prescaler::DividedBy6 => 6, - Prescaler::DividedBy8 => 8, - Prescaler::DividedBy10 => 10, - Prescaler::DividedBy12 => 12, - Prescaler::DividedBy16 => 16, - Prescaler::DividedBy32 => 32, - Prescaler::DividedBy64 => 64, - Prescaler::DividedBy128 => 128, - Prescaler::DividedBy256 => 256, - } - } - - fn presc(&self) -> Presc { - match self { - Prescaler::NotDivided => Presc::DIV1, - Prescaler::DividedBy2 => Presc::DIV2, - Prescaler::DividedBy4 => Presc::DIV4, - Prescaler::DividedBy6 => Presc::DIV6, - Prescaler::DividedBy8 => Presc::DIV8, - Prescaler::DividedBy10 => Presc::DIV10, - Prescaler::DividedBy12 => Presc::DIV12, - Prescaler::DividedBy16 => Presc::DIV16, - Prescaler::DividedBy32 => Presc::DIV32, - Prescaler::DividedBy64 => Presc::DIV64, - Prescaler::DividedBy128 => Presc::DIV128, - Prescaler::DividedBy256 => Presc::DIV256, - } +fn from_ker_ck(frequency: Hertz) -> Presc { + let raw_prescaler = frequency.0 / MAX_ADC_CLK_FREQ.0; + match raw_prescaler { + 0 => Presc::DIV1, + 1 => Presc::DIV2, + 2..=3 => Presc::DIV4, + 4..=5 => Presc::DIV6, + 6..=7 => Presc::DIV8, + 8..=9 => Presc::DIV10, + 10..=11 => Presc::DIV12, + _ => unimplemented!(), } } @@ -292,11 +238,11 @@ impl<'d, T: Instance + AnyInstance> Adc<'d, T> { pub fn new(adc: Peri<'d, T>, config: AdcConfig) -> Self { rcc::enable_and_reset::(); - let prescaler = Prescaler::from_ker_ck(T::frequency()); + let prescaler = from_ker_ck(T::frequency()); - T::common_regs().ccr().modify(|w| w.set_presc(prescaler.presc())); + T::common_regs().ccr().modify(|w| w.set_presc(prescaler)); - let frequency = Hertz(T::frequency().0 / prescaler.divisor()); + let frequency = T::frequency() / prescaler; trace!("ADC frequency set to {}", frequency); if frequency > MAX_ADC_CLK_FREQ { diff --git a/embassy-stm32/src/adc/v4.rs b/embassy-stm32/src/adc/v4.rs index 1b17b744f..5c4a1975f 100644 --- a/embassy-stm32/src/adc/v4.rs +++ b/embassy-stm32/src/adc/v4.rs @@ -59,71 +59,17 @@ impl super::SealedSpecialConverter for T { const CHANNEL: u8 = 18; } -// NOTE (unused): The prescaler enum closely copies the hardware capabilities, -// but high prescaling doesn't make a lot of sense in the current implementation and is ommited. -#[allow(unused)] -enum Prescaler { - NotDivided, - DividedBy2, - DividedBy4, - DividedBy6, - DividedBy8, - DividedBy10, - DividedBy12, - DividedBy16, - DividedBy32, - DividedBy64, - DividedBy128, - DividedBy256, -} - -impl Prescaler { - fn from_ker_ck(frequency: Hertz) -> Self { - let raw_prescaler = frequency.0 / MAX_ADC_CLK_FREQ.0; - match raw_prescaler { - 0 => Self::NotDivided, - 1 => Self::DividedBy2, - 2..=3 => Self::DividedBy4, - 4..=5 => Self::DividedBy6, - 6..=7 => Self::DividedBy8, - 8..=9 => Self::DividedBy10, - 10..=11 => Self::DividedBy12, - _ => unimplemented!(), - } - } - - fn divisor(&self) -> u32 { - match self { - Prescaler::NotDivided => 1, - Prescaler::DividedBy2 => 2, - Prescaler::DividedBy4 => 4, - Prescaler::DividedBy6 => 6, - Prescaler::DividedBy8 => 8, - Prescaler::DividedBy10 => 10, - Prescaler::DividedBy12 => 12, - Prescaler::DividedBy16 => 16, - Prescaler::DividedBy32 => 32, - Prescaler::DividedBy64 => 64, - Prescaler::DividedBy128 => 128, - Prescaler::DividedBy256 => 256, - } - } - - fn presc(&self) -> Presc { - match self { - Prescaler::NotDivided => Presc::DIV1, - Prescaler::DividedBy2 => Presc::DIV2, - Prescaler::DividedBy4 => Presc::DIV4, - Prescaler::DividedBy6 => Presc::DIV6, - Prescaler::DividedBy8 => Presc::DIV8, - Prescaler::DividedBy10 => Presc::DIV10, - Prescaler::DividedBy12 => Presc::DIV12, - Prescaler::DividedBy16 => Presc::DIV16, - Prescaler::DividedBy32 => Presc::DIV32, - Prescaler::DividedBy64 => Presc::DIV64, - Prescaler::DividedBy128 => Presc::DIV128, - Prescaler::DividedBy256 => Presc::DIV256, - } +fn from_ker_ck(frequency: Hertz) -> Presc { + let raw_prescaler = frequency.0 / MAX_ADC_CLK_FREQ.0; + match raw_prescaler { + 0 => Presc::DIV1, + 1 => Presc::DIV2, + 2..=3 => Presc::DIV4, + 4..=5 => Presc::DIV6, + 6..=7 => Presc::DIV8, + 8..=9 => Presc::DIV10, + 10..=11 => Presc::DIV12, + _ => unimplemented!(), } } @@ -292,11 +238,11 @@ impl<'d, T: Instance + super::AnyInstance> Adc<'d, T> { pub fn new(adc: Peri<'d, T>) -> Self { rcc::enable_and_reset::(); - let prescaler = Prescaler::from_ker_ck(T::frequency()); + let prescaler = from_ker_ck(T::frequency()); - T::common_regs().ccr().modify(|w| w.set_presc(prescaler.presc())); + T::common_regs().ccr().modify(|w| w.set_presc(prescaler)); - let frequency = Hertz(T::frequency().0 / prescaler.divisor()); + let frequency = T::frequency() / prescaler; info!("ADC frequency set to {}", frequency); if frequency > MAX_ADC_CLK_FREQ { -- cgit From 1a3da1c582b4a9fec1af561e2f4a7ed0352aff33 Mon Sep 17 00:00:00 2001 From: xoviat Date: Thu, 13 Nov 2025 09:33:27 -0600 Subject: adc: extract v2 psc. --- embassy-stm32/build.rs | 2 +- embassy-stm32/src/adc/v2.rs | 51 +++++++++++++++------------------------------ 2 files changed, 18 insertions(+), 35 deletions(-) diff --git a/embassy-stm32/build.rs b/embassy-stm32/build.rs index 67041a9e0..1e11eb8dc 100644 --- a/embassy-stm32/build.rs +++ b/embassy-stm32/build.rs @@ -1707,7 +1707,7 @@ fn main() { for e in psc_enums.iter() { fn is_adc_name(e: &str) -> bool { match e { - "Presc" | "Adc4Presc" => true, + "Presc" | "Adc4Presc" | "Adcpre" => true, _ => false, } } diff --git a/embassy-stm32/src/adc/v2.rs b/embassy-stm32/src/adc/v2.rs index 4065f89a7..07eaebf7c 100644 --- a/embassy-stm32/src/adc/v2.rs +++ b/embassy-stm32/src/adc/v2.rs @@ -3,6 +3,7 @@ use core::sync::atomic::{Ordering, compiler_fence}; use super::{ConversionMode, Temperature, Vbat, VrefInt, blocking_delay_us}; use crate::adc::{Adc, Instance, Resolution, SampleTime}; use crate::pac::adc::vals; +pub use crate::pac::adccommon::vals::Adcpre; use crate::time::Hertz; use crate::{Peri, rcc}; @@ -50,38 +51,20 @@ impl Temperature { } } -enum Prescaler { - Div2, - Div4, - Div6, - Div8, -} - -impl Prescaler { - fn from_pclk2(freq: Hertz) -> Self { - // Datasheet for F2 specifies min frequency 0.6 MHz, and max 30 MHz (with VDDA 2.4-3.6V). - #[cfg(stm32f2)] - const MAX_FREQUENCY: Hertz = Hertz(30_000_000); - // Datasheet for both F4 and F7 specifies min frequency 0.6 MHz, typ freq. 30 MHz and max 36 MHz. - #[cfg(not(stm32f2))] - const MAX_FREQUENCY: Hertz = Hertz(36_000_000); - let raw_div = freq.0 / MAX_FREQUENCY.0; - match raw_div { - 0..=1 => Self::Div2, - 2..=3 => Self::Div4, - 4..=5 => Self::Div6, - 6..=7 => Self::Div8, - _ => panic!("Selected PCLK2 frequency is too high for ADC with largest possible prescaler."), - } - } - - fn adcpre(&self) -> crate::pac::adccommon::vals::Adcpre { - match self { - Prescaler::Div2 => crate::pac::adccommon::vals::Adcpre::DIV2, - Prescaler::Div4 => crate::pac::adccommon::vals::Adcpre::DIV4, - Prescaler::Div6 => crate::pac::adccommon::vals::Adcpre::DIV6, - Prescaler::Div8 => crate::pac::adccommon::vals::Adcpre::DIV8, - } +fn from_pclk2(freq: Hertz) -> Adcpre { + // Datasheet for F2 specifies min frequency 0.6 MHz, and max 30 MHz (with VDDA 2.4-3.6V). + #[cfg(stm32f2)] + const MAX_FREQUENCY: Hertz = Hertz(30_000_000); + // Datasheet for both F4 and F7 specifies min frequency 0.6 MHz, typ freq. 30 MHz and max 36 MHz. + #[cfg(not(stm32f2))] + const MAX_FREQUENCY: Hertz = Hertz(36_000_000); + let raw_div = freq.0 / MAX_FREQUENCY.0; + match raw_div { + 0..=1 => Adcpre::DIV2, + 2..=3 => Adcpre::DIV4, + 4..=5 => Adcpre::DIV6, + 6..=7 => Adcpre::DIV8, + _ => panic!("Selected PCLK2 frequency is too high for ADC with largest possible prescaler."), } } @@ -224,8 +207,8 @@ where pub fn new_with_config(adc: Peri<'d, T>, config: AdcConfig) -> Self { rcc::enable_and_reset::(); - let presc = Prescaler::from_pclk2(T::frequency()); - T::common_regs().ccr().modify(|w| w.set_adcpre(presc.adcpre())); + let presc = from_pclk2(T::frequency()); + T::common_regs().ccr().modify(|w| w.set_adcpre(presc)); T::regs().cr2().modify(|reg| { reg.set_adon(true); }); -- cgit From a8de2ccb8c0721284281715ce6eda28271db3950 Mon Sep 17 00:00:00 2001 From: xoviat Date: Thu, 13 Nov 2025 10:26:56 -0600 Subject: remove allow dead_code --- embassy-stm32/src/adc/adc4.rs | 1 + embassy-stm32/src/adc/mod.rs | 14 +++++++------- embassy-stm32/src/adc/v3.rs | 1 + embassy-stm32/src/adc/v4.rs | 1 + 4 files changed, 10 insertions(+), 7 deletions(-) diff --git a/embassy-stm32/src/adc/adc4.rs b/embassy-stm32/src/adc/adc4.rs index d816eea57..babdebfdb 100644 --- a/embassy-stm32/src/adc/adc4.rs +++ b/embassy-stm32/src/adc/adc4.rs @@ -158,6 +158,7 @@ foreach_adc!( reg.set_chselrmod(Chselrmod::ENABLE_INPUT) }); } + #[cfg(any(adc_v2, adc_g4, adc_v3, adc_g0, adc_u0))] _ => unreachable!(), } } diff --git a/embassy-stm32/src/adc/mod.rs b/embassy-stm32/src/adc/mod.rs index c91d68e87..fd5b94224 100644 --- a/embassy-stm32/src/adc/mod.rs +++ b/embassy-stm32/src/adc/mod.rs @@ -88,19 +88,17 @@ pub(crate) trait SealedAdcChannel { } // Temporary patch for ADCs that have not implemented the standard iface yet -#[cfg(not(any(adc_v2, adc_v3, adc_g0, adc_h5, adc_h7rs, adc_u0, adc_v4, adc_u5, adc_wba, adc_g4)))] +#[cfg(any(adc_v1, adc_l0, adc_f1, adc_f3v1, adc_f3v2, adc_f3v3, adc_v1, adc_c0))] trait_set::trait_set! { pub trait AnyInstance = Instance; } #[cfg(any(adc_v2, adc_v3, adc_g0, adc_h5, adc_h7rs, adc_u0, adc_v4, adc_u5, adc_wba, adc_g4))] -#[allow(dead_code)] pub trait BasicAnyInstance { type SampleTime; } #[cfg(any(adc_v2, adc_v3, adc_g0, adc_h5, adc_h7rs, adc_u0, adc_v4, adc_u5, adc_wba, adc_g4))] -#[allow(dead_code)] pub(self) trait SealedAnyInstance: BasicAnyInstance { fn enable(); fn start(); @@ -108,6 +106,7 @@ pub(self) trait SealedAnyInstance: BasicAnyInstance { fn convert() -> u16; fn configure_dma(conversion_mode: ConversionMode); fn configure_sequence(sequence: impl ExactSizeIterator); + #[allow(dead_code)] fn dr() -> *mut u16; } @@ -169,17 +168,18 @@ pub enum Averaging { } #[cfg(any(adc_v2, adc_g4, adc_v3, adc_g0, adc_h5, adc_h7rs, adc_u0, adc_v4, adc_u5, adc_wba))] -#[allow(dead_code)] pub(crate) enum ConversionMode { + // Should match the cfg on "read" below #[cfg(any(adc_g4, adc_v3, adc_g0, adc_h5, adc_h7rs, adc_u0, adc_v4, adc_u5, adc_wba))] Singular, - #[allow(dead_code)] + // Should match the cfg on "into_ring_buffered" below + #[cfg(any(adc_v2, adc_g4, adc_v3, adc_g0, adc_u0))] Repeated(RegularConversionMode), } -#[cfg(any(adc_v2, adc_g4, adc_v3, adc_g0, adc_h5, adc_h7rs, adc_u0, adc_v4, adc_u5, adc_wba))] +// Should match the cfg on "into_ring_buffered" below +#[cfg(any(adc_v2, adc_g4, adc_v3, adc_g0, adc_u0))] // Conversion mode for regular ADC channels -#[allow(dead_code)] #[derive(Copy, Clone)] pub enum RegularConversionMode { // Samples as fast as possible diff --git a/embassy-stm32/src/adc/v3.rs b/embassy-stm32/src/adc/v3.rs index fa191c663..78b497727 100644 --- a/embassy-stm32/src/adc/v3.rs +++ b/embassy-stm32/src/adc/v3.rs @@ -259,6 +259,7 @@ impl super::SealedAnyInstance for T { reg.set_cont(true); reg.set_dmacfg(match conversion_mode { ConversionMode::Singular => Dmacfg::ONE_SHOT, + #[cfg(any(adc_v2, adc_g4, adc_v3, adc_g0, adc_u0))] ConversionMode::Repeated(_) => Dmacfg::CIRCULAR, }); reg.set_dmaen(true); diff --git a/embassy-stm32/src/adc/v4.rs b/embassy-stm32/src/adc/v4.rs index 5c4a1975f..804e63db6 100644 --- a/embassy-stm32/src/adc/v4.rs +++ b/embassy-stm32/src/adc/v4.rs @@ -143,6 +143,7 @@ impl super::SealedAnyInstance for T { reg.set_dmngt(Dmngt::DMA_ONE_SHOT); }); } + #[cfg(any(adc_v2, adc_g4, adc_v3, adc_g0, adc_u0))] _ => unreachable!(), } } -- cgit From 32408f4a031dff11c1c3c8c4aeb2044f1a7e8f42 Mon Sep 17 00:00:00 2001 From: xoviat Date: Thu, 13 Nov 2025 12:02:38 -0600 Subject: adc: extract c0 --- embassy-stm32/src/adc/c0.rs | 488 ++++++++++++++-------------------------- embassy-stm32/src/adc/mod.rs | 53 ++++- examples/stm32c0/src/bin/adc.rs | 18 +- 3 files changed, 214 insertions(+), 345 deletions(-) diff --git a/embassy-stm32/src/adc/c0.rs b/embassy-stm32/src/adc/c0.rs index 8992d6e6e..983e7c10d 100644 --- a/embassy-stm32/src/adc/c0.rs +++ b/embassy-stm32/src/adc/c0.rs @@ -1,12 +1,10 @@ -use pac::adc::vals::Scandir; #[allow(unused)] use pac::adc::vals::{Adstp, Align, Ckmode, Dmacfg, Exten, Ovrmod, Ovsr}; use pac::adccommon::vals::Presc; +use stm32_metapac::adc::vals::Scandir; -use super::{ - Adc, AdcChannel, AnyAdcChannel, Instance, Resolution, RxDma, SampleTime, SealedAdcChannel, blocking_delay_us, -}; -use crate::dma::Transfer; +use super::{Adc, Instance, Resolution, blocking_delay_us}; +use crate::adc::{AnyInstance, ConversionMode}; use crate::time::Hertz; use crate::{Peri, pac, rcc}; @@ -32,104 +30,184 @@ impl super::SealedSpecialConverter for T { const CHANNEL: u8 = 9; } -#[derive(Copy, Clone, Debug)] -pub enum Prescaler { - NotDivided, - DividedBy2, - DividedBy4, - DividedBy6, - DividedBy8, - DividedBy10, - DividedBy12, - DividedBy16, - DividedBy32, - DividedBy64, - DividedBy128, - DividedBy256, +fn from_ker_ck(frequency: Hertz) -> Presc { + let raw_prescaler = frequency.0 / MAX_ADC_CLK_FREQ.0; + match raw_prescaler { + 0 => Presc::DIV1, + 1 => Presc::DIV2, + 2..=3 => Presc::DIV4, + 4..=5 => Presc::DIV6, + 6..=7 => Presc::DIV8, + 8..=9 => Presc::DIV10, + 10..=11 => Presc::DIV12, + _ => unimplemented!(), + } } -impl Prescaler { - fn from_ker_ck(frequency: Hertz) -> Self { - let raw_prescaler = frequency.0 / MAX_ADC_CLK_FREQ.0; - match raw_prescaler { - 0 => Self::NotDivided, - 1 => Self::DividedBy2, - 2..=3 => Self::DividedBy4, - 4..=5 => Self::DividedBy6, - 6..=7 => Self::DividedBy8, - 8..=9 => Self::DividedBy10, - 10..=11 => Self::DividedBy12, - _ => unimplemented!(), - } +impl super::SealedAnyInstance for T { + fn dr() -> *mut u16 { + T::regs().dr().as_ptr() as *mut u16 } - #[allow(unused)] - fn divisor(&self) -> u32 { - match self { - Prescaler::NotDivided => 1, - Prescaler::DividedBy2 => 2, - Prescaler::DividedBy4 => 4, - Prescaler::DividedBy6 => 6, - Prescaler::DividedBy8 => 8, - Prescaler::DividedBy10 => 10, - Prescaler::DividedBy12 => 12, - Prescaler::DividedBy16 => 16, - Prescaler::DividedBy32 => 32, - Prescaler::DividedBy64 => 64, - Prescaler::DividedBy128 => 128, - Prescaler::DividedBy256 => 256, + fn enable() { + T::regs().isr().modify(|w| w.set_adrdy(true)); + T::regs().cr().modify(|w| w.set_aden(true)); + // ADRDY is "ADC ready". Wait until it will be True. + while !T::regs().isr().read().adrdy() {} + } + + fn start() { + // Start conversion + T::regs().cr().modify(|reg| { + reg.set_adstart(true); + }); + } + + fn stop() { + if T::regs().cr().read().adstart() && !T::regs().cr().read().addis() { + T::regs().cr().modify(|reg| { + reg.set_adstp(Adstp::STOP); + }); + while T::regs().cr().read().adstart() {} } + + // Reset configuration. + T::regs().cfgr1().modify(|reg| { + reg.set_cont(false); + reg.set_dmacfg(Dmacfg::from_bits(0)); + reg.set_dmaen(false); + }); } - fn presc(&self) -> Presc { - match self { - Prescaler::NotDivided => Presc::DIV1, - Prescaler::DividedBy2 => Presc::DIV2, - Prescaler::DividedBy4 => Presc::DIV4, - Prescaler::DividedBy6 => Presc::DIV6, - Prescaler::DividedBy8 => Presc::DIV8, - Prescaler::DividedBy10 => Presc::DIV10, - Prescaler::DividedBy12 => Presc::DIV12, - Prescaler::DividedBy16 => Presc::DIV16, - Prescaler::DividedBy32 => Presc::DIV32, - Prescaler::DividedBy64 => Presc::DIV64, - Prescaler::DividedBy128 => Presc::DIV128, - Prescaler::DividedBy256 => Presc::DIV256, + fn configure_dma(conversion_mode: super::ConversionMode) { + match conversion_mode { + ConversionMode::Singular => { + // Enable overrun control, so no new DMA requests will be generated until + // previous DR values is read. + T::regs().isr().modify(|reg| { + reg.set_ovr(true); + }); + + // Set continuous mode with oneshot dma. + T::regs().cfgr1().modify(|reg| { + reg.set_discen(false); + reg.set_cont(true); + reg.set_dmacfg(Dmacfg::DMA_ONE_SHOT); + reg.set_dmaen(true); + reg.set_ovrmod(Ovrmod::PRESERVE); + }); + } } } -} -#[cfg(feature = "defmt")] -impl<'a> defmt::Format for Prescaler { - fn format(&self, fmt: defmt::Formatter) { - match self { - Prescaler::NotDivided => defmt::write!(fmt, "Prescaler::NotDivided"), - Prescaler::DividedBy2 => defmt::write!(fmt, "Prescaler::DividedBy2"), - Prescaler::DividedBy4 => defmt::write!(fmt, "Prescaler::DividedBy4"), - Prescaler::DividedBy6 => defmt::write!(fmt, "Prescaler::DividedBy6"), - Prescaler::DividedBy8 => defmt::write!(fmt, "Prescaler::DividedBy8"), - Prescaler::DividedBy10 => defmt::write!(fmt, "Prescaler::DividedBy10"), - Prescaler::DividedBy12 => defmt::write!(fmt, "Prescaler::DividedBy12"), - Prescaler::DividedBy16 => defmt::write!(fmt, "Prescaler::DividedBy16"), - Prescaler::DividedBy32 => defmt::write!(fmt, "Prescaler::DividedBy32"), - Prescaler::DividedBy64 => defmt::write!(fmt, "Prescaler::DividedBy64"), - Prescaler::DividedBy128 => defmt::write!(fmt, "Prescaler::DividedBy128"), - Prescaler::DividedBy256 => defmt::write!(fmt, "Prescaler::DividedBy256"), + fn configure_sequence(mut sequence: impl ExactSizeIterator, blocking: bool) { + T::regs().cfgr1().modify(|reg| { + reg.set_chselrmod(!blocking); + reg.set_align(Align::RIGHT); + }); + + assert!(!blocking || sequence.len() == 1, "Sequence len must be 1 for blocking."); + if blocking { + let ((ch, _), sample_time) = sequence.next().unwrap(); + // Set all channels to use SMP1 field as source. + T::regs().smpr().modify(|w| { + w.smpsel(0); + w.set_smp1(sample_time); + }); + + // write() because we want all other bits to be set to 0. + T::regs().chselr().write(|w| w.set_chsel(ch.into(), true)); + } else { + let mut hw_channel_selection: u32 = 0; + let mut is_ordered_up = true; + let mut is_ordered_down = true; + let mut needs_hw = false; + + assert!( + sequence.len() <= CHSELR_SQ_SIZE, + "Sequence read set cannot be more than {} in size.", + CHSELR_SQ_SIZE + ); + let mut last_sq_set: usize = 0; + let mut last_channel: u8 = 0; + T::regs().chselr_sq().write(|w| { + for (i, ((channel, _), _sample_time)) in sequence.enumerate() { + needs_hw = needs_hw || channel > CHSELR_SQ_MAX_CHANNEL; + last_sq_set = i; + is_ordered_up = is_ordered_up && channel > last_channel; + is_ordered_down = is_ordered_down && channel < last_channel; + hw_channel_selection += 1 << channel; + last_channel = channel; + + if !needs_hw { + w.set_sq(i, channel); + } + } + + assert!( + !needs_hw || is_ordered_up || is_ordered_down, + "Sequencer is required because of unordered channels, but only support HW channels smaller than {}.", + CHSELR_SQ_MAX_CHANNEL + ); + + if needs_hw { + assert!( + hw_channel_selection != 0, + "Some bits in `hw_channel_selection` shall be set." + ); + assert!( + (hw_channel_selection >> NUM_HW_CHANNELS) == 0, + "STM32C0 only have {} ADC channels. `hw_channel_selection` cannot have bits higher than this number set.", + NUM_HW_CHANNELS + ); + + T::regs().cfgr1().modify(|reg| { + reg.set_chselrmod(false); + reg.set_scandir(if is_ordered_up { Scandir::UP} else { Scandir::BACK }); + }); + + // Set required channels for multi-convert. + unsafe { (T::regs().chselr().as_ptr() as *mut u32).write_volatile(hw_channel_selection) } + } else { + for i in (last_sq_set + 1)..CHSELR_SQ_SIZE { + w.set_sq(i, CHSELR_SQ_SEQUENCE_END_MARKER); + } + } + }); } + + // Trigger and wait for the channel selection procedure to complete. + T::regs().isr().modify(|w| w.set_ccrdy(false)); + while !T::regs().isr().read().ccrdy() {} + } + + fn convert() -> u16 { + // Set single conversion mode. + T::regs().cfgr1().modify(|w| w.set_cont(false)); + + // Start conversion + T::regs().cr().modify(|reg| { + reg.set_adstart(true); + }); + + // Waiting for End Of Conversion (EOC). + while !T::regs().isr().read().eoc() {} + + T::regs().dr().read().data() as u16 } } -impl<'d, T: Instance> Adc<'d, T> { +impl<'d, T: AnyInstance> Adc<'d, T> { /// Create a new ADC driver. pub fn new(adc: Peri<'d, T>, resolution: Resolution) -> Self { rcc::enable_and_reset::(); T::regs().cfgr2().modify(|w| w.set_ckmode(Ckmode::SYSCLK)); - let prescaler = Prescaler::from_ker_ck(T::frequency()); - T::common_regs().ccr().modify(|w| w.set_presc(prescaler.presc())); + let prescaler = from_ker_ck(T::frequency()); + T::common_regs().ccr().modify(|w| w.set_presc(prescaler)); - let frequency = Hertz(T::frequency().0 / prescaler.divisor()); + let frequency = T::frequency() / prescaler; debug!("ADC frequency set to {}", frequency); if frequency > MAX_ADC_CLK_FREQ { @@ -139,22 +217,6 @@ impl<'d, T: Instance> Adc<'d, T> { ); } - let mut s = Self { adc }; - - s.power_up(); - - s.set_resolution(resolution); - - s.calibrate(); - - s.enable(); - - s.configure_default(); - - s - } - - fn power_up(&mut self) { T::regs().cr().modify(|reg| { reg.set_advregen(true); }); @@ -162,9 +224,9 @@ impl<'d, T: Instance> Adc<'d, T> { // "The software must wait for the ADC voltage regulator startup time." // See datasheet for the value. blocking_delay_us(TIME_ADC_VOLTAGE_REGUALTOR_STARTUP_US + 1); - } - fn calibrate(&mut self) { + T::regs().cfgr1().modify(|reg| reg.set_res(resolution)); + // We have to make sure AUTOFF is OFF, but keep its value after calibration. let autoff_value = T::regs().cfgr1().read().autoff(); T::regs().cfgr1().modify(|w| w.set_autoff(false)); @@ -178,22 +240,17 @@ impl<'d, T: Instance> Adc<'d, T> { debug!("ADC calibration value: {}.", T::regs().dr().read().data()); T::regs().cfgr1().modify(|w| w.set_autoff(autoff_value)); - } - fn enable(&mut self) { - T::regs().isr().modify(|w| w.set_adrdy(true)); - T::regs().cr().modify(|w| w.set_aden(true)); - // ADRDY is "ADC ready". Wait until it will be True. - while !T::regs().isr().read().adrdy() {} - } + T::enable(); - fn configure_default(&mut self) { // single conversion mode, software trigger T::regs().cfgr1().modify(|w| { w.set_cont(false); w.set_exten(Exten::DISABLED); w.set_align(Align::RIGHT); }); + + Self { adc } } /// Enable reading the voltage reference internal channel. @@ -214,219 +271,4 @@ impl<'d, T: Instance> Adc<'d, T> { super::Temperature {} } - - /// Set the ADC sample time. - /// Shall only be called when ADC is not converting. - pub fn set_sample_time_all_channels(&mut self, sample_time: SampleTime) { - // Set all channels to use SMP1 field as source. - T::regs().smpr().modify(|w| { - w.smpsel(0); - w.set_smp1(sample_time); - }); - } - - /// Set the ADC resolution. - pub fn set_resolution(&mut self, resolution: Resolution) { - T::regs().cfgr1().modify(|reg| reg.set_res(resolution)); - } - - /// Perform a single conversion. - fn convert(&mut self) -> u16 { - // Set single conversion mode. - T::regs().cfgr1().modify(|w| w.set_cont(false)); - - // Start conversion - T::regs().cr().modify(|reg| { - reg.set_adstart(true); - }); - - // Waiting for End Of Conversion (EOC). - while !T::regs().isr().read().eoc() {} - - T::regs().dr().read().data() as u16 - } - - pub fn blocking_read(&mut self, channel: &mut impl AdcChannel, sample_time: SampleTime) -> u16 { - self.set_sample_time_all_channels(sample_time); - - Self::configure_channel(channel); - T::regs().cfgr1().write(|reg| { - reg.set_chselrmod(false); - reg.set_align(Align::RIGHT); - }); - self.convert() - } - - fn setup_channel_sequencer<'a>(channel_sequence: impl ExactSizeIterator>) { - assert!( - channel_sequence.len() <= CHSELR_SQ_SIZE, - "Seqenced read set cannot be more than {} in size.", - CHSELR_SQ_SIZE - ); - let mut last_sq_set: usize = 0; - T::regs().chselr_sq().write(|w| { - for (i, channel) in channel_sequence.enumerate() { - assert!( - channel.channel() <= CHSELR_SQ_MAX_CHANNEL, - "Sequencer only support HW channels smaller than {}.", - CHSELR_SQ_MAX_CHANNEL - ); - w.set_sq(i, channel.channel()); - last_sq_set = i; - } - - for i in (last_sq_set + 1)..CHSELR_SQ_SIZE { - w.set_sq(i, CHSELR_SQ_SEQUENCE_END_MARKER); - } - }); - - Self::apply_channel_conf() - } - - async fn dma_convert(&mut self, rx_dma: Peri<'_, impl RxDma>, readings: &mut [u16]) { - // Enable overrun control, so no new DMA requests will be generated until - // previous DR values is read. - T::regs().isr().modify(|reg| { - reg.set_ovr(true); - }); - - // Set continuous mode with oneshot dma. - T::regs().cfgr1().modify(|reg| { - reg.set_discen(false); - reg.set_cont(true); - reg.set_dmacfg(Dmacfg::DMA_ONE_SHOT); - reg.set_dmaen(true); - reg.set_ovrmod(Ovrmod::PRESERVE); - }); - - let request = rx_dma.request(); - let transfer = unsafe { - Transfer::new_read( - rx_dma, - request, - T::regs().dr().as_ptr() as *mut u16, - readings, - Default::default(), - ) - }; - - // Start conversion. - T::regs().cr().modify(|reg| { - reg.set_adstart(true); - }); - - // Wait for conversion sequence to finish. - transfer.await; - - // Ensure conversions are finished. - Self::cancel_conversions(); - - // Reset configuration. - T::regs().cfgr1().modify(|reg| { - reg.set_cont(false); - reg.set_dmacfg(Dmacfg::from_bits(0)); - reg.set_dmaen(false); - }); - } - - /// Read one or multiple ADC channels using DMA in hardware order. - /// Readings will be ordered based on **hardware** ADC channel number and `scandir` setting. - /// Readings won't be in the same order as in the `set`! - /// - /// In STM32C0, channels bigger than 14 cannot be read using sequencer, so you have to use - /// either blocking reads or use the mechanism to read in HW order (CHSELRMOD=0). - /// TODO(chudsaviet): externalize generic code and merge with read(). - pub async fn read_in_hw_order( - &mut self, - rx_dma: Peri<'_, impl RxDma>, - hw_channel_selection: u32, - scandir: Scandir, - readings: &mut [u16], - ) { - assert!( - hw_channel_selection != 0, - "Some bits in `hw_channel_selection` shall be set." - ); - assert!( - (hw_channel_selection >> NUM_HW_CHANNELS) == 0, - "STM32C0 only have {} ADC channels. `hw_channel_selection` cannot have bits higher than this number set.", - NUM_HW_CHANNELS - ); - // To check for correct readings slice size, we shall solve Hamming weight problem, - // which is either slow or memory consuming. - // Since we have limited resources, we don't do it here. - // Not doing this have a great potential for a bug through. - - // Ensure no conversions are ongoing. - Self::cancel_conversions(); - - T::regs().cfgr1().modify(|reg| { - reg.set_chselrmod(false); - reg.set_scandir(scandir); - reg.set_align(Align::RIGHT); - }); - - // Set required channels for multi-convert. - unsafe { (T::regs().chselr().as_ptr() as *mut u32).write_volatile(hw_channel_selection) } - - Self::apply_channel_conf(); - - self.dma_convert(rx_dma, readings).await - } - - // Read ADC channels in specified order using DMA (CHSELRMOD = 1). - // In STM32C0, only lower 14 ADC channels can be read this way. - // For other channels, use `read_in_hw_order()` or blocking read. - pub async fn read( - &mut self, - rx_dma: Peri<'_, impl RxDma>, - channel_sequence: impl ExactSizeIterator>, - readings: &mut [u16], - ) { - assert!( - channel_sequence.len() != 0, - "Asynchronous read channel sequence cannot be empty." - ); - assert!( - channel_sequence.len() == readings.len(), - "Channel sequence length must be equal to readings length." - ); - - // Ensure no conversions are ongoing. - Self::cancel_conversions(); - - T::regs().cfgr1().modify(|reg| { - reg.set_chselrmod(true); - reg.set_align(Align::RIGHT); - }); - - Self::setup_channel_sequencer(channel_sequence); - - self.dma_convert(rx_dma, readings).await - } - - fn configure_channel(channel: &mut impl AdcChannel) { - channel.setup(); - // write() because we want all other bits to be set to 0. - T::regs() - .chselr() - .write(|w| w.set_chsel(channel.channel().into(), true)); - - Self::apply_channel_conf(); - } - - fn apply_channel_conf() { - // Trigger and wait for the channel selection procedure to complete. - T::regs().isr().modify(|w| w.set_ccrdy(false)); - while !T::regs().isr().read().ccrdy() {} - } - - fn cancel_conversions() { - if T::regs().cr().read().adstart() && !T::regs().cr().read().addis() { - T::regs().cr().modify(|reg| { - reg.set_adstp(Adstp::STOP); - }); - while T::regs().cr().read().adstart() {} - } - } } diff --git a/embassy-stm32/src/adc/mod.rs b/embassy-stm32/src/adc/mod.rs index fd5b94224..549f2f5a5 100644 --- a/embassy-stm32/src/adc/mod.rs +++ b/embassy-stm32/src/adc/mod.rs @@ -88,30 +88,37 @@ pub(crate) trait SealedAdcChannel { } // Temporary patch for ADCs that have not implemented the standard iface yet -#[cfg(any(adc_v1, adc_l0, adc_f1, adc_f3v1, adc_f3v2, adc_f3v3, adc_v1, adc_c0))] +#[cfg(any(adc_v1, adc_l0, adc_f1, adc_f3v1, adc_f3v2, adc_f3v3, adc_v1))] trait_set::trait_set! { pub trait AnyInstance = Instance; } -#[cfg(any(adc_v2, adc_v3, adc_g0, adc_h5, adc_h7rs, adc_u0, adc_v4, adc_u5, adc_wba, adc_g4))] +#[cfg(any( + adc_v2, adc_v3, adc_g0, adc_h5, adc_h7rs, adc_u0, adc_v4, adc_u5, adc_wba, adc_g4, adc_c0 +))] pub trait BasicAnyInstance { type SampleTime; } -#[cfg(any(adc_v2, adc_v3, adc_g0, adc_h5, adc_h7rs, adc_u0, adc_v4, adc_u5, adc_wba, adc_g4))] +#[cfg(any( + adc_v2, adc_v3, adc_g0, adc_h5, adc_h7rs, adc_u0, adc_v4, adc_u5, adc_wba, adc_g4, adc_c0 +))] pub(self) trait SealedAnyInstance: BasicAnyInstance { fn enable(); fn start(); fn stop(); fn convert() -> u16; fn configure_dma(conversion_mode: ConversionMode); + #[cfg(not(adc_c0))] fn configure_sequence(sequence: impl ExactSizeIterator); + #[cfg(adc_c0)] + fn configure_sequence(sequence: impl ExactSizeIterator, blocking: bool); #[allow(dead_code)] fn dr() -> *mut u16; } // On chips without ADC4, AnyInstance is an Instance -#[cfg(any(adc_v2, adc_v3, adc_g0, adc_h5, adc_h7rs, adc_u0, adc_g4))] +#[cfg(any(adc_v2, adc_v3, adc_g0, adc_h5, adc_h7rs, adc_u0, adc_g4, adc_c0))] #[allow(private_bounds)] pub trait AnyInstance: SealedAnyInstance + Instance {} @@ -121,12 +128,16 @@ pub trait AnyInstance: SealedAnyInstance + Instance {} pub trait AnyInstance: SealedAnyInstance + crate::PeripheralType + crate::rcc::RccPeripheral {} // Implement AnyInstance automatically for SealedAnyInstance -#[cfg(any(adc_v2, adc_v3, adc_g0, adc_h5, adc_h7rs, adc_u0, adc_v4, adc_u5, adc_wba, adc_g4))] +#[cfg(any( + adc_v2, adc_v3, adc_g0, adc_h5, adc_h7rs, adc_u0, adc_v4, adc_u5, adc_wba, adc_g4, adc_c0 +))] impl BasicAnyInstance for T { type SampleTime = SampleTime; } -#[cfg(any(adc_v2, adc_v3, adc_g0, adc_h5, adc_h7rs, adc_u0, adc_v4, adc_u5, adc_wba, adc_g4))] +#[cfg(any( + adc_v2, adc_v3, adc_g0, adc_h5, adc_h7rs, adc_u0, adc_v4, adc_u5, adc_wba, adc_g4, adc_c0 +))] impl AnyInstance for T {} /// Performs a busy-wait delay for a specified number of microseconds. @@ -167,10 +178,12 @@ pub enum Averaging { Samples1024, } -#[cfg(any(adc_v2, adc_g4, adc_v3, adc_g0, adc_h5, adc_h7rs, adc_u0, adc_v4, adc_u5, adc_wba))] +#[cfg(any( + adc_v2, adc_g4, adc_v3, adc_g0, adc_h5, adc_h7rs, adc_u0, adc_v4, adc_u5, adc_wba, adc_c0 +))] pub(crate) enum ConversionMode { // Should match the cfg on "read" below - #[cfg(any(adc_g4, adc_v3, adc_g0, adc_h5, adc_h7rs, adc_u0, adc_v4, adc_u5, adc_wba))] + #[cfg(any(adc_g4, adc_v3, adc_g0, adc_h5, adc_h7rs, adc_u0, adc_v4, adc_u5, adc_wba, adc_c0))] Singular, // Should match the cfg on "into_ring_buffered" below #[cfg(any(adc_v2, adc_g4, adc_v3, adc_g0, adc_u0))] @@ -190,7 +203,9 @@ pub enum RegularConversionMode { } impl<'d, T: AnyInstance> Adc<'d, T> { - #[cfg(any(adc_v2, adc_g4, adc_v3, adc_g0, adc_h5, adc_h7rs, adc_u0, adc_u5, adc_v4, adc_wba))] + #[cfg(any( + adc_v2, adc_g4, adc_v3, adc_g0, adc_h5, adc_h7rs, adc_u0, adc_u5, adc_v4, adc_wba, adc_c0 + ))] /// Read an ADC pin. pub fn blocking_read(&mut self, channel: &mut impl AdcChannel, sample_time: T::SampleTime) -> u16 { #[cfg(any(adc_v1, adc_c0, adc_l0, adc_v2, adc_g4, adc_v4, adc_u5, adc_wba))] @@ -198,12 +213,18 @@ impl<'d, T: AnyInstance> Adc<'d, T> { #[cfg(not(adc_v4))] T::enable(); + #[cfg(not(adc_c0))] T::configure_sequence([((channel.channel(), channel.is_differential()), sample_time)].into_iter()); + #[cfg(adc_c0)] + T::configure_sequence( + [((channel.channel(), channel.is_differential()), sample_time)].into_iter(), + true, + ); T::convert() } - #[cfg(any(adc_g4, adc_v3, adc_g0, adc_h5, adc_h7rs, adc_u0, adc_v4, adc_u5, adc_wba))] + #[cfg(any(adc_g4, adc_v3, adc_g0, adc_h5, adc_h7rs, adc_u0, adc_v4, adc_u5, adc_wba, adc_c0))] /// Read one or multiple ADC regular channels using DMA. /// /// `sequence` iterator and `readings` must have the same length. @@ -232,6 +253,11 @@ impl<'d, T: AnyInstance> Adc<'d, T> { /// /// Note: This is not very efficient as the ADC needs to be reconfigured for each read. Use /// `into_ring_buffered`, `into_ring_buffered_and_injected` + /// + /// In STM32C0, channels bigger than 14 cannot be read using sequencer, so you have to use + /// either blocking reads or use the mechanism to read in HW order (CHSELRMOD=0). + /// + /// In addtion, on STM320, this method will panic if the channels are not passed in order pub async fn read( &mut self, rx_dma: embassy_hal_internal::Peri<'_, impl RxDma>, @@ -252,8 +278,15 @@ impl<'d, T: AnyInstance> Adc<'d, T> { T::stop(); T::enable(); + #[cfg(not(adc_c0))] + T::configure_sequence( + sequence.map(|(channel, sample_time)| ((channel.channel, channel.is_differential), sample_time)), + ); + + #[cfg(adc_c0)] T::configure_sequence( sequence.map(|(channel, sample_time)| ((channel.channel, channel.is_differential), sample_time)), + false, ); T::configure_dma(ConversionMode::Singular); diff --git a/examples/stm32c0/src/bin/adc.rs b/examples/stm32c0/src/bin/adc.rs index b52c9e7f8..ad597b63c 100644 --- a/examples/stm32c0/src/bin/adc.rs +++ b/examples/stm32c0/src/bin/adc.rs @@ -3,7 +3,6 @@ use defmt::*; use embassy_executor::Spawner; -use embassy_stm32::adc::vals::Scandir; use embassy_stm32::adc::{Adc, AdcChannel, AnyAdcChannel, Resolution, SampleTime}; use embassy_stm32::peripherals::ADC1; use embassy_time::Timer; @@ -35,8 +34,12 @@ async fn main(_spawner: Spawner) { blocking_vref, blocking_temp, blocing_pin0 ); - let channels_seqence: [&mut AnyAdcChannel; 3] = [&mut vref, &mut temp, &mut pin0]; - adc.read(dma.reborrow(), channels_seqence.into_iter(), &mut read_buffer) + let channels_sequence: [(&mut AnyAdcChannel, SampleTime); 3] = [ + (&mut vref, SampleTime::CYCLES12_5), + (&mut temp, SampleTime::CYCLES12_5), + (&mut pin0, SampleTime::CYCLES12_5), + ]; + adc.read(dma.reborrow(), channels_sequence.into_iter(), &mut read_buffer) .await; // Values are ordered according to hardware ADC channel number! info!( @@ -44,15 +47,6 @@ async fn main(_spawner: Spawner) { read_buffer[0], read_buffer[1], read_buffer[2] ); - let hw_channel_selection: u32 = - (1 << temp.get_hw_channel()) + (1 << vref.get_hw_channel()) + (1 << pin0.get_hw_channel()); - adc.read_in_hw_order(dma.reborrow(), hw_channel_selection, Scandir::UP, &mut read_buffer) - .await; - info!( - "DMA ADC read in hardware order: vref = {}, temp = {}, pin0 = {}.", - read_buffer[2], read_buffer[1], read_buffer[0] - ); - Timer::after_millis(2000).await; } } -- cgit From de16754a2d340eca49885238e265f50bfc3ec2e5 Mon Sep 17 00:00:00 2001 From: HybridChild Date: Thu, 13 Nov 2025 21:18:12 +0100 Subject: stm32/i2c: Fix async write_vectored and restore DMA implementation --- embassy-stm32/src/i2c/v2.rs | 41 ++++++++++++++++++++++++++++++++++------- 1 file changed, 34 insertions(+), 7 deletions(-) diff --git a/embassy-stm32/src/i2c/v2.rs b/embassy-stm32/src/i2c/v2.rs index 7bcfa00b0..c35f3694c 100644 --- a/embassy-stm32/src/i2c/v2.rs +++ b/embassy-stm32/src/i2c/v2.rs @@ -389,7 +389,9 @@ impl<'d, M: Mode, IM: MasterMode> I2c<'d, M, IM> { loop { let isr = self.info.regs.isr().read(); self.error_occurred(&isr, timeout)?; - if isr.tc() { + // Wait for either TC or TCR - both indicate transfer completion + // TCR occurs when RELOAD=1, TC occurs when RELOAD=0 + if isr.tc() || isr.tcr() { return Ok(()); } timeout.check()?; @@ -918,12 +920,12 @@ impl<'d, IM: MasterMode> I2c<'d, Async, IM> { address, total_len.min(255), Stop::Software, - total_len > 255, + (total_len > 255) || !last_slice, restart, timeout, )?; } else { - Self::reload(self.info, total_len.min(255), total_len > 255, Stop::Software, timeout)?; + Self::reload(self.info, total_len.min(255), (total_len > 255) || !last_slice, Stop::Software, timeout)?; self.info.regs.cr1().modify(|w| w.set_tcie(true)); } } else if !(isr.tcr() || isr.tc()) { @@ -935,7 +937,7 @@ impl<'d, IM: MasterMode> I2c<'d, Async, IM> { if let Err(e) = Self::reload( self.info, remaining_len.min(255), - remaining_len > 255, + (remaining_len > 255) || !last_slice, Stop::Software, timeout, ) { @@ -1085,10 +1087,35 @@ impl<'d, IM: MasterMode> I2c<'d, Async, IM> { pub async fn write_vectored(&mut self, address: Address, write: &[&[u8]]) -> Result<(), Error> { #[cfg(all(feature = "low-power", stm32wlex))] let _device_busy = crate::low_power::DeviceBusy::new_stop1(); + let timeout = self.timeout(); - // For now, use blocking implementation for write_vectored - // This avoids complexity of handling multiple non-contiguous buffers with DMA - self.blocking_write_vectored((address.addr() & 0xFF) as u8, write) + if write.is_empty() { + return Err(Error::ZeroLengthTransfer); + } + + let mut iter = write.iter(); + let mut first = true; + let mut current = iter.next(); + + while let Some(c) = current { + let next = iter.next(); + let is_last = next.is_none(); + + let fut = self.write_dma_internal( + address, + c, + first, // first_slice + is_last, // last_slice + is_last, // send_stop (only on last buffer) + false, // restart (false for all - they're one continuous write) + timeout, + ); + timeout.with(fut).await?; + + first = false; + current = next; + } + Ok(()) } /// Read. -- cgit From 42f38d5b3209134baa8bf424cdb754e1901ac0da Mon Sep 17 00:00:00 2001 From: HybridChild Date: Thu, 13 Nov 2025 21:23:13 +0100 Subject: stm32/i2c: Implement async DMA for transaction write groups --- embassy-stm32/src/i2c/v2.rs | 50 ++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 47 insertions(+), 3 deletions(-) diff --git a/embassy-stm32/src/i2c/v2.rs b/embassy-stm32/src/i2c/v2.rs index c35f3694c..8f51627ff 100644 --- a/embassy-stm32/src/i2c/v2.rs +++ b/embassy-stm32/src/i2c/v2.rs @@ -1222,9 +1222,53 @@ impl<'d, IM: MasterMode> I2c<'d, Async, IM> { is_last_group: bool, timeout: Timeout, ) -> Result<(), Error> { - // For now, use blocking implementation for write groups - // This avoids complexity of handling multiple non-contiguous buffers with DMA - self.execute_write_group(address, operations, is_first_group, is_last_group, timeout) + // Calculate total bytes across all operations in this group + let total_bytes = Self::total_operation_bytes(operations); + + if total_bytes == 0 { + // Handle empty write group using blocking call + if is_first_group { + Self::master_write(self.info, address, 0, Stop::Software, false, !is_first_group, timeout)?; + } + if is_last_group { + self.master_stop(); + } + return Ok(()); + } + + // Collect all write buffers + let mut write_buffers: heapless::Vec<&[u8], 16> = heapless::Vec::new(); + for operation in operations { + if let Operation::Write(buffer) = operation { + if !buffer.is_empty() { + let _ = write_buffers.push(buffer); + } + } + } + + if write_buffers.is_empty() { + return Ok(()); + } + + // Send each buffer using DMA + let num_buffers = write_buffers.len(); + for (idx, buffer) in write_buffers.iter().enumerate() { + let is_first_buffer = idx == 0; + let is_last_buffer = idx == num_buffers - 1; + + let fut = self.write_dma_internal( + address, + buffer, + is_first_buffer, // first_slice + is_last_buffer, // last_slice + is_last_buffer && is_last_group, // send_stop + is_first_buffer && !is_first_group, // restart (only for first buffer if not first group) + timeout, + ); + timeout.with(fut).await?; + } + + Ok(()) } async fn execute_read_group_async( -- cgit From 82dcaf118100c883a95397e3f9867a4043a4de7b Mon Sep 17 00:00:00 2001 From: HybridChild Date: Thu, 13 Nov 2025 21:24:09 +0100 Subject: stm32: Run cargo fmt --- embassy-stm32/src/i2c/v2.rs | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/embassy-stm32/src/i2c/v2.rs b/embassy-stm32/src/i2c/v2.rs index 8f51627ff..61e550ad4 100644 --- a/embassy-stm32/src/i2c/v2.rs +++ b/embassy-stm32/src/i2c/v2.rs @@ -925,7 +925,13 @@ impl<'d, IM: MasterMode> I2c<'d, Async, IM> { timeout, )?; } else { - Self::reload(self.info, total_len.min(255), (total_len > 255) || !last_slice, Stop::Software, timeout)?; + Self::reload( + self.info, + total_len.min(255), + (total_len > 255) || !last_slice, + Stop::Software, + timeout, + )?; self.info.regs.cr1().modify(|w| w.set_tcie(true)); } } else if !(isr.tcr() || isr.tc()) { @@ -1102,12 +1108,10 @@ impl<'d, IM: MasterMode> I2c<'d, Async, IM> { let is_last = next.is_none(); let fut = self.write_dma_internal( - address, - c, - first, // first_slice - is_last, // last_slice - is_last, // send_stop (only on last buffer) - false, // restart (false for all - they're one continuous write) + address, c, first, // first_slice + is_last, // last_slice + is_last, // send_stop (only on last buffer) + false, // restart (false for all - they're one continuous write) timeout, ); timeout.with(fut).await?; -- cgit From 64b9c28eca4822a3ba1bd07d20964c2291c01cf5 Mon Sep 17 00:00:00 2001 From: xoviat Date: Thu, 13 Nov 2025 14:42:14 -0600 Subject: stm32: extract block_for_us remove from pub api --- embassy-stm32/src/adc/c0.rs | 2 +- embassy-stm32/src/adc/f1.rs | 4 ++-- embassy-stm32/src/adc/f3.rs | 2 +- embassy-stm32/src/adc/mod.rs | 20 ++------------------ embassy-stm32/src/dsihost.rs | 17 +++++------------ embassy-stm32/src/eth/generic_phy.rs | 14 +------------- embassy-stm32/src/lib.rs | 14 ++++++++++++++ embassy-stm32/src/opamp.rs | 13 +++---------- examples/stm32f469/src/bin/dsi_bsp.rs | 14 +++++++------- 9 files changed, 36 insertions(+), 64 deletions(-) diff --git a/embassy-stm32/src/adc/c0.rs b/embassy-stm32/src/adc/c0.rs index 983e7c10d..3bdca7edb 100644 --- a/embassy-stm32/src/adc/c0.rs +++ b/embassy-stm32/src/adc/c0.rs @@ -223,7 +223,7 @@ impl<'d, T: AnyInstance> Adc<'d, T> { // "The software must wait for the ADC voltage regulator startup time." // See datasheet for the value. - blocking_delay_us(TIME_ADC_VOLTAGE_REGUALTOR_STARTUP_US + 1); + blocking_delay_us(TIME_ADC_VOLTAGE_REGUALTOR_STARTUP_US as u64 + 1); T::regs().cfgr1().modify(|reg| reg.set_res(resolution)); diff --git a/embassy-stm32/src/adc/f1.rs b/embassy-stm32/src/adc/f1.rs index f6220de78..d6c6f480b 100644 --- a/embassy-stm32/src/adc/f1.rs +++ b/embassy-stm32/src/adc/f1.rs @@ -43,7 +43,7 @@ impl<'d, T: Instance> Adc<'d, T> { // 11.4: Before starting a calibration, the ADC must have been in power-on state (ADON bit = ‘1’) // for at least two ADC clock cycles. - blocking_delay_us((1_000_000 * 2) / Self::freq().0 + 1); + blocking_delay_us((1_000_000 * 2) / Self::freq().0 as u64 + 1); // Reset calibration T::regs().cr2().modify(|reg| reg.set_rstcal(true)); @@ -58,7 +58,7 @@ impl<'d, T: Instance> Adc<'d, T> { } // One cycle after calibration - blocking_delay_us((1_000_000 * 1) / Self::freq().0 + 1); + blocking_delay_us((1_000_000 * 1) / Self::freq().0 as u64 + 1); T::Interrupt::unpend(); unsafe { T::Interrupt::enable() }; diff --git a/embassy-stm32/src/adc/f3.rs b/embassy-stm32/src/adc/f3.rs index 4a77f3c5b..29bfdac97 100644 --- a/embassy-stm32/src/adc/f3.rs +++ b/embassy-stm32/src/adc/f3.rs @@ -62,7 +62,7 @@ impl<'d, T: Instance> Adc<'d, T> { while T::regs().cr().read().adcal() {} // Wait more than 4 clock cycles after adcal is cleared (RM0364 p. 223). - blocking_delay_us((1_000_000 * 4) / Self::freq().0 + 1); + blocking_delay_us((1_000_000 * 4) / Self::freq().0 as u64 + 1); // Enable the adc T::regs().cr().modify(|w| w.set_aden(true)); diff --git a/embassy-stm32/src/adc/mod.rs b/embassy-stm32/src/adc/mod.rs index 549f2f5a5..5ec08a22d 100644 --- a/embassy-stm32/src/adc/mod.rs +++ b/embassy-stm32/src/adc/mod.rs @@ -35,6 +35,8 @@ pub use ringbuffered::RingBufferedAdc; #[path = "adc4.rs"] pub mod adc4; +#[allow(unused)] +pub(self) use crate::block_for_us as blocking_delay_us; pub use crate::pac::adc::vals; #[cfg(not(any(adc_f1, adc_f3v3)))] pub use crate::pac::adc::vals::Res as Resolution; @@ -140,24 +142,6 @@ impl BasicAnyInstance for T { ))] impl AnyInstance for T {} -/// Performs a busy-wait delay for a specified number of microseconds. -#[allow(unused)] -pub(crate) fn blocking_delay_us(us: u32) { - cfg_if::cfg_if! { - // this does strange things on stm32wlx in low power mode depending on exactly when it's called - // as in sometimes 15 us (1 tick) would take > 20 seconds. - if #[cfg(all(feature = "time", all(not(feature = "low-power"), not(stm32wlex))))] { - let duration = embassy_time::Duration::from_micros(us as u64); - embassy_time::block_for(duration); - } else { - let freq = unsafe { crate::rcc::get_freqs() }.sys.to_hertz().unwrap().0 as u64; - let us = us as u64; - let cycles = freq * us / 1_000_000; - cortex_m::asm::delay(cycles as u32); - } - } -} - #[cfg(any(adc_c0, adc_v3, adc_g0, adc_h5, adc_h7rs, adc_u0, adc_v4, adc_u5))] /// Number of samples used for averaging. #[derive(Copy, Clone, Debug)] diff --git a/embassy-stm32/src/dsihost.rs b/embassy-stm32/src/dsihost.rs index 59a2cbcdb..b8945820c 100644 --- a/embassy-stm32/src/dsihost.rs +++ b/embassy-stm32/src/dsihost.rs @@ -5,18 +5,11 @@ use core::marker::PhantomData; use embassy_hal_internal::PeripheralType; //use crate::gpio::{AnyPin, SealedPin}; +use crate::block_for_us; use crate::gpio::{AfType, AnyPin, OutputType, Speed}; use crate::rcc::{self, RccPeripheral}; use crate::{Peri, peripherals}; -/// Performs a busy-wait delay for a specified number of microseconds. -pub fn blocking_delay_ms(ms: u32) { - #[cfg(feature = "time")] - embassy_time::block_for(embassy_time::Duration::from_millis(ms as u64)); - #[cfg(not(feature = "time"))] - cortex_m::asm::delay(unsafe { crate::rcc::get_freqs() }.sys.to_hertz().unwrap().0 / 1_000 * ms); -} - /// PacketTypes extracted from CubeMX #[repr(u8)] #[allow(dead_code)] @@ -334,7 +327,7 @@ impl<'d, T: Instance> DsiHost<'d, T> { if T::regs().gpsr().read().cmdfe() { return Ok(()); } - blocking_delay_ms(1); + block_for_us(1_000); } Err(Error::FifoTimeout) } @@ -345,7 +338,7 @@ impl<'d, T: Instance> DsiHost<'d, T> { if !T::regs().gpsr().read().cmdff() { return Ok(()); } - blocking_delay_ms(1); + block_for_us(1_000); } Err(Error::FifoTimeout) } @@ -356,7 +349,7 @@ impl<'d, T: Instance> DsiHost<'d, T> { if !self.read_busy() { return Ok(()); } - blocking_delay_ms(1); + block_for_us(1_000); } Err(Error::ReadTimeout) } @@ -367,7 +360,7 @@ impl<'d, T: Instance> DsiHost<'d, T> { if !T::regs().gpsr().read().prdfe() { return Ok(()); } - blocking_delay_ms(1); + block_for_us(1_000); } Err(Error::FifoTimeout) } diff --git a/embassy-stm32/src/eth/generic_phy.rs b/embassy-stm32/src/eth/generic_phy.rs index 774beef80..947874d7f 100644 --- a/embassy-stm32/src/eth/generic_phy.rs +++ b/embassy-stm32/src/eth/generic_phy.rs @@ -8,6 +8,7 @@ use embassy_time::{Duration, Timer}; use futures_util::FutureExt; use super::{Phy, StationManagement}; +use crate::block_for_us as blocking_delay_us; #[allow(dead_code)] mod phy_consts { @@ -76,19 +77,6 @@ impl GenericPhy { } } -// TODO: Factor out to shared functionality -fn blocking_delay_us(us: u32) { - #[cfg(feature = "time")] - embassy_time::block_for(Duration::from_micros(us as u64)); - #[cfg(not(feature = "time"))] - { - let freq = unsafe { crate::rcc::get_freqs() }.sys.to_hertz().unwrap().0 as u64; - let us = us as u64; - let cycles = freq * us / 1_000_000; - cortex_m::asm::delay(cycles as u32); - } -} - impl Phy for GenericPhy { fn phy_reset(&mut self, sm: &mut S) { // Detect SMI address diff --git a/embassy-stm32/src/lib.rs b/embassy-stm32/src/lib.rs index 680edf433..6e492946a 100644 --- a/embassy-stm32/src/lib.rs +++ b/embassy-stm32/src/lib.rs @@ -658,3 +658,17 @@ fn init_hw(config: Config) -> Peripherals { p }) } + +/// Performs a busy-wait delay for a specified number of microseconds. +#[allow(unused)] +pub(crate) fn block_for_us(us: u64) { + cfg_if::cfg_if! { + // this does strange things on stm32wlx in low power mode depending on exactly when it's called + // as in sometimes 15 us (1 tick) would take > 20 seconds. + if #[cfg(all(feature = "time", all(not(feature = "low-power"), not(stm32wlex))))] { + embassy_time::block_for(embassy_time::Duration::from_micros(us)); + } else { + cortex_m::asm::delay(unsafe { rcc::get_freqs().sys.to_hertz().unwrap().0 as u64 * us / 1_000_000 } as u32); + } + } +} diff --git a/embassy-stm32/src/opamp.rs b/embassy-stm32/src/opamp.rs index ac8d5de21..4a55f5bd3 100644 --- a/embassy-stm32/src/opamp.rs +++ b/embassy-stm32/src/opamp.rs @@ -4,19 +4,12 @@ use embassy_hal_internal::PeripheralType; use crate::Peri; +#[cfg(opamp_v5)] +use crate::block_for_us; use crate::pac::opamp::vals::*; #[cfg(not(any(stm32g4, stm32f3)))] use crate::rcc::RccInfo; -/// Performs a busy-wait delay for a specified number of microseconds. -#[cfg(opamp_v5)] -fn blocking_delay_ms(ms: u32) { - #[cfg(feature = "time")] - embassy_time::block_for(embassy_time::Duration::from_millis(ms as u64)); - #[cfg(not(feature = "time"))] - cortex_m::asm::delay(unsafe { crate::rcc::get_freqs() }.sys.to_hertz().unwrap().0 / 1_000 * ms); -} - /// Gain #[allow(missing_docs)] #[derive(Clone, Copy)] @@ -439,7 +432,7 @@ impl<'d, T: Instance> OpAmp<'d, T> { // The closer the trimming value is to the optimum trimming value, the longer it takes to stabilize // (with a maximum stabilization time remaining below 2 ms in any case) -- RM0440 25.3.7 - blocking_delay_ms(2); + block_for_us(2_000); if !T::regs().csr().read().calout() { if mid == 0 { diff --git a/examples/stm32f469/src/bin/dsi_bsp.rs b/examples/stm32f469/src/bin/dsi_bsp.rs index d659291ff..7ba4da72b 100644 --- a/examples/stm32f469/src/bin/dsi_bsp.rs +++ b/examples/stm32f469/src/bin/dsi_bsp.rs @@ -3,7 +3,7 @@ use defmt::*; use embassy_executor::Spawner; -use embassy_stm32::dsihost::{DsiHost, PacketType, blocking_delay_ms}; +use embassy_stm32::dsihost::{DsiHost, PacketType}; use embassy_stm32::gpio::{Level, Output, Speed}; use embassy_stm32::ltdc::Ltdc; use embassy_stm32::pac::dsihost::regs::{Ier0, Ier1}; @@ -13,7 +13,7 @@ use embassy_stm32::rcc::{ AHBPrescaler, APBPrescaler, Hse, HseMode, Pll, PllMul, PllPDiv, PllPreDiv, PllQDiv, PllRDiv, PllSource, Sysclk, }; use embassy_stm32::time::mhz; -use embassy_time::Timer; +use embassy_time::{Duration, Timer, block_for}; use {defmt_rtt as _, panic_probe as _}; enum _Orientation { @@ -444,7 +444,7 @@ async fn main(_spawner: Spawner) { dsi.enable_wrapper_dsi(); // First, delay 120 ms (reason unknown, STM32 Cube Example does it) - blocking_delay_ms(120); + block_for(Duration::from_millis(120)); // 1 to 26 dsi.write_cmd(0, NT35510_WRITES_0[0], &NT35510_WRITES_0[1..]).unwrap(); @@ -480,7 +480,7 @@ async fn main(_spawner: Spawner) { dsi.write_cmd(0, NT35510_WRITES_37[0], &NT35510_WRITES_37[1..]).unwrap(); // Add a delay, otherwise MADCTL not taken - blocking_delay_ms(200); + block_for(Duration::from_millis(200)); // Configure orientation as landscape dsi.write_cmd(0, NT35510_MADCTL_LANDSCAPE[0], &NT35510_MADCTL_LANDSCAPE[1..]) @@ -494,7 +494,7 @@ async fn main(_spawner: Spawner) { dsi.write_cmd(0, NT35510_WRITES_27[0], &NT35510_WRITES_27[1..]).unwrap(); // Wait for sleep out exit - blocking_delay_ms(120); + block_for(Duration::from_millis(120)); // Configure COLOR_CODING dsi.write_cmd(0, NT35510_WRITES_37[0], &NT35510_WRITES_37[1..]).unwrap(); @@ -590,7 +590,7 @@ async fn main(_spawner: Spawner) { //LTDC->SRCR = LTDC_SRCR_IMR; LTDC.srcr().modify(|w| w.set_imr(Imr::RELOAD)); - blocking_delay_ms(5000); + block_for(Duration::from_millis(5000)); const READ_SIZE: u16 = 1; let mut data = [1u8; READ_SIZE as usize]; @@ -606,7 +606,7 @@ async fn main(_spawner: Spawner) { .unwrap(); info!("Display ID3: {:#04x}", data); - blocking_delay_ms(500); + block_for(Duration::from_millis(500)); info!("Config done, start blinking LED"); loop { -- cgit From 3f9865f9d8e9be95cad25df3f535fbe01087ed83 Mon Sep 17 00:00:00 2001 From: datdenkikniet Date: Wed, 12 Nov 2025 21:39:40 +0100 Subject: Give EthernetManagement owernship of MDIO pins --- embassy-stm32/src/eth/v1/mod.rs | 50 +++++++++++++++++++++++++---------------- embassy-stm32/src/eth/v2/mod.rs | 50 +++++++++++++++++++++++++---------------- 2 files changed, 62 insertions(+), 38 deletions(-) diff --git a/embassy-stm32/src/eth/v1/mod.rs b/embassy-stm32/src/eth/v1/mod.rs index a77eb8719..91daa9d0b 100644 --- a/embassy-stm32/src/eth/v1/mod.rs +++ b/embassy-stm32/src/eth/v1/mod.rs @@ -53,14 +53,14 @@ pub struct Ethernet<'d, T: Instance, P: Phy> { pins: Pins<'d>, pub(crate) phy: P, - pub(crate) station_management: EthernetStationManagement, + pub(crate) station_management: EthernetStationManagement<'d, T>, pub(crate) mac_addr: [u8; 6], } /// Pins of ethernet driver. enum Pins<'d> { - Rmii([Peri<'d, AnyPin>; 9]), - Mii([Peri<'d, AnyPin>; 14]), + Rmii([Peri<'d, AnyPin>; 7]), + Mii([Peri<'d, AnyPin>; 12]), } #[cfg(eth_v1a)] @@ -157,8 +157,6 @@ impl<'d, T: Instance, P: Phy> Ethernet<'d, T, P> { let pins = Pins::Rmii([ ref_clk.into(), - mdio.into(), - mdc.into(), crs.into(), rx_d0.into(), rx_d1.into(), @@ -167,7 +165,7 @@ impl<'d, T: Instance, P: Phy> Ethernet<'d, T, P> { tx_en.into(), ]); - Self::new_inner(queue, peri, irq, pins, phy, mac_addr) + Self::new_inner(queue, peri, irq, pins, mdio, mdc, phy, mac_addr) } fn new_inner( @@ -175,6 +173,8 @@ impl<'d, T: Instance, P: Phy> Ethernet<'d, T, P> { peri: Peri<'d, T>, _irq: impl interrupt::typelevel::Binding + 'd, pins: Pins<'d>, + mdio: Peri<'d, if_afio!(impl MDIOPin)>, + mdc: Peri<'d, if_afio!(impl MDCPin)>, phy: P, mac_addr: [u8; 6], ) -> Self { @@ -249,6 +249,7 @@ impl<'d, T: Instance, P: Phy> Ethernet<'d, T, P> { station_management: EthernetStationManagement { peri: PhantomData, clock_range: clock_range, + pins: [mdio.into(), mdc.into()], }, mac_addr, tx: TDesRing::new(&mut queue.tx_desc, &mut queue.tx_buf), @@ -357,8 +358,6 @@ impl<'d, T: Instance, P: Phy> Ethernet<'d, T, P> { let pins = Pins::Mii([ rx_clk.into(), tx_clk.into(), - mdio.into(), - mdc.into(), rxdv.into(), rx_d0.into(), rx_d1.into(), @@ -371,43 +370,50 @@ impl<'d, T: Instance, P: Phy> Ethernet<'d, T, P> { tx_en.into(), ]); - Self::new_inner(queue, peri, irq, pins, phy, mac_addr) + Self::new_inner(queue, peri, irq, pins, mdio, mdc, phy, mac_addr) } } /// Ethernet station management interface. -pub(crate) struct EthernetStationManagement { +pub(crate) struct EthernetStationManagement<'d, T: Instance> { peri: PhantomData, clock_range: Cr, + pins: [Peri<'d, AnyPin>; 2], } -impl StationManagement for EthernetStationManagement { +impl StationManagement for EthernetStationManagement<'_, T> { fn smi_read(&mut self, phy_addr: u8, reg: u8) -> u16 { - let mac = T::regs().ethernet_mac(); + let (macmiiar, macmiidr) = { + let regs = T::regs().ethernet_mac(); + (regs.macmiiar(), regs.macmiidr()) + }; - mac.macmiiar().modify(|w| { + macmiiar.modify(|w| { w.set_pa(phy_addr); w.set_mr(reg); w.set_mw(Mw::READ); // read operation w.set_cr(self.clock_range); w.set_mb(MbProgress::BUSY); // indicate that operation is in progress }); - while mac.macmiiar().read().mb() == MbProgress::BUSY {} - mac.macmiidr().read().md() + while macmiiar.read().mb() == MbProgress::BUSY {} + macmiidr.read().md() } fn smi_write(&mut self, phy_addr: u8, reg: u8, val: u16) { - let mac = T::regs().ethernet_mac(); + let (macmiiar, macmiidr) = { + let regs = T::regs().ethernet_mac(); + (regs.macmiiar(), regs.macmiidr()) + }; - mac.macmiidr().write(|w| w.set_md(val)); - mac.macmiiar().modify(|w| { + macmiidr.write(|w| w.set_md(val)); + macmiiar.modify(|w| { w.set_pa(phy_addr); w.set_mr(reg); w.set_mw(Mw::WRITE); // write w.set_cr(self.clock_range); w.set_mb(MbProgress::BUSY); }); - while mac.macmiiar().read().mb() == MbProgress::BUSY {} + while macmiiar.read().mb() == MbProgress::BUSY {} } } @@ -437,3 +443,9 @@ impl<'d, T: Instance, P: Phy> Drop for Ethernet<'d, T, P> { }) } } + +impl Drop for EthernetStationManagement<'_, T> { + fn drop(&mut self) { + self.pins.iter_mut().for_each(|p| p.set_as_disconnected()); + } +} diff --git a/embassy-stm32/src/eth/v2/mod.rs b/embassy-stm32/src/eth/v2/mod.rs index 39a6e8b0f..61dea6f3f 100644 --- a/embassy-stm32/src/eth/v2/mod.rs +++ b/embassy-stm32/src/eth/v2/mod.rs @@ -42,14 +42,14 @@ pub struct Ethernet<'d, T: Instance, P: Phy> { pub(crate) rx: RDesRing<'d>, pins: Pins<'d>, pub(crate) phy: P, - pub(crate) station_management: EthernetStationManagement, + pub(crate) station_management: EthernetStationManagement<'d, T>, pub(crate) mac_addr: [u8; 6], } /// Pins of ethernet driver. enum Pins<'d> { - Rmii([Peri<'d, AnyPin>; 9]), - Mii([Peri<'d, AnyPin>; 14]), + Rmii([Peri<'d, AnyPin>; 7]), + Mii([Peri<'d, AnyPin>; 12]), } macro_rules! config_pins { @@ -96,8 +96,6 @@ impl<'d, T: Instance, P: Phy> Ethernet<'d, T, P> { let pins = Pins::Rmii([ ref_clk.into(), - mdio.into(), - mdc.into(), crs.into(), rx_d0.into(), rx_d1.into(), @@ -106,7 +104,7 @@ impl<'d, T: Instance, P: Phy> Ethernet<'d, T, P> { tx_en.into(), ]); - Self::new_inner(queue, peri, irq, pins, phy, mac_addr) + Self::new_inner(queue, peri, irq, pins, mdio, mdc, phy, mac_addr) } /// Create a new MII ethernet driver using 14 pins. @@ -151,8 +149,6 @@ impl<'d, T: Instance, P: Phy> Ethernet<'d, T, P> { let pins = Pins::Mii([ rx_clk.into(), tx_clk.into(), - mdio.into(), - mdc.into(), rxdv.into(), rx_d0.into(), rx_d1.into(), @@ -165,7 +161,7 @@ impl<'d, T: Instance, P: Phy> Ethernet<'d, T, P> { tx_en.into(), ]); - Self::new_inner(queue, peri, irq, pins, phy, mac_addr) + Self::new_inner(queue, peri, irq, pins, mdio, mdc, phy, mac_addr) } fn new_inner( @@ -173,6 +169,8 @@ impl<'d, T: Instance, P: Phy> Ethernet<'d, T, P> { peri: Peri<'d, T>, _irq: impl interrupt::typelevel::Binding + 'd, pins: Pins<'d>, + mdio: Peri<'d, impl MDIOPin>, + mdc: Peri<'d, impl MDCPin>, phy: P, mac_addr: [u8; 6], ) -> Self { @@ -262,6 +260,7 @@ impl<'d, T: Instance, P: Phy> Ethernet<'d, T, P> { station_management: EthernetStationManagement { peri: PhantomData, clock_range: clock_range, + pins: [mdio.into(), mdc.into()], }, mac_addr, }; @@ -299,38 +298,45 @@ impl<'d, T: Instance, P: Phy> Ethernet<'d, T, P> { } /// Ethernet SMI driver. -pub struct EthernetStationManagement { +pub struct EthernetStationManagement<'d, T: Instance> { peri: PhantomData, clock_range: u8, + pins: [Peri<'d, AnyPin>; 2], } -impl StationManagement for EthernetStationManagement { +impl StationManagement for EthernetStationManagement<'_, T> { fn smi_read(&mut self, phy_addr: u8, reg: u8) -> u16 { - let mac = T::regs().ethernet_mac(); + let (macmdioar, macmdiodr) = { + let regs = T::regs().ethernet_mac(); + (regs.macmdioar(), regs.macmdiodr()) + }; - mac.macmdioar().modify(|w| { + macmdioar.modify(|w| { w.set_pa(phy_addr); w.set_rda(reg); w.set_goc(0b11); // read w.set_cr(self.clock_range); w.set_mb(true); }); - while mac.macmdioar().read().mb() {} - mac.macmdiodr().read().md() + while macmdioar.read().mb() {} + macmdiodr.read().md() } fn smi_write(&mut self, phy_addr: u8, reg: u8, val: u16) { - let mac = T::regs().ethernet_mac(); + let (macmdioar, macmdiodr) = { + let regs = T::regs().ethernet_mac(); + (regs.macmdioar(), regs.macmdiodr()) + }; - mac.macmdiodr().write(|w| w.set_md(val)); - mac.macmdioar().modify(|w| { + macmdiodr.write(|w| w.set_md(val)); + macmdioar.modify(|w| { w.set_pa(phy_addr); w.set_rda(reg); w.set_goc(0b01); // write w.set_cr(self.clock_range); w.set_mb(true); }); - while mac.macmdioar().read().mb() {} + while macmdioar.read().mb() {} } } @@ -370,3 +376,9 @@ impl<'d, T: Instance, P: Phy> Drop for Ethernet<'d, T, P> { }) } } + +impl Drop for EthernetStationManagement<'_, T> { + fn drop(&mut self) { + self.pins.iter_mut().for_each(|p| p.set_as_disconnected()); + } +} -- cgit From aad63ecd597a5b12663bf12329327de6d9cd65f2 Mon Sep 17 00:00:00 2001 From: datdenkikniet Date: Wed, 12 Nov 2025 21:43:50 +0100 Subject: Add SMA peripheral --- embassy-stm32/build.rs | 5 +++++ embassy-stm32/src/eth/mod.rs | 1 + embassy-stm32/src/eth/sma/mod.rs | 33 +++++++++++++++++++++++++++++++++ 3 files changed, 39 insertions(+) create mode 100644 embassy-stm32/src/eth/sma/mod.rs diff --git a/embassy-stm32/build.rs b/embassy-stm32/build.rs index 1e11eb8dc..9dd94941c 100644 --- a/embassy-stm32/build.rs +++ b/embassy-stm32/build.rs @@ -170,6 +170,11 @@ fn main() { } singletons.push(p.name.to_string()); } + + "eth" => { + singletons.push(p.name.to_string()); + singletons.push("ETH_SMA".to_string()); + } //"dbgmcu" => {} //"syscfg" => {} //"dma" => {} diff --git a/embassy-stm32/src/eth/mod.rs b/embassy-stm32/src/eth/mod.rs index 10b3a0517..bef6a02b2 100644 --- a/embassy-stm32/src/eth/mod.rs +++ b/embassy-stm32/src/eth/mod.rs @@ -5,6 +5,7 @@ #[cfg_attr(eth_v2, path = "v2/mod.rs")] mod _version; mod generic_phy; +mod sma; use core::mem::MaybeUninit; use core::task::Context; diff --git a/embassy-stm32/src/eth/sma/mod.rs b/embassy-stm32/src/eth/sma/mod.rs new file mode 100644 index 000000000..106a6b2bd --- /dev/null +++ b/embassy-stm32/src/eth/sma/mod.rs @@ -0,0 +1,33 @@ +//! Station Management Agent (also known as MDIO or SMI). + +#![macro_use] + +use embassy_hal_internal::PeripheralType; +#[cfg(eth_v2)] +pub(crate) use regs::{Macmdioar as AddressRegister, Macmdiodr as DataRegister}; +#[cfg(any(eth_v1a, eth_v1b, eth_v1c))] +pub(crate) use regs::{Macmiiar as AddressRegister, Macmiidr as DataRegister}; +use stm32_metapac::common::{RW, Reg}; +use stm32_metapac::eth::regs; + +trait SealedInstance { + fn regs() -> (Reg, Reg); +} + +/// MDIO instance. +#[allow(private_bounds)] +pub trait Instance: SealedInstance + PeripheralType + Send + 'static {} + +impl SealedInstance for crate::peripherals::ETH_SMA { + fn regs() -> (Reg, Reg) { + let mac = crate::pac::ETH.ethernet_mac(); + + #[cfg(any(eth_v1a, eth_v1b, eth_v1c))] + return (mac.macmiiar(), mac.macmiidr()); + + #[cfg(eth_v2)] + return (mac.macmdioar(), mac.macmdiodr()); + } +} + +impl Instance for crate::peripherals::ETH_SMA {} -- cgit From a021b4940c852f01322a20a7358bc1a549b3d3c4 Mon Sep 17 00:00:00 2001 From: datdenkikniet Date: Wed, 12 Nov 2025 21:51:55 +0100 Subject: Move SMA responsibility to SMA peripheral --- embassy-stm32/build.rs | 5 ++ embassy-stm32/src/eth/mod.rs | 13 ++--- embassy-stm32/src/eth/sma/mod.rs | 32 ++++++++++-- embassy-stm32/src/eth/sma/v1.rs | 102 +++++++++++++++++++++++++++++++++++++++ embassy-stm32/src/eth/sma/v2.rs | 94 ++++++++++++++++++++++++++++++++++++ embassy-stm32/src/eth/v1/mod.rs | 89 +++++----------------------------- embassy-stm32/src/eth/v2/mod.rs | 81 +++---------------------------- 7 files changed, 249 insertions(+), 167 deletions(-) create mode 100644 embassy-stm32/src/eth/sma/v1.rs create mode 100644 embassy-stm32/src/eth/sma/v2.rs diff --git a/embassy-stm32/build.rs b/embassy-stm32/build.rs index 9dd94941c..8cbd38e10 100644 --- a/embassy-stm32/build.rs +++ b/embassy-stm32/build.rs @@ -1404,6 +1404,11 @@ fn main() { } } + // MDIO and MDC are special + if pin.signal == "MDIO" || pin.signal == "MDC" { + peri = format_ident!("{}", "ETH_SMA"); + } + // XSPI NCS pin to CSSEL mapping if pin.signal.ends_with("NCS1") { g.extend(quote! { diff --git a/embassy-stm32/src/eth/mod.rs b/embassy-stm32/src/eth/mod.rs index bef6a02b2..448d21f3f 100644 --- a/embassy-stm32/src/eth/mod.rs +++ b/embassy-stm32/src/eth/mod.rs @@ -16,6 +16,7 @@ use embassy_sync::waitqueue::AtomicWaker; pub use self::_version::{InterruptHandler, *}; pub use self::generic_phy::*; +pub use self::sma::{Sma, StationManagement}; use crate::rcc::RccPeripheral; #[allow(unused)] @@ -158,14 +159,6 @@ impl<'a, 'd> embassy_net_driver::TxToken for TxToken<'a, 'd> { } } -/// Station Management Interface (SMI) on an ethernet PHY -pub trait StationManagement { - /// Read a register over SMI. - fn smi_read(&mut self, phy_addr: u8, reg: u8) -> u16; - /// Write a register over SMI. - fn smi_write(&mut self, phy_addr: u8, reg: u8, val: u16); -} - /// Trait for an Ethernet PHY pub trait Phy { /// Reset PHY and wait for it to come out of reset. @@ -213,8 +206,8 @@ impl Instance for crate::peripherals::ETH {} pin_trait!(RXClkPin, Instance, @A); pin_trait!(TXClkPin, Instance, @A); pin_trait!(RefClkPin, Instance, @A); -pin_trait!(MDIOPin, Instance, @A); -pin_trait!(MDCPin, Instance, @A); +pin_trait!(MDIOPin, sma::Instance, @A); +pin_trait!(MDCPin, sma::Instance, @A); pin_trait!(RXDVPin, Instance, @A); pin_trait!(CRSPin, Instance, @A); pin_trait!(RXD0Pin, Instance, @A); diff --git a/embassy-stm32/src/eth/sma/mod.rs b/embassy-stm32/src/eth/sma/mod.rs index 106a6b2bd..558107abc 100644 --- a/embassy-stm32/src/eth/sma/mod.rs +++ b/embassy-stm32/src/eth/sma/mod.rs @@ -2,13 +2,22 @@ #![macro_use] +#[cfg_attr(eth_v2, path = "v2.rs")] +#[cfg_attr(any(eth_v1a, eth_v1b, eth_v1c), path = "v1.rs")] +mod _version; + use embassy_hal_internal::PeripheralType; -#[cfg(eth_v2)] -pub(crate) use regs::{Macmdioar as AddressRegister, Macmdiodr as DataRegister}; -#[cfg(any(eth_v1a, eth_v1b, eth_v1c))] -pub(crate) use regs::{Macmiiar as AddressRegister, Macmiidr as DataRegister}; use stm32_metapac::common::{RW, Reg}; -use stm32_metapac::eth::regs; + +pub use self::_version::*; + +/// Station Management Interface (SMI). +pub trait StationManagement { + /// Read a register over SMI. + fn smi_read(&mut self, phy_addr: u8, reg: u8) -> u16; + /// Write a register over SMI. + fn smi_write(&mut self, phy_addr: u8, reg: u8, val: u16); +} trait SealedInstance { fn regs() -> (Reg, Reg); @@ -30,4 +39,17 @@ impl SealedInstance for crate::peripherals::ETH_SMA { } } +impl SealedInstance for T { + fn regs() -> (Reg, Reg) { + let mac = ::regs().ethernet_mac(); + + #[cfg(any(eth_v1a, eth_v1b, eth_v1c))] + return (mac.macmiiar(), mac.macmiidr()); + + #[cfg(eth_v2)] + return (mac.macmdioar(), mac.macmdiodr()); + } +} + impl Instance for crate::peripherals::ETH_SMA {} +impl Instance for T {} diff --git a/embassy-stm32/src/eth/sma/v1.rs b/embassy-stm32/src/eth/sma/v1.rs new file mode 100644 index 000000000..db64a6c78 --- /dev/null +++ b/embassy-stm32/src/eth/sma/v1.rs @@ -0,0 +1,102 @@ +use embassy_hal_internal::Peri; +pub(crate) use regs::{Macmiiar as AddressRegister, Macmiidr as DataRegister}; +use stm32_metapac::eth::regs; +use stm32_metapac::eth::vals::{Cr, MbProgress, Mw}; + +use super::{Instance, StationManagement}; +use crate::eth::{MDCPin, MDIOPin}; +use crate::gpio::{AfType, AnyPin, OutputType, SealedPin, Speed}; + +/// Station Management Agent. +/// +/// This peripheral is used for SMI reads and writes to the connected +/// ethernet PHY/device(s). +pub struct Sma<'d, T: Instance> { + _peri: Peri<'d, T>, + clock_range: Cr, + pins: [Peri<'d, AnyPin>; 2], +} + +impl<'d, T: Instance> Sma<'d, T> { + /// Create a new instance of this peripheral. + pub fn new<#[cfg(afio)] A>( + peri: Peri<'d, T>, + mdio: Peri<'d, if_afio!(impl MDIOPin)>, + mdc: Peri<'d, if_afio!(impl MDCPin)>, + ) -> Self { + set_as_af!(mdio, AfType::output(OutputType::PushPull, Speed::VeryHigh)); + set_as_af!(mdc, AfType::output(OutputType::PushPull, Speed::VeryHigh)); + + // Enable necessary clocks. + critical_section::with(|_| { + #[cfg(eth_v1a)] + let reg = crate::pac::RCC.ahbenr(); + + #[cfg(any(eth_v1b, eth_v1c))] + let reg = crate::pac::RCC.ahb1enr(); + + reg.modify(|w| { + w.set_ethen(true); + }) + }); + + let hclk = unsafe { crate::rcc::get_freqs().hclk1.to_hertz() }; + let hclk = unwrap!(hclk, "SMA requires HCLK to be enabled, but it was not."); + let hclk_mhz = hclk.0 / 1_000_000; + + // Set the MDC clock frequency in the range 1MHz - 2.5MHz + let clock_range = match hclk_mhz { + 0..=24 => panic!("Invalid HCLK frequency - should be at least 25 MHz."), + 25..=34 => Cr::CR_20_35, // Divide by 16 + 35..=59 => Cr::CR_35_60, // Divide by 26 + 60..=99 => Cr::CR_60_100, // Divide by 42 + 100..=149 => Cr::CR_100_150, // Divide by 62 + 150..=216 => Cr::CR_150_168, // Divide by 102 + _ => { + panic!("HCLK results in MDC clock > 2.5MHz even for the highest CSR clock divider") + } + }; + + Self { + _peri: peri, + clock_range, + pins: [mdio.into(), mdc.into()], + } + } +} + +impl StationManagement for Sma<'_, T> { + fn smi_read(&mut self, phy_addr: u8, reg: u8) -> u16 { + let (macmiiar, macmiidr) = T::regs(); + + macmiiar.modify(|w| { + w.set_pa(phy_addr); + w.set_mr(reg); + w.set_mw(Mw::READ); // read operation + w.set_cr(self.clock_range); + w.set_mb(MbProgress::BUSY); // indicate that operation is in progress + }); + while macmiiar.read().mb() == MbProgress::BUSY {} + macmiidr.read().md() + } + + fn smi_write(&mut self, phy_addr: u8, reg: u8, val: u16) { + let (macmiiar, macmiidr) = T::regs(); + + macmiidr.write(|w| w.set_md(val)); + macmiiar.modify(|w| { + w.set_pa(phy_addr); + w.set_mr(reg); + w.set_mw(Mw::WRITE); // write + w.set_cr(self.clock_range); + w.set_mb(MbProgress::BUSY); + }); + while macmiiar.read().mb() == MbProgress::BUSY {} + } +} + +impl Drop for Sma<'_, T> { + fn drop(&mut self) { + self.pins.iter_mut().for_each(|p| p.set_as_disconnected()); + } +} diff --git a/embassy-stm32/src/eth/sma/v2.rs b/embassy-stm32/src/eth/sma/v2.rs new file mode 100644 index 000000000..6bc5230b5 --- /dev/null +++ b/embassy-stm32/src/eth/sma/v2.rs @@ -0,0 +1,94 @@ +use embassy_hal_internal::Peri; +pub(crate) use regs::{Macmdioar as AddressRegister, Macmdiodr as DataRegister}; +use stm32_metapac::eth::regs; + +use super::{Instance, StationManagement}; +use crate::eth::{MDCPin, MDIOPin}; +use crate::gpio::{AfType, AnyPin, OutputType, SealedPin, Speed}; + +/// Station Management Agent. +/// +/// This peripheral is used for SMI reads and writes to the connected +/// ethernet PHY/device(s). +pub struct Sma<'d, T: Instance> { + _peri: Peri<'d, T>, + pins: [Peri<'d, AnyPin>; 2], + clock_range: u8, +} + +impl<'d, T: Instance> Sma<'d, T> { + /// Create a new instance of this peripheral. + pub fn new(peri: Peri<'d, T>, mdio: Peri<'d, impl MDIOPin>, mdc: Peri<'d, impl MDCPin>) -> Self { + set_as_af!(mdio, AfType::output(OutputType::PushPull, Speed::VeryHigh)); + set_as_af!(mdc, AfType::output(OutputType::PushPull, Speed::VeryHigh)); + + // Enable necessary clocks. + critical_section::with(|_| { + crate::pac::RCC.ahb1enr().modify(|w| { + w.set_ethen(true); + }) + }); + + let hclk = unsafe { crate::rcc::get_freqs().hclk1.to_hertz() }; + let hclk = unwrap!(hclk, "SMA requires HCLK to be enabled, but it was not."); + let hclk_mhz = hclk.0 / 1_000_000; + + // Set the MDC clock frequency in the range 1MHz - 2.5MHz + let clock_range = match hclk_mhz { + 0..=34 => 2, // Divide by 16 + 35..=59 => 3, // Divide by 26 + 60..=99 => 0, // Divide by 42 + 100..=149 => 1, // Divide by 62 + 150..=249 => 4, // Divide by 102 + 250..=310 => 5, // Divide by 124 + _ => { + panic!("HCLK results in MDC clock > 2.5MHz even for the highest CSR clock divider") + } + }; + + Self { + _peri: peri, + clock_range, + pins: [mdio.into(), mdc.into()], + } + } +} + +impl StationManagement for Sma<'_, T> { + fn smi_read(&mut self, phy_addr: u8, reg: u8) -> u16 { + let (macmdioar, macmdiodr) = T::regs(); + + macmdioar.modify(|w| { + w.set_pa(phy_addr); + w.set_rda(reg); + w.set_goc(0b11); // read + w.set_cr(self.clock_range); + w.set_mb(true); + }); + + while macmdioar.read().mb() {} + + macmdiodr.read().md() + } + + fn smi_write(&mut self, phy_addr: u8, reg: u8, val: u16) { + let (macmdioar, macmdiodr) = T::regs(); + + macmdiodr.write(|w| w.set_md(val)); + macmdioar.modify(|w| { + w.set_pa(phy_addr); + w.set_rda(reg); + w.set_goc(0b01); // write + w.set_cr(self.clock_range); + w.set_mb(true); + }); + + while macmdioar.read().mb() {} + } +} + +impl Drop for Sma<'_, T> { + fn drop(&mut self) { + self.pins.iter_mut().for_each(|p| p.set_as_disconnected()); + } +} diff --git a/embassy-stm32/src/eth/v1/mod.rs b/embassy-stm32/src/eth/v1/mod.rs index 91daa9d0b..d448537c8 100644 --- a/embassy-stm32/src/eth/v1/mod.rs +++ b/embassy-stm32/src/eth/v1/mod.rs @@ -3,15 +3,16 @@ mod rx_desc; mod tx_desc; -use core::marker::PhantomData; use core::sync::atomic::{Ordering, fence}; use embassy_hal_internal::Peri; -use stm32_metapac::eth::vals::{Apcs, Cr, Dm, DmaomrSr, Fes, Ftf, Ifg, MbProgress, Mw, Pbl, Rsf, St, Tsf}; +use stm32_metapac::eth::vals::{Apcs, Dm, DmaomrSr, Fes, Ftf, Ifg, Pbl, Rsf, St, Tsf}; pub(crate) use self::rx_desc::{RDes, RDesRing}; pub(crate) use self::tx_desc::{TDes, TDesRing}; +use super::sma::Sma; use super::*; +use crate::eth::{MDCPin, MDIOPin}; #[cfg(eth_v1a)] use crate::gpio::Pull; use crate::gpio::{AfType, AnyPin, OutputType, SealedPin, Speed}; @@ -22,7 +23,6 @@ use crate::pac::AFIO; #[cfg(any(eth_v1b, eth_v1c))] use crate::pac::SYSCFG; use crate::pac::{ETH, RCC}; -use crate::rcc::SealedRccPeripheral; /// Interrupt handler. pub struct InterruptHandler {} @@ -53,7 +53,7 @@ pub struct Ethernet<'d, T: Instance, P: Phy> { pins: Pins<'d>, pub(crate) phy: P, - pub(crate) station_management: EthernetStationManagement<'d, T>, + pub(crate) station_management: Sma<'d, T>, pub(crate) mac_addr: [u8; 6], } @@ -149,11 +149,11 @@ impl<'d, T: Instance, P: Phy> Ethernet<'d, T, P> { #[cfg(eth_v1a)] { config_in_pins!(ref_clk, rx_d0, rx_d1); - config_af_pins!(mdio, mdc, tx_d0, tx_d1, tx_en); + config_af_pins!(tx_d0, tx_d1, tx_en); } #[cfg(any(eth_v1b, eth_v1c))] - config_pins!(ref_clk, mdio, mdc, crs, rx_d0, rx_d1, tx_d0, tx_d1, tx_en); + config_pins!(ref_clk, crs, rx_d0, rx_d1, tx_d0, tx_d1, tx_en); let pins = Pins::Rmii([ ref_clk.into(), @@ -168,7 +168,7 @@ impl<'d, T: Instance, P: Phy> Ethernet<'d, T, P> { Self::new_inner(queue, peri, irq, pins, mdio, mdc, phy, mac_addr) } - fn new_inner( + fn new_inner( queue: &'d mut PacketQueue, peri: Peri<'d, T>, _irq: impl interrupt::typelevel::Binding + 'd, @@ -226,31 +226,13 @@ impl<'d, T: Instance, P: Phy> Ethernet<'d, T, P> { // TODO MTU size setting not found for v1 ethernet, check if correct - let hclk = ::frequency(); - let hclk_mhz = hclk.0 / 1_000_000; - - // Set the MDC clock frequency in the range 1MHz - 2.5MHz - let clock_range = match hclk_mhz { - 0..=24 => panic!("Invalid HCLK frequency - should be at least 25 MHz."), - 25..=34 => Cr::CR_20_35, // Divide by 16 - 35..=59 => Cr::CR_35_60, // Divide by 26 - 60..=99 => Cr::CR_60_100, // Divide by 42 - 100..=149 => Cr::CR_100_150, // Divide by 62 - 150..=216 => Cr::CR_150_168, // Divide by 102 - _ => { - panic!("HCLK results in MDC clock > 2.5MHz even for the highest CSR clock divider") - } - }; + let sma_peri = unsafe { peri.clone_unchecked() }; let mut this = Self { _peri: peri, pins, phy: phy, - station_management: EthernetStationManagement { - peri: PhantomData, - clock_range: clock_range, - pins: [mdio.into(), mdc.into()], - }, + station_management: Sma::new(sma_peri, mdio, mdc), mac_addr, tx: TDesRing::new(&mut queue.tx_desc, &mut queue.tx_buf), rx: RDesRing::new(&mut queue.rx_desc, &mut queue.rx_buf), @@ -347,12 +329,12 @@ impl<'d, T: Instance, P: Phy> Ethernet<'d, T, P> { #[cfg(eth_v1a)] { config_in_pins!(rx_clk, tx_clk, rx_d0, rx_d1, rx_d2, rx_d3, rxdv); - config_af_pins!(mdio, mdc, tx_d0, tx_d1, tx_d2, tx_d3, tx_en); + config_af_pins!(tx_d0, tx_d1, tx_d2, tx_d3, tx_en); } #[cfg(any(eth_v1b, eth_v1c))] config_pins!( - rx_clk, tx_clk, mdio, mdc, rxdv, rx_d0, rx_d1, rx_d2, rx_d3, tx_d0, tx_d1, tx_d2, tx_d3, tx_en + rx_clk, tx_clk, rxdv, rx_d0, rx_d1, rx_d2, rx_d3, tx_d0, tx_d1, tx_d2, tx_d3, tx_en ); let pins = Pins::Mii([ @@ -374,49 +356,6 @@ impl<'d, T: Instance, P: Phy> Ethernet<'d, T, P> { } } -/// Ethernet station management interface. -pub(crate) struct EthernetStationManagement<'d, T: Instance> { - peri: PhantomData, - clock_range: Cr, - pins: [Peri<'d, AnyPin>; 2], -} - -impl StationManagement for EthernetStationManagement<'_, T> { - fn smi_read(&mut self, phy_addr: u8, reg: u8) -> u16 { - let (macmiiar, macmiidr) = { - let regs = T::regs().ethernet_mac(); - (regs.macmiiar(), regs.macmiidr()) - }; - - macmiiar.modify(|w| { - w.set_pa(phy_addr); - w.set_mr(reg); - w.set_mw(Mw::READ); // read operation - w.set_cr(self.clock_range); - w.set_mb(MbProgress::BUSY); // indicate that operation is in progress - }); - while macmiiar.read().mb() == MbProgress::BUSY {} - macmiidr.read().md() - } - - fn smi_write(&mut self, phy_addr: u8, reg: u8, val: u16) { - let (macmiiar, macmiidr) = { - let regs = T::regs().ethernet_mac(); - (regs.macmiiar(), regs.macmiidr()) - }; - - macmiidr.write(|w| w.set_md(val)); - macmiiar.modify(|w| { - w.set_pa(phy_addr); - w.set_mr(reg); - w.set_mw(Mw::WRITE); // write - w.set_cr(self.clock_range); - w.set_mb(MbProgress::BUSY); - }); - while macmiiar.read().mb() == MbProgress::BUSY {} - } -} - impl<'d, T: Instance, P: Phy> Drop for Ethernet<'d, T, P> { fn drop(&mut self) { let dma = T::regs().ethernet_dma(); @@ -443,9 +382,3 @@ impl<'d, T: Instance, P: Phy> Drop for Ethernet<'d, T, P> { }) } } - -impl Drop for EthernetStationManagement<'_, T> { - fn drop(&mut self) { - self.pins.iter_mut().for_each(|p| p.set_as_disconnected()); - } -} diff --git a/embassy-stm32/src/eth/v2/mod.rs b/embassy-stm32/src/eth/v2/mod.rs index 61dea6f3f..0db335a7c 100644 --- a/embassy-stm32/src/eth/v2/mod.rs +++ b/embassy-stm32/src/eth/v2/mod.rs @@ -1,18 +1,18 @@ mod descriptors; -use core::marker::PhantomData; use core::sync::atomic::{Ordering, fence}; use embassy_hal_internal::Peri; use stm32_metapac::syscfg::vals::EthSelPhy; pub(crate) use self::descriptors::{RDes, RDesRing, TDes, TDesRing}; +use super::sma::Sma; use super::*; +use crate::eth::{MDCPin, MDIOPin}; use crate::gpio::{AfType, AnyPin, OutputType, SealedPin as _, Speed}; use crate::interrupt; use crate::interrupt::InterruptExt; use crate::pac::ETH; -use crate::rcc::SealedRccPeripheral; /// Interrupt handler. pub struct InterruptHandler {} @@ -42,7 +42,7 @@ pub struct Ethernet<'d, T: Instance, P: Phy> { pub(crate) rx: RDesRing<'d>, pins: Pins<'d>, pub(crate) phy: P, - pub(crate) station_management: EthernetStationManagement<'d, T>, + pub(crate) station_management: Sma<'d, T>, pub(crate) mac_addr: [u8; 6], } @@ -92,7 +92,7 @@ impl<'d, T: Instance, P: Phy> Ethernet<'d, T, P> { crate::pac::SYSCFG.pmcr().modify(|w| w.set_eth_sel_phy(EthSelPhy::RMII)); }); - config_pins!(ref_clk, mdio, mdc, crs, rx_d0, rx_d1, tx_d0, tx_d1, tx_en); + config_pins!(ref_clk, crs, rx_d0, rx_d1, tx_d0, tx_d1, tx_en); let pins = Pins::Rmii([ ref_clk.into(), @@ -143,7 +143,7 @@ impl<'d, T: Instance, P: Phy> Ethernet<'d, T, P> { }); config_pins!( - rx_clk, tx_clk, mdio, mdc, rxdv, rx_d0, rx_d1, rx_d2, rx_d3, tx_d0, tx_d1, tx_d2, tx_d3, tx_en + rx_clk, tx_clk, rxdv, rx_d0, rx_d1, rx_d2, rx_d3, tx_d0, tx_d1, tx_d2, tx_d3, tx_en ); let pins = Pins::Mii([ @@ -235,21 +235,7 @@ impl<'d, T: Instance, P: Phy> Ethernet<'d, T, P> { w.set_rbsz(RX_BUFFER_SIZE as u16); }); - let hclk = ::frequency(); - let hclk_mhz = hclk.0 / 1_000_000; - - // Set the MDC clock frequency in the range 1MHz - 2.5MHz - let clock_range = match hclk_mhz { - 0..=34 => 2, // Divide by 16 - 35..=59 => 3, // Divide by 26 - 60..=99 => 0, // Divide by 42 - 100..=149 => 1, // Divide by 62 - 150..=249 => 4, // Divide by 102 - 250..=310 => 5, // Divide by 124 - _ => { - panic!("HCLK results in MDC clock > 2.5MHz even for the highest CSR clock divider") - } - }; + let sma_peri = unsafe { peri.clone_unchecked() }; let mut this = Self { _peri: peri, @@ -257,11 +243,7 @@ impl<'d, T: Instance, P: Phy> Ethernet<'d, T, P> { rx: RDesRing::new(&mut queue.rx_desc, &mut queue.rx_buf), pins, phy, - station_management: EthernetStationManagement { - peri: PhantomData, - clock_range: clock_range, - pins: [mdio.into(), mdc.into()], - }, + station_management: Sma::new(sma_peri, mdio, mdc), mac_addr, }; @@ -297,49 +279,6 @@ impl<'d, T: Instance, P: Phy> Ethernet<'d, T, P> { } } -/// Ethernet SMI driver. -pub struct EthernetStationManagement<'d, T: Instance> { - peri: PhantomData, - clock_range: u8, - pins: [Peri<'d, AnyPin>; 2], -} - -impl StationManagement for EthernetStationManagement<'_, T> { - fn smi_read(&mut self, phy_addr: u8, reg: u8) -> u16 { - let (macmdioar, macmdiodr) = { - let regs = T::regs().ethernet_mac(); - (regs.macmdioar(), regs.macmdiodr()) - }; - - macmdioar.modify(|w| { - w.set_pa(phy_addr); - w.set_rda(reg); - w.set_goc(0b11); // read - w.set_cr(self.clock_range); - w.set_mb(true); - }); - while macmdioar.read().mb() {} - macmdiodr.read().md() - } - - fn smi_write(&mut self, phy_addr: u8, reg: u8, val: u16) { - let (macmdioar, macmdiodr) = { - let regs = T::regs().ethernet_mac(); - (regs.macmdioar(), regs.macmdiodr()) - }; - - macmdiodr.write(|w| w.set_md(val)); - macmdioar.modify(|w| { - w.set_pa(phy_addr); - w.set_rda(reg); - w.set_goc(0b01); // write - w.set_cr(self.clock_range); - w.set_mb(true); - }); - while macmdioar.read().mb() {} - } -} - impl<'d, T: Instance, P: Phy> Drop for Ethernet<'d, T, P> { fn drop(&mut self) { let dma = T::regs().ethernet_dma(); @@ -376,9 +315,3 @@ impl<'d, T: Instance, P: Phy> Drop for Ethernet<'d, T, P> { }) } } - -impl Drop for EthernetStationManagement<'_, T> { - fn drop(&mut self) { - self.pins.iter_mut().for_each(|p| p.set_as_disconnected()); - } -} -- cgit From 44881e643702b9ca41fc72109c3d16afd4b0449b Mon Sep 17 00:00:00 2001 From: datdenkikniet Date: Wed, 12 Nov 2025 22:02:09 +0100 Subject: ETH no longer knows about Station management --- embassy-stm32/src/eth/generic_phy.rs | 45 +++++++++++++++++++----------------- embassy-stm32/src/eth/mod.rs | 15 ++++-------- embassy-stm32/src/eth/sma/mod.rs | 13 ----------- embassy-stm32/src/eth/v1/mod.rs | 24 +++++-------------- embassy-stm32/src/eth/v2/mod.rs | 24 +++++-------------- tests/stm32/src/bin/eth.rs | 14 ++++++----- 6 files changed, 48 insertions(+), 87 deletions(-) diff --git a/embassy-stm32/src/eth/generic_phy.rs b/embassy-stm32/src/eth/generic_phy.rs index 947874d7f..4e61a83e7 100644 --- a/embassy-stm32/src/eth/generic_phy.rs +++ b/embassy-stm32/src/eth/generic_phy.rs @@ -44,21 +44,23 @@ mod phy_consts { use self::phy_consts::*; /// Generic SMI Ethernet PHY implementation -pub struct GenericPhy { +pub struct GenericPhy { phy_addr: u8, + sm: SM, #[cfg(feature = "time")] poll_interval: Duration, } -impl GenericPhy { +impl GenericPhy { /// Construct the PHY. It assumes the address `phy_addr` in the SMI communication /// /// # Panics /// `phy_addr` must be in range `0..32` - pub fn new(phy_addr: u8) -> Self { + pub fn new(sm: SM, phy_addr: u8) -> Self { assert!(phy_addr < 32); Self { phy_addr, + sm, #[cfg(feature = "time")] poll_interval: Duration::from_millis(500), } @@ -68,8 +70,9 @@ impl GenericPhy { /// /// # Panics /// Initialization panics if PHY didn't respond on any address - pub fn new_auto() -> Self { + pub fn new_auto(sm: SM) -> Self { Self { + sm, phy_addr: 0xFF, #[cfg(feature = "time")] poll_interval: Duration::from_millis(500), @@ -77,14 +80,14 @@ impl GenericPhy { } } -impl Phy for GenericPhy { - fn phy_reset(&mut self, sm: &mut S) { +impl Phy for GenericPhy { + fn phy_reset(&mut self) { // Detect SMI address if self.phy_addr == 0xFF { for addr in 0..32 { - sm.smi_write(addr, PHY_REG_BCR, PHY_REG_BCR_RESET); + self.sm.smi_write(addr, PHY_REG_BCR, PHY_REG_BCR_RESET); for _ in 0..10 { - if sm.smi_read(addr, PHY_REG_BCR) & PHY_REG_BCR_RESET != PHY_REG_BCR_RESET { + if self.sm.smi_read(addr, PHY_REG_BCR) & PHY_REG_BCR_RESET != PHY_REG_BCR_RESET { trace!("Found ETH PHY on address {}", addr); self.phy_addr = addr; return; @@ -96,30 +99,30 @@ impl Phy for GenericPhy { panic!("PHY did not respond"); } - sm.smi_write(self.phy_addr, PHY_REG_BCR, PHY_REG_BCR_RESET); - while sm.smi_read(self.phy_addr, PHY_REG_BCR) & PHY_REG_BCR_RESET == PHY_REG_BCR_RESET {} + self.sm.smi_write(self.phy_addr, PHY_REG_BCR, PHY_REG_BCR_RESET); + while self.sm.smi_read(self.phy_addr, PHY_REG_BCR) & PHY_REG_BCR_RESET == PHY_REG_BCR_RESET {} } - fn phy_init(&mut self, sm: &mut S) { + fn phy_init(&mut self) { // Clear WU CSR - self.smi_write_ext(sm, PHY_REG_WUCSR, 0); + self.smi_write_ext(PHY_REG_WUCSR, 0); // Enable auto-negotiation - sm.smi_write( + self.sm.smi_write( self.phy_addr, PHY_REG_BCR, PHY_REG_BCR_AN | PHY_REG_BCR_ANRST | PHY_REG_BCR_100M, ); } - fn poll_link(&mut self, sm: &mut S, cx: &mut Context) -> bool { + fn poll_link(&mut self, cx: &mut Context) -> bool { #[cfg(not(feature = "time"))] cx.waker().wake_by_ref(); #[cfg(feature = "time")] let _ = Timer::after(self.poll_interval).poll_unpin(cx); - let bsr = sm.smi_read(self.phy_addr, PHY_REG_BSR); + let bsr = self.sm.smi_read(self.phy_addr, PHY_REG_BSR); // No link without autonegotiate if bsr & PHY_REG_BSR_ANDONE == 0 { @@ -136,7 +139,7 @@ impl Phy for GenericPhy { } /// Public functions for the PHY -impl GenericPhy { +impl GenericPhy { /// Set the SMI polling interval. #[cfg(feature = "time")] pub fn set_poll_interval(&mut self, poll_interval: Duration) { @@ -144,10 +147,10 @@ impl GenericPhy { } // Writes a value to an extended PHY register in MMD address space - fn smi_write_ext(&mut self, sm: &mut S, reg_addr: u16, reg_data: u16) { - sm.smi_write(self.phy_addr, PHY_REG_CTL, 0x0003); // set address - sm.smi_write(self.phy_addr, PHY_REG_ADDAR, reg_addr); - sm.smi_write(self.phy_addr, PHY_REG_CTL, 0x4003); // set data - sm.smi_write(self.phy_addr, PHY_REG_ADDAR, reg_data); + fn smi_write_ext(&mut self, reg_addr: u16, reg_data: u16) { + self.sm.smi_write(self.phy_addr, PHY_REG_CTL, 0x0003); // set address + self.sm.smi_write(self.phy_addr, PHY_REG_ADDAR, reg_addr); + self.sm.smi_write(self.phy_addr, PHY_REG_CTL, 0x4003); // set data + self.sm.smi_write(self.phy_addr, PHY_REG_ADDAR, reg_data); } } diff --git a/embassy-stm32/src/eth/mod.rs b/embassy-stm32/src/eth/mod.rs index 448d21f3f..c8bce0e8a 100644 --- a/embassy-stm32/src/eth/mod.rs +++ b/embassy-stm32/src/eth/mod.rs @@ -111,7 +111,7 @@ impl<'d, T: Instance, P: Phy> embassy_net_driver::Driver for Ethernet<'d, T, P> } fn link_state(&mut self, cx: &mut Context) -> LinkState { - if self.phy.poll_link(&mut self.station_management, cx) { + if self.phy.poll_link(cx) { LinkState::Up } else { LinkState::Down @@ -162,21 +162,14 @@ impl<'a, 'd> embassy_net_driver::TxToken for TxToken<'a, 'd> { /// Trait for an Ethernet PHY pub trait Phy { /// Reset PHY and wait for it to come out of reset. - fn phy_reset(&mut self, sm: &mut S); + fn phy_reset(&mut self); /// PHY initialisation. - fn phy_init(&mut self, sm: &mut S); + fn phy_init(&mut self); /// Poll link to see if it is up and FD with 100Mbps - fn poll_link(&mut self, sm: &mut S, cx: &mut Context) -> bool; + fn poll_link(&mut self, cx: &mut Context) -> bool; } impl<'d, T: Instance, P: Phy> Ethernet<'d, T, P> { - /// Directly expose the SMI interface used by the Ethernet driver. - /// - /// This can be used to for example configure special PHY registers for compliance testing. - pub fn station_management(&mut self) -> &mut impl StationManagement { - &mut self.station_management - } - /// Access the user-supplied `Phy`. pub fn phy(&self) -> &P { &self.phy diff --git a/embassy-stm32/src/eth/sma/mod.rs b/embassy-stm32/src/eth/sma/mod.rs index 558107abc..6c851911d 100644 --- a/embassy-stm32/src/eth/sma/mod.rs +++ b/embassy-stm32/src/eth/sma/mod.rs @@ -39,17 +39,4 @@ impl SealedInstance for crate::peripherals::ETH_SMA { } } -impl SealedInstance for T { - fn regs() -> (Reg, Reg) { - let mac = ::regs().ethernet_mac(); - - #[cfg(any(eth_v1a, eth_v1b, eth_v1c))] - return (mac.macmiiar(), mac.macmiidr()); - - #[cfg(eth_v2)] - return (mac.macmdioar(), mac.macmdiodr()); - } -} - impl Instance for crate::peripherals::ETH_SMA {} -impl Instance for T {} diff --git a/embassy-stm32/src/eth/v1/mod.rs b/embassy-stm32/src/eth/v1/mod.rs index d448537c8..45cd33d9a 100644 --- a/embassy-stm32/src/eth/v1/mod.rs +++ b/embassy-stm32/src/eth/v1/mod.rs @@ -10,9 +10,7 @@ use stm32_metapac::eth::vals::{Apcs, Dm, DmaomrSr, Fes, Ftf, Ifg, Pbl, Rsf, St, pub(crate) use self::rx_desc::{RDes, RDesRing}; pub(crate) use self::tx_desc::{TDes, TDesRing}; -use super::sma::Sma; use super::*; -use crate::eth::{MDCPin, MDIOPin}; #[cfg(eth_v1a)] use crate::gpio::Pull; use crate::gpio::{AfType, AnyPin, OutputType, SealedPin, Speed}; @@ -53,7 +51,6 @@ pub struct Ethernet<'d, T: Instance, P: Phy> { pins: Pins<'d>, pub(crate) phy: P, - pub(crate) station_management: Sma<'d, T>, pub(crate) mac_addr: [u8; 6], } @@ -104,8 +101,6 @@ impl<'d, T: Instance, P: Phy> Ethernet<'d, T, P> { peri: Peri<'d, T>, irq: impl interrupt::typelevel::Binding + 'd, ref_clk: Peri<'d, if_afio!(impl RefClkPin)>, - mdio: Peri<'d, if_afio!(impl MDIOPin)>, - mdc: Peri<'d, if_afio!(impl MDCPin)>, crs: Peri<'d, if_afio!(impl CRSPin)>, rx_d0: Peri<'d, if_afio!(impl RXD0Pin)>, rx_d1: Peri<'d, if_afio!(impl RXD1Pin)>, @@ -165,16 +160,14 @@ impl<'d, T: Instance, P: Phy> Ethernet<'d, T, P> { tx_en.into(), ]); - Self::new_inner(queue, peri, irq, pins, mdio, mdc, phy, mac_addr) + Self::new_inner(queue, peri, irq, pins, phy, mac_addr) } - fn new_inner( + fn new_inner( queue: &'d mut PacketQueue, peri: Peri<'d, T>, _irq: impl interrupt::typelevel::Binding + 'd, pins: Pins<'d>, - mdio: Peri<'d, if_afio!(impl MDIOPin)>, - mdc: Peri<'d, if_afio!(impl MDCPin)>, phy: P, mac_addr: [u8; 6], ) -> Self { @@ -226,13 +219,10 @@ impl<'d, T: Instance, P: Phy> Ethernet<'d, T, P> { // TODO MTU size setting not found for v1 ethernet, check if correct - let sma_peri = unsafe { peri.clone_unchecked() }; - let mut this = Self { _peri: peri, pins, phy: phy, - station_management: Sma::new(sma_peri, mdio, mdc), mac_addr, tx: TDesRing::new(&mut queue.tx_desc, &mut queue.tx_buf), rx: RDesRing::new(&mut queue.rx_desc, &mut queue.rx_buf), @@ -262,8 +252,8 @@ impl<'d, T: Instance, P: Phy> Ethernet<'d, T, P> { w.set_tie(true); }); - this.phy.phy_reset(&mut this.station_management); - this.phy.phy_init(&mut this.station_management); + this.phy.phy_reset(); + this.phy.phy_init(); interrupt::ETH.unpend(); unsafe { interrupt::ETH.enable() }; @@ -271,15 +261,13 @@ impl<'d, T: Instance, P: Phy> Ethernet<'d, T, P> { this } - /// Create a new MII ethernet driver using 14 pins. + /// Create a new MII ethernet driver using 12 pins. pub fn new_mii( queue: &'d mut PacketQueue, peri: Peri<'d, T>, irq: impl interrupt::typelevel::Binding + 'd, rx_clk: Peri<'d, if_afio!(impl RXClkPin)>, tx_clk: Peri<'d, if_afio!(impl TXClkPin)>, - mdio: Peri<'d, if_afio!(impl MDIOPin)>, - mdc: Peri<'d, if_afio!(impl MDCPin)>, rxdv: Peri<'d, if_afio!(impl RXDVPin)>, rx_d0: Peri<'d, if_afio!(impl RXD0Pin)>, rx_d1: Peri<'d, if_afio!(impl RXD1Pin)>, @@ -352,7 +340,7 @@ impl<'d, T: Instance, P: Phy> Ethernet<'d, T, P> { tx_en.into(), ]); - Self::new_inner(queue, peri, irq, pins, mdio, mdc, phy, mac_addr) + Self::new_inner(queue, peri, irq, pins, phy, mac_addr) } } diff --git a/embassy-stm32/src/eth/v2/mod.rs b/embassy-stm32/src/eth/v2/mod.rs index 0db335a7c..faed7d8e2 100644 --- a/embassy-stm32/src/eth/v2/mod.rs +++ b/embassy-stm32/src/eth/v2/mod.rs @@ -6,9 +6,7 @@ use embassy_hal_internal::Peri; use stm32_metapac::syscfg::vals::EthSelPhy; pub(crate) use self::descriptors::{RDes, RDesRing, TDes, TDesRing}; -use super::sma::Sma; use super::*; -use crate::eth::{MDCPin, MDIOPin}; use crate::gpio::{AfType, AnyPin, OutputType, SealedPin as _, Speed}; use crate::interrupt; use crate::interrupt::InterruptExt; @@ -42,7 +40,6 @@ pub struct Ethernet<'d, T: Instance, P: Phy> { pub(crate) rx: RDesRing<'d>, pins: Pins<'d>, pub(crate) phy: P, - pub(crate) station_management: Sma<'d, T>, pub(crate) mac_addr: [u8; 6], } @@ -64,14 +61,12 @@ macro_rules! config_pins { } impl<'d, T: Instance, P: Phy> Ethernet<'d, T, P> { - /// Create a new RMII ethernet driver using 9 pins. + /// Create a new RMII ethernet driver using 7 pins. pub fn new( queue: &'d mut PacketQueue, peri: Peri<'d, T>, irq: impl interrupt::typelevel::Binding + 'd, ref_clk: Peri<'d, impl RefClkPin>, - mdio: Peri<'d, impl MDIOPin>, - mdc: Peri<'d, impl MDCPin>, crs: Peri<'d, impl CRSPin>, rx_d0: Peri<'d, impl RXD0Pin>, rx_d1: Peri<'d, impl RXD1Pin>, @@ -104,18 +99,16 @@ impl<'d, T: Instance, P: Phy> Ethernet<'d, T, P> { tx_en.into(), ]); - Self::new_inner(queue, peri, irq, pins, mdio, mdc, phy, mac_addr) + Self::new_inner(queue, peri, irq, pins, phy, mac_addr) } - /// Create a new MII ethernet driver using 14 pins. + /// Create a new MII ethernet driver using 12 pins. pub fn new_mii( queue: &'d mut PacketQueue, peri: Peri<'d, T>, irq: impl interrupt::typelevel::Binding + 'd, rx_clk: Peri<'d, impl RXClkPin>, tx_clk: Peri<'d, impl TXClkPin>, - mdio: Peri<'d, impl MDIOPin>, - mdc: Peri<'d, impl MDCPin>, rxdv: Peri<'d, impl RXDVPin>, rx_d0: Peri<'d, impl RXD0Pin>, rx_d1: Peri<'d, impl RXD1Pin>, @@ -161,7 +154,7 @@ impl<'d, T: Instance, P: Phy> Ethernet<'d, T, P> { tx_en.into(), ]); - Self::new_inner(queue, peri, irq, pins, mdio, mdc, phy, mac_addr) + Self::new_inner(queue, peri, irq, pins, phy, mac_addr) } fn new_inner( @@ -169,8 +162,6 @@ impl<'d, T: Instance, P: Phy> Ethernet<'d, T, P> { peri: Peri<'d, T>, _irq: impl interrupt::typelevel::Binding + 'd, pins: Pins<'d>, - mdio: Peri<'d, impl MDIOPin>, - mdc: Peri<'d, impl MDCPin>, phy: P, mac_addr: [u8; 6], ) -> Self { @@ -235,15 +226,12 @@ impl<'d, T: Instance, P: Phy> Ethernet<'d, T, P> { w.set_rbsz(RX_BUFFER_SIZE as u16); }); - let sma_peri = unsafe { peri.clone_unchecked() }; - let mut this = Self { _peri: peri, tx: TDesRing::new(&mut queue.tx_desc, &mut queue.tx_buf), rx: RDesRing::new(&mut queue.rx_desc, &mut queue.rx_buf), pins, phy, - station_management: Sma::new(sma_peri, mdio, mdc), mac_addr, }; @@ -269,8 +257,8 @@ impl<'d, T: Instance, P: Phy> Ethernet<'d, T, P> { w.set_tie(true); }); - this.phy.phy_reset(&mut this.station_management); - this.phy.phy_init(&mut this.station_management); + this.phy.phy_reset(); + this.phy.phy_init(); interrupt::ETH.unpend(); unsafe { interrupt::ETH.enable() }; diff --git a/tests/stm32/src/bin/eth.rs b/tests/stm32/src/bin/eth.rs index a65682a02..95789ffc5 100644 --- a/tests/stm32/src/bin/eth.rs +++ b/tests/stm32/src/bin/eth.rs @@ -7,8 +7,8 @@ mod common; use common::*; use embassy_executor::Spawner; use embassy_net::StackResources; -use embassy_stm32::eth::{Ethernet, GenericPhy, PacketQueue}; -use embassy_stm32::peripherals::ETH; +use embassy_stm32::eth::{Ethernet, GenericPhy, PacketQueue, Sma}; +use embassy_stm32::peripherals::{ETH, ETH_SMA}; use embassy_stm32::rng::Rng; use embassy_stm32::{bind_interrupts, eth, peripherals, rng}; use static_cell::StaticCell; @@ -27,7 +27,7 @@ bind_interrupts!(struct Irqs { RNG => rng::InterruptHandler; }); -type Device = Ethernet<'static, ETH, GenericPhy>; +type Device = Ethernet<'static, ETH, GenericPhy>>; #[embassy_executor::task] async fn net_task(mut runner: embassy_net::Runner<'static, Device>) -> ! { @@ -69,13 +69,15 @@ async fn main(spawner: Spawner) { const PACKET_QUEUE_SIZE: usize = 4; static PACKETS: StaticCell> = StaticCell::new(); + + let sma = Sma::new(p.ETH_SMA, p.PA2, p.PC1); + let phy = GenericPhy::new_auto(sma); + let device = Ethernet::new( PACKETS.init(PacketQueue::::new()), p.ETH, Irqs, p.PA1, - p.PA2, - p.PC1, p.PA7, p.PC4, p.PC5, @@ -85,7 +87,7 @@ async fn main(spawner: Spawner) { #[cfg(feature = "stm32h563zi")] p.PB15, p.PG11, - GenericPhy::new_auto(), + phy, mac_addr, ); -- cgit From 694249487cd592a0f806aab61b28534e33c1c5f0 Mon Sep 17 00:00:00 2001 From: datdenkikniet Date: Thu, 13 Nov 2025 22:48:33 +0100 Subject: Run clock setup/enablement in new_inner --- embassy-stm32/src/eth/v1/mod.rs | 100 ++++++++++++++-------------------------- embassy-stm32/src/eth/v2/mod.rs | 40 ++++++---------- 2 files changed, 48 insertions(+), 92 deletions(-) diff --git a/embassy-stm32/src/eth/v1/mod.rs b/embassy-stm32/src/eth/v1/mod.rs index 45cd33d9a..8b04b74c4 100644 --- a/embassy-stm32/src/eth/v1/mod.rs +++ b/embassy-stm32/src/eth/v1/mod.rs @@ -110,37 +110,6 @@ impl<'d, T: Instance, P: Phy> Ethernet<'d, T, P> { phy: P, mac_addr: [u8; 6], ) -> Self { - // Enable the necessary Clocks - #[cfg(eth_v1a)] - critical_section::with(|_| { - RCC.apb2enr().modify(|w| w.set_afioen(true)); - - // Select RMII (Reduced Media Independent Interface) - // Must be done prior to enabling peripheral clock - AFIO.mapr().modify(|w| { - w.set_mii_rmii_sel(true); - w.set_swj_cfg(crate::pac::afio::vals::SwjCfg::NO_OP); - }); - - RCC.ahbenr().modify(|w| { - w.set_ethen(true); - w.set_ethtxen(true); - w.set_ethrxen(true); - }); - }); - - #[cfg(any(eth_v1b, eth_v1c))] - critical_section::with(|_| { - RCC.ahb1enr().modify(|w| { - w.set_ethen(true); - w.set_ethtxen(true); - w.set_ethrxen(true); - }); - - // RMII (Reduced Media Independent Interface) - SYSCFG.pmc().modify(|w| w.set_mii_rmii_sel(true)); - }); - #[cfg(eth_v1a)] { config_in_pins!(ref_clk, rx_d0, rx_d1); @@ -160,7 +129,7 @@ impl<'d, T: Instance, P: Phy> Ethernet<'d, T, P> { tx_en.into(), ]); - Self::new_inner(queue, peri, irq, pins, phy, mac_addr) + Self::new_inner(queue, peri, irq, pins, phy, mac_addr, true) } fn new_inner( @@ -170,7 +139,39 @@ impl<'d, T: Instance, P: Phy> Ethernet<'d, T, P> { pins: Pins<'d>, phy: P, mac_addr: [u8; 6], + rmii_mii_sel: bool, ) -> Self { + // Enable the necessary Clocks + #[cfg(eth_v1a)] + critical_section::with(|_| { + RCC.apb2enr().modify(|w| w.set_afioen(true)); + + // Select (R)MII (Reduced Media Independent Interface) + // Must be done prior to enabling peripheral clock + AFIO.mapr().modify(|w| { + w.set_mii_rmii_sel(rmii_mii_sel); + w.set_swj_cfg(crate::pac::afio::vals::SwjCfg::NO_OP); + }); + + RCC.ahbenr().modify(|w| { + w.set_ethen(true); + w.set_ethtxen(true); + w.set_ethrxen(true); + }); + }); + + #[cfg(any(eth_v1b, eth_v1c))] + critical_section::with(|_| { + RCC.ahb1enr().modify(|w| { + w.set_ethen(true); + w.set_ethtxen(true); + w.set_ethrxen(true); + }); + + // (R)MII ((Reduced) Media Independent Interface) + SYSCFG.pmc().modify(|w| w.set_mii_rmii_sel(rmii_mii_sel)); + }); + let dma = T::regs().ethernet_dma(); let mac = T::regs().ethernet_mac(); @@ -281,39 +282,6 @@ impl<'d, T: Instance, P: Phy> Ethernet<'d, T, P> { phy: P, mac_addr: [u8; 6], ) -> Self { - // TODO: Handle optional signals like CRS, MII_COL, RX_ER? - - // Enable the necessary Clocks - #[cfg(eth_v1a)] - critical_section::with(|_| { - RCC.apb2enr().modify(|w| w.set_afioen(true)); - - // Select MII (Media Independent Interface) - // Must be done prior to enabling peripheral clock - AFIO.mapr().modify(|w| { - w.set_mii_rmii_sel(false); - w.set_swj_cfg(crate::pac::afio::vals::SwjCfg::NO_OP); - }); - - RCC.ahbenr().modify(|w| { - w.set_ethen(true); - w.set_ethtxen(true); - w.set_ethrxen(true); - }); - }); - - #[cfg(any(eth_v1b, eth_v1c))] - critical_section::with(|_| { - RCC.ahb1enr().modify(|w| { - w.set_ethen(true); - w.set_ethtxen(true); - w.set_ethrxen(true); - }); - - // MII (Media Independent Interface) - SYSCFG.pmc().modify(|w| w.set_mii_rmii_sel(false)); - }); - #[cfg(eth_v1a)] { config_in_pins!(rx_clk, tx_clk, rx_d0, rx_d1, rx_d2, rx_d3, rxdv); @@ -340,7 +308,7 @@ impl<'d, T: Instance, P: Phy> Ethernet<'d, T, P> { tx_en.into(), ]); - Self::new_inner(queue, peri, irq, pins, phy, mac_addr) + Self::new_inner(queue, peri, irq, pins, phy, mac_addr, false) } } diff --git a/embassy-stm32/src/eth/v2/mod.rs b/embassy-stm32/src/eth/v2/mod.rs index faed7d8e2..05ecee5ba 100644 --- a/embassy-stm32/src/eth/v2/mod.rs +++ b/embassy-stm32/src/eth/v2/mod.rs @@ -76,17 +76,6 @@ impl<'d, T: Instance, P: Phy> Ethernet<'d, T, P> { phy: P, mac_addr: [u8; 6], ) -> Self { - // Enable the necessary clocks - critical_section::with(|_| { - crate::pac::RCC.ahb1enr().modify(|w| { - w.set_ethen(true); - w.set_ethtxen(true); - w.set_ethrxen(true); - }); - - crate::pac::SYSCFG.pmcr().modify(|w| w.set_eth_sel_phy(EthSelPhy::RMII)); - }); - config_pins!(ref_clk, crs, rx_d0, rx_d1, tx_d0, tx_d1, tx_en); let pins = Pins::Rmii([ @@ -99,7 +88,7 @@ impl<'d, T: Instance, P: Phy> Ethernet<'d, T, P> { tx_en.into(), ]); - Self::new_inner(queue, peri, irq, pins, phy, mac_addr) + Self::new_inner(queue, peri, irq, pins, phy, mac_addr, EthSelPhy::RMII) } /// Create a new MII ethernet driver using 12 pins. @@ -122,19 +111,6 @@ impl<'d, T: Instance, P: Phy> Ethernet<'d, T, P> { phy: P, mac_addr: [u8; 6], ) -> Self { - // Enable the necessary clocks - critical_section::with(|_| { - crate::pac::RCC.ahb1enr().modify(|w| { - w.set_ethen(true); - w.set_ethtxen(true); - w.set_ethrxen(true); - }); - - crate::pac::SYSCFG - .pmcr() - .modify(|w| w.set_eth_sel_phy(EthSelPhy::MII_GMII)); - }); - config_pins!( rx_clk, tx_clk, rxdv, rx_d0, rx_d1, rx_d2, rx_d3, tx_d0, tx_d1, tx_d2, tx_d3, tx_en ); @@ -154,7 +130,7 @@ impl<'d, T: Instance, P: Phy> Ethernet<'d, T, P> { tx_en.into(), ]); - Self::new_inner(queue, peri, irq, pins, phy, mac_addr) + Self::new_inner(queue, peri, irq, pins, phy, mac_addr, EthSelPhy::MII_GMII) } fn new_inner( @@ -164,7 +140,19 @@ impl<'d, T: Instance, P: Phy> Ethernet<'d, T, P> { pins: Pins<'d>, phy: P, mac_addr: [u8; 6], + eth_sel_phy: EthSelPhy, ) -> Self { + // Enable the necessary clocks + critical_section::with(|_| { + crate::pac::RCC.ahb1enr().modify(|w| { + w.set_ethen(true); + w.set_ethtxen(true); + w.set_ethrxen(true); + }); + + crate::pac::SYSCFG.pmcr().modify(|w| w.set_eth_sel_phy(eth_sel_phy)); + }); + let dma = T::regs().ethernet_dma(); let mac = T::regs().ethernet_mac(); let mtl = T::regs().ethernet_mtl(); -- cgit From 4fb60b5991c4c98427ef23e6c011210341ba09e1 Mon Sep 17 00:00:00 2001 From: xoviat Date: Thu, 13 Nov 2025 17:41:20 -0600 Subject: fix async adc for h5 and others closes #4882. --- embassy-stm32/src/adc/v3.rs | 47 ++++++++++++++++--------------------- examples/stm32h5/src/bin/adc_dma.rs | 9 +++++-- 2 files changed, 27 insertions(+), 29 deletions(-) diff --git a/embassy-stm32/src/adc/v3.rs b/embassy-stm32/src/adc/v3.rs index 4cce1dac3..ba1afbe05 100644 --- a/embassy-stm32/src/adc/v3.rs +++ b/embassy-stm32/src/adc/v3.rs @@ -189,38 +189,31 @@ impl super::SealedAnyInstance for T { } fn start() { - #[cfg(any(adc_v3, adc_g0, adc_u0))] - { - // Start adc conversion - T::regs().cr().modify(|reg| { - reg.set_adstart(true); - }); - } + T::regs().cr().modify(|reg| { + reg.set_adstart(true); + }); } fn stop() { - #[cfg(any(adc_v3, adc_g0, adc_u0))] - { - // Ensure conversions are finished. - if T::regs().cr().read().adstart() && !T::regs().cr().read().addis() { - T::regs().cr().modify(|reg| { - reg.set_adstp(true); - }); - while T::regs().cr().read().adstart() {} - } - - // Reset configuration. - #[cfg(not(any(adc_g0, adc_u0)))] - T::regs().cfgr().modify(|reg| { - reg.set_cont(false); - reg.set_dmaen(false); - }); - #[cfg(any(adc_g0, adc_u0))] - T::regs().cfgr1().modify(|reg| { - reg.set_cont(false); - reg.set_dmaen(false); + // Ensure conversions are finished. + if T::regs().cr().read().adstart() && !T::regs().cr().read().addis() { + T::regs().cr().modify(|reg| { + reg.set_adstp(true); }); + while T::regs().cr().read().adstart() {} } + + // Reset configuration. + #[cfg(not(any(adc_g0, adc_u0)))] + T::regs().cfgr().modify(|reg| { + reg.set_cont(false); + reg.set_dmaen(false); + }); + #[cfg(any(adc_g0, adc_u0))] + T::regs().cfgr1().modify(|reg| { + reg.set_cont(false); + reg.set_dmaen(false); + }); } /// Perform a single conversion. diff --git a/examples/stm32h5/src/bin/adc_dma.rs b/examples/stm32h5/src/bin/adc_dma.rs index fb9fcbc5c..2138257f7 100644 --- a/examples/stm32h5/src/bin/adc_dma.rs +++ b/examples/stm32h5/src/bin/adc_dma.rs @@ -6,7 +6,7 @@ use embassy_executor::Spawner; use embassy_stm32::adc::{self, Adc, AdcChannel, RxDma, SampleTime}; use embassy_stm32::peripherals::{ADC1, ADC2, GPDMA1_CH0, GPDMA1_CH1, PA0, PA1, PA2, PA3}; use embassy_stm32::{Config, Peri}; -use embassy_time::Instant; +use embassy_time::{Duration, Instant, Ticker}; use {defmt_rtt as _, panic_probe as _}; #[embassy_executor::main] @@ -76,6 +76,9 @@ async fn adc_task<'a, T: adc::Instance>( let mut pin1 = pin1.degrade_adc(); let mut pin2 = pin2.degrade_adc(); + info!("adc init"); + + let mut ticker = Ticker::every(Duration::from_millis(500)); let mut tic = Instant::now(); let mut buffer = [0u16; 512]; loop { @@ -84,11 +87,13 @@ async fn adc_task<'a, T: adc::Instance>( adc.read( dma.reborrow(), [(&mut pin1, SampleTime::CYCLES2_5), (&mut pin2, SampleTime::CYCLES2_5)].into_iter(), - &mut buffer, + &mut buffer[0..2], ) .await; let toc = Instant::now(); info!("\n adc1: {} dt = {}", buffer[0..16], (toc - tic).as_micros()); tic = toc; + + ticker.next().await; } } -- cgit From 945ed1200957d9a4265cc5ac811ee39dce132317 Mon Sep 17 00:00:00 2001 From: xoviat Date: Thu, 13 Nov 2025 19:47:29 -0600 Subject: adc: fix c0 algorithm --- embassy-stm32/src/adc/c0.rs | 122 +++++++++++++++++++------------------------ embassy-stm32/src/adc/mod.rs | 16 ------ 2 files changed, 53 insertions(+), 85 deletions(-) diff --git a/embassy-stm32/src/adc/c0.rs b/embassy-stm32/src/adc/c0.rs index 3bdca7edb..d87bd1ed4 100644 --- a/embassy-stm32/src/adc/c0.rs +++ b/embassy-stm32/src/adc/c0.rs @@ -1,7 +1,7 @@ #[allow(unused)] use pac::adc::vals::{Adstp, Align, Ckmode, Dmacfg, Exten, Ovrmod, Ovsr}; use pac::adccommon::vals::Presc; -use stm32_metapac::adc::vals::Scandir; +use stm32_metapac::adc::vals::{SampleTime, Scandir}; use super::{Adc, Instance, Resolution, blocking_delay_us}; use crate::adc::{AnyInstance, ConversionMode}; @@ -17,7 +17,6 @@ const MAX_ADC_CLK_FREQ: Hertz = Hertz::mhz(25); const TIME_ADC_VOLTAGE_REGUALTOR_STARTUP_US: u32 = 20; -const NUM_HW_CHANNELS: u8 = 22; const CHSELR_SQ_SIZE: usize = 8; const CHSELR_SQ_MAX_CHANNEL: u8 = 14; const CHSELR_SQ_SEQUENCE_END_MARKER: u8 = 0b1111; @@ -100,82 +99,67 @@ impl super::SealedAnyInstance for T { } } - fn configure_sequence(mut sequence: impl ExactSizeIterator, blocking: bool) { - T::regs().cfgr1().modify(|reg| { - reg.set_chselrmod(!blocking); - reg.set_align(Align::RIGHT); - }); + fn configure_sequence(sequence: impl ExactSizeIterator) { + let mut needs_hw = sequence.len() == 1 || sequence.len() > CHSELR_SQ_SIZE; + let mut is_ordered_up = true; + let mut is_ordered_down = true; - assert!(!blocking || sequence.len() == 1, "Sequence len must be 1 for blocking."); - if blocking { - let ((ch, _), sample_time) = sequence.next().unwrap(); - // Set all channels to use SMP1 field as source. - T::regs().smpr().modify(|w| { - w.smpsel(0); - w.set_smp1(sample_time); - }); + let sequence_len = sequence.len(); + let mut hw_channel_selection: u32 = 0; + let mut last_channel: u8 = 0; + let mut sample_time: Self::SampleTime = SampleTime::CYCLES2_5; + + T::regs().chselr_sq().write(|w| { + for (i, ((channel, _), _sample_time)) in sequence.enumerate() { + assert!( + sample_time == _sample_time || i == 0, + "C0 only supports one sample time for the sequence." + ); - // write() because we want all other bits to be set to 0. - T::regs().chselr().write(|w| w.set_chsel(ch.into(), true)); - } else { - let mut hw_channel_selection: u32 = 0; - let mut is_ordered_up = true; - let mut is_ordered_down = true; - let mut needs_hw = false; + sample_time = _sample_time; + needs_hw = needs_hw || channel > CHSELR_SQ_MAX_CHANNEL; + is_ordered_up = is_ordered_up && (channel > last_channel || i == 0); + is_ordered_down = is_ordered_down && (channel < last_channel || i == 0); + hw_channel_selection += 1 << channel; + last_channel = channel; + if !needs_hw { + w.set_sq(i, channel); + } + } + + for i in sequence_len..CHSELR_SQ_SIZE { + w.set_sq(i, CHSELR_SQ_SEQUENCE_END_MARKER); + } + }); + + if needs_hw { assert!( - sequence.len() <= CHSELR_SQ_SIZE, - "Sequence read set cannot be more than {} in size.", + sequence_len <= CHSELR_SQ_SIZE || is_ordered_up || is_ordered_down, + "Sequencer is required because of unordered channels, but read set cannot be more than {} in size.", CHSELR_SQ_SIZE ); - let mut last_sq_set: usize = 0; - let mut last_channel: u8 = 0; - T::regs().chselr_sq().write(|w| { - for (i, ((channel, _), _sample_time)) in sequence.enumerate() { - needs_hw = needs_hw || channel > CHSELR_SQ_MAX_CHANNEL; - last_sq_set = i; - is_ordered_up = is_ordered_up && channel > last_channel; - is_ordered_down = is_ordered_down && channel < last_channel; - hw_channel_selection += 1 << channel; - last_channel = channel; - - if !needs_hw { - w.set_sq(i, channel); - } - } - - assert!( - !needs_hw || is_ordered_up || is_ordered_down, - "Sequencer is required because of unordered channels, but only support HW channels smaller than {}.", - CHSELR_SQ_MAX_CHANNEL - ); + assert!( + sequence_len > CHSELR_SQ_SIZE || is_ordered_up || is_ordered_down, + "Sequencer is required because of unordered channels, but only support HW channels smaller than {}.", + CHSELR_SQ_MAX_CHANNEL + ); - if needs_hw { - assert!( - hw_channel_selection != 0, - "Some bits in `hw_channel_selection` shall be set." - ); - assert!( - (hw_channel_selection >> NUM_HW_CHANNELS) == 0, - "STM32C0 only have {} ADC channels. `hw_channel_selection` cannot have bits higher than this number set.", - NUM_HW_CHANNELS - ); - - T::regs().cfgr1().modify(|reg| { - reg.set_chselrmod(false); - reg.set_scandir(if is_ordered_up { Scandir::UP} else { Scandir::BACK }); - }); - - // Set required channels for multi-convert. - unsafe { (T::regs().chselr().as_ptr() as *mut u32).write_volatile(hw_channel_selection) } - } else { - for i in (last_sq_set + 1)..CHSELR_SQ_SIZE { - w.set_sq(i, CHSELR_SQ_SEQUENCE_END_MARKER); - } - } - }); + // Set required channels for multi-convert. + unsafe { (T::regs().chselr().as_ptr() as *mut u32).write_volatile(hw_channel_selection) } } + T::regs().smpr().modify(|w| { + w.smpsel(0); + w.set_smp1(sample_time); + }); + + T::regs().cfgr1().modify(|reg| { + reg.set_chselrmod(!needs_hw); + reg.set_align(Align::RIGHT); + reg.set_scandir(if is_ordered_up { Scandir::UP } else { Scandir::BACK }); + }); + // Trigger and wait for the channel selection procedure to complete. T::regs().isr().modify(|w| w.set_ccrdy(false)); while !T::regs().isr().read().ccrdy() {} diff --git a/embassy-stm32/src/adc/mod.rs b/embassy-stm32/src/adc/mod.rs index 5ec08a22d..13f8a1544 100644 --- a/embassy-stm32/src/adc/mod.rs +++ b/embassy-stm32/src/adc/mod.rs @@ -111,10 +111,7 @@ pub(self) trait SealedAnyInstance: BasicAnyInstance { fn stop(); fn convert() -> u16; fn configure_dma(conversion_mode: ConversionMode); - #[cfg(not(adc_c0))] fn configure_sequence(sequence: impl ExactSizeIterator); - #[cfg(adc_c0)] - fn configure_sequence(sequence: impl ExactSizeIterator, blocking: bool); #[allow(dead_code)] fn dr() -> *mut u16; } @@ -197,13 +194,7 @@ impl<'d, T: AnyInstance> Adc<'d, T> { #[cfg(not(adc_v4))] T::enable(); - #[cfg(not(adc_c0))] T::configure_sequence([((channel.channel(), channel.is_differential()), sample_time)].into_iter()); - #[cfg(adc_c0)] - T::configure_sequence( - [((channel.channel(), channel.is_differential()), sample_time)].into_iter(), - true, - ); T::convert() } @@ -262,15 +253,8 @@ impl<'d, T: AnyInstance> Adc<'d, T> { T::stop(); T::enable(); - #[cfg(not(adc_c0))] - T::configure_sequence( - sequence.map(|(channel, sample_time)| ((channel.channel, channel.is_differential), sample_time)), - ); - - #[cfg(adc_c0)] T::configure_sequence( sequence.map(|(channel, sample_time)| ((channel.channel, channel.is_differential), sample_time)), - false, ); T::configure_dma(ConversionMode::Singular); -- cgit From 7e5cd16ca79cf36d2c28c7e2fe35642a27f18b72 Mon Sep 17 00:00:00 2001 From: Daniel Nilsson Date: Thu, 13 Nov 2025 14:51:00 +0100 Subject: correcting channel on interval Vbat, adding Vbat resistor disable to preserve current when not sampling. --- embassy-stm32/src/adc/v3.rs | 22 ++++++++++++++++++++-- 1 file changed, 20 insertions(+), 2 deletions(-) diff --git a/embassy-stm32/src/adc/v3.rs b/embassy-stm32/src/adc/v3.rs index 78b497727..c65357aff 100644 --- a/embassy-stm32/src/adc/v3.rs +++ b/embassy-stm32/src/adc/v3.rs @@ -65,7 +65,7 @@ impl super::SealedSpecialConverter for T { } #[cfg(any(adc_h5, adc_h7rs))] impl super::SealedSpecialConverter for T { - const CHANNEL: u8 = 2; + const CHANNEL: u8 = 16; } #[cfg(adc_u0)] impl super::SealedSpecialConverter for T { @@ -82,7 +82,7 @@ cfg_if! { impl super::AdcChannel for VddCore {} impl super::SealedAdcChannel for VddCore { fn channel(&self) -> u8 { - 6 + 17 } } } @@ -582,6 +582,24 @@ impl<'d, T: Instance> Adc<'d, T> { Vbat {} } + pub fn disable_vbat(&self) { + cfg_if! { + if #[cfg(any(adc_g0, adc_u0))] { + T::regs().ccr().modify(|reg| { + reg.set_vbaten(false); + }); + } else if #[cfg(any(adc_h5, adc_h7rs))] { + T::common_regs().ccr().modify(|reg| { + reg.set_vbaten(false); + }); + } else { + T::common_regs().ccr().modify(|reg| { + reg.set_ch18sel(false); + }); + } + } + } + /* /// Convert a raw sample from the `Temperature` to deg C pub fn to_degrees_centigrade(sample: u16) -> f32 { -- cgit From 80eed58c30087d6d61474e8a1ac21da1ea679763 Mon Sep 17 00:00:00 2001 From: Daniel Nilsson Date: Fri, 14 Nov 2025 07:58:17 +0100 Subject: splitting up ADC1/2 implementations on Adc to ensure relevant methods are only visible on the ADC block where they are supported --- embassy-stm32/src/adc/v3.rs | 128 +++++++++++++++++++++++++++----------------- 1 file changed, 78 insertions(+), 50 deletions(-) diff --git a/embassy-stm32/src/adc/v3.rs b/embassy-stm32/src/adc/v3.rs index c65357aff..3bda0ae54 100644 --- a/embassy-stm32/src/adc/v3.rs +++ b/embassy-stm32/src/adc/v3.rs @@ -13,7 +13,7 @@ pub use pac::adc::vals::{Ovsr, Ovss, Presc}; use super::SealedAdcChannel; use super::{Adc, Averaging, Instance, Resolution, SampleTime, Temperature, Vbat, VrefInt, blocking_delay_us}; use crate::adc::ConversionMode; -use crate::{Peri, pac, rcc}; +use crate::{Peri, pac, rcc, peripherals}; /// Default VREF voltage used for sample conversion to millivolts. pub const VREF_DEFAULT_MV: u32 = 3300; @@ -489,6 +489,22 @@ impl<'d, T: Instance> Adc<'d, T> { s } + #[cfg(any(adc_g0, adc_u0))] + pub fn enable_vbat(&self) -> Vbat { + T::regs().ccr().modify(|reg| { + reg.set_vbaten(true); + }); + + Vbat { } + } + + #[cfg(any(adc_g0, adc_u0))] + pub fn disable_vbat(&self) { + T::regs().ccr().modify(|reg| { + reg.set_vbaten(false); + }); + } + #[cfg(adc_g0)] /// Initialize ADC with explicit clock for the analog ADC pub fn new_with_clock(adc: Peri<'d, T>, clock: Clock) -> Self { @@ -525,87 +541,99 @@ impl<'d, T: Instance> Adc<'d, T> { Self { adc } } - pub fn enable_vrefint(&self) -> VrefInt { - #[cfg(not(any(adc_g0, adc_u0)))] - T::common_regs().ccr().modify(|reg| { - reg.set_vrefen(true); - }); - #[cfg(any(adc_g0, adc_u0))] - T::regs().ccr().modify(|reg| { - reg.set_vrefen(true); - }); - - // "Table 24. Embedded internal voltage reference" states that it takes a maximum of 12 us - // to stabilize the internal voltage reference. - blocking_delay_us(15); - - VrefInt {} + /* + /// Convert a raw sample from the `Temperature` to deg C + pub fn to_degrees_centigrade(sample: u16) -> f32 { + (130.0 - 30.0) / (VtempCal130::get().read() as f32 - VtempCal30::get().read() as f32) + * (sample as f32 - VtempCal30::get().read() as f32) + + 30.0 } + */ +} - pub fn enable_temperature(&self) -> Temperature { + +#[cfg(not(any(adc_g0, adc_u0)))] +impl<'d> Adc<'d, peripherals::ADC2> { + pub fn enable_vbat(&self) -> Vbat { cfg_if! { - if #[cfg(any(adc_g0, adc_u0))] { - T::regs().ccr().modify(|reg| { - reg.set_tsen(true); - }); - } else if #[cfg(any(adc_h5, adc_h7rs))] { - T::common_regs().ccr().modify(|reg| { - reg.set_tsen(true); + if #[cfg(any(adc_h5, adc_h7rs))] { + pac::ADC12_COMMON.ccr().modify(|reg| { + reg.set_vbaten(true); }); } else { - T::common_regs().ccr().modify(|reg| { - reg.set_ch17sel(true); + pac::ADC12_COMMON.ccr().modify(|reg| { + reg.set_ch18sel(true); }); } } - Temperature {} + Vbat { } } - pub fn enable_vbat(&self) -> Vbat { + pub fn disable_vbat(&self) { cfg_if! { if #[cfg(any(adc_g0, adc_u0))] { - T::regs().ccr().modify(|reg| { - reg.set_vbaten(true); + pac::ADC2.ccr().modify(|reg| { + reg.set_vbaten(false); }); } else if #[cfg(any(adc_h5, adc_h7rs))] { - T::common_regs().ccr().modify(|reg| { - reg.set_vbaten(true); + pac::ADC12_COMMON.ccr().modify(|reg| { + reg.set_vbaten(false); }); } else { - T::common_regs().ccr().modify(|reg| { - reg.set_ch18sel(true); + pac::ADC12_COMMON.ccr().modify(|reg| { + reg.set_ch18sel(false); }); } } + } - Vbat {} + #[cfg(any(adc_h5, adc_h7rs))] + pub fn enable_vddcore(&self) -> VddCore { + pac::ADC2.or().modify(|reg| { + reg.set_op0(true); + }); + + VddCore { } } +} - pub fn disable_vbat(&self) { + +impl<'d> Adc<'d, peripherals::ADC1> { + pub fn enable_vrefint(&self) -> VrefInt { + #[cfg(not(any(adc_g0, adc_u0)))] + pac::ADC12_COMMON.ccr().modify(|reg| { + reg.set_vrefen(true); + }); + #[cfg(any(adc_g0, adc_u0))] + pac::ADC1.ccr().modify(|reg| { + reg.set_vrefen(true); + }); + + // "Table 24. Embedded internal voltage reference" states that it takes a maximum of 12 us + // to stabilize the internal voltage reference. + blocking_delay_us(15); + + VrefInt { } + } + + pub fn enable_temperature(&self) -> Temperature { cfg_if! { if #[cfg(any(adc_g0, adc_u0))] { - T::regs().ccr().modify(|reg| { - reg.set_vbaten(false); + pac::ADC1.ccr().modify(|reg| { + reg.set_tsen(true); }); } else if #[cfg(any(adc_h5, adc_h7rs))] { - T::common_regs().ccr().modify(|reg| { - reg.set_vbaten(false); + pac::ADC12_COMMON.ccr().modify(|reg| { + reg.set_tsen(true); }); } else { - T::common_regs().ccr().modify(|reg| { - reg.set_ch18sel(false); + pac::ADC12_COMMON.ccr().modify(|reg| { + reg.set_ch17sel(true); }); } } - } - /* - /// Convert a raw sample from the `Temperature` to deg C - pub fn to_degrees_centigrade(sample: u16) -> f32 { - (130.0 - 30.0) / (VtempCal130::get().read() as f32 - VtempCal30::get().read() as f32) - * (sample as f32 - VtempCal30::get().read() as f32) - + 30.0 + Temperature { } } - */ } -- cgit From 2c75390b8cbb9dd815b53130d03fe0803112a6c6 Mon Sep 17 00:00:00 2001 From: Daniel Nilsson Date: Fri, 14 Nov 2025 08:03:17 +0100 Subject: updating changelog --- embassy-stm32/CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/embassy-stm32/CHANGELOG.md b/embassy-stm32/CHANGELOG.md index 8bd930e79..259eaf9c0 100644 --- a/embassy-stm32/CHANGELOG.md +++ b/embassy-stm32/CHANGELOG.md @@ -58,6 +58,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - adc: reogranize and cleanup somewhat. require sample_time to be passed on conversion - fix: stm32/i2c v2 slave: prevent misaligned reads, error false positives, and incorrect counts of bytes read/written - feat: add flash support for c0 family ([#4874](https://github.com/embassy-rs/embassy/pull/4874)) +- fix: fixing channel numbers on vbat and vddcore for adc on adc +- adc: splitting up implementations to distinguish ADC1 & 2 hosted internal special channels are only accessible on the relevant block ## 0.4.0 - 2025-08-26 -- cgit From 9fd57c165997bf575517aea0fde98b930b1c893a Mon Sep 17 00:00:00 2001 From: Daniel Nilsson Date: Fri, 14 Nov 2025 08:04:58 +0100 Subject: fixing failed rust fmt ci --- embassy-stm32/src/adc/v3.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/embassy-stm32/src/adc/v3.rs b/embassy-stm32/src/adc/v3.rs index 3bda0ae54..55fe70f72 100644 --- a/embassy-stm32/src/adc/v3.rs +++ b/embassy-stm32/src/adc/v3.rs @@ -594,7 +594,7 @@ impl<'d> Adc<'d, peripherals::ADC2> { reg.set_op0(true); }); - VddCore { } + VddCore {} } } @@ -614,7 +614,7 @@ impl<'d> Adc<'d, peripherals::ADC1> { // to stabilize the internal voltage reference. blocking_delay_us(15); - VrefInt { } + VrefInt {} } pub fn enable_temperature(&self) -> Temperature { @@ -634,6 +634,6 @@ impl<'d> Adc<'d, peripherals::ADC1> { } } - Temperature { } + Temperature {} } } -- cgit From f7c1aba09f7ef0b99396dc626d5d8c575f5b18e4 Mon Sep 17 00:00:00 2001 From: Daniel Nilsson Date: Fri, 14 Nov 2025 08:06:18 +0100 Subject: fixing one more failed rust fmt ci --- embassy-stm32/src/adc/v3.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/embassy-stm32/src/adc/v3.rs b/embassy-stm32/src/adc/v3.rs index 55fe70f72..51e1e654b 100644 --- a/embassy-stm32/src/adc/v3.rs +++ b/embassy-stm32/src/adc/v3.rs @@ -495,7 +495,7 @@ impl<'d, T: Instance> Adc<'d, T> { reg.set_vbaten(true); }); - Vbat { } + Vbat {} } #[cfg(any(adc_g0, adc_u0))] @@ -567,7 +567,7 @@ impl<'d> Adc<'d, peripherals::ADC2> { } } - Vbat { } + Vbat {} } pub fn disable_vbat(&self) { -- cgit From 3bbc2515062046638cc19edb0f02f1490de21087 Mon Sep 17 00:00:00 2001 From: Daniel Nilsson Date: Fri, 14 Nov 2025 08:12:14 +0100 Subject: indention fix --- embassy-stm32/src/adc/v3.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/embassy-stm32/src/adc/v3.rs b/embassy-stm32/src/adc/v3.rs index 51e1e654b..54824c253 100644 --- a/embassy-stm32/src/adc/v3.rs +++ b/embassy-stm32/src/adc/v3.rs @@ -13,7 +13,7 @@ pub use pac::adc::vals::{Ovsr, Ovss, Presc}; use super::SealedAdcChannel; use super::{Adc, Averaging, Instance, Resolution, SampleTime, Temperature, Vbat, VrefInt, blocking_delay_us}; use crate::adc::ConversionMode; -use crate::{Peri, pac, rcc, peripherals}; +use crate::{Peri, pac, peripherals, rcc}; /// Default VREF voltage used for sample conversion to millivolts. pub const VREF_DEFAULT_MV: u32 = 3300; @@ -554,7 +554,7 @@ impl<'d, T: Instance> Adc<'d, T> { #[cfg(not(any(adc_g0, adc_u0)))] impl<'d> Adc<'d, peripherals::ADC2> { - pub fn enable_vbat(&self) -> Vbat { + pub fn enable_vbat(&self) -> Vbat { cfg_if! { if #[cfg(any(adc_h5, adc_h7rs))] { pac::ADC12_COMMON.ccr().modify(|reg| { -- cgit From 31a6bb84bc27c79640edb490d2a96117a413375e Mon Sep 17 00:00:00 2001 From: Daniel Nilsson Date: Fri, 14 Nov 2025 08:14:42 +0100 Subject: ci fix: whitespace removal --- embassy-stm32/src/adc/v3.rs | 2 -- 1 file changed, 2 deletions(-) diff --git a/embassy-stm32/src/adc/v3.rs b/embassy-stm32/src/adc/v3.rs index 54824c253..8559d0697 100644 --- a/embassy-stm32/src/adc/v3.rs +++ b/embassy-stm32/src/adc/v3.rs @@ -551,7 +551,6 @@ impl<'d, T: Instance> Adc<'d, T> { */ } - #[cfg(not(any(adc_g0, adc_u0)))] impl<'d> Adc<'d, peripherals::ADC2> { pub fn enable_vbat(&self) -> Vbat { @@ -598,7 +597,6 @@ impl<'d> Adc<'d, peripherals::ADC2> { } } - impl<'d> Adc<'d, peripherals::ADC1> { pub fn enable_vrefint(&self) -> VrefInt { #[cfg(not(any(adc_g0, adc_u0)))] -- cgit From d866a7f73775e0694f9c9a280df9d3603cb52541 Mon Sep 17 00:00:00 2001 From: Daniel Nilsson Date: Fri, 14 Nov 2025 08:42:46 +0100 Subject: walking around stm32wb differences --- embassy-stm32/src/adc/v3.rs | 29 +++++++++++++++++++---------- 1 file changed, 19 insertions(+), 10 deletions(-) diff --git a/embassy-stm32/src/adc/v3.rs b/embassy-stm32/src/adc/v3.rs index 8559d0697..b833247a9 100644 --- a/embassy-stm32/src/adc/v3.rs +++ b/embassy-stm32/src/adc/v3.rs @@ -551,7 +551,7 @@ impl<'d, T: Instance> Adc<'d, T> { */ } -#[cfg(not(any(adc_g0, adc_u0)))] +#[cfg(not(any(adc_g0, adc_u0, stm32wb)))] impl<'d> Adc<'d, peripherals::ADC2> { pub fn enable_vbat(&self) -> Vbat { cfg_if! { @@ -599,14 +599,21 @@ impl<'d> Adc<'d, peripherals::ADC2> { impl<'d> Adc<'d, peripherals::ADC1> { pub fn enable_vrefint(&self) -> VrefInt { - #[cfg(not(any(adc_g0, adc_u0)))] - pac::ADC12_COMMON.ccr().modify(|reg| { - reg.set_vrefen(true); - }); - #[cfg(any(adc_g0, adc_u0))] - pac::ADC1.ccr().modify(|reg| { - reg.set_vrefen(true); - }); + cfg_if! { + if #[cfg(not(any(adc_g0, adc_u0, stm32wb)))] { + pac::ADC12_COMMON.ccr().modify(|reg| { + reg.set_vrefen(true); + }); + } else if #[cfg(any(adc_g0, adc_u0))] { + pac::ADC1.ccr().modify(|reg| { + reg.set_vrefen(true); + }); + } else { + pac::ADC1_COMMON.ccr().modify(|reg| { + reg.set_vrefen(true); + }); + } + } // "Table 24. Embedded internal voltage reference" states that it takes a maximum of 12 us // to stabilize the internal voltage reference. @@ -625,10 +632,12 @@ impl<'d> Adc<'d, peripherals::ADC1> { pac::ADC12_COMMON.ccr().modify(|reg| { reg.set_tsen(true); }); + } else if #[cfg(any(stm32wb))] { + todo!(); } else { pac::ADC12_COMMON.ccr().modify(|reg| { reg.set_ch17sel(true); - }); + }); } } -- cgit From 536b4e8fe3a62fae25bd3b1d2ae0f196bfb734f9 Mon Sep 17 00:00:00 2001 From: Daniel Nilsson Date: Fri, 14 Nov 2025 08:52:08 +0100 Subject: walking around ci unreachable code warning --- embassy-stm32/src/adc/v3.rs | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/embassy-stm32/src/adc/v3.rs b/embassy-stm32/src/adc/v3.rs index b833247a9..e4ccaba53 100644 --- a/embassy-stm32/src/adc/v3.rs +++ b/embassy-stm32/src/adc/v3.rs @@ -11,7 +11,8 @@ pub use pac::adc::vals::{Ovsr, Ovss, Presc}; #[allow(unused_imports)] use super::SealedAdcChannel; -use super::{Adc, Averaging, Instance, Resolution, SampleTime, Temperature, Vbat, VrefInt, blocking_delay_us}; +#[allow(unused_imports)] +use super::{Adc, Avergaing, Instance, Resolution, SampleTime, Temperature, Vbat, VrefInt, blocking_delay_us}; use crate::adc::ConversionMode; use crate::{Peri, pac, peripherals, rcc}; @@ -628,19 +629,24 @@ impl<'d> Adc<'d, peripherals::ADC1> { pac::ADC1.ccr().modify(|reg| { reg.set_tsen(true); }); + + Temperature {} } else if #[cfg(any(adc_h5, adc_h7rs))] { pac::ADC12_COMMON.ccr().modify(|reg| { reg.set_tsen(true); }); + + Temperature {} } else if #[cfg(any(stm32wb))] { todo!(); } else { pac::ADC12_COMMON.ccr().modify(|reg| { reg.set_ch17sel(true); }); + + Temperature {} } } - Temperature {} } } -- cgit From 00f80b56c3f72db31117427d6294df19b7401f2e Mon Sep 17 00:00:00 2001 From: Daniel Nilsson Date: Fri, 14 Nov 2025 08:53:33 +0100 Subject: whitespace fix.. --- embassy-stm32/src/adc/v3.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/embassy-stm32/src/adc/v3.rs b/embassy-stm32/src/adc/v3.rs index e4ccaba53..e6ead5dcf 100644 --- a/embassy-stm32/src/adc/v3.rs +++ b/embassy-stm32/src/adc/v3.rs @@ -642,11 +642,10 @@ impl<'d> Adc<'d, peripherals::ADC1> { } else { pac::ADC12_COMMON.ccr().modify(|reg| { reg.set_ch17sel(true); - }); + }); Temperature {} } } - } } -- cgit From 31908a26e0ef597511af25b7ffb50f7c64e85560 Mon Sep 17 00:00:00 2001 From: Daniel Nilsson Date: Fri, 14 Nov 2025 09:32:26 +0100 Subject: import spelling error fix --- embassy-stm32/src/adc/v3.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/embassy-stm32/src/adc/v3.rs b/embassy-stm32/src/adc/v3.rs index e6ead5dcf..ce02168ba 100644 --- a/embassy-stm32/src/adc/v3.rs +++ b/embassy-stm32/src/adc/v3.rs @@ -12,7 +12,7 @@ pub use pac::adc::vals::{Ovsr, Ovss, Presc}; #[allow(unused_imports)] use super::SealedAdcChannel; #[allow(unused_imports)] -use super::{Adc, Avergaing, Instance, Resolution, SampleTime, Temperature, Vbat, VrefInt, blocking_delay_us}; +use super::{Adc, Averaging, Instance, Resolution, SampleTime, Temperature, Vbat, VrefInt, blocking_delay_us}; use crate::adc::ConversionMode; use crate::{Peri, pac, peripherals, rcc}; -- cgit From 20c75352c388546e8d105d03837c06f32d28ffbc Mon Sep 17 00:00:00 2001 From: Daniel Nilsson Date: Fri, 14 Nov 2025 10:10:42 +0100 Subject: adding support for stm32l4 --- embassy-stm32/src/adc/v3.rs | 32 +++++++++++++++++++++++--------- 1 file changed, 23 insertions(+), 9 deletions(-) diff --git a/embassy-stm32/src/adc/v3.rs b/embassy-stm32/src/adc/v3.rs index ce02168ba..93219168d 100644 --- a/embassy-stm32/src/adc/v3.rs +++ b/embassy-stm32/src/adc/v3.rs @@ -560,6 +560,10 @@ impl<'d> Adc<'d, peripherals::ADC2> { pac::ADC12_COMMON.ccr().modify(|reg| { reg.set_vbaten(true); }); + } else if #[cfg(stm32l4)] { + pac::ADC123_COMMON.ccr().modify(|reg| { + reg.set_ch18sel(true); + }); } else { pac::ADC12_COMMON.ccr().modify(|reg| { reg.set_ch18sel(true); @@ -572,18 +576,18 @@ impl<'d> Adc<'d, peripherals::ADC2> { pub fn disable_vbat(&self) { cfg_if! { - if #[cfg(any(adc_g0, adc_u0))] { - pac::ADC2.ccr().modify(|reg| { - reg.set_vbaten(false); - }); - } else if #[cfg(any(adc_h5, adc_h7rs))] { + if #[cfg(any(adc_h5, adc_h7rs))] { pac::ADC12_COMMON.ccr().modify(|reg| { reg.set_vbaten(false); }); + } else if #[cfg(stm32l4)] { + pac::ADC123_COMMON.ccr().modify(|reg| { + reg.set_ch18sel(false); + }); } else { pac::ADC12_COMMON.ccr().modify(|reg| { reg.set_ch18sel(false); - }); + }); } } } @@ -601,7 +605,7 @@ impl<'d> Adc<'d, peripherals::ADC2> { impl<'d> Adc<'d, peripherals::ADC1> { pub fn enable_vrefint(&self) -> VrefInt { cfg_if! { - if #[cfg(not(any(adc_g0, adc_u0, stm32wb)))] { + if #[cfg(any(adc_h5, adc_h7rs))] { pac::ADC12_COMMON.ccr().modify(|reg| { reg.set_vrefen(true); }); @@ -609,6 +613,10 @@ impl<'d> Adc<'d, peripherals::ADC1> { pac::ADC1.ccr().modify(|reg| { reg.set_vrefen(true); }); + } else if #[cfg(stm32l4)] { + pac::ADC123_COMMON.ccr().modify(|reg| { + reg.set_vrefen(true); + }); } else { pac::ADC1_COMMON.ccr().modify(|reg| { reg.set_vrefen(true); @@ -637,8 +645,14 @@ impl<'d> Adc<'d, peripherals::ADC1> { }); Temperature {} - } else if #[cfg(any(stm32wb))] { - todo!(); + } else if #[cfg(stm32wb)] { + todo(); + } else if #[cfg(stm32l4)] { + pac::ADC123_COMMON.ccr().modify(|reg| { + reg.set_ch17sel(true); + }); + + Temperature {} } else { pac::ADC12_COMMON.ccr().modify(|reg| { reg.set_ch17sel(true); -- cgit From 2cdefb7d09a6c77e325c8fc074017873fb0296ac Mon Sep 17 00:00:00 2001 From: Daniel Nilsson Date: Fri, 14 Nov 2025 10:17:33 +0100 Subject: fix whitespace issues --- embassy-stm32/src/adc/v3.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/embassy-stm32/src/adc/v3.rs b/embassy-stm32/src/adc/v3.rs index 93219168d..072d6e592 100644 --- a/embassy-stm32/src/adc/v3.rs +++ b/embassy-stm32/src/adc/v3.rs @@ -587,7 +587,7 @@ impl<'d> Adc<'d, peripherals::ADC2> { } else { pac::ADC12_COMMON.ccr().modify(|reg| { reg.set_ch18sel(false); - }); + }); } } } -- cgit From 7ddee557405bbd11a2915818044b508158aa149f Mon Sep 17 00:00:00 2001 From: Daniel Nilsson Date: Fri, 14 Nov 2025 10:37:04 +0100 Subject: misspelled todo macro --- embassy-stm32/src/adc/v3.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/embassy-stm32/src/adc/v3.rs b/embassy-stm32/src/adc/v3.rs index 072d6e592..c77a1d4f5 100644 --- a/embassy-stm32/src/adc/v3.rs +++ b/embassy-stm32/src/adc/v3.rs @@ -646,7 +646,7 @@ impl<'d> Adc<'d, peripherals::ADC1> { Temperature {} } else if #[cfg(stm32wb)] { - todo(); + todo!(); } else if #[cfg(stm32l4)] { pac::ADC123_COMMON.ccr().modify(|reg| { reg.set_ch17sel(true); -- cgit From 842ee9bef98975d2a874a425983cfad59610a963 Mon Sep 17 00:00:00 2001 From: Daniel Nilsson Date: Fri, 14 Nov 2025 11:15:42 +0100 Subject: undoing channel split --- embassy-stm32/src/adc/v3.rs | 154 ++++++++++++++------------------------------ 1 file changed, 50 insertions(+), 104 deletions(-) diff --git a/embassy-stm32/src/adc/v3.rs b/embassy-stm32/src/adc/v3.rs index c77a1d4f5..c65357aff 100644 --- a/embassy-stm32/src/adc/v3.rs +++ b/embassy-stm32/src/adc/v3.rs @@ -11,10 +11,9 @@ pub use pac::adc::vals::{Ovsr, Ovss, Presc}; #[allow(unused_imports)] use super::SealedAdcChannel; -#[allow(unused_imports)] use super::{Adc, Averaging, Instance, Resolution, SampleTime, Temperature, Vbat, VrefInt, blocking_delay_us}; use crate::adc::ConversionMode; -use crate::{Peri, pac, peripherals, rcc}; +use crate::{Peri, pac, rcc}; /// Default VREF voltage used for sample conversion to millivolts. pub const VREF_DEFAULT_MV: u32 = 3300; @@ -490,22 +489,6 @@ impl<'d, T: Instance> Adc<'d, T> { s } - #[cfg(any(adc_g0, adc_u0))] - pub fn enable_vbat(&self) -> Vbat { - T::regs().ccr().modify(|reg| { - reg.set_vbaten(true); - }); - - Vbat {} - } - - #[cfg(any(adc_g0, adc_u0))] - pub fn disable_vbat(&self) { - T::regs().ccr().modify(|reg| { - reg.set_vbaten(false); - }); - } - #[cfg(adc_g0)] /// Initialize ADC with explicit clock for the analog ADC pub fn new_with_clock(adc: Peri<'d, T>, clock: Clock) -> Self { @@ -542,124 +525,87 @@ impl<'d, T: Instance> Adc<'d, T> { Self { adc } } - /* - /// Convert a raw sample from the `Temperature` to deg C - pub fn to_degrees_centigrade(sample: u16) -> f32 { - (130.0 - 30.0) / (VtempCal130::get().read() as f32 - VtempCal30::get().read() as f32) - * (sample as f32 - VtempCal30::get().read() as f32) - + 30.0 - } - */ -} + pub fn enable_vrefint(&self) -> VrefInt { + #[cfg(not(any(adc_g0, adc_u0)))] + T::common_regs().ccr().modify(|reg| { + reg.set_vrefen(true); + }); + #[cfg(any(adc_g0, adc_u0))] + T::regs().ccr().modify(|reg| { + reg.set_vrefen(true); + }); -#[cfg(not(any(adc_g0, adc_u0, stm32wb)))] -impl<'d> Adc<'d, peripherals::ADC2> { - pub fn enable_vbat(&self) -> Vbat { - cfg_if! { - if #[cfg(any(adc_h5, adc_h7rs))] { - pac::ADC12_COMMON.ccr().modify(|reg| { - reg.set_vbaten(true); - }); - } else if #[cfg(stm32l4)] { - pac::ADC123_COMMON.ccr().modify(|reg| { - reg.set_ch18sel(true); - }); - } else { - pac::ADC12_COMMON.ccr().modify(|reg| { - reg.set_ch18sel(true); - }); - } - } + // "Table 24. Embedded internal voltage reference" states that it takes a maximum of 12 us + // to stabilize the internal voltage reference. + blocking_delay_us(15); - Vbat {} + VrefInt {} } - pub fn disable_vbat(&self) { + pub fn enable_temperature(&self) -> Temperature { cfg_if! { - if #[cfg(any(adc_h5, adc_h7rs))] { - pac::ADC12_COMMON.ccr().modify(|reg| { - reg.set_vbaten(false); + if #[cfg(any(adc_g0, adc_u0))] { + T::regs().ccr().modify(|reg| { + reg.set_tsen(true); }); - } else if #[cfg(stm32l4)] { - pac::ADC123_COMMON.ccr().modify(|reg| { - reg.set_ch18sel(false); + } else if #[cfg(any(adc_h5, adc_h7rs))] { + T::common_regs().ccr().modify(|reg| { + reg.set_tsen(true); }); } else { - pac::ADC12_COMMON.ccr().modify(|reg| { - reg.set_ch18sel(false); + T::common_regs().ccr().modify(|reg| { + reg.set_ch17sel(true); }); } } - } - - #[cfg(any(adc_h5, adc_h7rs))] - pub fn enable_vddcore(&self) -> VddCore { - pac::ADC2.or().modify(|reg| { - reg.set_op0(true); - }); - VddCore {} + Temperature {} } -} -impl<'d> Adc<'d, peripherals::ADC1> { - pub fn enable_vrefint(&self) -> VrefInt { + pub fn enable_vbat(&self) -> Vbat { cfg_if! { - if #[cfg(any(adc_h5, adc_h7rs))] { - pac::ADC12_COMMON.ccr().modify(|reg| { - reg.set_vrefen(true); - }); - } else if #[cfg(any(adc_g0, adc_u0))] { - pac::ADC1.ccr().modify(|reg| { - reg.set_vrefen(true); + if #[cfg(any(adc_g0, adc_u0))] { + T::regs().ccr().modify(|reg| { + reg.set_vbaten(true); }); - } else if #[cfg(stm32l4)] { - pac::ADC123_COMMON.ccr().modify(|reg| { - reg.set_vrefen(true); + } else if #[cfg(any(adc_h5, adc_h7rs))] { + T::common_regs().ccr().modify(|reg| { + reg.set_vbaten(true); }); } else { - pac::ADC1_COMMON.ccr().modify(|reg| { - reg.set_vrefen(true); + T::common_regs().ccr().modify(|reg| { + reg.set_ch18sel(true); }); } } - // "Table 24. Embedded internal voltage reference" states that it takes a maximum of 12 us - // to stabilize the internal voltage reference. - blocking_delay_us(15); - - VrefInt {} + Vbat {} } - pub fn enable_temperature(&self) -> Temperature { + pub fn disable_vbat(&self) { cfg_if! { if #[cfg(any(adc_g0, adc_u0))] { - pac::ADC1.ccr().modify(|reg| { - reg.set_tsen(true); + T::regs().ccr().modify(|reg| { + reg.set_vbaten(false); }); - - Temperature {} } else if #[cfg(any(adc_h5, adc_h7rs))] { - pac::ADC12_COMMON.ccr().modify(|reg| { - reg.set_tsen(true); - }); - - Temperature {} - } else if #[cfg(stm32wb)] { - todo!(); - } else if #[cfg(stm32l4)] { - pac::ADC123_COMMON.ccr().modify(|reg| { - reg.set_ch17sel(true); + T::common_regs().ccr().modify(|reg| { + reg.set_vbaten(false); }); - - Temperature {} } else { - pac::ADC12_COMMON.ccr().modify(|reg| { - reg.set_ch17sel(true); + T::common_regs().ccr().modify(|reg| { + reg.set_ch18sel(false); }); - - Temperature {} } } } + + /* + /// Convert a raw sample from the `Temperature` to deg C + pub fn to_degrees_centigrade(sample: u16) -> f32 { + (130.0 - 30.0) / (VtempCal130::get().read() as f32 - VtempCal30::get().read() as f32) + * (sample as f32 - VtempCal30::get().read() as f32) + + 30.0 + } + */ } -- cgit From 34b5b4eb92de4c135156c52ce3d5b59c14a5c841 Mon Sep 17 00:00:00 2001 From: Daniel Nilsson Date: Fri, 14 Nov 2025 11:17:02 +0100 Subject: adjusting changelog --- embassy-stm32/CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/embassy-stm32/CHANGELOG.md b/embassy-stm32/CHANGELOG.md index 259eaf9c0..9153e15b9 100644 --- a/embassy-stm32/CHANGELOG.md +++ b/embassy-stm32/CHANGELOG.md @@ -59,7 +59,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - fix: stm32/i2c v2 slave: prevent misaligned reads, error false positives, and incorrect counts of bytes read/written - feat: add flash support for c0 family ([#4874](https://github.com/embassy-rs/embassy/pull/4874)) - fix: fixing channel numbers on vbat and vddcore for adc on adc -- adc: splitting up implementations to distinguish ADC1 & 2 hosted internal special channels are only accessible on the relevant block +- adc: adding disable to vbat ## 0.4.0 - 2025-08-26 -- cgit From 9b5fc685a11bc4d5254dffde37beeaba721d1f2a Mon Sep 17 00:00:00 2001 From: Ulf Lilleengen Date: Fri, 14 Nov 2025 12:53:10 +0100 Subject: fix: use correct nrf54l15 flash size Both SVD and documentation agrees on 1524kB --- embassy-nrf/src/chips/nrf54l15_app.rs | 2 +- examples/nrf54l15/memory.x | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/embassy-nrf/src/chips/nrf54l15_app.rs b/embassy-nrf/src/chips/nrf54l15_app.rs index 0724f2ff6..8846717db 100644 --- a/embassy-nrf/src/chips/nrf54l15_app.rs +++ b/embassy-nrf/src/chips/nrf54l15_app.rs @@ -204,7 +204,7 @@ pub const FORCE_COPY_BUFFER_SIZE: usize = 1024; // 1.5 MB NVM #[allow(unused)] -pub const FLASH_SIZE: usize = 1536 * 1024; +pub const FLASH_SIZE: usize = 1524 * 1024; embassy_hal_internal::peripherals! { // PPI diff --git a/examples/nrf54l15/memory.x b/examples/nrf54l15/memory.x index 1064c8a5c..332200828 100644 --- a/examples/nrf54l15/memory.x +++ b/examples/nrf54l15/memory.x @@ -1,5 +1,5 @@ MEMORY { - FLASH : ORIGIN = 0x00000000, LENGTH = 1536K + FLASH : ORIGIN = 0x00000000, LENGTH = 1524K RAM : ORIGIN = 0x20000000, LENGTH = 256K } -- cgit From a5a4e9f82e1843261ae98418f8646be73fcb1f5e Mon Sep 17 00:00:00 2001 From: Ulf Lilleengen Date: Fri, 14 Nov 2025 12:59:00 +0100 Subject: docs: add changelog --- embassy-nrf/CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/embassy-nrf/CHANGELOG.md b/embassy-nrf/CHANGELOG.md index 52a8a7a05..f6fe1e14f 100644 --- a/embassy-nrf/CHANGELOG.md +++ b/embassy-nrf/CHANGELOG.md @@ -26,6 +26,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - added: add `gpiote::InputChannel::wait_for_high()` and `wait_for_low()` to wait for specific signal level - changed: `gpiote::InputChannel::wait()` now takes a mutable reference to `self` to avoid interference from concurrent calls - changed: `gpiote::InputChannel::wait()` now ensures events are seen as soon as the function is called, even if the future is not polled +- bugfix: use correct flash size for nRF54l ## 0.8.0 - 2025-09-30 -- cgit From 0b3bc35b7dc0f3d2ffa1673ab790df8e6650fe0f Mon Sep 17 00:00:00 2001 From: datdenkikniet Date: Thu, 13 Nov 2025 23:00:58 +0100 Subject: Add new{_with_phy}, new_mii{_with_phy} --- embassy-stm32/src/eth/generic_phy.rs | 5 ++ embassy-stm32/src/eth/v1/mod.rs | 80 +++++++++++++++++++++++-- embassy-stm32/src/eth/v2/mod.rs | 78 ++++++++++++++++++++++-- examples/stm32f4/src/bin/eth.rs | 12 ++-- examples/stm32f4/src/bin/eth_compliance_test.rs | 12 ++-- examples/stm32f7/src/bin/eth.rs | 12 ++-- examples/stm32h5/src/bin/eth.rs | 12 ++-- examples/stm32h7/src/bin/eth.rs | 12 ++-- examples/stm32h7/src/bin/eth_client.rs | 12 ++-- examples/stm32h7/src/bin/eth_client_mii.rs | 12 ++-- examples/stm32h7rs/src/bin/eth.rs | 12 ++-- tests/stm32/src/bin/eth.rs | 7 +-- 12 files changed, 206 insertions(+), 60 deletions(-) diff --git a/embassy-stm32/src/eth/generic_phy.rs b/embassy-stm32/src/eth/generic_phy.rs index 4e61a83e7..0a5f41de0 100644 --- a/embassy-stm32/src/eth/generic_phy.rs +++ b/embassy-stm32/src/eth/generic_phy.rs @@ -153,4 +153,9 @@ impl GenericPhy { self.sm.smi_write(self.phy_addr, PHY_REG_CTL, 0x4003); // set data self.sm.smi_write(self.phy_addr, PHY_REG_ADDAR, reg_data); } + + /// Access the underlying station management. + pub fn station_management(&mut self) -> &mut SM { + &mut self.sm + } } diff --git a/embassy-stm32/src/eth/v1/mod.rs b/embassy-stm32/src/eth/v1/mod.rs index 8b04b74c4..8de26ce9d 100644 --- a/embassy-stm32/src/eth/v1/mod.rs +++ b/embassy-stm32/src/eth/v1/mod.rs @@ -94,7 +94,15 @@ macro_rules! config_pins { }; } -impl<'d, T: Instance, P: Phy> Ethernet<'d, T, P> { +impl<'d, T: Instance, SMA: sma::Instance> Ethernet<'d, T, GenericPhy>> { + /// Create a new RMII ethernet driver using 7 pins. + /// + /// This function uses a [`GenericPhy::new_auto`] as PHY, created using the + /// provided [`SMA`](sma::Instance), and MDIO and MDC pins. + /// + /// See [`Ethernet::new_with_phy`] for creating an RMII ethernet + /// river with a non-standard PHY. + /// /// safety: the returned instance is not leak-safe pub fn new( queue: &'d mut PacketQueue, @@ -107,8 +115,72 @@ impl<'d, T: Instance, P: Phy> Ethernet<'d, T, P> { tx_d0: Peri<'d, if_afio!(impl TXD0Pin)>, tx_d1: Peri<'d, if_afio!(impl TXD1Pin)>, tx_en: Peri<'d, if_afio!(impl TXEnPin)>, - phy: P, mac_addr: [u8; 6], + sma: Peri<'d, SMA>, + mdio: Peri<'d, if_afio!(impl MDIOPin)>, + mdc: Peri<'d, if_afio!(impl MDCPin)>, + ) -> Self { + let sma = Sma::new(sma, mdio, mdc); + let phy = GenericPhy::new_auto(sma); + + Self::new_with_phy( + queue, peri, irq, ref_clk, crs, rx_d0, rx_d1, tx_d0, tx_d1, tx_en, mac_addr, phy, + ) + } + + /// Create a new MII ethernet driver using 14 pins. + /// + /// This function uses a [`GenericPhy::new_auto`] as PHY, created using the + /// provided [`SMA`](sma::Instance), and MDIO and MDC pins. + /// + /// See [`Ethernet::new_mii_with_phy`] for creating an RMII ethernet + /// river with a non-standard PHY. + pub fn new_mii( + queue: &'d mut PacketQueue, + peri: Peri<'d, T>, + irq: impl interrupt::typelevel::Binding + 'd, + rx_clk: Peri<'d, if_afio!(impl RXClkPin)>, + tx_clk: Peri<'d, if_afio!(impl TXClkPin)>, + rxdv: Peri<'d, if_afio!(impl RXDVPin)>, + rx_d0: Peri<'d, if_afio!(impl RXD0Pin)>, + rx_d1: Peri<'d, if_afio!(impl RXD1Pin)>, + rx_d2: Peri<'d, if_afio!(impl RXD2Pin)>, + rx_d3: Peri<'d, if_afio!(impl RXD3Pin)>, + tx_d0: Peri<'d, if_afio!(impl TXD0Pin)>, + tx_d1: Peri<'d, if_afio!(impl TXD1Pin)>, + tx_d2: Peri<'d, if_afio!(impl TXD2Pin)>, + tx_d3: Peri<'d, if_afio!(impl TXD3Pin)>, + tx_en: Peri<'d, if_afio!(impl TXEnPin)>, + mac_addr: [u8; 6], + sma: Peri<'d, SMA>, + mdio: Peri<'d, if_afio!(impl MDIOPin)>, + mdc: Peri<'d, if_afio!(impl MDCPin)>, + ) -> Self { + let sma = Sma::new(sma, mdio, mdc); + let phy = GenericPhy::new_auto(sma); + + Self::new_mii_with_phy( + queue, peri, irq, rx_clk, tx_clk, rxdv, rx_d0, rx_d1, rx_d2, rx_d3, tx_d0, tx_d1, tx_d2, tx_d3, tx_en, + mac_addr, phy, + ) + } +} + +impl<'d, T: Instance, P: Phy> Ethernet<'d, T, P> { + /// safety: the returned instance is not leak-safe + pub fn new_with_phy( + queue: &'d mut PacketQueue, + peri: Peri<'d, T>, + irq: impl interrupt::typelevel::Binding + 'd, + ref_clk: Peri<'d, if_afio!(impl RefClkPin)>, + crs: Peri<'d, if_afio!(impl CRSPin)>, + rx_d0: Peri<'d, if_afio!(impl RXD0Pin)>, + rx_d1: Peri<'d, if_afio!(impl RXD1Pin)>, + tx_d0: Peri<'d, if_afio!(impl TXD0Pin)>, + tx_d1: Peri<'d, if_afio!(impl TXD1Pin)>, + tx_en: Peri<'d, if_afio!(impl TXEnPin)>, + mac_addr: [u8; 6], + phy: P, ) -> Self { #[cfg(eth_v1a)] { @@ -263,7 +335,7 @@ impl<'d, T: Instance, P: Phy> Ethernet<'d, T, P> { } /// Create a new MII ethernet driver using 12 pins. - pub fn new_mii( + pub fn new_mii_with_phy( queue: &'d mut PacketQueue, peri: Peri<'d, T>, irq: impl interrupt::typelevel::Binding + 'd, @@ -279,8 +351,8 @@ impl<'d, T: Instance, P: Phy> Ethernet<'d, T, P> { tx_d2: Peri<'d, if_afio!(impl TXD2Pin)>, tx_d3: Peri<'d, if_afio!(impl TXD3Pin)>, tx_en: Peri<'d, if_afio!(impl TXEnPin)>, - phy: P, mac_addr: [u8; 6], + phy: P, ) -> Self { #[cfg(eth_v1a)] { diff --git a/embassy-stm32/src/eth/v2/mod.rs b/embassy-stm32/src/eth/v2/mod.rs index 05ecee5ba..7f92e351c 100644 --- a/embassy-stm32/src/eth/v2/mod.rs +++ b/embassy-stm32/src/eth/v2/mod.rs @@ -60,8 +60,14 @@ macro_rules! config_pins { }; } -impl<'d, T: Instance, P: Phy> Ethernet<'d, T, P> { +impl<'d, T: Instance, SMA: sma::Instance> Ethernet<'d, T, GenericPhy>> { /// Create a new RMII ethernet driver using 7 pins. + /// + /// This function uses a [`GenericPhy::new_auto`] as PHY, created using the + /// provided [`SMA`](sma::Instance), and MDIO and MDC pins. + /// + /// See [`Ethernet::new_with_phy`] for creating an RMII ethernet + /// river with a non-standard PHY. pub fn new( queue: &'d mut PacketQueue, peri: Peri<'d, T>, @@ -73,8 +79,72 @@ impl<'d, T: Instance, P: Phy> Ethernet<'d, T, P> { tx_d0: Peri<'d, impl TXD0Pin>, tx_d1: Peri<'d, impl TXD1Pin>, tx_en: Peri<'d, impl TXEnPin>, - phy: P, mac_addr: [u8; 6], + sma: Peri<'d, SMA>, + mdio: Peri<'d, impl MDIOPin>, + mdc: Peri<'d, impl MDCPin>, + ) -> Self { + let sma = Sma::new(sma, mdio, mdc); + let phy = GenericPhy::new_auto(sma); + + Self::new_with_phy( + queue, peri, irq, ref_clk, crs, rx_d0, rx_d1, tx_d0, tx_d1, tx_en, mac_addr, phy, + ) + } + + /// Create a new MII ethernet driver using 14 pins. + /// + /// This function uses a [`GenericPhy::new_auto`] as PHY, created using the + /// provided [`SMA`](sma::Instance), and MDIO and MDC pins. + /// + /// See [`Ethernet::new_mii_with_phy`] for creating an RMII ethernet + /// river with a non-standard PHY. + pub fn new_mii( + queue: &'d mut PacketQueue, + peri: Peri<'d, T>, + irq: impl interrupt::typelevel::Binding + 'd, + rx_clk: Peri<'d, impl RXClkPin>, + tx_clk: Peri<'d, impl TXClkPin>, + rxdv: Peri<'d, impl RXDVPin>, + rx_d0: Peri<'d, impl RXD0Pin>, + rx_d1: Peri<'d, impl RXD1Pin>, + rx_d2: Peri<'d, impl RXD2Pin>, + rx_d3: Peri<'d, impl RXD3Pin>, + tx_d0: Peri<'d, impl TXD0Pin>, + tx_d1: Peri<'d, impl TXD1Pin>, + tx_d2: Peri<'d, impl TXD2Pin>, + tx_d3: Peri<'d, impl TXD3Pin>, + tx_en: Peri<'d, impl TXEnPin>, + mac_addr: [u8; 6], + sma: Peri<'d, SMA>, + mdio: Peri<'d, impl MDIOPin>, + mdc: Peri<'d, impl MDCPin>, + ) -> Self { + let sma = Sma::new(sma, mdio, mdc); + let phy = GenericPhy::new_auto(sma); + + Self::new_mii_with_phy( + queue, peri, irq, rx_clk, tx_clk, rxdv, rx_d0, rx_d1, rx_d2, rx_d3, tx_d0, tx_d1, tx_d2, tx_d3, tx_en, + mac_addr, phy, + ) + } +} + +impl<'d, T: Instance, P: Phy> Ethernet<'d, T, P> { + /// Create a new RMII ethernet driver using 7 pins. + pub fn new_with_phy( + queue: &'d mut PacketQueue, + peri: Peri<'d, T>, + irq: impl interrupt::typelevel::Binding + 'd, + ref_clk: Peri<'d, impl RefClkPin>, + crs: Peri<'d, impl CRSPin>, + rx_d0: Peri<'d, impl RXD0Pin>, + rx_d1: Peri<'d, impl RXD1Pin>, + tx_d0: Peri<'d, impl TXD0Pin>, + tx_d1: Peri<'d, impl TXD1Pin>, + tx_en: Peri<'d, impl TXEnPin>, + mac_addr: [u8; 6], + phy: P, ) -> Self { config_pins!(ref_clk, crs, rx_d0, rx_d1, tx_d0, tx_d1, tx_en); @@ -92,7 +162,7 @@ impl<'d, T: Instance, P: Phy> Ethernet<'d, T, P> { } /// Create a new MII ethernet driver using 12 pins. - pub fn new_mii( + pub fn new_mii_with_phy( queue: &'d mut PacketQueue, peri: Peri<'d, T>, irq: impl interrupt::typelevel::Binding + 'd, @@ -108,8 +178,8 @@ impl<'d, T: Instance, P: Phy> Ethernet<'d, T, P> { tx_d2: Peri<'d, impl TXD2Pin>, tx_d3: Peri<'d, impl TXD3Pin>, tx_en: Peri<'d, impl TXEnPin>, - phy: P, mac_addr: [u8; 6], + phy: P, ) -> Self { config_pins!( rx_clk, tx_clk, rxdv, rx_d0, rx_d1, rx_d2, rx_d3, tx_d0, tx_d1, tx_d2, tx_d3, tx_en diff --git a/examples/stm32f4/src/bin/eth.rs b/examples/stm32f4/src/bin/eth.rs index 2d72b6b0b..8dfa0916d 100644 --- a/examples/stm32f4/src/bin/eth.rs +++ b/examples/stm32f4/src/bin/eth.rs @@ -5,8 +5,8 @@ use defmt::*; use embassy_executor::Spawner; use embassy_net::tcp::TcpSocket; use embassy_net::{Ipv4Address, StackResources}; -use embassy_stm32::eth::{Ethernet, GenericPhy, PacketQueue}; -use embassy_stm32::peripherals::ETH; +use embassy_stm32::eth::{Ethernet, GenericPhy, PacketQueue, Sma}; +use embassy_stm32::peripherals::{ETH, ETH_SMA}; use embassy_stm32::rng::Rng; use embassy_stm32::time::Hertz; use embassy_stm32::{Config, bind_interrupts, eth, peripherals, rng}; @@ -20,7 +20,7 @@ bind_interrupts!(struct Irqs { HASH_RNG => rng::InterruptHandler; }); -type Device = Ethernet<'static, ETH, GenericPhy>; +type Device = Ethernet<'static, ETH, GenericPhy>>; #[embassy_executor::task] async fn net_task(mut runner: embassy_net::Runner<'static, Device>) -> ! { @@ -67,16 +67,16 @@ async fn main(spawner: Spawner) -> ! { p.ETH, Irqs, p.PA1, - p.PA2, - p.PC1, p.PA7, p.PC4, p.PC5, p.PG13, p.PB13, p.PG11, - GenericPhy::new_auto(), mac_addr, + p.ETH_SMA, + p.PA2, + p.PC1, ); let config = embassy_net::Config::dhcpv4(Default::default()); diff --git a/examples/stm32f4/src/bin/eth_compliance_test.rs b/examples/stm32f4/src/bin/eth_compliance_test.rs index 734a14c2c..dc5d7dbb6 100644 --- a/examples/stm32f4/src/bin/eth_compliance_test.rs +++ b/examples/stm32f4/src/bin/eth_compliance_test.rs @@ -3,7 +3,7 @@ use defmt::*; use embassy_executor::Spawner; -use embassy_stm32::eth::{Ethernet, GenericPhy, PacketQueue, StationManagement}; +use embassy_stm32::eth::{Ethernet, PacketQueue, StationManagement}; use embassy_stm32::time::Hertz; use embassy_stm32::{Config, bind_interrupts, eth, peripherals, rng}; use embassy_time::Timer; @@ -43,27 +43,27 @@ async fn main(_spawner: Spawner) -> ! { let mac_addr = [0x00, 0x00, 0xDE, 0xAD, 0xBE, 0xEF]; - const PHY_ADDR: u8 = 0; static PACKETS: StaticCell> = StaticCell::new(); let mut device = Ethernet::new( PACKETS.init(PacketQueue::<4, 4>::new()), p.ETH, Irqs, p.PA1, - p.PA2, - p.PC1, p.PA7, p.PC4, p.PC5, p.PG13, p.PB13, p.PG11, - GenericPhy::new(PHY_ADDR), mac_addr, + p.ETH_SMA, + p.PA2, + p.PC1, ); - let sm = device.station_management(); + let sm = device.phy_mut().station_management(); + const PHY_ADDR: u8 = 0; // Just an example. Exact register settings depend on the specific PHY and test. sm.smi_write(PHY_ADDR, 0, 0x2100); sm.smi_write(PHY_ADDR, 11, 0xA000); diff --git a/examples/stm32f7/src/bin/eth.rs b/examples/stm32f7/src/bin/eth.rs index f8a129239..8613376b8 100644 --- a/examples/stm32f7/src/bin/eth.rs +++ b/examples/stm32f7/src/bin/eth.rs @@ -5,8 +5,8 @@ use defmt::*; use embassy_executor::Spawner; use embassy_net::tcp::TcpSocket; use embassy_net::{Ipv4Address, StackResources}; -use embassy_stm32::eth::{Ethernet, GenericPhy, PacketQueue}; -use embassy_stm32::peripherals::ETH; +use embassy_stm32::eth::{Ethernet, GenericPhy, PacketQueue, Sma}; +use embassy_stm32::peripherals::{ETH, ETH_SMA}; use embassy_stm32::rng::Rng; use embassy_stm32::time::Hertz; use embassy_stm32::{Config, bind_interrupts, eth, peripherals, rng}; @@ -20,7 +20,7 @@ bind_interrupts!(struct Irqs { HASH_RNG => rng::InterruptHandler; }); -type Device = Ethernet<'static, ETH, GenericPhy>; +type Device = Ethernet<'static, ETH, GenericPhy>>; #[embassy_executor::task] async fn net_task(mut runner: embassy_net::Runner<'static, Device>) -> ! { @@ -67,16 +67,16 @@ async fn main(spawner: Spawner) -> ! { p.ETH, Irqs, p.PA1, - p.PA2, - p.PC1, p.PA7, p.PC4, p.PC5, p.PG13, p.PB13, p.PG11, - GenericPhy::new_auto(), mac_addr, + p.ETH_SMA, + p.PA2, + p.PC1, ); let config = embassy_net::Config::dhcpv4(Default::default()); diff --git a/examples/stm32h5/src/bin/eth.rs b/examples/stm32h5/src/bin/eth.rs index a5c6cee26..6a3afb2d1 100644 --- a/examples/stm32h5/src/bin/eth.rs +++ b/examples/stm32h5/src/bin/eth.rs @@ -5,8 +5,8 @@ use defmt::*; use embassy_executor::Spawner; use embassy_net::tcp::TcpSocket; use embassy_net::{Ipv4Address, StackResources}; -use embassy_stm32::eth::{Ethernet, GenericPhy, PacketQueue}; -use embassy_stm32::peripherals::ETH; +use embassy_stm32::eth::{Ethernet, GenericPhy, PacketQueue, Sma}; +use embassy_stm32::peripherals::{ETH, ETH_SMA}; use embassy_stm32::rcc::{ AHBPrescaler, APBPrescaler, Hse, HseMode, Pll, PllDiv, PllMul, PllPreDiv, PllSource, Sysclk, VoltageScale, }; @@ -23,7 +23,7 @@ bind_interrupts!(struct Irqs { RNG => rng::InterruptHandler; }); -type Device = Ethernet<'static, ETH, GenericPhy>; +type Device = Ethernet<'static, ETH, GenericPhy>>; #[embassy_executor::task] async fn net_task(mut runner: embassy_net::Runner<'static, Device>) -> ! { @@ -70,16 +70,16 @@ async fn main(spawner: Spawner) -> ! { p.ETH, Irqs, p.PA1, - p.PA2, - p.PC1, p.PA7, p.PC4, p.PC5, p.PG13, p.PB15, p.PG11, - GenericPhy::new_auto(), mac_addr, + p.ETH_SMA, + p.PA2, + p.PC1, ); let config = embassy_net::Config::dhcpv4(Default::default()); diff --git a/examples/stm32h7/src/bin/eth.rs b/examples/stm32h7/src/bin/eth.rs index 589f4426e..09915799b 100644 --- a/examples/stm32h7/src/bin/eth.rs +++ b/examples/stm32h7/src/bin/eth.rs @@ -5,8 +5,8 @@ use defmt::*; use embassy_executor::Spawner; use embassy_net::tcp::TcpSocket; use embassy_net::{Ipv4Address, StackResources}; -use embassy_stm32::eth::{Ethernet, GenericPhy, PacketQueue}; -use embassy_stm32::peripherals::ETH; +use embassy_stm32::eth::{Ethernet, GenericPhy, PacketQueue, Sma}; +use embassy_stm32::peripherals::{ETH, ETH_SMA}; use embassy_stm32::rng::Rng; use embassy_stm32::{Config, bind_interrupts, eth, peripherals, rng}; use embassy_time::Timer; @@ -19,7 +19,7 @@ bind_interrupts!(struct Irqs { RNG => rng::InterruptHandler; }); -type Device = Ethernet<'static, ETH, GenericPhy>; +type Device = Ethernet<'static, ETH, GenericPhy>>; #[embassy_executor::task] async fn net_task(mut runner: embassy_net::Runner<'static, Device>) -> ! { @@ -69,16 +69,16 @@ async fn main(spawner: Spawner) -> ! { p.ETH, Irqs, p.PA1, // ref_clk - p.PA2, // mdio - p.PC1, // eth_mdc p.PA7, // CRS_DV: Carrier Sense p.PC4, // RX_D0: Received Bit 0 p.PC5, // RX_D1: Received Bit 1 p.PG13, // TX_D0: Transmit Bit 0 p.PB13, // TX_D1: Transmit Bit 1 p.PG11, // TX_EN: Transmit Enable - GenericPhy::new_auto(), mac_addr, + p.ETH_SMA, + p.PA2, // mdio + p.PC1, // mdc ); let config = embassy_net::Config::dhcpv4(Default::default()); diff --git a/examples/stm32h7/src/bin/eth_client.rs b/examples/stm32h7/src/bin/eth_client.rs index fed8f1a9c..189c99686 100644 --- a/examples/stm32h7/src/bin/eth_client.rs +++ b/examples/stm32h7/src/bin/eth_client.rs @@ -7,8 +7,8 @@ use defmt::*; use embassy_executor::Spawner; use embassy_net::StackResources; use embassy_net::tcp::client::{TcpClient, TcpClientState}; -use embassy_stm32::eth::{Ethernet, GenericPhy, PacketQueue}; -use embassy_stm32::peripherals::ETH; +use embassy_stm32::eth::{Ethernet, GenericPhy, PacketQueue, Sma}; +use embassy_stm32::peripherals::{ETH, ETH_SMA}; use embassy_stm32::rng::Rng; use embassy_stm32::{Config, bind_interrupts, eth, peripherals, rng}; use embassy_time::Timer; @@ -22,7 +22,7 @@ bind_interrupts!(struct Irqs { RNG => rng::InterruptHandler; }); -type Device = Ethernet<'static, ETH, GenericPhy>; +type Device = Ethernet<'static, ETH, GenericPhy>>; #[embassy_executor::task] async fn net_task(mut runner: embassy_net::Runner<'static, Device>) -> ! { @@ -71,16 +71,16 @@ async fn main(spawner: Spawner) -> ! { p.ETH, Irqs, p.PA1, - p.PA2, - p.PC1, p.PA7, p.PC4, p.PC5, p.PG13, p.PB13, p.PG11, - GenericPhy::new_auto(), mac_addr, + p.ETH_SMA, + p.PA2, + p.PC1, ); let config = embassy_net::Config::dhcpv4(Default::default()); diff --git a/examples/stm32h7/src/bin/eth_client_mii.rs b/examples/stm32h7/src/bin/eth_client_mii.rs index c3c631f0f..92c823567 100644 --- a/examples/stm32h7/src/bin/eth_client_mii.rs +++ b/examples/stm32h7/src/bin/eth_client_mii.rs @@ -7,8 +7,8 @@ use defmt::*; use embassy_executor::Spawner; use embassy_net::StackResources; use embassy_net::tcp::client::{TcpClient, TcpClientState}; -use embassy_stm32::eth::{Ethernet, GenericPhy, PacketQueue}; -use embassy_stm32::peripherals::ETH; +use embassy_stm32::eth::{Ethernet, GenericPhy, PacketQueue, Sma}; +use embassy_stm32::peripherals::{ETH, ETH_SMA}; use embassy_stm32::rng::Rng; use embassy_stm32::{Config, bind_interrupts, eth, peripherals, rng}; use embassy_time::Timer; @@ -22,7 +22,7 @@ bind_interrupts!(struct Irqs { RNG => rng::InterruptHandler; }); -type Device = Ethernet<'static, ETH, GenericPhy>; +type Device = Ethernet<'static, ETH, GenericPhy>>; #[embassy_executor::task] async fn net_task(mut runner: embassy_net::Runner<'static, Device>) -> ! { @@ -72,8 +72,6 @@ async fn main(spawner: Spawner) -> ! { Irqs, p.PA1, p.PC3, - p.PA2, - p.PC1, p.PA7, p.PC4, p.PC5, @@ -84,8 +82,10 @@ async fn main(spawner: Spawner) -> ! { p.PC2, p.PE2, p.PG11, - GenericPhy::new_auto(), mac_addr, + p.ETH_SMA, + p.PA2, + p.PC1, ); info!("Device created"); diff --git a/examples/stm32h7rs/src/bin/eth.rs b/examples/stm32h7rs/src/bin/eth.rs index 5ce1d4765..8e07d0a67 100644 --- a/examples/stm32h7rs/src/bin/eth.rs +++ b/examples/stm32h7rs/src/bin/eth.rs @@ -5,8 +5,8 @@ use defmt::*; use embassy_executor::Spawner; use embassy_net::udp::{PacketMetadata, UdpSocket}; use embassy_net::{Ipv4Address, Ipv4Cidr, StackResources}; -use embassy_stm32::eth::{Ethernet, GenericPhy, PacketQueue}; -use embassy_stm32::peripherals::ETH; +use embassy_stm32::eth::{Ethernet, GenericPhy, PacketQueue, Sma}; +use embassy_stm32::peripherals::{ETH, ETH_SMA}; use embassy_stm32::rng::Rng; use embassy_stm32::{Config, bind_interrupts, eth, peripherals, rng}; use embassy_time::Timer; @@ -19,7 +19,7 @@ bind_interrupts!(struct Irqs { RNG => rng::InterruptHandler; }); -type Device = Ethernet<'static, ETH, GenericPhy>; +type Device = Ethernet<'static, ETH, GenericPhy>>; #[embassy_executor::task] async fn net_task(mut runner: embassy_net::Runner<'static, Device>) -> ! { @@ -69,16 +69,16 @@ async fn main(spawner: Spawner) -> ! { p.ETH, Irqs, p.PB6, - p.PA2, - p.PG6, p.PA7, p.PG4, p.PG5, p.PG13, p.PG12, p.PG11, - GenericPhy::new(0), mac_addr, + p.ETH_SMA, + p.PA2, + p.PG6, ); // Have to use UDP w/ static config to fit in internal flash diff --git a/tests/stm32/src/bin/eth.rs b/tests/stm32/src/bin/eth.rs index 95789ffc5..ffc76b96f 100644 --- a/tests/stm32/src/bin/eth.rs +++ b/tests/stm32/src/bin/eth.rs @@ -70,9 +70,6 @@ async fn main(spawner: Spawner) { static PACKETS: StaticCell> = StaticCell::new(); - let sma = Sma::new(p.ETH_SMA, p.PA2, p.PC1); - let phy = GenericPhy::new_auto(sma); - let device = Ethernet::new( PACKETS.init(PacketQueue::::new()), p.ETH, @@ -87,8 +84,10 @@ async fn main(spawner: Spawner) { #[cfg(feature = "stm32h563zi")] p.PB15, p.PG11, - phy, mac_addr, + p.ETH_SMA, + p.PA2, + p.PC1, ); let config = embassy_net::Config::dhcpv4(Default::default()); -- cgit From a2c5a0d9480b99cdb09940637354bc61405ed7bd Mon Sep 17 00:00:00 2001 From: xoviat Date: Fri, 14 Nov 2025 10:17:45 -0600 Subject: adc: fix g4 injected sequence --- embassy-stm32/src/adc/g4.rs | 23 +++++++++++++++-------- 1 file changed, 15 insertions(+), 8 deletions(-) diff --git a/embassy-stm32/src/adc/g4.rs b/embassy-stm32/src/adc/g4.rs index 514734017..bd8ccbf17 100644 --- a/embassy-stm32/src/adc/g4.rs +++ b/embassy-stm32/src/adc/g4.rs @@ -1,3 +1,5 @@ +#[cfg(stm32g4)] +use pac::adc::regs::Difsel as DifselReg; #[allow(unused)] #[cfg(stm32h7)] use pac::adc::vals::{Adcaldif, Difsel, Exten}; @@ -179,6 +181,9 @@ impl super::SealedAnyInstance for T { w.set_l(sequence.len() as u8 - 1); }); + #[cfg(stm32g4)] + let mut difsel = DifselReg::default(); + // Configure channels and ranks for (_i, ((ch, is_differential), sample_time)) in sequence.enumerate() { let sample_time = sample_time.into(); @@ -214,10 +219,8 @@ impl super::SealedAnyInstance for T { #[cfg(stm32g4)] { - T::regs().cr().modify(|w| w.set_aden(false)); // disable adc - - T::regs().difsel().modify(|w| { - w.set_difsel( + if ch < 18 { + difsel.set_difsel( ch.into(), if is_differential { Difsel::DIFFERENTIAL @@ -225,11 +228,16 @@ impl super::SealedAnyInstance for T { Difsel::SINGLE_ENDED }, ); - }); - - T::regs().cr().modify(|w| w.set_aden(true)); // enable adc + } } } + + #[cfg(stm32g4)] + { + T::regs().cr().modify(|w| w.set_aden(false)); + T::regs().difsel().write_value(difsel); + T::enable(); + } } } @@ -412,7 +420,6 @@ impl<'d, T: Instance + AnyInstance> Adc<'d, T> { NR_INJECTED_RANKS ); - T::stop(); T::enable(); T::regs().jsqr().modify(|w| w.set_jl(N as u8 - 1)); -- cgit From b97b6d409c1b042b5d5f1b17dd2c8dfec50acdfc Mon Sep 17 00:00:00 2001 From: xoviat Date: Fri, 14 Nov 2025 14:53:09 -0600 Subject: low_power: cleanup add_time --- embassy-stm32/src/time_driver.rs | 33 +++++++-------------------------- 1 file changed, 7 insertions(+), 26 deletions(-) diff --git a/embassy-stm32/src/time_driver.rs b/embassy-stm32/src/time_driver.rs index 7db51d72e..bc34892ee 100644 --- a/embassy-stm32/src/time_driver.rs +++ b/embassy-stm32/src/time_driver.rs @@ -196,6 +196,11 @@ fn calc_now(period: u32, counter: u16) -> u64 { ((period as u64) << 15) + ((counter as u32 ^ ((period & 1) << 15)) as u64) } +#[cfg(feature = "low-power")] +fn calc_period_counter(ticks: u64) -> (u32, u16) { + (2 * (ticks >> 16) as u32 + (ticks as u16 >= 0x8000) as u32, ticks as u16) +} + struct AlarmState { timestamp: Cell, } @@ -358,34 +363,10 @@ impl RtcDriver { #[cfg(feature = "low-power")] /// Add the given offset to the current time fn add_time(&self, offset: embassy_time::Duration, cs: CriticalSection) { - let offset = offset.as_ticks(); - let cnt = regs_gp16().cnt().read().cnt() as u32; - let period = self.period.load(Ordering::SeqCst); - - // Correct the race, if it exists - let period = if period & 1 == 1 && cnt < u16::MAX as u32 / 2 { - period + 1 - } else { - period - }; - - // Normalize to the full overflow - let period = (period / 2) * 2; - - // Add the offset - let period = period + 2 * (offset / u16::MAX as u64) as u32; - let cnt = cnt + (offset % u16::MAX as u64) as u32; - - let (cnt, period) = if cnt > u16::MAX as u32 { - (cnt - u16::MAX as u32, period + 2) - } else { - (cnt, period) - }; - - let period = if cnt > u16::MAX as u32 / 2 { period + 1 } else { period }; + let (period, counter) = calc_period_counter(self.now() + offset.as_ticks()); self.period.store(period, Ordering::SeqCst); - regs_gp16().cnt().write(|w| w.set_cnt(cnt as u16)); + regs_gp16().cnt().write(|w| w.set_cnt(counter)); // Now, recompute alarm let alarm = self.alarm.borrow(cs); -- cgit From af9bfe52cb1949c0f484cbab3701a6072fcd8496 Mon Sep 17 00:00:00 2001 From: xoviat Date: Fri, 14 Nov 2025 15:17:25 -0600 Subject: changelog --- embassy-stm32/CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/embassy-stm32/CHANGELOG.md b/embassy-stm32/CHANGELOG.md index 8bd930e79..cb846588e 100644 --- a/embassy-stm32/CHANGELOG.md +++ b/embassy-stm32/CHANGELOG.md @@ -7,6 +7,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## Unreleased - ReleaseDate +- chore: cleanup low-power add time - fix: flash erase on dual-bank STM32Gxxx - feat: Add support for STM32N657X0 - feat: timer: Add 32-bit timer support to SimplePwm waveform_up method following waveform pattern ([#4717](https://github.com/embassy-rs/embassy/pull/4717)) -- cgit From c49398adfcb7d6c874f37ec00eba89e416712c50 Mon Sep 17 00:00:00 2001 From: xoviat Date: Fri, 14 Nov 2025 15:44:30 -0600 Subject: low_power remove wucksel enum --- embassy-stm32/src/rtc/low_power.rs | 69 +++++++------------------------------- 1 file changed, 13 insertions(+), 56 deletions(-) diff --git a/embassy-stm32/src/rtc/low_power.rs b/embassy-stm32/src/rtc/low_power.rs index e5bf30927..264b0b795 100644 --- a/embassy-stm32/src/rtc/low_power.rs +++ b/embassy-stm32/src/rtc/low_power.rs @@ -1,3 +1,4 @@ +use crate::pac::rtc::vals::Wucksel; #[cfg(feature = "time")] use embassy_time::{Duration, TICK_HZ}; @@ -58,60 +59,16 @@ impl core::ops::Sub for RtcInstant { } } -#[repr(u8)] -#[derive(Clone, Copy, Debug)] -pub(crate) enum WakeupPrescaler { - Div2 = 2, - Div4 = 4, - Div8 = 8, - Div16 = 16, -} - -#[cfg(any( - stm32f4, stm32l0, stm32g4, stm32l4, stm32l5, stm32wb, stm32h5, stm32g0, stm32u5, stm32u0, stm32wba, stm32wlex -))] -impl From for crate::pac::rtc::vals::Wucksel { - fn from(val: WakeupPrescaler) -> Self { - use crate::pac::rtc::vals::Wucksel; - - match val { - WakeupPrescaler::Div2 => Wucksel::DIV2, - WakeupPrescaler::Div4 => Wucksel::DIV4, - WakeupPrescaler::Div8 => Wucksel::DIV8, - WakeupPrescaler::Div16 => Wucksel::DIV16, - } - } -} - -#[cfg(any( - stm32f4, stm32l0, stm32g4, stm32l4, stm32l5, stm32wb, stm32h5, stm32g0, stm32u5, stm32u0, stm32wba, stm32wlex -))] -impl From for WakeupPrescaler { - fn from(val: crate::pac::rtc::vals::Wucksel) -> Self { - use crate::pac::rtc::vals::Wucksel; - - match val { - Wucksel::DIV2 => WakeupPrescaler::Div2, - Wucksel::DIV4 => WakeupPrescaler::Div4, - Wucksel::DIV8 => WakeupPrescaler::Div8, - Wucksel::DIV16 => WakeupPrescaler::Div16, - _ => unreachable!(), - } - } -} - -impl WakeupPrescaler { - pub fn compute_min(val: u32) -> Self { - *[ - WakeupPrescaler::Div2, - WakeupPrescaler::Div4, - WakeupPrescaler::Div8, - WakeupPrescaler::Div16, - ] - .iter() - .find(|psc| **psc as u32 > val) - .unwrap_or(&WakeupPrescaler::Div16) - } +fn wucksel_compute_min(val: u32) -> (Wucksel, u32) { + *[ + (Wucksel::DIV2, 2), + (Wucksel::DIV4, 4), + (Wucksel::DIV8, 8), + (Wucksel::DIV16, 16), + ] + .iter() + .find(|(_, psc)| *psc as u32 > val) + .unwrap_or(&(Wucksel::DIV16, 16)) } impl Rtc { @@ -138,7 +95,7 @@ impl Rtc { let requested_duration = requested_duration.as_ticks().clamp(0, u32::MAX as u64); let rtc_hz = Self::frequency().0 as u64; let rtc_ticks = requested_duration * rtc_hz / TICK_HZ; - let prescaler = WakeupPrescaler::compute_min((rtc_ticks / u16::MAX as u64) as u32); + let (wucksel, prescaler) = wucksel_compute_min((rtc_ticks / u16::MAX as u64) as u32); // adjust the rtc ticks to the prescaler and subtract one rtc tick let rtc_ticks = rtc_ticks / prescaler as u64; @@ -159,7 +116,7 @@ impl Rtc { while !regs.icsr().read().wutwf() {} } - regs.cr().modify(|w| w.set_wucksel(prescaler.into())); + regs.cr().modify(|w| w.set_wucksel(wucksel)); regs.wutr().write(|w| w.set_wut(rtc_ticks)); regs.cr().modify(|w| w.set_wute(true)); regs.cr().modify(|w| w.set_wutie(true)); -- cgit From 4a919328dea682f29ae469cc7b0d47116647e74b Mon Sep 17 00:00:00 2001 From: xoviat Date: Fri, 14 Nov 2025 15:51:05 -0600 Subject: fmt --- embassy-stm32/src/rtc/low_power.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/embassy-stm32/src/rtc/low_power.rs b/embassy-stm32/src/rtc/low_power.rs index 264b0b795..f049d6b12 100644 --- a/embassy-stm32/src/rtc/low_power.rs +++ b/embassy-stm32/src/rtc/low_power.rs @@ -1,9 +1,9 @@ -use crate::pac::rtc::vals::Wucksel; #[cfg(feature = "time")] use embassy_time::{Duration, TICK_HZ}; use super::{DateTimeError, Rtc, RtcError, bcd2_to_byte}; use crate::interrupt::typelevel::Interrupt; +use crate::pac::rtc::vals::Wucksel; use crate::peripherals::RTC; use crate::rtc::{RtcTimeProvider, SealedInstance}; -- cgit From eb18f8482dceb644a626002d070359f997f1e474 Mon Sep 17 00:00:00 2001 From: datdenkikniet Date: Fri, 14 Nov 2025 23:01:12 +0100 Subject: Update CHANGELOG.md --- embassy-stm32/CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/embassy-stm32/CHANGELOG.md b/embassy-stm32/CHANGELOG.md index 8bd930e79..d17663a61 100644 --- a/embassy-stm32/CHANGELOG.md +++ b/embassy-stm32/CHANGELOG.md @@ -7,6 +7,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## Unreleased - ReleaseDate +- change: stm32/eth: ethernet no longer has a hard dependency on station management, and station management can be used independently ([#4871](https://github.com/embassy-rs/embassy/pull/4871)) - fix: flash erase on dual-bank STM32Gxxx - feat: Add support for STM32N657X0 - feat: timer: Add 32-bit timer support to SimplePwm waveform_up method following waveform pattern ([#4717](https://github.com/embassy-rs/embassy/pull/4717)) -- cgit From 23d74db1d6113914f2c4b80f0992bfeed235a89d Mon Sep 17 00:00:00 2001 From: Jakob Date: Sat, 15 Nov 2025 13:36:23 +0100 Subject: Avoid generating update events when chaning timer period. Set frequency update methods to return applied ARR values which then can be used for calcualting new CCR values. --- embassy-stm32/src/timer/complementary_pwm.rs | 10 ++++++---- embassy-stm32/src/timer/low_level.rs | 24 ++++++++---------------- embassy-stm32/src/timer/simple_pwm.rs | 10 ++++++---- 3 files changed, 20 insertions(+), 24 deletions(-) diff --git a/embassy-stm32/src/timer/complementary_pwm.rs b/embassy-stm32/src/timer/complementary_pwm.rs index 9a56a41fb..90ba196fc 100644 --- a/embassy-stm32/src/timer/complementary_pwm.rs +++ b/embassy-stm32/src/timer/complementary_pwm.rs @@ -160,15 +160,17 @@ impl<'d, T: AdvancedInstance4Channel> ComplementaryPwm<'d, T> { /// Set PWM frequency. /// - /// Note: when you call this, the max duty value changes, so you will have to - /// call `set_duty` on all channels with the duty calculated based on the new max duty. - pub fn set_frequency(&mut self, freq: Hertz) { + /// Returns the applied ARR value which can be used to calculate CCR values. + /// + /// Note: that the frequency will not be applied in the timer until an update event + /// occurs. Reading the `max_duty` before the update event will return the old value + pub fn set_frequency(&mut self, freq: Hertz) -> u32 { let multiplier = if self.inner.get_counting_mode().is_center_aligned() { 2u8 } else { 1u8 }; - self.inner.set_frequency_internal(freq * multiplier, 16); + self.inner.set_frequency_internal(freq * multiplier, 16) } /// Get max duty value. diff --git a/embassy-stm32/src/timer/low_level.rs b/embassy-stm32/src/timer/low_level.rs index 0122fe4f7..f6af8be8c 100644 --- a/embassy-stm32/src/timer/low_level.rs +++ b/embassy-stm32/src/timer/low_level.rs @@ -293,19 +293,17 @@ impl<'d, T: CoreInstance> Timer<'d, T> { /// the timer counter will wrap around at the same frequency as is being set. /// In center-aligned mode (which not all timers support), the wrap-around frequency is effectively halved /// because it needs to count up and down. - pub fn set_frequency(&self, frequency: Hertz) { + pub fn set_frequency(&self, frequency: Hertz) -> u32 { match T::BITS { - TimerBits::Bits16 => { - self.set_frequency_internal(frequency, 16); - } + TimerBits::Bits16 => self.set_frequency_internal(frequency, 16), #[cfg(not(stm32l0))] - TimerBits::Bits32 => { - self.set_frequency_internal(frequency, 32); - } + TimerBits::Bits32 => self.set_frequency_internal(frequency, 32), } } - pub(crate) fn set_frequency_internal(&self, frequency: Hertz, max_divide_by_bits: u8) { + /// Calculate ARR based on desired frequency + /// Returns actual value written to the register as u32 + pub(crate) fn set_frequency_internal(&self, frequency: Hertz, max_divide_by_bits: u8) -> u32 { let f = frequency.0; assert!(f > 0); let timer_f = T::frequency().0; @@ -322,10 +320,7 @@ impl<'d, T: CoreInstance> Timer<'d, T> { let regs = self.regs_core(); regs.psc().write_value(psc); regs.arr().write(|r| r.set_arr(arr)); - - regs.cr1().modify(|r| r.set_urs(vals::Urs::COUNTER_ONLY)); - regs.egr().write(|r| r.set_ug(true)); - regs.cr1().modify(|r| r.set_urs(vals::Urs::ANY_EVENT)); + arr as u32 } #[cfg(not(stm32l0))] TimerBits::Bits32 => { @@ -335,10 +330,7 @@ impl<'d, T: CoreInstance> Timer<'d, T> { let regs = self.regs_gp32_unchecked(); regs.psc().write_value(psc); regs.arr().write_value(arr); - - regs.cr1().modify(|r| r.set_urs(vals::Urs::COUNTER_ONLY)); - regs.egr().write(|r| r.set_ug(true)); - regs.cr1().modify(|r| r.set_urs(vals::Urs::ANY_EVENT)); + arr } } } diff --git a/embassy-stm32/src/timer/simple_pwm.rs b/embassy-stm32/src/timer/simple_pwm.rs index c338b0fd4..01996c969 100644 --- a/embassy-stm32/src/timer/simple_pwm.rs +++ b/embassy-stm32/src/timer/simple_pwm.rs @@ -285,16 +285,18 @@ impl<'d, T: GeneralInstance4Channel> SimplePwm<'d, T> { /// Set PWM frequency. /// - /// Note: when you call this, the max duty value changes, so you will have to - /// call `set_duty` on all channels with the duty calculated based on the new max duty. - pub fn set_frequency(&mut self, freq: Hertz) { + /// Returns the applied ARR value which can be used to calculate CCR values. + /// + /// Note: that the frequency will not be applied in the timer until an update event + /// occurs. Reading the `max_duty` before the update event will return the old value + pub fn set_frequency(&mut self, freq: Hertz) -> u32 { // TODO: prevent ARR = u16::MAX? let multiplier = if self.inner.get_counting_mode().is_center_aligned() { 2u8 } else { 1u8 }; - self.inner.set_frequency_internal(freq * multiplier, 16); + self.inner.set_frequency_internal(freq * multiplier, 16) } /// Get max duty value. -- cgit From 67af86d664cd84122824d0a039ce366f2dcdae03 Mon Sep 17 00:00:00 2001 From: Jakob Date: Sat, 15 Nov 2025 13:47:07 +0100 Subject: Add changelog entry --- embassy-stm32/CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/embassy-stm32/CHANGELOG.md b/embassy-stm32/CHANGELOG.md index 9153e15b9..b0287f73a 100644 --- a/embassy-stm32/CHANGELOG.md +++ b/embassy-stm32/CHANGELOG.md @@ -7,6 +7,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## Unreleased - ReleaseDate +- fix: Avoid generating timer update events when updating the frequency, add ARR as return value ([#4890](https://github.com/embassy-rs/embassy/pull/4890)) - fix: flash erase on dual-bank STM32Gxxx - feat: Add support for STM32N657X0 - feat: timer: Add 32-bit timer support to SimplePwm waveform_up method following waveform pattern ([#4717](https://github.com/embassy-rs/embassy/pull/4717)) -- cgit From 8e9ec797f255c7addcf43b390c234493a0913f92 Mon Sep 17 00:00:00 2001 From: xoviat Date: Sat, 15 Nov 2025 10:20:36 -0600 Subject: adc: move enable after configure_sequence --- embassy-stm32/src/adc/g4.rs | 50 ++++++++++++++++++++++---------------------- embassy-stm32/src/adc/mod.rs | 38 ++++++++++++++++++++++++++------- embassy-stm32/src/adc/v3.rs | 16 +++++++++++++- 3 files changed, 70 insertions(+), 34 deletions(-) diff --git a/embassy-stm32/src/adc/g4.rs b/embassy-stm32/src/adc/g4.rs index bd8ccbf17..4957123a1 100644 --- a/embassy-stm32/src/adc/g4.rs +++ b/embassy-stm32/src/adc/g4.rs @@ -1,14 +1,12 @@ #[cfg(stm32g4)] use pac::adc::regs::Difsel as DifselReg; #[allow(unused)] +#[cfg(stm32g4)] +pub use pac::adc::vals::{Adcaldif, Adstp, Difsel, Dmacfg, Dmaen, Exten, Rovsm, Trovs}; +#[allow(unused)] #[cfg(stm32h7)] use pac::adc::vals::{Adcaldif, Difsel, Exten}; -#[allow(unused)] -#[cfg(stm32g4)] -pub use pac::adc::vals::{Adcaldif, Difsel, Exten, Rovsm, Trovs}; -pub use pac::adccommon::vals::Presc; -pub use stm32_metapac::adc::vals::{Adstp, Dmacfg, Dmaen}; -pub use stm32_metapac::adccommon::vals::Dual; +pub use pac::adccommon::vals::{Dual, Presc}; use super::{ Adc, AnyAdcChannel, ConversionMode, Instance, RegularConversionMode, Resolution, RxDma, SampleTime, @@ -176,6 +174,8 @@ impl super::SealedAnyInstance for T { } fn configure_sequence(sequence: impl ExactSizeIterator) { + T::regs().cr().modify(|w| w.set_aden(false)); + // Set sequence length T::regs().sqr1().modify(|w| { w.set_l(sequence.len() as u8 - 1); @@ -183,36 +183,34 @@ impl super::SealedAnyInstance for T { #[cfg(stm32g4)] let mut difsel = DifselReg::default(); + let mut smpr = T::regs().smpr().read(); + let mut smpr2 = T::regs().smpr2().read(); + let mut sqr1 = T::regs().sqr1().read(); + let mut sqr2 = T::regs().sqr2().read(); + let mut sqr3 = T::regs().sqr3().read(); + let mut sqr4 = T::regs().sqr4().read(); // Configure channels and ranks for (_i, ((ch, is_differential), sample_time)) in sequence.enumerate() { let sample_time = sample_time.into(); if ch <= 9 { - T::regs().smpr().modify(|reg| reg.set_smp(ch as _, sample_time)); + smpr.set_smp(ch as _, sample_time); } else { - T::regs().smpr2().modify(|reg| reg.set_smp((ch - 10) as _, sample_time)); + smpr2.set_smp((ch - 10) as _, sample_time); } match _i { 0..=3 => { - T::regs().sqr1().modify(|w| { - w.set_sq(_i, ch); - }); + sqr1.set_sq(_i, ch); } 4..=8 => { - T::regs().sqr2().modify(|w| { - w.set_sq(_i - 4, ch); - }); + sqr2.set_sq(_i - 4, ch); } 9..=13 => { - T::regs().sqr3().modify(|w| { - w.set_sq(_i - 9, ch); - }); + sqr3.set_sq(_i - 9, ch); } 14..=15 => { - T::regs().sqr4().modify(|w| { - w.set_sq(_i - 14, ch); - }); + sqr4.set_sq(_i - 14, ch); } _ => unreachable!(), } @@ -232,12 +230,14 @@ impl super::SealedAnyInstance for T { } } + T::regs().smpr().write_value(smpr); + T::regs().smpr2().write_value(smpr2); + T::regs().sqr1().write_value(sqr1); + T::regs().sqr2().write_value(sqr2); + T::regs().sqr3().write_value(sqr3); + T::regs().sqr4().write_value(sqr4); #[cfg(stm32g4)] - { - T::regs().cr().modify(|w| w.set_aden(false)); - T::regs().difsel().write_value(difsel); - T::enable(); - } + T::regs().difsel().write_value(difsel); } } diff --git a/embassy-stm32/src/adc/mod.rs b/embassy-stm32/src/adc/mod.rs index 13f8a1544..74648cc21 100644 --- a/embassy-stm32/src/adc/mod.rs +++ b/embassy-stm32/src/adc/mod.rs @@ -192,10 +192,16 @@ impl<'d, T: AnyInstance> Adc<'d, T> { #[cfg(any(adc_v1, adc_c0, adc_l0, adc_v2, adc_g4, adc_v4, adc_u5, adc_wba))] channel.setup(); - #[cfg(not(adc_v4))] + #[cfg(any(adc_v2, adc_v3, adc_g0, adc_h7rs, adc_u0, adc_u5, adc_wba, adc_c0))] T::enable(); T::configure_sequence([((channel.channel(), channel.is_differential()), sample_time)].into_iter()); + // On chips with differential channels, enable after configure_sequence to allow setting differential channels + // + // TODO: If hardware allows, enable after configure_sequence on all chips + #[cfg(any(adc_g4, adc_h5))] + T::enable(); + T::convert() } @@ -229,10 +235,10 @@ impl<'d, T: AnyInstance> Adc<'d, T> { /// Note: This is not very efficient as the ADC needs to be reconfigured for each read. Use /// `into_ring_buffered`, `into_ring_buffered_and_injected` /// - /// In STM32C0, channels bigger than 14 cannot be read using sequencer, so you have to use - /// either blocking reads or use the mechanism to read in HW order (CHSELRMOD=0). - /// - /// In addtion, on STM320, this method will panic if the channels are not passed in order + /// Note: Depending on hardware limitations, this method may require channels to be passed + /// in order or require the sequence to have the same sample time for all channnels, depending + /// on the number and properties of the channels in the sequence. This method will panic if + /// the hardware cannot deliver the requested configuration. pub async fn read( &mut self, rx_dma: embassy_hal_internal::Peri<'_, impl RxDma>, @@ -249,14 +255,20 @@ impl<'d, T: AnyInstance> Adc<'d, T> { "Asynchronous read sequence cannot be more than 16 in length" ); - // Ensure no conversions are ongoing and ADC is enabled. + // Ensure no conversions are ongoing T::stop(); + #[cfg(any(adc_g0, adc_v3, adc_h7rs, adc_u0, adc_v4, adc_u5, adc_wba, adc_c0))] T::enable(); T::configure_sequence( sequence.map(|(channel, sample_time)| ((channel.channel, channel.is_differential), sample_time)), ); + // On chips with differential channels, enable after configure_sequence to allow setting differential channels + // + // TODO: If hardware allows, enable after configure_sequence on all chips + #[cfg(any(adc_g4, adc_h5))] + T::enable(); T::configure_dma(ConversionMode::Singular); let request = rx_dma.request(); @@ -294,6 +306,11 @@ impl<'d, T: AnyInstance> Adc<'d, T> { /// /// # Returns /// A `RingBufferedAdc<'a, T>` instance configured for continuous DMA-based sampling. + /// + /// Note: Depending on hardware limitations, this method may require channels to be passed + /// in order or require the sequence to have the same sample time for all channnels, depending + /// on the number and properties of the channels in the sequence. This method will panic if + /// the hardware cannot deliver the requested configuration. pub fn into_ring_buffered<'a>( self, dma: embassy_hal_internal::Peri<'a, impl RxDma>, @@ -307,15 +324,20 @@ impl<'d, T: AnyInstance> Adc<'d, T> { sequence.len() <= 16, "Asynchronous read sequence cannot be more than 16 in length" ); - // reset conversions and enable the adc + // Ensure no conversions are ongoing T::stop(); + #[cfg(any(adc_g0, adc_v3, adc_h7rs, adc_u0, adc_v4, adc_u5, adc_wba, adc_c0))] T::enable(); - //adc side setup T::configure_sequence( sequence.map(|(channel, sample_time)| ((channel.channel, channel.is_differential), sample_time)), ); + // On chips with differential channels, enable after configure_sequence to allow setting differential channels + // + // TODO: If hardware allows, enable after configure_sequence on all chips + #[cfg(any(adc_g4, adc_h5))] + T::enable(); T::configure_dma(ConversionMode::Repeated(mode)); core::mem::forget(self); diff --git a/embassy-stm32/src/adc/v3.rs b/embassy-stm32/src/adc/v3.rs index 81eb1e3ee..b270588c4 100644 --- a/embassy-stm32/src/adc/v3.rs +++ b/embassy-stm32/src/adc/v3.rs @@ -260,6 +260,9 @@ impl super::SealedAnyInstance for T { } fn configure_sequence(sequence: impl ExactSizeIterator) { + #[cfg(adc_h5)] + T::regs().cr().modify(|w| w.set_aden(false)); + // Set sequence length #[cfg(not(any(adc_g0, adc_u0)))] T::regs().sqr1().modify(|w| { @@ -294,8 +297,11 @@ impl super::SealedAnyInstance for T { #[cfg(adc_u0)] let mut channel_mask = 0; + #[cfg(adc_h5)] + let mut difsel = 0u32; + // Configure channels and ranks - for (_i, ((channel, _), sample_time)) in sequence.enumerate() { + for (_i, ((channel, _is_differential), sample_time)) in sequence.enumerate() { // RM0492, RM0481, etc. // "This option bit must be set to 1 when ADCx_INP0 or ADCx_INN1 channel is selected." #[cfg(any(adc_h5, adc_h7rs))] @@ -357,12 +363,20 @@ impl super::SealedAnyInstance for T { _ => unreachable!(), } + #[cfg(adc_h5)] + { + difsel |= (_is_differential as u32) << channel; + } + #[cfg(adc_u0)] { channel_mask |= 1 << channel; } } + #[cfg(adc_h5)] + T::regs().difsel().write(|w| w.set_difsel(difsel)); + // On G0 and U0 enabled channels are sampled from 0 to last channel. // It is possible to add up to 8 sequences if CHSELRMOD = 1. // However for supporting more than 8 channels alternative CHSELRMOD = 0 approach is used. -- cgit From 3fb16229c7a237c29731aa05d5f29e8ea2eb015f Mon Sep 17 00:00:00 2001 From: everdrone Date: Sat, 15 Nov 2025 18:07:10 +0100 Subject: use try_into and unwrap --- embassy-stm32/src/sai/mod.rs | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/embassy-stm32/src/sai/mod.rs b/embassy-stm32/src/sai/mod.rs index 58e3b832a..ce4bc43c3 100644 --- a/embassy-stm32/src/sai/mod.rs +++ b/embassy-stm32/src/sai/mod.rs @@ -696,12 +696,7 @@ impl<'d, T: Instance, W: word::Word> Sai<'d, T, W> { w.set_fspol(config.frame_sync_polarity.fspol()); w.set_fsdef(config.frame_sync_definition.fsdef()); w.set_fsall(config.frame_sync_active_level_length.0 as u8 - 1); - - if config.frame_length > 256 { - panic!("Frame length cannot be greater than 256"); - } - - w.set_frl((config.frame_length - 1) as u8); + w.set_frl((config.frame_length - 1).try_into().unwrap()); }); ch.slotr().modify(|w| { -- cgit From 2880b00cbc59e19164574536e0f852e9aacf08b6 Mon Sep 17 00:00:00 2001 From: Albin Hedman Date: Sat, 15 Nov 2025 17:35:34 +0100 Subject: Move dma waveform methods down to low level timer --- embassy-stm32/src/timer/complementary_pwm.rs | 55 +------ embassy-stm32/src/timer/low_level.rs | 215 +++++++++++++++++++++++++- embassy-stm32/src/timer/simple_pwm.rs | 218 +-------------------------- 3 files changed, 224 insertions(+), 264 deletions(-) diff --git a/embassy-stm32/src/timer/complementary_pwm.rs b/embassy-stm32/src/timer/complementary_pwm.rs index 9a56a41fb..cb5e34790 100644 --- a/embassy-stm32/src/timer/complementary_pwm.rs +++ b/embassy-stm32/src/timer/complementary_pwm.rs @@ -218,60 +218,9 @@ impl<'d, T: AdvancedInstance4Channel> ComplementaryPwm<'d, T> { /// /// Note: /// you will need to provide corresponding TIMx_UP DMA channel to use this method. + #[inline(always)] pub async fn waveform_up(&mut self, dma: Peri<'_, impl super::UpDma>, channel: Channel, duty: &[u16]) { - #[allow(clippy::let_unit_value)] // eg. stm32f334 - let req = dma.request(); - - let original_duty_state = self.inner.get_compare_value(channel); - let original_enable_state = self.inner.get_channel_enable_state(channel); - let original_update_dma_state = self.inner.get_update_dma_state(); - - if !original_update_dma_state { - self.inner.enable_update_dma(true); - } - - if !original_enable_state { - self.inner.enable_channel(channel, true); - } - - unsafe { - #[cfg(not(any(bdma, gpdma)))] - use crate::dma::{Burst, FifoThreshold}; - use crate::dma::{Transfer, TransferOptions}; - - let dma_transfer_option = TransferOptions { - #[cfg(not(any(bdma, gpdma)))] - fifo_threshold: Some(FifoThreshold::Full), - #[cfg(not(any(bdma, gpdma)))] - mburst: Burst::Incr8, - ..Default::default() - }; - - Transfer::new_write( - dma, - req, - duty, - self.inner.regs_gp16().ccr(channel.index()).as_ptr() as *mut u16, - dma_transfer_option, - ) - .await - }; - - // restore output compare state - if !original_enable_state { - self.inner.enable_channel(channel, false); - } - - self.inner.set_compare_value(channel, original_duty_state); - - // Since DMA is closed before timer update event trigger DMA is turn off, - // this can almost always trigger a DMA FIFO error. - // - // optional TODO: - // clean FEIF after disable UDE - if !original_update_dma_state { - self.inner.enable_update_dma(false); - } + self.inner.waveform_up(dma, channel, duty).await } } diff --git a/embassy-stm32/src/timer/low_level.rs b/embassy-stm32/src/timer/low_level.rs index 0122fe4f7..c574277e7 100644 --- a/embassy-stm32/src/timer/low_level.rs +++ b/embassy-stm32/src/timer/low_level.rs @@ -14,8 +14,8 @@ pub use stm32_metapac::timer::vals::{FilterValue, Mms as MasterMode, Sms as Slav use super::*; use crate::pac::timer::vals; -use crate::rcc; use crate::time::Hertz; +use crate::{dma, rcc}; /// Input capture mode. #[derive(Clone, Copy)] @@ -656,6 +656,219 @@ impl<'d, T: GeneralInstance4Channel> Timer<'d, T> { } } + /// Generate a sequence of PWM waveform + /// + /// Note: + /// you will need to provide corresponding TIMx_UP DMA channel to use this method. + pub async fn waveform_up(&mut self, dma: Peri<'_, impl super::UpDma>, channel: Channel, duty: &[u16]) { + #[allow(clippy::let_unit_value)] // eg. stm32f334 + let req = dma.request(); + + let original_update_dma_state = self.get_update_dma_state(); + + if !original_update_dma_state { + self.enable_update_dma(true); + } + + self.waveform_helper(dma, req, channel, duty).await; + + // Since DMA is closed before timer update event trigger DMA is turn off, + // this can almost always trigger a DMA FIFO error. + // + // optional TODO: + // clean FEIF after disable UDE + if !original_update_dma_state { + self.enable_update_dma(false); + } + } + + /// Generate a multichannel sequence of PWM waveforms using DMA triggered by timer update events. + /// + /// This method utilizes the timer's DMA burst transfer capability to update multiple CCRx registers + /// in sequence on each update event (UEV). The data is written via the DMAR register using the + /// DMA base address (DBA) and burst length (DBL) configured in the DCR register. + /// + /// The `duty` buffer must be structured as a flattened 2D array in row-major order, where each row + /// represents a single update event and each column corresponds to a specific timer channel (starting + /// from `starting_channel` up to and including `ending_channel`). + /// + /// For example, if using channels 1 through 4, a buffer of 4 update steps might look like: + /// + /// ```rust,ignore + /// let dma_buf: [u16; 16] = [ + /// ch1_duty_1, ch2_duty_1, ch3_duty_1, ch4_duty_1, // update 1 + /// ch1_duty_2, ch2_duty_2, ch3_duty_2, ch4_duty_2, // update 2 + /// ch1_duty_3, ch2_duty_3, ch3_duty_3, ch4_duty_3, // update 3 + /// ch1_duty_4, ch2_duty_4, ch3_duty_4, ch4_duty_4, // update 4 + /// ]; + /// ``` + /// + /// Each group of `N` values (where `N` is number of channels) is transferred on one update event, + /// updating the duty cycles of all selected channels simultaneously. + /// + /// Note: + /// You will need to provide corresponding `TIMx_UP` DMA channel to use this method. + /// Also be aware that embassy timers use one of timers internally. It is possible to + /// switch this timer by using `time-driver-timX` feature. + /// + pub async fn waveform_up_multi_channel( + &mut self, + dma: Peri<'_, impl super::UpDma>, + starting_channel: Channel, + ending_channel: Channel, + duty: &[u16], + ) { + let cr1_addr = self.regs_gp16().cr1().as_ptr() as u32; + let start_ch_index = starting_channel.index(); + let end_ch_index = ending_channel.index(); + + assert!(start_ch_index <= end_ch_index); + + let ccrx_addr = self.regs_gp16().ccr(start_ch_index).as_ptr() as u32; + self.regs_gp16() + .dcr() + .modify(|w| w.set_dba(((ccrx_addr - cr1_addr) / 4) as u8)); + self.regs_gp16() + .dcr() + .modify(|w| w.set_dbl((end_ch_index - start_ch_index) as u8)); + + #[allow(clippy::let_unit_value)] // eg. stm32f334 + let req = dma.request(); + + let original_update_dma_state = self.get_update_dma_state(); + if !original_update_dma_state { + self.enable_update_dma(true); + } + + unsafe { + #[cfg(not(any(bdma, gpdma)))] + use crate::dma::{Burst, FifoThreshold}; + use crate::dma::{Transfer, TransferOptions}; + + let dma_transfer_option = TransferOptions { + #[cfg(not(any(bdma, gpdma)))] + fifo_threshold: Some(FifoThreshold::Full), + #[cfg(not(any(bdma, gpdma)))] + mburst: Burst::Incr4, + ..Default::default() + }; + + Transfer::new_write( + dma, + req, + duty, + self.regs_gp16().dmar().as_ptr() as *mut u16, + dma_transfer_option, + ) + .await + }; + + if !original_update_dma_state { + self.enable_update_dma(false); + } + } + + /// Generate a sequence of PWM waveform + pub async fn waveform(&mut self, dma: Peri<'_, impl super::Dma>, duty: &[u16]) { + use crate::pac::timer::vals::Ccds; + + #[allow(clippy::let_unit_value)] // eg. stm32f334 + let req = dma.request(); + + let cc_channel = C::CHANNEL; + + let original_cc_dma_on_update = self.get_cc_dma_selection() == Ccds::ON_UPDATE; + let original_cc_dma_enabled = self.get_cc_dma_enable_state(cc_channel); + + // redirect CC DMA request onto Update Event + if !original_cc_dma_on_update { + self.set_cc_dma_selection(Ccds::ON_UPDATE) + } + + if !original_cc_dma_enabled { + self.set_cc_dma_enable_state(cc_channel, true); + } + + self.waveform_helper(dma, req, cc_channel, duty).await; + + // Since DMA is closed before timer Capture Compare Event trigger DMA is turn off, + // this can almost always trigger a DMA FIFO error. + // + // optional TODO: + // clean FEIF after disable UDE + if !original_cc_dma_enabled { + self.set_cc_dma_enable_state(cc_channel, false); + } + + if !original_cc_dma_on_update { + self.set_cc_dma_selection(Ccds::ON_COMPARE) + } + } + + async fn waveform_helper( + &mut self, + dma: Peri<'_, impl dma::Channel>, + req: dma::Request, + channel: Channel, + duty: &[u16], + ) { + let original_duty_state = self.get_compare_value(channel); + let original_enable_state = self.get_channel_enable_state(channel); + + if !original_enable_state { + self.enable_channel(channel, true); + } + + unsafe { + #[cfg(not(any(bdma, gpdma)))] + use crate::dma::{Burst, FifoThreshold}; + use crate::dma::{Transfer, TransferOptions}; + + let dma_transfer_option = TransferOptions { + #[cfg(not(any(bdma, gpdma)))] + fifo_threshold: Some(FifoThreshold::Full), + #[cfg(not(any(bdma, gpdma)))] + mburst: Burst::Incr8, + ..Default::default() + }; + + match self.bits() { + TimerBits::Bits16 => { + Transfer::new_write( + dma, + req, + duty, + self.regs_1ch().ccr(channel.index()).as_ptr() as *mut u16, + dma_transfer_option, + ) + .await + } + #[cfg(not(any(stm32l0)))] + TimerBits::Bits32 => { + #[cfg(not(any(bdma, gpdma)))] + panic!("unsupported timer bits"); + + #[cfg(any(bdma, gpdma))] + Transfer::new_write( + dma, + req, + duty, + self.regs_1ch().ccr(channel.index()).as_ptr() as *mut u32, + dma_transfer_option, + ) + .await + } + }; + }; + + // restore output compare state + if !original_enable_state { + self.enable_channel(channel, false); + } + + self.set_compare_value(channel, original_duty_state); + } + /// Get capture value for a channel. pub fn get_capture_value(&self, channel: Channel) -> u32 { self.get_compare_value(channel) diff --git a/embassy-stm32/src/timer/simple_pwm.rs b/embassy-stm32/src/timer/simple_pwm.rs index c338b0fd4..19a0b38d1 100644 --- a/embassy-stm32/src/timer/simple_pwm.rs +++ b/embassy-stm32/src/timer/simple_pwm.rs @@ -4,7 +4,7 @@ use core::marker::PhantomData; use core::mem::ManuallyDrop; use super::low_level::{CountingMode, OutputCompareMode, OutputPolarity, Timer}; -use super::{Ch1, Ch2, Ch3, Ch4, Channel, GeneralInstance4Channel, TimerBits, TimerChannel, TimerPin}; +use super::{Ch1, Ch2, Ch3, Ch4, Channel, GeneralInstance4Channel, TimerChannel, TimerPin}; use crate::Peri; #[cfg(gpio_v2)] use crate::gpio::Pull; @@ -312,79 +312,9 @@ impl<'d, T: GeneralInstance4Channel> SimplePwm<'d, T> { /// You will need to provide corresponding `TIMx_UP` DMA channel to use this method. /// Also be aware that embassy timers use one of timers internally. It is possible to /// switch this timer by using `time-driver-timX` feature. + #[inline(always)] pub async fn waveform_up(&mut self, dma: Peri<'_, impl super::UpDma>, channel: Channel, duty: &[u16]) { - #[allow(clippy::let_unit_value)] // eg. stm32f334 - let req = dma.request(); - - let original_duty_state = self.channel(channel).current_duty_cycle(); - let original_enable_state = self.channel(channel).is_enabled(); - let original_update_dma_state = self.inner.get_update_dma_state(); - - if !original_update_dma_state { - self.inner.enable_update_dma(true); - } - - if !original_enable_state { - self.channel(channel).enable(); - } - - unsafe { - #[cfg(not(any(bdma, gpdma)))] - use crate::dma::{Burst, FifoThreshold}; - use crate::dma::{Transfer, TransferOptions}; - - let dma_transfer_option = TransferOptions { - #[cfg(not(any(bdma, gpdma)))] - fifo_threshold: Some(FifoThreshold::Full), - #[cfg(not(any(bdma, gpdma)))] - mburst: Burst::Incr8, - ..Default::default() - }; - - match self.inner.bits() { - TimerBits::Bits16 => { - Transfer::new_write( - dma, - req, - duty, - self.inner.regs_1ch().ccr(channel.index()).as_ptr() as *mut u16, - dma_transfer_option, - ) - .await - } - #[cfg(not(any(stm32l0)))] - TimerBits::Bits32 => { - #[cfg(not(any(bdma, gpdma)))] - panic!("unsupported timer bits"); - - #[cfg(any(bdma, gpdma))] - Transfer::new_write( - dma, - req, - duty, - self.inner.regs_1ch().ccr(channel.index()).as_ptr() as *mut u32, - dma_transfer_option, - ) - .await - } - }; - }; - - // restore output compare state - if !original_enable_state { - self.channel(channel).disable(); - } - - self.channel(channel).set_duty_cycle(original_duty_state); - - // Since DMA is closed before timer update event trigger DMA is turn off, - // this can almost always trigger a DMA FIFO error. - // - // optional TODO: - // clean FEIF after disable UDE - if !original_update_dma_state { - self.inner.enable_update_dma(false); - } + self.inner.waveform_up(dma, channel, duty).await; } /// Generate a multichannel sequence of PWM waveforms using DMA triggered by timer update events. @@ -416,6 +346,7 @@ impl<'d, T: GeneralInstance4Channel> SimplePwm<'d, T> { /// Also be aware that embassy timers use one of timers internally. It is possible to /// switch this timer by using `time-driver-timX` feature. /// + #[inline(always)] pub async fn waveform_up_multi_channel( &mut self, dma: Peri<'_, impl super::UpDma>, @@ -423,148 +354,15 @@ impl<'d, T: GeneralInstance4Channel> SimplePwm<'d, T> { ending_channel: Channel, duty: &[u16], ) { - let cr1_addr = self.inner.regs_gp16().cr1().as_ptr() as u32; - let start_ch_index = starting_channel.index(); - let end_ch_index = ending_channel.index(); - - assert!(start_ch_index <= end_ch_index); - - let ccrx_addr = self.inner.regs_gp16().ccr(start_ch_index).as_ptr() as u32; self.inner - .regs_gp16() - .dcr() - .modify(|w| w.set_dba(((ccrx_addr - cr1_addr) / 4) as u8)); - self.inner - .regs_gp16() - .dcr() - .modify(|w| w.set_dbl((end_ch_index - start_ch_index) as u8)); - - #[allow(clippy::let_unit_value)] // eg. stm32f334 - let req = dma.request(); - - let original_update_dma_state = self.inner.get_update_dma_state(); - if !original_update_dma_state { - self.inner.enable_update_dma(true); - } - - unsafe { - #[cfg(not(any(bdma, gpdma)))] - use crate::dma::{Burst, FifoThreshold}; - use crate::dma::{Transfer, TransferOptions}; - - let dma_transfer_option = TransferOptions { - #[cfg(not(any(bdma, gpdma)))] - fifo_threshold: Some(FifoThreshold::Full), - #[cfg(not(any(bdma, gpdma)))] - mburst: Burst::Incr4, - ..Default::default() - }; - - Transfer::new_write( - dma, - req, - duty, - self.inner.regs_gp16().dmar().as_ptr() as *mut u16, - dma_transfer_option, - ) - .await - }; - - if !original_update_dma_state { - self.inner.enable_update_dma(false); - } + .waveform_up_multi_channel(dma, starting_channel, ending_channel, duty) + .await; } -} -impl<'d, T: GeneralInstance4Channel> SimplePwm<'d, T> { /// Generate a sequence of PWM waveform + #[inline(always)] pub async fn waveform(&mut self, dma: Peri<'_, impl super::Dma>, duty: &[u16]) { - use crate::pac::timer::vals::Ccds; - - #[allow(clippy::let_unit_value)] // eg. stm32f334 - let req = dma.request(); - - let cc_channel = C::CHANNEL; - - let original_duty_state = self.channel(cc_channel).current_duty_cycle(); - let original_enable_state = self.channel(cc_channel).is_enabled(); - let original_cc_dma_on_update = self.inner.get_cc_dma_selection() == Ccds::ON_UPDATE; - let original_cc_dma_enabled = self.inner.get_cc_dma_enable_state(cc_channel); - - // redirect CC DMA request onto Update Event - if !original_cc_dma_on_update { - self.inner.set_cc_dma_selection(Ccds::ON_UPDATE) - } - - if !original_cc_dma_enabled { - self.inner.set_cc_dma_enable_state(cc_channel, true); - } - - if !original_enable_state { - self.channel(cc_channel).enable(); - } - - unsafe { - #[cfg(not(any(bdma, gpdma)))] - use crate::dma::{Burst, FifoThreshold}; - use crate::dma::{Transfer, TransferOptions}; - - let dma_transfer_option = TransferOptions { - #[cfg(not(any(bdma, gpdma)))] - fifo_threshold: Some(FifoThreshold::Full), - #[cfg(not(any(bdma, gpdma)))] - mburst: Burst::Incr8, - ..Default::default() - }; - - match self.inner.bits() { - TimerBits::Bits16 => { - Transfer::new_write( - dma, - req, - duty, - self.inner.regs_gp16().ccr(cc_channel.index()).as_ptr() as *mut u16, - dma_transfer_option, - ) - .await - } - #[cfg(not(any(stm32l0)))] - TimerBits::Bits32 => { - #[cfg(not(any(bdma, gpdma)))] - panic!("unsupported timer bits"); - - #[cfg(any(bdma, gpdma))] - Transfer::new_write( - dma, - req, - duty, - self.inner.regs_gp16().ccr(cc_channel.index()).as_ptr() as *mut u32, - dma_transfer_option, - ) - .await - } - }; - }; - - // restore output compare state - if !original_enable_state { - self.channel(cc_channel).disable(); - } - - self.channel(cc_channel).set_duty_cycle(original_duty_state); - - // Since DMA is closed before timer Capture Compare Event trigger DMA is turn off, - // this can almost always trigger a DMA FIFO error. - // - // optional TODO: - // clean FEIF after disable UDE - if !original_cc_dma_enabled { - self.inner.set_cc_dma_enable_state(cc_channel, false); - } - - if !original_cc_dma_on_update { - self.inner.set_cc_dma_selection(Ccds::ON_COMPARE) - } + self.inner.waveform(dma, duty).await; } } -- cgit From 7d75dbcf00e434507e7e8a658c1ff262d1501a0d Mon Sep 17 00:00:00 2001 From: Albin Hedman Date: Sat, 15 Nov 2025 17:38:50 +0100 Subject: Add dma waveform methods to complementary pwm too --- embassy-stm32/src/timer/complementary_pwm.rs | 48 ++++++++++++++++++++++++++++ 1 file changed, 48 insertions(+) diff --git a/embassy-stm32/src/timer/complementary_pwm.rs b/embassy-stm32/src/timer/complementary_pwm.rs index cb5e34790..76cbbe91d 100644 --- a/embassy-stm32/src/timer/complementary_pwm.rs +++ b/embassy-stm32/src/timer/complementary_pwm.rs @@ -222,6 +222,54 @@ impl<'d, T: AdvancedInstance4Channel> ComplementaryPwm<'d, T> { pub async fn waveform_up(&mut self, dma: Peri<'_, impl super::UpDma>, channel: Channel, duty: &[u16]) { self.inner.waveform_up(dma, channel, duty).await } + + /// Generate a multichannel sequence of PWM waveforms using DMA triggered by timer update events. + /// + /// This method utilizes the timer's DMA burst transfer capability to update multiple CCRx registers + /// in sequence on each update event (UEV). The data is written via the DMAR register using the + /// DMA base address (DBA) and burst length (DBL) configured in the DCR register. + /// + /// The `duty` buffer must be structured as a flattened 2D array in row-major order, where each row + /// represents a single update event and each column corresponds to a specific timer channel (starting + /// from `starting_channel` up to and including `ending_channel`). + /// + /// For example, if using channels 1 through 4, a buffer of 4 update steps might look like: + /// + /// ```rust,ignore + /// let dma_buf: [u16; 16] = [ + /// ch1_duty_1, ch2_duty_1, ch3_duty_1, ch4_duty_1, // update 1 + /// ch1_duty_2, ch2_duty_2, ch3_duty_2, ch4_duty_2, // update 2 + /// ch1_duty_3, ch2_duty_3, ch3_duty_3, ch4_duty_3, // update 3 + /// ch1_duty_4, ch2_duty_4, ch3_duty_4, ch4_duty_4, // update 4 + /// ]; + /// ``` + /// + /// Each group of `N` values (where `N` is number of channels) is transferred on one update event, + /// updating the duty cycles of all selected channels simultaneously. + /// + /// Note: + /// You will need to provide corresponding `TIMx_UP` DMA channel to use this method. + /// Also be aware that embassy timers use one of timers internally. It is possible to + /// switch this timer by using `time-driver-timX` feature. + /// + #[inline(always)] + pub async fn waveform_up_multi_channel( + &mut self, + dma: Peri<'_, impl super::UpDma>, + starting_channel: Channel, + ending_channel: Channel, + duty: &[u16], + ) { + self.inner + .waveform_up_multi_channel(dma, starting_channel, ending_channel, duty) + .await; + } + + /// Generate a sequence of PWM waveform + #[inline(always)] + pub async fn waveform(&mut self, dma: Peri<'_, impl super::Dma>, duty: &[u16]) { + self.inner.waveform(dma, duty).await; + } } impl<'d, T: AdvancedInstance4Channel> embedded_hal_02::Pwm for ComplementaryPwm<'d, T> { -- cgit From cf7a0ea280b823ea080c4dbf05adfa8c3be451c1 Mon Sep 17 00:00:00 2001 From: Albin Hedman Date: Sat, 15 Nov 2025 18:03:34 +0100 Subject: Update changelog --- embassy-stm32/CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/embassy-stm32/CHANGELOG.md b/embassy-stm32/CHANGELOG.md index 72fe1c7a8..2c3dfb3d3 100644 --- a/embassy-stm32/CHANGELOG.md +++ b/embassy-stm32/CHANGELOG.md @@ -7,6 +7,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## Unreleased - ReleaseDate +- feat: Add waveform methods to ComplementaryPwm - chore: cleanup low-power add time - fix: Allow setting SAI peripheral `frame_length` to `256` - fix: flash erase on dual-bank STM32Gxxx -- cgit From 4793f59cde20203b33dca7222d12cbd9f95d5e1c Mon Sep 17 00:00:00 2001 From: Jakob Date: Sat, 15 Nov 2025 20:19:06 +0100 Subject: Add separate method for generating update event. Make sure values are loaded into shadow registers before starting the timer. --- embassy-stm32/CHANGELOG.md | 2 +- embassy-stm32/src/timer/complementary_pwm.rs | 14 +++++++------- embassy-stm32/src/timer/input_capture.rs | 1 + embassy-stm32/src/timer/low_level.rs | 26 ++++++++++++++++++-------- embassy-stm32/src/timer/pwm_input.rs | 1 + embassy-stm32/src/timer/simple_pwm.rs | 14 ++++++++------ 6 files changed, 36 insertions(+), 22 deletions(-) diff --git a/embassy-stm32/CHANGELOG.md b/embassy-stm32/CHANGELOG.md index b0287f73a..71b8cdafa 100644 --- a/embassy-stm32/CHANGELOG.md +++ b/embassy-stm32/CHANGELOG.md @@ -7,7 +7,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## Unreleased - ReleaseDate -- fix: Avoid generating timer update events when updating the frequency, add ARR as return value ([#4890](https://github.com/embassy-rs/embassy/pull/4890)) +- fix: Avoid generating timer update events when updating the frequency ([#4890](https://github.com/embassy-rs/embassy/pull/4890)) - fix: flash erase on dual-bank STM32Gxxx - feat: Add support for STM32N657X0 - feat: timer: Add 32-bit timer support to SimplePwm waveform_up method following waveform pattern ([#4717](https://github.com/embassy-rs/embassy/pull/4717)) diff --git a/embassy-stm32/src/timer/complementary_pwm.rs b/embassy-stm32/src/timer/complementary_pwm.rs index 90ba196fc..3331e5b6b 100644 --- a/embassy-stm32/src/timer/complementary_pwm.rs +++ b/embassy-stm32/src/timer/complementary_pwm.rs @@ -77,8 +77,6 @@ impl<'d, T: AdvancedInstance4Channel> ComplementaryPwm<'d, T> { this.inner.set_counting_mode(counting_mode); this.set_frequency(freq); - this.inner.start(); - this.inner.enable_outputs(); [Channel::Ch1, Channel::Ch2, Channel::Ch3, Channel::Ch4] @@ -89,6 +87,10 @@ impl<'d, T: AdvancedInstance4Channel> ComplementaryPwm<'d, T> { }); this.inner.set_autoreload_preload(true); + // Generate update event so pre-load registers are written to the shadow registers + this.inner.generate_update_event(); + this.inner.start(); + this } @@ -160,17 +162,15 @@ impl<'d, T: AdvancedInstance4Channel> ComplementaryPwm<'d, T> { /// Set PWM frequency. /// - /// Returns the applied ARR value which can be used to calculate CCR values. - /// /// Note: that the frequency will not be applied in the timer until an update event - /// occurs. Reading the `max_duty` before the update event will return the old value - pub fn set_frequency(&mut self, freq: Hertz) -> u32 { + /// occurs. + pub fn set_frequency(&mut self, freq: Hertz) { let multiplier = if self.inner.get_counting_mode().is_center_aligned() { 2u8 } else { 1u8 }; - self.inner.set_frequency_internal(freq * multiplier, 16) + self.inner.set_frequency_internal(freq * multiplier, 16); } /// Get max duty value. diff --git a/embassy-stm32/src/timer/input_capture.rs b/embassy-stm32/src/timer/input_capture.rs index 2a4ec2db0..9cf0f8c34 100644 --- a/embassy-stm32/src/timer/input_capture.rs +++ b/embassy-stm32/src/timer/input_capture.rs @@ -60,6 +60,7 @@ impl<'d, T: GeneralInstance4Channel> InputCapture<'d, T> { this.inner.set_counting_mode(counting_mode); this.inner.set_tick_freq(freq); this.inner.enable_outputs(); // Required for advanced timers, see GeneralInstance4Channel for details + this.inner.generate_update_event(); this.inner.start(); // enable NVIC interrupt diff --git a/embassy-stm32/src/timer/low_level.rs b/embassy-stm32/src/timer/low_level.rs index f6af8be8c..55e1160ef 100644 --- a/embassy-stm32/src/timer/low_level.rs +++ b/embassy-stm32/src/timer/low_level.rs @@ -272,6 +272,16 @@ impl<'d, T: CoreInstance> Timer<'d, T> { self.regs_core().cr1().modify(|r| r.set_cen(true)); } + /// Generate timer update event from software. + /// + /// Set URS to avoid generating interrupt or DMA request. This update event is only + /// used to load value from pre-load registers. + pub fn generate_update_event(&self) { + self.regs_core().cr1().modify(|r| r.set_urs(vals::Urs::COUNTER_ONLY)); + self.regs_core().egr().write(|r| r.set_ug(true)); + self.regs_core().cr1().modify(|r| r.set_urs(vals::Urs::ANY_EVENT)); + } + /// Stop the timer. pub fn stop(&self) { self.regs_core().cr1().modify(|r| r.set_cen(false)); @@ -293,17 +303,19 @@ impl<'d, T: CoreInstance> Timer<'d, T> { /// the timer counter will wrap around at the same frequency as is being set. /// In center-aligned mode (which not all timers support), the wrap-around frequency is effectively halved /// because it needs to count up and down. - pub fn set_frequency(&self, frequency: Hertz) -> u32 { + pub fn set_frequency(&self, frequency: Hertz) { match T::BITS { - TimerBits::Bits16 => self.set_frequency_internal(frequency, 16), + TimerBits::Bits16 => { + self.set_frequency_internal(frequency, 16); + } #[cfg(not(stm32l0))] - TimerBits::Bits32 => self.set_frequency_internal(frequency, 32), + TimerBits::Bits32 => { + self.set_frequency_internal(frequency, 32); + } } } - /// Calculate ARR based on desired frequency - /// Returns actual value written to the register as u32 - pub(crate) fn set_frequency_internal(&self, frequency: Hertz, max_divide_by_bits: u8) -> u32 { + pub(crate) fn set_frequency_internal(&self, frequency: Hertz, max_divide_by_bits: u8) { let f = frequency.0; assert!(f > 0); let timer_f = T::frequency().0; @@ -320,7 +332,6 @@ impl<'d, T: CoreInstance> Timer<'d, T> { let regs = self.regs_core(); regs.psc().write_value(psc); regs.arr().write(|r| r.set_arr(arr)); - arr as u32 } #[cfg(not(stm32l0))] TimerBits::Bits32 => { @@ -330,7 +341,6 @@ impl<'d, T: CoreInstance> Timer<'d, T> { let regs = self.regs_gp32_unchecked(); regs.psc().write_value(psc); regs.arr().write_value(arr); - arr } } } diff --git a/embassy-stm32/src/timer/pwm_input.rs b/embassy-stm32/src/timer/pwm_input.rs index da8a79b09..057ab011a 100644 --- a/embassy-stm32/src/timer/pwm_input.rs +++ b/embassy-stm32/src/timer/pwm_input.rs @@ -47,6 +47,7 @@ impl<'d, T: GeneralInstance4Channel> PwmInput<'d, T> { inner.set_counting_mode(CountingMode::EdgeAlignedUp); inner.set_tick_freq(freq); inner.enable_outputs(); // Required for advanced timers, see GeneralInstance4Channel for details + inner.generate_update_event(); inner.start(); // Configuration steps from ST RM0390 (STM32F446) chapter 17.3.6 diff --git a/embassy-stm32/src/timer/simple_pwm.rs b/embassy-stm32/src/timer/simple_pwm.rs index 01996c969..58a2e2685 100644 --- a/embassy-stm32/src/timer/simple_pwm.rs +++ b/embassy-stm32/src/timer/simple_pwm.rs @@ -198,7 +198,6 @@ impl<'d, T: GeneralInstance4Channel> SimplePwm<'d, T> { this.inner.set_counting_mode(counting_mode); this.set_frequency(freq); this.inner.enable_outputs(); // Required for advanced timers, see GeneralInstance4Channel for details - this.inner.start(); [Channel::Ch1, Channel::Ch2, Channel::Ch3, Channel::Ch4] .iter() @@ -207,6 +206,11 @@ impl<'d, T: GeneralInstance4Channel> SimplePwm<'d, T> { this.inner.set_output_compare_preload(channel, true); }); + this.inner.set_autoreload_preload(true); + + // Generate update event so pre-load registers are written to the shadow registers + this.inner.generate_update_event(); + this.inner.start(); this } @@ -285,18 +289,16 @@ impl<'d, T: GeneralInstance4Channel> SimplePwm<'d, T> { /// Set PWM frequency. /// - /// Returns the applied ARR value which can be used to calculate CCR values. - /// /// Note: that the frequency will not be applied in the timer until an update event - /// occurs. Reading the `max_duty` before the update event will return the old value - pub fn set_frequency(&mut self, freq: Hertz) -> u32 { + /// occurs. + pub fn set_frequency(&mut self, freq: Hertz) { // TODO: prevent ARR = u16::MAX? let multiplier = if self.inner.get_counting_mode().is_center_aligned() { 2u8 } else { 1u8 }; - self.inner.set_frequency_internal(freq * multiplier, 16) + self.inner.set_frequency_internal(freq * multiplier, 16); } /// Get max duty value. -- cgit From a01e37fbc2800e87eb3b45f1e61f717e48c74127 Mon Sep 17 00:00:00 2001 From: 1-rafael-1 Date: Sat, 15 Nov 2025 20:49:07 +0100 Subject: Switch WiFi example to non-TLS httpbin request --- examples/rp/src/bin/wifi_webrequest.rs | 90 ++++++++++++++++++++++------------ 1 file changed, 59 insertions(+), 31 deletions(-) diff --git a/examples/rp/src/bin/wifi_webrequest.rs b/examples/rp/src/bin/wifi_webrequest.rs index b618d2b38..ce85f4b9a 100644 --- a/examples/rp/src/bin/wifi_webrequest.rs +++ b/examples/rp/src/bin/wifi_webrequest.rs @@ -1,9 +1,8 @@ //! This example uses the RP Pico W board Wifi chip (cyw43). -//! Connects to Wifi network and makes a web request to get the current time. +//! Connects to Wifi network and makes a web request to httpbin.org. #![no_std] #![no_main] -#![allow(async_fn_in_trait)] use core::str::from_utf8; @@ -20,11 +19,14 @@ use embassy_rp::gpio::{Level, Output}; use embassy_rp::peripherals::{DMA_CH0, PIO0}; use embassy_rp::pio::{InterruptHandler, Pio}; use embassy_time::{Duration, Timer}; -use reqwless::client::{HttpClient, TlsConfig, TlsVerify}; +use reqwless::client::HttpClient; +// Uncomment these for TLS requests: +// use reqwless::client::{HttpClient, TlsConfig, TlsVerify}; use reqwless::request::Method; use serde::Deserialize; +use serde_json_core::from_slice; use static_cell::StaticCell; -use {defmt_rtt as _, panic_probe as _, serde_json_core}; +use {defmt_rtt as _, panic_probe as _}; bind_interrupts!(struct Irqs { PIO0_IRQ_0 => InterruptHandler; @@ -119,64 +121,90 @@ async fn main(spawner: Spawner) { // And now we can use it! loop { - let mut rx_buffer = [0; 8192]; - let mut tls_read_buffer = [0; 16640]; - let mut tls_write_buffer = [0; 16640]; + let mut rx_buffer = [0; 4096]; + // Uncomment these for TLS requests: + // let mut tls_read_buffer = [0; 16640]; + // let mut tls_write_buffer = [0; 16640]; - let client_state = TcpClientState::<1, 1024, 1024>::new(); + let client_state = TcpClientState::<1, 4096, 4096>::new(); let tcp_client = TcpClient::new(stack, &client_state); let dns_client = DnsSocket::new(stack); - let tls_config = TlsConfig::new(seed, &mut tls_read_buffer, &mut tls_write_buffer, TlsVerify::None); + // Uncomment these for TLS requests: + // let tls_config = TlsConfig::new(seed, &mut tls_read_buffer, &mut tls_write_buffer, TlsVerify::None); - let mut http_client = HttpClient::new_with_tls(&tcp_client, &dns_client, tls_config); - let url = "https://worldtimeapi.org/api/timezone/Europe/Berlin"; - // for non-TLS requests, use this instead: - // let mut http_client = HttpClient::new(&tcp_client, &dns_client); - // let url = "http://worldtimeapi.org/api/timezone/Europe/Berlin"; + // Using non-TLS HTTP for this example + let mut http_client = HttpClient::new(&tcp_client, &dns_client); + let url = "http://httpbin.org/json"; + // For TLS requests, use this instead: + // let mut http_client = HttpClient::new_with_tls(&tcp_client, &dns_client, tls_config); + // let url = "https://httpbin.org/json"; info!("connecting to {}", &url); - let mut request = match http_client.request(Method::GET, &url).await { + let mut request = match http_client.request(Method::GET, url).await { Ok(req) => req, Err(e) => { error!("Failed to make HTTP request: {:?}", e); - return; // handle the error + Timer::after(Duration::from_secs(5)).await; + continue; } }; let response = match request.send(&mut rx_buffer).await { Ok(resp) => resp, - Err(_e) => { - error!("Failed to send HTTP request"); - return; // handle the error; + Err(e) => { + error!("Failed to send HTTP request: {:?}", e); + Timer::after(Duration::from_secs(5)).await; + continue; } }; - let body = match from_utf8(response.body().read_to_end().await.unwrap()) { + info!("Response status: {}", response.status.0); + + let body_bytes = match response.body().read_to_end().await { Ok(b) => b, Err(_e) => { error!("Failed to read response body"); - return; // handle the error + Timer::after(Duration::from_secs(5)).await; + continue; + } + }; + + let body = match from_utf8(body_bytes) { + Ok(b) => b, + Err(_e) => { + error!("Failed to parse response body as UTF-8"); + Timer::after(Duration::from_secs(5)).await; + continue; } }; - info!("Response body: {:?}", &body); + info!("Response body length: {} bytes", body.len()); - // parse the response body and update the RTC + // Parse the JSON response from httpbin.org/json + #[derive(Deserialize)] + struct SlideShow<'a> { + author: &'a str, + title: &'a str, + } #[derive(Deserialize)] - struct ApiResponse<'a> { - datetime: &'a str, - // other fields as needed + struct HttpBinResponse<'a> { + #[serde(borrow)] + slideshow: SlideShow<'a>, } let bytes = body.as_bytes(); - match serde_json_core::de::from_slice::(bytes) { + match from_slice::(bytes) { Ok((output, _used)) => { - info!("Datetime: {:?}", output.datetime); + info!("Successfully parsed JSON response!"); + info!("Slideshow title: {:?}", output.slideshow.title); + info!("Slideshow author: {:?}", output.slideshow.author); } - Err(_e) => { - error!("Failed to parse response body"); - return; // handle the error + Err(e) => { + error!("Failed to parse JSON response: {}", Debug2Format(&e)); + // Log preview of response for debugging + let preview = if body.len() > 200 { &body[..200] } else { body }; + info!("Response preview: {:?}", preview); } } -- cgit From dd0e6889c791d1d58d86ec9d5951c2232f7bf407 Mon Sep 17 00:00:00 2001 From: xoviat <49173759+xoviat@users.noreply.github.com> Date: Sat, 15 Nov 2025 13:54:17 -0600 Subject: Fix variable name for frequency in ADC prescaler --- embassy-stm32/src/adc/v2.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/embassy-stm32/src/adc/v2.rs b/embassy-stm32/src/adc/v2.rs index b05ab271b..75b1e485b 100644 --- a/embassy-stm32/src/adc/v2.rs +++ b/embassy-stm32/src/adc/v2.rs @@ -58,7 +58,7 @@ fn from_pclk2(freq: Hertz) -> Adcpre { // Datasheet for both F4 and F7 specifies min frequency 0.6 MHz, typ freq. 30 MHz and max 36 MHz. #[cfg(not(stm32f2))] const MAX_FREQUENCY: Hertz = Hertz(36_000_000); - let raw_div = rcc::raw_prescaler(frequency.0, MAX_ADC_CLK_FREQ.0); + let raw_div = rcc::raw_prescaler(freq.0, MAX_ADC_CLK_FREQ.0); match raw_div { 0..=1 => Adcpre::DIV2, 2..=3 => Adcpre::DIV4, -- cgit From 5e0867f5620e0a0e7c93a7f80a20643cb2d87957 Mon Sep 17 00:00:00 2001 From: xoviat <49173759+xoviat@users.noreply.github.com> Date: Sat, 15 Nov 2025 13:58:20 -0600 Subject: Update ADC clock frequency constant usage --- embassy-stm32/src/adc/v2.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/embassy-stm32/src/adc/v2.rs b/embassy-stm32/src/adc/v2.rs index 75b1e485b..341b15674 100644 --- a/embassy-stm32/src/adc/v2.rs +++ b/embassy-stm32/src/adc/v2.rs @@ -58,7 +58,7 @@ fn from_pclk2(freq: Hertz) -> Adcpre { // Datasheet for both F4 and F7 specifies min frequency 0.6 MHz, typ freq. 30 MHz and max 36 MHz. #[cfg(not(stm32f2))] const MAX_FREQUENCY: Hertz = Hertz(36_000_000); - let raw_div = rcc::raw_prescaler(freq.0, MAX_ADC_CLK_FREQ.0); + let raw_div = rcc::raw_prescaler(freq.0, MAX_FREQUENCY.0); match raw_div { 0..=1 => Adcpre::DIV2, 2..=3 => Adcpre::DIV4, -- cgit From 98052fbbeae66e4666d1fa4581550403aa40f295 Mon Sep 17 00:00:00 2001 From: xoviat Date: Sat, 15 Nov 2025 15:04:13 -0600 Subject: timer: add note about disruption --- embassy-stm32/src/timer/low_level.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/embassy-stm32/src/timer/low_level.rs b/embassy-stm32/src/timer/low_level.rs index 439b7f020..f0105ece8 100644 --- a/embassy-stm32/src/timer/low_level.rs +++ b/embassy-stm32/src/timer/low_level.rs @@ -275,7 +275,8 @@ impl<'d, T: CoreInstance> Timer<'d, T> { /// Generate timer update event from software. /// /// Set URS to avoid generating interrupt or DMA request. This update event is only - /// used to load value from pre-load registers. + /// used to load value from pre-load registers. If called when the timer is running, + /// it may disrupt the output waveform. pub fn generate_update_event(&self) { self.regs_core().cr1().modify(|r| r.set_urs(vals::Urs::COUNTER_ONLY)); self.regs_core().egr().write(|r| r.set_ug(true)); -- cgit From 87c1d2159e43b16c95e73633e214cfcb0ab217ab Mon Sep 17 00:00:00 2001 From: xoviat Date: Sat, 15 Nov 2025 15:46:45 -0600 Subject: low_power: optimize --- embassy-stm32/src/low_power.rs | 32 ++++++++----------- embassy-stm32/src/time_driver.rs | 69 +++++++++++++++++----------------------- 2 files changed, 42 insertions(+), 59 deletions(-) diff --git a/embassy-stm32/src/low_power.rs b/embassy-stm32/src/low_power.rs index 696dfe83f..36c7e2242 100644 --- a/embassy-stm32/src/low_power.rs +++ b/embassy-stm32/src/low_power.rs @@ -228,10 +228,13 @@ impl Executor { fn stop_mode(_cs: CriticalSection) -> Option { if unsafe { crate::rcc::REFCOUNT_STOP2 == 0 && crate::rcc::REFCOUNT_STOP1 == 0 } { + trace!("low power: stop 2"); Some(StopMode::Stop2) } else if unsafe { crate::rcc::REFCOUNT_STOP1 == 0 } { + trace!("low power: stop 1"); Some(StopMode::Stop1) } else { + trace!("low power: not ready to stop"); None } } @@ -258,27 +261,18 @@ impl Executor { compiler_fence(Ordering::SeqCst); - let stop_mode = critical_section::with(|cs| Self::stop_mode(cs)); - - if stop_mode.is_none() { - trace!("low power: not ready to stop"); - return; - } - - if get_driver().pause_time().is_err() { - trace!("low power: failed to pause time"); - return; - } + critical_section::with(|cs| { + let stop_mode = Self::stop_mode(cs)?; + let _ = get_driver().pause_time(cs).ok()?; - let stop_mode = stop_mode.unwrap(); - match stop_mode { - StopMode::Stop1 => trace!("low power: stop 1"), - StopMode::Stop2 => trace!("low power: stop 2"), - } - self.configure_stop(stop_mode); + Some(stop_mode) + }) + .map(|stop_mode| { + self.configure_stop(stop_mode); - #[cfg(not(feature = "low-power-debug-with-sleep"))] - self.scb.set_sleepdeep(); + #[cfg(not(feature = "low-power-debug-with-sleep"))] + self.scb.set_sleepdeep(); + }); } /// Run the executor. diff --git a/embassy-stm32/src/time_driver.rs b/embassy-stm32/src/time_driver.rs index bc34892ee..6d93b430a 100644 --- a/embassy-stm32/src/time_driver.rs +++ b/embassy-stm32/src/time_driver.rs @@ -380,13 +380,15 @@ impl RtcDriver { #[cfg(feature = "low-power")] /// Stop the wakeup alarm, if enabled, and add the appropriate offset fn stop_wakeup_alarm(&self, cs: CriticalSection) { - if let Some(offset) = self.rtc.borrow(cs).borrow_mut().as_mut().unwrap().stop_wakeup_alarm(cs) { + if !regs_gp16().cr1().read().cen() + && let Some(offset) = self.rtc.borrow(cs).borrow_mut().as_mut().unwrap().stop_wakeup_alarm(cs) + { self.add_time(offset, cs); } } /* - Low-power public functions: all create or require a critical section + Low-power public functions: all require a critical section */ #[cfg(feature = "low-power")] pub(crate) fn set_min_stop_pause(&self, cs: CriticalSection, min_stop_pause: embassy_time::Duration) { @@ -403,49 +405,36 @@ impl RtcDriver { #[cfg(feature = "low-power")] /// Pause the timer if ready; return err if not - pub(crate) fn pause_time(&self) -> Result<(), ()> { - critical_section::with(|cs| { - /* - If the wakeup timer is currently running, then we need to stop it and - add the elapsed time to the current time, as this will impact the result - of `time_until_next_alarm`. - */ - self.stop_wakeup_alarm(cs); - - let time_until_next_alarm = self.time_until_next_alarm(cs); - if time_until_next_alarm < self.min_stop_pause.borrow(cs).get() { - trace!( - "time_until_next_alarm < self.min_stop_pause ({})", - time_until_next_alarm - ); - Err(()) - } else { - self.rtc - .borrow(cs) - .borrow_mut() - .as_mut() - .unwrap() - .start_wakeup_alarm(time_until_next_alarm, cs); - - regs_gp16().cr1().modify(|w| w.set_cen(false)); - // save the count for the timer as its lost in STOP2 for stm32wlex - #[cfg(stm32wlex)] - self.saved_count - .store(regs_gp16().cnt().read().cnt() as u16, Ordering::SeqCst); - Ok(()) - } - }) + pub(crate) fn pause_time(&self, cs: CriticalSection) -> Result<(), ()> { + self.stop_wakeup_alarm(cs); + + let time_until_next_alarm = self.time_until_next_alarm(cs); + if time_until_next_alarm < self.min_stop_pause.borrow(cs).get() { + trace!( + "time_until_next_alarm < self.min_stop_pause ({})", + time_until_next_alarm + ); + Err(()) + } else { + self.rtc + .borrow(cs) + .borrow_mut() + .as_mut() + .unwrap() + .start_wakeup_alarm(time_until_next_alarm, cs); + + regs_gp16().cr1().modify(|w| w.set_cen(false)); + // save the count for the timer as its lost in STOP2 for stm32wlex + #[cfg(stm32wlex)] + self.saved_count + .store(regs_gp16().cnt().read().cnt() as u16, Ordering::SeqCst); + Ok(()) + } } #[cfg(feature = "low-power")] /// Resume the timer with the given offset pub(crate) fn resume_time(&self, cs: CriticalSection) { - if regs_gp16().cr1().read().cen() { - // Time isn't currently stopped - - return; - } - self.stop_wakeup_alarm(cs); regs_gp16().cr1().modify(|w| w.set_cen(true)); -- cgit From 80a8d7d9798514cd1cfa7f4188c51f25334952dc Mon Sep 17 00:00:00 2001 From: Siarhei B Date: Sun, 16 Nov 2025 01:12:19 +0100 Subject: mspm0: bump mspm0-metapac rev. & add minimath lib as dependency --- embassy-mspm0/Cargo.toml | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/embassy-mspm0/Cargo.toml b/embassy-mspm0/Cargo.toml index b76bc7e41..254e0209b 100644 --- a/embassy-mspm0/Cargo.toml +++ b/embassy-mspm0/Cargo.toml @@ -70,9 +70,10 @@ log = { version = "0.4.14", optional = true } cortex-m-rt = ">=0.6.15,<0.8" cortex-m = "0.7.6" critical-section = "1.2.0" +micromath = "2.0.0" # mspm0-metapac = { version = "" } -mspm0-metapac = { git = "https://github.com/mspm0-rs/mspm0-data-generated/", tag = "mspm0-data-8542f260cc89645a983b7f1a874c87b21822279e" } +mspm0-metapac = { git = "https://github.com/mspm0-rs/mspm0-data-generated/", tag = "mspm0-data-f21b04e9de074af4965bf67ec3646cb9fe1b9852" } [build-dependencies] proc-macro2 = "1.0.94" @@ -80,7 +81,7 @@ quote = "1.0.40" cfg_aliases = "0.2.1" # mspm0-metapac = { version = "", default-features = false, features = ["metadata"] } -mspm0-metapac = { git = "https://github.com/mspm0-rs/mspm0-data-generated/", tag = "mspm0-data-8542f260cc89645a983b7f1a874c87b21822279e", default-features = false, features = ["metadata"] } +mspm0-metapac = { git = "https://github.com/mspm0-rs/mspm0-data-generated/", tag = "mspm0-data-f21b04e9de074af4965bf67ec3646cb9fe1b9852", default-features = false, features = ["metadata"] } [features] default = ["rt"] -- cgit From 535c6d378fb68083623338f8534b5da8efd6a139 Mon Sep 17 00:00:00 2001 From: Siarhei B Date: Sun, 16 Nov 2025 01:15:18 +0100 Subject: mspm0: add MATHACL module and following operations: sin,cos --- embassy-mspm0/build.rs | 1 + embassy-mspm0/src/lib.rs | 1 + embassy-mspm0/src/mathacl.rs | 244 +++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 246 insertions(+) create mode 100644 embassy-mspm0/src/mathacl.rs diff --git a/embassy-mspm0/build.rs b/embassy-mspm0/build.rs index 4942364aa..0fe056c4e 100644 --- a/embassy-mspm0/build.rs +++ b/embassy-mspm0/build.rs @@ -591,6 +591,7 @@ fn generate_peripheral_instances() -> TokenStream { "i2c" => Some(quote! { impl_i2c_instance!(#peri, #fifo_size); }), "wwdt" => Some(quote! { impl_wwdt_instance!(#peri); }), "adc" => Some(quote! { impl_adc_instance!(#peri); }), + "mathacl" => Some(quote! { impl_mathacl_instance!(#peri); }), _ => None, }; diff --git a/embassy-mspm0/src/lib.rs b/embassy-mspm0/src/lib.rs index 9f3e4d5e8..dda8c373c 100644 --- a/embassy-mspm0/src/lib.rs +++ b/embassy-mspm0/src/lib.rs @@ -19,6 +19,7 @@ pub mod dma; pub mod gpio; pub mod i2c; pub mod i2c_target; +pub mod mathacl; pub mod timer; pub mod uart; pub mod wwdt; diff --git a/embassy-mspm0/src/mathacl.rs b/embassy-mspm0/src/mathacl.rs new file mode 100644 index 000000000..59687ce49 --- /dev/null +++ b/embassy-mspm0/src/mathacl.rs @@ -0,0 +1,244 @@ +//! MATHACL +//! +//! This HAL implements mathematical calculations performed by the CPU. + +#![macro_use] + +use embassy_hal_internal::PeripheralType; + +use crate::Peri; +use crate::pac::mathacl::{Mathacl as Regs, vals}; +use micromath::F32Ext; + +pub enum Precision { + High = 31, + Medium = 15, + Low = 1, +} + +/// Serial error +#[derive(Debug, Eq, PartialEq, Copy, Clone)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +#[non_exhaustive] +pub enum Error { + AngleInWrongRange, + NBitsTooBig, +} + +#[derive(Copy, Clone)] +pub struct Mathacl { + regs: &'static Regs, +} + +impl Mathacl { + /// Mathacl initialization. + pub fn new(_instance: Peri) -> Self { + // Init power + T::regs().gprcm(0).rstctl().write(|w| { + w.set_resetstkyclr(vals::Resetstkyclr::CLR); + w.set_resetassert(vals::Resetassert::ASSERT); + w.set_key(vals::ResetKey::KEY); + }); + + // Enable power + T::regs().gprcm(0).pwren().write(|w| { + w.set_enable(true); + w.set_key(vals::PwrenKey::KEY); + }); + + // init delay, 16 cycles + cortex_m::asm::delay(16); + + Self { regs: T::regs() } + } + + /// Internal helper SINCOS function. + fn sincos(&mut self, angle: f32, precision: Precision, sin: bool) -> Result { + self.regs.ctl().write(|w| { + w.set_func(vals::Func::SINCOS); + w.set_numiter(precision as u8); + }); + + if angle > 1.0 || angle < -1.0 { + return Err(Error::AngleInWrongRange); + } + + match signed_f32_to_register(angle, 0) { + Ok(val) => self.regs.op1().write(|w| {w.set_data(val);}), + Err(er) => return Err(er), + }; + + // check if done + while self.regs.status().read().busy() == vals::Busy::NOTDONE {} + + match sin { + true => register_to_signed_f32(self.regs.res2().read().data(), 0), + false => register_to_signed_f32(self.regs.res1().read().data(), 0), + } + } + + /// Calsulates trigonometric sine operation in the range [-1,1) with a give precision. + pub fn sin(&mut self, angle: f32, precision: Precision) -> Result { + self.sincos(angle, precision, true) + } + + /// Calsulates trigonometric cosine operation in the range [-1,1) with a give precision. + pub fn cos(&mut self, angle: f32, precision: Precision) -> Result { + self.sincos(angle, precision, false) + } +} + +pub(crate) trait SealedInstance { + fn regs() -> &'static Regs; +} + +/// Mathacl instance trait +#[allow(private_bounds)] +pub trait Instance: SealedInstance + PeripheralType {} + +macro_rules! impl_mathacl_instance { + ($instance: ident) => { + impl crate::mathacl::SealedInstance for crate::peripherals::$instance { + fn regs() -> &'static crate::pac::mathacl::Mathacl { + &crate::pac::$instance + } + } + + impl crate::mathacl::Instance for crate::peripherals::$instance {} + }; +} + +/// Convert f32 data to understandable by M0 format. +fn signed_f32_to_register(data: f32, n_bits: u8) -> Result { + let mut res: u32 = 0; + // check if negative + let negative = data < 0.0; + + // absolute value for extraction + let abs = data.abs(); + + // total integer bit count + let total_bits = 31; + + // Validate n_bits + if n_bits > 31 { + return Err(Error::NBitsTooBig); + } + + // number of fractional bits + let shift = total_bits - n_bits; + + // Compute masks + let (n_mask, m_mask) = if n_bits == 0 { + (0, 0x7FFFFFFF) + } else if n_bits == 31 { + (0x7FFFFFFF, 0) + } else { + ((1u32 << n_bits) - 1, (1u32 << shift) - 1) + }; + + // calc. integer(n) & fractional(m) parts + let n = abs.floor() as u32; + let mut m = ((abs - abs.floor()) * (1u32 << shift) as f32).round() as u32; + + // Handle trimming integer part + if n_bits == 0 && n > 0 { + m = 0x7FFFFFFF; + } + + // calculate result + if n_bits > 0 { + res = n << shift & n_mask; + } + if shift > 0 { + res = res | m & m_mask; + } + + // if negative, do 2’s compliment + if negative { + res = !res + 1; + } + Ok(res) +} + +/// Reversely converts M0-register format to native f32. +fn register_to_signed_f32(data: u32, n_bits: u8) -> Result { + // Validate n_bits + if n_bits > 31 { + return Err(Error::NBitsTooBig); + } + + // total integer bit count + let total_bits = 31; + + let negative = (data >> 31) == 1; + + // number of fractional bits + let shift = total_bits - n_bits; + + // Compute masks + let (n_mask, m_mask) = if n_bits == 0 { + (0, 0x7FFFFFFF) + } else if n_bits == 31 { + (0x7FFFFFFF, 0) + } else { + ((1u32 << n_bits) - 1, (1u32 << shift) - 1) + }; + + // Compute n and m + let mut n = if n_bits == 0 { + 0 + } else if shift >= 32 { + data & n_mask + } else { + (data >> shift) & n_mask + }; + let mut m = data & m_mask; + + // if negative, do 2’s compliment + if negative { + n = !n & n_mask; + m = (!m & m_mask) + 1; + } + + let mut value = (n as f32) + (m as f32) / (1u32 << shift) as f32; + if negative { + value = -value; + } + return Ok(value); +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn mathacl_convert_func_errors() { + assert_eq!(signed_f32_to_register(0.0, 32), Err(Error::NBitsTooBig)); + assert_eq!(register_to_signed_f32(0, 32), Err(Error::NBitsTooBig)); + } + + #[test] + fn mathacl_signed_f32_to_register() { + let mut test_float = 1.0; + assert_eq!(signed_f32_to_register(test_float, 0).unwrap(), 0x7FFFFFFF); + + test_float = 0.0; + assert_eq!(signed_f32_to_register(test_float, 0).unwrap(), 0x0); + + test_float = -1.0; + assert_eq!(signed_f32_to_register(test_float, 0).unwrap(), 0x80000001); + } + + #[test] + fn mathacl_register_to_signed_f32() { + let mut test_u32: u32 = 0x7FFFFFFF; + assert_eq!(register_to_signed_f32(test_u32, 0u8).unwrap(), 1.0); + + test_u32 = 0x0; + assert_eq!(register_to_signed_f32(test_u32, 0u8).unwrap(), 0.0); + + test_u32 = 0x80000001; + assert_eq!(register_to_signed_f32(test_u32, 0u8).unwrap(), -1.0); + } +} -- cgit From 5729b653ae5a95fe1af131dcbceb9dbb0397f4c6 Mon Sep 17 00:00:00 2001 From: Siarhei B Date: Sun, 16 Nov 2025 01:16:30 +0100 Subject: mspm0: add example of MATHACL based & tested on mspm0g3507 --- examples/mspm0g3507/src/bin/mathacl_ops.rs | 37 ++++++++++++++++++++++++++++++ 1 file changed, 37 insertions(+) create mode 100644 examples/mspm0g3507/src/bin/mathacl_ops.rs diff --git a/examples/mspm0g3507/src/bin/mathacl_ops.rs b/examples/mspm0g3507/src/bin/mathacl_ops.rs new file mode 100644 index 000000000..429cc5ad6 --- /dev/null +++ b/examples/mspm0g3507/src/bin/mathacl_ops.rs @@ -0,0 +1,37 @@ +//! Example of using mathematical calculations performed by the MSPM0G3507 chip. +//! +//! It prints the result of basics trigonometric calculation. + +#![no_std] +#![no_main] + +use defmt::*; +use embassy_executor::Spawner; +use embassy_mspm0::mathacl::{Mathacl, Precision}; +use embassy_time::Timer; +use {defmt_rtt as _, panic_halt as _}; + +#[embassy_executor::main] +async fn main(_spawner: Spawner) -> ! { + info!("Hello world!"); + + let d = embassy_mspm0::init(Default::default()); + + let mut macl = Mathacl::new(d.MATHACL); + + // in range [-1,1) + let angle = 0.5; + match macl.sin(angle, Precision::High) { + Ok(res) => info!("sin({}) = {}", angle*180.0, res), + Err(e) => error!("sin Error: {:?}", e), + } + + match macl.cos(angle, Precision::Medium) { + Ok(res) => info!("cos({}) = {}", angle*180.0, res), + Err(e) => error!("cos Error: {:?}", e), + } + + loop { + Timer::after_millis(500).await; + } +} -- cgit From 0e0253b67100b6ffbaad4f1307f671e9ddec3901 Mon Sep 17 00:00:00 2001 From: Siarhei B Date: Sun, 16 Nov 2025 01:24:21 +0100 Subject: mspm0: added PR #4897 description to CHANGELOG --- embassy-mspm0/CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/embassy-mspm0/CHANGELOG.md b/embassy-mspm0/CHANGELOG.md index f0b5868f4..6972a8472 100644 --- a/embassy-mspm0/CHANGELOG.md +++ b/embassy-mspm0/CHANGELOG.md @@ -19,3 +19,4 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - feat: Add i2c target implementation (#4605) - fix: group irq handlers must check for NO_INTR (#4785) - feat: Add read_reset_cause function +- feat: Add module Mathacl & example for mspm0g3507 (#4897) \ No newline at end of file -- cgit From f236bb49301e3e726d60e0af8f6b998083b6215e Mon Sep 17 00:00:00 2001 From: Siarhei B Date: Sun, 16 Nov 2025 01:29:53 +0100 Subject: mspm0: apply formatting for new Mathacl & example --- embassy-mspm0/src/mathacl.rs | 10 ++++++---- examples/mspm0g3507/src/bin/mathacl_ops.rs | 4 ++-- 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/embassy-mspm0/src/mathacl.rs b/embassy-mspm0/src/mathacl.rs index 59687ce49..1b1c67a71 100644 --- a/embassy-mspm0/src/mathacl.rs +++ b/embassy-mspm0/src/mathacl.rs @@ -5,10 +5,10 @@ #![macro_use] use embassy_hal_internal::PeripheralType; +use micromath::F32Ext; use crate::Peri; use crate::pac::mathacl::{Mathacl as Regs, vals}; -use micromath::F32Ext; pub enum Precision { High = 31, @@ -64,7 +64,9 @@ impl Mathacl { } match signed_f32_to_register(angle, 0) { - Ok(val) => self.regs.op1().write(|w| {w.set_data(val);}), + Ok(val) => self.regs.op1().write(|w| { + w.set_data(val); + }), Err(er) => return Err(er), }; @@ -142,7 +144,7 @@ fn signed_f32_to_register(data: f32, n_bits: u8) -> Result { let mut m = ((abs - abs.floor()) * (1u32 << shift) as f32).round() as u32; // Handle trimming integer part - if n_bits == 0 && n > 0 { + if n_bits == 0 && n > 0 { m = 0x7FFFFFFF; } @@ -189,7 +191,7 @@ fn register_to_signed_f32(data: u32, n_bits: u8) -> Result { let mut n = if n_bits == 0 { 0 } else if shift >= 32 { - data & n_mask + data & n_mask } else { (data >> shift) & n_mask }; diff --git a/examples/mspm0g3507/src/bin/mathacl_ops.rs b/examples/mspm0g3507/src/bin/mathacl_ops.rs index 429cc5ad6..06265ae18 100644 --- a/examples/mspm0g3507/src/bin/mathacl_ops.rs +++ b/examples/mspm0g3507/src/bin/mathacl_ops.rs @@ -22,12 +22,12 @@ async fn main(_spawner: Spawner) -> ! { // in range [-1,1) let angle = 0.5; match macl.sin(angle, Precision::High) { - Ok(res) => info!("sin({}) = {}", angle*180.0, res), + Ok(res) => info!("sin({}) = {}", angle * 180.0, res), Err(e) => error!("sin Error: {:?}", e), } match macl.cos(angle, Precision::Medium) { - Ok(res) => info!("cos({}) = {}", angle*180.0, res), + Ok(res) => info!("cos({}) = {}", angle * 180.0, res), Err(e) => error!("cos Error: {:?}", e), } -- cgit From d34dd3006dbcaff198c4e72469b5598dc3a8faa0 Mon Sep 17 00:00:00 2001 From: Siarhei B Date: Sun, 16 Nov 2025 11:59:57 +0100 Subject: mspm0-mathacl: switch to radians as input param for sincos operations --- embassy-mspm0/src/mathacl.rs | 22 +++++++++++++--------- examples/mspm0g3507/src/bin/mathacl_ops.rs | 13 +++++++------ 2 files changed, 20 insertions(+), 15 deletions(-) diff --git a/embassy-mspm0/src/mathacl.rs b/embassy-mspm0/src/mathacl.rs index 1b1c67a71..60e8cc5ed 100644 --- a/embassy-mspm0/src/mathacl.rs +++ b/embassy-mspm0/src/mathacl.rs @@ -4,6 +4,7 @@ #![macro_use] +use core::f32::consts::PI; use embassy_hal_internal::PeripheralType; use micromath::F32Ext; @@ -21,7 +22,7 @@ pub enum Precision { #[cfg_attr(feature = "defmt", derive(defmt::Format))] #[non_exhaustive] pub enum Error { - AngleInWrongRange, + ValueInWrongRange, NBitsTooBig, } @@ -53,17 +54,20 @@ impl Mathacl { } /// Internal helper SINCOS function. - fn sincos(&mut self, angle: f32, precision: Precision, sin: bool) -> Result { + fn sincos(&mut self, rad: f32, precision: Precision, sin: bool) -> Result { self.regs.ctl().write(|w| { w.set_func(vals::Func::SINCOS); w.set_numiter(precision as u8); }); - if angle > 1.0 || angle < -1.0 { - return Err(Error::AngleInWrongRange); + if rad > PI || rad < -PI { + return Err(Error::ValueInWrongRange); } - match signed_f32_to_register(angle, 0) { + // TODO: make f32 division on CPU + let native = rad / PI; + + match signed_f32_to_register(native, 0) { Ok(val) => self.regs.op1().write(|w| { w.set_data(val); }), @@ -80,13 +84,13 @@ impl Mathacl { } /// Calsulates trigonometric sine operation in the range [-1,1) with a give precision. - pub fn sin(&mut self, angle: f32, precision: Precision) -> Result { - self.sincos(angle, precision, true) + pub fn sin(&mut self, rad: f32, precision: Precision) -> Result { + self.sincos(rad, precision, true) } /// Calsulates trigonometric cosine operation in the range [-1,1) with a give precision. - pub fn cos(&mut self, angle: f32, precision: Precision) -> Result { - self.sincos(angle, precision, false) + pub fn cos(&mut self, rad: f32, precision: Precision) -> Result { + self.sincos(rad, precision, false) } } diff --git a/examples/mspm0g3507/src/bin/mathacl_ops.rs b/examples/mspm0g3507/src/bin/mathacl_ops.rs index 06265ae18..aeca96d2b 100644 --- a/examples/mspm0g3507/src/bin/mathacl_ops.rs +++ b/examples/mspm0g3507/src/bin/mathacl_ops.rs @@ -5,6 +5,7 @@ #![no_std] #![no_main] +use core::f32::consts::PI; use defmt::*; use embassy_executor::Spawner; use embassy_mspm0::mathacl::{Mathacl, Precision}; @@ -19,15 +20,15 @@ async fn main(_spawner: Spawner) -> ! { let mut macl = Mathacl::new(d.MATHACL); - // in range [-1,1) - let angle = 0.5; - match macl.sin(angle, Precision::High) { - Ok(res) => info!("sin({}) = {}", angle * 180.0, res), + // value radians [-PI; PI] + let rads = PI * 0.5; + match macl.sin(rads, Precision::High) { + Ok(res) => info!("sin({}) = {}", rads, res), Err(e) => error!("sin Error: {:?}", e), } - match macl.cos(angle, Precision::Medium) { - Ok(res) => info!("cos({}) = {}", angle * 180.0, res), + match macl.cos(rads, Precision::Medium) { + Ok(res) => info!("cos({}) = {}", rads, res), Err(e) => error!("cos Error: {:?}", e), } -- cgit From a3fdfb6db4f9ae79e34875a06325cf9776ce3f8c Mon Sep 17 00:00:00 2001 From: Siarhei B Date: Sun, 16 Nov 2025 12:53:36 +0100 Subject: mspm0-mathacl: add phantomdata for module --- embassy-mspm0/src/mathacl.rs | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/embassy-mspm0/src/mathacl.rs b/embassy-mspm0/src/mathacl.rs index 60e8cc5ed..a28c621c2 100644 --- a/embassy-mspm0/src/mathacl.rs +++ b/embassy-mspm0/src/mathacl.rs @@ -5,6 +5,7 @@ #![macro_use] use core::f32::consts::PI; +use core::marker::PhantomData; use embassy_hal_internal::PeripheralType; use micromath::F32Ext; @@ -26,14 +27,15 @@ pub enum Error { NBitsTooBig, } -#[derive(Copy, Clone)] -pub struct Mathacl { +pub struct Mathacl<'d, T: Instance> { + _peri: Peri<'d, T>, regs: &'static Regs, + _phantom: PhantomData, } -impl Mathacl { +impl<'d, T: Instance> Mathacl<'d, T> { /// Mathacl initialization. - pub fn new(_instance: Peri) -> Self { + pub fn new(instance: Peri<'d, T>) -> Self { // Init power T::regs().gprcm(0).rstctl().write(|w| { w.set_resetstkyclr(vals::Resetstkyclr::CLR); @@ -50,7 +52,11 @@ impl Mathacl { // init delay, 16 cycles cortex_m::asm::delay(16); - Self { regs: T::regs() } + Self { + _peri: instance, + regs: T::regs(), + _phantom: PhantomData, + } } /// Internal helper SINCOS function. -- cgit From 5539288b9e591ff0aa8d91d635bfd7f009d24cb1 Mon Sep 17 00:00:00 2001 From: Siarhei B Date: Sun, 16 Nov 2025 13:07:37 +0100 Subject: mspm0-mathacl: another round of format --- embassy-mspm0/src/mathacl.rs | 1 + examples/mspm0g3507/src/bin/mathacl_ops.rs | 1 + 2 files changed, 2 insertions(+) diff --git a/embassy-mspm0/src/mathacl.rs b/embassy-mspm0/src/mathacl.rs index a28c621c2..78646c90f 100644 --- a/embassy-mspm0/src/mathacl.rs +++ b/embassy-mspm0/src/mathacl.rs @@ -6,6 +6,7 @@ use core::f32::consts::PI; use core::marker::PhantomData; + use embassy_hal_internal::PeripheralType; use micromath::F32Ext; diff --git a/examples/mspm0g3507/src/bin/mathacl_ops.rs b/examples/mspm0g3507/src/bin/mathacl_ops.rs index aeca96d2b..25d74b29b 100644 --- a/examples/mspm0g3507/src/bin/mathacl_ops.rs +++ b/examples/mspm0g3507/src/bin/mathacl_ops.rs @@ -6,6 +6,7 @@ #![no_main] use core::f32::consts::PI; + use defmt::*; use embassy_executor::Spawner; use embassy_mspm0::mathacl::{Mathacl, Precision}; -- cgit From 29d4ade2866e6c8d2114b393853354ded1e61db7 Mon Sep 17 00:00:00 2001 From: xoviat Date: Sun, 16 Nov 2025 07:50:49 -0600 Subject: low_power: misc cleanups and allow main macro --- embassy-stm32/CHANGELOG.md | 1 + embassy-stm32/src/lib.rs | 5 +- embassy-stm32/src/low_power.rs | 88 ++++++++++++++++--------------- embassy-stm32/src/rcc/l.rs | 43 --------------- embassy-stm32/src/rcc/mod.rs | 4 ++ embassy-stm32/src/rtc/mod.rs | 7 ++- embassy-stm32/src/time_driver.rs | 7 +-- examples/stm32h5/src/bin/stop.rs | 12 +---- examples/stm32l5/src/bin/stop.rs | 12 +---- examples/stm32wle5/src/bin/adc.rs | 12 +---- examples/stm32wle5/src/bin/blinky.rs | 12 +---- examples/stm32wle5/src/bin/button_exti.rs | 12 +---- examples/stm32wle5/src/bin/i2c.rs | 13 +---- tests/stm32/src/bin/stop.rs | 14 ++--- 14 files changed, 72 insertions(+), 170 deletions(-) diff --git a/embassy-stm32/CHANGELOG.md b/embassy-stm32/CHANGELOG.md index df832d15f..8e3e802a4 100644 --- a/embassy-stm32/CHANGELOG.md +++ b/embassy-stm32/CHANGELOG.md @@ -7,6 +7,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## Unreleased - ReleaseDate +- feat: allow embassy_executor::main for low power - feat: Add waveform methods to ComplementaryPwm - fix: Avoid generating timer update events when updating the frequency ([#4890](https://github.com/embassy-rs/embassy/pull/4890)) - chore: cleanup low-power add time diff --git a/embassy-stm32/src/lib.rs b/embassy-stm32/src/lib.rs index 6e492946a..7c3770643 100644 --- a/embassy-stm32/src/lib.rs +++ b/embassy-stm32/src/lib.rs @@ -649,10 +649,7 @@ fn init_hw(config: Config) -> Peripherals { rcc::init_rcc(cs, config.rcc); #[cfg(feature = "low-power")] - crate::rtc::init_rtc(cs, config.rtc); - - #[cfg(feature = "low-power")] - crate::time_driver::get_driver().set_min_stop_pause(cs, config.min_stop_pause); + rtc::init_rtc(cs, config.rtc, config.min_stop_pause); } p diff --git a/embassy-stm32/src/low_power.rs b/embassy-stm32/src/low_power.rs index 36c7e2242..cf8f2b393 100644 --- a/embassy-stm32/src/low_power.rs +++ b/embassy-stm32/src/low_power.rs @@ -14,7 +14,7 @@ //! //! 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. +//! [`time_driver::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 @@ -22,21 +22,16 @@ //! //! ```rust,no_run //! use embassy_executor::Spawner; -//! use embassy_stm32::low_power::Executor; +//! use embassy_stm32::low_power; //! use embassy_stm32::rtc::{Rtc, RtcConfig}; -//! use static_cell::StaticCell; +//! use embassy_time::Duration; //! -//! #[cortex_m_rt::entry] -//! fn main() -> ! { -//! Executor::take().run(|spawner| { -//! spawner.spawn(unwrap!(async_main(spawner))); -//! }); -//! } -//! -//! #[embassy_executor::task] +//! #[embassy_executor::main(executor = "low_power::Executor")] //! async fn async_main(spawner: Spawner) { //! // initialize the platform... //! let mut config = embassy_stm32::Config::default(); +//! // the default value, but can be adjusted +//! config.min_stop_pause = Duration::from_millis(250); //! // when enabled the power-consumption is much higher during stop, but debugging and RTT is working //! config.enable_debug_during_sleep = false; //! let p = embassy_stm32::init(config); @@ -45,11 +40,9 @@ //! } //! ``` -// TODO: Usage of `static mut` here is unsound. Fix then remove this `allow`.` -#![allow(static_mut_refs)] - use core::arch::asm; use core::marker::PhantomData; +use core::mem; use core::sync::atomic::{Ordering, compiler_fence}; use cortex_m::peripheral::SCB; @@ -57,11 +50,12 @@ use critical_section::CriticalSection; use embassy_executor::*; use crate::interrupt; +use crate::rcc::{REFCOUNT_STOP1, REFCOUNT_STOP2}; use crate::time_driver::get_driver; const THREAD_PENDER: usize = usize::MAX; -static mut EXECUTOR: Option = None; +static mut EXECUTOR_TAKEN: bool = false; /// Prevent the device from going into the stop mode if held pub struct DeviceBusy(StopMode); @@ -182,42 +176,47 @@ impl Into for StopMode { pub struct Executor { inner: raw::Executor, not_send: PhantomData<*mut ()>, - scb: SCB, } impl Executor { /// Create a new Executor. - pub fn take() -> &'static mut Self { - critical_section::with(|_| unsafe { - assert!(EXECUTOR.is_none()); - - EXECUTOR = Some(Self { - inner: raw::Executor::new(THREAD_PENDER as *mut ()), - not_send: PhantomData, - scb: cortex_m::Peripherals::steal().SCB, - }); - - let executor = EXECUTOR.as_mut().unwrap(); + pub fn new() -> Self { + unsafe { + if EXECUTOR_TAKEN { + panic!("Low power executor can only be taken once."); + } else { + EXECUTOR_TAKEN = true; + } + } - executor - }) + Self { + inner: raw::Executor::new(THREAD_PENDER as *mut ()), + not_send: PhantomData, + } } pub(crate) unsafe fn on_wakeup_irq() { critical_section::with(|cs| { #[cfg(stm32wlex)] { - let extscr = crate::pac::PWR.extscr().read(); + use crate::pac::rcc::vals::Sw; + use crate::pac::{PWR, RCC}; + use crate::rcc::{RCC_CONFIG, init as init_rcc}; + + let extscr = PWR.extscr().read(); if extscr.c1stop2f() || extscr.c1stopf() { // when we wake from any stop mode we need to re-initialize the rcc - crate::rcc::apply_resume_config(); + while RCC.cfgr().read().sws() != Sw::MSI {} + + init_rcc(RCC_CONFIG.unwrap()); + if extscr.c1stop2f() { // when we wake from STOP2, we need to re-initialize the time driver - crate::time_driver::init_timer(cs); + get_driver().init_timer(cs); // reset the refcounts for STOP2 and STOP1 (initializing the time driver will increment one of them for the timer) // and given that we just woke from STOP2, we can reset them - crate::rcc::REFCOUNT_STOP2 = 0; - crate::rcc::REFCOUNT_STOP1 = 0; + REFCOUNT_STOP2 = 0; + REFCOUNT_STOP1 = 0; } } } @@ -226,11 +225,15 @@ impl Executor { }); } + const fn get_scb() -> SCB { + unsafe { mem::transmute(()) } + } + fn stop_mode(_cs: CriticalSection) -> Option { - if unsafe { crate::rcc::REFCOUNT_STOP2 == 0 && crate::rcc::REFCOUNT_STOP1 == 0 } { + if unsafe { REFCOUNT_STOP2 == 0 && REFCOUNT_STOP1 == 0 } { trace!("low power: stop 2"); Some(StopMode::Stop2) - } else if unsafe { crate::rcc::REFCOUNT_STOP1 == 0 } { + } else if unsafe { REFCOUNT_STOP1 == 0 } { trace!("low power: stop 1"); Some(StopMode::Stop1) } else { @@ -240,7 +243,7 @@ impl Executor { } #[allow(unused_variables)] - fn configure_stop(&mut self, stop_mode: StopMode) { + fn configure_stop(&self, stop_mode: StopMode) { #[cfg(any(stm32l4, stm32l5, stm32u5, stm32u0, stm32wba, stm32wlex))] crate::pac::PWR.cr1().modify(|m| m.set_lpms(stop_mode.into())); #[cfg(stm32h5)] @@ -251,8 +254,8 @@ impl Executor { }); } - fn configure_pwr(&mut self) { - self.scb.clear_sleepdeep(); + fn configure_pwr(&self) { + Self::get_scb().clear_sleepdeep(); // Clear any previous stop flags #[cfg(stm32wlex)] crate::pac::PWR.extscr().modify(|w| { @@ -271,7 +274,7 @@ impl Executor { self.configure_stop(stop_mode); #[cfg(not(feature = "low-power-debug-with-sleep"))] - self.scb.set_sleepdeep(); + Self::get_scb().set_sleepdeep(); }); } @@ -294,12 +297,11 @@ impl Executor { /// /// This function never returns. pub fn run(&'static mut self, init: impl FnOnce(Spawner)) -> ! { - let executor = unsafe { EXECUTOR.as_mut().unwrap() }; - init(executor.inner.spawner()); + init(self.inner.spawner()); loop { unsafe { - executor.inner.poll(); + self.inner.poll(); self.configure_pwr(); asm!("wfe"); #[cfg(stm32wlex)] diff --git a/embassy-stm32/src/rcc/l.rs b/embassy-stm32/src/rcc/l.rs index 584957c6d..2e1cbd702 100644 --- a/embassy-stm32/src/rcc/l.rs +++ b/embassy-stm32/src/rcc/l.rs @@ -1,6 +1,3 @@ -#[cfg(all(feature = "low-power", stm32wlex))] -use core::mem::MaybeUninit; - #[cfg(any(stm32l0, stm32l1))] pub use crate::pac::pwr::vals::Vos as VoltageScale; use crate::pac::rcc::regs::Cfgr; @@ -14,42 +11,6 @@ use crate::time::Hertz; /// HSI speed pub const HSI_FREQ: Hertz = Hertz(16_000_000); -/// Saved RCC Config -/// -/// Used when exiting STOP2 to re-enable clocks to their last configured state -/// for chips that need it. -#[cfg(all(feature = "low-power", stm32wlex))] -static mut RESUME_RCC_CONFIG: MaybeUninit = MaybeUninit::uninit(); - -/// Set the rcc config to be restored when exiting STOP2 -/// -/// Safety: Sets a mutable global. -#[cfg(all(feature = "low-power", stm32wlex))] -pub(crate) unsafe fn set_resume_config(config: Config) { - trace!("rcc set_resume_config()"); - RESUME_RCC_CONFIG = MaybeUninit::new(config); -} - -/// Get the rcc config to be restored when exiting STOP2 -/// -/// Safety: Reads a mutable global. -#[cfg(all(feature = "low-power", stm32wlex))] -pub(crate) unsafe fn get_resume_config() -> Config { - *(*core::ptr::addr_of_mut!(RESUME_RCC_CONFIG)).assume_init_ref() -} - -#[cfg(all(feature = "low-power", stm32wlex))] -/// Safety: should only be called from low power executable just after resuming from STOP2 -pub(crate) unsafe fn apply_resume_config() { - trace!("rcc apply_resume_config()"); - - while RCC.cfgr().read().sws() != Sysclk::MSI {} - - let config = get_resume_config(); - - init(config); -} - #[derive(Clone, Copy, Eq, PartialEq)] pub enum HseMode { /// crystal/ceramic oscillator (HSEBYP=0) @@ -193,10 +154,6 @@ fn msi_enable(range: MSIRange) { } pub(crate) unsafe fn init(config: Config) { - // save the rcc config because if we enter stop 2 we need to re-apply it on wakeup - #[cfg(all(feature = "low-power", stm32wlex))] - set_resume_config(config); - // Switch to MSI to prevent problems with PLL configuration. if !RCC.cr().read().msion() { // Turn on MSI and configure it to 4MHz. diff --git a/embassy-stm32/src/rcc/mod.rs b/embassy-stm32/src/rcc/mod.rs index ca7c28cbc..66ee06e17 100644 --- a/embassy-stm32/src/rcc/mod.rs +++ b/embassy-stm32/src/rcc/mod.rs @@ -49,6 +49,9 @@ pub(crate) static mut REFCOUNT_STOP1: u32 = 0; /// May be read without a critical section pub(crate) static mut REFCOUNT_STOP2: u32 = 0; +#[cfg(feature = "low-power")] +pub(crate) static mut RCC_CONFIG: Option = None; + #[cfg(backup_sram)] pub(crate) static mut BKSRAM_RETAINED: bool = false; @@ -408,6 +411,7 @@ pub(crate) fn init_rcc(_cs: CriticalSection, config: Config) { #[cfg(feature = "low-power")] { + RCC_CONFIG = Some(config); REFCOUNT_STOP2 = 0; REFCOUNT_STOP1 = 0; } diff --git a/embassy-stm32/src/rtc/mod.rs b/embassy-stm32/src/rtc/mod.rs index 116b3c7ed..e88bd7ab2 100644 --- a/embassy-stm32/src/rtc/mod.rs +++ b/embassy-stm32/src/rtc/mod.rs @@ -379,13 +379,16 @@ trait SealedInstance { } #[cfg(feature = "low-power")] -pub(crate) fn init_rtc(cs: CriticalSection, config: RtcConfig) { +pub(crate) fn init_rtc(cs: CriticalSection, config: RtcConfig, min_stop_pause: embassy_time::Duration) { + use crate::time_driver::get_driver; + #[cfg(feature = "_allow-disable-rtc")] if config._disable_rtc { return; } - crate::time_driver::get_driver().set_rtc(cs, Rtc::new_inner(config)); + get_driver().set_rtc(cs, Rtc::new_inner(config)); + get_driver().set_min_stop_pause(cs, min_stop_pause); trace!("low power: stop with rtc configured"); } diff --git a/embassy-stm32/src/time_driver.rs b/embassy-stm32/src/time_driver.rs index 6d93b430a..0b75aef92 100644 --- a/embassy-stm32/src/time_driver.rs +++ b/embassy-stm32/src/time_driver.rs @@ -245,7 +245,7 @@ embassy_time_driver::time_driver_impl!(static DRIVER: RtcDriver = RtcDriver { impl RtcDriver { /// initialize the timer, but don't start it. Used for chips like stm32wle5 /// for low power where the timer config is lost in STOP2. - fn init_timer(&'static self, cs: critical_section::CriticalSection) { + pub(crate) fn init_timer(&'static self, cs: critical_section::CriticalSection) { let r = regs_gp16(); rcc::enable_and_reset_with_cs::(cs); @@ -516,8 +516,3 @@ pub(crate) const fn get_driver() -> &'static RtcDriver { pub(crate) fn init(cs: CriticalSection) { DRIVER.init(cs) } - -#[cfg(all(feature = "low-power", stm32wlex))] -pub(crate) fn init_timer(cs: CriticalSection) { - DRIVER.init_timer(cs) -} diff --git a/examples/stm32h5/src/bin/stop.rs b/examples/stm32h5/src/bin/stop.rs index caebc9daf..8d5456b80 100644 --- a/examples/stm32h5/src/bin/stop.rs +++ b/examples/stm32h5/src/bin/stop.rs @@ -7,20 +7,12 @@ use defmt::*; use embassy_executor::Spawner; use embassy_stm32::gpio::{AnyPin, Level, Output, Speed}; -use embassy_stm32::low_power::Executor; use embassy_stm32::rcc::{HSIPrescaler, LsConfig}; -use embassy_stm32::{Config, Peri}; +use embassy_stm32::{Config, Peri, low_power}; use embassy_time::Timer; use {defmt_rtt as _, panic_probe as _}; -#[cortex_m_rt::entry] -fn main() -> ! { - Executor::take().run(|spawner| { - spawner.spawn(unwrap!(async_main(spawner))); - }) -} - -#[embassy_executor::task] +#[embassy_executor::main(executor = "low_power::Executor")] async fn async_main(spawner: Spawner) { defmt::info!("Program Start"); diff --git a/examples/stm32l5/src/bin/stop.rs b/examples/stm32l5/src/bin/stop.rs index 3d119f90f..fde804fb7 100644 --- a/examples/stm32l5/src/bin/stop.rs +++ b/examples/stm32l5/src/bin/stop.rs @@ -4,20 +4,12 @@ use defmt::*; use embassy_executor::Spawner; use embassy_stm32::gpio::{AnyPin, Level, Output, Speed}; -use embassy_stm32::low_power::Executor; use embassy_stm32::rcc::LsConfig; -use embassy_stm32::{Config, Peri}; +use embassy_stm32::{Config, Peri, low_power}; use embassy_time::Timer; use {defmt_rtt as _, panic_probe as _}; -#[cortex_m_rt::entry] -fn main() -> ! { - Executor::take().run(|spawner| { - spawner.spawn(unwrap!(async_main(spawner))); - }) -} - -#[embassy_executor::task] +#[embassy_executor::main(executor = "low_power::Executor")] async fn async_main(spawner: Spawner) { let mut config = Config::default(); config.rcc.ls = LsConfig::default_lsi(); diff --git a/examples/stm32wle5/src/bin/adc.rs b/examples/stm32wle5/src/bin/adc.rs index 4e0574d97..ea91fb063 100644 --- a/examples/stm32wle5/src/bin/adc.rs +++ b/examples/stm32wle5/src/bin/adc.rs @@ -6,20 +6,12 @@ use defmt::*; use defmt_rtt as _; use embassy_executor::Spawner; use embassy_stm32::adc::{Adc, SampleTime}; -use embassy_stm32::low_power::Executor; +use embassy_stm32::low_power; use embassy_time::Timer; use panic_probe as _; use static_cell::StaticCell; -#[cortex_m_rt::entry] -fn main() -> ! { - info!("main: Starting!"); - Executor::take().run(|spawner| { - spawner.spawn(unwrap!(async_main(spawner))); - }); -} - -#[embassy_executor::task] +#[embassy_executor::main(executor = "low_power::Executor")] async fn async_main(_spawner: Spawner) { let mut config = embassy_stm32::Config::default(); // enable HSI clock diff --git a/examples/stm32wle5/src/bin/blinky.rs b/examples/stm32wle5/src/bin/blinky.rs index b2745fdaf..9f0c04672 100644 --- a/examples/stm32wle5/src/bin/blinky.rs +++ b/examples/stm32wle5/src/bin/blinky.rs @@ -6,20 +6,12 @@ use defmt::*; use defmt_rtt as _; use embassy_executor::Spawner; use embassy_stm32::gpio::{Level, Output, Speed}; -use embassy_stm32::low_power::Executor; +use embassy_stm32::low_power; use embassy_time::Timer; use panic_probe as _; use static_cell::StaticCell; -#[cortex_m_rt::entry] -fn main() -> ! { - info!("main: Starting!"); - Executor::take().run(|spawner| { - spawner.spawn(unwrap!(async_main(spawner))); - }); -} - -#[embassy_executor::task] +#[embassy_executor::main(executor = "low_power::Executor")] async fn async_main(_spawner: Spawner) { let mut config = embassy_stm32::Config::default(); // enable HSI clock diff --git a/examples/stm32wle5/src/bin/button_exti.rs b/examples/stm32wle5/src/bin/button_exti.rs index db1bff0be..878eca7d0 100644 --- a/examples/stm32wle5/src/bin/button_exti.rs +++ b/examples/stm32wle5/src/bin/button_exti.rs @@ -7,19 +7,11 @@ use defmt_rtt as _; use embassy_executor::Spawner; use embassy_stm32::exti::ExtiInput; use embassy_stm32::gpio::Pull; -use embassy_stm32::low_power::Executor; +use embassy_stm32::low_power; use panic_probe as _; use static_cell::StaticCell; -#[cortex_m_rt::entry] -fn main() -> ! { - info!("main: Starting!"); - Executor::take().run(|spawner| { - spawner.spawn(unwrap!(async_main(spawner))); - }); -} - -#[embassy_executor::task] +#[embassy_executor::main(executor = "low_power::Executor")] async fn async_main(_spawner: Spawner) { let mut config = embassy_stm32::Config::default(); // enable HSI clock diff --git a/examples/stm32wle5/src/bin/i2c.rs b/examples/stm32wle5/src/bin/i2c.rs index c31c673c9..68c17a672 100644 --- a/examples/stm32wle5/src/bin/i2c.rs +++ b/examples/stm32wle5/src/bin/i2c.rs @@ -6,9 +6,8 @@ use defmt::*; use defmt_rtt as _; use embassy_executor::Spawner; use embassy_stm32::i2c::I2c; -use embassy_stm32::low_power::Executor; use embassy_stm32::time::Hertz; -use embassy_stm32::{bind_interrupts, i2c, peripherals}; +use embassy_stm32::{bind_interrupts, i2c, low_power, peripherals}; use embassy_time::{Duration, Timer}; use panic_probe as _; use static_cell::StaticCell; @@ -18,15 +17,7 @@ bind_interrupts!(struct IrqsI2C{ I2C2_ER => i2c::ErrorInterruptHandler; }); -#[cortex_m_rt::entry] -fn main() -> ! { - info!("main: Starting!"); - Executor::take().run(|spawner| { - spawner.spawn(unwrap!(async_main(spawner))); - }); -} - -#[embassy_executor::task] +#[embassy_executor::main(executor = "low_power::Executor")] async fn async_main(_spawner: Spawner) { let mut config = embassy_stm32::Config::default(); // enable HSI clock diff --git a/tests/stm32/src/bin/stop.rs b/tests/stm32/src/bin/stop.rs index 1fe65d867..83c375bc5 100644 --- a/tests/stm32/src/bin/stop.rs +++ b/tests/stm32/src/bin/stop.rs @@ -7,21 +7,13 @@ mod common; use chrono::NaiveDate; use common::*; -use cortex_m_rt::entry; use embassy_executor::Spawner; -use embassy_stm32::Config; -use embassy_stm32::low_power::{Executor, StopMode, stop_ready}; +use embassy_stm32::low_power::{StopMode, stop_ready}; use embassy_stm32::rcc::LsConfig; use embassy_stm32::rtc::Rtc; +use embassy_stm32::{Config, low_power}; use embassy_time::Timer; -#[entry] -fn main() -> ! { - Executor::take().run(|spawner| { - spawner.spawn(unwrap!(async_main(spawner))); - }); -} - #[embassy_executor::task] async fn task_1() { for _ in 0..9 { @@ -43,7 +35,7 @@ async fn task_2() { cortex_m::asm::bkpt(); } -#[embassy_executor::task] +#[embassy_executor::main(executor = "low_power::Executor")] async fn async_main(spawner: Spawner) { let _ = config(); -- cgit From 6d2d0c3dee41b03a985890db3d666ac5bff5e956 Mon Sep 17 00:00:00 2001 From: xoviat Date: Sun, 16 Nov 2025 18:47:12 -0600 Subject: wpan: restructure mac driver --- embassy-stm32-wpan/Cargo.toml | 1 + embassy-stm32-wpan/src/mac/control.rs | 171 +++++++++++++++++++++++++++----- embassy-stm32-wpan/src/mac/driver.rs | 120 ++++++++++++++++++---- embassy-stm32-wpan/src/mac/mod.rs | 6 +- embassy-stm32-wpan/src/mac/runner.rs | 73 ++++++++------ embassy-stm32-wpan/src/sub/mac.rs | 83 ++++++++++++---- examples/stm32wb/src/bin/mac_ffd_net.rs | 115 +++------------------ 7 files changed, 369 insertions(+), 200 deletions(-) diff --git a/embassy-stm32-wpan/Cargo.toml b/embassy-stm32-wpan/Cargo.toml index 0802b7328..75d978d1a 100644 --- a/embassy-stm32-wpan/Cargo.toml +++ b/embassy-stm32-wpan/Cargo.toml @@ -40,6 +40,7 @@ log = { version = "0.4.17", optional = true } cortex-m = "0.7.6" heapless = "0.8" aligned = "0.4.1" +critical-section = "1.1" bit_field = "0.10.2" stm32-device-signature = { version = "0.3.3", features = ["stm32wb5x"] } diff --git a/embassy-stm32-wpan/src/mac/control.rs b/embassy-stm32-wpan/src/mac/control.rs index e8d2f9f7b..fae00c6dc 100644 --- a/embassy-stm32-wpan/src/mac/control.rs +++ b/embassy-stm32-wpan/src/mac/control.rs @@ -1,65 +1,186 @@ +use core::cell::RefCell; use core::future::Future; +use core::sync::atomic::{Ordering, compiler_fence}; use core::task; use core::task::Poll; +use embassy_net_driver::LinkState; +use embassy_sync::blocking_mutex; use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex; -use embassy_sync::mutex::MutexGuard; +use embassy_sync::mutex::Mutex; use embassy_sync::signal::Signal; use futures_util::FutureExt; -use super::commands::MacCommand; -use super::event::MacEvent; -use super::typedefs::MacError; -use crate::mac::runner::Runner; +use crate::mac::commands::MacCommand; +use crate::mac::commands::*; +use crate::mac::driver::NetworkState; +use crate::mac::event::MacEvent; +use crate::mac::runner::ZeroCopyPubSub; +use crate::mac::typedefs::MacError; +use crate::mac::typedefs::*; +use crate::sub::mac::MacTx; pub struct Control<'a> { - runner: &'a Runner<'a>, + rx_event_channel: &'a ZeroCopyPubSub>, + mac_tx: &'a Mutex, + #[allow(unused)] + network_state: &'a blocking_mutex::Mutex>, } impl<'a> Control<'a> { - pub(crate) fn new(runner: &'a Runner<'a>) -> Self { - Self { runner: runner } + pub(crate) fn new( + rx_event_channel: &'a ZeroCopyPubSub>, + mac_tx: &'a Mutex, + network_state: &'a blocking_mutex::Mutex>, + ) -> Self { + Self { + rx_event_channel, + mac_tx, + network_state, + } + } + + pub async fn init_link(&mut self, short_address: [u8; 2], extended_address: [u8; 8], pan_id: [u8; 2]) { + debug!("resetting"); + + debug!( + "{:#x}", + self.send_command_and_get_response(&ResetRequest { + set_default_pib: true, + ..Default::default() + }) + .await + .unwrap() + .await + ); + + debug!("setting extended address"); + let extended_address: u64 = u64::from_be_bytes(extended_address); + debug!( + "{:#x}", + self.send_command_and_get_response(&SetRequest { + pib_attribute_ptr: &extended_address as *const _ as *const u8, + pib_attribute: PibId::ExtendedAddress, + }) + .await + .unwrap() + .await + ); + + debug!("setting short address"); + let short_address: u16 = u16::from_be_bytes(short_address); + debug!( + "{:#x}", + self.send_command_and_get_response(&SetRequest { + pib_attribute_ptr: &short_address as *const _ as *const u8, + pib_attribute: PibId::ShortAddress, + }) + .await + .unwrap() + .await + ); + + critical_section::with(|cs| { + self.network_state.borrow(cs).borrow_mut().mac_addr = extended_address.to_be_bytes(); + }); + + debug!("setting association permit"); + let association_permit: bool = true; + debug!( + "{:#x}", + self.send_command_and_get_response(&SetRequest { + pib_attribute_ptr: &association_permit as *const _ as *const u8, + pib_attribute: PibId::AssociationPermit, + }) + .await + .unwrap() + .await + ); + + debug!("setting TX power"); + let transmit_power: i8 = 2; + debug!( + "{:#x}", + self.send_command_and_get_response(&SetRequest { + pib_attribute_ptr: &transmit_power as *const _ as *const u8, + pib_attribute: PibId::TransmitPower, + }) + .await + .unwrap() + .await + ); + + debug!("starting FFD device"); + debug!( + "{:#x}", + self.send_command_and_get_response(&StartRequest { + pan_id: PanId(pan_id), + channel_number: MacChannel::Channel16, + beacon_order: 0x0F, + superframe_order: 0x0F, + pan_coordinator: true, + battery_life_extension: false, + ..Default::default() + }) + .await + .unwrap() + .await + ); + + debug!("setting RX on when idle"); + let rx_on_while_idle: bool = true; + debug!( + "{:#x}", + self.send_command_and_get_response(&SetRequest { + pib_attribute_ptr: &rx_on_while_idle as *const _ as *const u8, + pib_attribute: PibId::RxOnWhenIdle, + }) + .await + .unwrap() + .await + ); + + critical_section::with(|cs| { + let mut network_state = self.network_state.borrow(cs).borrow_mut(); + + network_state.link_state = LinkState::Up; + network_state.link_waker.wake(); + }); } pub async fn send_command(&self, cmd: &T) -> Result<(), MacError> where T: MacCommand, { - let _wm = self.runner.write_mutex.lock().await; - - self.runner.mac_subsystem.send_command(cmd).await + self.mac_tx.lock().await.send_command(cmd).await } pub async fn send_command_and_get_response(&self, cmd: &T) -> Result, MacError> where T: MacCommand, { - let rm = self.runner.read_mutex.lock().await; - let _wm = self.runner.write_mutex.lock().await; - let token = EventToken::new(self.runner, rm); + let token = EventToken::new(self.rx_event_channel); + + compiler_fence(Ordering::Release); - self.runner.mac_subsystem.send_command(cmd).await?; + self.mac_tx.lock().await.send_command(cmd).await?; Ok(token) } } pub struct EventToken<'a> { - runner: &'a Runner<'a>, - _mutex_guard: MutexGuard<'a, CriticalSectionRawMutex, ()>, + rx_event_channel: &'a ZeroCopyPubSub>, } impl<'a> EventToken<'a> { - pub(crate) fn new(runner: &'a Runner<'a>, mutex_guard: MutexGuard<'a, CriticalSectionRawMutex, ()>) -> Self { + pub(crate) fn new(rx_event_channel: &'a ZeroCopyPubSub>) -> Self { // Enable event receiving - runner.rx_event_channel.lock(|s| { + rx_event_channel.lock(|s| { *s.borrow_mut() = Some(Signal::new()); }); - Self { - runner: runner, - _mutex_guard: mutex_guard, - } + Self { rx_event_channel } } } @@ -67,7 +188,7 @@ impl<'a> Future for EventToken<'a> { type Output = MacEvent<'a>; fn poll(self: core::pin::Pin<&mut Self>, cx: &mut task::Context<'_>) -> Poll { - self.get_mut().runner.rx_event_channel.lock(|s| { + self.rx_event_channel.lock(|s| { let signal = s.borrow_mut(); let signal = match &*signal { Some(s) => s, @@ -88,7 +209,7 @@ impl<'a> Drop for EventToken<'a> { fn drop(&mut self) { // Disable event receiving // This will also drop the contained event, if it exists, and will free up receiving the next event - self.runner.rx_event_channel.lock(|s| { + self.rx_event_channel.lock(|s| { *s.borrow_mut() = None; }); } diff --git a/embassy-stm32-wpan/src/mac/driver.rs b/embassy-stm32-wpan/src/mac/driver.rs index 480ac3790..819299b48 100644 --- a/embassy-stm32-wpan/src/mac/driver.rs +++ b/embassy-stm32-wpan/src/mac/driver.rs @@ -1,22 +1,97 @@ #![deny(unused_must_use)] +use core::cell::RefCell; use core::task::Context; use embassy_net_driver::{Capabilities, HardwareAddress, LinkState}; +use embassy_sync::blocking_mutex; use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex; use embassy_sync::channel::Channel; +use embassy_sync::mutex::Mutex; +use embassy_sync::waitqueue::AtomicWaker; -use crate::mac::MTU; use crate::mac::event::MacEvent; -use crate::mac::runner::Runner; +use crate::mac::runner::{BUF_SIZE, ZeroCopyPubSub}; +use crate::mac::{Control, MTU, Runner}; +use crate::sub::mac::{Mac, MacRx, MacTx}; + +pub struct NetworkState { + pub mac_addr: [u8; 8], + pub link_state: LinkState, + pub link_waker: AtomicWaker, +} + +impl NetworkState { + pub const fn new() -> Self { + Self { + mac_addr: [0u8; 8], + link_state: LinkState::Down, + link_waker: AtomicWaker::new(), + } + } +} + +pub struct DriverState<'d> { + pub mac_tx: Mutex, + pub mac_rx: MacRx, + pub rx_event_channel: ZeroCopyPubSub>, + pub rx_data_channel: Channel, 1>, + pub tx_data_channel: Channel, + pub tx_buf_channel: Channel, + pub tx_buf_queue: [[u8; MTU]; BUF_SIZE], + pub network_state: blocking_mutex::Mutex>, +} + +impl<'d> DriverState<'d> { + pub const fn new(mac: Mac) -> Self { + let (mac_rx, mac_tx) = mac.split(); + let mac_tx = Mutex::new(mac_tx); + + Self { + mac_tx, + mac_rx, + rx_event_channel: ZeroCopyPubSub::new(RefCell::new(None)), + rx_data_channel: Channel::new(), + tx_data_channel: Channel::new(), + tx_buf_channel: Channel::new(), + tx_buf_queue: [[0u8; MTU]; BUF_SIZE], + network_state: blocking_mutex::Mutex::new(RefCell::new(NetworkState::new())), + } + } +} pub struct Driver<'d> { - runner: &'d Runner<'d>, + tx_data_channel: &'d Channel, + tx_buf_channel: &'d Channel, + rx_data_channel: &'d Channel, 1>, + network_state: &'d blocking_mutex::Mutex>, } impl<'d> Driver<'d> { - pub(crate) fn new(runner: &'d Runner<'d>) -> Self { - Self { runner: runner } + pub fn new(driver_state: &'d mut DriverState<'d>) -> (Self, Runner<'d>, Control<'d>) { + ( + Self { + tx_data_channel: &driver_state.tx_data_channel, + tx_buf_channel: &driver_state.tx_buf_channel, + rx_data_channel: &driver_state.rx_data_channel, + network_state: &driver_state.network_state, + }, + Runner::new( + &driver_state.rx_event_channel, + &driver_state.rx_data_channel, + &mut driver_state.mac_rx, + &driver_state.tx_data_channel, + &driver_state.tx_buf_channel, + &driver_state.mac_tx, + &mut driver_state.tx_buf_queue, + &driver_state.network_state, + ), + Control::new( + &driver_state.rx_event_channel, + &driver_state.mac_tx, + &driver_state.network_state, + ), + ) } } @@ -33,16 +108,16 @@ impl<'d> embassy_net_driver::Driver for Driver<'d> { Self: 'a; fn receive(&mut self, cx: &mut Context) -> Option<(Self::RxToken<'_>, Self::TxToken<'_>)> { - if self.runner.rx_channel.poll_ready_to_receive(cx).is_ready() - && self.runner.tx_buf_channel.poll_ready_to_receive(cx).is_ready() + if self.rx_data_channel.poll_ready_to_receive(cx).is_ready() + && self.tx_buf_channel.poll_ready_to_receive(cx).is_ready() { Some(( RxToken { - rx: &self.runner.rx_channel, + rx: self.rx_data_channel, }, TxToken { - tx: &self.runner.tx_channel, - tx_buf: &self.runner.tx_buf_channel, + tx: self.tx_data_channel, + tx_buf: self.tx_buf_channel, }, )) } else { @@ -51,10 +126,10 @@ impl<'d> embassy_net_driver::Driver for Driver<'d> { } fn transmit(&mut self, cx: &mut Context) -> Option> { - if self.runner.tx_buf_channel.poll_ready_to_receive(cx).is_ready() { + if self.tx_buf_channel.poll_ready_to_receive(cx).is_ready() { Some(TxToken { - tx: &self.runner.tx_channel, - tx_buf: &self.runner.tx_buf_channel, + tx: self.tx_data_channel, + tx_buf: self.tx_buf_channel, }) } else { None @@ -68,13 +143,20 @@ impl<'d> embassy_net_driver::Driver for Driver<'d> { caps } - fn link_state(&mut self, _cx: &mut Context) -> LinkState { - LinkState::Down + fn link_state(&mut self, cx: &mut Context) -> LinkState { + critical_section::with(|cs| { + let network_state = self.network_state.borrow(cs).borrow_mut(); + + // Unconditionally register the waker to avoid a race + network_state.link_waker.register(cx.waker()); + network_state.link_state + }) } fn hardware_address(&self) -> HardwareAddress { - // self.mac_addr - HardwareAddress::Ieee802154([0; 8]) + HardwareAddress::Ieee802154(critical_section::with(|cs| { + self.network_state.borrow(cs).borrow().mac_addr + })) } } @@ -99,8 +181,8 @@ impl<'d> embassy_net_driver::RxToken for RxToken<'d> { } pub struct TxToken<'d> { - tx: &'d Channel, - tx_buf: &'d Channel, + tx: &'d Channel, + tx_buf: &'d Channel, } impl<'d> embassy_net_driver::TxToken for TxToken<'d> { diff --git a/embassy-stm32-wpan/src/mac/mod.rs b/embassy-stm32-wpan/src/mac/mod.rs index c847a5cca..ac50a6b29 100644 --- a/embassy-stm32-wpan/src/mac/mod.rs +++ b/embassy-stm32-wpan/src/mac/mod.rs @@ -11,11 +11,7 @@ pub mod runner; pub mod typedefs; pub use crate::mac::control::Control; -use crate::mac::driver::Driver; +pub use crate::mac::driver::{Driver, DriverState}; pub use crate::mac::runner::Runner; const MTU: usize = 127; - -pub async fn new<'a>(runner: &'a Runner<'a>) -> (Control<'a>, Driver<'a>) { - (Control::new(runner), Driver::new(runner)) -} diff --git a/embassy-stm32-wpan/src/mac/runner.rs b/embassy-stm32-wpan/src/mac/runner.rs index 2409f994d..26fdf23e0 100644 --- a/embassy-stm32-wpan/src/mac/runner.rs +++ b/embassy-stm32-wpan/src/mac/runner.rs @@ -8,52 +8,65 @@ use embassy_sync::mutex::Mutex; use embassy_sync::signal::Signal; use crate::mac::MTU; -use crate::mac::commands::DataRequest; +use crate::mac::commands::*; +use crate::mac::driver::NetworkState; use crate::mac::event::MacEvent; -use crate::mac::typedefs::{AddressMode, MacAddress, PanId, SecurityLevel}; -use crate::sub::mac::Mac; +use crate::mac::typedefs::*; +use crate::sub::mac::{MacRx, MacTx}; -type ZeroCopyPubSub = blocking_mutex::Mutex>>>; +pub type ZeroCopyPubSub = blocking_mutex::Mutex>>>; + +pub const BUF_SIZE: usize = 3; pub struct Runner<'a> { - pub(crate) mac_subsystem: Mac, // rx event backpressure is already provided through the MacEvent drop mechanism // therefore, we don't need to worry about overwriting events - pub(crate) rx_event_channel: ZeroCopyPubSub>, - pub(crate) read_mutex: Mutex, - pub(crate) write_mutex: Mutex, - pub(crate) rx_channel: Channel, 1>, - pub(crate) tx_channel: Channel, - pub(crate) tx_buf_channel: Channel, + rx_event_channel: &'a ZeroCopyPubSub>, + rx_data_channel: &'a Channel, 1>, + mac_rx: &'a mut MacRx, + + tx_data_channel: &'a Channel, + tx_buf_channel: &'a Channel, + mac_tx: &'a Mutex, + + #[allow(unused)] + network_state: &'a blocking_mutex::Mutex>, } impl<'a> Runner<'a> { - pub fn new(mac: Mac, tx_buf_queue: [&'a mut [u8; MTU]; 5]) -> Self { - let this = Self { - mac_subsystem: mac, - rx_event_channel: blocking_mutex::Mutex::new(RefCell::new(None)), - read_mutex: Mutex::new(()), - write_mutex: Mutex::new(()), - rx_channel: Channel::new(), - tx_channel: Channel::new(), - tx_buf_channel: Channel::new(), - }; - + pub fn new( + rx_event_channel: &'a ZeroCopyPubSub>, + rx_data_channel: &'a Channel, 1>, + mac_rx: &'a mut MacRx, + tx_data_channel: &'a Channel, + tx_buf_channel: &'a Channel, + mac_tx: &'a Mutex, + tx_buf_queue: &'a mut [[u8; MTU]; BUF_SIZE], + network_state: &'a blocking_mutex::Mutex>, + ) -> Self { for buf in tx_buf_queue { - this.tx_buf_channel.try_send(buf).unwrap(); + tx_buf_channel.try_send(buf).unwrap(); } - this + Self { + rx_event_channel, + rx_data_channel, + mac_rx, + tx_data_channel, + tx_buf_channel, + mac_tx, + network_state, + } } pub async fn run(&'a self) -> ! { join::join( async { loop { - if let Ok(mac_event) = self.mac_subsystem.read().await { + if let Ok(mac_event) = self.mac_rx.read().await { match mac_event { MacEvent::McpsDataInd(_) => { - self.rx_channel.send(mac_event).await; + self.rx_data_channel.send(mac_event).await; } _ => { self.rx_event_channel.lock(|s| { @@ -73,11 +86,13 @@ impl<'a> Runner<'a> { let mut msdu_handle = 0x02; loop { - let (buf, len) = self.tx_channel.receive().await; - let _wm = self.write_mutex.lock().await; + let (buf, len) = self.tx_data_channel.receive().await; + let mac_tx = self.mac_tx.lock().await; + + // TODO: skip this if the link state is down // The mutex should be dropped on the next loop iteration - self.mac_subsystem + mac_tx .send_command( DataRequest { src_addr_mode: AddressMode::Short, diff --git a/embassy-stm32-wpan/src/sub/mac.rs b/embassy-stm32-wpan/src/sub/mac.rs index baf4da979..93cafbc72 100644 --- a/embassy-stm32-wpan/src/sub/mac.rs +++ b/embassy-stm32-wpan/src/sub/mac.rs @@ -28,32 +28,39 @@ impl Mac { Self { _private: () } } - /// `HW_IPCC_MAC_802_15_4_EvtNot` - /// - /// This function will stall if the previous `EvtBox` has not been dropped - pub async fn tl_read(&self) -> EvtBox { - // Wait for the last event box to be dropped - poll_fn(|cx| { - MAC_WAKER.register(cx.waker()); - if MAC_EVT_OUT.load(Ordering::SeqCst) { - Poll::Pending - } else { - Poll::Ready(()) - } - }) - .await; + pub const fn split(self) -> (MacRx, MacTx) { + (MacRx { _private: () }, MacTx { _private: () }) + } - // Return a new event box - Ipcc::receive(channels::cpu2::IPCC_MAC_802_15_4_NOTIFICATION_ACK_CHANNEL, || unsafe { - // The closure is not async, therefore the closure must execute to completion (cannot be dropped) - // Therefore, the event box is guaranteed to be cleaned up if it's not leaked - MAC_EVT_OUT.store(true, Ordering::SeqCst); + pub async fn tl_write_and_get_response(&self, opcode: u16, payload: &[u8]) -> u8 { + MacTx { _private: () }.tl_write_and_get_response(opcode, payload).await + } - Some(EvtBox::new(MAC_802_15_4_NOTIF_RSP_EVT_BUFFER.as_mut_ptr() as *mut _)) - }) - .await + pub async fn tl_write(&self, opcode: u16, payload: &[u8]) { + MacTx { _private: () }.tl_write(opcode, payload).await } + pub async fn send_command(&self, cmd: &T) -> Result<(), MacError> + where + T: MacCommand, + { + MacTx { _private: () }.send_command(cmd).await + } + + pub async fn tl_read(&self) -> EvtBox { + MacRx { _private: () }.tl_read().await + } + + pub async fn read(&self) -> Result, ()> { + MacRx { _private: () }.read().await + } +} + +pub struct MacTx { + _private: (), +} + +impl MacTx { /// `HW_IPCC_MAC_802_15_4_CmdEvtNot` pub async fn tl_write_and_get_response(&self, opcode: u16, payload: &[u8]) -> u8 { self.tl_write(opcode, payload).await; @@ -92,6 +99,38 @@ impl Mac { Err(MacError::from(response)) } } +} + +pub struct MacRx { + _private: (), +} + +impl MacRx { + /// `HW_IPCC_MAC_802_15_4_EvtNot` + /// + /// This function will stall if the previous `EvtBox` has not been dropped + pub async fn tl_read(&self) -> EvtBox { + // Wait for the last event box to be dropped + poll_fn(|cx| { + MAC_WAKER.register(cx.waker()); + if MAC_EVT_OUT.load(Ordering::SeqCst) { + Poll::Pending + } else { + Poll::Ready(()) + } + }) + .await; + + // Return a new event box + Ipcc::receive(channels::cpu2::IPCC_MAC_802_15_4_NOTIFICATION_ACK_CHANNEL, || unsafe { + // The closure is not async, therefore the closure must execute to completion (cannot be dropped) + // Therefore, the event box is guaranteed to be cleaned up if it's not leaked + MAC_EVT_OUT.store(true, Ordering::SeqCst); + + Some(EvtBox::new(MAC_802_15_4_NOTIF_RSP_EVT_BUFFER.as_mut_ptr() as *mut _)) + }) + .await + } pub async fn read(&self) -> Result, ()> { MacEvent::new(self.tl_read().await) diff --git a/examples/stm32wb/src/bin/mac_ffd_net.rs b/examples/stm32wb/src/bin/mac_ffd_net.rs index 5296943a1..9b705dda9 100644 --- a/examples/stm32wb/src/bin/mac_ffd_net.rs +++ b/examples/stm32wb/src/bin/mac_ffd_net.rs @@ -7,9 +7,7 @@ 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_stm32_wpan::mac::commands::{ResetRequest, SetRequest, StartRequest}; -use embassy_stm32_wpan::mac::typedefs::{MacChannel, PanId, PibId}; -use embassy_stm32_wpan::mac::{self, Runner}; +use embassy_stm32_wpan::mac::{Driver, DriverState, Runner}; use embassy_stm32_wpan::sub::mm; use static_cell::StaticCell; use {defmt_rtt as _, panic_probe as _}; @@ -72,106 +70,23 @@ async fn main(spawner: Spawner) { let result = mbox.sys_subsystem.shci_c2_mac_802_15_4_init().await; info!("initialized mac: {}", result); - info!("resetting"); - mbox.mac_subsystem - .send_command(&ResetRequest { - set_default_pib: true, - ..Default::default() - }) - .await - .unwrap(); - defmt::info!("{:#x}", mbox.mac_subsystem.read().await.unwrap()); - - info!("setting extended address"); - let extended_address: u64 = 0xACDE480000000001; - mbox.mac_subsystem - .send_command(&SetRequest { - pib_attribute_ptr: &extended_address as *const _ as *const u8, - pib_attribute: PibId::ExtendedAddress, - }) - .await - .unwrap(); - defmt::info!("{:#x}", mbox.mac_subsystem.read().await.unwrap()); - - info!("setting short address"); - let short_address: u16 = 0x1122; - mbox.mac_subsystem - .send_command(&SetRequest { - pib_attribute_ptr: &short_address as *const _ as *const u8, - pib_attribute: PibId::ShortAddress, - }) - .await - .unwrap(); - defmt::info!("{:#x}", mbox.mac_subsystem.read().await.unwrap()); - - info!("setting association permit"); - let association_permit: bool = true; - mbox.mac_subsystem - .send_command(&SetRequest { - pib_attribute_ptr: &association_permit as *const _ as *const u8, - pib_attribute: PibId::AssociationPermit, - }) - .await - .unwrap(); - defmt::info!("{:#x}", mbox.mac_subsystem.read().await.unwrap()); - - info!("setting TX power"); - let transmit_power: i8 = 2; - mbox.mac_subsystem - .send_command(&SetRequest { - pib_attribute_ptr: &transmit_power as *const _ as *const u8, - pib_attribute: PibId::TransmitPower, - }) - .await - .unwrap(); - defmt::info!("{:#x}", mbox.mac_subsystem.read().await.unwrap()); - - info!("starting FFD device"); - mbox.mac_subsystem - .send_command(&StartRequest { - pan_id: PanId([0x1A, 0xAA]), - channel_number: MacChannel::Channel16, - beacon_order: 0x0F, - superframe_order: 0x0F, - pan_coordinator: true, - battery_life_extension: false, - ..Default::default() - }) - .await - .unwrap(); - defmt::info!("{:#x}", mbox.mac_subsystem.read().await.unwrap()); - - info!("setting RX on when idle"); - let rx_on_while_idle: bool = true; - mbox.mac_subsystem - .send_command(&SetRequest { - pib_attribute_ptr: &rx_on_while_idle as *const _ as *const u8, - pib_attribute: PibId::RxOnWhenIdle, - }) - .await - .unwrap(); - defmt::info!("{:#x}", mbox.mac_subsystem.read().await.unwrap()); - - static TX1: StaticCell<[u8; 127]> = StaticCell::new(); - static TX2: StaticCell<[u8; 127]> = StaticCell::new(); - static TX3: StaticCell<[u8; 127]> = StaticCell::new(); - static TX4: StaticCell<[u8; 127]> = StaticCell::new(); - static TX5: StaticCell<[u8; 127]> = StaticCell::new(); - let tx_queue = [ - TX1.init([0u8; 127]), - TX2.init([0u8; 127]), - TX3.init([0u8; 127]), - TX4.init([0u8; 127]), - TX5.init([0u8; 127]), - ]; - + static DRIVER_STATE: StaticCell = StaticCell::new(); static RUNNER: StaticCell = StaticCell::new(); - let runner = RUNNER.init(Runner::new(mbox.mac_subsystem, tx_queue)); - spawner.spawn(run_mac(runner).unwrap()); + let driver_state = DRIVER_STATE.init(DriverState::new(mbox.mac_subsystem)); + let (driver, runner, mut control) = Driver::new(driver_state); + + spawner.spawn(run_mac(RUNNER.init(runner)).unwrap()); + + control + .init_link( + 0x1122u16.to_be_bytes().try_into().unwrap(), + 0xACDE480000000001u64.to_be_bytes().try_into().unwrap(), + [0x1A, 0xAA], + ) + .await; - let (driver, control) = mac::new(runner).await; + cortex_m::asm::bkpt(); let _ = driver; - let _ = control; } -- cgit From f5c9fac0569f075ed4bf93ffb3eb60e72d3652e7 Mon Sep 17 00:00:00 2001 From: xoviat Date: Sun, 16 Nov 2025 18:54:50 -0600 Subject: fmt --- embassy-stm32-wpan/src/mac/control.rs | 2 -- 1 file changed, 2 deletions(-) diff --git a/embassy-stm32-wpan/src/mac/control.rs b/embassy-stm32-wpan/src/mac/control.rs index fae00c6dc..8fb971da3 100644 --- a/embassy-stm32-wpan/src/mac/control.rs +++ b/embassy-stm32-wpan/src/mac/control.rs @@ -11,12 +11,10 @@ use embassy_sync::mutex::Mutex; use embassy_sync::signal::Signal; use futures_util::FutureExt; -use crate::mac::commands::MacCommand; use crate::mac::commands::*; use crate::mac::driver::NetworkState; use crate::mac::event::MacEvent; use crate::mac::runner::ZeroCopyPubSub; -use crate::mac::typedefs::MacError; use crate::mac::typedefs::*; use crate::sub::mac::MacTx; -- cgit From 282d8e1c088b7c28654e6a724028b59aae6477f9 Mon Sep 17 00:00:00 2001 From: xoviat Date: Sun, 16 Nov 2025 18:55:52 -0600 Subject: changelog --- embassy-stm32-wpan/CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/embassy-stm32-wpan/CHANGELOG.md b/embassy-stm32-wpan/CHANGELOG.md index 7042ad14c..c567fe1de 100644 --- a/embassy-stm32-wpan/CHANGELOG.md +++ b/embassy-stm32-wpan/CHANGELOG.md @@ -8,4 +8,5 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## Unreleased - ReleaseDate +- restructure to allow embassy net driver to work. - First release with changelog. -- cgit From 21dd55b69ed2418f62a86185b52d6c7df9d6292b Mon Sep 17 00:00:00 2001 From: i509VCB Date: Sat, 25 Oct 2025 13:14:06 -0500 Subject: nxp: generate all chip peripherals and impls from metadata --- embassy-nxp/CHANGELOG.md | 1 + embassy-nxp/Cargo.toml | 4 +- embassy-nxp/build.rs | 353 ++++++++++++++++++++++++++++--- embassy-nxp/src/chips/lpc55.rs | 127 +---------- embassy-nxp/src/chips/mimxrt1011.rs | 104 +-------- embassy-nxp/src/chips/mimxrt1062.rs | 273 +----------------------- embassy-nxp/src/dma.rs | 1 + embassy-nxp/src/dma/lpc55.rs | 47 ++-- embassy-nxp/src/gpio/lpc55.rs | 93 ++------ embassy-nxp/src/gpio/rt1xxx.rs | 50 ++--- embassy-nxp/src/iomuxc.rs | 29 +++ embassy-nxp/src/lib.rs | 16 +- embassy-nxp/src/pwm.rs | 2 + embassy-nxp/src/pwm/lpc55.rs | 113 ++-------- embassy-nxp/src/sct.rs | 56 +++++ embassy-nxp/src/usart.rs | 2 + embassy-nxp/src/usart/lpc55.rs | 115 ++++------ examples/lpc55s69/src/bin/pwm.rs | 2 +- examples/lpc55s69/src/bin/usart_async.rs | 4 +- 19 files changed, 549 insertions(+), 843 deletions(-) create mode 100644 embassy-nxp/src/iomuxc.rs create mode 100644 embassy-nxp/src/sct.rs diff --git a/embassy-nxp/CHANGELOG.md b/embassy-nxp/CHANGELOG.md index 39f5c75bd..e6f117da4 100644 --- a/embassy-nxp/CHANGELOG.md +++ b/embassy-nxp/CHANGELOG.md @@ -7,6 +7,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## Unreleased - ReleaseDate +- Codegen using `nxp-pac` metadata - LPC55: PWM simple - LPC55: Move ALT definitions for USART to TX/RX pin impls. - LPC55: Remove internal match_iocon macro diff --git a/embassy-nxp/Cargo.toml b/embassy-nxp/Cargo.toml index f8c63ba29..b78c26c77 100644 --- a/embassy-nxp/Cargo.toml +++ b/embassy-nxp/Cargo.toml @@ -38,13 +38,13 @@ embassy-time-queue-utils = { version = "0.3.0", path = "../embassy-time-queue-ut embedded-io = "0.6.1" embedded-hal-02 = { package = "embedded-hal", version = "0.2.6", features = ["unproven"] } ## Chip dependencies -nxp-pac = { version = "0.1.0", optional = true, git = "https://github.com/i509VCB/nxp-pac", rev = "477dfdbfd5e6c75c0730c56494b601c1b2257263"} +nxp-pac = { version = "0.1.0", optional = true, git = "https://github.com/i509VCB/nxp-pac", rev = "af5122e1cbe1483833c5d2e5af96b26a34ed5d62"} imxrt-rt = { version = "0.1.7", optional = true, features = ["device"] } [build-dependencies] cfg_aliases = "0.2.1" -nxp-pac = { version = "0.1.0", git = "https://github.com/i509VCB/nxp-pac", rev = "477dfdbfd5e6c75c0730c56494b601c1b2257263", features = ["metadata"], optional = true } +nxp-pac = { version = "0.1.0", git = "https://github.com/i509VCB/nxp-pac", rev = "af5122e1cbe1483833c5d2e5af96b26a34ed5d62", features = ["metadata"], optional = true } proc-macro2 = "1.0.95" quote = "1.0.15" diff --git a/embassy-nxp/build.rs b/embassy-nxp/build.rs index f3c062c87..f53c29161 100644 --- a/embassy-nxp/build.rs +++ b/embassy-nxp/build.rs @@ -4,10 +4,12 @@ use std::process::Command; use std::{env, fs}; use cfg_aliases::cfg_aliases; -#[cfg(feature = "_rt1xxx")] use nxp_pac::metadata; +use nxp_pac::metadata::{METADATA, Peripheral}; #[allow(unused)] use proc_macro2::TokenStream; +use proc_macro2::{Ident, Literal, Span}; +use quote::format_ident; #[allow(unused)] use quote::quote; @@ -31,56 +33,188 @@ fn main() { .unwrap() .to_ascii_lowercase(); + let singletons = singletons(&mut cfgs); + cfg_aliases! { rt1xxx: { any(feature = "mimxrt1011", feature = "mimxrt1062") }, - gpio1: { any(feature = "mimxrt1011", feature = "mimxrt1062") }, - gpio2: { any(feature = "mimxrt1011", feature = "mimxrt1062") }, - gpio3: { feature = "mimxrt1062" }, - gpio4: { feature = "mimxrt1062" }, - gpio5: { any(feature = "mimxrt1011", feature = "mimxrt1062") }, } eprintln!("chip: {chip_name}"); - generate_code(); + generate_code(&mut cfgs, &singletons); } -#[cfg(feature = "_rt1xxx")] -fn generate_iomuxc() -> TokenStream { - use proc_macro2::{Ident, Span}; +/// A peripheral singleton returned by `embassy_nxp::init`. +struct Singleton { + name: String, - let pads = metadata::iomuxc::IOMUXC_REGISTERS.iter().map(|registers| { - let name = Ident::new(®isters.name, Span::call_site()); - let address = registers.pad_ctl; + /// A cfg guard which indicates whether the `Peripherals` struct will give the user this singleton. + cfg: Option, +} - quote! { - pub const #name: u32 = #address; +fn singletons(cfgs: &mut common::CfgSet) -> Vec { + let mut singletons = Vec::new(); + + for peripheral in METADATA.peripherals { + // GPIO and DMA are generated in a 2nd pass. + let skip_singleton = if peripheral.name.starts_with("GPIO") || peripheral.name.starts_with("DMA") { + true + } else { + false + }; + + if !skip_singleton { + singletons.push(Singleton { + name: peripheral.name.into(), + cfg: None, + }); } - }); + } - let muxes = metadata::iomuxc::IOMUXC_REGISTERS.iter().map(|registers| { - let name = Ident::new(®isters.name, Span::call_site()); - let address = registers.mux_ctl; + cfgs.declare_all(&[ + "gpio1", + "gpio1_hi", + "gpio2", + "gpio2_hi", + "gpio3", + "gpio3_hi", + "gpio4", + "gpio4_hi", + "gpio5", + "gpio5_hi", + "gpio10", + "gpio10_hi", + ]); - quote! { - pub const #name: u32 = #address; + for peripheral in METADATA.peripherals.iter().filter(|p| p.name.starts_with("GPIO")) { + let number = peripheral.name.strip_prefix("GPIO").unwrap(); + assert!(number.parse::().is_ok()); + cfgs.enable(format!("gpio{}", number)); + + for signal in peripheral.signals.iter() { + let pin_number = signal.name.parse::().unwrap(); + + if pin_number > 15 { + cfgs.enable(format!("gpio{}_hi", number)); + } + + // GPIO signals only defined a single signal, on a single pin. + assert_eq!(signal.pins.len(), 1); + + singletons.push(Singleton { + name: signal.pins[0].pin.into(), + cfg: None, + }); + } + } + + for peripheral in METADATA.peripherals.iter().filter(|p| p.name.starts_with("DMA")) { + let instance = peripheral.name.strip_prefix("DMA").unwrap(); + assert!(instance.parse::().is_ok()); + + for signal in peripheral.signals.iter() { + let channel_number = signal.name.parse::().unwrap(); + let name = format!("DMA{instance}_CH{channel_number}"); + + // DMA has no pins. + assert!(signal.pins.is_empty()); + + singletons.push(Singleton { name, cfg: None }); + } + } + + for peripheral in METADATA.peripherals.iter().filter(|p| p.name.starts_with("SCT")) { + let instance = peripheral.name.strip_prefix("SCT").unwrap(); + assert!(instance.parse::().is_ok()); + + for signal in peripheral.signals.iter() { + if !signal.name.starts_with("OUT") { + continue; + } + + let channel_number = signal.name.strip_prefix("OUT").unwrap().parse::().unwrap(); + let name = format!("SCT{instance}_OUT{channel_number}"); + + singletons.push(Singleton { name, cfg: None }); } + } + + singletons +} + +#[cfg(feature = "_rt1xxx")] +fn generate_iomuxc() -> TokenStream { + let iomuxc_pad_impls = metadata::METADATA + .pins + .iter() + .filter(|p| p.iomuxc.as_ref().filter(|i| i.mux.is_some()).is_some()) + .map(|pin| { + let Some(ref iomuxc) = pin.iomuxc else { + panic!("Pin {} has no IOMUXC definitions", pin.name); + }; + + let name = Ident::new(pin.name, Span::call_site()); + let mux = iomuxc.mux.unwrap(); + let pad = iomuxc.pad; + + quote! { + impl_iomuxc_pad!(#name, #pad, #mux); + } + }); + + let base_match_arms = metadata::METADATA + .peripherals + .iter() + .filter(|p| p.name.starts_with("GPIO")) + .map(|peripheral| { + peripheral.signals.iter().map(|signal| { + // All GPIO signals have a single pin. + let pin = &signal.pins[0]; + let instance = peripheral.name.strip_prefix("GPIO").unwrap(); + let bank_match = format_ident!("Gpio{}", instance); + let pin_number = signal.name.parse::().unwrap(); + let pin_ident = Ident::new(pin.pin, Span::call_site()); + + quote! { + (Bank::#bank_match, #pin_number) => + } + }) + }) + .flatten() + .collect::>(); + + let pad_match_arms = base_match_arms.iter().map(|arm| { + quote! { #arm::PAD } + }); + + let mux_match_arms = base_match_arms.iter().map(|arm| { + quote! { #arm::MUX } }); quote! { - pub mod iomuxc { - pub mod pads { - #(#pads)* + #(#iomuxc_pad_impls)* + + pub(crate) fn iomuxc_pad(bank: crate::gpio::Bank, pin: u8) -> *mut () { + use crate::gpio::Bank; + + match (bank, pin) { + #(#pad_match_arms),*, + _ => unreachable!() } + } + + pub(crate) fn iomuxc_mux(bank: crate::gpio::Bank, pin: u8) -> Option<*mut ()> { + use crate::gpio::Bank; - pub mod muxes { - #(#muxes)* + match (bank, pin) { + #(#mux_match_arms),*, + _ => unreachable!() } } } } -fn generate_code() { +fn generate_code(cfgs: &mut common::CfgSet, singletons: &[Singleton]) { #[allow(unused)] use std::fmt::Write; @@ -88,14 +222,179 @@ fn generate_code() { #[allow(unused_mut)] let mut output = String::new(); + writeln!(&mut output, "{}", peripherals(singletons)).unwrap(); + #[cfg(feature = "_rt1xxx")] writeln!(&mut output, "{}", generate_iomuxc()).unwrap(); + writeln!(&mut output, "{}", interrupts()).unwrap(); + writeln!(&mut output, "{}", impl_peripherals(cfgs, singletons)).unwrap(); + let out_file = out_dir.join("_generated.rs").to_string_lossy().to_string(); fs::write(&out_file, output).unwrap(); rustfmt(&out_file); } +fn interrupts() -> TokenStream { + let interrupts = METADATA.interrupts.iter().map(|interrupt| format_ident!("{interrupt}")); + + quote! { + embassy_hal_internal::interrupt_mod!(#(#interrupts),*); + } +} + +fn peripherals(singletons: &[Singleton]) -> TokenStream { + let defs = singletons.iter().map(|s| { + let ident = Ident::new(&s.name, Span::call_site()); + quote! { #ident } + }); + + let peripherals = singletons.iter().map(|s| { + let ident = Ident::new(&s.name, Span::call_site()); + let cfg = s.cfg.clone().unwrap_or_else(|| quote! {}); + quote! { + #cfg + #ident + } + }); + + quote! { + embassy_hal_internal::peripherals_definition!(#(#defs),*); + embassy_hal_internal::peripherals_struct!(#(#peripherals),*); + } +} + +fn impl_gpio_pin(impls: &mut Vec, peripheral: &Peripheral) { + let instance = peripheral.name.strip_prefix("GPIO").unwrap(); + let bank = format_ident!("Gpio{}", instance); + // let pin = + + for signal in peripheral.signals.iter() { + let pin_number = signal.name.parse::().unwrap(); + let pin = Ident::new(signal.pins[0].pin, Span::call_site()); + + impls.push(quote! { + impl_pin!(#pin, #bank, #pin_number); + }); + } +} + +fn impl_dma_channel(impls: &mut Vec, peripheral: &Peripheral) { + let instance = Ident::new(peripheral.name, Span::call_site()); + + for signal in peripheral.signals.iter() { + let channel_number = signal.name.parse::().unwrap(); + let channel_name = format_ident!("{instance}_CH{channel_number}"); + + impls.push(quote! { + impl_dma_channel!(#instance, #channel_name, #channel_number); + }); + } +} + +fn impl_usart(impls: &mut Vec, peripheral: &Peripheral) { + let instance = Ident::new(peripheral.name, Span::call_site()); + let flexcomm = Ident::new( + peripheral.flexcomm.expect("LPC55 must specify FLEXCOMM instance"), + Span::call_site(), + ); + let number = Literal::u8_unsuffixed(peripheral.name.strip_prefix("USART").unwrap().parse::().unwrap()); + + impls.push(quote! { + impl_usart_instance!(#instance, #flexcomm, #number); + }); + + for signal in peripheral.signals { + let r#macro = match signal.name { + "TXD" => format_ident!("impl_usart_txd_pin"), + "RXD" => format_ident!("impl_usart_rxd_pin"), + _ => unreachable!(), + }; + + for pin in signal.pins { + let alt = format_ident!("ALT{}", pin.alt); + let pin = format_ident!("{}", pin.pin); + + impls.push(quote! { + #r#macro!(#pin, #instance, #alt); + }); + } + } + + for dma_mux in peripheral.dma_muxing { + assert_eq!(dma_mux.mux, "DMA0", "TODO: USART for more than LPC55"); + + let r#macro = match dma_mux.signal { + "TX" => format_ident!("impl_usart_tx_channel"), + "RX" => format_ident!("impl_usart_rx_channel"), + _ => unreachable!(), + }; + + let channel = format_ident!("DMA0_CH{}", dma_mux.request); + + impls.push(quote! { + #r#macro!(#instance, #channel); + }); + } +} + +fn impl_sct(impls: &mut Vec, peripheral: &Peripheral) { + let instance = Ident::new(peripheral.name, Span::call_site()); + + impls.push(quote! { + impl_sct_instance!(#instance); + }); + + for signal in peripheral.signals.iter() { + if signal.name.starts_with("OUT") { + let channel_number = signal.name.strip_prefix("OUT").unwrap().parse::().unwrap(); + + let channel_name = format_ident!("{instance}_OUT{channel_number}"); + + impls.push(quote! { + impl_sct_output_instance!(#instance, #channel_name, #channel_number); + }); + + if signal.name.starts_with("OUT") { + for pin in signal.pins { + let pin_name = format_ident!("{}", pin.pin); + let alt = format_ident!("ALT{}", pin.alt); + + impls.push(quote! { + impl_sct_output_pin!(#instance, #channel_name, #pin_name, #alt); + }); + } + } + } + } +} + +fn impl_peripherals(_cfgs: &mut common::CfgSet, _singletons: &[Singleton]) -> TokenStream { + let mut impls = Vec::new(); + + for peripheral in metadata::METADATA.peripherals.iter() { + if peripheral.name.starts_with("GPIO") { + impl_gpio_pin(&mut impls, peripheral); + } + + if peripheral.name.starts_with("DMA") { + impl_dma_channel(&mut impls, peripheral); + } + + if peripheral.name.starts_with("USART") { + impl_usart(&mut impls, peripheral); + } + + if peripheral.name.starts_with("SCT") { + impl_sct(&mut impls, peripheral); + } + } + + quote! { + #(#impls)* + } +} + /// rustfmt a given path. /// Failures are logged to stderr and ignored. fn rustfmt(path: impl AsRef) { diff --git a/embassy-nxp/src/chips/lpc55.rs b/embassy-nxp/src/chips/lpc55.rs index e9addddb6..7967e07d1 100644 --- a/embassy-nxp/src/chips/lpc55.rs +++ b/embassy-nxp/src/chips/lpc55.rs @@ -1,121 +1,10 @@ -pub use nxp_pac as pac; +pub(crate) mod _generated { + #![allow(dead_code)] + #![allow(unused_imports)] + #![allow(non_snake_case)] + #![allow(missing_docs)] -embassy_hal_internal::interrupt_mod!( - FLEXCOMM0, FLEXCOMM1, FLEXCOMM2, FLEXCOMM3, FLEXCOMM4, FLEXCOMM5, FLEXCOMM6, FLEXCOMM7 -); - -embassy_hal_internal::peripherals! { - // External pins. These are not only GPIOs, they are multi-purpose pins and can be used by other - // peripheral types (e.g. I2C). - PIO0_0, - PIO0_1, - PIO0_2, - PIO0_3, - PIO0_4, - PIO0_5, - PIO0_6, - PIO0_7, - PIO0_8, - PIO0_9, - PIO0_10, - PIO0_11, - PIO0_12, - PIO0_13, - PIO0_14, - PIO0_15, - PIO0_16, - PIO0_17, - PIO0_18, - PIO0_19, - PIO0_20, - PIO0_21, - PIO0_22, - PIO0_23, - PIO0_24, - PIO0_25, - PIO0_26, - PIO0_27, - PIO0_28, - PIO0_29, - PIO0_30, - PIO0_31, - PIO1_0, - PIO1_1, - PIO1_2, - PIO1_3, - PIO1_4, - PIO1_5, - PIO1_6, - PIO1_7, - PIO1_8, - PIO1_9, - PIO1_10, - PIO1_11, - PIO1_12, - PIO1_13, - PIO1_14, - PIO1_15, - PIO1_16, - PIO1_17, - PIO1_18, - PIO1_19, - PIO1_20, - PIO1_21, - PIO1_22, - PIO1_23, - PIO1_24, - PIO1_25, - PIO1_26, - PIO1_27, - PIO1_28, - PIO1_29, - PIO1_30, - PIO1_31, - - // Direct Memory Access (DMA) channels. They are used for asynchronous modes of peripherals. - DMA_CH0, - DMA_CH1, - DMA_CH2, - DMA_CH3, - DMA_CH4, - DMA_CH5, - DMA_CH6, - DMA_CH7, - DMA_CH8, - DMA_CH9, - DMA_CH10, - DMA_CH11, - DMA_CH12, - DMA_CH13, - DMA_CH14, - DMA_CH15, - DMA_CH16, - DMA_CH17, - DMA_CH18, - DMA_CH19, - DMA_CH20, - DMA_CH21, - DMA_CH22, - - // Pulse-Width Modulation Outputs. - PWM_OUTPUT0, - PWM_OUTPUT1, - PWM_OUTPUT2, - PWM_OUTPUT3, - PWM_OUTPUT4, - PWM_OUTPUT5, - PWM_OUTPUT6, - PWM_OUTPUT7, - PWM_OUTPUT8, - PWM_OUTPUT9, - - // Universal Synchronous/Asynchronous Receiver/Transmitter (USART) instances. - USART0, - USART1, - USART2, - USART3, - USART4, - USART5, - USART6, - USART7 + include!(concat!(env!("OUT_DIR"), "/_generated.rs")); } + +pub use _generated::*; diff --git a/embassy-nxp/src/chips/mimxrt1011.rs b/embassy-nxp/src/chips/mimxrt1011.rs index a74d953fc..d5969a24b 100644 --- a/embassy-nxp/src/chips/mimxrt1011.rs +++ b/embassy-nxp/src/chips/mimxrt1011.rs @@ -1,107 +1,5 @@ // This must be imported so that __preinit is defined. use imxrt_rt as _; -pub use nxp_pac as pac; - -embassy_hal_internal::peripherals! { - // External pins. These are not only GPIOs, they are multi-purpose pins and can be used by other - // peripheral types (e.g. I2C). - GPIO_00, - GPIO_01, - GPIO_02, - GPIO_03, - GPIO_04, - GPIO_05, - GPIO_06, - GPIO_07, - GPIO_08, - GPIO_09, - GPIO_10, - GPIO_11, - GPIO_12, - GPIO_13, - GPIO_AD_00, - GPIO_AD_01, - GPIO_AD_02, - GPIO_AD_03, - GPIO_AD_04, - GPIO_AD_05, - GPIO_AD_06, - GPIO_AD_07, - GPIO_AD_08, - GPIO_AD_09, - GPIO_AD_10, - GPIO_AD_11, - GPIO_AD_12, - GPIO_AD_13, - GPIO_AD_14, - GPIO_SD_00, - GPIO_SD_01, - GPIO_SD_02, - GPIO_SD_03, - GPIO_SD_04, - GPIO_SD_05, - GPIO_SD_06, - GPIO_SD_07, - GPIO_SD_08, - GPIO_SD_09, - GPIO_SD_10, - GPIO_SD_11, - GPIO_SD_12, - GPIO_SD_13, - PMIC_ON_REQ, -} - -impl_gpio! { - // GPIO Bank 1 - GPIO_00(Gpio1, 0); - GPIO_01(Gpio1, 1); - GPIO_02(Gpio1, 2); - GPIO_03(Gpio1, 3); - GPIO_04(Gpio1, 4); - GPIO_05(Gpio1, 5); - GPIO_06(Gpio1, 6); - GPIO_07(Gpio1, 7); - GPIO_08(Gpio1, 8); - GPIO_09(Gpio1, 9); - GPIO_10(Gpio1, 10); - GPIO_11(Gpio1, 11); - GPIO_12(Gpio1, 12); - GPIO_13(Gpio1, 13); - GPIO_AD_00(Gpio1, 14); - GPIO_AD_01(Gpio1, 15); - GPIO_AD_02(Gpio1, 16); - GPIO_AD_03(Gpio1, 17); - GPIO_AD_04(Gpio1, 18); - GPIO_AD_05(Gpio1, 19); - GPIO_AD_06(Gpio1, 20); - GPIO_AD_07(Gpio1, 21); - GPIO_AD_08(Gpio1, 22); - GPIO_AD_09(Gpio1, 23); - GPIO_AD_10(Gpio1, 24); - GPIO_AD_11(Gpio1, 25); - GPIO_AD_12(Gpio1, 26); - GPIO_AD_13(Gpio1, 27); - GPIO_AD_14(Gpio1, 28); - - // GPIO Bank 2 - GPIO_SD_00(Gpio2, 0); - GPIO_SD_01(Gpio2, 1); - GPIO_SD_02(Gpio2, 2); - GPIO_SD_03(Gpio2, 3); - GPIO_SD_04(Gpio2, 4); - GPIO_SD_05(Gpio2, 5); - GPIO_SD_06(Gpio2, 6); - GPIO_SD_07(Gpio2, 7); - GPIO_SD_08(Gpio2, 8); - GPIO_SD_09(Gpio2, 9); - GPIO_SD_10(Gpio2, 10); - GPIO_SD_11(Gpio2, 11); - GPIO_SD_12(Gpio2, 12); - GPIO_SD_13(Gpio2, 13); - - // GPIO Bank 5 - PMIC_ON_REQ(Gpio5, 0); -} pub(crate) mod _generated { #![allow(dead_code)] @@ -111,3 +9,5 @@ pub(crate) mod _generated { include!(concat!(env!("OUT_DIR"), "/_generated.rs")); } + +pub use _generated::*; diff --git a/embassy-nxp/src/chips/mimxrt1062.rs b/embassy-nxp/src/chips/mimxrt1062.rs index ef153bd66..d5969a24b 100644 --- a/embassy-nxp/src/chips/mimxrt1062.rs +++ b/embassy-nxp/src/chips/mimxrt1062.rs @@ -1,276 +1,5 @@ // This must be imported so that __preinit is defined. use imxrt_rt as _; -pub use nxp_pac as pac; - -embassy_hal_internal::peripherals! { - // External pins. These are not only GPIOs, they are multi-purpose pins and can be used by other - // peripheral types (e.g. I2C). - GPIO_AD_B0_00, - GPIO_AD_B0_01, - GPIO_AD_B0_02, - GPIO_AD_B0_03, - GPIO_AD_B0_04, - GPIO_AD_B0_05, - GPIO_AD_B0_06, - GPIO_AD_B0_07, - GPIO_AD_B0_08, - GPIO_AD_B0_09, - GPIO_AD_B0_10, - GPIO_AD_B0_11, - GPIO_AD_B0_12, - GPIO_AD_B0_13, - GPIO_AD_B0_14, - GPIO_AD_B0_15, - GPIO_AD_B1_00, - GPIO_AD_B1_01, - GPIO_AD_B1_02, - GPIO_AD_B1_03, - GPIO_AD_B1_04, - GPIO_AD_B1_05, - GPIO_AD_B1_06, - GPIO_AD_B1_07, - GPIO_AD_B1_08, - GPIO_AD_B1_09, - GPIO_AD_B1_10, - GPIO_AD_B1_11, - GPIO_AD_B1_12, - GPIO_AD_B1_13, - GPIO_AD_B1_14, - GPIO_AD_B1_15, - GPIO_B0_00, - GPIO_B0_01, - GPIO_B0_02, - GPIO_B0_03, - GPIO_B0_04, - GPIO_B0_05, - GPIO_B0_06, - GPIO_B0_07, - GPIO_B0_08, - GPIO_B0_09, - GPIO_B0_10, - GPIO_B0_11, - GPIO_B0_12, - GPIO_B0_13, - GPIO_B0_14, - GPIO_B0_15, - GPIO_B1_00, - GPIO_B1_01, - GPIO_B1_02, - GPIO_B1_03, - GPIO_B1_04, - GPIO_B1_05, - GPIO_B1_06, - GPIO_B1_07, - GPIO_B1_08, - GPIO_B1_09, - GPIO_B1_10, - GPIO_B1_11, - GPIO_B1_12, - GPIO_B1_13, - GPIO_B1_14, - GPIO_B1_15, - GPIO_EMC_00, - GPIO_EMC_01, - GPIO_EMC_02, - GPIO_EMC_03, - GPIO_EMC_04, - GPIO_EMC_05, - GPIO_EMC_06, - GPIO_EMC_07, - GPIO_EMC_08, - GPIO_EMC_09, - GPIO_EMC_10, - GPIO_EMC_11, - GPIO_EMC_12, - GPIO_EMC_13, - GPIO_EMC_14, - GPIO_EMC_15, - GPIO_EMC_16, - GPIO_EMC_17, - GPIO_EMC_18, - GPIO_EMC_19, - GPIO_EMC_20, - GPIO_EMC_21, - GPIO_EMC_22, - GPIO_EMC_23, - GPIO_EMC_24, - GPIO_EMC_25, - GPIO_EMC_26, - GPIO_EMC_27, - GPIO_EMC_28, - GPIO_EMC_29, - GPIO_EMC_30, - GPIO_EMC_31, - GPIO_EMC_32, - GPIO_EMC_33, - GPIO_EMC_34, - GPIO_EMC_35, - GPIO_EMC_36, - GPIO_EMC_37, - GPIO_EMC_38, - GPIO_EMC_39, - GPIO_EMC_40, - GPIO_EMC_41, - GPIO_SD_B0_00, - GPIO_SD_B0_01, - GPIO_SD_B0_02, - GPIO_SD_B0_03, - GPIO_SD_B0_04, - GPIO_SD_B0_05, - GPIO_SD_B1_00, - GPIO_SD_B1_01, - GPIO_SD_B1_02, - GPIO_SD_B1_03, - GPIO_SD_B1_04, - GPIO_SD_B1_05, - GPIO_SD_B1_06, - GPIO_SD_B1_07, - GPIO_SD_B1_08, - GPIO_SD_B1_09, - GPIO_SD_B1_10, - GPIO_SD_B1_11, - WAKEUP, - PMIC_ON_REQ, - PMIC_STBY_REQ, -} - -impl_gpio! { - // GPIO Bank 1 - GPIO_AD_B0_00(Gpio1, 0); - GPIO_AD_B0_01(Gpio1, 1); - GPIO_AD_B0_02(Gpio1, 2); - GPIO_AD_B0_03(Gpio1, 3); - GPIO_AD_B0_04(Gpio1, 4); - GPIO_AD_B0_05(Gpio1, 5); - GPIO_AD_B0_06(Gpio1, 6); - GPIO_AD_B0_07(Gpio1, 7); - GPIO_AD_B0_08(Gpio1, 8); - GPIO_AD_B0_09(Gpio1, 9); - GPIO_AD_B0_10(Gpio1, 10); - GPIO_AD_B0_11(Gpio1, 11); - GPIO_AD_B0_12(Gpio1, 12); - GPIO_AD_B0_13(Gpio1, 13); - GPIO_AD_B0_14(Gpio1, 14); - GPIO_AD_B0_15(Gpio1, 15); - GPIO_AD_B1_00(Gpio1, 16); - GPIO_AD_B1_01(Gpio1, 17); - GPIO_AD_B1_02(Gpio1, 18); - GPIO_AD_B1_03(Gpio1, 19); - GPIO_AD_B1_04(Gpio1, 20); - GPIO_AD_B1_05(Gpio1, 21); - GPIO_AD_B1_06(Gpio1, 22); - GPIO_AD_B1_07(Gpio1, 23); - GPIO_AD_B1_08(Gpio1, 24); - GPIO_AD_B1_09(Gpio1, 25); - GPIO_AD_B1_10(Gpio1, 26); - GPIO_AD_B1_11(Gpio1, 27); - GPIO_AD_B1_12(Gpio1, 28); - GPIO_AD_B1_13(Gpio1, 29); - GPIO_AD_B1_14(Gpio1, 30); - GPIO_AD_B1_15(Gpio1, 31); - - // GPIO Bank 2 - GPIO_B0_00(Gpio2, 0); - GPIO_B0_01(Gpio2, 1); - GPIO_B0_02(Gpio2, 2); - GPIO_B0_03(Gpio2, 3); - GPIO_B0_04(Gpio2, 4); - GPIO_B0_05(Gpio2, 5); - GPIO_B0_06(Gpio2, 6); - GPIO_B0_07(Gpio2, 7); - GPIO_B0_08(Gpio2, 8); - GPIO_B0_09(Gpio2, 9); - GPIO_B0_10(Gpio2, 10); - GPIO_B0_11(Gpio2, 11); - GPIO_B0_12(Gpio2, 12); - GPIO_B0_13(Gpio2, 13); - GPIO_B0_14(Gpio2, 14); - GPIO_B0_15(Gpio2, 15); - GPIO_B1_00(Gpio2, 16); - GPIO_B1_01(Gpio2, 17); - GPIO_B1_02(Gpio2, 18); - GPIO_B1_03(Gpio2, 19); - GPIO_B1_04(Gpio2, 20); - GPIO_B1_05(Gpio2, 21); - GPIO_B1_06(Gpio2, 22); - GPIO_B1_07(Gpio2, 23); - GPIO_B1_08(Gpio2, 24); - GPIO_B1_09(Gpio2, 25); - GPIO_B1_10(Gpio2, 26); - GPIO_B1_11(Gpio2, 27); - GPIO_B1_12(Gpio2, 28); - GPIO_B1_13(Gpio2, 29); - GPIO_B1_14(Gpio2, 30); - GPIO_B1_15(Gpio2, 31); - - // GPIO Bank 4 (EMC is 4, then 3) - GPIO_EMC_00(Gpio4, 0); - GPIO_EMC_01(Gpio4, 1); - GPIO_EMC_02(Gpio4, 2); - GPIO_EMC_03(Gpio4, 3); - GPIO_EMC_04(Gpio4, 4); - GPIO_EMC_05(Gpio4, 5); - GPIO_EMC_06(Gpio4, 6); - GPIO_EMC_07(Gpio4, 7); - GPIO_EMC_08(Gpio4, 8); - GPIO_EMC_09(Gpio4, 9); - GPIO_EMC_10(Gpio4, 10); - GPIO_EMC_11(Gpio4, 11); - GPIO_EMC_12(Gpio4, 12); - GPIO_EMC_13(Gpio4, 13); - GPIO_EMC_14(Gpio4, 14); - GPIO_EMC_15(Gpio4, 15); - GPIO_EMC_16(Gpio4, 16); - GPIO_EMC_17(Gpio4, 17); - GPIO_EMC_18(Gpio4, 18); - GPIO_EMC_19(Gpio4, 19); - GPIO_EMC_20(Gpio4, 20); - GPIO_EMC_21(Gpio4, 21); - GPIO_EMC_22(Gpio4, 22); - GPIO_EMC_23(Gpio4, 23); - GPIO_EMC_24(Gpio4, 24); - GPIO_EMC_25(Gpio4, 25); - GPIO_EMC_26(Gpio4, 26); - GPIO_EMC_27(Gpio4, 27); - GPIO_EMC_28(Gpio4, 28); - GPIO_EMC_29(Gpio4, 29); - GPIO_EMC_30(Gpio4, 30); - GPIO_EMC_31(Gpio4, 31); - - // GPIO Bank 3 - GPIO_EMC_32(Gpio3, 18); - GPIO_EMC_33(Gpio3, 19); - GPIO_EMC_34(Gpio3, 20); - GPIO_EMC_35(Gpio3, 21); - GPIO_EMC_36(Gpio3, 22); - GPIO_EMC_37(Gpio3, 23); - GPIO_EMC_38(Gpio3, 24); - GPIO_EMC_39(Gpio3, 25); - GPIO_EMC_40(Gpio3, 26); - GPIO_EMC_41(Gpio3, 27); - GPIO_SD_B0_00(Gpio3, 12); - GPIO_SD_B0_01(Gpio3, 13); - GPIO_SD_B0_02(Gpio3, 14); - GPIO_SD_B0_03(Gpio3, 15); - GPIO_SD_B0_04(Gpio3, 16); - GPIO_SD_B0_05(Gpio3, 17); - GPIO_SD_B1_00(Gpio3, 0); - GPIO_SD_B1_01(Gpio3, 1); - GPIO_SD_B1_02(Gpio3, 2); - GPIO_SD_B1_03(Gpio3, 3); - GPIO_SD_B1_04(Gpio3, 4); - GPIO_SD_B1_05(Gpio3, 5); - GPIO_SD_B1_06(Gpio3, 6); - GPIO_SD_B1_07(Gpio3, 7); - GPIO_SD_B1_08(Gpio3, 8); - GPIO_SD_B1_09(Gpio3, 9); - GPIO_SD_B1_10(Gpio3, 10); - GPIO_SD_B1_11(Gpio3, 11); - - WAKEUP(Gpio5, 0); - PMIC_ON_REQ(Gpio5, 1); - PMIC_STBY_REQ(Gpio5, 2); -} pub(crate) mod _generated { #![allow(dead_code)] @@ -280,3 +9,5 @@ pub(crate) mod _generated { include!(concat!(env!("OUT_DIR"), "/_generated.rs")); } + +pub use _generated::*; diff --git a/embassy-nxp/src/dma.rs b/embassy-nxp/src/dma.rs index e2df65fc9..1f479122d 100644 --- a/embassy-nxp/src/dma.rs +++ b/embassy-nxp/src/dma.rs @@ -1,3 +1,4 @@ +#![macro_use] //! Direct Memory Access (DMA) driver. #[cfg_attr(feature = "lpc55-core0", path = "./dma/lpc55.rs")] diff --git a/embassy-nxp/src/dma/lpc55.rs b/embassy-nxp/src/dma/lpc55.rs index 5bd763f03..623644bf1 100644 --- a/embassy-nxp/src/dma/lpc55.rs +++ b/embassy-nxp/src/dma/lpc55.rs @@ -1,3 +1,5 @@ +#![macro_use] + use core::cell::RefCell; use core::future::Future; use core::pin::Pin; @@ -9,9 +11,12 @@ use embassy_hal_internal::interrupt::InterruptExt; use embassy_hal_internal::{PeripheralType, impl_peripheral}; use embassy_sync::waitqueue::AtomicWaker; -use crate::pac::{DMA0, SYSCON, *}; -use crate::{Peri, peripherals}; +use crate::Peri; +#[cfg(feature = "rt")] +use crate::pac::interrupt; +use crate::pac::{SYSCON, *}; +#[cfg(feature = "rt")] #[interrupt] fn DMA0() { let inta = DMA0.inta0().read().ia(); @@ -278,7 +283,7 @@ static DMA_DESCRIPTORS: Mutex> = Mutex::new(RefCell: }; CHANNEL_COUNT], })); -trait SealedChannel {} +pub(crate) trait SealedChannel {} trait SealedWord {} /// DMA channel interface. @@ -323,7 +328,7 @@ impl Word for u32 { /// Type erased DMA channel. pub struct AnyChannel { - number: u8, + pub(crate) number: u8, } impl_peripheral!(AnyChannel); @@ -335,10 +340,10 @@ impl Channel for AnyChannel { } } -macro_rules! channel { - ($name:ident, $num:expr) => { - impl SealedChannel for peripherals::$name {} - impl Channel for peripherals::$name { +macro_rules! impl_dma_channel { + ($instance:ident, $name:ident, $num:expr) => { + impl crate::dma::SealedChannel for crate::peripherals::$name {} + impl crate::dma::Channel for crate::peripherals::$name { fn number(&self) -> u8 { $num } @@ -346,32 +351,10 @@ macro_rules! channel { impl From for crate::dma::AnyChannel { fn from(val: peripherals::$name) -> Self { + use crate::dma::Channel; + Self { number: val.number() } } } }; } - -channel!(DMA_CH0, 0); -channel!(DMA_CH1, 1); -channel!(DMA_CH2, 2); -channel!(DMA_CH3, 3); -channel!(DMA_CH4, 4); -channel!(DMA_CH5, 5); -channel!(DMA_CH6, 6); -channel!(DMA_CH7, 7); -channel!(DMA_CH8, 8); -channel!(DMA_CH9, 9); -channel!(DMA_CH10, 10); -channel!(DMA_CH11, 11); -channel!(DMA_CH12, 12); -channel!(DMA_CH13, 13); -channel!(DMA_CH14, 14); -channel!(DMA_CH15, 15); -channel!(DMA_CH16, 16); -channel!(DMA_CH17, 17); -channel!(DMA_CH18, 18); -channel!(DMA_CH19, 19); -channel!(DMA_CH20, 20); -channel!(DMA_CH21, 21); -channel!(DMA_CH22, 22); diff --git a/embassy-nxp/src/gpio/lpc55.rs b/embassy-nxp/src/gpio/lpc55.rs index 6039d8ca8..6be405463 100644 --- a/embassy-nxp/src/gpio/lpc55.rs +++ b/embassy-nxp/src/gpio/lpc55.rs @@ -1,9 +1,11 @@ +#![macro_use] + use embassy_hal_internal::{PeripheralType, impl_peripheral}; +use crate::Peri; use crate::pac::common::{RW, Reg}; use crate::pac::iocon::vals::{PioDigimode, PioMode}; use crate::pac::{GPIO, IOCON, SYSCON, iocon}; -use crate::{Peri, peripherals}; pub(crate) fn init() { // Enable clocks for GPIO, PINT, and IOCON @@ -39,8 +41,8 @@ pub enum Pull { /// The LPC55 boards have two GPIO banks, each with 32 pins. This enum represents the two banks. #[derive(Debug, Eq, PartialEq, Clone, Copy)] pub enum Bank { - Bank0 = 0, - Bank1 = 1, + Gpio0 = 0, + Gpio1 = 1, } /// GPIO output driver. Internally, this is a specialized [Flex] pin. @@ -228,8 +230,8 @@ pub(crate) trait SealedPin: Sized { #[inline] fn pio(&self) -> Reg { match self.pin_bank() { - Bank::Bank0 => IOCON.pio0(self.pin_number() as usize), - Bank::Bank1 => IOCON.pio1(self.pin_number() as usize), + Bank::Gpio0 => IOCON.pio0(self.pin_number() as usize), + Bank::Gpio1 => IOCON.pio1(self.pin_number() as usize), } } } @@ -254,8 +256,8 @@ pub trait Pin: PeripheralType + Into + SealedPin + Sized + 'static { /// Type-erased GPIO pin. pub struct AnyPin { - pin_bank: Bank, - pin_number: u8, + pub(crate) pin_bank: Bank, + pub(crate) pin_number: u8, } impl AnyPin { @@ -285,12 +287,12 @@ impl SealedPin for AnyPin { } macro_rules! impl_pin { - ($name:ident, $bank:expr, $pin_num:expr) => { - impl Pin for peripherals::$name {} - impl SealedPin for peripherals::$name { + ($name:ident, $bank:ident, $pin_num:expr) => { + impl crate::gpio::Pin for peripherals::$name {} + impl crate::gpio::SealedPin for peripherals::$name { #[inline] - fn pin_bank(&self) -> Bank { - $bank + fn pin_bank(&self) -> crate::gpio::Bank { + crate::gpio::Bank::$bank } #[inline] @@ -301,6 +303,8 @@ macro_rules! impl_pin { impl From for crate::gpio::AnyPin { fn from(val: peripherals::$name) -> Self { + use crate::gpio::SealedPin; + Self { pin_bank: val.pin_bank(), pin_number: val.pin_number(), @@ -309,68 +313,3 @@ macro_rules! impl_pin { } }; } - -impl_pin!(PIO0_0, Bank::Bank0, 0); -impl_pin!(PIO0_1, Bank::Bank0, 1); -impl_pin!(PIO0_2, Bank::Bank0, 2); -impl_pin!(PIO0_3, Bank::Bank0, 3); -impl_pin!(PIO0_4, Bank::Bank0, 4); -impl_pin!(PIO0_5, Bank::Bank0, 5); -impl_pin!(PIO0_6, Bank::Bank0, 6); -impl_pin!(PIO0_7, Bank::Bank0, 7); -impl_pin!(PIO0_8, Bank::Bank0, 8); -impl_pin!(PIO0_9, Bank::Bank0, 9); -impl_pin!(PIO0_10, Bank::Bank0, 10); -impl_pin!(PIO0_11, Bank::Bank0, 11); -impl_pin!(PIO0_12, Bank::Bank0, 12); -impl_pin!(PIO0_13, Bank::Bank0, 13); -impl_pin!(PIO0_14, Bank::Bank0, 14); -impl_pin!(PIO0_15, Bank::Bank0, 15); -impl_pin!(PIO0_16, Bank::Bank0, 16); -impl_pin!(PIO0_17, Bank::Bank0, 17); -impl_pin!(PIO0_18, Bank::Bank0, 18); -impl_pin!(PIO0_19, Bank::Bank0, 19); -impl_pin!(PIO0_20, Bank::Bank0, 20); -impl_pin!(PIO0_21, Bank::Bank0, 21); -impl_pin!(PIO0_22, Bank::Bank0, 22); -impl_pin!(PIO0_23, Bank::Bank0, 23); -impl_pin!(PIO0_24, Bank::Bank0, 24); -impl_pin!(PIO0_25, Bank::Bank0, 25); -impl_pin!(PIO0_26, Bank::Bank0, 26); -impl_pin!(PIO0_27, Bank::Bank0, 27); -impl_pin!(PIO0_28, Bank::Bank0, 28); -impl_pin!(PIO0_29, Bank::Bank0, 29); -impl_pin!(PIO0_30, Bank::Bank0, 30); -impl_pin!(PIO0_31, Bank::Bank0, 31); -impl_pin!(PIO1_0, Bank::Bank1, 0); -impl_pin!(PIO1_1, Bank::Bank1, 1); -impl_pin!(PIO1_2, Bank::Bank1, 2); -impl_pin!(PIO1_3, Bank::Bank1, 3); -impl_pin!(PIO1_4, Bank::Bank1, 4); -impl_pin!(PIO1_5, Bank::Bank1, 5); -impl_pin!(PIO1_6, Bank::Bank1, 6); -impl_pin!(PIO1_7, Bank::Bank1, 7); -impl_pin!(PIO1_8, Bank::Bank1, 8); -impl_pin!(PIO1_9, Bank::Bank1, 9); -impl_pin!(PIO1_10, Bank::Bank1, 10); -impl_pin!(PIO1_11, Bank::Bank1, 11); -impl_pin!(PIO1_12, Bank::Bank1, 12); -impl_pin!(PIO1_13, Bank::Bank1, 13); -impl_pin!(PIO1_14, Bank::Bank1, 14); -impl_pin!(PIO1_15, Bank::Bank1, 15); -impl_pin!(PIO1_16, Bank::Bank1, 16); -impl_pin!(PIO1_17, Bank::Bank1, 17); -impl_pin!(PIO1_18, Bank::Bank1, 18); -impl_pin!(PIO1_19, Bank::Bank1, 19); -impl_pin!(PIO1_20, Bank::Bank1, 20); -impl_pin!(PIO1_21, Bank::Bank1, 21); -impl_pin!(PIO1_22, Bank::Bank1, 22); -impl_pin!(PIO1_23, Bank::Bank1, 23); -impl_pin!(PIO1_24, Bank::Bank1, 24); -impl_pin!(PIO1_25, Bank::Bank1, 25); -impl_pin!(PIO1_26, Bank::Bank1, 26); -impl_pin!(PIO1_27, Bank::Bank1, 27); -impl_pin!(PIO1_28, Bank::Bank1, 28); -impl_pin!(PIO1_29, Bank::Bank1, 29); -impl_pin!(PIO1_30, Bank::Bank1, 30); -impl_pin!(PIO1_31, Bank::Bank1, 31); diff --git a/embassy-nxp/src/gpio/rt1xxx.rs b/embassy-nxp/src/gpio/rt1xxx.rs index c4dc110ff..8a560310c 100644 --- a/embassy-nxp/src/gpio/rt1xxx.rs +++ b/embassy-nxp/src/gpio/rt1xxx.rs @@ -10,7 +10,7 @@ use embassy_sync::waitqueue::AtomicWaker; use nxp_pac::gpio::vals::Icr; use nxp_pac::iomuxc::vals::Pus; -use crate::chip::{mux_address, pad_address}; +use crate::chip::{iomuxc_mux, iomuxc_pad}; use crate::pac::common::{RW, Reg}; use crate::pac::gpio::Gpio; #[cfg(feature = "rt")] @@ -121,6 +121,10 @@ pub enum Bank { /// Bank 5 #[cfg(gpio5)] Gpio5, + + #[cfg(gpio10)] + /// Bank 10 + Gpio10, } /// GPIO flexible pin. @@ -656,6 +660,8 @@ static GPIO3_WAKERS: [AtomicWaker; 32] = [const { AtomicWaker::new() }; 32]; static GPIO4_WAKERS: [AtomicWaker; 32] = [const { AtomicWaker::new() }; 32]; #[cfg(gpio5)] static GPIO5_WAKERS: [AtomicWaker; 32] = [const { AtomicWaker::new() }; 32]; +#[cfg(gpio10)] +static GPIO10_WAKERS: [AtomicWaker; 32] = [const { AtomicWaker::new() }; 32]; /// Sealed trait for pins. This trait is sealed and cannot be implemented outside of this crate. pub(crate) trait SealedPin: Sized { @@ -676,13 +682,15 @@ pub(crate) trait SealedPin: Sized { Bank::Gpio4 => pac::GPIO4, #[cfg(gpio5)] Bank::Gpio5 => pac::GPIO5, + #[cfg(gpio10)] + Bank::Gpio10 => pac::GPIO10, } } #[inline] fn mux(&self) -> Reg { // SAFETY: The generated mux address table is valid since it is generated from the SVD files. - let address = unsafe { mux_address(self._bank(), self.pin_number()).unwrap_unchecked() }; + let address = unsafe { iomuxc_mux(self._bank(), self.pin_number()).unwrap_unchecked() }; // SAFETY: The register at the address is an instance of MuxCtl. unsafe { Reg::from_ptr(address as *mut _) } @@ -690,8 +698,7 @@ pub(crate) trait SealedPin: Sized { #[inline] fn pad(&self) -> Reg { - // SAFETY: The generated pad address table is valid since it is generated from the SVD files. - let address = unsafe { pad_address(self._bank(), self.pin_number()).unwrap_unchecked() }; + let address = iomuxc_pad(self._bank(), self.pin_number()); // SAFETY: The register at the address is an instance of Ctl. unsafe { Reg::from_ptr(address as *mut _) } @@ -709,6 +716,8 @@ pub(crate) trait SealedPin: Sized { Bank::Gpio4 => &GPIO4_WAKERS[self.pin_number() as usize], #[cfg(gpio5)] Bank::Gpio5 => &GPIO5_WAKERS[self.pin_number() as usize], + #[cfg(gpio10)] + Bank::Gpio10 => &GPIO10_WAKERS[self.pin_number() as usize], } } } @@ -793,39 +802,6 @@ impl<'d> Future for InputFuture<'d> { } } -/// A macro to generate all GPIO pins. -/// -/// This generates a lookup table for IOMUX register addresses. -macro_rules! impl_gpio { - ( - $($name: ident($bank: ident, $pin_number: expr);)* - ) => { - #[inline] - pub(crate) const fn pad_address(bank: crate::gpio::Bank, pin: u8) -> Option { - match (bank, pin) { - $( - (crate::gpio::Bank::$bank, $pin_number) => Some(crate::chip::_generated::iomuxc::pads::$name), - )* - _ => None - } - } - - #[inline] - pub(crate) const fn mux_address(bank: crate::gpio::Bank, pin: u8) -> Option { - match (bank, pin) { - $( - (crate::gpio::Bank::$bank, $pin_number) => Some(crate::chip::_generated::iomuxc::muxes::$name), - )* - _ => None - } - } - - $( - impl_pin!($name, $bank, $pin_number); - )* - }; -} - macro_rules! impl_pin { ($name: ident, $bank: ident, $pin_num: expr) => { impl crate::gpio::Pin for crate::peripherals::$name {} diff --git a/embassy-nxp/src/iomuxc.rs b/embassy-nxp/src/iomuxc.rs new file mode 100644 index 000000000..c015ecbc2 --- /dev/null +++ b/embassy-nxp/src/iomuxc.rs @@ -0,0 +1,29 @@ +#![macro_use] + +/// An IOMUXC pad. +/// +/// This trait does not imply that GPIO can be used with this pad. [`Pin`](crate::gpio::Pin) must +/// also be implemented for GPIO. +#[allow(private_bounds)] +pub trait Pad: SealedPad {} + +pub(crate) trait SealedPad { + /// Address of the pad register for this pad. + const PAD: *mut (); + + /// Address of the mux register for this pad. + /// + /// Some pads do not allow muxing (e.g. ONOFF). + const MUX: Option<*mut ()>; +} + +macro_rules! impl_iomuxc_pad { + ($name: ident, $pad: expr, $mux: expr) => { + impl crate::iomuxc::SealedPad for crate::peripherals::$name { + const PAD: *mut () = $pad as *mut (); + const MUX: Option<*mut ()> = Some($mux as *mut ()); + } + + impl crate::iomuxc::Pad for crate::peripherals::$name {} + }; +} diff --git a/embassy-nxp/src/lib.rs b/embassy-nxp/src/lib.rs index 4058881a5..4c3dbebb9 100644 --- a/embassy-nxp/src/lib.rs +++ b/embassy-nxp/src/lib.rs @@ -12,8 +12,13 @@ pub mod pint; #[cfg(feature = "lpc55-core0")] pub mod pwm; #[cfg(feature = "lpc55-core0")] +pub mod sct; +#[cfg(feature = "lpc55-core0")] pub mod usart; +#[cfg(rt1xxx)] +mod iomuxc; + #[cfg(feature = "_time_driver")] #[cfg_attr(feature = "time-driver-pit", path = "time_driver/pit.rs")] #[cfg_attr(feature = "time-driver-rtc", path = "time_driver/rtc.rs")] @@ -25,15 +30,12 @@ mod time_driver; #[cfg_attr(feature = "mimxrt1062", path = "chips/mimxrt1062.rs")] mod chip; -// TODO: Remove when this module is implemented for other chips -#[cfg(feature = "lpc55-core0")] -pub use chip::interrupt; +pub use chip::{Peripherals, interrupt, peripherals}; +pub use embassy_hal_internal::{Peri, PeripheralType}; #[cfg(feature = "unstable-pac")] -pub use chip::pac; +pub use nxp_pac as pac; #[cfg(not(feature = "unstable-pac"))] -pub(crate) use chip::pac; -pub use chip::{Peripherals, peripherals}; -pub use embassy_hal_internal::{Peri, PeripheralType}; +pub(crate) use nxp_pac as pac; /// Macro to bind interrupts to handlers. /// (Copied from `embassy-rp`) diff --git a/embassy-nxp/src/pwm.rs b/embassy-nxp/src/pwm.rs index 68980924a..c87a39c34 100644 --- a/embassy-nxp/src/pwm.rs +++ b/embassy-nxp/src/pwm.rs @@ -1,3 +1,5 @@ +#![macro_use] + //! Pulse-Width Modulation (PWM) driver. #[cfg_attr(feature = "lpc55-core0", path = "./pwm/lpc55.rs")] diff --git a/embassy-nxp/src/pwm/lpc55.rs b/embassy-nxp/src/pwm/lpc55.rs index 197184ad6..4cdbd8526 100644 --- a/embassy-nxp/src/pwm/lpc55.rs +++ b/embassy-nxp/src/pwm/lpc55.rs @@ -1,12 +1,15 @@ +#![macro_use] + use core::sync::atomic::{AtomicU8, AtomicU32, Ordering}; -use embassy_hal_internal::{Peri, PeripheralType}; +use embassy_hal_internal::Peri; use crate::gpio::AnyPin; -use crate::pac::iocon::vals::{PioDigimode, PioFunc, PioMode, PioOd, PioSlew}; +use crate::pac::iocon::vals::{PioDigimode, PioMode, PioOd, PioSlew}; use crate::pac::sct0::vals; use crate::pac::syscon::vals::{SctRst, SctclkselSel}; use crate::pac::{SCT0, SYSCON}; +use crate::sct; // Since for now the counter is shared, the TOP value has to be kept. static TOP_VALUE: AtomicU32 = AtomicU32::new(0); @@ -75,7 +78,11 @@ impl<'d> Pwm<'d> { SYSCON.presetctrl1().modify(|w| w.set_sct_rst(SctRst::ASSERTED)); SYSCON.presetctrl1().modify(|w| w.set_sct_rst(SctRst::RELEASED)); } - fn new_inner(output: usize, channel: Peri<'d, impl OutputChannelPin>, config: Config) -> Self { + fn new_inner>( + output: usize, + channel: Peri<'d, impl sct::OutputPin>, + config: Config, + ) -> Self { // Enable clocks (Syscon is enabled by default) critical_section::with(|_cs| { if !SYSCON.ahbclkctrl0().read().iocon() { @@ -109,12 +116,12 @@ impl<'d> Pwm<'d> { /// Create PWM driver with a single 'a' pin as output. #[inline] - pub fn new_output( - output: Peri<'d, T>, - channel: Peri<'d, impl OutputChannelPin>, + pub fn new_output>( + output: Peri<'d, O>, + channel: Peri<'d, impl sct::OutputPin>, config: Config, ) -> Self { - Self::new_inner(output.number(), channel, config) + Self::new_inner::(output.number(), channel, config) } /// Set the PWM config. @@ -196,18 +203,18 @@ impl<'d> Pwm<'d> { // TODO(frihetselsker): optimize nxp-pac so that `set_clr` and `set_set` are turned into a bit array. if config.invert { // Low when event 0 is active - SCT0.out(output_number).out_clr().modify(|w| w.set_clr(1 << 0)); + SCT0.out(output_number).out_clr().modify(|w| w.set_clr(0, true)); // High when event `output_number + 1` is active SCT0.out(output_number) .out_set() - .modify(|w| w.set_set(1 << (output_number + 1))); + .modify(|w| w.set_set(output_number, true)); } else { // High when event 0 is active - SCT0.out(output_number).out_set().modify(|w| w.set_set(1 << 0)); + SCT0.out(output_number).out_set().modify(|w| w.set_set(0, true)); // Low when event `output_number + 1` is active SCT0.out(output_number) .out_clr() - .modify(|w| w.set_clr(1 << (output_number + 1))); + .modify(|w| w.set_clr(output_number, true)); } if config.phase_correct { @@ -239,87 +246,3 @@ impl<'d> Drop for Pwm<'d> { } } } - -trait SealedOutput { - /// Output number. - fn number(&self) -> usize; -} - -/// PWM Output. -#[allow(private_bounds)] -pub trait Output: PeripheralType + SealedOutput {} - -macro_rules! output { - ($name:ident, $num:expr) => { - impl SealedOutput for crate::peripherals::$name { - fn number(&self) -> usize { - $num - } - } - impl Output for crate::peripherals::$name {} - }; -} - -output!(PWM_OUTPUT0, 0); -output!(PWM_OUTPUT1, 1); -output!(PWM_OUTPUT2, 2); -output!(PWM_OUTPUT3, 3); -output!(PWM_OUTPUT4, 4); -output!(PWM_OUTPUT5, 5); -output!(PWM_OUTPUT6, 6); -output!(PWM_OUTPUT7, 7); -output!(PWM_OUTPUT8, 8); -output!(PWM_OUTPUT9, 9); - -/// PWM Output Channel. -pub trait OutputChannelPin: crate::gpio::Pin { - fn pin_func(&self) -> PioFunc; -} - -macro_rules! impl_pin { - ($pin:ident, $output:ident, $func:ident) => { - impl crate::pwm::inner::OutputChannelPin for crate::peripherals::$pin { - fn pin_func(&self) -> PioFunc { - PioFunc::$func - } - } - }; -} - -impl_pin!(PIO0_2, PWM_OUTPUT0, ALT3); -impl_pin!(PIO0_17, PWM_OUTPUT0, ALT4); -impl_pin!(PIO1_4, PWM_OUTPUT0, ALT4); -impl_pin!(PIO1_23, PWM_OUTPUT0, ALT2); - -impl_pin!(PIO0_3, PWM_OUTPUT1, ALT3); -impl_pin!(PIO0_18, PWM_OUTPUT1, ALT4); -impl_pin!(PIO1_8, PWM_OUTPUT1, ALT4); -impl_pin!(PIO1_24, PWM_OUTPUT1, ALT2); - -impl_pin!(PIO0_10, PWM_OUTPUT2, ALT5); -impl_pin!(PIO0_15, PWM_OUTPUT2, ALT4); -impl_pin!(PIO0_19, PWM_OUTPUT2, ALT4); -impl_pin!(PIO1_9, PWM_OUTPUT2, ALT4); -impl_pin!(PIO1_25, PWM_OUTPUT2, ALT2); - -impl_pin!(PIO0_22, PWM_OUTPUT3, ALT4); -impl_pin!(PIO0_31, PWM_OUTPUT3, ALT4); -impl_pin!(PIO1_10, PWM_OUTPUT3, ALT4); -impl_pin!(PIO1_26, PWM_OUTPUT3, ALT2); - -impl_pin!(PIO0_23, PWM_OUTPUT4, ALT4); -impl_pin!(PIO1_3, PWM_OUTPUT4, ALT4); -impl_pin!(PIO1_17, PWM_OUTPUT4, ALT4); - -impl_pin!(PIO0_26, PWM_OUTPUT5, ALT4); -impl_pin!(PIO1_18, PWM_OUTPUT5, ALT4); - -impl_pin!(PIO0_27, PWM_OUTPUT6, ALT4); -impl_pin!(PIO1_31, PWM_OUTPUT6, ALT4); - -impl_pin!(PIO0_28, PWM_OUTPUT7, ALT4); -impl_pin!(PIO1_19, PWM_OUTPUT7, ALT2); - -impl_pin!(PIO0_29, PWM_OUTPUT8, ALT4); - -impl_pin!(PIO0_30, PWM_OUTPUT9, ALT4); diff --git a/embassy-nxp/src/sct.rs b/embassy-nxp/src/sct.rs new file mode 100644 index 000000000..b6b0e35a9 --- /dev/null +++ b/embassy-nxp/src/sct.rs @@ -0,0 +1,56 @@ +#![macro_use] + +use embassy_hal_internal::PeripheralType; +use nxp_pac::iocon::vals::PioFunc; + +use crate::gpio; + +/// SCT instance. +#[allow(private_bounds)] +pub trait Instance: SealedInstance + PeripheralType {} + +pub(crate) trait SealedInstance {} + +/// An SCT output. +#[allow(private_bounds)] +pub trait Output: SealedOutput + PeripheralType {} + +pub(crate) trait SealedOutput { + /// Output number. + fn number(&self) -> usize; +} + +/// An SCT output capable pin. +pub trait OutputPin>: gpio::Pin { + fn pin_func(&self) -> PioFunc; +} + +macro_rules! impl_sct_instance { + ($instance: ident) => { + impl crate::sct::SealedInstance for crate::peripherals::$instance {} + impl crate::sct::Instance for crate::peripherals::$instance {} + }; +} + +macro_rules! impl_sct_output_instance { + ($instance: ident, $name: ident, $num: expr) => { + impl crate::sct::SealedOutput for crate::peripherals::$name { + fn number(&self) -> usize { + $num as usize + } + } + impl crate::sct::Output for crate::peripherals::$name {} + }; +} + +macro_rules! impl_sct_output_pin { + ($instance: ident, $output_instance: ident, $pin: ident, $alt: ident) => { + impl crate::sct::OutputPin + for crate::peripherals::$pin + { + fn pin_func(&self) -> crate::pac::iocon::vals::PioFunc { + crate::pac::iocon::vals::PioFunc::$alt + } + } + }; +} diff --git a/embassy-nxp/src/usart.rs b/embassy-nxp/src/usart.rs index 1d8886f24..af039dee4 100644 --- a/embassy-nxp/src/usart.rs +++ b/embassy-nxp/src/usart.rs @@ -1,3 +1,5 @@ +#![macro_use] + //! Universal Synchronous/Asynchronous Receiver/Transmitter (USART) driver. #[cfg_attr(feature = "lpc55-core0", path = "./usart/lpc55.rs")] diff --git a/embassy-nxp/src/usart/lpc55.rs b/embassy-nxp/src/usart/lpc55.rs index d54927b25..d77f08fd8 100644 --- a/embassy-nxp/src/usart/lpc55.rs +++ b/embassy-nxp/src/usart/lpc55.rs @@ -1,3 +1,5 @@ +#![macro_use] + use core::fmt::Debug; use core::future::poll_fn; use core::marker::PhantomData; @@ -13,7 +15,7 @@ use embedded_io::{self, ErrorKind}; use crate::dma::{AnyChannel, Channel}; use crate::gpio::{AnyPin, SealedPin}; use crate::interrupt::Interrupt; -use crate::interrupt::typelevel::{Binding, Interrupt as _}; +use crate::interrupt::typelevel::Binding; use crate::pac::flexcomm::Flexcomm as FlexcommReg; use crate::pac::iocon::vals::PioFunc; use crate::pac::usart::Usart as UsartReg; @@ -113,8 +115,8 @@ impl Default for Config { /// Internal DMA state of UART RX. pub struct DmaState { - rx_err_waker: AtomicWaker, - rx_err: AtomicBool, + pub(crate) rx_err_waker: AtomicWaker, + pub(crate) rx_err: AtomicBool, } /// # Type parameters @@ -818,13 +820,13 @@ impl<'d> embedded_io::Read for Usart<'d, Blocking> { } } -struct Info { - usart_reg: UsartReg, - fc_reg: FlexcommReg, - interrupt: Interrupt, +pub(crate) struct Info { + pub(crate) usart_reg: UsartReg, + pub(crate) fc_reg: FlexcommReg, + pub(crate) interrupt: Interrupt, } -trait SealedInstance { +pub(crate) trait SealedInstance { fn info() -> &'static Info; fn dma_state() -> &'static DmaState; fn instance_number() -> usize; @@ -837,10 +839,13 @@ pub trait Instance: SealedInstance + PeripheralType { type Interrupt: crate::interrupt::typelevel::Interrupt; } -macro_rules! impl_instance { +macro_rules! impl_usart_instance { ($inst:ident, $fc:ident, $fc_num:expr) => { - impl $crate::usart::inner::SealedInstance for $crate::peripherals::$inst { - fn info() -> &'static Info { + impl crate::usart::SealedInstance for $crate::peripherals::$inst { + fn info() -> &'static crate::usart::Info { + use crate::interrupt::typelevel::Interrupt; + use crate::usart::Info; + static INFO: Info = Info { usart_reg: crate::pac::$inst, fc_reg: crate::pac::$fc, @@ -849,7 +854,13 @@ macro_rules! impl_instance { &INFO } - fn dma_state() -> &'static DmaState { + fn dma_state() -> &'static crate::usart::DmaState { + use core::sync::atomic::AtomicBool; + + use embassy_sync::waitqueue::AtomicWaker; + + use crate::usart::DmaState; + static STATE: DmaState = DmaState { rx_err_waker: AtomicWaker::new(), rx_err: AtomicBool::new(false), @@ -867,15 +878,6 @@ macro_rules! impl_instance { }; } -impl_instance!(USART0, FLEXCOMM0, 0); -impl_instance!(USART1, FLEXCOMM1, 1); -impl_instance!(USART2, FLEXCOMM2, 2); -impl_instance!(USART3, FLEXCOMM3, 3); -impl_instance!(USART4, FLEXCOMM4, 4); -impl_instance!(USART5, FLEXCOMM5, 5); -impl_instance!(USART6, FLEXCOMM6, 6); -impl_instance!(USART7, FLEXCOMM7, 7); - pub(crate) trait SealedTxPin: crate::gpio::Pin { fn pin_func(&self) -> PioFunc; } @@ -892,75 +894,46 @@ pub trait TxPin: SealedTxPin + crate::gpio::Pin {} #[allow(private_bounds)] pub trait RxPin: SealedRxPin + crate::gpio::Pin {} -macro_rules! impl_tx_pin { +macro_rules! impl_usart_txd_pin { ($pin:ident, $instance:ident, $func: ident) => { - impl SealedTxPin for crate::peripherals::$pin { - fn pin_func(&self) -> PioFunc { + impl crate::usart::SealedTxPin for crate::peripherals::$pin { + fn pin_func(&self) -> crate::pac::iocon::vals::PioFunc { + use crate::pac::iocon::vals::PioFunc; PioFunc::$func } } - impl TxPin for crate::peripherals::$pin {} + impl crate::usart::TxPin for crate::peripherals::$pin {} }; } -macro_rules! impl_rx_pin { +macro_rules! impl_usart_rxd_pin { ($pin:ident, $instance:ident, $func: ident) => { - impl SealedRxPin for crate::peripherals::$pin { - fn pin_func(&self) -> PioFunc { + impl crate::usart::SealedRxPin for crate::peripherals::$pin { + fn pin_func(&self) -> crate::pac::iocon::vals::PioFunc { + use crate::pac::iocon::vals::PioFunc; PioFunc::$func } } - impl RxPin for crate::peripherals::$pin {} + impl crate::usart::RxPin for crate::peripherals::$pin {} }; } -impl_tx_pin!(PIO1_6, USART0, ALT1); -impl_tx_pin!(PIO1_11, USART1, ALT2); -impl_tx_pin!(PIO0_27, USART2, ALT1); -impl_tx_pin!(PIO0_2, USART3, ALT1); -impl_tx_pin!(PIO0_16, USART4, ALT1); -impl_tx_pin!(PIO0_9, USART5, ALT3); -impl_tx_pin!(PIO1_16, USART6, ALT2); -impl_tx_pin!(PIO0_19, USART7, ALT7); - -impl_rx_pin!(PIO1_5, USART0, ALT1); -impl_rx_pin!(PIO1_10, USART1, ALT2); -impl_rx_pin!(PIO1_24, USART2, ALT1); -impl_rx_pin!(PIO0_3, USART3, ALT1); -impl_rx_pin!(PIO0_5, USART4, ALT2); -impl_rx_pin!(PIO0_8, USART5, ALT3); -impl_rx_pin!(PIO1_13, USART6, ALT2); -impl_rx_pin!(PIO0_20, USART7, ALT7); - -/// Trait for TX DMA channels. +/// Marker trait indicating a DMA channel may be used for USART transmit. pub trait TxChannel: crate::dma::Channel {} -/// Trait for RX DMA channels. + +/// Marker trait indicating a DMA channel may be used for USART recieve. pub trait RxChannel: crate::dma::Channel {} -macro_rules! impl_channel { - ($dma:ident, $instance:ident, Tx) => { - impl TxChannel for crate::peripherals::$dma {} - }; - ($dma:ident, $instance:ident, Rx) => { - impl RxChannel for crate::peripherals::$dma {} +macro_rules! impl_usart_tx_channel { + ($instance: ident, $channel: ident) => { + impl crate::usart::TxChannel for crate::peripherals::$channel {} }; } -impl_channel!(DMA_CH4, USART0, Rx); -impl_channel!(DMA_CH5, USART0, Tx); -impl_channel!(DMA_CH6, USART1, Rx); -impl_channel!(DMA_CH7, USART1, Tx); -impl_channel!(DMA_CH10, USART2, Rx); -impl_channel!(DMA_CH11, USART2, Tx); -impl_channel!(DMA_CH8, USART3, Rx); -impl_channel!(DMA_CH9, USART3, Tx); -impl_channel!(DMA_CH12, USART4, Rx); -impl_channel!(DMA_CH13, USART4, Tx); -impl_channel!(DMA_CH14, USART5, Rx); -impl_channel!(DMA_CH15, USART5, Tx); -impl_channel!(DMA_CH16, USART6, Rx); -impl_channel!(DMA_CH17, USART6, Tx); -impl_channel!(DMA_CH18, USART7, Rx); -impl_channel!(DMA_CH19, USART7, Tx); +macro_rules! impl_usart_rx_channel { + ($instance: ident, $channel: ident) => { + impl crate::usart::RxChannel for crate::peripherals::$channel {} + }; +} diff --git a/examples/lpc55s69/src/bin/pwm.rs b/examples/lpc55s69/src/bin/pwm.rs index 93b898b9d..8a9894b94 100644 --- a/examples/lpc55s69/src/bin/pwm.rs +++ b/examples/lpc55s69/src/bin/pwm.rs @@ -10,7 +10,7 @@ use {defmt_rtt as _, panic_halt as _}; #[embassy_executor::main] async fn main(_spawner: Spawner) { let p = embassy_nxp::init(Default::default()); - let pwm = Pwm::new_output(p.PWM_OUTPUT1, p.PIO0_18, Config::new(1_000_000_000, 2_000_000_000)); + let pwm = Pwm::new_output(p.SCT0_OUT1, p.PIO0_18, Config::new(1_000_000_000, 2_000_000_000)); loop { info!("Counter: {}", pwm.counter()); Timer::after_millis(50).await; diff --git a/examples/lpc55s69/src/bin/usart_async.rs b/examples/lpc55s69/src/bin/usart_async.rs index b06abd477..a9815b920 100644 --- a/examples/lpc55s69/src/bin/usart_async.rs +++ b/examples/lpc55s69/src/bin/usart_async.rs @@ -38,8 +38,8 @@ async fn main(spawner: Spawner) { p.PIO0_27, p.PIO1_24, Irqs, - p.DMA_CH11, - p.DMA_CH10, + p.DMA0_CH11, + p.DMA0_CH10, Config::default(), ); let led = Output::new(p.PIO1_6, Level::Low); -- cgit From 80ceb42eb1c842fcb214a5fbfbb1c39265c6b29b Mon Sep 17 00:00:00 2001 From: xoviat Date: Mon, 17 Nov 2025 08:23:34 -0600 Subject: wpan_ get net example actually working --- embassy-stm32-wpan/src/mac/control.rs | 36 +++++-------- embassy-stm32-wpan/src/mac/driver.rs | 12 ++++- embassy-stm32-wpan/src/mac/runner.rs | 24 +++++---- embassy-stm32-wpan/src/sub/mm.rs | 2 +- examples/stm32wb/src/bin/mac_ffd_net.rs | 90 +++++++++++++++++++++++++++------ 5 files changed, 115 insertions(+), 49 deletions(-) diff --git a/embassy-stm32-wpan/src/mac/control.rs b/embassy-stm32-wpan/src/mac/control.rs index 8fb971da3..d2a7b65ee 100644 --- a/embassy-stm32-wpan/src/mac/control.rs +++ b/embassy-stm32-wpan/src/mac/control.rs @@ -38,7 +38,7 @@ impl<'a> Control<'a> { } } - pub async fn init_link(&mut self, short_address: [u8; 2], extended_address: [u8; 8], pan_id: [u8; 2]) { + pub async fn init_link(&mut self, pan_id: [u8; 2]) { debug!("resetting"); debug!( @@ -52,12 +52,19 @@ impl<'a> Control<'a> { .await ); + let (short_address, mac_address) = critical_section::with(|cs| { + let mut network_state = self.network_state.borrow(cs).borrow_mut(); + + network_state.pan_id = pan_id; + + (network_state.short_addr, network_state.mac_addr) + }); + debug!("setting extended address"); - let extended_address: u64 = u64::from_be_bytes(extended_address); debug!( "{:#x}", self.send_command_and_get_response(&SetRequest { - pib_attribute_ptr: &extended_address as *const _ as *const u8, + pib_attribute_ptr: &u64::from_be_bytes(mac_address) as *const _ as *const u8, pib_attribute: PibId::ExtendedAddress, }) .await @@ -66,11 +73,10 @@ impl<'a> Control<'a> { ); debug!("setting short address"); - let short_address: u16 = u16::from_be_bytes(short_address); debug!( "{:#x}", self.send_command_and_get_response(&SetRequest { - pib_attribute_ptr: &short_address as *const _ as *const u8, + pib_attribute_ptr: &u16::from_be_bytes(short_address) as *const _ as *const u8, pib_attribute: PibId::ShortAddress, }) .await @@ -78,10 +84,6 @@ impl<'a> Control<'a> { .await ); - critical_section::with(|cs| { - self.network_state.borrow(cs).borrow_mut().mac_addr = extended_address.to_be_bytes(); - }); - debug!("setting association permit"); let association_permit: bool = true; debug!( @@ -186,20 +188,8 @@ impl<'a> Future for EventToken<'a> { type Output = MacEvent<'a>; fn poll(self: core::pin::Pin<&mut Self>, cx: &mut task::Context<'_>) -> Poll { - self.rx_event_channel.lock(|s| { - let signal = s.borrow_mut(); - let signal = match &*signal { - Some(s) => s, - _ => unreachable!(), - }; - - let result = match signal.wait().poll_unpin(cx) { - Poll::Ready(mac_event) => Poll::Ready(mac_event), - Poll::Pending => Poll::Pending, - }; - - result - }) + self.rx_event_channel + .lock(|s| s.borrow_mut().as_mut().unwrap().wait().poll_unpin(cx)) } } diff --git a/embassy-stm32-wpan/src/mac/driver.rs b/embassy-stm32-wpan/src/mac/driver.rs index 819299b48..5592723a2 100644 --- a/embassy-stm32-wpan/src/mac/driver.rs +++ b/embassy-stm32-wpan/src/mac/driver.rs @@ -17,6 +17,8 @@ use crate::sub::mac::{Mac, MacRx, MacTx}; pub struct NetworkState { pub mac_addr: [u8; 8], + pub short_addr: [u8; 2], + pub pan_id: [u8; 2], pub link_state: LinkState, pub link_waker: AtomicWaker, } @@ -25,6 +27,8 @@ impl NetworkState { pub const fn new() -> Self { Self { mac_addr: [0u8; 8], + short_addr: [0u8; 2], + pan_id: [0u8; 2], link_state: LinkState::Down, link_waker: AtomicWaker::new(), } @@ -68,7 +72,11 @@ pub struct Driver<'d> { } impl<'d> Driver<'d> { - pub fn new(driver_state: &'d mut DriverState<'d>) -> (Self, Runner<'d>, Control<'d>) { + pub fn new( + driver_state: &'d mut DriverState<'d>, + short_address: [u8; 2], + mac_address: [u8; 8], + ) -> (Self, Runner<'d>, Control<'d>) { ( Self { tx_data_channel: &driver_state.tx_data_channel, @@ -85,6 +93,8 @@ impl<'d> Driver<'d> { &driver_state.mac_tx, &mut driver_state.tx_buf_queue, &driver_state.network_state, + short_address, + mac_address, ), Control::new( &driver_state.rx_event_channel, diff --git a/embassy-stm32-wpan/src/mac/runner.rs b/embassy-stm32-wpan/src/mac/runner.rs index 26fdf23e0..92c74c2ee 100644 --- a/embassy-stm32-wpan/src/mac/runner.rs +++ b/embassy-stm32-wpan/src/mac/runner.rs @@ -34,7 +34,7 @@ pub struct Runner<'a> { } impl<'a> Runner<'a> { - pub fn new( + pub(crate) fn new( rx_event_channel: &'a ZeroCopyPubSub>, rx_data_channel: &'a Channel, 1>, mac_rx: &'a mut MacRx, @@ -43,11 +43,20 @@ impl<'a> Runner<'a> { mac_tx: &'a Mutex, tx_buf_queue: &'a mut [[u8; MTU]; BUF_SIZE], network_state: &'a blocking_mutex::Mutex>, + short_address: [u8; 2], + mac_address: [u8; 8], ) -> Self { for buf in tx_buf_queue { tx_buf_channel.try_send(buf).unwrap(); } + critical_section::with(|cs| { + let mut network_state = network_state.borrow(cs).borrow_mut(); + + network_state.mac_addr = mac_address; + network_state.short_addr = short_address; + }); + Self { rx_event_channel, rx_data_channel, @@ -70,12 +79,7 @@ impl<'a> Runner<'a> { } _ => { self.rx_event_channel.lock(|s| { - match &*s.borrow() { - Some(signal) => { - signal.signal(mac_event); - } - None => {} - }; + s.borrow().as_ref().map(|signal| signal.signal(mac_event)); }); } } @@ -89,7 +93,9 @@ impl<'a> Runner<'a> { let (buf, len) = self.tx_data_channel.receive().await; let mac_tx = self.mac_tx.lock().await; - // TODO: skip this if the link state is down + let pan_id = critical_section::with(|cs| self.network_state.borrow(cs).borrow().pan_id); + + // TODO: get the destination address from the packet instead of using the broadcast address // The mutex should be dropped on the next loop iteration mac_tx @@ -97,7 +103,7 @@ impl<'a> Runner<'a> { DataRequest { src_addr_mode: AddressMode::Short, dst_addr_mode: AddressMode::Short, - dst_pan_id: PanId([0x1A, 0xAA]), + dst_pan_id: PanId(pan_id), dst_address: MacAddress::BROADCAST, msdu_handle: msdu_handle, ack_tx: 0x00, diff --git a/embassy-stm32-wpan/src/sub/mm.rs b/embassy-stm32-wpan/src/sub/mm.rs index 62d0de8bd..a90c6ee55 100644 --- a/embassy-stm32-wpan/src/sub/mm.rs +++ b/embassy-stm32-wpan/src/sub/mm.rs @@ -46,7 +46,7 @@ impl MemoryManager { Self { _private: () } } - pub async fn run_queue(&self) { + pub async fn run_queue(&self) -> ! { loop { poll_fn(|cx| unsafe { MM_WAKER.register(cx.waker()); diff --git a/examples/stm32wb/src/bin/mac_ffd_net.rs b/examples/stm32wb/src/bin/mac_ffd_net.rs index 9b705dda9..5d946b35b 100644 --- a/examples/stm32wb/src/bin/mac_ffd_net.rs +++ b/examples/stm32wb/src/bin/mac_ffd_net.rs @@ -1,30 +1,44 @@ #![no_std] #![no_main] +use core::net::Ipv6Addr; + use defmt::*; use embassy_executor::Spawner; +use embassy_net::udp::{PacketMetadata, UdpSocket}; +use embassy_net::{Ipv6Cidr, StackResources, StaticConfigV6}; use embassy_stm32::bind_interrupts; use embassy_stm32::ipcc::{Config, ReceiveInterruptHandler, TransmitInterruptHandler}; +use embassy_stm32::peripherals::RNG; use embassy_stm32::rcc::WPAN_DEFAULT; +use embassy_stm32::rng::InterruptHandler as RngInterruptHandler; use embassy_stm32_wpan::TlMbox; use embassy_stm32_wpan::mac::{Driver, DriverState, Runner}; use embassy_stm32_wpan::sub::mm; +use embassy_time::{Duration, Timer}; +use heapless::Vec; use static_cell::StaticCell; use {defmt_rtt as _, panic_probe as _}; bind_interrupts!(struct Irqs{ IPCC_C1_RX => ReceiveInterruptHandler; IPCC_C1_TX => TransmitInterruptHandler; + RNG => RngInterruptHandler; }); #[embassy_executor::task] -async fn run_mm_queue(memory_manager: mm::MemoryManager) { - memory_manager.run_queue().await; +async fn run_mm_queue(memory_manager: mm::MemoryManager) -> ! { + memory_manager.run_queue().await +} + +#[embassy_executor::task] +async fn run_mac(runner: &'static Runner<'static>) -> ! { + runner.run().await } #[embassy_executor::task] -async fn run_mac(runner: &'static Runner<'static>) { - runner.run().await; +async fn run_net(mut runner: embassy_net::Runner<'static, Driver<'static>>) -> ! { + runner.run().await } #[embassy_executor::main] @@ -72,21 +86,67 @@ async fn main(spawner: Spawner) { static DRIVER_STATE: StaticCell = StaticCell::new(); static RUNNER: StaticCell = StaticCell::new(); + static RESOURCES: StaticCell> = StaticCell::new(); let driver_state = DRIVER_STATE.init(DriverState::new(mbox.mac_subsystem)); - let (driver, runner, mut control) = Driver::new(driver_state); - spawner.spawn(run_mac(RUNNER.init(runner)).unwrap()); + let (driver, mac_runner, mut control) = Driver::new( + driver_state, + 0x1122u16.to_be_bytes().try_into().unwrap(), + 0xACDE480000000001u64.to_be_bytes().try_into().unwrap(), + ); - control - .init_link( - 0x1122u16.to_be_bytes().try_into().unwrap(), - 0xACDE480000000001u64.to_be_bytes().try_into().unwrap(), - [0x1A, 0xAA], - ) - .await; + // TODO: rng does not work for some reason + // Generate random seed. + // let mut rng = Rng::new(p.RNG, Irqs); + let seed = [0; 8]; + // let _ = rng.async_fill_bytes(&mut seed).await; + let seed = u64::from_le_bytes(seed); - cortex_m::asm::bkpt(); + info!("seed generated"); + + // Init network stack + let ipv6_addr = Ipv6Addr::new(0, 0, 0, 0, 0, 0xffff, 0xc00a, 0x2ff); + + let config = embassy_net::Config::ipv6_static(StaticConfigV6 { + address: Ipv6Cidr::new(ipv6_addr, 104), + gateway: None, + dns_servers: Vec::new(), + }); + + let (stack, eth_runner) = embassy_net::new(driver, config, RESOURCES.init(StackResources::new()), seed); + + // wpan runner + spawner.spawn(run_mac(RUNNER.init(mac_runner)).unwrap()); + + // Launch network task + spawner.spawn(unwrap!(run_net(eth_runner))); + + info!("Network task initialized"); - let _ = driver; + control.init_link([0x1A, 0xAA]).await; + + // Ensure DHCP configuration is up before trying connect + stack.wait_config_up().await; + + info!("Network up"); + + // Then we can use it! + let mut rx_meta = [PacketMetadata::EMPTY]; + let mut rx_buffer = [0; 4096]; + let mut tx_meta = [PacketMetadata::EMPTY]; + let mut tx_buffer = [0; 4096]; + + let mut socket = UdpSocket::new(stack, &mut rx_meta, &mut rx_buffer, &mut tx_meta, &mut tx_buffer); + + let remote_endpoint = (Ipv6Addr::new(0, 0, 0, 0, 0, 0xffff, 0xc00a, 0x2fb), 8000); + + let send_buf = [0u8; 20]; + + socket.bind((ipv6_addr, 8000)).unwrap(); + socket.send_to(&send_buf, remote_endpoint).await.unwrap(); + + Timer::after(Duration::from_secs(2)).await; + + cortex_m::asm::bkpt(); } -- cgit From 26038cfbc60e9a7b64d70ade51e9e9f34c912e2e Mon Sep 17 00:00:00 2001 From: xoviat Date: Mon, 17 Nov 2025 11:16:55 -0600 Subject: wpan: parse frames correctly --- embassy-stm32-wpan/Cargo.toml | 3 +- embassy-stm32-wpan/src/mac/commands.rs | 26 +++++++++ embassy-stm32-wpan/src/mac/driver.rs | 12 ++--- embassy-stm32-wpan/src/mac/indications.rs | 18 +++++++ embassy-stm32-wpan/src/mac/runner.rs | 87 +++++++++++++++++++------------ embassy-stm32-wpan/src/mac/typedefs.rs | 42 +++++++++++++++ 6 files changed, 148 insertions(+), 40 deletions(-) diff --git a/embassy-stm32-wpan/Cargo.toml b/embassy-stm32-wpan/Cargo.toml index 75d978d1a..05d76f4a6 100644 --- a/embassy-stm32-wpan/Cargo.toml +++ b/embassy-stm32-wpan/Cargo.toml @@ -33,6 +33,7 @@ embassy-futures = { version = "0.1.2", path = "../embassy-futures" } embassy-hal-internal = { version = "0.3.0", path = "../embassy-hal-internal" } embassy-embedded-hal = { version = "0.5.0", path = "../embassy-embedded-hal" } embassy-net-driver = { version = "0.2.0", path = "../embassy-net-driver", optional=true } +smoltcp = { version = "0.12.0", optional=true, default-features = false } defmt = { version = "1.0.1", optional = true } log = { version = "0.4.17", optional = true } @@ -52,7 +53,7 @@ bitflags = { version = "2.3.3", optional = true } 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" ] +mac = ["dep:bitflags", "dep:embassy-net-driver", "dep:smoltcp", "smoltcp/medium-ieee802154"] extended = [] diff --git a/embassy-stm32-wpan/src/mac/commands.rs b/embassy-stm32-wpan/src/mac/commands.rs index 82b9d2772..e0bc50e2a 100644 --- a/embassy-stm32-wpan/src/mac/commands.rs +++ b/embassy-stm32-wpan/src/mac/commands.rs @@ -8,6 +8,8 @@ use super::typedefs::{ PanId, PibId, ScanType, SecurityLevel, }; +use smoltcp::wire::ieee802154::Frame; + pub trait MacCommand: Sized { const OPCODE: OpcodeM4ToM0; @@ -379,6 +381,30 @@ impl DataRequest { } } +impl<'a, T: AsRef<[u8]>> TryFrom> for DataRequest { + type Error = (); + + fn try_from(frame: Frame<&'a T>) -> Result { + // TODO: map the rest of these + + let mut request = DataRequest { + src_addr_mode: frame.src_addressing_mode().try_into()?, + dst_addr_mode: frame.dst_addressing_mode().try_into()?, + dst_pan_id: frame.dst_pan_id().ok_or(())?.into(), + dst_address: frame.dst_addr().ok_or(())?.into(), + msdu_handle: frame.sequence_number().ok_or(())?, + ack_tx: 0x00, + gts_tx: false, + security_level: SecurityLevel::Unsecure, + ..Default::default() + }; + + request.set_buffer(frame.payload().ok_or(())?); + + Ok(request) + } +} + impl Default for DataRequest { fn default() -> Self { Self { diff --git a/embassy-stm32-wpan/src/mac/driver.rs b/embassy-stm32-wpan/src/mac/driver.rs index 5592723a2..c71fabe09 100644 --- a/embassy-stm32-wpan/src/mac/driver.rs +++ b/embassy-stm32-wpan/src/mac/driver.rs @@ -11,6 +11,7 @@ use embassy_sync::mutex::Mutex; use embassy_sync::waitqueue::AtomicWaker; use crate::mac::event::MacEvent; +use crate::mac::indications::write_frame_from_data_indication; use crate::mac::runner::{BUF_SIZE, ZeroCopyPubSub}; use crate::mac::{Control, MTU, Runner}; use crate::sub::mac::{Mac, MacRx, MacTx}; @@ -179,14 +180,13 @@ impl<'d> embassy_net_driver::RxToken for RxToken<'d> { where F: FnOnce(&mut [u8]) -> R, { - // Only valid data events should be put into the queue - - let data_event = match self.rx.try_receive().unwrap() { - MacEvent::McpsDataInd(data_event) => data_event, - _ => unreachable!(), + let mut buffer = [0u8; MTU]; + match self.rx.try_receive().unwrap() { + MacEvent::McpsDataInd(data_event) => write_frame_from_data_indication(data_event, &mut buffer), + _ => {} }; - f(&mut data_event.payload()) + f(&mut buffer[..]) } } diff --git a/embassy-stm32-wpan/src/mac/indications.rs b/embassy-stm32-wpan/src/mac/indications.rs index c0b86d745..45f79ac5b 100644 --- a/embassy-stm32-wpan/src/mac/indications.rs +++ b/embassy-stm32-wpan/src/mac/indications.rs @@ -7,6 +7,9 @@ use super::typedefs::{ PanId, SecurityLevel, }; +use smoltcp::wire::Ieee802154FrameType; +use smoltcp::wire::ieee802154::Frame; + /// MLME ASSOCIATE Indication which will be used by the MAC /// to indicate the reception of an association request command #[repr(C)] @@ -250,6 +253,21 @@ impl DataIndication { } } +pub fn write_frame_from_data_indication<'a, T: AsRef<[u8]> + AsMut<[u8]>>(data: &'a DataIndication, buffer: &'a mut T) { + let mut frame = Frame::new_unchecked(buffer); + + // TODO: complete frame creation + frame.set_frame_type(Ieee802154FrameType::Data); + frame.set_dst_addr(data.dst_address.into()); + frame.set_src_addr(data.src_address.into()); + frame.set_dst_pan_id(data.dst_pan_id.into()); + frame.set_src_pan_id(data.src_pan_id.into()); + frame.set_sequence_number(data.dsn); + + // No way around the copy with the current API + frame.payload_mut().unwrap().copy_from_slice(data.payload()); +} + /// MLME POLL Indication which will be used for indicating the Data Request /// reception to upper layer as defined in Zigbee r22 - D.8.2 #[repr(C)] diff --git a/embassy-stm32-wpan/src/mac/runner.rs b/embassy-stm32-wpan/src/mac/runner.rs index 92c74c2ee..acca70019 100644 --- a/embassy-stm32-wpan/src/mac/runner.rs +++ b/embassy-stm32-wpan/src/mac/runner.rs @@ -6,12 +6,13 @@ use embassy_sync::blocking_mutex::raw::{CriticalSectionRawMutex, NoopRawMutex}; use embassy_sync::channel::Channel; use embassy_sync::mutex::Mutex; use embassy_sync::signal::Signal; +use smoltcp::wire::Ieee802154FrameType; +use smoltcp::wire::ieee802154::Frame; use crate::mac::MTU; use crate::mac::commands::*; use crate::mac::driver::NetworkState; use crate::mac::event::MacEvent; -use crate::mac::typedefs::*; use crate::sub::mac::{MacRx, MacTx}; pub type ZeroCopyPubSub = blocking_mutex::Mutex>>>; @@ -68,55 +69,75 @@ impl<'a> Runner<'a> { } } + async fn send_request>(&self, frame: U) -> Result<(), ()> + where + (): From<>::Error>, + { + let request: T = frame.try_into()?; + self.mac_tx.lock().await.send_command(&request).await.map_err(|_| ())?; + + Ok(()) + } + pub async fn run(&'a self) -> ! { join::join( async { loop { if let Ok(mac_event) = self.mac_rx.read().await { match mac_event { + MacEvent::MlmeAssociateCnf(_) + | MacEvent::MlmeDisassociateCnf(_) + | MacEvent::MlmeGetCnf(_) + | MacEvent::MlmeGtsCnf(_) + | MacEvent::MlmeResetCnf(_) + | MacEvent::MlmeRxEnableCnf(_) + | MacEvent::MlmeScanCnf(_) + | MacEvent::MlmeSetCnf(_) + | MacEvent::MlmeStartCnf(_) + | MacEvent::MlmePollCnf(_) + | MacEvent::MlmeDpsCnf(_) + | MacEvent::MlmeSoundingCnf(_) + | MacEvent::MlmeCalibrateCnf(_) + | MacEvent::McpsDataCnf(_) + | MacEvent::McpsPurgeCnf(_) => { + self.rx_event_channel.lock(|s| { + s.borrow().as_ref().map(|signal| signal.signal(mac_event)); + }); + } MacEvent::McpsDataInd(_) => { + // Pattern should match driver self.rx_data_channel.send(mac_event).await; } _ => { - self.rx_event_channel.lock(|s| { - s.borrow().as_ref().map(|signal| signal.signal(mac_event)); - }); + debug!("unhandled mac event: {:#x}", mac_event); } } } } }, async { - let mut msdu_handle = 0x02; - loop { - let (buf, len) = self.tx_data_channel.receive().await; - let mac_tx = self.mac_tx.lock().await; - - let pan_id = critical_section::with(|cs| self.network_state.borrow(cs).borrow().pan_id); - - // TODO: get the destination address from the packet instead of using the broadcast address - - // The mutex should be dropped on the next loop iteration - mac_tx - .send_command( - DataRequest { - src_addr_mode: AddressMode::Short, - dst_addr_mode: AddressMode::Short, - dst_pan_id: PanId(pan_id), - dst_address: MacAddress::BROADCAST, - msdu_handle: msdu_handle, - ack_tx: 0x00, - gts_tx: false, - security_level: SecurityLevel::Unsecure, - ..Default::default() - } - .set_buffer(&buf[..len]), - ) - .await - .unwrap(); - - msdu_handle = msdu_handle.wrapping_add(1); + let (buf, _) = self.tx_data_channel.receive().await; + + // Smoltcp has created this frame, so there's no need to reparse it. + let frame = Frame::new_unchecked(&buf); + + let result: Result<(), ()> = match frame.frame_type() { + Ieee802154FrameType::Beacon => Err(()), + Ieee802154FrameType::Data => self.send_request::(frame).await, + Ieee802154FrameType::Acknowledgement => Err(()), + Ieee802154FrameType::MacCommand => Err(()), + Ieee802154FrameType::Multipurpose => Err(()), + Ieee802154FrameType::FragmentOrFrak => Err(()), + Ieee802154FrameType::Extended => Err(()), + _ => Err(()), + }; + + if result.is_err() { + debug!("failed to parse mac frame"); + } else { + trace!("data frame sent!"); + } // The tx channel should always be of equal capacity to the tx_buf channel self.tx_buf_channel.try_send(buf).unwrap(); diff --git a/embassy-stm32-wpan/src/mac/typedefs.rs b/embassy-stm32-wpan/src/mac/typedefs.rs index 0552b8ea1..44028bf47 100644 --- a/embassy-stm32-wpan/src/mac/typedefs.rs +++ b/embassy-stm32-wpan/src/mac/typedefs.rs @@ -1,4 +1,5 @@ use core::fmt::Debug; +use smoltcp::wire::ieee802154::{Address, AddressingMode, Pan}; use crate::numeric_enum; @@ -109,12 +110,41 @@ numeric_enum! { } } +impl TryFrom for AddressMode { + type Error = (); + + fn try_from(value: AddressingMode) -> Result { + match value { + AddressingMode::Absent => Ok(Self::NoAddress), + AddressingMode::Extended => Ok(Self::Extended), + AddressingMode::Short => Ok(Self::Short), + AddressingMode::Unknown(_) => Err(()), + } + } +} + #[derive(Clone, Copy)] pub union MacAddress { pub short: [u8; 2], pub extended: [u8; 8], } +impl From
for MacAddress { + fn from(value: Address) -> Self { + match value { + Address::Short(addr) => Self { short: addr }, + Address::Extended(addr) => Self { extended: addr }, + Address::Absent => Self { short: [0u8; 2] }, + } + } +} + +impl From for Address { + fn from(_value: MacAddress) -> Self { + todo!() + } +} + impl Debug for MacAddress { fn fmt(&self, fmt: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { unsafe { @@ -379,3 +409,15 @@ pub struct PanId(pub [u8; 2]); impl PanId { pub const BROADCAST: Self = Self([0xFF, 0xFF]); } + +impl From for PanId { + fn from(value: Pan) -> Self { + Self(value.0.to_be_bytes()) + } +} + +impl From for Pan { + fn from(value: PanId) -> Self { + Self(u16::from_be_bytes(value.0)) + } +} -- cgit From ca02cdf792d4aff16e2f265616bec5abdb3c7a1a Mon Sep 17 00:00:00 2001 From: xoviat Date: Mon, 17 Nov 2025 11:24:49 -0600 Subject: fmt --- embassy-stm32-wpan/src/mac/commands.rs | 4 ++-- embassy-stm32-wpan/src/mac/indications.rs | 6 +++--- embassy-stm32-wpan/src/mac/typedefs.rs | 1 + 3 files changed, 6 insertions(+), 5 deletions(-) diff --git a/embassy-stm32-wpan/src/mac/commands.rs b/embassy-stm32-wpan/src/mac/commands.rs index e0bc50e2a..a3a40f377 100644 --- a/embassy-stm32-wpan/src/mac/commands.rs +++ b/embassy-stm32-wpan/src/mac/commands.rs @@ -2,14 +2,14 @@ use core::{mem, slice}; +use smoltcp::wire::ieee802154::Frame; + use super::opcodes::OpcodeM4ToM0; use super::typedefs::{ AddressMode, Capabilities, DisassociationReason, GtsCharacteristics, KeyIdMode, MacAddress, MacChannel, MacStatus, PanId, PibId, ScanType, SecurityLevel, }; -use smoltcp::wire::ieee802154::Frame; - pub trait MacCommand: Sized { const OPCODE: OpcodeM4ToM0; diff --git a/embassy-stm32-wpan/src/mac/indications.rs b/embassy-stm32-wpan/src/mac/indications.rs index 45f79ac5b..05869ba2a 100644 --- a/embassy-stm32-wpan/src/mac/indications.rs +++ b/embassy-stm32-wpan/src/mac/indications.rs @@ -1,5 +1,8 @@ use core::slice; +use smoltcp::wire::Ieee802154FrameType; +use smoltcp::wire::ieee802154::Frame; + use super::consts::MAX_PENDING_ADDRESS; use super::event::ParseableMacEvent; use super::typedefs::{ @@ -7,9 +10,6 @@ use super::typedefs::{ PanId, SecurityLevel, }; -use smoltcp::wire::Ieee802154FrameType; -use smoltcp::wire::ieee802154::Frame; - /// MLME ASSOCIATE Indication which will be used by the MAC /// to indicate the reception of an association request command #[repr(C)] diff --git a/embassy-stm32-wpan/src/mac/typedefs.rs b/embassy-stm32-wpan/src/mac/typedefs.rs index 44028bf47..7e3ef4962 100644 --- a/embassy-stm32-wpan/src/mac/typedefs.rs +++ b/embassy-stm32-wpan/src/mac/typedefs.rs @@ -1,4 +1,5 @@ use core::fmt::Debug; + use smoltcp::wire::ieee802154::{Address, AddressingMode, Pan}; use crate::numeric_enum; -- cgit From 36d533c2de1db2a3b7ef265e22fb4481e046ef17 Mon Sep 17 00:00:00 2001 From: xoviat Date: Mon, 17 Nov 2025 13:18:06 -0600 Subject: wpan: fix src/dst address --- embassy-stm32-wpan/src/mac/driver.rs | 3 ++- embassy-stm32-wpan/src/mac/indications.rs | 24 +++++++++++++++++++++--- embassy-stm32-wpan/src/mac/typedefs.rs | 18 ++++++++++++++---- 3 files changed, 37 insertions(+), 8 deletions(-) diff --git a/embassy-stm32-wpan/src/mac/driver.rs b/embassy-stm32-wpan/src/mac/driver.rs index c71fabe09..c43d595b7 100644 --- a/embassy-stm32-wpan/src/mac/driver.rs +++ b/embassy-stm32-wpan/src/mac/driver.rs @@ -11,7 +11,7 @@ use embassy_sync::mutex::Mutex; use embassy_sync::waitqueue::AtomicWaker; use crate::mac::event::MacEvent; -use crate::mac::indications::write_frame_from_data_indication; +use crate::mac::indications::{write_frame_from_beacon_indication, write_frame_from_data_indication}; use crate::mac::runner::{BUF_SIZE, ZeroCopyPubSub}; use crate::mac::{Control, MTU, Runner}; use crate::sub::mac::{Mac, MacRx, MacTx}; @@ -183,6 +183,7 @@ impl<'d> embassy_net_driver::RxToken for RxToken<'d> { let mut buffer = [0u8; MTU]; match self.rx.try_receive().unwrap() { MacEvent::McpsDataInd(data_event) => write_frame_from_data_indication(data_event, &mut buffer), + MacEvent::MlmeBeaconNotifyInd(data_event) => write_frame_from_beacon_indication(data_event, &mut buffer), _ => {} }; diff --git a/embassy-stm32-wpan/src/mac/indications.rs b/embassy-stm32-wpan/src/mac/indications.rs index 05869ba2a..a43777e75 100644 --- a/embassy-stm32-wpan/src/mac/indications.rs +++ b/embassy-stm32-wpan/src/mac/indications.rs @@ -3,6 +3,8 @@ use core::slice; use smoltcp::wire::Ieee802154FrameType; use smoltcp::wire::ieee802154::Frame; +use crate::mac::typedefs::MacAddressAndMode; + use super::consts::MAX_PENDING_ADDRESS; use super::event::ParseableMacEvent; use super::typedefs::{ @@ -77,6 +79,22 @@ pub struct BeaconNotifyIndication { impl ParseableMacEvent for BeaconNotifyIndication {} +impl BeaconNotifyIndication { + pub fn payload<'a>(&'a self) -> &'a mut [u8] { + unsafe { slice::from_raw_parts_mut(self.sdu_ptr as *mut _, self.sdu_length as usize) } + } +} + +pub fn write_frame_from_beacon_indication<'a, T: AsRef<[u8]> + AsMut<[u8]>>( + data: &'a BeaconNotifyIndication, + buffer: &'a mut T, +) { + let mut frame = Frame::new_unchecked(buffer); + + frame.set_frame_type(Ieee802154FrameType::Beacon); + frame.set_sequence_number(data.bsn); +} + /// MLME COMM STATUS Indication which is used by the MAC to indicate a communications status #[repr(C)] #[derive(Debug)] @@ -256,13 +274,13 @@ impl DataIndication { pub fn write_frame_from_data_indication<'a, T: AsRef<[u8]> + AsMut<[u8]>>(data: &'a DataIndication, buffer: &'a mut T) { let mut frame = Frame::new_unchecked(buffer); - // TODO: complete frame creation frame.set_frame_type(Ieee802154FrameType::Data); - frame.set_dst_addr(data.dst_address.into()); - frame.set_src_addr(data.src_address.into()); + frame.set_src_addr(MacAddressAndMode(data.src_address, data.src_addr_mode).into()); + frame.set_dst_addr(MacAddressAndMode(data.dst_address, data.dst_addr_mode).into()); frame.set_dst_pan_id(data.dst_pan_id.into()); frame.set_src_pan_id(data.src_pan_id.into()); frame.set_sequence_number(data.dsn); + frame.set_security_enabled(data.security_level == SecurityLevel::Secured); // No way around the copy with the current API frame.payload_mut().unwrap().copy_from_slice(data.payload()); diff --git a/embassy-stm32-wpan/src/mac/typedefs.rs b/embassy-stm32-wpan/src/mac/typedefs.rs index 7e3ef4962..175d4a37d 100644 --- a/embassy-stm32-wpan/src/mac/typedefs.rs +++ b/embassy-stm32-wpan/src/mac/typedefs.rs @@ -140,9 +140,19 @@ impl From
for MacAddress { } } -impl From for Address { - fn from(_value: MacAddress) -> Self { - todo!() +pub struct MacAddressAndMode(pub MacAddress, pub AddressMode); + +impl From for Address { + fn from(mac_address_and_mode: MacAddressAndMode) -> Self { + let address = mac_address_and_mode.0; + let mode = mac_address_and_mode.1; + + match mode { + AddressMode::Short => Address::Short(unsafe { address.short }), + AddressMode::Extended => Address::Extended(unsafe { address.extended }), + AddressMode::NoAddress => Address::Absent, + AddressMode::Reserved => Address::Absent, + } } } @@ -377,7 +387,7 @@ numeric_enum! { numeric_enum! { #[repr(u8)] - #[derive(Default, Clone, Copy, Debug)] + #[derive(Default, Clone, Copy, Debug, PartialEq)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] pub enum SecurityLevel { /// MAC Unsecured Mode Security -- cgit From 50501e0f49e5c2ae939ef0e231d1d3cf319b0a76 Mon Sep 17 00:00:00 2001 From: xoviat Date: Mon, 17 Nov 2025 13:23:10 -0600 Subject: fmt --- embassy-stm32-wpan/src/mac/indications.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/embassy-stm32-wpan/src/mac/indications.rs b/embassy-stm32-wpan/src/mac/indications.rs index a43777e75..5673514c9 100644 --- a/embassy-stm32-wpan/src/mac/indications.rs +++ b/embassy-stm32-wpan/src/mac/indications.rs @@ -3,14 +3,13 @@ use core::slice; use smoltcp::wire::Ieee802154FrameType; use smoltcp::wire::ieee802154::Frame; -use crate::mac::typedefs::MacAddressAndMode; - use super::consts::MAX_PENDING_ADDRESS; use super::event::ParseableMacEvent; use super::typedefs::{ AddressMode, Capabilities, DisassociationReason, KeyIdMode, MacAddress, MacChannel, MacStatus, PanDescriptor, PanId, SecurityLevel, }; +use crate::mac::typedefs::MacAddressAndMode; /// MLME ASSOCIATE Indication which will be used by the MAC /// to indicate the reception of an association request command -- cgit From e0b6bcb13bb3c5b419ace922b8f9955a0c620d35 Mon Sep 17 00:00:00 2001 From: Filip Brozovic Date: Tue, 18 Nov 2025 14:01:07 +0100 Subject: stm32: async flash erase/write for h7 --- embassy-stm32/CHANGELOG.md | 1 + embassy-stm32/src/flash/h7.rs | 190 +++++++++++++++++++++++++++++++++-------- embassy-stm32/src/flash/mod.rs | 4 +- 3 files changed, 158 insertions(+), 37 deletions(-) diff --git a/embassy-stm32/CHANGELOG.md b/embassy-stm32/CHANGELOG.md index 8e3e802a4..ed523debd 100644 --- a/embassy-stm32/CHANGELOG.md +++ b/embassy-stm32/CHANGELOG.md @@ -66,6 +66,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - feat: add flash support for c0 family ([#4874](https://github.com/embassy-rs/embassy/pull/4874)) - fix: fixing channel numbers on vbat and vddcore for adc on adc - adc: adding disable to vbat +- feat: stm32/flash: add async support for h7 family ## 0.4.0 - 2025-08-26 diff --git a/embassy-stm32/src/flash/h7.rs b/embassy-stm32/src/flash/h7.rs index 8a43cce3f..b342f4a83 100644 --- a/embassy-stm32/src/flash/h7.rs +++ b/embassy-stm32/src/flash/h7.rs @@ -1,10 +1,31 @@ use core::ptr::write_volatile; use core::sync::atomic::{Ordering, fence}; +use embassy_sync::waitqueue::AtomicWaker; +use pac::flash::regs::Sr; + use super::{BANK1_REGION, FLASH_REGIONS, FlashSector, WRITE_SIZE}; use crate::flash::Error; use crate::pac; +static WAKER: AtomicWaker = AtomicWaker::new(); + +pub(crate) unsafe fn on_interrupt() { + // Clear IRQ flags + pac::FLASH.bank(0).ccr().write(|w| { + w.set_clr_eop(true); + w.set_clr_operr(true); + }); + if is_dual_bank() { + pac::FLASH.bank(1).ccr().write(|w| { + w.set_clr_eop(true); + w.set_clr_operr(true); + }); + } + + WAKER.wake(); +} + const fn is_dual_bank() -> bool { FLASH_REGIONS.len() >= 2 } @@ -29,12 +50,68 @@ pub(crate) unsafe fn unlock() { } } +pub(crate) unsafe fn enable_write() { + enable_blocking_write(); +} + +pub(crate) unsafe fn disable_write() { + disable_blocking_write(); +} + pub(crate) unsafe fn enable_blocking_write() { assert_eq!(0, WRITE_SIZE % 4); } pub(crate) unsafe fn disable_blocking_write() {} +pub(crate) async unsafe fn write(start_address: u32, buf: &[u8; WRITE_SIZE]) -> Result<(), Error> { + // We cannot have the write setup sequence in begin_write as it depends on the address + let bank = if start_address < BANK1_REGION.end() { + pac::FLASH.bank(0) + } else { + pac::FLASH.bank(1) + }; + bank.cr().write(|w| { + w.set_pg(true); + #[cfg(flash_h7)] + w.set_psize(2); // 32 bits at once + w.set_eopie(true); + w.set_operrie(true); + }); + cortex_m::asm::isb(); + cortex_m::asm::dsb(); + fence(Ordering::SeqCst); + + let mut res = None; + let mut address = start_address; + for val in buf.chunks(4) { + write_volatile(address as *mut u32, u32::from_le_bytes(unwrap!(val.try_into()))); + address += val.len() as u32; + + res = Some(wait_ready(bank).await); + bank.sr().modify(|w| { + if w.eop() { + w.set_eop(true); + } + }); + if unwrap!(res).is_err() { + break; + } + } + + cortex_m::asm::isb(); + cortex_m::asm::dsb(); + fence(Ordering::SeqCst); + + bank.cr().write(|w| { + w.set_pg(false); + w.set_eopie(false); + w.set_operrie(false); + }); + + unwrap!(res) +} + pub(crate) unsafe fn blocking_write(start_address: u32, buf: &[u8; WRITE_SIZE]) -> Result<(), Error> { // We cannot have the write setup sequence in begin_write as it depends on the address let bank = if start_address < BANK1_REGION.end() { @@ -77,6 +154,36 @@ pub(crate) unsafe fn blocking_write(start_address: u32, buf: &[u8; WRITE_SIZE]) unwrap!(res) } +pub(crate) async unsafe fn erase_sector(sector: &FlashSector) -> Result<(), Error> { + let bank = pac::FLASH.bank(sector.bank as usize); + bank.cr().modify(|w| { + w.set_ser(true); + #[cfg(flash_h7)] + w.set_snb(sector.index_in_bank); + #[cfg(flash_h7ab)] + w.set_ssn(sector.index_in_bank); + w.set_eopie(true); + w.set_operrie(true); + }); + + bank.cr().modify(|w| { + w.set_start(true); + }); + + cortex_m::asm::isb(); + cortex_m::asm::dsb(); + fence(Ordering::SeqCst); + + let ret: Result<(), Error> = wait_ready(bank).await; + bank.cr().modify(|w| { + w.set_ser(false); + w.set_eopie(false); + w.set_operrie(false); + }); + bank_clear_all_err(bank); + ret +} + pub(crate) unsafe fn blocking_erase_sector(sector: &FlashSector) -> Result<(), Error> { let bank = pac::FLASH.bank(sector.bank as usize); bank.cr().modify(|w| { @@ -112,46 +219,59 @@ unsafe fn bank_clear_all_err(bank: pac::flash::Bank) { bank.sr().modify(|_| {}); } +async fn wait_ready(bank: pac::flash::Bank) -> Result<(), Error> { + use core::future::poll_fn; + use core::task::Poll; + + poll_fn(|cx| { + WAKER.register(cx.waker()); + + let sr = bank.sr().read(); + if !sr.bsy() && !sr.qw() { + Poll::Ready(get_result(sr)) + } else { + return Poll::Pending; + } + }) + .await +} + unsafe fn blocking_wait_ready(bank: pac::flash::Bank) -> Result<(), Error> { loop { let sr = bank.sr().read(); if !sr.bsy() && !sr.qw() { - if sr.wrperr() { - return Err(Error::Protected); - } - if sr.pgserr() { - error!("pgserr"); - return Err(Error::Seq); - } - if sr.incerr() { - // writing to a different address when programming 256 bit word was not finished - error!("incerr"); - return Err(Error::Seq); - } - if sr.crcrderr() { - error!("crcrderr"); - return Err(Error::Seq); - } - if sr.operr() { - return Err(Error::Prog); - } - if sr.sneccerr1() { - // single ECC error - return Err(Error::Prog); - } - if sr.dbeccerr() { - // double ECC error - return Err(Error::Prog); - } - if sr.rdperr() { - return Err(Error::Protected); - } - if sr.rdserr() { - return Err(Error::Protected); - } - - return Ok(()); + return get_result(sr); } } } + +fn get_result(sr: Sr) -> Result<(), Error> { + if sr.wrperr() { + Err(Error::Protected) + } else if sr.pgserr() { + error!("pgserr"); + Err(Error::Seq) + } else if sr.incerr() { + // writing to a different address when programming 256 bit word was not finished + error!("incerr"); + Err(Error::Seq) + } else if sr.crcrderr() { + error!("crcrderr"); + Err(Error::Seq) + } else if sr.operr() { + Err(Error::Prog) + } else if sr.sneccerr1() { + // single ECC error + Err(Error::Prog) + } else if sr.dbeccerr() { + // double ECC error + Err(Error::Prog) + } else if sr.rdperr() { + Err(Error::Protected) + } else if sr.rdserr() { + Err(Error::Protected) + } else { + Ok(()) + } +} diff --git a/embassy-stm32/src/flash/mod.rs b/embassy-stm32/src/flash/mod.rs index 39cd9b3a9..6211a37b7 100644 --- a/embassy-stm32/src/flash/mod.rs +++ b/embassy-stm32/src/flash/mod.rs @@ -1,14 +1,14 @@ //! Flash memory (FLASH) use embedded_storage::nor_flash::{NorFlashError, NorFlashErrorKind}; -#[cfg(flash_f4)] +#[cfg(any(flash_f4, flash_h7, flash_h7ab))] mod asynch; #[cfg(flash)] mod common; #[cfg(eeprom)] mod eeprom; -#[cfg(flash_f4)] +#[cfg(any(flash_f4, flash_h7, flash_h7ab))] pub use asynch::InterruptHandler; #[cfg(flash)] pub use common::*; -- cgit From 8f6f6ae8e90610ddd76df001e618075524299d2b Mon Sep 17 00:00:00 2001 From: Filip Brozovic Date: Tue, 18 Nov 2025 14:05:34 +0100 Subject: stm32: add async flash example for h7 --- examples/stm32h7/src/bin/flash_async.rs | 84 +++++++++++++++++++++++++++++++++ 1 file changed, 84 insertions(+) create mode 100644 examples/stm32h7/src/bin/flash_async.rs diff --git a/examples/stm32h7/src/bin/flash_async.rs b/examples/stm32h7/src/bin/flash_async.rs new file mode 100644 index 000000000..96d1936f3 --- /dev/null +++ b/examples/stm32h7/src/bin/flash_async.rs @@ -0,0 +1,84 @@ +#![no_std] +#![no_main] + +use defmt::{info, unwrap}; +use embassy_executor::Spawner; +use embassy_stm32::flash::{Flash, InterruptHandler}; +use embassy_stm32::gpio::{AnyPin, Level, Output, Speed}; +use embassy_stm32::{Peri, bind_interrupts}; +use embassy_time::Timer; +use {defmt_rtt as _, panic_probe as _}; + +bind_interrupts!(struct Irqs { + FLASH => InterruptHandler; +}); + +#[embassy_executor::main] +async fn main(spawner: Spawner) { + let p = embassy_stm32::init(Default::default()); + info!("Hello Flash!"); + + let mut f = Flash::new(p.FLASH, Irqs); + + // Led should blink uninterrupted during ~2sec erase operation + spawner.spawn(blinky(p.PB14.into()).unwrap()); + + // Test on bank 2 in order not to stall CPU. + test_flash(&mut f, 1024 * 1024, 128 * 1024).await; +} + +#[embassy_executor::task] +async fn blinky(p: Peri<'static, AnyPin>) { + let mut led = Output::new(p, Level::High, Speed::Low); + + loop { + info!("high"); + led.set_high(); + Timer::after_millis(300).await; + + info!("low"); + led.set_low(); + Timer::after_millis(300).await; + } +} + +async fn test_flash<'a>(f: &mut Flash<'a>, offset: u32, size: u32) { + info!("Testing offset: {=u32:#X}, size: {=u32:#X}", offset, size); + + info!("Reading..."); + let mut buf = [0u8; 32]; + unwrap!(f.blocking_read(offset, &mut buf)); + info!("Read: {=[u8]:x}", buf); + + info!("Erasing..."); + unwrap!(f.erase(offset, offset + size).await); + + info!("Reading..."); + let mut buf = [0u8; 32]; + unwrap!(f.blocking_read(offset, &mut buf)); + info!("Read after erase: {=[u8]:x}", buf); + + info!("Writing..."); + unwrap!( + f.write( + offset, + &[ + 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, + 29, 30, 31, 32 + ] + ) + .await + ); + + info!("Reading..."); + let mut buf = [0u8; 32]; + unwrap!(f.blocking_read(offset, &mut buf)); + info!("Read: {=[u8]:x}", buf); + assert_eq!( + &buf[..], + &[ + 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, + 30, 31, 32 + ] + ); +} -- cgit From 4e808345883ae64099135a8384f774c44feccc5c Mon Sep 17 00:00:00 2001 From: xoviat Date: Tue, 18 Nov 2025 16:27:10 -0600 Subject: fix: fix incorrect logic for buffered usart transmission complete. --- embassy-stm32/CHANGELOG.md | 1 + embassy-stm32/src/usart/buffered.rs | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/embassy-stm32/CHANGELOG.md b/embassy-stm32/CHANGELOG.md index b6caf8f65..5fd43744e 100644 --- a/embassy-stm32/CHANGELOG.md +++ b/embassy-stm32/CHANGELOG.md @@ -7,6 +7,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## Unreleased - ReleaseDate +- fix: fix incorrect logic for buffered usart transmission complete. - change: stm32/eth: ethernet no longer has a hard dependency on station management, and station management can be used independently ([#4871](https://github.com/embassy-rs/embassy/pull/4871)) - feat: allow embassy_executor::main for low power - feat: Add waveform methods to ComplementaryPwm diff --git a/embassy-stm32/src/usart/buffered.rs b/embassy-stm32/src/usart/buffered.rs index 69c3a740f..26d2b8991 100644 --- a/embassy-stm32/src/usart/buffered.rs +++ b/embassy-stm32/src/usart/buffered.rs @@ -87,7 +87,7 @@ unsafe fn on_interrupt(r: Regs, state: &'static State) { // With `usart_v4` hardware FIFO is enabled and Transmission complete (TC) // indicates that all bytes are pushed out from the FIFO. // For other usart variants it shows that last byte from the buffer was just sent. - if sr_val.tc() { + if sr_val.tc() && r.cr1().read().tcie() { // For others it is cleared above with `clear_interrupt_flags`. #[cfg(any(usart_v1, usart_v2))] sr(r).modify(|w| w.set_tc(false)); -- cgit From 9f229ac7508187b1c6754984f0b33719e5f8167f Mon Sep 17 00:00:00 2001 From: Steven Schulteis Date: Sun, 16 Nov 2025 09:50:32 -0600 Subject: Add missing feature documentation for embassy-boot --- embassy-boot/Cargo.toml | 14 +++++++++++++- embassy-boot/src/lib.rs | 4 ++++ 2 files changed, 17 insertions(+), 1 deletion(-) diff --git a/embassy-boot/Cargo.toml b/embassy-boot/Cargo.toml index 8c5c1f633..754c6e5f1 100644 --- a/embassy-boot/Cargo.toml +++ b/embassy-boot/Cargo.toml @@ -26,6 +26,7 @@ features = ["defmt"] [dependencies] defmt = { version = "1.0.1", optional = true } digest = "0.10" +document-features = "0.2.7" log = { version = "0.4", optional = true } ed25519-dalek = { version = "2", default-features = false, features = ["digest"], optional = true } embassy-embedded-hal = { version = "0.5.0", path = "../embassy-embedded-hal" } @@ -45,11 +46,22 @@ critical-section = { version = "1.1.1", features = ["std"] } ed25519-dalek = { version = "2", default-features = false, features = ["std", "rand_core", "digest"] } [features] +## Use [`defmt`](https://docs.rs/defmt/latest/defmt/) for logging defmt = ["dep:defmt"] +## Use log for logging log = ["dep:log"] + +## Enable for devices that set erased flash bytes to `0x00` instead of the usual `0xFF` +flash-erase-zero = [] + +#! ## Firmware Signing +#! Enable one of these features to allow verification of DFU signatures with +#! `FirmwareUpdater::verify_and_mark_updated`. + +## Use the `ed25519-dalek` package to verify DFU signatures. ed25519-dalek = ["dep:ed25519-dalek", "_verify"] +## Use the `salty` package to verify DFU signatures. ed25519-salty = ["dep:salty", "_verify"] -flash-erase-zero = [] #Internal features _verify = [] diff --git a/embassy-boot/src/lib.rs b/embassy-boot/src/lib.rs index 7dc811f66..3e61d6036 100644 --- a/embassy-boot/src/lib.rs +++ b/embassy-boot/src/lib.rs @@ -3,6 +3,10 @@ #![allow(unsafe_op_in_unsafe_fn)] #![warn(missing_docs)] #![doc = include_str!("../README.md")] + +//! ## Feature flags +#![doc = document_features::document_features!(feature_label = r#"{feature}"#)] + mod fmt; mod boot_loader; -- cgit From 5e90c3fdb3b87970926b1ecc86cc4ad8ab260569 Mon Sep 17 00:00:00 2001 From: Steven Schulteis Date: Sun, 16 Nov 2025 09:51:38 -0600 Subject: Fix docs for embassy-boot state partition size --- docs/pages/bootloader.adoc | 6 +++--- embassy-boot/src/boot_loader.rs | 12 +++++++----- 2 files changed, 10 insertions(+), 8 deletions(-) diff --git a/docs/pages/bootloader.adoc b/docs/pages/bootloader.adoc index b0f0331aa..c010b0622 100644 --- a/docs/pages/bootloader.adoc +++ b/docs/pages/bootloader.adoc @@ -43,14 +43,14 @@ Partition Size~dfu~= Partition Size~active~+ Page Size~active~ + All values are specified in bytes. -* BOOTLOADER STATE - Where the bootloader stores the current state describing if the active and dfu partitions need to be swapped. When the new firmware has been written to the DFU partition, a magic field is written to instruct the bootloader that the partitions should be swapped. This partition must be able to store a magic field as well as the partition swap progress. The partition size given by: +* BOOTLOADER STATE - Where the bootloader stores the current state describing if the active and dfu partitions need to be swapped. When the new firmware has been written to the DFU partition, a magic field is written to instruct the bootloader that the partitions should be swapped. This partition must be able to store a magic field as well as the partition swap progress. The partition size is given by: + -Partition Size~state~ = Write Size~state~ + (2 × Partition Size~active~ / Page Size~active~) +Partition Size~state~ = (2 × Write Size~state~) + (4 × Write Size~state~ × Partition Size~active~ / Page Size~active~) + All values are specified in bytes. The partitions for ACTIVE (+BOOTLOADER), DFU and BOOTLOADER_STATE may be placed in separate flash. The page size used by the bootloader is determined by the lowest common multiple of the ACTIVE and DFU page sizes. -The BOOTLOADER_STATE partition must be big enough to store one word per page in the ACTIVE and DFU partitions combined. +The BOOTLOADER_STATE partition must be big enough to store two words, plus four words per page in the ACTIVE partition. The bootloader has a platform-agnostic part, which implements the power fail safe swapping algorithm given the boundaries set by the partitions. The platform-specific part is a minimal shim that provides additional functionality such as watchdogs or supporting the nRF52 softdevice. diff --git a/embassy-boot/src/boot_loader.rs b/embassy-boot/src/boot_loader.rs index c38940d6e..a3a307051 100644 --- a/embassy-boot/src/boot_loader.rs +++ b/embassy-boot/src/boot_loader.rs @@ -135,10 +135,12 @@ pub struct BootLoader { dfu: DFU, /// The state partition has the following format: /// All ranges are in multiples of WRITE_SIZE bytes. - /// | Range | Description | - /// | 0..1 | Magic indicating bootloader state. BOOT_MAGIC means boot, SWAP_MAGIC means swap. | - /// | 1..2 | Progress validity. ERASE_VALUE means valid, !ERASE_VALUE means invalid. | - /// | 2..2 + N | Progress index used while swapping or reverting + /// N = Active partition size divided by WRITE_SIZE. + /// | Range | Description | + /// | 0..1 | Magic indicating bootloader state. BOOT_MAGIC means boot, SWAP_MAGIC means swap. | + /// | 1..2 | Progress validity. ERASE_VALUE means valid, !ERASE_VALUE means invalid. | + /// | 2..(2 + 2N) | Progress index used while swapping | + /// | (2 + 2N)..(2 + 4N) | Progress index used while reverting state: STATE, } @@ -429,7 +431,7 @@ fn assert_partitions( assert_eq!(dfu.capacity() as u32 % page_size, 0); // DFU partition has to be bigger than ACTIVE partition to handle swap algorithm assert!(dfu.capacity() as u32 - active.capacity() as u32 >= page_size); - assert!(2 + 2 * (active.capacity() as u32 / page_size) <= state.capacity() as u32 / STATE::WRITE_SIZE as u32); + assert!(2 + 4 * (active.capacity() as u32 / page_size) <= state.capacity() as u32 / STATE::WRITE_SIZE as u32); } #[cfg(test)] -- cgit From f40b85d784824a24d42eded11bf9c83d7649ae2b Mon Sep 17 00:00:00 2001 From: xoviat Date: Tue, 18 Nov 2025 19:34:10 -0600 Subject: stm32/exti: add poll_for methods --- embassy-stm32/CHANGELOG.md | 1 + embassy-stm32/src/exti.rs | 46 ++++++++++++++++++++++++++++++++++++---------- 2 files changed, 37 insertions(+), 10 deletions(-) diff --git a/embassy-stm32/CHANGELOG.md b/embassy-stm32/CHANGELOG.md index b6caf8f65..7534f5b8e 100644 --- a/embassy-stm32/CHANGELOG.md +++ b/embassy-stm32/CHANGELOG.md @@ -7,6 +7,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## Unreleased - ReleaseDate +- feat: add poll_for methods to exti - change: stm32/eth: ethernet no longer has a hard dependency on station management, and station management can be used independently ([#4871](https://github.com/embassy-rs/embassy/pull/4871)) - feat: allow embassy_executor::main for low power - feat: Add waveform methods to ComplementaryPwm diff --git a/embassy-stm32/src/exti.rs b/embassy-stm32/src/exti.rs index cb46d362c..899d5e677 100644 --- a/embassy-stm32/src/exti.rs +++ b/embassy-stm32/src/exti.rs @@ -7,6 +7,7 @@ use core::task::{Context, Poll}; use embassy_hal_internal::{PeripheralType, impl_peripheral}; use embassy_sync::waitqueue::AtomicWaker; +use futures_util::FutureExt; use crate::gpio::{AnyPin, Input, Level, Pin as GpioPin, PinNumber, Pull}; use crate::pac::EXTI; @@ -133,7 +134,7 @@ impl<'d> ExtiInput<'d> { /// /// This returns immediately if the pin is already high. pub async fn wait_for_high(&mut self) { - let fut = ExtiInputFuture::new(self.pin.pin.pin.pin(), self.pin.pin.pin.port(), true, false); + let fut = ExtiInputFuture::new(self.pin.pin.pin.pin(), self.pin.pin.pin.port(), true, false, true); if self.is_high() { return; } @@ -144,7 +145,7 @@ impl<'d> ExtiInput<'d> { /// /// This returns immediately if the pin is already low. pub async fn wait_for_low(&mut self) { - let fut = ExtiInputFuture::new(self.pin.pin.pin.pin(), self.pin.pin.pin.port(), false, true); + let fut = ExtiInputFuture::new(self.pin.pin.pin.pin(), self.pin.pin.pin.port(), false, true, true); if self.is_low() { return; } @@ -155,19 +156,40 @@ impl<'d> ExtiInput<'d> { /// /// If the pin is already high, it will wait for it to go low then back high. pub async fn wait_for_rising_edge(&mut self) { - ExtiInputFuture::new(self.pin.pin.pin.pin(), self.pin.pin.pin.port(), true, false).await + ExtiInputFuture::new(self.pin.pin.pin.pin(), self.pin.pin.pin.port(), true, false, true).await + } + + /// Asynchronously wait until the pin sees a rising edge. + /// + /// If the pin is already high, it will wait for it to go low then back high. + pub fn poll_for_rising_edge<'a>(&mut self, cx: &mut Context<'a>) { + let _ = + ExtiInputFuture::new(self.pin.pin.pin.pin(), self.pin.pin.pin.port(), true, false, false).poll_unpin(cx); } /// Asynchronously wait until the pin sees a falling edge. /// /// If the pin is already low, it will wait for it to go high then back low. pub async fn wait_for_falling_edge(&mut self) { - ExtiInputFuture::new(self.pin.pin.pin.pin(), self.pin.pin.pin.port(), false, true).await + ExtiInputFuture::new(self.pin.pin.pin.pin(), self.pin.pin.pin.port(), false, true, true).await + } + + /// Asynchronously wait until the pin sees a falling edge. + /// + /// If the pin is already low, it will wait for it to go high then back low. + pub fn poll_for_falling_edge<'a>(&mut self, cx: &mut Context<'a>) { + let _ = + ExtiInputFuture::new(self.pin.pin.pin.pin(), self.pin.pin.pin.port(), false, true, false).poll_unpin(cx); } /// Asynchronously wait until the pin sees any edge (either rising or falling). pub async fn wait_for_any_edge(&mut self) { - ExtiInputFuture::new(self.pin.pin.pin.pin(), self.pin.pin.pin.port(), true, true).await + ExtiInputFuture::new(self.pin.pin.pin.pin(), self.pin.pin.pin.port(), true, true, true).await + } + + /// Asynchronously wait until the pin sees any edge (either rising or falling). + pub fn poll_for_any_edge<'a>(&mut self, cx: &mut Context<'a>) { + let _ = ExtiInputFuture::new(self.pin.pin.pin.pin(), self.pin.pin.pin.port(), true, true, false).poll_unpin(cx); } } @@ -227,11 +249,12 @@ impl<'d> embedded_hal_async::digital::Wait for ExtiInput<'d> { #[must_use = "futures do nothing unless you `.await` or poll them"] struct ExtiInputFuture<'a> { pin: PinNumber, + drop: bool, phantom: PhantomData<&'a mut AnyPin>, } impl<'a> ExtiInputFuture<'a> { - fn new(pin: PinNumber, port: PinNumber, rising: bool, falling: bool) -> Self { + fn new(pin: PinNumber, port: PinNumber, rising: bool, falling: bool, drop: bool) -> Self { critical_section::with(|_| { let pin = pin as usize; exticr_regs().exticr(pin / 4).modify(|w| w.set_exti(pin % 4, port)); @@ -252,6 +275,7 @@ impl<'a> ExtiInputFuture<'a> { Self { pin, + drop, phantom: PhantomData, } } @@ -259,10 +283,12 @@ impl<'a> ExtiInputFuture<'a> { impl<'a> Drop for ExtiInputFuture<'a> { fn drop(&mut self) { - critical_section::with(|_| { - let pin = self.pin as _; - cpu_regs().imr(0).modify(|w| w.set_line(pin, false)); - }); + if self.drop { + critical_section::with(|_| { + let pin = self.pin as _; + cpu_regs().imr(0).modify(|w| w.set_line(pin, false)); + }); + } } } -- cgit From 579f1b4e0b10671605d63d5ddc67a4d9384e84b9 Mon Sep 17 00:00:00 2001 From: Steven Schulteis Date: Tue, 18 Nov 2025 19:46:07 -0600 Subject: Update changelog --- embassy-boot/CHANGELOG.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/embassy-boot/CHANGELOG.md b/embassy-boot/CHANGELOG.md index 8d6395357..1d41043cb 100644 --- a/embassy-boot/CHANGELOG.md +++ b/embassy-boot/CHANGELOG.md @@ -8,6 +8,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## Unreleased - ReleaseDate +- Fixed documentation and assertion of STATE partition size requirements +- Added documentation for package features + ## 0.6.1 - 2025-08-26 - First release with changelog. -- cgit From 3a958cdf685a5d4780572c310aed26272839f78a Mon Sep 17 00:00:00 2001 From: Eicke Hecht Date: Wed, 19 Nov 2025 13:50:20 +0100 Subject: feat: Add generation of a continuous waveforms for SimplePWM --- embassy-stm32/src/timer/low_level.rs | 44 +++++++++++++++++++++++++++++++++-- embassy-stm32/src/timer/simple_pwm.rs | 6 +++++ 2 files changed, 48 insertions(+), 2 deletions(-) diff --git a/embassy-stm32/src/timer/low_level.rs b/embassy-stm32/src/timer/low_level.rs index f0105ece8..670298d23 100644 --- a/embassy-stm32/src/timer/low_level.rs +++ b/embassy-stm32/src/timer/low_level.rs @@ -673,7 +673,7 @@ impl<'d, T: GeneralInstance4Channel> Timer<'d, T> { self.enable_update_dma(true); } - self.waveform_helper(dma, req, channel, duty).await; + self.waveform_helper(dma, req, channel, duty, false).await; // Since DMA is closed before timer update event trigger DMA is turn off, // this can almost always trigger a DMA FIFO error. @@ -780,6 +780,44 @@ impl<'d, T: GeneralInstance4Channel> Timer<'d, T> { let cc_channel = C::CHANNEL; + let original_cc_dma_on_update = self.get_cc_dma_selection() == Ccds::ON_UPDATE; + let original_cc_dma_enabled = self.get_c fsc_dma_enable_state(cc_channel); + + // redirect CC DMA request onto Update Event + if !original_cc_dma_on_update { + self.set_cc_dma_selection(Ccds::ON_UPDATE) + } + + if !original_cc_dma_enabled { + self.set_cc_dma_enable_state(cc_channel, true); + } + + self.waveform_helper(dma, req, cc_channel, duty, false).await; + + // Since DMA is closed before timer Capture Compare Event trigger DMA is turn off, + // this can almost always trigger a DMA FIFO error. + // + // optional TODO: + // clean FEIF after disable UDE + if !original_cc_dma_enabled { + self.set_cc_dma_enable_state(cc_channel, false); + } + + if !original_cc_dma_on_update { + self.set_cc_dma_selection(Ccds::ON_COMPARE) + } + } + + /// Generate a sequence of PWM waveform that will run continously + /// You may want to start this in a new thread as this will block forever + pub async fn waveform_continuous(&mut self, dma: Peri<'_, impl super::Dma>, duty: &[u16]) { + use crate::pac::timer::vals::Ccds; + + #[allow(clippy::let_unit_value)] // eg. stm32f334 + let req = dma.request(); + + let cc_channel = C::CHANNEL; + let original_cc_dma_on_update = self.get_cc_dma_selection() == Ccds::ON_UPDATE; let original_cc_dma_enabled = self.get_cc_dma_enable_state(cc_channel); @@ -792,7 +830,7 @@ impl<'d, T: GeneralInstance4Channel> Timer<'d, T> { self.set_cc_dma_enable_state(cc_channel, true); } - self.waveform_helper(dma, req, cc_channel, duty).await; + self.waveform_helper(dma, req, cc_channel, duty, true).await; // Since DMA is closed before timer Capture Compare Event trigger DMA is turn off, // this can almost always trigger a DMA FIFO error. @@ -814,6 +852,7 @@ impl<'d, T: GeneralInstance4Channel> Timer<'d, T> { req: dma::Request, channel: Channel, duty: &[u16], + circular: bool, ) { let original_duty_state = self.get_compare_value(channel); let original_enable_state = self.get_channel_enable_state(channel); @@ -832,6 +871,7 @@ impl<'d, T: GeneralInstance4Channel> Timer<'d, T> { fifo_threshold: Some(FifoThreshold::Full), #[cfg(not(any(bdma, gpdma)))] mburst: Burst::Incr8, + circular: circular, ..Default::default() }; diff --git a/embassy-stm32/src/timer/simple_pwm.rs b/embassy-stm32/src/timer/simple_pwm.rs index 6c9ef17e0..56d00ea59 100644 --- a/embassy-stm32/src/timer/simple_pwm.rs +++ b/embassy-stm32/src/timer/simple_pwm.rs @@ -368,6 +368,12 @@ impl<'d, T: GeneralInstance4Channel> SimplePwm<'d, T> { pub async fn waveform(&mut self, dma: Peri<'_, impl super::Dma>, duty: &[u16]) { self.inner.waveform(dma, duty).await; } + /// Generate a sequence of PWM waveform that will run continously + /// You may want to start this in a new thread as this will block forever + #[inline(always)] + pub async fn waveform_continuous(&mut self, dma: Peri<'_, impl super::Dma>, duty: &[u16]) { + self.inner.waveform_continuous(dma, duty).await; + } } impl<'d, T: GeneralInstance4Channel> embedded_hal_1::pwm::ErrorType for SimplePwmChannel<'d, T> { -- cgit From ac1f0ec4ecc6450de7d1c6044e799bca8791f7e5 Mon Sep 17 00:00:00 2001 From: Eicke Hecht Date: Wed, 19 Nov 2025 14:00:02 +0100 Subject: doc: Add changelog --- embassy-stm32/CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/embassy-stm32/CHANGELOG.md b/embassy-stm32/CHANGELOG.md index b6caf8f65..898a91e4b 100644 --- a/embassy-stm32/CHANGELOG.md +++ b/embassy-stm32/CHANGELOG.md @@ -7,6 +7,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## Unreleased - ReleaseDate +- feat: Add continuous waveform method to SimplePWM - change: stm32/eth: ethernet no longer has a hard dependency on station management, and station management can be used independently ([#4871](https://github.com/embassy-rs/embassy/pull/4871)) - feat: allow embassy_executor::main for low power - feat: Add waveform methods to ComplementaryPwm -- cgit From 141685f6f7ff5e78fb08d3aefb67be5d16485ceb Mon Sep 17 00:00:00 2001 From: xoviat Date: Wed, 19 Nov 2025 13:20:17 -0600 Subject: hsem: add hardware test and rework --- embassy-stm32/CHANGELOG.md | 1 + embassy-stm32/src/hsem/mod.rs | 252 +++++++++++++++++++++++++++++++----------- tests/stm32/Cargo.toml | 10 +- tests/stm32/src/bin/hsem.rs | 47 ++++++++ 4 files changed, 245 insertions(+), 65 deletions(-) create mode 100644 tests/stm32/src/bin/hsem.rs diff --git a/embassy-stm32/CHANGELOG.md b/embassy-stm32/CHANGELOG.md index b6caf8f65..597cbb192 100644 --- a/embassy-stm32/CHANGELOG.md +++ b/embassy-stm32/CHANGELOG.md @@ -7,6 +7,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## Unreleased - ReleaseDate +- change: rework hsem and add HIL test for some chips. - change: stm32/eth: ethernet no longer has a hard dependency on station management, and station management can be used independently ([#4871](https://github.com/embassy-rs/embassy/pull/4871)) - feat: allow embassy_executor::main for low power - feat: Add waveform methods to ComplementaryPwm diff --git a/embassy-stm32/src/hsem/mod.rs b/embassy-stm32/src/hsem/mod.rs index 4d3a5d68d..a6b910a53 100644 --- a/embassy-stm32/src/hsem/mod.rs +++ b/embassy-stm32/src/hsem/mod.rs @@ -1,14 +1,20 @@ //! Hardware Semaphore (HSEM) +use core::future::poll_fn; +use core::marker::PhantomData; +use core::sync::atomic::{Ordering, compiler_fence}; +use core::task::Poll; + use embassy_hal_internal::PeripheralType; +use embassy_sync::waitqueue::AtomicWaker; // TODO: This code works for all HSEM implemenations except for the STM32WBA52/4/5xx MCUs. // Those MCUs have a different HSEM implementation (Secure semaphore lock support, // Privileged / unprivileged semaphore lock support, Semaphore lock protection via semaphore attribute), // which is not yet supported by this code. use crate::Peri; -use crate::pac; use crate::rcc::{self, RccPeripheral}; +use crate::{interrupt, pac}; /// HSEM error. #[derive(Debug)] @@ -41,63 +47,136 @@ pub enum CoreId { Core1 = 0x8, } -/// Get the current core id -/// This code assume that it is only executed on a Cortex-M M0+, M4 or M7 core. -#[inline(always)] -pub fn get_current_coreid() -> CoreId { - let cpuid = unsafe { cortex_m::peripheral::CPUID::PTR.read_volatile().base.read() }; - match (cpuid & 0x000000F0) >> 4 { - #[cfg(any(stm32wb, stm32wl))] - 0x0 => CoreId::Core1, +impl CoreId { + /// Get the current core id + /// This code assume that it is only executed on a Cortex-M M0+, M4 or M7 core. + pub fn current() -> Self { + let cpuid = unsafe { cortex_m::peripheral::CPUID::PTR.read_volatile().base.read() }; + match (cpuid & 0x000000F0) >> 4 { + #[cfg(any(stm32wb, stm32wl))] + 0x0 => CoreId::Core1, + + #[cfg(not(any(stm32h745, stm32h747, stm32h755, stm32h757)))] + 0x4 => CoreId::Core0, - #[cfg(not(any(stm32h745, stm32h747, stm32h755, stm32h757)))] - 0x4 => CoreId::Core0, + #[cfg(any(stm32h745, stm32h747, stm32h755, stm32h757))] + 0x4 => CoreId::Core1, - #[cfg(any(stm32h745, stm32h747, stm32h755, stm32h757))] - 0x4 => CoreId::Core1, + #[cfg(any(stm32h745, stm32h747, stm32h755, stm32h757))] + 0x7 => CoreId::Core0, + _ => panic!("Unknown Cortex-M core"), + } + } - #[cfg(any(stm32h745, stm32h747, stm32h755, stm32h757))] - 0x7 => CoreId::Core0, - _ => panic!("Unknown Cortex-M core"), + /// Translates the core ID to an index into the interrupt registers. + pub fn to_index(&self) -> usize { + match &self { + CoreId::Core0 => 0, + #[cfg(any(stm32h745, stm32h747, stm32h755, stm32h757, stm32wb, stm32wl))] + CoreId::Core1 => 1, + } } } -/// Translates the core ID to an index into the interrupt registers. -#[inline(always)] -fn core_id_to_index(core: CoreId) -> usize { - match core { - CoreId::Core0 => 0, - #[cfg(any(stm32h745, stm32h747, stm32h755, stm32h757, stm32wb, stm32wl))] - CoreId::Core1 => 1, +/// TX interrupt handler. +pub struct HardwareSemaphoreInterruptHandler { + _phantom: PhantomData, +} + +impl interrupt::typelevel::Handler for HardwareSemaphoreInterruptHandler { + unsafe fn on_interrupt() { + let core_id = CoreId::current(); + + for number in 0..5 { + if T::regs().isr(core_id.to_index()).read().isf(number as usize) { + T::regs() + .icr(core_id.to_index()) + .write(|w| w.set_isc(number as usize, true)); + + T::regs() + .ier(core_id.to_index()) + .modify(|w| w.set_ise(number as usize, false)); + + T::state().waker_for(number).wake(); + } + } } } -/// HSEM driver -pub struct HardwareSemaphore<'d, T: Instance> { - _peri: Peri<'d, T>, +/// Hardware semaphore mutex +pub struct HardwareSemaphoreMutex<'a, T: Instance> { + index: u8, + process_id: u8, + _lifetime: PhantomData<&'a mut T>, } -impl<'d, T: Instance> HardwareSemaphore<'d, T> { - /// Creates a new HardwareSemaphore instance. - pub fn new(peripheral: Peri<'d, T>) -> Self { - rcc::enable_and_reset::(); +impl<'a, T: Instance> Drop for HardwareSemaphoreMutex<'a, T> { + fn drop(&mut self) { + HardwareSemaphoreChannel::<'a, T> { + index: self.index, + _lifetime: PhantomData, + } + .unlock(self.process_id); + } +} + +/// Hardware semaphore channel +pub struct HardwareSemaphoreChannel<'a, T: Instance> { + index: u8, + _lifetime: PhantomData<&'a mut T>, +} + +impl<'a, T: Instance> HardwareSemaphoreChannel<'a, T> { + pub(self) const fn new(number: u8) -> Self { + core::assert!(number > 0 && number <= 6); + + Self { + index: number - 1, + _lifetime: PhantomData, + } + } + + /// Locks the semaphore. + /// The 2-step lock procedure consists in a write to lock the semaphore, followed by a read to + /// check if the lock has been successful, carried out from the HSEM_Rx register. + pub async fn lock(&mut self, process_id: u8) -> HardwareSemaphoreMutex<'a, T> { + let core_id = CoreId::current(); + + poll_fn(|cx| { + T::state().waker_for(self.index).register(cx.waker()); + + compiler_fence(Ordering::SeqCst); - HardwareSemaphore { _peri: peripheral } + T::regs() + .ier(core_id.to_index()) + .modify(|w| w.set_ise(self.index as usize, true)); + + if self.two_step_lock(process_id).is_ok() { + Poll::Ready(HardwareSemaphoreMutex { + index: self.index, + process_id: process_id, + _lifetime: PhantomData, + }) + } else { + Poll::Pending + } + }) + .await } /// Locks the semaphore. /// The 2-step lock procedure consists in a write to lock the semaphore, followed by a read to /// check if the lock has been successful, carried out from the HSEM_Rx register. - pub fn two_step_lock(&mut self, sem_id: u8, process_id: u8) -> Result<(), HsemError> { - T::regs().r(sem_id as usize).write(|w| { + pub fn two_step_lock(&mut self, process_id: u8) -> Result<(), HsemError> { + T::regs().r(self.index as usize).write(|w| { w.set_procid(process_id); - w.set_coreid(get_current_coreid() as u8); + w.set_coreid(CoreId::current() as u8); w.set_lock(true); }); - let reg = T::regs().r(sem_id as usize).read(); + let reg = T::regs().r(self.index as usize).read(); match ( reg.lock(), - reg.coreid() == get_current_coreid() as u8, + reg.coreid() == CoreId::current() as u8, reg.procid() == process_id, ) { (true, true, true) => Ok(()), @@ -108,9 +187,9 @@ impl<'d, T: Instance> HardwareSemaphore<'d, T> { /// Locks the semaphore. /// The 1-step procedure consists in a read to lock and check the semaphore in a single step, /// carried out from the HSEM_RLRx register. - pub fn one_step_lock(&mut self, sem_id: u8) -> Result<(), HsemError> { - let reg = T::regs().rlr(sem_id as usize).read(); - match (reg.lock(), reg.coreid() == get_current_coreid() as u8, reg.procid()) { + pub fn one_step_lock(&mut self) -> Result<(), HsemError> { + let reg = T::regs().rlr(self.index as usize).read(); + match (reg.lock(), reg.coreid() == CoreId::current() as u8, reg.procid()) { (false, true, 0) => Ok(()), _ => Err(HsemError::LockFailed), } @@ -119,14 +198,53 @@ impl<'d, T: Instance> HardwareSemaphore<'d, T> { /// Unlocks the semaphore. /// Unlocking a semaphore is a protected process, to prevent accidental clearing by a AHB bus /// core ID or by a process not having the semaphore lock right. - pub fn unlock(&mut self, sem_id: u8, process_id: u8) { - T::regs().r(sem_id as usize).write(|w| { + pub fn unlock(&mut self, process_id: u8) { + T::regs().r(self.index as usize).write(|w| { w.set_procid(process_id); - w.set_coreid(get_current_coreid() as u8); + w.set_coreid(CoreId::current() as u8); w.set_lock(false); }); } + /// Checks if the semaphore is locked. + pub fn is_semaphore_locked(&self) -> bool { + T::regs().r(self.index as usize).read().lock() + } +} + +/// HSEM driver +pub struct HardwareSemaphore { + _type: PhantomData, +} + +impl HardwareSemaphore { + /// Creates a new HardwareSemaphore instance. + pub fn new<'d>( + _peripheral: Peri<'d, T>, + _irq: impl interrupt::typelevel::Binding> + 'd, + ) -> Self { + rcc::enable_and_reset::(); + + HardwareSemaphore { _type: PhantomData } + } + + /// Get a single channel, and keep the global struct + pub const fn channel_for<'a>(&'a mut self, number: u8) -> HardwareSemaphoreChannel<'a, T> { + HardwareSemaphoreChannel::new(number) + } + + /// Split the global struct into channels + pub const fn split<'a>(self) -> [HardwareSemaphoreChannel<'a, T>; 6] { + [ + HardwareSemaphoreChannel::new(1), + HardwareSemaphoreChannel::new(2), + HardwareSemaphoreChannel::new(3), + HardwareSemaphoreChannel::new(4), + HardwareSemaphoreChannel::new(5), + HardwareSemaphoreChannel::new(6), + ] + } + /// Unlocks all semaphores. /// All semaphores locked by a COREID can be unlocked at once by using the HSEM_CR /// register. Write COREID and correct KEY value in HSEM_CR. All locked semaphores with a @@ -138,11 +256,6 @@ impl<'d, T: Instance> HardwareSemaphore<'d, T> { }); } - /// Checks if the semaphore is locked. - pub fn is_semaphore_locked(&self, sem_id: u8) -> bool { - T::regs().r(sem_id as usize).read().lock() - } - /// Sets the clear (unlock) key pub fn set_clear_key(&mut self, key: u16) { T::regs().keyr().modify(|w| w.set_key(key)); @@ -152,38 +265,51 @@ impl<'d, T: Instance> HardwareSemaphore<'d, T> { pub fn get_clear_key(&mut self) -> u16 { T::regs().keyr().read().key() } +} - /// Sets the interrupt enable bit for the semaphore. - pub fn enable_interrupt(&mut self, core_id: CoreId, sem_x: usize, enable: bool) { - T::regs() - .ier(core_id_to_index(core_id)) - .modify(|w| w.set_ise(sem_x, enable)); - } +struct State { + wakers: [AtomicWaker; 6], +} - /// Gets the interrupt flag for the semaphore. - pub fn is_interrupt_active(&mut self, core_id: CoreId, sem_x: usize) -> bool { - T::regs().isr(core_id_to_index(core_id)).read().isf(sem_x) +impl State { + const fn new() -> Self { + Self { + wakers: [const { AtomicWaker::new() }; 6], + } } - /// Clears the interrupt flag for the semaphore. - pub fn clear_interrupt(&mut self, core_id: CoreId, sem_x: usize) { - T::regs() - .icr(core_id_to_index(core_id)) - .write(|w| w.set_isc(sem_x, false)); + const fn waker_for(&self, number: u8) -> &AtomicWaker { + &self.wakers[number as usize] } } trait SealedInstance { fn regs() -> pac::hsem::Hsem; + fn state() -> &'static State; } /// HSEM instance trait. #[allow(private_bounds)] -pub trait Instance: SealedInstance + PeripheralType + RccPeripheral + Send + 'static {} +pub trait Instance: SealedInstance + PeripheralType + RccPeripheral + Send + 'static { + /// Interrupt for this peripheral. + type Interrupt: interrupt::typelevel::Interrupt; +} impl SealedInstance for crate::peripherals::HSEM { fn regs() -> crate::pac::hsem::Hsem { crate::pac::HSEM } + + fn state() -> &'static State { + static STATE: State = State::new(); + &STATE + } } -impl Instance for crate::peripherals::HSEM {} + +foreach_interrupt!( + ($inst:ident, hsem, $block:ident, $signal_name:ident, $irq:ident) => { + impl Instance for crate::peripherals::$inst { + type Interrupt = crate::interrupt::typelevel::$irq; + } + }; +); diff --git a/tests/stm32/Cargo.toml b/tests/stm32/Cargo.toml index b92b47be2..f5684d4df 100644 --- a/tests/stm32/Cargo.toml +++ b/tests/stm32/Cargo.toml @@ -31,9 +31,9 @@ stm32l4r5zi = ["embassy-stm32/stm32l4r5zi", "chrono", "not-gpdma", "rng", "dual- stm32l552ze = ["embassy-stm32/stm32l552ze", "not-gpdma", "rng", "hash", "dual-bank"] stm32u585ai = ["embassy-stm32/stm32u585ai", "spi-v345", "chrono", "rng", "hash", "cordic"] stm32u5a5zj = ["embassy-stm32/stm32u5a5zj", "spi-v345", "chrono", "rng", "hash"] # FIXME: cordic test cause it crash -stm32wb55rg = ["embassy-stm32/stm32wb55rg", "chrono", "not-gpdma", "ble", "mac" , "rng"] +stm32wb55rg = ["embassy-stm32/stm32wb55rg", "chrono", "not-gpdma", "ble", "mac" , "rng", "hsem"] stm32wba52cg = ["embassy-stm32/stm32wba52cg", "spi-v345", "chrono", "rng", "hash"] -stm32wl55jc = ["embassy-stm32/stm32wl55jc-cm4", "not-gpdma", "rng", "chrono"] +stm32wl55jc = ["embassy-stm32/stm32wl55jc-cm4", "not-gpdma", "rng", "chrono", "hsem"] stm32f091rc = ["embassy-stm32/stm32f091rc", "cm0", "not-gpdma", "chrono"] stm32h503rb = ["embassy-stm32/stm32h503rb", "spi-v345", "rng", "stop"] stm32h7s3l8 = ["embassy-stm32/stm32h7s3l8", "spi-v345", "rng", "cordic", "hash-v34"] # TODO: fdcan crashes, cryp dma hangs. @@ -58,6 +58,7 @@ not-gpdma = [] dac = [] ucpd = [] cordic = ["dep:num-traits"] +hsem = [] dual-bank = ["embassy-stm32/dual-bank"] single-bank = ["embassy-stm32/single-bank"] eeprom = [] @@ -224,6 +225,11 @@ name = "wpan_mac" path = "src/bin/wpan_mac.rs" required-features = [ "mac",] +[[bin]] +name = "hsem" +path = "src/bin/hsem.rs" +required-features = [ "hsem",] + # END TESTS [profile.dev] diff --git a/tests/stm32/src/bin/hsem.rs b/tests/stm32/src/bin/hsem.rs new file mode 100644 index 000000000..19648997c --- /dev/null +++ b/tests/stm32/src/bin/hsem.rs @@ -0,0 +1,47 @@ +// required-features: hsem +#![no_std] +#![no_main] + +#[path = "../common.rs"] +mod common; + +use common::*; +use embassy_executor::Spawner; +use embassy_stm32::bind_interrupts; +use embassy_stm32::hsem::{HardwareSemaphore, HardwareSemaphoreInterruptHandler}; +use embassy_stm32::peripherals::HSEM; + +bind_interrupts!(struct Irqs{ + HSEM => HardwareSemaphoreInterruptHandler; +}); + +#[embassy_executor::main] +async fn main(_spawner: Spawner) { + let p: embassy_stm32::Peripherals = init(); + + let hsem = HardwareSemaphore::new(p.HSEM, Irqs); + + // if hsem.channel_for(SemaphoreNumber::Channel5).is_semaphore_locked() { + // defmt::panic!("Semaphore 5 already locked!") + // } + // + // hsem.channel_for(SemaphoreNumber::Channel5).one_step_lock().unwrap(); + // hsem.channel_for(SemaphoreNumber::Channel1).two_step_lock(0).unwrap(); + // + // hsem.channel_for(SemaphoreNumber::Channel5).unlock(0); + + let [_channel1, _channel2, _channel3, _channel4, mut channel5, _channel6] = hsem.split(); + + info!("Locking channel 5"); + + let mutex = channel5.lock(0).await; + + info!("Locked channel 5"); + + drop(mutex); + + info!("Unlocked channel 5"); + + info!("Test OK"); + cortex_m::asm::bkpt(); +} -- cgit From 4104d114955be79cf419cb4cfb5c0e72b0abd261 Mon Sep 17 00:00:00 2001 From: xoviat Date: Wed, 19 Nov 2025 15:24:05 -0600 Subject: stm32: impl. stop for stm32wb --- embassy-stm32/CHANGELOG.md | 1 + embassy-stm32/src/hsem/mod.rs | 60 +++++++++++++++++++++++++++---------- embassy-stm32/src/lib.rs | 3 ++ embassy-stm32/src/low_power.rs | 68 ++++++++++++++++++++++++++++++++++++++---- examples/stm32wb/Cargo.toml | 2 +- 5 files changed, 112 insertions(+), 22 deletions(-) diff --git a/embassy-stm32/CHANGELOG.md b/embassy-stm32/CHANGELOG.md index 597cbb192..7d8e7c617 100644 --- a/embassy-stm32/CHANGELOG.md +++ b/embassy-stm32/CHANGELOG.md @@ -7,6 +7,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## Unreleased - ReleaseDate +- feat: implement stop for stm32wb. - change: rework hsem and add HIL test for some chips. - change: stm32/eth: ethernet no longer has a hard dependency on station management, and station management can be used independently ([#4871](https://github.com/embassy-rs/embassy/pull/4871)) - feat: allow embassy_executor::main for low power diff --git a/embassy-stm32/src/hsem/mod.rs b/embassy-stm32/src/hsem/mod.rs index a6b910a53..5f1ed9b09 100644 --- a/embassy-stm32/src/hsem/mod.rs +++ b/embassy-stm32/src/hsem/mod.rs @@ -5,6 +5,8 @@ use core::marker::PhantomData; use core::sync::atomic::{Ordering, compiler_fence}; use core::task::Poll; +#[cfg(all(stm32wb, feature = "low-power"))] +use critical_section::CriticalSection; use embassy_hal_internal::PeripheralType; use embassy_sync::waitqueue::AtomicWaker; @@ -78,6 +80,12 @@ impl CoreId { } } +#[cfg(not(all(stm32wb, feature = "low-power")))] +const PUB_CHANNELS: usize = 6; + +#[cfg(all(stm32wb, feature = "low-power"))] +const PUB_CHANNELS: usize = 4; + /// TX interrupt handler. pub struct HardwareSemaphoreInterruptHandler { _phantom: PhantomData, @@ -127,7 +135,7 @@ pub struct HardwareSemaphoreChannel<'a, T: Instance> { } impl<'a, T: Instance> HardwareSemaphoreChannel<'a, T> { - pub(self) const fn new(number: u8) -> Self { + pub(crate) const fn new(number: u8) -> Self { core::assert!(number > 0 && number <= 6); Self { @@ -151,19 +159,29 @@ impl<'a, T: Instance> HardwareSemaphoreChannel<'a, T> { .ier(core_id.to_index()) .modify(|w| w.set_ise(self.index as usize, true)); - if self.two_step_lock(process_id).is_ok() { - Poll::Ready(HardwareSemaphoreMutex { - index: self.index, - process_id: process_id, - _lifetime: PhantomData, - }) - } else { - Poll::Pending + match self.try_lock(process_id) { + Some(mutex) => Poll::Ready(mutex), + None => Poll::Pending, } }) .await } + /// Try to lock the semaphor + /// The 2-step lock procedure consists in a write to lock the semaphore, followed by a read to + /// check if the lock has been successful, carried out from the HSEM_Rx register. + pub fn try_lock(&mut self, process_id: u8) -> Option> { + if self.two_step_lock(process_id).is_ok() { + Some(HardwareSemaphoreMutex { + index: self.index, + process_id: process_id, + _lifetime: PhantomData, + }) + } else { + None + } + } + /// Locks the semaphore. /// The 2-step lock procedure consists in a write to lock the semaphore, followed by a read to /// check if the lock has been successful, carried out from the HSEM_Rx register. @@ -206,9 +224,9 @@ impl<'a, T: Instance> HardwareSemaphoreChannel<'a, T> { }); } - /// Checks if the semaphore is locked. - pub fn is_semaphore_locked(&self) -> bool { - T::regs().r(self.index as usize).read().lock() + /// Return the channel number + pub const fn channel(&self) -> u8 { + self.index + 1 } } @@ -230,15 +248,22 @@ impl HardwareSemaphore { /// Get a single channel, and keep the global struct pub const fn channel_for<'a>(&'a mut self, number: u8) -> HardwareSemaphoreChannel<'a, T> { + #[cfg(all(stm32wb, feature = "low-power"))] + core::assert!(number != 3 && number != 4); + HardwareSemaphoreChannel::new(number) } /// Split the global struct into channels - pub const fn split<'a>(self) -> [HardwareSemaphoreChannel<'a, T>; 6] { + /// + /// If using low-power mode, channels 3 and 4 will not be returned + pub const fn split<'a>(self) -> [HardwareSemaphoreChannel<'a, T>; PUB_CHANNELS] { [ HardwareSemaphoreChannel::new(1), HardwareSemaphoreChannel::new(2), + #[cfg(not(all(stm32wb, feature = "low-power")))] HardwareSemaphoreChannel::new(3), + #[cfg(not(all(stm32wb, feature = "low-power")))] HardwareSemaphoreChannel::new(4), HardwareSemaphoreChannel::new(5), HardwareSemaphoreChannel::new(6), @@ -267,6 +292,11 @@ impl HardwareSemaphore { } } +#[cfg(all(stm32wb, feature = "low-power"))] +pub(crate) fn init_hsem(_cs: CriticalSection) { + rcc::enable_and_reset::(); +} + struct State { wakers: [AtomicWaker; 6], } @@ -278,8 +308,8 @@ impl State { } } - const fn waker_for(&self, number: u8) -> &AtomicWaker { - &self.wakers[number as usize] + const fn waker_for(&self, index: u8) -> &AtomicWaker { + &self.wakers[index as usize] } } diff --git a/embassy-stm32/src/lib.rs b/embassy-stm32/src/lib.rs index 7c3770643..ef6f1d6dc 100644 --- a/embassy-stm32/src/lib.rs +++ b/embassy-stm32/src/lib.rs @@ -650,6 +650,9 @@ fn init_hw(config: Config) -> Peripherals { #[cfg(feature = "low-power")] rtc::init_rtc(cs, config.rtc, config.min_stop_pause); + + #[cfg(all(stm32wb, feature = "low-power"))] + hsem::init_hsem(cs); } p diff --git a/embassy-stm32/src/low_power.rs b/embassy-stm32/src/low_power.rs index cf8f2b393..15478eed4 100644 --- a/embassy-stm32/src/low_power.rs +++ b/embassy-stm32/src/low_power.rs @@ -148,7 +148,7 @@ pub enum StopMode { } #[cfg(any(stm32l4, stm32l5, stm32u5, stm32wba, stm32wlex, stm32u0))] -use stm32_metapac::pwr::vals::Lpms; +use crate::pac::pwr::vals::Lpms; #[cfg(any(stm32l4, stm32l5, stm32u5, stm32wba, stm32wlex, stm32u0))] impl Into for StopMode { @@ -242,8 +242,63 @@ impl Executor { } } + #[cfg(all(stm32wb, feature = "low-power"))] + fn configure_stop_stm32wb(&self, _stop_mode: StopMode) -> Result<(), ()> { + use core::task::Poll; + + use embassy_futures::poll_once; + + use crate::hsem::HardwareSemaphoreChannel; + use crate::pac::rcc::vals::{Smps, Sw}; + use crate::pac::{PWR, RCC}; + + let sem3_mutex = match poll_once(HardwareSemaphoreChannel::::new(3).lock(0)) { + Poll::Pending => None, + Poll::Ready(mutex) => Some(mutex), + } + .ok_or(())?; + + let sem4_mutex = HardwareSemaphoreChannel::::new(4).try_lock(0); + if let Some(sem4_mutex) = sem4_mutex { + if PWR.extscr().read().c2ds() { + drop(sem4_mutex); + } else { + return Ok(()); + } + } + + // Sem4 not granted + // Set HSION + RCC.cr().modify(|w| { + w.set_hsion(true); + }); + + // Wait for HSIRDY + while !RCC.cr().read().hsirdy() {} + + // Set SW to HSI + RCC.cfgr().modify(|w| { + w.set_sw(Sw::HSI); + }); + + // Wait for SWS to report HSI + while !RCC.cfgr().read().sws().eq(&Sw::HSI) {} + + // Set SMPSSEL to HSI + RCC.smpscr().modify(|w| { + w.set_smpssel(Smps::HSI); + }); + + drop(sem3_mutex); + + Ok(()) + } + #[allow(unused_variables)] - fn configure_stop(&self, stop_mode: StopMode) { + fn configure_stop(&self, stop_mode: StopMode) -> Result<(), ()> { + #[cfg(all(stm32wb, feature = "low-power"))] + self.configure_stop_stm32wb(stop_mode)?; + #[cfg(any(stm32l4, stm32l5, stm32u5, stm32u0, stm32wba, stm32wlex))] crate::pac::PWR.cr1().modify(|m| m.set_lpms(stop_mode.into())); #[cfg(stm32h5)] @@ -252,6 +307,8 @@ impl Executor { v.set_lpms(vals::Lpms::STOP); v.set_svos(vals::Svos::SCALE3); }); + + Ok(()) } fn configure_pwr(&self) { @@ -267,12 +324,11 @@ impl Executor { critical_section::with(|cs| { let stop_mode = Self::stop_mode(cs)?; let _ = get_driver().pause_time(cs).ok()?; + self.configure_stop(stop_mode).ok()?; - Some(stop_mode) + Some(()) }) - .map(|stop_mode| { - self.configure_stop(stop_mode); - + .map(|_| { #[cfg(not(feature = "low-power-debug-with-sleep"))] Self::get_scb().set_sleepdeep(); }); diff --git a/examples/stm32wb/Cargo.toml b/examples/stm32wb/Cargo.toml index 783690c11..83119e3a0 100644 --- a/examples/stm32wb/Cargo.toml +++ b/examples/stm32wb/Cargo.toml @@ -7,7 +7,7 @@ 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"] } +embassy-stm32 = { version = "0.4.0", path = "../../embassy-stm32", features = [ "defmt", "stm32wb55rg", "time-driver-any", "memory-x", "exti", "low-power"] } 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"] } -- cgit From 19e61543198e2d15fd4c7aef9377c8f40ae86ae0 Mon Sep 17 00:00:00 2001 From: xoviat Date: Wed, 19 Nov 2025 16:14:23 -0600 Subject: stm32: impl. low power test for stm32wb55 --- embassy-stm32/src/hsem/mod.rs | 9 +++++++-- embassy-stm32/src/low_power.rs | 24 ++++++++++++++++-------- tests/stm32/Cargo.toml | 2 +- tests/stm32/src/bin/hsem.rs | 3 +++ 4 files changed, 27 insertions(+), 11 deletions(-) diff --git a/embassy-stm32/src/hsem/mod.rs b/embassy-stm32/src/hsem/mod.rs index 5f1ed9b09..e62de0454 100644 --- a/embassy-stm32/src/hsem/mod.rs +++ b/embassy-stm32/src/hsem/mod.rs @@ -293,8 +293,13 @@ impl HardwareSemaphore { } #[cfg(all(stm32wb, feature = "low-power"))] -pub(crate) fn init_hsem(_cs: CriticalSection) { - rcc::enable_and_reset::(); +pub(crate) fn init_hsem(cs: CriticalSection) { + rcc::enable_and_reset_with_cs::(cs); + + unsafe { + crate::rcc::REFCOUNT_STOP1 = 0; + crate::rcc::REFCOUNT_STOP2 = 0; + } } struct State { diff --git a/embassy-stm32/src/low_power.rs b/embassy-stm32/src/low_power.rs index 15478eed4..f35eb71e4 100644 --- a/embassy-stm32/src/low_power.rs +++ b/embassy-stm32/src/low_power.rs @@ -147,17 +147,17 @@ pub enum StopMode { Stop2, } -#[cfg(any(stm32l4, stm32l5, stm32u5, stm32wba, stm32wlex, stm32u0))] +#[cfg(any(stm32l4, stm32l5, stm32u5, stm32wba, stm32wb, stm32wlex, stm32u0))] use crate::pac::pwr::vals::Lpms; -#[cfg(any(stm32l4, stm32l5, stm32u5, stm32wba, stm32wlex, stm32u0))] +#[cfg(any(stm32l4, stm32l5, stm32u5, stm32wba, stm32wb, stm32wlex, stm32u0))] impl Into for StopMode { fn into(self) -> Lpms { match self { StopMode::Stop1 => Lpms::STOP1, - #[cfg(not(stm32wba))] + #[cfg(not(any(stm32wb, stm32wba)))] StopMode::Stop2 => Lpms::STOP2, - #[cfg(stm32wba)] + #[cfg(any(stm32wb, stm32wba))] StopMode::Stop2 => Lpms::STOP1, // TODO: WBA has no STOP2? } } @@ -237,13 +237,15 @@ impl Executor { trace!("low power: stop 1"); Some(StopMode::Stop1) } else { - trace!("low power: not ready to stop"); + trace!("low power: not ready to stop (refcount_stop1: {})", unsafe { + REFCOUNT_STOP1 + }); None } } #[cfg(all(stm32wb, feature = "low-power"))] - fn configure_stop_stm32wb(&self, _stop_mode: StopMode) -> Result<(), ()> { + fn configure_stop_stm32wb(&self) -> Result<(), ()> { use core::task::Poll; use embassy_futures::poll_once; @@ -252,14 +254,20 @@ impl Executor { use crate::pac::rcc::vals::{Smps, Sw}; use crate::pac::{PWR, RCC}; + trace!("low power: trying to get sem3"); + let sem3_mutex = match poll_once(HardwareSemaphoreChannel::::new(3).lock(0)) { Poll::Pending => None, Poll::Ready(mutex) => Some(mutex), } .ok_or(())?; + trace!("low power: got sem3"); + let sem4_mutex = HardwareSemaphoreChannel::::new(4).try_lock(0); if let Some(sem4_mutex) = sem4_mutex { + trace!("low power: got sem4"); + if PWR.extscr().read().c2ds() { drop(sem4_mutex); } else { @@ -297,9 +305,9 @@ impl Executor { #[allow(unused_variables)] fn configure_stop(&self, stop_mode: StopMode) -> Result<(), ()> { #[cfg(all(stm32wb, feature = "low-power"))] - self.configure_stop_stm32wb(stop_mode)?; + self.configure_stop_stm32wb()?; - #[cfg(any(stm32l4, stm32l5, stm32u5, stm32u0, stm32wba, stm32wlex))] + #[cfg(any(stm32l4, stm32l5, stm32u5, stm32u0, stm32wb, stm32wba, stm32wlex))] crate::pac::PWR.cr1().modify(|m| m.set_lpms(stop_mode.into())); #[cfg(stm32h5)] crate::pac::PWR.pmcr().modify(|v| { diff --git a/tests/stm32/Cargo.toml b/tests/stm32/Cargo.toml index f5684d4df..8fcb6b2b4 100644 --- a/tests/stm32/Cargo.toml +++ b/tests/stm32/Cargo.toml @@ -31,7 +31,7 @@ stm32l4r5zi = ["embassy-stm32/stm32l4r5zi", "chrono", "not-gpdma", "rng", "dual- stm32l552ze = ["embassy-stm32/stm32l552ze", "not-gpdma", "rng", "hash", "dual-bank"] stm32u585ai = ["embassy-stm32/stm32u585ai", "spi-v345", "chrono", "rng", "hash", "cordic"] stm32u5a5zj = ["embassy-stm32/stm32u5a5zj", "spi-v345", "chrono", "rng", "hash"] # FIXME: cordic test cause it crash -stm32wb55rg = ["embassy-stm32/stm32wb55rg", "chrono", "not-gpdma", "ble", "mac" , "rng", "hsem"] +stm32wb55rg = ["embassy-stm32/stm32wb55rg", "chrono", "not-gpdma", "ble", "mac" , "rng", "hsem", "stop"] stm32wba52cg = ["embassy-stm32/stm32wba52cg", "spi-v345", "chrono", "rng", "hash"] stm32wl55jc = ["embassy-stm32/stm32wl55jc-cm4", "not-gpdma", "rng", "chrono", "hsem"] stm32f091rc = ["embassy-stm32/stm32f091rc", "cm0", "not-gpdma", "chrono"] diff --git a/tests/stm32/src/bin/hsem.rs b/tests/stm32/src/bin/hsem.rs index 19648997c..fa69f22b2 100644 --- a/tests/stm32/src/bin/hsem.rs +++ b/tests/stm32/src/bin/hsem.rs @@ -30,6 +30,9 @@ async fn main(_spawner: Spawner) { // // hsem.channel_for(SemaphoreNumber::Channel5).unlock(0); + #[cfg(feature = "stm32wb55rg")] + let [_channel1, _channel2, mut channel5, _channel6] = hsem.split(); + #[cfg(not(feature = "stm32wb55rg"))] let [_channel1, _channel2, _channel3, _channel4, mut channel5, _channel6] = hsem.split(); info!("Locking channel 5"); -- cgit From de4d7f56473df58d9b3fa8ec4917ab86550005ae Mon Sep 17 00:00:00 2001 From: WillaWillNot Date: Fri, 14 Nov 2025 17:00:23 -0500 Subject: Added type-erased AnyBinding for interrupt-handler bindings, and changed Exti driver to accept custom bindings without sacrificing its ability to accept type-erased Channels --- embassy-hal-internal/src/interrupt.rs | 55 +++++++++++++++++++++++++++++++- embassy-stm32/src/exti.rs | 59 +++++++++++++++++++++++++---------- 2 files changed, 97 insertions(+), 17 deletions(-) diff --git a/embassy-hal-internal/src/interrupt.rs b/embassy-hal-internal/src/interrupt.rs index ce6057e2d..c0ea666da 100644 --- a/embassy-hal-internal/src/interrupt.rs +++ b/embassy-hal-internal/src/interrupt.rs @@ -124,6 +124,10 @@ macro_rules! interrupt_mod { /// /// This function must ONLY be called from the interrupt handler for `I`. unsafe fn on_interrupt(); + + /// Source ID of the Handler. No need to override the default outside of internal implementations, + /// where it will always be HandlerType::User. + const SOURCE_ID: HandlerType = HandlerType::User; } /// Compile-time assertion that an interrupt has been bound to a handler. @@ -137,7 +141,56 @@ macro_rules! interrupt_mod { /// to be called every time the `I` interrupt fires. /// /// This allows drivers to check bindings at compile-time. - pub unsafe trait Binding> {} + pub unsafe trait Binding> { + /// Obtain a type-erased Binding. + /// + /// If using the `bind_interrupts!` macro, you will likely have to use a fully qualified path + /// to call this on the output struct: `>::into_any()` + fn into_any() -> AnyBinding { + AnyBinding { + irq: I::IRQ, + handler_source: H::SOURCE_ID, + } + } + } + + #[doc(hidden)] + #[derive(Copy, Clone)] + pub struct PrivateHandlerType { + pub(crate) _private: (), + } + impl PrivateHandlerType { + pub(crate) const fn new() -> Self { + Self { + _private: (), + } + } + } + + /// Driver which defined the Handler. Always User for user-defined handlers. + #[derive(Copy, Clone)] + pub enum HandlerType { + User, + Embassy(PrivateHandlerType), + EmbassyStm32Exti(PrivateHandlerType), + } + + /// Type-erased Binding. + /// + /// Useful for proving a particular binding has been made to a driver which also accepts + /// type-erased peripheral arguments that hide the necessary Interrupt type at compile time. + pub struct AnyBinding { + irq: super::Interrupt, + handler_source: HandlerType, + } + impl AnyBinding { + pub const fn irq(&self) -> super::Interrupt { + self.irq + } + pub const fn source(&self) -> HandlerType { + self.handler_source + } + } } } }; diff --git a/embassy-stm32/src/exti.rs b/embassy-stm32/src/exti.rs index 899d5e677..172435caa 100644 --- a/embassy-stm32/src/exti.rs +++ b/embassy-stm32/src/exti.rs @@ -10,9 +10,11 @@ use embassy_sync::waitqueue::AtomicWaker; use futures_util::FutureExt; use crate::gpio::{AnyPin, Input, Level, Pin as GpioPin, PinNumber, Pull}; +use crate::interrupt::Interrupt as InterruptEnum; +use crate::interrupt::typelevel::{AnyBinding, HandlerType, Interrupt as InterruptType, PrivateHandlerType}; use crate::pac::EXTI; use crate::pac::exti::regs::Lines; -use crate::{Peri, interrupt, pac, peripherals}; +use crate::{Peri, pac, peripherals}; const EXTI_COUNT: usize = 16; static EXTI_WAKERS: [AtomicWaker; EXTI_COUNT] = [const { AtomicWaker::new() }; EXTI_COUNT]; @@ -106,15 +108,24 @@ impl<'d> Unpin for ExtiInput<'d> {} impl<'d> ExtiInput<'d> { /// Create an EXTI input. - pub fn new(pin: Peri<'d, T>, ch: Peri<'d, T::ExtiChannel>, pull: Pull) -> Self { + /// + /// The interrupt [Binding] must be type-erased to [AnyBinding] via [Binding::into_any()] in order + /// to support type-erased [AnyChannel] arguments. + /// + /// The Binding must bind the Channel's IRQ to [InterruptHandler]. + pub fn new(pin: Peri<'d, T>, ch: Peri<'d, T::ExtiChannel>, pull: Pull, binding: AnyBinding) -> Self { // Needed if using AnyPin+AnyChannel. assert_eq!(pin.pin(), ch.number()); + assert_eq!(ch.irq(), binding.irq()); + assert!(matches!(binding.source(), HandlerType::EmbassyStm32Exti(_))); Self { pin: Input::new(pin, pull), } } + //pub fn new, I: Instance, C: Channel>( + /// Get whether the pin is high. pub fn is_high(&self) -> bool { self.pin.is_high() @@ -328,7 +339,7 @@ macro_rules! foreach_exti_irq { (EXTI15) => { $action!(EXTI15); }; // plus the weird ones - (EXTI0_1) => { $action!( EXTI0_1 ); }; + (EXTI0_1) => { $action!(EXTI0_1); }; (EXTI15_10) => { $action!(EXTI15_10); }; (EXTI15_4) => { $action!(EXTI15_4); }; (EXTI1_0) => { $action!(EXTI1_0); }; @@ -341,18 +352,25 @@ macro_rules! foreach_exti_irq { }; } -macro_rules! impl_irq { - ($e:ident) => { - #[allow(non_snake_case)] - #[cfg(feature = "rt")] - #[interrupt] - unsafe fn $e() { - on_irq() - } - }; +///EXTI interrupt handler. All EXTI interrupt vectors should be bound to this handler. +/// +/// It is generic over the [Interrupt](crate::interrupt::typelevel::Interrupt) rather +/// than the [Instance](crate::exti::Instance) because it should not be bound multiple +/// times to the same vector on chips which multiplex multiple EXTI interrupts into one vector. +// +// It technically doesn't need to be generic at all, except to satisfy the generic argument +// of [Handler](crate::interrupt::typelevel::Handler). All EXTI interrupts eventually +// land in the same on_irq() function. +pub struct InterruptHandler { + _phantom: PhantomData, } -foreach_exti_irq!(impl_irq); +impl crate::interrupt::typelevel::Handler for InterruptHandler { + const SOURCE_ID: HandlerType = HandlerType::EmbassyStm32Exti(PrivateHandlerType::new()); + unsafe fn on_interrupt() { + on_irq() + } +} trait SealedChannel {} @@ -361,6 +379,8 @@ trait SealedChannel {} pub trait Channel: PeripheralType + SealedChannel + Sized { /// Get the EXTI channel number. fn number(&self) -> PinNumber; + /// Get the EXTI IRQ, which may be the same for multiple channels + fn irq(&self) -> InterruptEnum; } /// Type-erased EXTI channel. @@ -368,6 +388,7 @@ pub trait Channel: PeripheralType + SealedChannel + Sized { /// This represents ownership over any EXTI channel, known at runtime. pub struct AnyChannel { number: PinNumber, + irq: InterruptEnum, } impl_peripheral!(AnyChannel); @@ -376,6 +397,9 @@ impl Channel for AnyChannel { fn number(&self) -> PinNumber { self.number } + fn irq(&self) -> InterruptEnum { + self.irq + } } macro_rules! impl_exti { @@ -385,12 +409,15 @@ macro_rules! impl_exti { fn number(&self) -> PinNumber { $number } + fn irq(&self) -> InterruptEnum { + crate::_generated::peripheral_interrupts::EXTI::$type::IRQ + } } - impl From for AnyChannel { - fn from(val: peripherals::$type) -> Self { + fn from(_val: peripherals::$type) -> Self { Self { - number: val.number() as PinNumber, + number: $number, + irq: crate::_generated::peripheral_interrupts::EXTI::$type::IRQ, } } } -- cgit From d111eceb4ba0094d34f58a4695bb3d43d188e591 Mon Sep 17 00:00:00 2001 From: Dion Dokter Date: Thu, 20 Nov 2025 14:16:43 +0100 Subject: Update LCD to modern embassy --- embassy-stm32/src/lcd.rs | 58 +++++++++++++++++++------------------ examples/stm32u0/Cargo.toml | 2 +- examples/stm32u0/src/bin/lcd.rs | 63 +++++++++++++++++++++-------------------- 3 files changed, 64 insertions(+), 59 deletions(-) diff --git a/embassy-stm32/src/lcd.rs b/embassy-stm32/src/lcd.rs index 15d3e8fbc..7c1877a38 100644 --- a/embassy-stm32/src/lcd.rs +++ b/embassy-stm32/src/lcd.rs @@ -1,12 +1,12 @@ //! LCD use core::marker::PhantomData; -use embassy_hal_internal::{into_ref, PeripheralRef}; +use embassy_hal_internal::{Peri, PeripheralType}; -use crate::gpio::{AFType, AnyPin, SealedPin}; +use crate::gpio::{AfType, AnyPin, SealedPin}; +use crate::peripherals; use crate::rcc::{self, RccPeripheral}; use crate::time::Hertz; -use crate::{peripherals, Peripheral}; #[non_exhaustive] #[derive(Debug, Clone, Copy)] @@ -123,21 +123,24 @@ pub struct Lcd<'d, T: Instance> { impl<'d, T: Instance> Lcd<'d, T> { /// Initialize the lcd driver pub fn new( - _peri: impl Peripheral

+ 'd, + _peripheral: Peri<'d, T>, config: Config, - vlcd_pin: impl Peripheral

> + 'd, + vlcd_pin: Peri<'_, impl VlcdPin>, pins: [LcdPin<'d, T>; N], ) -> Self { rcc::enable_and_reset::(); - into_ref!(vlcd_pin); - vlcd_pin.set_as_af(vlcd_pin.af_num(), AFType::OutputPushPull); - vlcd_pin.set_speed(crate::gpio::Speed::VeryHigh); + vlcd_pin.set_as_af( + vlcd_pin.af_num(), + AfType::output(crate::gpio::OutputType::PushPull, crate::gpio::Speed::VeryHigh), + ); // Set the pins for pin in pins { - pin.pin.set_as_af(pin.af_num, AFType::OutputPushPull); - pin.pin.set_speed(crate::gpio::Speed::VeryHigh); + pin.pin.set_as_af( + pin.af_num, + AfType::output(crate::gpio::OutputType::PushPull, crate::gpio::Speed::VeryHigh), + ); } // Initialize the display ram to 0 @@ -147,7 +150,7 @@ impl<'d, T: Instance> Lcd<'d, T> { } // Calculate the clock dividers - let Some(lcd_clk) = (unsafe { rcc::get_freqs().rtc }) else { + let Some(lcd_clk) = (unsafe { rcc::get_freqs().rtc.to_hertz() }) else { panic!("The LCD driver needs the RTC/LCD clock to be running"); }; let duty_divider = match config.duty { @@ -183,10 +186,7 @@ impl<'d, T: Instance> Lcd<'d, T> { trace!( "lcd_clk: {}, fps: {}, ps: {}, div: {}", - lcd_clk, - best_fps_match, - ps, - div + lcd_clk, best_fps_match, ps, div ); if best_fps_match == u32::MAX || ps > 0xF { @@ -228,7 +228,7 @@ impl<'d, T: Instance> Lcd<'d, T> { } /// Change the contrast by changing the voltage being used. - /// + /// /// This from low at 0 to high at 7. pub fn set_contrast_control(&mut self, value: u8) { T::regs().fcr().modify(|w| w.set_cc(value)); @@ -236,17 +236,19 @@ impl<'d, T: Instance> Lcd<'d, T> { /// Change the contrast by introducing a deadtime to the signals /// where the voltages are held at 0V. - /// + /// /// This from no dead time at 0 to high dead time at 7. pub fn set_dead_time(&mut self, value: u8) { - T::regs().fcr().modify(|w: &mut stm32_metapac::lcd::regs::Fcr| w.set_dead(value)); + T::regs() + .fcr() + .modify(|w: &mut stm32_metapac::lcd::regs::Fcr| w.set_dead(value)); } /// Write frame data to the peripheral. - /// + /// /// What each bit means depends on the exact microcontroller you use, /// which pins are connected to your LCD and also the LCD layout itself. - /// + /// /// This function blocks until the last update display request has been processed. pub fn write_frame(&mut self, data: &[u32; 16]) { while T::regs().sr().read().udr() {} @@ -265,37 +267,37 @@ impl<'d, T: Instance> Lcd<'d, T> { impl<'d, T: Instance> Drop for Lcd<'d, T> { fn drop(&mut self) { + // Disable the lcd + T::regs().cr().modify(|w| w.set_lcden(false)); rcc::disable::(); } } pub struct LcdPin<'d, T: Instance> { - pin: PeripheralRef<'d, AnyPin>, + pin: Peri<'d, AnyPin>, af_num: u8, _phantom: PhantomData, } -impl<'d, T: Instance, Pin: Peripheral> + 'd> From for LcdPin<'d, T> { - fn from(value: Pin) -> Self { +impl<'d, T: Instance, Pin: SegComPin> From> for LcdPin<'d, T> { + fn from(value: Peri<'d, Pin>) -> Self { Self::new(value) } } impl<'d, T: Instance> LcdPin<'d, T> { - pub fn new(pin: impl Peripheral

> + 'd) -> Self { - into_ref!(pin); - + pub fn new(pin: Peri<'d, impl SegComPin>) -> Self { let af = pin.af_num(); Self { - pin: pin.map_into(), + pin: pin.into(), af_num: af, _phantom: PhantomData, } } } -trait SealedInstance: crate::rcc::SealedRccPeripheral { +trait SealedInstance: crate::rcc::SealedRccPeripheral + PeripheralType { fn regs() -> crate::pac::lcd::Lcd; } diff --git a/examples/stm32u0/Cargo.toml b/examples/stm32u0/Cargo.toml index 9f5227e3f..42d349cda 100644 --- a/examples/stm32u0/Cargo.toml +++ b/examples/stm32u0/Cargo.toml @@ -7,7 +7,7 @@ publish = false [dependencies] # Change stm32u083rc to your chip name, if necessary. -embassy-stm32 = { version = "0.4.0", path = "../../embassy-stm32", features = [ "defmt", "time-driver-any", "stm32u083rc", "memory-x", "unstable-pac", "exti", "chrono"] } +embassy-stm32 = { version = "0.4.0", path = "../../embassy-stm32", features = [ "defmt", "time-driver-any", "stm32u083mc", "memory-x", "unstable-pac", "exti", "chrono"] } 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-time = { version = "0.5.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } diff --git a/examples/stm32u0/src/bin/lcd.rs b/examples/stm32u0/src/bin/lcd.rs index 5551dd819..f27c4458b 100644 --- a/examples/stm32u0/src/bin/lcd.rs +++ b/examples/stm32u0/src/bin/lcd.rs @@ -3,7 +3,10 @@ use defmt::*; use embassy_executor::Spawner; -use embassy_stm32::{lcd::{Bias, Config, Duty, Lcd, VoltageSource}, time::Hertz}; +use embassy_stm32::{ + lcd::{Bias, Config, Duty, Lcd, LcdPin}, + time::Hertz, +}; use {defmt_rtt as _, panic_probe as _}; #[embassy_executor::main] @@ -31,41 +34,41 @@ async fn main(_spawner: Spawner) { let mut config = Config::default(); config.bias = Bias::Third; config.duty = Duty::Quarter; - config.target_fps = Hertz(60); + config.target_fps = Hertz(100); let mut lcd = Lcd::new( p.LCD, config, p.PC3, [ - p.PA8.into(), - p.PA9.into(), - p.PA10.into(), - p.PB1.into(), - p.PB9.into(), - p.PB11.into(), - p.PB14.into(), - p.PB15.into(), - p.PC4.into(), - p.PC5.into(), - p.PC6.into(), - p.PC8.into(), - p.PC9.into(), - p.PC10.into(), - p.PC11.into(), - p.PD8.into(), - p.PD9.into(), - p.PD12.into(), - p.PD13.into(), - p.PD0.into(), - p.PD1.into(), - p.PD3.into(), - p.PD4.into(), - p.PD5.into(), - p.PD6.into(), - p.PE7.into(), - p.PE8.into(), - p.PE9.into(), + LcdPin::from(p.PA8), + LcdPin::from(p.PA9), + LcdPin::from(p.PA10), + LcdPin::from(p.PB1), + LcdPin::from(p.PB9), + LcdPin::from(p.PB11), + LcdPin::from(p.PB14), + LcdPin::from(p.PB15), + LcdPin::from(p.PC4), + LcdPin::from(p.PC5), + LcdPin::from(p.PC6), + LcdPin::from(p.PC8), + LcdPin::from(p.PC9), + LcdPin::from(p.PC10), + LcdPin::from(p.PC11), + LcdPin::from(p.PD8), + LcdPin::from(p.PD9), + LcdPin::from(p.PD12), + LcdPin::from(p.PD13), + LcdPin::from(p.PD0), + LcdPin::from(p.PD1), + LcdPin::from(p.PD3), + LcdPin::from(p.PD4), + LcdPin::from(p.PD5), + LcdPin::from(p.PD6), + LcdPin::from(p.PE7), + LcdPin::from(p.PE8), + LcdPin::from(p.PE9), ], ); -- cgit From 331901135c9537e726e08390eacdcd6c965d2406 Mon Sep 17 00:00:00 2001 From: xoviat Date: Thu, 20 Nov 2025 07:49:04 -0600 Subject: low power: don't enter stop without rcc config --- embassy-stm32/src/low_power.rs | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/embassy-stm32/src/low_power.rs b/embassy-stm32/src/low_power.rs index f35eb71e4..9de49c867 100644 --- a/embassy-stm32/src/low_power.rs +++ b/embassy-stm32/src/low_power.rs @@ -50,7 +50,7 @@ use critical_section::CriticalSection; use embassy_executor::*; use crate::interrupt; -use crate::rcc::{REFCOUNT_STOP1, REFCOUNT_STOP2}; +use crate::rcc::{RCC_CONFIG, REFCOUNT_STOP1, REFCOUNT_STOP2}; use crate::time_driver::get_driver; const THREAD_PENDER: usize = usize::MAX; @@ -201,7 +201,7 @@ impl Executor { { use crate::pac::rcc::vals::Sw; use crate::pac::{PWR, RCC}; - use crate::rcc::{RCC_CONFIG, init as init_rcc}; + use crate::rcc::init as init_rcc; let extscr = PWR.extscr().read(); if extscr.c1stop2f() || extscr.c1stopf() { @@ -245,7 +245,7 @@ impl Executor { } #[cfg(all(stm32wb, feature = "low-power"))] - fn configure_stop_stm32wb(&self) -> Result<(), ()> { + fn configure_stop_stm32wb(&self, _cs: CriticalSection) -> Result<(), ()> { use core::task::Poll; use embassy_futures::poll_once; @@ -303,9 +303,9 @@ impl Executor { } #[allow(unused_variables)] - fn configure_stop(&self, stop_mode: StopMode) -> Result<(), ()> { + fn configure_stop(&self, _cs: CriticalSection, stop_mode: StopMode) -> Result<(), ()> { #[cfg(all(stm32wb, feature = "low-power"))] - self.configure_stop_stm32wb()?; + self.configure_stop_stm32wb(_cs)?; #[cfg(any(stm32l4, stm32l5, stm32u5, stm32u0, stm32wb, stm32wba, stm32wlex))] crate::pac::PWR.cr1().modify(|m| m.set_lpms(stop_mode.into())); @@ -330,9 +330,10 @@ impl Executor { compiler_fence(Ordering::SeqCst); critical_section::with(|cs| { + let _ = unsafe { RCC_CONFIG }?; let stop_mode = Self::stop_mode(cs)?; - let _ = get_driver().pause_time(cs).ok()?; - self.configure_stop(stop_mode).ok()?; + get_driver().pause_time(cs).ok()?; + self.configure_stop(cs, stop_mode).ok()?; Some(()) }) -- cgit From 8d1b4fde897af2c943b5b1abe1503a49a5b8560a Mon Sep 17 00:00:00 2001 From: xoviat Date: Thu, 20 Nov 2025 07:49:14 -0600 Subject: adc: fix ringbuf stop --- embassy-stm32/src/adc/ringbuffered.rs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/embassy-stm32/src/adc/ringbuffered.rs b/embassy-stm32/src/adc/ringbuffered.rs index a56f8ca0b..5437866d3 100644 --- a/embassy-stm32/src/adc/ringbuffered.rs +++ b/embassy-stm32/src/adc/ringbuffered.rs @@ -49,8 +49,6 @@ impl<'d, T: Instance + AnyInstance> RingBufferedAdc<'d, T> { } pub fn stop(&mut self) { - T::stop(); - self.ring_buf.request_pause(); compiler_fence(Ordering::SeqCst); @@ -161,7 +159,7 @@ impl<'d, T: Instance + AnyInstance> RingBufferedAdc<'d, T> { return Ok(len); } Err(_) => { - self.stop(); + self.ring_buf.request_pause(); return Err(OverrunError); } -- cgit From dd2a4d6126988d13e6ff21b26dc15deac1977531 Mon Sep 17 00:00:00 2001 From: Dion Dokter Date: Thu, 20 Nov 2025 17:48:04 +0100 Subject: Create demo --- embassy-stm32/src/lcd.rs | 77 ++++++++-- examples/stm32u0/src/bin/lcd.rs | 331 +++++++++++++++++++++++++++++++++++++++- 2 files changed, 388 insertions(+), 20 deletions(-) diff --git a/embassy-stm32/src/lcd.rs b/embassy-stm32/src/lcd.rs index 7c1877a38..d4e65a925 100644 --- a/embassy-stm32/src/lcd.rs +++ b/embassy-stm32/src/lcd.rs @@ -8,6 +8,11 @@ use crate::peripherals; use crate::rcc::{self, RccPeripheral}; use crate::time::Hertz; +#[cfg(stm32u0)] +const NUM_SEGMENTS: u8 = 52; +#[cfg(stm32wb)] +const NUM_SEGMENTS: u8 = 44; + #[non_exhaustive] #[derive(Debug, Clone, Copy)] pub struct Config { @@ -70,6 +75,18 @@ pub enum Duty { Eigth = 0b100, } +impl Duty { + fn num_com_pins(&self) -> u8 { + match self { + Duty::Static => 1, + Duty::Half => 2, + Duty::Third => 3, + Duty::Quarter => 4, + Duty::Eigth => 8, + } + } +} + /// Whether to use the internal or external voltage source to drive the LCD #[repr(u8)] #[derive(Debug, Default, Clone, Copy)] @@ -118,6 +135,7 @@ pub enum Drive { /// LCD driver. pub struct Lcd<'d, T: Instance> { _peri: PhantomData<&'d mut T>, + duty: Duty, } impl<'d, T: Instance> Lcd<'d, T> { @@ -224,45 +242,76 @@ impl<'d, T: Instance> Lcd<'d, T> { // Wait for the stepup converter to be ready while !T::regs().sr().read().rdy() {} - Self { _peri: PhantomData } + Self { + _peri: PhantomData, + duty: config.duty, + } } /// Change the contrast by changing the voltage being used. /// - /// This from low at 0 to high at 7. + /// This is from low at 0 to high at 7. pub fn set_contrast_control(&mut self, value: u8) { + assert!((0..=7).contains(&value)); T::regs().fcr().modify(|w| w.set_cc(value)); } /// Change the contrast by introducing a deadtime to the signals /// where the voltages are held at 0V. /// - /// This from no dead time at 0 to high dead time at 7. + /// This is from no dead time at 0 to high dead time at 7. pub fn set_dead_time(&mut self, value: u8) { + assert!((0..=7).contains(&value)); T::regs() .fcr() .modify(|w: &mut stm32_metapac::lcd::regs::Fcr| w.set_dead(value)); } - /// Write frame data to the peripheral. - /// - /// What each bit means depends on the exact microcontroller you use, - /// which pins are connected to your LCD and also the LCD layout itself. - /// - /// This function blocks until the last update display request has been processed. - pub fn write_frame(&mut self, data: &[u32; 16]) { + pub fn write_com_segments(&mut self, com_index: u8, segments: u64) { while T::regs().sr().read().udr() {} - for i in 0..8 { - T::regs().ram_com(i).low().write_value(data[i * 2]); - T::regs().ram_com(i).low().write_value(data[i * 2 + 1]); - } + assert!( + com_index < self.duty.num_com_pins(), + "Com index cannot be higher than number of configured com pins (through the Duty setting in the config)" + ); + + assert!( + segments.leading_zeros() >= 64 - self.num_segments() as u32, + "Invalid segment pixel set", + ); + + T::regs() + .ram_com(com_index as usize) + .low() + .write_value((segments & 0xFFFF_FFFF) as u32); + T::regs() + .ram_com(com_index as usize) + .high() + .write_value(((segments >> 32) & 0xFFFF_FFFF) as u32); + } + pub fn submit_frame(&mut self) { + while T::regs().sr().read().udr() {} // Clear the update done flag T::regs().sr().write(|w| w.set_udd(true)); // Set the update request flag T::regs().sr().write(|w| w.set_udr(true)); } + + pub fn num_segments(&self) -> u8 { + match self.duty { + Duty::Eigth => NUM_SEGMENTS - 4, // With 8 coms, 4 of the segment pins turn into com pins + _ => NUM_SEGMENTS, + } + } + + pub fn segment_pixel_mask(&self) -> u64 { + (1 << self.num_segments()) - 1 + } + + pub fn num_com_pins(&self) -> u8 { + self.duty.num_com_pins() + } } impl<'d, T: Instance> Drop for Lcd<'d, T> { diff --git a/examples/stm32u0/src/bin/lcd.rs b/examples/stm32u0/src/bin/lcd.rs index f27c4458b..f401b1dcd 100644 --- a/examples/stm32u0/src/bin/lcd.rs +++ b/examples/stm32u0/src/bin/lcd.rs @@ -5,8 +5,10 @@ use defmt::*; use embassy_executor::Spawner; use embassy_stm32::{ lcd::{Bias, Config, Duty, Lcd, LcdPin}, + peripherals::LCD, time::Hertz, }; +use embassy_time::Duration; use {defmt_rtt as _, panic_probe as _}; #[embassy_executor::main] @@ -72,15 +74,332 @@ async fn main(_spawner: Spawner) { ], ); + { + let mut buffer = DisplayBuffer::new(); + for i in 0..4 { + buffer.write_colon(i); + buffer.write(&mut lcd); + embassy_time::Timer::after_millis(200).await; + buffer.write_dot(i); + buffer.write(&mut lcd); + embassy_time::Timer::after_millis(200).await; + } + for i in 0..4 { + buffer.write_bar(i); + buffer.write(&mut lcd); + embassy_time::Timer::after_millis(200).await; + } + } + + embassy_time::Timer::after_millis(1000).await; + + const MESSAGE: &str = "Hello embassy people. Hope you like this LCD demo :} "; loop { - defmt::info!("Writing frame"); - lcd.write_frame(&[0xAAAAAAAA; 16]); + print_message(MESSAGE, &mut lcd, Duration::from_millis(250)).await; + print_message(characters::ALL_CHARS, &mut lcd, Duration::from_millis(500)).await; + } +} + +async fn print_message(message: &str, lcd: &mut Lcd<'_, LCD>, delay: Duration) { + let mut display_buffer = DisplayBuffer::new(); + + let mut char_buffer = [' '; 6]; + for char in message.chars() { + char_buffer.copy_within(1.., 0); + char_buffer[5] = char; + + display_buffer.clear(); + for (i, char) in char_buffer.iter().enumerate() { + display_buffer.write_char(i, *char); + } + display_buffer.write(lcd); + + embassy_time::Timer::after(delay).await; + } +} + +/// Display layout for the U0-DK +mod display_layout { + // Character layout. There are 6 characters, left-to-right + // T + // ───────── + // │ N │ + // │ │ │ │ │ + // TL │ └┐ │ ┌┘ │ TR + // │NW│ │ │NE│ + // │ │ │ + // W─── ───E + // │ │ │ + // │SW│ │ │SE│ + // BL │ ┌┘ │ └┐ │ BR + // │ │ │ │ │ + // │ S │ + // ───────── + // B + + pub const CHAR_N_COM: u8 = 3; + pub const CHAR_N_SEG: [u8; 6] = [39, 37, 35, 48, 26, 33]; + pub const CHAR_NW_COM: u8 = 3; + pub const CHAR_NW_SEG: [u8; 6] = [49, 38, 36, 34, 27, 24]; + pub const CHAR_W_COM: u8 = 0; + pub const CHAR_W_SEG: [u8; 6] = CHAR_NW_SEG; + pub const CHAR_SW_COM: u8 = 2; + pub const CHAR_SW_SEG: [u8; 6] = CHAR_NW_SEG; + pub const CHAR_S_COM: u8 = 2; + pub const CHAR_S_SEG: [u8; 6] = [22, 6, 46, 11, 15, 29]; + pub const CHAR_SE_COM: u8 = 3; + pub const CHAR_SE_SEG: [u8; 6] = CHAR_S_SEG; + pub const CHAR_E_COM: u8 = 0; + pub const CHAR_E_SEG: [u8; 6] = [23, 45, 47, 14, 28, 32]; + pub const CHAR_NE_COM: u8 = 2; + pub const CHAR_NE_SEG: [u8; 6] = CHAR_N_SEG; + pub const CHAR_T_COM: u8 = 1; + pub const CHAR_T_SEG: [u8; 6] = CHAR_N_SEG; + pub const CHAR_TL_COM: u8 = 1; + pub const CHAR_TL_SEG: [u8; 6] = CHAR_NW_SEG; + pub const CHAR_BL_COM: u8 = 0; + pub const CHAR_BL_SEG: [u8; 6] = CHAR_S_SEG; + pub const CHAR_B_COM: u8 = 1; + pub const CHAR_B_SEG: [u8; 6] = CHAR_S_SEG; + pub const CHAR_BR_COM: u8 = 1; + pub const CHAR_BR_SEG: [u8; 6] = CHAR_E_SEG; + pub const CHAR_TR_COM: u8 = 0; + pub const CHAR_TR_SEG: [u8; 6] = CHAR_N_SEG; + + pub const COLON_COM: u8 = 2; + pub const COLON_SEG: [u8; 4] = [23, 45, 47, 14]; + pub const DOT_COM: u8 = 3; + pub const DOT_SEG: [u8; 4] = COLON_SEG; + /// COM + SEG, bar from top to bottom + pub const BAR: [(u8, u8); 4] = [(2, 28), (3, 28), (2, 32), (3, 32)]; +} + +mod characters { + use super::CharSegment::{self, *}; + + pub const CHAR_0: &[CharSegment] = &[T, TL, BL, B, BR, TR, NW, SE]; + pub const CHAR_1: &[CharSegment] = &[NE, TR, BR]; + pub const CHAR_2: &[CharSegment] = &[T, BL, B, TR, E, W]; + pub const CHAR_3: &[CharSegment] = &[T, B, BR, TR, E]; + pub const CHAR_4: &[CharSegment] = &[TL, BR, TR, E, W]; + pub const CHAR_5: &[CharSegment] = &[T, TL, B, BR, E, W]; + pub const CHAR_6: &[CharSegment] = &[T, TL, BL, B, BR, E, W]; + pub const CHAR_7: &[CharSegment] = &[T, NE, S]; + pub const CHAR_8: &[CharSegment] = &[T, TL, BL, B, BR, TR, E, W]; + pub const CHAR_9: &[CharSegment] = &[T, TL, BR, TR, E, W]; + + pub const CHAR_COLON: &[CharSegment] = &[N, S]; + pub const CHAR_SEMICOLON: &[CharSegment] = &[N, SW]; + pub const CHAR_EQUALS: &[CharSegment] = &[E, W, B]; + pub const CHAR_SLASH: &[CharSegment] = &[SW, NE]; + pub const CHAR_BACKSLASH: &[CharSegment] = &[SE, NW]; + pub const CHAR_PLUS: &[CharSegment] = &[N, E, S, W]; + pub const CHAR_STAR: &[CharSegment] = &[NE, N, NW, SE, S, SW]; + pub const CHAR_QUOTE: &[CharSegment] = &[N]; + pub const CHAR_BACKTICK: &[CharSegment] = &[NW]; + pub const CHAR_DASH: &[CharSegment] = &[W, E]; + pub const CHAR_COMMA: &[CharSegment] = &[SW]; + pub const CHAR_DOT: &[CharSegment] = &[S]; + pub const CHAR_CURLYOPEN: &[CharSegment] = &[T, NW, W, SW, B]; + pub const CHAR_CURLYCLOSE: &[CharSegment] = &[T, NE, E, SE, B]; + pub const CHAR_AMPERSAND: &[CharSegment] = &[T, NE, NW, W, BL, B, SE]; + + pub const CHAR_A: &[CharSegment] = &[T, TL, TR, E, W, BL, BR]; + pub const CHAR_B: &[CharSegment] = &[T, TR, BR, B, N, S, E]; + pub const CHAR_C: &[CharSegment] = &[T, TL, BL, B]; + pub const CHAR_D: &[CharSegment] = &[T, TR, BR, B, N, S]; + pub const CHAR_E: &[CharSegment] = &[T, TL, BL, B, W]; + pub const CHAR_F: &[CharSegment] = &[T, TL, BL, W]; + pub const CHAR_G: &[CharSegment] = &[T, TL, BL, B, BR, E]; + pub const CHAR_H: &[CharSegment] = &[TL, BL, E, W, TR, BR]; + pub const CHAR_I: &[CharSegment] = &[T, N, S, B]; + pub const CHAR_J: &[CharSegment] = &[TR, BR, B, BL]; + pub const CHAR_K: &[CharSegment] = &[TL, BL, W, NE, SE]; + pub const CHAR_L: &[CharSegment] = &[TL, BL, B]; + pub const CHAR_M: &[CharSegment] = &[BL, TL, NW, NE, TR, BR]; + pub const CHAR_N: &[CharSegment] = &[BL, TL, NW, SE, BR, TR]; + pub const CHAR_O: &[CharSegment] = &[T, TL, BL, B, BR, TR]; + pub const CHAR_P: &[CharSegment] = &[BL, TL, T, TR, E, W]; + pub const CHAR_Q: &[CharSegment] = &[T, TL, BL, B, BR, TR, SE]; + pub const CHAR_R: &[CharSegment] = &[BL, TL, T, TR, E, W, SE]; + pub const CHAR_S: &[CharSegment] = &[T, NW, E, BR, B]; + pub const CHAR_T: &[CharSegment] = &[T, N, S]; + pub const CHAR_U: &[CharSegment] = &[TL, BL, B, BR, TR]; + pub const CHAR_V: &[CharSegment] = &[TL, BL, SW, NE]; + pub const CHAR_W: &[CharSegment] = &[TL, BL, SW, SE, BR, TR]; + pub const CHAR_X: &[CharSegment] = &[NE, NW, SE, SW]; + pub const CHAR_Y: &[CharSegment] = &[NE, NW, S]; + pub const CHAR_Z: &[CharSegment] = &[T, NE, SW, B]; + + pub const CHAR_UNKNOWN: &[CharSegment] = &[N, NW, W, SW, S, SE, E, NE, T, TL, BL, B, BR, TR]; + + pub const ALL_CHARS: &str = + "0 1 2 3 4 5 6 7 8 9 : ; = / \\ + * ' ` - , . { } & A B C D E F G H I J K L M N O P Q R S T U V W X Y Z � "; + + pub fn get_char_segments(val: char) -> &'static [CharSegment] { + match val { + val if val.is_whitespace() => &[], + + '0' => CHAR_0, + '1' => CHAR_1, + '2' => CHAR_2, + '3' => CHAR_3, + '4' => CHAR_4, + '5' => CHAR_5, + '6' => CHAR_6, + '7' => CHAR_7, + '8' => CHAR_8, + '9' => CHAR_9, + + ':' => CHAR_COLON, + ';' => CHAR_SEMICOLON, + '=' => CHAR_EQUALS, + '/' => CHAR_SLASH, + '\\' => CHAR_BACKSLASH, + '+' => CHAR_PLUS, + '*' => CHAR_STAR, + '\'' => CHAR_QUOTE, + '`' => CHAR_BACKTICK, + '-' => CHAR_DASH, + ',' => CHAR_COMMA, + '.' => CHAR_DOT, + '{' => CHAR_CURLYOPEN, + '}' => CHAR_CURLYCLOSE, + '&' => CHAR_AMPERSAND, - embassy_time::Timer::after_secs(1).await; + 'A' | 'a' => CHAR_A, + 'B' | 'b' => CHAR_B, + 'C' | 'c' => CHAR_C, + 'D' | 'd' => CHAR_D, + 'E' | 'e' => CHAR_E, + 'F' | 'f' => CHAR_F, + 'G' | 'g' => CHAR_G, + 'H' | 'h' => CHAR_H, + 'I' | 'i' => CHAR_I, + 'J' | 'j' => CHAR_J, + 'K' | 'k' => CHAR_K, + 'L' | 'l' => CHAR_L, + 'M' | 'm' => CHAR_M, + 'N' | 'n' => CHAR_N, + 'O' | 'o' => CHAR_O, + 'P' | 'p' => CHAR_P, + 'Q' | 'q' => CHAR_Q, + 'R' | 'r' => CHAR_R, + 'S' | 's' => CHAR_S, + 'T' | 't' => CHAR_T, + 'U' | 'u' => CHAR_U, + 'V' | 'v' => CHAR_V, + 'W' | 'w' => CHAR_W, + 'X' | 'x' => CHAR_X, + 'Y' | 'y' => CHAR_Y, + 'Z' | 'z' => CHAR_Z, - defmt::info!("Writing frame"); - lcd.write_frame(&[!0xAAAAAAAA; 16]); + _ => CHAR_UNKNOWN, + } + } +} + +pub struct DisplayBuffer { + pixels: [u64; 4], +} + +impl DisplayBuffer { + pub const fn new() -> Self { + Self { pixels: [0; 4] } + } + + pub fn clear(&mut self) { + *self = Self::new(); + } + + fn write_char_segment(&mut self, index: usize, value: CharSegment) { + defmt::assert!(index < 6); + let (com, segments) = value.get_com_seg(); + self.pixels[com as usize] |= 1 << segments[index]; + } + + pub fn write_char(&mut self, index: usize, val: char) { + let segments = characters::get_char_segments(val); + + for segment in segments { + self.write_char_segment(index, *segment); + } + } + + pub fn write(&self, lcd: &mut Lcd<'_, LCD>) { + lcd.write_com_segments(0, self.pixels[0]); + lcd.write_com_segments(1, self.pixels[1]); + lcd.write_com_segments(2, self.pixels[2]); + lcd.write_com_segments(3, self.pixels[3]); + lcd.submit_frame(); + } + + pub fn write_colon(&mut self, index: usize) { + defmt::assert!(index < 4); + self.pixels[display_layout::COLON_COM as usize] |= 1 << display_layout::COLON_SEG[index]; + } + + pub fn write_dot(&mut self, index: usize) { + defmt::assert!(index < 4); + self.pixels[display_layout::DOT_COM as usize] |= 1 << display_layout::DOT_SEG[index]; + } + + pub fn write_bar(&mut self, index: usize) { + defmt::assert!(index < 4); + let (bar_com, bar_seg) = display_layout::BAR[index]; + self.pixels[bar_com as usize] |= 1 << bar_seg; + } +} + +#[derive(Debug, Clone, Copy)] +enum CharSegment { + /// North + N, + /// North west + NW, + /// West + W, + /// South west + SW, + /// South + S, + /// South East + SE, + /// East + E, + /// North East + NE, + /// Top + T, + /// Top left + TL, + /// Bottom left + BL, + /// Bottom + B, + /// Bottom right + BR, + /// Top right + TR, +} - embassy_time::Timer::after_secs(1).await; +impl CharSegment { + fn get_com_seg(&self) -> (u8, [u8; 6]) { + match self { + CharSegment::N => (display_layout::CHAR_N_COM, display_layout::CHAR_N_SEG), + CharSegment::NW => (display_layout::CHAR_NW_COM, display_layout::CHAR_NW_SEG), + CharSegment::W => (display_layout::CHAR_W_COM, display_layout::CHAR_W_SEG), + CharSegment::SW => (display_layout::CHAR_SW_COM, display_layout::CHAR_SW_SEG), + CharSegment::S => (display_layout::CHAR_S_COM, display_layout::CHAR_S_SEG), + CharSegment::SE => (display_layout::CHAR_SE_COM, display_layout::CHAR_SE_SEG), + CharSegment::E => (display_layout::CHAR_E_COM, display_layout::CHAR_E_SEG), + CharSegment::NE => (display_layout::CHAR_NE_COM, display_layout::CHAR_NE_SEG), + CharSegment::T => (display_layout::CHAR_T_COM, display_layout::CHAR_T_SEG), + CharSegment::TL => (display_layout::CHAR_TL_COM, display_layout::CHAR_TL_SEG), + CharSegment::BL => (display_layout::CHAR_BL_COM, display_layout::CHAR_BL_SEG), + CharSegment::B => (display_layout::CHAR_B_COM, display_layout::CHAR_B_SEG), + CharSegment::BR => (display_layout::CHAR_BR_COM, display_layout::CHAR_BR_SEG), + CharSegment::TR => (display_layout::CHAR_TR_COM, display_layout::CHAR_TR_SEG), + } } } -- cgit From 219de4be85f6e63e73693c934be54687c9ad860c Mon Sep 17 00:00:00 2001 From: Gerzain Mata Date: Wed, 19 Nov 2025 20:45:36 -0700 Subject: stm32: Fixed ADC4 enable() for WBA --- embassy-stm32/CHANGELOG.md | 1 + embassy-stm32/src/adc/adc4.rs | 18 +++++++++++++++++- examples/stm32wba6/src/bin/adc.rs | 28 +++++++++++++++++++++++++++- 3 files changed, 45 insertions(+), 2 deletions(-) diff --git a/embassy-stm32/CHANGELOG.md b/embassy-stm32/CHANGELOG.md index d2f675dbc..8dc34ff72 100644 --- a/embassy-stm32/CHANGELOG.md +++ b/embassy-stm32/CHANGELOG.md @@ -7,6 +7,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## Unreleased - ReleaseDate +- fix: Fixed ADC4 enable() for WBA - feat: add poll_for methods to exti - feat: implement stop for stm32wb. - change: rework hsem and add HIL test for some chips. diff --git a/embassy-stm32/src/adc/adc4.rs b/embassy-stm32/src/adc/adc4.rs index 499fc2093..472eb46fd 100644 --- a/embassy-stm32/src/adc/adc4.rs +++ b/embassy-stm32/src/adc/adc4.rs @@ -113,10 +113,26 @@ foreach_adc!( } fn enable() { + let cr_initial = ADC4::regs().cr().read(); + let isr_initial = ADC4::regs().isr().read(); + + if cr_initial.aden() && isr_initial.adrdy() { + return; + } + + if cr_initial.aden() || cr_initial.adstart() { + if cr_initial.adstart() { + ADC4::regs().cr().modify(|w| w.set_adstp(true)); + while ADC4::regs().cr().read().adstart() {} + } + + ADC4::regs().cr().modify(|w| w.set_addis(true)); + while ADC4::regs().cr().read().aden() {} + } + ADC4::regs().isr().write(|w| w.set_adrdy(true)); ADC4::regs().cr().modify(|w| w.set_aden(true)); while !ADC4::regs().isr().read().adrdy() {} - ADC4::regs().isr().write(|w| w.set_adrdy(true)); } fn start() { diff --git a/examples/stm32wba6/src/bin/adc.rs b/examples/stm32wba6/src/bin/adc.rs index 9d1f39419..14f4a0636 100644 --- a/examples/stm32wba6/src/bin/adc.rs +++ b/examples/stm32wba6/src/bin/adc.rs @@ -3,11 +3,37 @@ use defmt::*; use embassy_stm32::adc::{Adc, AdcChannel, SampleTime, adc4}; +use embassy_stm32::Config; +use embassy_stm32::rcc::{ + AHB5Prescaler, AHBPrescaler, APBPrescaler, PllDiv, PllMul, PllPreDiv, PllSource, Sysclk, VoltageScale, +}; use {defmt_rtt as _, panic_probe as _}; #[embassy_executor::main] async fn main(_spawner: embassy_executor::Spawner) { - let config = embassy_stm32::Config::default(); + let mut config = Config::default(); + // Fine-tune PLL1 dividers/multipliers + config.rcc.pll1 = Some(embassy_stm32::rcc::Pll { + source: PllSource::HSI, + prediv: PllPreDiv::DIV1, // PLLM = 1 → HSI / 1 = 16 MHz + mul: PllMul::MUL30, // PLLN = 30 → 16 MHz * 30 = 480 MHz VCO + divr: Some(PllDiv::DIV5), // PLLR = 5 → 96 MHz (Sysclk) + // divq: Some(PllDiv::DIV10), // PLLQ = 10 → 48 MHz (NOT USED) + divq: None, + divp: Some(PllDiv::DIV30), // PLLP = 30 → 16 MHz (USBOTG) + frac: Some(0), // Fractional part (enabled) + }); + + config.rcc.ahb_pre = AHBPrescaler::DIV1; + config.rcc.apb1_pre = APBPrescaler::DIV1; + config.rcc.apb2_pre = APBPrescaler::DIV1; + config.rcc.apb7_pre = APBPrescaler::DIV1; + config.rcc.ahb5_pre = AHB5Prescaler::DIV4; + + // voltage scale for max performance + config.rcc.voltage_scale = VoltageScale::RANGE1; + // route PLL1_P into the USB‐OTG‐HS block + config.rcc.sys = Sysclk::PLL1_R; let mut p = embassy_stm32::init(config); -- cgit From 3abc2e592f66c16ada6c475e48cde282b79d3c1f Mon Sep 17 00:00:00 2001 From: xoviat Date: Thu, 20 Nov 2025 14:27:31 -0600 Subject: adc: allow usage of anyadcchannel for adc4 --- embassy-stm32/CHANGELOG.md | 1 + embassy-stm32/src/adc/mod.rs | 6 +++--- examples/stm32u5/src/bin/adc.rs | 2 +- 3 files changed, 5 insertions(+), 4 deletions(-) diff --git a/embassy-stm32/CHANGELOG.md b/embassy-stm32/CHANGELOG.md index 597cbb192..c547fa443 100644 --- a/embassy-stm32/CHANGELOG.md +++ b/embassy-stm32/CHANGELOG.md @@ -7,6 +7,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## Unreleased - ReleaseDate +- feat: allow use of anyadcchannel for adc4 - change: rework hsem and add HIL test for some chips. - change: stm32/eth: ethernet no longer has a hard dependency on station management, and station management can be used independently ([#4871](https://github.com/embassy-rs/embassy/pull/4871)) - feat: allow embassy_executor::main for low power diff --git a/embassy-stm32/src/adc/mod.rs b/embassy-stm32/src/adc/mod.rs index 74648cc21..755cb78c2 100644 --- a/embassy-stm32/src/adc/mod.rs +++ b/embassy-stm32/src/adc/mod.rs @@ -436,9 +436,9 @@ pub struct AnyAdcChannel { is_differential: bool, _phantom: PhantomData, } -impl_peripheral!(AnyAdcChannel); -impl AdcChannel for AnyAdcChannel {} -impl SealedAdcChannel for AnyAdcChannel { +impl_peripheral!(AnyAdcChannel); +impl AdcChannel for AnyAdcChannel {} +impl SealedAdcChannel for AnyAdcChannel { fn channel(&self) -> u8 { self.channel } diff --git a/examples/stm32u5/src/bin/adc.rs b/examples/stm32u5/src/bin/adc.rs index ad59c0bea..4d2d93aa2 100644 --- a/examples/stm32u5/src/bin/adc.rs +++ b/examples/stm32u5/src/bin/adc.rs @@ -31,7 +31,7 @@ async fn main(_spawner: embassy_executor::Spawner) { // **** ADC4 init **** let mut adc4 = Adc::new_adc4(p.ADC4); - let mut adc4_pin1 = p.PC1; // A4 + let mut adc4_pin1 = p.PC1.degrade_adc(); // A4 let mut adc4_pin2 = p.PC0; // A5 adc4.set_resolution_adc4(adc4::Resolution::BITS12); adc4.set_averaging_adc4(adc4::Averaging::Samples256); -- cgit From c646d589afc905020700376144da1dd1398287fc Mon Sep 17 00:00:00 2001 From: Siarhei B Date: Fri, 21 Nov 2025 00:10:56 +0100 Subject: mspm0-mathacl: add non-generic phantomdata --- embassy-mspm0/src/mathacl.rs | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/embassy-mspm0/src/mathacl.rs b/embassy-mspm0/src/mathacl.rs index 78646c90f..e29f4a59e 100644 --- a/embassy-mspm0/src/mathacl.rs +++ b/embassy-mspm0/src/mathacl.rs @@ -28,15 +28,14 @@ pub enum Error { NBitsTooBig, } -pub struct Mathacl<'d, T: Instance> { - _peri: Peri<'d, T>, +pub struct Mathacl<'d> { regs: &'static Regs, - _phantom: PhantomData, + _phantom: PhantomData<&'d mut ()>, } -impl<'d, T: Instance> Mathacl<'d, T> { +impl<'d> Mathacl<'d> { /// Mathacl initialization. - pub fn new(instance: Peri<'d, T>) -> Self { + pub fn new(_instance: Peri<'d, T>) -> Self { // Init power T::regs().gprcm(0).rstctl().write(|w| { w.set_resetstkyclr(vals::Resetstkyclr::CLR); @@ -54,7 +53,6 @@ impl<'d, T: Instance> Mathacl<'d, T> { cortex_m::asm::delay(16); Self { - _peri: instance, regs: T::regs(), _phantom: PhantomData, } -- cgit From 1cc2643ae60d429d0389213f5c1f6bbc007c6a2b Mon Sep 17 00:00:00 2001 From: xoviat Date: Fri, 21 Nov 2025 07:43:37 -0600 Subject: adc: fix start sequence for blocking_read --- embassy-stm32/src/adc/adc4.rs | 35 ++++++++++++----------------------- embassy-stm32/src/adc/mod.rs | 2 ++ embassy-stm32/src/adc/v2.rs | 17 +++++++++-------- 3 files changed, 23 insertions(+), 31 deletions(-) diff --git a/embassy-stm32/src/adc/adc4.rs b/embassy-stm32/src/adc/adc4.rs index 472eb46fd..453513309 100644 --- a/embassy-stm32/src/adc/adc4.rs +++ b/embassy-stm32/src/adc/adc4.rs @@ -113,26 +113,11 @@ foreach_adc!( } fn enable() { - let cr_initial = ADC4::regs().cr().read(); - let isr_initial = ADC4::regs().isr().read(); - - if cr_initial.aden() && isr_initial.adrdy() { - return; - } - - if cr_initial.aden() || cr_initial.adstart() { - if cr_initial.adstart() { - ADC4::regs().cr().modify(|w| w.set_adstp(true)); - while ADC4::regs().cr().read().adstart() {} - } - - ADC4::regs().cr().modify(|w| w.set_addis(true)); - while ADC4::regs().cr().read().aden() {} + if !ADC4::regs().cr().read().aden() || !ADC4::regs().isr().read().adrdy() { + ADC4::regs().isr().write(|w| w.set_adrdy(true)); + ADC4::regs().cr().modify(|w| w.set_aden(true)); + while !ADC4::regs().isr().read().adrdy() {} } - - ADC4::regs().isr().write(|w| w.set_adrdy(true)); - ADC4::regs().cr().modify(|w| w.set_aden(true)); - while !ADC4::regs().isr().read().adrdy() {} } fn start() { @@ -143,13 +128,17 @@ foreach_adc!( } fn stop() { - if ADC4::regs().cr().read().adstart() && !ADC4::regs().cr().read().addis() { - ADC4::regs().cr().modify(|reg| { - reg.set_adstp(true); - }); + let cr = ADC4::regs().cr().read(); + if cr.adstart() { + ADC4::regs().cr().modify(|w| w.set_adstp(true)); while ADC4::regs().cr().read().adstart() {} } + if cr.aden() || cr.adstart() { + ADC4::regs().cr().modify(|w| w.set_addis(true)); + while ADC4::regs().cr().read().aden() {} + } + // Reset configuration. ADC4::regs().cfgr1().modify(|reg| { reg.set_dmaen(false); diff --git a/embassy-stm32/src/adc/mod.rs b/embassy-stm32/src/adc/mod.rs index 755cb78c2..6d53d9b91 100644 --- a/embassy-stm32/src/adc/mod.rs +++ b/embassy-stm32/src/adc/mod.rs @@ -192,6 +192,8 @@ impl<'d, T: AnyInstance> Adc<'d, T> { #[cfg(any(adc_v1, adc_c0, adc_l0, adc_v2, adc_g4, adc_v4, adc_u5, adc_wba))] channel.setup(); + // Ensure no conversions are ongoing + T::stop(); #[cfg(any(adc_v2, adc_v3, adc_g0, adc_h7rs, adc_u0, adc_u5, adc_wba, adc_c0))] T::enable(); T::configure_sequence([((channel.channel(), channel.is_differential()), sample_time)].into_iter()); diff --git a/embassy-stm32/src/adc/v2.rs b/embassy-stm32/src/adc/v2.rs index 341b15674..3c4431ae0 100644 --- a/embassy-stm32/src/adc/v2.rs +++ b/embassy-stm32/src/adc/v2.rs @@ -79,12 +79,17 @@ impl super::SealedAnyInstance for T { T::regs().dr().as_ptr() as *mut u16 } - fn enable() {} + fn enable() { + T::regs().cr2().modify(|reg| { + reg.set_adon(true); + }); + + blocking_delay_us(3); + } fn start() { // Begin ADC conversions T::regs().cr2().modify(|reg| { - reg.set_adon(true); reg.set_swstart(true); }); } @@ -198,7 +203,7 @@ impl super::SealedAnyInstance for T { impl<'d, T> Adc<'d, T> where - T: Instance, + T: Instance + super::AnyInstance, { pub fn new(adc: Peri<'d, T>) -> Self { Self::new_with_config(adc, Default::default()) @@ -209,11 +214,7 @@ where let presc = from_pclk2(T::frequency()); T::common_regs().ccr().modify(|w| w.set_adcpre(presc)); - T::regs().cr2().modify(|reg| { - reg.set_adon(true); - }); - - blocking_delay_us(3); + T::enable(); if let Some(resolution) = config.resolution { T::regs().cr1().modify(|reg| reg.set_res(resolution.into())); -- cgit From 14436ce1300a5d621b617d56ee037e09ce55ea0a Mon Sep 17 00:00:00 2001 From: xoviat Date: Fri, 21 Nov 2025 08:22:31 -0600 Subject: adc: add test for wba --- tests/stm32/Cargo.toml | 8 +++++++- tests/stm32/src/bin/adc.rs | 39 +++++++++++++++++++++++++++++++++++++++ tests/stm32/src/common.rs | 1 + 3 files changed, 47 insertions(+), 1 deletion(-) create mode 100644 tests/stm32/src/bin/adc.rs diff --git a/tests/stm32/Cargo.toml b/tests/stm32/Cargo.toml index 8fcb6b2b4..496a9de18 100644 --- a/tests/stm32/Cargo.toml +++ b/tests/stm32/Cargo.toml @@ -32,7 +32,7 @@ stm32l552ze = ["embassy-stm32/stm32l552ze", "not-gpdma", "rng", "hash", "dual-ba stm32u585ai = ["embassy-stm32/stm32u585ai", "spi-v345", "chrono", "rng", "hash", "cordic"] stm32u5a5zj = ["embassy-stm32/stm32u5a5zj", "spi-v345", "chrono", "rng", "hash"] # FIXME: cordic test cause it crash stm32wb55rg = ["embassy-stm32/stm32wb55rg", "chrono", "not-gpdma", "ble", "mac" , "rng", "hsem", "stop"] -stm32wba52cg = ["embassy-stm32/stm32wba52cg", "spi-v345", "chrono", "rng", "hash"] +stm32wba52cg = ["embassy-stm32/stm32wba52cg", "spi-v345", "chrono", "rng", "hash", "adc"] stm32wl55jc = ["embassy-stm32/stm32wl55jc-cm4", "not-gpdma", "rng", "chrono", "hsem"] stm32f091rc = ["embassy-stm32/stm32f091rc", "cm0", "not-gpdma", "chrono"] stm32h503rb = ["embassy-stm32/stm32h503rb", "spi-v345", "rng", "stop"] @@ -56,6 +56,7 @@ mac = ["dep:embassy-stm32-wpan", "embassy-stm32-wpan/mac"] embassy-stm32-wpan = [] not-gpdma = [] dac = [] +adc = [] ucpd = [] cordic = ["dep:num-traits"] hsem = [] @@ -110,6 +111,11 @@ name = "afio" path = "src/bin/afio.rs" required-features = [ "afio",] +[[bin]] +name = "adc" +path = "src/bin/adc.rs" +required-features = [ "adc",] + [[bin]] name = "can" path = "src/bin/can.rs" diff --git a/tests/stm32/src/bin/adc.rs b/tests/stm32/src/bin/adc.rs new file mode 100644 index 000000000..6cedc6498 --- /dev/null +++ b/tests/stm32/src/bin/adc.rs @@ -0,0 +1,39 @@ +#![no_std] +#![no_main] + +// required-features: dac + +#[path = "../common.rs"] +mod common; + +use common::*; +use embassy_executor::Spawner; +use embassy_stm32::adc::{Adc, SampleTime}; +use embassy_time::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 = init(); + + let adc = peri!(p, ADC); + let mut adc_pin = peri!(p, DAC_PIN); + + let mut adc = Adc::new_adc4(adc); + + // Now wait a little to obtain a stable value + Timer::after_millis(30).await; + let _ = adc.blocking_read(&mut adc_pin, SampleTime::from_bits(0)); + + for _ in 0..=255 { + // Now wait a little to obtain a stable value + Timer::after_millis(30).await; + + // Need to steal the peripherals here because PA4 is obviously in use already + let _ = adc.blocking_read(&mut adc_pin, SampleTime::from_bits(0)); + } + + info!("Test OK"); + cortex_m::asm::bkpt(); +} diff --git a/tests/stm32/src/common.rs b/tests/stm32/src/common.rs index 096cce947..9f88b182a 100644 --- a/tests/stm32/src/common.rs +++ b/tests/stm32/src/common.rs @@ -259,6 +259,7 @@ define_peris!( define_peris!( UART = LPUART1, UART_TX = PB5, UART_RX = PA10, UART_TX_DMA = GPDMA1_CH0, UART_RX_DMA = GPDMA1_CH1, SPI = SPI1, SPI_SCK = PB4, SPI_MOSI = PA15, SPI_MISO = PB3, SPI_TX_DMA = GPDMA1_CH0, SPI_RX_DMA = GPDMA1_CH1, + ADC = ADC4, DAC_PIN = PA0, @irq UART = {LPUART1 => embassy_stm32::usart::InterruptHandler;}, ); #[cfg(feature = "stm32h7s3l8")] -- cgit From 672165572e36d04a59eb672e19ebf4c1726fc8aa Mon Sep 17 00:00:00 2001 From: Siarhei B Date: Fri, 21 Nov 2025 18:22:24 +0100 Subject: mspm0-mathacl: exclude the module for non-supported chips --- embassy-mspm0/src/lib.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/embassy-mspm0/src/lib.rs b/embassy-mspm0/src/lib.rs index dda8c373c..c43c81853 100644 --- a/embassy-mspm0/src/lib.rs +++ b/embassy-mspm0/src/lib.rs @@ -19,6 +19,7 @@ pub mod dma; pub mod gpio; pub mod i2c; pub mod i2c_target; +#[cfg(any(mspm0g150x, mspm0g151x, mspm0g350x, mspm0g351x))] pub mod mathacl; pub mod timer; pub mod uart; -- cgit From 623623a25f213f76de932eaf4458c3120823d205 Mon Sep 17 00:00:00 2001 From: WillaWillNot Date: Thu, 20 Nov 2025 16:24:15 -0500 Subject: Updated documentation, fixed EXTI definition issues with chips that have touch sensing, updated examples, added generation of convenience method to bind_interrupts for easier type erasure --- .../layer-by-layer/blinky-async/src/main.rs | 16 ++++++- docs/pages/faq.adoc | 6 ++- docs/pages/layer_by_layer.adoc | 2 +- embassy-hal-internal/CHANGELOG.md | 2 + embassy-hal-internal/src/interrupt.rs | 5 ++ embassy-stm32/CHANGELOG.md | 3 ++ embassy-stm32/build.rs | 4 ++ embassy-stm32/src/exti.rs | 53 ++++++++++++++-------- embassy-stm32/src/lib.rs | 15 +++++- examples/boot/application/stm32f3/src/bin/a.rs | 16 ++++++- examples/boot/application/stm32f7/src/bin/a.rs | 16 ++++++- examples/boot/application/stm32h7/src/bin/a.rs | 16 ++++++- examples/boot/application/stm32l0/src/bin/a.rs | 16 ++++++- examples/boot/application/stm32l1/src/bin/a.rs | 16 ++++++- examples/boot/application/stm32l4/src/bin/a.rs | 16 ++++++- examples/boot/application/stm32wl/src/bin/a.rs | 16 ++++++- examples/stm32c0/src/bin/button_exti.rs | 16 ++++++- .../stm32f0/src/bin/button_controlled_blink.rs | 16 ++++++- examples/stm32f0/src/bin/button_exti.rs | 16 ++++++- examples/stm32f3/src/bin/button_events.rs | 16 ++++++- examples/stm32f3/src/bin/button_exti.rs | 16 ++++++- examples/stm32f4/src/bin/button_exti.rs | 16 ++++++- examples/stm32f4/src/bin/eth_w5500.rs | 12 ++++- examples/stm32f4/src/bin/usb_hid_keyboard.rs | 12 ++++- examples/stm32f7/src/bin/button_exti.rs | 16 ++++++- examples/stm32g0/src/bin/button_exti.rs | 16 ++++++- examples/stm32g4/src/bin/button_exti.rs | 16 ++++++- examples/stm32h5/src/bin/button_exti.rs | 16 ++++++- examples/stm32h7/src/bin/button_exti.rs | 16 ++++++- examples/stm32h7rs/src/bin/button_exti.rs | 16 ++++++- examples/stm32l0/src/bin/button_exti.rs | 16 ++++++- examples/stm32l4/src/bin/button_exti.rs | 16 ++++++- examples/stm32l5/src/bin/button_exti.rs | 16 ++++++- examples/stm32n6/src/bin/blinky.rs | 16 ++++++- examples/stm32u0/src/bin/button_exti.rs | 16 ++++++- examples/stm32wb/src/bin/button_exti.rs | 16 ++++++- examples/stm32wba/src/bin/button_exti.rs | 16 ++++++- examples/stm32wba6/src/bin/button_exti.rs | 16 ++++++- examples/stm32wl/src/bin/button_exti.rs | 16 ++++++- examples/stm32wle5/src/bin/button_exti.rs | 16 ++++++- 40 files changed, 509 insertions(+), 85 deletions(-) diff --git a/docs/examples/layer-by-layer/blinky-async/src/main.rs b/docs/examples/layer-by-layer/blinky-async/src/main.rs index 004602816..5871534c5 100644 --- a/docs/examples/layer-by-layer/blinky-async/src/main.rs +++ b/docs/examples/layer-by-layer/blinky-async/src/main.rs @@ -2,15 +2,27 @@ #![no_main] use embassy_executor::Spawner; -use embassy_stm32::exti::ExtiInput; +use embassy_stm32::bind_interrupts; +use embassy_stm32::exti::{self, ExtiInput}; use embassy_stm32::gpio::{Level, Output, Pull, Speed}; +use embassy_stm32::interrupt; use {defmt_rtt as _, panic_probe as _}; +bind_interrupts!( + pub struct Irqs{ + EXTI15_10 => exti::InterruptHandler; +}); + #[embassy_executor::main] async fn main(_spawner: Spawner) { let p = embassy_stm32::init(Default::default()); let mut led = Output::new(p.PB14, Level::Low, Speed::VeryHigh); - let mut button = ExtiInput::new(p.PC13, p.EXTI13, Pull::Up); + let mut button = ExtiInput::new( + p.PC13, + p.EXTI13, + Pull::Up, + Irqs::as_any::>(), + ); loop { button.wait_for_any_edge().await; diff --git a/docs/pages/faq.adoc b/docs/pages/faq.adoc index 8098e12ac..e59ef7b46 100644 --- a/docs/pages/faq.adoc +++ b/docs/pages/faq.adoc @@ -171,7 +171,11 @@ Note that the git revision should match any other embassy patches or git depende == Can I use manual ISRs alongside Embassy? -Yes! This can be useful if you need to respond to an event as fast as possible, and the latency caused by the usual “ISR, wake, return from ISR, context switch to woken task” flow is too much for your application. Simply define a `#[interrupt] fn INTERRUPT_NAME() {}` handler as you would link:https://docs.rust-embedded.org/book/start/interrupts.html[in any other embedded rust project]. +Yes! This can be useful if you need to respond to an event as fast as possible, and the latency caused by the usual “ISR, wake, return from ISR, context switch to woken task” flow is too much for your application. + +You may simply define a `#[interrupt] fn INTERRUPT_NAME() {}` handler as you would link:https://docs.rust-embedded.org/book/start/interrupts.html[in any other embedded rust project]. + +Or you may define a struct implementing the `embassy-[family]::interrupt::typelevel::Handler` trait with an on_interrupt() method, and bind it to the interrupt vector via the `bind_interrupts!` macro, which introduces only a single indirection. This allows the mixing of manual ISRs with Embassy driver-defined ISRs; handlers will be called directly in the order they appear in the macro. == How can I measure resource usage (CPU, RAM, etc.)? diff --git a/docs/pages/layer_by_layer.adoc b/docs/pages/layer_by_layer.adoc index 0692ee4fa..f554e642a 100644 --- a/docs/pages/layer_by_layer.adoc +++ b/docs/pages/layer_by_layer.adoc @@ -76,7 +76,7 @@ The async version looks very similar to the HAL version, apart from a few minor * The peripheral initialization is done by the main macro, and is handed to the main task. * Before checking the button state, the application is awaiting a transition in the pin state (low -> high or high -> low). -When `button.wait_for_any_edge().await` is called, the executor will pause the main task and put the microcontroller in sleep mode, unless there are other tasks that can run. Internally, the Embassy HAL has configured the interrupt handler for the button (in `ExtiInput`), so that whenever an interrupt is raised, the task awaiting the button will be woken up. +When `button.wait_for_any_edge().await` is called, the executor will pause the main task and put the microcontroller in sleep mode, unless there are other tasks that can run. On this chip, interrupt signals on EXTI lines 10-15 (including the button on EXTI line 13) raise the hardware interrupt EXTI15_10. This interrupt handler has been bound (using `bind_interrupts!`) to call the `InterruptHandler` provided by the exti module, so that whenever an interrupt is raised, the task awaiting the button via `wait_for_any_edge()` will be woken up. The minimal overhead of the executor and the ability to run multiple tasks "concurrently" combined with the enormous simplification of the application, makes `async` a great fit for embedded. diff --git a/embassy-hal-internal/CHANGELOG.md b/embassy-hal-internal/CHANGELOG.md index c78923a93..cb637a0ec 100644 --- a/embassy-hal-internal/CHANGELOG.md +++ b/embassy-hal-internal/CHANGELOG.md @@ -8,6 +8,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## Unreleased - ReleaseDate +- feat: type erasure from Binding to AnyBinding, so that drivers accepting type erased peripheral arguments can still check bindings + ## 0.1.1 - 2025-08-15 - First release with changelog. diff --git a/embassy-hal-internal/src/interrupt.rs b/embassy-hal-internal/src/interrupt.rs index c0ea666da..c97dee70b 100644 --- a/embassy-hal-internal/src/interrupt.rs +++ b/embassy-hal-internal/src/interrupt.rs @@ -170,8 +170,11 @@ macro_rules! interrupt_mod { /// Driver which defined the Handler. Always User for user-defined handlers. #[derive(Copy, Clone)] pub enum HandlerType { + /// Defined in user code, or otherwise has not had its SOURCE_ID overridden. User, + /// Defined somewhere within Embassy. Embassy(PrivateHandlerType), + /// Defined by the [embassy-stm32::exti] module. EmbassyStm32Exti(PrivateHandlerType), } @@ -184,9 +187,11 @@ macro_rules! interrupt_mod { handler_source: HandlerType, } impl AnyBinding { + /// Get the IRQ (vector number) of the interrupt. pub const fn irq(&self) -> super::Interrupt { self.irq } + /// Get the source of the handler bound to the interrupt. pub const fn source(&self) -> HandlerType { self.handler_source } diff --git a/embassy-stm32/CHANGELOG.md b/embassy-stm32/CHANGELOG.md index d2f675dbc..7311ea683 100644 --- a/embassy-stm32/CHANGELOG.md +++ b/embassy-stm32/CHANGELOG.md @@ -71,6 +71,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - fix: fixing channel numbers on vbat and vddcore for adc on adc - adc: adding disable to vbat - feat: stm32/flash: add async support for h7 family +- feat: exti brought in line with other drivers' interrupt rebinding system +- feat: bind_interrupts generates convenience method for type erasing to AnyBinding +- fix: exti2 now usable as a normal exti on chips with touch sensing ## 0.4.0 - 2025-08-26 diff --git a/embassy-stm32/build.rs b/embassy-stm32/build.rs index 8cbd38e10..6be3ada51 100644 --- a/embassy-stm32/build.rs +++ b/embassy-stm32/build.rs @@ -56,12 +56,16 @@ fn main() { eprintln!("chip: {chip_name}"); + cfgs.declare("unimpl_tsc"); for p in METADATA.peripherals { if let Some(r) = &p.registers { cfgs.enable(r.kind); foreach_version_cfg(&mut cfgs, r.kind, r.version, |cfgs, cfg_name| { cfgs.enable(cfg_name); }); + } else if p.name == "TSC" { + //Even if the registers are missing, EXTI needs to know if TSC is present in silicon to know whether the EXTI2 interrupt is shadowed by EXTI2_TSC + cfgs.enable("unimpl_tsc") } } diff --git a/embassy-stm32/src/exti.rs b/embassy-stm32/src/exti.rs index 172435caa..93e15a90d 100644 --- a/embassy-stm32/src/exti.rs +++ b/embassy-stm32/src/exti.rs @@ -11,10 +11,10 @@ use futures_util::FutureExt; use crate::gpio::{AnyPin, Input, Level, Pin as GpioPin, PinNumber, Pull}; use crate::interrupt::Interrupt as InterruptEnum; -use crate::interrupt::typelevel::{AnyBinding, HandlerType, Interrupt as InterruptType, PrivateHandlerType}; +use crate::interrupt::typelevel::{AnyBinding, Handler, HandlerType, Interrupt as InterruptType, PrivateHandlerType}; use crate::pac::EXTI; use crate::pac::exti::regs::Lines; -use crate::{Peri, pac, peripherals}; +use crate::{Peri, pac}; const EXTI_COUNT: usize = 16; static EXTI_WAKERS: [AtomicWaker; EXTI_COUNT] = [const { AtomicWaker::new() }; EXTI_COUNT]; @@ -109,10 +109,12 @@ impl<'d> Unpin for ExtiInput<'d> {} impl<'d> ExtiInput<'d> { /// Create an EXTI input. /// - /// The interrupt [Binding] must be type-erased to [AnyBinding] via [Binding::into_any()] in order - /// to support type-erased [AnyChannel] arguments. - /// /// The Binding must bind the Channel's IRQ to [InterruptHandler]. + /// + /// The interrupt [Binding](crate::interrupt::typelevel::Binding) must be type-erased to [AnyBinding] + /// via [into_any()](crate::interrupt::typelevel::Binding::into_any()), in order to support type-erased + /// [AnyChannel] arguments. [bind_interrupts](crate::bind_interrupts) also generates a convenience + /// method as_any() for type erasure that doesn't need a trait object. pub fn new(pin: Peri<'d, T>, ch: Peri<'d, T::ExtiChannel>, pull: Pull, binding: AnyBinding) -> Self { // Needed if using AnyPin+AnyChannel. assert_eq!(pin.pin(), ch.number()); @@ -354,18 +356,17 @@ macro_rules! foreach_exti_irq { ///EXTI interrupt handler. All EXTI interrupt vectors should be bound to this handler. /// -/// It is generic over the [Interrupt](crate::interrupt::typelevel::Interrupt) rather -/// than the [Instance](crate::exti::Instance) because it should not be bound multiple +/// It is generic over the [Interrupt](InterruptType) rather +/// than the [Channel] because it should not be bound multiple /// times to the same vector on chips which multiplex multiple EXTI interrupts into one vector. // // It technically doesn't need to be generic at all, except to satisfy the generic argument -// of [Handler](crate::interrupt::typelevel::Handler). All EXTI interrupts eventually -// land in the same on_irq() function. +// of [Handler]. All EXTI interrupts eventually land in the same on_irq() function. pub struct InterruptHandler { _phantom: PhantomData, } -impl crate::interrupt::typelevel::Handler for InterruptHandler { +impl Handler for InterruptHandler { const SOURCE_ID: HandlerType = HandlerType::EmbassyStm32Exti(PrivateHandlerType::new()); unsafe fn on_interrupt() { on_irq() @@ -377,10 +378,15 @@ trait SealedChannel {} /// EXTI channel trait. #[allow(private_bounds)] pub trait Channel: PeripheralType + SealedChannel + Sized { - /// Get the EXTI channel number. + /// EXTI channel number. fn number(&self) -> PinNumber; - /// Get the EXTI IRQ, which may be the same for multiple channels + /// [Enum-level Interrupt](InterruptEnum), which may be the same for multiple channels. fn irq(&self) -> InterruptEnum; + /// [Type-level Interrupt](InterruptType), which may be the same for multiple channels. + /// Unavailable on type-erased [AnyChannel], where it is defined as the unit type. + /// has no trait bound, so mostly only useful for doc reference (e.g. while writing + /// [bind_interrupts](crate::bind_interrupts) invocation.) Use irq() for programmatic access. + type INTERRUPT; } /// Type-erased EXTI channel. @@ -400,24 +406,32 @@ impl Channel for AnyChannel { fn irq(&self) -> InterruptEnum { self.irq } + type INTERRUPT = bool; } macro_rules! impl_exti { ($type:ident, $number:expr) => { - impl SealedChannel for peripherals::$type {} - impl Channel for peripherals::$type { + impl_exti!(@inner $type, $number, crate::_generated::peripheral_interrupts::EXTI::$type); + }; + ($type:ident, $number:expr, @tsc) => { + impl_exti!(@inner $type, $number, crate::_generated::peripheral_interrupts::TSC::GLOBAL); + }; + (@inner $type:ident, $number:expr, $irq:path) => { + impl SealedChannel for crate::peripherals::$type {} + impl Channel for crate::peripherals::$type { fn number(&self) -> PinNumber { $number } fn irq(&self) -> InterruptEnum { - crate::_generated::peripheral_interrupts::EXTI::$type::IRQ + <$irq>::IRQ } + type INTERRUPT = $irq; } - impl From for AnyChannel { - fn from(_val: peripherals::$type) -> Self { + impl From for AnyChannel { + fn from(_val: crate::peripherals::$type) -> Self { Self { number: $number, - irq: crate::_generated::peripheral_interrupts::EXTI::$type::IRQ, + irq: <$irq>::IRQ, } } } @@ -426,7 +440,10 @@ macro_rules! impl_exti { impl_exti!(EXTI0, 0); impl_exti!(EXTI1, 1); +#[cfg(not(any(tsc, unimpl_tsc)))] impl_exti!(EXTI2, 2); +#[cfg(any(tsc, unimpl_tsc))] +impl_exti!(EXTI2, 2, @tsc); impl_exti!(EXTI3, 3); impl_exti!(EXTI4, 4); impl_exti!(EXTI5, 5); diff --git a/embassy-stm32/src/lib.rs b/embassy-stm32/src/lib.rs index ef6f1d6dc..e5ffbd753 100644 --- a/embassy-stm32/src/lib.rs +++ b/embassy-stm32/src/lib.rs @@ -151,7 +151,7 @@ pub use crate::_generated::interrupt; /// Macro to bind interrupts to handlers. /// /// 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 +/// and implements the right [`Binding`](crate::interrupt::typelevel::Binding)s for it. You can pass this struct to drivers to /// prove at compile-time that the right interrupts have been bound. /// /// Example of how to bind one interrupt: @@ -195,6 +195,19 @@ macro_rules! bind_interrupts { $(#[$outer])* $vis struct $name; + impl $name { + #[doc = r"Convenience method to call Binding::into_any(). Unlike the trait method, can be called with a turbofish."] + pub fn as_any< + I: $crate::interrupt::typelevel::Interrupt, + H: $crate::interrupt::typelevel::Handler + >() -> $crate::interrupt::typelevel::AnyBinding + where + Self: $crate::interrupt::typelevel::Binding + { + >::into_any() + } + } + $( #[allow(non_snake_case)] #[unsafe(no_mangle)] diff --git a/examples/boot/application/stm32f3/src/bin/a.rs b/examples/boot/application/stm32f3/src/bin/a.rs index b608b2e01..e42e24885 100644 --- a/examples/boot/application/stm32f3/src/bin/a.rs +++ b/examples/boot/application/stm32f3/src/bin/a.rs @@ -6,12 +6,19 @@ use defmt_rtt::*; use embassy_boot_stm32::{AlignedBuffer, FirmwareUpdater, FirmwareUpdaterConfig}; use embassy_embedded_hal::adapter::BlockingAsync; use embassy_executor::Spawner; -use embassy_stm32::exti::ExtiInput; +use embassy_stm32::bind_interrupts; +use embassy_stm32::exti::{self, ExtiInput}; use embassy_stm32::flash::{Flash, WRITE_SIZE}; use embassy_stm32::gpio::{Level, Output, Pull, Speed}; +use embassy_stm32::interrupt; use embassy_sync::mutex::Mutex; use panic_reset as _; +bind_interrupts!( + pub struct Irqs{ + EXTI15_10 => exti::InterruptHandler; +}); + #[cfg(feature = "skip-include")] static APP_B: &[u8] = &[0, 1, 2, 3]; #[cfg(not(feature = "skip-include"))] @@ -23,7 +30,12 @@ async fn main(_spawner: Spawner) { let flash = Flash::new_blocking(p.FLASH); let flash = Mutex::new(BlockingAsync::new(flash)); - let mut button = ExtiInput::new(p.PC13, p.EXTI13, Pull::Up); + let mut button = ExtiInput::new( + p.PC13, + p.EXTI13, + Pull::Up, + Irqs::as_any::>(), + ); let mut led = Output::new(p.PA5, Level::Low, Speed::Low); led.set_high(); diff --git a/examples/boot/application/stm32f7/src/bin/a.rs b/examples/boot/application/stm32f7/src/bin/a.rs index 172b4c235..5ec220c2e 100644 --- a/examples/boot/application/stm32f7/src/bin/a.rs +++ b/examples/boot/application/stm32f7/src/bin/a.rs @@ -7,9 +7,11 @@ use core::cell::RefCell; use defmt_rtt::*; use embassy_boot_stm32::{AlignedBuffer, BlockingFirmwareUpdater, FirmwareUpdaterConfig}; use embassy_executor::Spawner; -use embassy_stm32::exti::ExtiInput; +use embassy_stm32::bind_interrupts; +use embassy_stm32::exti::{self, ExtiInput}; use embassy_stm32::flash::{Flash, WRITE_SIZE}; use embassy_stm32::gpio::{Level, Output, Pull, Speed}; +use embassy_stm32::interrupt; use embassy_sync::blocking_mutex::Mutex; use embedded_storage::nor_flash::NorFlash; use panic_reset as _; @@ -19,13 +21,23 @@ static APP_B: &[u8] = &[0, 1, 2, 3]; #[cfg(not(feature = "skip-include"))] static APP_B: &[u8] = include_bytes!("../../b.bin"); +bind_interrupts!( + pub struct Irqs{ + EXTI15_10 => exti::InterruptHandler; +}); + #[embassy_executor::main] async fn main(_spawner: Spawner) { let p = embassy_stm32::init(Default::default()); let flash = Flash::new_blocking(p.FLASH); let flash = Mutex::new(RefCell::new(flash)); - let mut button = ExtiInput::new(p.PC13, p.EXTI13, Pull::Down); + let mut button = ExtiInput::new( + p.PC13, + p.EXTI13, + Pull::Down, + Irqs::as_any::>(), + ); let mut led = Output::new(p.PB7, Level::Low, Speed::Low); led.set_high(); diff --git a/examples/boot/application/stm32h7/src/bin/a.rs b/examples/boot/application/stm32h7/src/bin/a.rs index c1b1a267a..00ac89564 100644 --- a/examples/boot/application/stm32h7/src/bin/a.rs +++ b/examples/boot/application/stm32h7/src/bin/a.rs @@ -7,13 +7,20 @@ use core::cell::RefCell; use defmt_rtt::*; use embassy_boot_stm32::{AlignedBuffer, BlockingFirmwareUpdater, FirmwareUpdaterConfig}; use embassy_executor::Spawner; -use embassy_stm32::exti::ExtiInput; +use embassy_stm32::bind_interrupts; +use embassy_stm32::exti::{self, ExtiInput}; use embassy_stm32::flash::{Flash, WRITE_SIZE}; use embassy_stm32::gpio::{Level, Output, Pull, Speed}; +use embassy_stm32::interrupt; use embassy_sync::blocking_mutex::Mutex; use embedded_storage::nor_flash::NorFlash; use panic_reset as _; +bind_interrupts!( + pub struct Irqs{ + EXTI15_10 => exti::InterruptHandler; +}); + #[cfg(feature = "skip-include")] static APP_B: &[u8] = &[0, 1, 2, 3]; #[cfg(not(feature = "skip-include"))] @@ -25,7 +32,12 @@ async fn main(_spawner: Spawner) { let flash = Flash::new_blocking(p.FLASH); let flash = Mutex::new(RefCell::new(flash)); - let mut button = ExtiInput::new(p.PC13, p.EXTI13, Pull::Down); + let mut button = ExtiInput::new( + p.PC13, + p.EXTI13, + Pull::Down, + Irqs::as_any::>(), + ); let mut led = Output::new(p.PB14, Level::Low, Speed::Low); led.set_high(); diff --git a/examples/boot/application/stm32l0/src/bin/a.rs b/examples/boot/application/stm32l0/src/bin/a.rs index dcc10e5c6..6f21d9be0 100644 --- a/examples/boot/application/stm32l0/src/bin/a.rs +++ b/examples/boot/application/stm32l0/src/bin/a.rs @@ -6,13 +6,20 @@ use defmt_rtt::*; use embassy_boot_stm32::{AlignedBuffer, FirmwareUpdater, FirmwareUpdaterConfig}; use embassy_embedded_hal::adapter::BlockingAsync; use embassy_executor::Spawner; -use embassy_stm32::exti::ExtiInput; +use embassy_stm32::bind_interrupts; +use embassy_stm32::exti::{self, ExtiInput}; use embassy_stm32::flash::{Flash, WRITE_SIZE}; use embassy_stm32::gpio::{Level, Output, Pull, Speed}; +use embassy_stm32::interrupt; use embassy_sync::mutex::Mutex; use embassy_time::Timer; use panic_reset as _; +bind_interrupts!( + pub struct Irqs{ + EXTI2_3 => exti::InterruptHandler; +}); + #[cfg(feature = "skip-include")] static APP_B: &[u8] = &[0, 1, 2, 3]; #[cfg(not(feature = "skip-include"))] @@ -24,7 +31,12 @@ async fn main(_spawner: Spawner) { let flash = Flash::new_blocking(p.FLASH); let flash = Mutex::new(BlockingAsync::new(flash)); - let mut button = ExtiInput::new(p.PB2, p.EXTI2, Pull::Up); + let mut button = ExtiInput::new( + p.PB2, + p.EXTI2, + Pull::Up, + Irqs::as_any::>(), + ); let mut led = Output::new(p.PB5, Level::Low, Speed::Low); diff --git a/examples/boot/application/stm32l1/src/bin/a.rs b/examples/boot/application/stm32l1/src/bin/a.rs index dcc10e5c6..16f280776 100644 --- a/examples/boot/application/stm32l1/src/bin/a.rs +++ b/examples/boot/application/stm32l1/src/bin/a.rs @@ -6,9 +6,11 @@ use defmt_rtt::*; use embassy_boot_stm32::{AlignedBuffer, FirmwareUpdater, FirmwareUpdaterConfig}; use embassy_embedded_hal::adapter::BlockingAsync; use embassy_executor::Spawner; -use embassy_stm32::exti::ExtiInput; +use embassy_stm32::bind_interrupts; +use embassy_stm32::exti::{self, ExtiInput}; use embassy_stm32::flash::{Flash, WRITE_SIZE}; use embassy_stm32::gpio::{Level, Output, Pull, Speed}; +use embassy_stm32::interrupt; use embassy_sync::mutex::Mutex; use embassy_time::Timer; use panic_reset as _; @@ -18,6 +20,11 @@ static APP_B: &[u8] = &[0, 1, 2, 3]; #[cfg(not(feature = "skip-include"))] static APP_B: &[u8] = include_bytes!("../../b.bin"); +bind_interrupts!( + pub struct Irqs{ + EXTI2 => exti::InterruptHandler; +}); + #[embassy_executor::main] async fn main(_spawner: Spawner) { let p = embassy_stm32::init(Default::default()); @@ -26,7 +33,12 @@ async fn main(_spawner: Spawner) { let mut button = ExtiInput::new(p.PB2, p.EXTI2, Pull::Up); - let mut led = Output::new(p.PB5, Level::Low, Speed::Low); + let mut led = Output::new( + p.PB5, + Level::Low, + Speed::Low, + Irqs::as_any::>(), + ); led.set_high(); diff --git a/examples/boot/application/stm32l4/src/bin/a.rs b/examples/boot/application/stm32l4/src/bin/a.rs index 7f8015c04..5e5d45193 100644 --- a/examples/boot/application/stm32l4/src/bin/a.rs +++ b/examples/boot/application/stm32l4/src/bin/a.rs @@ -6,9 +6,11 @@ use defmt_rtt::*; use embassy_boot_stm32::{AlignedBuffer, FirmwareUpdater, FirmwareUpdaterConfig}; use embassy_embedded_hal::adapter::BlockingAsync; use embassy_executor::Spawner; -use embassy_stm32::exti::ExtiInput; +use embassy_stm32::bind_interrupts; +use embassy_stm32::exti::{self, ExtiInput}; use embassy_stm32::flash::{Flash, WRITE_SIZE}; use embassy_stm32::gpio::{Level, Output, Pull, Speed}; +use embassy_stm32::interrupt; use embassy_sync::mutex::Mutex; use panic_reset as _; @@ -17,13 +19,23 @@ static APP_B: &[u8] = &[0, 1, 2, 3]; #[cfg(not(feature = "skip-include"))] static APP_B: &[u8] = include_bytes!("../../b.bin"); +bind_interrupts!( + pub struct Irqs{ + EXTI15_10 => exti::InterruptHandler; +}); + #[embassy_executor::main] async fn main(_spawner: Spawner) { let p = embassy_stm32::init(Default::default()); let flash = Flash::new_blocking(p.FLASH); let flash = Mutex::new(BlockingAsync::new(flash)); - let mut button = ExtiInput::new(p.PC13, p.EXTI13, Pull::Up); + let mut button = ExtiInput::new( + p.PC13, + p.EXTI13, + Pull::Up, + Irqs::as_any::>(), + ); let mut led = Output::new(p.PB14, Level::Low, Speed::Low); led.set_high(); diff --git a/examples/boot/application/stm32wl/src/bin/a.rs b/examples/boot/application/stm32wl/src/bin/a.rs index 3f381fd80..ad45c1262 100644 --- a/examples/boot/application/stm32wl/src/bin/a.rs +++ b/examples/boot/application/stm32wl/src/bin/a.rs @@ -9,9 +9,11 @@ use embassy_boot_stm32::{AlignedBuffer, FirmwareUpdater, FirmwareUpdaterConfig}; use embassy_embedded_hal::adapter::BlockingAsync; use embassy_executor::Spawner; use embassy_stm32::SharedData; -use embassy_stm32::exti::ExtiInput; +use embassy_stm32::bind_interrupts; +use embassy_stm32::exti::{self, ExtiInput}; use embassy_stm32::flash::{Flash, WRITE_SIZE}; use embassy_stm32::gpio::{Level, Output, Pull, Speed}; +use embassy_stm32::interrupt; use embassy_sync::mutex::Mutex; use panic_reset as _; @@ -20,6 +22,11 @@ static APP_B: &[u8] = &[0, 1, 2, 3]; #[cfg(not(feature = "skip-include"))] static APP_B: &[u8] = include_bytes!("../../b.bin"); +bind_interrupts!( + pub struct Irqs{ + EXTEXTI0I2_3 => exti::InterruptHandler; +}); + #[unsafe(link_section = ".shared_data")] static SHARED_DATA: MaybeUninit = MaybeUninit::uninit(); @@ -29,7 +36,12 @@ async fn main(_spawner: Spawner) { let flash = Flash::new_blocking(p.FLASH); let flash = Mutex::new(BlockingAsync::new(flash)); - let mut button = ExtiInput::new(p.PA0, p.EXTI0, Pull::Up); + let mut button = ExtiInput::new( + p.PA0, + p.EXTI0, + Pull::Up, + Irqs::as_any::>(), + ); let mut led = Output::new(p.PB9, Level::Low, Speed::Low); led.set_high(); diff --git a/examples/stm32c0/src/bin/button_exti.rs b/examples/stm32c0/src/bin/button_exti.rs index 34a08bbc6..dbc9e2f50 100644 --- a/examples/stm32c0/src/bin/button_exti.rs +++ b/examples/stm32c0/src/bin/button_exti.rs @@ -3,16 +3,28 @@ use defmt::*; use embassy_executor::Spawner; -use embassy_stm32::exti::ExtiInput; +use embassy_stm32::bind_interrupts; +use embassy_stm32::exti::{self, ExtiInput}; use embassy_stm32::gpio::Pull; +use embassy_stm32::interrupt; use {defmt_rtt as _, panic_probe as _}; +bind_interrupts!( + pub struct Irqs{ + EXTI4_15 => exti::InterruptHandler; +}); + #[embassy_executor::main] async fn main(_spawner: Spawner) { let p = embassy_stm32::init(Default::default()); info!("Hello World!"); - let mut button = ExtiInput::new(p.PC13, p.EXTI13, Pull::Up); + let mut button = ExtiInput::new( + p.PC13, + p.EXTI13, + Pull::Up, + Irqs::as_any::>(), + ); info!("Press the USER button..."); diff --git a/examples/stm32f0/src/bin/button_controlled_blink.rs b/examples/stm32f0/src/bin/button_controlled_blink.rs index 0b678af01..f57d5d200 100644 --- a/examples/stm32f0/src/bin/button_controlled_blink.rs +++ b/examples/stm32f0/src/bin/button_controlled_blink.rs @@ -8,13 +8,20 @@ use core::sync::atomic::{AtomicU32, Ordering}; use defmt::info; use embassy_executor::Spawner; use embassy_stm32::Peri; -use embassy_stm32::exti::ExtiInput; +use embassy_stm32::bind_interrupts; +use embassy_stm32::exti::{self, ExtiInput}; use embassy_stm32::gpio::{AnyPin, Level, Output, Pull, Speed}; +use embassy_stm32::interrupt; use embassy_time::Timer; use {defmt_rtt as _, panic_probe as _}; static BLINK_MS: AtomicU32 = AtomicU32::new(0); +bind_interrupts!( + pub struct Irqs{ + EXTI4_15 => exti::InterruptHandler; +}); + #[embassy_executor::task] async fn led_task(led: Peri<'static, AnyPin>) { // Configure the LED pin as a push pull output and obtain handler. @@ -37,7 +44,12 @@ async fn main(spawner: Spawner) { // Configure the button pin and obtain handler. // On the Nucleo F091RC there is a button connected to pin PC13. - let mut button = ExtiInput::new(p.PC13, p.EXTI13, Pull::None); + let mut button = ExtiInput::new( + p.PC13, + p.EXTI13, + Pull::None, + Irqs::as_any::>(), + ); // Create and initialize a delay variable to manage delay loop let mut del_var = 2000; diff --git a/examples/stm32f0/src/bin/button_exti.rs b/examples/stm32f0/src/bin/button_exti.rs index fd615a215..67ea19215 100644 --- a/examples/stm32f0/src/bin/button_exti.rs +++ b/examples/stm32f0/src/bin/button_exti.rs @@ -3,17 +3,29 @@ use defmt::*; use embassy_executor::Spawner; -use embassy_stm32::exti::ExtiInput; +use embassy_stm32::bind_interrupts; +use embassy_stm32::exti::{self, ExtiInput}; use embassy_stm32::gpio::Pull; +use embassy_stm32::interrupt; use {defmt_rtt as _, panic_probe as _}; +bind_interrupts!( + pub struct Irqs{ + EXTI4_15 => exti::InterruptHandler; +}); + #[embassy_executor::main] async fn main(_spawner: Spawner) { // Initialize and create handle for devicer peripherals let p = embassy_stm32::init(Default::default()); // Configure the button pin and obtain handler. // On the Nucleo F091RC there is a button connected to pin PC13. - let mut button = ExtiInput::new(p.PC13, p.EXTI13, Pull::Down); + let mut button = ExtiInput::new( + p.PC13, + p.EXTI13, + Pull::Down, + Irqs::as_any::>(), + ); info!("Press the USER button..."); loop { diff --git a/examples/stm32f3/src/bin/button_events.rs b/examples/stm32f3/src/bin/button_events.rs index 99957a641..78c6592ee 100644 --- a/examples/stm32f3/src/bin/button_events.rs +++ b/examples/stm32f3/src/bin/button_events.rs @@ -11,13 +11,20 @@ use defmt::*; use embassy_executor::Spawner; -use embassy_stm32::exti::ExtiInput; +use embassy_stm32::bind_interrupts; +use embassy_stm32::exti::{self, ExtiInput}; use embassy_stm32::gpio::{Level, Output, Pull, Speed}; +use embassy_stm32::interrupt; use embassy_sync::blocking_mutex::raw::ThreadModeRawMutex; use embassy_sync::channel::Channel; use embassy_time::{Duration, Timer, with_timeout}; use {defmt_rtt as _, panic_probe as _}; +bind_interrupts!( + pub struct Irqs{ + EXTI0 => exti::InterruptHandler; +}); + struct Leds<'a> { leds: [Output<'a>; 8], direction: i8, @@ -99,7 +106,12 @@ static CHANNEL: Channel = Channel::new(); #[embassy_executor::main] async fn main(spawner: Spawner) { let p = embassy_stm32::init(Default::default()); - let button = ExtiInput::new(p.PA0, p.EXTI0, Pull::Down); + let button = ExtiInput::new( + p.PA0, + p.EXTI0, + Pull::Down, + Irqs::as_any::>(), + ); info!("Press the USER button..."); let leds = [ Output::new(p.PE9, Level::Low, Speed::Low), diff --git a/examples/stm32f3/src/bin/button_exti.rs b/examples/stm32f3/src/bin/button_exti.rs index a55530e0e..d6d613447 100644 --- a/examples/stm32f3/src/bin/button_exti.rs +++ b/examples/stm32f3/src/bin/button_exti.rs @@ -3,16 +3,28 @@ use defmt::*; use embassy_executor::Spawner; -use embassy_stm32::exti::ExtiInput; +use embassy_stm32::bind_interrupts; +use embassy_stm32::exti::{self, ExtiInput}; use embassy_stm32::gpio::Pull; +use embassy_stm32::interrupt; use {defmt_rtt as _, panic_probe as _}; +bind_interrupts!( + pub struct Irqs{ + EXTI0 => exti::InterruptHandler; +}); + #[embassy_executor::main] async fn main(_spawner: Spawner) { let p = embassy_stm32::init(Default::default()); info!("Hello World!"); - let mut button = ExtiInput::new(p.PA0, p.EXTI0, Pull::Down); + let mut button = ExtiInput::new( + p.PA0, + p.EXTI0, + Pull::Down, + Irqs::as_any::>(), + ); info!("Press the USER button..."); diff --git a/examples/stm32f4/src/bin/button_exti.rs b/examples/stm32f4/src/bin/button_exti.rs index 2a546dac5..77831224b 100644 --- a/examples/stm32f4/src/bin/button_exti.rs +++ b/examples/stm32f4/src/bin/button_exti.rs @@ -3,16 +3,28 @@ use defmt::*; use embassy_executor::Spawner; -use embassy_stm32::exti::ExtiInput; +use embassy_stm32::bind_interrupts; +use embassy_stm32::exti::{self, ExtiInput}; use embassy_stm32::gpio::Pull; +use embassy_stm32::interrupt; use {defmt_rtt as _, panic_probe as _}; +bind_interrupts!( + pub struct Irqs{ + EXTI15_10 => exti::InterruptHandler; +}); + #[embassy_executor::main] async fn main(_spawner: Spawner) { let p = embassy_stm32::init(Default::default()); info!("Hello World!"); - let mut button = ExtiInput::new(p.PC13, p.EXTI13, Pull::Down); + let mut button = ExtiInput::new( + p.PC13, + p.EXTI13, + Pull::Down, + Irqs::as_any::>(), + ); info!("Press the USER button..."); diff --git a/examples/stm32f4/src/bin/eth_w5500.rs b/examples/stm32f4/src/bin/eth_w5500.rs index 0adcda614..f7d2ce7de 100644 --- a/examples/stm32f4/src/bin/eth_w5500.rs +++ b/examples/stm32f4/src/bin/eth_w5500.rs @@ -7,8 +7,10 @@ use embassy_net::tcp::TcpSocket; use embassy_net::{Ipv4Address, StackResources}; use embassy_net_wiznet::chip::W5500; use embassy_net_wiznet::{Device, Runner, State}; -use embassy_stm32::exti::ExtiInput; +use embassy_stm32::bind_interrupts; +use embassy_stm32::exti::{self, ExtiInput}; use embassy_stm32::gpio::{Level, Output, Pull, Speed}; +use embassy_stm32::interrupt; use embassy_stm32::mode::Async; use embassy_stm32::rng::Rng; use embassy_stm32::spi::Spi; @@ -23,6 +25,7 @@ use {defmt_rtt as _, panic_probe as _}; bind_interrupts!(struct Irqs { HASH_RNG => rng::InterruptHandler; + EXTI0 => exti::InterruptHandler; }); type EthernetSPI = ExclusiveDevice, Output<'static>, Delay>; @@ -75,7 +78,12 @@ async fn main(spawner: Spawner) -> ! { let cs = Output::new(p.PA4, Level::High, Speed::VeryHigh); let spi = unwrap!(ExclusiveDevice::new(spi, cs, Delay)); - let w5500_int = ExtiInput::new(p.PB0, p.EXTI0, Pull::Up); + let w5500_int = ExtiInput::new( + p.PB0, + p.EXTI0, + Pull::Up, + Irqs::as_any::>(), + ); let w5500_reset = Output::new(p.PB1, Level::High, Speed::VeryHigh); let mac_addr = [0x02, 234, 3, 4, 82, 231]; diff --git a/examples/stm32f4/src/bin/usb_hid_keyboard.rs b/examples/stm32f4/src/bin/usb_hid_keyboard.rs index 9971e43f5..60922c2e8 100644 --- a/examples/stm32f4/src/bin/usb_hid_keyboard.rs +++ b/examples/stm32f4/src/bin/usb_hid_keyboard.rs @@ -6,8 +6,10 @@ use core::sync::atomic::{AtomicBool, AtomicU8, Ordering}; use defmt::*; use embassy_executor::Spawner; use embassy_futures::join::join; -use embassy_stm32::exti::ExtiInput; +use embassy_stm32::bind_interrupts; +use embassy_stm32::exti::{self, ExtiInput}; use embassy_stm32::gpio::Pull; +use embassy_stm32::interrupt; use embassy_stm32::time::Hertz; use embassy_stm32::usb::Driver; use embassy_stm32::{Config, bind_interrupts, peripherals, usb}; @@ -21,6 +23,7 @@ use {defmt_rtt as _, panic_probe as _}; bind_interrupts!(struct Irqs { OTG_FS => usb::InterruptHandler; + EXTI15_10 => exti::InterruptHandler; }); static HID_PROTOCOL_MODE: AtomicU8 = AtomicU8::new(HidProtocolMode::Boot as u8); @@ -123,7 +126,12 @@ async fn main(_spawner: Spawner) { let (reader, mut writer) = hid.split(); - let mut button = ExtiInput::new(p.PC13, p.EXTI13, Pull::Down); + let mut button = ExtiInput::new( + p.PC13, + p.EXTI13, + Pull::Down, + Irqs::as_any::>(), + ); // Do stuff with the class! let in_fut = async { diff --git a/examples/stm32f7/src/bin/button_exti.rs b/examples/stm32f7/src/bin/button_exti.rs index 2a546dac5..77831224b 100644 --- a/examples/stm32f7/src/bin/button_exti.rs +++ b/examples/stm32f7/src/bin/button_exti.rs @@ -3,16 +3,28 @@ use defmt::*; use embassy_executor::Spawner; -use embassy_stm32::exti::ExtiInput; +use embassy_stm32::bind_interrupts; +use embassy_stm32::exti::{self, ExtiInput}; use embassy_stm32::gpio::Pull; +use embassy_stm32::interrupt; use {defmt_rtt as _, panic_probe as _}; +bind_interrupts!( + pub struct Irqs{ + EXTI15_10 => exti::InterruptHandler; +}); + #[embassy_executor::main] async fn main(_spawner: Spawner) { let p = embassy_stm32::init(Default::default()); info!("Hello World!"); - let mut button = ExtiInput::new(p.PC13, p.EXTI13, Pull::Down); + let mut button = ExtiInput::new( + p.PC13, + p.EXTI13, + Pull::Down, + Irqs::as_any::>(), + ); info!("Press the USER button..."); diff --git a/examples/stm32g0/src/bin/button_exti.rs b/examples/stm32g0/src/bin/button_exti.rs index 34a08bbc6..dbc9e2f50 100644 --- a/examples/stm32g0/src/bin/button_exti.rs +++ b/examples/stm32g0/src/bin/button_exti.rs @@ -3,16 +3,28 @@ use defmt::*; use embassy_executor::Spawner; -use embassy_stm32::exti::ExtiInput; +use embassy_stm32::bind_interrupts; +use embassy_stm32::exti::{self, ExtiInput}; use embassy_stm32::gpio::Pull; +use embassy_stm32::interrupt; use {defmt_rtt as _, panic_probe as _}; +bind_interrupts!( + pub struct Irqs{ + EXTI4_15 => exti::InterruptHandler; +}); + #[embassy_executor::main] async fn main(_spawner: Spawner) { let p = embassy_stm32::init(Default::default()); info!("Hello World!"); - let mut button = ExtiInput::new(p.PC13, p.EXTI13, Pull::Up); + let mut button = ExtiInput::new( + p.PC13, + p.EXTI13, + Pull::Up, + Irqs::as_any::>(), + ); info!("Press the USER button..."); diff --git a/examples/stm32g4/src/bin/button_exti.rs b/examples/stm32g4/src/bin/button_exti.rs index 2a546dac5..77831224b 100644 --- a/examples/stm32g4/src/bin/button_exti.rs +++ b/examples/stm32g4/src/bin/button_exti.rs @@ -3,16 +3,28 @@ use defmt::*; use embassy_executor::Spawner; -use embassy_stm32::exti::ExtiInput; +use embassy_stm32::bind_interrupts; +use embassy_stm32::exti::{self, ExtiInput}; use embassy_stm32::gpio::Pull; +use embassy_stm32::interrupt; use {defmt_rtt as _, panic_probe as _}; +bind_interrupts!( + pub struct Irqs{ + EXTI15_10 => exti::InterruptHandler; +}); + #[embassy_executor::main] async fn main(_spawner: Spawner) { let p = embassy_stm32::init(Default::default()); info!("Hello World!"); - let mut button = ExtiInput::new(p.PC13, p.EXTI13, Pull::Down); + let mut button = ExtiInput::new( + p.PC13, + p.EXTI13, + Pull::Down, + Irqs::as_any::>(), + ); info!("Press the USER button..."); diff --git a/examples/stm32h5/src/bin/button_exti.rs b/examples/stm32h5/src/bin/button_exti.rs index 2a546dac5..f49f0ff1c 100644 --- a/examples/stm32h5/src/bin/button_exti.rs +++ b/examples/stm32h5/src/bin/button_exti.rs @@ -3,16 +3,28 @@ use defmt::*; use embassy_executor::Spawner; -use embassy_stm32::exti::ExtiInput; +use embassy_stm32::bind_interrupts; +use embassy_stm32::exti::{self, ExtiInput}; use embassy_stm32::gpio::Pull; +use embassy_stm32::interrupt; use {defmt_rtt as _, panic_probe as _}; +bind_interrupts!( + pub struct Irqs{ + EXTI13 => exti::InterruptHandler; +}); + #[embassy_executor::main] async fn main(_spawner: Spawner) { let p = embassy_stm32::init(Default::default()); info!("Hello World!"); - let mut button = ExtiInput::new(p.PC13, p.EXTI13, Pull::Down); + let mut button = ExtiInput::new( + p.PC13, + p.EXTI13, + Pull::Down, + Irqs::as_any::>(), + ); info!("Press the USER button..."); diff --git a/examples/stm32h7/src/bin/button_exti.rs b/examples/stm32h7/src/bin/button_exti.rs index 2a546dac5..77831224b 100644 --- a/examples/stm32h7/src/bin/button_exti.rs +++ b/examples/stm32h7/src/bin/button_exti.rs @@ -3,16 +3,28 @@ use defmt::*; use embassy_executor::Spawner; -use embassy_stm32::exti::ExtiInput; +use embassy_stm32::bind_interrupts; +use embassy_stm32::exti::{self, ExtiInput}; use embassy_stm32::gpio::Pull; +use embassy_stm32::interrupt; use {defmt_rtt as _, panic_probe as _}; +bind_interrupts!( + pub struct Irqs{ + EXTI15_10 => exti::InterruptHandler; +}); + #[embassy_executor::main] async fn main(_spawner: Spawner) { let p = embassy_stm32::init(Default::default()); info!("Hello World!"); - let mut button = ExtiInput::new(p.PC13, p.EXTI13, Pull::Down); + let mut button = ExtiInput::new( + p.PC13, + p.EXTI13, + Pull::Down, + Irqs::as_any::>(), + ); info!("Press the USER button..."); diff --git a/examples/stm32h7rs/src/bin/button_exti.rs b/examples/stm32h7rs/src/bin/button_exti.rs index 34a08bbc6..d4cf36fcc 100644 --- a/examples/stm32h7rs/src/bin/button_exti.rs +++ b/examples/stm32h7rs/src/bin/button_exti.rs @@ -3,16 +3,28 @@ use defmt::*; use embassy_executor::Spawner; -use embassy_stm32::exti::ExtiInput; +use embassy_stm32::bind_interrupts; +use embassy_stm32::exti::{self, ExtiInput}; use embassy_stm32::gpio::Pull; +use embassy_stm32::interrupt; use {defmt_rtt as _, panic_probe as _}; +bind_interrupts!( + pub struct Irqs{ + EXTI13 => exti::InterruptHandler; +}); + #[embassy_executor::main] async fn main(_spawner: Spawner) { let p = embassy_stm32::init(Default::default()); info!("Hello World!"); - let mut button = ExtiInput::new(p.PC13, p.EXTI13, Pull::Up); + let mut button = ExtiInput::new( + p.PC13, + p.EXTI13, + Pull::Up, + Irqs::as_any::>(), + ); info!("Press the USER button..."); diff --git a/examples/stm32l0/src/bin/button_exti.rs b/examples/stm32l0/src/bin/button_exti.rs index 7ff4a7d52..afdeb14af 100644 --- a/examples/stm32l0/src/bin/button_exti.rs +++ b/examples/stm32l0/src/bin/button_exti.rs @@ -4,16 +4,28 @@ use defmt::*; use embassy_executor::Spawner; use embassy_stm32::Config; -use embassy_stm32::exti::ExtiInput; +use embassy_stm32::bind_interrupts; +use embassy_stm32::exti::{self, ExtiInput}; use embassy_stm32::gpio::Pull; +use embassy_stm32::interrupt; use {defmt_rtt as _, panic_probe as _}; +bind_interrupts!( + pub struct Irqs{ + EXTI2_3 => exti::InterruptHandler; +}); + #[embassy_executor::main] async fn main(_spawner: Spawner) { let config = Config::default(); let p = embassy_stm32::init(config); - let mut button = ExtiInput::new(p.PB2, p.EXTI2, Pull::Up); + let mut button = ExtiInput::new( + p.PB2, + p.EXTI2, + Pull::Up, + Irqs::as_any::>(), + ); info!("Press the USER button..."); diff --git a/examples/stm32l4/src/bin/button_exti.rs b/examples/stm32l4/src/bin/button_exti.rs index 34a08bbc6..4d03671c0 100644 --- a/examples/stm32l4/src/bin/button_exti.rs +++ b/examples/stm32l4/src/bin/button_exti.rs @@ -3,16 +3,28 @@ use defmt::*; use embassy_executor::Spawner; -use embassy_stm32::exti::ExtiInput; +use embassy_stm32::bind_interrupts; +use embassy_stm32::exti::{self, ExtiInput}; use embassy_stm32::gpio::Pull; +use embassy_stm32::interrupt; use {defmt_rtt as _, panic_probe as _}; +bind_interrupts!( + pub struct Irqs{ + EXTI15_10 => exti::InterruptHandler; +}); + #[embassy_executor::main] async fn main(_spawner: Spawner) { let p = embassy_stm32::init(Default::default()); info!("Hello World!"); - let mut button = ExtiInput::new(p.PC13, p.EXTI13, Pull::Up); + let mut button = ExtiInput::new( + p.PC13, + p.EXTI13, + Pull::Up, + Irqs::as_any::>(), + ); info!("Press the USER button..."); diff --git a/examples/stm32l5/src/bin/button_exti.rs b/examples/stm32l5/src/bin/button_exti.rs index e6639d22b..02e2013c9 100644 --- a/examples/stm32l5/src/bin/button_exti.rs +++ b/examples/stm32l5/src/bin/button_exti.rs @@ -3,16 +3,28 @@ use defmt::*; use embassy_executor::Spawner; -use embassy_stm32::exti::ExtiInput; +use embassy_stm32::bind_interrupts; +use embassy_stm32::exti::{self, ExtiInput}; use embassy_stm32::gpio::Pull; +use embassy_stm32::interrupt; use {defmt_rtt as _, panic_probe as _}; +bind_interrupts!( + pub struct Irqs{ + EXTI13 => exti::InterruptHandler; +}); + #[embassy_executor::main] async fn main(_spawner: Spawner) { let p = embassy_stm32::init(Default::default()); info!("Hello World!"); - let mut button = ExtiInput::new(p.PC13, p.EXTI13, Pull::Down); + let mut button = ExtiInput::new( + p.PC13, + p.EXTI13, + Pull::Down, + Irqs::as_any::>(), + ); info!("Press the USER button..."); diff --git a/examples/stm32n6/src/bin/blinky.rs b/examples/stm32n6/src/bin/blinky.rs index 018967f08..c72e45628 100644 --- a/examples/stm32n6/src/bin/blinky.rs +++ b/examples/stm32n6/src/bin/blinky.rs @@ -3,11 +3,18 @@ use defmt::*; use embassy_executor::Spawner; -use embassy_stm32::exti::ExtiInput; +use embassy_stm32::bind_interrupts; +use embassy_stm32::exti::{self, ExtiInput}; use embassy_stm32::gpio::{Level, Output, Pull, Speed}; +use embassy_stm32::interrupt; use embassy_time::Timer; use {defmt_rtt as _, panic_probe as _}; +bind_interrupts!( + pub struct Irqs{ + EXTI13 => exti::InterruptHandler; +}); + #[embassy_executor::task] async fn button_task(mut p: ExtiInput<'static>) { loop { @@ -22,7 +29,12 @@ async fn main(spawner: Spawner) { info!("Hello World!"); let mut led = Output::new(p.PG10, Level::High, Speed::Low); - let button = ExtiInput::new(p.PC13, p.EXTI13, Pull::Up); + let button = ExtiInput::new( + p.PC13, + p.EXTI13, + Pull::Up, + Irqs::as_any::>(), + ); spawner.spawn(button_task(button).unwrap()); diff --git a/examples/stm32u0/src/bin/button_exti.rs b/examples/stm32u0/src/bin/button_exti.rs index 34a08bbc6..dbc9e2f50 100644 --- a/examples/stm32u0/src/bin/button_exti.rs +++ b/examples/stm32u0/src/bin/button_exti.rs @@ -3,16 +3,28 @@ use defmt::*; use embassy_executor::Spawner; -use embassy_stm32::exti::ExtiInput; +use embassy_stm32::bind_interrupts; +use embassy_stm32::exti::{self, ExtiInput}; use embassy_stm32::gpio::Pull; +use embassy_stm32::interrupt; use {defmt_rtt as _, panic_probe as _}; +bind_interrupts!( + pub struct Irqs{ + EXTI4_15 => exti::InterruptHandler; +}); + #[embassy_executor::main] async fn main(_spawner: Spawner) { let p = embassy_stm32::init(Default::default()); info!("Hello World!"); - let mut button = ExtiInput::new(p.PC13, p.EXTI13, Pull::Up); + let mut button = ExtiInput::new( + p.PC13, + p.EXTI13, + Pull::Up, + Irqs::as_any::>(), + ); info!("Press the USER button..."); diff --git a/examples/stm32wb/src/bin/button_exti.rs b/examples/stm32wb/src/bin/button_exti.rs index 2871fd55f..2736b98f1 100644 --- a/examples/stm32wb/src/bin/button_exti.rs +++ b/examples/stm32wb/src/bin/button_exti.rs @@ -3,16 +3,28 @@ use defmt::*; use embassy_executor::Spawner; -use embassy_stm32::exti::ExtiInput; +use embassy_stm32::bind_interrupts; +use embassy_stm32::exti::{self, ExtiInput}; use embassy_stm32::gpio::Pull; +use embassy_stm32::interrupt; use {defmt_rtt as _, panic_probe as _}; +bind_interrupts!( + pub struct Irqs{ + EXTI4 => exti::InterruptHandler; +}); + #[embassy_executor::main] async fn main(_spawner: Spawner) { let p = embassy_stm32::init(Default::default()); info!("Hello World!"); - let mut button = ExtiInput::new(p.PC4, p.EXTI4, Pull::Up); + let mut button = ExtiInput::new( + p.PC4, + p.EXTI4, + Pull::Up, + Irqs::as_any::>(), + ); info!("Press the USER button..."); diff --git a/examples/stm32wba/src/bin/button_exti.rs b/examples/stm32wba/src/bin/button_exti.rs index 34a08bbc6..d4cf36fcc 100644 --- a/examples/stm32wba/src/bin/button_exti.rs +++ b/examples/stm32wba/src/bin/button_exti.rs @@ -3,16 +3,28 @@ use defmt::*; use embassy_executor::Spawner; -use embassy_stm32::exti::ExtiInput; +use embassy_stm32::bind_interrupts; +use embassy_stm32::exti::{self, ExtiInput}; use embassy_stm32::gpio::Pull; +use embassy_stm32::interrupt; use {defmt_rtt as _, panic_probe as _}; +bind_interrupts!( + pub struct Irqs{ + EXTI13 => exti::InterruptHandler; +}); + #[embassy_executor::main] async fn main(_spawner: Spawner) { let p = embassy_stm32::init(Default::default()); info!("Hello World!"); - let mut button = ExtiInput::new(p.PC13, p.EXTI13, Pull::Up); + let mut button = ExtiInput::new( + p.PC13, + p.EXTI13, + Pull::Up, + Irqs::as_any::>(), + ); info!("Press the USER button..."); diff --git a/examples/stm32wba6/src/bin/button_exti.rs b/examples/stm32wba6/src/bin/button_exti.rs index 34a08bbc6..d4cf36fcc 100644 --- a/examples/stm32wba6/src/bin/button_exti.rs +++ b/examples/stm32wba6/src/bin/button_exti.rs @@ -3,16 +3,28 @@ use defmt::*; use embassy_executor::Spawner; -use embassy_stm32::exti::ExtiInput; +use embassy_stm32::bind_interrupts; +use embassy_stm32::exti::{self, ExtiInput}; use embassy_stm32::gpio::Pull; +use embassy_stm32::interrupt; use {defmt_rtt as _, panic_probe as _}; +bind_interrupts!( + pub struct Irqs{ + EXTI13 => exti::InterruptHandler; +}); + #[embassy_executor::main] async fn main(_spawner: Spawner) { let p = embassy_stm32::init(Default::default()); info!("Hello World!"); - let mut button = ExtiInput::new(p.PC13, p.EXTI13, Pull::Up); + let mut button = ExtiInput::new( + p.PC13, + p.EXTI13, + Pull::Up, + Irqs::as_any::>(), + ); info!("Press the USER button..."); diff --git a/examples/stm32wl/src/bin/button_exti.rs b/examples/stm32wl/src/bin/button_exti.rs index 953b13bac..183f93f2f 100644 --- a/examples/stm32wl/src/bin/button_exti.rs +++ b/examples/stm32wl/src/bin/button_exti.rs @@ -6,10 +6,17 @@ use core::mem::MaybeUninit; use defmt::*; use embassy_executor::Spawner; use embassy_stm32::SharedData; -use embassy_stm32::exti::ExtiInput; +use embassy_stm32::bind_interrupts; +use embassy_stm32::exti::{self, ExtiInput}; use embassy_stm32::gpio::Pull; +use embassy_stm32::interrupt; use {defmt_rtt as _, panic_probe as _}; +bind_interrupts!( + pub struct Irqs{ + EXTI0 => exti::InterruptHandler; +}); + #[unsafe(link_section = ".shared_data")] static SHARED_DATA: MaybeUninit = MaybeUninit::uninit(); @@ -18,7 +25,12 @@ async fn main(_spawner: Spawner) { let p = embassy_stm32::init_primary(Default::default(), &SHARED_DATA); info!("Hello World!"); - let mut button = ExtiInput::new(p.PA0, p.EXTI0, Pull::Up); + let mut button = ExtiInput::new( + p.PA0, + p.EXTI0, + Pull::Up, + Irqs::as_any::>(), + ); info!("Press the USER button..."); diff --git a/examples/stm32wle5/src/bin/button_exti.rs b/examples/stm32wle5/src/bin/button_exti.rs index 878eca7d0..196afa330 100644 --- a/examples/stm32wle5/src/bin/button_exti.rs +++ b/examples/stm32wle5/src/bin/button_exti.rs @@ -5,12 +5,19 @@ use defmt::*; #[cfg(feature = "defmt-rtt")] use defmt_rtt as _; use embassy_executor::Spawner; -use embassy_stm32::exti::ExtiInput; +use embassy_stm32::bind_interrupts; +use embassy_stm32::exti::{self, ExtiInput}; use embassy_stm32::gpio::Pull; +use embassy_stm32::interrupt; use embassy_stm32::low_power; use panic_probe as _; use static_cell::StaticCell; +bind_interrupts!( + pub struct Irqs{ + EXTI0 => exti::InterruptHandler; +}); + #[embassy_executor::main(executor = "low_power::Executor")] async fn async_main(_spawner: Spawner) { let mut config = embassy_stm32::Config::default(); @@ -64,7 +71,12 @@ async fn async_main(_spawner: Spawner) { info!("Hello World!"); - let mut button = ExtiInput::new(p.PA0, p.EXTI0, Pull::Up); + let mut button = ExtiInput::new( + p.PA0, + p.EXTI0, + Pull::Up, + Irqs::as_any::>(), + ); info!("Press the USER button..."); -- cgit From c972b81a737e43e5580a76d6538caa625a39f829 Mon Sep 17 00:00:00 2001 From: Dion Dokter Date: Fri, 21 Nov 2025 23:47:23 +0100 Subject: Add ram read function and add docs --- embassy-stm32/src/lcd.rs | 55 ++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 53 insertions(+), 2 deletions(-) diff --git a/embassy-stm32/src/lcd.rs b/embassy-stm32/src/lcd.rs index d4e65a925..3a890e529 100644 --- a/embassy-stm32/src/lcd.rs +++ b/embassy-stm32/src/lcd.rs @@ -13,15 +13,27 @@ const NUM_SEGMENTS: u8 = 52; #[cfg(stm32wb)] const NUM_SEGMENTS: u8 = 44; +/// LCD configuration struct #[non_exhaustive] #[derive(Debug, Clone, Copy)] pub struct Config { + /// Enable the voltage output buffer for higher driving capability. + /// + /// The LCD driving capability is improved as buffers prevent the LCD capacitive loads from loading the resistor + /// bridge unacceptably and interfering with its voltage generation. pub use_voltage_output_buffer: bool, + /// Enable SEG pin remapping. SEG[31:28] multiplexed with SEG[43:40] pub use_segment_muxing: bool, + /// Bias selector pub bias: Bias, + /// Duty selector pub duty: Duty, + /// Internal or external voltage source pub voltage_source: VoltageSource, + /// The frequency used to update the LCD with. + /// Should be between ~30 and ~100. Lower is better for power consumption, but has lower visual fidelity. pub target_fps: Hertz, + /// LCD driver selector pub drive: Drive, } @@ -33,7 +45,7 @@ impl Default for Config { bias: Default::default(), duty: Default::default(), voltage_source: Default::default(), - target_fps: Hertz(30), + target_fps: Hertz(60), drive: Drive::Medium, } } @@ -139,7 +151,10 @@ pub struct Lcd<'d, T: Instance> { } impl<'d, T: Instance> Lcd<'d, T> { - /// Initialize the lcd driver + /// Initialize the lcd driver. + /// + /// The `pins` parameter must contain *all* segment and com pins that are connected to the LCD. + /// This is not further checked by this driver. Pins not routed to the LCD can be used for other purposes. pub fn new( _peripheral: Peri<'d, T>, config: Config, @@ -267,6 +282,13 @@ impl<'d, T: Instance> Lcd<'d, T> { .modify(|w: &mut stm32_metapac::lcd::regs::Fcr| w.set_dead(value)); } + /// Write data into the display RAM. This overwrites the data already in it for the specified com index. + /// + /// The `com_index` value determines which part of the RAM is written to. + /// The `segments` value is a bitmap where each bit represents whether a pixel is turned on or off. + /// + /// This function waits last update request to be finished, but does not submit the buffer to the LCD with a new request. + /// Submission has to be done manually using [Self::submit_frame]. pub fn write_com_segments(&mut self, com_index: u8, segments: u64) { while T::regs().sr().read().udr() {} @@ -290,6 +312,28 @@ impl<'d, T: Instance> Lcd<'d, T> { .write_value(((segments >> 32) & 0xFFFF_FFFF) as u32); } + /// Read the data from the display RAM. + /// + /// The `com_index` value determines which part of the RAM is read from. + /// + /// This function waits for the last update request to be finished. + pub fn read_com_segments(&self, com_index: u8) -> u64 { + while T::regs().sr().read().udr() {} + + assert!( + com_index < self.duty.num_com_pins(), + "Com index cannot be higher than number of configured com pins (through the Duty setting in the config)" + ); + + let low = T::regs().ram_com(com_index as usize).low().read(); + let high = T::regs().ram_com(com_index as usize).high().read(); + + ((high as u64) << 32) | low as u64 + } + + /// Submit the current RAM data to the LCD. + /// + /// This function waits until the RAM is writable, but does not wait for the frame to be drawn. pub fn submit_frame(&mut self) { while T::regs().sr().read().udr() {} // Clear the update done flag @@ -298,6 +342,7 @@ impl<'d, T: Instance> Lcd<'d, T> { T::regs().sr().write(|w| w.set_udr(true)); } + /// Get the number of segments that are supported on this LCD pub fn num_segments(&self) -> u8 { match self.duty { Duty::Eigth => NUM_SEGMENTS - 4, // With 8 coms, 4 of the segment pins turn into com pins @@ -305,10 +350,13 @@ impl<'d, T: Instance> Lcd<'d, T> { } } + /// Get the pixel mask for the current LCD setup. + /// This is a mask of all bits that are allowed to be set in the [Self::write_com_segments] function. pub fn segment_pixel_mask(&self) -> u64 { (1 << self.num_segments()) - 1 } + /// Get the number of COM pins that were configured through the Drive config pub fn num_com_pins(&self) -> u8 { self.duty.num_com_pins() } @@ -322,6 +370,8 @@ impl<'d, T: Instance> Drop for Lcd<'d, T> { } } +/// A type-erased pin that can be configured as an LCD pin. +/// This is used for passing pins to the new function in the array. pub struct LcdPin<'d, T: Instance> { pin: Peri<'d, AnyPin>, af_num: u8, @@ -335,6 +385,7 @@ impl<'d, T: Instance, Pin: SegComPin> From> for LcdPin<'d, T> { } impl<'d, T: Instance> LcdPin<'d, T> { + /// Construct an LCD pin from any pin that supports it pub fn new(pin: Peri<'d, impl SegComPin>) -> Self { let af = pin.af_num(); -- cgit From 1479fbbee76b52e04bf658244fc535e462e17637 Mon Sep 17 00:00:00 2001 From: Dion Dokter Date: Sat, 22 Nov 2025 00:37:19 +0100 Subject: Restructure build script and pin traits a little bit --- embassy-stm32/build.rs | 16 ++++++------ embassy-stm32/src/lcd.rs | 33 ++++++++++++++++-------- examples/stm32u0/src/bin/lcd.rs | 56 ++++++++++++++++++++--------------------- 3 files changed, 58 insertions(+), 47 deletions(-) diff --git a/embassy-stm32/build.rs b/embassy-stm32/build.rs index 8cddb68f9..fa493da84 100644 --- a/embassy-stm32/build.rs +++ b/embassy-stm32/build.rs @@ -1352,28 +1352,28 @@ fn main() { (("tsc", "G8_IO2"), quote!(crate::tsc::G8IO2Pin)), (("tsc", "G8_IO3"), quote!(crate::tsc::G8IO3Pin)), (("tsc", "G8_IO4"), quote!(crate::tsc::G8IO4Pin)), - (("lcd", "SEG"), quote!(crate::lcd::SegComPin)), - (("lcd", "COM"), quote!(crate::lcd::SegComPin)), + (("lcd", "SEG"), quote!(crate::lcd::SegPin)), + (("lcd", "COM"), quote!(crate::lcd::ComPin)), + (("lcd", "VLCD"), quote!(crate::lcd::VlcdPin)), (("dac", "OUT1"), quote!(crate::dac::DacPin)), (("dac", "OUT2"), quote!(crate::dac::DacPin)), ].into(); - let mut seen_lcd_pins = HashSet::new(); - for p in METADATA.peripherals { if let Some(regs) = &p.registers { let mut adc_pairs: BTreeMap, Option)> = BTreeMap::new(); + let mut seen_lcd_seg_pins = HashSet::new(); for pin in p.pins { let mut key = (regs.kind, pin.signal); - // LCD is special + // LCD is special. There are so many pins! if regs.kind == "lcd" { key.1 = pin.signal.trim_end_matches(char::is_numeric); - // Some lcd pins have multiple lcd functions - // Dedup so they don't get the trait implemented twice - if !seen_lcd_pins.insert(pin.pin) { + if key.1 == "SEG" && !seen_lcd_seg_pins.insert(pin.pin) { + // LCD has SEG pins multiplexed in the peripheral + // This means we can see them twice. We need to skip those so we're not impl'ing the trait twice continue; } } diff --git a/embassy-stm32/src/lcd.rs b/embassy-stm32/src/lcd.rs index 3a890e529..66a9386b7 100644 --- a/embassy-stm32/src/lcd.rs +++ b/embassy-stm32/src/lcd.rs @@ -168,6 +168,12 @@ impl<'d, T: Instance> Lcd<'d, T> { AfType::output(crate::gpio::OutputType::PushPull, crate::gpio::Speed::VeryHigh), ); + assert_eq!( + pins.iter().filter(|pin| !pin.is_seg).count(), + config.duty.num_com_pins() as usize, + "The number of provided COM pins is not the same as the duty configures" + ); + // Set the pins for pin in pins { pin.pin.set_as_af( @@ -375,23 +381,31 @@ impl<'d, T: Instance> Drop for Lcd<'d, T> { pub struct LcdPin<'d, T: Instance> { pin: Peri<'d, AnyPin>, af_num: u8, + is_seg: bool, _phantom: PhantomData, } -impl<'d, T: Instance, Pin: SegComPin> From> for LcdPin<'d, T> { - fn from(value: Peri<'d, Pin>) -> Self { - Self::new(value) +impl<'d, T: Instance> LcdPin<'d, T> { + /// Construct an LCD pin from any pin that supports it + pub fn new_seg(pin: Peri<'d, impl SegPin>) -> Self { + let af = pin.af_num(); + + Self { + pin: pin.into(), + af_num: af, + is_seg: true, + _phantom: PhantomData, + } } -} -impl<'d, T: Instance> LcdPin<'d, T> { /// Construct an LCD pin from any pin that supports it - pub fn new(pin: Peri<'d, impl SegComPin>) -> Self { + pub fn new_com(pin: Peri<'d, impl ComPin>) -> Self { let af = pin.af_num(); Self { pin: pin.into(), af_num: af, + is_seg: false, _phantom: PhantomData, } } @@ -405,13 +419,10 @@ trait SealedInstance: crate::rcc::SealedRccPeripheral + PeripheralType { #[allow(private_bounds)] pub trait Instance: SealedInstance + RccPeripheral + 'static {} -pin_trait!(SegComPin, Instance); +pin_trait!(SegPin, Instance); +pin_trait!(ComPin, Instance); pin_trait!(VlcdPin, Instance); -// TODO: pull into build.rs, but the metapack doesn't have this info -pin_trait_impl!(crate::lcd::VlcdPin, LCD, PC3, 11); -pin_trait_impl!(crate::lcd::VlcdPin, LCD, PB2, 11); - foreach_peripheral!( (lcd, $inst:ident) => { impl crate::lcd::SealedInstance for peripherals::$inst { diff --git a/examples/stm32u0/src/bin/lcd.rs b/examples/stm32u0/src/bin/lcd.rs index f401b1dcd..39db5c9c8 100644 --- a/examples/stm32u0/src/bin/lcd.rs +++ b/examples/stm32u0/src/bin/lcd.rs @@ -43,34 +43,34 @@ async fn main(_spawner: Spawner) { config, p.PC3, [ - LcdPin::from(p.PA8), - LcdPin::from(p.PA9), - LcdPin::from(p.PA10), - LcdPin::from(p.PB1), - LcdPin::from(p.PB9), - LcdPin::from(p.PB11), - LcdPin::from(p.PB14), - LcdPin::from(p.PB15), - LcdPin::from(p.PC4), - LcdPin::from(p.PC5), - LcdPin::from(p.PC6), - LcdPin::from(p.PC8), - LcdPin::from(p.PC9), - LcdPin::from(p.PC10), - LcdPin::from(p.PC11), - LcdPin::from(p.PD8), - LcdPin::from(p.PD9), - LcdPin::from(p.PD12), - LcdPin::from(p.PD13), - LcdPin::from(p.PD0), - LcdPin::from(p.PD1), - LcdPin::from(p.PD3), - LcdPin::from(p.PD4), - LcdPin::from(p.PD5), - LcdPin::from(p.PD6), - LcdPin::from(p.PE7), - LcdPin::from(p.PE8), - LcdPin::from(p.PE9), + LcdPin::new_com(p.PA8), + LcdPin::new_com(p.PA9), + LcdPin::new_com(p.PA10), + LcdPin::new_seg(p.PB1), + LcdPin::new_com(p.PB9), + LcdPin::new_seg(p.PB11), + LcdPin::new_seg(p.PB14), + LcdPin::new_seg(p.PB15), + LcdPin::new_seg(p.PC4), + LcdPin::new_seg(p.PC5), + LcdPin::new_seg(p.PC6), + LcdPin::new_seg(p.PC8), + LcdPin::new_seg(p.PC9), + LcdPin::new_seg(p.PC10), + LcdPin::new_seg(p.PC11), + LcdPin::new_seg(p.PD8), + LcdPin::new_seg(p.PD9), + LcdPin::new_seg(p.PD12), + LcdPin::new_seg(p.PD13), + LcdPin::new_seg(p.PD0), + LcdPin::new_seg(p.PD1), + LcdPin::new_seg(p.PD3), + LcdPin::new_seg(p.PD4), + LcdPin::new_seg(p.PD5), + LcdPin::new_seg(p.PD6), + LcdPin::new_seg(p.PE7), + LcdPin::new_seg(p.PE8), + LcdPin::new_seg(p.PE9), ], ); -- cgit From 11a5cd2c753fc39ec9fcf22c805ed7769ed6e0ec Mon Sep 17 00:00:00 2001 From: Dion Dokter Date: Sat, 22 Nov 2025 00:40:29 +0100 Subject: fmt + clippy + changelog --- embassy-stm32/CHANGELOG.md | 1 + embassy-stm32/src/lib.rs | 2 +- examples/stm32u0/src/bin/lcd.rs | 6 ++++++ 3 files changed, 8 insertions(+), 1 deletion(-) diff --git a/embassy-stm32/CHANGELOG.md b/embassy-stm32/CHANGELOG.md index d2f675dbc..055a54c07 100644 --- a/embassy-stm32/CHANGELOG.md +++ b/embassy-stm32/CHANGELOG.md @@ -71,6 +71,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - fix: fixing channel numbers on vbat and vddcore for adc on adc - adc: adding disable to vbat - feat: stm32/flash: add async support for h7 family +- feat: stm32/lcd: added implementation ## 0.4.0 - 2025-08-26 diff --git a/embassy-stm32/src/lib.rs b/embassy-stm32/src/lib.rs index 1f0449cf3..9e6ba1f34 100644 --- a/embassy-stm32/src/lib.rs +++ b/embassy-stm32/src/lib.rs @@ -227,7 +227,7 @@ macro_rules! bind_interrupts { } // Reexports -pub use _generated::{peripherals, Peripherals}; +pub use _generated::{Peripherals, peripherals}; pub use embassy_hal_internal::{Peri, PeripheralType}; #[cfg(feature = "unstable-pac")] pub use stm32_metapac as pac; diff --git a/examples/stm32u0/src/bin/lcd.rs b/examples/stm32u0/src/bin/lcd.rs index 39db5c9c8..c3f489ea9 100644 --- a/examples/stm32u0/src/bin/lcd.rs +++ b/examples/stm32u0/src/bin/lcd.rs @@ -351,6 +351,12 @@ impl DisplayBuffer { } } +impl Default for DisplayBuffer { + fn default() -> Self { + Self::new() + } +} + #[derive(Debug, Clone, Copy)] enum CharSegment { /// North -- cgit From 8c9a6521e813f7ab9cfe787bd46a583c8173bac2 Mon Sep 17 00:00:00 2001 From: Dion Dokter Date: Sat, 22 Nov 2025 00:46:18 +0100 Subject: Minimize cargo toml + revert probe-rs config --- examples/stm32u0/.cargo/config.toml | 4 ++-- examples/stm32u0/Cargo.toml | 4 +--- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/examples/stm32u0/.cargo/config.toml b/examples/stm32u0/.cargo/config.toml index 06eed6c8f..e9212cacb 100644 --- a/examples/stm32u0/.cargo/config.toml +++ b/examples/stm32u0/.cargo/config.toml @@ -1,6 +1,6 @@ [target.'cfg(all(target_arch = "arm", target_os = "none"))'] -# replace stm32u083rctx with your chip as listed in `probe-rs chip list` -runner = "probe-rs run --chip stm32u083rctx --catch-hardfault" +# replace stm32u083mctx with your chip as listed in `probe-rs chip list` +runner = "probe-rs run --chip stm32u083mctx" [build] target = "thumbv6m-none-eabi" diff --git a/examples/stm32u0/Cargo.toml b/examples/stm32u0/Cargo.toml index 42d349cda..8cc894cb3 100644 --- a/examples/stm32u0/Cargo.toml +++ b/examples/stm32u0/Cargo.toml @@ -6,7 +6,7 @@ license = "MIT OR Apache-2.0" publish = false [dependencies] -# Change stm32u083rc to your chip name, if necessary. +# Change stm32u083mc to your chip name, if necessary. embassy-stm32 = { version = "0.4.0", path = "../../embassy-stm32", features = [ "defmt", "time-driver-any", "stm32u083mc", "memory-x", "unstable-pac", "exti", "chrono"] } 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"] } @@ -19,9 +19,7 @@ defmt-rtt = "1.0.0" cortex-m = { version = "0.7.6", features = ["critical-section-single-core"] } cortex-m-rt = "0.7.0" -embedded-hal = "0.2.6" panic-probe = { version = "1.0.0", features = ["print-defmt"] } -heapless = { version = "0.8", default-features = false } micromath = "2.0.0" chrono = { version = "0.4.38", default-features = false } -- cgit From 992ab2ec63d4ca41045d52f50be487a001023396 Mon Sep 17 00:00:00 2001 From: Dion Dokter Date: Sat, 22 Nov 2025 01:47:54 +0100 Subject: Add L0 family --- embassy-stm32/src/lcd.rs | 4 +++- examples/stm32u0/src/bin/lcd.rs | 8 +++----- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/embassy-stm32/src/lcd.rs b/embassy-stm32/src/lcd.rs index 66a9386b7..dc1989168 100644 --- a/embassy-stm32/src/lcd.rs +++ b/embassy-stm32/src/lcd.rs @@ -8,10 +8,12 @@ use crate::peripherals; use crate::rcc::{self, RccPeripheral}; use crate::time::Hertz; -#[cfg(stm32u0)] +#[cfg(any(stm32u0, stm32l073, stm32l083))] const NUM_SEGMENTS: u8 = 52; #[cfg(stm32wb)] const NUM_SEGMENTS: u8 = 44; +#[cfg(any(stm32l053, stm32l063))] +const NUM_SEGMENTS: u8 = 32; /// LCD configuration struct #[non_exhaustive] diff --git a/examples/stm32u0/src/bin/lcd.rs b/examples/stm32u0/src/bin/lcd.rs index c3f489ea9..6e4378074 100644 --- a/examples/stm32u0/src/bin/lcd.rs +++ b/examples/stm32u0/src/bin/lcd.rs @@ -3,11 +3,9 @@ use defmt::*; use embassy_executor::Spawner; -use embassy_stm32::{ - lcd::{Bias, Config, Duty, Lcd, LcdPin}, - peripherals::LCD, - time::Hertz, -}; +use embassy_stm32::lcd::{Bias, Config, Duty, Lcd, LcdPin}; +use embassy_stm32::peripherals::LCD; +use embassy_stm32::time::Hertz; use embassy_time::Duration; use {defmt_rtt as _, panic_probe as _}; -- cgit From 5d3d485a73cd1b1cff4077914ca1103e0cf6b84b Mon Sep 17 00:00:00 2001 From: xoviat Date: Fri, 21 Nov 2025 18:55:27 -0600 Subject: low power: store stop mode for dma channels --- embassy-stm32/CHANGELOG.md | 1 + embassy-stm32/build.rs | 15 ++++++- embassy-stm32/src/dma/dma_bdma.rs | 16 ++++--- embassy-stm32/src/dma/gpdma/mod.rs | 13 ++++-- embassy-stm32/src/dma/gpdma/ringbuffered.rs | 10 ++--- embassy-stm32/src/dma/mod.rs | 66 ++++++++++++++++++++++++++++- embassy-stm32/src/low_power.rs | 42 +++++------------- embassy-stm32/src/rcc/mod.rs | 59 +++++++++++++++++--------- 8 files changed, 150 insertions(+), 72 deletions(-) diff --git a/embassy-stm32/CHANGELOG.md b/embassy-stm32/CHANGELOG.md index 87a8ef7c9..6e1381925 100644 --- a/embassy-stm32/CHANGELOG.md +++ b/embassy-stm32/CHANGELOG.md @@ -7,6 +7,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## Unreleased - ReleaseDate +- change: low power: store stop mode for dma channels - fix: Fixed ADC4 enable() for WBA - feat: allow use of anyadcchannel for adc4 - fix: fix incorrect logic for buffered usart transmission complete. diff --git a/embassy-stm32/build.rs b/embassy-stm32/build.rs index 8cbd38e10..3277ba440 100644 --- a/embassy-stm32/build.rs +++ b/embassy-stm32/build.rs @@ -1947,6 +1947,19 @@ fn main() { continue; } + let stop_mode = METADATA + .peripherals + .iter() + .find(|p| p.name == ch.dma) + .map(|p| p.rcc.as_ref().map(|rcc| rcc.stop_mode.clone()).unwrap_or_default()) + .unwrap_or_default(); + + let stop_mode = match stop_mode { + StopMode::Standby => quote! { Standby }, + StopMode::Stop2 => quote! { Stop2 }, + StopMode::Stop1 => quote! { Stop1 }, + }; + let name = format_ident!("{}", ch.name); let idx = ch_idx as u8; #[cfg(feature = "_dual-core")] @@ -1959,7 +1972,7 @@ fn main() { quote!(crate::pac::Interrupt::#irq_name) }; - g.extend(quote!(dma_channel_impl!(#name, #idx);)); + g.extend(quote!(dma_channel_impl!(#name, #idx, #stop_mode);)); let dma = format_ident!("{}", ch.dma); let ch_num = ch.channel as usize; diff --git a/embassy-stm32/src/dma/dma_bdma.rs b/embassy-stm32/src/dma/dma_bdma.rs index 90dbf4f09..b46ae2813 100644 --- a/embassy-stm32/src/dma/dma_bdma.rs +++ b/embassy-stm32/src/dma/dma_bdma.rs @@ -8,7 +8,7 @@ use embassy_sync::waitqueue::AtomicWaker; use super::ringbuffer::{DmaCtrl, Error, ReadableDmaRingBuffer, WritableDmaRingBuffer}; use super::word::{Word, WordSize}; -use super::{AnyChannel, Channel, Dir, Request, STATE}; +use super::{AnyChannel, BusyChannel, Channel, Dir, Request, STATE}; use crate::interrupt::typelevel::Interrupt; use crate::{interrupt, pac}; @@ -602,7 +602,7 @@ impl AnyChannel { /// DMA transfer. #[must_use = "futures do nothing unless you `.await` or poll them"] pub struct Transfer<'a> { - channel: Peri<'a, AnyChannel>, + channel: BusyChannel<'a>, } impl<'a> Transfer<'a> { @@ -713,7 +713,9 @@ impl<'a> Transfer<'a> { _request, dir, peri_addr, mem_addr, mem_len, incr_mem, mem_size, peri_size, options, ); channel.start(); - Self { channel } + Self { + channel: BusyChannel::new(channel), + } } /// Request the transfer to pause, keeping the existing configuration for this channel. @@ -816,7 +818,7 @@ impl<'a> DmaCtrl for DmaCtrlImpl<'a> { /// Ringbuffer for receiving data using DMA circular mode. pub struct ReadableRingBuffer<'a, W: Word> { - channel: Peri<'a, AnyChannel>, + channel: BusyChannel<'a>, ringbuf: ReadableDmaRingBuffer<'a, W>, } @@ -853,7 +855,7 @@ impl<'a, W: Word> ReadableRingBuffer<'a, W> { ); Self { - channel, + channel: BusyChannel::new(channel), ringbuf: ReadableDmaRingBuffer::new(buffer), } } @@ -972,7 +974,7 @@ impl<'a, W: Word> Drop for ReadableRingBuffer<'a, W> { /// Ringbuffer for writing data using DMA circular mode. pub struct WritableRingBuffer<'a, W: Word> { - channel: Peri<'a, AnyChannel>, + channel: BusyChannel<'a>, ringbuf: WritableDmaRingBuffer<'a, W>, } @@ -1009,7 +1011,7 @@ impl<'a, W: Word> WritableRingBuffer<'a, W> { ); Self { - channel, + channel: BusyChannel::new(channel), ringbuf: WritableDmaRingBuffer::new(buffer), } } diff --git a/embassy-stm32/src/dma/gpdma/mod.rs b/embassy-stm32/src/dma/gpdma/mod.rs index 106558d20..383c74a78 100644 --- a/embassy-stm32/src/dma/gpdma/mod.rs +++ b/embassy-stm32/src/dma/gpdma/mod.rs @@ -11,6 +11,7 @@ use linked_list::Table; use super::word::{Word, WordSize}; use super::{AnyChannel, Channel, Dir, Request, STATE}; +use crate::dma::BusyChannel; use crate::interrupt::typelevel::Interrupt; use crate::pac; use crate::pac::gpdma::vals; @@ -408,7 +409,7 @@ impl AnyChannel { /// Linked-list DMA transfer. #[must_use = "futures do nothing unless you `.await` or poll them"] pub struct LinkedListTransfer<'a, const ITEM_COUNT: usize> { - channel: Peri<'a, AnyChannel>, + channel: BusyChannel<'a>, } impl<'a, const ITEM_COUNT: usize> LinkedListTransfer<'a, ITEM_COUNT> { @@ -429,7 +430,9 @@ impl<'a, const ITEM_COUNT: usize> LinkedListTransfer<'a, ITEM_COUNT> { channel.configure_linked_list(&table, options); channel.start(); - Self { channel } + Self { + channel: BusyChannel::new(channel), + } } /// Request the transfer to pause, keeping the existing configuration for this channel. @@ -505,7 +508,7 @@ impl<'a, const ITEM_COUNT: usize> Future for LinkedListTransfer<'a, ITEM_COUNT> /// DMA transfer. #[must_use = "futures do nothing unless you `.await` or poll them"] pub struct Transfer<'a> { - channel: Peri<'a, AnyChannel>, + channel: BusyChannel<'a>, } impl<'a> Transfer<'a> { @@ -625,7 +628,9 @@ impl<'a> Transfer<'a> { ); channel.start(); - Self { channel } + Self { + channel: BusyChannel::new(channel), + } } /// Request the transfer to pause, keeping the existing configuration for this channel. diff --git a/embassy-stm32/src/dma/gpdma/ringbuffered.rs b/embassy-stm32/src/dma/gpdma/ringbuffered.rs index 94c597e0d..54e4d5f71 100644 --- a/embassy-stm32/src/dma/gpdma/ringbuffered.rs +++ b/embassy-stm32/src/dma/gpdma/ringbuffered.rs @@ -12,7 +12,7 @@ use super::{AnyChannel, STATE, TransferOptions}; use crate::dma::gpdma::linked_list::{RunMode, Table}; use crate::dma::ringbuffer::{DmaCtrl, Error, ReadableDmaRingBuffer, WritableDmaRingBuffer}; use crate::dma::word::Word; -use crate::dma::{Channel, Dir, Request}; +use crate::dma::{BusyChannel, Channel, Dir, Request}; struct DmaCtrlImpl<'a>(Peri<'a, AnyChannel>); @@ -49,7 +49,7 @@ impl<'a> DmaCtrl for DmaCtrlImpl<'a> { /// Ringbuffer for receiving data using GPDMA linked-list mode. pub struct ReadableRingBuffer<'a, W: Word> { - channel: Peri<'a, AnyChannel>, + channel: BusyChannel<'a>, ringbuf: ReadableDmaRingBuffer<'a, W>, table: Table<2>, options: TransferOptions, @@ -70,7 +70,7 @@ impl<'a, W: Word> ReadableRingBuffer<'a, W> { let table = Table::<2>::new_ping_pong::(request, peri_addr, buffer, Dir::PeripheralToMemory); Self { - channel, + channel: BusyChannel::new(channel), ringbuf: ReadableDmaRingBuffer::new(buffer), table, options, @@ -189,7 +189,7 @@ impl<'a, W: Word> Drop for ReadableRingBuffer<'a, W> { /// Ringbuffer for writing data using GPDMA linked-list mode. pub struct WritableRingBuffer<'a, W: Word> { - channel: Peri<'a, AnyChannel>, + channel: BusyChannel<'a>, ringbuf: WritableDmaRingBuffer<'a, W>, table: Table<2>, options: TransferOptions, @@ -210,7 +210,7 @@ impl<'a, W: Word> WritableRingBuffer<'a, W> { let table = Table::<2>::new_ping_pong::(request, peri_addr, buffer, Dir::MemoryToPeripheral); Self { - channel, + channel: BusyChannel::new(channel), ringbuf: WritableDmaRingBuffer::new(buffer), table, options, diff --git a/embassy-stm32/src/dma/mod.rs b/embassy-stm32/src/dma/mod.rs index de7a2c175..4becc2d87 100644 --- a/embassy-stm32/src/dma/mod.rs +++ b/embassy-stm32/src/dma/mod.rs @@ -3,11 +3,14 @@ #[cfg(any(bdma, dma))] mod dma_bdma; +use core::ops; + #[cfg(any(bdma, dma))] pub use dma_bdma::*; #[cfg(gpdma)] pub(crate) mod gpdma; +use embassy_hal_internal::Peri; #[cfg(gpdma)] pub use gpdma::ringbuffered::*; #[cfg(gpdma)] @@ -48,6 +51,8 @@ pub type Request = (); pub(crate) trait SealedChannel { #[cfg(not(stm32n6))] fn id(&self) -> u8; + #[cfg(feature = "low-power")] + fn stop_mode(&self) -> crate::rcc::StopMode; } #[cfg(not(stm32n6))] @@ -62,15 +67,25 @@ pub trait Channel: SealedChannel + PeripheralType + Into + 'static { #[cfg(not(stm32n6))] macro_rules! dma_channel_impl { - ($channel_peri:ident, $index:expr) => { + ($channel_peri:ident, $index:expr, $stop_mode:ident) => { impl crate::dma::SealedChannel for crate::peripherals::$channel_peri { fn id(&self) -> u8 { $index } + + #[cfg(feature = "low-power")] + fn stop_mode(&self) -> crate::rcc::StopMode { + crate::rcc::StopMode::$stop_mode + } } impl crate::dma::ChannelInterrupt for crate::peripherals::$channel_peri { unsafe fn on_irq() { - crate::dma::AnyChannel { id: $index }.on_irq(); + crate::dma::AnyChannel { + id: $index, + #[cfg(feature = "low-power")] + stop_mode: crate::rcc::StopMode::$stop_mode, + } + .on_irq(); } } @@ -80,15 +95,57 @@ macro_rules! dma_channel_impl { fn from(val: crate::peripherals::$channel_peri) -> Self { Self { id: crate::dma::SealedChannel::id(&val), + #[cfg(feature = "low-power")] + stop_mode: crate::dma::SealedChannel::stop_mode(&val), } } } }; } +pub(crate) struct BusyChannel<'a> { + channel: Peri<'a, AnyChannel>, +} + +impl<'a> BusyChannel<'a> { + pub fn new(channel: Peri<'a, AnyChannel>) -> Self { + #[cfg(feature = "low-power")] + critical_section::with(|cs| { + crate::rcc::increment_stop_refcount(cs, channel.stop_mode); + }); + + Self { channel } + } +} + +impl<'a> Drop for BusyChannel<'a> { + fn drop(&mut self) { + #[cfg(feature = "low-power")] + critical_section::with(|cs| { + crate::rcc::decrement_stop_refcount(cs, self.stop_mode); + }); + } +} + +impl<'a> ops::Deref for BusyChannel<'a> { + type Target = Peri<'a, AnyChannel>; + + fn deref(&self) -> &Self::Target { + &self.channel + } +} + +impl<'a> ops::DerefMut for BusyChannel<'a> { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.channel + } +} + /// Type-erased DMA channel. pub struct AnyChannel { pub(crate) id: u8, + #[cfg(feature = "low-power")] + pub(crate) stop_mode: crate::rcc::StopMode, } impl_peripheral!(AnyChannel); @@ -103,6 +160,11 @@ impl SealedChannel for AnyChannel { fn id(&self) -> u8 { self.id } + + #[cfg(feature = "low-power")] + fn stop_mode(&self) -> crate::rcc::StopMode { + self.stop_mode + } } impl Channel for AnyChannel {} diff --git a/embassy-stm32/src/low_power.rs b/embassy-stm32/src/low_power.rs index 9de49c867..bd8290da0 100644 --- a/embassy-stm32/src/low_power.rs +++ b/embassy-stm32/src/low_power.rs @@ -50,7 +50,8 @@ use critical_section::CriticalSection; use embassy_executor::*; use crate::interrupt; -use crate::rcc::{RCC_CONFIG, REFCOUNT_STOP1, REFCOUNT_STOP2}; +pub use crate::rcc::StopMode; +use crate::rcc::{RCC_CONFIG, REFCOUNT_STOP1, REFCOUNT_STOP2, decrement_stop_refcount, increment_stop_refcount}; use crate::time_driver::get_driver; const THREAD_PENDER: usize = usize::MAX; @@ -73,15 +74,8 @@ impl DeviceBusy { /// Create a new DeviceBusy. pub fn new(stop_mode: StopMode) -> Self { - critical_section::with(|_| unsafe { - match stop_mode { - StopMode::Stop1 => { - crate::rcc::REFCOUNT_STOP1 += 1; - } - StopMode::Stop2 => { - crate::rcc::REFCOUNT_STOP2 += 1; - } - } + critical_section::with(|cs| { + increment_stop_refcount(cs, stop_mode); }); Self(stop_mode) @@ -90,15 +84,8 @@ impl DeviceBusy { impl Drop for DeviceBusy { fn drop(&mut self) { - critical_section::with(|_| unsafe { - match self.0 { - StopMode::Stop1 => { - crate::rcc::REFCOUNT_STOP1 -= 1; - } - StopMode::Stop2 => { - crate::rcc::REFCOUNT_STOP2 -= 1; - } - } + critical_section::with(|cs| { + decrement_stop_refcount(cs, self.0); }); } } @@ -131,22 +118,12 @@ foreach_interrupt! { /// prevents entering the given stop mode. pub fn stop_ready(stop_mode: StopMode) -> bool { critical_section::with(|cs| match Executor::stop_mode(cs) { - Some(StopMode::Stop2) => true, + Some(StopMode::Standby | StopMode::Stop2) => true, Some(StopMode::Stop1) => stop_mode == StopMode::Stop1, None => false, }) } -/// Available Stop modes. -#[non_exhaustive] -#[derive(PartialEq)] -pub enum StopMode { - /// STOP 1 - Stop1, - /// STOP 2 - Stop2, -} - #[cfg(any(stm32l4, stm32l5, stm32u5, stm32wba, stm32wb, stm32wlex, stm32u0))] use crate::pac::pwr::vals::Lpms; @@ -156,9 +133,9 @@ impl Into for StopMode { match self { StopMode::Stop1 => Lpms::STOP1, #[cfg(not(any(stm32wb, stm32wba)))] - StopMode::Stop2 => Lpms::STOP2, + StopMode::Standby | StopMode::Stop2 => Lpms::STOP2, #[cfg(any(stm32wb, stm32wba))] - StopMode::Stop2 => Lpms::STOP1, // TODO: WBA has no STOP2? + StopMode::Standby | StopMode::Stop2 => Lpms::STOP1, // TODO: WBA has no STOP2? } } } @@ -230,6 +207,7 @@ impl Executor { } fn stop_mode(_cs: CriticalSection) -> Option { + // We cannot enter standby because we will lose program state. if unsafe { REFCOUNT_STOP2 == 0 && REFCOUNT_STOP1 == 0 } { trace!("low power: stop 2"); Some(StopMode::Stop2) diff --git a/embassy-stm32/src/rcc/mod.rs b/embassy-stm32/src/rcc/mod.rs index 66ee06e17..85434fa83 100644 --- a/embassy-stm32/src/rcc/mod.rs +++ b/embassy-stm32/src/rcc/mod.rs @@ -111,6 +111,32 @@ pub fn clocks<'a>(_rcc: &'a crate::Peri<'a, crate::peripherals::RCC>) -> &'a Clo unsafe { get_freqs() } } +#[cfg(feature = "low-power")] +pub(crate) fn increment_stop_refcount(_cs: CriticalSection, stop_mode: StopMode) { + match stop_mode { + StopMode::Standby => {} + StopMode::Stop2 => unsafe { + REFCOUNT_STOP2 += 1; + }, + StopMode::Stop1 => unsafe { + REFCOUNT_STOP1 += 1; + }, + } +} + +#[cfg(feature = "low-power")] +pub(crate) fn decrement_stop_refcount(_cs: CriticalSection, stop_mode: StopMode) { + match stop_mode { + StopMode::Standby => {} + StopMode::Stop2 => unsafe { + REFCOUNT_STOP2 -= 1; + }, + StopMode::Stop1 => unsafe { + REFCOUNT_STOP1 -= 1; + }, + } +} + pub(crate) trait SealedRccPeripheral { fn frequency() -> Hertz; #[allow(dead_code)] @@ -141,12 +167,19 @@ pub(crate) struct RccInfo { stop_mode: StopMode, } +/// Specifies a limit for the stop mode of the peripheral or the stop mode to be entered. +/// E.g. if `StopMode::Stop1` is selected, the peripheral prevents the chip from entering Stop1 mode. #[cfg(feature = "low-power")] #[allow(dead_code)] -pub(crate) enum StopMode { - Standby, - Stop2, +#[derive(Debug, Clone, Copy, PartialEq, Default)] +pub enum StopMode { + #[default] + /// Peripheral prevents chip from entering Stop1 or executor will enter Stop1 Stop1, + /// Peripheral prevents chip from entering Stop2 or executor will enter Stop2 + Stop2, + /// Peripheral does not prevent chip from entering Stop + Standby, } impl RccInfo { @@ -202,15 +235,7 @@ impl RccInfo { } #[cfg(feature = "low-power")] - match self.stop_mode { - StopMode::Standby => {} - StopMode::Stop2 => unsafe { - REFCOUNT_STOP2 += 1; - }, - StopMode::Stop1 => unsafe { - REFCOUNT_STOP1 += 1; - }, - } + increment_stop_refcount(_cs, self.stop_mode); // set the xxxRST bit let reset_ptr = self.reset_ptr(); @@ -268,15 +293,7 @@ impl RccInfo { } #[cfg(feature = "low-power")] - match self.stop_mode { - StopMode::Standby => {} - StopMode::Stop2 => unsafe { - REFCOUNT_STOP2 -= 1; - }, - StopMode::Stop1 => unsafe { - REFCOUNT_STOP1 -= 1; - }, - } + decrement_stop_refcount(_cs, self.stop_mode); // clear the xxxEN bit let enable_ptr = self.enable_ptr(); -- cgit From 54d57bc72f0e2b3eef0fa92d0b730ed6efd7bcaa Mon Sep 17 00:00:00 2001 From: WillaWillNot Date: Fri, 21 Nov 2025 20:35:54 -0500 Subject: Fixed broken examples/formatting reported by CI --- .vscode/settings.json | 6 +++++- docs/examples/layer-by-layer/blinky-async/src/main.rs | 3 +-- examples/boot/application/stm32f3/src/bin/a.rs | 3 +-- examples/boot/application/stm32f7/src/bin/a.rs | 3 +-- examples/boot/application/stm32h7/src/bin/a.rs | 3 +-- examples/boot/application/stm32l0/src/bin/a.rs | 3 +-- examples/boot/application/stm32l1/src/bin/a.rs | 15 +++++++-------- examples/boot/application/stm32l4/src/bin/a.rs | 3 +-- examples/boot/application/stm32wl/src/bin/a.rs | 6 ++---- examples/stm32c0/src/bin/button_exti.rs | 3 +-- examples/stm32f0/src/bin/button_controlled_blink.rs | 4 +--- examples/stm32f0/src/bin/button_exti.rs | 3 +-- examples/stm32f3/src/bin/button_events.rs | 3 +-- examples/stm32f3/src/bin/button_exti.rs | 3 +-- examples/stm32f4/src/bin/button_exti.rs | 3 +-- examples/stm32f4/src/bin/eth_w5500.rs | 4 +--- examples/stm32f4/src/bin/usb_hid_keyboard.rs | 4 +--- examples/stm32f7/src/bin/button_exti.rs | 3 +-- examples/stm32g0/src/bin/button_exti.rs | 3 +-- examples/stm32g4/src/bin/button_exti.rs | 3 +-- examples/stm32h5/src/bin/button_exti.rs | 3 +-- examples/stm32h7/src/bin/button_exti.rs | 3 +-- examples/stm32h7rs/src/bin/button_exti.rs | 3 +-- examples/stm32l0/src/bin/button_exti.rs | 4 +--- examples/stm32l4/src/bin/button_exti.rs | 3 +-- examples/stm32l4/src/bin/spe_adin1110_http_server.rs | 11 +++++++++-- examples/stm32l4/src/bin/spi_dma.rs | 4 ++-- examples/stm32l5/src/bin/button_exti.rs | 3 +-- examples/stm32n6/src/bin/blinky.rs | 3 +-- examples/stm32u0/src/bin/button_exti.rs | 3 +-- examples/stm32wb/src/bin/button_exti.rs | 3 +-- examples/stm32wba/src/bin/button_exti.rs | 3 +-- examples/stm32wba6/src/bin/button_exti.rs | 3 +-- examples/stm32wl/src/bin/button_exti.rs | 4 +--- examples/stm32wle5/src/bin/button_exti.rs | 4 +--- 35 files changed, 55 insertions(+), 83 deletions(-) diff --git a/.vscode/settings.json b/.vscode/settings.json index c504f3ccd..3c9cce18b 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -61,4 +61,8 @@ // "examples/stm32wl/Cargo.toml", // "examples/wasm/Cargo.toml", ], -} + "rust-analyzer.rustfmt.extraArgs": [ + //Uncomment to run rustfmt with nightly-only settings that match the CI + // "+nightly" + ], +} \ No newline at end of file diff --git a/docs/examples/layer-by-layer/blinky-async/src/main.rs b/docs/examples/layer-by-layer/blinky-async/src/main.rs index 5871534c5..02cee2a4b 100644 --- a/docs/examples/layer-by-layer/blinky-async/src/main.rs +++ b/docs/examples/layer-by-layer/blinky-async/src/main.rs @@ -2,10 +2,9 @@ #![no_main] use embassy_executor::Spawner; -use embassy_stm32::bind_interrupts; use embassy_stm32::exti::{self, ExtiInput}; use embassy_stm32::gpio::{Level, Output, Pull, Speed}; -use embassy_stm32::interrupt; +use embassy_stm32::{bind_interrupts, interrupt}; use {defmt_rtt as _, panic_probe as _}; bind_interrupts!( diff --git a/examples/boot/application/stm32f3/src/bin/a.rs b/examples/boot/application/stm32f3/src/bin/a.rs index e42e24885..8e0a6685e 100644 --- a/examples/boot/application/stm32f3/src/bin/a.rs +++ b/examples/boot/application/stm32f3/src/bin/a.rs @@ -6,11 +6,10 @@ use defmt_rtt::*; use embassy_boot_stm32::{AlignedBuffer, FirmwareUpdater, FirmwareUpdaterConfig}; use embassy_embedded_hal::adapter::BlockingAsync; use embassy_executor::Spawner; -use embassy_stm32::bind_interrupts; use embassy_stm32::exti::{self, ExtiInput}; use embassy_stm32::flash::{Flash, WRITE_SIZE}; use embassy_stm32::gpio::{Level, Output, Pull, Speed}; -use embassy_stm32::interrupt; +use embassy_stm32::{bind_interrupts, interrupt}; use embassy_sync::mutex::Mutex; use panic_reset as _; diff --git a/examples/boot/application/stm32f7/src/bin/a.rs b/examples/boot/application/stm32f7/src/bin/a.rs index 5ec220c2e..db808934f 100644 --- a/examples/boot/application/stm32f7/src/bin/a.rs +++ b/examples/boot/application/stm32f7/src/bin/a.rs @@ -7,11 +7,10 @@ use core::cell::RefCell; use defmt_rtt::*; use embassy_boot_stm32::{AlignedBuffer, BlockingFirmwareUpdater, FirmwareUpdaterConfig}; use embassy_executor::Spawner; -use embassy_stm32::bind_interrupts; use embassy_stm32::exti::{self, ExtiInput}; use embassy_stm32::flash::{Flash, WRITE_SIZE}; use embassy_stm32::gpio::{Level, Output, Pull, Speed}; -use embassy_stm32::interrupt; +use embassy_stm32::{bind_interrupts, interrupt}; use embassy_sync::blocking_mutex::Mutex; use embedded_storage::nor_flash::NorFlash; use panic_reset as _; diff --git a/examples/boot/application/stm32h7/src/bin/a.rs b/examples/boot/application/stm32h7/src/bin/a.rs index 00ac89564..ed2d5b89e 100644 --- a/examples/boot/application/stm32h7/src/bin/a.rs +++ b/examples/boot/application/stm32h7/src/bin/a.rs @@ -7,11 +7,10 @@ use core::cell::RefCell; use defmt_rtt::*; use embassy_boot_stm32::{AlignedBuffer, BlockingFirmwareUpdater, FirmwareUpdaterConfig}; use embassy_executor::Spawner; -use embassy_stm32::bind_interrupts; use embassy_stm32::exti::{self, ExtiInput}; use embassy_stm32::flash::{Flash, WRITE_SIZE}; use embassy_stm32::gpio::{Level, Output, Pull, Speed}; -use embassy_stm32::interrupt; +use embassy_stm32::{bind_interrupts, interrupt}; use embassy_sync::blocking_mutex::Mutex; use embedded_storage::nor_flash::NorFlash; use panic_reset as _; diff --git a/examples/boot/application/stm32l0/src/bin/a.rs b/examples/boot/application/stm32l0/src/bin/a.rs index 6f21d9be0..3f9e6d24e 100644 --- a/examples/boot/application/stm32l0/src/bin/a.rs +++ b/examples/boot/application/stm32l0/src/bin/a.rs @@ -6,11 +6,10 @@ use defmt_rtt::*; use embassy_boot_stm32::{AlignedBuffer, FirmwareUpdater, FirmwareUpdaterConfig}; use embassy_embedded_hal::adapter::BlockingAsync; use embassy_executor::Spawner; -use embassy_stm32::bind_interrupts; use embassy_stm32::exti::{self, ExtiInput}; use embassy_stm32::flash::{Flash, WRITE_SIZE}; use embassy_stm32::gpio::{Level, Output, Pull, Speed}; -use embassy_stm32::interrupt; +use embassy_stm32::{bind_interrupts, interrupt}; use embassy_sync::mutex::Mutex; use embassy_time::Timer; use panic_reset as _; diff --git a/examples/boot/application/stm32l1/src/bin/a.rs b/examples/boot/application/stm32l1/src/bin/a.rs index 16f280776..b5432eb91 100644 --- a/examples/boot/application/stm32l1/src/bin/a.rs +++ b/examples/boot/application/stm32l1/src/bin/a.rs @@ -6,11 +6,10 @@ use defmt_rtt::*; use embassy_boot_stm32::{AlignedBuffer, FirmwareUpdater, FirmwareUpdaterConfig}; use embassy_embedded_hal::adapter::BlockingAsync; use embassy_executor::Spawner; -use embassy_stm32::bind_interrupts; use embassy_stm32::exti::{self, ExtiInput}; use embassy_stm32::flash::{Flash, WRITE_SIZE}; use embassy_stm32::gpio::{Level, Output, Pull, Speed}; -use embassy_stm32::interrupt; +use embassy_stm32::{bind_interrupts, interrupt}; use embassy_sync::mutex::Mutex; use embassy_time::Timer; use panic_reset as _; @@ -31,15 +30,15 @@ async fn main(_spawner: Spawner) { let flash = Flash::new_blocking(p.FLASH); let flash = Mutex::new(BlockingAsync::new(flash)); - let mut button = ExtiInput::new(p.PB2, p.EXTI2, Pull::Up); - - let mut led = Output::new( - p.PB5, - Level::Low, - Speed::Low, + let mut button = ExtiInput::new( + p.PB2, + p.EXTI2, + Pull::Up, Irqs::as_any::>(), ); + let mut led = Output::new(p.PB5, Level::Low, Speed::Low); + led.set_high(); let config = FirmwareUpdaterConfig::from_linkerfile(&flash, &flash); diff --git a/examples/boot/application/stm32l4/src/bin/a.rs b/examples/boot/application/stm32l4/src/bin/a.rs index 5e5d45193..349e57f94 100644 --- a/examples/boot/application/stm32l4/src/bin/a.rs +++ b/examples/boot/application/stm32l4/src/bin/a.rs @@ -6,11 +6,10 @@ use defmt_rtt::*; use embassy_boot_stm32::{AlignedBuffer, FirmwareUpdater, FirmwareUpdaterConfig}; use embassy_embedded_hal::adapter::BlockingAsync; use embassy_executor::Spawner; -use embassy_stm32::bind_interrupts; use embassy_stm32::exti::{self, ExtiInput}; use embassy_stm32::flash::{Flash, WRITE_SIZE}; use embassy_stm32::gpio::{Level, Output, Pull, Speed}; -use embassy_stm32::interrupt; +use embassy_stm32::{bind_interrupts, interrupt}; use embassy_sync::mutex::Mutex; use panic_reset as _; diff --git a/examples/boot/application/stm32wl/src/bin/a.rs b/examples/boot/application/stm32wl/src/bin/a.rs index ad45c1262..285d57e41 100644 --- a/examples/boot/application/stm32wl/src/bin/a.rs +++ b/examples/boot/application/stm32wl/src/bin/a.rs @@ -8,12 +8,10 @@ use defmt_rtt::*; use embassy_boot_stm32::{AlignedBuffer, FirmwareUpdater, FirmwareUpdaterConfig}; use embassy_embedded_hal::adapter::BlockingAsync; use embassy_executor::Spawner; -use embassy_stm32::SharedData; -use embassy_stm32::bind_interrupts; use embassy_stm32::exti::{self, ExtiInput}; use embassy_stm32::flash::{Flash, WRITE_SIZE}; use embassy_stm32::gpio::{Level, Output, Pull, Speed}; -use embassy_stm32::interrupt; +use embassy_stm32::{SharedData, bind_interrupts, interrupt}; use embassy_sync::mutex::Mutex; use panic_reset as _; @@ -24,7 +22,7 @@ static APP_B: &[u8] = include_bytes!("../../b.bin"); bind_interrupts!( pub struct Irqs{ - EXTEXTI0I2_3 => exti::InterruptHandler; + EXTI0 => exti::InterruptHandler; }); #[unsafe(link_section = ".shared_data")] diff --git a/examples/stm32c0/src/bin/button_exti.rs b/examples/stm32c0/src/bin/button_exti.rs index dbc9e2f50..52fdc39c5 100644 --- a/examples/stm32c0/src/bin/button_exti.rs +++ b/examples/stm32c0/src/bin/button_exti.rs @@ -3,10 +3,9 @@ use defmt::*; use embassy_executor::Spawner; -use embassy_stm32::bind_interrupts; use embassy_stm32::exti::{self, ExtiInput}; use embassy_stm32::gpio::Pull; -use embassy_stm32::interrupt; +use embassy_stm32::{bind_interrupts, interrupt}; use {defmt_rtt as _, panic_probe as _}; bind_interrupts!( diff --git a/examples/stm32f0/src/bin/button_controlled_blink.rs b/examples/stm32f0/src/bin/button_controlled_blink.rs index f57d5d200..f9f636bad 100644 --- a/examples/stm32f0/src/bin/button_controlled_blink.rs +++ b/examples/stm32f0/src/bin/button_controlled_blink.rs @@ -7,11 +7,9 @@ use core::sync::atomic::{AtomicU32, Ordering}; use defmt::info; use embassy_executor::Spawner; -use embassy_stm32::Peri; -use embassy_stm32::bind_interrupts; use embassy_stm32::exti::{self, ExtiInput}; use embassy_stm32::gpio::{AnyPin, Level, Output, Pull, Speed}; -use embassy_stm32::interrupt; +use embassy_stm32::{Peri, bind_interrupts, interrupt}; use embassy_time::Timer; use {defmt_rtt as _, panic_probe as _}; diff --git a/examples/stm32f0/src/bin/button_exti.rs b/examples/stm32f0/src/bin/button_exti.rs index 67ea19215..e91010514 100644 --- a/examples/stm32f0/src/bin/button_exti.rs +++ b/examples/stm32f0/src/bin/button_exti.rs @@ -3,10 +3,9 @@ use defmt::*; use embassy_executor::Spawner; -use embassy_stm32::bind_interrupts; use embassy_stm32::exti::{self, ExtiInput}; use embassy_stm32::gpio::Pull; -use embassy_stm32::interrupt; +use embassy_stm32::{bind_interrupts, interrupt}; use {defmt_rtt as _, panic_probe as _}; bind_interrupts!( diff --git a/examples/stm32f3/src/bin/button_events.rs b/examples/stm32f3/src/bin/button_events.rs index 78c6592ee..e52622d55 100644 --- a/examples/stm32f3/src/bin/button_events.rs +++ b/examples/stm32f3/src/bin/button_events.rs @@ -11,10 +11,9 @@ use defmt::*; use embassy_executor::Spawner; -use embassy_stm32::bind_interrupts; use embassy_stm32::exti::{self, ExtiInput}; use embassy_stm32::gpio::{Level, Output, Pull, Speed}; -use embassy_stm32::interrupt; +use embassy_stm32::{bind_interrupts, interrupt}; use embassy_sync::blocking_mutex::raw::ThreadModeRawMutex; use embassy_sync::channel::Channel; use embassy_time::{Duration, Timer, with_timeout}; diff --git a/examples/stm32f3/src/bin/button_exti.rs b/examples/stm32f3/src/bin/button_exti.rs index d6d613447..4a75e031c 100644 --- a/examples/stm32f3/src/bin/button_exti.rs +++ b/examples/stm32f3/src/bin/button_exti.rs @@ -3,10 +3,9 @@ use defmt::*; use embassy_executor::Spawner; -use embassy_stm32::bind_interrupts; use embassy_stm32::exti::{self, ExtiInput}; use embassy_stm32::gpio::Pull; -use embassy_stm32::interrupt; +use embassy_stm32::{bind_interrupts, interrupt}; use {defmt_rtt as _, panic_probe as _}; bind_interrupts!( diff --git a/examples/stm32f4/src/bin/button_exti.rs b/examples/stm32f4/src/bin/button_exti.rs index 77831224b..93560ed06 100644 --- a/examples/stm32f4/src/bin/button_exti.rs +++ b/examples/stm32f4/src/bin/button_exti.rs @@ -3,10 +3,9 @@ use defmt::*; use embassy_executor::Spawner; -use embassy_stm32::bind_interrupts; use embassy_stm32::exti::{self, ExtiInput}; use embassy_stm32::gpio::Pull; -use embassy_stm32::interrupt; +use embassy_stm32::{bind_interrupts, interrupt}; use {defmt_rtt as _, panic_probe as _}; bind_interrupts!( diff --git a/examples/stm32f4/src/bin/eth_w5500.rs b/examples/stm32f4/src/bin/eth_w5500.rs index f7d2ce7de..f59f1d3b5 100644 --- a/examples/stm32f4/src/bin/eth_w5500.rs +++ b/examples/stm32f4/src/bin/eth_w5500.rs @@ -7,16 +7,14 @@ use embassy_net::tcp::TcpSocket; use embassy_net::{Ipv4Address, StackResources}; use embassy_net_wiznet::chip::W5500; use embassy_net_wiznet::{Device, Runner, State}; -use embassy_stm32::bind_interrupts; use embassy_stm32::exti::{self, ExtiInput}; use embassy_stm32::gpio::{Level, Output, Pull, Speed}; -use embassy_stm32::interrupt; use embassy_stm32::mode::Async; use embassy_stm32::rng::Rng; use embassy_stm32::spi::Spi; use embassy_stm32::spi::mode::Master; use embassy_stm32::time::Hertz; -use embassy_stm32::{Config, bind_interrupts, peripherals, rng, spi}; +use embassy_stm32::{Config, bind_interrupts, interrupt, peripherals, rng, spi}; use embassy_time::{Delay, Timer}; use embedded_hal_bus::spi::ExclusiveDevice; use embedded_io_async::Write; diff --git a/examples/stm32f4/src/bin/usb_hid_keyboard.rs b/examples/stm32f4/src/bin/usb_hid_keyboard.rs index 60922c2e8..b5a7e86e4 100644 --- a/examples/stm32f4/src/bin/usb_hid_keyboard.rs +++ b/examples/stm32f4/src/bin/usb_hid_keyboard.rs @@ -6,13 +6,11 @@ use core::sync::atomic::{AtomicBool, AtomicU8, Ordering}; use defmt::*; use embassy_executor::Spawner; use embassy_futures::join::join; -use embassy_stm32::bind_interrupts; use embassy_stm32::exti::{self, ExtiInput}; use embassy_stm32::gpio::Pull; -use embassy_stm32::interrupt; use embassy_stm32::time::Hertz; use embassy_stm32::usb::Driver; -use embassy_stm32::{Config, bind_interrupts, peripherals, usb}; +use embassy_stm32::{Config, bind_interrupts, interrupt, peripherals, usb}; use embassy_usb::class::hid::{ HidBootProtocol, HidProtocolMode, HidReaderWriter, HidSubclass, ReportId, RequestHandler, State, }; diff --git a/examples/stm32f7/src/bin/button_exti.rs b/examples/stm32f7/src/bin/button_exti.rs index 77831224b..93560ed06 100644 --- a/examples/stm32f7/src/bin/button_exti.rs +++ b/examples/stm32f7/src/bin/button_exti.rs @@ -3,10 +3,9 @@ use defmt::*; use embassy_executor::Spawner; -use embassy_stm32::bind_interrupts; use embassy_stm32::exti::{self, ExtiInput}; use embassy_stm32::gpio::Pull; -use embassy_stm32::interrupt; +use embassy_stm32::{bind_interrupts, interrupt}; use {defmt_rtt as _, panic_probe as _}; bind_interrupts!( diff --git a/examples/stm32g0/src/bin/button_exti.rs b/examples/stm32g0/src/bin/button_exti.rs index dbc9e2f50..52fdc39c5 100644 --- a/examples/stm32g0/src/bin/button_exti.rs +++ b/examples/stm32g0/src/bin/button_exti.rs @@ -3,10 +3,9 @@ use defmt::*; use embassy_executor::Spawner; -use embassy_stm32::bind_interrupts; use embassy_stm32::exti::{self, ExtiInput}; use embassy_stm32::gpio::Pull; -use embassy_stm32::interrupt; +use embassy_stm32::{bind_interrupts, interrupt}; use {defmt_rtt as _, panic_probe as _}; bind_interrupts!( diff --git a/examples/stm32g4/src/bin/button_exti.rs b/examples/stm32g4/src/bin/button_exti.rs index 77831224b..93560ed06 100644 --- a/examples/stm32g4/src/bin/button_exti.rs +++ b/examples/stm32g4/src/bin/button_exti.rs @@ -3,10 +3,9 @@ use defmt::*; use embassy_executor::Spawner; -use embassy_stm32::bind_interrupts; use embassy_stm32::exti::{self, ExtiInput}; use embassy_stm32::gpio::Pull; -use embassy_stm32::interrupt; +use embassy_stm32::{bind_interrupts, interrupt}; use {defmt_rtt as _, panic_probe as _}; bind_interrupts!( diff --git a/examples/stm32h5/src/bin/button_exti.rs b/examples/stm32h5/src/bin/button_exti.rs index f49f0ff1c..70dee04be 100644 --- a/examples/stm32h5/src/bin/button_exti.rs +++ b/examples/stm32h5/src/bin/button_exti.rs @@ -3,10 +3,9 @@ use defmt::*; use embassy_executor::Spawner; -use embassy_stm32::bind_interrupts; use embassy_stm32::exti::{self, ExtiInput}; use embassy_stm32::gpio::Pull; -use embassy_stm32::interrupt; +use embassy_stm32::{bind_interrupts, interrupt}; use {defmt_rtt as _, panic_probe as _}; bind_interrupts!( diff --git a/examples/stm32h7/src/bin/button_exti.rs b/examples/stm32h7/src/bin/button_exti.rs index 77831224b..93560ed06 100644 --- a/examples/stm32h7/src/bin/button_exti.rs +++ b/examples/stm32h7/src/bin/button_exti.rs @@ -3,10 +3,9 @@ use defmt::*; use embassy_executor::Spawner; -use embassy_stm32::bind_interrupts; use embassy_stm32::exti::{self, ExtiInput}; use embassy_stm32::gpio::Pull; -use embassy_stm32::interrupt; +use embassy_stm32::{bind_interrupts, interrupt}; use {defmt_rtt as _, panic_probe as _}; bind_interrupts!( diff --git a/examples/stm32h7rs/src/bin/button_exti.rs b/examples/stm32h7rs/src/bin/button_exti.rs index d4cf36fcc..29159a3b3 100644 --- a/examples/stm32h7rs/src/bin/button_exti.rs +++ b/examples/stm32h7rs/src/bin/button_exti.rs @@ -3,10 +3,9 @@ use defmt::*; use embassy_executor::Spawner; -use embassy_stm32::bind_interrupts; use embassy_stm32::exti::{self, ExtiInput}; use embassy_stm32::gpio::Pull; -use embassy_stm32::interrupt; +use embassy_stm32::{bind_interrupts, interrupt}; use {defmt_rtt as _, panic_probe as _}; bind_interrupts!( diff --git a/examples/stm32l0/src/bin/button_exti.rs b/examples/stm32l0/src/bin/button_exti.rs index afdeb14af..043261347 100644 --- a/examples/stm32l0/src/bin/button_exti.rs +++ b/examples/stm32l0/src/bin/button_exti.rs @@ -3,11 +3,9 @@ use defmt::*; use embassy_executor::Spawner; -use embassy_stm32::Config; -use embassy_stm32::bind_interrupts; use embassy_stm32::exti::{self, ExtiInput}; use embassy_stm32::gpio::Pull; -use embassy_stm32::interrupt; +use embassy_stm32::{Config, bind_interrupts, interrupt}; use {defmt_rtt as _, panic_probe as _}; bind_interrupts!( diff --git a/examples/stm32l4/src/bin/button_exti.rs b/examples/stm32l4/src/bin/button_exti.rs index 4d03671c0..82479eb24 100644 --- a/examples/stm32l4/src/bin/button_exti.rs +++ b/examples/stm32l4/src/bin/button_exti.rs @@ -3,10 +3,9 @@ use defmt::*; use embassy_executor::Spawner; -use embassy_stm32::bind_interrupts; use embassy_stm32::exti::{self, ExtiInput}; use embassy_stm32::gpio::Pull; -use embassy_stm32::interrupt; +use embassy_stm32::{bind_interrupts, interrupt}; use {defmt_rtt as _, panic_probe as _}; bind_interrupts!( diff --git a/examples/stm32l4/src/bin/spe_adin1110_http_server.rs b/examples/stm32l4/src/bin/spe_adin1110_http_server.rs index 0dbf515cf..e079f22c7 100644 --- a/examples/stm32l4/src/bin/spe_adin1110_http_server.rs +++ b/examples/stm32l4/src/bin/spe_adin1110_http_server.rs @@ -24,6 +24,7 @@ use embassy_futures::yield_now; use embassy_net::tcp::TcpSocket; use embassy_net::{Ipv4Address, Ipv4Cidr, Stack, StackResources, StaticConfigV4}; use embassy_net_adin1110::{ADIN1110, Device, Runner}; +use embassy_stm32::exti::ExtiInput; use embassy_stm32::gpio::{Input, Level, Output, Pull, Speed}; use embassy_stm32::i2c::{self, Config as I2C_Config, I2c}; use embassy_stm32::mode::Async; @@ -31,7 +32,7 @@ use embassy_stm32::rng::{self, Rng}; use embassy_stm32::spi::mode::Master; use embassy_stm32::spi::{Config as SPI_Config, Spi}; use embassy_stm32::time::Hertz; -use embassy_stm32::{bind_interrupts, exti, pac, peripherals}; +use embassy_stm32::{bind_interrupts, exti, interrupt, pac, peripherals}; use embassy_time::{Delay, Duration, Ticker, Timer}; use embedded_hal_async::i2c::I2c as I2cBus; use embedded_hal_bus::spi::ExclusiveDevice; @@ -45,6 +46,7 @@ bind_interrupts!(struct Irqs { I2C3_EV => i2c::EventInterruptHandler; I2C3_ER => i2c::ErrorInterruptHandler; RNG => rng::InterruptHandler; + EXTI15_10 => exti::InterruptHandler; }); // Basic settings @@ -125,7 +127,12 @@ async fn main(spawner: Spawner) { let spe_cfg1 = Input::new(dp.PC9, Pull::None); let _spe_ts_capt = Output::new(dp.PC6, Level::Low, Speed::Low); - let spe_int = exti::ExtiInput::new(dp.PB11, dp.EXTI11, Pull::None); + let spe_int = ExtiInput::new( + dp.PB11, + dp.EXTI11, + Pull::None, + Irqs::as_any::>(), + ); let spe_spi_cs_n = Output::new(dp.PB12, Level::High, Speed::High); let spe_spi_sclk = dp.PB13; diff --git a/examples/stm32l4/src/bin/spi_dma.rs b/examples/stm32l4/src/bin/spi_dma.rs index 946a759b1..970a0c608 100644 --- a/examples/stm32l4/src/bin/spi_dma.rs +++ b/examples/stm32l4/src/bin/spi_dma.rs @@ -34,8 +34,8 @@ async fn main(_spawner: Spawner) { info!("waiting for ready"); } - let write = [0x0A; 10]; - let mut read = [0; 10]; + let write = [0x0Au8; 10]; + let mut read = [0u8; 10]; cs.set_low(); spi.transfer(&mut read, &write).await.ok(); cs.set_high(); diff --git a/examples/stm32l5/src/bin/button_exti.rs b/examples/stm32l5/src/bin/button_exti.rs index 02e2013c9..41412b00f 100644 --- a/examples/stm32l5/src/bin/button_exti.rs +++ b/examples/stm32l5/src/bin/button_exti.rs @@ -3,10 +3,9 @@ use defmt::*; use embassy_executor::Spawner; -use embassy_stm32::bind_interrupts; use embassy_stm32::exti::{self, ExtiInput}; use embassy_stm32::gpio::Pull; -use embassy_stm32::interrupt; +use embassy_stm32::{bind_interrupts, interrupt}; use {defmt_rtt as _, panic_probe as _}; bind_interrupts!( diff --git a/examples/stm32n6/src/bin/blinky.rs b/examples/stm32n6/src/bin/blinky.rs index c72e45628..764b8d6ef 100644 --- a/examples/stm32n6/src/bin/blinky.rs +++ b/examples/stm32n6/src/bin/blinky.rs @@ -3,10 +3,9 @@ use defmt::*; use embassy_executor::Spawner; -use embassy_stm32::bind_interrupts; use embassy_stm32::exti::{self, ExtiInput}; use embassy_stm32::gpio::{Level, Output, Pull, Speed}; -use embassy_stm32::interrupt; +use embassy_stm32::{bind_interrupts, interrupt}; use embassy_time::Timer; use {defmt_rtt as _, panic_probe as _}; diff --git a/examples/stm32u0/src/bin/button_exti.rs b/examples/stm32u0/src/bin/button_exti.rs index dbc9e2f50..52fdc39c5 100644 --- a/examples/stm32u0/src/bin/button_exti.rs +++ b/examples/stm32u0/src/bin/button_exti.rs @@ -3,10 +3,9 @@ use defmt::*; use embassy_executor::Spawner; -use embassy_stm32::bind_interrupts; use embassy_stm32::exti::{self, ExtiInput}; use embassy_stm32::gpio::Pull; -use embassy_stm32::interrupt; +use embassy_stm32::{bind_interrupts, interrupt}; use {defmt_rtt as _, panic_probe as _}; bind_interrupts!( diff --git a/examples/stm32wb/src/bin/button_exti.rs b/examples/stm32wb/src/bin/button_exti.rs index 2736b98f1..e2698450a 100644 --- a/examples/stm32wb/src/bin/button_exti.rs +++ b/examples/stm32wb/src/bin/button_exti.rs @@ -3,10 +3,9 @@ use defmt::*; use embassy_executor::Spawner; -use embassy_stm32::bind_interrupts; use embassy_stm32::exti::{self, ExtiInput}; use embassy_stm32::gpio::Pull; -use embassy_stm32::interrupt; +use embassy_stm32::{bind_interrupts, interrupt}; use {defmt_rtt as _, panic_probe as _}; bind_interrupts!( diff --git a/examples/stm32wba/src/bin/button_exti.rs b/examples/stm32wba/src/bin/button_exti.rs index d4cf36fcc..29159a3b3 100644 --- a/examples/stm32wba/src/bin/button_exti.rs +++ b/examples/stm32wba/src/bin/button_exti.rs @@ -3,10 +3,9 @@ use defmt::*; use embassy_executor::Spawner; -use embassy_stm32::bind_interrupts; use embassy_stm32::exti::{self, ExtiInput}; use embassy_stm32::gpio::Pull; -use embassy_stm32::interrupt; +use embassy_stm32::{bind_interrupts, interrupt}; use {defmt_rtt as _, panic_probe as _}; bind_interrupts!( diff --git a/examples/stm32wba6/src/bin/button_exti.rs b/examples/stm32wba6/src/bin/button_exti.rs index d4cf36fcc..29159a3b3 100644 --- a/examples/stm32wba6/src/bin/button_exti.rs +++ b/examples/stm32wba6/src/bin/button_exti.rs @@ -3,10 +3,9 @@ use defmt::*; use embassy_executor::Spawner; -use embassy_stm32::bind_interrupts; use embassy_stm32::exti::{self, ExtiInput}; use embassy_stm32::gpio::Pull; -use embassy_stm32::interrupt; +use embassy_stm32::{bind_interrupts, interrupt}; use {defmt_rtt as _, panic_probe as _}; bind_interrupts!( diff --git a/examples/stm32wl/src/bin/button_exti.rs b/examples/stm32wl/src/bin/button_exti.rs index 183f93f2f..c22de0322 100644 --- a/examples/stm32wl/src/bin/button_exti.rs +++ b/examples/stm32wl/src/bin/button_exti.rs @@ -5,11 +5,9 @@ use core::mem::MaybeUninit; use defmt::*; use embassy_executor::Spawner; -use embassy_stm32::SharedData; -use embassy_stm32::bind_interrupts; use embassy_stm32::exti::{self, ExtiInput}; use embassy_stm32::gpio::Pull; -use embassy_stm32::interrupt; +use embassy_stm32::{SharedData, bind_interrupts, interrupt}; use {defmt_rtt as _, panic_probe as _}; bind_interrupts!( diff --git a/examples/stm32wle5/src/bin/button_exti.rs b/examples/stm32wle5/src/bin/button_exti.rs index 196afa330..538801990 100644 --- a/examples/stm32wle5/src/bin/button_exti.rs +++ b/examples/stm32wle5/src/bin/button_exti.rs @@ -5,11 +5,9 @@ use defmt::*; #[cfg(feature = "defmt-rtt")] use defmt_rtt as _; use embassy_executor::Spawner; -use embassy_stm32::bind_interrupts; use embassy_stm32::exti::{self, ExtiInput}; use embassy_stm32::gpio::Pull; -use embassy_stm32::interrupt; -use embassy_stm32::low_power; +use embassy_stm32::{bind_interrupts, interrupt, low_power}; use panic_probe as _; use static_cell::StaticCell; -- cgit From 2649ba2a49fad0c04e3323d17373707f2eb03097 Mon Sep 17 00:00:00 2001 From: Dion Dokter Date: Sat, 22 Nov 2025 14:12:46 +0100 Subject: Update metapack and add all(?) families + v1 support --- embassy-stm32/Cargo.toml | 4 ++-- embassy-stm32/src/lcd.rs | 7 +++++-- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/embassy-stm32/Cargo.toml b/embassy-stm32/Cargo.toml index 2f4f2ce51..fbd00895d 100644 --- a/embassy-stm32/Cargo.toml +++ b/embassy-stm32/Cargo.toml @@ -200,11 +200,11 @@ aligned = "0.4.1" heapless = "0.9.1" #stm32-metapac = { version = "18" } -stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-b77c8d968f53b18d6bdcd052e354b5070ec2bbc2" } +stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-61abfb115273dc66ed99bf14545779a2aa0722c8" } [build-dependencies] #stm32-metapac = { version = "18", default-features = false, features = ["metadata"]} -stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-b77c8d968f53b18d6bdcd052e354b5070ec2bbc2", default-features = false, features = ["metadata"] } +stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-61abfb115273dc66ed99bf14545779a2aa0722c8", default-features = false, features = ["metadata"] } proc-macro2 = "1.0.36" quote = "1.0.15" diff --git a/embassy-stm32/src/lcd.rs b/embassy-stm32/src/lcd.rs index dc1989168..348865fbd 100644 --- a/embassy-stm32/src/lcd.rs +++ b/embassy-stm32/src/lcd.rs @@ -10,15 +10,16 @@ use crate::time::Hertz; #[cfg(any(stm32u0, stm32l073, stm32l083))] const NUM_SEGMENTS: u8 = 52; -#[cfg(stm32wb)] +#[cfg(any(stm32wb, stm32l4x6, stm32l15x, stm32l162, stm32l4x3, stm32l4x6))] const NUM_SEGMENTS: u8 = 44; -#[cfg(any(stm32l053, stm32l063))] +#[cfg(any(stm32l053, stm32l063, stm32l100))] const NUM_SEGMENTS: u8 = 32; /// LCD configuration struct #[non_exhaustive] #[derive(Debug, Clone, Copy)] pub struct Config { + #[cfg(lcd_v2)] /// Enable the voltage output buffer for higher driving capability. /// /// The LCD driving capability is improved as buffers prevent the LCD capacitive loads from loading the resistor @@ -42,6 +43,7 @@ pub struct Config { impl Default for Config { fn default() -> Self { Self { + #[cfg(lcd_v2)] use_voltage_output_buffer: false, use_segment_muxing: false, bias: Default::default(), @@ -249,6 +251,7 @@ impl<'d, T: Instance> Lcd<'d, T> { // Set the control register values T::regs().cr().modify(|w| { + #[cfg(lcd_v2)] w.set_bufen(config.use_voltage_output_buffer); w.set_mux_seg(config.use_segment_muxing); w.set_bias(config.bias as u8); -- cgit From f686ce9ebb19081e12eae203e92273f8ecb6eaf2 Mon Sep 17 00:00:00 2001 From: Dion Dokter Date: Sat, 22 Nov 2025 15:25:54 +0100 Subject: Impl blink --- embassy-stm32/src/lcd.rs | 73 +++++++++++++++++++++++++++++++++++++++-- examples/stm32u0/src/bin/lcd.rs | 5 ++- 2 files changed, 75 insertions(+), 3 deletions(-) diff --git a/embassy-stm32/src/lcd.rs b/embassy-stm32/src/lcd.rs index 348865fbd..ea29f1398 100644 --- a/embassy-stm32/src/lcd.rs +++ b/embassy-stm32/src/lcd.rs @@ -152,6 +152,7 @@ pub enum Drive { pub struct Lcd<'d, T: Instance> { _peri: PhantomData<&'d mut T>, duty: Duty, + ck_div: u32, } impl<'d, T: Instance> Lcd<'d, T> { @@ -227,9 +228,11 @@ impl<'d, T: Instance> Lcd<'d, T> { } } + let ck_div = lcd_clk.0 / ((1 << ps) * (div + 16)); + trace!( - "lcd_clk: {}, fps: {}, ps: {}, div: {}", - lcd_clk, best_fps_match, ps, div + "lcd_clk: {}, fps: {}, ps: {}, div: {}, ck_div: {}", + lcd_clk, best_fps_match, ps, div, ck_div ); if best_fps_match == u32::MAX || ps > 0xF { @@ -271,6 +274,7 @@ impl<'d, T: Instance> Lcd<'d, T> { Self { _peri: PhantomData, duty: config.duty, + ck_div, } } @@ -371,6 +375,42 @@ impl<'d, T: Instance> Lcd<'d, T> { pub fn num_com_pins(&self) -> u8 { self.duty.num_com_pins() } + + /// Set the blink behavior on some pixels. + /// + /// The blink frequency is an approximation. It's divided from the clock selected by the FPS. + /// Play with the FPS value if you want the blink frequency to be more accurate. + /// + /// If a blink frequency cannot be attained, this function will panic. + pub fn set_blink(&mut self, selector: BlinkSelector, freq: BlinkFreq) { + // Freq * 100 to be able to do integer math + let scaled_blink_freq = match freq { + BlinkFreq::Hz0_25 => 25, + BlinkFreq::Hz0_5 => 50, + BlinkFreq::Hz1 => 100, + BlinkFreq::Hz2 => 200, + BlinkFreq::Hz4 => 400, + }; + + let desired_divider = self.ck_div * 100 / scaled_blink_freq; + let target_divider = desired_divider.next_power_of_two(); + let power_divisions = target_divider.trailing_zeros(); + + trace!( + "Setting LCD blink frequency -> desired_divider: {}, target_divider: {}", + desired_divider, target_divider + ); + + assert!( + (8..=1024).contains(&target_divider), + "LCD blink frequency cannot be attained" + ); + + T::regs().fcr().modify(|reg| { + reg.set_blinkf((power_divisions - 3) as u8); + reg.set_blink(selector as u8); + }) + } } impl<'d, T: Instance> Drop for Lcd<'d, T> { @@ -381,6 +421,35 @@ impl<'d, T: Instance> Drop for Lcd<'d, T> { } } +/// Blink frequency +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum BlinkFreq { + /// 0.25 hz + Hz0_25, + /// 0.5 hz + Hz0_5, + /// 1 hz + Hz1, + /// 2 hz + Hz2, + /// 4 hz + Hz4, +} + +/// Blink pixel selector +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +#[repr(u8)] +pub enum BlinkSelector { + /// No pixels blink + None = 0b00, + /// The SEG0, COM0 pixel blinks if the pixel is set + Seg0Com0 = 0b01, + /// The SEG0 pixel of all COMs blinks if the pixel is set + Seg0ComAll = 0b10, + /// All pixels blink if the pixel is set + All = 0b11, +} + /// A type-erased pin that can be configured as an LCD pin. /// This is used for passing pins to the new function in the array. pub struct LcdPin<'d, T: Instance> { diff --git a/examples/stm32u0/src/bin/lcd.rs b/examples/stm32u0/src/bin/lcd.rs index 6e4378074..2b34d4ef1 100644 --- a/examples/stm32u0/src/bin/lcd.rs +++ b/examples/stm32u0/src/bin/lcd.rs @@ -3,7 +3,7 @@ use defmt::*; use embassy_executor::Spawner; -use embassy_stm32::lcd::{Bias, Config, Duty, Lcd, LcdPin}; +use embassy_stm32::lcd::{Bias, BlinkFreq, BlinkSelector, Config, Duty, Lcd, LcdPin}; use embassy_stm32::peripherals::LCD; use embassy_stm32::time::Hertz; use embassy_time::Duration; @@ -72,6 +72,7 @@ async fn main(_spawner: Spawner) { ], ); + lcd.set_blink(BlinkSelector::All, BlinkFreq::Hz4); { let mut buffer = DisplayBuffer::new(); for i in 0..4 { @@ -91,6 +92,8 @@ async fn main(_spawner: Spawner) { embassy_time::Timer::after_millis(1000).await; + lcd.set_blink(BlinkSelector::None, BlinkFreq::Hz4); + const MESSAGE: &str = "Hello embassy people. Hope you like this LCD demo :} "; loop { print_message(MESSAGE, &mut lcd, Duration::from_millis(250)).await; -- cgit From 7e768e63a60e1e583a679b8cf9705c2604fdb28d Mon Sep 17 00:00:00 2001 From: xoviat Date: Sat, 22 Nov 2025 09:46:44 -0600 Subject: low_power: remove device busys for wle --- embassy-stm32/src/i2c/v2.rs | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/embassy-stm32/src/i2c/v2.rs b/embassy-stm32/src/i2c/v2.rs index 61e550ad4..b2ba94e21 100644 --- a/embassy-stm32/src/i2c/v2.rs +++ b/embassy-stm32/src/i2c/v2.rs @@ -1075,8 +1075,6 @@ impl<'d, IM: MasterMode> I2c<'d, Async, IM> { /// Write. pub async fn write(&mut self, address: u8, write: &[u8]) -> Result<(), Error> { - #[cfg(all(feature = "low-power", stm32wlex))] - let _device_busy = crate::low_power::DeviceBusy::new_stop1(); let timeout = self.timeout(); if write.is_empty() { self.write_internal(address.into(), write, true, timeout) @@ -1091,8 +1089,6 @@ impl<'d, IM: MasterMode> I2c<'d, Async, IM> { /// /// The buffers are concatenated in a single write transaction. pub async fn write_vectored(&mut self, address: Address, write: &[&[u8]]) -> Result<(), Error> { - #[cfg(all(feature = "low-power", stm32wlex))] - let _device_busy = crate::low_power::DeviceBusy::new_stop1(); let timeout = self.timeout(); if write.is_empty() { @@ -1124,8 +1120,6 @@ impl<'d, IM: MasterMode> I2c<'d, Async, IM> { /// Read. pub async fn read(&mut self, address: u8, buffer: &mut [u8]) -> Result<(), Error> { - #[cfg(all(feature = "low-power", stm32wlex))] - let _device_busy = crate::low_power::DeviceBusy::new_stop1(); let timeout = self.timeout(); if buffer.is_empty() { @@ -1138,8 +1132,6 @@ impl<'d, IM: MasterMode> I2c<'d, Async, IM> { /// Write, restart, read. pub async fn write_read(&mut self, address: u8, write: &[u8], read: &mut [u8]) -> Result<(), Error> { - #[cfg(all(feature = "low-power", stm32wlex))] - let _device_busy = crate::low_power::DeviceBusy::new_stop1(); let timeout = self.timeout(); if write.is_empty() { @@ -1165,9 +1157,6 @@ impl<'d, IM: MasterMode> I2c<'d, Async, IM> { /// /// [transaction contract]: embedded_hal_1::i2c::I2c::transaction pub async fn transaction(&mut self, addr: u8, operations: &mut [Operation<'_>]) -> Result<(), Error> { - #[cfg(all(feature = "low-power", stm32wlex))] - let _device_busy = crate::low_power::DeviceBusy::new_stop1(); - if operations.is_empty() { return Err(Error::ZeroLengthTransfer); } -- cgit From 764c921a573e42f510a74d61f31302b8609bbd6c Mon Sep 17 00:00:00 2001 From: WillaWillNot Date: Sat, 22 Nov 2025 16:48:19 -0500 Subject: Reverted adding AnyBinding, removed AnyChannel, removed ability for ExtiInput to accept AnyPin and AnyChannel arguments, added ExtiPin trait for all pins which is lost on converstion to AnyPin and contains type-level ExtiChannel information --- .../layer-by-layer/blinky-async/src/main.rs | 7 +-- embassy-hal-internal/CHANGELOG.md | 2 - embassy-hal-internal/src/interrupt.rs | 60 +------------------ embassy-stm32/CHANGELOG.md | 6 +- embassy-stm32/src/exti.rs | 69 +++++++++------------- embassy-stm32/src/gpio.rs | 21 ++++--- embassy-stm32/src/lib.rs | 17 ++---- examples/boot/application/stm32f3/src/bin/a.rs | 7 +-- examples/boot/application/stm32f7/src/bin/a.rs | 7 +-- examples/boot/application/stm32h7/src/bin/a.rs | 7 +-- examples/boot/application/stm32l0/src/bin/a.rs | 7 +-- examples/boot/application/stm32l1/src/bin/a.rs | 7 +-- examples/boot/application/stm32l4/src/bin/a.rs | 7 +-- examples/boot/application/stm32wl/src/bin/a.rs | 7 +-- examples/stm32c0/src/bin/button_exti.rs | 7 +-- .../stm32f0/src/bin/button_controlled_blink.rs | 7 +-- examples/stm32f0/src/bin/button_exti.rs | 7 +-- examples/stm32f3/src/bin/button_events.rs | 7 +-- examples/stm32f3/src/bin/button_exti.rs | 7 +-- examples/stm32f4/src/bin/button_exti.rs | 7 +-- examples/stm32f4/src/bin/eth_w5500.rs | 7 +-- examples/stm32f4/src/bin/usb_hid_keyboard.rs | 7 +-- examples/stm32f7/src/bin/button_exti.rs | 7 +-- examples/stm32g0/src/bin/button_exti.rs | 7 +-- examples/stm32g4/src/bin/button_exti.rs | 7 +-- examples/stm32h5/src/bin/button_exti.rs | 7 +-- examples/stm32h7/src/bin/button_exti.rs | 7 +-- examples/stm32h7rs/src/bin/button_exti.rs | 7 +-- examples/stm32l0/src/bin/button_exti.rs | 7 +-- examples/stm32l4/src/bin/button_exti.rs | 7 +-- .../stm32l4/src/bin/spe_adin1110_http_server.rs | 7 +-- examples/stm32l5/src/bin/button_exti.rs | 7 +-- examples/stm32n6/src/bin/blinky.rs | 7 +-- examples/stm32u0/src/bin/button_exti.rs | 7 +-- examples/stm32wb/src/bin/button_exti.rs | 7 +-- examples/stm32wba/src/bin/button_exti.rs | 7 +-- examples/stm32wba6/src/bin/button_exti.rs | 7 +-- examples/stm32wl/src/bin/button_exti.rs | 7 +-- examples/stm32wle5/src/bin/button_exti.rs | 7 +-- 39 files changed, 80 insertions(+), 326 deletions(-) diff --git a/docs/examples/layer-by-layer/blinky-async/src/main.rs b/docs/examples/layer-by-layer/blinky-async/src/main.rs index 02cee2a4b..007f7da46 100644 --- a/docs/examples/layer-by-layer/blinky-async/src/main.rs +++ b/docs/examples/layer-by-layer/blinky-async/src/main.rs @@ -16,12 +16,7 @@ bind_interrupts!( async fn main(_spawner: Spawner) { let p = embassy_stm32::init(Default::default()); let mut led = Output::new(p.PB14, Level::Low, Speed::VeryHigh); - let mut button = ExtiInput::new( - p.PC13, - p.EXTI13, - Pull::Up, - Irqs::as_any::>(), - ); + let mut button = ExtiInput::new(p.PC13, p.EXTI13, Pull::Up, Irqs); loop { button.wait_for_any_edge().await; diff --git a/embassy-hal-internal/CHANGELOG.md b/embassy-hal-internal/CHANGELOG.md index cb637a0ec..c78923a93 100644 --- a/embassy-hal-internal/CHANGELOG.md +++ b/embassy-hal-internal/CHANGELOG.md @@ -8,8 +8,6 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## Unreleased - ReleaseDate -- feat: type erasure from Binding to AnyBinding, so that drivers accepting type erased peripheral arguments can still check bindings - ## 0.1.1 - 2025-08-15 - First release with changelog. diff --git a/embassy-hal-internal/src/interrupt.rs b/embassy-hal-internal/src/interrupt.rs index c97dee70b..ce6057e2d 100644 --- a/embassy-hal-internal/src/interrupt.rs +++ b/embassy-hal-internal/src/interrupt.rs @@ -124,10 +124,6 @@ macro_rules! interrupt_mod { /// /// This function must ONLY be called from the interrupt handler for `I`. unsafe fn on_interrupt(); - - /// Source ID of the Handler. No need to override the default outside of internal implementations, - /// where it will always be HandlerType::User. - const SOURCE_ID: HandlerType = HandlerType::User; } /// Compile-time assertion that an interrupt has been bound to a handler. @@ -141,61 +137,7 @@ macro_rules! interrupt_mod { /// to be called every time the `I` interrupt fires. /// /// This allows drivers to check bindings at compile-time. - pub unsafe trait Binding> { - /// Obtain a type-erased Binding. - /// - /// If using the `bind_interrupts!` macro, you will likely have to use a fully qualified path - /// to call this on the output struct: `>::into_any()` - fn into_any() -> AnyBinding { - AnyBinding { - irq: I::IRQ, - handler_source: H::SOURCE_ID, - } - } - } - - #[doc(hidden)] - #[derive(Copy, Clone)] - pub struct PrivateHandlerType { - pub(crate) _private: (), - } - impl PrivateHandlerType { - pub(crate) const fn new() -> Self { - Self { - _private: (), - } - } - } - - /// Driver which defined the Handler. Always User for user-defined handlers. - #[derive(Copy, Clone)] - pub enum HandlerType { - /// Defined in user code, or otherwise has not had its SOURCE_ID overridden. - User, - /// Defined somewhere within Embassy. - Embassy(PrivateHandlerType), - /// Defined by the [embassy-stm32::exti] module. - EmbassyStm32Exti(PrivateHandlerType), - } - - /// Type-erased Binding. - /// - /// Useful for proving a particular binding has been made to a driver which also accepts - /// type-erased peripheral arguments that hide the necessary Interrupt type at compile time. - pub struct AnyBinding { - irq: super::Interrupt, - handler_source: HandlerType, - } - impl AnyBinding { - /// Get the IRQ (vector number) of the interrupt. - pub const fn irq(&self) -> super::Interrupt { - self.irq - } - /// Get the source of the handler bound to the interrupt. - pub const fn source(&self) -> HandlerType { - self.handler_source - } - } + pub unsafe trait Binding> {} } } }; diff --git a/embassy-stm32/CHANGELOG.md b/embassy-stm32/CHANGELOG.md index 6e6a07037..b0c74f7d0 100644 --- a/embassy-stm32/CHANGELOG.md +++ b/embassy-stm32/CHANGELOG.md @@ -74,9 +74,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - fix: fixing channel numbers on vbat and vddcore for adc on adc - adc: adding disable to vbat - feat: stm32/flash: add async support for h7 family -- feat: exti brought in line with other drivers' interrupt rebinding system -- feat: bind_interrupts generates convenience method for type erasing to AnyBinding -- fix: exti2 now usable as a normal exti on chips with touch sensing +- feat: exti brought in line with other drivers' interrupt rebinding system ([#4922](https://github.com/embassy-rs/embassy/pull/4922)) +- removal: ExtiInput no longer accepts AnyPin/AnyChannel; AnyChannel removed entirely +- change: build script now generates `unimpl_tsc` cfg option when a chip has a TSC peripheral but no driver ## 0.4.0 - 2025-08-26 diff --git a/embassy-stm32/src/exti.rs b/embassy-stm32/src/exti.rs index 93e15a90d..57217034e 100644 --- a/embassy-stm32/src/exti.rs +++ b/embassy-stm32/src/exti.rs @@ -5,13 +5,13 @@ use core::marker::PhantomData; use core::pin::Pin; use core::task::{Context, Poll}; -use embassy_hal_internal::{PeripheralType, impl_peripheral}; +use embassy_hal_internal::PeripheralType; use embassy_sync::waitqueue::AtomicWaker; use futures_util::FutureExt; -use crate::gpio::{AnyPin, Input, Level, Pin as GpioPin, PinNumber, Pull}; +use crate::gpio::{AnyPin, ExtiPin, Input, Level, Pin as GpioPin, PinNumber, Pull}; use crate::interrupt::Interrupt as InterruptEnum; -use crate::interrupt::typelevel::{AnyBinding, Handler, HandlerType, Interrupt as InterruptType, PrivateHandlerType}; +use crate::interrupt::typelevel::{Binding, Handler, Interrupt as InterruptType}; use crate::pac::EXTI; use crate::pac::exti::regs::Lines; use crate::{Peri, pac}; @@ -110,24 +110,20 @@ impl<'d> ExtiInput<'d> { /// Create an EXTI input. /// /// The Binding must bind the Channel's IRQ to [InterruptHandler]. - /// - /// The interrupt [Binding](crate::interrupt::typelevel::Binding) must be type-erased to [AnyBinding] - /// via [into_any()](crate::interrupt::typelevel::Binding::into_any()), in order to support type-erased - /// [AnyChannel] arguments. [bind_interrupts](crate::bind_interrupts) also generates a convenience - /// method as_any() for type erasure that doesn't need a trait object. - pub fn new(pin: Peri<'d, T>, ch: Peri<'d, T::ExtiChannel>, pull: Pull, binding: AnyBinding) -> Self { - // Needed if using AnyPin+AnyChannel. - assert_eq!(pin.pin(), ch.number()); - assert_eq!(ch.irq(), binding.irq()); - assert!(matches!(binding.source(), HandlerType::EmbassyStm32Exti(_))); - + pub fn new( + pin: Peri<'d, T>, + _ch: Peri<'d, T::ExtiChannel>, + pull: Pull, + _irq: impl Binding< + <::ExtiChannel as Channel>::IRQ, + InterruptHandler<<::ExtiChannel as Channel>::IRQ>, + >, + ) -> Self { Self { pin: Input::new(pin, pull), } } - //pub fn new, I: Instance, C: Channel>( - /// Get whether the pin is high. pub fn is_high(&self) -> bool { self.pin.is_high() @@ -367,7 +363,6 @@ pub struct InterruptHandler { } impl Handler for InterruptHandler { - const SOURCE_ID: HandlerType = HandlerType::EmbassyStm32Exti(PrivateHandlerType::new()); unsafe fn on_interrupt() { on_irq() } @@ -383,30 +378,19 @@ pub trait Channel: PeripheralType + SealedChannel + Sized { /// [Enum-level Interrupt](InterruptEnum), which may be the same for multiple channels. fn irq(&self) -> InterruptEnum; /// [Type-level Interrupt](InterruptType), which may be the same for multiple channels. - /// Unavailable on type-erased [AnyChannel], where it is defined as the unit type. - /// has no trait bound, so mostly only useful for doc reference (e.g. while writing - /// [bind_interrupts](crate::bind_interrupts) invocation.) Use irq() for programmatic access. - type INTERRUPT; + type IRQ: InterruptType; } -/// Type-erased EXTI channel. +//Doc isn't hidden in order to surface the explanation to users, even though it's completely inoperable, not just deprecated. +//Entire type along with doc can probably be removed after deprecation has appeared in a release once. +/// Deprecated type-erased EXTI channel. /// -/// This represents ownership over any EXTI channel, known at runtime. +/// Support for AnyChannel was removed in order to support manually bindable EXTI interrupts via bind_interrupts; [ExtiInput::new()] +/// must know the required IRQ at compile time, and therefore cannot support type-erased channels. +#[deprecated = "type-erased EXTI channels are no longer supported, in order to support manually bindable EXTI interrupts (more info: https://github.com/embassy-rs/embassy/pull/4922)"] pub struct AnyChannel { + #[allow(unused)] number: PinNumber, - irq: InterruptEnum, -} - -impl_peripheral!(AnyChannel); -impl SealedChannel for AnyChannel {} -impl Channel for AnyChannel { - fn number(&self) -> PinNumber { - self.number - } - fn irq(&self) -> InterruptEnum { - self.irq - } - type INTERRUPT = bool; } macro_rules! impl_exti { @@ -425,16 +409,17 @@ macro_rules! impl_exti { fn irq(&self) -> InterruptEnum { <$irq>::IRQ } - type INTERRUPT = $irq; + type IRQ = $irq; } + + //Still here to surface deprecation messages to the user - remove when removing AnyChannel + #[allow(deprecated)] impl From for AnyChannel { fn from(_val: crate::peripherals::$type) -> Self { - Self { - number: $number, - irq: <$irq>::IRQ, - } + Self { + number: $number, } - } + }} }; } diff --git a/embassy-stm32/src/gpio.rs b/embassy-stm32/src/gpio.rs index 17c5a9962..e7d4e9ad3 100644 --- a/embassy-stm32/src/gpio.rs +++ b/embassy-stm32/src/gpio.rs @@ -812,15 +812,19 @@ pub type PinNumber = u8; #[cfg(stm32n6)] pub type PinNumber = u16; -/// GPIO pin trait. +/// Pin that can be used to configure an [ExtiInput](crate::exti::ExtiInput). This trait is lost when converting to [AnyPin]. +#[cfg(feature = "exti")] #[allow(private_bounds)] -pub trait Pin: PeripheralType + Into + SealedPin + Sized + 'static { +pub trait ExtiPin: PeripheralType + SealedPin { /// EXTI channel assigned to this pin. /// /// For example, PC4 uses EXTI4. - #[cfg(feature = "exti")] type ExtiChannel: crate::exti::Channel; +} +/// GPIO pin trait. +#[allow(private_bounds)] +pub trait Pin: PeripheralType + Into + SealedPin + Sized + 'static { /// Number of the pin within the port (0..31) #[inline] fn pin(&self) -> PinNumber { @@ -834,7 +838,7 @@ pub trait Pin: PeripheralType + Into + SealedPin + Sized + 'static { } } -/// Type-erased GPIO pin +/// Type-erased GPIO pin. pub struct AnyPin { pin_port: PinNumber, } @@ -862,10 +866,7 @@ impl AnyPin { } impl_peripheral!(AnyPin); -impl Pin for AnyPin { - #[cfg(feature = "exti")] - type ExtiChannel = crate::exti::AnyChannel; -} +impl Pin for AnyPin {} impl SealedPin for AnyPin { #[inline] fn pin_port(&self) -> PinNumber { @@ -878,7 +879,9 @@ impl SealedPin for AnyPin { foreach_pin!( ($pin_name:ident, $port_name:ident, $port_num:expr, $pin_num:expr, $exti_ch:ident) => { impl Pin for peripherals::$pin_name { - #[cfg(feature = "exti")] + } + #[cfg(feature = "exti")] + impl ExtiPin for peripherals::$pin_name { type ExtiChannel = peripherals::$exti_ch; } impl SealedPin for peripherals::$pin_name { diff --git a/embassy-stm32/src/lib.rs b/embassy-stm32/src/lib.rs index e5ffbd753..08231dc60 100644 --- a/embassy-stm32/src/lib.rs +++ b/embassy-stm32/src/lib.rs @@ -178,6 +178,10 @@ pub use crate::_generated::interrupt; /// } /// ); /// ``` +/// +/// Some chips collate multiple interrupt signals into a single interrupt vector. In the above example, I2C2_3 is a +/// single vector which is activated by events and errors on both peripherals I2C2 and I2C3. Check your chip's list +/// of interrupt vectors if you get an unexpected compile error trying to bind the standard name. // developer note: this macro can't be in `embassy-hal-internal` due to the use of `$crate`. #[macro_export] macro_rules! bind_interrupts { @@ -195,19 +199,6 @@ macro_rules! bind_interrupts { $(#[$outer])* $vis struct $name; - impl $name { - #[doc = r"Convenience method to call Binding::into_any(). Unlike the trait method, can be called with a turbofish."] - pub fn as_any< - I: $crate::interrupt::typelevel::Interrupt, - H: $crate::interrupt::typelevel::Handler - >() -> $crate::interrupt::typelevel::AnyBinding - where - Self: $crate::interrupt::typelevel::Binding - { - >::into_any() - } - } - $( #[allow(non_snake_case)] #[unsafe(no_mangle)] diff --git a/examples/boot/application/stm32f3/src/bin/a.rs b/examples/boot/application/stm32f3/src/bin/a.rs index 8e0a6685e..da3cbf1e6 100644 --- a/examples/boot/application/stm32f3/src/bin/a.rs +++ b/examples/boot/application/stm32f3/src/bin/a.rs @@ -29,12 +29,7 @@ async fn main(_spawner: Spawner) { let flash = Flash::new_blocking(p.FLASH); let flash = Mutex::new(BlockingAsync::new(flash)); - let mut button = ExtiInput::new( - p.PC13, - p.EXTI13, - Pull::Up, - Irqs::as_any::>(), - ); + let mut button = ExtiInput::new(p.PC13, p.EXTI13, Pull::Up, Irqs); let mut led = Output::new(p.PA5, Level::Low, Speed::Low); led.set_high(); diff --git a/examples/boot/application/stm32f7/src/bin/a.rs b/examples/boot/application/stm32f7/src/bin/a.rs index db808934f..62f1da269 100644 --- a/examples/boot/application/stm32f7/src/bin/a.rs +++ b/examples/boot/application/stm32f7/src/bin/a.rs @@ -31,12 +31,7 @@ async fn main(_spawner: Spawner) { let flash = Flash::new_blocking(p.FLASH); let flash = Mutex::new(RefCell::new(flash)); - let mut button = ExtiInput::new( - p.PC13, - p.EXTI13, - Pull::Down, - Irqs::as_any::>(), - ); + let mut button = ExtiInput::new(p.PC13, p.EXTI13, Pull::Down, Irqs); let mut led = Output::new(p.PB7, Level::Low, Speed::Low); led.set_high(); diff --git a/examples/boot/application/stm32h7/src/bin/a.rs b/examples/boot/application/stm32h7/src/bin/a.rs index ed2d5b89e..226971e02 100644 --- a/examples/boot/application/stm32h7/src/bin/a.rs +++ b/examples/boot/application/stm32h7/src/bin/a.rs @@ -31,12 +31,7 @@ async fn main(_spawner: Spawner) { let flash = Flash::new_blocking(p.FLASH); let flash = Mutex::new(RefCell::new(flash)); - let mut button = ExtiInput::new( - p.PC13, - p.EXTI13, - Pull::Down, - Irqs::as_any::>(), - ); + let mut button = ExtiInput::new(p.PC13, p.EXTI13, Pull::Down, Irqs); let mut led = Output::new(p.PB14, Level::Low, Speed::Low); led.set_high(); diff --git a/examples/boot/application/stm32l0/src/bin/a.rs b/examples/boot/application/stm32l0/src/bin/a.rs index 3f9e6d24e..0aa723eaa 100644 --- a/examples/boot/application/stm32l0/src/bin/a.rs +++ b/examples/boot/application/stm32l0/src/bin/a.rs @@ -30,12 +30,7 @@ async fn main(_spawner: Spawner) { let flash = Flash::new_blocking(p.FLASH); let flash = Mutex::new(BlockingAsync::new(flash)); - let mut button = ExtiInput::new( - p.PB2, - p.EXTI2, - Pull::Up, - Irqs::as_any::>(), - ); + let mut button = ExtiInput::new(p.PB2, p.EXTI2, Pull::Up, Irqs); let mut led = Output::new(p.PB5, Level::Low, Speed::Low); diff --git a/examples/boot/application/stm32l1/src/bin/a.rs b/examples/boot/application/stm32l1/src/bin/a.rs index b5432eb91..7ad7046fb 100644 --- a/examples/boot/application/stm32l1/src/bin/a.rs +++ b/examples/boot/application/stm32l1/src/bin/a.rs @@ -30,12 +30,7 @@ async fn main(_spawner: Spawner) { let flash = Flash::new_blocking(p.FLASH); let flash = Mutex::new(BlockingAsync::new(flash)); - let mut button = ExtiInput::new( - p.PB2, - p.EXTI2, - Pull::Up, - Irqs::as_any::>(), - ); + let mut button = ExtiInput::new(p.PB2, p.EXTI2, Pull::Up, Irqs); let mut led = Output::new(p.PB5, Level::Low, Speed::Low); diff --git a/examples/boot/application/stm32l4/src/bin/a.rs b/examples/boot/application/stm32l4/src/bin/a.rs index 349e57f94..4edd338c5 100644 --- a/examples/boot/application/stm32l4/src/bin/a.rs +++ b/examples/boot/application/stm32l4/src/bin/a.rs @@ -29,12 +29,7 @@ async fn main(_spawner: Spawner) { let flash = Flash::new_blocking(p.FLASH); let flash = Mutex::new(BlockingAsync::new(flash)); - let mut button = ExtiInput::new( - p.PC13, - p.EXTI13, - Pull::Up, - Irqs::as_any::>(), - ); + let mut button = ExtiInput::new(p.PC13, p.EXTI13, Pull::Up, Irqs); let mut led = Output::new(p.PB14, Level::Low, Speed::Low); led.set_high(); diff --git a/examples/boot/application/stm32wl/src/bin/a.rs b/examples/boot/application/stm32wl/src/bin/a.rs index 285d57e41..58063eb50 100644 --- a/examples/boot/application/stm32wl/src/bin/a.rs +++ b/examples/boot/application/stm32wl/src/bin/a.rs @@ -34,12 +34,7 @@ async fn main(_spawner: Spawner) { let flash = Flash::new_blocking(p.FLASH); let flash = Mutex::new(BlockingAsync::new(flash)); - let mut button = ExtiInput::new( - p.PA0, - p.EXTI0, - Pull::Up, - Irqs::as_any::>(), - ); + let mut button = ExtiInput::new(p.PA0, p.EXTI0, Pull::Up, Irqs); let mut led = Output::new(p.PB9, Level::Low, Speed::Low); led.set_high(); diff --git a/examples/stm32c0/src/bin/button_exti.rs b/examples/stm32c0/src/bin/button_exti.rs index 52fdc39c5..9d54479da 100644 --- a/examples/stm32c0/src/bin/button_exti.rs +++ b/examples/stm32c0/src/bin/button_exti.rs @@ -18,12 +18,7 @@ async fn main(_spawner: Spawner) { let p = embassy_stm32::init(Default::default()); info!("Hello World!"); - let mut button = ExtiInput::new( - p.PC13, - p.EXTI13, - Pull::Up, - Irqs::as_any::>(), - ); + let mut button = ExtiInput::new(p.PC13, p.EXTI13, Pull::Up, Irqs); info!("Press the USER button..."); diff --git a/examples/stm32f0/src/bin/button_controlled_blink.rs b/examples/stm32f0/src/bin/button_controlled_blink.rs index f9f636bad..9c7bf8a95 100644 --- a/examples/stm32f0/src/bin/button_controlled_blink.rs +++ b/examples/stm32f0/src/bin/button_controlled_blink.rs @@ -42,12 +42,7 @@ async fn main(spawner: Spawner) { // Configure the button pin and obtain handler. // On the Nucleo F091RC there is a button connected to pin PC13. - let mut button = ExtiInput::new( - p.PC13, - p.EXTI13, - Pull::None, - Irqs::as_any::>(), - ); + let mut button = ExtiInput::new(p.PC13, p.EXTI13, Pull::None, Irqs); // Create and initialize a delay variable to manage delay loop let mut del_var = 2000; diff --git a/examples/stm32f0/src/bin/button_exti.rs b/examples/stm32f0/src/bin/button_exti.rs index e91010514..d1312e1be 100644 --- a/examples/stm32f0/src/bin/button_exti.rs +++ b/examples/stm32f0/src/bin/button_exti.rs @@ -19,12 +19,7 @@ async fn main(_spawner: Spawner) { let p = embassy_stm32::init(Default::default()); // Configure the button pin and obtain handler. // On the Nucleo F091RC there is a button connected to pin PC13. - let mut button = ExtiInput::new( - p.PC13, - p.EXTI13, - Pull::Down, - Irqs::as_any::>(), - ); + let mut button = ExtiInput::new(p.PC13, p.EXTI13, Pull::Down, Irqs); info!("Press the USER button..."); loop { diff --git a/examples/stm32f3/src/bin/button_events.rs b/examples/stm32f3/src/bin/button_events.rs index e52622d55..643f499ed 100644 --- a/examples/stm32f3/src/bin/button_events.rs +++ b/examples/stm32f3/src/bin/button_events.rs @@ -105,12 +105,7 @@ static CHANNEL: Channel = Channel::new(); #[embassy_executor::main] async fn main(spawner: Spawner) { let p = embassy_stm32::init(Default::default()); - let button = ExtiInput::new( - p.PA0, - p.EXTI0, - Pull::Down, - Irqs::as_any::>(), - ); + let button = ExtiInput::new(p.PA0, p.EXTI0, Pull::Down, Irqs); info!("Press the USER button..."); let leds = [ Output::new(p.PE9, Level::Low, Speed::Low), diff --git a/examples/stm32f3/src/bin/button_exti.rs b/examples/stm32f3/src/bin/button_exti.rs index 4a75e031c..1df4735ca 100644 --- a/examples/stm32f3/src/bin/button_exti.rs +++ b/examples/stm32f3/src/bin/button_exti.rs @@ -18,12 +18,7 @@ async fn main(_spawner: Spawner) { let p = embassy_stm32::init(Default::default()); info!("Hello World!"); - let mut button = ExtiInput::new( - p.PA0, - p.EXTI0, - Pull::Down, - Irqs::as_any::>(), - ); + let mut button = ExtiInput::new(p.PA0, p.EXTI0, Pull::Down, Irqs); info!("Press the USER button..."); diff --git a/examples/stm32f4/src/bin/button_exti.rs b/examples/stm32f4/src/bin/button_exti.rs index 93560ed06..e7e1549a8 100644 --- a/examples/stm32f4/src/bin/button_exti.rs +++ b/examples/stm32f4/src/bin/button_exti.rs @@ -18,12 +18,7 @@ async fn main(_spawner: Spawner) { let p = embassy_stm32::init(Default::default()); info!("Hello World!"); - let mut button = ExtiInput::new( - p.PC13, - p.EXTI13, - Pull::Down, - Irqs::as_any::>(), - ); + let mut button = ExtiInput::new(p.PC13, p.EXTI13, Pull::Down, Irqs); info!("Press the USER button..."); diff --git a/examples/stm32f4/src/bin/eth_w5500.rs b/examples/stm32f4/src/bin/eth_w5500.rs index f59f1d3b5..e274d2a66 100644 --- a/examples/stm32f4/src/bin/eth_w5500.rs +++ b/examples/stm32f4/src/bin/eth_w5500.rs @@ -76,12 +76,7 @@ async fn main(spawner: Spawner) -> ! { let cs = Output::new(p.PA4, Level::High, Speed::VeryHigh); let spi = unwrap!(ExclusiveDevice::new(spi, cs, Delay)); - let w5500_int = ExtiInput::new( - p.PB0, - p.EXTI0, - Pull::Up, - Irqs::as_any::>(), - ); + let w5500_int = ExtiInput::new(p.PB0, p.EXTI0, Pull::Up, Irqs); let w5500_reset = Output::new(p.PB1, Level::High, Speed::VeryHigh); let mac_addr = [0x02, 234, 3, 4, 82, 231]; diff --git a/examples/stm32f4/src/bin/usb_hid_keyboard.rs b/examples/stm32f4/src/bin/usb_hid_keyboard.rs index b5a7e86e4..2d834dcf7 100644 --- a/examples/stm32f4/src/bin/usb_hid_keyboard.rs +++ b/examples/stm32f4/src/bin/usb_hid_keyboard.rs @@ -124,12 +124,7 @@ async fn main(_spawner: Spawner) { let (reader, mut writer) = hid.split(); - let mut button = ExtiInput::new( - p.PC13, - p.EXTI13, - Pull::Down, - Irqs::as_any::>(), - ); + let mut button = ExtiInput::new(p.PC13, p.EXTI13, Pull::Down, Irqs); // Do stuff with the class! let in_fut = async { diff --git a/examples/stm32f7/src/bin/button_exti.rs b/examples/stm32f7/src/bin/button_exti.rs index 93560ed06..e7e1549a8 100644 --- a/examples/stm32f7/src/bin/button_exti.rs +++ b/examples/stm32f7/src/bin/button_exti.rs @@ -18,12 +18,7 @@ async fn main(_spawner: Spawner) { let p = embassy_stm32::init(Default::default()); info!("Hello World!"); - let mut button = ExtiInput::new( - p.PC13, - p.EXTI13, - Pull::Down, - Irqs::as_any::>(), - ); + let mut button = ExtiInput::new(p.PC13, p.EXTI13, Pull::Down, Irqs); info!("Press the USER button..."); diff --git a/examples/stm32g0/src/bin/button_exti.rs b/examples/stm32g0/src/bin/button_exti.rs index 52fdc39c5..9d54479da 100644 --- a/examples/stm32g0/src/bin/button_exti.rs +++ b/examples/stm32g0/src/bin/button_exti.rs @@ -18,12 +18,7 @@ async fn main(_spawner: Spawner) { let p = embassy_stm32::init(Default::default()); info!("Hello World!"); - let mut button = ExtiInput::new( - p.PC13, - p.EXTI13, - Pull::Up, - Irqs::as_any::>(), - ); + let mut button = ExtiInput::new(p.PC13, p.EXTI13, Pull::Up, Irqs); info!("Press the USER button..."); diff --git a/examples/stm32g4/src/bin/button_exti.rs b/examples/stm32g4/src/bin/button_exti.rs index 93560ed06..e7e1549a8 100644 --- a/examples/stm32g4/src/bin/button_exti.rs +++ b/examples/stm32g4/src/bin/button_exti.rs @@ -18,12 +18,7 @@ async fn main(_spawner: Spawner) { let p = embassy_stm32::init(Default::default()); info!("Hello World!"); - let mut button = ExtiInput::new( - p.PC13, - p.EXTI13, - Pull::Down, - Irqs::as_any::>(), - ); + let mut button = ExtiInput::new(p.PC13, p.EXTI13, Pull::Down, Irqs); info!("Press the USER button..."); diff --git a/examples/stm32h5/src/bin/button_exti.rs b/examples/stm32h5/src/bin/button_exti.rs index 70dee04be..220f89228 100644 --- a/examples/stm32h5/src/bin/button_exti.rs +++ b/examples/stm32h5/src/bin/button_exti.rs @@ -18,12 +18,7 @@ async fn main(_spawner: Spawner) { let p = embassy_stm32::init(Default::default()); info!("Hello World!"); - let mut button = ExtiInput::new( - p.PC13, - p.EXTI13, - Pull::Down, - Irqs::as_any::>(), - ); + let mut button = ExtiInput::new(p.PC13, p.EXTI13, Pull::Down, Irqs); info!("Press the USER button..."); diff --git a/examples/stm32h7/src/bin/button_exti.rs b/examples/stm32h7/src/bin/button_exti.rs index 93560ed06..e7e1549a8 100644 --- a/examples/stm32h7/src/bin/button_exti.rs +++ b/examples/stm32h7/src/bin/button_exti.rs @@ -18,12 +18,7 @@ async fn main(_spawner: Spawner) { let p = embassy_stm32::init(Default::default()); info!("Hello World!"); - let mut button = ExtiInput::new( - p.PC13, - p.EXTI13, - Pull::Down, - Irqs::as_any::>(), - ); + let mut button = ExtiInput::new(p.PC13, p.EXTI13, Pull::Down, Irqs); info!("Press the USER button..."); diff --git a/examples/stm32h7rs/src/bin/button_exti.rs b/examples/stm32h7rs/src/bin/button_exti.rs index 29159a3b3..d63290d42 100644 --- a/examples/stm32h7rs/src/bin/button_exti.rs +++ b/examples/stm32h7rs/src/bin/button_exti.rs @@ -18,12 +18,7 @@ async fn main(_spawner: Spawner) { let p = embassy_stm32::init(Default::default()); info!("Hello World!"); - let mut button = ExtiInput::new( - p.PC13, - p.EXTI13, - Pull::Up, - Irqs::as_any::>(), - ); + let mut button = ExtiInput::new(p.PC13, p.EXTI13, Pull::Up, Irqs); info!("Press the USER button..."); diff --git a/examples/stm32l0/src/bin/button_exti.rs b/examples/stm32l0/src/bin/button_exti.rs index 043261347..a118c7a5a 100644 --- a/examples/stm32l0/src/bin/button_exti.rs +++ b/examples/stm32l0/src/bin/button_exti.rs @@ -18,12 +18,7 @@ async fn main(_spawner: Spawner) { let config = Config::default(); let p = embassy_stm32::init(config); - let mut button = ExtiInput::new( - p.PB2, - p.EXTI2, - Pull::Up, - Irqs::as_any::>(), - ); + let mut button = ExtiInput::new(p.PB2, p.EXTI2, Pull::Up, Irqs); info!("Press the USER button..."); diff --git a/examples/stm32l4/src/bin/button_exti.rs b/examples/stm32l4/src/bin/button_exti.rs index 82479eb24..c84b11dab 100644 --- a/examples/stm32l4/src/bin/button_exti.rs +++ b/examples/stm32l4/src/bin/button_exti.rs @@ -18,12 +18,7 @@ async fn main(_spawner: Spawner) { let p = embassy_stm32::init(Default::default()); info!("Hello World!"); - let mut button = ExtiInput::new( - p.PC13, - p.EXTI13, - Pull::Up, - Irqs::as_any::>(), - ); + let mut button = ExtiInput::new(p.PC13, p.EXTI13, Pull::Up, Irqs); info!("Press the USER button..."); diff --git a/examples/stm32l4/src/bin/spe_adin1110_http_server.rs b/examples/stm32l4/src/bin/spe_adin1110_http_server.rs index e079f22c7..8f2510cdc 100644 --- a/examples/stm32l4/src/bin/spe_adin1110_http_server.rs +++ b/examples/stm32l4/src/bin/spe_adin1110_http_server.rs @@ -127,12 +127,7 @@ async fn main(spawner: Spawner) { let spe_cfg1 = Input::new(dp.PC9, Pull::None); let _spe_ts_capt = Output::new(dp.PC6, Level::Low, Speed::Low); - let spe_int = ExtiInput::new( - dp.PB11, - dp.EXTI11, - Pull::None, - Irqs::as_any::>(), - ); + let spe_int = ExtiInput::new(dp.PB11, dp.EXTI11, Pull::None, Irqs); let spe_spi_cs_n = Output::new(dp.PB12, Level::High, Speed::High); let spe_spi_sclk = dp.PB13; diff --git a/examples/stm32l5/src/bin/button_exti.rs b/examples/stm32l5/src/bin/button_exti.rs index 41412b00f..225a7b3fd 100644 --- a/examples/stm32l5/src/bin/button_exti.rs +++ b/examples/stm32l5/src/bin/button_exti.rs @@ -18,12 +18,7 @@ async fn main(_spawner: Spawner) { let p = embassy_stm32::init(Default::default()); info!("Hello World!"); - let mut button = ExtiInput::new( - p.PC13, - p.EXTI13, - Pull::Down, - Irqs::as_any::>(), - ); + let mut button = ExtiInput::new(p.PC13, p.EXTI13, Pull::Down, Irqs); info!("Press the USER button..."); diff --git a/examples/stm32n6/src/bin/blinky.rs b/examples/stm32n6/src/bin/blinky.rs index 764b8d6ef..a8baf16af 100644 --- a/examples/stm32n6/src/bin/blinky.rs +++ b/examples/stm32n6/src/bin/blinky.rs @@ -28,12 +28,7 @@ async fn main(spawner: Spawner) { info!("Hello World!"); let mut led = Output::new(p.PG10, Level::High, Speed::Low); - let button = ExtiInput::new( - p.PC13, - p.EXTI13, - Pull::Up, - Irqs::as_any::>(), - ); + let button = ExtiInput::new(p.PC13, p.EXTI13, Pull::Up, Irqs); spawner.spawn(button_task(button).unwrap()); diff --git a/examples/stm32u0/src/bin/button_exti.rs b/examples/stm32u0/src/bin/button_exti.rs index 52fdc39c5..9d54479da 100644 --- a/examples/stm32u0/src/bin/button_exti.rs +++ b/examples/stm32u0/src/bin/button_exti.rs @@ -18,12 +18,7 @@ async fn main(_spawner: Spawner) { let p = embassy_stm32::init(Default::default()); info!("Hello World!"); - let mut button = ExtiInput::new( - p.PC13, - p.EXTI13, - Pull::Up, - Irqs::as_any::>(), - ); + let mut button = ExtiInput::new(p.PC13, p.EXTI13, Pull::Up, Irqs); info!("Press the USER button..."); diff --git a/examples/stm32wb/src/bin/button_exti.rs b/examples/stm32wb/src/bin/button_exti.rs index e2698450a..3c58eb556 100644 --- a/examples/stm32wb/src/bin/button_exti.rs +++ b/examples/stm32wb/src/bin/button_exti.rs @@ -18,12 +18,7 @@ async fn main(_spawner: Spawner) { let p = embassy_stm32::init(Default::default()); info!("Hello World!"); - let mut button = ExtiInput::new( - p.PC4, - p.EXTI4, - Pull::Up, - Irqs::as_any::>(), - ); + let mut button = ExtiInput::new(p.PC4, p.EXTI4, Pull::Up, Irqs); info!("Press the USER button..."); diff --git a/examples/stm32wba/src/bin/button_exti.rs b/examples/stm32wba/src/bin/button_exti.rs index 29159a3b3..d63290d42 100644 --- a/examples/stm32wba/src/bin/button_exti.rs +++ b/examples/stm32wba/src/bin/button_exti.rs @@ -18,12 +18,7 @@ async fn main(_spawner: Spawner) { let p = embassy_stm32::init(Default::default()); info!("Hello World!"); - let mut button = ExtiInput::new( - p.PC13, - p.EXTI13, - Pull::Up, - Irqs::as_any::>(), - ); + let mut button = ExtiInput::new(p.PC13, p.EXTI13, Pull::Up, Irqs); info!("Press the USER button..."); diff --git a/examples/stm32wba6/src/bin/button_exti.rs b/examples/stm32wba6/src/bin/button_exti.rs index 29159a3b3..d63290d42 100644 --- a/examples/stm32wba6/src/bin/button_exti.rs +++ b/examples/stm32wba6/src/bin/button_exti.rs @@ -18,12 +18,7 @@ async fn main(_spawner: Spawner) { let p = embassy_stm32::init(Default::default()); info!("Hello World!"); - let mut button = ExtiInput::new( - p.PC13, - p.EXTI13, - Pull::Up, - Irqs::as_any::>(), - ); + let mut button = ExtiInput::new(p.PC13, p.EXTI13, Pull::Up, Irqs); info!("Press the USER button..."); diff --git a/examples/stm32wl/src/bin/button_exti.rs b/examples/stm32wl/src/bin/button_exti.rs index c22de0322..2bb39c709 100644 --- a/examples/stm32wl/src/bin/button_exti.rs +++ b/examples/stm32wl/src/bin/button_exti.rs @@ -23,12 +23,7 @@ async fn main(_spawner: Spawner) { let p = embassy_stm32::init_primary(Default::default(), &SHARED_DATA); info!("Hello World!"); - let mut button = ExtiInput::new( - p.PA0, - p.EXTI0, - Pull::Up, - Irqs::as_any::>(), - ); + let mut button = ExtiInput::new(p.PA0, p.EXTI0, Pull::Up, Irqs); info!("Press the USER button..."); diff --git a/examples/stm32wle5/src/bin/button_exti.rs b/examples/stm32wle5/src/bin/button_exti.rs index 538801990..f248b6147 100644 --- a/examples/stm32wle5/src/bin/button_exti.rs +++ b/examples/stm32wle5/src/bin/button_exti.rs @@ -69,12 +69,7 @@ async fn async_main(_spawner: Spawner) { info!("Hello World!"); - let mut button = ExtiInput::new( - p.PA0, - p.EXTI0, - Pull::Up, - Irqs::as_any::>(), - ); + let mut button = ExtiInput::new(p.PA0, p.EXTI0, Pull::Up, Irqs); info!("Press the USER button..."); -- cgit From b877b0dc6b5fe4541a45e6b43ed9d82131608aee Mon Sep 17 00:00:00 2001 From: WillaWillNot Date: Sat, 22 Nov 2025 18:18:04 -0500 Subject: Build script now injects EXTI2 => EXTI2_TSC peripheral/interrupt mapping if it's not present in the PAC, removed macro magic in exti that was working around this omission --- embassy-stm32/CHANGELOG.md | 2 +- embassy-stm32/build.rs | 21 +++++++++++++++++---- embassy-stm32/src/exti.rs | 18 ++++-------------- 3 files changed, 22 insertions(+), 19 deletions(-) diff --git a/embassy-stm32/CHANGELOG.md b/embassy-stm32/CHANGELOG.md index 0f8113863..a66b2d437 100644 --- a/embassy-stm32/CHANGELOG.md +++ b/embassy-stm32/CHANGELOG.md @@ -77,7 +77,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - feat: stm32/flash: add async support for h7 family - feat: exti brought in line with other drivers' interrupt rebinding system ([#4922](https://github.com/embassy-rs/embassy/pull/4922)) - removal: ExtiInput no longer accepts AnyPin/AnyChannel; AnyChannel removed entirely -- change: build script now generates `unimpl_tsc` cfg option when a chip has a TSC peripheral but no driver +- fix: build script ensures EXTI2_TSC is listed as the IRQ of EXTI2 even if the PAC doesn't - feat: stm32/lcd: added implementation ## 0.4.0 - 2025-08-26 diff --git a/embassy-stm32/build.rs b/embassy-stm32/build.rs index 3d0b13fe2..109571e8f 100644 --- a/embassy-stm32/build.rs +++ b/embassy-stm32/build.rs @@ -56,16 +56,12 @@ fn main() { eprintln!("chip: {chip_name}"); - cfgs.declare("unimpl_tsc"); for p in METADATA.peripherals { if let Some(r) = &p.registers { cfgs.enable(r.kind); foreach_version_cfg(&mut cfgs, r.kind, r.version, |cfgs, cfg_name| { cfgs.enable(cfg_name); }); - } else if p.name == "TSC" { - //Even if the registers are missing, EXTI needs to know if TSC is present in silicon to know whether the EXTI2 interrupt is shadowed by EXTI2_TSC - cfgs.enable("unimpl_tsc") } } @@ -357,8 +353,13 @@ fn main() { // ======== // Generate interrupt declarations + let mut exti2_tsc_shared_int_present: Option = None; let mut irqs = Vec::new(); for irq in METADATA.interrupts { + // The PAC doesn't ensure this is listed as the IRQ of EXTI2, so we must do so + if irq.name == "EXTI2_TSC" { + exti2_tsc_shared_int_present = Some(irq.clone()) + } irqs.push(format_ident!("{}", irq.name)); } @@ -1816,7 +1817,19 @@ fn main() { for p in METADATA.peripherals { let mut pt = TokenStream::new(); + let mut exti2_tsc_injected = false; + if let Some(ref irq) = exti2_tsc_shared_int_present + && p.name == "EXTI" + { + exti2_tsc_injected = true; + let iname = format_ident!("{}", irq.name); + let sname = format_ident!("{}", "EXTI2"); + pt.extend(quote!(pub type #sname = crate::interrupt::typelevel::#iname;)); + } for irq in p.interrupts { + if exti2_tsc_injected && irq.signal == "EXTI2" { + continue; + } let iname = format_ident!("{}", irq.interrupt); let sname = format_ident!("{}", irq.signal); pt.extend(quote!(pub type #sname = crate::interrupt::typelevel::#iname;)); diff --git a/embassy-stm32/src/exti.rs b/embassy-stm32/src/exti.rs index 57217034e..7b7896d46 100644 --- a/embassy-stm32/src/exti.rs +++ b/embassy-stm32/src/exti.rs @@ -395,40 +395,30 @@ pub struct AnyChannel { macro_rules! impl_exti { ($type:ident, $number:expr) => { - impl_exti!(@inner $type, $number, crate::_generated::peripheral_interrupts::EXTI::$type); - }; - ($type:ident, $number:expr, @tsc) => { - impl_exti!(@inner $type, $number, crate::_generated::peripheral_interrupts::TSC::GLOBAL); - }; - (@inner $type:ident, $number:expr, $irq:path) => { impl SealedChannel for crate::peripherals::$type {} impl Channel for crate::peripherals::$type { fn number(&self) -> PinNumber { $number } fn irq(&self) -> InterruptEnum { - <$irq>::IRQ + crate::_generated::peripheral_interrupts::EXTI::$type::IRQ } - type IRQ = $irq; + type IRQ = crate::_generated::peripheral_interrupts::EXTI::$type; } //Still here to surface deprecation messages to the user - remove when removing AnyChannel #[allow(deprecated)] impl From for AnyChannel { fn from(_val: crate::peripherals::$type) -> Self { - Self { - number: $number, + Self { number: $number } } - }} + } }; } impl_exti!(EXTI0, 0); impl_exti!(EXTI1, 1); -#[cfg(not(any(tsc, unimpl_tsc)))] impl_exti!(EXTI2, 2); -#[cfg(any(tsc, unimpl_tsc))] -impl_exti!(EXTI2, 2, @tsc); impl_exti!(EXTI3, 3); impl_exti!(EXTI4, 4); impl_exti!(EXTI5, 5); -- cgit From 36f4075b9054576ccf6dba2dedb08c62484a0599 Mon Sep 17 00:00:00 2001 From: Eicke Hecht Date: Sun, 23 Nov 2025 12:29:56 +0100 Subject: fix: Fix waveform for channels > 1 and disallow for unsupported dmas --- embassy-stm32/src/timer/low_level.rs | 12 +++++-- examples/stm32f7/src/bin/pwm.rs | 65 ++++++++++++++++++++++++++++++++++++ 2 files changed, 74 insertions(+), 3 deletions(-) create mode 100644 examples/stm32f7/src/bin/pwm.rs diff --git a/embassy-stm32/src/timer/low_level.rs b/embassy-stm32/src/timer/low_level.rs index 670298d23..307d614bf 100644 --- a/embassy-stm32/src/timer/low_level.rs +++ b/embassy-stm32/src/timer/low_level.rs @@ -781,7 +781,7 @@ impl<'d, T: GeneralInstance4Channel> Timer<'d, T> { let cc_channel = C::CHANNEL; let original_cc_dma_on_update = self.get_cc_dma_selection() == Ccds::ON_UPDATE; - let original_cc_dma_enabled = self.get_c fsc_dma_enable_state(cc_channel); + let original_cc_dma_enabled = self.get_cc_dma_enable_state(cc_channel); // redirect CC DMA request onto Update Event if !original_cc_dma_on_update { @@ -810,7 +810,12 @@ impl<'d, T: GeneralInstance4Channel> Timer<'d, T> { /// Generate a sequence of PWM waveform that will run continously /// You may want to start this in a new thread as this will block forever + pub async fn waveform_continuous(&mut self, dma: Peri<'_, impl super::Dma>, duty: &[u16]) { + + #[cfg(any(bdma, gpdma))] + panic!("unsupported DMA"); + use crate::pac::timer::vals::Ccds; #[allow(clippy::let_unit_value)] // eg. stm32f334 @@ -871,6 +876,7 @@ impl<'d, T: GeneralInstance4Channel> Timer<'d, T> { fifo_threshold: Some(FifoThreshold::Full), #[cfg(not(any(bdma, gpdma)))] mburst: Burst::Incr8, + #[cfg(not(any(bdma, gpdma)))] circular: circular, ..Default::default() }; @@ -881,7 +887,7 @@ impl<'d, T: GeneralInstance4Channel> Timer<'d, T> { dma, req, duty, - self.regs_1ch().ccr(channel.index()).as_ptr() as *mut u16, + self.regs_gp16().ccr(channel.index()).as_ptr() as *mut u16, dma_transfer_option, ) .await @@ -896,7 +902,7 @@ impl<'d, T: GeneralInstance4Channel> Timer<'d, T> { dma, req, duty, - self.regs_1ch().ccr(channel.index()).as_ptr() as *mut u32, + self.regs_gp16().ccr(channel.index()).as_ptr() as *mut u32, dma_transfer_option, ) .await diff --git a/examples/stm32f7/src/bin/pwm.rs b/examples/stm32f7/src/bin/pwm.rs new file mode 100644 index 000000000..872d99859 --- /dev/null +++ b/examples/stm32f7/src/bin/pwm.rs @@ -0,0 +1,65 @@ +#![no_std] +#![no_main] + +use defmt::{panic, *}; +use embassy_executor::Spawner; +use embassy_futures::join::join; +use embassy_stm32::time::Hertz; +use embassy_stm32::{Config, peripherals}; +use embassy_stm32::gpio::OutputType; +use embassy_stm32::time::mhz; +use embassy_stm32::timer::simple_pwm::{PwmPin, SimplePwm}; +use embassy_time::Timer; +use {defmt_rtt as _, panic_probe as _}; + +// If you are trying this and your USB device doesn't connect, the most +// common issues are the RCC config and vbus_detection +// +// See https://embassy.dev/book/#_the_usb_examples_are_not_working_on_my_board_is_there_anything_else_i_need_to_configure +// for more information. +#[embassy_executor::main] +async fn main(_spawner: Spawner) { + info!("Hello World!"); + + let mut config = Config::default(); + { + use embassy_stm32::rcc::*; + config.rcc.hse = Some(Hse { + freq: Hertz(8_000_000), + mode: HseMode::Bypass, + }); + config.rcc.pll_src = PllSource::HSE; + config.rcc.pll = Some(Pll { + prediv: PllPreDiv::DIV4, + mul: PllMul::MUL200, + divp: Some(PllPDiv::DIV2), // 8mhz / 4 * 200 / 2 = 200Mhz + divq: Some(PllQDiv::DIV4), // 8mhz / 4 * 200 / 4 = 100Mhz + divr: None, + }); + config.rcc.ahb_pre = AHBPrescaler::DIV1; + config.rcc.apb1_pre = APBPrescaler::DIV4; + config.rcc.apb2_pre = APBPrescaler::DIV2; + config.rcc.sys = Sysclk::PLL1_P; + } + let p = embassy_stm32::init(config); + let ch1_pin = PwmPin::new(p.PE9, OutputType::PushPull); + let ch2_pin = PwmPin::new(p.PE11, OutputType::PushPull); + let mut pwm = SimplePwm::new(p.TIM1, Some(ch1_pin), Some(ch2_pin), None, None, mhz(1), Default::default()); + let mut ch1 = pwm.ch1(); + ch1.enable(); + info!("PWM initialized"); + info!("PWM max duty {}", ch1.max_duty_cycle()); + + info!("PWM duty on channel 1 (D6) 50%"); + ch1.set_duty_cycle_fraction(1, 2); + info!("PWM waveform on channel 2 (D5)"); + const max_duty: usize = 200; + let mut duty = [0u16;max_duty]; + for i in 0..max_duty { + duty[i] = i as u16; + } + pwm.waveform_continuous::(p.DMA2_CH6, &duty).await; + + +} + -- cgit From 51c31d1d604046836f60ff9c22aa184a97bc9778 Mon Sep 17 00:00:00 2001 From: xoviat Date: Sun, 23 Nov 2025 11:29:36 -0600 Subject: stm32: update pac --- embassy-stm32/Cargo.toml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/embassy-stm32/Cargo.toml b/embassy-stm32/Cargo.toml index fbd00895d..e10409112 100644 --- a/embassy-stm32/Cargo.toml +++ b/embassy-stm32/Cargo.toml @@ -200,11 +200,11 @@ aligned = "0.4.1" heapless = "0.9.1" #stm32-metapac = { version = "18" } -stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-61abfb115273dc66ed99bf14545779a2aa0722c8" } +stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-f61ed017ef12ec84ff04c49e3147694bda3b29cb" } [build-dependencies] #stm32-metapac = { version = "18", default-features = false, features = ["metadata"]} -stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-61abfb115273dc66ed99bf14545779a2aa0722c8", default-features = false, features = ["metadata"] } +stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-f61ed017ef12ec84ff04c49e3147694bda3b29cb", default-features = false, features = ["metadata"] } proc-macro2 = "1.0.36" quote = "1.0.15" -- cgit From 06076c5461c9a4de00704ee05099dd65ec5111ea Mon Sep 17 00:00:00 2001 From: xoviat Date: Sun, 23 Nov 2025 11:33:23 -0600 Subject: fix vscode fmt settings --- .vscode/settings.json | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/.vscode/settings.json b/.vscode/settings.json index 3c9cce18b..73dfa53a4 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -5,6 +5,9 @@ "[markdown]": { "editor.formatOnSave": false }, + "rust-analyzer.rustfmt.extraArgs": [ + "+nightly" + ], "rust-analyzer.check.allTargets": false, "rust-analyzer.check.noDefaultFeatures": true, "rust-analyzer.cargo.noDefaultFeatures": true, @@ -61,8 +64,4 @@ // "examples/stm32wl/Cargo.toml", // "examples/wasm/Cargo.toml", ], - "rust-analyzer.rustfmt.extraArgs": [ - //Uncomment to run rustfmt with nightly-only settings that match the CI - // "+nightly" - ], } \ No newline at end of file -- cgit From 25cd9603ed738e3e4b90b2644a4323d57de71216 Mon Sep 17 00:00:00 2001 From: xoviat Date: Sun, 23 Nov 2025 12:15:57 -0600 Subject: wpan: use ptr arithmatic --- embassy-stm32-wpan/src/cmd.rs | 21 +++++++++++++-------- embassy-stm32-wpan/src/evt.rs | 1 + embassy-stm32-wpan/src/shci.rs | 18 +++++++++++++++++- embassy-stm32-wpan/src/sub/sys.rs | 17 +++++------------ embassy-stm32/src/ipcc.rs | 2 +- 5 files changed, 37 insertions(+), 22 deletions(-) diff --git a/embassy-stm32-wpan/src/cmd.rs b/embassy-stm32-wpan/src/cmd.rs index 5c81a4aa7..787c22c4b 100644 --- a/embassy-stm32-wpan/src/cmd.rs +++ b/embassy-stm32-wpan/src/cmd.rs @@ -1,4 +1,5 @@ use core::ptr; +use core::sync::atomic::{Ordering, compiler_fence}; use crate::PacketHeader; use crate::consts::TlPacketType; @@ -45,11 +46,11 @@ pub struct CmdPacket { impl CmdPacket { pub unsafe fn write_into(cmd_buf: *mut CmdPacket, packet_type: TlPacketType, cmd_code: u16, payload: &[u8]) { - let p_cmd_serial = &mut (*cmd_buf).cmdserial as *mut _ as *mut CmdSerialStub; - let p_payload = &mut (*cmd_buf).cmdserial.cmd.payload as *mut _; + let p_cmd_serial = (cmd_buf as *mut u8).add(size_of::()); + let p_payload = p_cmd_serial.add(size_of::()); - ptr::write_volatile( - p_cmd_serial, + ptr::write_unaligned( + p_cmd_serial as *mut _, CmdSerialStub { ty: packet_type as u8, cmd_code, @@ -58,6 +59,8 @@ impl CmdPacket { ); ptr::copy_nonoverlapping(payload as *const _ as *const u8, p_payload, payload.len()); + + compiler_fence(Ordering::Release); } } @@ -87,11 +90,11 @@ pub struct AclDataPacket { impl AclDataPacket { pub unsafe fn write_into(cmd_buf: *mut AclDataPacket, packet_type: TlPacketType, handle: u16, payload: &[u8]) { - let p_cmd_serial = &mut (*cmd_buf).acl_data_serial as *mut _ as *mut AclDataSerialStub; - let p_payload = &mut (*cmd_buf).acl_data_serial.acl_data as *mut _; + let p_cmd_serial = (cmd_buf as *mut u8).add(size_of::()); + let p_payload = p_cmd_serial.add(size_of::()); - ptr::write_volatile( - p_cmd_serial, + ptr::write_unaligned( + p_cmd_serial as *mut _, AclDataSerialStub { ty: packet_type as u8, handle: handle, @@ -100,5 +103,7 @@ impl AclDataPacket { ); ptr::copy_nonoverlapping(payload as *const _ as *const u8, p_payload, payload.len()); + + compiler_fence(Ordering::Release); } } diff --git a/embassy-stm32-wpan/src/evt.rs b/embassy-stm32-wpan/src/evt.rs index c6528413d..f32821269 100644 --- a/embassy-stm32-wpan/src/evt.rs +++ b/embassy-stm32-wpan/src/evt.rs @@ -67,6 +67,7 @@ pub struct EvtSerial { pub struct EvtStub { pub kind: u8, pub evt_code: u8, + pub payload_len: u8, } /// This format shall be used for all events (asynchronous and command response) reported diff --git a/embassy-stm32-wpan/src/shci.rs b/embassy-stm32-wpan/src/shci.rs index 30d689716..e93f55598 100644 --- a/embassy-stm32-wpan/src/shci.rs +++ b/embassy-stm32-wpan/src/shci.rs @@ -1,6 +1,10 @@ -use core::{mem, slice}; +use core::sync::atomic::{Ordering, compiler_fence}; +use core::{mem, ptr, slice}; +use crate::PacketHeader; +use crate::cmd::CmdPacket; use crate::consts::{TL_CS_EVT_SIZE, TL_EVT_HEADER_SIZE, TL_PACKET_HEADER_SIZE}; +use crate::evt::{CcEvt, EvtStub}; const SHCI_OGF: u16 = 0x3F; @@ -21,6 +25,18 @@ pub enum SchiCommandStatus { ShciFusCmdNotSupported = 0xFF, } +impl SchiCommandStatus { + pub unsafe fn from_packet(cmd_buf: *const CmdPacket) -> Result { + let p_cmd_serial = (cmd_buf as *mut u8).add(size_of::()); + let p_evt_payload = p_cmd_serial.add(size_of::()); + + compiler_fence(Ordering::Acquire); + let cc_evt = ptr::read_unaligned(p_evt_payload as *const CcEvt); + + cc_evt.payload[0].try_into() + } +} + impl TryFrom for SchiCommandStatus { type Error = (); diff --git a/embassy-stm32-wpan/src/sub/sys.rs b/embassy-stm32-wpan/src/sub/sys.rs index 8a3382f86..549811685 100644 --- a/embassy-stm32-wpan/src/sub/sys.rs +++ b/embassy-stm32-wpan/src/sub/sys.rs @@ -1,10 +1,9 @@ -use core::ptr; - use crate::cmd::CmdPacket; use crate::consts::TlPacketType; -use crate::evt::{CcEvt, EvtBox, EvtPacket}; -#[allow(unused_imports)] -use crate::shci::{SchiCommandStatus, ShciBleInitCmdParam, ShciOpcode}; +use crate::evt::EvtBox; +#[cfg(feature = "ble")] +use crate::shci::ShciBleInitCmdParam; +use crate::shci::{SchiCommandStatus, ShciOpcode}; use crate::sub::mm; use crate::tables::{SysTable, WirelessFwInfoTable}; use crate::unsafe_linked_list::LinkedListNode; @@ -50,13 +49,7 @@ impl Sys { self.write(opcode, payload).await; Ipcc::flush(channels::cpu1::IPCC_SYSTEM_CMD_RSP_CHANNEL).await; - unsafe { - let p_event_packet = SYS_CMD_BUF.as_ptr() as *const EvtPacket; - let p_command_event = &((*p_event_packet).evt_serial.evt.payload) as *const _ as *const CcEvt; - let p_payload = &((*p_command_event).payload) as *const u8; - - ptr::read_volatile(p_payload).try_into() - } + unsafe { SchiCommandStatus::from_packet(SYS_CMD_BUF.as_ptr()) } } #[cfg(feature = "mac")] diff --git a/embassy-stm32/src/ipcc.rs b/embassy-stm32/src/ipcc.rs index e1d8b1c2a..b9be1b1d2 100644 --- a/embassy-stm32/src/ipcc.rs +++ b/embassy-stm32/src/ipcc.rs @@ -147,7 +147,7 @@ impl Ipcc { // If bit is set to 1 then interrupt is disabled; we want to enable the interrupt regs.cpu(0).mr().modify(|w| w.set_chfm(channel as usize, false)); - compiler_fence(Ordering::SeqCst); + compiler_fence(Ordering::Release); if !regs.cpu(0).sr().read().chf(channel as usize) { // If bit is set to 1 then interrupt is disabled; we want to disable the interrupt -- cgit From 08125aa919a4dc4de79f91de9a7f3586e51a7739 Mon Sep 17 00:00:00 2001 From: xoviat Date: Sun, 23 Nov 2025 13:11:18 -0600 Subject: wpan: restructure ipcc and hil test wpan_mac --- ci.sh | 2 +- embassy-stm32-wpan/src/channels.rs | 15 +- embassy-stm32-wpan/src/lib.rs | 46 +++-- embassy-stm32-wpan/src/mac/commands.rs | 9 +- embassy-stm32-wpan/src/mac/control.rs | 4 +- embassy-stm32-wpan/src/mac/driver.rs | 6 +- embassy-stm32-wpan/src/mac/event.rs | 6 +- embassy-stm32-wpan/src/mac/runner.rs | 12 +- embassy-stm32-wpan/src/shci.rs | 134 +++++++------- embassy-stm32-wpan/src/sub/ble.rs | 85 +++++---- embassy-stm32-wpan/src/sub/mac.rs | 144 ++++++++------- embassy-stm32-wpan/src/sub/mm.rs | 37 ++-- embassy-stm32-wpan/src/sub/sys.rs | 82 +++++---- embassy-stm32-wpan/src/tables.rs | 50 +++--- embassy-stm32/src/ipcc.rs | 250 ++++++++++++++++----------- examples/stm32wb/src/bin/eddystone_beacon.rs | 116 ++++++------- examples/stm32wb/src/bin/gatt_server.rs | 118 ++++++------- examples/stm32wb/src/bin/mac_ffd.rs | 47 +++-- examples/stm32wb/src/bin/mac_ffd_net.rs | 9 +- examples/stm32wb/src/bin/mac_rfd.rs | 40 ++--- examples/stm32wb/src/bin/tl_mbox.rs | 2 +- examples/stm32wb/src/bin/tl_mbox_ble.rs | 21 ++- examples/stm32wb/src/bin/tl_mbox_mac.rs | 12 +- tests/stm32/src/bin/wpan_ble.rs | 120 ++++++------- tests/stm32/src/bin/wpan_mac.rs | 29 ++-- 25 files changed, 736 insertions(+), 660 deletions(-) diff --git a/ci.sh b/ci.sh index 6cc2a031d..b4ed0dc18 100755 --- a/ci.sh +++ b/ci.sh @@ -35,7 +35,7 @@ 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_mac rm out/tests/stm32wb55rg/wpan_ble # unstable, I think it's running out of RAM? diff --git a/embassy-stm32-wpan/src/channels.rs b/embassy-stm32-wpan/src/channels.rs index 9a2be1cfa..58f857136 100644 --- a/embassy-stm32-wpan/src/channels.rs +++ b/embassy-stm32-wpan/src/channels.rs @@ -48,9 +48,20 @@ //! |<----HW_IPCC_TRACES_CHANNEL----------------------| //! | | //! +//! + +#[repr(u8)] +pub enum IpccChannel { + Channel1 = 1, + Channel2 = 2, + Channel3 = 3, + Channel4 = 4, + Channel5 = 5, + Channel6 = 6, +} pub mod cpu1 { - use embassy_stm32::ipcc::IpccChannel; + use super::IpccChannel; pub const IPCC_BLE_CMD_CHANNEL: IpccChannel = IpccChannel::Channel1; pub const IPCC_SYSTEM_CMD_RSP_CHANNEL: IpccChannel = IpccChannel::Channel2; @@ -70,7 +81,7 @@ pub mod cpu1 { } pub mod cpu2 { - use embassy_stm32::ipcc::IpccChannel; + use super::IpccChannel; pub const IPCC_BLE_EVENT_CHANNEL: IpccChannel = IpccChannel::Channel1; pub const IPCC_SYSTEM_EVENT_CHANNEL: IpccChannel = IpccChannel::Channel2; diff --git a/embassy-stm32-wpan/src/lib.rs b/embassy-stm32-wpan/src/lib.rs index f6b1f6021..18b0feada 100644 --- a/embassy-stm32-wpan/src/lib.rs +++ b/embassy-stm32-wpan/src/lib.rs @@ -26,7 +26,7 @@ use core::sync::atomic::{Ordering, compiler_fence}; use embassy_hal_internal::Peri; use embassy_stm32::interrupt; -use embassy_stm32::ipcc::{Config, Ipcc, ReceiveInterruptHandler, TransmitInterruptHandler}; +use embassy_stm32::ipcc::{Config, Ipcc, IpccRxChannel, ReceiveInterruptHandler, TransmitInterruptHandler}; use embassy_stm32::peripherals::IPCC; use sub::mm::MemoryManager; use sub::sys::Sys; @@ -53,14 +53,13 @@ type PacketHeader = LinkedListNode; /// Transport Layer for the Mailbox interface pub struct TlMbox<'d> { - _ipcc: Peri<'d, IPCC>, - - pub sys_subsystem: Sys, - pub mm_subsystem: MemoryManager, + pub sys_subsystem: Sys<'d>, + pub mm_subsystem: MemoryManager<'d>, #[cfg(feature = "ble")] - pub ble_subsystem: sub::ble::Ble, + pub ble_subsystem: sub::ble::Ble<'d>, #[cfg(feature = "mac")] - pub mac_subsystem: sub::mac::Mac, + pub mac_subsystem: sub::mac::Mac<'d>, + pub traces: IpccRxChannel<'d>, } impl<'d> TlMbox<'d> { @@ -92,7 +91,7 @@ impl<'d> TlMbox<'d> { /// be handled by `TL_BLE_Init`; see Figure 66). This completes the procedure laid out in /// Figure 66. // TODO: document what the user should do after calling init to use the mac_802_15_4 subsystem - pub fn init( + pub async fn init( ipcc: Peri<'d, IPCC>, _irqs: impl interrupt::typelevel::Binding + interrupt::typelevel::Binding, @@ -185,16 +184,35 @@ impl<'d> TlMbox<'d> { compiler_fence(Ordering::SeqCst); // this is equivalent to `HW_IPCC_Enable`, which is called by `TL_Enable` - Ipcc::enable(config); + let [ + (_hw_ipcc_ble_cmd_channel, _ipcc_ble_event_channel), + (ipcc_system_cmd_rsp_channel, ipcc_system_event_channel), + (_ipcc_mac_802_15_4_cmd_rsp_channel, _ipcc_mac_802_15_4_notification_ack_channel), + (ipcc_mm_release_buffer_channel, _ipcc_traces_channel), + (_ipcc_ble_lld_cmd_channel, _ipcc_ble_lld_rsp_channel), + (_ipcc_hci_acl_data_channel, _), + ] = Ipcc::new(ipcc, _irqs, config).split(); + + let mm = sub::mm::MemoryManager::new(ipcc_mm_release_buffer_channel); + let mut sys = sub::sys::Sys::new(ipcc_system_cmd_rsp_channel, ipcc_system_event_channel); + + debug!("sys event: {}", sys.read().await.payload()); Self { - _ipcc: ipcc, - sys_subsystem: sub::sys::Sys::new(), + sys_subsystem: sys, #[cfg(feature = "ble")] - ble_subsystem: sub::ble::Ble::new(), + ble_subsystem: sub::ble::Ble::new( + _hw_ipcc_ble_cmd_channel, + _ipcc_ble_event_channel, + _ipcc_hci_acl_data_channel, + ), #[cfg(feature = "mac")] - mac_subsystem: sub::mac::Mac::new(), - mm_subsystem: sub::mm::MemoryManager::new(), + mac_subsystem: sub::mac::Mac::new( + _ipcc_mac_802_15_4_cmd_rsp_channel, + _ipcc_mac_802_15_4_notification_ack_channel, + ), + mm_subsystem: mm, + traces: _ipcc_traces_channel, } } } diff --git a/embassy-stm32-wpan/src/mac/commands.rs b/embassy-stm32-wpan/src/mac/commands.rs index a3a40f377..d96f0094a 100644 --- a/embassy-stm32-wpan/src/mac/commands.rs +++ b/embassy-stm32-wpan/src/mac/commands.rs @@ -393,9 +393,14 @@ impl<'a, T: AsRef<[u8]>> TryFrom> for DataRequest { dst_pan_id: frame.dst_pan_id().ok_or(())?.into(), dst_address: frame.dst_addr().ok_or(())?.into(), msdu_handle: frame.sequence_number().ok_or(())?, - ack_tx: 0x00, + key_source: frame.key_source().unwrap_or_default().try_into().unwrap_or_default(), + ack_tx: frame.ack_request() as u8, gts_tx: false, - security_level: SecurityLevel::Unsecure, + security_level: if frame.security_enabled() { + SecurityLevel::Secured + } else { + SecurityLevel::Unsecure + }, ..Default::default() }; diff --git a/embassy-stm32-wpan/src/mac/control.rs b/embassy-stm32-wpan/src/mac/control.rs index d2a7b65ee..14c6fdd2b 100644 --- a/embassy-stm32-wpan/src/mac/control.rs +++ b/embassy-stm32-wpan/src/mac/control.rs @@ -20,7 +20,7 @@ use crate::sub::mac::MacTx; pub struct Control<'a> { rx_event_channel: &'a ZeroCopyPubSub>, - mac_tx: &'a Mutex, + mac_tx: &'a Mutex>, #[allow(unused)] network_state: &'a blocking_mutex::Mutex>, } @@ -28,7 +28,7 @@ pub struct Control<'a> { impl<'a> Control<'a> { pub(crate) fn new( rx_event_channel: &'a ZeroCopyPubSub>, - mac_tx: &'a Mutex, + mac_tx: &'a Mutex>, network_state: &'a blocking_mutex::Mutex>, ) -> Self { Self { diff --git a/embassy-stm32-wpan/src/mac/driver.rs b/embassy-stm32-wpan/src/mac/driver.rs index c43d595b7..41171ce3d 100644 --- a/embassy-stm32-wpan/src/mac/driver.rs +++ b/embassy-stm32-wpan/src/mac/driver.rs @@ -37,8 +37,8 @@ impl NetworkState { } pub struct DriverState<'d> { - pub mac_tx: Mutex, - pub mac_rx: MacRx, + pub mac_tx: Mutex>, + pub mac_rx: MacRx<'d>, pub rx_event_channel: ZeroCopyPubSub>, pub rx_data_channel: Channel, 1>, pub tx_data_channel: Channel, @@ -48,7 +48,7 @@ pub struct DriverState<'d> { } impl<'d> DriverState<'d> { - pub const fn new(mac: Mac) -> Self { + pub const fn new(mac: Mac<'d>) -> Self { let (mac_rx, mac_tx) = mac.split(); let mac_tx = Mutex::new(mac_tx); diff --git a/embassy-stm32-wpan/src/mac/event.rs b/embassy-stm32-wpan/src/mac/event.rs index 9ca4f5a2a..39856e185 100644 --- a/embassy-stm32-wpan/src/mac/event.rs +++ b/embassy-stm32-wpan/src/mac/event.rs @@ -10,7 +10,7 @@ use super::responses::{ }; use crate::evt::{EvtBox, MemoryManager}; use crate::mac::opcodes::OpcodeM0ToM4; -use crate::sub::mac::{self, Mac}; +use crate::sub::mac::{self, MacRx}; pub(crate) trait ParseableMacEvent: Sized { fn from_buffer<'a>(buf: &'a [u8]) -> Result<&'a Self, ()> { @@ -53,7 +53,7 @@ pub enum MacEvent<'a> { } impl<'a> MacEvent<'a> { - pub(crate) fn new(event_box: EvtBox) -> Result { + pub(crate) fn new(event_box: EvtBox) -> Result { let payload = event_box.payload(); let opcode = u16::from_le_bytes(payload[0..2].try_into().unwrap()); @@ -148,6 +148,6 @@ unsafe impl<'a> Send for MacEvent<'a> {} impl<'a> Drop for MacEvent<'a> { fn drop(&mut self) { - unsafe { mac::Mac::drop_event_packet(ptr::null_mut()) }; + unsafe { mac::MacRx::drop_event_packet(ptr::null_mut()) }; } } diff --git a/embassy-stm32-wpan/src/mac/runner.rs b/embassy-stm32-wpan/src/mac/runner.rs index acca70019..3b7d895df 100644 --- a/embassy-stm32-wpan/src/mac/runner.rs +++ b/embassy-stm32-wpan/src/mac/runner.rs @@ -24,11 +24,11 @@ pub struct Runner<'a> { // therefore, we don't need to worry about overwriting events rx_event_channel: &'a ZeroCopyPubSub>, rx_data_channel: &'a Channel, 1>, - mac_rx: &'a mut MacRx, + mac_rx: Mutex>, tx_data_channel: &'a Channel, tx_buf_channel: &'a Channel, - mac_tx: &'a Mutex, + mac_tx: &'a Mutex>, #[allow(unused)] network_state: &'a blocking_mutex::Mutex>, @@ -38,10 +38,10 @@ impl<'a> Runner<'a> { pub(crate) fn new( rx_event_channel: &'a ZeroCopyPubSub>, rx_data_channel: &'a Channel, 1>, - mac_rx: &'a mut MacRx, + mac_rx: &'a mut MacRx<'a>, tx_data_channel: &'a Channel, tx_buf_channel: &'a Channel, - mac_tx: &'a Mutex, + mac_tx: &'a Mutex>, tx_buf_queue: &'a mut [[u8; MTU]; BUF_SIZE], network_state: &'a blocking_mutex::Mutex>, short_address: [u8; 2], @@ -61,7 +61,7 @@ impl<'a> Runner<'a> { Self { rx_event_channel, rx_data_channel, - mac_rx, + mac_rx: Mutex::new(mac_rx), tx_data_channel, tx_buf_channel, mac_tx, @@ -83,7 +83,7 @@ impl<'a> Runner<'a> { join::join( async { loop { - if let Ok(mac_event) = self.mac_rx.read().await { + if let Ok(mac_event) = self.mac_rx.try_lock().unwrap().read().await { match mac_event { MacEvent::MlmeAssociateCnf(_) | MacEvent::MlmeDisassociateCnf(_) diff --git a/embassy-stm32-wpan/src/shci.rs b/embassy-stm32-wpan/src/shci.rs index 30d689716..1946c55fd 100644 --- a/embassy-stm32-wpan/src/shci.rs +++ b/embassy-stm32-wpan/src/shci.rs @@ -274,69 +274,64 @@ pub struct ShciBleInitCmdParam { pub options: u8, /// Reserved for future use - shall be set to 0 pub hw_version: u8, - // /** - // * Maximum number of connection-oriented channels in initiator mode. - // * Range: 0 .. 64 - // */ - // pub max_coc_initiator_nbr: u8, - // - // /** - // * Minimum transmit power in dBm supported by the Controller. - // * Range: -127 .. 20 - // */ - // pub min_tx_power: i8, - // - // /** - // * Maximum transmit power in dBm supported by the Controller. - // * Range: -127 .. 20 - // */ - // pub max_tx_power: i8, - // - // /** - // * RX model configuration - // * - bit 0: 1: agc_rssi model improved vs RF blockers 0: Legacy agc_rssi model - // * - other bits: reserved ( shall be set to 0) - // */ - // pub rx_model_config: u8, - // - // /* Maximum number of advertising sets. - // * Range: 1 .. 8 with limitation: - // * This parameter is linked to max_adv_data_len such as both compliant with allocated Total memory computed with BLE_EXT_ADV_BUFFER_SIZE based - // * on Max Extended advertising configuration supported. - // * This parameter is considered by the CPU2 when Options has SHCI_C2_BLE_INIT_OPTIONS_EXT_ADV flag set - // */ - // pub max_adv_set_nbr: u8, - // - // /* Maximum advertising data length (in bytes) - // * Range: 31 .. 1650 with limitation: - // * This parameter is linked to max_adv_set_nbr such as both compliant with allocated Total memory computed with BLE_EXT_ADV_BUFFER_SIZE based - // * on Max Extended advertising configuration supported. - // * This parameter is considered by the CPU2 when Options has SHCI_C2_BLE_INIT_OPTIONS_EXT_ADV flag set - // */ - // pub max_adv_data_len: u16, - // - // /* RF TX Path Compensation Value (16-bit signed integer). Units: 0.1 dB. - // * Range: -1280 .. 1280 - // */ - // pub tx_path_compens: i16, - // - // /* RF RX Path Compensation Value (16-bit signed integer). Units: 0.1 dB. - // * Range: -1280 .. 1280 - // */ - // pub rx_path_compens: i16, - // - // /* BLE core specification version (8-bit unsigned integer). - // * values as: 11(5.2), 12(5.3) - // */ - // pub ble_core_version: u8, - // - // /** - // * Options flags extension - // * - bit 0: 1: appearance Writable 0: appearance Read-Only - // * - bit 1: 1: Enhanced ATT supported 0: Enhanced ATT not supported - // * - other bits: reserved ( shall be set to 0) - // */ - // pub options_extension: u8, + /// + /// Maximum number of connection-oriented channels in initiator mode. + /// Range: 0 .. 64 + pub max_coc_initiator_nbr: u8, + + /// + /// Minimum transmit power in dBm supported by the Controller. + /// Range: -127 .. 20 + pub min_tx_power: i8, + + /// + /// Maximum transmit power in dBm supported by the Controller. + /// Range: -127 .. 20 + pub max_tx_power: i8, + + /// + /// RX model configuration + /// - bit 0: 1: agc_rssi model improved vs RF blockers 0: Legacy agc_rssi model + /// - other bits: reserved ( shall be set to 0) + pub rx_model_config: u8, + + /// Maximum number of advertising sets. + /// Range: 1 .. 8 with limitation: + /// This parameter is linked to max_adv_data_len such as both compliant with allocated Total memory computed with BLE_EXT_ADV_BUFFER_SIZE based + /// on Max Extended advertising configuration supported. + /// This parameter is considered by the CPU2 when Options has SHCI_C2_BLE_INIT_OPTIONS_EXT_ADV flag set + pub max_adv_set_nbr: u8, + + /// Maximum advertising data length (in bytes) + /// Range: 31 .. 1650 with limitation: + /// This parameter is linked to max_adv_set_nbr such as both compliant with allocated Total memory computed with BLE_EXT_ADV_BUFFER_SIZE based + /// on Max Extended advertising configuration supported. + /// This parameter is considered by the CPU2 when Options has SHCI_C2_BLE_INIT_OPTIONS_EXT_ADV flag set + pub max_adv_data_len: u16, + + /// RF TX Path Compensation Value (16-bit signed integer). Units: 0.1 dB. + /// Range: -1280 .. 1280 + pub tx_path_compens: i16, + + //// RF RX Path Compensation Value (16-bit signed integer). Units: 0.1 dB. + /// Range: -1280 .. 1280 + pub rx_path_compens: i16, + + /// BLE core specification version (8-bit unsigned integer). + /// values as: 11(5.2), 12(5.3) + pub ble_core_version: u8, + + /// Options flags extension + /// - bit 0: 1: appearance Writable 0: appearance Read-Only + /// - bit 1: 1: Enhanced ATT supported 0: Enhanced ATT not supported + /// - other bits: reserved ( shall be set to 0) + pub options_extension: u8, + + /// MaxAddEattBearers + /// Maximum number of bearers that can be created for Enhanced ATT + /// in addition to the number of links + /// - Range: 0 .. 4 + pub max_add_eatt_bearers: u8, } impl ShciBleInitCmdParam { @@ -351,7 +346,7 @@ impl Default for ShciBleInitCmdParam { p_ble_buffer_address: 0, ble_buffer_size: 0, num_attr_record: 68, - num_attr_serv: 8, + num_attr_serv: 4, attr_value_arr_size: 1344, num_of_links: 2, extended_packet_length_enable: 1, @@ -366,6 +361,17 @@ impl Default for ShciBleInitCmdParam { viterbi_enable: 1, options: 0, hw_version: 0, + max_coc_initiator_nbr: 32, + min_tx_power: -40, + max_tx_power: 6, + rx_model_config: 0, + max_adv_set_nbr: 2, + max_adv_data_len: 1650, + tx_path_compens: 0, + rx_path_compens: 0, + ble_core_version: 11, + options_extension: 0, + max_add_eatt_bearers: 4, } } } diff --git a/embassy-stm32-wpan/src/sub/ble.rs b/embassy-stm32-wpan/src/sub/ble.rs index cd69a0479..d5ed2ca28 100644 --- a/embassy-stm32-wpan/src/sub/ble.rs +++ b/embassy-stm32-wpan/src/sub/ble.rs @@ -1,6 +1,6 @@ use core::ptr; -use embassy_stm32::ipcc::Ipcc; +use embassy_stm32::ipcc::{IpccRxChannel, IpccTxChannel}; use hci::Opcode; use crate::cmd::CmdPacket; @@ -9,7 +9,7 @@ use crate::evt::{EvtBox, EvtPacket, EvtStub}; use crate::sub::mm; use crate::tables::{BLE_CMD_BUFFER, BleTable, CS_BUFFER, EVT_QUEUE, HCI_ACL_DATA_BUFFER, TL_BLE_TABLE}; use crate::unsafe_linked_list::LinkedListNode; -use crate::{channels, evt}; +use crate::evt; /// A guard that, once constructed, may be used to send BLE commands to CPU2. /// @@ -36,15 +36,21 @@ use crate::{channels, evt}; /// # mbox.ble_subsystem.reset().await; /// # let _reset_response = mbox.ble_subsystem.read().await; /// ``` -pub struct Ble { - _private: (), +pub struct Ble<'a> { + hw_ipcc_ble_cmd_channel: IpccTxChannel<'a>, + ipcc_ble_event_channel: IpccRxChannel<'a>, + ipcc_hci_acl_data_channel: IpccTxChannel<'a>, } -impl Ble { +impl<'a> Ble<'a> { /// Constructs a guard that allows for BLE commands to be sent to CPU2. /// /// This takes the place of `TL_BLE_Init`, completing that step as laid out in AN5289, Fig 66. - pub(crate) fn new() -> Self { + pub(crate) fn new( + hw_ipcc_ble_cmd_channel: IpccTxChannel<'a>, + ipcc_ble_event_channel: IpccRxChannel<'a>, + ipcc_hci_acl_data_channel: IpccTxChannel<'a>, + ) -> Self { unsafe { LinkedListNode::init_head(EVT_QUEUE.as_mut_ptr()); @@ -56,44 +62,51 @@ impl Ble { }); } - Self { _private: () } + Self { + hw_ipcc_ble_cmd_channel, + ipcc_ble_event_channel, + ipcc_hci_acl_data_channel, + } } /// `HW_IPCC_BLE_EvtNot` - pub async fn tl_read(&self) -> EvtBox { - Ipcc::receive(channels::cpu2::IPCC_BLE_EVENT_CHANNEL, || unsafe { - if let Some(node_ptr) = LinkedListNode::remove_head(EVT_QUEUE.as_mut_ptr()) { - Some(EvtBox::new(node_ptr.cast())) - } else { - None - } - }) - .await + pub async fn tl_read(&mut self) -> EvtBox { + self.ipcc_ble_event_channel + .receive(|| unsafe { + if let Some(node_ptr) = LinkedListNode::remove_head(EVT_QUEUE.as_mut_ptr()) { + Some(EvtBox::new(node_ptr.cast())) + } else { + None + } + }) + .await } /// `TL_BLE_SendCmd` - pub async fn tl_write(&self, opcode: u16, payload: &[u8]) { - Ipcc::send(channels::cpu1::IPCC_BLE_CMD_CHANNEL, || unsafe { - CmdPacket::write_into(BLE_CMD_BUFFER.as_mut_ptr(), TlPacketType::BleCmd, opcode, payload); - }) - .await; + pub async fn tl_write(&mut self, opcode: u16, payload: &[u8]) { + self.hw_ipcc_ble_cmd_channel + .send(|| unsafe { + CmdPacket::write_into(BLE_CMD_BUFFER.as_mut_ptr(), TlPacketType::BleCmd, opcode, payload); + }) + .await; } /// `TL_BLE_SendAclData` - pub async fn acl_write(&self, handle: u16, payload: &[u8]) { - Ipcc::send(channels::cpu1::IPCC_HCI_ACL_DATA_CHANNEL, || unsafe { - CmdPacket::write_into( - HCI_ACL_DATA_BUFFER.as_mut_ptr() as *mut _, - TlPacketType::AclData, - handle, - payload, - ); - }) - .await; + pub async fn acl_write(&mut self, handle: u16, payload: &[u8]) { + self.ipcc_hci_acl_data_channel + .send(|| unsafe { + CmdPacket::write_into( + HCI_ACL_DATA_BUFFER.as_mut_ptr() as *mut _, + TlPacketType::AclData, + handle, + payload, + ); + }) + .await; } } -impl evt::MemoryManager for Ble { +impl<'a> evt::MemoryManager for Ble<'a> { /// SAFETY: passing a pointer to something other than a managed event packet is UB unsafe fn drop_event_packet(evt: *mut EvtPacket) { let stub = unsafe { @@ -110,13 +123,17 @@ impl evt::MemoryManager for Ble { pub extern crate stm32wb_hci as hci; -impl hci::Controller for Ble { +impl<'a> hci::Controller for Ble<'a> { async fn controller_write(&mut self, opcode: Opcode, payload: &[u8]) { self.tl_write(opcode.0, payload).await; } + #[allow(invalid_reference_casting)] async fn controller_read_into(&self, buf: &mut [u8]) { - let evt_box = self.tl_read().await; + // A complete hack since I cannot update the trait + let s = unsafe { &mut *(self as *const _ as *mut Ble) }; + + let evt_box = s.tl_read().await; let evt_serial = evt_box.serial(); buf[..evt_serial.len()].copy_from_slice(evt_serial); diff --git a/embassy-stm32-wpan/src/sub/mac.rs b/embassy-stm32-wpan/src/sub/mac.rs index 93cafbc72..ce2903e61 100644 --- a/embassy-stm32-wpan/src/sub/mac.rs +++ b/embassy-stm32-wpan/src/sub/mac.rs @@ -4,67 +4,78 @@ use core::sync::atomic::{AtomicBool, Ordering}; use core::task::Poll; use embassy_futures::poll_once; -use embassy_stm32::ipcc::Ipcc; +use embassy_stm32::ipcc::{Ipcc, IpccRxChannel, IpccTxChannel}; use embassy_sync::waitqueue::AtomicWaker; use crate::cmd::CmdPacket; use crate::consts::TlPacketType; +use crate::evt; use crate::evt::{EvtBox, EvtPacket}; use crate::mac::commands::MacCommand; use crate::mac::event::MacEvent; use crate::mac::typedefs::MacError; use crate::tables::{MAC_802_15_4_CMD_BUFFER, MAC_802_15_4_NOTIF_RSP_EVT_BUFFER}; -use crate::{channels, evt}; +use crate::unsafe_linked_list::LinkedListNode; static MAC_WAKER: AtomicWaker = AtomicWaker::new(); static MAC_EVT_OUT: AtomicBool = AtomicBool::new(false); -pub struct Mac { - _private: (), +pub struct Mac<'a> { + ipcc_mac_802_15_4_cmd_rsp_channel: IpccTxChannel<'a>, + ipcc_mac_802_15_4_notification_ack_channel: IpccRxChannel<'a>, } -impl Mac { - pub(crate) fn new() -> Self { - Self { _private: () } - } - - pub const fn split(self) -> (MacRx, MacTx) { - (MacRx { _private: () }, MacTx { _private: () }) - } - - pub async fn tl_write_and_get_response(&self, opcode: u16, payload: &[u8]) -> u8 { - MacTx { _private: () }.tl_write_and_get_response(opcode, payload).await - } - - pub async fn tl_write(&self, opcode: u16, payload: &[u8]) { - MacTx { _private: () }.tl_write(opcode, payload).await - } - - pub async fn send_command(&self, cmd: &T) -> Result<(), MacError> - where - T: MacCommand, - { - MacTx { _private: () }.send_command(cmd).await - } +impl<'a> Mac<'a> { + pub(crate) fn new( + ipcc_mac_802_15_4_cmd_rsp_channel: IpccTxChannel<'a>, + ipcc_mac_802_15_4_notification_ack_channel: IpccRxChannel<'a>, + ) -> Self { + use crate::tables::{ + MAC_802_15_4_CMD_BUFFER, MAC_802_15_4_NOTIF_RSP_EVT_BUFFER, Mac802_15_4Table, TL_MAC_802_15_4_TABLE, + TL_TRACES_TABLE, TRACES_EVT_QUEUE, TracesTable, + }; - pub async fn tl_read(&self) -> EvtBox { - MacRx { _private: () }.tl_read().await + unsafe { + LinkedListNode::init_head(TRACES_EVT_QUEUE.as_mut_ptr() as *mut _); + + TL_TRACES_TABLE.as_mut_ptr().write_volatile(TracesTable { + traces_queue: TRACES_EVT_QUEUE.as_ptr() as *const _, + }); + + TL_MAC_802_15_4_TABLE.as_mut_ptr().write_volatile(Mac802_15_4Table { + p_cmdrsp_buffer: MAC_802_15_4_CMD_BUFFER.as_mut_ptr().cast(), + p_notack_buffer: MAC_802_15_4_NOTIF_RSP_EVT_BUFFER.as_mut_ptr().cast(), + evt_queue: core::ptr::null_mut(), + }); + }; + + Self { + ipcc_mac_802_15_4_cmd_rsp_channel, + ipcc_mac_802_15_4_notification_ack_channel, + } } - pub async fn read(&self) -> Result, ()> { - MacRx { _private: () }.read().await + pub const fn split(self) -> (MacRx<'a>, MacTx<'a>) { + ( + MacRx { + ipcc_mac_802_15_4_notification_ack_channel: self.ipcc_mac_802_15_4_notification_ack_channel, + }, + MacTx { + ipcc_mac_802_15_4_cmd_rsp_channel: self.ipcc_mac_802_15_4_cmd_rsp_channel, + }, + ) } } -pub struct MacTx { - _private: (), +pub struct MacTx<'a> { + ipcc_mac_802_15_4_cmd_rsp_channel: IpccTxChannel<'a>, } -impl MacTx { +impl<'a> MacTx<'a> { /// `HW_IPCC_MAC_802_15_4_CmdEvtNot` - pub async fn tl_write_and_get_response(&self, opcode: u16, payload: &[u8]) -> u8 { + pub async fn tl_write_and_get_response(&mut self, opcode: u16, payload: &[u8]) -> u8 { self.tl_write(opcode, payload).await; - Ipcc::flush(channels::cpu1::IPCC_MAC_802_15_4_CMD_RSP_CHANNEL).await; + self.ipcc_mac_802_15_4_cmd_rsp_channel.flush().await; unsafe { let p_event_packet = MAC_802_15_4_CMD_BUFFER.as_ptr() as *const EvtPacket; @@ -75,19 +86,20 @@ impl MacTx { } /// `TL_MAC_802_15_4_SendCmd` - pub async fn tl_write(&self, opcode: u16, payload: &[u8]) { - Ipcc::send(channels::cpu1::IPCC_MAC_802_15_4_CMD_RSP_CHANNEL, || unsafe { - CmdPacket::write_into( - MAC_802_15_4_CMD_BUFFER.as_mut_ptr(), - TlPacketType::MacCmd, - opcode, - payload, - ); - }) - .await; + pub async fn tl_write(&mut self, opcode: u16, payload: &[u8]) { + self.ipcc_mac_802_15_4_cmd_rsp_channel + .send(|| unsafe { + CmdPacket::write_into( + MAC_802_15_4_CMD_BUFFER.as_mut_ptr(), + TlPacketType::MacCmd, + opcode, + payload, + ); + }) + .await; } - pub async fn send_command(&self, cmd: &T) -> Result<(), MacError> + pub async fn send_command(&mut self, cmd: &T) -> Result<(), MacError> where T: MacCommand, { @@ -101,19 +113,19 @@ impl MacTx { } } -pub struct MacRx { - _private: (), +pub struct MacRx<'a> { + ipcc_mac_802_15_4_notification_ack_channel: IpccRxChannel<'a>, } -impl MacRx { +impl<'a> MacRx<'a> { /// `HW_IPCC_MAC_802_15_4_EvtNot` /// /// This function will stall if the previous `EvtBox` has not been dropped - pub async fn tl_read(&self) -> EvtBox { + pub async fn tl_read(&mut self) -> EvtBox> { // Wait for the last event box to be dropped poll_fn(|cx| { MAC_WAKER.register(cx.waker()); - if MAC_EVT_OUT.load(Ordering::SeqCst) { + if MAC_EVT_OUT.load(Ordering::Acquire) { Poll::Pending } else { Poll::Ready(()) @@ -122,22 +134,23 @@ impl MacRx { .await; // Return a new event box - Ipcc::receive(channels::cpu2::IPCC_MAC_802_15_4_NOTIFICATION_ACK_CHANNEL, || unsafe { - // The closure is not async, therefore the closure must execute to completion (cannot be dropped) - // Therefore, the event box is guaranteed to be cleaned up if it's not leaked - MAC_EVT_OUT.store(true, Ordering::SeqCst); - - Some(EvtBox::new(MAC_802_15_4_NOTIF_RSP_EVT_BUFFER.as_mut_ptr() as *mut _)) - }) - .await + self.ipcc_mac_802_15_4_notification_ack_channel + .receive(|| unsafe { + // The closure is not async, therefore the closure must execute to completion (cannot be dropped) + // Therefore, the event box is guaranteed to be cleaned up if it's not leaked + MAC_EVT_OUT.store(true, Ordering::SeqCst); + + Some(EvtBox::new(MAC_802_15_4_NOTIF_RSP_EVT_BUFFER.as_mut_ptr() as *mut _)) + }) + .await } - pub async fn read(&self) -> Result, ()> { + pub async fn read<'b>(&mut self) -> Result, ()> { MacEvent::new(self.tl_read().await) } } -impl evt::MemoryManager for Mac { +impl<'a> evt::MemoryManager for MacRx<'a> { /// SAFETY: passing a pointer to something other than a managed event packet is UB unsafe fn drop_event_packet(_: *mut EvtPacket) { trace!("mac drop event"); @@ -151,13 +164,10 @@ impl evt::MemoryManager for Mac { ); // Clear the rx flag - let _ = poll_once(Ipcc::receive::<()>( - channels::cpu2::IPCC_MAC_802_15_4_NOTIFICATION_ACK_CHANNEL, - || None, - )); + let _ = poll_once(Ipcc::receive::<()>(3, || None)); // Allow a new read call - MAC_EVT_OUT.store(false, Ordering::SeqCst); + MAC_EVT_OUT.store(false, Ordering::Release); MAC_WAKER.wake(); } } diff --git a/embassy-stm32-wpan/src/sub/mm.rs b/embassy-stm32-wpan/src/sub/mm.rs index a90c6ee55..aac252929 100644 --- a/embassy-stm32-wpan/src/sub/mm.rs +++ b/embassy-stm32-wpan/src/sub/mm.rs @@ -5,26 +5,26 @@ use core::task::Poll; use aligned::{A4, Aligned}; use cortex_m::interrupt; -use embassy_stm32::ipcc::Ipcc; +use embassy_stm32::ipcc::IpccTxChannel; use embassy_sync::waitqueue::AtomicWaker; use crate::consts::POOL_SIZE; +use crate::evt; use crate::evt::EvtPacket; #[cfg(feature = "ble")] use crate::tables::BLE_SPARE_EVT_BUF; use crate::tables::{EVT_POOL, FREE_BUF_QUEUE, MemManagerTable, SYS_SPARE_EVT_BUF, TL_MEM_MANAGER_TABLE}; use crate::unsafe_linked_list::LinkedListNode; -use crate::{channels, evt}; static MM_WAKER: AtomicWaker = AtomicWaker::new(); static mut LOCAL_FREE_BUF_QUEUE: Aligned> = Aligned(MaybeUninit::uninit()); -pub struct MemoryManager { - _private: (), +pub struct MemoryManager<'a> { + ipcc_mm_release_buffer_channel: IpccTxChannel<'a>, } -impl MemoryManager { - pub(crate) fn new() -> Self { +impl<'a> MemoryManager<'a> { + pub(crate) fn new(ipcc_mm_release_buffer_channel: IpccTxChannel<'a>) -> Self { unsafe { LinkedListNode::init_head(FREE_BUF_QUEUE.as_mut_ptr()); LinkedListNode::init_head(LOCAL_FREE_BUF_QUEUE.as_mut_ptr()); @@ -43,10 +43,12 @@ impl MemoryManager { }); } - Self { _private: () } + Self { + ipcc_mm_release_buffer_channel, + } } - pub async fn run_queue(&self) -> ! { + pub async fn run_queue(&mut self) -> ! { loop { poll_fn(|cx| unsafe { MM_WAKER.register(cx.waker()); @@ -58,20 +60,21 @@ impl MemoryManager { }) .await; - Ipcc::send(channels::cpu1::IPCC_MM_RELEASE_BUFFER_CHANNEL, || { - interrupt::free(|_| unsafe { - // CS required while moving nodes - while let Some(node_ptr) = LinkedListNode::remove_head(LOCAL_FREE_BUF_QUEUE.as_mut_ptr()) { - LinkedListNode::insert_head(FREE_BUF_QUEUE.as_mut_ptr(), node_ptr); - } + self.ipcc_mm_release_buffer_channel + .send(|| { + interrupt::free(|_| unsafe { + // CS required while moving nodes + while let Some(node_ptr) = LinkedListNode::remove_head(LOCAL_FREE_BUF_QUEUE.as_mut_ptr()) { + LinkedListNode::insert_head(FREE_BUF_QUEUE.as_mut_ptr(), node_ptr); + } + }) }) - }) - .await; + .await; } } } -impl evt::MemoryManager for MemoryManager { +impl<'a> evt::MemoryManager for MemoryManager<'a> { /// SAFETY: passing a pointer to something other than a managed event packet is UB unsafe fn drop_event_packet(evt: *mut EvtPacket) { interrupt::free(|_| unsafe { diff --git a/embassy-stm32-wpan/src/sub/sys.rs b/embassy-stm32-wpan/src/sub/sys.rs index 8a3382f86..dffe9942c 100644 --- a/embassy-stm32-wpan/src/sub/sys.rs +++ b/embassy-stm32-wpan/src/sub/sys.rs @@ -1,5 +1,7 @@ use core::ptr; +use embassy_stm32::ipcc::{IpccRxChannel, IpccTxChannel}; + use crate::cmd::CmdPacket; use crate::consts::TlPacketType; use crate::evt::{CcEvt, EvtBox, EvtPacket}; @@ -8,16 +10,20 @@ use crate::shci::{SchiCommandStatus, ShciBleInitCmdParam, ShciOpcode}; use crate::sub::mm; use crate::tables::{SysTable, WirelessFwInfoTable}; use crate::unsafe_linked_list::LinkedListNode; -use crate::{Ipcc, SYS_CMD_BUF, SYSTEM_EVT_QUEUE, TL_DEVICE_INFO_TABLE, TL_SYS_TABLE, channels}; +use crate::{SYS_CMD_BUF, SYSTEM_EVT_QUEUE, TL_DEVICE_INFO_TABLE, TL_SYS_TABLE}; /// A guard that, once constructed, allows for sys commands to be sent to CPU2. -pub struct Sys { - _private: (), +pub struct Sys<'a> { + ipcc_system_cmd_rsp_channel: IpccTxChannel<'a>, + ipcc_system_event_channel: IpccRxChannel<'a>, } -impl Sys { +impl<'a> Sys<'a> { /// TL_Sys_Init - pub(crate) fn new() -> Self { + pub(crate) fn new( + ipcc_system_cmd_rsp_channel: IpccTxChannel<'a>, + ipcc_system_event_channel: IpccRxChannel<'a>, + ) -> Self { unsafe { LinkedListNode::init_head(SYSTEM_EVT_QUEUE.as_mut_ptr()); @@ -27,7 +33,10 @@ impl Sys { }); } - Self { _private: () } + Self { + ipcc_system_cmd_rsp_channel, + ipcc_system_event_channel, + } } /// Returns CPU2 wireless firmware information (if present). @@ -38,17 +47,22 @@ impl Sys { if info.version != 0 { Some(info) } else { None } } - pub async fn write(&self, opcode: ShciOpcode, payload: &[u8]) { - Ipcc::send(channels::cpu1::IPCC_SYSTEM_CMD_RSP_CHANNEL, || unsafe { - CmdPacket::write_into(SYS_CMD_BUF.as_mut_ptr(), TlPacketType::SysCmd, opcode as u16, payload); - }) - .await; + pub async fn write(&mut self, opcode: ShciOpcode, payload: &[u8]) { + self.ipcc_system_cmd_rsp_channel + .send(|| unsafe { + CmdPacket::write_into(SYS_CMD_BUF.as_mut_ptr(), TlPacketType::SysCmd, opcode as u16, payload); + }) + .await; } /// `HW_IPCC_SYS_CmdEvtNot` - pub async fn write_and_get_response(&self, opcode: ShciOpcode, payload: &[u8]) -> Result { + pub async fn write_and_get_response( + &mut self, + opcode: ShciOpcode, + payload: &[u8], + ) -> Result { self.write(opcode, payload).await; - Ipcc::flush(channels::cpu1::IPCC_SYSTEM_CMD_RSP_CHANNEL).await; + self.ipcc_system_cmd_rsp_channel.flush().await; unsafe { let p_event_packet = SYS_CMD_BUF.as_ptr() as *const EvtPacket; @@ -60,26 +74,7 @@ impl Sys { } #[cfg(feature = "mac")] - pub async fn shci_c2_mac_802_15_4_init(&self) -> Result { - use crate::tables::{ - MAC_802_15_4_CMD_BUFFER, MAC_802_15_4_NOTIF_RSP_EVT_BUFFER, Mac802_15_4Table, TL_MAC_802_15_4_TABLE, - TL_TRACES_TABLE, TRACES_EVT_QUEUE, TracesTable, - }; - - unsafe { - LinkedListNode::init_head(TRACES_EVT_QUEUE.as_mut_ptr() as *mut _); - - TL_TRACES_TABLE.as_mut_ptr().write_volatile(TracesTable { - traces_queue: TRACES_EVT_QUEUE.as_ptr() as *const _, - }); - - TL_MAC_802_15_4_TABLE.as_mut_ptr().write_volatile(Mac802_15_4Table { - p_cmdrsp_buffer: MAC_802_15_4_CMD_BUFFER.as_mut_ptr().cast(), - p_notack_buffer: MAC_802_15_4_NOTIF_RSP_EVT_BUFFER.as_mut_ptr().cast(), - evt_queue: core::ptr::null_mut(), - }); - }; - + pub async fn shci_c2_mac_802_15_4_init(&mut self) -> Result { self.write_and_get_response(ShciOpcode::Mac802_15_4Init, &[]).await } @@ -90,7 +85,7 @@ impl Sys { /// `HW_IPCC_SYS_EvtNot`, aka `IoBusCallBackUserEvt` (as detailed in Figure 65), aka /// [crate::sub::ble::hci::host::uart::UartHci::read]. #[cfg(feature = "ble")] - pub async fn shci_c2_ble_init(&self, param: ShciBleInitCmdParam) -> Result { + pub async fn shci_c2_ble_init(&mut self, param: ShciBleInitCmdParam) -> Result { self.write_and_get_response(ShciOpcode::BleInit, param.payload()).await } @@ -99,14 +94,15 @@ impl Sys { /// This method takes the place of the `HW_IPCC_SYS_EvtNot`/`SysUserEvtRx`/`APPE_SysUserEvtRx`, /// as the embassy implementation avoids the need to call C public bindings, and instead /// handles the event channels directly. - pub async fn read(&self) -> EvtBox { - Ipcc::receive(channels::cpu2::IPCC_SYSTEM_EVENT_CHANNEL, || unsafe { - if let Some(node_ptr) = LinkedListNode::remove_head(SYSTEM_EVT_QUEUE.as_mut_ptr()) { - Some(EvtBox::new(node_ptr.cast())) - } else { - None - } - }) - .await + pub async fn read<'b>(&mut self) -> EvtBox> { + self.ipcc_system_event_channel + .receive(|| unsafe { + if let Some(node_ptr) = LinkedListNode::remove_head(SYSTEM_EVT_QUEUE.as_mut_ptr()) { + Some(EvtBox::new(node_ptr.cast())) + } else { + None + } + }) + .await } } diff --git a/embassy-stm32-wpan/src/tables.rs b/embassy-stm32-wpan/src/tables.rs index 1dafed159..20d2c190f 100644 --- a/embassy-stm32-wpan/src/tables.rs +++ b/embassy-stm32-wpan/src/tables.rs @@ -191,93 +191,93 @@ pub struct RefTable { // --------------------- ref table --------------------- #[unsafe(link_section = "TL_REF_TABLE")] -pub static mut TL_REF_TABLE: MaybeUninit = MaybeUninit::uninit(); +pub static mut TL_REF_TABLE: MaybeUninit = MaybeUninit::zeroed(); #[unsafe(link_section = "MB_MEM1")] -pub static mut TL_DEVICE_INFO_TABLE: Aligned> = Aligned(MaybeUninit::uninit()); +pub static mut TL_DEVICE_INFO_TABLE: Aligned> = Aligned(MaybeUninit::zeroed()); #[unsafe(link_section = "MB_MEM1")] -pub static mut TL_BLE_TABLE: Aligned> = Aligned(MaybeUninit::uninit()); +pub static mut TL_BLE_TABLE: Aligned> = Aligned(MaybeUninit::zeroed()); #[unsafe(link_section = "MB_MEM1")] -pub static mut TL_THREAD_TABLE: Aligned> = Aligned(MaybeUninit::uninit()); +pub static mut TL_THREAD_TABLE: Aligned> = Aligned(MaybeUninit::zeroed()); #[unsafe(link_section = "MB_MEM1")] -pub static mut TL_LLD_TESTS_TABLE: Aligned> = Aligned(MaybeUninit::uninit()); +pub static mut TL_LLD_TESTS_TABLE: Aligned> = Aligned(MaybeUninit::zeroed()); #[unsafe(link_section = "MB_MEM1")] -pub static mut TL_BLE_LLD_TABLE: Aligned> = Aligned(MaybeUninit::uninit()); +pub static mut TL_BLE_LLD_TABLE: Aligned> = Aligned(MaybeUninit::zeroed()); #[unsafe(link_section = "MB_MEM1")] -pub static mut TL_SYS_TABLE: Aligned> = Aligned(MaybeUninit::uninit()); +pub static mut TL_SYS_TABLE: Aligned> = Aligned(MaybeUninit::zeroed()); #[unsafe(link_section = "MB_MEM1")] -pub static mut TL_MEM_MANAGER_TABLE: Aligned> = Aligned(MaybeUninit::uninit()); +pub static mut TL_MEM_MANAGER_TABLE: Aligned> = Aligned(MaybeUninit::zeroed()); #[unsafe(link_section = "MB_MEM1")] -pub static mut TL_TRACES_TABLE: Aligned> = Aligned(MaybeUninit::uninit()); +pub static mut TL_TRACES_TABLE: Aligned> = Aligned(MaybeUninit::zeroed()); #[unsafe(link_section = "MB_MEM1")] -pub static mut TL_MAC_802_15_4_TABLE: Aligned> = Aligned(MaybeUninit::uninit()); +pub static mut TL_MAC_802_15_4_TABLE: Aligned> = Aligned(MaybeUninit::zeroed()); #[unsafe(link_section = "MB_MEM1")] -pub static mut TL_ZIGBEE_TABLE: Aligned> = Aligned(MaybeUninit::uninit()); +pub static mut TL_ZIGBEE_TABLE: Aligned> = Aligned(MaybeUninit::zeroed()); // --------------------- tables --------------------- #[unsafe(link_section = "MB_MEM1")] -pub static mut FREE_BUF_QUEUE: Aligned> = Aligned(MaybeUninit::uninit()); +pub static mut FREE_BUF_QUEUE: Aligned> = Aligned(MaybeUninit::zeroed()); #[allow(dead_code)] #[unsafe(link_section = "MB_MEM1")] -pub static mut TRACES_EVT_QUEUE: Aligned> = Aligned(MaybeUninit::uninit()); +pub static mut TRACES_EVT_QUEUE: Aligned> = Aligned(MaybeUninit::zeroed()); #[unsafe(link_section = "MB_MEM2")] pub static mut CS_BUFFER: Aligned> = - Aligned(MaybeUninit::uninit()); + Aligned(MaybeUninit::zeroed()); #[unsafe(link_section = "MB_MEM2")] -pub static mut EVT_QUEUE: Aligned> = Aligned(MaybeUninit::uninit()); +pub static mut EVT_QUEUE: Aligned> = Aligned(MaybeUninit::zeroed()); #[unsafe(link_section = "MB_MEM2")] -pub static mut SYSTEM_EVT_QUEUE: Aligned> = Aligned(MaybeUninit::uninit()); +pub static mut SYSTEM_EVT_QUEUE: Aligned> = Aligned(MaybeUninit::zeroed()); // --------------------- app tables --------------------- #[cfg(feature = "mac")] #[unsafe(link_section = "MB_MEM2")] -pub static mut MAC_802_15_4_CMD_BUFFER: Aligned> = Aligned(MaybeUninit::uninit()); +pub static mut MAC_802_15_4_CMD_BUFFER: Aligned> = Aligned(MaybeUninit::zeroed()); #[cfg(feature = "mac")] #[unsafe(link_section = "MB_MEM2")] pub static mut MAC_802_15_4_NOTIF_RSP_EVT_BUFFER: MaybeUninit< Aligned, -> = MaybeUninit::uninit(); +> = MaybeUninit::zeroed(); #[unsafe(link_section = "MB_MEM2")] -pub static mut EVT_POOL: Aligned> = Aligned(MaybeUninit::uninit()); +pub static mut EVT_POOL: Aligned> = Aligned(MaybeUninit::zeroed()); #[unsafe(link_section = "MB_MEM2")] -pub static mut SYS_CMD_BUF: Aligned> = Aligned(MaybeUninit::uninit()); +pub static mut SYS_CMD_BUF: Aligned> = Aligned(MaybeUninit::zeroed()); #[unsafe(link_section = "MB_MEM2")] pub static mut SYS_SPARE_EVT_BUF: Aligned> = - Aligned(MaybeUninit::uninit()); + Aligned(MaybeUninit::zeroed()); #[cfg(feature = "mac")] #[unsafe(link_section = "MB_MEM2")] pub static mut MAC_802_15_4_CNFINDNOT: Aligned> = - Aligned(MaybeUninit::uninit()); + Aligned(MaybeUninit::zeroed()); #[cfg(feature = "ble")] #[unsafe(link_section = "MB_MEM1")] -pub static mut BLE_CMD_BUFFER: Aligned> = Aligned(MaybeUninit::uninit()); +pub static mut BLE_CMD_BUFFER: Aligned> = Aligned(MaybeUninit::zeroed()); #[cfg(feature = "ble")] #[unsafe(link_section = "MB_MEM2")] pub static mut BLE_SPARE_EVT_BUF: Aligned> = - Aligned(MaybeUninit::uninit()); + Aligned(MaybeUninit::zeroed()); #[cfg(feature = "ble")] #[unsafe(link_section = "MB_MEM2")] // fuck these "magic" numbers from ST ---v---v pub static mut HCI_ACL_DATA_BUFFER: Aligned> = - Aligned(MaybeUninit::uninit()); + Aligned(MaybeUninit::zeroed()); diff --git a/embassy-stm32/src/ipcc.rs b/embassy-stm32/src/ipcc.rs index e1d8b1c2a..10c4a820b 100644 --- a/embassy-stm32/src/ipcc.rs +++ b/embassy-stm32/src/ipcc.rs @@ -1,9 +1,11 @@ //! Inter-Process Communication Controller (IPCC) use core::future::poll_fn; +use core::marker::PhantomData; use core::sync::atomic::{Ordering, compiler_fence}; use core::task::Poll; +use embassy_hal_internal::Peri; use embassy_sync::waitqueue::AtomicWaker; use crate::interrupt::typelevel::Interrupt; @@ -17,25 +19,16 @@ impl interrupt::typelevel::Handler for Receive unsafe fn on_interrupt() { let regs = IPCC::regs(); - let channels = [ - IpccChannel::Channel1, - IpccChannel::Channel2, - IpccChannel::Channel3, - IpccChannel::Channel4, - IpccChannel::Channel5, - IpccChannel::Channel6, - ]; - // Status register gives channel occupied status. For rx, use cpu1. let sr = regs.cpu(1).sr().read(); regs.cpu(0).mr().modify(|w| { - for channel in channels { - if sr.chf(channel as usize) { + for index in 0..5 { + if sr.chf(index as usize) { // If bit is set to 1 then interrupt is disabled; we want to disable the interrupt - w.set_chom(channel as usize, true); + w.set_chom(index as usize, true); // There shouldn't be a race because the channel is masked only if the interrupt has fired - IPCC::state().rx_waker_for(channel).wake(); + IPCC::state().rx_waker_for(index).wake(); } } }) @@ -49,25 +42,16 @@ impl interrupt::typelevel::Handler for Transmi unsafe fn on_interrupt() { let regs = IPCC::regs(); - let channels = [ - IpccChannel::Channel1, - IpccChannel::Channel2, - IpccChannel::Channel3, - IpccChannel::Channel4, - IpccChannel::Channel5, - IpccChannel::Channel6, - ]; - // Status register gives channel occupied status. For tx, use cpu0. let sr = regs.cpu(0).sr().read(); regs.cpu(0).mr().modify(|w| { - for channel in channels { - if !sr.chf(channel as usize) { + for index in 0..5 { + if !sr.chf(index as usize) { // If bit is set to 1 then interrupt is disabled; we want to disable the interrupt - w.set_chfm(channel as usize, true); + w.set_chfm(index as usize, true); // There shouldn't be a race because the channel is masked only if the interrupt has fired - IPCC::state().tx_waker_for(channel).wake(); + IPCC::state().tx_waker_for(index).wake(); } } }); @@ -82,76 +66,55 @@ pub struct Config { // reserved for future use } -/// Channel. -#[allow(missing_docs)] -#[derive(Debug, Clone, Copy)] -#[repr(C)] -pub enum IpccChannel { - Channel1 = 0, - Channel2 = 1, - Channel3 = 2, - Channel4 = 3, - Channel5 = 4, - Channel6 = 5, +/// IPCC TX Channel +pub struct IpccTxChannel<'a> { + index: u8, + _lifetime: PhantomData<&'a mut usize>, } -/// IPCC driver. -pub struct Ipcc; - -impl Ipcc { - /// Enable IPCC. - pub fn enable(_config: Config) { - rcc::enable_and_reset::(); - IPCC::set_cpu2(true); - - let regs = IPCC::regs(); - - regs.cpu(0).cr().modify(|w| { - w.set_rxoie(true); - w.set_txfie(true); - }); - - // enable interrupts - crate::interrupt::typelevel::IPCC_C1_RX::unpend(); - crate::interrupt::typelevel::IPCC_C1_TX::unpend(); +impl<'a> IpccTxChannel<'a> { + pub(crate) const fn new(index: u8) -> Self { + core::assert!(index < 6); - unsafe { crate::interrupt::typelevel::IPCC_C1_RX::enable() }; - unsafe { crate::interrupt::typelevel::IPCC_C1_TX::enable() }; + Self { + index: index, + _lifetime: PhantomData, + } } /// Send data to an IPCC channel. The closure is called to write the data when appropriate. - pub async fn send(channel: IpccChannel, f: impl FnOnce()) { + pub async fn send(&mut self, f: impl FnOnce()) { let regs = IPCC::regs(); - Self::flush(channel).await; + self.flush().await; f(); compiler_fence(Ordering::SeqCst); - trace!("ipcc: ch {}: send data", channel as u8); - regs.cpu(0).scr().write(|w| w.set_chs(channel as usize, true)); + trace!("ipcc: ch {}: send data", self.index as u8); + regs.cpu(0).scr().write(|w| w.set_chs(self.index as usize, true)); } /// Wait for the tx channel to become clear - pub async fn flush(channel: IpccChannel) { + pub async fn flush(&mut self) { let regs = IPCC::regs(); // This is a race, but is nice for debugging - if regs.cpu(0).sr().read().chf(channel as usize) { - trace!("ipcc: ch {}: wait for tx free", channel as u8); + if regs.cpu(0).sr().read().chf(self.index as usize) { + trace!("ipcc: ch {}: wait for tx free", self.index as u8); } poll_fn(|cx| { - IPCC::state().tx_waker_for(channel).register(cx.waker()); + IPCC::state().tx_waker_for(self.index).register(cx.waker()); // If bit is set to 1 then interrupt is disabled; we want to enable the interrupt - regs.cpu(0).mr().modify(|w| w.set_chfm(channel as usize, false)); + regs.cpu(0).mr().modify(|w| w.set_chfm(self.index as usize, false)); compiler_fence(Ordering::SeqCst); - if !regs.cpu(0).sr().read().chf(channel as usize) { + if !regs.cpu(0).sr().read().chf(self.index as usize) { // If bit is set to 1 then interrupt is disabled; we want to disable the interrupt - regs.cpu(0).mr().modify(|w| w.set_chfm(channel as usize, true)); + regs.cpu(0).mr().modify(|w| w.set_chfm(self.index as usize, true)); Poll::Ready(()) } else { @@ -160,27 +123,44 @@ impl Ipcc { }) .await; } +} + +/// IPCC RX Channel +pub struct IpccRxChannel<'a> { + index: u8, + _lifetime: PhantomData<&'a mut usize>, +} + +impl<'a> IpccRxChannel<'a> { + pub(crate) const fn new(index: u8) -> Self { + core::assert!(index < 6); + + Self { + index: index, + _lifetime: PhantomData, + } + } /// Receive data from an IPCC channel. The closure is called to read the data when appropriate. - pub async fn receive(channel: IpccChannel, mut f: impl FnMut() -> Option) -> R { + pub async fn receive(&mut self, mut f: impl FnMut() -> Option) -> R { let regs = IPCC::regs(); loop { // This is a race, but is nice for debugging - if !regs.cpu(1).sr().read().chf(channel as usize) { - trace!("ipcc: ch {}: wait for rx occupied", channel as u8); + if !regs.cpu(1).sr().read().chf(self.index as usize) { + trace!("ipcc: ch {}: wait for rx occupied", self.index as u8); } poll_fn(|cx| { - IPCC::state().rx_waker_for(channel).register(cx.waker()); + IPCC::state().rx_waker_for(self.index).register(cx.waker()); // If bit is set to 1 then interrupt is disabled; we want to enable the interrupt - regs.cpu(0).mr().modify(|w| w.set_chom(channel as usize, false)); + regs.cpu(0).mr().modify(|w| w.set_chom(self.index as usize, false)); compiler_fence(Ordering::SeqCst); - if regs.cpu(1).sr().read().chf(channel as usize) { + if regs.cpu(1).sr().read().chf(self.index as usize) { // If bit is set to 1 then interrupt is disabled; we want to disable the interrupt - regs.cpu(0).mr().modify(|w| w.set_chfm(channel as usize, true)); + regs.cpu(0).mr().modify(|w| w.set_chfm(self.index as usize, true)); Poll::Ready(()) } else { @@ -189,21 +169,111 @@ impl Ipcc { }) .await; - trace!("ipcc: ch {}: read data", channel as u8); + trace!("ipcc: ch {}: read data", self.index as u8); match f() { Some(ret) => return ret, None => {} } - trace!("ipcc: ch {}: clear rx", channel as u8); + trace!("ipcc: ch {}: clear rx", self.index as u8); compiler_fence(Ordering::SeqCst); // If the channel is clear and the read function returns none, fetch more data - regs.cpu(0).scr().write(|w| w.set_chc(channel as usize, true)); + regs.cpu(0).scr().write(|w| w.set_chc(self.index as usize, true)); } } } +/// IPCC Channel +pub struct IpccChannel<'a> { + index: u8, + _lifetime: PhantomData<&'a mut usize>, +} + +impl<'a> IpccChannel<'a> { + pub(crate) const fn new(number: u8) -> Self { + core::assert!(number > 0 && number <= 6); + + Self { + index: number - 1, + _lifetime: PhantomData, + } + } + + /// Split into a tx and rx channel + pub const fn split(self) -> (IpccTxChannel<'a>, IpccRxChannel<'a>) { + (IpccTxChannel::new(self.index), IpccRxChannel::new(self.index)) + } +} + +/// IPCC driver. +pub struct Ipcc { + _private: (), +} + +impl Ipcc { + /// Creates a new HardwareSemaphore instance. + pub fn new<'d>( + _peripheral: Peri<'d, crate::peripherals::IPCC>, + _irq: impl interrupt::typelevel::Binding + + interrupt::typelevel::Binding + + 'd, + _config: Config, + ) -> Self { + rcc::enable_and_reset::(); + IPCC::set_cpu2(true); + + let regs = IPCC::regs(); + + regs.cpu(0).cr().modify(|w| { + w.set_rxoie(true); + w.set_txfie(true); + }); + + // enable interrupts + crate::interrupt::typelevel::IPCC_C1_RX::unpend(); + crate::interrupt::typelevel::IPCC_C1_TX::unpend(); + + unsafe { crate::interrupt::typelevel::IPCC_C1_RX::enable() }; + unsafe { crate::interrupt::typelevel::IPCC_C1_TX::enable() }; + + Self { _private: () } + } + + /// Split into a tx and rx channel + pub const fn split<'a>(self) -> [(IpccTxChannel<'a>, IpccRxChannel<'a>); 6] { + [ + IpccChannel::new(1).split(), + IpccChannel::new(2).split(), + IpccChannel::new(3).split(), + IpccChannel::new(4).split(), + IpccChannel::new(5).split(), + IpccChannel::new(6).split(), + ] + } + + /// Receive from a channel number + pub async unsafe fn receive(number: u8, f: impl FnMut() -> Option) -> R { + core::assert!(number > 0 && number <= 6); + + IpccRxChannel::new(number - 1).receive(f).await + } + + /// Send to a channel number + pub async unsafe fn send(number: u8, f: impl FnOnce()) { + core::assert!(number > 0 && number <= 6); + + IpccTxChannel::new(number - 1).send(f).await + } + + /// Send to a channel number + pub async unsafe fn flush(number: u8) { + core::assert!(number > 0 && number <= 6); + + IpccTxChannel::new(number - 1).flush().await + } +} + impl SealedInstance for crate::peripherals::IPCC { fn regs() -> crate::pac::ipcc::Ipcc { crate::pac::IPCC @@ -232,26 +302,12 @@ impl State { } } - const fn rx_waker_for(&self, channel: IpccChannel) -> &AtomicWaker { - match channel { - IpccChannel::Channel1 => &self.rx_wakers[0], - IpccChannel::Channel2 => &self.rx_wakers[1], - IpccChannel::Channel3 => &self.rx_wakers[2], - IpccChannel::Channel4 => &self.rx_wakers[3], - IpccChannel::Channel5 => &self.rx_wakers[4], - IpccChannel::Channel6 => &self.rx_wakers[5], - } + const fn rx_waker_for(&self, index: u8) -> &AtomicWaker { + &self.rx_wakers[index as usize] } - const fn tx_waker_for(&self, channel: IpccChannel) -> &AtomicWaker { - match channel { - IpccChannel::Channel1 => &self.tx_wakers[0], - IpccChannel::Channel2 => &self.tx_wakers[1], - IpccChannel::Channel3 => &self.tx_wakers[2], - IpccChannel::Channel4 => &self.tx_wakers[3], - IpccChannel::Channel5 => &self.tx_wakers[4], - IpccChannel::Channel6 => &self.tx_wakers[5], - } + const fn tx_waker_for(&self, index: u8) -> &AtomicWaker { + &self.tx_wakers[index as usize] } } diff --git a/examples/stm32wb/src/bin/eddystone_beacon.rs b/examples/stm32wb/src/bin/eddystone_beacon.rs index f309ca3a2..413b1ac8f 100644 --- a/examples/stm32wb/src/bin/eddystone_beacon.rs +++ b/examples/stm32wb/src/bin/eddystone_beacon.rs @@ -57,126 +57,112 @@ async fn main(_spawner: Spawner) { info!("Hello World!"); let config = Config::default(); - let mut mbox = TlMbox::init(p.IPCC, Irqs, config); + let mbox = TlMbox::init(p.IPCC, Irqs, config).await; + let mut sys = mbox.sys_subsystem; + let mut ble = mbox.ble_subsystem; - let sys_event = mbox.sys_subsystem.read().await; - info!("sys event: {}", sys_event.payload()); - - let _ = mbox.sys_subsystem.shci_c2_ble_init(Default::default()).await; + let _ = sys.shci_c2_ble_init(Default::default()).await; info!("resetting BLE..."); - mbox.ble_subsystem.reset().await; - let response = mbox.ble_subsystem.read().await.unwrap(); + ble.reset().await; + let response = ble.read().await.unwrap(); defmt::info!("{}", response); info!("config public address..."); - mbox.ble_subsystem - .write_config_data(&ConfigData::public_address(get_bd_addr()).build()) + ble.write_config_data(&ConfigData::public_address(get_bd_addr()).build()) .await; - let response = mbox.ble_subsystem.read().await.unwrap(); + let response = ble.read().await.unwrap(); defmt::info!("{}", response); info!("config random address..."); - mbox.ble_subsystem - .write_config_data(&ConfigData::random_address(get_random_addr()).build()) + ble.write_config_data(&ConfigData::random_address(get_random_addr()).build()) .await; - let response = mbox.ble_subsystem.read().await.unwrap(); + let response = ble.read().await.unwrap(); defmt::info!("{}", response); info!("config identity root..."); - mbox.ble_subsystem - .write_config_data(&ConfigData::identity_root(&get_irk()).build()) + ble.write_config_data(&ConfigData::identity_root(&get_irk()).build()) .await; - let response = mbox.ble_subsystem.read().await.unwrap(); + let response = ble.read().await.unwrap(); defmt::info!("{}", response); info!("config encryption root..."); - mbox.ble_subsystem - .write_config_data(&ConfigData::encryption_root(&get_erk()).build()) + ble.write_config_data(&ConfigData::encryption_root(&get_erk()).build()) .await; - let response = mbox.ble_subsystem.read().await.unwrap(); + let response = ble.read().await.unwrap(); defmt::info!("{}", response); info!("config tx power level..."); - mbox.ble_subsystem.set_tx_power_level(PowerLevel::ZerodBm).await; - let response = mbox.ble_subsystem.read().await.unwrap(); + ble.set_tx_power_level(PowerLevel::ZerodBm).await; + let response = ble.read().await.unwrap(); defmt::info!("{}", response); info!("GATT init..."); - mbox.ble_subsystem.init_gatt().await; - let response = mbox.ble_subsystem.read().await.unwrap(); + ble.init_gatt().await; + let response = ble.read().await.unwrap(); defmt::info!("{}", response); info!("GAP init..."); - mbox.ble_subsystem - .init_gap(Role::PERIPHERAL, false, BLE_GAP_DEVICE_NAME_LENGTH) - .await; - let response = mbox.ble_subsystem.read().await.unwrap(); + ble.init_gap(Role::PERIPHERAL, false, BLE_GAP_DEVICE_NAME_LENGTH).await; + let response = ble.read().await.unwrap(); defmt::info!("{}", response); // info!("set scan response..."); - // mbox.ble_subsystem.le_set_scan_response_data(&[]).await.unwrap(); - // let response = mbox.ble_subsystem.read().await.unwrap(); + // ble.le_set_scan_response_data(&[]).await.unwrap(); + // let response = ble.read().await.unwrap(); // defmt::info!("{}", response); info!("set discoverable..."); - mbox.ble_subsystem - .set_discoverable(&DiscoverableParameters { - advertising_type: AdvertisingType::NonConnectableUndirected, - advertising_interval: Some((Duration::from_millis(250), Duration::from_millis(250))), - address_type: OwnAddressType::Public, - filter_policy: AdvertisingFilterPolicy::AllowConnectionAndScan, - local_name: None, - advertising_data: &[], - conn_interval: (None, None), - }) - .await - .unwrap(); - - let response = mbox.ble_subsystem.read().await; + ble.set_discoverable(&DiscoverableParameters { + advertising_type: AdvertisingType::NonConnectableUndirected, + advertising_interval: Some((Duration::from_millis(250), Duration::from_millis(250))), + address_type: OwnAddressType::Public, + filter_policy: AdvertisingFilterPolicy::AllowConnectionAndScan, + local_name: None, + advertising_data: &[], + conn_interval: (None, None), + }) + .await + .unwrap(); + + let response = ble.read().await; defmt::info!("{}", response); // remove some advertisement to decrease the packet size info!("delete tx power ad type..."); - mbox.ble_subsystem - .delete_ad_type(AdvertisingDataType::TxPowerLevel) - .await; - let response = mbox.ble_subsystem.read().await.unwrap(); + ble.delete_ad_type(AdvertisingDataType::TxPowerLevel).await; + let response = ble.read().await.unwrap(); defmt::info!("{}", response); info!("delete conn interval ad type..."); - mbox.ble_subsystem - .delete_ad_type(AdvertisingDataType::PeripheralConnectionInterval) + ble.delete_ad_type(AdvertisingDataType::PeripheralConnectionInterval) .await; - let response = mbox.ble_subsystem.read().await.unwrap(); + let response = ble.read().await.unwrap(); defmt::info!("{}", response); info!("update advertising data..."); - mbox.ble_subsystem - .update_advertising_data(&eddystone_advertising_data()) + ble.update_advertising_data(&eddystone_advertising_data()) .await .unwrap(); - let response = mbox.ble_subsystem.read().await.unwrap(); + let response = ble.read().await.unwrap(); defmt::info!("{}", response); info!("update advertising data type..."); - mbox.ble_subsystem - .update_advertising_data(&[3, AdvertisingDataType::UuidCompleteList16 as u8, 0xaa, 0xfe]) + ble.update_advertising_data(&[3, AdvertisingDataType::UuidCompleteList16 as u8, 0xaa, 0xfe]) .await .unwrap(); - let response = mbox.ble_subsystem.read().await.unwrap(); + let response = ble.read().await.unwrap(); defmt::info!("{}", response); info!("update advertising data flags..."); - mbox.ble_subsystem - .update_advertising_data(&[ - 2, - AdvertisingDataType::Flags as u8, - (0x02 | 0x04) as u8, // BLE general discoverable, without BR/EDR support - ]) - .await - .unwrap(); - let response = mbox.ble_subsystem.read().await.unwrap(); + ble.update_advertising_data(&[ + 2, + AdvertisingDataType::Flags as u8, + (0x02 | 0x04) as u8, // BLE general discoverable, without BR/EDR support + ]) + .await + .unwrap(); + let response = ble.read().await.unwrap(); defmt::info!("{}", response); cortex_m::asm::wfi(); diff --git a/examples/stm32wb/src/bin/gatt_server.rs b/examples/stm32wb/src/bin/gatt_server.rs index 2ed257566..3484f1844 100644 --- a/examples/stm32wb/src/bin/gatt_server.rs +++ b/examples/stm32wb/src/bin/gatt_server.rs @@ -69,92 +69,85 @@ async fn main(spawner: Spawner) { info!("Hello World!"); let config = Config::default(); - let mut mbox = TlMbox::init(p.IPCC, Irqs, config); + let mbox = TlMbox::init(p.IPCC, Irqs, config).await; + let mut sys = mbox.sys_subsystem; + let mut ble = mbox.ble_subsystem; spawner.spawn(run_mm_queue(mbox.mm_subsystem).unwrap()); - let sys_event = mbox.sys_subsystem.read().await; - info!("sys event: {}", sys_event.payload()); - let _ = mbox.sys_subsystem.shci_c2_ble_init(Default::default()).await; + let _ = sys.shci_c2_ble_init(Default::default()).await; info!("resetting BLE..."); - mbox.ble_subsystem.reset().await; - let response = mbox.ble_subsystem.read().await; + ble.reset().await; + let response = ble.read().await; defmt::debug!("{}", response); info!("config public address..."); - mbox.ble_subsystem - .write_config_data(&ConfigData::public_address(get_bd_addr()).build()) + ble.write_config_data(&ConfigData::public_address(get_bd_addr()).build()) .await; - let response = mbox.ble_subsystem.read().await; + let response = ble.read().await; defmt::debug!("{}", response); info!("config random address..."); - mbox.ble_subsystem - .write_config_data(&ConfigData::random_address(get_random_addr()).build()) + ble.write_config_data(&ConfigData::random_address(get_random_addr()).build()) .await; - let response = mbox.ble_subsystem.read().await; + let response = ble.read().await; defmt::debug!("{}", response); info!("config identity root..."); - mbox.ble_subsystem - .write_config_data(&ConfigData::identity_root(&get_irk()).build()) + ble.write_config_data(&ConfigData::identity_root(&get_irk()).build()) .await; - let response = mbox.ble_subsystem.read().await; + let response = ble.read().await; defmt::debug!("{}", response); info!("config encryption root..."); - mbox.ble_subsystem - .write_config_data(&ConfigData::encryption_root(&get_erk()).build()) + ble.write_config_data(&ConfigData::encryption_root(&get_erk()).build()) .await; - let response = mbox.ble_subsystem.read().await; + let response = ble.read().await; defmt::debug!("{}", response); info!("config tx power level..."); - mbox.ble_subsystem.set_tx_power_level(PowerLevel::ZerodBm).await; - let response = mbox.ble_subsystem.read().await; + ble.set_tx_power_level(PowerLevel::ZerodBm).await; + let response = ble.read().await; defmt::debug!("{}", response); info!("GATT init..."); - mbox.ble_subsystem.init_gatt().await; - let response = mbox.ble_subsystem.read().await; + ble.init_gatt().await; + let response = ble.read().await; defmt::debug!("{}", response); info!("GAP init..."); - mbox.ble_subsystem - .init_gap(Role::PERIPHERAL, false, BLE_GAP_DEVICE_NAME_LENGTH) - .await; - let response = mbox.ble_subsystem.read().await; + ble.init_gap(Role::PERIPHERAL, false, BLE_GAP_DEVICE_NAME_LENGTH).await; + let response = ble.read().await; defmt::debug!("{}", response); info!("set IO capabilities..."); - mbox.ble_subsystem.set_io_capability(IoCapability::DisplayConfirm).await; - let response = mbox.ble_subsystem.read().await; + ble.set_io_capability(IoCapability::DisplayConfirm).await; + let response = ble.read().await; defmt::debug!("{}", response); info!("set authentication requirements..."); - mbox.ble_subsystem - .set_authentication_requirement(&AuthenticationRequirements { - bonding_required: false, - keypress_notification_support: false, - mitm_protection_required: false, - encryption_key_size_range: (8, 16), - fixed_pin: Pin::Requested, - identity_address_type: AddressType::Public, - secure_connection_support: SecureConnectionSupport::Optional, - }) - .await - .unwrap(); - let response = mbox.ble_subsystem.read().await; + ble.set_authentication_requirement(&AuthenticationRequirements { + bonding_required: false, + keypress_notification_support: false, + mitm_protection_required: false, + encryption_key_size_range: (8, 16), + fixed_pin: Pin::Requested, + identity_address_type: AddressType::Public, + secure_connection_support: SecureConnectionSupport::Optional, + }) + .await + .unwrap(); + let response = ble.read().await; defmt::debug!("{}", response); info!("set scan response data..."); - mbox.ble_subsystem.le_set_scan_response_data(b"TXTX").await.unwrap(); - let response = mbox.ble_subsystem.read().await; + ble.le_set_scan_response_data(b"TXTX").await.unwrap(); + let response = ble.read().await; defmt::debug!("{}", response); defmt::info!("initializing services and characteristics..."); - let mut ble_context = init_gatt_services(&mut mbox.ble_subsystem).await.unwrap(); + let mut ble_context = init_gatt_services(&mut ble).await.unwrap(); defmt::info!("{}", ble_context); let discovery_params = DiscoverableParameters { @@ -168,12 +161,12 @@ async fn main(spawner: Spawner) { }; info!("set discoverable..."); - mbox.ble_subsystem.set_discoverable(&discovery_params).await.unwrap(); - let response = mbox.ble_subsystem.read().await; + ble.set_discoverable(&discovery_params).await.unwrap(); + let response = ble.read().await; defmt::debug!("{}", response); loop { - let response = mbox.ble_subsystem.read().await; + let response = ble.read().await; defmt::debug!("{}", response); if let Ok(Packet::Event(event)) = response { @@ -184,24 +177,23 @@ async fn main(spawner: Spawner) { Event::DisconnectionComplete(_) => { defmt::info!("disconnected"); ble_context.is_subscribed = false; - mbox.ble_subsystem.set_discoverable(&discovery_params).await.unwrap(); + ble.set_discoverable(&discovery_params).await.unwrap(); } Event::Vendor(vendor_event) => match vendor_event { VendorEvent::AttReadPermitRequest(read_req) => { defmt::info!("read request received {}, allowing", read_req); - mbox.ble_subsystem.allow_read(read_req.conn_handle).await + ble.allow_read(read_req.conn_handle).await } VendorEvent::AttWritePermitRequest(write_req) => { defmt::info!("write request received {}, allowing", write_req); - mbox.ble_subsystem - .write_response(&WriteResponseParameters { - conn_handle: write_req.conn_handle, - attribute_handle: write_req.attribute_handle, - status: Ok(()), - value: write_req.value(), - }) - .await - .unwrap() + ble.write_response(&WriteResponseParameters { + conn_handle: write_req.conn_handle, + attribute_handle: write_req.attribute_handle, + status: Ok(()), + value: write_req.value(), + }) + .await + .unwrap() } VendorEvent::GattAttributeModified(attribute) => { defmt::info!("{}", ble_context); @@ -224,7 +216,7 @@ async fn main(spawner: Spawner) { } #[embassy_executor::task] -async fn run_mm_queue(memory_manager: mm::MemoryManager) { +async fn run_mm_queue(mut memory_manager: mm::MemoryManager<'static>) { memory_manager.run_queue().await; } @@ -285,7 +277,7 @@ pub struct CharHandles { pub notify: AttributeHandle, } -pub async fn init_gatt_services(ble_subsystem: &mut Ble) -> Result { +pub async fn init_gatt_services<'a>(ble_subsystem: &mut Ble<'a>) -> Result { let service_handle = gatt_add_service(ble_subsystem, Uuid::Uuid16(0x500)).await?; let read = gatt_add_char( @@ -322,7 +314,7 @@ pub async fn init_gatt_services(ble_subsystem: &mut Ble) -> Result Result { +async fn gatt_add_service<'a>(ble_subsystem: &mut Ble<'a>, uuid: Uuid) -> Result { ble_subsystem .add_service(&AddServiceParameters { uuid, @@ -348,8 +340,8 @@ async fn gatt_add_service(ble_subsystem: &mut Ble, uuid: Uuid) -> Result( + ble_subsystem: &mut Ble<'a>, service_handle: AttributeHandle, characteristic_uuid: Uuid, characteristic_properties: CharacteristicProperty, diff --git a/examples/stm32wb/src/bin/mac_ffd.rs b/examples/stm32wb/src/bin/mac_ffd.rs index 18a52e162..4bab6ea9f 100644 --- a/examples/stm32wb/src/bin/mac_ffd.rs +++ b/examples/stm32wb/src/bin/mac_ffd.rs @@ -19,7 +19,7 @@ bind_interrupts!(struct Irqs{ }); #[embassy_executor::task] -async fn run_mm_queue(memory_manager: mm::MemoryManager) { +async fn run_mm_queue(mut memory_manager: mm::MemoryManager<'static>) { memory_manager.run_queue().await; } @@ -54,74 +54,72 @@ async fn main(spawner: Spawner) { info!("Hello World!"); let config = Config::default(); - let mbox = TlMbox::init(p.IPCC, Irqs, config); + let mbox = TlMbox::init(p.IPCC, Irqs, config).await; + let mut sys = mbox.sys_subsystem; spawner.spawn(run_mm_queue(mbox.mm_subsystem).unwrap()); - let sys_event = mbox.sys_subsystem.read().await; - info!("sys event: {}", sys_event.payload()); - - core::mem::drop(sys_event); - - let result = mbox.sys_subsystem.shci_c2_mac_802_15_4_init().await; + let result = sys.shci_c2_mac_802_15_4_init().await; info!("initialized mac: {}", result); + let (mut mac_rx, mut mac_tx) = mbox.mac_subsystem.split(); + info!("resetting"); - mbox.mac_subsystem + mac_tx .send_command(&ResetRequest { set_default_pib: true, ..Default::default() }) .await .unwrap(); - defmt::info!("{:#x}", mbox.mac_subsystem.read().await.unwrap()); + defmt::info!("{:#x}", mac_rx.read().await.unwrap()); info!("setting extended address"); let extended_address: u64 = 0xACDE480000000001; - mbox.mac_subsystem + mac_tx .send_command(&SetRequest { pib_attribute_ptr: &extended_address as *const _ as *const u8, pib_attribute: PibId::ExtendedAddress, }) .await .unwrap(); - defmt::info!("{:#x}", mbox.mac_subsystem.read().await.unwrap()); + defmt::info!("{:#x}", mac_rx.read().await.unwrap()); info!("setting short address"); let short_address: u16 = 0x1122; - mbox.mac_subsystem + mac_tx .send_command(&SetRequest { pib_attribute_ptr: &short_address as *const _ as *const u8, pib_attribute: PibId::ShortAddress, }) .await .unwrap(); - defmt::info!("{:#x}", mbox.mac_subsystem.read().await.unwrap()); + defmt::info!("{:#x}", mac_rx.read().await.unwrap()); info!("setting association permit"); let association_permit: bool = true; - mbox.mac_subsystem + mac_tx .send_command(&SetRequest { pib_attribute_ptr: &association_permit as *const _ as *const u8, pib_attribute: PibId::AssociationPermit, }) .await .unwrap(); - defmt::info!("{:#x}", mbox.mac_subsystem.read().await.unwrap()); + defmt::info!("{:#x}", mac_rx.read().await.unwrap()); info!("setting TX power"); let transmit_power: i8 = 2; - mbox.mac_subsystem + mac_tx .send_command(&SetRequest { pib_attribute_ptr: &transmit_power as *const _ as *const u8, pib_attribute: PibId::TransmitPower, }) .await .unwrap(); - defmt::info!("{:#x}", mbox.mac_subsystem.read().await.unwrap()); + defmt::info!("{:#x}", mac_rx.read().await.unwrap()); info!("starting FFD device"); - mbox.mac_subsystem + mac_tx .send_command(&StartRequest { pan_id: PanId([0x1A, 0xAA]), channel_number: MacChannel::Channel16, @@ -133,28 +131,27 @@ async fn main(spawner: Spawner) { }) .await .unwrap(); - defmt::info!("{:#x}", mbox.mac_subsystem.read().await.unwrap()); + defmt::info!("{:#x}", mac_rx.read().await.unwrap()); info!("setting RX on when idle"); let rx_on_while_idle: bool = true; - mbox.mac_subsystem + mac_tx .send_command(&SetRequest { pib_attribute_ptr: &rx_on_while_idle as *const _ as *const u8, pib_attribute: PibId::RxOnWhenIdle, }) .await .unwrap(); - defmt::info!("{:#x}", mbox.mac_subsystem.read().await.unwrap()); + defmt::info!("{:#x}", mac_rx.read().await.unwrap()); loop { - let evt = mbox.mac_subsystem.read().await; + let evt = mac_rx.read().await; if let Ok(evt) = evt { defmt::info!("parsed mac event"); defmt::info!("{:#x}", evt); match evt { - MacEvent::MlmeAssociateInd(association) => mbox - .mac_subsystem + MacEvent::MlmeAssociateInd(association) => mac_tx .send_command(&AssociateResponse { device_address: association.device_address, assoc_short_address: [0x33, 0x44], diff --git a/examples/stm32wb/src/bin/mac_ffd_net.rs b/examples/stm32wb/src/bin/mac_ffd_net.rs index 5d946b35b..b4789e3ee 100644 --- a/examples/stm32wb/src/bin/mac_ffd_net.rs +++ b/examples/stm32wb/src/bin/mac_ffd_net.rs @@ -27,7 +27,7 @@ bind_interrupts!(struct Irqs{ }); #[embassy_executor::task] -async fn run_mm_queue(memory_manager: mm::MemoryManager) -> ! { +async fn run_mm_queue(mut memory_manager: mm::MemoryManager<'static>) -> ! { memory_manager.run_queue().await } @@ -72,15 +72,10 @@ async fn main(spawner: Spawner) { info!("Hello World!"); let config = Config::default(); - let mbox = TlMbox::init(p.IPCC, Irqs, config); + let mut mbox = TlMbox::init(p.IPCC, Irqs, config).await; spawner.spawn(run_mm_queue(mbox.mm_subsystem).unwrap()); - let sys_event = mbox.sys_subsystem.read().await; - info!("sys event: {}", sys_event.payload()); - - core::mem::drop(sys_event); - let result = mbox.sys_subsystem.shci_c2_mac_802_15_4_init().await; info!("initialized mac: {}", result); diff --git a/examples/stm32wb/src/bin/mac_rfd.rs b/examples/stm32wb/src/bin/mac_rfd.rs index 883179023..dae3c5200 100644 --- a/examples/stm32wb/src/bin/mac_rfd.rs +++ b/examples/stm32wb/src/bin/mac_rfd.rs @@ -21,7 +21,7 @@ bind_interrupts!(struct Irqs{ }); #[embassy_executor::task] -async fn run_mm_queue(memory_manager: mm::MemoryManager) { +async fn run_mm_queue(mut memory_manager: mm::MemoryManager<'static>) { memory_manager.run_queue().await; } @@ -56,41 +56,39 @@ async fn main(spawner: Spawner) { info!("Hello World!"); let config = Config::default(); - let mbox = TlMbox::init(p.IPCC, Irqs, config); + let mbox = TlMbox::init(p.IPCC, Irqs, config).await; + let mut sys = mbox.sys_subsystem; spawner.spawn(run_mm_queue(mbox.mm_subsystem).unwrap()); - let sys_event = mbox.sys_subsystem.read().await; - info!("sys event: {}", sys_event.payload()); - - core::mem::drop(sys_event); - - let result = mbox.sys_subsystem.shci_c2_mac_802_15_4_init().await; + let result = sys.shci_c2_mac_802_15_4_init().await; info!("initialized mac: {}", result); + let (mut mac_rx, mut mac_tx) = mbox.mac_subsystem.split(); + info!("resetting"); - mbox.mac_subsystem + mac_tx .send_command(&ResetRequest { set_default_pib: true, ..Default::default() }) .await .unwrap(); - defmt::info!("{:#x}", mbox.mac_subsystem.read().await.unwrap()); + defmt::info!("{:#x}", mac_rx.read().await.unwrap()); info!("setting extended address"); let extended_address: u64 = 0xACDE480000000002; - mbox.mac_subsystem + mac_tx .send_command(&SetRequest { pib_attribute_ptr: &extended_address as *const _ as *const u8, pib_attribute: PibId::ExtendedAddress, }) .await .unwrap(); - defmt::info!("{:#x}", mbox.mac_subsystem.read().await.unwrap()); + defmt::info!("{:#x}", mac_rx.read().await.unwrap()); info!("getting extended address"); - mbox.mac_subsystem + mac_tx .send_command(&GetRequest { pib_attribute: PibId::ExtendedAddress, ..Default::default() @@ -99,7 +97,7 @@ async fn main(spawner: Spawner) { .unwrap(); { - let evt = mbox.mac_subsystem.read().await.unwrap(); + let evt = mac_rx.read().await.unwrap(); info!("{:#x}", evt); if let MacEvent::MlmeGetCnf(evt) = evt { @@ -125,9 +123,9 @@ async fn main(spawner: Spawner) { key_index: 152, }; info!("{}", a); - mbox.mac_subsystem.send_command(&a).await.unwrap(); + mac_tx.send_command(&a).await.unwrap(); let short_addr = { - let evt = mbox.mac_subsystem.read().await.unwrap(); + let evt = mac_rx.read().await.unwrap(); info!("{:#x}", evt); if let MacEvent::MlmeAssociateCnf(conf) = evt { @@ -138,7 +136,7 @@ async fn main(spawner: Spawner) { }; info!("setting short address"); - mbox.mac_subsystem + mac_tx .send_command(&SetRequest { pib_attribute_ptr: &short_addr as *const _ as *const u8, pib_attribute: PibId::ShortAddress, @@ -146,13 +144,13 @@ async fn main(spawner: Spawner) { .await .unwrap(); { - let evt = mbox.mac_subsystem.read().await.unwrap(); + let evt = mac_rx.read().await.unwrap(); info!("{:#x}", evt); } info!("sending data"); let data = b"Hello from embassy!"; - mbox.mac_subsystem + mac_tx .send_command( DataRequest { src_addr_mode: AddressMode::Short, @@ -170,12 +168,12 @@ async fn main(spawner: Spawner) { .await .unwrap(); { - let evt = mbox.mac_subsystem.read().await.unwrap(); + let evt = mac_rx.read().await.unwrap(); info!("{:#x}", evt); } loop { - match mbox.mac_subsystem.read().await { + match mac_rx.read().await { Ok(evt) => info!("{:#x}", evt), _ => continue, }; diff --git a/examples/stm32wb/src/bin/tl_mbox.rs b/examples/stm32wb/src/bin/tl_mbox.rs index 4e7f2304d..0902e28e8 100644 --- a/examples/stm32wb/src/bin/tl_mbox.rs +++ b/examples/stm32wb/src/bin/tl_mbox.rs @@ -46,7 +46,7 @@ async fn main(_spawner: Spawner) { info!("Hello World!"); let config = Config::default(); - let mbox = TlMbox::init(p.IPCC, Irqs, config); + let mbox = TlMbox::init(p.IPCC, Irqs, config).await; loop { let wireless_fw_info = mbox.sys_subsystem.wireless_fw_info(); diff --git a/examples/stm32wb/src/bin/tl_mbox_ble.rs b/examples/stm32wb/src/bin/tl_mbox_ble.rs index 72a4c18e6..763dc32cd 100644 --- a/examples/stm32wb/src/bin/tl_mbox_ble.rs +++ b/examples/stm32wb/src/bin/tl_mbox_ble.rs @@ -7,6 +7,7 @@ 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_stm32_wpan::sub::mm; use {defmt_rtt as _, panic_probe as _}; bind_interrupts!(struct Irqs{ @@ -14,8 +15,13 @@ bind_interrupts!(struct Irqs{ IPCC_C1_TX => TransmitInterruptHandler; }); +#[embassy_executor::task] +async fn run_mm_queue(mut memory_manager: mm::MemoryManager<'static>) { + memory_manager.run_queue().await; +} + #[embassy_executor::main] -async fn main(_spawner: Spawner) { +async fn main(spawner: Spawner) { /* How to make this work: @@ -45,18 +51,19 @@ async fn main(_spawner: Spawner) { info!("Hello World!"); let config = Config::default(); - let mbox = TlMbox::init(p.IPCC, Irqs, config); + let mbox = TlMbox::init(p.IPCC, Irqs, config).await; + let mut sys = mbox.sys_subsystem; + let mut ble = mbox.ble_subsystem; - let sys_event = mbox.sys_subsystem.read().await; - info!("sys event: {}", sys_event.payload()); + spawner.spawn(run_mm_queue(mbox.mm_subsystem).unwrap()); - let _ = mbox.sys_subsystem.shci_c2_ble_init(Default::default()).await; + let _ = sys.shci_c2_ble_init(Default::default()).await; info!("starting ble..."); - mbox.ble_subsystem.tl_write(0x0c, &[]).await; + ble.tl_write(0x0c, &[]).await; info!("waiting for ble..."); - let ble_event = mbox.ble_subsystem.tl_read().await; + let ble_event = ble.tl_read().await; info!("ble event: {}", ble_event.payload()); diff --git a/examples/stm32wb/src/bin/tl_mbox_mac.rs b/examples/stm32wb/src/bin/tl_mbox_mac.rs index 16d0a1527..235a48241 100644 --- a/examples/stm32wb/src/bin/tl_mbox_mac.rs +++ b/examples/stm32wb/src/bin/tl_mbox_mac.rs @@ -16,7 +16,7 @@ bind_interrupts!(struct Irqs{ }); #[embassy_executor::task] -async fn run_mm_queue(memory_manager: mm::MemoryManager) { +async fn run_mm_queue(mut memory_manager: mm::MemoryManager<'static>) { memory_manager.run_queue().await; } @@ -51,16 +51,12 @@ async fn main(spawner: Spawner) { info!("Hello World!"); let config = Config::default(); - let mbox = TlMbox::init(p.IPCC, Irqs, config); + let mbox = TlMbox::init(p.IPCC, Irqs, config).await; + let mut sys = mbox.sys_subsystem; spawner.spawn(run_mm_queue(mbox.mm_subsystem).unwrap()); - let sys_event = mbox.sys_subsystem.read().await; - info!("sys event: {}", sys_event.payload()); - - core::mem::drop(sys_event); - - let result = mbox.sys_subsystem.shci_c2_mac_802_15_4_init().await; + let result = sys.shci_c2_mac_802_15_4_init().await; info!("initialized mac: {}", result); // diff --git a/tests/stm32/src/bin/wpan_ble.rs b/tests/stm32/src/bin/wpan_ble.rs index 0f396b848..b4c0cbf56 100644 --- a/tests/stm32/src/bin/wpan_ble.rs +++ b/tests/stm32/src/bin/wpan_ble.rs @@ -32,7 +32,7 @@ bind_interrupts!(struct Irqs{ const BLE_GAP_DEVICE_NAME_LENGTH: u8 = 7; #[embassy_executor::task] -async fn run_mm_queue(memory_manager: mm::MemoryManager) { +async fn run_mm_queue(mut memory_manager: mm::MemoryManager<'static>) { memory_manager.run_queue().await; } @@ -45,14 +45,13 @@ async fn main(spawner: Spawner) { info!("Hello World!"); let config = Config::default(); - let mut mbox = TlMbox::init(p.IPCC, Irqs, config); + let mbox = TlMbox::init(p.IPCC, Irqs, config).await; + let mut sys = mbox.sys_subsystem; + let mut ble = mbox.ble_subsystem; spawner.spawn(run_mm_queue(mbox.mm_subsystem).unwrap()); - let sys_event = mbox.sys_subsystem.read().await; - info!("sys event: {}", sys_event.payload()); - - let fw_info = mbox.sys_subsystem.wireless_fw_info().unwrap(); + let fw_info = sys.wireless_fw_info().unwrap(); let version_major = fw_info.version_major(); let version_minor = fw_info.version_minor(); let subversion = fw_info.subversion(); @@ -65,121 +64,108 @@ async fn main(spawner: Spawner) { version_major, version_minor, subversion, sram2a_size, sram2b_size ); - let _ = mbox.sys_subsystem.shci_c2_ble_init(Default::default()).await; + let _ = sys.shci_c2_ble_init(Default::default()).await; info!("resetting BLE..."); - mbox.ble_subsystem.reset().await; - let response = mbox.ble_subsystem.read().await.unwrap(); + ble.reset().await; + let response = ble.read().await.unwrap(); info!("{}", response); info!("config public address..."); - mbox.ble_subsystem - .write_config_data(&ConfigData::public_address(get_bd_addr()).build()) + ble.write_config_data(&ConfigData::public_address(get_bd_addr()).build()) .await; - let response = mbox.ble_subsystem.read().await.unwrap(); + let response = ble.read().await.unwrap(); info!("{}", response); info!("config random address..."); - mbox.ble_subsystem - .write_config_data(&ConfigData::random_address(get_random_addr()).build()) + ble.write_config_data(&ConfigData::random_address(get_random_addr()).build()) .await; - let response = mbox.ble_subsystem.read().await.unwrap(); + let response = ble.read().await.unwrap(); info!("{}", response); info!("config identity root..."); - mbox.ble_subsystem - .write_config_data(&ConfigData::identity_root(&get_irk()).build()) + ble.write_config_data(&ConfigData::identity_root(&get_irk()).build()) .await; - let response = mbox.ble_subsystem.read().await.unwrap(); + let response = ble.read().await.unwrap(); info!("{}", response); info!("config encryption root..."); - mbox.ble_subsystem - .write_config_data(&ConfigData::encryption_root(&get_erk()).build()) + ble.write_config_data(&ConfigData::encryption_root(&get_erk()).build()) .await; - let response = mbox.ble_subsystem.read().await.unwrap(); + let response = ble.read().await.unwrap(); info!("{}", response); info!("config tx power level..."); - mbox.ble_subsystem.set_tx_power_level(PowerLevel::ZerodBm).await; - let response = mbox.ble_subsystem.read().await.unwrap(); + ble.set_tx_power_level(PowerLevel::ZerodBm).await; + let response = ble.read().await.unwrap(); info!("{}", response); info!("GATT init..."); - mbox.ble_subsystem.init_gatt().await; - let response = mbox.ble_subsystem.read().await.unwrap(); + ble.init_gatt().await; + let response = ble.read().await.unwrap(); info!("{}", response); info!("GAP init..."); - mbox.ble_subsystem - .init_gap(Role::PERIPHERAL, false, BLE_GAP_DEVICE_NAME_LENGTH) - .await; - let response = mbox.ble_subsystem.read().await.unwrap(); + ble.init_gap(Role::PERIPHERAL, false, BLE_GAP_DEVICE_NAME_LENGTH).await; + let response = ble.read().await.unwrap(); info!("{}", response); // info!("set scan response..."); - // mbox.ble_subsystem.le_set_scan_response_data(&[]).await.unwrap(); - // let response = mbox.ble_subsystem.read().await.unwrap(); + // ble.le_set_scan_response_data(&[]).await.unwrap(); + // let response = ble.read().await.unwrap(); // info!("{}", response); info!("set discoverable..."); - mbox.ble_subsystem - .set_discoverable(&DiscoverableParameters { - advertising_type: AdvertisingType::NonConnectableUndirected, - advertising_interval: Some((Duration::from_millis(250), Duration::from_millis(250))), - address_type: OwnAddressType::Public, - filter_policy: AdvertisingFilterPolicy::AllowConnectionAndScan, - local_name: None, - advertising_data: &[], - conn_interval: (None, None), - }) - .await - .unwrap(); - - let response = mbox.ble_subsystem.read().await; + ble.set_discoverable(&DiscoverableParameters { + advertising_type: AdvertisingType::NonConnectableUndirected, + advertising_interval: Some((Duration::from_millis(250), Duration::from_millis(250))), + address_type: OwnAddressType::Public, + filter_policy: AdvertisingFilterPolicy::AllowConnectionAndScan, + local_name: None, + advertising_data: &[], + conn_interval: (None, None), + }) + .await + .unwrap(); + + let response = ble.read().await; info!("{}", response); // remove some advertisement to decrease the packet size info!("delete tx power ad type..."); - mbox.ble_subsystem - .delete_ad_type(AdvertisingDataType::TxPowerLevel) - .await; - let response = mbox.ble_subsystem.read().await.unwrap(); + ble.delete_ad_type(AdvertisingDataType::TxPowerLevel).await; + let response = ble.read().await.unwrap(); info!("{}", response); info!("delete conn interval ad type..."); - mbox.ble_subsystem - .delete_ad_type(AdvertisingDataType::PeripheralConnectionInterval) + ble.delete_ad_type(AdvertisingDataType::PeripheralConnectionInterval) .await; - let response = mbox.ble_subsystem.read().await.unwrap(); + let response = ble.read().await.unwrap(); info!("{}", response); info!("update advertising data..."); - mbox.ble_subsystem - .update_advertising_data(&eddystone_advertising_data()) + ble.update_advertising_data(&eddystone_advertising_data()) .await .unwrap(); - let response = mbox.ble_subsystem.read().await.unwrap(); + let response = ble.read().await.unwrap(); info!("{}", response); info!("update advertising data type..."); - mbox.ble_subsystem - .update_advertising_data(&[3, AdvertisingDataType::UuidCompleteList16 as u8, 0xaa, 0xfe]) + ble.update_advertising_data(&[3, AdvertisingDataType::UuidCompleteList16 as u8, 0xaa, 0xfe]) .await .unwrap(); - let response = mbox.ble_subsystem.read().await.unwrap(); + let response = ble.read().await.unwrap(); info!("{}", response); info!("update advertising data flags..."); - mbox.ble_subsystem - .update_advertising_data(&[ - 2, - AdvertisingDataType::Flags as u8, - (0x02 | 0x04) as u8, // BLE general discoverable, without BR/EDR support - ]) - .await - .unwrap(); - let response = mbox.ble_subsystem.read().await.unwrap(); + ble.update_advertising_data(&[ + 2, + AdvertisingDataType::Flags as u8, + (0x02 | 0x04) as u8, // BLE general discoverable, without BR/EDR support + ]) + .await + .unwrap(); + let response = ble.read().await.unwrap(); info!("{}", response); info!("Test OK"); diff --git a/tests/stm32/src/bin/wpan_mac.rs b/tests/stm32/src/bin/wpan_mac.rs index f27146c44..42db39e7e 100644 --- a/tests/stm32/src/bin/wpan_mac.rs +++ b/tests/stm32/src/bin/wpan_mac.rs @@ -25,7 +25,7 @@ bind_interrupts!(struct Irqs{ }); #[embassy_executor::task] -async fn run_mm_queue(memory_manager: mm::MemoryManager) { +async fn run_mm_queue(mut memory_manager: mm::MemoryManager<'static>) { memory_manager.run_queue().await; } @@ -38,20 +38,17 @@ async fn main(spawner: Spawner) { info!("Hello World!"); let config = Config::default(); - let mbox = TlMbox::init(p.IPCC, Irqs, config); + let mbox = TlMbox::init(p.IPCC, Irqs, config).await; + let mut sys = mbox.sys_subsystem; + let (mut mac_rx, mut mac_tx) = mbox.mac_subsystem.split(); spawner.spawn(run_mm_queue(mbox.mm_subsystem).unwrap()); - let sys_event = mbox.sys_subsystem.read().await; - info!("sys event: {}", sys_event.payload()); - - core::mem::drop(sys_event); - - let result = mbox.sys_subsystem.shci_c2_mac_802_15_4_init().await; + let result = sys.shci_c2_mac_802_15_4_init().await; info!("initialized mac: {}", result); info!("resetting"); - mbox.mac_subsystem + mac_tx .send_command(&ResetRequest { set_default_pib: true, ..Default::default() @@ -59,13 +56,13 @@ async fn main(spawner: Spawner) { .await .unwrap(); { - let evt = mbox.mac_subsystem.read().await.unwrap(); + let evt = mac_rx.read().await.unwrap(); info!("{:#x}", evt); } info!("setting extended address"); let extended_address: u64 = 0xACDE480000000002; - mbox.mac_subsystem + mac_tx .send_command(&SetRequest { pib_attribute_ptr: &extended_address as *const _ as *const u8, pib_attribute: PibId::ExtendedAddress, @@ -73,12 +70,12 @@ async fn main(spawner: Spawner) { .await .unwrap(); { - let evt = mbox.mac_subsystem.read().await.unwrap(); + let evt = mac_rx.read().await.unwrap(); info!("{:#x}", evt); } info!("getting extended address"); - mbox.mac_subsystem + mac_tx .send_command(&GetRequest { pib_attribute: PibId::ExtendedAddress, ..Default::default() @@ -87,7 +84,7 @@ async fn main(spawner: Spawner) { .unwrap(); { - let evt = mbox.mac_subsystem.read().await.unwrap(); + let evt = mac_rx.read().await.unwrap(); info!("{:#x}", evt); if let MacEvent::MlmeGetCnf(evt) = evt { @@ -113,8 +110,8 @@ async fn main(spawner: Spawner) { key_index: 152, }; info!("{}", a); - mbox.mac_subsystem.send_command(&a).await.unwrap(); - let short_addr = if let MacEvent::MlmeAssociateCnf(conf) = mbox.mac_subsystem.read().await.unwrap() { + mac_tx.send_command(&a).await.unwrap(); + let short_addr = if let MacEvent::MlmeAssociateCnf(conf) = mac_rx.read().await.unwrap() { conf.assoc_short_address } else { defmt::panic!() -- cgit From a0d3714773ce8d55955954ff9270fa2438a32ba6 Mon Sep 17 00:00:00 2001 From: xoviat Date: Sun, 23 Nov 2025 13:14:23 -0600 Subject: fmt --- embassy-stm32-wpan/src/sub/sys.rs | 2 -- 1 file changed, 2 deletions(-) diff --git a/embassy-stm32-wpan/src/sub/sys.rs b/embassy-stm32-wpan/src/sub/sys.rs index 718ccae8b..3ee539bb9 100644 --- a/embassy-stm32-wpan/src/sub/sys.rs +++ b/embassy-stm32-wpan/src/sub/sys.rs @@ -1,5 +1,3 @@ -use core::ptr; - use embassy_stm32::ipcc::{IpccRxChannel, IpccTxChannel}; use crate::cmd::CmdPacket; -- cgit From 1d87650ef529e49b3a61521529bc8775ac918cbd Mon Sep 17 00:00:00 2001 From: xoviat Date: Sun, 23 Nov 2025 13:18:45 -0600 Subject: changelog --- embassy-stm32-wpan/CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/embassy-stm32-wpan/CHANGELOG.md b/embassy-stm32-wpan/CHANGELOG.md index c567fe1de..eb17856fc 100644 --- a/embassy-stm32-wpan/CHANGELOG.md +++ b/embassy-stm32-wpan/CHANGELOG.md @@ -8,5 +8,6 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## Unreleased - ReleaseDate +- wpan: restructure hil and test wpan mac - restructure to allow embassy net driver to work. - First release with changelog. -- cgit From 7b349ce9d86b03f6fbc7320e13bf11c8be0e7882 Mon Sep 17 00:00:00 2001 From: xoviat Date: Sun, 23 Nov 2025 13:24:08 -0600 Subject: fmt --- embassy-stm32-wpan/src/sub/ble.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/embassy-stm32-wpan/src/sub/ble.rs b/embassy-stm32-wpan/src/sub/ble.rs index d5ed2ca28..afc4a510a 100644 --- a/embassy-stm32-wpan/src/sub/ble.rs +++ b/embassy-stm32-wpan/src/sub/ble.rs @@ -5,11 +5,11 @@ use hci::Opcode; use crate::cmd::CmdPacket; use crate::consts::{TL_BLEEVT_CC_OPCODE, TL_BLEEVT_CS_OPCODE, TlPacketType}; +use crate::evt; use crate::evt::{EvtBox, EvtPacket, EvtStub}; use crate::sub::mm; use crate::tables::{BLE_CMD_BUFFER, BleTable, CS_BUFFER, EVT_QUEUE, HCI_ACL_DATA_BUFFER, TL_BLE_TABLE}; use crate::unsafe_linked_list::LinkedListNode; -use crate::evt; /// A guard that, once constructed, may be used to send BLE commands to CPU2. /// -- cgit From 1a79bb51bf1490de5cc6f6ad021edd161c088b9f Mon Sep 17 00:00:00 2001 From: Eicke Hecht Date: Sun, 23 Nov 2025 23:38:38 +0100 Subject: wip: adding basic ringbuffered structure --- embassy-stm32/src/timer/mod.rs | 1 + embassy-stm32/src/timer/ringbuffered.rs | 47 +++++++++++++++++++++++++++++++++ 2 files changed, 48 insertions(+) create mode 100644 embassy-stm32/src/timer/ringbuffered.rs diff --git a/embassy-stm32/src/timer/mod.rs b/embassy-stm32/src/timer/mod.rs index 804d1ef37..aef3598f1 100644 --- a/embassy-stm32/src/timer/mod.rs +++ b/embassy-stm32/src/timer/mod.rs @@ -13,6 +13,7 @@ pub mod one_pulse; pub mod pwm_input; pub mod qei; pub mod simple_pwm; +pub mod ringbuffered; use crate::interrupt; use crate::rcc::RccPeripheral; diff --git a/embassy-stm32/src/timer/ringbuffered.rs b/embassy-stm32/src/timer/ringbuffered.rs new file mode 100644 index 000000000..d20c5d532 --- /dev/null +++ b/embassy-stm32/src/timer/ringbuffered.rs @@ -0,0 +1,47 @@ +//! RingBuffered PWM driver. + +use core::mem::ManuallyDrop; + +use super::low_level::Timer; +use super::{Channel, GeneralInstance4Channel, TimerChannel, TimerPin}; +use crate::Peri; +use crate::dma::ringbuffer::WritableDmaRingBuffer; +use super::simple_pwm::SimplePwm; + +pub struct RingBufferedPwmChannel<'d, T: GeneralInstance4Channel> { + timer: ManuallyDrop>, + ring_buf: WritableDmaRingBuffer<'d, u8>, + channel: Channel, +} + +/// A group of four [`SimplePwmChannel`]s, obtained from [`SimplePwm::split`]. +pub struct RingBufferedPwmChannels<'d, T: GeneralInstance4Channel> { + /// Channel 1 + pub ch1: RingBufferedPwmChannel<'d, T>, + /// Channel 2 + pub ch2: RingBufferedPwmChannel<'d, T>, + /// Channel 3 + pub ch3: RingBufferedPwmChannel<'d, T>, + /// Channel 4 + pub ch4: RingBufferedPwmChannel<'d, T>, +} + +/// Simple PWM driver. +pub struct RingBufferedPwm<'d, T: GeneralInstance4Channel> { + inner: Timer<'d, T>, +} + +impl<'d, T: GeneralInstance4Channel> SimplePwm<'d, T> { + pub fn into_ring_buffered_channel(mut self, tx_dma: Peri<'_, impl super::Dma>, dma_buf: &'d mut [u8]) -> RingBufferedPwmChannel<'d> { + assert!(!dma_buf.is_empty() && dma_buf.len() <= 0xFFFF); + let ring_buf = WritableDmaRingBuffer::new(dma_buf); + let channel = C::CHANNEL; + RingBufferedPwmChannel { + timer: unsafe { self.inner.clone_unchecked() }, + channel, + ring_buf + } + + // let ring_buf = WriteableRingBuffer::new(); + } +} -- cgit From a227e61137a689ecd875c41a7efb5f2a6bb73876 Mon Sep 17 00:00:00 2001 From: Eicke Hecht Date: Sun, 23 Nov 2025 23:39:09 +0100 Subject: fix: Formatting and clippy --- examples/stm32f7/src/bin/pwm.rs | 30 +++++++++++++++++------------- 1 file changed, 17 insertions(+), 13 deletions(-) diff --git a/examples/stm32f7/src/bin/pwm.rs b/examples/stm32f7/src/bin/pwm.rs index 872d99859..53efc2d8a 100644 --- a/examples/stm32f7/src/bin/pwm.rs +++ b/examples/stm32f7/src/bin/pwm.rs @@ -1,15 +1,13 @@ #![no_std] #![no_main] -use defmt::{panic, *}; +use defmt::*; use embassy_executor::Spawner; -use embassy_futures::join::join; -use embassy_stm32::time::Hertz; -use embassy_stm32::{Config, peripherals}; +use embassy_stm32::Config; use embassy_stm32::gpio::OutputType; +use embassy_stm32::time::Hertz; use embassy_stm32::time::mhz; use embassy_stm32::timer::simple_pwm::{PwmPin, SimplePwm}; -use embassy_time::Timer; use {defmt_rtt as _, panic_probe as _}; // If you are trying this and your USB device doesn't connect, the most @@ -44,7 +42,15 @@ async fn main(_spawner: Spawner) { let p = embassy_stm32::init(config); let ch1_pin = PwmPin::new(p.PE9, OutputType::PushPull); let ch2_pin = PwmPin::new(p.PE11, OutputType::PushPull); - let mut pwm = SimplePwm::new(p.TIM1, Some(ch1_pin), Some(ch2_pin), None, None, mhz(1), Default::default()); + let mut pwm = SimplePwm::new( + p.TIM1, + Some(ch1_pin), + Some(ch2_pin), + None, + None, + mhz(1), + Default::default(), + ); let mut ch1 = pwm.ch1(); ch1.enable(); info!("PWM initialized"); @@ -53,13 +59,11 @@ async fn main(_spawner: Spawner) { info!("PWM duty on channel 1 (D6) 50%"); ch1.set_duty_cycle_fraction(1, 2); info!("PWM waveform on channel 2 (D5)"); - const max_duty: usize = 200; - let mut duty = [0u16;max_duty]; - for i in 0..max_duty { + const MAX_DUTY: usize = 200; + let mut duty = [0u16; MAX_DUTY]; + for i in 0..MAX_DUTY { duty[i] = i as u16; } - pwm.waveform_continuous::(p.DMA2_CH6, &duty).await; - - + pwm.waveform_continuous::(p.DMA2_CH6, &duty) + .await; } - -- cgit From df119ba155ffde03503e8072dc9371010e71d105 Mon Sep 17 00:00:00 2001 From: Matthew Tran <0e4ef622@gmail.com> Date: Sun, 23 Nov 2025 23:37:48 -0600 Subject: embassy-nrf: add workaround for anomaly 66 on nrf52 --- embassy-nrf/CHANGELOG.md | 1 + embassy-nrf/src/lib.rs | 24 ++++++++++++++++++++++++ 2 files changed, 25 insertions(+) diff --git a/embassy-nrf/CHANGELOG.md b/embassy-nrf/CHANGELOG.md index f6fe1e14f..be79bde5d 100644 --- a/embassy-nrf/CHANGELOG.md +++ b/embassy-nrf/CHANGELOG.md @@ -27,6 +27,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - changed: `gpiote::InputChannel::wait()` now takes a mutable reference to `self` to avoid interference from concurrent calls - changed: `gpiote::InputChannel::wait()` now ensures events are seen as soon as the function is called, even if the future is not polled - bugfix: use correct flash size for nRF54l +- changed: add workaround for anomaly 66 on nrf52 ## 0.8.0 - 2025-09-30 diff --git a/embassy-nrf/src/lib.rs b/embassy-nrf/src/lib.rs index 2f5ad352f..28d2119ae 100644 --- a/embassy-nrf/src/lib.rs +++ b/embassy-nrf/src/lib.rs @@ -776,6 +776,30 @@ pub fn init(config: config::Config) -> Peripherals { } } + // Workaround for anomaly 66 + #[cfg(feature = "_nrf52")] + { + let ficr = pac::FICR; + let temp = pac::TEMP; + temp.a(0).write_value(ficr.temp().a0().read().0); + temp.a(1).write_value(ficr.temp().a1().read().0); + temp.a(2).write_value(ficr.temp().a2().read().0); + temp.a(3).write_value(ficr.temp().a3().read().0); + temp.a(4).write_value(ficr.temp().a4().read().0); + temp.a(5).write_value(ficr.temp().a5().read().0); + temp.b(0).write_value(ficr.temp().b0().read().0); + temp.b(1).write_value(ficr.temp().b1().read().0); + temp.b(2).write_value(ficr.temp().b2().read().0); + temp.b(3).write_value(ficr.temp().b3().read().0); + temp.b(4).write_value(ficr.temp().b4().read().0); + temp.b(5).write_value(ficr.temp().b5().read().0); + temp.t(0).write_value(ficr.temp().t0().read().0); + temp.t(1).write_value(ficr.temp().t1().read().0); + temp.t(2).write_value(ficr.temp().t2().read().0); + temp.t(3).write_value(ficr.temp().t3().read().0); + temp.t(4).write_value(ficr.temp().t4().read().0); + } + // GLITCHDET is only accessible for secure code #[cfg(all(feature = "_nrf54l", feature = "_s"))] { -- cgit From d45376583386272bc49fecc7eed8951067f84ac8 Mon Sep 17 00:00:00 2001 From: Eicke Hecht Date: Mon, 24 Nov 2025 21:42:54 +0100 Subject: feat: Implement basic ring buffered PWM channel --- embassy-stm32/src/timer/low_level.rs | 2 - embassy-stm32/src/timer/ringbuffered.rs | 151 +++++++++++++++++++++++++++----- embassy-stm32/src/timer/simple_pwm.rs | 35 ++++++++ 3 files changed, 163 insertions(+), 25 deletions(-) diff --git a/embassy-stm32/src/timer/low_level.rs b/embassy-stm32/src/timer/low_level.rs index 307d614bf..2cee5f1f5 100644 --- a/embassy-stm32/src/timer/low_level.rs +++ b/embassy-stm32/src/timer/low_level.rs @@ -813,8 +813,6 @@ impl<'d, T: GeneralInstance4Channel> Timer<'d, T> { pub async fn waveform_continuous(&mut self, dma: Peri<'_, impl super::Dma>, duty: &[u16]) { - #[cfg(any(bdma, gpdma))] - panic!("unsupported DMA"); use crate::pac::timer::vals::Ccds; diff --git a/embassy-stm32/src/timer/ringbuffered.rs b/embassy-stm32/src/timer/ringbuffered.rs index d20c5d532..bb602f8a7 100644 --- a/embassy-stm32/src/timer/ringbuffered.rs +++ b/embassy-stm32/src/timer/ringbuffered.rs @@ -1,19 +1,142 @@ //! RingBuffered PWM driver. use core::mem::ManuallyDrop; +use core::task::Waker; use super::low_level::Timer; -use super::{Channel, GeneralInstance4Channel, TimerChannel, TimerPin}; -use crate::Peri; -use crate::dma::ringbuffer::WritableDmaRingBuffer; -use super::simple_pwm::SimplePwm; +use super::{Channel, GeneralInstance4Channel}; +use crate::dma::ringbuffer::Error; +use crate::dma::WritableRingBuffer; pub struct RingBufferedPwmChannel<'d, T: GeneralInstance4Channel> { timer: ManuallyDrop>, - ring_buf: WritableDmaRingBuffer<'d, u8>, + ring_buf: WritableRingBuffer<'d, u16>, channel: Channel, } +impl<'d, T: GeneralInstance4Channel> RingBufferedPwmChannel<'d, T> { + pub(crate) fn new(timer: ManuallyDrop>, channel: Channel, ring_buf: WritableRingBuffer<'d, u16>) -> Self { + Self { + timer, + ring_buf, + channel, + } + } + + /// Start the ring buffer operation. + /// + /// You must call this after creating it for it to work. + pub fn start(&mut self) { + self.ring_buf.start() + } + + /// Clear all data in the ring buffer. + pub fn clear(&mut self) { + self.ring_buf.clear() + } + + /// Write elements directly to the raw buffer. This can be used to fill the buffer before starting the DMA transfer. + pub fn write_immediate(&mut self, buf: &[u16]) -> Result<(usize, usize), Error> { + self.ring_buf.write_immediate(buf) + } + + /// Write elements from the ring buffer + /// Return a tuple of the length written and the length remaining in the buffer + pub fn write(&mut self, buf: &[u16]) -> Result<(usize, usize), Error> { + self.ring_buf.write(buf) + } + + /// Write an exact number of elements to the ringbuffer. + pub async fn write_exact(&mut self, buffer: &[u16]) -> Result { + self.ring_buf.write_exact(buffer).await + } + + /// Wait for any ring buffer write error. + pub async fn wait_write_error(&mut self) -> Result { + self.ring_buf.wait_write_error().await + } + + /// The current length of the ringbuffer + pub fn len(&mut self) -> Result { + self.ring_buf.len() + } + + /// The capacity of the ringbuffer + pub const fn capacity(&self) -> usize { + self.ring_buf.capacity() + } + + /// Set a waker to be woken when at least one byte is send. + pub fn set_waker(&mut self, waker: &Waker) { + self.ring_buf.set_waker(waker) + } + + /// Request the DMA to reset. The configuration for this channel will not be preserved. + /// + /// This doesn't immediately stop the transfer, you have to wait until is_running returns false. + pub fn request_reset(&mut self) { + self.ring_buf.request_reset() + } + + /// Request the transfer to pause, keeping the existing configuration for this channel. + /// To restart the transfer, call [`start`](Self::start) again. + /// + /// This doesn't immediately stop the transfer, you have to wait until is_running returns false. + pub fn request_pause(&mut self) { + self.ring_buf.request_pause() + } + + /// Return whether DMA is still running. + /// + /// If this returns false, it can be because either the transfer finished, or it was requested to stop early with request_stop. + pub fn is_running(&mut self) -> bool { + self.ring_buf.is_running() + } + + /// Stop the DMA transfer and await until the buffer is empty. + /// + /// This disables the DMA transfer's circular mode so that the transfer stops when all available data has been written. + /// + /// This is designed to be used with streaming output data such as the I2S/SAI or DAC. + pub async fn stop(&mut self) { + self.ring_buf.stop().await + } + + /// Enable the given channel. + pub fn enable(&mut self) { + self.timer.enable_channel(self.channel, true); + } + + /// Disable the given channel. + pub fn disable(&mut self) { + self.timer.enable_channel(self.channel, false); + } + + /// Check whether given channel is enabled + pub fn is_enabled(&self) -> bool { + self.timer.get_channel_enable_state(self.channel) + } + + /// Get max duty value. + /// + /// This value depends on the configured frequency and the timer's clock rate from RCC. + pub fn max_duty_cycle(&self) -> u16 { + let max = self.timer.get_max_compare_value(); + assert!(max < u16::MAX as u32); + max as u16 + 1 + } + + /// Set the output polarity for a given channel. + pub fn set_polarity(&mut self, polarity: super::low_level::OutputPolarity) { + self.timer.set_output_polarity(self.channel, polarity); + } + + /// Set the output compare mode for a given channel. + pub fn set_output_compare_mode(&mut self, mode: super::low_level::OutputCompareMode) { + self.timer.set_output_compare_mode(self.channel, mode); + } +} + /// A group of four [`SimplePwmChannel`]s, obtained from [`SimplePwm::split`]. pub struct RingBufferedPwmChannels<'d, T: GeneralInstance4Channel> { /// Channel 1 @@ -26,22 +149,4 @@ pub struct RingBufferedPwmChannels<'d, T: GeneralInstance4Channel> { pub ch4: RingBufferedPwmChannel<'d, T>, } -/// Simple PWM driver. -pub struct RingBufferedPwm<'d, T: GeneralInstance4Channel> { - inner: Timer<'d, T>, -} - -impl<'d, T: GeneralInstance4Channel> SimplePwm<'d, T> { - pub fn into_ring_buffered_channel(mut self, tx_dma: Peri<'_, impl super::Dma>, dma_buf: &'d mut [u8]) -> RingBufferedPwmChannel<'d> { - assert!(!dma_buf.is_empty() && dma_buf.len() <= 0xFFFF); - let ring_buf = WritableDmaRingBuffer::new(dma_buf); - let channel = C::CHANNEL; - RingBufferedPwmChannel { - timer: unsafe { self.inner.clone_unchecked() }, - channel, - ring_buf - } - // let ring_buf = WriteableRingBuffer::new(); - } -} diff --git a/embassy-stm32/src/timer/simple_pwm.rs b/embassy-stm32/src/timer/simple_pwm.rs index 56d00ea59..53e0345ea 100644 --- a/embassy-stm32/src/timer/simple_pwm.rs +++ b/embassy-stm32/src/timer/simple_pwm.rs @@ -4,8 +4,13 @@ use core::marker::PhantomData; use core::mem::ManuallyDrop; use super::low_level::{CountingMode, OutputCompareMode, OutputPolarity, Timer}; +use super::ringbuffered::RingBufferedPwmChannel; use super::{Ch1, Ch2, Ch3, Ch4, Channel, GeneralInstance4Channel, TimerChannel, TimerPin}; +use crate::dma::WritableRingBuffer; use crate::Peri; +#[cfg(not(any(bdma, gpdma)))] +use crate::dma::{Burst, FifoThreshold}; +use crate::dma::TransferOptions; #[cfg(gpio_v2)] use crate::gpio::Pull; use crate::gpio::{AfType, AnyPin, OutputType, Speed}; @@ -374,6 +379,36 @@ impl<'d, T: GeneralInstance4Channel> SimplePwm<'d, T> { pub async fn waveform_continuous(&mut self, dma: Peri<'_, impl super::Dma>, duty: &[u16]) { self.inner.waveform_continuous(dma, duty).await; } + pub fn into_ring_buffered_channel(self, tx_dma: Peri<'d, impl super::Dma>, dma_buf: &'d mut [u16]) -> RingBufferedPwmChannel<'d, T> { + assert!(!dma_buf.is_empty() && dma_buf.len() <= 0xFFFF); + + let channel = C::CHANNEL; + let request = tx_dma.request(); + + let opts = TransferOptions { + #[cfg(not(any(bdma, gpdma)))] + fifo_threshold: Some(FifoThreshold::Full), + #[cfg(not(any(bdma, gpdma)))] + mburst: Burst::Incr8, + ..Default::default() + }; + + let ring_buf = unsafe { + WritableRingBuffer::new( + tx_dma, + request, + self.inner.regs_gp16().ccr(channel.index()).as_ptr() as *mut u16, + dma_buf, + opts, + ) + }; + + RingBufferedPwmChannel::new( + unsafe { self.inner.clone_unchecked() }, + channel, + ring_buf + ) + } } impl<'d, T: GeneralInstance4Channel> embedded_hal_1::pwm::ErrorType for SimplePwmChannel<'d, T> { -- cgit From 05417e91093dfd7e150083738988259d66ee4e37 Mon Sep 17 00:00:00 2001 From: Eicke Hecht Date: Mon, 24 Nov 2025 21:54:25 +0100 Subject: fix: Warnings and formatting all fixed --- embassy-stm32/src/timer/low_level.rs | 4 +--- embassy-stm32/src/timer/ringbuffered.rs | 25 +++++++++++++++++++++---- embassy-stm32/src/timer/simple_pwm.rs | 31 +++++++++++++++++++++++-------- 3 files changed, 45 insertions(+), 15 deletions(-) diff --git a/embassy-stm32/src/timer/low_level.rs b/embassy-stm32/src/timer/low_level.rs index 2cee5f1f5..d1cf11386 100644 --- a/embassy-stm32/src/timer/low_level.rs +++ b/embassy-stm32/src/timer/low_level.rs @@ -812,8 +812,6 @@ impl<'d, T: GeneralInstance4Channel> Timer<'d, T> { /// You may want to start this in a new thread as this will block forever pub async fn waveform_continuous(&mut self, dma: Peri<'_, impl super::Dma>, duty: &[u16]) { - - use crate::pac::timer::vals::Ccds; #[allow(clippy::let_unit_value)] // eg. stm32f334 @@ -855,7 +853,7 @@ impl<'d, T: GeneralInstance4Channel> Timer<'d, T> { req: dma::Request, channel: Channel, duty: &[u16], - circular: bool, + #[allow(unused_variables)] circular: bool, ) { let original_duty_state = self.get_compare_value(channel); let original_enable_state = self.get_channel_enable_state(channel); diff --git a/embassy-stm32/src/timer/ringbuffered.rs b/embassy-stm32/src/timer/ringbuffered.rs index bb602f8a7..e8f97bf59 100644 --- a/embassy-stm32/src/timer/ringbuffered.rs +++ b/embassy-stm32/src/timer/ringbuffered.rs @@ -5,9 +5,24 @@ use core::task::Waker; use super::low_level::Timer; use super::{Channel, GeneralInstance4Channel}; -use crate::dma::ringbuffer::Error; use crate::dma::WritableRingBuffer; +use crate::dma::ringbuffer::Error; +/// A PWM channel that uses a DMA ring buffer for continuous waveform generation. +/// +/// This allows you to continuously update PWM duty cycles via DMA without blocking the CPU. +/// The ring buffer enables smooth, uninterrupted waveform generation by automatically cycling +/// through duty cycle values stored in memory. +/// +/// You can write new duty cycle values to the ring buffer while it's running, enabling +/// dynamic waveform generation for applications like motor control, LED dimming, or audio output. +/// +/// # Example +/// ```ignore +/// let mut channel = pwm.ch1().into_ring_buffered_channel(dma_ch, &mut buffer); +/// channel.start(); // Start DMA transfer +/// channel.write(&[100, 200, 300]).ok(); // Update duty cycles +/// ``` pub struct RingBufferedPwmChannel<'d, T: GeneralInstance4Channel> { timer: ManuallyDrop>, ring_buf: WritableRingBuffer<'d, u16>, @@ -15,7 +30,11 @@ pub struct RingBufferedPwmChannel<'d, T: GeneralInstance4Channel> { } impl<'d, T: GeneralInstance4Channel> RingBufferedPwmChannel<'d, T> { - pub(crate) fn new(timer: ManuallyDrop>, channel: Channel, ring_buf: WritableRingBuffer<'d, u16>) -> Self { + pub(crate) fn new( + timer: ManuallyDrop>, + channel: Channel, + ring_buf: WritableRingBuffer<'d, u16>, + ) -> Self { Self { timer, ring_buf, @@ -148,5 +167,3 @@ pub struct RingBufferedPwmChannels<'d, T: GeneralInstance4Channel> { /// Channel 4 pub ch4: RingBufferedPwmChannel<'d, T>, } - - diff --git a/embassy-stm32/src/timer/simple_pwm.rs b/embassy-stm32/src/timer/simple_pwm.rs index 53e0345ea..f4656b7bd 100644 --- a/embassy-stm32/src/timer/simple_pwm.rs +++ b/embassy-stm32/src/timer/simple_pwm.rs @@ -6,11 +6,11 @@ use core::mem::ManuallyDrop; use super::low_level::{CountingMode, OutputCompareMode, OutputPolarity, Timer}; use super::ringbuffered::RingBufferedPwmChannel; use super::{Ch1, Ch2, Ch3, Ch4, Channel, GeneralInstance4Channel, TimerChannel, TimerPin}; -use crate::dma::WritableRingBuffer; use crate::Peri; +use crate::dma::TransferOptions; +use crate::dma::WritableRingBuffer; #[cfg(not(any(bdma, gpdma)))] use crate::dma::{Burst, FifoThreshold}; -use crate::dma::TransferOptions; #[cfg(gpio_v2)] use crate::gpio::Pull; use crate::gpio::{AfType, AnyPin, OutputType, Speed}; @@ -379,9 +379,27 @@ impl<'d, T: GeneralInstance4Channel> SimplePwm<'d, T> { pub async fn waveform_continuous(&mut self, dma: Peri<'_, impl super::Dma>, duty: &[u16]) { self.inner.waveform_continuous(dma, duty).await; } - pub fn into_ring_buffered_channel(self, tx_dma: Peri<'d, impl super::Dma>, dma_buf: &'d mut [u16]) -> RingBufferedPwmChannel<'d, T> { + + /// Convert this PWM channel into a ring-buffered PWM channel. + /// + /// This allows continuous PWM waveform generation using a DMA ring buffer. + /// The ring buffer enables dynamic updates to the PWM duty cycle without blocking. + /// + /// # Arguments + /// * `tx_dma` - The DMA channel to use for transferring duty cycle values + /// * `dma_buf` - The buffer to use as a ring buffer (must be non-empty and <= 65535 elements) + /// + /// # Panics + /// Panics if `dma_buf` is empty or longer than 65535 elements. + pub fn into_ring_buffered_channel( + self, + tx_dma: Peri<'d, impl super::Dma>, + dma_buf: &'d mut [u16], + ) -> RingBufferedPwmChannel<'d, T> { assert!(!dma_buf.is_empty() && dma_buf.len() <= 0xFFFF); + use crate::pac::timer::vals::Ccds; + let channel = C::CHANNEL; let request = tx_dma.request(); @@ -393,6 +411,7 @@ impl<'d, T: GeneralInstance4Channel> SimplePwm<'d, T> { ..Default::default() }; + self.inner.set_cc_dma_selection(Ccds::ON_UPDATE); let ring_buf = unsafe { WritableRingBuffer::new( tx_dma, @@ -403,11 +422,7 @@ impl<'d, T: GeneralInstance4Channel> SimplePwm<'d, T> { ) }; - RingBufferedPwmChannel::new( - unsafe { self.inner.clone_unchecked() }, - channel, - ring_buf - ) + RingBufferedPwmChannel::new(unsafe { self.inner.clone_unchecked() }, channel, ring_buf) } } -- cgit From 6a97b6718e1389ce9e5dd3fb989b3b9f3fcfbd09 Mon Sep 17 00:00:00 2001 From: Eicke Hecht Date: Mon, 24 Nov 2025 22:31:04 +0100 Subject: wip: Add a working example for the stm32f767zi nucleo. Currently no PWM signal visible... --- embassy-stm32/src/timer/simple_pwm.rs | 2 + examples/stm32f7/src/bin/pwm.rs | 31 +++--- examples/stm32f7/src/bin/pwm_ringbuffer.rs | 157 +++++++++++++++++++++++++++++ 3 files changed, 171 insertions(+), 19 deletions(-) create mode 100644 examples/stm32f7/src/bin/pwm_ringbuffer.rs diff --git a/embassy-stm32/src/timer/simple_pwm.rs b/embassy-stm32/src/timer/simple_pwm.rs index f4656b7bd..8b738741a 100644 --- a/embassy-stm32/src/timer/simple_pwm.rs +++ b/embassy-stm32/src/timer/simple_pwm.rs @@ -412,6 +412,8 @@ impl<'d, T: GeneralInstance4Channel> SimplePwm<'d, T> { }; self.inner.set_cc_dma_selection(Ccds::ON_UPDATE); + self.inner.set_cc_dma_enable_state(channel, true); + let ring_buf = unsafe { WritableRingBuffer::new( tx_dma, diff --git a/examples/stm32f7/src/bin/pwm.rs b/examples/stm32f7/src/bin/pwm.rs index 53efc2d8a..c1c7ec6ce 100644 --- a/examples/stm32f7/src/bin/pwm.rs +++ b/examples/stm32f7/src/bin/pwm.rs @@ -8,6 +8,7 @@ use embassy_stm32::gpio::OutputType; use embassy_stm32::time::Hertz; use embassy_stm32::time::mhz; use embassy_stm32::timer::simple_pwm::{PwmPin, SimplePwm}; +use embassy_time::Timer; use {defmt_rtt as _, panic_probe as _}; // If you are trying this and your USB device doesn't connect, the most @@ -41,29 +42,21 @@ async fn main(_spawner: Spawner) { } let p = embassy_stm32::init(config); let ch1_pin = PwmPin::new(p.PE9, OutputType::PushPull); - let ch2_pin = PwmPin::new(p.PE11, OutputType::PushPull); - let mut pwm = SimplePwm::new( - p.TIM1, - Some(ch1_pin), - Some(ch2_pin), - None, - None, - mhz(1), - Default::default(), - ); + let mut pwm = SimplePwm::new(p.TIM1, Some(ch1_pin), None, None, None, mhz(1), Default::default()); let mut ch1 = pwm.ch1(); ch1.enable(); + info!("PWM initialized"); info!("PWM max duty {}", ch1.max_duty_cycle()); - info!("PWM duty on channel 1 (D6) 50%"); - ch1.set_duty_cycle_fraction(1, 2); - info!("PWM waveform on channel 2 (D5)"); - const MAX_DUTY: usize = 200; - let mut duty = [0u16; MAX_DUTY]; - for i in 0..MAX_DUTY { - duty[i] = i as u16; + loop { + ch1.set_duty_cycle_fully_off(); + Timer::after_millis(300).await; + ch1.set_duty_cycle_fraction(1, 4); + Timer::after_millis(300).await; + ch1.set_duty_cycle_fraction(1, 2); + Timer::after_millis(300).await; + ch1.set_duty_cycle(ch1.max_duty_cycle() - 1); + Timer::after_millis(300).await; } - pwm.waveform_continuous::(p.DMA2_CH6, &duty) - .await; } diff --git a/examples/stm32f7/src/bin/pwm_ringbuffer.rs b/examples/stm32f7/src/bin/pwm_ringbuffer.rs new file mode 100644 index 000000000..a8744f190 --- /dev/null +++ b/examples/stm32f7/src/bin/pwm_ringbuffer.rs @@ -0,0 +1,157 @@ +#![no_std] +#![no_main] + +use defmt::*; +use embassy_executor::Spawner; +use embassy_stm32::Config; +use embassy_stm32::gpio::OutputType; +use embassy_stm32::time::mhz; +use embassy_stm32::timer::simple_pwm::{PwmPin, SimplePwm}; +use embassy_time::Timer; +use {defmt_rtt as _, panic_probe as _}; + +// If you are trying this and your USB device doesn't connect, the most +// common issues are the RCC config and vbus_detection +// +// See https://embassy.dev/book/#_the_usb_examples_are_not_working_on_my_board_is_there_anything_else_i_need_to_configure +// for more information. +#[embassy_executor::main] +async fn main(_spawner: Spawner) { + info!("PWM Ring Buffer Example"); + + let mut config = Config::default(); + { + use embassy_stm32::rcc::*; + use embassy_stm32::time::Hertz; + config.rcc.hse = Some(Hse { + freq: Hertz(8_000_000), + mode: HseMode::Bypass, + }); + config.rcc.pll_src = PllSource::HSE; + config.rcc.pll = Some(Pll { + prediv: PllPreDiv::DIV4, + mul: PllMul::MUL200, + divp: Some(PllPDiv::DIV2), // 8mhz / 4 * 200 / 2 = 200Mhz + divq: Some(PllQDiv::DIV4), // 8mhz / 4 * 200 / 4 = 100Mhz + divr: None, + }); + config.rcc.ahb_pre = AHBPrescaler::DIV1; + config.rcc.apb1_pre = APBPrescaler::DIV4; + config.rcc.apb2_pre = APBPrescaler::DIV2; + config.rcc.sys = Sysclk::PLL1_P; + } + let p = embassy_stm32::init(config); + + // Initialize PWM on TIM1 + let ch1_pin = PwmPin::new(p.PE9, OutputType::PushPull); + let ch2_pin = PwmPin::new(p.PE11, OutputType::PushPull); + let mut pwm = SimplePwm::new( + p.TIM1, + Some(ch1_pin), + Some(ch2_pin), + None, + None, + mhz(1), + Default::default(), + ); + + // Use channel 1 for static PWM at 50% + let mut ch1 = pwm.ch1(); + ch1.enable(); + ch1.set_duty_cycle_fraction(1, 2); + info!("Channel 1 (PE9/D6): Static 50% duty cycle"); + + // Get max duty from channel 1 before converting channel 2 + let max_duty = ch1.max_duty_cycle(); + info!("PWM max duty: {}", max_duty); + + // Create a DMA ring buffer for channel 2 + const BUFFER_SIZE: usize = 128; + static mut DMA_BUFFER: [u16; BUFFER_SIZE] = [0u16; BUFFER_SIZE]; + let dma_buffer = unsafe { &mut *core::ptr::addr_of_mut!(DMA_BUFFER) }; + + // Pre-fill buffer with initial sine wave using lookup table approach + for i in 0..BUFFER_SIZE { + // Simple sine approximation using triangle wave + let phase = (i * 256) / BUFFER_SIZE; + let sine_approx = if phase < 128 { + phase as u16 * 2 + } else { + (255 - phase) as u16 * 2 + }; + dma_buffer[i] = (sine_approx as u32 * max_duty as u32 / 256) as u16; + } + + // Convert channel 2 to ring-buffered PWM + let mut ring_pwm = pwm.into_ring_buffered_channel::(p.DMA2_CH6, dma_buffer); + + info!("Ring buffer capacity: {}", ring_pwm.capacity()); + + // Enable the PWM channel output + ring_pwm.enable(); + + // Pre-write some initial data to the buffer before starting + info!("Pre-writing initial waveform data..."); + + // Start the DMA ring buffer + ring_pwm.start(); + info!("Channel 2 (PE11/D5): Ring buffered sine wave started"); + + // Give DMA time to start consuming + Timer::after_millis(10).await; + + // Continuously update the waveform + let mut phase: f32 = 0.0; + let mut amplitude: f32 = 1.0; + let mut amplitude_direction = -0.05; + + loop { + Timer::after_millis(50).await; + + // Generate new waveform data with varying amplitude + let mut new_data = [0u16; 32]; + for i in 0..new_data.len() { + // Triangle wave approximation for sine + let pos = ((i as u32 + phase as u32) * 4) % 256; + let sine_approx = if pos < 128 { + pos as u16 * 2 + } else { + (255 - pos) as u16 * 2 + }; + let scaled = (sine_approx as u32 * (amplitude * 256.0) as u32) / (256 * 256); + new_data[i] = ((scaled * max_duty as u32) / 256) as u16; + } + + // Write new data to the ring buffer + match ring_pwm.write(&new_data) { + Ok((written, _remaining)) => { + if written < new_data.len() { + info!("Ring buffer getting full, wrote {} of {}", written, new_data.len()); + } + } + Err(e) => { + info!("Write error: {:?}", e); + } + } + + // Update phase for animation effect + phase += 2.0; + if phase >= 64.0 { + phase = 0.0; + } + + // Vary amplitude for breathing effect + amplitude += amplitude_direction; + if amplitude <= 0.2 || amplitude >= 1.0 { + amplitude_direction = -amplitude_direction; + } + + // Log buffer status periodically + if (phase as u32) % 10 == 0 { + match ring_pwm.len() { + Ok(len) => info!("Ring buffer fill: {}/{}", len, ring_pwm.capacity()), + Err(_) => info!("Error reading buffer length"), + } + } + } +} -- cgit From 96ffbec30a0d17470dd93a6c50f4264060bf1e69 Mon Sep 17 00:00:00 2001 From: Eicke Hecht Date: Mon, 24 Nov 2025 22:44:56 +0100 Subject: fix: formatting --- embassy-stm32/src/timer/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/embassy-stm32/src/timer/mod.rs b/embassy-stm32/src/timer/mod.rs index aef3598f1..3fa363881 100644 --- a/embassy-stm32/src/timer/mod.rs +++ b/embassy-stm32/src/timer/mod.rs @@ -12,8 +12,8 @@ pub mod low_level; pub mod one_pulse; pub mod pwm_input; pub mod qei; -pub mod simple_pwm; pub mod ringbuffered; +pub mod simple_pwm; use crate::interrupt; use crate::rcc::RccPeripheral; -- cgit From ff939095a2a883361346ea0cf6f2b6f9d1d24936 Mon Sep 17 00:00:00 2001 From: Eicke Hecht Date: Mon, 24 Nov 2025 23:03:02 +0100 Subject: fix: Formatting, use nightly... --- embassy-stm32/src/timer/simple_pwm.rs | 3 +-- examples/stm32f7/src/bin/pwm.rs | 3 +-- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/embassy-stm32/src/timer/simple_pwm.rs b/embassy-stm32/src/timer/simple_pwm.rs index 8b738741a..bbe7ef595 100644 --- a/embassy-stm32/src/timer/simple_pwm.rs +++ b/embassy-stm32/src/timer/simple_pwm.rs @@ -7,10 +7,9 @@ use super::low_level::{CountingMode, OutputCompareMode, OutputPolarity, Timer}; use super::ringbuffered::RingBufferedPwmChannel; use super::{Ch1, Ch2, Ch3, Ch4, Channel, GeneralInstance4Channel, TimerChannel, TimerPin}; use crate::Peri; -use crate::dma::TransferOptions; -use crate::dma::WritableRingBuffer; #[cfg(not(any(bdma, gpdma)))] use crate::dma::{Burst, FifoThreshold}; +use crate::dma::{TransferOptions, WritableRingBuffer}; #[cfg(gpio_v2)] use crate::gpio::Pull; use crate::gpio::{AfType, AnyPin, OutputType, Speed}; diff --git a/examples/stm32f7/src/bin/pwm.rs b/examples/stm32f7/src/bin/pwm.rs index c1c7ec6ce..b071eb597 100644 --- a/examples/stm32f7/src/bin/pwm.rs +++ b/examples/stm32f7/src/bin/pwm.rs @@ -5,8 +5,7 @@ use defmt::*; use embassy_executor::Spawner; use embassy_stm32::Config; use embassy_stm32::gpio::OutputType; -use embassy_stm32::time::Hertz; -use embassy_stm32::time::mhz; +use embassy_stm32::time::{Hertz, mhz}; use embassy_stm32::timer::simple_pwm::{PwmPin, SimplePwm}; use embassy_time::Timer; use {defmt_rtt as _, panic_probe as _}; -- cgit From 883a998b8da7274d1c157157d5a5d4110904a93c Mon Sep 17 00:00:00 2001 From: Alex Strandberg <8642246+alexstrandberg@users.noreply.github.com> Date: Mon, 24 Nov 2025 17:03:29 -0500 Subject: embassy-rp: add support for TX-only, no SCK SPI --- embassy-rp/CHANGELOG.md | 1 + embassy-rp/src/spi.rs | 25 +++++++++++++++++++++++++ 2 files changed, 26 insertions(+) diff --git a/embassy-rp/CHANGELOG.md b/embassy-rp/CHANGELOG.md index 4b0d738a7..fa8609bbf 100644 --- a/embassy-rp/CHANGELOG.md +++ b/embassy-rp/CHANGELOG.md @@ -18,6 +18,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Add reset_to_usb_boot for rp235x ([#4705](https://github.com/embassy-rs/embassy/pull/4705)) - Add fix #4822 in PIO onewire. Change to disable the state machine before setting y register ([#4824](https://github.com/embassy-rs/embassy/pull/4824)) - Add PIO::Ws2812 color order support +- Add TX-only, no SCK SPI support ## 0.8.0 - 2025-08-26 diff --git a/embassy-rp/src/spi.rs b/embassy-rp/src/spi.rs index d9410e78d..39f128214 100644 --- a/embassy-rp/src/spi.rs +++ b/embassy-rp/src/spi.rs @@ -308,6 +308,11 @@ impl<'d, T: Instance> Spi<'d, T, Blocking> { ) } + /// Create an SPI driver in blocking mode supporting writes only, without SCK pin. + pub fn new_blocking_txonly_nosck(inner: Peri<'d, T>, mosi: Peri<'d, impl MosiPin + 'd>, config: Config) -> Self { + Self::new_inner(inner, None, Some(mosi.into()), None, None, None, None, config) + } + /// Create an SPI driver in blocking mode supporting reads only. pub fn new_blocking_rxonly( inner: Peri<'d, T>, @@ -371,6 +376,26 @@ impl<'d, T: Instance> Spi<'d, T, Async> { ) } + /// Create an SPI driver in async mode supporting DMA write operations only, + /// without SCK pin. + pub fn new_txonly_nosck( + inner: Peri<'d, T>, + mosi: Peri<'d, impl MosiPin + 'd>, + tx_dma: Peri<'d, impl Channel>, + config: Config, + ) -> Self { + Self::new_inner( + inner, + None, + Some(mosi.into()), + None, + None, + Some(tx_dma.into()), + None, + config, + ) + } + /// Create an SPI driver in async mode supporting DMA read operations only. pub fn new_rxonly( inner: Peri<'d, T>, -- cgit From 9330b56e651a864f35e1e72861245a6d221ee725 Mon Sep 17 00:00:00 2001 From: Ulf Lilleengen Date: Tue, 25 Nov 2025 12:40:27 +0100 Subject: feat: add ppi events for SPIS peripheral --- embassy-nrf/CHANGELOG.md | 1 + embassy-nrf/src/spis.rs | 15 +++++++++++++++ 2 files changed, 16 insertions(+) diff --git a/embassy-nrf/CHANGELOG.md b/embassy-nrf/CHANGELOG.md index be79bde5d..cfb040ef5 100644 --- a/embassy-nrf/CHANGELOG.md +++ b/embassy-nrf/CHANGELOG.md @@ -28,6 +28,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - changed: `gpiote::InputChannel::wait()` now ensures events are seen as soon as the function is called, even if the future is not polled - bugfix: use correct flash size for nRF54l - changed: add workaround for anomaly 66 on nrf52 +- added: expose PPI events available on SPIS peripheral ## 0.8.0 - 2025-09-30 diff --git a/embassy-nrf/src/spis.rs b/embassy-nrf/src/spis.rs index 96a9c0ae0..6f837c317 100644 --- a/embassy-nrf/src/spis.rs +++ b/embassy-nrf/src/spis.rs @@ -17,6 +17,7 @@ use crate::gpio::{self, AnyPin, OutputDrive, Pin as GpioPin, SealedPin as _, con use crate::interrupt::typelevel::Interrupt; use crate::pac::gpio::vals as gpiovals; use crate::pac::spis::vals; +use crate::ppi::Event; use crate::util::slice_in_ram_or; use crate::{interrupt, pac}; @@ -334,6 +335,20 @@ impl<'d> Spis<'d> { Ok((n_rx, n_tx)) } + /// Returns the ACQUIRED event, for use with PPI. + /// + /// This event will fire when the semaphore is acquired. + pub fn event_acquired(&self) -> Event<'d> { + Event::from_reg(self.r.events_acquired()) + } + + /// Returns the END event, for use with PPI. + /// + /// This event will fire when the slave transaction is complete. + pub fn event_end(&self) -> Event<'d> { + Event::from_reg(self.r.events_end()) + } + async fn async_inner(&mut self, rx: &mut [u8], tx: &[u8]) -> Result<(usize, usize), Error> { match self.async_inner_from_ram(rx, tx).await { Ok(n) => Ok(n), -- cgit From 424d9d3aa961d4170be96ac23331aa5a3cba3e5b Mon Sep 17 00:00:00 2001 From: xoviat Date: Tue, 25 Nov 2025 08:23:53 -0600 Subject: stm32: remove waveform method --- embassy-stm32/CHANGELOG.md | 1 + embassy-stm32/src/timer/complementary_pwm.rs | 6 ----- embassy-stm32/src/timer/low_level.rs | 37 ---------------------------- embassy-stm32/src/timer/simple_pwm.rs | 6 ----- 4 files changed, 1 insertion(+), 49 deletions(-) diff --git a/embassy-stm32/CHANGELOG.md b/embassy-stm32/CHANGELOG.md index 6140b3238..949ea03b5 100644 --- a/embassy-stm32/CHANGELOG.md +++ b/embassy-stm32/CHANGELOG.md @@ -7,6 +7,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## Unreleased - ReleaseDate +- change: remove waveform timer method - change: low power: store stop mode for dma channels - fix: Fixed ADC4 enable() for WBA - feat: allow use of anyadcchannel for adc4 diff --git a/embassy-stm32/src/timer/complementary_pwm.rs b/embassy-stm32/src/timer/complementary_pwm.rs index 6d4c70dff..9f34f3ec7 100644 --- a/embassy-stm32/src/timer/complementary_pwm.rs +++ b/embassy-stm32/src/timer/complementary_pwm.rs @@ -266,12 +266,6 @@ impl<'d, T: AdvancedInstance4Channel> ComplementaryPwm<'d, T> { .waveform_up_multi_channel(dma, starting_channel, ending_channel, duty) .await; } - - /// Generate a sequence of PWM waveform - #[inline(always)] - pub async fn waveform(&mut self, dma: Peri<'_, impl super::Dma>, duty: &[u16]) { - self.inner.waveform(dma, duty).await; - } } impl<'d, T: AdvancedInstance4Channel> embedded_hal_02::Pwm for ComplementaryPwm<'d, T> { diff --git a/embassy-stm32/src/timer/low_level.rs b/embassy-stm32/src/timer/low_level.rs index f0105ece8..8fbedafdf 100644 --- a/embassy-stm32/src/timer/low_level.rs +++ b/embassy-stm32/src/timer/low_level.rs @@ -771,43 +771,6 @@ impl<'d, T: GeneralInstance4Channel> Timer<'d, T> { } } - /// Generate a sequence of PWM waveform - pub async fn waveform(&mut self, dma: Peri<'_, impl super::Dma>, duty: &[u16]) { - use crate::pac::timer::vals::Ccds; - - #[allow(clippy::let_unit_value)] // eg. stm32f334 - let req = dma.request(); - - let cc_channel = C::CHANNEL; - - let original_cc_dma_on_update = self.get_cc_dma_selection() == Ccds::ON_UPDATE; - let original_cc_dma_enabled = self.get_cc_dma_enable_state(cc_channel); - - // redirect CC DMA request onto Update Event - if !original_cc_dma_on_update { - self.set_cc_dma_selection(Ccds::ON_UPDATE) - } - - if !original_cc_dma_enabled { - self.set_cc_dma_enable_state(cc_channel, true); - } - - self.waveform_helper(dma, req, cc_channel, duty).await; - - // Since DMA is closed before timer Capture Compare Event trigger DMA is turn off, - // this can almost always trigger a DMA FIFO error. - // - // optional TODO: - // clean FEIF after disable UDE - if !original_cc_dma_enabled { - self.set_cc_dma_enable_state(cc_channel, false); - } - - if !original_cc_dma_on_update { - self.set_cc_dma_selection(Ccds::ON_COMPARE) - } - } - async fn waveform_helper( &mut self, dma: Peri<'_, impl dma::Channel>, diff --git a/embassy-stm32/src/timer/simple_pwm.rs b/embassy-stm32/src/timer/simple_pwm.rs index 6c9ef17e0..15399b108 100644 --- a/embassy-stm32/src/timer/simple_pwm.rs +++ b/embassy-stm32/src/timer/simple_pwm.rs @@ -362,12 +362,6 @@ impl<'d, T: GeneralInstance4Channel> SimplePwm<'d, T> { .waveform_up_multi_channel(dma, starting_channel, ending_channel, duty) .await; } - - /// Generate a sequence of PWM waveform - #[inline(always)] - pub async fn waveform(&mut self, dma: Peri<'_, impl super::Dma>, duty: &[u16]) { - self.inner.waveform(dma, duty).await; - } } impl<'d, T: GeneralInstance4Channel> embedded_hal_1::pwm::ErrorType for SimplePwmChannel<'d, T> { -- cgit From 2612f07f549fa0b9d8565ef760814d5f7ebea785 Mon Sep 17 00:00:00 2001 From: xoviat Date: Tue, 25 Nov 2025 08:54:11 -0600 Subject: cleanup low-level timer methods --- embassy-stm32/src/timer/complementary_pwm.rs | 11 ++- embassy-stm32/src/timer/low_level.rs | 137 +++++++++------------------ embassy-stm32/src/timer/simple_pwm.rs | 11 ++- 3 files changed, 59 insertions(+), 100 deletions(-) diff --git a/embassy-stm32/src/timer/complementary_pwm.rs b/embassy-stm32/src/timer/complementary_pwm.rs index 9f34f3ec7..77f19a37b 100644 --- a/embassy-stm32/src/timer/complementary_pwm.rs +++ b/embassy-stm32/src/timer/complementary_pwm.rs @@ -220,9 +220,11 @@ impl<'d, T: AdvancedInstance4Channel> ComplementaryPwm<'d, T> { /// /// Note: /// you will need to provide corresponding TIMx_UP DMA channel to use this method. - #[inline(always)] pub async fn waveform_up(&mut self, dma: Peri<'_, impl super::UpDma>, channel: Channel, duty: &[u16]) { - self.inner.waveform_up(dma, channel, duty).await + self.inner.enable_channel(channel, true); + self.inner.enable_update_dma(true); + self.inner.setup_update_dma(dma, channel, duty).await; + self.inner.enable_update_dma(false); } /// Generate a multichannel sequence of PWM waveforms using DMA triggered by timer update events. @@ -254,7 +256,6 @@ impl<'d, T: AdvancedInstance4Channel> ComplementaryPwm<'d, T> { /// Also be aware that embassy timers use one of timers internally. It is possible to /// switch this timer by using `time-driver-timX` feature. /// - #[inline(always)] pub async fn waveform_up_multi_channel( &mut self, dma: Peri<'_, impl super::UpDma>, @@ -262,9 +263,11 @@ impl<'d, T: AdvancedInstance4Channel> ComplementaryPwm<'d, T> { ending_channel: Channel, duty: &[u16], ) { + self.inner.enable_update_dma(true); self.inner - .waveform_up_multi_channel(dma, starting_channel, ending_channel, duty) + .setup_update_dma_burst(dma, starting_channel, ending_channel, duty) .await; + self.inner.enable_update_dma(false); } } diff --git a/embassy-stm32/src/timer/low_level.rs b/embassy-stm32/src/timer/low_level.rs index 8fbedafdf..f986c8dab 100644 --- a/embassy-stm32/src/timer/low_level.rs +++ b/embassy-stm32/src/timer/low_level.rs @@ -13,9 +13,10 @@ use embassy_hal_internal::Peri; pub use stm32_metapac::timer::vals::{FilterValue, Mms as MasterMode, Sms as SlaveMode, Ts as TriggerSource}; use super::*; +use crate::dma::Transfer; use crate::pac::timer::vals; +use crate::rcc; use crate::time::Hertz; -use crate::{dma, rcc}; /// Input capture mode. #[derive(Clone, Copy)] @@ -663,25 +664,51 @@ impl<'d, T: GeneralInstance4Channel> Timer<'d, T> { /// /// Note: /// you will need to provide corresponding TIMx_UP DMA channel to use this method. - pub async fn waveform_up(&mut self, dma: Peri<'_, impl super::UpDma>, channel: Channel, duty: &[u16]) { + pub fn setup_update_dma<'a>( + &mut self, + dma: Peri<'a, impl super::UpDma>, + channel: Channel, + duty: &'a [u16], + ) -> Transfer<'a> { #[allow(clippy::let_unit_value)] // eg. stm32f334 let req = dma.request(); - let original_update_dma_state = self.get_update_dma_state(); + unsafe { + #[cfg(not(any(bdma, gpdma)))] + use crate::dma::{Burst, FifoThreshold}; + use crate::dma::{Transfer, TransferOptions}; - if !original_update_dma_state { - self.enable_update_dma(true); - } + let dma_transfer_option = TransferOptions { + #[cfg(not(any(bdma, gpdma)))] + fifo_threshold: Some(FifoThreshold::Full), + #[cfg(not(any(bdma, gpdma)))] + mburst: Burst::Incr8, + ..Default::default() + }; - self.waveform_helper(dma, req, channel, duty).await; + match self.bits() { + TimerBits::Bits16 => Transfer::new_write( + dma, + req, + duty, + self.regs_1ch().ccr(channel.index()).as_ptr() as *mut u16, + dma_transfer_option, + ), + #[cfg(not(any(stm32l0)))] + TimerBits::Bits32 => { + #[cfg(not(any(bdma, gpdma)))] + panic!("unsupported timer bits"); - // Since DMA is closed before timer update event trigger DMA is turn off, - // this can almost always trigger a DMA FIFO error. - // - // optional TODO: - // clean FEIF after disable UDE - if !original_update_dma_state { - self.enable_update_dma(false); + #[cfg(any(bdma, gpdma))] + Transfer::new_write( + dma, + req, + duty, + self.regs_1ch().ccr(channel.index()).as_ptr() as *mut u32, + dma_transfer_option, + ) + } + } } } @@ -714,13 +741,13 @@ impl<'d, T: GeneralInstance4Channel> Timer<'d, T> { /// Also be aware that embassy timers use one of timers internally. It is possible to /// switch this timer by using `time-driver-timX` feature. /// - pub async fn waveform_up_multi_channel( + pub fn setup_update_dma_burst<'a>( &mut self, - dma: Peri<'_, impl super::UpDma>, + dma: Peri<'a, impl super::UpDma>, starting_channel: Channel, ending_channel: Channel, - duty: &[u16], - ) { + duty: &'a [u16], + ) -> Transfer<'a> { let cr1_addr = self.regs_gp16().cr1().as_ptr() as u32; let start_ch_index = starting_channel.index(); let end_ch_index = ending_channel.index(); @@ -738,11 +765,6 @@ impl<'d, T: GeneralInstance4Channel> Timer<'d, T> { #[allow(clippy::let_unit_value)] // eg. stm32f334 let req = dma.request(); - let original_update_dma_state = self.get_update_dma_state(); - if !original_update_dma_state { - self.enable_update_dma(true); - } - unsafe { #[cfg(not(any(bdma, gpdma)))] use crate::dma::{Burst, FifoThreshold}; @@ -763,76 +785,7 @@ impl<'d, T: GeneralInstance4Channel> Timer<'d, T> { self.regs_gp16().dmar().as_ptr() as *mut u16, dma_transfer_option, ) - .await - }; - - if !original_update_dma_state { - self.enable_update_dma(false); - } - } - - async fn waveform_helper( - &mut self, - dma: Peri<'_, impl dma::Channel>, - req: dma::Request, - channel: Channel, - duty: &[u16], - ) { - let original_duty_state = self.get_compare_value(channel); - let original_enable_state = self.get_channel_enable_state(channel); - - if !original_enable_state { - self.enable_channel(channel, true); - } - - unsafe { - #[cfg(not(any(bdma, gpdma)))] - use crate::dma::{Burst, FifoThreshold}; - use crate::dma::{Transfer, TransferOptions}; - - let dma_transfer_option = TransferOptions { - #[cfg(not(any(bdma, gpdma)))] - fifo_threshold: Some(FifoThreshold::Full), - #[cfg(not(any(bdma, gpdma)))] - mburst: Burst::Incr8, - ..Default::default() - }; - - match self.bits() { - TimerBits::Bits16 => { - Transfer::new_write( - dma, - req, - duty, - self.regs_1ch().ccr(channel.index()).as_ptr() as *mut u16, - dma_transfer_option, - ) - .await - } - #[cfg(not(any(stm32l0)))] - TimerBits::Bits32 => { - #[cfg(not(any(bdma, gpdma)))] - panic!("unsupported timer bits"); - - #[cfg(any(bdma, gpdma))] - Transfer::new_write( - dma, - req, - duty, - self.regs_1ch().ccr(channel.index()).as_ptr() as *mut u32, - dma_transfer_option, - ) - .await - } - }; - }; - - // restore output compare state - if !original_enable_state { - self.enable_channel(channel, false); } - - self.set_compare_value(channel, original_duty_state); } /// Get capture value for a channel. diff --git a/embassy-stm32/src/timer/simple_pwm.rs b/embassy-stm32/src/timer/simple_pwm.rs index 15399b108..eb1b66358 100644 --- a/embassy-stm32/src/timer/simple_pwm.rs +++ b/embassy-stm32/src/timer/simple_pwm.rs @@ -316,9 +316,11 @@ impl<'d, T: GeneralInstance4Channel> SimplePwm<'d, T> { /// You will need to provide corresponding `TIMx_UP` DMA channel to use this method. /// Also be aware that embassy timers use one of timers internally. It is possible to /// switch this timer by using `time-driver-timX` feature. - #[inline(always)] pub async fn waveform_up(&mut self, dma: Peri<'_, impl super::UpDma>, channel: Channel, duty: &[u16]) { - self.inner.waveform_up(dma, channel, duty).await; + self.inner.enable_channel(channel, true); + self.inner.enable_update_dma(true); + self.inner.setup_update_dma(dma, channel, duty).await; + self.inner.enable_update_dma(false); } /// Generate a multichannel sequence of PWM waveforms using DMA triggered by timer update events. @@ -350,7 +352,6 @@ impl<'d, T: GeneralInstance4Channel> SimplePwm<'d, T> { /// Also be aware that embassy timers use one of timers internally. It is possible to /// switch this timer by using `time-driver-timX` feature. /// - #[inline(always)] pub async fn waveform_up_multi_channel( &mut self, dma: Peri<'_, impl super::UpDma>, @@ -358,9 +359,11 @@ impl<'d, T: GeneralInstance4Channel> SimplePwm<'d, T> { ending_channel: Channel, duty: &[u16], ) { + self.inner.enable_update_dma(true); self.inner - .waveform_up_multi_channel(dma, starting_channel, ending_channel, duty) + .setup_update_dma_burst(dma, starting_channel, ending_channel, duty) .await; + self.inner.enable_update_dma(false); } } -- cgit From 843d890483561d11aa217a68891e2f4ae8de2f94 Mon Sep 17 00:00:00 2001 From: Matan Radomski Date: Tue, 25 Nov 2025 17:35:21 +0200 Subject: Added 375KHz Clock Support --- embassy-time-driver/Cargo.toml | 2 ++ embassy-time-driver/gen_tick.py | 3 ++- embassy-time-driver/src/tick.rs | 3 +++ embassy-time/Cargo.toml | 2 ++ 4 files changed, 9 insertions(+), 1 deletion(-) diff --git a/embassy-time-driver/Cargo.toml b/embassy-time-driver/Cargo.toml index a52e82433..cbb6168b9 100644 --- a/embassy-time-driver/Cargo.toml +++ b/embassy-time-driver/Cargo.toml @@ -118,6 +118,8 @@ tick-hz-256_000 = [] tick-hz-262_144 = [] ## 320.0kHz Tick Rate tick-hz-320_000 = [] +## 375.0kHz Tick Rate +tick-hz-375_000 = [] ## 512.0kHz Tick Rate tick-hz-512_000 = [] ## 524.288kHz Tick Rate diff --git a/embassy-time-driver/gen_tick.py b/embassy-time-driver/gen_tick.py index 080434457..3cb6552df 100644 --- a/embassy-time-driver/gen_tick.py +++ b/embassy-time-driver/gen_tick.py @@ -1,5 +1,4 @@ import os -from glob import glob abspath = os.path.abspath(__file__) dname = os.path.dirname(abspath) @@ -22,6 +21,8 @@ for i in range(1, 30): ticks.append(10 * i * 1_000_000) for i in range(15, 50): ticks.append(20 * i * 1_000_000) + +ticks.append(375 * 1000) ticks.append(133 * 1_000_000) seen = set() diff --git a/embassy-time-driver/src/tick.rs b/embassy-time-driver/src/tick.rs index 5059e1628..247ec9ab3 100644 --- a/embassy-time-driver/src/tick.rs +++ b/embassy-time-driver/src/tick.rs @@ -74,6 +74,8 @@ pub const TICK_HZ: u64 = 256_000; pub const TICK_HZ: u64 = 262_144; #[cfg(feature = "tick-hz-320_000")] pub const TICK_HZ: u64 = 320_000; +#[cfg(feature = "tick-hz-375_000")] +pub const TICK_HZ: u64 = 375_000; #[cfg(feature = "tick-hz-512_000")] pub const TICK_HZ: u64 = 512_000; #[cfg(feature = "tick-hz-524_288")] @@ -358,6 +360,7 @@ pub const TICK_HZ: u64 = 5_242_880_000; feature = "tick-hz-256_000", feature = "tick-hz-262_144", feature = "tick-hz-320_000", + feature = "tick-hz-375_000", feature = "tick-hz-512_000", feature = "tick-hz-524_288", feature = "tick-hz-640_000", diff --git a/embassy-time/Cargo.toml b/embassy-time/Cargo.toml index 05614dbf5..a7ed51e78 100644 --- a/embassy-time/Cargo.toml +++ b/embassy-time/Cargo.toml @@ -178,6 +178,8 @@ tick-hz-256_000 = ["embassy-time-driver/tick-hz-256_000"] tick-hz-262_144 = ["embassy-time-driver/tick-hz-262_144"] ## 320.0kHz Tick Rate tick-hz-320_000 = ["embassy-time-driver/tick-hz-320_000"] +## 375.0kHz Tick Rate +tick-hz-375_000 = ["embassy-time-driver/tick-hz-375_000"] ## 512.0kHz Tick Rate tick-hz-512_000 = ["embassy-time-driver/tick-hz-512_000"] ## 524.288kHz Tick Rate -- cgit From 2632bc42e02a4162680cefaf9f08689952907c0b Mon Sep 17 00:00:00 2001 From: Matan Radomski Date: Tue, 25 Nov 2025 17:39:11 +0200 Subject: Updated Changelog --- embassy-time-driver/CHANGELOG.md | 3 ++- embassy-time/CHANGELOG.md | 1 + 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/embassy-time-driver/CHANGELOG.md b/embassy-time-driver/CHANGELOG.md index cdd432437..4951f8c3e 100644 --- a/embassy-time-driver/CHANGELOG.md +++ b/embassy-time-driver/CHANGELOG.md @@ -11,7 +11,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## 0.2.1 - 2025-08-26 - Allow inlining on time driver boundary -- add 133MHz tick rate to support PR2040 @ 133MHz when `TIMERx`'s `SOURCE` is set to `SYSCLK` +- Add 133MHz tick rate to support PR2040 @ 133MHz when `TIMERx`'s `SOURCE` is set to `SYSCLK` +- Add 375KHz tick rate support ## 0.2.0 - 2025-01-02 diff --git a/embassy-time/CHANGELOG.md b/embassy-time/CHANGELOG.md index 4a50da8ef..17f8a3837 100644 --- a/embassy-time/CHANGELOG.md +++ b/embassy-time/CHANGELOG.md @@ -9,6 +9,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## Unreleased - ReleaseDate - Add as_nanos and from_nanos where missing +- Added 375KHz tick rate support ## 0.5.0 - 2025-08-26 -- cgit From 2e70c376c884a64fc931406350fecb6c0314dcf0 Mon Sep 17 00:00:00 2001 From: xoviat Date: Tue, 25 Nov 2025 10:14:55 -0600 Subject: timer: add writable ring buffer --- embassy-stm32/CHANGELOG.md | 1 + embassy-stm32/src/timer/low_level.rs | 35 +++++- embassy-stm32/src/timer/mod.rs | 1 + embassy-stm32/src/timer/ringbuffered.rs | 169 +++++++++++++++++++++++++++++ embassy-stm32/src/timer/simple_pwm.rs | 31 ++++++ examples/stm32f7/src/bin/pwm.rs | 61 +++++++++++ examples/stm32f7/src/bin/pwm_ringbuffer.rs | 153 ++++++++++++++++++++++++++ 7 files changed, 450 insertions(+), 1 deletion(-) create mode 100644 embassy-stm32/src/timer/ringbuffered.rs create mode 100644 examples/stm32f7/src/bin/pwm.rs create mode 100644 examples/stm32f7/src/bin/pwm_ringbuffer.rs diff --git a/embassy-stm32/CHANGELOG.md b/embassy-stm32/CHANGELOG.md index 949ea03b5..d3e5ba48d 100644 --- a/embassy-stm32/CHANGELOG.md +++ b/embassy-stm32/CHANGELOG.md @@ -7,6 +7,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## Unreleased - ReleaseDate +- feat: Add continuous waveform method to SimplePWM - change: remove waveform timer method - change: low power: store stop mode for dma channels - fix: Fixed ADC4 enable() for WBA diff --git a/embassy-stm32/src/timer/low_level.rs b/embassy-stm32/src/timer/low_level.rs index f986c8dab..aba08081f 100644 --- a/embassy-stm32/src/timer/low_level.rs +++ b/embassy-stm32/src/timer/low_level.rs @@ -13,7 +13,7 @@ use embassy_hal_internal::Peri; pub use stm32_metapac::timer::vals::{FilterValue, Mms as MasterMode, Sms as SlaveMode, Ts as TriggerSource}; use super::*; -use crate::dma::Transfer; +use crate::dma::{Transfer, WritableRingBuffer}; use crate::pac::timer::vals; use crate::rcc; use crate::time::Hertz; @@ -660,6 +660,39 @@ impl<'d, T: GeneralInstance4Channel> Timer<'d, T> { } } + /// Setup a ring buffer for the channel + pub fn setup_ring_buffer<'a>( + &mut self, + dma: Peri<'a, impl super::UpDma>, + channel: Channel, + dma_buf: &'a mut [u16], + ) -> WritableRingBuffer<'a, u16> { + #[allow(clippy::let_unit_value)] // eg. stm32f334 + let req = dma.request(); + + unsafe { + use crate::dma::TransferOptions; + #[cfg(not(any(bdma, gpdma)))] + use crate::dma::{Burst, FifoThreshold}; + + let dma_transfer_option = TransferOptions { + #[cfg(not(any(bdma, gpdma)))] + fifo_threshold: Some(FifoThreshold::Full), + #[cfg(not(any(bdma, gpdma)))] + mburst: Burst::Incr8, + ..Default::default() + }; + + WritableRingBuffer::new( + dma, + req, + self.regs_1ch().ccr(channel.index()).as_ptr() as *mut u16, + dma_buf, + dma_transfer_option, + ) + } + } + /// Generate a sequence of PWM waveform /// /// Note: diff --git a/embassy-stm32/src/timer/mod.rs b/embassy-stm32/src/timer/mod.rs index 804d1ef37..3fa363881 100644 --- a/embassy-stm32/src/timer/mod.rs +++ b/embassy-stm32/src/timer/mod.rs @@ -12,6 +12,7 @@ pub mod low_level; pub mod one_pulse; pub mod pwm_input; pub mod qei; +pub mod ringbuffered; pub mod simple_pwm; use crate::interrupt; diff --git a/embassy-stm32/src/timer/ringbuffered.rs b/embassy-stm32/src/timer/ringbuffered.rs new file mode 100644 index 000000000..e8f97bf59 --- /dev/null +++ b/embassy-stm32/src/timer/ringbuffered.rs @@ -0,0 +1,169 @@ +//! RingBuffered PWM driver. + +use core::mem::ManuallyDrop; +use core::task::Waker; + +use super::low_level::Timer; +use super::{Channel, GeneralInstance4Channel}; +use crate::dma::WritableRingBuffer; +use crate::dma::ringbuffer::Error; + +/// A PWM channel that uses a DMA ring buffer for continuous waveform generation. +/// +/// This allows you to continuously update PWM duty cycles via DMA without blocking the CPU. +/// The ring buffer enables smooth, uninterrupted waveform generation by automatically cycling +/// through duty cycle values stored in memory. +/// +/// You can write new duty cycle values to the ring buffer while it's running, enabling +/// dynamic waveform generation for applications like motor control, LED dimming, or audio output. +/// +/// # Example +/// ```ignore +/// let mut channel = pwm.ch1().into_ring_buffered_channel(dma_ch, &mut buffer); +/// channel.start(); // Start DMA transfer +/// channel.write(&[100, 200, 300]).ok(); // Update duty cycles +/// ``` +pub struct RingBufferedPwmChannel<'d, T: GeneralInstance4Channel> { + timer: ManuallyDrop>, + ring_buf: WritableRingBuffer<'d, u16>, + channel: Channel, +} + +impl<'d, T: GeneralInstance4Channel> RingBufferedPwmChannel<'d, T> { + pub(crate) fn new( + timer: ManuallyDrop>, + channel: Channel, + ring_buf: WritableRingBuffer<'d, u16>, + ) -> Self { + Self { + timer, + ring_buf, + channel, + } + } + + /// Start the ring buffer operation. + /// + /// You must call this after creating it for it to work. + pub fn start(&mut self) { + self.ring_buf.start() + } + + /// Clear all data in the ring buffer. + pub fn clear(&mut self) { + self.ring_buf.clear() + } + + /// Write elements directly to the raw buffer. This can be used to fill the buffer before starting the DMA transfer. + pub fn write_immediate(&mut self, buf: &[u16]) -> Result<(usize, usize), Error> { + self.ring_buf.write_immediate(buf) + } + + /// Write elements from the ring buffer + /// Return a tuple of the length written and the length remaining in the buffer + pub fn write(&mut self, buf: &[u16]) -> Result<(usize, usize), Error> { + self.ring_buf.write(buf) + } + + /// Write an exact number of elements to the ringbuffer. + pub async fn write_exact(&mut self, buffer: &[u16]) -> Result { + self.ring_buf.write_exact(buffer).await + } + + /// Wait for any ring buffer write error. + pub async fn wait_write_error(&mut self) -> Result { + self.ring_buf.wait_write_error().await + } + + /// The current length of the ringbuffer + pub fn len(&mut self) -> Result { + self.ring_buf.len() + } + + /// The capacity of the ringbuffer + pub const fn capacity(&self) -> usize { + self.ring_buf.capacity() + } + + /// Set a waker to be woken when at least one byte is send. + pub fn set_waker(&mut self, waker: &Waker) { + self.ring_buf.set_waker(waker) + } + + /// Request the DMA to reset. The configuration for this channel will not be preserved. + /// + /// This doesn't immediately stop the transfer, you have to wait until is_running returns false. + pub fn request_reset(&mut self) { + self.ring_buf.request_reset() + } + + /// Request the transfer to pause, keeping the existing configuration for this channel. + /// To restart the transfer, call [`start`](Self::start) again. + /// + /// This doesn't immediately stop the transfer, you have to wait until is_running returns false. + pub fn request_pause(&mut self) { + self.ring_buf.request_pause() + } + + /// Return whether DMA is still running. + /// + /// If this returns false, it can be because either the transfer finished, or it was requested to stop early with request_stop. + pub fn is_running(&mut self) -> bool { + self.ring_buf.is_running() + } + + /// Stop the DMA transfer and await until the buffer is empty. + /// + /// This disables the DMA transfer's circular mode so that the transfer stops when all available data has been written. + /// + /// This is designed to be used with streaming output data such as the I2S/SAI or DAC. + pub async fn stop(&mut self) { + self.ring_buf.stop().await + } + + /// Enable the given channel. + pub fn enable(&mut self) { + self.timer.enable_channel(self.channel, true); + } + + /// Disable the given channel. + pub fn disable(&mut self) { + self.timer.enable_channel(self.channel, false); + } + + /// Check whether given channel is enabled + pub fn is_enabled(&self) -> bool { + self.timer.get_channel_enable_state(self.channel) + } + + /// Get max duty value. + /// + /// This value depends on the configured frequency and the timer's clock rate from RCC. + pub fn max_duty_cycle(&self) -> u16 { + let max = self.timer.get_max_compare_value(); + assert!(max < u16::MAX as u32); + max as u16 + 1 + } + + /// Set the output polarity for a given channel. + pub fn set_polarity(&mut self, polarity: super::low_level::OutputPolarity) { + self.timer.set_output_polarity(self.channel, polarity); + } + + /// Set the output compare mode for a given channel. + pub fn set_output_compare_mode(&mut self, mode: super::low_level::OutputCompareMode) { + self.timer.set_output_compare_mode(self.channel, mode); + } +} + +/// A group of four [`SimplePwmChannel`]s, obtained from [`SimplePwm::split`]. +pub struct RingBufferedPwmChannels<'d, T: GeneralInstance4Channel> { + /// Channel 1 + pub ch1: RingBufferedPwmChannel<'d, T>, + /// Channel 2 + pub ch2: RingBufferedPwmChannel<'d, T>, + /// Channel 3 + pub ch3: RingBufferedPwmChannel<'d, T>, + /// Channel 4 + pub ch4: RingBufferedPwmChannel<'d, T>, +} diff --git a/embassy-stm32/src/timer/simple_pwm.rs b/embassy-stm32/src/timer/simple_pwm.rs index eb1b66358..bc33a52ea 100644 --- a/embassy-stm32/src/timer/simple_pwm.rs +++ b/embassy-stm32/src/timer/simple_pwm.rs @@ -4,8 +4,12 @@ use core::marker::PhantomData; use core::mem::ManuallyDrop; use super::low_level::{CountingMode, OutputCompareMode, OutputPolarity, Timer}; +use super::ringbuffered::RingBufferedPwmChannel; use super::{Ch1, Ch2, Ch3, Ch4, Channel, GeneralInstance4Channel, TimerChannel, TimerPin}; use crate::Peri; +#[cfg(not(any(bdma, gpdma)))] +use crate::dma::{Burst, FifoThreshold}; +use crate::dma::{TransferOptions, WritableRingBuffer}; #[cfg(gpio_v2)] use crate::gpio::Pull; use crate::gpio::{AfType, AnyPin, OutputType, Speed}; @@ -158,6 +162,33 @@ impl<'d, T: GeneralInstance4Channel> SimplePwmChannel<'d, T> { pub fn set_output_compare_mode(&mut self, mode: OutputCompareMode) { self.timer.set_output_compare_mode(self.channel, mode); } + + /// Convert this PWM channel into a ring-buffered PWM channel. + /// + /// This allows continuous PWM waveform generation using a DMA ring buffer. + /// The ring buffer enables dynamic updates to the PWM duty cycle without blocking. + /// + /// # Arguments + /// * `tx_dma` - The DMA channel to use for transferring duty cycle values + /// * `dma_buf` - The buffer to use as a ring buffer (must be non-empty and <= 65535 elements) + /// + /// # Panics + /// Panics if `dma_buf` is empty or longer than 65535 elements. + pub fn into_ring_buffered_channel( + mut self, + tx_dma: Peri<'d, impl super::UpDma>, + dma_buf: &'d mut [u16], + ) -> RingBufferedPwmChannel<'d, T> { + assert!(!dma_buf.is_empty() && dma_buf.len() <= 0xFFFF); + + self.timer.enable_update_dma(true); + + RingBufferedPwmChannel::new( + unsafe { self.timer.clone_unchecked() }, + self.channel, + self.timer.setup_ring_buffer(tx_dma, self.channel, dma_buf), + ) + } } /// A group of four [`SimplePwmChannel`]s, obtained from [`SimplePwm::split`]. diff --git a/examples/stm32f7/src/bin/pwm.rs b/examples/stm32f7/src/bin/pwm.rs new file mode 100644 index 000000000..b071eb597 --- /dev/null +++ b/examples/stm32f7/src/bin/pwm.rs @@ -0,0 +1,61 @@ +#![no_std] +#![no_main] + +use defmt::*; +use embassy_executor::Spawner; +use embassy_stm32::Config; +use embassy_stm32::gpio::OutputType; +use embassy_stm32::time::{Hertz, mhz}; +use embassy_stm32::timer::simple_pwm::{PwmPin, SimplePwm}; +use embassy_time::Timer; +use {defmt_rtt as _, panic_probe as _}; + +// If you are trying this and your USB device doesn't connect, the most +// common issues are the RCC config and vbus_detection +// +// See https://embassy.dev/book/#_the_usb_examples_are_not_working_on_my_board_is_there_anything_else_i_need_to_configure +// for more information. +#[embassy_executor::main] +async fn main(_spawner: Spawner) { + info!("Hello World!"); + + let mut config = Config::default(); + { + use embassy_stm32::rcc::*; + config.rcc.hse = Some(Hse { + freq: Hertz(8_000_000), + mode: HseMode::Bypass, + }); + config.rcc.pll_src = PllSource::HSE; + config.rcc.pll = Some(Pll { + prediv: PllPreDiv::DIV4, + mul: PllMul::MUL200, + divp: Some(PllPDiv::DIV2), // 8mhz / 4 * 200 / 2 = 200Mhz + divq: Some(PllQDiv::DIV4), // 8mhz / 4 * 200 / 4 = 100Mhz + divr: None, + }); + config.rcc.ahb_pre = AHBPrescaler::DIV1; + config.rcc.apb1_pre = APBPrescaler::DIV4; + config.rcc.apb2_pre = APBPrescaler::DIV2; + config.rcc.sys = Sysclk::PLL1_P; + } + let p = embassy_stm32::init(config); + let ch1_pin = PwmPin::new(p.PE9, OutputType::PushPull); + let mut pwm = SimplePwm::new(p.TIM1, Some(ch1_pin), None, None, None, mhz(1), Default::default()); + let mut ch1 = pwm.ch1(); + ch1.enable(); + + info!("PWM initialized"); + info!("PWM max duty {}", ch1.max_duty_cycle()); + + loop { + ch1.set_duty_cycle_fully_off(); + Timer::after_millis(300).await; + ch1.set_duty_cycle_fraction(1, 4); + Timer::after_millis(300).await; + ch1.set_duty_cycle_fraction(1, 2); + Timer::after_millis(300).await; + ch1.set_duty_cycle(ch1.max_duty_cycle() - 1); + Timer::after_millis(300).await; + } +} diff --git a/examples/stm32f7/src/bin/pwm_ringbuffer.rs b/examples/stm32f7/src/bin/pwm_ringbuffer.rs new file mode 100644 index 000000000..4d191ac13 --- /dev/null +++ b/examples/stm32f7/src/bin/pwm_ringbuffer.rs @@ -0,0 +1,153 @@ +#![no_std] +#![no_main] + +use defmt::*; +use embassy_executor::Spawner; +use embassy_stm32::Config; +use embassy_stm32::gpio::OutputType; +use embassy_stm32::time::mhz; +use embassy_stm32::timer::simple_pwm::{PwmPin, SimplePwm}; +use embassy_time::Timer; +use {defmt_rtt as _, panic_probe as _}; + +// If you are trying this and your USB device doesn't connect, the most +// common issues are the RCC config and vbus_detection +// +// See https://embassy.dev/book/#_the_usb_examples_are_not_working_on_my_board_is_there_anything_else_i_need_to_configure +// for more information. +#[embassy_executor::main] +async fn main(_spawner: Spawner) { + info!("PWM Ring Buffer Example"); + + let mut config = Config::default(); + { + use embassy_stm32::rcc::*; + use embassy_stm32::time::Hertz; + config.rcc.hse = Some(Hse { + freq: Hertz(8_000_000), + mode: HseMode::Bypass, + }); + config.rcc.pll_src = PllSource::HSE; + config.rcc.pll = Some(Pll { + prediv: PllPreDiv::DIV4, + mul: PllMul::MUL200, + divp: Some(PllPDiv::DIV2), // 8mhz / 4 * 200 / 2 = 200Mhz + divq: Some(PllQDiv::DIV4), // 8mhz / 4 * 200 / 4 = 100Mhz + divr: None, + }); + config.rcc.ahb_pre = AHBPrescaler::DIV1; + config.rcc.apb1_pre = APBPrescaler::DIV4; + config.rcc.apb2_pre = APBPrescaler::DIV2; + config.rcc.sys = Sysclk::PLL1_P; + } + let p = embassy_stm32::init(config); + + // Initialize PWM on TIM1 + let ch1_pin = PwmPin::new(p.PE9, OutputType::PushPull); + let ch2_pin = PwmPin::new(p.PE11, OutputType::PushPull); + let mut pwm = SimplePwm::new( + p.TIM1, + Some(ch1_pin), + Some(ch2_pin), + None, + None, + mhz(1), + Default::default(), + ); + + // Use channel 1 for static PWM at 50% + let mut ch1 = pwm.ch1(); + ch1.enable(); + ch1.set_duty_cycle_fraction(1, 2); + info!("Channel 1 (PE9/D6): Static 50% duty cycle"); + + // Get max duty from channel 1 before converting channel 2 + let max_duty = ch1.max_duty_cycle(); + info!("PWM max duty: {}", max_duty); + + // Create a DMA ring buffer for channel 2 + const BUFFER_SIZE: usize = 128; + static mut DMA_BUFFER: [u16; BUFFER_SIZE] = [0u16; BUFFER_SIZE]; + let dma_buffer = unsafe { &mut *core::ptr::addr_of_mut!(DMA_BUFFER) }; + + // Pre-fill buffer with initial sine wave using lookup table approach + for i in 0..BUFFER_SIZE { + // Simple sine approximation using triangle wave + let phase = (i * 256) / BUFFER_SIZE; + let sine_approx = if phase < 128 { + phase as u16 * 2 + } else { + (255 - phase) as u16 * 2 + }; + dma_buffer[i] = (sine_approx as u32 * max_duty as u32 / 256) as u16; + } + + // Convert channel 2 to ring-buffered PWM + let mut ring_pwm = pwm.ch1().into_ring_buffered_channel(p.DMA2_CH5, dma_buffer); + + info!("Ring buffer capacity: {}", ring_pwm.capacity()); + + // Pre-write some initial data to the buffer before starting + info!("Pre-writing initial waveform data..."); + + ring_pwm.write(&[0; BUFFER_SIZE]).unwrap(); + + // Enable the PWM channel output + ring_pwm.enable(); + + // Start the DMA ring buffer + ring_pwm.start(); + info!("Channel 2 (PE11/D5): Ring buffered sine wave started"); + + // Give DMA time to start consuming + Timer::after_millis(10).await; + + // Continuously update the waveform + let mut phase: f32 = 0.0; + let mut amplitude: f32 = 1.0; + let mut amplitude_direction = -0.05; + + loop { + // Generate new waveform data with varying amplitude + let mut new_data = [0u16; 32]; + for i in 0..new_data.len() { + // Triangle wave approximation for sine + let pos = ((i as u32 + phase as u32) * 4) % 256; + let sine_approx = if pos < 128 { + pos as u16 * 2 + } else { + (255 - pos) as u16 * 2 + }; + let scaled = (sine_approx as u32 * (amplitude * 256.0) as u32) / (256 * 256); + new_data[i] = ((scaled * max_duty as u32) / 256) as u16; + } + + // Write new data to the ring buffer + match ring_pwm.write_exact(&new_data).await { + Ok(_remaining) => {} + Err(e) => { + info!("Write error: {:?}", e); + } + } + + // Update phase for animation effect + phase += 2.0; + if phase >= 64.0 { + phase = 0.0; + } + + // Vary amplitude for breathing effect + amplitude += amplitude_direction; + if amplitude <= 0.2 || amplitude >= 1.0 { + amplitude_direction = -amplitude_direction; + } + + // Log buffer status periodically + if (phase as u32) % 10 == 0 { + match ring_pwm.len() { + Ok(len) => info!("Ring buffer fill: {}/{}", len, ring_pwm.capacity()), + Err(_) => info!("Error reading buffer length"), + } + } + } +} -- cgit From b2ec93caf32153157fd87cceccaaec460ada7aaa Mon Sep 17 00:00:00 2001 From: xoviat Date: Tue, 25 Nov 2025 10:18:55 -0600 Subject: pwm: cleanup --- embassy-stm32/src/timer/simple_pwm.rs | 53 ----------------------------------- 1 file changed, 53 deletions(-) diff --git a/embassy-stm32/src/timer/simple_pwm.rs b/embassy-stm32/src/timer/simple_pwm.rs index bd0b1d7de..bc33a52ea 100644 --- a/embassy-stm32/src/timer/simple_pwm.rs +++ b/embassy-stm32/src/timer/simple_pwm.rs @@ -396,59 +396,6 @@ impl<'d, T: GeneralInstance4Channel> SimplePwm<'d, T> { .await; self.inner.enable_update_dma(false); } - /// Generate a sequence of PWM waveform that will run continously - /// You may want to start this in a new thread as this will block forever - #[inline(always)] - pub async fn waveform_continuous(&mut self, dma: Peri<'_, impl super::Dma>, duty: &[u16]) { - self.inner.waveform_continuous(dma, duty).await; - } - - /// Convert this PWM channel into a ring-buffered PWM channel. - /// - /// This allows continuous PWM waveform generation using a DMA ring buffer. - /// The ring buffer enables dynamic updates to the PWM duty cycle without blocking. - /// - /// # Arguments - /// * `tx_dma` - The DMA channel to use for transferring duty cycle values - /// * `dma_buf` - The buffer to use as a ring buffer (must be non-empty and <= 65535 elements) - /// - /// # Panics - /// Panics if `dma_buf` is empty or longer than 65535 elements. - pub fn into_ring_buffered_channel( - self, - tx_dma: Peri<'d, impl super::Dma>, - dma_buf: &'d mut [u16], - ) -> RingBufferedPwmChannel<'d, T> { - assert!(!dma_buf.is_empty() && dma_buf.len() <= 0xFFFF); - - use crate::pac::timer::vals::Ccds; - - let channel = C::CHANNEL; - let request = tx_dma.request(); - - let opts = TransferOptions { - #[cfg(not(any(bdma, gpdma)))] - fifo_threshold: Some(FifoThreshold::Full), - #[cfg(not(any(bdma, gpdma)))] - mburst: Burst::Incr8, - ..Default::default() - }; - - self.inner.set_cc_dma_selection(Ccds::ON_UPDATE); - self.inner.set_cc_dma_enable_state(channel, true); - - let ring_buf = unsafe { - WritableRingBuffer::new( - tx_dma, - request, - self.inner.regs_gp16().ccr(channel.index()).as_ptr() as *mut u16, - dma_buf, - opts, - ) - }; - - RingBufferedPwmChannel::new(unsafe { self.inner.clone_unchecked() }, channel, ring_buf) - } } impl<'d, T: GeneralInstance4Channel> embedded_hal_1::pwm::ErrorType for SimplePwmChannel<'d, T> { -- cgit From 309ee44c7484c4d11adc3fbd527536027eac8a94 Mon Sep 17 00:00:00 2001 From: xoviat Date: Tue, 25 Nov 2025 10:25:06 -0600 Subject: fmt --- embassy-stm32/src/timer/simple_pwm.rs | 3 --- 1 file changed, 3 deletions(-) diff --git a/embassy-stm32/src/timer/simple_pwm.rs b/embassy-stm32/src/timer/simple_pwm.rs index bc33a52ea..484e9fd81 100644 --- a/embassy-stm32/src/timer/simple_pwm.rs +++ b/embassy-stm32/src/timer/simple_pwm.rs @@ -7,9 +7,6 @@ use super::low_level::{CountingMode, OutputCompareMode, OutputPolarity, Timer}; use super::ringbuffered::RingBufferedPwmChannel; use super::{Ch1, Ch2, Ch3, Ch4, Channel, GeneralInstance4Channel, TimerChannel, TimerPin}; use crate::Peri; -#[cfg(not(any(bdma, gpdma)))] -use crate::dma::{Burst, FifoThreshold}; -use crate::dma::{TransferOptions, WritableRingBuffer}; #[cfg(gpio_v2)] use crate::gpio::Pull; use crate::gpio::{AfType, AnyPin, OutputType, Speed}; -- cgit