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 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 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 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 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 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