From 663fa2addd30463328f6186e14f98680b4a35c9b Mon Sep 17 00:00:00 2001 From: Frostie314159 Date: Mon, 11 Dec 2023 13:27:55 +0100 Subject: [PATCH 01/62] Introduce reset_{at|after} functions for Ticker. --- embassy-time/src/timer.rs | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/embassy-time/src/timer.rs b/embassy-time/src/timer.rs index 574d715da..3444d3e24 100644 --- a/embassy-time/src/timer.rs +++ b/embassy-time/src/timer.rs @@ -184,6 +184,16 @@ impl Ticker { self.expires_at = Instant::now() + self.duration; } + /// Reset the ticker to fire for the next time on the deadline. + pub fn reset_at(&mut self, deadline: Instant) { + self.expires_at = deadline; + } + + /// Resets the ticker, after the specified duration has passed. + pub fn reset_after(&mut self, after: Duration) { + self.expires_at = Instant::now() + after; + } + /// Waits for the next tick. pub fn next(&mut self) -> impl Future + '_ { poll_fn(|cx| { From 8707462ec23807782796fbac4295bc5bce9ff136 Mon Sep 17 00:00:00 2001 From: Frostie314159 Date: Mon, 11 Dec 2023 16:11:57 +0100 Subject: [PATCH 02/62] Adjusted documentation and reset_after behaviour. --- embassy-time/src/timer.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/embassy-time/src/timer.rs b/embassy-time/src/timer.rs index 3444d3e24..fe0e93951 100644 --- a/embassy-time/src/timer.rs +++ b/embassy-time/src/timer.rs @@ -185,13 +185,15 @@ impl Ticker { } /// Reset the ticker to fire for the next time on the deadline. + /// If the deadline is in the past, the ticker will fire instantly. pub fn reset_at(&mut self, deadline: Instant) { self.expires_at = deadline; } /// Resets the ticker, after the specified duration has passed. + /// If the specified duration is zero, the next tick will be after the duration of the ticker. pub fn reset_after(&mut self, after: Duration) { - self.expires_at = Instant::now() + after; + self.expires_at = Instant::now() + after + self.duration; } /// Waits for the next tick. From 84258e168091f1cba226f2abd91fbfa5431c12c2 Mon Sep 17 00:00:00 2001 From: Grant Miller Date: Wed, 14 Feb 2024 15:57:06 -0600 Subject: [PATCH 03/62] wip --- embassy-executor-macros/src/macros/task.rs | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/embassy-executor-macros/src/macros/task.rs b/embassy-executor-macros/src/macros/task.rs index 1efb2788b..82579cbbe 100644 --- a/embassy-executor-macros/src/macros/task.rs +++ b/embassy-executor-macros/src/macros/task.rs @@ -93,10 +93,22 @@ pub fn run(args: &[NestedMeta], f: syn::ItemFn) -> Result ::embassy_executor::SpawnToken { - type Fut = impl ::core::future::Future + 'static; + trait Task { + type Fut: core::future::Future + 'static; + fn construct(#fargs) -> Self::Fut; + } + + struct ThisTask; + impl Task for ThisTask { + type Fut = impl core::future::Future + 'static; + fn construct(#fargs) -> Self::Fut { + #task_inner_ident(#(#full_args,)*) + } + } + const POOL_SIZE: usize = #pool_size; - static POOL: ::embassy_executor::raw::TaskPool = ::embassy_executor::raw::TaskPool::new(); - unsafe { POOL._spawn_async_fn(move || #task_inner_ident(#(#full_args,)*)) } + static POOL: ::embassy_executor::raw::TaskPool<::Fut, POOL_SIZE> = ::embassy_executor::raw::TaskPool::new(); + unsafe { POOL._spawn_async_fn(move || ThisTask::construct(#(#full_args,)*)) } } }; #[cfg(not(feature = "nightly"))] From 5a6384333f9bbd61565f820097bb89f9fbcce282 Mon Sep 17 00:00:00 2001 From: Grant Miller Date: Wed, 14 Feb 2024 16:14:41 -0600 Subject: [PATCH 04/62] Fix feature flag in executor tests --- embassy-executor/tests/test.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/embassy-executor/tests/test.rs b/embassy-executor/tests/test.rs index 2c2441dd5..348cc7dc4 100644 --- a/embassy-executor/tests/test.rs +++ b/embassy-executor/tests/test.rs @@ -1,4 +1,4 @@ -#![cfg_attr(feature = "nightly", feature(type_alias_impl_trait))] +#![cfg_attr(feature = "nightly", feature(impl_trait_in_assoc_type))] use std::boxed::Box; use std::future::poll_fn; From dd549dad1a93be4a71fd65ebb220e9cab4d1d70d Mon Sep 17 00:00:00 2001 From: Grant Miller Date: Wed, 14 Feb 2024 16:59:43 -0600 Subject: [PATCH 05/62] Improve hygiene --- embassy-executor-macros/src/macros/task.rs | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/embassy-executor-macros/src/macros/task.rs b/embassy-executor-macros/src/macros/task.rs index 82579cbbe..548fa4629 100644 --- a/embassy-executor-macros/src/macros/task.rs +++ b/embassy-executor-macros/src/macros/task.rs @@ -93,13 +93,12 @@ pub fn run(args: &[NestedMeta], f: syn::ItemFn) -> Result ::embassy_executor::SpawnToken { - trait Task { - type Fut: core::future::Future + 'static; + trait _EmbassyInternalTaskTrait { + type Fut: ::core::future::Future + 'static; fn construct(#fargs) -> Self::Fut; } - struct ThisTask; - impl Task for ThisTask { + impl _EmbassyInternalTaskTrait for () { type Fut = impl core::future::Future + 'static; fn construct(#fargs) -> Self::Fut { #task_inner_ident(#(#full_args,)*) @@ -107,7 +106,7 @@ pub fn run(args: &[NestedMeta], f: syn::ItemFn) -> Result::Fut, POOL_SIZE> = ::embassy_executor::raw::TaskPool::new(); + static POOL: ::embassy_executor::raw::TaskPool<<() as _EmbassyInternalTaskTrait>::Fut, POOL_SIZE> = ::embassy_executor::raw::TaskPool::new(); unsafe { POOL._spawn_async_fn(move || ThisTask::construct(#(#full_args,)*)) } } }; From ea89b0c4a0d752c67976aebf2ee264ddc8f77218 Mon Sep 17 00:00:00 2001 From: Grant Miller Date: Fri, 23 Feb 2024 18:45:07 -0600 Subject: [PATCH 06/62] oops --- embassy-executor-macros/src/macros/task.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/embassy-executor-macros/src/macros/task.rs b/embassy-executor-macros/src/macros/task.rs index 548fa4629..96c6267b2 100644 --- a/embassy-executor-macros/src/macros/task.rs +++ b/embassy-executor-macros/src/macros/task.rs @@ -107,7 +107,7 @@ pub fn run(args: &[NestedMeta], f: syn::ItemFn) -> Result::Fut, POOL_SIZE> = ::embassy_executor::raw::TaskPool::new(); - unsafe { POOL._spawn_async_fn(move || ThisTask::construct(#(#full_args,)*)) } + unsafe { POOL._spawn_async_fn(move || <() as _EmbassyInternalTaskTrait>::construct(#(#full_args,)*)) } } }; #[cfg(not(feature = "nightly"))] From 4bbcc2a7fbf1a363719d224dd80dfbcbbee6f26e Mon Sep 17 00:00:00 2001 From: Peter Krull Date: Sun, 3 Mar 2024 15:35:52 +0100 Subject: [PATCH 07/62] Add OnceLock sync primitive --- embassy-sync/src/lib.rs | 1 + embassy-sync/src/once_lock.rs | 237 ++++++++++++++++++++++++++++++++++ 2 files changed, 238 insertions(+) create mode 100644 embassy-sync/src/once_lock.rs diff --git a/embassy-sync/src/lib.rs b/embassy-sync/src/lib.rs index d88c76db5..61b173e80 100644 --- a/embassy-sync/src/lib.rs +++ b/embassy-sync/src/lib.rs @@ -13,6 +13,7 @@ mod ring_buffer; pub mod blocking_mutex; pub mod channel; pub mod mutex; +pub mod once_lock; pub mod pipe; pub mod priority_channel; pub mod pubsub; diff --git a/embassy-sync/src/once_lock.rs b/embassy-sync/src/once_lock.rs new file mode 100644 index 000000000..f83577a6d --- /dev/null +++ b/embassy-sync/src/once_lock.rs @@ -0,0 +1,237 @@ +//! Syncronization primitive for initializing a value once, allowing others to await a reference to the value. + +use core::cell::Cell; +use core::future::poll_fn; +use core::mem::MaybeUninit; +use core::sync::atomic::{AtomicBool, Ordering}; +use core::task::Poll; + +/// The `OnceLock` is a synchronization primitive that allows for +/// initializing a value once, and allowing others to `.await` a +/// reference to the value. This is useful for lazy initialization of +/// a static value. +/// +/// **Note**: this implementation uses a busy loop to poll the value, +/// which is not as efficient as registering a dedicated `Waker`. +/// However, the if the usecase for is to initialize a static variable +/// relatively early in the program life cycle, it should be fine. +/// +/// # Example +/// ``` +/// use futures_executor::block_on; +/// use embassy_sync::once_lock::OnceLock; +/// +/// // Define a static value that will be lazily initialized +/// static VALUE: OnceLock = OnceLock::new(); +/// +/// let f = async { +/// +/// // Initialize the value +/// let reference = VALUE.get_or_init(|| 20); +/// assert_eq!(reference, &20); +/// +/// // Wait for the value to be initialized +/// // and get a static reference it +/// assert_eq!(VALUE.get().await, &20); +/// +/// }; +/// block_on(f) +/// ``` +pub struct OnceLock { + init: AtomicBool, + data: Cell>, +} + +unsafe impl Sync for OnceLock {} + +impl OnceLock { + /// Create a new uninitialized `OnceLock`. + pub const fn new() -> Self { + Self { + init: AtomicBool::new(false), + data: Cell::new(MaybeUninit::zeroed()), + } + } + + /// Get a reference to the underlying value, waiting for it to be set. + /// If the value is already set, this will return immediately. + pub async fn get(&self) -> &T { + + poll_fn(|cx| match self.try_get() { + Some(data) => Poll::Ready(data), + None => { + cx.waker().wake_by_ref(); + Poll::Pending + } + }) + .await + } + + /// Try to get a reference to the underlying value if it exists. + pub fn try_get(&self) -> Option<&T> { + if self.init.load(Ordering::Relaxed) { + Some(unsafe { self.get_ref_unchecked() }) + } else { + None + } + } + + /// Set the underlying value. If the value is already set, this will return an error with the given value. + pub fn init(&self, value: T) -> Result<(), T> { + // Critical section is required to ensure that the value is + // not simultaniously initialized elsewhere at the same time. + critical_section::with(|_| { + // If the value is not set, set it and return Ok. + if !self.init.load(Ordering::Relaxed) { + self.data.set(MaybeUninit::new(value)); + self.init.store(true, Ordering::Relaxed); + Ok(()) + + // Otherwise return an error with the given value. + } else { + Err(value) + } + }) + } + + /// Get a reference to the underlying value, initializing it if it does not exist. + pub fn get_or_init(&self, f: F) -> &T + where + F: FnOnce() -> T, + { + // Critical section is required to ensure that the value is + // not simultaniously initialized elsewhere at the same time. + critical_section::with(|_| { + // If the value is not set, set it. + if !self.init.load(Ordering::Relaxed) { + self.data.set(MaybeUninit::new(f())); + self.init.store(true, Ordering::Relaxed); + } + }); + + // Return a reference to the value. + unsafe { self.get_ref_unchecked() } + } + + /// Consume the `OnceLock`, returning the underlying value if it was initialized. + pub fn into_inner(self) -> Option { + if self.init.load(Ordering::Relaxed) { + Some(unsafe { self.data.into_inner().assume_init() }) + } else { + None + } + } + + /// Take the underlying value if it was initialized, uninitializing the `OnceLock` in the process. + pub fn take(&mut self) -> Option { + // If the value is set, uninitialize the lock and return the value. + critical_section::with(|_| { + if self.init.load(Ordering::Relaxed) { + let val = unsafe { self.data.replace(MaybeUninit::zeroed()).assume_init() }; + self.init.store(false, Ordering::Relaxed); + Some(val) + + // Otherwise return None. + } else { + None + } + }) + } + + /// Check if the value has been set. + pub fn is_set(&self) -> bool { + self.init.load(Ordering::Relaxed) + } + + /// Get a reference to the underlying value. + /// # Safety + /// Must only be used if a value has been set. + unsafe fn get_ref_unchecked(&self) -> &T { + (*self.data.as_ptr()).assume_init_ref() + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn once_lock() { + let lock = OnceLock::new(); + assert_eq!(lock.try_get(), None); + assert_eq!(lock.is_set(), false); + + let v = 42; + assert_eq!(lock.init(v), Ok(())); + assert_eq!(lock.is_set(), true); + assert_eq!(lock.try_get(), Some(&v)); + assert_eq!(lock.try_get(), Some(&v)); + + let v = 43; + assert_eq!(lock.init(v), Err(v)); + assert_eq!(lock.is_set(), true); + assert_eq!(lock.try_get(), Some(&42)); + } + + #[test] + fn once_lock_get_or_init() { + let lock = OnceLock::new(); + assert_eq!(lock.try_get(), None); + assert_eq!(lock.is_set(), false); + + let v = lock.get_or_init(|| 42); + assert_eq!(v, &42); + assert_eq!(lock.is_set(), true); + assert_eq!(lock.try_get(), Some(&42)); + + let v = lock.get_or_init(|| 43); + assert_eq!(v, &42); + assert_eq!(lock.is_set(), true); + assert_eq!(lock.try_get(), Some(&42)); + } + + #[test] + fn once_lock_static() { + static LOCK: OnceLock = OnceLock::new(); + + let v: &'static i32 = LOCK.get_or_init(|| 42); + assert_eq!(v, &42); + + let v: &'static i32 = LOCK.get_or_init(|| 43); + assert_eq!(v, &42); + } + + #[futures_test::test] + async fn once_lock_async() { + static LOCK: OnceLock = OnceLock::new(); + + assert!(LOCK.init(42).is_ok()); + + let v: &'static i32 = LOCK.get().await; + assert_eq!(v, &42); + } + + #[test] + fn once_lock_into_inner() { + let lock: OnceLock = OnceLock::new(); + + let v = lock.get_or_init(|| 42); + assert_eq!(v, &42); + + assert_eq!(lock.into_inner(), Some(42)); + } + + #[test] + fn once_lock_take_init() { + let mut lock: OnceLock = OnceLock::new(); + + assert_eq!(lock.get_or_init(|| 42), &42); + assert_eq!(lock.is_set(), true); + + assert_eq!(lock.take(), Some(42)); + assert_eq!(lock.is_set(), false); + + assert_eq!(lock.get_or_init(|| 43), &43); + assert_eq!(lock.is_set(), true); + } +} From 245e7d3bc20d66fa77f6763ce5e5bec0ea6ddeff Mon Sep 17 00:00:00 2001 From: Peter Krull Date: Sun, 3 Mar 2024 15:43:01 +0100 Subject: [PATCH 08/62] This one is for ci/rustfmt --- embassy-sync/src/once_lock.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/embassy-sync/src/once_lock.rs b/embassy-sync/src/once_lock.rs index f83577a6d..31cc99711 100644 --- a/embassy-sync/src/once_lock.rs +++ b/embassy-sync/src/once_lock.rs @@ -33,7 +33,7 @@ use core::task::Poll; /// // Wait for the value to be initialized /// // and get a static reference it /// assert_eq!(VALUE.get().await, &20); -/// +/// /// }; /// block_on(f) /// ``` @@ -56,7 +56,6 @@ impl OnceLock { /// Get a reference to the underlying value, waiting for it to be set. /// If the value is already set, this will return immediately. pub async fn get(&self) -> &T { - poll_fn(|cx| match self.try_get() { Some(data) => Poll::Ready(data), None => { From 255ed29853eb88bff2ee548c63fb4d0a6dfad7e8 Mon Sep 17 00:00:00 2001 From: Rafael Bachmann Date: Mon, 18 Mar 2024 23:28:58 +0100 Subject: [PATCH 09/62] fix minor clippy lints in embassy_rp --- embassy-rp/src/adc.rs | 11 +++-------- embassy-rp/src/clocks.rs | 4 +--- embassy-rp/src/flash.rs | 4 ++-- embassy-rp/src/gpio.rs | 6 +++--- embassy-rp/src/i2c.rs | 6 +++--- embassy-rp/src/i2c_slave.rs | 16 +++++++--------- embassy-rp/src/lib.rs | 4 ++-- embassy-rp/src/multicore.rs | 2 +- embassy-rp/src/pio/mod.rs | 12 +++++------- embassy-rp/src/pwm.rs | 6 +++--- embassy-rp/src/relocate.rs | 6 +++--- embassy-rp/src/rtc/mod.rs | 3 +-- embassy-rp/src/uart/buffered.rs | 4 ++-- embassy-rp/src/uart/mod.rs | 2 +- 14 files changed, 37 insertions(+), 49 deletions(-) diff --git a/embassy-rp/src/adc.rs b/embassy-rp/src/adc.rs index 21360bf66..4c01fe195 100644 --- a/embassy-rp/src/adc.rs +++ b/embassy-rp/src/adc.rs @@ -19,14 +19,9 @@ static WAKER: AtomicWaker = AtomicWaker::new(); /// ADC config. #[non_exhaustive] +#[derive(Default)] pub struct Config {} -impl Default for Config { - fn default() -> Self { - Self {} - } -} - enum Source<'p> { Pin(PeripheralRef<'p, AnyPin>), TempSensor(PeripheralRef<'p, ADC_TEMP_SENSOR>), @@ -175,7 +170,7 @@ impl<'d, M: Mode> Adc<'d, M> { while !r.cs().read().ready() {} match r.cs().read().err() { true => Err(Error::ConversionFailed), - false => Ok(r.result().read().result().into()), + false => Ok(r.result().read().result()), } } } @@ -221,7 +216,7 @@ impl<'d> Adc<'d, Async> { Self::wait_for_ready().await; match r.cs().read().err() { true => Err(Error::ConversionFailed), - false => Ok(r.result().read().result().into()), + false => Ok(r.result().read().result()), } } diff --git a/embassy-rp/src/clocks.rs b/embassy-rp/src/clocks.rs index 19232b801..b02f3796f 100644 --- a/embassy-rp/src/clocks.rs +++ b/embassy-rp/src/clocks.rs @@ -1,5 +1,4 @@ //! Clock configuration for the RP2040 -use core::arch::asm; use core::marker::PhantomData; use core::sync::atomic::{AtomicU16, AtomicU32, Ordering}; @@ -8,7 +7,6 @@ use pac::clocks::vals::*; use crate::gpio::sealed::Pin; use crate::gpio::AnyPin; -use crate::pac::common::{Reg, RW}; use crate::{pac, reset, Peripheral}; // NOTE: all gpin handling is commented out for future reference. @@ -737,7 +735,7 @@ fn configure_pll(p: pac::pll::Pll, input_freq: u32, config: PllConfig) -> u32 { assert!(config.refdiv >= 1 && config.refdiv <= 63); assert!(ref_freq >= 5_000_000 && ref_freq <= 800_000_000); let vco_freq = ref_freq.saturating_mul(config.fbdiv as u32); - assert!(vco_freq >= 750_000_000 && vco_freq <= 1800_000_000); + assert!(vco_freq >= 750_000_000 && vco_freq <= 1_800_000_000); // Load VCO-related dividers before starting VCO p.cs().write(|w| w.set_refdiv(config.refdiv as _)); diff --git a/embassy-rp/src/flash.rs b/embassy-rp/src/flash.rs index 2d673cf6c..8bac93684 100644 --- a/embassy-rp/src/flash.rs +++ b/embassy-rp/src/flash.rs @@ -326,9 +326,9 @@ impl<'d, T: Instance, const FLASH_SIZE: usize> Flash<'d, T, Async, FLASH_SIZE> { // If the destination address is already aligned, then we can just DMA directly if (bytes.as_ptr() as u32) % 4 == 0 { // Safety: alignment and size have been checked for compatibility - let mut buf: &mut [u32] = + let buf: &mut [u32] = unsafe { core::slice::from_raw_parts_mut(bytes.as_mut_ptr() as *mut u32, bytes.len() / 4) }; - self.background_read(offset, &mut buf)?.await; + self.background_read(offset, buf)?.await; return Ok(()); } diff --git a/embassy-rp/src/gpio.rs b/embassy-rp/src/gpio.rs index 62eeb4cf6..405bddfd8 100644 --- a/embassy-rp/src/gpio.rs +++ b/embassy-rp/src/gpio.rs @@ -225,8 +225,8 @@ fn irq_handler(bank: pac::io::Io, wakers: &[AtomicWaker; N]) { // The status register is divided into groups of four, one group for // each pin. Each group consists of four trigger levels LEVEL_LOW, // LEVEL_HIGH, EDGE_LOW, and EDGE_HIGH for each pin. - let pin_group = (pin % 8) as usize; - let event = (intsx.read().0 >> pin_group * 4) & 0xf as u32; + let pin_group = pin % 8; + let event = (intsx.read().0 >> (pin_group * 4)) & 0xf; // no more than one event can be awaited per pin at any given time, so // we can just clear all interrupt enables for that pin without having @@ -238,7 +238,7 @@ fn irq_handler(bank: pac::io::Io, wakers: &[AtomicWaker; N]) { w.set_level_high(pin_group, true); w.set_level_low(pin_group, true); }); - wakers[pin as usize].wake(); + wakers[pin].wake(); } } } diff --git a/embassy-rp/src/i2c.rs b/embassy-rp/src/i2c.rs index ac0eac96d..26a819b25 100644 --- a/embassy-rp/src/i2c.rs +++ b/embassy-rp/src/i2c.rs @@ -352,7 +352,7 @@ impl interrupt::typelevel::Handler for InterruptHandl } } -pub(crate) fn set_up_i2c_pin<'d, P, T>(pin: &P) +pub(crate) fn set_up_i2c_pin(pin: &P) where P: core::ops::Deref, T: crate::gpio::Pin, @@ -749,7 +749,7 @@ where let addr: u16 = address.into(); - if operations.len() > 0 { + if !operations.is_empty() { Self::setup(addr)?; } let mut iterator = operations.iter_mut(); @@ -762,7 +762,7 @@ where self.read_async_internal(buffer, false, last).await?; } Operation::Write(buffer) => { - self.write_async_internal(buffer.into_iter().cloned(), last).await?; + self.write_async_internal(buffer.iter().cloned(), last).await?; } } } diff --git a/embassy-rp/src/i2c_slave.rs b/embassy-rp/src/i2c_slave.rs index 97ca17295..e2d4fbac0 100644 --- a/embassy-rp/src/i2c_slave.rs +++ b/embassy-rp/src/i2c_slave.rs @@ -289,7 +289,7 @@ impl<'d, T: Instance> I2cSlave<'d, T> { pub async fn respond_to_read(&mut self, buffer: &[u8]) -> Result { let p = T::regs(); - if buffer.len() == 0 { + if buffer.is_empty() { return Err(Error::InvalidResponseBufferLength); } @@ -318,15 +318,13 @@ impl<'d, T: Instance> I2cSlave<'d, T> { } Poll::Pending + } else if stat.rx_done() { + p.ic_clr_rx_done().read(); + Poll::Ready(Ok(ReadStatus::Done)) + } else if stat.rd_req() && stat.tx_empty() { + Poll::Ready(Ok(ReadStatus::NeedMoreBytes)) } else { - if stat.rx_done() { - p.ic_clr_rx_done().read(); - Poll::Ready(Ok(ReadStatus::Done)) - } else if stat.rd_req() && stat.tx_empty() { - Poll::Ready(Ok(ReadStatus::NeedMoreBytes)) - } else { - Poll::Pending - } + Poll::Pending } }, |_me| { diff --git a/embassy-rp/src/lib.rs b/embassy-rp/src/lib.rs index 46973fdc8..7092b3fab 100644 --- a/embassy-rp/src/lib.rs +++ b/embassy-rp/src/lib.rs @@ -238,8 +238,8 @@ select_bootloader! { } /// Installs a stack guard for the CORE0 stack in MPU region 0. -/// Will fail if the MPU is already confgigured. This function requires -/// a `_stack_end` symbol to be defined by the linker script, and expexcts +/// Will fail if the MPU is already configured. This function requires +/// a `_stack_end` symbol to be defined by the linker script, and expects /// `_stack_end` to be located at the lowest address (largest depth) of /// the stack. /// diff --git a/embassy-rp/src/multicore.rs b/embassy-rp/src/multicore.rs index 252f30dc1..d9d65694a 100644 --- a/embassy-rp/src/multicore.rs +++ b/embassy-rp/src/multicore.rs @@ -59,7 +59,7 @@ static IS_CORE1_INIT: AtomicBool = AtomicBool::new(false); #[inline(always)] fn core1_setup(stack_bottom: *mut usize) { - if let Err(_) = install_stack_guard(stack_bottom) { + if install_stack_guard(stack_bottom).is_err() { // currently only happens if the MPU was already set up, which // would indicate that the core is already in use from outside // embassy, somehow. trap if so since we can't deal with that. diff --git a/embassy-rp/src/pio/mod.rs b/embassy-rp/src/pio/mod.rs index ca9795024..804a7636d 100644 --- a/embassy-rp/src/pio/mod.rs +++ b/embassy-rp/src/pio/mod.rs @@ -268,7 +268,7 @@ impl<'l, PIO: Instance> Pin<'l, PIO> { } /// Set the pin's input sync bypass. - pub fn set_input_sync_bypass<'a>(&mut self, bypass: bool) { + pub fn set_input_sync_bypass(&mut self, bypass: bool) { let mask = 1 << self.pin(); if bypass { PIO::PIO.input_sync_bypass().write_set(|w| *w = mask); @@ -463,7 +463,7 @@ impl<'d, PIO: Instance, const SM: usize> Drop for StateMachine<'d, PIO, SM> { } } -fn assert_consecutive<'d, PIO: Instance>(pins: &[&Pin<'d, PIO>]) { +fn assert_consecutive(pins: &[&Pin]) { for (p1, p2) in pins.iter().zip(pins.iter().skip(1)) { // purposely does not allow wrap-around because we can't claim pins 30 and 31. assert!(p1.pin() + 1 == p2.pin(), "pins must be consecutive"); @@ -749,7 +749,7 @@ impl<'d, PIO: Instance + 'd, const SM: usize> StateMachine<'d, PIO, SM> { w.set_set_count(1); }); // SET PINDIRS, (dir) - unsafe { sm.exec_instr(0b111_00000_100_00000 | dir as u16) }; + unsafe { sm.exec_instr(0b1110_0000_1000_0000 | dir as u16) }; } }); } @@ -764,7 +764,7 @@ impl<'d, PIO: Instance + 'd, const SM: usize> StateMachine<'d, PIO, SM> { w.set_set_count(1); }); // SET PINS, (dir) - unsafe { sm.exec_instr(0b111_00000_000_00000 | level as u16) }; + unsafe { sm.exec_instr(0b1110_0000_0000_0000 | level as u16) }; } }); } @@ -867,9 +867,7 @@ impl<'d, PIO: Instance> Common<'d, PIO> { prog: &Program, ) -> Result, LoadError> { match prog.origin { - Some(origin) => self - .try_load_program_at(prog, origin) - .map_err(|a| LoadError::AddressInUse(a)), + Some(origin) => self.try_load_program_at(prog, origin).map_err(LoadError::AddressInUse), None => { // naively search for free space, allowing wraparound since // PIO does support that. with only 32 instruction slots it diff --git a/embassy-rp/src/pwm.rs b/embassy-rp/src/pwm.rs index 784a05f92..e6f3b2aa2 100644 --- a/embassy-rp/src/pwm.rs +++ b/embassy-rp/src/pwm.rs @@ -114,8 +114,8 @@ impl<'d, T: Channel> Pwm<'d, T> { } Self { inner, - pin_a: a.into(), - pin_b: b.into(), + pin_a: a, + pin_b: b, } } @@ -190,7 +190,7 @@ impl<'d, T: Channel> Pwm<'d, T> { } fn configure(p: pac::pwm::Channel, config: &Config) { - if config.divider > FixedU16::::from_bits(0xFF_F) { + if config.divider > FixedU16::::from_bits(0xFFF) { panic!("Requested divider is too large"); } diff --git a/embassy-rp/src/relocate.rs b/embassy-rp/src/relocate.rs index b35b4ed72..40cb2667b 100644 --- a/embassy-rp/src/relocate.rs +++ b/embassy-rp/src/relocate.rs @@ -22,15 +22,15 @@ where { type Item = u16; fn next(&mut self) -> Option { - self.iter.next().and_then(|&instr| { - Some(if instr & 0b1110_0000_0000_0000 == 0 { + self.iter.next().map(|&instr| { + if instr & 0b1110_0000_0000_0000 == 0 { // this is a JMP instruction -> add offset to address let address = (instr & 0b1_1111) as u8; let address = address.wrapping_add(self.offset) % 32; instr & (!0b11111) | address as u16 } else { instr - }) + } }) } } diff --git a/embassy-rp/src/rtc/mod.rs b/embassy-rp/src/rtc/mod.rs index b696989f5..c8691bdc2 100644 --- a/embassy-rp/src/rtc/mod.rs +++ b/embassy-rp/src/rtc/mod.rs @@ -29,8 +29,7 @@ impl<'d, T: Instance> Rtc<'d, T> { // Set the RTC divider inner.regs().clkdiv_m1().write(|w| w.set_clkdiv_m1(clk_rtc_freq() - 1)); - let result = Self { inner }; - result + Self { inner } } /// Enable or disable the leap year check. The rp2040 chip will always add a Feb 29th on every year that is divisable by 4, but this may be incorrect (e.g. on century years). This function allows you to disable this check. diff --git a/embassy-rp/src/uart/buffered.rs b/embassy-rp/src/uart/buffered.rs index 99c958129..7622539f1 100644 --- a/embassy-rp/src/uart/buffered.rs +++ b/embassy-rp/src/uart/buffered.rs @@ -467,7 +467,7 @@ impl<'d, T: Instance> Drop for BufferedUartRx<'d, T> { // TX is inactive if the the buffer is not available. // We can now unregister the interrupt handler - if state.tx_buf.len() == 0 { + if state.tx_buf.is_empty() { T::Interrupt::disable(); } } @@ -480,7 +480,7 @@ impl<'d, T: Instance> Drop for BufferedUartTx<'d, T> { // RX is inactive if the the buffer is not available. // We can now unregister the interrupt handler - if state.rx_buf.len() == 0 { + if state.rx_buf.is_empty() { T::Interrupt::disable(); } } diff --git a/embassy-rp/src/uart/mod.rs b/embassy-rp/src/uart/mod.rs index f372cb640..65dcf4eb4 100644 --- a/embassy-rp/src/uart/mod.rs +++ b/embassy-rp/src/uart/mod.rs @@ -322,7 +322,7 @@ impl<'d, T: Instance, M: Mode> UartRx<'d, T, M> { impl<'d, T: Instance, M: Mode> Drop for UartRx<'d, T, M> { fn drop(&mut self) { - if let Some(_) = self.rx_dma { + if self.rx_dma.is_some() { T::Interrupt::disable(); // clear dma flags. irq handlers use these to disambiguate among themselves. T::regs().uartdmacr().write_clear(|reg| { From 47ebec82b86a9dc9aa029ee7404b270cb0e70344 Mon Sep 17 00:00:00 2001 From: Alejandro Nadal <32724827+AlejandroFNadal@users.noreply.github.com> Date: Wed, 20 Mar 2024 13:56:15 +0100 Subject: [PATCH 10/62] Add comment warning for new users about changing pins on Ethernet for different devices Not all STM32H7 devices share the pins of the example. Added a warning and a specific example for STM32H747XIH --- examples/stm32h7/src/bin/eth.rs | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/examples/stm32h7/src/bin/eth.rs b/examples/stm32h7/src/bin/eth.rs index cd9a27fcd..825c16c6c 100644 --- a/examples/stm32h7/src/bin/eth.rs +++ b/examples/stm32h7/src/bin/eth.rs @@ -64,19 +64,21 @@ async fn main(spawner: Spawner) -> ! { let mac_addr = [0x00, 0x00, 0xDE, 0xAD, 0xBE, 0xEF]; static PACKETS: StaticCell> = StaticCell::new(); + // warning: Not all STM32H7 devices have the exact same pins here + // for STM32H747XIH, replace p.PB13 for PG12 let device = Ethernet::new( PACKETS.init(PacketQueue::<4, 4>::new()), p.ETH, Irqs, - p.PA1, - p.PA2, - p.PC1, - p.PA7, - p.PC4, - p.PC5, - p.PG13, - p.PB13, - p.PG11, + p.PA1, // ref_clk + p.PA2, // mdio + p.PC1, // eth_mdc + p.PA7, // CRS_DV: Carrier Sense + p.PC4, // RX_D0: Received Bit 0 + p.PC5, // RX_D1: Received Bit 1 + p.PG13,// TX_D0: Transmit Bit 0 + p.PB13,// TX_D1: Transmit Bit 1 + p.PG11,// TX_EN: Transmit Enable GenericSMI::new(0), mac_addr, ); From 2587ade63e30fc8aa222b4190515ab2e3fe51637 Mon Sep 17 00:00:00 2001 From: AlejandroFNadal Date: Wed, 20 Mar 2024 14:11:04 +0100 Subject: [PATCH 11/62] Rust formatting for comments. --- examples/stm32h7/src/bin/eth.rs | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/examples/stm32h7/src/bin/eth.rs b/examples/stm32h7/src/bin/eth.rs index 825c16c6c..7c7964ecd 100644 --- a/examples/stm32h7/src/bin/eth.rs +++ b/examples/stm32h7/src/bin/eth.rs @@ -70,15 +70,15 @@ async fn main(spawner: Spawner) -> ! { PACKETS.init(PacketQueue::<4, 4>::new()), p.ETH, Irqs, - p.PA1, // ref_clk - p.PA2, // mdio - p.PC1, // eth_mdc - p.PA7, // CRS_DV: Carrier Sense - p.PC4, // RX_D0: Received Bit 0 - p.PC5, // RX_D1: Received Bit 1 - p.PG13,// TX_D0: Transmit Bit 0 - p.PB13,// TX_D1: Transmit Bit 1 - p.PG11,// TX_EN: Transmit Enable + p.PA1, // ref_clk + p.PA2, // mdio + p.PC1, // eth_mdc + p.PA7, // CRS_DV: Carrier Sense + p.PC4, // RX_D0: Received Bit 0 + p.PC5, // RX_D1: Received Bit 1 + p.PG13, // TX_D0: Transmit Bit 0 + p.PB13, // TX_D1: Transmit Bit 1 + p.PG11, // TX_EN: Transmit Enable GenericSMI::new(0), mac_addr, ); From 3d842dac85a4ea21519f56d4ec6342b528805b8a Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Wed, 20 Mar 2024 14:53:19 +0100 Subject: [PATCH 12/62] fmt: disable "unused" warnings. --- cyw43/src/fmt.rs | 3 +-- embassy-boot-nrf/src/fmt.rs | 3 +-- embassy-boot-rp/src/fmt.rs | 3 +-- embassy-boot-stm32/src/fmt.rs | 3 +-- embassy-boot/src/fmt.rs | 3 +-- embassy-executor/src/fmt.rs | 3 +-- embassy-futures/src/fmt.rs | 3 +-- embassy-hal-internal/src/fmt.rs | 3 +-- embassy-net-adin1110/src/fmt.rs | 29 +++++++++++++++------------ embassy-net-driver-channel/src/fmt.rs | 3 +-- embassy-net-enc28j60/src/fmt.rs | 3 +-- embassy-net-esp-hosted/src/fmt.rs | 3 +-- embassy-net-ppp/src/fmt.rs | 3 +-- embassy-net/src/fmt.rs | 3 +-- embassy-nrf/src/fmt.rs | 3 +-- embassy-rp/src/fmt.rs | 3 +-- embassy-stm32-wpan/src/fmt.rs | 3 +-- embassy-stm32/src/fmt.rs | 3 +-- embassy-sync/src/fmt.rs | 3 +-- embassy-time/src/fmt.rs | 3 +-- embassy-usb-dfu/src/fmt.rs | 3 +-- embassy-usb/src/fmt.rs | 3 +-- 22 files changed, 37 insertions(+), 55 deletions(-) diff --git a/cyw43/src/fmt.rs b/cyw43/src/fmt.rs index 78e583c1c..2ac42c557 100644 --- a/cyw43/src/fmt.rs +++ b/cyw43/src/fmt.rs @@ -1,5 +1,5 @@ #![macro_use] -#![allow(unused_macros)] +#![allow(unused)] use core::fmt::{Debug, Display, LowerHex}; @@ -229,7 +229,6 @@ impl Try for Result { } } -#[allow(unused)] pub(crate) struct Bytes<'a>(pub &'a [u8]); impl<'a> Debug for Bytes<'a> { diff --git a/embassy-boot-nrf/src/fmt.rs b/embassy-boot-nrf/src/fmt.rs index 78e583c1c..2ac42c557 100644 --- a/embassy-boot-nrf/src/fmt.rs +++ b/embassy-boot-nrf/src/fmt.rs @@ -1,5 +1,5 @@ #![macro_use] -#![allow(unused_macros)] +#![allow(unused)] use core::fmt::{Debug, Display, LowerHex}; @@ -229,7 +229,6 @@ impl Try for Result { } } -#[allow(unused)] pub(crate) struct Bytes<'a>(pub &'a [u8]); impl<'a> Debug for Bytes<'a> { diff --git a/embassy-boot-rp/src/fmt.rs b/embassy-boot-rp/src/fmt.rs index 78e583c1c..2ac42c557 100644 --- a/embassy-boot-rp/src/fmt.rs +++ b/embassy-boot-rp/src/fmt.rs @@ -1,5 +1,5 @@ #![macro_use] -#![allow(unused_macros)] +#![allow(unused)] use core::fmt::{Debug, Display, LowerHex}; @@ -229,7 +229,6 @@ impl Try for Result { } } -#[allow(unused)] pub(crate) struct Bytes<'a>(pub &'a [u8]); impl<'a> Debug for Bytes<'a> { diff --git a/embassy-boot-stm32/src/fmt.rs b/embassy-boot-stm32/src/fmt.rs index 78e583c1c..2ac42c557 100644 --- a/embassy-boot-stm32/src/fmt.rs +++ b/embassy-boot-stm32/src/fmt.rs @@ -1,5 +1,5 @@ #![macro_use] -#![allow(unused_macros)] +#![allow(unused)] use core::fmt::{Debug, Display, LowerHex}; @@ -229,7 +229,6 @@ impl Try for Result { } } -#[allow(unused)] pub(crate) struct Bytes<'a>(pub &'a [u8]); impl<'a> Debug for Bytes<'a> { diff --git a/embassy-boot/src/fmt.rs b/embassy-boot/src/fmt.rs index 78e583c1c..2ac42c557 100644 --- a/embassy-boot/src/fmt.rs +++ b/embassy-boot/src/fmt.rs @@ -1,5 +1,5 @@ #![macro_use] -#![allow(unused_macros)] +#![allow(unused)] use core::fmt::{Debug, Display, LowerHex}; @@ -229,7 +229,6 @@ impl Try for Result { } } -#[allow(unused)] pub(crate) struct Bytes<'a>(pub &'a [u8]); impl<'a> Debug for Bytes<'a> { diff --git a/embassy-executor/src/fmt.rs b/embassy-executor/src/fmt.rs index 78e583c1c..2ac42c557 100644 --- a/embassy-executor/src/fmt.rs +++ b/embassy-executor/src/fmt.rs @@ -1,5 +1,5 @@ #![macro_use] -#![allow(unused_macros)] +#![allow(unused)] use core::fmt::{Debug, Display, LowerHex}; @@ -229,7 +229,6 @@ impl Try for Result { } } -#[allow(unused)] pub(crate) struct Bytes<'a>(pub &'a [u8]); impl<'a> Debug for Bytes<'a> { diff --git a/embassy-futures/src/fmt.rs b/embassy-futures/src/fmt.rs index 78e583c1c..2ac42c557 100644 --- a/embassy-futures/src/fmt.rs +++ b/embassy-futures/src/fmt.rs @@ -1,5 +1,5 @@ #![macro_use] -#![allow(unused_macros)] +#![allow(unused)] use core::fmt::{Debug, Display, LowerHex}; @@ -229,7 +229,6 @@ impl Try for Result { } } -#[allow(unused)] pub(crate) struct Bytes<'a>(pub &'a [u8]); impl<'a> Debug for Bytes<'a> { diff --git a/embassy-hal-internal/src/fmt.rs b/embassy-hal-internal/src/fmt.rs index 78e583c1c..2ac42c557 100644 --- a/embassy-hal-internal/src/fmt.rs +++ b/embassy-hal-internal/src/fmt.rs @@ -1,5 +1,5 @@ #![macro_use] -#![allow(unused_macros)] +#![allow(unused)] use core::fmt::{Debug, Display, LowerHex}; @@ -229,7 +229,6 @@ impl Try for Result { } } -#[allow(unused)] pub(crate) struct Bytes<'a>(pub &'a [u8]); impl<'a> Debug for Bytes<'a> { diff --git a/embassy-net-adin1110/src/fmt.rs b/embassy-net-adin1110/src/fmt.rs index 12737c690..2ac42c557 100644 --- a/embassy-net-adin1110/src/fmt.rs +++ b/embassy-net-adin1110/src/fmt.rs @@ -1,5 +1,5 @@ #![macro_use] -#![allow(unused_macros)] +#![allow(unused)] use core::fmt::{Debug, Display, LowerHex}; @@ -83,14 +83,17 @@ macro_rules! todo { }; } +#[cfg(not(feature = "defmt"))] macro_rules! unreachable { ($($x:tt)*) => { - { - #[cfg(not(feature = "defmt"))] - ::core::unreachable!($($x)*); - #[cfg(feature = "defmt")] - ::defmt::unreachable!($($x)*); - } + ::core::unreachable!($($x)*) + }; +} + +#[cfg(feature = "defmt")] +macro_rules! unreachable { + ($($x:tt)*) => { + ::defmt::unreachable!($($x)*) }; } @@ -113,7 +116,7 @@ macro_rules! trace { #[cfg(feature = "defmt")] ::defmt::trace!($s $(, $x)*); #[cfg(not(any(feature = "log", feature="defmt")))] - let _ignored = ($( & $x ),*); + let _ = ($( & $x ),*); } }; } @@ -126,7 +129,7 @@ macro_rules! debug { #[cfg(feature = "defmt")] ::defmt::debug!($s $(, $x)*); #[cfg(not(any(feature = "log", feature="defmt")))] - let _ignored = ($( & $x ),*); + let _ = ($( & $x ),*); } }; } @@ -139,7 +142,7 @@ macro_rules! info { #[cfg(feature = "defmt")] ::defmt::info!($s $(, $x)*); #[cfg(not(any(feature = "log", feature="defmt")))] - let _ignored = ($( & $x ),*); + let _ = ($( & $x ),*); } }; } @@ -152,7 +155,7 @@ macro_rules! warn { #[cfg(feature = "defmt")] ::defmt::warn!($s $(, $x)*); #[cfg(not(any(feature = "log", feature="defmt")))] - let _ignored = ($( & $x ),*); + let _ = ($( & $x ),*); } }; } @@ -165,7 +168,7 @@ macro_rules! error { #[cfg(feature = "defmt")] ::defmt::error!($s $(, $x)*); #[cfg(not(any(feature = "log", feature="defmt")))] - let _ignored = ($( & $x ),*); + let _ = ($( & $x ),*); } }; } @@ -226,7 +229,7 @@ impl Try for Result { } } -pub struct Bytes<'a>(pub &'a [u8]); +pub(crate) struct Bytes<'a>(pub &'a [u8]); impl<'a> Debug for Bytes<'a> { fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { diff --git a/embassy-net-driver-channel/src/fmt.rs b/embassy-net-driver-channel/src/fmt.rs index 78e583c1c..2ac42c557 100644 --- a/embassy-net-driver-channel/src/fmt.rs +++ b/embassy-net-driver-channel/src/fmt.rs @@ -1,5 +1,5 @@ #![macro_use] -#![allow(unused_macros)] +#![allow(unused)] use core::fmt::{Debug, Display, LowerHex}; @@ -229,7 +229,6 @@ impl Try for Result { } } -#[allow(unused)] pub(crate) struct Bytes<'a>(pub &'a [u8]); impl<'a> Debug for Bytes<'a> { diff --git a/embassy-net-enc28j60/src/fmt.rs b/embassy-net-enc28j60/src/fmt.rs index 78e583c1c..2ac42c557 100644 --- a/embassy-net-enc28j60/src/fmt.rs +++ b/embassy-net-enc28j60/src/fmt.rs @@ -1,5 +1,5 @@ #![macro_use] -#![allow(unused_macros)] +#![allow(unused)] use core::fmt::{Debug, Display, LowerHex}; @@ -229,7 +229,6 @@ impl Try for Result { } } -#[allow(unused)] pub(crate) struct Bytes<'a>(pub &'a [u8]); impl<'a> Debug for Bytes<'a> { diff --git a/embassy-net-esp-hosted/src/fmt.rs b/embassy-net-esp-hosted/src/fmt.rs index 78e583c1c..2ac42c557 100644 --- a/embassy-net-esp-hosted/src/fmt.rs +++ b/embassy-net-esp-hosted/src/fmt.rs @@ -1,5 +1,5 @@ #![macro_use] -#![allow(unused_macros)] +#![allow(unused)] use core::fmt::{Debug, Display, LowerHex}; @@ -229,7 +229,6 @@ impl Try for Result { } } -#[allow(unused)] pub(crate) struct Bytes<'a>(pub &'a [u8]); impl<'a> Debug for Bytes<'a> { diff --git a/embassy-net-ppp/src/fmt.rs b/embassy-net-ppp/src/fmt.rs index 78e583c1c..2ac42c557 100644 --- a/embassy-net-ppp/src/fmt.rs +++ b/embassy-net-ppp/src/fmt.rs @@ -1,5 +1,5 @@ #![macro_use] -#![allow(unused_macros)] +#![allow(unused)] use core::fmt::{Debug, Display, LowerHex}; @@ -229,7 +229,6 @@ impl Try for Result { } } -#[allow(unused)] pub(crate) struct Bytes<'a>(pub &'a [u8]); impl<'a> Debug for Bytes<'a> { diff --git a/embassy-net/src/fmt.rs b/embassy-net/src/fmt.rs index 78e583c1c..2ac42c557 100644 --- a/embassy-net/src/fmt.rs +++ b/embassy-net/src/fmt.rs @@ -1,5 +1,5 @@ #![macro_use] -#![allow(unused_macros)] +#![allow(unused)] use core::fmt::{Debug, Display, LowerHex}; @@ -229,7 +229,6 @@ impl Try for Result { } } -#[allow(unused)] pub(crate) struct Bytes<'a>(pub &'a [u8]); impl<'a> Debug for Bytes<'a> { diff --git a/embassy-nrf/src/fmt.rs b/embassy-nrf/src/fmt.rs index 78e583c1c..2ac42c557 100644 --- a/embassy-nrf/src/fmt.rs +++ b/embassy-nrf/src/fmt.rs @@ -1,5 +1,5 @@ #![macro_use] -#![allow(unused_macros)] +#![allow(unused)] use core::fmt::{Debug, Display, LowerHex}; @@ -229,7 +229,6 @@ impl Try for Result { } } -#[allow(unused)] pub(crate) struct Bytes<'a>(pub &'a [u8]); impl<'a> Debug for Bytes<'a> { diff --git a/embassy-rp/src/fmt.rs b/embassy-rp/src/fmt.rs index 78e583c1c..2ac42c557 100644 --- a/embassy-rp/src/fmt.rs +++ b/embassy-rp/src/fmt.rs @@ -1,5 +1,5 @@ #![macro_use] -#![allow(unused_macros)] +#![allow(unused)] use core::fmt::{Debug, Display, LowerHex}; @@ -229,7 +229,6 @@ impl Try for Result { } } -#[allow(unused)] pub(crate) struct Bytes<'a>(pub &'a [u8]); impl<'a> Debug for Bytes<'a> { diff --git a/embassy-stm32-wpan/src/fmt.rs b/embassy-stm32-wpan/src/fmt.rs index 78e583c1c..2ac42c557 100644 --- a/embassy-stm32-wpan/src/fmt.rs +++ b/embassy-stm32-wpan/src/fmt.rs @@ -1,5 +1,5 @@ #![macro_use] -#![allow(unused_macros)] +#![allow(unused)] use core::fmt::{Debug, Display, LowerHex}; @@ -229,7 +229,6 @@ impl Try for Result { } } -#[allow(unused)] pub(crate) struct Bytes<'a>(pub &'a [u8]); impl<'a> Debug for Bytes<'a> { diff --git a/embassy-stm32/src/fmt.rs b/embassy-stm32/src/fmt.rs index 78e583c1c..2ac42c557 100644 --- a/embassy-stm32/src/fmt.rs +++ b/embassy-stm32/src/fmt.rs @@ -1,5 +1,5 @@ #![macro_use] -#![allow(unused_macros)] +#![allow(unused)] use core::fmt::{Debug, Display, LowerHex}; @@ -229,7 +229,6 @@ impl Try for Result { } } -#[allow(unused)] pub(crate) struct Bytes<'a>(pub &'a [u8]); impl<'a> Debug for Bytes<'a> { diff --git a/embassy-sync/src/fmt.rs b/embassy-sync/src/fmt.rs index 78e583c1c..2ac42c557 100644 --- a/embassy-sync/src/fmt.rs +++ b/embassy-sync/src/fmt.rs @@ -1,5 +1,5 @@ #![macro_use] -#![allow(unused_macros)] +#![allow(unused)] use core::fmt::{Debug, Display, LowerHex}; @@ -229,7 +229,6 @@ impl Try for Result { } } -#[allow(unused)] pub(crate) struct Bytes<'a>(pub &'a [u8]); impl<'a> Debug for Bytes<'a> { diff --git a/embassy-time/src/fmt.rs b/embassy-time/src/fmt.rs index 78e583c1c..2ac42c557 100644 --- a/embassy-time/src/fmt.rs +++ b/embassy-time/src/fmt.rs @@ -1,5 +1,5 @@ #![macro_use] -#![allow(unused_macros)] +#![allow(unused)] use core::fmt::{Debug, Display, LowerHex}; @@ -229,7 +229,6 @@ impl Try for Result { } } -#[allow(unused)] pub(crate) struct Bytes<'a>(pub &'a [u8]); impl<'a> Debug for Bytes<'a> { diff --git a/embassy-usb-dfu/src/fmt.rs b/embassy-usb-dfu/src/fmt.rs index 78e583c1c..2ac42c557 100644 --- a/embassy-usb-dfu/src/fmt.rs +++ b/embassy-usb-dfu/src/fmt.rs @@ -1,5 +1,5 @@ #![macro_use] -#![allow(unused_macros)] +#![allow(unused)] use core::fmt::{Debug, Display, LowerHex}; @@ -229,7 +229,6 @@ impl Try for Result { } } -#[allow(unused)] pub(crate) struct Bytes<'a>(pub &'a [u8]); impl<'a> Debug for Bytes<'a> { diff --git a/embassy-usb/src/fmt.rs b/embassy-usb/src/fmt.rs index 78e583c1c..2ac42c557 100644 --- a/embassy-usb/src/fmt.rs +++ b/embassy-usb/src/fmt.rs @@ -1,5 +1,5 @@ #![macro_use] -#![allow(unused_macros)] +#![allow(unused)] use core::fmt::{Debug, Display, LowerHex}; @@ -229,7 +229,6 @@ impl Try for Result { } } -#[allow(unused)] pub(crate) struct Bytes<'a>(pub &'a [u8]); impl<'a> Debug for Bytes<'a> { From eca9aac194580956c851e42565546e5fc50d8070 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Wed, 20 Mar 2024 14:54:25 +0100 Subject: [PATCH 13/62] Fix warnings in recent nightly. --- embassy-executor-macros/src/util/ctxt.rs | 1 - embassy-executor/src/raw/mod.rs | 2 +- embassy-net-enc28j60/src/lib.rs | 7 +++---- embassy-net-tuntap/src/lib.rs | 2 +- embassy-nrf/src/gpio.rs | 2 ++ embassy-nrf/src/gpiote.rs | 2 ++ embassy-nrf/src/timer.rs | 2 -- embassy-rp/src/dma.rs | 2 +- embassy-rp/src/flash.rs | 2 -- embassy-rp/src/float/mod.rs | 1 + embassy-rp/src/gpio.rs | 2 -- embassy-rp/src/lib.rs | 3 ++- embassy-rp/src/relocate.rs | 2 -- embassy-rp/src/uart/buffered.rs | 10 ++-------- embassy-rp/src/usb.rs | 11 ---------- embassy-stm32-wpan/src/consts.rs | 2 -- embassy-stm32/src/can/bx/mod.rs | 2 +- embassy-stm32/src/can/bxcan.rs | 1 - embassy-stm32/src/can/fd/message_ram/mod.rs | 20 ------------------- embassy-stm32/src/can/fdcan.rs | 4 ++-- embassy-stm32/src/dma/mod.rs | 1 + embassy-stm32/src/flash/f0.rs | 1 - embassy-stm32/src/flash/f1f3.rs | 1 - embassy-stm32/src/flash/f4.rs | 1 - embassy-stm32/src/flash/f7.rs | 1 - embassy-stm32/src/flash/g.rs | 1 - embassy-stm32/src/flash/h7.rs | 1 - embassy-stm32/src/flash/u5.rs | 1 - embassy-stm32/src/i2c/v1.rs | 1 - embassy-stm32/src/i2c/v2.rs | 1 - embassy-stm32/src/rtc/datetime.rs | 5 +---- embassy-stm32/src/sdmmc/mod.rs | 2 -- embassy-stm32/src/time_driver.rs | 1 - embassy-stm32/src/timer/complementary_pwm.rs | 1 - embassy-stm32/src/timer/simple_pwm.rs | 1 - embassy-stm32/src/uid.rs | 2 +- embassy-stm32/src/usart/buffered.rs | 5 +---- embassy-stm32/src/usb/otg.rs | 4 ++-- embassy-stm32/src/usb/usb.rs | 11 ---------- examples/rp/src/bin/multicore.rs | 12 +++++++---- examples/std/src/bin/net.rs | 2 -- examples/std/src/bin/net_dns.rs | 2 -- examples/std/src/bin/tcp_accept.rs | 1 - examples/stm32f2/src/bin/pll.rs | 2 -- examples/stm32h7/src/bin/camera.rs | 4 ++-- .../src/bin/spe_adin1110_http_server.rs | 2 +- tests/rp/src/bin/gpio_multicore.rs | 12 +++++++---- tests/rp/src/bin/i2c.rs | 12 +++++++---- tests/rp/src/bin/multicore.rs | 12 +++++++---- 49 files changed, 59 insertions(+), 124 deletions(-) diff --git a/embassy-executor-macros/src/util/ctxt.rs b/embassy-executor-macros/src/util/ctxt.rs index 74c872c3c..9c78cda01 100644 --- a/embassy-executor-macros/src/util/ctxt.rs +++ b/embassy-executor-macros/src/util/ctxt.rs @@ -7,7 +7,6 @@ use std::thread; use proc_macro2::TokenStream; use quote::{quote, ToTokens}; -use syn; /// A type to collect errors together and format them. /// diff --git a/embassy-executor/src/raw/mod.rs b/embassy-executor/src/raw/mod.rs index 3d5e3ab9f..d9ea5c005 100644 --- a/embassy-executor/src/raw/mod.rs +++ b/embassy-executor/src/raw/mod.rs @@ -30,7 +30,7 @@ use core::ptr::NonNull; use core::task::{Context, Poll}; #[cfg(feature = "integrated-timers")] -use embassy_time_driver::{self, AlarmHandle}; +use embassy_time_driver::AlarmHandle; #[cfg(feature = "rtos-trace")] use rtos_trace::trace; diff --git a/embassy-net-enc28j60/src/lib.rs b/embassy-net-enc28j60/src/lib.rs index f18134927..dda35f498 100644 --- a/embassy-net-enc28j60/src/lib.rs +++ b/embassy-net-enc28j60/src/lib.rs @@ -17,7 +17,6 @@ mod phy; mod traits; use core::cmp; -use core::convert::TryInto; use embassy_net_driver::{Capabilities, HardwareAddress, LinkState}; use embassy_time::Duration; @@ -645,8 +644,8 @@ where Self: 'a; fn receive(&mut self, cx: &mut core::task::Context) -> Option<(Self::RxToken<'_>, Self::TxToken<'_>)> { - let rx_buf = unsafe { &mut RX_BUF }; - let tx_buf = unsafe { &mut TX_BUF }; + let rx_buf = unsafe { &mut *core::ptr::addr_of_mut!(RX_BUF) }; + let tx_buf = unsafe { &mut *core::ptr::addr_of_mut!(TX_BUF) }; if let Some(n) = self.receive(rx_buf) { Some((RxToken { buf: &mut rx_buf[..n] }, TxToken { buf: tx_buf, eth: self })) } else { @@ -656,7 +655,7 @@ where } fn transmit(&mut self, _cx: &mut core::task::Context) -> Option> { - let tx_buf = unsafe { &mut TX_BUF }; + let tx_buf = unsafe { &mut *core::ptr::addr_of_mut!(TX_BUF) }; Some(TxToken { buf: tx_buf, eth: self }) } diff --git a/embassy-net-tuntap/src/lib.rs b/embassy-net-tuntap/src/lib.rs index de30934eb..56f55fba1 100644 --- a/embassy-net-tuntap/src/lib.rs +++ b/embassy-net-tuntap/src/lib.rs @@ -6,7 +6,7 @@ use std::os::unix::io::{AsRawFd, RawFd}; use std::task::Context; use async_io::Async; -use embassy_net_driver::{self, Capabilities, Driver, HardwareAddress, LinkState}; +use embassy_net_driver::{Capabilities, Driver, HardwareAddress, LinkState}; use log::*; /// Get the MTU of the given interface. diff --git a/embassy-nrf/src/gpio.rs b/embassy-nrf/src/gpio.rs index 3649ea61a..f2353f21d 100644 --- a/embassy-nrf/src/gpio.rs +++ b/embassy-nrf/src/gpio.rs @@ -473,10 +473,12 @@ impl sealed::Pin for AnyPin { // ==================== +#[cfg(not(feature = "_nrf51"))] pub(crate) trait PselBits { fn psel_bits(&self) -> u32; } +#[cfg(not(feature = "_nrf51"))] impl<'a, P: Pin> PselBits for Option> { #[inline] fn psel_bits(&self) -> u32 { diff --git a/embassy-nrf/src/gpiote.rs b/embassy-nrf/src/gpiote.rs index 12f4ed0a0..4a28279a9 100644 --- a/embassy-nrf/src/gpiote.rs +++ b/embassy-nrf/src/gpiote.rs @@ -167,8 +167,10 @@ unsafe fn handle_gpiote_interrupt() { } } +#[cfg(not(feature = "_nrf51"))] struct BitIter(u32); +#[cfg(not(feature = "_nrf51"))] impl Iterator for BitIter { type Item = u32; diff --git a/embassy-nrf/src/timer.rs b/embassy-nrf/src/timer.rs index 3c35baee5..2970ad3f2 100644 --- a/embassy-nrf/src/timer.rs +++ b/embassy-nrf/src/timer.rs @@ -21,8 +21,6 @@ pub(crate) mod sealed { fn regs() -> &'static pac::timer0::RegisterBlock; } pub trait ExtendedInstance {} - - pub trait TimerType {} } /// Basic Timer instance. diff --git a/embassy-rp/src/dma.rs b/embassy-rp/src/dma.rs index 088a842a1..44aabce6b 100644 --- a/embassy-rp/src/dma.rs +++ b/embassy-rp/src/dma.rs @@ -96,7 +96,7 @@ pub unsafe fn write_repeated<'a, C: Channel, W: Word>( ) -> Transfer<'a, C> { copy_inner( ch, - &mut DUMMY as *const u32, + core::ptr::addr_of_mut!(DUMMY) as *const u32, to as *mut u32, len, W::size(), diff --git a/embassy-rp/src/flash.rs b/embassy-rp/src/flash.rs index 2d673cf6c..68b1ecab3 100644 --- a/embassy-rp/src/flash.rs +++ b/embassy-rp/src/flash.rs @@ -420,8 +420,6 @@ impl<'d, T: Instance, const FLASH_SIZE: usize> embedded_storage_async::nor_flash #[allow(dead_code)] mod ram_helpers { - use core::marker::PhantomData; - use super::*; use crate::rom_data; diff --git a/embassy-rp/src/float/mod.rs b/embassy-rp/src/float/mod.rs index 945afff90..3ad6f1c50 100644 --- a/embassy-rp/src/float/mod.rs +++ b/embassy-rp/src/float/mod.rs @@ -89,6 +89,7 @@ pub(crate) trait Float: } /// Returns true if `self` is infinity + #[allow(unused)] fn is_infinity(self) -> bool { (self.repr() & (Self::EXPONENT_MASK | Self::SIGNIFICAND_MASK)) == Self::EXPONENT_MASK } diff --git a/embassy-rp/src/gpio.rs b/embassy-rp/src/gpio.rs index 62eeb4cf6..ddba8f8fb 100644 --- a/embassy-rp/src/gpio.rs +++ b/embassy-rp/src/gpio.rs @@ -976,8 +976,6 @@ impl_pin!(PIN_QSPI_SD3, Bank::Qspi, 5); // ==================== mod eh02 { - use core::convert::Infallible; - use super::*; impl<'d> embedded_hal_02::digital::v2::InputPin for Input<'d> { diff --git a/embassy-rp/src/lib.rs b/embassy-rp/src/lib.rs index 7092b3fab..d91cea410 100644 --- a/embassy-rp/src/lib.rs +++ b/embassy-rp/src/lib.rs @@ -274,7 +274,7 @@ pub fn install_core0_stack_guard() -> Result<(), ()> { extern "C" { static mut _stack_end: usize; } - unsafe { install_stack_guard(&mut _stack_end as *mut usize) } + unsafe { install_stack_guard(core::ptr::addr_of_mut!(_stack_end)) } } #[inline(always)] @@ -354,6 +354,7 @@ pub fn init(config: config::Config) -> Peripherals { /// Extension trait for PAC regs, adding atomic xor/bitset/bitclear writes. trait RegExt { + #[allow(unused)] fn write_xor(&self, f: impl FnOnce(&mut T) -> R) -> R; fn write_set(&self, f: impl FnOnce(&mut T) -> R) -> R; fn write_clear(&self, f: impl FnOnce(&mut T) -> R) -> R; diff --git a/embassy-rp/src/relocate.rs b/embassy-rp/src/relocate.rs index b35b4ed72..6139d892f 100644 --- a/embassy-rp/src/relocate.rs +++ b/embassy-rp/src/relocate.rs @@ -1,5 +1,3 @@ -use core::iter::Iterator; - use pio::{Program, SideSet, Wrap}; pub struct CodeIterator<'a, I> diff --git a/embassy-rp/src/uart/buffered.rs b/embassy-rp/src/uart/buffered.rs index 99c958129..8178cd801 100644 --- a/embassy-rp/src/uart/buffered.rs +++ b/embassy-rp/src/uart/buffered.rs @@ -1,17 +1,11 @@ //! Buffered UART driver. -use core::future::{poll_fn, Future}; +use core::future::Future; use core::slice; -use core::task::Poll; -use atomic_polyfill::{AtomicU8, Ordering}; +use atomic_polyfill::AtomicU8; use embassy_hal_internal::atomic_ring_buffer::RingBuffer; -use embassy_sync::waitqueue::AtomicWaker; -use embassy_time::Timer; use super::*; -use crate::clocks::clk_peri_freq; -use crate::interrupt::typelevel::{Binding, Interrupt}; -use crate::{interrupt, RegExt}; pub struct State { tx_waker: AtomicWaker, diff --git a/embassy-rp/src/usb.rs b/embassy-rp/src/usb.rs index 905661d64..d68dee4a3 100644 --- a/embassy-rp/src/usb.rs +++ b/embassy-rp/src/usb.rs @@ -465,7 +465,6 @@ impl<'d, T: Instance> driver::Bus for Bus<'d, T> { trait Dir { fn dir() -> Direction; - fn waker(i: usize) -> &'static AtomicWaker; } /// Type for In direction. @@ -474,11 +473,6 @@ impl Dir for In { fn dir() -> Direction { Direction::In } - - #[inline] - fn waker(i: usize) -> &'static AtomicWaker { - &EP_IN_WAKERS[i] - } } /// Type for Out direction. @@ -487,11 +481,6 @@ impl Dir for Out { fn dir() -> Direction { Direction::Out } - - #[inline] - fn waker(i: usize) -> &'static AtomicWaker { - &EP_OUT_WAKERS[i] - } } /// Endpoint for RP USB driver. diff --git a/embassy-stm32-wpan/src/consts.rs b/embassy-stm32-wpan/src/consts.rs index bd70851ea..6aaef1d35 100644 --- a/embassy-stm32-wpan/src/consts.rs +++ b/embassy-stm32-wpan/src/consts.rs @@ -1,5 +1,3 @@ -use core::convert::TryFrom; - use crate::evt::CsEvt; use crate::PacketHeader; diff --git a/embassy-stm32/src/can/bx/mod.rs b/embassy-stm32/src/can/bx/mod.rs index 33e702c6e..a369ae6fd 100644 --- a/embassy-stm32/src/can/bx/mod.rs +++ b/embassy-stm32/src/can/bx/mod.rs @@ -28,7 +28,7 @@ pub mod filter; #[allow(clippy::all)] // generated code use core::cmp::{Ord, Ordering}; -use core::convert::{Infallible, Into, TryInto}; +use core::convert::Infallible; use core::marker::PhantomData; use core::mem; diff --git a/embassy-stm32/src/can/bxcan.rs b/embassy-stm32/src/can/bxcan.rs index bb7cc3d7f..1a625bdc4 100644 --- a/embassy-stm32/src/can/bxcan.rs +++ b/embassy-stm32/src/can/bxcan.rs @@ -1,4 +1,3 @@ -use core::convert::AsMut; use core::future::poll_fn; use core::marker::PhantomData; use core::ops::{Deref, DerefMut}; diff --git a/embassy-stm32/src/can/fd/message_ram/mod.rs b/embassy-stm32/src/can/fd/message_ram/mod.rs index 830edf3bb..040a999b4 100644 --- a/embassy-stm32/src/can/fd/message_ram/mod.rs +++ b/embassy-stm32/src/can/fd/message_ram/mod.rs @@ -140,26 +140,6 @@ pub(crate) struct _TxBufferElement; impl generic::Readable for TxBufferElementHeader {} impl generic::Writable for TxBufferElementHeader {} -/// FdCan Message RAM instance. -/// -/// # Safety -/// -/// It is only safe to implement this trait, when: -/// -/// * The implementing type has ownership of the Message RAM, preventing any -/// other accesses to the register block. -/// * `MSG_RAM` is a pointer to the Message RAM block and can be safely accessed -/// for as long as ownership or a borrow of the implementing type is present. -pub unsafe trait Instance { - const MSG_RAM: *mut RegisterBlock; - fn msg_ram(&self) -> &RegisterBlock { - unsafe { &*Self::MSG_RAM } - } - fn msg_ram_mut(&mut self) -> &mut RegisterBlock { - unsafe { &mut *Self::MSG_RAM } - } -} - // Ensure the RegisterBlock is the same size as on pg 1957 of RM0440. static_assertions::assert_eq_size!(Filters, [u32; 28 + 16]); static_assertions::assert_eq_size!(Receive, [u32; 54]); diff --git a/embassy-stm32/src/can/fdcan.rs b/embassy-stm32/src/can/fdcan.rs index fe8969a5a..4c40f10e3 100644 --- a/embassy-stm32/src/can/fdcan.rs +++ b/embassy-stm32/src/can/fdcan.rs @@ -883,9 +883,9 @@ macro_rules! impl_fdcan { fn ram() -> &'static crate::pac::fdcanram::Fdcanram { &crate::pac::$msg_ram_inst } - unsafe fn mut_state() -> & 'static mut sealed::State { + unsafe fn mut_state() -> &'static mut sealed::State { static mut STATE: sealed::State = sealed::State::new(); - & mut STATE + &mut *core::ptr::addr_of_mut!(STATE) } fn state() -> &'static sealed::State { unsafe { peripherals::$inst::mut_state() } diff --git a/embassy-stm32/src/dma/mod.rs b/embassy-stm32/src/dma/mod.rs index d5e88a20a..2f98b9857 100644 --- a/embassy-stm32/src/dma/mod.rs +++ b/embassy-stm32/src/dma/mod.rs @@ -44,6 +44,7 @@ pub(crate) mod sealed { fn id(&self) -> u8; } pub trait ChannelInterrupt { + #[cfg_attr(not(feature = "rt"), allow(unused))] unsafe fn on_irq(); } } diff --git a/embassy-stm32/src/flash/f0.rs b/embassy-stm32/src/flash/f0.rs index e0c76e6b2..e2f135208 100644 --- a/embassy-stm32/src/flash/f0.rs +++ b/embassy-stm32/src/flash/f0.rs @@ -1,4 +1,3 @@ -use core::convert::TryInto; use core::ptr::write_volatile; use core::sync::atomic::{fence, Ordering}; diff --git a/embassy-stm32/src/flash/f1f3.rs b/embassy-stm32/src/flash/f1f3.rs index e7790369a..b16354a74 100644 --- a/embassy-stm32/src/flash/f1f3.rs +++ b/embassy-stm32/src/flash/f1f3.rs @@ -1,4 +1,3 @@ -use core::convert::TryInto; use core::ptr::write_volatile; use core::sync::atomic::{fence, Ordering}; diff --git a/embassy-stm32/src/flash/f4.rs b/embassy-stm32/src/flash/f4.rs index 57447bea5..00e61f2d2 100644 --- a/embassy-stm32/src/flash/f4.rs +++ b/embassy-stm32/src/flash/f4.rs @@ -1,4 +1,3 @@ -use core::convert::TryInto; use core::ptr::write_volatile; use core::sync::atomic::{fence, AtomicBool, Ordering}; diff --git a/embassy-stm32/src/flash/f7.rs b/embassy-stm32/src/flash/f7.rs index 0f512bbc4..72de0b445 100644 --- a/embassy-stm32/src/flash/f7.rs +++ b/embassy-stm32/src/flash/f7.rs @@ -1,4 +1,3 @@ -use core::convert::TryInto; use core::ptr::write_volatile; use core::sync::atomic::{fence, Ordering}; diff --git a/embassy-stm32/src/flash/g.rs b/embassy-stm32/src/flash/g.rs index b69c4343b..6a5adc941 100644 --- a/embassy-stm32/src/flash/g.rs +++ b/embassy-stm32/src/flash/g.rs @@ -1,4 +1,3 @@ -use core::convert::TryInto; use core::ptr::write_volatile; use core::sync::atomic::{fence, Ordering}; diff --git a/embassy-stm32/src/flash/h7.rs b/embassy-stm32/src/flash/h7.rs index 743925e17..e32a82eef 100644 --- a/embassy-stm32/src/flash/h7.rs +++ b/embassy-stm32/src/flash/h7.rs @@ -1,4 +1,3 @@ -use core::convert::TryInto; use core::ptr::write_volatile; use core::sync::atomic::{fence, Ordering}; diff --git a/embassy-stm32/src/flash/u5.rs b/embassy-stm32/src/flash/u5.rs index 3787082f9..580c490da 100644 --- a/embassy-stm32/src/flash/u5.rs +++ b/embassy-stm32/src/flash/u5.rs @@ -1,4 +1,3 @@ -use core::convert::TryInto; use core::ptr::write_volatile; use core::sync::atomic::{fence, Ordering}; diff --git a/embassy-stm32/src/i2c/v1.rs b/embassy-stm32/src/i2c/v1.rs index f1ed7ca40..9f29ed5e0 100644 --- a/embassy-stm32/src/i2c/v1.rs +++ b/embassy-stm32/src/i2c/v1.rs @@ -15,7 +15,6 @@ use embedded_hal_1::i2c::Operation; use super::*; use crate::dma::Transfer; use crate::pac::i2c; -use crate::time::Hertz; // /!\ /!\ // /!\ Implementation note! /!\ diff --git a/embassy-stm32/src/i2c/v2.rs b/embassy-stm32/src/i2c/v2.rs index 1ac2740df..8baf2849d 100644 --- a/embassy-stm32/src/i2c/v2.rs +++ b/embassy-stm32/src/i2c/v2.rs @@ -9,7 +9,6 @@ use embedded_hal_1::i2c::Operation; use super::*; use crate::dma::Transfer; use crate::pac::i2c; -use crate::time::Hertz; pub(crate) unsafe fn on_interrupt() { let regs = T::regs(); diff --git a/embassy-stm32/src/rtc/datetime.rs b/embassy-stm32/src/rtc/datetime.rs index ef92fa4bb..2bad79923 100644 --- a/embassy-stm32/src/rtc/datetime.rs +++ b/embassy-stm32/src/rtc/datetime.rs @@ -1,8 +1,5 @@ #[cfg(feature = "chrono")] -use core::convert::From; - -#[cfg(feature = "chrono")] -use chrono::{self, Datelike, NaiveDate, Timelike, Weekday}; +use chrono::{Datelike, NaiveDate, Timelike, Weekday}; #[cfg(any(feature = "defmt", feature = "time"))] use crate::peripherals::RTC; diff --git a/embassy-stm32/src/sdmmc/mod.rs b/embassy-stm32/src/sdmmc/mod.rs index fa1f710d8..c0b3a2183 100644 --- a/embassy-stm32/src/sdmmc/mod.rs +++ b/embassy-stm32/src/sdmmc/mod.rs @@ -1427,8 +1427,6 @@ pub(crate) mod sealed { fn regs() -> RegBlock; fn state() -> &'static AtomicWaker; } - - pub trait Pins {} } /// SDMMC instance trait. diff --git a/embassy-stm32/src/time_driver.rs b/embassy-stm32/src/time_driver.rs index 37b2e7526..9be56d3d1 100644 --- a/embassy-stm32/src/time_driver.rs +++ b/embassy-stm32/src/time_driver.rs @@ -1,7 +1,6 @@ #![allow(non_snake_case)] use core::cell::Cell; -use core::convert::TryInto; use core::sync::atomic::{compiler_fence, AtomicU32, AtomicU8, Ordering}; use core::{mem, ptr}; diff --git a/embassy-stm32/src/timer/complementary_pwm.rs b/embassy-stm32/src/timer/complementary_pwm.rs index 72f1ec864..723e8506b 100644 --- a/embassy-stm32/src/timer/complementary_pwm.rs +++ b/embassy-stm32/src/timer/complementary_pwm.rs @@ -10,7 +10,6 @@ use super::*; #[allow(unused_imports)] use crate::gpio::sealed::{AFType, Pin}; use crate::gpio::{AnyPin, OutputType}; -use crate::time::Hertz; use crate::Peripheral; /// Complementary PWM pin wrapper. diff --git a/embassy-stm32/src/timer/simple_pwm.rs b/embassy-stm32/src/timer/simple_pwm.rs index 6df2f66ec..4669fc6cc 100644 --- a/embassy-stm32/src/timer/simple_pwm.rs +++ b/embassy-stm32/src/timer/simple_pwm.rs @@ -8,7 +8,6 @@ use super::*; #[allow(unused_imports)] use crate::gpio::sealed::{AFType, Pin}; use crate::gpio::{AnyPin, OutputType}; -use crate::time::Hertz; use crate::Peripheral; /// Channel 1 marker type. diff --git a/embassy-stm32/src/uid.rs b/embassy-stm32/src/uid.rs index aa13586f8..5e38532bd 100644 --- a/embassy-stm32/src/uid.rs +++ b/embassy-stm32/src/uid.rs @@ -27,5 +27,5 @@ pub fn uid_hex_bytes() -> &'static [u8; 24] { LOADED = true; } }); - unsafe { &UID_HEX } + unsafe { &*core::ptr::addr_of!(UID_HEX) } } diff --git a/embassy-stm32/src/usart/buffered.rs b/embassy-stm32/src/usart/buffered.rs index c11e3382f..683b0f8b2 100644 --- a/embassy-stm32/src/usart/buffered.rs +++ b/embassy-stm32/src/usart/buffered.rs @@ -1,13 +1,10 @@ -use core::future::poll_fn; use core::slice; -use core::sync::atomic::{AtomicBool, Ordering}; -use core::task::Poll; +use core::sync::atomic::AtomicBool; use embassy_hal_internal::atomic_ring_buffer::RingBuffer; use embassy_sync::waitqueue::AtomicWaker; use super::*; -use crate::interrupt::typelevel::Interrupt; /// Interrupt handler. pub struct InterruptHandler { diff --git a/embassy-stm32/src/usb/otg.rs b/embassy-stm32/src/usb/otg.rs index 80a08f3c5..d4095b466 100644 --- a/embassy-stm32/src/usb/otg.rs +++ b/embassy-stm32/src/usb/otg.rs @@ -6,8 +6,8 @@ use core::task::Poll; use embassy_hal_internal::{into_ref, Peripheral}; use embassy_sync::waitqueue::AtomicWaker; use embassy_usb_driver::{ - self, Bus as _, Direction, EndpointAddress, EndpointAllocError, EndpointError, EndpointIn, EndpointInfo, - EndpointOut, EndpointType, Event, Unsupported, + Bus as _, Direction, EndpointAddress, EndpointAllocError, EndpointError, EndpointIn, EndpointInfo, EndpointOut, + EndpointType, Event, Unsupported, }; use futures::future::poll_fn; diff --git a/embassy-stm32/src/usb/usb.rs b/embassy-stm32/src/usb/usb.rs index 1fb2c9ebb..c4f9140da 100644 --- a/embassy-stm32/src/usb/usb.rs +++ b/embassy-stm32/src/usb/usb.rs @@ -637,7 +637,6 @@ impl<'d, T: Instance> driver::Bus for Bus<'d, T> { trait Dir { fn dir() -> Direction; - fn waker(i: usize) -> &'static AtomicWaker; } /// Marker type for the "IN" direction. @@ -646,11 +645,6 @@ impl Dir for In { fn dir() -> Direction { Direction::In } - - #[inline] - fn waker(i: usize) -> &'static AtomicWaker { - &EP_IN_WAKERS[i] - } } /// Marker type for the "OUT" direction. @@ -659,11 +653,6 @@ impl Dir for Out { fn dir() -> Direction { Direction::Out } - - #[inline] - fn waker(i: usize) -> &'static AtomicWaker { - &EP_OUT_WAKERS[i] - } } /// USB endpoint. diff --git a/examples/rp/src/bin/multicore.rs b/examples/rp/src/bin/multicore.rs index c7b087476..7cb546c91 100644 --- a/examples/rp/src/bin/multicore.rs +++ b/examples/rp/src/bin/multicore.rs @@ -30,10 +30,14 @@ fn main() -> ! { let p = embassy_rp::init(Default::default()); let led = Output::new(p.PIN_25, Level::Low); - spawn_core1(p.CORE1, unsafe { &mut CORE1_STACK }, move || { - let executor1 = EXECUTOR1.init(Executor::new()); - executor1.run(|spawner| unwrap!(spawner.spawn(core1_task(led)))); - }); + spawn_core1( + p.CORE1, + unsafe { &mut *core::ptr::addr_of_mut!(CORE1_STACK) }, + move || { + let executor1 = EXECUTOR1.init(Executor::new()); + executor1.run(|spawner| unwrap!(spawner.spawn(core1_task(led)))); + }, + ); let executor0 = EXECUTOR0.init(Executor::new()); executor0.run(|spawner| unwrap!(spawner.spawn(core0_task()))); diff --git a/examples/std/src/bin/net.rs b/examples/std/src/bin/net.rs index dad93d0a1..59813d8cb 100644 --- a/examples/std/src/bin/net.rs +++ b/examples/std/src/bin/net.rs @@ -1,5 +1,3 @@ -use std::default::Default; - use clap::Parser; use embassy_executor::{Executor, Spawner}; use embassy_net::tcp::TcpSocket; diff --git a/examples/std/src/bin/net_dns.rs b/examples/std/src/bin/net_dns.rs index fca1e076e..3b6a3de37 100644 --- a/examples/std/src/bin/net_dns.rs +++ b/examples/std/src/bin/net_dns.rs @@ -1,5 +1,3 @@ -use std::default::Default; - use clap::Parser; use embassy_executor::{Executor, Spawner}; use embassy_net::dns::DnsQueryType; diff --git a/examples/std/src/bin/tcp_accept.rs b/examples/std/src/bin/tcp_accept.rs index 00ccd83a7..e8b6eaa6c 100644 --- a/examples/std/src/bin/tcp_accept.rs +++ b/examples/std/src/bin/tcp_accept.rs @@ -1,5 +1,4 @@ use core::fmt::Write as _; -use std::default::Default; use clap::Parser; use embassy_executor::{Executor, Spawner}; diff --git a/examples/stm32f2/src/bin/pll.rs b/examples/stm32f2/src/bin/pll.rs index e32f283d1..e39e2daec 100644 --- a/examples/stm32f2/src/bin/pll.rs +++ b/examples/stm32f2/src/bin/pll.rs @@ -1,8 +1,6 @@ #![no_std] #![no_main] -use core::convert::TryFrom; - use defmt::*; use embassy_executor::Spawner; use embassy_stm32::time::Hertz; diff --git a/examples/stm32h7/src/bin/camera.rs b/examples/stm32h7/src/bin/camera.rs index e5a104baf..170a5aa28 100644 --- a/examples/stm32h7/src/bin/camera.rs +++ b/examples/stm32h7/src/bin/camera.rs @@ -78,9 +78,9 @@ async fn main(_spawner: Spawner) { ); defmt::info!("attempting capture"); - defmt::unwrap!(dcmi.capture(unsafe { &mut FRAME }).await); + defmt::unwrap!(dcmi.capture(unsafe { &mut *core::ptr::addr_of_mut!(FRAME) }).await); - defmt::info!("captured frame: {:x}", unsafe { &FRAME }); + defmt::info!("captured frame: {:x}", unsafe { &*core::ptr::addr_of!(FRAME) }); defmt::info!("main loop running"); loop { diff --git a/examples/stm32l4/src/bin/spe_adin1110_http_server.rs b/examples/stm32l4/src/bin/spe_adin1110_http_server.rs index 32bfab6eb..343e09e68 100644 --- a/examples/stm32l4/src/bin/spe_adin1110_http_server.rs +++ b/examples/stm32l4/src/bin/spe_adin1110_http_server.rs @@ -42,7 +42,7 @@ bind_interrupts!(struct Irqs { RNG => rng::InterruptHandler; }); -use embassy_net_adin1110::{self, Device, Runner, ADIN1110}; +use embassy_net_adin1110::{Device, Runner, ADIN1110}; use embedded_hal_bus::spi::ExclusiveDevice; use hal::gpio::Pull; use hal::i2c::Config as I2C_Config; diff --git a/tests/rp/src/bin/gpio_multicore.rs b/tests/rp/src/bin/gpio_multicore.rs index 8aed9b80c..e9c6f3122 100644 --- a/tests/rp/src/bin/gpio_multicore.rs +++ b/tests/rp/src/bin/gpio_multicore.rs @@ -21,10 +21,14 @@ static CHANNEL1: Channel = Channel::new(); #[cortex_m_rt::entry] fn main() -> ! { let p = embassy_rp::init(Default::default()); - spawn_core1(p.CORE1, unsafe { &mut CORE1_STACK }, move || { - let executor1 = EXECUTOR1.init(Executor::new()); - executor1.run(|spawner| unwrap!(spawner.spawn(core1_task(p.PIN_1)))); - }); + spawn_core1( + p.CORE1, + unsafe { &mut *core::ptr::addr_of_mut!(CORE1_STACK) }, + move || { + let executor1 = EXECUTOR1.init(Executor::new()); + executor1.run(|spawner| unwrap!(spawner.spawn(core1_task(p.PIN_1)))); + }, + ); let executor0 = EXECUTOR0.init(Executor::new()); executor0.run(|spawner| unwrap!(spawner.spawn(core0_task(p.PIN_0)))); } diff --git a/tests/rp/src/bin/i2c.rs b/tests/rp/src/bin/i2c.rs index 153b37999..9615007bd 100644 --- a/tests/rp/src/bin/i2c.rs +++ b/tests/rp/src/bin/i2c.rs @@ -210,10 +210,14 @@ async fn controller_task(con: &mut i2c::I2c<'static, I2C0, i2c::Async>) { config.addr = DEV_ADDR as u16; let device = i2c_slave::I2cSlave::new(p.I2C1, d_sda, d_scl, Irqs, config); - spawn_core1(p.CORE1, unsafe { &mut CORE1_STACK }, move || { - let executor1 = EXECUTOR1.init(Executor::new()); - executor1.run(|spawner| unwrap!(spawner.spawn(device_task(device)))); - }); + spawn_core1( + p.CORE1, + unsafe { &mut *core::ptr::addr_of_mut!(CORE1_STACK) }, + move || { + let executor1 = EXECUTOR1.init(Executor::new()); + executor1.run(|spawner| unwrap!(spawner.spawn(device_task(device)))); + }, + ); let c_sda = p.PIN_21; let c_scl = p.PIN_20; diff --git a/tests/rp/src/bin/multicore.rs b/tests/rp/src/bin/multicore.rs index 60d9f85ec..783ea0f27 100644 --- a/tests/rp/src/bin/multicore.rs +++ b/tests/rp/src/bin/multicore.rs @@ -19,10 +19,14 @@ static CHANNEL1: Channel = Channel::new(); #[cortex_m_rt::entry] fn main() -> ! { let p = embassy_rp::init(Default::default()); - spawn_core1(p.CORE1, unsafe { &mut CORE1_STACK }, move || { - let executor1 = EXECUTOR1.init(Executor::new()); - executor1.run(|spawner| unwrap!(spawner.spawn(core1_task()))); - }); + spawn_core1( + p.CORE1, + unsafe { &mut *core::ptr::addr_of_mut!(CORE1_STACK) }, + move || { + let executor1 = EXECUTOR1.init(Executor::new()); + executor1.run(|spawner| unwrap!(spawner.spawn(core1_task()))); + }, + ); let executor0 = EXECUTOR0.init(Executor::new()); executor0.run(|spawner| unwrap!(spawner.spawn(core0_task()))); } From e5b9b5b3e95a82dbbc15c0688781bfb6fee572ac Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Wed, 20 Mar 2024 16:37:42 +0100 Subject: [PATCH 14/62] Update Nightly in CI. --- rust-toolchain-nightly.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rust-toolchain-nightly.toml b/rust-toolchain-nightly.toml index b8a7db353..98696fd2b 100644 --- a/rust-toolchain-nightly.toml +++ b/rust-toolchain-nightly.toml @@ -1,5 +1,5 @@ [toolchain] -channel = "nightly-2023-12-20" +channel = "nightly-2024-03-20" components = [ "rust-src", "rustfmt", "llvm-tools", "miri" ] targets = [ "thumbv7em-none-eabi", From fb9d42684bed1d0625da5b582e64c5fa56b7fd01 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9=20van=20Dorst?= Date: Wed, 20 Mar 2024 19:59:17 +0100 Subject: [PATCH 15/62] stm32: Fix psc compile error with current stm32-data Commit https://github.com/embassy-rs/stm32-data/commit/cc525f1b252c91272529cbea1d3d4399b43c60b4 has changed the definition of the `psc` register. Update timer/mod.rs to reflect the stm32-data change. --- embassy-stm32/src/timer/mod.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/embassy-stm32/src/timer/mod.rs b/embassy-stm32/src/timer/mod.rs index ef893c7f5..e5e84c255 100644 --- a/embassy-stm32/src/timer/mod.rs +++ b/embassy-stm32/src/timer/mod.rs @@ -97,7 +97,7 @@ pub(crate) mod sealed { let arr = unwrap!(u16::try_from(divide_by - 1)); let regs = Self::regs_core(); - regs.psc().write(|r| r.set_psc(psc)); + regs.psc().write_value(psc); regs.arr().write(|r| r.set_arr(arr)); regs.cr1().modify(|r| r.set_urs(vals::Urs::COUNTERONLY)); @@ -137,7 +137,7 @@ pub(crate) mod sealed { let regs = Self::regs_core(); let arr = regs.arr().read().arr(); - let psc = regs.psc().read().psc(); + let psc = regs.psc().read(); timer_f / arr / (psc + 1) } @@ -378,7 +378,7 @@ pub(crate) mod sealed { let arr: u32 = unwrap!((pclk_ticks_per_timer_period / (psc as u64 + 1)).try_into()); let regs = Self::regs_gp32(); - regs.psc().write(|r| r.set_psc(psc)); + regs.psc().write_value(psc); regs.arr().write_value(arr); regs.cr1().modify(|r| r.set_urs(vals::Urs::COUNTERONLY)); @@ -392,7 +392,7 @@ pub(crate) mod sealed { let regs = Self::regs_gp32(); let arr = regs.arr().read(); - let psc = regs.psc().read().psc(); + let psc = regs.psc().read(); timer_f / arr / (psc + 1) } From ab7c767c464c566fdec6fe7c0f72f084a4c891de Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9=20van=20Dorst?= Date: Wed, 20 Mar 2024 20:31:02 +0100 Subject: [PATCH 16/62] Bump stm32-data to latest tag. --- embassy-stm32/Cargo.toml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/embassy-stm32/Cargo.toml b/embassy-stm32/Cargo.toml index d3626610e..7c6312f6c 100644 --- a/embassy-stm32/Cargo.toml +++ b/embassy-stm32/Cargo.toml @@ -70,7 +70,7 @@ rand_core = "0.6.3" sdio-host = "0.5.0" critical-section = "1.1" #stm32-metapac = { version = "15" } -stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-c8b32ecae7d70cea2705095c4fc6bd5f59d238d5" } +stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-f84633553331c2d154ee72de779a40cbb10fd1bd" } vcell = "0.1.3" nb = "1.0.0" stm32-fmc = "0.3.0" @@ -94,7 +94,7 @@ critical-section = { version = "1.1", features = ["std"] } proc-macro2 = "1.0.36" quote = "1.0.15" #stm32-metapac = { version = "15", default-features = false, features = ["metadata"]} -stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-c8b32ecae7d70cea2705095c4fc6bd5f59d238d5", default-features = false, features = ["metadata"]} +stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-f84633553331c2d154ee72de779a40cbb10fd1bd", default-features = false, features = ["metadata"]} [features] From 92fa49f5023a2f1932bfb122efd93372f40084a0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9=20van=20Dorst?= Date: Wed, 20 Mar 2024 20:42:03 +0100 Subject: [PATCH 17/62] Also fix time_driver.rs --- embassy-stm32/src/time_driver.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/embassy-stm32/src/time_driver.rs b/embassy-stm32/src/time_driver.rs index 9be56d3d1..e78f81dca 100644 --- a/embassy-stm32/src/time_driver.rs +++ b/embassy-stm32/src/time_driver.rs @@ -286,7 +286,7 @@ impl RtcDriver { Ok(n) => n, }; - r.psc().write(|w| w.set_psc(psc)); + r.psc().write_value(psc); r.arr().write(|w| w.set_arr(u16::MAX)); // Set URS, generate update and clear URS From 53ed4b8b2e79ee41692132793aa7910dd972e524 Mon Sep 17 00:00:00 2001 From: Felix Lelchuk <4553555+flelchuk@users.noreply.github.com> Date: Wed, 20 Mar 2024 21:15:22 +0100 Subject: [PATCH 18/62] usb-logger: avoid data loss at pipe wraparound --- embassy-usb-logger/src/lib.rs | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/embassy-usb-logger/src/lib.rs b/embassy-usb-logger/src/lib.rs index da5ff0f36..6f4836d21 100644 --- a/embassy-usb-logger/src/lib.rs +++ b/embassy-usb-logger/src/lib.rs @@ -151,7 +151,17 @@ struct Writer<'d, const N: usize>(&'d Pipe); impl<'d, const N: usize> core::fmt::Write for Writer<'d, N> { fn write_str(&mut self, s: &str) -> Result<(), core::fmt::Error> { - let _ = self.0.try_write(s.as_bytes()); + // The Pipe is implemented in such way that we cannot + // write across the wraparound discontinuity. + let b = s.as_bytes(); + if let Ok(n) = self.0.try_write(b) { + if n < b.len() { + // We wrote some data but not all, attempt again + // as the reason might be a wraparound in the + // ring buffer, which resolves on second attempt. + let _ = self.0.try_write(&b[n..]); + } + } Ok(()) } } From 08e2ba9d74a1ea1a17b67d362db14839d26dfb77 Mon Sep 17 00:00:00 2001 From: Ralf Date: Thu, 21 Mar 2024 08:35:19 +0100 Subject: [PATCH 19/62] STM32 BufferedUart: wake receive task for each received byte Fixes https://github.com/embassy-rs/embassy/issues/2719 --- embassy-stm32/src/usart/buffered.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/embassy-stm32/src/usart/buffered.rs b/embassy-stm32/src/usart/buffered.rs index 683b0f8b2..51862e185 100644 --- a/embassy-stm32/src/usart/buffered.rs +++ b/embassy-stm32/src/usart/buffered.rs @@ -52,7 +52,7 @@ impl interrupt::typelevel::Handler for Interrupt // FIXME: Should we disable any further RX interrupts when the buffer becomes full. } - if state.rx_buf.is_full() { + if !state.rx_buf.is_empty() { state.rx_waker.wake(); } } From 3ae19bb8f983b627d067e6167db4a7f19af28d1e Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Thu, 21 Mar 2024 15:15:51 +0100 Subject: [PATCH 20/62] Update stable Rust to 1.77 --- rust-toolchain.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rust-toolchain.toml b/rust-toolchain.toml index a6fe52ee2..2f5d17069 100644 --- a/rust-toolchain.toml +++ b/rust-toolchain.toml @@ -1,5 +1,5 @@ [toolchain] -channel = "1.75" +channel = "1.77" components = [ "rust-src", "rustfmt", "llvm-tools" ] targets = [ "thumbv7em-none-eabi", From 10597e3b4d18818d322be95e1a57656855f99852 Mon Sep 17 00:00:00 2001 From: DeepSOIC Date: Thu, 21 Mar 2024 19:18:35 +0100 Subject: [PATCH 21/62] nRF52840/config: add dcdc voltage parameter address #2700 --- embassy-nrf/src/lib.rs | 39 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 39 insertions(+) diff --git a/embassy-nrf/src/lib.rs b/embassy-nrf/src/lib.rs index 718f229a3..3457dd933 100644 --- a/embassy-nrf/src/lib.rs +++ b/embassy-nrf/src/lib.rs @@ -225,10 +225,31 @@ pub mod config { /// Config for the first stage DCDC (VDDH -> VDD), if disabled LDO will be used. #[cfg(feature = "nrf52840")] pub reg0: bool, + /// Configure the voltage of the first stage DCDC. It is stored in non-volatile memory (UICR.REGOUT0 register); pass None to not touch it. + #[cfg(feature = "nrf52840")] + pub reg0_voltage: Option, /// Config for the second stage DCDC (VDD -> DEC4), if disabled LDO will be used. pub reg1: bool, } + /// Output voltage setting for REG0 regulator stage. + #[cfg(feature = "nrf52840")] + pub enum Reg0Voltage { + /// 1.8 V + _1V8 = 0, + /// 2.1 V + _2V1 = 1, + /// 2.4 V + _2V4 = 2, + /// 2.7 V + _2V7 = 3, + /// 3.0 V + _3V0 = 4, + /// 3.3 V + _3v3 = 5, + //ERASED = 7, means 1.8V + } + /// Settings for enabling the built in DCDC converters. #[cfg(feature = "_nrf5340-app")] pub struct DcdcConfig { @@ -279,6 +300,8 @@ pub mod config { dcdc: DcdcConfig { #[cfg(feature = "nrf52840")] reg0: false, + #[cfg(feature = "nrf52840")] + reg0_voltage: None, reg1: false, }, #[cfg(feature = "_nrf5340-app")] @@ -337,6 +360,7 @@ mod consts { pub const UICR_PSELRESET2: *mut u32 = 0x10001204 as *mut u32; pub const UICR_NFCPINS: *mut u32 = 0x1000120C as *mut u32; pub const UICR_APPROTECT: *mut u32 = 0x10001208 as *mut u32; + pub const UICR_REGOUT0: *mut u32 = 0x10001304 as *mut u32; pub const APPROTECT_ENABLED: u32 = 0x0000_0000; pub const APPROTECT_DISABLED: u32 = 0x0000_005a; } @@ -493,6 +517,21 @@ pub fn init(config: config::Config) -> Peripherals { } } + #[cfg(feature = "nrf52840")] + unsafe { + if let Some(value) = config.dcdc.reg0_voltage { + let value = value as u32; + let res = uicr_write_masked(consts::UICR_REGOUT0, value, 0b00000000_00000000_00000000_00000111); + needs_reset |= res == WriteResult::Written; + if res == WriteResult::Failed { + warn!( + "Failed to set regulator voltage, as UICR is already programmed to some other setting, and can't be changed without erasing it.\n\ + To fix this, erase UICR manually, for example using `probe-rs erase` or `nrfjprog --eraseuicr`." + ); + } + } + } + if needs_reset { cortex_m::peripheral::SCB::sys_reset(); } From a85e9c6e68fbe23aa8f1fc6a1c1e7ca935c1506a Mon Sep 17 00:00:00 2001 From: Maarten de Vries Date: Fri, 22 Mar 2024 12:29:01 +0100 Subject: [PATCH 22/62] Forward the "std" feature to the critical-section crate in embassy-sync. Otherwise, using embassy-sync in unit tests will result in linker errors when using the CriticalSectionRawMutex. --- embassy-sync/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/embassy-sync/Cargo.toml b/embassy-sync/Cargo.toml index 85673026c..aaf6fab1d 100644 --- a/embassy-sync/Cargo.toml +++ b/embassy-sync/Cargo.toml @@ -20,7 +20,7 @@ src_base_git = "https://github.com/embassy-rs/embassy/blob/$COMMIT/embassy-sync/ target = "thumbv7em-none-eabi" [features] -std = [] +std = ["critical-section/std"] turbowakers = [] [dependencies] From 7cf6490fbaea88410c91755f57cf8703806ab4a1 Mon Sep 17 00:00:00 2001 From: Adam Greig Date: Sat, 23 Mar 2024 00:11:05 +0000 Subject: [PATCH 23/62] usb: remove device_descriptor buffer, store bytes in UsbDevice.inner instead --- embassy-usb-logger/src/lib.rs | 3 -- embassy-usb/src/builder.rs | 7 --- embassy-usb/src/descriptor.rs | 51 ++++++++++--------- embassy-usb/src/lib.rs | 9 ++-- embassy-usb/src/msos.rs | 6 +-- .../boot/application/stm32wb-dfu/src/main.rs | 2 - .../boot/bootloader/stm32wb-dfu/src/main.rs | 2 - examples/nrf52840/src/bin/usb_ethernet.rs | 2 - examples/nrf52840/src/bin/usb_hid_keyboard.rs | 2 - examples/nrf52840/src/bin/usb_hid_mouse.rs | 2 - examples/nrf52840/src/bin/usb_serial.rs | 2 - .../nrf52840/src/bin/usb_serial_multitask.rs | 2 - .../nrf52840/src/bin/usb_serial_winusb.rs | 2 - examples/rp/src/bin/pio_uart.rs | 2 - examples/rp/src/bin/usb_ethernet.rs | 2 - examples/rp/src/bin/usb_hid_keyboard.rs | 2 - examples/rp/src/bin/usb_hid_mouse.rs | 2 - examples/rp/src/bin/usb_midi.rs | 2 - examples/rp/src/bin/usb_raw.rs | 2 - examples/rp/src/bin/usb_raw_bulk.rs | 2 - examples/rp/src/bin/usb_serial.rs | 2 - examples/rp/src/bin/usb_serial_with_logger.rs | 2 - examples/stm32f1/src/bin/usb_serial.rs | 2 - examples/stm32f3/src/bin/usb_serial.rs | 2 - examples/stm32f4/src/bin/usb_ethernet.rs | 2 - examples/stm32f4/src/bin/usb_hid_keyboard.rs | 2 - examples/stm32f4/src/bin/usb_hid_mouse.rs | 2 - examples/stm32f4/src/bin/usb_raw.rs | 2 - examples/stm32f4/src/bin/usb_serial.rs | 2 - examples/stm32f7/src/bin/usb_serial.rs | 2 - examples/stm32g0/src/bin/usb_serial.rs | 2 - examples/stm32g4/src/bin/usb_serial.rs | 2 - examples/stm32h5/src/bin/usb_serial.rs | 2 - examples/stm32h7/src/bin/usb_serial.rs | 2 - examples/stm32l1/src/bin/usb_serial.rs | 2 - examples/stm32l4/src/bin/usb_serial.rs | 2 - examples/stm32l5/src/bin/usb_ethernet.rs | 2 - examples/stm32l5/src/bin/usb_hid_mouse.rs | 2 - examples/stm32l5/src/bin/usb_serial.rs | 2 - examples/stm32u5/src/bin/usb_serial.rs | 2 - 40 files changed, 33 insertions(+), 113 deletions(-) diff --git a/embassy-usb-logger/src/lib.rs b/embassy-usb-logger/src/lib.rs index 6f4836d21..34d1ca663 100644 --- a/embassy-usb-logger/src/lib.rs +++ b/embassy-usb-logger/src/lib.rs @@ -16,7 +16,6 @@ type CS = embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex; /// The logger state containing buffers that must live as long as the USB peripheral. pub struct LoggerState<'d> { state: State<'d>, - device_descriptor: [u8; 32], config_descriptor: [u8; 128], bos_descriptor: [u8; 16], msos_descriptor: [u8; 256], @@ -28,7 +27,6 @@ impl<'d> LoggerState<'d> { pub fn new() -> Self { Self { state: State::new(), - device_descriptor: [0; 32], config_descriptor: [0; 128], bos_descriptor: [0; 16], msos_descriptor: [0; 256], @@ -74,7 +72,6 @@ impl UsbLogger { let mut builder = Builder::new( driver, config, - &mut state.device_descriptor, &mut state.config_descriptor, &mut state.bos_descriptor, &mut state.msos_descriptor, diff --git a/embassy-usb/src/builder.rs b/embassy-usb/src/builder.rs index c4705d041..c06107396 100644 --- a/embassy-usb/src/builder.rs +++ b/embassy-usb/src/builder.rs @@ -128,7 +128,6 @@ pub struct Builder<'d, D: Driver<'d>> { driver: D, next_string_index: u8, - device_descriptor: DescriptorWriter<'d>, config_descriptor: DescriptorWriter<'d>, bos_descriptor: BosWriter<'d>, @@ -144,7 +143,6 @@ impl<'d, D: Driver<'d>> Builder<'d, D> { pub fn new( driver: D, config: Config<'d>, - device_descriptor_buf: &'d mut [u8], config_descriptor_buf: &'d mut [u8], bos_descriptor_buf: &'d mut [u8], msos_descriptor_buf: &'d mut [u8], @@ -167,11 +165,9 @@ impl<'d, D: Driver<'d>> Builder<'d, D> { _ => panic!("invalid max_packet_size_0, the allowed values are 8, 16, 32 or 64"), } - let mut device_descriptor = DescriptorWriter::new(device_descriptor_buf); let mut config_descriptor = DescriptorWriter::new(config_descriptor_buf); let mut bos_descriptor = BosWriter::new(DescriptorWriter::new(bos_descriptor_buf)); - device_descriptor.device(&config); config_descriptor.configuration(&config); bos_descriptor.bos(); @@ -183,7 +179,6 @@ impl<'d, D: Driver<'d>> Builder<'d, D> { control_buf, next_string_index: STRING_INDEX_CUSTOM_START, - device_descriptor, config_descriptor, bos_descriptor, @@ -199,7 +194,6 @@ impl<'d, D: Driver<'d>> Builder<'d, D> { self.bos_descriptor.end_bos(); // Log the number of allocator bytes actually used in descriptor buffers - info!("USB: device_descriptor used: {}", self.device_descriptor.position()); info!("USB: config_descriptor used: {}", self.config_descriptor.position()); info!("USB: bos_descriptor used: {}", self.bos_descriptor.writer.position()); info!("USB: msos_descriptor used: {}", msos_descriptor.len()); @@ -209,7 +203,6 @@ impl<'d, D: Driver<'d>> Builder<'d, D> { self.driver, self.config, self.handlers, - self.device_descriptor.into_buf(), self.config_descriptor.into_buf(), self.bos_descriptor.writer.into_buf(), msos_descriptor, diff --git a/embassy-usb/src/descriptor.rs b/embassy-usb/src/descriptor.rs index fa83ef583..eb3d1f53a 100644 --- a/embassy-usb/src/descriptor.rs +++ b/embassy-usb/src/descriptor.rs @@ -82,30 +82,6 @@ impl<'a> DescriptorWriter<'a> { self.position = start + length; } - pub(crate) fn device(&mut self, config: &Config) { - self.write( - descriptor_type::DEVICE, - &[ - 0x10, - 0x02, // bcdUSB 2.1 - config.device_class, // bDeviceClass - config.device_sub_class, // bDeviceSubClass - config.device_protocol, // bDeviceProtocol - config.max_packet_size_0, // bMaxPacketSize0 - config.vendor_id as u8, - (config.vendor_id >> 8) as u8, // idVendor - config.product_id as u8, - (config.product_id >> 8) as u8, // idProduct - config.device_release as u8, - (config.device_release >> 8) as u8, // bcdDevice - config.manufacturer.map_or(0, |_| 1), // iManufacturer - config.product.map_or(0, |_| 2), // iProduct - config.serial_number.map_or(0, |_| 3), // iSerialNumber - 1, // bNumConfigurations - ], - ); - } - pub(crate) fn configuration(&mut self, config: &Config) { self.num_interfaces_mark = Some(self.position + 4); @@ -269,6 +245,33 @@ impl<'a> DescriptorWriter<'a> { } } +/// Create a new Device Descriptor array. +/// +/// All device descriptors are always 18 bytes, so there's no need for +/// a variable-length buffer or DescriptorWriter. +pub(crate) fn device_descriptor(config: &Config) -> [u8; 18] { + [ + 18, // bLength + 0x01, // bDescriptorType + 0x10, + 0x02, // bcdUSB 2.1 + config.device_class, // bDeviceClass + config.device_sub_class, // bDeviceSubClass + config.device_protocol, // bDeviceProtocol + config.max_packet_size_0, // bMaxPacketSize0 + config.vendor_id as u8, + (config.vendor_id >> 8) as u8, // idVendor + config.product_id as u8, + (config.product_id >> 8) as u8, // idProduct + config.device_release as u8, + (config.device_release >> 8) as u8, // bcdDevice + config.manufacturer.map_or(0, |_| 1), // iManufacturer + config.product.map_or(0, |_| 2), // iProduct + config.serial_number.map_or(0, |_| 3), // iSerialNumber + 1, // bNumConfigurations + ] +} + /// A writer for Binary Object Store descriptor. pub struct BosWriter<'a> { pub(crate) writer: DescriptorWriter<'a>, diff --git a/embassy-usb/src/lib.rs b/embassy-usb/src/lib.rs index 241e33a78..d58950838 100644 --- a/embassy-usb/src/lib.rs +++ b/embassy-usb/src/lib.rs @@ -168,8 +168,6 @@ struct Interface { #[derive(PartialEq, Eq, Copy, Clone, Debug)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct UsbBufferReport { - /// Number of device descriptor bytes used - pub device_descriptor_used: usize, /// Number of config descriptor bytes used pub config_descriptor_used: usize, /// Number of bos descriptor bytes used @@ -191,7 +189,7 @@ struct Inner<'d, D: Driver<'d>> { bus: D::Bus, config: Config<'d>, - device_descriptor: &'d [u8], + device_descriptor: [u8; 18], config_descriptor: &'d [u8], bos_descriptor: &'d [u8], msos_descriptor: crate::msos::MsOsDescriptorSet<'d>, @@ -217,7 +215,6 @@ impl<'d, D: Driver<'d>> UsbDevice<'d, D> { driver: D, config: Config<'d>, handlers: Vec<&'d mut dyn Handler, MAX_HANDLER_COUNT>, - device_descriptor: &'d [u8], config_descriptor: &'d [u8], bos_descriptor: &'d [u8], msos_descriptor: crate::msos::MsOsDescriptorSet<'d>, @@ -227,6 +224,7 @@ impl<'d, D: Driver<'d>> UsbDevice<'d, D> { // Start the USB bus. // This prevent further allocation by consuming the driver. let (bus, control) = driver.start(config.max_packet_size_0 as u16); + let device_descriptor = descriptor::device_descriptor(&config); Self { control_buf, @@ -256,7 +254,6 @@ impl<'d, D: Driver<'d>> UsbDevice<'d, D> { /// Useful for tuning buffer sizes for actual usage pub fn buffer_usage(&self) -> UsbBufferReport { UsbBufferReport { - device_descriptor_used: self.inner.device_descriptor.len(), config_descriptor_used: self.inner.config_descriptor.len(), bos_descriptor_used: self.inner.bos_descriptor.len(), msos_descriptor_used: self.inner.msos_descriptor.len(), @@ -720,7 +717,7 @@ impl<'d, D: Driver<'d>> Inner<'d, D> { match dtype { descriptor_type::BOS => InResponse::Accepted(self.bos_descriptor), - descriptor_type::DEVICE => InResponse::Accepted(self.device_descriptor), + descriptor_type::DEVICE => InResponse::Accepted(&self.device_descriptor), descriptor_type::CONFIGURATION => InResponse::Accepted(self.config_descriptor), descriptor_type::STRING => { if index == 0 { diff --git a/embassy-usb/src/msos.rs b/embassy-usb/src/msos.rs index 3858c0f51..a285a3ccd 100644 --- a/embassy-usb/src/msos.rs +++ b/embassy-usb/src/msos.rs @@ -255,7 +255,7 @@ use sealed::*; unsafe fn transmute_write_to(t: &T, buf: &mut [u8]) { let bytes = core::slice::from_raw_parts((t as *const T) as *const u8, size_of::()); assert!(buf.len() >= bytes.len(), "MS OS descriptor buffer full"); - (&mut buf[..bytes.len()]).copy_from_slice(bytes); + buf[..bytes.len()].copy_from_slice(bytes); } /// Table 9. Microsoft OS 2.0 descriptor wDescriptorType values. @@ -444,9 +444,9 @@ impl CompatibleIdFeatureDescriptor { pub fn new(compatible_id: &str, sub_compatible_id: &str) -> Self { assert!(compatible_id.len() <= 8 && sub_compatible_id.len() <= 8); let mut cid = [0u8; 8]; - (&mut cid[..compatible_id.len()]).copy_from_slice(compatible_id.as_bytes()); + cid[..compatible_id.len()].copy_from_slice(compatible_id.as_bytes()); let mut scid = [0u8; 8]; - (&mut scid[..sub_compatible_id.len()]).copy_from_slice(sub_compatible_id.as_bytes()); + scid[..sub_compatible_id.len()].copy_from_slice(sub_compatible_id.as_bytes()); Self::new_raw(cid, scid) } diff --git a/examples/boot/application/stm32wb-dfu/src/main.rs b/examples/boot/application/stm32wb-dfu/src/main.rs index 37c3d7d90..929d6802c 100644 --- a/examples/boot/application/stm32wb-dfu/src/main.rs +++ b/examples/boot/application/stm32wb-dfu/src/main.rs @@ -41,7 +41,6 @@ async fn main(_spawner: Spawner) { config.product = Some("USB-DFU Runtime example"); config.serial_number = Some("1235678"); - let mut device_descriptor = [0; 256]; let mut config_descriptor = [0; 256]; let mut bos_descriptor = [0; 256]; let mut control_buf = [0; 64]; @@ -49,7 +48,6 @@ async fn main(_spawner: Spawner) { let mut builder = Builder::new( driver, config, - &mut device_descriptor, &mut config_descriptor, &mut bos_descriptor, &mut [], diff --git a/examples/boot/bootloader/stm32wb-dfu/src/main.rs b/examples/boot/bootloader/stm32wb-dfu/src/main.rs index d989fbfdf..093b39f9d 100644 --- a/examples/boot/bootloader/stm32wb-dfu/src/main.rs +++ b/examples/boot/bootloader/stm32wb-dfu/src/main.rs @@ -49,7 +49,6 @@ fn main() -> ! { let mut buffer = AlignedBuffer([0; WRITE_SIZE]); let updater = BlockingFirmwareUpdater::new(fw_config, &mut buffer.0[..]); - let mut device_descriptor = [0; 256]; let mut config_descriptor = [0; 256]; let mut bos_descriptor = [0; 256]; let mut control_buf = [0; 4096]; @@ -57,7 +56,6 @@ fn main() -> ! { let mut builder = Builder::new( driver, config, - &mut device_descriptor, &mut config_descriptor, &mut bos_descriptor, &mut [], diff --git a/examples/nrf52840/src/bin/usb_ethernet.rs b/examples/nrf52840/src/bin/usb_ethernet.rs index 3469c6e5f..a7e5c2668 100644 --- a/examples/nrf52840/src/bin/usb_ethernet.rs +++ b/examples/nrf52840/src/bin/usb_ethernet.rs @@ -70,7 +70,6 @@ async fn main(spawner: Spawner) { config.device_protocol = 0x01; // Create embassy-usb DeviceBuilder using the driver and config. - static DEVICE_DESC: StaticCell<[u8; 256]> = StaticCell::new(); static CONFIG_DESC: StaticCell<[u8; 256]> = StaticCell::new(); static BOS_DESC: StaticCell<[u8; 256]> = StaticCell::new(); static MSOS_DESC: StaticCell<[u8; 128]> = StaticCell::new(); @@ -78,7 +77,6 @@ async fn main(spawner: Spawner) { let mut builder = Builder::new( driver, config, - &mut DEVICE_DESC.init([0; 256])[..], &mut CONFIG_DESC.init([0; 256])[..], &mut BOS_DESC.init([0; 256])[..], &mut MSOS_DESC.init([0; 128])[..], diff --git a/examples/nrf52840/src/bin/usb_hid_keyboard.rs b/examples/nrf52840/src/bin/usb_hid_keyboard.rs index 3e86590c4..52f081487 100644 --- a/examples/nrf52840/src/bin/usb_hid_keyboard.rs +++ b/examples/nrf52840/src/bin/usb_hid_keyboard.rs @@ -50,7 +50,6 @@ async fn main(_spawner: Spawner) { // Create embassy-usb DeviceBuilder using the driver and config. // It needs some buffers for building the descriptors. - let mut device_descriptor = [0; 256]; let mut config_descriptor = [0; 256]; let mut bos_descriptor = [0; 256]; let mut msos_descriptor = [0; 256]; @@ -63,7 +62,6 @@ async fn main(_spawner: Spawner) { let mut builder = Builder::new( driver, config, - &mut device_descriptor, &mut config_descriptor, &mut bos_descriptor, &mut msos_descriptor, diff --git a/examples/nrf52840/src/bin/usb_hid_mouse.rs b/examples/nrf52840/src/bin/usb_hid_mouse.rs index 04ad841b7..5d2837793 100644 --- a/examples/nrf52840/src/bin/usb_hid_mouse.rs +++ b/examples/nrf52840/src/bin/usb_hid_mouse.rs @@ -43,7 +43,6 @@ async fn main(_spawner: Spawner) { // Create embassy-usb DeviceBuilder using the driver and config. // It needs some buffers for building the descriptors. - let mut device_descriptor = [0; 256]; let mut config_descriptor = [0; 256]; let mut bos_descriptor = [0; 256]; let mut msos_descriptor = [0; 256]; @@ -55,7 +54,6 @@ async fn main(_spawner: Spawner) { let mut builder = Builder::new( driver, config, - &mut device_descriptor, &mut config_descriptor, &mut bos_descriptor, &mut msos_descriptor, diff --git a/examples/nrf52840/src/bin/usb_serial.rs b/examples/nrf52840/src/bin/usb_serial.rs index aff539b1b..02048e692 100644 --- a/examples/nrf52840/src/bin/usb_serial.rs +++ b/examples/nrf52840/src/bin/usb_serial.rs @@ -48,7 +48,6 @@ async fn main(_spawner: Spawner) { // Create embassy-usb DeviceBuilder using the driver and config. // It needs some buffers for building the descriptors. - let mut device_descriptor = [0; 256]; let mut config_descriptor = [0; 256]; let mut bos_descriptor = [0; 256]; let mut msos_descriptor = [0; 256]; @@ -59,7 +58,6 @@ async fn main(_spawner: Spawner) { let mut builder = Builder::new( driver, config, - &mut device_descriptor, &mut config_descriptor, &mut bos_descriptor, &mut msos_descriptor, diff --git a/examples/nrf52840/src/bin/usb_serial_multitask.rs b/examples/nrf52840/src/bin/usb_serial_multitask.rs index 4e8118fb8..895cca8b9 100644 --- a/examples/nrf52840/src/bin/usb_serial_multitask.rs +++ b/examples/nrf52840/src/bin/usb_serial_multitask.rs @@ -67,7 +67,6 @@ async fn main(spawner: Spawner) { let state = STATE.init(State::new()); // Create embassy-usb DeviceBuilder using the driver and config. - static DEVICE_DESC: StaticCell<[u8; 256]> = StaticCell::new(); static CONFIG_DESC: StaticCell<[u8; 256]> = StaticCell::new(); static BOS_DESC: StaticCell<[u8; 256]> = StaticCell::new(); static MSOS_DESC: StaticCell<[u8; 128]> = StaticCell::new(); @@ -75,7 +74,6 @@ async fn main(spawner: Spawner) { let mut builder = Builder::new( driver, config, - &mut DEVICE_DESC.init([0; 256])[..], &mut CONFIG_DESC.init([0; 256])[..], &mut BOS_DESC.init([0; 256])[..], &mut MSOS_DESC.init([0; 128])[..], diff --git a/examples/nrf52840/src/bin/usb_serial_winusb.rs b/examples/nrf52840/src/bin/usb_serial_winusb.rs index 060f9ba94..c6675a3d3 100644 --- a/examples/nrf52840/src/bin/usb_serial_winusb.rs +++ b/examples/nrf52840/src/bin/usb_serial_winusb.rs @@ -53,7 +53,6 @@ async fn main(_spawner: Spawner) { // Create embassy-usb DeviceBuilder using the driver and config. // It needs some buffers for building the descriptors. - let mut device_descriptor = [0; 256]; let mut config_descriptor = [0; 256]; let mut bos_descriptor = [0; 256]; let mut msos_descriptor = [0; 256]; @@ -64,7 +63,6 @@ async fn main(_spawner: Spawner) { let mut builder = Builder::new( driver, config, - &mut device_descriptor, &mut config_descriptor, &mut bos_descriptor, &mut msos_descriptor, diff --git a/examples/rp/src/bin/pio_uart.rs b/examples/rp/src/bin/pio_uart.rs index a07f1c180..53b696309 100644 --- a/examples/rp/src/bin/pio_uart.rs +++ b/examples/rp/src/bin/pio_uart.rs @@ -60,7 +60,6 @@ async fn main(_spawner: Spawner) { // Create embassy-usb DeviceBuilder using the driver and config. // It needs some buffers for building the descriptors. - let mut device_descriptor = [0; 256]; let mut config_descriptor = [0; 256]; let mut bos_descriptor = [0; 256]; let mut control_buf = [0; 64]; @@ -70,7 +69,6 @@ async fn main(_spawner: Spawner) { let mut builder = Builder::new( driver, config, - &mut device_descriptor, &mut config_descriptor, &mut bos_descriptor, &mut [], // no msos descriptors diff --git a/examples/rp/src/bin/usb_ethernet.rs b/examples/rp/src/bin/usb_ethernet.rs index 01f0d5967..f1b124efa 100644 --- a/examples/rp/src/bin/usb_ethernet.rs +++ b/examples/rp/src/bin/usb_ethernet.rs @@ -64,14 +64,12 @@ async fn main(spawner: Spawner) { config.device_protocol = 0x01; // Create embassy-usb DeviceBuilder using the driver and config. - static DEVICE_DESC: StaticCell<[u8; 256]> = StaticCell::new(); static CONFIG_DESC: StaticCell<[u8; 256]> = StaticCell::new(); static BOS_DESC: StaticCell<[u8; 256]> = StaticCell::new(); static CONTROL_BUF: StaticCell<[u8; 128]> = StaticCell::new(); let mut builder = Builder::new( driver, config, - &mut DEVICE_DESC.init([0; 256])[..], &mut CONFIG_DESC.init([0; 256])[..], &mut BOS_DESC.init([0; 256])[..], &mut [], // no msos descriptors diff --git a/examples/rp/src/bin/usb_hid_keyboard.rs b/examples/rp/src/bin/usb_hid_keyboard.rs index b5ac16245..710be8d13 100644 --- a/examples/rp/src/bin/usb_hid_keyboard.rs +++ b/examples/rp/src/bin/usb_hid_keyboard.rs @@ -36,7 +36,6 @@ async fn main(_spawner: Spawner) { // Create embassy-usb DeviceBuilder using the driver and config. // It needs some buffers for building the descriptors. - let mut device_descriptor = [0; 256]; let mut config_descriptor = [0; 256]; let mut bos_descriptor = [0; 256]; // You can also add a Microsoft OS descriptor. @@ -50,7 +49,6 @@ async fn main(_spawner: Spawner) { let mut builder = Builder::new( driver, config, - &mut device_descriptor, &mut config_descriptor, &mut bos_descriptor, &mut msos_descriptor, diff --git a/examples/rp/src/bin/usb_hid_mouse.rs b/examples/rp/src/bin/usb_hid_mouse.rs index afebd8813..e8b399cb1 100644 --- a/examples/rp/src/bin/usb_hid_mouse.rs +++ b/examples/rp/src/bin/usb_hid_mouse.rs @@ -39,7 +39,6 @@ async fn main(_spawner: Spawner) { // Create embassy-usb DeviceBuilder using the driver and config. // It needs some buffers for building the descriptors. - let mut device_descriptor = [0; 256]; let mut config_descriptor = [0; 256]; let mut bos_descriptor = [0; 256]; // You can also add a Microsoft OS descriptor. @@ -53,7 +52,6 @@ async fn main(_spawner: Spawner) { let mut builder = Builder::new( driver, config, - &mut device_descriptor, &mut config_descriptor, &mut bos_descriptor, &mut msos_descriptor, diff --git a/examples/rp/src/bin/usb_midi.rs b/examples/rp/src/bin/usb_midi.rs index 95306a35c..11db1b2e1 100644 --- a/examples/rp/src/bin/usb_midi.rs +++ b/examples/rp/src/bin/usb_midi.rs @@ -46,7 +46,6 @@ async fn main(_spawner: Spawner) { // Create embassy-usb DeviceBuilder using the driver and config. // It needs some buffers for building the descriptors. - let mut device_descriptor = [0; 256]; let mut config_descriptor = [0; 256]; let mut bos_descriptor = [0; 256]; let mut control_buf = [0; 64]; @@ -54,7 +53,6 @@ async fn main(_spawner: Spawner) { let mut builder = Builder::new( driver, config, - &mut device_descriptor, &mut config_descriptor, &mut bos_descriptor, &mut [], // no msos descriptors diff --git a/examples/rp/src/bin/usb_raw.rs b/examples/rp/src/bin/usb_raw.rs index a6c8a5b2e..97e7e0244 100644 --- a/examples/rp/src/bin/usb_raw.rs +++ b/examples/rp/src/bin/usb_raw.rs @@ -93,7 +93,6 @@ async fn main(_spawner: Spawner) { // Create embassy-usb DeviceBuilder using the driver and config. // It needs some buffers for building the descriptors. - let mut device_descriptor = [0; 256]; let mut config_descriptor = [0; 256]; let mut bos_descriptor = [0; 256]; let mut msos_descriptor = [0; 256]; @@ -106,7 +105,6 @@ async fn main(_spawner: Spawner) { let mut builder = Builder::new( driver, config, - &mut device_descriptor, &mut config_descriptor, &mut bos_descriptor, &mut msos_descriptor, diff --git a/examples/rp/src/bin/usb_raw_bulk.rs b/examples/rp/src/bin/usb_raw_bulk.rs index 0dc8e9f72..331c3da4c 100644 --- a/examples/rp/src/bin/usb_raw_bulk.rs +++ b/examples/rp/src/bin/usb_raw_bulk.rs @@ -71,7 +71,6 @@ async fn main(_spawner: Spawner) { // Create embassy-usb DeviceBuilder using the driver and config. // It needs some buffers for building the descriptors. - let mut device_descriptor = [0; 256]; let mut config_descriptor = [0; 256]; let mut bos_descriptor = [0; 256]; let mut msos_descriptor = [0; 256]; @@ -80,7 +79,6 @@ async fn main(_spawner: Spawner) { let mut builder = Builder::new( driver, config, - &mut device_descriptor, &mut config_descriptor, &mut bos_descriptor, &mut msos_descriptor, diff --git a/examples/rp/src/bin/usb_serial.rs b/examples/rp/src/bin/usb_serial.rs index ab24a994c..3c9bc96dd 100644 --- a/examples/rp/src/bin/usb_serial.rs +++ b/examples/rp/src/bin/usb_serial.rs @@ -46,7 +46,6 @@ async fn main(_spawner: Spawner) { // Create embassy-usb DeviceBuilder using the driver and config. // It needs some buffers for building the descriptors. - let mut device_descriptor = [0; 256]; let mut config_descriptor = [0; 256]; let mut bos_descriptor = [0; 256]; let mut control_buf = [0; 64]; @@ -56,7 +55,6 @@ async fn main(_spawner: Spawner) { let mut builder = Builder::new( driver, config, - &mut device_descriptor, &mut config_descriptor, &mut bos_descriptor, &mut [], // no msos descriptors diff --git a/examples/rp/src/bin/usb_serial_with_logger.rs b/examples/rp/src/bin/usb_serial_with_logger.rs index 4ba4fc25c..f9cfdef94 100644 --- a/examples/rp/src/bin/usb_serial_with_logger.rs +++ b/examples/rp/src/bin/usb_serial_with_logger.rs @@ -46,7 +46,6 @@ async fn main(_spawner: Spawner) { // Create embassy-usb DeviceBuilder using the driver and config. // It needs some buffers for building the descriptors. - let mut device_descriptor = [0; 256]; let mut config_descriptor = [0; 256]; let mut bos_descriptor = [0; 256]; let mut control_buf = [0; 64]; @@ -57,7 +56,6 @@ async fn main(_spawner: Spawner) { let mut builder = Builder::new( driver, config, - &mut device_descriptor, &mut config_descriptor, &mut bos_descriptor, &mut [], // no msos descriptors diff --git a/examples/stm32f1/src/bin/usb_serial.rs b/examples/stm32f1/src/bin/usb_serial.rs index 1ae6c1dee..ee99acf41 100644 --- a/examples/stm32f1/src/bin/usb_serial.rs +++ b/examples/stm32f1/src/bin/usb_serial.rs @@ -60,7 +60,6 @@ async fn main(_spawner: Spawner) { // Create embassy-usb DeviceBuilder using the driver and config. // It needs some buffers for building the descriptors. - let mut device_descriptor = [0; 256]; let mut config_descriptor = [0; 256]; let mut bos_descriptor = [0; 256]; let mut control_buf = [0; 7]; @@ -70,7 +69,6 @@ async fn main(_spawner: Spawner) { let mut builder = Builder::new( driver, config, - &mut device_descriptor, &mut config_descriptor, &mut bos_descriptor, &mut [], // no msos descriptors diff --git a/examples/stm32f3/src/bin/usb_serial.rs b/examples/stm32f3/src/bin/usb_serial.rs index ee1c43afd..5760f2c1c 100644 --- a/examples/stm32f3/src/bin/usb_serial.rs +++ b/examples/stm32f3/src/bin/usb_serial.rs @@ -54,7 +54,6 @@ async fn main(_spawner: Spawner) { // Create embassy-usb DeviceBuilder using the driver and config. // It needs some buffers for building the descriptors. - let mut device_descriptor = [0; 256]; let mut config_descriptor = [0; 256]; let mut bos_descriptor = [0; 256]; let mut control_buf = [0; 7]; @@ -64,7 +63,6 @@ async fn main(_spawner: Spawner) { let mut builder = Builder::new( driver, config, - &mut device_descriptor, &mut config_descriptor, &mut bos_descriptor, &mut [], // no msos descriptors diff --git a/examples/stm32f4/src/bin/usb_ethernet.rs b/examples/stm32f4/src/bin/usb_ethernet.rs index 405cce6bc..d2cbeea1b 100644 --- a/examples/stm32f4/src/bin/usb_ethernet.rs +++ b/examples/stm32f4/src/bin/usb_ethernet.rs @@ -89,14 +89,12 @@ async fn main(spawner: Spawner) { config.device_protocol = 0x01; // Create embassy-usb DeviceBuilder using the driver and config. - static DEVICE_DESC: StaticCell<[u8; 256]> = StaticCell::new(); static CONFIG_DESC: StaticCell<[u8; 256]> = StaticCell::new(); static BOS_DESC: StaticCell<[u8; 256]> = StaticCell::new(); static CONTROL_BUF: StaticCell<[u8; 128]> = StaticCell::new(); let mut builder = Builder::new( driver, config, - &mut DEVICE_DESC.init([0; 256])[..], &mut CONFIG_DESC.init([0; 256])[..], &mut BOS_DESC.init([0; 256])[..], &mut [], // no msos descriptors diff --git a/examples/stm32f4/src/bin/usb_hid_keyboard.rs b/examples/stm32f4/src/bin/usb_hid_keyboard.rs index 6c25a0a64..a799b4e72 100644 --- a/examples/stm32f4/src/bin/usb_hid_keyboard.rs +++ b/examples/stm32f4/src/bin/usb_hid_keyboard.rs @@ -69,7 +69,6 @@ async fn main(_spawner: Spawner) { // Create embassy-usb DeviceBuilder using the driver and config. // It needs some buffers for building the descriptors. - let mut device_descriptor = [0; 256]; let mut config_descriptor = [0; 256]; let mut bos_descriptor = [0; 256]; // You can also add a Microsoft OS descriptor. @@ -84,7 +83,6 @@ async fn main(_spawner: Spawner) { let mut builder = Builder::new( driver, config, - &mut device_descriptor, &mut config_descriptor, &mut bos_descriptor, &mut msos_descriptor, diff --git a/examples/stm32f4/src/bin/usb_hid_mouse.rs b/examples/stm32f4/src/bin/usb_hid_mouse.rs index d4725d597..0bc236119 100644 --- a/examples/stm32f4/src/bin/usb_hid_mouse.rs +++ b/examples/stm32f4/src/bin/usb_hid_mouse.rs @@ -64,7 +64,6 @@ async fn main(_spawner: Spawner) { // Create embassy-usb DeviceBuilder using the driver and config. // It needs some buffers for building the descriptors. - let mut device_descriptor = [0; 256]; let mut config_descriptor = [0; 256]; let mut bos_descriptor = [0; 256]; let mut control_buf = [0; 64]; @@ -76,7 +75,6 @@ async fn main(_spawner: Spawner) { let mut builder = Builder::new( driver, config, - &mut device_descriptor, &mut config_descriptor, &mut bos_descriptor, &mut [], // no msos descriptors diff --git a/examples/stm32f4/src/bin/usb_raw.rs b/examples/stm32f4/src/bin/usb_raw.rs index 15a98ff8b..4e583aeb8 100644 --- a/examples/stm32f4/src/bin/usb_raw.rs +++ b/examples/stm32f4/src/bin/usb_raw.rs @@ -117,7 +117,6 @@ async fn main(_spawner: Spawner) { // Create embassy-usb DeviceBuilder using the driver and config. // It needs some buffers for building the descriptors. - let mut device_descriptor = [0; 256]; let mut config_descriptor = [0; 256]; let mut bos_descriptor = [0; 256]; let mut msos_descriptor = [0; 256]; @@ -130,7 +129,6 @@ async fn main(_spawner: Spawner) { let mut builder = Builder::new( driver, config, - &mut device_descriptor, &mut config_descriptor, &mut bos_descriptor, &mut msos_descriptor, diff --git a/examples/stm32f4/src/bin/usb_serial.rs b/examples/stm32f4/src/bin/usb_serial.rs index 6080b34a8..f3a375d31 100644 --- a/examples/stm32f4/src/bin/usb_serial.rs +++ b/examples/stm32f4/src/bin/usb_serial.rs @@ -64,7 +64,6 @@ async fn main(_spawner: Spawner) { // Create embassy-usb DeviceBuilder using the driver and config. // It needs some buffers for building the descriptors. - let mut device_descriptor = [0; 256]; let mut config_descriptor = [0; 256]; let mut bos_descriptor = [0; 256]; let mut control_buf = [0; 64]; @@ -74,7 +73,6 @@ async fn main(_spawner: Spawner) { let mut builder = Builder::new( driver, config, - &mut device_descriptor, &mut config_descriptor, &mut bos_descriptor, &mut [], // no msos descriptors diff --git a/examples/stm32f7/src/bin/usb_serial.rs b/examples/stm32f7/src/bin/usb_serial.rs index 26ecf3bc8..39a5512f4 100644 --- a/examples/stm32f7/src/bin/usb_serial.rs +++ b/examples/stm32f7/src/bin/usb_serial.rs @@ -64,7 +64,6 @@ async fn main(_spawner: Spawner) { // Create embassy-usb DeviceBuilder using the driver and config. // It needs some buffers for building the descriptors. - let mut device_descriptor = [0; 256]; let mut config_descriptor = [0; 256]; let mut bos_descriptor = [0; 256]; let mut control_buf = [0; 64]; @@ -74,7 +73,6 @@ async fn main(_spawner: Spawner) { let mut builder = Builder::new( driver, config, - &mut device_descriptor, &mut config_descriptor, &mut bos_descriptor, &mut [], // no msos descriptors diff --git a/examples/stm32g0/src/bin/usb_serial.rs b/examples/stm32g0/src/bin/usb_serial.rs index 8b9915626..162dfd86b 100644 --- a/examples/stm32g0/src/bin/usb_serial.rs +++ b/examples/stm32g0/src/bin/usb_serial.rs @@ -36,7 +36,6 @@ async fn main(_spawner: Spawner) { // Create embassy-usb DeviceBuilder using the driver and config. // It needs some buffers for building the descriptors. - let mut device_descriptor = [0; 256]; let mut config_descriptor = [0; 256]; let mut bos_descriptor = [0; 256]; let mut control_buf = [0; 7]; @@ -46,7 +45,6 @@ async fn main(_spawner: Spawner) { let mut builder = Builder::new( driver, config, - &mut device_descriptor, &mut config_descriptor, &mut bos_descriptor, &mut [], // no msos descriptors diff --git a/examples/stm32g4/src/bin/usb_serial.rs b/examples/stm32g4/src/bin/usb_serial.rs index dc95aa6e5..dbe8f27c1 100644 --- a/examples/stm32g4/src/bin/usb_serial.rs +++ b/examples/stm32g4/src/bin/usb_serial.rs @@ -56,7 +56,6 @@ async fn main(_spawner: Spawner) { config.device_protocol = 0x01; config.composite_with_iads = true; - let mut device_descriptor = [0; 256]; let mut config_descriptor = [0; 256]; let mut bos_descriptor = [0; 256]; let mut control_buf = [0; 64]; @@ -66,7 +65,6 @@ async fn main(_spawner: Spawner) { let mut builder = Builder::new( driver, config, - &mut device_descriptor, &mut config_descriptor, &mut bos_descriptor, &mut [], // no msos descriptors diff --git a/examples/stm32h5/src/bin/usb_serial.rs b/examples/stm32h5/src/bin/usb_serial.rs index 83477c8fa..4f86bb342 100644 --- a/examples/stm32h5/src/bin/usb_serial.rs +++ b/examples/stm32h5/src/bin/usb_serial.rs @@ -65,7 +65,6 @@ async fn main(_spawner: Spawner) { // Create embassy-usb DeviceBuilder using the driver and config. // It needs some buffers for building the descriptors. - let mut device_descriptor = [0; 256]; let mut config_descriptor = [0; 256]; let mut bos_descriptor = [0; 256]; let mut control_buf = [0; 64]; @@ -75,7 +74,6 @@ async fn main(_spawner: Spawner) { let mut builder = Builder::new( driver, config, - &mut device_descriptor, &mut config_descriptor, &mut bos_descriptor, &mut [], // no msos descriptors diff --git a/examples/stm32h7/src/bin/usb_serial.rs b/examples/stm32h7/src/bin/usb_serial.rs index c3ddda72a..576506ad3 100644 --- a/examples/stm32h7/src/bin/usb_serial.rs +++ b/examples/stm32h7/src/bin/usb_serial.rs @@ -65,7 +65,6 @@ async fn main(_spawner: Spawner) { // Create embassy-usb DeviceBuilder using the driver and config. // It needs some buffers for building the descriptors. - let mut device_descriptor = [0; 256]; let mut config_descriptor = [0; 256]; let mut bos_descriptor = [0; 256]; let mut control_buf = [0; 64]; @@ -75,7 +74,6 @@ async fn main(_spawner: Spawner) { let mut builder = Builder::new( driver, config, - &mut device_descriptor, &mut config_descriptor, &mut bos_descriptor, &mut [], // no msos descriptors diff --git a/examples/stm32l1/src/bin/usb_serial.rs b/examples/stm32l1/src/bin/usb_serial.rs index f738ea358..653bbd6d2 100644 --- a/examples/stm32l1/src/bin/usb_serial.rs +++ b/examples/stm32l1/src/bin/usb_serial.rs @@ -46,7 +46,6 @@ async fn main(_spawner: Spawner) { config.device_protocol = 0x01; config.composite_with_iads = true; - let mut device_descriptor = [0; 256]; let mut config_descriptor = [0; 256]; let mut bos_descriptor = [0; 256]; let mut control_buf = [0; 64]; @@ -56,7 +55,6 @@ async fn main(_spawner: Spawner) { let mut builder = Builder::new( driver, config, - &mut device_descriptor, &mut config_descriptor, &mut bos_descriptor, &mut [], // no msos descriptors diff --git a/examples/stm32l4/src/bin/usb_serial.rs b/examples/stm32l4/src/bin/usb_serial.rs index 047234d60..198504b59 100644 --- a/examples/stm32l4/src/bin/usb_serial.rs +++ b/examples/stm32l4/src/bin/usb_serial.rs @@ -60,7 +60,6 @@ async fn main(_spawner: Spawner) { // Create embassy-usb DeviceBuilder using the driver and config. // It needs some buffers for building the descriptors. - let mut device_descriptor = [0; 256]; let mut config_descriptor = [0; 256]; let mut bos_descriptor = [0; 256]; let mut control_buf = [0; 64]; @@ -70,7 +69,6 @@ async fn main(_spawner: Spawner) { let mut builder = Builder::new( driver, config, - &mut device_descriptor, &mut config_descriptor, &mut bos_descriptor, &mut [], // no msos descriptors diff --git a/examples/stm32l5/src/bin/usb_ethernet.rs b/examples/stm32l5/src/bin/usb_ethernet.rs index dc1e7022d..7f73fd677 100644 --- a/examples/stm32l5/src/bin/usb_ethernet.rs +++ b/examples/stm32l5/src/bin/usb_ethernet.rs @@ -79,14 +79,12 @@ async fn main(spawner: Spawner) { config.device_protocol = 0x01; // Create embassy-usb DeviceBuilder using the driver and config. - static DEVICE_DESC: StaticCell<[u8; 256]> = StaticCell::new(); static CONFIG_DESC: StaticCell<[u8; 256]> = StaticCell::new(); static BOS_DESC: StaticCell<[u8; 256]> = StaticCell::new(); static CONTROL_BUF: StaticCell<[u8; 128]> = StaticCell::new(); let mut builder = Builder::new( driver, config, - &mut DEVICE_DESC.init([0; 256])[..], &mut CONFIG_DESC.init([0; 256])[..], &mut BOS_DESC.init([0; 256])[..], &mut [], // no msos descriptors diff --git a/examples/stm32l5/src/bin/usb_hid_mouse.rs b/examples/stm32l5/src/bin/usb_hid_mouse.rs index b86fba455..9d30205bb 100644 --- a/examples/stm32l5/src/bin/usb_hid_mouse.rs +++ b/examples/stm32l5/src/bin/usb_hid_mouse.rs @@ -51,7 +51,6 @@ async fn main(_spawner: Spawner) { // Create embassy-usb DeviceBuilder using the driver and config. // It needs some buffers for building the descriptors. - let mut device_descriptor = [0; 256]; let mut config_descriptor = [0; 256]; let mut bos_descriptor = [0; 256]; let mut control_buf = [0; 64]; @@ -62,7 +61,6 @@ async fn main(_spawner: Spawner) { let mut builder = Builder::new( driver, config, - &mut device_descriptor, &mut config_descriptor, &mut bos_descriptor, &mut [], // no msos descriptors diff --git a/examples/stm32l5/src/bin/usb_serial.rs b/examples/stm32l5/src/bin/usb_serial.rs index 5e2378b58..a64bda31b 100644 --- a/examples/stm32l5/src/bin/usb_serial.rs +++ b/examples/stm32l5/src/bin/usb_serial.rs @@ -47,7 +47,6 @@ async fn main(_spawner: Spawner) { // Create embassy-usb DeviceBuilder using the driver and config. // It needs some buffers for building the descriptors. - let mut device_descriptor = [0; 256]; let mut config_descriptor = [0; 256]; let mut bos_descriptor = [0; 256]; let mut control_buf = [0; 7]; @@ -57,7 +56,6 @@ async fn main(_spawner: Spawner) { let mut builder = Builder::new( driver, config, - &mut device_descriptor, &mut config_descriptor, &mut bos_descriptor, &mut [], // no msos descriptors diff --git a/examples/stm32u5/src/bin/usb_serial.rs b/examples/stm32u5/src/bin/usb_serial.rs index 33e02ce3b..6a313efb0 100644 --- a/examples/stm32u5/src/bin/usb_serial.rs +++ b/examples/stm32u5/src/bin/usb_serial.rs @@ -61,7 +61,6 @@ async fn main(_spawner: Spawner) { // Create embassy-usb DeviceBuilder using the driver and config. // It needs some buffers for building the descriptors. - let mut device_descriptor = [0; 256]; let mut config_descriptor = [0; 256]; let mut bos_descriptor = [0; 256]; let mut control_buf = [0; 64]; @@ -71,7 +70,6 @@ async fn main(_spawner: Spawner) { let mut builder = Builder::new( driver, config, - &mut device_descriptor, &mut config_descriptor, &mut bos_descriptor, &mut [], // no msos descriptors From 389cbc0a77daea15decae706818f104d89446020 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Sat, 23 Mar 2024 00:35:06 +0100 Subject: [PATCH 24/62] stm32/timer: simplify traits, convert from trait methods to struct. --- ci.sh | 1 + embassy-stm32/src/time_driver.rs | 34 +- embassy-stm32/src/timer/complementary_pwm.rs | 38 +- embassy-stm32/src/timer/low_level.rs | 638 +++++++++++ embassy-stm32/src/timer/mod.rs | 1011 ++--------------- embassy-stm32/src/timer/qei.rs | 33 +- embassy-stm32/src/timer/simple_pwm.rs | 46 +- examples/stm32f4/src/bin/ws2812_pwm.rs | 5 +- examples/stm32h7/src/bin/dac_dma.rs | 26 +- .../stm32h7/src/bin/low_level_timer_api.rs | 34 +- examples/stm32l4/src/bin/dac_dma.rs | 26 +- 11 files changed, 885 insertions(+), 1007 deletions(-) create mode 100644 embassy-stm32/src/timer/low_level.rs diff --git a/ci.sh b/ci.sh index cd82af2f1..d17f4e13e 100755 --- a/ci.sh +++ b/ci.sh @@ -124,6 +124,7 @@ cargo batch \ --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32h755zi-cm7,defmt,exti,time-driver-any,time \ --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32h725re,defmt,exti,time-driver-any,time \ --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32h7b3ai,defmt,exti,time-driver-any,time \ + --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32h7b3ai,defmt,exti,time-driver-tim1,time \ --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32l431cb,defmt,exti,time-driver-any,time \ --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32l476vg,defmt,exti,time-driver-any,time \ --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32l422cb,defmt,exti,time-driver-any,time \ diff --git a/embassy-stm32/src/time_driver.rs b/embassy-stm32/src/time_driver.rs index e78f81dca..c961fc14b 100644 --- a/embassy-stm32/src/time_driver.rs +++ b/embassy-stm32/src/time_driver.rs @@ -8,7 +8,7 @@ use critical_section::CriticalSection; use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex; use embassy_sync::blocking_mutex::Mutex; use embassy_time_driver::{AlarmHandle, Driver, TICK_HZ}; -use stm32_metapac::timer::regs; +use stm32_metapac::timer::{regs, TimGp16}; use crate::interrupt::typelevel::Interrupt; use crate::pac::timer::vals; @@ -16,8 +16,8 @@ use crate::rcc::sealed::RccPeripheral; #[cfg(feature = "low-power")] use crate::rtc::Rtc; #[cfg(any(time_driver_tim1, time_driver_tim8, time_driver_tim20))] -use crate::timer::sealed::AdvancedControlInstance; -use crate::timer::sealed::{CoreInstance, GeneralPurpose16bitInstance as Instance}; +use crate::timer::AdvancedInstance1Channel; +use crate::timer::CoreInstance; use crate::{interrupt, peripherals}; // NOTE regarding ALARM_COUNT: @@ -207,6 +207,10 @@ foreach_interrupt! { }; } +fn regs_gp16() -> TimGp16 { + unsafe { TimGp16::from_ptr(T::regs()) } +} + // Clock timekeeping works with something we call "periods", which are time intervals // of 2^15 ticks. The Clock counter value is 16 bits, so one "overflow cycle" is 2 periods. // @@ -271,7 +275,7 @@ embassy_time_driver::time_driver_impl!(static DRIVER: RtcDriver = RtcDriver { impl RtcDriver { fn init(&'static self, cs: critical_section::CriticalSection) { - let r = T::regs_gp16(); + let r = regs_gp16(); ::enable_and_reset_with_cs(cs); @@ -308,9 +312,9 @@ impl RtcDriver { #[cfg(any(time_driver_tim1, time_driver_tim8, time_driver_tim20))] { - ::CaptureCompareInterrupt::unpend(); + ::CaptureCompareInterrupt::unpend(); unsafe { - ::CaptureCompareInterrupt::enable(); + ::CaptureCompareInterrupt::enable(); } } @@ -318,7 +322,7 @@ impl RtcDriver { } fn on_interrupt(&self) { - let r = T::regs_gp16(); + let r = regs_gp16(); // XXX: reduce the size of this critical section ? critical_section::with(|cs| { @@ -349,7 +353,7 @@ impl RtcDriver { } fn next_period(&self) { - let r = T::regs_gp16(); + let r = regs_gp16(); // We only modify the period from the timer interrupt, so we know this can't race. let period = self.period.load(Ordering::Relaxed) + 1; @@ -413,7 +417,7 @@ impl RtcDriver { /// 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 = T::regs_gp16().cnt().read().cnt() as u32; + let cnt = regs_gp16().cnt().read().cnt() as u32; let period = self.period.load(Ordering::SeqCst); // Correct the race, if it exists @@ -439,7 +443,7 @@ impl RtcDriver { let period = if cnt > u16::MAX as u32 / 2 { period + 1 } else { period }; self.period.store(period, Ordering::SeqCst); - T::regs_gp16().cnt().write(|w| w.set_cnt(cnt as u16)); + regs_gp16().cnt().write(|w| w.set_cnt(cnt as u16)); // Now, recompute all alarms for i in 0..ALARM_COUNT { @@ -496,7 +500,7 @@ impl RtcDriver { .unwrap() .start_wakeup_alarm(time_until_next_alarm, cs); - T::regs_gp16().cr1().modify(|w| w.set_cen(false)); + regs_gp16().cr1().modify(|w| w.set_cen(false)); Ok(()) } @@ -506,7 +510,7 @@ impl RtcDriver { #[cfg(feature = "low-power")] /// Resume the timer with the given offset pub(crate) fn resume_time(&self) { - if T::regs_gp16().cr1().read().cen() { + if regs_gp16().cr1().read().cen() { // Time isn't currently stopped return; @@ -515,14 +519,14 @@ impl RtcDriver { critical_section::with(|cs| { self.stop_wakeup_alarm(cs); - T::regs_gp16().cr1().modify(|w| w.set_cen(true)); + regs_gp16().cr1().modify(|w| w.set_cen(true)); }) } } impl Driver for RtcDriver { fn now(&self) -> u64 { - let r = T::regs_gp16(); + let r = regs_gp16(); let period = self.period.load(Ordering::Relaxed); compiler_fence(Ordering::Acquire); @@ -553,7 +557,7 @@ impl Driver for RtcDriver { fn set_alarm(&self, alarm: AlarmHandle, timestamp: u64) -> bool { critical_section::with(|cs| { - let r = T::regs_gp16(); + let r = regs_gp16(); let n = alarm.id() as usize; let alarm = self.get_alarm(cs, alarm); diff --git a/embassy-stm32/src/timer/complementary_pwm.rs b/embassy-stm32/src/timer/complementary_pwm.rs index 723e8506b..a892646cf 100644 --- a/embassy-stm32/src/timer/complementary_pwm.rs +++ b/embassy-stm32/src/timer/complementary_pwm.rs @@ -5,11 +5,15 @@ use core::marker::PhantomData; use embassy_hal_internal::{into_ref, PeripheralRef}; use stm32_metapac::timer::vals::Ckd; -use super::simple_pwm::*; -use super::*; -#[allow(unused_imports)] -use crate::gpio::sealed::{AFType, Pin}; +use super::low_level::{CountingMode, OutputPolarity, Timer}; +use super::simple_pwm::{Ch1, Ch2, Ch3, Ch4, PwmPin}; +use super::{ + AdvancedInstance4Channel, Channel, Channel1ComplementaryPin, Channel2ComplementaryPin, Channel3ComplementaryPin, + Channel4ComplementaryPin, +}; use crate::gpio::{AnyPin, OutputType}; +use crate::time::Hertz; +use crate::timer::low_level::OutputCompareMode; use crate::Peripheral; /// Complementary PWM pin wrapper. @@ -22,7 +26,7 @@ pub struct ComplementaryPwmPin<'d, T, C> { macro_rules! complementary_channel_impl { ($new_chx:ident, $channel:ident, $pin_trait:ident) => { - impl<'d, T: ComplementaryCaptureCompare16bitInstance> ComplementaryPwmPin<'d, T, $channel> { + impl<'d, T: AdvancedInstance4Channel> ComplementaryPwmPin<'d, T, $channel> { #[doc = concat!("Create a new ", stringify!($channel), " complementary PWM pin instance.")] pub fn $new_chx(pin: impl Peripheral

> + 'd, output_type: OutputType) -> Self { into_ref!(pin); @@ -47,11 +51,11 @@ complementary_channel_impl!(new_ch3, Ch3, Channel3ComplementaryPin); complementary_channel_impl!(new_ch4, Ch4, Channel4ComplementaryPin); /// PWM driver with support for standard and complementary outputs. -pub struct ComplementaryPwm<'d, T> { - inner: PeripheralRef<'d, T>, +pub struct ComplementaryPwm<'d, T: AdvancedInstance4Channel> { + inner: Timer<'d, T>, } -impl<'d, T: ComplementaryCaptureCompare16bitInstance> ComplementaryPwm<'d, T> { +impl<'d, T: AdvancedInstance4Channel> ComplementaryPwm<'d, T> { /// Create a new complementary PWM driver. #[allow(clippy::too_many_arguments)] pub fn new( @@ -71,11 +75,7 @@ impl<'d, T: ComplementaryCaptureCompare16bitInstance> ComplementaryPwm<'d, T> { } fn new_inner(tim: impl Peripheral

+ 'd, freq: Hertz, counting_mode: CountingMode) -> Self { - into_ref!(tim); - - T::enable_and_reset(); - - let mut this = Self { inner: tim }; + let mut this = Self { inner: Timer::new(tim) }; this.inner.set_counting_mode(counting_mode); this.set_frequency(freq); @@ -122,7 +122,7 @@ impl<'d, T: ComplementaryCaptureCompare16bitInstance> ComplementaryPwm<'d, T> { /// /// This value depends on the configured frequency and the timer's clock rate from RCC. pub fn get_max_duty(&self) -> u16 { - self.inner.get_max_compare_value() + 1 + self.inner.get_max_compare_value() as u16 + 1 } /// Set the duty for a given channel. @@ -130,7 +130,7 @@ impl<'d, T: ComplementaryCaptureCompare16bitInstance> ComplementaryPwm<'d, T> { /// The value ranges from 0 for 0% duty, to [`get_max_duty`](Self::get_max_duty) for 100% duty, both included. pub fn set_duty(&mut self, channel: Channel, duty: u16) { assert!(duty <= self.get_max_duty()); - self.inner.set_compare_value(channel, duty) + self.inner.set_compare_value(channel, duty as _) } /// Set the output polarity for a given channel. @@ -148,7 +148,7 @@ impl<'d, T: ComplementaryCaptureCompare16bitInstance> ComplementaryPwm<'d, T> { } } -impl<'d, T: ComplementaryCaptureCompare16bitInstance> embedded_hal_02::Pwm for ComplementaryPwm<'d, T> { +impl<'d, T: AdvancedInstance4Channel> embedded_hal_02::Pwm for ComplementaryPwm<'d, T> { type Channel = Channel; type Time = Hertz; type Duty = u16; @@ -168,16 +168,16 @@ impl<'d, T: ComplementaryCaptureCompare16bitInstance> embedded_hal_02::Pwm for C } fn get_duty(&self, channel: Self::Channel) -> Self::Duty { - self.inner.get_compare_value(channel) + self.inner.get_compare_value(channel) as u16 } fn get_max_duty(&self) -> Self::Duty { - self.inner.get_max_compare_value() + 1 + self.inner.get_max_compare_value() as u16 + 1 } fn set_duty(&mut self, channel: Self::Channel, duty: Self::Duty) { assert!(duty <= self.get_max_duty()); - self.inner.set_compare_value(channel, duty) + self.inner.set_compare_value(channel, duty as u32) } fn set_period

(&mut self, period: P) diff --git a/embassy-stm32/src/timer/low_level.rs b/embassy-stm32/src/timer/low_level.rs new file mode 100644 index 000000000..a5d942314 --- /dev/null +++ b/embassy-stm32/src/timer/low_level.rs @@ -0,0 +1,638 @@ +//! Low-level timer driver. +//! +//! This is an unopinionated, very low-level driver for all STM32 timers. It allows direct register +//! manipulation with the `regs_*()` methods, and has utility functions that are thin wrappers +//! over the registers. +//! +//! The available functionality depends on the timer type. + +use embassy_hal_internal::{into_ref, Peripheral, PeripheralRef}; + +use super::*; +use crate::pac::timer::vals; +use crate::time::Hertz; + +/// Input capture mode. +#[derive(Clone, Copy)] +pub enum InputCaptureMode { + /// Rising edge only. + Rising, + /// Falling edge only. + Falling, + /// Both rising or falling edges. + BothEdges, +} + +/// Input TI selection. +#[derive(Clone, Copy)] +pub enum InputTISelection { + /// Normal + Normal, + /// Alternate + Alternate, + /// TRC + TRC, +} + +impl From for stm32_metapac::timer::vals::CcmrInputCcs { + fn from(tisel: InputTISelection) -> Self { + match tisel { + InputTISelection::Normal => stm32_metapac::timer::vals::CcmrInputCcs::TI4, + InputTISelection::Alternate => stm32_metapac::timer::vals::CcmrInputCcs::TI3, + InputTISelection::TRC => stm32_metapac::timer::vals::CcmrInputCcs::TRC, + } + } +} + +/// Timer counting mode. +#[repr(u8)] +#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)] +pub enum CountingMode { + #[default] + /// The timer counts up to the reload value and then resets back to 0. + EdgeAlignedUp, + /// The timer counts down to 0 and then resets back to the reload value. + EdgeAlignedDown, + /// The timer counts up to the reload value and then counts back to 0. + /// + /// The output compare interrupt flags of channels configured in output are + /// set when the counter is counting down. + CenterAlignedDownInterrupts, + /// The timer counts up to the reload value and then counts back to 0. + /// + /// The output compare interrupt flags of channels configured in output are + /// set when the counter is counting up. + CenterAlignedUpInterrupts, + /// The timer counts up to the reload value and then counts back to 0. + /// + /// The output compare interrupt flags of channels configured in output are + /// set when the counter is counting both up or down. + CenterAlignedBothInterrupts, +} + +impl CountingMode { + /// Return whether this mode is edge-aligned (up or down). + pub fn is_edge_aligned(&self) -> bool { + matches!(self, CountingMode::EdgeAlignedUp | CountingMode::EdgeAlignedDown) + } + + /// Return whether this mode is center-aligned. + pub fn is_center_aligned(&self) -> bool { + matches!( + self, + CountingMode::CenterAlignedDownInterrupts + | CountingMode::CenterAlignedUpInterrupts + | CountingMode::CenterAlignedBothInterrupts + ) + } +} + +impl From for (vals::Cms, vals::Dir) { + fn from(value: CountingMode) -> Self { + match value { + CountingMode::EdgeAlignedUp => (vals::Cms::EDGEALIGNED, vals::Dir::UP), + CountingMode::EdgeAlignedDown => (vals::Cms::EDGEALIGNED, vals::Dir::DOWN), + CountingMode::CenterAlignedDownInterrupts => (vals::Cms::CENTERALIGNED1, vals::Dir::UP), + CountingMode::CenterAlignedUpInterrupts => (vals::Cms::CENTERALIGNED2, vals::Dir::UP), + CountingMode::CenterAlignedBothInterrupts => (vals::Cms::CENTERALIGNED3, vals::Dir::UP), + } + } +} + +impl From<(vals::Cms, vals::Dir)> for CountingMode { + fn from(value: (vals::Cms, vals::Dir)) -> Self { + match value { + (vals::Cms::EDGEALIGNED, vals::Dir::UP) => CountingMode::EdgeAlignedUp, + (vals::Cms::EDGEALIGNED, vals::Dir::DOWN) => CountingMode::EdgeAlignedDown, + (vals::Cms::CENTERALIGNED1, _) => CountingMode::CenterAlignedDownInterrupts, + (vals::Cms::CENTERALIGNED2, _) => CountingMode::CenterAlignedUpInterrupts, + (vals::Cms::CENTERALIGNED3, _) => CountingMode::CenterAlignedBothInterrupts, + } + } +} + +/// Output compare mode. +#[derive(Clone, Copy)] +pub enum OutputCompareMode { + /// The comparison between the output compare register TIMx_CCRx and + /// the counter TIMx_CNT has no effect on the outputs. + /// (this mode is used to generate a timing base). + Frozen, + /// Set channel to active level on match. OCxREF signal is forced high when the + /// counter TIMx_CNT matches the capture/compare register x (TIMx_CCRx). + ActiveOnMatch, + /// Set channel to inactive level on match. OCxREF signal is forced low when the + /// counter TIMx_CNT matches the capture/compare register x (TIMx_CCRx). + InactiveOnMatch, + /// Toggle - OCxREF toggles when TIMx_CNT=TIMx_CCRx. + Toggle, + /// Force inactive level - OCxREF is forced low. + ForceInactive, + /// Force active level - OCxREF is forced high. + ForceActive, + /// PWM mode 1 - In upcounting, channel is active as long as TIMx_CNTTIMx_CCRx else active (OCxREF=1). + PwmMode1, + /// PWM mode 2 - In upcounting, channel is inactive as long as + /// TIMx_CNTTIMx_CCRx else inactive. + PwmMode2, + // TODO: there's more modes here depending on the chip family. +} + +impl From for stm32_metapac::timer::vals::Ocm { + fn from(mode: OutputCompareMode) -> Self { + match mode { + OutputCompareMode::Frozen => stm32_metapac::timer::vals::Ocm::FROZEN, + OutputCompareMode::ActiveOnMatch => stm32_metapac::timer::vals::Ocm::ACTIVEONMATCH, + OutputCompareMode::InactiveOnMatch => stm32_metapac::timer::vals::Ocm::INACTIVEONMATCH, + OutputCompareMode::Toggle => stm32_metapac::timer::vals::Ocm::TOGGLE, + OutputCompareMode::ForceInactive => stm32_metapac::timer::vals::Ocm::FORCEINACTIVE, + OutputCompareMode::ForceActive => stm32_metapac::timer::vals::Ocm::FORCEACTIVE, + OutputCompareMode::PwmMode1 => stm32_metapac::timer::vals::Ocm::PWMMODE1, + OutputCompareMode::PwmMode2 => stm32_metapac::timer::vals::Ocm::PWMMODE2, + } + } +} + +/// Timer output pin polarity. +#[derive(Clone, Copy)] +pub enum OutputPolarity { + /// Active high (higher duty value makes the pin spend more time high). + ActiveHigh, + /// Active low (higher duty value makes the pin spend more time low). + ActiveLow, +} + +impl From for bool { + fn from(mode: OutputPolarity) -> Self { + match mode { + OutputPolarity::ActiveHigh => false, + OutputPolarity::ActiveLow => true, + } + } +} + +/// Low-level timer driver. +pub struct Timer<'d, T: CoreInstance> { + tim: PeripheralRef<'d, T>, +} + +impl<'d, T: CoreInstance> Drop for Timer<'d, T> { + fn drop(&mut self) { + T::disable() + } +} + +impl<'d, T: CoreInstance> Timer<'d, T> { + /// Create a new timer driver. + pub fn new(tim: impl Peripheral

+ 'd) -> Self { + into_ref!(tim); + + T::enable_and_reset(); + + Self { tim } + } + + /// Get access to the virutal core 16bit timer registers. + /// + /// Note: This works even if the timer is more capable, because registers + /// for the less capable timers are a subset. This allows writing a driver + /// for a given set of capabilities, and having it transparently work with + /// more capable timers. + pub fn regs_core(&self) -> crate::pac::timer::TimCore { + unsafe { crate::pac::timer::TimCore::from_ptr(T::regs()) } + } + + #[cfg(not(stm32l0))] + fn regs_gp32_unchecked(&self) -> crate::pac::timer::TimGp32 { + unsafe { crate::pac::timer::TimGp32::from_ptr(T::regs()) } + } + + /// Start the timer. + pub fn start(&self) { + self.regs_core().cr1().modify(|r| r.set_cen(true)); + } + + /// Stop the timer. + pub fn stop(&self) { + self.regs_core().cr1().modify(|r| r.set_cen(false)); + } + + /// Reset the counter value to 0 + pub fn reset(&self) { + self.regs_core().cnt().write(|r| r.set_cnt(0)); + } + + /// Set the frequency of how many times per second the timer counts up to the max value or down to 0. + /// + /// This means that in the default edge-aligned mode, + /// 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) { + let f = frequency.0; + assert!(f > 0); + let timer_f = T::frequency().0; + + match T::BITS { + TimerBits::Bits16 => { + let pclk_ticks_per_timer_period = timer_f / f; + let psc: u16 = unwrap!(((pclk_ticks_per_timer_period - 1) / (1 << 16)).try_into()); + let divide_by = pclk_ticks_per_timer_period / (u32::from(psc) + 1); + + // the timer counts `0..=arr`, we want it to count `0..divide_by` + let arr = unwrap!(u16::try_from(divide_by - 1)); + + 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::COUNTERONLY)); + regs.egr().write(|r| r.set_ug(true)); + regs.cr1().modify(|r| r.set_urs(vals::Urs::ANYEVENT)); + } + #[cfg(not(stm32l0))] + TimerBits::Bits32 => { + let pclk_ticks_per_timer_period = (timer_f / f) as u64; + let psc: u16 = unwrap!(((pclk_ticks_per_timer_period - 1) / (1 << 32)).try_into()); + let arr: u32 = unwrap!((pclk_ticks_per_timer_period / (psc as u64 + 1)).try_into()); + + 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::COUNTERONLY)); + regs.egr().write(|r| r.set_ug(true)); + regs.cr1().modify(|r| r.set_urs(vals::Urs::ANYEVENT)); + } + } + } + + /// Clear update interrupt. + /// + /// Returns whether the update interrupt flag was set. + pub fn clear_update_interrupt(&self) -> bool { + let regs = self.regs_core(); + let sr = regs.sr().read(); + if sr.uif() { + regs.sr().modify(|r| { + r.set_uif(false); + }); + true + } else { + false + } + } + + /// Enable/disable the update interrupt. + pub fn enable_update_interrupt(&self, enable: bool) { + self.regs_core().dier().modify(|r| r.set_uie(enable)); + } + + /// Enable/disable autoreload preload. + pub fn set_autoreload_preload(&self, enable: bool) { + self.regs_core().cr1().modify(|r| r.set_arpe(enable)); + } + + /// Get the timer frequency. + pub fn get_frequency(&self) -> Hertz { + let timer_f = T::frequency(); + + match T::BITS { + TimerBits::Bits16 => { + let regs = self.regs_core(); + let arr = regs.arr().read().arr(); + let psc = regs.psc().read(); + + timer_f / arr / (psc + 1) + } + #[cfg(not(stm32l0))] + TimerBits::Bits32 => { + let regs = self.regs_gp32_unchecked(); + let arr = regs.arr().read(); + let psc = regs.psc().read(); + + timer_f / arr / (psc + 1) + } + } + } +} + +impl<'d, T: BasicNoCr2Instance> Timer<'d, T> { + /// Get access to the Baisc 16bit timer registers. + /// + /// Note: This works even if the timer is more capable, because registers + /// for the less capable timers are a subset. This allows writing a driver + /// for a given set of capabilities, and having it transparently work with + /// more capable timers. + pub fn regs_basic_no_cr2(&self) -> crate::pac::timer::TimBasicNoCr2 { + unsafe { crate::pac::timer::TimBasicNoCr2::from_ptr(T::regs()) } + } + + /// Enable/disable the update dma. + pub fn enable_update_dma(&self, enable: bool) { + self.regs_basic_no_cr2().dier().modify(|r| r.set_ude(enable)); + } + + /// Get the update dma enable/disable state. + pub fn get_update_dma_state(&self) -> bool { + self.regs_basic_no_cr2().dier().read().ude() + } +} + +impl<'d, T: BasicInstance> Timer<'d, T> { + /// Get access to the Baisc 16bit timer registers. + /// + /// Note: This works even if the timer is more capable, because registers + /// for the less capable timers are a subset. This allows writing a driver + /// for a given set of capabilities, and having it transparently work with + /// more capable timers. + pub fn regs_basic(&self) -> crate::pac::timer::TimBasic { + unsafe { crate::pac::timer::TimBasic::from_ptr(T::regs()) } + } +} + +impl<'d, T: GeneralInstance1Channel> Timer<'d, T> { + /// Get access to the general purpose 1 channel 16bit timer registers. + /// + /// Note: This works even if the timer is more capable, because registers + /// for the less capable timers are a subset. This allows writing a driver + /// for a given set of capabilities, and having it transparently work with + /// more capable timers. + pub fn regs_1ch(&self) -> crate::pac::timer::Tim1ch { + unsafe { crate::pac::timer::Tim1ch::from_ptr(T::regs()) } + } + + /// Set clock divider. + pub fn set_clock_division(&self, ckd: vals::Ckd) { + self.regs_1ch().cr1().modify(|r| r.set_ckd(ckd)); + } + + /// Get max compare value. This depends on the timer frequency and the clock frequency from RCC. + pub fn get_max_compare_value(&self) -> u32 { + match T::BITS { + TimerBits::Bits16 => self.regs_1ch().arr().read().arr() as u32, + #[cfg(not(stm32l0))] + TimerBits::Bits32 => self.regs_gp32_unchecked().arr().read(), + } + } +} + +impl<'d, T: GeneralInstance2Channel> Timer<'d, T> { + /// Get access to the general purpose 2 channel 16bit timer registers. + /// + /// Note: This works even if the timer is more capable, because registers + /// for the less capable timers are a subset. This allows writing a driver + /// for a given set of capabilities, and having it transparently work with + /// more capable timers. + pub fn regs_2ch(&self) -> crate::pac::timer::Tim2ch { + unsafe { crate::pac::timer::Tim2ch::from_ptr(T::regs()) } + } +} + +impl<'d, T: GeneralInstance4Channel> Timer<'d, T> { + /// Get access to the general purpose 16bit timer registers. + /// + /// Note: This works even if the timer is more capable, because registers + /// for the less capable timers are a subset. This allows writing a driver + /// for a given set of capabilities, and having it transparently work with + /// more capable timers. + pub fn regs_gp16(&self) -> crate::pac::timer::TimGp16 { + unsafe { crate::pac::timer::TimGp16::from_ptr(T::regs()) } + } + + /// Enable timer outputs. + pub fn enable_outputs(&self) { + self.tim.enable_outputs() + } + + /// Set counting mode. + pub fn set_counting_mode(&self, mode: CountingMode) { + let (cms, dir) = mode.into(); + + let timer_enabled = self.regs_core().cr1().read().cen(); + // Changing from edge aligned to center aligned (and vice versa) is not allowed while the timer is running. + // Changing direction is discouraged while the timer is running. + assert!(!timer_enabled); + + self.regs_gp16().cr1().modify(|r| r.set_dir(dir)); + self.regs_gp16().cr1().modify(|r| r.set_cms(cms)) + } + + /// Get counting mode. + pub fn get_counting_mode(&self) -> CountingMode { + let cr1 = self.regs_gp16().cr1().read(); + (cr1.cms(), cr1.dir()).into() + } + + /// Set input capture filter. + pub fn set_input_capture_filter(&self, channel: Channel, icf: vals::FilterValue) { + let raw_channel = channel.index(); + self.regs_gp16() + .ccmr_input(raw_channel / 2) + .modify(|r| r.set_icf(raw_channel % 2, icf)); + } + + /// Clear input interrupt. + pub fn clear_input_interrupt(&self, channel: Channel) { + self.regs_gp16().sr().modify(|r| r.set_ccif(channel.index(), false)); + } + + /// Enable input interrupt. + pub fn enable_input_interrupt(&self, channel: Channel, enable: bool) { + self.regs_gp16().dier().modify(|r| r.set_ccie(channel.index(), enable)); + } + + /// Set input capture prescaler. + pub fn set_input_capture_prescaler(&self, channel: Channel, factor: u8) { + let raw_channel = channel.index(); + self.regs_gp16() + .ccmr_input(raw_channel / 2) + .modify(|r| r.set_icpsc(raw_channel % 2, factor)); + } + + /// Set input TI selection. + pub fn set_input_ti_selection(&self, channel: Channel, tisel: InputTISelection) { + let raw_channel = channel.index(); + self.regs_gp16() + .ccmr_input(raw_channel / 2) + .modify(|r| r.set_ccs(raw_channel % 2, tisel.into())); + } + + /// Set input capture mode. + pub fn set_input_capture_mode(&self, channel: Channel, mode: InputCaptureMode) { + self.regs_gp16().ccer().modify(|r| match mode { + InputCaptureMode::Rising => { + r.set_ccnp(channel.index(), false); + r.set_ccp(channel.index(), false); + } + InputCaptureMode::Falling => { + r.set_ccnp(channel.index(), false); + r.set_ccp(channel.index(), true); + } + InputCaptureMode::BothEdges => { + r.set_ccnp(channel.index(), true); + r.set_ccp(channel.index(), true); + } + }); + } + + /// Set output compare mode. + pub fn set_output_compare_mode(&self, channel: Channel, mode: OutputCompareMode) { + let raw_channel: usize = channel.index(); + self.regs_gp16() + .ccmr_output(raw_channel / 2) + .modify(|w| w.set_ocm(raw_channel % 2, mode.into())); + } + + /// Set output polarity. + pub fn set_output_polarity(&self, channel: Channel, polarity: OutputPolarity) { + self.regs_gp16() + .ccer() + .modify(|w| w.set_ccp(channel.index(), polarity.into())); + } + + /// Enable/disable a channel. + pub fn enable_channel(&self, channel: Channel, enable: bool) { + self.regs_gp16().ccer().modify(|w| w.set_cce(channel.index(), enable)); + } + + /// Get enable/disable state of a channel + pub fn get_channel_enable_state(&self, channel: Channel) -> bool { + self.regs_gp16().ccer().read().cce(channel.index()) + } + + /// Set compare value for a channel. + pub fn set_compare_value(&self, channel: Channel, value: u32) { + match T::BITS { + TimerBits::Bits16 => { + let value = unwrap!(u16::try_from(value)); + self.regs_gp16().ccr(channel.index()).modify(|w| w.set_ccr(value)); + } + #[cfg(not(stm32l0))] + TimerBits::Bits32 => { + self.regs_gp32_unchecked().ccr(channel.index()).write_value(value); + } + } + } + + /// Get compare value for a channel. + pub fn get_compare_value(&self, channel: Channel) -> u32 { + match T::BITS { + TimerBits::Bits16 => self.regs_gp16().ccr(channel.index()).read().ccr() as u32, + #[cfg(not(stm32l0))] + TimerBits::Bits32 => self.regs_gp32_unchecked().ccr(channel.index()).read(), + } + } + + /// Get capture value for a channel. + pub fn get_capture_value(&self, channel: Channel) -> u32 { + self.get_compare_value(channel) + } + + /// Set output compare preload. + pub fn set_output_compare_preload(&self, channel: Channel, preload: bool) { + let channel_index = channel.index(); + self.regs_gp16() + .ccmr_output(channel_index / 2) + .modify(|w| w.set_ocpe(channel_index % 2, preload)); + } + + /// Get capture compare DMA selection + pub fn get_cc_dma_selection(&self) -> vals::Ccds { + self.regs_gp16().cr2().read().ccds() + } + + /// Set capture compare DMA selection + pub fn set_cc_dma_selection(&self, ccds: vals::Ccds) { + self.regs_gp16().cr2().modify(|w| w.set_ccds(ccds)) + } + + /// Get capture compare DMA enable state + pub fn get_cc_dma_enable_state(&self, channel: Channel) -> bool { + self.regs_gp16().dier().read().ccde(channel.index()) + } + + /// Set capture compare DMA enable state + pub fn set_cc_dma_enable_state(&self, channel: Channel, ccde: bool) { + self.regs_gp16().dier().modify(|w| w.set_ccde(channel.index(), ccde)) + } +} + +#[cfg(not(stm32l0))] +impl<'d, T: GeneralInstance32bit4Channel> Timer<'d, T> { + /// Get access to the general purpose 32bit timer registers. + /// + /// Note: This works even if the timer is more capable, because registers + /// for the less capable timers are a subset. This allows writing a driver + /// for a given set of capabilities, and having it transparently work with + /// more capable timers. + pub fn regs_gp32(&self) -> crate::pac::timer::TimGp32 { + unsafe { crate::pac::timer::TimGp32::from_ptr(T::regs()) } + } +} + +#[cfg(not(stm32l0))] +impl<'d, T: AdvancedInstance1Channel> Timer<'d, T> { + /// Get access to the general purpose 1 channel with one complementary 16bit timer registers. + /// + /// Note: This works even if the timer is more capable, because registers + /// for the less capable timers are a subset. This allows writing a driver + /// for a given set of capabilities, and having it transparently work with + /// more capable timers. + pub fn regs_1ch_cmp(&self) -> crate::pac::timer::Tim1chCmp { + unsafe { crate::pac::timer::Tim1chCmp::from_ptr(T::regs()) } + } + + /// Set clock divider for the dead time. + pub fn set_dead_time_clock_division(&self, value: vals::Ckd) { + self.regs_1ch_cmp().cr1().modify(|w| w.set_ckd(value)); + } + + /// Set dead time, as a fraction of the max duty value. + pub fn set_dead_time_value(&self, value: u8) { + self.regs_1ch_cmp().bdtr().modify(|w| w.set_dtg(value)); + } + + /// Set state of MOE-bit in BDTR register to en-/disable output + pub fn set_moe(&self, enable: bool) { + self.regs_1ch_cmp().bdtr().modify(|w| w.set_moe(enable)); + } +} + +#[cfg(not(stm32l0))] +impl<'d, T: AdvancedInstance2Channel> Timer<'d, T> { + /// Get access to the general purpose 2 channel with one complementary 16bit timer registers. + /// + /// Note: This works even if the timer is more capable, because registers + /// for the less capable timers are a subset. This allows writing a driver + /// for a given set of capabilities, and having it transparently work with + /// more capable timers. + pub fn regs_2ch_cmp(&self) -> crate::pac::timer::Tim2chCmp { + unsafe { crate::pac::timer::Tim2chCmp::from_ptr(T::regs()) } + } +} + +#[cfg(not(stm32l0))] +impl<'d, T: AdvancedInstance4Channel> Timer<'d, T> { + /// Get access to the advanced timer registers. + pub fn regs_advanced(&self) -> crate::pac::timer::TimAdv { + unsafe { crate::pac::timer::TimAdv::from_ptr(T::regs()) } + } + + /// Set complementary output polarity. + pub fn set_complementary_output_polarity(&self, channel: Channel, polarity: OutputPolarity) { + self.regs_advanced() + .ccer() + .modify(|w| w.set_ccnp(channel.index(), polarity.into())); + } + + /// Enable/disable a complementary channel. + pub fn enable_complementary_channel(&self, channel: Channel, enable: bool) { + self.regs_advanced() + .ccer() + .modify(|w| w.set_ccne(channel.index(), enable)); + } +} diff --git a/embassy-stm32/src/timer/mod.rs b/embassy-stm32/src/timer/mod.rs index e5e84c255..2ba6b3f11 100644 --- a/embassy-stm32/src/timer/mod.rs +++ b/embassy-stm32/src/timer/mod.rs @@ -1,490 +1,13 @@ //! Timers, PWM, quadrature decoder. -//! - -//! Timer inheritance -//! - -// sealed: -// -// Core -------------------------> 1CH -------------------------> 1CH_CMP -// | | ^ | -// +--> Basic_NoCr2 --> Basic +--> 2CH --> GP16 --> GP32 | +--> 2CH_CMP --> ADV -// | | | ^ | | ^ ^ -// | | +------|--|--------------|-----------+ | -// | +--------------------+ +--------------|-----------|---------+ -// | | | | -// | +--------------------------------------|-----------+ -// +----------------------------------------------------+ - -//! ```text -//! BasicInstance --> CaptureCompare16bitInstance --+--> ComplementaryCaptureCompare16bitInstance -//! | -//! +--> CaptureCompare32bitInstance -//! ``` -//! -//! Mapping: -//! -//! | trait | timer | -//! | :----------------------------------------: | ------------------------------------------------------------------------------------------------- | -//! | [BasicInstance] | Basic Timer | -//! | [CaptureCompare16bitInstance] | 1-channel Timer, 2-channel Timer, General Purpose 16-bit Timer | -//! | [CaptureCompare32bitInstance] | General Purpose 32-bit Timer | -//! | [ComplementaryCaptureCompare16bitInstance] | 1-channel with one complentary Timer, 2-channel with one complentary Timer, Advance Control Timer | #[cfg(not(stm32l0))] pub mod complementary_pwm; +pub mod low_level; pub mod qei; pub mod simple_pwm; -use stm32_metapac::timer::vals; - use crate::interrupt; use crate::rcc::RccPeripheral; -use crate::time::Hertz; - -/// Low-level timer access. -#[cfg(feature = "unstable-pac")] -pub mod low_level { - pub use super::sealed::*; -} - -pub(crate) mod sealed { - use super::*; - - /// Virtual Core 16-bit timer instance. - pub trait CoreInstance: RccPeripheral { - /// Interrupt for this timer. - type Interrupt: interrupt::typelevel::Interrupt; - - /// Get access to the virutal core 16bit timer registers. - /// - /// Note: This works even if the timer is more capable, because registers - /// for the less capable timers are a subset. This allows writing a driver - /// for a given set of capabilities, and having it transparently work with - /// more capable timers. - fn regs_core() -> crate::pac::timer::TimCore; - - /// Start the timer. - fn start(&self) { - Self::regs_core().cr1().modify(|r| r.set_cen(true)); - } - - /// Stop the timer. - fn stop(&self) { - Self::regs_core().cr1().modify(|r| r.set_cen(false)); - } - - /// Reset the counter value to 0 - fn reset(&self) { - Self::regs_core().cnt().write(|r| r.set_cnt(0)); - } - - /// Set the frequency of how many times per second the timer counts up to the max value or down to 0. - /// - /// This means that in the default edge-aligned mode, - /// 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. - fn set_frequency(&self, frequency: Hertz) { - let f = frequency.0; - let timer_f = Self::frequency().0; - assert!(f > 0); - let pclk_ticks_per_timer_period = timer_f / f; - let psc: u16 = unwrap!(((pclk_ticks_per_timer_period - 1) / (1 << 16)).try_into()); - let divide_by = pclk_ticks_per_timer_period / (u32::from(psc) + 1); - - // the timer counts `0..=arr`, we want it to count `0..divide_by` - let arr = unwrap!(u16::try_from(divide_by - 1)); - - 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::COUNTERONLY)); - regs.egr().write(|r| r.set_ug(true)); - regs.cr1().modify(|r| r.set_urs(vals::Urs::ANYEVENT)); - } - - /// Clear update interrupt. - /// - /// Returns whether the update interrupt flag was set. - fn clear_update_interrupt(&self) -> bool { - let regs = Self::regs_core(); - let sr = regs.sr().read(); - if sr.uif() { - regs.sr().modify(|r| { - r.set_uif(false); - }); - true - } else { - false - } - } - - /// Enable/disable the update interrupt. - fn enable_update_interrupt(&self, enable: bool) { - Self::regs_core().dier().modify(|r| r.set_uie(enable)); - } - - /// Enable/disable autoreload preload. - fn set_autoreload_preload(&self, enable: bool) { - Self::regs_core().cr1().modify(|r| r.set_arpe(enable)); - } - - /// Get the timer frequency. - fn get_frequency(&self) -> Hertz { - let timer_f = Self::frequency(); - - let regs = Self::regs_core(); - let arr = regs.arr().read().arr(); - let psc = regs.psc().read(); - - timer_f / arr / (psc + 1) - } - } - - /// Virtual Basic without CR2 16-bit timer instance. - pub trait BasicNoCr2Instance: CoreInstance { - /// Get access to the Baisc 16bit timer registers. - /// - /// Note: This works even if the timer is more capable, because registers - /// for the less capable timers are a subset. This allows writing a driver - /// for a given set of capabilities, and having it transparently work with - /// more capable timers. - fn regs_basic_no_cr2() -> crate::pac::timer::TimBasicNoCr2; - - /// Enable/disable the update dma. - fn enable_update_dma(&self, enable: bool) { - Self::regs_basic_no_cr2().dier().modify(|r| r.set_ude(enable)); - } - - /// Get the update dma enable/disable state. - fn get_update_dma_state(&self) -> bool { - Self::regs_basic_no_cr2().dier().read().ude() - } - } - - /// Basic 16-bit timer instance. - pub trait BasicInstance: BasicNoCr2Instance { - /// Get access to the Baisc 16bit timer registers. - /// - /// Note: This works even if the timer is more capable, because registers - /// for the less capable timers are a subset. This allows writing a driver - /// for a given set of capabilities, and having it transparently work with - /// more capable timers. - fn regs_basic() -> crate::pac::timer::TimBasic; - } - - /// Gneral-purpose 1 channel 16-bit timer instance. - pub trait GeneralPurpose1ChannelInstance: CoreInstance { - /// Get access to the general purpose 1 channel 16bit timer registers. - /// - /// Note: This works even if the timer is more capable, because registers - /// for the less capable timers are a subset. This allows writing a driver - /// for a given set of capabilities, and having it transparently work with - /// more capable timers. - fn regs_1ch() -> crate::pac::timer::Tim1ch; - - /// Set clock divider. - fn set_clock_division(&self, ckd: vals::Ckd) { - Self::regs_1ch().cr1().modify(|r| r.set_ckd(ckd)); - } - - /// Get max compare value. This depends on the timer frequency and the clock frequency from RCC. - fn get_max_compare_value(&self) -> u16 { - Self::regs_1ch().arr().read().arr() - } - } - - /// Gneral-purpose 1 channel 16-bit timer instance. - pub trait GeneralPurpose2ChannelInstance: GeneralPurpose1ChannelInstance { - /// Get access to the general purpose 2 channel 16bit timer registers. - /// - /// Note: This works even if the timer is more capable, because registers - /// for the less capable timers are a subset. This allows writing a driver - /// for a given set of capabilities, and having it transparently work with - /// more capable timers. - fn regs_2ch() -> crate::pac::timer::Tim2ch; - } - - /// Gneral-purpose 16-bit timer instance. - pub trait GeneralPurpose16bitInstance: BasicInstance + GeneralPurpose2ChannelInstance { - /// Get access to the general purpose 16bit timer registers. - /// - /// Note: This works even if the timer is more capable, because registers - /// for the less capable timers are a subset. This allows writing a driver - /// for a given set of capabilities, and having it transparently work with - /// more capable timers. - fn regs_gp16() -> crate::pac::timer::TimGp16; - - /// Set counting mode. - fn set_counting_mode(&self, mode: CountingMode) { - let (cms, dir) = mode.into(); - - let timer_enabled = Self::regs_core().cr1().read().cen(); - // Changing from edge aligned to center aligned (and vice versa) is not allowed while the timer is running. - // Changing direction is discouraged while the timer is running. - assert!(!timer_enabled); - - Self::regs_gp16().cr1().modify(|r| r.set_dir(dir)); - Self::regs_gp16().cr1().modify(|r| r.set_cms(cms)) - } - - /// Get counting mode. - fn get_counting_mode(&self) -> CountingMode { - let cr1 = Self::regs_gp16().cr1().read(); - (cr1.cms(), cr1.dir()).into() - } - - /// Set input capture filter. - fn set_input_capture_filter(&self, channel: Channel, icf: vals::FilterValue) { - let raw_channel = channel.index(); - Self::regs_gp16() - .ccmr_input(raw_channel / 2) - .modify(|r| r.set_icf(raw_channel % 2, icf)); - } - - /// Clear input interrupt. - fn clear_input_interrupt(&self, channel: Channel) { - Self::regs_gp16().sr().modify(|r| r.set_ccif(channel.index(), false)); - } - - /// Enable input interrupt. - fn enable_input_interrupt(&self, channel: Channel, enable: bool) { - Self::regs_gp16().dier().modify(|r| r.set_ccie(channel.index(), enable)); - } - - /// Set input capture prescaler. - fn set_input_capture_prescaler(&self, channel: Channel, factor: u8) { - let raw_channel = channel.index(); - Self::regs_gp16() - .ccmr_input(raw_channel / 2) - .modify(|r| r.set_icpsc(raw_channel % 2, factor)); - } - - /// Set input TI selection. - fn set_input_ti_selection(&self, channel: Channel, tisel: InputTISelection) { - let raw_channel = channel.index(); - Self::regs_gp16() - .ccmr_input(raw_channel / 2) - .modify(|r| r.set_ccs(raw_channel % 2, tisel.into())); - } - - /// Set input capture mode. - fn set_input_capture_mode(&self, channel: Channel, mode: InputCaptureMode) { - Self::regs_gp16().ccer().modify(|r| match mode { - InputCaptureMode::Rising => { - r.set_ccnp(channel.index(), false); - r.set_ccp(channel.index(), false); - } - InputCaptureMode::Falling => { - r.set_ccnp(channel.index(), false); - r.set_ccp(channel.index(), true); - } - InputCaptureMode::BothEdges => { - r.set_ccnp(channel.index(), true); - r.set_ccp(channel.index(), true); - } - }); - } - - /// Set output compare mode. - fn set_output_compare_mode(&self, channel: Channel, mode: OutputCompareMode) { - let raw_channel: usize = channel.index(); - Self::regs_gp16() - .ccmr_output(raw_channel / 2) - .modify(|w| w.set_ocm(raw_channel % 2, mode.into())); - } - - /// Set output polarity. - fn set_output_polarity(&self, channel: Channel, polarity: OutputPolarity) { - Self::regs_gp16() - .ccer() - .modify(|w| w.set_ccp(channel.index(), polarity.into())); - } - - /// Enable/disable a channel. - fn enable_channel(&self, channel: Channel, enable: bool) { - Self::regs_gp16().ccer().modify(|w| w.set_cce(channel.index(), enable)); - } - - /// Get enable/disable state of a channel - fn get_channel_enable_state(&self, channel: Channel) -> bool { - Self::regs_gp16().ccer().read().cce(channel.index()) - } - - /// Set compare value for a channel. - fn set_compare_value(&self, channel: Channel, value: u16) { - Self::regs_gp16().ccr(channel.index()).modify(|w| w.set_ccr(value)); - } - - /// Get capture value for a channel. - fn get_capture_value(&self, channel: Channel) -> u16 { - Self::regs_gp16().ccr(channel.index()).read().ccr() - } - - /// Get compare value for a channel. - fn get_compare_value(&self, channel: Channel) -> u16 { - Self::regs_gp16().ccr(channel.index()).read().ccr() - } - - /// Set output compare preload. - fn set_output_compare_preload(&self, channel: Channel, preload: bool) { - let channel_index = channel.index(); - Self::regs_gp16() - .ccmr_output(channel_index / 2) - .modify(|w| w.set_ocpe(channel_index % 2, preload)); - } - - /// Get capture compare DMA selection - fn get_cc_dma_selection(&self) -> super::vals::Ccds { - Self::regs_gp16().cr2().read().ccds() - } - - /// Set capture compare DMA selection - fn set_cc_dma_selection(&self, ccds: super::vals::Ccds) { - Self::regs_gp16().cr2().modify(|w| w.set_ccds(ccds)) - } - - /// Get capture compare DMA enable state - fn get_cc_dma_enable_state(&self, channel: Channel) -> bool { - Self::regs_gp16().dier().read().ccde(channel.index()) - } - - /// Set capture compare DMA enable state - fn set_cc_dma_enable_state(&self, channel: Channel, ccde: bool) { - Self::regs_gp16().dier().modify(|w| w.set_ccde(channel.index(), ccde)) - } - } - - #[cfg(not(stm32l0))] - /// Gneral-purpose 32-bit timer instance. - pub trait GeneralPurpose32bitInstance: GeneralPurpose16bitInstance { - /// Get access to the general purpose 32bit timer registers. - /// - /// Note: This works even if the timer is more capable, because registers - /// for the less capable timers are a subset. This allows writing a driver - /// for a given set of capabilities, and having it transparently work with - /// more capable timers. - fn regs_gp32() -> crate::pac::timer::TimGp32; - - /// Set timer frequency. - fn set_frequency(&self, frequency: Hertz) { - let f = frequency.0; - assert!(f > 0); - let timer_f = Self::frequency().0; - let pclk_ticks_per_timer_period = (timer_f / f) as u64; - let psc: u16 = unwrap!(((pclk_ticks_per_timer_period - 1) / (1 << 32)).try_into()); - let arr: u32 = unwrap!((pclk_ticks_per_timer_period / (psc as u64 + 1)).try_into()); - - let regs = Self::regs_gp32(); - regs.psc().write_value(psc); - regs.arr().write_value(arr); - - regs.cr1().modify(|r| r.set_urs(vals::Urs::COUNTERONLY)); - regs.egr().write(|r| r.set_ug(true)); - regs.cr1().modify(|r| r.set_urs(vals::Urs::ANYEVENT)); - } - - /// Get timer frequency. - fn get_frequency(&self) -> Hertz { - let timer_f = Self::frequency(); - - let regs = Self::regs_gp32(); - let arr = regs.arr().read(); - let psc = regs.psc().read(); - - timer_f / arr / (psc + 1) - } - - /// Set comapre value for a channel. - fn set_compare_value(&self, channel: Channel, value: u32) { - Self::regs_gp32().ccr(channel.index()).write_value(value); - } - - /// Get capture value for a channel. - fn get_capture_value(&self, channel: Channel) -> u32 { - Self::regs_gp32().ccr(channel.index()).read() - } - - /// Get max compare value. This depends on the timer frequency and the clock frequency from RCC. - fn get_max_compare_value(&self) -> u32 { - Self::regs_gp32().arr().read() - } - - /// Get compare value for a channel. - fn get_compare_value(&self, channel: Channel) -> u32 { - Self::regs_gp32().ccr(channel.index()).read() - } - } - - #[cfg(not(stm32l0))] - /// Gneral-purpose 1 channel with one complementary 16-bit timer instance. - pub trait GeneralPurpose1ChannelComplementaryInstance: BasicNoCr2Instance + GeneralPurpose1ChannelInstance { - /// Get access to the general purpose 1 channel with one complementary 16bit timer registers. - /// - /// Note: This works even if the timer is more capable, because registers - /// for the less capable timers are a subset. This allows writing a driver - /// for a given set of capabilities, and having it transparently work with - /// more capable timers. - fn regs_1ch_cmp() -> crate::pac::timer::Tim1chCmp; - - /// Set clock divider for the dead time. - fn set_dead_time_clock_division(&self, value: vals::Ckd) { - Self::regs_1ch_cmp().cr1().modify(|w| w.set_ckd(value)); - } - - /// Set dead time, as a fraction of the max duty value. - fn set_dead_time_value(&self, value: u8) { - Self::regs_1ch_cmp().bdtr().modify(|w| w.set_dtg(value)); - } - - /// Set state of MOE-bit in BDTR register to en-/disable output - fn set_moe(&self, enable: bool) { - Self::regs_1ch_cmp().bdtr().modify(|w| w.set_moe(enable)); - } - } - - #[cfg(not(stm32l0))] - /// Gneral-purpose 2 channel with one complementary 16-bit timer instance. - pub trait GeneralPurpose2ChannelComplementaryInstance: - BasicInstance + GeneralPurpose2ChannelInstance + GeneralPurpose1ChannelComplementaryInstance - { - /// Get access to the general purpose 2 channel with one complementary 16bit timer registers. - /// - /// Note: This works even if the timer is more capable, because registers - /// for the less capable timers are a subset. This allows writing a driver - /// for a given set of capabilities, and having it transparently work with - /// more capable timers. - fn regs_2ch_cmp() -> crate::pac::timer::Tim2chCmp; - } - - #[cfg(not(stm32l0))] - /// Advanced control timer instance. - pub trait AdvancedControlInstance: - GeneralPurpose2ChannelComplementaryInstance + GeneralPurpose16bitInstance - { - /// Capture compare interrupt for this timer. - type CaptureCompareInterrupt: interrupt::typelevel::Interrupt; - - /// Get access to the advanced timer registers. - fn regs_advanced() -> crate::pac::timer::TimAdv; - - /// Set complementary output polarity. - fn set_complementary_output_polarity(&self, channel: Channel, polarity: OutputPolarity) { - Self::regs_advanced() - .ccer() - .modify(|w| w.set_ccnp(channel.index(), polarity.into())); - } - - /// Enable/disable a complementary channel. - fn enable_complementary_channel(&self, channel: Channel, enable: bool) { - Self::regs_advanced() - .ccer() - .modify(|w| w.set_ccne(channel.index(), enable)); - } - } -} /// Timer channel. #[derive(Clone, Copy)] @@ -511,181 +34,44 @@ impl Channel { } } -/// Input capture mode. -#[derive(Clone, Copy)] -pub enum InputCaptureMode { - /// Rising edge only. - Rising, - /// Falling edge only. - Falling, - /// Both rising or falling edges. - BothEdges, +/// Amount of bits of a timer. +#[derive(Clone, Copy, PartialEq, Eq, Debug)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +pub enum TimerBits { + /// 16 bits. + Bits16, + /// 32 bits. + #[cfg(not(stm32l0))] + Bits32, } -/// Input TI selection. -#[derive(Clone, Copy)] -pub enum InputTISelection { - /// Normal - Normal, - /// Alternate - Alternate, - /// TRC - TRC, -} +/// Core timer instance. +pub trait CoreInstance: RccPeripheral + 'static { + /// Interrupt for this timer. + type Interrupt: interrupt::typelevel::Interrupt; -impl From for stm32_metapac::timer::vals::CcmrInputCcs { - fn from(tisel: InputTISelection) -> Self { - match tisel { - InputTISelection::Normal => stm32_metapac::timer::vals::CcmrInputCcs::TI4, - InputTISelection::Alternate => stm32_metapac::timer::vals::CcmrInputCcs::TI3, - InputTISelection::TRC => stm32_metapac::timer::vals::CcmrInputCcs::TRC, - } - } -} + /// Amount of bits this timer has. + const BITS: TimerBits; -/// Timer counting mode. -#[repr(u8)] -#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)] -pub enum CountingMode { - #[default] - /// The timer counts up to the reload value and then resets back to 0. - EdgeAlignedUp, - /// The timer counts down to 0 and then resets back to the reload value. - EdgeAlignedDown, - /// The timer counts up to the reload value and then counts back to 0. + /// Registers for this timer. /// - /// The output compare interrupt flags of channels configured in output are - /// set when the counter is counting down. - CenterAlignedDownInterrupts, - /// The timer counts up to the reload value and then counts back to 0. - /// - /// The output compare interrupt flags of channels configured in output are - /// set when the counter is counting up. - CenterAlignedUpInterrupts, - /// The timer counts up to the reload value and then counts back to 0. - /// - /// The output compare interrupt flags of channels configured in output are - /// set when the counter is counting both up or down. - CenterAlignedBothInterrupts, + /// This is a raw pointer to the register block. The actual register block layout varies depending on the timer type. + fn regs() -> *mut (); } +/// Cut-down basic timer instance. +pub trait BasicNoCr2Instance: CoreInstance {} +/// Basic timer instance. +pub trait BasicInstance: BasicNoCr2Instance {} -impl CountingMode { - /// Return whether this mode is edge-aligned (up or down). - pub fn is_edge_aligned(&self) -> bool { - matches!(self, CountingMode::EdgeAlignedUp | CountingMode::EdgeAlignedDown) - } +/// General-purpose 16-bit timer with 1 channel instance. +pub trait GeneralInstance1Channel: CoreInstance {} - /// Return whether this mode is center-aligned. - pub fn is_center_aligned(&self) -> bool { - matches!( - self, - CountingMode::CenterAlignedDownInterrupts - | CountingMode::CenterAlignedUpInterrupts - | CountingMode::CenterAlignedBothInterrupts - ) - } -} +/// General-purpose 16-bit timer with 2 channels instance. +pub trait GeneralInstance2Channel: GeneralInstance1Channel {} -impl From for (vals::Cms, vals::Dir) { - fn from(value: CountingMode) -> Self { - match value { - CountingMode::EdgeAlignedUp => (vals::Cms::EDGEALIGNED, vals::Dir::UP), - CountingMode::EdgeAlignedDown => (vals::Cms::EDGEALIGNED, vals::Dir::DOWN), - CountingMode::CenterAlignedDownInterrupts => (vals::Cms::CENTERALIGNED1, vals::Dir::UP), - CountingMode::CenterAlignedUpInterrupts => (vals::Cms::CENTERALIGNED2, vals::Dir::UP), - CountingMode::CenterAlignedBothInterrupts => (vals::Cms::CENTERALIGNED3, vals::Dir::UP), - } - } -} - -impl From<(vals::Cms, vals::Dir)> for CountingMode { - fn from(value: (vals::Cms, vals::Dir)) -> Self { - match value { - (vals::Cms::EDGEALIGNED, vals::Dir::UP) => CountingMode::EdgeAlignedUp, - (vals::Cms::EDGEALIGNED, vals::Dir::DOWN) => CountingMode::EdgeAlignedDown, - (vals::Cms::CENTERALIGNED1, _) => CountingMode::CenterAlignedDownInterrupts, - (vals::Cms::CENTERALIGNED2, _) => CountingMode::CenterAlignedUpInterrupts, - (vals::Cms::CENTERALIGNED3, _) => CountingMode::CenterAlignedBothInterrupts, - } - } -} - -/// Output compare mode. -#[derive(Clone, Copy)] -pub enum OutputCompareMode { - /// The comparison between the output compare register TIMx_CCRx and - /// the counter TIMx_CNT has no effect on the outputs. - /// (this mode is used to generate a timing base). - Frozen, - /// Set channel to active level on match. OCxREF signal is forced high when the - /// counter TIMx_CNT matches the capture/compare register x (TIMx_CCRx). - ActiveOnMatch, - /// Set channel to inactive level on match. OCxREF signal is forced low when the - /// counter TIMx_CNT matches the capture/compare register x (TIMx_CCRx). - InactiveOnMatch, - /// Toggle - OCxREF toggles when TIMx_CNT=TIMx_CCRx. - Toggle, - /// Force inactive level - OCxREF is forced low. - ForceInactive, - /// Force active level - OCxREF is forced high. - ForceActive, - /// PWM mode 1 - In upcounting, channel is active as long as TIMx_CNTTIMx_CCRx else active (OCxREF=1). - PwmMode1, - /// PWM mode 2 - In upcounting, channel is inactive as long as - /// TIMx_CNTTIMx_CCRx else inactive. - PwmMode2, - // TODO: there's more modes here depending on the chip family. -} - -impl From for stm32_metapac::timer::vals::Ocm { - fn from(mode: OutputCompareMode) -> Self { - match mode { - OutputCompareMode::Frozen => stm32_metapac::timer::vals::Ocm::FROZEN, - OutputCompareMode::ActiveOnMatch => stm32_metapac::timer::vals::Ocm::ACTIVEONMATCH, - OutputCompareMode::InactiveOnMatch => stm32_metapac::timer::vals::Ocm::INACTIVEONMATCH, - OutputCompareMode::Toggle => stm32_metapac::timer::vals::Ocm::TOGGLE, - OutputCompareMode::ForceInactive => stm32_metapac::timer::vals::Ocm::FORCEINACTIVE, - OutputCompareMode::ForceActive => stm32_metapac::timer::vals::Ocm::FORCEACTIVE, - OutputCompareMode::PwmMode1 => stm32_metapac::timer::vals::Ocm::PWMMODE1, - OutputCompareMode::PwmMode2 => stm32_metapac::timer::vals::Ocm::PWMMODE2, - } - } -} - -/// Timer output pin polarity. -#[derive(Clone, Copy)] -pub enum OutputPolarity { - /// Active high (higher duty value makes the pin spend more time high). - ActiveHigh, - /// Active low (higher duty value makes the pin spend more time low). - ActiveLow, -} - -impl From for bool { - fn from(mode: OutputPolarity) -> Self { - match mode { - OutputPolarity::ActiveHigh => false, - OutputPolarity::ActiveLow => true, - } - } -} - -/// Basic 16-bit timer instance. -pub trait BasicInstance: sealed::BasicInstance + sealed::BasicNoCr2Instance + sealed::CoreInstance + 'static {} - -// It's just a General-purpose 16-bit timer instance. -/// Capture Compare timer instance. -pub trait CaptureCompare16bitInstance: - BasicInstance - + sealed::GeneralPurpose2ChannelInstance - + sealed::GeneralPurpose1ChannelInstance - + sealed::GeneralPurpose16bitInstance - + 'static -{ - // SimplePwm<'d, T> is implemented for T: CaptureCompare16bitInstance +/// General-purpose 16-bit timer with 4 channels instance. +pub trait GeneralInstance4Channel: BasicInstance + GeneralInstance2Channel { + // SimplePwm<'d, T> is implemented for T: GeneralInstance4Channel // Advanced timers implement this trait, but the output needs to be // enabled explicitly. // To support general-purpose and advanced timers, this function is added @@ -694,296 +80,149 @@ pub trait CaptureCompare16bitInstance: fn enable_outputs(&self) {} } -#[cfg(not(stm32l0))] -// It's just a General-purpose 32-bit timer instance. -/// Capture Compare 32-bit timer instance. -pub trait CaptureCompare32bitInstance: - CaptureCompare16bitInstance + sealed::GeneralPurpose32bitInstance + 'static -{ +/// General-purpose 32-bit timer with 4 channels instance. +pub trait GeneralInstance32bit4Channel: GeneralInstance4Channel {} + +/// Advanced 16-bit timer with 1 channel instance. +pub trait AdvancedInstance1Channel: BasicNoCr2Instance + GeneralInstance1Channel { + /// Capture compare interrupt for this timer. + type CaptureCompareInterrupt: interrupt::typelevel::Interrupt; } +/// Advanced 16-bit timer with 2 channels instance. -#[cfg(not(stm32l0))] -// It's just a Advanced Control timer instance. -/// Complementary Capture Compare 32-bit timer instance. -pub trait ComplementaryCaptureCompare16bitInstance: - CaptureCompare16bitInstance - + sealed::GeneralPurpose1ChannelComplementaryInstance - + sealed::GeneralPurpose2ChannelComplementaryInstance - + sealed::AdvancedControlInstance - + 'static -{ -} +pub trait AdvancedInstance2Channel: BasicInstance + GeneralInstance2Channel + AdvancedInstance1Channel {} -pin_trait!(Channel1Pin, CaptureCompare16bitInstance); -pin_trait!(Channel2Pin, CaptureCompare16bitInstance); -pin_trait!(Channel3Pin, CaptureCompare16bitInstance); -pin_trait!(Channel4Pin, CaptureCompare16bitInstance); -pin_trait!(ExternalTriggerPin, CaptureCompare16bitInstance); +/// Advanced 16-bit timer with 4 channels instance. +pub trait AdvancedInstance4Channel: AdvancedInstance2Channel + GeneralInstance4Channel {} -cfg_if::cfg_if! { - if #[cfg(not(stm32l0))] { - pin_trait!(Channel1ComplementaryPin, ComplementaryCaptureCompare16bitInstance); - pin_trait!(Channel2ComplementaryPin, ComplementaryCaptureCompare16bitInstance); - pin_trait!(Channel3ComplementaryPin, ComplementaryCaptureCompare16bitInstance); - pin_trait!(Channel4ComplementaryPin, ComplementaryCaptureCompare16bitInstance); +pin_trait!(Channel1Pin, GeneralInstance4Channel); +pin_trait!(Channel2Pin, GeneralInstance4Channel); +pin_trait!(Channel3Pin, GeneralInstance4Channel); +pin_trait!(Channel4Pin, GeneralInstance4Channel); +pin_trait!(ExternalTriggerPin, GeneralInstance4Channel); - pin_trait!(BreakInputPin, ComplementaryCaptureCompare16bitInstance); - pin_trait!(BreakInput2Pin, ComplementaryCaptureCompare16bitInstance); +pin_trait!(Channel1ComplementaryPin, AdvancedInstance4Channel); +pin_trait!(Channel2ComplementaryPin, AdvancedInstance4Channel); +pin_trait!(Channel3ComplementaryPin, AdvancedInstance4Channel); +pin_trait!(Channel4ComplementaryPin, AdvancedInstance4Channel); - pin_trait!(BreakInputComparator1Pin, ComplementaryCaptureCompare16bitInstance); - pin_trait!(BreakInputComparator2Pin, ComplementaryCaptureCompare16bitInstance); +pin_trait!(BreakInputPin, AdvancedInstance4Channel); +pin_trait!(BreakInput2Pin, AdvancedInstance4Channel); - pin_trait!(BreakInput2Comparator1Pin, ComplementaryCaptureCompare16bitInstance); - pin_trait!(BreakInput2Comparator2Pin, ComplementaryCaptureCompare16bitInstance); - } -} +pin_trait!(BreakInputComparator1Pin, AdvancedInstance4Channel); +pin_trait!(BreakInputComparator2Pin, AdvancedInstance4Channel); + +pin_trait!(BreakInput2Comparator1Pin, AdvancedInstance4Channel); +pin_trait!(BreakInput2Comparator2Pin, AdvancedInstance4Channel); + +// Update Event trigger DMA for every timer +dma_trait!(UpDma, BasicInstance); + +dma_trait!(Ch1Dma, GeneralInstance4Channel); +dma_trait!(Ch2Dma, GeneralInstance4Channel); +dma_trait!(Ch3Dma, GeneralInstance4Channel); +dma_trait!(Ch4Dma, GeneralInstance4Channel); #[allow(unused)] macro_rules! impl_core_timer { - ($inst:ident, $irq:ident) => { - impl sealed::CoreInstance for crate::peripherals::$inst { - type Interrupt = crate::interrupt::typelevel::$irq; + ($inst:ident, $bits:expr) => { + impl CoreInstance for crate::peripherals::$inst { + type Interrupt = crate::_generated::peripheral_interrupts::$inst::UP; - fn regs_core() -> crate::pac::timer::TimCore { - unsafe { crate::pac::timer::TimCore::from_ptr(crate::pac::$inst.as_ptr()) } - } - } - }; -} + const BITS: TimerBits = $bits; -#[allow(unused)] -macro_rules! impl_basic_no_cr2_timer { - ($inst:ident) => { - impl sealed::BasicNoCr2Instance for crate::peripherals::$inst { - fn regs_basic_no_cr2() -> crate::pac::timer::TimBasicNoCr2 { - unsafe { crate::pac::timer::TimBasicNoCr2::from_ptr(crate::pac::$inst.as_ptr()) } - } - } - }; -} - -#[allow(unused)] -macro_rules! impl_basic_timer { - ($inst:ident) => { - impl sealed::BasicInstance for crate::peripherals::$inst { - fn regs_basic() -> crate::pac::timer::TimBasic { - unsafe { crate::pac::timer::TimBasic::from_ptr(crate::pac::$inst.as_ptr()) } - } - } - }; -} - -#[allow(unused)] -macro_rules! impl_1ch_timer { - ($inst:ident) => { - impl sealed::GeneralPurpose1ChannelInstance for crate::peripherals::$inst { - fn regs_1ch() -> crate::pac::timer::Tim1ch { - unsafe { crate::pac::timer::Tim1ch::from_ptr(crate::pac::$inst.as_ptr()) } - } - } - }; -} - -#[allow(unused)] -macro_rules! impl_2ch_timer { - ($inst:ident) => { - impl sealed::GeneralPurpose2ChannelInstance for crate::peripherals::$inst { - fn regs_2ch() -> crate::pac::timer::Tim2ch { - unsafe { crate::pac::timer::Tim2ch::from_ptr(crate::pac::$inst.as_ptr()) } - } - } - }; -} - -#[allow(unused)] -macro_rules! impl_gp16_timer { - ($inst:ident) => { - impl sealed::GeneralPurpose16bitInstance for crate::peripherals::$inst { - fn regs_gp16() -> crate::pac::timer::TimGp16 { - unsafe { crate::pac::timer::TimGp16::from_ptr(crate::pac::$inst.as_ptr()) } - } - } - }; -} - -#[allow(unused)] -macro_rules! impl_gp32_timer { - ($inst:ident) => { - impl sealed::GeneralPurpose32bitInstance for crate::peripherals::$inst { - fn regs_gp32() -> crate::pac::timer::TimGp32 { - crate::pac::$inst - } - } - }; -} - -#[allow(unused)] -macro_rules! impl_1ch_cmp_timer { - ($inst:ident) => { - impl sealed::GeneralPurpose1ChannelComplementaryInstance for crate::peripherals::$inst { - fn regs_1ch_cmp() -> crate::pac::timer::Tim1chCmp { - unsafe { crate::pac::timer::Tim1chCmp::from_ptr(crate::pac::$inst.as_ptr()) } - } - } - }; -} - -#[allow(unused)] -macro_rules! impl_2ch_cmp_timer { - ($inst:ident) => { - impl sealed::GeneralPurpose2ChannelComplementaryInstance for crate::peripherals::$inst { - fn regs_2ch_cmp() -> crate::pac::timer::Tim2chCmp { - unsafe { crate::pac::timer::Tim2chCmp::from_ptr(crate::pac::$inst.as_ptr()) } - } - } - }; -} - -#[allow(unused)] -macro_rules! impl_adv_timer { - ($inst:ident, $irq:ident) => { - impl sealed::AdvancedControlInstance for crate::peripherals::$inst { - type CaptureCompareInterrupt = crate::interrupt::typelevel::$irq; - - fn regs_advanced() -> crate::pac::timer::TimAdv { - unsafe { crate::pac::timer::TimAdv::from_ptr(crate::pac::$inst.as_ptr()) } + fn regs() -> *mut () { + crate::pac::$inst.as_ptr() } } }; } foreach_interrupt! { - ($inst:ident, timer, TIM_BASIC, UP, $irq:ident) => { - impl_core_timer!($inst, $irq); - impl_basic_no_cr2_timer!($inst); - impl_basic_timer!($inst); + impl_core_timer!($inst, TimerBits::Bits16); + impl BasicNoCr2Instance for crate::peripherals::$inst {} impl BasicInstance for crate::peripherals::$inst {} }; ($inst:ident, timer, TIM_1CH, UP, $irq:ident) => { - impl_core_timer!($inst, $irq); - impl_basic_no_cr2_timer!($inst); - impl_basic_timer!($inst); - impl_1ch_timer!($inst); - impl_2ch_timer!($inst); - impl_gp16_timer!($inst); + impl_core_timer!($inst, TimerBits::Bits16); + impl BasicNoCr2Instance for crate::peripherals::$inst {} impl BasicInstance for crate::peripherals::$inst {} - impl CaptureCompare16bitInstance for crate::peripherals::$inst {} + impl GeneralInstance1Channel for crate::peripherals::$inst {} + impl GeneralInstance2Channel for crate::peripherals::$inst {} + impl GeneralInstance4Channel for crate::peripherals::$inst {} }; - ($inst:ident, timer, TIM_2CH, UP, $irq:ident) => { - impl_core_timer!($inst, $irq); - impl_basic_no_cr2_timer!($inst); - impl_basic_timer!($inst); - impl_1ch_timer!($inst); - impl_2ch_timer!($inst); - impl_gp16_timer!($inst); + impl_core_timer!($inst, TimerBits::Bits16); + impl BasicNoCr2Instance for crate::peripherals::$inst {} impl BasicInstance for crate::peripherals::$inst {} - impl CaptureCompare16bitInstance for crate::peripherals::$inst {} + impl GeneralInstance1Channel for crate::peripherals::$inst {} + impl GeneralInstance2Channel for crate::peripherals::$inst {} + impl GeneralInstance4Channel for crate::peripherals::$inst {} }; ($inst:ident, timer, TIM_GP16, UP, $irq:ident) => { - impl_core_timer!($inst, $irq); - impl_basic_no_cr2_timer!($inst); - impl_basic_timer!($inst); - impl_1ch_timer!($inst); - impl_2ch_timer!($inst); - impl_gp16_timer!($inst); + impl_core_timer!($inst, TimerBits::Bits16); + impl BasicNoCr2Instance for crate::peripherals::$inst {} impl BasicInstance for crate::peripherals::$inst {} - impl CaptureCompare16bitInstance for crate::peripherals::$inst {} + impl GeneralInstance1Channel for crate::peripherals::$inst {} + impl GeneralInstance2Channel for crate::peripherals::$inst {} + impl GeneralInstance4Channel for crate::peripherals::$inst {} }; ($inst:ident, timer, TIM_GP32, UP, $irq:ident) => { - impl_core_timer!($inst, $irq); - impl_basic_no_cr2_timer!($inst); - impl_basic_timer!($inst); - impl_1ch_timer!($inst); - impl_2ch_timer!($inst); - impl_gp16_timer!($inst); - impl_gp32_timer!($inst); + impl_core_timer!($inst, TimerBits::Bits32); + impl BasicNoCr2Instance for crate::peripherals::$inst {} impl BasicInstance for crate::peripherals::$inst {} - impl CaptureCompare16bitInstance for crate::peripherals::$inst {} - impl CaptureCompare32bitInstance for crate::peripherals::$inst {} + impl GeneralInstance1Channel for crate::peripherals::$inst {} + impl GeneralInstance2Channel for crate::peripherals::$inst {} + impl GeneralInstance4Channel for crate::peripherals::$inst {} + impl GeneralInstance32bit4Channel for crate::peripherals::$inst {} }; ($inst:ident, timer, TIM_1CH_CMP, UP, $irq:ident) => { - impl_core_timer!($inst, $irq); - impl_basic_no_cr2_timer!($inst); - impl_basic_timer!($inst); - impl_1ch_timer!($inst); - impl_2ch_timer!($inst); - impl_gp16_timer!($inst); - impl_1ch_cmp_timer!($inst); - impl_2ch_cmp_timer!($inst); + impl_core_timer!($inst, TimerBits::Bits16); + impl BasicNoCr2Instance for crate::peripherals::$inst {} impl BasicInstance for crate::peripherals::$inst {} - impl CaptureCompare16bitInstance for crate::peripherals::$inst { - /// Enable timer outputs. - fn enable_outputs(&self) { - use crate::timer::sealed::GeneralPurpose1ChannelComplementaryInstance; - self.set_moe(true); - } - } - impl ComplementaryCaptureCompare16bitInstance for crate::peripherals::$inst {} + impl GeneralInstance1Channel for crate::peripherals::$inst {} + impl GeneralInstance2Channel for crate::peripherals::$inst {} + impl GeneralInstance4Channel for crate::peripherals::$inst { fn enable_outputs(&self) { set_moe::() }} + impl AdvancedInstance1Channel for crate::peripherals::$inst { type CaptureCompareInterrupt = crate::_generated::peripheral_interrupts::$inst::CC; } + impl AdvancedInstance2Channel for crate::peripherals::$inst {} + impl AdvancedInstance4Channel for crate::peripherals::$inst {} }; - ($inst:ident, timer, TIM_1CH_CMP, CC, $irq:ident) => { - impl_adv_timer!($inst, $irq); - }; - ($inst:ident, timer, TIM_2CH_CMP, UP, $irq:ident) => { - impl_core_timer!($inst, $irq); - impl_basic_no_cr2_timer!($inst); - impl_basic_timer!($inst); - impl_1ch_timer!($inst); - impl_2ch_timer!($inst); - impl_gp16_timer!($inst); - impl_1ch_cmp_timer!($inst); - impl_2ch_cmp_timer!($inst); + impl_core_timer!($inst, TimerBits::Bits16); + impl BasicNoCr2Instance for crate::peripherals::$inst {} impl BasicInstance for crate::peripherals::$inst {} - impl CaptureCompare16bitInstance for crate::peripherals::$inst { - /// Enable timer outputs. - fn enable_outputs(&self) { - use crate::timer::sealed::GeneralPurpose1ChannelComplementaryInstance; - self.set_moe(true); - } - } - impl ComplementaryCaptureCompare16bitInstance for crate::peripherals::$inst {} + impl GeneralInstance1Channel for crate::peripherals::$inst {} + impl GeneralInstance2Channel for crate::peripherals::$inst {} + impl GeneralInstance4Channel for crate::peripherals::$inst { fn enable_outputs(&self) { set_moe::() }} + impl AdvancedInstance1Channel for crate::peripherals::$inst { type CaptureCompareInterrupt = crate::_generated::peripheral_interrupts::$inst::CC; } + impl AdvancedInstance2Channel for crate::peripherals::$inst {} + impl AdvancedInstance4Channel for crate::peripherals::$inst {} }; - ($inst:ident, timer, TIM_2CH_CMP, CC, $irq:ident) => { - impl_adv_timer!($inst, $irq); - }; - ($inst:ident, timer, TIM_ADV, UP, $irq:ident) => { - impl_core_timer!($inst, $irq); - impl_basic_no_cr2_timer!($inst); - impl_basic_timer!($inst); - impl_1ch_timer!($inst); - impl_2ch_timer!($inst); - impl_gp16_timer!($inst); - impl_1ch_cmp_timer!($inst); - impl_2ch_cmp_timer!($inst); + impl_core_timer!($inst, TimerBits::Bits16); + impl BasicNoCr2Instance for crate::peripherals::$inst {} impl BasicInstance for crate::peripherals::$inst {} - impl CaptureCompare16bitInstance for crate::peripherals::$inst { - /// Enable timer outputs. - fn enable_outputs(&self) { - use crate::timer::sealed::GeneralPurpose1ChannelComplementaryInstance; - self.set_moe(true); - } - } - impl ComplementaryCaptureCompare16bitInstance for crate::peripherals::$inst {} - }; - ($inst:ident, timer, TIM_ADV, CC, $irq:ident) => { - impl_adv_timer!($inst, $irq); + impl GeneralInstance1Channel for crate::peripherals::$inst {} + impl GeneralInstance2Channel for crate::peripherals::$inst {} + impl GeneralInstance4Channel for crate::peripherals::$inst { fn enable_outputs(&self) { set_moe::() }} + impl AdvancedInstance1Channel for crate::peripherals::$inst { type CaptureCompareInterrupt = crate::_generated::peripheral_interrupts::$inst::CC; } + impl AdvancedInstance2Channel for crate::peripherals::$inst {} + impl AdvancedInstance4Channel for crate::peripherals::$inst {} }; } -// Update Event trigger DMA for every timer -dma_trait!(UpDma, BasicInstance); - -dma_trait!(Ch1Dma, CaptureCompare16bitInstance); -dma_trait!(Ch2Dma, CaptureCompare16bitInstance); -dma_trait!(Ch3Dma, CaptureCompare16bitInstance); -dma_trait!(Ch4Dma, CaptureCompare16bitInstance); +#[cfg(not(stm32l0))] +#[allow(unused)] +fn set_moe() { + unsafe { crate::pac::timer::Tim1chCmp::from_ptr(T::regs()) } + .bdtr() + .modify(|w| w.set_moe(true)); +} diff --git a/embassy-stm32/src/timer/qei.rs b/embassy-stm32/src/timer/qei.rs index 59efb72ba..b6ab939cc 100644 --- a/embassy-stm32/src/timer/qei.rs +++ b/embassy-stm32/src/timer/qei.rs @@ -3,8 +3,10 @@ use core::marker::PhantomData; use embassy_hal_internal::{into_ref, PeripheralRef}; +use stm32_metapac::timer::vals; -use super::*; +use super::low_level::Timer; +use super::{Channel1Pin, Channel2Pin, GeneralInstance4Channel}; use crate::gpio::sealed::AFType; use crate::gpio::AnyPin; use crate::Peripheral; @@ -30,7 +32,7 @@ pub struct QeiPin<'d, T, Channel> { macro_rules! channel_impl { ($new_chx:ident, $channel:ident, $pin_trait:ident) => { - impl<'d, T: CaptureCompare16bitInstance> QeiPin<'d, T, $channel> { + impl<'d, T: GeneralInstance4Channel> QeiPin<'d, T, $channel> { #[doc = concat!("Create a new ", stringify!($channel), " QEI pin instance.")] pub fn $new_chx(pin: impl Peripheral

> + 'd) -> Self { into_ref!(pin); @@ -53,29 +55,28 @@ channel_impl!(new_ch1, Ch1, Channel1Pin); channel_impl!(new_ch2, Ch2, Channel2Pin); /// Quadrature decoder driver. -pub struct Qei<'d, T> { - _inner: PeripheralRef<'d, T>, +pub struct Qei<'d, T: GeneralInstance4Channel> { + inner: Timer<'d, T>, } -impl<'d, T: CaptureCompare16bitInstance> Qei<'d, T> { +impl<'d, T: GeneralInstance4Channel> Qei<'d, T> { /// Create a new quadrature decoder driver. pub fn new(tim: impl Peripheral

+ 'd, _ch1: QeiPin<'d, T, Ch1>, _ch2: QeiPin<'d, T, Ch2>) -> Self { Self::new_inner(tim) } fn new_inner(tim: impl Peripheral

+ 'd) -> Self { - into_ref!(tim); - - T::enable_and_reset(); + let inner = Timer::new(tim); + let r = inner.regs_gp16(); // Configure TxC1 and TxC2 as captures - T::regs_gp16().ccmr_input(0).modify(|w| { + r.ccmr_input(0).modify(|w| { w.set_ccs(0, vals::CcmrInputCcs::TI4); w.set_ccs(1, vals::CcmrInputCcs::TI4); }); // enable and configure to capture on rising edge - T::regs_gp16().ccer().modify(|w| { + r.ccer().modify(|w| { w.set_cce(0, true); w.set_cce(1, true); @@ -83,19 +84,19 @@ impl<'d, T: CaptureCompare16bitInstance> Qei<'d, T> { w.set_ccp(1, false); }); - T::regs_gp16().smcr().modify(|w| { + r.smcr().modify(|w| { w.set_sms(vals::Sms::ENCODER_MODE_3); }); - T::regs_gp16().arr().modify(|w| w.set_arr(u16::MAX)); - T::regs_gp16().cr1().modify(|w| w.set_cen(true)); + r.arr().modify(|w| w.set_arr(u16::MAX)); + r.cr1().modify(|w| w.set_cen(true)); - Self { _inner: tim } + Self { inner } } /// Get direction. pub fn read_direction(&self) -> Direction { - match T::regs_gp16().cr1().read().dir() { + match self.inner.regs_gp16().cr1().read().dir() { vals::Dir::DOWN => Direction::Downcounting, vals::Dir::UP => Direction::Upcounting, } @@ -103,6 +104,6 @@ impl<'d, T: CaptureCompare16bitInstance> Qei<'d, T> { /// Get count. pub fn count(&self) -> u16 { - T::regs_gp16().cnt().read().cnt() + self.inner.regs_gp16().cnt().read().cnt() } } diff --git a/embassy-stm32/src/timer/simple_pwm.rs b/embassy-stm32/src/timer/simple_pwm.rs index 4669fc6cc..b54e9a0d6 100644 --- a/embassy-stm32/src/timer/simple_pwm.rs +++ b/embassy-stm32/src/timer/simple_pwm.rs @@ -4,10 +4,10 @@ use core::marker::PhantomData; use embassy_hal_internal::{into_ref, PeripheralRef}; -use super::*; -#[allow(unused_imports)] -use crate::gpio::sealed::{AFType, Pin}; +use super::low_level::{CountingMode, OutputCompareMode, OutputPolarity, Timer}; +use super::{Channel, Channel1Pin, Channel2Pin, Channel3Pin, Channel4Pin, GeneralInstance4Channel}; use crate::gpio::{AnyPin, OutputType}; +use crate::time::Hertz; use crate::Peripheral; /// Channel 1 marker type. @@ -29,7 +29,7 @@ pub struct PwmPin<'d, T, C> { macro_rules! channel_impl { ($new_chx:ident, $channel:ident, $pin_trait:ident) => { - impl<'d, T: CaptureCompare16bitInstance> PwmPin<'d, T, $channel> { + impl<'d, T: GeneralInstance4Channel> PwmPin<'d, T, $channel> { #[doc = concat!("Create a new ", stringify!($channel), " PWM pin instance.")] pub fn $new_chx(pin: impl Peripheral

> + 'd, output_type: OutputType) -> Self { into_ref!(pin); @@ -54,11 +54,11 @@ channel_impl!(new_ch3, Ch3, Channel3Pin); channel_impl!(new_ch4, Ch4, Channel4Pin); /// Simple PWM driver. -pub struct SimplePwm<'d, T> { - inner: PeripheralRef<'d, T>, +pub struct SimplePwm<'d, T: GeneralInstance4Channel> { + inner: Timer<'d, T>, } -impl<'d, T: CaptureCompare16bitInstance> SimplePwm<'d, T> { +impl<'d, T: GeneralInstance4Channel> SimplePwm<'d, T> { /// Create a new simple PWM driver. pub fn new( tim: impl Peripheral

+ 'd, @@ -73,15 +73,11 @@ impl<'d, T: CaptureCompare16bitInstance> SimplePwm<'d, T> { } fn new_inner(tim: impl Peripheral

+ 'd, freq: Hertz, counting_mode: CountingMode) -> Self { - into_ref!(tim); - - T::enable_and_reset(); - - let mut this = Self { inner: tim }; + let mut this = Self { inner: Timer::new(tim) }; this.inner.set_counting_mode(counting_mode); this.set_frequency(freq); - this.inner.enable_outputs(); // Required for advanced timers, see CaptureCompare16bitInstance for details + this.inner.enable_outputs(); // Required for advanced timers, see GeneralInstance4Channel for details this.inner.start(); [Channel::Ch1, Channel::Ch2, Channel::Ch3, Channel::Ch4] @@ -126,14 +122,14 @@ impl<'d, T: CaptureCompare16bitInstance> SimplePwm<'d, T> { /// Get max duty value. /// /// This value depends on the configured frequency and the timer's clock rate from RCC. - pub fn get_max_duty(&self) -> u16 { + pub fn get_max_duty(&self) -> u32 { self.inner.get_max_compare_value() + 1 } /// Set the duty for a given channel. /// /// The value ranges from 0 for 0% duty, to [`get_max_duty`](Self::get_max_duty) for 100% duty, both included. - pub fn set_duty(&mut self, channel: Channel, duty: u16) { + pub fn set_duty(&mut self, channel: Channel, duty: u32) { assert!(duty <= self.get_max_duty()); self.inner.set_compare_value(channel, duty) } @@ -141,7 +137,7 @@ impl<'d, T: CaptureCompare16bitInstance> SimplePwm<'d, T> { /// Get the duty for a given channel. /// /// The value ranges from 0 for 0% duty, to [`get_max_duty`](Self::get_max_duty) for 100% duty, both included. - pub fn get_duty(&self, channel: Channel) -> u16 { + pub fn get_duty(&self, channel: Channel) -> u32 { self.inner.get_compare_value(channel) } @@ -165,8 +161,6 @@ impl<'d, T: CaptureCompare16bitInstance> SimplePwm<'d, T> { channel: Channel, duty: &[u16], ) { - assert!(duty.iter().all(|v| *v <= self.get_max_duty())); - into_ref!(dma); #[allow(clippy::let_unit_value)] // eg. stm32f334 @@ -201,7 +195,7 @@ impl<'d, T: CaptureCompare16bitInstance> SimplePwm<'d, T> { &mut dma, req, duty, - T::regs_1ch().ccr(channel.index()).as_ptr() as *mut _, + self.inner.regs_1ch().ccr(channel.index()).as_ptr() as *mut _, dma_transfer_option, ) .await @@ -227,22 +221,20 @@ impl<'d, T: CaptureCompare16bitInstance> SimplePwm<'d, T> { macro_rules! impl_waveform_chx { ($fn_name:ident, $dma_ch:ident, $cc_ch:ident) => { - impl<'d, T: CaptureCompare16bitInstance> SimplePwm<'d, T> { + impl<'d, T: GeneralInstance4Channel> SimplePwm<'d, T> { /// Generate a sequence of PWM waveform /// /// Note: /// you will need to provide corresponding TIMx_CHy DMA channel to use this method. pub async fn $fn_name(&mut self, dma: impl Peripheral

>, duty: &[u16]) { - use super::vals::Ccds; - - assert!(duty.iter().all(|v| *v <= self.get_max_duty())); + use crate::pac::timer::vals::Ccds; into_ref!(dma); #[allow(clippy::let_unit_value)] // eg. stm32f334 let req = dma.request(); - let cc_channel = super::Channel::$cc_ch; + let cc_channel = Channel::$cc_ch; let original_duty_state = self.get_duty(cc_channel); let original_enable_state = self.is_enabled(cc_channel); @@ -279,7 +271,7 @@ macro_rules! impl_waveform_chx { &mut dma, req, duty, - T::regs_gp16().ccr(cc_channel.index()).as_ptr() as *mut _, + self.inner.regs_gp16().ccr(cc_channel.index()).as_ptr() as *mut _, dma_transfer_option, ) .await @@ -314,10 +306,10 @@ impl_waveform_chx!(waveform_ch2, Ch2Dma, Ch2); impl_waveform_chx!(waveform_ch3, Ch3Dma, Ch3); impl_waveform_chx!(waveform_ch4, Ch4Dma, Ch4); -impl<'d, T: CaptureCompare16bitInstance> embedded_hal_02::Pwm for SimplePwm<'d, T> { +impl<'d, T: GeneralInstance4Channel> embedded_hal_02::Pwm for SimplePwm<'d, T> { type Channel = Channel; type Time = Hertz; - type Duty = u16; + type Duty = u32; fn disable(&mut self, channel: Self::Channel) { self.inner.enable_channel(channel, false); diff --git a/examples/stm32f4/src/bin/ws2812_pwm.rs b/examples/stm32f4/src/bin/ws2812_pwm.rs index 6122cea2d..cbaff75fc 100644 --- a/examples/stm32f4/src/bin/ws2812_pwm.rs +++ b/examples/stm32f4/src/bin/ws2812_pwm.rs @@ -15,8 +15,9 @@ use embassy_executor::Spawner; use embassy_stm32::gpio::OutputType; use embassy_stm32::time::khz; +use embassy_stm32::timer::low_level::CountingMode; use embassy_stm32::timer::simple_pwm::{PwmPin, SimplePwm}; -use embassy_stm32::timer::{Channel, CountingMode}; +use embassy_stm32::timer::Channel; use embassy_time::{Duration, Ticker, Timer}; use {defmt_rtt as _, panic_probe as _}; @@ -60,7 +61,7 @@ async fn main(_spawner: Spawner) { // construct ws2812 non-return-to-zero (NRZ) code bit by bit // ws2812 only need 24 bits for each LED, but we add one bit more to keep PWM output low - let max_duty = ws2812_pwm.get_max_duty(); + let max_duty = ws2812_pwm.get_max_duty() as u16; let n0 = 8 * max_duty / 25; // ws2812 Bit 0 high level timing let n1 = 2 * n0; // ws2812 Bit 1 high level timing diff --git a/examples/stm32h7/src/bin/dac_dma.rs b/examples/stm32h7/src/bin/dac_dma.rs index feec28993..c45747f35 100644 --- a/examples/stm32h7/src/bin/dac_dma.rs +++ b/examples/stm32h7/src/bin/dac_dma.rs @@ -8,7 +8,7 @@ use embassy_stm32::pac::timer::vals::Mms; use embassy_stm32::peripherals::{DAC1, DMA1_CH3, DMA1_CH4, TIM6, TIM7}; use embassy_stm32::rcc::low_level::RccPeripheral; use embassy_stm32::time::Hertz; -use embassy_stm32::timer::low_level::BasicInstance; +use embassy_stm32::timer::low_level::Timer; use micromath::F32Ext; use {defmt_rtt as _, panic_probe as _}; @@ -51,12 +51,12 @@ async fn main(spawner: Spawner) { // Obtain two independent channels (p.DAC1 can only be consumed once, though!) let (dac_ch1, dac_ch2) = embassy_stm32::dac::Dac::new(p.DAC1, p.DMA1_CH3, p.DMA1_CH4, p.PA4, p.PA5).split(); - spawner.spawn(dac_task1(dac_ch1)).ok(); - spawner.spawn(dac_task2(dac_ch2)).ok(); + spawner.spawn(dac_task1(p.TIM6, dac_ch1)).ok(); + spawner.spawn(dac_task2(p.TIM7, dac_ch2)).ok(); } #[embassy_executor::task] -async fn dac_task1(mut dac: DacCh1<'static, DAC1, DMA1_CH3>) { +async fn dac_task1(tim: TIM6, mut dac: DacCh1<'static, DAC1, DMA1_CH3>) { let data: &[u8; 256] = &calculate_array::<256>(); info!("TIM6 frequency is {}", TIM6::frequency()); @@ -74,10 +74,10 @@ async fn dac_task1(mut dac: DacCh1<'static, DAC1, DMA1_CH3>) { dac.set_triggering(true); dac.enable(); - TIM6::enable_and_reset(); - TIM6::regs_basic().arr().modify(|w| w.set_arr(reload as u16 - 1)); - TIM6::regs_basic().cr2().modify(|w| w.set_mms(Mms::UPDATE)); - TIM6::regs_basic().cr1().modify(|w| { + let tim = Timer::new(tim); + tim.regs_basic().arr().modify(|w| w.set_arr(reload as u16 - 1)); + tim.regs_basic().cr2().modify(|w| w.set_mms(Mms::UPDATE)); + tim.regs_basic().cr1().modify(|w| { w.set_opm(false); w.set_cen(true); }); @@ -99,7 +99,7 @@ async fn dac_task1(mut dac: DacCh1<'static, DAC1, DMA1_CH3>) { } #[embassy_executor::task] -async fn dac_task2(mut dac: DacCh2<'static, DAC1, DMA1_CH4>) { +async fn dac_task2(tim: TIM7, mut dac: DacCh2<'static, DAC1, DMA1_CH4>) { let data: &[u8; 256] = &calculate_array::<256>(); info!("TIM7 frequency is {}", TIM7::frequency()); @@ -111,10 +111,10 @@ async fn dac_task2(mut dac: DacCh2<'static, DAC1, DMA1_CH4>) { error!("Reload value {} below threshold!", reload); } - TIM7::enable_and_reset(); - TIM7::regs_basic().arr().modify(|w| w.set_arr(reload as u16 - 1)); - TIM7::regs_basic().cr2().modify(|w| w.set_mms(Mms::UPDATE)); - TIM7::regs_basic().cr1().modify(|w| { + let tim = Timer::new(tim); + tim.regs_basic().arr().modify(|w| w.set_arr(reload as u16 - 1)); + tim.regs_basic().cr2().modify(|w| w.set_mms(Mms::UPDATE)); + tim.regs_basic().cr1().modify(|w| { w.set_opm(false); w.set_cen(true); }); diff --git a/examples/stm32h7/src/bin/low_level_timer_api.rs b/examples/stm32h7/src/bin/low_level_timer_api.rs index 049d9967d..780fbc6f0 100644 --- a/examples/stm32h7/src/bin/low_level_timer_api.rs +++ b/examples/stm32h7/src/bin/low_level_timer_api.rs @@ -6,8 +6,9 @@ use embassy_executor::Spawner; use embassy_stm32::gpio::low_level::AFType; use embassy_stm32::gpio::Speed; use embassy_stm32::time::{khz, Hertz}; -use embassy_stm32::timer::*; -use embassy_stm32::{into_ref, Config, Peripheral, PeripheralRef}; +use embassy_stm32::timer::low_level::{OutputCompareMode, Timer as LLTimer}; +use embassy_stm32::timer::{Channel, Channel1Pin, Channel2Pin, Channel3Pin, Channel4Pin, GeneralInstance32bit4Channel}; +use embassy_stm32::{into_ref, Config, Peripheral}; use embassy_time::Timer; use {defmt_rtt as _, panic_probe as _}; @@ -56,11 +57,11 @@ async fn main(_spawner: Spawner) { Timer::after_millis(300).await; } } -pub struct SimplePwm32<'d, T: CaptureCompare32bitInstance> { - inner: PeripheralRef<'d, T>, +pub struct SimplePwm32<'d, T: GeneralInstance32bit4Channel> { + tim: LLTimer<'d, T>, } -impl<'d, T: CaptureCompare32bitInstance> SimplePwm32<'d, T> { +impl<'d, T: GeneralInstance32bit4Channel> SimplePwm32<'d, T> { pub fn new( tim: impl Peripheral

+ 'd, ch1: impl Peripheral

> + 'd, @@ -69,9 +70,7 @@ impl<'d, T: CaptureCompare32bitInstance> SimplePwm32<'d, T> { ch4: impl Peripheral

> + 'd, freq: Hertz, ) -> Self { - into_ref!(tim, ch1, ch2, ch3, ch4); - - T::enable_and_reset(); + into_ref!(ch1, ch2, ch3, ch4); ch1.set_speed(Speed::VeryHigh); ch1.set_as_af(ch1.af_num(), AFType::OutputPushPull); @@ -82,12 +81,12 @@ impl<'d, T: CaptureCompare32bitInstance> SimplePwm32<'d, T> { ch4.set_speed(Speed::VeryHigh); ch4.set_as_af(ch1.af_num(), AFType::OutputPushPull); - let mut this = Self { inner: tim }; + let mut this = Self { tim: LLTimer::new(tim) }; this.set_frequency(freq); - this.inner.start(); + this.tim.start(); - let r = T::regs_gp32(); + let r = this.tim.regs_gp32(); r.ccmr_output(0) .modify(|w| w.set_ocm(0, OutputCompareMode::PwmMode1.into())); r.ccmr_output(0) @@ -101,23 +100,26 @@ impl<'d, T: CaptureCompare32bitInstance> SimplePwm32<'d, T> { } pub fn enable(&mut self, channel: Channel) { - T::regs_gp32().ccer().modify(|w| w.set_cce(channel.index(), true)); + self.tim.regs_gp32().ccer().modify(|w| w.set_cce(channel.index(), true)); } pub fn disable(&mut self, channel: Channel) { - T::regs_gp32().ccer().modify(|w| w.set_cce(channel.index(), false)); + self.tim + .regs_gp32() + .ccer() + .modify(|w| w.set_cce(channel.index(), false)); } pub fn set_frequency(&mut self, freq: Hertz) { - ::set_frequency(&mut self.inner, freq); + self.tim.set_frequency(freq); } pub fn get_max_duty(&self) -> u32 { - T::regs_gp32().arr().read() + self.tim.regs_gp32().arr().read() } pub fn set_duty(&mut self, channel: Channel, duty: u32) { defmt::assert!(duty < self.get_max_duty()); - T::regs_gp32().ccr(channel.index()).write_value(duty) + self.tim.regs_gp32().ccr(channel.index()).write_value(duty) } } diff --git a/examples/stm32l4/src/bin/dac_dma.rs b/examples/stm32l4/src/bin/dac_dma.rs index f227812cd..98edd39c0 100644 --- a/examples/stm32l4/src/bin/dac_dma.rs +++ b/examples/stm32l4/src/bin/dac_dma.rs @@ -8,7 +8,7 @@ use embassy_stm32::pac::timer::vals::Mms; use embassy_stm32::peripherals::{DAC1, DMA1_CH3, DMA1_CH4, TIM6, TIM7}; use embassy_stm32::rcc::low_level::RccPeripheral; use embassy_stm32::time::Hertz; -use embassy_stm32::timer::low_level::BasicInstance; +use embassy_stm32::timer::low_level::Timer; use micromath::F32Ext; use {defmt_rtt as _, panic_probe as _}; @@ -22,12 +22,12 @@ async fn main(spawner: Spawner) { // Obtain two independent channels (p.DAC1 can only be consumed once, though!) let (dac_ch1, dac_ch2) = embassy_stm32::dac::Dac::new(p.DAC1, p.DMA1_CH3, p.DMA1_CH4, p.PA4, p.PA5).split(); - spawner.spawn(dac_task1(dac_ch1)).ok(); - spawner.spawn(dac_task2(dac_ch2)).ok(); + spawner.spawn(dac_task1(p.TIM6, dac_ch1)).ok(); + spawner.spawn(dac_task2(p.TIM7, dac_ch2)).ok(); } #[embassy_executor::task] -async fn dac_task1(mut dac: DacCh1<'static, DAC1, DMA1_CH3>) { +async fn dac_task1(tim: TIM6, mut dac: DacCh1<'static, DAC1, DMA1_CH3>) { let data: &[u8; 256] = &calculate_array::<256>(); info!("TIM6 frequency is {}", TIM6::frequency()); @@ -45,10 +45,10 @@ async fn dac_task1(mut dac: DacCh1<'static, DAC1, DMA1_CH3>) { dac.set_triggering(true); dac.enable(); - TIM6::enable_and_reset(); - TIM6::regs_basic().arr().modify(|w| w.set_arr(reload as u16 - 1)); - TIM6::regs_basic().cr2().modify(|w| w.set_mms(Mms::UPDATE)); - TIM6::regs_basic().cr1().modify(|w| { + let tim = Timer::new(tim); + tim.regs_basic().arr().modify(|w| w.set_arr(reload as u16 - 1)); + tim.regs_basic().cr2().modify(|w| w.set_mms(Mms::UPDATE)); + tim.regs_basic().cr1().modify(|w| { w.set_opm(false); w.set_cen(true); }); @@ -70,7 +70,7 @@ async fn dac_task1(mut dac: DacCh1<'static, DAC1, DMA1_CH3>) { } #[embassy_executor::task] -async fn dac_task2(mut dac: DacCh2<'static, DAC1, DMA1_CH4>) { +async fn dac_task2(tim: TIM7, mut dac: DacCh2<'static, DAC1, DMA1_CH4>) { let data: &[u8; 256] = &calculate_array::<256>(); info!("TIM7 frequency is {}", TIM7::frequency()); @@ -82,10 +82,10 @@ async fn dac_task2(mut dac: DacCh2<'static, DAC1, DMA1_CH4>) { error!("Reload value {} below threshold!", reload); } - TIM7::enable_and_reset(); - TIM7::regs_basic().arr().modify(|w| w.set_arr(reload as u16 - 1)); - TIM7::regs_basic().cr2().modify(|w| w.set_mms(Mms::UPDATE)); - TIM7::regs_basic().cr1().modify(|w| { + let tim = Timer::new(tim); + tim.regs_basic().arr().modify(|w| w.set_arr(reload as u16 - 1)); + tim.regs_basic().cr2().modify(|w| w.set_mms(Mms::UPDATE)); + tim.regs_basic().cr1().modify(|w| { w.set_opm(false); w.set_cen(true); }); From 2bca875b5f72578cbd20404010d174795d263313 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Sat, 23 Mar 2024 01:38:51 +0100 Subject: [PATCH 25/62] stm32: use private_bounds for sealed traits. --- embassy-stm32/build.rs | 4 +- embassy-stm32/src/adc/f1.rs | 4 +- embassy-stm32/src/adc/f3.rs | 6 +- embassy-stm32/src/adc/f3_v1_1.rs | 4 +- embassy-stm32/src/adc/mod.rs | 100 +++-- embassy-stm32/src/adc/v1.rs | 6 +- embassy-stm32/src/adc/v2.rs | 6 +- embassy-stm32/src/adc/v3.rs | 8 +- embassy-stm32/src/adc/v4.rs | 8 +- embassy-stm32/src/can/bxcan.rs | 54 ++- embassy-stm32/src/can/fd/peripheral.rs | 61 +-- embassy-stm32/src/can/fdcan.rs | 378 +++++++++--------- embassy-stm32/src/crc/v1.rs | 2 +- embassy-stm32/src/crc/v2v3.rs | 2 +- embassy-stm32/src/cryp/mod.rs | 13 +- embassy-stm32/src/dac/mod.rs | 17 +- embassy-stm32/src/dcmi.rs | 14 +- embassy-stm32/src/dma/dmamux.rs | 9 +- embassy-stm32/src/dma/mod.rs | 24 +- embassy-stm32/src/dma/word.rs | 9 +- embassy-stm32/src/eth/mod.rs | 11 +- embassy-stm32/src/eth/v1/mod.rs | 23 +- embassy-stm32/src/eth/v2/mod.rs | 29 +- embassy-stm32/src/exti.rs | 11 +- embassy-stm32/src/fmc.rs | 18 +- embassy-stm32/src/gpio.rs | 305 +++++++------- embassy-stm32/src/hash/mod.rs | 15 +- embassy-stm32/src/hrtim/mod.rs | 17 +- embassy-stm32/src/hrtim/traits.rs | 128 +++--- embassy-stm32/src/i2c/mod.rs | 42 +- embassy-stm32/src/i2s.rs | 3 +- embassy-stm32/src/ipcc.rs | 89 ++--- embassy-stm32/src/lib.rs | 2 +- embassy-stm32/src/opamp.rs | 58 +-- embassy-stm32/src/qspi/mod.rs | 16 +- embassy-stm32/src/rcc/hsi48.rs | 2 +- embassy-stm32/src/rcc/mco.rs | 27 +- embassy-stm32/src/rcc/mod.rs | 41 +- embassy-stm32/src/rng.rs | 13 +- embassy-stm32/src/rtc/datetime.rs | 2 +- embassy-stm32/src/rtc/mod.rs | 57 ++- embassy-stm32/src/rtc/v2.rs | 5 +- embassy-stm32/src/rtc/v3.rs | 6 +- embassy-stm32/src/sai/mod.rs | 41 +- embassy-stm32/src/sdmmc/mod.rs | 29 +- embassy-stm32/src/spi/mod.rs | 37 +- embassy-stm32/src/time_driver.rs | 4 +- embassy-stm32/src/timer/qei.rs | 3 +- embassy-stm32/src/ucpd.rs | 60 ++- embassy-stm32/src/usart/mod.rs | 90 ++--- embassy-stm32/src/usb/mod.rs | 4 +- embassy-stm32/src/usb/otg.rs | 27 +- embassy-stm32/src/usb/usb.rs | 17 +- embassy-stm32/src/wdg/mod.rs | 11 +- examples/stm32h7/src/bin/dac_dma.rs | 14 +- .../stm32h7/src/bin/low_level_timer_api.rs | 35 +- examples/stm32l4/src/bin/dac_dma.rs | 14 +- 57 files changed, 955 insertions(+), 1080 deletions(-) diff --git a/embassy-stm32/build.rs b/embassy-stm32/build.rs index ee224da67..15bb8ea62 100644 --- a/embassy-stm32/build.rs +++ b/embassy-stm32/build.rs @@ -584,7 +584,7 @@ fn main() { }; g.extend(quote! { - impl crate::rcc::sealed::RccPeripheral for peripherals::#pname { + impl crate::rcc::SealedRccPeripheral for peripherals::#pname { fn frequency() -> crate::time::Hertz { #clock_frequency } @@ -1486,7 +1486,7 @@ fn main() { #[crate::interrupt] unsafe fn #irq () { #( - ::on_irq(); + ::on_irq(); )* } } diff --git a/embassy-stm32/src/adc/f1.rs b/embassy-stm32/src/adc/f1.rs index b27b99827..cecf67947 100644 --- a/embassy-stm32/src/adc/f1.rs +++ b/embassy-stm32/src/adc/f1.rs @@ -33,7 +33,7 @@ impl interrupt::typelevel::Handler for InterruptHandl pub struct Vref; impl AdcPin for Vref {} -impl super::sealed::AdcPin for Vref { +impl super::SealedAdcPin for Vref { fn channel(&self) -> u8 { 17 } @@ -41,7 +41,7 @@ impl super::sealed::AdcPin for Vref { pub struct Temperature; impl AdcPin for Temperature {} -impl super::sealed::AdcPin for Temperature { +impl super::SealedAdcPin for Temperature { fn channel(&self) -> u8 { 16 } diff --git a/embassy-stm32/src/adc/f3.rs b/embassy-stm32/src/adc/f3.rs index efade1f64..c5581dba1 100644 --- a/embassy-stm32/src/adc/f3.rs +++ b/embassy-stm32/src/adc/f3.rs @@ -33,7 +33,7 @@ impl interrupt::typelevel::Handler for InterruptHandl pub struct Vref; impl AdcPin for Vref {} -impl super::sealed::AdcPin for Vref { +impl super::SealedAdcPin for Vref { fn channel(&self) -> u8 { 18 } @@ -48,7 +48,7 @@ impl Vref { pub struct Temperature; impl AdcPin for Temperature {} -impl super::sealed::AdcPin for Temperature { +impl super::SealedAdcPin for Temperature { fn channel(&self) -> u8 { 16 } @@ -102,7 +102,7 @@ impl<'d, T: Instance> Adc<'d, T> { } fn freq() -> Hertz { - ::frequency() + ::frequency() } pub fn sample_time_for_us(&self, us: u32) -> SampleTime { diff --git a/embassy-stm32/src/adc/f3_v1_1.rs b/embassy-stm32/src/adc/f3_v1_1.rs index f842893fa..672ace04f 100644 --- a/embassy-stm32/src/adc/f3_v1_1.rs +++ b/embassy-stm32/src/adc/f3_v1_1.rs @@ -65,7 +65,7 @@ fn update_vref(op: i8) { pub struct Vref(core::marker::PhantomData); impl AdcPin for Vref {} -impl super::sealed::AdcPin for Vref { +impl super::SealedAdcPin for Vref { fn channel(&self) -> u8 { 17 } @@ -124,7 +124,7 @@ impl Drop for Vref { pub struct Temperature(core::marker::PhantomData); impl AdcPin for Temperature {} -impl super::sealed::AdcPin for Temperature { +impl super::SealedAdcPin for Temperature { fn channel(&self) -> u8 { 16 } diff --git a/embassy-stm32/src/adc/mod.rs b/embassy-stm32/src/adc/mod.rs index 0d0d40549..ead2357ce 100644 --- a/embassy-stm32/src/adc/mod.rs +++ b/embassy-stm32/src/adc/mod.rs @@ -17,6 +17,8 @@ mod _version; #[allow(unused)] #[cfg(not(adc_f3_v2))] pub use _version::*; +#[cfg(any(adc_f1, adc_f3, adc_v1, adc_l0, adc_f3_v1_1))] +use embassy_sync::waitqueue::AtomicWaker; #[cfg(not(any(adc_f1, adc_f3_v2)))] pub use crate::pac::adc::vals::Res as Resolution; @@ -31,63 +33,65 @@ pub struct Adc<'d, T: Instance> { sample_time: SampleTime, } -pub(crate) mod sealed { - #[cfg(any(adc_f1, adc_f3, adc_v1, adc_l0, adc_f3_v1_1))] - use embassy_sync::waitqueue::AtomicWaker; +#[cfg(any(adc_f1, adc_f3, adc_v1, adc_l0, adc_f3_v1_1))] +pub struct State { + pub waker: AtomicWaker, +} - #[cfg(any(adc_f1, adc_f3, adc_v1, adc_l0, adc_f3_v1_1))] - pub struct State { - pub waker: AtomicWaker, - } - - #[cfg(any(adc_f1, adc_f3, adc_v1, adc_l0, adc_f3_v1_1))] - impl State { - pub const fn new() -> Self { - Self { - waker: AtomicWaker::new(), - } +#[cfg(any(adc_f1, adc_f3, adc_v1, adc_l0, adc_f3_v1_1))] +impl State { + pub const fn new() -> Self { + Self { + waker: AtomicWaker::new(), } } +} - pub trait InterruptableInstance { - type Interrupt: crate::interrupt::typelevel::Interrupt; - } +trait SealedInstance { + #[allow(unused)] + fn regs() -> crate::pac::adc::Adc; + #[cfg(not(any(adc_f1, adc_v1, adc_l0, adc_f3_v2, adc_f3_v1_1, adc_g0)))] + fn common_regs() -> crate::pac::adccommon::AdcCommon; + #[cfg(any(adc_f1, adc_f3, adc_v1, adc_l0, adc_f3_v1_1))] + fn state() -> &'static State; +} - pub trait Instance: InterruptableInstance { - fn regs() -> crate::pac::adc::Adc; - #[cfg(not(any(adc_f1, adc_v1, adc_l0, adc_f3_v2, adc_f3_v1_1, adc_g0)))] - fn common_regs() -> crate::pac::adccommon::AdcCommon; - #[cfg(any(adc_f1, adc_f3, adc_v1, adc_l0, adc_f3_v1_1))] - fn state() -> &'static State; - } +pub(crate) trait SealedAdcPin { + #[cfg(any(adc_v1, adc_l0, adc_v2))] + fn set_as_analog(&mut self) {} - pub trait AdcPin { - #[cfg(any(adc_v1, adc_l0, adc_v2))] - fn set_as_analog(&mut self) {} + #[allow(unused)] + fn channel(&self) -> u8; +} - fn channel(&self) -> u8; - } - - pub trait InternalChannel { - fn channel(&self) -> u8; - } +trait SealedInternalChannel { + #[allow(unused)] + fn channel(&self) -> u8; } /// ADC instance. #[cfg(not(any(adc_f1, adc_v1, adc_l0, adc_v2, adc_v3, adc_v4, adc_f3, adc_f3_v1_1, adc_g0, adc_h5)))] -pub trait Instance: sealed::Instance + crate::Peripheral

{} +#[allow(private_bounds)] +pub trait Instance: SealedInstance + crate::Peripheral

{ + type Interrupt: crate::interrupt::typelevel::Interrupt; +} /// ADC instance. #[cfg(any(adc_f1, adc_v1, adc_l0, adc_v2, adc_v3, adc_v4, adc_f3, adc_f3_v1_1, adc_g0, adc_h5))] -pub trait Instance: sealed::Instance + crate::Peripheral

+ crate::rcc::RccPeripheral {} +#[allow(private_bounds)] +pub trait Instance: SealedInstance + crate::Peripheral

+ crate::rcc::RccPeripheral { + type Interrupt: crate::interrupt::typelevel::Interrupt; +} /// ADC pin. -pub trait AdcPin: sealed::AdcPin {} +#[allow(private_bounds)] +pub trait AdcPin: SealedAdcPin {} /// ADC internal channel. -pub trait InternalChannel: sealed::InternalChannel {} +#[allow(private_bounds)] +pub trait InternalChannel: SealedInternalChannel {} foreach_adc!( ($inst:ident, $common_inst:ident, $clock:ident) => { - impl crate::adc::sealed::Instance for peripherals::$inst { + impl crate::adc::SealedInstance for peripherals::$inst { fn regs() -> crate::pac::adc::Adc { crate::pac::$inst } @@ -98,21 +102,15 @@ foreach_adc!( } #[cfg(any(adc_f1, adc_f3, adc_v1, adc_l0, adc_f3_v1_1))] - fn state() -> &'static sealed::State { - static STATE: sealed::State = sealed::State::new(); + fn state() -> &'static State { + static STATE: State = State::new(); &STATE } } - foreach_interrupt!( - ($inst,adc,ADC,GLOBAL,$irq:ident) => { - impl sealed::InterruptableInstance for peripherals::$inst { - type Interrupt = crate::interrupt::typelevel::$irq; - } - }; - ); - - impl crate::adc::Instance for peripherals::$inst {} + impl crate::adc::Instance for peripherals::$inst { + type Interrupt = crate::_generated::peripheral_interrupts::$inst::GLOBAL; + } }; ); @@ -120,10 +118,10 @@ macro_rules! impl_adc_pin { ($inst:ident, $pin:ident, $ch:expr) => { impl crate::adc::AdcPin for crate::peripherals::$pin {} - impl crate::adc::sealed::AdcPin for crate::peripherals::$pin { + impl crate::adc::SealedAdcPin for crate::peripherals::$pin { #[cfg(any(adc_v1, adc_l0, adc_v2))] fn set_as_analog(&mut self) { - ::set_as_analog(self); + ::set_as_analog(self); } fn channel(&self) -> u8 { diff --git a/embassy-stm32/src/adc/v1.rs b/embassy-stm32/src/adc/v1.rs index a8dc6ce98..e9b46be80 100644 --- a/embassy-stm32/src/adc/v1.rs +++ b/embassy-stm32/src/adc/v1.rs @@ -39,7 +39,7 @@ pub struct Vbat; impl AdcPin for Vbat {} #[cfg(not(adc_l0))] -impl super::sealed::AdcPin for Vbat { +impl super::SealedAdcPin for Vbat { fn channel(&self) -> u8 { 18 } @@ -47,7 +47,7 @@ impl super::sealed::AdcPin for Vbat { pub struct Vref; impl AdcPin for Vref {} -impl super::sealed::AdcPin for Vref { +impl super::SealedAdcPin for Vref { fn channel(&self) -> u8 { 17 } @@ -55,7 +55,7 @@ impl super::sealed::AdcPin for Vref { pub struct Temperature; impl AdcPin for Temperature {} -impl super::sealed::AdcPin for Temperature { +impl super::SealedAdcPin for Temperature { fn channel(&self) -> u8 { 16 } diff --git a/embassy-stm32/src/adc/v2.rs b/embassy-stm32/src/adc/v2.rs index f6f7dbfcc..a43eb72db 100644 --- a/embassy-stm32/src/adc/v2.rs +++ b/embassy-stm32/src/adc/v2.rs @@ -16,7 +16,7 @@ pub const ADC_POWERUP_TIME_US: u32 = 3; pub struct VrefInt; impl AdcPin for VrefInt {} -impl super::sealed::AdcPin for VrefInt { +impl super::SealedAdcPin for VrefInt { fn channel(&self) -> u8 { 17 } @@ -31,7 +31,7 @@ impl VrefInt { pub struct Temperature; impl AdcPin for Temperature {} -impl super::sealed::AdcPin for Temperature { +impl super::SealedAdcPin for Temperature { fn channel(&self) -> u8 { cfg_if::cfg_if! { if #[cfg(any(stm32f2, stm32f40, stm32f41))] { @@ -52,7 +52,7 @@ impl Temperature { pub struct Vbat; impl AdcPin for Vbat {} -impl super::sealed::AdcPin for Vbat { +impl super::SealedAdcPin for Vbat { fn channel(&self) -> u8 { 18 } diff --git a/embassy-stm32/src/adc/v3.rs b/embassy-stm32/src/adc/v3.rs index 5f3512cad..8c9b47197 100644 --- a/embassy-stm32/src/adc/v3.rs +++ b/embassy-stm32/src/adc/v3.rs @@ -12,7 +12,7 @@ pub const VREF_CALIB_MV: u32 = 3000; pub struct VrefInt; impl AdcPin for VrefInt {} -impl super::sealed::AdcPin for VrefInt { +impl super::SealedAdcPin for VrefInt { fn channel(&self) -> u8 { cfg_if! { if #[cfg(adc_g0)] { @@ -29,7 +29,7 @@ impl super::sealed::AdcPin for VrefInt { pub struct Temperature; impl AdcPin for Temperature {} -impl super::sealed::AdcPin for Temperature { +impl super::SealedAdcPin for Temperature { fn channel(&self) -> u8 { cfg_if! { if #[cfg(adc_g0)] { @@ -46,7 +46,7 @@ impl super::sealed::AdcPin for Temperature { pub struct Vbat; impl AdcPin for Vbat {} -impl super::sealed::AdcPin for Vbat { +impl super::SealedAdcPin for Vbat { fn channel(&self) -> u8 { cfg_if! { if #[cfg(adc_g0)] { @@ -65,7 +65,7 @@ cfg_if! { if #[cfg(adc_h5)] { pub struct VddCore; impl AdcPin for VddCore {} - impl super::sealed::AdcPin for VddCore { + impl super::SealedAdcPin for VddCore { fn channel(&self) -> u8 { 6 } diff --git a/embassy-stm32/src/adc/v4.rs b/embassy-stm32/src/adc/v4.rs index 3fd047375..1ae25bea2 100644 --- a/embassy-stm32/src/adc/v4.rs +++ b/embassy-stm32/src/adc/v4.rs @@ -35,7 +35,7 @@ const VBAT_CHANNEL: u8 = 17; /// Internal voltage reference channel. pub struct VrefInt; impl InternalChannel for VrefInt {} -impl super::sealed::InternalChannel for VrefInt { +impl super::SealedInternalChannel for VrefInt { fn channel(&self) -> u8 { VREF_CHANNEL } @@ -44,7 +44,7 @@ impl super::sealed::InternalChannel for VrefInt { /// Internal temperature channel. pub struct Temperature; impl InternalChannel for Temperature {} -impl super::sealed::InternalChannel for Temperature { +impl super::SealedInternalChannel for Temperature { fn channel(&self) -> u8 { TEMP_CHANNEL } @@ -53,7 +53,7 @@ impl super::sealed::InternalChannel for Temperature { /// Internal battery voltage channel. pub struct Vbat; impl InternalChannel for Vbat {} -impl super::sealed::InternalChannel for Vbat { +impl super::SealedInternalChannel for Vbat { fn channel(&self) -> u8 { VBAT_CHANNEL } @@ -276,7 +276,7 @@ impl<'d, T: Instance> Adc<'d, T> { pub fn read

(&mut self, pin: &mut P) -> u16 where P: AdcPin, - P: crate::gpio::sealed::Pin, + P: crate::gpio::Pin, { pin.set_as_analog(); diff --git a/embassy-stm32/src/can/bxcan.rs b/embassy-stm32/src/can/bxcan.rs index 1a625bdc4..017c5110d 100644 --- a/embassy-stm32/src/can/bxcan.rs +++ b/embassy-stm32/src/can/bxcan.rs @@ -7,9 +7,12 @@ pub mod bx; pub use bx::{filter, Data, ExtendedId, Fifo, Frame, Header, Id, StandardId}; use embassy_hal_internal::{into_ref, PeripheralRef}; +use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex; +use embassy_sync::channel::Channel; +use embassy_sync::waitqueue::AtomicWaker; use futures::FutureExt; -use crate::gpio::sealed::AFType; +use crate::gpio::AFType; use crate::interrupt::typelevel::Interrupt; use crate::pac::can::vals::{Ide, Lec}; use crate::rcc::RccPeripheral; @@ -485,37 +488,30 @@ impl<'d, T: Instance> DerefMut for Can<'d, T> { } } -pub(crate) mod sealed { - use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex; - use embassy_sync::channel::Channel; - use embassy_sync::waitqueue::AtomicWaker; +struct State { + pub tx_waker: AtomicWaker, + pub err_waker: AtomicWaker, + pub rx_queue: Channel, +} - use super::Envelope; - - pub struct State { - pub tx_waker: AtomicWaker, - pub err_waker: AtomicWaker, - pub rx_queue: Channel, - } - - impl State { - pub const fn new() -> Self { - Self { - tx_waker: AtomicWaker::new(), - err_waker: AtomicWaker::new(), - rx_queue: Channel::new(), - } +impl State { + pub const fn new() -> Self { + Self { + tx_waker: AtomicWaker::new(), + err_waker: AtomicWaker::new(), + rx_queue: Channel::new(), } } - - pub trait Instance { - fn regs() -> crate::pac::can::Can; - fn state() -> &'static State; - } +} + +trait SealedInstance { + fn regs() -> crate::pac::can::Can; + fn state() -> &'static State; } /// CAN instance trait. -pub trait Instance: sealed::Instance + RccPeripheral + 'static { +#[allow(private_bounds)] +pub trait Instance: SealedInstance + RccPeripheral + 'static { /// TX interrupt for this instance. type TXInterrupt: crate::interrupt::typelevel::Interrupt; /// RX0 interrupt for this instance. @@ -533,14 +529,14 @@ unsafe impl<'d, T: Instance> crate::can::bx::Instance for BxcanInstance<'d, T> { foreach_peripheral!( (can, $inst:ident) => { - impl sealed::Instance for peripherals::$inst { + impl SealedInstance for peripherals::$inst { fn regs() -> crate::pac::can::Can { crate::pac::$inst } - fn state() -> &'static sealed::State { - static STATE: sealed::State = sealed::State::new(); + fn state() -> &'static State { + static STATE: State = State::new(); &STATE } } diff --git a/embassy-stm32/src/can/fd/peripheral.rs b/embassy-stm32/src/can/fd/peripheral.rs index 682e13f4b..76b76afe1 100644 --- a/embassy-stm32/src/can/fd/peripheral.rs +++ b/embassy-stm32/src/can/fd/peripheral.rs @@ -325,17 +325,6 @@ impl Registers { */ } - /// Disables the CAN interface and returns back the raw peripheral it was created from. - #[inline] - pub fn free(mut self) { - //self.disable_interrupts(Interrupts::all()); - - //TODO check this! - self.enter_init_mode(); - self.set_power_down_mode(true); - //self.control.instance - } - /// Applies the settings of a new FdCanConfig See [`FdCanConfig`] #[inline] pub fn apply_config(&mut self, config: FdCanConfig) { @@ -419,55 +408,6 @@ impl Registers { self.leave_init_mode(config); } - /// Moves out of ConfigMode and into InternalLoopbackMode - #[inline] - pub fn into_internal_loopback(mut self, config: FdCanConfig) { - self.set_loopback_mode(LoopbackMode::Internal); - self.leave_init_mode(config); - } - - /// Moves out of ConfigMode and into ExternalLoopbackMode - #[inline] - pub fn into_external_loopback(mut self, config: FdCanConfig) { - self.set_loopback_mode(LoopbackMode::External); - self.leave_init_mode(config); - } - - /// Moves out of ConfigMode and into RestrictedOperationMode - #[inline] - pub fn into_restricted(mut self, config: FdCanConfig) { - self.set_restricted_operations(true); - self.leave_init_mode(config); - } - - /// Moves out of ConfigMode and into NormalOperationMode - #[inline] - pub fn into_normal(mut self, config: FdCanConfig) { - self.set_normal_operations(true); - self.leave_init_mode(config); - } - - /// Moves out of ConfigMode and into BusMonitoringMode - #[inline] - pub fn into_bus_monitoring(mut self, config: FdCanConfig) { - self.set_bus_monitoring_mode(true); - self.leave_init_mode(config); - } - - /// Moves out of ConfigMode and into Testmode - #[inline] - pub fn into_test_mode(mut self, config: FdCanConfig) { - self.set_test_mode(true); - self.leave_init_mode(config); - } - - /// Moves out of ConfigMode and into PoweredDownmode - #[inline] - pub fn into_powered_down(mut self, config: FdCanConfig) { - self.set_power_down_mode(true); - self.leave_init_mode(config); - } - /// Configures the bit timings. /// /// You can use to calculate the `btr` parameter. Enter @@ -565,6 +505,7 @@ impl Registers { /// Configures and resets the timestamp counter #[inline] + #[allow(unused)] pub fn set_timestamp_counter_source(&mut self, select: TimestampSource) { #[cfg(stm32h7)] let (tcp, tss) = match select { diff --git a/embassy-stm32/src/can/fdcan.rs b/embassy-stm32/src/can/fdcan.rs index 4c40f10e3..4ea036ab4 100644 --- a/embassy-stm32/src/can/fdcan.rs +++ b/embassy-stm32/src/can/fdcan.rs @@ -5,10 +5,11 @@ use core::task::Poll; use embassy_hal_internal::{into_ref, PeripheralRef}; use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex; -use embassy_sync::channel::Channel; +use embassy_sync::channel::{Channel, DynamicReceiver, DynamicSender}; +use embassy_sync::waitqueue::AtomicWaker; use crate::can::fd::peripheral::Registers; -use crate::gpio::sealed::AFType; +use crate::gpio::AFType; use crate::interrupt::typelevel::Interrupt; use crate::rcc::RccPeripheral; use crate::{interrupt, peripherals, Peripheral}; @@ -53,8 +54,8 @@ impl interrupt::typelevel::Handler for IT0Interrup } match &T::state().tx_mode { - sealed::TxMode::NonBuffered(waker) => waker.wake(), - sealed::TxMode::ClassicBuffered(buf) => { + TxMode::NonBuffered(waker) => waker.wake(), + TxMode::ClassicBuffered(buf) => { if !T::registers().tx_queue_is_full() { match buf.tx_receiver.try_receive() { Ok(frame) => { @@ -64,7 +65,7 @@ impl interrupt::typelevel::Handler for IT0Interrup } } } - sealed::TxMode::FdBuffered(buf) => { + TxMode::FdBuffered(buf) => { if !T::registers().tx_queue_is_full() { match buf.tx_receiver.try_receive() { Ok(frame) => { @@ -467,14 +468,14 @@ impl<'c, 'd, T: Instance, const TX_BUF_SIZE: usize, const RX_BUF_SIZE: usize> fn setup(self) -> Self { // We don't want interrupts being processed while we change modes. critical_section::with(|_| unsafe { - let rx_inner = sealed::ClassicBufferedRxInner { + let rx_inner = ClassicBufferedRxInner { rx_sender: self.rx_buf.sender().into(), }; - let tx_inner = sealed::ClassicBufferedTxInner { + let tx_inner = ClassicBufferedTxInner { tx_receiver: self.tx_buf.receiver().into(), }; - T::mut_state().rx_mode = sealed::RxMode::ClassicBuffered(rx_inner); - T::mut_state().tx_mode = sealed::TxMode::ClassicBuffered(tx_inner); + T::mut_state().rx_mode = RxMode::ClassicBuffered(rx_inner); + T::mut_state().tx_mode = TxMode::ClassicBuffered(tx_inner); }); self } @@ -509,8 +510,8 @@ impl<'c, 'd, T: Instance, const TX_BUF_SIZE: usize, const RX_BUF_SIZE: usize> Dr { fn drop(&mut self) { critical_section::with(|_| unsafe { - T::mut_state().rx_mode = sealed::RxMode::NonBuffered(embassy_sync::waitqueue::AtomicWaker::new()); - T::mut_state().tx_mode = sealed::TxMode::NonBuffered(embassy_sync::waitqueue::AtomicWaker::new()); + T::mut_state().rx_mode = RxMode::NonBuffered(embassy_sync::waitqueue::AtomicWaker::new()); + T::mut_state().tx_mode = TxMode::NonBuffered(embassy_sync::waitqueue::AtomicWaker::new()); }); } } @@ -585,14 +586,14 @@ impl<'c, 'd, T: Instance, const TX_BUF_SIZE: usize, const RX_BUF_SIZE: usize> fn setup(self) -> Self { // We don't want interrupts being processed while we change modes. critical_section::with(|_| unsafe { - let rx_inner = sealed::FdBufferedRxInner { + let rx_inner = FdBufferedRxInner { rx_sender: self.rx_buf.sender().into(), }; - let tx_inner = sealed::FdBufferedTxInner { + let tx_inner = FdBufferedTxInner { tx_receiver: self.tx_buf.receiver().into(), }; - T::mut_state().rx_mode = sealed::RxMode::FdBuffered(rx_inner); - T::mut_state().tx_mode = sealed::TxMode::FdBuffered(tx_inner); + T::mut_state().rx_mode = RxMode::FdBuffered(rx_inner); + T::mut_state().tx_mode = TxMode::FdBuffered(tx_inner); }); self } @@ -627,8 +628,8 @@ impl<'c, 'd, T: Instance, const TX_BUF_SIZE: usize, const RX_BUF_SIZE: usize> Dr { fn drop(&mut self) { critical_section::with(|_| unsafe { - T::mut_state().rx_mode = sealed::RxMode::NonBuffered(embassy_sync::waitqueue::AtomicWaker::new()); - T::mut_state().tx_mode = sealed::TxMode::NonBuffered(embassy_sync::waitqueue::AtomicWaker::new()); + T::mut_state().rx_mode = RxMode::NonBuffered(embassy_sync::waitqueue::AtomicWaker::new()); + T::mut_state().tx_mode = TxMode::NonBuffered(embassy_sync::waitqueue::AtomicWaker::new()); }); } } @@ -677,192 +678,180 @@ impl<'c, 'd, T: Instance> FdcanRx<'d, T> { } } -pub(crate) mod sealed { - use core::future::poll_fn; - use core::task::Poll; +struct ClassicBufferedRxInner { + rx_sender: DynamicSender<'static, Result<(ClassicFrame, Timestamp), BusError>>, +} +struct ClassicBufferedTxInner { + tx_receiver: DynamicReceiver<'static, ClassicFrame>, +} - use embassy_sync::channel::{DynamicReceiver, DynamicSender}; - use embassy_sync::waitqueue::AtomicWaker; +struct FdBufferedRxInner { + rx_sender: DynamicSender<'static, Result<(FdFrame, Timestamp), BusError>>, +} +struct FdBufferedTxInner { + tx_receiver: DynamicReceiver<'static, FdFrame>, +} - use super::CanHeader; - use crate::can::_version::{BusError, Timestamp}; - use crate::can::frame::{ClassicFrame, FdFrame}; +enum RxMode { + NonBuffered(AtomicWaker), + ClassicBuffered(ClassicBufferedRxInner), + FdBuffered(FdBufferedRxInner), +} - pub struct ClassicBufferedRxInner { - pub rx_sender: DynamicSender<'static, Result<(ClassicFrame, Timestamp), BusError>>, - } - pub struct ClassicBufferedTxInner { - pub tx_receiver: DynamicReceiver<'static, ClassicFrame>, - } - - pub struct FdBufferedRxInner { - pub rx_sender: DynamicSender<'static, Result<(FdFrame, Timestamp), BusError>>, - } - pub struct FdBufferedTxInner { - pub tx_receiver: DynamicReceiver<'static, FdFrame>, - } - - pub enum RxMode { - NonBuffered(AtomicWaker), - ClassicBuffered(ClassicBufferedRxInner), - FdBuffered(FdBufferedRxInner), - } - - impl RxMode { - pub fn register(&self, arg: &core::task::Waker) { - match self { - RxMode::NonBuffered(waker) => waker.register(arg), - _ => { - panic!("Bad Mode") - } - } - } - - pub fn on_interrupt(&self, fifonr: usize) { - T::regs().ir().write(|w| w.set_rfn(fifonr, true)); - match self { - RxMode::NonBuffered(waker) => { - waker.wake(); - } - RxMode::ClassicBuffered(buf) => { - if let Some(result) = self.read::() { - let _ = buf.rx_sender.try_send(result); - } - } - RxMode::FdBuffered(buf) => { - if let Some(result) = self.read::() { - let _ = buf.rx_sender.try_send(result); - } - } - } - } - - fn read(&self) -> Option> { - if let Some((msg, ts)) = T::registers().read(0) { - let ts = T::calc_timestamp(T::state().ns_per_timer_tick, ts); - Some(Ok((msg, ts))) - } else if let Some((msg, ts)) = T::registers().read(1) { - let ts = T::calc_timestamp(T::state().ns_per_timer_tick, ts); - Some(Ok((msg, ts))) - } else if let Some(err) = T::registers().curr_error() { - // TODO: this is probably wrong - Some(Err(err)) - } else { - None - } - } - - async fn read_async(&self) -> Result<(F, Timestamp), BusError> { - poll_fn(|cx| { - T::state().err_waker.register(cx.waker()); - self.register(cx.waker()); - match self.read::() { - Some(result) => Poll::Ready(result), - None => Poll::Pending, - } - }) - .await - } - - pub async fn read_classic(&self) -> Result<(ClassicFrame, Timestamp), BusError> { - self.read_async::().await - } - - pub async fn read_fd(&self) -> Result<(FdFrame, Timestamp), BusError> { - self.read_async::().await - } - } - - pub enum TxMode { - NonBuffered(AtomicWaker), - ClassicBuffered(ClassicBufferedTxInner), - FdBuffered(FdBufferedTxInner), - } - - impl TxMode { - pub fn register(&self, arg: &core::task::Waker) { - match self { - TxMode::NonBuffered(waker) => { - waker.register(arg); - } - _ => { - panic!("Bad mode"); - } - } - } - - /// Queues the message to be sent but exerts backpressure. If a lower-priority - /// frame is dropped from the mailbox, it is returned. If no lower-priority frames - /// can be replaced, this call asynchronously waits for a frame to be successfully - /// transmitted, then tries again. - async fn write_generic(&self, frame: &F) -> Option { - poll_fn(|cx| { - self.register(cx.waker()); - - if let Ok(dropped) = T::registers().write(frame) { - return Poll::Ready(dropped); - } - - // Couldn't replace any lower priority frames. Need to wait for some mailboxes - // to clear. - Poll::Pending - }) - .await - } - - /// Queues the message to be sent but exerts backpressure. If a lower-priority - /// frame is dropped from the mailbox, it is returned. If no lower-priority frames - /// can be replaced, this call asynchronously waits for a frame to be successfully - /// transmitted, then tries again. - pub async fn write(&self, frame: &ClassicFrame) -> Option { - self.write_generic::(frame).await - } - - /// Queues the message to be sent but exerts backpressure. If a lower-priority - /// frame is dropped from the mailbox, it is returned. If no lower-priority frames - /// can be replaced, this call asynchronously waits for a frame to be successfully - /// transmitted, then tries again. - pub async fn write_fd(&self, frame: &FdFrame) -> Option { - self.write_generic::(frame).await - } - } - - pub struct State { - pub rx_mode: RxMode, - pub tx_mode: TxMode, - pub ns_per_timer_tick: u64, - - pub err_waker: AtomicWaker, - } - - impl State { - pub const fn new() -> Self { - Self { - rx_mode: RxMode::NonBuffered(AtomicWaker::new()), - tx_mode: TxMode::NonBuffered(AtomicWaker::new()), - ns_per_timer_tick: 0, - err_waker: AtomicWaker::new(), +impl RxMode { + fn register(&self, arg: &core::task::Waker) { + match self { + RxMode::NonBuffered(waker) => waker.register(arg), + _ => { + panic!("Bad Mode") } } } - pub trait Instance { - const MSG_RAM_OFFSET: usize; + fn on_interrupt(&self, fifonr: usize) { + T::regs().ir().write(|w| w.set_rfn(fifonr, true)); + match self { + RxMode::NonBuffered(waker) => { + waker.wake(); + } + RxMode::ClassicBuffered(buf) => { + if let Some(result) = self.read::() { + let _ = buf.rx_sender.try_send(result); + } + } + RxMode::FdBuffered(buf) => { + if let Some(result) = self.read::() { + let _ = buf.rx_sender.try_send(result); + } + } + } + } - fn regs() -> &'static crate::pac::can::Fdcan; - fn registers() -> crate::can::fd::peripheral::Registers; - fn ram() -> &'static crate::pac::fdcanram::Fdcanram; - fn state() -> &'static State; - unsafe fn mut_state() -> &'static mut State; - fn calc_timestamp(ns_per_timer_tick: u64, ts_val: u16) -> Timestamp; + fn read(&self) -> Option> { + if let Some((msg, ts)) = T::registers().read(0) { + let ts = T::calc_timestamp(T::state().ns_per_timer_tick, ts); + Some(Ok((msg, ts))) + } else if let Some((msg, ts)) = T::registers().read(1) { + let ts = T::calc_timestamp(T::state().ns_per_timer_tick, ts); + Some(Ok((msg, ts))) + } else if let Some(err) = T::registers().curr_error() { + // TODO: this is probably wrong + Some(Err(err)) + } else { + None + } + } + + async fn read_async(&self) -> Result<(F, Timestamp), BusError> { + poll_fn(|cx| { + T::state().err_waker.register(cx.waker()); + self.register(cx.waker()); + match self.read::() { + Some(result) => Poll::Ready(result), + None => Poll::Pending, + } + }) + .await + } + + async fn read_classic(&self) -> Result<(ClassicFrame, Timestamp), BusError> { + self.read_async::().await + } + + async fn read_fd(&self) -> Result<(FdFrame, Timestamp), BusError> { + self.read_async::().await } } +enum TxMode { + NonBuffered(AtomicWaker), + ClassicBuffered(ClassicBufferedTxInner), + FdBuffered(FdBufferedTxInner), +} + +impl TxMode { + fn register(&self, arg: &core::task::Waker) { + match self { + TxMode::NonBuffered(waker) => { + waker.register(arg); + } + _ => { + panic!("Bad mode"); + } + } + } + + /// Queues the message to be sent but exerts backpressure. If a lower-priority + /// frame is dropped from the mailbox, it is returned. If no lower-priority frames + /// can be replaced, this call asynchronously waits for a frame to be successfully + /// transmitted, then tries again. + async fn write_generic(&self, frame: &F) -> Option { + poll_fn(|cx| { + self.register(cx.waker()); + + if let Ok(dropped) = T::registers().write(frame) { + return Poll::Ready(dropped); + } + + // Couldn't replace any lower priority frames. Need to wait for some mailboxes + // to clear. + Poll::Pending + }) + .await + } + + /// Queues the message to be sent but exerts backpressure. If a lower-priority + /// frame is dropped from the mailbox, it is returned. If no lower-priority frames + /// can be replaced, this call asynchronously waits for a frame to be successfully + /// transmitted, then tries again. + async fn write(&self, frame: &ClassicFrame) -> Option { + self.write_generic::(frame).await + } + + /// Queues the message to be sent but exerts backpressure. If a lower-priority + /// frame is dropped from the mailbox, it is returned. If no lower-priority frames + /// can be replaced, this call asynchronously waits for a frame to be successfully + /// transmitted, then tries again. + async fn write_fd(&self, frame: &FdFrame) -> Option { + self.write_generic::(frame).await + } +} + +struct State { + pub rx_mode: RxMode, + pub tx_mode: TxMode, + pub ns_per_timer_tick: u64, + + pub err_waker: AtomicWaker, +} + +impl State { + const fn new() -> Self { + Self { + rx_mode: RxMode::NonBuffered(AtomicWaker::new()), + tx_mode: TxMode::NonBuffered(AtomicWaker::new()), + ns_per_timer_tick: 0, + err_waker: AtomicWaker::new(), + } + } +} + +trait SealedInstance { + const MSG_RAM_OFFSET: usize; + + fn regs() -> &'static crate::pac::can::Fdcan; + fn registers() -> crate::can::fd::peripheral::Registers; + fn state() -> &'static State; + unsafe fn mut_state() -> &'static mut State; + fn calc_timestamp(ns_per_timer_tick: u64, ts_val: u16) -> Timestamp; +} + /// Instance trait -pub trait Instance: sealed::Instance + RccPeripheral + 'static { +#[allow(private_bounds)] +pub trait Instance: SealedInstance + RccPeripheral + 'static { /// Interrupt 0 type IT0Interrupt: crate::interrupt::typelevel::Interrupt; - /// Interrupt 0 + /// Interrupt 1 type IT1Interrupt: crate::interrupt::typelevel::Interrupt; } @@ -871,7 +860,7 @@ pub struct FdcanInstance<'a, T>(PeripheralRef<'a, T>); macro_rules! impl_fdcan { ($inst:ident, $msg_ram_inst:ident, $msg_ram_offset:literal) => { - impl sealed::Instance for peripherals::$inst { + impl SealedInstance for peripherals::$inst { const MSG_RAM_OFFSET: usize = $msg_ram_offset; fn regs() -> &'static crate::pac::can::Fdcan { @@ -880,14 +869,11 @@ macro_rules! impl_fdcan { fn registers() -> Registers { Registers{regs: &crate::pac::$inst, msgram: &crate::pac::$msg_ram_inst, msg_ram_offset: Self::MSG_RAM_OFFSET} } - fn ram() -> &'static crate::pac::fdcanram::Fdcanram { - &crate::pac::$msg_ram_inst - } - unsafe fn mut_state() -> &'static mut sealed::State { - static mut STATE: sealed::State = sealed::State::new(); + unsafe fn mut_state() -> &'static mut State { + static mut STATE: State = State::new(); &mut *core::ptr::addr_of_mut!(STATE) } - fn state() -> &'static sealed::State { + fn state() -> &'static State { unsafe { peripherals::$inst::mut_state() } } diff --git a/embassy-stm32/src/crc/v1.rs b/embassy-stm32/src/crc/v1.rs index 0166ab819..f8909d438 100644 --- a/embassy-stm32/src/crc/v1.rs +++ b/embassy-stm32/src/crc/v1.rs @@ -2,7 +2,7 @@ use embassy_hal_internal::{into_ref, PeripheralRef}; use crate::pac::CRC as PAC_CRC; use crate::peripherals::CRC; -use crate::rcc::sealed::RccPeripheral; +use crate::rcc::SealedRccPeripheral; use crate::Peripheral; /// CRC driver. diff --git a/embassy-stm32/src/crc/v2v3.rs b/embassy-stm32/src/crc/v2v3.rs index 0c4ae55ce..46f5ea1be 100644 --- a/embassy-stm32/src/crc/v2v3.rs +++ b/embassy-stm32/src/crc/v2v3.rs @@ -3,7 +3,7 @@ use embassy_hal_internal::{into_ref, PeripheralRef}; use crate::pac::crc::vals; use crate::pac::CRC as PAC_CRC; use crate::peripherals::CRC; -use crate::rcc::sealed::RccPeripheral; +use crate::rcc::SealedRccPeripheral; use crate::Peripheral; /// CRC driver. diff --git a/embassy-stm32/src/cryp/mod.rs b/embassy-stm32/src/cryp/mod.rs index 74b095b6f..18b5ec918 100644 --- a/embassy-stm32/src/cryp/mod.rs +++ b/embassy-stm32/src/cryp/mod.rs @@ -1885,16 +1885,13 @@ impl<'d, T: Instance, DmaIn, DmaOut> Cryp<'d, T, DmaIn, DmaOut> { } } -pub(crate) mod sealed { - use super::*; - - pub trait Instance { - fn regs() -> pac::cryp::Cryp; - } +trait SealedInstance { + fn regs() -> pac::cryp::Cryp; } /// CRYP instance trait. -pub trait Instance: sealed::Instance + Peripheral

+ crate::rcc::RccPeripheral + 'static + Send { +#[allow(private_bounds)] +pub trait Instance: SealedInstance + Peripheral

+ crate::rcc::RccPeripheral + 'static + Send { /// Interrupt for this CRYP instance. type Interrupt: interrupt::typelevel::Interrupt; } @@ -1905,7 +1902,7 @@ foreach_interrupt!( type Interrupt = crate::interrupt::typelevel::$irq; } - impl sealed::Instance for peripherals::$inst { + impl SealedInstance for peripherals::$inst { fn regs() -> crate::pac::cryp::Cryp { crate::pac::$inst } diff --git a/embassy-stm32/src/dac/mod.rs b/embassy-stm32/src/dac/mod.rs index 60f9404c2..acfed8356 100644 --- a/embassy-stm32/src/dac/mod.rs +++ b/embassy-stm32/src/dac/mod.rs @@ -127,7 +127,7 @@ impl<'d, T: Instance, const N: u8, DMA> DacChannel<'d, T, N, DMA> { pub fn new( _peri: impl Peripheral

+ 'd, dma: impl Peripheral

+ 'd, - pin: impl Peripheral

+ crate::gpio::sealed::Pin> + 'd, + pin: impl Peripheral

+ crate::gpio::Pin> + 'd, ) -> Self { into_ref!(dma, pin); pin.set_as_analog(); @@ -392,8 +392,8 @@ impl<'d, T: Instance, DMACh1, DMACh2> Dac<'d, T, DMACh1, DMACh2> { _peri: impl Peripheral

+ 'd, dma_ch1: impl Peripheral

+ 'd, dma_ch2: impl Peripheral

+ 'd, - pin_ch1: impl Peripheral

+ crate::gpio::sealed::Pin> + 'd, - pin_ch2: impl Peripheral

+ crate::gpio::sealed::Pin> + 'd, + pin_ch1: impl Peripheral

+ crate::gpio::Pin> + 'd, + pin_ch2: impl Peripheral

+ crate::gpio::Pin> + 'd, ) -> Self { into_ref!(dma_ch1, dma_ch2, pin_ch1, pin_ch2); pin_ch1.set_as_analog(); @@ -488,14 +488,13 @@ impl<'d, T: Instance, DMACh1, DMACh2> Dac<'d, T, DMACh1, DMACh2> { } } -pub(crate) mod sealed { - pub trait Instance { - fn regs() -> &'static crate::pac::dac::Dac; - } +trait SealedInstance { + fn regs() -> &'static crate::pac::dac::Dac; } /// DAC instance. -pub trait Instance: sealed::Instance + RccPeripheral + 'static {} +#[allow(private_bounds)] +pub trait Instance: SealedInstance + RccPeripheral + 'static {} dma_trait!(DacDma1, Instance); dma_trait!(DacDma2, Instance); @@ -504,7 +503,7 @@ pub trait DacPin: crate::gpio::Pin + 'static {} foreach_peripheral!( (dac, $inst:ident) => { - impl crate::dac::sealed::Instance for peripherals::$inst { + impl crate::dac::SealedInstance for peripherals::$inst { fn regs() -> &'static crate::pac::dac::Dac { &crate::pac::$inst } diff --git a/embassy-stm32/src/dcmi.rs b/embassy-stm32/src/dcmi.rs index 826b04a4b..646ee2ce2 100644 --- a/embassy-stm32/src/dcmi.rs +++ b/embassy-stm32/src/dcmi.rs @@ -7,8 +7,7 @@ use embassy_hal_internal::{into_ref, PeripheralRef}; use embassy_sync::waitqueue::AtomicWaker; use crate::dma::Transfer; -use crate::gpio::sealed::AFType; -use crate::gpio::Speed; +use crate::gpio::{AFType, Speed}; use crate::interrupt::typelevel::Interrupt; use crate::{interrupt, Peripheral}; @@ -431,14 +430,13 @@ where } } -mod sealed { - pub trait Instance: crate::rcc::RccPeripheral { - fn regs(&self) -> crate::pac::dcmi::Dcmi; - } +trait SealedInstance: crate::rcc::RccPeripheral { + fn regs(&self) -> crate::pac::dcmi::Dcmi; } /// DCMI instance. -pub trait Instance: sealed::Instance + 'static { +#[allow(private_bounds)] +pub trait Instance: SealedInstance + 'static { /// Interrupt for this instance. type Interrupt: interrupt::typelevel::Interrupt; } @@ -465,7 +463,7 @@ pin_trait!(PixClkPin, Instance); #[allow(unused)] macro_rules! impl_peripheral { ($inst:ident, $irq:ident) => { - impl sealed::Instance for crate::peripherals::$inst { + impl SealedInstance for crate::peripherals::$inst { fn regs(&self) -> crate::pac::dcmi::Dcmi { crate::pac::$inst } diff --git a/embassy-stm32/src/dma/dmamux.rs b/embassy-stm32/src/dma/dmamux.rs index 1e9ab5944..dc7cd3a66 100644 --- a/embassy-stm32/src/dma/dmamux.rs +++ b/embassy-stm32/src/dma/dmamux.rs @@ -19,9 +19,7 @@ pub(crate) fn configure_dmamux(info: &DmamuxInfo, request: u8) { }); } -pub(crate) mod dmamux_sealed { - pub trait MuxChannel {} -} +pub(crate) trait SealedMuxChannel {} /// DMAMUX1 instance. pub struct DMAMUX1; @@ -30,14 +28,15 @@ pub struct DMAMUX1; pub struct DMAMUX2; /// DMAMUX channel trait. -pub trait MuxChannel: dmamux_sealed::MuxChannel { +#[allow(private_bounds)] +pub trait MuxChannel: SealedMuxChannel { /// DMAMUX instance this channel is on. type Mux; } macro_rules! dmamux_channel_impl { ($channel_peri:ident, $dmamux:ident) => { - impl crate::dma::dmamux_sealed::MuxChannel for crate::peripherals::$channel_peri {} + impl crate::dma::SealedMuxChannel for crate::peripherals::$channel_peri {} impl crate::dma::MuxChannel for crate::peripherals::$channel_peri { type Mux = crate::dma::$dmamux; } diff --git a/embassy-stm32/src/dma/mod.rs b/embassy-stm32/src/dma/mod.rs index 2f98b9857..7e3681469 100644 --- a/embassy-stm32/src/dma/mod.rs +++ b/embassy-stm32/src/dma/mod.rs @@ -39,18 +39,18 @@ pub type Request = u8; #[cfg(not(any(dma_v2, bdma_v2, gpdma, dmamux)))] pub type Request = (); -pub(crate) mod sealed { - pub trait Channel { - fn id(&self) -> u8; - } - pub trait ChannelInterrupt { - #[cfg_attr(not(feature = "rt"), allow(unused))] - unsafe fn on_irq(); - } +pub(crate) trait SealedChannel { + fn id(&self) -> u8; +} + +pub(crate) trait ChannelInterrupt { + #[cfg_attr(not(feature = "rt"), allow(unused))] + unsafe fn on_irq(); } /// DMA channel. -pub trait Channel: sealed::Channel + Peripheral

+ Into + 'static { +#[allow(private_bounds)] +pub trait Channel: SealedChannel + Peripheral

+ Into + 'static { /// Type-erase (degrade) this pin into an `AnyChannel`. /// /// This converts DMA channel singletons (`DMA1_CH3`, `DMA2_CH1`, ...), which @@ -64,12 +64,12 @@ pub trait Channel: sealed::Channel + Peripheral

+ Into + ' macro_rules! dma_channel_impl { ($channel_peri:ident, $index:expr) => { - impl crate::dma::sealed::Channel for crate::peripherals::$channel_peri { + impl crate::dma::SealedChannel for crate::peripherals::$channel_peri { fn id(&self) -> u8 { $index } } - impl crate::dma::sealed::ChannelInterrupt for crate::peripherals::$channel_peri { + impl crate::dma::ChannelInterrupt for crate::peripherals::$channel_peri { unsafe fn on_irq() { crate::dma::AnyChannel { id: $index }.on_irq(); } @@ -97,7 +97,7 @@ impl AnyChannel { } } -impl sealed::Channel for AnyChannel { +impl SealedChannel for AnyChannel { fn id(&self) -> u8 { self.id } diff --git a/embassy-stm32/src/dma/word.rs b/embassy-stm32/src/dma/word.rs index a72c4b7d9..fb1bde860 100644 --- a/embassy-stm32/src/dma/word.rs +++ b/embassy-stm32/src/dma/word.rs @@ -20,14 +20,13 @@ impl WordSize { } } -mod sealed { - pub trait Word {} -} +trait SealedWord {} /// DMA word trait. /// /// This is implemented for u8, u16, u32, etc. -pub trait Word: sealed::Word + Default + Copy + 'static { +#[allow(private_bounds)] +pub trait Word: SealedWord + Default + Copy + 'static { /// Word size fn size() -> WordSize; /// Amount of bits of this word size. @@ -36,7 +35,7 @@ pub trait Word: sealed::Word + Default + Copy + 'static { macro_rules! impl_word { (_, $T:ident, $bits:literal, $size:ident) => { - impl sealed::Word for $T {} + impl SealedWord for $T {} impl Word for $T { fn bits() -> usize { $bits diff --git a/embassy-stm32/src/eth/mod.rs b/embassy-stm32/src/eth/mod.rs index 71fe09c3f..bfe8a60d6 100644 --- a/embassy-stm32/src/eth/mod.rs +++ b/embassy-stm32/src/eth/mod.rs @@ -177,16 +177,15 @@ pub unsafe trait PHY { fn poll_link(&mut self, sm: &mut S, cx: &mut Context) -> bool; } -pub(crate) mod sealed { - pub trait Instance { - fn regs() -> crate::pac::eth::Eth; - } +trait SealedInstance { + fn regs() -> crate::pac::eth::Eth; } /// Ethernet instance. -pub trait Instance: sealed::Instance + RccPeripheral + Send + 'static {} +#[allow(private_bounds)] +pub trait Instance: SealedInstance + RccPeripheral + Send + 'static {} -impl sealed::Instance for crate::peripherals::ETH { +impl SealedInstance for crate::peripherals::ETH { fn regs() -> crate::pac::eth::Eth { crate::pac::ETH } diff --git a/embassy-stm32/src/eth/v1/mod.rs b/embassy-stm32/src/eth/v1/mod.rs index e5b7b0452..6f0174def 100644 --- a/embassy-stm32/src/eth/v1/mod.rs +++ b/embassy-stm32/src/eth/v1/mod.rs @@ -12,15 +12,14 @@ use stm32_metapac::eth::vals::{Apcs, Cr, Dm, DmaomrSr, Fes, Ftf, Ifg, MbProgress pub(crate) use self::rx_desc::{RDes, RDesRing}; pub(crate) use self::tx_desc::{TDes, TDesRing}; use super::*; -use crate::gpio::sealed::{AFType, Pin as __GpioPin}; -use crate::gpio::AnyPin; +use crate::gpio::{AFType, AnyPin, SealedPin}; use crate::interrupt::InterruptExt; #[cfg(eth_v1a)] use crate::pac::AFIO; #[cfg(any(eth_v1b, eth_v1c))] use crate::pac::SYSCFG; use crate::pac::{ETH, RCC}; -use crate::rcc::sealed::RccPeripheral; +use crate::rcc::SealedRccPeripheral; use crate::{interrupt, Peripheral}; /// Interrupt handler. @@ -149,8 +148,8 @@ impl<'d, T: Instance, P: PHY> Ethernet<'d, T, P> { #[cfg(any(eth_v1b, eth_v1c))] config_pins!(ref_clk, mdio, mdc, crs, rx_d0, rx_d1, tx_d0, tx_d1, tx_en); - let dma = ETH.ethernet_dma(); - let mac = ETH.ethernet_mac(); + let dma = T::regs().ethernet_dma(); + let mac = T::regs().ethernet_mac(); // Reset and wait dma.dmabmr().modify(|w| w.set_sr(true)); @@ -192,7 +191,7 @@ impl<'d, T: Instance, P: PHY> Ethernet<'d, T, P> { // TODO MTU size setting not found for v1 ethernet, check if correct - let hclk = ::frequency(); + let hclk = ::frequency(); let hclk_mhz = hclk.0 / 1_000_000; // Set the MDC clock frequency in the range 1MHz - 2.5MHz @@ -235,8 +234,8 @@ impl<'d, T: Instance, P: PHY> Ethernet<'d, T, P> { fence(Ordering::SeqCst); - let mac = ETH.ethernet_mac(); - let dma = ETH.ethernet_dma(); + let mac = T::regs().ethernet_mac(); + let dma = T::regs().ethernet_dma(); mac.maccr().modify(|w| { w.set_re(true); @@ -275,7 +274,7 @@ pub struct EthernetStationManagement { unsafe impl StationManagement for EthernetStationManagement { fn smi_read(&mut self, phy_addr: u8, reg: u8) -> u16 { - let mac = ETH.ethernet_mac(); + let mac = T::regs().ethernet_mac(); mac.macmiiar().modify(|w| { w.set_pa(phy_addr); @@ -289,7 +288,7 @@ unsafe impl StationManagement for EthernetStationManagement { } fn smi_write(&mut self, phy_addr: u8, reg: u8, val: u16) { - let mac = ETH.ethernet_mac(); + let mac = T::regs().ethernet_mac(); mac.macmiidr().write(|w| w.set_md(val)); mac.macmiiar().modify(|w| { @@ -305,8 +304,8 @@ unsafe impl StationManagement for EthernetStationManagement { impl<'d, T: Instance, P: PHY> Drop for Ethernet<'d, T, P> { fn drop(&mut self) { - let dma = ETH.ethernet_dma(); - let mac = ETH.ethernet_mac(); + let dma = T::regs().ethernet_dma(); + let mac = T::regs().ethernet_mac(); // Disable the TX DMA and wait for any previous transmissions to be completed dma.dmaomr().modify(|w| w.set_st(St::STOPPED)); diff --git a/embassy-stm32/src/eth/v2/mod.rs b/embassy-stm32/src/eth/v2/mod.rs index 8d69561d4..c6e015022 100644 --- a/embassy-stm32/src/eth/v2/mod.rs +++ b/embassy-stm32/src/eth/v2/mod.rs @@ -7,11 +7,10 @@ use embassy_hal_internal::{into_ref, PeripheralRef}; pub(crate) use self::descriptors::{RDes, RDesRing, TDes, TDesRing}; use super::*; -use crate::gpio::sealed::{AFType, Pin as _}; -use crate::gpio::{AnyPin, Speed}; +use crate::gpio::{AFType, AnyPin, SealedPin as _, Speed}; use crate::interrupt::InterruptExt; use crate::pac::ETH; -use crate::rcc::sealed::RccPeripheral; +use crate::rcc::SealedRccPeripheral; use crate::{interrupt, Peripheral}; /// Interrupt handler. @@ -207,9 +206,9 @@ impl<'d, T: Instance, P: PHY> Ethernet<'d, T, P> { phy: P, mac_addr: [u8; 6], ) -> Self { - let dma = ETH.ethernet_dma(); - let mac = ETH.ethernet_mac(); - let mtl = ETH.ethernet_mtl(); + let dma = T::regs().ethernet_dma(); + let mac = T::regs().ethernet_mac(); + let mtl = T::regs().ethernet_mtl(); // Reset and wait dma.dmamr().modify(|w| w.set_swr(true)); @@ -265,7 +264,7 @@ impl<'d, T: Instance, P: PHY> Ethernet<'d, T, P> { w.set_rbsz(RX_BUFFER_SIZE as u16); }); - let hclk = ::frequency(); + let hclk = ::frequency(); let hclk_mhz = hclk.0 / 1_000_000; // Set the MDC clock frequency in the range 1MHz - 2.5MHz @@ -296,9 +295,9 @@ impl<'d, T: Instance, P: PHY> Ethernet<'d, T, P> { fence(Ordering::SeqCst); - let mac = ETH.ethernet_mac(); - let mtl = ETH.ethernet_mtl(); - let dma = ETH.ethernet_dma(); + let mac = T::regs().ethernet_mac(); + let mtl = T::regs().ethernet_mtl(); + let dma = T::regs().ethernet_dma(); mac.maccr().modify(|w| { w.set_re(true); @@ -334,7 +333,7 @@ pub struct EthernetStationManagement { unsafe impl StationManagement for EthernetStationManagement { fn smi_read(&mut self, phy_addr: u8, reg: u8) -> u16 { - let mac = ETH.ethernet_mac(); + let mac = T::regs().ethernet_mac(); mac.macmdioar().modify(|w| { w.set_pa(phy_addr); @@ -348,7 +347,7 @@ unsafe impl StationManagement for EthernetStationManagement { } fn smi_write(&mut self, phy_addr: u8, reg: u8, val: u16) { - let mac = ETH.ethernet_mac(); + let mac = T::regs().ethernet_mac(); mac.macmdiodr().write(|w| w.set_md(val)); mac.macmdioar().modify(|w| { @@ -364,9 +363,9 @@ unsafe impl StationManagement for EthernetStationManagement { impl<'d, T: Instance, P: PHY> Drop for Ethernet<'d, T, P> { fn drop(&mut self) { - let dma = ETH.ethernet_dma(); - let mac = ETH.ethernet_mac(); - let mtl = ETH.ethernet_mtl(); + let dma = T::regs().ethernet_dma(); + let mac = T::regs().ethernet_mac(); + let mtl = T::regs().ethernet_mtl(); // Disable the TX DMA and wait for any previous transmissions to be completed dma.dmactx_cr().modify(|w| w.set_st(false)); diff --git a/embassy-stm32/src/exti.rs b/embassy-stm32/src/exti.rs index bd10ba158..8d5dae436 100644 --- a/embassy-stm32/src/exti.rs +++ b/embassy-stm32/src/exti.rs @@ -330,12 +330,11 @@ macro_rules! impl_irq { foreach_exti_irq!(impl_irq); -pub(crate) mod sealed { - pub trait Channel {} -} +trait SealedChannel {} /// EXTI channel trait. -pub trait Channel: sealed::Channel + Sized { +#[allow(private_bounds)] +pub trait Channel: SealedChannel + Sized { /// Get the EXTI channel number. fn number(&self) -> u8; @@ -359,7 +358,7 @@ pub struct AnyChannel { } impl_peripheral!(AnyChannel); -impl sealed::Channel for AnyChannel {} +impl SealedChannel for AnyChannel {} impl Channel for AnyChannel { fn number(&self) -> u8 { self.number @@ -368,7 +367,7 @@ impl Channel for AnyChannel { macro_rules! impl_exti { ($type:ident, $number:expr) => { - impl sealed::Channel for peripherals::$type {} + impl SealedChannel for peripherals::$type {} impl Channel for peripherals::$type { fn number(&self) -> u8 { $number diff --git a/embassy-stm32/src/fmc.rs b/embassy-stm32/src/fmc.rs index 9d731a512..aced69878 100644 --- a/embassy-stm32/src/fmc.rs +++ b/embassy-stm32/src/fmc.rs @@ -3,8 +3,7 @@ use core::marker::PhantomData; use embassy_hal_internal::into_ref; -use crate::gpio::sealed::AFType; -use crate::gpio::{Pull, Speed}; +use crate::gpio::{AFType, Pull, Speed}; use crate::Peripheral; /// FMC driver @@ -44,7 +43,7 @@ where /// Get the kernel clock currently in use for this FMC instance. pub fn source_clock_hz(&self) -> u32 { - ::frequency().0 + ::frequency().0 } } @@ -69,7 +68,7 @@ where } fn source_clock_hz(&self) -> u32 { - ::frequency().0 + ::frequency().0 } } @@ -201,18 +200,17 @@ impl<'d, T: Instance> Fmc<'d, T> { )); } -pub(crate) mod sealed { - pub trait Instance: crate::rcc::sealed::RccPeripheral { - const REGS: crate::pac::fmc::Fmc; - } +trait SealedInstance: crate::rcc::SealedRccPeripheral { + const REGS: crate::pac::fmc::Fmc; } /// FMC instance trait. -pub trait Instance: sealed::Instance + 'static {} +#[allow(private_bounds)] +pub trait Instance: SealedInstance + 'static {} foreach_peripheral!( (fmc, $inst:ident) => { - impl crate::fmc::sealed::Instance for crate::peripherals::$inst { + impl crate::fmc::SealedInstance for crate::peripherals::$inst { const REGS: crate::pac::fmc::Fmc = crate::pac::$inst; } impl crate::fmc::Instance for crate::peripherals::$inst {} diff --git a/embassy-stm32/src/gpio.rs b/embassy-stm32/src/gpio.rs index 7cc28ff56..33f22f676 100644 --- a/embassy-stm32/src/gpio.rs +++ b/embassy-stm32/src/gpio.rs @@ -6,7 +6,6 @@ use core::convert::Infallible; use critical_section::CriticalSection; use embassy_hal_internal::{impl_peripheral, into_ref, PeripheralRef}; -use self::sealed::Pin as _; use crate::pac::gpio::{self, vals}; use crate::{pac, peripherals, Peripheral}; @@ -129,6 +128,18 @@ impl<'d> Flex<'d> { }); } + /// Put the pin into AF mode, unchecked. + /// + /// This puts the pin into the AF mode, with the requested number, pull and speed. This is + /// completely unchecked, it can attach the pin to literally any peripheral, so use with care. + #[inline] + pub fn set_as_af_unchecked(&mut self, af_num: u8, af_type: AFType, pull: Pull, speed: Speed) { + critical_section::with(|_| { + self.pin.set_as_af_pull(af_num, af_type, pull); + self.pin.set_speed(speed); + }); + } + /// Get whether the pin input level is high. #[inline] pub fn is_high(&self) -> bool { @@ -508,172 +519,168 @@ pub enum OutputType { OpenDrain, } -impl From for sealed::AFType { +impl From for AFType { fn from(value: OutputType) -> Self { match value { - OutputType::OpenDrain => sealed::AFType::OutputOpenDrain, - OutputType::PushPull => sealed::AFType::OutputPushPull, + OutputType::OpenDrain => AFType::OutputOpenDrain, + OutputType::PushPull => AFType::OutputPushPull, } } } -#[allow(missing_docs)] -pub(crate) mod sealed { - use super::*; +/// Alternate function type settings +#[derive(Debug, Copy, Clone)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +pub enum AFType { + /// Input + Input, + /// Output, drive the pin both high or low. + OutputPushPull, + /// Output, drive the pin low, or don't drive it at all if the output level is high. + OutputOpenDrain, +} - /// Alternate function type settings - #[derive(Debug, Copy, Clone)] - #[cfg_attr(feature = "defmt", derive(defmt::Format))] - pub enum AFType { - /// Input - Input, - /// Output, drive the pin both high or low. - OutputPushPull, - /// Output, drive the pin low, or don't drive it at all if the output level is high. - OutputOpenDrain, +pub(crate) trait SealedPin { + fn pin_port(&self) -> u8; + + #[inline] + fn _pin(&self) -> u8 { + self.pin_port() % 16 + } + #[inline] + fn _port(&self) -> u8 { + self.pin_port() / 16 } - pub trait Pin { - fn pin_port(&self) -> u8; + #[inline] + fn block(&self) -> gpio::Gpio { + pac::GPIO(self._port() as _) + } - #[inline] - fn _pin(&self) -> u8 { - self.pin_port() % 16 - } - #[inline] - fn _port(&self) -> u8 { - self.pin_port() / 16 - } + /// Set the output as high. + #[inline] + fn set_high(&self) { + let n = self._pin() as _; + self.block().bsrr().write(|w| w.set_bs(n, true)); + } - #[inline] - fn block(&self) -> gpio::Gpio { - pac::GPIO(self._port() as _) - } + /// Set the output as low. + #[inline] + fn set_low(&self) { + let n = self._pin() as _; + self.block().bsrr().write(|w| w.set_br(n, true)); + } - /// Set the output as high. - #[inline] - fn set_high(&self) { - let n = self._pin() as _; - self.block().bsrr().write(|w| w.set_bs(n, true)); - } + #[inline] + fn set_as_af(&self, af_num: u8, af_type: AFType) { + self.set_as_af_pull(af_num, af_type, Pull::None); + } - /// Set the output as low. - #[inline] - fn set_low(&self) { - let n = self._pin() as _; - self.block().bsrr().write(|w| w.set_br(n, true)); - } + #[cfg(gpio_v1)] + #[inline] + fn set_as_af_pull(&self, _af_num: u8, af_type: AFType, pull: Pull) { + // F1 uses the AFIO register for remapping. + // For now, this is not implemented, so af_num is ignored + // _af_num should be zero here, since it is not set by stm32-data + let r = self.block(); + let n = self._pin() as usize; + let crlh = if n < 8 { 0 } else { 1 }; + match af_type { + AFType::Input => { + let cnf = match pull { + Pull::Up => { + r.bsrr().write(|w| w.set_bs(n, true)); + vals::CnfIn::PULL + } + Pull::Down => { + r.bsrr().write(|w| w.set_br(n, true)); + vals::CnfIn::PULL + } + Pull::None => vals::CnfIn::FLOATING, + }; - #[inline] - fn set_as_af(&self, af_num: u8, af_type: AFType) { - self.set_as_af_pull(af_num, af_type, Pull::None); + r.cr(crlh).modify(|w| { + w.set_mode(n % 8, vals::Mode::INPUT); + w.set_cnf_in(n % 8, cnf); + }); + } + AFType::OutputPushPull => { + r.cr(crlh).modify(|w| { + w.set_mode(n % 8, vals::Mode::OUTPUT50MHZ); + w.set_cnf_out(n % 8, vals::CnfOut::ALTPUSHPULL); + }); + } + AFType::OutputOpenDrain => { + r.cr(crlh).modify(|w| { + w.set_mode(n % 8, vals::Mode::OUTPUT50MHZ); + w.set_cnf_out(n % 8, vals::CnfOut::ALTOPENDRAIN); + }); + } } + } + + #[cfg(gpio_v2)] + #[inline] + fn set_as_af_pull(&self, af_num: u8, af_type: AFType, pull: Pull) { + let pin = self._pin() as usize; + let block = self.block(); + block.afr(pin / 8).modify(|w| w.set_afr(pin % 8, af_num)); + match af_type { + AFType::Input => {} + AFType::OutputPushPull => block.otyper().modify(|w| w.set_ot(pin, vals::Ot::PUSHPULL)), + AFType::OutputOpenDrain => block.otyper().modify(|w| w.set_ot(pin, vals::Ot::OPENDRAIN)), + } + block.pupdr().modify(|w| w.set_pupdr(pin, pull.into())); + + block.moder().modify(|w| w.set_moder(pin, vals::Moder::ALTERNATE)); + } + + #[inline] + fn set_as_analog(&self) { + let pin = self._pin() as usize; + let block = self.block(); + #[cfg(gpio_v1)] + { + let crlh = if pin < 8 { 0 } else { 1 }; + block.cr(crlh).modify(|w| { + w.set_mode(pin % 8, vals::Mode::INPUT); + w.set_cnf_in(pin % 8, vals::CnfIn::ANALOG); + }); + } + #[cfg(gpio_v2)] + block.moder().modify(|w| w.set_moder(pin, vals::Moder::ANALOG)); + } + + /// Set the pin as "disconnected", ie doing nothing and consuming the lowest + /// amount of power possible. + /// + /// This is currently the same as set_as_analog but is semantically different really. + /// Drivers should set_as_disconnected pins when dropped. + #[inline] + fn set_as_disconnected(&self) { + self.set_as_analog(); + } + + #[inline] + fn set_speed(&self, speed: Speed) { + let pin = self._pin() as usize; #[cfg(gpio_v1)] - #[inline] - fn set_as_af_pull(&self, _af_num: u8, af_type: AFType, pull: Pull) { - // F1 uses the AFIO register for remapping. - // For now, this is not implemented, so af_num is ignored - // _af_num should be zero here, since it is not set by stm32-data - let r = self.block(); - let n = self._pin() as usize; - let crlh = if n < 8 { 0 } else { 1 }; - match af_type { - AFType::Input => { - let cnf = match pull { - Pull::Up => { - r.bsrr().write(|w| w.set_bs(n, true)); - vals::CnfIn::PULL - } - Pull::Down => { - r.bsrr().write(|w| w.set_br(n, true)); - vals::CnfIn::PULL - } - Pull::None => vals::CnfIn::FLOATING, - }; - - r.cr(crlh).modify(|w| { - w.set_mode(n % 8, vals::Mode::INPUT); - w.set_cnf_in(n % 8, cnf); - }); - } - AFType::OutputPushPull => { - r.cr(crlh).modify(|w| { - w.set_mode(n % 8, vals::Mode::OUTPUT50MHZ); - w.set_cnf_out(n % 8, vals::CnfOut::ALTPUSHPULL); - }); - } - AFType::OutputOpenDrain => { - r.cr(crlh).modify(|w| { - w.set_mode(n % 8, vals::Mode::OUTPUT50MHZ); - w.set_cnf_out(n % 8, vals::CnfOut::ALTOPENDRAIN); - }); - } - } + { + let crlh = if pin < 8 { 0 } else { 1 }; + self.block().cr(crlh).modify(|w| { + w.set_mode(pin % 8, speed.into()); + }); } #[cfg(gpio_v2)] - #[inline] - fn set_as_af_pull(&self, af_num: u8, af_type: AFType, pull: Pull) { - let pin = self._pin() as usize; - let block = self.block(); - block.afr(pin / 8).modify(|w| w.set_afr(pin % 8, af_num)); - match af_type { - AFType::Input => {} - AFType::OutputPushPull => block.otyper().modify(|w| w.set_ot(pin, vals::Ot::PUSHPULL)), - AFType::OutputOpenDrain => block.otyper().modify(|w| w.set_ot(pin, vals::Ot::OPENDRAIN)), - } - block.pupdr().modify(|w| w.set_pupdr(pin, pull.into())); - - block.moder().modify(|w| w.set_moder(pin, vals::Moder::ALTERNATE)); - } - - #[inline] - fn set_as_analog(&self) { - let pin = self._pin() as usize; - let block = self.block(); - #[cfg(gpio_v1)] - { - let crlh = if pin < 8 { 0 } else { 1 }; - block.cr(crlh).modify(|w| { - w.set_mode(pin % 8, vals::Mode::INPUT); - w.set_cnf_in(pin % 8, vals::CnfIn::ANALOG); - }); - } - #[cfg(gpio_v2)] - block.moder().modify(|w| w.set_moder(pin, vals::Moder::ANALOG)); - } - - /// Set the pin as "disconnected", ie doing nothing and consuming the lowest - /// amount of power possible. - /// - /// This is currently the same as set_as_analog but is semantically different really. - /// Drivers should set_as_disconnected pins when dropped. - #[inline] - fn set_as_disconnected(&self) { - self.set_as_analog(); - } - - #[inline] - fn set_speed(&self, speed: Speed) { - let pin = self._pin() as usize; - - #[cfg(gpio_v1)] - { - let crlh = if pin < 8 { 0 } else { 1 }; - self.block().cr(crlh).modify(|w| { - w.set_mode(pin % 8, speed.into()); - }); - } - - #[cfg(gpio_v2)] - self.block().ospeedr().modify(|w| w.set_ospeedr(pin, speed.into())); - } + self.block().ospeedr().modify(|w| w.set_ospeedr(pin, speed.into())); } } /// GPIO pin trait. -pub trait Pin: Peripheral

+ Into + sealed::Pin + Sized + 'static { +#[allow(private_bounds)] +pub trait Pin: Peripheral

+ Into + SealedPin + Sized + 'static { /// EXTI channel assigned to this pin. /// /// For example, PC4 uses EXTI4. @@ -737,7 +744,7 @@ impl Pin for AnyPin { #[cfg(feature = "exti")] type ExtiChannel = crate::exti::AnyChannel; } -impl sealed::Pin for AnyPin { +impl SealedPin for AnyPin { #[inline] fn pin_port(&self) -> u8 { self.pin_port @@ -752,7 +759,7 @@ foreach_pin!( #[cfg(feature = "exti")] type ExtiChannel = peripherals::$exti_ch; } - impl sealed::Pin for peripherals::$pin_name { + impl SealedPin for peripherals::$pin_name { #[inline] fn pin_port(&self) -> u8 { $port_num * 16 + $pin_num @@ -769,7 +776,7 @@ foreach_pin!( pub(crate) unsafe fn init(_cs: CriticalSection) { #[cfg(afio)] - ::enable_and_reset_with_cs(_cs); + ::enable_and_reset_with_cs(_cs); crate::_generated::init_gpio(); @@ -1061,9 +1068,3 @@ impl<'d> embedded_hal_1::digital::StatefulOutputPin for Flex<'d> { Ok((*self).is_set_low()) } } - -/// Low-level GPIO manipulation. -#[cfg(feature = "unstable-pac")] -pub mod low_level { - pub use super::sealed::*; -} diff --git a/embassy-stm32/src/hash/mod.rs b/embassy-stm32/src/hash/mod.rs index b47814f8b..787d5b1c9 100644 --- a/embassy-stm32/src/hash/mod.rs +++ b/embassy-stm32/src/hash/mod.rs @@ -17,7 +17,7 @@ use crate::dma::NoDma; use crate::dma::Transfer; use crate::interrupt::typelevel::Interrupt; use crate::peripherals::HASH; -use crate::rcc::sealed::RccPeripheral; +use crate::rcc::SealedRccPeripheral; use crate::{interrupt, pac, peripherals, Peripheral}; #[cfg(hash_v1)] @@ -561,16 +561,13 @@ impl<'d, T: Instance, D> Hash<'d, T, D> { } } -pub(crate) mod sealed { - use super::*; - - pub trait Instance { - fn regs() -> pac::hash::Hash; - } +trait SealedInstance { + fn regs() -> pac::hash::Hash; } /// HASH instance trait. -pub trait Instance: sealed::Instance + Peripheral

+ crate::rcc::RccPeripheral + 'static + Send { +#[allow(private_bounds)] +pub trait Instance: SealedInstance + Peripheral

+ crate::rcc::RccPeripheral + 'static + Send { /// Interrupt for this HASH instance. type Interrupt: interrupt::typelevel::Interrupt; } @@ -581,7 +578,7 @@ foreach_interrupt!( type Interrupt = crate::interrupt::typelevel::$irq; } - impl sealed::Instance for peripherals::$inst { + impl SealedInstance for peripherals::$inst { fn regs() -> crate::pac::hash::Hash { crate::pac::$inst } diff --git a/embassy-stm32/src/hrtim/mod.rs b/embassy-stm32/src/hrtim/mod.rs index 3ec646fc3..02e45819c 100644 --- a/embassy-stm32/src/hrtim/mod.rs +++ b/embassy-stm32/src/hrtim/mod.rs @@ -7,9 +7,7 @@ use core::marker::PhantomData; use embassy_hal_internal::{into_ref, PeripheralRef}; pub use traits::Instance; -#[allow(unused_imports)] -use crate::gpio::sealed::{AFType, Pin}; -use crate::gpio::AnyPin; +use crate::gpio::{AFType, AnyPin}; use crate::time::Hertz; use crate::Peripheral; @@ -54,16 +52,13 @@ pub struct ChF { phantom: PhantomData, } -mod sealed { - use super::Instance; - - pub trait AdvancedChannel { - fn raw() -> usize; - } +trait SealedAdvancedChannel { + fn raw() -> usize; } /// Advanced channel instance trait. -pub trait AdvancedChannel: sealed::AdvancedChannel {} +#[allow(private_bounds)] +pub trait AdvancedChannel: SealedAdvancedChannel {} /// HRTIM PWM pin. pub struct PwmPin<'d, T, C> { @@ -113,7 +108,7 @@ macro_rules! advanced_channel_impl { } } - impl sealed::AdvancedChannel for $channel { + impl SealedAdvancedChannel for $channel { fn raw() -> usize { $ch_num } diff --git a/embassy-stm32/src/hrtim/traits.rs b/embassy-stm32/src/hrtim/traits.rs index dcc2b9ef4..75f9971e2 100644 --- a/embassy-stm32/src/hrtim/traits.rs +++ b/embassy-stm32/src/hrtim/traits.rs @@ -1,4 +1,4 @@ -use crate::rcc::sealed::RccPeripheral; +use crate::rcc::RccPeripheral; use crate::time::Hertz; #[repr(u8)] @@ -72,94 +72,92 @@ impl Prescaler { } } -pub(crate) mod sealed { - use super::*; +pub(crate) trait SealedInstance: RccPeripheral { + fn regs() -> crate::pac::hrtim::Hrtim; - pub trait Instance: RccPeripheral { - fn regs() -> crate::pac::hrtim::Hrtim; + #[allow(unused)] + fn set_master_frequency(frequency: Hertz) { + let f = frequency.0; - fn set_master_frequency(frequency: Hertz) { - let f = frequency.0; + // TODO: wire up HRTIM to the RCC mux infra. + //#[cfg(stm32f334)] + //let timer_f = unsafe { crate::rcc::get_freqs() }.hrtim.unwrap_or(Self::frequency()).0; + //#[cfg(not(stm32f334))] + let timer_f = Self::frequency().0; - // TODO: wire up HRTIM to the RCC mux infra. - //#[cfg(stm32f334)] - //let timer_f = unsafe { crate::rcc::get_freqs() }.hrtim.unwrap_or(Self::frequency()).0; - //#[cfg(not(stm32f334))] - let timer_f = Self::frequency().0; + let psc_min = (timer_f / f) / (u16::MAX as u32 / 32); + let psc = if Self::regs().isr().read().dllrdy() { + Prescaler::compute_min_high_res(psc_min) + } else { + Prescaler::compute_min_low_res(psc_min) + }; - let psc_min = (timer_f / f) / (u16::MAX as u32 / 32); - let psc = if Self::regs().isr().read().dllrdy() { - Prescaler::compute_min_high_res(psc_min) - } else { - Prescaler::compute_min_low_res(psc_min) - }; + let timer_f = 32 * (timer_f / psc as u32); + let per: u16 = (timer_f / f) as u16; - let timer_f = 32 * (timer_f / psc as u32); - let per: u16 = (timer_f / f) as u16; + let regs = Self::regs(); - let regs = Self::regs(); + regs.mcr().modify(|w| w.set_ckpsc(psc.into())); + regs.mper().modify(|w| w.set_mper(per)); + } - regs.mcr().modify(|w| w.set_ckpsc(psc.into())); - regs.mper().modify(|w| w.set_mper(per)); - } + fn set_channel_frequency(channel: usize, frequency: Hertz) { + let f = frequency.0; - fn set_channel_frequency(channel: usize, frequency: Hertz) { - let f = frequency.0; + // TODO: wire up HRTIM to the RCC mux infra. + //#[cfg(stm32f334)] + //let timer_f = unsafe { crate::rcc::get_freqs() }.hrtim.unwrap_or(Self::frequency()).0; + //#[cfg(not(stm32f334))] + let timer_f = Self::frequency().0; - // TODO: wire up HRTIM to the RCC mux infra. - //#[cfg(stm32f334)] - //let timer_f = unsafe { crate::rcc::get_freqs() }.hrtim.unwrap_or(Self::frequency()).0; - //#[cfg(not(stm32f334))] - let timer_f = Self::frequency().0; + let psc_min = (timer_f / f) / (u16::MAX as u32 / 32); + let psc = if Self::regs().isr().read().dllrdy() { + Prescaler::compute_min_high_res(psc_min) + } else { + Prescaler::compute_min_low_res(psc_min) + }; - let psc_min = (timer_f / f) / (u16::MAX as u32 / 32); - let psc = if Self::regs().isr().read().dllrdy() { - Prescaler::compute_min_high_res(psc_min) - } else { - Prescaler::compute_min_low_res(psc_min) - }; + let timer_f = 32 * (timer_f / psc as u32); + let per: u16 = (timer_f / f) as u16; - let timer_f = 32 * (timer_f / psc as u32); - let per: u16 = (timer_f / f) as u16; + let regs = Self::regs(); - let regs = Self::regs(); + regs.tim(channel).cr().modify(|w| w.set_ckpsc(psc.into())); + regs.tim(channel).per().modify(|w| w.set_per(per)); + } - regs.tim(channel).cr().modify(|w| w.set_ckpsc(psc.into())); - regs.tim(channel).per().modify(|w| w.set_per(per)); - } + /// Set the dead time as a proportion of max_duty + fn set_channel_dead_time(channel: usize, dead_time: u16) { + let regs = Self::regs(); - /// Set the dead time as a proportion of max_duty - fn set_channel_dead_time(channel: usize, dead_time: u16) { - let regs = Self::regs(); + let channel_psc: Prescaler = regs.tim(channel).cr().read().ckpsc().into(); - let channel_psc: Prescaler = regs.tim(channel).cr().read().ckpsc().into(); + // The dead-time base clock runs 4 times slower than the hrtim base clock + // u9::MAX = 511 + let psc_min = (channel_psc as u32 * dead_time as u32) / (4 * 511); + let psc = if Self::regs().isr().read().dllrdy() { + Prescaler::compute_min_high_res(psc_min) + } else { + Prescaler::compute_min_low_res(psc_min) + }; - // The dead-time base clock runs 4 times slower than the hrtim base clock - // u9::MAX = 511 - let psc_min = (channel_psc as u32 * dead_time as u32) / (4 * 511); - let psc = if Self::regs().isr().read().dllrdy() { - Prescaler::compute_min_high_res(psc_min) - } else { - Prescaler::compute_min_low_res(psc_min) - }; + let dt_val = (psc as u32 * dead_time as u32) / (4 * channel_psc as u32); - let dt_val = (psc as u32 * dead_time as u32) / (4 * channel_psc as u32); - - regs.tim(channel).dt().modify(|w| { - w.set_dtprsc(psc.into()); - w.set_dtf(dt_val as u16); - w.set_dtr(dt_val as u16); - }); - } + regs.tim(channel).dt().modify(|w| { + w.set_dtprsc(psc.into()); + w.set_dtf(dt_val as u16); + w.set_dtr(dt_val as u16); + }); } } /// HRTIM instance trait. -pub trait Instance: sealed::Instance + 'static {} +#[allow(private_bounds)] +pub trait Instance: SealedInstance + 'static {} foreach_interrupt! { ($inst:ident, hrtim, HRTIM, MASTER, $irq:ident) => { - impl sealed::Instance for crate::peripherals::$inst { + impl SealedInstance for crate::peripherals::$inst { fn regs() -> crate::pac::hrtim::Hrtim { crate::pac::$inst } diff --git a/embassy-stm32/src/i2c/mod.rs b/embassy-stm32/src/i2c/mod.rs index 2c606c3c9..f1b11cc44 100644 --- a/embassy-stm32/src/i2c/mod.rs +++ b/embassy-stm32/src/i2c/mod.rs @@ -14,8 +14,7 @@ use embassy_sync::waitqueue::AtomicWaker; use embassy_time::{Duration, Instant}; use crate::dma::NoDma; -use crate::gpio::sealed::AFType; -use crate::gpio::Pull; +use crate::gpio::{AFType, Pull}; use crate::interrupt::typelevel::Interrupt; use crate::time::Hertz; use crate::{interrupt, peripherals}; @@ -175,30 +174,27 @@ impl Timeout { } } -pub(crate) mod sealed { - use super::*; +struct State { + #[allow(unused)] + waker: AtomicWaker, +} - pub struct State { - #[allow(unused)] - pub waker: AtomicWaker, - } - - impl State { - pub const fn new() -> Self { - Self { - waker: AtomicWaker::new(), - } +impl State { + const fn new() -> Self { + Self { + waker: AtomicWaker::new(), } } - - pub trait Instance: crate::rcc::RccPeripheral { - fn regs() -> crate::pac::i2c::I2c; - fn state() -> &'static State; - } +} + +trait SealedInstance: crate::rcc::RccPeripheral { + fn regs() -> crate::pac::i2c::I2c; + fn state() -> &'static State; } /// I2C peripheral instance -pub trait Instance: sealed::Instance + 'static { +#[allow(private_bounds)] +pub trait Instance: SealedInstance + 'static { /// Event interrupt for this instance type EventInterrupt: interrupt::typelevel::Interrupt; /// Error interrupt for this instance @@ -234,13 +230,13 @@ impl interrupt::typelevel::Handler for ErrorInte foreach_peripheral!( (i2c, $inst:ident) => { - impl sealed::Instance for peripherals::$inst { + impl SealedInstance for peripherals::$inst { fn regs() -> crate::pac::i2c::I2c { crate::pac::$inst } - fn state() -> &'static sealed::State { - static STATE: sealed::State = sealed::State::new(); + fn state() -> &'static State { + static STATE: State = State::new(); &STATE } } diff --git a/embassy-stm32/src/i2s.rs b/embassy-stm32/src/i2s.rs index fa9ec0532..c5a606b21 100644 --- a/embassy-stm32/src/i2s.rs +++ b/embassy-stm32/src/i2s.rs @@ -1,8 +1,7 @@ //! Inter-IC Sound (I2S) use embassy_hal_internal::into_ref; -use crate::gpio::sealed::{AFType, Pin as _}; -use crate::gpio::AnyPin; +use crate::gpio::{AFType, AnyPin, SealedPin}; use crate::pac::spi::vals; use crate::spi::{Config as SpiConfig, *}; use crate::time::Hertz; diff --git a/embassy-stm32/src/ipcc.rs b/embassy-stm32/src/ipcc.rs index 523719bb9..4d535cce2 100644 --- a/embassy-stm32/src/ipcc.rs +++ b/embassy-stm32/src/ipcc.rs @@ -4,11 +4,12 @@ use core::future::poll_fn; use core::sync::atomic::{compiler_fence, Ordering}; use core::task::Poll; -use self::sealed::Instance; +use embassy_sync::waitqueue::AtomicWaker; + use crate::interrupt; use crate::interrupt::typelevel::Interrupt; use crate::peripherals::IPCC; -use crate::rcc::sealed::RccPeripheral; +use crate::rcc::SealedRccPeripheral; /// Interrupt handler. pub struct ReceiveInterruptHandler {} @@ -207,7 +208,7 @@ impl Ipcc { } } -impl sealed::Instance for crate::peripherals::IPCC { +impl SealedInstance for crate::peripherals::IPCC { fn regs() -> crate::pac::ipcc::Ipcc { crate::pac::IPCC } @@ -216,58 +217,52 @@ impl sealed::Instance for crate::peripherals::IPCC { crate::pac::PWR.cr4().modify(|w| w.set_c2boot(enabled)); } - fn state() -> &'static self::sealed::State { - static STATE: self::sealed::State = self::sealed::State::new(); + fn state() -> &'static State { + static STATE: State = State::new(); &STATE } } -pub(crate) mod sealed { - use embassy_sync::waitqueue::AtomicWaker; +struct State { + rx_wakers: [AtomicWaker; 6], + tx_wakers: [AtomicWaker; 6], +} - use super::*; +impl State { + const fn new() -> Self { + const WAKER: AtomicWaker = AtomicWaker::new(); - pub struct State { - rx_wakers: [AtomicWaker; 6], - tx_wakers: [AtomicWaker; 6], - } - - impl State { - pub const fn new() -> Self { - const WAKER: AtomicWaker = AtomicWaker::new(); - - Self { - rx_wakers: [WAKER; 6], - tx_wakers: [WAKER; 6], - } - } - - pub const fn rx_waker_for(&self, channel: IpccChannel) -> &AtomicWaker { - match channel { - IpccChannel::Channel1 => &self.rx_wakers[0], - IpccChannel::Channel2 => &self.rx_wakers[1], - IpccChannel::Channel3 => &self.rx_wakers[2], - IpccChannel::Channel4 => &self.rx_wakers[3], - IpccChannel::Channel5 => &self.rx_wakers[4], - IpccChannel::Channel6 => &self.rx_wakers[5], - } - } - - pub const fn tx_waker_for(&self, channel: IpccChannel) -> &AtomicWaker { - match channel { - IpccChannel::Channel1 => &self.tx_wakers[0], - IpccChannel::Channel2 => &self.tx_wakers[1], - IpccChannel::Channel3 => &self.tx_wakers[2], - IpccChannel::Channel4 => &self.tx_wakers[3], - IpccChannel::Channel5 => &self.tx_wakers[4], - IpccChannel::Channel6 => &self.tx_wakers[5], - } + Self { + rx_wakers: [WAKER; 6], + tx_wakers: [WAKER; 6], } } - pub trait Instance: crate::rcc::RccPeripheral { - fn regs() -> crate::pac::ipcc::Ipcc; - fn set_cpu2(enabled: bool); - fn state() -> &'static State; + const fn rx_waker_for(&self, channel: IpccChannel) -> &AtomicWaker { + match channel { + IpccChannel::Channel1 => &self.rx_wakers[0], + IpccChannel::Channel2 => &self.rx_wakers[1], + IpccChannel::Channel3 => &self.rx_wakers[2], + IpccChannel::Channel4 => &self.rx_wakers[3], + IpccChannel::Channel5 => &self.rx_wakers[4], + IpccChannel::Channel6 => &self.rx_wakers[5], + } + } + + const fn tx_waker_for(&self, channel: IpccChannel) -> &AtomicWaker { + match channel { + IpccChannel::Channel1 => &self.tx_wakers[0], + IpccChannel::Channel2 => &self.tx_wakers[1], + IpccChannel::Channel3 => &self.tx_wakers[2], + IpccChannel::Channel4 => &self.tx_wakers[3], + IpccChannel::Channel5 => &self.tx_wakers[4], + IpccChannel::Channel6 => &self.tx_wakers[5], + } } } + +trait SealedInstance: crate::rcc::RccPeripheral { + fn regs() -> crate::pac::ipcc::Ipcc; + fn set_cpu2(enabled: bool); + fn state() -> &'static State; +} diff --git a/embassy-stm32/src/lib.rs b/embassy-stm32/src/lib.rs index 9e26a3513..6a3d1c463 100644 --- a/embassy-stm32/src/lib.rs +++ b/embassy-stm32/src/lib.rs @@ -158,7 +158,7 @@ pub(crate) use stm32_metapac as pac; use crate::interrupt::Priority; #[cfg(feature = "rt")] pub use crate::pac::NVIC_PRIO_BITS; -use crate::rcc::sealed::RccPeripheral; +use crate::rcc::SealedRccPeripheral; /// `embassy-stm32` global configuration. #[non_exhaustive] diff --git a/embassy-stm32/src/opamp.rs b/embassy-stm32/src/opamp.rs index cf531e266..a3b4352c0 100644 --- a/embassy-stm32/src/opamp.rs +++ b/embassy-stm32/src/opamp.rs @@ -81,8 +81,8 @@ impl<'d, T: Instance> OpAmp<'d, T> { /// [`OpAmpOutput`] is dropped. pub fn buffer_ext( &'d mut self, - in_pin: impl Peripheral

+ crate::gpio::sealed::Pin>, - out_pin: impl Peripheral

+ crate::gpio::sealed::Pin> + 'd, + in_pin: impl Peripheral

+ crate::gpio::Pin>, + out_pin: impl Peripheral

+ crate::gpio::Pin> + 'd, gain: OpAmpGain, ) -> OpAmpOutput<'d, T> { into_ref!(in_pin); @@ -122,7 +122,7 @@ impl<'d, T: Instance> OpAmp<'d, T> { #[cfg(opamp_g4)] pub fn buffer_int( &'d mut self, - pin: impl Peripheral

+ crate::gpio::sealed::Pin>, + pin: impl Peripheral

+ crate::gpio::Pin>, gain: OpAmpGain, ) -> OpAmpInternalOutput<'d, T> { into_ref!(pin); @@ -166,37 +166,39 @@ impl<'d, T: Instance> Drop for OpAmpInternalOutput<'d, T> { } } -/// Opamp instance trait. -pub trait Instance: sealed::Instance + 'static {} - -pub(crate) mod sealed { - pub trait Instance { - fn regs() -> crate::pac::opamp::Opamp; - } - - pub trait NonInvertingPin { - fn channel(&self) -> u8; - } - - pub trait InvertingPin { - fn channel(&self) -> u8; - } - - pub trait OutputPin {} +pub(crate) trait SealedInstance { + fn regs() -> crate::pac::opamp::Opamp; } +pub(crate) trait SealedNonInvertingPin { + fn channel(&self) -> u8; +} + +pub(crate) trait SealedInvertingPin { + #[allow(unused)] + fn channel(&self) -> u8; +} + +pub(crate) trait SealedOutputPin {} + +/// Opamp instance trait. +#[allow(private_bounds)] +pub trait Instance: SealedInstance + 'static {} /// Non-inverting pin trait. -pub trait NonInvertingPin: sealed::NonInvertingPin {} +#[allow(private_bounds)] +pub trait NonInvertingPin: SealedNonInvertingPin {} /// Inverting pin trait. -pub trait InvertingPin: sealed::InvertingPin {} +#[allow(private_bounds)] +pub trait InvertingPin: SealedInvertingPin {} /// Output pin trait. -pub trait OutputPin: sealed::OutputPin {} +#[allow(private_bounds)] +pub trait OutputPin: SealedOutputPin {} macro_rules! impl_opamp_external_output { ($inst:ident, $adc:ident, $ch:expr) => { foreach_adc!( ($adc, $common_inst:ident, $adc_clock:ident) => { - impl<'d> crate::adc::sealed::AdcPin + impl<'d> crate::adc::SealedAdcPin for OpAmpOutput<'d, crate::peripherals::$inst> { fn channel(&self) -> u8 { @@ -242,7 +244,7 @@ macro_rules! impl_opamp_internal_output { ($inst:ident, $adc:ident, $ch:expr) => { foreach_adc!( ($adc, $common_inst:ident, $adc_clock:ident) => { - impl<'d> crate::adc::sealed::AdcPin + impl<'d> crate::adc::SealedAdcPin for OpAmpInternalOutput<'d, crate::peripherals::$inst> { fn channel(&self) -> u8 { @@ -291,7 +293,7 @@ foreach_peripheral!( foreach_peripheral! { (opamp, $inst:ident) => { - impl sealed::Instance for crate::peripherals::$inst { + impl SealedInstance for crate::peripherals::$inst { fn regs() -> crate::pac::opamp::Opamp { crate::pac::$inst } @@ -306,7 +308,7 @@ foreach_peripheral! { macro_rules! impl_opamp_vp_pin { ($inst:ident, $pin:ident, $ch:expr) => { impl crate::opamp::NonInvertingPin for crate::peripherals::$pin {} - impl crate::opamp::sealed::NonInvertingPin for crate::peripherals::$pin { + impl crate::opamp::SealedNonInvertingPin for crate::peripherals::$pin { fn channel(&self) -> u8 { $ch } @@ -318,6 +320,6 @@ macro_rules! impl_opamp_vp_pin { macro_rules! impl_opamp_vout_pin { ($inst:ident, $pin:ident) => { impl crate::opamp::OutputPin for crate::peripherals::$pin {} - impl crate::opamp::sealed::OutputPin for crate::peripherals::$pin {} + impl crate::opamp::SealedOutputPin for crate::peripherals::$pin {} }; } diff --git a/embassy-stm32/src/qspi/mod.rs b/embassy-stm32/src/qspi/mod.rs index 8a709a89e..3c054e666 100644 --- a/embassy-stm32/src/qspi/mod.rs +++ b/embassy-stm32/src/qspi/mod.rs @@ -8,8 +8,7 @@ use embassy_hal_internal::{into_ref, PeripheralRef}; use enums::*; use crate::dma::Transfer; -use crate::gpio::sealed::AFType; -use crate::gpio::{AnyPin, Pull}; +use crate::gpio::{AFType, AnyPin, Pull}; use crate::pac::quadspi::Quadspi as Regs; use crate::rcc::RccPeripheral; use crate::{peripherals, Peripheral}; @@ -381,16 +380,13 @@ impl<'d, T: Instance, Dma> Qspi<'d, T, Dma> { } } -pub(crate) mod sealed { - use super::*; - - pub trait Instance { - const REGS: Regs; - } +trait SealedInstance { + const REGS: Regs; } /// QSPI instance trait. -pub trait Instance: Peripheral

+ sealed::Instance + RccPeripheral {} +#[allow(private_bounds)] +pub trait Instance: Peripheral

+ SealedInstance + RccPeripheral {} pin_trait!(SckPin, Instance); pin_trait!(BK1D0Pin, Instance); @@ -409,7 +405,7 @@ dma_trait!(QuadDma, Instance); foreach_peripheral!( (quadspi, $inst:ident) => { - impl sealed::Instance for peripherals::$inst { + impl SealedInstance for peripherals::$inst { const REGS: Regs = crate::pac::$inst; } diff --git a/embassy-stm32/src/rcc/hsi48.rs b/embassy-stm32/src/rcc/hsi48.rs index 19a8c8cb9..6f0d7b379 100644 --- a/embassy-stm32/src/rcc/hsi48.rs +++ b/embassy-stm32/src/rcc/hsi48.rs @@ -2,7 +2,7 @@ use crate::pac::crs::vals::Syncsrc; use crate::pac::{CRS, RCC}; -use crate::rcc::sealed::RccPeripheral; +use crate::rcc::SealedRccPeripheral; use crate::time::Hertz; /// HSI48 speed diff --git a/embassy-stm32/src/rcc/mco.rs b/embassy-stm32/src/rcc/mco.rs index 654943bc1..d8604e07e 100644 --- a/embassy-stm32/src/rcc/mco.rs +++ b/embassy-stm32/src/rcc/mco.rs @@ -2,8 +2,7 @@ use core::marker::PhantomData; use embassy_hal_internal::into_ref; -use crate::gpio::sealed::AFType; -use crate::gpio::Speed; +use crate::gpio::{AFType, Speed}; #[cfg(not(any(stm32f1, rcc_f0v1, rcc_f3v1, rcc_f37)))] pub use crate::pac::rcc::vals::Mcopre as McoPrescaler; #[cfg(not(any(rcc_f2, rcc_f410, rcc_f4, rcc_f7, rcc_h50, rcc_h5, rcc_h7ab, rcc_h7rm0433, rcc_h7)))] @@ -19,23 +18,25 @@ pub enum McoPrescaler { DIV1, } -pub(crate) mod sealed { - pub trait McoInstance { - type Source; - unsafe fn apply_clock_settings(source: Self::Source, prescaler: super::McoPrescaler); - } -} +pub(crate) trait SealedMcoInstance {} -pub trait McoInstance: sealed::McoInstance + 'static {} +#[allow(private_bounds)] +pub trait McoInstance: SealedMcoInstance + 'static { + type Source; + + #[doc(hidden)] + unsafe fn _apply_clock_settings(source: Self::Source, prescaler: super::McoPrescaler); +} pin_trait!(McoPin, McoInstance); macro_rules! impl_peri { ($peri:ident, $source:ident, $set_source:ident, $set_prescaler:ident) => { - impl sealed::McoInstance for peripherals::$peri { + impl SealedMcoInstance for peripherals::$peri {} + impl McoInstance for peripherals::$peri { type Source = $source; - unsafe fn apply_clock_settings(source: Self::Source, _prescaler: McoPrescaler) { + unsafe fn _apply_clock_settings(source: Self::Source, _prescaler: McoPrescaler) { #[cfg(not(any(stm32u5, stm32wba)))] let r = RCC.cfgr(); #[cfg(any(stm32u5, stm32wba))] @@ -48,8 +49,6 @@ macro_rules! impl_peri { }); } } - - impl McoInstance for peripherals::$peri {} }; } @@ -79,7 +78,7 @@ impl<'d, T: McoInstance> Mco<'d, T> { into_ref!(pin); critical_section::with(|_| unsafe { - T::apply_clock_settings(source, prescaler); + T::_apply_clock_settings(source, prescaler); pin.set_as_af(pin.af_num(), AFType::OutputPushPull); pin.set_speed(Speed::VeryHigh); }); diff --git a/embassy-stm32/src/rcc/mod.rs b/embassy-stm32/src/rcc/mod.rs index 910ebe205..d53d02203 100644 --- a/embassy-stm32/src/rcc/mod.rs +++ b/embassy-stm32/src/rcc/mod.rs @@ -10,6 +10,7 @@ pub use bd::*; #[cfg(any(mco, mco1, mco2))] mod mco; +use critical_section::CriticalSection; #[cfg(any(mco, mco1, mco2))] pub use mco::*; @@ -32,6 +33,7 @@ mod _version; pub use _version::*; pub use crate::_generated::{mux, Clocks}; +use crate::time::Hertz; #[cfg(feature = "low-power")] /// Must be written within a critical section @@ -63,29 +65,21 @@ pub(crate) unsafe fn get_freqs() -> &'static Clocks { CLOCK_FREQS.assume_init_ref() } -#[cfg(feature = "unstable-pac")] -pub mod low_level { - pub use super::sealed::*; -} +pub(crate) trait SealedRccPeripheral { + fn frequency() -> crate::time::Hertz; + fn enable_and_reset_with_cs(cs: CriticalSection); + fn disable_with_cs(cs: CriticalSection); -pub(crate) mod sealed { - use critical_section::CriticalSection; - - pub trait RccPeripheral { - fn frequency() -> crate::time::Hertz; - fn enable_and_reset_with_cs(cs: CriticalSection); - fn disable_with_cs(cs: CriticalSection); - - fn enable_and_reset() { - critical_section::with(|cs| Self::enable_and_reset_with_cs(cs)) - } - fn disable() { - critical_section::with(|cs| Self::disable_with_cs(cs)) - } + fn enable_and_reset() { + critical_section::with(|cs| Self::enable_and_reset_with_cs(cs)) + } + fn disable() { + critical_section::with(|cs| Self::disable_with_cs(cs)) } } -pub trait RccPeripheral: sealed::RccPeripheral + 'static {} +#[allow(private_bounds)] +pub trait RccPeripheral: SealedRccPeripheral + 'static {} #[allow(unused)] mod util { @@ -116,3 +110,12 @@ mod util { Ok(Some(x)) } } + +/// Get the kernel clocok frequency of the peripheral `T`. +/// +/// # Panics +/// +/// Panics if the clock is not active. +pub fn frequency() -> Hertz { + T::frequency() +} diff --git a/embassy-stm32/src/rng.rs b/embassy-stm32/src/rng.rs index ca641f352..7a228e4a4 100644 --- a/embassy-stm32/src/rng.rs +++ b/embassy-stm32/src/rng.rs @@ -222,16 +222,13 @@ impl<'d, T: Instance> RngCore for Rng<'d, T> { impl<'d, T: Instance> CryptoRng for Rng<'d, T> {} -pub(crate) mod sealed { - use super::*; - - pub trait Instance { - fn regs() -> pac::rng::Rng; - } +trait SealedInstance { + fn regs() -> pac::rng::Rng; } /// RNG instance trait. -pub trait Instance: sealed::Instance + Peripheral

+ crate::rcc::RccPeripheral + 'static + Send { +#[allow(private_bounds)] +pub trait Instance: SealedInstance + Peripheral

+ crate::rcc::RccPeripheral + 'static + Send { /// Interrupt for this RNG instance. type Interrupt: interrupt::typelevel::Interrupt; } @@ -242,7 +239,7 @@ foreach_interrupt!( type Interrupt = crate::interrupt::typelevel::$irq; } - impl sealed::Instance for peripherals::$inst { + impl SealedInstance for peripherals::$inst { fn regs() -> crate::pac::rng::Rng { crate::pac::$inst } diff --git a/embassy-stm32/src/rtc/datetime.rs b/embassy-stm32/src/rtc/datetime.rs index 2bad79923..bab8cf4a3 100644 --- a/embassy-stm32/src/rtc/datetime.rs +++ b/embassy-stm32/src/rtc/datetime.rs @@ -4,7 +4,7 @@ use chrono::{Datelike, NaiveDate, Timelike, Weekday}; #[cfg(any(feature = "defmt", feature = "time"))] use crate::peripherals::RTC; #[cfg(any(feature = "defmt", feature = "time"))] -use crate::rtc::sealed::Instance; +use crate::rtc::SealedInstance; /// Represents an instant in time that can be substracted to compute a duration pub struct RtcInstant { diff --git a/embassy-stm32/src/rtc/mod.rs b/embassy-stm32/src/rtc/mod.rs index 169505501..00abe9356 100644 --- a/embassy-stm32/src/rtc/mod.rs +++ b/embassy-stm32/src/rtc/mod.rs @@ -31,7 +31,6 @@ pub use _version::*; use embassy_hal_internal::Peripheral; use crate::peripherals::RTC; -use crate::rtc::sealed::Instance; #[allow(dead_code)] #[repr(u8)] @@ -212,7 +211,7 @@ impl Rtc { /// Create a new RTC instance. pub fn new(_rtc: impl Peripheral

, rtc_config: RtcConfig) -> Self { #[cfg(not(any(stm32l0, stm32f3, stm32l1, stm32f0, stm32f2)))] - ::enable_and_reset(); + ::enable_and_reset(); let mut this = Self { #[cfg(feature = "low-power")] @@ -437,7 +436,7 @@ impl Rtc { .fpr(0) .modify(|w| w.set_line(RTC::EXTI_WAKEUP_LINE, true)); - ::WakeupInterrupt::unpend(); + ::WakeupInterrupt::unpend(); }); } @@ -449,8 +448,8 @@ impl Rtc { use crate::interrupt::typelevel::Interrupt; use crate::pac::EXTI; - ::WakeupInterrupt::unpend(); - unsafe { ::WakeupInterrupt::enable() }; + ::WakeupInterrupt::unpend(); + unsafe { ::WakeupInterrupt::enable() }; EXTI.rtsr(0).modify(|w| w.set_line(RTC::EXTI_WAKEUP_LINE, true)); EXTI.imr(0).modify(|w| w.set_line(RTC::EXTI_WAKEUP_LINE, true)); @@ -477,34 +476,30 @@ pub(crate) fn bcd2_to_byte(bcd: (u8, u8)) -> u8 { tmp + (value & 0x0F) } -pub(crate) mod sealed { - use crate::pac::rtc::Rtc; +trait SealedInstance { + const BACKUP_REGISTER_COUNT: usize; - pub trait Instance { - const BACKUP_REGISTER_COUNT: usize; + #[cfg(feature = "low-power")] + const EXTI_WAKEUP_LINE: usize; - #[cfg(feature = "low-power")] - const EXTI_WAKEUP_LINE: usize; + #[cfg(feature = "low-power")] + type WakeupInterrupt: crate::interrupt::typelevel::Interrupt; - #[cfg(feature = "low-power")] - type WakeupInterrupt: crate::interrupt::typelevel::Interrupt; - - fn regs() -> Rtc { - crate::pac::RTC - } - - /// Read content of the backup register. - /// - /// The registers retain their values during wakes from standby mode or system resets. They also - /// retain their value when Vdd is switched off as long as V_BAT is powered. - fn read_backup_register(rtc: &Rtc, register: usize) -> Option; - - /// Set content of the backup register. - /// - /// The registers retain their values during wakes from standby mode or system resets. They also - /// retain their value when Vdd is switched off as long as V_BAT is powered. - fn write_backup_register(rtc: &Rtc, register: usize, value: u32); - - // fn apply_config(&mut self, rtc_config: RtcConfig); + fn regs() -> crate::pac::rtc::Rtc { + crate::pac::RTC } + + /// Read content of the backup register. + /// + /// The registers retain their values during wakes from standby mode or system resets. They also + /// retain their value when Vdd is switched off as long as V_BAT is powered. + fn read_backup_register(rtc: &crate::pac::rtc::Rtc, register: usize) -> Option; + + /// Set content of the backup register. + /// + /// The registers retain their values during wakes from standby mode or system resets. They also + /// retain their value when Vdd is switched off as long as V_BAT is powered. + fn write_backup_register(rtc: &crate::pac::rtc::Rtc, register: usize, value: u32); + + // fn apply_config(&mut self, rtc_config: RtcConfig); } diff --git a/embassy-stm32/src/rtc/v2.rs b/embassy-stm32/src/rtc/v2.rs index 1eda097a7..92f9de846 100644 --- a/embassy-stm32/src/rtc/v2.rs +++ b/embassy-stm32/src/rtc/v2.rs @@ -1,9 +1,8 @@ use stm32_metapac::rtc::vals::{Osel, Pol}; -use super::sealed; +use super::SealedInstance; use crate::pac::rtc::Rtc; use crate::peripherals::RTC; -use crate::rtc::sealed::Instance; #[allow(dead_code)] impl super::Rtc { @@ -126,7 +125,7 @@ impl super::Rtc { } } -impl sealed::Instance for crate::peripherals::RTC { +impl SealedInstance for crate::peripherals::RTC { const BACKUP_REGISTER_COUNT: usize = 20; #[cfg(all(feature = "low-power", stm32f4))] diff --git a/embassy-stm32/src/rtc/v3.rs b/embassy-stm32/src/rtc/v3.rs index 3d44a52ff..8a78d16e1 100644 --- a/embassy-stm32/src/rtc/v3.rs +++ b/embassy-stm32/src/rtc/v3.rs @@ -1,9 +1,9 @@ use stm32_metapac::rtc::vals::{Calp, Calw16, Calw8, Fmt, Key, Osel, Pol, TampalrmType}; -use super::{sealed, RtcCalibrationCyclePeriod}; +use super::RtcCalibrationCyclePeriod; use crate::pac::rtc::Rtc; use crate::peripherals::RTC; -use crate::rtc::sealed::Instance; +use crate::rtc::SealedInstance; impl super::Rtc { /// Applies the RTC config @@ -126,7 +126,7 @@ impl super::Rtc { } } -impl sealed::Instance for crate::peripherals::RTC { +impl SealedInstance for crate::peripherals::RTC { const BACKUP_REGISTER_COUNT: usize = 32; #[cfg(all(feature = "low-power", stm32g4))] diff --git a/embassy-stm32/src/sai/mod.rs b/embassy-stm32/src/sai/mod.rs index 294620031..54dd81524 100644 --- a/embassy-stm32/src/sai/mod.rs +++ b/embassy-stm32/src/sai/mod.rs @@ -6,12 +6,10 @@ use core::marker::PhantomData; use embassy_hal_internal::{into_ref, PeripheralRef}; -use self::sealed::WhichSubBlock; pub use crate::dma::word; #[cfg(not(gpdma))] use crate::dma::{ringbuffer, Channel, ReadableRingBuffer, Request, TransferOptions, WritableRingBuffer}; -use crate::gpio::sealed::{AFType, Pin as _}; -use crate::gpio::AnyPin; +use crate::gpio::{AFType, AnyPin, SealedPin as _}; use crate::pac::sai::{vals, Sai as Regs}; use crate::rcc::RccPeripheral; use crate::{peripherals, Peripheral}; @@ -1041,43 +1039,42 @@ impl<'d, T: Instance, W: word::Word> Drop for Sai<'d, T, W> { } } -pub(crate) mod sealed { - use super::*; +trait SealedInstance { + const REGS: Regs; +} - pub trait Instance { - const REGS: Regs; - } +#[derive(Copy, Clone)] +enum WhichSubBlock { + A = 0, + B = 1, +} - #[derive(Copy, Clone)] - pub enum WhichSubBlock { - A = 0, - B = 1, - } - - pub trait SubBlock { - const WHICH: WhichSubBlock; - } +trait SealedSubBlock { + const WHICH: WhichSubBlock; } /// Sub-block instance trait. -pub trait SubBlockInstance: sealed::SubBlock {} +#[allow(private_bounds)] +pub trait SubBlockInstance: SealedSubBlock {} /// Sub-block A. pub enum A {} -impl sealed::SubBlock for A { +impl SealedSubBlock for A { const WHICH: WhichSubBlock = WhichSubBlock::A; } impl SubBlockInstance for A {} /// Sub-block B. pub enum B {} -impl sealed::SubBlock for B { +impl SealedSubBlock for B { const WHICH: WhichSubBlock = WhichSubBlock::B; } impl SubBlockInstance for B {} /// SAI instance trait. -pub trait Instance: Peripheral

+ sealed::Instance + RccPeripheral {} +#[allow(private_bounds)] +pub trait Instance: Peripheral

+ SealedInstance + RccPeripheral {} + pin_trait!(SckPin, Instance, SubBlockInstance); pin_trait!(FsPin, Instance, SubBlockInstance); pin_trait!(SdPin, Instance, SubBlockInstance); @@ -1087,7 +1084,7 @@ dma_trait!(Dma, Instance, SubBlockInstance); foreach_peripheral!( (sai, $inst:ident) => { - impl sealed::Instance for peripherals::$inst { + impl SealedInstance for peripherals::$inst { const REGS: Regs = crate::pac::$inst; } diff --git a/embassy-stm32/src/sdmmc/mod.rs b/embassy-stm32/src/sdmmc/mod.rs index c0b3a2183..f79a11606 100644 --- a/embassy-stm32/src/sdmmc/mod.rs +++ b/embassy-stm32/src/sdmmc/mod.rs @@ -13,8 +13,7 @@ use embassy_sync::waitqueue::AtomicWaker; use sdio_host::{BusWidth, CardCapacity, CardStatus, CurrentState, SDStatus, CID, CSD, OCR, SCR}; use crate::dma::NoDma; -use crate::gpio::sealed::{AFType, Pin}; -use crate::gpio::{AnyPin, Pull, Speed}; +use crate::gpio::{AFType, AnyPin, Pull, SealedPin, Speed}; use crate::interrupt::typelevel::Interrupt; use crate::pac::sdmmc::Sdmmc as RegBlock; use crate::rcc::RccPeripheral; @@ -1418,19 +1417,17 @@ impl Cmd { ////////////////////////////////////////////////////// -pub(crate) mod sealed { - use super::*; - - pub trait Instance { - type Interrupt: interrupt::typelevel::Interrupt; - - fn regs() -> RegBlock; - fn state() -> &'static AtomicWaker; - } +trait SealedInstance { + fn regs() -> RegBlock; + fn state() -> &'static AtomicWaker; } /// SDMMC instance trait. -pub trait Instance: sealed::Instance + RccPeripheral + 'static {} +#[allow(private_bounds)] +pub trait Instance: SealedInstance + RccPeripheral + 'static { + /// Interrupt for this instance. + type Interrupt: interrupt::typelevel::Interrupt; +} pin_trait!(CkPin, Instance); pin_trait!(CmdPin, Instance); @@ -1457,9 +1454,7 @@ impl SdmmcDma for NoDma {} foreach_peripheral!( (sdmmc, $inst:ident) => { - impl sealed::Instance for peripherals::$inst { - type Interrupt = crate::interrupt::typelevel::$inst; - + impl SealedInstance for peripherals::$inst { fn regs() -> RegBlock { crate::pac::$inst } @@ -1470,6 +1465,8 @@ foreach_peripheral!( } } - impl Instance for peripherals::$inst {} + impl Instance for peripherals::$inst { + type Interrupt = crate::interrupt::typelevel::$inst; + } }; ); diff --git a/embassy-stm32/src/spi/mod.rs b/embassy-stm32/src/spi/mod.rs index b517f640a..0b38c4288 100644 --- a/embassy-stm32/src/spi/mod.rs +++ b/embassy-stm32/src/spi/mod.rs @@ -9,8 +9,7 @@ use embassy_hal_internal::{into_ref, PeripheralRef}; pub use embedded_hal_02::spi::{Mode, Phase, Polarity, MODE_0, MODE_1, MODE_2, MODE_3}; use crate::dma::{slice_ptr_parts, word, Transfer}; -use crate::gpio::sealed::{AFType, Pin as _}; -use crate::gpio::{AnyPin, Pull}; +use crate::gpio::{AFType, AnyPin, Pull, SealedPin as _}; use crate::pac::spi::{regs, vals, Spi as Regs}; use crate::rcc::RccPeripheral; use crate::time::Hertz; @@ -210,7 +209,7 @@ impl<'d, T: Instance, Tx, Rx> Spi<'d, T, Tx, Rx> { // see RM0453 rev 1 section 7.2.13 page 291 // The SUBGHZSPI_SCK frequency is obtained by PCLK3 divided by two. // The SUBGHZSPI_SCK clock maximum speed must not exceed 16 MHz. - let pclk3_freq = ::frequency().0; + let pclk3_freq = ::frequency().0; let freq = Hertz(core::cmp::min(pclk3_freq / 2, 16_000_000)); let mut config = Config::default(); config.mode = MODE_0; @@ -271,13 +270,13 @@ impl<'d, T: Instance, Tx, Rx> Spi<'d, T, Tx, Rx> { if mosi.is_none() { w.set_rxonly(vals::Rxonly::OUTPUTDISABLED); } - w.set_dff(::CONFIG) + w.set_dff(::CONFIG) }); } #[cfg(spi_v2)] { T::REGS.cr2().modify(|w| { - let (ds, frxth) = ::CONFIG; + let (ds, frxth) = ::CONFIG; w.set_frxth(frxth); w.set_ds(ds); w.set_ssoe(false); @@ -317,7 +316,7 @@ impl<'d, T: Instance, Tx, Rx> Spi<'d, T, Tx, Rx> { T::REGS.cfg1().modify(|w| { w.set_crcen(false); w.set_mbr(br); - w.set_dsize(::CONFIG); + w.set_dsize(::CONFIG); w.set_fthlv(vals::Fthlv::ONEFRAME); }); T::REGS.cr2().modify(|w| { @@ -336,7 +335,7 @@ impl<'d, T: Instance, Tx, Rx> Spi<'d, T, Tx, Rx> { miso, txdma, rxdma, - current_word_size: ::CONFIG, + current_word_size: ::CONFIG, } } @@ -975,24 +974,21 @@ impl<'d, T: Instance, Tx: TxDma, Rx: RxDma, W: Word> embedded_hal_async::s } } -pub(crate) mod sealed { - use super::*; +pub(crate) trait SealedInstance { + const REGS: Regs; +} - pub trait Instance { - const REGS: Regs; - } - - pub trait Word { - const CONFIG: word_impl::Config; - } +trait SealedWord { + const CONFIG: word_impl::Config; } /// Word sizes usable for SPI. -pub trait Word: word::Word + sealed::Word {} +#[allow(private_bounds)] +pub trait Word: word::Word + SealedWord {} macro_rules! impl_word { ($T:ty, $config:expr) => { - impl sealed::Word for $T { + impl SealedWord for $T { const CONFIG: Config = $config; } impl Word for $T {} @@ -1068,7 +1064,8 @@ mod word_impl { } /// SPI instance trait. -pub trait Instance: Peripheral

+ sealed::Instance + RccPeripheral {} +#[allow(private_bounds)] +pub trait Instance: Peripheral

+ SealedInstance + RccPeripheral {} pin_trait!(SckPin, Instance); pin_trait!(MosiPin, Instance); @@ -1082,7 +1079,7 @@ dma_trait!(TxDma, Instance); foreach_peripheral!( (spi, $inst:ident) => { - impl sealed::Instance for peripherals::$inst { + impl SealedInstance for peripherals::$inst { const REGS: Regs = crate::pac::$inst; } diff --git a/embassy-stm32/src/time_driver.rs b/embassy-stm32/src/time_driver.rs index c961fc14b..cc8161276 100644 --- a/embassy-stm32/src/time_driver.rs +++ b/embassy-stm32/src/time_driver.rs @@ -12,7 +12,7 @@ use stm32_metapac::timer::{regs, TimGp16}; use crate::interrupt::typelevel::Interrupt; use crate::pac::timer::vals; -use crate::rcc::sealed::RccPeripheral; +use crate::rcc::SealedRccPeripheral; #[cfg(feature = "low-power")] use crate::rtc::Rtc; #[cfg(any(time_driver_tim1, time_driver_tim8, time_driver_tim20))] @@ -277,7 +277,7 @@ impl RtcDriver { fn init(&'static self, cs: critical_section::CriticalSection) { let r = regs_gp16(); - ::enable_and_reset_with_cs(cs); + ::enable_and_reset_with_cs(cs); let timer_freq = T::frequency(); diff --git a/embassy-stm32/src/timer/qei.rs b/embassy-stm32/src/timer/qei.rs index b6ab939cc..ab9879be6 100644 --- a/embassy-stm32/src/timer/qei.rs +++ b/embassy-stm32/src/timer/qei.rs @@ -7,8 +7,7 @@ use stm32_metapac::timer::vals; use super::low_level::Timer; use super::{Channel1Pin, Channel2Pin, GeneralInstance4Channel}; -use crate::gpio::sealed::AFType; -use crate::gpio::AnyPin; +use crate::gpio::{AFType, AnyPin}; use crate::Peripheral; /// Counting direction diff --git a/embassy-stm32/src/ucpd.rs b/embassy-stm32/src/ucpd.rs index 9c37d2c04..fe614b811 100644 --- a/embassy-stm32/src/ucpd.rs +++ b/embassy-stm32/src/ucpd.rs @@ -16,11 +16,12 @@ use core::future::poll_fn; use core::marker::PhantomData; -use core::sync::atomic::Ordering; +use core::sync::atomic::{AtomicBool, Ordering}; use core::task::Poll; use embassy_hal_internal::drop::OnDrop; use embassy_hal_internal::{into_ref, Peripheral, PeripheralRef}; +use embassy_sync::waitqueue::AtomicWaker; use crate::dma::{AnyChannel, Request, Transfer, TransferOptions}; use crate::interrupt; @@ -555,50 +556,47 @@ impl interrupt::typelevel::Handler for InterruptHandl } } -/// UCPD instance trait. -pub trait Instance: sealed::Instance + RccPeripheral {} +struct State { + waker: AtomicWaker, + // Inverted logic for a default state of 0 so that the data goes into the .bss section. + drop_not_ready: AtomicBool, +} -mod sealed { - use core::sync::atomic::AtomicBool; - - use embassy_sync::waitqueue::AtomicWaker; - - pub struct State { - pub waker: AtomicWaker, - // Inverted logic for a default state of 0 so that the data goes into the .bss section. - pub drop_not_ready: AtomicBool, - } - - impl State { - pub const fn new() -> Self { - Self { - waker: AtomicWaker::new(), - drop_not_ready: AtomicBool::new(false), - } +impl State { + pub const fn new() -> Self { + Self { + waker: AtomicWaker::new(), + drop_not_ready: AtomicBool::new(false), } } +} - pub trait Instance { - type Interrupt: crate::interrupt::typelevel::Interrupt; - const REGS: crate::pac::ucpd::Ucpd; - fn state() -> &'static crate::ucpd::sealed::State; - } +trait SealedInstance { + const REGS: crate::pac::ucpd::Ucpd; + fn state() -> &'static State; +} + +/// UCPD instance trait. +#[allow(private_bounds)] +pub trait Instance: SealedInstance + RccPeripheral { + /// Interrupt for this instance. + type Interrupt: crate::interrupt::typelevel::Interrupt; } foreach_interrupt!( ($inst:ident, ucpd, UCPD, GLOBAL, $irq:ident) => { - impl sealed::Instance for crate::peripherals::$inst { - type Interrupt = crate::interrupt::typelevel::$irq; - + impl SealedInstance for crate::peripherals::$inst { const REGS: crate::pac::ucpd::Ucpd = crate::pac::$inst; - fn state() -> &'static crate::ucpd::sealed::State { - static STATE: crate::ucpd::sealed::State = crate::ucpd::sealed::State::new(); + fn state() -> &'static State { + static STATE: State = State::new(); &STATE } } - impl Instance for crate::peripherals::$inst {} + impl Instance for crate::peripherals::$inst { + type Interrupt = crate::interrupt::typelevel::$irq; + } }; ); diff --git a/embassy-stm32/src/usart/mod.rs b/embassy-stm32/src/usart/mod.rs index ea727b010..7ab33043a 100644 --- a/embassy-stm32/src/usart/mod.rs +++ b/embassy-stm32/src/usart/mod.rs @@ -10,10 +10,11 @@ use core::task::Poll; use embassy_embedded_hal::SetConfig; use embassy_hal_internal::drop::OnDrop; use embassy_hal_internal::{into_ref, PeripheralRef}; +use embassy_sync::waitqueue::AtomicWaker; use futures::future::{select, Either}; use crate::dma::{NoDma, Transfer}; -use crate::gpio::sealed::AFType; +use crate::gpio::AFType; use crate::interrupt::typelevel::Interrupt; #[allow(unused_imports)] #[cfg(not(any(usart_v1, usart_v2)))] @@ -1326,8 +1327,6 @@ mod ringbuffered; #[cfg(not(gpdma))] pub use ringbuffered::RingBufferedUartRx; -use self::sealed::Kind; - #[cfg(any(usart_v1, usart_v2))] fn tdr(r: crate::pac::usart::Usart) -> *mut u8 { r.dr().as_ptr() as _ @@ -1370,52 +1369,50 @@ fn clear_interrupt_flags(r: Regs, sr: regs::Isr) { r.icr().write(|w| *w = regs::Icr(sr.0)); } -pub(crate) mod sealed { - use embassy_sync::waitqueue::AtomicWaker; +#[derive(Clone, Copy, PartialEq, Eq)] +enum Kind { + Uart, + #[cfg(any(usart_v3, usart_v4))] + #[allow(unused)] + Lpuart, +} - use super::*; +struct State { + rx_waker: AtomicWaker, +} - #[derive(Clone, Copy, PartialEq, Eq)] - pub enum Kind { - Uart, - #[cfg(any(usart_v3, usart_v4))] - Lpuart, - } - - pub struct State { - pub rx_waker: AtomicWaker, - pub tx_waker: AtomicWaker, - } - - impl State { - pub const fn new() -> Self { - Self { - rx_waker: AtomicWaker::new(), - tx_waker: AtomicWaker::new(), - } +impl State { + const fn new() -> Self { + Self { + rx_waker: AtomicWaker::new(), } } - - pub trait BasicInstance: crate::rcc::RccPeripheral { - const KIND: Kind; - type Interrupt: interrupt::typelevel::Interrupt; - - fn regs() -> Regs; - fn state() -> &'static State; - - fn buffered_state() -> &'static buffered::State; - } - - pub trait FullInstance: BasicInstance { - fn regs_uart() -> crate::pac::usart::Usart; - } +} + +trait SealedBasicInstance: crate::rcc::RccPeripheral { + const KIND: Kind; + + fn regs() -> Regs; + fn state() -> &'static State; + + fn buffered_state() -> &'static buffered::State; +} + +trait SealedFullInstance: SealedBasicInstance { + #[allow(unused)] + fn regs_uart() -> crate::pac::usart::Usart; } /// Basic UART driver instance -pub trait BasicInstance: Peripheral

+ sealed::BasicInstance + 'static + Send {} +#[allow(private_bounds)] +pub trait BasicInstance: Peripheral

+ SealedBasicInstance + 'static + Send { + /// Interrupt for this instance. + type Interrupt: interrupt::typelevel::Interrupt; +} /// Full UART driver instance -pub trait FullInstance: sealed::FullInstance {} +#[allow(private_bounds)] +pub trait FullInstance: SealedFullInstance {} pin_trait!(RxPin, BasicInstance); pin_trait!(TxPin, BasicInstance); @@ -1429,16 +1426,15 @@ dma_trait!(RxDma, BasicInstance); macro_rules! impl_usart { ($inst:ident, $irq:ident, $kind:expr) => { - impl sealed::BasicInstance for crate::peripherals::$inst { + impl SealedBasicInstance for crate::peripherals::$inst { const KIND: Kind = $kind; - type Interrupt = crate::interrupt::typelevel::$irq; fn regs() -> Regs { unsafe { Regs::from_ptr(crate::pac::$inst.as_ptr()) } } - fn state() -> &'static crate::usart::sealed::State { - static STATE: crate::usart::sealed::State = crate::usart::sealed::State::new(); + fn state() -> &'static crate::usart::State { + static STATE: crate::usart::State = crate::usart::State::new(); &STATE } @@ -1448,7 +1444,9 @@ macro_rules! impl_usart { } } - impl BasicInstance for peripherals::$inst {} + impl BasicInstance for peripherals::$inst { + type Interrupt = crate::interrupt::typelevel::$irq; + } }; } @@ -1460,7 +1458,7 @@ foreach_interrupt!( ($inst:ident, usart, $block:ident, $signal_name:ident, $irq:ident) => { impl_usart!($inst, $irq, Kind::Uart); - impl sealed::FullInstance for peripherals::$inst { + impl SealedFullInstance for peripherals::$inst { fn regs_uart() -> crate::pac::usart::Usart { crate::pac::$inst } diff --git a/embassy-stm32/src/usb/mod.rs b/embassy-stm32/src/usb/mod.rs index 788f61f16..1e3c44167 100644 --- a/embassy-stm32/src/usb/mod.rs +++ b/embassy-stm32/src/usb/mod.rs @@ -6,7 +6,7 @@ mod _version; pub use _version::*; use crate::interrupt::typelevel::Interrupt; -use crate::rcc::sealed::RccPeripheral; +use crate::rcc::SealedRccPeripheral; /// clock, power initialization stuff that's common for USB and OTG. fn common_init() { @@ -65,5 +65,5 @@ fn common_init() { T::Interrupt::unpend(); unsafe { T::Interrupt::enable() }; - ::enable_and_reset(); + ::enable_and_reset(); } diff --git a/embassy-stm32/src/usb/otg.rs b/embassy-stm32/src/usb/otg.rs index d4095b466..b0e7067bd 100644 --- a/embassy-stm32/src/usb/otg.rs +++ b/embassy-stm32/src/usb/otg.rs @@ -11,11 +11,11 @@ use embassy_usb_driver::{ }; use futures::future::poll_fn; -use crate::gpio::sealed::AFType; +use crate::gpio::AFType; use crate::interrupt; use crate::interrupt::typelevel::Interrupt; use crate::pac::otg::{regs, vals}; -use crate::rcc::sealed::RccPeripheral; +use crate::rcc::{RccPeripheral, SealedRccPeripheral}; use crate::time::Hertz; /// Interrupt handler. @@ -809,7 +809,7 @@ impl<'d, T: Instance> Bus<'d, T> { fn disable(&mut self) { T::Interrupt::disable(); - ::disable(); + ::disable(); #[cfg(stm32l4)] crate::pac::PWR.cr2().modify(|w| w.set_usv(false)); @@ -1436,19 +1436,18 @@ fn quirk_setup_late_cnak(r: crate::pac::otg::Otg) -> bool { // Using Instance::ENDPOINT_COUNT requires feature(const_generic_expr) so just define maximum eps const MAX_EP_COUNT: usize = 9; -pub(crate) mod sealed { - pub trait Instance { - const HIGH_SPEED: bool; - const FIFO_DEPTH_WORDS: u16; - const ENDPOINT_COUNT: usize; +trait SealedInstance { + const HIGH_SPEED: bool; + const FIFO_DEPTH_WORDS: u16; + const ENDPOINT_COUNT: usize; - fn regs() -> crate::pac::otg::Otg; - fn state() -> &'static super::State<{ super::MAX_EP_COUNT }>; - } + fn regs() -> crate::pac::otg::Otg; + fn state() -> &'static super::State<{ MAX_EP_COUNT }>; } /// USB instance trait. -pub trait Instance: sealed::Instance + RccPeripheral + 'static { +#[allow(private_bounds)] +pub trait Instance: SealedInstance + RccPeripheral + 'static { /// Interrupt for this USB instance. type Interrupt: interrupt::typelevel::Interrupt; } @@ -1473,7 +1472,7 @@ pin_trait!(UlpiD7Pin, Instance); foreach_interrupt!( (USB_OTG_FS, otg, $block:ident, GLOBAL, $irq:ident) => { - impl sealed::Instance for crate::peripherals::USB_OTG_FS { + impl SealedInstance for crate::peripherals::USB_OTG_FS { const HIGH_SPEED: bool = false; cfg_if::cfg_if! { @@ -1538,7 +1537,7 @@ foreach_interrupt!( }; (USB_OTG_HS, otg, $block:ident, GLOBAL, $irq:ident) => { - impl sealed::Instance for crate::peripherals::USB_OTG_HS { + impl SealedInstance for crate::peripherals::USB_OTG_HS { const HIGH_SPEED: bool = true; cfg_if::cfg_if! { diff --git a/embassy-stm32/src/usb/usb.rs b/embassy-stm32/src/usb/usb.rs index c4f9140da..f48808cb3 100644 --- a/embassy-stm32/src/usb/usb.rs +++ b/embassy-stm32/src/usb/usb.rs @@ -15,7 +15,7 @@ use embassy_usb_driver::{ use crate::pac::usb::regs; use crate::pac::usb::vals::{EpType, Stat}; use crate::pac::USBRAM; -use crate::rcc::sealed::RccPeripheral; +use crate::rcc::RccPeripheral; use crate::{interrupt, Peripheral}; /// Interrupt handler. @@ -277,8 +277,8 @@ impl<'d, T: Instance> Driver<'d, T> { #[cfg(not(stm32l1))] { - dp.set_as_af(dp.af_num(), crate::gpio::sealed::AFType::OutputPushPull); - dm.set_as_af(dm.af_num(), crate::gpio::sealed::AFType::OutputPushPull); + dp.set_as_af(dp.af_num(), crate::gpio::AFType::OutputPushPull); + dm.set_as_af(dm.af_num(), crate::gpio::AFType::OutputPushPull); } #[cfg(stm32l1)] let _ = (dp, dm); // suppress "unused" warnings. @@ -1037,14 +1037,13 @@ impl<'d, T: Instance> driver::ControlPipe for ControlPipe<'d, T> { } } -pub(crate) mod sealed { - pub trait Instance { - fn regs() -> crate::pac::usb::Usb; - } +trait SealedInstance { + fn regs() -> crate::pac::usb::Usb; } /// USB instance trait. -pub trait Instance: sealed::Instance + RccPeripheral + 'static { +#[allow(private_bounds)] +pub trait Instance: SealedInstance + RccPeripheral + 'static { /// Interrupt for this USB instance. type Interrupt: interrupt::typelevel::Interrupt; } @@ -1055,7 +1054,7 @@ pin_trait!(DmPin, Instance); foreach_interrupt!( ($inst:ident, usb, $block:ident, LP, $irq:ident) => { - impl sealed::Instance for crate::peripherals::$inst { + impl SealedInstance for crate::peripherals::$inst { fn regs() -> crate::pac::usb::Usb { crate::pac::$inst } diff --git a/embassy-stm32/src/wdg/mod.rs b/embassy-stm32/src/wdg/mod.rs index 2ff0db09e..ab21c4b6b 100644 --- a/embassy-stm32/src/wdg/mod.rs +++ b/embassy-stm32/src/wdg/mod.rs @@ -80,18 +80,17 @@ impl<'d, T: Instance> IndependentWatchdog<'d, T> { } } -mod sealed { - pub trait Instance { - fn regs() -> crate::pac::iwdg::Iwdg; - } +trait SealedInstance { + fn regs() -> crate::pac::iwdg::Iwdg; } /// IWDG instance trait. -pub trait Instance: sealed::Instance {} +#[allow(private_bounds)] +pub trait Instance: SealedInstance {} foreach_peripheral!( (iwdg, $inst:ident) => { - impl sealed::Instance for crate::peripherals::$inst { + impl SealedInstance for crate::peripherals::$inst { fn regs() -> crate::pac::iwdg::Iwdg { crate::pac::$inst } diff --git a/examples/stm32h7/src/bin/dac_dma.rs b/examples/stm32h7/src/bin/dac_dma.rs index c45747f35..3a9887e3c 100644 --- a/examples/stm32h7/src/bin/dac_dma.rs +++ b/examples/stm32h7/src/bin/dac_dma.rs @@ -6,7 +6,7 @@ use embassy_executor::Spawner; use embassy_stm32::dac::{DacCh1, DacCh2, ValueArray}; use embassy_stm32::pac::timer::vals::Mms; use embassy_stm32::peripherals::{DAC1, DMA1_CH3, DMA1_CH4, TIM6, TIM7}; -use embassy_stm32::rcc::low_level::RccPeripheral; +use embassy_stm32::rcc::frequency; use embassy_stm32::time::Hertz; use embassy_stm32::timer::low_level::Timer; use micromath::F32Ext; @@ -59,11 +59,11 @@ async fn main(spawner: Spawner) { async fn dac_task1(tim: TIM6, mut dac: DacCh1<'static, DAC1, DMA1_CH3>) { let data: &[u8; 256] = &calculate_array::<256>(); - info!("TIM6 frequency is {}", TIM6::frequency()); + info!("TIM6 frequency is {}", frequency::()); const FREQUENCY: Hertz = Hertz::hz(200); // Compute the reload value such that we obtain the FREQUENCY for the sine - let reload: u32 = (TIM6::frequency().0 / FREQUENCY.0) / data.len() as u32; + let reload: u32 = (frequency::().0 / FREQUENCY.0) / data.len() as u32; // Depends on your clock and on the specific chip used, you may need higher or lower values here if reload < 10 { @@ -84,7 +84,7 @@ async fn dac_task1(tim: TIM6, mut dac: DacCh1<'static, DAC1, DMA1_CH3>) { debug!( "TIM6 Frequency {}, Target Frequency {}, Reload {}, Reload as u16 {}, Samples {}", - TIM6::frequency(), + frequency::(), FREQUENCY, reload, reload as u16, @@ -102,10 +102,10 @@ async fn dac_task1(tim: TIM6, mut dac: DacCh1<'static, DAC1, DMA1_CH3>) { async fn dac_task2(tim: TIM7, mut dac: DacCh2<'static, DAC1, DMA1_CH4>) { let data: &[u8; 256] = &calculate_array::<256>(); - info!("TIM7 frequency is {}", TIM7::frequency()); + info!("TIM7 frequency is {}", frequency::()); const FREQUENCY: Hertz = Hertz::hz(600); - let reload: u32 = (TIM7::frequency().0 / FREQUENCY.0) / data.len() as u32; + let reload: u32 = (frequency::().0 / FREQUENCY.0) / data.len() as u32; if reload < 10 { error!("Reload value {} below threshold!", reload); @@ -125,7 +125,7 @@ async fn dac_task2(tim: TIM7, mut dac: DacCh2<'static, DAC1, DMA1_CH4>) { debug!( "TIM7 Frequency {}, Target Frequency {}, Reload {}, Reload as u16 {}, Samples {}", - TIM7::frequency(), + frequency::(), FREQUENCY, reload, reload as u16, diff --git a/examples/stm32h7/src/bin/low_level_timer_api.rs b/examples/stm32h7/src/bin/low_level_timer_api.rs index 780fbc6f0..a95b44b74 100644 --- a/examples/stm32h7/src/bin/low_level_timer_api.rs +++ b/examples/stm32h7/src/bin/low_level_timer_api.rs @@ -3,8 +3,7 @@ use defmt::*; use embassy_executor::Spawner; -use embassy_stm32::gpio::low_level::AFType; -use embassy_stm32::gpio::Speed; +use embassy_stm32::gpio::{AFType, Flex, Pull, Speed}; use embassy_stm32::time::{khz, Hertz}; use embassy_stm32::timer::low_level::{OutputCompareMode, Timer as LLTimer}; use embassy_stm32::timer::{Channel, Channel1Pin, Channel2Pin, Channel3Pin, Channel4Pin, GeneralInstance32bit4Channel}; @@ -59,6 +58,10 @@ async fn main(_spawner: Spawner) { } pub struct SimplePwm32<'d, T: GeneralInstance32bit4Channel> { tim: LLTimer<'d, T>, + _ch1: Flex<'d>, + _ch2: Flex<'d>, + _ch3: Flex<'d>, + _ch4: Flex<'d>, } impl<'d, T: GeneralInstance32bit4Channel> SimplePwm32<'d, T> { @@ -72,16 +75,26 @@ impl<'d, T: GeneralInstance32bit4Channel> SimplePwm32<'d, T> { ) -> Self { into_ref!(ch1, ch2, ch3, ch4); - ch1.set_speed(Speed::VeryHigh); - ch1.set_as_af(ch1.af_num(), AFType::OutputPushPull); - ch2.set_speed(Speed::VeryHigh); - ch2.set_as_af(ch1.af_num(), AFType::OutputPushPull); - ch3.set_speed(Speed::VeryHigh); - ch3.set_as_af(ch1.af_num(), AFType::OutputPushPull); - ch4.set_speed(Speed::VeryHigh); - ch4.set_as_af(ch1.af_num(), AFType::OutputPushPull); + let af1 = ch1.af_num(); + let af2 = ch2.af_num(); + let af3 = ch3.af_num(); + let af4 = ch4.af_num(); + let mut ch1 = Flex::new(ch1); + let mut ch2 = Flex::new(ch2); + let mut ch3 = Flex::new(ch3); + let mut ch4 = Flex::new(ch4); + ch1.set_as_af_unchecked(af1, AFType::OutputPushPull, Pull::None, Speed::VeryHigh); + ch2.set_as_af_unchecked(af2, AFType::OutputPushPull, Pull::None, Speed::VeryHigh); + ch3.set_as_af_unchecked(af3, AFType::OutputPushPull, Pull::None, Speed::VeryHigh); + ch4.set_as_af_unchecked(af4, AFType::OutputPushPull, Pull::None, Speed::VeryHigh); - let mut this = Self { tim: LLTimer::new(tim) }; + let mut this = Self { + tim: LLTimer::new(tim), + _ch1: ch1, + _ch2: ch2, + _ch3: ch3, + _ch4: ch4, + }; this.set_frequency(freq); this.tim.start(); diff --git a/examples/stm32l4/src/bin/dac_dma.rs b/examples/stm32l4/src/bin/dac_dma.rs index 98edd39c0..d01b016c0 100644 --- a/examples/stm32l4/src/bin/dac_dma.rs +++ b/examples/stm32l4/src/bin/dac_dma.rs @@ -6,7 +6,7 @@ use embassy_executor::Spawner; use embassy_stm32::dac::{DacCh1, DacCh2, ValueArray}; use embassy_stm32::pac::timer::vals::Mms; use embassy_stm32::peripherals::{DAC1, DMA1_CH3, DMA1_CH4, TIM6, TIM7}; -use embassy_stm32::rcc::low_level::RccPeripheral; +use embassy_stm32::rcc::frequency; use embassy_stm32::time::Hertz; use embassy_stm32::timer::low_level::Timer; use micromath::F32Ext; @@ -30,11 +30,11 @@ async fn main(spawner: Spawner) { async fn dac_task1(tim: TIM6, mut dac: DacCh1<'static, DAC1, DMA1_CH3>) { let data: &[u8; 256] = &calculate_array::<256>(); - info!("TIM6 frequency is {}", TIM6::frequency()); + info!("TIM6 frequency is {}", frequency::()); const FREQUENCY: Hertz = Hertz::hz(200); // Compute the reload value such that we obtain the FREQUENCY for the sine - let reload: u32 = (TIM6::frequency().0 / FREQUENCY.0) / data.len() as u32; + let reload: u32 = (frequency::().0 / FREQUENCY.0) / data.len() as u32; // Depends on your clock and on the specific chip used, you may need higher or lower values here if reload < 10 { @@ -55,7 +55,7 @@ async fn dac_task1(tim: TIM6, mut dac: DacCh1<'static, DAC1, DMA1_CH3>) { debug!( "TIM6 Frequency {}, Target Frequency {}, Reload {}, Reload as u16 {}, Samples {}", - TIM6::frequency(), + frequency::(), FREQUENCY, reload, reload as u16, @@ -73,10 +73,10 @@ async fn dac_task1(tim: TIM6, mut dac: DacCh1<'static, DAC1, DMA1_CH3>) { async fn dac_task2(tim: TIM7, mut dac: DacCh2<'static, DAC1, DMA1_CH4>) { let data: &[u8; 256] = &calculate_array::<256>(); - info!("TIM7 frequency is {}", TIM7::frequency()); + info!("TIM7 frequency is {}", frequency::()); const FREQUENCY: Hertz = Hertz::hz(600); - let reload: u32 = (TIM7::frequency().0 / FREQUENCY.0) / data.len() as u32; + let reload: u32 = (frequency::().0 / FREQUENCY.0) / data.len() as u32; if reload < 10 { error!("Reload value {} below threshold!", reload); @@ -96,7 +96,7 @@ async fn dac_task2(tim: TIM7, mut dac: DacCh2<'static, DAC1, DMA1_CH4>) { debug!( "TIM7 Frequency {}, Target Frequency {}, Reload {}, Reload as u16 {}, Samples {}", - TIM7::frequency(), + frequency::(), FREQUENCY, reload, reload as u16, From 4aa4ea99c2c860d3c3012fee55db9b824d2ad2ef Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Sat, 23 Mar 2024 01:44:46 +0100 Subject: [PATCH 26/62] use private_bounds for sealed traits. --- embassy-hal-internal/src/interrupt.rs | 8 ++-- embassy-net-wiznet/src/chip/mod.rs | 63 ++++++++++++--------------- embassy-net-wiznet/src/chip/w5100s.rs | 2 +- embassy-net-wiznet/src/chip/w5500.rs | 2 +- embassy-usb/src/msos.rs | 28 +++++------- 5 files changed, 44 insertions(+), 59 deletions(-) diff --git a/embassy-hal-internal/src/interrupt.rs b/embassy-hal-internal/src/interrupt.rs index 19dabcf6f..5e64dce9d 100644 --- a/embassy-hal-internal/src/interrupt.rs +++ b/embassy-hal-internal/src/interrupt.rs @@ -30,14 +30,12 @@ macro_rules! interrupt_mod { pub mod typelevel { use super::InterruptExt; - mod sealed { - pub trait Interrupt {} - } + trait SealedInterrupt {} /// Type-level interrupt. /// /// This trait is implemented for all typelevel interrupt types in this module. - pub trait Interrupt: sealed::Interrupt { + pub trait Interrupt: SealedInterrupt { /// Interrupt enum variant. /// @@ -105,7 +103,7 @@ macro_rules! interrupt_mod { #[doc=stringify!($irqs)] #[doc=" typelevel interrupt."] pub enum $irqs {} - impl sealed::Interrupt for $irqs{} + impl SealedInterrupt for $irqs{} impl Interrupt for $irqs { const IRQ: super::Interrupt = super::Interrupt::$irqs; } diff --git a/embassy-net-wiznet/src/chip/mod.rs b/embassy-net-wiznet/src/chip/mod.rs index b987c2b36..e1f963d95 100644 --- a/embassy-net-wiznet/src/chip/mod.rs +++ b/embassy-net-wiznet/src/chip/mod.rs @@ -2,49 +2,40 @@ mod w5500; pub use w5500::W5500; mod w5100s; +use embedded_hal_async::spi::SpiDevice; pub use w5100s::W5100S; -pub(crate) mod sealed { - use embedded_hal_async::spi::SpiDevice; +pub(crate) trait SealedChip { + type Address; - pub trait Chip { - type Address; + const COMMON_MODE: Self::Address; + const COMMON_MAC: Self::Address; + const COMMON_SOCKET_INTR: Self::Address; + const COMMON_PHY_CFG: Self::Address; + const SOCKET_MODE: Self::Address; + const SOCKET_COMMAND: Self::Address; + const SOCKET_RXBUF_SIZE: Self::Address; + const SOCKET_TXBUF_SIZE: Self::Address; + const SOCKET_TX_FREE_SIZE: Self::Address; + const SOCKET_TX_DATA_WRITE_PTR: Self::Address; + const SOCKET_RECVD_SIZE: Self::Address; + const SOCKET_RX_DATA_READ_PTR: Self::Address; + const SOCKET_INTR_MASK: Self::Address; + const SOCKET_INTR: Self::Address; - const COMMON_MODE: Self::Address; - const COMMON_MAC: Self::Address; - const COMMON_SOCKET_INTR: Self::Address; - const COMMON_PHY_CFG: Self::Address; - const SOCKET_MODE: Self::Address; - const SOCKET_COMMAND: Self::Address; - const SOCKET_RXBUF_SIZE: Self::Address; - const SOCKET_TXBUF_SIZE: Self::Address; - const SOCKET_TX_FREE_SIZE: Self::Address; - const SOCKET_TX_DATA_WRITE_PTR: Self::Address; - const SOCKET_RECVD_SIZE: Self::Address; - const SOCKET_RX_DATA_READ_PTR: Self::Address; - const SOCKET_INTR_MASK: Self::Address; - const SOCKET_INTR: Self::Address; + const SOCKET_MODE_VALUE: u8; - const SOCKET_MODE_VALUE: u8; + const BUF_SIZE: u16; + const AUTO_WRAP: bool; - const BUF_SIZE: u16; - const AUTO_WRAP: bool; + fn rx_addr(addr: u16) -> Self::Address; + fn tx_addr(addr: u16) -> Self::Address; - fn rx_addr(addr: u16) -> Self::Address; - fn tx_addr(addr: u16) -> Self::Address; - - async fn bus_read( - spi: &mut SPI, - address: Self::Address, - data: &mut [u8], - ) -> Result<(), SPI::Error>; - async fn bus_write( - spi: &mut SPI, - address: Self::Address, - data: &[u8], - ) -> Result<(), SPI::Error>; - } + async fn bus_read(spi: &mut SPI, address: Self::Address, data: &mut [u8]) + -> Result<(), SPI::Error>; + async fn bus_write(spi: &mut SPI, address: Self::Address, data: &[u8]) -> Result<(), SPI::Error>; } /// Trait for Wiznet chips. -pub trait Chip: sealed::Chip {} +#[allow(private_bounds)] +pub trait Chip: SealedChip {} diff --git a/embassy-net-wiznet/src/chip/w5100s.rs b/embassy-net-wiznet/src/chip/w5100s.rs index 7d328bce5..23ce3ed83 100644 --- a/embassy-net-wiznet/src/chip/w5100s.rs +++ b/embassy-net-wiznet/src/chip/w5100s.rs @@ -8,7 +8,7 @@ const RX_BASE: u16 = 0x6000; pub enum W5100S {} impl super::Chip for W5100S {} -impl super::sealed::Chip for W5100S { +impl super::SealedChip for W5100S { type Address = u16; const COMMON_MODE: Self::Address = 0x00; diff --git a/embassy-net-wiznet/src/chip/w5500.rs b/embassy-net-wiznet/src/chip/w5500.rs index 16236126d..12e610ea2 100644 --- a/embassy-net-wiznet/src/chip/w5500.rs +++ b/embassy-net-wiznet/src/chip/w5500.rs @@ -12,7 +12,7 @@ pub enum RegisterBlock { pub enum W5500 {} impl super::Chip for W5500 {} -impl super::sealed::Chip for W5500 { +impl super::SealedChip for W5500 { type Address = (RegisterBlock, u16); const COMMON_MODE: Self::Address = (RegisterBlock::Common, 0x00); diff --git a/embassy-usb/src/msos.rs b/embassy-usb/src/msos.rs index a285a3ccd..25936d084 100644 --- a/embassy-usb/src/msos.rs +++ b/embassy-usb/src/msos.rs @@ -226,27 +226,21 @@ pub mod windows_version { pub const WIN10: u32 = 0x0A000000; } -mod sealed { - use core::mem::size_of; +/// A trait for descriptors +trait Descriptor: Sized { + const TYPE: DescriptorType; - /// A trait for descriptors - pub trait Descriptor: Sized { - const TYPE: super::DescriptorType; - - /// The size of the descriptor's header. - fn size(&self) -> usize { - size_of::() - } - - fn write_to(&self, buf: &mut [u8]); + /// The size of the descriptor's header. + fn size(&self) -> usize { + size_of::() } - pub trait DescriptorSet: Descriptor { - const LENGTH_OFFSET: usize; - } + fn write_to(&self, buf: &mut [u8]); } -use sealed::*; +trait DescriptorSet: Descriptor { + const LENGTH_OFFSET: usize; +} /// Copies the data of `t` into `buf`. /// @@ -412,9 +406,11 @@ impl DescriptorSet for FunctionSubsetHeader { // Feature Descriptors /// A marker trait for feature descriptors that are valid at the device level. +#[allow(private_bounds)] pub trait DeviceLevelDescriptor: Descriptor {} /// A marker trait for feature descriptors that are valid at the function level. +#[allow(private_bounds)] pub trait FunctionLevelDescriptor: Descriptor {} /// Table 13. Microsoft OS 2.0 compatible ID descriptor. From 8f1bed3d956c1e2fae2ab11b292c8fe73c7b6a2b Mon Sep 17 00:00:00 2001 From: Caleb Jamison Date: Sat, 23 Mar 2024 12:21:13 -0400 Subject: [PATCH 27/62] Allow changing Spi/I2cDeviceWithConfig's config at runtime --- embassy-embedded-hal/src/shared_bus/asynch/i2c.rs | 5 +++++ embassy-embedded-hal/src/shared_bus/asynch/spi.rs | 5 +++++ embassy-embedded-hal/src/shared_bus/blocking/i2c.rs | 5 +++++ embassy-embedded-hal/src/shared_bus/blocking/spi.rs | 5 +++++ 4 files changed, 20 insertions(+) diff --git a/embassy-embedded-hal/src/shared_bus/asynch/i2c.rs b/embassy-embedded-hal/src/shared_bus/asynch/i2c.rs index 779c04263..71ce09def 100644 --- a/embassy-embedded-hal/src/shared_bus/asynch/i2c.rs +++ b/embassy-embedded-hal/src/shared_bus/asynch/i2c.rs @@ -106,6 +106,11 @@ impl<'a, M: RawMutex, BUS: SetConfig> I2cDeviceWithConfig<'a, M, BUS> { pub fn new(bus: &'a Mutex, config: BUS::Config) -> Self { Self { bus, config } } + + /// Change the device's config at runtime + pub fn set_config(&mut self, config: BUS::Config) { + self.config = config; + } } impl<'a, M, BUS> i2c::ErrorType for I2cDeviceWithConfig<'a, M, BUS> diff --git a/embassy-embedded-hal/src/shared_bus/asynch/spi.rs b/embassy-embedded-hal/src/shared_bus/asynch/spi.rs index 62b2c92a0..9890f218d 100644 --- a/embassy-embedded-hal/src/shared_bus/asynch/spi.rs +++ b/embassy-embedded-hal/src/shared_bus/asynch/spi.rs @@ -122,6 +122,11 @@ impl<'a, M: RawMutex, BUS: SetConfig, CS> SpiDeviceWithConfig<'a, M, BUS, CS> { pub fn new(bus: &'a Mutex, cs: CS, config: BUS::Config) -> Self { Self { bus, cs, config } } + + /// Change the device's config at runtime + pub fn set_config(&mut self, config: BUS::Config) { + self.config = config; + } } impl<'a, M, BUS, CS> spi::ErrorType for SpiDeviceWithConfig<'a, M, BUS, CS> diff --git a/embassy-embedded-hal/src/shared_bus/blocking/i2c.rs b/embassy-embedded-hal/src/shared_bus/blocking/i2c.rs index 1b08cb28e..627767c8a 100644 --- a/embassy-embedded-hal/src/shared_bus/blocking/i2c.rs +++ b/embassy-embedded-hal/src/shared_bus/blocking/i2c.rs @@ -132,6 +132,11 @@ impl<'a, M: RawMutex, BUS: SetConfig> I2cDeviceWithConfig<'a, M, BUS> { pub fn new(bus: &'a Mutex>, config: BUS::Config) -> Self { Self { bus, config } } + + /// Change the device's config at runtime + pub fn set_config(&mut self, config: BUS::Config) { + self.config = config; + } } impl<'a, M, BUS> ErrorType for I2cDeviceWithConfig<'a, M, BUS> diff --git a/embassy-embedded-hal/src/shared_bus/blocking/spi.rs b/embassy-embedded-hal/src/shared_bus/blocking/spi.rs index 59b65bfbd..801899f9f 100644 --- a/embassy-embedded-hal/src/shared_bus/blocking/spi.rs +++ b/embassy-embedded-hal/src/shared_bus/blocking/spi.rs @@ -147,6 +147,11 @@ impl<'a, M: RawMutex, BUS: SetConfig, CS> SpiDeviceWithConfig<'a, M, BUS, CS> { pub fn new(bus: &'a Mutex>, cs: CS, config: BUS::Config) -> Self { Self { bus, cs, config } } + + /// Change the device's config at runtime + pub fn set_config(&mut self, config: BUS::Config) { + self.config = config; + } } impl<'a, M, BUS, CS> spi::ErrorType for SpiDeviceWithConfig<'a, M, BUS, CS> From 9bc00e68fac8f41983ec64becc45648d4a05919c Mon Sep 17 00:00:00 2001 From: ZhangYong Date: Sun, 24 Mar 2024 18:25:04 +0800 Subject: [PATCH 28/62] update comments use 4/5/6 as new firmware for better understanding --- embassy-boot/src/boot_loader.rs | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/embassy-boot/src/boot_loader.rs b/embassy-boot/src/boot_loader.rs index ca1a1b10c..a38558056 100644 --- a/embassy-boot/src/boot_loader.rs +++ b/embassy-boot/src/boot_loader.rs @@ -183,29 +183,29 @@ impl BootLoader BootLoader Result { // Ensure we have enough progress pages to store copy progress From e139a4e076b2f61ddf0cdfa2ed5484ecb327afad Mon Sep 17 00:00:00 2001 From: Rafael Bachmann Date: Mon, 25 Mar 2024 15:09:41 +0100 Subject: [PATCH 29/62] fix: CI --- embassy-rp/src/clocks.rs | 2 ++ embassy-rp/src/pio/mod.rs | 4 ++-- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/embassy-rp/src/clocks.rs b/embassy-rp/src/clocks.rs index b02f3796f..b7f6aeac9 100644 --- a/embassy-rp/src/clocks.rs +++ b/embassy-rp/src/clocks.rs @@ -1,4 +1,5 @@ //! Clock configuration for the RP2040 +use core::arch::asm; use core::marker::PhantomData; use core::sync::atomic::{AtomicU16, AtomicU32, Ordering}; @@ -7,6 +8,7 @@ use pac::clocks::vals::*; use crate::gpio::sealed::Pin; use crate::gpio::AnyPin; +use crate::pac::common::{Reg, RW}; use crate::{pac, reset, Peripheral}; // NOTE: all gpin handling is commented out for future reference. diff --git a/embassy-rp/src/pio/mod.rs b/embassy-rp/src/pio/mod.rs index 804a7636d..7eca700ba 100644 --- a/embassy-rp/src/pio/mod.rs +++ b/embassy-rp/src/pio/mod.rs @@ -749,7 +749,7 @@ impl<'d, PIO: Instance + 'd, const SM: usize> StateMachine<'d, PIO, SM> { w.set_set_count(1); }); // SET PINDIRS, (dir) - unsafe { sm.exec_instr(0b1110_0000_1000_0000 | dir as u16) }; + unsafe { sm.exec_instr(0b111_00000_100_00000 | dir as u16) }; } }); } @@ -764,7 +764,7 @@ impl<'d, PIO: Instance + 'd, const SM: usize> StateMachine<'d, PIO, SM> { w.set_set_count(1); }); // SET PINS, (dir) - unsafe { sm.exec_instr(0b1110_0000_0000_0000 | level as u16) }; + unsafe { sm.exec_instr(0b11100_000_000_00000 | level as u16) }; } }); } From 64964bd61444838f2e6a99fcde7ef8fc39b1f0c8 Mon Sep 17 00:00:00 2001 From: Emilie Burgun Date: Tue, 26 Mar 2024 16:22:05 +0100 Subject: [PATCH 30/62] Add a config option to make the VDDIO2 supply line valid On STM32L4[7-A]xx, STM32L5xxx and STM32U5xxx chips, the GPIOG[2..15] pins are only available once the IOSV bit has been set in PWR->CR2 (U5 chips have the bit in a funkier register). This is meant to allow the user to have control over this power supply, so the GPIOG pins are initially insulated, until the user wishes to un-insulate them (or something like that?). For most applications, though, the VDDIO2 is connected to the VDD line, and this behavior only gets in the way and causes confusing issues. This submission adds an option in `embassy_stm32::Config`, called `enable_independent_io_supply`, which simply enables the IOSV bit. It is only available on chips for which I could find a mention of IOSV (STM32L4 and STM32L5) or IO2SV (STM32U5). --- embassy-stm32/src/lib.rs | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/embassy-stm32/src/lib.rs b/embassy-stm32/src/lib.rs index 6a3d1c463..05b1ff045 100644 --- a/embassy-stm32/src/lib.rs +++ b/embassy-stm32/src/lib.rs @@ -172,6 +172,14 @@ pub struct Config { #[cfg(dbgmcu)] pub enable_debug_during_sleep: bool, + /// On low-power boards (eg. `stm32l4`, `stm32l5` and `stm32u5`), + /// some GPIO pins are powered by an auxiliary, independent power supply (`VDDIO2`), + /// which needs to be enabled before these pins can be used. + /// + /// May increase power consumption. Defaults to true. + #[cfg(any(stm32l4, stm32l5, stm32u5))] + pub enable_independent_io_supply: bool, + /// BDMA interrupt priority. /// /// Defaults to P0 (highest). @@ -209,6 +217,8 @@ impl Default for Config { rcc: Default::default(), #[cfg(dbgmcu)] enable_debug_during_sleep: true, + #[cfg(any(stm32l4, stm32l5, stm32u5))] + enable_independent_io_supply: true, #[cfg(bdma)] bdma_interrupt_priority: Priority::P0, #[cfg(dma)] @@ -270,6 +280,23 @@ pub fn init(config: Config) -> Peripherals { #[cfg(not(any(stm32f2, stm32f4, stm32f7, stm32l0, stm32h5, stm32h7)))] peripherals::FLASH::enable_and_reset_with_cs(cs); + // Enable the VDDIO2 power supply on chips that have it. + // Note that this requires the PWR peripheral to be enabled first. + #[cfg(any(stm32l4, stm32l5))] + { + crate::pac::PWR.cr2().modify(|w| { + // The official documentation states that we should ideally enable VDDIO2 + // through the PVME2 bit, but it looks like this bit + w.set_iosv(config.enable_independent_io_supply); + }); + } + #[cfg(stm32u5)] + { + crate::pac::PWR.svmcr().modify(|w| { + w.set_io2sv(config.enable_independent_io_supply); + }); + } + // dead battery functionality is still present on these // chips despite them not having UCPD- disable it #[cfg(any(stm32g070, stm32g0b0))] From ca998c170f18c252e0d4c5a6aed1085113c27dd0 Mon Sep 17 00:00:00 2001 From: Emilie Burgun Date: Tue, 26 Mar 2024 16:33:41 +0100 Subject: [PATCH 31/62] Missing half of the implementation detail comment --- embassy-stm32/src/lib.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/embassy-stm32/src/lib.rs b/embassy-stm32/src/lib.rs index 05b1ff045..ab6ef8ef4 100644 --- a/embassy-stm32/src/lib.rs +++ b/embassy-stm32/src/lib.rs @@ -286,7 +286,8 @@ pub fn init(config: Config) -> Peripherals { { crate::pac::PWR.cr2().modify(|w| { // The official documentation states that we should ideally enable VDDIO2 - // through the PVME2 bit, but it looks like this bit + // through the PVME2 bit, but it looks like this isn't required, + // and CubeMX itself skips this step. w.set_iosv(config.enable_independent_io_supply); }); } From 402def86ee6a1d8695fc0a4cf157d1ffca7550d9 Mon Sep 17 00:00:00 2001 From: Emilie Burgun Date: Tue, 26 Mar 2024 17:27:02 +0100 Subject: [PATCH 32/62] Remove ad-hoc fixes for setting the IOSV bit to true --- embassy-stm32/src/gpio.rs | 7 ------- examples/stm32l4/src/bin/spe_adin1110_http_server.rs | 6 ------ tests/stm32/src/common.rs | 7 ------- 3 files changed, 20 deletions(-) diff --git a/embassy-stm32/src/gpio.rs b/embassy-stm32/src/gpio.rs index 33f22f676..214813a42 100644 --- a/embassy-stm32/src/gpio.rs +++ b/embassy-stm32/src/gpio.rs @@ -779,13 +779,6 @@ pub(crate) unsafe fn init(_cs: CriticalSection) { ::enable_and_reset_with_cs(_cs); crate::_generated::init_gpio(); - - // Setting this bit is mandatory to use PG[15:2]. - #[cfg(stm32u5)] - crate::pac::PWR.svmcr().modify(|w| { - w.set_io2sv(true); - w.set_io2vmen(true); - }); } impl<'d> embedded_hal_02::digital::v2::InputPin for Input<'d> { diff --git a/examples/stm32l4/src/bin/spe_adin1110_http_server.rs b/examples/stm32l4/src/bin/spe_adin1110_http_server.rs index 343e09e68..77aa929ab 100644 --- a/examples/stm32l4/src/bin/spe_adin1110_http_server.rs +++ b/examples/stm32l4/src/bin/spe_adin1110_http_server.rs @@ -93,12 +93,6 @@ async fn main(spawner: Spawner) { let dp = embassy_stm32::init(config); - // RM0432rev9, 5.1.2: Independent I/O supply rail - // After reset, the I/Os supplied by VDDIO2 are logically and electrically isolated and - // therefore are not available. The isolation must be removed before using any I/O from - // PG[15:2], by setting the IOSV bit in the PWR_CR2 register, once the VDDIO2 supply is present - pac::PWR.cr2().modify(|w| w.set_iosv(true)); - let reset_status = pac::RCC.bdcr().read().0; defmt::println!("bdcr before: 0x{:X}", reset_status); diff --git a/tests/stm32/src/common.rs b/tests/stm32/src/common.rs index c379863a8..0e555efc8 100644 --- a/tests/stm32/src/common.rs +++ b/tests/stm32/src/common.rs @@ -251,13 +251,6 @@ define_peris!( ); pub fn config() -> Config { - // Setting this bit is mandatory to use PG[15:2]. - #[cfg(feature = "stm32u5a5zj")] - embassy_stm32::pac::PWR.svmcr().modify(|w| { - w.set_io2sv(true); - w.set_io2vmen(true); - }); - #[allow(unused_mut)] let mut config = Config::default(); From 1acc34bfaa43dfc3410396cb20e371128e693d1a Mon Sep 17 00:00:00 2001 From: Emilie Burgun Date: Tue, 26 Mar 2024 17:43:22 +0100 Subject: [PATCH 33/62] Remove the need for TxDma to be a DMA channel in the blocking UartTx impl --- embassy-stm32/src/usart/mod.rs | 2 -- 1 file changed, 2 deletions(-) diff --git a/embassy-stm32/src/usart/mod.rs b/embassy-stm32/src/usart/mod.rs index 7ab33043a..62ea717b3 100644 --- a/embassy-stm32/src/usart/mod.rs +++ b/embassy-stm32/src/usart/mod.rs @@ -1260,7 +1260,6 @@ where impl embedded_io::Write for Uart<'_, T, TxDma, RxDma> where T: BasicInstance, - TxDma: crate::usart::TxDma, { fn write(&mut self, buf: &[u8]) -> Result { self.blocking_write(buf)?; @@ -1275,7 +1274,6 @@ where impl embedded_io::Write for UartTx<'_, T, TxDma> where T: BasicInstance, - TxDma: crate::usart::TxDma, { fn write(&mut self, buf: &[u8]) -> Result { self.blocking_write(buf)?; From cf11d28d628f48e8f3b048deae76e7d18c0a2eec Mon Sep 17 00:00:00 2001 From: eZio Pan Date: Wed, 27 Mar 2024 00:55:44 +0800 Subject: [PATCH 34/62] stm32 H5: LSE low drive mode is not functional --- embassy-stm32/src/rcc/bd.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/embassy-stm32/src/rcc/bd.rs b/embassy-stm32/src/rcc/bd.rs index 39407b28c..54d3c662b 100644 --- a/embassy-stm32/src/rcc/bd.rs +++ b/embassy-stm32/src/rcc/bd.rs @@ -24,6 +24,7 @@ pub struct LseConfig { #[allow(dead_code)] #[derive(Default, Clone, Copy)] pub enum LseDrive { + #[cfg(not(stm32h5))] // ES0565: LSE Low drive mode is not functional Low = 0, MediumLow = 0x01, #[default] @@ -38,6 +39,7 @@ impl From for crate::pac::rcc::vals::Lsedrv { use crate::pac::rcc::vals::Lsedrv; match value { + #[cfg(not(stm32h5))] // ES0565: LSE Low drive mode is not functional LseDrive::Low => Lsedrv::LOW, LseDrive::MediumLow => Lsedrv::MEDIUMLOW, LseDrive::MediumHigh => Lsedrv::MEDIUMHIGH, From e3ef7cd99faf2fe46463882b35d00ef0f2a34481 Mon Sep 17 00:00:00 2001 From: Emilie Burgun Date: Wed, 27 Mar 2024 11:10:16 +0100 Subject: [PATCH 35/62] Document why embedded_io::Read cannot be implemented for the base Uart --- embassy-stm32/src/usart/mod.rs | 44 +++++++++++++++++++++++++++++++--- 1 file changed, 41 insertions(+), 3 deletions(-) diff --git a/embassy-stm32/src/usart/mod.rs b/embassy-stm32/src/usart/mod.rs index 62ea717b3..7c0523a25 100644 --- a/embassy-stm32/src/usart/mod.rs +++ b/embassy-stm32/src/usart/mod.rs @@ -209,7 +209,14 @@ enum ReadCompletionEvent { Idle(usize), } -/// Bidirectional UART Driver +/// Bidirectional UART Driver, which acts as a combination of [`UartTx`] and [`UartRx`]. +/// +/// ### Notes on [`embedded_io::Read`] +/// +/// `embedded_io::Read` requires guarantees that the base [`UartRx`] cannot provide. +/// +/// See [`UartRx`] for more details, and see [`BufferedUart`] and [`RingBufferedUartRx`] +/// as alternatives that do provide the necessary guarantees for `embedded_io::Read`. pub struct Uart<'d, T: BasicInstance, TxDma = NoDma, RxDma = NoDma> { tx: UartTx<'d, T, TxDma>, rx: UartRx<'d, T, RxDma>, @@ -225,7 +232,10 @@ impl<'d, T: BasicInstance, TxDma, RxDma> SetConfig for Uart<'d, T, TxDma, RxDma> } } -/// Tx-only UART Driver +/// Tx-only UART Driver. +/// +/// Can be obtained from [`Uart::split`], or can be constructed independently, +/// if you do not need the receiving half of the driver. pub struct UartTx<'d, T: BasicInstance, TxDma = NoDma> { phantom: PhantomData<&'d mut T>, tx_dma: PeripheralRef<'d, TxDma>, @@ -240,7 +250,35 @@ impl<'d, T: BasicInstance, TxDma> SetConfig for UartTx<'d, T, TxDma> { } } -/// Rx-only UART Driver +/// Rx-only UART Driver. +/// +/// Can be obtained from [`Uart::split`], or can be constructed independently, +/// if you do not need the transmitting half of the driver. +/// +/// ### Notes on [`embedded_io::Read`] +/// +/// `embedded_io::Read` requires guarantees that this struct cannot provide: +/// +/// - Any data received between calls to [`UartRx::read`] or [`UartRx::blocking_read`] +/// will be thrown away, as `UartRx` is unbuffered. +/// Users of `embedded_io::Read` are likely to not expect this behavior +/// (for instance if they read multiple small chunks in a row). +/// - [`UartRx::read`] and [`UartRx::blocking_read`] only return once the entire buffer has been +/// filled, whereas `embedded_io::Read` requires us to fill the buffer with what we already +/// received, and only block/wait until the first byte arrived. +///
+/// While [`UartRx::read_until_idle`] does return early, it will still eagerly wait for data until +/// the buffer is full or no data has been transmitted in a while, +/// which may not be what users of `embedded_io::Read` expect. +/// +/// [`UartRx::into_ring_buffered`] can be called to equip `UartRx` with a buffer, +/// that it can then use to store data received between calls to `read`, +/// provided you are using DMA already. +/// +/// Alternatively, you can use [`BufferedUartRx`], which is interrupt-based and which can also +/// store data received between calls. +/// +/// Also see [this github comment](https://github.com/embassy-rs/embassy/pull/2185#issuecomment-1810047043). pub struct UartRx<'d, T: BasicInstance, RxDma = NoDma> { _peri: PeripheralRef<'d, T>, rx_dma: PeripheralRef<'d, RxDma>, From c0590626273f020893a8fedf7bffa1cb9a9a6d77 Mon Sep 17 00:00:00 2001 From: Maarten de Vries Date: Wed, 27 Mar 2024 15:39:44 +0100 Subject: [PATCH 36/62] embassy_stm32: Preseve the RTR flag in messages. --- embassy-stm32/src/can/bx/mod.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/embassy-stm32/src/can/bx/mod.rs b/embassy-stm32/src/can/bx/mod.rs index a369ae6fd..121da1bb2 100644 --- a/embassy-stm32/src/can/bx/mod.rs +++ b/embassy-stm32/src/can/bx/mod.rs @@ -776,13 +776,13 @@ where let mb = self.canregs.tx(idx); - let id = IdReg(mb.tir().read().0).id(); + let id = IdReg(mb.tir().read().0); let mut data = [0xff; 8]; data[0..4].copy_from_slice(&mb.tdlr().read().0.to_ne_bytes()); data[4..8].copy_from_slice(&mb.tdhr().read().0.to_ne_bytes()); let len = mb.tdtr().read().dlc(); - Some(Frame::new(Header::new(id, len, false), &data).unwrap()) + Some(Frame::new(Header::new(id.id(), len, id.rtr()), &data).unwrap()) } else { // Abort request failed because the frame was already sent (or being sent) on // the bus. All mailboxes are now free. This can happen for small prescaler @@ -915,7 +915,7 @@ fn receive_fifo(canregs: crate::pac::can::Can, fifo_nr: usize) -> nb::Result nb::Result Date: Sun, 24 Mar 2024 07:19:00 +1000 Subject: [PATCH 37/62] CAN: Move some FDCAN definitions into a module to share with BXCAN. --- embassy-stm32/src/can/common.rs | 51 +++++++++++++++++++++++++++ embassy-stm32/src/can/fdcan.rs | 62 ++++++--------------------------- 2 files changed, 61 insertions(+), 52 deletions(-) create mode 100644 embassy-stm32/src/can/common.rs diff --git a/embassy-stm32/src/can/common.rs b/embassy-stm32/src/can/common.rs new file mode 100644 index 000000000..1de54e5a1 --- /dev/null +++ b/embassy-stm32/src/can/common.rs @@ -0,0 +1,51 @@ +use embassy_sync::channel::{DynamicReceiver, DynamicSender}; + +use crate::can::_version::frame::*; +use crate::can::_version::Timestamp; +use crate::can::_version::enums::*; + +pub(crate) struct ClassicBufferedRxInner { + pub rx_sender: DynamicSender<'static, Result<(ClassicFrame, Timestamp), BusError>>, +} +pub(crate) struct ClassicBufferedTxInner { + pub tx_receiver: DynamicReceiver<'static, ClassicFrame>, +} + +pub(crate) struct FdBufferedRxInner { + pub rx_sender: DynamicSender<'static, Result<(FdFrame, Timestamp), BusError>>, +} +pub(crate) struct FdBufferedTxInner { + pub tx_receiver: DynamicReceiver<'static, FdFrame>, +} + +/// Sender that can be used for sending CAN frames. +#[derive(Copy, Clone)] +pub struct BufferedCanSender { + pub(crate) tx_buf: embassy_sync::channel::DynamicSender<'static, ClassicFrame>, + pub(crate) waker: fn(), +} + +impl BufferedCanSender { + /// Async write frame to TX buffer. + pub fn try_write(&mut self, frame: ClassicFrame) -> Result<(), embassy_sync::channel::TrySendError> { + self.tx_buf.try_send(frame)?; + (self.waker)(); + Ok(()) + } + + /// Async write frame to TX buffer. + pub async fn write(&mut self, frame: ClassicFrame) { + self.tx_buf.send(frame).await; + (self.waker)(); + } + + /// Allows a poll_fn to poll until the channel is ready to write + pub fn poll_ready_to_send(&self, cx: &mut core::task::Context<'_>) -> core::task::Poll<()> { + self.tx_buf.poll_ready_to_send(cx) + } +} + +/// Receiver that can be used for receiving CAN frames. Note, each CAN frame will only be received by one receiver. +pub type BufferedCanReceiver = + embassy_sync::channel::DynamicReceiver<'static, Result<(ClassicFrame, Timestamp), BusError>>; + diff --git a/embassy-stm32/src/can/fdcan.rs b/embassy-stm32/src/can/fdcan.rs index 4ea036ab4..c42c42853 100644 --- a/embassy-stm32/src/can/fdcan.rs +++ b/embassy-stm32/src/can/fdcan.rs @@ -14,6 +14,7 @@ use crate::interrupt::typelevel::Interrupt; use crate::rcc::RccPeripheral; use crate::{interrupt, peripherals, Peripheral}; +mod common; pub mod enums; pub(crate) mod fd; pub mod frame; @@ -24,6 +25,7 @@ use fd::config::*; use fd::filter::*; pub use fd::{config, filter}; use frame::*; +pub use self::common::{BufferedCanSender, BufferedCanReceiver}; /// Timestamp for incoming packets. Use Embassy time when enabled. #[cfg(feature = "time")] @@ -414,36 +416,6 @@ pub struct BufferedCan<'d, T: Instance, const TX_BUF_SIZE: usize, const RX_BUF_S rx_buf: &'static RxBuf, } -/// Sender that can be used for sending CAN frames. -#[derive(Copy, Clone)] -pub struct BufferedCanSender { - tx_buf: embassy_sync::channel::DynamicSender<'static, ClassicFrame>, - waker: fn(), -} - -impl BufferedCanSender { - /// Async write frame to TX buffer. - pub fn try_write(&mut self, frame: ClassicFrame) -> Result<(), embassy_sync::channel::TrySendError> { - self.tx_buf.try_send(frame)?; - (self.waker)(); - Ok(()) - } - - /// Async write frame to TX buffer. - pub async fn write(&mut self, frame: ClassicFrame) { - self.tx_buf.send(frame).await; - (self.waker)(); - } - - /// Allows a poll_fn to poll until the channel is ready to write - pub fn poll_ready_to_send(&self, cx: &mut core::task::Context<'_>) -> core::task::Poll<()> { - self.tx_buf.poll_ready_to_send(cx) - } -} - -/// Receiver that can be used for receiving CAN frames. Note, each CAN frame will only be received by one receiver. -pub type BufferedCanReceiver = - embassy_sync::channel::DynamicReceiver<'static, Result<(ClassicFrame, Timestamp), BusError>>; impl<'c, 'd, T: Instance, const TX_BUF_SIZE: usize, const RX_BUF_SIZE: usize> BufferedCan<'d, T, TX_BUF_SIZE, RX_BUF_SIZE> @@ -468,10 +440,10 @@ impl<'c, 'd, T: Instance, const TX_BUF_SIZE: usize, const RX_BUF_SIZE: usize> fn setup(self) -> Self { // We don't want interrupts being processed while we change modes. critical_section::with(|_| unsafe { - let rx_inner = ClassicBufferedRxInner { + let rx_inner = self::common::ClassicBufferedRxInner { rx_sender: self.rx_buf.sender().into(), }; - let tx_inner = ClassicBufferedTxInner { + let tx_inner = self::common::ClassicBufferedTxInner { tx_receiver: self.tx_buf.receiver().into(), }; T::mut_state().rx_mode = RxMode::ClassicBuffered(rx_inner); @@ -586,10 +558,10 @@ impl<'c, 'd, T: Instance, const TX_BUF_SIZE: usize, const RX_BUF_SIZE: usize> fn setup(self) -> Self { // We don't want interrupts being processed while we change modes. critical_section::with(|_| unsafe { - let rx_inner = FdBufferedRxInner { + let rx_inner = self::common::FdBufferedRxInner { rx_sender: self.rx_buf.sender().into(), }; - let tx_inner = FdBufferedTxInner { + let tx_inner = self::common::FdBufferedTxInner { tx_receiver: self.tx_buf.receiver().into(), }; T::mut_state().rx_mode = RxMode::FdBuffered(rx_inner); @@ -678,24 +650,10 @@ impl<'c, 'd, T: Instance> FdcanRx<'d, T> { } } -struct ClassicBufferedRxInner { - rx_sender: DynamicSender<'static, Result<(ClassicFrame, Timestamp), BusError>>, -} -struct ClassicBufferedTxInner { - tx_receiver: DynamicReceiver<'static, ClassicFrame>, -} - -struct FdBufferedRxInner { - rx_sender: DynamicSender<'static, Result<(FdFrame, Timestamp), BusError>>, -} -struct FdBufferedTxInner { - tx_receiver: DynamicReceiver<'static, FdFrame>, -} - enum RxMode { NonBuffered(AtomicWaker), - ClassicBuffered(ClassicBufferedRxInner), - FdBuffered(FdBufferedRxInner), + ClassicBuffered(self::common::ClassicBufferedRxInner), + FdBuffered(self::common::FdBufferedRxInner), } impl RxMode { @@ -765,8 +723,8 @@ impl RxMode { enum TxMode { NonBuffered(AtomicWaker), - ClassicBuffered(ClassicBufferedTxInner), - FdBuffered(FdBufferedTxInner), + ClassicBuffered(self::common::ClassicBufferedTxInner), + FdBuffered(self::common::FdBufferedTxInner), } impl TxMode { From 32065d7719e8dd2f5da7787d4b7edf3109c632ba Mon Sep 17 00:00:00 2001 From: Corey Schuhen Date: Thu, 14 Mar 2024 21:13:19 +1000 Subject: [PATCH 38/62] BXCAN: Cut out more that wasn't required from BXCAN crate. --- embassy-stm32/src/can/bx/mod.rs | 123 +++----------------------------- embassy-stm32/src/can/bxcan.rs | 7 +- 2 files changed, 11 insertions(+), 119 deletions(-) diff --git a/embassy-stm32/src/can/bx/mod.rs b/embassy-stm32/src/can/bx/mod.rs index 121da1bb2..5ea0471c9 100644 --- a/embassy-stm32/src/can/bx/mod.rs +++ b/embassy-stm32/src/can/bx/mod.rs @@ -602,54 +602,14 @@ where unsafe { Tx::::conjure(self.canregs).abort(mailbox) } } - /// Returns a received frame if available. - /// - /// This will first check FIFO 0 for a message or error. If none are available, FIFO 1 is - /// checked. - /// - /// Returns `Err` when a frame was lost due to buffer overrun. - pub fn receive(&mut self) -> nb::Result { - // Safety: We have a `&mut self` and have unique access to the peripheral. - let mut rx0 = unsafe { Rx0::::conjure(self.canregs) }; - let mut rx1 = unsafe { Rx1::::conjure(self.canregs) }; - match rx0.receive() { - Err(nb::Error::WouldBlock) => rx1.receive(), - result => result, - } - } - - /// Returns a reference to the RX FIFO 0. - pub fn rx0(&mut self) -> Rx0 { - // Safety: We take `&mut self` and the return value lifetimes are tied to `self`'s lifetime. - unsafe { Rx0::conjure(self.canregs) } - } - - /// Returns a reference to the RX FIFO 1. - pub fn rx1(&mut self) -> Rx1 { - // Safety: We take `&mut self` and the return value lifetimes are tied to `self`'s lifetime. - unsafe { Rx1::conjure(self.canregs) } - } - - pub(crate) fn split_by_ref(&mut self) -> (Tx, Rx0, Rx1) { + pub(crate) fn split_by_ref(&mut self) -> (Tx, Rx) { // Safety: We take `&mut self` and the return value lifetimes are tied to `self`'s lifetime. let tx = unsafe { Tx::conjure(self.canregs) }; - let rx0 = unsafe { Rx0::conjure(self.canregs) }; - let rx1 = unsafe { Rx1::conjure(self.canregs) }; - (tx, rx0, rx1) + let rx0 = unsafe { Rx::conjure() }; + (tx, rx0) } - /// Consumes this `Can` instance and splits it into transmitting and receiving halves. - pub fn split(self) -> (Tx, Rx0, Rx1) { - // Safety: `Self` is not `Copy` and is destroyed by moving it into this method. - unsafe { - ( - Tx::conjure(self.canregs), - Rx0::conjure(self.canregs), - Rx1::conjure(self.canregs), - ) - } - } } impl Can { @@ -662,7 +622,7 @@ impl Can { } } -/// Interface to the CAN transmitter part. +/// Marker for Tx half pub struct Tx { _can: PhantomData, canregs: crate::pac::can::Can, @@ -844,87 +804,20 @@ where } } -/// Interface to receiver FIFO 0. -pub struct Rx0 { +/// Marker for Rx half +pub struct Rx { _can: PhantomData, - canregs: crate::pac::can::Can, } -impl Rx0 +impl Rx where I: Instance, { - unsafe fn conjure(canregs: crate::pac::can::Can) -> Self { + unsafe fn conjure() -> Self { Self { _can: PhantomData, - canregs, } } - - /// Returns a received frame if available. - /// - /// Returns `Err` when a frame was lost due to buffer overrun. - pub fn receive(&mut self) -> nb::Result { - receive_fifo(self.canregs, 0) - } -} - -/// Interface to receiver FIFO 1. -pub struct Rx1 { - _can: PhantomData, - canregs: crate::pac::can::Can, -} - -impl Rx1 -where - I: Instance, -{ - unsafe fn conjure(canregs: crate::pac::can::Can) -> Self { - Self { - _can: PhantomData, - canregs, - } - } - - /// Returns a received frame if available. - /// - /// Returns `Err` when a frame was lost due to buffer overrun. - pub fn receive(&mut self) -> nb::Result { - receive_fifo(self.canregs, 1) - } -} - -fn receive_fifo(canregs: crate::pac::can::Can, fifo_nr: usize) -> nb::Result { - assert!(fifo_nr < 2); - let rfr = canregs.rfr(fifo_nr); - let rx = canregs.rx(fifo_nr); - - //let rfr = &can.rfr[fifo_nr]; - //let rx = &can.rx[fifo_nr]; - - // Check if a frame is available in the mailbox. - let rfr_read = rfr.read(); - if rfr_read.fmp() == 0 { - return Err(nb::Error::WouldBlock); - } - - // Check for RX FIFO overrun. - if rfr_read.fovr() { - rfr.write(|w| w.set_fovr(true)); - return Err(nb::Error::Other(OverrunError { _priv: () })); - } - - // Read the frame. - let id = IdReg(rx.rir().read().0); - let mut data = [0xff; 8]; - data[0..4].copy_from_slice(&rx.rdlr().read().0.to_ne_bytes()); - data[4..8].copy_from_slice(&rx.rdhr().read().0.to_ne_bytes()); - let len = rx.rdtr().read().dlc(); - - // Release the mailbox. - rfr.write(|w| w.set_rfom(true)); - - Ok(Frame::new(Header::new(id.id(), len, id.rtr()), &data).unwrap()) } /// Identifies one of the two receive FIFOs. diff --git a/embassy-stm32/src/can/bxcan.rs b/embassy-stm32/src/can/bxcan.rs index 017c5110d..9d2b8797e 100644 --- a/embassy-stm32/src/can/bxcan.rs +++ b/embassy-stm32/src/can/bxcan.rs @@ -296,8 +296,8 @@ impl<'d, T: Instance> Can<'d, T> { /// /// Useful for doing separate transmit/receive tasks. pub fn split<'c>(&'c mut self) -> (CanTx<'d, T>, CanRx<'d, T>) { - let (tx, rx0, rx1) = self.can.split_by_ref(); - (CanTx { tx }, CanRx { rx0, rx1 }) + let (tx, rx) = self.can.split_by_ref(); + (CanTx { tx }, CanRx { rx}) } } @@ -401,8 +401,7 @@ impl<'d, T: Instance> CanTx<'d, T> { /// CAN driver, receive half. #[allow(dead_code)] pub struct CanRx<'d, T: Instance> { - rx0: crate::can::bx::Rx0>, - rx1: crate::can::bx::Rx1>, + rx: crate::can::bx::Rx>, } impl<'d, T: Instance> CanRx<'d, T> { From 3bdaad39e8955fe52e55c65a834dfc42dc54d676 Mon Sep 17 00:00:00 2001 From: Corey Schuhen Date: Sun, 24 Mar 2024 07:20:33 +1000 Subject: [PATCH 39/62] BXCAN: Register access into new Registers struct. --- embassy-stm32/src/can/bx/mod.rs | 753 +++++++++++++++++++------------- embassy-stm32/src/can/bxcan.rs | 76 +--- embassy-stm32/src/can/common.rs | 12 +- embassy-stm32/src/can/fdcan.rs | 4 +- 4 files changed, 465 insertions(+), 380 deletions(-) diff --git a/embassy-stm32/src/can/bx/mod.rs b/embassy-stm32/src/can/bx/mod.rs index 5ea0471c9..9b6ebf5a4 100644 --- a/embassy-stm32/src/can/bx/mod.rs +++ b/embassy-stm32/src/can/bx/mod.rs @@ -43,7 +43,35 @@ pub type Data = crate::can::frame::ClassicData; /// CAN Frame pub type Frame = crate::can::frame::ClassicFrame; +use crate::can::_version::Envelope; use crate::can::bx::filter::MasterFilters; +use crate::can::enums::BusError; +use crate::pac::can::vals::Lec; + +#[derive(Debug, Copy, Clone, Eq, PartialEq)] +pub(crate) enum RxFifo { + Fifo0, + Fifo1, +} + +trait IntoBusError { + fn into_bus_err(self) -> Option; +} + +impl IntoBusError for Lec { + fn into_bus_err(self) -> Option { + match self { + Lec::STUFF => Some(BusError::Stuff), + Lec::FORM => Some(BusError::Form), + Lec::ACK => Some(BusError::Acknowledge), + Lec::BITRECESSIVE => Some(BusError::BitRecessive), + Lec::BITDOMINANT => Some(BusError::BitDominant), + Lec::CRC => Some(BusError::Crc), + Lec::CUSTOM => Some(BusError::Software), + _ => None, + } + } +} /// A bxCAN peripheral instance. /// @@ -233,229 +261,36 @@ impl PartialOrd for IdReg { } } -/// Configuration proxy returned by [`Can::modify_config`]. -#[must_use = "`CanConfig` leaves the peripheral in uninitialized state, call `CanConfig::enable` or explicitly drop the value"] -pub struct CanConfig<'a, I: Instance> { - can: &'a mut Can, +pub(crate) struct Registers { + pub canregs: crate::pac::can::Can, } -impl CanConfig<'_, I> { - /// Configures the bit timings. - /// - /// You can use to calculate the `btr` parameter. Enter - /// parameters as follows: - /// - /// - *Clock Rate*: The input clock speed to the CAN peripheral (*not* the CPU clock speed). - /// This is the clock rate of the peripheral bus the CAN peripheral is attached to (eg. APB1). - /// - *Sample Point*: Should normally be left at the default value of 87.5%. - /// - *SJW*: Should normally be left at the default value of 1. - /// - /// Then copy the `CAN_BUS_TIME` register value from the table and pass it as the `btr` - /// parameter to this method. - pub fn set_bit_timing(self, bt: crate::can::util::NominalBitTiming) -> Self { - self.can.set_bit_timing(bt); - self - } - - /// Enables or disables loopback mode: Internally connects the TX and RX - /// signals together. - pub fn set_loopback(self, enabled: bool) -> Self { - self.can.canregs.btr().modify(|reg| reg.set_lbkm(enabled)); - self - } - - /// Enables or disables silent mode: Disconnects the TX signal from the pin. - pub fn set_silent(self, enabled: bool) -> Self { - let mode = match enabled { - false => stm32_metapac::can::vals::Silm::NORMAL, - true => stm32_metapac::can::vals::Silm::SILENT, - }; - self.can.canregs.btr().modify(|reg| reg.set_silm(mode)); - self - } - - /// Enables or disables automatic retransmission of messages. - /// - /// If this is enabled, the CAN peripheral will automatically try to retransmit each frame - /// until it can be sent. Otherwise, it will try only once to send each frame. - /// - /// Automatic retransmission is enabled by default. - pub fn set_automatic_retransmit(self, enabled: bool) -> Self { - self.can.canregs.mcr().modify(|reg| reg.set_nart(enabled)); - self - } - - /// Leaves initialization mode and enables the peripheral. - /// - /// To sync with the CAN bus, this will block until 11 consecutive recessive bits are detected - /// on the bus. - /// - /// If you want to finish configuration without enabling the peripheral, you can call - /// [`CanConfig::leave_disabled`] or [`drop`] the [`CanConfig`] instead. - pub fn enable(mut self) { - self.leave_init_mode(); - - match nb::block!(self.can.enable_non_blocking()) { - Ok(()) => {} - Err(void) => match void {}, - } - - // Don't run the destructor. - mem::forget(self); - } - - /// Leaves initialization mode, but keeps the peripheral in sleep mode. - /// - /// Before the [`Can`] instance can be used, you have to enable it by calling - /// [`Can::enable_non_blocking`]. - pub fn leave_disabled(mut self) { - self.leave_init_mode(); - } - - /// Leaves initialization mode, enters sleep mode. - fn leave_init_mode(&mut self) { - self.can.canregs.mcr().modify(|reg| { - reg.set_sleep(true); - reg.set_inrq(false); - }); - loop { - let msr = self.can.canregs.msr().read(); - if msr.slak() && !msr.inak() { - break; - } - } - } -} - -impl Drop for CanConfig<'_, I> { - #[inline] - fn drop(&mut self) { - self.leave_init_mode(); - } -} - -/// Builder returned by [`Can::builder`]. -#[must_use = "`CanBuilder` leaves the peripheral in uninitialized state, call `CanBuilder::enable` or `CanBuilder::leave_disabled`"] -pub struct CanBuilder { - can: Can, -} - -impl CanBuilder { - /// Configures the bit timings. - /// - /// You can use to calculate the `btr` parameter. Enter - /// parameters as follows: - /// - /// - *Clock Rate*: The input clock speed to the CAN peripheral (*not* the CPU clock speed). - /// This is the clock rate of the peripheral bus the CAN peripheral is attached to (eg. APB1). - /// - *Sample Point*: Should normally be left at the default value of 87.5%. - /// - *SJW*: Should normally be left at the default value of 1. - /// - /// Then copy the `CAN_BUS_TIME` register value from the table and pass it as the `btr` - /// parameter to this method. - pub fn set_bit_timing(mut self, bt: crate::can::util::NominalBitTiming) -> Self { - self.can.set_bit_timing(bt); - self - } - /// Enables or disables loopback mode: Internally connects the TX and RX - /// signals together. - pub fn set_loopback(self, enabled: bool) -> Self { - self.can.canregs.btr().modify(|reg| reg.set_lbkm(enabled)); - self - } - - /// Enables or disables silent mode: Disconnects the TX signal from the pin. - pub fn set_silent(self, enabled: bool) -> Self { - let mode = match enabled { - false => stm32_metapac::can::vals::Silm::NORMAL, - true => stm32_metapac::can::vals::Silm::SILENT, - }; - self.can.canregs.btr().modify(|reg| reg.set_silm(mode)); - self - } - - /// Enables or disables automatic retransmission of messages. - /// - /// If this is enabled, the CAN peripheral will automatically try to retransmit each frame - /// until it can be sent. Otherwise, it will try only once to send each frame. - /// - /// Automatic retransmission is enabled by default. - pub fn set_automatic_retransmit(self, enabled: bool) -> Self { - self.can.canregs.mcr().modify(|reg| reg.set_nart(enabled)); - self - } - - /// Leaves initialization mode and enables the peripheral. - /// - /// To sync with the CAN bus, this will block until 11 consecutive recessive bits are detected - /// on the bus. - /// - /// If you want to finish configuration without enabling the peripheral, you can call - /// [`CanBuilder::leave_disabled`] instead. - pub fn enable(mut self) -> Can { - self.leave_init_mode(); - - match nb::block!(self.can.enable_non_blocking()) { - Ok(()) => self.can, - Err(void) => match void {}, - } - } - - /// Returns the [`Can`] interface without enabling it. - /// - /// This leaves initialization mode, but keeps the peripheral in sleep mode instead of enabling - /// it. - /// - /// Before the [`Can`] instance can be used, you have to enable it by calling - /// [`Can::enable_non_blocking`]. - pub fn leave_disabled(mut self) -> Can { - self.leave_init_mode(); - self.can - } - - /// Leaves initialization mode, enters sleep mode. - fn leave_init_mode(&mut self) { - self.can.canregs.mcr().modify(|reg| { - reg.set_sleep(true); - reg.set_inrq(false); - }); - loop { - let msr = self.can.canregs.msr().read(); - if msr.slak() && !msr.inak() { - break; - } - } - } -} - -/// Interface to a bxCAN peripheral. -pub struct Can { - instance: I, - canregs: crate::pac::can::Can, -} - -impl Can -where - I: Instance, -{ - /// Creates a [`CanBuilder`] for constructing a CAN interface. - pub fn builder(instance: I, canregs: crate::pac::can::Can) -> CanBuilder { - let can_builder = CanBuilder { - can: Can { instance, canregs }, - }; - - canregs.mcr().modify(|reg| { +impl Registers { + fn enter_init_mode(&mut self) { + self.canregs.mcr().modify(|reg| { reg.set_sleep(false); reg.set_inrq(true); }); loop { - let msr = canregs.msr().read(); + let msr = self.canregs.msr().read(); if !msr.slak() && msr.inak() { break; } } + } - can_builder + // Leaves initialization mode, enters sleep mode. + fn leave_init_mode(&mut self) { + self.canregs.mcr().modify(|reg| { + reg.set_sleep(true); + reg.set_inrq(false); + }); + loop { + let msr = self.canregs.msr().read(); + if msr.slak() && !msr.inak() { + break; + } + } } fn set_bit_timing(&mut self, bt: crate::can::util::NominalBitTiming) { @@ -471,38 +306,29 @@ where }); } - /// Returns a reference to the peripheral instance. - /// - /// This allows accessing HAL-specific data stored in the instance type. - pub fn instance(&mut self) -> &mut I { - &mut self.instance + /// Enables or disables silent mode: Disconnects the TX signal from the pin. + pub fn set_silent(&self, enabled: bool) { + let mode = match enabled { + false => stm32_metapac::can::vals::Silm::NORMAL, + true => stm32_metapac::can::vals::Silm::SILENT, + }; + self.canregs.btr().modify(|reg| reg.set_silm(mode)); } - /// Disables the CAN interface and returns back the raw peripheral it was created from. + /// Enables or disables automatic retransmission of messages. /// - /// The peripheral is disabled by setting `RESET` in `CAN_MCR`, which causes the peripheral to - /// enter sleep mode. - pub fn free(self) -> I { - self.canregs.mcr().write(|reg| reg.set_reset(true)); - self.instance + /// If this is enabled, the CAN peripheral will automatically try to retransmit each frame + /// until it can be sent. Otherwise, it will try only once to send each frame. + /// + /// Automatic retransmission is enabled by default. + pub fn set_automatic_retransmit(&self, enabled: bool) { + self.canregs.mcr().modify(|reg| reg.set_nart(enabled)); } - /// Configure bit timings and silent/loop-back mode. - /// - /// Calling this method will enter initialization mode. - pub fn modify_config(&mut self) -> CanConfig<'_, I> { - self.canregs.mcr().modify(|reg| { - reg.set_sleep(false); - reg.set_inrq(true); - }); - loop { - let msr = self.canregs.msr().read(); - if !msr.slak() && msr.inak() { - break; - } - } - - CanConfig { can: self } + /// Enables or disables loopback mode: Internally connects the TX and RX + /// signals together. + pub fn set_loopback(&self, enabled: bool) { + self.canregs.btr().modify(|reg| reg.set_lbkm(enabled)); } /// Configures the automatic wake-up feature. @@ -512,6 +338,7 @@ where /// When turned on, an incoming frame will cause the peripheral to wake up from sleep and /// receive the frame. If enabled, [`Interrupt::Wakeup`] will also be triggered by the incoming /// frame. + #[allow(dead_code)] pub fn set_automatic_wakeup(&mut self, enabled: bool) { self.canregs.mcr().modify(|reg| reg.set_awum(enabled)); } @@ -540,6 +367,7 @@ where /// Puts the peripheral in a sleep mode to save power. /// /// While in sleep mode, an incoming CAN frame will trigger [`Interrupt::Wakeup`] if enabled. + #[allow(dead_code)] pub fn sleep(&mut self) { self.canregs.mcr().modify(|reg| { reg.set_sleep(true); @@ -553,10 +381,19 @@ where } } + /// Disables the CAN interface. + /// + /// The peripheral is disabled by setting `RESET` in `CAN_MCR`, which causes the peripheral to + /// enter sleep mode. + pub fn reset(&self) { + self.canregs.mcr().write(|reg| reg.set_reset(true)); + } + /// Wakes up from sleep mode. /// /// Note that this will not trigger [`Interrupt::Wakeup`], only reception of an incoming CAN /// frame will cause that interrupt. + #[allow(dead_code)] pub fn wakeup(&mut self) { self.canregs.mcr().modify(|reg| { reg.set_sleep(false); @@ -569,74 +406,19 @@ where } } } - - /// Puts a CAN frame in a free transmit mailbox for transmission on the bus. - /// - /// Frames are transmitted to the bus based on their priority (see [`FramePriority`]). - /// Transmit order is preserved for frames with identical priority. - /// - /// If all transmit mailboxes are full, and `frame` has a higher priority than the - /// lowest-priority message in the transmit mailboxes, transmission of the enqueued frame is - /// cancelled and `frame` is enqueued instead. The frame that was replaced is returned as - /// [`TransmitStatus::dequeued_frame`]. - pub fn transmit(&mut self, frame: &Frame) -> nb::Result { - // Safety: We have a `&mut self` and have unique access to the peripheral. - unsafe { Tx::::conjure(self.canregs).transmit(frame) } - } - - /// Returns `true` if no frame is pending for transmission. - pub fn is_transmitter_idle(&self) -> bool { - // Safety: Read-only operation. - unsafe { Tx::::conjure(self.canregs).is_idle() } - } - - /// Attempts to abort the sending of a frame that is pending in a mailbox. - /// - /// If there is no frame in the provided mailbox, or its transmission succeeds before it can be - /// aborted, this function has no effect and returns `false`. - /// - /// If there is a frame in the provided mailbox, and it is canceled successfully, this function - /// returns `true`. - pub fn abort(&mut self, mailbox: Mailbox) -> bool { - // Safety: We have a `&mut self` and have unique access to the peripheral. - unsafe { Tx::::conjure(self.canregs).abort(mailbox) } - } - - - pub(crate) fn split_by_ref(&mut self) -> (Tx, Rx) { - // Safety: We take `&mut self` and the return value lifetimes are tied to `self`'s lifetime. - let tx = unsafe { Tx::conjure(self.canregs) }; - let rx0 = unsafe { Rx::conjure() }; - (tx, rx0) - } - -} - -impl Can { - /// Accesses the filter banks owned by this CAN peripheral. - /// - /// To modify filters of a slave peripheral, `modify_filters` has to be called on the master - /// peripheral instead. - pub fn modify_filters(&mut self) -> MasterFilters<'_, I> { - unsafe { MasterFilters::new(self.canregs) } - } -} - -/// Marker for Tx half -pub struct Tx { - _can: PhantomData, - canregs: crate::pac::can::Can, -} - -impl Tx -where - I: Instance, -{ - unsafe fn conjure(canregs: crate::pac::can::Can) -> Self { - Self { - _can: PhantomData, - canregs, + + pub fn curr_error(&self) -> Option { + let err = { self.canregs.esr().read() }; + if err.boff() { + return Some(BusError::BusOff); + } else if err.epvf() { + return Some(BusError::BusPassive); + } else if err.ewgf() { + return Some(BusError::BusWarning); + } else if let Some(err) = err.lec().into_bus_err() { + return Some(err); } + None } /// Puts a CAN frame in a transmit mailbox for transmission on the bus. @@ -802,6 +584,361 @@ where reg.set_rqcp(2, true); }); } + + pub fn receive_fifo(&self, fifo: crate::can::_version::bx::RxFifo) -> Option { + // Generate timestamp as early as possible + #[cfg(feature = "time")] + let ts = embassy_time::Instant::now(); + + use crate::pac::can::vals::Ide; + + let fifo_idx = match fifo { + crate::can::_version::bx::RxFifo::Fifo0 => 0usize, + crate::can::_version::bx::RxFifo::Fifo1 => 1usize, + }; + let rfr = self.canregs.rfr(fifo_idx); + let fifo = self.canregs.rx(fifo_idx); + + // If there are no pending messages, there is nothing to do + if rfr.read().fmp() == 0 { + return None; + } + + let rir = fifo.rir().read(); + let id: embedded_can::Id = if rir.ide() == Ide::STANDARD { + embedded_can::StandardId::new(rir.stid()).unwrap().into() + } else { + let stid = (rir.stid() & 0x7FF) as u32; + let exid = rir.exid() & 0x3FFFF; + let id = (stid << 18) | (exid); + embedded_can::ExtendedId::new(id).unwrap().into() + }; + let data_len = fifo.rdtr().read().dlc(); + let mut data: [u8; 8] = [0; 8]; + data[0..4].copy_from_slice(&fifo.rdlr().read().0.to_ne_bytes()); + data[4..8].copy_from_slice(&fifo.rdhr().read().0.to_ne_bytes()); + + let frame = Frame::new(Header::new(id, data_len, false), &data).unwrap(); + let envelope = Envelope { + #[cfg(feature = "time")] + ts, + frame, + }; + + rfr.modify(|v| v.set_rfom(true)); + + Some(envelope) + } +} + +/// Configuration proxy returned by [`Can::modify_config`]. +#[must_use = "`CanConfig` leaves the peripheral in uninitialized state, call `CanConfig::enable` or explicitly drop the value"] +pub struct CanConfig<'a, I: Instance> { + can: &'a mut Can, +} + +impl CanConfig<'_, I> { + /// Configures the bit timings. + /// + /// You can use to calculate the `btr` parameter. Enter + /// parameters as follows: + /// + /// - *Clock Rate*: The input clock speed to the CAN peripheral (*not* the CPU clock speed). + /// This is the clock rate of the peripheral bus the CAN peripheral is attached to (eg. APB1). + /// - *Sample Point*: Should normally be left at the default value of 87.5%. + /// - *SJW*: Should normally be left at the default value of 1. + /// + /// Then copy the `CAN_BUS_TIME` register value from the table and pass it as the `btr` + /// parameter to this method. + pub fn set_bit_timing(self, bt: crate::can::util::NominalBitTiming) -> Self { + self.can.registers.set_bit_timing(bt); + self + } + + /// Enables or disables loopback mode: Internally connects the TX and RX + /// signals together. + pub fn set_loopback(self, enabled: bool) -> Self { + self.can.registers.set_loopback(enabled); + self + } + + /// Enables or disables silent mode: Disconnects the TX signal from the pin. + pub fn set_silent(self, enabled: bool) -> Self { + self.can.registers.set_silent(enabled); + self + } + + /// Enables or disables automatic retransmission of messages. + /// + /// If this is enabled, the CAN peripheral will automatically try to retransmit each frame + /// until it can be sent. Otherwise, it will try only once to send each frame. + /// + /// Automatic retransmission is enabled by default. + pub fn set_automatic_retransmit(self, enabled: bool) -> Self { + self.can.registers.set_automatic_retransmit(enabled); + self + } + + /// Leaves initialization mode and enables the peripheral. + /// + /// To sync with the CAN bus, this will block until 11 consecutive recessive bits are detected + /// on the bus. + /// + /// If you want to finish configuration without enabling the peripheral, you can call + /// [`CanConfig::leave_disabled`] or [`drop`] the [`CanConfig`] instead. + pub fn enable(self) { + self.can.registers.leave_init_mode(); + + match nb::block!(self.can.registers.enable_non_blocking()) { + Ok(()) => {} + Err(void) => match void {}, + } + + // Don't run the destructor. + mem::forget(self); + } + + /// Leaves initialization mode, but keeps the peripheral in sleep mode. + /// + /// Before the [`Can`] instance can be used, you have to enable it by calling + /// [`Can::enable_non_blocking`]. + pub fn leave_disabled(self) { + self.can.registers.leave_init_mode(); + } +} + +impl Drop for CanConfig<'_, I> { + #[inline] + fn drop(&mut self) { + self.can.registers.leave_init_mode(); + } +} + +/// Builder returned by [`Can::builder`]. +#[must_use = "`CanBuilder` leaves the peripheral in uninitialized state, call `CanBuilder::enable` or `CanBuilder::leave_disabled`"] +pub struct CanBuilder { + can: Can, +} + +impl CanBuilder { + /// Configures the bit timings. + /// + /// You can use to calculate the `btr` parameter. Enter + /// parameters as follows: + /// + /// - *Clock Rate*: The input clock speed to the CAN peripheral (*not* the CPU clock speed). + /// This is the clock rate of the peripheral bus the CAN peripheral is attached to (eg. APB1). + /// - *Sample Point*: Should normally be left at the default value of 87.5%. + /// - *SJW*: Should normally be left at the default value of 1. + /// + /// Then copy the `CAN_BUS_TIME` register value from the table and pass it as the `btr` + /// parameter to this method. + pub fn set_bit_timing(mut self, bt: crate::can::util::NominalBitTiming) -> Self { + self.can.registers.set_bit_timing(bt); + self + } + /// Enables or disables loopback mode: Internally connects the TX and RX + /// signals together. + pub fn set_loopback(self, enabled: bool) -> Self { + self.can.registers.set_loopback(enabled); + self + } + + /// Enables or disables silent mode: Disconnects the TX signal from the pin. + pub fn set_silent(self, enabled: bool) -> Self { + self.can.registers.set_silent(enabled); + self + } + + /// Enables or disables automatic retransmission of messages. + /// + /// If this is enabled, the CAN peripheral will automatically try to retransmit each frame + /// until it can be sent. Otherwise, it will try only once to send each frame. + /// + /// Automatic retransmission is enabled by default. + pub fn set_automatic_retransmit(self, enabled: bool) -> Self { + self.can.registers.set_automatic_retransmit(enabled); + self + } + + /// Leaves initialization mode and enables the peripheral. + /// + /// To sync with the CAN bus, this will block until 11 consecutive recessive bits are detected + /// on the bus. + /// + /// If you want to finish configuration without enabling the peripheral, you can call + /// [`CanBuilder::leave_disabled`] instead. + pub fn enable(mut self) -> Can { + self.leave_init_mode(); + + match nb::block!(self.can.registers.enable_non_blocking()) { + Ok(()) => self.can, + Err(void) => match void {}, + } + } + + /// Returns the [`Can`] interface without enabling it. + /// + /// This leaves initialization mode, but keeps the peripheral in sleep mode instead of enabling + /// it. + /// + /// Before the [`Can`] instance can be used, you have to enable it by calling + /// [`Can::enable_non_blocking`]. + pub fn leave_disabled(mut self) -> Can { + self.leave_init_mode(); + self.can + } + + /// Leaves initialization mode, enters sleep mode. + fn leave_init_mode(&mut self) { + self.can.registers.leave_init_mode(); + } +} + +/// Interface to a bxCAN peripheral. +pub struct Can { + instance: I, + canregs: crate::pac::can::Can, + pub(crate) registers: Registers, +} + +impl Can +where + I: Instance, +{ + /// Creates a [`CanBuilder`] for constructing a CAN interface. + pub fn builder(instance: I, canregs: crate::pac::can::Can) -> CanBuilder { + let mut can_builder = CanBuilder { + can: Can { + instance, + canregs, + registers: Registers { canregs }, + }, + }; + + can_builder.can.registers.enter_init_mode(); + + can_builder + } + + /// Disables the CAN interface and returns back the raw peripheral it was created from. + /// + /// The peripheral is disabled by setting `RESET` in `CAN_MCR`, which causes the peripheral to + /// enter sleep mode. + pub fn free(self) -> I { + self.registers.reset(); + self.instance + } + + /// Configure bit timings and silent/loop-back mode. + /// + /// Calling this method will enter initialization mode. + pub fn modify_config(&mut self) -> CanConfig<'_, I> { + self.registers.enter_init_mode(); + + CanConfig { can: self } + } + + /// Puts a CAN frame in a free transmit mailbox for transmission on the bus. + /// + /// Frames are transmitted to the bus based on their priority (see [`FramePriority`]). + /// Transmit order is preserved for frames with identical priority. + /// + /// If all transmit mailboxes are full, and `frame` has a higher priority than the + /// lowest-priority message in the transmit mailboxes, transmission of the enqueued frame is + /// cancelled and `frame` is enqueued instead. The frame that was replaced is returned as + /// [`TransmitStatus::dequeued_frame`]. + pub fn transmit(&mut self, frame: &Frame) -> nb::Result { + // Safety: We have a `&mut self` and have unique access to the peripheral. + unsafe { Tx::::conjure(self.canregs).transmit(frame) } + } + + /// Returns `true` if no frame is pending for transmission. + pub fn is_transmitter_idle(&self) -> bool { + // Safety: Read-only operation. + unsafe { Tx::::conjure(self.canregs).is_idle() } + } + + /// Attempts to abort the sending of a frame that is pending in a mailbox. + /// + /// If there is no frame in the provided mailbox, or its transmission succeeds before it can be + /// aborted, this function has no effect and returns `false`. + /// + /// If there is a frame in the provided mailbox, and it is canceled successfully, this function + /// returns `true`. + pub fn abort(&mut self, mailbox: Mailbox) -> bool { + // Safety: We have a `&mut self` and have unique access to the peripheral. + unsafe { Tx::::conjure(self.canregs).abort(mailbox) } + } + + pub(crate) fn split_by_ref(&mut self) -> (Tx, Rx) { + // Safety: We take `&mut self` and the return value lifetimes are tied to `self`'s lifetime. + let tx = unsafe { Tx::conjure(self.canregs) }; + let rx0 = unsafe { Rx::conjure() }; + (tx, rx0) + } +} + +impl Can { + /// Accesses the filter banks owned by this CAN peripheral. + /// + /// To modify filters of a slave peripheral, `modify_filters` has to be called on the master + /// peripheral instead. + pub fn modify_filters(&mut self) -> MasterFilters<'_, I> { + unsafe { MasterFilters::new(self.canregs) } + } +} + +/// Marker for Tx half +pub struct Tx { + _can: PhantomData, + pub(crate) registers: Registers, +} + +impl Tx +where + I: Instance, +{ + unsafe fn conjure(canregs: crate::pac::can::Can) -> Self { + Self { + _can: PhantomData, + registers: Registers { canregs }, //canregs, + } + } + + /// Puts a CAN frame in a transmit mailbox for transmission on the bus. + /// + /// Frames are transmitted to the bus based on their priority (see [`FramePriority`]). + /// Transmit order is preserved for frames with identical priority. + /// + /// If all transmit mailboxes are full, and `frame` has a higher priority than the + /// lowest-priority message in the transmit mailboxes, transmission of the enqueued frame is + /// cancelled and `frame` is enqueued instead. The frame that was replaced is returned as + /// [`TransmitStatus::dequeued_frame`]. + pub fn transmit(&mut self, frame: &Frame) -> nb::Result { + self.registers.transmit(frame) + } + + /// Attempts to abort the sending of a frame that is pending in a mailbox. + /// + /// If there is no frame in the provided mailbox, or its transmission succeeds before it can be + /// aborted, this function has no effect and returns `false`. + /// + /// If there is a frame in the provided mailbox, and it is canceled successfully, this function + /// returns `true`. + pub fn abort(&mut self, mailbox: Mailbox) -> bool { + self.registers.abort(mailbox) + } + + /// Returns `true` if no frame is pending for transmission. + pub fn is_idle(&self) -> bool { + self.registers.is_idle() + } + + /// Clears the request complete flag for all mailboxes. + pub fn clear_interrupt_flags(&mut self) { + self.registers.clear_interrupt_flags() + } } /// Marker for Rx half @@ -814,9 +951,7 @@ where I: Instance, { unsafe fn conjure() -> Self { - Self { - _can: PhantomData, - } + Self { _can: PhantomData } } } diff --git a/embassy-stm32/src/can/bxcan.rs b/embassy-stm32/src/can/bxcan.rs index 9d2b8797e..d865bbe7d 100644 --- a/embassy-stm32/src/can/bxcan.rs +++ b/embassy-stm32/src/can/bxcan.rs @@ -14,7 +14,6 @@ use futures::FutureExt; use crate::gpio::AFType; use crate::interrupt::typelevel::Interrupt; -use crate::pac::can::vals::{Ide, Lec}; use crate::rcc::RccPeripheral; use crate::{interrupt, peripherals, Peripheral}; @@ -185,7 +184,7 @@ impl<'d, T: Instance> Can<'d, T> { /// This will wait for 11 consecutive recessive bits (bus idle state). /// Contrary to enable method from bxcan library, this will not freeze the executor while waiting. pub async fn enable(&mut self) { - while self.enable_non_blocking().is_err() { + while self.registers.enable_non_blocking().is_err() { // SCE interrupt is only generated for entering sleep mode, but not leaving. // Yield to allow other tasks to execute while can bus is initializing. embassy_futures::yield_now().await; @@ -243,52 +242,17 @@ impl<'d, T: Instance> Can<'d, T> { } unsafe fn receive_fifo(fifo: RxFifo) { - // Generate timestamp as early as possible - #[cfg(feature = "time")] - let ts = embassy_time::Instant::now(); - let state = T::state(); - let regs = T::regs(); - let fifo_idx = match fifo { - RxFifo::Fifo0 => 0usize, - RxFifo::Fifo1 => 1usize, - }; - let rfr = regs.rfr(fifo_idx); - let fifo = regs.rx(fifo_idx); + let regsisters = crate::can::bx::Registers { canregs: T::regs() }; loop { - // If there are no pending messages, there is nothing to do - if rfr.read().fmp() == 0 { - return; - } - - let rir = fifo.rir().read(); - let id: embedded_can::Id = if rir.ide() == Ide::STANDARD { - embedded_can::StandardId::new(rir.stid()).unwrap().into() - } else { - let stid = (rir.stid() & 0x7FF) as u32; - let exid = rir.exid() & 0x3FFFF; - let id = (stid << 18) | (exid); - embedded_can::ExtendedId::new(id).unwrap().into() + match regsisters.receive_fifo(fifo) { + Some(envelope) => { + // NOTE: consensus was reached that if rx_queue is full, packets should be dropped + let _ = state.rx_queue.try_send(envelope); + } + None => return, }; - let data_len = fifo.rdtr().read().dlc(); - let mut data: [u8; 8] = [0; 8]; - data[0..4].copy_from_slice(&fifo.rdlr().read().0.to_ne_bytes()); - data[4..8].copy_from_slice(&fifo.rdhr().read().0.to_ne_bytes()); - - let frame = Frame::new(Header::new(id, data_len, false), &data).unwrap(); - let envelope = Envelope { - #[cfg(feature = "time")] - ts, - frame, - }; - - rfr.modify(|v| v.set_rfom(true)); - - /* - NOTE: consensus was reached that if rx_queue is full, packets should be dropped - */ - let _ = state.rx_queue.try_send(envelope); } } @@ -297,7 +261,7 @@ impl<'d, T: Instance> Can<'d, T> { /// Useful for doing separate transmit/receive tasks. pub fn split<'c>(&'c mut self) -> (CanTx<'d, T>, CanRx<'d, T>) { let (tx, rx) = self.can.split_by_ref(); - (CanTx { tx }, CanRx { rx}) + (CanTx { tx }, CanRx { rx }) } } @@ -459,10 +423,7 @@ impl<'d, T: Instance> CanRx<'d, T> { } } -enum RxFifo { - Fifo0, - Fifo1, -} +use crate::can::bx::RxFifo; impl<'d, T: Instance> Drop for Can<'d, T> { fn drop(&mut self) { @@ -601,21 +562,4 @@ impl Index for crate::can::bx::Mailbox { } } -trait IntoBusError { - fn into_bus_err(self) -> Option; -} -impl IntoBusError for Lec { - fn into_bus_err(self) -> Option { - match self { - Lec::STUFF => Some(BusError::Stuff), - Lec::FORM => Some(BusError::Form), - Lec::ACK => Some(BusError::Acknowledge), - Lec::BITRECESSIVE => Some(BusError::BitRecessive), - Lec::BITDOMINANT => Some(BusError::BitDominant), - Lec::CRC => Some(BusError::Crc), - Lec::CUSTOM => Some(BusError::Software), - _ => None, - } - } -} diff --git a/embassy-stm32/src/can/common.rs b/embassy-stm32/src/can/common.rs index 1de54e5a1..8c9990798 100644 --- a/embassy-stm32/src/can/common.rs +++ b/embassy-stm32/src/can/common.rs @@ -1,8 +1,15 @@ use embassy_sync::channel::{DynamicReceiver, DynamicSender}; -use crate::can::_version::frame::*; -use crate::can::_version::Timestamp; use crate::can::_version::enums::*; +use crate::can::_version::frame::*; + +/// Timestamp for incoming packets. Use Embassy time when enabled. +#[cfg(feature = "time")] +pub type Timestamp = embassy_time::Instant; + +/// Timestamp for incoming packets. +#[cfg(not(feature = "time"))] +pub type Timestamp = u16; pub(crate) struct ClassicBufferedRxInner { pub rx_sender: DynamicSender<'static, Result<(ClassicFrame, Timestamp), BusError>>, @@ -48,4 +55,3 @@ impl BufferedCanSender { /// Receiver that can be used for receiving CAN frames. Note, each CAN frame will only be received by one receiver. pub type BufferedCanReceiver = embassy_sync::channel::DynamicReceiver<'static, Result<(ClassicFrame, Timestamp), BusError>>; - diff --git a/embassy-stm32/src/can/fdcan.rs b/embassy-stm32/src/can/fdcan.rs index c42c42853..42c9bd9f6 100644 --- a/embassy-stm32/src/can/fdcan.rs +++ b/embassy-stm32/src/can/fdcan.rs @@ -25,7 +25,8 @@ use fd::config::*; use fd::filter::*; pub use fd::{config, filter}; use frame::*; -pub use self::common::{BufferedCanSender, BufferedCanReceiver}; + +pub use self::common::{BufferedCanReceiver, BufferedCanSender}; /// Timestamp for incoming packets. Use Embassy time when enabled. #[cfg(feature = "time")] @@ -416,7 +417,6 @@ pub struct BufferedCan<'d, T: Instance, const TX_BUF_SIZE: usize, const RX_BUF_S rx_buf: &'static RxBuf, } - impl<'c, 'd, T: Instance, const TX_BUF_SIZE: usize, const RX_BUF_SIZE: usize> BufferedCan<'d, T, TX_BUF_SIZE, RX_BUF_SIZE> { From 26c739c2f93252e9dc476d69f591a84d44491787 Mon Sep 17 00:00:00 2001 From: Corey Schuhen Date: Sun, 24 Mar 2024 07:27:29 +1000 Subject: [PATCH 40/62] BXCAN: Create RxMode enum and move reader methods into it, laying foundations for different Rx buffering modes. --- embassy-stm32/src/can/bx/mod.rs | 12 +- embassy-stm32/src/can/bxcan.rs | 264 +++++++++++++++++++++++--------- embassy-stm32/src/can/common.rs | 4 + embassy-stm32/src/can/enums.rs | 10 ++ 4 files changed, 218 insertions(+), 72 deletions(-) diff --git a/embassy-stm32/src/can/bx/mod.rs b/embassy-stm32/src/can/bx/mod.rs index 9b6ebf5a4..c508ef2fe 100644 --- a/embassy-stm32/src/can/bx/mod.rs +++ b/embassy-stm32/src/can/bx/mod.rs @@ -406,7 +406,7 @@ impl Registers { } } } - + pub fn curr_error(&self) -> Option { let err = { self.canregs.esr().read() }; if err.boff() { @@ -585,6 +585,16 @@ impl Registers { }); } + pub fn receive_frame_available(&self) -> bool { + if self.canregs.rfr(0).read().fmp() != 0 { + true + } else if self.canregs.rfr(1).read().fmp() != 0 { + true + } else { + false + } + } + pub fn receive_fifo(&self, fifo: crate::can::_version::bx::RxFifo) -> Option { // Generate timestamp as early as possible #[cfg(feature = "time")] diff --git a/embassy-stm32/src/can/bxcan.rs b/embassy-stm32/src/can/bxcan.rs index d865bbe7d..66f6e7067 100644 --- a/embassy-stm32/src/can/bxcan.rs +++ b/embassy-stm32/src/can/bxcan.rs @@ -10,7 +10,6 @@ use embassy_hal_internal::{into_ref, PeripheralRef}; use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex; use embassy_sync::channel::Channel; use embassy_sync::waitqueue::AtomicWaker; -use futures::FutureExt; use crate::gpio::AFType; use crate::interrupt::typelevel::Interrupt; @@ -22,6 +21,9 @@ use enums::*; pub mod frame; pub mod util; +mod common; +pub use self::common::{BufferedCanReceiver, BufferedCanSender, Timestamp}; + /// Contains CAN frame and additional metadata. /// /// Timestamp is available if `time` feature is enabled. @@ -59,8 +61,7 @@ pub struct Rx0InterruptHandler { impl interrupt::typelevel::Handler for Rx0InterruptHandler { unsafe fn on_interrupt() { - // info!("rx0 irq"); - Can::::receive_fifo(RxFifo::Fifo0); + T::state().rx_mode.on_interrupt::(RxFifo::Fifo0); } } @@ -71,8 +72,7 @@ pub struct Rx1InterruptHandler { impl interrupt::typelevel::Handler for Rx1InterruptHandler { unsafe fn on_interrupt() { - // info!("rx1 irq"); - Can::::receive_fifo(RxFifo::Fifo1); + T::state().rx_mode.on_interrupt::(RxFifo::Fifo1); } } @@ -99,16 +99,6 @@ pub struct Can<'d, T: Instance> { can: crate::can::bx::Can>, } -/// Error returned by `try_read` -#[derive(Debug)] -#[cfg_attr(feature = "defmt", derive(defmt::Format))] -pub enum TryReadError { - /// Bus error - BusError(BusError), - /// Receive buffer is empty - Empty, -} - /// Error returned by `try_write` #[derive(Debug)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] @@ -226,34 +216,19 @@ impl<'d, T: Instance> Can<'d, T> { /// /// Returns a tuple of the time the message was received and the message frame pub async fn read(&mut self) -> Result { - self.split().1.read().await + T::state().rx_mode.read::().await } /// Attempts to read a CAN frame without blocking. /// /// Returns [Err(TryReadError::Empty)] if there are no frames in the rx queue. pub fn try_read(&mut self) -> Result { - self.split().1.try_read() + T::state().rx_mode.try_read::() } /// Waits while receive queue is empty. pub async fn wait_not_empty(&mut self) { - self.split().1.wait_not_empty().await - } - - unsafe fn receive_fifo(fifo: RxFifo) { - let state = T::state(); - let regsisters = crate::can::bx::Registers { canregs: T::regs() }; - - loop { - match regsisters.receive_fifo(fifo) { - Some(envelope) => { - // NOTE: consensus was reached that if rx_queue is full, packets should be dropped - let _ = state.rx_queue.try_send(envelope); - } - None => return, - }; - } + T::state().rx_mode.wait_not_empty::().await } /// Split the CAN driver into transmit and receive halves. @@ -375,51 +350,95 @@ impl<'d, T: Instance> CanRx<'d, T> { /// /// Returns a tuple of the time the message was received and the message frame pub async fn read(&mut self) -> Result { - poll_fn(|cx| { - T::state().err_waker.register(cx.waker()); - if let Poll::Ready(envelope) = T::state().rx_queue.receive().poll_unpin(cx) { - return Poll::Ready(Ok(envelope)); - } else if let Some(err) = self.curr_error() { - return Poll::Ready(Err(err)); - } - - Poll::Pending - }) - .await + T::state().rx_mode.read::().await } /// Attempts to read a CAN frame without blocking. /// /// Returns [Err(TryReadError::Empty)] if there are no frames in the rx queue. pub fn try_read(&mut self) -> Result { - if let Ok(envelope) = T::state().rx_queue.try_receive() { - return Ok(envelope); - } - - if let Some(err) = self.curr_error() { - return Err(TryReadError::BusError(err)); - } - - Err(TryReadError::Empty) + T::state().rx_mode.try_read::() } /// Waits while receive queue is empty. pub async fn wait_not_empty(&mut self) { - poll_fn(|cx| T::state().rx_queue.poll_ready_to_receive(cx)).await + T::state().rx_mode.wait_not_empty::().await } - fn curr_error(&self) -> Option { - let err = { T::regs().esr().read() }; - if err.boff() { - return Some(BusError::BusOff); - } else if err.epvf() { - return Some(BusError::BusPassive); - } else if err.ewgf() { - return Some(BusError::BusWarning); - } else if let Some(err) = err.lec().into_bus_err() { - return Some(err); + /// Return a buffered instance of driver without CAN FD support. User must supply Buffers + pub fn buffered( + self, + rxb: &'static mut RxBuf, + ) -> BufferedCanRx<'d, T, RX_BUF_SIZE> { + BufferedCanRx::new(self.rx, rxb) + } +} + +/// User supplied buffer for RX Buffering +pub type RxBuf = + Channel, BUF_SIZE>; + +/// CAN driver, receive half in Buffered mode. +pub struct BufferedCanRx<'d, T: Instance, const RX_BUF_SIZE: usize> { + _rx: crate::can::bx::Rx>, + rx_buf: &'static RxBuf, +} + +impl<'d, T: Instance, const RX_BUF_SIZE: usize> BufferedCanRx<'d, T, RX_BUF_SIZE> { + fn new(_rx: crate::can::bx::Rx>, rx_buf: &'static RxBuf) -> Self { + BufferedCanRx { _rx, rx_buf }.setup() + } + + fn setup(self) -> Self { + // We don't want interrupts being processed while we change modes. + critical_section::with(|_| unsafe { + let rx_inner = self::common::ClassicBufferedRxInner { + rx_sender: self.rx_buf.sender().into(), + }; + T::mut_state().rx_mode = RxMode::Buffered(rx_inner); + }); + self + } + + /// Async read frame from RX buffer. + pub async fn read(&mut self) -> Result<(Frame, Timestamp), BusError> { + self.rx_buf.receive().await + } + + /// Attempts to read a CAN frame without blocking. + /// + /// Returns [Err(TryReadError::Empty)] if there are no frames in the rx queue. + pub fn try_read(&mut self) -> Result { + match &T::state().rx_mode { + RxMode::Buffered(_) => { + if let Ok(result) = self.rx_buf.try_receive() { + match result { + Ok((frame, ts)) => Ok(Envelope { ts, frame }), + Err(e) => Err(TryReadError::BusError(e)), + } + } else { + let registers = crate::can::bx::Registers { canregs: T::regs() }; + if let Some(err) = registers.curr_error() { + return Err(TryReadError::BusError(err)); + } else { + Err(TryReadError::Empty) + } + } + } + _ => { + panic!("Bad Mode") + } } - None + } + + /// Waits while receive queue is empty. + pub async fn wait_not_empty(&mut self) { + poll_fn(|cx| self.rx_buf.poll_ready_to_receive(cx)).await + } + + /// Returns a receiver that can be used for receiving CAN frames. Note, each CAN frame will only be received by one receiver. + pub fn reader(&self) -> BufferedCanReceiver { + self.rx_buf.receiver().into() } } @@ -448,10 +467,111 @@ impl<'d, T: Instance> DerefMut for Can<'d, T> { } } +use crate::can::enums::{BusError, TryReadError}; + +pub(crate) enum RxMode { + NonBuffered(AtomicWaker), + Buffered(crate::can::_version::common::ClassicBufferedRxInner), +} + +impl RxMode { + pub fn on_interrupt(&self, fifo: crate::can::_version::bx::RxFifo) { + match self { + Self::NonBuffered(waker) => { + // Disable interrupts until read + let fifo_idx = match fifo { + crate::can::_version::bx::RxFifo::Fifo0 => 0usize, + crate::can::_version::bx::RxFifo::Fifo1 => 1usize, + }; + T::regs().ier().write(|w| { + w.set_fmpie(fifo_idx, false); + }); + waker.wake(); + } + Self::Buffered(buf) => { + let regsisters = crate::can::bx::Registers { canregs: T::regs() }; + + loop { + match regsisters.receive_fifo(fifo) { + Some(envelope) => { + // NOTE: consensus was reached that if rx_queue is full, packets should be dropped + let _ = buf.rx_sender.try_send(Ok((envelope.frame, envelope.ts))); + } + None => return, + }; + } + } + } + } + + pub async fn read(&self) -> Result { + match self { + Self::NonBuffered(waker) => { + poll_fn(|cx| { + T::state().err_waker.register(cx.waker()); + waker.register(cx.waker()); + match self.try_read::() { + Ok(result) => Poll::Ready(Ok(result)), + Err(TryReadError::Empty) => Poll::Pending, + Err(TryReadError::BusError(be)) => Poll::Ready(Err(be)), + } + }) + .await + } + _ => { + panic!("Bad Mode") + } + } + } + pub fn try_read(&self) -> Result { + match self { + Self::NonBuffered(_) => { + let registers = crate::can::bx::Registers { canregs: T::regs() }; + if let Some(msg) = registers.receive_fifo(super::bx::RxFifo::Fifo0) { + T::regs().ier().write(|w| { + w.set_fmpie(0, true); + }); + Ok(msg) + } else if let Some(msg) = registers.receive_fifo(super::bx::RxFifo::Fifo1) { + T::regs().ier().write(|w| { + w.set_fmpie(1, true); + }); + Ok(msg) + } else if let Some(err) = registers.curr_error() { + Err(TryReadError::BusError(err)) + } else { + Err(TryReadError::Empty) + } + } + _ => { + panic!("Bad Mode") + } + } + } + pub async fn wait_not_empty(&self) { + match &T::state().rx_mode { + Self::NonBuffered(waker) => { + poll_fn(|cx| { + waker.register(cx.waker()); + let registers = crate::can::bx::Registers { canregs: T::regs() }; + if registers.receive_frame_available() { + Poll::Ready(()) + } else { + Poll::Pending + } + }) + .await + } + _ => { + panic!("Bad Mode") + } + } + } +} struct State { pub tx_waker: AtomicWaker, pub err_waker: AtomicWaker, - pub rx_queue: Channel, + pub(crate) rx_mode: RxMode, } impl State { @@ -459,7 +579,7 @@ impl State { Self { tx_waker: AtomicWaker::new(), err_waker: AtomicWaker::new(), - rx_queue: Channel::new(), + rx_mode: RxMode::NonBuffered(AtomicWaker::new()), } } } @@ -467,6 +587,7 @@ impl State { trait SealedInstance { fn regs() -> crate::pac::can::Can; fn state() -> &'static State; + unsafe fn mut_state() -> &'static mut State; } /// CAN instance trait. @@ -495,9 +616,12 @@ foreach_peripheral!( crate::pac::$inst } + unsafe fn mut_state() -> & 'static mut State { + static mut STATE: State = State::new(); + &mut *core::ptr::addr_of_mut!(STATE) + } fn state() -> &'static State { - static STATE: State = State::new(); - &STATE + unsafe { peripherals::$inst::mut_state() } } } @@ -561,5 +685,3 @@ impl Index for crate::can::bx::Mailbox { } } } - - diff --git a/embassy-stm32/src/can/common.rs b/embassy-stm32/src/can/common.rs index 8c9990798..b0d177a6d 100644 --- a/embassy-stm32/src/can/common.rs +++ b/embassy-stm32/src/can/common.rs @@ -18,9 +18,13 @@ pub(crate) struct ClassicBufferedTxInner { pub tx_receiver: DynamicReceiver<'static, ClassicFrame>, } +#[cfg(any(can_fdcan_v1, can_fdcan_h7))] + pub(crate) struct FdBufferedRxInner { pub rx_sender: DynamicSender<'static, Result<(FdFrame, Timestamp), BusError>>, } + +#[cfg(any(can_fdcan_v1, can_fdcan_h7))] pub(crate) struct FdBufferedTxInner { pub tx_receiver: DynamicReceiver<'static, FdFrame>, } diff --git a/embassy-stm32/src/can/enums.rs b/embassy-stm32/src/can/enums.rs index 651de9194..4d89c84d1 100644 --- a/embassy-stm32/src/can/enums.rs +++ b/embassy-stm32/src/can/enums.rs @@ -40,3 +40,13 @@ pub enum FrameCreateError { /// Invalid ID. InvalidCanId, } + +/// Error returned by `try_read` +#[derive(Debug)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +pub enum TryReadError { + /// Bus error + BusError(BusError), + /// Receive buffer is empty + Empty, +} From 41b7e4a434c0d1fe1fc5d93afe76f8058aa411db Mon Sep 17 00:00:00 2001 From: Corey Schuhen Date: Sun, 24 Mar 2024 08:08:12 +1000 Subject: [PATCH 41/62] BXCAN: Create TxMode in order to support buffered TX. --- embassy-stm32/src/can/bxcan.rs | 130 ++++++++++++++++++++++++++++++--- embassy-stm32/src/can/fdcan.rs | 5 +- 2 files changed, 120 insertions(+), 15 deletions(-) diff --git a/embassy-stm32/src/can/bxcan.rs b/embassy-stm32/src/can/bxcan.rs index 66f6e7067..45a3836c0 100644 --- a/embassy-stm32/src/can/bxcan.rs +++ b/embassy-stm32/src/can/bxcan.rs @@ -17,7 +17,6 @@ use crate::rcc::RccPeripheral; use crate::{interrupt, peripherals, Peripheral}; pub mod enums; -use enums::*; pub mod frame; pub mod util; @@ -49,8 +48,7 @@ impl interrupt::typelevel::Handler for TxInterruptH v.set_rqcp(1, true); v.set_rqcp(2, true); }); - - T::state().tx_waker.wake(); + T::state().tx_mode.on_interrupt::(); } } @@ -258,7 +256,7 @@ impl<'d, T: Instance> CanTx<'d, T> { /// If the TX queue is full, this will wait until there is space, therefore exerting backpressure. pub async fn write(&mut self, frame: &Frame) -> crate::can::bx::TransmitStatus { poll_fn(|cx| { - T::state().tx_waker.register(cx.waker()); + T::state().tx_mode.register(cx.waker()); if let Ok(status) = self.tx.transmit(frame) { return Poll::Ready(status); } @@ -277,7 +275,7 @@ impl<'d, T: Instance> CanTx<'d, T> { async fn flush_inner(mb: crate::can::bx::Mailbox) { poll_fn(|cx| { - T::state().tx_waker.register(cx.waker()); + T::state().tx_mode.register(cx.waker()); if T::regs().tsr().read().tme(mb.index()) { return Poll::Ready(()); } @@ -294,7 +292,7 @@ impl<'d, T: Instance> CanTx<'d, T> { async fn flush_any_inner() { poll_fn(|cx| { - T::state().tx_waker.register(cx.waker()); + T::state().tx_mode.register(cx.waker()); let tsr = T::regs().tsr().read(); if tsr.tme(crate::can::bx::Mailbox::Mailbox0.index()) @@ -316,7 +314,7 @@ impl<'d, T: Instance> CanTx<'d, T> { async fn flush_all_inner() { poll_fn(|cx| { - T::state().tx_waker.register(cx.waker()); + T::state().tx_mode.register(cx.waker()); let tsr = T::regs().tsr().read(); if tsr.tme(crate::can::bx::Mailbox::Mailbox0.index()) @@ -335,6 +333,62 @@ impl<'d, T: Instance> CanTx<'d, T> { pub async fn flush_all(&self) { Self::flush_all_inner().await } + + /// Return a buffered instance of driver. User must supply Buffers + pub fn buffered( + self, + txb: &'static mut TxBuf, + ) -> BufferedCanTx<'d, T, TX_BUF_SIZE> { + BufferedCanTx::new(self.tx, txb) + } +} + +/// User supplied buffer for TX buffering +pub type TxBuf = Channel; + +/// CAN driver, transmit half. +pub struct BufferedCanTx<'d, T: Instance, const TX_BUF_SIZE: usize> { + _tx: crate::can::bx::Tx>, + tx_buf: &'static TxBuf, +} + +impl<'d, T: Instance, const TX_BUF_SIZE: usize> BufferedCanTx<'d, T, TX_BUF_SIZE> { + fn new(_tx: crate::can::bx::Tx>, tx_buf: &'static TxBuf) -> Self { + Self { _tx, tx_buf }.setup() + } + + fn setup(self) -> Self { + // We don't want interrupts being processed while we change modes. + critical_section::with(|_| unsafe { + let tx_inner = self::common::ClassicBufferedTxInner { + tx_receiver: self.tx_buf.receiver().into(), + }; + T::mut_state().tx_mode = TxMode::Buffered(tx_inner); + }); + self + } + + /// Async write frame to TX buffer. + pub async fn write(&mut self, frame: &Frame) { + self.tx_buf.send(*frame).await; + T::TXInterrupt::pend(); // Wake for Tx + } + + /// Returns a sender that can be used for sending CAN frames. + pub fn writer(&self) -> BufferedCanSender { + BufferedCanSender { + tx_buf: self.tx_buf.sender().into(), + waker: T::TXInterrupt::pend, + } + } +} + +impl<'d, T: Instance, const TX_BUF_SIZE: usize> Drop for BufferedCanTx<'d, T, TX_BUF_SIZE> { + fn drop(&mut self) { + critical_section::with(|_| unsafe { + T::mut_state().tx_mode = TxMode::NonBuffered(embassy_sync::waitqueue::AtomicWaker::new()); + }); + } } /// CAN driver, receive half. @@ -365,7 +419,7 @@ impl<'d, T: Instance> CanRx<'d, T> { T::state().rx_mode.wait_not_empty::().await } - /// Return a buffered instance of driver without CAN FD support. User must supply Buffers + /// Return a buffered instance of driver. User must supply Buffers pub fn buffered( self, rxb: &'static mut RxBuf, @@ -442,6 +496,14 @@ impl<'d, T: Instance, const RX_BUF_SIZE: usize> BufferedCanRx<'d, T, RX_BUF_SIZE } } +impl<'d, T: Instance, const RX_BUF_SIZE: usize> Drop for BufferedCanRx<'d, T, RX_BUF_SIZE> { + fn drop(&mut self) { + critical_section::with(|_| unsafe { + T::mut_state().rx_mode = RxMode::NonBuffered(embassy_sync::waitqueue::AtomicWaker::new()); + }); + } +} + use crate::can::bx::RxFifo; impl<'d, T: Instance> Drop for Can<'d, T> { @@ -568,18 +630,62 @@ impl RxMode { } } } + +enum TxMode { + NonBuffered(AtomicWaker), + Buffered(self::common::ClassicBufferedTxInner), +} + +impl TxMode { + pub fn buffer_free(&self) -> bool { + let tsr = T::regs().tsr().read(); + tsr.tme(crate::can::bx::Mailbox::Mailbox0.index()) + || tsr.tme(crate::can::bx::Mailbox::Mailbox1.index()) + || tsr.tme(crate::can::bx::Mailbox::Mailbox2.index()) + } + pub fn on_interrupt(&self) { + match &T::state().tx_mode { + TxMode::NonBuffered(waker) => waker.wake(), + TxMode::Buffered(buf) => { + while self.buffer_free::() { + match buf.tx_receiver.try_receive() { + Ok(frame) => { + let mut registers = crate::can::bx::Registers { canregs: T::regs() }; + _ = registers.transmit(&frame); + } + Err(_) => { + break; + } + } + } + } + } + } + + fn register(&self, arg: &core::task::Waker) { + match self { + TxMode::NonBuffered(waker) => { + waker.register(arg); + } + _ => { + panic!("Bad mode"); + } + } + } +} + struct State { - pub tx_waker: AtomicWaker, - pub err_waker: AtomicWaker, pub(crate) rx_mode: RxMode, + pub(crate) tx_mode: TxMode, + pub err_waker: AtomicWaker, } impl State { pub const fn new() -> Self { Self { - tx_waker: AtomicWaker::new(), - err_waker: AtomicWaker::new(), rx_mode: RxMode::NonBuffered(AtomicWaker::new()), + tx_mode: TxMode::NonBuffered(AtomicWaker::new()), + err_waker: AtomicWaker::new(), } } } diff --git a/embassy-stm32/src/can/fdcan.rs b/embassy-stm32/src/can/fdcan.rs index 42c9bd9f6..e58d8c0ec 100644 --- a/embassy-stm32/src/can/fdcan.rs +++ b/embassy-stm32/src/can/fdcan.rs @@ -507,7 +507,7 @@ pub struct BufferedCanFd<'d, T: Instance, const TX_BUF_SIZE: usize, const RX_BUF /// Sender that can be used for sending CAN frames. #[derive(Copy, Clone)] pub struct BufferedFdCanSender { - tx_buf: embassy_sync::channel::DynamicSender<'static, FdFrame>, + tx_buf: DynamicSender<'static, FdFrame>, waker: fn(), } @@ -532,8 +532,7 @@ impl BufferedFdCanSender { } /// Receiver that can be used for receiving CAN frames. Note, each CAN frame will only be received by one receiver. -pub type BufferedFdCanReceiver = - embassy_sync::channel::DynamicReceiver<'static, Result<(FdFrame, Timestamp), BusError>>; +pub type BufferedFdCanReceiver = DynamicReceiver<'static, Result<(FdFrame, Timestamp), BusError>>; impl<'c, 'd, T: Instance, const TX_BUF_SIZE: usize, const RX_BUF_SIZE: usize> BufferedCanFd<'d, T, TX_BUF_SIZE, RX_BUF_SIZE> From f5daa50a7baceb44f2aad44bf6ce055bccb08433 Mon Sep 17 00:00:00 2001 From: Corey Schuhen Date: Sun, 24 Mar 2024 14:15:46 +1000 Subject: [PATCH 42/62] BXCAN: Add struct that combines Buffered RX and Buffered TX. --- embassy-stm32/src/can/bxcan.rs | 55 +++++++++++++++++++++++++++++++++- 1 file changed, 54 insertions(+), 1 deletion(-) diff --git a/embassy-stm32/src/can/bxcan.rs b/embassy-stm32/src/can/bxcan.rs index 45a3836c0..be2e34963 100644 --- a/embassy-stm32/src/can/bxcan.rs +++ b/embassy-stm32/src/can/bxcan.rs @@ -236,6 +236,19 @@ impl<'d, T: Instance> Can<'d, T> { let (tx, rx) = self.can.split_by_ref(); (CanTx { tx }, CanRx { rx }) } + + /// Return a buffered instance of driver. User must supply Buffers + pub fn buffered<'c, const TX_BUF_SIZE: usize, const RX_BUF_SIZE: usize>( + &'c mut self, + txb: &'static mut TxBuf, + rxb: &'static mut RxBuf, + ) -> BufferedCan<'d, T, TX_BUF_SIZE, RX_BUF_SIZE> { + let (tx, rx) = self.split(); + BufferedCan { + tx: tx.buffered(txb), + rx: rx.buffered(rxb), + } + } } impl<'d, T: Instance> AsMut>> for Can<'d, T> { @@ -245,6 +258,46 @@ impl<'d, T: Instance> AsMut>> for Can<' } } +/// Buffered CAN driver. +pub struct BufferedCan<'d, T: Instance, const TX_BUF_SIZE: usize, const RX_BUF_SIZE: usize> { + tx: BufferedCanTx<'d, T, TX_BUF_SIZE>, + rx: BufferedCanRx<'d, T, RX_BUF_SIZE>, +} + +impl<'d, T: Instance, const TX_BUF_SIZE: usize, const RX_BUF_SIZE: usize> BufferedCan<'d, T, TX_BUF_SIZE, RX_BUF_SIZE> { + /// Async write frame to TX buffer. + pub async fn write(&mut self, frame: &Frame) { + self.tx.write(frame).await + } + + /// Returns a sender that can be used for sending CAN frames. + pub fn writer(&self) -> BufferedCanSender { + self.tx.writer() + } + + /// Async read frame from RX buffer. + pub async fn read(&mut self) -> Result<(Frame, Timestamp), BusError> { + self.rx.read().await + } + + /// Attempts to read a CAN frame without blocking. + /// + /// Returns [Err(TryReadError::Empty)] if there are no frames in the rx queue. + pub fn try_read(&mut self) -> Result { + self.rx.try_read() + } + + /// Waits while receive queue is empty. + pub async fn wait_not_empty(&mut self) { + self.rx.wait_not_empty().await + } + + /// Returns a receiver that can be used for receiving CAN frames. Note, each CAN frame will only be received by one receiver. + pub fn reader(&self) -> BufferedCanReceiver { + self.rx.reader() + } +} + /// CAN driver, transmit half. pub struct CanTx<'d, T: Instance> { tx: crate::can::bx::Tx>, @@ -346,7 +399,7 @@ impl<'d, T: Instance> CanTx<'d, T> { /// User supplied buffer for TX buffering pub type TxBuf = Channel; -/// CAN driver, transmit half. +/// Buffered CAN driver, transmit half. pub struct BufferedCanTx<'d, T: Instance, const TX_BUF_SIZE: usize> { _tx: crate::can::bx::Tx>, tx_buf: &'static TxBuf, From 2217b802781b5f2188b4da659aca47f9d89ee032 Mon Sep 17 00:00:00 2001 From: Corey Schuhen Date: Sun, 24 Mar 2024 15:13:55 +1000 Subject: [PATCH 43/62] CAN: Unify API's between BXCAN and FDCAN. Use Envelope for all read methods instead of a tuple sometimes. --- embassy-stm32/src/can/bx/mod.rs | 5 +- embassy-stm32/src/can/bxcan.rs | 27 ++--- embassy-stm32/src/can/common.rs | 23 ++-- embassy-stm32/src/can/fd/peripheral.rs | 12 +-- embassy-stm32/src/can/fdcan.rs | 140 ++++++++++++++++--------- embassy-stm32/src/can/frame.rs | 60 +++++++++-- examples/stm32f1/Cargo.toml | 1 + examples/stm32f1/src/bin/can.rs | 100 +++++++++++++++--- examples/stm32g4/src/bin/can.rs | 29 ++--- examples/stm32h5/src/bin/can.rs | 12 ++- examples/stm32h7/src/bin/can.rs | 12 ++- tests/stm32/src/bin/fdcan.rs | 24 ++--- 12 files changed, 294 insertions(+), 151 deletions(-) diff --git a/embassy-stm32/src/can/bx/mod.rs b/embassy-stm32/src/can/bx/mod.rs index c508ef2fe..253bcee13 100644 --- a/embassy-stm32/src/can/bx/mod.rs +++ b/embassy-stm32/src/can/bx/mod.rs @@ -40,12 +40,11 @@ pub type Header = crate::can::frame::Header; /// Data for a CAN Frame pub type Data = crate::can::frame::ClassicData; -/// CAN Frame -pub type Frame = crate::can::frame::ClassicFrame; - use crate::can::_version::Envelope; use crate::can::bx::filter::MasterFilters; use crate::can::enums::BusError; +/// CAN Frame +pub use crate::can::frame::Frame; use crate::pac::can::vals::Lec; #[derive(Debug, Copy, Clone, Eq, PartialEq)] diff --git a/embassy-stm32/src/can/bxcan.rs b/embassy-stm32/src/can/bxcan.rs index be2e34963..fd6a79092 100644 --- a/embassy-stm32/src/can/bxcan.rs +++ b/embassy-stm32/src/can/bxcan.rs @@ -19,22 +19,10 @@ use crate::{interrupt, peripherals, Peripheral}; pub mod enums; pub mod frame; pub mod util; +pub use frame::Envelope; mod common; -pub use self::common::{BufferedCanReceiver, BufferedCanSender, Timestamp}; - -/// Contains CAN frame and additional metadata. -/// -/// Timestamp is available if `time` feature is enabled. -#[derive(Debug, Clone)] -#[cfg_attr(feature = "defmt", derive(defmt::Format))] -pub struct Envelope { - /// Reception time. - #[cfg(feature = "time")] - pub ts: embassy_time::Instant, - /// The actual CAN frame. - pub frame: Frame, -} +pub use self::common::{BufferedCanReceiver, BufferedCanSender}; /// Interrupt handler. pub struct TxInterruptHandler { @@ -276,7 +264,7 @@ impl<'d, T: Instance, const TX_BUF_SIZE: usize, const RX_BUF_SIZE: usize> Buffer } /// Async read frame from RX buffer. - pub async fn read(&mut self) -> Result<(Frame, Timestamp), BusError> { + pub async fn read(&mut self) -> Result { self.rx.read().await } @@ -482,8 +470,7 @@ impl<'d, T: Instance> CanRx<'d, T> { } /// User supplied buffer for RX Buffering -pub type RxBuf = - Channel, BUF_SIZE>; +pub type RxBuf = Channel, BUF_SIZE>; /// CAN driver, receive half in Buffered mode. pub struct BufferedCanRx<'d, T: Instance, const RX_BUF_SIZE: usize> { @@ -508,7 +495,7 @@ impl<'d, T: Instance, const RX_BUF_SIZE: usize> BufferedCanRx<'d, T, RX_BUF_SIZE } /// Async read frame from RX buffer. - pub async fn read(&mut self) -> Result<(Frame, Timestamp), BusError> { + pub async fn read(&mut self) -> Result { self.rx_buf.receive().await } @@ -520,7 +507,7 @@ impl<'d, T: Instance, const RX_BUF_SIZE: usize> BufferedCanRx<'d, T, RX_BUF_SIZE RxMode::Buffered(_) => { if let Ok(result) = self.rx_buf.try_receive() { match result { - Ok((frame, ts)) => Ok(Envelope { ts, frame }), + Ok(envelope) => Ok(envelope), Err(e) => Err(TryReadError::BusError(e)), } } else { @@ -610,7 +597,7 @@ impl RxMode { match regsisters.receive_fifo(fifo) { Some(envelope) => { // NOTE: consensus was reached that if rx_queue is full, packets should be dropped - let _ = buf.rx_sender.try_send(Ok((envelope.frame, envelope.ts))); + let _ = buf.rx_sender.try_send(Ok(envelope)); } None => return, }; diff --git a/embassy-stm32/src/can/common.rs b/embassy-stm32/src/can/common.rs index b0d177a6d..570761b19 100644 --- a/embassy-stm32/src/can/common.rs +++ b/embassy-stm32/src/can/common.rs @@ -3,25 +3,17 @@ use embassy_sync::channel::{DynamicReceiver, DynamicSender}; use crate::can::_version::enums::*; use crate::can::_version::frame::*; -/// Timestamp for incoming packets. Use Embassy time when enabled. -#[cfg(feature = "time")] -pub type Timestamp = embassy_time::Instant; - -/// Timestamp for incoming packets. -#[cfg(not(feature = "time"))] -pub type Timestamp = u16; - pub(crate) struct ClassicBufferedRxInner { - pub rx_sender: DynamicSender<'static, Result<(ClassicFrame, Timestamp), BusError>>, + pub rx_sender: DynamicSender<'static, Result>, } pub(crate) struct ClassicBufferedTxInner { - pub tx_receiver: DynamicReceiver<'static, ClassicFrame>, + pub tx_receiver: DynamicReceiver<'static, Frame>, } #[cfg(any(can_fdcan_v1, can_fdcan_h7))] pub(crate) struct FdBufferedRxInner { - pub rx_sender: DynamicSender<'static, Result<(FdFrame, Timestamp), BusError>>, + pub rx_sender: DynamicSender<'static, Result>, } #[cfg(any(can_fdcan_v1, can_fdcan_h7))] @@ -32,20 +24,20 @@ pub(crate) struct FdBufferedTxInner { /// Sender that can be used for sending CAN frames. #[derive(Copy, Clone)] pub struct BufferedCanSender { - pub(crate) tx_buf: embassy_sync::channel::DynamicSender<'static, ClassicFrame>, + pub(crate) tx_buf: embassy_sync::channel::DynamicSender<'static, Frame>, pub(crate) waker: fn(), } impl BufferedCanSender { /// Async write frame to TX buffer. - pub fn try_write(&mut self, frame: ClassicFrame) -> Result<(), embassy_sync::channel::TrySendError> { + pub fn try_write(&mut self, frame: Frame) -> Result<(), embassy_sync::channel::TrySendError> { self.tx_buf.try_send(frame)?; (self.waker)(); Ok(()) } /// Async write frame to TX buffer. - pub async fn write(&mut self, frame: ClassicFrame) { + pub async fn write(&mut self, frame: Frame) { self.tx_buf.send(frame).await; (self.waker)(); } @@ -57,5 +49,4 @@ impl BufferedCanSender { } /// Receiver that can be used for receiving CAN frames. Note, each CAN frame will only be received by one receiver. -pub type BufferedCanReceiver = - embassy_sync::channel::DynamicReceiver<'static, Result<(ClassicFrame, Timestamp), BusError>>; +pub type BufferedCanReceiver = embassy_sync::channel::DynamicReceiver<'static, Result>; diff --git a/embassy-stm32/src/can/fd/peripheral.rs b/embassy-stm32/src/can/fd/peripheral.rs index 76b76afe1..e32f19d91 100644 --- a/embassy-stm32/src/can/fd/peripheral.rs +++ b/embassy-stm32/src/can/fd/peripheral.rs @@ -397,13 +397,13 @@ impl Registers { /// Moves out of ConfigMode and into specified mode #[inline] - pub fn into_mode(mut self, config: FdCanConfig, mode: crate::can::_version::FdcanOperatingMode) { + pub fn into_mode(mut self, config: FdCanConfig, mode: crate::can::_version::OperatingMode) { match mode { - crate::can::FdcanOperatingMode::InternalLoopbackMode => self.set_loopback_mode(LoopbackMode::Internal), - crate::can::FdcanOperatingMode::ExternalLoopbackMode => self.set_loopback_mode(LoopbackMode::External), - crate::can::FdcanOperatingMode::NormalOperationMode => self.set_normal_operations(true), - crate::can::FdcanOperatingMode::RestrictedOperationMode => self.set_restricted_operations(true), - crate::can::FdcanOperatingMode::BusMonitoringMode => self.set_bus_monitoring_mode(true), + crate::can::OperatingMode::InternalLoopbackMode => self.set_loopback_mode(LoopbackMode::Internal), + crate::can::OperatingMode::ExternalLoopbackMode => self.set_loopback_mode(LoopbackMode::External), + crate::can::OperatingMode::NormalOperationMode => self.set_normal_operations(true), + crate::can::OperatingMode::RestrictedOperationMode => self.set_restricted_operations(true), + crate::can::OperatingMode::BusMonitoringMode => self.set_bus_monitoring_mode(true), } self.leave_init_mode(config); } diff --git a/embassy-stm32/src/can/fdcan.rs b/embassy-stm32/src/can/fdcan.rs index e58d8c0ec..2ccf4b093 100644 --- a/embassy-stm32/src/can/fdcan.rs +++ b/embassy-stm32/src/can/fdcan.rs @@ -110,7 +110,7 @@ impl interrupt::typelevel::Handler for IT1Interrup #[derive(Debug, Copy, Clone, Eq, PartialEq)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] /// Different operating modes -pub enum FdcanOperatingMode { +pub enum OperatingMode { //PoweredDownMode, //ConfigMode, /// This mode can be used for a “Hot Selftest”, meaning the FDCAN can be tested without @@ -148,7 +148,7 @@ pub enum FdcanOperatingMode { /// FDCAN Configuration instance instance /// Create instance of this first -pub struct FdcanConfigurator<'d, T: Instance> { +pub struct CanConfigurator<'d, T: Instance> { config: crate::can::fd::config::FdCanConfig, /// Reference to internals. instance: FdcanInstance<'d, T>, @@ -169,7 +169,7 @@ fn calc_ns_per_timer_tick(mode: crate::can::fd::config::FrameTransm } } -impl<'d, T: Instance> FdcanConfigurator<'d, T> { +impl<'d, T: Instance> CanConfigurator<'d, T> { /// Creates a new Fdcan instance, keeping the peripheral in sleep mode. /// You must call [Fdcan::enable_non_blocking] to use the peripheral. pub fn new( @@ -179,7 +179,7 @@ impl<'d, T: Instance> FdcanConfigurator<'d, T> { _irqs: impl interrupt::typelevel::Binding> + interrupt::typelevel::Binding> + 'd, - ) -> FdcanConfigurator<'d, T> { + ) -> CanConfigurator<'d, T> { into_ref!(peri, rx, tx); rx.set_as_af(rx.af_num(), AFType::Input); @@ -273,13 +273,13 @@ impl<'d, T: Instance> FdcanConfigurator<'d, T> { } /// Start in mode. - pub fn start(self, mode: FdcanOperatingMode) -> Fdcan<'d, T> { + pub fn start(self, mode: OperatingMode) -> Can<'d, T> { let ns_per_timer_tick = calc_ns_per_timer_tick::(self.config.frame_transmit); critical_section::with(|_| unsafe { T::mut_state().ns_per_timer_tick = ns_per_timer_tick; }); T::registers().into_mode(self.config, mode); - let ret = Fdcan { + let ret = Can { config: self.config, instance: self.instance, _mode: mode, @@ -288,30 +288,30 @@ impl<'d, T: Instance> FdcanConfigurator<'d, T> { } /// Start, entering mode. Does same as start(mode) - pub fn into_normal_mode(self) -> Fdcan<'d, T> { - self.start(FdcanOperatingMode::NormalOperationMode) + pub fn into_normal_mode(self) -> Can<'d, T> { + self.start(OperatingMode::NormalOperationMode) } /// Start, entering mode. Does same as start(mode) - pub fn into_internal_loopback_mode(self) -> Fdcan<'d, T> { - self.start(FdcanOperatingMode::InternalLoopbackMode) + pub fn into_internal_loopback_mode(self) -> Can<'d, T> { + self.start(OperatingMode::InternalLoopbackMode) } /// Start, entering mode. Does same as start(mode) - pub fn into_external_loopback_mode(self) -> Fdcan<'d, T> { - self.start(FdcanOperatingMode::ExternalLoopbackMode) + pub fn into_external_loopback_mode(self) -> Can<'d, T> { + self.start(OperatingMode::ExternalLoopbackMode) } } /// FDCAN Instance -pub struct Fdcan<'d, T: Instance> { +pub struct Can<'d, T: Instance> { config: crate::can::fd::config::FdCanConfig, /// Reference to internals. instance: FdcanInstance<'d, T>, - _mode: FdcanOperatingMode, + _mode: OperatingMode, } -impl<'d, T: Instance> Fdcan<'d, T> { +impl<'d, T: Instance> Can<'d, T> { /// Flush one of the TX mailboxes. pub async fn flush(&self, idx: usize) { poll_fn(|cx| { @@ -334,12 +334,12 @@ impl<'d, T: Instance> Fdcan<'d, T> { /// frame is dropped from the mailbox, it is returned. If no lower-priority frames /// can be replaced, this call asynchronously waits for a frame to be successfully /// transmitted, then tries again. - pub async fn write(&mut self, frame: &ClassicFrame) -> Option { + pub async fn write(&mut self, frame: &Frame) -> Option { T::state().tx_mode.write::(frame).await } /// Returns the next received message frame - pub async fn read(&mut self) -> Result<(ClassicFrame, Timestamp), BusError> { + pub async fn read(&mut self) -> Result { T::state().rx_mode.read_classic::().await } @@ -352,19 +352,19 @@ impl<'d, T: Instance> Fdcan<'d, T> { } /// Returns the next received message frame - pub async fn read_fd(&mut self) -> Result<(FdFrame, Timestamp), BusError> { + pub async fn read_fd(&mut self) -> Result { T::state().rx_mode.read_fd::().await } /// Split instance into separate Tx(write) and Rx(read) portions - pub fn split(self) -> (FdcanTx<'d, T>, FdcanRx<'d, T>) { + pub fn split(self) -> (CanTx<'d, T>, CanRx<'d, T>) { ( - FdcanTx { + CanTx { config: self.config, _instance: self.instance, _mode: self._mode, }, - FdcanRx { + CanRx { _instance1: PhantomData::, _instance2: T::regs(), _mode: self._mode, @@ -373,8 +373,8 @@ impl<'d, T: Instance> Fdcan<'d, T> { } /// Join split rx and tx portions back together - pub fn join(tx: FdcanTx<'d, T>, rx: FdcanRx<'d, T>) -> Self { - Fdcan { + pub fn join(tx: CanTx<'d, T>, rx: CanRx<'d, T>) -> Self { + Can { config: tx.config, //_instance2: T::regs(), instance: tx._instance, @@ -402,17 +402,16 @@ impl<'d, T: Instance> Fdcan<'d, T> { } /// User supplied buffer for RX Buffering -pub type RxBuf = - Channel, BUF_SIZE>; +pub type RxBuf = Channel, BUF_SIZE>; /// User supplied buffer for TX buffering -pub type TxBuf = Channel; +pub type TxBuf = Channel; /// Buffered FDCAN Instance pub struct BufferedCan<'d, T: Instance, const TX_BUF_SIZE: usize, const RX_BUF_SIZE: usize> { _instance1: PhantomData, _instance2: &'d crate::pac::can::Fdcan, - _mode: FdcanOperatingMode, + _mode: OperatingMode, tx_buf: &'static TxBuf, rx_buf: &'static RxBuf, } @@ -423,7 +422,7 @@ impl<'c, 'd, T: Instance, const TX_BUF_SIZE: usize, const RX_BUF_SIZE: usize> fn new( _instance1: PhantomData, _instance2: &'d crate::pac::can::Fdcan, - _mode: FdcanOperatingMode, + _mode: OperatingMode, tx_buf: &'static TxBuf, rx_buf: &'static RxBuf, ) -> Self { @@ -453,13 +452,13 @@ impl<'c, 'd, T: Instance, const TX_BUF_SIZE: usize, const RX_BUF_SIZE: usize> } /// Async write frame to TX buffer. - pub async fn write(&mut self, frame: ClassicFrame) { + pub async fn write(&mut self, frame: Frame) { self.tx_buf.send(frame).await; T::IT0Interrupt::pend(); // Wake for Tx } /// Async read frame from RX buffer. - pub async fn read(&mut self) -> Result<(ClassicFrame, Timestamp), BusError> { + pub async fn read(&mut self) -> Result { self.rx_buf.receive().await } @@ -489,8 +488,7 @@ impl<'c, 'd, T: Instance, const TX_BUF_SIZE: usize, const RX_BUF_SIZE: usize> Dr } /// User supplied buffer for RX Buffering -pub type RxFdBuf = - Channel, BUF_SIZE>; +pub type RxFdBuf = Channel, BUF_SIZE>; /// User supplied buffer for TX buffering pub type TxFdBuf = Channel; @@ -499,7 +497,7 @@ pub type TxFdBuf = Channel { _instance1: PhantomData, _instance2: &'d crate::pac::can::Fdcan, - _mode: FdcanOperatingMode, + _mode: OperatingMode, tx_buf: &'static TxFdBuf, rx_buf: &'static RxFdBuf, } @@ -532,7 +530,7 @@ impl BufferedFdCanSender { } /// Receiver that can be used for receiving CAN frames. Note, each CAN frame will only be received by one receiver. -pub type BufferedFdCanReceiver = DynamicReceiver<'static, Result<(FdFrame, Timestamp), BusError>>; +pub type BufferedFdCanReceiver = DynamicReceiver<'static, Result>; impl<'c, 'd, T: Instance, const TX_BUF_SIZE: usize, const RX_BUF_SIZE: usize> BufferedCanFd<'d, T, TX_BUF_SIZE, RX_BUF_SIZE> @@ -540,7 +538,7 @@ impl<'c, 'd, T: Instance, const TX_BUF_SIZE: usize, const RX_BUF_SIZE: usize> fn new( _instance1: PhantomData, _instance2: &'d crate::pac::can::Fdcan, - _mode: FdcanOperatingMode, + _mode: OperatingMode, tx_buf: &'static TxFdBuf, rx_buf: &'static RxFdBuf, ) -> Self { @@ -576,7 +574,7 @@ impl<'c, 'd, T: Instance, const TX_BUF_SIZE: usize, const RX_BUF_SIZE: usize> } /// Async read frame from RX buffer. - pub async fn read(&mut self) -> Result<(FdFrame, Timestamp), BusError> { + pub async fn read(&mut self) -> Result { self.rx_buf.receive().await } @@ -606,25 +604,25 @@ impl<'c, 'd, T: Instance, const TX_BUF_SIZE: usize, const RX_BUF_SIZE: usize> Dr } /// FDCAN Rx only Instance -pub struct FdcanRx<'d, T: Instance> { +pub struct CanRx<'d, T: Instance> { _instance1: PhantomData, _instance2: &'d crate::pac::can::Fdcan, - _mode: FdcanOperatingMode, + _mode: OperatingMode, } /// FDCAN Tx only Instance -pub struct FdcanTx<'d, T: Instance> { +pub struct CanTx<'d, T: Instance> { config: crate::can::fd::config::FdCanConfig, _instance: FdcanInstance<'d, T>, //(PeripheralRef<'a, T>); - _mode: FdcanOperatingMode, + _mode: OperatingMode, } -impl<'c, 'd, T: Instance> FdcanTx<'d, T> { +impl<'c, 'd, T: Instance> CanTx<'d, T> { /// Queues the message to be sent but exerts backpressure. If a lower-priority /// frame is dropped from the mailbox, it is returned. If no lower-priority frames /// can be replaced, this call asynchronously waits for a frame to be successfully /// transmitted, then tries again. - pub async fn write(&mut self, frame: &ClassicFrame) -> Option { + pub async fn write(&mut self, frame: &Frame) -> Option { T::state().tx_mode.write::(frame).await } @@ -637,14 +635,14 @@ impl<'c, 'd, T: Instance> FdcanTx<'d, T> { } } -impl<'c, 'd, T: Instance> FdcanRx<'d, T> { +impl<'c, 'd, T: Instance> CanRx<'d, T> { /// Returns the next received message frame - pub async fn read(&mut self) -> Result<(ClassicFrame, Timestamp), BusError> { + pub async fn read(&mut self) -> Result { T::state().rx_mode.read_classic::().await } /// Returns the next received message frame - pub async fn read_fd(&mut self) -> Result<(FdFrame, Timestamp), BusError> { + pub async fn read_fd(&mut self) -> Result { T::state().rx_mode.read_fd::().await } } @@ -672,18 +670,50 @@ impl RxMode { waker.wake(); } RxMode::ClassicBuffered(buf) => { - if let Some(result) = self.read::() { + if let Some(result) = self.try_read::() { let _ = buf.rx_sender.try_send(result); } } RxMode::FdBuffered(buf) => { - if let Some(result) = self.read::() { + if let Some(result) = self.try_read_fd::() { let _ = buf.rx_sender.try_send(result); } } } } + //async fn read_classic(&self) -> Result { + fn try_read(&self) -> Option> { + if let Some((frame, ts)) = T::registers().read(0) { + let ts = T::calc_timestamp(T::state().ns_per_timer_tick, ts); + Some(Ok(Envelope { ts, frame })) + } else if let Some((frame, ts)) = T::registers().read(1) { + let ts = T::calc_timestamp(T::state().ns_per_timer_tick, ts); + Some(Ok(Envelope { ts, frame })) + } else if let Some(err) = T::registers().curr_error() { + // TODO: this is probably wrong + Some(Err(err)) + } else { + None + } + } + + //async fn read_classic(&self) -> Result { + fn try_read_fd(&self) -> Option> { + if let Some((frame, ts)) = T::registers().read(0) { + let ts = T::calc_timestamp(T::state().ns_per_timer_tick, ts); + Some(Ok(FdEnvelope { ts, frame })) + } else if let Some((frame, ts)) = T::registers().read(1) { + let ts = T::calc_timestamp(T::state().ns_per_timer_tick, ts); + Some(Ok(FdEnvelope { ts, frame })) + } else if let Some(err) = T::registers().curr_error() { + // TODO: this is probably wrong + Some(Err(err)) + } else { + None + } + } + fn read(&self) -> Option> { if let Some((msg, ts)) = T::registers().read(0) { let ts = T::calc_timestamp(T::state().ns_per_timer_tick, ts); @@ -711,12 +741,18 @@ impl RxMode { .await } - async fn read_classic(&self) -> Result<(ClassicFrame, Timestamp), BusError> { - self.read_async::().await + async fn read_classic(&self) -> Result { + match self.read_async::().await { + Ok((frame, ts)) => Ok(Envelope { ts, frame }), + Err(e) => Err(e), + } } - async fn read_fd(&self) -> Result<(FdFrame, Timestamp), BusError> { - self.read_async::().await + async fn read_fd(&self) -> Result { + match self.read_async::().await { + Ok((frame, ts)) => Ok(FdEnvelope { ts, frame }), + Err(e) => Err(e), + } } } @@ -761,7 +797,7 @@ impl TxMode { /// frame is dropped from the mailbox, it is returned. If no lower-priority frames /// can be replaced, this call asynchronously waits for a frame to be successfully /// transmitted, then tries again. - async fn write(&self, frame: &ClassicFrame) -> Option { + async fn write(&self, frame: &Frame) -> Option { self.write_generic::(frame).await } diff --git a/embassy-stm32/src/can/frame.rs b/embassy-stm32/src/can/frame.rs index 14fc32c51..fb032aee2 100644 --- a/embassy-stm32/src/can/frame.rs +++ b/embassy-stm32/src/can/frame.rs @@ -136,19 +136,20 @@ impl ClassicData { } } -/// Frame with up to 8 bytes of data payload as per Classic CAN +/// Frame with up to 8 bytes of data payload as per Classic(non-FD) CAN +/// For CAN-FD support use FdFrame #[derive(Debug, Copy, Clone)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] -pub struct ClassicFrame { +pub struct Frame { can_header: Header, data: ClassicData, } -impl ClassicFrame { +impl Frame { /// Create a new CAN classic Frame pub fn new(can_header: Header, raw_data: &[u8]) -> Result { let data = ClassicData::new(raw_data)?; - Ok(ClassicFrame { can_header, data: data }) + Ok(Frame { can_header, data: data }) } /// Creates a new data frame. @@ -206,9 +207,9 @@ impl ClassicFrame { } } -impl embedded_can::Frame for ClassicFrame { +impl embedded_can::Frame for Frame { fn new(id: impl Into, raw_data: &[u8]) -> Option { - let frameopt = ClassicFrame::new(Header::new(id.into(), raw_data.len() as u8, false), raw_data); + let frameopt = Frame::new(Header::new(id.into(), raw_data.len() as u8, false), raw_data); match frameopt { Ok(frame) => Some(frame), Err(_) => None, @@ -216,7 +217,7 @@ impl embedded_can::Frame for ClassicFrame { } fn new_remote(id: impl Into, len: usize) -> Option { if len <= 8 { - let frameopt = ClassicFrame::new(Header::new(id.into(), len as u8, true), &[0; 8]); + let frameopt = Frame::new(Header::new(id.into(), len as u8, true), &[0; 8]); match frameopt { Ok(frame) => Some(frame), Err(_) => None, @@ -245,7 +246,7 @@ impl embedded_can::Frame for ClassicFrame { } } -impl CanHeader for ClassicFrame { +impl CanHeader for Frame { fn from_header(header: Header, data: &[u8]) -> Result { Self::new(header, data) } @@ -255,10 +256,32 @@ impl CanHeader for ClassicFrame { } } +/// Contains CAN frame and additional metadata. +/// +/// Timestamp is available if `time` feature is enabled. +/// For CAN-FD support use FdEnvelope +#[derive(Debug, Clone)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +pub struct Envelope { + /// Reception time. + #[cfg(feature = "time")] + pub ts: embassy_time::Instant, + /// The actual CAN frame. + pub frame: Frame, +} + +impl Envelope { + /// Convert into a tuple + pub fn parts(self) -> (Frame, embassy_time::Instant) { + (self.frame, self.ts) + } +} + /// Payload of a (FD)CAN data frame. /// /// Contains 0 to 64 Bytes of data. #[derive(Debug, Copy, Clone)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct FdData { pub(crate) bytes: [u8; 64], } @@ -308,6 +331,7 @@ impl FdData { /// Frame with up to 8 bytes of data payload as per Fd CAN #[derive(Debug, Copy, Clone)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct FdFrame { can_header: Header, data: FdData, @@ -410,3 +434,23 @@ impl CanHeader for FdFrame { self.header() } } + +/// Contains CAN FD frame and additional metadata. +/// +/// Timestamp is available if `time` feature is enabled. +#[derive(Debug, Clone)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +pub struct FdEnvelope { + /// Reception time. + #[cfg(feature = "time")] + pub ts: embassy_time::Instant, + /// The actual CAN frame. + pub frame: FdFrame, +} + +impl FdEnvelope { + /// Convert into a tuple + pub fn parts(self) -> (FdFrame, embassy_time::Instant) { + (self.frame, self.ts) + } +} diff --git a/examples/stm32f1/Cargo.toml b/examples/stm32f1/Cargo.toml index df5d32f70..4f282f326 100644 --- a/examples/stm32f1/Cargo.toml +++ b/examples/stm32f1/Cargo.toml @@ -23,6 +23,7 @@ panic-probe = { version = "0.3", features = ["print-defmt"] } futures = { version = "0.3.17", default-features = false, features = ["async-await"] } heapless = { version = "0.8", default-features = false } nb = "1.0.0" +static_cell = "2.0.0" [profile.dev] opt-level = "s" diff --git a/examples/stm32f1/src/bin/can.rs b/examples/stm32f1/src/bin/can.rs index ac337e8a0..90cb9e46b 100644 --- a/examples/stm32f1/src/bin/can.rs +++ b/examples/stm32f1/src/bin/can.rs @@ -4,11 +4,12 @@ use defmt::*; use embassy_executor::Spawner; use embassy_stm32::can::{ - filter, Can, Fifo, Frame, Id, Rx0InterruptHandler, Rx1InterruptHandler, SceInterruptHandler, StandardId, + filter, Can, Envelope, Fifo, Frame, Id, Rx0InterruptHandler, Rx1InterruptHandler, SceInterruptHandler, StandardId, TxInterruptHandler, }; use embassy_stm32::peripherals::CAN; use embassy_stm32::{bind_interrupts, Config}; +use static_cell::StaticCell; use {defmt_rtt as _, panic_probe as _}; bind_interrupts!(struct Irqs { @@ -21,6 +22,27 @@ bind_interrupts!(struct Irqs { // This example is configured to work with real CAN transceivers on B8/B9. // See other examples for loopback. +fn handle_frame(env: Envelope, read_mode: &str) { + match env.frame.id() { + Id::Extended(id) => { + defmt::println!( + "{} Extended Frame id={:x} {:02x}", + read_mode, + id.as_raw(), + env.frame.data() + ); + } + Id::Standard(id) => { + defmt::println!( + "{} Standard Frame id={:x} {:02x}", + read_mode, + id.as_raw(), + env.frame.data() + ); + } + } +} + #[embassy_executor::main] async fn main(_spawner: Spawner) { let p = embassy_stm32::init(Config::default()); @@ -28,6 +50,9 @@ async fn main(_spawner: Spawner) { // Set alternate pin mapping to B8/B9 embassy_stm32::pac::AFIO.mapr().modify(|w| w.set_can1_remap(2)); + static RX_BUF: StaticCell> = StaticCell::new(); + static TX_BUF: StaticCell> = StaticCell::new(); + let mut can = Can::new(p.CAN, p.PB8, p.PB9, Irqs); can.as_mut() @@ -43,21 +68,72 @@ async fn main(_spawner: Spawner) { can.set_bitrate(250_000); can.enable().await; - let mut i: u8 = 0; + + /* + // Example for using buffered Tx and Rx without needing to + // split first as is done below. + let mut can = can.buffered( + TX_BUF.init(embassy_stm32::can::TxBuf::<10>::new()), + RX_BUF.init(embassy_stm32::can::RxBuf::<10>::new())); + loop { + let tx_frame = Frame::new_data(unwrap!(StandardId::new(i as _)), &[i, 0, 1, 2, 3, 4, 5, 6]).unwrap(); + can.write(&tx_frame).await; + + match can.read().await { + Ok((frame, ts)) => { + handle_frame(Envelope { ts, frame }, "Buf"); + } + Err(err) => { + defmt::println!("Error {}", err); + } + } + i += 1; + } + + */ + let (mut tx, mut rx) = can.split(); + + // This example shows using the wait_not_empty API before try read + while i < 3 { + let tx_frame = Frame::new_data(unwrap!(StandardId::new(i as _)), &[i, 0, 1, 2, 3, 4, 5, 6]).unwrap(); + tx.write(&tx_frame).await; + + rx.wait_not_empty().await; + let env = rx.try_read().unwrap(); + handle_frame(env, "Wait"); + i += 1; + } + + // This example shows using the full async non-buffered API + while i < 6 { + let tx_frame = Frame::new_data(unwrap!(StandardId::new(i as _)), &[i, 0, 1, 2, 3, 4, 5, 6]).unwrap(); + tx.write(&tx_frame).await; + + match rx.read().await { + Ok(env) => { + handle_frame(env, "NoBuf"); + } + Err(err) => { + defmt::println!("Error {}", err); + } + } + i += 1; + } + + // This example shows using buffered RX and TX. User passes in desired buffer (size) + // It's possible this way to have just RX or TX buffered. + let mut rx = rx.buffered(RX_BUF.init(embassy_stm32::can::RxBuf::<10>::new())); + let mut tx = tx.buffered(TX_BUF.init(embassy_stm32::can::TxBuf::<10>::new())); + loop { let tx_frame = Frame::new_data(unwrap!(StandardId::new(i as _)), &[i, 0, 1, 2, 3, 4, 5, 6]).unwrap(); - can.write(&tx_frame).await; + tx.write(&tx_frame).await; - match can.read().await { - Ok(env) => match env.frame.id() { - Id::Extended(id) => { - defmt::println!("Extended Frame id={:x} {:02x}", id.as_raw(), env.frame.data()); - } - Id::Standard(id) => { - defmt::println!("Standard Frame id={:x} {:02x}", id.as_raw(), env.frame.data()); - } - }, + match rx.read().await { + Ok(envelope) => { + handle_frame(envelope, "Buf"); + } Err(err) => { defmt::println!("Error {}", err); } diff --git a/examples/stm32g4/src/bin/can.rs b/examples/stm32g4/src/bin/can.rs index 4373a89a8..2ed632a93 100644 --- a/examples/stm32g4/src/bin/can.rs +++ b/examples/stm32g4/src/bin/can.rs @@ -36,7 +36,7 @@ async fn main(_spawner: Spawner) { } let peripherals = embassy_stm32::init(config); - let mut can = can::FdcanConfigurator::new(peripherals.FDCAN1, peripherals.PA11, peripherals.PA12, Irqs); + let mut can = can::CanConfigurator::new(peripherals.FDCAN1, peripherals.PA11, peripherals.PA12, Irqs); can.set_extended_filter( can::filter::ExtendedFilterSlot::_0, @@ -56,21 +56,22 @@ async fn main(_spawner: Spawner) { info!("Configured"); let mut can = can.start(match use_fd { - true => can::FdcanOperatingMode::InternalLoopbackMode, - false => can::FdcanOperatingMode::NormalOperationMode, + true => can::OperatingMode::InternalLoopbackMode, + false => can::OperatingMode::NormalOperationMode, }); let mut i = 0; let mut last_read_ts = embassy_time::Instant::now(); loop { - let frame = can::frame::ClassicFrame::new_extended(0x123456F, &[i; 8]).unwrap(); + let frame = can::frame::Frame::new_extended(0x123456F, &[i; 8]).unwrap(); info!("Writing frame"); _ = can.write(&frame).await; match can.read().await { - Ok((rx_frame, ts)) => { + Ok(envelope) => { + let (ts, rx_frame) = (envelope.ts, envelope.frame); let delta = (ts - last_read_ts).as_millis(); last_read_ts = ts; info!( @@ -105,7 +106,8 @@ async fn main(_spawner: Spawner) { } match can.read_fd().await { - Ok((rx_frame, ts)) => { + Ok(envelope) => { + let (ts, rx_frame) = (envelope.ts, envelope.frame); let delta = (ts - last_read_ts).as_millis(); last_read_ts = ts; info!( @@ -129,12 +131,13 @@ async fn main(_spawner: Spawner) { let (mut tx, mut rx) = can.split(); // With split loop { - let frame = can::frame::ClassicFrame::new_extended(0x123456F, &[i; 8]).unwrap(); + let frame = can::frame::Frame::new_extended(0x123456F, &[i; 8]).unwrap(); info!("Writing frame"); _ = tx.write(&frame).await; match rx.read().await { - Ok((rx_frame, ts)) => { + Ok(envelope) => { + let (ts, rx_frame) = (envelope.ts, envelope.frame); let delta = (ts - last_read_ts).as_millis(); last_read_ts = ts; info!( @@ -156,7 +159,7 @@ async fn main(_spawner: Spawner) { } } - let can = can::Fdcan::join(tx, rx); + let can = can::Can::join(tx, rx); info!("\n\n\nBuffered\n"); if use_fd { @@ -173,7 +176,8 @@ async fn main(_spawner: Spawner) { _ = can.write(frame).await; match can.read().await { - Ok((rx_frame, ts)) => { + Ok(envelope) => { + let (ts, rx_frame) = (envelope.ts, envelope.frame); let delta = (ts - last_read_ts).as_millis(); last_read_ts = ts; info!( @@ -198,7 +202,7 @@ async fn main(_spawner: Spawner) { RX_BUF.init(can::RxBuf::<10>::new()), ); loop { - let frame = can::frame::ClassicFrame::new_extended(0x123456F, &[i; 8]).unwrap(); + let frame = can::frame::Frame::new_extended(0x123456F, &[i; 8]).unwrap(); info!("Writing frame"); // You can use any of these approaches to send. The writer makes it @@ -208,7 +212,8 @@ async fn main(_spawner: Spawner) { can.writer().write(frame).await; match can.read().await { - Ok((rx_frame, ts)) => { + Ok(envelope) => { + let (ts, rx_frame) = (envelope.ts, envelope.frame); let delta = (ts - last_read_ts).as_millis(); last_read_ts = ts; info!( diff --git a/examples/stm32h5/src/bin/can.rs b/examples/stm32h5/src/bin/can.rs index 643df27f9..dd625c90a 100644 --- a/examples/stm32h5/src/bin/can.rs +++ b/examples/stm32h5/src/bin/can.rs @@ -24,7 +24,7 @@ async fn main(_spawner: Spawner) { let peripherals = embassy_stm32::init(config); - let mut can = can::FdcanConfigurator::new(peripherals.FDCAN1, peripherals.PA11, peripherals.PA12, Irqs); + let mut can = can::CanConfigurator::new(peripherals.FDCAN1, peripherals.PA11, peripherals.PA12, Irqs); // 250k bps can.set_bitrate(250_000); @@ -38,12 +38,13 @@ async fn main(_spawner: Spawner) { let mut last_read_ts = embassy_time::Instant::now(); loop { - let frame = can::frame::ClassicFrame::new_extended(0x123456F, &[i; 8]).unwrap(); + let frame = can::frame::Frame::new_extended(0x123456F, &[i; 8]).unwrap(); info!("Writing frame"); _ = can.write(&frame).await; match can.read().await { - Ok((rx_frame, ts)) => { + Ok(envelope) => { + let (rx_frame, ts) = envelope.parts(); let delta = (ts - last_read_ts).as_millis(); last_read_ts = ts; info!( @@ -69,12 +70,13 @@ async fn main(_spawner: Spawner) { let (mut tx, mut rx) = can.split(); // With split loop { - let frame = can::frame::ClassicFrame::new_extended(0x123456F, &[i; 8]).unwrap(); + let frame = can::frame::Frame::new_extended(0x123456F, &[i; 8]).unwrap(); info!("Writing frame"); _ = tx.write(&frame).await; match rx.read().await { - Ok((rx_frame, ts)) => { + Ok(envelope) => { + let (rx_frame, ts) = envelope.parts(); let delta = (ts - last_read_ts).as_millis(); last_read_ts = ts; info!( diff --git a/examples/stm32h7/src/bin/can.rs b/examples/stm32h7/src/bin/can.rs index 13a6a5051..22cb27481 100644 --- a/examples/stm32h7/src/bin/can.rs +++ b/examples/stm32h7/src/bin/can.rs @@ -24,7 +24,7 @@ async fn main(_spawner: Spawner) { let peripherals = embassy_stm32::init(config); - let mut can = can::FdcanConfigurator::new(peripherals.FDCAN1, peripherals.PA11, peripherals.PA12, Irqs); + let mut can = can::CanConfigurator::new(peripherals.FDCAN1, peripherals.PA11, peripherals.PA12, Irqs); // 250k bps can.set_bitrate(250_000); @@ -38,12 +38,13 @@ async fn main(_spawner: Spawner) { let mut last_read_ts = embassy_time::Instant::now(); loop { - let frame = can::frame::ClassicFrame::new_extended(0x123456F, &[i; 8]).unwrap(); + let frame = can::frame::Frame::new_extended(0x123456F, &[i; 8]).unwrap(); info!("Writing frame"); _ = can.write(&frame).await; match can.read().await { - Ok((rx_frame, ts)) => { + Ok(envelope) => { + let (rx_frame, ts) = envelope.parts(); let delta = (ts - last_read_ts).as_millis(); last_read_ts = ts; info!( @@ -69,12 +70,13 @@ async fn main(_spawner: Spawner) { let (mut tx, mut rx) = can.split(); // With split loop { - let frame = can::frame::ClassicFrame::new_extended(0x123456F, &[i; 8]).unwrap(); + let frame = can::frame::Frame::new_extended(0x123456F, &[i; 8]).unwrap(); info!("Writing frame"); _ = tx.write(&frame).await; match rx.read().await { - Ok((rx_frame, ts)) => { + Ok(envelope) => { + let (rx_frame, ts) = envelope.parts(); let delta = (ts - last_read_ts).as_millis(); last_read_ts = ts; info!( diff --git a/tests/stm32/src/bin/fdcan.rs b/tests/stm32/src/bin/fdcan.rs index c7373e294..bddfa7684 100644 --- a/tests/stm32/src/bin/fdcan.rs +++ b/tests/stm32/src/bin/fdcan.rs @@ -79,8 +79,8 @@ async fn main(_spawner: Spawner) { let options = options(); let peripherals = embassy_stm32::init(options.config); - let mut can = can::FdcanConfigurator::new(peripherals.FDCAN1, peripherals.PB8, peripherals.PB9, Irqs1); - let mut can2 = can::FdcanConfigurator::new(peripherals.FDCAN2, peripherals.PB12, peripherals.PB13, Irqs2); + let mut can = can::CanConfigurator::new(peripherals.FDCAN1, peripherals.PB8, peripherals.PB9, Irqs1); + let mut can2 = can::CanConfigurator::new(peripherals.FDCAN2, peripherals.PB12, peripherals.PB13, Irqs2); // 250k bps can.set_bitrate(250_000); @@ -102,13 +102,13 @@ async fn main(_spawner: Spawner) { let mut i: u8 = 0; loop { - let tx_frame = can::frame::ClassicFrame::new_standard(0x123, &[i; 1]).unwrap(); + let tx_frame = can::frame::Frame::new_standard(0x123, &[i; 1]).unwrap(); info!("Transmitting frame..."); let tx_ts = Instant::now(); can.write(&tx_frame).await; - let (frame, timestamp) = can.read().await.unwrap(); + let (frame, timestamp) = can.read().await.unwrap().parts(); info!("Frame received!"); // Check data. @@ -139,13 +139,13 @@ async fn main(_spawner: Spawner) { let mut i: u8 = 0; loop { - let tx_frame = can::frame::ClassicFrame::new_standard(0x123, &[i; 1]).unwrap(); + let tx_frame = can::frame::Frame::new_standard(0x123, &[i; 1]).unwrap(); info!("Transmitting frame..."); let tx_ts = Instant::now(); can2.write(&tx_frame).await; - let (frame, timestamp) = can2.read().await.unwrap(); + let (frame, timestamp) = can2.read().await.unwrap().parts(); info!("Frame received!"); //print_regs().await; @@ -182,20 +182,20 @@ async fn main(_spawner: Spawner) { // in each FIFO so make sure we write enough to fill them both up before reading. for i in 0..3 { // Try filling up the RX FIFO0 buffers with standard packets - let tx_frame = can::frame::ClassicFrame::new_standard(0x123, &[i; 1]).unwrap(); + let tx_frame = can::frame::Frame::new_standard(0x123, &[i; 1]).unwrap(); info!("Transmitting frame {}", i); can.write(&tx_frame).await; } for i in 3..max_buffered { // Try filling up the RX FIFO0 buffers with extended packets - let tx_frame = can::frame::ClassicFrame::new_extended(0x1232344, &[i; 1]).unwrap(); + let tx_frame = can::frame::Frame::new_extended(0x1232344, &[i; 1]).unwrap(); info!("Transmitting frame {}", i); can.write(&tx_frame).await; } // Try and receive all 6 packets for i in 0..max_buffered { - let (frame, _ts) = can.read().await.unwrap(); + let (frame, _ts) = can.read().await.unwrap().parts(); match frame.id() { embedded_can::Id::Extended(id) => { info!("Extended received! {:x} {} {}", id.as_raw(), frame.data()[0], i); @@ -210,20 +210,20 @@ async fn main(_spawner: Spawner) { let (mut tx, mut rx) = can.split(); for i in 0..3 { // Try filling up the RX FIFO0 buffers with standard packets - let tx_frame = can::frame::ClassicFrame::new_standard(0x123, &[i; 1]).unwrap(); + let tx_frame = can::frame::Frame::new_standard(0x123, &[i; 1]).unwrap(); info!("Transmitting frame {}", i); tx.write(&tx_frame).await; } for i in 3..max_buffered { // Try filling up the RX FIFO0 buffers with extended packets - let tx_frame = can::frame::ClassicFrame::new_extended(0x1232344, &[i; 1]).unwrap(); + let tx_frame = can::frame::Frame::new_extended(0x1232344, &[i; 1]).unwrap(); info!("Transmitting frame {}", i); tx.write(&tx_frame).await; } // Try and receive all 6 packets for i in 0..max_buffered { - let (frame, _ts) = rx.read().await.unwrap(); + let (frame, _ts) = rx.read().await.unwrap().parts(); match frame.id() { embedded_can::Id::Extended(id) => { info!("Extended received! {:x} {} {}", id.as_raw(), frame.data()[0], i); From 8d43fb4da4712dd9bb5a2ae343168e536c2b3129 Mon Sep 17 00:00:00 2001 From: Corey Schuhen Date: Wed, 27 Mar 2024 19:43:19 +1000 Subject: [PATCH 44/62] CAN: Use the same testing code for BXCAN and FDCAN h/w. --- tests/stm32/src/bin/can.rs | 50 +++----- tests/stm32/src/bin/can_common.rs | 112 +++++++++++++++++ tests/stm32/src/bin/fdcan.rs | 195 ++++++------------------------ 3 files changed, 163 insertions(+), 194 deletions(-) create mode 100644 tests/stm32/src/bin/can_common.rs diff --git a/tests/stm32/src/bin/can.rs b/tests/stm32/src/bin/can.rs index c08c69a3b..74d84c42f 100644 --- a/tests/stm32/src/bin/can.rs +++ b/tests/stm32/src/bin/can.rs @@ -6,17 +6,19 @@ #[path = "../common.rs"] mod common; use common::*; -use defmt::assert; use embassy_executor::Spawner; use embassy_stm32::bind_interrupts; use embassy_stm32::can::bx::filter::Mask32; -use embassy_stm32::can::bx::{Fifo, Frame, StandardId}; +use embassy_stm32::can::bx::Fifo; use embassy_stm32::can::{Can, Rx0InterruptHandler, Rx1InterruptHandler, SceInterruptHandler, TxInterruptHandler}; use embassy_stm32::gpio::{Input, Pull}; use embassy_stm32::peripherals::CAN1; -use embassy_time::{Duration, Instant}; +use embassy_time::Duration; use {defmt_rtt as _, panic_probe as _}; +mod can_common; +use can_common::*; + bind_interrupts!(struct Irqs { CAN1_RX0 => Rx0InterruptHandler; CAN1_RX1 => Rx1InterruptHandler; @@ -29,6 +31,11 @@ async fn main(_spawner: Spawner) { let p = embassy_stm32::init(config()); info!("Hello World!"); + let options = TestOptions { + max_latency: Duration::from_micros(1200), + max_buffered: 2, + }; + let can = peri!(p, CAN); let tx = peri!(p, CAN_TX); let mut rx = peri!(p, CAN_RX); @@ -58,40 +65,13 @@ async fn main(_spawner: Spawner) { info!("Can configured"); - let mut i: u8 = 0; - loop { - let tx_frame = Frame::new_data(unwrap!(StandardId::new(i as _)), &[i]).unwrap(); + run_can_tests(&mut can, &options).await; - info!("Transmitting frame..."); - let tx_ts = Instant::now(); - can.write(&tx_frame).await; - - let envelope = can.read().await.unwrap(); - info!("Frame received!"); - - info!("loopback time {}", envelope.ts); - info!("loopback frame {=u8}", envelope.frame.data()[0]); - - let latency = envelope.ts.saturating_duration_since(tx_ts); - info!("loopback latency {} us", latency.as_micros()); - - // Theoretical minimum latency is 55us, actual is usually ~80us - const MIN_LATENCY: Duration = Duration::from_micros(50); - const MAX_LATENCY: Duration = Duration::from_micros(150); - assert!( - MIN_LATENCY <= latency && latency <= MAX_LATENCY, - "{} <= {} <= {}", - MIN_LATENCY, - latency, - MAX_LATENCY - ); - - i += 1; - if i > 10 { - break; - } - } + // Test again with a split + let (mut tx, mut rx) = can.split(); + run_split_can_tests(&mut tx, &mut rx, &options).await; info!("Test OK"); + cortex_m::asm::bkpt(); } diff --git a/tests/stm32/src/bin/can_common.rs b/tests/stm32/src/bin/can_common.rs new file mode 100644 index 000000000..4b39269cc --- /dev/null +++ b/tests/stm32/src/bin/can_common.rs @@ -0,0 +1,112 @@ +use defmt::{assert, *}; +use embassy_stm32::can; +use embassy_time::{Duration, Instant}; + +#[derive(Clone, Copy, Debug)] +pub struct TestOptions { + pub max_latency: Duration, + pub max_buffered: u8, +} + +pub async fn run_can_tests<'d, T: can::Instance>(can: &mut can::Can<'d, T>, options: &TestOptions) { + let mut i: u8 = 0; + loop { + //let tx_frame = can::frame::Frame::new_standard(0x123, &[i, 0x12 as u8, 0x34 as u8, 0x56 as u8, 0x78 as u8, 0x9A as u8, 0xBC as u8 ]).unwrap(); + let tx_frame = can::frame::Frame::new_standard(0x123, &[i; 1]).unwrap(); + + //info!("Transmitting frame..."); + let tx_ts = Instant::now(); + can.write(&tx_frame).await; + + let (frame, timestamp) = can.read().await.unwrap().parts(); + //info!("Frame received!"); + + // Check data. + assert!(i == frame.data()[0], "{} == {}", i, frame.data()[0]); + + //info!("loopback time {}", timestamp); + //info!("loopback frame {=u8}", frame.data()[0]); + let latency = timestamp.saturating_duration_since(tx_ts); + info!("loopback latency {} us", latency.as_micros()); + + // Theoretical minimum latency is 55us, actual is usually ~80us + const MIN_LATENCY: Duration = Duration::from_micros(50); + // Was failing at 150 but we are not getting a real time stamp. I'm not + // sure if there are other delays + assert!( + MIN_LATENCY <= latency && latency <= options.max_latency, + "{} <= {} <= {}", + MIN_LATENCY, + latency, + options.max_latency + ); + + i += 1; + if i > 5 { + break; + } + } + + // Below here, check that we can receive from both FIFO0 and FIFO1 + // Above we configured FIFO1 for extended ID packets. There are only 3 slots + // in each FIFO so make sure we write enough to fill them both up before reading. + for i in 0..options.max_buffered { + // Try filling up the RX FIFO0 buffers + //let tx_frame = if 0 != (i & 0x01) { + let tx_frame = if i < options.max_buffered / 2 { + info!("Transmitting standard frame {}", i); + can::frame::Frame::new_standard(0x123, &[i; 1]).unwrap() + } else { + info!("Transmitting extended frame {}", i); + can::frame::Frame::new_extended(0x1232344, &[i; 1]).unwrap() + }; + can.write(&tx_frame).await; + } + + // Try and receive all 6 packets + for _i in 0..options.max_buffered { + let (frame, _ts) = can.read().await.unwrap().parts(); + match frame.id() { + embedded_can::Id::Extended(_id) => { + info!("Extended received! {}", frame.data()[0]); + //info!("Extended received! {:x} {} {}", id.as_raw(), frame.data()[0], i); + } + embedded_can::Id::Standard(_id) => { + info!("Standard received! {}", frame.data()[0]); + //info!("Standard received! {:x} {} {}", id.as_raw(), frame.data()[0], i); + } + } + } +} + +pub async fn run_split_can_tests<'d, T: can::Instance>( + tx: &mut can::CanTx<'d, T>, + rx: &mut can::CanRx<'d, T>, + options: &TestOptions, +) { + for i in 0..options.max_buffered { + // Try filling up the RX FIFO0 buffers + //let tx_frame = if 0 != (i & 0x01) { + let tx_frame = if i < options.max_buffered / 2 { + info!("Transmitting standard frame {}", i); + can::frame::Frame::new_standard(0x123, &[i; 1]).unwrap() + } else { + info!("Transmitting extended frame {}", i); + can::frame::Frame::new_extended(0x1232344, &[i; 1]).unwrap() + }; + tx.write(&tx_frame).await; + } + + // Try and receive all 6 packets + for _i in 0..options.max_buffered { + let (frame, _ts) = rx.read().await.unwrap().parts(); + match frame.id() { + embedded_can::Id::Extended(_id) => { + info!("Extended received! {}", frame.data()[0]); + } + embedded_can::Id::Standard(_id) => { + info!("Standard received! {}", frame.data()[0]); + } + } + } +} diff --git a/tests/stm32/src/bin/fdcan.rs b/tests/stm32/src/bin/fdcan.rs index bddfa7684..27bdd038a 100644 --- a/tests/stm32/src/bin/fdcan.rs +++ b/tests/stm32/src/bin/fdcan.rs @@ -6,13 +6,15 @@ #[path = "../common.rs"] mod common; use common::*; -use defmt::assert; use embassy_executor::Spawner; use embassy_stm32::peripherals::*; use embassy_stm32::{bind_interrupts, can, Config}; -use embassy_time::{Duration, Instant}; +use embassy_time::Duration; use {defmt_rtt as _, panic_probe as _}; +mod can_common; +use can_common::*; + bind_interrupts!(struct Irqs2 { FDCAN2_IT0 => can::IT0InterruptHandler; FDCAN2_IT1 => can::IT1InterruptHandler; @@ -22,14 +24,8 @@ bind_interrupts!(struct Irqs1 { FDCAN1_IT1 => can::IT1InterruptHandler; }); -struct TestOptions { - config: Config, - max_latency: Duration, - second_fifo_working: bool, -} - #[cfg(any(feature = "stm32h755zi", feature = "stm32h753zi", feature = "stm32h563zi"))] -fn options() -> TestOptions { +fn options() -> (Config, TestOptions) { use embassy_stm32::rcc; info!("H75 config"); let mut c = config(); @@ -38,15 +34,17 @@ fn options() -> TestOptions { mode: rcc::HseMode::Oscillator, }); c.rcc.mux.fdcansel = rcc::mux::Fdcansel::HSE; - TestOptions { - config: c, - max_latency: Duration::from_micros(1200), - second_fifo_working: false, - } + ( + c, + TestOptions { + max_latency: Duration::from_micros(1200), + max_buffered: 3, + }, + ) } #[cfg(any(feature = "stm32h7a3zi"))] -fn options() -> TestOptions { +fn options() -> (Config, TestOptions) { use embassy_stm32::rcc; info!("H7a config"); let mut c = config(); @@ -55,29 +53,33 @@ fn options() -> TestOptions { mode: rcc::HseMode::Oscillator, }); c.rcc.mux.fdcansel = rcc::mux::Fdcansel::HSE; - TestOptions { - config: c, - max_latency: Duration::from_micros(1200), - second_fifo_working: false, - } + ( + c, + TestOptions { + max_latency: Duration::from_micros(1200), + max_buffered: 3, + }, + ) } #[cfg(any(feature = "stm32g491re", feature = "stm32g431cb"))] -fn options() -> TestOptions { +fn options() -> (Config, TestOptions) { info!("G4 config"); - TestOptions { - config: config(), - max_latency: Duration::from_micros(500), - second_fifo_working: true, - } + ( + config(), + TestOptions { + max_latency: Duration::from_micros(500), + max_buffered: 6, + }, + ) } #[embassy_executor::main] async fn main(_spawner: Spawner) { //let peripherals = embassy_stm32::init(config()); - let options = options(); - let peripherals = embassy_stm32::init(options.config); + let (config, options) = options(); + let peripherals = embassy_stm32::init(config); let mut can = can::CanConfigurator::new(peripherals.FDCAN1, peripherals.PB8, peripherals.PB9, Irqs1); let mut can2 = can::CanConfigurator::new(peripherals.FDCAN2, peripherals.PB12, peripherals.PB13, Irqs2); @@ -98,141 +100,16 @@ async fn main(_spawner: Spawner) { let mut can = can.into_internal_loopback_mode(); let mut can2 = can2.into_internal_loopback_mode(); + run_can_tests(&mut can, &options).await; + run_can_tests(&mut can2, &options).await; + info!("CAN Configured"); - let mut i: u8 = 0; - loop { - let tx_frame = can::frame::Frame::new_standard(0x123, &[i; 1]).unwrap(); - - info!("Transmitting frame..."); - let tx_ts = Instant::now(); - can.write(&tx_frame).await; - - let (frame, timestamp) = can.read().await.unwrap().parts(); - info!("Frame received!"); - - // Check data. - assert!(i == frame.data()[0], "{} == {}", i, frame.data()[0]); - - info!("loopback time {}", timestamp); - info!("loopback frame {=u8}", frame.data()[0]); - let latency = timestamp.saturating_duration_since(tx_ts); - info!("loopback latency {} us", latency.as_micros()); - - // Theoretical minimum latency is 55us, actual is usually ~80us - const MIN_LATENCY: Duration = Duration::from_micros(50); - // Was failing at 150 but we are not getting a real time stamp. I'm not - // sure if there are other delays - assert!( - MIN_LATENCY <= latency && latency <= options.max_latency, - "{} <= {} <= {}", - MIN_LATENCY, - latency, - options.max_latency - ); - - i += 1; - if i > 10 { - break; - } - } - - let mut i: u8 = 0; - loop { - let tx_frame = can::frame::Frame::new_standard(0x123, &[i; 1]).unwrap(); - - info!("Transmitting frame..."); - let tx_ts = Instant::now(); - can2.write(&tx_frame).await; - - let (frame, timestamp) = can2.read().await.unwrap().parts(); - info!("Frame received!"); - - //print_regs().await; - // Check data. - assert!(i == frame.data()[0], "{} == {}", i, frame.data()[0]); - - info!("loopback time {}", timestamp); - info!("loopback frame {=u8}", frame.data()[0]); - let latency = timestamp.saturating_duration_since(tx_ts); - info!("loopback latency {} us", latency.as_micros()); - - // Theoretical minimum latency is 55us, actual is usually ~80us - const MIN_LATENCY: Duration = Duration::from_micros(50); - // Was failing at 150 but we are not getting a real time stamp. I'm not - // sure if there are other delays - assert!( - MIN_LATENCY <= latency && latency <= options.max_latency, - "{} <= {} <= {}", - MIN_LATENCY, - latency, - options.max_latency - ); - - i += 1; - if i > 10 { - break; - } - } - - let max_buffered = if options.second_fifo_working { 6 } else { 3 }; - - // Below here, check that we can receive from both FIFO0 and FIFO0 - // Above we configured FIFO1 for extended ID packets. There are only 3 slots - // in each FIFO so make sure we write enough to fill them both up before reading. - for i in 0..3 { - // Try filling up the RX FIFO0 buffers with standard packets - let tx_frame = can::frame::Frame::new_standard(0x123, &[i; 1]).unwrap(); - info!("Transmitting frame {}", i); - can.write(&tx_frame).await; - } - for i in 3..max_buffered { - // Try filling up the RX FIFO0 buffers with extended packets - let tx_frame = can::frame::Frame::new_extended(0x1232344, &[i; 1]).unwrap(); - info!("Transmitting frame {}", i); - can.write(&tx_frame).await; - } - - // Try and receive all 6 packets - for i in 0..max_buffered { - let (frame, _ts) = can.read().await.unwrap().parts(); - match frame.id() { - embedded_can::Id::Extended(id) => { - info!("Extended received! {:x} {} {}", id.as_raw(), frame.data()[0], i); - } - embedded_can::Id::Standard(id) => { - info!("Standard received! {:x} {} {}", id.as_raw(), frame.data()[0], i); - } - } - } - // Test again with a split let (mut tx, mut rx) = can.split(); - for i in 0..3 { - // Try filling up the RX FIFO0 buffers with standard packets - let tx_frame = can::frame::Frame::new_standard(0x123, &[i; 1]).unwrap(); - info!("Transmitting frame {}", i); - tx.write(&tx_frame).await; - } - for i in 3..max_buffered { - // Try filling up the RX FIFO0 buffers with extended packets - let tx_frame = can::frame::Frame::new_extended(0x1232344, &[i; 1]).unwrap(); - info!("Transmitting frame {}", i); - tx.write(&tx_frame).await; - } - - // Try and receive all 6 packets - for i in 0..max_buffered { - let (frame, _ts) = rx.read().await.unwrap().parts(); - match frame.id() { - embedded_can::Id::Extended(id) => { - info!("Extended received! {:x} {} {}", id.as_raw(), frame.data()[0], i); - } - embedded_can::Id::Standard(id) => { - info!("Standard received! {:x} {} {}", id.as_raw(), frame.data()[0], i); - } - } - } + let (mut tx2, mut rx2) = can2.split(); + run_split_can_tests(&mut tx, &mut rx, &options).await; + run_split_can_tests(&mut tx2, &mut rx2, &options).await; info!("Test OK"); cortex_m::asm::bkpt(); From a9f0c8c3a941684c1a610a72dfb2b925535fc8cd Mon Sep 17 00:00:00 2001 From: Corey Schuhen Date: Wed, 27 Mar 2024 20:29:28 +1000 Subject: [PATCH 45/62] Fixes for no-time. --- embassy-stm32/src/can/bx/mod.rs | 13 +++++++------ embassy-stm32/src/can/frame.rs | 19 +++++++++++++------ 2 files changed, 20 insertions(+), 12 deletions(-) diff --git a/embassy-stm32/src/can/bx/mod.rs b/embassy-stm32/src/can/bx/mod.rs index 253bcee13..cb83799d3 100644 --- a/embassy-stm32/src/can/bx/mod.rs +++ b/embassy-stm32/src/can/bx/mod.rs @@ -622,17 +622,18 @@ impl Registers { let id = (stid << 18) | (exid); embedded_can::ExtendedId::new(id).unwrap().into() }; - let data_len = fifo.rdtr().read().dlc(); + let rdtr = fifo.rdtr().read(); + let data_len = rdtr.dlc(); + + #[cfg(not(feature = "time"))] + let ts = rdtr.time(); + let mut data: [u8; 8] = [0; 8]; data[0..4].copy_from_slice(&fifo.rdlr().read().0.to_ne_bytes()); data[4..8].copy_from_slice(&fifo.rdhr().read().0.to_ne_bytes()); let frame = Frame::new(Header::new(id, data_len, false), &data).unwrap(); - let envelope = Envelope { - #[cfg(feature = "time")] - ts, - frame, - }; + let envelope = Envelope { ts, frame }; rfr.modify(|v| v.set_rfom(true)); diff --git a/embassy-stm32/src/can/frame.rs b/embassy-stm32/src/can/frame.rs index fb032aee2..d2d1f7aa6 100644 --- a/embassy-stm32/src/can/frame.rs +++ b/embassy-stm32/src/can/frame.rs @@ -3,6 +3,14 @@ use bit_field::BitField; use crate::can::enums::FrameCreateError; +/// Calculate proper timestamp when available. +#[cfg(feature = "time")] +pub type Timestamp = embassy_time::Instant; + +/// Raw register timestamp +#[cfg(not(feature = "time"))] +pub type Timestamp = u16; + /// CAN Header, without meta data #[derive(Debug, Copy, Clone)] pub struct Header { @@ -264,15 +272,14 @@ impl CanHeader for Frame { #[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct Envelope { /// Reception time. - #[cfg(feature = "time")] - pub ts: embassy_time::Instant, + pub ts: Timestamp, /// The actual CAN frame. pub frame: Frame, } impl Envelope { /// Convert into a tuple - pub fn parts(self) -> (Frame, embassy_time::Instant) { + pub fn parts(self) -> (Frame, Timestamp) { (self.frame, self.ts) } } @@ -442,15 +449,15 @@ impl CanHeader for FdFrame { #[cfg_attr(feature = "defmt", derive(defmt::Format))] pub struct FdEnvelope { /// Reception time. - #[cfg(feature = "time")] - pub ts: embassy_time::Instant, + pub ts: Timestamp, + /// The actual CAN frame. pub frame: FdFrame, } impl FdEnvelope { /// Convert into a tuple - pub fn parts(self) -> (FdFrame, embassy_time::Instant) { + pub fn parts(self) -> (FdFrame, Timestamp) { (self.frame, self.ts) } } From 25618cd93d5209d864fa250b2077a59532e0bedf Mon Sep 17 00:00:00 2001 From: Corey Schuhen Date: Thu, 28 Mar 2024 09:47:16 +1000 Subject: [PATCH 46/62] RTR fix. --- embassy-stm32/src/can/bx/mod.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/embassy-stm32/src/can/bx/mod.rs b/embassy-stm32/src/can/bx/mod.rs index cb83799d3..cd82148ba 100644 --- a/embassy-stm32/src/can/bx/mod.rs +++ b/embassy-stm32/src/can/bx/mod.rs @@ -624,6 +624,7 @@ impl Registers { }; let rdtr = fifo.rdtr().read(); let data_len = rdtr.dlc(); + let rtr = rir.rtr() == stm32_metapac::can::vals::Rtr::REMOTE; #[cfg(not(feature = "time"))] let ts = rdtr.time(); @@ -632,7 +633,7 @@ impl Registers { data[0..4].copy_from_slice(&fifo.rdlr().read().0.to_ne_bytes()); data[4..8].copy_from_slice(&fifo.rdhr().read().0.to_ne_bytes()); - let frame = Frame::new(Header::new(id, data_len, false), &data).unwrap(); + let frame = Frame::new(Header::new(id, data_len, rtr), &data).unwrap(); let envelope = Envelope { ts, frame }; rfr.modify(|v| v.set_rfom(true)); From 2ea1040e073a54d2a97f75c7d68d0d9a230d15c8 Mon Sep 17 00:00:00 2001 From: Frostie314159 Date: Fri, 22 Mar 2024 08:50:56 +0100 Subject: [PATCH 47/62] Adjusted behavior. --- embassy-time/src/timer.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/embassy-time/src/timer.rs b/embassy-time/src/timer.rs index 763bfdeeb..d6d0a46e2 100644 --- a/embassy-time/src/timer.rs +++ b/embassy-time/src/timer.rs @@ -190,10 +190,10 @@ impl Ticker { self.expires_at = Instant::now() + self.duration; } - /// Reset the ticker to fire for the next time on the deadline. + /// Reset the ticker at the deadline. /// If the deadline is in the past, the ticker will fire instantly. pub fn reset_at(&mut self, deadline: Instant) { - self.expires_at = deadline; + self.expires_at = deadline + self.duration; } /// Resets the ticker, after the specified duration has passed. From a38cbbdc5974fe31f70bffb4fd1995391e4ff331 Mon Sep 17 00:00:00 2001 From: Alexandru Radovici Date: Sat, 30 Mar 2024 22:36:30 +0200 Subject: [PATCH 48/62] fix typo --- embassy-sync/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/embassy-sync/README.md b/embassy-sync/README.md index c2e13799e..2c1c0cf68 100644 --- a/embassy-sync/README.md +++ b/embassy-sync/README.md @@ -5,7 +5,7 @@ An [Embassy](https://embassy.dev) project. Synchronization primitives and data structures with async support: - [`Channel`](channel::Channel) - A Multiple Producer Multiple Consumer (MPMC) channel. Each message is only received by a single consumer. -- [`PriorityChannel`](channel::priority::PriorityChannel) - A Multiple Producer Multiple Consumer (MPMC) channel. Each message is only received by a single consumer. Higher priority items are sifted to the front of the channel. +- [`PriorityChannel`](channel::priority::PriorityChannel) - A Multiple Producer Multiple Consumer (MPMC) channel. Each message is only received by a single consumer. Higher priority items are shifted to the front of the channel. - [`PubSubChannel`](pubsub::PubSubChannel) - A broadcast channel (publish-subscribe) channel. Each message is received by all consumers. - [`Signal`](signal::Signal) - Signalling latest value to a single consumer. - [`Mutex`](mutex::Mutex) - Mutex for synchronizing state between asynchronous tasks. From f8a6007e1cb3fe9b13db4a56872228155ed8f1cb Mon Sep 17 00:00:00 2001 From: Alex Moon Date: Sat, 30 Mar 2024 22:19:55 -0400 Subject: [PATCH 49/62] Semaphore synchronization primitive This provides both a "greedy" and "fair" async semaphore implementation. --- embassy-sync/src/lib.rs | 1 + embassy-sync/src/semaphore.rs | 704 ++++++++++++++++++++++++++++++++++ 2 files changed, 705 insertions(+) create mode 100644 embassy-sync/src/semaphore.rs diff --git a/embassy-sync/src/lib.rs b/embassy-sync/src/lib.rs index 61b173e80..1873483f9 100644 --- a/embassy-sync/src/lib.rs +++ b/embassy-sync/src/lib.rs @@ -17,6 +17,7 @@ pub mod once_lock; pub mod pipe; pub mod priority_channel; pub mod pubsub; +pub mod semaphore; pub mod signal; pub mod waitqueue; pub mod zerocopy_channel; diff --git a/embassy-sync/src/semaphore.rs b/embassy-sync/src/semaphore.rs new file mode 100644 index 000000000..52c468b4a --- /dev/null +++ b/embassy-sync/src/semaphore.rs @@ -0,0 +1,704 @@ +//! A synchronization primitive for controlling access to a pool of resources. +use core::cell::{Cell, RefCell}; +use core::convert::Infallible; +use core::future::poll_fn; +use core::mem::MaybeUninit; +use core::task::{Poll, Waker}; + +use heapless::Deque; + +use crate::blocking_mutex::raw::RawMutex; +use crate::blocking_mutex::Mutex; +use crate::waitqueue::WakerRegistration; + +/// An asynchronous semaphore. +/// +/// A semaphore tracks a number of permits, typically representing a pool of shared resources. +/// Users can acquire permits to synchronize access to those resources. The semaphore does not +/// contain the resources themselves, only the count of available permits. +pub trait Semaphore: Sized { + /// The error returned when the semaphore is unable to acquire the requested permits. + type Error; + + /// Asynchronously acquire one or more permits from the semaphore. + async fn acquire(&self, permits: usize) -> Result, Self::Error>; + + /// Try to immediately acquire one or more permits from the semaphore. + fn try_acquire(&self, permits: usize) -> Option>; + + /// Asynchronously acquire all permits controlled by the semaphore. + /// + /// This method will wait until at least `min` permits are available, then acquire all available permits + /// from the semaphore. Note that other tasks may have already acquired some permits which could be released + /// back to the semaphore at any time. The number of permits actually acquired may be determined by calling + /// [`SemaphoreReleaser::permits`]. + async fn acquire_all(&self, min: usize) -> Result, Self::Error>; + + /// Try to immediately acquire all available permits from the semaphore, if at least `min` permits are available. + fn try_acquire_all(&self, min: usize) -> Option>; + + /// Release `permits` back to the semaphore, making them available to be acquired. + fn release(&self, permits: usize); + + /// Reset the number of available permints in the semaphore to `permits`. + fn set(&self, permits: usize); +} + +/// A representation of a number of acquired permits. +/// +/// The acquired permits will be released back to the [`Semaphore`] when this is dropped. +pub struct SemaphoreReleaser<'a, S: Semaphore> { + semaphore: &'a S, + permits: usize, +} + +impl<'a, S: Semaphore> Drop for SemaphoreReleaser<'a, S> { + fn drop(&mut self) { + self.semaphore.release(self.permits); + } +} + +impl<'a, S: Semaphore> SemaphoreReleaser<'a, S> { + /// The number of acquired permits. + pub fn permits(&self) -> usize { + self.permits + } + + /// Prevent the acquired permits from being released on drop. + /// + /// Returns the number of acquired permits. + pub fn disarm(self) -> usize { + let permits = self.permits; + core::mem::forget(self); + permits + } +} + +/// A greedy [`Semaphore`] implementation. +/// +/// Tasks can acquire permits as soon as they become available, even if another task +/// is waiting on a larger number of permits. +pub struct GreedySemaphore { + state: Mutex>, +} + +impl Default for GreedySemaphore { + fn default() -> Self { + Self::new(0) + } +} + +impl GreedySemaphore { + /// Create a new `Semaphore`. + pub const fn new(permits: usize) -> Self { + Self { + state: Mutex::new(Cell::new(SemaphoreState { + permits, + waker: WakerRegistration::new(), + })), + } + } + + #[cfg(test)] + fn permits(&self) -> usize { + self.state.lock(|cell| { + let state = cell.replace(SemaphoreState::EMPTY); + let permits = state.permits; + cell.replace(state); + permits + }) + } + + fn poll_acquire( + &self, + permits: usize, + acquire_all: bool, + waker: Option<&Waker>, + ) -> Poll, Infallible>> { + self.state.lock(|cell| { + let mut state = cell.replace(SemaphoreState::EMPTY); + if let Some(permits) = state.take(permits, acquire_all) { + cell.set(state); + Poll::Ready(Ok(SemaphoreReleaser { + semaphore: self, + permits, + })) + } else { + if let Some(waker) = waker { + state.register(waker); + } + cell.set(state); + Poll::Pending + } + }) + } +} + +impl Semaphore for GreedySemaphore { + type Error = Infallible; + + async fn acquire(&self, permits: usize) -> Result, Self::Error> { + poll_fn(|cx| self.poll_acquire(permits, false, Some(cx.waker()))).await + } + + fn try_acquire(&self, permits: usize) -> Option> { + match self.poll_acquire(permits, false, None) { + Poll::Ready(Ok(n)) => Some(n), + _ => None, + } + } + + async fn acquire_all(&self, min: usize) -> Result, Self::Error> { + poll_fn(|cx| self.poll_acquire(min, true, Some(cx.waker()))).await + } + + fn try_acquire_all(&self, min: usize) -> Option> { + match self.poll_acquire(min, true, None) { + Poll::Ready(Ok(n)) => Some(n), + _ => None, + } + } + + fn release(&self, permits: usize) { + if permits > 0 { + self.state.lock(|cell| { + let mut state = cell.replace(SemaphoreState::EMPTY); + state.permits += permits; + state.wake(); + cell.set(state); + }); + } + } + + fn set(&self, permits: usize) { + self.state.lock(|cell| { + let mut state = cell.replace(SemaphoreState::EMPTY); + if permits > state.permits { + state.wake(); + } + state.permits = permits; + cell.set(state); + }); + } +} + +struct SemaphoreState { + permits: usize, + waker: WakerRegistration, +} + +impl SemaphoreState { + const EMPTY: SemaphoreState = SemaphoreState { + permits: 0, + waker: WakerRegistration::new(), + }; + + fn register(&mut self, w: &Waker) { + self.waker.register(w); + } + + fn take(&mut self, mut permits: usize, acquire_all: bool) -> Option { + if self.permits < permits { + None + } else { + if acquire_all { + permits = self.permits; + } + self.permits -= permits; + Some(permits) + } + } + + fn wake(&mut self) { + self.waker.wake(); + } +} + +/// A fair [`Semaphore`] implementation. +/// +/// Tasks are allowed to acquire permits in FIFO order. A task waiting to acquire +/// a large number of permits will prevent other tasks from acquiring any permits +/// until its request is satisfied. +/// +/// Up to `N` tasks may attempt to acquire permits concurrently. If additional +/// tasks attempt to acquire a permit, a [`WaitQueueFull`] error will be returned. +pub struct FairSemaphore +where + M: RawMutex, +{ + state: Mutex>>, +} + +impl Default for FairSemaphore +where + M: RawMutex, +{ + fn default() -> Self { + Self::new(0) + } +} + +impl FairSemaphore +where + M: RawMutex, +{ + /// Create a new `FairSemaphore`. + pub const fn new(permits: usize) -> Self { + Self { + state: Mutex::new(RefCell::new(FairSemaphoreState::new(permits))), + } + } + + #[cfg(test)] + fn permits(&self) -> usize { + self.state.lock(|cell| cell.borrow().permits) + } + + fn poll_acquire( + &self, + permits: usize, + acquire_all: bool, + cx: Option<(&Cell>, &Waker)>, + ) -> Poll, WaitQueueFull>> { + let ticket = cx.as_ref().map(|(cell, _)| cell.get()).unwrap_or(None); + self.state.lock(|cell| { + let mut state = cell.borrow_mut(); + if let Some(permits) = state.take(ticket, permits, acquire_all) { + Poll::Ready(Ok(SemaphoreReleaser { + semaphore: self, + permits, + })) + } else if let Some((cell, waker)) = cx { + match state.register(ticket, waker) { + Ok(ticket) => { + cell.set(Some(ticket)); + Poll::Pending + } + Err(err) => Poll::Ready(Err(err)), + } + } else { + Poll::Pending + } + }) + } +} + +/// An error indicating the [`FairSemaphore`]'s wait queue is full. +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +pub struct WaitQueueFull; + +impl Semaphore for FairSemaphore { + type Error = WaitQueueFull; + + async fn acquire(&self, permits: usize) -> Result, Self::Error> { + let ticket = Cell::new(None); + let _guard = OnDrop::new(|| self.state.lock(|cell| cell.borrow_mut().cancel(ticket.get()))); + poll_fn(|cx| self.poll_acquire(permits, false, Some((&ticket, cx.waker())))).await + } + + fn try_acquire(&self, permits: usize) -> Option> { + match self.poll_acquire(permits, false, None) { + Poll::Ready(Ok(x)) => Some(x), + _ => None, + } + } + + async fn acquire_all(&self, min: usize) -> Result, Self::Error> { + let ticket = Cell::new(None); + let _guard = OnDrop::new(|| self.state.lock(|cell| cell.borrow_mut().cancel(ticket.get()))); + poll_fn(|cx| self.poll_acquire(min, true, Some((&ticket, cx.waker())))).await + } + + fn try_acquire_all(&self, min: usize) -> Option> { + match self.poll_acquire(min, true, None) { + Poll::Ready(Ok(x)) => Some(x), + _ => None, + } + } + + fn release(&self, permits: usize) { + if permits > 0 { + self.state.lock(|cell| { + let mut state = cell.borrow_mut(); + state.permits += permits; + state.wake(); + }); + } + } + + fn set(&self, permits: usize) { + self.state.lock(|cell| { + let mut state = cell.borrow_mut(); + if permits > state.permits { + state.wake(); + } + state.permits = permits; + }); + } +} + +struct FairSemaphoreState { + permits: usize, + next_ticket: usize, + wakers: Deque, N>, +} + +impl FairSemaphoreState { + /// Create a new empty instance + const fn new(permits: usize) -> Self { + Self { + permits, + next_ticket: 0, + wakers: Deque::new(), + } + } + + /// Register a waker. If the queue is full the function returns an error + fn register(&mut self, ticket: Option, w: &Waker) -> Result { + self.pop_canceled(); + + match ticket { + None => { + let ticket = self.next_ticket.wrapping_add(self.wakers.len()); + self.wakers.push_back(Some(w.clone())).or(Err(WaitQueueFull))?; + Ok(ticket) + } + Some(ticket) => { + self.set_waker(ticket, Some(w.clone())); + Ok(ticket) + } + } + } + + fn cancel(&mut self, ticket: Option) { + if let Some(ticket) = ticket { + self.set_waker(ticket, None); + } + } + + fn set_waker(&mut self, ticket: usize, waker: Option) { + let i = ticket.wrapping_sub(self.next_ticket); + if i < self.wakers.len() { + let (a, b) = self.wakers.as_mut_slices(); + let x = if i < a.len() { &mut a[i] } else { &mut b[i - a.len()] }; + *x = waker; + } + } + + fn take(&mut self, ticket: Option, mut permits: usize, acquire_all: bool) -> Option { + self.pop_canceled(); + + if permits > self.permits { + return None; + } + + match ticket { + Some(n) if n != self.next_ticket => return None, + None if !self.wakers.is_empty() => return None, + _ => (), + } + + if acquire_all { + permits = self.permits; + } + self.permits -= permits; + + if ticket.is_some() { + self.pop(); + } + + Some(permits) + } + + fn pop_canceled(&mut self) { + while let Some(None) = self.wakers.front() { + self.pop(); + } + } + + /// Panics if `self.wakers` is empty + fn pop(&mut self) { + self.wakers.pop_front().unwrap(); + self.next_ticket = self.next_ticket.wrapping_add(1); + } + + fn wake(&mut self) { + self.pop_canceled(); + + if let Some(Some(waker)) = self.wakers.front() { + waker.wake_by_ref(); + } + } +} + +/// A type to delay the drop handler invocation. +#[must_use = "to delay the drop handler invocation to the end of the scope"] +struct OnDrop { + f: MaybeUninit, +} + +impl OnDrop { + /// Create a new instance. + pub fn new(f: F) -> Self { + Self { f: MaybeUninit::new(f) } + } +} + +impl Drop for OnDrop { + fn drop(&mut self) { + unsafe { self.f.as_ptr().read()() } + } +} + +#[cfg(test)] +mod tests { + mod greedy { + use core::pin::pin; + + use futures_util::poll; + + use super::super::*; + use crate::blocking_mutex::raw::NoopRawMutex; + + #[test] + fn try_acquire() { + let semaphore = GreedySemaphore::::new(3); + + let a = semaphore.try_acquire(1).unwrap(); + assert_eq!(a.permits(), 1); + assert_eq!(semaphore.permits(), 2); + + core::mem::drop(a); + assert_eq!(semaphore.permits(), 3); + } + + #[test] + fn disarm() { + let semaphore = GreedySemaphore::::new(3); + + let a = semaphore.try_acquire(1).unwrap(); + assert_eq!(a.disarm(), 1); + assert_eq!(semaphore.permits(), 2); + } + + #[futures_test::test] + async fn acquire() { + let semaphore = GreedySemaphore::::new(3); + + let a = semaphore.acquire(1).await.unwrap(); + assert_eq!(a.permits(), 1); + assert_eq!(semaphore.permits(), 2); + + core::mem::drop(a); + assert_eq!(semaphore.permits(), 3); + } + + #[test] + fn try_acquire_all() { + let semaphore = GreedySemaphore::::new(3); + + let a = semaphore.try_acquire_all(1).unwrap(); + assert_eq!(a.permits(), 3); + assert_eq!(semaphore.permits(), 0); + } + + #[futures_test::test] + async fn acquire_all() { + let semaphore = GreedySemaphore::::new(3); + + let a = semaphore.acquire_all(1).await.unwrap(); + assert_eq!(a.permits(), 3); + assert_eq!(semaphore.permits(), 0); + } + + #[test] + fn release() { + let semaphore = GreedySemaphore::::new(3); + assert_eq!(semaphore.permits(), 3); + semaphore.release(2); + assert_eq!(semaphore.permits(), 5); + } + + #[test] + fn set() { + let semaphore = GreedySemaphore::::new(3); + assert_eq!(semaphore.permits(), 3); + semaphore.set(2); + assert_eq!(semaphore.permits(), 2); + } + + #[test] + fn contested() { + let semaphore = GreedySemaphore::::new(3); + + let a = semaphore.try_acquire(1).unwrap(); + let b = semaphore.try_acquire(3); + assert!(b.is_none()); + + core::mem::drop(a); + + let b = semaphore.try_acquire(3); + assert!(b.is_some()); + } + + #[futures_test::test] + async fn greedy() { + let semaphore = GreedySemaphore::::new(3); + + let a = semaphore.try_acquire(1).unwrap(); + + let b_fut = semaphore.acquire(3); + let mut b_fut = pin!(b_fut); + let b = poll!(b_fut.as_mut()); + assert!(b.is_pending()); + + // Succeed even through `b` is waiting + let c = semaphore.try_acquire(1); + assert!(c.is_some()); + + let b = poll!(b_fut.as_mut()); + assert!(b.is_pending()); + + core::mem::drop(a); + + let b = poll!(b_fut.as_mut()); + assert!(b.is_pending()); + + core::mem::drop(c); + + let b = poll!(b_fut.as_mut()); + assert!(b.is_ready()); + } + } + + mod fair { + use core::pin::pin; + + use futures_util::poll; + + use super::super::*; + use crate::blocking_mutex::raw::NoopRawMutex; + + #[test] + fn try_acquire() { + let semaphore = FairSemaphore::::new(3); + + let a = semaphore.try_acquire(1).unwrap(); + assert_eq!(a.permits(), 1); + assert_eq!(semaphore.permits(), 2); + + core::mem::drop(a); + assert_eq!(semaphore.permits(), 3); + } + + #[test] + fn disarm() { + let semaphore = FairSemaphore::::new(3); + + let a = semaphore.try_acquire(1).unwrap(); + assert_eq!(a.disarm(), 1); + assert_eq!(semaphore.permits(), 2); + } + + #[futures_test::test] + async fn acquire() { + let semaphore = FairSemaphore::::new(3); + + let a = semaphore.acquire(1).await.unwrap(); + assert_eq!(a.permits(), 1); + assert_eq!(semaphore.permits(), 2); + + core::mem::drop(a); + assert_eq!(semaphore.permits(), 3); + } + + #[test] + fn try_acquire_all() { + let semaphore = FairSemaphore::::new(3); + + let a = semaphore.try_acquire_all(1).unwrap(); + assert_eq!(a.permits(), 3); + assert_eq!(semaphore.permits(), 0); + } + + #[futures_test::test] + async fn acquire_all() { + let semaphore = FairSemaphore::::new(3); + + let a = semaphore.acquire_all(1).await.unwrap(); + assert_eq!(a.permits(), 3); + assert_eq!(semaphore.permits(), 0); + } + + #[test] + fn release() { + let semaphore = FairSemaphore::::new(3); + assert_eq!(semaphore.permits(), 3); + semaphore.release(2); + assert_eq!(semaphore.permits(), 5); + } + + #[test] + fn set() { + let semaphore = FairSemaphore::::new(3); + assert_eq!(semaphore.permits(), 3); + semaphore.set(2); + assert_eq!(semaphore.permits(), 2); + } + + #[test] + fn contested() { + let semaphore = FairSemaphore::::new(3); + + let a = semaphore.try_acquire(1).unwrap(); + let b = semaphore.try_acquire(3); + assert!(b.is_none()); + + core::mem::drop(a); + + let b = semaphore.try_acquire(3); + assert!(b.is_some()); + } + + #[futures_test::test] + async fn fairness() { + let semaphore = FairSemaphore::::new(3); + + let a = semaphore.try_acquire(1); + assert!(a.is_some()); + + let b_fut = semaphore.acquire(3); + let mut b_fut = pin!(b_fut); + let b = poll!(b_fut.as_mut()); // Poll `b_fut` once so it is registered + assert!(b.is_pending()); + + let c = semaphore.try_acquire(1); + assert!(c.is_none()); + + let c_fut = semaphore.acquire(1); + let mut c_fut = pin!(c_fut); + let c = poll!(c_fut.as_mut()); // Poll `c_fut` once so it is registered + assert!(c.is_pending()); // `c` is blocked behind `b` + + let d = semaphore.acquire(1).await; + assert!(matches!(d, Err(WaitQueueFull))); + + core::mem::drop(a); + + let c = poll!(c_fut.as_mut()); + assert!(c.is_pending()); // `c` is still blocked behind `b` + + let b = poll!(b_fut.as_mut()); + assert!(b.is_ready()); + + let c = poll!(c_fut.as_mut()); + assert!(c.is_pending()); // `c` is still blocked behind `b` + + core::mem::drop(b); + + let c = poll!(c_fut.as_mut()); + assert!(c.is_ready()); + } + } +} From a2f9aa592ec61beb247065003016515f0d423c13 Mon Sep 17 00:00:00 2001 From: Frostie314159 Date: Sun, 31 Mar 2024 15:07:01 +0200 Subject: [PATCH 50/62] Made Ticker::next Send+Sync. --- embassy-time/src/timer.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/embassy-time/src/timer.rs b/embassy-time/src/timer.rs index 574d715da..a123c1d01 100644 --- a/embassy-time/src/timer.rs +++ b/embassy-time/src/timer.rs @@ -185,7 +185,7 @@ impl Ticker { } /// Waits for the next tick. - pub fn next(&mut self) -> impl Future + '_ { + pub fn next(&mut self) -> impl Future + Send + Sync + '_ { poll_fn(|cx| { if self.expires_at <= Instant::now() { let dur = self.duration; From cb01d0383523af4b9f80c4bdc4410791eba1173e Mon Sep 17 00:00:00 2001 From: Tyler Gilbert Date: Sun, 31 Mar 2024 16:31:47 -0500 Subject: [PATCH 51/62] Add async stop() function to stm32 bdma_dma --- embassy-stm32/src/dma/dma_bdma.rs | 63 ++++++++++++++++++++++++++++++- 1 file changed, 62 insertions(+), 1 deletion(-) diff --git a/embassy-stm32/src/dma/dma_bdma.rs b/embassy-stm32/src/dma/dma_bdma.rs index 7b5b3cf58..a6344cf06 100644 --- a/embassy-stm32/src/dma/dma_bdma.rs +++ b/embassy-stm32/src/dma/dma_bdma.rs @@ -1,4 +1,4 @@ -use core::future::Future; +use core::future::{poll_fn, Future}; use core::pin::Pin; use core::sync::atomic::{fence, AtomicUsize, Ordering}; use core::task::{Context, Poll, Waker}; @@ -510,6 +510,31 @@ impl AnyChannel { DmaInfo::Bdma(r) => r.ch(info.num).ndtr().read().ndt(), } } + + fn disable_circular_mode(&self) { + let info = self.info(); + match self.info().dma { + #[cfg(dma)] + DmaInfo::Dma(regs) => regs.st(info.num).cr().modify(|w| { + w.set_circ(false); + }), + #[cfg(bdma)] + DmaInfo::Bdma(regs) => regs.ch(info.num).cr().modify(|w| { + w.set_circ(false); + }), + } + } + + fn poll_stop(&self) -> Poll<()> { + use core::sync::atomic::compiler_fence; + compiler_fence(Ordering::SeqCst); + + if !self.is_running() { + Poll::Ready(()) + } else { + Poll::Pending + } + } } /// DMA transfer. @@ -829,6 +854,25 @@ impl<'a, W: Word> ReadableRingBuffer<'a, W> { pub fn is_running(&mut self) -> bool { self.channel.is_running() } + + /// Stop the DMA transfer and await until the buffer is full. + /// + /// This disables the DMA transfer's circular mode so that the transfer + /// stops when the buffer is full. + /// + /// This is designed to be used with streaming input data such as the + /// I2S/SAI or ADC. + /// + /// When using the UART, you probably want `request_stop()`. + pub async fn stop(&mut self) { + self.channel.disable_circular_mode(); + //wait until cr.susp reads as true + poll_fn(|cx| { + self.set_waker(cx.waker()); + self.channel.poll_stop() + }) + .await + } } impl<'a, W: Word> Drop for ReadableRingBuffer<'a, W> { @@ -940,6 +984,23 @@ impl<'a, W: Word> WritableRingBuffer<'a, W> { pub fn is_running(&mut self) -> bool { self.channel.is_running() } + + /// Stop the DMA transfer and await until the buffer is empty. + /// + /// This disables the DMA transfer's circular mode so that the transfer + /// stops when all available data has been written. + /// + /// This is designed to be used with streaming output data such as the + /// I2S/SAI or DAC. + pub async fn stop(&mut self) { + self.channel.disable_circular_mode(); + //wait until cr.susp reads as true + poll_fn(|cx| { + self.set_waker(cx.waker()); + self.channel.poll_stop() + }) + .await + } } impl<'a, W: Word> Drop for WritableRingBuffer<'a, W> { From c8936edb6c13eb099dfb31a4a51be2dd3bb4661e Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Tue, 2 Apr 2024 01:29:52 +0200 Subject: [PATCH 52/62] stm32/can: simplify bxcan api, merging bx::* into the main structs. The bx::* separate structs (Can, Rx, Tx) and separate `Instance` trait are a relic from the `bxcan` crate. Remove them, move the functionality into the main structs. --- embassy-stm32/src/can/bx/mod.rs | 1010 ----------------- embassy-stm32/src/can/{bx => bxcan}/filter.rs | 2 +- .../src/can/{bxcan.rs => bxcan/mod.rs} | 344 ++++-- embassy-stm32/src/can/bxcan/registers.rs | 510 +++++++++ embassy-stm32/src/can/common.rs | 4 +- embassy-stm32/src/can/fdcan.rs | 34 +- embassy-stm32/src/can/mod.rs | 9 +- examples/stm32f1/src/bin/can.rs | 13 +- examples/stm32f4/src/bin/can.rs | 11 +- examples/stm32f7/src/bin/can.rs | 12 +- tests/stm32/src/bin/can.rs | 19 +- 11 files changed, 808 insertions(+), 1160 deletions(-) delete mode 100644 embassy-stm32/src/can/bx/mod.rs rename embassy-stm32/src/can/{bx => bxcan}/filter.rs (99%) rename embassy-stm32/src/can/{bxcan.rs => bxcan/mod.rs} (68%) create mode 100644 embassy-stm32/src/can/bxcan/registers.rs diff --git a/embassy-stm32/src/can/bx/mod.rs b/embassy-stm32/src/can/bx/mod.rs deleted file mode 100644 index cd82148ba..000000000 --- a/embassy-stm32/src/can/bx/mod.rs +++ /dev/null @@ -1,1010 +0,0 @@ -//! Driver for the STM32 bxCAN peripheral. -//! -//! This crate provides a reusable driver for the bxCAN peripheral found in many low- to middle-end -//! STM32 microcontrollers. HALs for compatible chips can reexport this crate and implement its -//! traits to easily expose a featureful CAN driver. -//! -//! # Features -//! -//! - Supports both single- and dual-peripheral configurations (where one bxCAN instance manages the -//! filters of a secondary instance). -//! - Handles standard and extended frames, and data and remote frames. -//! - Support for interrupts emitted by the bxCAN peripheral. -//! - Transmission respects CAN IDs and protects against priority inversion (a lower-priority frame -//! may be dequeued when enqueueing a higher-priority one). -//! - Implements the [`embedded-hal`] traits for interoperability. -//! - Support for both RX FIFOs (as [`Rx0`] and [`Rx1`]). -//! -//! # Limitations -//! -//! - Support for querying error states and handling error interrupts is incomplete. -//! - -// Deny a few warnings in doctests, since rustdoc `allow`s many warnings by default -#![allow(clippy::unnecessary_operation)] // lint is bugged - -//mod embedded_hal; -pub mod filter; - -#[allow(clippy::all)] // generated code -use core::cmp::{Ord, Ordering}; -use core::convert::Infallible; -use core::marker::PhantomData; -use core::mem; - -pub use embedded_can::{ExtendedId, Id, StandardId}; - -/// CAN Header: includes ID and length -pub type Header = crate::can::frame::Header; - -/// Data for a CAN Frame -pub type Data = crate::can::frame::ClassicData; - -use crate::can::_version::Envelope; -use crate::can::bx::filter::MasterFilters; -use crate::can::enums::BusError; -/// CAN Frame -pub use crate::can::frame::Frame; -use crate::pac::can::vals::Lec; - -#[derive(Debug, Copy, Clone, Eq, PartialEq)] -pub(crate) enum RxFifo { - Fifo0, - Fifo1, -} - -trait IntoBusError { - fn into_bus_err(self) -> Option; -} - -impl IntoBusError for Lec { - fn into_bus_err(self) -> Option { - match self { - Lec::STUFF => Some(BusError::Stuff), - Lec::FORM => Some(BusError::Form), - Lec::ACK => Some(BusError::Acknowledge), - Lec::BITRECESSIVE => Some(BusError::BitRecessive), - Lec::BITDOMINANT => Some(BusError::BitDominant), - Lec::CRC => Some(BusError::Crc), - Lec::CUSTOM => Some(BusError::Software), - _ => None, - } - } -} - -/// A bxCAN peripheral instance. -/// -/// This trait is meant to be implemented for a HAL-specific type that represent ownership of -/// the CAN peripheral (and any pins required by it, although that is entirely up to the HAL). -/// -/// # Safety -/// -/// It is only safe to implement this trait, when: -/// -/// * The implementing type has ownership of the peripheral, preventing any other accesses to the -/// register block. -/// * `REGISTERS` is a pointer to that peripheral's register block and can be safely accessed for as -/// long as ownership or a borrow of the implementing type is present. -pub unsafe trait Instance {} - -/// A bxCAN instance that owns filter banks. -/// -/// In master-slave-instance setups, only the master instance owns the filter banks, and needs to -/// split some of them off for use by the slave instance. In that case, the master instance should -/// implement [`FilterOwner`] and [`MasterInstance`], while the slave instance should only implement -/// [`Instance`]. -/// -/// In single-instance configurations, the instance owns all filter banks and they can not be split -/// off. In that case, the instance should implement [`Instance`] and [`FilterOwner`]. -/// -/// # Safety -/// -/// This trait must only be implemented if the instance does, in fact, own its associated filter -/// banks, and `NUM_FILTER_BANKS` must be correct. -pub unsafe trait FilterOwner: Instance { - /// The total number of filter banks available to the instance. - /// - /// This is usually either 14 or 28, and should be specified in the chip's reference manual or datasheet. - const NUM_FILTER_BANKS: u8; -} - -/// A bxCAN master instance that shares filter banks with a slave instance. -/// -/// In master-slave-instance setups, this trait should be implemented for the master instance. -/// -/// # Safety -/// -/// This trait must only be implemented when there is actually an associated slave instance. -pub unsafe trait MasterInstance: FilterOwner {} - -// TODO: what to do with these? -/* -#[derive(Debug, Copy, Clone, Eq, PartialEq, Format)] -pub enum Error { - Stuff, - Form, - Acknowledgement, - BitRecessive, - BitDominant, - Crc, - Software, -}*/ - -/// Error that indicates that an incoming message has been lost due to buffer overrun. -#[derive(Debug, Clone, Copy, PartialEq, Eq)] -#[cfg_attr(feature = "defmt", derive(defmt::Format))] -pub struct OverrunError { - _priv: (), -} - -/// Identifier of a CAN message. -/// -/// Can be either a standard identifier (11bit, Range: 0..0x3FF) or a -/// extendended identifier (29bit , Range: 0..0x1FFFFFFF). -/// -/// The `Ord` trait can be used to determine the frame’s priority this ID -/// belongs to. -/// Lower identifier values have a higher priority. Additionally standard frames -/// have a higher priority than extended frames and data frames have a higher -/// priority than remote frames. -#[derive(Clone, Copy, Debug, PartialEq, Eq)] -#[cfg_attr(feature = "defmt", derive(defmt::Format))] -pub(crate) struct IdReg(u32); - -impl IdReg { - const STANDARD_SHIFT: u32 = 21; - - const EXTENDED_SHIFT: u32 = 3; - - const IDE_MASK: u32 = 0x0000_0004; - - const RTR_MASK: u32 = 0x0000_0002; - - /// Creates a new standard identifier (11bit, Range: 0..0x7FF) - /// - /// Panics for IDs outside the allowed range. - fn new_standard(id: StandardId) -> Self { - Self(u32::from(id.as_raw()) << Self::STANDARD_SHIFT) - } - - /// Creates a new extendended identifier (29bit , Range: 0..0x1FFFFFFF). - /// - /// Panics for IDs outside the allowed range. - fn new_extended(id: ExtendedId) -> IdReg { - Self(id.as_raw() << Self::EXTENDED_SHIFT | Self::IDE_MASK) - } - - fn from_register(reg: u32) -> IdReg { - Self(reg & 0xFFFF_FFFE) - } - - /// Returns the identifier. - fn to_id(self) -> Id { - if self.is_extended() { - Id::Extended(unsafe { ExtendedId::new_unchecked(self.0 >> Self::EXTENDED_SHIFT) }) - } else { - Id::Standard(unsafe { StandardId::new_unchecked((self.0 >> Self::STANDARD_SHIFT) as u16) }) - } - } - - /// Returns the identifier. - fn id(self) -> embedded_can::Id { - if self.is_extended() { - embedded_can::ExtendedId::new(self.0 >> Self::EXTENDED_SHIFT) - .unwrap() - .into() - } else { - embedded_can::StandardId::new((self.0 >> Self::STANDARD_SHIFT) as u16) - .unwrap() - .into() - } - } - - /// Returns `true` if the identifier is an extended identifier. - fn is_extended(self) -> bool { - self.0 & Self::IDE_MASK != 0 - } - - /// Returns `true` if the identifer is part of a remote frame (RTR bit set). - fn rtr(self) -> bool { - self.0 & Self::RTR_MASK != 0 - } -} - -impl From<&embedded_can::Id> for IdReg { - fn from(eid: &embedded_can::Id) -> Self { - match eid { - embedded_can::Id::Standard(id) => IdReg::new_standard(StandardId::new(id.as_raw()).unwrap()), - embedded_can::Id::Extended(id) => IdReg::new_extended(ExtendedId::new(id.as_raw()).unwrap()), - } - } -} - -impl From for embedded_can::Id { - fn from(idr: IdReg) -> Self { - idr.id() - } -} - -/// `IdReg` is ordered by priority. -impl Ord for IdReg { - fn cmp(&self, other: &Self) -> Ordering { - // When the IDs match, data frames have priority over remote frames. - let rtr = self.rtr().cmp(&other.rtr()).reverse(); - - let id_a = self.to_id(); - let id_b = other.to_id(); - match (id_a, id_b) { - (Id::Standard(a), Id::Standard(b)) => { - // Lower IDs have priority over higher IDs. - a.as_raw().cmp(&b.as_raw()).reverse().then(rtr) - } - (Id::Extended(a), Id::Extended(b)) => a.as_raw().cmp(&b.as_raw()).reverse().then(rtr), - (Id::Standard(a), Id::Extended(b)) => { - // Standard frames have priority over extended frames if their Base IDs match. - a.as_raw() - .cmp(&b.standard_id().as_raw()) - .reverse() - .then(Ordering::Greater) - } - (Id::Extended(a), Id::Standard(b)) => { - a.standard_id().as_raw().cmp(&b.as_raw()).reverse().then(Ordering::Less) - } - } - } -} - -impl PartialOrd for IdReg { - fn partial_cmp(&self, other: &Self) -> Option { - Some(self.cmp(other)) - } -} - -pub(crate) struct Registers { - pub canregs: crate::pac::can::Can, -} - -impl Registers { - fn enter_init_mode(&mut self) { - self.canregs.mcr().modify(|reg| { - reg.set_sleep(false); - reg.set_inrq(true); - }); - loop { - let msr = self.canregs.msr().read(); - if !msr.slak() && msr.inak() { - break; - } - } - } - - // Leaves initialization mode, enters sleep mode. - fn leave_init_mode(&mut self) { - self.canregs.mcr().modify(|reg| { - reg.set_sleep(true); - reg.set_inrq(false); - }); - loop { - let msr = self.canregs.msr().read(); - if msr.slak() && !msr.inak() { - break; - } - } - } - - fn set_bit_timing(&mut self, bt: crate::can::util::NominalBitTiming) { - let prescaler = u16::from(bt.prescaler) & 0x1FF; - let seg1 = u8::from(bt.seg1); - let seg2 = u8::from(bt.seg2) & 0x7F; - let sync_jump_width = u8::from(bt.sync_jump_width) & 0x7F; - self.canregs.btr().modify(|reg| { - reg.set_brp(prescaler - 1); - reg.set_ts(0, seg1 - 1); - reg.set_ts(1, seg2 - 1); - reg.set_sjw(sync_jump_width - 1); - }); - } - - /// Enables or disables silent mode: Disconnects the TX signal from the pin. - pub fn set_silent(&self, enabled: bool) { - let mode = match enabled { - false => stm32_metapac::can::vals::Silm::NORMAL, - true => stm32_metapac::can::vals::Silm::SILENT, - }; - self.canregs.btr().modify(|reg| reg.set_silm(mode)); - } - - /// Enables or disables automatic retransmission of messages. - /// - /// If this is enabled, the CAN peripheral will automatically try to retransmit each frame - /// until it can be sent. Otherwise, it will try only once to send each frame. - /// - /// Automatic retransmission is enabled by default. - pub fn set_automatic_retransmit(&self, enabled: bool) { - self.canregs.mcr().modify(|reg| reg.set_nart(enabled)); - } - - /// Enables or disables loopback mode: Internally connects the TX and RX - /// signals together. - pub fn set_loopback(&self, enabled: bool) { - self.canregs.btr().modify(|reg| reg.set_lbkm(enabled)); - } - - /// Configures the automatic wake-up feature. - /// - /// This is turned off by default. - /// - /// When turned on, an incoming frame will cause the peripheral to wake up from sleep and - /// receive the frame. If enabled, [`Interrupt::Wakeup`] will also be triggered by the incoming - /// frame. - #[allow(dead_code)] - pub fn set_automatic_wakeup(&mut self, enabled: bool) { - self.canregs.mcr().modify(|reg| reg.set_awum(enabled)); - } - - /// Leaves initialization mode and enables the peripheral (non-blocking version). - /// - /// Usually, it is recommended to call [`CanConfig::enable`] instead. This method is only needed - /// if you want non-blocking initialization. - /// - /// If this returns [`WouldBlock`][nb::Error::WouldBlock], the peripheral will enable itself - /// in the background. The peripheral is enabled and ready to use when this method returns - /// successfully. - pub fn enable_non_blocking(&mut self) -> nb::Result<(), Infallible> { - let msr = self.canregs.msr().read(); - if msr.slak() { - self.canregs.mcr().modify(|reg| { - reg.set_abom(true); - reg.set_sleep(false); - }); - Err(nb::Error::WouldBlock) - } else { - Ok(()) - } - } - - /// Puts the peripheral in a sleep mode to save power. - /// - /// While in sleep mode, an incoming CAN frame will trigger [`Interrupt::Wakeup`] if enabled. - #[allow(dead_code)] - pub fn sleep(&mut self) { - self.canregs.mcr().modify(|reg| { - reg.set_sleep(true); - reg.set_inrq(false); - }); - loop { - let msr = self.canregs.msr().read(); - if msr.slak() && !msr.inak() { - break; - } - } - } - - /// Disables the CAN interface. - /// - /// The peripheral is disabled by setting `RESET` in `CAN_MCR`, which causes the peripheral to - /// enter sleep mode. - pub fn reset(&self) { - self.canregs.mcr().write(|reg| reg.set_reset(true)); - } - - /// Wakes up from sleep mode. - /// - /// Note that this will not trigger [`Interrupt::Wakeup`], only reception of an incoming CAN - /// frame will cause that interrupt. - #[allow(dead_code)] - pub fn wakeup(&mut self) { - self.canregs.mcr().modify(|reg| { - reg.set_sleep(false); - reg.set_inrq(false); - }); - loop { - let msr = self.canregs.msr().read(); - if !msr.slak() && !msr.inak() { - break; - } - } - } - - pub fn curr_error(&self) -> Option { - let err = { self.canregs.esr().read() }; - if err.boff() { - return Some(BusError::BusOff); - } else if err.epvf() { - return Some(BusError::BusPassive); - } else if err.ewgf() { - return Some(BusError::BusWarning); - } else if let Some(err) = err.lec().into_bus_err() { - return Some(err); - } - None - } - - /// Puts a CAN frame in a transmit mailbox for transmission on the bus. - /// - /// Frames are transmitted to the bus based on their priority (see [`FramePriority`]). - /// Transmit order is preserved for frames with identical priority. - /// - /// If all transmit mailboxes are full, and `frame` has a higher priority than the - /// lowest-priority message in the transmit mailboxes, transmission of the enqueued frame is - /// cancelled and `frame` is enqueued instead. The frame that was replaced is returned as - /// [`TransmitStatus::dequeued_frame`]. - pub fn transmit(&mut self, frame: &Frame) -> nb::Result { - // Get the index of the next free mailbox or the one with the lowest priority. - let tsr = self.canregs.tsr().read(); - let idx = tsr.code() as usize; - - let frame_is_pending = !tsr.tme(0) || !tsr.tme(1) || !tsr.tme(2); - let pending_frame = if frame_is_pending { - // High priority frames are transmitted first by the mailbox system. - // Frames with identical identifier shall be transmitted in FIFO order. - // The controller schedules pending frames of same priority based on the - // mailbox index instead. As a workaround check all pending mailboxes - // and only accept higher priority frames. - self.check_priority(0, frame.id().into())?; - self.check_priority(1, frame.id().into())?; - self.check_priority(2, frame.id().into())?; - - let all_frames_are_pending = !tsr.tme(0) && !tsr.tme(1) && !tsr.tme(2); - if all_frames_are_pending { - // No free mailbox is available. This can only happen when three frames with - // ascending priority (descending IDs) were requested for transmission and all - // of them are blocked by bus traffic with even higher priority. - // To prevent a priority inversion abort and replace the lowest priority frame. - self.read_pending_mailbox(idx) - } else { - // There was a free mailbox. - None - } - } else { - // All mailboxes are available: Send frame without performing any checks. - None - }; - - self.write_mailbox(idx, frame); - - let mailbox = match idx { - 0 => Mailbox::Mailbox0, - 1 => Mailbox::Mailbox1, - 2 => Mailbox::Mailbox2, - _ => unreachable!(), - }; - Ok(TransmitStatus { - dequeued_frame: pending_frame, - mailbox, - }) - } - - /// Returns `Ok` when the mailbox is free or if it contains pending frame with a - /// lower priority (higher ID) than the identifier `id`. - fn check_priority(&self, idx: usize, id: IdReg) -> nb::Result<(), Infallible> { - // Read the pending frame's id to check its priority. - assert!(idx < 3); - let tir = &self.canregs.tx(idx).tir().read(); - //let tir = &can.tx[idx].tir.read(); - - // Check the priority by comparing the identifiers. But first make sure the - // frame has not finished the transmission (`TXRQ` == 0) in the meantime. - if tir.txrq() && id <= IdReg::from_register(tir.0) { - // There's a mailbox whose priority is higher or equal - // the priority of the new frame. - return Err(nb::Error::WouldBlock); - } - - Ok(()) - } - - fn write_mailbox(&mut self, idx: usize, frame: &Frame) { - debug_assert!(idx < 3); - - let mb = self.canregs.tx(idx); - mb.tdtr().write(|w| w.set_dlc(frame.header().len() as u8)); - - mb.tdlr() - .write(|w| w.0 = u32::from_ne_bytes(frame.data()[0..4].try_into().unwrap())); - mb.tdhr() - .write(|w| w.0 = u32::from_ne_bytes(frame.data()[4..8].try_into().unwrap())); - let id: IdReg = frame.id().into(); - mb.tir().write(|w| { - w.0 = id.0; - w.set_txrq(true); - }); - } - - fn read_pending_mailbox(&mut self, idx: usize) -> Option { - if self.abort_by_index(idx) { - debug_assert!(idx < 3); - - let mb = self.canregs.tx(idx); - - let id = IdReg(mb.tir().read().0); - let mut data = [0xff; 8]; - data[0..4].copy_from_slice(&mb.tdlr().read().0.to_ne_bytes()); - data[4..8].copy_from_slice(&mb.tdhr().read().0.to_ne_bytes()); - let len = mb.tdtr().read().dlc(); - - Some(Frame::new(Header::new(id.id(), len, id.rtr()), &data).unwrap()) - } else { - // Abort request failed because the frame was already sent (or being sent) on - // the bus. All mailboxes are now free. This can happen for small prescaler - // values (e.g. 1MBit/s bit timing with a source clock of 8MHz) or when an ISR - // has preempted the execution. - None - } - } - - /// Tries to abort a pending frame. Returns `true` when aborted. - fn abort_by_index(&mut self, idx: usize) -> bool { - self.canregs.tsr().write(|reg| reg.set_abrq(idx, true)); - - // Wait for the abort request to be finished. - loop { - let tsr = self.canregs.tsr().read(); - if false == tsr.abrq(idx) { - break tsr.txok(idx) == false; - } - } - } - - /// Attempts to abort the sending of a frame that is pending in a mailbox. - /// - /// If there is no frame in the provided mailbox, or its transmission succeeds before it can be - /// aborted, this function has no effect and returns `false`. - /// - /// If there is a frame in the provided mailbox, and it is canceled successfully, this function - /// returns `true`. - pub fn abort(&mut self, mailbox: Mailbox) -> bool { - // If the mailbox is empty, the value of TXOKx depends on what happened with the previous - // frame in that mailbox. Only call abort_by_index() if the mailbox is not empty. - let tsr = self.canregs.tsr().read(); - let mailbox_empty = match mailbox { - Mailbox::Mailbox0 => tsr.tme(0), - Mailbox::Mailbox1 => tsr.tme(1), - Mailbox::Mailbox2 => tsr.tme(2), - }; - if mailbox_empty { - false - } else { - self.abort_by_index(mailbox as usize) - } - } - - /// Returns `true` if no frame is pending for transmission. - pub fn is_idle(&self) -> bool { - let tsr = self.canregs.tsr().read(); - tsr.tme(0) && tsr.tme(1) && tsr.tme(2) - } - - /// Clears the request complete flag for all mailboxes. - pub fn clear_interrupt_flags(&mut self) { - self.canregs.tsr().write(|reg| { - reg.set_rqcp(0, true); - reg.set_rqcp(1, true); - reg.set_rqcp(2, true); - }); - } - - pub fn receive_frame_available(&self) -> bool { - if self.canregs.rfr(0).read().fmp() != 0 { - true - } else if self.canregs.rfr(1).read().fmp() != 0 { - true - } else { - false - } - } - - pub fn receive_fifo(&self, fifo: crate::can::_version::bx::RxFifo) -> Option { - // Generate timestamp as early as possible - #[cfg(feature = "time")] - let ts = embassy_time::Instant::now(); - - use crate::pac::can::vals::Ide; - - let fifo_idx = match fifo { - crate::can::_version::bx::RxFifo::Fifo0 => 0usize, - crate::can::_version::bx::RxFifo::Fifo1 => 1usize, - }; - let rfr = self.canregs.rfr(fifo_idx); - let fifo = self.canregs.rx(fifo_idx); - - // If there are no pending messages, there is nothing to do - if rfr.read().fmp() == 0 { - return None; - } - - let rir = fifo.rir().read(); - let id: embedded_can::Id = if rir.ide() == Ide::STANDARD { - embedded_can::StandardId::new(rir.stid()).unwrap().into() - } else { - let stid = (rir.stid() & 0x7FF) as u32; - let exid = rir.exid() & 0x3FFFF; - let id = (stid << 18) | (exid); - embedded_can::ExtendedId::new(id).unwrap().into() - }; - let rdtr = fifo.rdtr().read(); - let data_len = rdtr.dlc(); - let rtr = rir.rtr() == stm32_metapac::can::vals::Rtr::REMOTE; - - #[cfg(not(feature = "time"))] - let ts = rdtr.time(); - - let mut data: [u8; 8] = [0; 8]; - data[0..4].copy_from_slice(&fifo.rdlr().read().0.to_ne_bytes()); - data[4..8].copy_from_slice(&fifo.rdhr().read().0.to_ne_bytes()); - - let frame = Frame::new(Header::new(id, data_len, rtr), &data).unwrap(); - let envelope = Envelope { ts, frame }; - - rfr.modify(|v| v.set_rfom(true)); - - Some(envelope) - } -} - -/// Configuration proxy returned by [`Can::modify_config`]. -#[must_use = "`CanConfig` leaves the peripheral in uninitialized state, call `CanConfig::enable` or explicitly drop the value"] -pub struct CanConfig<'a, I: Instance> { - can: &'a mut Can, -} - -impl CanConfig<'_, I> { - /// Configures the bit timings. - /// - /// You can use to calculate the `btr` parameter. Enter - /// parameters as follows: - /// - /// - *Clock Rate*: The input clock speed to the CAN peripheral (*not* the CPU clock speed). - /// This is the clock rate of the peripheral bus the CAN peripheral is attached to (eg. APB1). - /// - *Sample Point*: Should normally be left at the default value of 87.5%. - /// - *SJW*: Should normally be left at the default value of 1. - /// - /// Then copy the `CAN_BUS_TIME` register value from the table and pass it as the `btr` - /// parameter to this method. - pub fn set_bit_timing(self, bt: crate::can::util::NominalBitTiming) -> Self { - self.can.registers.set_bit_timing(bt); - self - } - - /// Enables or disables loopback mode: Internally connects the TX and RX - /// signals together. - pub fn set_loopback(self, enabled: bool) -> Self { - self.can.registers.set_loopback(enabled); - self - } - - /// Enables or disables silent mode: Disconnects the TX signal from the pin. - pub fn set_silent(self, enabled: bool) -> Self { - self.can.registers.set_silent(enabled); - self - } - - /// Enables or disables automatic retransmission of messages. - /// - /// If this is enabled, the CAN peripheral will automatically try to retransmit each frame - /// until it can be sent. Otherwise, it will try only once to send each frame. - /// - /// Automatic retransmission is enabled by default. - pub fn set_automatic_retransmit(self, enabled: bool) -> Self { - self.can.registers.set_automatic_retransmit(enabled); - self - } - - /// Leaves initialization mode and enables the peripheral. - /// - /// To sync with the CAN bus, this will block until 11 consecutive recessive bits are detected - /// on the bus. - /// - /// If you want to finish configuration without enabling the peripheral, you can call - /// [`CanConfig::leave_disabled`] or [`drop`] the [`CanConfig`] instead. - pub fn enable(self) { - self.can.registers.leave_init_mode(); - - match nb::block!(self.can.registers.enable_non_blocking()) { - Ok(()) => {} - Err(void) => match void {}, - } - - // Don't run the destructor. - mem::forget(self); - } - - /// Leaves initialization mode, but keeps the peripheral in sleep mode. - /// - /// Before the [`Can`] instance can be used, you have to enable it by calling - /// [`Can::enable_non_blocking`]. - pub fn leave_disabled(self) { - self.can.registers.leave_init_mode(); - } -} - -impl Drop for CanConfig<'_, I> { - #[inline] - fn drop(&mut self) { - self.can.registers.leave_init_mode(); - } -} - -/// Builder returned by [`Can::builder`]. -#[must_use = "`CanBuilder` leaves the peripheral in uninitialized state, call `CanBuilder::enable` or `CanBuilder::leave_disabled`"] -pub struct CanBuilder { - can: Can, -} - -impl CanBuilder { - /// Configures the bit timings. - /// - /// You can use to calculate the `btr` parameter. Enter - /// parameters as follows: - /// - /// - *Clock Rate*: The input clock speed to the CAN peripheral (*not* the CPU clock speed). - /// This is the clock rate of the peripheral bus the CAN peripheral is attached to (eg. APB1). - /// - *Sample Point*: Should normally be left at the default value of 87.5%. - /// - *SJW*: Should normally be left at the default value of 1. - /// - /// Then copy the `CAN_BUS_TIME` register value from the table and pass it as the `btr` - /// parameter to this method. - pub fn set_bit_timing(mut self, bt: crate::can::util::NominalBitTiming) -> Self { - self.can.registers.set_bit_timing(bt); - self - } - /// Enables or disables loopback mode: Internally connects the TX and RX - /// signals together. - pub fn set_loopback(self, enabled: bool) -> Self { - self.can.registers.set_loopback(enabled); - self - } - - /// Enables or disables silent mode: Disconnects the TX signal from the pin. - pub fn set_silent(self, enabled: bool) -> Self { - self.can.registers.set_silent(enabled); - self - } - - /// Enables or disables automatic retransmission of messages. - /// - /// If this is enabled, the CAN peripheral will automatically try to retransmit each frame - /// until it can be sent. Otherwise, it will try only once to send each frame. - /// - /// Automatic retransmission is enabled by default. - pub fn set_automatic_retransmit(self, enabled: bool) -> Self { - self.can.registers.set_automatic_retransmit(enabled); - self - } - - /// Leaves initialization mode and enables the peripheral. - /// - /// To sync with the CAN bus, this will block until 11 consecutive recessive bits are detected - /// on the bus. - /// - /// If you want to finish configuration without enabling the peripheral, you can call - /// [`CanBuilder::leave_disabled`] instead. - pub fn enable(mut self) -> Can { - self.leave_init_mode(); - - match nb::block!(self.can.registers.enable_non_blocking()) { - Ok(()) => self.can, - Err(void) => match void {}, - } - } - - /// Returns the [`Can`] interface without enabling it. - /// - /// This leaves initialization mode, but keeps the peripheral in sleep mode instead of enabling - /// it. - /// - /// Before the [`Can`] instance can be used, you have to enable it by calling - /// [`Can::enable_non_blocking`]. - pub fn leave_disabled(mut self) -> Can { - self.leave_init_mode(); - self.can - } - - /// Leaves initialization mode, enters sleep mode. - fn leave_init_mode(&mut self) { - self.can.registers.leave_init_mode(); - } -} - -/// Interface to a bxCAN peripheral. -pub struct Can { - instance: I, - canregs: crate::pac::can::Can, - pub(crate) registers: Registers, -} - -impl Can -where - I: Instance, -{ - /// Creates a [`CanBuilder`] for constructing a CAN interface. - pub fn builder(instance: I, canregs: crate::pac::can::Can) -> CanBuilder { - let mut can_builder = CanBuilder { - can: Can { - instance, - canregs, - registers: Registers { canregs }, - }, - }; - - can_builder.can.registers.enter_init_mode(); - - can_builder - } - - /// Disables the CAN interface and returns back the raw peripheral it was created from. - /// - /// The peripheral is disabled by setting `RESET` in `CAN_MCR`, which causes the peripheral to - /// enter sleep mode. - pub fn free(self) -> I { - self.registers.reset(); - self.instance - } - - /// Configure bit timings and silent/loop-back mode. - /// - /// Calling this method will enter initialization mode. - pub fn modify_config(&mut self) -> CanConfig<'_, I> { - self.registers.enter_init_mode(); - - CanConfig { can: self } - } - - /// Puts a CAN frame in a free transmit mailbox for transmission on the bus. - /// - /// Frames are transmitted to the bus based on their priority (see [`FramePriority`]). - /// Transmit order is preserved for frames with identical priority. - /// - /// If all transmit mailboxes are full, and `frame` has a higher priority than the - /// lowest-priority message in the transmit mailboxes, transmission of the enqueued frame is - /// cancelled and `frame` is enqueued instead. The frame that was replaced is returned as - /// [`TransmitStatus::dequeued_frame`]. - pub fn transmit(&mut self, frame: &Frame) -> nb::Result { - // Safety: We have a `&mut self` and have unique access to the peripheral. - unsafe { Tx::::conjure(self.canregs).transmit(frame) } - } - - /// Returns `true` if no frame is pending for transmission. - pub fn is_transmitter_idle(&self) -> bool { - // Safety: Read-only operation. - unsafe { Tx::::conjure(self.canregs).is_idle() } - } - - /// Attempts to abort the sending of a frame that is pending in a mailbox. - /// - /// If there is no frame in the provided mailbox, or its transmission succeeds before it can be - /// aborted, this function has no effect and returns `false`. - /// - /// If there is a frame in the provided mailbox, and it is canceled successfully, this function - /// returns `true`. - pub fn abort(&mut self, mailbox: Mailbox) -> bool { - // Safety: We have a `&mut self` and have unique access to the peripheral. - unsafe { Tx::::conjure(self.canregs).abort(mailbox) } - } - - pub(crate) fn split_by_ref(&mut self) -> (Tx, Rx) { - // Safety: We take `&mut self` and the return value lifetimes are tied to `self`'s lifetime. - let tx = unsafe { Tx::conjure(self.canregs) }; - let rx0 = unsafe { Rx::conjure() }; - (tx, rx0) - } -} - -impl Can { - /// Accesses the filter banks owned by this CAN peripheral. - /// - /// To modify filters of a slave peripheral, `modify_filters` has to be called on the master - /// peripheral instead. - pub fn modify_filters(&mut self) -> MasterFilters<'_, I> { - unsafe { MasterFilters::new(self.canregs) } - } -} - -/// Marker for Tx half -pub struct Tx { - _can: PhantomData, - pub(crate) registers: Registers, -} - -impl Tx -where - I: Instance, -{ - unsafe fn conjure(canregs: crate::pac::can::Can) -> Self { - Self { - _can: PhantomData, - registers: Registers { canregs }, //canregs, - } - } - - /// Puts a CAN frame in a transmit mailbox for transmission on the bus. - /// - /// Frames are transmitted to the bus based on their priority (see [`FramePriority`]). - /// Transmit order is preserved for frames with identical priority. - /// - /// If all transmit mailboxes are full, and `frame` has a higher priority than the - /// lowest-priority message in the transmit mailboxes, transmission of the enqueued frame is - /// cancelled and `frame` is enqueued instead. The frame that was replaced is returned as - /// [`TransmitStatus::dequeued_frame`]. - pub fn transmit(&mut self, frame: &Frame) -> nb::Result { - self.registers.transmit(frame) - } - - /// Attempts to abort the sending of a frame that is pending in a mailbox. - /// - /// If there is no frame in the provided mailbox, or its transmission succeeds before it can be - /// aborted, this function has no effect and returns `false`. - /// - /// If there is a frame in the provided mailbox, and it is canceled successfully, this function - /// returns `true`. - pub fn abort(&mut self, mailbox: Mailbox) -> bool { - self.registers.abort(mailbox) - } - - /// Returns `true` if no frame is pending for transmission. - pub fn is_idle(&self) -> bool { - self.registers.is_idle() - } - - /// Clears the request complete flag for all mailboxes. - pub fn clear_interrupt_flags(&mut self) { - self.registers.clear_interrupt_flags() - } -} - -/// Marker for Rx half -pub struct Rx { - _can: PhantomData, -} - -impl Rx -where - I: Instance, -{ - unsafe fn conjure() -> Self { - Self { _can: PhantomData } - } -} - -/// Identifies one of the two receive FIFOs. -#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)] -#[cfg_attr(feature = "defmt", derive(defmt::Format))] -pub enum Fifo { - /// First receive FIFO - Fifo0 = 0, - /// Second receive FIFO - Fifo1 = 1, -} - -/// Identifies one of the three transmit mailboxes. -#[derive(Debug, Copy, Clone, Ord, PartialOrd, Eq, PartialEq)] -#[cfg_attr(feature = "defmt", derive(defmt::Format))] -pub enum Mailbox { - /// Transmit mailbox 0 - Mailbox0 = 0, - /// Transmit mailbox 1 - Mailbox1 = 1, - /// Transmit mailbox 2 - Mailbox2 = 2, -} - -/// Contains information about a frame enqueued for transmission via [`Can::transmit`] or -/// [`Tx::transmit`]. -pub struct TransmitStatus { - dequeued_frame: Option, - mailbox: Mailbox, -} - -impl TransmitStatus { - /// Returns the lower-priority frame that was dequeued to make space for the new frame. - #[inline] - pub fn dequeued_frame(&self) -> Option<&Frame> { - self.dequeued_frame.as_ref() - } - - /// Returns the [`Mailbox`] the frame was enqueued in. - #[inline] - pub fn mailbox(&self) -> Mailbox { - self.mailbox - } -} diff --git a/embassy-stm32/src/can/bx/filter.rs b/embassy-stm32/src/can/bxcan/filter.rs similarity index 99% rename from embassy-stm32/src/can/bx/filter.rs rename to embassy-stm32/src/can/bxcan/filter.rs index 51766aa31..9940c7f50 100644 --- a/embassy-stm32/src/can/bx/filter.rs +++ b/embassy-stm32/src/can/bxcan/filter.rs @@ -2,7 +2,7 @@ use core::marker::PhantomData; -use crate::can::bx::{ExtendedId, Fifo, FilterOwner, Id, Instance, MasterInstance, StandardId}; +use super::{ExtendedId, Fifo, FilterOwner, Id, Instance, MasterInstance, StandardId}; const F32_RTR: u32 = 0b010; // set the RTR bit to match remote frames const F32_IDE: u32 = 0b100; // set the IDE bit to match extended identifiers diff --git a/embassy-stm32/src/can/bxcan.rs b/embassy-stm32/src/can/bxcan/mod.rs similarity index 68% rename from embassy-stm32/src/can/bxcan.rs rename to embassy-stm32/src/can/bxcan/mod.rs index fd6a79092..65fd0e9c2 100644 --- a/embassy-stm32/src/can/bxcan.rs +++ b/embassy-stm32/src/can/bxcan/mod.rs @@ -1,29 +1,27 @@ +pub mod filter; +mod registers; + use core::future::poll_fn; use core::marker::PhantomData; -use core::ops::{Deref, DerefMut}; use core::task::Poll; -pub mod bx; - -pub use bx::{filter, Data, ExtendedId, Fifo, Frame, Header, Id, StandardId}; use embassy_hal_internal::{into_ref, PeripheralRef}; use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex; use embassy_sync::channel::Channel; use embassy_sync::waitqueue::AtomicWaker; +pub use embedded_can::{ExtendedId, Id, StandardId}; +use self::filter::MasterFilters; +use self::registers::{Registers, RxFifo}; +pub use super::common::{BufferedCanReceiver, BufferedCanSender}; +use super::frame::{Envelope, Frame}; +use super::util; +use crate::can::enums::{BusError, TryReadError}; use crate::gpio::AFType; use crate::interrupt::typelevel::Interrupt; use crate::rcc::RccPeripheral; use crate::{interrupt, peripherals, Peripheral}; -pub mod enums; -pub mod frame; -pub mod util; -pub use frame::Envelope; - -mod common; -pub use self::common::{BufferedCanReceiver, BufferedCanSender}; - /// Interrupt handler. pub struct TxInterruptHandler { _phantom: PhantomData, @@ -80,9 +78,72 @@ impl interrupt::typelevel::Handler for SceInterrup } } +/// Configuration proxy returned by [`Can::modify_config`]. +pub struct CanConfig<'a, T: Instance> { + can: PhantomData<&'a mut T>, +} + +impl CanConfig<'_, T> { + /// Configures the bit timings. + /// + /// You can use to calculate the `btr` parameter. Enter + /// parameters as follows: + /// + /// - *Clock Rate*: The input clock speed to the CAN peripheral (*not* the CPU clock speed). + /// This is the clock rate of the peripheral bus the CAN peripheral is attached to (eg. APB1). + /// - *Sample Point*: Should normally be left at the default value of 87.5%. + /// - *SJW*: Should normally be left at the default value of 1. + /// + /// Then copy the `CAN_BUS_TIME` register value from the table and pass it as the `btr` + /// parameter to this method. + pub fn set_bit_timing(self, bt: crate::can::util::NominalBitTiming) -> Self { + Registers(T::regs()).set_bit_timing(bt); + self + } + + /// Configure the CAN bit rate. + /// + /// This is a helper that internally calls `set_bit_timing()`[Self::set_bit_timing]. + pub fn set_bitrate(self, bitrate: u32) -> Self { + let bit_timing = util::calc_can_timings(T::frequency(), bitrate).unwrap(); + self.set_bit_timing(bit_timing) + } + + /// Enables or disables loopback mode: Internally connects the TX and RX + /// signals together. + pub fn set_loopback(self, enabled: bool) -> Self { + Registers(T::regs()).set_loopback(enabled); + self + } + + /// Enables or disables silent mode: Disconnects the TX signal from the pin. + pub fn set_silent(self, enabled: bool) -> Self { + Registers(T::regs()).set_silent(enabled); + self + } + + /// Enables or disables automatic retransmission of messages. + /// + /// If this is enabled, the CAN peripheral will automatically try to retransmit each frame + /// until it can be sent. Otherwise, it will try only once to send each frame. + /// + /// Automatic retransmission is enabled by default. + pub fn set_automatic_retransmit(self, enabled: bool) -> Self { + Registers(T::regs()).set_automatic_retransmit(enabled); + self + } +} + +impl Drop for CanConfig<'_, T> { + #[inline] + fn drop(&mut self) { + Registers(T::regs()).leave_init_mode(); + } +} + /// CAN driver pub struct Can<'d, T: Instance> { - can: crate::can::bx::Can>, + peri: PeripheralRef<'d, T>, } /// Error returned by `try_write` @@ -145,14 +206,25 @@ impl<'d, T: Instance> Can<'d, T> { rx.set_as_af(rx.af_num(), AFType::Input); tx.set_as_af(tx.af_num(), AFType::OutputPushPull); - let can = crate::can::bx::Can::builder(BxcanInstance(peri), T::regs()).leave_disabled(); - Self { can } + Registers(T::regs()).leave_init_mode(); + + Self { peri } } /// Set CAN bit rate. pub fn set_bitrate(&mut self, bitrate: u32) { let bit_timing = util::calc_can_timings(T::frequency(), bitrate).unwrap(); - self.can.modify_config().set_bit_timing(bit_timing).leave_disabled(); + self.modify_config().set_bit_timing(bit_timing); + } + + /// Configure bit timings and silent/loop-back mode. + /// + /// Calling this method will enter initialization mode. You must enable the peripheral + /// again afterwards with [`enable`](Self::enable). + pub fn modify_config(&mut self) -> CanConfig<'_, T> { + Registers(T::regs()).enter_init_mode(); + + CanConfig { can: PhantomData } } /// Enables the peripheral and synchronizes with the bus. @@ -160,7 +232,7 @@ impl<'d, T: Instance> Can<'d, T> { /// This will wait for 11 consecutive recessive bits (bus idle state). /// Contrary to enable method from bxcan library, this will not freeze the executor while waiting. pub async fn enable(&mut self) { - while self.registers.enable_non_blocking().is_err() { + while Registers(T::regs()).enable_non_blocking().is_err() { // SCE interrupt is only generated for entering sleep mode, but not leaving. // Yield to allow other tasks to execute while can bus is initializing. embassy_futures::yield_now().await; @@ -170,19 +242,19 @@ impl<'d, T: Instance> Can<'d, T> { /// Queues the message to be sent. /// /// If the TX queue is full, this will wait until there is space, therefore exerting backpressure. - pub async fn write(&mut self, frame: &Frame) -> crate::can::bx::TransmitStatus { + pub async fn write(&mut self, frame: &Frame) -> TransmitStatus { self.split().0.write(frame).await } /// Attempts to transmit a frame without blocking. /// /// Returns [Err(TryWriteError::Full)] if all transmit mailboxes are full. - pub fn try_write(&mut self, frame: &Frame) -> Result { + pub fn try_write(&mut self, frame: &Frame) -> Result { self.split().0.try_write(frame) } /// Waits for a specific transmit mailbox to become empty - pub async fn flush(&self, mb: crate::can::bx::Mailbox) { + pub async fn flush(&self, mb: Mailbox) { CanTx::::flush_inner(mb).await } @@ -196,6 +268,22 @@ impl<'d, T: Instance> Can<'d, T> { CanTx::::flush_all_inner().await } + /// Attempts to abort the sending of a frame that is pending in a mailbox. + /// + /// If there is no frame in the provided mailbox, or its transmission succeeds before it can be + /// aborted, this function has no effect and returns `false`. + /// + /// If there is a frame in the provided mailbox, and it is canceled successfully, this function + /// returns `true`. + pub fn abort(&mut self, mailbox: Mailbox) -> bool { + Registers(T::regs()).abort(mailbox) + } + + /// Returns `true` if no frame is pending for transmission. + pub fn is_transmitter_idle(&self) -> bool { + Registers(T::regs()).is_idle() + } + /// Read a CAN frame. /// /// If no CAN frame is in the RX buffer, this will wait until there is one. @@ -221,8 +309,14 @@ impl<'d, T: Instance> Can<'d, T> { /// /// Useful for doing separate transmit/receive tasks. pub fn split<'c>(&'c mut self) -> (CanTx<'d, T>, CanRx<'d, T>) { - let (tx, rx) = self.can.split_by_ref(); - (CanTx { tx }, CanRx { rx }) + ( + CanTx { + _peri: unsafe { self.peri.clone_unchecked() }, + }, + CanRx { + peri: unsafe { self.peri.clone_unchecked() }, + }, + ) } /// Return a buffered instance of driver. User must supply Buffers @@ -239,10 +333,13 @@ impl<'d, T: Instance> Can<'d, T> { } } -impl<'d, T: Instance> AsMut>> for Can<'d, T> { - /// Get mutable access to the lower-level driver from the `bxcan` crate. - fn as_mut(&mut self) -> &mut crate::can::bx::Can> { - &mut self.can +impl<'d, T: FilterOwner> Can<'d, T> { + /// Accesses the filter banks owned by this CAN peripheral. + /// + /// To modify filters of a slave peripheral, `modify_filters` has to be called on the master + /// peripheral instead. + pub fn modify_filters(&mut self) -> MasterFilters<'_, T> { + unsafe { MasterFilters::new(T::regs()) } } } @@ -288,17 +385,17 @@ impl<'d, T: Instance, const TX_BUF_SIZE: usize, const RX_BUF_SIZE: usize> Buffer /// CAN driver, transmit half. pub struct CanTx<'d, T: Instance> { - tx: crate::can::bx::Tx>, + _peri: PeripheralRef<'d, T>, } impl<'d, T: Instance> CanTx<'d, T> { /// Queues the message to be sent. /// /// If the TX queue is full, this will wait until there is space, therefore exerting backpressure. - pub async fn write(&mut self, frame: &Frame) -> crate::can::bx::TransmitStatus { + pub async fn write(&mut self, frame: &Frame) -> TransmitStatus { poll_fn(|cx| { T::state().tx_mode.register(cx.waker()); - if let Ok(status) = self.tx.transmit(frame) { + if let Ok(status) = Registers(T::regs()).transmit(frame) { return Poll::Ready(status); } @@ -310,11 +407,11 @@ impl<'d, T: Instance> CanTx<'d, T> { /// Attempts to transmit a frame without blocking. /// /// Returns [Err(TryWriteError::Full)] if all transmit mailboxes are full. - pub fn try_write(&mut self, frame: &Frame) -> Result { - self.tx.transmit(frame).map_err(|_| TryWriteError::Full) + pub fn try_write(&mut self, frame: &Frame) -> Result { + Registers(T::regs()).transmit(frame).map_err(|_| TryWriteError::Full) } - async fn flush_inner(mb: crate::can::bx::Mailbox) { + async fn flush_inner(mb: Mailbox) { poll_fn(|cx| { T::state().tx_mode.register(cx.waker()); if T::regs().tsr().read().tme(mb.index()) { @@ -327,7 +424,7 @@ impl<'d, T: Instance> CanTx<'d, T> { } /// Waits for a specific transmit mailbox to become empty - pub async fn flush(&self, mb: crate::can::bx::Mailbox) { + pub async fn flush(&self, mb: Mailbox) { Self::flush_inner(mb).await } @@ -336,9 +433,9 @@ impl<'d, T: Instance> CanTx<'d, T> { T::state().tx_mode.register(cx.waker()); let tsr = T::regs().tsr().read(); - if tsr.tme(crate::can::bx::Mailbox::Mailbox0.index()) - || tsr.tme(crate::can::bx::Mailbox::Mailbox1.index()) - || tsr.tme(crate::can::bx::Mailbox::Mailbox2.index()) + if tsr.tme(Mailbox::Mailbox0.index()) + || tsr.tme(Mailbox::Mailbox1.index()) + || tsr.tme(Mailbox::Mailbox2.index()) { return Poll::Ready(()); } @@ -358,9 +455,9 @@ impl<'d, T: Instance> CanTx<'d, T> { T::state().tx_mode.register(cx.waker()); let tsr = T::regs().tsr().read(); - if tsr.tme(crate::can::bx::Mailbox::Mailbox0.index()) - && tsr.tme(crate::can::bx::Mailbox::Mailbox1.index()) - && tsr.tme(crate::can::bx::Mailbox::Mailbox2.index()) + if tsr.tme(Mailbox::Mailbox0.index()) + && tsr.tme(Mailbox::Mailbox1.index()) + && tsr.tme(Mailbox::Mailbox2.index()) { return Poll::Ready(()); } @@ -375,12 +472,28 @@ impl<'d, T: Instance> CanTx<'d, T> { Self::flush_all_inner().await } + /// Attempts to abort the sending of a frame that is pending in a mailbox. + /// + /// If there is no frame in the provided mailbox, or its transmission succeeds before it can be + /// aborted, this function has no effect and returns `false`. + /// + /// If there is a frame in the provided mailbox, and it is canceled successfully, this function + /// returns `true`. + pub fn abort(&mut self, mailbox: Mailbox) -> bool { + Registers(T::regs()).abort(mailbox) + } + + /// Returns `true` if no frame is pending for transmission. + pub fn is_idle(&self) -> bool { + Registers(T::regs()).is_idle() + } + /// Return a buffered instance of driver. User must supply Buffers pub fn buffered( self, txb: &'static mut TxBuf, ) -> BufferedCanTx<'d, T, TX_BUF_SIZE> { - BufferedCanTx::new(self.tx, txb) + BufferedCanTx::new(self, txb) } } @@ -389,19 +502,19 @@ pub type TxBuf = Channel { - _tx: crate::can::bx::Tx>, + _tx: CanTx<'d, T>, tx_buf: &'static TxBuf, } impl<'d, T: Instance, const TX_BUF_SIZE: usize> BufferedCanTx<'d, T, TX_BUF_SIZE> { - fn new(_tx: crate::can::bx::Tx>, tx_buf: &'static TxBuf) -> Self { + fn new(_tx: CanTx<'d, T>, tx_buf: &'static TxBuf) -> Self { Self { _tx, tx_buf }.setup() } fn setup(self) -> Self { // We don't want interrupts being processed while we change modes. critical_section::with(|_| unsafe { - let tx_inner = self::common::ClassicBufferedTxInner { + let tx_inner = super::common::ClassicBufferedTxInner { tx_receiver: self.tx_buf.receiver().into(), }; T::mut_state().tx_mode = TxMode::Buffered(tx_inner); @@ -435,7 +548,7 @@ impl<'d, T: Instance, const TX_BUF_SIZE: usize> Drop for BufferedCanTx<'d, T, TX /// CAN driver, receive half. #[allow(dead_code)] pub struct CanRx<'d, T: Instance> { - rx: crate::can::bx::Rx>, + peri: PeripheralRef<'d, T>, } impl<'d, T: Instance> CanRx<'d, T> { @@ -465,7 +578,7 @@ impl<'d, T: Instance> CanRx<'d, T> { self, rxb: &'static mut RxBuf, ) -> BufferedCanRx<'d, T, RX_BUF_SIZE> { - BufferedCanRx::new(self.rx, rxb) + BufferedCanRx::new(self, rxb) } } @@ -474,19 +587,19 @@ pub type RxBuf = Channel { - _rx: crate::can::bx::Rx>, + _rx: CanRx<'d, T>, rx_buf: &'static RxBuf, } impl<'d, T: Instance, const RX_BUF_SIZE: usize> BufferedCanRx<'d, T, RX_BUF_SIZE> { - fn new(_rx: crate::can::bx::Rx>, rx_buf: &'static RxBuf) -> Self { + fn new(_rx: CanRx<'d, T>, rx_buf: &'static RxBuf) -> Self { BufferedCanRx { _rx, rx_buf }.setup() } fn setup(self) -> Self { // We don't want interrupts being processed while we change modes. critical_section::with(|_| unsafe { - let rx_inner = self::common::ClassicBufferedRxInner { + let rx_inner = super::common::ClassicBufferedRxInner { rx_sender: self.rx_buf.sender().into(), }; T::mut_state().rx_mode = RxMode::Buffered(rx_inner); @@ -511,8 +624,7 @@ impl<'d, T: Instance, const RX_BUF_SIZE: usize> BufferedCanRx<'d, T, RX_BUF_SIZE Err(e) => Err(TryReadError::BusError(e)), } } else { - let registers = crate::can::bx::Registers { canregs: T::regs() }; - if let Some(err) = registers.curr_error() { + if let Some(err) = Registers(T::regs()).curr_error() { return Err(TryReadError::BusError(err)); } else { Err(TryReadError::Empty) @@ -544,8 +656,6 @@ impl<'d, T: Instance, const RX_BUF_SIZE: usize> Drop for BufferedCanRx<'d, T, RX } } -use crate::can::bx::RxFifo; - impl<'d, T: Instance> Drop for Can<'d, T> { fn drop(&mut self) { // Cannot call `free()` because it moves the instance. @@ -555,35 +665,62 @@ impl<'d, T: Instance> Drop for Can<'d, T> { } } -impl<'d, T: Instance> Deref for Can<'d, T> { - type Target = crate::can::bx::Can>; - - fn deref(&self) -> &Self::Target { - &self.can - } +/// Identifies one of the two receive FIFOs. +#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +pub enum Fifo { + /// First receive FIFO + Fifo0 = 0, + /// Second receive FIFO + Fifo1 = 1, } -impl<'d, T: Instance> DerefMut for Can<'d, T> { - fn deref_mut(&mut self) -> &mut Self::Target { - &mut self.can - } +/// Identifies one of the three transmit mailboxes. +#[derive(Debug, Copy, Clone, Ord, PartialOrd, Eq, PartialEq)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +pub enum Mailbox { + /// Transmit mailbox 0 + Mailbox0 = 0, + /// Transmit mailbox 1 + Mailbox1 = 1, + /// Transmit mailbox 2 + Mailbox2 = 2, } -use crate::can::enums::{BusError, TryReadError}; +/// Contains information about a frame enqueued for transmission via [`Can::transmit`] or +/// [`Tx::transmit`]. +pub struct TransmitStatus { + dequeued_frame: Option, + mailbox: Mailbox, +} + +impl TransmitStatus { + /// Returns the lower-priority frame that was dequeued to make space for the new frame. + #[inline] + pub fn dequeued_frame(&self) -> Option<&Frame> { + self.dequeued_frame.as_ref() + } + + /// Returns the [`Mailbox`] the frame was enqueued in. + #[inline] + pub fn mailbox(&self) -> Mailbox { + self.mailbox + } +} pub(crate) enum RxMode { NonBuffered(AtomicWaker), - Buffered(crate::can::_version::common::ClassicBufferedRxInner), + Buffered(super::common::ClassicBufferedRxInner), } impl RxMode { - pub fn on_interrupt(&self, fifo: crate::can::_version::bx::RxFifo) { + pub fn on_interrupt(&self, fifo: RxFifo) { match self { Self::NonBuffered(waker) => { // Disable interrupts until read let fifo_idx = match fifo { - crate::can::_version::bx::RxFifo::Fifo0 => 0usize, - crate::can::_version::bx::RxFifo::Fifo1 => 1usize, + RxFifo::Fifo0 => 0usize, + RxFifo::Fifo1 => 1usize, }; T::regs().ier().write(|w| { w.set_fmpie(fifo_idx, false); @@ -591,10 +728,8 @@ impl RxMode { waker.wake(); } Self::Buffered(buf) => { - let regsisters = crate::can::bx::Registers { canregs: T::regs() }; - loop { - match regsisters.receive_fifo(fifo) { + match Registers(T::regs()).receive_fifo(fifo) { Some(envelope) => { // NOTE: consensus was reached that if rx_queue is full, packets should be dropped let _ = buf.rx_sender.try_send(Ok(envelope)); @@ -628,13 +763,13 @@ impl RxMode { pub fn try_read(&self) -> Result { match self { Self::NonBuffered(_) => { - let registers = crate::can::bx::Registers { canregs: T::regs() }; - if let Some(msg) = registers.receive_fifo(super::bx::RxFifo::Fifo0) { + let registers = Registers(T::regs()); + if let Some(msg) = registers.receive_fifo(RxFifo::Fifo0) { T::regs().ier().write(|w| { w.set_fmpie(0, true); }); Ok(msg) - } else if let Some(msg) = registers.receive_fifo(super::bx::RxFifo::Fifo1) { + } else if let Some(msg) = registers.receive_fifo(RxFifo::Fifo1) { T::regs().ier().write(|w| { w.set_fmpie(1, true); }); @@ -655,8 +790,7 @@ impl RxMode { Self::NonBuffered(waker) => { poll_fn(|cx| { waker.register(cx.waker()); - let registers = crate::can::bx::Registers { canregs: T::regs() }; - if registers.receive_frame_available() { + if Registers(T::regs()).receive_frame_available() { Poll::Ready(()) } else { Poll::Pending @@ -673,15 +807,13 @@ impl RxMode { enum TxMode { NonBuffered(AtomicWaker), - Buffered(self::common::ClassicBufferedTxInner), + Buffered(super::common::ClassicBufferedTxInner), } impl TxMode { pub fn buffer_free(&self) -> bool { let tsr = T::regs().tsr().read(); - tsr.tme(crate::can::bx::Mailbox::Mailbox0.index()) - || tsr.tme(crate::can::bx::Mailbox::Mailbox1.index()) - || tsr.tme(crate::can::bx::Mailbox::Mailbox2.index()) + tsr.tme(Mailbox::Mailbox0.index()) || tsr.tme(Mailbox::Mailbox1.index()) || tsr.tme(Mailbox::Mailbox2.index()) } pub fn on_interrupt(&self) { match &T::state().tx_mode { @@ -690,8 +822,7 @@ impl TxMode { while self.buffer_free::() { match buf.tx_receiver.try_receive() { Ok(frame) => { - let mut registers = crate::can::bx::Registers { canregs: T::regs() }; - _ = registers.transmit(&frame); + _ = Registers(T::regs()).transmit(&frame); } Err(_) => { break; @@ -738,7 +869,7 @@ trait SealedInstance { /// CAN instance trait. #[allow(private_bounds)] -pub trait Instance: SealedInstance + RccPeripheral + 'static { +pub trait Instance: Peripheral

+ SealedInstance + RccPeripheral + 'static { /// TX interrupt for this instance. type TXInterrupt: crate::interrupt::typelevel::Interrupt; /// RX0 interrupt for this instance. @@ -749,10 +880,35 @@ pub trait Instance: SealedInstance + RccPeripheral + 'static { type SCEInterrupt: crate::interrupt::typelevel::Interrupt; } -/// BXCAN instance newtype. -pub struct BxcanInstance<'a, T>(PeripheralRef<'a, T>); +/// A bxCAN instance that owns filter banks. +/// +/// In master-slave-instance setups, only the master instance owns the filter banks, and needs to +/// split some of them off for use by the slave instance. In that case, the master instance should +/// implement [`FilterOwner`] and [`MasterInstance`], while the slave instance should only implement +/// [`Instance`]. +/// +/// In single-instance configurations, the instance owns all filter banks and they can not be split +/// off. In that case, the instance should implement [`Instance`] and [`FilterOwner`]. +/// +/// # Safety +/// +/// This trait must only be implemented if the instance does, in fact, own its associated filter +/// banks, and `NUM_FILTER_BANKS` must be correct. +pub unsafe trait FilterOwner: Instance { + /// The total number of filter banks available to the instance. + /// + /// This is usually either 14 or 28, and should be specified in the chip's reference manual or datasheet. + const NUM_FILTER_BANKS: u8; +} -unsafe impl<'d, T: Instance> crate::can::bx::Instance for BxcanInstance<'d, T> {} +/// A bxCAN master instance that shares filter banks with a slave instance. +/// +/// In master-slave-instance setups, this trait should be implemented for the master instance. +/// +/// # Safety +/// +/// This trait must only be implemented when there is actually an associated slave instance. +pub unsafe trait MasterInstance: FilterOwner {} foreach_peripheral!( (can, $inst:ident) => { @@ -782,7 +938,7 @@ foreach_peripheral!( foreach_peripheral!( (can, CAN) => { - unsafe impl<'d> crate::can::bx::FilterOwner for BxcanInstance<'d, peripherals::CAN> { + unsafe impl FilterOwner for peripherals::CAN { const NUM_FILTER_BANKS: u8 = 14; } }; @@ -797,19 +953,19 @@ foreach_peripheral!( ))] { // Most L4 devices and some F7 devices use the name "CAN1" // even if there is no "CAN2" peripheral. - unsafe impl<'d> crate::can::bx::FilterOwner for BxcanInstance<'d, peripherals::CAN1> { + unsafe impl FilterOwner for peripherals::CAN1 { const NUM_FILTER_BANKS: u8 = 14; } } else { - unsafe impl<'d> crate::can::bx::FilterOwner for BxcanInstance<'d, peripherals::CAN1> { + unsafe impl FilterOwner for peripherals::CAN1 { const NUM_FILTER_BANKS: u8 = 28; } - unsafe impl<'d> crate::can::bx::MasterInstance for BxcanInstance<'d, peripherals::CAN1> {} + unsafe impl MasterInstance for peripherals::CAN1 {} } } }; (can, CAN3) => { - unsafe impl<'d> crate::can::bx::FilterOwner for BxcanInstance<'d, peripherals::CAN3> { + unsafe impl FilterOwner for peripherals::CAN3 { const NUM_FILTER_BANKS: u8 = 14; } }; @@ -822,12 +978,12 @@ trait Index { fn index(&self) -> usize; } -impl Index for crate::can::bx::Mailbox { +impl Index for Mailbox { fn index(&self) -> usize { match self { - crate::can::bx::Mailbox::Mailbox0 => 0, - crate::can::bx::Mailbox::Mailbox1 => 1, - crate::can::bx::Mailbox::Mailbox2 => 2, + Mailbox::Mailbox0 => 0, + Mailbox::Mailbox1 => 1, + Mailbox::Mailbox2 => 2, } } } diff --git a/embassy-stm32/src/can/bxcan/registers.rs b/embassy-stm32/src/can/bxcan/registers.rs new file mode 100644 index 000000000..732567797 --- /dev/null +++ b/embassy-stm32/src/can/bxcan/registers.rs @@ -0,0 +1,510 @@ +use core::cmp::Ordering; +use core::convert::Infallible; + +pub use embedded_can::{ExtendedId, Id, StandardId}; +use stm32_metapac::can::vals::Lec; + +use super::{Mailbox, TransmitStatus}; +use crate::can::enums::BusError; +use crate::can::frame::{Envelope, Frame, Header}; + +pub(crate) struct Registers(pub crate::pac::can::Can); + +impl Registers { + pub fn enter_init_mode(&mut self) { + self.0.mcr().modify(|reg| { + reg.set_sleep(false); + reg.set_inrq(true); + }); + loop { + let msr = self.0.msr().read(); + if !msr.slak() && msr.inak() { + break; + } + } + } + + // Leaves initialization mode, enters sleep mode. + pub fn leave_init_mode(&mut self) { + self.0.mcr().modify(|reg| { + reg.set_sleep(true); + reg.set_inrq(false); + }); + loop { + let msr = self.0.msr().read(); + if msr.slak() && !msr.inak() { + break; + } + } + } + + pub fn set_bit_timing(&mut self, bt: crate::can::util::NominalBitTiming) { + let prescaler = u16::from(bt.prescaler) & 0x1FF; + let seg1 = u8::from(bt.seg1); + let seg2 = u8::from(bt.seg2) & 0x7F; + let sync_jump_width = u8::from(bt.sync_jump_width) & 0x7F; + self.0.btr().modify(|reg| { + reg.set_brp(prescaler - 1); + reg.set_ts(0, seg1 - 1); + reg.set_ts(1, seg2 - 1); + reg.set_sjw(sync_jump_width - 1); + }); + } + + /// Enables or disables silent mode: Disconnects the TX signal from the pin. + pub fn set_silent(&self, enabled: bool) { + let mode = match enabled { + false => stm32_metapac::can::vals::Silm::NORMAL, + true => stm32_metapac::can::vals::Silm::SILENT, + }; + self.0.btr().modify(|reg| reg.set_silm(mode)); + } + + /// Enables or disables automatic retransmission of messages. + /// + /// If this is enabled, the CAN peripheral will automatically try to retransmit each frame + /// until it can be sent. Otherwise, it will try only once to send each frame. + /// + /// Automatic retransmission is enabled by default. + pub fn set_automatic_retransmit(&self, enabled: bool) { + self.0.mcr().modify(|reg| reg.set_nart(enabled)); + } + + /// Enables or disables loopback mode: Internally connects the TX and RX + /// signals together. + pub fn set_loopback(&self, enabled: bool) { + self.0.btr().modify(|reg| reg.set_lbkm(enabled)); + } + + /// Configures the automatic wake-up feature. + /// + /// This is turned off by default. + /// + /// When turned on, an incoming frame will cause the peripheral to wake up from sleep and + /// receive the frame. If enabled, [`Interrupt::Wakeup`] will also be triggered by the incoming + /// frame. + #[allow(dead_code)] + pub fn set_automatic_wakeup(&mut self, enabled: bool) { + self.0.mcr().modify(|reg| reg.set_awum(enabled)); + } + + /// Leaves initialization mode and enables the peripheral (non-blocking version). + /// + /// Usually, it is recommended to call [`CanConfig::enable`] instead. This method is only needed + /// if you want non-blocking initialization. + /// + /// If this returns [`WouldBlock`][nb::Error::WouldBlock], the peripheral will enable itself + /// in the background. The peripheral is enabled and ready to use when this method returns + /// successfully. + pub fn enable_non_blocking(&mut self) -> nb::Result<(), Infallible> { + let msr = self.0.msr().read(); + if msr.slak() { + self.0.mcr().modify(|reg| { + reg.set_abom(true); + reg.set_sleep(false); + }); + Err(nb::Error::WouldBlock) + } else { + Ok(()) + } + } + + /// Puts the peripheral in a sleep mode to save power. + /// + /// While in sleep mode, an incoming CAN frame will trigger [`Interrupt::Wakeup`] if enabled. + #[allow(dead_code)] + pub fn sleep(&mut self) { + self.0.mcr().modify(|reg| { + reg.set_sleep(true); + reg.set_inrq(false); + }); + loop { + let msr = self.0.msr().read(); + if msr.slak() && !msr.inak() { + break; + } + } + } + + /// Wakes up from sleep mode. + /// + /// Note that this will not trigger [`Interrupt::Wakeup`], only reception of an incoming CAN + /// frame will cause that interrupt. + #[allow(dead_code)] + pub fn wakeup(&mut self) { + self.0.mcr().modify(|reg| { + reg.set_sleep(false); + reg.set_inrq(false); + }); + loop { + let msr = self.0.msr().read(); + if !msr.slak() && !msr.inak() { + break; + } + } + } + + pub fn curr_error(&self) -> Option { + let err = { self.0.esr().read() }; + if err.boff() { + return Some(BusError::BusOff); + } else if err.epvf() { + return Some(BusError::BusPassive); + } else if err.ewgf() { + return Some(BusError::BusWarning); + } else if err.lec() != Lec::NOERROR { + return Some(match err.lec() { + Lec::STUFF => BusError::Stuff, + Lec::FORM => BusError::Form, + Lec::ACK => BusError::Acknowledge, + Lec::BITRECESSIVE => BusError::BitRecessive, + Lec::BITDOMINANT => BusError::BitDominant, + Lec::CRC => BusError::Crc, + Lec::CUSTOM => BusError::Software, + Lec::NOERROR => unreachable!(), + }); + } + None + } + + /// Puts a CAN frame in a transmit mailbox for transmission on the bus. + /// + /// Frames are transmitted to the bus based on their priority (see [`FramePriority`]). + /// Transmit order is preserved for frames with identical priority. + /// + /// If all transmit mailboxes are full, and `frame` has a higher priority than the + /// lowest-priority message in the transmit mailboxes, transmission of the enqueued frame is + /// cancelled and `frame` is enqueued instead. The frame that was replaced is returned as + /// [`TransmitStatus::dequeued_frame`]. + pub fn transmit(&mut self, frame: &Frame) -> nb::Result { + // Get the index of the next free mailbox or the one with the lowest priority. + let tsr = self.0.tsr().read(); + let idx = tsr.code() as usize; + + let frame_is_pending = !tsr.tme(0) || !tsr.tme(1) || !tsr.tme(2); + let pending_frame = if frame_is_pending { + // High priority frames are transmitted first by the mailbox system. + // Frames with identical identifier shall be transmitted in FIFO order. + // The controller schedules pending frames of same priority based on the + // mailbox index instead. As a workaround check all pending mailboxes + // and only accept higher priority frames. + self.check_priority(0, frame.id().into())?; + self.check_priority(1, frame.id().into())?; + self.check_priority(2, frame.id().into())?; + + let all_frames_are_pending = !tsr.tme(0) && !tsr.tme(1) && !tsr.tme(2); + if all_frames_are_pending { + // No free mailbox is available. This can only happen when three frames with + // ascending priority (descending IDs) were requested for transmission and all + // of them are blocked by bus traffic with even higher priority. + // To prevent a priority inversion abort and replace the lowest priority frame. + self.read_pending_mailbox(idx) + } else { + // There was a free mailbox. + None + } + } else { + // All mailboxes are available: Send frame without performing any checks. + None + }; + + self.write_mailbox(idx, frame); + + let mailbox = match idx { + 0 => Mailbox::Mailbox0, + 1 => Mailbox::Mailbox1, + 2 => Mailbox::Mailbox2, + _ => unreachable!(), + }; + Ok(TransmitStatus { + dequeued_frame: pending_frame, + mailbox, + }) + } + + /// Returns `Ok` when the mailbox is free or if it contains pending frame with a + /// lower priority (higher ID) than the identifier `id`. + fn check_priority(&self, idx: usize, id: IdReg) -> nb::Result<(), Infallible> { + // Read the pending frame's id to check its priority. + assert!(idx < 3); + let tir = &self.0.tx(idx).tir().read(); + //let tir = &can.tx[idx].tir.read(); + + // Check the priority by comparing the identifiers. But first make sure the + // frame has not finished the transmission (`TXRQ` == 0) in the meantime. + if tir.txrq() && id <= IdReg::from_register(tir.0) { + // There's a mailbox whose priority is higher or equal + // the priority of the new frame. + return Err(nb::Error::WouldBlock); + } + + Ok(()) + } + + fn write_mailbox(&mut self, idx: usize, frame: &Frame) { + debug_assert!(idx < 3); + + let mb = self.0.tx(idx); + mb.tdtr().write(|w| w.set_dlc(frame.header().len() as u8)); + + mb.tdlr() + .write(|w| w.0 = u32::from_ne_bytes(frame.data()[0..4].try_into().unwrap())); + mb.tdhr() + .write(|w| w.0 = u32::from_ne_bytes(frame.data()[4..8].try_into().unwrap())); + let id: IdReg = frame.id().into(); + mb.tir().write(|w| { + w.0 = id.0; + w.set_txrq(true); + }); + } + + fn read_pending_mailbox(&mut self, idx: usize) -> Option { + if self.abort_by_index(idx) { + debug_assert!(idx < 3); + + let mb = self.0.tx(idx); + + let id = IdReg(mb.tir().read().0); + let mut data = [0xff; 8]; + data[0..4].copy_from_slice(&mb.tdlr().read().0.to_ne_bytes()); + data[4..8].copy_from_slice(&mb.tdhr().read().0.to_ne_bytes()); + let len = mb.tdtr().read().dlc(); + + Some(Frame::new(Header::new(id.id(), len, id.rtr()), &data).unwrap()) + } else { + // Abort request failed because the frame was already sent (or being sent) on + // the bus. All mailboxes are now free. This can happen for small prescaler + // values (e.g. 1MBit/s bit timing with a source clock of 8MHz) or when an ISR + // has preempted the execution. + None + } + } + + /// Tries to abort a pending frame. Returns `true` when aborted. + fn abort_by_index(&mut self, idx: usize) -> bool { + self.0.tsr().write(|reg| reg.set_abrq(idx, true)); + + // Wait for the abort request to be finished. + loop { + let tsr = self.0.tsr().read(); + if false == tsr.abrq(idx) { + break tsr.txok(idx) == false; + } + } + } + + /// Attempts to abort the sending of a frame that is pending in a mailbox. + /// + /// If there is no frame in the provided mailbox, or its transmission succeeds before it can be + /// aborted, this function has no effect and returns `false`. + /// + /// If there is a frame in the provided mailbox, and it is canceled successfully, this function + /// returns `true`. + pub fn abort(&mut self, mailbox: Mailbox) -> bool { + // If the mailbox is empty, the value of TXOKx depends on what happened with the previous + // frame in that mailbox. Only call abort_by_index() if the mailbox is not empty. + let tsr = self.0.tsr().read(); + let mailbox_empty = match mailbox { + Mailbox::Mailbox0 => tsr.tme(0), + Mailbox::Mailbox1 => tsr.tme(1), + Mailbox::Mailbox2 => tsr.tme(2), + }; + if mailbox_empty { + false + } else { + self.abort_by_index(mailbox as usize) + } + } + + /// Returns `true` if no frame is pending for transmission. + pub fn is_idle(&self) -> bool { + let tsr = self.0.tsr().read(); + tsr.tme(0) && tsr.tme(1) && tsr.tme(2) + } + + pub fn receive_frame_available(&self) -> bool { + if self.0.rfr(0).read().fmp() != 0 { + true + } else if self.0.rfr(1).read().fmp() != 0 { + true + } else { + false + } + } + + pub fn receive_fifo(&self, fifo: RxFifo) -> Option { + // Generate timestamp as early as possible + #[cfg(feature = "time")] + let ts = embassy_time::Instant::now(); + + use crate::pac::can::vals::Ide; + + let fifo_idx = match fifo { + RxFifo::Fifo0 => 0usize, + RxFifo::Fifo1 => 1usize, + }; + let rfr = self.0.rfr(fifo_idx); + let fifo = self.0.rx(fifo_idx); + + // If there are no pending messages, there is nothing to do + if rfr.read().fmp() == 0 { + return None; + } + + let rir = fifo.rir().read(); + let id: embedded_can::Id = if rir.ide() == Ide::STANDARD { + embedded_can::StandardId::new(rir.stid()).unwrap().into() + } else { + let stid = (rir.stid() & 0x7FF) as u32; + let exid = rir.exid() & 0x3FFFF; + let id = (stid << 18) | (exid); + embedded_can::ExtendedId::new(id).unwrap().into() + }; + let rdtr = fifo.rdtr().read(); + let data_len = rdtr.dlc(); + let rtr = rir.rtr() == stm32_metapac::can::vals::Rtr::REMOTE; + + #[cfg(not(feature = "time"))] + let ts = rdtr.time(); + + let mut data: [u8; 8] = [0; 8]; + data[0..4].copy_from_slice(&fifo.rdlr().read().0.to_ne_bytes()); + data[4..8].copy_from_slice(&fifo.rdhr().read().0.to_ne_bytes()); + + let frame = Frame::new(Header::new(id, data_len, rtr), &data).unwrap(); + let envelope = Envelope { ts, frame }; + + rfr.modify(|v| v.set_rfom(true)); + + Some(envelope) + } +} + +/// Identifier of a CAN message. +/// +/// Can be either a standard identifier (11bit, Range: 0..0x3FF) or a +/// extendended identifier (29bit , Range: 0..0x1FFFFFFF). +/// +/// The `Ord` trait can be used to determine the frame’s priority this ID +/// belongs to. +/// Lower identifier values have a higher priority. Additionally standard frames +/// have a higher priority than extended frames and data frames have a higher +/// priority than remote frames. +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +pub(crate) struct IdReg(u32); + +impl IdReg { + const STANDARD_SHIFT: u32 = 21; + + const EXTENDED_SHIFT: u32 = 3; + + const IDE_MASK: u32 = 0x0000_0004; + + const RTR_MASK: u32 = 0x0000_0002; + + /// Creates a new standard identifier (11bit, Range: 0..0x7FF) + /// + /// Panics for IDs outside the allowed range. + fn new_standard(id: StandardId) -> Self { + Self(u32::from(id.as_raw()) << Self::STANDARD_SHIFT) + } + + /// Creates a new extendended identifier (29bit , Range: 0..0x1FFFFFFF). + /// + /// Panics for IDs outside the allowed range. + fn new_extended(id: ExtendedId) -> IdReg { + Self(id.as_raw() << Self::EXTENDED_SHIFT | Self::IDE_MASK) + } + + fn from_register(reg: u32) -> IdReg { + Self(reg & 0xFFFF_FFFE) + } + + /// Returns the identifier. + fn to_id(self) -> Id { + if self.is_extended() { + Id::Extended(unsafe { ExtendedId::new_unchecked(self.0 >> Self::EXTENDED_SHIFT) }) + } else { + Id::Standard(unsafe { StandardId::new_unchecked((self.0 >> Self::STANDARD_SHIFT) as u16) }) + } + } + + /// Returns the identifier. + fn id(self) -> embedded_can::Id { + if self.is_extended() { + embedded_can::ExtendedId::new(self.0 >> Self::EXTENDED_SHIFT) + .unwrap() + .into() + } else { + embedded_can::StandardId::new((self.0 >> Self::STANDARD_SHIFT) as u16) + .unwrap() + .into() + } + } + + /// Returns `true` if the identifier is an extended identifier. + fn is_extended(self) -> bool { + self.0 & Self::IDE_MASK != 0 + } + + /// Returns `true` if the identifer is part of a remote frame (RTR bit set). + fn rtr(self) -> bool { + self.0 & Self::RTR_MASK != 0 + } +} + +impl From<&embedded_can::Id> for IdReg { + fn from(eid: &embedded_can::Id) -> Self { + match eid { + embedded_can::Id::Standard(id) => IdReg::new_standard(StandardId::new(id.as_raw()).unwrap()), + embedded_can::Id::Extended(id) => IdReg::new_extended(ExtendedId::new(id.as_raw()).unwrap()), + } + } +} + +impl From for embedded_can::Id { + fn from(idr: IdReg) -> Self { + idr.id() + } +} + +/// `IdReg` is ordered by priority. +impl Ord for IdReg { + fn cmp(&self, other: &Self) -> Ordering { + // When the IDs match, data frames have priority over remote frames. + let rtr = self.rtr().cmp(&other.rtr()).reverse(); + + let id_a = self.to_id(); + let id_b = other.to_id(); + match (id_a, id_b) { + (Id::Standard(a), Id::Standard(b)) => { + // Lower IDs have priority over higher IDs. + a.as_raw().cmp(&b.as_raw()).reverse().then(rtr) + } + (Id::Extended(a), Id::Extended(b)) => a.as_raw().cmp(&b.as_raw()).reverse().then(rtr), + (Id::Standard(a), Id::Extended(b)) => { + // Standard frames have priority over extended frames if their Base IDs match. + a.as_raw() + .cmp(&b.standard_id().as_raw()) + .reverse() + .then(Ordering::Greater) + } + (Id::Extended(a), Id::Standard(b)) => { + a.standard_id().as_raw().cmp(&b.as_raw()).reverse().then(Ordering::Less) + } + } + } +} + +impl PartialOrd for IdReg { + fn partial_cmp(&self, other: &Self) -> Option { + Some(self.cmp(other)) + } +} + +#[derive(Debug, Copy, Clone, Eq, PartialEq)] +pub(crate) enum RxFifo { + Fifo0, + Fifo1, +} diff --git a/embassy-stm32/src/can/common.rs b/embassy-stm32/src/can/common.rs index 570761b19..a54b54f6e 100644 --- a/embassy-stm32/src/can/common.rs +++ b/embassy-stm32/src/can/common.rs @@ -1,7 +1,7 @@ use embassy_sync::channel::{DynamicReceiver, DynamicSender}; -use crate::can::_version::enums::*; -use crate::can::_version::frame::*; +use super::enums::*; +use super::frame::*; pub(crate) struct ClassicBufferedRxInner { pub rx_sender: DynamicSender<'static, Result>, diff --git a/embassy-stm32/src/can/fdcan.rs b/embassy-stm32/src/can/fdcan.rs index 2ccf4b093..e31821ca2 100644 --- a/embassy-stm32/src/can/fdcan.rs +++ b/embassy-stm32/src/can/fdcan.rs @@ -14,19 +14,15 @@ use crate::interrupt::typelevel::Interrupt; use crate::rcc::RccPeripheral; use crate::{interrupt, peripherals, Peripheral}; -mod common; -pub mod enums; pub(crate) mod fd; -pub mod frame; -mod util; -use enums::*; -use fd::config::*; -use fd::filter::*; -pub use fd::{config, filter}; -use frame::*; - -pub use self::common::{BufferedCanReceiver, BufferedCanSender}; +use self::fd::config::*; +use self::fd::filter::*; +pub use self::fd::{config, filter}; +pub use super::common::{BufferedCanReceiver, BufferedCanSender}; +use super::enums::*; +use super::frame::*; +use super::util; /// Timestamp for incoming packets. Use Embassy time when enabled. #[cfg(feature = "time")] @@ -439,10 +435,10 @@ impl<'c, 'd, T: Instance, const TX_BUF_SIZE: usize, const RX_BUF_SIZE: usize> fn setup(self) -> Self { // We don't want interrupts being processed while we change modes. critical_section::with(|_| unsafe { - let rx_inner = self::common::ClassicBufferedRxInner { + let rx_inner = super::common::ClassicBufferedRxInner { rx_sender: self.rx_buf.sender().into(), }; - let tx_inner = self::common::ClassicBufferedTxInner { + let tx_inner = super::common::ClassicBufferedTxInner { tx_receiver: self.tx_buf.receiver().into(), }; T::mut_state().rx_mode = RxMode::ClassicBuffered(rx_inner); @@ -555,10 +551,10 @@ impl<'c, 'd, T: Instance, const TX_BUF_SIZE: usize, const RX_BUF_SIZE: usize> fn setup(self) -> Self { // We don't want interrupts being processed while we change modes. critical_section::with(|_| unsafe { - let rx_inner = self::common::FdBufferedRxInner { + let rx_inner = super::common::FdBufferedRxInner { rx_sender: self.rx_buf.sender().into(), }; - let tx_inner = self::common::FdBufferedTxInner { + let tx_inner = super::common::FdBufferedTxInner { tx_receiver: self.tx_buf.receiver().into(), }; T::mut_state().rx_mode = RxMode::FdBuffered(rx_inner); @@ -649,8 +645,8 @@ impl<'c, 'd, T: Instance> CanRx<'d, T> { enum RxMode { NonBuffered(AtomicWaker), - ClassicBuffered(self::common::ClassicBufferedRxInner), - FdBuffered(self::common::FdBufferedRxInner), + ClassicBuffered(super::common::ClassicBufferedRxInner), + FdBuffered(super::common::FdBufferedRxInner), } impl RxMode { @@ -758,8 +754,8 @@ impl RxMode { enum TxMode { NonBuffered(AtomicWaker), - ClassicBuffered(self::common::ClassicBufferedTxInner), - FdBuffered(self::common::FdBufferedTxInner), + ClassicBuffered(super::common::ClassicBufferedTxInner), + FdBuffered(super::common::FdBufferedTxInner), } impl TxMode { diff --git a/embassy-stm32/src/can/mod.rs b/embassy-stm32/src/can/mod.rs index 915edb3a6..410a6bfcb 100644 --- a/embassy-stm32/src/can/mod.rs +++ b/embassy-stm32/src/can/mod.rs @@ -1,7 +1,14 @@ //! Controller Area Network (CAN) #![macro_use] -#[cfg_attr(can_bxcan, path = "bxcan.rs")] +#[cfg_attr(can_bxcan, path = "bxcan/mod.rs")] #[cfg_attr(any(can_fdcan_v1, can_fdcan_h7), path = "fdcan.rs")] mod _version; pub use _version::*; + +mod common; +pub mod enums; +pub mod frame; +pub mod util; + +pub use frame::Frame; diff --git a/examples/stm32f1/src/bin/can.rs b/examples/stm32f1/src/bin/can.rs index 90cb9e46b..1c13d623d 100644 --- a/examples/stm32f1/src/bin/can.rs +++ b/examples/stm32f1/src/bin/can.rs @@ -3,8 +3,9 @@ use defmt::*; use embassy_executor::Spawner; +use embassy_stm32::can::frame::Envelope; use embassy_stm32::can::{ - filter, Can, Envelope, Fifo, Frame, Id, Rx0InterruptHandler, Rx1InterruptHandler, SceInterruptHandler, StandardId, + filter, Can, Fifo, Frame, Id, Rx0InterruptHandler, Rx1InterruptHandler, SceInterruptHandler, StandardId, TxInterruptHandler, }; use embassy_stm32::peripherals::CAN; @@ -55,17 +56,13 @@ async fn main(_spawner: Spawner) { let mut can = Can::new(p.CAN, p.PB8, p.PB9, Irqs); - can.as_mut() - .modify_filters() + can.modify_filters() .enable_bank(0, Fifo::Fifo0, filter::Mask32::accept_all()); - can.as_mut() - .modify_config() + can.modify_config() .set_loopback(false) .set_silent(false) - .leave_disabled(); - - can.set_bitrate(250_000); + .set_bitrate(250_000); can.enable().await; let mut i: u8 = 0; diff --git a/examples/stm32f4/src/bin/can.rs b/examples/stm32f4/src/bin/can.rs index 71b9453eb..cedc057a7 100644 --- a/examples/stm32f4/src/bin/can.rs +++ b/examples/stm32f4/src/bin/can.rs @@ -35,17 +35,12 @@ async fn main(_spawner: Spawner) { let mut can = Can::new(p.CAN1, p.PA11, p.PA12, Irqs); - can.as_mut() - .modify_filters() - .enable_bank(0, Fifo::Fifo0, Mask32::accept_all()); + can.modify_filters().enable_bank(0, Fifo::Fifo0, Mask32::accept_all()); - can.as_mut() - .modify_config() + can.modify_config() .set_loopback(true) // Receive own frames .set_silent(true) - .leave_disabled(); - - can.set_bitrate(1_000_000); + .set_bitrate(1_000_000); can.enable().await; diff --git a/examples/stm32f7/src/bin/can.rs b/examples/stm32f7/src/bin/can.rs index 221ac2a05..e32b4d3df 100644 --- a/examples/stm32f7/src/bin/can.rs +++ b/examples/stm32f7/src/bin/can.rs @@ -47,20 +47,18 @@ async fn main(spawner: Spawner) { static CAN: StaticCell> = StaticCell::new(); let can = CAN.init(Can::new(p.CAN3, p.PA8, p.PA15, Irqs)); - can.as_mut() - .modify_filters() - .enable_bank(0, Fifo::Fifo0, Mask32::accept_all()); + can.modify_filters().enable_bank(0, Fifo::Fifo0, Mask32::accept_all()); - can.as_mut() - .modify_config() + can.modify_config() .set_bit_timing(can::util::NominalBitTiming { prescaler: NonZeroU16::new(2).unwrap(), seg1: NonZeroU8::new(13).unwrap(), seg2: NonZeroU8::new(2).unwrap(), sync_jump_width: NonZeroU8::new(1).unwrap(), }) // http://www.bittiming.can-wiki.info/ - .set_loopback(true) - .enable(); + .set_loopback(true); + + can.enable().await; let (tx, mut rx) = can.split(); diff --git a/tests/stm32/src/bin/can.rs b/tests/stm32/src/bin/can.rs index 74d84c42f..551764458 100644 --- a/tests/stm32/src/bin/can.rs +++ b/tests/stm32/src/bin/can.rs @@ -8,9 +8,10 @@ mod common; use common::*; use embassy_executor::Spawner; use embassy_stm32::bind_interrupts; -use embassy_stm32::can::bx::filter::Mask32; -use embassy_stm32::can::bx::Fifo; -use embassy_stm32::can::{Can, Rx0InterruptHandler, Rx1InterruptHandler, SceInterruptHandler, TxInterruptHandler}; +use embassy_stm32::can::filter::Mask32; +use embassy_stm32::can::{ + Can, Fifo, Rx0InterruptHandler, Rx1InterruptHandler, SceInterruptHandler, TxInterruptHandler, +}; use embassy_stm32::gpio::{Input, Pull}; use embassy_stm32::peripherals::CAN1; use embassy_time::Duration; @@ -51,17 +52,15 @@ async fn main(_spawner: Spawner) { info!("Configuring can..."); - can.as_mut() - .modify_filters() - .enable_bank(0, Fifo::Fifo0, Mask32::accept_all()); + can.modify_filters().enable_bank(0, Fifo::Fifo0, Mask32::accept_all()); - can.set_bitrate(1_000_000); - can.as_mut() - .modify_config() + can.modify_config() .set_loopback(true) // Receive own frames .set_silent(true) // .set_bit_timing(0x001c0003) - .enable(); + .set_bitrate(1_000_000); + + can.enable().await; info!("Can configured"); From aa9eb2ab01cb21e06bb12efb425088827f6127d9 Mon Sep 17 00:00:00 2001 From: Alexandru RADOVICI Date: Tue, 2 Apr 2024 17:20:35 +0300 Subject: [PATCH 53/62] rename PWM_CH to PWM_SLICE --- embassy-rp/src/lib.rs | 16 +++---- embassy-rp/src/pwm.rs | 76 +++++++++++++++--------------- examples/rp/src/bin/pio_hd44780.rs | 2 +- examples/rp/src/bin/pwm.rs | 2 +- examples/rp/src/bin/pwm_input.rs | 2 +- tests/rp/src/bin/pwm.rs | 14 +++--- 6 files changed, 56 insertions(+), 56 deletions(-) diff --git a/embassy-rp/src/lib.rs b/embassy-rp/src/lib.rs index d91cea410..1c83e306d 100644 --- a/embassy-rp/src/lib.rs +++ b/embassy-rp/src/lib.rs @@ -183,14 +183,14 @@ embassy_hal_internal::peripherals! { DMA_CH10, DMA_CH11, - PWM_CH0, - PWM_CH1, - PWM_CH2, - PWM_CH3, - PWM_CH4, - PWM_CH5, - PWM_CH6, - PWM_CH7, + PWM_SLICE0, + PWM_SLICE1, + PWM_SLICE2, + PWM_SLICE3, + PWM_SLICE4, + PWM_SLICE5, + PWM_SLICE6, + PWM_SLICE7, USB, diff --git a/embassy-rp/src/pwm.rs b/embassy-rp/src/pwm.rs index e6f3b2aa2..3b980108a 100644 --- a/embassy-rp/src/pwm.rs +++ b/embassy-rp/src/pwm.rs @@ -326,14 +326,14 @@ macro_rules! channel { }; } -channel!(PWM_CH0, 0); -channel!(PWM_CH1, 1); -channel!(PWM_CH2, 2); -channel!(PWM_CH3, 3); -channel!(PWM_CH4, 4); -channel!(PWM_CH5, 5); -channel!(PWM_CH6, 6); -channel!(PWM_CH7, 7); +channel!(PWM_SLICE0, 0); +channel!(PWM_SLICE1, 1); +channel!(PWM_SLICE2, 2); +channel!(PWM_SLICE3, 3); +channel!(PWM_SLICE4, 4); +channel!(PWM_SLICE5, 5); +channel!(PWM_SLICE6, 6); +channel!(PWM_SLICE7, 7); /// PWM Pin A. pub trait PwmPinA: GpioPin {} @@ -346,33 +346,33 @@ macro_rules! impl_pin { }; } -impl_pin!(PIN_0, PWM_CH0, PwmPinA); -impl_pin!(PIN_1, PWM_CH0, PwmPinB); -impl_pin!(PIN_2, PWM_CH1, PwmPinA); -impl_pin!(PIN_3, PWM_CH1, PwmPinB); -impl_pin!(PIN_4, PWM_CH2, PwmPinA); -impl_pin!(PIN_5, PWM_CH2, PwmPinB); -impl_pin!(PIN_6, PWM_CH3, PwmPinA); -impl_pin!(PIN_7, PWM_CH3, PwmPinB); -impl_pin!(PIN_8, PWM_CH4, PwmPinA); -impl_pin!(PIN_9, PWM_CH4, PwmPinB); -impl_pin!(PIN_10, PWM_CH5, PwmPinA); -impl_pin!(PIN_11, PWM_CH5, PwmPinB); -impl_pin!(PIN_12, PWM_CH6, PwmPinA); -impl_pin!(PIN_13, PWM_CH6, PwmPinB); -impl_pin!(PIN_14, PWM_CH7, PwmPinA); -impl_pin!(PIN_15, PWM_CH7, PwmPinB); -impl_pin!(PIN_16, PWM_CH0, PwmPinA); -impl_pin!(PIN_17, PWM_CH0, PwmPinB); -impl_pin!(PIN_18, PWM_CH1, PwmPinA); -impl_pin!(PIN_19, PWM_CH1, PwmPinB); -impl_pin!(PIN_20, PWM_CH2, PwmPinA); -impl_pin!(PIN_21, PWM_CH2, PwmPinB); -impl_pin!(PIN_22, PWM_CH3, PwmPinA); -impl_pin!(PIN_23, PWM_CH3, PwmPinB); -impl_pin!(PIN_24, PWM_CH4, PwmPinA); -impl_pin!(PIN_25, PWM_CH4, PwmPinB); -impl_pin!(PIN_26, PWM_CH5, PwmPinA); -impl_pin!(PIN_27, PWM_CH5, PwmPinB); -impl_pin!(PIN_28, PWM_CH6, PwmPinA); -impl_pin!(PIN_29, PWM_CH6, PwmPinB); +impl_pin!(PIN_0, PWM_SLICE0, PwmPinA); +impl_pin!(PIN_1, PWM_SLICE0, PwmPinB); +impl_pin!(PIN_2, PWM_SLICE1, PwmPinA); +impl_pin!(PIN_3, PWM_SLICE1, PwmPinB); +impl_pin!(PIN_4, PWM_SLICE2, PwmPinA); +impl_pin!(PIN_5, PWM_SLICE2, PwmPinB); +impl_pin!(PIN_6, PWM_SLICE3, PwmPinA); +impl_pin!(PIN_7, PWM_SLICE3, PwmPinB); +impl_pin!(PIN_8, PWM_SLICE4, PwmPinA); +impl_pin!(PIN_9, PWM_SLICE4, PwmPinB); +impl_pin!(PIN_10, PWM_SLICE5, PwmPinA); +impl_pin!(PIN_11, PWM_SLICE5, PwmPinB); +impl_pin!(PIN_12, PWM_SLICE6, PwmPinA); +impl_pin!(PIN_13, PWM_SLICE6, PwmPinB); +impl_pin!(PIN_14, PWM_SLICE7, PwmPinA); +impl_pin!(PIN_15, PWM_SLICE7, PwmPinB); +impl_pin!(PIN_16, PWM_SLICE0, PwmPinA); +impl_pin!(PIN_17, PWM_SLICE0, PwmPinB); +impl_pin!(PIN_18, PWM_SLICE1, PwmPinA); +impl_pin!(PIN_19, PWM_SLICE1, PwmPinB); +impl_pin!(PIN_20, PWM_SLICE2, PwmPinA); +impl_pin!(PIN_21, PWM_SLICE2, PwmPinB); +impl_pin!(PIN_22, PWM_SLICE3, PwmPinA); +impl_pin!(PIN_23, PWM_SLICE3, PwmPinB); +impl_pin!(PIN_24, PWM_SLICE4, PwmPinA); +impl_pin!(PIN_25, PWM_SLICE4, PwmPinB); +impl_pin!(PIN_26, PWM_SLICE5, PwmPinA); +impl_pin!(PIN_27, PWM_SLICE5, PwmPinB); +impl_pin!(PIN_28, PWM_SLICE6, PwmPinA); +impl_pin!(PIN_29, PWM_SLICE6, PwmPinB); diff --git a/examples/rp/src/bin/pio_hd44780.rs b/examples/rp/src/bin/pio_hd44780.rs index 3fab7b5f2..6c02630e0 100644 --- a/examples/rp/src/bin/pio_hd44780.rs +++ b/examples/rp/src/bin/pio_hd44780.rs @@ -35,7 +35,7 @@ async fn main(_spawner: Spawner) { // allowing direct connection of the display to the RP2040 without level shifters. let p = embassy_rp::init(Default::default()); - let _pwm = Pwm::new_output_b(p.PWM_CH7, p.PIN_15, { + let _pwm = Pwm::new_output_b(p.PWM_SLICE7, p.PIN_15, { let mut c = pwm::Config::default(); c.divider = 125.into(); c.top = 100; diff --git a/examples/rp/src/bin/pwm.rs b/examples/rp/src/bin/pwm.rs index 4fb62546d..26e233260 100644 --- a/examples/rp/src/bin/pwm.rs +++ b/examples/rp/src/bin/pwm.rs @@ -18,7 +18,7 @@ async fn main(_spawner: Spawner) { let mut c: Config = Default::default(); c.top = 0x8000; c.compare_b = 8; - let mut pwm = Pwm::new_output_b(p.PWM_CH4, p.PIN_25, c.clone()); + let mut pwm = Pwm::new_output_b(p.PWM_SLICE4, p.PIN_25, c.clone()); loop { info!("current LED duty cycle: {}/32768", c.compare_b); diff --git a/examples/rp/src/bin/pwm_input.rs b/examples/rp/src/bin/pwm_input.rs index e7bcbfbd4..0652dc42b 100644 --- a/examples/rp/src/bin/pwm_input.rs +++ b/examples/rp/src/bin/pwm_input.rs @@ -14,7 +14,7 @@ async fn main(_spawner: Spawner) { let p = embassy_rp::init(Default::default()); let cfg: Config = Default::default(); - let pwm = Pwm::new_input(p.PWM_CH2, p.PIN_5, InputMode::RisingEdge, cfg); + let pwm = Pwm::new_input(p.PWM_SLICE2, p.PIN_5, InputMode::RisingEdge, cfg); let mut ticker = Ticker::every(Duration::from_secs(1)); loop { diff --git a/tests/rp/src/bin/pwm.rs b/tests/rp/src/bin/pwm.rs index e71d9e610..4b02e5bab 100644 --- a/tests/rp/src/bin/pwm.rs +++ b/tests/rp/src/bin/pwm.rs @@ -28,7 +28,7 @@ async fn main(_spawner: Spawner) { // Test free-running clock { - let pwm = Pwm::new_free(&mut p.PWM_CH3, cfg.clone()); + let pwm = Pwm::new_free(&mut p.PWM_SLICE3, cfg.clone()); cortex_m::asm::delay(125); let ctr = pwm.counter(); assert!(ctr > 0); @@ -46,7 +46,7 @@ async fn main(_spawner: Spawner) { // Test output from A { let pin1 = Input::new(&mut p9, Pull::None); - let _pwm = Pwm::new_output_a(&mut p.PWM_CH3, &mut p6, cfg.clone()); + let _pwm = Pwm::new_output_a(&mut p.PWM_SLICE3, &mut p6, cfg.clone()); Timer::after_millis(1).await; assert_eq!(pin1.is_low(), invert_a); Timer::after_millis(5).await; @@ -60,7 +60,7 @@ async fn main(_spawner: Spawner) { // Test output from B { let pin2 = Input::new(&mut p11, Pull::None); - let _pwm = Pwm::new_output_b(&mut p.PWM_CH3, &mut p7, cfg.clone()); + let _pwm = Pwm::new_output_b(&mut p.PWM_SLICE3, &mut p7, cfg.clone()); Timer::after_millis(1).await; assert_ne!(pin2.is_low(), invert_a); Timer::after_millis(5).await; @@ -75,7 +75,7 @@ async fn main(_spawner: Spawner) { { let pin1 = Input::new(&mut p9, Pull::None); let pin2 = Input::new(&mut p11, Pull::None); - let _pwm = Pwm::new_output_ab(&mut p.PWM_CH3, &mut p6, &mut p7, cfg.clone()); + let _pwm = Pwm::new_output_ab(&mut p.PWM_SLICE3, &mut p6, &mut p7, cfg.clone()); Timer::after_millis(1).await; assert_eq!(pin1.is_low(), invert_a); assert_ne!(pin2.is_low(), invert_a); @@ -94,7 +94,7 @@ async fn main(_spawner: Spawner) { // Test level-gated { let mut pin2 = Output::new(&mut p11, Level::Low); - let pwm = Pwm::new_input(&mut p.PWM_CH3, &mut p7, InputMode::Level, cfg.clone()); + let pwm = Pwm::new_input(&mut p.PWM_SLICE3, &mut p7, InputMode::Level, cfg.clone()); assert_eq!(pwm.counter(), 0); Timer::after_millis(5).await; assert_eq!(pwm.counter(), 0); @@ -110,7 +110,7 @@ async fn main(_spawner: Spawner) { // Test rising-gated { let mut pin2 = Output::new(&mut p11, Level::Low); - let pwm = Pwm::new_input(&mut p.PWM_CH3, &mut p7, InputMode::RisingEdge, cfg.clone()); + let pwm = Pwm::new_input(&mut p.PWM_SLICE3, &mut p7, InputMode::RisingEdge, cfg.clone()); assert_eq!(pwm.counter(), 0); Timer::after_millis(5).await; assert_eq!(pwm.counter(), 0); @@ -125,7 +125,7 @@ async fn main(_spawner: Spawner) { // Test falling-gated { let mut pin2 = Output::new(&mut p11, Level::High); - let pwm = Pwm::new_input(&mut p.PWM_CH3, &mut p7, InputMode::FallingEdge, cfg.clone()); + let pwm = Pwm::new_input(&mut p.PWM_SLICE3, &mut p7, InputMode::FallingEdge, cfg.clone()); assert_eq!(pwm.counter(), 0); Timer::after_millis(5).await; assert_eq!(pwm.counter(), 0); From 7b9546c9c8206147d1d79ba34ac19e97bb735c57 Mon Sep 17 00:00:00 2001 From: Alexandru RADOVICI Date: Tue, 2 Apr 2024 19:42:37 +0300 Subject: [PATCH 54/62] rename the Channel trait to Slice and the PwmPin to PwmChannel --- embassy-rp/src/pwm.rs | 116 +++++++++++++++++++++--------------------- 1 file changed, 58 insertions(+), 58 deletions(-) diff --git a/embassy-rp/src/pwm.rs b/embassy-rp/src/pwm.rs index 3b980108a..2a6772043 100644 --- a/embassy-rp/src/pwm.rs +++ b/embassy-rp/src/pwm.rs @@ -129,7 +129,7 @@ impl<'d, T: Channel> Pwm<'d, T> { #[inline] pub fn new_output_a( inner: impl Peripheral

+ 'd, - a: impl Peripheral

> + 'd, + a: impl Peripheral

> + 'd, config: Config, ) -> Self { into_ref!(a); @@ -140,7 +140,7 @@ impl<'d, T: Channel> Pwm<'d, T> { #[inline] pub fn new_output_b( inner: impl Peripheral

+ 'd, - b: impl Peripheral

> + 'd, + b: impl Peripheral

> + 'd, config: Config, ) -> Self { into_ref!(b); @@ -151,8 +151,8 @@ impl<'d, T: Channel> Pwm<'d, T> { #[inline] pub fn new_output_ab( inner: impl Peripheral

+ 'd, - a: impl Peripheral

> + 'd, - b: impl Peripheral

> + 'd, + a: impl Peripheral

> + 'd, + b: impl Peripheral

> + 'd, config: Config, ) -> Self { into_ref!(a, b); @@ -163,7 +163,7 @@ impl<'d, T: Channel> Pwm<'d, T> { #[inline] pub fn new_input( inner: impl Peripheral

+ 'd, - b: impl Peripheral

> + 'd, + b: impl Peripheral

> + 'd, mode: InputMode, config: Config, ) -> Self { @@ -175,8 +175,8 @@ impl<'d, T: Channel> Pwm<'d, T> { #[inline] pub fn new_output_input( inner: impl Peripheral

+ 'd, - a: impl Peripheral

> + 'd, - b: impl Peripheral

> + 'd, + a: impl Peripheral

> + 'd, + b: impl Peripheral

> + 'd, mode: InputMode, config: Config, ) -> Self { @@ -301,24 +301,24 @@ impl<'d, T: Channel> Drop for Pwm<'d, T> { } mod sealed { - pub trait Channel {} + pub trait Slice {} } -/// PWM Channel. -pub trait Channel: Peripheral

+ sealed::Channel + Sized + 'static { - /// Channel number. +/// PWM Slice. +pub trait Slice: Peripheral

+ sealed::Channel + Sized + 'static { + /// Slice number. fn number(&self) -> u8; - /// Channel register block. + /// Slice register block. fn regs(&self) -> pac::pwm::Channel { pac::PWM.ch(self.number() as _) } } -macro_rules! channel { +macro_rules! slice { ($name:ident, $num:expr) => { - impl sealed::Channel for peripherals::$name {} - impl Channel for peripherals::$name { + impl sealed::Slice for peripherals::$name {} + impl Slice for peripherals::$name { fn number(&self) -> u8 { $num } @@ -326,53 +326,53 @@ macro_rules! channel { }; } -channel!(PWM_SLICE0, 0); -channel!(PWM_SLICE1, 1); -channel!(PWM_SLICE2, 2); -channel!(PWM_SLICE3, 3); -channel!(PWM_SLICE4, 4); -channel!(PWM_SLICE5, 5); -channel!(PWM_SLICE6, 6); -channel!(PWM_SLICE7, 7); +slice!(PWM_SLICE0, 0); +slice!(PWM_SLICE1, 1); +slice!(PWM_SLICE2, 2); +slice!(PWM_SLICE3, 3); +slice!(PWM_SLICE4, 4); +slice!(PWM_SLICE5, 5); +slice!(PWM_SLICE6, 6); +slice!(PWM_SLICE7, 7); -/// PWM Pin A. -pub trait PwmPinA: GpioPin {} -/// PWM Pin B. -pub trait PwmPinB: GpioPin {} +/// PWM Channel A. +pub trait PwmChannelA: GpioPin {} +/// PWM Channel B. +pub trait PwmChannelB: GpioPin {} -macro_rules! impl_pin { +macro_rules! impl_channel { ($pin:ident, $channel:ident, $kind:ident) => { impl $kind for peripherals::$pin {} }; } -impl_pin!(PIN_0, PWM_SLICE0, PwmPinA); -impl_pin!(PIN_1, PWM_SLICE0, PwmPinB); -impl_pin!(PIN_2, PWM_SLICE1, PwmPinA); -impl_pin!(PIN_3, PWM_SLICE1, PwmPinB); -impl_pin!(PIN_4, PWM_SLICE2, PwmPinA); -impl_pin!(PIN_5, PWM_SLICE2, PwmPinB); -impl_pin!(PIN_6, PWM_SLICE3, PwmPinA); -impl_pin!(PIN_7, PWM_SLICE3, PwmPinB); -impl_pin!(PIN_8, PWM_SLICE4, PwmPinA); -impl_pin!(PIN_9, PWM_SLICE4, PwmPinB); -impl_pin!(PIN_10, PWM_SLICE5, PwmPinA); -impl_pin!(PIN_11, PWM_SLICE5, PwmPinB); -impl_pin!(PIN_12, PWM_SLICE6, PwmPinA); -impl_pin!(PIN_13, PWM_SLICE6, PwmPinB); -impl_pin!(PIN_14, PWM_SLICE7, PwmPinA); -impl_pin!(PIN_15, PWM_SLICE7, PwmPinB); -impl_pin!(PIN_16, PWM_SLICE0, PwmPinA); -impl_pin!(PIN_17, PWM_SLICE0, PwmPinB); -impl_pin!(PIN_18, PWM_SLICE1, PwmPinA); -impl_pin!(PIN_19, PWM_SLICE1, PwmPinB); -impl_pin!(PIN_20, PWM_SLICE2, PwmPinA); -impl_pin!(PIN_21, PWM_SLICE2, PwmPinB); -impl_pin!(PIN_22, PWM_SLICE3, PwmPinA); -impl_pin!(PIN_23, PWM_SLICE3, PwmPinB); -impl_pin!(PIN_24, PWM_SLICE4, PwmPinA); -impl_pin!(PIN_25, PWM_SLICE4, PwmPinB); -impl_pin!(PIN_26, PWM_SLICE5, PwmPinA); -impl_pin!(PIN_27, PWM_SLICE5, PwmPinB); -impl_pin!(PIN_28, PWM_SLICE6, PwmPinA); -impl_pin!(PIN_29, PWM_SLICE6, PwmPinB); +impl_channel!(PIN_0, PWM_SLICE0, PwmChannelA); +impl_channel!(PIN_1, PWM_SLICE0, PwmChannelB); +impl_channel!(PIN_2, PWM_SLICE1, PwmChannelA); +impl_channel!(PIN_3, PWM_SLICE1, PwmChannelB); +impl_channel!(PIN_4, PWM_SLICE2, PwmChannelA); +impl_channel!(PIN_5, PWM_SLICE2, PwmChannelB); +impl_channel!(PIN_6, PWM_SLICE3, PwmChannelA); +impl_channel!(PIN_7, PWM_SLICE3, PwmChannelB); +impl_channel!(PIN_8, PWM_SLICE4, PwmChannelA); +impl_channel!(PIN_9, PWM_SLICE4, PwmChannelB); +impl_channel!(PIN_10, PWM_SLICE5, PwmChannelA); +impl_channel!(PIN_11, PWM_SLICE5, PwmChannelB); +impl_channel!(PIN_12, PWM_SLICE6, PwmChannelA); +impl_channel!(PIN_13, PWM_SLICE6, PwmChannelB); +impl_channel!(PIN_14, PWM_SLICE7, PwmChannelA); +impl_channel!(PIN_15, PWM_SLICE7, PwmChannelB); +impl_channel!(PIN_16, PWM_SLICE0, PwmChannelA); +impl_channel!(PIN_17, PWM_SLICE0, PwmChannelB); +impl_channel!(PIN_18, PWM_SLICE1, PwmChannelA); +impl_channel!(PIN_19, PWM_SLICE1, PwmChannelB); +impl_channel!(PIN_20, PWM_SLICE2, PwmChannelA); +impl_channel!(PIN_21, PWM_SLICE2, PwmChannelB); +impl_channel!(PIN_22, PWM_SLICE3, PwmChannelA); +impl_channel!(PIN_23, PWM_SLICE3, PwmChannelB); +impl_channel!(PIN_24, PWM_SLICE4, PwmChannelA); +impl_channel!(PIN_25, PWM_SLICE4, PwmChannelB); +impl_channel!(PIN_26, PWM_SLICE5, PwmChannelA); +impl_channel!(PIN_27, PWM_SLICE5, PwmChannelB); +impl_channel!(PIN_28, PWM_SLICE6, PwmChannelA); +impl_channel!(PIN_29, PWM_SLICE6, PwmChannelB); From d35572c11f11980bebfda4d8738f46202c3384c3 Mon Sep 17 00:00:00 2001 From: Alexandru RADOVICI Date: Tue, 2 Apr 2024 19:50:13 +0300 Subject: [PATCH 55/62] rename pwm channels to pwm slices, including in documentation --- embassy-rp/src/pwm.rs | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/embassy-rp/src/pwm.rs b/embassy-rp/src/pwm.rs index 2a6772043..d0e89f250 100644 --- a/embassy-rp/src/pwm.rs +++ b/embassy-rp/src/pwm.rs @@ -82,13 +82,13 @@ impl From for Divmode { } /// PWM driver. -pub struct Pwm<'d, T: Channel> { +pub struct Pwm<'d, T: Slice> { inner: PeripheralRef<'d, T>, pin_a: Option>, pin_b: Option>, } -impl<'d, T: Channel> Pwm<'d, T> { +impl<'d, T: Slice> Pwm<'d, T> { fn new_inner( inner: impl Peripheral

+ 'd, a: Option>, @@ -265,18 +265,18 @@ impl<'d, T: Channel> Pwm<'d, T> { } } -/// Batch representation of PWM channels. +/// Batch representation of PWM slices. pub struct PwmBatch(u32); impl PwmBatch { #[inline] - /// Enable a PWM channel in this batch. - pub fn enable(&mut self, pwm: &Pwm<'_, impl Channel>) { + /// Enable a PWM slice in this batch. + pub fn enable(&mut self, pwm: &Pwm<'_, impl Slice>) { self.0 |= pwm.bit(); } #[inline] - /// Enable channels in this batch in a PWM. + /// Enable slices in this batch in a PWM. pub fn set_enabled(enabled: bool, batch: impl FnOnce(&mut PwmBatch)) { let mut en = PwmBatch(0); batch(&mut en); @@ -288,7 +288,7 @@ impl PwmBatch { } } -impl<'d, T: Channel> Drop for Pwm<'d, T> { +impl<'d, T: Slice> Drop for Pwm<'d, T> { fn drop(&mut self) { self.inner.regs().csr().write_clear(|w| w.set_en(false)); if let Some(pin) = &self.pin_a { @@ -305,7 +305,7 @@ mod sealed { } /// PWM Slice. -pub trait Slice: Peripheral

+ sealed::Channel + Sized + 'static { +pub trait Slice: Peripheral

+ sealed::Slice + Sized + 'static { /// Slice number. fn number(&self) -> u8; From 7c36e8dbc7867c7fef0e74816b482a577ae2dc2d Mon Sep 17 00:00:00 2001 From: Alexandru RADOVICI Date: Tue, 2 Apr 2024 20:05:46 +0300 Subject: [PATCH 56/62] rename pins data type and the macro --- embassy-rp/src/pwm.rs | 80 +++++++++++++++++++++---------------------- 1 file changed, 40 insertions(+), 40 deletions(-) diff --git a/embassy-rp/src/pwm.rs b/embassy-rp/src/pwm.rs index d0e89f250..5aab3ff4f 100644 --- a/embassy-rp/src/pwm.rs +++ b/embassy-rp/src/pwm.rs @@ -129,7 +129,7 @@ impl<'d, T: Slice> Pwm<'d, T> { #[inline] pub fn new_output_a( inner: impl Peripheral

+ 'd, - a: impl Peripheral

> + 'd, + a: impl Peripheral

> + 'd, config: Config, ) -> Self { into_ref!(a); @@ -140,7 +140,7 @@ impl<'d, T: Slice> Pwm<'d, T> { #[inline] pub fn new_output_b( inner: impl Peripheral

+ 'd, - b: impl Peripheral

> + 'd, + b: impl Peripheral

> + 'd, config: Config, ) -> Self { into_ref!(b); @@ -151,8 +151,8 @@ impl<'d, T: Slice> Pwm<'d, T> { #[inline] pub fn new_output_ab( inner: impl Peripheral

+ 'd, - a: impl Peripheral

> + 'd, - b: impl Peripheral

> + 'd, + a: impl Peripheral

> + 'd, + b: impl Peripheral

> + 'd, config: Config, ) -> Self { into_ref!(a, b); @@ -163,7 +163,7 @@ impl<'d, T: Slice> Pwm<'d, T> { #[inline] pub fn new_input( inner: impl Peripheral

+ 'd, - b: impl Peripheral

> + 'd, + b: impl Peripheral

> + 'd, mode: InputMode, config: Config, ) -> Self { @@ -175,8 +175,8 @@ impl<'d, T: Slice> Pwm<'d, T> { #[inline] pub fn new_output_input( inner: impl Peripheral

+ 'd, - a: impl Peripheral

> + 'd, - b: impl Peripheral

> + 'd, + a: impl Peripheral

> + 'd, + b: impl Peripheral

> + 'd, mode: InputMode, config: Config, ) -> Self { @@ -336,43 +336,43 @@ slice!(PWM_SLICE6, 6); slice!(PWM_SLICE7, 7); /// PWM Channel A. -pub trait PwmChannelA: GpioPin {} +pub trait ChannelAPin: GpioPin {} /// PWM Channel B. -pub trait PwmChannelB: GpioPin {} +pub trait ChannelBPin: GpioPin {} -macro_rules! impl_channel { +macro_rules! impl_pin { ($pin:ident, $channel:ident, $kind:ident) => { impl $kind for peripherals::$pin {} }; } -impl_channel!(PIN_0, PWM_SLICE0, PwmChannelA); -impl_channel!(PIN_1, PWM_SLICE0, PwmChannelB); -impl_channel!(PIN_2, PWM_SLICE1, PwmChannelA); -impl_channel!(PIN_3, PWM_SLICE1, PwmChannelB); -impl_channel!(PIN_4, PWM_SLICE2, PwmChannelA); -impl_channel!(PIN_5, PWM_SLICE2, PwmChannelB); -impl_channel!(PIN_6, PWM_SLICE3, PwmChannelA); -impl_channel!(PIN_7, PWM_SLICE3, PwmChannelB); -impl_channel!(PIN_8, PWM_SLICE4, PwmChannelA); -impl_channel!(PIN_9, PWM_SLICE4, PwmChannelB); -impl_channel!(PIN_10, PWM_SLICE5, PwmChannelA); -impl_channel!(PIN_11, PWM_SLICE5, PwmChannelB); -impl_channel!(PIN_12, PWM_SLICE6, PwmChannelA); -impl_channel!(PIN_13, PWM_SLICE6, PwmChannelB); -impl_channel!(PIN_14, PWM_SLICE7, PwmChannelA); -impl_channel!(PIN_15, PWM_SLICE7, PwmChannelB); -impl_channel!(PIN_16, PWM_SLICE0, PwmChannelA); -impl_channel!(PIN_17, PWM_SLICE0, PwmChannelB); -impl_channel!(PIN_18, PWM_SLICE1, PwmChannelA); -impl_channel!(PIN_19, PWM_SLICE1, PwmChannelB); -impl_channel!(PIN_20, PWM_SLICE2, PwmChannelA); -impl_channel!(PIN_21, PWM_SLICE2, PwmChannelB); -impl_channel!(PIN_22, PWM_SLICE3, PwmChannelA); -impl_channel!(PIN_23, PWM_SLICE3, PwmChannelB); -impl_channel!(PIN_24, PWM_SLICE4, PwmChannelA); -impl_channel!(PIN_25, PWM_SLICE4, PwmChannelB); -impl_channel!(PIN_26, PWM_SLICE5, PwmChannelA); -impl_channel!(PIN_27, PWM_SLICE5, PwmChannelB); -impl_channel!(PIN_28, PWM_SLICE6, PwmChannelA); -impl_channel!(PIN_29, PWM_SLICE6, PwmChannelB); +impl_pin!(PIN_0, PWM_SLICE0, ChannelAPin); +impl_pin!(PIN_1, PWM_SLICE0, ChannelBPin); +impl_pin!(PIN_2, PWM_SLICE1, ChannelAPin); +impl_pin!(PIN_3, PWM_SLICE1, ChannelBPin); +impl_pin!(PIN_4, PWM_SLICE2, ChannelAPin); +impl_pin!(PIN_5, PWM_SLICE2, ChannelBPin); +impl_pin!(PIN_6, PWM_SLICE3, ChannelAPin); +impl_pin!(PIN_7, PWM_SLICE3, ChannelBPin); +impl_pin!(PIN_8, PWM_SLICE4, ChannelAPin); +impl_pin!(PIN_9, PWM_SLICE4, ChannelBPin); +impl_pin!(PIN_10, PWM_SLICE5, ChannelAPin); +impl_pin!(PIN_11, PWM_SLICE5, ChannelBPin); +impl_pin!(PIN_12, PWM_SLICE6, ChannelAPin); +impl_pin!(PIN_13, PWM_SLICE6, ChannelBPin); +impl_pin!(PIN_14, PWM_SLICE7, ChannelAPin); +impl_pin!(PIN_15, PWM_SLICE7, ChannelBPin); +impl_pin!(PIN_16, PWM_SLICE0, ChannelAPin); +impl_pin!(PIN_17, PWM_SLICE0, ChannelBPin); +impl_pin!(PIN_18, PWM_SLICE1, ChannelAPin); +impl_pin!(PIN_19, PWM_SLICE1, ChannelBPin); +impl_pin!(PIN_20, PWM_SLICE2, ChannelAPin); +impl_pin!(PIN_21, PWM_SLICE2, ChannelBPin); +impl_pin!(PIN_22, PWM_SLICE3, ChannelAPin); +impl_pin!(PIN_23, PWM_SLICE3, ChannelBPin); +impl_pin!(PIN_24, PWM_SLICE4, ChannelAPin); +impl_pin!(PIN_25, PWM_SLICE4, ChannelBPin); +impl_pin!(PIN_26, PWM_SLICE5, ChannelAPin); +impl_pin!(PIN_27, PWM_SLICE5, ChannelBPin); +impl_pin!(PIN_28, PWM_SLICE6, ChannelAPin); +impl_pin!(PIN_29, PWM_SLICE6, ChannelBPin); From 1e399fbf9d716297acd9281be5c7d6546392e805 Mon Sep 17 00:00:00 2001 From: Boris Faure Date: Tue, 2 Apr 2024 22:16:11 +0200 Subject: [PATCH 57/62] stm32: fix typo in doc --- embassy-stm32/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/embassy-stm32/src/lib.rs b/embassy-stm32/src/lib.rs index ab6ef8ef4..8b826e5ac 100644 --- a/embassy-stm32/src/lib.rs +++ b/embassy-stm32/src/lib.rs @@ -168,7 +168,7 @@ pub struct Config { /// Enable debug during sleep and stop. /// - /// May incrase power consumption. Defaults to true. + /// May increase power consumption. Defaults to true. #[cfg(dbgmcu)] pub enable_debug_during_sleep: bool, From 54be08afe44f1902f042d3228a1df4aa24f56a6c Mon Sep 17 00:00:00 2001 From: David Zwart Date: Wed, 3 Apr 2024 22:24:09 +0200 Subject: [PATCH 58/62] Update faq.adoc with memory.x definition helping hand --- docs/modules/ROOT/pages/faq.adoc | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/docs/modules/ROOT/pages/faq.adoc b/docs/modules/ROOT/pages/faq.adoc index 6b5e6d009..b5d702040 100644 --- a/docs/modules/ROOT/pages/faq.adoc +++ b/docs/modules/ROOT/pages/faq.adoc @@ -208,3 +208,24 @@ Tools like `cargo size` and `cargo nm` can tell you the size of any globals or o === For Max Stack Usage Check out link:https://github.com/Dirbaio/cargo-call-stack/[`cargo-call-stack`] for statically calculating worst-case stack usage. There are some caveats and inaccuracies possible with this, but this is a good way to get the general idea. See link:https://github.com/dirbaio/cargo-call-stack#known-limitations[the README] for more details. + +== The memory definition for my STM chip seems wrong, how do I define a `memory.x` file? + +It could happen that your project compiles, flashes but fails to run. The following situation can be true for your setup: + +The `memory.x` is generated automatically when enabling the `memory-x` feature on the `embassy-stm32` crate in the `Cargo.toml` file. +This, in turn, uses `stm32-metapac` to generate the `memory.x` file for you. Unfortunately, more often than not this memory definition is not correct. + +You can override this by adding your own `memory.x` file. Such a file could look like this: +``` +MEMORY +{ + FLASH (rx) : ORIGIN = 0x08000000, LENGTH = 1024K + RAM (xrw) : ORIGIN = 0x20000000, LENGTH = 320K +} + +_stack_start = ORIGIN(RAM) + LENGTH(RAM); +``` + +Please refer to the STM32 documentation for the specific values suitable for your board and setup. The STM32 Cube examples often contain a linker script `.ld` file. +Look for the `MEMORY` section and try to determine the FLASH and RAM sizes and section start. From 7aef047defd56c11c852156fec9f579de496af31 Mon Sep 17 00:00:00 2001 From: David Zwart Date: Wed, 3 Apr 2024 22:32:28 +0200 Subject: [PATCH 59/62] Update faq.adoc github link --- docs/modules/ROOT/pages/faq.adoc | 2 ++ 1 file changed, 2 insertions(+) diff --git a/docs/modules/ROOT/pages/faq.adoc b/docs/modules/ROOT/pages/faq.adoc index b5d702040..4e09d9e2d 100644 --- a/docs/modules/ROOT/pages/faq.adoc +++ b/docs/modules/ROOT/pages/faq.adoc @@ -227,5 +227,7 @@ MEMORY _stack_start = ORIGIN(RAM) + LENGTH(RAM); ``` +If you find a case where the memory.x is wrong, please report it on [this Github issue](https://github.com/embassy-rs/stm32-data/issues/301) so other users are not caught by surprise. + Please refer to the STM32 documentation for the specific values suitable for your board and setup. The STM32 Cube examples often contain a linker script `.ld` file. Look for the `MEMORY` section and try to determine the FLASH and RAM sizes and section start. From 446965a903112a9a09f67213abe46b9836d2bb2f Mon Sep 17 00:00:00 2001 From: David Zwart Date: Wed, 3 Apr 2024 22:33:21 +0200 Subject: [PATCH 60/62] Update faq.adoc --- docs/modules/ROOT/pages/faq.adoc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/modules/ROOT/pages/faq.adoc b/docs/modules/ROOT/pages/faq.adoc index 4e09d9e2d..c6b893de5 100644 --- a/docs/modules/ROOT/pages/faq.adoc +++ b/docs/modules/ROOT/pages/faq.adoc @@ -227,7 +227,7 @@ MEMORY _stack_start = ORIGIN(RAM) + LENGTH(RAM); ``` -If you find a case where the memory.x is wrong, please report it on [this Github issue](https://github.com/embassy-rs/stm32-data/issues/301) so other users are not caught by surprise. - Please refer to the STM32 documentation for the specific values suitable for your board and setup. The STM32 Cube examples often contain a linker script `.ld` file. Look for the `MEMORY` section and try to determine the FLASH and RAM sizes and section start. + +If you find a case where the memory.x is wrong, please report it on [this Github issue](https://github.com/embassy-rs/stm32-data/issues/301) so other users are not caught by surprise. From c9acebf783c64784fe6b659a94b40fa080b6fbe8 Mon Sep 17 00:00:00 2001 From: Alex Moon Date: Wed, 3 Apr 2024 19:13:57 -0400 Subject: [PATCH 61/62] Fix `FairSemaphore` bugs - `acquire` and `acquire_all` futures were `!Send`, even for `M: RawMutex + Send` due to the captured `Cell`. - If multiple `acquire` tasks were queued, waking the first would not wake the second, even if there were permits remaining after the first `acquire` completed. --- embassy-sync/src/semaphore.rs | 136 +++++++++++++++++++++++++--------- 1 file changed, 102 insertions(+), 34 deletions(-) diff --git a/embassy-sync/src/semaphore.rs b/embassy-sync/src/semaphore.rs index 52c468b4a..d30eee30b 100644 --- a/embassy-sync/src/semaphore.rs +++ b/embassy-sync/src/semaphore.rs @@ -1,8 +1,7 @@ //! A synchronization primitive for controlling access to a pool of resources. use core::cell::{Cell, RefCell}; use core::convert::Infallible; -use core::future::poll_fn; -use core::mem::MaybeUninit; +use core::future::{poll_fn, Future}; use core::task::{Poll, Waker}; use heapless::Deque; @@ -258,9 +257,9 @@ where &self, permits: usize, acquire_all: bool, - cx: Option<(&Cell>, &Waker)>, + cx: Option<(&mut Option, &Waker)>, ) -> Poll, WaitQueueFull>> { - let ticket = cx.as_ref().map(|(cell, _)| cell.get()).unwrap_or(None); + let ticket = cx.as_ref().map(|(x, _)| **x).unwrap_or(None); self.state.lock(|cell| { let mut state = cell.borrow_mut(); if let Some(permits) = state.take(ticket, permits, acquire_all) { @@ -268,10 +267,10 @@ where semaphore: self, permits, })) - } else if let Some((cell, waker)) = cx { + } else if let Some((ticket_ref, waker)) = cx { match state.register(ticket, waker) { Ok(ticket) => { - cell.set(Some(ticket)); + *ticket_ref = Some(ticket); Poll::Pending } Err(err) => Poll::Ready(Err(err)), @@ -291,10 +290,12 @@ pub struct WaitQueueFull; impl Semaphore for FairSemaphore { type Error = WaitQueueFull; - async fn acquire(&self, permits: usize) -> Result, Self::Error> { - let ticket = Cell::new(None); - let _guard = OnDrop::new(|| self.state.lock(|cell| cell.borrow_mut().cancel(ticket.get()))); - poll_fn(|cx| self.poll_acquire(permits, false, Some((&ticket, cx.waker())))).await + fn acquire(&self, permits: usize) -> impl Future, Self::Error>> { + FairAcquire { + sema: self, + permits, + ticket: None, + } } fn try_acquire(&self, permits: usize) -> Option> { @@ -304,10 +305,12 @@ impl Semaphore for FairSemaphore { } } - async fn acquire_all(&self, min: usize) -> Result, Self::Error> { - let ticket = Cell::new(None); - let _guard = OnDrop::new(|| self.state.lock(|cell| cell.borrow_mut().cancel(ticket.get()))); - poll_fn(|cx| self.poll_acquire(min, true, Some((&ticket, cx.waker())))).await + fn acquire_all(&self, min: usize) -> impl Future, Self::Error>> { + FairAcquireAll { + sema: self, + min, + ticket: None, + } } fn try_acquire_all(&self, min: usize) -> Option> { @@ -338,6 +341,52 @@ impl Semaphore for FairSemaphore { } } +struct FairAcquire<'a, M: RawMutex, const N: usize> { + sema: &'a FairSemaphore, + permits: usize, + ticket: Option, +} + +impl<'a, M: RawMutex, const N: usize> Drop for FairAcquire<'a, M, N> { + fn drop(&mut self) { + self.sema + .state + .lock(|cell| cell.borrow_mut().cancel(self.ticket.take())); + } +} + +impl<'a, M: RawMutex, const N: usize> core::future::Future for FairAcquire<'a, M, N> { + type Output = Result>, WaitQueueFull>; + + fn poll(mut self: core::pin::Pin<&mut Self>, cx: &mut core::task::Context<'_>) -> Poll { + self.sema + .poll_acquire(self.permits, false, Some((&mut self.ticket, cx.waker()))) + } +} + +struct FairAcquireAll<'a, M: RawMutex, const N: usize> { + sema: &'a FairSemaphore, + min: usize, + ticket: Option, +} + +impl<'a, M: RawMutex, const N: usize> Drop for FairAcquireAll<'a, M, N> { + fn drop(&mut self) { + self.sema + .state + .lock(|cell| cell.borrow_mut().cancel(self.ticket.take())); + } +} + +impl<'a, M: RawMutex, const N: usize> core::future::Future for FairAcquireAll<'a, M, N> { + type Output = Result>, WaitQueueFull>; + + fn poll(mut self: core::pin::Pin<&mut Self>, cx: &mut core::task::Context<'_>) -> Poll { + self.sema + .poll_acquire(self.min, true, Some((&mut self.ticket, cx.waker()))) + } +} + struct FairSemaphoreState { permits: usize, next_ticket: usize, @@ -406,6 +455,9 @@ impl FairSemaphoreState { if ticket.is_some() { self.pop(); + if self.permits > 0 { + self.wake(); + } } Some(permits) @@ -432,25 +484,6 @@ impl FairSemaphoreState { } } -/// A type to delay the drop handler invocation. -#[must_use = "to delay the drop handler invocation to the end of the scope"] -struct OnDrop { - f: MaybeUninit, -} - -impl OnDrop { - /// Create a new instance. - pub fn new(f: F) -> Self { - Self { f: MaybeUninit::new(f) } - } -} - -impl Drop for OnDrop { - fn drop(&mut self) { - unsafe { self.f.as_ptr().read()() } - } -} - #[cfg(test)] mod tests { mod greedy { @@ -574,11 +607,16 @@ mod tests { mod fair { use core::pin::pin; + use core::time::Duration; + use futures_executor::ThreadPool; + use futures_timer::Delay; use futures_util::poll; + use futures_util::task::SpawnExt; + use static_cell::StaticCell; use super::super::*; - use crate::blocking_mutex::raw::NoopRawMutex; + use crate::blocking_mutex::raw::{CriticalSectionRawMutex, NoopRawMutex}; #[test] fn try_acquire() { @@ -700,5 +738,35 @@ mod tests { let c = poll!(c_fut.as_mut()); assert!(c.is_ready()); } + + #[futures_test::test] + async fn wakers() { + let executor = ThreadPool::new().unwrap(); + + static SEMAPHORE: StaticCell> = StaticCell::new(); + let semaphore = &*SEMAPHORE.init(FairSemaphore::new(3)); + + let a = semaphore.try_acquire(2); + assert!(a.is_some()); + + let b_task = executor + .spawn_with_handle(async move { semaphore.acquire(2).await }) + .unwrap(); + while semaphore.state.lock(|x| x.borrow().wakers.is_empty()) { + Delay::new(Duration::from_millis(50)).await; + } + + let c_task = executor + .spawn_with_handle(async move { semaphore.acquire(1).await }) + .unwrap(); + + core::mem::drop(a); + + let b = b_task.await.unwrap(); + assert_eq!(b.permits(), 2); + + let c = c_task.await.unwrap(); + assert_eq!(c.permits(), 1); + } } } From 8fbd21d2161caaf3d52ad60db1bb2fd0ea91e6f9 Mon Sep 17 00:00:00 2001 From: Dominic Date: Wed, 27 Mar 2024 16:30:32 +0100 Subject: [PATCH 62/62] Add multiprio example for stm32h7 inspired by stm32f4 --- examples/stm32h7/Cargo.toml | 4 +- examples/stm32h7/src/bin/multiprio.rs | 145 ++++++++++++++++++++++++++ 2 files changed, 147 insertions(+), 2 deletions(-) create mode 100644 examples/stm32h7/src/bin/multiprio.rs diff --git a/examples/stm32h7/Cargo.toml b/examples/stm32h7/Cargo.toml index d9ea2626d..84a89b378 100644 --- a/examples/stm32h7/Cargo.toml +++ b/examples/stm32h7/Cargo.toml @@ -6,9 +6,9 @@ license = "MIT OR Apache-2.0" [dependencies] # Change stm32h743bi to your chip name, if necessary. -embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["defmt", "stm32h743bi", "time-driver-any", "exti", "memory-x", "unstable-pac", "chrono"] } +embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["defmt", "stm32h743bi", "time-driver-tim2", "exti", "memory-x", "unstable-pac", "chrono"] } embassy-sync = { version = "0.5.0", path = "../../embassy-sync", features = ["defmt"] } -embassy-executor = { version = "0.5.0", path = "../../embassy-executor", features = ["task-arena-size-32768", "arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] } +embassy-executor = { version = "0.5.0", path = "../../embassy-executor", features = ["task-arena-size-32768", "arch-cortex-m", "executor-thread", "executor-interrupt", "defmt", "integrated-timers"] } embassy-time = { version = "0.3.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } embassy-net = { version = "0.4.0", path = "../../embassy-net", features = ["defmt", "tcp", "dhcpv4", "medium-ethernet", "proto-ipv6", "dns"] } embassy-usb = { version = "0.1.0", path = "../../embassy-usb", features = ["defmt"] } diff --git a/examples/stm32h7/src/bin/multiprio.rs b/examples/stm32h7/src/bin/multiprio.rs new file mode 100644 index 000000000..73f8dd092 --- /dev/null +++ b/examples/stm32h7/src/bin/multiprio.rs @@ -0,0 +1,145 @@ +//! This example showcases how to create multiple Executor instances to run tasks at +//! different priority levels. +//! +//! Low priority executor runs in thread mode (not interrupt), and uses `sev` for signaling +//! there's work in the queue, and `wfe` for waiting for work. +//! +//! Medium and high priority executors run in two interrupts with different priorities. +//! Signaling work is done by pending the interrupt. No "waiting" needs to be done explicitly, since +//! when there's work the interrupt will trigger and run the executor. +//! +//! Sample output below. Note that high priority ticks can interrupt everything else, and +//! medium priority computations can interrupt low priority computations, making them to appear +//! to take significantly longer time. +//! +//! ```not_rust +//! [med] Starting long computation +//! [med] done in 992 ms +//! [high] tick! +//! [low] Starting long computation +//! [med] Starting long computation +//! [high] tick! +//! [high] tick! +//! [med] done in 993 ms +//! [med] Starting long computation +//! [high] tick! +//! [high] tick! +//! [med] done in 993 ms +//! [low] done in 3972 ms +//! [med] Starting long computation +//! [high] tick! +//! [high] tick! +//! [med] done in 993 ms +//! ``` +//! +//! For comparison, try changing the code so all 3 tasks get spawned on the low priority executor. +//! You will get an output like the following. Note that no computation is ever interrupted. +//! +//! ```not_rust +//! [high] tick! +//! [med] Starting long computation +//! [med] done in 496 ms +//! [low] Starting long computation +//! [low] done in 992 ms +//! [med] Starting long computation +//! [med] done in 496 ms +//! [high] tick! +//! [low] Starting long computation +//! [low] done in 992 ms +//! [high] tick! +//! [med] Starting long computation +//! [med] done in 496 ms +//! [high] tick! +//! ``` +//! + +#![no_std] +#![no_main] + +use cortex_m_rt::entry; +use defmt::*; +use embassy_executor::{Executor, InterruptExecutor}; +use embassy_stm32::interrupt; +use embassy_stm32::interrupt::{InterruptExt, Priority}; +use embassy_time::{Instant, Timer}; +use static_cell::StaticCell; +use {defmt_rtt as _, panic_probe as _}; + +#[embassy_executor::task] +async fn run_high() { + loop { + info!(" [high] tick!"); + Timer::after_ticks(27374).await; + } +} + +#[embassy_executor::task] +async fn run_med() { + loop { + let start = Instant::now(); + info!(" [med] Starting long computation"); + + // Spin-wait to simulate a long CPU computation + cortex_m::asm::delay(128_000_000); // ~1 second + + let end = Instant::now(); + let ms = end.duration_since(start).as_ticks() / 33; + info!(" [med] done in {} ms", ms); + + Timer::after_ticks(23421).await; + } +} + +#[embassy_executor::task] +async fn run_low() { + loop { + let start = Instant::now(); + info!("[low] Starting long computation"); + + // Spin-wait to simulate a long CPU computation + cortex_m::asm::delay(256_000_000); // ~2 seconds + + let end = Instant::now(); + let ms = end.duration_since(start).as_ticks() / 33; + info!("[low] done in {} ms", ms); + + Timer::after_ticks(32983).await; + } +} + +static EXECUTOR_HIGH: InterruptExecutor = InterruptExecutor::new(); +static EXECUTOR_MED: InterruptExecutor = InterruptExecutor::new(); +static EXECUTOR_LOW: StaticCell = StaticCell::new(); + +#[interrupt] +unsafe fn UART4() { + EXECUTOR_HIGH.on_interrupt() +} + +#[interrupt] +unsafe fn UART5() { + EXECUTOR_MED.on_interrupt() +} + +#[entry] +fn main() -> ! { + info!("Hello World!"); + + let _p = embassy_stm32::init(Default::default()); + + // High-priority executor: UART4, priority level 6 + interrupt::UART4.set_priority(Priority::P6); + let spawner = EXECUTOR_HIGH.start(interrupt::UART4); + unwrap!(spawner.spawn(run_high())); + + // Medium-priority executor: UART5, priority level 7 + interrupt::UART5.set_priority(Priority::P7); + let spawner = EXECUTOR_MED.start(interrupt::UART5); + unwrap!(spawner.spawn(run_med())); + + // Low priority executor: runs in thread mode, using WFE/SEV + let executor = EXECUTOR_LOW.init(Executor::new()); + executor.run(|spawner| { + unwrap!(spawner.spawn(run_low())); + }); +}