From 26acee2902c519075bd3d5f98d2a1a498ee3d1a9 Mon Sep 17 00:00:00 2001 From: Peter Krull Date: Tue, 13 Feb 2024 14:50:26 +0100 Subject: [PATCH 001/361] Add initial implementation of `MultiSignal` sync primitive --- embassy-sync/src/lib.rs | 1 + embassy-sync/src/multi_signal.rs | 285 +++++++++++++++++++++++++++++++ 2 files changed, 286 insertions(+) create mode 100644 embassy-sync/src/multi_signal.rs diff --git a/embassy-sync/src/lib.rs b/embassy-sync/src/lib.rs index d88c76db5..f02985564 100644 --- a/embassy-sync/src/lib.rs +++ b/embassy-sync/src/lib.rs @@ -12,6 +12,7 @@ mod ring_buffer; pub mod blocking_mutex; pub mod channel; +pub mod multi_signal; pub mod mutex; pub mod pipe; pub mod priority_channel; diff --git a/embassy-sync/src/multi_signal.rs b/embassy-sync/src/multi_signal.rs new file mode 100644 index 000000000..db858f269 --- /dev/null +++ b/embassy-sync/src/multi_signal.rs @@ -0,0 +1,285 @@ +//! A synchronization primitive for passing the latest value to **multiple** tasks. +use core::{ + cell::RefCell, + marker::PhantomData, + ops::{Deref, DerefMut}, + pin::Pin, + task::{Context, Poll}, +}; + +use futures_util::Future; + +use crate::{ + blocking_mutex::{raw::RawMutex, Mutex}, + waitqueue::MultiWakerRegistration, +}; + +/// A `MultiSignal` is a single-slot signaling primitive, which can awake `N` separate [`Receiver`]s. +/// +/// Similar to a [`Signal`](crate::signal::Signal), except `MultiSignal` allows for multiple tasks to +/// `.await` the latest value, and all receive it. +/// +/// This is similar to a [`PubSubChannel`](crate::pubsub::PubSubChannel) with a buffer size of 1, except +/// "sending" to it (calling [`MultiSignal::write`]) will immediately overwrite the previous value instead +/// of waiting for the receivers to pop the previous value. +/// +/// `MultiSignal` is useful when a single task is responsible for updating a value or "state", which multiple other +/// tasks are interested in getting notified about changes to the latest value of. It is therefore fine for +/// [`Receiver`]s to "lose" stale values. +/// +/// Anyone with a reference to the MultiSignal can update or peek the value. MultiSignals are generally declared +/// as `static`s and then borrowed as required to either [`MultiSignal::peek`] the value or obtain a [`Receiver`] +/// with [`MultiSignal::receiver`] which has async methods. +/// ``` +/// +/// use futures_executor::block_on; +/// use embassy_sync::multi_signal::MultiSignal; +/// use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex; +/// +/// let f = async { +/// +/// static SOME_SIGNAL: MultiSignal = MultiSignal::new(0); +/// +/// // Obtain Receivers +/// let mut rcv0 = SOME_SIGNAL.receiver().unwrap(); +/// let mut rcv1 = SOME_SIGNAL.receiver().unwrap(); +/// assert!(SOME_SIGNAL.receiver().is_err()); +/// +/// SOME_SIGNAL.write(10); +/// +/// // Receive the new value +/// assert_eq!(rcv0.changed().await, 10); +/// assert_eq!(rcv1.try_changed(), Some(10)); +/// +/// // No update +/// assert_eq!(rcv0.try_changed(), None); +/// assert_eq!(rcv1.try_changed(), None); +/// +/// SOME_SIGNAL.write(20); +/// +/// // Receive new value with predicate +/// assert_eq!(rcv0.changed_and(|x|x>&10).await, 20); +/// assert_eq!(rcv1.try_changed_and(|x|x>&30), None); +/// +/// // Anyone can peek the current value +/// assert_eq!(rcv0.peek(), 20); +/// assert_eq!(rcv1.peek(), 20); +/// assert_eq!(SOME_SIGNAL.peek(), 20); +/// assert_eq!(SOME_SIGNAL.peek_and(|x|x>&30), None); +/// }; +/// block_on(f); +/// ``` +pub struct MultiSignal<'a, M: RawMutex, T: Clone, const N: usize> { + mutex: Mutex>>, + _phantom: PhantomData<&'a ()>, +} + +struct MultiSignalState { + data: T, + current_id: u64, + wakers: MultiWakerRegistration, + receiver_count: usize, +} + +#[derive(Debug)] +/// An error that can occur when a `MultiSignal` returns a `Result`. +pub enum Error { + /// The maximum number of [`Receiver`](crate::multi_signal::Receiver) has been reached. + MaximumReceiversReached, +} + +impl<'a, M: RawMutex, T: Clone, const N: usize> MultiSignal<'a, M, T, N> { + /// Create a new `MultiSignal` initialized with the given value. + pub const fn new(init: T) -> Self { + Self { + mutex: Mutex::new(RefCell::new(MultiSignalState { + data: init, + current_id: 1, + wakers: MultiWakerRegistration::new(), + receiver_count: 0, + })), + _phantom: PhantomData, + } + } + + /// Get a [`Receiver`] for the `MultiSignal`. + pub fn receiver(&'a self) -> Result, Error> { + self.mutex.lock(|state| { + let mut s = state.borrow_mut(); + if s.receiver_count < N { + s.receiver_count += 1; + Ok(Receiver(Rcv::new(self))) + } else { + Err(Error::MaximumReceiversReached) + } + }) + } + + /// Update the value of the `MultiSignal`. + pub fn write(&self, data: T) { + self.mutex.lock(|state| { + let mut s = state.borrow_mut(); + s.data = data; + s.current_id += 1; + s.wakers.wake(); + }) + } + + /// Peek the current value of the `MultiSignal`. + pub fn peek(&self) -> T { + self.mutex.lock(|state| state.borrow().data.clone()) + } + + /// Peek the current value of the `MultiSignal` and check if it satisfies the predicate `f`. + pub fn peek_and(&self, f: fn(&T) -> bool) -> Option { + self.mutex.lock(|state| { + let s = state.borrow(); + if f(&s.data) { + Some(s.data.clone()) + } else { + None + } + }) + } + + /// Get the ID of the current value of the `MultiSignal`. + /// This method is mostly for testing purposes. + #[allow(dead_code)] + fn get_id(&self) -> u64 { + self.mutex.lock(|state| state.borrow().current_id) + } + + /// Poll the `MultiSignal` with an optional context. + fn get_with_context(&'a self, waker: &mut Rcv<'a, M, T, N>, cx: Option<&mut Context>) -> Poll { + self.mutex.lock(|state| { + let mut s = state.borrow_mut(); + match (s.current_id > waker.at_id, waker.predicate) { + (true, None) => { + waker.at_id = s.current_id; + Poll::Ready(s.data.clone()) + } + (true, Some(f)) if f(&s.data) => { + waker.at_id = s.current_id; + Poll::Ready(s.data.clone()) + } + _ => { + if let Some(cx) = cx { + s.wakers.register(cx.waker()); + } + Poll::Pending + } + } + }) + } +} + +/// A receiver is able to `.await` a changed `MultiSignal` value. +pub struct Rcv<'a, M: RawMutex, T: Clone, const N: usize> { + multi_sig: &'a MultiSignal<'a, M, T, N>, + predicate: Option bool>, + at_id: u64, +} + +// f: Option bool> +impl<'a, M: RawMutex, T: Clone, const N: usize> Rcv<'a, M, T, N> { + /// Create a new `Receiver` with a reference the given `MultiSignal`. + fn new(multi_sig: &'a MultiSignal<'a, M, T, N>) -> Self { + Self { + multi_sig, + predicate: None, + at_id: 0, + } + } + + /// Wait for a change to the value of the corresponding `MultiSignal`. + pub fn changed<'s>(&'s mut self) -> ReceiverFuture<'s, 'a, M, T, N> { + self.predicate = None; + ReceiverFuture { subscriber: self } + } + + /// Wait for a change to the value of the corresponding `MultiSignal` which matches the predicate `f`. + pub fn changed_and<'s>(&'s mut self, f: fn(&T) -> bool) -> ReceiverFuture<'s, 'a, M, T, N> { + self.predicate = Some(f); + ReceiverFuture { subscriber: self } + } + + /// Try to get a changed value of the corresponding `MultiSignal`. + pub fn try_changed(&mut self) -> Option { + self.multi_sig.mutex.lock(|state| { + let s = state.borrow(); + match s.current_id > self.at_id { + true => { + self.at_id = s.current_id; + Some(s.data.clone()) + } + false => None, + } + }) + } + + /// Try to get a changed value of the corresponding `MultiSignal` which matches the predicate `f`. + pub fn try_changed_and(&mut self, f: fn(&T) -> bool) -> Option { + self.multi_sig.mutex.lock(|state| { + let s = state.borrow(); + match s.current_id > self.at_id && f(&s.data) { + true => { + self.at_id = s.current_id; + Some(s.data.clone()) + } + false => None, + } + }) + } + + /// Peek the current value of the corresponding `MultiSignal`. + pub fn peek(&self) -> T { + self.multi_sig.peek() + } + + /// Peek the current value of the corresponding `MultiSignal` and check if it satisfies the predicate `f`. + pub fn peek_and(&self, f: fn(&T) -> bool) -> Option { + self.multi_sig.peek_and(f) + } + + /// Check if the value of the corresponding `MultiSignal` has changed. + pub fn has_changed(&mut self) -> bool { + self.multi_sig + .mutex + .lock(|state| state.borrow().current_id > self.at_id) + } +} + +/// A `Receiver` is able to `.await` a change to the corresponding [`MultiSignal`] value. +pub struct Receiver<'a, M: RawMutex, T: Clone, const N: usize>(Rcv<'a, M, T, N>); + +impl<'a, M: RawMutex, T: Clone, const N: usize> Deref for Receiver<'a, M, T, N> { + type Target = Rcv<'a, M, T, N>; + + fn deref(&self) -> &Self::Target { + &self.0 + } +} + +impl<'a, M: RawMutex, T: Clone, const N: usize> DerefMut for Receiver<'a, M, T, N> { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.0 + } +} + +/// Future for the `Receiver` wait action +#[must_use = "futures do nothing unless you `.await` or poll them"] +pub struct ReceiverFuture<'s, 'a, M: RawMutex, T: Clone, const N: usize> { + subscriber: &'s mut Rcv<'a, M, T, N>, +} + +impl<'s, 'a, M: RawMutex, T: Clone, const N: usize> Future for ReceiverFuture<'s, 'a, M, T, N> { + type Output = T; + + fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { + self.subscriber + .multi_sig + .get_with_context(&mut self.subscriber, Some(cx)) + } +} + +impl<'s, 'a, M: RawMutex, T: Clone, const N: usize> Unpin for ReceiverFuture<'s, 'a, M, T, N> {} From 410c2d440afa2a500ef1398b5b48e746f77815bd Mon Sep 17 00:00:00 2001 From: Peter Krull Date: Tue, 13 Feb 2024 15:37:07 +0100 Subject: [PATCH 002/361] Change import formatting --- embassy-sync/src/multi_signal.rs | 19 ++++++++----------- 1 file changed, 8 insertions(+), 11 deletions(-) diff --git a/embassy-sync/src/multi_signal.rs b/embassy-sync/src/multi_signal.rs index db858f269..bff7ad048 100644 --- a/embassy-sync/src/multi_signal.rs +++ b/embassy-sync/src/multi_signal.rs @@ -1,18 +1,15 @@ //! A synchronization primitive for passing the latest value to **multiple** tasks. -use core::{ - cell::RefCell, - marker::PhantomData, - ops::{Deref, DerefMut}, - pin::Pin, - task::{Context, Poll}, -}; +use core::cell::RefCell; +use core::marker::PhantomData; +use core::ops::{Deref, DerefMut}; +use core::pin::Pin; +use core::task::{Context, Poll}; use futures_util::Future; -use crate::{ - blocking_mutex::{raw::RawMutex, Mutex}, - waitqueue::MultiWakerRegistration, -}; +use crate::blocking_mutex::raw::RawMutex; +use crate::blocking_mutex::Mutex; +use crate::waitqueue::MultiWakerRegistration; /// A `MultiSignal` is a single-slot signaling primitive, which can awake `N` separate [`Receiver`]s. /// From 37f1c9ac27b0542fdf404392e9bb265fa8ec41d3 Mon Sep 17 00:00:00 2001 From: Peter Krull Date: Tue, 13 Feb 2024 23:14:16 +0100 Subject: [PATCH 003/361] Removed unused lifetime, change most fn -> FnMut --- embassy-sync/src/multi_signal.rs | 26 ++++++++++++-------------- 1 file changed, 12 insertions(+), 14 deletions(-) diff --git a/embassy-sync/src/multi_signal.rs b/embassy-sync/src/multi_signal.rs index bff7ad048..5f724c76b 100644 --- a/embassy-sync/src/multi_signal.rs +++ b/embassy-sync/src/multi_signal.rs @@ -1,6 +1,5 @@ //! A synchronization primitive for passing the latest value to **multiple** tasks. use core::cell::RefCell; -use core::marker::PhantomData; use core::ops::{Deref, DerefMut}; use core::pin::Pin; use core::task::{Context, Poll}; @@ -66,9 +65,8 @@ use crate::waitqueue::MultiWakerRegistration; /// }; /// block_on(f); /// ``` -pub struct MultiSignal<'a, M: RawMutex, T: Clone, const N: usize> { +pub struct MultiSignal { mutex: Mutex>>, - _phantom: PhantomData<&'a ()>, } struct MultiSignalState { @@ -85,7 +83,7 @@ pub enum Error { MaximumReceiversReached, } -impl<'a, M: RawMutex, T: Clone, const N: usize> MultiSignal<'a, M, T, N> { +impl<'a, M: RawMutex, T: Clone, const N: usize> MultiSignal { /// Create a new `MultiSignal` initialized with the given value. pub const fn new(init: T) -> Self { Self { @@ -95,7 +93,6 @@ impl<'a, M: RawMutex, T: Clone, const N: usize> MultiSignal<'a, M, T, N> { wakers: MultiWakerRegistration::new(), receiver_count: 0, })), - _phantom: PhantomData, } } @@ -128,7 +125,7 @@ impl<'a, M: RawMutex, T: Clone, const N: usize> MultiSignal<'a, M, T, N> { } /// Peek the current value of the `MultiSignal` and check if it satisfies the predicate `f`. - pub fn peek_and(&self, f: fn(&T) -> bool) -> Option { + pub fn peek_and(&self, mut f: impl FnMut(&T) -> bool) -> Option { self.mutex.lock(|state| { let s = state.borrow(); if f(&s.data) { @@ -147,16 +144,16 @@ impl<'a, M: RawMutex, T: Clone, const N: usize> MultiSignal<'a, M, T, N> { } /// Poll the `MultiSignal` with an optional context. - fn get_with_context(&'a self, waker: &mut Rcv<'a, M, T, N>, cx: Option<&mut Context>) -> Poll { + fn get_with_context(&self, rcv: &mut Rcv<'a, M, T, N>, cx: Option<&mut Context>) -> Poll { self.mutex.lock(|state| { let mut s = state.borrow_mut(); - match (s.current_id > waker.at_id, waker.predicate) { + match (s.current_id > rcv.at_id, rcv.predicate) { (true, None) => { - waker.at_id = s.current_id; + rcv.at_id = s.current_id; Poll::Ready(s.data.clone()) } (true, Some(f)) if f(&s.data) => { - waker.at_id = s.current_id; + rcv.at_id = s.current_id; Poll::Ready(s.data.clone()) } _ => { @@ -172,7 +169,7 @@ impl<'a, M: RawMutex, T: Clone, const N: usize> MultiSignal<'a, M, T, N> { /// A receiver is able to `.await` a changed `MultiSignal` value. pub struct Rcv<'a, M: RawMutex, T: Clone, const N: usize> { - multi_sig: &'a MultiSignal<'a, M, T, N>, + multi_sig: &'a MultiSignal, predicate: Option bool>, at_id: u64, } @@ -180,7 +177,7 @@ pub struct Rcv<'a, M: RawMutex, T: Clone, const N: usize> { // f: Option bool> impl<'a, M: RawMutex, T: Clone, const N: usize> Rcv<'a, M, T, N> { /// Create a new `Receiver` with a reference the given `MultiSignal`. - fn new(multi_sig: &'a MultiSignal<'a, M, T, N>) -> Self { + fn new(multi_sig: &'a MultiSignal) -> Self { Self { multi_sig, predicate: None, @@ -195,6 +192,7 @@ impl<'a, M: RawMutex, T: Clone, const N: usize> Rcv<'a, M, T, N> { } /// Wait for a change to the value of the corresponding `MultiSignal` which matches the predicate `f`. + // TODO: How do we make this work with a FnMut closure? pub fn changed_and<'s>(&'s mut self, f: fn(&T) -> bool) -> ReceiverFuture<'s, 'a, M, T, N> { self.predicate = Some(f); ReceiverFuture { subscriber: self } @@ -215,7 +213,7 @@ impl<'a, M: RawMutex, T: Clone, const N: usize> Rcv<'a, M, T, N> { } /// Try to get a changed value of the corresponding `MultiSignal` which matches the predicate `f`. - pub fn try_changed_and(&mut self, f: fn(&T) -> bool) -> Option { + pub fn try_changed_and(&mut self, mut f: impl FnMut(&T) -> bool) -> Option { self.multi_sig.mutex.lock(|state| { let s = state.borrow(); match s.current_id > self.at_id && f(&s.data) { @@ -234,7 +232,7 @@ impl<'a, M: RawMutex, T: Clone, const N: usize> Rcv<'a, M, T, N> { } /// Peek the current value of the corresponding `MultiSignal` and check if it satisfies the predicate `f`. - pub fn peek_and(&self, f: fn(&T) -> bool) -> Option { + pub fn peek_and(&self, f: impl FnMut(&T) -> bool) -> Option { self.multi_sig.peek_and(f) } From 24a4379832d387754d407b77ff7aac5e55401eb3 Mon Sep 17 00:00:00 2001 From: Peter Krull Date: Wed, 14 Feb 2024 01:23:11 +0100 Subject: [PATCH 004/361] Got closures to work in async, added bunch of tests --- embassy-sync/src/multi_signal.rs | 340 ++++++++++++++++++++++++++----- 1 file changed, 292 insertions(+), 48 deletions(-) diff --git a/embassy-sync/src/multi_signal.rs b/embassy-sync/src/multi_signal.rs index 5f724c76b..1481dc8f8 100644 --- a/embassy-sync/src/multi_signal.rs +++ b/embassy-sync/src/multi_signal.rs @@ -97,7 +97,7 @@ impl<'a, M: RawMutex, T: Clone, const N: usize> MultiSignal { } /// Get a [`Receiver`] for the `MultiSignal`. - pub fn receiver(&'a self) -> Result, Error> { + pub fn receiver<'s>(&'a self) -> Result, Error> { self.mutex.lock(|state| { let mut s = state.borrow_mut(); if s.receiver_count < N { @@ -142,60 +142,36 @@ impl<'a, M: RawMutex, T: Clone, const N: usize> MultiSignal { fn get_id(&self) -> u64 { self.mutex.lock(|state| state.borrow().current_id) } - - /// Poll the `MultiSignal` with an optional context. - fn get_with_context(&self, rcv: &mut Rcv<'a, M, T, N>, cx: Option<&mut Context>) -> Poll { - self.mutex.lock(|state| { - let mut s = state.borrow_mut(); - match (s.current_id > rcv.at_id, rcv.predicate) { - (true, None) => { - rcv.at_id = s.current_id; - Poll::Ready(s.data.clone()) - } - (true, Some(f)) if f(&s.data) => { - rcv.at_id = s.current_id; - Poll::Ready(s.data.clone()) - } - _ => { - if let Some(cx) = cx { - s.wakers.register(cx.waker()); - } - Poll::Pending - } - } - }) - } } /// A receiver is able to `.await` a changed `MultiSignal` value. pub struct Rcv<'a, M: RawMutex, T: Clone, const N: usize> { multi_sig: &'a MultiSignal, - predicate: Option bool>, at_id: u64, } -// f: Option bool> -impl<'a, M: RawMutex, T: Clone, const N: usize> Rcv<'a, M, T, N> { +impl<'s, 'a, M: RawMutex, T: Clone, const N: usize> Rcv<'a, M, T, N> { /// Create a new `Receiver` with a reference the given `MultiSignal`. fn new(multi_sig: &'a MultiSignal) -> Self { - Self { - multi_sig, - predicate: None, - at_id: 0, - } + Self { multi_sig, at_id: 0 } } /// Wait for a change to the value of the corresponding `MultiSignal`. - pub fn changed<'s>(&'s mut self) -> ReceiverFuture<'s, 'a, M, T, N> { - self.predicate = None; - ReceiverFuture { subscriber: self } + pub async fn changed(&mut self) -> T { + ReceiverWaitFuture { subscriber: self }.await } /// Wait for a change to the value of the corresponding `MultiSignal` which matches the predicate `f`. // TODO: How do we make this work with a FnMut closure? - pub fn changed_and<'s>(&'s mut self, f: fn(&T) -> bool) -> ReceiverFuture<'s, 'a, M, T, N> { - self.predicate = Some(f); - ReceiverFuture { subscriber: self } + pub async fn changed_and(&mut self, f: F) -> T + where + F: FnMut(&T) -> bool, + { + ReceiverPredFuture { + subscriber: self, + predicate: f, + } + .await } /// Try to get a changed value of the corresponding `MultiSignal`. @@ -213,7 +189,10 @@ impl<'a, M: RawMutex, T: Clone, const N: usize> Rcv<'a, M, T, N> { } /// Try to get a changed value of the corresponding `MultiSignal` which matches the predicate `f`. - pub fn try_changed_and(&mut self, mut f: impl FnMut(&T) -> bool) -> Option { + pub fn try_changed_and(&mut self, mut f: F) -> Option + where + F: FnMut(&T) -> bool, + { self.multi_sig.mutex.lock(|state| { let s = state.borrow(); match s.current_id > self.at_id && f(&s.data) { @@ -232,7 +211,10 @@ impl<'a, M: RawMutex, T: Clone, const N: usize> Rcv<'a, M, T, N> { } /// Peek the current value of the corresponding `MultiSignal` and check if it satisfies the predicate `f`. - pub fn peek_and(&self, f: impl FnMut(&T) -> bool) -> Option { + pub fn peek_and(&self, f: F) -> Option + where + F: FnMut(&T) -> bool, + { self.multi_sig.peek_and(f) } @@ -247,7 +229,7 @@ impl<'a, M: RawMutex, T: Clone, const N: usize> Rcv<'a, M, T, N> { /// A `Receiver` is able to `.await` a change to the corresponding [`MultiSignal`] value. pub struct Receiver<'a, M: RawMutex, T: Clone, const N: usize>(Rcv<'a, M, T, N>); -impl<'a, M: RawMutex, T: Clone, const N: usize> Deref for Receiver<'a, M, T, N> { +impl<'s, 'a, M: RawMutex, T: Clone, const N: usize> Deref for Receiver<'a, M, T, N> { type Target = Rcv<'a, M, T, N>; fn deref(&self) -> &Self::Target { @@ -255,7 +237,7 @@ impl<'a, M: RawMutex, T: Clone, const N: usize> Deref for Receiver<'a, M, T, N> } } -impl<'a, M: RawMutex, T: Clone, const N: usize> DerefMut for Receiver<'a, M, T, N> { +impl<'s, 'a, M: RawMutex, T: Clone, const N: usize> DerefMut for Receiver<'a, M, T, N> { fn deref_mut(&mut self) -> &mut Self::Target { &mut self.0 } @@ -263,18 +245,280 @@ impl<'a, M: RawMutex, T: Clone, const N: usize> DerefMut for Receiver<'a, M, T, /// Future for the `Receiver` wait action #[must_use = "futures do nothing unless you `.await` or poll them"] -pub struct ReceiverFuture<'s, 'a, M: RawMutex, T: Clone, const N: usize> { +pub struct ReceiverWaitFuture<'s, 'a, M: RawMutex, T: Clone, const N: usize> { subscriber: &'s mut Rcv<'a, M, T, N>, } -impl<'s, 'a, M: RawMutex, T: Clone, const N: usize> Future for ReceiverFuture<'s, 'a, M, T, N> { +impl<'s, 'a, M: RawMutex, T: Clone, const N: usize> Unpin for ReceiverWaitFuture<'s, 'a, M, T, N> {} +impl<'s, 'a, M: RawMutex, T: Clone, const N: usize> Future for ReceiverWaitFuture<'s, 'a, M, T, N> { type Output = T; fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { - self.subscriber - .multi_sig - .get_with_context(&mut self.subscriber, Some(cx)) + self.get_with_context(Some(cx)) } } -impl<'s, 'a, M: RawMutex, T: Clone, const N: usize> Unpin for ReceiverFuture<'s, 'a, M, T, N> {} +impl<'s, 'a, M: RawMutex, T: Clone, const N: usize> ReceiverWaitFuture<'s, 'a, M, T, N> { + /// Poll the `MultiSignal` with an optional context. + fn get_with_context(&mut self, cx: Option<&mut Context>) -> Poll { + self.subscriber.multi_sig.mutex.lock(|state| { + let mut s = state.borrow_mut(); + match s.current_id > self.subscriber.at_id { + true => { + self.subscriber.at_id = s.current_id; + Poll::Ready(s.data.clone()) + } + _ => { + if let Some(cx) = cx { + s.wakers.register(cx.waker()); + } + Poll::Pending + } + } + }) + } +} + +/// Future for the `Receiver` wait action, with the ability to filter the value with a predicate. +#[must_use = "futures do nothing unless you `.await` or poll them"] +pub struct ReceiverPredFuture<'s, 'a, M: RawMutex, T: Clone, F: FnMut(&'a T) -> bool, const N: usize> { + subscriber: &'s mut Rcv<'a, M, T, N>, + predicate: F, +} + +impl<'s, 'a, M: RawMutex, T: Clone, F: FnMut(&T) -> bool, const N: usize> Unpin for ReceiverPredFuture<'s, 'a, M, T, F, N> {} +impl<'s, 'a, M: RawMutex, T: Clone, F: FnMut(&T) -> bool, const N: usize> Future for ReceiverPredFuture<'s, 'a, M, T, F, N>{ + type Output = T; + + fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { + self.get_with_context_pred(Some(cx)) + } +} + +impl<'s, 'a, M: RawMutex, T: Clone, F: FnMut(&T) -> bool, const N: usize> ReceiverPredFuture<'s, 'a, M, T, F, N> { + /// Poll the `MultiSignal` with an optional context. + fn get_with_context_pred(&mut self, cx: Option<&mut Context>) -> Poll { + self.subscriber.multi_sig.mutex.lock(|state| { + let mut s = state.borrow_mut(); + match s.current_id > self.subscriber.at_id { + true if (self.predicate)(&s.data) => { + self.subscriber.at_id = s.current_id; + Poll::Ready(s.data.clone()) + } + _ => { + if let Some(cx) = cx { + s.wakers.register(cx.waker()); + } + Poll::Pending + } + } + }) + } +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::blocking_mutex::raw::CriticalSectionRawMutex; + use futures_executor::block_on; + + #[test] + fn multiple_writes() { + let f = async { + static SOME_SIGNAL: MultiSignal = MultiSignal::new(0); + + // Obtain Receivers + let mut rcv0 = SOME_SIGNAL.receiver().unwrap(); + let mut rcv1 = SOME_SIGNAL.receiver().unwrap(); + + SOME_SIGNAL.write(10); + + // Receive the new value + assert_eq!(rcv0.changed().await, 10); + assert_eq!(rcv1.changed().await, 10); + + // No update + assert_eq!(rcv0.try_changed(), None); + assert_eq!(rcv1.try_changed(), None); + + SOME_SIGNAL.write(20); + + assert_eq!(rcv0.changed().await, 20); + assert_eq!(rcv1.changed().await, 20); + }; + block_on(f); + } + + #[test] + fn max_receivers() { + let f = async { + static SOME_SIGNAL: MultiSignal = MultiSignal::new(0); + + // Obtain Receivers + let _ = SOME_SIGNAL.receiver().unwrap(); + let _ = SOME_SIGNAL.receiver().unwrap(); + assert!(SOME_SIGNAL.receiver().is_err()); + }; + block_on(f); + } + + // Really weird edge case, but it's possible to have a receiver that never gets a value. + #[test] + fn receive_initial() { + let f = async { + static SOME_SIGNAL: MultiSignal = MultiSignal::new(0); + + // Obtain Receivers + let mut rcv0 = SOME_SIGNAL.receiver().unwrap(); + let mut rcv1 = SOME_SIGNAL.receiver().unwrap(); + + assert_eq!(rcv0.try_changed(), Some(0)); + assert_eq!(rcv1.try_changed(), Some(0)); + + assert_eq!(rcv0.try_changed(), None); + assert_eq!(rcv1.try_changed(), None); + }; + block_on(f); + } + + #[test] + fn count_ids() { + let f = async { + static SOME_SIGNAL: MultiSignal = MultiSignal::new(0); + + // Obtain Receivers + let mut rcv0 = SOME_SIGNAL.receiver().unwrap(); + let mut rcv1 = SOME_SIGNAL.receiver().unwrap(); + + SOME_SIGNAL.write(10); + + assert_eq!(rcv0.changed().await, 10); + assert_eq!(rcv1.changed().await, 10); + + assert_eq!(rcv0.try_changed(), None); + assert_eq!(rcv1.try_changed(), None); + + SOME_SIGNAL.write(20); + SOME_SIGNAL.write(20); + SOME_SIGNAL.write(20); + + assert_eq!(rcv0.changed().await, 20); + assert_eq!(rcv1.changed().await, 20); + + assert_eq!(rcv0.try_changed(), None); + assert_eq!(rcv1.try_changed(), None); + + assert_eq!(SOME_SIGNAL.get_id(), 5); + }; + block_on(f); + } + + #[test] + fn peek_still_await() { + let f = async { + static SOME_SIGNAL: MultiSignal = MultiSignal::new(0); + + // Obtain Receivers + let mut rcv0 = SOME_SIGNAL.receiver().unwrap(); + let mut rcv1 = SOME_SIGNAL.receiver().unwrap(); + + SOME_SIGNAL.write(10); + + assert_eq!(rcv0.peek(), 10); + assert_eq!(rcv1.peek(), 10); + + assert_eq!(rcv0.changed().await, 10); + assert_eq!(rcv1.changed().await, 10); + }; + block_on(f); + } + + #[test] + fn predicate() { + let f = async { + static SOME_SIGNAL: MultiSignal = MultiSignal::new(0); + + // Obtain Receivers + let mut rcv0 = SOME_SIGNAL.receiver().unwrap(); + let mut rcv1 = SOME_SIGNAL.receiver().unwrap(); + + SOME_SIGNAL.write(20); + + assert_eq!(rcv0.changed_and(|x| x > &10).await, 20); + assert_eq!(rcv1.try_changed_and(|x| x > &30), None); + }; + block_on(f); + } + + #[test] + fn mutable_predicate() { + let f = async { + static SOME_SIGNAL: MultiSignal = MultiSignal::new(0); + + // Obtain Receivers + let mut rcv = SOME_SIGNAL.receiver().unwrap(); + + SOME_SIGNAL.write(10); + + let mut largest = 0; + let mut predicate = |x: &u8| { + if *x > largest { + largest = *x; + } + true + }; + + assert_eq!(rcv.changed_and(&mut predicate).await, 10); + + SOME_SIGNAL.write(20); + + assert_eq!(rcv.changed_and(&mut predicate).await, 20); + + SOME_SIGNAL.write(5); + + assert_eq!(rcv.changed_and(&mut predicate).await, 5); + + assert_eq!(largest, 20) + }; + block_on(f); + } + + #[test] + fn peek_and() { + let f = async { + static SOME_SIGNAL: MultiSignal = MultiSignal::new(0); + + // Obtain Receivers + let mut rcv0 = SOME_SIGNAL.receiver().unwrap(); + let mut rcv1 = SOME_SIGNAL.receiver().unwrap(); + + SOME_SIGNAL.write(20); + + assert_eq!(rcv0.peek_and(|x| x > &10), Some(20)); + assert_eq!(rcv1.peek_and(|x| x > &30), None); + + assert_eq!(rcv0.changed().await, 20); + assert_eq!(rcv1.changed().await, 20); + }; + block_on(f); + } + + #[test] + fn peek_with_static() { + let f = async { + static SOME_SIGNAL: MultiSignal = MultiSignal::new(0); + + // Obtain Receivers + let rcv0 = SOME_SIGNAL.receiver().unwrap(); + let rcv1 = SOME_SIGNAL.receiver().unwrap(); + + SOME_SIGNAL.write(20); + + assert_eq!(rcv0.peek(), 20); + assert_eq!(rcv1.peek(), 20); + assert_eq!(SOME_SIGNAL.peek(), 20); + assert_eq!(SOME_SIGNAL.peek_and(|x| x > &30), None); + }; + block_on(f); + } +} From 2f58d1968a7310335a0dac4d947c6972a7707ed5 Mon Sep 17 00:00:00 2001 From: Peter Krull Date: Wed, 14 Feb 2024 01:27:48 +0100 Subject: [PATCH 005/361] Updated formatting --- embassy-sync/src/multi_signal.rs | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/embassy-sync/src/multi_signal.rs b/embassy-sync/src/multi_signal.rs index 1481dc8f8..ff9f72f2e 100644 --- a/embassy-sync/src/multi_signal.rs +++ b/embassy-sync/src/multi_signal.rs @@ -162,7 +162,6 @@ impl<'s, 'a, M: RawMutex, T: Clone, const N: usize> Rcv<'a, M, T, N> { } /// Wait for a change to the value of the corresponding `MultiSignal` which matches the predicate `f`. - // TODO: How do we make this work with a FnMut closure? pub async fn changed_and(&mut self, f: F) -> T where F: FnMut(&T) -> bool, @@ -286,8 +285,13 @@ pub struct ReceiverPredFuture<'s, 'a, M: RawMutex, T: Clone, F: FnMut(&'a T) -> predicate: F, } -impl<'s, 'a, M: RawMutex, T: Clone, F: FnMut(&T) -> bool, const N: usize> Unpin for ReceiverPredFuture<'s, 'a, M, T, F, N> {} -impl<'s, 'a, M: RawMutex, T: Clone, F: FnMut(&T) -> bool, const N: usize> Future for ReceiverPredFuture<'s, 'a, M, T, F, N>{ +impl<'s, 'a, M: RawMutex, T: Clone, F: FnMut(&T) -> bool, const N: usize> Unpin + for ReceiverPredFuture<'s, 'a, M, T, F, N> +{ +} +impl<'s, 'a, M: RawMutex, T: Clone, F: FnMut(&T) -> bool, const N: usize> Future + for ReceiverPredFuture<'s, 'a, M, T, F, N> +{ type Output = T; fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { @@ -318,9 +322,10 @@ impl<'s, 'a, M: RawMutex, T: Clone, F: FnMut(&T) -> bool, const N: usize> Receiv #[cfg(test)] mod tests { + use futures_executor::block_on; + use super::*; use crate::blocking_mutex::raw::CriticalSectionRawMutex; - use futures_executor::block_on; #[test] fn multiple_writes() { From 6defb4fed98432dee948634f3b2001cb4ea7ec5b Mon Sep 17 00:00:00 2001 From: Peter Krull Date: Wed, 28 Feb 2024 20:59:38 +0100 Subject: [PATCH 006/361] Changed name to `Watch`, added `DynReceiver`, `get`-method and more reworks. --- embassy-sync/src/lib.rs | 2 +- embassy-sync/src/multi_signal.rs | 529 ------------------------------- embassy-sync/src/watch.rs | 515 ++++++++++++++++++++++++++++++ 3 files changed, 516 insertions(+), 530 deletions(-) delete mode 100644 embassy-sync/src/multi_signal.rs create mode 100644 embassy-sync/src/watch.rs diff --git a/embassy-sync/src/lib.rs b/embassy-sync/src/lib.rs index f02985564..8a69541a5 100644 --- a/embassy-sync/src/lib.rs +++ b/embassy-sync/src/lib.rs @@ -12,11 +12,11 @@ mod ring_buffer; pub mod blocking_mutex; pub mod channel; -pub mod multi_signal; pub mod mutex; pub mod pipe; pub mod priority_channel; pub mod pubsub; pub mod signal; pub mod waitqueue; +pub mod watch; pub mod zerocopy_channel; diff --git a/embassy-sync/src/multi_signal.rs b/embassy-sync/src/multi_signal.rs deleted file mode 100644 index ff9f72f2e..000000000 --- a/embassy-sync/src/multi_signal.rs +++ /dev/null @@ -1,529 +0,0 @@ -//! A synchronization primitive for passing the latest value to **multiple** tasks. -use core::cell::RefCell; -use core::ops::{Deref, DerefMut}; -use core::pin::Pin; -use core::task::{Context, Poll}; - -use futures_util::Future; - -use crate::blocking_mutex::raw::RawMutex; -use crate::blocking_mutex::Mutex; -use crate::waitqueue::MultiWakerRegistration; - -/// A `MultiSignal` is a single-slot signaling primitive, which can awake `N` separate [`Receiver`]s. -/// -/// Similar to a [`Signal`](crate::signal::Signal), except `MultiSignal` allows for multiple tasks to -/// `.await` the latest value, and all receive it. -/// -/// This is similar to a [`PubSubChannel`](crate::pubsub::PubSubChannel) with a buffer size of 1, except -/// "sending" to it (calling [`MultiSignal::write`]) will immediately overwrite the previous value instead -/// of waiting for the receivers to pop the previous value. -/// -/// `MultiSignal` is useful when a single task is responsible for updating a value or "state", which multiple other -/// tasks are interested in getting notified about changes to the latest value of. It is therefore fine for -/// [`Receiver`]s to "lose" stale values. -/// -/// Anyone with a reference to the MultiSignal can update or peek the value. MultiSignals are generally declared -/// as `static`s and then borrowed as required to either [`MultiSignal::peek`] the value or obtain a [`Receiver`] -/// with [`MultiSignal::receiver`] which has async methods. -/// ``` -/// -/// use futures_executor::block_on; -/// use embassy_sync::multi_signal::MultiSignal; -/// use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex; -/// -/// let f = async { -/// -/// static SOME_SIGNAL: MultiSignal = MultiSignal::new(0); -/// -/// // Obtain Receivers -/// let mut rcv0 = SOME_SIGNAL.receiver().unwrap(); -/// let mut rcv1 = SOME_SIGNAL.receiver().unwrap(); -/// assert!(SOME_SIGNAL.receiver().is_err()); -/// -/// SOME_SIGNAL.write(10); -/// -/// // Receive the new value -/// assert_eq!(rcv0.changed().await, 10); -/// assert_eq!(rcv1.try_changed(), Some(10)); -/// -/// // No update -/// assert_eq!(rcv0.try_changed(), None); -/// assert_eq!(rcv1.try_changed(), None); -/// -/// SOME_SIGNAL.write(20); -/// -/// // Receive new value with predicate -/// assert_eq!(rcv0.changed_and(|x|x>&10).await, 20); -/// assert_eq!(rcv1.try_changed_and(|x|x>&30), None); -/// -/// // Anyone can peek the current value -/// assert_eq!(rcv0.peek(), 20); -/// assert_eq!(rcv1.peek(), 20); -/// assert_eq!(SOME_SIGNAL.peek(), 20); -/// assert_eq!(SOME_SIGNAL.peek_and(|x|x>&30), None); -/// }; -/// block_on(f); -/// ``` -pub struct MultiSignal { - mutex: Mutex>>, -} - -struct MultiSignalState { - data: T, - current_id: u64, - wakers: MultiWakerRegistration, - receiver_count: usize, -} - -#[derive(Debug)] -/// An error that can occur when a `MultiSignal` returns a `Result`. -pub enum Error { - /// The maximum number of [`Receiver`](crate::multi_signal::Receiver) has been reached. - MaximumReceiversReached, -} - -impl<'a, M: RawMutex, T: Clone, const N: usize> MultiSignal { - /// Create a new `MultiSignal` initialized with the given value. - pub const fn new(init: T) -> Self { - Self { - mutex: Mutex::new(RefCell::new(MultiSignalState { - data: init, - current_id: 1, - wakers: MultiWakerRegistration::new(), - receiver_count: 0, - })), - } - } - - /// Get a [`Receiver`] for the `MultiSignal`. - pub fn receiver<'s>(&'a self) -> Result, Error> { - self.mutex.lock(|state| { - let mut s = state.borrow_mut(); - if s.receiver_count < N { - s.receiver_count += 1; - Ok(Receiver(Rcv::new(self))) - } else { - Err(Error::MaximumReceiversReached) - } - }) - } - - /// Update the value of the `MultiSignal`. - pub fn write(&self, data: T) { - self.mutex.lock(|state| { - let mut s = state.borrow_mut(); - s.data = data; - s.current_id += 1; - s.wakers.wake(); - }) - } - - /// Peek the current value of the `MultiSignal`. - pub fn peek(&self) -> T { - self.mutex.lock(|state| state.borrow().data.clone()) - } - - /// Peek the current value of the `MultiSignal` and check if it satisfies the predicate `f`. - pub fn peek_and(&self, mut f: impl FnMut(&T) -> bool) -> Option { - self.mutex.lock(|state| { - let s = state.borrow(); - if f(&s.data) { - Some(s.data.clone()) - } else { - None - } - }) - } - - /// Get the ID of the current value of the `MultiSignal`. - /// This method is mostly for testing purposes. - #[allow(dead_code)] - fn get_id(&self) -> u64 { - self.mutex.lock(|state| state.borrow().current_id) - } -} - -/// A receiver is able to `.await` a changed `MultiSignal` value. -pub struct Rcv<'a, M: RawMutex, T: Clone, const N: usize> { - multi_sig: &'a MultiSignal, - at_id: u64, -} - -impl<'s, 'a, M: RawMutex, T: Clone, const N: usize> Rcv<'a, M, T, N> { - /// Create a new `Receiver` with a reference the given `MultiSignal`. - fn new(multi_sig: &'a MultiSignal) -> Self { - Self { multi_sig, at_id: 0 } - } - - /// Wait for a change to the value of the corresponding `MultiSignal`. - pub async fn changed(&mut self) -> T { - ReceiverWaitFuture { subscriber: self }.await - } - - /// Wait for a change to the value of the corresponding `MultiSignal` which matches the predicate `f`. - pub async fn changed_and(&mut self, f: F) -> T - where - F: FnMut(&T) -> bool, - { - ReceiverPredFuture { - subscriber: self, - predicate: f, - } - .await - } - - /// Try to get a changed value of the corresponding `MultiSignal`. - pub fn try_changed(&mut self) -> Option { - self.multi_sig.mutex.lock(|state| { - let s = state.borrow(); - match s.current_id > self.at_id { - true => { - self.at_id = s.current_id; - Some(s.data.clone()) - } - false => None, - } - }) - } - - /// Try to get a changed value of the corresponding `MultiSignal` which matches the predicate `f`. - pub fn try_changed_and(&mut self, mut f: F) -> Option - where - F: FnMut(&T) -> bool, - { - self.multi_sig.mutex.lock(|state| { - let s = state.borrow(); - match s.current_id > self.at_id && f(&s.data) { - true => { - self.at_id = s.current_id; - Some(s.data.clone()) - } - false => None, - } - }) - } - - /// Peek the current value of the corresponding `MultiSignal`. - pub fn peek(&self) -> T { - self.multi_sig.peek() - } - - /// Peek the current value of the corresponding `MultiSignal` and check if it satisfies the predicate `f`. - pub fn peek_and(&self, f: F) -> Option - where - F: FnMut(&T) -> bool, - { - self.multi_sig.peek_and(f) - } - - /// Check if the value of the corresponding `MultiSignal` has changed. - pub fn has_changed(&mut self) -> bool { - self.multi_sig - .mutex - .lock(|state| state.borrow().current_id > self.at_id) - } -} - -/// A `Receiver` is able to `.await` a change to the corresponding [`MultiSignal`] value. -pub struct Receiver<'a, M: RawMutex, T: Clone, const N: usize>(Rcv<'a, M, T, N>); - -impl<'s, 'a, M: RawMutex, T: Clone, const N: usize> Deref for Receiver<'a, M, T, N> { - type Target = Rcv<'a, M, T, N>; - - fn deref(&self) -> &Self::Target { - &self.0 - } -} - -impl<'s, 'a, M: RawMutex, T: Clone, const N: usize> DerefMut for Receiver<'a, M, T, N> { - fn deref_mut(&mut self) -> &mut Self::Target { - &mut self.0 - } -} - -/// Future for the `Receiver` wait action -#[must_use = "futures do nothing unless you `.await` or poll them"] -pub struct ReceiverWaitFuture<'s, 'a, M: RawMutex, T: Clone, const N: usize> { - subscriber: &'s mut Rcv<'a, M, T, N>, -} - -impl<'s, 'a, M: RawMutex, T: Clone, const N: usize> Unpin for ReceiverWaitFuture<'s, 'a, M, T, N> {} -impl<'s, 'a, M: RawMutex, T: Clone, const N: usize> Future for ReceiverWaitFuture<'s, 'a, M, T, N> { - type Output = T; - - fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { - self.get_with_context(Some(cx)) - } -} - -impl<'s, 'a, M: RawMutex, T: Clone, const N: usize> ReceiverWaitFuture<'s, 'a, M, T, N> { - /// Poll the `MultiSignal` with an optional context. - fn get_with_context(&mut self, cx: Option<&mut Context>) -> Poll { - self.subscriber.multi_sig.mutex.lock(|state| { - let mut s = state.borrow_mut(); - match s.current_id > self.subscriber.at_id { - true => { - self.subscriber.at_id = s.current_id; - Poll::Ready(s.data.clone()) - } - _ => { - if let Some(cx) = cx { - s.wakers.register(cx.waker()); - } - Poll::Pending - } - } - }) - } -} - -/// Future for the `Receiver` wait action, with the ability to filter the value with a predicate. -#[must_use = "futures do nothing unless you `.await` or poll them"] -pub struct ReceiverPredFuture<'s, 'a, M: RawMutex, T: Clone, F: FnMut(&'a T) -> bool, const N: usize> { - subscriber: &'s mut Rcv<'a, M, T, N>, - predicate: F, -} - -impl<'s, 'a, M: RawMutex, T: Clone, F: FnMut(&T) -> bool, const N: usize> Unpin - for ReceiverPredFuture<'s, 'a, M, T, F, N> -{ -} -impl<'s, 'a, M: RawMutex, T: Clone, F: FnMut(&T) -> bool, const N: usize> Future - for ReceiverPredFuture<'s, 'a, M, T, F, N> -{ - type Output = T; - - fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { - self.get_with_context_pred(Some(cx)) - } -} - -impl<'s, 'a, M: RawMutex, T: Clone, F: FnMut(&T) -> bool, const N: usize> ReceiverPredFuture<'s, 'a, M, T, F, N> { - /// Poll the `MultiSignal` with an optional context. - fn get_with_context_pred(&mut self, cx: Option<&mut Context>) -> Poll { - self.subscriber.multi_sig.mutex.lock(|state| { - let mut s = state.borrow_mut(); - match s.current_id > self.subscriber.at_id { - true if (self.predicate)(&s.data) => { - self.subscriber.at_id = s.current_id; - Poll::Ready(s.data.clone()) - } - _ => { - if let Some(cx) = cx { - s.wakers.register(cx.waker()); - } - Poll::Pending - } - } - }) - } -} - -#[cfg(test)] -mod tests { - use futures_executor::block_on; - - use super::*; - use crate::blocking_mutex::raw::CriticalSectionRawMutex; - - #[test] - fn multiple_writes() { - let f = async { - static SOME_SIGNAL: MultiSignal = MultiSignal::new(0); - - // Obtain Receivers - let mut rcv0 = SOME_SIGNAL.receiver().unwrap(); - let mut rcv1 = SOME_SIGNAL.receiver().unwrap(); - - SOME_SIGNAL.write(10); - - // Receive the new value - assert_eq!(rcv0.changed().await, 10); - assert_eq!(rcv1.changed().await, 10); - - // No update - assert_eq!(rcv0.try_changed(), None); - assert_eq!(rcv1.try_changed(), None); - - SOME_SIGNAL.write(20); - - assert_eq!(rcv0.changed().await, 20); - assert_eq!(rcv1.changed().await, 20); - }; - block_on(f); - } - - #[test] - fn max_receivers() { - let f = async { - static SOME_SIGNAL: MultiSignal = MultiSignal::new(0); - - // Obtain Receivers - let _ = SOME_SIGNAL.receiver().unwrap(); - let _ = SOME_SIGNAL.receiver().unwrap(); - assert!(SOME_SIGNAL.receiver().is_err()); - }; - block_on(f); - } - - // Really weird edge case, but it's possible to have a receiver that never gets a value. - #[test] - fn receive_initial() { - let f = async { - static SOME_SIGNAL: MultiSignal = MultiSignal::new(0); - - // Obtain Receivers - let mut rcv0 = SOME_SIGNAL.receiver().unwrap(); - let mut rcv1 = SOME_SIGNAL.receiver().unwrap(); - - assert_eq!(rcv0.try_changed(), Some(0)); - assert_eq!(rcv1.try_changed(), Some(0)); - - assert_eq!(rcv0.try_changed(), None); - assert_eq!(rcv1.try_changed(), None); - }; - block_on(f); - } - - #[test] - fn count_ids() { - let f = async { - static SOME_SIGNAL: MultiSignal = MultiSignal::new(0); - - // Obtain Receivers - let mut rcv0 = SOME_SIGNAL.receiver().unwrap(); - let mut rcv1 = SOME_SIGNAL.receiver().unwrap(); - - SOME_SIGNAL.write(10); - - assert_eq!(rcv0.changed().await, 10); - assert_eq!(rcv1.changed().await, 10); - - assert_eq!(rcv0.try_changed(), None); - assert_eq!(rcv1.try_changed(), None); - - SOME_SIGNAL.write(20); - SOME_SIGNAL.write(20); - SOME_SIGNAL.write(20); - - assert_eq!(rcv0.changed().await, 20); - assert_eq!(rcv1.changed().await, 20); - - assert_eq!(rcv0.try_changed(), None); - assert_eq!(rcv1.try_changed(), None); - - assert_eq!(SOME_SIGNAL.get_id(), 5); - }; - block_on(f); - } - - #[test] - fn peek_still_await() { - let f = async { - static SOME_SIGNAL: MultiSignal = MultiSignal::new(0); - - // Obtain Receivers - let mut rcv0 = SOME_SIGNAL.receiver().unwrap(); - let mut rcv1 = SOME_SIGNAL.receiver().unwrap(); - - SOME_SIGNAL.write(10); - - assert_eq!(rcv0.peek(), 10); - assert_eq!(rcv1.peek(), 10); - - assert_eq!(rcv0.changed().await, 10); - assert_eq!(rcv1.changed().await, 10); - }; - block_on(f); - } - - #[test] - fn predicate() { - let f = async { - static SOME_SIGNAL: MultiSignal = MultiSignal::new(0); - - // Obtain Receivers - let mut rcv0 = SOME_SIGNAL.receiver().unwrap(); - let mut rcv1 = SOME_SIGNAL.receiver().unwrap(); - - SOME_SIGNAL.write(20); - - assert_eq!(rcv0.changed_and(|x| x > &10).await, 20); - assert_eq!(rcv1.try_changed_and(|x| x > &30), None); - }; - block_on(f); - } - - #[test] - fn mutable_predicate() { - let f = async { - static SOME_SIGNAL: MultiSignal = MultiSignal::new(0); - - // Obtain Receivers - let mut rcv = SOME_SIGNAL.receiver().unwrap(); - - SOME_SIGNAL.write(10); - - let mut largest = 0; - let mut predicate = |x: &u8| { - if *x > largest { - largest = *x; - } - true - }; - - assert_eq!(rcv.changed_and(&mut predicate).await, 10); - - SOME_SIGNAL.write(20); - - assert_eq!(rcv.changed_and(&mut predicate).await, 20); - - SOME_SIGNAL.write(5); - - assert_eq!(rcv.changed_and(&mut predicate).await, 5); - - assert_eq!(largest, 20) - }; - block_on(f); - } - - #[test] - fn peek_and() { - let f = async { - static SOME_SIGNAL: MultiSignal = MultiSignal::new(0); - - // Obtain Receivers - let mut rcv0 = SOME_SIGNAL.receiver().unwrap(); - let mut rcv1 = SOME_SIGNAL.receiver().unwrap(); - - SOME_SIGNAL.write(20); - - assert_eq!(rcv0.peek_and(|x| x > &10), Some(20)); - assert_eq!(rcv1.peek_and(|x| x > &30), None); - - assert_eq!(rcv0.changed().await, 20); - assert_eq!(rcv1.changed().await, 20); - }; - block_on(f); - } - - #[test] - fn peek_with_static() { - let f = async { - static SOME_SIGNAL: MultiSignal = MultiSignal::new(0); - - // Obtain Receivers - let rcv0 = SOME_SIGNAL.receiver().unwrap(); - let rcv1 = SOME_SIGNAL.receiver().unwrap(); - - SOME_SIGNAL.write(20); - - assert_eq!(rcv0.peek(), 20); - assert_eq!(rcv1.peek(), 20); - assert_eq!(SOME_SIGNAL.peek(), 20); - assert_eq!(SOME_SIGNAL.peek_and(|x| x > &30), None); - }; - block_on(f); - } -} diff --git a/embassy-sync/src/watch.rs b/embassy-sync/src/watch.rs new file mode 100644 index 000000000..7e5e92741 --- /dev/null +++ b/embassy-sync/src/watch.rs @@ -0,0 +1,515 @@ +//! A synchronization primitive for passing the latest value to **multiple** tasks. + +use core::cell::RefCell; +use core::future::poll_fn; +use core::marker::PhantomData; +use core::ops::{Deref, DerefMut}; +use core::task::{Context, Poll}; + +use crate::blocking_mutex::raw::RawMutex; +use crate::blocking_mutex::Mutex; +use crate::waitqueue::MultiWakerRegistration; + +/// A `Watch` is a single-slot signaling primitive, which can awake `N` up to separate [`Receiver`]s. +/// +/// Similar to a [`Signal`](crate::signal::Signal), except `Watch` allows for multiple tasks to +/// `.await` the latest value, and all receive it. +/// +/// This is similar to a [`PubSubChannel`](crate::pubsub::PubSubChannel) with a buffer size of 1, except +/// "sending" to it (calling [`Watch::write`]) will immediately overwrite the previous value instead +/// of waiting for the receivers to pop the previous value. +/// +/// `Watch` is useful when a single task is responsible for updating a value or "state", which multiple other +/// tasks are interested in getting notified about changes to the latest value of. It is therefore fine for +/// [`Receiver`]s to "lose" stale values. +/// +/// Anyone with a reference to the Watch can update or peek the value. Watches are generally declared +/// as `static`s and then borrowed as required to either [`Watch::peek`] the value or obtain a [`Receiver`] +/// with [`Watch::receiver`] which has async methods. +/// ``` +/// +/// use futures_executor::block_on; +/// use embassy_sync::watch::Watch; +/// use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex; +/// +/// let f = async { +/// +/// static WATCH: Watch = Watch::new(); +/// +/// // Obtain Receivers +/// let mut rcv0 = WATCH.receiver().unwrap(); +/// let mut rcv1 = WATCH.receiver().unwrap(); +/// assert!(WATCH.receiver().is_err()); +/// +/// assert_eq!(rcv1.try_changed(), None); +/// +/// WATCH.write(10); +/// assert_eq!(WATCH.try_peek(), Some(10)); +/// +/// +/// // Receive the new value +/// assert_eq!(rcv0.changed().await, 10); +/// assert_eq!(rcv1.try_changed(), Some(10)); +/// +/// // No update +/// assert_eq!(rcv0.try_changed(), None); +/// assert_eq!(rcv1.try_changed(), None); +/// +/// WATCH.write(20); +/// +/// // Defference `between` peek `get`. +/// assert_eq!(rcv0.peek().await, 20); +/// assert_eq!(rcv1.get().await, 20); +/// +/// assert_eq!(rcv0.try_changed(), Some(20)); +/// assert_eq!(rcv1.try_changed(), None); +/// +/// }; +/// block_on(f); +/// ``` +pub struct Watch { + mutex: Mutex>>, +} + +struct WatchState { + data: Option, + current_id: u64, + wakers: MultiWakerRegistration, + receiver_count: usize, +} + +/// A trait representing the 'inner' behavior of the `Watch`. +pub trait WatchBehavior { + /// Poll the `Watch` for the current value, **without** making it as seen. + fn inner_poll_peek(&self, cx: &mut Context<'_>) -> Poll; + + /// Tries to peek the value of the `Watch`, **without** marking it as seen. + fn inner_try_peek(&self) -> Option; + + /// Poll the `Watch` for the current value, making it as seen. + fn inner_poll_get(&self, id: &mut u64, cx: &mut Context<'_>) -> Poll; + + /// Tries to get the value of the `Watch`, marking it as seen. + fn inner_try_get(&self, id: &mut u64) -> Option; + + /// Poll the `Watch` for a changed value, marking it as seen. + fn inner_poll_changed(&self, id: &mut u64, cx: &mut Context<'_>) -> Poll; + + /// Tries to retrieve the value of the `Watch` if it has changed, marking it as seen. + fn inner_try_changed(&self, id: &mut u64) -> Option; + + /// Checks if the `Watch` is been initialized with a value. + fn inner_contains_value(&self) -> bool; +} + +impl WatchBehavior for Watch { + fn inner_poll_peek(&self, cx: &mut Context<'_>) -> Poll { + self.mutex.lock(|state| { + let mut s = state.borrow_mut(); + match &s.data { + Some(data) => Poll::Ready(data.clone()), + None => { + s.wakers.register(cx.waker()); + Poll::Pending + } + } + }) + } + + fn inner_try_peek(&self) -> Option { + self.mutex.lock(|state| state.borrow().data.clone()) + } + + fn inner_poll_get(&self, id: &mut u64, cx: &mut Context<'_>) -> Poll { + self.mutex.lock(|state| { + let mut s = state.borrow_mut(); + match &s.data { + Some(data) => { + *id = s.current_id; + Poll::Ready(data.clone()) + } + None => { + s.wakers.register(cx.waker()); + Poll::Pending + } + } + }) + } + + fn inner_try_get(&self, id: &mut u64) -> Option { + self.mutex.lock(|state| { + let s = state.borrow(); + *id = s.current_id; + state.borrow().data.clone() + }) + } + + fn inner_poll_changed(&self, id: &mut u64, cx: &mut Context<'_>) -> Poll { + self.mutex.lock(|state| { + let mut s = state.borrow_mut(); + match (&s.data, s.current_id > *id) { + (Some(data), true) => { + *id = s.current_id; + Poll::Ready(data.clone()) + } + _ => { + s.wakers.register(cx.waker()); + Poll::Pending + } + } + }) + } + + fn inner_try_changed(&self, id: &mut u64) -> Option { + self.mutex.lock(|state| { + let s = state.borrow(); + match s.current_id > *id { + true => { + *id = s.current_id; + state.borrow().data.clone() + } + false => None, + } + }) + } + + fn inner_contains_value(&self) -> bool { + self.mutex.lock(|state| state.borrow().data.is_some()) + } +} + +#[derive(Debug)] +/// An error that can occur when a `Watch` returns a `Result::Err(_)`. +pub enum Error { + /// The maximum number of [`Receiver`](crate::watch::Receiver)/[`DynReceiver`](crate::watch::DynReceiver) has been reached. + MaximumReceiversReached, +} + +impl<'a, M: RawMutex, T: Clone, const N: usize> Watch { + /// Create a new `Watch` channel. + pub const fn new() -> Self { + Self { + mutex: Mutex::new(RefCell::new(WatchState { + data: None, + current_id: 0, + wakers: MultiWakerRegistration::new(), + receiver_count: 0, + })), + } + } + + /// Write a new value to the `Watch`. + pub fn write(&self, val: T) { + self.mutex.lock(|state| { + let mut s = state.borrow_mut(); + s.data = Some(val); + s.current_id += 1; + s.wakers.wake(); + }) + } + + /// Create a new [`Receiver`] for the `Watch`. + pub fn receiver(&self) -> Result, Error> { + self.mutex.lock(|state| { + let mut s = state.borrow_mut(); + if s.receiver_count < N { + s.receiver_count += 1; + Ok(Receiver(Rcv::new(self))) + } else { + Err(Error::MaximumReceiversReached) + } + }) + } + + /// Create a new [`DynReceiver`] for the `Watch`. + pub fn dyn_receiver(&self) -> Result, Error> { + self.mutex.lock(|state| { + let mut s = state.borrow_mut(); + if s.receiver_count < N { + s.receiver_count += 1; + Ok(DynReceiver(Rcv::new(self))) + } else { + Err(Error::MaximumReceiversReached) + } + }) + } + + /// Tries to retrieve the value of the `Watch`. + pub fn try_peek(&self) -> Option { + self.inner_try_peek() + } + + /// Returns true if the `Watch` contains a value. + pub fn contains_value(&self) -> bool { + self.inner_contains_value() + } + + /// Clears the value of the `Watch`. This will cause calls to [`Rcv::get`] and [`Rcv::peek`] to be pending. + pub fn clear(&self) { + self.mutex.lock(|state| { + let mut s = state.borrow_mut(); + s.data = None; + }) + } +} + +/// A receiver can `.await` a change in the `Watch` value. +pub struct Rcv<'a, T: Clone, W: WatchBehavior + ?Sized> { + watch: &'a W, + at_id: u64, + _phantom: PhantomData, +} + +impl<'a, T: Clone, W: WatchBehavior + ?Sized> Rcv<'a, T, W> { + /// Creates a new `Receiver` with a reference to the `Watch`. + fn new(watch: &'a W) -> Self { + Self { + watch, + at_id: 0, + _phantom: PhantomData, + } + } + + /// Returns the current value of the `Watch` if it is initialized, **without** marking it as seen. + pub async fn peek(&self) -> T { + poll_fn(|cx| self.watch.inner_poll_peek(cx)).await + } + + /// Tries to peek the current value of the `Watch` without waiting, and **without** marking it as seen. + pub fn try_peek(&self) -> Option { + self.watch.inner_try_peek() + } + + /// Returns the current value of the `Watch` if it is initialized, marking it as seen. + pub async fn get(&mut self) -> T { + poll_fn(|cx| self.watch.inner_poll_get(&mut self.at_id, cx)).await + } + + /// Tries to get the current value of the `Watch` without waiting, marking it as seen. + pub fn try_get(&mut self) -> Option { + self.watch.inner_try_get(&mut self.at_id) + } + + /// Waits for the `Watch` to change and returns the new value, marking it as seen. + pub async fn changed(&mut self) -> T { + poll_fn(|cx| self.watch.inner_poll_changed(&mut self.at_id, cx)).await + } + + /// Tries to get the new value of the watch without waiting, marking it as seen. + pub fn try_changed(&mut self) -> Option { + self.watch.inner_try_changed(&mut self.at_id) + } + + /// Checks if the `Watch` contains a value. If this returns true, + /// then awaiting [`Rcv::get`] and [`Rcv::peek`] will return immediately. + pub fn contains_value(&self) -> bool { + self.watch.inner_contains_value() + } +} + +/// A receiver of a `Watch` channel. +pub struct Receiver<'a, M: RawMutex, T: Clone, const N: usize>(Rcv<'a, T, Watch>); + +/// A receiver which holds a **reference** to a `Watch` channel. +/// +/// This is an alternative to [`Receiver`] with a simpler type definition, at the expense of +/// some runtime performance due to dynamic dispatch. +pub struct DynReceiver<'a, T: Clone>(Rcv<'a, T, dyn WatchBehavior + 'a>); + +impl<'a, M: RawMutex, T: Clone, const N: usize> Deref for Receiver<'a, M, T, N> { + type Target = Rcv<'a, T, Watch>; + + fn deref(&self) -> &Self::Target { + &self.0 + } +} + +impl<'a, M: RawMutex, T: Clone, const N: usize> DerefMut for Receiver<'a, M, T, N> { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.0 + } +} + +impl<'a, T: Clone> Deref for DynReceiver<'a, T> { + type Target = Rcv<'a, T, dyn WatchBehavior + 'a>; + + fn deref(&self) -> &Self::Target { + &self.0 + } +} + +impl<'a, T: Clone> DerefMut for DynReceiver<'a, T> { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.0 + } +} + +#[cfg(test)] +mod tests { + use futures_executor::block_on; + + use super::*; + use crate::blocking_mutex::raw::CriticalSectionRawMutex; + + #[test] + fn multiple_writes() { + let f = async { + static WATCH: Watch = Watch::new(); + + // Obtain Receivers + let mut rcv0 = WATCH.receiver().unwrap(); + let mut rcv1 = WATCH.dyn_receiver().unwrap(); + + WATCH.write(10); + + // Receive the new value + assert_eq!(rcv0.changed().await, 10); + assert_eq!(rcv1.changed().await, 10); + + // No update + assert_eq!(rcv0.try_changed(), None); + assert_eq!(rcv1.try_changed(), None); + + WATCH.write(20); + + assert_eq!(rcv0.changed().await, 20); + assert_eq!(rcv1.changed().await, 20); + }; + block_on(f); + } + + #[test] + fn max_receivers() { + let f = async { + static WATCH: Watch = Watch::new(); + + // Obtain Receivers + let _ = WATCH.receiver().unwrap(); + let _ = WATCH.receiver().unwrap(); + assert!(WATCH.receiver().is_err()); + }; + block_on(f); + } + + #[test] + fn receive_initial() { + let f = async { + static WATCH: Watch = Watch::new(); + + // Obtain Receivers + let mut rcv0 = WATCH.receiver().unwrap(); + let mut rcv1 = WATCH.receiver().unwrap(); + + assert_eq!(rcv0.contains_value(), false); + + assert_eq!(rcv0.try_changed(), None); + assert_eq!(rcv1.try_changed(), None); + + WATCH.write(0); + + assert_eq!(rcv0.contains_value(), true); + + assert_eq!(rcv0.try_changed(), Some(0)); + assert_eq!(rcv1.try_changed(), Some(0)); + }; + block_on(f); + } + + #[test] + fn peek_get_changed() { + let f = async { + static WATCH: Watch = Watch::new(); + + // Obtain Receivers + let mut rcv0 = WATCH.receiver().unwrap(); + + WATCH.write(10); + + // Ensure peek does not mark as seen + assert_eq!(rcv0.peek().await, 10); + assert_eq!(rcv0.try_changed(), Some(10)); + assert_eq!(rcv0.try_changed(), None); + assert_eq!(rcv0.peek().await, 10); + + WATCH.write(20); + + // Ensure get does mark as seen + assert_eq!(rcv0.get().await, 20); + assert_eq!(rcv0.try_changed(), None); + assert_eq!(rcv0.try_get(), Some(20)); + }; + block_on(f); + } + + #[test] + fn count_ids() { + let f = async { + static WATCH: Watch = Watch::new(); + + // Obtain Receivers + let mut rcv0 = WATCH.receiver().unwrap(); + let mut rcv1 = WATCH.receiver().unwrap(); + + let get_id = || WATCH.mutex.lock(|state| state.borrow().current_id); + + WATCH.write(10); + + assert_eq!(rcv0.changed().await, 10); + assert_eq!(rcv1.changed().await, 10); + + assert_eq!(rcv0.try_changed(), None); + assert_eq!(rcv1.try_changed(), None); + + WATCH.write(20); + WATCH.write(20); + WATCH.write(20); + + assert_eq!(rcv0.changed().await, 20); + assert_eq!(rcv1.changed().await, 20); + + assert_eq!(rcv0.try_changed(), None); + assert_eq!(rcv1.try_changed(), None); + + assert_eq!(get_id(), 4); + }; + block_on(f); + } + + #[test] + fn peek_still_await() { + let f = async { + static WATCH: Watch = Watch::new(); + + // Obtain Receivers + let mut rcv0 = WATCH.receiver().unwrap(); + let mut rcv1 = WATCH.receiver().unwrap(); + + WATCH.write(10); + + assert_eq!(rcv0.peek().await, 10); + assert_eq!(rcv1.try_peek(), Some(10)); + + assert_eq!(rcv0.changed().await, 10); + assert_eq!(rcv1.changed().await, 10); + }; + block_on(f); + } + + #[test] + fn peek_with_static() { + let f = async { + static WATCH: Watch = Watch::new(); + + // Obtain Receivers + let rcv0 = WATCH.receiver().unwrap(); + let rcv1 = WATCH.receiver().unwrap(); + + WATCH.write(20); + + assert_eq!(rcv0.peek().await, 20); + assert_eq!(rcv1.peek().await, 20); + assert_eq!(WATCH.try_peek(), Some(20)); + }; + block_on(f); + } +} From ae2f10992149279884ea564b00eb18c8bf1f464e Mon Sep 17 00:00:00 2001 From: Peter Krull Date: Thu, 29 Feb 2024 16:45:44 +0100 Subject: [PATCH 007/361] Added sender types, support for dropping receivers, converting to dyn-types, revised tests. --- embassy-sync/src/watch.rs | 537 +++++++++++++++++++++++++++----------- 1 file changed, 382 insertions(+), 155 deletions(-) diff --git a/embassy-sync/src/watch.rs b/embassy-sync/src/watch.rs index 7e5e92741..3e22b1e7b 100644 --- a/embassy-sync/src/watch.rs +++ b/embassy-sync/src/watch.rs @@ -1,4 +1,4 @@ -//! A synchronization primitive for passing the latest value to **multiple** tasks. +//! A synchronization primitive for passing the latest value to **multiple** receivers. use core::cell::RefCell; use core::future::poll_fn; @@ -10,22 +10,17 @@ use crate::blocking_mutex::raw::RawMutex; use crate::blocking_mutex::Mutex; use crate::waitqueue::MultiWakerRegistration; -/// A `Watch` is a single-slot signaling primitive, which can awake `N` up to separate [`Receiver`]s. +/// The `Watch` is a single-slot signaling primitive that allows multiple receivers to concurrently await +/// changes to the value. Unlike a [`Signal`](crate::signal::Signal), `Watch` supports multiple receivers, +/// and unlike a [`PubSubChannel`](crate::pubsub::PubSubChannel), `Watch` immediately overwrites the previous +/// value when a new one is sent, without waiting for all receivers to read the previous value. /// -/// Similar to a [`Signal`](crate::signal::Signal), except `Watch` allows for multiple tasks to -/// `.await` the latest value, and all receive it. +/// This makes `Watch` particularly useful when a single task updates a value or "state", and multiple other tasks +/// need to be notified about changes to this value asynchronously. Receivers may "lose" stale values, as they are +/// always provided with the latest value. /// -/// This is similar to a [`PubSubChannel`](crate::pubsub::PubSubChannel) with a buffer size of 1, except -/// "sending" to it (calling [`Watch::write`]) will immediately overwrite the previous value instead -/// of waiting for the receivers to pop the previous value. -/// -/// `Watch` is useful when a single task is responsible for updating a value or "state", which multiple other -/// tasks are interested in getting notified about changes to the latest value of. It is therefore fine for -/// [`Receiver`]s to "lose" stale values. -/// -/// Anyone with a reference to the Watch can update or peek the value. Watches are generally declared -/// as `static`s and then borrowed as required to either [`Watch::peek`] the value or obtain a [`Receiver`] -/// with [`Watch::receiver`] which has async methods. +/// Typically, `Watch` instances are declared as `static`, and a [`Sender`] and [`Receiver`] +/// (or [`DynSender`] and/or [`DynReceiver`]) are obtained and passed to the relevant parts of the program. /// ``` /// /// use futures_executor::block_on; @@ -36,18 +31,18 @@ use crate::waitqueue::MultiWakerRegistration; /// /// static WATCH: Watch = Watch::new(); /// -/// // Obtain Receivers +/// // Obtain receivers and sender /// let mut rcv0 = WATCH.receiver().unwrap(); -/// let mut rcv1 = WATCH.receiver().unwrap(); -/// assert!(WATCH.receiver().is_err()); +/// let mut rcv1 = WATCH.dyn_receiver().unwrap(); +/// let mut snd = WATCH.sender(); /// +/// // No more receivers, and no update +/// assert!(WATCH.receiver().is_err()); /// assert_eq!(rcv1.try_changed(), None); /// -/// WATCH.write(10); -/// assert_eq!(WATCH.try_peek(), Some(10)); -/// +/// snd.send(10); /// -/// // Receive the new value +/// // Receive the new value (async or try) /// assert_eq!(rcv0.changed().await, 10); /// assert_eq!(rcv1.try_changed(), Some(10)); /// @@ -55,13 +50,14 @@ use crate::waitqueue::MultiWakerRegistration; /// assert_eq!(rcv0.try_changed(), None); /// assert_eq!(rcv1.try_changed(), None); /// -/// WATCH.write(20); +/// snd.send(20); /// -/// // Defference `between` peek `get`. +/// // Peek does not mark the value as seen /// assert_eq!(rcv0.peek().await, 20); -/// assert_eq!(rcv1.get().await, 20); -/// /// assert_eq!(rcv0.try_changed(), Some(20)); +/// +/// // Get marks the value as seen +/// assert_eq!(rcv1.get().await, 20); /// assert_eq!(rcv1.try_changed(), None); /// /// }; @@ -80,30 +76,57 @@ struct WatchState { /// A trait representing the 'inner' behavior of the `Watch`. pub trait WatchBehavior { + /// Sends a new value to the `Watch`. + fn send(&self, val: T); + + /// Clears the value of the `Watch`. + fn clear(&self); + /// Poll the `Watch` for the current value, **without** making it as seen. - fn inner_poll_peek(&self, cx: &mut Context<'_>) -> Poll; + fn poll_peek(&self, cx: &mut Context<'_>) -> Poll; /// Tries to peek the value of the `Watch`, **without** marking it as seen. - fn inner_try_peek(&self) -> Option; + fn try_peek(&self) -> Option; /// Poll the `Watch` for the current value, making it as seen. - fn inner_poll_get(&self, id: &mut u64, cx: &mut Context<'_>) -> Poll; + fn poll_get(&self, id: &mut u64, cx: &mut Context<'_>) -> Poll; /// Tries to get the value of the `Watch`, marking it as seen. - fn inner_try_get(&self, id: &mut u64) -> Option; + fn try_get(&self, id: &mut u64) -> Option; /// Poll the `Watch` for a changed value, marking it as seen. - fn inner_poll_changed(&self, id: &mut u64, cx: &mut Context<'_>) -> Poll; + fn poll_changed(&self, id: &mut u64, cx: &mut Context<'_>) -> Poll; /// Tries to retrieve the value of the `Watch` if it has changed, marking it as seen. - fn inner_try_changed(&self, id: &mut u64) -> Option; + fn try_changed(&self, id: &mut u64) -> Option; /// Checks if the `Watch` is been initialized with a value. - fn inner_contains_value(&self) -> bool; + fn contains_value(&self) -> bool; + + /// Used when a receiver is dropped to decrement the receiver count. + /// + /// ## This method should not be called by the user. + fn drop_receiver(&self); } impl WatchBehavior for Watch { - fn inner_poll_peek(&self, cx: &mut Context<'_>) -> Poll { + fn send(&self, val: T) { + self.mutex.lock(|state| { + let mut s = state.borrow_mut(); + s.data = Some(val); + s.current_id += 1; + s.wakers.wake(); + }) + } + + fn clear(&self) { + self.mutex.lock(|state| { + let mut s = state.borrow_mut(); + s.data = None; + }) + } + + fn poll_peek(&self, cx: &mut Context<'_>) -> Poll { self.mutex.lock(|state| { let mut s = state.borrow_mut(); match &s.data { @@ -116,11 +139,11 @@ impl WatchBehavior for Watch }) } - fn inner_try_peek(&self) -> Option { + fn try_peek(&self) -> Option { self.mutex.lock(|state| state.borrow().data.clone()) } - fn inner_poll_get(&self, id: &mut u64, cx: &mut Context<'_>) -> Poll { + fn poll_get(&self, id: &mut u64, cx: &mut Context<'_>) -> Poll { self.mutex.lock(|state| { let mut s = state.borrow_mut(); match &s.data { @@ -136,7 +159,7 @@ impl WatchBehavior for Watch }) } - fn inner_try_get(&self, id: &mut u64) -> Option { + fn try_get(&self, id: &mut u64) -> Option { self.mutex.lock(|state| { let s = state.borrow(); *id = s.current_id; @@ -144,7 +167,7 @@ impl WatchBehavior for Watch }) } - fn inner_poll_changed(&self, id: &mut u64, cx: &mut Context<'_>) -> Poll { + fn poll_changed(&self, id: &mut u64, cx: &mut Context<'_>) -> Poll { self.mutex.lock(|state| { let mut s = state.borrow_mut(); match (&s.data, s.current_id > *id) { @@ -160,7 +183,7 @@ impl WatchBehavior for Watch }) } - fn inner_try_changed(&self, id: &mut u64) -> Option { + fn try_changed(&self, id: &mut u64) -> Option { self.mutex.lock(|state| { let s = state.borrow(); match s.current_id > *id { @@ -173,9 +196,16 @@ impl WatchBehavior for Watch }) } - fn inner_contains_value(&self) -> bool { + fn contains_value(&self) -> bool { self.mutex.lock(|state| state.borrow().data.is_some()) } + + fn drop_receiver(&self) { + self.mutex.lock(|state| { + let mut s = state.borrow_mut(); + s.receiver_count -= 1; + }) + } } #[derive(Debug)] @@ -198,14 +228,14 @@ impl<'a, M: RawMutex, T: Clone, const N: usize> Watch { } } - /// Write a new value to the `Watch`. - pub fn write(&self, val: T) { - self.mutex.lock(|state| { - let mut s = state.borrow_mut(); - s.data = Some(val); - s.current_id += 1; - s.wakers.wake(); - }) + /// Create a new [`Receiver`] for the `Watch`. + pub fn sender(&self) -> Sender<'_, M, T, N> { + Sender(Snd::new(self)) + } + + /// Create a new [`DynReceiver`] for the `Watch`. + pub fn dyn_sender(&self) -> DynSender<'_, T> { + DynSender(Snd::new(self)) } /// Create a new [`Receiver`] for the `Watch`. @@ -214,7 +244,7 @@ impl<'a, M: RawMutex, T: Clone, const N: usize> Watch { let mut s = state.borrow_mut(); if s.receiver_count < N { s.receiver_count += 1; - Ok(Receiver(Rcv::new(self))) + Ok(Receiver(Rcv::new(self, 0))) } else { Err(Error::MaximumReceiversReached) } @@ -227,29 +257,121 @@ impl<'a, M: RawMutex, T: Clone, const N: usize> Watch { let mut s = state.borrow_mut(); if s.receiver_count < N { s.receiver_count += 1; - Ok(DynReceiver(Rcv::new(self))) + Ok(DynReceiver(Rcv::new(self, 0))) } else { Err(Error::MaximumReceiversReached) } }) } +} + +/// A receiver can `.await` a change in the `Watch` value. +pub struct Snd<'a, T: Clone, W: WatchBehavior + ?Sized> { + watch: &'a W, + _phantom: PhantomData, +} + +impl<'a, T: Clone, W: WatchBehavior + ?Sized> Clone for Snd<'a, T, W> { + fn clone(&self) -> Self { + Self { + watch: self.watch, + _phantom: PhantomData, + } + } +} + +impl<'a, T: Clone, W: WatchBehavior + ?Sized> Snd<'a, T, W> { + /// Creates a new `Receiver` with a reference to the `Watch`. + fn new(watch: &'a W) -> Self { + Self { + watch, + _phantom: PhantomData, + } + } + + /// Sends a new value to the `Watch`. + pub fn send(&self, val: T) { + self.watch.send(val) + } + + /// Clears the value of the `Watch`. + /// This will cause calls to [`Rcv::get`] and [`Rcv::peek`] to be pending. + pub fn clear(&self) { + self.watch.clear() + } /// Tries to retrieve the value of the `Watch`. pub fn try_peek(&self) -> Option { - self.inner_try_peek() + self.watch.try_peek() } /// Returns true if the `Watch` contains a value. pub fn contains_value(&self) -> bool { - self.inner_contains_value() + self.watch.contains_value() } +} - /// Clears the value of the `Watch`. This will cause calls to [`Rcv::get`] and [`Rcv::peek`] to be pending. - pub fn clear(&self) { - self.mutex.lock(|state| { - let mut s = state.borrow_mut(); - s.data = None; - }) +/// A sender of a `Watch` channel. +/// +/// For a simpler type definition, consider [`DynSender`] at the expense of +/// some runtime performance due to dynamic dispatch. +pub struct Sender<'a, M: RawMutex, T: Clone, const N: usize>(Snd<'a, T, Watch>); + +impl<'a, M: RawMutex, T: Clone, const N: usize> Clone for Sender<'a, M, T, N> { + fn clone(&self) -> Self { + Self(self.0.clone()) + } +} + +impl<'a, M: RawMutex, T: Clone, const N: usize> Sender<'a, M, T, N> { + /// Converts the `Sender` into a [`DynSender`]. + pub fn as_dyn(self) -> DynSender<'a, T> { + DynSender(Snd::new(self.watch)) + } +} + +impl<'a, M: RawMutex, T: Clone, const N: usize> Into> for Sender<'a, M, T, N> { + fn into(self) -> DynSender<'a, T> { + self.as_dyn() + } +} + +impl<'a, M: RawMutex, T: Clone, const N: usize> Deref for Sender<'a, M, T, N> { + type Target = Snd<'a, T, Watch>; + + fn deref(&self) -> &Self::Target { + &self.0 + } +} + +impl<'a, M: RawMutex, T: Clone, const N: usize> DerefMut for Sender<'a, M, T, N> { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.0 + } +} + +/// A sender which holds a **dynamic** reference to a `Watch` channel. +/// +/// This is an alternative to [`Sender`] with a simpler type definition, +pub struct DynSender<'a, T: Clone>(Snd<'a, T, dyn WatchBehavior + 'a>); + +impl<'a, T: Clone> Clone for DynSender<'a, T> { + fn clone(&self) -> Self { + Self(self.0.clone()) + } +} + +impl<'a, T: Clone> Deref for DynSender<'a, T> { + type Target = Snd<'a, T, dyn WatchBehavior + 'a>; + + fn deref(&self) -> &Self::Target { + &self.0 + } +} + +impl<'a, T: Clone> DerefMut for DynSender<'a, T> { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.0 } } @@ -262,59 +384,83 @@ pub struct Rcv<'a, T: Clone, W: WatchBehavior + ?Sized> { impl<'a, T: Clone, W: WatchBehavior + ?Sized> Rcv<'a, T, W> { /// Creates a new `Receiver` with a reference to the `Watch`. - fn new(watch: &'a W) -> Self { + fn new(watch: &'a W, at_id: u64) -> Self { Self { watch, - at_id: 0, + at_id, _phantom: PhantomData, } } - /// Returns the current value of the `Watch` if it is initialized, **without** marking it as seen. + /// Returns the current value of the `Watch` once it is initialized, **without** marking it as seen. + /// + /// **Note**: Futures do nothing unless you `.await` or poll them. pub async fn peek(&self) -> T { - poll_fn(|cx| self.watch.inner_poll_peek(cx)).await + poll_fn(|cx| self.watch.poll_peek(cx)).await } /// Tries to peek the current value of the `Watch` without waiting, and **without** marking it as seen. pub fn try_peek(&self) -> Option { - self.watch.inner_try_peek() + self.watch.try_peek() } - /// Returns the current value of the `Watch` if it is initialized, marking it as seen. + /// Returns the current value of the `Watch` once it is initialized, marking it as seen. + /// + /// **Note**: Futures do nothing unless you `.await` or poll them. pub async fn get(&mut self) -> T { - poll_fn(|cx| self.watch.inner_poll_get(&mut self.at_id, cx)).await + poll_fn(|cx| self.watch.poll_get(&mut self.at_id, cx)).await } /// Tries to get the current value of the `Watch` without waiting, marking it as seen. pub fn try_get(&mut self) -> Option { - self.watch.inner_try_get(&mut self.at_id) + self.watch.try_get(&mut self.at_id) } /// Waits for the `Watch` to change and returns the new value, marking it as seen. + /// + /// **Note**: Futures do nothing unless you `.await` or poll them. pub async fn changed(&mut self) -> T { - poll_fn(|cx| self.watch.inner_poll_changed(&mut self.at_id, cx)).await + poll_fn(|cx| self.watch.poll_changed(&mut self.at_id, cx)).await } /// Tries to get the new value of the watch without waiting, marking it as seen. pub fn try_changed(&mut self) -> Option { - self.watch.inner_try_changed(&mut self.at_id) + self.watch.try_changed(&mut self.at_id) } /// Checks if the `Watch` contains a value. If this returns true, /// then awaiting [`Rcv::get`] and [`Rcv::peek`] will return immediately. pub fn contains_value(&self) -> bool { - self.watch.inner_contains_value() + self.watch.contains_value() + } +} + +impl<'a, T: Clone, W: WatchBehavior + ?Sized> Drop for Rcv<'a, T, W> { + fn drop(&mut self) { + self.watch.drop_receiver(); } } /// A receiver of a `Watch` channel. pub struct Receiver<'a, M: RawMutex, T: Clone, const N: usize>(Rcv<'a, T, Watch>); -/// A receiver which holds a **reference** to a `Watch` channel. -/// -/// This is an alternative to [`Receiver`] with a simpler type definition, at the expense of -/// some runtime performance due to dynamic dispatch. -pub struct DynReceiver<'a, T: Clone>(Rcv<'a, T, dyn WatchBehavior + 'a>); +impl<'a, M: RawMutex, T: Clone, const N: usize> Receiver<'a, M, T, N> { + /// Converts the `Receiver` into a [`DynReceiver`]. + pub fn as_dyn(self) -> DynReceiver<'a, T> { + // We need to increment the receiver count since the original + // receiver is being dropped, which decrements the count. + self.watch.mutex.lock(|state| { + state.borrow_mut().receiver_count += 1; + }); + DynReceiver(Rcv::new(self.0.watch, self.at_id)) + } +} + +impl<'a, M: RawMutex, T: Clone, const N: usize> Into> for Receiver<'a, M, T, N> { + fn into(self) -> DynReceiver<'a, T> { + self.as_dyn() + } +} impl<'a, M: RawMutex, T: Clone, const N: usize> Deref for Receiver<'a, M, T, N> { type Target = Rcv<'a, T, Watch>; @@ -330,6 +476,12 @@ impl<'a, M: RawMutex, T: Clone, const N: usize> DerefMut for Receiver<'a, M, T, } } +/// A receiver which holds a **dynamic** reference to a `Watch` channel. +/// +/// This is an alternative to [`Receiver`] with a simpler type definition, at the expense of +/// some runtime performance due to dynamic dispatch. +pub struct DynReceiver<'a, T: Clone>(Rcv<'a, T, dyn WatchBehavior + 'a>); + impl<'a, T: Clone> Deref for DynReceiver<'a, T> { type Target = Rcv<'a, T, dyn WatchBehavior + 'a>; @@ -348,167 +500,242 @@ impl<'a, T: Clone> DerefMut for DynReceiver<'a, T> { mod tests { use futures_executor::block_on; - use super::*; + use super::Watch; use crate::blocking_mutex::raw::CriticalSectionRawMutex; #[test] - fn multiple_writes() { + fn multiple_sends() { let f = async { - static WATCH: Watch = Watch::new(); + static WATCH: Watch = Watch::new(); - // Obtain Receivers - let mut rcv0 = WATCH.receiver().unwrap(); - let mut rcv1 = WATCH.dyn_receiver().unwrap(); + // Obtain receiver and sender + let mut rcv = WATCH.receiver().unwrap(); + let snd = WATCH.sender(); - WATCH.write(10); + // Not initialized + assert_eq!(rcv.try_changed(), None); // Receive the new value - assert_eq!(rcv0.changed().await, 10); - assert_eq!(rcv1.changed().await, 10); + snd.send(10); + assert_eq!(rcv.changed().await, 10); + + // Receive another value + snd.send(20); + assert_eq!(rcv.try_changed(), Some(20)); // No update - assert_eq!(rcv0.try_changed(), None); - assert_eq!(rcv1.try_changed(), None); - - WATCH.write(20); - - assert_eq!(rcv0.changed().await, 20); - assert_eq!(rcv1.changed().await, 20); + assert_eq!(rcv.try_changed(), None); }; block_on(f); } #[test] - fn max_receivers() { + fn receive_after_create() { let f = async { - static WATCH: Watch = Watch::new(); + static WATCH: Watch = Watch::new(); - // Obtain Receivers - let _ = WATCH.receiver().unwrap(); - let _ = WATCH.receiver().unwrap(); - assert!(WATCH.receiver().is_err()); + // Obtain sender and send value + let snd = WATCH.sender(); + snd.send(10); + + // Obtain receiver and receive value + let mut rcv = WATCH.receiver().unwrap(); + assert_eq!(rcv.try_changed(), Some(10)); }; block_on(f); } #[test] - fn receive_initial() { + fn max_receivers_drop() { let f = async { static WATCH: Watch = Watch::new(); - // Obtain Receivers + // Try to create 3 receivers (only 2 can exist at once) + let rcv0 = WATCH.receiver(); + let rcv1 = WATCH.receiver(); + let rcv2 = WATCH.receiver(); + + // Ensure the first two are successful and the third is not + assert!(rcv0.is_ok()); + assert!(rcv1.is_ok()); + assert!(rcv2.is_err()); + + // Drop the first receiver + drop(rcv0); + + // Create another receiver and ensure it is successful + let rcv3 = WATCH.receiver(); + assert!(rcv3.is_ok()); + }; + block_on(f); + } + + #[test] + fn multiple_receivers() { + let f = async { + static WATCH: Watch = Watch::new(); + + // Obtain receivers and sender let mut rcv0 = WATCH.receiver().unwrap(); let mut rcv1 = WATCH.receiver().unwrap(); + let snd = WATCH.sender(); - assert_eq!(rcv0.contains_value(), false); - + // No update for both assert_eq!(rcv0.try_changed(), None); assert_eq!(rcv1.try_changed(), None); - WATCH.write(0); - - assert_eq!(rcv0.contains_value(), true); + // Send a new value + snd.send(0); + // Both receivers receive the new value assert_eq!(rcv0.try_changed(), Some(0)); assert_eq!(rcv1.try_changed(), Some(0)); }; block_on(f); } + #[test] + fn clone_senders() { + let f = async { + // Obtain different ways to send + static WATCH: Watch = Watch::new(); + let snd0 = WATCH.sender(); + let snd1 = snd0.clone(); + + // Obtain Receiver + let mut rcv = WATCH.receiver().unwrap().as_dyn(); + + // Send a value from first sender + snd0.send(10); + assert_eq!(rcv.try_changed(), Some(10)); + + // Send a value from second sender + snd1.send(20); + assert_eq!(rcv.try_changed(), Some(20)); + }; + block_on(f); + } + #[test] fn peek_get_changed() { let f = async { static WATCH: Watch = Watch::new(); - // Obtain Receivers - let mut rcv0 = WATCH.receiver().unwrap(); + // Obtain receiver and sender + let mut rcv = WATCH.receiver().unwrap(); + let snd = WATCH.sender(); - WATCH.write(10); + // Send a value + snd.send(10); // Ensure peek does not mark as seen - assert_eq!(rcv0.peek().await, 10); - assert_eq!(rcv0.try_changed(), Some(10)); - assert_eq!(rcv0.try_changed(), None); - assert_eq!(rcv0.peek().await, 10); + assert_eq!(rcv.peek().await, 10); + assert_eq!(rcv.try_changed(), Some(10)); + assert_eq!(rcv.try_changed(), None); + assert_eq!(rcv.try_peek(), Some(10)); - WATCH.write(20); + // Send a value + snd.send(20); // Ensure get does mark as seen - assert_eq!(rcv0.get().await, 20); - assert_eq!(rcv0.try_changed(), None); - assert_eq!(rcv0.try_get(), Some(20)); + assert_eq!(rcv.get().await, 20); + assert_eq!(rcv.try_changed(), None); + assert_eq!(rcv.try_get(), Some(20)); }; block_on(f); } #[test] - fn count_ids() { + fn use_dynamics() { let f = async { static WATCH: Watch = Watch::new(); - // Obtain Receivers - let mut rcv0 = WATCH.receiver().unwrap(); - let mut rcv1 = WATCH.receiver().unwrap(); + // Obtain receiver and sender + let mut dyn_rcv = WATCH.dyn_receiver().unwrap(); + let dyn_snd = WATCH.dyn_sender(); - let get_id = || WATCH.mutex.lock(|state| state.borrow().current_id); + // Send a value + dyn_snd.send(10); - WATCH.write(10); - - assert_eq!(rcv0.changed().await, 10); - assert_eq!(rcv1.changed().await, 10); - - assert_eq!(rcv0.try_changed(), None); - assert_eq!(rcv1.try_changed(), None); - - WATCH.write(20); - WATCH.write(20); - WATCH.write(20); - - assert_eq!(rcv0.changed().await, 20); - assert_eq!(rcv1.changed().await, 20); - - assert_eq!(rcv0.try_changed(), None); - assert_eq!(rcv1.try_changed(), None); - - assert_eq!(get_id(), 4); + // Ensure the dynamic receiver receives the value + assert_eq!(dyn_rcv.try_changed(), Some(10)); + assert_eq!(dyn_rcv.try_changed(), None); }; block_on(f); } #[test] - fn peek_still_await() { + fn convert_to_dyn() { let f = async { static WATCH: Watch = Watch::new(); - // Obtain Receivers - let mut rcv0 = WATCH.receiver().unwrap(); - let mut rcv1 = WATCH.receiver().unwrap(); + // Obtain receiver and sender + let rcv = WATCH.receiver().unwrap(); + let snd = WATCH.sender(); - WATCH.write(10); + // Convert to dynamic + let mut dyn_rcv = rcv.as_dyn(); + let dyn_snd = snd.as_dyn(); - assert_eq!(rcv0.peek().await, 10); - assert_eq!(rcv1.try_peek(), Some(10)); + // Send a value + dyn_snd.send(10); - assert_eq!(rcv0.changed().await, 10); - assert_eq!(rcv1.changed().await, 10); + // Ensure the dynamic receiver receives the value + assert_eq!(dyn_rcv.try_changed(), Some(10)); + assert_eq!(dyn_rcv.try_changed(), None); }; block_on(f); } #[test] - fn peek_with_static() { + fn dynamic_receiver_count() { let f = async { static WATCH: Watch = Watch::new(); - // Obtain Receivers - let rcv0 = WATCH.receiver().unwrap(); - let rcv1 = WATCH.receiver().unwrap(); + // Obtain receiver and sender + let rcv0 = WATCH.receiver(); + let rcv1 = WATCH.receiver(); + let rcv2 = WATCH.receiver(); - WATCH.write(20); + // Ensure the first two are successful and the third is not + assert!(rcv0.is_ok()); + assert!(rcv1.is_ok()); + assert!(rcv2.is_err()); - assert_eq!(rcv0.peek().await, 20); - assert_eq!(rcv1.peek().await, 20); - assert_eq!(WATCH.try_peek(), Some(20)); + // Convert to dynamic + let dyn_rcv0 = rcv0.unwrap().as_dyn(); + + // Drop the (now dynamic) receiver + drop(dyn_rcv0); + + // Create another receiver and ensure it is successful + let rcv3 = WATCH.receiver(); + let rcv4 = WATCH.receiver(); + assert!(rcv3.is_ok()); + assert!(rcv4.is_err()); + }; + block_on(f); + } + + #[test] + fn contains_value() { + let f = async { + static WATCH: Watch = Watch::new(); + + // Obtain receiver and sender + let rcv = WATCH.receiver().unwrap(); + let snd = WATCH.sender(); + + // check if the watch contains a value + assert_eq!(rcv.contains_value(), false); + assert_eq!(snd.contains_value(), false); + + // Send a value + snd.send(10); + + // check if the watch contains a value + assert_eq!(rcv.contains_value(), true); + assert_eq!(snd.contains_value(), true); }; block_on(f); } From 3208e0fec4717ff1580d1cc0cf93e18cbba2db91 Mon Sep 17 00:00:00 2001 From: Peter Krull Date: Thu, 29 Feb 2024 16:56:52 +0100 Subject: [PATCH 008/361] Use Option instead of Result for receiver creation since it is the only way it can fail. --- embassy-sync/src/watch.rs | 27 +++++++++++---------------- 1 file changed, 11 insertions(+), 16 deletions(-) diff --git a/embassy-sync/src/watch.rs b/embassy-sync/src/watch.rs index 3e22b1e7b..2bba93915 100644 --- a/embassy-sync/src/watch.rs +++ b/embassy-sync/src/watch.rs @@ -208,14 +208,7 @@ impl WatchBehavior for Watch } } -#[derive(Debug)] -/// An error that can occur when a `Watch` returns a `Result::Err(_)`. -pub enum Error { - /// The maximum number of [`Receiver`](crate::watch::Receiver)/[`DynReceiver`](crate::watch::DynReceiver) has been reached. - MaximumReceiversReached, -} - -impl<'a, M: RawMutex, T: Clone, const N: usize> Watch { +impl Watch { /// Create a new `Watch` channel. pub const fn new() -> Self { Self { @@ -238,28 +231,30 @@ impl<'a, M: RawMutex, T: Clone, const N: usize> Watch { DynSender(Snd::new(self)) } - /// Create a new [`Receiver`] for the `Watch`. - pub fn receiver(&self) -> Result, Error> { + /// Try to create a new [`Receiver`] for the `Watch`. If the + /// maximum number of receivers has been reached, `None` is returned. + pub fn receiver(&self) -> Option> { self.mutex.lock(|state| { let mut s = state.borrow_mut(); if s.receiver_count < N { s.receiver_count += 1; - Ok(Receiver(Rcv::new(self, 0))) + Some(Receiver(Rcv::new(self, 0))) } else { - Err(Error::MaximumReceiversReached) + None } }) } - /// Create a new [`DynReceiver`] for the `Watch`. - pub fn dyn_receiver(&self) -> Result, Error> { + /// Try to create a new [`DynReceiver`] for the `Watch`. If the + /// maximum number of receivers has been reached, `None` is returned. + pub fn dyn_receiver(&self) -> Option> { self.mutex.lock(|state| { let mut s = state.borrow_mut(); if s.receiver_count < N { s.receiver_count += 1; - Ok(DynReceiver(Rcv::new(self, 0))) + Some(DynReceiver(Rcv::new(self, 0))) } else { - Err(Error::MaximumReceiversReached) + None } }) } From c08e75057a4282a0dfee13a6e181a2077944c1b0 Mon Sep 17 00:00:00 2001 From: Peter Krull Date: Thu, 29 Feb 2024 16:58:21 +0100 Subject: [PATCH 009/361] Update tests to reflect changes in previous commit --- embassy-sync/src/watch.rs | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/embassy-sync/src/watch.rs b/embassy-sync/src/watch.rs index 2bba93915..1301eb817 100644 --- a/embassy-sync/src/watch.rs +++ b/embassy-sync/src/watch.rs @@ -551,16 +551,16 @@ mod tests { let rcv2 = WATCH.receiver(); // Ensure the first two are successful and the third is not - assert!(rcv0.is_ok()); - assert!(rcv1.is_ok()); - assert!(rcv2.is_err()); + assert!(rcv0.is_some()); + assert!(rcv1.is_some()); + assert!(rcv2.is_none()); // Drop the first receiver drop(rcv0); // Create another receiver and ensure it is successful let rcv3 = WATCH.receiver(); - assert!(rcv3.is_ok()); + assert!(rcv3.is_some()); }; block_on(f); } @@ -693,9 +693,9 @@ mod tests { let rcv2 = WATCH.receiver(); // Ensure the first two are successful and the third is not - assert!(rcv0.is_ok()); - assert!(rcv1.is_ok()); - assert!(rcv2.is_err()); + assert!(rcv0.is_some()); + assert!(rcv1.is_some()); + assert!(rcv2.is_none()); // Convert to dynamic let dyn_rcv0 = rcv0.unwrap().as_dyn(); @@ -706,8 +706,8 @@ mod tests { // Create another receiver and ensure it is successful let rcv3 = WATCH.receiver(); let rcv4 = WATCH.receiver(); - assert!(rcv3.is_ok()); - assert!(rcv4.is_err()); + assert!(rcv3.is_some()); + assert!(rcv4.is_none()); }; block_on(f); } From df282aa23d00b2f3116081be2b07ba0c9f810fc3 Mon Sep 17 00:00:00 2001 From: Peter Krull Date: Thu, 29 Feb 2024 16:59:58 +0100 Subject: [PATCH 010/361] Forgot to update doc comment --- embassy-sync/src/watch.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/embassy-sync/src/watch.rs b/embassy-sync/src/watch.rs index 1301eb817..01d82def4 100644 --- a/embassy-sync/src/watch.rs +++ b/embassy-sync/src/watch.rs @@ -37,7 +37,7 @@ use crate::waitqueue::MultiWakerRegistration; /// let mut snd = WATCH.sender(); /// /// // No more receivers, and no update -/// assert!(WATCH.receiver().is_err()); +/// assert!(WATCH.receiver().is_none()); /// assert_eq!(rcv1.try_changed(), None); /// /// snd.send(10); From 311ab07a9af0029060813779038220481d1bf1c5 Mon Sep 17 00:00:00 2001 From: Peter Krull Date: Sat, 2 Mar 2024 00:14:11 +0100 Subject: [PATCH 011/361] Reintroduce predicate methods. Add ability for sender to modify value in-place. --- embassy-sync/src/watch.rs | 267 +++++++++++++++++++++++++++++++++++++- 1 file changed, 260 insertions(+), 7 deletions(-) diff --git a/embassy-sync/src/watch.rs b/embassy-sync/src/watch.rs index 01d82def4..520696f7d 100644 --- a/embassy-sync/src/watch.rs +++ b/embassy-sync/src/watch.rs @@ -88,21 +88,48 @@ pub trait WatchBehavior { /// Tries to peek the value of the `Watch`, **without** marking it as seen. fn try_peek(&self) -> Option; + /// Poll the `Watch` for the value if it matches the predicate function + /// `f`, **without** making it as seen. + fn poll_peek_and(&self, f: &mut dyn Fn(&T) -> bool, cx: &mut Context<'_>) -> Poll; + + /// Tries to peek the value of the `Watch` if it matches the predicate function `f`, **without** marking it as seen. + fn try_peek_and(&self, f: &mut dyn Fn(&T) -> bool) -> Option; + /// Poll the `Watch` for the current value, making it as seen. fn poll_get(&self, id: &mut u64, cx: &mut Context<'_>) -> Poll; /// Tries to get the value of the `Watch`, marking it as seen. fn try_get(&self, id: &mut u64) -> Option; + /// Poll the `Watch` for the value if it matches the predicate function + /// `f`, making it as seen. + fn poll_get_and(&self, id: &mut u64, f: &mut dyn Fn(&T) -> bool, cx: &mut Context<'_>) -> Poll; + + /// Tries to get the value of the `Watch` if it matches the predicate function + /// `f`, marking it as seen. + fn try_get_and(&self, id: &mut u64, f: &mut dyn Fn(&T) -> bool) -> Option; + /// Poll the `Watch` for a changed value, marking it as seen. fn poll_changed(&self, id: &mut u64, cx: &mut Context<'_>) -> Poll; /// Tries to retrieve the value of the `Watch` if it has changed, marking it as seen. fn try_changed(&self, id: &mut u64) -> Option; + /// Poll the `Watch` for a changed value that matches the predicate function + /// `f`, marking it as seen. + fn poll_changed_and(&self, id: &mut u64, f: &mut dyn Fn(&T) -> bool, cx: &mut Context<'_>) -> Poll; + + /// Tries to retrieve the value of the `Watch` if it has changed and matches the + /// predicate function `f`, marking it as seen. + fn try_changed_and(&self, id: &mut u64, f: &mut dyn Fn(&T) -> bool) -> Option; + /// Checks if the `Watch` is been initialized with a value. fn contains_value(&self) -> bool; + /// Modify the value of the `Watch` using a closure. Returns `false` if the + /// `Watch` does not already contain a value. + fn modify(&self, f: &mut dyn Fn(&mut Option)); + /// Used when a receiver is dropped to decrement the receiver count. /// /// ## This method should not be called by the user. @@ -143,6 +170,29 @@ impl WatchBehavior for Watch self.mutex.lock(|state| state.borrow().data.clone()) } + fn poll_peek_and(&self, f: &mut dyn Fn(&T) -> bool, cx: &mut Context<'_>) -> Poll { + self.mutex.lock(|state| { + let mut s = state.borrow_mut(); + match s.data { + Some(ref data) if f(data) => Poll::Ready(data.clone()), + _ => { + s.wakers.register(cx.waker()); + Poll::Pending + } + } + }) + } + + fn try_peek_and(&self, f: &mut dyn Fn(&T) -> bool) -> Option { + self.mutex.lock(|state| { + let s = state.borrow(); + match s.data { + Some(ref data) if f(data) => Some(data.clone()), + _ => None, + } + }) + } + fn poll_get(&self, id: &mut u64, cx: &mut Context<'_>) -> Poll { self.mutex.lock(|state| { let mut s = state.borrow_mut(); @@ -167,6 +217,35 @@ impl WatchBehavior for Watch }) } + fn poll_get_and(&self, id: &mut u64, f: &mut dyn Fn(&T) -> bool, cx: &mut Context<'_>) -> Poll { + self.mutex.lock(|state| { + let mut s = state.borrow_mut(); + match s.data { + Some(ref data) if f(data) => { + *id = s.current_id; + Poll::Ready(data.clone()) + } + _ => { + s.wakers.register(cx.waker()); + Poll::Pending + } + } + }) + } + + fn try_get_and(&self, id: &mut u64, f: &mut dyn Fn(&T) -> bool) -> Option { + self.mutex.lock(|state| { + let s = state.borrow(); + match s.data { + Some(ref data) if f(data) => { + *id = s.current_id; + Some(data.clone()) + } + _ => None, + } + }) + } + fn poll_changed(&self, id: &mut u64, cx: &mut Context<'_>) -> Poll { self.mutex.lock(|state| { let mut s = state.borrow_mut(); @@ -189,13 +268,42 @@ impl WatchBehavior for Watch match s.current_id > *id { true => { *id = s.current_id; - state.borrow().data.clone() + s.data.clone() } false => None, } }) } + fn poll_changed_and(&self, id: &mut u64, f: &mut dyn Fn(&T) -> bool, cx: &mut Context<'_>) -> Poll { + self.mutex.lock(|state| { + let mut s = state.borrow_mut(); + match (&s.data, s.current_id > *id) { + (Some(data), true) if f(data) => { + *id = s.current_id; + Poll::Ready(data.clone()) + } + _ => { + s.wakers.register(cx.waker()); + Poll::Pending + } + } + }) + } + + fn try_changed_and(&self, id: &mut u64, f: &mut dyn Fn(&T) -> bool) -> Option { + self.mutex.lock(|state| { + let s = state.borrow(); + match (&s.data, s.current_id > *id) { + (Some(data), true) if f(data) => { + *id = s.current_id; + s.data.clone() + } + _ => None, + } + }) + } + fn contains_value(&self) -> bool { self.mutex.lock(|state| state.borrow().data.is_some()) } @@ -206,6 +314,15 @@ impl WatchBehavior for Watch s.receiver_count -= 1; }) } + + fn modify(&self, f: &mut dyn Fn(&mut Option)) { + self.mutex.lock(|state| { + let mut s = state.borrow_mut(); + f(&mut s.data); + s.current_id += 1; + s.wakers.wake(); + }) + } } impl Watch { @@ -300,10 +417,27 @@ impl<'a, T: Clone, W: WatchBehavior + ?Sized> Snd<'a, T, W> { self.watch.try_peek() } + /// Tries to peek the current value of the `Watch` if it matches the predicate + /// function `f`. + pub fn try_peek_and(&self, mut f: F) -> Option + where + F: Fn(&T) -> bool, + { + self.watch.try_peek_and(&mut f) + } + /// Returns true if the `Watch` contains a value. pub fn contains_value(&self) -> bool { self.watch.contains_value() } + + /// Modify the value of the `Watch` using a closure. + pub fn modify(&self, mut f: F) + where + F: Fn(&mut Option), + { + self.watch.modify(&mut f) + } } /// A sender of a `Watch` channel. @@ -399,6 +533,26 @@ impl<'a, T: Clone, W: WatchBehavior + ?Sized> Rcv<'a, T, W> { self.watch.try_peek() } + /// Returns the current value of the `Watch` if it matches the predicate function `f`, + /// or waits for it to match, **without** marking it as seen. + /// + /// **Note**: Futures do nothing unless you `.await` or poll them. + pub async fn peek_and(&self, mut f: F) -> T + where + F: Fn(&T) -> bool, + { + poll_fn(|cx| self.watch.poll_peek_and(&mut f, cx)).await + } + + /// Tries to peek the current value of the `Watch` if it matches the predicate + /// function `f` without waiting, and **without** marking it as seen. + pub fn try_peek_and(&self, mut f: F) -> Option + where + F: Fn(&T) -> bool, + { + self.watch.try_peek_and(&mut f) + } + /// Returns the current value of the `Watch` once it is initialized, marking it as seen. /// /// **Note**: Futures do nothing unless you `.await` or poll them. @@ -411,6 +565,26 @@ impl<'a, T: Clone, W: WatchBehavior + ?Sized> Rcv<'a, T, W> { self.watch.try_get(&mut self.at_id) } + /// Returns the value of the `Watch` if it matches the predicate function `f`, + /// or waits for it to match, marking it as seen. + /// + /// **Note**: Futures do nothing unless you `.await` or poll them. + pub async fn get_and(&mut self, mut f: F) -> T + where + F: Fn(&T) -> bool, + { + poll_fn(|cx| self.watch.poll_get_and(&mut self.at_id, &mut f, cx)).await + } + + /// Tries to get the current value of the `Watch` if it matches the predicate + /// function `f` without waiting, marking it as seen. + pub fn try_get_and(&mut self, mut f: F) -> Option + where + F: Fn(&T) -> bool, + { + self.watch.try_get_and(&mut self.at_id, &mut f) + } + /// Waits for the `Watch` to change and returns the new value, marking it as seen. /// /// **Note**: Futures do nothing unless you `.await` or poll them. @@ -423,6 +597,26 @@ impl<'a, T: Clone, W: WatchBehavior + ?Sized> Rcv<'a, T, W> { self.watch.try_changed(&mut self.at_id) } + /// Waits for the `Watch` to change to a value which satisfies the predicate + /// function `f` and returns the new value, marking it as seen. + /// + /// **Note**: Futures do nothing unless you `.await` or poll them. + pub async fn changed_and(&mut self, mut f: F) -> T + where + F: Fn(&T) -> bool, + { + poll_fn(|cx| self.watch.poll_changed_and(&mut self.at_id, &mut f, cx)).await + } + + /// Tries to get the new value of the watch which satisfies the predicate + /// function `f` and returns the new value without waiting, marking it as seen. + pub fn try_changed_and(&mut self, mut f: F) -> Option + where + F: Fn(&T) -> bool, + { + self.watch.try_changed_and(&mut self.at_id, &mut f) + } + /// Checks if the `Watch` contains a value. If this returns true, /// then awaiting [`Rcv::get`] and [`Rcv::peek`] will return immediately. pub fn contains_value(&self) -> bool { @@ -442,12 +636,9 @@ pub struct Receiver<'a, M: RawMutex, T: Clone, const N: usize>(Rcv<'a, T, Watch< impl<'a, M: RawMutex, T: Clone, const N: usize> Receiver<'a, M, T, N> { /// Converts the `Receiver` into a [`DynReceiver`]. pub fn as_dyn(self) -> DynReceiver<'a, T> { - // We need to increment the receiver count since the original - // receiver is being dropped, which decrements the count. - self.watch.mutex.lock(|state| { - state.borrow_mut().receiver_count += 1; - }); - DynReceiver(Rcv::new(self.0.watch, self.at_id)) + let rcv = DynReceiver(Rcv::new(self.0.watch, self.at_id)); + core::mem::forget(self); // Ensures the destructor is not called + rcv } } @@ -524,6 +715,68 @@ mod tests { block_on(f); } + #[test] + fn sender_modify() { + let f = async { + static WATCH: Watch = Watch::new(); + + // Obtain receiver and sender + let mut rcv = WATCH.receiver().unwrap(); + let snd = WATCH.sender(); + + // Receive the new value + snd.send(10); + assert_eq!(rcv.try_changed(), Some(10)); + + // Modify the value inplace + snd.modify(|opt|{ + if let Some(inner) = opt { + *inner += 5; + } + }); + + // Get the modified value + assert_eq!(rcv.try_changed(), Some(15)); + assert_eq!(rcv.try_changed(), None); + }; + block_on(f); + } + + #[test] + fn predicate_fn() { + let f = async { + static WATCH: Watch = Watch::new(); + + // Obtain receiver and sender + let mut rcv = WATCH.receiver().unwrap(); + let snd = WATCH.sender(); + + snd.send(10); + assert_eq!(rcv.try_peek_and(|x| x > &5), Some(10)); + assert_eq!(rcv.try_peek_and(|x| x < &5), None); + assert!(rcv.try_changed().is_some()); + + snd.send(15); + assert_eq!(rcv.try_get_and(|x| x > &5), Some(15)); + assert_eq!(rcv.try_get_and(|x| x < &5), None); + assert!(rcv.try_changed().is_none()); + + snd.send(20); + assert_eq!(rcv.try_changed_and(|x| x > &5), Some(20)); + assert_eq!(rcv.try_changed_and(|x| x > &5), None); + + snd.send(25); + assert_eq!(rcv.try_changed_and(|x| x < &5), None); + assert_eq!(rcv.try_changed(), Some(25)); + + snd.send(30); + assert_eq!(rcv.changed_and(|x| x > &5).await, 30); + assert_eq!(rcv.peek_and(|x| x > &5).await, 30); + assert_eq!(rcv.get_and(|x| x > &5).await, 30); + }; + block_on(f); + } + #[test] fn receive_after_create() { let f = async { From e02a987bafd4f0fcf9d80e7c4f6e1504b8b02cec Mon Sep 17 00:00:00 2001 From: Peter Krull Date: Sat, 2 Mar 2024 00:16:17 +0100 Subject: [PATCH 012/361] This one is for cargo fmt --- embassy-sync/src/watch.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/embassy-sync/src/watch.rs b/embassy-sync/src/watch.rs index 520696f7d..298c09d43 100644 --- a/embassy-sync/src/watch.rs +++ b/embassy-sync/src/watch.rs @@ -729,7 +729,7 @@ mod tests { assert_eq!(rcv.try_changed(), Some(10)); // Modify the value inplace - snd.modify(|opt|{ + snd.modify(|opt| { if let Some(inner) = opt { *inner += 5; } From f6d92b76111d8619c92b13aa28e765abe356465c Mon Sep 17 00:00:00 2001 From: Badr Bouslikhin Date: Fri, 30 Aug 2024 18:52:23 +0200 Subject: [PATCH 013/361] fix(stm32): disable transmitter during half-duplex read --- embassy-stm32/src/usart/mod.rs | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/embassy-stm32/src/usart/mod.rs b/embassy-stm32/src/usart/mod.rs index 89d92dda2..af34b548e 100644 --- a/embassy-stm32/src/usart/mod.rs +++ b/embassy-stm32/src/usart/mod.rs @@ -526,9 +526,12 @@ fn blocking_flush(info: &Info) -> Result<(), Error> { let r = info.regs; while !sr(r).read().tc() {} - // Enable Receiver after transmission complete for Half-Duplex mode + // Disable Transmitter and enable receiver after transmission complete for Half-Duplex mode if r.cr3().read().hdsel() { - r.cr1().modify(|reg| reg.set_re(true)); + r.cr1().modify(|reg| { + reg.set_te(false); + reg.set_re(true); + }); } Ok(()) From 8ac758bdee793496f842f4c267bda2b6f935a0bb Mon Sep 17 00:00:00 2001 From: Grant Miller Date: Fri, 6 Sep 2024 09:07:29 -0500 Subject: [PATCH 014/361] embassy-stm32: Add SimplePwmChannel --- embassy-stm32/src/timer/simple_pwm.rs | 112 ++++++++++++++++++++++++++ 1 file changed, 112 insertions(+) diff --git a/embassy-stm32/src/timer/simple_pwm.rs b/embassy-stm32/src/timer/simple_pwm.rs index b7771bd64..f94ed1be5 100644 --- a/embassy-stm32/src/timer/simple_pwm.rs +++ b/embassy-stm32/src/timer/simple_pwm.rs @@ -51,6 +51,96 @@ channel_impl!(new_ch2, Ch2, Channel2Pin); channel_impl!(new_ch3, Ch3, Channel3Pin); channel_impl!(new_ch4, Ch4, Channel4Pin); +/// A single channel of a pwm, obtained from [`SimplePwm::split`]. +/// +/// It is not possible to change the pwm frequency because +/// the frequency configuration is shared with all four channels. +pub struct SimplePwmChannel<'d, T: GeneralInstance4Channel> { + timer: &'d Timer<'d, T>, + channel: Channel, +} + +// TODO: check for RMW races +impl<'d, T: GeneralInstance4Channel> SimplePwmChannel<'d, T> { + /// Enable the given channel. + pub fn enable(&mut self) { + self.timer.enable_channel(self.channel, true); + } + + /// Disable the given channel. + pub fn disable(&mut self) { + self.timer.enable_channel(self.channel, false); + } + + /// Check whether given channel is enabled + pub fn is_enabled(&self) -> bool { + self.timer.get_channel_enable_state(self.channel) + } + + /// Get max duty value. + /// + /// This value depends on the configured frequency and the timer's clock rate from RCC. + pub fn get_max_duty(&self) -> u32 { + self.timer.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, duty: u32) { + assert!(duty <= self.get_max_duty()); + self.timer.set_compare_value(self.channel, duty) + } + + /// 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) -> u32 { + self.timer.get_compare_value(self.channel) + } + + /// Set the output polarity for a given channel. + pub fn set_polarity(&mut self, polarity: OutputPolarity) { + self.timer.set_output_polarity(self.channel, polarity); + } + + /// Set the output compare mode for a given channel. + pub fn set_output_compare_mode(&mut self, mode: OutputCompareMode) { + self.timer.set_output_compare_mode(self.channel, mode); + } +} + +/// A group of four [`SimplePwmChannel`]s, obtained from [`SimplePwm::split`]. +pub struct SimplePwmChannels<'d, T: GeneralInstance4Channel> { + /// Channel 1 + pub ch1: SimplePwmChannel<'d, T>, + /// Channel 2 + pub ch2: SimplePwmChannel<'d, T>, + /// Channel 3 + pub ch3: SimplePwmChannel<'d, T>, + /// Channel 4 + pub ch4: SimplePwmChannel<'d, T>, +} + +impl<'d, T: GeneralInstance4Channel> embedded_hal_1::pwm::ErrorType for SimplePwmChannel<'d, T> { + type Error = core::convert::Infallible; +} + +impl<'d, T: GeneralInstance4Channel> embedded_hal_1::pwm::SetDutyCycle for SimplePwmChannel<'d, T> { + fn max_duty_cycle(&self) -> u16 { + // TODO: panics if CCR is 0xFFFF + // TODO: rename get_max_duty to max_duty_cycle + unwrap!(self.get_max_duty().try_into()) + } + + fn set_duty_cycle(&mut self, duty: u16) -> Result<(), Self::Error> { + self.set_duty(duty.into()); + Ok(()) + } + + // TODO: default methods? +} + /// Simple PWM driver. pub struct SimplePwm<'d, T: GeneralInstance4Channel> { inner: Timer<'d, T>, @@ -89,6 +179,28 @@ impl<'d, T: GeneralInstance4Channel> SimplePwm<'d, T> { this } + fn channel(&self, channel: Channel) -> SimplePwmChannel<'_, T> { + SimplePwmChannel { + timer: &self.inner, + channel, + } + } + + /// Splits a [`SimplePwm`] into four pwm channels. + /// + /// This returns all four channels, including channels that + /// aren't configured with a [`PwmPin`]. + // TODO: I hate the name "split" + pub fn split(&mut self) -> SimplePwmChannels<'_, T> { + // TODO: pre-enable channels? + SimplePwmChannels { + ch1: self.channel(Channel::Ch1), + ch2: self.channel(Channel::Ch2), + ch3: self.channel(Channel::Ch3), + ch4: self.channel(Channel::Ch4), + } + } + /// Enable the given channel. pub fn enable(&mut self, channel: Channel) { self.inner.enable_channel(channel, true); From f7f062e0a3f303dfc4f976907b2555f2cfb48621 Mon Sep 17 00:00:00 2001 From: Grant Miller Date: Fri, 6 Sep 2024 10:25:29 -0500 Subject: [PATCH 015/361] Deduplicate SimplePwm's channel methods --- embassy-stm32/src/timer/simple_pwm.rs | 119 ++++++++++++++------------ 1 file changed, 63 insertions(+), 56 deletions(-) diff --git a/embassy-stm32/src/timer/simple_pwm.rs b/embassy-stm32/src/timer/simple_pwm.rs index f94ed1be5..7179a7a59 100644 --- a/embassy-stm32/src/timer/simple_pwm.rs +++ b/embassy-stm32/src/timer/simple_pwm.rs @@ -51,7 +51,8 @@ channel_impl!(new_ch2, Ch2, Channel2Pin); channel_impl!(new_ch3, Ch3, Channel3Pin); channel_impl!(new_ch4, Ch4, Channel4Pin); -/// A single channel of a pwm, obtained from [`SimplePwm::split`]. +/// A single channel of a pwm, obtained from [`SimplePwm::split`], +/// [`SimplePwm::channel`], [`SimplePwm::ch1`], etc. /// /// It is not possible to change the pwm frequency because /// the frequency configuration is shared with all four channels. @@ -179,13 +180,52 @@ impl<'d, T: GeneralInstance4Channel> SimplePwm<'d, T> { this } - fn channel(&self, channel: Channel) -> SimplePwmChannel<'_, T> { + /// Get a single channel + /// + /// If you need to use multiple channels, use [`Self::split`]. + pub fn channel(&mut self, channel: Channel) -> SimplePwmChannel<'_, T> { SimplePwmChannel { timer: &self.inner, channel, } } + /// Channel 1 + /// + /// This is just a convenience wrapper around [`Self::channel`]. + /// + /// If you need to use multiple channels, use [`Self::split`]. + pub fn ch1(&mut self) -> SimplePwmChannel<'_, T> { + self.channel(Channel::Ch1) + } + + /// Channel 2 + /// + /// This is just a convenience wrapper around [`Self::channel`]. + /// + /// If you need to use multiple channels, use [`Self::split`]. + pub fn ch2(&mut self) -> SimplePwmChannel<'_, T> { + self.channel(Channel::Ch2) + } + + /// Channel 3 + /// + /// This is just a convenience wrapper around [`Self::channel`]. + /// + /// If you need to use multiple channels, use [`Self::split`]. + pub fn ch3(&mut self) -> SimplePwmChannel<'_, T> { + self.channel(Channel::Ch3) + } + + /// Channel 4 + /// + /// This is just a convenience wrapper around [`Self::channel`]. + /// + /// If you need to use multiple channels, use [`Self::split`]. + pub fn ch4(&mut self) -> SimplePwmChannel<'_, T> { + self.channel(Channel::Ch4) + } + /// Splits a [`SimplePwm`] into four pwm channels. /// /// This returns all four channels, including channels that @@ -193,29 +233,21 @@ impl<'d, T: GeneralInstance4Channel> SimplePwm<'d, T> { // TODO: I hate the name "split" pub fn split(&mut self) -> SimplePwmChannels<'_, T> { // TODO: pre-enable channels? + + // we can't use self.channel() because that takes &mut self + let ch = |channel| SimplePwmChannel { + timer: &self.inner, + channel, + }; + SimplePwmChannels { - ch1: self.channel(Channel::Ch1), - ch2: self.channel(Channel::Ch2), - ch3: self.channel(Channel::Ch3), - ch4: self.channel(Channel::Ch4), + ch1: ch(Channel::Ch1), + ch2: ch(Channel::Ch2), + ch3: ch(Channel::Ch3), + ch4: ch(Channel::Ch4), } } - /// Enable the given channel. - pub fn enable(&mut self, channel: Channel) { - self.inner.enable_channel(channel, true); - } - - /// Disable the given channel. - pub fn disable(&mut self, channel: Channel) { - self.inner.enable_channel(channel, false); - } - - /// Check whether given channel is enabled - pub fn is_enabled(&self, channel: Channel) -> bool { - self.inner.get_channel_enable_state(channel) - } - /// Set PWM frequency. /// /// Note: when you call this, the max duty value changes, so you will have to @@ -236,31 +268,6 @@ impl<'d, T: GeneralInstance4Channel> SimplePwm<'d, T> { 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: u32) { - assert!(duty <= self.get_max_duty()); - self.inner.set_compare_value(channel, duty) - } - - /// 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) -> u32 { - self.inner.get_compare_value(channel) - } - - /// Set the output polarity for a given channel. - pub fn set_polarity(&mut self, channel: Channel, polarity: OutputPolarity) { - self.inner.set_output_polarity(channel, polarity); - } - - /// Set the output compare mode for a given channel. - pub fn set_output_compare_mode(&mut self, channel: Channel, mode: OutputCompareMode) { - self.inner.set_output_compare_mode(channel, mode); - } - /// Generate a sequence of PWM waveform /// /// Note: @@ -276,8 +283,8 @@ impl<'d, T: GeneralInstance4Channel> SimplePwm<'d, T> { #[allow(clippy::let_unit_value)] // eg. stm32f334 let req = dma.request(); - let original_duty_state = self.get_duty(channel); - let original_enable_state = self.is_enabled(channel); + let original_duty_state = self.channel(channel).get_duty(); + let original_enable_state = self.channel(channel).is_enabled(); let original_update_dma_state = self.inner.get_update_dma_state(); if !original_update_dma_state { @@ -285,7 +292,7 @@ impl<'d, T: GeneralInstance4Channel> SimplePwm<'d, T> { } if !original_enable_state { - self.enable(channel); + self.channel(channel).enable(); } unsafe { @@ -313,10 +320,10 @@ impl<'d, T: GeneralInstance4Channel> SimplePwm<'d, T> { // restore output compare state if !original_enable_state { - self.disable(channel); + self.channel(channel).disable(); } - self.set_duty(channel, original_duty_state); + self.channel(channel).set_duty(original_duty_state); // Since DMA is closed before timer update event trigger DMA is turn off, // this can almost always trigger a DMA FIFO error. @@ -346,8 +353,8 @@ macro_rules! impl_waveform_chx { let cc_channel = Channel::$cc_ch; - let original_duty_state = self.get_duty(cc_channel); - let original_enable_state = self.is_enabled(cc_channel); + let original_duty_state = self.channel(cc_channel).get_duty(); + let original_enable_state = self.channel(cc_channel).is_enabled(); let original_cc_dma_on_update = self.inner.get_cc_dma_selection() == Ccds::ONUPDATE; let original_cc_dma_enabled = self.inner.get_cc_dma_enable_state(cc_channel); @@ -361,7 +368,7 @@ macro_rules! impl_waveform_chx { } if !original_enable_state { - self.enable(cc_channel); + self.channel(cc_channel).enable(); } unsafe { @@ -389,10 +396,10 @@ macro_rules! impl_waveform_chx { // restore output compare state if !original_enable_state { - self.disable(cc_channel); + self.channel(cc_channel).disable(); } - self.set_duty(cc_channel, original_duty_state); + self.channel(cc_channel).set_duty(original_duty_state); // Since DMA is closed before timer Capture Compare Event trigger DMA is turn off, // this can almost always trigger a DMA FIFO error. From cfc76cec711447300e2d957439a32d6ad418a58c Mon Sep 17 00:00:00 2001 From: Grant Miller Date: Fri, 6 Sep 2024 13:29:54 -0500 Subject: [PATCH 016/361] Match embedded-hal api --- embassy-stm32/src/timer/simple_pwm.rs | 105 ++++++++++++++++++-------- 1 file changed, 72 insertions(+), 33 deletions(-) diff --git a/embassy-stm32/src/timer/simple_pwm.rs b/embassy-stm32/src/timer/simple_pwm.rs index 7179a7a59..23c727731 100644 --- a/embassy-stm32/src/timer/simple_pwm.rs +++ b/embassy-stm32/src/timer/simple_pwm.rs @@ -81,23 +81,45 @@ impl<'d, T: GeneralInstance4Channel> SimplePwmChannel<'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) -> u32 { - self.timer.get_max_compare_value() + 1 + pub fn max_duty_cycle(&self) -> u16 { + unwrap!(self.timer.get_max_compare_value().checked_add(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, duty: u32) { - assert!(duty <= self.get_max_duty()); - self.timer.set_compare_value(self.channel, duty) + /// The value ranges from 0 for 0% duty, to [`max_duty_cycle`](Self::max_duty_cycle) for 100% duty, both included. + pub fn set_duty_cycle(&mut self, duty: u16) { + assert!(duty <= (*self).max_duty_cycle()); + self.timer.set_compare_value(self.channel, duty.into()) + } + + fn set_duty_cycle_fully_off(&mut self) { + self.set_duty_cycle(0); + } + + fn set_duty_cycle_fully_on(&mut self) { + self.set_duty_cycle((*self).max_duty_cycle()); + } + + fn set_duty_cycle_fraction(&mut self, num: u16, denom: u16) { + assert!(denom != 0); + assert!(num <= denom); + let duty = u32::from(num) * u32::from(self.max_duty_cycle()) / u32::from(denom); + + // This is safe because we know that `num <= denom`, so `duty <= self.max_duty_cycle()` (u16) + #[allow(clippy::cast_possible_truncation)] + self.set_duty_cycle(duty as u16); + } + + fn set_duty_cycle_percent(&mut self, percent: u8) { + self.set_duty_cycle_fraction(u16::from(percent), 100) } /// 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) -> u32 { - self.timer.get_compare_value(self.channel) + /// The value ranges from 0 for 0% duty, to [`max_duty_cycle`](Self::max_duty_cycle) for 100% duty, both included. + pub fn get_duty(&self) -> u16 { + unwrap!(self.timer.get_compare_value(self.channel).try_into()) } /// Set the output polarity for a given channel. @@ -123,25 +145,6 @@ pub struct SimplePwmChannels<'d, T: GeneralInstance4Channel> { pub ch4: SimplePwmChannel<'d, T>, } -impl<'d, T: GeneralInstance4Channel> embedded_hal_1::pwm::ErrorType for SimplePwmChannel<'d, T> { - type Error = core::convert::Infallible; -} - -impl<'d, T: GeneralInstance4Channel> embedded_hal_1::pwm::SetDutyCycle for SimplePwmChannel<'d, T> { - fn max_duty_cycle(&self) -> u16 { - // TODO: panics if CCR is 0xFFFF - // TODO: rename get_max_duty to max_duty_cycle - unwrap!(self.get_max_duty().try_into()) - } - - fn set_duty_cycle(&mut self, duty: u16) -> Result<(), Self::Error> { - self.set_duty(duty.into()); - Ok(()) - } - - // TODO: default methods? -} - /// Simple PWM driver. pub struct SimplePwm<'d, T: GeneralInstance4Channel> { inner: Timer<'d, T>, @@ -253,6 +256,7 @@ impl<'d, T: GeneralInstance4Channel> SimplePwm<'d, T> { /// Note: when you call this, the max duty value changes, so you will have to /// call `set_duty` on all channels with the duty calculated based on the new max duty. pub fn set_frequency(&mut self, freq: Hertz) { + // TODO: prevent ARR = u16::MAX? let multiplier = if self.inner.get_counting_mode().is_center_aligned() { 2u8 } else { @@ -264,8 +268,8 @@ impl<'d, T: GeneralInstance4Channel> 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) -> u32 { - self.inner.get_max_compare_value() + 1 + pub fn max_duty_cycle(&self) -> u16 { + unwrap!(self.inner.get_max_compare_value().checked_add(1)) } /// Generate a sequence of PWM waveform @@ -323,7 +327,7 @@ impl<'d, T: GeneralInstance4Channel> SimplePwm<'d, T> { self.channel(channel).disable(); } - self.channel(channel).set_duty(original_duty_state); + self.channel(channel).set_duty_cycle(original_duty_state); // Since DMA is closed before timer update event trigger DMA is turn off, // this can almost always trigger a DMA FIFO error. @@ -399,7 +403,7 @@ macro_rules! impl_waveform_chx { self.channel(cc_channel).disable(); } - self.channel(cc_channel).set_duty(original_duty_state); + self.channel(cc_channel).set_duty_cycle(original_duty_state); // Since DMA is closed before timer Capture Compare Event trigger DMA is turn off, // this can almost always trigger a DMA FIFO error. @@ -423,6 +427,41 @@ impl_waveform_chx!(waveform_ch2, Ch2Dma, Ch2); impl_waveform_chx!(waveform_ch3, Ch3Dma, Ch3); impl_waveform_chx!(waveform_ch4, Ch4Dma, Ch4); +impl<'d, T: GeneralInstance4Channel> embedded_hal_1::pwm::ErrorType for SimplePwmChannel<'d, T> { + type Error = core::convert::Infallible; +} + +impl<'d, T: GeneralInstance4Channel> embedded_hal_1::pwm::SetDutyCycle for SimplePwmChannel<'d, T> { + fn max_duty_cycle(&self) -> u16 { + self.max_duty_cycle() + } + + fn set_duty_cycle(&mut self, duty: u16) -> Result<(), Self::Error> { + self.set_duty_cycle(duty); + Ok(()) + } + + fn set_duty_cycle_fully_off(&mut self) -> Result<(), Self::Error> { + self.set_duty_cycle_fully_off(); + Ok(()) + } + + fn set_duty_cycle_fully_on(&mut self) -> Result<(), Self::Error> { + self.set_duty_cycle_fully_on(); + Ok(()) + } + + fn set_duty_cycle_fraction(&mut self, num: u16, denom: u16) -> Result<(), Self::Error> { + self.set_duty_cycle_fraction(num, denom); + Ok(()) + } + + fn set_duty_cycle_percent(&mut self, percent: u8) -> Result<(), Self::Error> { + self.set_duty_cycle_percent(percent); + Ok(()) + } +} + impl<'d, T: GeneralInstance4Channel> embedded_hal_02::Pwm for SimplePwm<'d, T> { type Channel = Channel; type Time = Hertz; @@ -449,7 +488,7 @@ impl<'d, T: GeneralInstance4Channel> embedded_hal_02::Pwm for SimplePwm<'d, T> { } fn set_duty(&mut self, channel: Self::Channel, duty: Self::Duty) { - assert!(duty <= self.get_max_duty()); + assert!(duty <= self.max_duty_cycle() as u32); self.inner.set_compare_value(channel, duty) } From 1a8977db7837a5635d3c5e0ae8213d1b3181ffb7 Mon Sep 17 00:00:00 2001 From: Grant Miller Date: Fri, 6 Sep 2024 13:53:49 -0500 Subject: [PATCH 017/361] Update examples --- examples/stm32f4/src/bin/pwm.rs | 18 +++++++++--------- examples/stm32f4/src/bin/ws2812_pwm.rs | 4 ++-- examples/stm32g0/src/bin/input_capture.rs | 8 ++++---- examples/stm32g0/src/bin/pwm_input.rs | 9 ++++----- examples/stm32g4/src/bin/pwm.rs | 18 +++++++++--------- examples/stm32h7/src/bin/pwm.rs | 18 +++++++++--------- 6 files changed, 37 insertions(+), 38 deletions(-) diff --git a/examples/stm32f4/src/bin/pwm.rs b/examples/stm32f4/src/bin/pwm.rs index 8844a9f0e..0cd65a258 100644 --- a/examples/stm32f4/src/bin/pwm.rs +++ b/examples/stm32f4/src/bin/pwm.rs @@ -15,22 +15,22 @@ async fn main(_spawner: Spawner) { let p = embassy_stm32::init(Default::default()); info!("Hello World!"); - let ch1 = PwmPin::new_ch1(p.PE9, OutputType::PushPull); - let mut pwm = SimplePwm::new(p.TIM1, Some(ch1), None, None, None, khz(10), Default::default()); - let max = pwm.get_max_duty(); - pwm.enable(Channel::Ch1); + let ch1_pin = PwmPin::new_ch1(p.PE9, OutputType::PushPull); + let mut pwm = SimplePwm::new(p.TIM1, Some(ch1_pin), None, None, None, khz(10), Default::default()); + let mut ch1 = pwm.ch1(); + ch1.enable(); info!("PWM initialized"); - info!("PWM max duty {}", max); + info!("PWM max duty {}", ch1.max_duty_cycle()); loop { - pwm.set_duty(Channel::Ch1, 0); + ch1.set_duty_cycle_fully_off(); Timer::after_millis(300).await; - pwm.set_duty(Channel::Ch1, max / 4); + ch1.set_duty_cycle_fraction(1, 4); Timer::after_millis(300).await; - pwm.set_duty(Channel::Ch1, max / 2); + ch1.set_dutycycle_fraction(1, 2); Timer::after_millis(300).await; - pwm.set_duty(Channel::Ch1, max - 1); + ch1.set_duty_cycle(ch1.max_duty_cycle() - 1); Timer::after_millis(300).await; } } diff --git a/examples/stm32f4/src/bin/ws2812_pwm.rs b/examples/stm32f4/src/bin/ws2812_pwm.rs index cbaff75fc..7a9fa302b 100644 --- a/examples/stm32f4/src/bin/ws2812_pwm.rs +++ b/examples/stm32f4/src/bin/ws2812_pwm.rs @@ -61,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() as u16; + let max_duty = ws2812_pwm.max_duty_cycle(); let n0 = 8 * max_duty / 25; // ws2812 Bit 0 high level timing let n1 = 2 * n0; // ws2812 Bit 1 high level timing @@ -84,7 +84,7 @@ async fn main(_spawner: Spawner) { let pwm_channel = Channel::Ch1; // make sure PWM output keep low on first start - ws2812_pwm.set_duty(pwm_channel, 0); + ws2812_pwm.channel(pwm_channel).set_duty(0); // flip color at 2 Hz let mut ticker = Ticker::every(Duration::from_millis(500)); diff --git a/examples/stm32g0/src/bin/input_capture.rs b/examples/stm32g0/src/bin/input_capture.rs index 69fdae96d..bc814cb13 100644 --- a/examples/stm32g0/src/bin/input_capture.rs +++ b/examples/stm32g0/src/bin/input_capture.rs @@ -47,10 +47,10 @@ async fn main(spawner: Spawner) { unwrap!(spawner.spawn(blinky(p.PB1))); // Connect PB1 and PA8 with a 1k Ohm resistor - let ch1 = PwmPin::new_ch1(p.PA8, OutputType::PushPull); - let mut pwm = SimplePwm::new(p.TIM1, Some(ch1), None, None, None, khz(1), Default::default()); - pwm.enable(Channel::Ch1); - pwm.set_duty(Channel::Ch1, 50); + let ch1_pin = PwmPin::new_ch1(p.PA8, OutputType::PushPull); + let mut pwm = SimplePwm::new(p.TIM1, Some(ch1_pin), None, None, None, khz(1), Default::default()); + pwm.ch1().enable(); + pwm.ch1().set_duty_cycle(50); let ch1 = CapturePin::new_ch1(p.PA0, Pull::None); let mut ic = InputCapture::new(p.TIM2, Some(ch1), None, None, None, Irqs, khz(1000), Default::default()); diff --git a/examples/stm32g0/src/bin/pwm_input.rs b/examples/stm32g0/src/bin/pwm_input.rs index 152ecda86..983705e2f 100644 --- a/examples/stm32g0/src/bin/pwm_input.rs +++ b/examples/stm32g0/src/bin/pwm_input.rs @@ -43,11 +43,10 @@ async fn main(spawner: Spawner) { unwrap!(spawner.spawn(blinky(p.PB1))); // Connect PA8 and PA6 with a 1k Ohm resistor - let ch1 = PwmPin::new_ch1(p.PA8, OutputType::PushPull); - let mut pwm = SimplePwm::new(p.TIM1, Some(ch1), None, None, None, khz(1), Default::default()); - let max = pwm.get_max_duty(); - pwm.set_duty(Channel::Ch1, max / 4); - pwm.enable(Channel::Ch1); + let ch1_pin = PwmPin::new_ch1(p.PA8, OutputType::PushPull); + let mut pwm = SimplePwm::new(p.TIM1, Some(ch1_pin), None, None, None, khz(1), Default::default()); + pwm.ch1().set_duty_cycle_fraction(1, 4); + pwm.ch1().enable(); let mut pwm_input = PwmInput::new(p.TIM2, p.PA0, Pull::None, khz(1000)); pwm_input.enable(); diff --git a/examples/stm32g4/src/bin/pwm.rs b/examples/stm32g4/src/bin/pwm.rs index d4809a481..3833be58f 100644 --- a/examples/stm32g4/src/bin/pwm.rs +++ b/examples/stm32g4/src/bin/pwm.rs @@ -15,22 +15,22 @@ async fn main(_spawner: Spawner) { let p = embassy_stm32::init(Default::default()); info!("Hello World!"); - let ch1 = PwmPin::new_ch1(p.PC0, OutputType::PushPull); - let mut pwm = SimplePwm::new(p.TIM1, Some(ch1), None, None, None, khz(10), Default::default()); - let max = pwm.get_max_duty(); - pwm.enable(Channel::Ch1); + let ch1_pin = PwmPin::new_ch1(p.PC0, OutputType::PushPull); + let mut pwm = SimplePwm::new(p.TIM1, Some(ch1_pin), None, None, None, khz(10), Default::default()); + let mut ch1 = pwm.ch1(); + ch1.enable(); info!("PWM initialized"); - info!("PWM max duty {}", max); + info!("PWM max duty {}", ch1.max_duty_cycle()); loop { - pwm.set_duty(Channel::Ch1, 0); + ch1.set_duty_cycle_fully_off(); Timer::after_millis(300).await; - pwm.set_duty(Channel::Ch1, max / 4); + ch1.set_duty_cycle_fraction(1, 4); Timer::after_millis(300).await; - pwm.set_duty(Channel::Ch1, max / 2); + ch1.set_dutycycle_fraction(1, 2); Timer::after_millis(300).await; - pwm.set_duty(Channel::Ch1, max - 1); + ch1.set_duty_cycle(ch1.max_duty_cycle() - 1); Timer::after_millis(300).await; } } diff --git a/examples/stm32h7/src/bin/pwm.rs b/examples/stm32h7/src/bin/pwm.rs index 1e48ba67b..1d0b89e97 100644 --- a/examples/stm32h7/src/bin/pwm.rs +++ b/examples/stm32h7/src/bin/pwm.rs @@ -37,22 +37,22 @@ async fn main(_spawner: Spawner) { let p = embassy_stm32::init(config); info!("Hello World!"); - let ch1 = PwmPin::new_ch1(p.PA6, OutputType::PushPull); - let mut pwm = SimplePwm::new(p.TIM3, Some(ch1), None, None, None, khz(10), Default::default()); - let max = pwm.get_max_duty(); - pwm.enable(Channel::Ch1); + let ch1_pin = PwmPin::new_ch1(p.PA6, OutputType::PushPull); + let mut pwm = SimplePwm::new(p.TIM3, Some(ch1_pin), None, None, None, khz(10), Default::default()); + let mut ch1 = pwm.ch1; + ch1.enable(); info!("PWM initialized"); - info!("PWM max duty {}", max); + info!("PWM max duty {}", ch1.max_duty_cycle()); loop { - pwm.set_duty(Channel::Ch1, 0); + ch1.set_duty_cycle_fully_off(); Timer::after_millis(300).await; - pwm.set_duty(Channel::Ch1, max / 4); + ch1.set_duty_cycle_fraction(1, 4); Timer::after_millis(300).await; - pwm.set_duty(Channel::Ch1, max / 2); + ch1.set_dutycycle_fraction(1, 2); Timer::after_millis(300).await; - pwm.set_duty(Channel::Ch1, max - 1); + ch1.set_duty_cycle(ch1.max_duty_cycle() - 1); Timer::after_millis(300).await; } } From 71e49839fc350c19c2fd7eebda0fe76afbfdb157 Mon Sep 17 00:00:00 2001 From: Grant Miller Date: Fri, 6 Sep 2024 14:01:10 -0500 Subject: [PATCH 018/361] oops --- embassy-stm32/src/timer/simple_pwm.rs | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/embassy-stm32/src/timer/simple_pwm.rs b/embassy-stm32/src/timer/simple_pwm.rs index 23c727731..885abd23f 100644 --- a/embassy-stm32/src/timer/simple_pwm.rs +++ b/embassy-stm32/src/timer/simple_pwm.rs @@ -82,7 +82,9 @@ impl<'d, T: GeneralInstance4Channel> SimplePwmChannel<'d, T> { /// /// This value depends on the configured frequency and the timer's clock rate from RCC. pub fn max_duty_cycle(&self) -> u16 { - unwrap!(self.timer.get_max_compare_value().checked_add(1)) + let max = self.timer.get_max_compare_value(); + assert!(max < u16::MAX as u32); + max as u16 + 1 } /// Set the duty for a given channel. @@ -269,7 +271,9 @@ impl<'d, T: GeneralInstance4Channel> SimplePwm<'d, T> { /// /// This value depends on the configured frequency and the timer's clock rate from RCC. pub fn max_duty_cycle(&self) -> u16 { - unwrap!(self.inner.get_max_compare_value().checked_add(1)) + let max = self.inner.get_max_compare_value(); + assert!(max < u16::MAX as u32); + max as u16 + 1 } /// Generate a sequence of PWM waveform From f571ab9d600cdb6e3f146a6b2bf464fb817065af Mon Sep 17 00:00:00 2001 From: Grant Miller Date: Fri, 6 Sep 2024 14:04:58 -0500 Subject: [PATCH 019/361] oops again --- embassy-stm32/src/timer/simple_pwm.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/embassy-stm32/src/timer/simple_pwm.rs b/embassy-stm32/src/timer/simple_pwm.rs index 885abd23f..8673bce54 100644 --- a/embassy-stm32/src/timer/simple_pwm.rs +++ b/embassy-stm32/src/timer/simple_pwm.rs @@ -95,15 +95,15 @@ impl<'d, T: GeneralInstance4Channel> SimplePwmChannel<'d, T> { self.timer.set_compare_value(self.channel, duty.into()) } - fn set_duty_cycle_fully_off(&mut self) { + pub fn set_duty_cycle_fully_off(&mut self) { self.set_duty_cycle(0); } - fn set_duty_cycle_fully_on(&mut self) { + pub fn set_duty_cycle_fully_on(&mut self) { self.set_duty_cycle((*self).max_duty_cycle()); } - fn set_duty_cycle_fraction(&mut self, num: u16, denom: u16) { + pub fn set_duty_cycle_fraction(&mut self, num: u16, denom: u16) { assert!(denom != 0); assert!(num <= denom); let duty = u32::from(num) * u32::from(self.max_duty_cycle()) / u32::from(denom); @@ -113,7 +113,7 @@ impl<'d, T: GeneralInstance4Channel> SimplePwmChannel<'d, T> { self.set_duty_cycle(duty as u16); } - fn set_duty_cycle_percent(&mut self, percent: u8) { + pub fn set_duty_cycle_percent(&mut self, percent: u8) { self.set_duty_cycle_fraction(u16::from(percent), 100) } From d24c47a3ff4a82786b67f06d876bafb1bdabc163 Mon Sep 17 00:00:00 2001 From: Grant Miller Date: Fri, 6 Sep 2024 14:08:22 -0500 Subject: [PATCH 020/361] Missing docs --- embassy-stm32/src/timer/simple_pwm.rs | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/embassy-stm32/src/timer/simple_pwm.rs b/embassy-stm32/src/timer/simple_pwm.rs index 8673bce54..47188053c 100644 --- a/embassy-stm32/src/timer/simple_pwm.rs +++ b/embassy-stm32/src/timer/simple_pwm.rs @@ -95,14 +95,20 @@ impl<'d, T: GeneralInstance4Channel> SimplePwmChannel<'d, T> { self.timer.set_compare_value(self.channel, duty.into()) } + /// Set the duty cycle to 0%, or always inactive. pub fn set_duty_cycle_fully_off(&mut self) { self.set_duty_cycle(0); } + /// Set the duty cycle to 100%, or always active. pub fn set_duty_cycle_fully_on(&mut self) { self.set_duty_cycle((*self).max_duty_cycle()); } + /// Set the duty cycle to `num / denom`. + /// + /// The caller is responsible for ensuring that `num` is less than or equal to `denom`, + /// and that `denom` is not zero. pub fn set_duty_cycle_fraction(&mut self, num: u16, denom: u16) { assert!(denom != 0); assert!(num <= denom); @@ -113,6 +119,9 @@ impl<'d, T: GeneralInstance4Channel> SimplePwmChannel<'d, T> { self.set_duty_cycle(duty as u16); } + /// Set the duty cycle to `percent / 100` + /// + /// The caller is responsible for ensuring that `percent` is less than or equal to 100. pub fn set_duty_cycle_percent(&mut self, percent: u8) { self.set_duty_cycle_fraction(u16::from(percent), 100) } From b8beaba6df08c4455f55780a6e13191d95ad9eec Mon Sep 17 00:00:00 2001 From: Grant Miller Date: Fri, 6 Sep 2024 15:08:58 -0500 Subject: [PATCH 021/361] last oops I promise --- embassy-stm32/src/timer/simple_pwm.rs | 6 +++--- examples/stm32f4/src/bin/pwm.rs | 3 +-- examples/stm32f4/src/bin/ws2812_pwm.rs | 2 +- examples/stm32g0/src/bin/pwm_input.rs | 1 - examples/stm32g4/src/bin/pwm.rs | 3 +-- examples/stm32h7/src/bin/pwm.rs | 5 ++--- 6 files changed, 8 insertions(+), 12 deletions(-) diff --git a/embassy-stm32/src/timer/simple_pwm.rs b/embassy-stm32/src/timer/simple_pwm.rs index 47188053c..7e2e9c202 100644 --- a/embassy-stm32/src/timer/simple_pwm.rs +++ b/embassy-stm32/src/timer/simple_pwm.rs @@ -129,7 +129,7 @@ impl<'d, T: GeneralInstance4Channel> SimplePwmChannel<'d, T> { /// Get the duty for a given channel. /// /// The value ranges from 0 for 0% duty, to [`max_duty_cycle`](Self::max_duty_cycle) for 100% duty, both included. - pub fn get_duty(&self) -> u16 { + pub fn current_duty_cycle(&self) -> u16 { unwrap!(self.timer.get_compare_value(self.channel).try_into()) } @@ -300,7 +300,7 @@ impl<'d, T: GeneralInstance4Channel> SimplePwm<'d, T> { #[allow(clippy::let_unit_value)] // eg. stm32f334 let req = dma.request(); - let original_duty_state = self.channel(channel).get_duty(); + let original_duty_state = self.channel(channel).current_duty_cycle(); let original_enable_state = self.channel(channel).is_enabled(); let original_update_dma_state = self.inner.get_update_dma_state(); @@ -370,7 +370,7 @@ macro_rules! impl_waveform_chx { let cc_channel = Channel::$cc_ch; - let original_duty_state = self.channel(cc_channel).get_duty(); + let original_duty_state = self.channel(cc_channel).current_duty_cycle(); let original_enable_state = self.channel(cc_channel).is_enabled(); let original_cc_dma_on_update = self.inner.get_cc_dma_selection() == Ccds::ONUPDATE; let original_cc_dma_enabled = self.inner.get_cc_dma_enable_state(cc_channel); diff --git a/examples/stm32f4/src/bin/pwm.rs b/examples/stm32f4/src/bin/pwm.rs index 0cd65a258..04811162b 100644 --- a/examples/stm32f4/src/bin/pwm.rs +++ b/examples/stm32f4/src/bin/pwm.rs @@ -6,7 +6,6 @@ use embassy_executor::Spawner; use embassy_stm32::gpio::OutputType; use embassy_stm32::time::khz; use embassy_stm32::timer::simple_pwm::{PwmPin, SimplePwm}; -use embassy_stm32::timer::Channel; use embassy_time::Timer; use {defmt_rtt as _, panic_probe as _}; @@ -28,7 +27,7 @@ async fn main(_spawner: Spawner) { Timer::after_millis(300).await; ch1.set_duty_cycle_fraction(1, 4); Timer::after_millis(300).await; - ch1.set_dutycycle_fraction(1, 2); + ch1.set_duty_cycle_fraction(1, 2); Timer::after_millis(300).await; ch1.set_duty_cycle(ch1.max_duty_cycle() - 1); Timer::after_millis(300).await; diff --git a/examples/stm32f4/src/bin/ws2812_pwm.rs b/examples/stm32f4/src/bin/ws2812_pwm.rs index 7a9fa302b..3ab93d6e0 100644 --- a/examples/stm32f4/src/bin/ws2812_pwm.rs +++ b/examples/stm32f4/src/bin/ws2812_pwm.rs @@ -84,7 +84,7 @@ async fn main(_spawner: Spawner) { let pwm_channel = Channel::Ch1; // make sure PWM output keep low on first start - ws2812_pwm.channel(pwm_channel).set_duty(0); + ws2812_pwm.channel(pwm_channel).set_duty_cycle(0); // flip color at 2 Hz let mut ticker = Ticker::every(Duration::from_millis(500)); diff --git a/examples/stm32g0/src/bin/pwm_input.rs b/examples/stm32g0/src/bin/pwm_input.rs index 983705e2f..db9cf4f8a 100644 --- a/examples/stm32g0/src/bin/pwm_input.rs +++ b/examples/stm32g0/src/bin/pwm_input.rs @@ -14,7 +14,6 @@ use embassy_stm32::gpio::{Level, Output, OutputType, Pull, Speed}; use embassy_stm32::time::khz; use embassy_stm32::timer::pwm_input::PwmInput; use embassy_stm32::timer::simple_pwm::{PwmPin, SimplePwm}; -use embassy_stm32::timer::Channel; use embassy_stm32::{bind_interrupts, peripherals, timer}; use embassy_time::Timer; use {defmt_rtt as _, panic_probe as _}; diff --git a/examples/stm32g4/src/bin/pwm.rs b/examples/stm32g4/src/bin/pwm.rs index 3833be58f..6c965012c 100644 --- a/examples/stm32g4/src/bin/pwm.rs +++ b/examples/stm32g4/src/bin/pwm.rs @@ -6,7 +6,6 @@ use embassy_executor::Spawner; use embassy_stm32::gpio::OutputType; use embassy_stm32::time::khz; use embassy_stm32::timer::simple_pwm::{PwmPin, SimplePwm}; -use embassy_stm32::timer::Channel; use embassy_time::Timer; use {defmt_rtt as _, panic_probe as _}; @@ -28,7 +27,7 @@ async fn main(_spawner: Spawner) { Timer::after_millis(300).await; ch1.set_duty_cycle_fraction(1, 4); Timer::after_millis(300).await; - ch1.set_dutycycle_fraction(1, 2); + ch1.set_duty_cycle_fraction(1, 2); Timer::after_millis(300).await; ch1.set_duty_cycle(ch1.max_duty_cycle() - 1); Timer::after_millis(300).await; diff --git a/examples/stm32h7/src/bin/pwm.rs b/examples/stm32h7/src/bin/pwm.rs index 1d0b89e97..a1c53fc3f 100644 --- a/examples/stm32h7/src/bin/pwm.rs +++ b/examples/stm32h7/src/bin/pwm.rs @@ -6,7 +6,6 @@ use embassy_executor::Spawner; use embassy_stm32::gpio::OutputType; use embassy_stm32::time::khz; use embassy_stm32::timer::simple_pwm::{PwmPin, SimplePwm}; -use embassy_stm32::timer::Channel; use embassy_stm32::Config; use embassy_time::Timer; use {defmt_rtt as _, panic_probe as _}; @@ -39,7 +38,7 @@ async fn main(_spawner: Spawner) { let ch1_pin = PwmPin::new_ch1(p.PA6, OutputType::PushPull); let mut pwm = SimplePwm::new(p.TIM3, Some(ch1_pin), None, None, None, khz(10), Default::default()); - let mut ch1 = pwm.ch1; + let mut ch1 = pwm.ch1(); ch1.enable(); info!("PWM initialized"); @@ -50,7 +49,7 @@ async fn main(_spawner: Spawner) { Timer::after_millis(300).await; ch1.set_duty_cycle_fraction(1, 4); Timer::after_millis(300).await; - ch1.set_dutycycle_fraction(1, 2); + ch1.set_duty_cycle_fraction(1, 2); Timer::after_millis(300).await; ch1.set_duty_cycle(ch1.max_duty_cycle() - 1); Timer::after_millis(300).await; From df06c2bbfe51e22e0d3eda3d760839f617ffbd96 Mon Sep 17 00:00:00 2001 From: Grant Miller Date: Sat, 7 Sep 2024 11:13:18 -0500 Subject: [PATCH 022/361] wip: split by value --- embassy-stm32/src/timer/low_level.rs | 9 +++++++++ embassy-stm32/src/timer/mod.rs | 1 + embassy-stm32/src/timer/simple_pwm.rs | 17 +++++++++++------ 3 files changed, 21 insertions(+), 6 deletions(-) diff --git a/embassy-stm32/src/timer/low_level.rs b/embassy-stm32/src/timer/low_level.rs index e643722aa..6377054c5 100644 --- a/embassy-stm32/src/timer/low_level.rs +++ b/embassy-stm32/src/timer/low_level.rs @@ -6,6 +6,8 @@ //! //! The available functionality depends on the timer type. +use core::mem::ManuallyDrop; + use embassy_hal_internal::{into_ref, Peripheral, PeripheralRef}; // Re-export useful enums pub use stm32_metapac::timer::vals::{FilterValue, Sms as SlaveMode, Ts as TriggerSource}; @@ -198,6 +200,13 @@ impl<'d, T: CoreInstance> Timer<'d, T> { Self { tim } } + pub(crate) unsafe fn clone_unchecked(&self) -> ManuallyDrop { + // this doesn't work for some reason + // let tim = unsafe { self.tim.clone_unchecked() }; + let tim = todo!(); + ManuallyDrop::new(Self { tim }) + } + /// Get access to the virutal core 16bit timer registers. /// /// Note: This works even if the timer is more capable, because registers diff --git a/embassy-stm32/src/timer/mod.rs b/embassy-stm32/src/timer/mod.rs index 25782ee13..6cf22689b 100644 --- a/embassy-stm32/src/timer/mod.rs +++ b/embassy-stm32/src/timer/mod.rs @@ -2,6 +2,7 @@ use core::marker::PhantomData; +use embassy_hal_internal::Peripheral; use embassy_sync::waitqueue::AtomicWaker; #[cfg(not(stm32l0))] diff --git a/embassy-stm32/src/timer/simple_pwm.rs b/embassy-stm32/src/timer/simple_pwm.rs index 7e2e9c202..9e4a09095 100644 --- a/embassy-stm32/src/timer/simple_pwm.rs +++ b/embassy-stm32/src/timer/simple_pwm.rs @@ -1,6 +1,7 @@ //! Simple PWM driver. use core::marker::PhantomData; +use core::mem::ManuallyDrop; use embassy_hal_internal::{into_ref, PeripheralRef}; @@ -57,7 +58,7 @@ channel_impl!(new_ch4, Ch4, Channel4Pin); /// It is not possible to change the pwm frequency because /// the frequency configuration is shared with all four channels. pub struct SimplePwmChannel<'d, T: GeneralInstance4Channel> { - timer: &'d Timer<'d, T>, + timer: ManuallyDrop>, channel: Channel, } @@ -199,7 +200,7 @@ impl<'d, T: GeneralInstance4Channel> SimplePwm<'d, T> { /// If you need to use multiple channels, use [`Self::split`]. pub fn channel(&mut self, channel: Channel) -> SimplePwmChannel<'_, T> { SimplePwmChannel { - timer: &self.inner, + timer: unsafe { self.inner.clone_unchecked() }, channel, } } @@ -245,12 +246,16 @@ impl<'d, T: GeneralInstance4Channel> SimplePwm<'d, T> { /// This returns all four channels, including channels that /// aren't configured with a [`PwmPin`]. // TODO: I hate the name "split" - pub fn split(&mut self) -> SimplePwmChannels<'_, T> { - // TODO: pre-enable channels? + pub fn split(self) -> SimplePwmChannels<'static, T> + where + // must be static because the timer will never be dropped/disabled + 'd: 'static, + { + // without this, the timer would be disabled at the end of this function + let timer = ManuallyDrop::new(self.inner); - // we can't use self.channel() because that takes &mut self let ch = |channel| SimplePwmChannel { - timer: &self.inner, + timer: unsafe { timer.clone_unchecked() }, channel, }; From 89bad07e817dec482d385f765da5be3b6d2d0e4c Mon Sep 17 00:00:00 2001 From: Nathan Perry Date: Fri, 20 Sep 2024 01:52:59 -0400 Subject: [PATCH 023/361] embassy_sync: `Sink` adapter for `pubsub::Pub` Corresponding to the `Stream` impl for `pubsub::Sub`. Notable difference is that we need a separate adapter type to store the pending item, i.e. we can't `impl Sink for Pub` directly. Instead a method `Pub::sink(&self)` is exposed, which constructs a `PubSink`. --- embassy-sync/Cargo.toml | 3 +- embassy-sync/src/pubsub/mod.rs | 26 +++++++++++ embassy-sync/src/pubsub/publisher.rs | 67 ++++++++++++++++++++++++++++ 3 files changed, 95 insertions(+), 1 deletion(-) diff --git a/embassy-sync/Cargo.toml b/embassy-sync/Cargo.toml index 7b7d2bf8e..2f049b6bc 100644 --- a/embassy-sync/Cargo.toml +++ b/embassy-sync/Cargo.toml @@ -27,6 +27,7 @@ turbowakers = [] defmt = { version = "0.3", optional = true } log = { version = "0.4.14", optional = true } +futures-sink = { version = "0.3", default-features = false, features = [] } futures-util = { version = "0.3.17", default-features = false } critical-section = "1.1" heapless = "0.8" @@ -37,7 +38,7 @@ embedded-io-async = { version = "0.6.1" } futures-executor = { version = "0.3.17", features = [ "thread-pool" ] } futures-test = "0.3.17" futures-timer = "3.0.2" -futures-util = { version = "0.3.17", features = [ "channel" ] } +futures-util = { version = "0.3.17", features = [ "channel", "sink" ] } # Enable critical-section implementation for std, for tests critical-section = { version = "1.1", features = ["std"] } diff --git a/embassy-sync/src/pubsub/mod.rs b/embassy-sync/src/pubsub/mod.rs index 812302e2b..1c0f68bd0 100644 --- a/embassy-sync/src/pubsub/mod.rs +++ b/embassy-sync/src/pubsub/mod.rs @@ -755,4 +755,30 @@ mod tests { assert_eq!(1, sub0.try_next_message_pure().unwrap().0); assert_eq!(0, sub1.try_next_message_pure().unwrap().0); } + + #[futures_test::test] + async fn publisher_sink() { + use futures_util::{SinkExt, StreamExt}; + + let channel = PubSubChannel::::new(); + + let mut sub = channel.subscriber().unwrap(); + + let publ = channel.publisher().unwrap(); + let mut sink = publ.sink(); + + sink.send(0).await.unwrap(); + assert_eq!(0, sub.try_next_message_pure().unwrap()); + + sink.send(1).await.unwrap(); + assert_eq!(1, sub.try_next_message_pure().unwrap()); + + sink.send_all(&mut futures_util::stream::iter(0..4).map(Ok)) + .await + .unwrap(); + assert_eq!(0, sub.try_next_message_pure().unwrap()); + assert_eq!(1, sub.try_next_message_pure().unwrap()); + assert_eq!(2, sub.try_next_message_pure().unwrap()); + assert_eq!(3, sub.try_next_message_pure().unwrap()); + } } diff --git a/embassy-sync/src/pubsub/publisher.rs b/embassy-sync/src/pubsub/publisher.rs index e66b3b1db..7a1ab66de 100644 --- a/embassy-sync/src/pubsub/publisher.rs +++ b/embassy-sync/src/pubsub/publisher.rs @@ -74,6 +74,12 @@ impl<'a, PSB: PubSubBehavior + ?Sized, T: Clone> Pub<'a, PSB, T> { pub fn is_full(&self) -> bool { self.channel.is_full() } + + /// Create a [`futures::Sink`] adapter for this publisher. + #[inline] + pub const fn sink(&self) -> PubSink<'a, '_, PSB, T> { + PubSink { publ: self, fut: None } + } } impl<'a, PSB: PubSubBehavior + ?Sized, T: Clone> Drop for Pub<'a, PSB, T> { @@ -221,6 +227,67 @@ impl<'a, M: RawMutex, T: Clone, const CAP: usize, const SUBS: usize, const PUBS: } } +#[must_use = "Sinks do nothing unless polled"] +/// [`futures_sink::Sink`] adapter for [`Pub`]. +pub struct PubSink<'a, 'p, PSB, T> +where + T: Clone, + PSB: PubSubBehavior + ?Sized, +{ + publ: &'p Pub<'a, PSB, T>, + fut: Option>, +} + +impl<'a, 'p, PSB, T> PubSink<'a, 'p, PSB, T> +where + PSB: PubSubBehavior + ?Sized, + T: Clone, +{ + /// Try to make progress on the pending future if we have one. + fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<()> { + let Some(mut fut) = self.fut.take() else { + return Poll::Ready(()); + }; + + if Pin::new(&mut fut).poll(cx).is_pending() { + self.fut = Some(fut); + return Poll::Pending; + } + + Poll::Ready(()) + } +} + +impl<'a, 'p, PSB, T> futures_sink::Sink for PubSink<'a, 'p, PSB, T> +where + PSB: PubSubBehavior + ?Sized, + T: Clone, +{ + type Error = core::convert::Infallible; + + #[inline] + fn poll_ready(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + self.poll(cx).map(Ok) + } + + #[inline] + fn start_send(mut self: Pin<&mut Self>, item: T) -> Result<(), Self::Error> { + self.fut = Some(self.publ.publish(item)); + + Ok(()) + } + + #[inline] + fn poll_flush(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + self.poll(cx).map(Ok) + } + + #[inline] + fn poll_close(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + self.poll(cx).map(Ok) + } +} + /// Future for the publisher wait action #[must_use = "futures do nothing unless you `.await` or poll them"] pub struct PublisherWaitFuture<'s, 'a, PSB: PubSubBehavior + ?Sized, T: Clone> { From f2646b29a6b0a741fc424f88c5ca3dc25fce9369 Mon Sep 17 00:00:00 2001 From: Grant Miller Date: Sat, 21 Sep 2024 07:52:54 -0500 Subject: [PATCH 024/361] Make clone_unchecked work --- embassy-stm32/src/timer/low_level.rs | 4 +--- embassy-stm32/src/timer/mod.rs | 2 +- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/embassy-stm32/src/timer/low_level.rs b/embassy-stm32/src/timer/low_level.rs index 6377054c5..3136ea4e9 100644 --- a/embassy-stm32/src/timer/low_level.rs +++ b/embassy-stm32/src/timer/low_level.rs @@ -201,9 +201,7 @@ impl<'d, T: CoreInstance> Timer<'d, T> { } pub(crate) unsafe fn clone_unchecked(&self) -> ManuallyDrop { - // this doesn't work for some reason - // let tim = unsafe { self.tim.clone_unchecked() }; - let tim = todo!(); + let tim = unsafe { self.tim.clone_unchecked() }; ManuallyDrop::new(Self { tim }) } diff --git a/embassy-stm32/src/timer/mod.rs b/embassy-stm32/src/timer/mod.rs index 6cf22689b..aa9dd91d9 100644 --- a/embassy-stm32/src/timer/mod.rs +++ b/embassy-stm32/src/timer/mod.rs @@ -67,7 +67,7 @@ impl State { } } -trait SealedInstance: RccPeripheral { +trait SealedInstance: RccPeripheral + Peripheral

{ /// Async state for this timer fn state() -> &'static State; } From a669611d7c8fb17666f35086ea20c476b6029854 Mon Sep 17 00:00:00 2001 From: Peter Krull Date: Mon, 23 Sep 2024 20:09:35 +0200 Subject: [PATCH 025/361] Discontinue peek, add AnonReceiver --- embassy-sync/src/watch.rs | 440 +++++++++++++++++++++++++------------- 1 file changed, 289 insertions(+), 151 deletions(-) diff --git a/embassy-sync/src/watch.rs b/embassy-sync/src/watch.rs index 298c09d43..1b4a8b589 100644 --- a/embassy-sync/src/watch.rs +++ b/embassy-sync/src/watch.rs @@ -20,7 +20,9 @@ use crate::waitqueue::MultiWakerRegistration; /// always provided with the latest value. /// /// Typically, `Watch` instances are declared as `static`, and a [`Sender`] and [`Receiver`] -/// (or [`DynSender`] and/or [`DynReceiver`]) are obtained and passed to the relevant parts of the program. +/// (or [`DynSender`] and/or [`DynReceiver`]) are obtained where relevant. An [`AnonReceiver`] +/// and [`DynAnonReceiver`] are also available, which do not increase the receiver count for the +/// channel, and unwrapping is therefore not required, but it is not possible to `.await` the channel. /// ``` /// /// use futures_executor::block_on; @@ -41,25 +43,25 @@ use crate::waitqueue::MultiWakerRegistration; /// assert_eq!(rcv1.try_changed(), None); /// /// snd.send(10); -/// +/// /// // Receive the new value (async or try) /// assert_eq!(rcv0.changed().await, 10); /// assert_eq!(rcv1.try_changed(), Some(10)); -/// +/// /// // No update /// assert_eq!(rcv0.try_changed(), None); /// assert_eq!(rcv1.try_changed(), None); /// /// snd.send(20); /// -/// // Peek does not mark the value as seen -/// assert_eq!(rcv0.peek().await, 20); -/// assert_eq!(rcv0.try_changed(), Some(20)); -/// -/// // Get marks the value as seen +/// // Using `get` marks the value as seen /// assert_eq!(rcv1.get().await, 20); /// assert_eq!(rcv1.try_changed(), None); /// +/// // But `get` also returns when unchanged +/// assert_eq!(rcv1.get().await, 20); +/// assert_eq!(rcv1.get().await, 20); +/// /// }; /// block_on(f); /// ``` @@ -82,24 +84,11 @@ pub trait WatchBehavior { /// Clears the value of the `Watch`. fn clear(&self); - /// Poll the `Watch` for the current value, **without** making it as seen. - fn poll_peek(&self, cx: &mut Context<'_>) -> Poll; - - /// Tries to peek the value of the `Watch`, **without** marking it as seen. - fn try_peek(&self) -> Option; - - /// Poll the `Watch` for the value if it matches the predicate function - /// `f`, **without** making it as seen. - fn poll_peek_and(&self, f: &mut dyn Fn(&T) -> bool, cx: &mut Context<'_>) -> Poll; - - /// Tries to peek the value of the `Watch` if it matches the predicate function `f`, **without** marking it as seen. - fn try_peek_and(&self, f: &mut dyn Fn(&T) -> bool) -> Option; - /// Poll the `Watch` for the current value, making it as seen. fn poll_get(&self, id: &mut u64, cx: &mut Context<'_>) -> Poll; - /// Tries to get the value of the `Watch`, marking it as seen. - fn try_get(&self, id: &mut u64) -> Option; + /// Tries to get the value of the `Watch`, marking it as seen, if an id is given. + fn try_get(&self, id: Option<&mut u64>) -> Option; /// Poll the `Watch` for the value if it matches the predicate function /// `f`, making it as seen. @@ -107,9 +96,9 @@ pub trait WatchBehavior { /// Tries to get the value of the `Watch` if it matches the predicate function /// `f`, marking it as seen. - fn try_get_and(&self, id: &mut u64, f: &mut dyn Fn(&T) -> bool) -> Option; + fn try_get_and(&self, id: Option<&mut u64>, f: &mut dyn Fn(&T) -> bool) -> Option; - /// Poll the `Watch` for a changed value, marking it as seen. + /// Poll the `Watch` for a changed value, marking it as seen, if an id is given. fn poll_changed(&self, id: &mut u64, cx: &mut Context<'_>) -> Poll; /// Tries to retrieve the value of the `Watch` if it has changed, marking it as seen. @@ -128,7 +117,11 @@ pub trait WatchBehavior { /// Modify the value of the `Watch` using a closure. Returns `false` if the /// `Watch` does not already contain a value. - fn modify(&self, f: &mut dyn Fn(&mut Option)); + fn send_modify(&self, f: &mut dyn Fn(&mut Option)); + + /// Modify the value of the `Watch` using a closure. Returns `false` if the + /// `Watch` does not already contain a value. + fn send_if_modified(&self, f: &mut dyn Fn(&mut Option) -> bool); /// Used when a receiver is dropped to decrement the receiver count. /// @@ -153,46 +146,6 @@ impl WatchBehavior for Watch }) } - fn poll_peek(&self, cx: &mut Context<'_>) -> Poll { - self.mutex.lock(|state| { - let mut s = state.borrow_mut(); - match &s.data { - Some(data) => Poll::Ready(data.clone()), - None => { - s.wakers.register(cx.waker()); - Poll::Pending - } - } - }) - } - - fn try_peek(&self) -> Option { - self.mutex.lock(|state| state.borrow().data.clone()) - } - - fn poll_peek_and(&self, f: &mut dyn Fn(&T) -> bool, cx: &mut Context<'_>) -> Poll { - self.mutex.lock(|state| { - let mut s = state.borrow_mut(); - match s.data { - Some(ref data) if f(data) => Poll::Ready(data.clone()), - _ => { - s.wakers.register(cx.waker()); - Poll::Pending - } - } - }) - } - - fn try_peek_and(&self, f: &mut dyn Fn(&T) -> bool) -> Option { - self.mutex.lock(|state| { - let s = state.borrow(); - match s.data { - Some(ref data) if f(data) => Some(data.clone()), - _ => None, - } - }) - } - fn poll_get(&self, id: &mut u64, cx: &mut Context<'_>) -> Poll { self.mutex.lock(|state| { let mut s = state.borrow_mut(); @@ -209,11 +162,13 @@ impl WatchBehavior for Watch }) } - fn try_get(&self, id: &mut u64) -> Option { + fn try_get(&self, id: Option<&mut u64>) -> Option { self.mutex.lock(|state| { let s = state.borrow(); - *id = s.current_id; - state.borrow().data.clone() + if let Some(id) = id { + *id = s.current_id; + } + s.data.clone() }) } @@ -233,12 +188,14 @@ impl WatchBehavior for Watch }) } - fn try_get_and(&self, id: &mut u64, f: &mut dyn Fn(&T) -> bool) -> Option { + fn try_get_and(&self, id: Option<&mut u64>, f: &mut dyn Fn(&T) -> bool) -> Option { self.mutex.lock(|state| { let s = state.borrow(); match s.data { Some(ref data) if f(data) => { - *id = s.current_id; + if let Some(id) = id { + *id = s.current_id; + } Some(data.clone()) } _ => None, @@ -315,7 +272,7 @@ impl WatchBehavior for Watch }) } - fn modify(&self, f: &mut dyn Fn(&mut Option)) { + fn send_modify(&self, f: &mut dyn Fn(&mut Option)) { self.mutex.lock(|state| { let mut s = state.borrow_mut(); f(&mut s.data); @@ -323,6 +280,16 @@ impl WatchBehavior for Watch s.wakers.wake(); }) } + + fn send_if_modified(&self, f: &mut dyn Fn(&mut Option) -> bool) { + self.mutex.lock(|state| { + let mut s = state.borrow_mut(); + if f(&mut s.data) { + s.current_id += 1; + s.wakers.wake(); + } + }) + } } impl Watch { @@ -375,6 +342,60 @@ impl Watch { } }) } + + /// Try to create a new [`AnonReceiver`] for the `Watch`. + pub fn anon_receiver(&self) -> AnonReceiver<'_, M, T, N> { + AnonReceiver(AnonRcv::new(self, 0)) + } + + /// Try to create a new [`DynAnonReceiver`] for the `Watch`. + pub fn dyn_anon_receiver(&self) -> DynAnonReceiver<'_, T> { + DynAnonReceiver(AnonRcv::new(self, 0)) + } + + /// Returns the message ID of the latest message sent to the `Watch`. + /// + /// This counter is monotonic, and is incremented every time a new message is sent. + pub fn get_msg_id(&self) -> u64 { + self.mutex.lock(|state| state.borrow().current_id) + } + + /// Waits for the `Watch` to be initialized with a value using a busy-wait mechanism. + /// + /// This is useful for initialization code where receivers may only be interested in + /// awaiting the value once in the lifetime of the program. It is therefore a temporaryily + /// CPU-inefficient operation, while being more memory efficient than using a `Receiver`. + /// + /// **Note** Be careful about using this within an InterruptExecutor, as it will starve + /// tasks in lower-priority executors. + pub async fn spin_get(&self) -> T { + poll_fn(|cx| { + self.mutex.lock(|state| { + let s = state.borrow(); + match &s.data { + Some(data) => Poll::Ready(data.clone()), + None => { + cx.waker().wake_by_ref(); + Poll::Pending + } + } + }) + }) + .await + } + + /// Tries to get the value of the `Watch`. + pub fn try_get(&self) -> Option { + WatchBehavior::try_get(self, None) + } + + /// Tries to get the value of the `Watch` if it matches the predicate function `f`. + pub fn try_get_and(&self, mut f: F) -> Option + where + F: Fn(&T) -> bool, + { + WatchBehavior::try_get_and(self, None, &mut f) + } } /// A receiver can `.await` a change in the `Watch` value. @@ -407,23 +428,23 @@ impl<'a, T: Clone, W: WatchBehavior + ?Sized> Snd<'a, T, W> { } /// Clears the value of the `Watch`. - /// This will cause calls to [`Rcv::get`] and [`Rcv::peek`] to be pending. + /// This will cause calls to [`Rcv::get`] to be pending. pub fn clear(&self) { self.watch.clear() } /// Tries to retrieve the value of the `Watch`. - pub fn try_peek(&self) -> Option { - self.watch.try_peek() + pub fn try_get(&self) -> Option { + self.watch.try_get(None) } /// Tries to peek the current value of the `Watch` if it matches the predicate /// function `f`. - pub fn try_peek_and(&self, mut f: F) -> Option + pub fn try_get_and(&self, mut f: F) -> Option where F: Fn(&T) -> bool, { - self.watch.try_peek_and(&mut f) + self.watch.try_get_and(None, &mut f) } /// Returns true if the `Watch` contains a value. @@ -432,11 +453,20 @@ impl<'a, T: Clone, W: WatchBehavior + ?Sized> Snd<'a, T, W> { } /// Modify the value of the `Watch` using a closure. - pub fn modify(&self, mut f: F) + pub fn send_modify(&self, mut f: F) where F: Fn(&mut Option), { - self.watch.modify(&mut f) + self.watch.send_modify(&mut f) + } + + /// Modify the value of the `Watch` using a closure. The closure must return + /// `true` if the value was modified, which notifies all receivers. + pub fn send_if_modified(&self, mut f: F) + where + F: Fn(&mut Option) -> bool, + { + self.watch.send_if_modified(&mut f) } } @@ -521,38 +551,6 @@ impl<'a, T: Clone, W: WatchBehavior + ?Sized> Rcv<'a, T, W> { } } - /// Returns the current value of the `Watch` once it is initialized, **without** marking it as seen. - /// - /// **Note**: Futures do nothing unless you `.await` or poll them. - pub async fn peek(&self) -> T { - poll_fn(|cx| self.watch.poll_peek(cx)).await - } - - /// Tries to peek the current value of the `Watch` without waiting, and **without** marking it as seen. - pub fn try_peek(&self) -> Option { - self.watch.try_peek() - } - - /// Returns the current value of the `Watch` if it matches the predicate function `f`, - /// or waits for it to match, **without** marking it as seen. - /// - /// **Note**: Futures do nothing unless you `.await` or poll them. - pub async fn peek_and(&self, mut f: F) -> T - where - F: Fn(&T) -> bool, - { - poll_fn(|cx| self.watch.poll_peek_and(&mut f, cx)).await - } - - /// Tries to peek the current value of the `Watch` if it matches the predicate - /// function `f` without waiting, and **without** marking it as seen. - pub fn try_peek_and(&self, mut f: F) -> Option - where - F: Fn(&T) -> bool, - { - self.watch.try_peek_and(&mut f) - } - /// Returns the current value of the `Watch` once it is initialized, marking it as seen. /// /// **Note**: Futures do nothing unless you `.await` or poll them. @@ -562,7 +560,7 @@ impl<'a, T: Clone, W: WatchBehavior + ?Sized> Rcv<'a, T, W> { /// Tries to get the current value of the `Watch` without waiting, marking it as seen. pub fn try_get(&mut self) -> Option { - self.watch.try_get(&mut self.at_id) + self.watch.try_get(Some(&mut self.at_id)) } /// Returns the value of the `Watch` if it matches the predicate function `f`, @@ -582,7 +580,7 @@ impl<'a, T: Clone, W: WatchBehavior + ?Sized> Rcv<'a, T, W> { where F: Fn(&T) -> bool, { - self.watch.try_get_and(&mut self.at_id, &mut f) + self.watch.try_get_and(Some(&mut self.at_id), &mut f) } /// Waits for the `Watch` to change and returns the new value, marking it as seen. @@ -618,7 +616,7 @@ impl<'a, T: Clone, W: WatchBehavior + ?Sized> Rcv<'a, T, W> { } /// Checks if the `Watch` contains a value. If this returns true, - /// then awaiting [`Rcv::get`] and [`Rcv::peek`] will return immediately. + /// then awaiting [`Rcv::get`] will return immediately. pub fn contains_value(&self) -> bool { self.watch.contains_value() } @@ -630,6 +628,58 @@ impl<'a, T: Clone, W: WatchBehavior + ?Sized> Drop for Rcv<'a, T, W> { } } +/// A anonymous receiver can NOT `.await` a change in the `Watch` value. +pub struct AnonRcv<'a, T: Clone, W: WatchBehavior + ?Sized> { + watch: &'a W, + at_id: u64, + _phantom: PhantomData, +} + +impl<'a, T: Clone, W: WatchBehavior + ?Sized> AnonRcv<'a, T, W> { + /// Creates a new `Receiver` with a reference to the `Watch`. + fn new(watch: &'a W, at_id: u64) -> Self { + Self { + watch, + at_id, + _phantom: PhantomData, + } + } + + /// Tries to get the current value of the `Watch` without waiting, marking it as seen. + pub fn try_get(&mut self) -> Option { + self.watch.try_get(Some(&mut self.at_id)) + } + + /// Tries to get the current value of the `Watch` if it matches the predicate + /// function `f` without waiting, marking it as seen. + pub fn try_get_and(&mut self, mut f: F) -> Option + where + F: Fn(&T) -> bool, + { + self.watch.try_get_and(Some(&mut self.at_id), &mut f) + } + + /// Tries to get the new value of the watch without waiting, marking it as seen. + pub fn try_changed(&mut self) -> Option { + self.watch.try_changed(&mut self.at_id) + } + + /// Tries to get the new value of the watch which satisfies the predicate + /// function `f` and returns the new value without waiting, marking it as seen. + pub fn try_changed_and(&mut self, mut f: F) -> Option + where + F: Fn(&T) -> bool, + { + self.watch.try_changed_and(&mut self.at_id, &mut f) + } + + /// Checks if the `Watch` contains a value. If this returns true, + /// then awaiting [`Rcv::get`] will return immediately. + pub fn contains_value(&self) -> bool { + self.watch.contains_value() + } +} + /// A receiver of a `Watch` channel. pub struct Receiver<'a, M: RawMutex, T: Clone, const N: usize>(Rcv<'a, T, Watch>); @@ -682,6 +732,58 @@ impl<'a, T: Clone> DerefMut for DynReceiver<'a, T> { } } +/// A receiver of a `Watch` channel that cannot `.await` values. +pub struct AnonReceiver<'a, M: RawMutex, T: Clone, const N: usize>(AnonRcv<'a, T, Watch>); + +impl<'a, M: RawMutex, T: Clone, const N: usize> AnonReceiver<'a, M, T, N> { + /// Converts the `Receiver` into a [`DynReceiver`]. + pub fn as_dyn(self) -> DynAnonReceiver<'a, T> { + let rcv = DynAnonReceiver(AnonRcv::new(self.0.watch, self.at_id)); + core::mem::forget(self); // Ensures the destructor is not called + rcv + } +} + +impl<'a, M: RawMutex, T: Clone, const N: usize> Into> for AnonReceiver<'a, M, T, N> { + fn into(self) -> DynAnonReceiver<'a, T> { + self.as_dyn() + } +} + +impl<'a, M: RawMutex, T: Clone, const N: usize> Deref for AnonReceiver<'a, M, T, N> { + type Target = AnonRcv<'a, T, Watch>; + + fn deref(&self) -> &Self::Target { + &self.0 + } +} + +impl<'a, M: RawMutex, T: Clone, const N: usize> DerefMut for AnonReceiver<'a, M, T, N> { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.0 + } +} + +/// A receiver that cannot `.await` value, which holds a **dynamic** reference to a `Watch` channel. +/// +/// This is an alternative to [`AnonReceiver`] with a simpler type definition, at the expense of +/// some runtime performance due to dynamic dispatch. +pub struct DynAnonReceiver<'a, T: Clone>(AnonRcv<'a, T, dyn WatchBehavior + 'a>); + +impl<'a, T: Clone> Deref for DynAnonReceiver<'a, T> { + type Target = AnonRcv<'a, T, dyn WatchBehavior + 'a>; + + fn deref(&self) -> &Self::Target { + &self.0 + } +} + +impl<'a, T: Clone> DerefMut for DynAnonReceiver<'a, T> { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.0 + } +} + #[cfg(test)] mod tests { use futures_executor::block_on; @@ -715,6 +817,72 @@ mod tests { block_on(f); } + #[test] + fn all_try_get() { + let f = async { + static WATCH: Watch = Watch::new(); + + // Obtain receiver and sender + let mut rcv = WATCH.receiver().unwrap(); + let snd = WATCH.sender(); + + // Not initialized + assert_eq!(WATCH.try_get(), None); + assert_eq!(rcv.try_get(), None); + assert_eq!(snd.try_get(), None); + + // Receive the new value + snd.send(10); + assert_eq!(WATCH.try_get(), Some(10)); + assert_eq!(rcv.try_get(), Some(10)); + assert_eq!(snd.try_get(), Some(10)); + + assert_eq!(WATCH.try_get_and(|x| x > &5), Some(10)); + assert_eq!(rcv.try_get_and(|x| x > &5), Some(10)); + assert_eq!(snd.try_get_and(|x| x > &5), Some(10)); + + assert_eq!(WATCH.try_get_and(|x| x < &5), None); + assert_eq!(rcv.try_get_and(|x| x < &5), None); + assert_eq!(snd.try_get_and(|x| x < &5), None); + }; + block_on(f); + } + + #[test] + fn once_lock_like() { + let f = async { + static CONFIG0: u8 = 10; + static CONFIG1: u8 = 20; + + static WATCH: Watch = Watch::new(); + + // Obtain receiver and sender + let mut rcv = WATCH.receiver().unwrap(); + let snd = WATCH.sender(); + + // Not initialized + assert_eq!(rcv.try_changed(), None); + + // Receive the new value + snd.send(&CONFIG0); + let rcv0 = rcv.changed().await; + assert_eq!(rcv0, &10); + + // Receive another value + snd.send(&CONFIG1); + let rcv1 = rcv.try_changed(); + assert_eq!(rcv1, Some(&20)); + + // No update + assert_eq!(rcv.try_changed(), None); + + // Ensure similarity with original static + assert_eq!(rcv0, &CONFIG0); + assert_eq!(rcv1, Some(&CONFIG1)); + }; + block_on(f); + } + #[test] fn sender_modify() { let f = async { @@ -729,7 +897,7 @@ mod tests { assert_eq!(rcv.try_changed(), Some(10)); // Modify the value inplace - snd.modify(|opt| { + snd.send_modify(|opt| { if let Some(inner) = opt { *inner += 5; } @@ -751,11 +919,6 @@ mod tests { let mut rcv = WATCH.receiver().unwrap(); let snd = WATCH.sender(); - snd.send(10); - assert_eq!(rcv.try_peek_and(|x| x > &5), Some(10)); - assert_eq!(rcv.try_peek_and(|x| x < &5), None); - assert!(rcv.try_changed().is_some()); - snd.send(15); assert_eq!(rcv.try_get_and(|x| x > &5), Some(15)); assert_eq!(rcv.try_get_and(|x| x < &5), None); @@ -771,7 +934,6 @@ mod tests { snd.send(30); assert_eq!(rcv.changed_and(|x| x > &5).await, 30); - assert_eq!(rcv.peek_and(|x| x > &5).await, 30); assert_eq!(rcv.get_and(|x| x > &5).await, 30); }; block_on(f); @@ -825,7 +987,7 @@ mod tests { // Obtain receivers and sender let mut rcv0 = WATCH.receiver().unwrap(); - let mut rcv1 = WATCH.receiver().unwrap(); + let mut rcv1 = WATCH.anon_receiver(); let snd = WATCH.sender(); // No update for both @@ -864,41 +1026,13 @@ mod tests { block_on(f); } - #[test] - fn peek_get_changed() { - let f = async { - static WATCH: Watch = Watch::new(); - - // Obtain receiver and sender - let mut rcv = WATCH.receiver().unwrap(); - let snd = WATCH.sender(); - - // Send a value - snd.send(10); - - // Ensure peek does not mark as seen - assert_eq!(rcv.peek().await, 10); - assert_eq!(rcv.try_changed(), Some(10)); - assert_eq!(rcv.try_changed(), None); - assert_eq!(rcv.try_peek(), Some(10)); - - // Send a value - snd.send(20); - - // Ensure get does mark as seen - assert_eq!(rcv.get().await, 20); - assert_eq!(rcv.try_changed(), None); - assert_eq!(rcv.try_get(), Some(20)); - }; - block_on(f); - } - #[test] fn use_dynamics() { let f = async { static WATCH: Watch = Watch::new(); // Obtain receiver and sender + let mut anon_rcv = WATCH.dyn_anon_receiver(); let mut dyn_rcv = WATCH.dyn_receiver().unwrap(); let dyn_snd = WATCH.dyn_sender(); @@ -906,6 +1040,7 @@ mod tests { dyn_snd.send(10); // Ensure the dynamic receiver receives the value + assert_eq!(anon_rcv.try_changed(), Some(10)); assert_eq!(dyn_rcv.try_changed(), Some(10)); assert_eq!(dyn_rcv.try_changed(), None); }; @@ -918,10 +1053,12 @@ mod tests { static WATCH: Watch = Watch::new(); // Obtain receiver and sender + let anon_rcv = WATCH.anon_receiver(); let rcv = WATCH.receiver().unwrap(); let snd = WATCH.sender(); // Convert to dynamic + let mut dyn_anon_rcv = anon_rcv.as_dyn(); let mut dyn_rcv = rcv.as_dyn(); let dyn_snd = snd.as_dyn(); @@ -929,6 +1066,7 @@ mod tests { dyn_snd.send(10); // Ensure the dynamic receiver receives the value + assert_eq!(dyn_anon_rcv.try_changed(), Some(10)); assert_eq!(dyn_rcv.try_changed(), Some(10)); assert_eq!(dyn_rcv.try_changed(), None); }; From 999807f226623669a9cfc8ca218d3c81f0c04a77 Mon Sep 17 00:00:00 2001 From: Peter Krull Date: Mon, 23 Sep 2024 20:29:50 +0200 Subject: [PATCH 026/361] Added SealedWatchBehavior to limit access to core functions --- embassy-sync/src/watch.rs | 137 ++++++++++++++++++++------------------ 1 file changed, 71 insertions(+), 66 deletions(-) diff --git a/embassy-sync/src/watch.rs b/embassy-sync/src/watch.rs index 1b4a8b589..4b7ffa5fc 100644 --- a/embassy-sync/src/watch.rs +++ b/embassy-sync/src/watch.rs @@ -76,28 +76,14 @@ struct WatchState { receiver_count: usize, } -/// A trait representing the 'inner' behavior of the `Watch`. -pub trait WatchBehavior { - /// Sends a new value to the `Watch`. - fn send(&self, val: T); - - /// Clears the value of the `Watch`. - fn clear(&self); - +trait SealedWatchBehavior { /// Poll the `Watch` for the current value, making it as seen. fn poll_get(&self, id: &mut u64, cx: &mut Context<'_>) -> Poll; - /// Tries to get the value of the `Watch`, marking it as seen, if an id is given. - fn try_get(&self, id: Option<&mut u64>) -> Option; - /// Poll the `Watch` for the value if it matches the predicate function /// `f`, making it as seen. fn poll_get_and(&self, id: &mut u64, f: &mut dyn Fn(&T) -> bool, cx: &mut Context<'_>) -> Poll; - /// Tries to get the value of the `Watch` if it matches the predicate function - /// `f`, marking it as seen. - fn try_get_and(&self, id: Option<&mut u64>, f: &mut dyn Fn(&T) -> bool) -> Option; - /// Poll the `Watch` for a changed value, marking it as seen, if an id is given. fn poll_changed(&self, id: &mut u64, cx: &mut Context<'_>) -> Poll; @@ -112,8 +98,16 @@ pub trait WatchBehavior { /// predicate function `f`, marking it as seen. fn try_changed_and(&self, id: &mut u64, f: &mut dyn Fn(&T) -> bool) -> Option; - /// Checks if the `Watch` is been initialized with a value. - fn contains_value(&self) -> bool; + /// Used when a receiver is dropped to decrement the receiver count. + /// + /// ## This method should not be called by the user. + fn drop_receiver(&self); + + /// Clears the value of the `Watch`. + fn clear(&self); + + /// Sends a new value to the `Watch`. + fn send(&self, val: T); /// Modify the value of the `Watch` using a closure. Returns `false` if the /// `Watch` does not already contain a value. @@ -122,30 +116,23 @@ pub trait WatchBehavior { /// Modify the value of the `Watch` using a closure. Returns `false` if the /// `Watch` does not already contain a value. fn send_if_modified(&self, f: &mut dyn Fn(&mut Option) -> bool); - - /// Used when a receiver is dropped to decrement the receiver count. - /// - /// ## This method should not be called by the user. - fn drop_receiver(&self); } -impl WatchBehavior for Watch { - fn send(&self, val: T) { - self.mutex.lock(|state| { - let mut s = state.borrow_mut(); - s.data = Some(val); - s.current_id += 1; - s.wakers.wake(); - }) - } +/// A trait representing the 'inner' behavior of the `Watch`. +#[allow(private_bounds)] +pub trait WatchBehavior: SealedWatchBehavior { + /// Tries to get the value of the `Watch`, marking it as seen, if an id is given. + fn try_get(&self, id: Option<&mut u64>) -> Option; - fn clear(&self) { - self.mutex.lock(|state| { - let mut s = state.borrow_mut(); - s.data = None; - }) - } + /// Tries to get the value of the `Watch` if it matches the predicate function + /// `f`, marking it as seen. + fn try_get_and(&self, id: Option<&mut u64>, f: &mut dyn Fn(&T) -> bool) -> Option; + /// Checks if the `Watch` is been initialized with a value. + fn contains_value(&self) -> bool; +} + +impl SealedWatchBehavior for Watch { fn poll_get(&self, id: &mut u64, cx: &mut Context<'_>) -> Poll { self.mutex.lock(|state| { let mut s = state.borrow_mut(); @@ -162,16 +149,6 @@ impl WatchBehavior for Watch }) } - fn try_get(&self, id: Option<&mut u64>) -> Option { - self.mutex.lock(|state| { - let s = state.borrow(); - if let Some(id) = id { - *id = s.current_id; - } - s.data.clone() - }) - } - fn poll_get_and(&self, id: &mut u64, f: &mut dyn Fn(&T) -> bool, cx: &mut Context<'_>) -> Poll { self.mutex.lock(|state| { let mut s = state.borrow_mut(); @@ -188,21 +165,6 @@ impl WatchBehavior for Watch }) } - fn try_get_and(&self, id: Option<&mut u64>, f: &mut dyn Fn(&T) -> bool) -> Option { - self.mutex.lock(|state| { - let s = state.borrow(); - match s.data { - Some(ref data) if f(data) => { - if let Some(id) = id { - *id = s.current_id; - } - Some(data.clone()) - } - _ => None, - } - }) - } - fn poll_changed(&self, id: &mut u64, cx: &mut Context<'_>) -> Poll { self.mutex.lock(|state| { let mut s = state.borrow_mut(); @@ -261,10 +223,6 @@ impl WatchBehavior for Watch }) } - fn contains_value(&self) -> bool { - self.mutex.lock(|state| state.borrow().data.is_some()) - } - fn drop_receiver(&self) { self.mutex.lock(|state| { let mut s = state.borrow_mut(); @@ -272,6 +230,22 @@ impl WatchBehavior for Watch }) } + fn clear(&self) { + self.mutex.lock(|state| { + let mut s = state.borrow_mut(); + s.data = None; + }) + } + + fn send(&self, val: T) { + self.mutex.lock(|state| { + let mut s = state.borrow_mut(); + s.data = Some(val); + s.current_id += 1; + s.wakers.wake(); + }) + } + fn send_modify(&self, f: &mut dyn Fn(&mut Option)) { self.mutex.lock(|state| { let mut s = state.borrow_mut(); @@ -292,6 +266,37 @@ impl WatchBehavior for Watch } } +impl WatchBehavior for Watch { + fn try_get(&self, id: Option<&mut u64>) -> Option { + self.mutex.lock(|state| { + let s = state.borrow(); + if let Some(id) = id { + *id = s.current_id; + } + s.data.clone() + }) + } + + fn try_get_and(&self, id: Option<&mut u64>, f: &mut dyn Fn(&T) -> bool) -> Option { + self.mutex.lock(|state| { + let s = state.borrow(); + match s.data { + Some(ref data) if f(data) => { + if let Some(id) = id { + *id = s.current_id; + } + Some(data.clone()) + } + _ => None, + } + }) + } + + fn contains_value(&self) -> bool { + self.mutex.lock(|state| state.borrow().data.is_some()) + } +} + impl Watch { /// Create a new `Watch` channel. pub const fn new() -> Self { From 5e1912a2d3adea920039dae3622643f34289290b Mon Sep 17 00:00:00 2001 From: Peter Krull Date: Tue, 24 Sep 2024 12:37:32 +0200 Subject: [PATCH 027/361] Reverse generics order, remove spin_get --- embassy-sync/src/watch.rs | 28 ++-------------------------- 1 file changed, 2 insertions(+), 26 deletions(-) diff --git a/embassy-sync/src/watch.rs b/embassy-sync/src/watch.rs index 4b7ffa5fc..336e64ba9 100644 --- a/embassy-sync/src/watch.rs +++ b/embassy-sync/src/watch.rs @@ -66,10 +66,10 @@ use crate::waitqueue::MultiWakerRegistration; /// block_on(f); /// ``` pub struct Watch { - mutex: Mutex>>, + mutex: Mutex>>, } -struct WatchState { +struct WatchState { data: Option, current_id: u64, wakers: MultiWakerRegistration, @@ -365,30 +365,6 @@ impl Watch { self.mutex.lock(|state| state.borrow().current_id) } - /// Waits for the `Watch` to be initialized with a value using a busy-wait mechanism. - /// - /// This is useful for initialization code where receivers may only be interested in - /// awaiting the value once in the lifetime of the program. It is therefore a temporaryily - /// CPU-inefficient operation, while being more memory efficient than using a `Receiver`. - /// - /// **Note** Be careful about using this within an InterruptExecutor, as it will starve - /// tasks in lower-priority executors. - pub async fn spin_get(&self) -> T { - poll_fn(|cx| { - self.mutex.lock(|state| { - let s = state.borrow(); - match &s.data { - Some(data) => Poll::Ready(data.clone()), - None => { - cx.waker().wake_by_ref(); - Poll::Pending - } - } - }) - }) - .await - } - /// Tries to get the value of the `Watch`. pub fn try_get(&self) -> Option { WatchBehavior::try_get(self, None) From a498bf11af2768b7aca24c3d84d4dfa20711c593 Mon Sep 17 00:00:00 2001 From: Caleb Jamison Date: Tue, 24 Sep 2024 18:45:20 -0400 Subject: [PATCH 028/361] Disable pad isolation on PWM A pins. Also fixes minor bug for 2040 where A pins didn't have their pull up/down enabled. --- embassy-rp/src/pwm.rs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/embassy-rp/src/pwm.rs b/embassy-rp/src/pwm.rs index 7da3dccb0..9ba3a2be3 100644 --- a/embassy-rp/src/pwm.rs +++ b/embassy-rp/src/pwm.rs @@ -106,6 +106,12 @@ impl<'d> Pwm<'d> { if let Some(pin) = &a { pin.gpio().ctrl().write(|w| w.set_funcsel(4)); + pin.pad_ctrl().modify(|w| { + #[cfg(feature = "_rp235x")] + w.set_iso(false); + w.set_pue(b_pull == Pull::Up); + w.set_pde(b_pull == Pull::Down); + }); } if let Some(pin) = &b { pin.gpio().ctrl().write(|w| w.set_funcsel(4)); From b743dce8e4777a3d1d3c2d1a40501be3af8b07e5 Mon Sep 17 00:00:00 2001 From: Caleb Jamison Date: Tue, 24 Sep 2024 18:55:05 -0400 Subject: [PATCH 029/361] Only B pins can be inputs. --- embassy-rp/src/pwm.rs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/embassy-rp/src/pwm.rs b/embassy-rp/src/pwm.rs index 9ba3a2be3..027f5504e 100644 --- a/embassy-rp/src/pwm.rs +++ b/embassy-rp/src/pwm.rs @@ -106,11 +106,9 @@ impl<'d> Pwm<'d> { if let Some(pin) = &a { pin.gpio().ctrl().write(|w| w.set_funcsel(4)); + #[cfg(feature = "_rp235x")] pin.pad_ctrl().modify(|w| { - #[cfg(feature = "_rp235x")] w.set_iso(false); - w.set_pue(b_pull == Pull::Up); - w.set_pde(b_pull == Pull::Down); }); } if let Some(pin) = &b { From ae5b78b27a9c644850c905c197e216a6cd2ab0b9 Mon Sep 17 00:00:00 2001 From: Anthony Grondin <104731965+AnthonyGrondin@users.noreply.github.com> Date: Mon, 23 Sep 2024 16:30:14 -0400 Subject: [PATCH 030/361] feat(embassy-net): Implement `wait_recv_ready()` + `wait_send_ready()` for UdpSocket - Provides `pub async fn wait_recv_ready(&self) -> ()` and `pub fn poll_recv_ready(&self, cx: &mut Context<'_>) -> Poll<()>`. This allows polling / waiting on a socket until it can be read, without dequeuing any packets. - Provides `pub async fn wait_send_ready(&self) -> ()` and `pub fn poll_send_ready(&self, cx: &mut Context<'_> -> Poll<()>` This allows polling / waiting on a socket until it becomes writable. --- embassy-net/src/udp.rs | 53 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 53 insertions(+) diff --git a/embassy-net/src/udp.rs b/embassy-net/src/udp.rs index 3eb6e2f83..b71037522 100644 --- a/embassy-net/src/udp.rs +++ b/embassy-net/src/udp.rs @@ -103,6 +103,32 @@ impl<'a> UdpSocket<'a> { }) } + /// Wait until the socket becomes readable. + /// + /// A socket is readable when a packet has been received, or when there are queued packets in + /// the buffer. + pub async fn wait_recv_ready(&self) { + poll_fn(move |cx| self.poll_recv_ready(cx)).await + } + + /// Wait until a datagram can be read. + /// + /// When no datagram is readable, this method will return `Poll::Pending` and + /// register the current task to be notified when a datagram is received. + /// + /// When a datagram is received, this method will return `Poll::Ready`. + pub fn poll_recv_ready(&self, cx: &mut Context<'_>) -> Poll<()> { + self.with_mut(|s, _| { + if s.can_recv() { + Poll::Ready(()) + } else { + // socket buffer is empty wait until at least one byte has arrived + s.register_recv_waker(cx.waker()); + Poll::Pending + } + }) + } + /// Receive a datagram. /// /// This method will wait until a datagram is received. @@ -164,6 +190,33 @@ impl<'a> UdpSocket<'a> { .await } + /// Wait until the socket becomes writable. + /// + /// A socket becomes writable when there is space in the buffer, from initial memory or after + /// dispatching datagrams on a full buffer. + pub async fn wait_send_ready(&self) { + poll_fn(move |cx| self.poll_send_ready(cx)).await + } + + /// Wait until a datagram can be sent. + /// + /// When no datagram can be sent (i.e. the buffer is full), this method will return + /// `Poll::Pending` and register the current task to be notified when + /// space is freed in the buffer after a datagram has been dispatched. + /// + /// When a datagram can be sent, this method will return `Poll::Ready`. + pub fn poll_send_ready(&self, cx: &mut Context<'_>) -> Poll<()> { + self.with_mut(|s, _| { + if s.can_send() { + Poll::Ready(()) + } else { + // socket buffer is full wait until a datagram has been dispatched + s.register_send_waker(cx.waker()); + Poll::Pending + } + }) + } + /// Send a datagram to the specified remote endpoint. /// /// This method will wait until the datagram has been sent. From e8da38772641ac19e5ded539144467efc9ed5a7b Mon Sep 17 00:00:00 2001 From: Anthony Grondin <104731965+AnthonyGrondin@users.noreply.github.com> Date: Tue, 24 Sep 2024 10:25:10 -0400 Subject: [PATCH 031/361] docs(embassy-net): Update can_send() and may_send() documentation to reflect actual behavior from smoltcp --- embassy-net/src/tcp.rs | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/embassy-net/src/tcp.rs b/embassy-net/src/tcp.rs index bcddbc95b..fc66d6192 100644 --- a/embassy-net/src/tcp.rs +++ b/embassy-net/src/tcp.rs @@ -376,11 +376,25 @@ impl<'a> TcpSocket<'a> { self.io.with_mut(|s, _| s.abort()) } - /// Get whether the socket is ready to send data, i.e. whether there is space in the send buffer. + /// Return whether the transmit half of the full-duplex connection is open. + /// + /// This function returns true if it's possible to send data and have it arrive + /// to the remote endpoint. However, it does not make any guarantees about the state + /// of the transmit buffer, and even if it returns true, [write](#method.write) may + /// not be able to enqueue any octets. + /// + /// In terms of the TCP state machine, the socket must be in the `ESTABLISHED` or + /// `CLOSE-WAIT` state. pub fn may_send(&self) -> bool { self.io.with(|s, _| s.may_send()) } + /// Check whether the transmit half of the full-duplex connection is open + /// (see [may_send](#method.may_send)), and the transmit buffer is not full. + pub fn can_send(&self) -> bool { + self.io.with(|s, _| s.can_send()) + } + /// return whether the receive half of the full-duplex connection is open. /// This function returns true if it’s possible to receive data from the remote endpoint. /// It will return true while there is data in the receive buffer, and if there isn’t, From 712fa08363067d0a0e3ff07b3bd0633bee3ba07e Mon Sep 17 00:00:00 2001 From: Anthony Grondin <104731965+AnthonyGrondin@users.noreply.github.com> Date: Tue, 24 Sep 2024 10:42:06 -0400 Subject: [PATCH 032/361] feat(embassy-net): Implement `wait_read_ready()` + `wait_write_ready()` for TcpSocket --- embassy-net/src/tcp.rs | 46 ++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 44 insertions(+), 2 deletions(-) diff --git a/embassy-net/src/tcp.rs b/embassy-net/src/tcp.rs index fc66d6192..8fdad01cc 100644 --- a/embassy-net/src/tcp.rs +++ b/embassy-net/src/tcp.rs @@ -10,7 +10,7 @@ use core::future::poll_fn; use core::mem; -use core::task::Poll; +use core::task::{Context, Poll}; use embassy_time::Duration; use smoltcp::iface::{Interface, SocketHandle}; @@ -274,6 +274,16 @@ impl<'a> TcpSocket<'a> { .await } + /// Wait until the socket becomes readable. + /// + /// A socket becomes readable when the receive half of the full-duplex connection is open + /// (see [may_recv](#method.may_recv)), and there is some pending data in the receive buffer. + /// + /// This is the equivalent of [read](#method.read), without buffering any data. + pub async fn wait_read_ready(&self) { + poll_fn(move |cx| self.io.poll_read_ready(cx)).await + } + /// Read data from the socket. /// /// Returns how many bytes were read, or an error. If no data is available, it waits @@ -285,6 +295,16 @@ impl<'a> TcpSocket<'a> { self.io.read(buf).await } + /// Wait until the socket becomes writable. + /// + /// A socket becomes writable when the transmit half of the full-duplex connection is open + /// (see [may_send](#method.may_send)), and the transmit buffer is not full. + /// + /// This is the equivalent of [write](#method.write), without sending any data. + pub async fn wait_write_ready(&self) { + poll_fn(move |cx| self.io.poll_write_ready(cx)).await + } + /// Write data to the socket. /// /// Returns how many bytes were written, or an error. If the socket is not ready to @@ -441,7 +461,7 @@ impl<'d> TcpIo<'d> { }) } - fn with_mut(&mut self, f: impl FnOnce(&mut tcp::Socket, &mut Interface) -> R) -> R { + fn with_mut(&self, f: impl FnOnce(&mut tcp::Socket, &mut Interface) -> R) -> R { self.stack.with_mut(|i| { let socket = i.sockets.get_mut::(self.handle); let res = f(socket, &mut i.iface); @@ -450,6 +470,17 @@ impl<'d> TcpIo<'d> { }) } + fn poll_read_ready(&self, cx: &mut Context<'_>) -> Poll<()> { + self.with_mut(|s, _| { + if s.can_recv() { + Poll::Ready(()) + } else { + s.register_recv_waker(cx.waker()); + Poll::Pending + } + }) + } + async fn read(&mut self, buf: &mut [u8]) -> Result { poll_fn(move |cx| { // CAUTION: smoltcp semantics around EOF are different to what you'd expect @@ -478,6 +509,17 @@ impl<'d> TcpIo<'d> { .await } + fn poll_write_ready(&self, cx: &mut Context<'_>) -> Poll<()> { + self.with_mut(|s, _| { + if s.can_send() { + Poll::Ready(()) + } else { + s.register_send_waker(cx.waker()); + Poll::Pending + } + }) + } + async fn write(&mut self, buf: &[u8]) -> Result { poll_fn(move |cx| { self.with_mut(|s, _| match s.send_slice(buf) { From 6e2c5d0b4500762b5a045a166c91f8b0e59db10e Mon Sep 17 00:00:00 2001 From: Romain Reignier Date: Thu, 26 Sep 2024 13:24:50 +0200 Subject: [PATCH 033/361] rp23: add missing binary info in linker script See https://github.com/rp-rs/rp-hal/issues/853 And https://github.com/rp-rs/rp-hal/pull/854 --- examples/rp23/memory.x | 1 + 1 file changed, 1 insertion(+) diff --git a/examples/rp23/memory.x b/examples/rp23/memory.x index 777492062..c803896f6 100644 --- a/examples/rp23/memory.x +++ b/examples/rp23/memory.x @@ -31,6 +31,7 @@ SECTIONS { { __start_block_addr = .; KEEP(*(.start_block)); + KEEP(*(.boot_info)); } > FLASH } INSERT AFTER .vector_table; From f19718b4f0400dec4e64d32d649c6b0d9eb554e5 Mon Sep 17 00:00:00 2001 From: Ulf Lilleengen Date: Thu, 26 Sep 2024 15:41:21 +0200 Subject: [PATCH 034/361] Add config option for setting SIM pin --- embassy-net-nrf91/src/context.rs | 12 ++++++++++++ examples/nrf9160/src/bin/modem_tcp_client.rs | 1 + 2 files changed, 13 insertions(+) diff --git a/embassy-net-nrf91/src/context.rs b/embassy-net-nrf91/src/context.rs index 8b45919ef..2dda615c1 100644 --- a/embassy-net-nrf91/src/context.rs +++ b/embassy-net-nrf91/src/context.rs @@ -21,6 +21,8 @@ pub struct Config<'a> { pub auth_prot: AuthProt, /// Credentials. pub auth: Option<(&'a [u8], &'a [u8])>, + /// SIM pin + pub pin: Option<&'a [u8]>, } /// Authentication protocol. @@ -133,6 +135,16 @@ impl<'a> Control<'a> { // info!("RES2: {}", unsafe { core::str::from_utf8_unchecked(&buf[..n]) }); CommandParser::parse(&buf[..n]).expect_identifier(b"OK").finish()?; + if let Some(pin) = config.pin { + let op = CommandBuilder::create_set(&mut cmd, true) + .named("+CPIN") + .with_string_parameter(pin) + .finish() + .map_err(|_| Error::BufferTooSmall)?; + let _ = self.control.at_command(op, &mut buf).await; + // Ignore ERROR which means no pin required + } + Ok(()) } diff --git a/examples/nrf9160/src/bin/modem_tcp_client.rs b/examples/nrf9160/src/bin/modem_tcp_client.rs index 929883884..495ee26dd 100644 --- a/examples/nrf9160/src/bin/modem_tcp_client.rs +++ b/examples/nrf9160/src/bin/modem_tcp_client.rs @@ -163,6 +163,7 @@ async fn main(spawner: Spawner) { apn: b"iot.nat.es", auth_prot: context::AuthProt::Pap, auth: Some((b"orange", b"orange")), + pin: None, }, stack ))); From 3792b14161ded15fe513640d0c1b52f39fb80856 Mon Sep 17 00:00:00 2001 From: Dinu Blanovschi Date: Fri, 27 Sep 2024 10:40:24 +0200 Subject: [PATCH 035/361] fix: change duplicate reference to `FirmwareUpdaterConfig::from_linkerfile_blocking` in rustdoc --- embassy-boot/src/firmware_updater/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/embassy-boot/src/firmware_updater/mod.rs b/embassy-boot/src/firmware_updater/mod.rs index 4c4f4f10b..4814786bf 100644 --- a/embassy-boot/src/firmware_updater/mod.rs +++ b/embassy-boot/src/firmware_updater/mod.rs @@ -8,7 +8,7 @@ use embedded_storage::nor_flash::{NorFlashError, NorFlashErrorKind}; /// Firmware updater flash configuration holding the two flashes used by the updater /// /// If only a single flash is actually used, then that flash should be partitioned into two partitions before use. -/// The easiest way to do this is to use [`FirmwareUpdaterConfig::from_linkerfile_blocking`] or [`FirmwareUpdaterConfig::from_linkerfile_blocking`] which will partition +/// The easiest way to do this is to use [`FirmwareUpdaterConfig::from_linkerfile`] or [`FirmwareUpdaterConfig::from_linkerfile_blocking`] which will partition /// the provided flash according to symbols defined in the linkerfile. pub struct FirmwareUpdaterConfig { /// The dfu flash partition From bc0180800d751e651c0d15c807285c11cdb4f486 Mon Sep 17 00:00:00 2001 From: Caleb Jamison Date: Tue, 1 Oct 2024 10:51:18 -0400 Subject: [PATCH 036/361] Remove binary_info blocks from most examples. (#3385) --- examples/rp23/src/bin/adc.rs | 12 +----------- examples/rp23/src/bin/adc_dma.rs | 10 ---------- examples/rp23/src/bin/assign_resources.rs | 10 ---------- examples/rp23/src/bin/blinky.rs | 11 +++++++---- examples/rp23/src/bin/blinky_two_channels.rs | 10 ---------- examples/rp23/src/bin/blinky_two_tasks.rs | 10 ---------- examples/rp23/src/bin/button.rs | 10 ---------- examples/rp23/src/bin/debounce.rs | 10 ---------- examples/rp23/src/bin/flash.rs | 10 ---------- examples/rp23/src/bin/gpio_async.rs | 10 ---------- examples/rp23/src/bin/gpout.rs | 10 ---------- examples/rp23/src/bin/i2c_async.rs | 10 ---------- examples/rp23/src/bin/i2c_async_embassy.rs | 10 ---------- examples/rp23/src/bin/i2c_blocking.rs | 10 ---------- examples/rp23/src/bin/i2c_slave.rs | 10 ---------- examples/rp23/src/bin/interrupt.rs | 10 ---------- examples/rp23/src/bin/multicore.rs | 10 ---------- examples/rp23/src/bin/multiprio.rs | 10 ---------- examples/rp23/src/bin/otp.rs | 10 ---------- examples/rp23/src/bin/pio_async.rs | 10 ---------- examples/rp23/src/bin/pio_dma.rs | 10 ---------- examples/rp23/src/bin/pio_hd44780.rs | 10 ---------- examples/rp23/src/bin/pio_i2s.rs | 10 ---------- examples/rp23/src/bin/pio_pwm.rs | 10 ---------- examples/rp23/src/bin/pio_rotary_encoder.rs | 10 ---------- examples/rp23/src/bin/pio_servo.rs | 10 ---------- examples/rp23/src/bin/pio_stepper.rs | 10 ---------- examples/rp23/src/bin/pio_ws2812.rs | 10 ---------- examples/rp23/src/bin/pwm.rs | 10 ---------- examples/rp23/src/bin/pwm_input.rs | 10 ---------- examples/rp23/src/bin/rosc.rs | 10 ---------- examples/rp23/src/bin/shared_bus.rs | 10 ---------- examples/rp23/src/bin/sharing.rs | 10 ---------- examples/rp23/src/bin/spi.rs | 10 ---------- examples/rp23/src/bin/spi_async.rs | 10 ---------- examples/rp23/src/bin/spi_display.rs | 10 ---------- examples/rp23/src/bin/spi_sdmmc.rs | 10 ---------- examples/rp23/src/bin/trng.rs | 10 ---------- examples/rp23/src/bin/uart.rs | 10 ---------- examples/rp23/src/bin/uart_buffered_split.rs | 10 ---------- examples/rp23/src/bin/uart_r503.rs | 10 ---------- examples/rp23/src/bin/uart_unidir.rs | 10 ---------- examples/rp23/src/bin/usb_webusb.rs | 10 ---------- examples/rp23/src/bin/watchdog.rs | 10 ---------- examples/rp23/src/bin/zerocopy.rs | 10 ---------- 45 files changed, 8 insertions(+), 445 deletions(-) diff --git a/examples/rp23/src/bin/adc.rs b/examples/rp23/src/bin/adc.rs index d1f053d39..f7db9653a 100644 --- a/examples/rp23/src/bin/adc.rs +++ b/examples/rp23/src/bin/adc.rs @@ -1,4 +1,4 @@ -//! This example test the ADC (Analog to Digital Conversion) of the RS2040 pin 26, 27 and 28. +//! This example test the ADC (Analog to Digital Conversion) of the RP2350A pins 26, 27 and 28. //! It also reads the temperature sensor in the chip. #![no_std] @@ -17,16 +17,6 @@ use {defmt_rtt as _, panic_probe as _}; #[used] pub static IMAGE_DEF: ImageDef = ImageDef::secure_exe(); -// Program metadata for `picotool info` -#[link_section = ".bi_entries"] -#[used] -pub static PICOTOOL_ENTRIES: [embassy_rp::binary_info::EntryAddr; 4] = [ - embassy_rp::binary_info::rp_program_name!(c"example"), - embassy_rp::binary_info::rp_cargo_version!(), - embassy_rp::binary_info::rp_program_description!(c"Blinky"), - embassy_rp::binary_info::rp_program_build_attribute!(), -]; - bind_interrupts!(struct Irqs { ADC_IRQ_FIFO => InterruptHandler; }); diff --git a/examples/rp23/src/bin/adc_dma.rs b/examples/rp23/src/bin/adc_dma.rs index 5046e5530..a6814c23a 100644 --- a/examples/rp23/src/bin/adc_dma.rs +++ b/examples/rp23/src/bin/adc_dma.rs @@ -17,16 +17,6 @@ use {defmt_rtt as _, panic_probe as _}; #[used] pub static IMAGE_DEF: ImageDef = ImageDef::secure_exe(); -// Program metadata for `picotool info` -#[link_section = ".bi_entries"] -#[used] -pub static PICOTOOL_ENTRIES: [embassy_rp::binary_info::EntryAddr; 4] = [ - embassy_rp::binary_info::rp_program_name!(c"example"), - embassy_rp::binary_info::rp_cargo_version!(), - embassy_rp::binary_info::rp_program_description!(c"Blinky"), - embassy_rp::binary_info::rp_program_build_attribute!(), -]; - bind_interrupts!(struct Irqs { ADC_IRQ_FIFO => InterruptHandler; }); diff --git a/examples/rp23/src/bin/assign_resources.rs b/examples/rp23/src/bin/assign_resources.rs index 2f9783917..0d4ad8dc3 100644 --- a/examples/rp23/src/bin/assign_resources.rs +++ b/examples/rp23/src/bin/assign_resources.rs @@ -24,16 +24,6 @@ use {defmt_rtt as _, panic_probe as _}; #[used] pub static IMAGE_DEF: ImageDef = ImageDef::secure_exe(); -// Program metadata for `picotool info` -#[link_section = ".bi_entries"] -#[used] -pub static PICOTOOL_ENTRIES: [embassy_rp::binary_info::EntryAddr; 4] = [ - embassy_rp::binary_info::rp_program_name!(c"example"), - embassy_rp::binary_info::rp_cargo_version!(), - embassy_rp::binary_info::rp_program_description!(c"Blinky"), - embassy_rp::binary_info::rp_program_build_attribute!(), -]; - #[embassy_executor::main] async fn main(spawner: Spawner) { // initialize the peripherals diff --git a/examples/rp23/src/bin/blinky.rs b/examples/rp23/src/bin/blinky.rs index 9e45679c8..c1ddbb7d2 100644 --- a/examples/rp23/src/bin/blinky.rs +++ b/examples/rp23/src/bin/blinky.rs @@ -17,20 +17,23 @@ use {defmt_rtt as _, panic_probe as _}; #[used] pub static IMAGE_DEF: ImageDef = ImageDef::secure_exe(); -// Program metadata for `picotool info` +// Program metadata for `picotool info`. +// This isn't needed, but it's recomended to have these minimal entries. #[link_section = ".bi_entries"] #[used] pub static PICOTOOL_ENTRIES: [embassy_rp::binary_info::EntryAddr; 4] = [ - embassy_rp::binary_info::rp_program_name!(c"example"), + embassy_rp::binary_info::rp_program_name!(c"Blinky Example"), + embassy_rp::binary_info::rp_program_description!( + c"This example tests the RP Pico on board LED, connected to gpio 25" + ), embassy_rp::binary_info::rp_cargo_version!(), - embassy_rp::binary_info::rp_program_description!(c"Blinky"), embassy_rp::binary_info::rp_program_build_attribute!(), ]; #[embassy_executor::main] async fn main(_spawner: Spawner) { let p = embassy_rp::init(Default::default()); - let mut led = Output::new(p.PIN_2, Level::Low); + let mut led = Output::new(p.PIN_25, Level::Low); loop { info!("led on!"); diff --git a/examples/rp23/src/bin/blinky_two_channels.rs b/examples/rp23/src/bin/blinky_two_channels.rs index 87fc58bbc..ce482858e 100644 --- a/examples/rp23/src/bin/blinky_two_channels.rs +++ b/examples/rp23/src/bin/blinky_two_channels.rs @@ -19,16 +19,6 @@ use {defmt_rtt as _, panic_probe as _}; #[used] pub static IMAGE_DEF: ImageDef = ImageDef::secure_exe(); -// Program metadata for `picotool info` -#[link_section = ".bi_entries"] -#[used] -pub static PICOTOOL_ENTRIES: [embassy_rp::binary_info::EntryAddr; 4] = [ - embassy_rp::binary_info::rp_program_name!(c"example"), - embassy_rp::binary_info::rp_cargo_version!(), - embassy_rp::binary_info::rp_program_description!(c"Blinky"), - embassy_rp::binary_info::rp_program_build_attribute!(), -]; - enum LedState { Toggle, } diff --git a/examples/rp23/src/bin/blinky_two_tasks.rs b/examples/rp23/src/bin/blinky_two_tasks.rs index 40236c53b..5dc62245d 100644 --- a/examples/rp23/src/bin/blinky_two_tasks.rs +++ b/examples/rp23/src/bin/blinky_two_tasks.rs @@ -19,16 +19,6 @@ use {defmt_rtt as _, panic_probe as _}; #[used] pub static IMAGE_DEF: ImageDef = ImageDef::secure_exe(); -// Program metadata for `picotool info` -#[link_section = ".bi_entries"] -#[used] -pub static PICOTOOL_ENTRIES: [embassy_rp::binary_info::EntryAddr; 4] = [ - embassy_rp::binary_info::rp_program_name!(c"example"), - embassy_rp::binary_info::rp_cargo_version!(), - embassy_rp::binary_info::rp_program_description!(c"Blinky"), - embassy_rp::binary_info::rp_program_build_attribute!(), -]; - type LedType = Mutex>>; static LED: LedType = Mutex::new(None); diff --git a/examples/rp23/src/bin/button.rs b/examples/rp23/src/bin/button.rs index fb067a370..85f1bcae3 100644 --- a/examples/rp23/src/bin/button.rs +++ b/examples/rp23/src/bin/button.rs @@ -14,16 +14,6 @@ use {defmt_rtt as _, panic_probe as _}; #[used] pub static IMAGE_DEF: ImageDef = ImageDef::secure_exe(); -// Program metadata for `picotool info` -#[link_section = ".bi_entries"] -#[used] -pub static PICOTOOL_ENTRIES: [embassy_rp::binary_info::EntryAddr; 4] = [ - embassy_rp::binary_info::rp_program_name!(c"example"), - embassy_rp::binary_info::rp_cargo_version!(), - embassy_rp::binary_info::rp_program_description!(c"Blinky"), - embassy_rp::binary_info::rp_program_build_attribute!(), -]; - #[embassy_executor::main] async fn main(_spawner: Spawner) { let p = embassy_rp::init(Default::default()); diff --git a/examples/rp23/src/bin/debounce.rs b/examples/rp23/src/bin/debounce.rs index e672521ec..4c8b80d92 100644 --- a/examples/rp23/src/bin/debounce.rs +++ b/examples/rp23/src/bin/debounce.rs @@ -15,16 +15,6 @@ use {defmt_rtt as _, panic_probe as _}; #[used] pub static IMAGE_DEF: ImageDef = ImageDef::secure_exe(); -// Program metadata for `picotool info` -#[link_section = ".bi_entries"] -#[used] -pub static PICOTOOL_ENTRIES: [embassy_rp::binary_info::EntryAddr; 4] = [ - embassy_rp::binary_info::rp_program_name!(c"example"), - embassy_rp::binary_info::rp_cargo_version!(), - embassy_rp::binary_info::rp_program_description!(c"Blinky"), - embassy_rp::binary_info::rp_program_build_attribute!(), -]; - pub struct Debouncer<'a> { input: Input<'a>, debounce: Duration, diff --git a/examples/rp23/src/bin/flash.rs b/examples/rp23/src/bin/flash.rs index 84011e394..28dec24c9 100644 --- a/examples/rp23/src/bin/flash.rs +++ b/examples/rp23/src/bin/flash.rs @@ -15,16 +15,6 @@ use {defmt_rtt as _, panic_probe as _}; #[used] pub static IMAGE_DEF: ImageDef = ImageDef::secure_exe(); -// Program metadata for `picotool info` -#[link_section = ".bi_entries"] -#[used] -pub static PICOTOOL_ENTRIES: [embassy_rp::binary_info::EntryAddr; 4] = [ - embassy_rp::binary_info::rp_program_name!(c"example"), - embassy_rp::binary_info::rp_cargo_version!(), - embassy_rp::binary_info::rp_program_description!(c"Flash"), - embassy_rp::binary_info::rp_program_build_attribute!(), -]; - const ADDR_OFFSET: u32 = 0x100000; const FLASH_SIZE: usize = 2 * 1024 * 1024; diff --git a/examples/rp23/src/bin/gpio_async.rs b/examples/rp23/src/bin/gpio_async.rs index ff12367bf..bfb9a3f95 100644 --- a/examples/rp23/src/bin/gpio_async.rs +++ b/examples/rp23/src/bin/gpio_async.rs @@ -17,16 +17,6 @@ use {defmt_rtt as _, panic_probe as _}; #[used] pub static IMAGE_DEF: ImageDef = ImageDef::secure_exe(); -// Program metadata for `picotool info` -#[link_section = ".bi_entries"] -#[used] -pub static PICOTOOL_ENTRIES: [embassy_rp::binary_info::EntryAddr; 4] = [ - embassy_rp::binary_info::rp_program_name!(c"example"), - embassy_rp::binary_info::rp_cargo_version!(), - embassy_rp::binary_info::rp_program_description!(c"Blinky"), - embassy_rp::binary_info::rp_program_build_attribute!(), -]; - /// It requires an external signal to be manually triggered on PIN 16. For /// example, this could be accomplished using an external power source with a /// button so that it is possible to toggle the signal from low to high. diff --git a/examples/rp23/src/bin/gpout.rs b/examples/rp23/src/bin/gpout.rs index d2ee55197..3cc2ea938 100644 --- a/examples/rp23/src/bin/gpout.rs +++ b/examples/rp23/src/bin/gpout.rs @@ -16,16 +16,6 @@ use {defmt_rtt as _, panic_probe as _}; #[used] pub static IMAGE_DEF: ImageDef = ImageDef::secure_exe(); -// Program metadata for `picotool info` -#[link_section = ".bi_entries"] -#[used] -pub static PICOTOOL_ENTRIES: [embassy_rp::binary_info::EntryAddr; 4] = [ - embassy_rp::binary_info::rp_program_name!(c"example"), - embassy_rp::binary_info::rp_cargo_version!(), - embassy_rp::binary_info::rp_program_description!(c"Blinky"), - embassy_rp::binary_info::rp_program_build_attribute!(), -]; - #[embassy_executor::main] async fn main(_spawner: Spawner) { let p = embassy_rp::init(Default::default()); diff --git a/examples/rp23/src/bin/i2c_async.rs b/examples/rp23/src/bin/i2c_async.rs index c8d918b56..b30088bae 100644 --- a/examples/rp23/src/bin/i2c_async.rs +++ b/examples/rp23/src/bin/i2c_async.rs @@ -20,16 +20,6 @@ use {defmt_rtt as _, panic_probe as _}; #[used] pub static IMAGE_DEF: ImageDef = ImageDef::secure_exe(); -// Program metadata for `picotool info` -#[link_section = ".bi_entries"] -#[used] -pub static PICOTOOL_ENTRIES: [embassy_rp::binary_info::EntryAddr; 4] = [ - embassy_rp::binary_info::rp_program_name!(c"example"), - embassy_rp::binary_info::rp_cargo_version!(), - embassy_rp::binary_info::rp_program_description!(c"Blinky"), - embassy_rp::binary_info::rp_program_build_attribute!(), -]; - bind_interrupts!(struct Irqs { I2C1_IRQ => InterruptHandler; }); diff --git a/examples/rp23/src/bin/i2c_async_embassy.rs b/examples/rp23/src/bin/i2c_async_embassy.rs index cce0abcde..c783a80c5 100644 --- a/examples/rp23/src/bin/i2c_async_embassy.rs +++ b/examples/rp23/src/bin/i2c_async_embassy.rs @@ -15,16 +15,6 @@ use {defmt_rtt as _, panic_probe as _}; #[used] pub static IMAGE_DEF: ImageDef = ImageDef::secure_exe(); -// Program metadata for `picotool info` -#[link_section = ".bi_entries"] -#[used] -pub static PICOTOOL_ENTRIES: [embassy_rp::binary_info::EntryAddr; 4] = [ - embassy_rp::binary_info::rp_program_name!(c"example"), - embassy_rp::binary_info::rp_cargo_version!(), - embassy_rp::binary_info::rp_program_description!(c"Blinky"), - embassy_rp::binary_info::rp_program_build_attribute!(), -]; - // Our anonymous hypotetical temperature sensor could be: // a 12-bit sensor, with 100ms startup time, range of -40*C - 125*C, and precision 0.25*C // It requires no configuration or calibration, works with all i2c bus speeds, diff --git a/examples/rp23/src/bin/i2c_blocking.rs b/examples/rp23/src/bin/i2c_blocking.rs index 85c33bf0d..a68677311 100644 --- a/examples/rp23/src/bin/i2c_blocking.rs +++ b/examples/rp23/src/bin/i2c_blocking.rs @@ -18,16 +18,6 @@ use {defmt_rtt as _, panic_probe as _}; #[used] pub static IMAGE_DEF: ImageDef = ImageDef::secure_exe(); -// Program metadata for `picotool info` -#[link_section = ".bi_entries"] -#[used] -pub static PICOTOOL_ENTRIES: [embassy_rp::binary_info::EntryAddr; 4] = [ - embassy_rp::binary_info::rp_program_name!(c"example"), - embassy_rp::binary_info::rp_cargo_version!(), - embassy_rp::binary_info::rp_program_description!(c"Blinky"), - embassy_rp::binary_info::rp_program_build_attribute!(), -]; - #[allow(dead_code)] mod mcp23017 { pub const ADDR: u8 = 0x20; // default addr diff --git a/examples/rp23/src/bin/i2c_slave.rs b/examples/rp23/src/bin/i2c_slave.rs index fb5f3cda1..8817538c0 100644 --- a/examples/rp23/src/bin/i2c_slave.rs +++ b/examples/rp23/src/bin/i2c_slave.rs @@ -15,16 +15,6 @@ use {defmt_rtt as _, panic_probe as _}; #[used] pub static IMAGE_DEF: ImageDef = ImageDef::secure_exe(); -// Program metadata for `picotool info` -#[link_section = ".bi_entries"] -#[used] -pub static PICOTOOL_ENTRIES: [embassy_rp::binary_info::EntryAddr; 4] = [ - embassy_rp::binary_info::rp_program_name!(c"example"), - embassy_rp::binary_info::rp_cargo_version!(), - embassy_rp::binary_info::rp_program_description!(c"Blinky"), - embassy_rp::binary_info::rp_program_build_attribute!(), -]; - bind_interrupts!(struct Irqs { I2C0_IRQ => i2c::InterruptHandler; I2C1_IRQ => i2c::InterruptHandler; diff --git a/examples/rp23/src/bin/interrupt.rs b/examples/rp23/src/bin/interrupt.rs index ee3d9bfe7..d9b662253 100644 --- a/examples/rp23/src/bin/interrupt.rs +++ b/examples/rp23/src/bin/interrupt.rs @@ -29,16 +29,6 @@ use {defmt_rtt as _, panic_probe as _}; #[used] pub static IMAGE_DEF: ImageDef = ImageDef::secure_exe(); -// Program metadata for `picotool info` -#[link_section = ".bi_entries"] -#[used] -pub static PICOTOOL_ENTRIES: [embassy_rp::binary_info::EntryAddr; 4] = [ - embassy_rp::binary_info::rp_program_name!(c"example"), - embassy_rp::binary_info::rp_cargo_version!(), - embassy_rp::binary_info::rp_program_description!(c"Blinky"), - embassy_rp::binary_info::rp_program_build_attribute!(), -]; - static COUNTER: AtomicU32 = AtomicU32::new(0); static PWM: Mutex>> = Mutex::new(RefCell::new(None)); static ADC: Mutex, adc::Channel)>>> = diff --git a/examples/rp23/src/bin/multicore.rs b/examples/rp23/src/bin/multicore.rs index 9ab43d7a5..d4d470fa2 100644 --- a/examples/rp23/src/bin/multicore.rs +++ b/examples/rp23/src/bin/multicore.rs @@ -20,16 +20,6 @@ use {defmt_rtt as _, panic_probe as _}; #[used] pub static IMAGE_DEF: ImageDef = ImageDef::secure_exe(); -// Program metadata for `picotool info` -#[link_section = ".bi_entries"] -#[used] -pub static PICOTOOL_ENTRIES: [embassy_rp::binary_info::EntryAddr; 4] = [ - embassy_rp::binary_info::rp_program_name!(c"example"), - embassy_rp::binary_info::rp_cargo_version!(), - embassy_rp::binary_info::rp_program_description!(c"Blinky"), - embassy_rp::binary_info::rp_program_build_attribute!(), -]; - static mut CORE1_STACK: Stack<4096> = Stack::new(); static EXECUTOR0: StaticCell = StaticCell::new(); static EXECUTOR1: StaticCell = StaticCell::new(); diff --git a/examples/rp23/src/bin/multiprio.rs b/examples/rp23/src/bin/multiprio.rs index 27cd3656e..787854aa9 100644 --- a/examples/rp23/src/bin/multiprio.rs +++ b/examples/rp23/src/bin/multiprio.rs @@ -70,16 +70,6 @@ use {defmt_rtt as _, panic_probe as _}; #[used] pub static IMAGE_DEF: ImageDef = ImageDef::secure_exe(); -// Program metadata for `picotool info` -#[link_section = ".bi_entries"] -#[used] -pub static PICOTOOL_ENTRIES: [embassy_rp::binary_info::EntryAddr; 4] = [ - embassy_rp::binary_info::rp_program_name!(c"example"), - embassy_rp::binary_info::rp_cargo_version!(), - embassy_rp::binary_info::rp_program_description!(c"Blinky"), - embassy_rp::binary_info::rp_program_build_attribute!(), -]; - #[embassy_executor::task] async fn run_high() { loop { diff --git a/examples/rp23/src/bin/otp.rs b/examples/rp23/src/bin/otp.rs index 106e514ca..c67c9821a 100644 --- a/examples/rp23/src/bin/otp.rs +++ b/examples/rp23/src/bin/otp.rs @@ -14,16 +14,6 @@ use {defmt_rtt as _, panic_probe as _}; #[used] pub static IMAGE_DEF: ImageDef = ImageDef::secure_exe(); -// Program metadata for `picotool info` -#[link_section = ".bi_entries"] -#[used] -pub static PICOTOOL_ENTRIES: [embassy_rp::binary_info::EntryAddr; 4] = [ - embassy_rp::binary_info::rp_program_name!(c"OTP Read Example"), - embassy_rp::binary_info::rp_cargo_version!(), - embassy_rp::binary_info::rp_program_description!(c"OTP Read Example"), - embassy_rp::binary_info::rp_program_build_attribute!(), -]; - #[embassy_executor::main] async fn main(_spawner: Spawner) { let _ = embassy_rp::init(Default::default()); diff --git a/examples/rp23/src/bin/pio_async.rs b/examples/rp23/src/bin/pio_async.rs index 231afc80e..896447e28 100644 --- a/examples/rp23/src/bin/pio_async.rs +++ b/examples/rp23/src/bin/pio_async.rs @@ -16,16 +16,6 @@ use {defmt_rtt as _, panic_probe as _}; #[used] pub static IMAGE_DEF: ImageDef = ImageDef::secure_exe(); -// Program metadata for `picotool info` -#[link_section = ".bi_entries"] -#[used] -pub static PICOTOOL_ENTRIES: [embassy_rp::binary_info::EntryAddr; 4] = [ - embassy_rp::binary_info::rp_program_name!(c"example"), - embassy_rp::binary_info::rp_cargo_version!(), - embassy_rp::binary_info::rp_program_description!(c"Blinky"), - embassy_rp::binary_info::rp_program_build_attribute!(), -]; - bind_interrupts!(struct Irqs { PIO0_IRQ_0 => InterruptHandler; }); diff --git a/examples/rp23/src/bin/pio_dma.rs b/examples/rp23/src/bin/pio_dma.rs index 60fbcb83a..b5f754798 100644 --- a/examples/rp23/src/bin/pio_dma.rs +++ b/examples/rp23/src/bin/pio_dma.rs @@ -17,16 +17,6 @@ use {defmt_rtt as _, panic_probe as _}; #[used] pub static IMAGE_DEF: ImageDef = ImageDef::secure_exe(); -// Program metadata for `picotool info` -#[link_section = ".bi_entries"] -#[used] -pub static PICOTOOL_ENTRIES: [embassy_rp::binary_info::EntryAddr; 4] = [ - embassy_rp::binary_info::rp_program_name!(c"example"), - embassy_rp::binary_info::rp_cargo_version!(), - embassy_rp::binary_info::rp_program_description!(c"Blinky"), - embassy_rp::binary_info::rp_program_build_attribute!(), -]; - bind_interrupts!(struct Irqs { PIO0_IRQ_0 => InterruptHandler; }); diff --git a/examples/rp23/src/bin/pio_hd44780.rs b/examples/rp23/src/bin/pio_hd44780.rs index 92aa858f9..5a6d7a9c5 100644 --- a/examples/rp23/src/bin/pio_hd44780.rs +++ b/examples/rp23/src/bin/pio_hd44780.rs @@ -22,16 +22,6 @@ use {defmt_rtt as _, panic_probe as _}; #[used] pub static IMAGE_DEF: ImageDef = ImageDef::secure_exe(); -// Program metadata for `picotool info` -#[link_section = ".bi_entries"] -#[used] -pub static PICOTOOL_ENTRIES: [embassy_rp::binary_info::EntryAddr; 4] = [ - embassy_rp::binary_info::rp_program_name!(c"example"), - embassy_rp::binary_info::rp_cargo_version!(), - embassy_rp::binary_info::rp_program_description!(c"Blinky"), - embassy_rp::binary_info::rp_program_build_attribute!(), -]; - bind_interrupts!(pub struct Irqs { PIO0_IRQ_0 => InterruptHandler; }); diff --git a/examples/rp23/src/bin/pio_i2s.rs b/examples/rp23/src/bin/pio_i2s.rs index d6d2d0ade..46e5eac88 100644 --- a/examples/rp23/src/bin/pio_i2s.rs +++ b/examples/rp23/src/bin/pio_i2s.rs @@ -25,16 +25,6 @@ use {defmt_rtt as _, panic_probe as _}; #[used] pub static IMAGE_DEF: ImageDef = ImageDef::secure_exe(); -// Program metadata for `picotool info` -#[link_section = ".bi_entries"] -#[used] -pub static PICOTOOL_ENTRIES: [embassy_rp::binary_info::EntryAddr; 4] = [ - embassy_rp::binary_info::rp_program_name!(c"example"), - embassy_rp::binary_info::rp_cargo_version!(), - embassy_rp::binary_info::rp_program_description!(c"Blinky"), - embassy_rp::binary_info::rp_program_build_attribute!(), -]; - bind_interrupts!(struct Irqs { PIO0_IRQ_0 => InterruptHandler; }); diff --git a/examples/rp23/src/bin/pio_pwm.rs b/examples/rp23/src/bin/pio_pwm.rs index 587f91ac3..3cffd213d 100644 --- a/examples/rp23/src/bin/pio_pwm.rs +++ b/examples/rp23/src/bin/pio_pwm.rs @@ -18,16 +18,6 @@ use {defmt_rtt as _, panic_probe as _}; #[used] pub static IMAGE_DEF: ImageDef = ImageDef::secure_exe(); -// Program metadata for `picotool info` -#[link_section = ".bi_entries"] -#[used] -pub static PICOTOOL_ENTRIES: [embassy_rp::binary_info::EntryAddr; 4] = [ - embassy_rp::binary_info::rp_program_name!(c"example"), - embassy_rp::binary_info::rp_cargo_version!(), - embassy_rp::binary_info::rp_program_description!(c"Blinky"), - embassy_rp::binary_info::rp_program_build_attribute!(), -]; - const REFRESH_INTERVAL: u64 = 20000; bind_interrupts!(struct Irqs { diff --git a/examples/rp23/src/bin/pio_rotary_encoder.rs b/examples/rp23/src/bin/pio_rotary_encoder.rs index c147351e8..9542d63b7 100644 --- a/examples/rp23/src/bin/pio_rotary_encoder.rs +++ b/examples/rp23/src/bin/pio_rotary_encoder.rs @@ -17,16 +17,6 @@ use {defmt_rtt as _, panic_probe as _}; #[used] pub static IMAGE_DEF: ImageDef = ImageDef::secure_exe(); -// Program metadata for `picotool info` -#[link_section = ".bi_entries"] -#[used] -pub static PICOTOOL_ENTRIES: [embassy_rp::binary_info::EntryAddr; 4] = [ - embassy_rp::binary_info::rp_program_name!(c"example"), - embassy_rp::binary_info::rp_cargo_version!(), - embassy_rp::binary_info::rp_program_description!(c"Blinky"), - embassy_rp::binary_info::rp_program_build_attribute!(), -]; - bind_interrupts!(struct Irqs { PIO0_IRQ_0 => InterruptHandler; }); diff --git a/examples/rp23/src/bin/pio_servo.rs b/examples/rp23/src/bin/pio_servo.rs index 5e8714178..3202ab475 100644 --- a/examples/rp23/src/bin/pio_servo.rs +++ b/examples/rp23/src/bin/pio_servo.rs @@ -18,16 +18,6 @@ use {defmt_rtt as _, panic_probe as _}; #[used] pub static IMAGE_DEF: ImageDef = ImageDef::secure_exe(); -// Program metadata for `picotool info` -#[link_section = ".bi_entries"] -#[used] -pub static PICOTOOL_ENTRIES: [embassy_rp::binary_info::EntryAddr; 4] = [ - embassy_rp::binary_info::rp_program_name!(c"example"), - embassy_rp::binary_info::rp_cargo_version!(), - embassy_rp::binary_info::rp_program_description!(c"Blinky"), - embassy_rp::binary_info::rp_program_build_attribute!(), -]; - const DEFAULT_MIN_PULSE_WIDTH: u64 = 1000; // uncalibrated default, the shortest duty cycle sent to a servo const DEFAULT_MAX_PULSE_WIDTH: u64 = 2000; // uncalibrated default, the longest duty cycle sent to a servo const DEFAULT_MAX_DEGREE_ROTATION: u64 = 160; // 160 degrees is typical diff --git a/examples/rp23/src/bin/pio_stepper.rs b/examples/rp23/src/bin/pio_stepper.rs index 24785443b..5e87da6eb 100644 --- a/examples/rp23/src/bin/pio_stepper.rs +++ b/examples/rp23/src/bin/pio_stepper.rs @@ -21,16 +21,6 @@ use {defmt_rtt as _, panic_probe as _}; #[used] pub static IMAGE_DEF: ImageDef = ImageDef::secure_exe(); -// Program metadata for `picotool info` -#[link_section = ".bi_entries"] -#[used] -pub static PICOTOOL_ENTRIES: [embassy_rp::binary_info::EntryAddr; 4] = [ - embassy_rp::binary_info::rp_program_name!(c"example"), - embassy_rp::binary_info::rp_cargo_version!(), - embassy_rp::binary_info::rp_program_description!(c"Blinky"), - embassy_rp::binary_info::rp_program_build_attribute!(), -]; - bind_interrupts!(struct Irqs { PIO0_IRQ_0 => InterruptHandler; }); diff --git a/examples/rp23/src/bin/pio_ws2812.rs b/examples/rp23/src/bin/pio_ws2812.rs index 00fe5e396..1f1984c4d 100644 --- a/examples/rp23/src/bin/pio_ws2812.rs +++ b/examples/rp23/src/bin/pio_ws2812.rs @@ -23,16 +23,6 @@ use {defmt_rtt as _, panic_probe as _}; #[used] pub static IMAGE_DEF: ImageDef = ImageDef::secure_exe(); -// Program metadata for `picotool info` -#[link_section = ".bi_entries"] -#[used] -pub static PICOTOOL_ENTRIES: [embassy_rp::binary_info::EntryAddr; 4] = [ - embassy_rp::binary_info::rp_program_name!(c"example"), - embassy_rp::binary_info::rp_cargo_version!(), - embassy_rp::binary_info::rp_program_description!(c"Blinky"), - embassy_rp::binary_info::rp_program_build_attribute!(), -]; - bind_interrupts!(struct Irqs { PIO0_IRQ_0 => InterruptHandler; }); diff --git a/examples/rp23/src/bin/pwm.rs b/examples/rp23/src/bin/pwm.rs index bfc2c6f67..15eae09ee 100644 --- a/examples/rp23/src/bin/pwm.rs +++ b/examples/rp23/src/bin/pwm.rs @@ -16,16 +16,6 @@ use {defmt_rtt as _, panic_probe as _}; #[used] pub static IMAGE_DEF: ImageDef = ImageDef::secure_exe(); -// Program metadata for `picotool info` -#[link_section = ".bi_entries"] -#[used] -pub static PICOTOOL_ENTRIES: [embassy_rp::binary_info::EntryAddr; 4] = [ - embassy_rp::binary_info::rp_program_name!(c"example"), - embassy_rp::binary_info::rp_cargo_version!(), - embassy_rp::binary_info::rp_program_description!(c"Blinky"), - embassy_rp::binary_info::rp_program_build_attribute!(), -]; - #[embassy_executor::main] async fn main(_spawner: Spawner) { let p = embassy_rp::init(Default::default()); diff --git a/examples/rp23/src/bin/pwm_input.rs b/examples/rp23/src/bin/pwm_input.rs index b65f2778b..ef87fe8b5 100644 --- a/examples/rp23/src/bin/pwm_input.rs +++ b/examples/rp23/src/bin/pwm_input.rs @@ -15,16 +15,6 @@ use {defmt_rtt as _, panic_probe as _}; #[used] pub static IMAGE_DEF: ImageDef = ImageDef::secure_exe(); -// Program metadata for `picotool info` -#[link_section = ".bi_entries"] -#[used] -pub static PICOTOOL_ENTRIES: [embassy_rp::binary_info::EntryAddr; 4] = [ - embassy_rp::binary_info::rp_program_name!(c"example"), - embassy_rp::binary_info::rp_cargo_version!(), - embassy_rp::binary_info::rp_program_description!(c"Blinky"), - embassy_rp::binary_info::rp_program_build_attribute!(), -]; - #[embassy_executor::main] async fn main(_spawner: Spawner) { let p = embassy_rp::init(Default::default()); diff --git a/examples/rp23/src/bin/rosc.rs b/examples/rp23/src/bin/rosc.rs index f65b236b1..a096f0b7a 100644 --- a/examples/rp23/src/bin/rosc.rs +++ b/examples/rp23/src/bin/rosc.rs @@ -17,16 +17,6 @@ use {defmt_rtt as _, panic_probe as _}; #[used] pub static IMAGE_DEF: ImageDef = ImageDef::secure_exe(); -// Program metadata for `picotool info` -#[link_section = ".bi_entries"] -#[used] -pub static PICOTOOL_ENTRIES: [embassy_rp::binary_info::EntryAddr; 4] = [ - embassy_rp::binary_info::rp_program_name!(c"example"), - embassy_rp::binary_info::rp_cargo_version!(), - embassy_rp::binary_info::rp_program_description!(c"Blinky"), - embassy_rp::binary_info::rp_program_build_attribute!(), -]; - #[embassy_executor::main] async fn main(_spawner: Spawner) { let mut config = embassy_rp::config::Config::default(); diff --git a/examples/rp23/src/bin/shared_bus.rs b/examples/rp23/src/bin/shared_bus.rs index b3fde13e3..2151ccb56 100644 --- a/examples/rp23/src/bin/shared_bus.rs +++ b/examples/rp23/src/bin/shared_bus.rs @@ -23,16 +23,6 @@ use {defmt_rtt as _, panic_probe as _}; #[used] pub static IMAGE_DEF: ImageDef = ImageDef::secure_exe(); -// Program metadata for `picotool info` -#[link_section = ".bi_entries"] -#[used] -pub static PICOTOOL_ENTRIES: [embassy_rp::binary_info::EntryAddr; 4] = [ - embassy_rp::binary_info::rp_program_name!(c"example"), - embassy_rp::binary_info::rp_cargo_version!(), - embassy_rp::binary_info::rp_program_description!(c"Blinky"), - embassy_rp::binary_info::rp_program_build_attribute!(), -]; - type Spi1Bus = Mutex>; type I2c1Bus = Mutex>; diff --git a/examples/rp23/src/bin/sharing.rs b/examples/rp23/src/bin/sharing.rs index 4a3301cfd..68eb5d133 100644 --- a/examples/rp23/src/bin/sharing.rs +++ b/examples/rp23/src/bin/sharing.rs @@ -36,16 +36,6 @@ use {defmt_rtt as _, panic_probe as _}; #[used] pub static IMAGE_DEF: ImageDef = ImageDef::secure_exe(); -// Program metadata for `picotool info` -#[link_section = ".bi_entries"] -#[used] -pub static PICOTOOL_ENTRIES: [embassy_rp::binary_info::EntryAddr; 4] = [ - embassy_rp::binary_info::rp_program_name!(c"example"), - embassy_rp::binary_info::rp_cargo_version!(), - embassy_rp::binary_info::rp_program_description!(c"Blinky"), - embassy_rp::binary_info::rp_program_build_attribute!(), -]; - type UartAsyncMutex = mutex::Mutex>; struct MyType { diff --git a/examples/rp23/src/bin/spi.rs b/examples/rp23/src/bin/spi.rs index 924873e60..aacb8c7db 100644 --- a/examples/rp23/src/bin/spi.rs +++ b/examples/rp23/src/bin/spi.rs @@ -17,16 +17,6 @@ use {defmt_rtt as _, panic_probe as _}; #[used] pub static IMAGE_DEF: ImageDef = ImageDef::secure_exe(); -// Program metadata for `picotool info` -#[link_section = ".bi_entries"] -#[used] -pub static PICOTOOL_ENTRIES: [embassy_rp::binary_info::EntryAddr; 4] = [ - embassy_rp::binary_info::rp_program_name!(c"example"), - embassy_rp::binary_info::rp_cargo_version!(), - embassy_rp::binary_info::rp_program_description!(c"Blinky"), - embassy_rp::binary_info::rp_program_build_attribute!(), -]; - #[embassy_executor::main] async fn main(_spawner: Spawner) { let p = embassy_rp::init(Default::default()); diff --git a/examples/rp23/src/bin/spi_async.rs b/examples/rp23/src/bin/spi_async.rs index 4a74f991c..ac7f02fa8 100644 --- a/examples/rp23/src/bin/spi_async.rs +++ b/examples/rp23/src/bin/spi_async.rs @@ -15,16 +15,6 @@ use {defmt_rtt as _, panic_probe as _}; #[used] pub static IMAGE_DEF: ImageDef = ImageDef::secure_exe(); -// Program metadata for `picotool info` -#[link_section = ".bi_entries"] -#[used] -pub static PICOTOOL_ENTRIES: [embassy_rp::binary_info::EntryAddr; 4] = [ - embassy_rp::binary_info::rp_program_name!(c"example"), - embassy_rp::binary_info::rp_cargo_version!(), - embassy_rp::binary_info::rp_program_description!(c"Blinky"), - embassy_rp::binary_info::rp_program_build_attribute!(), -]; - #[embassy_executor::main] async fn main(_spawner: Spawner) { let p = embassy_rp::init(Default::default()); diff --git a/examples/rp23/src/bin/spi_display.rs b/examples/rp23/src/bin/spi_display.rs index 71dd84658..195db5a97 100644 --- a/examples/rp23/src/bin/spi_display.rs +++ b/examples/rp23/src/bin/spi_display.rs @@ -32,16 +32,6 @@ use {defmt_rtt as _, panic_probe as _}; #[used] pub static IMAGE_DEF: ImageDef = ImageDef::secure_exe(); -// Program metadata for `picotool info` -#[link_section = ".bi_entries"] -#[used] -pub static PICOTOOL_ENTRIES: [embassy_rp::binary_info::EntryAddr; 4] = [ - embassy_rp::binary_info::rp_program_name!(c"example"), - embassy_rp::binary_info::rp_cargo_version!(), - embassy_rp::binary_info::rp_program_description!(c"Blinky"), - embassy_rp::binary_info::rp_program_build_attribute!(), -]; - use crate::my_display_interface::SPIDeviceInterface; use crate::touch::Touch; diff --git a/examples/rp23/src/bin/spi_sdmmc.rs b/examples/rp23/src/bin/spi_sdmmc.rs index dabf41ab8..aa6b44ffa 100644 --- a/examples/rp23/src/bin/spi_sdmmc.rs +++ b/examples/rp23/src/bin/spi_sdmmc.rs @@ -21,16 +21,6 @@ use {defmt_rtt as _, panic_probe as _}; #[used] pub static IMAGE_DEF: ImageDef = ImageDef::secure_exe(); -// Program metadata for `picotool info` -#[link_section = ".bi_entries"] -#[used] -pub static PICOTOOL_ENTRIES: [embassy_rp::binary_info::EntryAddr; 4] = [ - embassy_rp::binary_info::rp_program_name!(c"example"), - embassy_rp::binary_info::rp_cargo_version!(), - embassy_rp::binary_info::rp_program_description!(c"Blinky"), - embassy_rp::binary_info::rp_program_build_attribute!(), -]; - struct DummyTimesource(); impl embedded_sdmmc::TimeSource for DummyTimesource { diff --git a/examples/rp23/src/bin/trng.rs b/examples/rp23/src/bin/trng.rs index e146baa2e..8251ebd8b 100644 --- a/examples/rp23/src/bin/trng.rs +++ b/examples/rp23/src/bin/trng.rs @@ -18,16 +18,6 @@ use {defmt_rtt as _, panic_probe as _}; #[used] pub static IMAGE_DEF: ImageDef = ImageDef::secure_exe(); -// Program metadata for `picotool info` -#[link_section = ".bi_entries"] -#[used] -pub static PICOTOOL_ENTRIES: [embassy_rp::binary_info::EntryAddr; 4] = [ - embassy_rp::binary_info::rp_program_name!(c"example"), - embassy_rp::binary_info::rp_cargo_version!(), - embassy_rp::binary_info::rp_program_description!(c"Blinky"), - embassy_rp::binary_info::rp_program_build_attribute!(), -]; - bind_interrupts!(struct Irqs { TRNG_IRQ => embassy_rp::trng::InterruptHandler; }); diff --git a/examples/rp23/src/bin/uart.rs b/examples/rp23/src/bin/uart.rs index 0ffe0b293..fe28bb046 100644 --- a/examples/rp23/src/bin/uart.rs +++ b/examples/rp23/src/bin/uart.rs @@ -16,16 +16,6 @@ use {defmt_rtt as _, panic_probe as _}; #[used] pub static IMAGE_DEF: ImageDef = ImageDef::secure_exe(); -// Program metadata for `picotool info` -#[link_section = ".bi_entries"] -#[used] -pub static PICOTOOL_ENTRIES: [embassy_rp::binary_info::EntryAddr; 4] = [ - embassy_rp::binary_info::rp_program_name!(c"example"), - embassy_rp::binary_info::rp_cargo_version!(), - embassy_rp::binary_info::rp_program_description!(c"Blinky"), - embassy_rp::binary_info::rp_program_build_attribute!(), -]; - #[embassy_executor::main] async fn main(_spawner: Spawner) { let p = embassy_rp::init(Default::default()); diff --git a/examples/rp23/src/bin/uart_buffered_split.rs b/examples/rp23/src/bin/uart_buffered_split.rs index 4e69a20c4..9ed130727 100644 --- a/examples/rp23/src/bin/uart_buffered_split.rs +++ b/examples/rp23/src/bin/uart_buffered_split.rs @@ -22,16 +22,6 @@ use {defmt_rtt as _, panic_probe as _}; #[used] pub static IMAGE_DEF: ImageDef = ImageDef::secure_exe(); -// Program metadata for `picotool info` -#[link_section = ".bi_entries"] -#[used] -pub static PICOTOOL_ENTRIES: [embassy_rp::binary_info::EntryAddr; 4] = [ - embassy_rp::binary_info::rp_program_name!(c"example"), - embassy_rp::binary_info::rp_cargo_version!(), - embassy_rp::binary_info::rp_program_description!(c"Blinky"), - embassy_rp::binary_info::rp_program_build_attribute!(), -]; - bind_interrupts!(struct Irqs { UART0_IRQ => BufferedInterruptHandler; }); diff --git a/examples/rp23/src/bin/uart_r503.rs b/examples/rp23/src/bin/uart_r503.rs index 5ac8839e3..9aed42785 100644 --- a/examples/rp23/src/bin/uart_r503.rs +++ b/examples/rp23/src/bin/uart_r503.rs @@ -15,16 +15,6 @@ use {defmt_rtt as _, panic_probe as _}; #[used] pub static IMAGE_DEF: ImageDef = ImageDef::secure_exe(); -// Program metadata for `picotool info` -#[link_section = ".bi_entries"] -#[used] -pub static PICOTOOL_ENTRIES: [embassy_rp::binary_info::EntryAddr; 4] = [ - embassy_rp::binary_info::rp_program_name!(c"example"), - embassy_rp::binary_info::rp_cargo_version!(), - embassy_rp::binary_info::rp_program_description!(c"Blinky"), - embassy_rp::binary_info::rp_program_build_attribute!(), -]; - bind_interrupts!(pub struct Irqs { UART0_IRQ => UARTInterruptHandler; }); diff --git a/examples/rp23/src/bin/uart_unidir.rs b/examples/rp23/src/bin/uart_unidir.rs index 988e44a79..12214c4c2 100644 --- a/examples/rp23/src/bin/uart_unidir.rs +++ b/examples/rp23/src/bin/uart_unidir.rs @@ -21,16 +21,6 @@ use {defmt_rtt as _, panic_probe as _}; #[used] pub static IMAGE_DEF: ImageDef = ImageDef::secure_exe(); -// Program metadata for `picotool info` -#[link_section = ".bi_entries"] -#[used] -pub static PICOTOOL_ENTRIES: [embassy_rp::binary_info::EntryAddr; 4] = [ - embassy_rp::binary_info::rp_program_name!(c"example"), - embassy_rp::binary_info::rp_cargo_version!(), - embassy_rp::binary_info::rp_program_description!(c"Blinky"), - embassy_rp::binary_info::rp_program_build_attribute!(), -]; - bind_interrupts!(struct Irqs { UART1_IRQ => InterruptHandler; }); diff --git a/examples/rp23/src/bin/usb_webusb.rs b/examples/rp23/src/bin/usb_webusb.rs index 3ade2226b..15279cabc 100644 --- a/examples/rp23/src/bin/usb_webusb.rs +++ b/examples/rp23/src/bin/usb_webusb.rs @@ -34,16 +34,6 @@ use {defmt_rtt as _, panic_probe as _}; #[used] pub static IMAGE_DEF: ImageDef = ImageDef::secure_exe(); -// Program metadata for `picotool info` -#[link_section = ".bi_entries"] -#[used] -pub static PICOTOOL_ENTRIES: [embassy_rp::binary_info::EntryAddr; 4] = [ - embassy_rp::binary_info::rp_program_name!(c"example"), - embassy_rp::binary_info::rp_cargo_version!(), - embassy_rp::binary_info::rp_program_description!(c"Blinky"), - embassy_rp::binary_info::rp_program_build_attribute!(), -]; - bind_interrupts!(struct Irqs { USBCTRL_IRQ => InterruptHandler; }); diff --git a/examples/rp23/src/bin/watchdog.rs b/examples/rp23/src/bin/watchdog.rs index a901c1164..efc24c4e3 100644 --- a/examples/rp23/src/bin/watchdog.rs +++ b/examples/rp23/src/bin/watchdog.rs @@ -18,16 +18,6 @@ use {defmt_rtt as _, panic_probe as _}; #[used] pub static IMAGE_DEF: ImageDef = ImageDef::secure_exe(); -// Program metadata for `picotool info` -#[link_section = ".bi_entries"] -#[used] -pub static PICOTOOL_ENTRIES: [embassy_rp::binary_info::EntryAddr; 4] = [ - embassy_rp::binary_info::rp_program_name!(c"example"), - embassy_rp::binary_info::rp_cargo_version!(), - embassy_rp::binary_info::rp_program_description!(c"Blinky"), - embassy_rp::binary_info::rp_program_build_attribute!(), -]; - #[embassy_executor::main] async fn main(_spawner: Spawner) { let p = embassy_rp::init(Default::default()); diff --git a/examples/rp23/src/bin/zerocopy.rs b/examples/rp23/src/bin/zerocopy.rs index 86fca6f12..d317c4b56 100644 --- a/examples/rp23/src/bin/zerocopy.rs +++ b/examples/rp23/src/bin/zerocopy.rs @@ -23,16 +23,6 @@ use {defmt_rtt as _, panic_probe as _}; #[used] pub static IMAGE_DEF: ImageDef = ImageDef::secure_exe(); -// Program metadata for `picotool info` -#[link_section = ".bi_entries"] -#[used] -pub static PICOTOOL_ENTRIES: [embassy_rp::binary_info::EntryAddr; 4] = [ - embassy_rp::binary_info::rp_program_name!(c"example"), - embassy_rp::binary_info::rp_cargo_version!(), - embassy_rp::binary_info::rp_program_description!(c"Blinky"), - embassy_rp::binary_info::rp_program_build_attribute!(), -]; - type SampleBuffer = [u16; 512]; bind_interrupts!(struct Irqs { From ce701c3e8ef5f3cd354b74c1a06b6d77a9e812c6 Mon Sep 17 00:00:00 2001 From: Paul Fornage <36117326+paulwrath1223@users.noreply.github.com> Date: Wed, 2 Oct 2024 13:35:59 -0700 Subject: [PATCH 037/361] Fixed overflow on `pio_stepper.rs` --- examples/rp/src/bin/pio_stepper.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/rp/src/bin/pio_stepper.rs b/examples/rp/src/bin/pio_stepper.rs index 4952f4fbd..6ee45a414 100644 --- a/examples/rp/src/bin/pio_stepper.rs +++ b/examples/rp/src/bin/pio_stepper.rs @@ -154,7 +154,7 @@ async fn main(_spawner: Spawner) { stepper.step(1000).await; info!("CCW full steps, drop after 1 sec"); - if let Err(_) = with_timeout(Duration::from_secs(1), stepper.step(i32::MIN)).await { + if let Err(_) = with_timeout(Duration::from_secs(1), stepper.step(-i32::MAX)).await { info!("Time's up!"); Timer::after(Duration::from_secs(1)).await; } From b73b3f2da0940907dfc39bec62ff33c976874017 Mon Sep 17 00:00:00 2001 From: Sebastian Quilitz Date: Sat, 5 Oct 2024 12:18:33 +0200 Subject: [PATCH 038/361] rp: Run RP235x at 150 MHz instead of 125 --- embassy-rp/src/clocks.rs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/embassy-rp/src/clocks.rs b/embassy-rp/src/clocks.rs index f229a5acd..e82beb0f1 100644 --- a/embassy-rp/src/clocks.rs +++ b/embassy-rp/src/clocks.rs @@ -109,7 +109,10 @@ impl ClockConfig { sys_pll: Some(PllConfig { refdiv: 1, fbdiv: 125, + #[cfg(feature = "rp2040")] post_div1: 6, + #[cfg(feature = "_rp235x")] + post_div1: 5, post_div2: 2, }), usb_pll: Some(PllConfig { From d643d50f41cc6091eb573e2845fde48eeed9be4c Mon Sep 17 00:00:00 2001 From: rafael Date: Sat, 5 Oct 2024 12:24:17 +0200 Subject: [PATCH 039/361] Add Watch to embassy-sync README --- embassy-sync/README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/embassy-sync/README.md b/embassy-sync/README.md index 97a663d6d..3dcd9dcc8 100644 --- a/embassy-sync/README.md +++ b/embassy-sync/README.md @@ -8,6 +8,7 @@ Synchronization primitives and data structures with async support: - [`PriorityChannel`](channel::priority_channel::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. +- [`Watch`](watch::Watch) - Signalling latest value to multiple consumers. - [`Mutex`](mutex::Mutex) - Mutex for synchronizing state between asynchronous tasks. - [`Pipe`](pipe::Pipe) - Byte stream implementing `embedded_io` traits. - [`WakerRegistration`](waitqueue::WakerRegistration) - Utility to register and wake a `Waker`. From 383ad72b63b11ed1fc50ad5803534ac69996aff6 Mon Sep 17 00:00:00 2001 From: Oliver Rockstedt Date: Sat, 5 Oct 2024 13:39:27 +0200 Subject: [PATCH 040/361] embassy-sync: add clear, len, is_empty and is_full functions to zerocopy_channel --- embassy-sync/CHANGELOG.md | 1 + embassy-sync/src/zerocopy_channel.rs | 76 ++++++++++++++++++++++++++++ 2 files changed, 77 insertions(+) diff --git a/embassy-sync/CHANGELOG.md b/embassy-sync/CHANGELOG.md index 8f2d26fe0..8847e7e88 100644 --- a/embassy-sync/CHANGELOG.md +++ b/embassy-sync/CHANGELOG.md @@ -8,6 +8,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## Unreleased - Add LazyLock sync primitive. +- Add `clear`, `len`, `is_empty` and `is_full` functions to `zerocopy_channel`. ## 0.6.0 - 2024-05-29 diff --git a/embassy-sync/src/zerocopy_channel.rs b/embassy-sync/src/zerocopy_channel.rs index cfce9a571..a2c763294 100644 --- a/embassy-sync/src/zerocopy_channel.rs +++ b/embassy-sync/src/zerocopy_channel.rs @@ -70,6 +70,28 @@ impl<'a, M: RawMutex, T> Channel<'a, M, T> { pub fn split(&mut self) -> (Sender<'_, M, T>, Receiver<'_, M, T>) { (Sender { channel: self }, Receiver { channel: self }) } + + /// Clears all elements in the channel. + pub fn clear(&mut self) { + self.state.lock(|s| { + s.borrow_mut().clear(); + }); + } + + /// Returns the number of elements currently in the channel. + pub fn len(&self) -> usize { + self.state.lock(|s| s.borrow().len()) + } + + /// Returns whether the channel is empty. + pub fn is_empty(&self) -> bool { + self.state.lock(|s| s.borrow().is_empty()) + } + + /// Returns whether the channel is full. + pub fn is_full(&self) -> bool { + self.state.lock(|s| s.borrow().is_full()) + } } /// Send-only access to a [`Channel`]. @@ -130,6 +152,28 @@ impl<'a, M: RawMutex, T> Sender<'a, M, T> { pub fn send_done(&mut self) { self.channel.state.lock(|s| s.borrow_mut().push_done()) } + + /// Clears all elements in the channel. + pub fn clear(&mut self) { + self.channel.state.lock(|s| { + s.borrow_mut().clear(); + }); + } + + /// Returns the number of elements currently in the channel. + pub fn len(&self) -> usize { + self.channel.state.lock(|s| s.borrow().len()) + } + + /// Returns whether the channel is empty. + pub fn is_empty(&self) -> bool { + self.channel.state.lock(|s| s.borrow().is_empty()) + } + + /// Returns whether the channel is full. + pub fn is_full(&self) -> bool { + self.channel.state.lock(|s| s.borrow().is_full()) + } } /// Receive-only access to a [`Channel`]. @@ -190,6 +234,28 @@ impl<'a, M: RawMutex, T> Receiver<'a, M, T> { pub fn receive_done(&mut self) { self.channel.state.lock(|s| s.borrow_mut().pop_done()) } + + /// Clears all elements in the channel. + pub fn clear(&mut self) { + self.channel.state.lock(|s| { + s.borrow_mut().clear(); + }); + } + + /// Returns the number of elements currently in the channel. + pub fn len(&self) -> usize { + self.channel.state.lock(|s| s.borrow().len()) + } + + /// Returns whether the channel is empty. + pub fn is_empty(&self) -> bool { + self.channel.state.lock(|s| s.borrow().is_empty()) + } + + /// Returns whether the channel is full. + pub fn is_full(&self) -> bool { + self.channel.state.lock(|s| s.borrow().is_full()) + } } struct State { @@ -217,6 +283,16 @@ impl State { } } + fn clear(&mut self) { + self.front = 0; + self.back = 0; + self.full = false; + } + + fn len(&self) -> usize { + self.len + } + fn is_full(&self) -> bool { self.full } From 67836f955af133fa2ea7427172bbf352c51e4ab2 Mon Sep 17 00:00:00 2001 From: Chris Maniewski Date: Sat, 5 Oct 2024 14:16:00 +0200 Subject: [PATCH 041/361] docs: fix Sender/Receiver typo --- embassy-sync/src/watch.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/embassy-sync/src/watch.rs b/embassy-sync/src/watch.rs index 336e64ba9..59798d04f 100644 --- a/embassy-sync/src/watch.rs +++ b/embassy-sync/src/watch.rs @@ -310,12 +310,12 @@ impl Watch { } } - /// Create a new [`Receiver`] for the `Watch`. + /// Create a new [`Sender`] for the `Watch`. pub fn sender(&self) -> Sender<'_, M, T, N> { Sender(Snd::new(self)) } - /// Create a new [`DynReceiver`] for the `Watch`. + /// Create a new [`DynSender`] for the `Watch`. pub fn dyn_sender(&self) -> DynSender<'_, T> { DynSender(Snd::new(self)) } From f3ed0c60265c84ddcc11e4dea980bdc0b8343985 Mon Sep 17 00:00:00 2001 From: Oliver Rockstedt Date: Sun, 6 Oct 2024 17:39:47 +0200 Subject: [PATCH 042/361] embassy-sync: fix len calculation for zerocopy_channel --- embassy-sync/src/zerocopy_channel.rs | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/embassy-sync/src/zerocopy_channel.rs b/embassy-sync/src/zerocopy_channel.rs index a2c763294..a669cbd09 100644 --- a/embassy-sync/src/zerocopy_channel.rs +++ b/embassy-sync/src/zerocopy_channel.rs @@ -290,7 +290,15 @@ impl State { } fn len(&self) -> usize { - self.len + if !self.full { + if self.back >= self.front { + self.back - self.front + } else { + self.len + self.back - self.front + } + } else { + self.len + } } fn is_full(&self) -> bool { From 12e6add058b1bbe69660717bdef3d414a04b8b19 Mon Sep 17 00:00:00 2001 From: Oliver Rockstedt Date: Sun, 6 Oct 2024 17:45:03 +0200 Subject: [PATCH 043/361] embassy-sync: renamed field len to capacity on zerocopy_channel state --- embassy-sync/src/zerocopy_channel.rs | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/embassy-sync/src/zerocopy_channel.rs b/embassy-sync/src/zerocopy_channel.rs index a669cbd09..fabb69bf6 100644 --- a/embassy-sync/src/zerocopy_channel.rs +++ b/embassy-sync/src/zerocopy_channel.rs @@ -53,7 +53,7 @@ impl<'a, M: RawMutex, T> Channel<'a, M, T> { buf: buf.as_mut_ptr(), phantom: PhantomData, state: Mutex::new(RefCell::new(State { - len, + capacity: len, front: 0, back: 0, full: false, @@ -259,7 +259,8 @@ impl<'a, M: RawMutex, T> Receiver<'a, M, T> { } struct State { - len: usize, + /// Maximum number of elements the channel can hold. + capacity: usize, /// Front index. Always 0..=(N-1) front: usize, @@ -276,7 +277,7 @@ struct State { impl State { fn increment(&self, i: usize) -> usize { - if i + 1 == self.len { + if i + 1 == self.capacity { 0 } else { i + 1 @@ -294,10 +295,10 @@ impl State { if self.back >= self.front { self.back - self.front } else { - self.len + self.back - self.front + self.capacity + self.back - self.front } } else { - self.len + self.capacity } } From f6155cf735678fa1e297baa4ace992af3a871ae7 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Sun, 6 Oct 2024 23:47:43 +0200 Subject: [PATCH 044/361] Update smoltcp, embedded-nal-async to use the `core::net` IP addr types. --- embassy-net-ppp/Cargo.toml | 2 +- embassy-net/Cargo.toml | 4 ++-- embassy-net/src/dns.rs | 17 ++++++++--------- embassy-net/src/tcp.rs | 12 ++++-------- examples/nrf9160/src/bin/modem_tcp_client.rs | 12 +++++------- examples/rp/Cargo.toml | 4 ++-- examples/rp23/Cargo.toml | 2 -- examples/std/src/bin/net_ppp.rs | 6 +++--- examples/stm32h5/Cargo.toml | 2 +- examples/stm32h7/Cargo.toml | 2 +- examples/stm32h7/src/bin/eth_client.rs | 4 +++- examples/stm32h7/src/bin/eth_client_mii.rs | 4 +++- examples/stm32h755cm4/Cargo.toml | 2 +- examples/stm32h755cm7/Cargo.toml | 2 +- examples/stm32h7rs/Cargo.toml | 2 +- .../stm32l4/src/bin/spe_adin1110_http_server.rs | 2 +- 16 files changed, 37 insertions(+), 42 deletions(-) diff --git a/embassy-net-ppp/Cargo.toml b/embassy-net-ppp/Cargo.toml index f6371f955..d2f43ef45 100644 --- a/embassy-net-ppp/Cargo.toml +++ b/embassy-net-ppp/Cargo.toml @@ -20,7 +20,7 @@ log = { version = "0.4.14", optional = true } embedded-io-async = { version = "0.6.1" } embassy-net-driver-channel = { version = "0.3.0", path = "../embassy-net-driver-channel" } embassy-futures = { version = "0.1.0", path = "../embassy-futures" } -ppproto = { version = "0.1.2"} +ppproto = { version = "0.2.0"} embassy-sync = { version = "0.6.0", path = "../embassy-sync" } [package.metadata.embassy_docs] diff --git a/embassy-net/Cargo.toml b/embassy-net/Cargo.toml index 2e21b4231..a33c693fc 100644 --- a/embassy-net/Cargo.toml +++ b/embassy-net/Cargo.toml @@ -68,7 +68,7 @@ multicast = ["smoltcp/multicast"] defmt = { version = "0.3", optional = true } log = { version = "0.4.14", optional = true } -smoltcp = { git="https://github.com/smoltcp-rs/smoltcp", rev="dd43c8f189178b0ab3bda798ed8578b5b0a6f094", default-features = false, features = [ +smoltcp = { git="https://github.com/smoltcp-rs/smoltcp", rev="b65e1b64dc9b66fa984a2ad34e90685cb0b606de", default-features = false, features = [ "socket", "async", ] } @@ -80,5 +80,5 @@ embedded-io-async = { version = "0.6.1" } managed = { version = "0.8.0", default-features = false, features = [ "map" ] } heapless = { version = "0.8", default-features = false } -embedded-nal-async = { version = "0.7.1" } +embedded-nal-async = "0.8.0" document-features = "0.2.7" diff --git a/embassy-net/src/dns.rs b/embassy-net/src/dns.rs index 1fbaea4f0..dbe73776c 100644 --- a/embassy-net/src/dns.rs +++ b/embassy-net/src/dns.rs @@ -73,8 +73,11 @@ impl<'a> embedded_nal_async::Dns for DnsSocket<'a> { &self, host: &str, addr_type: embedded_nal_async::AddrType, - ) -> Result { - use embedded_nal_async::{AddrType, IpAddr}; + ) -> Result { + use core::net::IpAddr; + + use embedded_nal_async::AddrType; + let (qtype, secondary_qtype) = match addr_type { AddrType::IPv4 => (DnsQueryType::A, None), AddrType::IPv6 => (DnsQueryType::Aaaa, None), @@ -98,20 +101,16 @@ impl<'a> embedded_nal_async::Dns for DnsSocket<'a> { if let Some(first) = addrs.get(0) { Ok(match first { #[cfg(feature = "proto-ipv4")] - IpAddress::Ipv4(addr) => IpAddr::V4(addr.0.into()), + IpAddress::Ipv4(addr) => IpAddr::V4(*addr), #[cfg(feature = "proto-ipv6")] - IpAddress::Ipv6(addr) => IpAddr::V6(addr.0.into()), + IpAddress::Ipv6(addr) => IpAddr::V6(*addr), }) } else { Err(Error::Failed) } } - async fn get_host_by_address( - &self, - _addr: embedded_nal_async::IpAddr, - _result: &mut [u8], - ) -> Result { + async fn get_host_by_address(&self, _addr: core::net::IpAddr, _result: &mut [u8]) -> Result { todo!() } } diff --git a/embassy-net/src/tcp.rs b/embassy-net/src/tcp.rs index bcddbc95b..1bd582b65 100644 --- a/embassy-net/src/tcp.rs +++ b/embassy-net/src/tcp.rs @@ -675,10 +675,9 @@ mod embedded_io_impls { pub mod client { use core::cell::{Cell, UnsafeCell}; use core::mem::MaybeUninit; + use core::net::IpAddr; use core::ptr::NonNull; - use embedded_nal_async::IpAddr; - use super::*; /// TCP client connection pool compatible with `embedded-nal-async` traits. @@ -715,17 +714,14 @@ pub mod client { type Error = Error; type Connection<'m> = TcpConnection<'m, N, TX_SZ, RX_SZ> where Self: 'm; - async fn connect<'a>( - &'a self, - remote: embedded_nal_async::SocketAddr, - ) -> Result, Self::Error> { + async fn connect<'a>(&'a self, remote: core::net::SocketAddr) -> Result, Self::Error> { let addr: crate::IpAddress = match remote.ip() { #[cfg(feature = "proto-ipv4")] - IpAddr::V4(addr) => crate::IpAddress::Ipv4(crate::Ipv4Address::from_bytes(&addr.octets())), + IpAddr::V4(addr) => crate::IpAddress::Ipv4(addr), #[cfg(not(feature = "proto-ipv4"))] IpAddr::V4(_) => panic!("ipv4 support not enabled"), #[cfg(feature = "proto-ipv6")] - IpAddr::V6(addr) => crate::IpAddress::Ipv6(crate::Ipv6Address::from_bytes(&addr.octets())), + IpAddr::V6(addr) => crate::IpAddress::Ipv6(addr), #[cfg(not(feature = "proto-ipv6"))] IpAddr::V6(_) => panic!("ipv6 support not enabled"), }; diff --git a/examples/nrf9160/src/bin/modem_tcp_client.rs b/examples/nrf9160/src/bin/modem_tcp_client.rs index 495ee26dd..067ec4276 100644 --- a/examples/nrf9160/src/bin/modem_tcp_client.rs +++ b/examples/nrf9160/src/bin/modem_tcp_client.rs @@ -9,7 +9,7 @@ use core::str::FromStr; use defmt::{info, unwrap, warn}; use embassy_executor::Spawner; -use embassy_net::{Ipv4Address, Ipv4Cidr, Stack, StackResources}; +use embassy_net::{Ipv4Cidr, Stack, StackResources}; use embassy_net_nrf91::context::Status; use embassy_net_nrf91::{context, Runner, State, TraceBuffer, TraceReader}; use embassy_nrf::buffered_uarte::{self, BufferedUarteTx}; @@ -70,18 +70,16 @@ fn status_to_config(status: &Status) -> embassy_net::ConfigV4 { let Some(IpAddr::V4(addr)) = status.ip else { panic!("Unexpected IP address"); }; - let addr = Ipv4Address(addr.octets()); - let gateway = if let Some(IpAddr::V4(addr)) = status.gateway { - Some(Ipv4Address(addr.octets())) - } else { - None + let gateway = match status.gateway { + Some(IpAddr::V4(addr)) => Some(addr), + _ => None, }; let mut dns_servers = Vec::new(); for dns in status.dns.iter() { if let IpAddr::V4(ip) = dns { - unwrap!(dns_servers.push(Ipv4Address(ip.octets()))); + unwrap!(dns_servers.push(*ip)); } } diff --git a/examples/rp/Cargo.toml b/examples/rp/Cargo.toml index 04b4c6317..674d331ab 100644 --- a/examples/rp/Cargo.toml +++ b/examples/rp/Cargo.toml @@ -12,7 +12,7 @@ embassy-executor = { version = "0.6.0", path = "../../embassy-executor", feature embassy-time = { version = "0.3.2", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime"] } embassy-rp = { version = "0.2.0", path = "../../embassy-rp", features = ["defmt", "unstable-pac", "time-driver", "critical-section-impl", "rp2040"] } embassy-usb = { version = "0.3.0", path = "../../embassy-usb", features = ["defmt"] } -embassy-net = { version = "0.4.0", path = "../../embassy-net", features = ["defmt", "tcp", "udp", "raw", "dhcpv4", "medium-ethernet", "dns"] } +embassy-net = { version = "0.4.0", path = "../../embassy-net", features = ["defmt", "tcp", "udp", "raw", "dhcpv4", "medium-ethernet", "dns", "proto-ipv4", "proto-ipv6", "multicast"] } embassy-net-wiznet = { version = "0.1.0", path = "../../embassy-net-wiznet", features = ["defmt"] } embassy-futures = { version = "0.1.0", path = "../../embassy-futures" } embassy-usb-logger = { version = "0.2.0", path = "../../embassy-usb-logger" } @@ -25,7 +25,7 @@ fixed = "1.23.1" fixed-macro = "1.2" # for web request example -reqwless = { version = "0.12.0", features = ["defmt",]} +reqwless = { git="https://github.com/drogue-iot/reqwless", rev="673e8d2cfbaad79254ec51fa50cc8b697531fbff", features = ["defmt",]} serde = { version = "1.0.203", default-features = false, features = ["derive"] } serde-json-core = "0.5.1" diff --git a/examples/rp23/Cargo.toml b/examples/rp23/Cargo.toml index 087f6fd69..08646463c 100644 --- a/examples/rp23/Cargo.toml +++ b/examples/rp23/Cargo.toml @@ -24,8 +24,6 @@ defmt-rtt = "0.4" fixed = "1.23.1" fixed-macro = "1.2" -# for web request example -reqwless = { version = "0.12.0", features = ["defmt",]} serde = { version = "1.0.203", default-features = false, features = ["derive"] } serde-json-core = "0.5.1" diff --git a/examples/std/src/bin/net_ppp.rs b/examples/std/src/bin/net_ppp.rs index 7d0f1327f..ea3fbebef 100644 --- a/examples/std/src/bin/net_ppp.rs +++ b/examples/std/src/bin/net_ppp.rs @@ -16,7 +16,7 @@ use async_io::Async; use clap::Parser; use embassy_executor::{Executor, Spawner}; use embassy_net::tcp::TcpSocket; -use embassy_net::{Config, ConfigV4, Ipv4Address, Ipv4Cidr, Stack, StackResources}; +use embassy_net::{Config, ConfigV4, Ipv4Cidr, Stack, StackResources}; use embassy_net_ppp::Runner; use embedded_io_async::Write; use futures::io::BufReader; @@ -60,10 +60,10 @@ async fn ppp_task(stack: Stack<'static>, mut runner: Runner<'static>, port: Seri }; let mut dns_servers = Vec::new(); for s in ipv4.dns_servers.iter().flatten() { - let _ = dns_servers.push(Ipv4Address::from_bytes(&s.0)); + let _ = dns_servers.push(*s); } let config = ConfigV4::Static(embassy_net::StaticConfigV4 { - address: Ipv4Cidr::new(Ipv4Address::from_bytes(&addr.0), 0), + address: Ipv4Cidr::new(addr, 0), gateway: None, dns_servers, }); diff --git a/examples/stm32h5/Cargo.toml b/examples/stm32h5/Cargo.toml index 30b1d2be9..1aa264ab2 100644 --- a/examples/stm32h5/Cargo.toml +++ b/examples/stm32h5/Cargo.toml @@ -23,7 +23,7 @@ embedded-hal = "0.2.6" embedded-hal-1 = { package = "embedded-hal", version = "1.0" } embedded-hal-async = { version = "1.0" } embedded-io-async = { version = "0.6.1" } -embedded-nal-async = { version = "0.7.1" } +embedded-nal-async = "0.8.0" panic-probe = { version = "0.3", features = ["print-defmt"] } heapless = { version = "0.8", default-features = false } rand_core = "0.6.3" diff --git a/examples/stm32h7/Cargo.toml b/examples/stm32h7/Cargo.toml index 13fce7dc7..d0f22cf82 100644 --- a/examples/stm32h7/Cargo.toml +++ b/examples/stm32h7/Cargo.toml @@ -23,7 +23,7 @@ cortex-m-rt = "0.7.0" embedded-hal = "0.2.6" embedded-hal-1 = { package = "embedded-hal", version = "1.0" } embedded-hal-async = { version = "1.0" } -embedded-nal-async = { version = "0.7.1" } +embedded-nal-async = "0.8.0" embedded-io-async = { version = "0.6.1" } panic-probe = { version = "0.3", features = ["print-defmt"] } heapless = { version = "0.8", default-features = false } diff --git a/examples/stm32h7/src/bin/eth_client.rs b/examples/stm32h7/src/bin/eth_client.rs index 24983ca85..a1558b079 100644 --- a/examples/stm32h7/src/bin/eth_client.rs +++ b/examples/stm32h7/src/bin/eth_client.rs @@ -1,6 +1,8 @@ #![no_std] #![no_main] +use core::net::{Ipv4Addr, SocketAddr, SocketAddrV4}; + use defmt::*; use embassy_executor::Spawner; use embassy_net::tcp::client::{TcpClient, TcpClientState}; @@ -12,7 +14,7 @@ use embassy_stm32::rng::Rng; use embassy_stm32::{bind_interrupts, eth, peripherals, rng, Config}; use embassy_time::Timer; use embedded_io_async::Write; -use embedded_nal_async::{Ipv4Addr, SocketAddr, SocketAddrV4, TcpConnect}; +use embedded_nal_async::TcpConnect; use rand_core::RngCore; use static_cell::StaticCell; use {defmt_rtt as _, panic_probe as _}; diff --git a/examples/stm32h7/src/bin/eth_client_mii.rs b/examples/stm32h7/src/bin/eth_client_mii.rs index 768d85993..a352ef444 100644 --- a/examples/stm32h7/src/bin/eth_client_mii.rs +++ b/examples/stm32h7/src/bin/eth_client_mii.rs @@ -1,6 +1,8 @@ #![no_std] #![no_main] +use core::net::{Ipv4Addr, SocketAddr, SocketAddrV4}; + use defmt::*; use embassy_executor::Spawner; use embassy_net::tcp::client::{TcpClient, TcpClientState}; @@ -12,7 +14,7 @@ use embassy_stm32::rng::Rng; use embassy_stm32::{bind_interrupts, eth, peripherals, rng, Config}; use embassy_time::Timer; use embedded_io_async::Write; -use embedded_nal_async::{Ipv4Addr, SocketAddr, SocketAddrV4, TcpConnect}; +use embedded_nal_async::TcpConnect; use rand_core::RngCore; use static_cell::StaticCell; use {defmt_rtt as _, panic_probe as _}; diff --git a/examples/stm32h755cm4/Cargo.toml b/examples/stm32h755cm4/Cargo.toml index 7a42fbdaa..75de40b9a 100644 --- a/examples/stm32h755cm4/Cargo.toml +++ b/examples/stm32h755cm4/Cargo.toml @@ -23,7 +23,7 @@ cortex-m-rt = "0.7.0" embedded-hal = "0.2.6" embedded-hal-1 = { package = "embedded-hal", version = "1.0" } embedded-hal-async = { version = "1.0" } -embedded-nal-async = { version = "0.7.1" } +embedded-nal-async = "0.8.0" embedded-io-async = { version = "0.6.1" } panic-probe = { version = "0.3", features = ["print-defmt"] } heapless = { version = "0.8", default-features = false } diff --git a/examples/stm32h755cm7/Cargo.toml b/examples/stm32h755cm7/Cargo.toml index 4f0f69c3f..911a4e79b 100644 --- a/examples/stm32h755cm7/Cargo.toml +++ b/examples/stm32h755cm7/Cargo.toml @@ -23,7 +23,7 @@ cortex-m-rt = "0.7.0" embedded-hal = "0.2.6" embedded-hal-1 = { package = "embedded-hal", version = "1.0" } embedded-hal-async = { version = "1.0" } -embedded-nal-async = { version = "0.7.1" } +embedded-nal-async = "0.8.0" embedded-io-async = { version = "0.6.1" } panic-probe = { version = "0.3", features = ["print-defmt"] } heapless = { version = "0.8", default-features = false } diff --git a/examples/stm32h7rs/Cargo.toml b/examples/stm32h7rs/Cargo.toml index f97dfd722..05f638408 100644 --- a/examples/stm32h7rs/Cargo.toml +++ b/examples/stm32h7rs/Cargo.toml @@ -22,7 +22,7 @@ cortex-m-rt = "0.7.0" embedded-hal = "0.2.6" embedded-hal-1 = { package = "embedded-hal", version = "1.0" } embedded-hal-async = { version = "1.0" } -embedded-nal-async = { version = "0.7.1" } +embedded-nal-async = "0.8.0" embedded-io-async = { version = "0.6.1" } panic-probe = { version = "0.3", features = ["print-defmt"] } heapless = { version = "0.8", default-features = false } diff --git a/examples/stm32l4/src/bin/spe_adin1110_http_server.rs b/examples/stm32l4/src/bin/spe_adin1110_http_server.rs index be4270ada..4a7c01f9f 100644 --- a/examples/stm32l4/src/bin/spe_adin1110_http_server.rs +++ b/examples/stm32l4/src/bin/spe_adin1110_http_server.rs @@ -51,7 +51,7 @@ bind_interrupts!(struct Irqs { // MAC-address used by the adin1110 const MAC: [u8; 6] = [0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff]; // Static IP settings -const IP_ADDRESS: Ipv4Cidr = Ipv4Cidr::new(Ipv4Address([192, 168, 1, 5]), 24); +const IP_ADDRESS: Ipv4Cidr = Ipv4Cidr::new(Ipv4Address::new(192, 168, 1, 5), 24); // Listen port for the webserver const HTTP_LISTEN_PORT: u16 = 80; From 9e6e09a8d747ec90aae215df8471dfe349993487 Mon Sep 17 00:00:00 2001 From: Dummyc0m Date: Sun, 6 Oct 2024 23:23:33 -0700 Subject: [PATCH 045/361] executor/spin: introduce an architecture agnostic executor Spin polls the raw executor and never sleeps. It is useful for disabling any power features associated with wfi/wfe-like instructions. When implementing support for the CH32V30x MCU, the wfi instruction had issues interacting with the USB OTG peripheral and appeared to be non-spec-compliant. 1. When sending a USB Data-in packet, the USB peripheral appears to be unable to read the system main memory while in WFI. This manifests in the USB peripheral sending all or partially zeroed DATA packets. Disabling WFI works around this issue. 2. The WFI instruction does not wake up the processor when MIE is disabled. The MCU provides a WFITOWFE bit to emulate the WFE instruction on arm, which, when enabled, ignores the MIE and allows the processor to wake up. This works around the non-compliant WFI implementation. Co-authored-by: Codetector Co-authored-by: Dummyc0m --- embassy-executor-macros/src/lib.rs | 29 +++++++++++ embassy-executor-macros/src/macros/main.rs | 29 ++++++++++- embassy-executor/Cargo.toml | 2 + embassy-executor/src/arch/spin.rs | 58 ++++++++++++++++++++++ embassy-executor/src/lib.rs | 10 +++- 5 files changed, 126 insertions(+), 2 deletions(-) create mode 100644 embassy-executor/src/arch/spin.rs diff --git a/embassy-executor-macros/src/lib.rs b/embassy-executor-macros/src/lib.rs index 5461fe04c..61d388b9e 100644 --- a/embassy-executor-macros/src/lib.rs +++ b/embassy-executor-macros/src/lib.rs @@ -94,6 +94,35 @@ pub fn main_cortex_m(args: TokenStream, item: TokenStream) -> TokenStream { main::run(&args.meta, f, main::cortex_m()).unwrap_or_else(|x| x).into() } +/// Creates a new `executor` instance and declares an architecture agnostic application entry point spawning +/// the corresponding function body as an async task. +/// +/// The following restrictions apply: +/// +/// * The function must accept exactly 1 parameter, an `embassy_executor::Spawner` handle that it can use to spawn additional tasks. +/// * The function must be declared `async`. +/// * The function must not use generics. +/// * Only a single `main` task may be declared. +/// +/// A user-defined entry macro must provided via the `entry` argument +/// +/// ## Examples +/// Spawning a task: +/// ``` rust +/// #[embassy_executor::main(entry = "qingke_rt::entry")] +/// async fn main(_s: embassy_executor::Spawner) { +/// // Function body +/// } +/// ``` +#[proc_macro_attribute] +pub fn main_spin(args: TokenStream, item: TokenStream) -> TokenStream { + let args = syn::parse_macro_input!(args as Args); + let f = syn::parse_macro_input!(item as syn::ItemFn); + main::run(&args.meta, f, main::spin(&args.meta)) + .unwrap_or_else(|x| x) + .into() +} + /// Creates a new `executor` instance and declares an application entry point for RISC-V spawning the corresponding function body as an async task. /// /// The following restrictions apply: diff --git a/embassy-executor-macros/src/macros/main.rs b/embassy-executor-macros/src/macros/main.rs index 26dfa2397..66a3965d0 100644 --- a/embassy-executor-macros/src/macros/main.rs +++ b/embassy-executor-macros/src/macros/main.rs @@ -1,5 +1,5 @@ use darling::export::NestedMeta; -use darling::FromMeta; +use darling::{Error, FromMeta}; use proc_macro2::TokenStream; use quote::quote; use syn::{Expr, ReturnType, Type}; @@ -50,6 +50,33 @@ pub fn riscv(args: &[NestedMeta]) -> TokenStream { } } +pub fn spin(args: &[NestedMeta]) -> TokenStream { + let maybe_entry = match Args::from_list(args) { + Ok(args) => args.entry, + Err(e) => return e.write_errors(), + }; + + let entry = match maybe_entry { + Some(str) => str, + None => return Error::missing_field("entry").write_errors(), + }; + let entry = match Expr::from_string(&entry) { + Ok(expr) => expr, + Err(e) => return e.write_errors(), + }; + + quote! { + #[#entry] + fn main() -> ! { + let mut executor = ::embassy_executor::Executor::new(); + let executor = unsafe { __make_static(&mut executor) }; + executor.run(|spawner| { + spawner.must_spawn(__embassy_main(spawner)); + }) + } + } +} + pub fn cortex_m() -> TokenStream { quote! { #[cortex_m_rt::entry] diff --git a/embassy-executor/Cargo.toml b/embassy-executor/Cargo.toml index 01fa28b88..e2fedce3c 100644 --- a/embassy-executor/Cargo.toml +++ b/embassy-executor/Cargo.toml @@ -82,6 +82,8 @@ arch-riscv32 = ["_arch"] arch-wasm = ["_arch", "dep:wasm-bindgen", "dep:js-sys", "critical-section/std"] ## AVR arch-avr = ["_arch", "dep:portable-atomic", "dep:avr-device"] +## spin (architecture agnostic; never sleeps) +arch-spin = ["_arch"] #! ### Executor diff --git a/embassy-executor/src/arch/spin.rs b/embassy-executor/src/arch/spin.rs new file mode 100644 index 000000000..340023620 --- /dev/null +++ b/embassy-executor/src/arch/spin.rs @@ -0,0 +1,58 @@ +#[cfg(feature = "executor-interrupt")] +compile_error!("`executor-interrupt` is not supported with `arch-spin`."); + +#[cfg(feature = "executor-thread")] +pub use thread::*; +#[cfg(feature = "executor-thread")] +mod thread { + use core::marker::PhantomData; + + pub use embassy_executor_macros::main_spin as main; + + use crate::{raw, Spawner}; + + #[export_name = "__pender"] + fn __pender(_context: *mut ()) {} + + /// Spin Executor + pub struct Executor { + inner: raw::Executor, + not_send: PhantomData<*mut ()>, + } + + impl Executor { + /// Create a new Executor. + pub fn new() -> Self { + Self { + inner: raw::Executor::new(core::ptr::null_mut()), + not_send: PhantomData, + } + } + + /// Run the executor. + /// + /// The `init` closure is called with a [`Spawner`] that spawns tasks on + /// this executor. Use it to spawn the initial task(s). After `init` returns, + /// the executor starts running the tasks. + /// + /// To spawn more tasks later, you may keep copies of the [`Spawner`] (it is `Copy`), + /// for example by passing it as an argument to the initial tasks. + /// + /// This function requires `&'static mut self`. This means you have to store the + /// Executor instance in a place where it'll live forever and grants you mutable + /// access. There's a few ways to do this: + /// + /// - a [StaticCell](https://docs.rs/static_cell/latest/static_cell/) (safe) + /// - a `static mut` (unsafe) + /// - a local variable in a function you know never returns (like `fn main() -> !`), upgrading its lifetime with `transmute`. (unsafe) + /// + /// This function never returns. + pub fn run(&'static mut self, init: impl FnOnce(Spawner)) -> ! { + init(self.inner.spawner()); + + loop { + unsafe { self.inner.poll() }; + } + } + } +} diff --git a/embassy-executor/src/lib.rs b/embassy-executor/src/lib.rs index 6a2e493a2..d816539ac 100644 --- a/embassy-executor/src/lib.rs +++ b/embassy-executor/src/lib.rs @@ -23,7 +23,14 @@ macro_rules! check_at_most_one { check_at_most_one!(@amo [$($f)*] [$($f)*] []); }; } -check_at_most_one!("arch-avr", "arch-cortex-m", "arch-riscv32", "arch-std", "arch-wasm",); +check_at_most_one!( + "arch-avr", + "arch-cortex-m", + "arch-riscv32", + "arch-std", + "arch-wasm", + "arch-spin", +); #[cfg(feature = "_arch")] #[cfg_attr(feature = "arch-avr", path = "arch/avr.rs")] @@ -31,6 +38,7 @@ check_at_most_one!("arch-avr", "arch-cortex-m", "arch-riscv32", "arch-std", "arc #[cfg_attr(feature = "arch-riscv32", path = "arch/riscv32.rs")] #[cfg_attr(feature = "arch-std", path = "arch/std.rs")] #[cfg_attr(feature = "arch-wasm", path = "arch/wasm.rs")] +#[cfg_attr(feature = "arch-spin", path = "arch/spin.rs")] mod arch; #[cfg(feature = "_arch")] From e7e245eeb77974bf1f374f24cbf5c0bc41f745f1 Mon Sep 17 00:00:00 2001 From: George Cosma Date: Wed, 5 Jun 2024 13:54:00 +0300 Subject: [PATCH 046/361] feat: embassy-lpc55 hal with gpio and pint driver --- ci.sh | 2 + embassy-nxp/Cargo.toml | 17 + embassy-nxp/src/gpio.rs | 361 +++++++++++++++ embassy-nxp/src/lib.rs | 95 ++++ embassy-nxp/src/pac_utils.rs | 323 ++++++++++++++ embassy-nxp/src/pint.rs | 440 +++++++++++++++++++ examples/lpc55s69/.cargo/config.toml | 8 + examples/lpc55s69/Cargo.toml | 22 + examples/lpc55s69/build.rs | 35 ++ examples/lpc55s69/memory.x | 28 ++ examples/lpc55s69/src/bin/blinky_nop.rs | 33 ++ examples/lpc55s69/src/bin/button_executor.rs | 25 ++ 12 files changed, 1389 insertions(+) create mode 100644 embassy-nxp/Cargo.toml create mode 100644 embassy-nxp/src/gpio.rs create mode 100644 embassy-nxp/src/lib.rs create mode 100644 embassy-nxp/src/pac_utils.rs create mode 100644 embassy-nxp/src/pint.rs create mode 100644 examples/lpc55s69/.cargo/config.toml create mode 100644 examples/lpc55s69/Cargo.toml create mode 100644 examples/lpc55s69/build.rs create mode 100644 examples/lpc55s69/memory.x create mode 100644 examples/lpc55s69/src/bin/blinky_nop.rs create mode 100644 examples/lpc55s69/src/bin/button_executor.rs diff --git a/ci.sh b/ci.sh index 8fef731a4..503f8c8dc 100755 --- a/ci.sh +++ b/ci.sh @@ -171,6 +171,7 @@ cargo batch \ --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv6m-none-eabi --features stm32u031r8,defmt,exti,time-driver-any,time \ --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv6m-none-eabi --features stm32u073mb,defmt,exti,time-driver-any,time \ --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv6m-none-eabi --features stm32u083rc,defmt,exti,time-driver-any,time \ + --- build --release --manifest-path embassy-nxp/Cargo.toml --target thumbv8m.main-none-eabihf \ --- build --release --manifest-path cyw43/Cargo.toml --target thumbv6m-none-eabi --features ''\ --- build --release --manifest-path cyw43/Cargo.toml --target thumbv6m-none-eabi --features 'log' \ --- build --release --manifest-path cyw43/Cargo.toml --target thumbv6m-none-eabi --features 'defmt' \ @@ -227,6 +228,7 @@ cargo batch \ --- build --release --manifest-path examples/stm32wb/Cargo.toml --target thumbv7em-none-eabi --out-dir out/examples/stm32wb \ --- build --release --manifest-path examples/stm32wba/Cargo.toml --target thumbv8m.main-none-eabihf --out-dir out/examples/stm32wba \ --- build --release --manifest-path examples/stm32wl/Cargo.toml --target thumbv7em-none-eabi --out-dir out/examples/stm32wl \ + --- build --release --manifest-path examples/lpc55s69/Cargo.toml --target thumbv8m.main-none-eabihf --out-dir out/examples/lpc55s69 \ --- build --release --manifest-path examples/boot/application/nrf/Cargo.toml --target thumbv7em-none-eabi --features embassy-nrf/nrf52840,skip-include --out-dir out/examples/boot/nrf52840 \ --- build --release --manifest-path examples/boot/application/nrf/Cargo.toml --target thumbv8m.main-none-eabihf --features embassy-nrf/nrf9160-ns,skip-include --out-dir out/examples/boot/nrf9160 \ --- build --release --manifest-path examples/boot/application/nrf/Cargo.toml --target thumbv8m.main-none-eabihf --features embassy-nrf/nrf9120-ns,skip-include --out-dir out/examples/boot/nrf9120 \ diff --git a/embassy-nxp/Cargo.toml b/embassy-nxp/Cargo.toml new file mode 100644 index 000000000..df329be66 --- /dev/null +++ b/embassy-nxp/Cargo.toml @@ -0,0 +1,17 @@ +[package] +name = "embassy-nxp" +version = "0.1.0" +edition = "2021" + +[dependencies] +cortex-m = "0.7.7" +cortex-m-rt = "0.7.0" +critical-section = "1.1.2" +embassy-hal-internal = {version = "0.2.0", path = "../embassy-hal-internal", features = ["cortex-m", "prio-bits-2"] } +embassy-sync = { version = "0.6.0", path = "../embassy-sync" } +lpc55-pac = "0.5.0" +defmt = "0.3.8" + +[features] +default = ["rt"] +rt = ["lpc55-pac/rt"] \ No newline at end of file diff --git a/embassy-nxp/src/gpio.rs b/embassy-nxp/src/gpio.rs new file mode 100644 index 000000000..d5d04ee69 --- /dev/null +++ b/embassy-nxp/src/gpio.rs @@ -0,0 +1,361 @@ +use embassy_hal_internal::impl_peripheral; + +use crate::pac_utils::*; +use crate::{peripherals, Peripheral, PeripheralRef}; + +pub(crate) fn init() { + // Enable clocks for GPIO, PINT, and IOCON + syscon_reg() + .ahbclkctrl0 + .modify(|_, w| w.gpio0().enable().gpio1().enable().mux().enable().iocon().enable()); +} + +/// The GPIO pin level for pins set on "Digital" mode. +#[derive(Debug, Eq, PartialEq, Clone, Copy)] +pub enum Level { + /// Logical low. Corresponds to 0V. + Low, + /// Logical high. Corresponds to VDD. + High, +} + +/// Pull setting for a GPIO input set on "Digital" mode. +#[derive(Debug, Clone, Copy, Eq, PartialEq)] +pub enum Pull { + /// No pull. + None, + /// Internal pull-up resistor. + Up, + /// Internal pull-down resistor. + Down, +} + +/// The LPC55 boards have two GPIO banks, each with 32 pins. This enum represents the two banks. +#[derive(Debug, Eq, PartialEq, Clone, Copy)] +pub enum Bank { + Bank0 = 0, + Bank1 = 1, +} + +/// GPIO output driver. Internally, this is a specialized [Flex] pin. +pub struct Output<'d> { + pub(crate) pin: Flex<'d>, +} + +impl<'d> Output<'d> { + /// Create GPIO output driver for a [Pin] with the provided [initial output](Level). + #[inline] + pub fn new(pin: impl Peripheral

+ 'd, initial_output: Level) -> Self { + let mut pin = Flex::new(pin); + pin.set_as_output(); + let mut result = Self { pin }; + + match initial_output { + Level::High => result.set_high(), + Level::Low => result.set_low(), + }; + + result + } + + pub fn set_high(&mut self) { + gpio_reg().set[self.pin.pin_bank() as usize].write(|w| unsafe { w.bits(self.pin.bit()) }) + } + + pub fn set_low(&mut self) { + gpio_reg().clr[self.pin.pin_bank() as usize].write(|w| unsafe { w.bits(self.pin.bit()) }) + } + + pub fn toggle(&mut self) { + gpio_reg().not[self.pin.pin_bank() as usize].write(|w| unsafe { w.bits(self.pin.bit()) }) + } + + /// Get the current output level of the pin. Note that the value returned by this function is + /// the voltage level reported by the pin, not the value set by the output driver. + pub fn level(&self) -> Level { + let bits = gpio_reg().pin[self.pin.pin_bank() as usize].read().bits(); + if bits & self.pin.bit() != 0 { + Level::High + } else { + Level::Low + } + } +} + +/// GPIO input driver. Internally, this is a specialized [Flex] pin. +pub struct Input<'d> { + pub(crate) pin: Flex<'d>, +} + +impl<'d> Input<'d> { + /// Create GPIO output driver for a [Pin] with the provided [Pull]. + #[inline] + pub fn new(pin: impl Peripheral

+ 'd, pull: Pull) -> Self { + let mut pin = Flex::new(pin); + pin.set_as_input(); + let mut result = Self { pin }; + result.set_pull(pull); + + result + } + + /// Set the pull configuration for the pin. To disable the pull, use [Pull::None]. + pub fn set_pull(&mut self, pull: Pull) { + match_iocon!(register, iocon_reg(), self.pin.pin_bank(), self.pin.pin_number(), { + register.modify(|_, w| match pull { + Pull::None => w.mode().inactive(), + Pull::Up => w.mode().pull_up(), + Pull::Down => w.mode().pull_down(), + }); + }); + } + + /// Get the current input level of the pin. + pub fn read(&self) -> Level { + let bits = gpio_reg().pin[self.pin.pin_bank() as usize].read().bits(); + if bits & self.pin.bit() != 0 { + Level::High + } else { + Level::Low + } + } +} + +/// A flexible GPIO (digital mode) pin whose mode is not yet determined. Under the hood, this is a +/// reference to a type-erased pin called ["AnyPin"](AnyPin). +pub struct Flex<'d> { + pub(crate) pin: PeripheralRef<'d, AnyPin>, +} + +impl<'d> Flex<'d> { + /// Wrap the pin in a `Flex`. + /// + /// Note: you cannot assume that the pin will be in Digital mode after this call. + #[inline] + pub fn new(pin: impl Peripheral

+ 'd) -> Self { + Self { + pin: pin.into_ref().map_into(), + } + } + + /// Get the bank of this pin. See also [Bank]. + /// + /// # Example + /// + /// ``` + /// use embassy_nxp::gpio::{Bank, Flex}; + /// + /// let p = embassy_nxp::init(Default::default()); + /// let pin = Flex::new(p.PIO1_15); + /// + /// assert_eq!(pin.pin_bank(), Bank::Bank1); + /// ``` + pub fn pin_bank(&self) -> Bank { + self.pin.pin_bank() + } + + /// Get the number of this pin within its bank. See also [Bank]. + /// + /// # Example + /// + /// ``` + /// use embassy_nxp::gpio::Flex; + /// + /// let p = embassy_nxp::init(Default::default()); + /// let pin = Flex::new(p.PIO1_15); + /// + /// assert_eq!(pin.pin_number(), 15 as u8); + /// ``` + pub fn pin_number(&self) -> u8 { + self.pin.pin_number() + } + + /// Get the bit mask for this pin. Useful for setting or clearing bits in a register. Note: + /// PIOx_0 is bit 0, PIOx_1 is bit 1, etc. + /// + /// # Example + /// + /// ``` + /// use embassy_nxp::gpio::Flex; + /// + /// let p = embassy_nxp::init(Default::default()); + /// let pin = Flex::new(p.PIO1_3); + /// + /// assert_eq!(pin.bit(), 0b0000_1000); + /// ``` + pub fn bit(&self) -> u32 { + 1 << self.pin.pin_number() + } + + /// Set the pin to digital mode. This is required for using a pin as a GPIO pin. The default + /// setting for pins is (usually) non-digital. + fn set_as_digital(&mut self) { + match_iocon!(register, iocon_reg(), self.pin_bank(), self.pin_number(), { + register.modify(|_, w| w.digimode().digital()); + }); + } + + /// Set the pin in output mode. This implies setting the pin to digital mode, which this + /// function handles itself. + pub fn set_as_output(&mut self) { + self.set_as_digital(); + gpio_reg().dirset[self.pin.pin_bank() as usize].write(|w| unsafe { w.dirsetp().bits(self.bit()) }) + } + + pub fn set_as_input(&mut self) { + self.set_as_digital(); + gpio_reg().dirclr[self.pin.pin_bank() as usize].write(|w| unsafe { w.dirclrp().bits(self.bit()) }) + } +} + +/// Sealed trait for pins. This trait is sealed and cannot be implemented outside of this crate. +pub(crate) trait SealedPin: Sized { + fn pin_bank(&self) -> Bank; + fn pin_number(&self) -> u8; +} + +/// Interface for a Pin that can be configured by an [Input] or [Output] driver, or converted to an +/// [AnyPin]. By default, this trait is sealed and cannot be implemented outside of the +/// `embassy-nxp` crate due to the [SealedPin] trait. +#[allow(private_bounds)] +pub trait Pin: Peripheral

+ Into + SealedPin + Sized + 'static { + /// Degrade to a generic pin struct + fn degrade(self) -> AnyPin { + AnyPin { + pin_bank: self.pin_bank(), + pin_number: self.pin_number(), + } + } + + /// Returns the pin number within a bank + #[inline] + fn pin(&self) -> u8 { + self.pin_number() + } + + /// Returns the bank of this pin + #[inline] + fn bank(&self) -> Bank { + self.pin_bank() + } +} + +/// Type-erased GPIO pin. +pub struct AnyPin { + pin_bank: Bank, + pin_number: u8, +} + +impl AnyPin { + /// Unsafely create a new type-erased pin. + /// + /// # Safety + /// + /// You must ensure that you’re only using one instance of this type at a time. + pub unsafe fn steal(pin_bank: Bank, pin_number: u8) -> Self { + Self { pin_bank, pin_number } + } +} + +impl_peripheral!(AnyPin); + +impl Pin for AnyPin {} +impl SealedPin for AnyPin { + #[inline] + fn pin_bank(&self) -> Bank { + self.pin_bank + } + + #[inline] + fn pin_number(&self) -> u8 { + self.pin_number + } +} + +macro_rules! impl_pin { + ($name:ident, $bank:expr, $pin_num:expr) => { + impl Pin for peripherals::$name {} + impl SealedPin for peripherals::$name { + #[inline] + fn pin_bank(&self) -> Bank { + $bank + } + + #[inline] + fn pin_number(&self) -> u8 { + $pin_num + } + } + + impl From for crate::gpio::AnyPin { + fn from(val: peripherals::$name) -> Self { + crate::gpio::Pin::degrade(val) + } + } + }; +} + +impl_pin!(PIO0_0, Bank::Bank0, 0); +impl_pin!(PIO0_1, Bank::Bank0, 1); +impl_pin!(PIO0_2, Bank::Bank0, 2); +impl_pin!(PIO0_3, Bank::Bank0, 3); +impl_pin!(PIO0_4, Bank::Bank0, 4); +impl_pin!(PIO0_5, Bank::Bank0, 5); +impl_pin!(PIO0_6, Bank::Bank0, 6); +impl_pin!(PIO0_7, Bank::Bank0, 7); +impl_pin!(PIO0_8, Bank::Bank0, 8); +impl_pin!(PIO0_9, Bank::Bank0, 9); +impl_pin!(PIO0_10, Bank::Bank0, 10); +impl_pin!(PIO0_11, Bank::Bank0, 11); +impl_pin!(PIO0_12, Bank::Bank0, 12); +impl_pin!(PIO0_13, Bank::Bank0, 13); +impl_pin!(PIO0_14, Bank::Bank0, 14); +impl_pin!(PIO0_15, Bank::Bank0, 15); +impl_pin!(PIO0_16, Bank::Bank0, 16); +impl_pin!(PIO0_17, Bank::Bank0, 17); +impl_pin!(PIO0_18, Bank::Bank0, 18); +impl_pin!(PIO0_19, Bank::Bank0, 19); +impl_pin!(PIO0_20, Bank::Bank0, 20); +impl_pin!(PIO0_21, Bank::Bank0, 21); +impl_pin!(PIO0_22, Bank::Bank0, 22); +impl_pin!(PIO0_23, Bank::Bank0, 23); +impl_pin!(PIO0_24, Bank::Bank0, 24); +impl_pin!(PIO0_25, Bank::Bank0, 25); +impl_pin!(PIO0_26, Bank::Bank0, 26); +impl_pin!(PIO0_27, Bank::Bank0, 27); +impl_pin!(PIO0_28, Bank::Bank0, 28); +impl_pin!(PIO0_29, Bank::Bank0, 29); +impl_pin!(PIO0_30, Bank::Bank0, 30); +impl_pin!(PIO0_31, Bank::Bank0, 31); +impl_pin!(PIO1_0, Bank::Bank1, 0); +impl_pin!(PIO1_1, Bank::Bank1, 1); +impl_pin!(PIO1_2, Bank::Bank1, 2); +impl_pin!(PIO1_3, Bank::Bank1, 3); +impl_pin!(PIO1_4, Bank::Bank1, 4); +impl_pin!(PIO1_5, Bank::Bank1, 5); +impl_pin!(PIO1_6, Bank::Bank1, 6); +impl_pin!(PIO1_7, Bank::Bank1, 7); +impl_pin!(PIO1_8, Bank::Bank1, 8); +impl_pin!(PIO1_9, Bank::Bank1, 9); +impl_pin!(PIO1_10, Bank::Bank1, 10); +impl_pin!(PIO1_11, Bank::Bank1, 11); +impl_pin!(PIO1_12, Bank::Bank1, 12); +impl_pin!(PIO1_13, Bank::Bank1, 13); +impl_pin!(PIO1_14, Bank::Bank1, 14); +impl_pin!(PIO1_15, Bank::Bank1, 15); +impl_pin!(PIO1_16, Bank::Bank1, 16); +impl_pin!(PIO1_17, Bank::Bank1, 17); +impl_pin!(PIO1_18, Bank::Bank1, 18); +impl_pin!(PIO1_19, Bank::Bank1, 19); +impl_pin!(PIO1_20, Bank::Bank1, 20); +impl_pin!(PIO1_21, Bank::Bank1, 21); +impl_pin!(PIO1_22, Bank::Bank1, 22); +impl_pin!(PIO1_23, Bank::Bank1, 23); +impl_pin!(PIO1_24, Bank::Bank1, 24); +impl_pin!(PIO1_25, Bank::Bank1, 25); +impl_pin!(PIO1_26, Bank::Bank1, 26); +impl_pin!(PIO1_27, Bank::Bank1, 27); +impl_pin!(PIO1_28, Bank::Bank1, 28); +impl_pin!(PIO1_29, Bank::Bank1, 29); +impl_pin!(PIO1_30, Bank::Bank1, 30); +impl_pin!(PIO1_31, Bank::Bank1, 31); diff --git a/embassy-nxp/src/lib.rs b/embassy-nxp/src/lib.rs new file mode 100644 index 000000000..80fdecb2e --- /dev/null +++ b/embassy-nxp/src/lib.rs @@ -0,0 +1,95 @@ +#![no_std] + +pub mod gpio; +mod pac_utils; +pub mod pint; + +pub use embassy_hal_internal::{into_ref, Peripheral, PeripheralRef}; +pub use lpc55_pac as pac; + +/// Initialize the `embassy-nxp` HAL with the provided configuration. +/// +/// This returns the peripheral singletons that can be used for creating drivers. +/// +/// This should only be called once and at startup, otherwise it panics. +pub fn init(_config: config::Config) -> Peripherals { + gpio::init(); + pint::init(); + + crate::Peripherals::take() +} + +embassy_hal_internal::peripherals! { + // External pins. These are not only GPIOs, they are multi-purpose pins and can be used by other + // peripheral types (e.g. I2C). + PIO0_0, + PIO0_1, + PIO0_2, + PIO0_3, + PIO0_4, + PIO0_5, + PIO0_6, + PIO0_7, + PIO0_8, + PIO0_9, + PIO0_10, + PIO0_11, + PIO0_12, + PIO0_13, + PIO0_14, + PIO0_15, + PIO0_16, + PIO0_17, + PIO0_18, + PIO0_19, + PIO0_20, + PIO0_21, + PIO0_22, + PIO0_23, + PIO0_24, + PIO0_25, + PIO0_26, + PIO0_27, + PIO0_28, + PIO0_29, + PIO0_30, + PIO0_31, + PIO1_0, + PIO1_1, + PIO1_2, + PIO1_3, + PIO1_4, + PIO1_5, + PIO1_6, + PIO1_7, + PIO1_8, + PIO1_9, + PIO1_10, + PIO1_11, + PIO1_12, + PIO1_13, + PIO1_14, + PIO1_15, + PIO1_16, + PIO1_17, + PIO1_18, + PIO1_19, + PIO1_20, + PIO1_21, + PIO1_22, + PIO1_23, + PIO1_24, + PIO1_25, + PIO1_26, + PIO1_27, + PIO1_28, + PIO1_29, + PIO1_30, + PIO1_31, +} + +/// HAL configuration for the NXP board. +pub mod config { + #[derive(Default)] + pub struct Config {} +} diff --git a/embassy-nxp/src/pac_utils.rs b/embassy-nxp/src/pac_utils.rs new file mode 100644 index 000000000..86a807f6c --- /dev/null +++ b/embassy-nxp/src/pac_utils.rs @@ -0,0 +1,323 @@ +/// Get the GPIO register block. This is used to configure all GPIO pins. +/// +/// # Safety +/// Due to the type system of peripherals, access to the settings of a single pin is possible only +/// by a single thread at a time. Read/Write operations on a single registers are NOT atomic. You +/// must ensure that the GPIO registers are not accessed concurrently by multiple threads. +pub(crate) fn gpio_reg() -> &'static lpc55_pac::gpio::RegisterBlock { + unsafe { &*lpc55_pac::GPIO::ptr() } +} + +/// Get the IOCON register block. +/// +/// # Safety +/// Read/Write operations on a single registers are NOT atomic. You must ensure that the GPIO +/// registers are not accessed concurrently by multiple threads. +pub(crate) fn iocon_reg() -> &'static lpc55_pac::iocon::RegisterBlock { + unsafe { &*lpc55_pac::IOCON::ptr() } +} + +/// Get the INPUTMUX register block. +/// +/// # Safety +/// Read/Write operations on a single registers are NOT atomic. You must ensure that the GPIO +/// registers are not accessed concurrently by multiple threads. +pub(crate) fn inputmux_reg() -> &'static lpc55_pac::inputmux::RegisterBlock { + unsafe { &*lpc55_pac::INPUTMUX::ptr() } +} + +/// Get the SYSCON register block. +/// +/// # Safety +/// Read/Write operations on a single registers are NOT atomic. You must ensure that the GPIO +/// registers are not accessed concurrently by multiple threads. +pub(crate) fn syscon_reg() -> &'static lpc55_pac::syscon::RegisterBlock { + unsafe { &*lpc55_pac::SYSCON::ptr() } +} + +/// Get the PINT register block. +/// +/// # Safety +/// Read/Write operations on a single registers are NOT atomic. You must ensure that the GPIO +/// registers are not accessed concurrently by multiple threads. +pub(crate) fn pint_reg() -> &'static lpc55_pac::pint::RegisterBlock { + unsafe { &*lpc55_pac::PINT::ptr() } +} + +/// Match the pin bank and number of a pin to the corresponding IOCON register. +/// +/// # Example +/// ``` +/// use embassy_nxp::gpio::Bank; +/// use embassy_nxp::pac_utils::{iocon_reg, match_iocon}; +/// +/// // Make pin PIO1_6 digital and set it to pull-down mode. +/// match_iocon!(register, iocon_reg(), Bank::Bank1, 6, { +/// register.modify(|_, w| w.mode().pull_down().digimode().digital()); +/// }); +/// ``` +macro_rules! match_iocon { + ($register:ident, $iocon_register:expr, $pin_bank:expr, $pin_number:expr, $action:expr) => { + match ($pin_bank, $pin_number) { + (Bank::Bank0, 0) => { + let $register = &($iocon_register).pio0_0; + $action; + } + (Bank::Bank0, 1) => { + let $register = &($iocon_register).pio0_1; + $action; + } + (Bank::Bank0, 2) => { + let $register = &($iocon_register).pio0_2; + $action; + } + (Bank::Bank0, 3) => { + let $register = &($iocon_register).pio0_3; + $action; + } + (Bank::Bank0, 4) => { + let $register = &($iocon_register).pio0_4; + $action; + } + (Bank::Bank0, 5) => { + let $register = &($iocon_register).pio0_5; + $action; + } + (Bank::Bank0, 6) => { + let $register = &($iocon_register).pio0_6; + $action; + } + (Bank::Bank0, 7) => { + let $register = &($iocon_register).pio0_7; + $action; + } + (Bank::Bank0, 8) => { + let $register = &($iocon_register).pio0_8; + $action; + } + (Bank::Bank0, 9) => { + let $register = &($iocon_register).pio0_9; + $action; + } + (Bank::Bank0, 10) => { + let $register = &($iocon_register).pio0_10; + $action; + } + (Bank::Bank0, 11) => { + let $register = &($iocon_register).pio0_11; + $action; + } + (Bank::Bank0, 12) => { + let $register = &($iocon_register).pio0_12; + $action; + } + (Bank::Bank0, 13) => { + let $register = &($iocon_register).pio0_13; + $action; + } + (Bank::Bank0, 14) => { + let $register = &($iocon_register).pio0_14; + $action; + } + (Bank::Bank0, 15) => { + let $register = &($iocon_register).pio0_15; + $action; + } + (Bank::Bank0, 16) => { + let $register = &($iocon_register).pio0_16; + $action; + } + (Bank::Bank0, 17) => { + let $register = &($iocon_register).pio0_17; + $action; + } + (Bank::Bank0, 18) => { + let $register = &($iocon_register).pio0_18; + $action; + } + (Bank::Bank0, 19) => { + let $register = &($iocon_register).pio0_19; + $action; + } + (Bank::Bank0, 20) => { + let $register = &($iocon_register).pio0_20; + $action; + } + (Bank::Bank0, 21) => { + let $register = &($iocon_register).pio0_21; + $action; + } + (Bank::Bank0, 22) => { + let $register = &($iocon_register).pio0_22; + $action; + } + (Bank::Bank0, 23) => { + let $register = &($iocon_register).pio0_23; + $action; + } + (Bank::Bank0, 24) => { + let $register = &($iocon_register).pio0_24; + $action; + } + (Bank::Bank0, 25) => { + let $register = &($iocon_register).pio0_25; + $action; + } + (Bank::Bank0, 26) => { + let $register = &($iocon_register).pio0_26; + $action; + } + (Bank::Bank0, 27) => { + let $register = &($iocon_register).pio0_27; + $action; + } + (Bank::Bank0, 28) => { + let $register = &($iocon_register).pio0_28; + $action; + } + (Bank::Bank0, 29) => { + let $register = &($iocon_register).pio0_29; + $action; + } + (Bank::Bank0, 30) => { + let $register = &($iocon_register).pio0_30; + $action; + } + (Bank::Bank0, 31) => { + let $register = &($iocon_register).pio0_31; + $action; + } + (Bank::Bank1, 0) => { + let $register = &($iocon_register).pio1_0; + $action; + } + (Bank::Bank1, 1) => { + let $register = &($iocon_register).pio1_1; + $action; + } + (Bank::Bank1, 2) => { + let $register = &($iocon_register).pio1_2; + $action; + } + (Bank::Bank1, 3) => { + let $register = &($iocon_register).pio1_3; + $action; + } + (Bank::Bank1, 4) => { + let $register = &($iocon_register).pio1_4; + $action; + } + (Bank::Bank1, 5) => { + let $register = &($iocon_register).pio1_5; + $action; + } + (Bank::Bank1, 6) => { + let $register = &($iocon_register).pio1_6; + $action; + } + (Bank::Bank1, 7) => { + let $register = &($iocon_register).pio1_7; + $action; + } + (Bank::Bank1, 8) => { + let $register = &($iocon_register).pio1_8; + $action; + } + (Bank::Bank1, 9) => { + let $register = &($iocon_register).pio1_9; + $action; + } + (Bank::Bank1, 10) => { + let $register = &($iocon_register).pio1_10; + $action; + } + (Bank::Bank1, 11) => { + let $register = &($iocon_register).pio1_11; + $action; + } + (Bank::Bank1, 12) => { + let $register = &($iocon_register).pio1_12; + $action; + } + (Bank::Bank1, 13) => { + let $register = &($iocon_register).pio1_13; + $action; + } + (Bank::Bank1, 14) => { + let $register = &($iocon_register).pio1_14; + $action; + } + (Bank::Bank1, 15) => { + let $register = &($iocon_register).pio1_15; + $action; + } + (Bank::Bank1, 16) => { + let $register = &($iocon_register).pio1_16; + $action; + } + (Bank::Bank1, 17) => { + let $register = &($iocon_register).pio1_17; + $action; + } + (Bank::Bank1, 18) => { + let $register = &($iocon_register).pio1_18; + $action; + } + (Bank::Bank1, 19) => { + let $register = &($iocon_register).pio1_19; + $action; + } + (Bank::Bank1, 20) => { + let $register = &($iocon_register).pio1_20; + $action; + } + (Bank::Bank1, 21) => { + let $register = &($iocon_register).pio1_21; + $action; + } + (Bank::Bank1, 22) => { + let $register = &($iocon_register).pio1_22; + $action; + } + (Bank::Bank1, 23) => { + let $register = &($iocon_register).pio1_23; + $action; + } + (Bank::Bank1, 24) => { + let $register = &($iocon_register).pio1_24; + $action; + } + (Bank::Bank1, 25) => { + let $register = &($iocon_register).pio1_25; + $action; + } + (Bank::Bank1, 26) => { + let $register = &($iocon_register).pio1_26; + $action; + } + (Bank::Bank1, 27) => { + let $register = &($iocon_register).pio1_27; + $action; + } + (Bank::Bank1, 28) => { + let $register = &($iocon_register).pio1_28; + $action; + } + (Bank::Bank1, 29) => { + let $register = &($iocon_register).pio1_29; + $action; + } + (Bank::Bank1, 30) => { + let $register = &($iocon_register).pio1_30; + $action; + } + (Bank::Bank1, 31) => { + let $register = &($iocon_register).pio1_31; + $action; + } + _ => unreachable!(), + } + }; +} + +pub(crate) use match_iocon; diff --git a/embassy-nxp/src/pint.rs b/embassy-nxp/src/pint.rs new file mode 100644 index 000000000..3313f91a6 --- /dev/null +++ b/embassy-nxp/src/pint.rs @@ -0,0 +1,440 @@ +//! Pin Interrupt module. +use core::cell::RefCell; +use core::future::Future; +use core::pin::Pin as FuturePin; +use core::task::{Context, Poll}; + +use critical_section::Mutex; +use embassy_hal_internal::{Peripheral, PeripheralRef}; +use embassy_sync::waitqueue::AtomicWaker; + +use crate::gpio::{self, AnyPin, Level, SealedPin}; +use crate::pac::interrupt; +use crate::pac_utils::*; + +struct PinInterrupt { + assigned: bool, + waker: AtomicWaker, + /// If true, the interrupt was triggered due to this PinInterrupt. This is used to determine if + /// an [InputFuture] should return Poll::Ready. + at_fault: bool, +} + +impl PinInterrupt { + pub fn interrupt_active(&self) -> bool { + self.assigned + } + + /// Mark the interrupt as assigned to a pin. + pub fn enable(&mut self) { + self.assigned = true; + self.at_fault = false; + } + + /// Mark the interrupt as available. + pub fn disable(&mut self) { + self.assigned = false; + self.at_fault = false; + } + + /// Returns true if the interrupt was triggered due to this PinInterrupt. + /// + /// If this function returns true, it will also reset the at_fault flag. + pub fn at_fault(&mut self) -> bool { + let val = self.at_fault; + self.at_fault = false; + val + } + + /// Set the at_fault flag to true. + pub fn fault(&mut self) { + self.at_fault = true; + } +} + +const NEW_PIN_INTERRUPT: PinInterrupt = PinInterrupt { + assigned: false, + waker: AtomicWaker::new(), + at_fault: false, +}; +const INTERUPT_COUNT: usize = 8; +static PIN_INTERRUPTS: Mutex> = + Mutex::new(RefCell::new([NEW_PIN_INTERRUPT; INTERUPT_COUNT])); + +fn next_available_interrupt() -> Option { + critical_section::with(|cs| { + for (i, pin_interrupt) in PIN_INTERRUPTS.borrow(cs).borrow().iter().enumerate() { + if !pin_interrupt.interrupt_active() { + return Some(i); + } + } + + None + }) +} + +#[derive(Clone, Copy, PartialEq, Eq)] +enum Edge { + Rising, + Falling, + Both, +} + +#[derive(Clone, Copy, PartialEq, Eq)] +enum InterruptOn { + Level(Level), + Edge(Edge), +} + +pub(crate) fn init() { + syscon_reg().ahbclkctrl0.modify(|_, w| w.pint().enable()); + + // Enable interrupts + unsafe { + crate::pac::NVIC::unmask(crate::pac::Interrupt::PIN_INT0); + crate::pac::NVIC::unmask(crate::pac::Interrupt::PIN_INT1); + crate::pac::NVIC::unmask(crate::pac::Interrupt::PIN_INT2); + crate::pac::NVIC::unmask(crate::pac::Interrupt::PIN_INT3); + crate::pac::NVIC::unmask(crate::pac::Interrupt::PIN_INT4); + crate::pac::NVIC::unmask(crate::pac::Interrupt::PIN_INT5); + crate::pac::NVIC::unmask(crate::pac::Interrupt::PIN_INT6); + crate::pac::NVIC::unmask(crate::pac::Interrupt::PIN_INT7); + }; +} + +#[must_use = "futures do nothing unless you `.await` or poll them"] +struct InputFuture<'d> { + #[allow(dead_code)] + pin: PeripheralRef<'d, AnyPin>, + interrupt_number: usize, +} + +impl<'d> InputFuture<'d> { + /// Create a new input future. Returns None if all interrupts are in use. + fn new(pin: impl Peripheral

+ 'd, interrupt_on: InterruptOn) -> Option { + let pin = pin.into_ref().map_into(); + let interrupt_number = next_available_interrupt()?; + + // Clear interrupt, just in case + pint_reg() + .rise + .write(|w| unsafe { w.rdet().bits(1 << interrupt_number) }); + pint_reg() + .fall + .write(|w| unsafe { w.fdet().bits(1 << interrupt_number) }); + + // Enable input multiplexing on pin interrupt register 0 for pin (32*bank + pin_number) + inputmux_reg().pintsel[interrupt_number] + .write(|w| unsafe { w.intpin().bits(32 * pin.pin_bank() as u8 + pin.pin_number()) }); + + match interrupt_on { + InterruptOn::Level(level) => { + // Set pin interrupt register to edge sensitive or level sensitive + // 0 = edge sensitive, 1 = level sensitive + pint_reg() + .isel + .modify(|r, w| unsafe { w.bits(r.bits() | (1 << interrupt_number)) }); + + // Enable level interrupt. + // + // Note: Level sensitive interrupts are enabled by the same register as rising edge + // is activated. + + // 0 = no-op, 1 = enable + pint_reg() + .sienr + .write(|w| unsafe { w.setenrl().bits(1 << interrupt_number) }); + + // Set active level + match level { + Level::Low => { + // 0 = no-op, 1 = select LOW + pint_reg() + .cienf + .write(|w| unsafe { w.cenaf().bits(1 << interrupt_number) }); + } + Level::High => { + // 0 = no-op, 1 = select HIGH + pint_reg() + .sienf + .write(|w| unsafe { w.setenaf().bits(1 << interrupt_number) }); + } + } + } + InterruptOn::Edge(edge) => { + // Set pin interrupt register to edge sensitive or level sensitive + // 0 = edge sensitive, 1 = level sensitive + pint_reg() + .isel + .modify(|r, w| unsafe { w.bits(r.bits() & !(1 << interrupt_number)) }); + + // Enable rising/falling edge detection + match edge { + Edge::Rising => { + // 0 = no-op, 1 = enable rising edge + pint_reg() + .sienr + .write(|w| unsafe { w.setenrl().bits(1 << interrupt_number) }); + // 0 = no-op, 1 = disable falling edge + pint_reg() + .cienf + .write(|w| unsafe { w.cenaf().bits(1 << interrupt_number) }); + } + Edge::Falling => { + // 0 = no-op, 1 = enable falling edge + pint_reg() + .sienf + .write(|w| unsafe { w.setenaf().bits(1 << interrupt_number) }); + // 0 = no-op, 1 = disable rising edge + pint_reg() + .cienr + .write(|w| unsafe { w.cenrl().bits(1 << interrupt_number) }); + } + Edge::Both => { + // 0 = no-op, 1 = enable + pint_reg() + .sienr + .write(|w| unsafe { w.setenrl().bits(1 << interrupt_number) }); + pint_reg() + .sienf + .write(|w| unsafe { w.setenaf().bits(1 << interrupt_number) }); + } + } + } + } + + critical_section::with(|cs| { + let mut pin_interrupts = PIN_INTERRUPTS.borrow(cs).borrow_mut(); + let pin_interrupt = &mut pin_interrupts[interrupt_number]; + + pin_interrupt.enable(); + }); + + Some(Self { pin, interrupt_number }) + } + + /// Returns true if the interrupt was triggered for this pin. + fn interrupt_triggered(&self) -> bool { + let interrupt_number = self.interrupt_number; + + // Initially, we determine if the interrupt was triggered by this InputFuture by checking + // the flags of the interrupt_number. However, by the time we get to this point, the + // interrupt may have been triggered again, so we needed to clear the cpu flags immediately. + // As a solution, we mark which [PinInterrupt] is responsible for the interrupt ("at fault") + critical_section::with(|cs| { + let mut pin_interrupts = PIN_INTERRUPTS.borrow(cs).borrow_mut(); + let pin_interrupt = &mut pin_interrupts[interrupt_number]; + + pin_interrupt.at_fault() + }) + } +} + +impl<'d> Drop for InputFuture<'d> { + fn drop(&mut self) { + let interrupt_number = self.interrupt_number; + + // Disable pin interrupt + // 0 = no-op, 1 = disable + pint_reg() + .cienr + .write(|w| unsafe { w.cenrl().bits(1 << interrupt_number) }); + pint_reg() + .cienf + .write(|w| unsafe { w.cenaf().bits(1 << interrupt_number) }); + + critical_section::with(|cs| { + let mut pin_interrupts = PIN_INTERRUPTS.borrow(cs).borrow_mut(); + let pin_interrupt = &mut pin_interrupts[interrupt_number]; + + pin_interrupt.disable(); + }); + } +} + +impl<'d> Future for InputFuture<'d> { + type Output = (); + + fn poll(self: FuturePin<&mut Self>, cx: &mut Context<'_>) -> Poll { + let interrupt_number = self.interrupt_number; + + critical_section::with(|cs| { + let mut pin_interrupts = PIN_INTERRUPTS.borrow(cs).borrow_mut(); + let pin_interrupt = &mut pin_interrupts[interrupt_number]; + + pin_interrupt.waker.register(cx.waker()); + }); + + if self.interrupt_triggered() { + Poll::Ready(()) + } else { + Poll::Pending + } + } +} + +fn handle_interrupt(interrupt_number: usize) { + pint_reg() + .rise + .write(|w| unsafe { w.rdet().bits(1 << interrupt_number) }); + pint_reg() + .fall + .write(|w| unsafe { w.fdet().bits(1 << interrupt_number) }); + + critical_section::with(|cs| { + let mut pin_interrupts = PIN_INTERRUPTS.borrow(cs).borrow_mut(); + let pin_interrupt = &mut pin_interrupts[interrupt_number]; + + pin_interrupt.fault(); + pin_interrupt.waker.wake(); + }); +} + +#[allow(non_snake_case)] +#[interrupt] +fn PIN_INT0() { + handle_interrupt(0); +} + +#[allow(non_snake_case)] +#[interrupt] +fn PIN_INT1() { + handle_interrupt(1); +} + +#[allow(non_snake_case)] +#[interrupt] +fn PIN_INT2() { + handle_interrupt(2); +} + +#[allow(non_snake_case)] +#[interrupt] +fn PIN_INT3() { + handle_interrupt(3); +} + +#[allow(non_snake_case)] +#[interrupt] +fn PIN_INT4() { + handle_interrupt(4); +} + +#[allow(non_snake_case)] +#[interrupt] +fn PIN_INT5() { + handle_interrupt(5); +} + +#[allow(non_snake_case)] +#[interrupt] +fn PIN_INT6() { + handle_interrupt(6); +} + +#[allow(non_snake_case)] +#[interrupt] +fn PIN_INT7() { + handle_interrupt(7); +} + +impl gpio::Flex<'_> { + /// Wait for a falling or rising edge on the pin. You can have at most 8 pins waiting. If you + /// try to wait for more than 8 pins, this function will return `None`. + pub async fn wait_for_any_edge(&mut self) -> Option<()> { + InputFuture::new(&mut self.pin, InterruptOn::Edge(Edge::Both))?.await; + Some(()) + } + + /// Wait for a falling edge on the pin. You can have at most 8 pins waiting. If you try to wait + /// for more than 8 pins, this function will return `None`. + pub async fn wait_for_falling_edge(&mut self) -> Option<()> { + InputFuture::new(&mut self.pin, InterruptOn::Edge(Edge::Falling))?.await; + Some(()) + } + + /// Wait for a rising edge on the pin. You can have at most 8 pins waiting. If you try to wait + /// for more than 8 pins, this function will return `None`. + pub async fn wait_for_rising_edge(&mut self) -> Option<()> { + InputFuture::new(&mut self.pin, InterruptOn::Edge(Edge::Rising))?.await; + Some(()) + } + + /// Wait for a low level on the pin. You can have at most 8 pins waiting. If you try to wait for + /// more than 8 pins, this function will return `None`. + pub async fn wait_for_low(&mut self) -> Option<()> { + InputFuture::new(&mut self.pin, InterruptOn::Level(Level::Low))?.await; + Some(()) + } + + /// Wait for a high level on the pin. You can have at most 8 pins waiting. If you try to wait for + /// more than 8 pins, this function will return `None`. + pub async fn wait_for_high(&mut self) -> Option<()> { + InputFuture::new(&mut self.pin, InterruptOn::Level(Level::High))?.await; + Some(()) + } +} + +impl gpio::Input<'_> { + /// Wait for a falling or rising edge on the pin. You can have at most 8 pins waiting. If you + /// try to wait for more than 8 pins, this function will return `None`. + pub async fn wait_for_any_edge(&mut self) -> Option<()> { + self.pin.wait_for_any_edge().await + } + + /// Wait for a falling edge on the pin. You can have at most 8 pins waiting. If you try to wait + /// for more than 8 pins, this function will return `None`. + pub async fn wait_for_falling_edge(&mut self) -> Option<()> { + self.pin.wait_for_falling_edge().await + } + + /// Wait for a rising edge on the pin. You can have at most 8 pins waiting. If you try to wait + /// for more than 8 pins, this function will return `None`. + pub async fn wait_for_rising_edge(&mut self) -> Option<()> { + self.pin.wait_for_rising_edge().await + } + + /// Wait for a low level on the pin. You can have at most 8 pins waiting. If you try to wait for + /// more than 8 pins, this function will return `None`. + pub async fn wait_for_low(&mut self) -> Option<()> { + self.pin.wait_for_low().await + } + + /// Wait for a high level on the pin. You can have at most 8 pins waiting. If you try to wait for + /// more than 8 pins, this function will return `None`. + pub async fn wait_for_high(&mut self) -> Option<()> { + self.pin.wait_for_high().await + } +} + +impl gpio::Output<'_> { + /// Wait for a falling or rising edge on the pin. You can have at most 8 pins waiting. If you + /// try to wait for more than 8 pins, this function will return `None`. + pub async fn wait_for_any_edge(&mut self) -> Option<()> { + self.pin.wait_for_any_edge().await + } + + /// Wait for a falling edge on the pin. You can have at most 8 pins waiting. If you try to wait + /// for more than 8 pins, this function will return `None`. + pub async fn wait_for_falling_edge(&mut self) -> Option<()> { + self.pin.wait_for_falling_edge().await + } + + /// Wait for a rising edge on the pin. You can have at most 8 pins waiting. If you try to wait + /// for more than 8 pins, this function will return `None`. + pub async fn wait_for_rising_edge(&mut self) -> Option<()> { + self.pin.wait_for_rising_edge().await + } + + /// Wait for a low level on the pin. You can have at most 8 pins waiting. If you try to wait for + /// more than 8 pins, this function will return `None`. + pub async fn wait_for_low(&mut self) -> Option<()> { + self.pin.wait_for_low().await + } + + /// Wait for a high level on the pin. You can have at most 8 pins waiting. If you try to wait for + /// more than 8 pins, this function will return `None`. + pub async fn wait_for_high(&mut self) -> Option<()> { + self.pin.wait_for_high().await + } +} diff --git a/examples/lpc55s69/.cargo/config.toml b/examples/lpc55s69/.cargo/config.toml new file mode 100644 index 000000000..9556de72f --- /dev/null +++ b/examples/lpc55s69/.cargo/config.toml @@ -0,0 +1,8 @@ +[target.'cfg(all(target_arch = "arm", target_os = "none"))'] +runner = "probe-rs run --chip LPC55S69JBD100" + +[build] +target = "thumbv8m.main-none-eabihf" + +[env] +DEFMT_LOG = "debug" diff --git a/examples/lpc55s69/Cargo.toml b/examples/lpc55s69/Cargo.toml new file mode 100644 index 000000000..14ec2d47e --- /dev/null +++ b/examples/lpc55s69/Cargo.toml @@ -0,0 +1,22 @@ +[package] +edition = "2021" +name = "embassy-nxp-lpc55s69-examples" +version = "0.1.0" +license = "MIT OR Apache-2.0" + + +[dependencies] +embassy-nxp = { version = "0.1.0", path = "../../embassy-nxp", features = ["rt"] } +embassy-executor = { version = "0.6.0", path = "../../embassy-executor", features = ["task-arena-size-32768", "arch-cortex-m", "executor-thread", "executor-interrupt"] } +embassy-sync = { version = "0.6.0", path = "../../embassy-sync", features = ["defmt"] } +embassy-time = { version = "0.3.0", path = "../../embassy-time", features = ["defmt"] } +panic-halt = "0.2.0" +cortex-m = { version = "0.7.6", features = ["inline-asm", "critical-section-single-core"] } +cortex-m-rt = {version = "0.7.0"} +defmt = "0.3" +defmt-rtt = "0.4" +panic-probe = { version = "0.3.2", features = ["print-defmt"] } +panic-semihosting = "0.6.0" + +[profile.release] +debug = 2 diff --git a/examples/lpc55s69/build.rs b/examples/lpc55s69/build.rs new file mode 100644 index 000000000..30691aa97 --- /dev/null +++ b/examples/lpc55s69/build.rs @@ -0,0 +1,35 @@ +//! This build script copies the `memory.x` file from the crate root into +//! a directory where the linker can always find it at build time. +//! For many projects this is optional, as the linker always searches the +//! project root directory -- wherever `Cargo.toml` is. However, if you +//! are using a workspace or have a more complicated build setup, this +//! build script becomes required. Additionally, by requesting that +//! Cargo re-run the build script whenever `memory.x` is changed, +//! updating `memory.x` ensures a rebuild of the application with the +//! new memory settings. + +use std::env; +use std::fs::File; +use std::io::Write; +use std::path::PathBuf; + +fn main() { + // Put `memory.x` in our output directory and ensure it's + // on the linker search path. + let out = &PathBuf::from(env::var_os("OUT_DIR").unwrap()); + File::create(out.join("memory.x")) + .unwrap() + .write_all(include_bytes!("memory.x")) + .unwrap(); + println!("cargo:rustc-link-search={}", out.display()); + + // By default, Cargo will re-run a build script whenever + // any file in the project changes. By specifying `memory.x` + // here, we ensure the build script is only re-run when + // `memory.x` is changed. + println!("cargo:rerun-if-changed=memory.x"); + + println!("cargo:rustc-link-arg-bins=--nmagic"); + println!("cargo:rustc-link-arg-bins=-Tlink.x"); + println!("cargo:rustc-link-arg-bins=-Tdefmt.x"); +} diff --git a/examples/lpc55s69/memory.x b/examples/lpc55s69/memory.x new file mode 100644 index 000000000..1483b2fad --- /dev/null +++ b/examples/lpc55s69/memory.x @@ -0,0 +1,28 @@ +/* File originally from lpc55-hal repo: https://github.com/lpc55/lpc55-hal/blob/main/memory.x */ +MEMORY +{ + FLASH : ORIGIN = 0x00000000, LENGTH = 512K + + /* for use with standard link.x */ + RAM : ORIGIN = 0x20000000, LENGTH = 256K + + /* would be used with proper link.x */ + /* needs changes to r0 (initialization code) */ + /* SRAM0 : ORIGIN = 0x20000000, LENGTH = 64K */ + /* SRAM1 : ORIGIN = 0x20010000, LENGTH = 64K */ + /* SRAM2 : ORIGIN = 0x20020000, LENGTH = 64K */ + /* SRAM3 : ORIGIN = 0x20030000, LENGTH = 64K */ + + /* CASPER SRAM regions */ + /* SRAMX0: ORIGIN = 0x1400_0000, LENGTH = 4K /1* to 0x1400_0FFF *1/ */ + /* SRAMX1: ORIGIN = 0x1400_4000, LENGTH = 4K /1* to 0x1400_4FFF *1/ */ + + /* USB1 SRAM regin */ + /* USB1_SRAM : ORIGIN = 0x40100000, LENGTH = 16K */ + + /* To define our own USB RAM section in one regular */ + /* RAM, probably easiest to shorten length of RAM */ + /* above, and use this freed RAM section */ + +} + diff --git a/examples/lpc55s69/src/bin/blinky_nop.rs b/examples/lpc55s69/src/bin/blinky_nop.rs new file mode 100644 index 000000000..58e2d9808 --- /dev/null +++ b/examples/lpc55s69/src/bin/blinky_nop.rs @@ -0,0 +1,33 @@ +//! This example has been made with the LPCXpresso55S69 board in mind, which has a built-in LED on PIO1_6. + +#![no_std] +#![no_main] + +use cortex_m::asm::nop; +use defmt::*; +use embassy_executor::Spawner; +use embassy_nxp::gpio::{Level, Output}; +use {defmt_rtt as _, panic_halt as _}; + +#[embassy_executor::main] +async fn main(_spawner: Spawner) { + let p = embassy_nxp::init(Default::default()); + + let mut led = Output::new(p.PIO1_6, Level::Low); + + loop { + info!("led off!"); + led.set_high(); + + for _ in 0..200_000 { + nop(); + } + + info!("led on!"); + led.set_low(); + + for _ in 0..200_000 { + nop(); + } + } +} diff --git a/examples/lpc55s69/src/bin/button_executor.rs b/examples/lpc55s69/src/bin/button_executor.rs new file mode 100644 index 000000000..836b1c9eb --- /dev/null +++ b/examples/lpc55s69/src/bin/button_executor.rs @@ -0,0 +1,25 @@ +//! This example has been made with the LPCXpresso55S69 board in mind, which has a built-in LED on +//! PIO1_6 and a button (labeled "user") on PIO1_9. + +#![no_std] +#![no_main] + +use defmt::*; +use embassy_executor::Spawner; +use embassy_nxp::gpio::{Input, Level, Output, Pull}; +use {defmt_rtt as _, panic_halt as _}; + +#[embassy_executor::main] +async fn main(_spawner: Spawner) -> ! { + let p = embassy_nxp::init(Default::default()); + + let mut led = Output::new(p.PIO1_6, Level::Low); + let mut button = Input::new(p.PIO1_9, Pull::Up); + + info!("Entered main loop"); + loop { + button.wait_for_rising_edge().await; + info!("Button pressed"); + led.toggle(); + } +} From baef775f6b33b4fd45dd3a4bb1122a52c6d22c67 Mon Sep 17 00:00:00 2001 From: Oliver Rockstedt Date: Mon, 7 Oct 2024 13:30:46 +0200 Subject: [PATCH 047/361] Add capacity, free_capacity, clear, len, is_empty and is_full functions to Channel::{Sender, Receiver} --- embassy-sync/CHANGELOG.md | 1 + embassy-sync/src/channel.rs | 84 +++++++++++++++++++++++++++++++++++++ 2 files changed, 85 insertions(+) diff --git a/embassy-sync/CHANGELOG.md b/embassy-sync/CHANGELOG.md index 8847e7e88..253a6cc82 100644 --- a/embassy-sync/CHANGELOG.md +++ b/embassy-sync/CHANGELOG.md @@ -9,6 +9,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Add LazyLock sync primitive. - Add `clear`, `len`, `is_empty` and `is_full` functions to `zerocopy_channel`. +- Add `capacity`, `free_capacity`, `clear`, `len`, `is_empty` and `is_full` functions to `Channel::{Sender, Receiver}`. ## 0.6.0 - 2024-05-29 diff --git a/embassy-sync/src/channel.rs b/embassy-sync/src/channel.rs index 55ac5fb66..18b053111 100644 --- a/embassy-sync/src/channel.rs +++ b/embassy-sync/src/channel.rs @@ -72,6 +72,48 @@ where pub fn poll_ready_to_send(&self, cx: &mut Context<'_>) -> Poll<()> { self.channel.poll_ready_to_send(cx) } + + /// Returns the maximum number of elements the channel can hold. + /// + /// See [`Channel::capacity()`] + pub const fn capacity(&self) -> usize { + self.channel.capacity() + } + + /// Returns the free capacity of the channel. + /// + /// See [`Channel::free_capacity()`] + pub fn free_capacity(&self) -> usize { + self.channel.free_capacity() + } + + /// Clears all elements in the channel. + /// + /// See [`Channel::clear()`] + pub fn clear(&self) { + self.channel.clear(); + } + + /// Returns the number of elements currently in the channel. + /// + /// See [`Channel::len()`] + pub fn len(&self) -> usize { + self.channel.len() + } + + /// Returns whether the channel is empty. + /// + /// See [`Channel::is_empty()`] + pub fn is_empty(&self) -> bool { + self.channel.is_empty() + } + + /// Returns whether the channel is full. + /// + /// See [`Channel::is_full()`] + pub fn is_full(&self) -> bool { + self.channel.is_full() + } } /// Send-only access to a [`Channel`] without knowing channel size. @@ -179,6 +221,48 @@ where pub fn poll_receive(&self, cx: &mut Context<'_>) -> Poll { self.channel.poll_receive(cx) } + + /// Returns the maximum number of elements the channel can hold. + /// + /// See [`Channel::capacity()`] + pub const fn capacity(&self) -> usize { + self.channel.capacity() + } + + /// Returns the free capacity of the channel. + /// + /// See [`Channel::free_capacity()`] + pub fn free_capacity(&self) -> usize { + self.channel.free_capacity() + } + + /// Clears all elements in the channel. + /// + /// See [`Channel::clear()`] + pub fn clear(&self) { + self.channel.clear(); + } + + /// Returns the number of elements currently in the channel. + /// + /// See [`Channel::len()`] + pub fn len(&self) -> usize { + self.channel.len() + } + + /// Returns whether the channel is empty. + /// + /// See [`Channel::is_empty()`] + pub fn is_empty(&self) -> bool { + self.channel.is_empty() + } + + /// Returns whether the channel is full. + /// + /// See [`Channel::is_full()`] + pub fn is_full(&self) -> bool { + self.channel.is_full() + } } /// Receive-only access to a [`Channel`] without knowing channel size. From e3fd33d372b96cd32007dbffe5755150c3df22f2 Mon Sep 17 00:00:00 2001 From: Oliver Rockstedt Date: Mon, 7 Oct 2024 13:41:15 +0200 Subject: [PATCH 048/361] Minor changelog fix --- embassy-sync/CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/embassy-sync/CHANGELOG.md b/embassy-sync/CHANGELOG.md index 253a6cc82..83fd666ac 100644 --- a/embassy-sync/CHANGELOG.md +++ b/embassy-sync/CHANGELOG.md @@ -9,7 +9,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Add LazyLock sync primitive. - Add `clear`, `len`, `is_empty` and `is_full` functions to `zerocopy_channel`. -- Add `capacity`, `free_capacity`, `clear`, `len`, `is_empty` and `is_full` functions to `Channel::{Sender, Receiver}`. +- Add `capacity`, `free_capacity`, `clear`, `len`, `is_empty` and `is_full` functions to `channel::{Sender, Receiver}`. ## 0.6.0 - 2024-05-29 From 07748131dde887d214c1d9373ec642907d547dcd Mon Sep 17 00:00:00 2001 From: Oliver Rockstedt Date: Mon, 7 Oct 2024 17:24:56 +0200 Subject: [PATCH 049/361] embassy-sync: fixed link to priority_channel in README --- 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 3dcd9dcc8..6871bcabc 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_channel::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. +- [`PriorityChannel`](priority_channel::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. - [`Watch`](watch::Watch) - Signalling latest value to multiple consumers. From 2704ac3d289650173b60e1a29d70e8903bea4cf1 Mon Sep 17 00:00:00 2001 From: Oliver Rockstedt Date: Mon, 7 Oct 2024 17:35:11 +0200 Subject: [PATCH 050/361] Add capacity, free_capacity, clear, len, is_empty and is_full functions to priority_channel::{Sender, Receiver} --- embassy-sync/CHANGELOG.md | 1 + embassy-sync/src/priority_channel.rs | 84 ++++++++++++++++++++++++++++ 2 files changed, 85 insertions(+) diff --git a/embassy-sync/CHANGELOG.md b/embassy-sync/CHANGELOG.md index 83fd666ac..1668c9319 100644 --- a/embassy-sync/CHANGELOG.md +++ b/embassy-sync/CHANGELOG.md @@ -10,6 +10,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Add LazyLock sync primitive. - Add `clear`, `len`, `is_empty` and `is_full` functions to `zerocopy_channel`. - Add `capacity`, `free_capacity`, `clear`, `len`, `is_empty` and `is_full` functions to `channel::{Sender, Receiver}`. +- Add `capacity`, `free_capacity`, `clear`, `len`, `is_empty` and `is_full` functions to `priority_channel::{Sender, Receiver}`. ## 0.6.0 - 2024-05-29 diff --git a/embassy-sync/src/priority_channel.rs b/embassy-sync/src/priority_channel.rs index 24c6c5a7f..1f4d8667c 100644 --- a/embassy-sync/src/priority_channel.rs +++ b/embassy-sync/src/priority_channel.rs @@ -71,6 +71,48 @@ where pub fn poll_ready_to_send(&self, cx: &mut Context<'_>) -> Poll<()> { self.channel.poll_ready_to_send(cx) } + + /// Returns the maximum number of elements the channel can hold. + /// + /// See [`PriorityChannel::capacity()`] + pub const fn capacity(&self) -> usize { + self.channel.capacity() + } + + /// Returns the free capacity of the channel. + /// + /// See [`PriorityChannel::free_capacity()`] + pub fn free_capacity(&self) -> usize { + self.channel.free_capacity() + } + + /// Clears all elements in the channel. + /// + /// See [`PriorityChannel::clear()`] + pub fn clear(&self) { + self.channel.clear(); + } + + /// Returns the number of elements currently in the channel. + /// + /// See [`PriorityChannel::len()`] + pub fn len(&self) -> usize { + self.channel.len() + } + + /// Returns whether the channel is empty. + /// + /// See [`PriorityChannel::is_empty()`] + pub fn is_empty(&self) -> bool { + self.channel.is_empty() + } + + /// Returns whether the channel is full. + /// + /// See [`PriorityChannel::is_full()`] + pub fn is_full(&self) -> bool { + self.channel.is_full() + } } impl<'ch, M, T, K, const N: usize> From> for DynamicSender<'ch, T> @@ -146,6 +188,48 @@ where pub fn poll_receive(&self, cx: &mut Context<'_>) -> Poll { self.channel.poll_receive(cx) } + + /// Returns the maximum number of elements the channel can hold. + /// + /// See [`PriorityChannel::capacity()`] + pub const fn capacity(&self) -> usize { + self.channel.capacity() + } + + /// Returns the free capacity of the channel. + /// + /// See [`PriorityChannel::free_capacity()`] + pub fn free_capacity(&self) -> usize { + self.channel.free_capacity() + } + + /// Clears all elements in the channel. + /// + /// See [`PriorityChannel::clear()`] + pub fn clear(&self) { + self.channel.clear(); + } + + /// Returns the number of elements currently in the channel. + /// + /// See [`PriorityChannel::len()`] + pub fn len(&self) -> usize { + self.channel.len() + } + + /// Returns whether the channel is empty. + /// + /// See [`PriorityChannel::is_empty()`] + pub fn is_empty(&self) -> bool { + self.channel.is_empty() + } + + /// Returns whether the channel is full. + /// + /// See [`PriorityChannel::is_full()`] + pub fn is_full(&self) -> bool { + self.channel.is_full() + } } impl<'ch, M, T, K, const N: usize> From> for DynamicReceiver<'ch, T> From bf60b239e87faa0b9905a42013d8ae9a9f4162ea Mon Sep 17 00:00:00 2001 From: Oliver Rockstedt Date: Mon, 7 Oct 2024 18:05:15 +0200 Subject: [PATCH 051/361] embassy-sync: fixed some clippy warnings --- embassy-sync/src/blocking_mutex/mod.rs | 1 + embassy-sync/src/mutex.rs | 2 +- embassy-sync/src/pubsub/mod.rs | 4 ++-- 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/embassy-sync/src/blocking_mutex/mod.rs b/embassy-sync/src/blocking_mutex/mod.rs index 8a4a4c642..beafdb43d 100644 --- a/embassy-sync/src/blocking_mutex/mod.rs +++ b/embassy-sync/src/blocking_mutex/mod.rs @@ -104,6 +104,7 @@ impl Mutex { impl Mutex { /// Borrows the data + #[allow(clippy::should_implement_trait)] pub fn borrow(&self) -> &T { let ptr = self.data.get() as *const T; unsafe { &*ptr } diff --git a/embassy-sync/src/mutex.rs b/embassy-sync/src/mutex.rs index 8c3a3af9f..08f66e374 100644 --- a/embassy-sync/src/mutex.rs +++ b/embassy-sync/src/mutex.rs @@ -138,7 +138,7 @@ impl From for Mutex { impl Default for Mutex where M: RawMutex, - T: ?Sized + Default, + T: Default, { fn default() -> Self { Self::new(Default::default()) diff --git a/embassy-sync/src/pubsub/mod.rs b/embassy-sync/src/pubsub/mod.rs index 812302e2b..ae5951829 100644 --- a/embassy-sync/src/pubsub/mod.rs +++ b/embassy-sync/src/pubsub/mod.rs @@ -27,8 +27,8 @@ pub use subscriber::{DynSubscriber, Subscriber}; /// /// - With [Pub::publish()] the publisher has to wait until there is space in the internal message queue. /// - With [Pub::publish_immediate()] the publisher doesn't await and instead lets the oldest message -/// in the queue drop if necessary. This will cause any [Subscriber] that missed the message to receive -/// an error to indicate that it has lagged. +/// in the queue drop if necessary. This will cause any [Subscriber] that missed the message to receive +/// an error to indicate that it has lagged. /// /// ## Example /// From 4110cb494fa21184f46dbbc2fd81baaeb3d0dc26 Mon Sep 17 00:00:00 2001 From: Oliver Rockstedt Date: Mon, 7 Oct 2024 18:12:45 +0200 Subject: [PATCH 052/361] embassy-sync: added Watch primitive to changelog --- embassy-sync/CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/embassy-sync/CHANGELOG.md b/embassy-sync/CHANGELOG.md index 1668c9319..af5682560 100644 --- a/embassy-sync/CHANGELOG.md +++ b/embassy-sync/CHANGELOG.md @@ -8,6 +8,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## Unreleased - Add LazyLock sync primitive. +- Add `Watch` sync primitive. - Add `clear`, `len`, `is_empty` and `is_full` functions to `zerocopy_channel`. - Add `capacity`, `free_capacity`, `clear`, `len`, `is_empty` and `is_full` functions to `channel::{Sender, Receiver}`. - Add `capacity`, `free_capacity`, `clear`, `len`, `is_empty` and `is_full` functions to `priority_channel::{Sender, Receiver}`. From 592bb5a8ca8138b95ba878cd6e509c0a21d088d5 Mon Sep 17 00:00:00 2001 From: Oliver Rockstedt Date: Mon, 7 Oct 2024 18:16:47 +0200 Subject: [PATCH 053/361] embassy-sync: made changelog formatting more consistent --- embassy-sync/CHANGELOG.md | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/embassy-sync/CHANGELOG.md b/embassy-sync/CHANGELOG.md index af5682560..a7dd6f66e 100644 --- a/embassy-sync/CHANGELOG.md +++ b/embassy-sync/CHANGELOG.md @@ -7,7 +7,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## Unreleased -- Add LazyLock sync primitive. +- Add `LazyLock` sync primitive. - Add `Watch` sync primitive. - Add `clear`, `len`, `is_empty` and `is_full` functions to `zerocopy_channel`. - Add `capacity`, `free_capacity`, `clear`, `len`, `is_empty` and `is_full` functions to `channel::{Sender, Receiver}`. @@ -20,20 +20,20 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Add `capacity`, `free_capacity`, `clear`, `len`, `is_empty` and `is_full` functions to `PubSubChannel`. - Made `PubSubBehavior` sealed - If you called `.publish_immediate(...)` on the queue directly before, then now call `.immediate_publisher().publish_immediate(...)` -- Add OnceLock sync primitive. -- Add constructor for DynamicChannel -- Add ready_to_receive functions to Channel and Receiver. +- Add `OnceLock` sync primitive. +- Add constructor for `DynamicChannel` +- Add ready_to_receive functions to `Channel` and `Receiver`. ## 0.5.0 - 2023-12-04 -- Add a PriorityChannel. -- Remove nightly and unstable-traits features in preparation for 1.75. -- Upgrade heapless to 0.8. -- Upgrade static-cell to 2.0. +- Add a `PriorityChannel`. +- Remove `nightly` and `unstable-traits` features in preparation for 1.75. +- Upgrade `heapless` to 0.8. +- Upgrade `static-cell` to 2.0. ## 0.4.0 - 2023-10-31 -- Re-add impl_trait_projections +- Re-add `impl_trait_projections` - switch to `embedded-io 0.6` ## 0.3.0 - 2023-09-14 From df0fc041981105a19beb690e30c3ab1f532d7298 Mon Sep 17 00:00:00 2001 From: Lena Berlin Date: Wed, 11 Sep 2024 11:36:24 -0400 Subject: [PATCH 054/361] fix: stm32l0 low-power EXTI IRQ handler wiped pending bits before they were checked --- embassy-stm32/src/exti.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/embassy-stm32/src/exti.rs b/embassy-stm32/src/exti.rs index 224d51b84..87512c92d 100644 --- a/embassy-stm32/src/exti.rs +++ b/embassy-stm32/src/exti.rs @@ -41,9 +41,6 @@ fn exticr_regs() -> pac::afio::Afio { } unsafe fn on_irq() { - #[cfg(feature = "low-power")] - crate::low_power::on_wakeup_irq(); - #[cfg(not(any(exti_c0, exti_g0, exti_u0, exti_l5, exti_u5, exti_h5, exti_h50)))] let bits = EXTI.pr(0).read().0; #[cfg(any(exti_c0, exti_g0, exti_u0, exti_l5, exti_u5, exti_h5, exti_h50))] @@ -68,6 +65,9 @@ unsafe fn on_irq() { EXTI.rpr(0).write_value(Lines(bits)); EXTI.fpr(0).write_value(Lines(bits)); } + + #[cfg(feature = "low-power")] + crate::low_power::on_wakeup_irq(); } struct BitIter(u32); From 57c1fbf3089e2a2dc9fe5b7d1f1e094596566395 Mon Sep 17 00:00:00 2001 From: Caleb Jamison Date: Wed, 9 Oct 2024 10:04:35 -0400 Subject: [PATCH 055/361] Move pio programs into embassy-rp --- embassy-rp/Cargo.toml | 1 + embassy-rp/src/lib.rs | 1 + embassy-rp/src/pio_programs/hd44780.rs | 203 ++++++++++++++++ embassy-rp/src/pio_programs/i2s.rs | 97 ++++++++ embassy-rp/src/pio_programs/mod.rs | 10 + embassy-rp/src/pio_programs/onewire.rs | 109 +++++++++ embassy-rp/src/pio_programs/pwm.rs | 114 +++++++++ embassy-rp/src/pio_programs/rotary_encoder.rs | 72 ++++++ embassy-rp/src/pio_programs/stepper.rs | 146 ++++++++++++ embassy-rp/src/pio_programs/uart.rs | 186 +++++++++++++++ embassy-rp/src/pio_programs/ws2812.rs | 118 ++++++++++ examples/rp/Cargo.toml | 2 +- examples/rp/src/bin/pio_hd44780.rs | 201 ++-------------- examples/rp/src/bin/pio_i2s.rs | 71 ++---- examples/rp/src/bin/pio_onewire.rs | 98 +------- examples/rp/src/bin/pio_pwm.rs | 90 +------ examples/rp/src/bin/pio_rotary_encoder.rs | 91 +++---- examples/rp/src/bin/pio_servo.rs | 96 +------- examples/rp/src/bin/pio_stepper.rs | 135 +---------- examples/rp/src/bin/pio_uart.rs | 222 ++---------------- examples/rp/src/bin/pio_ws2812.rs | 105 +-------- examples/rp23/src/bin/pio_hd44780.rs | 201 ++-------------- examples/rp23/src/bin/pio_i2s.rs | 77 ++---- examples/rp23/src/bin/pio_onewire.rs | 88 +++++++ examples/rp23/src/bin/pio_pwm.rs | 90 +------ examples/rp23/src/bin/pio_rotary_encoder.rs | 91 +++---- examples/rp23/src/bin/pio_servo.rs | 96 +------- examples/rp23/src/bin/pio_stepper.rs | 135 +---------- examples/rp23/src/bin/pio_uart.rs | 203 ++++++++++++++++ examples/rp23/src/bin/pio_ws2812.rs | 105 +-------- 30 files changed, 1590 insertions(+), 1664 deletions(-) create mode 100644 embassy-rp/src/pio_programs/hd44780.rs create mode 100644 embassy-rp/src/pio_programs/i2s.rs create mode 100644 embassy-rp/src/pio_programs/mod.rs create mode 100644 embassy-rp/src/pio_programs/onewire.rs create mode 100644 embassy-rp/src/pio_programs/pwm.rs create mode 100644 embassy-rp/src/pio_programs/rotary_encoder.rs create mode 100644 embassy-rp/src/pio_programs/stepper.rs create mode 100644 embassy-rp/src/pio_programs/uart.rs create mode 100644 embassy-rp/src/pio_programs/ws2812.rs create mode 100644 examples/rp23/src/bin/pio_onewire.rs create mode 100644 examples/rp23/src/bin/pio_uart.rs diff --git a/embassy-rp/Cargo.toml b/embassy-rp/Cargo.toml index 29a8a3c53..54de238b3 100644 --- a/embassy-rp/Cargo.toml +++ b/embassy-rp/Cargo.toml @@ -144,6 +144,7 @@ rp2040-boot2 = "0.3" document-features = "0.2.7" sha2-const-stable = "0.1" rp-binary-info = { version = "0.1.0", optional = true } +smart-leds = "0.4.0" [dev-dependencies] embassy-executor = { version = "0.6.0", path = "../embassy-executor", features = ["arch-std", "executor-thread"] } diff --git a/embassy-rp/src/lib.rs b/embassy-rp/src/lib.rs index d402cf793..7ac18c1f8 100644 --- a/embassy-rp/src/lib.rs +++ b/embassy-rp/src/lib.rs @@ -34,6 +34,7 @@ pub mod i2c_slave; pub mod multicore; #[cfg(feature = "_rp235x")] pub mod otp; +pub mod pio_programs; pub mod pwm; mod reset; pub mod rom_data; diff --git a/embassy-rp/src/pio_programs/hd44780.rs b/embassy-rp/src/pio_programs/hd44780.rs new file mode 100644 index 000000000..9bbf44fc4 --- /dev/null +++ b/embassy-rp/src/pio_programs/hd44780.rs @@ -0,0 +1,203 @@ +//! [HD44780 display driver](https://www.sparkfun.com/datasheets/LCD/HD44780.pdf) + +use crate::dma::{AnyChannel, Channel}; +use crate::pio::{ + Common, Config, Direction, FifoJoin, Instance, Irq, LoadedProgram, PioPin, ShiftConfig, ShiftDirection, + StateMachine, +}; +use crate::{into_ref, Peripheral, PeripheralRef}; + +/// This struct represents a HD44780 program that takes command words ( <0:4>) +pub struct PioHD44780CommandWordProgram<'a, PIO: Instance> { + prg: LoadedProgram<'a, PIO>, +} + +impl<'a, PIO: Instance> PioHD44780CommandWordProgram<'a, PIO> { + /// Load the program into the given pio + pub fn new(common: &mut Common<'a, PIO>) -> Self { + let prg = pio_proc::pio_asm!( + r#" + .side_set 1 opt + .origin 20 + + loop: + out x, 24 + delay: + jmp x--, delay + out pins, 4 side 1 + out null, 4 side 0 + jmp !osre, loop + irq 0 + "#, + ); + + let prg = common.load_program(&prg.program); + + Self { prg } + } +} + +/// This struct represents a HD44780 program that takes command sequences ( , data...) +pub struct PioHD44780CommandSequenceProgram<'a, PIO: Instance> { + prg: LoadedProgram<'a, PIO>, +} + +impl<'a, PIO: Instance> PioHD44780CommandSequenceProgram<'a, PIO> { + /// Load the program into the given pio + pub fn new(common: &mut Common<'a, PIO>) -> Self { + // many side sets are only there to free up a delay bit! + let prg = pio_proc::pio_asm!( + r#" + .origin 27 + .side_set 1 + + .wrap_target + pull side 0 + out x 1 side 0 ; !rs + out y 7 side 0 ; #data - 1 + + ; rs/rw to e: >= 60ns + ; e high time: >= 500ns + ; e low time: >= 500ns + ; read data valid after e falling: ~5ns + ; write data hold after e falling: ~10ns + + loop: + pull side 0 + jmp !x data side 0 + command: + set pins 0b00 side 0 + jmp shift side 0 + data: + set pins 0b01 side 0 + shift: + out pins 4 side 1 [9] + nop side 0 [9] + out pins 4 side 1 [9] + mov osr null side 0 [7] + out pindirs 4 side 0 + set pins 0b10 side 0 + busy: + nop side 1 [9] + jmp pin more side 0 [9] + mov osr ~osr side 1 [9] + nop side 0 [4] + out pindirs 4 side 0 + jmp y-- loop side 0 + .wrap + more: + nop side 1 [9] + jmp busy side 0 [9] + "# + ); + + let prg = common.load_program(&prg.program); + + Self { prg } + } +} + +/// Pio backed HD44780 driver +pub struct PioHD44780<'l, P: Instance, const S: usize> { + dma: PeripheralRef<'l, AnyChannel>, + sm: StateMachine<'l, P, S>, + + buf: [u8; 40], +} + +impl<'l, P: Instance, const S: usize> PioHD44780<'l, P, S> { + /// Configure the given state machine to first init, then write data to, a HD44780 display. + pub async fn new( + common: &mut Common<'l, P>, + mut sm: StateMachine<'l, P, S>, + mut irq: Irq<'l, P, S>, + dma: impl Peripheral

+ 'l, + rs: impl PioPin, + rw: impl PioPin, + e: impl PioPin, + db4: impl PioPin, + db5: impl PioPin, + db6: impl PioPin, + db7: impl PioPin, + word_prg: &PioHD44780CommandWordProgram<'l, P>, + seq_prg: &PioHD44780CommandSequenceProgram<'l, P>, + ) -> PioHD44780<'l, P, S> { + into_ref!(dma); + + let rs = common.make_pio_pin(rs); + let rw = common.make_pio_pin(rw); + let e = common.make_pio_pin(e); + let db4 = common.make_pio_pin(db4); + let db5 = common.make_pio_pin(db5); + let db6 = common.make_pio_pin(db6); + let db7 = common.make_pio_pin(db7); + + sm.set_pin_dirs(Direction::Out, &[&rs, &rw, &e, &db4, &db5, &db6, &db7]); + + let mut cfg = Config::default(); + cfg.use_program(&word_prg.prg, &[&e]); + cfg.clock_divider = 125u8.into(); + cfg.set_out_pins(&[&db4, &db5, &db6, &db7]); + cfg.shift_out = ShiftConfig { + auto_fill: true, + direction: ShiftDirection::Left, + threshold: 32, + }; + cfg.fifo_join = FifoJoin::TxOnly; + sm.set_config(&cfg); + + sm.set_enable(true); + // init to 8 bit thrice + sm.tx().push((50000 << 8) | 0x30); + sm.tx().push((5000 << 8) | 0x30); + sm.tx().push((200 << 8) | 0x30); + // init 4 bit + sm.tx().push((200 << 8) | 0x20); + // set font and lines + sm.tx().push((50 << 8) | 0x20); + sm.tx().push(0b1100_0000); + + irq.wait().await; + sm.set_enable(false); + + let mut cfg = Config::default(); + cfg.use_program(&seq_prg.prg, &[&e]); + cfg.clock_divider = 8u8.into(); // ~64ns/insn + cfg.set_jmp_pin(&db7); + cfg.set_set_pins(&[&rs, &rw]); + cfg.set_out_pins(&[&db4, &db5, &db6, &db7]); + cfg.shift_out.direction = ShiftDirection::Left; + cfg.fifo_join = FifoJoin::TxOnly; + sm.set_config(&cfg); + + sm.set_enable(true); + + // display on and cursor on and blinking, reset display + sm.tx().dma_push(dma.reborrow(), &[0x81u8, 0x0f, 1]).await; + + Self { + dma: dma.map_into(), + sm, + buf: [0x20; 40], + } + } + + /// Write a line to the display + pub async fn add_line(&mut self, s: &[u8]) { + // move cursor to 0:0, prepare 16 characters + self.buf[..3].copy_from_slice(&[0x80, 0x80, 15]); + // move line 2 up + self.buf.copy_within(22..38, 3); + // move cursor to 1:0, prepare 16 characters + self.buf[19..22].copy_from_slice(&[0x80, 0xc0, 15]); + // file line 2 with spaces + self.buf[22..38].fill(0x20); + // copy input line + let len = s.len().min(16); + self.buf[22..22 + len].copy_from_slice(&s[0..len]); + // set cursor to 1:15 + self.buf[38..].copy_from_slice(&[0x80, 0xcf]); + + self.sm.tx().dma_push(self.dma.reborrow(), &self.buf).await; + } +} diff --git a/embassy-rp/src/pio_programs/i2s.rs b/embassy-rp/src/pio_programs/i2s.rs new file mode 100644 index 000000000..3c8ef8bb6 --- /dev/null +++ b/embassy-rp/src/pio_programs/i2s.rs @@ -0,0 +1,97 @@ +//! Pio backed I2s output + +use crate::{ + dma::{AnyChannel, Channel, Transfer}, + into_ref, + pio::{ + Common, Config, Direction, FifoJoin, Instance, LoadedProgram, PioPin, ShiftConfig, ShiftDirection, StateMachine, + }, + Peripheral, PeripheralRef, +}; +use fixed::traits::ToFixed; + +/// This struct represents an i2s output driver program +pub struct PioI2sOutProgram<'a, PIO: Instance> { + prg: LoadedProgram<'a, PIO>, +} + +impl<'a, PIO: Instance> PioI2sOutProgram<'a, PIO> { + /// Load the program into the given pio + pub fn new(common: &mut Common<'a, PIO>) -> Self { + let prg = pio_proc::pio_asm!( + ".side_set 2", + " set x, 14 side 0b01", // side 0bWB - W = Word Clock, B = Bit Clock + "left_data:", + " out pins, 1 side 0b00", + " jmp x-- left_data side 0b01", + " out pins 1 side 0b10", + " set x, 14 side 0b11", + "right_data:", + " out pins 1 side 0b10", + " jmp x-- right_data side 0b11", + " out pins 1 side 0b00", + ); + + let prg = common.load_program(&prg.program); + + Self { prg } + } +} + +/// Pio backed I2s output driver +pub struct PioI2sOut<'a, P: Instance, const S: usize> { + dma: PeripheralRef<'a, AnyChannel>, + sm: StateMachine<'a, P, S>, +} + +impl<'a, P: Instance, const S: usize> PioI2sOut<'a, P, S> { + /// Configure a state machine to output I2s + pub fn new( + common: &mut Common<'a, P>, + mut sm: StateMachine<'a, P, S>, + dma: impl Peripheral

+ 'a, + data_pin: impl PioPin, + bit_clock_pin: impl PioPin, + lr_clock_pin: impl PioPin, + sample_rate: u32, + bit_depth: u32, + channels: u32, + program: &PioI2sOutProgram<'a, P>, + ) -> Self { + into_ref!(dma); + + let data_pin = common.make_pio_pin(data_pin); + let bit_clock_pin = common.make_pio_pin(bit_clock_pin); + let left_right_clock_pin = common.make_pio_pin(lr_clock_pin); + + let cfg = { + let mut cfg = Config::default(); + cfg.use_program(&program.prg, &[&bit_clock_pin, &left_right_clock_pin]); + cfg.set_out_pins(&[&data_pin]); + let clock_frequency = sample_rate * bit_depth * channels; + cfg.clock_divider = (125_000_000. / clock_frequency as f64 / 2.).to_fixed(); + cfg.shift_out = ShiftConfig { + threshold: 32, + direction: ShiftDirection::Left, + auto_fill: true, + }; + // join fifos to have twice the time to start the next dma transfer + cfg.fifo_join = FifoJoin::TxOnly; + cfg + }; + sm.set_config(&cfg); + sm.set_pin_dirs(Direction::Out, &[&data_pin, &left_right_clock_pin, &bit_clock_pin]); + + sm.set_enable(true); + + Self { + dma: dma.map_into(), + sm, + } + } + + /// Return an in-prograss dma transfer future. Awaiting it will guarentee a complete transfer. + pub fn write<'b>(&'b mut self, buff: &'b [u32]) -> Transfer<'b, AnyChannel> { + self.sm.tx().dma_push(self.dma.reborrow(), buff) + } +} diff --git a/embassy-rp/src/pio_programs/mod.rs b/embassy-rp/src/pio_programs/mod.rs new file mode 100644 index 000000000..74537825b --- /dev/null +++ b/embassy-rp/src/pio_programs/mod.rs @@ -0,0 +1,10 @@ +//! Pre-built pio programs for common interfaces + +pub mod hd44780; +pub mod i2s; +pub mod onewire; +pub mod pwm; +pub mod rotary_encoder; +pub mod stepper; +pub mod uart; +pub mod ws2812; diff --git a/embassy-rp/src/pio_programs/onewire.rs b/embassy-rp/src/pio_programs/onewire.rs new file mode 100644 index 000000000..f3bc5fcd7 --- /dev/null +++ b/embassy-rp/src/pio_programs/onewire.rs @@ -0,0 +1,109 @@ +//! OneWire pio driver + +use crate::pio::{self, Common, Config, Instance, LoadedProgram, PioPin, ShiftConfig, ShiftDirection, StateMachine}; + +/// This struct represents an onewire driver program +pub struct PioOneWireProgram<'a, PIO: Instance> { + prg: LoadedProgram<'a, PIO>, +} + +impl<'a, PIO: Instance> PioOneWireProgram<'a, PIO> { + /// Load the program into the given pio + pub fn new(common: &mut Common<'a, PIO>) -> Self { + let prg = pio_proc::pio_asm!( + r#" + .wrap_target + again: + pull block + mov x, osr + jmp !x, read + write: + set pindirs, 1 + set pins, 0 + loop1: + jmp x--,loop1 + set pindirs, 0 [31] + wait 1 pin 0 [31] + pull block + mov x, osr + bytes1: + pull block + set y, 7 + set pindirs, 1 + bit1: + set pins, 0 [1] + out pins,1 [31] + set pins, 1 [20] + jmp y--,bit1 + jmp x--,bytes1 + set pindirs, 0 [31] + jmp again + read: + pull block + mov x, osr + bytes2: + set y, 7 + bit2: + set pindirs, 1 + set pins, 0 [1] + set pindirs, 0 [5] + in pins,1 [10] + jmp y--,bit2 + jmp x--,bytes2 + .wrap + "#, + ); + let prg = common.load_program(&prg.program); + + Self { prg } + } +} + +/// Pio backed OneWire driver +pub struct PioOneWire<'d, PIO: pio::Instance, const SM: usize> { + sm: StateMachine<'d, PIO, SM>, +} + +impl<'d, PIO: pio::Instance, const SM: usize> PioOneWire<'d, PIO, SM> { + /// Create a new instance the driver + pub fn new( + common: &mut Common<'d, PIO>, + mut sm: StateMachine<'d, PIO, SM>, + pin: impl PioPin, + program: &PioOneWireProgram<'d, PIO>, + ) -> Self { + let pin = common.make_pio_pin(pin); + let mut cfg = Config::default(); + cfg.use_program(&program.prg, &[]); + cfg.set_out_pins(&[&pin]); + cfg.set_in_pins(&[&pin]); + cfg.set_set_pins(&[&pin]); + cfg.shift_in = ShiftConfig { + auto_fill: true, + direction: ShiftDirection::Right, + threshold: 8, + }; + cfg.clock_divider = 255_u8.into(); + sm.set_config(&cfg); + sm.set_enable(true); + Self { sm } + } + + /// Write bytes over the wire + pub async fn write_bytes(&mut self, bytes: &[u8]) { + self.sm.tx().wait_push(250).await; + self.sm.tx().wait_push(bytes.len() as u32 - 1).await; + for b in bytes { + self.sm.tx().wait_push(*b as u32).await; + } + } + + /// Read bytes from the wire + pub async fn read_bytes(&mut self, bytes: &mut [u8]) { + self.sm.tx().wait_push(0).await; + self.sm.tx().wait_push(bytes.len() as u32 - 1).await; + for b in bytes.iter_mut() { + *b = (self.sm.rx().wait_pull().await >> 24) as u8; + } + } +} diff --git a/embassy-rp/src/pio_programs/pwm.rs b/embassy-rp/src/pio_programs/pwm.rs new file mode 100644 index 000000000..5dfd70cc9 --- /dev/null +++ b/embassy-rp/src/pio_programs/pwm.rs @@ -0,0 +1,114 @@ +//! PIO backed PWM driver + +use core::time::Duration; + +use crate::{ + clocks, + gpio::Level, + pio::{Common, Config, Direction, Instance, LoadedProgram, PioPin, StateMachine}, +}; +use pio::InstructionOperands; + +fn to_pio_cycles(duration: Duration) -> u32 { + (clocks::clk_sys_freq() / 1_000_000) / 3 * duration.as_micros() as u32 // parentheses are required to prevent overflow +} + +/// This struct represents a PWM program loaded into pio instruction memory. +pub struct PioPwmProgram<'a, PIO: Instance> { + prg: LoadedProgram<'a, PIO>, +} + +impl<'a, PIO: Instance> PioPwmProgram<'a, PIO> { + /// Load the program into the given pio + pub fn new(common: &mut Common<'a, PIO>) -> Self { + let prg = pio_proc::pio_asm!( + ".side_set 1 opt" + "pull noblock side 0" + "mov x, osr" + "mov y, isr" + "countloop:" + "jmp x!=y noset" + "jmp skip side 1" + "noset:" + "nop" + "skip:" + "jmp y-- countloop" + ); + + let prg = common.load_program(&prg.program); + + Self { prg } + } +} + +/// Pio backed PWM output +pub struct PioPwm<'d, T: Instance, const SM: usize> { + sm: StateMachine<'d, T, SM>, +} + +impl<'d, T: Instance, const SM: usize> PioPwm<'d, T, SM> { + /// Configure a state machine as a PWM output + pub fn new( + pio: &mut Common<'d, T>, + mut sm: StateMachine<'d, T, SM>, + pin: impl PioPin, + program: &PioPwmProgram<'d, T>, + ) -> Self { + let pin = pio.make_pio_pin(pin); + sm.set_pins(Level::High, &[&pin]); + sm.set_pin_dirs(Direction::Out, &[&pin]); + + let mut cfg = Config::default(); + cfg.use_program(&program.prg, &[&pin]); + + sm.set_config(&cfg); + + Self { sm } + } + + /// Enable PWM output + pub fn start(&mut self) { + self.sm.set_enable(true); + } + + /// Disable PWM output + pub fn stop(&mut self) { + self.sm.set_enable(false); + } + + /// Set pwm period + pub fn set_period(&mut self, duration: Duration) { + let is_enabled = self.sm.is_enabled(); + while !self.sm.tx().empty() {} // Make sure that the queue is empty + self.sm.set_enable(false); + self.sm.tx().push(to_pio_cycles(duration)); + unsafe { + self.sm.exec_instr( + InstructionOperands::PULL { + if_empty: false, + block: false, + } + .encode(), + ); + self.sm.exec_instr( + InstructionOperands::OUT { + destination: ::pio::OutDestination::ISR, + bit_count: 32, + } + .encode(), + ); + }; + if is_enabled { + self.sm.set_enable(true) // Enable if previously enabled + } + } + + fn set_level(&mut self, level: u32) { + self.sm.tx().push(level); + } + + /// Set the pulse width high time + pub fn write(&mut self, duration: Duration) { + self.set_level(to_pio_cycles(duration)); + } +} diff --git a/embassy-rp/src/pio_programs/rotary_encoder.rs b/embassy-rp/src/pio_programs/rotary_encoder.rs new file mode 100644 index 000000000..323f839bc --- /dev/null +++ b/embassy-rp/src/pio_programs/rotary_encoder.rs @@ -0,0 +1,72 @@ +//! PIO backed quadrature encoder + +use crate::gpio::Pull; +use crate::pio::{self, Common, Config, FifoJoin, Instance, LoadedProgram, PioPin, ShiftDirection, StateMachine}; +use fixed::traits::ToFixed; + +/// This struct represents an Encoder program loaded into pio instruction memory. +pub struct PioEncoderProgram<'a, PIO: Instance> { + prg: LoadedProgram<'a, PIO>, +} + +impl<'a, PIO: Instance> PioEncoderProgram<'a, PIO> { + /// Load the program into the given pio + pub fn new(common: &mut Common<'a, PIO>) -> Self { + let prg = pio_proc::pio_asm!("wait 1 pin 1", "wait 0 pin 1", "in pins, 2", "push",); + + let prg = common.load_program(&prg.program); + + Self { prg } + } +} + +/// Pio Backed quadrature encoder reader +pub struct PioEncoder<'d, T: Instance, const SM: usize> { + sm: StateMachine<'d, T, SM>, +} + +impl<'d, T: Instance, const SM: usize> PioEncoder<'d, T, SM> { + /// Configure a state machine with the loaded [PioEncoderProgram] + pub fn new( + pio: &mut Common<'d, T>, + mut sm: StateMachine<'d, T, SM>, + pin_a: impl PioPin, + pin_b: impl PioPin, + program: &PioEncoderProgram<'d, T>, + ) -> Self { + let mut pin_a = pio.make_pio_pin(pin_a); + let mut pin_b = pio.make_pio_pin(pin_b); + pin_a.set_pull(Pull::Up); + pin_b.set_pull(Pull::Up); + sm.set_pin_dirs(pio::Direction::In, &[&pin_a, &pin_b]); + + let mut cfg = Config::default(); + cfg.set_in_pins(&[&pin_a, &pin_b]); + cfg.fifo_join = FifoJoin::RxOnly; + cfg.shift_in.direction = ShiftDirection::Left; + cfg.clock_divider = 10_000.to_fixed(); + cfg.use_program(&program.prg, &[]); + sm.set_config(&cfg); + sm.set_enable(true); + Self { sm } + } + + /// Read a single count from the encoder + pub async fn read(&mut self) -> Direction { + loop { + match self.sm.rx().wait_pull().await { + 0 => return Direction::CounterClockwise, + 1 => return Direction::Clockwise, + _ => {} + } + } + } +} + +/// Encoder Count Direction +pub enum Direction { + /// Encoder turned clockwise + Clockwise, + /// Encoder turned counter clockwise + CounterClockwise, +} diff --git a/embassy-rp/src/pio_programs/stepper.rs b/embassy-rp/src/pio_programs/stepper.rs new file mode 100644 index 000000000..0ecc4eff0 --- /dev/null +++ b/embassy-rp/src/pio_programs/stepper.rs @@ -0,0 +1,146 @@ +//! Pio Stepper Driver for 5-wire steppers + +use core::mem::{self, MaybeUninit}; + +use crate::pio::{Common, Config, Direction, Instance, Irq, LoadedProgram, PioPin, StateMachine}; +use fixed::traits::ToFixed; +use fixed::types::extra::U8; +use fixed::FixedU32; + +/// This struct represents a Stepper driver program loaded into pio instruction memory. +pub struct PioStepperProgram<'a, PIO: Instance> { + prg: LoadedProgram<'a, PIO>, +} + +impl<'a, PIO: Instance> PioStepperProgram<'a, PIO> { + /// Load the program into the given pio + pub fn new(common: &mut Common<'a, PIO>) -> Self { + let prg = pio_proc::pio_asm!( + "pull block", + "mov x, osr", + "pull block", + "mov y, osr", + "jmp !x end", + "loop:", + "jmp !osre step", + "mov osr, y", + "step:", + "out pins, 4 [31]" + "jmp x-- loop", + "end:", + "irq 0 rel" + ); + + let prg = common.load_program(&prg.program); + + Self { prg } + } +} + +/// Pio backed Stepper driver +pub struct PioStepper<'d, T: Instance, const SM: usize> { + irq: Irq<'d, T, SM>, + sm: StateMachine<'d, T, SM>, +} + +impl<'d, T: Instance, const SM: usize> PioStepper<'d, T, SM> { + /// Configure a state machine to drive a stepper + pub fn new( + pio: &mut Common<'d, T>, + mut sm: StateMachine<'d, T, SM>, + irq: Irq<'d, T, SM>, + pin0: impl PioPin, + pin1: impl PioPin, + pin2: impl PioPin, + pin3: impl PioPin, + program: &PioStepperProgram<'d, T>, + ) -> Self { + let pin0 = pio.make_pio_pin(pin0); + let pin1 = pio.make_pio_pin(pin1); + let pin2 = pio.make_pio_pin(pin2); + let pin3 = pio.make_pio_pin(pin3); + sm.set_pin_dirs(Direction::Out, &[&pin0, &pin1, &pin2, &pin3]); + let mut cfg = Config::default(); + cfg.set_out_pins(&[&pin0, &pin1, &pin2, &pin3]); + cfg.clock_divider = (125_000_000 / (100 * 136)).to_fixed(); + cfg.use_program(&program.prg, &[]); + sm.set_config(&cfg); + sm.set_enable(true); + Self { irq, sm } + } + + /// Set pulse frequency + pub fn set_frequency(&mut self, freq: u32) { + let clock_divider: FixedU32 = (125_000_000 / (freq * 136)).to_fixed(); + assert!(clock_divider <= 65536, "clkdiv must be <= 65536"); + assert!(clock_divider >= 1, "clkdiv must be >= 1"); + self.sm.set_clock_divider(clock_divider); + self.sm.clkdiv_restart(); + } + + /// Full step, one phase + pub async fn step(&mut self, steps: i32) { + if steps > 0 { + self.run(steps, 0b1000_0100_0010_0001_1000_0100_0010_0001).await + } else { + self.run(-steps, 0b0001_0010_0100_1000_0001_0010_0100_1000).await + } + } + + /// Full step, two phase + pub async fn step2(&mut self, steps: i32) { + if steps > 0 { + self.run(steps, 0b1001_1100_0110_0011_1001_1100_0110_0011).await + } else { + self.run(-steps, 0b0011_0110_1100_1001_0011_0110_1100_1001).await + } + } + + /// Half step + pub async fn step_half(&mut self, steps: i32) { + if steps > 0 { + self.run(steps, 0b1001_1000_1100_0100_0110_0010_0011_0001).await + } else { + self.run(-steps, 0b0001_0011_0010_0110_0100_1100_1000_1001).await + } + } + + async fn run(&mut self, steps: i32, pattern: u32) { + self.sm.tx().wait_push(steps as u32).await; + self.sm.tx().wait_push(pattern).await; + let drop = OnDrop::new(|| { + self.sm.clear_fifos(); + unsafe { + self.sm.exec_instr( + pio::InstructionOperands::JMP { + address: 0, + condition: pio::JmpCondition::Always, + } + .encode(), + ); + } + }); + self.irq.wait().await; + drop.defuse(); + } +} + +struct OnDrop { + f: MaybeUninit, +} + +impl OnDrop { + pub fn new(f: F) -> Self { + Self { f: MaybeUninit::new(f) } + } + + pub fn defuse(self) { + mem::forget(self) + } +} + +impl Drop for OnDrop { + fn drop(&mut self) { + unsafe { self.f.as_ptr().read()() } + } +} diff --git a/embassy-rp/src/pio_programs/uart.rs b/embassy-rp/src/pio_programs/uart.rs new file mode 100644 index 000000000..f4b3b204e --- /dev/null +++ b/embassy-rp/src/pio_programs/uart.rs @@ -0,0 +1,186 @@ +//! Pio backed uart drivers + +use crate::{ + clocks::clk_sys_freq, + gpio::Level, + pio::{ + Common, Config, Direction as PioDirection, FifoJoin, Instance, LoadedProgram, PioPin, ShiftDirection, + StateMachine, + }, +}; +use core::convert::Infallible; +use embedded_io_async::{ErrorType, Read, Write}; +use fixed::traits::ToFixed; + +/// This struct represents a uart tx program loaded into pio instruction memory. +pub struct PioUartTxProgram<'a, PIO: Instance> { + prg: LoadedProgram<'a, PIO>, +} + +impl<'a, PIO: Instance> PioUartTxProgram<'a, PIO> { + /// Load the uart tx program into the given pio + pub fn new(common: &mut Common<'a, PIO>) -> Self { + let prg = pio_proc::pio_asm!( + r#" + .side_set 1 opt + + ; An 8n1 UART transmit program. + ; OUT pin 0 and side-set pin 0 are both mapped to UART TX pin. + + pull side 1 [7] ; Assert stop bit, or stall with line in idle state + set x, 7 side 0 [7] ; Preload bit counter, assert start bit for 8 clocks + bitloop: ; This loop will run 8 times (8n1 UART) + out pins, 1 ; Shift 1 bit from OSR to the first OUT pin + jmp x-- bitloop [6] ; Each loop iteration is 8 cycles. + "# + ); + + let prg = common.load_program(&prg.program); + + Self { prg } + } +} + +/// PIO backed Uart transmitter +pub struct PioUartTx<'a, PIO: Instance, const SM: usize> { + sm_tx: StateMachine<'a, PIO, SM>, +} + +impl<'a, PIO: Instance, const SM: usize> PioUartTx<'a, PIO, SM> { + /// Configure a pio state machine to use the loaded tx program. + pub fn new( + baud: u32, + common: &mut Common<'a, PIO>, + mut sm_tx: StateMachine<'a, PIO, SM>, + tx_pin: impl PioPin, + program: &PioUartTxProgram<'a, PIO>, + ) -> Self { + let tx_pin = common.make_pio_pin(tx_pin); + sm_tx.set_pins(Level::High, &[&tx_pin]); + sm_tx.set_pin_dirs(PioDirection::Out, &[&tx_pin]); + + let mut cfg = Config::default(); + + cfg.set_out_pins(&[&tx_pin]); + cfg.use_program(&program.prg, &[&tx_pin]); + cfg.shift_out.auto_fill = false; + cfg.shift_out.direction = ShiftDirection::Right; + cfg.fifo_join = FifoJoin::TxOnly; + cfg.clock_divider = (clk_sys_freq() / (8 * baud)).to_fixed(); + sm_tx.set_config(&cfg); + sm_tx.set_enable(true); + + Self { sm_tx } + } + + /// Write a single u8 + pub async fn write_u8(&mut self, data: u8) { + self.sm_tx.tx().wait_push(data as u32).await; + } +} + +impl ErrorType for PioUartTx<'_, PIO, SM> { + type Error = Infallible; +} + +impl Write for PioUartTx<'_, PIO, SM> { + async fn write(&mut self, buf: &[u8]) -> Result { + for byte in buf { + self.write_u8(*byte).await; + } + Ok(buf.len()) + } +} + +/// This struct represents a Uart Rx program loaded into pio instruction memory. +pub struct PioUartRxProgram<'a, PIO: Instance> { + prg: LoadedProgram<'a, PIO>, +} + +impl<'a, PIO: Instance> PioUartRxProgram<'a, PIO> { + /// Load the uart rx program into the given pio + pub fn new(common: &mut Common<'a, PIO>) -> Self { + let prg = pio_proc::pio_asm!( + r#" + ; Slightly more fleshed-out 8n1 UART receiver which handles framing errors and + ; break conditions more gracefully. + ; IN pin 0 and JMP pin are both mapped to the GPIO used as UART RX. + + start: + wait 0 pin 0 ; Stall until start bit is asserted + set x, 7 [10] ; Preload bit counter, then delay until halfway through + rx_bitloop: ; the first data bit (12 cycles incl wait, set). + in pins, 1 ; Shift data bit into ISR + jmp x-- rx_bitloop [6] ; Loop 8 times, each loop iteration is 8 cycles + jmp pin good_rx_stop ; Check stop bit (should be high) + + irq 4 rel ; Either a framing error or a break. Set a sticky flag, + wait 1 pin 0 ; and wait for line to return to idle state. + jmp start ; Don't push data if we didn't see good framing. + + good_rx_stop: ; No delay before returning to start; a little slack is + in null 24 + push ; important in case the TX clock is slightly too fast. + "# + ); + + let prg = common.load_program(&prg.program); + + Self { prg } + } +} + +/// PIO backed Uart reciever +pub struct PioUartRx<'a, PIO: Instance, const SM: usize> { + sm_rx: StateMachine<'a, PIO, SM>, +} + +impl<'a, PIO: Instance, const SM: usize> PioUartRx<'a, PIO, SM> { + /// Configure a pio state machine to use the loaded rx program. + pub fn new( + baud: u32, + common: &mut Common<'a, PIO>, + mut sm_rx: StateMachine<'a, PIO, SM>, + rx_pin: impl PioPin, + program: &PioUartRxProgram<'a, PIO>, + ) -> Self { + let mut cfg = Config::default(); + cfg.use_program(&program.prg, &[]); + + let rx_pin = common.make_pio_pin(rx_pin); + sm_rx.set_pins(Level::High, &[&rx_pin]); + cfg.set_in_pins(&[&rx_pin]); + cfg.set_jmp_pin(&rx_pin); + sm_rx.set_pin_dirs(PioDirection::In, &[&rx_pin]); + + cfg.clock_divider = (clk_sys_freq() / (8 * baud)).to_fixed(); + cfg.shift_in.auto_fill = false; + cfg.shift_in.direction = ShiftDirection::Right; + cfg.shift_in.threshold = 32; + cfg.fifo_join = FifoJoin::RxOnly; + sm_rx.set_config(&cfg); + sm_rx.set_enable(true); + + Self { sm_rx } + } + + /// Wait for a single u8 + pub async fn read_u8(&mut self) -> u8 { + self.sm_rx.rx().wait_pull().await as u8 + } +} + +impl ErrorType for PioUartRx<'_, PIO, SM> { + type Error = Infallible; +} + +impl Read for PioUartRx<'_, PIO, SM> { + async fn read(&mut self, buf: &mut [u8]) -> Result { + let mut i = 0; + while i < buf.len() { + buf[i] = self.read_u8().await; + i += 1; + } + Ok(i) + } +} diff --git a/embassy-rp/src/pio_programs/ws2812.rs b/embassy-rp/src/pio_programs/ws2812.rs new file mode 100644 index 000000000..3fc7e1017 --- /dev/null +++ b/embassy-rp/src/pio_programs/ws2812.rs @@ -0,0 +1,118 @@ +//! [ws2812](https://www.sparkfun.com/datasheets/LCD/HD44780.pdf) + +use crate::{ + clocks::clk_sys_freq, + dma::{AnyChannel, Channel}, + into_ref, + pio::{Common, Config, FifoJoin, Instance, LoadedProgram, PioPin, ShiftConfig, ShiftDirection, StateMachine}, + Peripheral, PeripheralRef, +}; +use embassy_time::Timer; +use fixed::types::U24F8; +use smart_leds::RGB8; + +const T1: u8 = 2; // start bit +const T2: u8 = 5; // data bit +const T3: u8 = 3; // stop bit +const CYCLES_PER_BIT: u32 = (T1 + T2 + T3) as u32; + +/// This struct represents a ws2812 program loaded into pio instruction memory. +pub struct PioWs2812Program<'a, PIO: Instance> { + prg: LoadedProgram<'a, PIO>, +} + +impl<'a, PIO: Instance> PioWs2812Program<'a, PIO> { + /// Load the ws2812 program into the given pio + pub fn new(common: &mut Common<'a, PIO>) -> Self { + let side_set = pio::SideSet::new(false, 1, false); + let mut a: pio::Assembler<32> = pio::Assembler::new_with_side_set(side_set); + + let mut wrap_target = a.label(); + let mut wrap_source = a.label(); + let mut do_zero = a.label(); + a.set_with_side_set(pio::SetDestination::PINDIRS, 1, 0); + a.bind(&mut wrap_target); + // Do stop bit + a.out_with_delay_and_side_set(pio::OutDestination::X, 1, T3 - 1, 0); + // Do start bit + a.jmp_with_delay_and_side_set(pio::JmpCondition::XIsZero, &mut do_zero, T1 - 1, 1); + // Do data bit = 1 + a.jmp_with_delay_and_side_set(pio::JmpCondition::Always, &mut wrap_target, T2 - 1, 1); + a.bind(&mut do_zero); + // Do data bit = 0 + a.nop_with_delay_and_side_set(T2 - 1, 0); + a.bind(&mut wrap_source); + + let prg = a.assemble_with_wrap(wrap_source, wrap_target); + let prg = common.load_program(&prg); + + Self { prg } + } +} + +/// Pio backed ws2812 driver +/// Const N is the number of ws2812 leds attached to this pin +pub struct PioWs2812<'d, P: Instance, const S: usize, const N: usize> { + dma: PeripheralRef<'d, AnyChannel>, + sm: StateMachine<'d, P, S>, +} + +impl<'d, P: Instance, const S: usize, const N: usize> PioWs2812<'d, P, S, N> { + /// Configure a pio state machine to use the loaded ws2812 program. + pub fn new( + pio: &mut Common<'d, P>, + mut sm: StateMachine<'d, P, S>, + dma: impl Peripheral

+ 'd, + pin: impl PioPin, + program: &PioWs2812Program<'d, P>, + ) -> Self { + into_ref!(dma); + + // Setup sm0 + let mut cfg = Config::default(); + + // Pin config + let out_pin = pio.make_pio_pin(pin); + cfg.set_out_pins(&[&out_pin]); + cfg.set_set_pins(&[&out_pin]); + + cfg.use_program(&program.prg, &[&out_pin]); + + // Clock config, measured in kHz to avoid overflows + let clock_freq = U24F8::from_num(clk_sys_freq() / 1000); + let ws2812_freq = U24F8::from_num(800); + let bit_freq = ws2812_freq * CYCLES_PER_BIT; + cfg.clock_divider = clock_freq / bit_freq; + + // FIFO config + cfg.fifo_join = FifoJoin::TxOnly; + cfg.shift_out = ShiftConfig { + auto_fill: true, + threshold: 24, + direction: ShiftDirection::Left, + }; + + sm.set_config(&cfg); + sm.set_enable(true); + + Self { + dma: dma.map_into(), + sm, + } + } + + /// Write a buffer of [smart_leds::RGB8] to the ws2812 string + pub async fn write(&mut self, colors: &[RGB8; N]) { + // Precompute the word bytes from the colors + let mut words = [0u32; N]; + for i in 0..N { + let word = (u32::from(colors[i].g) << 24) | (u32::from(colors[i].r) << 16) | (u32::from(colors[i].b) << 8); + words[i] = word; + } + + // DMA transfer + self.sm.tx().dma_push(self.dma.reborrow(), &words).await; + + Timer::after_micros(55).await; + } +} diff --git a/examples/rp/Cargo.toml b/examples/rp/Cargo.toml index 04b4c6317..264b40750 100644 --- a/examples/rp/Cargo.toml +++ b/examples/rp/Cargo.toml @@ -42,7 +42,7 @@ embedded-graphics = "0.7.1" st7789 = "0.6.1" display-interface = "0.4.1" byte-slice-cast = { version = "1.2.0", default-features = false } -smart-leds = "0.3.0" +smart-leds = "0.4.0" heapless = "0.8" usbd-hid = "0.8.1" diff --git a/examples/rp/src/bin/pio_hd44780.rs b/examples/rp/src/bin/pio_hd44780.rs index 6c02630e0..164e6f8d3 100644 --- a/examples/rp/src/bin/pio_hd44780.rs +++ b/examples/rp/src/bin/pio_hd44780.rs @@ -7,13 +7,11 @@ use core::fmt::Write; use embassy_executor::Spawner; -use embassy_rp::dma::{AnyChannel, Channel}; +use embassy_rp::bind_interrupts; use embassy_rp::peripherals::PIO0; -use embassy_rp::pio::{ - Config, Direction, FifoJoin, InterruptHandler, Pio, PioPin, ShiftConfig, ShiftDirection, StateMachine, -}; +use embassy_rp::pio::{InterruptHandler, Pio}; +use embassy_rp::pio_programs::hd44780::{PioHD44780, PioHD44780CommandSequenceProgram, PioHD44780CommandWordProgram}; use embassy_rp::pwm::{self, Pwm}; -use embassy_rp::{bind_interrupts, into_ref, Peripheral, PeripheralRef}; use embassy_time::{Instant, Timer}; use {defmt_rtt as _, panic_probe as _}; @@ -43,8 +41,27 @@ async fn main(_spawner: Spawner) { c }); - let mut hd = HD44780::new( - p.PIO0, Irqs, p.DMA_CH3, p.PIN_0, p.PIN_1, p.PIN_2, p.PIN_3, p.PIN_4, p.PIN_5, p.PIN_6, + let Pio { + mut common, sm0, irq0, .. + } = Pio::new(p.PIO0, Irqs); + + let word_prg = PioHD44780CommandWordProgram::new(&mut common); + let seq_prg = PioHD44780CommandSequenceProgram::new(&mut common); + + let mut hd = PioHD44780::new( + &mut common, + sm0, + irq0, + p.DMA_CH3, + p.PIN_0, + p.PIN_1, + p.PIN_2, + p.PIN_3, + p.PIN_4, + p.PIN_5, + p.PIN_6, + &word_prg, + &seq_prg, ) .await; @@ -68,173 +85,3 @@ async fn main(_spawner: Spawner) { Timer::after_secs(1).await; } } - -pub struct HD44780<'l> { - dma: PeripheralRef<'l, AnyChannel>, - sm: StateMachine<'l, PIO0, 0>, - - buf: [u8; 40], -} - -impl<'l> HD44780<'l> { - pub async fn new( - pio: impl Peripheral

+ 'l, - irq: Irqs, - dma: impl Peripheral

+ 'l, - rs: impl PioPin, - rw: impl PioPin, - e: impl PioPin, - db4: impl PioPin, - db5: impl PioPin, - db6: impl PioPin, - db7: impl PioPin, - ) -> HD44780<'l> { - into_ref!(dma); - - let Pio { - mut common, - mut irq0, - mut sm0, - .. - } = Pio::new(pio, irq); - - // takes command words ( <0:4>) - let prg = pio_proc::pio_asm!( - r#" - .side_set 1 opt - .origin 20 - - loop: - out x, 24 - delay: - jmp x--, delay - out pins, 4 side 1 - out null, 4 side 0 - jmp !osre, loop - irq 0 - "#, - ); - - let rs = common.make_pio_pin(rs); - let rw = common.make_pio_pin(rw); - let e = common.make_pio_pin(e); - let db4 = common.make_pio_pin(db4); - let db5 = common.make_pio_pin(db5); - let db6 = common.make_pio_pin(db6); - let db7 = common.make_pio_pin(db7); - - sm0.set_pin_dirs(Direction::Out, &[&rs, &rw, &e, &db4, &db5, &db6, &db7]); - - let mut cfg = Config::default(); - cfg.use_program(&common.load_program(&prg.program), &[&e]); - cfg.clock_divider = 125u8.into(); - cfg.set_out_pins(&[&db4, &db5, &db6, &db7]); - cfg.shift_out = ShiftConfig { - auto_fill: true, - direction: ShiftDirection::Left, - threshold: 32, - }; - cfg.fifo_join = FifoJoin::TxOnly; - sm0.set_config(&cfg); - - sm0.set_enable(true); - // init to 8 bit thrice - sm0.tx().push((50000 << 8) | 0x30); - sm0.tx().push((5000 << 8) | 0x30); - sm0.tx().push((200 << 8) | 0x30); - // init 4 bit - sm0.tx().push((200 << 8) | 0x20); - // set font and lines - sm0.tx().push((50 << 8) | 0x20); - sm0.tx().push(0b1100_0000); - - irq0.wait().await; - sm0.set_enable(false); - - // takes command sequences ( , data...) - // many side sets are only there to free up a delay bit! - let prg = pio_proc::pio_asm!( - r#" - .origin 27 - .side_set 1 - - .wrap_target - pull side 0 - out x 1 side 0 ; !rs - out y 7 side 0 ; #data - 1 - - ; rs/rw to e: >= 60ns - ; e high time: >= 500ns - ; e low time: >= 500ns - ; read data valid after e falling: ~5ns - ; write data hold after e falling: ~10ns - - loop: - pull side 0 - jmp !x data side 0 - command: - set pins 0b00 side 0 - jmp shift side 0 - data: - set pins 0b01 side 0 - shift: - out pins 4 side 1 [9] - nop side 0 [9] - out pins 4 side 1 [9] - mov osr null side 0 [7] - out pindirs 4 side 0 - set pins 0b10 side 0 - busy: - nop side 1 [9] - jmp pin more side 0 [9] - mov osr ~osr side 1 [9] - nop side 0 [4] - out pindirs 4 side 0 - jmp y-- loop side 0 - .wrap - more: - nop side 1 [9] - jmp busy side 0 [9] - "# - ); - - let mut cfg = Config::default(); - cfg.use_program(&common.load_program(&prg.program), &[&e]); - cfg.clock_divider = 8u8.into(); // ~64ns/insn - cfg.set_jmp_pin(&db7); - cfg.set_set_pins(&[&rs, &rw]); - cfg.set_out_pins(&[&db4, &db5, &db6, &db7]); - cfg.shift_out.direction = ShiftDirection::Left; - cfg.fifo_join = FifoJoin::TxOnly; - sm0.set_config(&cfg); - - sm0.set_enable(true); - - // display on and cursor on and blinking, reset display - sm0.tx().dma_push(dma.reborrow(), &[0x81u8, 0x0f, 1]).await; - - Self { - dma: dma.map_into(), - sm: sm0, - buf: [0x20; 40], - } - } - - pub async fn add_line(&mut self, s: &[u8]) { - // move cursor to 0:0, prepare 16 characters - self.buf[..3].copy_from_slice(&[0x80, 0x80, 15]); - // move line 2 up - self.buf.copy_within(22..38, 3); - // move cursor to 1:0, prepare 16 characters - self.buf[19..22].copy_from_slice(&[0x80, 0xc0, 15]); - // file line 2 with spaces - self.buf[22..38].fill(0x20); - // copy input line - let len = s.len().min(16); - self.buf[22..22 + len].copy_from_slice(&s[0..len]); - // set cursor to 1:15 - self.buf[38..].copy_from_slice(&[0x80, 0xcf]); - - self.sm.tx().dma_push(self.dma.reborrow(), &self.buf).await; - } -} diff --git a/examples/rp/src/bin/pio_i2s.rs b/examples/rp/src/bin/pio_i2s.rs index cf60e5b30..447100ddf 100644 --- a/examples/rp/src/bin/pio_i2s.rs +++ b/examples/rp/src/bin/pio_i2s.rs @@ -13,10 +13,10 @@ use core::mem; use embassy_executor::Spawner; +use embassy_rp::bind_interrupts; use embassy_rp::peripherals::PIO0; -use embassy_rp::pio::{Config, FifoJoin, InterruptHandler, Pio, ShiftConfig, ShiftDirection}; -use embassy_rp::{bind_interrupts, Peripheral}; -use fixed::traits::ToFixed; +use embassy_rp::pio::{InterruptHandler, Pio}; +use embassy_rp::pio_programs::i2s::{PioI2sOut, PioI2sOutProgram}; use static_cell::StaticCell; use {defmt_rtt as _, panic_probe as _}; @@ -25,61 +25,32 @@ bind_interrupts!(struct Irqs { }); const SAMPLE_RATE: u32 = 48_000; +const BIT_DEPTH: u32 = 16; +const CHANNELS: u32 = 2; #[embassy_executor::main] async fn main(_spawner: Spawner) { let mut p = embassy_rp::init(Default::default()); // Setup pio state machine for i2s output - let mut pio = Pio::new(p.PIO0, Irqs); - - #[rustfmt::skip] - let pio_program = pio_proc::pio_asm!( - ".side_set 2", - " set x, 14 side 0b01", // side 0bWB - W = Word Clock, B = Bit Clock - "left_data:", - " out pins, 1 side 0b00", - " jmp x-- left_data side 0b01", - " out pins 1 side 0b10", - " set x, 14 side 0b11", - "right_data:", - " out pins 1 side 0b10", - " jmp x-- right_data side 0b11", - " out pins 1 side 0b00", - ); + let Pio { mut common, sm0, .. } = Pio::new(p.PIO0, Irqs); let bit_clock_pin = p.PIN_18; let left_right_clock_pin = p.PIN_19; let data_pin = p.PIN_20; - let data_pin = pio.common.make_pio_pin(data_pin); - let bit_clock_pin = pio.common.make_pio_pin(bit_clock_pin); - let left_right_clock_pin = pio.common.make_pio_pin(left_right_clock_pin); - - let cfg = { - let mut cfg = Config::default(); - cfg.use_program( - &pio.common.load_program(&pio_program.program), - &[&bit_clock_pin, &left_right_clock_pin], - ); - cfg.set_out_pins(&[&data_pin]); - const BIT_DEPTH: u32 = 16; - const CHANNELS: u32 = 2; - let clock_frequency = SAMPLE_RATE * BIT_DEPTH * CHANNELS; - cfg.clock_divider = (125_000_000. / clock_frequency as f64 / 2.).to_fixed(); - cfg.shift_out = ShiftConfig { - threshold: 32, - direction: ShiftDirection::Left, - auto_fill: true, - }; - // join fifos to have twice the time to start the next dma transfer - cfg.fifo_join = FifoJoin::TxOnly; - cfg - }; - pio.sm0.set_config(&cfg); - pio.sm0.set_pin_dirs( - embassy_rp::pio::Direction::Out, - &[&data_pin, &left_right_clock_pin, &bit_clock_pin], + let program = PioI2sOutProgram::new(&mut common); + let mut i2s = PioI2sOut::new( + &mut common, + sm0, + p.DMA_CH0, + data_pin, + bit_clock_pin, + left_right_clock_pin, + SAMPLE_RATE, + BIT_DEPTH, + CHANNELS, + &program, ); // create two audio buffers (back and front) which will take turns being @@ -90,17 +61,13 @@ async fn main(_spawner: Spawner) { let (mut back_buffer, mut front_buffer) = dma_buffer.split_at_mut(BUFFER_SIZE); // start pio state machine - pio.sm0.set_enable(true); - let tx = pio.sm0.tx(); - let mut dma_ref = p.DMA_CH0.into_ref(); - let mut fade_value: i32 = 0; let mut phase: i32 = 0; loop { // trigger transfer of front buffer data to the pio fifo // but don't await the returned future, yet - let dma_future = tx.dma_push(dma_ref.reborrow(), front_buffer); + let dma_future = i2s.write(front_buffer); // fade in audio when bootsel is pressed let fade_target = if p.BOOTSEL.is_pressed() { i32::MAX } else { 0 }; diff --git a/examples/rp/src/bin/pio_onewire.rs b/examples/rp/src/bin/pio_onewire.rs index 5076101ec..991510851 100644 --- a/examples/rp/src/bin/pio_onewire.rs +++ b/examples/rp/src/bin/pio_onewire.rs @@ -6,7 +6,8 @@ use defmt::*; use embassy_executor::Spawner; use embassy_rp::bind_interrupts; use embassy_rp::peripherals::PIO0; -use embassy_rp::pio::{self, Common, Config, InterruptHandler, Pio, PioPin, ShiftConfig, ShiftDirection, StateMachine}; +use embassy_rp::pio::{self, InterruptHandler, Pio}; +use embassy_rp::pio_programs::onewire::{PioOneWire, PioOneWireProgram}; use embassy_time::Timer; use {defmt_rtt as _, panic_probe as _}; @@ -18,7 +19,11 @@ bind_interrupts!(struct Irqs { async fn main(_spawner: Spawner) { let p = embassy_rp::init(Default::default()); let mut pio = Pio::new(p.PIO0, Irqs); - let mut sensor = Ds18b20::new(&mut pio.common, pio.sm0, p.PIN_2); + + let prg = PioOneWireProgram::new(&mut pio.common); + let onewire = PioOneWire::new(&mut pio.common, pio.sm0, p.PIN_2, &prg); + + let mut sensor = Ds18b20::new(onewire); loop { sensor.start().await; // Start a new measurement @@ -33,89 +38,12 @@ async fn main(_spawner: Spawner) { /// DS18B20 temperature sensor driver pub struct Ds18b20<'d, PIO: pio::Instance, const SM: usize> { - sm: StateMachine<'d, PIO, SM>, + wire: PioOneWire<'d, PIO, SM>, } impl<'d, PIO: pio::Instance, const SM: usize> Ds18b20<'d, PIO, SM> { - /// Create a new instance the driver - pub fn new(common: &mut Common<'d, PIO>, mut sm: StateMachine<'d, PIO, SM>, pin: impl PioPin) -> Self { - let prg = pio_proc::pio_asm!( - r#" - .wrap_target - again: - pull block - mov x, osr - jmp !x, read - write: - set pindirs, 1 - set pins, 0 - loop1: - jmp x--,loop1 - set pindirs, 0 [31] - wait 1 pin 0 [31] - pull block - mov x, osr - bytes1: - pull block - set y, 7 - set pindirs, 1 - bit1: - set pins, 0 [1] - out pins,1 [31] - set pins, 1 [20] - jmp y--,bit1 - jmp x--,bytes1 - set pindirs, 0 [31] - jmp again - read: - pull block - mov x, osr - bytes2: - set y, 7 - bit2: - set pindirs, 1 - set pins, 0 [1] - set pindirs, 0 [5] - in pins,1 [10] - jmp y--,bit2 - jmp x--,bytes2 - .wrap - "#, - ); - - let pin = common.make_pio_pin(pin); - let mut cfg = Config::default(); - cfg.use_program(&common.load_program(&prg.program), &[]); - cfg.set_out_pins(&[&pin]); - cfg.set_in_pins(&[&pin]); - cfg.set_set_pins(&[&pin]); - cfg.shift_in = ShiftConfig { - auto_fill: true, - direction: ShiftDirection::Right, - threshold: 8, - }; - cfg.clock_divider = 255_u8.into(); - sm.set_config(&cfg); - sm.set_enable(true); - Self { sm } - } - - /// Write bytes over the wire - async fn write_bytes(&mut self, bytes: &[u8]) { - self.sm.tx().wait_push(250).await; - self.sm.tx().wait_push(bytes.len() as u32 - 1).await; - for b in bytes { - self.sm.tx().wait_push(*b as u32).await; - } - } - - /// Read bytes from the wire - async fn read_bytes(&mut self, bytes: &mut [u8]) { - self.sm.tx().wait_push(0).await; - self.sm.tx().wait_push(bytes.len() as u32 - 1).await; - for b in bytes.iter_mut() { - *b = (self.sm.rx().wait_pull().await >> 24) as u8; - } + pub fn new(wire: PioOneWire<'d, PIO, SM>) -> Self { + Self { wire } } /// Calculate CRC8 of the data @@ -139,14 +67,14 @@ impl<'d, PIO: pio::Instance, const SM: usize> Ds18b20<'d, PIO, SM> { /// Start a new measurement. Allow at least 1000ms before getting `temperature`. pub async fn start(&mut self) { - self.write_bytes(&[0xCC, 0x44]).await; + self.wire.write_bytes(&[0xCC, 0x44]).await; } /// Read the temperature. Ensure >1000ms has passed since `start` before calling this. pub async fn temperature(&mut self) -> Result { - self.write_bytes(&[0xCC, 0xBE]).await; + self.wire.write_bytes(&[0xCC, 0xBE]).await; let mut data = [0; 9]; - self.read_bytes(&mut data).await; + self.wire.read_bytes(&mut data).await; match Self::crc8(&data) == 0 { true => Ok(((data[1] as u32) << 8 | data[0] as u32) as f32 / 16.), false => Err(()), diff --git a/examples/rp/src/bin/pio_pwm.rs b/examples/rp/src/bin/pio_pwm.rs index 23d63d435..7eabb2289 100644 --- a/examples/rp/src/bin/pio_pwm.rs +++ b/examples/rp/src/bin/pio_pwm.rs @@ -5,12 +5,11 @@ use core::time::Duration; use embassy_executor::Spawner; -use embassy_rp::gpio::Level; +use embassy_rp::bind_interrupts; use embassy_rp::peripherals::PIO0; -use embassy_rp::pio::{Common, Config, Direction, Instance, InterruptHandler, Pio, PioPin, StateMachine}; -use embassy_rp::{bind_interrupts, clocks}; +use embassy_rp::pio::{InterruptHandler, Pio}; +use embassy_rp::pio_programs::pwm::{PioPwm, PioPwmProgram}; use embassy_time::Timer; -use pio::InstructionOperands; use {defmt_rtt as _, panic_probe as _}; const REFRESH_INTERVAL: u64 = 20000; @@ -19,93 +18,14 @@ bind_interrupts!(struct Irqs { PIO0_IRQ_0 => InterruptHandler; }); -pub fn to_pio_cycles(duration: Duration) -> u32 { - (clocks::clk_sys_freq() / 1_000_000) / 3 * duration.as_micros() as u32 // parentheses are required to prevent overflow -} - -pub struct PwmPio<'d, T: Instance, const SM: usize> { - sm: StateMachine<'d, T, SM>, -} - -impl<'d, T: Instance, const SM: usize> PwmPio<'d, T, SM> { - pub fn new(pio: &mut Common<'d, T>, mut sm: StateMachine<'d, T, SM>, pin: impl PioPin) -> Self { - let prg = pio_proc::pio_asm!( - ".side_set 1 opt" - "pull noblock side 0" - "mov x, osr" - "mov y, isr" - "countloop:" - "jmp x!=y noset" - "jmp skip side 1" - "noset:" - "nop" - "skip:" - "jmp y-- countloop" - ); - - pio.load_program(&prg.program); - let pin = pio.make_pio_pin(pin); - sm.set_pins(Level::High, &[&pin]); - sm.set_pin_dirs(Direction::Out, &[&pin]); - - let mut cfg = Config::default(); - cfg.use_program(&pio.load_program(&prg.program), &[&pin]); - - sm.set_config(&cfg); - - Self { sm } - } - - pub fn start(&mut self) { - self.sm.set_enable(true); - } - - pub fn stop(&mut self) { - self.sm.set_enable(false); - } - - pub fn set_period(&mut self, duration: Duration) { - let is_enabled = self.sm.is_enabled(); - while !self.sm.tx().empty() {} // Make sure that the queue is empty - self.sm.set_enable(false); - self.sm.tx().push(to_pio_cycles(duration)); - unsafe { - self.sm.exec_instr( - InstructionOperands::PULL { - if_empty: false, - block: false, - } - .encode(), - ); - self.sm.exec_instr( - InstructionOperands::OUT { - destination: ::pio::OutDestination::ISR, - bit_count: 32, - } - .encode(), - ); - }; - if is_enabled { - self.sm.set_enable(true) // Enable if previously enabled - } - } - - pub fn set_level(&mut self, level: u32) { - self.sm.tx().push(level); - } - - pub fn write(&mut self, duration: Duration) { - self.set_level(to_pio_cycles(duration)); - } -} - #[embassy_executor::main] async fn main(_spawner: Spawner) { let p = embassy_rp::init(Default::default()); let Pio { mut common, sm0, .. } = Pio::new(p.PIO0, Irqs); // Note that PIN_25 is the led pin on the Pico - let mut pwm_pio = PwmPio::new(&mut common, sm0, p.PIN_25); + let prg = PioPwmProgram::new(&mut common); + let mut pwm_pio = PioPwm::new(&mut common, sm0, p.PIN_25, &prg); pwm_pio.set_period(Duration::from_micros(REFRESH_INTERVAL)); pwm_pio.start(); diff --git a/examples/rp/src/bin/pio_rotary_encoder.rs b/examples/rp/src/bin/pio_rotary_encoder.rs index 58bdadbc0..a7ecc8d0e 100644 --- a/examples/rp/src/bin/pio_rotary_encoder.rs +++ b/examples/rp/src/bin/pio_rotary_encoder.rs @@ -5,70 +5,20 @@ use defmt::info; use embassy_executor::Spawner; -use embassy_rp::gpio::Pull; use embassy_rp::peripherals::PIO0; -use embassy_rp::{bind_interrupts, pio}; -use fixed::traits::ToFixed; -use pio::{Common, Config, FifoJoin, Instance, InterruptHandler, Pio, PioPin, ShiftDirection, StateMachine}; +use embassy_rp::{ + bind_interrupts, + pio::{InterruptHandler, Pio}, + pio_programs::rotary_encoder::{Direction, PioEncoder, PioEncoderProgram}, +}; use {defmt_rtt as _, panic_probe as _}; bind_interrupts!(struct Irqs { PIO0_IRQ_0 => InterruptHandler; }); -pub struct PioEncoder<'d, T: Instance, const SM: usize> { - sm: StateMachine<'d, T, SM>, -} - -impl<'d, T: Instance, const SM: usize> PioEncoder<'d, T, SM> { - pub fn new( - pio: &mut Common<'d, T>, - mut sm: StateMachine<'d, T, SM>, - pin_a: impl PioPin, - pin_b: impl PioPin, - ) -> Self { - let mut pin_a = pio.make_pio_pin(pin_a); - let mut pin_b = pio.make_pio_pin(pin_b); - pin_a.set_pull(Pull::Up); - pin_b.set_pull(Pull::Up); - sm.set_pin_dirs(pio::Direction::In, &[&pin_a, &pin_b]); - - let prg = pio_proc::pio_asm!("wait 1 pin 1", "wait 0 pin 1", "in pins, 2", "push",); - - let mut cfg = Config::default(); - cfg.set_in_pins(&[&pin_a, &pin_b]); - cfg.fifo_join = FifoJoin::RxOnly; - cfg.shift_in.direction = ShiftDirection::Left; - cfg.clock_divider = 10_000.to_fixed(); - cfg.use_program(&pio.load_program(&prg.program), &[]); - sm.set_config(&cfg); - sm.set_enable(true); - Self { sm } - } - - pub async fn read(&mut self) -> Direction { - loop { - match self.sm.rx().wait_pull().await { - 0 => return Direction::CounterClockwise, - 1 => return Direction::Clockwise, - _ => {} - } - } - } -} - -pub enum Direction { - Clockwise, - CounterClockwise, -} - -#[embassy_executor::main] -async fn main(_spawner: Spawner) { - let p = embassy_rp::init(Default::default()); - let Pio { mut common, sm0, .. } = Pio::new(p.PIO0, Irqs); - - let mut encoder = PioEncoder::new(&mut common, sm0, p.PIN_4, p.PIN_5); - +#[embassy_executor::task] +async fn encoder_0(mut encoder: PioEncoder<'static, PIO0, 0>) { let mut count = 0; loop { info!("Count: {}", count); @@ -78,3 +28,30 @@ async fn main(_spawner: Spawner) { }; } } + +#[embassy_executor::task] +async fn encoder_1(mut encoder: PioEncoder<'static, PIO0, 1>) { + let mut count = 0; + loop { + info!("Count: {}", count); + count += match encoder.read().await { + Direction::Clockwise => 1, + Direction::CounterClockwise => -1, + }; + } +} + +#[embassy_executor::main] +async fn main(spawner: Spawner) { + let p = embassy_rp::init(Default::default()); + let Pio { + mut common, sm0, sm1, .. + } = Pio::new(p.PIO0, Irqs); + + let prg = PioEncoderProgram::new(&mut common); + let encoder0 = PioEncoder::new(&mut common, sm0, p.PIN_4, p.PIN_5, &prg); + let encoder1 = PioEncoder::new(&mut common, sm1, p.PIN_6, p.PIN_7, &prg); + + spawner.must_spawn(encoder_0(encoder0)); + spawner.must_spawn(encoder_1(encoder1)); +} diff --git a/examples/rp/src/bin/pio_servo.rs b/examples/rp/src/bin/pio_servo.rs index a79540479..c52ee7492 100644 --- a/examples/rp/src/bin/pio_servo.rs +++ b/examples/rp/src/bin/pio_servo.rs @@ -5,12 +5,11 @@ use core::time::Duration; use embassy_executor::Spawner; -use embassy_rp::gpio::Level; +use embassy_rp::bind_interrupts; use embassy_rp::peripherals::PIO0; -use embassy_rp::pio::{Common, Config, Direction, Instance, InterruptHandler, Pio, PioPin, StateMachine}; -use embassy_rp::{bind_interrupts, clocks}; +use embassy_rp::pio::{Instance, InterruptHandler, Pio}; +use embassy_rp::pio_programs::pwm::{PioPwm, PioPwmProgram}; use embassy_time::Timer; -use pio::InstructionOperands; use {defmt_rtt as _, panic_probe as _}; const DEFAULT_MIN_PULSE_WIDTH: u64 = 1000; // uncalibrated default, the shortest duty cycle sent to a servo @@ -22,88 +21,8 @@ bind_interrupts!(struct Irqs { PIO0_IRQ_0 => InterruptHandler; }); -pub fn to_pio_cycles(duration: Duration) -> u32 { - (clocks::clk_sys_freq() / 1_000_000) / 3 * duration.as_micros() as u32 // parentheses are required to prevent overflow -} - -pub struct PwmPio<'d, T: Instance, const SM: usize> { - sm: StateMachine<'d, T, SM>, -} - -impl<'d, T: Instance, const SM: usize> PwmPio<'d, T, SM> { - pub fn new(pio: &mut Common<'d, T>, mut sm: StateMachine<'d, T, SM>, pin: impl PioPin) -> Self { - let prg = pio_proc::pio_asm!( - ".side_set 1 opt" - "pull noblock side 0" - "mov x, osr" - "mov y, isr" - "countloop:" - "jmp x!=y noset" - "jmp skip side 1" - "noset:" - "nop" - "skip:" - "jmp y-- countloop" - ); - - pio.load_program(&prg.program); - let pin = pio.make_pio_pin(pin); - sm.set_pins(Level::High, &[&pin]); - sm.set_pin_dirs(Direction::Out, &[&pin]); - - let mut cfg = Config::default(); - cfg.use_program(&pio.load_program(&prg.program), &[&pin]); - - sm.set_config(&cfg); - - Self { sm } - } - - pub fn start(&mut self) { - self.sm.set_enable(true); - } - - pub fn stop(&mut self) { - self.sm.set_enable(false); - } - - pub fn set_period(&mut self, duration: Duration) { - let is_enabled = self.sm.is_enabled(); - while !self.sm.tx().empty() {} // Make sure that the queue is empty - self.sm.set_enable(false); - self.sm.tx().push(to_pio_cycles(duration)); - unsafe { - self.sm.exec_instr( - InstructionOperands::PULL { - if_empty: false, - block: false, - } - .encode(), - ); - self.sm.exec_instr( - InstructionOperands::OUT { - destination: ::pio::OutDestination::ISR, - bit_count: 32, - } - .encode(), - ); - }; - if is_enabled { - self.sm.set_enable(true) // Enable if previously enabled - } - } - - pub fn set_level(&mut self, level: u32) { - self.sm.tx().push(level); - } - - pub fn write(&mut self, duration: Duration) { - self.set_level(to_pio_cycles(duration)); - } -} - pub struct ServoBuilder<'d, T: Instance, const SM: usize> { - pwm: PwmPio<'d, T, SM>, + pwm: PioPwm<'d, T, SM>, period: Duration, min_pulse_width: Duration, max_pulse_width: Duration, @@ -111,7 +30,7 @@ pub struct ServoBuilder<'d, T: Instance, const SM: usize> { } impl<'d, T: Instance, const SM: usize> ServoBuilder<'d, T, SM> { - pub fn new(pwm: PwmPio<'d, T, SM>) -> Self { + pub fn new(pwm: PioPwm<'d, T, SM>) -> Self { Self { pwm, period: Duration::from_micros(REFRESH_INTERVAL), @@ -153,7 +72,7 @@ impl<'d, T: Instance, const SM: usize> ServoBuilder<'d, T, SM> { } pub struct Servo<'d, T: Instance, const SM: usize> { - pwm: PwmPio<'d, T, SM>, + pwm: PioPwm<'d, T, SM>, min_pulse_width: Duration, max_pulse_width: Duration, max_degree_rotation: u64, @@ -190,7 +109,8 @@ async fn main(_spawner: Spawner) { let p = embassy_rp::init(Default::default()); let Pio { mut common, sm0, .. } = Pio::new(p.PIO0, Irqs); - let pwm_pio = PwmPio::new(&mut common, sm0, p.PIN_1); + let prg = PioPwmProgram::new(&mut common); + let pwm_pio = PioPwm::new(&mut common, sm0, p.PIN_1, &prg); let mut servo = ServoBuilder::new(pwm_pio) .set_max_degree_rotation(120) // Example of adjusting values for MG996R servo .set_min_pulse_width(Duration::from_micros(350)) // This value was detemined by a rough experiment. diff --git a/examples/rp/src/bin/pio_stepper.rs b/examples/rp/src/bin/pio_stepper.rs index 6ee45a414..3862c248b 100644 --- a/examples/rp/src/bin/pio_stepper.rs +++ b/examples/rp/src/bin/pio_stepper.rs @@ -3,143 +3,20 @@ #![no_std] #![no_main] -use core::mem::{self, MaybeUninit}; use defmt::info; use embassy_executor::Spawner; use embassy_rp::bind_interrupts; use embassy_rp::peripherals::PIO0; -use embassy_rp::pio::{Common, Config, Direction, Instance, InterruptHandler, Irq, Pio, PioPin, StateMachine}; +use embassy_rp::pio::{InterruptHandler, Pio}; +use embassy_rp::pio_programs::stepper::{PioStepper, PioStepperProgram}; use embassy_time::{with_timeout, Duration, Timer}; -use fixed::traits::ToFixed; -use fixed::types::extra::U8; -use fixed::FixedU32; use {defmt_rtt as _, panic_probe as _}; bind_interrupts!(struct Irqs { PIO0_IRQ_0 => InterruptHandler; }); -pub struct PioStepper<'d, T: Instance, const SM: usize> { - irq: Irq<'d, T, SM>, - sm: StateMachine<'d, T, SM>, -} - -impl<'d, T: Instance, const SM: usize> PioStepper<'d, T, SM> { - pub fn new( - pio: &mut Common<'d, T>, - mut sm: StateMachine<'d, T, SM>, - irq: Irq<'d, T, SM>, - pin0: impl PioPin, - pin1: impl PioPin, - pin2: impl PioPin, - pin3: impl PioPin, - ) -> Self { - let prg = pio_proc::pio_asm!( - "pull block", - "mov x, osr", - "pull block", - "mov y, osr", - "jmp !x end", - "loop:", - "jmp !osre step", - "mov osr, y", - "step:", - "out pins, 4 [31]" - "jmp x-- loop", - "end:", - "irq 0 rel" - ); - let pin0 = pio.make_pio_pin(pin0); - let pin1 = pio.make_pio_pin(pin1); - let pin2 = pio.make_pio_pin(pin2); - let pin3 = pio.make_pio_pin(pin3); - sm.set_pin_dirs(Direction::Out, &[&pin0, &pin1, &pin2, &pin3]); - let mut cfg = Config::default(); - cfg.set_out_pins(&[&pin0, &pin1, &pin2, &pin3]); - cfg.clock_divider = (125_000_000 / (100 * 136)).to_fixed(); - cfg.use_program(&pio.load_program(&prg.program), &[]); - sm.set_config(&cfg); - sm.set_enable(true); - Self { irq, sm } - } - - // Set pulse frequency - pub fn set_frequency(&mut self, freq: u32) { - let clock_divider: FixedU32 = (125_000_000 / (freq * 136)).to_fixed(); - assert!(clock_divider <= 65536, "clkdiv must be <= 65536"); - assert!(clock_divider >= 1, "clkdiv must be >= 1"); - self.sm.set_clock_divider(clock_divider); - self.sm.clkdiv_restart(); - } - - // Full step, one phase - pub async fn step(&mut self, steps: i32) { - if steps > 0 { - self.run(steps, 0b1000_0100_0010_0001_1000_0100_0010_0001).await - } else { - self.run(-steps, 0b0001_0010_0100_1000_0001_0010_0100_1000).await - } - } - - // Full step, two phase - pub async fn step2(&mut self, steps: i32) { - if steps > 0 { - self.run(steps, 0b1001_1100_0110_0011_1001_1100_0110_0011).await - } else { - self.run(-steps, 0b0011_0110_1100_1001_0011_0110_1100_1001).await - } - } - - // Half step - pub async fn step_half(&mut self, steps: i32) { - if steps > 0 { - self.run(steps, 0b1001_1000_1100_0100_0110_0010_0011_0001).await - } else { - self.run(-steps, 0b0001_0011_0010_0110_0100_1100_1000_1001).await - } - } - - async fn run(&mut self, steps: i32, pattern: u32) { - self.sm.tx().wait_push(steps as u32).await; - self.sm.tx().wait_push(pattern).await; - let drop = OnDrop::new(|| { - self.sm.clear_fifos(); - unsafe { - self.sm.exec_instr( - pio::InstructionOperands::JMP { - address: 0, - condition: pio::JmpCondition::Always, - } - .encode(), - ); - } - }); - self.irq.wait().await; - drop.defuse(); - } -} - -struct OnDrop { - f: MaybeUninit, -} - -impl OnDrop { - pub fn new(f: F) -> Self { - Self { f: MaybeUninit::new(f) } - } - - pub fn defuse(self) { - mem::forget(self) - } -} - -impl Drop for OnDrop { - fn drop(&mut self) { - unsafe { self.f.as_ptr().read()() } - } -} - #[embassy_executor::main] async fn main(_spawner: Spawner) { let p = embassy_rp::init(Default::default()); @@ -147,14 +24,18 @@ async fn main(_spawner: Spawner) { mut common, irq0, sm0, .. } = Pio::new(p.PIO0, Irqs); - let mut stepper = PioStepper::new(&mut common, sm0, irq0, p.PIN_4, p.PIN_5, p.PIN_6, p.PIN_7); + let prg = PioStepperProgram::new(&mut common); + let mut stepper = PioStepper::new(&mut common, sm0, irq0, p.PIN_4, p.PIN_5, p.PIN_6, p.PIN_7, &prg); stepper.set_frequency(120); loop { info!("CW full steps"); stepper.step(1000).await; info!("CCW full steps, drop after 1 sec"); - if let Err(_) = with_timeout(Duration::from_secs(1), stepper.step(-i32::MAX)).await { + if with_timeout(Duration::from_secs(1), stepper.step(-i32::MAX)) + .await + .is_err() + { info!("Time's up!"); Timer::after(Duration::from_secs(1)).await; } diff --git a/examples/rp/src/bin/pio_uart.rs b/examples/rp/src/bin/pio_uart.rs index 53b696309..b9e01b0ac 100644 --- a/examples/rp/src/bin/pio_uart.rs +++ b/examples/rp/src/bin/pio_uart.rs @@ -15,7 +15,8 @@ use embassy_executor::Spawner; use embassy_futures::join::{join, join3}; use embassy_rp::bind_interrupts; use embassy_rp::peripherals::{PIO0, USB}; -use embassy_rp::pio::InterruptHandler as PioInterruptHandler; +use embassy_rp::pio; +use embassy_rp::pio_programs::uart::{PioUartRx, PioUartRxProgram, PioUartTx, PioUartTxProgram}; use embassy_rp::usb::{Driver, Instance, InterruptHandler}; use embassy_sync::blocking_mutex::raw::NoopRawMutex; use embassy_sync::pipe::Pipe; @@ -25,13 +26,11 @@ use embassy_usb::{Builder, Config}; use embedded_io_async::{Read, Write}; use {defmt_rtt as _, panic_probe as _}; -use crate::uart::PioUart; -use crate::uart_rx::PioUartRx; -use crate::uart_tx::PioUartTx; +//use crate::uart::PioUart; bind_interrupts!(struct Irqs { USBCTRL_IRQ => InterruptHandler; - PIO0_IRQ_0 => PioInterruptHandler; + PIO0_IRQ_0 => pio::InterruptHandler; }); #[embassy_executor::main] @@ -85,8 +84,15 @@ async fn main(_spawner: Spawner) { let usb_fut = usb.run(); // PIO UART setup - let uart = PioUart::new(9600, p.PIO0, p.PIN_4, p.PIN_5); - let (mut uart_tx, mut uart_rx) = uart.split(); + let pio::Pio { + mut common, sm0, sm1, .. + } = pio::Pio::new(p.PIO0, Irqs); + + let tx_program = PioUartTxProgram::new(&mut common); + let mut uart_tx = PioUartTx::new(9600, &mut common, sm0, p.PIN_4, &tx_program); + + let rx_program = PioUartRxProgram::new(&mut common); + let mut uart_rx = PioUartRx::new(9600, &mut common, sm1, p.PIN_5, &rx_program); // Pipe setup let mut usb_pipe: Pipe = Pipe::new(); @@ -163,8 +169,8 @@ async fn usb_write<'d, T: Instance + 'd>( } /// Read from the UART and write it to the USB TX pipe -async fn uart_read( - uart_rx: &mut PioUartRx<'_>, +async fn uart_read( + uart_rx: &mut PioUartRx<'_, PIO, SM>, usb_pipe_writer: &mut embassy_sync::pipe::Writer<'_, NoopRawMutex, 20>, ) -> ! { let mut buf = [0; 64]; @@ -180,8 +186,8 @@ async fn uart_read( } /// Read from the UART TX pipe and write it to the UART -async fn uart_write( - uart_tx: &mut PioUartTx<'_>, +async fn uart_write( + uart_tx: &mut PioUartTx<'_, PIO, SM>, uart_pipe_reader: &mut embassy_sync::pipe::Reader<'_, NoopRawMutex, 20>, ) -> ! { let mut buf = [0; 64]; @@ -192,197 +198,3 @@ async fn uart_write( let _ = uart_tx.write(&data).await; } } - -mod uart { - use embassy_rp::peripherals::PIO0; - use embassy_rp::pio::{Pio, PioPin}; - use embassy_rp::Peripheral; - - use crate::uart_rx::PioUartRx; - use crate::uart_tx::PioUartTx; - use crate::Irqs; - - pub struct PioUart<'a> { - tx: PioUartTx<'a>, - rx: PioUartRx<'a>, - } - - impl<'a> PioUart<'a> { - pub fn new( - baud: u64, - pio: impl Peripheral

+ 'a, - tx_pin: impl PioPin, - rx_pin: impl PioPin, - ) -> PioUart<'a> { - let Pio { - mut common, sm0, sm1, .. - } = Pio::new(pio, Irqs); - - let tx = PioUartTx::new(&mut common, sm0, tx_pin, baud); - let rx = PioUartRx::new(&mut common, sm1, rx_pin, baud); - - PioUart { tx, rx } - } - - pub fn split(self) -> (PioUartTx<'a>, PioUartRx<'a>) { - (self.tx, self.rx) - } - } -} - -mod uart_tx { - use core::convert::Infallible; - - use embassy_rp::gpio::Level; - use embassy_rp::peripherals::PIO0; - use embassy_rp::pio::{Common, Config, Direction, FifoJoin, PioPin, ShiftDirection, StateMachine}; - use embedded_io_async::{ErrorType, Write}; - use fixed::traits::ToFixed; - use fixed_macro::types::U56F8; - - pub struct PioUartTx<'a> { - sm_tx: StateMachine<'a, PIO0, 0>, - } - - impl<'a> PioUartTx<'a> { - pub fn new( - common: &mut Common<'a, PIO0>, - mut sm_tx: StateMachine<'a, PIO0, 0>, - tx_pin: impl PioPin, - baud: u64, - ) -> Self { - let prg = pio_proc::pio_asm!( - r#" - .side_set 1 opt - - ; An 8n1 UART transmit program. - ; OUT pin 0 and side-set pin 0 are both mapped to UART TX pin. - - pull side 1 [7] ; Assert stop bit, or stall with line in idle state - set x, 7 side 0 [7] ; Preload bit counter, assert start bit for 8 clocks - bitloop: ; This loop will run 8 times (8n1 UART) - out pins, 1 ; Shift 1 bit from OSR to the first OUT pin - jmp x-- bitloop [6] ; Each loop iteration is 8 cycles. - "# - ); - let tx_pin = common.make_pio_pin(tx_pin); - sm_tx.set_pins(Level::High, &[&tx_pin]); - sm_tx.set_pin_dirs(Direction::Out, &[&tx_pin]); - - let mut cfg = Config::default(); - - cfg.set_out_pins(&[&tx_pin]); - cfg.use_program(&common.load_program(&prg.program), &[&tx_pin]); - cfg.shift_out.auto_fill = false; - cfg.shift_out.direction = ShiftDirection::Right; - cfg.fifo_join = FifoJoin::TxOnly; - cfg.clock_divider = (U56F8!(125_000_000) / (8 * baud)).to_fixed(); - sm_tx.set_config(&cfg); - sm_tx.set_enable(true); - - Self { sm_tx } - } - - pub async fn write_u8(&mut self, data: u8) { - self.sm_tx.tx().wait_push(data as u32).await; - } - } - - impl ErrorType for PioUartTx<'_> { - type Error = Infallible; - } - - impl Write for PioUartTx<'_> { - async fn write(&mut self, buf: &[u8]) -> Result { - for byte in buf { - self.write_u8(*byte).await; - } - Ok(buf.len()) - } - } -} - -mod uart_rx { - use core::convert::Infallible; - - use embassy_rp::gpio::Level; - use embassy_rp::peripherals::PIO0; - use embassy_rp::pio::{Common, Config, Direction, FifoJoin, PioPin, ShiftDirection, StateMachine}; - use embedded_io_async::{ErrorType, Read}; - use fixed::traits::ToFixed; - use fixed_macro::types::U56F8; - - pub struct PioUartRx<'a> { - sm_rx: StateMachine<'a, PIO0, 1>, - } - - impl<'a> PioUartRx<'a> { - pub fn new( - common: &mut Common<'a, PIO0>, - mut sm_rx: StateMachine<'a, PIO0, 1>, - rx_pin: impl PioPin, - baud: u64, - ) -> Self { - let prg = pio_proc::pio_asm!( - r#" - ; Slightly more fleshed-out 8n1 UART receiver which handles framing errors and - ; break conditions more gracefully. - ; IN pin 0 and JMP pin are both mapped to the GPIO used as UART RX. - - start: - wait 0 pin 0 ; Stall until start bit is asserted - set x, 7 [10] ; Preload bit counter, then delay until halfway through - rx_bitloop: ; the first data bit (12 cycles incl wait, set). - in pins, 1 ; Shift data bit into ISR - jmp x-- rx_bitloop [6] ; Loop 8 times, each loop iteration is 8 cycles - jmp pin good_rx_stop ; Check stop bit (should be high) - - irq 4 rel ; Either a framing error or a break. Set a sticky flag, - wait 1 pin 0 ; and wait for line to return to idle state. - jmp start ; Don't push data if we didn't see good framing. - - good_rx_stop: ; No delay before returning to start; a little slack is - in null 24 - push ; important in case the TX clock is slightly too fast. - "# - ); - let mut cfg = Config::default(); - cfg.use_program(&common.load_program(&prg.program), &[]); - - let rx_pin = common.make_pio_pin(rx_pin); - sm_rx.set_pins(Level::High, &[&rx_pin]); - cfg.set_in_pins(&[&rx_pin]); - cfg.set_jmp_pin(&rx_pin); - sm_rx.set_pin_dirs(Direction::In, &[&rx_pin]); - - cfg.clock_divider = (U56F8!(125_000_000) / (8 * baud)).to_fixed(); - cfg.shift_in.auto_fill = false; - cfg.shift_in.direction = ShiftDirection::Right; - cfg.shift_in.threshold = 32; - cfg.fifo_join = FifoJoin::RxOnly; - sm_rx.set_config(&cfg); - sm_rx.set_enable(true); - - Self { sm_rx } - } - - pub async fn read_u8(&mut self) -> u8 { - self.sm_rx.rx().wait_pull().await as u8 - } - } - - impl ErrorType for PioUartRx<'_> { - type Error = Infallible; - } - - impl Read for PioUartRx<'_> { - async fn read(&mut self, buf: &mut [u8]) -> Result { - let mut i = 0; - while i < buf.len() { - buf[i] = self.read_u8().await; - i += 1; - } - Ok(i) - } - } -} diff --git a/examples/rp/src/bin/pio_ws2812.rs b/examples/rp/src/bin/pio_ws2812.rs index ac145933c..d1fcfc471 100644 --- a/examples/rp/src/bin/pio_ws2812.rs +++ b/examples/rp/src/bin/pio_ws2812.rs @@ -6,15 +6,11 @@ use defmt::*; use embassy_executor::Spawner; -use embassy_rp::dma::{AnyChannel, Channel}; +use embassy_rp::bind_interrupts; use embassy_rp::peripherals::PIO0; -use embassy_rp::pio::{ - Common, Config, FifoJoin, Instance, InterruptHandler, Pio, PioPin, ShiftConfig, ShiftDirection, StateMachine, -}; -use embassy_rp::{bind_interrupts, clocks, into_ref, Peripheral, PeripheralRef}; -use embassy_time::{Duration, Ticker, Timer}; -use fixed::types::U24F8; -use fixed_macro::fixed; +use embassy_rp::pio::{InterruptHandler, Pio}; +use embassy_rp::pio_programs::ws2812::{PioWs2812, PioWs2812Program}; +use embassy_time::{Duration, Ticker}; use smart_leds::RGB8; use {defmt_rtt as _, panic_probe as _}; @@ -22,96 +18,6 @@ bind_interrupts!(struct Irqs { PIO0_IRQ_0 => InterruptHandler; }); -pub struct Ws2812<'d, P: Instance, const S: usize, const N: usize> { - dma: PeripheralRef<'d, AnyChannel>, - sm: StateMachine<'d, P, S>, -} - -impl<'d, P: Instance, const S: usize, const N: usize> Ws2812<'d, P, S, N> { - pub fn new( - pio: &mut Common<'d, P>, - mut sm: StateMachine<'d, P, S>, - dma: impl Peripheral

+ 'd, - pin: impl PioPin, - ) -> Self { - into_ref!(dma); - - // Setup sm0 - - // prepare the PIO program - let side_set = pio::SideSet::new(false, 1, false); - let mut a: pio::Assembler<32> = pio::Assembler::new_with_side_set(side_set); - - const T1: u8 = 2; // start bit - const T2: u8 = 5; // data bit - const T3: u8 = 3; // stop bit - const CYCLES_PER_BIT: u32 = (T1 + T2 + T3) as u32; - - let mut wrap_target = a.label(); - let mut wrap_source = a.label(); - let mut do_zero = a.label(); - a.set_with_side_set(pio::SetDestination::PINDIRS, 1, 0); - a.bind(&mut wrap_target); - // Do stop bit - a.out_with_delay_and_side_set(pio::OutDestination::X, 1, T3 - 1, 0); - // Do start bit - a.jmp_with_delay_and_side_set(pio::JmpCondition::XIsZero, &mut do_zero, T1 - 1, 1); - // Do data bit = 1 - a.jmp_with_delay_and_side_set(pio::JmpCondition::Always, &mut wrap_target, T2 - 1, 1); - a.bind(&mut do_zero); - // Do data bit = 0 - a.nop_with_delay_and_side_set(T2 - 1, 0); - a.bind(&mut wrap_source); - - let prg = a.assemble_with_wrap(wrap_source, wrap_target); - let mut cfg = Config::default(); - - // Pin config - let out_pin = pio.make_pio_pin(pin); - cfg.set_out_pins(&[&out_pin]); - cfg.set_set_pins(&[&out_pin]); - - cfg.use_program(&pio.load_program(&prg), &[&out_pin]); - - // Clock config, measured in kHz to avoid overflows - // TODO CLOCK_FREQ should come from embassy_rp - let clock_freq = U24F8::from_num(clocks::clk_sys_freq() / 1000); - let ws2812_freq = fixed!(800: U24F8); - let bit_freq = ws2812_freq * CYCLES_PER_BIT; - cfg.clock_divider = clock_freq / bit_freq; - - // FIFO config - cfg.fifo_join = FifoJoin::TxOnly; - cfg.shift_out = ShiftConfig { - auto_fill: true, - threshold: 24, - direction: ShiftDirection::Left, - }; - - sm.set_config(&cfg); - sm.set_enable(true); - - Self { - dma: dma.map_into(), - sm, - } - } - - pub async fn write(&mut self, colors: &[RGB8; N]) { - // Precompute the word bytes from the colors - let mut words = [0u32; N]; - for i in 0..N { - let word = (u32::from(colors[i].g) << 24) | (u32::from(colors[i].r) << 16) | (u32::from(colors[i].b) << 8); - words[i] = word; - } - - // DMA transfer - self.sm.tx().dma_push(self.dma.reborrow(), &words).await; - - Timer::after_micros(55).await; - } -} - /// Input a value 0 to 255 to get a color value /// The colours are a transition r - g - b - back to r. fn wheel(mut wheel_pos: u8) -> RGB8 { @@ -142,7 +48,8 @@ async fn main(_spawner: Spawner) { // Common neopixel pins: // Thing plus: 8 // Adafruit Feather: 16; Adafruit Feather+RFM95: 4 - let mut ws2812 = Ws2812::new(&mut common, sm0, p.DMA_CH0, p.PIN_16); + let program = PioWs2812Program::new(&mut common); + let mut ws2812 = PioWs2812::new(&mut common, sm0, p.DMA_CH0, p.PIN_16, &program); // Loop forever making RGB values and pushing them out to the WS2812. let mut ticker = Ticker::every(Duration::from_millis(10)); diff --git a/examples/rp23/src/bin/pio_hd44780.rs b/examples/rp23/src/bin/pio_hd44780.rs index 5a6d7a9c5..c6f5f6db0 100644 --- a/examples/rp23/src/bin/pio_hd44780.rs +++ b/examples/rp23/src/bin/pio_hd44780.rs @@ -7,14 +7,12 @@ use core::fmt::Write; use embassy_executor::Spawner; +use embassy_rp::bind_interrupts; use embassy_rp::block::ImageDef; -use embassy_rp::dma::{AnyChannel, Channel}; use embassy_rp::peripherals::PIO0; -use embassy_rp::pio::{ - Config, Direction, FifoJoin, InterruptHandler, Pio, PioPin, ShiftConfig, ShiftDirection, StateMachine, -}; +use embassy_rp::pio::{InterruptHandler, Pio}; +use embassy_rp::pio_programs::hd44780::{PioHD44780, PioHD44780CommandSequenceProgram, PioHD44780CommandWordProgram}; use embassy_rp::pwm::{self, Pwm}; -use embassy_rp::{bind_interrupts, into_ref, Peripheral, PeripheralRef}; use embassy_time::{Instant, Timer}; use {defmt_rtt as _, panic_probe as _}; @@ -48,8 +46,27 @@ async fn main(_spawner: Spawner) { c }); - let mut hd = HD44780::new( - p.PIO0, Irqs, p.DMA_CH3, p.PIN_0, p.PIN_1, p.PIN_2, p.PIN_3, p.PIN_4, p.PIN_5, p.PIN_6, + let Pio { + mut common, sm0, irq0, .. + } = Pio::new(p.PIO0, Irqs); + + let word_prg = PioHD44780CommandWordProgram::new(&mut common); + let seq_prg = PioHD44780CommandSequenceProgram::new(&mut common); + + let mut hd = PioHD44780::new( + &mut common, + sm0, + irq0, + p.DMA_CH3, + p.PIN_0, + p.PIN_1, + p.PIN_2, + p.PIN_3, + p.PIN_4, + p.PIN_5, + p.PIN_6, + &word_prg, + &seq_prg, ) .await; @@ -73,173 +90,3 @@ async fn main(_spawner: Spawner) { Timer::after_secs(1).await; } } - -pub struct HD44780<'l> { - dma: PeripheralRef<'l, AnyChannel>, - sm: StateMachine<'l, PIO0, 0>, - - buf: [u8; 40], -} - -impl<'l> HD44780<'l> { - pub async fn new( - pio: impl Peripheral

+ 'l, - irq: Irqs, - dma: impl Peripheral

+ 'l, - rs: impl PioPin, - rw: impl PioPin, - e: impl PioPin, - db4: impl PioPin, - db5: impl PioPin, - db6: impl PioPin, - db7: impl PioPin, - ) -> HD44780<'l> { - into_ref!(dma); - - let Pio { - mut common, - mut irq0, - mut sm0, - .. - } = Pio::new(pio, irq); - - // takes command words ( <0:4>) - let prg = pio_proc::pio_asm!( - r#" - .side_set 1 opt - .origin 20 - - loop: - out x, 24 - delay: - jmp x--, delay - out pins, 4 side 1 - out null, 4 side 0 - jmp !osre, loop - irq 0 - "#, - ); - - let rs = common.make_pio_pin(rs); - let rw = common.make_pio_pin(rw); - let e = common.make_pio_pin(e); - let db4 = common.make_pio_pin(db4); - let db5 = common.make_pio_pin(db5); - let db6 = common.make_pio_pin(db6); - let db7 = common.make_pio_pin(db7); - - sm0.set_pin_dirs(Direction::Out, &[&rs, &rw, &e, &db4, &db5, &db6, &db7]); - - let mut cfg = Config::default(); - cfg.use_program(&common.load_program(&prg.program), &[&e]); - cfg.clock_divider = 125u8.into(); - cfg.set_out_pins(&[&db4, &db5, &db6, &db7]); - cfg.shift_out = ShiftConfig { - auto_fill: true, - direction: ShiftDirection::Left, - threshold: 32, - }; - cfg.fifo_join = FifoJoin::TxOnly; - sm0.set_config(&cfg); - - sm0.set_enable(true); - // init to 8 bit thrice - sm0.tx().push((50000 << 8) | 0x30); - sm0.tx().push((5000 << 8) | 0x30); - sm0.tx().push((200 << 8) | 0x30); - // init 4 bit - sm0.tx().push((200 << 8) | 0x20); - // set font and lines - sm0.tx().push((50 << 8) | 0x20); - sm0.tx().push(0b1100_0000); - - irq0.wait().await; - sm0.set_enable(false); - - // takes command sequences ( , data...) - // many side sets are only there to free up a delay bit! - let prg = pio_proc::pio_asm!( - r#" - .origin 27 - .side_set 1 - - .wrap_target - pull side 0 - out x 1 side 0 ; !rs - out y 7 side 0 ; #data - 1 - - ; rs/rw to e: >= 60ns - ; e high time: >= 500ns - ; e low time: >= 500ns - ; read data valid after e falling: ~5ns - ; write data hold after e falling: ~10ns - - loop: - pull side 0 - jmp !x data side 0 - command: - set pins 0b00 side 0 - jmp shift side 0 - data: - set pins 0b01 side 0 - shift: - out pins 4 side 1 [9] - nop side 0 [9] - out pins 4 side 1 [9] - mov osr null side 0 [7] - out pindirs 4 side 0 - set pins 0b10 side 0 - busy: - nop side 1 [9] - jmp pin more side 0 [9] - mov osr ~osr side 1 [9] - nop side 0 [4] - out pindirs 4 side 0 - jmp y-- loop side 0 - .wrap - more: - nop side 1 [9] - jmp busy side 0 [9] - "# - ); - - let mut cfg = Config::default(); - cfg.use_program(&common.load_program(&prg.program), &[&e]); - cfg.clock_divider = 8u8.into(); // ~64ns/insn - cfg.set_jmp_pin(&db7); - cfg.set_set_pins(&[&rs, &rw]); - cfg.set_out_pins(&[&db4, &db5, &db6, &db7]); - cfg.shift_out.direction = ShiftDirection::Left; - cfg.fifo_join = FifoJoin::TxOnly; - sm0.set_config(&cfg); - - sm0.set_enable(true); - - // display on and cursor on and blinking, reset display - sm0.tx().dma_push(dma.reborrow(), &[0x81u8, 0x0f, 1]).await; - - Self { - dma: dma.map_into(), - sm: sm0, - buf: [0x20; 40], - } - } - - pub async fn add_line(&mut self, s: &[u8]) { - // move cursor to 0:0, prepare 16 characters - self.buf[..3].copy_from_slice(&[0x80, 0x80, 15]); - // move line 2 up - self.buf.copy_within(22..38, 3); - // move cursor to 1:0, prepare 16 characters - self.buf[19..22].copy_from_slice(&[0x80, 0xc0, 15]); - // file line 2 with spaces - self.buf[22..38].fill(0x20); - // copy input line - let len = s.len().min(16); - self.buf[22..22 + len].copy_from_slice(&s[0..len]); - // set cursor to 1:15 - self.buf[38..].copy_from_slice(&[0x80, 0xcf]); - - self.sm.tx().dma_push(self.dma.reborrow(), &self.buf).await; - } -} diff --git a/examples/rp23/src/bin/pio_i2s.rs b/examples/rp23/src/bin/pio_i2s.rs index 46e5eac88..90491fb45 100644 --- a/examples/rp23/src/bin/pio_i2s.rs +++ b/examples/rp23/src/bin/pio_i2s.rs @@ -13,11 +13,11 @@ use core::mem; use embassy_executor::Spawner; +use embassy_rp::bind_interrupts; use embassy_rp::block::ImageDef; use embassy_rp::peripherals::PIO0; -use embassy_rp::pio::{Config, FifoJoin, InterruptHandler, Pio, ShiftConfig, ShiftDirection}; -use embassy_rp::{bind_interrupts, Peripheral}; -use fixed::traits::ToFixed; +use embassy_rp::pio::{InterruptHandler, Pio}; +use embassy_rp::pio_programs::i2s::{PioI2sOut, PioI2sOutProgram}; use static_cell::StaticCell; use {defmt_rtt as _, panic_probe as _}; @@ -30,61 +30,32 @@ bind_interrupts!(struct Irqs { }); const SAMPLE_RATE: u32 = 48_000; +const BIT_DEPTH: u32 = 16; +const CHANNELS: u32 = 2; #[embassy_executor::main] async fn main(_spawner: Spawner) { - let p = embassy_rp::init(Default::default()); + let mut p = embassy_rp::init(Default::default()); // Setup pio state machine for i2s output - let mut pio = Pio::new(p.PIO0, Irqs); - - #[rustfmt::skip] - let pio_program = pio_proc::pio_asm!( - ".side_set 2", - " set x, 14 side 0b01", // side 0bWB - W = Word Clock, B = Bit Clock - "left_data:", - " out pins, 1 side 0b00", - " jmp x-- left_data side 0b01", - " out pins 1 side 0b10", - " set x, 14 side 0b11", - "right_data:", - " out pins 1 side 0b10", - " jmp x-- right_data side 0b11", - " out pins 1 side 0b00", - ); + let Pio { mut common, sm0, .. } = Pio::new(p.PIO0, Irqs); let bit_clock_pin = p.PIN_18; let left_right_clock_pin = p.PIN_19; let data_pin = p.PIN_20; - let data_pin = pio.common.make_pio_pin(data_pin); - let bit_clock_pin = pio.common.make_pio_pin(bit_clock_pin); - let left_right_clock_pin = pio.common.make_pio_pin(left_right_clock_pin); - - let cfg = { - let mut cfg = Config::default(); - cfg.use_program( - &pio.common.load_program(&pio_program.program), - &[&bit_clock_pin, &left_right_clock_pin], - ); - cfg.set_out_pins(&[&data_pin]); - const BIT_DEPTH: u32 = 16; - const CHANNELS: u32 = 2; - let clock_frequency = SAMPLE_RATE * BIT_DEPTH * CHANNELS; - cfg.clock_divider = (125_000_000. / clock_frequency as f64 / 2.).to_fixed(); - cfg.shift_out = ShiftConfig { - threshold: 32, - direction: ShiftDirection::Left, - auto_fill: true, - }; - // join fifos to have twice the time to start the next dma transfer - cfg.fifo_join = FifoJoin::TxOnly; - cfg - }; - pio.sm0.set_config(&cfg); - pio.sm0.set_pin_dirs( - embassy_rp::pio::Direction::Out, - &[&data_pin, &left_right_clock_pin, &bit_clock_pin], + let program = PioI2sOutProgram::new(&mut common); + let mut i2s = PioI2sOut::new( + &mut common, + sm0, + p.DMA_CH0, + data_pin, + bit_clock_pin, + left_right_clock_pin, + SAMPLE_RATE, + BIT_DEPTH, + CHANNELS, + &program, ); // create two audio buffers (back and front) which will take turns being @@ -95,20 +66,16 @@ async fn main(_spawner: Spawner) { let (mut back_buffer, mut front_buffer) = dma_buffer.split_at_mut(BUFFER_SIZE); // start pio state machine - pio.sm0.set_enable(true); - let tx = pio.sm0.tx(); - let mut dma_ref = p.DMA_CH0.into_ref(); - let mut fade_value: i32 = 0; let mut phase: i32 = 0; loop { // trigger transfer of front buffer data to the pio fifo // but don't await the returned future, yet - let dma_future = tx.dma_push(dma_ref.reborrow(), front_buffer); + let dma_future = i2s.write(front_buffer); - // fade in audio - let fade_target = i32::MAX; + // fade in audio when bootsel is pressed + let fade_target = if p.BOOTSEL.is_pressed() { i32::MAX } else { 0 }; // fill back buffer with fresh audio samples before awaiting the dma future for s in back_buffer.iter_mut() { diff --git a/examples/rp23/src/bin/pio_onewire.rs b/examples/rp23/src/bin/pio_onewire.rs new file mode 100644 index 000000000..7f227d04b --- /dev/null +++ b/examples/rp23/src/bin/pio_onewire.rs @@ -0,0 +1,88 @@ +//! This example shows how you can use PIO to read a `DS18B20` one-wire temperature sensor. + +#![no_std] +#![no_main] +use defmt::*; +use embassy_executor::Spawner; +use embassy_rp::bind_interrupts; +use embassy_rp::block::ImageDef; +use embassy_rp::peripherals::PIO0; +use embassy_rp::pio::{self, InterruptHandler, Pio}; +use embassy_rp::pio_programs::onewire::{PioOneWire, PioOneWireProgram}; +use embassy_time::Timer; +use {defmt_rtt as _, panic_probe as _}; + +#[link_section = ".start_block"] +#[used] +pub static IMAGE_DEF: ImageDef = ImageDef::secure_exe(); + +bind_interrupts!(struct Irqs { + PIO0_IRQ_0 => InterruptHandler; +}); + +#[embassy_executor::main] +async fn main(_spawner: Spawner) { + let p = embassy_rp::init(Default::default()); + let mut pio = Pio::new(p.PIO0, Irqs); + + let prg = PioOneWireProgram::new(&mut pio.common); + let onewire = PioOneWire::new(&mut pio.common, pio.sm0, p.PIN_2, &prg); + + let mut sensor = Ds18b20::new(onewire); + + loop { + sensor.start().await; // Start a new measurement + Timer::after_secs(1).await; // Allow 1s for the measurement to finish + match sensor.temperature().await { + Ok(temp) => info!("temp = {:?} deg C", temp), + _ => error!("sensor error"), + } + Timer::after_secs(1).await; + } +} + +/// DS18B20 temperature sensor driver +pub struct Ds18b20<'d, PIO: pio::Instance, const SM: usize> { + wire: PioOneWire<'d, PIO, SM>, +} + +impl<'d, PIO: pio::Instance, const SM: usize> Ds18b20<'d, PIO, SM> { + pub fn new(wire: PioOneWire<'d, PIO, SM>) -> Self { + Self { wire } + } + + /// Calculate CRC8 of the data + fn crc8(data: &[u8]) -> u8 { + let mut temp; + let mut data_byte; + let mut crc = 0; + for b in data { + data_byte = *b; + for _ in 0..8 { + temp = (crc ^ data_byte) & 0x01; + crc >>= 1; + if temp != 0 { + crc ^= 0x8C; + } + data_byte >>= 1; + } + } + crc + } + + /// Start a new measurement. Allow at least 1000ms before getting `temperature`. + pub async fn start(&mut self) { + self.wire.write_bytes(&[0xCC, 0x44]).await; + } + + /// Read the temperature. Ensure >1000ms has passed since `start` before calling this. + pub async fn temperature(&mut self) -> Result { + self.wire.write_bytes(&[0xCC, 0xBE]).await; + let mut data = [0; 9]; + self.wire.read_bytes(&mut data).await; + match Self::crc8(&data) == 0 { + true => Ok(((data[1] as u32) << 8 | data[0] as u32) as f32 / 16.), + false => Err(()), + } + } +} diff --git a/examples/rp23/src/bin/pio_pwm.rs b/examples/rp23/src/bin/pio_pwm.rs index 3cffd213d..11af62a7a 100644 --- a/examples/rp23/src/bin/pio_pwm.rs +++ b/examples/rp23/src/bin/pio_pwm.rs @@ -5,13 +5,12 @@ use core::time::Duration; use embassy_executor::Spawner; +use embassy_rp::bind_interrupts; use embassy_rp::block::ImageDef; -use embassy_rp::gpio::Level; use embassy_rp::peripherals::PIO0; -use embassy_rp::pio::{Common, Config, Direction, Instance, InterruptHandler, Pio, PioPin, StateMachine}; -use embassy_rp::{bind_interrupts, clocks}; +use embassy_rp::pio::{InterruptHandler, Pio}; +use embassy_rp::pio_programs::pwm::{PioPwm, PioPwmProgram}; use embassy_time::Timer; -use pio::InstructionOperands; use {defmt_rtt as _, panic_probe as _}; #[link_section = ".start_block"] @@ -24,93 +23,14 @@ bind_interrupts!(struct Irqs { PIO0_IRQ_0 => InterruptHandler; }); -pub fn to_pio_cycles(duration: Duration) -> u32 { - (clocks::clk_sys_freq() / 1_000_000) / 3 * duration.as_micros() as u32 // parentheses are required to prevent overflow -} - -pub struct PwmPio<'d, T: Instance, const SM: usize> { - sm: StateMachine<'d, T, SM>, -} - -impl<'d, T: Instance, const SM: usize> PwmPio<'d, T, SM> { - pub fn new(pio: &mut Common<'d, T>, mut sm: StateMachine<'d, T, SM>, pin: impl PioPin) -> Self { - let prg = pio_proc::pio_asm!( - ".side_set 1 opt" - "pull noblock side 0" - "mov x, osr" - "mov y, isr" - "countloop:" - "jmp x!=y noset" - "jmp skip side 1" - "noset:" - "nop" - "skip:" - "jmp y-- countloop" - ); - - pio.load_program(&prg.program); - let pin = pio.make_pio_pin(pin); - sm.set_pins(Level::High, &[&pin]); - sm.set_pin_dirs(Direction::Out, &[&pin]); - - let mut cfg = Config::default(); - cfg.use_program(&pio.load_program(&prg.program), &[&pin]); - - sm.set_config(&cfg); - - Self { sm } - } - - pub fn start(&mut self) { - self.sm.set_enable(true); - } - - pub fn stop(&mut self) { - self.sm.set_enable(false); - } - - pub fn set_period(&mut self, duration: Duration) { - let is_enabled = self.sm.is_enabled(); - while !self.sm.tx().empty() {} // Make sure that the queue is empty - self.sm.set_enable(false); - self.sm.tx().push(to_pio_cycles(duration)); - unsafe { - self.sm.exec_instr( - InstructionOperands::PULL { - if_empty: false, - block: false, - } - .encode(), - ); - self.sm.exec_instr( - InstructionOperands::OUT { - destination: ::pio::OutDestination::ISR, - bit_count: 32, - } - .encode(), - ); - }; - if is_enabled { - self.sm.set_enable(true) // Enable if previously enabled - } - } - - pub fn set_level(&mut self, level: u32) { - self.sm.tx().push(level); - } - - pub fn write(&mut self, duration: Duration) { - self.set_level(to_pio_cycles(duration)); - } -} - #[embassy_executor::main] async fn main(_spawner: Spawner) { let p = embassy_rp::init(Default::default()); let Pio { mut common, sm0, .. } = Pio::new(p.PIO0, Irqs); // Note that PIN_25 is the led pin on the Pico - let mut pwm_pio = PwmPio::new(&mut common, sm0, p.PIN_25); + let prg = PioPwmProgram::new(&mut common); + let mut pwm_pio = PioPwm::new(&mut common, sm0, p.PIN_25, &prg); pwm_pio.set_period(Duration::from_micros(REFRESH_INTERVAL)); pwm_pio.start(); diff --git a/examples/rp23/src/bin/pio_rotary_encoder.rs b/examples/rp23/src/bin/pio_rotary_encoder.rs index 9542d63b7..9a953951f 100644 --- a/examples/rp23/src/bin/pio_rotary_encoder.rs +++ b/examples/rp23/src/bin/pio_rotary_encoder.rs @@ -6,11 +6,12 @@ use defmt::info; use embassy_executor::Spawner; use embassy_rp::block::ImageDef; -use embassy_rp::gpio::Pull; use embassy_rp::peripherals::PIO0; -use embassy_rp::{bind_interrupts, pio}; -use fixed::traits::ToFixed; -use pio::{Common, Config, FifoJoin, Instance, InterruptHandler, Pio, PioPin, ShiftDirection, StateMachine}; +use embassy_rp::{ + bind_interrupts, + pio::{InterruptHandler, Pio}, + pio_programs::rotary_encoder::{Direction, PioEncoder, PioEncoderProgram}, +}; use {defmt_rtt as _, panic_probe as _}; #[link_section = ".start_block"] @@ -21,59 +22,8 @@ bind_interrupts!(struct Irqs { PIO0_IRQ_0 => InterruptHandler; }); -pub struct PioEncoder<'d, T: Instance, const SM: usize> { - sm: StateMachine<'d, T, SM>, -} - -impl<'d, T: Instance, const SM: usize> PioEncoder<'d, T, SM> { - pub fn new( - pio: &mut Common<'d, T>, - mut sm: StateMachine<'d, T, SM>, - pin_a: impl PioPin, - pin_b: impl PioPin, - ) -> Self { - let mut pin_a = pio.make_pio_pin(pin_a); - let mut pin_b = pio.make_pio_pin(pin_b); - pin_a.set_pull(Pull::Up); - pin_b.set_pull(Pull::Up); - sm.set_pin_dirs(pio::Direction::In, &[&pin_a, &pin_b]); - - let prg = pio_proc::pio_asm!("wait 1 pin 1", "wait 0 pin 1", "in pins, 2", "push",); - - let mut cfg = Config::default(); - cfg.set_in_pins(&[&pin_a, &pin_b]); - cfg.fifo_join = FifoJoin::RxOnly; - cfg.shift_in.direction = ShiftDirection::Left; - cfg.clock_divider = 10_000.to_fixed(); - cfg.use_program(&pio.load_program(&prg.program), &[]); - sm.set_config(&cfg); - sm.set_enable(true); - Self { sm } - } - - pub async fn read(&mut self) -> Direction { - loop { - match self.sm.rx().wait_pull().await { - 0 => return Direction::CounterClockwise, - 1 => return Direction::Clockwise, - _ => {} - } - } - } -} - -pub enum Direction { - Clockwise, - CounterClockwise, -} - -#[embassy_executor::main] -async fn main(_spawner: Spawner) { - let p = embassy_rp::init(Default::default()); - let Pio { mut common, sm0, .. } = Pio::new(p.PIO0, Irqs); - - let mut encoder = PioEncoder::new(&mut common, sm0, p.PIN_4, p.PIN_5); - +#[embassy_executor::task] +async fn encoder_0(mut encoder: PioEncoder<'static, PIO0, 0>) { let mut count = 0; loop { info!("Count: {}", count); @@ -83,3 +33,30 @@ async fn main(_spawner: Spawner) { }; } } + +#[embassy_executor::task] +async fn encoder_1(mut encoder: PioEncoder<'static, PIO0, 1>) { + let mut count = 0; + loop { + info!("Count: {}", count); + count += match encoder.read().await { + Direction::Clockwise => 1, + Direction::CounterClockwise => -1, + }; + } +} + +#[embassy_executor::main] +async fn main(spawner: Spawner) { + let p = embassy_rp::init(Default::default()); + let Pio { + mut common, sm0, sm1, .. + } = Pio::new(p.PIO0, Irqs); + + let prg = PioEncoderProgram::new(&mut common); + let encoder0 = PioEncoder::new(&mut common, sm0, p.PIN_4, p.PIN_5, &prg); + let encoder1 = PioEncoder::new(&mut common, sm1, p.PIN_6, p.PIN_7, &prg); + + spawner.must_spawn(encoder_0(encoder0)); + spawner.must_spawn(encoder_1(encoder1)); +} diff --git a/examples/rp23/src/bin/pio_servo.rs b/examples/rp23/src/bin/pio_servo.rs index 3202ab475..4e94103f1 100644 --- a/examples/rp23/src/bin/pio_servo.rs +++ b/examples/rp23/src/bin/pio_servo.rs @@ -5,13 +5,12 @@ use core::time::Duration; use embassy_executor::Spawner; +use embassy_rp::bind_interrupts; use embassy_rp::block::ImageDef; -use embassy_rp::gpio::Level; use embassy_rp::peripherals::PIO0; -use embassy_rp::pio::{Common, Config, Direction, Instance, InterruptHandler, Pio, PioPin, StateMachine}; -use embassy_rp::{bind_interrupts, clocks}; +use embassy_rp::pio::{Instance, InterruptHandler, Pio}; +use embassy_rp::pio_programs::pwm::{PioPwm, PioPwmProgram}; use embassy_time::Timer; -use pio::InstructionOperands; use {defmt_rtt as _, panic_probe as _}; #[link_section = ".start_block"] @@ -27,88 +26,8 @@ bind_interrupts!(struct Irqs { PIO0_IRQ_0 => InterruptHandler; }); -pub fn to_pio_cycles(duration: Duration) -> u32 { - (clocks::clk_sys_freq() / 1_000_000) / 3 * duration.as_micros() as u32 // parentheses are required to prevent overflow -} - -pub struct PwmPio<'d, T: Instance, const SM: usize> { - sm: StateMachine<'d, T, SM>, -} - -impl<'d, T: Instance, const SM: usize> PwmPio<'d, T, SM> { - pub fn new(pio: &mut Common<'d, T>, mut sm: StateMachine<'d, T, SM>, pin: impl PioPin) -> Self { - let prg = pio_proc::pio_asm!( - ".side_set 1 opt" - "pull noblock side 0" - "mov x, osr" - "mov y, isr" - "countloop:" - "jmp x!=y noset" - "jmp skip side 1" - "noset:" - "nop" - "skip:" - "jmp y-- countloop" - ); - - pio.load_program(&prg.program); - let pin = pio.make_pio_pin(pin); - sm.set_pins(Level::High, &[&pin]); - sm.set_pin_dirs(Direction::Out, &[&pin]); - - let mut cfg = Config::default(); - cfg.use_program(&pio.load_program(&prg.program), &[&pin]); - - sm.set_config(&cfg); - - Self { sm } - } - - pub fn start(&mut self) { - self.sm.set_enable(true); - } - - pub fn stop(&mut self) { - self.sm.set_enable(false); - } - - pub fn set_period(&mut self, duration: Duration) { - let is_enabled = self.sm.is_enabled(); - while !self.sm.tx().empty() {} // Make sure that the queue is empty - self.sm.set_enable(false); - self.sm.tx().push(to_pio_cycles(duration)); - unsafe { - self.sm.exec_instr( - InstructionOperands::PULL { - if_empty: false, - block: false, - } - .encode(), - ); - self.sm.exec_instr( - InstructionOperands::OUT { - destination: ::pio::OutDestination::ISR, - bit_count: 32, - } - .encode(), - ); - }; - if is_enabled { - self.sm.set_enable(true) // Enable if previously enabled - } - } - - pub fn set_level(&mut self, level: u32) { - self.sm.tx().push(level); - } - - pub fn write(&mut self, duration: Duration) { - self.set_level(to_pio_cycles(duration)); - } -} - pub struct ServoBuilder<'d, T: Instance, const SM: usize> { - pwm: PwmPio<'d, T, SM>, + pwm: PioPwm<'d, T, SM>, period: Duration, min_pulse_width: Duration, max_pulse_width: Duration, @@ -116,7 +35,7 @@ pub struct ServoBuilder<'d, T: Instance, const SM: usize> { } impl<'d, T: Instance, const SM: usize> ServoBuilder<'d, T, SM> { - pub fn new(pwm: PwmPio<'d, T, SM>) -> Self { + pub fn new(pwm: PioPwm<'d, T, SM>) -> Self { Self { pwm, period: Duration::from_micros(REFRESH_INTERVAL), @@ -158,7 +77,7 @@ impl<'d, T: Instance, const SM: usize> ServoBuilder<'d, T, SM> { } pub struct Servo<'d, T: Instance, const SM: usize> { - pwm: PwmPio<'d, T, SM>, + pwm: PioPwm<'d, T, SM>, min_pulse_width: Duration, max_pulse_width: Duration, max_degree_rotation: u64, @@ -195,7 +114,8 @@ async fn main(_spawner: Spawner) { let p = embassy_rp::init(Default::default()); let Pio { mut common, sm0, .. } = Pio::new(p.PIO0, Irqs); - let pwm_pio = PwmPio::new(&mut common, sm0, p.PIN_1); + let prg = PioPwmProgram::new(&mut common); + let pwm_pio = PioPwm::new(&mut common, sm0, p.PIN_1, &prg); let mut servo = ServoBuilder::new(pwm_pio) .set_max_degree_rotation(120) // Example of adjusting values for MG996R servo .set_min_pulse_width(Duration::from_micros(350)) // This value was detemined by a rough experiment. diff --git a/examples/rp23/src/bin/pio_stepper.rs b/examples/rp23/src/bin/pio_stepper.rs index 5e87da6eb..4fabe78ca 100644 --- a/examples/rp23/src/bin/pio_stepper.rs +++ b/examples/rp23/src/bin/pio_stepper.rs @@ -3,18 +3,15 @@ #![no_std] #![no_main] -use core::mem::{self, MaybeUninit}; use defmt::info; use embassy_executor::Spawner; use embassy_rp::bind_interrupts; use embassy_rp::block::ImageDef; use embassy_rp::peripherals::PIO0; -use embassy_rp::pio::{Common, Config, Direction, Instance, InterruptHandler, Irq, Pio, PioPin, StateMachine}; +use embassy_rp::pio::{InterruptHandler, Pio}; +use embassy_rp::pio_programs::stepper::{PioStepper, PioStepperProgram}; use embassy_time::{with_timeout, Duration, Timer}; -use fixed::traits::ToFixed; -use fixed::types::extra::U8; -use fixed::FixedU32; use {defmt_rtt as _, panic_probe as _}; #[link_section = ".start_block"] @@ -25,126 +22,6 @@ bind_interrupts!(struct Irqs { PIO0_IRQ_0 => InterruptHandler; }); -pub struct PioStepper<'d, T: Instance, const SM: usize> { - irq: Irq<'d, T, SM>, - sm: StateMachine<'d, T, SM>, -} - -impl<'d, T: Instance, const SM: usize> PioStepper<'d, T, SM> { - pub fn new( - pio: &mut Common<'d, T>, - mut sm: StateMachine<'d, T, SM>, - irq: Irq<'d, T, SM>, - pin0: impl PioPin, - pin1: impl PioPin, - pin2: impl PioPin, - pin3: impl PioPin, - ) -> Self { - let prg = pio_proc::pio_asm!( - "pull block", - "mov x, osr", - "pull block", - "mov y, osr", - "jmp !x end", - "loop:", - "jmp !osre step", - "mov osr, y", - "step:", - "out pins, 4 [31]" - "jmp x-- loop", - "end:", - "irq 0 rel" - ); - let pin0 = pio.make_pio_pin(pin0); - let pin1 = pio.make_pio_pin(pin1); - let pin2 = pio.make_pio_pin(pin2); - let pin3 = pio.make_pio_pin(pin3); - sm.set_pin_dirs(Direction::Out, &[&pin0, &pin1, &pin2, &pin3]); - let mut cfg = Config::default(); - cfg.set_out_pins(&[&pin0, &pin1, &pin2, &pin3]); - cfg.clock_divider = (125_000_000 / (100 * 136)).to_fixed(); - cfg.use_program(&pio.load_program(&prg.program), &[]); - sm.set_config(&cfg); - sm.set_enable(true); - Self { irq, sm } - } - - // Set pulse frequency - pub fn set_frequency(&mut self, freq: u32) { - let clock_divider: FixedU32 = (125_000_000 / (freq * 136)).to_fixed(); - assert!(clock_divider <= 65536, "clkdiv must be <= 65536"); - assert!(clock_divider >= 1, "clkdiv must be >= 1"); - self.sm.set_clock_divider(clock_divider); - self.sm.clkdiv_restart(); - } - - // Full step, one phase - pub async fn step(&mut self, steps: i32) { - if steps > 0 { - self.run(steps, 0b1000_0100_0010_0001_1000_0100_0010_0001).await - } else { - self.run(-steps, 0b0001_0010_0100_1000_0001_0010_0100_1000).await - } - } - - // Full step, two phase - pub async fn step2(&mut self, steps: i32) { - if steps > 0 { - self.run(steps, 0b1001_1100_0110_0011_1001_1100_0110_0011).await - } else { - self.run(-steps, 0b0011_0110_1100_1001_0011_0110_1100_1001).await - } - } - - // Half step - pub async fn step_half(&mut self, steps: i32) { - if steps > 0 { - self.run(steps, 0b1001_1000_1100_0100_0110_0010_0011_0001).await - } else { - self.run(-steps, 0b0001_0011_0010_0110_0100_1100_1000_1001).await - } - } - - async fn run(&mut self, steps: i32, pattern: u32) { - self.sm.tx().wait_push(steps as u32).await; - self.sm.tx().wait_push(pattern).await; - let drop = OnDrop::new(|| { - self.sm.clear_fifos(); - unsafe { - self.sm.exec_instr( - pio::InstructionOperands::JMP { - address: 0, - condition: pio::JmpCondition::Always, - } - .encode(), - ); - } - }); - self.irq.wait().await; - drop.defuse(); - } -} - -struct OnDrop { - f: MaybeUninit, -} - -impl OnDrop { - pub fn new(f: F) -> Self { - Self { f: MaybeUninit::new(f) } - } - - pub fn defuse(self) { - mem::forget(self) - } -} - -impl Drop for OnDrop { - fn drop(&mut self) { - unsafe { self.f.as_ptr().read()() } - } -} - #[embassy_executor::main] async fn main(_spawner: Spawner) { let p = embassy_rp::init(Default::default()); @@ -152,14 +29,18 @@ async fn main(_spawner: Spawner) { mut common, irq0, sm0, .. } = Pio::new(p.PIO0, Irqs); - let mut stepper = PioStepper::new(&mut common, sm0, irq0, p.PIN_4, p.PIN_5, p.PIN_6, p.PIN_7); + let prg = PioStepperProgram::new(&mut common); + let mut stepper = PioStepper::new(&mut common, sm0, irq0, p.PIN_4, p.PIN_5, p.PIN_6, p.PIN_7, &prg); stepper.set_frequency(120); loop { info!("CW full steps"); stepper.step(1000).await; info!("CCW full steps, drop after 1 sec"); - if let Err(_) = with_timeout(Duration::from_secs(1), stepper.step(i32::MIN)).await { + if with_timeout(Duration::from_secs(1), stepper.step(-i32::MAX)) + .await + .is_err() + { info!("Time's up!"); Timer::after(Duration::from_secs(1)).await; } diff --git a/examples/rp23/src/bin/pio_uart.rs b/examples/rp23/src/bin/pio_uart.rs new file mode 100644 index 000000000..6899a5d7c --- /dev/null +++ b/examples/rp23/src/bin/pio_uart.rs @@ -0,0 +1,203 @@ +//! This example shows how to use the PIO module in the RP2040 chip to implement a duplex UART. +//! The PIO module is a very powerful peripheral that can be used to implement many different +//! protocols. It is a very flexible state machine that can be programmed to do almost anything. +//! +//! This example opens up a USB device that implements a CDC ACM serial port. It then uses the +//! PIO module to implement a UART that is connected to the USB serial port. This allows you to +//! communicate with a device connected to the RP2040 over USB serial. + +#![no_std] +#![no_main] +#![allow(async_fn_in_trait)] + +use defmt::{info, panic, trace}; +use embassy_executor::Spawner; +use embassy_futures::join::{join, join3}; +use embassy_rp::bind_interrupts; +use embassy_rp::block::ImageDef; +use embassy_rp::peripherals::{PIO0, USB}; +use embassy_rp::pio; +use embassy_rp::pio_programs::uart::{PioUartRx, PioUartRxProgram, PioUartTx, PioUartTxProgram}; +use embassy_rp::usb::{Driver, Instance, InterruptHandler}; +use embassy_sync::blocking_mutex::raw::NoopRawMutex; +use embassy_sync::pipe::Pipe; +use embassy_usb::class::cdc_acm::{CdcAcmClass, Receiver, Sender, State}; +use embassy_usb::driver::EndpointError; +use embassy_usb::{Builder, Config}; +use embedded_io_async::{Read, Write}; +use {defmt_rtt as _, panic_probe as _}; + +#[link_section = ".start_block"] +#[used] +pub static IMAGE_DEF: ImageDef = ImageDef::secure_exe(); + +bind_interrupts!(struct Irqs { + USBCTRL_IRQ => InterruptHandler; + PIO0_IRQ_0 => pio::InterruptHandler; +}); + +#[embassy_executor::main] +async fn main(_spawner: Spawner) { + info!("Hello there!"); + + let p = embassy_rp::init(Default::default()); + + // Create the driver, from the HAL. + let driver = Driver::new(p.USB, Irqs); + + // Create embassy-usb Config + let mut config = Config::new(0xc0de, 0xcafe); + config.manufacturer = Some("Embassy"); + config.product = Some("PIO UART example"); + config.serial_number = Some("12345678"); + config.max_power = 100; + config.max_packet_size_0 = 64; + + // Required for windows compatibility. + // https://developer.nordicsemi.com/nRF_Connect_SDK/doc/1.9.1/kconfig/CONFIG_CDC_ACM_IAD.html#help + config.device_class = 0xEF; + config.device_sub_class = 0x02; + config.device_protocol = 0x01; + config.composite_with_iads = true; + + // Create embassy-usb DeviceBuilder using the driver and config. + // It needs some buffers for building the descriptors. + let mut config_descriptor = [0; 256]; + let mut bos_descriptor = [0; 256]; + let mut control_buf = [0; 64]; + + let mut state = State::new(); + + let mut builder = Builder::new( + driver, + config, + &mut config_descriptor, + &mut bos_descriptor, + &mut [], // no msos descriptors + &mut control_buf, + ); + + // Create classes on the builder. + let class = CdcAcmClass::new(&mut builder, &mut state, 64); + + // Build the builder. + let mut usb = builder.build(); + + // Run the USB device. + let usb_fut = usb.run(); + + // PIO UART setup + let pio::Pio { + mut common, sm0, sm1, .. + } = pio::Pio::new(p.PIO0, Irqs); + + let tx_program = PioUartTxProgram::new(&mut common); + let mut uart_tx = PioUartTx::new(9600, &mut common, sm0, p.PIN_4, &tx_program); + + let rx_program = PioUartRxProgram::new(&mut common); + let mut uart_rx = PioUartRx::new(9600, &mut common, sm1, p.PIN_5, &rx_program); + + // Pipe setup + let mut usb_pipe: Pipe = Pipe::new(); + let (mut usb_pipe_reader, mut usb_pipe_writer) = usb_pipe.split(); + + let mut uart_pipe: Pipe = Pipe::new(); + let (mut uart_pipe_reader, mut uart_pipe_writer) = uart_pipe.split(); + + let (mut usb_tx, mut usb_rx) = class.split(); + + // Read + write from USB + let usb_future = async { + loop { + info!("Wait for USB connection"); + usb_rx.wait_connection().await; + info!("Connected"); + let _ = join( + usb_read(&mut usb_rx, &mut uart_pipe_writer), + usb_write(&mut usb_tx, &mut usb_pipe_reader), + ) + .await; + info!("Disconnected"); + } + }; + + // Read + write from UART + let uart_future = join( + uart_read(&mut uart_rx, &mut usb_pipe_writer), + uart_write(&mut uart_tx, &mut uart_pipe_reader), + ); + + // Run everything concurrently. + // If we had made everything `'static` above instead, we could do this using separate tasks instead. + join3(usb_fut, usb_future, uart_future).await; +} + +struct Disconnected {} + +impl From for Disconnected { + fn from(val: EndpointError) -> Self { + match val { + EndpointError::BufferOverflow => panic!("Buffer overflow"), + EndpointError::Disabled => Disconnected {}, + } + } +} + +/// Read from the USB and write it to the UART TX pipe +async fn usb_read<'d, T: Instance + 'd>( + usb_rx: &mut Receiver<'d, Driver<'d, T>>, + uart_pipe_writer: &mut embassy_sync::pipe::Writer<'_, NoopRawMutex, 20>, +) -> Result<(), Disconnected> { + let mut buf = [0; 64]; + loop { + let n = usb_rx.read_packet(&mut buf).await?; + let data = &buf[..n]; + trace!("USB IN: {:x}", data); + (*uart_pipe_writer).write(data).await; + } +} + +/// Read from the USB TX pipe and write it to the USB +async fn usb_write<'d, T: Instance + 'd>( + usb_tx: &mut Sender<'d, Driver<'d, T>>, + usb_pipe_reader: &mut embassy_sync::pipe::Reader<'_, NoopRawMutex, 20>, +) -> Result<(), Disconnected> { + let mut buf = [0; 64]; + loop { + let n = (*usb_pipe_reader).read(&mut buf).await; + let data = &buf[..n]; + trace!("USB OUT: {:x}", data); + usb_tx.write_packet(&data).await?; + } +} + +/// Read from the UART and write it to the USB TX pipe +async fn uart_read( + uart_rx: &mut PioUartRx<'_, PIO, SM>, + usb_pipe_writer: &mut embassy_sync::pipe::Writer<'_, NoopRawMutex, 20>, +) -> ! { + let mut buf = [0; 64]; + loop { + let n = uart_rx.read(&mut buf).await.expect("UART read error"); + if n == 0 { + continue; + } + let data = &buf[..n]; + trace!("UART IN: {:x}", buf); + (*usb_pipe_writer).write(data).await; + } +} + +/// Read from the UART TX pipe and write it to the UART +async fn uart_write( + uart_tx: &mut PioUartTx<'_, PIO, SM>, + uart_pipe_reader: &mut embassy_sync::pipe::Reader<'_, NoopRawMutex, 20>, +) -> ! { + let mut buf = [0; 64]; + loop { + let n = (*uart_pipe_reader).read(&mut buf).await; + let data = &buf[..n]; + trace!("UART OUT: {:x}", data); + let _ = uart_tx.write(&data).await; + } +} diff --git a/examples/rp23/src/bin/pio_ws2812.rs b/examples/rp23/src/bin/pio_ws2812.rs index 1f1984c4d..4d258234e 100644 --- a/examples/rp23/src/bin/pio_ws2812.rs +++ b/examples/rp23/src/bin/pio_ws2812.rs @@ -6,16 +6,12 @@ use defmt::*; use embassy_executor::Spawner; +use embassy_rp::bind_interrupts; use embassy_rp::block::ImageDef; -use embassy_rp::dma::{AnyChannel, Channel}; use embassy_rp::peripherals::PIO0; -use embassy_rp::pio::{ - Common, Config, FifoJoin, Instance, InterruptHandler, Pio, PioPin, ShiftConfig, ShiftDirection, StateMachine, -}; -use embassy_rp::{bind_interrupts, clocks, into_ref, Peripheral, PeripheralRef}; -use embassy_time::{Duration, Ticker, Timer}; -use fixed::types::U24F8; -use fixed_macro::fixed; +use embassy_rp::pio::{InterruptHandler, Pio}; +use embassy_rp::pio_programs::ws2812::{PioWs2812, PioWs2812Program}; +use embassy_time::{Duration, Ticker}; use smart_leds::RGB8; use {defmt_rtt as _, panic_probe as _}; @@ -27,96 +23,6 @@ bind_interrupts!(struct Irqs { PIO0_IRQ_0 => InterruptHandler; }); -pub struct Ws2812<'d, P: Instance, const S: usize, const N: usize> { - dma: PeripheralRef<'d, AnyChannel>, - sm: StateMachine<'d, P, S>, -} - -impl<'d, P: Instance, const S: usize, const N: usize> Ws2812<'d, P, S, N> { - pub fn new( - pio: &mut Common<'d, P>, - mut sm: StateMachine<'d, P, S>, - dma: impl Peripheral

+ 'd, - pin: impl PioPin, - ) -> Self { - into_ref!(dma); - - // Setup sm0 - - // prepare the PIO program - let side_set = pio::SideSet::new(false, 1, false); - let mut a: pio::Assembler<32> = pio::Assembler::new_with_side_set(side_set); - - const T1: u8 = 2; // start bit - const T2: u8 = 5; // data bit - const T3: u8 = 3; // stop bit - const CYCLES_PER_BIT: u32 = (T1 + T2 + T3) as u32; - - let mut wrap_target = a.label(); - let mut wrap_source = a.label(); - let mut do_zero = a.label(); - a.set_with_side_set(pio::SetDestination::PINDIRS, 1, 0); - a.bind(&mut wrap_target); - // Do stop bit - a.out_with_delay_and_side_set(pio::OutDestination::X, 1, T3 - 1, 0); - // Do start bit - a.jmp_with_delay_and_side_set(pio::JmpCondition::XIsZero, &mut do_zero, T1 - 1, 1); - // Do data bit = 1 - a.jmp_with_delay_and_side_set(pio::JmpCondition::Always, &mut wrap_target, T2 - 1, 1); - a.bind(&mut do_zero); - // Do data bit = 0 - a.nop_with_delay_and_side_set(T2 - 1, 0); - a.bind(&mut wrap_source); - - let prg = a.assemble_with_wrap(wrap_source, wrap_target); - let mut cfg = Config::default(); - - // Pin config - let out_pin = pio.make_pio_pin(pin); - cfg.set_out_pins(&[&out_pin]); - cfg.set_set_pins(&[&out_pin]); - - cfg.use_program(&pio.load_program(&prg), &[&out_pin]); - - // Clock config, measured in kHz to avoid overflows - // TODO CLOCK_FREQ should come from embassy_rp - let clock_freq = U24F8::from_num(clocks::clk_sys_freq() / 1000); - let ws2812_freq = fixed!(800: U24F8); - let bit_freq = ws2812_freq * CYCLES_PER_BIT; - cfg.clock_divider = clock_freq / bit_freq; - - // FIFO config - cfg.fifo_join = FifoJoin::TxOnly; - cfg.shift_out = ShiftConfig { - auto_fill: true, - threshold: 24, - direction: ShiftDirection::Left, - }; - - sm.set_config(&cfg); - sm.set_enable(true); - - Self { - dma: dma.map_into(), - sm, - } - } - - pub async fn write(&mut self, colors: &[RGB8; N]) { - // Precompute the word bytes from the colors - let mut words = [0u32; N]; - for i in 0..N { - let word = (u32::from(colors[i].g) << 24) | (u32::from(colors[i].r) << 16) | (u32::from(colors[i].b) << 8); - words[i] = word; - } - - // DMA transfer - self.sm.tx().dma_push(self.dma.reborrow(), &words).await; - - Timer::after_micros(55).await; - } -} - /// Input a value 0 to 255 to get a color value /// The colours are a transition r - g - b - back to r. fn wheel(mut wheel_pos: u8) -> RGB8 { @@ -147,7 +53,8 @@ async fn main(_spawner: Spawner) { // Common neopixel pins: // Thing plus: 8 // Adafruit Feather: 16; Adafruit Feather+RFM95: 4 - let mut ws2812 = Ws2812::new(&mut common, sm0, p.DMA_CH0, p.PIN_16); + let program = PioWs2812Program::new(&mut common); + let mut ws2812 = PioWs2812::new(&mut common, sm0, p.DMA_CH0, p.PIN_16, &program); // Loop forever making RGB values and pushing them out to the WS2812. let mut ticker = Ticker::every(Duration::from_millis(10)); From fc978c2ee9dda07b9fe7113e2aa0f2d3fb33fd1b Mon Sep 17 00:00:00 2001 From: Caleb Jamison Date: Wed, 9 Oct 2024 11:37:15 -0400 Subject: [PATCH 056/361] Fix rp23 i2s example, boot_sel isn't supported yet. --- examples/rp23/src/bin/pio_i2s.rs | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/examples/rp23/src/bin/pio_i2s.rs b/examples/rp23/src/bin/pio_i2s.rs index 90491fb45..1fd34357b 100644 --- a/examples/rp23/src/bin/pio_i2s.rs +++ b/examples/rp23/src/bin/pio_i2s.rs @@ -15,6 +15,7 @@ use core::mem; use embassy_executor::Spawner; use embassy_rp::bind_interrupts; use embassy_rp::block::ImageDef; +use embassy_rp::gpio::{Input, Pull}; use embassy_rp::peripherals::PIO0; use embassy_rp::pio::{InterruptHandler, Pio}; use embassy_rp::pio_programs::i2s::{PioI2sOut, PioI2sOutProgram}; @@ -35,7 +36,7 @@ const CHANNELS: u32 = 2; #[embassy_executor::main] async fn main(_spawner: Spawner) { - let mut p = embassy_rp::init(Default::default()); + let p = embassy_rp::init(Default::default()); // Setup pio state machine for i2s output let Pio { mut common, sm0, .. } = Pio::new(p.PIO0, Irqs); @@ -58,6 +59,8 @@ async fn main(_spawner: Spawner) { &program, ); + let fade_input = Input::new(p.PIN_0, Pull::Up); + // create two audio buffers (back and front) which will take turns being // filled with new audio data and being sent to the pio fifo using dma const BUFFER_SIZE: usize = 960; @@ -75,7 +78,7 @@ async fn main(_spawner: Spawner) { let dma_future = i2s.write(front_buffer); // fade in audio when bootsel is pressed - let fade_target = if p.BOOTSEL.is_pressed() { i32::MAX } else { 0 }; + let fade_target = if fade_input.is_low() { i32::MAX } else { 0 }; // fill back buffer with fresh audio samples before awaiting the dma future for s in back_buffer.iter_mut() { From c7f7728eb164edea2f8041d2511a976f9ebc17ca Mon Sep 17 00:00:00 2001 From: Caleb Jamison Date: Wed, 9 Oct 2024 11:44:58 -0400 Subject: [PATCH 057/361] cargo +nightly fmt --- embassy-rp/src/pio_programs/i2s.rs | 14 ++++++-------- embassy-rp/src/pio_programs/pwm.rs | 9 ++++----- embassy-rp/src/pio_programs/rotary_encoder.rs | 3 ++- embassy-rp/src/pio_programs/stepper.rs | 3 ++- embassy-rp/src/pio_programs/uart.rs | 15 +++++++-------- embassy-rp/src/pio_programs/ws2812.rs | 14 +++++++------- 6 files changed, 28 insertions(+), 30 deletions(-) diff --git a/embassy-rp/src/pio_programs/i2s.rs b/embassy-rp/src/pio_programs/i2s.rs index 3c8ef8bb6..e3f1f89d4 100644 --- a/embassy-rp/src/pio_programs/i2s.rs +++ b/embassy-rp/src/pio_programs/i2s.rs @@ -1,15 +1,13 @@ //! Pio backed I2s output -use crate::{ - dma::{AnyChannel, Channel, Transfer}, - into_ref, - pio::{ - Common, Config, Direction, FifoJoin, Instance, LoadedProgram, PioPin, ShiftConfig, ShiftDirection, StateMachine, - }, - Peripheral, PeripheralRef, -}; use fixed::traits::ToFixed; +use crate::dma::{AnyChannel, Channel, Transfer}; +use crate::pio::{ + Common, Config, Direction, FifoJoin, Instance, LoadedProgram, PioPin, ShiftConfig, ShiftDirection, StateMachine, +}; +use crate::{into_ref, Peripheral, PeripheralRef}; + /// This struct represents an i2s output driver program pub struct PioI2sOutProgram<'a, PIO: Instance> { prg: LoadedProgram<'a, PIO>, diff --git a/embassy-rp/src/pio_programs/pwm.rs b/embassy-rp/src/pio_programs/pwm.rs index 5dfd70cc9..8a0f3d5ee 100644 --- a/embassy-rp/src/pio_programs/pwm.rs +++ b/embassy-rp/src/pio_programs/pwm.rs @@ -2,13 +2,12 @@ use core::time::Duration; -use crate::{ - clocks, - gpio::Level, - pio::{Common, Config, Direction, Instance, LoadedProgram, PioPin, StateMachine}, -}; use pio::InstructionOperands; +use crate::clocks; +use crate::gpio::Level; +use crate::pio::{Common, Config, Direction, Instance, LoadedProgram, PioPin, StateMachine}; + fn to_pio_cycles(duration: Duration) -> u32 { (clocks::clk_sys_freq() / 1_000_000) / 3 * duration.as_micros() as u32 // parentheses are required to prevent overflow } diff --git a/embassy-rp/src/pio_programs/rotary_encoder.rs b/embassy-rp/src/pio_programs/rotary_encoder.rs index 323f839bc..86423fd31 100644 --- a/embassy-rp/src/pio_programs/rotary_encoder.rs +++ b/embassy-rp/src/pio_programs/rotary_encoder.rs @@ -1,8 +1,9 @@ //! PIO backed quadrature encoder +use fixed::traits::ToFixed; + use crate::gpio::Pull; use crate::pio::{self, Common, Config, FifoJoin, Instance, LoadedProgram, PioPin, ShiftDirection, StateMachine}; -use fixed::traits::ToFixed; /// This struct represents an Encoder program loaded into pio instruction memory. pub struct PioEncoderProgram<'a, PIO: Instance> { diff --git a/embassy-rp/src/pio_programs/stepper.rs b/embassy-rp/src/pio_programs/stepper.rs index 0ecc4eff0..0d58c754c 100644 --- a/embassy-rp/src/pio_programs/stepper.rs +++ b/embassy-rp/src/pio_programs/stepper.rs @@ -2,11 +2,12 @@ use core::mem::{self, MaybeUninit}; -use crate::pio::{Common, Config, Direction, Instance, Irq, LoadedProgram, PioPin, StateMachine}; use fixed::traits::ToFixed; use fixed::types::extra::U8; use fixed::FixedU32; +use crate::pio::{Common, Config, Direction, Instance, Irq, LoadedProgram, PioPin, StateMachine}; + /// This struct represents a Stepper driver program loaded into pio instruction memory. pub struct PioStepperProgram<'a, PIO: Instance> { prg: LoadedProgram<'a, PIO>, diff --git a/embassy-rp/src/pio_programs/uart.rs b/embassy-rp/src/pio_programs/uart.rs index f4b3b204e..c643f1063 100644 --- a/embassy-rp/src/pio_programs/uart.rs +++ b/embassy-rp/src/pio_programs/uart.rs @@ -1,17 +1,16 @@ //! Pio backed uart drivers -use crate::{ - clocks::clk_sys_freq, - gpio::Level, - pio::{ - Common, Config, Direction as PioDirection, FifoJoin, Instance, LoadedProgram, PioPin, ShiftDirection, - StateMachine, - }, -}; use core::convert::Infallible; + use embedded_io_async::{ErrorType, Read, Write}; use fixed::traits::ToFixed; +use crate::clocks::clk_sys_freq; +use crate::gpio::Level; +use crate::pio::{ + Common, Config, Direction as PioDirection, FifoJoin, Instance, LoadedProgram, PioPin, ShiftDirection, StateMachine, +}; + /// This struct represents a uart tx program loaded into pio instruction memory. pub struct PioUartTxProgram<'a, PIO: Instance> { prg: LoadedProgram<'a, PIO>, diff --git a/embassy-rp/src/pio_programs/ws2812.rs b/embassy-rp/src/pio_programs/ws2812.rs index 3fc7e1017..875f0209f 100644 --- a/embassy-rp/src/pio_programs/ws2812.rs +++ b/embassy-rp/src/pio_programs/ws2812.rs @@ -1,16 +1,16 @@ //! [ws2812](https://www.sparkfun.com/datasheets/LCD/HD44780.pdf) -use crate::{ - clocks::clk_sys_freq, - dma::{AnyChannel, Channel}, - into_ref, - pio::{Common, Config, FifoJoin, Instance, LoadedProgram, PioPin, ShiftConfig, ShiftDirection, StateMachine}, - Peripheral, PeripheralRef, -}; use embassy_time::Timer; use fixed::types::U24F8; use smart_leds::RGB8; +use crate::clocks::clk_sys_freq; +use crate::dma::{AnyChannel, Channel}; +use crate::pio::{ + Common, Config, FifoJoin, Instance, LoadedProgram, PioPin, ShiftConfig, ShiftDirection, StateMachine, +}; +use crate::{into_ref, Peripheral, PeripheralRef}; + const T1: u8 = 2; // start bit const T2: u8 = 5; // data bit const T3: u8 = 3; // stop bit From e47c031b671555f3fffe6b128cbb9d3f8bfec534 Mon Sep 17 00:00:00 2001 From: Caleb Jamison Date: Wed, 9 Oct 2024 11:47:04 -0400 Subject: [PATCH 058/361] fmt examples too --- examples/rp/src/bin/pio_rotary_encoder.rs | 8 +++----- examples/rp/src/bin/pio_uart.rs | 3 +-- examples/rp23/src/bin/pio_rotary_encoder.rs | 8 +++----- examples/rp23/src/bin/pio_uart.rs | 3 +-- 4 files changed, 8 insertions(+), 14 deletions(-) diff --git a/examples/rp/src/bin/pio_rotary_encoder.rs b/examples/rp/src/bin/pio_rotary_encoder.rs index a7ecc8d0e..2750f61ae 100644 --- a/examples/rp/src/bin/pio_rotary_encoder.rs +++ b/examples/rp/src/bin/pio_rotary_encoder.rs @@ -5,12 +5,10 @@ use defmt::info; use embassy_executor::Spawner; +use embassy_rp::bind_interrupts; use embassy_rp::peripherals::PIO0; -use embassy_rp::{ - bind_interrupts, - pio::{InterruptHandler, Pio}, - pio_programs::rotary_encoder::{Direction, PioEncoder, PioEncoderProgram}, -}; +use embassy_rp::pio::{InterruptHandler, Pio}; +use embassy_rp::pio_programs::rotary_encoder::{Direction, PioEncoder, PioEncoderProgram}; use {defmt_rtt as _, panic_probe as _}; bind_interrupts!(struct Irqs { diff --git a/examples/rp/src/bin/pio_uart.rs b/examples/rp/src/bin/pio_uart.rs index b9e01b0ac..aaf2a524f 100644 --- a/examples/rp/src/bin/pio_uart.rs +++ b/examples/rp/src/bin/pio_uart.rs @@ -13,11 +13,10 @@ use defmt::{info, panic, trace}; use embassy_executor::Spawner; use embassy_futures::join::{join, join3}; -use embassy_rp::bind_interrupts; use embassy_rp::peripherals::{PIO0, USB}; -use embassy_rp::pio; use embassy_rp::pio_programs::uart::{PioUartRx, PioUartRxProgram, PioUartTx, PioUartTxProgram}; use embassy_rp::usb::{Driver, Instance, InterruptHandler}; +use embassy_rp::{bind_interrupts, pio}; use embassy_sync::blocking_mutex::raw::NoopRawMutex; use embassy_sync::pipe::Pipe; use embassy_usb::class::cdc_acm::{CdcAcmClass, Receiver, Sender, State}; diff --git a/examples/rp23/src/bin/pio_rotary_encoder.rs b/examples/rp23/src/bin/pio_rotary_encoder.rs index 9a953951f..2bb0e67f9 100644 --- a/examples/rp23/src/bin/pio_rotary_encoder.rs +++ b/examples/rp23/src/bin/pio_rotary_encoder.rs @@ -5,13 +5,11 @@ use defmt::info; use embassy_executor::Spawner; +use embassy_rp::bind_interrupts; use embassy_rp::block::ImageDef; use embassy_rp::peripherals::PIO0; -use embassy_rp::{ - bind_interrupts, - pio::{InterruptHandler, Pio}, - pio_programs::rotary_encoder::{Direction, PioEncoder, PioEncoderProgram}, -}; +use embassy_rp::pio::{InterruptHandler, Pio}; +use embassy_rp::pio_programs::rotary_encoder::{Direction, PioEncoder, PioEncoderProgram}; use {defmt_rtt as _, panic_probe as _}; #[link_section = ".start_block"] diff --git a/examples/rp23/src/bin/pio_uart.rs b/examples/rp23/src/bin/pio_uart.rs index 6899a5d7c..f8398c22a 100644 --- a/examples/rp23/src/bin/pio_uart.rs +++ b/examples/rp23/src/bin/pio_uart.rs @@ -13,12 +13,11 @@ use defmt::{info, panic, trace}; use embassy_executor::Spawner; use embassy_futures::join::{join, join3}; -use embassy_rp::bind_interrupts; use embassy_rp::block::ImageDef; use embassy_rp::peripherals::{PIO0, USB}; -use embassy_rp::pio; use embassy_rp::pio_programs::uart::{PioUartRx, PioUartRxProgram, PioUartTx, PioUartTxProgram}; use embassy_rp::usb::{Driver, Instance, InterruptHandler}; +use embassy_rp::{bind_interrupts, pio}; use embassy_sync::blocking_mutex::raw::NoopRawMutex; use embassy_sync::pipe::Pipe; use embassy_usb::class::cdc_acm::{CdcAcmClass, Receiver, Sender, State}; From 22fe4932577cf261a561e33c2a8378c168fab1dd Mon Sep 17 00:00:00 2001 From: Bjorn <75190918+BjornTheProgrammer@users.noreply.github.com> Date: Wed, 9 Oct 2024 10:12:43 -0700 Subject: [PATCH 059/361] Better docs and adding of release for PioPwm --- embassy-rp/src/pio_programs/pwm.rs | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/embassy-rp/src/pio_programs/pwm.rs b/embassy-rp/src/pio_programs/pwm.rs index 8a0f3d5ee..7b3157877 100644 --- a/embassy-rp/src/pio_programs/pwm.rs +++ b/embassy-rp/src/pio_programs/pwm.rs @@ -1,6 +1,7 @@ //! PIO backed PWM driver use core::time::Duration; +use crate::pio::Pin; use pio::InstructionOperands; @@ -8,6 +9,7 @@ use crate::clocks; use crate::gpio::Level; use crate::pio::{Common, Config, Direction, Instance, LoadedProgram, PioPin, StateMachine}; +/// This converts the duration provided into the number of cycles the PIO needs to run to make it take the same time fn to_pio_cycles(duration: Duration) -> u32 { (clocks::clk_sys_freq() / 1_000_000) / 3 * duration.as_micros() as u32 // parentheses are required to prevent overflow } @@ -43,6 +45,7 @@ impl<'a, PIO: Instance> PioPwmProgram<'a, PIO> { /// Pio backed PWM output pub struct PioPwm<'d, T: Instance, const SM: usize> { sm: StateMachine<'d, T, SM>, + pin: Pin<'d, T> } impl<'d, T: Instance, const SM: usize> PioPwm<'d, T, SM> { @@ -62,20 +65,20 @@ impl<'d, T: Instance, const SM: usize> PioPwm<'d, T, SM> { sm.set_config(&cfg); - Self { sm } + Self { sm, pin } } - /// Enable PWM output + /// Enable's the PIO program, continuing the wave generation from the PIO program. pub fn start(&mut self) { self.sm.set_enable(true); } - /// Disable PWM output + /// Stops the PIO program, ceasing all signals from the PIN that were generated via PIO. pub fn stop(&mut self) { self.sm.set_enable(false); } - /// Set pwm period + /// Sets the pwm period, which is the length of time for each pio wave until reset. pub fn set_period(&mut self, duration: Duration) { let is_enabled = self.sm.is_enabled(); while !self.sm.tx().empty() {} // Make sure that the queue is empty @@ -102,7 +105,8 @@ impl<'d, T: Instance, const SM: usize> PioPwm<'d, T, SM> { } } - fn set_level(&mut self, level: u32) { + /// Set the number of pio cycles to set the wave on high to. + pub fn set_level(&mut self, level: u32) { self.sm.tx().push(level); } @@ -110,4 +114,9 @@ impl<'d, T: Instance, const SM: usize> PioPwm<'d, T, SM> { pub fn write(&mut self, duration: Duration) { self.set_level(to_pio_cycles(duration)); } + + // Return the state machine and pin. + pub fn release(self) -> (StateMachine<'d, T, SM>, Pin<'d, T>) { + (self.sm, self.pin) + } } From 1b32b7bcb44a9df149bb9194cb83e6c1f370db12 Mon Sep 17 00:00:00 2001 From: Caleb Jamison Date: Wed, 9 Oct 2024 16:51:52 -0400 Subject: [PATCH 060/361] fmt --- embassy-rp/src/pio_programs/pwm.rs | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/embassy-rp/src/pio_programs/pwm.rs b/embassy-rp/src/pio_programs/pwm.rs index 7b3157877..dfc5feb19 100644 --- a/embassy-rp/src/pio_programs/pwm.rs +++ b/embassy-rp/src/pio_programs/pwm.rs @@ -1,13 +1,12 @@ //! PIO backed PWM driver use core::time::Duration; -use crate::pio::Pin; use pio::InstructionOperands; use crate::clocks; use crate::gpio::Level; -use crate::pio::{Common, Config, Direction, Instance, LoadedProgram, PioPin, StateMachine}; +use crate::pio::{Common, Config, Direction, Instance, LoadedProgram, Pin, PioPin, StateMachine}; /// This converts the duration provided into the number of cycles the PIO needs to run to make it take the same time fn to_pio_cycles(duration: Duration) -> u32 { @@ -45,7 +44,7 @@ impl<'a, PIO: Instance> PioPwmProgram<'a, PIO> { /// Pio backed PWM output pub struct PioPwm<'d, T: Instance, const SM: usize> { sm: StateMachine<'d, T, SM>, - pin: Pin<'d, T> + pin: Pin<'d, T>, } impl<'d, T: Instance, const SM: usize> PioPwm<'d, T, SM> { From 70bd158d03afa92edb0ce23d05860bde1651cf61 Mon Sep 17 00:00:00 2001 From: Caleb Jamison Date: Wed, 9 Oct 2024 16:57:02 -0400 Subject: [PATCH 061/361] Make the docs be docs --- embassy-rp/src/pio_programs/pwm.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/embassy-rp/src/pio_programs/pwm.rs b/embassy-rp/src/pio_programs/pwm.rs index dfc5feb19..c6502387a 100644 --- a/embassy-rp/src/pio_programs/pwm.rs +++ b/embassy-rp/src/pio_programs/pwm.rs @@ -114,7 +114,7 @@ impl<'d, T: Instance, const SM: usize> PioPwm<'d, T, SM> { self.set_level(to_pio_cycles(duration)); } - // Return the state machine and pin. + /// Return the state machine and pin. pub fn release(self) -> (StateMachine<'d, T, SM>, Pin<'d, T>) { (self.sm, self.pin) } From 3870411a4a4546f814140b178609a86d5209d734 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fabian=20Vi=C3=B6l?= Date: Thu, 10 Oct 2024 16:12:51 +0200 Subject: [PATCH 062/361] stm32/i2c: disable pullup instead of pulldown --- embassy-stm32/src/i2c/mod.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/embassy-stm32/src/i2c/mod.rs b/embassy-stm32/src/i2c/mod.rs index 739e960b9..1fc91f1ef 100644 --- a/embassy-stm32/src/i2c/mod.rs +++ b/embassy-stm32/src/i2c/mod.rs @@ -88,7 +88,7 @@ impl Config { Speed::Medium, match self.scl_pullup { true => Pull::Up, - false => Pull::Down, + false => Pull::None, }, ); } @@ -102,7 +102,7 @@ impl Config { Speed::Medium, match self.sda_pullup { true => Pull::Up, - false => Pull::Down, + false => Pull::None, }, ); } From 350a15a0cd72dc1d8ef65d2c398f279d571f5193 Mon Sep 17 00:00:00 2001 From: Joost Buijgers Date: Fri, 11 Oct 2024 12:18:04 +0200 Subject: [PATCH 063/361] make bluetooth module public --- cyw43/src/lib.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/cyw43/src/lib.rs b/cyw43/src/lib.rs index 6b71c18e6..3cd0e4988 100644 --- a/cyw43/src/lib.rs +++ b/cyw43/src/lib.rs @@ -9,7 +9,8 @@ pub(crate) mod fmt; #[cfg(feature = "bluetooth")] -mod bluetooth; +/// Bluetooth module. +pub mod bluetooth; mod bus; mod consts; mod control; From 0bf99820f3e5efaba821652fe33e99ed621ece7f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Beat=20K=C3=BCng?= Date: Sat, 12 Oct 2024 10:35:43 +0200 Subject: [PATCH 064/361] stm32: add RX Pull configuration option to USART --- embassy-stm32/src/usart/buffered.rs | 4 ++-- embassy-stm32/src/usart/mod.rs | 18 +++++++++++------- 2 files changed, 13 insertions(+), 9 deletions(-) diff --git a/embassy-stm32/src/usart/buffered.rs b/embassy-stm32/src/usart/buffered.rs index 86f56eb7c..4fbe33b2e 100644 --- a/embassy-stm32/src/usart/buffered.rs +++ b/embassy-stm32/src/usart/buffered.rs @@ -210,7 +210,7 @@ impl<'d> BufferedUart<'d> { ) -> Result { Self::new_inner( peri, - new_pin!(rx, AfType::input(Pull::None)), + new_pin!(rx, AfType::input(config.rx_pull)), new_pin!(tx, AfType::output(OutputType::PushPull, Speed::Medium)), None, None, @@ -260,7 +260,7 @@ impl<'d> BufferedUart<'d> { ) -> Result { Self::new_inner( peri, - new_pin!(rx, AfType::input(Pull::None)), + new_pin!(rx, AfType::input(config.rx_pull)), new_pin!(tx, AfType::output(OutputType::PushPull, Speed::Medium)), None, None, diff --git a/embassy-stm32/src/usart/mod.rs b/embassy-stm32/src/usart/mod.rs index e7f2f890a..5c96e202d 100644 --- a/embassy-stm32/src/usart/mod.rs +++ b/embassy-stm32/src/usart/mod.rs @@ -167,6 +167,9 @@ pub struct Config { #[cfg(any(usart_v3, usart_v4))] pub invert_rx: bool, + /// Set the pull configuration for the RX pin. + pub rx_pull: Pull, + // private: set by new_half_duplex, not by the user. half_duplex: bool, } @@ -175,7 +178,7 @@ impl Config { fn tx_af(&self) -> AfType { #[cfg(any(usart_v3, usart_v4))] if self.swap_rx_tx { - return AfType::input(Pull::None); + return AfType::input(self.rx_pull); }; AfType::output(OutputType::PushPull, Speed::Medium) } @@ -185,7 +188,7 @@ impl Config { if self.swap_rx_tx { return AfType::output(OutputType::PushPull, Speed::Medium); }; - AfType::input(Pull::None) + AfType::input(self.rx_pull) } } @@ -206,6 +209,7 @@ impl Default for Config { invert_tx: false, #[cfg(any(usart_v3, usart_v4))] invert_rx: false, + rx_pull: Pull::None, half_duplex: false, } } @@ -448,7 +452,7 @@ impl<'d> UartTx<'d, Blocking> { Self::new_inner( peri, new_pin!(tx, AfType::output(OutputType::PushPull, Speed::Medium)), - new_pin!(cts, AfType::input(Pull::None)), + new_pin!(cts, AfType::input(config.rx_pull)), None, config, ) @@ -567,7 +571,7 @@ impl<'d> UartRx<'d, Async> { ) -> Result { Self::new_inner( peri, - new_pin!(rx, AfType::input(Pull::None)), + new_pin!(rx, AfType::input(config.rx_pull)), None, new_dma!(rx_dma), config, @@ -585,7 +589,7 @@ impl<'d> UartRx<'d, Async> { ) -> Result { Self::new_inner( peri, - new_pin!(rx, AfType::input(Pull::None)), + new_pin!(rx, AfType::input(config.rx_pull)), new_pin!(rts, AfType::output(OutputType::PushPull, Speed::Medium)), new_dma!(rx_dma), config, @@ -815,7 +819,7 @@ impl<'d> UartRx<'d, Blocking> { rx: impl Peripheral

> + 'd, config: Config, ) -> Result { - Self::new_inner(peri, new_pin!(rx, AfType::input(Pull::None)), None, None, config) + Self::new_inner(peri, new_pin!(rx, AfType::input(config.rx_pull)), None, None, config) } /// Create a new rx-only UART with a request-to-send pin @@ -827,7 +831,7 @@ impl<'d> UartRx<'d, Blocking> { ) -> Result { Self::new_inner( peri, - new_pin!(rx, AfType::input(Pull::None)), + new_pin!(rx, AfType::input(config.rx_pull)), new_pin!(rts, AfType::output(OutputType::PushPull, Speed::Medium)), None, config, From cdcd9de05143827c0c138359e0b43887d64cf98f Mon Sep 17 00:00:00 2001 From: Keisuke Tottori Date: Fri, 27 Sep 2024 17:19:35 +0900 Subject: [PATCH 065/361] Enable FPU for RP235X Core1 --- embassy-rp/src/multicore.rs | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/embassy-rp/src/multicore.rs b/embassy-rp/src/multicore.rs index 9f7d77bf5..7e2e776ea 100644 --- a/embassy-rp/src/multicore.rs +++ b/embassy-rp/src/multicore.rs @@ -169,6 +169,13 @@ where interrupt::SIO_IRQ_FIFO.enable() }; + // Enable FPU + #[cfg(feature = "_rp235x")] + unsafe { + let p = cortex_m::Peripherals::steal(); + p.SCB.cpacr.modify(|cpacr| cpacr | (3 << 20) | (3 << 22)); + } + entry() } From 0222faa8a1f3afbc60eb455989fc581ec9adfac1 Mon Sep 17 00:00:00 2001 From: HaoboGu Date: Mon, 14 Oct 2024 04:32:22 +0800 Subject: [PATCH 066/361] Add octospim support for octospi (#3102) * feat: add octospim to ospi Signed-off-by: Haobo Gu * feat: make octospim behind feature gate Signed-off-by: Haobo Gu * refactor: fix fmt issue Signed-off-by: Haobo Gu * refactor: fix ci failure Signed-off-by: Haobo Gu * feat: add octospim reg writing code Signed-off-by: Haobo Gu * feat(octospi): enable rcc for octospim at the initialization Signed-off-by: Haobo Gu * fix: add octospim feature gate Signed-off-by: Haobo Gu * fix: fix cfg flag Signed-off-by: Haobo Gu * fix: fix rcc register on stm32l4 and stm32u5 Signed-off-by: Haobo Gu * feat(ospi): support OCTOSPI2 in build.rs Signed-off-by: Haobo Gu * feat(ospi): add OCTOSPI2 pin impls Signed-off-by: HaoboGu * feat(ospi): support both ospi instances in stm32 OCTOSPIM Signed-off-by: Haobo Gu --------- Signed-off-by: Haobo Gu Signed-off-by: HaoboGu --- embassy-stm32/build.rs | 33 ++++++++++ embassy-stm32/src/ospi/mod.rs | 118 ++++++++++++++++++++++++++++++++++ 2 files changed, 151 insertions(+) diff --git a/embassy-stm32/build.rs b/embassy-stm32/build.rs index 28c619c6b..966dce121 100644 --- a/embassy-stm32/build.rs +++ b/embassy-stm32/build.rs @@ -1054,6 +1054,30 @@ fn main() { (("octospi", "NCS"), quote!(crate::ospi::NSSPin)), (("octospi", "CLK"), quote!(crate::ospi::SckPin)), (("octospi", "NCLK"), quote!(crate::ospi::NckPin)), + (("octospim", "P1_IO0"), quote!(crate::ospi::D0Pin)), + (("octospim", "P1_IO1"), quote!(crate::ospi::D1Pin)), + (("octospim", "P1_IO2"), quote!(crate::ospi::D2Pin)), + (("octospim", "P1_IO3"), quote!(crate::ospi::D3Pin)), + (("octospim", "P1_IO4"), quote!(crate::ospi::D4Pin)), + (("octospim", "P1_IO5"), quote!(crate::ospi::D5Pin)), + (("octospim", "P1_IO6"), quote!(crate::ospi::D6Pin)), + (("octospim", "P1_IO7"), quote!(crate::ospi::D7Pin)), + (("octospim", "P1_DQS"), quote!(crate::ospi::DQSPin)), + (("octospim", "P1_NCS"), quote!(crate::ospi::NSSPin)), + (("octospim", "P1_CLK"), quote!(crate::ospi::SckPin)), + (("octospim", "P1_NCLK"), quote!(crate::ospi::NckPin)), + (("octospim", "P2_IO0"), quote!(crate::ospi::D0Pin)), + (("octospim", "P2_IO1"), quote!(crate::ospi::D1Pin)), + (("octospim", "P2_IO2"), quote!(crate::ospi::D2Pin)), + (("octospim", "P2_IO3"), quote!(crate::ospi::D3Pin)), + (("octospim", "P2_IO4"), quote!(crate::ospi::D4Pin)), + (("octospim", "P2_IO5"), quote!(crate::ospi::D5Pin)), + (("octospim", "P2_IO6"), quote!(crate::ospi::D6Pin)), + (("octospim", "P2_IO7"), quote!(crate::ospi::D7Pin)), + (("octospim", "P2_DQS"), quote!(crate::ospi::DQSPin)), + (("octospim", "P2_NCS"), quote!(crate::ospi::NSSPin)), + (("octospim", "P2_CLK"), quote!(crate::ospi::SckPin)), + (("octospim", "P2_NCLK"), quote!(crate::ospi::NckPin)), (("tsc", "G1_IO1"), quote!(crate::tsc::G1IO1Pin)), (("tsc", "G1_IO2"), quote!(crate::tsc::G1IO2Pin)), (("tsc", "G1_IO3"), quote!(crate::tsc::G1IO3Pin)), @@ -1111,6 +1135,15 @@ fn main() { peri = format_ident!("{}", pin.signal.replace('_', "")); } + // OCTOSPIM is special + if p.name == "OCTOSPIM" { + peri = format_ident!("{}", "OCTOSPI1"); + g.extend(quote! { + pin_trait_impl!(#tr, #peri, #pin_name, #af); + }); + peri = format_ident!("{}", "OCTOSPI2"); + } + g.extend(quote! { pin_trait_impl!(#tr, #peri, #pin_name, #af); }) diff --git a/embassy-stm32/src/ospi/mod.rs b/embassy-stm32/src/ospi/mod.rs index 289bfa672..48a1ea5e6 100644 --- a/embassy-stm32/src/ospi/mod.rs +++ b/embassy-stm32/src/ospi/mod.rs @@ -16,6 +16,8 @@ use crate::dma::{word, ChannelAndRequest}; use crate::gpio::{AfType, AnyPin, OutputType, Pull, SealedPin as _, Speed}; use crate::mode::{Async, Blocking, Mode as PeriMode}; use crate::pac::octospi::{vals, Octospi as Regs}; +#[cfg(octospim_v1)] +use crate::pac::octospim::Octospim; use crate::rcc::{self, RccPeripheral}; use crate::{peripherals, Peripheral}; @@ -197,6 +199,83 @@ impl<'d, T: Instance, M: PeriMode> Ospi<'d, T, M> { ) -> Self { into_ref!(peri); + #[cfg(octospim_v1)] + { + // RCC for octospim should be enabled before writing register + #[cfg(stm32l4)] + crate::pac::RCC.ahb2smenr().modify(|w| w.set_octospimsmen(true)); + #[cfg(stm32u5)] + crate::pac::RCC.ahb2enr1().modify(|w| w.set_octospimen(true)); + #[cfg(not(any(stm32l4, stm32u5)))] + crate::pac::RCC.ahb3enr().modify(|w| w.set_iomngren(true)); + + // Disable OctoSPI peripheral first + T::REGS.cr().modify(|w| { + w.set_en(false); + }); + + // OctoSPI IO Manager has been enabled before + T::OCTOSPIM_REGS.cr().modify(|w| { + w.set_muxen(false); + w.set_req2ack_time(0xff); + }); + + // Clear config + T::OCTOSPIM_REGS.p1cr().modify(|w| { + w.set_clksrc(false); + w.set_dqssrc(false); + w.set_ncssrc(false); + w.set_clken(false); + w.set_dqsen(false); + w.set_ncsen(false); + w.set_iolsrc(0); + w.set_iohsrc(0); + }); + + T::OCTOSPIM_REGS.p1cr().modify(|w| { + let octospi_src = if T::OCTOSPI_IDX == 1 { false } else { true }; + w.set_ncsen(true); + w.set_ncssrc(octospi_src); + w.set_clken(true); + w.set_clksrc(octospi_src); + if dqs.is_some() { + w.set_dqsen(true); + w.set_dqssrc(octospi_src); + } + + // Set OCTOSPIM IOL and IOH according to the index of OCTOSPI instance + if T::OCTOSPI_IDX == 1 { + w.set_iolen(true); + w.set_iolsrc(0); + // Enable IOH in octo and dual quad mode + if let OspiWidth::OCTO = width { + w.set_iohen(true); + w.set_iohsrc(0b01); + } else if dual_quad { + w.set_iohen(true); + w.set_iohsrc(0b00); + } else { + w.set_iohen(false); + w.set_iohsrc(0b00); + } + } else { + w.set_iolen(true); + w.set_iolsrc(0b10); + // Enable IOH in octo and dual quad mode + if let OspiWidth::OCTO = width { + w.set_iohen(true); + w.set_iohsrc(0b11); + } else if dual_quad { + w.set_iohen(true); + w.set_iohsrc(0b10); + } else { + w.set_iohen(false); + w.set_iohsrc(0b00); + } + } + }); + } + // System configuration rcc::enable_and_reset::(); while T::REGS.sr().read().busy() {} @@ -1056,11 +1135,25 @@ fn finish_dma(regs: Regs) { }); } +#[cfg(octospim_v1)] +/// OctoSPI I/O manager instance trait. +pub(crate) trait SealedOctospimInstance { + const OCTOSPIM_REGS: Octospim; + const OCTOSPI_IDX: u8; +} + +/// OctoSPI instance trait. pub(crate) trait SealedInstance { const REGS: Regs; } /// OSPI instance trait. +#[cfg(octospim_v1)] +#[allow(private_bounds)] +pub trait Instance: Peripheral

+ SealedInstance + RccPeripheral + SealedOctospimInstance {} + +/// OSPI instance trait. +#[cfg(not(octospim_v1))] #[allow(private_bounds)] pub trait Instance: Peripheral

+ SealedInstance + RccPeripheral {} @@ -1078,6 +1171,31 @@ pin_trait!(DQSPin, Instance); pin_trait!(NSSPin, Instance); dma_trait!(OctoDma, Instance); +// Hard-coded the octospi index, for OCTOSPIM +#[cfg(octospim_v1)] +impl SealedOctospimInstance for peripherals::OCTOSPI1 { + const OCTOSPIM_REGS: Octospim = crate::pac::OCTOSPIM; + const OCTOSPI_IDX: u8 = 1; +} + +#[cfg(octospim_v1)] +impl SealedOctospimInstance for peripherals::OCTOSPI2 { + const OCTOSPIM_REGS: Octospim = crate::pac::OCTOSPIM; + const OCTOSPI_IDX: u8 = 2; +} + +#[cfg(octospim_v1)] +foreach_peripheral!( + (octospi, $inst:ident) => { + impl SealedInstance for peripherals::$inst { + const REGS: Regs = crate::pac::$inst; + } + + impl Instance for peripherals::$inst {} + }; +); + +#[cfg(not(octospim_v1))] foreach_peripheral!( (octospi, $inst:ident) => { impl SealedInstance for peripherals::$inst { From a4636d819f4b98a781cc88a05c2e89397c71e1ed Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Sun, 13 Oct 2024 21:47:40 +0200 Subject: [PATCH 067/361] rp/multicore: enable fpu on second core only if building for -eabihf targets. --- embassy-rp/build.rs | 22 +++++++++++++++++++++- embassy-rp/src/multicore.rs | 2 +- 2 files changed, 22 insertions(+), 2 deletions(-) diff --git a/embassy-rp/build.rs b/embassy-rp/build.rs index 3216a3826..a8d387611 100644 --- a/embassy-rp/build.rs +++ b/embassy-rp/build.rs @@ -1,7 +1,8 @@ use std::env; +use std::ffi::OsStr; use std::fs::File; use std::io::Write; -use std::path::PathBuf; +use std::path::{Path, PathBuf}; fn main() { if env::var("CARGO_FEATURE_RP2040").is_ok() { @@ -16,4 +17,23 @@ fn main() { println!("cargo:rerun-if-changed=build.rs"); println!("cargo:rerun-if-changed=link-rp.x.in"); } + + // code below taken from https://github.com/rust-embedded/cortex-m/blob/master/cortex-m-rt/build.rs + + let mut target = env::var("TARGET").unwrap(); + + // When using a custom target JSON, `$TARGET` contains the path to that JSON file. By + // convention, these files are named after the actual target triple, eg. + // `thumbv7m-customos-elf.json`, so we extract the file stem here to allow custom target specs. + let path = Path::new(&target); + if path.extension() == Some(OsStr::new("json")) { + target = path + .file_stem() + .map_or(target.clone(), |stem| stem.to_str().unwrap().to_string()); + } + + println!("cargo::rustc-check-cfg=cfg(has_fpu)"); + if target.ends_with("-eabihf") { + println!("cargo:rustc-cfg=has_fpu"); + } } diff --git a/embassy-rp/src/multicore.rs b/embassy-rp/src/multicore.rs index 7e2e776ea..81de84907 100644 --- a/embassy-rp/src/multicore.rs +++ b/embassy-rp/src/multicore.rs @@ -170,7 +170,7 @@ where }; // Enable FPU - #[cfg(feature = "_rp235x")] + #[cfg(all(feature = "_rp235x", has_fpu))] unsafe { let p = cortex_m::Peripherals::steal(); p.SCB.cpacr.modify(|cpacr| cpacr | (3 << 20) | (3 << 22)); From ee669ee5c57851ade034beca7cfaf81825c4c21b Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Mon, 14 Oct 2024 00:01:49 +0200 Subject: [PATCH 068/361] Update nighlty, fix warnings. Fixes #2599 --- embassy-rp/src/lib.rs | 6 +++--- embassy-rp/src/multicore.rs | 4 ++-- embassy-stm32-wpan/src/lib.rs | 1 + embassy-stm32-wpan/src/mac/commands.rs | 2 +- embassy-stm32/src/low_power.rs | 3 +++ embassy-stm32/src/rcc/mod.rs | 10 +++++++--- examples/stm32h7/src/bin/sai.rs | 10 ++++++---- examples/stm32h7/src/bin/spi_bdma.rs | 7 ++++--- rust-toolchain-nightly.toml | 2 +- tests/nrf/src/bin/buffered_uart_spam.rs | 2 +- tests/stm32/src/bin/usart_rx_ringbuffered.rs | 3 +-- 11 files changed, 30 insertions(+), 20 deletions(-) diff --git a/embassy-rp/src/lib.rs b/embassy-rp/src/lib.rs index 7ac18c1f8..f56cfba27 100644 --- a/embassy-rp/src/lib.rs +++ b/embassy-rp/src/lib.rs @@ -480,7 +480,7 @@ pub fn install_core0_stack_guard() -> Result<(), ()> { #[cfg(all(feature = "rp2040", not(feature = "_test")))] #[inline(always)] -fn install_stack_guard(stack_bottom: *mut usize) -> Result<(), ()> { +unsafe fn install_stack_guard(stack_bottom: *mut usize) -> Result<(), ()> { let core = unsafe { cortex_m::Peripherals::steal() }; // Fail if MPU is already configured @@ -508,7 +508,7 @@ fn install_stack_guard(stack_bottom: *mut usize) -> Result<(), ()> { #[cfg(all(feature = "_rp235x", not(feature = "_test")))] #[inline(always)] -fn install_stack_guard(stack_bottom: *mut usize) -> Result<(), ()> { +unsafe fn install_stack_guard(stack_bottom: *mut usize) -> Result<(), ()> { let core = unsafe { cortex_m::Peripherals::steal() }; // Fail if MPU is already configured @@ -528,7 +528,7 @@ fn install_stack_guard(stack_bottom: *mut usize) -> Result<(), ()> { // so the compile fails when we try to use ARMv8 peripherals. #[cfg(feature = "_test")] #[inline(always)] -fn install_stack_guard(_stack_bottom: *mut usize) -> Result<(), ()> { +unsafe fn install_stack_guard(_stack_bottom: *mut usize) -> Result<(), ()> { Ok(()) } diff --git a/embassy-rp/src/multicore.rs b/embassy-rp/src/multicore.rs index 81de84907..ea0a29a36 100644 --- a/embassy-rp/src/multicore.rs +++ b/embassy-rp/src/multicore.rs @@ -58,7 +58,7 @@ const RESUME_TOKEN: u32 = !0xDEADBEEF; static IS_CORE1_INIT: AtomicBool = AtomicBool::new(false); #[inline(always)] -fn core1_setup(stack_bottom: *mut usize) { +unsafe fn core1_setup(stack_bottom: *mut usize) { 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 @@ -148,7 +148,7 @@ where entry: *mut ManuallyDrop, stack_bottom: *mut usize, ) -> ! { - core1_setup(stack_bottom); + unsafe { core1_setup(stack_bottom) }; let entry = unsafe { ManuallyDrop::take(&mut *entry) }; diff --git a/embassy-stm32-wpan/src/lib.rs b/embassy-stm32-wpan/src/lib.rs index f9560d235..fb34d4ba0 100644 --- a/embassy-stm32-wpan/src/lib.rs +++ b/embassy-stm32-wpan/src/lib.rs @@ -2,6 +2,7 @@ #![allow(async_fn_in_trait)] #![doc = include_str!("../README.md")] // #![warn(missing_docs)] +#![allow(static_mut_refs)] // TODO: Fix // This must go FIRST so that all the other modules see its macros. mod fmt; diff --git a/embassy-stm32-wpan/src/mac/commands.rs b/embassy-stm32-wpan/src/mac/commands.rs index c97c609c3..82b9d2772 100644 --- a/embassy-stm32-wpan/src/mac/commands.rs +++ b/embassy-stm32-wpan/src/mac/commands.rs @@ -371,7 +371,7 @@ pub struct DataRequest { } impl DataRequest { - pub fn set_buffer<'a>(&'a mut self, buf: &'a [u8]) -> &mut Self { + pub fn set_buffer<'a>(&'a mut self, buf: &'a [u8]) -> &'a mut Self { self.msdu_ptr = buf as *const _ as *const u8; self.msdu_length = buf.len() as u8; diff --git a/embassy-stm32/src/low_power.rs b/embassy-stm32/src/low_power.rs index f3e4c6994..2be1a42b7 100644 --- a/embassy-stm32/src/low_power.rs +++ b/embassy-stm32/src/low_power.rs @@ -51,6 +51,9 @@ //! } //! ``` +// TODO: Usage of `static mut` here is unsound. Fix then remove this `allow`.` +#![allow(static_mut_refs)] + use core::arch::asm; use core::marker::PhantomData; use core::sync::atomic::{compiler_fence, Ordering}; diff --git a/embassy-stm32/src/rcc/mod.rs b/embassy-stm32/src/rcc/mod.rs index 8022a35a4..4f43d3748 100644 --- a/embassy-stm32/src/rcc/mod.rs +++ b/embassy-stm32/src/rcc/mod.rs @@ -86,7 +86,7 @@ pub(crate) unsafe fn set_freqs(freqs: Clocks) { #[cfg(not(feature = "_dual-core"))] /// Safety: Reads a mutable global. pub(crate) unsafe fn get_freqs() -> &'static Clocks { - CLOCK_FREQS.assume_init_ref() + (*core::ptr::addr_of_mut!(CLOCK_FREQS)).assume_init_ref() } #[cfg(feature = "_dual-core")] @@ -171,7 +171,9 @@ impl RccInfo { // Use .get_mut instead of []-operator so that we control how bounds checks happen. // Otherwise, core::fmt will be pulled in here in order to format the integer in the // out-of-bounds error. - if let Some(refcount) = unsafe { crate::_generated::REFCOUNTS.get_mut(refcount_idx) } { + if let Some(refcount) = + unsafe { (*core::ptr::addr_of_mut!(crate::_generated::REFCOUNTS)).get_mut(refcount_idx) } + { *refcount += 1; if *refcount > 1 { return; @@ -235,7 +237,9 @@ impl RccInfo { // Use .get_mut instead of []-operator so that we control how bounds checks happen. // Otherwise, core::fmt will be pulled in here in order to format the integer in the // out-of-bounds error. - if let Some(refcount) = unsafe { crate::_generated::REFCOUNTS.get_mut(refcount_idx) } { + if let Some(refcount) = + unsafe { (*core::ptr::addr_of_mut!(crate::_generated::REFCOUNTS)).get_mut(refcount_idx) } + { *refcount -= 1; if *refcount > 0 { return; diff --git a/examples/stm32h7/src/bin/sai.rs b/examples/stm32h7/src/bin/sai.rs index f6735e235..04d14bd6b 100644 --- a/examples/stm32h7/src/bin/sai.rs +++ b/examples/stm32h7/src/bin/sai.rs @@ -81,8 +81,9 @@ async fn main(_spawner: Spawner) { rx_config.sync_output = false; let tx_buffer: &mut [u32] = unsafe { - TX_BUFFER.initialize_all_copied(0); - let (ptr, len) = TX_BUFFER.get_ptr_len(); + let buf = &mut *core::ptr::addr_of_mut!(TX_BUFFER); + buf.initialize_all_copied(0); + let (ptr, len) = buf.get_ptr_len(); core::slice::from_raw_parts_mut(ptr, len) }; @@ -98,8 +99,9 @@ async fn main(_spawner: Spawner) { ); let rx_buffer: &mut [u32] = unsafe { - RX_BUFFER.initialize_all_copied(0); - let (ptr, len) = RX_BUFFER.get_ptr_len(); + let buf = &mut *core::ptr::addr_of_mut!(RX_BUFFER); + buf.initialize_all_copied(0); + let (ptr, len) = buf.get_ptr_len(); core::slice::from_raw_parts_mut(ptr, len) }; diff --git a/examples/stm32h7/src/bin/spi_bdma.rs b/examples/stm32h7/src/bin/spi_bdma.rs index 43fb6b41c..9166fe9b6 100644 --- a/examples/stm32h7/src/bin/spi_bdma.rs +++ b/examples/stm32h7/src/bin/spi_bdma.rs @@ -22,10 +22,11 @@ static mut RAM_D3: GroundedArrayCell = GroundedArrayCell::uninit(); #[embassy_executor::task] async fn main_task(mut spi: spi::Spi<'static, Async>) { let (read_buffer, write_buffer) = unsafe { - RAM_D3.initialize_all_copied(0); + let ram = &mut *core::ptr::addr_of_mut!(RAM_D3); + ram.initialize_all_copied(0); ( - RAM_D3.get_subslice_mut_unchecked(0, 128), - RAM_D3.get_subslice_mut_unchecked(128, 128), + ram.get_subslice_mut_unchecked(0, 128), + ram.get_subslice_mut_unchecked(128, 128), ) }; diff --git a/rust-toolchain-nightly.toml b/rust-toolchain-nightly.toml index 0b10d7194..acab9d615 100644 --- a/rust-toolchain-nightly.toml +++ b/rust-toolchain-nightly.toml @@ -1,5 +1,5 @@ [toolchain] -channel = "nightly-2024-09-06" +channel = "nightly-2024-10-13" components = [ "rust-src", "rustfmt", "llvm-tools", "miri" ] targets = [ "thumbv7em-none-eabi", diff --git a/tests/nrf/src/bin/buffered_uart_spam.rs b/tests/nrf/src/bin/buffered_uart_spam.rs index e8fca452e..843313537 100644 --- a/tests/nrf/src/bin/buffered_uart_spam.rs +++ b/tests/nrf/src/bin/buffered_uart_spam.rs @@ -55,7 +55,7 @@ async fn main(_spawner: Spawner) { let task = unsafe { Task::new_unchecked(NonNull::new_unchecked(&spam_peri.tasks_starttx as *const _ as _)) }; let mut spam_ppi = Ppi::new_one_to_one(p.PPI_CH2, event, task); spam_ppi.enable(); - let p = unsafe { TX_BUF.as_mut_ptr() }; + let p = unsafe { core::ptr::addr_of_mut!(TX_BUF) } as *mut u8; spam_peri.txd.ptr.write(|w| unsafe { w.ptr().bits(p as u32) }); spam_peri.txd.maxcnt.write(|w| unsafe { w.maxcnt().bits(NSPAM as _) }); spam_peri.tasks_starttx.write(|w| unsafe { w.bits(1) }); diff --git a/tests/stm32/src/bin/usart_rx_ringbuffered.rs b/tests/stm32/src/bin/usart_rx_ringbuffered.rs index 98c7ef312..83c0887ac 100644 --- a/tests/stm32/src/bin/usart_rx_ringbuffered.rs +++ b/tests/stm32/src/bin/usart_rx_ringbuffered.rs @@ -43,8 +43,7 @@ async fn main(spawner: Spawner) { let usart = Uart::new(usart, rx, tx, irq, tx_dma, rx_dma, config).unwrap(); let (tx, rx) = usart.split(); static mut DMA_BUF: [u8; DMA_BUF_SIZE] = [0; DMA_BUF_SIZE]; - let dma_buf = unsafe { DMA_BUF.as_mut() }; - let rx = rx.into_ring_buffered(dma_buf); + let rx = rx.into_ring_buffered(unsafe { &mut *core::ptr::addr_of_mut!(DMA_BUF) }); info!("Spawning tasks"); spawner.spawn(transmit_task(tx)).unwrap(); From 9a45d776d870582cda3db0db233e3f5aea15d34e Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Mon, 14 Oct 2024 00:12:45 +0200 Subject: [PATCH 069/361] rustfmt for new nightly. --- embassy-net-driver-channel/src/lib.rs | 10 ++++++++-- embassy-net-driver/src/lib.rs | 6 ++++-- embassy-net-enc28j60/src/lib.rs | 6 ++++-- embassy-net-tuntap/src/lib.rs | 10 ++++++++-- embassy-net/src/driver_util.rs | 10 ++++++++-- embassy-net/src/tcp.rs | 5 ++++- embassy-stm32-wpan/src/mac/driver.rs | 10 ++++++++-- embassy-stm32/src/eth/mod.rs | 10 ++++++++-- 8 files changed, 52 insertions(+), 15 deletions(-) diff --git a/embassy-net-driver-channel/src/lib.rs b/embassy-net-driver-channel/src/lib.rs index 7ad4d449e..6390502a8 100644 --- a/embassy-net-driver-channel/src/lib.rs +++ b/embassy-net-driver-channel/src/lib.rs @@ -326,8 +326,14 @@ pub struct Device<'d, const MTU: usize> { } impl<'d, const MTU: usize> embassy_net_driver::Driver for Device<'d, MTU> { - type RxToken<'a> = RxToken<'a, MTU> where Self: 'a ; - type TxToken<'a> = TxToken<'a, MTU> where Self: 'a ; + type RxToken<'a> + = RxToken<'a, MTU> + where + Self: 'a; + type TxToken<'a> + = TxToken<'a, MTU> + where + Self: 'a; fn receive(&mut self, cx: &mut Context) -> Option<(Self::RxToken<'_>, Self::TxToken<'_>)> { if self.rx.poll_receive(cx).is_ready() && self.tx.poll_send(cx).is_ready() { diff --git a/embassy-net-driver/src/lib.rs b/embassy-net-driver/src/lib.rs index 87f9f6ed1..4c847718d 100644 --- a/embassy-net-driver/src/lib.rs +++ b/embassy-net-driver/src/lib.rs @@ -83,10 +83,12 @@ pub trait Driver { } impl Driver for &mut T { - type RxToken<'a> = T::RxToken<'a> + type RxToken<'a> + = T::RxToken<'a> where Self: 'a; - type TxToken<'a> = T::TxToken<'a> + type TxToken<'a> + = T::TxToken<'a> where Self: 'a; diff --git a/embassy-net-enc28j60/src/lib.rs b/embassy-net-enc28j60/src/lib.rs index dda35f498..c1f32719a 100644 --- a/embassy-net-enc28j60/src/lib.rs +++ b/embassy-net-enc28j60/src/lib.rs @@ -635,11 +635,13 @@ where S: SpiDevice, O: OutputPin, { - type RxToken<'a> = RxToken<'a> + type RxToken<'a> + = RxToken<'a> where Self: 'a; - type TxToken<'a> = TxToken<'a, S, O> + type TxToken<'a> + = TxToken<'a, S, O> where Self: 'a; diff --git a/embassy-net-tuntap/src/lib.rs b/embassy-net-tuntap/src/lib.rs index 56f55fba1..2ff23f462 100644 --- a/embassy-net-tuntap/src/lib.rs +++ b/embassy-net-tuntap/src/lib.rs @@ -152,8 +152,14 @@ impl TunTapDevice { } impl Driver for TunTapDevice { - type RxToken<'a> = RxToken where Self: 'a; - type TxToken<'a> = TxToken<'a> where Self: 'a; + type RxToken<'a> + = RxToken + where + Self: 'a; + type TxToken<'a> + = TxToken<'a> + where + Self: 'a; fn receive(&mut self, cx: &mut Context) -> Option<(Self::RxToken<'_>, Self::TxToken<'_>)> { let mut buf = vec![0; self.device.get_ref().mtu]; diff --git a/embassy-net/src/driver_util.rs b/embassy-net/src/driver_util.rs index b2af1d499..f51641425 100644 --- a/embassy-net/src/driver_util.rs +++ b/embassy-net/src/driver_util.rs @@ -18,8 +18,14 @@ impl<'d, 'c, T> phy::Device for DriverAdapter<'d, 'c, T> where T: Driver, { - type RxToken<'a> = RxTokenAdapter> where Self: 'a; - type TxToken<'a> = TxTokenAdapter> where Self: 'a; + type RxToken<'a> + = RxTokenAdapter> + where + Self: 'a; + type TxToken<'a> + = TxTokenAdapter> + where + Self: 'a; fn receive(&mut self, _timestamp: Instant) -> Option<(Self::RxToken<'_>, Self::TxToken<'_>)> { self.inner diff --git a/embassy-net/src/tcp.rs b/embassy-net/src/tcp.rs index 1bd582b65..043062e06 100644 --- a/embassy-net/src/tcp.rs +++ b/embassy-net/src/tcp.rs @@ -712,7 +712,10 @@ pub mod client { for TcpClient<'d, N, TX_SZ, RX_SZ> { type Error = Error; - type Connection<'m> = TcpConnection<'m, N, TX_SZ, RX_SZ> where Self: 'm; + type Connection<'m> + = TcpConnection<'m, N, TX_SZ, RX_SZ> + where + Self: 'm; async fn connect<'a>(&'a self, remote: core::net::SocketAddr) -> Result, Self::Error> { let addr: crate::IpAddress = match remote.ip() { diff --git a/embassy-stm32-wpan/src/mac/driver.rs b/embassy-stm32-wpan/src/mac/driver.rs index 5b9d5daf4..41cca09e3 100644 --- a/embassy-stm32-wpan/src/mac/driver.rs +++ b/embassy-stm32-wpan/src/mac/driver.rs @@ -23,8 +23,14 @@ impl<'d> Driver<'d> { impl<'d> embassy_net_driver::Driver for Driver<'d> { // type RxToken<'a> = RxToken<'a, 'd> where Self: 'a; // type TxToken<'a> = TxToken<'a, 'd> where Self: 'a; - type RxToken<'a> = RxToken<'d> where Self: 'a; - type TxToken<'a> = TxToken<'d> where Self: 'a; + type RxToken<'a> + = RxToken<'d> + where + Self: 'a; + type TxToken<'a> + = TxToken<'d> + where + Self: 'a; fn receive(&mut self, cx: &mut Context) -> Option<(Self::RxToken<'_>, Self::TxToken<'_>)> { if self.runner.rx_channel.poll_ready_to_receive(cx).is_ready() diff --git a/embassy-stm32/src/eth/mod.rs b/embassy-stm32/src/eth/mod.rs index 6442176da..1b875b71a 100644 --- a/embassy-stm32/src/eth/mod.rs +++ b/embassy-stm32/src/eth/mod.rs @@ -74,8 +74,14 @@ impl PacketQueue { static WAKER: AtomicWaker = AtomicWaker::new(); impl<'d, T: Instance, P: PHY> embassy_net_driver::Driver for Ethernet<'d, T, P> { - type RxToken<'a> = RxToken<'a, 'd> where Self: 'a; - type TxToken<'a> = TxToken<'a, 'd> where Self: 'a; + type RxToken<'a> + = RxToken<'a, 'd> + where + Self: 'a; + type TxToken<'a> + = TxToken<'a, 'd> + where + Self: 'a; fn receive(&mut self, cx: &mut Context) -> Option<(Self::RxToken<'_>, Self::TxToken<'_>)> { WAKER.register(cx.waker()); From 6862ac56cb1c1a29bad3e872b45386774c8edd58 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=A0=D0=BE=D0=BC=D0=B0=D0=BD=20=D0=9A=D1=80=D0=B8=D0=B2?= =?UTF-8?q?=D0=B5=D0=BD=D0=BA=D0=BE=D0=B2?= Date: Mon, 16 Sep 2024 11:02:27 +0400 Subject: [PATCH 070/361] Stm32: implement async flush for UART --- embassy-stm32/src/usart/mod.rs | 71 ++++++++++++++++++++++++++++------ 1 file changed, 59 insertions(+), 12 deletions(-) diff --git a/embassy-stm32/src/usart/mod.rs b/embassy-stm32/src/usart/mod.rs index 6f838cce5..333e01e36 100644 --- a/embassy-stm32/src/usart/mod.rs +++ b/embassy-stm32/src/usart/mod.rs @@ -69,6 +69,12 @@ unsafe fn on_interrupt(r: Regs, s: &'static State) { // disable idle line detection w.set_idleie(false); }); + } else if cr1.tcie() && sr.tc() { + // Transmission complete detected + r.cr1().modify(|w| { + // disable Transmission complete interrupt + w.set_tcie(false); + }); } else if cr1.rxneie() { // We cannot check the RXNE flag as it is auto-cleared by the DMA controller @@ -420,7 +426,7 @@ impl<'d> UartTx<'d, Async> { /// Wait until transmission complete pub async fn flush(&mut self) -> Result<(), Error> { - self.blocking_flush() + flush(&self.info, &self.state).await } } @@ -531,16 +537,40 @@ impl<'d, M: Mode> UartTx<'d, M> { } } +/// Wait until transmission complete +async fn flush(info: &Info, state: &State) -> Result<(), Error> { + let r = info.regs; + if r.cr1().read().te() && !sr(r).read().tc() { + r.cr1().modify(|w| { + // enable Transmission Complete interrupt + w.set_tcie(true); + }); + + compiler_fence(Ordering::SeqCst); + + // future which completes when Transmission complete is detected + let abort = poll_fn(move |cx| { + state.rx_waker.register(cx.waker()); + + let sr = sr(r).read(); + if sr.tc() { + // Transmission complete detected + return Poll::Ready(()); + } + + Poll::Pending + }); + + abort.await; + } + + Ok(()) +} + fn blocking_flush(info: &Info) -> Result<(), Error> { let r = info.regs; - while !sr(r).read().tc() {} - - // Disable Transmitter and enable receiver after transmission complete for Half-Duplex mode - if r.cr3().read().hdsel() { - r.cr1().modify(|reg| { - reg.set_te(false); - reg.set_re(true); - }); + if r.cr1().read().te() { + while !sr(r).read().tc() {} } Ok(()) @@ -621,7 +651,13 @@ impl<'d> UartRx<'d, Async> { // Call flush for Half-Duplex mode if some bytes were written and flush was not called. // It prevents reading of bytes which have just been written. if r.cr3().read().hdsel() && r.cr1().read().te() { - blocking_flush(self.info)?; + flush(&self.info, &self.state).await?; + + // Disable Transmitter and enable Receiver after flush + r.cr1().modify(|reg| { + reg.set_re(true); + reg.set_te(false); + }); } // make sure USART state is restored to neutral state when this future is dropped @@ -960,6 +996,12 @@ impl<'d, M: Mode> UartRx<'d, M> { // It prevents reading of bytes which have just been written. if r.cr3().read().hdsel() && r.cr1().read().te() { blocking_flush(self.info)?; + + // Disable Transmitter and enable Receiver after flush + r.cr1().modify(|reg| { + reg.set_re(true); + reg.set_te(false); + }); } for b in buffer { @@ -1155,6 +1197,11 @@ impl<'d> Uart<'d, Async> { self.tx.write(buffer).await } + /// Wait until transmission complete + pub async fn flush(&mut self) -> Result<(), Error> { + self.tx.flush().await + } + /// Perform an asynchronous read into `buffer` pub async fn read(&mut self, buffer: &mut [u8]) -> Result<(), Error> { self.rx.read(buffer).await @@ -1733,7 +1780,7 @@ impl embedded_io_async::Write for Uart<'_, Async> { } async fn flush(&mut self) -> Result<(), Self::Error> { - self.blocking_flush() + self.flush().await } } @@ -1744,7 +1791,7 @@ impl embedded_io_async::Write for UartTx<'_, Async> { } async fn flush(&mut self) -> Result<(), Self::Error> { - self.blocking_flush() + self.flush().await } } From ad5f7bf6f72f6e1ff512b29eaa843b0dae58d931 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Mon, 14 Oct 2024 12:43:38 +0200 Subject: [PATCH 071/361] tests: remove deprecated -Cinline-threshold. --- tests/rp/.cargo/config.toml | 1 - tests/stm32/.cargo/config.toml | 1 - 2 files changed, 2 deletions(-) diff --git a/tests/rp/.cargo/config.toml b/tests/rp/.cargo/config.toml index de7bb0e56..4337924cc 100644 --- a/tests/rp/.cargo/config.toml +++ b/tests/rp/.cargo/config.toml @@ -11,7 +11,6 @@ runner = "teleprobe client run" rustflags = [ # Code-size optimizations. #"-Z", "trap-unreachable=no", - "-C", "inline-threshold=5", "-C", "no-vectorize-loops", ] diff --git a/tests/stm32/.cargo/config.toml b/tests/stm32/.cargo/config.toml index 8752da59b..d94342598 100644 --- a/tests/stm32/.cargo/config.toml +++ b/tests/stm32/.cargo/config.toml @@ -9,7 +9,6 @@ runner = "teleprobe client run" rustflags = [ # Code-size optimizations. #"-Z", "trap-unreachable=no", - "-C", "inline-threshold=5", "-C", "no-vectorize-loops", ] From 014583aaa5dd10d4580aa24ec9b9f2ddb963559a Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Mon, 14 Oct 2024 12:43:59 +0200 Subject: [PATCH 072/361] tests/stm32: add uart async and blocking flush test. --- tests/stm32/src/bin/usart.rs | 7 +++++++ tests/stm32/src/bin/usart_dma.rs | 17 +++++++++++++++++ 2 files changed, 24 insertions(+) diff --git a/tests/stm32/src/bin/usart.rs b/tests/stm32/src/bin/usart.rs index 53da30fff..2f601ad0e 100644 --- a/tests/stm32/src/bin/usart.rs +++ b/tests/stm32/src/bin/usart.rs @@ -33,6 +33,13 @@ async fn main(_spawner: Spawner) { let mut buf = [0; 2]; usart.blocking_read(&mut buf).unwrap(); assert_eq!(buf, data); + + // Test flush doesn't hang. + usart.blocking_write(&data).unwrap(); + usart.blocking_flush().unwrap(); + + // Test flush doesn't hang if there's nothing to flush + usart.blocking_flush().unwrap(); } // Test error handling with with an overflow error diff --git a/tests/stm32/src/bin/usart_dma.rs b/tests/stm32/src/bin/usart_dma.rs index 266b81809..a34498376 100644 --- a/tests/stm32/src/bin/usart_dma.rs +++ b/tests/stm32/src/bin/usart_dma.rs @@ -51,6 +51,23 @@ async fn main(_spawner: Spawner) { assert_eq!(tx_buf, rx_buf); } + // Test flush doesn't hang. Check multiple combinations of async+blocking. + tx.write(&tx_buf).await.unwrap(); + tx.flush().await.unwrap(); + tx.flush().await.unwrap(); + + tx.write(&tx_buf).await.unwrap(); + tx.blocking_flush().unwrap(); + tx.flush().await.unwrap(); + + tx.blocking_write(&tx_buf).unwrap(); + tx.blocking_flush().unwrap(); + tx.flush().await.unwrap(); + + tx.blocking_write(&tx_buf).unwrap(); + tx.flush().await.unwrap(); + tx.blocking_flush().unwrap(); + info!("Test OK"); cortex_m::asm::bkpt(); } From 2b10caafd488987b6cf9a2cd69820c81d0a0826e Mon Sep 17 00:00:00 2001 From: Alexandros Liarokapis Date: Sun, 15 Sep 2024 18:47:33 +0300 Subject: [PATCH 073/361] stm32: initial support for alternative ringbuffer implementation --- embassy-stm32/Cargo.toml | 2 + embassy-stm32/src/dma/dma_bdma.rs | 4 - embassy-stm32/src/dma/ringbuffer.rs | 668 ------------------ embassy-stm32/src/dma/ringbuffer/mod.rs | 293 ++++++++ embassy-stm32/src/dma/ringbuffer/tests/mod.rs | 165 +++++ .../src/dma/ringbuffer/tests/prop_test/mod.rs | 50 ++ .../dma/ringbuffer/tests/prop_test/reader.rs | 122 ++++ .../dma/ringbuffer/tests/prop_test/writer.rs | 121 ++++ 8 files changed, 753 insertions(+), 672 deletions(-) delete mode 100644 embassy-stm32/src/dma/ringbuffer.rs create mode 100644 embassy-stm32/src/dma/ringbuffer/mod.rs create mode 100644 embassy-stm32/src/dma/ringbuffer/tests/mod.rs create mode 100644 embassy-stm32/src/dma/ringbuffer/tests/prop_test/mod.rs create mode 100644 embassy-stm32/src/dma/ringbuffer/tests/prop_test/reader.rs create mode 100644 embassy-stm32/src/dma/ringbuffer/tests/prop_test/writer.rs diff --git a/embassy-stm32/Cargo.toml b/embassy-stm32/Cargo.toml index 8fc8da006..53ec1b27f 100644 --- a/embassy-stm32/Cargo.toml +++ b/embassy-stm32/Cargo.toml @@ -93,6 +93,8 @@ aligned = "0.4.1" [dev-dependencies] critical-section = { version = "1.1", features = ["std"] } +proptest = "1.5.0" +proptest-state-machine = "0.3.0" [build-dependencies] proc-macro2 = "1.0.36" diff --git a/embassy-stm32/src/dma/dma_bdma.rs b/embassy-stm32/src/dma/dma_bdma.rs index d10b5554f..a43137c6e 100644 --- a/embassy-stm32/src/dma/dma_bdma.rs +++ b/embassy-stm32/src/dma/dma_bdma.rs @@ -763,10 +763,6 @@ impl<'a> DmaCtrl for DmaCtrlImpl<'a> { self.0.get_remaining_transfers() as _ } - fn get_complete_count(&self) -> usize { - STATE[self.0.id as usize].complete_count.load(Ordering::Acquire) - } - fn reset_complete_count(&mut self) -> usize { let state = &STATE[self.0.id as usize]; #[cfg(not(armv6m))] diff --git a/embassy-stm32/src/dma/ringbuffer.rs b/embassy-stm32/src/dma/ringbuffer.rs deleted file mode 100644 index 23f1d67d5..000000000 --- a/embassy-stm32/src/dma/ringbuffer.rs +++ /dev/null @@ -1,668 +0,0 @@ -#![cfg_attr(gpdma, allow(unused))] - -use core::future::poll_fn; -use core::ops::Range; -use core::sync::atomic::{compiler_fence, Ordering}; -use core::task::{Poll, Waker}; - -use super::word::Word; - -/// A "read-only" ring-buffer to be used together with the DMA controller which -/// writes in a circular way, "uncontrolled" to the buffer. -/// -/// A snapshot of the ring buffer state can be attained by setting the `ndtr` field -/// to the current register value. `ndtr` describes the current position of the DMA -/// write. -/// -/// # Buffer layout -/// -/// ```text -/// Without wraparound: With wraparound: -/// -/// + buf +--- NDTR ---+ + buf +---------- NDTR ----------+ -/// | | | | | | -/// v v v v v v -/// +-----------------------------------------+ +-----------------------------------------+ -/// |oooooooooooXXXXXXXXXXXXXXXXoooooooooooooo| |XXXXXXXXXXXXXooooooooooooXXXXXXXXXXXXXXXX| -/// +-----------------------------------------+ +-----------------------------------------+ -/// ^ ^ ^ ^ ^ ^ -/// | | | | | | -/// +- start --+ | +- end ------+ | -/// | | | | -/// +- end --------------------+ +- start ----------------+ -/// ``` -pub struct ReadableDmaRingBuffer<'a, W: Word> { - pub(crate) dma_buf: &'a mut [W], - start: usize, -} - -#[derive(Debug, PartialEq)] -#[cfg_attr(feature = "defmt", derive(defmt::Format))] -pub struct OverrunError; - -pub trait DmaCtrl { - /// Get the NDTR register value, i.e. the space left in the underlying - /// buffer until the dma writer wraps. - fn get_remaining_transfers(&self) -> usize; - - /// Get the transfer completed counter. - /// This counter is incremented by the dma controller when NDTR is reloaded, - /// i.e. when the writing wraps. - fn get_complete_count(&self) -> usize; - - /// Reset the transfer completed counter to 0 and return the value just prior to the reset. - fn reset_complete_count(&mut self) -> usize; - - /// Set the waker for a running poll_fn - fn set_waker(&mut self, waker: &Waker); -} - -impl<'a, W: Word> ReadableDmaRingBuffer<'a, W> { - pub fn new(dma_buf: &'a mut [W]) -> Self { - Self { dma_buf, start: 0 } - } - - /// Reset the ring buffer to its initial state - pub fn clear(&mut self, dma: &mut impl DmaCtrl) { - self.start = 0; - dma.reset_complete_count(); - } - - /// The capacity of the ringbuffer - pub const fn cap(&self) -> usize { - self.dma_buf.len() - } - - /// The current position of the ringbuffer - fn pos(&self, dma: &mut impl DmaCtrl) -> usize { - self.cap() - dma.get_remaining_transfers() - } - - /// Read an exact number of elements from the ringbuffer. - /// - /// Returns the remaining number of elements available for immediate reading. - /// OverrunError is returned if the portion to be read was overwritten by the DMA controller. - /// - /// Async/Wake Behavior: - /// The underlying DMA peripheral only can wake us when its buffer pointer has reached the halfway point, - /// and when it wraps around. This means that when called with a buffer of length 'M', when this - /// ring buffer was created with a buffer of size 'N': - /// - If M equals N/2 or N/2 divides evenly into M, this function will return every N/2 elements read on the DMA source. - /// - Otherwise, this function may need up to N/2 extra elements to arrive before returning. - pub async fn read_exact(&mut self, dma: &mut impl DmaCtrl, buffer: &mut [W]) -> Result { - let mut read_data = 0; - let buffer_len = buffer.len(); - - poll_fn(|cx| { - dma.set_waker(cx.waker()); - - compiler_fence(Ordering::SeqCst); - - match self.read(dma, &mut buffer[read_data..buffer_len]) { - Ok((len, remaining)) => { - read_data += len; - if read_data == buffer_len { - Poll::Ready(Ok(remaining)) - } else { - Poll::Pending - } - } - Err(e) => Poll::Ready(Err(e)), - } - }) - .await - } - - /// Read elements from the ring buffer - /// Return a tuple of the length read and the length remaining in the buffer - /// If not all of the elements were read, then there will be some elements in the buffer remaining - /// The length remaining is the capacity, ring_buf.len(), less the elements remaining after the read - /// OverrunError is returned if the portion to be read was overwritten by the DMA controller. - pub fn read(&mut self, dma: &mut impl DmaCtrl, buf: &mut [W]) -> Result<(usize, usize), OverrunError> { - /* - This algorithm is optimistic: we assume we haven't overrun more than a full buffer and then check - after we've done our work to see we have. This is because on stm32, an interrupt is not guaranteed - to fire in the same clock cycle that a register is read, so checking get_complete_count early does - not yield relevant information. - - Therefore, the only variable we really need to know is ndtr. If the dma has overrun by more than a full - buffer, we will do a bit more work than we have to, but algorithms should not be optimized for error - conditions. - - After we've done our work, we confirm that we haven't overrun more than a full buffer, and also that - the dma has not overrun within the data we could have copied. We check the data we could have copied - rather than the data we actually copied because it costs nothing and confirms an error condition - earlier. - */ - let end = self.pos(dma); - if self.start == end && dma.get_complete_count() == 0 { - // No elements are available in the buffer - Ok((0, self.cap())) - } else if self.start < end { - // The available, unread portion in the ring buffer DOES NOT wrap - // Copy out the elements from the dma buffer - let len = self.copy_to(buf, self.start..end); - - compiler_fence(Ordering::SeqCst); - - /* - first, check if the dma has wrapped at all if it's after end - or more than once if it's before start - - this is in a critical section to try to reduce mushy behavior. - it's not ideal but it's the best we can do - - then, get the current position of of the dma write and check - if it's inside data we could have copied - */ - let (pos, complete_count) = critical_section::with(|_| (self.pos(dma), dma.get_complete_count())); - if (pos >= self.start && pos < end) || (complete_count > 0 && pos >= end) || complete_count > 1 { - Err(OverrunError) - } else { - self.start = (self.start + len) % self.cap(); - - Ok((len, self.cap() - self.start)) - } - } else if self.start + buf.len() < self.cap() { - // The available, unread portion in the ring buffer DOES wrap - // The DMA writer has wrapped since we last read and is currently - // writing (or the next byte added will be) in the beginning of the ring buffer. - - // The provided read buffer is not large enough to include all elements from the tail of the dma buffer. - - // Copy out from the dma buffer - let len = self.copy_to(buf, self.start..self.cap()); - - compiler_fence(Ordering::SeqCst); - - /* - first, check if the dma has wrapped around more than once - - then, get the current position of of the dma write and check - if it's inside data we could have copied - */ - let pos = self.pos(dma); - if pos > self.start || pos < end || dma.get_complete_count() > 1 { - Err(OverrunError) - } else { - self.start = (self.start + len) % self.cap(); - - Ok((len, self.start + end)) - } - } else { - // The available, unread portion in the ring buffer DOES wrap - // The DMA writer has wrapped since we last read and is currently - // writing (or the next byte added will be) in the beginning of the ring buffer. - - // The provided read buffer is large enough to include all elements from the tail of the dma buffer, - // so the next read will not have any unread tail elements in the ring buffer. - - // Copy out from the dma buffer - let tail = self.copy_to(buf, self.start..self.cap()); - let head = self.copy_to(&mut buf[tail..], 0..end); - - compiler_fence(Ordering::SeqCst); - - /* - first, check if the dma has wrapped around more than once - - then, get the current position of of the dma write and check - if it's inside data we could have copied - */ - let pos = self.pos(dma); - if pos > self.start || pos < end || dma.reset_complete_count() > 1 { - Err(OverrunError) - } else { - self.start = head; - Ok((tail + head, self.cap() - self.start)) - } - } - } - /// Copy from the dma buffer at `data_range` into `buf` - fn copy_to(&mut self, buf: &mut [W], data_range: Range) -> usize { - // Limit the number of elements that can be copied - let length = usize::min(data_range.len(), buf.len()); - - // Copy from dma buffer into read buffer - // We need to do it like this instead of a simple copy_from_slice() because - // reading from a part of memory that may be simultaneously written to is unsafe - unsafe { - let dma_buf = self.dma_buf.as_ptr(); - - for i in 0..length { - buf[i] = core::ptr::read_volatile(dma_buf.offset((data_range.start + i) as isize)); - } - } - - length - } -} - -pub struct WritableDmaRingBuffer<'a, W: Word> { - pub(crate) dma_buf: &'a mut [W], - end: usize, -} - -impl<'a, W: Word> WritableDmaRingBuffer<'a, W> { - pub fn new(dma_buf: &'a mut [W]) -> Self { - Self { dma_buf, end: 0 } - } - - /// Reset the ring buffer to its initial state - pub fn clear(&mut self, dma: &mut impl DmaCtrl) { - self.end = 0; - dma.reset_complete_count(); - } - - /// The capacity of the ringbuffer - pub const fn cap(&self) -> usize { - self.dma_buf.len() - } - - /// The current position of the ringbuffer - fn pos(&self, dma: &mut impl DmaCtrl) -> usize { - self.cap() - dma.get_remaining_transfers() - } - - /// Write elements directly to the buffer. This must be done before the DMA is started - /// or after the buffer has been cleared using `clear()`. - pub fn write_immediate(&mut self, buffer: &[W]) -> Result<(usize, usize), OverrunError> { - if self.end != 0 { - return Err(OverrunError); - } - let written = self.copy_from(buffer, 0..self.cap()); - self.end = written % self.cap(); - Ok((written, self.cap() - written)) - } - - /// Write an exact number of elements to the ringbuffer. - pub async fn write_exact(&mut self, dma: &mut impl DmaCtrl, buffer: &[W]) -> Result { - let mut written_data = 0; - let buffer_len = buffer.len(); - - poll_fn(|cx| { - dma.set_waker(cx.waker()); - - compiler_fence(Ordering::SeqCst); - - match self.write(dma, &buffer[written_data..buffer_len]) { - Ok((len, remaining)) => { - written_data += len; - if written_data == buffer_len { - Poll::Ready(Ok(remaining)) - } else { - Poll::Pending - } - } - Err(e) => Poll::Ready(Err(e)), - } - }) - .await - } - - /// Write elements from the ring buffer - /// Return a tuple of the length written and the capacity remaining to be written in the buffer - pub fn write(&mut self, dma: &mut impl DmaCtrl, buf: &[W]) -> Result<(usize, usize), OverrunError> { - let start = self.pos(dma); - if start > self.end { - // The occupied portion in the ring buffer DOES wrap - let len = self.copy_from(buf, self.end..start); - - compiler_fence(Ordering::SeqCst); - - // Confirm that the DMA is not inside data we could have written - let (pos, complete_count) = critical_section::with(|_| (self.pos(dma), dma.get_complete_count())); - if (pos >= self.end && pos < start) || (complete_count > 0 && pos >= start) || complete_count > 1 { - Err(OverrunError) - } else { - self.end = (self.end + len) % self.cap(); - - Ok((len, self.cap() - (start - self.end))) - } - } else if start == self.end && dma.get_complete_count() == 0 { - Ok((0, 0)) - } else if start <= self.end && self.end + buf.len() < self.cap() { - // The occupied portion in the ring buffer DOES NOT wrap - // and copying elements into the buffer WILL NOT cause it to - - // Copy into the dma buffer - let len = self.copy_from(buf, self.end..self.cap()); - - compiler_fence(Ordering::SeqCst); - - // Confirm that the DMA is not inside data we could have written - let pos = self.pos(dma); - if pos > self.end || pos < start || dma.get_complete_count() > 1 { - Err(OverrunError) - } else { - self.end = (self.end + len) % self.cap(); - - Ok((len, self.cap() - (self.end - start))) - } - } else { - // The occupied portion in the ring buffer DOES NOT wrap - // and copying elements into the buffer WILL cause it to - - let tail = self.copy_from(buf, self.end..self.cap()); - let head = self.copy_from(&buf[tail..], 0..start); - - compiler_fence(Ordering::SeqCst); - - // Confirm that the DMA is not inside data we could have written - let pos = self.pos(dma); - if pos > self.end || pos < start || dma.reset_complete_count() > 1 { - Err(OverrunError) - } else { - self.end = head; - - Ok((tail + head, self.cap() - (start - self.end))) - } - } - } - /// Copy into the dma buffer at `data_range` from `buf` - fn copy_from(&mut self, buf: &[W], data_range: Range) -> usize { - // Limit the number of elements that can be copied - let length = usize::min(data_range.len(), buf.len()); - - // Copy into dma buffer from read buffer - // We need to do it like this instead of a simple copy_from_slice() because - // reading from a part of memory that may be simultaneously written to is unsafe - unsafe { - let dma_buf = self.dma_buf.as_mut_ptr(); - - for i in 0..length { - core::ptr::write_volatile(dma_buf.offset((data_range.start + i) as isize), buf[i]); - } - } - - length - } -} -#[cfg(test)] -mod tests { - use core::array; - use std::{cell, vec}; - - use super::*; - - #[allow(dead_code)] - #[derive(PartialEq, Debug)] - enum TestCircularTransferRequest { - GetCompleteCount(usize), - ResetCompleteCount(usize), - PositionRequest(usize), - } - - struct TestCircularTransfer { - len: usize, - requests: cell::RefCell>, - } - - impl DmaCtrl for TestCircularTransfer { - fn get_remaining_transfers(&self) -> usize { - match self.requests.borrow_mut().pop().unwrap() { - TestCircularTransferRequest::PositionRequest(pos) => { - let len = self.len; - - assert!(len >= pos); - - len - pos - } - _ => unreachable!(), - } - } - - fn get_complete_count(&self) -> usize { - match self.requests.borrow_mut().pop().unwrap() { - TestCircularTransferRequest::GetCompleteCount(complete_count) => complete_count, - _ => unreachable!(), - } - } - - fn reset_complete_count(&mut self) -> usize { - match self.requests.get_mut().pop().unwrap() { - TestCircularTransferRequest::ResetCompleteCount(complete_count) => complete_count, - _ => unreachable!(), - } - } - - fn set_waker(&mut self, waker: &Waker) {} - } - - impl TestCircularTransfer { - pub fn new(len: usize) -> Self { - Self { - requests: cell::RefCell::new(vec![]), - len, - } - } - - pub fn setup(&self, mut requests: vec::Vec) { - requests.reverse(); - self.requests.replace(requests); - } - } - - #[test] - fn empty_and_read_not_started() { - let mut dma_buf = [0u8; 16]; - let ringbuf = ReadableDmaRingBuffer::new(&mut dma_buf); - - assert_eq!(0, ringbuf.start); - } - - #[test] - fn can_read() { - let mut dma = TestCircularTransfer::new(16); - - let mut dma_buf: [u8; 16] = array::from_fn(|idx| idx as u8); // 0, 1, ..., 15 - let mut ringbuf = ReadableDmaRingBuffer::new(&mut dma_buf); - - assert_eq!(0, ringbuf.start); - assert_eq!(16, ringbuf.cap()); - - dma.setup(vec![ - TestCircularTransferRequest::PositionRequest(8), - TestCircularTransferRequest::PositionRequest(10), - TestCircularTransferRequest::GetCompleteCount(0), - ]); - let mut buf = [0; 2]; - assert_eq!(2, ringbuf.read(&mut dma, &mut buf).unwrap().0); - assert_eq!([0, 1], buf); - assert_eq!(2, ringbuf.start); - - dma.setup(vec![ - TestCircularTransferRequest::PositionRequest(10), - TestCircularTransferRequest::PositionRequest(12), - TestCircularTransferRequest::GetCompleteCount(0), - ]); - let mut buf = [0; 2]; - assert_eq!(2, ringbuf.read(&mut dma, &mut buf).unwrap().0); - assert_eq!([2, 3], buf); - assert_eq!(4, ringbuf.start); - - dma.setup(vec![ - TestCircularTransferRequest::PositionRequest(12), - TestCircularTransferRequest::PositionRequest(14), - TestCircularTransferRequest::GetCompleteCount(0), - ]); - let mut buf = [0; 8]; - assert_eq!(8, ringbuf.read(&mut dma, &mut buf).unwrap().0); - assert_eq!([4, 5, 6, 7, 8, 9], buf[..6]); - assert_eq!(12, ringbuf.start); - } - - #[test] - fn can_read_with_wrap() { - let mut dma = TestCircularTransfer::new(16); - - let mut dma_buf: [u8; 16] = array::from_fn(|idx| idx as u8); // 0, 1, ..., 15 - let mut ringbuf = ReadableDmaRingBuffer::new(&mut dma_buf); - - assert_eq!(0, ringbuf.start); - assert_eq!(16, ringbuf.cap()); - - /* - Read to close to the end of the buffer - */ - dma.setup(vec![ - TestCircularTransferRequest::PositionRequest(14), - TestCircularTransferRequest::PositionRequest(16), - TestCircularTransferRequest::GetCompleteCount(0), - ]); - let mut buf = [0; 14]; - assert_eq!(14, ringbuf.read(&mut dma, &mut buf).unwrap().0); - assert_eq!(14, ringbuf.start); - - /* - Now, read around the buffer - */ - dma.setup(vec![ - TestCircularTransferRequest::PositionRequest(6), - TestCircularTransferRequest::PositionRequest(8), - TestCircularTransferRequest::ResetCompleteCount(1), - ]); - let mut buf = [0; 6]; - assert_eq!(6, ringbuf.read(&mut dma, &mut buf).unwrap().0); - assert_eq!(4, ringbuf.start); - } - - #[test] - fn can_read_when_dma_writer_is_wrapped_and_read_does_not_wrap() { - let mut dma = TestCircularTransfer::new(16); - - let mut dma_buf: [u8; 16] = array::from_fn(|idx| idx as u8); // 0, 1, ..., 15 - let mut ringbuf = ReadableDmaRingBuffer::new(&mut dma_buf); - - assert_eq!(0, ringbuf.start); - assert_eq!(16, ringbuf.cap()); - - /* - Read to close to the end of the buffer - */ - dma.setup(vec![ - TestCircularTransferRequest::PositionRequest(14), - TestCircularTransferRequest::PositionRequest(16), - TestCircularTransferRequest::GetCompleteCount(0), - ]); - let mut buf = [0; 14]; - assert_eq!(14, ringbuf.read(&mut dma, &mut buf).unwrap().0); - assert_eq!(14, ringbuf.start); - - /* - Now, read to the end of the buffer - */ - dma.setup(vec![ - TestCircularTransferRequest::PositionRequest(6), - TestCircularTransferRequest::PositionRequest(8), - TestCircularTransferRequest::ResetCompleteCount(1), - ]); - let mut buf = [0; 2]; - assert_eq!(2, ringbuf.read(&mut dma, &mut buf).unwrap().0); - assert_eq!(0, ringbuf.start); - } - - #[test] - fn can_read_when_dma_writer_wraps_once_with_same_ndtr() { - let mut dma = TestCircularTransfer::new(16); - - let mut dma_buf: [u8; 16] = array::from_fn(|idx| idx as u8); // 0, 1, ..., 15 - let mut ringbuf = ReadableDmaRingBuffer::new(&mut dma_buf); - - assert_eq!(0, ringbuf.start); - assert_eq!(16, ringbuf.cap()); - - /* - Read to about the middle of the buffer - */ - dma.setup(vec![ - TestCircularTransferRequest::PositionRequest(6), - TestCircularTransferRequest::PositionRequest(6), - TestCircularTransferRequest::GetCompleteCount(0), - ]); - let mut buf = [0; 6]; - assert_eq!(6, ringbuf.read(&mut dma, &mut buf).unwrap().0); - assert_eq!(6, ringbuf.start); - - /* - Now, wrap the DMA controller around - */ - dma.setup(vec![ - TestCircularTransferRequest::PositionRequest(6), - TestCircularTransferRequest::GetCompleteCount(1), - TestCircularTransferRequest::PositionRequest(6), - TestCircularTransferRequest::GetCompleteCount(1), - ]); - let mut buf = [0; 6]; - assert_eq!(6, ringbuf.read(&mut dma, &mut buf).unwrap().0); - assert_eq!(12, ringbuf.start); - } - - #[test] - fn cannot_read_when_dma_writer_overwrites_during_not_wrapping_read() { - let mut dma = TestCircularTransfer::new(16); - - let mut dma_buf: [u8; 16] = array::from_fn(|idx| idx as u8); // 0, 1, ..., 15 - let mut ringbuf = ReadableDmaRingBuffer::new(&mut dma_buf); - - assert_eq!(0, ringbuf.start); - assert_eq!(16, ringbuf.cap()); - - /* - Read a few bytes - */ - dma.setup(vec![ - TestCircularTransferRequest::PositionRequest(2), - TestCircularTransferRequest::PositionRequest(2), - TestCircularTransferRequest::GetCompleteCount(0), - ]); - let mut buf = [0; 6]; - assert_eq!(2, ringbuf.read(&mut dma, &mut buf).unwrap().0); - assert_eq!(2, ringbuf.start); - - /* - Now, overtake the reader - */ - dma.setup(vec![ - TestCircularTransferRequest::PositionRequest(4), - TestCircularTransferRequest::PositionRequest(6), - TestCircularTransferRequest::GetCompleteCount(1), - ]); - let mut buf = [0; 6]; - assert_eq!(OverrunError, ringbuf.read(&mut dma, &mut buf).unwrap_err()); - } - - #[test] - fn cannot_read_when_dma_writer_overwrites_during_wrapping_read() { - let mut dma = TestCircularTransfer::new(16); - - let mut dma_buf: [u8; 16] = array::from_fn(|idx| idx as u8); // 0, 1, ..., 15 - let mut ringbuf = ReadableDmaRingBuffer::new(&mut dma_buf); - - assert_eq!(0, ringbuf.start); - assert_eq!(16, ringbuf.cap()); - - /* - Read to close to the end of the buffer - */ - dma.setup(vec![ - TestCircularTransferRequest::PositionRequest(14), - TestCircularTransferRequest::PositionRequest(16), - TestCircularTransferRequest::GetCompleteCount(0), - ]); - let mut buf = [0; 14]; - assert_eq!(14, ringbuf.read(&mut dma, &mut buf).unwrap().0); - assert_eq!(14, ringbuf.start); - - /* - Now, overtake the reader - */ - dma.setup(vec![ - TestCircularTransferRequest::PositionRequest(8), - TestCircularTransferRequest::PositionRequest(10), - TestCircularTransferRequest::ResetCompleteCount(2), - ]); - let mut buf = [0; 6]; - assert_eq!(OverrunError, ringbuf.read(&mut dma, &mut buf).unwrap_err()); - } -} diff --git a/embassy-stm32/src/dma/ringbuffer/mod.rs b/embassy-stm32/src/dma/ringbuffer/mod.rs new file mode 100644 index 000000000..10a9ff975 --- /dev/null +++ b/embassy-stm32/src/dma/ringbuffer/mod.rs @@ -0,0 +1,293 @@ +#![cfg_attr(gpdma, allow(unused))] + +use core::future::poll_fn; +use core::task::{Poll, Waker}; + +use crate::dma::word::Word; + +pub trait DmaCtrl { + /// Get the NDTR register value, i.e. the space left in the underlying + /// buffer until the dma writer wraps. + fn get_remaining_transfers(&self) -> usize; + + /// Reset the transfer completed counter to 0 and return the value just prior to the reset. + fn reset_complete_count(&mut self) -> usize; + + /// Set the waker for a running poll_fn + fn set_waker(&mut self, waker: &Waker); +} + +#[derive(Debug, PartialEq)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +pub struct OverrunError; + +#[derive(Debug, Clone, Copy, Default)] +struct DmaIndex { + completion_count: usize, + pos: usize, +} + +fn pos(cap: usize, dma: &impl DmaCtrl) -> usize { + cap - dma.get_remaining_transfers() +} + +impl DmaIndex { + fn reset(&mut self) { + self.pos = 0; + self.completion_count = 0; + } + + fn as_index(&self, cap: usize, offset: usize) -> usize { + (self.pos + offset) % cap + } + + fn dma_sync(&mut self, cap: usize, dma: &mut impl DmaCtrl) { + let fst_pos = pos(cap, dma); + let fst_count = dma.reset_complete_count(); + let pos = pos(cap, dma); + + let wrap_count = if pos >= fst_pos { + fst_count + } else { + fst_count + dma.reset_complete_count() + }; + + self.pos = pos; + self.completion_count += wrap_count; + } + + fn advance(&mut self, cap: usize, steps: usize) { + let next = self.pos + steps; + self.completion_count += next / cap; + self.pos = next % cap; + } + + fn normalize(lhs: &mut DmaIndex, rhs: &mut DmaIndex) { + let min_count = lhs.completion_count.min(rhs.completion_count); + lhs.completion_count -= min_count; + rhs.completion_count -= min_count; + } + + fn diff(&mut self, cap: usize, rhs: &mut DmaIndex) -> isize { + Self::normalize(self, rhs); + (self.completion_count * cap + self.pos) as isize - (rhs.completion_count * cap + rhs.pos) as isize + } +} + +pub struct ReadableDmaRingBuffer<'a, W: Word> { + dma_buf: &'a mut [W], + write_index: DmaIndex, + read_index: DmaIndex, +} + +impl<'a, W: Word> ReadableDmaRingBuffer<'a, W> { + /// Construct an empty buffer. + pub fn new(dma_buf: &'a mut [W]) -> Self { + Self { + dma_buf, + write_index: Default::default(), + read_index: Default::default(), + } + } + + /// Reset the ring buffer to its initial state + pub fn clear(&mut self, dma: &mut impl DmaCtrl) { + dma.reset_complete_count(); + self.write_index.reset(); + self.update_dma_index(dma); + self.read_index = self.write_index; + } + + /// The capacity of the ringbuffer + pub const fn cap(&self) -> usize { + self.dma_buf.len() + } + + /// Read elements from the ring buffer + /// Return a tuple of the length read and the length remaining in the buffer + /// If not all of the elements were read, then there will be some elements in the buffer remaining + /// The length remaining is the capacity, ring_buf.len(), less the elements remaining after the read + /// OverrunError is returned if the portion to be read was overwritten by the DMA controller. + pub fn read(&mut self, dma: &mut impl DmaCtrl, buf: &mut [W]) -> Result<(usize, usize), OverrunError> { + let readable = self.margin(dma)?.min(buf.len()); + for i in 0..readable { + buf[i] = self.read_buf(i); + } + let available = self.margin(dma)?; + self.read_index.advance(self.cap(), readable); + Ok((readable, available - readable)) + } + + /// Read an exact number of elements from the ringbuffer. + /// + /// Returns the remaining number of elements available for immediate reading. + /// OverrunError is returned if the portion to be read was overwritten by the DMA controller. + /// + /// Async/Wake Behavior: + /// The underlying DMA peripheral only can wake us when its buffer pointer has reached the halfway point, + /// and when it wraps around. This means that when called with a buffer of length 'M', when this + /// ring buffer was created with a buffer of size 'N': + /// - If M equals N/2 or N/2 divides evenly into M, this function will return every N/2 elements read on the DMA source. + /// - Otherwise, this function may need up to N/2 extra elements to arrive before returning. + pub async fn read_exact(&mut self, dma: &mut impl DmaCtrl, buffer: &mut [W]) -> Result { + let mut read_data = 0; + let buffer_len = buffer.len(); + + poll_fn(|cx| { + dma.set_waker(cx.waker()); + + match self.read(dma, &mut buffer[read_data..buffer_len]) { + Ok((len, remaining)) => { + read_data += len; + if read_data == buffer_len { + Poll::Ready(Ok(remaining)) + } else { + Poll::Pending + } + } + Err(e) => Poll::Ready(Err(e)), + } + }) + .await + } + + fn update_dma_index(&mut self, dma: &mut impl DmaCtrl) { + self.write_index.dma_sync(self.cap(), dma) + } + + fn read_buf(&self, offset: usize) -> W { + unsafe { + core::ptr::read_volatile( + self.dma_buf + .as_ptr() + .offset(self.read_index.as_index(self.cap(), offset) as isize), + ) + } + } + + /// Returns available dma samples + fn margin(&mut self, dma: &mut impl DmaCtrl) -> Result { + self.update_dma_index(dma); + + let diff: usize = self + .write_index + .diff(self.cap(), &mut self.read_index) + .try_into() + .unwrap(); + + if diff > self.cap() { + Err(OverrunError) + } else { + Ok(diff) + } + } +} + +pub struct WritableDmaRingBuffer<'a, W: Word> { + dma_buf: &'a mut [W], + read_index: DmaIndex, + write_index: DmaIndex, +} + +impl<'a, W: Word> WritableDmaRingBuffer<'a, W> { + /// Construct a ringbuffer filled with the given buffer data. + pub fn new(dma_buf: &'a mut [W]) -> Self { + let len = dma_buf.len(); + Self { + dma_buf, + read_index: Default::default(), + write_index: DmaIndex { + completion_count: 0, + pos: len, + }, + } + } + + /// Reset the ring buffer to its initial state. The buffer after the reset will be full. + pub fn clear(&mut self, dma: &mut impl DmaCtrl) { + dma.reset_complete_count(); + self.read_index.reset(); + self.update_dma_index(dma); + self.write_index = self.read_index; + self.write_index.advance(self.cap(), self.cap()); + } + + /// Get the capacity of the ringbuffer. + pub const fn cap(&self) -> usize { + self.dma_buf.len() + } + + /// Append data to the ring buffer. + /// Returns a tuple of the data written and the remaining write capacity in the buffer. + pub fn write(&mut self, dma: &mut impl DmaCtrl, buf: &[W]) -> Result<(usize, usize), OverrunError> { + let writable = self.margin(dma)?.min(buf.len()); + for i in 0..writable { + self.write_buf(i, buf[i]); + } + let available = self.margin(dma)?; + self.write_index.advance(self.cap(), writable); + Ok((writable, available - writable)) + } + + /// Write elements directly to the buffer. + pub fn write_immediate(&mut self, buf: &[W]) -> Result<(usize, usize), OverrunError> { + for (i, data) in buf.iter().enumerate() { + self.write_buf(i, *data) + } + let written = buf.len().min(self.cap()); + Ok((written, self.cap() - written)) + } + + /// Write an exact number of elements to the ringbuffer. + pub async fn write_exact(&mut self, dma: &mut impl DmaCtrl, buffer: &[W]) -> Result { + let mut written_data = 0; + let buffer_len = buffer.len(); + + poll_fn(|cx| { + dma.set_waker(cx.waker()); + + match self.write(dma, &buffer[written_data..buffer_len]) { + Ok((len, remaining)) => { + written_data += len; + if written_data == buffer_len { + Poll::Ready(Ok(remaining)) + } else { + Poll::Pending + } + } + Err(e) => Poll::Ready(Err(e)), + } + }) + .await + } + + fn update_dma_index(&mut self, dma: &mut impl DmaCtrl) { + self.read_index.dma_sync(self.cap(), dma); + } + + fn write_buf(&mut self, offset: usize, value: W) { + unsafe { + core::ptr::write_volatile( + self.dma_buf + .as_mut_ptr() + .offset(self.write_index.as_index(self.cap(), offset) as isize), + value, + ) + } + } + + fn margin(&mut self, dma: &mut impl DmaCtrl) -> Result { + self.update_dma_index(dma); + + let diff = self.write_index.diff(self.cap(), &mut self.read_index); + + if diff < 0 { + Err(OverrunError) + } else { + Ok(self.cap().saturating_sub(diff as usize)) + } + } +} + +#[cfg(test)] +mod tests; diff --git a/embassy-stm32/src/dma/ringbuffer/tests/mod.rs b/embassy-stm32/src/dma/ringbuffer/tests/mod.rs new file mode 100644 index 000000000..9768e1df8 --- /dev/null +++ b/embassy-stm32/src/dma/ringbuffer/tests/mod.rs @@ -0,0 +1,165 @@ +use std::{cell, vec}; + +use super::*; + +#[allow(dead_code)] +#[derive(PartialEq, Debug)] +enum TestCircularTransferRequest { + ResetCompleteCount(usize), + PositionRequest(usize), +} + +struct TestCircularTransfer { + len: usize, + requests: cell::RefCell>, +} + +impl DmaCtrl for TestCircularTransfer { + fn get_remaining_transfers(&self) -> usize { + match self.requests.borrow_mut().pop().unwrap() { + TestCircularTransferRequest::PositionRequest(pos) => { + let len = self.len; + + assert!(len >= pos); + + len - pos + } + _ => unreachable!(), + } + } + + fn reset_complete_count(&mut self) -> usize { + match self.requests.get_mut().pop().unwrap() { + TestCircularTransferRequest::ResetCompleteCount(complete_count) => complete_count, + _ => unreachable!(), + } + } + + fn set_waker(&mut self, _waker: &Waker) {} +} + +impl TestCircularTransfer { + pub fn new(len: usize) -> Self { + Self { + requests: cell::RefCell::new(vec![]), + len, + } + } + + pub fn setup(&self, mut requests: vec::Vec) { + requests.reverse(); + self.requests.replace(requests); + } +} + +const CAP: usize = 16; + +#[test] +fn dma_index_dma_sync_syncs_position_to_last_read_if_sync_takes_place_on_same_dma_cycle() { + let mut dma = TestCircularTransfer::new(CAP); + dma.setup(vec![ + TestCircularTransferRequest::PositionRequest(4), + TestCircularTransferRequest::ResetCompleteCount(0), + TestCircularTransferRequest::PositionRequest(7), + ]); + let mut index = DmaIndex::default(); + index.dma_sync(CAP, &mut dma); + assert_eq!(index.completion_count, 0); + assert_eq!(index.pos, 7); +} + +#[test] +fn dma_index_dma_sync_updates_completion_count_properly_if_sync_takes_place_on_same_dma_cycle() { + let mut dma = TestCircularTransfer::new(CAP); + dma.setup(vec![ + TestCircularTransferRequest::PositionRequest(4), + TestCircularTransferRequest::ResetCompleteCount(2), + TestCircularTransferRequest::PositionRequest(7), + ]); + let mut index = DmaIndex::default(); + index.completion_count = 1; + index.dma_sync(CAP, &mut dma); + assert_eq!(index.completion_count, 3); + assert_eq!(index.pos, 7); +} + +#[test] +fn dma_index_dma_sync_syncs_to_last_position_if_reads_occur_on_different_dma_cycles() { + let mut dma = TestCircularTransfer::new(CAP); + dma.setup(vec![ + TestCircularTransferRequest::PositionRequest(10), + TestCircularTransferRequest::ResetCompleteCount(1), + TestCircularTransferRequest::PositionRequest(5), + TestCircularTransferRequest::ResetCompleteCount(0), + ]); + let mut index = DmaIndex::default(); + index.dma_sync(CAP, &mut dma); + assert_eq!(index.completion_count, 1); + assert_eq!(index.pos, 5); +} + +#[test] +fn dma_index_dma_sync_detects_new_cycle_if_later_position_is_less_than_first_and_first_completion_count_occurs_on_first_cycle( +) { + let mut dma = TestCircularTransfer::new(CAP); + dma.setup(vec![ + TestCircularTransferRequest::PositionRequest(10), + TestCircularTransferRequest::ResetCompleteCount(1), + TestCircularTransferRequest::PositionRequest(5), + TestCircularTransferRequest::ResetCompleteCount(1), + ]); + let mut index = DmaIndex::default(); + index.completion_count = 1; + index.dma_sync(CAP, &mut dma); + assert_eq!(index.completion_count, 3); + assert_eq!(index.pos, 5); +} + +#[test] +fn dma_index_dma_sync_detects_new_cycle_if_later_position_is_less_than_first_and_first_completion_count_occurs_on_later_cycle( +) { + let mut dma = TestCircularTransfer::new(CAP); + dma.setup(vec![ + TestCircularTransferRequest::PositionRequest(10), + TestCircularTransferRequest::ResetCompleteCount(2), + TestCircularTransferRequest::PositionRequest(5), + TestCircularTransferRequest::ResetCompleteCount(0), + ]); + let mut index = DmaIndex::default(); + index.completion_count = 1; + index.dma_sync(CAP, &mut dma); + assert_eq!(index.completion_count, 3); + assert_eq!(index.pos, 5); +} + +#[test] +fn dma_index_as_index_returns_index_mod_cap_by_default() { + let index = DmaIndex::default(); + assert_eq!(index.as_index(CAP, 0), 0); + assert_eq!(index.as_index(CAP, 1), 1); + assert_eq!(index.as_index(CAP, 2), 2); + assert_eq!(index.as_index(CAP, 3), 3); + assert_eq!(index.as_index(CAP, 4), 4); + assert_eq!(index.as_index(CAP, CAP), 0); + assert_eq!(index.as_index(CAP, CAP + 1), 1); +} + +#[test] +fn dma_index_advancing_increases_as_index() { + let mut index = DmaIndex::default(); + assert_eq!(index.as_index(CAP, 0), 0); + index.advance(CAP, 1); + assert_eq!(index.as_index(CAP, 0), 1); + index.advance(CAP, 1); + assert_eq!(index.as_index(CAP, 0), 2); + index.advance(CAP, 1); + assert_eq!(index.as_index(CAP, 0), 3); + index.advance(CAP, 1); + assert_eq!(index.as_index(CAP, 0), 4); + index.advance(CAP, CAP - 4); + assert_eq!(index.as_index(CAP, 0), 0); + index.advance(CAP, 1); + assert_eq!(index.as_index(CAP, 0), 1); +} + +mod prop_test; diff --git a/embassy-stm32/src/dma/ringbuffer/tests/prop_test/mod.rs b/embassy-stm32/src/dma/ringbuffer/tests/prop_test/mod.rs new file mode 100644 index 000000000..661fb1728 --- /dev/null +++ b/embassy-stm32/src/dma/ringbuffer/tests/prop_test/mod.rs @@ -0,0 +1,50 @@ +use std::task::Waker; + +use proptest::prop_oneof; +use proptest::strategy::{self, BoxedStrategy, Strategy as _}; +use proptest_state_machine::{prop_state_machine, ReferenceStateMachine, StateMachineTest}; + +use super::*; + +const CAP: usize = 128; + +#[derive(Debug, Default)] +struct DmaMock { + pos: usize, + wraps: usize, +} + +impl DmaMock { + pub fn advance(&mut self, steps: usize) { + let next = self.pos + steps; + self.pos = next % CAP; + self.wraps += next / CAP; + } +} + +impl DmaCtrl for DmaMock { + fn get_remaining_transfers(&self) -> usize { + CAP - self.pos + } + + fn reset_complete_count(&mut self) -> usize { + core::mem::replace(&mut self.wraps, 0) + } + + fn set_waker(&mut self, _waker: &Waker) {} +} + +#[derive(Debug, Clone)] +enum Status { + Available(usize), + Failed, +} + +impl Status { + pub fn new(capacity: usize) -> Self { + Self::Available(capacity) + } +} + +mod reader; +mod writer; diff --git a/embassy-stm32/src/dma/ringbuffer/tests/prop_test/reader.rs b/embassy-stm32/src/dma/ringbuffer/tests/prop_test/reader.rs new file mode 100644 index 000000000..6555ebfb0 --- /dev/null +++ b/embassy-stm32/src/dma/ringbuffer/tests/prop_test/reader.rs @@ -0,0 +1,122 @@ +use core::fmt::Debug; + +use super::*; + +#[derive(Debug, Clone)] +enum ReaderTransition { + Write(usize), + Clear, + ReadUpTo(usize), +} + +struct ReaderSM; + +impl ReferenceStateMachine for ReaderSM { + type State = Status; + type Transition = ReaderTransition; + + fn init_state() -> BoxedStrategy { + strategy::Just(Status::new(0)).boxed() + } + + fn transitions(_state: &Self::State) -> BoxedStrategy { + prop_oneof![ + (1..50_usize).prop_map(ReaderTransition::Write), + (1..50_usize).prop_map(ReaderTransition::ReadUpTo), + strategy::Just(ReaderTransition::Clear), + ] + .boxed() + } + + fn apply(status: Self::State, transition: &Self::Transition) -> Self::State { + match (status, transition) { + (_, ReaderTransition::Clear) => Status::Available(0), + (Status::Available(x), ReaderTransition::Write(y)) => { + if x + y > CAP { + Status::Failed + } else { + Status::Available(x + y) + } + } + (Status::Available(x), ReaderTransition::ReadUpTo(y)) => Status::Available(x.saturating_sub(*y)), + (Status::Failed, _) => Status::Failed, + } + } +} + +struct ReaderSut { + status: Status, + buffer: *mut [u8], + producer: DmaMock, + consumer: ReadableDmaRingBuffer<'static, u8>, +} + +impl Debug for ReaderSut { + fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result { + ::fmt(&self.producer, f) + } +} + +struct ReaderTest; + +impl StateMachineTest for ReaderTest { + type SystemUnderTest = ReaderSut; + type Reference = ReaderSM; + + fn init_test(ref_status: &::State) -> Self::SystemUnderTest { + let buffer = Box::into_raw(Box::new([0; CAP])); + ReaderSut { + status: ref_status.clone(), + buffer, + producer: DmaMock::default(), + consumer: ReadableDmaRingBuffer::new(unsafe { &mut *buffer }), + } + } + + fn teardown(state: Self::SystemUnderTest) { + unsafe { + let _ = Box::from_raw(state.buffer); + }; + } + + fn apply( + mut sut: Self::SystemUnderTest, + ref_state: &::State, + transition: ::Transition, + ) -> Self::SystemUnderTest { + match transition { + ReaderTransition::Write(x) => sut.producer.advance(x), + ReaderTransition::Clear => { + sut.consumer.clear(&mut sut.producer); + } + ReaderTransition::ReadUpTo(x) => { + let status = sut.status; + let ReaderSut { + ref mut producer, + ref mut consumer, + .. + } = sut; + let mut buf = vec![0; x]; + let res = consumer.read(producer, &mut buf); + match status { + Status::Available(n) => { + let readable = x.min(n); + + assert_eq!(res.unwrap().0, readable); + } + Status::Failed => assert!(res.is_err()), + } + } + } + + ReaderSut { + status: ref_state.clone(), + ..sut + } + } +} + +prop_state_machine! { + #[test] + fn reader_state_test(sequential 1..20 => ReaderTest); +} diff --git a/embassy-stm32/src/dma/ringbuffer/tests/prop_test/writer.rs b/embassy-stm32/src/dma/ringbuffer/tests/prop_test/writer.rs new file mode 100644 index 000000000..15f54c672 --- /dev/null +++ b/embassy-stm32/src/dma/ringbuffer/tests/prop_test/writer.rs @@ -0,0 +1,121 @@ +use core::fmt::Debug; + +use super::*; + +#[derive(Debug, Clone)] +enum WriterTransition { + Read(usize), + WriteUpTo(usize), + Clear, +} + +struct WriterSM; + +impl ReferenceStateMachine for WriterSM { + type State = Status; + type Transition = WriterTransition; + + fn init_state() -> BoxedStrategy { + strategy::Just(Status::new(CAP)).boxed() + } + + fn transitions(_state: &Self::State) -> BoxedStrategy { + prop_oneof![ + (1..50_usize).prop_map(WriterTransition::Read), + (1..50_usize).prop_map(WriterTransition::WriteUpTo), + strategy::Just(WriterTransition::Clear), + ] + .boxed() + } + + fn apply(status: Self::State, transition: &Self::Transition) -> Self::State { + match (status, transition) { + (_, WriterTransition::Clear) => Status::Available(CAP), + (Status::Available(x), WriterTransition::Read(y)) => { + if x < *y { + Status::Failed + } else { + Status::Available(x - y) + } + } + (Status::Available(x), WriterTransition::WriteUpTo(y)) => Status::Available((x + *y).min(CAP)), + (Status::Failed, _) => Status::Failed, + } + } +} + +struct WriterSut { + status: Status, + buffer: *mut [u8], + producer: WritableDmaRingBuffer<'static, u8>, + consumer: DmaMock, +} + +impl Debug for WriterSut { + fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result { + ::fmt(&self.consumer, f) + } +} + +struct WriterTest; + +impl StateMachineTest for WriterTest { + type SystemUnderTest = WriterSut; + type Reference = WriterSM; + + fn init_test(ref_status: &::State) -> Self::SystemUnderTest { + let buffer = Box::into_raw(Box::new([0; CAP])); + WriterSut { + status: ref_status.clone(), + buffer, + producer: WritableDmaRingBuffer::new(unsafe { &mut *buffer }), + consumer: DmaMock::default(), + } + } + + fn teardown(state: Self::SystemUnderTest) { + unsafe { + let _ = Box::from_raw(state.buffer); + }; + } + + fn apply( + mut sut: Self::SystemUnderTest, + ref_status: &::State, + transition: ::Transition, + ) -> Self::SystemUnderTest { + match transition { + WriterTransition::Read(x) => sut.consumer.advance(x), + WriterTransition::Clear => { + sut.producer.clear(&mut sut.consumer); + } + WriterTransition::WriteUpTo(x) => { + let status = sut.status; + let WriterSut { + ref mut producer, + ref mut consumer, + .. + } = sut; + let mut buf = vec![0; x]; + let res = producer.write(consumer, &mut buf); + match status { + Status::Available(n) => { + let writable = x.min(CAP - n.min(CAP)); + assert_eq!(res.unwrap().0, writable); + } + Status::Failed => assert!(res.is_err()), + } + } + } + + WriterSut { + status: ref_status.clone(), + ..sut + } + } +} + +prop_state_machine! { + #[test] + fn writer_state_test(sequential 1..20 => WriterTest); +} From f4ec0cb4d4d16bd4e1c242864c5ca828745704c0 Mon Sep 17 00:00:00 2001 From: Alexandros Liarokapis Date: Mon, 23 Sep 2024 02:01:44 +0300 Subject: [PATCH 074/361] simplify and rename ringbuffer methods, make len available --- embassy-stm32/src/dma/ringbuffer/mod.rs | 92 ++++++++++++------------- 1 file changed, 43 insertions(+), 49 deletions(-) diff --git a/embassy-stm32/src/dma/ringbuffer/mod.rs b/embassy-stm32/src/dma/ringbuffer/mod.rs index 10a9ff975..d4d119a69 100644 --- a/embassy-stm32/src/dma/ringbuffer/mod.rs +++ b/embassy-stm32/src/dma/ringbuffer/mod.rs @@ -27,10 +27,6 @@ struct DmaIndex { pos: usize, } -fn pos(cap: usize, dma: &impl DmaCtrl) -> usize { - cap - dma.get_remaining_transfers() -} - impl DmaIndex { fn reset(&mut self) { self.pos = 0; @@ -42,9 +38,9 @@ impl DmaIndex { } fn dma_sync(&mut self, cap: usize, dma: &mut impl DmaCtrl) { - let fst_pos = pos(cap, dma); + let fst_pos = cap - dma.get_remaining_transfers(); let fst_count = dma.reset_complete_count(); - let pos = pos(cap, dma); + let pos = cap - dma.get_remaining_transfers(); let wrap_count = if pos >= fst_pos { fst_count @@ -68,8 +64,7 @@ impl DmaIndex { rhs.completion_count -= min_count; } - fn diff(&mut self, cap: usize, rhs: &mut DmaIndex) -> isize { - Self::normalize(self, rhs); + fn diff(&self, cap: usize, rhs: &DmaIndex) -> isize { (self.completion_count * cap + self.pos) as isize - (rhs.completion_count * cap + rhs.pos) as isize } } @@ -94,26 +89,39 @@ impl<'a, W: Word> ReadableDmaRingBuffer<'a, W> { pub fn clear(&mut self, dma: &mut impl DmaCtrl) { dma.reset_complete_count(); self.write_index.reset(); - self.update_dma_index(dma); + self.write_index.dma_sync(self.cap(), dma); self.read_index = self.write_index; } - /// The capacity of the ringbuffer + /// Get the full ringbuffer capacity. pub const fn cap(&self) -> usize { self.dma_buf.len() } + /// Get the available readable dma samples. + pub fn len(&self) -> Result { + let diff: usize = self.write_index.diff(self.cap(), &self.read_index).try_into().unwrap(); + + if diff > self.cap() { + Err(OverrunError) + } else { + Ok(diff) + } + } + /// Read elements from the ring buffer /// Return a tuple of the length read and the length remaining in the buffer /// If not all of the elements were read, then there will be some elements in the buffer remaining /// The length remaining is the capacity, ring_buf.len(), less the elements remaining after the read /// OverrunError is returned if the portion to be read was overwritten by the DMA controller. pub fn read(&mut self, dma: &mut impl DmaCtrl, buf: &mut [W]) -> Result<(usize, usize), OverrunError> { - let readable = self.margin(dma)?.min(buf.len()); + self.sync_write_index(dma); + let readable = self.len()?.min(buf.len()); for i in 0..readable { buf[i] = self.read_buf(i); } - let available = self.margin(dma)?; + self.sync_write_index(dma); + let available = self.len()?; self.read_index.advance(self.cap(), readable); Ok((readable, available - readable)) } @@ -151,10 +159,6 @@ impl<'a, W: Word> ReadableDmaRingBuffer<'a, W> { .await } - fn update_dma_index(&mut self, dma: &mut impl DmaCtrl) { - self.write_index.dma_sync(self.cap(), dma) - } - fn read_buf(&self, offset: usize) -> W { unsafe { core::ptr::read_volatile( @@ -165,21 +169,9 @@ impl<'a, W: Word> ReadableDmaRingBuffer<'a, W> { } } - /// Returns available dma samples - fn margin(&mut self, dma: &mut impl DmaCtrl) -> Result { - self.update_dma_index(dma); - - let diff: usize = self - .write_index - .diff(self.cap(), &mut self.read_index) - .try_into() - .unwrap(); - - if diff > self.cap() { - Err(OverrunError) - } else { - Ok(diff) - } + fn sync_write_index(&mut self, dma: &mut impl DmaCtrl) { + self.write_index.dma_sync(self.cap(), dma); + DmaIndex::normalize(&mut self.write_index, &mut self.read_index); } } @@ -207,12 +199,23 @@ impl<'a, W: Word> WritableDmaRingBuffer<'a, W> { pub fn clear(&mut self, dma: &mut impl DmaCtrl) { dma.reset_complete_count(); self.read_index.reset(); - self.update_dma_index(dma); + self.read_index.dma_sync(self.cap(), dma); self.write_index = self.read_index; self.write_index.advance(self.cap(), self.cap()); } - /// Get the capacity of the ringbuffer. + /// Get the remaining writable dma samples. + pub fn len(&self) -> Result { + let diff = self.write_index.diff(self.cap(), &self.read_index); + + if diff < 0 { + Err(OverrunError) + } else { + Ok(self.cap().saturating_sub(diff as usize)) + } + } + + /// Get the full ringbuffer capacity. pub const fn cap(&self) -> usize { self.dma_buf.len() } @@ -220,11 +223,13 @@ impl<'a, W: Word> WritableDmaRingBuffer<'a, W> { /// Append data to the ring buffer. /// Returns a tuple of the data written and the remaining write capacity in the buffer. pub fn write(&mut self, dma: &mut impl DmaCtrl, buf: &[W]) -> Result<(usize, usize), OverrunError> { - let writable = self.margin(dma)?.min(buf.len()); + self.sync_read_index(dma); + let writable = self.len()?.min(buf.len()); for i in 0..writable { self.write_buf(i, buf[i]); } - let available = self.margin(dma)?; + self.sync_read_index(dma); + let available = self.len()?; self.write_index.advance(self.cap(), writable); Ok((writable, available - writable)) } @@ -261,10 +266,6 @@ impl<'a, W: Word> WritableDmaRingBuffer<'a, W> { .await } - fn update_dma_index(&mut self, dma: &mut impl DmaCtrl) { - self.read_index.dma_sync(self.cap(), dma); - } - fn write_buf(&mut self, offset: usize, value: W) { unsafe { core::ptr::write_volatile( @@ -276,16 +277,9 @@ impl<'a, W: Word> WritableDmaRingBuffer<'a, W> { } } - fn margin(&mut self, dma: &mut impl DmaCtrl) -> Result { - self.update_dma_index(dma); - - let diff = self.write_index.diff(self.cap(), &mut self.read_index); - - if diff < 0 { - Err(OverrunError) - } else { - Ok(self.cap().saturating_sub(diff as usize)) - } + fn sync_read_index(&mut self, dma: &mut impl DmaCtrl) { + self.read_index.dma_sync(self.cap(), dma); + DmaIndex::normalize(&mut self.read_index, &mut self.write_index); } } From 85fb890b0025db386459f8b6a573c29f00bf3ed1 Mon Sep 17 00:00:00 2001 From: Alexandros Liarokapis Date: Mon, 23 Sep 2024 02:49:05 +0300 Subject: [PATCH 075/361] add auto-clear functionality to ringbuffer --- embassy-stm32/src/dma/ringbuffer/mod.rs | 56 ++++++++++++------- .../dma/ringbuffer/tests/prop_test/reader.rs | 3 +- .../dma/ringbuffer/tests/prop_test/writer.rs | 3 +- 3 files changed, 40 insertions(+), 22 deletions(-) diff --git a/embassy-stm32/src/dma/ringbuffer/mod.rs b/embassy-stm32/src/dma/ringbuffer/mod.rs index d4d119a69..abc01e3cc 100644 --- a/embassy-stm32/src/dma/ringbuffer/mod.rs +++ b/embassy-stm32/src/dma/ringbuffer/mod.rs @@ -85,7 +85,7 @@ impl<'a, W: Word> ReadableDmaRingBuffer<'a, W> { } } - /// Reset the ring buffer to its initial state + /// Reset the ring buffer to its initial state. pub fn clear(&mut self, dma: &mut impl DmaCtrl) { dma.reset_complete_count(); self.write_index.reset(); @@ -113,17 +113,12 @@ impl<'a, W: Word> ReadableDmaRingBuffer<'a, W> { /// Return a tuple of the length read and the length remaining in the buffer /// If not all of the elements were read, then there will be some elements in the buffer remaining /// The length remaining is the capacity, ring_buf.len(), less the elements remaining after the read - /// OverrunError is returned if the portion to be read was overwritten by the DMA controller. + /// OverrunError is returned if the portion to be read was overwritten by the DMA controller, + /// in which case the rinbuffer will automatically clear itself. pub fn read(&mut self, dma: &mut impl DmaCtrl, buf: &mut [W]) -> Result<(usize, usize), OverrunError> { - self.sync_write_index(dma); - let readable = self.len()?.min(buf.len()); - for i in 0..readable { - buf[i] = self.read_buf(i); - } - self.sync_write_index(dma); - let available = self.len()?; - self.read_index.advance(self.cap(), readable); - Ok((readable, available - readable)) + self.read_raw(dma, buf).inspect_err(|_e| { + self.clear(dma); + }) } /// Read an exact number of elements from the ringbuffer. @@ -159,6 +154,18 @@ impl<'a, W: Word> ReadableDmaRingBuffer<'a, W> { .await } + fn read_raw(&mut self, dma: &mut impl DmaCtrl, buf: &mut [W]) -> Result<(usize, usize), OverrunError> { + self.sync_write_index(dma); + let readable = self.len()?.min(buf.len()); + for i in 0..readable { + buf[i] = self.read_buf(i); + } + self.sync_write_index(dma); + let available = self.len()?; + self.read_index.advance(self.cap(), readable); + Ok((readable, available - readable)) + } + fn read_buf(&self, offset: usize) -> W { unsafe { core::ptr::read_volatile( @@ -222,16 +229,13 @@ impl<'a, W: Word> WritableDmaRingBuffer<'a, W> { /// Append data to the ring buffer. /// Returns a tuple of the data written and the remaining write capacity in the buffer. + /// OverrunError is returned if the portion to be written was previously read by the DMA controller. + /// In this case, the ringbuffer will automatically reset itself, giving a full buffer worth of + /// leeway between the write index and the DMA. pub fn write(&mut self, dma: &mut impl DmaCtrl, buf: &[W]) -> Result<(usize, usize), OverrunError> { - self.sync_read_index(dma); - let writable = self.len()?.min(buf.len()); - for i in 0..writable { - self.write_buf(i, buf[i]); - } - self.sync_read_index(dma); - let available = self.len()?; - self.write_index.advance(self.cap(), writable); - Ok((writable, available - writable)) + self.write_raw(dma, buf).inspect_err(|_e| { + self.clear(dma); + }) } /// Write elements directly to the buffer. @@ -266,6 +270,18 @@ impl<'a, W: Word> WritableDmaRingBuffer<'a, W> { .await } + pub fn write_raw(&mut self, dma: &mut impl DmaCtrl, buf: &[W]) -> Result<(usize, usize), OverrunError> { + self.sync_read_index(dma); + let writable = self.len()?.min(buf.len()); + for i in 0..writable { + self.write_buf(i, buf[i]); + } + self.sync_read_index(dma); + let available = self.len()?; + self.write_index.advance(self.cap(), writable); + Ok((writable, available - writable)) + } + fn write_buf(&mut self, offset: usize, value: W) { unsafe { core::ptr::write_volatile( diff --git a/embassy-stm32/src/dma/ringbuffer/tests/prop_test/reader.rs b/embassy-stm32/src/dma/ringbuffer/tests/prop_test/reader.rs index 6555ebfb0..6e640a813 100644 --- a/embassy-stm32/src/dma/ringbuffer/tests/prop_test/reader.rs +++ b/embassy-stm32/src/dma/ringbuffer/tests/prop_test/reader.rs @@ -38,8 +38,9 @@ impl ReferenceStateMachine for ReaderSM { Status::Available(x + y) } } + (Status::Failed, ReaderTransition::Write(_)) => Status::Failed, (Status::Available(x), ReaderTransition::ReadUpTo(y)) => Status::Available(x.saturating_sub(*y)), - (Status::Failed, _) => Status::Failed, + (Status::Failed, ReaderTransition::ReadUpTo(_)) => Status::Available(0), } } } diff --git a/embassy-stm32/src/dma/ringbuffer/tests/prop_test/writer.rs b/embassy-stm32/src/dma/ringbuffer/tests/prop_test/writer.rs index 15f54c672..c1b3859b2 100644 --- a/embassy-stm32/src/dma/ringbuffer/tests/prop_test/writer.rs +++ b/embassy-stm32/src/dma/ringbuffer/tests/prop_test/writer.rs @@ -38,8 +38,9 @@ impl ReferenceStateMachine for WriterSM { Status::Available(x - y) } } + (Status::Failed, WriterTransition::Read(_)) => Status::Failed, (Status::Available(x), WriterTransition::WriteUpTo(y)) => Status::Available((x + *y).min(CAP)), - (Status::Failed, _) => Status::Failed, + (Status::Failed, WriterTransition::WriteUpTo(_)) => Status::Available(CAP), } } } From 82712252166a274c1499c3680b63f86cdfbb8dfb Mon Sep 17 00:00:00 2001 From: Alexandros Liarokapis Date: Mon, 23 Sep 2024 16:50:26 +0300 Subject: [PATCH 076/361] make len method take mut self and remove sync index calls --- embassy-stm32/src/dma/ringbuffer/mod.rs | 32 ++++++++++--------------- 1 file changed, 12 insertions(+), 20 deletions(-) diff --git a/embassy-stm32/src/dma/ringbuffer/mod.rs b/embassy-stm32/src/dma/ringbuffer/mod.rs index abc01e3cc..76f7f36ec 100644 --- a/embassy-stm32/src/dma/ringbuffer/mod.rs +++ b/embassy-stm32/src/dma/ringbuffer/mod.rs @@ -99,7 +99,10 @@ impl<'a, W: Word> ReadableDmaRingBuffer<'a, W> { } /// Get the available readable dma samples. - pub fn len(&self) -> Result { + pub fn len(&mut self, dma: &mut impl DmaCtrl) -> Result { + self.write_index.dma_sync(self.cap(), dma); + DmaIndex::normalize(&mut self.write_index, &mut self.read_index); + let diff: usize = self.write_index.diff(self.cap(), &self.read_index).try_into().unwrap(); if diff > self.cap() { @@ -155,13 +158,11 @@ impl<'a, W: Word> ReadableDmaRingBuffer<'a, W> { } fn read_raw(&mut self, dma: &mut impl DmaCtrl, buf: &mut [W]) -> Result<(usize, usize), OverrunError> { - self.sync_write_index(dma); - let readable = self.len()?.min(buf.len()); + let readable = self.len(dma)?.min(buf.len()); for i in 0..readable { buf[i] = self.read_buf(i); } - self.sync_write_index(dma); - let available = self.len()?; + let available = self.len(dma)?; self.read_index.advance(self.cap(), readable); Ok((readable, available - readable)) } @@ -175,11 +176,6 @@ impl<'a, W: Word> ReadableDmaRingBuffer<'a, W> { ) } } - - fn sync_write_index(&mut self, dma: &mut impl DmaCtrl) { - self.write_index.dma_sync(self.cap(), dma); - DmaIndex::normalize(&mut self.write_index, &mut self.read_index); - } } pub struct WritableDmaRingBuffer<'a, W: Word> { @@ -212,7 +208,10 @@ impl<'a, W: Word> WritableDmaRingBuffer<'a, W> { } /// Get the remaining writable dma samples. - pub fn len(&self) -> Result { + pub fn len(&mut self, dma: &mut impl DmaCtrl) -> Result { + self.read_index.dma_sync(self.cap(), dma); + DmaIndex::normalize(&mut self.read_index, &mut self.write_index); + let diff = self.write_index.diff(self.cap(), &self.read_index); if diff < 0 { @@ -271,13 +270,11 @@ impl<'a, W: Word> WritableDmaRingBuffer<'a, W> { } pub fn write_raw(&mut self, dma: &mut impl DmaCtrl, buf: &[W]) -> Result<(usize, usize), OverrunError> { - self.sync_read_index(dma); - let writable = self.len()?.min(buf.len()); + let writable = self.len(dma)?.min(buf.len()); for i in 0..writable { self.write_buf(i, buf[i]); } - self.sync_read_index(dma); - let available = self.len()?; + let available = self.len(dma)?; self.write_index.advance(self.cap(), writable); Ok((writable, available - writable)) } @@ -292,11 +289,6 @@ impl<'a, W: Word> WritableDmaRingBuffer<'a, W> { ) } } - - fn sync_read_index(&mut self, dma: &mut impl DmaCtrl) { - self.read_index.dma_sync(self.cap(), dma); - DmaIndex::normalize(&mut self.read_index, &mut self.write_index); - } } #[cfg(test)] From 9c7b296432af40ac44e5e65a3742c691f1414444 Mon Sep 17 00:00:00 2001 From: Alexandros Liarokapis Date: Tue, 24 Sep 2024 00:59:34 +0300 Subject: [PATCH 077/361] overrun at invalid diffs, rename clear to reset, simplify dma_sync method --- embassy-stm32/src/adc/ringbuffered_v2.rs | 3 +- embassy-stm32/src/dma/dma_bdma.rs | 4 +- embassy-stm32/src/dma/ringbuffer/mod.rs | 53 +++++++++---------- embassy-stm32/src/dma/ringbuffer/tests/mod.rs | 22 ++++---- .../dma/ringbuffer/tests/prop_test/reader.rs | 10 ++-- .../dma/ringbuffer/tests/prop_test/writer.rs | 10 ++-- 6 files changed, 49 insertions(+), 53 deletions(-) diff --git a/embassy-stm32/src/adc/ringbuffered_v2.rs b/embassy-stm32/src/adc/ringbuffered_v2.rs index 3b064044e..9fc233222 100644 --- a/embassy-stm32/src/adc/ringbuffered_v2.rs +++ b/embassy-stm32/src/adc/ringbuffered_v2.rs @@ -226,9 +226,8 @@ impl<'d, T: Instance> RingBufferedAdc<'d, T> { /// Turns on ADC if it is not already turned on and starts continuous DMA transfer. pub fn start(&mut self) -> Result<(), OverrunError> { - self.ring_buf.clear(); - self.setup_adc(); + self.ring_buf.clear(); Ok(()) } diff --git a/embassy-stm32/src/dma/dma_bdma.rs b/embassy-stm32/src/dma/dma_bdma.rs index a43137c6e..59ad4d988 100644 --- a/embassy-stm32/src/dma/dma_bdma.rs +++ b/embassy-stm32/src/dma/dma_bdma.rs @@ -833,7 +833,7 @@ impl<'a, W: Word> ReadableRingBuffer<'a, W> { /// Clear all data in the ring buffer. pub fn clear(&mut self) { - self.ringbuf.clear(&mut DmaCtrlImpl(self.channel.reborrow())); + self.ringbuf.reset(&mut DmaCtrlImpl(self.channel.reborrow())); } /// Read elements from the ring buffer @@ -980,7 +980,7 @@ impl<'a, W: Word> WritableRingBuffer<'a, W> { /// Clear all data in the ring buffer. pub fn clear(&mut self) { - self.ringbuf.clear(&mut DmaCtrlImpl(self.channel.reborrow())); + self.ringbuf.reset(&mut DmaCtrlImpl(self.channel.reborrow())); } /// Write elements directly to the raw buffer. diff --git a/embassy-stm32/src/dma/ringbuffer/mod.rs b/embassy-stm32/src/dma/ringbuffer/mod.rs index 76f7f36ec..eb64ad016 100644 --- a/embassy-stm32/src/dma/ringbuffer/mod.rs +++ b/embassy-stm32/src/dma/ringbuffer/mod.rs @@ -23,14 +23,14 @@ pub struct OverrunError; #[derive(Debug, Clone, Copy, Default)] struct DmaIndex { - completion_count: usize, + complete_count: usize, pos: usize, } impl DmaIndex { fn reset(&mut self) { self.pos = 0; - self.completion_count = 0; + self.complete_count = 0; } fn as_index(&self, cap: usize, offset: usize) -> usize { @@ -38,34 +38,31 @@ impl DmaIndex { } fn dma_sync(&mut self, cap: usize, dma: &mut impl DmaCtrl) { - let fst_pos = cap - dma.get_remaining_transfers(); - let fst_count = dma.reset_complete_count(); - let pos = cap - dma.get_remaining_transfers(); + let first_pos = cap - dma.get_remaining_transfers(); + self.complete_count += dma.reset_complete_count(); + self.pos = cap - dma.get_remaining_transfers(); - let wrap_count = if pos >= fst_pos { - fst_count - } else { - fst_count + dma.reset_complete_count() - }; - - self.pos = pos; - self.completion_count += wrap_count; + // If the latter call to get_remaining_transfers() returned a smaller value than the first, the dma + // has wrapped around between calls and we must check if the complete count also incremented. + if self.pos < first_pos { + self.complete_count += dma.reset_complete_count(); + } } fn advance(&mut self, cap: usize, steps: usize) { let next = self.pos + steps; - self.completion_count += next / cap; + self.complete_count += next / cap; self.pos = next % cap; } fn normalize(lhs: &mut DmaIndex, rhs: &mut DmaIndex) { - let min_count = lhs.completion_count.min(rhs.completion_count); - lhs.completion_count -= min_count; - rhs.completion_count -= min_count; + let min_count = lhs.complete_count.min(rhs.complete_count); + lhs.complete_count -= min_count; + rhs.complete_count -= min_count; } fn diff(&self, cap: usize, rhs: &DmaIndex) -> isize { - (self.completion_count * cap + self.pos) as isize - (rhs.completion_count * cap + rhs.pos) as isize + (self.complete_count * cap + self.pos) as isize - (rhs.complete_count * cap + rhs.pos) as isize } } @@ -86,7 +83,7 @@ impl<'a, W: Word> ReadableDmaRingBuffer<'a, W> { } /// Reset the ring buffer to its initial state. - pub fn clear(&mut self, dma: &mut impl DmaCtrl) { + pub fn reset(&mut self, dma: &mut impl DmaCtrl) { dma.reset_complete_count(); self.write_index.reset(); self.write_index.dma_sync(self.cap(), dma); @@ -103,12 +100,12 @@ impl<'a, W: Word> ReadableDmaRingBuffer<'a, W> { self.write_index.dma_sync(self.cap(), dma); DmaIndex::normalize(&mut self.write_index, &mut self.read_index); - let diff: usize = self.write_index.diff(self.cap(), &self.read_index).try_into().unwrap(); + let diff = self.write_index.diff(self.cap(), &self.read_index); - if diff > self.cap() { + if diff < 0 || diff > self.cap() as isize { Err(OverrunError) } else { - Ok(diff) + Ok(diff as usize) } } @@ -117,10 +114,10 @@ impl<'a, W: Word> ReadableDmaRingBuffer<'a, W> { /// If not all of the elements were read, then there will be some elements in the buffer remaining /// The length remaining is the capacity, ring_buf.len(), less the elements remaining after the read /// OverrunError is returned if the portion to be read was overwritten by the DMA controller, - /// in which case the rinbuffer will automatically clear itself. + /// in which case the rinbuffer will automatically reset itself. pub fn read(&mut self, dma: &mut impl DmaCtrl, buf: &mut [W]) -> Result<(usize, usize), OverrunError> { self.read_raw(dma, buf).inspect_err(|_e| { - self.clear(dma); + self.reset(dma); }) } @@ -192,14 +189,14 @@ impl<'a, W: Word> WritableDmaRingBuffer<'a, W> { dma_buf, read_index: Default::default(), write_index: DmaIndex { - completion_count: 0, + complete_count: 0, pos: len, }, } } /// Reset the ring buffer to its initial state. The buffer after the reset will be full. - pub fn clear(&mut self, dma: &mut impl DmaCtrl) { + pub fn reset(&mut self, dma: &mut impl DmaCtrl) { dma.reset_complete_count(); self.read_index.reset(); self.read_index.dma_sync(self.cap(), dma); @@ -214,7 +211,7 @@ impl<'a, W: Word> WritableDmaRingBuffer<'a, W> { let diff = self.write_index.diff(self.cap(), &self.read_index); - if diff < 0 { + if diff < 0 || diff > self.cap() as isize { Err(OverrunError) } else { Ok(self.cap().saturating_sub(diff as usize)) @@ -233,7 +230,7 @@ impl<'a, W: Word> WritableDmaRingBuffer<'a, W> { /// leeway between the write index and the DMA. pub fn write(&mut self, dma: &mut impl DmaCtrl, buf: &[W]) -> Result<(usize, usize), OverrunError> { self.write_raw(dma, buf).inspect_err(|_e| { - self.clear(dma); + self.reset(dma); }) } diff --git a/embassy-stm32/src/dma/ringbuffer/tests/mod.rs b/embassy-stm32/src/dma/ringbuffer/tests/mod.rs index 9768e1df8..1800eae69 100644 --- a/embassy-stm32/src/dma/ringbuffer/tests/mod.rs +++ b/embassy-stm32/src/dma/ringbuffer/tests/mod.rs @@ -64,12 +64,12 @@ fn dma_index_dma_sync_syncs_position_to_last_read_if_sync_takes_place_on_same_dm ]); let mut index = DmaIndex::default(); index.dma_sync(CAP, &mut dma); - assert_eq!(index.completion_count, 0); + assert_eq!(index.complete_count, 0); assert_eq!(index.pos, 7); } #[test] -fn dma_index_dma_sync_updates_completion_count_properly_if_sync_takes_place_on_same_dma_cycle() { +fn dma_index_dma_sync_updates_complete_count_properly_if_sync_takes_place_on_same_dma_cycle() { let mut dma = TestCircularTransfer::new(CAP); dma.setup(vec![ TestCircularTransferRequest::PositionRequest(4), @@ -77,9 +77,9 @@ fn dma_index_dma_sync_updates_completion_count_properly_if_sync_takes_place_on_s TestCircularTransferRequest::PositionRequest(7), ]); let mut index = DmaIndex::default(); - index.completion_count = 1; + index.complete_count = 1; index.dma_sync(CAP, &mut dma); - assert_eq!(index.completion_count, 3); + assert_eq!(index.complete_count, 3); assert_eq!(index.pos, 7); } @@ -94,12 +94,12 @@ fn dma_index_dma_sync_syncs_to_last_position_if_reads_occur_on_different_dma_cyc ]); let mut index = DmaIndex::default(); index.dma_sync(CAP, &mut dma); - assert_eq!(index.completion_count, 1); + assert_eq!(index.complete_count, 1); assert_eq!(index.pos, 5); } #[test] -fn dma_index_dma_sync_detects_new_cycle_if_later_position_is_less_than_first_and_first_completion_count_occurs_on_first_cycle( +fn dma_index_dma_sync_detects_new_cycle_if_later_position_is_less_than_first_and_first_complete_count_occurs_on_first_cycle( ) { let mut dma = TestCircularTransfer::new(CAP); dma.setup(vec![ @@ -109,14 +109,14 @@ fn dma_index_dma_sync_detects_new_cycle_if_later_position_is_less_than_first_and TestCircularTransferRequest::ResetCompleteCount(1), ]); let mut index = DmaIndex::default(); - index.completion_count = 1; + index.complete_count = 1; index.dma_sync(CAP, &mut dma); - assert_eq!(index.completion_count, 3); + assert_eq!(index.complete_count, 3); assert_eq!(index.pos, 5); } #[test] -fn dma_index_dma_sync_detects_new_cycle_if_later_position_is_less_than_first_and_first_completion_count_occurs_on_later_cycle( +fn dma_index_dma_sync_detects_new_cycle_if_later_position_is_less_than_first_and_first_complete_count_occurs_on_later_cycle( ) { let mut dma = TestCircularTransfer::new(CAP); dma.setup(vec![ @@ -126,9 +126,9 @@ fn dma_index_dma_sync_detects_new_cycle_if_later_position_is_less_than_first_and TestCircularTransferRequest::ResetCompleteCount(0), ]); let mut index = DmaIndex::default(); - index.completion_count = 1; + index.complete_count = 1; index.dma_sync(CAP, &mut dma); - assert_eq!(index.completion_count, 3); + assert_eq!(index.complete_count, 3); assert_eq!(index.pos, 5); } diff --git a/embassy-stm32/src/dma/ringbuffer/tests/prop_test/reader.rs b/embassy-stm32/src/dma/ringbuffer/tests/prop_test/reader.rs index 6e640a813..4f3957a68 100644 --- a/embassy-stm32/src/dma/ringbuffer/tests/prop_test/reader.rs +++ b/embassy-stm32/src/dma/ringbuffer/tests/prop_test/reader.rs @@ -5,7 +5,7 @@ use super::*; #[derive(Debug, Clone)] enum ReaderTransition { Write(usize), - Clear, + Reset, ReadUpTo(usize), } @@ -23,14 +23,14 @@ impl ReferenceStateMachine for ReaderSM { prop_oneof![ (1..50_usize).prop_map(ReaderTransition::Write), (1..50_usize).prop_map(ReaderTransition::ReadUpTo), - strategy::Just(ReaderTransition::Clear), + strategy::Just(ReaderTransition::Reset), ] .boxed() } fn apply(status: Self::State, transition: &Self::Transition) -> Self::State { match (status, transition) { - (_, ReaderTransition::Clear) => Status::Available(0), + (_, ReaderTransition::Reset) => Status::Available(0), (Status::Available(x), ReaderTransition::Write(y)) => { if x + y > CAP { Status::Failed @@ -87,8 +87,8 @@ impl StateMachineTest for ReaderTest { ) -> Self::SystemUnderTest { match transition { ReaderTransition::Write(x) => sut.producer.advance(x), - ReaderTransition::Clear => { - sut.consumer.clear(&mut sut.producer); + ReaderTransition::Reset => { + sut.consumer.reset(&mut sut.producer); } ReaderTransition::ReadUpTo(x) => { let status = sut.status; diff --git a/embassy-stm32/src/dma/ringbuffer/tests/prop_test/writer.rs b/embassy-stm32/src/dma/ringbuffer/tests/prop_test/writer.rs index c1b3859b2..15433c0ee 100644 --- a/embassy-stm32/src/dma/ringbuffer/tests/prop_test/writer.rs +++ b/embassy-stm32/src/dma/ringbuffer/tests/prop_test/writer.rs @@ -6,7 +6,7 @@ use super::*; enum WriterTransition { Read(usize), WriteUpTo(usize), - Clear, + Reset, } struct WriterSM; @@ -23,14 +23,14 @@ impl ReferenceStateMachine for WriterSM { prop_oneof![ (1..50_usize).prop_map(WriterTransition::Read), (1..50_usize).prop_map(WriterTransition::WriteUpTo), - strategy::Just(WriterTransition::Clear), + strategy::Just(WriterTransition::Reset), ] .boxed() } fn apply(status: Self::State, transition: &Self::Transition) -> Self::State { match (status, transition) { - (_, WriterTransition::Clear) => Status::Available(CAP), + (_, WriterTransition::Reset) => Status::Available(CAP), (Status::Available(x), WriterTransition::Read(y)) => { if x < *y { Status::Failed @@ -87,8 +87,8 @@ impl StateMachineTest for WriterTest { ) -> Self::SystemUnderTest { match transition { WriterTransition::Read(x) => sut.consumer.advance(x), - WriterTransition::Clear => { - sut.producer.clear(&mut sut.consumer); + WriterTransition::Reset => { + sut.producer.reset(&mut sut.consumer); } WriterTransition::WriteUpTo(x) => { let status = sut.status; From c991ddb76662cc7da5e847f33c1a446f17822887 Mon Sep 17 00:00:00 2001 From: Alexandros Liarokapis Date: Tue, 24 Sep 2024 17:46:53 +0300 Subject: [PATCH 078/361] use request_pause instead of request_stop at adc shutdown --- embassy-stm32/src/adc/ringbuffered_v2.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/embassy-stm32/src/adc/ringbuffered_v2.rs b/embassy-stm32/src/adc/ringbuffered_v2.rs index 9fc233222..9c114e463 100644 --- a/embassy-stm32/src/adc/ringbuffered_v2.rs +++ b/embassy-stm32/src/adc/ringbuffered_v2.rs @@ -244,7 +244,7 @@ impl<'d, T: Instance> RingBufferedAdc<'d, T> { /// [`start`]: #method.start pub fn teardown_adc(&mut self) { // Stop the DMA transfer - self.ring_buf.request_stop(); + self.ring_buf.request_pause(); let r = T::regs(); From f0d2ebdc7ead41307155b083790b8450ca2b7eac Mon Sep 17 00:00:00 2001 From: Alexandros Liarokapis Date: Tue, 1 Oct 2024 17:59:04 +0300 Subject: [PATCH 079/361] stm32: fix ringbugger overrun errors due to bad dma wrap-around behavior --- embassy-stm32/src/dma/dma_bdma.rs | 30 ++++--- embassy-stm32/src/dma/ringbuffer/mod.rs | 65 +++++++++------ embassy-stm32/src/dma/ringbuffer/tests/mod.rs | 83 +------------------ embassy-stm32/src/sai/mod.rs | 10 ++- embassy-stm32/src/usart/ringbuffered.rs | 1 - 5 files changed, 72 insertions(+), 117 deletions(-) diff --git a/embassy-stm32/src/dma/dma_bdma.rs b/embassy-stm32/src/dma/dma_bdma.rs index 59ad4d988..3f047a9a4 100644 --- a/embassy-stm32/src/dma/dma_bdma.rs +++ b/embassy-stm32/src/dma/dma_bdma.rs @@ -6,7 +6,7 @@ use core::task::{Context, Poll, Waker}; use embassy_hal_internal::{into_ref, Peripheral, PeripheralRef}; use embassy_sync::waitqueue::AtomicWaker; -use super::ringbuffer::{DmaCtrl, OverrunError, ReadableDmaRingBuffer, WritableDmaRingBuffer}; +use super::ringbuffer::{DmaCtrl, Error, ReadableDmaRingBuffer, WritableDmaRingBuffer}; use super::word::{Word, WordSize}; use super::{AnyChannel, Channel, Dir, Request, STATE}; use crate::interrupt::typelevel::Interrupt; @@ -299,7 +299,6 @@ impl AnyChannel { } else { return; } - state.waker.wake(); } #[cfg(bdma)] @@ -828,7 +827,8 @@ impl<'a, W: Word> ReadableRingBuffer<'a, W> { /// /// You must call this after creating it for it to work. pub fn start(&mut self) { - self.channel.start() + self.channel.start(); + self.clear(); } /// Clear all data in the ring buffer. @@ -840,15 +840,15 @@ impl<'a, W: Word> ReadableRingBuffer<'a, W> { /// Return a tuple of the length read and the length remaining in the buffer /// If not all of the elements were read, then there will be some elements in the buffer remaining /// The length remaining is the capacity, ring_buf.len(), less the elements remaining after the read - /// OverrunError is returned if the portion to be read was overwritten by the DMA controller. - pub fn read(&mut self, buf: &mut [W]) -> Result<(usize, usize), OverrunError> { + /// Error is returned if the portion to be read was overwritten by the DMA controller. + pub fn read(&mut self, buf: &mut [W]) -> Result<(usize, usize), Error> { self.ringbuf.read(&mut DmaCtrlImpl(self.channel.reborrow()), buf) } /// Read an exact number of elements from the ringbuffer. /// /// Returns the remaining number of elements available for immediate reading. - /// OverrunError is returned if the portion to be read was overwritten by the DMA controller. + /// Error is returned if the portion to be read was overwritten by the DMA controller. /// /// Async/Wake Behavior: /// The underlying DMA peripheral only can wake us when its buffer pointer has reached the halfway point, @@ -856,12 +856,17 @@ impl<'a, W: Word> ReadableRingBuffer<'a, W> { /// ring buffer was created with a buffer of size 'N': /// - If M equals N/2 or N/2 divides evenly into M, this function will return every N/2 elements read on the DMA source. /// - Otherwise, this function may need up to N/2 extra elements to arrive before returning. - pub async fn read_exact(&mut self, buffer: &mut [W]) -> Result { + pub async fn read_exact(&mut self, buffer: &mut [W]) -> Result { self.ringbuf .read_exact(&mut DmaCtrlImpl(self.channel.reborrow()), buffer) .await } + /// The current length of the ringbuffer + pub fn len(&mut self) -> Result { + Ok(self.ringbuf.len(&mut DmaCtrlImpl(self.channel.reborrow()))?) + } + /// The capacity of the ringbuffer pub const fn capacity(&self) -> usize { self.ringbuf.cap() @@ -986,23 +991,28 @@ impl<'a, W: Word> WritableRingBuffer<'a, W> { /// Write elements directly to the raw buffer. /// This can be used to fill the buffer before starting the DMA transfer. #[allow(dead_code)] - pub fn write_immediate(&mut self, buf: &[W]) -> Result<(usize, usize), OverrunError> { + pub fn write_immediate(&mut self, buf: &[W]) -> Result<(usize, usize), Error> { self.ringbuf.write_immediate(buf) } /// Write elements from the ring buffer /// Return a tuple of the length written and the length remaining in the buffer - pub fn write(&mut self, buf: &[W]) -> Result<(usize, usize), OverrunError> { + pub fn write(&mut self, buf: &[W]) -> Result<(usize, usize), Error> { self.ringbuf.write(&mut DmaCtrlImpl(self.channel.reborrow()), buf) } /// Write an exact number of elements to the ringbuffer. - pub async fn write_exact(&mut self, buffer: &[W]) -> Result { + pub async fn write_exact(&mut self, buffer: &[W]) -> Result { self.ringbuf .write_exact(&mut DmaCtrlImpl(self.channel.reborrow()), buffer) .await } + /// The current length of the ringbuffer + pub fn len(&mut self) -> Result { + Ok(self.ringbuf.len(&mut DmaCtrlImpl(self.channel.reborrow()))?) + } + /// The capacity of the ringbuffer pub const fn capacity(&self) -> usize { self.ringbuf.cap() diff --git a/embassy-stm32/src/dma/ringbuffer/mod.rs b/embassy-stm32/src/dma/ringbuffer/mod.rs index eb64ad016..a257faa5b 100644 --- a/embassy-stm32/src/dma/ringbuffer/mod.rs +++ b/embassy-stm32/src/dma/ringbuffer/mod.rs @@ -19,9 +19,13 @@ pub trait DmaCtrl { #[derive(Debug, PartialEq)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] -pub struct OverrunError; +pub enum Error { + Overrun, + DmaUnsynced, +} #[derive(Debug, Clone, Copy, Default)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] struct DmaIndex { complete_count: usize, pos: usize, @@ -38,15 +42,19 @@ impl DmaIndex { } fn dma_sync(&mut self, cap: usize, dma: &mut impl DmaCtrl) { - let first_pos = cap - dma.get_remaining_transfers(); - self.complete_count += dma.reset_complete_count(); - self.pos = cap - dma.get_remaining_transfers(); + // Important! + // The ordering of the first two lines matters! + // If changed, the code will detect a wrong +capacity + // jump at wrap-around. + let count_diff = dma.reset_complete_count(); + let pos = cap - dma.get_remaining_transfers(); + self.pos = if pos < self.pos && count_diff == 0 { + cap - 1 + } else { + pos + }; - // If the latter call to get_remaining_transfers() returned a smaller value than the first, the dma - // has wrapped around between calls and we must check if the complete count also incremented. - if self.pos < first_pos { - self.complete_count += dma.reset_complete_count(); - } + self.complete_count += count_diff; } fn advance(&mut self, cap: usize, steps: usize) { @@ -96,14 +104,18 @@ impl<'a, W: Word> ReadableDmaRingBuffer<'a, W> { } /// Get the available readable dma samples. - pub fn len(&mut self, dma: &mut impl DmaCtrl) -> Result { + pub fn len(&mut self, dma: &mut impl DmaCtrl) -> Result { self.write_index.dma_sync(self.cap(), dma); DmaIndex::normalize(&mut self.write_index, &mut self.read_index); let diff = self.write_index.diff(self.cap(), &self.read_index); - if diff < 0 || diff > self.cap() as isize { - Err(OverrunError) + if diff < 0 { + return Err(Error::DmaUnsynced); + } + + if diff > self.cap() as isize { + Err(Error::Overrun) } else { Ok(diff as usize) } @@ -113,9 +125,9 @@ impl<'a, W: Word> ReadableDmaRingBuffer<'a, W> { /// Return a tuple of the length read and the length remaining in the buffer /// If not all of the elements were read, then there will be some elements in the buffer remaining /// The length remaining is the capacity, ring_buf.len(), less the elements remaining after the read - /// OverrunError is returned if the portion to be read was overwritten by the DMA controller, + /// Error is returned if the portion to be read was overwritten by the DMA controller, /// in which case the rinbuffer will automatically reset itself. - pub fn read(&mut self, dma: &mut impl DmaCtrl, buf: &mut [W]) -> Result<(usize, usize), OverrunError> { + pub fn read(&mut self, dma: &mut impl DmaCtrl, buf: &mut [W]) -> Result<(usize, usize), Error> { self.read_raw(dma, buf).inspect_err(|_e| { self.reset(dma); }) @@ -124,7 +136,7 @@ impl<'a, W: Word> ReadableDmaRingBuffer<'a, W> { /// Read an exact number of elements from the ringbuffer. /// /// Returns the remaining number of elements available for immediate reading. - /// OverrunError is returned if the portion to be read was overwritten by the DMA controller. + /// Error is returned if the portion to be read was overwritten by the DMA controller. /// /// Async/Wake Behavior: /// The underlying DMA peripheral only can wake us when its buffer pointer has reached the halfway point, @@ -132,7 +144,7 @@ impl<'a, W: Word> ReadableDmaRingBuffer<'a, W> { /// ring buffer was created with a buffer of size 'N': /// - If M equals N/2 or N/2 divides evenly into M, this function will return every N/2 elements read on the DMA source. /// - Otherwise, this function may need up to N/2 extra elements to arrive before returning. - pub async fn read_exact(&mut self, dma: &mut impl DmaCtrl, buffer: &mut [W]) -> Result { + pub async fn read_exact(&mut self, dma: &mut impl DmaCtrl, buffer: &mut [W]) -> Result { let mut read_data = 0; let buffer_len = buffer.len(); @@ -154,7 +166,7 @@ impl<'a, W: Word> ReadableDmaRingBuffer<'a, W> { .await } - fn read_raw(&mut self, dma: &mut impl DmaCtrl, buf: &mut [W]) -> Result<(usize, usize), OverrunError> { + fn read_raw(&mut self, dma: &mut impl DmaCtrl, buf: &mut [W]) -> Result<(usize, usize), Error> { let readable = self.len(dma)?.min(buf.len()); for i in 0..readable { buf[i] = self.read_buf(i); @@ -205,14 +217,17 @@ impl<'a, W: Word> WritableDmaRingBuffer<'a, W> { } /// Get the remaining writable dma samples. - pub fn len(&mut self, dma: &mut impl DmaCtrl) -> Result { + pub fn len(&mut self, dma: &mut impl DmaCtrl) -> Result { self.read_index.dma_sync(self.cap(), dma); DmaIndex::normalize(&mut self.read_index, &mut self.write_index); let diff = self.write_index.diff(self.cap(), &self.read_index); - if diff < 0 || diff > self.cap() as isize { - Err(OverrunError) + if diff > self.cap() as isize { + return Err(Error::DmaUnsynced); + } + if diff < 0 { + Err(Error::Overrun) } else { Ok(self.cap().saturating_sub(diff as usize)) } @@ -225,17 +240,17 @@ impl<'a, W: Word> WritableDmaRingBuffer<'a, W> { /// Append data to the ring buffer. /// Returns a tuple of the data written and the remaining write capacity in the buffer. - /// OverrunError is returned if the portion to be written was previously read by the DMA controller. + /// Error is returned if the portion to be written was previously read by the DMA controller. /// In this case, the ringbuffer will automatically reset itself, giving a full buffer worth of /// leeway between the write index and the DMA. - pub fn write(&mut self, dma: &mut impl DmaCtrl, buf: &[W]) -> Result<(usize, usize), OverrunError> { + pub fn write(&mut self, dma: &mut impl DmaCtrl, buf: &[W]) -> Result<(usize, usize), Error> { self.write_raw(dma, buf).inspect_err(|_e| { self.reset(dma); }) } /// Write elements directly to the buffer. - pub fn write_immediate(&mut self, buf: &[W]) -> Result<(usize, usize), OverrunError> { + pub fn write_immediate(&mut self, buf: &[W]) -> Result<(usize, usize), Error> { for (i, data) in buf.iter().enumerate() { self.write_buf(i, *data) } @@ -244,7 +259,7 @@ impl<'a, W: Word> WritableDmaRingBuffer<'a, W> { } /// Write an exact number of elements to the ringbuffer. - pub async fn write_exact(&mut self, dma: &mut impl DmaCtrl, buffer: &[W]) -> Result { + pub async fn write_exact(&mut self, dma: &mut impl DmaCtrl, buffer: &[W]) -> Result { let mut written_data = 0; let buffer_len = buffer.len(); @@ -266,7 +281,7 @@ impl<'a, W: Word> WritableDmaRingBuffer<'a, W> { .await } - pub fn write_raw(&mut self, dma: &mut impl DmaCtrl, buf: &[W]) -> Result<(usize, usize), OverrunError> { + fn write_raw(&mut self, dma: &mut impl DmaCtrl, buf: &[W]) -> Result<(usize, usize), Error> { let writable = self.len(dma)?.min(buf.len()); for i in 0..writable { self.write_buf(i, buf[i]); diff --git a/embassy-stm32/src/dma/ringbuffer/tests/mod.rs b/embassy-stm32/src/dma/ringbuffer/tests/mod.rs index 1800eae69..6fabedb83 100644 --- a/embassy-stm32/src/dma/ringbuffer/tests/mod.rs +++ b/embassy-stm32/src/dma/ringbuffer/tests/mod.rs @@ -2,13 +2,14 @@ use std::{cell, vec}; use super::*; -#[allow(dead_code)] +#[allow(unused)] #[derive(PartialEq, Debug)] enum TestCircularTransferRequest { ResetCompleteCount(usize), PositionRequest(usize), } +#[allow(unused)] struct TestCircularTransfer { len: usize, requests: cell::RefCell>, @@ -39,6 +40,7 @@ impl DmaCtrl for TestCircularTransfer { } impl TestCircularTransfer { + #[allow(unused)] pub fn new(len: usize) -> Self { Self { requests: cell::RefCell::new(vec![]), @@ -46,6 +48,7 @@ impl TestCircularTransfer { } } + #[allow(unused)] pub fn setup(&self, mut requests: vec::Vec) { requests.reverse(); self.requests.replace(requests); @@ -54,84 +57,6 @@ impl TestCircularTransfer { const CAP: usize = 16; -#[test] -fn dma_index_dma_sync_syncs_position_to_last_read_if_sync_takes_place_on_same_dma_cycle() { - let mut dma = TestCircularTransfer::new(CAP); - dma.setup(vec![ - TestCircularTransferRequest::PositionRequest(4), - TestCircularTransferRequest::ResetCompleteCount(0), - TestCircularTransferRequest::PositionRequest(7), - ]); - let mut index = DmaIndex::default(); - index.dma_sync(CAP, &mut dma); - assert_eq!(index.complete_count, 0); - assert_eq!(index.pos, 7); -} - -#[test] -fn dma_index_dma_sync_updates_complete_count_properly_if_sync_takes_place_on_same_dma_cycle() { - let mut dma = TestCircularTransfer::new(CAP); - dma.setup(vec![ - TestCircularTransferRequest::PositionRequest(4), - TestCircularTransferRequest::ResetCompleteCount(2), - TestCircularTransferRequest::PositionRequest(7), - ]); - let mut index = DmaIndex::default(); - index.complete_count = 1; - index.dma_sync(CAP, &mut dma); - assert_eq!(index.complete_count, 3); - assert_eq!(index.pos, 7); -} - -#[test] -fn dma_index_dma_sync_syncs_to_last_position_if_reads_occur_on_different_dma_cycles() { - let mut dma = TestCircularTransfer::new(CAP); - dma.setup(vec![ - TestCircularTransferRequest::PositionRequest(10), - TestCircularTransferRequest::ResetCompleteCount(1), - TestCircularTransferRequest::PositionRequest(5), - TestCircularTransferRequest::ResetCompleteCount(0), - ]); - let mut index = DmaIndex::default(); - index.dma_sync(CAP, &mut dma); - assert_eq!(index.complete_count, 1); - assert_eq!(index.pos, 5); -} - -#[test] -fn dma_index_dma_sync_detects_new_cycle_if_later_position_is_less_than_first_and_first_complete_count_occurs_on_first_cycle( -) { - let mut dma = TestCircularTransfer::new(CAP); - dma.setup(vec![ - TestCircularTransferRequest::PositionRequest(10), - TestCircularTransferRequest::ResetCompleteCount(1), - TestCircularTransferRequest::PositionRequest(5), - TestCircularTransferRequest::ResetCompleteCount(1), - ]); - let mut index = DmaIndex::default(); - index.complete_count = 1; - index.dma_sync(CAP, &mut dma); - assert_eq!(index.complete_count, 3); - assert_eq!(index.pos, 5); -} - -#[test] -fn dma_index_dma_sync_detects_new_cycle_if_later_position_is_less_than_first_and_first_complete_count_occurs_on_later_cycle( -) { - let mut dma = TestCircularTransfer::new(CAP); - dma.setup(vec![ - TestCircularTransferRequest::PositionRequest(10), - TestCircularTransferRequest::ResetCompleteCount(2), - TestCircularTransferRequest::PositionRequest(5), - TestCircularTransferRequest::ResetCompleteCount(0), - ]); - let mut index = DmaIndex::default(); - index.complete_count = 1; - index.dma_sync(CAP, &mut dma); - assert_eq!(index.complete_count, 3); - assert_eq!(index.pos, 5); -} - #[test] fn dma_index_as_index_returns_index_mod_cap_by_default() { let index = DmaIndex::default(); diff --git a/embassy-stm32/src/sai/mod.rs b/embassy-stm32/src/sai/mod.rs index 63f48ace0..7d2f071de 100644 --- a/embassy-stm32/src/sai/mod.rs +++ b/embassy-stm32/src/sai/mod.rs @@ -27,8 +27,14 @@ pub enum Error { } #[cfg(not(gpdma))] -impl From for Error { - fn from(_: ringbuffer::OverrunError) -> Self { +impl From for Error { + fn from(#[allow(unused)] err: ringbuffer::Error) -> Self { + #[cfg(feature = "defmt")] + { + if err == ringbuffer::Error::DmaUnsynced { + defmt::error!("Ringbuffer broken invariants detected!"); + } + } Self::Overrun } } diff --git a/embassy-stm32/src/usart/ringbuffered.rs b/embassy-stm32/src/usart/ringbuffered.rs index 75834bf37..eb2399d9c 100644 --- a/embassy-stm32/src/usart/ringbuffered.rs +++ b/embassy-stm32/src/usart/ringbuffered.rs @@ -83,7 +83,6 @@ impl<'d> RingBufferedUartRx<'d> { // Clear the buffer so that it is ready to receive data compiler_fence(Ordering::SeqCst); self.ring_buf.start(); - self.ring_buf.clear(); let r = self.info.regs; // clear all interrupts and DMA Rx Request From 2ec05da5ddadad9b34c72e8ef1a57a7662a6f0e0 Mon Sep 17 00:00:00 2001 From: Alexandros Liarokapis Date: Wed, 2 Oct 2024 13:10:07 +0300 Subject: [PATCH 080/361] simplify if/else handling on ringbuffer --- embassy-stm32/src/dma/ringbuffer/mod.rs | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/embassy-stm32/src/dma/ringbuffer/mod.rs b/embassy-stm32/src/dma/ringbuffer/mod.rs index a257faa5b..ac4be3e18 100644 --- a/embassy-stm32/src/dma/ringbuffer/mod.rs +++ b/embassy-stm32/src/dma/ringbuffer/mod.rs @@ -111,10 +111,8 @@ impl<'a, W: Word> ReadableDmaRingBuffer<'a, W> { let diff = self.write_index.diff(self.cap(), &self.read_index); if diff < 0 { - return Err(Error::DmaUnsynced); - } - - if diff > self.cap() as isize { + Err(Error::DmaUnsynced) + } else if diff > self.cap() as isize { Err(Error::Overrun) } else { Ok(diff as usize) @@ -223,11 +221,10 @@ impl<'a, W: Word> WritableDmaRingBuffer<'a, W> { let diff = self.write_index.diff(self.cap(), &self.read_index); - if diff > self.cap() as isize { - return Err(Error::DmaUnsynced); - } if diff < 0 { Err(Error::Overrun) + } else if diff > self.cap() as isize { + Err(Error::DmaUnsynced) } else { Ok(self.cap().saturating_sub(diff as usize)) } From d280b234283d3c65e7ba6087e52005160a538ad2 Mon Sep 17 00:00:00 2001 From: Alexandros Liarokapis Date: Thu, 3 Oct 2024 13:04:15 +0300 Subject: [PATCH 081/361] fix adc/ringbuffered_v2.rs --- embassy-stm32/src/adc/ringbuffered_v2.rs | 4 +++- embassy-stm32/src/dma/ringbuffer/mod.rs | 3 ++- embassy-stm32/src/lib.rs | 3 +++ 3 files changed, 8 insertions(+), 2 deletions(-) diff --git a/embassy-stm32/src/adc/ringbuffered_v2.rs b/embassy-stm32/src/adc/ringbuffered_v2.rs index 9c114e463..3f0c1a57a 100644 --- a/embassy-stm32/src/adc/ringbuffered_v2.rs +++ b/embassy-stm32/src/adc/ringbuffered_v2.rs @@ -6,11 +6,13 @@ use embassy_hal_internal::{into_ref, Peripheral}; use stm32_metapac::adc::vals::SampleTime; use crate::adc::{Adc, AdcChannel, Instance, RxDma}; -use crate::dma::ringbuffer::OverrunError; use crate::dma::{Priority, ReadableRingBuffer, TransferOptions}; use crate::pac::adc::vals; use crate::rcc; +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +pub struct OverrunError; + fn clear_interrupt_flags(r: crate::pac::adc::Adc) { r.sr().modify(|regs| { regs.set_eoc(false); diff --git a/embassy-stm32/src/dma/ringbuffer/mod.rs b/embassy-stm32/src/dma/ringbuffer/mod.rs index ac4be3e18..12d418414 100644 --- a/embassy-stm32/src/dma/ringbuffer/mod.rs +++ b/embassy-stm32/src/dma/ringbuffer/mod.rs @@ -119,7 +119,8 @@ impl<'a, W: Word> ReadableDmaRingBuffer<'a, W> { } } - /// Read elements from the ring buffer + /// Read elements from the ring buffer. + /// /// Return a tuple of the length read and the length remaining in the buffer /// If not all of the elements were read, then there will be some elements in the buffer remaining /// The length remaining is the capacity, ring_buf.len(), less the elements remaining after the read diff --git a/embassy-stm32/src/lib.rs b/embassy-stm32/src/lib.rs index 451f595e0..5f103e652 100644 --- a/embassy-stm32/src/lib.rs +++ b/embassy-stm32/src/lib.rs @@ -296,6 +296,9 @@ mod dual_core { /// It cannot be initialized by the user. The intended use is: /// /// ``` + /// use core::mem::MaybeUninit; + /// use embassy_stm32::{init_secondary, SharedData}; + /// /// #[link_section = ".ram_d3"] /// static SHARED_DATA: MaybeUninit = MaybeUninit::uninit(); /// From 4f810e47f5244b0cb7780ae1eebdba108d9e88c9 Mon Sep 17 00:00:00 2001 From: Alexandros Liarokapis Date: Thu, 3 Oct 2024 21:54:47 +0300 Subject: [PATCH 082/361] enable ci usart tests --- ci.sh | 5 ----- 1 file changed, 5 deletions(-) diff --git a/ci.sh b/ci.sh index ee47ee1c5..aa078be31 100755 --- a/ci.sh +++ b/ci.sh @@ -305,11 +305,6 @@ rm out/tests/stm32f207zg/eth # doesn't work, gives "noise error", no idea why. usart_dma does pass. rm out/tests/stm32u5a5zj/usart -# flaky, probably due to bad ringbuffered dma code. -rm out/tests/stm32l152re/usart_rx_ringbuffered -rm out/tests/stm32f207zg/usart_rx_ringbuffered -rm out/tests/stm32wl55jc/usart_rx_ringbuffered - if [[ -z "${TELEPROBE_TOKEN-}" ]]; then echo No teleprobe token found, skipping running HIL tests exit From 28d03537e958213dab345ad4502dad2b32256135 Mon Sep 17 00:00:00 2001 From: Alexandros Liarokapis Date: Mon, 7 Oct 2024 23:12:35 +0300 Subject: [PATCH 083/361] stm32: Automatically clear on WritableRingBuffer start --- embassy-stm32/src/dma/dma_bdma.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/embassy-stm32/src/dma/dma_bdma.rs b/embassy-stm32/src/dma/dma_bdma.rs index 3f047a9a4..cdc603e2c 100644 --- a/embassy-stm32/src/dma/dma_bdma.rs +++ b/embassy-stm32/src/dma/dma_bdma.rs @@ -980,7 +980,8 @@ impl<'a, W: Word> WritableRingBuffer<'a, W> { /// /// You must call this after creating it for it to work. pub fn start(&mut self) { - self.channel.start() + self.channel.start(); + self.clear(); } /// Clear all data in the ring buffer. From e350ca836a985829b7548b8ac3009f38573b4215 Mon Sep 17 00:00:00 2001 From: Dion Dokter Date: Tue, 15 Oct 2024 13:23:57 +0200 Subject: [PATCH 084/361] STM32 QSPI typo (#3418) --- embassy-stm32/src/qspi/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/embassy-stm32/src/qspi/mod.rs b/embassy-stm32/src/qspi/mod.rs index 308947e99..1dfd0e918 100644 --- a/embassy-stm32/src/qspi/mod.rs +++ b/embassy-stm32/src/qspi/mod.rs @@ -18,7 +18,7 @@ use crate::{peripherals, Peripheral}; /// QSPI transfer configuration. pub struct TransferConfig { - /// Instraction width (IMODE) + /// Instruction width (IMODE) pub iwidth: QspiWidth, /// Address width (ADMODE) pub awidth: QspiWidth, From 8af52488e7668897ecdf6a77f76a0003978d04e4 Mon Sep 17 00:00:00 2001 From: Tu Nguyen Date: Wed, 16 Oct 2024 17:45:40 +0700 Subject: [PATCH 085/361] add RTR flag if it is remote frame --- embassy-stm32/src/can/bxcan/registers.rs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/embassy-stm32/src/can/bxcan/registers.rs b/embassy-stm32/src/can/bxcan/registers.rs index c5de1c683..9798a058b 100644 --- a/embassy-stm32/src/can/bxcan/registers.rs +++ b/embassy-stm32/src/can/bxcan/registers.rs @@ -2,7 +2,7 @@ use core::cmp::Ordering; use core::convert::Infallible; pub use embedded_can::{ExtendedId, Id, StandardId}; -use stm32_metapac::can::vals::Lec; +use stm32_metapac::can::vals::{Lec, Rtr}; use super::{Mailbox, TransmitStatus}; use crate::can::enums::BusError; @@ -306,6 +306,9 @@ impl Registers { mb.tir().write(|w| { w.0 = id.0; w.set_txrq(true); + if frame.header().rtr() { + w.set_rtr(Rtr::REMOTE); + } }); } From 9f1b6b47916126fba7b5968106b5fd25006fc4c2 Mon Sep 17 00:00:00 2001 From: Alexandros Liarokapis Date: Mon, 20 May 2024 16:15:41 +0300 Subject: [PATCH 086/361] Revise I2S interface to ring-buffered. --- embassy-stm32/src/i2s.rs | 337 ++++++++++++++++++++++------ embassy-stm32/src/spi/mod.rs | 13 +- examples/stm32f4/src/bin/i2s_dma.rs | 13 +- 3 files changed, 278 insertions(+), 85 deletions(-) diff --git a/embassy-stm32/src/i2s.rs b/embassy-stm32/src/i2s.rs index 094de2461..f11371f98 100644 --- a/embassy-stm32/src/i2s.rs +++ b/embassy-stm32/src/i2s.rs @@ -1,12 +1,13 @@ //! Inter-IC Sound (I2S) +use embassy_futures::join::join; use embassy_hal_internal::into_ref; +use stm32_metapac::spi::vals; -use crate::dma::ChannelAndRequest; +use crate::dma::{ringbuffer, ChannelAndRequest, ReadableRingBuffer, TransferOptions, WritableRingBuffer}; use crate::gpio::{AfType, AnyPin, OutputType, SealedPin, Speed}; use crate::mode::Async; -use crate::pac::spi::vals; -use crate::spi::{Config as SpiConfig, *}; +use crate::spi::{Config as SpiConfig, RegsExt as _, *}; use crate::time::Hertz; use crate::{Peripheral, PeripheralRef}; @@ -19,6 +20,19 @@ pub enum Mode { Slave, } +/// I2S function +#[derive(Copy, Clone)] +#[allow(dead_code)] +enum Function { + /// Transmit audio data + Transmit, + /// Receive audio data + Receive, + #[cfg(spi_v3)] + /// Transmit and Receive audio data + FullDuplex, +} + /// I2C standard #[derive(Copy, Clone)] pub enum Standard { @@ -34,6 +48,30 @@ pub enum Standard { PcmShortSync, } +/// SAI error +#[derive(Debug, PartialEq, Eq)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +pub enum Error { + /// `write` called on a SAI in receive mode. + NotATransmitter, + /// `read` called on a SAI in transmit mode. + NotAReceiver, + /// Overrun + Overrun, +} + +impl From for Error { + fn from(#[allow(unused)] err: ringbuffer::Error) -> Self { + #[cfg(feature = "defmt")] + { + if err == ringbuffer::Error::DmaUnsynced { + defmt::error!("Ringbuffer broken invariants detected!"); + } + } + Self::Overrun + } +} + impl Standard { #[cfg(any(spi_v1, spi_v3, spi_f1))] const fn i2sstd(&self) -> vals::I2sstd { @@ -142,31 +180,62 @@ impl Default for Config { } } +/// I2S driver writer. Useful for moving write functionality across tasks. +pub struct Writer<'s, 'd, W: Word>(&'s mut WritableRingBuffer<'d, W>); + +impl<'s, 'd, W: Word> Writer<'s, 'd, W> { + /// Write data to the I2S ringbuffer. + /// This appends the data to the buffer and returns immediately. The data will be transmitted in the background. + /// If thfre’s no space in the buffer, this waits until there is. + pub async fn write(&mut self, data: &[W]) -> Result<(), Error> { + self.0.write_exact(data).await?; + Ok(()) + } + + /// Reset the ring buffer to its initial state. + /// Can be used to recover from overrun. + /// The ringbuffer will always auto-reset on Overrun in any case. + pub fn reset(&mut self) { + self.0.clear(); + } +} + +/// I2S driver reader. Useful for moving read functionality across tasks. +pub struct Reader<'s, 'd, W: Word>(&'s mut ReadableRingBuffer<'d, W>); + +impl<'s, 'd, W: Word> Reader<'s, 'd, W> { + /// Read data from the I2S ringbuffer. + /// SAI is always receiving data in the background. This function pops already-received data from the buffer. + /// If there’s less than data.len() data in the buffer, this waits until there is. + pub async fn read(&mut self, data: &mut [W]) -> Result<(), Error> { + self.0.read_exact(data).await?; + Ok(()) + } + + /// Reset the ring buffer to its initial state. + /// Can be used to prevent overrun. + /// The ringbuffer will always auto-reset on Overrun in any case. + pub fn reset(&mut self) { + self.0.clear(); + } +} + /// I2S driver. -pub struct I2S<'d> { - _peri: Spi<'d, Async>, +pub struct I2S<'d, W: Word> { + #[allow(dead_code)] + mode: Mode, + spi: Spi<'d, Async>, txsd: Option>, rxsd: Option>, ws: Option>, ck: Option>, mck: Option>, + tx_ring_buffer: Option>, + rx_ring_buffer: Option>, } -/// I2S function -#[derive(Copy, Clone)] -#[allow(dead_code)] -enum Function { - /// Transmit audio data - Transmit, - /// Receive audio data - Receive, - #[cfg(spi_v3)] - /// Transmit and Receive audio data - FullDuplex, -} - -impl<'d> I2S<'d> { - /// Create a transmitter driver +impl<'d, W: Word> I2S<'d, W> { + /// Create a transmitter driver. pub fn new_txonly( peri: impl Peripheral

+ 'd, sd: impl Peripheral

> + 'd, @@ -174,18 +243,18 @@ impl<'d> I2S<'d> { ck: impl Peripheral

> + 'd, mck: impl Peripheral

> + 'd, txdma: impl Peripheral

> + 'd, + txdma_buf: &'d mut [W], freq: Hertz, config: Config, ) -> Self { - into_ref!(sd); Self::new_inner( peri, new_pin!(sd, AfType::output(OutputType::PushPull, Speed::VeryHigh)), None, ws, ck, - mck, - new_dma!(txdma), + new_pin!(mck, AfType::output(OutputType::PushPull, Speed::VeryHigh)), + new_dma!(txdma).map(|d| (d, txdma_buf)), None, freq, config, @@ -193,42 +262,61 @@ impl<'d> I2S<'d> { ) } - /// Create a receiver driver + /// Create a transmitter driver without a master clock pin. + pub fn new_txonly_nomck( + peri: impl Peripheral

+ 'd, + sd: impl Peripheral

> + 'd, + ws: impl Peripheral

> + 'd, + ck: impl Peripheral

> + 'd, + txdma: impl Peripheral

> + 'd, + txdma_buf: &'d mut [W], + freq: Hertz, + config: Config, + ) -> Self { + Self::new_inner( + peri, + new_pin!(sd, AfType::output(OutputType::PushPull, Speed::VeryHigh)), + None, + ws, + ck, + None, + new_dma!(txdma).map(|d| (d, txdma_buf)), + None, + freq, + config, + Function::Transmit, + ) + } + + /// Create a receiver driver. pub fn new_rxonly( peri: impl Peripheral

+ 'd, sd: impl Peripheral

> + 'd, ws: impl Peripheral

> + 'd, ck: impl Peripheral

> + 'd, mck: impl Peripheral

> + 'd, - #[cfg(not(spi_v3))] txdma: impl Peripheral

> + 'd, rxdma: impl Peripheral

> + 'd, + rxdma_buf: &'d mut [W], freq: Hertz, config: Config, ) -> Self { - into_ref!(sd); Self::new_inner( peri, None, new_pin!(sd, AfType::output(OutputType::PushPull, Speed::VeryHigh)), ws, ck, - mck, - #[cfg(not(spi_v3))] - new_dma!(txdma), - #[cfg(spi_v3)] + new_pin!(mck, AfType::output(OutputType::PushPull, Speed::VeryHigh)), None, - new_dma!(rxdma), + new_dma!(rxdma).map(|d| (d, rxdma_buf)), freq, config, - #[cfg(not(spi_v3))] - Function::Transmit, - #[cfg(spi_v3)] Function::Receive, ) } #[cfg(spi_v3)] - /// Create a full duplex transmitter driver + /// Create a full duplex driver. pub fn new_full_duplex( peri: impl Peripheral

+ 'd, txsd: impl Peripheral

> + 'd, @@ -237,44 +325,144 @@ impl<'d> I2S<'d> { ck: impl Peripheral

> + 'd, mck: impl Peripheral

> + 'd, txdma: impl Peripheral

> + 'd, + txdma_buf: &'d mut [W], rxdma: impl Peripheral

> + 'd, + rxdma_buf: &'d mut [W], freq: Hertz, config: Config, ) -> Self { - into_ref!(txsd, rxsd); Self::new_inner( peri, new_pin!(txsd, AfType::output(OutputType::PushPull, Speed::VeryHigh)), new_pin!(rxsd, AfType::output(OutputType::PushPull, Speed::VeryHigh)), ws, ck, - mck, - new_dma!(txdma), - new_dma!(rxdma), + new_pin!(mck, AfType::output(OutputType::PushPull, Speed::VeryHigh)), + new_dma!(txdma).map(|d| (d, txdma_buf)), + new_dma!(rxdma).map(|d| (d, rxdma_buf)), freq, config, Function::FullDuplex, ) } - /// Write audio data. - pub async fn read(&mut self, data: &mut [W]) -> Result<(), Error> { - self._peri.read(data).await + /// Start I2S driver. + pub fn start(&mut self) { + self.spi.info.regs.cr1().modify(|w| { + w.set_spe(false); + }); + self.spi.set_word_size(W::CONFIG); + if let Some(tx_ring_buffer) = &mut self.tx_ring_buffer { + tx_ring_buffer.start(); + + set_txdmaen(self.spi.info.regs, true); + } + if let Some(rx_ring_buffer) = &mut self.rx_ring_buffer { + rx_ring_buffer.start(); + // SPIv3 clears rxfifo on SPE=0 + #[cfg(not(any(spi_v3, spi_v4, spi_v5)))] + flush_rx_fifo(self.spi.info.regs); + + set_rxdmaen(self.spi.info.regs, true); + } + self.spi.info.regs.cr1().modify(|w| { + w.set_spe(true); + }); + #[cfg(any(spi_v3, spi_v4, spi_v5))] + self.spi.info.regs.cr1().modify(|w| { + w.set_cstart(true); + }); } - /// Write audio data. - pub async fn write(&mut self, data: &[W]) -> Result<(), Error> { - self._peri.write(data).await + /// Reset the ring buffer to its initial state. + /// Can be used to recover from overrun. + pub fn clear(&mut self) { + if let Some(rx_ring_buffer) = &mut self.rx_ring_buffer { + rx_ring_buffer.clear(); + } + if let Some(tx_ring_buffer) = &mut self.tx_ring_buffer { + tx_ring_buffer.clear(); + } } - /// Transfer audio data. - pub async fn transfer(&mut self, read: &mut [W], write: &[W]) -> Result<(), Error> { - self._peri.transfer(read, write).await + /// Stop I2S driver. + pub async fn stop(&mut self) { + let regs = self.spi.info.regs; + + let tx_f = async { + if let Some(tx_ring_buffer) = &mut self.tx_ring_buffer { + tx_ring_buffer.stop().await; + + set_txdmaen(regs, false); + } + }; + + let rx_f = async { + if let Some(rx_ring_buffer) = &mut self.rx_ring_buffer { + rx_ring_buffer.stop().await; + + set_rxdmaen(regs, false); + } + }; + + join(rx_f, tx_f).await; + + #[cfg(any(spi_v3, spi_v4, spi_v5))] + { + if let Mode::Master = self.mode { + regs.cr1().modify(|w| { + w.set_csusp(true); + }); + + while regs.cr1().read().cstart() {} + } + } + + regs.cr1().modify(|w| { + w.set_spe(false); + }); + + self.clear(); } - /// Transfer audio data in place. - pub async fn transfer_in_place(&mut self, data: &mut [W]) -> Result<(), Error> { - self._peri.transfer_in_place(data).await + /// Split the driver into a Reader/Writer pair. + /// Useful for splitting the reader/writer functionality across tasks or + /// for calling the read/write methods in parallel. + pub fn split<'s>(&'s mut self) -> Result<(Reader<'s, 'd, W>, Writer<'s, 'd, W>), Error> { + match (&mut self.rx_ring_buffer, &mut self.tx_ring_buffer) { + (None, _) => Err(Error::NotAReceiver), + (_, None) => Err(Error::NotATransmitter), + (Some(rx_ring), Some(tx_ring)) => Ok((Reader(rx_ring), Writer(tx_ring))), + } + } + + /// Read data from the I2S ringbuffer. + /// SAI is always receiving data in the background. This function pops already-received data from the buffer. + /// If there’s less than data.len() data in the buffer, this waits until there is. + pub async fn read(&mut self, data: &mut [W]) -> Result<(), Error> { + match &mut self.rx_ring_buffer { + Some(ring) => Reader(ring).read(data).await, + _ => Err(Error::NotAReceiver), + } + } + + /// Write data to the I2S ringbuffer. + /// This appends the data to the buffer and returns immediately. The data will be transmitted in the background. + /// If thfre’s no space in the buffer, this waits until there is. + pub async fn write(&mut self, data: &[W]) -> Result<(), Error> { + match &mut self.tx_ring_buffer { + Some(ring) => Writer(ring).write(data).await, + _ => Err(Error::NotATransmitter), + } + } + + /// Write data directly to the raw I2S ringbuffer. + /// This can be used to fill the buffer before starting the DMA transfer. + pub async fn write_immediate(&mut self, data: &mut [W]) -> Result<(usize, usize), Error> { + match &mut self.tx_ring_buffer { + Some(ring) => Ok(ring.write_immediate(data)?), + _ => return Err(Error::NotATransmitter), + } } fn new_inner( @@ -283,23 +471,23 @@ impl<'d> I2S<'d> { rxsd: Option>, ws: impl Peripheral

> + 'd, ck: impl Peripheral

> + 'd, - mck: impl Peripheral

> + 'd, - txdma: Option>, - rxdma: Option>, + mck: Option>, + txdma: Option<(ChannelAndRequest<'d>, &'d mut [W])>, + rxdma: Option<(ChannelAndRequest<'d>, &'d mut [W])>, freq: Hertz, config: Config, function: Function, ) -> Self { - into_ref!(ws, ck, mck); + into_ref!(ws, ck); ws.set_as_af(ws.af_num(), AfType::output(OutputType::PushPull, Speed::VeryHigh)); ck.set_as_af(ck.af_num(), AfType::output(OutputType::PushPull, Speed::VeryHigh)); - mck.set_as_af(mck.af_num(), AfType::output(OutputType::PushPull, Speed::VeryHigh)); - let mut spi_cfg = SpiConfig::default(); - spi_cfg.frequency = freq; - - let spi = Spi::new_internal(peri, txdma, rxdma, spi_cfg); + let spi = Spi::new_internal(peri, None, None, { + let mut config = SpiConfig::default(); + config.frequency = freq; + config + }); let regs = T::info().regs; @@ -390,22 +578,29 @@ impl<'d> I2S<'d> { w.set_i2se(true); }); - #[cfg(spi_v3)] - regs.cr1().modify(|w| w.set_spe(true)); - } + let mut opts = TransferOptions::default(); + opts.half_transfer_ir = true; - Self { - _peri: spi, - txsd: txsd.map(|w| w.map_into()), - rxsd: rxsd.map(|w| w.map_into()), - ws: Some(ws.map_into()), - ck: Some(ck.map_into()), - mck: Some(mck.map_into()), + Self { + mode: config.mode, + spi, + txsd: txsd.map(|w| w.map_into()), + rxsd: rxsd.map(|w| w.map_into()), + ws: Some(ws.map_into()), + ck: Some(ck.map_into()), + mck: mck.map(|w| w.map_into()), + tx_ring_buffer: txdma.map(|(ch, buf)| unsafe { + WritableRingBuffer::new(ch.channel, ch.request, regs.tx_ptr(), buf, opts) + }), + rx_ring_buffer: rxdma.map(|(ch, buf)| unsafe { + ReadableRingBuffer::new(ch.channel, ch.request, regs.rx_ptr(), buf, opts) + }), + } } } } -impl<'d> Drop for I2S<'d> { +impl<'d, W: Word> Drop for I2S<'d, W> { fn drop(&mut self) { self.txsd.as_ref().map(|x| x.set_as_disconnected()); self.rxsd.as_ref().map(|x| x.set_as_disconnected()); diff --git a/embassy-stm32/src/spi/mod.rs b/embassy-stm32/src/spi/mod.rs index d034c028e..a65b0cc64 100644 --- a/embassy-stm32/src/spi/mod.rs +++ b/embassy-stm32/src/spi/mod.rs @@ -311,8 +311,7 @@ impl<'d, M: PeriMode> Spi<'d, M> { } } - /// Set SPI word size. Disables SPI if needed, you have to enable it back yourself. - fn set_word_size(&mut self, word_size: word_impl::Config) { + pub(crate) fn set_word_size(&mut self, word_size: word_impl::Config) { if self.current_word_size == word_size { return; } @@ -895,7 +894,7 @@ fn compute_frequency(kernel_clock: Hertz, br: Br) -> Hertz { kernel_clock / div } -trait RegsExt { +pub(crate) trait RegsExt { fn tx_ptr(&self) -> *mut W; fn rx_ptr(&self) -> *mut W; } @@ -983,7 +982,7 @@ fn spin_until_rx_ready(regs: Regs) -> Result<(), Error> { } } -fn flush_rx_fifo(regs: Regs) { +pub(crate) fn flush_rx_fifo(regs: Regs) { #[cfg(not(any(spi_v3, spi_v4, spi_v5)))] while regs.sr().read().rxne() { #[cfg(not(spi_v2))] @@ -997,7 +996,7 @@ fn flush_rx_fifo(regs: Regs) { } } -fn set_txdmaen(regs: Regs, val: bool) { +pub(crate) fn set_txdmaen(regs: Regs, val: bool) { #[cfg(not(any(spi_v3, spi_v4, spi_v5)))] regs.cr2().modify(|reg| { reg.set_txdmaen(val); @@ -1008,7 +1007,7 @@ fn set_txdmaen(regs: Regs, val: bool) { }); } -fn set_rxdmaen(regs: Regs, val: bool) { +pub(crate) fn set_rxdmaen(regs: Regs, val: bool) { #[cfg(not(any(spi_v3, spi_v4, spi_v5)))] regs.cr2().modify(|reg| { reg.set_rxdmaen(val); @@ -1169,7 +1168,7 @@ impl<'d, W: Word> embedded_hal_async::spi::SpiBus for Spi<'d, Async> { } } -trait SealedWord { +pub(crate) trait SealedWord { const CONFIG: word_impl::Config; } diff --git a/examples/stm32f4/src/bin/i2s_dma.rs b/examples/stm32f4/src/bin/i2s_dma.rs index 27b165f1b..68392847b 100644 --- a/examples/stm32f4/src/bin/i2s_dma.rs +++ b/examples/stm32f4/src/bin/i2s_dma.rs @@ -1,13 +1,10 @@ #![no_std] #![no_main] -use core::fmt::Write; - use defmt::*; use embassy_executor::Spawner; use embassy_stm32::i2s::{Config, I2S}; use embassy_stm32::time::Hertz; -use heapless::String; use {defmt_rtt as _, panic_probe as _}; #[embassy_executor::main] @@ -15,6 +12,8 @@ async fn main(_spawner: Spawner) { let p = embassy_stm32::init(Default::default()); info!("Hello World!"); + let mut dma_buffer = [0x00_u16; 128]; + let mut i2s = I2S::new_txonly( p.SPI2, p.PC3, // sd @@ -22,13 +21,13 @@ async fn main(_spawner: Spawner) { p.PB10, // ck p.PC6, // mck p.DMA1_CH4, + &mut dma_buffer, Hertz(1_000_000), Config::default(), ); - for n in 0u32.. { - let mut write: String<128> = String::new(); - core::write!(&mut write, "Hello DMA World {}!\r\n", n).unwrap(); - i2s.write(&mut write.as_bytes()).await.ok(); + for i in 0_u16.. { + i2s.write(&mut [i * 2; 64]).await.ok(); + i2s.write(&mut [i * 2 + 1; 64]).await.ok(); } } From 9cf75d7eac11cacf545f1848d06808fc9fb745e1 Mon Sep 17 00:00:00 2001 From: Christian Enderle Date: Thu, 17 Oct 2024 19:40:27 +0200 Subject: [PATCH 087/361] stm32/flash: add support for l5 --- embassy-stm32/src/flash/l.rs | 134 ++++++++++++++++++++++++++------- embassy-stm32/src/flash/mod.rs | 7 +- 2 files changed, 110 insertions(+), 31 deletions(-) diff --git a/embassy-stm32/src/flash/l.rs b/embassy-stm32/src/flash/l.rs index a0bfeb395..ea00bf499 100644 --- a/embassy-stm32/src/flash/l.rs +++ b/embassy-stm32/src/flash/l.rs @@ -23,6 +23,9 @@ pub(crate) unsafe fn lock() { w.set_prglock(true); w.set_pelock(true); }); + + #[cfg(any(flash_l5))] + pac::FLASH.nscr().modify(|w| w.set_nslock(true)); } pub(crate) unsafe fn unlock() { @@ -46,6 +49,14 @@ pub(crate) unsafe fn unlock() { pac::FLASH.prgkeyr().write_value(0x1314_1516); } } + + #[cfg(any(flash_l5))] + { + if pac::FLASH.nscr().read().nslock() { + pac::FLASH.nskeyr().write_value(0x4567_0123); + pac::FLASH.nskeyr().write_value(0xCDEF_89AB); + } + } } pub(crate) unsafe fn enable_blocking_write() { @@ -53,11 +64,17 @@ pub(crate) unsafe fn enable_blocking_write() { #[cfg(any(flash_wl, flash_wb, flash_l4))] pac::FLASH.cr().write(|w| w.set_pg(true)); + + #[cfg(any(flash_l5))] + pac::FLASH.nscr().write(|w| w.set_nspg(true)); } pub(crate) unsafe fn disable_blocking_write() { #[cfg(any(flash_wl, flash_wb, flash_l4))] pac::FLASH.cr().write(|w| w.set_pg(false)); + + #[cfg(any(flash_l5))] + pac::FLASH.nscr().write(|w| w.set_nspg(false)); } pub(crate) unsafe fn blocking_write(start_address: u32, buf: &[u8; WRITE_SIZE]) -> Result<(), Error> { @@ -84,13 +101,25 @@ pub(crate) unsafe fn blocking_erase_sector(sector: &FlashSector) -> Result<(), E write_volatile(sector.start as *mut u32, 0xFFFFFFFF); } - #[cfg(any(flash_wl, flash_wb, flash_l4))] + #[cfg(any(flash_wl, flash_wb, flash_l4, flash_l5))] { let idx = (sector.start - super::FLASH_BASE as u32) / super::BANK1_REGION.erase_size as u32; #[cfg(flash_l4)] let (idx, bank) = if idx > 255 { (idx - 256, true) } else { (idx, false) }; + #[cfg(flash_l5)] + let (idx, bank) = if pac::FLASH.optr().read().dbank() { + if idx > 255 { + (idx - 256, Some(true)) + } else { + (idx, Some(false)) + } + } else { + (idx, None) + }; + + #[cfg(not(flash_l5))] pac::FLASH.cr().modify(|w| { w.set_per(true); w.set_pnb(idx as u8); @@ -101,6 +130,16 @@ pub(crate) unsafe fn blocking_erase_sector(sector: &FlashSector) -> Result<(), E #[cfg(any(flash_l4))] w.set_bker(bank); }); + + #[cfg(flash_l5)] + pac::FLASH.nscr().modify(|w| { + w.set_nsper(true); + w.set_nspnb(idx as u8); + if let Some(bank) = bank { + w.set_nsbker(bank); + } + w.set_nsstrt(true); + }); } let ret: Result<(), Error> = wait_ready_blocking(); @@ -108,6 +147,9 @@ pub(crate) unsafe fn blocking_erase_sector(sector: &FlashSector) -> Result<(), E #[cfg(any(flash_wl, flash_wb, flash_l4))] pac::FLASH.cr().modify(|w| w.set_per(false)); + #[cfg(any(flash_l5))] + pac::FLASH.nscr().modify(|w| w.set_nsper(false)); + #[cfg(any(flash_l0, flash_l1))] pac::FLASH.pecr().modify(|w| { w.set_erase(false); @@ -121,42 +163,78 @@ pub(crate) unsafe fn blocking_erase_sector(sector: &FlashSector) -> Result<(), E pub(crate) unsafe fn clear_all_err() { // read and write back the same value. // This clears all "write 1 to clear" bits. + #[cfg(not(flash_l5))] pac::FLASH.sr().modify(|_| {}); + + #[cfg(flash_l5)] + pac::FLASH.nssr().modify(|_| {}); } unsafe fn wait_ready_blocking() -> Result<(), Error> { loop { - let sr = pac::FLASH.sr().read(); + #[cfg(not(flash_l5))] + { + let sr = pac::FLASH.sr().read(); - if !sr.bsy() { - #[cfg(any(flash_wl, flash_wb, flash_l4))] - if sr.progerr() { - return Err(Error::Prog); + if !sr.bsy() { + #[cfg(any(flash_wl, flash_wb, flash_l4))] + if sr.progerr() { + return Err(Error::Prog); + } + + if sr.wrperr() { + return Err(Error::Protected); + } + + if sr.pgaerr() { + return Err(Error::Unaligned); + } + + if sr.sizerr() { + return Err(Error::Size); + } + + #[cfg(any(flash_wl, flash_wb, flash_l4))] + if sr.miserr() { + return Err(Error::Miss); + } + + #[cfg(any(flash_wl, flash_wb, flash_l4))] + if sr.pgserr() { + return Err(Error::Seq); + } + + return Ok(()); } + } - if sr.wrperr() { - return Err(Error::Protected); + #[cfg(flash_l5)] + { + let nssr = pac::FLASH.nssr().read(); + + if !nssr.nsbsy() { + if nssr.nsprogerr() { + return Err(Error::Prog); + } + + if nssr.nswrperr() { + return Err(Error::Protected); + } + + if nssr.nspgaerr() { + return Err(Error::Unaligned); + } + + if nssr.nssizerr() { + return Err(Error::Size); + } + + if nssr.nspgserr() { + return Err(Error::Seq); + } + + return Ok(()); } - - if sr.pgaerr() { - return Err(Error::Unaligned); - } - - if sr.sizerr() { - return Err(Error::Size); - } - - #[cfg(any(flash_wl, flash_wb, flash_l4))] - if sr.miserr() { - return Err(Error::Miss); - } - - #[cfg(any(flash_wl, flash_wb, flash_l4))] - if sr.pgserr() { - return Err(Error::Seq); - } - - return Ok(()); } } } diff --git a/embassy-stm32/src/flash/mod.rs b/embassy-stm32/src/flash/mod.rs index bce638db0..d64a1c28a 100644 --- a/embassy-stm32/src/flash/mod.rs +++ b/embassy-stm32/src/flash/mod.rs @@ -91,7 +91,7 @@ pub enum FlashBank { Bank2 = 1, } -#[cfg_attr(any(flash_l0, flash_l1, flash_l4, flash_wl, flash_wb), path = "l.rs")] +#[cfg_attr(any(flash_l0, flash_l1, flash_l4, flash_l5, flash_wl, flash_wb), path = "l.rs")] #[cfg_attr(flash_f0, path = "f0.rs")] #[cfg_attr(any(flash_f1, flash_f3), path = "f1f3.rs")] #[cfg_attr(flash_f2, path = "f2.rs")] @@ -105,8 +105,9 @@ pub enum FlashBank { #[cfg_attr(flash_u0, path = "u0.rs")] #[cfg_attr( not(any( - flash_l0, flash_l1, flash_l4, flash_wl, flash_wb, flash_f0, flash_f1, flash_f2, flash_f3, flash_f4, flash_f7, - flash_g0, flash_g4c2, flash_g4c3, flash_g4c4, flash_h7, flash_h7ab, flash_u5, flash_h50, flash_u0 + flash_l0, flash_l1, flash_l4, flash_l5, flash_wl, flash_wb, flash_f0, flash_f1, flash_f2, flash_f3, flash_f4, + flash_f7, flash_g0, flash_g0, flash_g4c2, flash_g4c3, flash_g4c4, flash_h7, flash_h7ab, flash_u5, flash_h50, + flash_u0 )), path = "other.rs" )] From dcdc0638b6fbec5b68055a5592d71458f83da875 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Fri, 18 Oct 2024 00:31:42 +0200 Subject: [PATCH 088/361] Update to rust 1.82. --- rust-toolchain.toml | 2 +- tests/nrf/src/bin/buffered_uart_spam.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/rust-toolchain.toml b/rust-toolchain.toml index 80acb3b5e..69a964040 100644 --- a/rust-toolchain.toml +++ b/rust-toolchain.toml @@ -1,5 +1,5 @@ [toolchain] -channel = "1.81" +channel = "1.82" components = [ "rust-src", "rustfmt", "llvm-tools" ] targets = [ "thumbv7em-none-eabi", diff --git a/tests/nrf/src/bin/buffered_uart_spam.rs b/tests/nrf/src/bin/buffered_uart_spam.rs index 843313537..45daaae0c 100644 --- a/tests/nrf/src/bin/buffered_uart_spam.rs +++ b/tests/nrf/src/bin/buffered_uart_spam.rs @@ -55,7 +55,7 @@ async fn main(_spawner: Spawner) { let task = unsafe { Task::new_unchecked(NonNull::new_unchecked(&spam_peri.tasks_starttx as *const _ as _)) }; let mut spam_ppi = Ppi::new_one_to_one(p.PPI_CH2, event, task); spam_ppi.enable(); - let p = unsafe { core::ptr::addr_of_mut!(TX_BUF) } as *mut u8; + let p = (&raw mut TX_BUF) as *mut u8; spam_peri.txd.ptr.write(|w| unsafe { w.ptr().bits(p as u32) }); spam_peri.txd.maxcnt.write(|w| unsafe { w.maxcnt().bits(NSPAM as _) }); spam_peri.tasks_starttx.write(|w| unsafe { w.bits(1) }); From 1f58e0efd05e5c96409db301e4d481098038aedf Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Fri, 18 Oct 2024 03:18:59 +0200 Subject: [PATCH 089/361] executor: fix unsoundness due to `impl Trait`, improve macro error handling. (#3425) * executor-macros: don't parse function bodies. * executor-macros: refactor for better recovery and ide-friendliness on errors. * executor-macros: disallow `impl Trait` in task arguments. Fixes #3420 * Fix example using `impl Trait` in tasks. --- embassy-executor-macros/Cargo.toml | 2 +- embassy-executor-macros/src/lib.rs | 50 +---- embassy-executor-macros/src/macros/main.rs | 240 ++++++++++----------- embassy-executor-macros/src/macros/task.rs | 155 ++++++++----- embassy-executor-macros/src/util.rs | 74 +++++++ embassy-executor-macros/src/util/ctxt.rs | 72 ------- embassy-executor-macros/src/util/mod.rs | 1 - examples/stm32h7/src/bin/i2c_shared.rs | 6 +- 8 files changed, 309 insertions(+), 291 deletions(-) create mode 100644 embassy-executor-macros/src/util.rs delete mode 100644 embassy-executor-macros/src/util/ctxt.rs delete mode 100644 embassy-executor-macros/src/util/mod.rs diff --git a/embassy-executor-macros/Cargo.toml b/embassy-executor-macros/Cargo.toml index 218e820ce..ef509c3f9 100644 --- a/embassy-executor-macros/Cargo.toml +++ b/embassy-executor-macros/Cargo.toml @@ -13,7 +13,7 @@ categories = [ ] [dependencies] -syn = { version = "2.0.15", features = ["full", "extra-traits"] } +syn = { version = "2.0.15", features = ["full", "visit"] } quote = "1.0.9" darling = "0.20.1" proc-macro2 = "1.0.29" diff --git a/embassy-executor-macros/src/lib.rs b/embassy-executor-macros/src/lib.rs index 61d388b9e..5f2182f10 100644 --- a/embassy-executor-macros/src/lib.rs +++ b/embassy-executor-macros/src/lib.rs @@ -1,28 +1,11 @@ #![doc = include_str!("../README.md")] extern crate proc_macro; -use darling::ast::NestedMeta; use proc_macro::TokenStream; mod macros; mod util; use macros::*; -use syn::parse::{Parse, ParseBuffer}; -use syn::punctuated::Punctuated; -use syn::Token; - -struct Args { - meta: Vec, -} - -impl Parse for Args { - fn parse(input: &ParseBuffer) -> syn::Result { - let meta = Punctuated::::parse_terminated(input)?; - Ok(Args { - meta: meta.into_iter().collect(), - }) - } -} /// Declares an async task that can be run by `embassy-executor`. The optional `pool_size` parameter can be used to specify how /// many concurrent tasks can be spawned (default is 1) for the function. @@ -56,17 +39,12 @@ impl Parse for Args { /// ``` #[proc_macro_attribute] pub fn task(args: TokenStream, item: TokenStream) -> TokenStream { - let args = syn::parse_macro_input!(args as Args); - let f = syn::parse_macro_input!(item as syn::ItemFn); - - task::run(&args.meta, f).unwrap_or_else(|x| x).into() + task::run(args.into(), item.into()).into() } #[proc_macro_attribute] pub fn main_avr(args: TokenStream, item: TokenStream) -> TokenStream { - let args = syn::parse_macro_input!(args as Args); - let f = syn::parse_macro_input!(item as syn::ItemFn); - main::run(&args.meta, f, main::avr()).unwrap_or_else(|x| x).into() + main::run(args.into(), item.into(), &main::ARCH_AVR).into() } /// Creates a new `executor` instance and declares an application entry point for Cortex-M spawning the corresponding function body as an async task. @@ -89,9 +67,7 @@ pub fn main_avr(args: TokenStream, item: TokenStream) -> TokenStream { /// ``` #[proc_macro_attribute] pub fn main_cortex_m(args: TokenStream, item: TokenStream) -> TokenStream { - let args = syn::parse_macro_input!(args as Args); - let f = syn::parse_macro_input!(item as syn::ItemFn); - main::run(&args.meta, f, main::cortex_m()).unwrap_or_else(|x| x).into() + main::run(args.into(), item.into(), &main::ARCH_CORTEX_M).into() } /// Creates a new `executor` instance and declares an architecture agnostic application entry point spawning @@ -116,11 +92,7 @@ pub fn main_cortex_m(args: TokenStream, item: TokenStream) -> TokenStream { /// ``` #[proc_macro_attribute] pub fn main_spin(args: TokenStream, item: TokenStream) -> TokenStream { - let args = syn::parse_macro_input!(args as Args); - let f = syn::parse_macro_input!(item as syn::ItemFn); - main::run(&args.meta, f, main::spin(&args.meta)) - .unwrap_or_else(|x| x) - .into() + main::run(args.into(), item.into(), &main::ARCH_SPIN).into() } /// Creates a new `executor` instance and declares an application entry point for RISC-V spawning the corresponding function body as an async task. @@ -153,11 +125,7 @@ pub fn main_spin(args: TokenStream, item: TokenStream) -> TokenStream { /// ``` #[proc_macro_attribute] pub fn main_riscv(args: TokenStream, item: TokenStream) -> TokenStream { - let args = syn::parse_macro_input!(args as Args); - let f = syn::parse_macro_input!(item as syn::ItemFn); - main::run(&args.meta, f, main::riscv(&args.meta)) - .unwrap_or_else(|x| x) - .into() + main::run(args.into(), item.into(), &main::ARCH_RISCV).into() } /// Creates a new `executor` instance and declares an application entry point for STD spawning the corresponding function body as an async task. @@ -180,9 +148,7 @@ pub fn main_riscv(args: TokenStream, item: TokenStream) -> TokenStream { /// ``` #[proc_macro_attribute] pub fn main_std(args: TokenStream, item: TokenStream) -> TokenStream { - let args = syn::parse_macro_input!(args as Args); - let f = syn::parse_macro_input!(item as syn::ItemFn); - main::run(&args.meta, f, main::std()).unwrap_or_else(|x| x).into() + main::run(args.into(), item.into(), &main::ARCH_STD).into() } /// Creates a new `executor` instance and declares an application entry point for WASM spawning the corresponding function body as an async task. @@ -205,7 +171,5 @@ pub fn main_std(args: TokenStream, item: TokenStream) -> TokenStream { /// ``` #[proc_macro_attribute] pub fn main_wasm(args: TokenStream, item: TokenStream) -> TokenStream { - let args = syn::parse_macro_input!(args as Args); - let f = syn::parse_macro_input!(item as syn::ItemFn); - main::run(&args.meta, f, main::wasm()).unwrap_or_else(|x| x).into() + main::run(args.into(), item.into(), &main::ARCH_WASM).into() } diff --git a/embassy-executor-macros/src/macros/main.rs b/embassy-executor-macros/src/macros/main.rs index 66a3965d0..14b1d9de2 100644 --- a/embassy-executor-macros/src/macros/main.rs +++ b/embassy-executor-macros/src/macros/main.rs @@ -1,152 +1,107 @@ +use std::str::FromStr; + use darling::export::NestedMeta; -use darling::{Error, FromMeta}; +use darling::FromMeta; use proc_macro2::TokenStream; use quote::quote; -use syn::{Expr, ReturnType, Type}; +use syn::{ReturnType, Type}; -use crate::util::ctxt::Ctxt; +use crate::util::*; -#[derive(Debug, FromMeta)] +enum Flavor { + Standard, + Wasm, +} + +pub(crate) struct Arch { + default_entry: Option<&'static str>, + flavor: Flavor, +} + +pub static ARCH_AVR: Arch = Arch { + default_entry: Some("avr_device::entry"), + flavor: Flavor::Standard, +}; + +pub static ARCH_RISCV: Arch = Arch { + default_entry: Some("riscv_rt::entry"), + flavor: Flavor::Standard, +}; + +pub static ARCH_CORTEX_M: Arch = Arch { + default_entry: Some("cortex_m_rt::entry"), + flavor: Flavor::Standard, +}; + +pub static ARCH_SPIN: Arch = Arch { + default_entry: None, + flavor: Flavor::Standard, +}; + +pub static ARCH_STD: Arch = Arch { + default_entry: None, + flavor: Flavor::Standard, +}; + +pub static ARCH_WASM: Arch = Arch { + default_entry: Some("wasm_bindgen::prelude::wasm_bindgen(start)"), + flavor: Flavor::Wasm, +}; + +#[derive(Debug, FromMeta, Default)] struct Args { #[darling(default)] entry: Option, } -pub fn avr() -> TokenStream { - quote! { - #[avr_device::entry] - fn main() -> ! { - let mut executor = ::embassy_executor::Executor::new(); - let executor = unsafe { __make_static(&mut executor) }; +pub fn run(args: TokenStream, item: TokenStream, arch: &Arch) -> TokenStream { + let mut errors = TokenStream::new(); - executor.run(|spawner| { - spawner.must_spawn(__embassy_main(spawner)); - }) - } - } -} - -pub fn riscv(args: &[NestedMeta]) -> TokenStream { - let maybe_entry = match Args::from_list(args) { - Ok(args) => args.entry, - Err(e) => return e.write_errors(), + // If any of the steps for this macro fail, we still want to expand to an item that is as close + // to the expected output as possible. This helps out IDEs such that completions and other + // related features keep working. + let f: ItemFn = match syn::parse2(item.clone()) { + Ok(x) => x, + Err(e) => return token_stream_with_error(item, e), }; - let entry = maybe_entry.unwrap_or("riscv_rt::entry".into()); - let entry = match Expr::from_string(&entry) { - Ok(expr) => expr, - Err(e) => return e.write_errors(), + let args = match NestedMeta::parse_meta_list(args) { + Ok(x) => x, + Err(e) => return token_stream_with_error(item, e), }; - quote! { - #[#entry] - fn main() -> ! { - let mut executor = ::embassy_executor::Executor::new(); - let executor = unsafe { __make_static(&mut executor) }; - executor.run(|spawner| { - spawner.must_spawn(__embassy_main(spawner)); - }) + let args = match Args::from_list(&args) { + Ok(x) => x, + Err(e) => { + errors.extend(e.write_errors()); + Args::default() } - } -} - -pub fn spin(args: &[NestedMeta]) -> TokenStream { - let maybe_entry = match Args::from_list(args) { - Ok(args) => args.entry, - Err(e) => return e.write_errors(), }; - let entry = match maybe_entry { - Some(str) => str, - None => return Error::missing_field("entry").write_errors(), - }; - let entry = match Expr::from_string(&entry) { - Ok(expr) => expr, - Err(e) => return e.write_errors(), - }; - - quote! { - #[#entry] - fn main() -> ! { - let mut executor = ::embassy_executor::Executor::new(); - let executor = unsafe { __make_static(&mut executor) }; - executor.run(|spawner| { - spawner.must_spawn(__embassy_main(spawner)); - }) - } - } -} - -pub fn cortex_m() -> TokenStream { - quote! { - #[cortex_m_rt::entry] - fn main() -> ! { - let mut executor = ::embassy_executor::Executor::new(); - let executor = unsafe { __make_static(&mut executor) }; - executor.run(|spawner| { - spawner.must_spawn(__embassy_main(spawner)); - }) - } - } -} - -pub fn wasm() -> TokenStream { - quote! { - #[wasm_bindgen::prelude::wasm_bindgen(start)] - pub fn main() -> Result<(), wasm_bindgen::JsValue> { - let executor = ::std::boxed::Box::leak(::std::boxed::Box::new(::embassy_executor::Executor::new())); - - executor.start(|spawner| { - spawner.must_spawn(__embassy_main(spawner)); - }); - - Ok(()) - } - } -} - -pub fn std() -> TokenStream { - quote! { - fn main() -> ! { - let mut executor = ::embassy_executor::Executor::new(); - let executor = unsafe { __make_static(&mut executor) }; - - executor.run(|spawner| { - spawner.must_spawn(__embassy_main(spawner)); - }) - } - } -} - -pub fn run(args: &[NestedMeta], f: syn::ItemFn, main: TokenStream) -> Result { - #[allow(unused_variables)] - let args = Args::from_list(args).map_err(|e| e.write_errors())?; - let fargs = f.sig.inputs.clone(); - let ctxt = Ctxt::new(); - if f.sig.asyncness.is_none() { - ctxt.error_spanned_by(&f.sig, "main function must be async"); + error(&mut errors, &f.sig, "main function must be async"); } if !f.sig.generics.params.is_empty() { - ctxt.error_spanned_by(&f.sig, "main function must not be generic"); + error(&mut errors, &f.sig, "main function must not be generic"); } if !f.sig.generics.where_clause.is_none() { - ctxt.error_spanned_by(&f.sig, "main function must not have `where` clauses"); + error(&mut errors, &f.sig, "main function must not have `where` clauses"); } if !f.sig.abi.is_none() { - ctxt.error_spanned_by(&f.sig, "main function must not have an ABI qualifier"); + error(&mut errors, &f.sig, "main function must not have an ABI qualifier"); } if !f.sig.variadic.is_none() { - ctxt.error_spanned_by(&f.sig, "main function must not be variadic"); + error(&mut errors, &f.sig, "main function must not be variadic"); } match &f.sig.output { ReturnType::Default => {} ReturnType::Type(_, ty) => match &**ty { Type::Tuple(tuple) if tuple.elems.is_empty() => {} Type::Never(_) => {} - _ => ctxt.error_spanned_by( + _ => error( + &mut errors, &f.sig, "main function must either not return a value, return `()` or return `!`", ), @@ -154,26 +109,69 @@ pub fn run(args: &[NestedMeta], f: syn::ItemFn, main: TokenStream) -> Result TokenStream::new(), + Some(x) => match TokenStream::from_str(x) { + Ok(x) => quote!(#[#x]), + Err(e) => { + error(&mut errors, &f.sig, e); + TokenStream::new() + } + }, + }; - let f_body = f.block; + let f_body = f.body; let out = &f.sig.output; + let (main_ret, mut main_body) = match arch.flavor { + Flavor::Standard => ( + quote!(!), + quote! { + unsafe fn __make_static(t: &mut T) -> &'static mut T { + ::core::mem::transmute(t) + } + + let mut executor = ::embassy_executor::Executor::new(); + let executor = unsafe { __make_static(&mut executor) }; + executor.run(|spawner| { + spawner.must_spawn(__embassy_main(spawner)); + }) + }, + ), + Flavor::Wasm => ( + quote!(Result<(), wasm_bindgen::JsValue>), + quote! { + let executor = ::std::boxed::Box::leak(::std::boxed::Box::new(::embassy_executor::Executor::new())); + + executor.start(|spawner| { + spawner.must_spawn(__embassy_main(spawner)); + }); + + Ok(()) + }, + ), + }; + + if !errors.is_empty() { + main_body = quote! {loop{}}; + } + let result = quote! { #[::embassy_executor::task()] async fn __embassy_main(#fargs) #out { #f_body } - unsafe fn __make_static(t: &mut T) -> &'static mut T { - ::core::mem::transmute(t) + #entry + fn main() -> #main_ret { + #main_body } - #main + #errors }; - Ok(result) + result } diff --git a/embassy-executor-macros/src/macros/task.rs b/embassy-executor-macros/src/macros/task.rs index 96c6267b2..0404dba64 100644 --- a/embassy-executor-macros/src/macros/task.rs +++ b/embassy-executor-macros/src/macros/task.rs @@ -2,47 +2,68 @@ use darling::export::NestedMeta; use darling::FromMeta; use proc_macro2::{Span, TokenStream}; use quote::{format_ident, quote}; -use syn::{parse_quote, Expr, ExprLit, ItemFn, Lit, LitInt, ReturnType, Type}; +use syn::visit::Visit; +use syn::{Expr, ExprLit, Lit, LitInt, ReturnType, Type}; -use crate::util::ctxt::Ctxt; +use crate::util::*; -#[derive(Debug, FromMeta)] +#[derive(Debug, FromMeta, Default)] struct Args { #[darling(default)] pool_size: Option, } -pub fn run(args: &[NestedMeta], f: syn::ItemFn) -> Result { - let args = Args::from_list(args).map_err(|e| e.write_errors())?; +pub fn run(args: TokenStream, item: TokenStream) -> TokenStream { + let mut errors = TokenStream::new(); + + // If any of the steps for this macro fail, we still want to expand to an item that is as close + // to the expected output as possible. This helps out IDEs such that completions and other + // related features keep working. + let f: ItemFn = match syn::parse2(item.clone()) { + Ok(x) => x, + Err(e) => return token_stream_with_error(item, e), + }; + + let args = match NestedMeta::parse_meta_list(args) { + Ok(x) => x, + Err(e) => return token_stream_with_error(item, e), + }; + + let args = match Args::from_list(&args) { + Ok(x) => x, + Err(e) => { + errors.extend(e.write_errors()); + Args::default() + } + }; let pool_size = args.pool_size.unwrap_or(Expr::Lit(ExprLit { attrs: vec![], lit: Lit::Int(LitInt::new("1", Span::call_site())), })); - let ctxt = Ctxt::new(); - if f.sig.asyncness.is_none() { - ctxt.error_spanned_by(&f.sig, "task functions must be async"); + error(&mut errors, &f.sig, "task functions must be async"); } if !f.sig.generics.params.is_empty() { - ctxt.error_spanned_by(&f.sig, "task functions must not be generic"); + error(&mut errors, &f.sig, "task functions must not be generic"); } if !f.sig.generics.where_clause.is_none() { - ctxt.error_spanned_by(&f.sig, "task functions must not have `where` clauses"); + error(&mut errors, &f.sig, "task functions must not have `where` clauses"); } if !f.sig.abi.is_none() { - ctxt.error_spanned_by(&f.sig, "task functions must not have an ABI qualifier"); + error(&mut errors, &f.sig, "task functions must not have an ABI qualifier"); } if !f.sig.variadic.is_none() { - ctxt.error_spanned_by(&f.sig, "task functions must not be variadic"); + error(&mut errors, &f.sig, "task functions must not be variadic"); } match &f.sig.output { ReturnType::Default => {} ReturnType::Type(_, ty) => match &**ty { Type::Tuple(tuple) if tuple.elems.is_empty() => {} Type::Never(_) => {} - _ => ctxt.error_spanned_by( + _ => error( + &mut errors, &f.sig, "task functions must either not return a value, return `()` or return `!`", ), @@ -55,26 +76,31 @@ pub fn run(args: &[NestedMeta], f: syn::ItemFn) -> Result { - ctxt.error_spanned_by(arg, "task functions must not have receiver arguments"); + error(&mut errors, arg, "task functions must not have receiver arguments"); } - syn::FnArg::Typed(t) => match t.pat.as_mut() { - syn::Pat::Ident(id) => { - id.mutability = None; - args.push((id.clone(), t.attrs.clone())); + syn::FnArg::Typed(t) => { + check_arg_ty(&mut errors, &t.ty); + match t.pat.as_mut() { + syn::Pat::Ident(id) => { + id.mutability = None; + args.push((id.clone(), t.attrs.clone())); + } + _ => { + error( + &mut errors, + arg, + "pattern matching in task arguments is not yet supported", + ); + } } - _ => { - ctxt.error_spanned_by(arg, "pattern matching in task arguments is not yet supported"); - } - }, + } } } - ctxt.check()?; - let task_ident = f.sig.ident.clone(); let task_inner_ident = format_ident!("__{}_task", task_ident); - let mut task_inner = f; + let mut task_inner = f.clone(); let visibility = task_inner.vis.clone(); task_inner.vis = syn::Visibility::Inherited; task_inner.sig.ident = task_inner_ident.clone(); @@ -91,35 +117,43 @@ pub fn run(args: &[NestedMeta], f: syn::ItemFn) -> Result ::embassy_executor::SpawnToken { - trait _EmbassyInternalTaskTrait { - type Fut: ::core::future::Future + 'static; - fn construct(#fargs) -> Self::Fut; - } - - impl _EmbassyInternalTaskTrait for () { - 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<<() as _EmbassyInternalTaskTrait>::Fut, POOL_SIZE> = ::embassy_executor::raw::TaskPool::new(); - unsafe { POOL._spawn_async_fn(move || <() as _EmbassyInternalTaskTrait>::construct(#(#full_args,)*)) } + let mut task_outer_body = quote! { + trait _EmbassyInternalTaskTrait { + type Fut: ::core::future::Future + 'static; + fn construct(#fargs) -> Self::Fut; } + + impl _EmbassyInternalTaskTrait for () { + 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<<() as _EmbassyInternalTaskTrait>::Fut, POOL_SIZE> = ::embassy_executor::raw::TaskPool::new(); + unsafe { POOL._spawn_async_fn(move || <() as _EmbassyInternalTaskTrait>::construct(#(#full_args,)*)) } }; #[cfg(not(feature = "nightly"))] - let mut task_outer: ItemFn = parse_quote! { - #visibility fn #task_ident(#fargs) -> ::embassy_executor::SpawnToken { - const POOL_SIZE: usize = #pool_size; - static POOL: ::embassy_executor::_export::TaskPoolRef = ::embassy_executor::_export::TaskPoolRef::new(); - unsafe { POOL.get::<_, POOL_SIZE>()._spawn_async_fn(move || #task_inner_ident(#(#full_args,)*)) } - } + let mut task_outer_body = quote! { + const POOL_SIZE: usize = #pool_size; + static POOL: ::embassy_executor::_export::TaskPoolRef = ::embassy_executor::_export::TaskPoolRef::new(); + unsafe { POOL.get::<_, POOL_SIZE>()._spawn_async_fn(move || #task_inner_ident(#(#full_args,)*)) } }; - task_outer.attrs.append(&mut task_inner.attrs.clone()); + let task_outer_attrs = task_inner.attrs.clone(); + + if !errors.is_empty() { + task_outer_body = quote! { + #![allow(unused_variables, unreachable_code)] + let _x: ::embassy_executor::SpawnToken<()> = ::core::todo!(); + _x + }; + } + + // Copy the generics + where clause to avoid more spurious errors. + let generics = &f.sig.generics; + let where_clause = &f.sig.generics.where_clause; let result = quote! { // This is the user's task function, renamed. @@ -129,8 +163,27 @@ pub fn run(args: &[NestedMeta], f: syn::ItemFn) -> Result ::embassy_executor::SpawnToken #where_clause{ + #task_outer_body + } + + #errors }; - Ok(result) + result +} + +fn check_arg_ty(errors: &mut TokenStream, ty: &Type) { + struct Visitor<'a> { + errors: &'a mut TokenStream, + } + + impl<'a, 'ast> Visit<'ast> for Visitor<'a> { + fn visit_type_impl_trait(&mut self, i: &'ast syn::TypeImplTrait) { + error(self.errors, i, "`impl Trait` is not allowed in task arguments. It is syntax sugar for generics, and tasks can't be generic."); + } + } + + Visit::visit_type(&mut Visitor { errors }, ty); } diff --git a/embassy-executor-macros/src/util.rs b/embassy-executor-macros/src/util.rs new file mode 100644 index 000000000..ebd032a62 --- /dev/null +++ b/embassy-executor-macros/src/util.rs @@ -0,0 +1,74 @@ +use std::fmt::Display; + +use proc_macro2::{TokenStream, TokenTree}; +use quote::{ToTokens, TokenStreamExt}; +use syn::parse::{Parse, ParseStream}; +use syn::{braced, bracketed, token, AttrStyle, Attribute, Signature, Token, Visibility}; + +pub fn token_stream_with_error(mut tokens: TokenStream, error: syn::Error) -> TokenStream { + tokens.extend(error.into_compile_error()); + tokens +} + +pub fn error(s: &mut TokenStream, obj: A, msg: T) { + s.extend(syn::Error::new_spanned(obj.into_token_stream(), msg).into_compile_error()) +} + +/// Function signature and body. +/// +/// Same as `syn`'s `ItemFn` except we keep the body as a TokenStream instead of +/// parsing it. This makes the macro not error if there's a syntax error in the body, +/// which helps IDE autocomplete work better. +#[derive(Debug, Clone)] +pub struct ItemFn { + pub attrs: Vec, + pub vis: Visibility, + pub sig: Signature, + pub brace_token: token::Brace, + pub body: TokenStream, +} + +impl Parse for ItemFn { + fn parse(input: ParseStream) -> syn::Result { + let mut attrs = input.call(Attribute::parse_outer)?; + let vis: Visibility = input.parse()?; + let sig: Signature = input.parse()?; + + let content; + let brace_token = braced!(content in input); + while content.peek(Token![#]) && content.peek2(Token![!]) { + let content2; + attrs.push(Attribute { + pound_token: content.parse()?, + style: AttrStyle::Inner(content.parse()?), + bracket_token: bracketed!(content2 in content), + meta: content2.parse()?, + }); + } + + let mut body = Vec::new(); + while !content.is_empty() { + body.push(content.parse::()?); + } + let body = body.into_iter().collect(); + + Ok(ItemFn { + attrs, + vis, + sig, + brace_token, + body, + }) + } +} + +impl ToTokens for ItemFn { + fn to_tokens(&self, tokens: &mut TokenStream) { + tokens.append_all(self.attrs.iter().filter(|a| matches!(a.style, AttrStyle::Outer))); + self.vis.to_tokens(tokens); + self.sig.to_tokens(tokens); + self.brace_token.surround(tokens, |tokens| { + tokens.append_all(self.body.clone()); + }); + } +} diff --git a/embassy-executor-macros/src/util/ctxt.rs b/embassy-executor-macros/src/util/ctxt.rs deleted file mode 100644 index 9c78cda01..000000000 --- a/embassy-executor-macros/src/util/ctxt.rs +++ /dev/null @@ -1,72 +0,0 @@ -// nifty utility borrowed from serde :) -// https://github.com/serde-rs/serde/blob/master/serde_derive/src/internals/ctxt.rs - -use std::cell::RefCell; -use std::fmt::Display; -use std::thread; - -use proc_macro2::TokenStream; -use quote::{quote, ToTokens}; - -/// A type to collect errors together and format them. -/// -/// Dropping this object will cause a panic. It must be consumed using `check`. -/// -/// References can be shared since this type uses run-time exclusive mut checking. -#[derive(Default)] -pub struct Ctxt { - // The contents will be set to `None` during checking. This is so that checking can be - // enforced. - errors: RefCell>>, -} - -impl Ctxt { - /// Create a new context object. - /// - /// This object contains no errors, but will still trigger a panic if it is not `check`ed. - pub fn new() -> Self { - Ctxt { - errors: RefCell::new(Some(Vec::new())), - } - } - - /// Add an error to the context object with a tokenenizable object. - /// - /// The object is used for spanning in error messages. - pub fn error_spanned_by(&self, obj: A, msg: T) { - self.errors - .borrow_mut() - .as_mut() - .unwrap() - // Curb monomorphization from generating too many identical methods. - .push(syn::Error::new_spanned(obj.into_token_stream(), msg)); - } - - /// Add one of Syn's parse errors. - #[allow(unused)] - pub fn syn_error(&self, err: syn::Error) { - self.errors.borrow_mut().as_mut().unwrap().push(err); - } - - /// Consume this object, producing a formatted error string if there are errors. - pub fn check(self) -> Result<(), TokenStream> { - let errors = self.errors.borrow_mut().take().unwrap(); - match errors.len() { - 0 => Ok(()), - _ => Err(to_compile_errors(errors)), - } - } -} - -fn to_compile_errors(errors: Vec) -> proc_macro2::TokenStream { - let compile_errors = errors.iter().map(syn::Error::to_compile_error); - quote!(#(#compile_errors)*) -} - -impl Drop for Ctxt { - fn drop(&mut self) { - if !thread::panicking() && self.errors.borrow().is_some() { - panic!("forgot to check for errors"); - } - } -} diff --git a/embassy-executor-macros/src/util/mod.rs b/embassy-executor-macros/src/util/mod.rs deleted file mode 100644 index 28702809e..000000000 --- a/embassy-executor-macros/src/util/mod.rs +++ /dev/null @@ -1 +0,0 @@ -pub mod ctxt; diff --git a/examples/stm32h7/src/bin/i2c_shared.rs b/examples/stm32h7/src/bin/i2c_shared.rs index 6f4815582..136b91eeb 100644 --- a/examples/stm32h7/src/bin/i2c_shared.rs +++ b/examples/stm32h7/src/bin/i2c_shared.rs @@ -10,8 +10,10 @@ use embassy_stm32::i2c::{self, I2c}; use embassy_stm32::mode::Async; use embassy_stm32::time::Hertz; use embassy_stm32::{bind_interrupts, peripherals}; +use embassy_sync::blocking_mutex::raw::NoopRawMutex; use embassy_sync::blocking_mutex::NoopMutex; use embassy_time::{Duration, Timer}; +use embedded_hal_1::i2c::I2c as _; use static_cell::StaticCell; use {defmt_rtt as _, panic_probe as _}; @@ -31,7 +33,7 @@ bind_interrupts!(struct Irqs { }); #[embassy_executor::task] -async fn temperature(mut i2c: impl embedded_hal_1::i2c::I2c + 'static) { +async fn temperature(mut i2c: I2cDevice<'static, NoopRawMutex, I2c<'static, Async>>) { let mut data = [0u8; 2]; loop { @@ -48,7 +50,7 @@ async fn temperature(mut i2c: impl embedded_hal_1::i2c::I2c + 'static) { } #[embassy_executor::task] -async fn humidity(mut i2c: impl embedded_hal_1::i2c::I2c + 'static) { +async fn humidity(mut i2c: I2cDevice<'static, NoopRawMutex, I2c<'static, Async>>) { let mut data = [0u8; 6]; loop { From f21b19164be17df930aa557a90b3c5ab1dc39b9e Mon Sep 17 00:00:00 2001 From: Christian Enderle Date: Sat, 19 Oct 2024 15:36:56 +0200 Subject: [PATCH 090/361] embassy-futures: add select 5 and 6 --- embassy-futures/src/select.rs | 163 ++++++++++++++++++++++++++++++++++ 1 file changed, 163 insertions(+) diff --git a/embassy-futures/src/select.rs b/embassy-futures/src/select.rs index 57f0cb41f..bb175253b 100644 --- a/embassy-futures/src/select.rs +++ b/embassy-futures/src/select.rs @@ -188,6 +188,169 @@ where // ==================================================================== +/// Result for [`select5`]. +#[derive(Debug, Clone)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +pub enum Either5 { + /// First future finished first. + First(A), + /// Second future finished first. + Second(B), + /// Third future finished first. + Third(C), + /// Fourth future finished first. + Fourth(D), + /// Fifth future finished first. + Fifth(E), +} + +/// Same as [`select`], but with more futures. +pub fn select5(a: A, b: B, c: C, d: D, e: E) -> Select5 +where + A: Future, + B: Future, + C: Future, + D: Future, + E: Future, +{ + Select5 { a, b, c, d, e } +} + +/// Future for the [`select5`] function. +#[derive(Debug)] +#[must_use = "futures do nothing unless you `.await` or poll them"] +pub struct Select5 { + a: A, + b: B, + c: C, + d: D, + e: E, +} + +impl Future for Select5 +where + A: Future, + B: Future, + C: Future, + D: Future, + E: Future, +{ + type Output = Either5; + + fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { + let this = unsafe { self.get_unchecked_mut() }; + let a = unsafe { Pin::new_unchecked(&mut this.a) }; + let b = unsafe { Pin::new_unchecked(&mut this.b) }; + let c = unsafe { Pin::new_unchecked(&mut this.c) }; + let d = unsafe { Pin::new_unchecked(&mut this.d) }; + let e = unsafe { Pin::new_unchecked(&mut this.e) }; + if let Poll::Ready(x) = a.poll(cx) { + return Poll::Ready(Either5::First(x)); + } + if let Poll::Ready(x) = b.poll(cx) { + return Poll::Ready(Either5::Second(x)); + } + if let Poll::Ready(x) = c.poll(cx) { + return Poll::Ready(Either5::Third(x)); + } + if let Poll::Ready(x) = d.poll(cx) { + return Poll::Ready(Either5::Fourth(x)); + } + if let Poll::Ready(x) = e.poll(cx) { + return Poll::Ready(Either5::Fifth(x)); + } + Poll::Pending + } +} + +// ==================================================================== + +/// Result for [`select6`]. +#[derive(Debug, Clone)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +pub enum Either6 { + /// First future finished first. + First(A), + /// Second future finished first. + Second(B), + /// Third future finished first. + Third(C), + /// Fourth future finished first. + Fourth(D), + /// Fifth future finished first. + Fifth(E), + /// Sixth future finished first. + Sixth(F), +} + +/// Same as [`select`], but with more futures. +pub fn select6(a: A, b: B, c: C, d: D, e: E, f: F) -> Select6 +where + A: Future, + B: Future, + C: Future, + D: Future, + E: Future, + F: Future, +{ + Select6 { a, b, c, d, e, f } +} + +/// Future for the [`select6`] function. +#[derive(Debug)] +#[must_use = "futures do nothing unless you `.await` or poll them"] +pub struct Select6 { + a: A, + b: B, + c: C, + d: D, + e: E, + f: F, +} + +impl Future for Select6 +where + A: Future, + B: Future, + C: Future, + D: Future, + E: Future, + F: Future, +{ + type Output = Either6; + + fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { + let this = unsafe { self.get_unchecked_mut() }; + let a = unsafe { Pin::new_unchecked(&mut this.a) }; + let b = unsafe { Pin::new_unchecked(&mut this.b) }; + let c = unsafe { Pin::new_unchecked(&mut this.c) }; + let d = unsafe { Pin::new_unchecked(&mut this.d) }; + let e = unsafe { Pin::new_unchecked(&mut this.e) }; + let f = unsafe { Pin::new_unchecked(&mut this.f) }; + if let Poll::Ready(x) = a.poll(cx) { + return Poll::Ready(Either6::First(x)); + } + if let Poll::Ready(x) = b.poll(cx) { + return Poll::Ready(Either6::Second(x)); + } + if let Poll::Ready(x) = c.poll(cx) { + return Poll::Ready(Either6::Third(x)); + } + if let Poll::Ready(x) = d.poll(cx) { + return Poll::Ready(Either6::Fourth(x)); + } + if let Poll::Ready(x) = e.poll(cx) { + return Poll::Ready(Either6::Fifth(x)); + } + if let Poll::Ready(x) = f.poll(cx) { + return Poll::Ready(Either6::Sixth(x)); + } + Poll::Pending + } +} + +// ==================================================================== + /// Future for the [`select_array`] function. #[derive(Debug)] #[must_use = "futures do nothing unless you `.await` or poll them"] From 7fc09f89e8e4b611a868bc986104762b1c5ba81a Mon Sep 17 00:00:00 2001 From: rafael Date: Sun, 20 Oct 2024 23:28:47 +0200 Subject: [PATCH 091/361] embassy_rp: implement pwm traits from embedded_hal MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit • Update crate versions • Implement embedded-hal PWM traits • Add TB6612FNG motor driver example --- embassy-rp/Cargo.toml | 8 +- embassy-rp/src/pwm.rs | 42 +++++++ examples/rp23/Cargo.toml | 3 + .../src/bin/pwm_tb6612fng_motor_driver.rs | 114 ++++++++++++++++++ 4 files changed, 163 insertions(+), 4 deletions(-) create mode 100644 examples/rp23/src/bin/pwm_tb6612fng_motor_driver.rs diff --git a/embassy-rp/Cargo.toml b/embassy-rp/Cargo.toml index 54de238b3..baa92398b 100644 --- a/embassy-rp/Cargo.toml +++ b/embassy-rp/Cargo.toml @@ -118,18 +118,18 @@ embassy-usb-driver = {version = "0.1.0", path = "../embassy-usb-driver" } atomic-polyfill = "1.0.1" defmt = { version = "0.3", optional = true } log = { version = "0.4.14", optional = true } -nb = "1.0.0" +nb = "1.1.0" cfg-if = "1.0.0" cortex-m-rt = ">=0.6.15,<0.8" cortex-m = "0.7.6" -critical-section = "1.1" +critical-section = "1.2.0" chrono = { version = "0.4", default-features = false, optional = true } embedded-io = { version = "0.6.1" } embedded-io-async = { version = "0.6.1" } embedded-storage = { version = "0.3" } embedded-storage-async = { version = "0.4.1" } rand_core = "0.6.4" -fixed = "1.23.1" +fixed = "1.28.0" rp-pac = { git = "https://github.com/embassy-rs/rp-pac.git", rev = "a7f42d25517f7124ad3b4ed492dec8b0f50a0e6c", feature = ["rt"] } @@ -141,7 +141,7 @@ embedded-hal-nb = { version = "1.0" } pio-proc = {version= "0.2" } pio = {version= "0.2.1" } rp2040-boot2 = "0.3" -document-features = "0.2.7" +document-features = "0.2.10" sha2-const-stable = "0.1" rp-binary-info = { version = "0.1.0", optional = true } smart-leds = "0.4.0" diff --git a/embassy-rp/src/pwm.rs b/embassy-rp/src/pwm.rs index 027f5504e..79e626802 100644 --- a/embassy-rp/src/pwm.rs +++ b/embassy-rp/src/pwm.rs @@ -1,6 +1,7 @@ //! Pulse Width Modulation (PWM) use embassy_hal_internal::{into_ref, Peripheral, PeripheralRef}; +use embedded_hal_1::pwm::{Error, ErrorKind, ErrorType, SetDutyCycle}; use fixed::traits::ToFixed; use fixed::FixedU16; use pac::pwm::regs::{ChDiv, Intr}; @@ -80,6 +81,21 @@ impl From for Divmode { } } +/// PWM error. +#[derive(Debug)] +pub enum PwmError { + /// Invalid Duty Cycle. + InvalidDutyCycle, +} + +impl Error for PwmError { + fn kind(&self) -> ErrorKind { + match self { + PwmError::InvalidDutyCycle => ErrorKind::Other, + } + } +} + /// PWM driver. pub struct Pwm<'d> { pin_a: Option>, @@ -87,6 +103,32 @@ pub struct Pwm<'d> { slice: usize, } +impl<'d> ErrorType for Pwm<'d> { + type Error = PwmError; +} + +impl<'d> SetDutyCycle for Pwm<'d> { + fn max_duty_cycle(&self) -> u16 { + pac::PWM.ch(self.slice).top().read().top() + } + + fn set_duty_cycle(&mut self, duty: u16) -> Result<(), Self::Error> { + info!("duty {}",&duty); + let max_duty = self.max_duty_cycle(); + info!("max duty {}", &max_duty); + if duty > max_duty { + return Err(PwmError::InvalidDutyCycle); + } + + let p = pac::PWM.ch(self.slice); + p.cc().modify(|w| { + w.set_a(duty); + w.set_b(duty); + }); + Ok(()) + } +} + impl<'d> Pwm<'d> { fn new_inner( slice: usize, diff --git a/examples/rp23/Cargo.toml b/examples/rp23/Cargo.toml index 08646463c..f35e3a11d 100644 --- a/examples/rp23/Cargo.toml +++ b/examples/rp23/Cargo.toml @@ -30,6 +30,9 @@ serde-json-core = "0.5.1" # for assign resources example assign-resources = { git = "https://github.com/adamgreig/assign-resources", rev = "94ad10e2729afdf0fd5a77cd12e68409a982f58a" } +# for TB6612FNG example +tb6612fng = "1.0.0" + #cortex-m = { version = "0.7.6", features = ["critical-section-single-core"] } cortex-m = { version = "0.7.6", features = ["inline-asm"] } cortex-m-rt = "0.7.0" diff --git a/examples/rp23/src/bin/pwm_tb6612fng_motor_driver.rs b/examples/rp23/src/bin/pwm_tb6612fng_motor_driver.rs new file mode 100644 index 000000000..92c1ff6ba --- /dev/null +++ b/examples/rp23/src/bin/pwm_tb6612fng_motor_driver.rs @@ -0,0 +1,114 @@ +//! # PWM TB6612FNG motor driver +//! +//! This example shows the use of a TB6612FNG motor driver. The driver is built on top of embedded_hal and the example demonstrates how embassy_rp can be used to interact with ist. + +#![no_std] +#![no_main] + +use defmt::*; +use embassy_executor::Spawner; +use embassy_rp::block::ImageDef; +use embassy_rp::config::Config; +use embassy_rp::gpio::Output; +use embassy_rp::peripherals; +use embassy_rp::gpio; +use embassy_rp::pwm; +use embassy_time::Duration; +use embassy_time::Timer; +use {defmt_rtt as _, panic_probe as _}; +use tb6612fng::{DriveCommand, Motor, Tb6612fng}; +use assign_resources::assign_resources; + +/// Maximum PWM value (fully on) +const PWM_MAX: u16 = 50000; + +/// Minimum PWM value (fully off) +const PWM_MIN: u16 = 0; + +#[link_section = ".start_block"] +#[used] +pub static IMAGE_DEF: ImageDef = ImageDef::secure_exe(); + +assign_resources! { + motor: MotorResources { + standby_pin: PIN_22, + left_slice: PWM_SLICE6, + left_pwm_pin: PIN_28, + left_forward_pin: PIN_21, + left_backward_pin: PIN_20, + right_slice: PWM_SLICE5, + right_pwm_pin: PIN_27, + right_forward_pin: PIN_19, + right_backward_pin: PIN_18, + }, +} + +#[embassy_executor::main] +async fn main(_spawner: Spawner) { + let p = embassy_rp::init(Config::default()); + let s = split_resources!(p); + let r = s.motor; + + // we need a standby output and two motors to construct a full TB6612FNG + + // standby pin + let stby = Output::new(r.standby_pin, gpio::Level::Low); + + // motor A, here defined to be the left motor + let left_fwd = gpio::Output::new(r.left_forward_pin, gpio::Level::Low); + let left_bckw = gpio::Output::new(r.left_backward_pin, gpio::Level::Low); + let mut left_speed = pwm::Config::default(); + left_speed.top = PWM_MAX; + left_speed.compare_a = PWM_MIN; + let left_pwm = pwm::Pwm::new_output_a(r.left_slice, r.left_pwm_pin, left_speed); + let left_motor = Motor::new(left_fwd, left_bckw, left_pwm).unwrap(); + + // motor B, here defined to be the right motor + let right_fwd = gpio::Output::new(r.right_forward_pin, gpio::Level::Low); + let right_bckw = gpio::Output::new(r.right_backward_pin, gpio::Level::Low); + let mut right_speed = pwm::Config::default(); + right_speed.top = PWM_MAX; + right_speed.compare_b = PWM_MIN; + let right_pwm = pwm::Pwm::new_output_b(r.right_slice, r.right_pwm_pin, right_speed); + let right_motor = Motor::new(right_fwd, right_bckw, right_pwm).unwrap(); + + // construct the motor driver + let mut control = Tb6612fng::new(left_motor, right_motor, stby).unwrap(); + + loop { + // wake up the motor driver + info!("end standby"); + control.disable_standby().unwrap(); + Timer::after(Duration::from_millis(100)).await; + + // drive a straight line forward at 20% speed for 5s + info!("drive straight"); + control.motor_a.drive(DriveCommand::Forward(20)).unwrap(); + control.motor_b.drive(DriveCommand::Forward(20)).unwrap(); + Timer::after(Duration::from_secs(5)).await; + + // coast for 2s + info!("coast"); + control.motor_a.drive(DriveCommand::Stop).unwrap(); + control.motor_b.drive(DriveCommand::Stop).unwrap(); + Timer::after(Duration::from_secs(2)).await; + + // actively brake + info!("brake"); + control.motor_a.drive(DriveCommand::Brake).unwrap(); + control.motor_b.drive(DriveCommand::Brake).unwrap(); + Timer::after(Duration::from_secs(1)).await; + + // slowly turn for 3s + info!( "turn"); + control.motor_a.drive(DriveCommand::Backward(10)).unwrap(); + control.motor_b.drive(DriveCommand::Forward(10)).unwrap(); + Timer::after(Duration::from_secs(3)).await; + + // and put the driver in standby mode and wait for 5s + info!( "standby"); + control.enable_standby().unwrap(); + Timer::after(Duration::from_secs(5)).await; + } +} + From 8baf88f8f4668bdb54c1415bdb8ad62cf2fa5f24 Mon Sep 17 00:00:00 2001 From: rafael Date: Sun, 20 Oct 2024 23:31:53 +0200 Subject: [PATCH 092/361] rustfmt --- .../rp23/src/bin/pwm_tb6612fng_motor_driver.rs | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/examples/rp23/src/bin/pwm_tb6612fng_motor_driver.rs b/examples/rp23/src/bin/pwm_tb6612fng_motor_driver.rs index 92c1ff6ba..36b8d9620 100644 --- a/examples/rp23/src/bin/pwm_tb6612fng_motor_driver.rs +++ b/examples/rp23/src/bin/pwm_tb6612fng_motor_driver.rs @@ -1,23 +1,23 @@ //! # PWM TB6612FNG motor driver -//! +//! //! This example shows the use of a TB6612FNG motor driver. The driver is built on top of embedded_hal and the example demonstrates how embassy_rp can be used to interact with ist. #![no_std] #![no_main] +use assign_resources::assign_resources; use defmt::*; use embassy_executor::Spawner; use embassy_rp::block::ImageDef; use embassy_rp::config::Config; +use embassy_rp::gpio; use embassy_rp::gpio::Output; use embassy_rp::peripherals; -use embassy_rp::gpio; use embassy_rp::pwm; use embassy_time::Duration; use embassy_time::Timer; -use {defmt_rtt as _, panic_probe as _}; use tb6612fng::{DriveCommand, Motor, Tb6612fng}; -use assign_resources::assign_resources; +use {defmt_rtt as _, panic_probe as _}; /// Maximum PWM value (fully on) const PWM_MAX: u16 = 50000; @@ -94,21 +94,20 @@ async fn main(_spawner: Spawner) { Timer::after(Duration::from_secs(2)).await; // actively brake - info!("brake"); + info!("brake"); control.motor_a.drive(DriveCommand::Brake).unwrap(); control.motor_b.drive(DriveCommand::Brake).unwrap(); Timer::after(Duration::from_secs(1)).await; // slowly turn for 3s - info!( "turn"); + info!("turn"); control.motor_a.drive(DriveCommand::Backward(10)).unwrap(); control.motor_b.drive(DriveCommand::Forward(10)).unwrap(); Timer::after(Duration::from_secs(3)).await; // and put the driver in standby mode and wait for 5s - info!( "standby"); + info!("standby"); control.enable_standby().unwrap(); Timer::after(Duration::from_secs(5)).await; } } - From f0de0493084759a5e7310c816919996b201f0bc4 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Sun, 20 Oct 2024 23:45:10 +0200 Subject: [PATCH 093/361] executor-macros: improve error messages. --- embassy-executor-macros/src/macros/task.rs | 26 ++++++++++++++++++++-- 1 file changed, 24 insertions(+), 2 deletions(-) diff --git a/embassy-executor-macros/src/macros/task.rs b/embassy-executor-macros/src/macros/task.rs index 0404dba64..2f2aeda76 100644 --- a/embassy-executor-macros/src/macros/task.rs +++ b/embassy-executor-macros/src/macros/task.rs @@ -2,7 +2,7 @@ use darling::export::NestedMeta; use darling::FromMeta; use proc_macro2::{Span, TokenStream}; use quote::{format_ident, quote}; -use syn::visit::Visit; +use syn::visit::{self, Visit}; use syn::{Expr, ExprLit, Lit, LitInt, ReturnType, Type}; use crate::util::*; @@ -76,7 +76,7 @@ pub fn run(args: TokenStream, item: TokenStream) -> TokenStream { for arg in fargs.iter_mut() { match arg { syn::FnArg::Receiver(_) => { - error(&mut errors, arg, "task functions must not have receiver arguments"); + error(&mut errors, arg, "task functions must not have `self` arguments"); } syn::FnArg::Typed(t) => { check_arg_ty(&mut errors, &t.ty); @@ -180,6 +180,28 @@ fn check_arg_ty(errors: &mut TokenStream, ty: &Type) { } impl<'a, 'ast> Visit<'ast> for Visitor<'a> { + fn visit_type_reference(&mut self, i: &'ast syn::TypeReference) { + // only check for elided lifetime here. If not elided, it's checked by `visit_lifetime`. + if i.lifetime.is_none() { + error( + self.errors, + i.and_token, + "Arguments for tasks must live forever. Try using the `'static` lifetime.", + ) + } + visit::visit_type_reference(self, i); + } + + fn visit_lifetime(&mut self, i: &'ast syn::Lifetime) { + if i.ident.to_string() != "static" { + error( + self.errors, + i, + "Arguments for tasks must live forever. Try using the `'static` lifetime.", + ) + } + } + fn visit_type_impl_trait(&mut self, i: &'ast syn::TypeImplTrait) { error(self.errors, i, "`impl Trait` is not allowed in task arguments. It is syntax sugar for generics, and tasks can't be generic."); } From 8f9826872332fb0d2abd3ffc3889ff4c0e1c3909 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Sun, 20 Oct 2024 23:48:58 +0200 Subject: [PATCH 094/361] executor: add compile-fail / ui tests. --- .github/ci/test-nightly.sh | 3 +++ .github/ci/test.sh | 1 + embassy-executor/Cargo.toml | 2 +- embassy-executor/tests/ui.rs | 23 +++++++++++++++++++ embassy-executor/tests/ui/abi.rs | 8 +++++++ embassy-executor/tests/ui/abi.stderr | 5 ++++ embassy-executor/tests/ui/bad_return.rs | 10 ++++++++ embassy-executor/tests/ui/bad_return.stderr | 5 ++++ embassy-executor/tests/ui/generics.rs | 8 +++++++ embassy-executor/tests/ui/generics.stderr | 5 ++++ embassy-executor/tests/ui/impl_trait.rs | 6 +++++ embassy-executor/tests/ui/impl_trait.stderr | 5 ++++ .../tests/ui/impl_trait_nested.rs | 8 +++++++ .../tests/ui/impl_trait_nested.stderr | 5 ++++ .../tests/ui/impl_trait_static.rs | 6 +++++ .../tests/ui/impl_trait_static.stderr | 5 ++++ .../tests/ui/nonstatic_ref_anon.rs | 6 +++++ .../tests/ui/nonstatic_ref_anon.stderr | 5 ++++ .../tests/ui/nonstatic_ref_anon_nested.rs | 6 +++++ .../tests/ui/nonstatic_ref_anon_nested.stderr | 5 ++++ .../tests/ui/nonstatic_ref_elided.rs | 6 +++++ .../tests/ui/nonstatic_ref_elided.stderr | 5 ++++ .../tests/ui/nonstatic_ref_generic.rs | 6 +++++ .../tests/ui/nonstatic_ref_generic.stderr | 11 +++++++++ .../tests/ui/nonstatic_struct_anon.rs | 8 +++++++ .../tests/ui/nonstatic_struct_anon.stderr | 5 ++++ .../tests/ui/nonstatic_struct_elided.rs | 8 +++++++ .../tests/ui/nonstatic_struct_elided.stderr | 10 ++++++++ .../tests/ui/nonstatic_struct_generic.rs | 8 +++++++ .../tests/ui/nonstatic_struct_generic.stderr | 11 +++++++++ embassy-executor/tests/ui/not_async.rs | 8 +++++++ embassy-executor/tests/ui/not_async.stderr | 5 ++++ embassy-executor/tests/ui/self.rs | 8 +++++++ embassy-executor/tests/ui/self.stderr | 13 +++++++++++ embassy-executor/tests/ui/self_ref.rs | 8 +++++++ embassy-executor/tests/ui/self_ref.stderr | 13 +++++++++++ embassy-executor/tests/ui/where_clause.rs | 12 ++++++++++ embassy-executor/tests/ui/where_clause.stderr | 7 ++++++ 38 files changed, 278 insertions(+), 1 deletion(-) create mode 100644 embassy-executor/tests/ui.rs create mode 100644 embassy-executor/tests/ui/abi.rs create mode 100644 embassy-executor/tests/ui/abi.stderr create mode 100644 embassy-executor/tests/ui/bad_return.rs create mode 100644 embassy-executor/tests/ui/bad_return.stderr create mode 100644 embassy-executor/tests/ui/generics.rs create mode 100644 embassy-executor/tests/ui/generics.stderr create mode 100644 embassy-executor/tests/ui/impl_trait.rs create mode 100644 embassy-executor/tests/ui/impl_trait.stderr create mode 100644 embassy-executor/tests/ui/impl_trait_nested.rs create mode 100644 embassy-executor/tests/ui/impl_trait_nested.stderr create mode 100644 embassy-executor/tests/ui/impl_trait_static.rs create mode 100644 embassy-executor/tests/ui/impl_trait_static.stderr create mode 100644 embassy-executor/tests/ui/nonstatic_ref_anon.rs create mode 100644 embassy-executor/tests/ui/nonstatic_ref_anon.stderr create mode 100644 embassy-executor/tests/ui/nonstatic_ref_anon_nested.rs create mode 100644 embassy-executor/tests/ui/nonstatic_ref_anon_nested.stderr create mode 100644 embassy-executor/tests/ui/nonstatic_ref_elided.rs create mode 100644 embassy-executor/tests/ui/nonstatic_ref_elided.stderr create mode 100644 embassy-executor/tests/ui/nonstatic_ref_generic.rs create mode 100644 embassy-executor/tests/ui/nonstatic_ref_generic.stderr create mode 100644 embassy-executor/tests/ui/nonstatic_struct_anon.rs create mode 100644 embassy-executor/tests/ui/nonstatic_struct_anon.stderr create mode 100644 embassy-executor/tests/ui/nonstatic_struct_elided.rs create mode 100644 embassy-executor/tests/ui/nonstatic_struct_elided.stderr create mode 100644 embassy-executor/tests/ui/nonstatic_struct_generic.rs create mode 100644 embassy-executor/tests/ui/nonstatic_struct_generic.stderr create mode 100644 embassy-executor/tests/ui/not_async.rs create mode 100644 embassy-executor/tests/ui/not_async.stderr create mode 100644 embassy-executor/tests/ui/self.rs create mode 100644 embassy-executor/tests/ui/self.stderr create mode 100644 embassy-executor/tests/ui/self_ref.rs create mode 100644 embassy-executor/tests/ui/self_ref.stderr create mode 100644 embassy-executor/tests/ui/where_clause.rs create mode 100644 embassy-executor/tests/ui/where_clause.stderr diff --git a/.github/ci/test-nightly.sh b/.github/ci/test-nightly.sh index 1724ffe89..a03b55e8d 100755 --- a/.github/ci/test-nightly.sh +++ b/.github/ci/test-nightly.sh @@ -9,6 +9,9 @@ export CARGO_HOME=/ci/cache/cargo export CARGO_TARGET_DIR=/ci/cache/target mv rust-toolchain-nightly.toml rust-toolchain.toml +cargo test --manifest-path ./embassy-executor/Cargo.toml +cargo test --manifest-path ./embassy-executor/Cargo.toml --features nightly + MIRIFLAGS=-Zmiri-ignore-leaks cargo miri test --manifest-path ./embassy-executor/Cargo.toml MIRIFLAGS=-Zmiri-ignore-leaks cargo miri test --manifest-path ./embassy-executor/Cargo.toml --features nightly MIRIFLAGS=-Zmiri-ignore-leaks cargo miri test --manifest-path ./embassy-sync/Cargo.toml diff --git a/.github/ci/test.sh b/.github/ci/test.sh index 0de265049..0fe088bfe 100755 --- a/.github/ci/test.sh +++ b/.github/ci/test.sh @@ -12,6 +12,7 @@ export CARGO_TARGET_DIR=/ci/cache/target # used when pointing stm32-metapac to a CI-built one. export CARGO_NET_GIT_FETCH_WITH_CLI=true +cargo test --manifest-path ./embassy-executor/Cargo.toml cargo test --manifest-path ./embassy-futures/Cargo.toml cargo test --manifest-path ./embassy-sync/Cargo.toml cargo test --manifest-path ./embassy-embedded-hal/Cargo.toml diff --git a/embassy-executor/Cargo.toml b/embassy-executor/Cargo.toml index e2fedce3c..e138f93b0 100644 --- a/embassy-executor/Cargo.toml +++ b/embassy-executor/Cargo.toml @@ -55,7 +55,7 @@ avr-device = { version = "0.5.3", optional = true } [dev-dependencies] critical-section = { version = "1.1", features = ["std"] } - +trybuild = "1.0" [features] diff --git a/embassy-executor/tests/ui.rs b/embassy-executor/tests/ui.rs new file mode 100644 index 000000000..be4679485 --- /dev/null +++ b/embassy-executor/tests/ui.rs @@ -0,0 +1,23 @@ +#[cfg(not(miri))] +#[test] +fn ui() { + let t = trybuild::TestCases::new(); + t.compile_fail("tests/ui/abi.rs"); + t.compile_fail("tests/ui/bad_return.rs"); + t.compile_fail("tests/ui/generics.rs"); + t.compile_fail("tests/ui/impl_trait_nested.rs"); + t.compile_fail("tests/ui/impl_trait.rs"); + t.compile_fail("tests/ui/impl_trait_static.rs"); + t.compile_fail("tests/ui/nonstatic_ref_anon_nested.rs"); + t.compile_fail("tests/ui/nonstatic_ref_anon.rs"); + t.compile_fail("tests/ui/nonstatic_ref_elided.rs"); + t.compile_fail("tests/ui/nonstatic_ref_generic.rs"); + t.compile_fail("tests/ui/nonstatic_struct_anon.rs"); + #[cfg(not(feature = "nightly"))] // we can't catch this case with the macro, so the output changes on nightly. + t.compile_fail("tests/ui/nonstatic_struct_elided.rs"); + t.compile_fail("tests/ui/nonstatic_struct_generic.rs"); + t.compile_fail("tests/ui/not_async.rs"); + t.compile_fail("tests/ui/self_ref.rs"); + t.compile_fail("tests/ui/self.rs"); + t.compile_fail("tests/ui/where_clause.rs"); +} diff --git a/embassy-executor/tests/ui/abi.rs b/embassy-executor/tests/ui/abi.rs new file mode 100644 index 000000000..fd52f7e41 --- /dev/null +++ b/embassy-executor/tests/ui/abi.rs @@ -0,0 +1,8 @@ +#![cfg_attr(feature = "nightly", feature(impl_trait_in_assoc_type))] + +struct Foo<'a>(&'a ()); + +#[embassy_executor::task] +async extern "C" fn task() {} + +fn main() {} diff --git a/embassy-executor/tests/ui/abi.stderr b/embassy-executor/tests/ui/abi.stderr new file mode 100644 index 000000000..e264e371a --- /dev/null +++ b/embassy-executor/tests/ui/abi.stderr @@ -0,0 +1,5 @@ +error: task functions must not have an ABI qualifier + --> tests/ui/abi.rs:6:1 + | +6 | async extern "C" fn task() {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/embassy-executor/tests/ui/bad_return.rs b/embassy-executor/tests/ui/bad_return.rs new file mode 100644 index 000000000..f09a5205b --- /dev/null +++ b/embassy-executor/tests/ui/bad_return.rs @@ -0,0 +1,10 @@ +#![cfg_attr(feature = "nightly", feature(impl_trait_in_assoc_type))] + +struct Foo<'a>(&'a ()); + +#[embassy_executor::task] +async fn task() -> u32 { + 5 +} + +fn main() {} diff --git a/embassy-executor/tests/ui/bad_return.stderr b/embassy-executor/tests/ui/bad_return.stderr new file mode 100644 index 000000000..e9d94dff8 --- /dev/null +++ b/embassy-executor/tests/ui/bad_return.stderr @@ -0,0 +1,5 @@ +error: task functions must either not return a value, return `()` or return `!` + --> tests/ui/bad_return.rs:6:1 + | +6 | async fn task() -> u32 { + | ^^^^^^^^^^^^^^^^^^^^^^ diff --git a/embassy-executor/tests/ui/generics.rs b/embassy-executor/tests/ui/generics.rs new file mode 100644 index 000000000..b83123bb1 --- /dev/null +++ b/embassy-executor/tests/ui/generics.rs @@ -0,0 +1,8 @@ +#![cfg_attr(feature = "nightly", feature(impl_trait_in_assoc_type))] + +struct Foo<'a>(&'a ()); + +#[embassy_executor::task] +async fn task(_t: T) {} + +fn main() {} diff --git a/embassy-executor/tests/ui/generics.stderr b/embassy-executor/tests/ui/generics.stderr new file mode 100644 index 000000000..197719a7b --- /dev/null +++ b/embassy-executor/tests/ui/generics.stderr @@ -0,0 +1,5 @@ +error: task functions must not be generic + --> tests/ui/generics.rs:6:1 + | +6 | async fn task(_t: T) {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/embassy-executor/tests/ui/impl_trait.rs b/embassy-executor/tests/ui/impl_trait.rs new file mode 100644 index 000000000..a21402aa0 --- /dev/null +++ b/embassy-executor/tests/ui/impl_trait.rs @@ -0,0 +1,6 @@ +#![cfg_attr(feature = "nightly", feature(impl_trait_in_assoc_type))] + +#[embassy_executor::task] +async fn foo(_x: impl Sized) {} + +fn main() {} diff --git a/embassy-executor/tests/ui/impl_trait.stderr b/embassy-executor/tests/ui/impl_trait.stderr new file mode 100644 index 000000000..099b1828f --- /dev/null +++ b/embassy-executor/tests/ui/impl_trait.stderr @@ -0,0 +1,5 @@ +error: `impl Trait` is not allowed in task arguments. It is syntax sugar for generics, and tasks can't be generic. + --> tests/ui/impl_trait.rs:4:18 + | +4 | async fn foo(_x: impl Sized) {} + | ^^^^^^^^^^ diff --git a/embassy-executor/tests/ui/impl_trait_nested.rs b/embassy-executor/tests/ui/impl_trait_nested.rs new file mode 100644 index 000000000..07442b8fa --- /dev/null +++ b/embassy-executor/tests/ui/impl_trait_nested.rs @@ -0,0 +1,8 @@ +#![cfg_attr(feature = "nightly", feature(impl_trait_in_assoc_type))] + +struct Foo(T); + +#[embassy_executor::task] +async fn foo(_x: Foo) {} + +fn main() {} diff --git a/embassy-executor/tests/ui/impl_trait_nested.stderr b/embassy-executor/tests/ui/impl_trait_nested.stderr new file mode 100644 index 000000000..39592f958 --- /dev/null +++ b/embassy-executor/tests/ui/impl_trait_nested.stderr @@ -0,0 +1,5 @@ +error: `impl Trait` is not allowed in task arguments. It is syntax sugar for generics, and tasks can't be generic. + --> tests/ui/impl_trait_nested.rs:6:22 + | +6 | async fn foo(_x: Foo) {} + | ^^^^^^^^^^^^^^^^^^^^ diff --git a/embassy-executor/tests/ui/impl_trait_static.rs b/embassy-executor/tests/ui/impl_trait_static.rs new file mode 100644 index 000000000..272470f98 --- /dev/null +++ b/embassy-executor/tests/ui/impl_trait_static.rs @@ -0,0 +1,6 @@ +#![cfg_attr(feature = "nightly", feature(impl_trait_in_assoc_type))] + +#[embassy_executor::task] +async fn foo(_x: impl Sized + 'static) {} + +fn main() {} diff --git a/embassy-executor/tests/ui/impl_trait_static.stderr b/embassy-executor/tests/ui/impl_trait_static.stderr new file mode 100644 index 000000000..0032a20c9 --- /dev/null +++ b/embassy-executor/tests/ui/impl_trait_static.stderr @@ -0,0 +1,5 @@ +error: `impl Trait` is not allowed in task arguments. It is syntax sugar for generics, and tasks can't be generic. + --> tests/ui/impl_trait_static.rs:4:18 + | +4 | async fn foo(_x: impl Sized + 'static) {} + | ^^^^^^^^^^^^^^^^^^^^ diff --git a/embassy-executor/tests/ui/nonstatic_ref_anon.rs b/embassy-executor/tests/ui/nonstatic_ref_anon.rs new file mode 100644 index 000000000..417c360a1 --- /dev/null +++ b/embassy-executor/tests/ui/nonstatic_ref_anon.rs @@ -0,0 +1,6 @@ +#![cfg_attr(feature = "nightly", feature(impl_trait_in_assoc_type))] + +#[embassy_executor::task] +async fn foo(_x: &'_ u32) {} + +fn main() {} diff --git a/embassy-executor/tests/ui/nonstatic_ref_anon.stderr b/embassy-executor/tests/ui/nonstatic_ref_anon.stderr new file mode 100644 index 000000000..0544de843 --- /dev/null +++ b/embassy-executor/tests/ui/nonstatic_ref_anon.stderr @@ -0,0 +1,5 @@ +error: Arguments for tasks must live forever. Try using the `'static` lifetime. + --> tests/ui/nonstatic_ref_anon.rs:4:19 + | +4 | async fn foo(_x: &'_ u32) {} + | ^^ diff --git a/embassy-executor/tests/ui/nonstatic_ref_anon_nested.rs b/embassy-executor/tests/ui/nonstatic_ref_anon_nested.rs new file mode 100644 index 000000000..175ebccc1 --- /dev/null +++ b/embassy-executor/tests/ui/nonstatic_ref_anon_nested.rs @@ -0,0 +1,6 @@ +#![cfg_attr(feature = "nightly", feature(impl_trait_in_assoc_type))] + +#[embassy_executor::task] +async fn foo(_x: &'static &'_ u32) {} + +fn main() {} diff --git a/embassy-executor/tests/ui/nonstatic_ref_anon_nested.stderr b/embassy-executor/tests/ui/nonstatic_ref_anon_nested.stderr new file mode 100644 index 000000000..79f262e6b --- /dev/null +++ b/embassy-executor/tests/ui/nonstatic_ref_anon_nested.stderr @@ -0,0 +1,5 @@ +error: Arguments for tasks must live forever. Try using the `'static` lifetime. + --> tests/ui/nonstatic_ref_anon_nested.rs:4:28 + | +4 | async fn foo(_x: &'static &'_ u32) {} + | ^^ diff --git a/embassy-executor/tests/ui/nonstatic_ref_elided.rs b/embassy-executor/tests/ui/nonstatic_ref_elided.rs new file mode 100644 index 000000000..cf49ad709 --- /dev/null +++ b/embassy-executor/tests/ui/nonstatic_ref_elided.rs @@ -0,0 +1,6 @@ +#![cfg_attr(feature = "nightly", feature(impl_trait_in_assoc_type))] + +#[embassy_executor::task] +async fn foo(_x: &u32) {} + +fn main() {} diff --git a/embassy-executor/tests/ui/nonstatic_ref_elided.stderr b/embassy-executor/tests/ui/nonstatic_ref_elided.stderr new file mode 100644 index 000000000..7e2b9eb7c --- /dev/null +++ b/embassy-executor/tests/ui/nonstatic_ref_elided.stderr @@ -0,0 +1,5 @@ +error: Arguments for tasks must live forever. Try using the `'static` lifetime. + --> tests/ui/nonstatic_ref_elided.rs:4:18 + | +4 | async fn foo(_x: &u32) {} + | ^ diff --git a/embassy-executor/tests/ui/nonstatic_ref_generic.rs b/embassy-executor/tests/ui/nonstatic_ref_generic.rs new file mode 100644 index 000000000..3f8a26cf8 --- /dev/null +++ b/embassy-executor/tests/ui/nonstatic_ref_generic.rs @@ -0,0 +1,6 @@ +#![cfg_attr(feature = "nightly", feature(impl_trait_in_assoc_type))] + +#[embassy_executor::task] +async fn foo<'a>(_x: &'a u32) {} + +fn main() {} diff --git a/embassy-executor/tests/ui/nonstatic_ref_generic.stderr b/embassy-executor/tests/ui/nonstatic_ref_generic.stderr new file mode 100644 index 000000000..af8491ad7 --- /dev/null +++ b/embassy-executor/tests/ui/nonstatic_ref_generic.stderr @@ -0,0 +1,11 @@ +error: task functions must not be generic + --> tests/ui/nonstatic_ref_generic.rs:4:1 + | +4 | async fn foo<'a>(_x: &'a u32) {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: Arguments for tasks must live forever. Try using the `'static` lifetime. + --> tests/ui/nonstatic_ref_generic.rs:4:23 + | +4 | async fn foo<'a>(_x: &'a u32) {} + | ^^ diff --git a/embassy-executor/tests/ui/nonstatic_struct_anon.rs b/embassy-executor/tests/ui/nonstatic_struct_anon.rs new file mode 100644 index 000000000..ba95d1459 --- /dev/null +++ b/embassy-executor/tests/ui/nonstatic_struct_anon.rs @@ -0,0 +1,8 @@ +#![cfg_attr(feature = "nightly", feature(impl_trait_in_assoc_type))] + +struct Foo<'a>(&'a ()); + +#[embassy_executor::task] +async fn task(_x: Foo<'_>) {} + +fn main() {} diff --git a/embassy-executor/tests/ui/nonstatic_struct_anon.stderr b/embassy-executor/tests/ui/nonstatic_struct_anon.stderr new file mode 100644 index 000000000..5df2a6e06 --- /dev/null +++ b/embassy-executor/tests/ui/nonstatic_struct_anon.stderr @@ -0,0 +1,5 @@ +error: Arguments for tasks must live forever. Try using the `'static` lifetime. + --> tests/ui/nonstatic_struct_anon.rs:6:23 + | +6 | async fn task(_x: Foo<'_>) {} + | ^^ diff --git a/embassy-executor/tests/ui/nonstatic_struct_elided.rs b/embassy-executor/tests/ui/nonstatic_struct_elided.rs new file mode 100644 index 000000000..4cfe2966a --- /dev/null +++ b/embassy-executor/tests/ui/nonstatic_struct_elided.rs @@ -0,0 +1,8 @@ +#![cfg_attr(feature = "nightly", feature(impl_trait_in_assoc_type))] + +struct Foo<'a>(&'a ()); + +#[embassy_executor::task] +async fn task(_x: Foo) {} + +fn main() {} diff --git a/embassy-executor/tests/ui/nonstatic_struct_elided.stderr b/embassy-executor/tests/ui/nonstatic_struct_elided.stderr new file mode 100644 index 000000000..099ef8b4e --- /dev/null +++ b/embassy-executor/tests/ui/nonstatic_struct_elided.stderr @@ -0,0 +1,10 @@ +error[E0726]: implicit elided lifetime not allowed here + --> tests/ui/nonstatic_struct_elided.rs:6:19 + | +6 | async fn task(_x: Foo) {} + | ^^^ expected lifetime parameter + | +help: indicate the anonymous lifetime + | +6 | async fn task(_x: Foo<'_>) {} + | ++++ diff --git a/embassy-executor/tests/ui/nonstatic_struct_generic.rs b/embassy-executor/tests/ui/nonstatic_struct_generic.rs new file mode 100644 index 000000000..ec3d908f6 --- /dev/null +++ b/embassy-executor/tests/ui/nonstatic_struct_generic.rs @@ -0,0 +1,8 @@ +#![cfg_attr(feature = "nightly", feature(impl_trait_in_assoc_type))] + +struct Foo<'a>(&'a ()); + +#[embassy_executor::task] +async fn task<'a>(_x: Foo<'a>) {} + +fn main() {} diff --git a/embassy-executor/tests/ui/nonstatic_struct_generic.stderr b/embassy-executor/tests/ui/nonstatic_struct_generic.stderr new file mode 100644 index 000000000..61d5231bc --- /dev/null +++ b/embassy-executor/tests/ui/nonstatic_struct_generic.stderr @@ -0,0 +1,11 @@ +error: task functions must not be generic + --> tests/ui/nonstatic_struct_generic.rs:6:1 + | +6 | async fn task<'a>(_x: Foo<'a>) {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: Arguments for tasks must live forever. Try using the `'static` lifetime. + --> tests/ui/nonstatic_struct_generic.rs:6:27 + | +6 | async fn task<'a>(_x: Foo<'a>) {} + | ^^ diff --git a/embassy-executor/tests/ui/not_async.rs b/embassy-executor/tests/ui/not_async.rs new file mode 100644 index 000000000..f3f7e9bd2 --- /dev/null +++ b/embassy-executor/tests/ui/not_async.rs @@ -0,0 +1,8 @@ +#![cfg_attr(feature = "nightly", feature(impl_trait_in_assoc_type))] + +struct Foo<'a>(&'a ()); + +#[embassy_executor::task] +fn task() {} + +fn main() {} diff --git a/embassy-executor/tests/ui/not_async.stderr b/embassy-executor/tests/ui/not_async.stderr new file mode 100644 index 000000000..27f040d9c --- /dev/null +++ b/embassy-executor/tests/ui/not_async.stderr @@ -0,0 +1,5 @@ +error: task functions must be async + --> tests/ui/not_async.rs:6:1 + | +6 | fn task() {} + | ^^^^^^^^^ diff --git a/embassy-executor/tests/ui/self.rs b/embassy-executor/tests/ui/self.rs new file mode 100644 index 000000000..f83a962d1 --- /dev/null +++ b/embassy-executor/tests/ui/self.rs @@ -0,0 +1,8 @@ +#![cfg_attr(feature = "nightly", feature(impl_trait_in_assoc_type))] + +struct Foo<'a>(&'a ()); + +#[embassy_executor::task] +async fn task(self) {} + +fn main() {} diff --git a/embassy-executor/tests/ui/self.stderr b/embassy-executor/tests/ui/self.stderr new file mode 100644 index 000000000..aaf031573 --- /dev/null +++ b/embassy-executor/tests/ui/self.stderr @@ -0,0 +1,13 @@ +error: task functions must not have `self` arguments + --> tests/ui/self.rs:6:15 + | +6 | async fn task(self) {} + | ^^^^ + +error: `self` parameter is only allowed in associated functions + --> tests/ui/self.rs:6:15 + | +6 | async fn task(self) {} + | ^^^^ not semantically valid as function parameter + | + = note: associated functions are those in `impl` or `trait` definitions diff --git a/embassy-executor/tests/ui/self_ref.rs b/embassy-executor/tests/ui/self_ref.rs new file mode 100644 index 000000000..5e49bba5e --- /dev/null +++ b/embassy-executor/tests/ui/self_ref.rs @@ -0,0 +1,8 @@ +#![cfg_attr(feature = "nightly", feature(impl_trait_in_assoc_type))] + +struct Foo<'a>(&'a ()); + +#[embassy_executor::task] +async fn task(&mut self) {} + +fn main() {} diff --git a/embassy-executor/tests/ui/self_ref.stderr b/embassy-executor/tests/ui/self_ref.stderr new file mode 100644 index 000000000..dd2052977 --- /dev/null +++ b/embassy-executor/tests/ui/self_ref.stderr @@ -0,0 +1,13 @@ +error: task functions must not have `self` arguments + --> tests/ui/self_ref.rs:6:15 + | +6 | async fn task(&mut self) {} + | ^^^^^^^^^ + +error: `self` parameter is only allowed in associated functions + --> tests/ui/self_ref.rs:6:15 + | +6 | async fn task(&mut self) {} + | ^^^^^^^^^ not semantically valid as function parameter + | + = note: associated functions are those in `impl` or `trait` definitions diff --git a/embassy-executor/tests/ui/where_clause.rs b/embassy-executor/tests/ui/where_clause.rs new file mode 100644 index 000000000..848d78149 --- /dev/null +++ b/embassy-executor/tests/ui/where_clause.rs @@ -0,0 +1,12 @@ +#![cfg_attr(feature = "nightly", feature(impl_trait_in_assoc_type))] + +struct Foo<'a>(&'a ()); + +#[embassy_executor::task] +async fn task() +where + (): Sized, +{ +} + +fn main() {} diff --git a/embassy-executor/tests/ui/where_clause.stderr b/embassy-executor/tests/ui/where_clause.stderr new file mode 100644 index 000000000..eba45af40 --- /dev/null +++ b/embassy-executor/tests/ui/where_clause.stderr @@ -0,0 +1,7 @@ +error: task functions must not have `where` clauses + --> tests/ui/where_clause.rs:6:1 + | +6 | / async fn task() +7 | | where +8 | | (): Sized, + | |______________^ From 1a24b4f018cd6e807a02a5b55343d33a9213c8ab Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Mon, 21 Oct 2024 01:26:02 +0200 Subject: [PATCH 095/361] Release embassy-executor v0.6.1, embassy-executor-macros v0.6.1 --- embassy-executor-macros/Cargo.toml | 2 +- embassy-executor/CHANGELOG.md | 11 ++++++++++- embassy-executor/Cargo.toml | 4 ++-- embassy-rp/Cargo.toml | 2 +- embassy-stm32/Cargo.toml | 2 +- embassy-time/Cargo.toml | 2 +- examples/boot/application/nrf/Cargo.toml | 2 +- examples/boot/application/rp/Cargo.toml | 2 +- examples/boot/application/stm32f3/Cargo.toml | 2 +- examples/boot/application/stm32f7/Cargo.toml | 2 +- examples/boot/application/stm32h7/Cargo.toml | 2 +- examples/boot/application/stm32l0/Cargo.toml | 2 +- examples/boot/application/stm32l1/Cargo.toml | 2 +- examples/boot/application/stm32l4/Cargo.toml | 2 +- examples/boot/application/stm32wb-dfu/Cargo.toml | 2 +- examples/boot/application/stm32wl/Cargo.toml | 2 +- examples/lpc55s69/Cargo.toml | 2 +- examples/nrf-rtos-trace/Cargo.toml | 2 +- examples/nrf51/Cargo.toml | 2 +- examples/nrf52810/Cargo.toml | 2 +- examples/nrf52840/Cargo.toml | 2 +- examples/nrf5340/Cargo.toml | 2 +- examples/nrf9151/ns/Cargo.toml | 2 +- examples/nrf9151/s/Cargo.toml | 2 +- examples/nrf9160/Cargo.toml | 2 +- examples/rp/Cargo.toml | 2 +- examples/rp23/Cargo.toml | 2 +- examples/std/Cargo.toml | 2 +- examples/stm32c0/Cargo.toml | 2 +- examples/stm32f0/Cargo.toml | 2 +- examples/stm32f1/Cargo.toml | 2 +- examples/stm32f2/Cargo.toml | 2 +- examples/stm32f3/Cargo.toml | 2 +- examples/stm32f334/Cargo.toml | 2 +- examples/stm32f4/Cargo.toml | 2 +- examples/stm32f469/Cargo.toml | 2 +- examples/stm32f7/Cargo.toml | 2 +- examples/stm32g0/Cargo.toml | 2 +- examples/stm32g4/Cargo.toml | 2 +- examples/stm32h5/Cargo.toml | 2 +- examples/stm32h7/Cargo.toml | 2 +- examples/stm32h735/Cargo.toml | 2 +- examples/stm32h755cm4/Cargo.toml | 2 +- examples/stm32h755cm7/Cargo.toml | 2 +- examples/stm32h7rs/Cargo.toml | 2 +- examples/stm32l0/Cargo.toml | 2 +- examples/stm32l1/Cargo.toml | 2 +- examples/stm32l4/Cargo.toml | 2 +- examples/stm32l5/Cargo.toml | 2 +- examples/stm32u0/Cargo.toml | 2 +- examples/stm32u5/Cargo.toml | 2 +- examples/stm32wb/Cargo.toml | 2 +- examples/stm32wba/Cargo.toml | 2 +- examples/stm32wl/Cargo.toml | 2 +- examples/wasm/Cargo.toml | 2 +- tests/nrf/Cargo.toml | 2 +- tests/riscv32/Cargo.toml | 2 +- tests/rp/Cargo.toml | 2 +- tests/stm32/Cargo.toml | 2 +- 59 files changed, 69 insertions(+), 60 deletions(-) diff --git a/embassy-executor-macros/Cargo.toml b/embassy-executor-macros/Cargo.toml index ef509c3f9..306cf8352 100644 --- a/embassy-executor-macros/Cargo.toml +++ b/embassy-executor-macros/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "embassy-executor-macros" -version = "0.5.0" +version = "0.6.1" edition = "2021" license = "MIT OR Apache-2.0" description = "macros for creating the entry point and tasks for embassy-executor" diff --git a/embassy-executor/CHANGELOG.md b/embassy-executor/CHANGELOG.md index 5582b56ec..ac1c8cb0c 100644 --- a/embassy-executor/CHANGELOG.md +++ b/embassy-executor/CHANGELOG.md @@ -7,10 +7,19 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## Unreleased +## 0.6.1 - 2024-10-21 + +- Soundness fix: Deny using `impl Trait` in task arguments. This was previously accidentally allowed when not using the `nightly` feature, + and could cause out of bounds memory accesses if spawning the same task mulitple times with different underlying types + for the `impl Trait`. Affected versions are 0.4.x, 0.5.x and 0.6.0, which have been yanked. +- Add an architecture-agnostic executor that spins waiting for tasks to run, enabled with the `arch-spin` feature. +- Update for breaking change in the nightly waker_getters API. The `nightly` feature now requires`nightly-2024-09-06` or newer. +- Improve macro error messages. + ## 0.6.0 - 2024-08-05 - Add collapse_debuginfo to fmt.rs macros. -- initial support for avr +- initial support for AVR - use nightly waker_getters APIs ## 0.5.0 - 2024-01-11 diff --git a/embassy-executor/Cargo.toml b/embassy-executor/Cargo.toml index e138f93b0..22a176621 100644 --- a/embassy-executor/Cargo.toml +++ b/embassy-executor/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "embassy-executor" -version = "0.6.0" +version = "0.6.1" edition = "2021" license = "MIT OR Apache-2.0" description = "async/await executor designed for embedded usage" @@ -33,7 +33,7 @@ defmt = { version = "0.3", optional = true } log = { version = "0.4.14", optional = true } rtos-trace = { version = "0.1.2", optional = true } -embassy-executor-macros = { version = "0.5.0", path = "../embassy-executor-macros" } +embassy-executor-macros = { version = "0.6.1", path = "../embassy-executor-macros" } embassy-time-driver = { version = "0.1.0", path = "../embassy-time-driver", optional = true } embassy-time-queue-driver = { version = "0.1.0", path = "../embassy-time-queue-driver", optional = true } critical-section = "1.1" diff --git a/embassy-rp/Cargo.toml b/embassy-rp/Cargo.toml index 54de238b3..547d64e43 100644 --- a/embassy-rp/Cargo.toml +++ b/embassy-rp/Cargo.toml @@ -147,5 +147,5 @@ rp-binary-info = { version = "0.1.0", optional = true } smart-leds = "0.4.0" [dev-dependencies] -embassy-executor = { version = "0.6.0", path = "../embassy-executor", features = ["arch-std", "executor-thread"] } +embassy-executor = { version = "0.6.1", path = "../embassy-executor", features = ["arch-std", "executor-thread"] } static_cell = { version = "2" } diff --git a/embassy-stm32/Cargo.toml b/embassy-stm32/Cargo.toml index 53ec1b27f..9fdc799d8 100644 --- a/embassy-stm32/Cargo.toml +++ b/embassy-stm32/Cargo.toml @@ -51,7 +51,7 @@ embassy-embedded-hal = {version = "0.2.0", path = "../embassy-embedded-hal", def embassy-net-driver = { version = "0.2.0", path = "../embassy-net-driver" } embassy-usb-driver = {version = "0.1.0", path = "../embassy-usb-driver" } embassy-usb-synopsys-otg = {version = "0.1.0", path = "../embassy-usb-synopsys-otg" } -embassy-executor = { version = "0.6.0", path = "../embassy-executor", optional = true } +embassy-executor = { version = "0.6.1", path = "../embassy-executor", optional = true } embedded-hal-02 = { package = "embedded-hal", version = "0.2.6", features = ["unproven"] } embedded-hal-1 = { package = "embedded-hal", version = "1.0" } diff --git a/embassy-time/Cargo.toml b/embassy-time/Cargo.toml index c3b4e4e3a..dc3561145 100644 --- a/embassy-time/Cargo.toml +++ b/embassy-time/Cargo.toml @@ -431,4 +431,4 @@ wasm-timer = { version = "0.2.5", optional = true } [dev-dependencies] serial_test = "0.9" critical-section = { version = "1.1", features = ["std"] } -embassy-executor = { version = "0.6.0", path = "../embassy-executor" } +embassy-executor = { version = "0.6.1", path = "../embassy-executor" } diff --git a/examples/boot/application/nrf/Cargo.toml b/examples/boot/application/nrf/Cargo.toml index 93e49faef..3815ac196 100644 --- a/examples/boot/application/nrf/Cargo.toml +++ b/examples/boot/application/nrf/Cargo.toml @@ -6,7 +6,7 @@ license = "MIT OR Apache-2.0" [dependencies] embassy-sync = { version = "0.6.0", path = "../../../../embassy-sync" } -embassy-executor = { version = "0.6.0", path = "../../../../embassy-executor", features = ["task-arena-size-16384", "arch-cortex-m", "executor-thread", "integrated-timers", "arch-cortex-m", "executor-thread"] } +embassy-executor = { version = "0.6.1", path = "../../../../embassy-executor", features = ["task-arena-size-16384", "arch-cortex-m", "executor-thread", "integrated-timers", "arch-cortex-m", "executor-thread"] } embassy-time = { version = "0.3.2", path = "../../../../embassy-time", features = [] } embassy-nrf = { version = "0.2.0", path = "../../../../embassy-nrf", features = ["time-driver-rtc1", "gpiote", ] } embassy-boot = { version = "0.3.0", path = "../../../../embassy-boot", features = [] } diff --git a/examples/boot/application/rp/Cargo.toml b/examples/boot/application/rp/Cargo.toml index 8bb8afdfe..86ea9fc2a 100644 --- a/examples/boot/application/rp/Cargo.toml +++ b/examples/boot/application/rp/Cargo.toml @@ -6,7 +6,7 @@ license = "MIT OR Apache-2.0" [dependencies] embassy-sync = { version = "0.6.0", path = "../../../../embassy-sync" } -embassy-executor = { version = "0.6.0", path = "../../../../embassy-executor", features = ["task-arena-size-16384", "arch-cortex-m", "executor-thread", "integrated-timers", "arch-cortex-m", "executor-thread"] } +embassy-executor = { version = "0.6.1", path = "../../../../embassy-executor", features = ["task-arena-size-16384", "arch-cortex-m", "executor-thread", "integrated-timers", "arch-cortex-m", "executor-thread"] } embassy-time = { version = "0.3.2", path = "../../../../embassy-time", features = [] } embassy-rp = { version = "0.2.0", path = "../../../../embassy-rp", features = ["time-driver", "rp2040"] } embassy-boot-rp = { version = "0.3.0", path = "../../../../embassy-boot-rp", features = [] } diff --git a/examples/boot/application/stm32f3/Cargo.toml b/examples/boot/application/stm32f3/Cargo.toml index 1c2934298..29049e58c 100644 --- a/examples/boot/application/stm32f3/Cargo.toml +++ b/examples/boot/application/stm32f3/Cargo.toml @@ -6,7 +6,7 @@ license = "MIT OR Apache-2.0" [dependencies] embassy-sync = { version = "0.6.0", path = "../../../../embassy-sync" } -embassy-executor = { version = "0.6.0", path = "../../../../embassy-executor", features = ["task-arena-size-8192", "arch-cortex-m", "executor-thread", "integrated-timers"] } +embassy-executor = { version = "0.6.1", path = "../../../../embassy-executor", features = ["task-arena-size-8192", "arch-cortex-m", "executor-thread", "integrated-timers"] } embassy-time = { version = "0.3.2", path = "../../../../embassy-time", features = [ "tick-hz-32_768"] } embassy-stm32 = { version = "0.1.0", path = "../../../../embassy-stm32", features = ["stm32f303re", "time-driver-any", "exti"] } embassy-boot-stm32 = { version = "0.2.0", path = "../../../../embassy-boot-stm32" } diff --git a/examples/boot/application/stm32f7/Cargo.toml b/examples/boot/application/stm32f7/Cargo.toml index 09e34c7df..ed102b073 100644 --- a/examples/boot/application/stm32f7/Cargo.toml +++ b/examples/boot/application/stm32f7/Cargo.toml @@ -6,7 +6,7 @@ license = "MIT OR Apache-2.0" [dependencies] embassy-sync = { version = "0.6.0", path = "../../../../embassy-sync" } -embassy-executor = { version = "0.6.0", path = "../../../../embassy-executor", features = ["task-arena-size-8192", "arch-cortex-m", "executor-thread", "integrated-timers"] } +embassy-executor = { version = "0.6.1", path = "../../../../embassy-executor", features = ["task-arena-size-8192", "arch-cortex-m", "executor-thread", "integrated-timers"] } embassy-time = { version = "0.3.2", path = "../../../../embassy-time", features = [ "tick-hz-32_768"] } embassy-stm32 = { version = "0.1.0", path = "../../../../embassy-stm32", features = ["stm32f767zi", "time-driver-any", "exti"] } embassy-boot-stm32 = { version = "0.2.0", path = "../../../../embassy-boot-stm32", features = [] } diff --git a/examples/boot/application/stm32h7/Cargo.toml b/examples/boot/application/stm32h7/Cargo.toml index 5e7f4d5e7..cc705b1d6 100644 --- a/examples/boot/application/stm32h7/Cargo.toml +++ b/examples/boot/application/stm32h7/Cargo.toml @@ -6,7 +6,7 @@ license = "MIT OR Apache-2.0" [dependencies] embassy-sync = { version = "0.6.0", path = "../../../../embassy-sync" } -embassy-executor = { version = "0.6.0", path = "../../../../embassy-executor", features = ["task-arena-size-8192", "arch-cortex-m", "executor-thread", "integrated-timers"] } +embassy-executor = { version = "0.6.1", path = "../../../../embassy-executor", features = ["task-arena-size-8192", "arch-cortex-m", "executor-thread", "integrated-timers"] } embassy-time = { version = "0.3.2", path = "../../../../embassy-time", features = [ "tick-hz-32_768"] } embassy-stm32 = { version = "0.1.0", path = "../../../../embassy-stm32", features = ["stm32h743zi", "time-driver-any", "exti"] } embassy-boot-stm32 = { version = "0.2.0", path = "../../../../embassy-boot-stm32", features = [] } diff --git a/examples/boot/application/stm32l0/Cargo.toml b/examples/boot/application/stm32l0/Cargo.toml index 60fdcfafb..17fc1d7d0 100644 --- a/examples/boot/application/stm32l0/Cargo.toml +++ b/examples/boot/application/stm32l0/Cargo.toml @@ -6,7 +6,7 @@ license = "MIT OR Apache-2.0" [dependencies] embassy-sync = { version = "0.6.0", path = "../../../../embassy-sync" } -embassy-executor = { version = "0.6.0", path = "../../../../embassy-executor", features = ["task-arena-size-8192", "arch-cortex-m", "executor-thread", "integrated-timers"] } +embassy-executor = { version = "0.6.1", path = "../../../../embassy-executor", features = ["task-arena-size-8192", "arch-cortex-m", "executor-thread", "integrated-timers"] } embassy-time = { version = "0.3.2", path = "../../../../embassy-time", features = [ "tick-hz-32_768"] } embassy-stm32 = { version = "0.1.0", path = "../../../../embassy-stm32", features = ["stm32l072cz", "time-driver-any", "exti", "memory-x"] } embassy-boot-stm32 = { version = "0.2.0", path = "../../../../embassy-boot-stm32", features = [] } diff --git a/examples/boot/application/stm32l1/Cargo.toml b/examples/boot/application/stm32l1/Cargo.toml index fe3ab2c04..fd2c19f99 100644 --- a/examples/boot/application/stm32l1/Cargo.toml +++ b/examples/boot/application/stm32l1/Cargo.toml @@ -6,7 +6,7 @@ license = "MIT OR Apache-2.0" [dependencies] embassy-sync = { version = "0.6.0", path = "../../../../embassy-sync" } -embassy-executor = { version = "0.6.0", path = "../../../../embassy-executor", features = ["task-arena-size-8192", "arch-cortex-m", "executor-thread", "integrated-timers"] } +embassy-executor = { version = "0.6.1", path = "../../../../embassy-executor", features = ["task-arena-size-8192", "arch-cortex-m", "executor-thread", "integrated-timers"] } embassy-time = { version = "0.3.2", path = "../../../../embassy-time", features = [ "tick-hz-32_768"] } embassy-stm32 = { version = "0.1.0", path = "../../../../embassy-stm32", features = ["stm32l151cb-a", "time-driver-any", "exti"] } embassy-boot-stm32 = { version = "0.2.0", path = "../../../../embassy-boot-stm32", features = [] } diff --git a/examples/boot/application/stm32l4/Cargo.toml b/examples/boot/application/stm32l4/Cargo.toml index 169856358..3852261ac 100644 --- a/examples/boot/application/stm32l4/Cargo.toml +++ b/examples/boot/application/stm32l4/Cargo.toml @@ -6,7 +6,7 @@ license = "MIT OR Apache-2.0" [dependencies] embassy-sync = { version = "0.6.0", path = "../../../../embassy-sync" } -embassy-executor = { version = "0.6.0", path = "../../../../embassy-executor", features = ["task-arena-size-8192", "arch-cortex-m", "executor-thread", "integrated-timers"] } +embassy-executor = { version = "0.6.1", path = "../../../../embassy-executor", features = ["task-arena-size-8192", "arch-cortex-m", "executor-thread", "integrated-timers"] } embassy-time = { version = "0.3.2", path = "../../../../embassy-time", features = [ "tick-hz-32_768"] } embassy-stm32 = { version = "0.1.0", path = "../../../../embassy-stm32", features = ["stm32l475vg", "time-driver-any", "exti"] } embassy-boot-stm32 = { version = "0.2.0", path = "../../../../embassy-boot-stm32", features = [] } diff --git a/examples/boot/application/stm32wb-dfu/Cargo.toml b/examples/boot/application/stm32wb-dfu/Cargo.toml index 7cef8fe0d..2d89f6d42 100644 --- a/examples/boot/application/stm32wb-dfu/Cargo.toml +++ b/examples/boot/application/stm32wb-dfu/Cargo.toml @@ -6,7 +6,7 @@ license = "MIT OR Apache-2.0" [dependencies] embassy-sync = { version = "0.6.0", path = "../../../../embassy-sync" } -embassy-executor = { version = "0.6.0", path = "../../../../embassy-executor", features = ["task-arena-size-8192", "arch-cortex-m", "executor-thread", "integrated-timers"] } +embassy-executor = { version = "0.6.1", path = "../../../../embassy-executor", features = ["task-arena-size-8192", "arch-cortex-m", "executor-thread", "integrated-timers"] } embassy-time = { version = "0.3.2", path = "../../../../embassy-time", features = [ "tick-hz-32_768"] } embassy-stm32 = { version = "0.1.0", path = "../../../../embassy-stm32", features = ["stm32wb55rg", "time-driver-any", "exti"] } embassy-boot-stm32 = { version = "0.2.0", path = "../../../../embassy-boot-stm32", features = [] } diff --git a/examples/boot/application/stm32wl/Cargo.toml b/examples/boot/application/stm32wl/Cargo.toml index 860a835a9..e07d97d86 100644 --- a/examples/boot/application/stm32wl/Cargo.toml +++ b/examples/boot/application/stm32wl/Cargo.toml @@ -6,7 +6,7 @@ license = "MIT OR Apache-2.0" [dependencies] embassy-sync = { version = "0.6.0", path = "../../../../embassy-sync" } -embassy-executor = { version = "0.6.0", path = "../../../../embassy-executor", features = ["task-arena-size-8192", "arch-cortex-m", "executor-thread", "integrated-timers"] } +embassy-executor = { version = "0.6.1", path = "../../../../embassy-executor", features = ["task-arena-size-8192", "arch-cortex-m", "executor-thread", "integrated-timers"] } embassy-time = { version = "0.3.2", path = "../../../../embassy-time", features = [ "tick-hz-32_768"] } embassy-stm32 = { version = "0.1.0", path = "../../../../embassy-stm32", features = ["stm32wl55jc-cm4", "time-driver-any", "exti"] } embassy-boot-stm32 = { version = "0.2.0", path = "../../../../embassy-boot-stm32", features = [] } diff --git a/examples/lpc55s69/Cargo.toml b/examples/lpc55s69/Cargo.toml index 14ec2d47e..a69007a2c 100644 --- a/examples/lpc55s69/Cargo.toml +++ b/examples/lpc55s69/Cargo.toml @@ -7,7 +7,7 @@ license = "MIT OR Apache-2.0" [dependencies] embassy-nxp = { version = "0.1.0", path = "../../embassy-nxp", features = ["rt"] } -embassy-executor = { version = "0.6.0", path = "../../embassy-executor", features = ["task-arena-size-32768", "arch-cortex-m", "executor-thread", "executor-interrupt"] } +embassy-executor = { version = "0.6.1", path = "../../embassy-executor", features = ["task-arena-size-32768", "arch-cortex-m", "executor-thread", "executor-interrupt"] } embassy-sync = { version = "0.6.0", path = "../../embassy-sync", features = ["defmt"] } embassy-time = { version = "0.3.0", path = "../../embassy-time", features = ["defmt"] } panic-halt = "0.2.0" diff --git a/examples/nrf-rtos-trace/Cargo.toml b/examples/nrf-rtos-trace/Cargo.toml index 98a678815..2358ced56 100644 --- a/examples/nrf-rtos-trace/Cargo.toml +++ b/examples/nrf-rtos-trace/Cargo.toml @@ -16,7 +16,7 @@ log = [ [dependencies] embassy-sync = { version = "0.6.0", path = "../../embassy-sync" } -embassy-executor = { version = "0.6.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "rtos-trace", "integrated-timers"] } +embassy-executor = { version = "0.6.1", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "rtos-trace", "integrated-timers"] } embassy-time = { version = "0.3.2", path = "../../embassy-time" } embassy-nrf = { version = "0.2.0", path = "../../embassy-nrf", features = ["nrf52840", "time-driver-rtc1", "gpiote", "unstable-pac"] } diff --git a/examples/nrf51/Cargo.toml b/examples/nrf51/Cargo.toml index 93a19bea7..f94026158 100644 --- a/examples/nrf51/Cargo.toml +++ b/examples/nrf51/Cargo.toml @@ -5,7 +5,7 @@ version = "0.1.0" license = "MIT OR Apache-2.0" [dependencies] -embassy-executor = { version = "0.6.0", path = "../../embassy-executor", features = ["task-arena-size-4096", "arch-cortex-m", "executor-thread", "executor-interrupt", "defmt", "integrated-timers"] } +embassy-executor = { version = "0.6.1", path = "../../embassy-executor", features = ["task-arena-size-4096", "arch-cortex-m", "executor-thread", "executor-interrupt", "defmt", "integrated-timers"] } embassy-time = { version = "0.3.2", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime"] } embassy-nrf = { version = "0.2.0", path = "../../embassy-nrf", features = ["defmt", "nrf51", "gpiote", "time-driver-rtc1", "unstable-pac", "time", "rt"] } diff --git a/examples/nrf52810/Cargo.toml b/examples/nrf52810/Cargo.toml index 0e3e81c3f..867ead6d8 100644 --- a/examples/nrf52810/Cargo.toml +++ b/examples/nrf52810/Cargo.toml @@ -7,7 +7,7 @@ license = "MIT OR Apache-2.0" [dependencies] embassy-futures = { version = "0.1.0", path = "../../embassy-futures" } embassy-sync = { version = "0.6.0", path = "../../embassy-sync", features = ["defmt"] } -embassy-executor = { version = "0.6.0", path = "../../embassy-executor", features = ["task-arena-size-8192", "arch-cortex-m", "executor-thread", "executor-interrupt", "defmt", "integrated-timers"] } +embassy-executor = { version = "0.6.1", path = "../../embassy-executor", features = ["task-arena-size-8192", "arch-cortex-m", "executor-thread", "executor-interrupt", "defmt", "integrated-timers"] } embassy-time = { version = "0.3.2", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime"] } embassy-nrf = { version = "0.2.0", path = "../../embassy-nrf", features = ["defmt", "nrf52810", "time-driver-rtc1", "gpiote", "unstable-pac", "time"] } diff --git a/examples/nrf52840/Cargo.toml b/examples/nrf52840/Cargo.toml index 17fa6234d..cb05b7204 100644 --- a/examples/nrf52840/Cargo.toml +++ b/examples/nrf52840/Cargo.toml @@ -7,7 +7,7 @@ license = "MIT OR Apache-2.0" [dependencies] embassy-futures = { version = "0.1.0", path = "../../embassy-futures" } embassy-sync = { version = "0.6.0", path = "../../embassy-sync", features = ["defmt"] } -embassy-executor = { version = "0.6.0", path = "../../embassy-executor", features = ["task-arena-size-32768", "arch-cortex-m", "executor-thread", "executor-interrupt", "defmt", "integrated-timers"] } +embassy-executor = { version = "0.6.1", path = "../../embassy-executor", features = ["task-arena-size-32768", "arch-cortex-m", "executor-thread", "executor-interrupt", "defmt", "integrated-timers"] } embassy-time = { version = "0.3.2", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime"] } embassy-nrf = { version = "0.2.0", path = "../../embassy-nrf", features = ["defmt", "nrf52840", "time-driver-rtc1", "gpiote", "unstable-pac", "time"] } embassy-net = { version = "0.4.0", path = "../../embassy-net", features = ["defmt", "tcp", "dhcpv4", "medium-ethernet"] } diff --git a/examples/nrf5340/Cargo.toml b/examples/nrf5340/Cargo.toml index 0da85be07..2387eaa58 100644 --- a/examples/nrf5340/Cargo.toml +++ b/examples/nrf5340/Cargo.toml @@ -7,7 +7,7 @@ license = "MIT OR Apache-2.0" [dependencies] embassy-futures = { version = "0.1.0", path = "../../embassy-futures" } embassy-sync = { version = "0.6.0", path = "../../embassy-sync", features = ["defmt"] } -embassy-executor = { version = "0.6.0", path = "../../embassy-executor", features = ["task-arena-size-32768", "arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] } +embassy-executor = { version = "0.6.1", path = "../../embassy-executor", features = ["task-arena-size-32768", "arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] } embassy-time = { version = "0.3.2", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime"] } embassy-nrf = { version = "0.2.0", path = "../../embassy-nrf", features = ["defmt", "nrf5340-app-s", "time-driver-rtc1", "gpiote", "unstable-pac"] } embassy-net = { version = "0.4.0", path = "../../embassy-net", features = ["defmt", "tcp", "dhcpv4", "medium-ethernet"] } diff --git a/examples/nrf9151/ns/Cargo.toml b/examples/nrf9151/ns/Cargo.toml index 17fe27b67..78260dae3 100644 --- a/examples/nrf9151/ns/Cargo.toml +++ b/examples/nrf9151/ns/Cargo.toml @@ -5,7 +5,7 @@ version = "0.1.0" license = "MIT OR Apache-2.0" [dependencies] -embassy-executor = { version = "0.6.0", path = "../../../embassy-executor", features = ["task-arena-size-32768", "arch-cortex-m", "executor-thread", "executor-interrupt", "defmt", "integrated-timers"] } +embassy-executor = { version = "0.6.1", path = "../../../embassy-executor", features = ["task-arena-size-32768", "arch-cortex-m", "executor-thread", "executor-interrupt", "defmt", "integrated-timers"] } embassy-time = { version = "0.3.2", path = "../../../embassy-time", features = ["defmt", "defmt-timestamp-uptime"] } embassy-nrf = { version = "0.2.0", path = "../../../embassy-nrf", features = ["defmt", "nrf9120-ns", "time-driver-rtc1", "gpiote", "unstable-pac", "time"] } diff --git a/examples/nrf9151/s/Cargo.toml b/examples/nrf9151/s/Cargo.toml index 7253fc4be..17ead4ab4 100644 --- a/examples/nrf9151/s/Cargo.toml +++ b/examples/nrf9151/s/Cargo.toml @@ -5,7 +5,7 @@ version = "0.1.0" license = "MIT OR Apache-2.0" [dependencies] -embassy-executor = { version = "0.6.0", path = "../../../embassy-executor", features = ["task-arena-size-32768", "arch-cortex-m", "executor-thread", "executor-interrupt", "defmt", "integrated-timers"] } +embassy-executor = { version = "0.6.1", path = "../../../embassy-executor", features = ["task-arena-size-32768", "arch-cortex-m", "executor-thread", "executor-interrupt", "defmt", "integrated-timers"] } embassy-time = { version = "0.3.2", path = "../../../embassy-time", features = ["defmt", "defmt-timestamp-uptime"] } embassy-nrf = { version = "0.2.0", path = "../../../embassy-nrf", features = ["defmt", "nrf9120-s", "time-driver-rtc1", "gpiote", "unstable-pac", "time"] } diff --git a/examples/nrf9160/Cargo.toml b/examples/nrf9160/Cargo.toml index 9aeb99317..2572f3353 100644 --- a/examples/nrf9160/Cargo.toml +++ b/examples/nrf9160/Cargo.toml @@ -5,7 +5,7 @@ version = "0.1.0" license = "MIT OR Apache-2.0" [dependencies] -embassy-executor = { version = "0.6.0", path = "../../embassy-executor", features = ["task-arena-size-32768", "arch-cortex-m", "executor-thread", "executor-interrupt", "defmt", "integrated-timers"] } +embassy-executor = { version = "0.6.1", path = "../../embassy-executor", features = ["task-arena-size-32768", "arch-cortex-m", "executor-thread", "executor-interrupt", "defmt", "integrated-timers"] } embassy-time = { version = "0.3.2", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime"] } embassy-nrf = { version = "0.2.0", path = "../../embassy-nrf", features = ["defmt", "nrf9160-s", "time-driver-rtc1", "gpiote", "unstable-pac", "time"] } embassy-net-nrf91 = { version = "0.1.0", path = "../../embassy-net-nrf91", features = ["defmt"] } diff --git a/examples/rp/Cargo.toml b/examples/rp/Cargo.toml index a220b9a77..8e5f81b7e 100644 --- a/examples/rp/Cargo.toml +++ b/examples/rp/Cargo.toml @@ -8,7 +8,7 @@ license = "MIT OR Apache-2.0" [dependencies] embassy-embedded-hal = { version = "0.2.0", path = "../../embassy-embedded-hal", features = ["defmt"] } embassy-sync = { version = "0.6.0", path = "../../embassy-sync", features = ["defmt"] } -embassy-executor = { version = "0.6.0", path = "../../embassy-executor", features = ["task-arena-size-98304", "arch-cortex-m", "executor-thread", "executor-interrupt", "defmt", "integrated-timers"] } +embassy-executor = { version = "0.6.1", path = "../../embassy-executor", features = ["task-arena-size-98304", "arch-cortex-m", "executor-thread", "executor-interrupt", "defmt", "integrated-timers"] } embassy-time = { version = "0.3.2", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime"] } embassy-rp = { version = "0.2.0", path = "../../embassy-rp", features = ["defmt", "unstable-pac", "time-driver", "critical-section-impl", "rp2040"] } embassy-usb = { version = "0.3.0", path = "../../embassy-usb", features = ["defmt"] } diff --git a/examples/rp23/Cargo.toml b/examples/rp23/Cargo.toml index 08646463c..9ae75e16c 100644 --- a/examples/rp23/Cargo.toml +++ b/examples/rp23/Cargo.toml @@ -8,7 +8,7 @@ license = "MIT OR Apache-2.0" [dependencies] embassy-embedded-hal = { version = "0.2.0", path = "../../embassy-embedded-hal", features = ["defmt"] } embassy-sync = { version = "0.6.0", path = "../../embassy-sync", features = ["defmt"] } -embassy-executor = { version = "0.6.0", path = "../../embassy-executor", features = ["task-arena-size-98304", "arch-cortex-m", "executor-thread", "executor-interrupt", "defmt", "integrated-timers"] } +embassy-executor = { version = "0.6.1", path = "../../embassy-executor", features = ["task-arena-size-98304", "arch-cortex-m", "executor-thread", "executor-interrupt", "defmt", "integrated-timers"] } embassy-time = { version = "0.3.2", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime"] } embassy-rp = { version = "0.2.0", path = "../../embassy-rp", features = ["defmt", "unstable-pac", "time-driver", "critical-section-impl", "rp235xa", "binary-info"] } embassy-usb = { version = "0.3.0", path = "../../embassy-usb", features = ["defmt"] } diff --git a/examples/std/Cargo.toml b/examples/std/Cargo.toml index 87491b1d2..27d0062fb 100644 --- a/examples/std/Cargo.toml +++ b/examples/std/Cargo.toml @@ -6,7 +6,7 @@ license = "MIT OR Apache-2.0" [dependencies] embassy-sync = { version = "0.6.0", path = "../../embassy-sync", features = ["log"] } -embassy-executor = { version = "0.6.0", path = "../../embassy-executor", features = ["task-arena-size-32768", "arch-std", "executor-thread", "log", "integrated-timers"] } +embassy-executor = { version = "0.6.1", path = "../../embassy-executor", features = ["task-arena-size-32768", "arch-std", "executor-thread", "log", "integrated-timers"] } embassy-time = { version = "0.3.2", path = "../../embassy-time", features = ["log", "std", ] } embassy-net = { version = "0.4.0", path = "../../embassy-net", features=[ "std", "log", "medium-ethernet", "medium-ip", "tcp", "udp", "dns", "dhcpv4", "proto-ipv6"] } embassy-net-tuntap = { version = "0.1.0", path = "../../embassy-net-tuntap" } diff --git a/examples/stm32c0/Cargo.toml b/examples/stm32c0/Cargo.toml index 9102467eb..46a25cc4d 100644 --- a/examples/stm32c0/Cargo.toml +++ b/examples/stm32c0/Cargo.toml @@ -8,7 +8,7 @@ license = "MIT OR Apache-2.0" # Change stm32c031c6 to your chip name, if necessary. embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = [ "defmt", "time-driver-any", "stm32c031c6", "memory-x", "unstable-pac", "exti"] } embassy-sync = { version = "0.6.0", path = "../../embassy-sync", features = ["defmt"] } -embassy-executor = { version = "0.6.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] } +embassy-executor = { version = "0.6.1", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] } embassy-time = { version = "0.3.2", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } defmt = "0.3" diff --git a/examples/stm32f0/Cargo.toml b/examples/stm32f0/Cargo.toml index 724efdaff..93d9ab7ce 100644 --- a/examples/stm32f0/Cargo.toml +++ b/examples/stm32f0/Cargo.toml @@ -13,7 +13,7 @@ defmt = "0.3" defmt-rtt = "0.4" panic-probe = { version = "0.3", features = ["print-defmt"] } embassy-sync = { version = "0.6.0", path = "../../embassy-sync", features = ["defmt"] } -embassy-executor = { version = "0.6.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "executor-interrupt", "defmt", "integrated-timers"] } +embassy-executor = { version = "0.6.1", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "executor-interrupt", "defmt", "integrated-timers"] } embassy-time = { version = "0.3.2", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } static_cell = "2" portable-atomic = { version = "1.5", features = ["unsafe-assume-single-core"] } diff --git a/examples/stm32f1/Cargo.toml b/examples/stm32f1/Cargo.toml index 0084651a3..a75d0fe4c 100644 --- a/examples/stm32f1/Cargo.toml +++ b/examples/stm32f1/Cargo.toml @@ -8,7 +8,7 @@ license = "MIT OR Apache-2.0" # Change stm32f103c8 to your chip name, if necessary. embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = [ "defmt", "stm32f103c8", "unstable-pac", "memory-x", "time-driver-any" ] } embassy-sync = { version = "0.6.0", path = "../../embassy-sync", features = ["defmt"] } -embassy-executor = { version = "0.6.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] } +embassy-executor = { version = "0.6.1", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] } embassy-time = { version = "0.3.2", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } embassy-usb = { version = "0.3.0", path = "../../embassy-usb", features = ["defmt"] } embassy-futures = { version = "0.1.0", path = "../../embassy-futures" } diff --git a/examples/stm32f2/Cargo.toml b/examples/stm32f2/Cargo.toml index 60eb0eb93..166729211 100644 --- a/examples/stm32f2/Cargo.toml +++ b/examples/stm32f2/Cargo.toml @@ -8,7 +8,7 @@ license = "MIT OR Apache-2.0" # Change stm32f207zg to your chip name, if necessary. embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = [ "defmt", "stm32f207zg", "unstable-pac", "memory-x", "time-driver-any", "exti"] } embassy-sync = { version = "0.6.0", path = "../../embassy-sync", features = ["defmt"] } -embassy-executor = { version = "0.6.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] } +embassy-executor = { version = "0.6.1", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] } embassy-time = { version = "0.3.2", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } defmt = "0.3" diff --git a/examples/stm32f3/Cargo.toml b/examples/stm32f3/Cargo.toml index 7fda410d9..e60ea80c6 100644 --- a/examples/stm32f3/Cargo.toml +++ b/examples/stm32f3/Cargo.toml @@ -8,7 +8,7 @@ license = "MIT OR Apache-2.0" # Change stm32f303ze to your chip name, if necessary. embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = [ "defmt", "stm32f303ze", "unstable-pac", "memory-x", "time-driver-any", "exti"] } embassy-sync = { version = "0.6.0", path = "../../embassy-sync", features = ["defmt"] } -embassy-executor = { version = "0.6.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "executor-interrupt", "defmt", "integrated-timers"] } +embassy-executor = { version = "0.6.1", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "executor-interrupt", "defmt", "integrated-timers"] } embassy-time = { version = "0.3.2", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } embassy-usb = { version = "0.3.0", path = "../../embassy-usb", features = ["defmt"] } embassy-futures = { version = "0.1.0", path = "../../embassy-futures" } diff --git a/examples/stm32f334/Cargo.toml b/examples/stm32f334/Cargo.toml index 1cc0a97da..4ba993009 100644 --- a/examples/stm32f334/Cargo.toml +++ b/examples/stm32f334/Cargo.toml @@ -6,7 +6,7 @@ license = "MIT OR Apache-2.0" [dependencies] embassy-sync = { version = "0.6.0", path = "../../embassy-sync", features = ["defmt"] } -embassy-executor = { version = "0.6.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "executor-interrupt", "defmt", "integrated-timers"] } +embassy-executor = { version = "0.6.1", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "executor-interrupt", "defmt", "integrated-timers"] } embassy-time = { version = "0.3.2", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = [ "defmt", "stm32f334r8", "unstable-pac", "memory-x", "time-driver-any", "exti"] } embassy-usb = { version = "0.3.0", path = "../../embassy-usb", features = ["defmt"] } diff --git a/examples/stm32f4/Cargo.toml b/examples/stm32f4/Cargo.toml index b85361596..975e47398 100644 --- a/examples/stm32f4/Cargo.toml +++ b/examples/stm32f4/Cargo.toml @@ -8,7 +8,7 @@ license = "MIT OR Apache-2.0" # Change stm32f429zi to your chip name, if necessary. embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["defmt", "stm32f429zi", "unstable-pac", "memory-x", "time-driver-any", "exti", "chrono"] } embassy-sync = { version = "0.6.0", path = "../../embassy-sync", features = ["defmt"] } -embassy-executor = { version = "0.6.0", path = "../../embassy-executor", features = ["task-arena-size-32768", "arch-cortex-m", "executor-thread", "executor-interrupt", "defmt", "integrated-timers"] } +embassy-executor = { version = "0.6.1", path = "../../embassy-executor", features = ["task-arena-size-32768", "arch-cortex-m", "executor-thread", "executor-interrupt", "defmt", "integrated-timers"] } embassy-time = { version = "0.3.2", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } embassy-usb = { version = "0.3.0", path = "../../embassy-usb", features = ["defmt" ] } embassy-net = { version = "0.4.0", path = "../../embassy-net", features = ["defmt", "tcp", "dhcpv4", "medium-ethernet", ] } diff --git a/examples/stm32f469/Cargo.toml b/examples/stm32f469/Cargo.toml index 6a5bd0b29..4e1a778cd 100644 --- a/examples/stm32f469/Cargo.toml +++ b/examples/stm32f469/Cargo.toml @@ -7,7 +7,7 @@ license = "MIT OR Apache-2.0" [dependencies] # Specific examples only for stm32f469 embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["defmt", "stm32f469ni", "unstable-pac", "memory-x", "time-driver-any", "exti", "chrono"] } -embassy-executor = { version = "0.6.0", path = "../../embassy-executor", features = ["task-arena-size-32768", "arch-cortex-m", "executor-thread", "executor-interrupt", "defmt", "integrated-timers"] } +embassy-executor = { version = "0.6.1", path = "../../embassy-executor", features = ["task-arena-size-32768", "arch-cortex-m", "executor-thread", "executor-interrupt", "defmt", "integrated-timers"] } embassy-time = { version = "0.3.2", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } defmt = "0.3" diff --git a/examples/stm32f7/Cargo.toml b/examples/stm32f7/Cargo.toml index 8c591ebd2..879b05d91 100644 --- a/examples/stm32f7/Cargo.toml +++ b/examples/stm32f7/Cargo.toml @@ -8,7 +8,7 @@ license = "MIT OR Apache-2.0" # Change stm32f777zi to your chip name, if necessary. embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["defmt", "stm32f777zi", "memory-x", "unstable-pac", "time-driver-any", "exti"] } embassy-sync = { version = "0.6.0", path = "../../embassy-sync", features = ["defmt"] } -embassy-executor = { version = "0.6.0", path = "../../embassy-executor", features = ["task-arena-size-32768", "arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] } +embassy-executor = { version = "0.6.1", path = "../../embassy-executor", features = ["task-arena-size-32768", "arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] } embassy-time = { version = "0.3.2", 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"] } embedded-io-async = { version = "0.6.1" } diff --git a/examples/stm32g0/Cargo.toml b/examples/stm32g0/Cargo.toml index a50074ce0..77f70e57d 100644 --- a/examples/stm32g0/Cargo.toml +++ b/examples/stm32g0/Cargo.toml @@ -8,7 +8,7 @@ license = "MIT OR Apache-2.0" # Change stm32g0b1re to your chip name, if necessary. embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = [ "defmt", "time-driver-any", "stm32g0b1re", "memory-x", "unstable-pac", "exti"] } embassy-sync = { version = "0.6.0", path = "../../embassy-sync", features = ["defmt"] } -embassy-executor = { version = "0.6.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] } +embassy-executor = { version = "0.6.1", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] } embassy-time = { version = "0.3.2", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } embassy-usb = { version = "0.3.0", path = "../../embassy-usb", default-features = false, features = ["defmt"] } embassy-futures = { version = "0.1.0", path = "../../embassy-futures" } diff --git a/examples/stm32g4/Cargo.toml b/examples/stm32g4/Cargo.toml index 2768147a1..86d29f94e 100644 --- a/examples/stm32g4/Cargo.toml +++ b/examples/stm32g4/Cargo.toml @@ -8,7 +8,7 @@ license = "MIT OR Apache-2.0" # Change stm32g491re to your chip name, if necessary. embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = [ "defmt", "time-driver-any", "stm32g491re", "memory-x", "unstable-pac", "exti"] } embassy-sync = { version = "0.6.0", path = "../../embassy-sync", features = ["defmt"] } -embassy-executor = { version = "0.6.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] } +embassy-executor = { version = "0.6.1", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] } embassy-time = { version = "0.3.2", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } embassy-usb = { version = "0.3.0", path = "../../embassy-usb", features = ["defmt"] } embassy-futures = { version = "0.1.0", path = "../../embassy-futures" } diff --git a/examples/stm32h5/Cargo.toml b/examples/stm32h5/Cargo.toml index 1aa264ab2..9ca8bc969 100644 --- a/examples/stm32h5/Cargo.toml +++ b/examples/stm32h5/Cargo.toml @@ -8,7 +8,7 @@ license = "MIT OR Apache-2.0" # Change stm32h563zi to your chip name, if necessary. embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["defmt", "stm32h563zi", "memory-x", "time-driver-any", "exti", "unstable-pac", "low-power"] } embassy-sync = { version = "0.6.0", path = "../../embassy-sync", features = ["defmt"] } -embassy-executor = { version = "0.6.0", path = "../../embassy-executor", features = ["task-arena-size-32768", "arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] } +embassy-executor = { version = "0.6.1", path = "../../embassy-executor", features = ["task-arena-size-32768", "arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] } embassy-time = { version = "0.3.2", 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"] } embassy-usb = { version = "0.3.0", path = "../../embassy-usb", features = ["defmt"] } diff --git a/examples/stm32h7/Cargo.toml b/examples/stm32h7/Cargo.toml index d0f22cf82..f2f207395 100644 --- a/examples/stm32h7/Cargo.toml +++ b/examples/stm32h7/Cargo.toml @@ -9,7 +9,7 @@ license = "MIT OR Apache-2.0" 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.6.0", path = "../../embassy-sync", features = ["defmt"] } embassy-embedded-hal = { version = "0.2.0", path = "../../embassy-embedded-hal" } -embassy-executor = { version = "0.6.0", path = "../../embassy-executor", features = ["task-arena-size-32768", "arch-cortex-m", "executor-thread", "executor-interrupt", "defmt", "integrated-timers"] } +embassy-executor = { version = "0.6.1", path = "../../embassy-executor", features = ["task-arena-size-32768", "arch-cortex-m", "executor-thread", "executor-interrupt", "defmt", "integrated-timers"] } embassy-time = { version = "0.3.2", 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.3.0", path = "../../embassy-usb", features = ["defmt"] } diff --git a/examples/stm32h735/Cargo.toml b/examples/stm32h735/Cargo.toml index 93e9575b6..bac76ddfb 100644 --- a/examples/stm32h735/Cargo.toml +++ b/examples/stm32h735/Cargo.toml @@ -8,7 +8,7 @@ license = "MIT OR Apache-2.0" embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["defmt", "stm32h735ig", "time-driver-tim2", "exti", "memory-x", "unstable-pac", "chrono"] } embassy-sync = { version = "0.6.0", path = "../../embassy-sync", features = ["defmt"] } embassy-embedded-hal = { version = "0.2.0", path = "../../embassy-embedded-hal" } -embassy-executor = { version = "0.6.0", path = "../../embassy-executor", features = ["task-arena-size-32768", "arch-cortex-m", "executor-thread", "executor-interrupt", "defmt", "integrated-timers"] } +embassy-executor = { version = "0.6.1", path = "../../embassy-executor", features = ["task-arena-size-32768", "arch-cortex-m", "executor-thread", "executor-interrupt", "defmt", "integrated-timers"] } embassy-time = { version = "0.3.2", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } embassy-futures = { version = "0.1.0", path = "../../embassy-futures" } diff --git a/examples/stm32h755cm4/Cargo.toml b/examples/stm32h755cm4/Cargo.toml index 75de40b9a..1fbceddde 100644 --- a/examples/stm32h755cm4/Cargo.toml +++ b/examples/stm32h755cm4/Cargo.toml @@ -9,7 +9,7 @@ license = "MIT OR Apache-2.0" embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["defmt", "stm32h755zi-cm4", "time-driver-tim2", "exti", "memory-x", "unstable-pac", "chrono"] } embassy-sync = { version = "0.6.0", path = "../../embassy-sync", features = ["defmt"] } embassy-embedded-hal = { version = "0.2.0", path = "../../embassy-embedded-hal" } -embassy-executor = { version = "0.6.0", path = "../../embassy-executor", features = ["task-arena-size-32768", "arch-cortex-m", "executor-thread", "executor-interrupt", "defmt", "integrated-timers"] } +embassy-executor = { version = "0.6.1", path = "../../embassy-executor", features = ["task-arena-size-32768", "arch-cortex-m", "executor-thread", "executor-interrupt", "defmt", "integrated-timers"] } embassy-time = { version = "0.3.1", 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.3.0", path = "../../embassy-usb", features = ["defmt"] } diff --git a/examples/stm32h755cm7/Cargo.toml b/examples/stm32h755cm7/Cargo.toml index 911a4e79b..8b864169b 100644 --- a/examples/stm32h755cm7/Cargo.toml +++ b/examples/stm32h755cm7/Cargo.toml @@ -9,7 +9,7 @@ license = "MIT OR Apache-2.0" embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["defmt", "stm32h755zi-cm7", "time-driver-tim3", "exti", "memory-x", "unstable-pac", "chrono"] } embassy-sync = { version = "0.6.0", path = "../../embassy-sync", features = ["defmt"] } embassy-embedded-hal = { version = "0.2.0", path = "../../embassy-embedded-hal" } -embassy-executor = { version = "0.6.0", path = "../../embassy-executor", features = ["task-arena-size-32768", "arch-cortex-m", "executor-thread", "executor-interrupt", "defmt", "integrated-timers"] } +embassy-executor = { version = "0.6.1", path = "../../embassy-executor", features = ["task-arena-size-32768", "arch-cortex-m", "executor-thread", "executor-interrupt", "defmt", "integrated-timers"] } embassy-time = { version = "0.3.1", 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.3.0", path = "../../embassy-usb", features = ["defmt"] } diff --git a/examples/stm32h7rs/Cargo.toml b/examples/stm32h7rs/Cargo.toml index 05f638408..29881bf8b 100644 --- a/examples/stm32h7rs/Cargo.toml +++ b/examples/stm32h7rs/Cargo.toml @@ -8,7 +8,7 @@ license = "MIT OR Apache-2.0" # Change stm32h743bi to your chip name, if necessary. embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["defmt", "stm32h7s3l8", "time-driver-tim2", "exti", "memory-x", "unstable-pac", "chrono"] } embassy-sync = { version = "0.6.0", path = "../../embassy-sync", features = ["defmt"] } -embassy-executor = { version = "0.6.0", path = "../../embassy-executor", features = ["task-arena-size-32768", "arch-cortex-m", "executor-thread", "executor-interrupt", "defmt", "integrated-timers"] } +embassy-executor = { version = "0.6.1", path = "../../embassy-executor", features = ["task-arena-size-32768", "arch-cortex-m", "executor-thread", "executor-interrupt", "defmt", "integrated-timers"] } embassy-time = { version = "0.3.2", 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.3.0", path = "../../embassy-usb", features = ["defmt"] } diff --git a/examples/stm32l0/Cargo.toml b/examples/stm32l0/Cargo.toml index 2577f19e0..273cdce1d 100644 --- a/examples/stm32l0/Cargo.toml +++ b/examples/stm32l0/Cargo.toml @@ -8,7 +8,7 @@ license = "MIT OR Apache-2.0" # Change stm32l072cz to your chip name, if necessary. embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["defmt", "stm32l072cz", "unstable-pac", "time-driver-any", "exti", "memory-x"] } embassy-sync = { version = "0.6.0", path = "../../embassy-sync", features = ["defmt"] } -embassy-executor = { version = "0.6.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] } +embassy-executor = { version = "0.6.1", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] } embassy-time = { version = "0.3.2", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } defmt = "0.3" diff --git a/examples/stm32l1/Cargo.toml b/examples/stm32l1/Cargo.toml index 062044f32..af4775a74 100644 --- a/examples/stm32l1/Cargo.toml +++ b/examples/stm32l1/Cargo.toml @@ -6,7 +6,7 @@ license = "MIT OR Apache-2.0" [dependencies] embassy-sync = { version = "0.6.0", path = "../../embassy-sync", features = ["defmt"] } -embassy-executor = { version = "0.6.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] } +embassy-executor = { version = "0.6.1", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] } embassy-time = { version = "0.3.2", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = [ "defmt", "stm32l151cb-a", "time-driver-any", "memory-x"] } embassy-usb = { version = "0.3.0", path = "../../embassy-usb", features = ["defmt"] } diff --git a/examples/stm32l4/Cargo.toml b/examples/stm32l4/Cargo.toml index c5478b17b..70c5c5e26 100644 --- a/examples/stm32l4/Cargo.toml +++ b/examples/stm32l4/Cargo.toml @@ -8,7 +8,7 @@ license = "MIT OR Apache-2.0" # Change stm32l4s5vi to your chip name, if necessary. embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = [ "defmt", "unstable-pac", "stm32l4s5qi", "memory-x", "time-driver-any", "exti", "chrono"] } embassy-sync = { version = "0.6.0", path = "../../embassy-sync", features = ["defmt"] } -embassy-executor = { version = "0.6.0", path = "../../embassy-executor", features = ["task-arena-size-32768", "arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] } +embassy-executor = { version = "0.6.1", path = "../../embassy-executor", features = ["task-arena-size-32768", "arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] } embassy-time = { version = "0.3.2", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768", ] } embassy-embedded-hal = { version = "0.2.0", path = "../../embassy-embedded-hal" } embassy-usb = { version = "0.3.0", path = "../../embassy-usb", features = ["defmt"] } diff --git a/examples/stm32l5/Cargo.toml b/examples/stm32l5/Cargo.toml index 16c184de2..78cb79eac 100644 --- a/examples/stm32l5/Cargo.toml +++ b/examples/stm32l5/Cargo.toml @@ -8,7 +8,7 @@ license = "MIT OR Apache-2.0" # Change stm32l552ze to your chip name, if necessary. embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = [ "defmt", "unstable-pac", "stm32l552ze", "time-driver-any", "exti", "memory-x", "low-power"] } embassy-sync = { version = "0.6.0", path = "../../embassy-sync", features = ["defmt"] } -embassy-executor = { version = "0.6.0", path = "../../embassy-executor", features = ["task-arena-size-32768", "arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] } +embassy-executor = { version = "0.6.1", path = "../../embassy-executor", features = ["task-arena-size-32768", "arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] } embassy-time = { version = "0.3.2", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } embassy-usb = { version = "0.3.0", path = "../../embassy-usb", features = ["defmt"] } embassy-net = { version = "0.4.0", path = "../../embassy-net", features = ["defmt", "tcp", "dhcpv4", "medium-ethernet"] } diff --git a/examples/stm32u0/Cargo.toml b/examples/stm32u0/Cargo.toml index 2e890cdb5..931409b51 100644 --- a/examples/stm32u0/Cargo.toml +++ b/examples/stm32u0/Cargo.toml @@ -8,7 +8,7 @@ license = "MIT OR Apache-2.0" # Change stm32u083rc to your chip name, if necessary. embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = [ "defmt", "time-driver-any", "stm32u083rc", "memory-x", "unstable-pac", "exti", "chrono"] } embassy-sync = { version = "0.6.0", path = "../../embassy-sync", features = ["defmt"] } -embassy-executor = { version = "0.6.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] } +embassy-executor = { version = "0.6.1", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] } embassy-time = { version = "0.3.2", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } embassy-usb = { version = "0.3.0", path = "../../embassy-usb", default-features = false, features = ["defmt"] } embassy-futures = { version = "0.1.0", path = "../../embassy-futures" } diff --git a/examples/stm32u5/Cargo.toml b/examples/stm32u5/Cargo.toml index 20d64c6f7..ad7db4c32 100644 --- a/examples/stm32u5/Cargo.toml +++ b/examples/stm32u5/Cargo.toml @@ -8,7 +8,7 @@ license = "MIT OR Apache-2.0" # Change stm32u585ai to your chip name, if necessary. embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["defmt", "unstable-pac", "stm32u585ai", "time-driver-any", "memory-x" ] } embassy-sync = { version = "0.6.0", path = "../../embassy-sync", features = ["defmt"] } -embassy-executor = { version = "0.6.0", path = "../../embassy-executor", features = ["task-arena-size-32768", "arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] } +embassy-executor = { version = "0.6.1", path = "../../embassy-executor", features = ["task-arena-size-32768", "arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] } embassy-time = { version = "0.3.2", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } embassy-usb = { version = "0.3.0", path = "../../embassy-usb", features = ["defmt"] } embassy-futures = { version = "0.1.0", path = "../../embassy-futures" } diff --git a/examples/stm32wb/Cargo.toml b/examples/stm32wb/Cargo.toml index 1e1a0efe2..8cea2ace8 100644 --- a/examples/stm32wb/Cargo.toml +++ b/examples/stm32wb/Cargo.toml @@ -9,7 +9,7 @@ license = "MIT OR Apache-2.0" embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = [ "defmt", "stm32wb55rg", "time-driver-any", "memory-x", "exti"] } embassy-stm32-wpan = { version = "0.1.0", path = "../../embassy-stm32-wpan", features = ["defmt", "stm32wb55rg"] } embassy-sync = { version = "0.6.0", path = "../../embassy-sync", features = ["defmt"] } -embassy-executor = { version = "0.6.0", path = "../../embassy-executor", features = ["task-arena-size-32768", "arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] } +embassy-executor = { version = "0.6.1", path = "../../embassy-executor", features = ["task-arena-size-32768", "arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] } embassy-time = { version = "0.3.2", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } embassy-net = { version = "0.4.0", path = "../../embassy-net", features = ["defmt", "udp", "proto-ipv6", "medium-ieee802154", ], optional=true } diff --git a/examples/stm32wba/Cargo.toml b/examples/stm32wba/Cargo.toml index 401281c0b..4261564dd 100644 --- a/examples/stm32wba/Cargo.toml +++ b/examples/stm32wba/Cargo.toml @@ -7,7 +7,7 @@ license = "MIT OR Apache-2.0" [dependencies] embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = [ "defmt", "stm32wba52cg", "time-driver-any", "memory-x", "exti"] } embassy-sync = { version = "0.6.0", path = "../../embassy-sync", features = ["defmt"] } -embassy-executor = { version = "0.6.0", path = "../../embassy-executor", features = ["task-arena-size-32768", "arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] } +embassy-executor = { version = "0.6.1", path = "../../embassy-executor", features = ["task-arena-size-32768", "arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] } embassy-time = { version = "0.3.2", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } embassy-net = { version = "0.4.0", path = "../../embassy-net", features = ["defmt", "udp", "proto-ipv6", "medium-ieee802154", ], optional=true } diff --git a/examples/stm32wl/Cargo.toml b/examples/stm32wl/Cargo.toml index 46af5218c..f8d5afb86 100644 --- a/examples/stm32wl/Cargo.toml +++ b/examples/stm32wl/Cargo.toml @@ -8,7 +8,7 @@ license = "MIT OR Apache-2.0" # Change stm32wl55jc-cm4 to your chip name, if necessary. embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["defmt", "stm32wl55jc-cm4", "time-driver-any", "memory-x", "unstable-pac", "exti", "chrono"] } embassy-sync = { version = "0.6.0", path = "../../embassy-sync", features = ["defmt"] } -embassy-executor = { version = "0.6.0", path = "../../embassy-executor", features = ["task-arena-size-4096", "arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] } +embassy-executor = { version = "0.6.1", path = "../../embassy-executor", features = ["task-arena-size-4096", "arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] } embassy-time = { version = "0.3.2", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } embassy-embedded-hal = { version = "0.2.0", path = "../../embassy-embedded-hal" } diff --git a/examples/wasm/Cargo.toml b/examples/wasm/Cargo.toml index 75de079b7..f8db71233 100644 --- a/examples/wasm/Cargo.toml +++ b/examples/wasm/Cargo.toml @@ -9,7 +9,7 @@ crate-type = ["cdylib"] [dependencies] embassy-sync = { version = "0.6.0", path = "../../embassy-sync", features = ["log"] } -embassy-executor = { version = "0.6.0", path = "../../embassy-executor", features = ["arch-wasm", "executor-thread", "log", "integrated-timers"] } +embassy-executor = { version = "0.6.1", path = "../../embassy-executor", features = ["arch-wasm", "executor-thread", "log", "integrated-timers"] } embassy-time = { version = "0.3.2", path = "../../embassy-time", features = ["log", "wasm", ] } wasm-logger = "0.2.0" diff --git a/tests/nrf/Cargo.toml b/tests/nrf/Cargo.toml index f666634d5..d36ab9c67 100644 --- a/tests/nrf/Cargo.toml +++ b/tests/nrf/Cargo.toml @@ -9,7 +9,7 @@ teleprobe-meta = "1" embassy-futures = { version = "0.1.0", path = "../../embassy-futures" } embassy-sync = { version = "0.6.0", path = "../../embassy-sync", features = ["defmt", ] } -embassy-executor = { version = "0.6.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "defmt", "task-arena-size-16384", "integrated-timers"] } +embassy-executor = { version = "0.6.1", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "defmt", "task-arena-size-16384", "integrated-timers"] } embassy-time = { version = "0.3.2", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime"] } embassy-nrf = { version = "0.2.0", path = "../../embassy-nrf", features = ["defmt", "time-driver-rtc1", "gpiote", "unstable-pac"] } embedded-io-async = { version = "0.6.1", features = ["defmt-03"] } diff --git a/tests/riscv32/Cargo.toml b/tests/riscv32/Cargo.toml index ae2b0e180..0ee7f5a00 100644 --- a/tests/riscv32/Cargo.toml +++ b/tests/riscv32/Cargo.toml @@ -7,7 +7,7 @@ license = "MIT OR Apache-2.0" [dependencies] critical-section = { version = "1.1.1", features = ["restore-state-bool"] } embassy-sync = { version = "0.6.0", path = "../../embassy-sync" } -embassy-executor = { version = "0.6.0", path = "../../embassy-executor", features = ["arch-riscv32", "executor-thread"] } +embassy-executor = { version = "0.6.1", path = "../../embassy-executor", features = ["arch-riscv32", "executor-thread"] } embassy-time = { version = "0.3.2", path = "../../embassy-time" } embassy-futures = { version = "0.1.0", path = "../../embassy-futures" } diff --git a/tests/rp/Cargo.toml b/tests/rp/Cargo.toml index 12f1ec3ce..ce812af78 100644 --- a/tests/rp/Cargo.toml +++ b/tests/rp/Cargo.toml @@ -8,7 +8,7 @@ license = "MIT OR Apache-2.0" teleprobe-meta = "1.1" embassy-sync = { version = "0.6.0", path = "../../embassy-sync", features = ["defmt"] } -embassy-executor = { version = "0.6.0", path = "../../embassy-executor", features = ["task-arena-size-32768", "arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] } +embassy-executor = { version = "0.6.1", path = "../../embassy-executor", features = ["task-arena-size-32768", "arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] } embassy-time = { version = "0.3.2", path = "../../embassy-time", features = ["defmt", ] } embassy-rp = { version = "0.2.0", path = "../../embassy-rp", features = [ "defmt", "unstable-pac", "time-driver", "critical-section-impl", "intrinsics", "rom-v2-intrinsics", "run-from-ram", "rp2040"] } embassy-futures = { version = "0.1.0", path = "../../embassy-futures" } diff --git a/tests/stm32/Cargo.toml b/tests/stm32/Cargo.toml index 2eac636e5..468d2ed54 100644 --- a/tests/stm32/Cargo.toml +++ b/tests/stm32/Cargo.toml @@ -60,7 +60,7 @@ cm0 = ["portable-atomic/unsafe-assume-single-core"] teleprobe-meta = "1" embassy-sync = { version = "0.6.0", path = "../../embassy-sync", features = ["defmt"] } -embassy-executor = { version = "0.6.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] } +embassy-executor = { version = "0.6.1", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] } embassy-time = { version = "0.3.2", path = "../../embassy-time", features = ["defmt", "tick-hz-131_072", "defmt-timestamp-uptime"] } embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = [ "defmt", "unstable-pac", "memory-x", "time-driver-any"] } embassy-futures = { version = "0.1.0", path = "../../embassy-futures" } From ecac24a1c7283ab3e3a10df801270eea5491ef3a Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Mon, 21 Oct 2024 01:10:55 +0200 Subject: [PATCH 096/361] stm32: Fix build for chips with octospim but not octospi2. --- ci.sh | 1 + embassy-stm32/build.rs | 14 +++++++++----- embassy-stm32/src/ospi/mod.rs | 2 +- 3 files changed, 11 insertions(+), 6 deletions(-) diff --git a/ci.sh b/ci.sh index aa078be31..b04a4b288 100755 --- a/ci.sh +++ b/ci.sh @@ -166,6 +166,7 @@ cargo batch \ --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv8m.main-none-eabihf --features stm32h562ag,defmt,exti,time-driver-any,time \ --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv8m.main-none-eabihf --features stm32wba50ke,defmt,exti,time-driver-any,time \ --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv8m.main-none-eabihf --features stm32wba55ug,defmt,exti,time-driver-any,time \ + --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv8m.main-none-eabihf --features stm32u5f9zj,defmt,exti,time-driver-any,time \ --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv8m.main-none-eabihf --features stm32u5g9nj,defmt,exti,time-driver-any,time \ --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv7em-none-eabi --features stm32wb35ce,defmt,exti,time-driver-any,time \ --- build --release --manifest-path embassy-stm32/Cargo.toml --target thumbv6m-none-eabi --features stm32u031r8,defmt,exti,time-driver-any,time \ diff --git a/embassy-stm32/build.rs b/embassy-stm32/build.rs index 966dce121..71bfb3747 100644 --- a/embassy-stm32/build.rs +++ b/embassy-stm32/build.rs @@ -55,7 +55,7 @@ fn main() { let mut singletons: Vec = Vec::new(); for p in METADATA.peripherals { if let Some(r) = &p.registers { - if r.kind == "adccommon" || r.kind == "sai" || r.kind == "ucpd" || r.kind == "otg" { + if r.kind == "adccommon" || r.kind == "sai" || r.kind == "ucpd" || r.kind == "otg" || r.kind == "octospi" { // TODO: should we emit this for all peripherals? if so, we will need a list of all // possible peripherals across all chips, so that we can declare the configs // (replacing the hard-coded list of `peri_*` cfgs below) @@ -113,6 +113,7 @@ fn main() { "peri_ucpd2", "peri_usb_otg_fs", "peri_usb_otg_hs", + "peri_octospi2", ]); cfgs.declare_all(&["mco", "mco1", "mco2"]); @@ -1137,11 +1138,14 @@ fn main() { // OCTOSPIM is special if p.name == "OCTOSPIM" { + // Some chips have OCTOSPIM but not OCTOSPI2. + if METADATA.peripherals.iter().any(|p| p.name == "OCTOSPI2") { + peri = format_ident!("{}", "OCTOSPI2"); + g.extend(quote! { + pin_trait_impl!(#tr, #peri, #pin_name, #af); + }); + } peri = format_ident!("{}", "OCTOSPI1"); - g.extend(quote! { - pin_trait_impl!(#tr, #peri, #pin_name, #af); - }); - peri = format_ident!("{}", "OCTOSPI2"); } g.extend(quote! { diff --git a/embassy-stm32/src/ospi/mod.rs b/embassy-stm32/src/ospi/mod.rs index 48a1ea5e6..e4adc4b09 100644 --- a/embassy-stm32/src/ospi/mod.rs +++ b/embassy-stm32/src/ospi/mod.rs @@ -1178,7 +1178,7 @@ impl SealedOctospimInstance for peripherals::OCTOSPI1 { const OCTOSPI_IDX: u8 = 1; } -#[cfg(octospim_v1)] +#[cfg(all(octospim_v1, peri_octospi2))] impl SealedOctospimInstance for peripherals::OCTOSPI2 { const OCTOSPIM_REGS: Octospim = crate::pac::OCTOSPIM; const OCTOSPI_IDX: u8 = 2; From b4ee17fb4f1a77c26fc08e9fe9e0d343d1c059b4 Mon Sep 17 00:00:00 2001 From: Fan Jiang Date: Sun, 20 Oct 2024 12:29:38 -0400 Subject: [PATCH 097/361] net: Add flush for UDP and Raw sockets. --- embassy-net/Cargo.toml | 2 +- embassy-net/src/raw.rs | 17 +++++++++++++++++ embassy-net/src/udp.rs | 17 +++++++++++++++++ 3 files changed, 35 insertions(+), 1 deletion(-) diff --git a/embassy-net/Cargo.toml b/embassy-net/Cargo.toml index a33c693fc..8a29aca79 100644 --- a/embassy-net/Cargo.toml +++ b/embassy-net/Cargo.toml @@ -68,7 +68,7 @@ multicast = ["smoltcp/multicast"] defmt = { version = "0.3", optional = true } log = { version = "0.4.14", optional = true } -smoltcp = { git="https://github.com/smoltcp-rs/smoltcp", rev="b65e1b64dc9b66fa984a2ad34e90685cb0b606de", default-features = false, features = [ +smoltcp = { git="https://github.com/smoltcp-rs/smoltcp", rev="fe0b4d102253465850cd1cf39cd33d4721a4a8d5", default-features = false, features = [ "socket", "async", ] } diff --git a/embassy-net/src/raw.rs b/embassy-net/src/raw.rs index 1098dc208..ace325a46 100644 --- a/embassy-net/src/raw.rs +++ b/embassy-net/src/raw.rs @@ -108,6 +108,23 @@ impl<'a> RawSocket<'a> { } }) } + + /// Flush the socket. + /// + /// This method will wait until the socket is flushed. + pub async fn flush(&mut self) { + poll_fn(move |cx| { + self.with_mut(|s, _| { + if s.send_queue() == 0 { + Poll::Ready(()) + } else { + s.register_send_waker(cx.waker()); + Poll::Pending + } + }) + }) + .await + } } impl Drop for RawSocket<'_> { diff --git a/embassy-net/src/udp.rs b/embassy-net/src/udp.rs index 3eb6e2f83..ac650364e 100644 --- a/embassy-net/src/udp.rs +++ b/embassy-net/src/udp.rs @@ -241,6 +241,23 @@ impl<'a> UdpSocket<'a> { .await } + /// Flush the socket. + /// + /// This method will wait until the socket is flushed. + pub async fn flush(&mut self) { + poll_fn(move |cx| { + self.with_mut(|s, _| { + if s.send_queue() == 0 { + Poll::Ready(()) + } else { + s.register_send_waker(cx.waker()); + Poll::Pending + } + }) + }) + .await + } + /// Returns the local endpoint of the socket. pub fn endpoint(&self) -> IpListenEndpoint { self.with(|s, _| s.endpoint()) From 1d395fc2b6e5d3d870afb90593de9f03d47b0efa Mon Sep 17 00:00:00 2001 From: Lucas Martins Date: Tue, 3 Sep 2024 14:38:22 -0300 Subject: [PATCH 098/361] stm32/flash: add stm32f2, stm32h5 flash driver --- embassy-stm32/src/flash/h5.rs | 177 +++++++++++++++++++++++++++++++++ embassy-stm32/src/flash/mod.rs | 5 +- 2 files changed, 180 insertions(+), 2 deletions(-) create mode 100644 embassy-stm32/src/flash/h5.rs diff --git a/embassy-stm32/src/flash/h5.rs b/embassy-stm32/src/flash/h5.rs new file mode 100644 index 000000000..9e131ca2b --- /dev/null +++ b/embassy-stm32/src/flash/h5.rs @@ -0,0 +1,177 @@ +use core::ptr::write_volatile; +use core::sync::atomic::{fence, Ordering}; + +use super::{FlashRegion, FlashSector, FLASH_REGIONS, WRITE_SIZE}; +use crate::flash::Error; +use crate::pac; + +pub(crate) const fn is_default_layout() -> bool { + true +} + +// const fn is_dual_bank() -> bool { +// FLASH_REGIONS.len() >= 2 +// } + +pub(crate) fn get_flash_regions() -> &'static [&'static FlashRegion] { + &FLASH_REGIONS +} + +pub(crate) unsafe fn lock() { + if !pac::FLASH.nscr().read().lock() { + pac::FLASH.nscr().modify(|r| { + r.set_lock(true); + }); + } +} + +pub(crate) unsafe fn unlock() { + // TODO: check locked first + while pac::FLASH.nssr().read().bsy() { + #[cfg(feature = "defmt")] + defmt::trace!("busy") + } + + // only unlock if locked to begin with + if pac::FLASH.nscr().read().lock() { + pac::FLASH.nskeyr().write_value(0x4567_0123); + pac::FLASH.nskeyr().write_value(0xCDEF_89AB); + } +} + +pub(crate) unsafe fn enable_blocking_write() { + assert_eq!(0, WRITE_SIZE % 4); +} + +pub(crate) unsafe fn disable_blocking_write() {} + +pub(crate) unsafe fn blocking_write(start_address: u32, buf: &[u8; WRITE_SIZE]) -> Result<(), Error> { + // // We cannot have the write setup sequence in begin_write as it depends on the address + // let bank = if start_address < BANK1_REGION.end() { + // pac::FLASH.bank(0) + // } else { + // pac::FLASH.bank(1) + // }; + + cortex_m::asm::isb(); + cortex_m::asm::dsb(); + fence(Ordering::SeqCst); + + clear_all_err(); + + pac::FLASH.nscr().write(|w| { + w.set_pg(true); + // w.set_psize(2); // 32 bits at once + }); + + let mut res = None; + let mut address = start_address; + // TODO: see write size + for val in buf.chunks(4) { + write_volatile(address as *mut u32, u32::from_le_bytes(unwrap!(val.try_into()))); + address += val.len() as u32; + + res = Some(blocking_wait_ready().map_err(|e| { + error!("write err"); + e + })); + pac::FLASH.nssr().modify(|w| { + if w.eop() { + w.set_eop(true); + } + }); + if unwrap!(res).is_err() { + break; + } + } + + cortex_m::asm::isb(); + cortex_m::asm::dsb(); + fence(Ordering::SeqCst); + + pac::FLASH.nscr().write(|w| w.set_pg(false)); + + unwrap!(res) +} + +pub(crate) unsafe fn blocking_erase_sector(sector: &FlashSector) -> Result<(), Error> { + // pac::FLASH.wrp2r_cur().read().wrpsg() + // TODO: write protection check + if pac::FLASH.nscr().read().lock() == true { + error!("flash locked"); + } + + loop { + let sr = pac::FLASH.nssr().read(); + if !sr.bsy() && !sr.dbne() { + break; + } + } + clear_all_err(); + + pac::FLASH.nscr().modify(|r| { + // TODO: later check bank swap + r.set_bksel(match sector.bank { + crate::flash::FlashBank::Bank1 => stm32_metapac::flash::vals::NscrBksel::B_0X0, + crate::flash::FlashBank::Bank2 => stm32_metapac::flash::vals::NscrBksel::B_0X1, + }); + r.set_snb(sector.index_in_bank); + r.set_ser(true); + }); + + pac::FLASH.nscr().modify(|r| { + r.set_strt(true); + }); + + cortex_m::asm::isb(); + cortex_m::asm::dsb(); + fence(Ordering::SeqCst); + + let ret: Result<(), Error> = blocking_wait_ready().map_err(|e| { + error!("erase err"); + e + }); + + pac::FLASH.nscr().modify(|w| w.set_ser(false)); + clear_all_err(); + ret +} + +pub(crate) unsafe fn clear_all_err() { + pac::FLASH.nssr().modify(|_| {}) +} + +unsafe fn blocking_wait_ready() -> Result<(), Error> { + loop { + let sr = pac::FLASH.nssr().read(); + + if !sr.bsy() { + if sr.optchangeerr() { + error!("optchangeerr"); + return Err(Error::Prog); + } + if sr.obkwerr() { + error!("obkwerr"); + return Err(Error::Seq); + } + if sr.obkerr() { + error!("obkerr"); + return Err(Error::Seq); + } + if sr.incerr() { + error!("incerr"); + return Err(Error::Unaligned); + } + if sr.strberr() { + error!("strberr"); + return Err(Error::Parallelism); + } + if sr.wrperr() { + error!("protected"); + return Err(Error::Protected); + } + + return Ok(()); + } + } +} diff --git a/embassy-stm32/src/flash/mod.rs b/embassy-stm32/src/flash/mod.rs index d64a1c28a..88fe6a291 100644 --- a/embassy-stm32/src/flash/mod.rs +++ b/embassy-stm32/src/flash/mod.rs @@ -101,13 +101,14 @@ pub enum FlashBank { #[cfg_attr(flash_h7, path = "h7.rs")] #[cfg_attr(flash_h7ab, path = "h7.rs")] #[cfg_attr(flash_u5, path = "u5.rs")] +#[cfg_attr(flash_h5, path = "h5.rs")] #[cfg_attr(flash_h50, path = "h50.rs")] #[cfg_attr(flash_u0, path = "u0.rs")] #[cfg_attr( not(any( flash_l0, flash_l1, flash_l4, flash_l5, flash_wl, flash_wb, flash_f0, flash_f1, flash_f2, flash_f3, flash_f4, - flash_f7, flash_g0, flash_g0, flash_g4c2, flash_g4c3, flash_g4c4, flash_h7, flash_h7ab, flash_u5, flash_h50, - flash_u0 + flash_f7, flash_g0, flash_g4c2, flash_g4c3, flash_g4c4, flash_h7, flash_h7ab, flash_u5, flash_h50, flash_u0, + flash_h5, )), path = "other.rs" )] From 693bd8c6ded003f78ae89fa2700ba7282fee64d0 Mon Sep 17 00:00:00 2001 From: rafael Date: Mon, 21 Oct 2024 11:54:17 +0200 Subject: [PATCH 099/361] re-export SetDutyCycle for user convenience --- embassy-rp/src/pwm.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/embassy-rp/src/pwm.rs b/embassy-rp/src/pwm.rs index 79e626802..de2c2f81e 100644 --- a/embassy-rp/src/pwm.rs +++ b/embassy-rp/src/pwm.rs @@ -1,7 +1,7 @@ //! Pulse Width Modulation (PWM) use embassy_hal_internal::{into_ref, Peripheral, PeripheralRef}; -use embedded_hal_1::pwm::{Error, ErrorKind, ErrorType, SetDutyCycle}; +use embedded_hal_1::pwm::{Error, ErrorKind, ErrorType}; use fixed::traits::ToFixed; use fixed::FixedU16; use pac::pwm::regs::{ChDiv, Intr}; @@ -10,6 +10,8 @@ use pac::pwm::vals::Divmode; use crate::gpio::{AnyPin, Pin as GpioPin, Pull, SealedPin as _}; use crate::{pac, peripherals, RegExt}; +pub use embedded_hal_1::pwm::SetDutyCycle; + /// The configuration of a PWM slice. /// Note the period in clock cycles of a slice can be computed as: /// `(top + 1) * (phase_correct ? 1 : 2) * divider` @@ -113,9 +115,7 @@ impl<'d> SetDutyCycle for Pwm<'d> { } fn set_duty_cycle(&mut self, duty: u16) -> Result<(), Self::Error> { - info!("duty {}",&duty); let max_duty = self.max_duty_cycle(); - info!("max duty {}", &max_duty); if duty > max_duty { return Err(PwmError::InvalidDutyCycle); } From f32b0fbc3b614ab79b7756b49e44a380e5e60192 Mon Sep 17 00:00:00 2001 From: rafael Date: Mon, 21 Oct 2024 11:55:10 +0200 Subject: [PATCH 100/361] change existing pwm example to reflect both existing ways to use pwm output --- examples/rp23/src/bin/pwm.rs | 55 +++++++++++++++++++++++++++++++----- 1 file changed, 48 insertions(+), 7 deletions(-) diff --git a/examples/rp23/src/bin/pwm.rs b/examples/rp23/src/bin/pwm.rs index 15eae09ee..7314ee637 100644 --- a/examples/rp23/src/bin/pwm.rs +++ b/examples/rp23/src/bin/pwm.rs @@ -1,6 +1,8 @@ -//! This example shows how to use PWM (Pulse Width Modulation) in the RP2040 chip. +//! This example shows how to use PWM (Pulse Width Modulation) in the RP235x chip. //! -//! The LED on the RP Pico W board is connected differently. Add a LED and resistor to another pin. +//! We demonstrate two ways of using PWM: +//! 1. Via config +//! 2. Via setting a duty cycle #![no_std] #![no_main] @@ -8,8 +10,10 @@ use defmt::*; use embassy_executor::Spawner; use embassy_rp::block::ImageDef; -use embassy_rp::pwm::{Config, Pwm}; +use embassy_rp::peripherals::{PIN_25, PIN_4, PWM_SLICE2, PWM_SLICE4}; +use embassy_rp::pwm::{Config, Pwm, SetDutyCycle}; use embassy_time::Timer; +// use embedded_hal_1::pwm::SetDutyCycle; use {defmt_rtt as _, panic_probe as _}; #[link_section = ".start_block"] @@ -17,13 +21,22 @@ use {defmt_rtt as _, panic_probe as _}; pub static IMAGE_DEF: ImageDef = ImageDef::secure_exe(); #[embassy_executor::main] -async fn main(_spawner: Spawner) { +async fn main(spawner: Spawner) { let p = embassy_rp::init(Default::default()); + spawner.spawn(pwm_set_config(p.PWM_SLICE4, p.PIN_25)).unwrap(); + spawner.spawn(pwm_set_dutycycle(p.PWM_SLICE2, p.PIN_4)).unwrap(); +} - let mut c: Config = Default::default(); - c.top = 0x8000; +/// Demonstrate PWM by modifying & applying the config +/// +/// Using the onboard led, if You are using a different Board than plain Pico2 (i.e. W variant) +/// you must use another slice & pin and an appropriate resistor. +#[embassy_executor::task] +async fn pwm_set_config(slice4: PWM_SLICE4, pin25: PIN_25) { + let mut c = Config::default(); + c.top = 32_768; c.compare_b = 8; - let mut pwm = Pwm::new_output_b(p.PWM_SLICE4, p.PIN_25, c.clone()); + let mut pwm = Pwm::new_output_b(slice4, pin25, c.clone()); loop { info!("current LED duty cycle: {}/32768", c.compare_b); @@ -32,3 +45,31 @@ async fn main(_spawner: Spawner) { pwm.set_config(&c); } } + +/// Demonstrate PWM by setting duty cycle +/// +/// Using GP4 in Slice2, make sure to use an appropriate resistor. +#[embassy_executor::task] +async fn pwm_set_dutycycle(slice2: PWM_SLICE2, pin4: PIN_4) { + let mut c = Config::default(); + c.top = 32_768; + let mut pwm = Pwm::new_output_a(slice2, pin4, c.clone()); + + loop { + // 100% duty cycle, fully on + pwm.set_duty_cycle_fully_on().unwrap(); + Timer::after_secs(1).await; + + // 50% duty cycle, half on. Expressed as simple percentage. + pwm.set_duty_cycle_percent(50).unwrap(); + Timer::after_secs(1).await; + + // 25% duty cycle, quarter on. Expressed as (duty / max_duty) + pwm.set_duty_cycle(8_192 / c.top).unwrap(); + Timer::after_secs(1).await; + + // 0% duty cycle, fully off. + pwm.set_duty_cycle_fully_off().unwrap(); + Timer::after_secs(1).await; + } +} From d7db8fbab9a2bc363520505757386500d5710735 Mon Sep 17 00:00:00 2001 From: rafael Date: Mon, 21 Oct 2024 11:59:03 +0200 Subject: [PATCH 101/361] rustfmt --- embassy-rp/src/pwm.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/embassy-rp/src/pwm.rs b/embassy-rp/src/pwm.rs index de2c2f81e..cfb99c569 100644 --- a/embassy-rp/src/pwm.rs +++ b/embassy-rp/src/pwm.rs @@ -1,6 +1,7 @@ //! Pulse Width Modulation (PWM) use embassy_hal_internal::{into_ref, Peripheral, PeripheralRef}; +pub use embedded_hal_1::pwm::SetDutyCycle; use embedded_hal_1::pwm::{Error, ErrorKind, ErrorType}; use fixed::traits::ToFixed; use fixed::FixedU16; @@ -10,8 +11,6 @@ use pac::pwm::vals::Divmode; use crate::gpio::{AnyPin, Pin as GpioPin, Pull, SealedPin as _}; use crate::{pac, peripherals, RegExt}; -pub use embedded_hal_1::pwm::SetDutyCycle; - /// The configuration of a PWM slice. /// Note the period in clock cycles of a slice can be computed as: /// `(top + 1) * (phase_correct ? 1 : 2) * divider` From d92fb002ecc3ff4dcac51d8e74927d977b2343b0 Mon Sep 17 00:00:00 2001 From: rafael Date: Mon, 21 Oct 2024 12:19:10 +0200 Subject: [PATCH 102/361] rustfmt --- examples/rp23/src/bin/pwm.rs | 1 - examples/rp23/src/bin/pwm_tb6612fng_motor_driver.rs | 7 ++----- 2 files changed, 2 insertions(+), 6 deletions(-) diff --git a/examples/rp23/src/bin/pwm.rs b/examples/rp23/src/bin/pwm.rs index 7314ee637..1dd5ca3de 100644 --- a/examples/rp23/src/bin/pwm.rs +++ b/examples/rp23/src/bin/pwm.rs @@ -13,7 +13,6 @@ use embassy_rp::block::ImageDef; use embassy_rp::peripherals::{PIN_25, PIN_4, PWM_SLICE2, PWM_SLICE4}; use embassy_rp::pwm::{Config, Pwm, SetDutyCycle}; use embassy_time::Timer; -// use embedded_hal_1::pwm::SetDutyCycle; use {defmt_rtt as _, panic_probe as _}; #[link_section = ".start_block"] diff --git a/examples/rp23/src/bin/pwm_tb6612fng_motor_driver.rs b/examples/rp23/src/bin/pwm_tb6612fng_motor_driver.rs index 36b8d9620..6c3a8998c 100644 --- a/examples/rp23/src/bin/pwm_tb6612fng_motor_driver.rs +++ b/examples/rp23/src/bin/pwm_tb6612fng_motor_driver.rs @@ -10,12 +10,9 @@ use defmt::*; use embassy_executor::Spawner; use embassy_rp::block::ImageDef; use embassy_rp::config::Config; -use embassy_rp::gpio; use embassy_rp::gpio::Output; -use embassy_rp::peripherals; -use embassy_rp::pwm; -use embassy_time::Duration; -use embassy_time::Timer; +use embassy_rp::{gpio, peripherals, pwm}; +use embassy_time::{Duration, Timer}; use tb6612fng::{DriveCommand, Motor, Tb6612fng}; use {defmt_rtt as _, panic_probe as _}; From e782eabff743175b34489401574d1988c370f06f Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Mon, 21 Oct 2024 16:20:40 +0200 Subject: [PATCH 103/361] Changelog executor v0.5.1 release. --- embassy-executor/CHANGELOG.md | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/embassy-executor/CHANGELOG.md b/embassy-executor/CHANGELOG.md index ac1c8cb0c..3d8c48676 100644 --- a/embassy-executor/CHANGELOG.md +++ b/embassy-executor/CHANGELOG.md @@ -11,7 +11,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Soundness fix: Deny using `impl Trait` in task arguments. This was previously accidentally allowed when not using the `nightly` feature, and could cause out of bounds memory accesses if spawning the same task mulitple times with different underlying types - for the `impl Trait`. Affected versions are 0.4.x, 0.5.x and 0.6.0, which have been yanked. + for the `impl Trait`. Affected versions are 0.4.x, 0.5.0 and 0.6.0, which have been yanked. - Add an architecture-agnostic executor that spins waiting for tasks to run, enabled with the `arch-spin` feature. - Update for breaking change in the nightly waker_getters API. The `nightly` feature now requires`nightly-2024-09-06` or newer. - Improve macro error messages. @@ -22,6 +22,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - initial support for AVR - use nightly waker_getters APIs +## 0.5.1 - 2024-10-21 + +- Soundness fix: Deny using `impl Trait` in task arguments. This was previously accidentally allowed when not using the `nightly` feature, + and could cause out of bounds memory accesses if spawning the same task mulitple times with different underlying types + for the `impl Trait`. Affected versions are 0.4.x, 0.5.0 and 0.6.0, which have been yanked. + ## 0.5.0 - 2024-01-11 - Updated to `embassy-time-driver 0.1`, `embassy-time-queue-driver 0.1`, compatible with `embassy-time v0.3` and higher. From 61f55be92ae11805ae233d9c2526b41b605b3f8e Mon Sep 17 00:00:00 2001 From: Ulf Lilleengen Date: Mon, 21 Oct 2024 19:41:28 +0200 Subject: [PATCH 104/361] Use released version of reqwless for examples --- examples/rp/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/rp/Cargo.toml b/examples/rp/Cargo.toml index 8e5f81b7e..6a2c99716 100644 --- a/examples/rp/Cargo.toml +++ b/examples/rp/Cargo.toml @@ -25,7 +25,7 @@ fixed = "1.23.1" fixed-macro = "1.2" # for web request example -reqwless = { git="https://github.com/drogue-iot/reqwless", rev="673e8d2cfbaad79254ec51fa50cc8b697531fbff", features = ["defmt",]} +reqwless = { version = "0.13.0", features = ["defmt"] } serde = { version = "1.0.203", default-features = false, features = ["derive"] } serde-json-core = "0.5.1" From c0addc005bb3859436cb1c2931b41a7065703077 Mon Sep 17 00:00:00 2001 From: Lucas Martins Date: Mon, 21 Oct 2024 14:48:57 -0300 Subject: [PATCH 105/361] add uart permutations usefull for rs485 --- embassy-stm32/src/usart/buffered.rs | 49 +++++++++++++++++++++++++++++ 1 file changed, 49 insertions(+) diff --git a/embassy-stm32/src/usart/buffered.rs b/embassy-stm32/src/usart/buffered.rs index 4fbe33b2e..03c0934f9 100644 --- a/embassy-stm32/src/usart/buffered.rs +++ b/embassy-stm32/src/usart/buffered.rs @@ -246,6 +246,55 @@ impl<'d> BufferedUart<'d> { ) } + + /// Create a new bidirectional buffered UART driver with only RTS pin as the DE pin + pub fn new_with_rts_as_de( + peri: impl Peripheral

+ 'd, + _irq: impl interrupt::typelevel::Binding> + 'd, + rx: impl Peripheral

> + 'd, + tx: impl Peripheral

> + 'd, + rts: impl Peripheral

> + 'd, + tx_buffer: &'d mut [u8], + rx_buffer: &'d mut [u8], + config: Config, + ) -> Result { + Self::new_inner( + peri, + new_pin!(rx, AfType::input(Pull::None)), + new_pin!(tx, AfType::output(OutputType::PushPull, Speed::Medium)), + None, + None, + new_pin!(rts, AfType::input(Pull::None)), // RTS mapped used as DE + tx_buffer, + rx_buffer, + config, + ) + } + + /// Create a new bidirectional buffered UART driver with only the request-to-send pin + pub fn new_with_rts( + peri: impl Peripheral

+ 'd, + _irq: impl interrupt::typelevel::Binding> + 'd, + rx: impl Peripheral

> + 'd, + tx: impl Peripheral

> + 'd, + rts: impl Peripheral

> + 'd, + tx_buffer: &'d mut [u8], + rx_buffer: &'d mut [u8], + config: Config, + ) -> Result { + Self::new_inner( + peri, + new_pin!(rx, AfType::input(Pull::None)), + new_pin!(tx, AfType::output(OutputType::PushPull, Speed::Medium)), + new_pin!(rts, AfType::input(Pull::None)), + None, // no RTS + None, // no DE + tx_buffer, + rx_buffer, + config, + ) + } + /// Create a new bidirectional buffered UART driver with a driver-enable pin #[cfg(not(any(usart_v1, usart_v2)))] pub fn new_with_de( From 2c9b528edde491ca1ed02234805212c78e2a6bd7 Mon Sep 17 00:00:00 2001 From: Lucas Martins Date: Mon, 21 Oct 2024 15:06:06 -0300 Subject: [PATCH 106/361] fmt --- embassy-stm32/src/usart/buffered.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/embassy-stm32/src/usart/buffered.rs b/embassy-stm32/src/usart/buffered.rs index 03c0934f9..edaf41be9 100644 --- a/embassy-stm32/src/usart/buffered.rs +++ b/embassy-stm32/src/usart/buffered.rs @@ -246,7 +246,6 @@ impl<'d> BufferedUart<'d> { ) } - /// Create a new bidirectional buffered UART driver with only RTS pin as the DE pin pub fn new_with_rts_as_de( peri: impl Peripheral

+ 'd, From 8dfc9ba1a3e3f69aedf5bce748783fb6a8f5e92e Mon Sep 17 00:00:00 2001 From: rafael Date: Mon, 21 Oct 2024 21:14:49 +0200 Subject: [PATCH 107/361] also add to the rp pwm example --- examples/rp/src/bin/pwm.rs | 52 +++++++++++++++++++++++++++++++----- examples/rp23/src/bin/pwm.rs | 8 +++--- 2 files changed, 50 insertions(+), 10 deletions(-) diff --git a/examples/rp/src/bin/pwm.rs b/examples/rp/src/bin/pwm.rs index 26e233260..862a7da22 100644 --- a/examples/rp/src/bin/pwm.rs +++ b/examples/rp/src/bin/pwm.rs @@ -1,24 +1,36 @@ //! This example shows how to use PWM (Pulse Width Modulation) in the RP2040 chip. //! -//! The LED on the RP Pico W board is connected differently. Add a LED and resistor to another pin. +//! We demonstrate two ways of using PWM: +//! 1. Via config +//! 2. Via setting a duty cycle #![no_std] #![no_main] use defmt::*; use embassy_executor::Spawner; -use embassy_rp::pwm::{Config, Pwm}; +use embassy_rp::peripherals::{PIN_25, PIN_4, PWM_SLICE2, PWM_SLICE4}; +use embassy_rp::pwm::{Config, Pwm, SetDutyCycle}; use embassy_time::Timer; use {defmt_rtt as _, panic_probe as _}; #[embassy_executor::main] -async fn main(_spawner: Spawner) { +async fn main(spawner: Spawner) { let p = embassy_rp::init(Default::default()); + spawner.spawn(pwm_set_config(p.PWM_SLICE4, p.PIN_25)).unwrap(); + spawner.spawn(pwm_set_dutycycle(p.PWM_SLICE2, p.PIN_4)).unwrap(); +} - let mut c: Config = Default::default(); - c.top = 0x8000; +/// Demonstrate PWM by modifying & applying the config +/// +/// Using the onboard led, if You are using a different Board than plain Pico2 (i.e. W variant) +/// you must use another slice & pin and an appropriate resistor. +#[embassy_executor::task] +async fn pwm_set_config(slice4: PWM_SLICE4, pin25: PIN_25) { + let mut c = Config::default(); + c.top = 32_768; c.compare_b = 8; - let mut pwm = Pwm::new_output_b(p.PWM_SLICE4, p.PIN_25, c.clone()); + let mut pwm = Pwm::new_output_b(slice4, pin25, c.clone()); loop { info!("current LED duty cycle: {}/32768", c.compare_b); @@ -27,3 +39,31 @@ async fn main(_spawner: Spawner) { pwm.set_config(&c); } } + +/// Demonstrate PWM by setting duty cycle +/// +/// Using GP4 in Slice2, make sure to use an appropriate resistor. +#[embassy_executor::task] +async fn pwm_set_dutycycle(slice2: PWM_SLICE2, pin4: PIN_4) { + let mut c = Config::default(); + c.top = 32_768; + let mut pwm = Pwm::new_output_a(slice2, pin4, c.clone()); + + loop { + // 100% duty cycle, fully on + pwm.set_duty_cycle_fully_on().unwrap(); + Timer::after_secs(1).await; + + // 66% duty cycle. Expressed as simple percentage. + pwm.set_duty_cycle_percent(66).unwrap(); + Timer::after_secs(1).await; + + // 25% duty cycle. Expressed as 32768/4 = 8192. + pwm.set_duty_cycle(8_192).unwrap(); + Timer::after_secs(1).await; + + // 0% duty cycle, fully off. + pwm.set_duty_cycle_fully_off().unwrap(); + Timer::after_secs(1).await; + } +} diff --git a/examples/rp23/src/bin/pwm.rs b/examples/rp23/src/bin/pwm.rs index 1dd5ca3de..838eee625 100644 --- a/examples/rp23/src/bin/pwm.rs +++ b/examples/rp23/src/bin/pwm.rs @@ -59,12 +59,12 @@ async fn pwm_set_dutycycle(slice2: PWM_SLICE2, pin4: PIN_4) { pwm.set_duty_cycle_fully_on().unwrap(); Timer::after_secs(1).await; - // 50% duty cycle, half on. Expressed as simple percentage. - pwm.set_duty_cycle_percent(50).unwrap(); + // 66% duty cycle. Expressed as simple percentage. + pwm.set_duty_cycle_percent(66).unwrap(); Timer::after_secs(1).await; - // 25% duty cycle, quarter on. Expressed as (duty / max_duty) - pwm.set_duty_cycle(8_192 / c.top).unwrap(); + // 25% duty cycle. Expressed as 32768/4 = 8192. + pwm.set_duty_cycle(8_192).unwrap(); Timer::after_secs(1).await; // 0% duty cycle, fully off. From 14e69309ebe25a76f67c38411c7a80a4d83da5ed Mon Sep 17 00:00:00 2001 From: rafael Date: Mon, 21 Oct 2024 22:42:18 +0200 Subject: [PATCH 108/361] add pwm frequency to examples --- examples/rp/src/bin/pwm.rs | 10 ++++++++-- examples/rp23/src/bin/pwm.rs | 10 ++++++++-- .../rp23/src/bin/pwm_tb6612fng_motor_driver.rs | 17 +++++++---------- 3 files changed, 23 insertions(+), 14 deletions(-) diff --git a/examples/rp/src/bin/pwm.rs b/examples/rp/src/bin/pwm.rs index 862a7da22..791b88b5b 100644 --- a/examples/rp/src/bin/pwm.rs +++ b/examples/rp/src/bin/pwm.rs @@ -45,8 +45,14 @@ async fn pwm_set_config(slice4: PWM_SLICE4, pin25: PIN_25) { /// Using GP4 in Slice2, make sure to use an appropriate resistor. #[embassy_executor::task] async fn pwm_set_dutycycle(slice2: PWM_SLICE2, pin4: PIN_4) { + // If we aim for a specific frequency, here is how we can calculate the top value. + // The top value sets the period of the PWM cycle, so a counter goes from 0 to top and then wraps around to 0. + // Every such wraparound is one PWM cycle. So here is how we get 25KHz: let mut c = Config::default(); - c.top = 32_768; + let pwm_freq = 25_000; // Hz, our desired frequency + let clock_freq = embassy_rp::clocks::clk_sys_freq(); + c.top = (clock_freq / pwm_freq) as u16 - 1; + let mut pwm = Pwm::new_output_a(slice2, pin4, c.clone()); loop { @@ -59,7 +65,7 @@ async fn pwm_set_dutycycle(slice2: PWM_SLICE2, pin4: PIN_4) { Timer::after_secs(1).await; // 25% duty cycle. Expressed as 32768/4 = 8192. - pwm.set_duty_cycle(8_192).unwrap(); + pwm.set_duty_cycle(c.top / 4).unwrap(); Timer::after_secs(1).await; // 0% duty cycle, fully off. diff --git a/examples/rp23/src/bin/pwm.rs b/examples/rp23/src/bin/pwm.rs index 838eee625..5a4457158 100644 --- a/examples/rp23/src/bin/pwm.rs +++ b/examples/rp23/src/bin/pwm.rs @@ -50,8 +50,14 @@ async fn pwm_set_config(slice4: PWM_SLICE4, pin25: PIN_25) { /// Using GP4 in Slice2, make sure to use an appropriate resistor. #[embassy_executor::task] async fn pwm_set_dutycycle(slice2: PWM_SLICE2, pin4: PIN_4) { + // If we aim for a specific frequency, here is how we can calculate the top value. + // The top value sets the period of the PWM cycle, so a counter goes from 0 to top and then wraps around to 0. + // Every such wraparound is one PWM cycle. So here is how we get 25KHz: let mut c = Config::default(); - c.top = 32_768; + let pwm_freq = 25_000; // Hz, our desired frequency + let clock_freq = embassy_rp::clocks::clk_sys_freq(); + c.top = (clock_freq / pwm_freq) as u16 - 1; + let mut pwm = Pwm::new_output_a(slice2, pin4, c.clone()); loop { @@ -64,7 +70,7 @@ async fn pwm_set_dutycycle(slice2: PWM_SLICE2, pin4: PIN_4) { Timer::after_secs(1).await; // 25% duty cycle. Expressed as 32768/4 = 8192. - pwm.set_duty_cycle(8_192).unwrap(); + pwm.set_duty_cycle(c.top / 4).unwrap(); Timer::after_secs(1).await; // 0% duty cycle, fully off. diff --git a/examples/rp23/src/bin/pwm_tb6612fng_motor_driver.rs b/examples/rp23/src/bin/pwm_tb6612fng_motor_driver.rs index 6c3a8998c..263b551de 100644 --- a/examples/rp23/src/bin/pwm_tb6612fng_motor_driver.rs +++ b/examples/rp23/src/bin/pwm_tb6612fng_motor_driver.rs @@ -16,12 +16,6 @@ use embassy_time::{Duration, Timer}; use tb6612fng::{DriveCommand, Motor, Tb6612fng}; use {defmt_rtt as _, panic_probe as _}; -/// Maximum PWM value (fully on) -const PWM_MAX: u16 = 50000; - -/// Minimum PWM value (fully off) -const PWM_MIN: u16 = 0; - #[link_section = ".start_block"] #[used] pub static IMAGE_DEF: ImageDef = ImageDef::secure_exe(); @@ -46,6 +40,11 @@ async fn main(_spawner: Spawner) { let s = split_resources!(p); let r = s.motor; + // we want a PWM frequency of 25KHz + let pwm_freq = 25_000; // Hz, our desired frequency + let clock_freq = embassy_rp::clocks::clk_sys_freq(); + let period = (clock_freq / pwm_freq) as u16 - 1; + // we need a standby output and two motors to construct a full TB6612FNG // standby pin @@ -55,8 +54,7 @@ async fn main(_spawner: Spawner) { let left_fwd = gpio::Output::new(r.left_forward_pin, gpio::Level::Low); let left_bckw = gpio::Output::new(r.left_backward_pin, gpio::Level::Low); let mut left_speed = pwm::Config::default(); - left_speed.top = PWM_MAX; - left_speed.compare_a = PWM_MIN; + left_speed.top = period; let left_pwm = pwm::Pwm::new_output_a(r.left_slice, r.left_pwm_pin, left_speed); let left_motor = Motor::new(left_fwd, left_bckw, left_pwm).unwrap(); @@ -64,8 +62,7 @@ async fn main(_spawner: Spawner) { let right_fwd = gpio::Output::new(r.right_forward_pin, gpio::Level::Low); let right_bckw = gpio::Output::new(r.right_backward_pin, gpio::Level::Low); let mut right_speed = pwm::Config::default(); - right_speed.top = PWM_MAX; - right_speed.compare_b = PWM_MIN; + right_speed.top = period; let right_pwm = pwm::Pwm::new_output_b(r.right_slice, r.right_pwm_pin, right_speed); let right_motor = Motor::new(right_fwd, right_bckw, right_pwm).unwrap(); From 82772e3a8f36b31052ca46af3947ba253458bef1 Mon Sep 17 00:00:00 2001 From: Lucas Martins Date: Mon, 21 Oct 2024 17:50:05 -0300 Subject: [PATCH 109/361] :memo: fix wrong comment --- embassy-stm32/src/usart/buffered.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/embassy-stm32/src/usart/buffered.rs b/embassy-stm32/src/usart/buffered.rs index edaf41be9..7ba209063 100644 --- a/embassy-stm32/src/usart/buffered.rs +++ b/embassy-stm32/src/usart/buffered.rs @@ -246,7 +246,7 @@ impl<'d> BufferedUart<'d> { ) } - /// Create a new bidirectional buffered UART driver with only RTS pin as the DE pin + /// Create a new bidirectional buffered UART driver with only the RTS pin as the DE pin pub fn new_with_rts_as_de( peri: impl Peripheral

+ 'd, _irq: impl interrupt::typelevel::Binding> + 'd, @@ -286,7 +286,7 @@ impl<'d> BufferedUart<'d> { new_pin!(rx, AfType::input(Pull::None)), new_pin!(tx, AfType::output(OutputType::PushPull, Speed::Medium)), new_pin!(rts, AfType::input(Pull::None)), - None, // no RTS + None, // no CTS None, // no DE tx_buffer, rx_buffer, From 548f11d5aef37d5ab1edac6970ceb7bee4300f4f Mon Sep 17 00:00:00 2001 From: rafael Date: Mon, 21 Oct 2024 23:19:45 +0200 Subject: [PATCH 110/361] cheaper motors need lower pwm frequency --- examples/rp23/src/bin/pwm_tb6612fng_motor_driver.rs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/examples/rp23/src/bin/pwm_tb6612fng_motor_driver.rs b/examples/rp23/src/bin/pwm_tb6612fng_motor_driver.rs index 263b551de..3fad2928c 100644 --- a/examples/rp23/src/bin/pwm_tb6612fng_motor_driver.rs +++ b/examples/rp23/src/bin/pwm_tb6612fng_motor_driver.rs @@ -40,8 +40,8 @@ async fn main(_spawner: Spawner) { let s = split_resources!(p); let r = s.motor; - // we want a PWM frequency of 25KHz - let pwm_freq = 25_000; // Hz, our desired frequency + // we want a PWM frequency of 1KHz, especially cheaper motors do not respond well to higher frequencies + let pwm_freq = 1_000; // Hz, our desired frequency let clock_freq = embassy_rp::clocks::clk_sys_freq(); let period = (clock_freq / pwm_freq) as u16 - 1; @@ -77,8 +77,8 @@ async fn main(_spawner: Spawner) { // drive a straight line forward at 20% speed for 5s info!("drive straight"); - control.motor_a.drive(DriveCommand::Forward(20)).unwrap(); - control.motor_b.drive(DriveCommand::Forward(20)).unwrap(); + control.motor_a.drive(DriveCommand::Forward(80)).unwrap(); + control.motor_b.drive(DriveCommand::Forward(80)).unwrap(); Timer::after(Duration::from_secs(5)).await; // coast for 2s @@ -95,8 +95,8 @@ async fn main(_spawner: Spawner) { // slowly turn for 3s info!("turn"); - control.motor_a.drive(DriveCommand::Backward(10)).unwrap(); - control.motor_b.drive(DriveCommand::Forward(10)).unwrap(); + control.motor_a.drive(DriveCommand::Backward(50)).unwrap(); + control.motor_b.drive(DriveCommand::Forward(50)).unwrap(); Timer::after(Duration::from_secs(3)).await; // and put the driver in standby mode and wait for 5s From c94c5516d1e9681c7b8222b9ee30f416b4e84c7d Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Tue, 22 Oct 2024 03:34:18 +0200 Subject: [PATCH 111/361] net: automatically enable defmt/ip_in_core --- embassy-net/Cargo.toml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/embassy-net/Cargo.toml b/embassy-net/Cargo.toml index 8a29aca79..1090892e0 100644 --- a/embassy-net/Cargo.toml +++ b/embassy-net/Cargo.toml @@ -27,7 +27,7 @@ default = [] std = [] ## Enable defmt -defmt = ["dep:defmt", "smoltcp/defmt", "embassy-net-driver/defmt", "heapless/defmt-03"] +defmt = ["dep:defmt", "smoltcp/defmt", "embassy-net-driver/defmt", "heapless/defmt-03", "defmt?/ip_in_core"] ## Trace all raw received and transmitted packets using defmt or log. packet-trace = [] @@ -65,7 +65,7 @@ multicast = ["smoltcp/multicast"] [dependencies] -defmt = { version = "0.3", optional = true } +defmt = { version = "0.3.8", optional = true } log = { version = "0.4.14", optional = true } smoltcp = { git="https://github.com/smoltcp-rs/smoltcp", rev="fe0b4d102253465850cd1cf39cd33d4721a4a8d5", default-features = false, features = [ From 3279c19eeeb942d15805c7780bf3bcad6159286e Mon Sep 17 00:00:00 2001 From: Dinu Blanovschi Date: Tue, 22 Oct 2024 12:30:37 +0200 Subject: [PATCH 112/361] feat(stm32): allow `bind_interrupts!` to accept conditional compilation attrs --- embassy-stm32/src/lib.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/embassy-stm32/src/lib.rs b/embassy-stm32/src/lib.rs index 451f595e0..3ff9dcb7e 100644 --- a/embassy-stm32/src/lib.rs +++ b/embassy-stm32/src/lib.rs @@ -165,7 +165,7 @@ pub use crate::_generated::interrupt; // developer note: this macro can't be in `embassy-hal-internal` due to the use of `$crate`. #[macro_export] macro_rules! bind_interrupts { - ($vis:vis struct $name:ident { $($irq:ident => $($handler:ty),*;)* }) => { + ($vis:vis struct $name:ident { $($(#[cfg($cond:meta)])? $irq:ident => $($handler:ty),*;)* }) => { #[derive(Copy, Clone)] $vis struct $name; @@ -174,11 +174,13 @@ macro_rules! bind_interrupts { #[no_mangle] unsafe extern "C" fn $irq() { $( + $(#[cfg($cond)])? <$handler as $crate::interrupt::typelevel::Handler<$crate::interrupt::typelevel::$irq>>::on_interrupt(); )* } $( + $(#[cfg($cond)])? unsafe impl $crate::interrupt::typelevel::Binding<$crate::interrupt::typelevel::$irq, $handler> for $name {} )* )* From 66822f1000f542cac767be77345cb527956ada04 Mon Sep 17 00:00:00 2001 From: Brad Gibson Date: Tue, 22 Oct 2024 05:29:11 -0700 Subject: [PATCH 113/361] update path to cyw43 firmware in `wifi_blinky.rs` comments --- examples/rp/src/bin/wifi_blinky.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/rp/src/bin/wifi_blinky.rs b/examples/rp/src/bin/wifi_blinky.rs index 04a61bbd5..0107a2326 100644 --- a/examples/rp/src/bin/wifi_blinky.rs +++ b/examples/rp/src/bin/wifi_blinky.rs @@ -33,8 +33,8 @@ async fn main(spawner: Spawner) { // To make flashing faster for development, you may want to flash the firmwares independently // at hardcoded addresses, instead of baking them into the program with `include_bytes!`: - // probe-rs download 43439A0.bin --binary-format bin --chip RP2040 --base-address 0x10100000 - // probe-rs download 43439A0_clm.bin --binary-format bin --chip RP2040 --base-address 0x10140000 + // probe-rs download ../../cyw43-firmware/43439A0.bin --binary-format bin --chip RP2040 --base-address 0x10100000 + // probe-rs download ../../cyw43-firmware/43439A0_clm.bin --binary-format bin --chip RP2040 --base-address 0x10140000 //let fw = unsafe { core::slice::from_raw_parts(0x10100000 as *const u8, 230321) }; //let clm = unsafe { core::slice::from_raw_parts(0x10140000 as *const u8, 4752) }; From ccd635f0dc5d9a21f7cec87e18368d70482b93ca Mon Sep 17 00:00:00 2001 From: Dinu Blanovschi Date: Tue, 22 Oct 2024 15:43:28 +0200 Subject: [PATCH 114/361] fix + allow both conditions on the irq and the handlers --- embassy-stm32/src/lib.rs | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/embassy-stm32/src/lib.rs b/embassy-stm32/src/lib.rs index 3ff9dcb7e..2ce18c3c0 100644 --- a/embassy-stm32/src/lib.rs +++ b/embassy-stm32/src/lib.rs @@ -165,22 +165,31 @@ pub use crate::_generated::interrupt; // developer note: this macro can't be in `embassy-hal-internal` due to the use of `$crate`. #[macro_export] macro_rules! bind_interrupts { - ($vis:vis struct $name:ident { $($(#[cfg($cond:meta)])? $irq:ident => $($handler:ty),*;)* }) => { + ($vis:vis struct $name:ident { + $( + $(#[cfg($cond_irq:meta)])? + $irq:ident => $( + $(#[cfg($cond_handler:meta)])? + $handler:ty + ),*; + )* + }) => { #[derive(Copy, Clone)] $vis struct $name; $( #[allow(non_snake_case)] #[no_mangle] + $(#[cfg($cond_irq)])? unsafe extern "C" fn $irq() { $( - $(#[cfg($cond)])? + $(#[cfg($cond_handler)])? <$handler as $crate::interrupt::typelevel::Handler<$crate::interrupt::typelevel::$irq>>::on_interrupt(); )* } $( - $(#[cfg($cond)])? + $(#[cfg(all($cond_irq, $cond_handler))])? unsafe impl $crate::interrupt::typelevel::Binding<$crate::interrupt::typelevel::$irq, $handler> for $name {} )* )* From 82a438a0379b70b6df4bb72750267fa51ed787ab Mon Sep 17 00:00:00 2001 From: Dinu Blanovschi Date: Tue, 22 Oct 2024 15:51:05 +0200 Subject: [PATCH 115/361] fix --- 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 2ce18c3c0..c59174988 100644 --- a/embassy-stm32/src/lib.rs +++ b/embassy-stm32/src/lib.rs @@ -189,7 +189,8 @@ macro_rules! bind_interrupts { } $( - $(#[cfg(all($cond_irq, $cond_handler))])? + $(#[cfg($cond_irq)])? + $(#[cfg($cond_handler)])? unsafe impl $crate::interrupt::typelevel::Binding<$crate::interrupt::typelevel::$irq, $handler> for $name {} )* )* From 5c23c789ee2776ea022957b82480dad6d0d0f660 Mon Sep 17 00:00:00 2001 From: Dinu Blanovschi Date: Tue, 22 Oct 2024 16:23:17 +0200 Subject: [PATCH 116/361] fix --- embassy-stm32/src/lib.rs | 25 +++++++++++++++++++++---- 1 file changed, 21 insertions(+), 4 deletions(-) diff --git a/embassy-stm32/src/lib.rs b/embassy-stm32/src/lib.rs index c59174988..de68c54dc 100644 --- a/embassy-stm32/src/lib.rs +++ b/embassy-stm32/src/lib.rs @@ -188,13 +188,30 @@ macro_rules! bind_interrupts { )* } - $( - $(#[cfg($cond_irq)])? + $crate::bind_interrupts!(@__generate_impls $name $(#[cfg($cond_irq)])? $irq => $( $(#[cfg($cond_handler)])? - unsafe impl $crate::interrupt::typelevel::Binding<$crate::interrupt::typelevel::$irq, $handler> for $name {} - )* + $handler; + )*); )* }; + + (@__generate_single_impl $name:ident $(#[cfg($cond_irq:meta)])? $irq:ident => $(#[cfg($cond_handler:meta)])? $handler:ty;) => { + #[cfg(all( + $($cond_irq,)? + $($cond_handler,)? + ))] + unsafe impl $crate::interrupt::typelevel::Binding<$crate::interrupt::typelevel::$irq, $handler> for $name {} + }; + + (@__generate_impls $name:ident $(#[cfg($cond_irq:meta)])? $irq:ident => $(#[cfg($cond_handler:meta)])? $handler:ty;) => { + $crate::bind_interrupts!(@__generate_single_impl $name $(#[cfg($cond_irq)])? $irq => $(#[cfg($cond_handler)])? $handler;); + }; + + (@__generate_impls $name:ident $(#[cfg($cond_irq:meta)])? $irq:ident => $(#[cfg($cond_handler:meta)])? $handler:ty; $(tail:tt)*) => { + $crate::bind_interrupts!(@__generate_single_impl $name $(#[cfg($cond_irq)])? $irq => $(#[cfg($cond_handler)])? $handler;); + + $crate::bind_interrupts!(@__generate_impls $name $(#[cfg($cond_irq)])? $irq => $(tail)*); + }; } // Reexports From e9f2e63796989c4e248418e341a442e0eff5946d Mon Sep 17 00:00:00 2001 From: Dinu Blanovschi Date: Tue, 22 Oct 2024 16:50:10 +0200 Subject: [PATCH 117/361] fix --- embassy-stm32/src/lib.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/embassy-stm32/src/lib.rs b/embassy-stm32/src/lib.rs index de68c54dc..4154a7275 100644 --- a/embassy-stm32/src/lib.rs +++ b/embassy-stm32/src/lib.rs @@ -207,10 +207,10 @@ macro_rules! bind_interrupts { $crate::bind_interrupts!(@__generate_single_impl $name $(#[cfg($cond_irq)])? $irq => $(#[cfg($cond_handler)])? $handler;); }; - (@__generate_impls $name:ident $(#[cfg($cond_irq:meta)])? $irq:ident => $(#[cfg($cond_handler:meta)])? $handler:ty; $(tail:tt)*) => { + (@__generate_impls $name:ident $(#[cfg($cond_irq:meta)])? $irq:ident => $(#[cfg($cond_handler:meta)])? $handler:ty; $($(#[cfg($cond_rest:meta)])? $handler_rest:ty;)+) => { $crate::bind_interrupts!(@__generate_single_impl $name $(#[cfg($cond_irq)])? $irq => $(#[cfg($cond_handler)])? $handler;); - $crate::bind_interrupts!(@__generate_impls $name $(#[cfg($cond_irq)])? $irq => $(tail)*); + $crate::bind_interrupts!(@__generate_impls $name $(#[cfg($cond_irq)])? $irq => $($(#[cfg($cond_rest)])? $handler_rest;)+); }; } From c79791552563746e9f62e3d2647d787d3947c249 Mon Sep 17 00:00:00 2001 From: Dinu Blanovschi Date: Tue, 22 Oct 2024 16:56:05 +0200 Subject: [PATCH 118/361] fix: review comments --- embassy-nrf/src/lib.rs | 19 ++++++++++++++----- embassy-rp/src/lib.rs | 19 ++++++++++++++----- embassy-stm32/src/lib.rs | 26 +++----------------------- 3 files changed, 31 insertions(+), 33 deletions(-) diff --git a/embassy-nrf/src/lib.rs b/embassy-nrf/src/lib.rs index 13623dd1c..bd53664a2 100644 --- a/embassy-nrf/src/lib.rs +++ b/embassy-nrf/src/lib.rs @@ -177,22 +177,31 @@ mod chip; // developer note: this macro can't be in `embassy-hal-internal` due to the use of `$crate`. #[macro_export] macro_rules! bind_interrupts { - ($vis:vis struct $name:ident { $($irq:ident => $($handler:ty),*;)* }) => { + ($vis:vis struct $name:ident { + $( + $(#[cfg($cond_irq:meta)])? + $irq:ident => $( + $(#[cfg($cond_handler:meta)])? + $handler:ty + ),*; + )* + }) => { #[derive(Copy, Clone)] $vis struct $name; $( #[allow(non_snake_case)] #[no_mangle] + $(#[cfg($cond_irq)])? unsafe extern "C" fn $irq() { $( + $(#[cfg($cond_handler)])? <$handler as $crate::interrupt::typelevel::Handler<$crate::interrupt::typelevel::$irq>>::on_interrupt(); + + $(#[cfg($cond_handler)])? + unsafe impl $crate::interrupt::typelevel::Binding<$crate::interrupt::typelevel::$irq, $handler> for $name {} )* } - - $( - unsafe impl $crate::interrupt::typelevel::Binding<$crate::interrupt::typelevel::$irq, $handler> for $name {} - )* )* }; } diff --git a/embassy-rp/src/lib.rs b/embassy-rp/src/lib.rs index d402cf793..a72cebdd8 100644 --- a/embassy-rp/src/lib.rs +++ b/embassy-rp/src/lib.rs @@ -165,22 +165,31 @@ embassy_hal_internal::interrupt_mod!( // developer note: this macro can't be in `embassy-hal-internal` due to the use of `$crate`. #[macro_export] macro_rules! bind_interrupts { - ($vis:vis struct $name:ident { $($irq:ident => $($handler:ty),*;)* }) => { + ($vis:vis struct $name:ident { + $( + $(#[cfg($cond_irq:meta)])? + $irq:ident => $( + $(#[cfg($cond_handler:meta)])? + $handler:ty + ),*; + )* + }) => { #[derive(Copy, Clone)] $vis struct $name; $( #[allow(non_snake_case)] #[no_mangle] + $(#[cfg($cond_irq)])? unsafe extern "C" fn $irq() { $( + $(#[cfg($cond_handler)])? <$handler as $crate::interrupt::typelevel::Handler<$crate::interrupt::typelevel::$irq>>::on_interrupt(); + + $(#[cfg($cond_handler)])? + unsafe impl $crate::interrupt::typelevel::Binding<$crate::interrupt::typelevel::$irq, $handler> for $name {} )* } - - $( - unsafe impl $crate::interrupt::typelevel::Binding<$crate::interrupt::typelevel::$irq, $handler> for $name {} - )* )* }; } diff --git a/embassy-stm32/src/lib.rs b/embassy-stm32/src/lib.rs index 4154a7275..65f02d62a 100644 --- a/embassy-stm32/src/lib.rs +++ b/embassy-stm32/src/lib.rs @@ -185,33 +185,13 @@ macro_rules! bind_interrupts { $( $(#[cfg($cond_handler)])? <$handler as $crate::interrupt::typelevel::Handler<$crate::interrupt::typelevel::$irq>>::on_interrupt(); + + $(#[cfg($cond_handler)])? + unsafe impl $crate::interrupt::typelevel::Binding<$crate::interrupt::typelevel::$irq, $handler> for $name {} )* } - - $crate::bind_interrupts!(@__generate_impls $name $(#[cfg($cond_irq)])? $irq => $( - $(#[cfg($cond_handler)])? - $handler; - )*); )* }; - - (@__generate_single_impl $name:ident $(#[cfg($cond_irq:meta)])? $irq:ident => $(#[cfg($cond_handler:meta)])? $handler:ty;) => { - #[cfg(all( - $($cond_irq,)? - $($cond_handler,)? - ))] - unsafe impl $crate::interrupt::typelevel::Binding<$crate::interrupt::typelevel::$irq, $handler> for $name {} - }; - - (@__generate_impls $name:ident $(#[cfg($cond_irq:meta)])? $irq:ident => $(#[cfg($cond_handler:meta)])? $handler:ty;) => { - $crate::bind_interrupts!(@__generate_single_impl $name $(#[cfg($cond_irq)])? $irq => $(#[cfg($cond_handler)])? $handler;); - }; - - (@__generate_impls $name:ident $(#[cfg($cond_irq:meta)])? $irq:ident => $(#[cfg($cond_handler:meta)])? $handler:ty; $($(#[cfg($cond_rest:meta)])? $handler_rest:ty;)+) => { - $crate::bind_interrupts!(@__generate_single_impl $name $(#[cfg($cond_irq)])? $irq => $(#[cfg($cond_handler)])? $handler;); - - $crate::bind_interrupts!(@__generate_impls $name $(#[cfg($cond_irq)])? $irq => $($(#[cfg($cond_rest)])? $handler_rest;)+); - }; } // Reexports From e5bc2666547d0320635fede91c76d936c56a1a90 Mon Sep 17 00:00:00 2001 From: Haobo Gu Date: Wed, 23 Oct 2024 12:54:50 +0800 Subject: [PATCH 119/361] feat: set ospi memory mapped mode Signed-off-by: Haobo Gu --- embassy-stm32/src/ospi/mod.rs | 37 +++++++++++++++++++++++++++++++++++ 1 file changed, 37 insertions(+) diff --git a/embassy-stm32/src/ospi/mod.rs b/embassy-stm32/src/ospi/mod.rs index e4adc4b09..0574f7816 100644 --- a/embassy-stm32/src/ospi/mod.rs +++ b/embassy-stm32/src/ospi/mod.rs @@ -179,6 +179,43 @@ pub struct Ospi<'d, T: Instance, M: PeriMode> { } impl<'d, T: Instance, M: PeriMode> Ospi<'d, T, M> { + pub fn enable_memory_mapped_mode(&mut self) { + let reg = T::REGS; + while reg.sr().read().busy() { + info!("wait ospi busy"); + } + + reg.ccr().modify(|r| { + r.set_isize(crate::ospi::vals::SizeInBits::_8BIT); + r.set_adsize(crate::ospi::vals::SizeInBits::_24BIT); + r.set_admode(crate::ospi::vals::PhaseMode::ONELINE); + r.set_imode(crate::ospi::vals::PhaseMode::ONELINE); + r.set_dmode(crate::ospi::vals::PhaseMode::FOURLINES); + }); + + reg.cr().modify(|r| { + r.set_fmode(crate::ospi::vals::FunctionalMode::MEMORYMAPPED); + r.set_dmaen(false); + r.set_en(true); + }); + + // reg.tcr().modify(|r| { + // r.set_dcyc(6); + // }); + + } + pub fn disable_memory_mapped_mode(&mut self) { + let reg = T::REGS; + while reg.sr().read().busy() { + info!("wait ospi busy"); + } + reg.cr().modify(|r| { + r.set_fmode(crate::ospi::vals::FunctionalMode::INDIRECTWRITE); + r.set_dmaen(false); + r.set_en(true); + }); + } + fn new_inner( peri: impl Peripheral

+ 'd, d0: Option>, From be50b62677f04f0d30727a9168b435b90584f06c Mon Sep 17 00:00:00 2001 From: dvdsk Date: Wed, 23 Oct 2024 21:06:58 +0200 Subject: [PATCH 120/361] stm32/uart impl ReadReady for RingbufferdUart --- embassy-stm32/src/usart/mod.rs | 3 +++ embassy-stm32/src/usart/ringbuffered.rs | 11 +++++++++++ 2 files changed, 14 insertions(+) diff --git a/embassy-stm32/src/usart/mod.rs b/embassy-stm32/src/usart/mod.rs index 333e01e36..86c94789a 100644 --- a/embassy-stm32/src/usart/mod.rs +++ b/embassy-stm32/src/usart/mod.rs @@ -260,6 +260,8 @@ pub enum Error { Parity, /// Buffer too large for DMA BufferTooLong, + // TODO: ask what this is and document it (dvdsk) + DmaUnsynced, } enum ReadCompletionEvent { @@ -1689,6 +1691,7 @@ impl embedded_hal_nb::serial::Error for Error { Self::Overrun => embedded_hal_nb::serial::ErrorKind::Overrun, Self::Parity => embedded_hal_nb::serial::ErrorKind::Parity, Self::BufferTooLong => embedded_hal_nb::serial::ErrorKind::Other, + Self::DmaUnsynced => embedded_hal_nb::serial::ErrorKind::Other, } } } diff --git a/embassy-stm32/src/usart/ringbuffered.rs b/embassy-stm32/src/usart/ringbuffered.rs index eb2399d9c..f1914e1bf 100644 --- a/embassy-stm32/src/usart/ringbuffered.rs +++ b/embassy-stm32/src/usart/ringbuffered.rs @@ -5,6 +5,7 @@ use core::task::Poll; use embassy_embedded_hal::SetConfig; use embassy_hal_internal::PeripheralRef; +use embedded_io_async::ReadReady; use futures_util::future::{select, Either}; use super::{clear_interrupt_flags, rdr, reconfigure, sr, Config, ConfigError, Error, Info, State, UartRx}; @@ -262,3 +263,13 @@ impl embedded_io_async::Read for RingBufferedUartRx<'_> { self.read(buf).await } } + +impl ReadReady for RingBufferedUartRx<'_> { + fn read_ready(&mut self) -> Result { + let len = self.ring_buf.len().map_err(|e| match e { + crate::dma::ringbuffer::Error::Overrun => Self::Error::Overrun, + crate::dma::ringbuffer::Error::DmaUnsynced => Self::Error::DmaUnsynced, + })?; + Ok(len > 0) + } +} From 528a3e43550b5d2ce4a5f44d890feaa3e56c113a Mon Sep 17 00:00:00 2001 From: Alex Moon Date: Wed, 14 Aug 2024 17:07:57 -0400 Subject: [PATCH 121/361] Add support for transactions to Twim in embassy-nrf --- embassy-nrf/src/twim.rs | 653 ++++++++++++++++++++++++---------------- 1 file changed, 393 insertions(+), 260 deletions(-) diff --git a/embassy-nrf/src/twim.rs b/embassy-nrf/src/twim.rs index c64743ecc..d0518807c 100644 --- a/embassy-nrf/src/twim.rs +++ b/embassy-nrf/src/twim.rs @@ -4,6 +4,7 @@ use core::future::{poll_fn, Future}; use core::marker::PhantomData; +use core::mem::MaybeUninit; use core::sync::atomic::compiler_fence; use core::sync::atomic::Ordering::SeqCst; use core::task::Poll; @@ -13,11 +14,12 @@ use embassy_hal_internal::{into_ref, PeripheralRef}; use embassy_sync::waitqueue::AtomicWaker; #[cfg(feature = "time")] use embassy_time::{Duration, Instant}; +use embedded_hal_1::i2c::Operation; use crate::chip::{EASY_DMA_SIZE, FORCE_COPY_BUFFER_SIZE}; use crate::gpio::Pin as GpioPin; use crate::interrupt::typelevel::Interrupt; -use crate::util::{slice_in_ram, slice_in_ram_or}; +use crate::util::slice_in_ram; use crate::{gpio, interrupt, pac, Peripheral}; /// TWI frequency @@ -103,6 +105,18 @@ impl interrupt::typelevel::Handler for InterruptHandl let r = T::regs(); let s = T::state(); + // Workaround for lack of LASTRX_SUSPEND short in some nRF chips + // Do this first to minimize latency + #[cfg(any(feature = "nrf52832", feature = "_nrf5340", feature = "_nrf9120"))] + if r.events_lastrx.read().bits() != 0 { + r.tasks_suspend.write(|w| unsafe { w.bits(1) }); + r.events_lastrx.reset(); + } + + if r.events_suspended.read().bits() != 0 { + s.end_waker.wake(); + r.intenclr.write(|w| w.suspended().clear()); + } if r.events_stopped.read().bits() != 0 { s.end_waker.wake(); r.intenclr.write(|w| w.stopped().clear()); @@ -182,8 +196,22 @@ impl<'d, T: Instance> Twim<'d, T> { } /// Set TX buffer, checking that it is in RAM and has suitable length. - unsafe fn set_tx_buffer(&mut self, buffer: &[u8]) -> Result<(), Error> { - slice_in_ram_or(buffer, Error::BufferNotInRAM)?; + unsafe fn set_tx_buffer( + &mut self, + buffer: &[u8], + ram_buffer: Option<&mut [MaybeUninit; FORCE_COPY_BUFFER_SIZE]>, + ) -> Result<(), Error> { + let buffer = if slice_in_ram(buffer) { + buffer + } else { + let ram_buffer = ram_buffer.ok_or(Error::BufferNotInRAM)?; + trace!("Copying TWIM tx buffer into RAM for DMA"); + let ram_buffer = &mut ram_buffer[..buffer.len()]; + // Inline implementation of the nightly API MaybeUninit::copy_from_slice(ram_buffer, buffer) + let uninit_src: &[MaybeUninit] = unsafe { core::mem::transmute(buffer) }; + ram_buffer.copy_from_slice(uninit_src); + unsafe { &*(ram_buffer as *const [MaybeUninit] as *const [u8]) } + }; if buffer.len() > EASY_DMA_SIZE { return Err(Error::TxBufferTooLong); @@ -290,7 +318,7 @@ impl<'d, T: Instance> Twim<'d, T> { fn blocking_wait(&mut self) { let r = T::regs(); loop { - if r.events_stopped.read().bits() != 0 { + if r.events_suspended.read().bits() != 0 || r.events_stopped.read().bits() != 0 { r.events_stopped.reset(); break; } @@ -307,7 +335,7 @@ impl<'d, T: Instance> Twim<'d, T> { let r = T::regs(); let deadline = Instant::now() + timeout; loop { - if r.events_stopped.read().bits() != 0 { + if r.events_suspended.read().bits() != 0 || r.events_stopped.read().bits() != 0 { r.events_stopped.reset(); break; } @@ -331,7 +359,7 @@ impl<'d, T: Instance> Twim<'d, T> { let s = T::state(); s.end_waker.register(cx.waker()); - if r.events_stopped.read().bits() != 0 { + if r.events_suspended.read().bits() != 0 || r.events_stopped.read().bits() != 0 { r.events_stopped.reset(); return Poll::Ready(()); @@ -347,75 +375,13 @@ impl<'d, T: Instance> Twim<'d, T> { }) } - fn setup_write_from_ram(&mut self, address: u8, buffer: &[u8], inten: bool) -> Result<(), Error> { - let r = T::regs(); - - compiler_fence(SeqCst); - - r.address.write(|w| unsafe { w.address().bits(address) }); - - // Set up the DMA write. - unsafe { self.set_tx_buffer(buffer)? }; - - // Clear events - r.events_stopped.reset(); - r.events_error.reset(); - r.events_lasttx.reset(); - self.clear_errorsrc(); - - if inten { - r.intenset.write(|w| w.stopped().set().error().set()); - } else { - r.intenclr.write(|w| w.stopped().clear().error().clear()); - } - - // Start write operation. - r.shorts.write(|w| w.lasttx_stop().enabled()); - r.tasks_starttx.write(|w| unsafe { w.bits(1) }); - if buffer.is_empty() { - // With a zero-length buffer, LASTTX doesn't fire (because there's no last byte!), so do the STOP ourselves. - r.tasks_stop.write(|w| unsafe { w.bits(1) }); - } - Ok(()) - } - - fn setup_read(&mut self, address: u8, buffer: &mut [u8], inten: bool) -> Result<(), Error> { - let r = T::regs(); - - compiler_fence(SeqCst); - - r.address.write(|w| unsafe { w.address().bits(address) }); - - // Set up the DMA read. - unsafe { self.set_rx_buffer(buffer)? }; - - // Clear events - r.events_stopped.reset(); - r.events_error.reset(); - self.clear_errorsrc(); - - if inten { - r.intenset.write(|w| w.stopped().set().error().set()); - } else { - r.intenclr.write(|w| w.stopped().clear().error().clear()); - } - - // Start read operation. - r.shorts.write(|w| w.lastrx_stop().enabled()); - r.tasks_startrx.write(|w| unsafe { w.bits(1) }); - if buffer.is_empty() { - // With a zero-length buffer, LASTRX doesn't fire (because there's no last byte!), so do the STOP ourselves. - r.tasks_stop.write(|w| unsafe { w.bits(1) }); - } - Ok(()) - } - - fn setup_write_read_from_ram( + fn setup_operations( &mut self, address: u8, - wr_buffer: &[u8], - rd_buffer: &mut [u8], + operations: &mut [Operation<'_>], + tx_ram_buffer: Option<&mut [MaybeUninit; FORCE_COPY_BUFFER_SIZE]>, inten: bool, + stop: bool, ) -> Result<(), Error> { let r = T::regs(); @@ -423,92 +389,343 @@ impl<'d, T: Instance> Twim<'d, T> { r.address.write(|w| unsafe { w.address().bits(address) }); - // Set up DMA buffers. - unsafe { - self.set_tx_buffer(wr_buffer)?; - self.set_rx_buffer(rd_buffer)?; - } - - // Clear events + let was_suspended = r.events_suspended.read().bits() != 0; + r.events_suspended.reset(); r.events_stopped.reset(); r.events_error.reset(); self.clear_errorsrc(); if inten { - r.intenset.write(|w| w.stopped().set().error().set()); + r.intenset.write(|w| w.suspended().set().stopped().set().error().set()); } else { - r.intenclr.write(|w| w.stopped().clear().error().clear()); + r.intenclr + .write(|w| w.suspended().clear().stopped().clear().error().clear()); } + #[cfg(any(feature = "nrf52832", feature = "_nrf5340", feature = "_nrf9120"))] + r.intenclr.write(|w| w.lastrx().clear()); - // Start write+read operation. - r.shorts.write(|w| { - w.lasttx_startrx().enabled(); - w.lastrx_stop().enabled(); - w - }); - r.tasks_starttx.write(|w| unsafe { w.bits(1) }); - if wr_buffer.is_empty() && rd_buffer.is_empty() { - // With a zero-length buffer, LASTRX/LASTTX doesn't fire (because there's no last byte!), so do the STOP ourselves. - // TODO handle when only one of the buffers is zero length - r.tasks_stop.write(|w| unsafe { w.bits(1) }); + match operations { + [Operation::Read(rd_buffer), Operation::Write(wr_buffer), rest @ ..] + if !rd_buffer.is_empty() && !wr_buffer.is_empty() => + { + let stop = stop && rest.is_empty(); + + // Set up DMA buffers. + unsafe { + self.set_tx_buffer(wr_buffer, tx_ram_buffer)?; + self.set_rx_buffer(rd_buffer)?; + } + + r.shorts.write(|w| { + w.lastrx_starttx().enabled(); + if stop { + w.lasttx_stop().enabled(); + } else { + w.lasttx_suspend().enabled(); + } + w + }); + + // Start read+write operation. + r.tasks_startrx.write(|w| unsafe { w.bits(1) }); + + if was_suspended { + r.tasks_resume.write(|w| unsafe { w.bits(1) }); + } + } + [Operation::Write(wr_buffer), Operation::Read(rd_buffer), rest @ ..] + if !wr_buffer.is_empty() && !rd_buffer.is_empty() => + { + let stop = stop && rest.is_empty(); + + // Set up DMA buffers. + unsafe { + self.set_tx_buffer(wr_buffer, tx_ram_buffer)?; + self.set_rx_buffer(rd_buffer)?; + } + + // Start write+read operation. + r.shorts.write(|w| { + w.lasttx_startrx().enabled(); + if stop { + w.lastrx_stop().enabled(); + } else { + #[cfg(not(any(feature = "nrf52832", feature = "_nrf5340", feature = "_nrf9120")))] + w.lastrx_suspend().enabled(); + } + w + }); + #[cfg(any(feature = "nrf52832", feature = "_nrf5340", feature = "_nrf9120"))] + if !stop { + r.intenset.write(|w| w.lastrx().set()); + } + + r.tasks_starttx.write(|w| unsafe { w.bits(1) }); + + if was_suspended { + r.tasks_resume.write(|w| unsafe { w.bits(1) }); + } + } + [Operation::Read(buffer), rest @ ..] => { + let stop = stop && rest.is_empty(); + + // Set up DMA buffers. + unsafe { + self.set_rx_buffer(buffer)?; + } + + // Start read operation. + r.shorts.write(|w| { + if stop { + w.lastrx_stop().enabled(); + } else { + #[cfg(not(any(feature = "nrf52832", feature = "_nrf5340", feature = "_nrf9120")))] + w.lastrx_suspend().enabled(); + } + w + }); + #[cfg(any(feature = "nrf52832", feature = "_nrf5340", feature = "_nrf9120"))] + if !stop { + r.intenset.write(|w| w.lastrx().set()); + } + + r.tasks_startrx.write(|w| unsafe { w.bits(1) }); + + if was_suspended { + r.tasks_resume.write(|w| unsafe { w.bits(1) }); + } + + if buffer.is_empty() { + // With a zero-length buffer, LASTRX doesn't fire (because there's no last byte!), so do the STOP/SUSPEND ourselves. + if stop { + r.tasks_stop.write(|w| unsafe { w.bits(1) }); + } else { + r.tasks_suspend.write(|w| unsafe { w.bits(1) }); + } + } + } + [Operation::Write(buffer), rest @ ..] => { + let stop = stop && rest.is_empty(); + + // Set up DMA buffers. + unsafe { + self.set_tx_buffer(buffer, tx_ram_buffer)?; + } + + // Start write operation. + r.shorts.write(|w| { + if stop { + w.lasttx_stop().enabled(); + } else { + w.lasttx_suspend().enabled(); + } + w + }); + + r.tasks_starttx.write(|w| unsafe { w.bits(1) }); + + if was_suspended { + r.tasks_resume.write(|w| unsafe { w.bits(1) }); + } + + if buffer.is_empty() { + // With a zero-length buffer, LASTTX doesn't fire (because there's no last byte!), so do the STOP/SUSPEND ourselves. + if stop { + r.tasks_stop.write(|w| unsafe { w.bits(1) }); + } else { + r.tasks_suspend.write(|w| unsafe { w.bits(1) }); + } + } + } + [] => { + if stop { + if was_suspended { + r.tasks_resume.write(|w| unsafe { w.bits(1) }); + } + + r.tasks_stop.write(|w| unsafe { w.bits(1) }); + } + } } Ok(()) } - fn setup_write_read( - &mut self, - address: u8, - wr_buffer: &[u8], - rd_buffer: &mut [u8], - inten: bool, - ) -> Result<(), Error> { - match self.setup_write_read_from_ram(address, wr_buffer, rd_buffer, inten) { - Ok(_) => Ok(()), - Err(Error::BufferNotInRAM) => { - trace!("Copying TWIM tx buffer into RAM for DMA"); - let tx_ram_buf = &mut [0; FORCE_COPY_BUFFER_SIZE][..wr_buffer.len()]; - tx_ram_buf.copy_from_slice(wr_buffer); - self.setup_write_read_from_ram(address, tx_ram_buf, rd_buffer, inten) + fn check_operations(&mut self, operations: &mut [Operation<'_>]) -> Result { + compiler_fence(SeqCst); + self.check_errorsrc()?; + + match operations { + [Operation::Read(rd_buffer), Operation::Write(wr_buffer), ..] + | [Operation::Write(wr_buffer), Operation::Read(rd_buffer), ..] + if !rd_buffer.is_empty() && !wr_buffer.is_empty() => + { + self.check_tx(wr_buffer.len())?; + self.check_rx(rd_buffer.len())?; + Ok(2) } - Err(error) => Err(error), + [Operation::Read(buffer), ..] => { + self.check_rx(buffer.len())?; + Ok(1) + } + [Operation::Write(buffer), ..] => { + self.check_tx(buffer.len())?; + Ok(1) + } + [] => Ok(0), } } - fn setup_write(&mut self, address: u8, wr_buffer: &[u8], inten: bool) -> Result<(), Error> { - match self.setup_write_from_ram(address, wr_buffer, inten) { - Ok(_) => Ok(()), - Err(Error::BufferNotInRAM) => { - trace!("Copying TWIM tx buffer into RAM for DMA"); - let tx_ram_buf = &mut [0; FORCE_COPY_BUFFER_SIZE][..wr_buffer.len()]; - tx_ram_buf.copy_from_slice(wr_buffer); - self.setup_write_from_ram(address, tx_ram_buf, inten) - } - Err(error) => Err(error), + // =========================================== + + /// Execute the provided operations on the I2C bus. + /// + /// Each buffer must have a length of at most 255 bytes on the nRF52832 + /// and at most 65535 bytes on the nRF52840. + /// + /// If `stop` is set, the transaction will be terminated with a STOP + /// condition and the Twim will be stopped. Otherwise, the bus will be + /// left busy via clock stretching and Twim will be suspended. + /// + /// The nrf52832, nrf5340, and nrf9120 do not have hardware support for + /// suspending following a read operation therefore it is emulated by the + /// interrupt handler. If the latency of servicing that interrupt is + /// longer than a byte worth of clocks on the bus, the SCL clock will + /// continue to run for one or more additional bytes. This applies to + /// consecutive read operations, certain write-read-write sequences, or + /// any sequence of operations ending in a read when `stop == false`. + pub fn blocking_transaction( + &mut self, + address: u8, + mut operations: &mut [Operation<'_>], + stop: bool, + ) -> Result<(), Error> { + let mut tx_ram_buffer = [MaybeUninit::uninit(); FORCE_COPY_BUFFER_SIZE]; + while !operations.is_empty() { + self.setup_operations(address, operations, Some(&mut tx_ram_buffer), false, stop)?; + self.blocking_wait(); + let consumed = self.check_operations(operations)?; + operations = &mut operations[consumed..]; } + Ok(()) } + /// Same as [`blocking_transaction`](Twim::blocking_transaction) but will fail instead of copying data into RAM. Consult the module level documentation to learn more. + pub fn blocking_transaction_from_ram( + &mut self, + address: u8, + mut operations: &mut [Operation<'_>], + stop: bool, + ) -> Result<(), Error> { + while !operations.is_empty() { + self.setup_operations(address, operations, None, false, stop)?; + self.blocking_wait(); + let consumed = self.check_operations(operations)?; + operations = &mut operations[consumed..]; + } + Ok(()) + } + + /// Execute the provided operations on the I2C bus with timeout. + /// + /// See [`blocking_transaction`]. + #[cfg(feature = "time")] + pub fn blocking_transaction_timeout( + &mut self, + address: u8, + mut operations: &mut [Operation<'_>], + stop: bool, + timeout: Duration, + ) -> Result<(), Error> { + let mut tx_ram_buffer = [MaybeUninit::uninit(); FORCE_COPY_BUFFER_SIZE]; + while !operations.is_empty() { + self.setup_operations(address, operations, Some(&mut tx_ram_buffer), false, stop)?; + self.blocking_wait_timeout(timeout)?; + let consumed = self.check_operations(operations)?; + operations = &mut operations[consumed..]; + } + Ok(()) + } + + /// Same as [`blocking_transaction_timeout`](Twim::blocking_transaction_timeout) but will fail instead of copying data into RAM. Consult the module level documentation to learn more. + #[cfg(feature = "time")] + pub fn blocking_transaction_from_ram_timeout( + &mut self, + address: u8, + mut operations: &mut [Operation<'_>], + stop: bool, + timeout: Duration, + ) -> Result<(), Error> { + while !operations.is_empty() { + self.setup_operations(address, operations, None, false, stop)?; + self.blocking_wait_timeout(timeout)?; + let consumed = self.check_operations(operations)?; + operations = &mut operations[consumed..]; + } + Ok(()) + } + + /// Execute the provided operations on the I2C bus. + /// + /// Each buffer must have a length of at most 255 bytes on the nRF52832 + /// and at most 65535 bytes on the nRF52840. + /// + /// If `stop` is set, the transaction will be terminated with a STOP + /// condition and the Twim will be stopped. Otherwise, the bus will be + /// left busy via clock stretching and Twim will be suspended. + /// + /// The nrf52832, nrf5340, and nrf9120 do not have hardware support for + /// suspending following a read operation therefore it is emulated by the + /// interrupt handler. If the latency of servicing that interrupt is + /// longer than a byte worth of clocks on the bus, the SCL clock will + /// continue to run for one or more additional bytes. This applies to + /// consecutive read operations, certain write-read-write sequences, or + /// any sequence of operations ending in a read when `stop == false`. + pub async fn transaction( + &mut self, + address: u8, + mut operations: &mut [Operation<'_>], + stop: bool, + ) -> Result<(), Error> { + let mut tx_ram_buffer = [MaybeUninit::uninit(); FORCE_COPY_BUFFER_SIZE]; + while !operations.is_empty() { + self.setup_operations(address, operations, Some(&mut tx_ram_buffer), true, stop)?; + self.async_wait().await; + let consumed = self.check_operations(operations)?; + operations = &mut operations[consumed..]; + } + Ok(()) + } + + /// Same as [`transaction`](Twim::transaction) but will fail instead of copying data into RAM. Consult the module level documentation to learn more. + pub async fn transaction_from_ram( + &mut self, + address: u8, + mut operations: &mut [Operation<'_>], + stop: bool, + ) -> Result<(), Error> { + while !operations.is_empty() { + self.setup_operations(address, operations, None, true, stop)?; + self.async_wait().await; + let consumed = self.check_operations(operations)?; + operations = &mut operations[consumed..]; + } + Ok(()) + } + + // =========================================== + /// Write to an I2C slave. /// /// The buffer must have a length of at most 255 bytes on the nRF52832 /// and at most 65535 bytes on the nRF52840. pub fn blocking_write(&mut self, address: u8, buffer: &[u8]) -> Result<(), Error> { - self.setup_write(address, buffer, false)?; - self.blocking_wait(); - compiler_fence(SeqCst); - self.check_errorsrc()?; - self.check_tx(buffer.len())?; - Ok(()) + self.blocking_transaction(address, &mut [Operation::Write(buffer)], true) } /// Same as [`blocking_write`](Twim::blocking_write) but will fail instead of copying data into RAM. Consult the module level documentation to learn more. pub fn blocking_write_from_ram(&mut self, address: u8, buffer: &[u8]) -> Result<(), Error> { - self.setup_write_from_ram(address, buffer, false)?; - self.blocking_wait(); - compiler_fence(SeqCst); - self.check_errorsrc()?; - self.check_tx(buffer.len())?; - Ok(()) + self.blocking_transaction_from_ram(address, &mut [Operation::Write(buffer)], true) } /// Read from an I2C slave. @@ -516,12 +733,7 @@ impl<'d, T: Instance> Twim<'d, T> { /// The buffer must have a length of at most 255 bytes on the nRF52832 /// and at most 65535 bytes on the nRF52840. pub fn blocking_read(&mut self, address: u8, buffer: &mut [u8]) -> Result<(), Error> { - self.setup_read(address, buffer, false)?; - self.blocking_wait(); - compiler_fence(SeqCst); - self.check_errorsrc()?; - self.check_rx(buffer.len())?; - Ok(()) + self.blocking_transaction(address, &mut [Operation::Read(buffer)], true) } /// Write data to an I2C slave, then read data from the slave without @@ -530,13 +742,11 @@ impl<'d, T: Instance> Twim<'d, T> { /// The buffers must have a length of at most 255 bytes on the nRF52832 /// and at most 65535 bytes on the nRF52840. pub fn blocking_write_read(&mut self, address: u8, wr_buffer: &[u8], rd_buffer: &mut [u8]) -> Result<(), Error> { - self.setup_write_read(address, wr_buffer, rd_buffer, false)?; - self.blocking_wait(); - compiler_fence(SeqCst); - self.check_errorsrc()?; - self.check_tx(wr_buffer.len())?; - self.check_rx(rd_buffer.len())?; - Ok(()) + self.blocking_transaction( + address, + &mut [Operation::Write(wr_buffer), Operation::Read(rd_buffer)], + true, + ) } /// Same as [`blocking_write_read`](Twim::blocking_write_read) but will fail instead of copying data into RAM. Consult the module level documentation to learn more. @@ -546,13 +756,11 @@ impl<'d, T: Instance> Twim<'d, T> { wr_buffer: &[u8], rd_buffer: &mut [u8], ) -> Result<(), Error> { - self.setup_write_read_from_ram(address, wr_buffer, rd_buffer, false)?; - self.blocking_wait(); - compiler_fence(SeqCst); - self.check_errorsrc()?; - self.check_tx(wr_buffer.len())?; - self.check_rx(rd_buffer.len())?; - Ok(()) + self.blocking_transaction_from_ram( + address, + &mut [Operation::Write(wr_buffer), Operation::Read(rd_buffer)], + true, + ) } // =========================================== @@ -562,12 +770,7 @@ impl<'d, T: Instance> Twim<'d, T> { /// See [`blocking_write`]. #[cfg(feature = "time")] pub fn blocking_write_timeout(&mut self, address: u8, buffer: &[u8], timeout: Duration) -> Result<(), Error> { - self.setup_write(address, buffer, false)?; - self.blocking_wait_timeout(timeout)?; - compiler_fence(SeqCst); - self.check_errorsrc()?; - self.check_tx(buffer.len())?; - Ok(()) + self.blocking_transaction_timeout(address, &mut [Operation::Write(buffer)], true, timeout) } /// Same as [`blocking_write`](Twim::blocking_write) but will fail instead of copying data into RAM. Consult the module level documentation to learn more. @@ -578,12 +781,7 @@ impl<'d, T: Instance> Twim<'d, T> { buffer: &[u8], timeout: Duration, ) -> Result<(), Error> { - self.setup_write_from_ram(address, buffer, false)?; - self.blocking_wait_timeout(timeout)?; - compiler_fence(SeqCst); - self.check_errorsrc()?; - self.check_tx(buffer.len())?; - Ok(()) + self.blocking_transaction_from_ram_timeout(address, &mut [Operation::Write(buffer)], true, timeout) } /// Read from an I2C slave. @@ -592,12 +790,7 @@ impl<'d, T: Instance> Twim<'d, T> { /// and at most 65535 bytes on the nRF52840. #[cfg(feature = "time")] pub fn blocking_read_timeout(&mut self, address: u8, buffer: &mut [u8], timeout: Duration) -> Result<(), Error> { - self.setup_read(address, buffer, false)?; - self.blocking_wait_timeout(timeout)?; - compiler_fence(SeqCst); - self.check_errorsrc()?; - self.check_rx(buffer.len())?; - Ok(()) + self.blocking_transaction_timeout(address, &mut [Operation::Read(buffer)], true, timeout) } /// Write data to an I2C slave, then read data from the slave without @@ -613,13 +806,12 @@ impl<'d, T: Instance> Twim<'d, T> { rd_buffer: &mut [u8], timeout: Duration, ) -> Result<(), Error> { - self.setup_write_read(address, wr_buffer, rd_buffer, false)?; - self.blocking_wait_timeout(timeout)?; - compiler_fence(SeqCst); - self.check_errorsrc()?; - self.check_tx(wr_buffer.len())?; - self.check_rx(rd_buffer.len())?; - Ok(()) + self.blocking_transaction_timeout( + address, + &mut [Operation::Write(wr_buffer), Operation::Read(rd_buffer)], + true, + timeout, + ) } /// Same as [`blocking_write_read`](Twim::blocking_write_read) but will fail instead of copying data into RAM. Consult the module level documentation to learn more. @@ -631,13 +823,12 @@ impl<'d, T: Instance> Twim<'d, T> { rd_buffer: &mut [u8], timeout: Duration, ) -> Result<(), Error> { - self.setup_write_read_from_ram(address, wr_buffer, rd_buffer, false)?; - self.blocking_wait_timeout(timeout)?; - compiler_fence(SeqCst); - self.check_errorsrc()?; - self.check_tx(wr_buffer.len())?; - self.check_rx(rd_buffer.len())?; - Ok(()) + self.blocking_transaction_from_ram_timeout( + address, + &mut [Operation::Write(wr_buffer), Operation::Read(rd_buffer)], + true, + timeout, + ) } // =========================================== @@ -647,12 +838,7 @@ impl<'d, T: Instance> Twim<'d, T> { /// The buffer must have a length of at most 255 bytes on the nRF52832 /// and at most 65535 bytes on the nRF52840. pub async fn read(&mut self, address: u8, buffer: &mut [u8]) -> Result<(), Error> { - self.setup_read(address, buffer, true)?; - self.async_wait().await; - compiler_fence(SeqCst); - self.check_errorsrc()?; - self.check_rx(buffer.len())?; - Ok(()) + self.transaction(address, &mut [Operation::Read(buffer)], true).await } /// Write to an I2C slave. @@ -660,22 +846,13 @@ impl<'d, T: Instance> Twim<'d, T> { /// The buffer must have a length of at most 255 bytes on the nRF52832 /// and at most 65535 bytes on the nRF52840. pub async fn write(&mut self, address: u8, buffer: &[u8]) -> Result<(), Error> { - self.setup_write(address, buffer, true)?; - self.async_wait().await; - compiler_fence(SeqCst); - self.check_errorsrc()?; - self.check_tx(buffer.len())?; - Ok(()) + self.transaction(address, &mut [Operation::Write(buffer)], true).await } /// Same as [`write`](Twim::write) but will fail instead of copying data into RAM. Consult the module level documentation to learn more. pub async fn write_from_ram(&mut self, address: u8, buffer: &[u8]) -> Result<(), Error> { - self.setup_write_from_ram(address, buffer, true)?; - self.async_wait().await; - compiler_fence(SeqCst); - self.check_errorsrc()?; - self.check_tx(buffer.len())?; - Ok(()) + self.transaction_from_ram(address, &mut [Operation::Write(buffer)], true) + .await } /// Write data to an I2C slave, then read data from the slave without @@ -684,13 +861,12 @@ impl<'d, T: Instance> Twim<'d, T> { /// The buffers must have a length of at most 255 bytes on the nRF52832 /// and at most 65535 bytes on the nRF52840. pub async fn write_read(&mut self, address: u8, wr_buffer: &[u8], rd_buffer: &mut [u8]) -> Result<(), Error> { - self.setup_write_read(address, wr_buffer, rd_buffer, true)?; - self.async_wait().await; - compiler_fence(SeqCst); - self.check_errorsrc()?; - self.check_tx(wr_buffer.len())?; - self.check_rx(rd_buffer.len())?; - Ok(()) + self.transaction( + address, + &mut [Operation::Write(wr_buffer), Operation::Read(rd_buffer)], + true, + ) + .await } /// Same as [`write_read`](Twim::write_read) but will fail instead of copying data into RAM. Consult the module level documentation to learn more. @@ -700,13 +876,12 @@ impl<'d, T: Instance> Twim<'d, T> { wr_buffer: &[u8], rd_buffer: &mut [u8], ) -> Result<(), Error> { - self.setup_write_read_from_ram(address, wr_buffer, rd_buffer, true)?; - self.async_wait().await; - compiler_fence(SeqCst); - self.check_errorsrc()?; - self.check_tx(wr_buffer.len())?; - self.check_rx(rd_buffer.len())?; - Ok(()) + self.transaction_from_ram( + address, + &mut [Operation::Write(wr_buffer), Operation::Read(rd_buffer)], + true, + ) + .await } } @@ -777,16 +952,7 @@ mod eh02 { type Error = Error; fn write(&mut self, addr: u8, bytes: &[u8]) -> Result<(), Error> { - if slice_in_ram(bytes) { - self.blocking_write(addr, bytes) - } else { - let buf = &mut [0; FORCE_COPY_BUFFER_SIZE][..]; - for chunk in bytes.chunks(FORCE_COPY_BUFFER_SIZE) { - buf[..chunk.len()].copy_from_slice(chunk); - self.blocking_write(addr, &buf[..chunk.len()])?; - } - Ok(()) - } + self.blocking_write(addr, bytes) } } @@ -832,47 +998,14 @@ impl<'d, T: Instance> embedded_hal_1::i2c::ErrorType for Twim<'d, T> { } impl<'d, T: Instance> embedded_hal_1::i2c::I2c for Twim<'d, T> { - fn read(&mut self, address: u8, buffer: &mut [u8]) -> Result<(), Self::Error> { - self.blocking_read(address, buffer) - } - - fn write(&mut self, address: u8, buffer: &[u8]) -> Result<(), Self::Error> { - self.blocking_write(address, buffer) - } - - fn write_read(&mut self, address: u8, wr_buffer: &[u8], rd_buffer: &mut [u8]) -> Result<(), Self::Error> { - self.blocking_write_read(address, wr_buffer, rd_buffer) - } - - fn transaction( - &mut self, - _address: u8, - _operations: &mut [embedded_hal_1::i2c::Operation<'_>], - ) -> Result<(), Self::Error> { - todo!(); + fn transaction(&mut self, address: u8, operations: &mut [Operation<'_>]) -> Result<(), Self::Error> { + self.blocking_transaction(address, operations, true) } } impl<'d, T: Instance> embedded_hal_async::i2c::I2c for Twim<'d, T> { - async fn read(&mut self, address: u8, read: &mut [u8]) -> Result<(), Self::Error> { - self.read(address, read).await - } - - async fn write(&mut self, address: u8, write: &[u8]) -> Result<(), Self::Error> { - self.write(address, write).await - } - async fn write_read(&mut self, address: u8, write: &[u8], read: &mut [u8]) -> Result<(), Self::Error> { - self.write_read(address, write, read).await - } - - async fn transaction( - &mut self, - address: u8, - operations: &mut [embedded_hal_1::i2c::Operation<'_>], - ) -> Result<(), Self::Error> { - let _ = address; - let _ = operations; - todo!() + async fn transaction(&mut self, address: u8, operations: &mut [Operation<'_>]) -> Result<(), Self::Error> { + self.transaction(address, operations, true).await } } From 110d87bbd2c4e5b60ed99c0a259b8366e5164d41 Mon Sep 17 00:00:00 2001 From: Alex Moon Date: Mon, 19 Aug 2024 16:35:13 -0400 Subject: [PATCH 122/361] Remove support for consecutive Read operations Due to Twim hardware limitations --- embassy-nrf/src/twim.rs | 189 ++++++++++++---------------------------- 1 file changed, 57 insertions(+), 132 deletions(-) diff --git a/embassy-nrf/src/twim.rs b/embassy-nrf/src/twim.rs index d0518807c..4a239e8b5 100644 --- a/embassy-nrf/src/twim.rs +++ b/embassy-nrf/src/twim.rs @@ -105,14 +105,6 @@ impl interrupt::typelevel::Handler for InterruptHandl let r = T::regs(); let s = T::state(); - // Workaround for lack of LASTRX_SUSPEND short in some nRF chips - // Do this first to minimize latency - #[cfg(any(feature = "nrf52832", feature = "_nrf5340", feature = "_nrf9120"))] - if r.events_lastrx.read().bits() != 0 { - r.tasks_suspend.write(|w| unsafe { w.bits(1) }); - r.events_lastrx.reset(); - } - if r.events_suspended.read().bits() != 0 { s.end_waker.wake(); r.intenclr.write(|w| w.suspended().clear()); @@ -381,7 +373,6 @@ impl<'d, T: Instance> Twim<'d, T> { operations: &mut [Operation<'_>], tx_ram_buffer: Option<&mut [MaybeUninit; FORCE_COPY_BUFFER_SIZE]>, inten: bool, - stop: bool, ) -> Result<(), Error> { let r = T::regs(); @@ -401,14 +392,12 @@ impl<'d, T: Instance> Twim<'d, T> { r.intenclr .write(|w| w.suspended().clear().stopped().clear().error().clear()); } - #[cfg(any(feature = "nrf52832", feature = "_nrf5340", feature = "_nrf9120"))] - r.intenclr.write(|w| w.lastrx().clear()); match operations { [Operation::Read(rd_buffer), Operation::Write(wr_buffer), rest @ ..] if !rd_buffer.is_empty() && !wr_buffer.is_empty() => { - let stop = stop && rest.is_empty(); + let stop = rest.is_empty(); // Set up DMA buffers. unsafe { @@ -433,11 +422,9 @@ impl<'d, T: Instance> Twim<'d, T> { r.tasks_resume.write(|w| unsafe { w.bits(1) }); } } - [Operation::Write(wr_buffer), Operation::Read(rd_buffer), rest @ ..] + [Operation::Write(wr_buffer), Operation::Read(rd_buffer)] if !wr_buffer.is_empty() && !rd_buffer.is_empty() => { - let stop = stop && rest.is_empty(); - // Set up DMA buffers. unsafe { self.set_tx_buffer(wr_buffer, tx_ram_buffer)?; @@ -447,18 +434,9 @@ impl<'d, T: Instance> Twim<'d, T> { // Start write+read operation. r.shorts.write(|w| { w.lasttx_startrx().enabled(); - if stop { - w.lastrx_stop().enabled(); - } else { - #[cfg(not(any(feature = "nrf52832", feature = "_nrf5340", feature = "_nrf9120")))] - w.lastrx_suspend().enabled(); - } + w.lastrx_stop().enabled(); w }); - #[cfg(any(feature = "nrf52832", feature = "_nrf5340", feature = "_nrf9120"))] - if !stop { - r.intenset.write(|w| w.lastrx().set()); - } r.tasks_starttx.write(|w| unsafe { w.bits(1) }); @@ -466,9 +444,7 @@ impl<'d, T: Instance> Twim<'d, T> { r.tasks_resume.write(|w| unsafe { w.bits(1) }); } } - [Operation::Read(buffer), rest @ ..] => { - let stop = stop && rest.is_empty(); - + [Operation::Read(buffer)] => { // Set up DMA buffers. unsafe { self.set_rx_buffer(buffer)?; @@ -476,18 +452,9 @@ impl<'d, T: Instance> Twim<'d, T> { // Start read operation. r.shorts.write(|w| { - if stop { - w.lastrx_stop().enabled(); - } else { - #[cfg(not(any(feature = "nrf52832", feature = "_nrf5340", feature = "_nrf9120")))] - w.lastrx_suspend().enabled(); - } + w.lastrx_stop().enabled(); w }); - #[cfg(any(feature = "nrf52832", feature = "_nrf5340", feature = "_nrf9120"))] - if !stop { - r.intenset.write(|w| w.lastrx().set()); - } r.tasks_startrx.write(|w| unsafe { w.bits(1) }); @@ -496,16 +463,15 @@ impl<'d, T: Instance> Twim<'d, T> { } if buffer.is_empty() { - // With a zero-length buffer, LASTRX doesn't fire (because there's no last byte!), so do the STOP/SUSPEND ourselves. - if stop { - r.tasks_stop.write(|w| unsafe { w.bits(1) }); - } else { - r.tasks_suspend.write(|w| unsafe { w.bits(1) }); - } + // With a zero-length buffer, LASTRX doesn't fire (because there's no last byte!), so do the STOP ourselves. + r.tasks_stop.write(|w| unsafe { w.bits(1) }); } } + [Operation::Read(_), ..] => { + panic!("Suspending after a read is not supported!"); + } [Operation::Write(buffer), rest @ ..] => { - let stop = stop && rest.is_empty(); + let stop = rest.is_empty(); // Set up DMA buffers. unsafe { @@ -538,13 +504,11 @@ impl<'d, T: Instance> Twim<'d, T> { } } [] => { - if stop { - if was_suspended { - r.tasks_resume.write(|w| unsafe { w.bits(1) }); - } - - r.tasks_stop.write(|w| unsafe { w.bits(1) }); + if was_suspended { + r.tasks_resume.write(|w| unsafe { w.bits(1) }); } + + r.tasks_stop.write(|w| unsafe { w.bits(1) }); } } @@ -557,17 +521,18 @@ impl<'d, T: Instance> Twim<'d, T> { match operations { [Operation::Read(rd_buffer), Operation::Write(wr_buffer), ..] - | [Operation::Write(wr_buffer), Operation::Read(rd_buffer), ..] + | [Operation::Write(wr_buffer), Operation::Read(rd_buffer)] if !rd_buffer.is_empty() && !wr_buffer.is_empty() => { self.check_tx(wr_buffer.len())?; self.check_rx(rd_buffer.len())?; Ok(2) } - [Operation::Read(buffer), ..] => { + [Operation::Read(buffer)] => { self.check_rx(buffer.len())?; Ok(1) } + [Operation::Read(_), ..] => unreachable!(), [Operation::Write(buffer), ..] => { self.check_tx(buffer.len())?; Ok(1) @@ -583,26 +548,17 @@ impl<'d, T: Instance> Twim<'d, T> { /// Each buffer must have a length of at most 255 bytes on the nRF52832 /// and at most 65535 bytes on the nRF52840. /// - /// If `stop` is set, the transaction will be terminated with a STOP - /// condition and the Twim will be stopped. Otherwise, the bus will be - /// left busy via clock stretching and Twim will be suspended. - /// - /// The nrf52832, nrf5340, and nrf9120 do not have hardware support for - /// suspending following a read operation therefore it is emulated by the - /// interrupt handler. If the latency of servicing that interrupt is - /// longer than a byte worth of clocks on the bus, the SCL clock will - /// continue to run for one or more additional bytes. This applies to - /// consecutive read operations, certain write-read-write sequences, or - /// any sequence of operations ending in a read when `stop == false`. - pub fn blocking_transaction( - &mut self, - address: u8, - mut operations: &mut [Operation<'_>], - stop: bool, - ) -> Result<(), Error> { + /// Consecutive `Read` operations are not supported because the Twim + /// hardware does not support suspending after a read operation. (Setting + /// the SUSPEND task in response to a LASTRX event causes the final byte of + /// the operation to be ACKed instead of NAKed. When the TWIM is resumed, + /// one more byte will be read before the new operation is started, leading + /// to an Overrun error if the RXD has not been updated, or an extraneous + /// byte read into the new buffer if the RXD has been updated.) + pub fn blocking_transaction(&mut self, address: u8, mut operations: &mut [Operation<'_>]) -> Result<(), Error> { let mut tx_ram_buffer = [MaybeUninit::uninit(); FORCE_COPY_BUFFER_SIZE]; while !operations.is_empty() { - self.setup_operations(address, operations, Some(&mut tx_ram_buffer), false, stop)?; + self.setup_operations(address, operations, Some(&mut tx_ram_buffer), false)?; self.blocking_wait(); let consumed = self.check_operations(operations)?; operations = &mut operations[consumed..]; @@ -615,10 +571,9 @@ impl<'d, T: Instance> Twim<'d, T> { &mut self, address: u8, mut operations: &mut [Operation<'_>], - stop: bool, ) -> Result<(), Error> { while !operations.is_empty() { - self.setup_operations(address, operations, None, false, stop)?; + self.setup_operations(address, operations, None, false)?; self.blocking_wait(); let consumed = self.check_operations(operations)?; operations = &mut operations[consumed..]; @@ -634,12 +589,11 @@ impl<'d, T: Instance> Twim<'d, T> { &mut self, address: u8, mut operations: &mut [Operation<'_>], - stop: bool, timeout: Duration, ) -> Result<(), Error> { let mut tx_ram_buffer = [MaybeUninit::uninit(); FORCE_COPY_BUFFER_SIZE]; while !operations.is_empty() { - self.setup_operations(address, operations, Some(&mut tx_ram_buffer), false, stop)?; + self.setup_operations(address, operations, Some(&mut tx_ram_buffer), false)?; self.blocking_wait_timeout(timeout)?; let consumed = self.check_operations(operations)?; operations = &mut operations[consumed..]; @@ -653,11 +607,10 @@ impl<'d, T: Instance> Twim<'d, T> { &mut self, address: u8, mut operations: &mut [Operation<'_>], - stop: bool, timeout: Duration, ) -> Result<(), Error> { while !operations.is_empty() { - self.setup_operations(address, operations, None, false, stop)?; + self.setup_operations(address, operations, None, false)?; self.blocking_wait_timeout(timeout)?; let consumed = self.check_operations(operations)?; operations = &mut operations[consumed..]; @@ -670,26 +623,17 @@ impl<'d, T: Instance> Twim<'d, T> { /// Each buffer must have a length of at most 255 bytes on the nRF52832 /// and at most 65535 bytes on the nRF52840. /// - /// If `stop` is set, the transaction will be terminated with a STOP - /// condition and the Twim will be stopped. Otherwise, the bus will be - /// left busy via clock stretching and Twim will be suspended. - /// - /// The nrf52832, nrf5340, and nrf9120 do not have hardware support for - /// suspending following a read operation therefore it is emulated by the - /// interrupt handler. If the latency of servicing that interrupt is - /// longer than a byte worth of clocks on the bus, the SCL clock will - /// continue to run for one or more additional bytes. This applies to - /// consecutive read operations, certain write-read-write sequences, or - /// any sequence of operations ending in a read when `stop == false`. - pub async fn transaction( - &mut self, - address: u8, - mut operations: &mut [Operation<'_>], - stop: bool, - ) -> Result<(), Error> { + /// Consecutive `Read` operations are not supported because the Twim + /// hardware does not support suspending after a read operation. (Setting + /// the SUSPEND task in response to a LASTRX event causes the final byte of + /// the operation to be ACKed instead of NAKed. When the TWIM is resumed, + /// one more byte will be read before the new operation is started, leading + /// to an Overrun error if the RXD has not been updated, or an extraneous + /// byte read into the new buffer if the RXD has been updated.) + pub async fn transaction(&mut self, address: u8, mut operations: &mut [Operation<'_>]) -> Result<(), Error> { let mut tx_ram_buffer = [MaybeUninit::uninit(); FORCE_COPY_BUFFER_SIZE]; while !operations.is_empty() { - self.setup_operations(address, operations, Some(&mut tx_ram_buffer), true, stop)?; + self.setup_operations(address, operations, Some(&mut tx_ram_buffer), true)?; self.async_wait().await; let consumed = self.check_operations(operations)?; operations = &mut operations[consumed..]; @@ -702,10 +646,9 @@ impl<'d, T: Instance> Twim<'d, T> { &mut self, address: u8, mut operations: &mut [Operation<'_>], - stop: bool, ) -> Result<(), Error> { while !operations.is_empty() { - self.setup_operations(address, operations, None, true, stop)?; + self.setup_operations(address, operations, None, true)?; self.async_wait().await; let consumed = self.check_operations(operations)?; operations = &mut operations[consumed..]; @@ -720,12 +663,12 @@ impl<'d, T: Instance> Twim<'d, T> { /// The buffer must have a length of at most 255 bytes on the nRF52832 /// and at most 65535 bytes on the nRF52840. pub fn blocking_write(&mut self, address: u8, buffer: &[u8]) -> Result<(), Error> { - self.blocking_transaction(address, &mut [Operation::Write(buffer)], true) + self.blocking_transaction(address, &mut [Operation::Write(buffer)]) } /// Same as [`blocking_write`](Twim::blocking_write) but will fail instead of copying data into RAM. Consult the module level documentation to learn more. pub fn blocking_write_from_ram(&mut self, address: u8, buffer: &[u8]) -> Result<(), Error> { - self.blocking_transaction_from_ram(address, &mut [Operation::Write(buffer)], true) + self.blocking_transaction_from_ram(address, &mut [Operation::Write(buffer)]) } /// Read from an I2C slave. @@ -733,7 +676,7 @@ impl<'d, T: Instance> Twim<'d, T> { /// The buffer must have a length of at most 255 bytes on the nRF52832 /// and at most 65535 bytes on the nRF52840. pub fn blocking_read(&mut self, address: u8, buffer: &mut [u8]) -> Result<(), Error> { - self.blocking_transaction(address, &mut [Operation::Read(buffer)], true) + self.blocking_transaction(address, &mut [Operation::Read(buffer)]) } /// Write data to an I2C slave, then read data from the slave without @@ -742,11 +685,7 @@ impl<'d, T: Instance> Twim<'d, T> { /// The buffers must have a length of at most 255 bytes on the nRF52832 /// and at most 65535 bytes on the nRF52840. pub fn blocking_write_read(&mut self, address: u8, wr_buffer: &[u8], rd_buffer: &mut [u8]) -> Result<(), Error> { - self.blocking_transaction( - address, - &mut [Operation::Write(wr_buffer), Operation::Read(rd_buffer)], - true, - ) + self.blocking_transaction(address, &mut [Operation::Write(wr_buffer), Operation::Read(rd_buffer)]) } /// Same as [`blocking_write_read`](Twim::blocking_write_read) but will fail instead of copying data into RAM. Consult the module level documentation to learn more. @@ -756,11 +695,7 @@ impl<'d, T: Instance> Twim<'d, T> { wr_buffer: &[u8], rd_buffer: &mut [u8], ) -> Result<(), Error> { - self.blocking_transaction_from_ram( - address, - &mut [Operation::Write(wr_buffer), Operation::Read(rd_buffer)], - true, - ) + self.blocking_transaction_from_ram(address, &mut [Operation::Write(wr_buffer), Operation::Read(rd_buffer)]) } // =========================================== @@ -770,7 +705,7 @@ impl<'d, T: Instance> Twim<'d, T> { /// See [`blocking_write`]. #[cfg(feature = "time")] pub fn blocking_write_timeout(&mut self, address: u8, buffer: &[u8], timeout: Duration) -> Result<(), Error> { - self.blocking_transaction_timeout(address, &mut [Operation::Write(buffer)], true, timeout) + self.blocking_transaction_timeout(address, &mut [Operation::Write(buffer)], timeout) } /// Same as [`blocking_write`](Twim::blocking_write) but will fail instead of copying data into RAM. Consult the module level documentation to learn more. @@ -781,7 +716,7 @@ impl<'d, T: Instance> Twim<'d, T> { buffer: &[u8], timeout: Duration, ) -> Result<(), Error> { - self.blocking_transaction_from_ram_timeout(address, &mut [Operation::Write(buffer)], true, timeout) + self.blocking_transaction_from_ram_timeout(address, &mut [Operation::Write(buffer)], timeout) } /// Read from an I2C slave. @@ -790,7 +725,7 @@ impl<'d, T: Instance> Twim<'d, T> { /// and at most 65535 bytes on the nRF52840. #[cfg(feature = "time")] pub fn blocking_read_timeout(&mut self, address: u8, buffer: &mut [u8], timeout: Duration) -> Result<(), Error> { - self.blocking_transaction_timeout(address, &mut [Operation::Read(buffer)], true, timeout) + self.blocking_transaction_timeout(address, &mut [Operation::Read(buffer)], timeout) } /// Write data to an I2C slave, then read data from the slave without @@ -809,7 +744,6 @@ impl<'d, T: Instance> Twim<'d, T> { self.blocking_transaction_timeout( address, &mut [Operation::Write(wr_buffer), Operation::Read(rd_buffer)], - true, timeout, ) } @@ -826,7 +760,6 @@ impl<'d, T: Instance> Twim<'d, T> { self.blocking_transaction_from_ram_timeout( address, &mut [Operation::Write(wr_buffer), Operation::Read(rd_buffer)], - true, timeout, ) } @@ -838,7 +771,7 @@ impl<'d, T: Instance> Twim<'d, T> { /// The buffer must have a length of at most 255 bytes on the nRF52832 /// and at most 65535 bytes on the nRF52840. pub async fn read(&mut self, address: u8, buffer: &mut [u8]) -> Result<(), Error> { - self.transaction(address, &mut [Operation::Read(buffer)], true).await + self.transaction(address, &mut [Operation::Read(buffer)]).await } /// Write to an I2C slave. @@ -846,12 +779,12 @@ impl<'d, T: Instance> Twim<'d, T> { /// The buffer must have a length of at most 255 bytes on the nRF52832 /// and at most 65535 bytes on the nRF52840. pub async fn write(&mut self, address: u8, buffer: &[u8]) -> Result<(), Error> { - self.transaction(address, &mut [Operation::Write(buffer)], true).await + self.transaction(address, &mut [Operation::Write(buffer)]).await } /// Same as [`write`](Twim::write) but will fail instead of copying data into RAM. Consult the module level documentation to learn more. pub async fn write_from_ram(&mut self, address: u8, buffer: &[u8]) -> Result<(), Error> { - self.transaction_from_ram(address, &mut [Operation::Write(buffer)], true) + self.transaction_from_ram(address, &mut [Operation::Write(buffer)]) .await } @@ -861,12 +794,8 @@ impl<'d, T: Instance> Twim<'d, T> { /// The buffers must have a length of at most 255 bytes on the nRF52832 /// and at most 65535 bytes on the nRF52840. pub async fn write_read(&mut self, address: u8, wr_buffer: &[u8], rd_buffer: &mut [u8]) -> Result<(), Error> { - self.transaction( - address, - &mut [Operation::Write(wr_buffer), Operation::Read(rd_buffer)], - true, - ) - .await + self.transaction(address, &mut [Operation::Write(wr_buffer), Operation::Read(rd_buffer)]) + .await } /// Same as [`write_read`](Twim::write_read) but will fail instead of copying data into RAM. Consult the module level documentation to learn more. @@ -876,12 +805,8 @@ impl<'d, T: Instance> Twim<'d, T> { wr_buffer: &[u8], rd_buffer: &mut [u8], ) -> Result<(), Error> { - self.transaction_from_ram( - address, - &mut [Operation::Write(wr_buffer), Operation::Read(rd_buffer)], - true, - ) - .await + self.transaction_from_ram(address, &mut [Operation::Write(wr_buffer), Operation::Read(rd_buffer)]) + .await } } @@ -999,13 +924,13 @@ impl<'d, T: Instance> embedded_hal_1::i2c::ErrorType for Twim<'d, T> { impl<'d, T: Instance> embedded_hal_1::i2c::I2c for Twim<'d, T> { fn transaction(&mut self, address: u8, operations: &mut [Operation<'_>]) -> Result<(), Self::Error> { - self.blocking_transaction(address, operations, true) + self.blocking_transaction(address, operations) } } impl<'d, T: Instance> embedded_hal_async::i2c::I2c for Twim<'d, T> { async fn transaction(&mut self, address: u8, operations: &mut [Operation<'_>]) -> Result<(), Self::Error> { - self.transaction(address, operations, true).await + self.transaction(address, operations).await } } From 55805de05b4be39546c0b4c5e2ebc97cd3b1f75c Mon Sep 17 00:00:00 2001 From: Alex Moon Date: Wed, 21 Aug 2024 10:57:14 -0400 Subject: [PATCH 123/361] Refactoring and cleanup --- embassy-nrf/src/twim.rs | 183 +++++++++++++++++++++------------------- 1 file changed, 96 insertions(+), 87 deletions(-) diff --git a/embassy-nrf/src/twim.rs b/embassy-nrf/src/twim.rs index 4a239e8b5..187fce021 100644 --- a/embassy-nrf/src/twim.rs +++ b/embassy-nrf/src/twim.rs @@ -311,6 +311,7 @@ impl<'d, T: Instance> Twim<'d, T> { let r = T::regs(); loop { if r.events_suspended.read().bits() != 0 || r.events_stopped.read().bits() != 0 { + r.events_suspended.reset(); r.events_stopped.reset(); break; } @@ -372,15 +373,15 @@ impl<'d, T: Instance> Twim<'d, T> { address: u8, operations: &mut [Operation<'_>], tx_ram_buffer: Option<&mut [MaybeUninit; FORCE_COPY_BUFFER_SIZE]>, + last_op: Option<&Operation<'_>>, inten: bool, - ) -> Result<(), Error> { + ) -> Result { let r = T::regs(); compiler_fence(SeqCst); r.address.write(|w| unsafe { w.address().bits(address) }); - let was_suspended = r.events_suspended.read().bits() != 0; r.events_suspended.reset(); r.events_stopped.reset(); r.events_error.reset(); @@ -393,10 +394,12 @@ impl<'d, T: Instance> Twim<'d, T> { .write(|w| w.suspended().clear().stopped().clear().error().clear()); } + assert!(!operations.is_empty()); match operations { - [Operation::Read(rd_buffer), Operation::Write(wr_buffer), rest @ ..] - if !rd_buffer.is_empty() && !wr_buffer.is_empty() => - { + [Operation::Read(_), Operation::Read(_), ..] => { + panic!("Consecutive read operations are not supported!") + } + [Operation::Read(rd_buffer), Operation::Write(wr_buffer), rest @ ..] => { let stop = rest.is_empty(); // Set up DMA buffers. @@ -417,10 +420,38 @@ impl<'d, T: Instance> Twim<'d, T> { // Start read+write operation. r.tasks_startrx.write(|w| unsafe { w.bits(1) }); - - if was_suspended { + if last_op.is_some() { r.tasks_resume.write(|w| unsafe { w.bits(1) }); } + + // TODO: Handle empty write buffer + if rd_buffer.is_empty() { + // With a zero-length buffer, LASTRX doesn't fire (because there's no last byte!), so do the STARTTX ourselves. + r.tasks_starttx.write(|w| unsafe { w.bits(1) }); + } + + Ok(2) + } + [Operation::Read(buffer)] => { + // Set up DMA buffers. + unsafe { + self.set_rx_buffer(buffer)?; + } + + r.shorts.write(|w| w.lastrx_stop().enabled()); + + // Start read operation. + r.tasks_startrx.write(|w| unsafe { w.bits(1) }); + if last_op.is_some() { + r.tasks_resume.write(|w| unsafe { w.bits(1) }); + } + + if buffer.is_empty() { + // With a zero-length buffer, LASTRX doesn't fire (because there's no last byte!), so do the STOP ourselves. + r.tasks_stop.write(|w| unsafe { w.bits(1) }); + } + + Ok(1) } [Operation::Write(wr_buffer), Operation::Read(rd_buffer)] if !wr_buffer.is_empty() && !rd_buffer.is_empty() => @@ -439,36 +470,11 @@ impl<'d, T: Instance> Twim<'d, T> { }); r.tasks_starttx.write(|w| unsafe { w.bits(1) }); - - if was_suspended { - r.tasks_resume.write(|w| unsafe { w.bits(1) }); - } - } - [Operation::Read(buffer)] => { - // Set up DMA buffers. - unsafe { - self.set_rx_buffer(buffer)?; - } - - // Start read operation. - r.shorts.write(|w| { - w.lastrx_stop().enabled(); - w - }); - - r.tasks_startrx.write(|w| unsafe { w.bits(1) }); - - if was_suspended { + if last_op.is_some() { r.tasks_resume.write(|w| unsafe { w.bits(1) }); } - if buffer.is_empty() { - // With a zero-length buffer, LASTRX doesn't fire (because there's no last byte!), so do the STOP ourselves. - r.tasks_stop.write(|w| unsafe { w.bits(1) }); - } - } - [Operation::Read(_), ..] => { - panic!("Suspending after a read is not supported!"); + Ok(2) } [Operation::Write(buffer), rest @ ..] => { let stop = rest.is_empty(); @@ -489,8 +495,7 @@ impl<'d, T: Instance> Twim<'d, T> { }); r.tasks_starttx.write(|w| unsafe { w.bits(1) }); - - if was_suspended { + if last_op.is_some() { r.tasks_resume.write(|w| unsafe { w.bits(1) }); } @@ -502,43 +507,33 @@ impl<'d, T: Instance> Twim<'d, T> { r.tasks_suspend.write(|w| unsafe { w.bits(1) }); } } - } - [] => { - if was_suspended { - r.tasks_resume.write(|w| unsafe { w.bits(1) }); - } - r.tasks_stop.write(|w| unsafe { w.bits(1) }); + Ok(1) } + [] => unreachable!(), } - - Ok(()) } - fn check_operations(&mut self, operations: &mut [Operation<'_>]) -> Result { + fn check_operations(&mut self, operations: &[Operation<'_>]) -> Result<(), Error> { compiler_fence(SeqCst); self.check_errorsrc()?; + assert!(operations.len() == 1 || operations.len() == 2); match operations { - [Operation::Read(rd_buffer), Operation::Write(wr_buffer), ..] - | [Operation::Write(wr_buffer), Operation::Read(rd_buffer)] - if !rd_buffer.is_empty() && !wr_buffer.is_empty() => - { - self.check_tx(wr_buffer.len())?; + [Operation::Read(rd_buffer), Operation::Write(wr_buffer)] + | [Operation::Write(wr_buffer), Operation::Read(rd_buffer)] => { self.check_rx(rd_buffer.len())?; - Ok(2) + self.check_tx(wr_buffer.len())?; } [Operation::Read(buffer)] => { self.check_rx(buffer.len())?; - Ok(1) } - [Operation::Read(_), ..] => unreachable!(), [Operation::Write(buffer), ..] => { self.check_tx(buffer.len())?; - Ok(1) } - [] => Ok(0), + _ => unreachable!(), } + Ok(()) } // =========================================== @@ -548,20 +543,21 @@ impl<'d, T: Instance> Twim<'d, T> { /// Each buffer must have a length of at most 255 bytes on the nRF52832 /// and at most 65535 bytes on the nRF52840. /// - /// Consecutive `Read` operations are not supported because the Twim - /// hardware does not support suspending after a read operation. (Setting - /// the SUSPEND task in response to a LASTRX event causes the final byte of - /// the operation to be ACKed instead of NAKed. When the TWIM is resumed, - /// one more byte will be read before the new operation is started, leading - /// to an Overrun error if the RXD has not been updated, or an extraneous - /// byte read into the new buffer if the RXD has been updated.) + /// Consecutive `Operation::Read`s are not supported due to hardware + /// limitations. + /// + /// An `Operation::Write` following an `Operation::Read` must have a + /// non-empty buffer. pub fn blocking_transaction(&mut self, address: u8, mut operations: &mut [Operation<'_>]) -> Result<(), Error> { let mut tx_ram_buffer = [MaybeUninit::uninit(); FORCE_COPY_BUFFER_SIZE]; + let mut last_op = None; while !operations.is_empty() { - self.setup_operations(address, operations, Some(&mut tx_ram_buffer), false)?; + let ops = self.setup_operations(address, operations, Some(&mut tx_ram_buffer), last_op, false)?; + let (in_progress, rest) = operations.split_at_mut(ops); self.blocking_wait(); - let consumed = self.check_operations(operations)?; - operations = &mut operations[consumed..]; + self.check_operations(in_progress)?; + last_op = in_progress.last(); + operations = rest; } Ok(()) } @@ -572,11 +568,14 @@ impl<'d, T: Instance> Twim<'d, T> { address: u8, mut operations: &mut [Operation<'_>], ) -> Result<(), Error> { + let mut last_op = None; while !operations.is_empty() { - self.setup_operations(address, operations, None, false)?; + let ops = self.setup_operations(address, operations, None, last_op, false)?; + let (in_progress, rest) = operations.split_at_mut(ops); self.blocking_wait(); - let consumed = self.check_operations(operations)?; - operations = &mut operations[consumed..]; + self.check_operations(in_progress)?; + last_op = in_progress.last(); + operations = rest; } Ok(()) } @@ -592,11 +591,14 @@ impl<'d, T: Instance> Twim<'d, T> { timeout: Duration, ) -> Result<(), Error> { let mut tx_ram_buffer = [MaybeUninit::uninit(); FORCE_COPY_BUFFER_SIZE]; + let mut last_op = None; while !operations.is_empty() { - self.setup_operations(address, operations, Some(&mut tx_ram_buffer), false)?; + let ops = self.setup_operations(address, operations, Some(&mut tx_ram_buffer), last_op, false)?; + let (in_progress, rest) = operations.split_at_mut(ops); self.blocking_wait_timeout(timeout)?; - let consumed = self.check_operations(operations)?; - operations = &mut operations[consumed..]; + self.check_operations(in_progress)?; + last_op = in_progress.last(); + operations = rest; } Ok(()) } @@ -609,11 +611,14 @@ impl<'d, T: Instance> Twim<'d, T> { mut operations: &mut [Operation<'_>], timeout: Duration, ) -> Result<(), Error> { + let mut last_op = None; while !operations.is_empty() { - self.setup_operations(address, operations, None, false)?; + let ops = self.setup_operations(address, operations, None, last_op, false)?; + let (in_progress, rest) = operations.split_at_mut(ops); self.blocking_wait_timeout(timeout)?; - let consumed = self.check_operations(operations)?; - operations = &mut operations[consumed..]; + self.check_operations(in_progress)?; + last_op = in_progress.last(); + operations = rest; } Ok(()) } @@ -623,20 +628,21 @@ impl<'d, T: Instance> Twim<'d, T> { /// Each buffer must have a length of at most 255 bytes on the nRF52832 /// and at most 65535 bytes on the nRF52840. /// - /// Consecutive `Read` operations are not supported because the Twim - /// hardware does not support suspending after a read operation. (Setting - /// the SUSPEND task in response to a LASTRX event causes the final byte of - /// the operation to be ACKed instead of NAKed. When the TWIM is resumed, - /// one more byte will be read before the new operation is started, leading - /// to an Overrun error if the RXD has not been updated, or an extraneous - /// byte read into the new buffer if the RXD has been updated.) + /// Consecutive `Operation::Read`s are not supported due to hardware + /// limitations. + /// + /// An `Operation::Write` following an `Operation::Read` must have a + /// non-empty buffer. pub async fn transaction(&mut self, address: u8, mut operations: &mut [Operation<'_>]) -> Result<(), Error> { let mut tx_ram_buffer = [MaybeUninit::uninit(); FORCE_COPY_BUFFER_SIZE]; + let mut last_op = None; while !operations.is_empty() { - self.setup_operations(address, operations, Some(&mut tx_ram_buffer), true)?; + let ops = self.setup_operations(address, operations, Some(&mut tx_ram_buffer), last_op, true)?; + let (in_progress, rest) = operations.split_at_mut(ops); self.async_wait().await; - let consumed = self.check_operations(operations)?; - operations = &mut operations[consumed..]; + self.check_operations(in_progress)?; + last_op = in_progress.last(); + operations = rest; } Ok(()) } @@ -647,11 +653,14 @@ impl<'d, T: Instance> Twim<'d, T> { address: u8, mut operations: &mut [Operation<'_>], ) -> Result<(), Error> { + let mut last_op = None; while !operations.is_empty() { - self.setup_operations(address, operations, None, true)?; + let ops = self.setup_operations(address, operations, None, last_op, true)?; + let (in_progress, rest) = operations.split_at_mut(ops); self.async_wait().await; - let consumed = self.check_operations(operations)?; - operations = &mut operations[consumed..]; + self.check_operations(in_progress)?; + last_op = in_progress.last(); + operations = rest; } Ok(()) } From 1fed8ac5dbb239e53cdc51b10c27a0c58ea92aeb Mon Sep 17 00:00:00 2001 From: Vincenzo Marturano Date: Thu, 24 Oct 2024 15:12:04 +0200 Subject: [PATCH 124/361] Allow separate control of duty cycle for each channel in a pwm slice by splitting the Pwm driver. --- embassy-rp/src/pwm.rs | 52 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 52 insertions(+) diff --git a/embassy-rp/src/pwm.rs b/embassy-rp/src/pwm.rs index cfb99c569..4f11eb8f7 100644 --- a/embassy-rp/src/pwm.rs +++ b/embassy-rp/src/pwm.rs @@ -344,6 +344,58 @@ impl<'d> Pwm<'d> { fn bit(&self) -> u32 { 1 << self.slice as usize } + + #[inline] + /// Split Pwm driver to allow separate duty cycle control of each channel + pub fn split(&self) -> (PwmOutput,PwmOutput){ + (PwmOutput::new(PwmChannel::A,self.slice.clone()),PwmOutput::new(PwmChannel::B,self.slice.clone())) + } +} + +enum PwmChannel{ + A, + B +} + +/// Single channel of Pwm driver. +pub struct PwmOutput { + channel: PwmChannel, + slice: usize +} + +impl PwmOutput { + fn new(channel: PwmChannel,slice: usize) -> Self{ + Self { channel, slice } + } +} + +impl SetDutyCycle for PwmOutput { + fn max_duty_cycle(&self) -> u16 { + pac::PWM.ch(self.slice).top().read().top() + } + + fn set_duty_cycle(&mut self, duty: u16) -> Result<(), Self::Error> { + let max_duty = self.max_duty_cycle(); + if duty > max_duty { + return Err(PwmError::InvalidDutyCycle); + } + + let p = pac::PWM.ch(self.slice); + match self.channel { + PwmChannel::A => { + p.cc().modify(|w| { + w.set_a(duty); + }); + }, + PwmChannel::B => { + p.cc().modify(|w| { + w.set_b(duty); + }); + }, + } + + Ok(()) + } } /// Batch representation of PWM slices. From 2596de52bba32b0d1e755ab18909c10a9e663aad Mon Sep 17 00:00:00 2001 From: Vincenzo Marturano Date: Thu, 24 Oct 2024 15:39:22 +0200 Subject: [PATCH 125/361] Fixed missing trait implementation for PwmOutput. --- embassy-rp/src/pwm.rs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/embassy-rp/src/pwm.rs b/embassy-rp/src/pwm.rs index 4f11eb8f7..66f0dc7f2 100644 --- a/embassy-rp/src/pwm.rs +++ b/embassy-rp/src/pwm.rs @@ -369,6 +369,10 @@ impl PwmOutput { } } +impl ErrorType for PwmOutput { + type Error = PwmError; +} + impl SetDutyCycle for PwmOutput { fn max_duty_cycle(&self) -> u16 { pac::PWM.ch(self.slice).top().read().top() From 31662eaeef0762a7c7b9c95aee61a9066d7e447a Mon Sep 17 00:00:00 2001 From: Vincenzo Marturano Date: Thu, 24 Oct 2024 16:04:32 +0200 Subject: [PATCH 126/361] Add new() method to PwmBatch so it can be istantiated. --- embassy-rp/src/pwm.rs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/embassy-rp/src/pwm.rs b/embassy-rp/src/pwm.rs index 66f0dc7f2..4da5e5e28 100644 --- a/embassy-rp/src/pwm.rs +++ b/embassy-rp/src/pwm.rs @@ -406,6 +406,10 @@ impl SetDutyCycle for PwmOutput { pub struct PwmBatch(u32); impl PwmBatch { + #[inline] + pub fn new() -> Self{ + Self(0) + } #[inline] /// Enable a PWM slice in this batch. pub fn enable(&mut self, pwm: &Pwm<'_>) { From 052463212b6b6c1647f517ce38272dd4dd3d4353 Mon Sep 17 00:00:00 2001 From: Vincenzo Marturano Date: Thu, 24 Oct 2024 16:20:28 +0200 Subject: [PATCH 127/361] Revert "Add new() method to PwmBatch so it can be istantiated." This reverts commit 31662eaeef0762a7c7b9c95aee61a9066d7e447a. --- embassy-rp/src/pwm.rs | 4 ---- 1 file changed, 4 deletions(-) diff --git a/embassy-rp/src/pwm.rs b/embassy-rp/src/pwm.rs index 4da5e5e28..66f0dc7f2 100644 --- a/embassy-rp/src/pwm.rs +++ b/embassy-rp/src/pwm.rs @@ -406,10 +406,6 @@ impl SetDutyCycle for PwmOutput { pub struct PwmBatch(u32); impl PwmBatch { - #[inline] - pub fn new() -> Self{ - Self(0) - } #[inline] /// Enable a PWM slice in this batch. pub fn enable(&mut self, pwm: &Pwm<'_>) { From 336ef01b05e87e6b2b3c9b98e9bdca0c7d78a6af Mon Sep 17 00:00:00 2001 From: Vincenzo Marturano Date: Thu, 24 Oct 2024 19:36:54 +0200 Subject: [PATCH 128/361] Implemented owned split. --- embassy-rp/src/pwm.rs | 47 ++++++++++++++++++++++++++----------------- 1 file changed, 29 insertions(+), 18 deletions(-) diff --git a/embassy-rp/src/pwm.rs b/embassy-rp/src/pwm.rs index 66f0dc7f2..5fea0901a 100644 --- a/embassy-rp/src/pwm.rs +++ b/embassy-rp/src/pwm.rs @@ -347,25 +347,36 @@ impl<'d> Pwm<'d> { #[inline] /// Split Pwm driver to allow separate duty cycle control of each channel - pub fn split(&self) -> (PwmOutput,PwmOutput){ - (PwmOutput::new(PwmChannel::A,self.slice.clone()),PwmOutput::new(PwmChannel::B,self.slice.clone())) + pub fn split(self) -> (Option, Option) { + + let pwm_output_a = if let Some(pin_a) = self.pin_a { + Some(PwmOutput::new(PwmChannelPin::A(pin_a), self.slice.clone())) + }; + + let pwm_output_b = if let Some(pin_b) = self.pin_b { + Some(PwmOutput::new(PwmChannelPin::B(pin_b), self.slice.clone())) + }; + + (pwm_output_a,pwm_output_b) } + } -enum PwmChannel{ - A, - B +enum PwmChannelPin<'d> { + A(PeripheralRef<'d, AnyPin>), + B(PeripheralRef<'d, AnyPin>) } /// Single channel of Pwm driver. -pub struct PwmOutput { - channel: PwmChannel, - slice: usize +pub struct PwmOutput<'d> { + //pin that can be ether ChannelAPin or ChannelBPin + channel_pin: PwmChannelPin<'d> , + slice: usize, } -impl PwmOutput { - fn new(channel: PwmChannel,slice: usize) -> Self{ - Self { channel, slice } +impl <'d> PwmOutput<'d> { + fn new(channel_pin: PwmChannelPin<'d>, slice: usize) -> Self { + Self { channel_pin ,slice } } } @@ -373,7 +384,7 @@ impl ErrorType for PwmOutput { type Error = PwmError; } -impl SetDutyCycle for PwmOutput { +impl<'d> SetDutyCycle for PwmOutput<'d> { fn max_duty_cycle(&self) -> u16 { pac::PWM.ch(self.slice).top().read().top() } @@ -385,19 +396,19 @@ impl SetDutyCycle for PwmOutput { } let p = pac::PWM.ch(self.slice); - match self.channel { - PwmChannel::A => { + match self.channel_pin { + PwmChannelPin::A => { p.cc().modify(|w| { w.set_a(duty); }); - }, - PwmChannel::B => { + } + PwmChannelPin::B => { p.cc().modify(|w| { w.set_b(duty); }); - }, + } } - + Ok(()) } } From 354ff3bac31a910598a6e350499262b3163d50ef Mon Sep 17 00:00:00 2001 From: Vincenzo Marturano Date: Thu, 24 Oct 2024 19:46:23 +0200 Subject: [PATCH 129/361] Fix missing lifetime --- embassy-rp/src/pwm.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/embassy-rp/src/pwm.rs b/embassy-rp/src/pwm.rs index 5fea0901a..2df2cfad3 100644 --- a/embassy-rp/src/pwm.rs +++ b/embassy-rp/src/pwm.rs @@ -347,7 +347,7 @@ impl<'d> Pwm<'d> { #[inline] /// Split Pwm driver to allow separate duty cycle control of each channel - pub fn split(self) -> (Option, Option) { + pub fn split(self) -> (Option>, Option>) { let pwm_output_a = if let Some(pin_a) = self.pin_a { Some(PwmOutput::new(PwmChannelPin::A(pin_a), self.slice.clone())) @@ -380,7 +380,7 @@ impl <'d> PwmOutput<'d> { } } -impl ErrorType for PwmOutput { +impl<'d> ErrorType for PwmOutput<'d> { type Error = PwmError; } From 874dbec5a4d11c57c3683a27ac09584e728694c2 Mon Sep 17 00:00:00 2001 From: Vincenzo Marturano Date: Thu, 24 Oct 2024 19:52:09 +0200 Subject: [PATCH 130/361] Fixed mistakes. --- embassy-rp/src/pwm.rs | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/embassy-rp/src/pwm.rs b/embassy-rp/src/pwm.rs index 2df2cfad3..443308158 100644 --- a/embassy-rp/src/pwm.rs +++ b/embassy-rp/src/pwm.rs @@ -351,10 +351,14 @@ impl<'d> Pwm<'d> { let pwm_output_a = if let Some(pin_a) = self.pin_a { Some(PwmOutput::new(PwmChannelPin::A(pin_a), self.slice.clone())) + }else{ + None }; let pwm_output_b = if let Some(pin_b) = self.pin_b { Some(PwmOutput::new(PwmChannelPin::B(pin_b), self.slice.clone())) + }else { + None }; (pwm_output_a,pwm_output_b) @@ -397,12 +401,12 @@ impl<'d> SetDutyCycle for PwmOutput<'d> { let p = pac::PWM.ch(self.slice); match self.channel_pin { - PwmChannelPin::A => { + PwmChannelPin::A(_) => { p.cc().modify(|w| { w.set_a(duty); }); } - PwmChannelPin::B => { + PwmChannelPin::B(_) => { p.cc().modify(|w| { w.set_b(duty); }); From 7b62d70d184ca9e8e7c6864d6ed39b4e8f75f02f Mon Sep 17 00:00:00 2001 From: Haobo Gu Date: Fri, 25 Oct 2024 18:27:48 +0800 Subject: [PATCH 131/361] feat(ospi): add memory mapped mode Signed-off-by: Haobo Gu --- embassy-stm32/src/ospi/mod.rs | 63 ++++++++++++++++++++++++++--------- 1 file changed, 48 insertions(+), 15 deletions(-) diff --git a/embassy-stm32/src/ospi/mod.rs b/embassy-stm32/src/ospi/mod.rs index 0574f7816..7ccafe361 100644 --- a/embassy-stm32/src/ospi/mod.rs +++ b/embassy-stm32/src/ospi/mod.rs @@ -179,39 +179,72 @@ pub struct Ospi<'d, T: Instance, M: PeriMode> { } impl<'d, T: Instance, M: PeriMode> Ospi<'d, T, M> { - pub fn enable_memory_mapped_mode(&mut self) { + /// Enter memory mode. + /// The Input `TransferConfig` is used to configure the read operation in memory mode + pub fn enable_memory_mapped_mode( + &mut self, + read_config: TransferConfig, + write_config: TransferConfig, + ) -> Result<(), OspiError> { + // Use configure command to set read config + self.configure_command(&read_config, None)?; + let reg = T::REGS; while reg.sr().read().busy() { info!("wait ospi busy"); } - + reg.ccr().modify(|r| { - r.set_isize(crate::ospi::vals::SizeInBits::_8BIT); - r.set_adsize(crate::ospi::vals::SizeInBits::_24BIT); - r.set_admode(crate::ospi::vals::PhaseMode::ONELINE); - r.set_imode(crate::ospi::vals::PhaseMode::ONELINE); - r.set_dmode(crate::ospi::vals::PhaseMode::FOURLINES); + r.set_dqse(false); + r.set_sioo(true); }); - + + // Set wrting configurations, there are separate registers for write configurations in memory mapped mode + reg.wccr().modify(|w| { + w.set_imode(PhaseMode::from_bits(write_config.iwidth.into())); + w.set_idtr(write_config.idtr); + w.set_isize(SizeInBits::from_bits(write_config.isize.into())); + + w.set_admode(PhaseMode::from_bits(write_config.adwidth.into())); + w.set_addtr(write_config.idtr); + w.set_adsize(SizeInBits::from_bits(write_config.adsize.into())); + + w.set_dmode(PhaseMode::from_bits(write_config.dwidth.into())); + w.set_ddtr(write_config.ddtr); + + w.set_abmode(PhaseMode::from_bits(write_config.abwidth.into())); + w.set_dqse(true); + }); + + reg.wtcr().modify(|w| w.set_dcyc(write_config.dummy.into())); + + // Enable memory mapped mode reg.cr().modify(|r| { r.set_fmode(crate::ospi::vals::FunctionalMode::MEMORYMAPPED); - r.set_dmaen(false); - r.set_en(true); + r.set_tcen(false); }); - - // reg.tcr().modify(|r| { - // r.set_dcyc(6); - // }); - + Ok(()) } + + /// Quit from memory mapped mode pub fn disable_memory_mapped_mode(&mut self) { let reg = T::REGS; while reg.sr().read().busy() { info!("wait ospi busy"); } + reg.cr().modify(|r| { r.set_fmode(crate::ospi::vals::FunctionalMode::INDIRECTWRITE); + r.set_abort(true); r.set_dmaen(false); + r.set_en(false); + }); + + // Clear transfer complete flag + reg.fcr().write(|w| w.set_ctcf(true)); + + // Re-enable ospi + reg.cr().modify(|r| { r.set_en(true); }); } From 71fe8a7b90abc7b625d0f5b822508b350f3444d2 Mon Sep 17 00:00:00 2001 From: Vincenzo Marturano Date: Fri, 25 Oct 2024 12:54:06 +0200 Subject: [PATCH 132/361] Fixed owned split and implemented split_by_ref. --- embassy-rp/src/pwm.rs | 80 ++++++++++++++++++++++++++++++++----------- 1 file changed, 60 insertions(+), 20 deletions(-) diff --git a/embassy-rp/src/pwm.rs b/embassy-rp/src/pwm.rs index 443308158..18d476ed4 100644 --- a/embassy-rp/src/pwm.rs +++ b/embassy-rp/src/pwm.rs @@ -347,40 +347,80 @@ impl<'d> Pwm<'d> { #[inline] /// Split Pwm driver to allow separate duty cycle control of each channel - pub fn split(self) -> (Option>, Option>) { - - let pwm_output_a = if let Some(pin_a) = self.pin_a { - Some(PwmOutput::new(PwmChannelPin::A(pin_a), self.slice.clone())) - }else{ - None - }; - - let pwm_output_b = if let Some(pin_b) = self.pin_b { - Some(PwmOutput::new(PwmChannelPin::B(pin_b), self.slice.clone())) - }else { - None - }; - - (pwm_output_a,pwm_output_b) + pub fn split(mut self) -> (Option>, Option>) { + ( + self.pin_a + .take() + .map(|pin| PwmOutput::new(PwmChannelPin::A(pin), self.slice.clone(), true)), + self.pin_b + .take() + .map(|pin| PwmOutput::new(PwmChannelPin::B(pin), self.slice.clone(), true)), + ) } + pub fn split_by_ref(&mut self) -> (Option>, Option>) { + ( + self.pin_a + .as_mut() + .map(|pin| PwmOutput::new(PwmChannelPin::A(pin.reborrow()), self.slice.clone(), false)), + self.pin_b + .as_mut() + .map(|pin| PwmOutput::new(PwmChannelPin::B(pin.reborrow()), self.slice.clone(), false)), + ) + } } enum PwmChannelPin<'d> { A(PeripheralRef<'d, AnyPin>), - B(PeripheralRef<'d, AnyPin>) + B(PeripheralRef<'d, AnyPin>), } /// Single channel of Pwm driver. pub struct PwmOutput<'d> { //pin that can be ether ChannelAPin or ChannelBPin - channel_pin: PwmChannelPin<'d> , + channel_pin: PwmChannelPin<'d>, slice: usize, + is_owned: bool, } -impl <'d> PwmOutput<'d> { - fn new(channel_pin: PwmChannelPin<'d>, slice: usize) -> Self { - Self { channel_pin ,slice } +impl<'d> PwmOutput<'d> { + fn new(channel_pin: PwmChannelPin<'d>, slice: usize, is_owned: bool) -> Self { + Self { + channel_pin, + slice, + is_owned, + } + } +} + +impl<'d> Drop for PwmOutput<'d> { + fn drop(&mut self) { + if self.is_owned { + let p = pac::PWM.ch(self.slice); + match &self.channel_pin { + PwmChannelPin::A(pin) => { + p.cc().modify(|w| { + w.set_a(0); + }); + + pin.gpio().ctrl().write(|w| w.set_funcsel(31)); + ///Enable pin PULL-DOWN + pin.pad_ctrl().modify(|w| { + w.set_pde(true); + }); + } + PwmChannelPin::B(pin) => { + p.cc().modify(|w| { + w.set_b(0); + }); + pin.gpio().ctrl().write(|w| w.set_funcsel(31)); + ///Enable pin PULL-DOWN + pin.pad_ctrl().modify(|w| { + w.set_pde(true); + }); + } + } + } } } From 9690bed5a6f9a37bc926c1e86585bc28eb667ebf Mon Sep 17 00:00:00 2001 From: Vincenzo Marturano Date: Fri, 25 Oct 2024 13:12:24 +0200 Subject: [PATCH 133/361] Fix documentation. --- embassy-rp/src/pwm.rs | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/embassy-rp/src/pwm.rs b/embassy-rp/src/pwm.rs index 18d476ed4..4fb8ade12 100644 --- a/embassy-rp/src/pwm.rs +++ b/embassy-rp/src/pwm.rs @@ -345,8 +345,8 @@ impl<'d> Pwm<'d> { 1 << self.slice as usize } + /// Splits the PWM driver into separate `PwmOutput` instances for channels A and B. #[inline] - /// Split Pwm driver to allow separate duty cycle control of each channel pub fn split(mut self) -> (Option>, Option>) { ( self.pin_a @@ -357,7 +357,9 @@ impl<'d> Pwm<'d> { .map(|pin| PwmOutput::new(PwmChannelPin::B(pin), self.slice.clone(), true)), ) } - + /// Splits the PWM driver by reference to allow for separate duty cycle control + /// of each channel (A and B) without taking ownership of the PWM instance. + #[inline] pub fn split_by_ref(&mut self) -> (Option>, Option>) { ( self.pin_a @@ -404,7 +406,7 @@ impl<'d> Drop for PwmOutput<'d> { }); pin.gpio().ctrl().write(|w| w.set_funcsel(31)); - ///Enable pin PULL-DOWN + //Enable pin PULL-DOWN pin.pad_ctrl().modify(|w| { w.set_pde(true); }); @@ -414,7 +416,7 @@ impl<'d> Drop for PwmOutput<'d> { w.set_b(0); }); pin.gpio().ctrl().write(|w| w.set_funcsel(31)); - ///Enable pin PULL-DOWN + //Enable pin PULL-DOWN pin.pad_ctrl().modify(|w| { w.set_pde(true); }); From 45e7a7a55aa6b5b8d41d81949b75b1b4a154f9bd Mon Sep 17 00:00:00 2001 From: William <174336620+williams-one@users.noreply.github.com> Date: Fri, 25 Oct 2024 15:03:26 +0200 Subject: [PATCH 134/361] Update CFBLR configuration As per section "43.7.23 LTDC layer x color frame buffer length register (LTDC_LxCFBLR)" of Reference manual for STM32U5 RM0456, CFBLL has to be set to the length of one pixel line plus 3 (instead of plus 7 as for H7) --- embassy-stm32/src/ltdc.rs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/embassy-stm32/src/ltdc.rs b/embassy-stm32/src/ltdc.rs index 4c5239971..e25c4f3fb 100644 --- a/embassy-stm32/src/ltdc.rs +++ b/embassy-stm32/src/ltdc.rs @@ -395,7 +395,10 @@ impl<'d, T: Instance> Ltdc<'d, T> { // framebuffer pitch and line length layer.cfblr().modify(|w| { w.set_cfbp(width * bytes_per_pixel); + #[cfg(not(stm32u5))] w.set_cfbll(width * bytes_per_pixel + 7); + #[cfg(stm32u5)] + w.set_cfbll(width * bytes_per_pixel + 3); }); // framebuffer line number From be5222421177b632cd120272e08156b64cc0b886 Mon Sep 17 00:00:00 2001 From: William <174336620+williams-one@users.noreply.github.com> Date: Fri, 25 Oct 2024 15:40:18 +0200 Subject: [PATCH 135/361] Add LTDC example for STM32U5G9J-DK2 demo board --- examples/stm32u5/Cargo.toml | 2 + examples/stm32u5/src/bin/ferris.bmp | Bin 0 -> 6794 bytes examples/stm32u5/src/bin/ltdc.rs | 462 ++++++++++++++++++++++++++++ 3 files changed, 464 insertions(+) create mode 100644 examples/stm32u5/src/bin/ferris.bmp create mode 100644 examples/stm32u5/src/bin/ltdc.rs diff --git a/examples/stm32u5/Cargo.toml b/examples/stm32u5/Cargo.toml index ad7db4c32..f594ad71a 100644 --- a/examples/stm32u5/Cargo.toml +++ b/examples/stm32u5/Cargo.toml @@ -21,6 +21,8 @@ cortex-m-rt = "0.7.0" embedded-hal = "0.2.6" panic-probe = { version = "0.3", features = ["print-defmt"] } heapless = { version = "0.8", default-features = false } +embedded-graphics = { version = "0.8.1" } +tinybmp = { version = "0.6.0" } micromath = "2.0.0" diff --git a/examples/stm32u5/src/bin/ferris.bmp b/examples/stm32u5/src/bin/ferris.bmp new file mode 100644 index 0000000000000000000000000000000000000000..7a222ab84ddd9f2707ca8dcebad84b1b39c21818 GIT binary patch literal 6794 zcmb`L33OD|8OQ(JNnU0$OJ>O|nIvR^V8})iwgjaFsH`H2vdNBYA}T_u7=r{^1}IQL zRKN`(6x0McMUzp1q-luKI(W`lj>IPOV3HPUtHrL?CG_66&B`R_wD+9%?zjHl_wKvz zzB@B!%1$SQwlfZ>qS6d{RM4Xj)bvc74X|j%gBqCw_y)L=0Aw&fZDYnyTPsSvKa;4T z@qpl<@ILJNN$8_Wgep#pIF%W3oDN*P1&Fr-oDF^AozTRmfFz_+Uk2jTX^2mtRVDNX z)LBT-WCDrVAetPg)VWY44nVwSAh^UlsI_@iFNQX85R4WR`sfM}r!9d_TMSL&FmSpO zBUmpCLzH%8FA+E(3tL``ZUB_CLqx~1qsO$pt4Mb)^ayA z<{99u_d;))OKY5tM9WMh*rq|3JPT^ed^!xXkwkk<@&Y;>55Qtx3blO>wAMv1Sr;PS z{vh=B#V}e|z+!m}oMQ>Jwv|w)EQQIw5<17j&^VXD>{tb>Z4LA(tDsF=frQk@piNna z{K5jL(^f<0e1hKN25_nCk(mBC^t897JdK33C!y`T23lt|3{=i5Do3(o8`SBQu%~W> zE^QMWY1JqmT!O@mDkS&a413xR7&5lNp1u>#)VEqjul+4{QWbMK5@-o;no`<&o4kTqi3rFS)u=RZvx~v08>bDmuSuaALeGta} z`{2wzfW(~VV9z`ZL%%~XQ8}6Rj;z;V&OU;qoR^S7`#VOBq`Vp!2fPAH|D#Buhdui( z=yHz1nNtJ(fM39r`x>mYH{5m{=A4sAp+{2g30MZa4JSQ}d2b>)_Y@oh-h(OsEg14n z!<_p*Y_w0IeNz5AunhbFPI@HgorSTm9;TuOSPRa`>c{k1GvFBdIgG>Fkvyam)}dXPJ@)~m4*3$c z;eUi>=vT0pU4x_iI+BOqfN{hZu$Fy|`HL1{!QzE@qH;afZD_!bT|2R&@^kEdb`O?3 zc>|mGe}&_3oSPBYs&^!;CwOp0 z|76MaR3My?J5mE$D0dt`F5tRH%H`#76;X+e#ylrED+2MVm%VmH3*j!~`H_l;NQ204 z)a8tvGH>n1?a!0Dn%xa`HT$+LpEg4Bx&tGjI?T5yS}=DJeaPtwcc5HupeHK>vLtzf z29DmtX*oTJ`v48~s$~aR9>L&AMWn4zNg(T}cao$AxT9H4b0BIX(?c5}p*quo9>@%) zOV3#0<_IsqB88jY$f80UA=^R?R2!&dkW%Js5QkXi8pQ-Q3{HB|9TxSF^P7ZNJ3=f> znZZEWMf4;xMhF*@x#BC(L~Wy7M)IS!h;*J8rg3?sqftXhU)TLKRq%O*kr2761~Ggc zAwyi1>cNr8Jl~=f3b|8y)q+BrHH34y#uFl}8~kAwCvxRyKa31@xqezG=6Kg#2qbs? zOrX+}smUKOi&8Tot6Z*`WTT+^*xMm+?~nq$oYUoUt@x0sZSx7?_O*2h&3obC{c~q6 zoV8%?y?0L-{*uSbtp3(5D)q}k>UuJsCf+_m~>XFZ`yzK6NPB5AI)^lknUuTSZ||* z^$++7^Rfx&dw~koRU7uda^jS`#dFzzO)Q+9uXSDaw7O3o+q1Q*n%-SK%ig78nOmi? zzre9^*e+rD76Dt=#q<7FS9SFfx5q0?YS|yw`+V(Zk5pHSFwcLBlS#i(g+ITp7(~I}<#~6%e7!cn9p(>H zmoO@AoOCPxc4;NdjeMc~a=Cm9usRGBqISI_Qt5fLnB=#~miY=LBt=l_0DI(h196@5 zHFVQCY%z#+_nv)Yi%Usk{4!9Sp;@un7NJ^84=Y?**{@ z@FOzmCqq~#$;!Yi3JvO<_>b=H?!PjwR-BNplYddm6>2br&onXB=QdKO$R%~5I7Rb+ z{2xmBJLBexciln0qn1BY!=usXLmdm2xWg1sxZwYmQodx7XGHGRhrXefFX*e$7#=I> zcLuof!X72^2BkbfBey8k{e@Zv5wch@gk^cyM_m|)+AvSBdg-fwe;RKpJRG20`ud-b ztI`idA6=IEhQw8etSZ4!LH2n&yuOervDSbNZ)A@9Lk;4bM~zx#s(K0%6;nASv`A%{lCl(;i}$X*|Ar* zsC?lQ<&~MN58;w!{Dsv{Zg$k%7{)uflqJo4&<-G+ON6y3Ck7&_E6N`yq4kP)67^?R zRG?%GY>Jejh{mmm#fav+O~ENbH10T-+Y#%dr@&tmUZipbrwGxw^sC=Aog=-Nbf<}G zD&2);DL6%l#wBA1Gr#;Fj#6Btu$jdTn=CwSCI(tmuIL^t?7teL?!vs{mTqzY6eh*s zrn0enm^dTLRzw_ literal 0 HcmV?d00001 diff --git a/examples/stm32u5/src/bin/ltdc.rs b/examples/stm32u5/src/bin/ltdc.rs new file mode 100644 index 000000000..5bf2cd67d --- /dev/null +++ b/examples/stm32u5/src/bin/ltdc.rs @@ -0,0 +1,462 @@ +#![no_std] +#![no_main] +#![macro_use] +#![allow(static_mut_refs)] + +/// This example was derived from examples\stm32h735\src\bin\ltdc.rs +/// It demonstrates the LTDC lcd display peripheral and was tested on an STM32U5G9J-DK2 demo board (embassy-stm32 feature "stm32u5g9zj" and probe-rs chip "STM32U5G9ZJTxQ") +/// +use bouncy_box::BouncyBox; +use defmt::{info, unwrap}; +use embassy_executor::Spawner; +use embassy_stm32::gpio::{Level, Output, Speed}; +use embassy_stm32::ltdc::{self, Ltdc, LtdcConfiguration, LtdcLayer, LtdcLayerConfig, PolarityActive, PolarityEdge}; +use embassy_stm32::{bind_interrupts, peripherals}; +use embassy_time::{Duration, Timer}; +use embedded_graphics::draw_target::DrawTarget; +use embedded_graphics::geometry::{OriginDimensions, Point, Size}; +use embedded_graphics::image::Image; +use embedded_graphics::pixelcolor::raw::RawU24; +use embedded_graphics::pixelcolor::Rgb888; +use embedded_graphics::prelude::*; +use embedded_graphics::primitives::Rectangle; +use embedded_graphics::Pixel; +use heapless::{Entry, FnvIndexMap}; +use tinybmp::Bmp; +use {defmt_rtt as _, panic_probe as _}; + +const DISPLAY_WIDTH: usize = 800; +const DISPLAY_HEIGHT: usize = 480; +const MY_TASK_POOL_SIZE: usize = 2; + +// the following two display buffers consume 261120 bytes that just about fits into axis ram found on the mcu +pub static mut FB1: [TargetPixelType; DISPLAY_WIDTH * DISPLAY_HEIGHT] = [0; DISPLAY_WIDTH * DISPLAY_HEIGHT]; +pub static mut FB2: [TargetPixelType; DISPLAY_WIDTH * DISPLAY_HEIGHT] = [0; DISPLAY_WIDTH * DISPLAY_HEIGHT]; + +bind_interrupts!(struct Irqs { + LTDC => ltdc::InterruptHandler; +}); + +const NUM_COLORS: usize = 256; + +#[embassy_executor::main] +async fn main(spawner: Spawner) { + let p = rcc_setup::stm32u5g9zj_init(); + + // enable ICACHE + embassy_stm32::pac::ICACHE.cr().write(|w| { + w.set_en(true); + }); + + // blink the led on another task + let led = Output::new(p.PD2, Level::High, Speed::Low); + unwrap!(spawner.spawn(led_task(led))); + + // numbers from STM32U5G9J-DK2.ioc + const RK050HR18H_HSYNC: u16 = 5; // Horizontal synchronization + const RK050HR18H_HBP: u16 = 8; // Horizontal back porch + const RK050HR18H_HFP: u16 = 8; // Horizontal front porch + const RK050HR18H_VSYNC: u16 = 5; // Vertical synchronization + const RK050HR18H_VBP: u16 = 8; // Vertical back porch + const RK050HR18H_VFP: u16 = 8; // Vertical front porch + + // NOTE: all polarities have to be reversed with respect to the STM32U5G9J-DK2 CubeMX parametrization + let ltdc_config = LtdcConfiguration { + active_width: DISPLAY_WIDTH as _, + active_height: DISPLAY_HEIGHT as _, + h_back_porch: RK050HR18H_HBP, + h_front_porch: RK050HR18H_HFP, + v_back_porch: RK050HR18H_VBP, + v_front_porch: RK050HR18H_VFP, + h_sync: RK050HR18H_HSYNC, + v_sync: RK050HR18H_VSYNC, + h_sync_polarity: PolarityActive::ActiveHigh, + v_sync_polarity: PolarityActive::ActiveHigh, + data_enable_polarity: PolarityActive::ActiveHigh, + pixel_clock_polarity: PolarityEdge::RisingEdge, + }; + + info!("init ltdc"); + let mut ltdc_de = Output::new(p.PD6, Level::Low, Speed::High); + let mut ltdc_disp_ctrl = Output::new(p.PE4, Level::Low, Speed::High); + let mut ltdc_bl_ctrl = Output::new(p.PE6, Level::Low, Speed::High); + let mut ltdc = Ltdc::new_with_pins( + p.LTDC, // PERIPHERAL + Irqs, // IRQS + p.PD3, // CLK + p.PE0, // HSYNC + p.PD13, // VSYNC + p.PB9, // B0 + p.PB2, // B1 + p.PD14, // B2 + p.PD15, // B3 + p.PD0, // B4 + p.PD1, // B5 + p.PE7, // B6 + p.PE8, // B7 + p.PC8, // G0 + p.PC9, // G1 + p.PE9, // G2 + p.PE10, // G3 + p.PE11, // G4 + p.PE12, // G5 + p.PE13, // G6 + p.PE14, // G7 + p.PC6, // R0 + p.PC7, // R1 + p.PE15, // R2 + p.PD8, // R3 + p.PD9, // R4 + p.PD10, // R5 + p.PD11, // R6 + p.PD12, // R7 + ); + ltdc.init(<dc_config); + ltdc_de.set_low(); + ltdc_bl_ctrl.set_high(); + ltdc_disp_ctrl.set_high(); + + // we only need to draw on one layer for this example (not to be confused with the double buffer) + info!("enable bottom layer"); + let layer_config = LtdcLayerConfig { + pixel_format: ltdc::PixelFormat::L8, // 1 byte per pixel + layer: LtdcLayer::Layer1, + window_x0: 0, + window_x1: DISPLAY_WIDTH as _, + window_y0: 0, + window_y1: DISPLAY_HEIGHT as _, + }; + + let ferris_bmp: Bmp = Bmp::from_slice(include_bytes!("./ferris.bmp")).unwrap(); + let color_map = build_color_lookup_map(&ferris_bmp); + let clut = build_clut(&color_map); + + // enable the bottom layer with a 256 color lookup table + ltdc.init_layer(&layer_config, Some(&clut)); + + // Safety: the DoubleBuffer controls access to the statically allocated frame buffers + // and it is the only thing that mutates their content + let mut double_buffer = DoubleBuffer::new( + unsafe { FB1.as_mut() }, + unsafe { FB2.as_mut() }, + layer_config, + color_map, + ); + + // this allows us to perform some simple animation for every frame + let mut bouncy_box = BouncyBox::new( + ferris_bmp.bounding_box(), + Rectangle::new(Point::zero(), Size::new(DISPLAY_WIDTH as u32, DISPLAY_HEIGHT as u32)), + 2, + ); + + loop { + // cpu intensive drawing to the buffer that is NOT currently being copied to the LCD screen + double_buffer.clear(); + let position = bouncy_box.next_point(); + let ferris = Image::new(&ferris_bmp, position); + unwrap!(ferris.draw(&mut double_buffer)); + + // perform async dma data transfer to the lcd screen + unwrap!(double_buffer.swap(&mut ltdc).await); + } +} + +/// builds the color look-up table from all unique colors found in the bitmap. This should be a 256 color indexed bitmap to work. +fn build_color_lookup_map(bmp: &Bmp) -> FnvIndexMap { + let mut color_map: FnvIndexMap = heapless::FnvIndexMap::new(); + let mut counter: u8 = 0; + + // add black to position 0 + color_map.insert(Rgb888::new(0, 0, 0).into_storage(), counter).unwrap(); + counter += 1; + + for Pixel(_point, color) in bmp.pixels() { + let raw = color.into_storage(); + if let Entry::Vacant(v) = color_map.entry(raw) { + v.insert(counter).expect("more than 256 colors detected"); + counter += 1; + } + } + color_map +} + +/// builds the color look-up table from the color map provided +fn build_clut(color_map: &FnvIndexMap) -> [ltdc::RgbColor; NUM_COLORS] { + let mut clut = [ltdc::RgbColor::default(); NUM_COLORS]; + for (color, index) in color_map.iter() { + let color = Rgb888::from(RawU24::new(*color)); + clut[*index as usize] = ltdc::RgbColor { + red: color.r(), + green: color.g(), + blue: color.b(), + }; + } + + clut +} + +#[embassy_executor::task(pool_size = MY_TASK_POOL_SIZE)] +async fn led_task(mut led: Output<'static>) { + let mut counter = 0; + loop { + info!("blink: {}", counter); + counter += 1; + + // on + led.set_low(); + Timer::after(Duration::from_millis(50)).await; + + // off + led.set_high(); + Timer::after(Duration::from_millis(450)).await; + } +} + +pub type TargetPixelType = u8; + +// A simple double buffer +pub struct DoubleBuffer { + buf0: &'static mut [TargetPixelType], + buf1: &'static mut [TargetPixelType], + is_buf0: bool, + layer_config: LtdcLayerConfig, + color_map: FnvIndexMap, +} + +impl DoubleBuffer { + pub fn new( + buf0: &'static mut [TargetPixelType], + buf1: &'static mut [TargetPixelType], + layer_config: LtdcLayerConfig, + color_map: FnvIndexMap, + ) -> Self { + Self { + buf0, + buf1, + is_buf0: true, + layer_config, + color_map, + } + } + + pub fn current(&mut self) -> (&FnvIndexMap, &mut [TargetPixelType]) { + if self.is_buf0 { + (&self.color_map, self.buf0) + } else { + (&self.color_map, self.buf1) + } + } + + pub async fn swap(&mut self, ltdc: &mut Ltdc<'_, T>) -> Result<(), ltdc::Error> { + let (_, buf) = self.current(); + let frame_buffer = buf.as_ptr(); + self.is_buf0 = !self.is_buf0; + ltdc.set_buffer(self.layer_config.layer, frame_buffer as *const _).await + } + + /// Clears the buffer + pub fn clear(&mut self) { + let (color_map, buf) = self.current(); + let black = Rgb888::new(0, 0, 0).into_storage(); + let color_index = color_map.get(&black).expect("no black found in the color map"); + + for a in buf.iter_mut() { + *a = *color_index; // solid black + } + } +} + +// Implement DrawTarget for +impl DrawTarget for DoubleBuffer { + type Color = Rgb888; + type Error = (); + + /// Draw a pixel + fn draw_iter(&mut self, pixels: I) -> Result<(), Self::Error> + where + I: IntoIterator>, + { + let size = self.size(); + let width = size.width as i32; + let height = size.height as i32; + let (color_map, buf) = self.current(); + + for pixel in pixels { + let Pixel(point, color) = pixel; + + if point.x >= 0 && point.y >= 0 && point.x < width && point.y < height { + let index = point.y * width + point.x; + let raw_color = color.into_storage(); + + match color_map.get(&raw_color) { + Some(x) => { + buf[index as usize] = *x; + } + None => panic!("color not found in color map: {}", raw_color), + }; + } else { + // Ignore invalid points + } + } + + Ok(()) + } +} + +impl OriginDimensions for DoubleBuffer { + /// Return the size of the display + fn size(&self) -> Size { + Size::new( + (self.layer_config.window_x1 - self.layer_config.window_x0) as _, + (self.layer_config.window_y1 - self.layer_config.window_y0) as _, + ) + } +} + +mod rcc_setup { + + use embassy_stm32::rcc; + use embassy_stm32::time::Hertz; + use embassy_stm32::{Config, Peripherals}; + + /// Sets up clocks for the stm32u5g9zj mcu + /// change this if you plan to use a different microcontroller + pub fn stm32u5g9zj_init() -> Peripherals { + // setup power and clocks for an STM32U5G9J-DK2 run from an external 16 Mhz external oscillator + let mut config = Config::default(); + config.rcc.hse = Some(rcc::Hse { + freq: Hertz(16_000_000), + mode: rcc::HseMode::Oscillator, + }); + config.rcc.pll1 = Some(rcc::Pll { + source: rcc::PllSource::HSE, + prediv: rcc::PllPreDiv::DIV1, + mul: rcc::PllMul::MUL10, + divp: None, + divq: None, + divr: Some(rcc::PllDiv::DIV1), + }); + config.rcc.sys = rcc::Sysclk::PLL1_R; // 160 Mhz + config.rcc.pll3 = Some(rcc::Pll { + source: rcc::PllSource::HSE, + prediv: rcc::PllPreDiv::DIV4, // PLL_M + mul: rcc::PllMul::MUL125, // PLL_N + divp: None, + divq: None, + divr: Some(rcc::PllDiv::DIV20), + }); + config.rcc.mux.ltdcsel = rcc::mux::Ltdcsel::PLL3_R; // 25 MHz + embassy_stm32::init(config) + } +} + +mod bouncy_box { + use embedded_graphics::geometry::Point; + use embedded_graphics::primitives::Rectangle; + + enum Direction { + DownLeft, + DownRight, + UpLeft, + UpRight, + } + + pub struct BouncyBox { + direction: Direction, + child_rect: Rectangle, + parent_rect: Rectangle, + current_point: Point, + move_by: usize, + } + + // This calculates the coordinates of a chile rectangle bounced around inside a parent bounded box + impl BouncyBox { + pub fn new(child_rect: Rectangle, parent_rect: Rectangle, move_by: usize) -> Self { + let center_box = parent_rect.center(); + let center_img = child_rect.center(); + let current_point = Point::new(center_box.x - center_img.x / 2, center_box.y - center_img.y / 2); + Self { + direction: Direction::DownRight, + child_rect, + parent_rect, + current_point, + move_by, + } + } + + pub fn next_point(&mut self) -> Point { + let direction = &self.direction; + let img_height = self.child_rect.size.height as i32; + let box_height = self.parent_rect.size.height as i32; + let img_width = self.child_rect.size.width as i32; + let box_width = self.parent_rect.size.width as i32; + let move_by = self.move_by as i32; + + match direction { + Direction::DownLeft => { + self.current_point.x -= move_by; + self.current_point.y += move_by; + + let x_out_of_bounds = self.current_point.x < 0; + let y_out_of_bounds = (self.current_point.y + img_height) > box_height; + + if x_out_of_bounds && y_out_of_bounds { + self.direction = Direction::UpRight + } else if x_out_of_bounds && !y_out_of_bounds { + self.direction = Direction::DownRight + } else if !x_out_of_bounds && y_out_of_bounds { + self.direction = Direction::UpLeft + } + } + Direction::DownRight => { + self.current_point.x += move_by; + self.current_point.y += move_by; + + let x_out_of_bounds = (self.current_point.x + img_width) > box_width; + let y_out_of_bounds = (self.current_point.y + img_height) > box_height; + + if x_out_of_bounds && y_out_of_bounds { + self.direction = Direction::UpLeft + } else if x_out_of_bounds && !y_out_of_bounds { + self.direction = Direction::DownLeft + } else if !x_out_of_bounds && y_out_of_bounds { + self.direction = Direction::UpRight + } + } + Direction::UpLeft => { + self.current_point.x -= move_by; + self.current_point.y -= move_by; + + let x_out_of_bounds = self.current_point.x < 0; + let y_out_of_bounds = self.current_point.y < 0; + + if x_out_of_bounds && y_out_of_bounds { + self.direction = Direction::DownRight + } else if x_out_of_bounds && !y_out_of_bounds { + self.direction = Direction::UpRight + } else if !x_out_of_bounds && y_out_of_bounds { + self.direction = Direction::DownLeft + } + } + Direction::UpRight => { + self.current_point.x += move_by; + self.current_point.y -= move_by; + + let x_out_of_bounds = (self.current_point.x + img_width) > box_width; + let y_out_of_bounds = self.current_point.y < 0; + + if x_out_of_bounds && y_out_of_bounds { + self.direction = Direction::DownLeft + } else if x_out_of_bounds && !y_out_of_bounds { + self.direction = Direction::UpLeft + } else if !x_out_of_bounds && y_out_of_bounds { + self.direction = Direction::DownRight + } + } + } + + self.current_point + } + } +} From d1db7d90434e7cf8d82361818427942c19923726 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?D=C3=A1niel=20Buga?= Date: Fri, 25 Oct 2024 18:53:59 +0200 Subject: [PATCH 136/361] Explain how to keep the executor awake --- docs/pages/faq.adoc | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/docs/pages/faq.adoc b/docs/pages/faq.adoc index 8eb947b5e..d4f49bfc1 100644 --- a/docs/pages/faq.adoc +++ b/docs/pages/faq.adoc @@ -372,3 +372,16 @@ Issues like these while implementing drivers often fall into one of the followin 3. Some kind of hardware errata, or some hardware misconfiguration like wrong clock speeds 4. Some issue with an interrupt handler, either enabling, disabling, or re-enabling of interrupts when necessary 5. Some kind of async issue, like not registering wakers fully before checking flags, or not registering or pending wakers at the right time + +== How can I prevent the thread-mode executor from going to sleep? == + +In some cases you might want to prevent the thread-mode executor from going to sleep, for example when doing so would result in current spikes that reduce analog performance. +As a workaround, you can spawn a task that yields in a loop, preventing the executor from going to sleep. Note that this may increase power consumption. + +[source,rust] +---- +#[embassy_executor::task] +async fn idle() { + loop { embassy_futures::yield_now().await; } +} +---- From 6545dfee6d370cd37701fbbbf85d46c1a8e34521 Mon Sep 17 00:00:00 2001 From: ckrenslehner Date: Fri, 25 Oct 2024 19:22:20 +0200 Subject: [PATCH 137/361] Update overview.adoc --- docs/pages/overview.adoc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/pages/overview.adoc b/docs/pages/overview.adoc index 2ebc85f6d..8f97d937f 100644 --- a/docs/pages/overview.adoc +++ b/docs/pages/overview.adoc @@ -52,7 +52,7 @@ link:https://github.com/embassy-rs/embassy/tree/main/embassy-boot[embassy-boot] == What is DMA? -For most I/O in embedded devices, the peripheral doesn't directly support the transmission of multiple bits at once, with CAN being a notable exception. Instead, the MCU must write each byte, one at a time, and then wait until the peripheral is ready to send the next. For high I/O rates, this can pose a problem if the MCU must devote an increasing portion of its time handling each byte. The solution to this problem is to use the Direct Memory Access controller. +For most I/O in embedded devices, the peripheral doesn't directly support the transmission of multiple bytes at once, with CAN being a notable exception. Instead, the MCU must write each byte, one at a time, and then wait until the peripheral is ready to send the next. For high I/O rates, this can pose a problem if the MCU must devote an increasing portion of its time handling each byte. The solution to this problem is to use the Direct Memory Access controller. The Direct Memory Access controller (DMA) is a controller that is present in MCUs that Embassy supports, including stm32 and nrf. The DMA allows the MCU to set up a transfer, either send or receive, and then wait for the transfer to complete. With DMA, once started, no MCU intervention is required until the transfer is complete, meaning that the MCU can perform other computation, or set up other I/O while the transfer is in progress. For high I/O rates, DMA can cut the time that the MCU spends handling I/O by over half. However, because DMA is more complex to set-up, it is less widely used in the embedded community. Embassy aims to change that by making DMA the first choice rather than the last. Using Embassy, there's no additional tuning required once I/O rates increase because your application is already set-up to handle them. @@ -80,4 +80,4 @@ For more reading material on async Rust and Embassy: Videos: -* link:https://www.youtube.com/watch?v=wni5h5vIPhU[From Zero to Async in Embedded Rust] \ No newline at end of file +* link:https://www.youtube.com/watch?v=wni5h5vIPhU[From Zero to Async in Embedded Rust] From 2c05ac5262eb2beaa043818f02d82b656f272b4f Mon Sep 17 00:00:00 2001 From: ckrenslehner Date: Fri, 25 Oct 2024 20:36:46 +0200 Subject: [PATCH 138/361] Update embassy Cargo.toml section in new_project.adoc MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Embassy is already published to crates.io 🍾 --- docs/pages/new_project.adoc | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/docs/pages/new_project.adoc b/docs/pages/new_project.adoc index 821bcbd27..fa4c2e359 100644 --- a/docs/pages/new_project.adoc +++ b/docs/pages/new_project.adoc @@ -73,16 +73,28 @@ Now that cargo knows what target to compile for (and probe-rs knows what chip to Looking in `examples/stm32g4/Cargo.toml`, we can see that the examples require a number of embassy crates. For blinky, we’ll only need three of them: `embassy-stm32`, `embassy-executor` and `embassy-time`. -At the time of writing, the latest version of embassy isn‘t available on crates.io, so we need to install it straight from the git repository. The recommended way of doing so is as follows: + +At the time of writing, embassy is already published to crates.io. Therefore, dependencies can easily added via Cargo.toml. + +[source,toml] +---- +[dependencies] +embassy-stm32 = { version = "0.1.0", features = ["defmt", "time-driver-any", "stm32g474re", "memory-x", "unstable-pac", "exti"] } +embassy-executor = { version = "0.6.1", features = ["nightly", "arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] } +embassy-time = { version = "0.3.2", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } +---- + +Prior embassy needed to be installed straight from the git repository. This can is still useful, if you want to checkout a specic revision of an embassy crate which is not yet published. +The recommended way of doing so is as follows: * Copy the required `embassy-*` lines from the example `Cargo.toml` * Make any necessary changes to `features`, e.g. requiring the `stm32g474re` feature of `embassy-stm32` * Remove the `path = ""` keys in the `embassy-*` entries * Create a `[patch.crates-io]` section, with entries for each embassy crate we need. These should all contain identical values: a link to the git repository, and a reference to the commit we’re checking out. Assuming you want the latest commit, you can find it by running `git ls-remote https://github.com/embassy-rs/embassy.git HEAD` -NOTE: When using this method, it’s necessary that the `version` keys in `[dependencies]` match up with the versions defined in each crate’s `Cargo.toml` in the specificed `rev` under `[patch.crates.io]`. This means that when updating, you have to a pick a new revision, change everything in `[patch.crates.io]` to match it, and then correct any versions under `[dependencies]` which have changed. Hopefully this will no longer be necessary once embassy is released on crates.io! +NOTE: When using this method, it’s necessary that the `version` keys in `[dependencies]` match up with the versions defined in each crate’s `Cargo.toml` in the specificed `rev` under `[patch.crates.io]`. This means that when updating, you have to a pick a new revision, change everything in `[patch.crates.io]` to match it, and then correct any versions under `[dependencies]` which have changed. -At the time of writing, this method produces the following results: +An example Cargo.toml file might look as follows: [source,toml] ---- From 3d0921ffc4afb49b5d28277a02cee55fcec08881 Mon Sep 17 00:00:00 2001 From: ckrenslehner Date: Fri, 25 Oct 2024 20:41:51 +0200 Subject: [PATCH 139/361] fix spelling --- docs/pages/new_project.adoc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/pages/new_project.adoc b/docs/pages/new_project.adoc index fa4c2e359..38cea044b 100644 --- a/docs/pages/new_project.adoc +++ b/docs/pages/new_project.adoc @@ -84,7 +84,7 @@ embassy-executor = { version = "0.6.1", features = ["nightly", "arch-cortex-m", embassy-time = { version = "0.3.2", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } ---- -Prior embassy needed to be installed straight from the git repository. This can is still useful, if you want to checkout a specic revision of an embassy crate which is not yet published. +Prior, embassy needed to be installed straight from the git repository. Installing from git is still useful, if you want to checkout a specic revision of an embassy crate which is not yet published. The recommended way of doing so is as follows: * Copy the required `embassy-*` lines from the example `Cargo.toml` From edac7cc6304c724c9626f73a35a22b074588c012 Mon Sep 17 00:00:00 2001 From: dvdsk Date: Sat, 26 Oct 2024 12:38:10 +0200 Subject: [PATCH 140/361] stm32/uart remove DmaUnsynced from public api Its an internal error which should never be exposed. It should only occur with wrong driver implementations. We log to the user and return an Overrun error since handling DmaUnsynced as an Overrun will resolve it. --- embassy-stm32/src/dma/ringbuffer/mod.rs | 4 ++++ embassy-stm32/src/usart/mod.rs | 2 -- embassy-stm32/src/usart/ringbuffered.rs | 9 ++++++++- 3 files changed, 12 insertions(+), 3 deletions(-) diff --git a/embassy-stm32/src/dma/ringbuffer/mod.rs b/embassy-stm32/src/dma/ringbuffer/mod.rs index 12d418414..0da8c374f 100644 --- a/embassy-stm32/src/dma/ringbuffer/mod.rs +++ b/embassy-stm32/src/dma/ringbuffer/mod.rs @@ -21,6 +21,10 @@ pub trait DmaCtrl { #[cfg_attr(feature = "defmt", derive(defmt::Format))] pub enum Error { Overrun, + /// the newly read DMA positions don't make sense compared to the previous + /// ones. This can usually only occur due to wrong Driver implementation, if + /// the driver author (or the user using raw metapac code) directly resets + /// the channel for instance. DmaUnsynced, } diff --git a/embassy-stm32/src/usart/mod.rs b/embassy-stm32/src/usart/mod.rs index 86c94789a..aaf7b41e6 100644 --- a/embassy-stm32/src/usart/mod.rs +++ b/embassy-stm32/src/usart/mod.rs @@ -260,8 +260,6 @@ pub enum Error { Parity, /// Buffer too large for DMA BufferTooLong, - // TODO: ask what this is and document it (dvdsk) - DmaUnsynced, } enum ReadCompletionEvent { diff --git a/embassy-stm32/src/usart/ringbuffered.rs b/embassy-stm32/src/usart/ringbuffered.rs index f1914e1bf..a791ac2a9 100644 --- a/embassy-stm32/src/usart/ringbuffered.rs +++ b/embassy-stm32/src/usart/ringbuffered.rs @@ -268,7 +268,14 @@ impl ReadReady for RingBufferedUartRx<'_> { fn read_ready(&mut self) -> Result { let len = self.ring_buf.len().map_err(|e| match e { crate::dma::ringbuffer::Error::Overrun => Self::Error::Overrun, - crate::dma::ringbuffer::Error::DmaUnsynced => Self::Error::DmaUnsynced, + crate::dma::ringbuffer::Error::DmaUnsynced => { + error!( + "Ringbuffer error: DmaUNsynced, driver implementation is + probably bugged please open an issue" + ); + // we report this as overrun since its recoverable in the same way + Self::Error::Overrun + } })?; Ok(len > 0) } From ca6bcb4250fc294c2294265fbc83bf00398e089c Mon Sep 17 00:00:00 2001 From: Haobo Gu Date: Sat, 26 Oct 2024 23:41:30 +0800 Subject: [PATCH 141/361] feat(ospi): add ospi example Signed-off-by: Haobo Gu --- embassy-stm32/src/ospi/mod.rs | 9 +- .../stm32h7/src/bin/ospi_memory_mapped.rs | 435 ++++++++++++++++++ 2 files changed, 437 insertions(+), 7 deletions(-) create mode 100644 examples/stm32h7/src/bin/ospi_memory_mapped.rs diff --git a/embassy-stm32/src/ospi/mod.rs b/embassy-stm32/src/ospi/mod.rs index 7ccafe361..f8ef66216 100644 --- a/embassy-stm32/src/ospi/mod.rs +++ b/embassy-stm32/src/ospi/mod.rs @@ -180,7 +180,7 @@ pub struct Ospi<'d, T: Instance, M: PeriMode> { impl<'d, T: Instance, M: PeriMode> Ospi<'d, T, M> { /// Enter memory mode. - /// The Input `TransferConfig` is used to configure the read operation in memory mode + /// The Input `read_config` is used to configure the read operation in memory mode pub fn enable_memory_mapped_mode( &mut self, read_config: TransferConfig, @@ -190,9 +190,7 @@ impl<'d, T: Instance, M: PeriMode> Ospi<'d, T, M> { self.configure_command(&read_config, None)?; let reg = T::REGS; - while reg.sr().read().busy() { - info!("wait ospi busy"); - } + while reg.sr().read().busy() {} reg.ccr().modify(|r| { r.set_dqse(false); @@ -229,9 +227,6 @@ impl<'d, T: Instance, M: PeriMode> Ospi<'d, T, M> { /// Quit from memory mapped mode pub fn disable_memory_mapped_mode(&mut self) { let reg = T::REGS; - while reg.sr().read().busy() { - info!("wait ospi busy"); - } reg.cr().modify(|r| { r.set_fmode(crate::ospi::vals::FunctionalMode::INDIRECTWRITE); diff --git a/examples/stm32h7/src/bin/ospi_memory_mapped.rs b/examples/stm32h7/src/bin/ospi_memory_mapped.rs new file mode 100644 index 000000000..40f39f751 --- /dev/null +++ b/examples/stm32h7/src/bin/ospi_memory_mapped.rs @@ -0,0 +1,435 @@ +#![no_main] +#![no_std] + +// Tested on weact stm32h7b0 board + w25q64 spi flash + +use defmt::info; +use defmt_rtt as _; +use embassy_executor::Spawner; +use embassy_stm32::{ + gpio::{Level, Output, Speed}, + mode::Blocking, + ospi::{AddressSize, DummyCycles, Instance, Ospi, OspiWidth, TransferConfig}, + ospi::{ChipSelectHighTime, FIFOThresholdLevel, MemorySize, MemoryType, WrapSize}, + time::Hertz, + Config, +}; +use embassy_time::Timer; +use panic_probe as _; + +#[embassy_executor::main] +async fn main(_spawner: Spawner) { + // RCC config + let mut config = Config::default(); + info!("START"); + { + use embassy_stm32::rcc::*; + config.rcc.hsi = Some(HSIPrescaler::DIV1); + config.rcc.csi = true; + // Needed for USB + config.rcc.hsi48 = Some(Hsi48Config { sync_from_usb: true }); + // External oscillator 25MHZ + config.rcc.hse = Some(Hse { + freq: Hertz(25_000_000), + mode: HseMode::Oscillator, + }); + config.rcc.pll1 = Some(Pll { + source: PllSource::HSE, + prediv: PllPreDiv::DIV5, + mul: PllMul::MUL112, + divp: Some(PllDiv::DIV2), + divq: Some(PllDiv::DIV2), + divr: Some(PllDiv::DIV2), + }); + config.rcc.sys = Sysclk::PLL1_P; + config.rcc.ahb_pre = AHBPrescaler::DIV2; + config.rcc.apb1_pre = APBPrescaler::DIV2; + config.rcc.apb2_pre = APBPrescaler::DIV2; + config.rcc.apb3_pre = APBPrescaler::DIV2; + config.rcc.apb4_pre = APBPrescaler::DIV2; + config.rcc.voltage_scale = VoltageScale::Scale0; + } + + // Initialize peripherals + let p = embassy_stm32::init(config); + + let qspi_config = embassy_stm32::ospi::Config { + fifo_threshold: FIFOThresholdLevel::_16Bytes, + memory_type: MemoryType::Micron, + device_size: MemorySize::_8MiB, + chip_select_high_time: ChipSelectHighTime::_1Cycle, + free_running_clock: false, + clock_mode: false, + wrap_size: WrapSize::None, + clock_prescaler: 2, + sample_shifting: true, + delay_hold_quarter_cycle: false, + chip_select_boundary: 0, + delay_block_bypass: true, + max_transfer: 0, + refresh: 0, + }; + let ospi = embassy_stm32::ospi::Ospi::new_blocking_quadspi( + p.OCTOSPI1, + p.PB2, + p.PD11, + p.PD12, + p.PE2, + p.PD13, + p.PB6, + qspi_config, + ); + + let mut flash = FlashMemory::new(ospi).await; + + let flash_id = flash.read_id(); + info!("FLASH ID: {=[u8]:x}", flash_id); + let mut wr_buf = [0u8; 8]; + for i in 0..8 { + wr_buf[i] = i as u8; + } + let mut rd_buf = [0u8; 8]; + flash.erase_sector(0).await; + flash.write_memory(0, &wr_buf, true).await; + flash.read_memory(0, &mut rd_buf, true); + info!("WRITE BUF: {=[u8]:#X}", wr_buf); + info!("READ BUF: {=[u8]:#X}", rd_buf); + info!("Enabling memory mapped mode"); + flash.enable_mm().await; + + let first_u32 = unsafe { *(0x90000000 as *const u32) }; + assert_eq!(first_u32, 0x03020100); + + let second_u32 = unsafe { *(0x90000004 as *const u32) }; + assert_eq!(second_u32, 0x07060504); + flash.disable_mm().await; + + info!("DONE"); + // Output pin PE3 + let mut led = Output::new(p.PE3, Level::Low, Speed::Low); + + loop { + led.toggle(); + Timer::after_millis(1000).await; + } +} + +const MEMORY_PAGE_SIZE: usize = 8; + +const CMD_QUAD_READ: u8 = 0x6B; + +const CMD_QUAD_WRITE_PG: u8 = 0x32; + +const CMD_READ_ID: u8 = 0x9F; +const CMD_READ_UUID: u8 = 0x4B; + +const CMD_ENABLE_RESET: u8 = 0x66; +const CMD_RESET: u8 = 0x99; + +const CMD_WRITE_ENABLE: u8 = 0x06; + +const CMD_CHIP_ERASE: u8 = 0xC7; +const CMD_SECTOR_ERASE: u8 = 0x20; +const CMD_BLOCK_ERASE_32K: u8 = 0x52; +const CMD_BLOCK_ERASE_64K: u8 = 0xD8; + +const CMD_READ_SR: u8 = 0x05; +const CMD_READ_CR: u8 = 0x35; + +const CMD_WRITE_SR: u8 = 0x01; +const CMD_WRITE_CR: u8 = 0x31; + +/// Implementation of access to flash chip. +/// Chip commands are hardcoded as it depends on used chip. +/// This implementation is using chip GD25Q64C from Giga Device +pub struct FlashMemory { + ospi: Ospi<'static, I, Blocking>, +} + +impl FlashMemory { + pub async fn new(ospi: Ospi<'static, I, Blocking>) -> Self { + let mut memory = Self { ospi }; + + memory.reset_memory().await; + memory.enable_quad(); + memory + } + + async fn qpi_mode(&mut self) { + // Enter qpi mode + self.exec_command(0x38).await; + + // Set read param + let transaction = TransferConfig { + iwidth: OspiWidth::QUAD, + dwidth: OspiWidth::QUAD, + instruction: Some(0xC0), + ..Default::default() + }; + self.enable_write().await; + self.ospi.blocking_write(&[0x30_u8], transaction).unwrap(); + self.wait_write_finish(); + } + + pub async fn disable_mm(&mut self) { + self.ospi.disable_memory_mapped_mode(); + } + + pub async fn enable_mm(&mut self) { + self.qpi_mode().await; + + let read_config = TransferConfig { + iwidth: OspiWidth::QUAD, + isize: AddressSize::_8Bit, + adwidth: OspiWidth::QUAD, + adsize: AddressSize::_24bit, + dwidth: OspiWidth::QUAD, + instruction: Some(0x0B), // Fast read in QPI mode + dummy: DummyCycles::_8, + ..Default::default() + }; + + let write_config = TransferConfig { + iwidth: OspiWidth::SING, + isize: AddressSize::_8Bit, + adwidth: OspiWidth::SING, + adsize: AddressSize::_24bit, + dwidth: OspiWidth::QUAD, + instruction: Some(0x32), // Write config + dummy: DummyCycles::_0, + ..Default::default() + }; + self.ospi.enable_memory_mapped_mode(read_config, write_config).unwrap(); + } + + fn enable_quad(&mut self) { + let cr = self.read_cr(); + // info!("Read cr: {:x}", cr); + self.write_cr(cr | 0x02); + // info!("Read cr after writing: {:x}", cr); + } + + pub fn disable_quad(&mut self) { + let cr = self.read_cr(); + self.write_cr(cr & (!(0x02))); + } + + async fn exec_command_4(&mut self, cmd: u8) { + let transaction = TransferConfig { + iwidth: OspiWidth::QUAD, + adwidth: OspiWidth::NONE, + // adsize: AddressSize::_24bit, + dwidth: OspiWidth::NONE, + instruction: Some(cmd as u32), + address: None, + dummy: DummyCycles::_0, + ..Default::default() + }; + self.ospi.command(&transaction).await.unwrap(); + } + + async fn exec_command(&mut self, cmd: u8) { + let transaction = TransferConfig { + iwidth: OspiWidth::SING, + adwidth: OspiWidth::NONE, + // adsize: AddressSize::_24bit, + dwidth: OspiWidth::NONE, + instruction: Some(cmd as u32), + address: None, + dummy: DummyCycles::_0, + ..Default::default() + }; + // info!("Excuting command: {:x}", transaction.instruction); + self.ospi.command(&transaction).await.unwrap(); + } + + pub async fn reset_memory(&mut self) { + self.exec_command_4(CMD_ENABLE_RESET).await; + self.exec_command_4(CMD_RESET).await; + self.exec_command(CMD_ENABLE_RESET).await; + self.exec_command(CMD_RESET).await; + self.wait_write_finish(); + } + + pub async fn enable_write(&mut self) { + self.exec_command(CMD_WRITE_ENABLE).await; + } + + pub fn read_id(&mut self) -> [u8; 3] { + let mut buffer = [0; 3]; + let transaction: TransferConfig = TransferConfig { + iwidth: OspiWidth::SING, + isize: AddressSize::_8Bit, + adwidth: OspiWidth::NONE, + // adsize: AddressSize::_24bit, + dwidth: OspiWidth::SING, + instruction: Some(CMD_READ_ID as u32), + ..Default::default() + }; + // info!("Reading id: 0x{:X}", transaction.instruction); + self.ospi.blocking_read(&mut buffer, transaction).unwrap(); + buffer + } + + pub fn read_id_4(&mut self) -> [u8; 3] { + let mut buffer = [0; 3]; + let transaction: TransferConfig = TransferConfig { + iwidth: OspiWidth::SING, + isize: AddressSize::_8Bit, + adwidth: OspiWidth::NONE, + dwidth: OspiWidth::QUAD, + instruction: Some(CMD_READ_ID as u32), + ..Default::default() + }; + info!("Reading id: 0x{:X}", transaction.instruction); + self.ospi.blocking_read(&mut buffer, transaction).unwrap(); + buffer + } + + pub fn read_memory(&mut self, addr: u32, buffer: &mut [u8], use_dma: bool) { + let transaction = TransferConfig { + iwidth: OspiWidth::SING, + adwidth: OspiWidth::SING, + adsize: AddressSize::_24bit, + dwidth: OspiWidth::QUAD, + instruction: Some(CMD_QUAD_READ as u32), + address: Some(addr), + dummy: DummyCycles::_8, + ..Default::default() + }; + if use_dma { + self.ospi.blocking_read(buffer, transaction).unwrap(); + } else { + self.ospi.blocking_read(buffer, transaction).unwrap(); + } + } + + fn wait_write_finish(&mut self) { + while (self.read_sr() & 0x01) != 0 {} + } + + async fn perform_erase(&mut self, addr: u32, cmd: u8) { + let transaction = TransferConfig { + iwidth: OspiWidth::SING, + adwidth: OspiWidth::SING, + adsize: AddressSize::_24bit, + dwidth: OspiWidth::NONE, + instruction: Some(cmd as u32), + address: Some(addr), + dummy: DummyCycles::_0, + ..Default::default() + }; + self.enable_write().await; + self.ospi.command(&transaction).await.unwrap(); + self.wait_write_finish(); + } + + pub async fn erase_sector(&mut self, addr: u32) { + self.perform_erase(addr, CMD_SECTOR_ERASE).await; + } + + pub async fn erase_block_32k(&mut self, addr: u32) { + self.perform_erase(addr, CMD_BLOCK_ERASE_32K).await; + } + + pub async fn erase_block_64k(&mut self, addr: u32) { + self.perform_erase(addr, CMD_BLOCK_ERASE_64K).await; + } + + pub async fn erase_chip(&mut self) { + self.exec_command(CMD_CHIP_ERASE).await; + } + + async fn write_page(&mut self, addr: u32, buffer: &[u8], len: usize, use_dma: bool) { + assert!( + (len as u32 + (addr & 0x000000ff)) <= MEMORY_PAGE_SIZE as u32, + "write_page(): page write length exceeds page boundary (len = {}, addr = {:X}", + len, + addr + ); + + let transaction = TransferConfig { + iwidth: OspiWidth::SING, + adsize: AddressSize::_24bit, + adwidth: OspiWidth::SING, + dwidth: OspiWidth::QUAD, + instruction: Some(CMD_QUAD_WRITE_PG as u32), + address: Some(addr), + dummy: DummyCycles::_0, + ..Default::default() + }; + self.enable_write().await; + if use_dma { + self.ospi.blocking_write(buffer, transaction).unwrap(); + } else { + self.ospi.blocking_write(buffer, transaction).unwrap(); + } + self.wait_write_finish(); + } + + pub async fn write_memory(&mut self, addr: u32, buffer: &[u8], use_dma: bool) { + let mut left = buffer.len(); + let mut place = addr; + let mut chunk_start = 0; + + while left > 0 { + let max_chunk_size = MEMORY_PAGE_SIZE - (place & 0x000000ff) as usize; + let chunk_size = if left >= max_chunk_size { max_chunk_size } else { left }; + let chunk = &buffer[chunk_start..(chunk_start + chunk_size)]; + self.write_page(place, chunk, chunk_size, use_dma).await; + place += chunk_size as u32; + left -= chunk_size; + chunk_start += chunk_size; + } + } + + fn read_register(&mut self, cmd: u8) -> u8 { + let mut buffer = [0; 1]; + let transaction: TransferConfig = TransferConfig { + iwidth: OspiWidth::SING, + isize: AddressSize::_8Bit, + adwidth: OspiWidth::NONE, + adsize: AddressSize::_24bit, + dwidth: OspiWidth::SING, + instruction: Some(cmd as u32), + address: None, + dummy: DummyCycles::_0, + ..Default::default() + }; + self.ospi.blocking_read(&mut buffer, transaction).unwrap(); + // info!("Read w25q64 register: 0x{:x}", buffer[0]); + buffer[0] + } + + fn write_register(&mut self, cmd: u8, value: u8) { + let buffer = [value; 1]; + let transaction: TransferConfig = TransferConfig { + iwidth: OspiWidth::SING, + isize: AddressSize::_8Bit, + instruction: Some(cmd as u32), + adsize: AddressSize::_24bit, + adwidth: OspiWidth::NONE, + dwidth: OspiWidth::SING, + address: None, + dummy: DummyCycles::_0, + ..Default::default() + }; + self.ospi.blocking_write(&buffer, transaction).unwrap(); + } + + pub fn read_sr(&mut self) -> u8 { + self.read_register(CMD_READ_SR) + } + + pub fn read_cr(&mut self) -> u8 { + self.read_register(CMD_READ_CR) + } + + pub fn write_sr(&mut self, value: u8) { + self.write_register(CMD_WRITE_SR, value); + } + + pub fn write_cr(&mut self, value: u8) { + self.write_register(CMD_WRITE_CR, value); + } +} From 04c9130d326990dc92577f2ed4b2dc927efe2c13 Mon Sep 17 00:00:00 2001 From: Haobo Gu Date: Sat, 26 Oct 2024 23:50:16 +0800 Subject: [PATCH 142/361] feat(example): move ospi memory mapped example for stm32h7b0 to separate folder Signed-off-by: Haobo Gu --- examples/stm32h7b0/.cargo/config.toml | 8 ++ examples/stm32h7b0/Cargo.toml | 74 +++++++++++++++++++ examples/stm32h7b0/build.rs | 35 +++++++++ examples/stm32h7b0/memory.x | 5 ++ .../src/bin/ospi_memory_mapped.rs | 5 +- 5 files changed, 124 insertions(+), 3 deletions(-) create mode 100644 examples/stm32h7b0/.cargo/config.toml create mode 100644 examples/stm32h7b0/Cargo.toml create mode 100644 examples/stm32h7b0/build.rs create mode 100644 examples/stm32h7b0/memory.x rename examples/{stm32h7 => stm32h7b0}/src/bin/ospi_memory_mapped.rs (99%) diff --git a/examples/stm32h7b0/.cargo/config.toml b/examples/stm32h7b0/.cargo/config.toml new file mode 100644 index 000000000..870849a27 --- /dev/null +++ b/examples/stm32h7b0/.cargo/config.toml @@ -0,0 +1,8 @@ +[target.thumbv7em-none-eabihf] +runner = 'probe-rs run --chip STM32H7B0VBTx' + +[build] +target = "thumbv7em-none-eabihf" # Cortex-M4F and Cortex-M7F (with FPU) + +[env] +DEFMT_LOG = "trace" diff --git a/examples/stm32h7b0/Cargo.toml b/examples/stm32h7b0/Cargo.toml new file mode 100644 index 000000000..02c620443 --- /dev/null +++ b/examples/stm32h7b0/Cargo.toml @@ -0,0 +1,74 @@ +[package] +edition = "2021" +name = "embassy-stm32h7b0-examples" +version = "0.1.0" +license = "MIT OR Apache-2.0" + +[dependencies] +embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["defmt", "stm32h7b0vb", "time-driver-tim2", "exti", "memory-x", "unstable-pac", "chrono"] } +embassy-sync = { version = "0.6.0", path = "../../embassy-sync", features = ["defmt"] } +embassy-embedded-hal = { version = "0.2.0", path = "../../embassy-embedded-hal" } +embassy-executor = { version = "0.6.1", path = "../../embassy-executor", features = ["task-arena-size-32768", "arch-cortex-m", "executor-thread", "executor-interrupt", "defmt", "integrated-timers"] } +embassy-time = { version = "0.3.2", 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.3.0", path = "../../embassy-usb", features = ["defmt"] } +embassy-futures = { version = "0.1.0", path = "../../embassy-futures" } + +defmt = "0.3" +defmt-rtt = "0.4" + +cortex-m = { version = "0.7.6", features = ["inline-asm", "critical-section-single-core"] } +cortex-m-rt = "0.7.0" +embedded-hal = "0.2.6" +embedded-hal-1 = { package = "embedded-hal", version = "1.0" } +embedded-hal-async = { version = "1.0" } +embedded-nal-async = "0.8.0" +embedded-io-async = { version = "0.6.1" } +panic-probe = { version = "0.3", features = ["print-defmt"] } +heapless = { version = "0.8", default-features = false } +rand_core = "0.6.3" +critical-section = "1.1" +micromath = "2.0.0" +stm32-fmc = "0.3.0" +embedded-storage = "0.3.1" +static_cell = "2" +chrono = { version = "^0.4", default-features = false } +grounded = "0.2.0" + +# cargo build/run +[profile.dev] +codegen-units = 1 +debug = 2 +debug-assertions = true # <- +incremental = false +opt-level = 3 # <- +overflow-checks = true # <- + +# cargo test +[profile.test] +codegen-units = 1 +debug = 2 +debug-assertions = true # <- +incremental = false +opt-level = 3 # <- +overflow-checks = true # <- + +# cargo build/run --release +[profile.release] +codegen-units = 1 +debug = 2 +debug-assertions = false # <- +incremental = false +lto = 'fat' +opt-level = 3 # <- +overflow-checks = false # <- + +# cargo test --release +[profile.bench] +codegen-units = 1 +debug = 2 +debug-assertions = false # <- +incremental = false +lto = 'fat' +opt-level = 3 # <- +overflow-checks = false # <- diff --git a/examples/stm32h7b0/build.rs b/examples/stm32h7b0/build.rs new file mode 100644 index 000000000..30691aa97 --- /dev/null +++ b/examples/stm32h7b0/build.rs @@ -0,0 +1,35 @@ +//! This build script copies the `memory.x` file from the crate root into +//! a directory where the linker can always find it at build time. +//! For many projects this is optional, as the linker always searches the +//! project root directory -- wherever `Cargo.toml` is. However, if you +//! are using a workspace or have a more complicated build setup, this +//! build script becomes required. Additionally, by requesting that +//! Cargo re-run the build script whenever `memory.x` is changed, +//! updating `memory.x` ensures a rebuild of the application with the +//! new memory settings. + +use std::env; +use std::fs::File; +use std::io::Write; +use std::path::PathBuf; + +fn main() { + // Put `memory.x` in our output directory and ensure it's + // on the linker search path. + let out = &PathBuf::from(env::var_os("OUT_DIR").unwrap()); + File::create(out.join("memory.x")) + .unwrap() + .write_all(include_bytes!("memory.x")) + .unwrap(); + println!("cargo:rustc-link-search={}", out.display()); + + // By default, Cargo will re-run a build script whenever + // any file in the project changes. By specifying `memory.x` + // here, we ensure the build script is only re-run when + // `memory.x` is changed. + println!("cargo:rerun-if-changed=memory.x"); + + println!("cargo:rustc-link-arg-bins=--nmagic"); + println!("cargo:rustc-link-arg-bins=-Tlink.x"); + println!("cargo:rustc-link-arg-bins=-Tdefmt.x"); +} diff --git a/examples/stm32h7b0/memory.x b/examples/stm32h7b0/memory.x new file mode 100644 index 000000000..6eb1bb7c1 --- /dev/null +++ b/examples/stm32h7b0/memory.x @@ -0,0 +1,5 @@ +MEMORY +{ + FLASH : ORIGIN = 0x08000000, LENGTH = 128K /* BANK_1 */ + RAM : ORIGIN = 0x24000000, LENGTH = 512K /* SRAM */ +} \ No newline at end of file diff --git a/examples/stm32h7/src/bin/ospi_memory_mapped.rs b/examples/stm32h7b0/src/bin/ospi_memory_mapped.rs similarity index 99% rename from examples/stm32h7/src/bin/ospi_memory_mapped.rs rename to examples/stm32h7b0/src/bin/ospi_memory_mapped.rs index 40f39f751..e32a22e41 100644 --- a/examples/stm32h7/src/bin/ospi_memory_mapped.rs +++ b/examples/stm32h7b0/src/bin/ospi_memory_mapped.rs @@ -61,7 +61,7 @@ async fn main(_spawner: Spawner) { free_running_clock: false, clock_mode: false, wrap_size: WrapSize::None, - clock_prescaler: 2, + clock_prescaler: 4, sample_shifting: true, delay_hold_quarter_cycle: false, chip_select_boundary: 0, @@ -94,8 +94,8 @@ async fn main(_spawner: Spawner) { flash.read_memory(0, &mut rd_buf, true); info!("WRITE BUF: {=[u8]:#X}", wr_buf); info!("READ BUF: {=[u8]:#X}", rd_buf); - info!("Enabling memory mapped mode"); flash.enable_mm().await; + info!("Enabled memory mapped mode"); let first_u32 = unsafe { *(0x90000000 as *const u32) }; assert_eq!(first_u32, 0x03020100); @@ -121,7 +121,6 @@ const CMD_QUAD_READ: u8 = 0x6B; const CMD_QUAD_WRITE_PG: u8 = 0x32; const CMD_READ_ID: u8 = 0x9F; -const CMD_READ_UUID: u8 = 0x4B; const CMD_ENABLE_RESET: u8 = 0x66; const CMD_RESET: u8 = 0x99; From 3b5284d99d7054ac42e5d3e065fc1d27527f823a Mon Sep 17 00:00:00 2001 From: Haobo Gu Date: Sat, 26 Oct 2024 23:51:38 +0800 Subject: [PATCH 143/361] fix: fmt code Signed-off-by: Haobo Gu --- .../stm32h7b0/src/bin/ospi_memory_mapped.rs | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/examples/stm32h7b0/src/bin/ospi_memory_mapped.rs b/examples/stm32h7b0/src/bin/ospi_memory_mapped.rs index e32a22e41..9c397e507 100644 --- a/examples/stm32h7b0/src/bin/ospi_memory_mapped.rs +++ b/examples/stm32h7b0/src/bin/ospi_memory_mapped.rs @@ -4,18 +4,17 @@ // Tested on weact stm32h7b0 board + w25q64 spi flash use defmt::info; -use defmt_rtt as _; use embassy_executor::Spawner; -use embassy_stm32::{ - gpio::{Level, Output, Speed}, - mode::Blocking, - ospi::{AddressSize, DummyCycles, Instance, Ospi, OspiWidth, TransferConfig}, - ospi::{ChipSelectHighTime, FIFOThresholdLevel, MemorySize, MemoryType, WrapSize}, - time::Hertz, - Config, +use embassy_stm32::gpio::{Level, Output, Speed}; +use embassy_stm32::mode::Blocking; +use embassy_stm32::ospi::{ + AddressSize, ChipSelectHighTime, DummyCycles, FIFOThresholdLevel, Instance, MemorySize, MemoryType, Ospi, + OspiWidth, TransferConfig, WrapSize, }; +use embassy_stm32::time::Hertz; +use embassy_stm32::Config; use embassy_time::Timer; -use panic_probe as _; +use {defmt_rtt as _, panic_probe as _}; #[embassy_executor::main] async fn main(_spawner: Spawner) { From 66205fdd859b4c8607d0b6be9e4fcb27e8096493 Mon Sep 17 00:00:00 2001 From: Georges Palauqui Date: Sun, 27 Oct 2024 09:50:57 +0100 Subject: [PATCH 144/361] update spi_display example for rp --- examples/rp/Cargo.toml | 8 +- examples/rp/src/bin/spi_display.rs | 163 +++-------------------------- 2 files changed, 18 insertions(+), 153 deletions(-) diff --git a/examples/rp/Cargo.toml b/examples/rp/Cargo.toml index 6a2c99716..b55b20c63 100644 --- a/examples/rp/Cargo.toml +++ b/examples/rp/Cargo.toml @@ -37,10 +37,10 @@ cortex-m = { version = "0.7.6", features = ["inline-asm"] } cortex-m-rt = "0.7.0" critical-section = "1.1" panic-probe = { version = "0.3", features = ["print-defmt"] } -display-interface-spi = "0.4.1" -embedded-graphics = "0.7.1" -st7789 = "0.6.1" -display-interface = "0.4.1" +display-interface-spi = "0.5.0" +embedded-graphics = "0.8.1" +mipidsi = "0.8.0" +display-interface = "0.5.0" byte-slice-cast = { version = "1.2.0", default-features = false } smart-leds = "0.4.0" heapless = "0.8" diff --git a/examples/rp/src/bin/spi_display.rs b/examples/rp/src/bin/spi_display.rs index e937b9d0a..dd114a4ae 100644 --- a/examples/rp/src/bin/spi_display.rs +++ b/examples/rp/src/bin/spi_display.rs @@ -9,11 +9,12 @@ use core::cell::RefCell; use defmt::*; +use display_interface_spi::SPIInterface; use embassy_embedded_hal::shared_bus::blocking::spi::SpiDeviceWithConfig; use embassy_executor::Spawner; use embassy_rp::gpio::{Level, Output}; use embassy_rp::spi; -use embassy_rp::spi::{Blocking, Spi}; +use embassy_rp::spi::Spi; use embassy_sync::blocking_mutex::raw::NoopRawMutex; use embassy_sync::blocking_mutex::Mutex; use embassy_time::Delay; @@ -24,10 +25,11 @@ use embedded_graphics::pixelcolor::Rgb565; use embedded_graphics::prelude::*; use embedded_graphics::primitives::{PrimitiveStyleBuilder, Rectangle}; use embedded_graphics::text::Text; -use st7789::{Orientation, ST7789}; +use mipidsi::models::ST7789; +use mipidsi::options::{Orientation, Rotation}; +use mipidsi::Builder; use {defmt_rtt as _, panic_probe as _}; -use crate::my_display_interface::SPIDeviceInterface; use crate::touch::Touch; const DISPLAY_FREQ: u32 = 64_000_000; @@ -58,7 +60,7 @@ async fn main(_spawner: Spawner) { touch_config.phase = spi::Phase::CaptureOnSecondTransition; touch_config.polarity = spi::Polarity::IdleHigh; - let spi: Spi<'_, _, Blocking> = Spi::new_blocking(p.SPI1, clk, mosi, miso, touch_config.clone()); + let spi = Spi::new_blocking(p.SPI1, clk, mosi, miso, touch_config.clone()); let spi_bus: Mutex = Mutex::new(RefCell::new(spi)); let display_spi = SpiDeviceWithConfig::new(&spi_bus, Output::new(display_cs, Level::High), display_config); @@ -74,17 +76,15 @@ async fn main(_spawner: Spawner) { let _bl = Output::new(bl, Level::High); // display interface abstraction from SPI and DC - let di = SPIDeviceInterface::new(display_spi, dcx); - - // create driver - let mut display = ST7789::new(di, rst, 240, 320); - - // initialize - display.init(&mut Delay).unwrap(); - - // set default orientation - display.set_orientation(Orientation::Landscape).unwrap(); + let di = SPIInterface::new(display_spi, dcx); + // Define the display from the display interface and initialize it + let mut display = Builder::new(ST7789, di) + .display_size(240, 320) + .reset_pin(rst) + .orientation(Orientation::new().rotate(Rotation::Deg90)) + .init(&mut Delay) + .unwrap(); display.clear(Rgb565::BLACK).unwrap(); let raw_image_data = ImageRawLE::new(include_bytes!("../../assets/ferris.raw"), 86); @@ -175,138 +175,3 @@ mod touch { } } } - -mod my_display_interface { - use display_interface::{DataFormat, DisplayError, WriteOnlyDataCommand}; - use embedded_hal_1::digital::OutputPin; - use embedded_hal_1::spi::SpiDevice; - - /// SPI display interface. - /// - /// This combines the SPI peripheral and a data/command pin - pub struct SPIDeviceInterface { - spi: SPI, - dc: DC, - } - - impl SPIDeviceInterface - where - SPI: SpiDevice, - DC: OutputPin, - { - /// Create new SPI interface for communciation with a display driver - pub fn new(spi: SPI, dc: DC) -> Self { - Self { spi, dc } - } - } - - impl WriteOnlyDataCommand for SPIDeviceInterface - where - SPI: SpiDevice, - DC: OutputPin, - { - fn send_commands(&mut self, cmds: DataFormat<'_>) -> Result<(), DisplayError> { - // 1 = data, 0 = command - self.dc.set_low().map_err(|_| DisplayError::DCError)?; - - send_u8(&mut self.spi, cmds).map_err(|_| DisplayError::BusWriteError)?; - Ok(()) - } - - fn send_data(&mut self, buf: DataFormat<'_>) -> Result<(), DisplayError> { - // 1 = data, 0 = command - self.dc.set_high().map_err(|_| DisplayError::DCError)?; - - send_u8(&mut self.spi, buf).map_err(|_| DisplayError::BusWriteError)?; - Ok(()) - } - } - - fn send_u8(spi: &mut T, words: DataFormat<'_>) -> Result<(), T::Error> { - match words { - DataFormat::U8(slice) => spi.write(slice), - DataFormat::U16(slice) => { - use byte_slice_cast::*; - spi.write(slice.as_byte_slice()) - } - DataFormat::U16LE(slice) => { - use byte_slice_cast::*; - for v in slice.as_mut() { - *v = v.to_le(); - } - spi.write(slice.as_byte_slice()) - } - DataFormat::U16BE(slice) => { - use byte_slice_cast::*; - for v in slice.as_mut() { - *v = v.to_be(); - } - spi.write(slice.as_byte_slice()) - } - DataFormat::U8Iter(iter) => { - let mut buf = [0; 32]; - let mut i = 0; - - for v in iter.into_iter() { - buf[i] = v; - i += 1; - - if i == buf.len() { - spi.write(&buf)?; - i = 0; - } - } - - if i > 0 { - spi.write(&buf[..i])?; - } - - Ok(()) - } - DataFormat::U16LEIter(iter) => { - use byte_slice_cast::*; - let mut buf = [0; 32]; - let mut i = 0; - - for v in iter.map(u16::to_le) { - buf[i] = v; - i += 1; - - if i == buf.len() { - spi.write(&buf.as_byte_slice())?; - i = 0; - } - } - - if i > 0 { - spi.write(&buf[..i].as_byte_slice())?; - } - - Ok(()) - } - DataFormat::U16BEIter(iter) => { - use byte_slice_cast::*; - let mut buf = [0; 64]; - let mut i = 0; - let len = buf.len(); - - for v in iter.map(u16::to_be) { - buf[i] = v; - i += 1; - - if i == len { - spi.write(&buf.as_byte_slice())?; - i = 0; - } - } - - if i > 0 { - spi.write(&buf[..i].as_byte_slice())?; - } - - Ok(()) - } - _ => unimplemented!(), - } - } -} From 7733b99e0960d41d7b7d88f4d94adb6b21eb17b4 Mon Sep 17 00:00:00 2001 From: Georges Palauqui Date: Sun, 27 Oct 2024 09:53:00 +0100 Subject: [PATCH 145/361] update spi_display example for rp23 --- examples/rp23/Cargo.toml | 8 +- examples/rp23/src/bin/spi_display.rs | 161 +++------------------------ 2 files changed, 17 insertions(+), 152 deletions(-) diff --git a/examples/rp23/Cargo.toml b/examples/rp23/Cargo.toml index 5527a1e0a..eec12a9ab 100644 --- a/examples/rp23/Cargo.toml +++ b/examples/rp23/Cargo.toml @@ -38,10 +38,10 @@ cortex-m = { version = "0.7.6", features = ["inline-asm"] } cortex-m-rt = "0.7.0" critical-section = "1.1" panic-probe = { version = "0.3", features = ["print-defmt"] } -display-interface-spi = "0.4.1" -embedded-graphics = "0.7.1" -st7789 = "0.6.1" -display-interface = "0.4.1" +display-interface-spi = "0.5.0" +embedded-graphics = "0.8.1" +mipidsi = "0.8.0" +display-interface = "0.5.0" byte-slice-cast = { version = "1.2.0", default-features = false } smart-leds = "0.3.0" heapless = "0.8" diff --git a/examples/rp23/src/bin/spi_display.rs b/examples/rp23/src/bin/spi_display.rs index 195db5a97..6b7c0781f 100644 --- a/examples/rp23/src/bin/spi_display.rs +++ b/examples/rp23/src/bin/spi_display.rs @@ -1,4 +1,4 @@ -//! This example shows how to use SPI (Serial Peripheral Interface) in the RP2040 chip. +//! This example shows how to use SPI (Serial Peripheral Interface) in the RP2350 chip. //! //! Example written for a display using the ST7789 chip. Possibly the Waveshare Pico-ResTouch //! (https://www.waveshare.com/wiki/Pico-ResTouch-LCD-2.8) @@ -9,6 +9,7 @@ use core::cell::RefCell; use defmt::*; +use display_interface_spi::SPIInterface; use embassy_embedded_hal::shared_bus::blocking::spi::SpiDeviceWithConfig; use embassy_executor::Spawner; use embassy_rp::block::ImageDef; @@ -25,14 +26,15 @@ use embedded_graphics::pixelcolor::Rgb565; use embedded_graphics::prelude::*; use embedded_graphics::primitives::{PrimitiveStyleBuilder, Rectangle}; use embedded_graphics::text::Text; -use st7789::{Orientation, ST7789}; +use mipidsi::models::ST7789; +use mipidsi::options::{Orientation, Rotation}; +use mipidsi::Builder; use {defmt_rtt as _, panic_probe as _}; #[link_section = ".start_block"] #[used] pub static IMAGE_DEF: ImageDef = ImageDef::secure_exe(); -use crate::my_display_interface::SPIDeviceInterface; use crate::touch::Touch; const DISPLAY_FREQ: u32 = 64_000_000; @@ -79,17 +81,15 @@ async fn main(_spawner: Spawner) { let _bl = Output::new(bl, Level::High); // display interface abstraction from SPI and DC - let di = SPIDeviceInterface::new(display_spi, dcx); - - // create driver - let mut display = ST7789::new(di, rst, 240, 320); - - // initialize - display.init(&mut Delay).unwrap(); - - // set default orientation - display.set_orientation(Orientation::Landscape).unwrap(); + let di = SPIInterface::new(display_spi, dcx); + // Define the display from the display interface and initialize it + let mut display = Builder::new(ST7789, di) + .display_size(240, 320) + .reset_pin(rst) + .orientation(Orientation::new().rotate(Rotation::Deg90)) + .init(&mut Delay) + .unwrap(); display.clear(Rgb565::BLACK).unwrap(); let raw_image_data = ImageRawLE::new(include_bytes!("../../assets/ferris.raw"), 86); @@ -180,138 +180,3 @@ mod touch { } } } - -mod my_display_interface { - use display_interface::{DataFormat, DisplayError, WriteOnlyDataCommand}; - use embedded_hal_1::digital::OutputPin; - use embedded_hal_1::spi::SpiDevice; - - /// SPI display interface. - /// - /// This combines the SPI peripheral and a data/command pin - pub struct SPIDeviceInterface { - spi: SPI, - dc: DC, - } - - impl SPIDeviceInterface - where - SPI: SpiDevice, - DC: OutputPin, - { - /// Create new SPI interface for communciation with a display driver - pub fn new(spi: SPI, dc: DC) -> Self { - Self { spi, dc } - } - } - - impl WriteOnlyDataCommand for SPIDeviceInterface - where - SPI: SpiDevice, - DC: OutputPin, - { - fn send_commands(&mut self, cmds: DataFormat<'_>) -> Result<(), DisplayError> { - // 1 = data, 0 = command - self.dc.set_low().map_err(|_| DisplayError::DCError)?; - - send_u8(&mut self.spi, cmds).map_err(|_| DisplayError::BusWriteError)?; - Ok(()) - } - - fn send_data(&mut self, buf: DataFormat<'_>) -> Result<(), DisplayError> { - // 1 = data, 0 = command - self.dc.set_high().map_err(|_| DisplayError::DCError)?; - - send_u8(&mut self.spi, buf).map_err(|_| DisplayError::BusWriteError)?; - Ok(()) - } - } - - fn send_u8(spi: &mut T, words: DataFormat<'_>) -> Result<(), T::Error> { - match words { - DataFormat::U8(slice) => spi.write(slice), - DataFormat::U16(slice) => { - use byte_slice_cast::*; - spi.write(slice.as_byte_slice()) - } - DataFormat::U16LE(slice) => { - use byte_slice_cast::*; - for v in slice.as_mut() { - *v = v.to_le(); - } - spi.write(slice.as_byte_slice()) - } - DataFormat::U16BE(slice) => { - use byte_slice_cast::*; - for v in slice.as_mut() { - *v = v.to_be(); - } - spi.write(slice.as_byte_slice()) - } - DataFormat::U8Iter(iter) => { - let mut buf = [0; 32]; - let mut i = 0; - - for v in iter.into_iter() { - buf[i] = v; - i += 1; - - if i == buf.len() { - spi.write(&buf)?; - i = 0; - } - } - - if i > 0 { - spi.write(&buf[..i])?; - } - - Ok(()) - } - DataFormat::U16LEIter(iter) => { - use byte_slice_cast::*; - let mut buf = [0; 32]; - let mut i = 0; - - for v in iter.map(u16::to_le) { - buf[i] = v; - i += 1; - - if i == buf.len() { - spi.write(&buf.as_byte_slice())?; - i = 0; - } - } - - if i > 0 { - spi.write(&buf[..i].as_byte_slice())?; - } - - Ok(()) - } - DataFormat::U16BEIter(iter) => { - use byte_slice_cast::*; - let mut buf = [0; 64]; - let mut i = 0; - let len = buf.len(); - - for v in iter.map(u16::to_be) { - buf[i] = v; - i += 1; - - if i == len { - spi.write(&buf.as_byte_slice())?; - i = 0; - } - } - - if i > 0 { - spi.write(&buf[..i].as_byte_slice())?; - } - - Ok(()) - } - _ => unimplemented!(), - } - } -} From 917f1d1f4de6f7e25f483561001a9b5b36cbce7e Mon Sep 17 00:00:00 2001 From: Emil Fresk Date: Sun, 27 Oct 2024 09:55:00 +0100 Subject: [PATCH 146/361] This fixes 2 issues where STM32 BXCAN would hang 1. If one received frames under an `interrupt_free` section, in my case `init` in RTIC, the RX IRQ will fire and clear it's enable bit after `interrupt_free` is complete. There is no frame to read so RX is now unconditionally disabled forever. 2. On clearing of RX IRQ, TX stops silently. This happens due to the use of `write` instead of `modify` when modifying IRQ enable bits. Solution 1: Enable RX IRQs on every call to `try_read` that return no data. This solution also solves the issue of very delayed handling of the RX IRQ which would cause the same issue. Solution 2: Use `modify` instead of `write`. --- embassy-stm32/src/can/bxcan/mod.rs | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/embassy-stm32/src/can/bxcan/mod.rs b/embassy-stm32/src/can/bxcan/mod.rs index baa4bee79..19f1cea29 100644 --- a/embassy-stm32/src/can/bxcan/mod.rs +++ b/embassy-stm32/src/can/bxcan/mod.rs @@ -893,7 +893,7 @@ impl RxMode { RxFifo::Fifo0 => 0usize, RxFifo::Fifo1 => 1usize, }; - T::regs().ier().write(|w| { + T::regs().ier().modify(|w| { w.set_fmpie(fifo_idx, false); }); waker.wake(); @@ -936,18 +936,22 @@ impl RxMode { Self::NonBuffered(_) => { let registers = &info.regs; if let Some(msg) = registers.receive_fifo(RxFifo::Fifo0) { - registers.0.ier().write(|w| { + registers.0.ier().modify(|w| { w.set_fmpie(0, true); }); Ok(msg) } else if let Some(msg) = registers.receive_fifo(RxFifo::Fifo1) { - registers.0.ier().write(|w| { + registers.0.ier().modify(|w| { w.set_fmpie(1, true); }); Ok(msg) } else if let Some(err) = registers.curr_error() { Err(TryReadError::BusError(err)) } else { + registers.0.ier().modify(|w| { + w.set_fmpie(0, true); + w.set_fmpie(1, true); + }); Err(TryReadError::Empty) } } From edd36382958f006667c32627c2afae24f1861892 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Sun, 27 Oct 2024 19:21:38 +0100 Subject: [PATCH 147/361] Add examples/stm32h7b0 to CI --- ci.sh | 1 + 1 file changed, 1 insertion(+) diff --git a/ci.sh b/ci.sh index b04a4b288..9f7a7a037 100755 --- a/ci.sh +++ b/ci.sh @@ -216,6 +216,7 @@ cargo batch \ --- build --release --manifest-path examples/stm32g4/Cargo.toml --target thumbv7em-none-eabi --out-dir out/examples/stm32g4 \ --- build --release --manifest-path examples/stm32h5/Cargo.toml --target thumbv8m.main-none-eabihf --out-dir out/examples/stm32h5 \ --- build --release --manifest-path examples/stm32h7/Cargo.toml --target thumbv7em-none-eabi --out-dir out/examples/stm32h7 \ + --- build --release --manifest-path examples/stm32h7b0/Cargo.toml --target thumbv7em-none-eabi --out-dir out/examples/stm32h7b0 \ --- build --release --manifest-path examples/stm32h735/Cargo.toml --target thumbv7em-none-eabi --out-dir out/examples/stm32h735 \ --- build --release --manifest-path examples/stm32h755cm4/Cargo.toml --target thumbv7em-none-eabi --out-dir out/examples/stm32h755cm4 \ --- build --release --manifest-path examples/stm32h755cm7/Cargo.toml --target thumbv7em-none-eabi --out-dir out/examples/stm32h755cm7 \ From ca8e885dbb65541a380e66c678ae148c24782e8e Mon Sep 17 00:00:00 2001 From: Connor <10554880+meigs2@users.noreply.github.com> Date: Sun, 27 Oct 2024 17:41:15 -0500 Subject: [PATCH 148/361] Add tx_dma to async spi --- embassy-rp/src/spi.rs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/embassy-rp/src/spi.rs b/embassy-rp/src/spi.rs index b89df74a2..a8f4e72c7 100644 --- a/embassy-rp/src/spi.rs +++ b/embassy-rp/src/spi.rs @@ -359,17 +359,18 @@ impl<'d, T: Instance> Spi<'d, T, Async> { inner: impl Peripheral

+ 'd, clk: impl Peripheral

+ 'd> + 'd, miso: impl Peripheral

+ 'd> + 'd, + tx_dma: impl Peripheral

+ 'd, rx_dma: impl Peripheral

+ 'd, config: Config, ) -> Self { - into_ref!(rx_dma, clk, miso); + into_ref!(tx_dma, rx_dma, clk, miso); Self::new_inner( inner, Some(clk.map_into()), None, Some(miso.map_into()), None, - None, + Some(tx_dma.map_into()), Some(rx_dma.map_into()), config, ) From bfff50a3619be3a9fbc052a70b2e43c95cc5dc42 Mon Sep 17 00:00:00 2001 From: William <174336620+williams-one@users.noreply.github.com> Date: Mon, 28 Oct 2024 08:40:38 +0100 Subject: [PATCH 149/361] Fix format --- examples/stm32u5/src/bin/ltdc.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/examples/stm32u5/src/bin/ltdc.rs b/examples/stm32u5/src/bin/ltdc.rs index 5bf2cd67d..bd59a9148 100644 --- a/examples/stm32u5/src/bin/ltdc.rs +++ b/examples/stm32u5/src/bin/ltdc.rs @@ -316,9 +316,8 @@ impl OriginDimensions for DoubleBuffer { mod rcc_setup { - use embassy_stm32::rcc; use embassy_stm32::time::Hertz; - use embassy_stm32::{Config, Peripherals}; + use embassy_stm32::{rcc, Config, Peripherals}; /// Sets up clocks for the stm32u5g9zj mcu /// change this if you plan to use a different microcontroller From 76606b6fe040d0735ab8a0e9ac99894de06264c4 Mon Sep 17 00:00:00 2001 From: William <174336620+williams-one@users.noreply.github.com> Date: Mon, 28 Oct 2024 08:46:07 +0100 Subject: [PATCH 150/361] Update chip from stm32u585ai to stm32u5g9zj and fix pinout --- examples/stm32u5/.cargo/config.toml | 4 ++-- examples/stm32u5/Cargo.toml | 4 ++-- examples/stm32u5/src/bin/i2c.rs | 2 +- examples/stm32u5/src/bin/usb_serial.rs | 4 ++-- 4 files changed, 7 insertions(+), 7 deletions(-) diff --git a/examples/stm32u5/.cargo/config.toml b/examples/stm32u5/.cargo/config.toml index 36c5b63a6..bdbd86354 100644 --- a/examples/stm32u5/.cargo/config.toml +++ b/examples/stm32u5/.cargo/config.toml @@ -1,6 +1,6 @@ [target.'cfg(all(target_arch = "arm", target_os = "none"))'] -# replace STM32U585AIIx with your chip as listed in `probe-rs chip list` -runner = "probe-rs run --chip STM32U585AIIx" +# replace STM32U5G9ZJTxQ with your chip as listed in `probe-rs chip list` +runner = "probe-rs run --chip STM32U5G9ZJTxQ" [build] target = "thumbv8m.main-none-eabihf" diff --git a/examples/stm32u5/Cargo.toml b/examples/stm32u5/Cargo.toml index f594ad71a..8b576425c 100644 --- a/examples/stm32u5/Cargo.toml +++ b/examples/stm32u5/Cargo.toml @@ -5,8 +5,8 @@ version = "0.1.0" license = "MIT OR Apache-2.0" [dependencies] -# Change stm32u585ai to your chip name, if necessary. -embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["defmt", "unstable-pac", "stm32u585ai", "time-driver-any", "memory-x" ] } +# Change stm32u5g9zj to your chip name, if necessary. +embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["defmt", "unstable-pac", "stm32u5g9zj", "time-driver-any", "memory-x" ] } embassy-sync = { version = "0.6.0", path = "../../embassy-sync", features = ["defmt"] } embassy-executor = { version = "0.6.1", path = "../../embassy-executor", features = ["task-arena-size-32768", "arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] } embassy-time = { version = "0.3.2", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } diff --git a/examples/stm32u5/src/bin/i2c.rs b/examples/stm32u5/src/bin/i2c.rs index 19a78eac9..d5f5d6f60 100644 --- a/examples/stm32u5/src/bin/i2c.rs +++ b/examples/stm32u5/src/bin/i2c.rs @@ -13,7 +13,7 @@ const WHOAMI: u8 = 0x0F; #[embassy_executor::main] async fn main(_spawner: Spawner) { let p = embassy_stm32::init(Default::default()); - let mut i2c = I2c::new_blocking(p.I2C2, p.PH4, p.PH5, Hertz(100_000), Default::default()); + let mut i2c = I2c::new_blocking(p.I2C2, p.PF1, p.PF0, Hertz(100_000), Default::default()); let mut data = [0u8; 1]; unwrap!(i2c.blocking_write_read(HTS221_ADDRESS, &[WHOAMI], &mut data)); diff --git a/examples/stm32u5/src/bin/usb_serial.rs b/examples/stm32u5/src/bin/usb_serial.rs index 4d56395da..4bb1a6079 100644 --- a/examples/stm32u5/src/bin/usb_serial.rs +++ b/examples/stm32u5/src/bin/usb_serial.rs @@ -13,7 +13,7 @@ use embassy_usb::Builder; use panic_probe as _; bind_interrupts!(struct Irqs { - OTG_FS => usb::InterruptHandler; + OTG_HS => usb::InterruptHandler; }); #[embassy_executor::main] @@ -48,7 +48,7 @@ async fn main(_spawner: Spawner) { // to enable vbus_detection to comply with the USB spec. If you enable it, the board // has to support it or USB won't work at all. See docs on `vbus_detection` for details. config.vbus_detection = false; - let driver = Driver::new_fs(p.USB_OTG_FS, Irqs, p.PA12, p.PA11, &mut ep_out_buffer, config); + let driver = Driver::new_hs(p.USB_OTG_HS, Irqs, p.PA12, p.PA11, &mut ep_out_buffer, config); // Create embassy-usb Config let mut config = embassy_usb::Config::new(0xc0de, 0xcafe); From 5db6b4874def68a5cd1fe25e75327cf0abdbaa59 Mon Sep 17 00:00:00 2001 From: Dion Dokter Date: Tue, 29 Oct 2024 17:13:09 +0100 Subject: [PATCH 151/361] Expose async functions for QSPI --- embassy-stm32/src/qspi/mod.rs | 32 ++++++++++++++++++++++++++++---- 1 file changed, 28 insertions(+), 4 deletions(-) diff --git a/embassy-stm32/src/qspi/mod.rs b/embassy-stm32/src/qspi/mod.rs index 308947e99..715c260e9 100644 --- a/embassy-stm32/src/qspi/mod.rs +++ b/embassy-stm32/src/qspi/mod.rs @@ -353,6 +353,21 @@ impl<'d, T: Instance> Qspi<'d, T, Async> { /// Blocking read data, using DMA. pub fn blocking_read_dma(&mut self, buf: &mut [u8], transaction: TransferConfig) { + let transfer = self.start_read_transfer(transaction, buf); + transfer.blocking_wait(); + } + + /// Blocking read data, using DMA. + pub async fn read_dma(&mut self, buf: &mut [u8], transaction: TransferConfig) { + let transfer = self.start_read_transfer(transaction, buf); + transfer.await; + } + + fn start_read_transfer<'a>( + &'a mut self, + transaction: TransferConfig, + buf: &'a mut [u8], + ) -> crate::dma::Transfer<'a> { self.setup_transaction(QspiMode::IndirectWrite, &transaction, Some(buf.len())); T::REGS.ccr().modify(|v| { @@ -373,12 +388,22 @@ impl<'d, T: Instance> Qspi<'d, T, Async> { // STM32H7 does not have dmaen #[cfg(not(stm32h7))] T::REGS.cr().modify(|v| v.set_dmaen(true)); - - transfer.blocking_wait(); + transfer } /// Blocking write data, using DMA. pub fn blocking_write_dma(&mut self, buf: &[u8], transaction: TransferConfig) { + let transfer = self.start_write_transfer(transaction, buf); + transfer.blocking_wait(); + } + + /// Async write data, using DMA. + pub async fn write_dma(&mut self, buf: &[u8], transaction: TransferConfig) { + let transfer = self.start_write_transfer(transaction, buf); + transfer.await; + } + + fn start_write_transfer<'a>(&'a mut self, transaction: TransferConfig, buf: &'a [u8]) -> crate::dma::Transfer<'a> { self.setup_transaction(QspiMode::IndirectWrite, &transaction, Some(buf.len())); T::REGS.ccr().modify(|v| { @@ -395,8 +420,7 @@ impl<'d, T: Instance> Qspi<'d, T, Async> { // STM32H7 does not have dmaen #[cfg(not(stm32h7))] T::REGS.cr().modify(|v| v.set_dmaen(true)); - - transfer.blocking_wait(); + transfer } } From 2d899a17e727a9b68ce01090b412c8211f055293 Mon Sep 17 00:00:00 2001 From: Dion Dokter Date: Tue, 29 Oct 2024 17:26:35 +0100 Subject: [PATCH 152/361] Add some sanity checks --- embassy-stm32/src/qspi/mod.rs | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/embassy-stm32/src/qspi/mod.rs b/embassy-stm32/src/qspi/mod.rs index 715c260e9..6530b75b1 100644 --- a/embassy-stm32/src/qspi/mod.rs +++ b/embassy-stm32/src/qspi/mod.rs @@ -202,6 +202,18 @@ impl<'d, T: Instance, M: PeriMode> Qspi<'d, T, M> { } fn setup_transaction(&mut self, fmode: QspiMode, transaction: &TransferConfig, data_len: Option) { + if let (Some(_), QspiWidth::NONE) = (transaction.address, transaction.awidth) { + panic!("QSPI address can't be sent with an address width of NONE"); + } + + if let (Some(_), QspiWidth::NONE) = (data_len, transaction.dwidth) { + panic!("QSPI data can't be sent with a data width of NONE"); + } + + if let Some(0) = data_len { + panic!("QSPI data must be at least one byte"); + } + T::REGS.fcr().modify(|v| { v.set_csmf(true); v.set_ctcf(true); From 9fdfe5e99bfd28c18af287e6351d556ff5458025 Mon Sep 17 00:00:00 2001 From: Dion Dokter Date: Tue, 29 Oct 2024 17:50:46 +0100 Subject: [PATCH 153/361] Fix typo --- embassy-stm32/src/qspi/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/embassy-stm32/src/qspi/mod.rs b/embassy-stm32/src/qspi/mod.rs index 6530b75b1..40c064d57 100644 --- a/embassy-stm32/src/qspi/mod.rs +++ b/embassy-stm32/src/qspi/mod.rs @@ -369,7 +369,7 @@ impl<'d, T: Instance> Qspi<'d, T, Async> { transfer.blocking_wait(); } - /// Blocking read data, using DMA. + /// Async read data, using DMA. pub async fn read_dma(&mut self, buf: &mut [u8], transaction: TransferConfig) { let transfer = self.start_read_transfer(transaction, buf); transfer.await; From 28b1e0a98baaaacb72fc44255a9fdad7af35763b Mon Sep 17 00:00:00 2001 From: Frostie314159 Date: Tue, 29 Oct 2024 23:05:50 +0100 Subject: [PATCH 154/361] Reexported some smoltcp types for raw socket. --- embassy-net/src/raw.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/embassy-net/src/raw.rs b/embassy-net/src/raw.rs index ace325a46..0169606d5 100644 --- a/embassy-net/src/raw.rs +++ b/embassy-net/src/raw.rs @@ -5,10 +5,10 @@ use core::mem; use core::task::{Context, Poll}; use embassy_net_driver::Driver; -use smoltcp::iface::{Interface, SocketHandle}; +pub use smoltcp::iface::{Interface, SocketHandle}; use smoltcp::socket::raw; pub use smoltcp::socket::raw::PacketMetadata; -use smoltcp::wire::{IpProtocol, IpVersion}; +pub use smoltcp::wire::{IpProtocol, IpVersion}; use crate::Stack; From a3bbb3b43abd61ce86d6cd7b949a115f243e5dba Mon Sep 17 00:00:00 2001 From: Dion Dokter Date: Tue, 29 Oct 2024 23:35:28 +0100 Subject: [PATCH 155/361] Add check for the flipside of the coin too --- embassy-stm32/src/qspi/mod.rs | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/embassy-stm32/src/qspi/mod.rs b/embassy-stm32/src/qspi/mod.rs index 40c064d57..10b1cbc70 100644 --- a/embassy-stm32/src/qspi/mod.rs +++ b/embassy-stm32/src/qspi/mod.rs @@ -202,16 +202,19 @@ impl<'d, T: Instance, M: PeriMode> Qspi<'d, T, M> { } fn setup_transaction(&mut self, fmode: QspiMode, transaction: &TransferConfig, data_len: Option) { - if let (Some(_), QspiWidth::NONE) = (transaction.address, transaction.awidth) { - panic!("QSPI address can't be sent with an address width of NONE"); + match (transaction.address, transaction.awidth) { + (Some(_), QspiWidth::NONE) => panic!("QSPI address can't be sent with an address width of NONE"), + (Some(_), _) => {} + (None, QspiWidth::NONE) => {} + (None, _) => panic!("QSPI address is not set, so the address width should be NONE"), } - if let (Some(_), QspiWidth::NONE) = (data_len, transaction.dwidth) { - panic!("QSPI data can't be sent with a data width of NONE"); - } - - if let Some(0) = data_len { - panic!("QSPI data must be at least one byte"); + match (data_len, transaction.dwidth) { + (Some(0), _) => panic!("QSPI data must be at least one byte"), + (Some(_), QspiWidth::NONE) => panic!("QSPI data can't be sent with a data width of NONE"), + (Some(_), _) => {} + (None, QspiWidth::NONE) => {} + (None, _) => panic!("QSPI data is empty, so the data width should be NONE"), } T::REGS.fcr().modify(|v| { From 117d091ea1dd7aac410e6d26492866f9d20d3ec6 Mon Sep 17 00:00:00 2001 From: Frostie314159 Date: Wed, 30 Oct 2024 12:30:48 +0100 Subject: [PATCH 156/361] Made import private again. --- embassy-net/src/raw.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/embassy-net/src/raw.rs b/embassy-net/src/raw.rs index 0169606d5..1f725d00b 100644 --- a/embassy-net/src/raw.rs +++ b/embassy-net/src/raw.rs @@ -5,7 +5,7 @@ use core::mem; use core::task::{Context, Poll}; use embassy_net_driver::Driver; -pub use smoltcp::iface::{Interface, SocketHandle}; +use smoltcp::iface::{Interface, SocketHandle}; use smoltcp::socket::raw; pub use smoltcp::socket::raw::PacketMetadata; pub use smoltcp::wire::{IpProtocol, IpVersion}; From 93dd21042ceadea114b01c40a250d33402672569 Mon Sep 17 00:00:00 2001 From: flippette Date: Thu, 31 Oct 2024 22:14:11 +0200 Subject: [PATCH 157/361] Implement `embedded_io::Write` for `Uart<'d, T: Instance, Blocking>` (#3483) * Implement `embedded_io::{Read,Write}` for `Uart<'d, T: Instance, Blocking>` * Unimplement `embedded_io::Read` for `Uart<'d, T: Instance, Blocking>` * Revert "Unimplement `embedded_io::Read` for `Uart<'d, T: Instance, Blocking>`" * Unimplement `embedded_io::Read` for `Uart<'d, T: Instance, Blocking>` (take 2) --- embassy-rp/src/uart/mod.rs | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/embassy-rp/src/uart/mod.rs b/embassy-rp/src/uart/mod.rs index c94e5e185..ad98a46c7 100644 --- a/embassy-rp/src/uart/mod.rs +++ b/embassy-rp/src/uart/mod.rs @@ -1264,6 +1264,20 @@ impl<'d, T: Instance, M: Mode> embedded_hal_nb::serial::Write for Uart<'d, T, M> } } +impl<'d, T: Instance> embedded_io::ErrorType for Uart<'d, T, Blocking> { + type Error = Error; +} + +impl<'d, T: Instance> embedded_io::Write for Uart<'d, T, Blocking> { + fn write(&mut self, buf: &[u8]) -> Result { + self.blocking_write(buf).map(|_| buf.len()) + } + + fn flush(&mut self) -> Result<(), Self::Error> { + self.blocking_flush() + } +} + trait SealedMode {} trait SealedInstance { From 3225848bd2e1ffd7fdc7df08d8262f5e6164409f Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Thu, 31 Oct 2024 21:26:40 +0100 Subject: [PATCH 158/361] rp/pio: ensure PADS IE=1 which is not the default in rp235x. Fixes #3476 --- embassy-rp/src/pio/mod.rs | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/embassy-rp/src/pio/mod.rs b/embassy-rp/src/pio/mod.rs index 72aa8f104..98f4f8943 100644 --- a/embassy-rp/src/pio/mod.rs +++ b/embassy-rp/src/pio/mod.rs @@ -1054,9 +1054,17 @@ impl<'d, PIO: Instance> Common<'d, PIO> { pub fn make_pio_pin(&mut self, pin: impl Peripheral

+ 'd) -> Pin<'d, PIO> { into_ref!(pin); pin.gpio().ctrl().write(|w| w.set_funcsel(PIO::FUNCSEL as _)); - #[cfg(feature = "_rp235x")] - pin.pad_ctrl().modify(|w| { + pin.pad_ctrl().write(|w| { + #[cfg(feature = "_rp235x")] w.set_iso(false); + w.set_schmitt(true); + w.set_slewfast(false); + // TODO rp235x errata E9 recommends to not enable IE if we're not + // going to use input. Maybe add an API for the user to enable/disable this? + w.set_ie(true); + w.set_od(false); + w.set_pue(false); + w.set_pde(false); }); // we can be relaxed about this because we're &mut here and nothing is cached PIO::state().used_pins.fetch_or(1 << pin.pin_bank(), Ordering::Relaxed); From 70214fc2c23aded1f5a1b8a62425ac3aa581102f Mon Sep 17 00:00:00 2001 From: Anthony Grondin <104731965+AnthonyGrondin@users.noreply.github.com> Date: Thu, 31 Oct 2024 16:20:01 -0400 Subject: [PATCH 159/361] feat(embassy-net): Implement `TcpReader::wait_read_ready()` + `TcpWriter::wait_send_ready()` --- embassy-net/src/tcp.rs | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/embassy-net/src/tcp.rs b/embassy-net/src/tcp.rs index a00ced8f4..150b4b36b 100644 --- a/embassy-net/src/tcp.rs +++ b/embassy-net/src/tcp.rs @@ -73,6 +73,16 @@ pub struct TcpWriter<'a> { } impl<'a> TcpReader<'a> { + /// Wait until the socket becomes readable. + /// + /// A socket becomes readable when the receive half of the full-duplex connection is open + /// (see [`may_recv()`](TcpSocket::may_recv)), and there is some pending data in the receive buffer. + /// + /// This is the equivalent of [read](#method.read), without buffering any data. + pub async fn wait_read_ready(&self) { + poll_fn(move |cx| self.io.poll_read_ready(cx)).await + } + /// Read data from the socket. /// /// Returns how many bytes were read, or an error. If no data is available, it waits @@ -115,6 +125,16 @@ impl<'a> TcpReader<'a> { } impl<'a> TcpWriter<'a> { + /// Wait until the socket becomes writable. + /// + /// A socket becomes writable when the transmit half of the full-duplex connection is open + /// (see [`may_send()`](TcpSocket::may_send)), and the transmit buffer is not full. + /// + /// This is the equivalent of [write](#method.write), without sending any data. + pub async fn wait_write_ready(&self) { + poll_fn(move |cx| self.io.poll_write_ready(cx)).await + } + /// Write data to the socket. /// /// Returns how many bytes were written, or an error. If the socket is not ready to From acaba50704b27abe6160539e6e764f5df0d58120 Mon Sep 17 00:00:00 2001 From: Anthony Grondin <104731965+AnthonyGrondin@users.noreply.github.com> Date: Thu, 31 Oct 2024 22:19:49 -0400 Subject: [PATCH 160/361] feat(embassy-net): Implement `wait_send_ready()` + `wait_recv_ready()` for Raw sockets. --- embassy-net/src/raw.rs | 53 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 53 insertions(+) diff --git a/embassy-net/src/raw.rs b/embassy-net/src/raw.rs index 1f725d00b..a88bcc458 100644 --- a/embassy-net/src/raw.rs +++ b/embassy-net/src/raw.rs @@ -62,6 +62,14 @@ impl<'a> RawSocket<'a> { }) } + /// Wait until the socket becomes readable. + /// + /// A socket is readable when a packet has been received, or when there are queued packets in + /// the buffer. + pub async fn wait_recv_ready(&self) { + poll_fn(move |cx| self.poll_recv_ready(cx)).await + } + /// Receive a datagram. /// /// This method will wait until a datagram is received. @@ -69,6 +77,24 @@ impl<'a> RawSocket<'a> { poll_fn(move |cx| self.poll_recv(buf, cx)).await } + /// Wait until a datagram can be read. + /// + /// When no datagram is readable, this method will return `Poll::Pending` and + /// register the current task to be notified when a datagram is received. + /// + /// When a datagram is received, this method will return `Poll::Ready`. + pub fn poll_recv_ready(&self, cx: &mut Context<'_>) -> Poll<()> { + self.with_mut(|s, _| { + if s.can_recv() { + Poll::Ready(()) + } else { + // socket buffer is empty wait until at least one byte has arrived + s.register_recv_waker(cx.waker()); + Poll::Pending + } + }) + } + /// Receive a datagram. /// /// When no datagram is available, this method will return `Poll::Pending` and @@ -85,6 +111,33 @@ impl<'a> RawSocket<'a> { }) } + /// Wait until the socket becomes writable. + /// + /// A socket becomes writable when there is space in the buffer, from initial memory or after + /// dispatching datagrams on a full buffer. + pub async fn wait_send_ready(&self) { + poll_fn(move |cx| self.poll_send_ready(cx)).await + } + + /// Wait until a datagram can be sent. + /// + /// When no datagram can be sent (i.e. the buffer is full), this method will return + /// `Poll::Pending` and register the current task to be notified when + /// space is freed in the buffer after a datagram has been dispatched. + /// + /// When a datagram can be sent, this method will return `Poll::Ready`. + pub fn poll_send_ready(&self, cx: &mut Context<'_>) -> Poll<()> { + self.with_mut(|s, _| { + if s.can_send() { + Poll::Ready(()) + } else { + // socket buffer is full wait until a datagram has been dispatched + s.register_send_waker(cx.waker()); + Poll::Pending + } + }) + } + /// Send a datagram. /// /// This method will wait until the datagram has been sent.` From 333d8584812c0ea3e1f9262922befbd3fe709775 Mon Sep 17 00:00:00 2001 From: Bjorn <75190918+BjornTheProgrammer@users.noreply.github.com> Date: Thu, 31 Oct 2024 22:51:03 -0700 Subject: [PATCH 161/361] Added ReceiverHandler to logger --- embassy-usb-logger/src/lib.rs | 92 ++++++++++++++++--- .../rp/src/bin/usb_serial_with_handler.rs | 64 +++++++++++++ 2 files changed, 143 insertions(+), 13 deletions(-) create mode 100644 examples/rp/src/bin/usb_serial_with_handler.rs diff --git a/embassy-usb-logger/src/lib.rs b/embassy-usb-logger/src/lib.rs index 11188b4ef..29c102f10 100644 --- a/embassy-usb-logger/src/lib.rs +++ b/embassy-usb-logger/src/lib.rs @@ -3,6 +3,7 @@ #![warn(missing_docs)] use core::fmt::Write as _; +use core::future::Future; use embassy_futures::join::join; use embassy_sync::pipe::Pipe; @@ -13,6 +14,25 @@ use log::{Metadata, Record}; type CS = embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex; +/// A trait that can be implemented and then passed to the +pub trait ReceiverHandler { + /// Data comes in from the serial port with each command and runs this function + fn handle_data(&self, data: &[u8]) -> impl Future + Send; + + /// Create a new instance of the Handler + fn new() -> Self; +} + +/// Use this Handler if you don't wish to use any handler +pub struct DummyHandler; + +impl ReceiverHandler for DummyHandler { + async fn handle_data(&self, _data: &[u8]) {} + fn new() -> Self { + Self {} + } +} + /// The logger state containing buffers that must live as long as the USB peripheral. pub struct LoggerState<'d> { state: State<'d>, @@ -39,17 +59,19 @@ impl<'d> LoggerState<'d> { pub const MAX_PACKET_SIZE: u8 = 64; /// The logger handle, which contains a pipe with configurable size for buffering log messages. -pub struct UsbLogger { +pub struct UsbLogger { buffer: Pipe, custom_style: Option) -> ()>, + recieve_handler: Option, } -impl UsbLogger { +impl UsbLogger { /// Create a new logger instance. pub const fn new() -> Self { Self { buffer: Pipe::new(), custom_style: None, + recieve_handler: None, } } @@ -58,9 +80,15 @@ impl UsbLogger { Self { buffer: Pipe::new(), custom_style: Some(custom_style), + recieve_handler: None, } } + /// Add a command handler to the logger + pub fn with_handler(&mut self, handler: T) { + self.recieve_handler = Some(handler); + } + /// Run the USB logger using the state and USB driver. Never returns. pub async fn run<'d, D>(&'d self, state: &'d mut LoggerState<'d>, driver: D) -> ! where @@ -118,15 +146,22 @@ impl UsbLogger { } } }; - let discard_fut = async { - let mut discard_buf: [u8; MAX_PACKET_SIZE as usize] = [0; MAX_PACKET_SIZE as usize]; + let reciever_fut = async { + let mut reciever_buf: [u8; MAX_PACKET_SIZE as usize] = [0; MAX_PACKET_SIZE as usize]; receiver.wait_connection().await; loop { - let _ = receiver.read_packet(&mut discard_buf).await; + let n = receiver.read_packet(&mut reciever_buf).await.unwrap(); + match &self.recieve_handler { + Some(handler) => { + let data = &reciever_buf[..n]; + handler.handle_data(data).await; + } + None => (), + } } }; - join(log_fut, discard_fut).await; + join(log_fut, reciever_fut).await; } /// Creates the futures needed for the logger from a given class @@ -142,7 +177,7 @@ impl UsbLogger { } } -impl log::Log for UsbLogger { +impl log::Log for UsbLogger { fn enabled(&self, _metadata: &Metadata) -> bool { true } @@ -182,7 +217,7 @@ impl<'d, const N: usize> core::fmt::Write for Writer<'d, N> { /// Initialize and run the USB serial logger, never returns. /// -/// Arguments specify the buffer size, log level and the USB driver, respectively. +/// Arguments specify the buffer size, log level and the USB driver, respectively. You can optionally add a RecieverHandler. /// /// # Usage /// @@ -196,17 +231,27 @@ impl<'d, const N: usize> core::fmt::Write for Writer<'d, N> { #[macro_export] macro_rules! run { ( $x:expr, $l:expr, $p:ident ) => { - static LOGGER: ::embassy_usb_logger::UsbLogger<$x> = ::embassy_usb_logger::UsbLogger::new(); + static LOGGER: ::embassy_usb_logger::UsbLogger<$x, ::embassy_usb_logger::DummyHandler> = + ::embassy_usb_logger::UsbLogger::new(); unsafe { let _ = ::log::set_logger_racy(&LOGGER).map(|()| log::set_max_level_racy($l)); } let _ = LOGGER.run(&mut ::embassy_usb_logger::LoggerState::new(), $p).await; }; + + ( $x:expr, $l:expr, $p:ident, $h:ty ) => { + unsafe { + static mut LOGGER: ::embassy_usb_logger::UsbLogger<$x, $h> = ::embassy_usb_logger::UsbLogger::new(); + LOGGER.with_handler(<$h>::new()); + let _ = ::log::set_logger_racy(&LOGGER).map(|()| log::set_max_level_racy($l)); + let _ = LOGGER.run(&mut ::embassy_usb_logger::LoggerState::new(), $p).await; + } + }; } /// Initialize the USB serial logger from a serial class and return the future to run it. /// -/// Arguments specify the buffer size, log level and the serial class, respectively. +/// Arguments specify the buffer size, log level and the serial class, respectively. You can optionally add a RecieverHandler. /// /// # Usage /// @@ -220,19 +265,29 @@ macro_rules! run { #[macro_export] macro_rules! with_class { ( $x:expr, $l:expr, $p:ident ) => {{ - static LOGGER: ::embassy_usb_logger::UsbLogger<$x> = ::embassy_usb_logger::UsbLogger::new(); + static LOGGER: ::embassy_usb_logger::UsbLogger<$x, ::embassy_usb_logger::DummyHandler> = + ::embassy_usb_logger::UsbLogger::new(); unsafe { let _ = ::log::set_logger_racy(&LOGGER).map(|()| log::set_max_level_racy($l)); } LOGGER.create_future_from_class($p) }}; + + ( $x:expr, $l:expr, $p:ident, $h:ty ) => {{ + unsafe { + static mut LOGGER: ::embassy_usb_logger::UsbLogger<$x, $h> = ::embassy_usb_logger::UsbLogger::new(); + LOGGER.with_handler(<$h>::new()); + let _ = ::log::set_logger_racy(&LOGGER).map(|()| log::set_max_level_racy($l)); + LOGGER.create_future_from_class($p) + } + }}; } /// Initialize the USB serial logger from a serial class and return the future to run it. /// This version of the macro allows for a custom style function to be passed in. /// The custom style function will be called for each log record and is responsible for writing the log message to the buffer. /// -/// Arguments specify the buffer size, log level, the serial class and the custom style function, respectively. +/// Arguments specify the buffer size, log level, the serial class and the custom style function, respectively. You can optionally add a RecieverHandler. /// /// # Usage /// @@ -250,10 +305,21 @@ macro_rules! with_class { #[macro_export] macro_rules! with_custom_style { ( $x:expr, $l:expr, $p:ident, $s:expr ) => {{ - static LOGGER: ::embassy_usb_logger::UsbLogger<$x> = ::embassy_usb_logger::UsbLogger::with_custom_style($s); + static LOGGER: ::embassy_usb_logger::UsbLogger<$x, ::embassy_usb_logger::DummyHandler> = + ::embassy_usb_logger::UsbLogger::with_custom_style($s); unsafe { let _ = ::log::set_logger_racy(&LOGGER).map(|()| log::set_max_level_racy($l)); } LOGGER.create_future_from_class($p) }}; + + ( $x:expr, $l:expr, $p:ident, $s:expr, $h:ty ) => {{ + unsafe { + static mut LOGGER: ::embassy_usb_logger::UsbLogger<$x, $h> = + ::embassy_usb_logger::UsbLogger::with_custom_style($s); + LOGGER.with_handler(<$h>::new()); + let _ = ::log::set_logger_racy(&LOGGER).map(|()| log::set_max_level_racy($l)); + LOGGER.create_future_from_class($p) + } + }}; } diff --git a/examples/rp/src/bin/usb_serial_with_handler.rs b/examples/rp/src/bin/usb_serial_with_handler.rs new file mode 100644 index 000000000..a9e65be70 --- /dev/null +++ b/examples/rp/src/bin/usb_serial_with_handler.rs @@ -0,0 +1,64 @@ +//! This example shows how to use USB (Universal Serial Bus) in the RP2040 chip. +//! +//! This creates the possibility to send log::info/warn/error/debug! to USB serial port. + +#![no_std] +#![no_main] + +use core::str; + +use embassy_executor::Spawner; +use embassy_rp::bind_interrupts; +use embassy_rp::peripherals::USB; +use embassy_rp::rom_data::reset_to_usb_boot; +use embassy_rp::usb::{Driver, InterruptHandler}; +use embassy_time::Timer; +use embassy_usb_logger::ReceiverHandler; +use {defmt_rtt as _, panic_probe as _}; + +bind_interrupts!(struct Irqs { + USBCTRL_IRQ => InterruptHandler; +}); + +struct Handler; + +impl ReceiverHandler for Handler { + async fn handle_data(&self, data: &[u8]) { + if let Ok(data) = str::from_utf8(data) { + let data = data.trim(); + + // If you are using elf2uf2-term with the '-t' flag, then when closing the serial monitor, + // this will automatically put the pico into boot mode + if data == "q" || data == "elf2uf2-term" { + reset_to_usb_boot(0, 0); // Restart the chip + } else if data.eq_ignore_ascii_case("hello") { + log::info!("World!"); + } else { + log::info!("Recieved: {:?}", data); + } + } + } + + fn new() -> Self { + Self + } +} + +#[embassy_executor::task] +async fn logger_task(driver: Driver<'static, USB>) { + embassy_usb_logger::run!(1024, log::LevelFilter::Info, driver, Handler); +} + +#[embassy_executor::main] +async fn main(spawner: Spawner) { + let p = embassy_rp::init(Default::default()); + let driver = Driver::new(p.USB, Irqs); + spawner.spawn(logger_task(driver)).unwrap(); + + let mut counter = 0; + loop { + counter += 1; + log::info!("Tick {}", counter); + Timer::after_secs(1).await; + } +} From e93ac532aca2c699f19c43a080f2d8243e562a9a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Krzysztof=20Kr=C3=B3lczyk?= Date: Fri, 1 Nov 2024 11:51:11 +0100 Subject: [PATCH 162/361] feat/stm32: disable multicast filtering on eth v2 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Initially, this was feature-gated, but has been requested to be changed to be unconditional, see PR 3488 for reasons. When filtering is enabled, it intercepts and drops silently ipv6 packets, possibly somewhere around smoltcp::iface::interface::ipv6 lines 36, 44 in current head sha e9b66eadaeacef758ebc4a12378f8d2162144cf4 With filtering disabled (this patch), packets are received and communication over ipv6 is possible, neighbor discovery works. related: #2496 Signed-off-by: Krzysztof Królczyk --- embassy-stm32/src/eth/v2/mod.rs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/embassy-stm32/src/eth/v2/mod.rs b/embassy-stm32/src/eth/v2/mod.rs index b26f08cd9..9dd7f7d95 100644 --- a/embassy-stm32/src/eth/v2/mod.rs +++ b/embassy-stm32/src/eth/v2/mod.rs @@ -192,6 +192,9 @@ impl<'d, T: Instance, P: PHY> Ethernet<'d, T, P> { // TODO: Carrier sense ? ECRSFD }); + // Disable multicast filter + mac.macpfr().modify(|w| w.set_pm(true)); + // Note: Writing to LR triggers synchronisation of both LR and HR into the MAC core, // so the LR write must happen after the HR write. mac.maca0hr() From af694d233cf1d19f1aec9b522e8ef286b82424a3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Krzysztof=20Kr=C3=B3lczyk?= Date: Fri, 1 Nov 2024 15:37:47 +0100 Subject: [PATCH 163/361] chore: improve some log msgs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Krzysztof Królczyk --- embassy-net/src/driver_util.rs | 4 ++-- embassy-net/src/lib.rs | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/embassy-net/src/driver_util.rs b/embassy-net/src/driver_util.rs index f51641425..536f4c3d9 100644 --- a/embassy-net/src/driver_util.rs +++ b/embassy-net/src/driver_util.rs @@ -84,7 +84,7 @@ where { self.0.consume(|buf| { #[cfg(feature = "packet-trace")] - trace!("rx: {:?}", buf); + trace!("embassy device rx: {:02x}", buf); f(buf) }) } @@ -105,7 +105,7 @@ where self.0.consume(len, |buf| { let r = f(buf); #[cfg(feature = "packet-trace")] - trace!("tx: {:?}", buf); + trace!("embassy device tx: {:02x}", buf); r }) } diff --git a/embassy-net/src/lib.rs b/embassy-net/src/lib.rs index a7b7efa87..22e7358ac 100644 --- a/embassy-net/src/lib.rs +++ b/embassy-net/src/lib.rs @@ -391,12 +391,12 @@ impl<'d> Stack<'d> { self.with(|i| i.hardware_address) } - /// Get whether the link is up. + /// Check whether the link is up. pub fn is_link_up(&self) -> bool { self.with(|i| i.link_up) } - /// Get whether the network stack has a valid IP configuration. + /// Check whether the network stack has a valid IP configuration. /// This is true if the network stack has a static IP configuration or if DHCP has completed pub fn is_config_up(&self) -> bool { let v4_up; From 9634dfd6c119b054e232a886058d015ede5aec00 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Krzysztof=20Kr=C3=B3lczyk?= Date: Fri, 1 Nov 2024 16:15:15 +0100 Subject: [PATCH 164/361] chore: address some clippy issues MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Krzysztof Królczyk --- embassy-net/src/lib.rs | 36 +++++++++++++++++------------------- embassy-net/src/tcp.rs | 4 ++-- 2 files changed, 19 insertions(+), 21 deletions(-) diff --git a/embassy-net/src/lib.rs b/embassy-net/src/lib.rs index 22e7358ac..ec7f10fdd 100644 --- a/embassy-net/src/lib.rs +++ b/embassy-net/src/lib.rs @@ -33,14 +33,14 @@ pub use embassy_net_driver as driver; use embassy_net_driver::{Driver, LinkState}; use embassy_sync::waitqueue::WakerRegistration; use embassy_time::{Instant, Timer}; -#[allow(unused_imports)] use heapless::Vec; #[cfg(feature = "dns")] pub use smoltcp::config::DNS_MAX_SERVER_COUNT; #[cfg(feature = "multicast")] pub use smoltcp::iface::MulticastError; -#[allow(unused_imports)] -use smoltcp::iface::{Interface, SocketHandle, SocketSet, SocketStorage}; +#[cfg(any(feature = "dns", feature = "dhcpv4"))] +use smoltcp::iface::SocketHandle; +use smoltcp::iface::{Interface, SocketSet, SocketStorage}; use smoltcp::phy::Medium; #[cfg(feature = "dhcpv4")] use smoltcp::socket::dhcpv4::{self, RetryConfig}; @@ -379,11 +379,11 @@ fn to_smoltcp_hardware_address(addr: driver::HardwareAddress) -> (HardwareAddres impl<'d> Stack<'d> { fn with(&self, f: impl FnOnce(&Inner) -> R) -> R { - f(&*self.inner.borrow()) + f(&self.inner.borrow()) } fn with_mut(&self, f: impl FnOnce(&mut Inner) -> R) -> R { - f(&mut *self.inner.borrow_mut()) + f(&mut self.inner.borrow_mut()) } /// Get the hardware address of the network interface. @@ -642,7 +642,7 @@ impl<'d> Stack<'d> { } impl Inner { - #[allow(clippy::absurd_extreme_comparisons, dead_code)] + #[allow(clippy::absurd_extreme_comparisons)] pub fn get_local_port(&mut self) -> u16 { let res = self.next_local_port; self.next_local_port = if res >= LOCAL_PORT_MAX { LOCAL_PORT_MIN } else { res + 1 }; @@ -732,7 +732,7 @@ impl Inner { debug!(" Default gateway: {:?}", config.gateway); unwrap!(addrs.push(IpCidr::Ipv4(config.address)).ok()); - gateway_v4 = config.gateway.into(); + gateway_v4 = config.gateway; #[cfg(feature = "dns")] for s in &config.dns_servers { debug!(" DNS server: {:?}", s); @@ -831,22 +831,19 @@ impl Inner { self.state_waker.wake(); } - #[allow(unused_mut)] - let mut apply_config = false; - #[cfg(feature = "dhcpv4")] if let Some(dhcp_handle) = self.dhcp_socket { let socket = self.sockets.get_mut::(dhcp_handle); - if self.link_up { + let configure = if self.link_up { if old_link_up != self.link_up { socket.reset(); } match socket.poll() { - None => {} + None => false, Some(dhcpv4::Event::Deconfigured) => { self.static_v4 = None; - apply_config = true; + true } Some(dhcpv4::Event::Configured(config)) => { self.static_v4 = Some(StaticConfigV4 { @@ -854,20 +851,21 @@ impl Inner { gateway: config.router, dns_servers: config.dns_servers, }); - apply_config = true; + true } } } else if old_link_up { socket.reset(); self.static_v4 = None; - apply_config = true; + true + } else { + false + }; + if configure { + self.apply_static_config() } } - if apply_config { - self.apply_static_config(); - } - if let Some(poll_at) = self.iface.poll_at(timestamp, &mut self.sockets) { let t = pin!(Timer::at(instant_from_smoltcp(poll_at))); if t.poll(cx).is_ready() { diff --git a/embassy-net/src/tcp.rs b/embassy-net/src/tcp.rs index 150b4b36b..32d374064 100644 --- a/embassy-net/src/tcp.rs +++ b/embassy-net/src/tcp.rs @@ -186,7 +186,7 @@ impl<'a> TcpSocket<'a> { }); Self { - io: TcpIo { stack: stack, handle }, + io: TcpIo { stack, handle }, } } @@ -806,7 +806,7 @@ pub mod client { }; let remote_endpoint = (addr, remote.port()); let mut socket = TcpConnection::new(self.stack, self.state)?; - socket.socket.set_timeout(self.socket_timeout.clone()); + socket.socket.set_timeout(self.socket_timeout); socket .socket .connect(remote_endpoint) From 84def1608fdd098676d957ed5c67ac4baf586963 Mon Sep 17 00:00:00 2001 From: flippette Date: Fri, 1 Nov 2024 23:44:37 +0200 Subject: [PATCH 165/361] Also implement `embedded_io::Write` for `UartTx<'d, T: Instance, Blocking>` --- embassy-rp/src/uart/mod.rs | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/embassy-rp/src/uart/mod.rs b/embassy-rp/src/uart/mod.rs index ad98a46c7..08f20924c 100644 --- a/embassy-rp/src/uart/mod.rs +++ b/embassy-rp/src/uart/mod.rs @@ -1248,6 +1248,20 @@ impl<'d, T: Instance, M: Mode> embedded_hal_nb::serial::Write for UartTx<'d, T, } } +impl<'d, T: Instance> embedded_io::ErrorType for UartTx<'d, T, Blocking> { + type Error = Error; +} + +impl<'d, T: Instance> embedded_io::Write for UartTx<'d, T, Blocking> { + fn write(&mut self, buf: &[u8]) -> Result { + self.blocking_write(buf).map(|_| buf.len()) + } + + fn flush(&mut self) -> Result<(), Self::Error> { + self.blocking_flush() + } +} + impl<'d, T: Instance, M: Mode> embedded_hal_nb::serial::Read for Uart<'d, T, M> { fn read(&mut self) -> Result> { embedded_hal_02::serial::Read::read(&mut self.rx) From 43ff5a9b286c8864d7c623362322a6027980b24d Mon Sep 17 00:00:00 2001 From: dvdsk Date: Sat, 2 Nov 2024 14:00:27 +0100 Subject: [PATCH 166/361] stm32/uart fix leftover DmaUnsynced public api --- embassy-stm32/src/usart/mod.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/embassy-stm32/src/usart/mod.rs b/embassy-stm32/src/usart/mod.rs index aaf7b41e6..333e01e36 100644 --- a/embassy-stm32/src/usart/mod.rs +++ b/embassy-stm32/src/usart/mod.rs @@ -1689,7 +1689,6 @@ impl embedded_hal_nb::serial::Error for Error { Self::Overrun => embedded_hal_nb::serial::ErrorKind::Overrun, Self::Parity => embedded_hal_nb::serial::ErrorKind::Parity, Self::BufferTooLong => embedded_hal_nb::serial::ErrorKind::Other, - Self::DmaUnsynced => embedded_hal_nb::serial::ErrorKind::Other, } } } From 1434d2f97a48f29cbe480a4d306ecf8716b1b2d4 Mon Sep 17 00:00:00 2001 From: Charles Edward Gagnon <76854355+carloskiki@users.noreply.github.com> Date: Sat, 2 Nov 2024 16:03:17 -0400 Subject: [PATCH 167/361] enhanced docs for time driver --- embassy-time-driver/src/lib.rs | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/embassy-time-driver/src/lib.rs b/embassy-time-driver/src/lib.rs index 8000a9dcb..75f7037e3 100644 --- a/embassy-time-driver/src/lib.rs +++ b/embassy-time-driver/src/lib.rs @@ -109,12 +109,20 @@ pub trait Driver: Send + Sync + 'static { /// Try allocating an alarm handle. Returns None if no alarms left. /// Initially the alarm has no callback set, and a null `ctx` pointer. /// + /// The allocated alarm is a reusable resource and can be used multiple times. + /// Once the alarm has fired, it remains allocated and can be set again without needing + /// to be reallocated. + /// /// # Safety /// It is UB to make the alarm fire before setting a callback. unsafe fn allocate_alarm(&self) -> Option; /// Set the callback function to be called when the alarm triggers. /// The callback may be called from any context (interrupt or thread mode). + /// + /// The callback is maintained after the alarm has fired. Callers do not need + /// to set a callback again before setting another alarm, unless they want to + /// change the callback function or context. fn set_alarm_callback(&self, alarm: AlarmHandle, callback: fn(*mut ()), ctx: *mut ()); /// Set an alarm at the given timestamp. From 5c1f9cf99cc0b6d0215357bb4041ce5b936e9095 Mon Sep 17 00:00:00 2001 From: Charles Edward Gagnon Date: Sat, 2 Nov 2024 16:11:53 -0400 Subject: [PATCH 168/361] rustfmt --- embassy-time-driver/src/lib.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/embassy-time-driver/src/lib.rs b/embassy-time-driver/src/lib.rs index 75f7037e3..12f40b9b9 100644 --- a/embassy-time-driver/src/lib.rs +++ b/embassy-time-driver/src/lib.rs @@ -109,8 +109,8 @@ pub trait Driver: Send + Sync + 'static { /// Try allocating an alarm handle. Returns None if no alarms left. /// Initially the alarm has no callback set, and a null `ctx` pointer. /// - /// The allocated alarm is a reusable resource and can be used multiple times. - /// Once the alarm has fired, it remains allocated and can be set again without needing + /// The allocated alarm is a reusable resource and can be used multiple times. + /// Once the alarm has fired, it remains allocated and can be set again without needing /// to be reallocated. /// /// # Safety @@ -120,8 +120,8 @@ pub trait Driver: Send + Sync + 'static { /// Set the callback function to be called when the alarm triggers. /// The callback may be called from any context (interrupt or thread mode). /// - /// The callback is maintained after the alarm has fired. Callers do not need - /// to set a callback again before setting another alarm, unless they want to + /// The callback is maintained after the alarm has fired. Callers do not need + /// to set a callback again before setting another alarm, unless they want to /// change the callback function or context. fn set_alarm_callback(&self, alarm: AlarmHandle, callback: fn(*mut ()), ctx: *mut ()); From 926ae1a1d5eeff34649c0c20804323b32403807f Mon Sep 17 00:00:00 2001 From: Christian Enderle Date: Sun, 3 Nov 2024 10:56:40 +0100 Subject: [PATCH 169/361] low-power: add support for stm32u5 --- embassy-stm32/src/low_power.rs | 6 +++--- embassy-stm32/src/rtc/low_power.rs | 19 ++++++++++++++----- embassy-stm32/src/rtc/mod.rs | 1 + embassy-stm32/src/rtc/v3.rs | 2 ++ 4 files changed, 20 insertions(+), 8 deletions(-) diff --git a/embassy-stm32/src/low_power.rs b/embassy-stm32/src/low_power.rs index 2be1a42b7..a779b8a09 100644 --- a/embassy-stm32/src/low_power.rs +++ b/embassy-stm32/src/low_power.rs @@ -112,10 +112,10 @@ pub enum StopMode { Stop2, } -#[cfg(any(stm32l4, stm32l5))] +#[cfg(any(stm32l4, stm32l5, stm32u5))] use stm32_metapac::pwr::vals::Lpms; -#[cfg(any(stm32l4, stm32l5))] +#[cfg(any(stm32l4, stm32l5, stm32u5))] impl Into for StopMode { fn into(self) -> Lpms { match self { @@ -184,7 +184,7 @@ impl Executor { #[allow(unused_variables)] fn configure_stop(&mut self, stop_mode: StopMode) { - #[cfg(any(stm32l4, stm32l5))] + #[cfg(any(stm32l4, stm32l5, stm32u5))] crate::pac::PWR.cr1().modify(|m| m.set_lpms(stop_mode.into())); #[cfg(stm32h5)] crate::pac::PWR.pmcr().modify(|v| { diff --git a/embassy-stm32/src/rtc/low_power.rs b/embassy-stm32/src/rtc/low_power.rs index 875eaa639..bba359f31 100644 --- a/embassy-stm32/src/rtc/low_power.rs +++ b/embassy-stm32/src/rtc/low_power.rs @@ -65,7 +65,7 @@ pub(crate) enum WakeupPrescaler { Div16 = 16, } -#[cfg(any(stm32f4, stm32l0, stm32g4, stm32l4, stm32l5, stm32wb, stm32h5, stm32g0))] +#[cfg(any(stm32f4, stm32l0, stm32g4, stm32l4, stm32l5, stm32wb, stm32h5, stm32g0, stm32u5))] impl From for crate::pac::rtc::vals::Wucksel { fn from(val: WakeupPrescaler) -> Self { use crate::pac::rtc::vals::Wucksel; @@ -79,7 +79,7 @@ impl From for crate::pac::rtc::vals::Wucksel { } } -#[cfg(any(stm32f4, stm32l0, stm32g4, stm32l4, stm32l5, stm32wb, stm32h5, stm32g0))] +#[cfg(any(stm32f4, stm32l0, stm32g4, stm32l4, stm32l5, stm32wb, stm32h5, stm32g0, stm32u5))] impl From for WakeupPrescaler { fn from(val: crate::pac::rtc::vals::Wucksel) -> Self { use crate::pac::rtc::vals::Wucksel; @@ -219,12 +219,21 @@ impl Rtc { pub(crate) fn enable_wakeup_line(&self) { use crate::interrupt::typelevel::Interrupt; - use crate::pac::EXTI; ::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)); + #[cfg(not(stm32u5))] + { + use crate::pac::EXTI; + 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)); + } + #[cfg(stm32u5)] + { + use crate::pac::RCC; + RCC.srdamr().modify(|w| w.set_rtcapbamen(true)); + RCC.apb3smenr().modify(|w| w.set_rtcapbsmen(true)); + } } } diff --git a/embassy-stm32/src/rtc/mod.rs b/embassy-stm32/src/rtc/mod.rs index fe57cfe66..3722d11ab 100644 --- a/embassy-stm32/src/rtc/mod.rs +++ b/embassy-stm32/src/rtc/mod.rs @@ -285,6 +285,7 @@ trait SealedInstance { const BACKUP_REGISTER_COUNT: usize; #[cfg(feature = "low-power")] + #[cfg(not(stm32u5))] const EXTI_WAKEUP_LINE: usize; #[cfg(feature = "low-power")] diff --git a/embassy-stm32/src/rtc/v3.rs b/embassy-stm32/src/rtc/v3.rs index 02fd5272e..12cb10bc7 100644 --- a/embassy-stm32/src/rtc/v3.rs +++ b/embassy-stm32/src/rtc/v3.rs @@ -140,6 +140,8 @@ impl SealedInstance for crate::peripherals::RTC { } else if #[cfg(any(stm32l5, stm32h5))] { const EXTI_WAKEUP_LINE: usize = 17; type WakeupInterrupt = crate::interrupt::typelevel::RTC; + } else if #[cfg(stm32u5)] { + type WakeupInterrupt = crate::interrupt::typelevel::RTC; } ); From 51f6b813e1a4311ffb4adf2e66ed3effb990d246 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Thu, 24 Oct 2024 13:31:53 +0200 Subject: [PATCH 170/361] nrf: port to chiptool-based `nrf-pac`. --- embassy-net-nrf91/Cargo.toml | 3 +- embassy-net-nrf91/src/lib.rs | 78 ++-- embassy-nrf/Cargo.toml | 55 +-- embassy-nrf/src/buffered_uarte.rs | 157 ++++---- embassy-nrf/src/chips/nrf51.rs | 2 +- embassy-nrf/src/chips/nrf52805.rs | 2 +- embassy-nrf/src/chips/nrf52810.rs | 2 +- embassy-nrf/src/chips/nrf52811.rs | 2 +- embassy-nrf/src/chips/nrf52820.rs | 2 +- embassy-nrf/src/chips/nrf52832.rs | 2 +- embassy-nrf/src/chips/nrf52833.rs | 2 +- embassy-nrf/src/chips/nrf52840.rs | 2 +- embassy-nrf/src/chips/nrf5340_app.rs | 43 ++- embassy-nrf/src/chips/nrf5340_net.rs | 40 +- embassy-nrf/src/chips/nrf9120.rs | 36 +- embassy-nrf/src/chips/nrf9160.rs | 34 +- embassy-nrf/src/egu.rs | 20 +- embassy-nrf/src/gpio.rs | 133 ++++--- embassy-nrf/src/gpiote.rs | 145 +++---- embassy-nrf/src/i2s.rs | 166 ++++---- embassy-nrf/src/lib.rs | 120 +++--- embassy-nrf/src/nvmc.rs | 23 +- embassy-nrf/src/pdm.rs | 183 ++++----- embassy-nrf/src/ppi/dppi.rs | 8 +- embassy-nrf/src/ppi/mod.rs | 37 +- embassy-nrf/src/ppi/ppi.rs | 32 +- embassy-nrf/src/pwm.rs | 263 ++++++------- embassy-nrf/src/qdec.rs | 83 ++-- embassy-nrf/src/qspi.rs | 164 ++++---- embassy-nrf/src/radio/ble.rs | 188 +++++---- embassy-nrf/src/radio/ieee802154.rs | 223 ++++++----- embassy-nrf/src/radio/mod.rs | 19 +- embassy-nrf/src/rng.rs | 28 +- embassy-nrf/src/saadc.rs | 247 ++++++------ embassy-nrf/src/spim.rs | 130 +++---- embassy-nrf/src/spis.rs | 145 +++---- embassy-nrf/src/temp.rs | 22 +- embassy-nrf/src/time_driver.rs | 45 ++- embassy-nrf/src/timer.rs | 80 ++-- embassy-nrf/src/twim.rs | 238 ++++++------ embassy-nrf/src/twis.rs | 362 +++++++++--------- embassy-nrf/src/uarte.rs | 356 ++++++++--------- embassy-nrf/src/usb/mod.rs | 281 ++++++-------- embassy-nrf/src/usb/vbus_detect.rs | 35 +- embassy-nrf/src/wdt.rs | 80 ++-- examples/boot/bootloader/nrf/src/main.rs | 6 +- examples/nrf52840/src/bin/usb_ethernet.rs | 7 +- examples/nrf52840/src/bin/usb_hid_keyboard.rs | 6 +- examples/nrf52840/src/bin/usb_hid_mouse.rs | 7 +- examples/nrf52840/src/bin/usb_serial.rs | 7 +- .../nrf52840/src/bin/usb_serial_multitask.rs | 7 +- .../nrf52840/src/bin/usb_serial_winusb.rs | 7 +- examples/nrf52840/src/bin/wdt.rs | 4 +- tests/nrf/Cargo.toml | 5 + tests/nrf/src/bin/buffered_uart_spam.rs | 12 +- tests/nrf/src/bin/gpio.rs | 2 + tests/nrf/src/bin/spim.rs | 42 ++ tests/nrf/src/common.rs | 15 + 58 files changed, 2154 insertions(+), 2291 deletions(-) create mode 100644 tests/nrf/src/bin/spim.rs diff --git a/embassy-net-nrf91/Cargo.toml b/embassy-net-nrf91/Cargo.toml index 07a0c8886..98356c1ba 100644 --- a/embassy-net-nrf91/Cargo.toml +++ b/embassy-net-nrf91/Cargo.toml @@ -17,7 +17,8 @@ log = [ "dep:log" ] defmt = { version = "0.3", optional = true } log = { version = "0.4.14", optional = true } -nrf9160-pac = { version = "0.12.0" } +nrf-pac = { git = "https://github.com/embassy-rs/nrf-pac", rev = "875a29629cc1c87aae00cfea647a956b3807d8be" } +cortex-m = "0.7.7" embassy-time = { version = "0.3.1", path = "../embassy-time" } embassy-sync = { version = "0.6.0", path = "../embassy-sync"} diff --git a/embassy-net-nrf91/src/lib.rs b/embassy-net-nrf91/src/lib.rs index 60cdc38c6..80d08f7f5 100644 --- a/embassy-net-nrf91/src/lib.rs +++ b/embassy-net-nrf91/src/lib.rs @@ -17,12 +17,12 @@ use core::slice; use core::sync::atomic::{compiler_fence, fence, Ordering}; use core::task::{Poll, Waker}; +use cortex_m::peripheral::NVIC; use embassy_sync::blocking_mutex::raw::NoopRawMutex; use embassy_sync::pipe; use embassy_sync::waitqueue::{AtomicWaker, WakerRegistration}; use heapless::Vec; -use pac::NVIC; -use {embassy_net_driver_channel as ch, nrf9160_pac as pac}; +use {embassy_net_driver_channel as ch, nrf_pac as pac}; const RX_SIZE: usize = 8 * 1024; const TRACE_SIZE: usize = 16 * 1024; @@ -38,11 +38,9 @@ static WAKER: AtomicWaker = AtomicWaker::new(); /// Call this function on IPC IRQ pub fn on_ipc_irq() { - let ipc = unsafe { &*pac::IPC_NS::ptr() }; - trace!("irq"); - ipc.inten.write(|w| w); + pac::IPC_NS.inten().write(|_| ()); WAKER.wake(); } @@ -135,22 +133,21 @@ async fn new_internal<'a>( "shmem must be in the lower 128kb of RAM" ); - let spu = unsafe { &*pac::SPU_S::ptr() }; + let spu = pac::SPU_S; debug!("Setting IPC RAM as nonsecure..."); let region_start = (shmem_ptr as usize - 0x2000_0000) / SPU_REGION_SIZE; let region_end = region_start + shmem_len / SPU_REGION_SIZE; for i in region_start..region_end { - spu.ramregion[i].perm.write(|w| { - w.execute().set_bit(); - w.write().set_bit(); - w.read().set_bit(); - w.secattr().clear_bit(); - w.lock().clear_bit(); - w + spu.ramregion(i).perm().write(|w| { + w.set_execute(true); + w.set_write(true); + w.set_read(true); + w.set_secattr(false); + w.set_lock(false); }) } - spu.periphid[42].perm.write(|w| w.secattr().non_secure()); + spu.periphid(42).perm().write(|w| w.set_secattr(false)); let mut alloc = Allocator { start: shmem_ptr, @@ -158,8 +155,8 @@ async fn new_internal<'a>( _phantom: PhantomData, }; - let ipc = unsafe { &*pac::IPC_NS::ptr() }; - let power = unsafe { &*pac::POWER_S::ptr() }; + let ipc = pac::IPC_NS; + let power = pac::POWER_S; let cb: &mut ControlBlock = alloc.alloc().write(unsafe { mem::zeroed() }); let rx = alloc.alloc_bytes(RX_SIZE); @@ -177,20 +174,20 @@ async fn new_internal<'a>( cb.trace.base = trace.as_mut_ptr() as _; cb.trace.size = TRACE_SIZE; - ipc.gpmem[0].write(|w| unsafe { w.bits(cb as *mut _ as u32) }); - ipc.gpmem[1].write(|w| unsafe { w.bits(0) }); + ipc.gpmem(0).write_value(cb as *mut _ as u32); + ipc.gpmem(1).write_value(0); // connect task/event i to channel i for i in 0..8 { - ipc.send_cnf[i].write(|w| unsafe { w.bits(1 << i) }); - ipc.receive_cnf[i].write(|w| unsafe { w.bits(1 << i) }); + ipc.send_cnf(i).write(|w| w.0 = 1 << i); + ipc.receive_cnf(i).write(|w| w.0 = 1 << i); } compiler_fence(Ordering::SeqCst); // POWER.LTEMODEM.STARTN = 0 // The reg is missing in the PAC?? - let startn = unsafe { (power as *const _ as *mut u32).add(0x610 / 4) }; + let startn = unsafe { (power.as_ptr() as *mut u32).add(0x610 / 4) }; unsafe { startn.write_volatile(0) } unsafe { NVIC::unmask(pac::Interrupt::IPC) }; @@ -322,15 +319,15 @@ struct StateInner { impl StateInner { fn poll(&mut self, trace_writer: &mut Option>, ch: &mut ch::Runner) { trace!("poll!"); - let ipc = unsafe { &*pac::IPC_NS::ptr() }; + let ipc = pac::IPC_NS; - if ipc.events_receive[0].read().bits() != 0 { - ipc.events_receive[0].reset(); + if ipc.events_receive(0).read() != 0 { + ipc.events_receive(0).write_value(0); trace!("ipc 0"); } - if ipc.events_receive[2].read().bits() != 0 { - ipc.events_receive[2].reset(); + if ipc.events_receive(2).read() != 0 { + ipc.events_receive(2).write_value(0); trace!("ipc 2"); if !self.init { @@ -353,8 +350,8 @@ impl StateInner { } } - if ipc.events_receive[4].read().bits() != 0 { - ipc.events_receive[4].reset(); + if ipc.events_receive(4).read() != 0 { + ipc.events_receive(4).write_value(0); trace!("ipc 4"); loop { @@ -368,13 +365,13 @@ impl StateInner { } } - if ipc.events_receive[6].read().bits() != 0 { - ipc.events_receive[6].reset(); + if ipc.events_receive(6).read() != 0 { + ipc.events_receive(6).write_value(0); trace!("ipc 6"); } - if ipc.events_receive[7].read().bits() != 0 { - ipc.events_receive[7].reset(); + if ipc.events_receive(7).read() != 0 { + ipc.events_receive(7).write_value(0); trace!("ipc 7: trace"); let msg = unsafe { addr_of!((*self.cb).trace.rx_state).read_volatile() }; @@ -437,13 +434,12 @@ impl StateInner { } } - ipc.intenset.write(|w| { - w.receive0().set_bit(); - w.receive2().set_bit(); - w.receive4().set_bit(); - w.receive6().set_bit(); - w.receive7().set_bit(); - w + ipc.intenset().write(|w| { + w.set_receive0(true); + w.set_receive2(true); + w.set_receive4(true); + w.set_receive6(true); + w.set_receive7(true); }); } @@ -546,8 +542,8 @@ impl StateInner { unsafe { addr_of_mut!((*list_item).state).write_volatile((self.tx_seq_no as u32) << 16 | 0x01) } self.tx_seq_no = self.tx_seq_no.wrapping_add(1); - let ipc = unsafe { &*pac::IPC_NS::ptr() }; - ipc.tasks_send[ipc_ch].write(|w| unsafe { w.bits(1) }); + let ipc = pac::IPC_NS; + ipc.tasks_send(ipc_ch).write_value(1); Ok(()) } diff --git a/embassy-nrf/Cargo.toml b/embassy-nrf/Cargo.toml index 3e66d6886..90fa7a16c 100644 --- a/embassy-nrf/Cargo.toml +++ b/embassy-nrf/Cargo.toml @@ -15,7 +15,7 @@ src_base_git = "https://github.com/embassy-rs/embassy/blob/$COMMIT/embassy-nrf/s features = ["time", "defmt", "unstable-pac", "gpiote", "time-driver-rtc1"] flavors = [ - { regex_feature = "nrf51", target = "thumbv6m-none-eabi" }, + { regex_feature = "_nrf51", target = "thumbv6m-none-eabi" }, { regex_feature = "nrf52.*", target = "thumbv7em-none-eabihf" }, { regex_feature = "nrf53.*", target = "thumbv8m.main-none-eabihf" }, { regex_feature = "nrf91.*", target = "thumbv8m.main-none-eabihf" }, @@ -28,20 +28,7 @@ rustdoc-args = ["--cfg", "docsrs"] [features] default = ["rt"] ## Cortex-M runtime (enabled by default) -rt = [ - "nrf51-pac?/rt", - "nrf52805-pac?/rt", - "nrf52810-pac?/rt", - "nrf52811-pac?/rt", - "nrf52820-pac?/rt", - "nrf52832-pac?/rt", - "nrf52833-pac?/rt", - "nrf52840-pac?/rt", - "nrf5340-app-pac?/rt", - "nrf5340-net-pac?/rt", - "nrf9160-pac?/rt", - "nrf9120-pac?/rt", -] +rt = ["nrf-pac/rt"] ## Enable features requiring `embassy-time` time = ["dep:embassy-time"] @@ -75,21 +62,21 @@ qspi-multiwrite-flash = [] #! ### Chip selection features ## nRF51 -nrf51 = ["nrf51-pac", "_nrf51"] +nrf51 = ["nrf-pac/nrf51", "_nrf51"] ## nRF52805 -nrf52805 = ["nrf52805-pac", "_nrf52"] +nrf52805 = ["nrf-pac/nrf52805", "_nrf52"] ## nRF52810 -nrf52810 = ["nrf52810-pac", "_nrf52"] +nrf52810 = ["nrf-pac/nrf52810", "_nrf52"] ## nRF52811 -nrf52811 = ["nrf52811-pac", "_nrf52"] +nrf52811 = ["nrf-pac/nrf52811", "_nrf52"] ## nRF52820 -nrf52820 = ["nrf52820-pac", "_nrf52"] +nrf52820 = ["nrf-pac/nrf52820", "_nrf52"] ## nRF52832 -nrf52832 = ["nrf52832-pac", "_nrf52", "_nrf52832_anomaly_109"] +nrf52832 = ["nrf-pac/nrf52832", "_nrf52", "_nrf52832_anomaly_109"] ## nRF52833 -nrf52833 = ["nrf52833-pac", "_nrf52", "_gpio-p1"] +nrf52833 = ["nrf-pac/nrf52833", "_nrf52", "_gpio-p1"] ## nRF52840 -nrf52840 = ["nrf52840-pac", "_nrf52", "_gpio-p1"] +nrf52840 = ["nrf-pac/nrf52840", "_nrf52", "_gpio-p1"] ## nRF5340 application core in Secure mode nrf5340-app-s = ["_nrf5340-app", "_s"] ## nRF5340 application core in Non-Secure mode @@ -113,11 +100,11 @@ nrf9161-ns = ["nrf9120-ns"] # Features starting with `_` are for internal use only. They're not intended # to be enabled by other crates, and are not covered by semver guarantees. -_nrf5340-app = ["_nrf5340", "nrf5340-app-pac"] -_nrf5340-net = ["_nrf5340", "nrf5340-net-pac"] +_nrf5340-app = ["_nrf5340", "nrf-pac/nrf5340-app"] +_nrf5340-net = ["_nrf5340", "nrf-pac/nrf5340-net"] _nrf5340 = ["_gpio-p1", "_dppi"] -_nrf9160 = ["nrf9160-pac", "_dppi"] -_nrf9120 = ["nrf9120-pac", "_dppi"] +_nrf9160 = ["nrf-pac/nrf9160", "_dppi"] +_nrf9120 = ["nrf-pac/nrf9120", "_dppi"] _nrf52 = ["_ppi"] _nrf51 = ["_ppi"] _nrf91 = [] @@ -149,6 +136,8 @@ embedded-hal-async = { version = "1.0" } embedded-io = { version = "0.6.0" } embedded-io-async = { version = "0.6.1" } +nrf-pac = { git = "https://github.com/embassy-rs/nrf-pac", rev = "875a29629cc1c87aae00cfea647a956b3807d8be" } + defmt = { version = "0.3", optional = true } bitflags = "2.4.2" log = { version = "0.4.14", optional = true } @@ -162,15 +151,3 @@ embedded-storage-async = "0.4.1" cfg-if = "1.0.0" document-features = "0.2.7" -nrf51-pac = { version = "0.12.0", optional = true } -nrf52805-pac = { version = "0.12.0", optional = true } -nrf52810-pac = { version = "0.12.0", optional = true } -nrf52811-pac = { version = "0.12.0", optional = true } -nrf52820-pac = { version = "0.12.0", optional = true } -nrf52832-pac = { version = "0.12.0", optional = true } -nrf52833-pac = { version = "0.12.0", optional = true } -nrf52840-pac = { version = "0.12.0", optional = true } -nrf5340-app-pac = { version = "0.12.0", optional = true } -nrf5340-net-pac = { version = "0.12.0", optional = true } -nrf9160-pac = { version = "0.12.0", optional = true } -nrf9120-pac = { version = "0.12.0", optional = true } diff --git a/embassy-nrf/src/buffered_uarte.rs b/embassy-nrf/src/buffered_uarte.rs index 6d39597c6..b55e70a36 100644 --- a/embassy-nrf/src/buffered_uarte.rs +++ b/embassy-nrf/src/buffered_uarte.rs @@ -17,16 +17,17 @@ use core::task::Poll; use embassy_hal_internal::atomic_ring_buffer::RingBuffer; use embassy_hal_internal::{into_ref, PeripheralRef}; +use pac::uarte::vals; // Re-export SVD variants to allow user to directly set values -pub use pac::uarte0::{baudrate::BAUDRATE_A as Baudrate, config::PARITY_A as Parity}; +pub use pac::uarte::vals::{Baudrate, ConfigParity as Parity}; -use crate::gpio::{AnyPin, Pin as GpioPin, PselBits, SealedPin}; +use crate::gpio::{AnyPin, Pin as GpioPin}; use crate::interrupt::typelevel::Interrupt; use crate::ppi::{ self, AnyConfigurableChannel, AnyGroup, Channel, ConfigurableChannel, Event, Group, Ppi, PpiGroup, Task, }; use crate::timer::{Instance as TimerInstance, Timer}; -use crate::uarte::{configure, drop_tx_rx, Config, Instance as UarteInstance}; +use crate::uarte::{configure, configure_rx_pins, configure_tx_pins, drop_tx_rx, Config, Instance as UarteInstance}; use crate::{interrupt, pac, Peripheral, EASY_DMA_SIZE}; pub(crate) struct State { @@ -79,57 +80,57 @@ impl interrupt::typelevel::Handler for Interrupt let buf_len = s.rx_buf.len(); let half_len = buf_len / 2; - if r.events_error.read().bits() != 0 { - r.events_error.reset(); - let errs = r.errorsrc.read(); - r.errorsrc.write(|w| unsafe { w.bits(errs.bits()) }); + if r.events_error().read() != 0 { + r.events_error().write_value(0); + let errs = r.errorsrc().read(); + r.errorsrc().write_value(errs); - if errs.overrun().bit() { + if errs.overrun() { panic!("BufferedUarte overrun"); } } // Received some bytes, wake task. - if r.inten.read().rxdrdy().bit_is_set() && r.events_rxdrdy.read().bits() != 0 { - r.intenclr.write(|w| w.rxdrdy().clear()); - r.events_rxdrdy.reset(); + if r.inten().read().rxdrdy() && r.events_rxdrdy().read() != 0 { + r.intenclr().write(|w| w.set_rxdrdy(true)); + r.events_rxdrdy().write_value(0); ss.rx_waker.wake(); } - if r.events_endrx.read().bits() != 0 { + if r.events_endrx().read() != 0 { //trace!(" irq_rx: endrx"); - r.events_endrx.reset(); + r.events_endrx().write_value(0); let val = s.rx_ended_count.load(Ordering::Relaxed); s.rx_ended_count.store(val.wrapping_add(1), Ordering::Relaxed); } - if r.events_rxstarted.read().bits() != 0 || !s.rx_started.load(Ordering::Relaxed) { + if r.events_rxstarted().read() != 0 || !s.rx_started.load(Ordering::Relaxed) { //trace!(" irq_rx: rxstarted"); let (ptr, len) = rx.push_buf(); if len >= half_len { - r.events_rxstarted.reset(); + r.events_rxstarted().write_value(0); //trace!(" irq_rx: starting second {:?}", half_len); // Set up the DMA read - r.rxd.ptr.write(|w| unsafe { w.ptr().bits(ptr as u32) }); - r.rxd.maxcnt.write(|w| unsafe { w.maxcnt().bits(half_len as _) }); + r.rxd().ptr().write_value(ptr as u32); + r.rxd().maxcnt().write(|w| w.set_maxcnt(half_len as _)); let chn = s.rx_ppi_ch.load(Ordering::Relaxed); // Enable endrx -> startrx PPI channel. // From this point on, if endrx happens, startrx is automatically fired. - ppi::regs().chenset.write(|w| unsafe { w.bits(1 << chn) }); + ppi::regs().chenset().write(|w| w.0 = 1 << chn); // It is possible that endrx happened BEFORE enabling the PPI. In this case // the PPI channel doesn't trigger, and we'd hang. We have to detect this // and manually start. // check again in case endrx has happened between the last check and now. - if r.events_endrx.read().bits() != 0 { + if r.events_endrx().read() != 0 { //trace!(" irq_rx: endrx"); - r.events_endrx.reset(); + r.events_endrx().write_value(0); let val = s.rx_ended_count.load(Ordering::Relaxed); s.rx_ended_count.store(val.wrapping_add(1), Ordering::Relaxed); @@ -144,7 +145,7 @@ impl interrupt::typelevel::Handler for Interrupt // Check if the PPI channel is still enabled. The PPI channel disables itself // when it fires, so if it's still enabled it hasn't fired. - let ppi_ch_enabled = ppi::regs().chen.read().bits() & (1 << chn) != 0; + let ppi_ch_enabled = ppi::regs().chen().read().ch(chn as _); // if rxend happened, and the ppi channel hasn't fired yet, the rxend got missed. // this condition also naturally matches if `!started`, needed to kickstart the DMA. @@ -152,10 +153,10 @@ impl interrupt::typelevel::Handler for Interrupt //trace!("manually starting."); // disable the ppi ch, it's of no use anymore. - ppi::regs().chenclr.write(|w| unsafe { w.bits(1 << chn) }); + ppi::regs().chenclr().write(|w| w.set_ch(chn as _, true)); // manually start - r.tasks_startrx.write(|w| unsafe { w.bits(1) }); + r.tasks_startrx().write_value(1); } rx.push_done(half_len); @@ -164,7 +165,7 @@ impl interrupt::typelevel::Handler for Interrupt s.rx_started.store(true, Ordering::Relaxed); } else { //trace!(" irq_rx: rxstarted no buf"); - r.intenclr.write(|w| w.rxstarted().clear()); + r.intenclr().write(|w| w.set_rxstarted(true)); } } } @@ -173,8 +174,8 @@ impl interrupt::typelevel::Handler for Interrupt if let Some(mut tx) = unsafe { s.tx_buf.try_reader() } { // TX end - if r.events_endtx.read().bits() != 0 { - r.events_endtx.reset(); + if r.events_endtx().read() != 0 { + r.events_endtx().write_value(0); let n = s.tx_count.load(Ordering::Relaxed); //trace!(" irq_tx: endtx {:?}", n); @@ -192,11 +193,11 @@ impl interrupt::typelevel::Handler for Interrupt s.tx_count.store(len, Ordering::Relaxed); // Set up the DMA write - r.txd.ptr.write(|w| unsafe { w.ptr().bits(ptr as u32) }); - r.txd.maxcnt.write(|w| unsafe { w.maxcnt().bits(len as _) }); + r.txd().ptr().write_value(ptr as u32); + r.txd().maxcnt().write(|w| w.set_maxcnt(len as _)); // Start UARTE Transmit transaction - r.tasks_starttx.write(|w| unsafe { w.bits(1) }); + r.tasks_starttx().write_value(1); } } } @@ -308,7 +309,7 @@ impl<'d, U: UarteInstance, T: TimerInstance> BufferedUarte<'d, U, T> { let tx = BufferedUarteTx::new_innerer(unsafe { peri.clone_unchecked() }, txd, cts, tx_buffer); let rx = BufferedUarteRx::new_innerer(peri, timer, ppi_ch1, ppi_ch2, ppi_group, rxd, rts, rx_buffer); - U::regs().enable.write(|w| w.enable().enabled()); + U::regs().enable().write(|w| w.set_enable(vals::Enable::ENABLED)); U::Interrupt::pend(); unsafe { U::Interrupt::enable() }; @@ -320,7 +321,7 @@ impl<'d, U: UarteInstance, T: TimerInstance> BufferedUarte<'d, U, T> { /// Adjust the baud rate to the provided value. pub fn set_baudrate(&mut self, baudrate: Baudrate) { let r = U::regs(); - r.baudrate.write(|w| w.baudrate().variant(baudrate)); + r.baudrate().write(|w| w.set_baudrate(baudrate)); } /// Split the UART in reader and writer parts. @@ -415,7 +416,7 @@ impl<'d, U: UarteInstance> BufferedUarteTx<'d, U> { let this = Self::new_innerer(peri, txd, cts, tx_buffer); - U::regs().enable.write(|w| w.enable().enabled()); + U::regs().enable().write(|w| w.set_enable(vals::Enable::ENABLED)); U::Interrupt::pend(); unsafe { U::Interrupt::enable() }; @@ -432,14 +433,7 @@ impl<'d, U: UarteInstance> BufferedUarteTx<'d, U> { ) -> Self { let r = U::regs(); - txd.set_high(); - txd.conf().write(|w| w.dir().output().drive().h0h1()); - r.psel.txd.write(|w| unsafe { w.bits(txd.psel_bits()) }); - - if let Some(pin) = &cts { - pin.conf().write(|w| w.input().connect().drive().h0h1()); - } - r.psel.cts.write(|w| unsafe { w.bits(cts.psel_bits()) }); + configure_tx_pins(r, txd, cts); // Initialize state let s = U::buffered_state(); @@ -447,12 +441,11 @@ impl<'d, U: UarteInstance> BufferedUarteTx<'d, U> { let len = tx_buffer.len(); unsafe { s.tx_buf.init(tx_buffer.as_mut_ptr(), len) }; - r.events_txstarted.reset(); + r.events_txstarted().write_value(0); // Enable interrupts - r.intenset.write(|w| { - w.endtx().set(); - w + r.intenset().write(|w| { + w.set_endtx(true); }); Self { _peri: peri } @@ -532,15 +525,14 @@ impl<'a, U: UarteInstance> Drop for BufferedUarteTx<'a, U> { fn drop(&mut self) { let r = U::regs(); - r.intenclr.write(|w| { - w.txdrdy().set_bit(); - w.txstarted().set_bit(); - w.txstopped().set_bit(); - w + r.intenclr().write(|w| { + w.set_txdrdy(true); + w.set_txstarted(true); + w.set_txstopped(true); }); - r.events_txstopped.reset(); - r.tasks_stoptx.write(|w| unsafe { w.bits(1) }); - while r.events_txstopped.read().bits() == 0 {} + r.events_txstopped().write_value(0); + r.tasks_stoptx().write_value(1); + while r.events_txstopped().read() == 0 {} let s = U::buffered_state(); unsafe { s.tx_buf.deinit() } @@ -639,7 +631,7 @@ impl<'d, U: UarteInstance, T: TimerInstance> BufferedUarteRx<'d, U, T> { let this = Self::new_innerer(peri, timer, ppi_ch1, ppi_ch2, ppi_group, rxd, rts, rx_buffer); - U::regs().enable.write(|w| w.enable().enabled()); + U::regs().enable().write(|w| w.set_enable(vals::Enable::ENABLED)); U::Interrupt::pend(); unsafe { U::Interrupt::enable() }; @@ -663,14 +655,7 @@ impl<'d, U: UarteInstance, T: TimerInstance> BufferedUarteRx<'d, U, T> { let r = U::regs(); - rxd.conf().write(|w| w.input().connect().drive().h0h1()); - r.psel.rxd.write(|w| unsafe { w.bits(rxd.psel_bits()) }); - - if let Some(pin) = &rts { - pin.set_high(); - pin.conf().write(|w| w.dir().output().drive().h0h1()); - } - r.psel.rts.write(|w| unsafe { w.bits(rts.psel_bits()) }); + configure_rx_pins(r, rxd, rts); // Initialize state let s = U::buffered_state(); @@ -681,20 +666,19 @@ impl<'d, U: UarteInstance, T: TimerInstance> BufferedUarteRx<'d, U, T> { unsafe { s.rx_buf.init(rx_buffer.as_mut_ptr(), rx_len) }; // clear errors - let errors = r.errorsrc.read().bits(); - r.errorsrc.write(|w| unsafe { w.bits(errors) }); + let errors = r.errorsrc().read(); + r.errorsrc().write_value(errors); - r.events_rxstarted.reset(); - r.events_error.reset(); - r.events_endrx.reset(); + r.events_rxstarted().write_value(0); + r.events_error().write_value(0); + r.events_endrx().write_value(0); // Enable interrupts - r.intenset.write(|w| { - w.endtx().set(); - w.rxstarted().set(); - w.error().set(); - w.endrx().set(); - w + r.intenset().write(|w| { + w.set_endtx(true); + w.set_rxstarted(true); + w.set_error(true); + w.set_endrx(true); }); // Configure byte counter. @@ -704,15 +688,15 @@ impl<'d, U: UarteInstance, T: TimerInstance> BufferedUarteRx<'d, U, T> { timer.clear(); timer.start(); - let mut ppi_ch1 = Ppi::new_one_to_one(ppi_ch1, Event::from_reg(&r.events_rxdrdy), timer.task_count()); + let mut ppi_ch1 = Ppi::new_one_to_one(ppi_ch1, Event::from_reg(r.events_rxdrdy()), timer.task_count()); ppi_ch1.enable(); s.rx_ppi_ch.store(ppi_ch2.number() as u8, Ordering::Relaxed); let mut ppi_group = PpiGroup::new(ppi_group); let mut ppi_ch2 = Ppi::new_one_to_two( ppi_ch2, - Event::from_reg(&r.events_endrx), - Task::from_reg(&r.tasks_startrx), + Event::from_reg(r.events_endrx()), + Task::from_reg(r.tasks_startrx()), ppi_group.task_disable_all(), ); ppi_ch2.disable(); @@ -747,8 +731,8 @@ impl<'d, U: UarteInstance, T: TimerInstance> BufferedUarteRx<'d, U, T> { let ss = U::state(); // Read the RXDRDY counter. - T::regs().tasks_capture[0].write(|w| unsafe { w.bits(1) }); - let mut end = T::regs().cc[0].read().bits() as usize; + T::regs().tasks_capture(0).write_value(1); + let mut end = T::regs().cc(0).read() as usize; //trace!(" rxdrdy count = {:?}", end); // We've set a compare channel that resets the counter to 0 when it reaches `len*2`. @@ -769,7 +753,7 @@ impl<'d, U: UarteInstance, T: TimerInstance> BufferedUarteRx<'d, U, T> { if start == end { //trace!(" empty"); ss.rx_waker.register(cx.waker()); - r.intenset.write(|w| w.rxdrdy().set_bit()); + r.intenset().write(|w| w.set_rxdrdy(true)); return Poll::Pending; } @@ -799,7 +783,7 @@ impl<'d, U: UarteInstance, T: TimerInstance> BufferedUarteRx<'d, U, T> { let s = U::buffered_state(); let mut rx = unsafe { s.rx_buf.reader() }; rx.pop_done(amt); - U::regs().intenset.write(|w| w.rxstarted().set()); + U::regs().intenset().write(|w| w.set_rxstarted(true)); } /// we are ready to read if there is data in the buffer @@ -817,15 +801,14 @@ impl<'a, U: UarteInstance, T: TimerInstance> Drop for BufferedUarteRx<'a, U, T> self.timer.stop(); - r.intenclr.write(|w| { - w.rxdrdy().set_bit(); - w.rxstarted().set_bit(); - w.rxto().set_bit(); - w + r.intenclr().write(|w| { + w.set_rxdrdy(true); + w.set_rxstarted(true); + w.set_rxto(true); }); - r.events_rxto.reset(); - r.tasks_stoprx.write(|w| unsafe { w.bits(1) }); - while r.events_rxto.read().bits() == 0 {} + r.events_rxto().write_value(0); + r.tasks_stoprx().write_value(1); + while r.events_rxto().read() == 0 {} let s = U::buffered_state(); unsafe { s.rx_buf.deinit() } diff --git a/embassy-nrf/src/chips/nrf51.rs b/embassy-nrf/src/chips/nrf51.rs index cc1cbc8a0..95fa926c3 100644 --- a/embassy-nrf/src/chips/nrf51.rs +++ b/embassy-nrf/src/chips/nrf51.rs @@ -1,4 +1,4 @@ -pub use nrf51_pac as pac; +pub use nrf_pac as pac; /// The maximum buffer size that the EasyDMA can send/recv in one operation. pub const EASY_DMA_SIZE: usize = (1 << 14) - 1; diff --git a/embassy-nrf/src/chips/nrf52805.rs b/embassy-nrf/src/chips/nrf52805.rs index b51b0c93e..fc8db856c 100644 --- a/embassy-nrf/src/chips/nrf52805.rs +++ b/embassy-nrf/src/chips/nrf52805.rs @@ -1,4 +1,4 @@ -pub use nrf52805_pac as pac; +pub use nrf_pac as pac; /// The maximum buffer size that the EasyDMA can send/recv in one operation. pub const EASY_DMA_SIZE: usize = (1 << 14) - 1; diff --git a/embassy-nrf/src/chips/nrf52810.rs b/embassy-nrf/src/chips/nrf52810.rs index 273098d9b..11a8b4dde 100644 --- a/embassy-nrf/src/chips/nrf52810.rs +++ b/embassy-nrf/src/chips/nrf52810.rs @@ -1,4 +1,4 @@ -pub use nrf52810_pac as pac; +pub use nrf_pac as pac; /// The maximum buffer size that the EasyDMA can send/recv in one operation. pub const EASY_DMA_SIZE: usize = (1 << 10) - 1; diff --git a/embassy-nrf/src/chips/nrf52811.rs b/embassy-nrf/src/chips/nrf52811.rs index 9bce38636..077a36e31 100644 --- a/embassy-nrf/src/chips/nrf52811.rs +++ b/embassy-nrf/src/chips/nrf52811.rs @@ -1,4 +1,4 @@ -pub use nrf52811_pac as pac; +pub use nrf_pac as pac; /// The maximum buffer size that the EasyDMA can send/recv in one operation. pub const EASY_DMA_SIZE: usize = (1 << 14) - 1; diff --git a/embassy-nrf/src/chips/nrf52820.rs b/embassy-nrf/src/chips/nrf52820.rs index 2acae2c8f..6ee16706d 100644 --- a/embassy-nrf/src/chips/nrf52820.rs +++ b/embassy-nrf/src/chips/nrf52820.rs @@ -1,4 +1,4 @@ -pub use nrf52820_pac as pac; +pub use nrf_pac as pac; /// The maximum buffer size that the EasyDMA can send/recv in one operation. pub const EASY_DMA_SIZE: usize = (1 << 15) - 1; diff --git a/embassy-nrf/src/chips/nrf52832.rs b/embassy-nrf/src/chips/nrf52832.rs index 94b373ab3..4a7a29229 100644 --- a/embassy-nrf/src/chips/nrf52832.rs +++ b/embassy-nrf/src/chips/nrf52832.rs @@ -1,4 +1,4 @@ -pub use nrf52832_pac as pac; +pub use nrf_pac as pac; /// The maximum buffer size that the EasyDMA can send/recv in one operation. pub const EASY_DMA_SIZE: usize = (1 << 8) - 1; diff --git a/embassy-nrf/src/chips/nrf52833.rs b/embassy-nrf/src/chips/nrf52833.rs index 09cde1ac1..6d70b763f 100644 --- a/embassy-nrf/src/chips/nrf52833.rs +++ b/embassy-nrf/src/chips/nrf52833.rs @@ -1,4 +1,4 @@ -pub use nrf52833_pac as pac; +pub use nrf_pac as pac; /// The maximum buffer size that the EasyDMA can send/recv in one operation. pub const EASY_DMA_SIZE: usize = (1 << 16) - 1; diff --git a/embassy-nrf/src/chips/nrf52840.rs b/embassy-nrf/src/chips/nrf52840.rs index 0f3d1b250..b6afbf213 100644 --- a/embassy-nrf/src/chips/nrf52840.rs +++ b/embassy-nrf/src/chips/nrf52840.rs @@ -1,4 +1,4 @@ -pub use nrf52840_pac as pac; +pub use nrf_pac as pac; /// The maximum buffer size that the EasyDMA can send/recv in one operation. pub const EASY_DMA_SIZE: usize = (1 << 16) - 1; diff --git a/embassy-nrf/src/chips/nrf5340_app.rs b/embassy-nrf/src/chips/nrf5340_app.rs index 584f6e43c..43588eef3 100644 --- a/embassy-nrf/src/chips/nrf5340_app.rs +++ b/embassy-nrf/src/chips/nrf5340_app.rs @@ -5,16 +5,17 @@ pub mod pac { // The nRF5340 has a secure and non-secure (NS) mode. // To avoid cfg spam, we remove _ns or _s suffixes here. - pub use nrf5340_app_pac::NVIC_PRIO_BITS; + #[cfg(feature="rt")] + pub use nrf_pac::NVIC_PRIO_BITS; + pub use nrf_pac::{common, shared}; #[cfg(feature="rt")] #[doc(no_inline)] - pub use nrf5340_app_pac::interrupt; + pub use nrf_pac::interrupt; #[doc(no_inline)] - pub use nrf5340_app_pac::{ + pub use nrf_pac::{ Interrupt, - Peripherals, cache_s as cache, cachedata_s as cachedata, @@ -26,11 +27,11 @@ pub mod pac { ctrlap_ns as ctrlap, dcnf_ns as dcnf, dppic_ns as dppic, - egu0_ns as egu0, + egu_ns as egu, ficr_s as ficr, fpu_ns as fpu, - gpiote0_s as gpiote, - i2s0_ns as i2s0, + gpiote_s as gpiote, + i2s_ns as i2s, ipc_ns as ipc, kmu_ns as kmu, lpcomp_ns as lpcomp, @@ -38,36 +39,36 @@ pub mod pac { nfct_ns as nfct, nvmc_ns as nvmc, oscillators_ns as oscillators, - p0_ns as p0, - pdm0_ns as pdm, + gpio_ns as gpio, + pdm_ns as pdm, power_ns as power, - pwm0_ns as pwm0, - qdec0_ns as qdec, + pwm_ns as pwm, + qdec_ns as qdec, qspi_ns as qspi, regulators_ns as regulators, reset_ns as reset, - rtc0_ns as rtc0, + rtc_ns as rtc, saadc_ns as saadc, - spim0_ns as spim0, - spis0_ns as spis0, + spim_ns as spim, + spis_ns as spis, spu_s as spu, tad_s as tad, - timer0_ns as timer0, - twim0_ns as twim0, - twis0_ns as twis0, - uarte0_ns as uarte0, + timer_ns as timer, + twim_ns as twim, + twis_ns as twis, + uarte_ns as uarte, uicr_s as uicr, usbd_ns as usbd, usbregulator_ns as usbregulator, vmc_ns as vmc, - wdt0_ns as wdt0, + wdt_ns as wdt, }; /// Non-Secure mode (NS) peripherals pub mod ns { #[cfg(feature = "nrf5340-app-ns")] #[doc(no_inline)] - pub use nrf5340_app_pac::{ + pub use nrf_pac::{ CLOCK_NS as CLOCK, COMP_NS as COMP, CTRLAP_NS as CTRLAP, @@ -141,7 +142,7 @@ pub mod pac { pub mod s { #[cfg(feature = "nrf5340-app-s")] #[doc(no_inline)] - pub use nrf5340_app_pac::{ + pub use nrf_pac::{ CACHEDATA_S as CACHEDATA, CACHEINFO_S as CACHEINFO, CACHE_S as CACHE, diff --git a/embassy-nrf/src/chips/nrf5340_net.rs b/embassy-nrf/src/chips/nrf5340_net.rs index d772c9a49..00ff5fea6 100644 --- a/embassy-nrf/src/chips/nrf5340_net.rs +++ b/embassy-nrf/src/chips/nrf5340_net.rs @@ -5,16 +5,17 @@ pub mod pac { // The nRF5340 has a secure and non-secure (NS) mode. // To avoid cfg spam, we remove _ns or _s suffixes here. - pub use nrf5340_net_pac::NVIC_PRIO_BITS; + #[cfg(feature="rt")] + pub use nrf_pac::NVIC_PRIO_BITS; + pub use nrf_pac::{common, shared}; #[cfg(feature="rt")] #[doc(no_inline)] - pub use nrf5340_net_pac::interrupt; + pub use nrf_pac::interrupt; #[doc(no_inline)] - pub use nrf5340_net_pac::{ + pub use nrf_pac::{ Interrupt, - Peripherals, aar_ns as aar, acl_ns as acl, @@ -26,25 +27,25 @@ pub mod pac { dcnf_ns as dcnf, dppic_ns as dppic, ecb_ns as ecb, - egu0_ns as egu0, + egu_ns as egu, ficr_ns as ficr, gpiote_ns as gpiote, ipc_ns as ipc, nvmc_ns as nvmc, - p0_ns as p0, + gpio_ns as gpio, power_ns as power, radio_ns as radio, reset_ns as reset, rng_ns as rng, - rtc0_ns as rtc0, - spim0_ns as spim0, - spis0_ns as spis0, - swi0_ns as swi0, + rtc_ns as rtc, + spim_ns as spim, + spis_ns as spis, + swi_ns as swi, temp_ns as temp, - timer0_ns as timer0, - twim0_ns as twim0, - twis0_ns as twis0, - uarte0_ns as uarte0, + timer_ns as timer, + twim_ns as twim, + twis_ns as twis, + uarte_ns as uarte, uicr_ns as uicr, vmc_ns as vmc, vreqctrl_ns as vreqctrl, @@ -54,25 +55,17 @@ pub mod pac { ACL_NS as ACL, APPMUTEX_NS as APPMUTEX, APPMUTEX_S as APPMUTEX_S, - CBP as CBP, CCM_NS as CCM, CLOCK_NS as CLOCK, - CPUID as CPUID, CTI_NS as CTI, CTRLAP_NS as CTRLAP, - DCB as DCB, DCNF_NS as DCNF, DPPIC_NS as DPPIC, - DWT as DWT, ECB_NS as ECB, EGU0_NS as EGU0, FICR_NS as FICR, - FPB as FPB, GPIOTE_NS as GPIOTE, IPC_NS as IPC, - ITM as ITM, - MPU as MPU, - NVIC as NVIC, NVMC_NS as NVMC, P0_NS as P0, P1_NS as P1, @@ -82,19 +75,16 @@ pub mod pac { RNG_NS as RNG, RTC0_NS as RTC0, RTC1_NS as RTC1, - SCB as SCB, SPIM0_NS as SPIM0, SPIS0_NS as SPIS0, SWI0_NS as SWI0, SWI1_NS as SWI1, SWI2_NS as SWI2, SWI3_NS as SWI3, - SYST as SYST, TEMP_NS as TEMP, TIMER0_NS as TIMER0, TIMER1_NS as TIMER1, TIMER2_NS as TIMER2, - TPIU as TPIU, TWIM0_NS as TWIM0, TWIS0_NS as TWIS0, UARTE0_NS as UARTE0, diff --git a/embassy-nrf/src/chips/nrf9120.rs b/embassy-nrf/src/chips/nrf9120.rs index b53510118..b89570dcd 100644 --- a/embassy-nrf/src/chips/nrf9120.rs +++ b/embassy-nrf/src/chips/nrf9120.rs @@ -5,14 +5,16 @@ pub mod pac { // The nRF9120 has a secure and non-secure (NS) mode. // To avoid cfg spam, we remove _ns or _s suffixes here. - pub use nrf9120_pac::NVIC_PRIO_BITS; + #[cfg(feature="rt")] + pub use nrf_pac::NVIC_PRIO_BITS; + pub use nrf_pac::{common, shared}; #[cfg(feature="rt")] #[doc(no_inline)] - pub use nrf9120_pac::interrupt; + pub use nrf_pac::interrupt; #[doc(no_inline)] - pub use nrf9120_pac::{ + pub use nrf_pac::{ Interrupt, cc_host_rgf_s as cc_host_rgf, @@ -20,29 +22,29 @@ pub mod pac { cryptocell_s as cryptocell, ctrl_ap_peri_s as ctrl_ap_peri, dppic_ns as dppic, - egu0_ns as egu0, + egu_ns as egu, ficr_s as ficr, fpu_ns as fpu, - gpiote0_s as gpiote, + gpiote_s as gpiote, i2s_ns as i2s, ipc_ns as ipc, kmu_ns as kmu, nvmc_ns as nvmc, - p0_ns as p0, + gpio_ns as gpio, pdm_ns as pdm, power_ns as power, - pwm0_ns as pwm0, + pwm_ns as pwm, regulators_ns as regulators, - rtc0_ns as rtc0, + rtc_ns as rtc, saadc_ns as saadc, - spim0_ns as spim0, - spis0_ns as spis0, + spim_ns as spim, + spis_ns as spis, spu_s as spu, tad_s as tad, - timer0_ns as timer0, - twim0_ns as twim0, - twis0_ns as twis0, - uarte0_ns as uarte0, + timer_ns as timer, + twim_ns as twim, + twis_ns as twis, + uarte_ns as uarte, uicr_s as uicr, vmc_ns as vmc, wdt_ns as wdt, @@ -51,7 +53,7 @@ pub mod pac { /// Non-Secure mode (NS) peripherals pub mod ns { #[doc(no_inline)] - pub use nrf9120_pac::{ + pub use nrf_pac::{ CLOCK_NS as CLOCK, DPPIC_NS as DPPIC, EGU0_NS as EGU0, @@ -108,7 +110,7 @@ pub mod pac { /// Secure mode (S) peripherals pub mod s { #[doc(no_inline)] - pub use nrf9120_pac::{ + pub use nrf_pac::{ CC_HOST_RGF_S as CC_HOST_RGF, CLOCK_S as CLOCK, CRYPTOCELL_S as CRYPTOCELL, @@ -121,7 +123,7 @@ pub mod pac { EGU4_S as EGU4, EGU5_S as EGU5, FICR_S as FICR, - FPU as FPU, + FPU_NS as FPU, GPIOTE0_S as GPIOTE0, I2S_S as I2S, IPC_S as IPC, diff --git a/embassy-nrf/src/chips/nrf9160.rs b/embassy-nrf/src/chips/nrf9160.rs index 8107ca175..dba3d1ef5 100644 --- a/embassy-nrf/src/chips/nrf9160.rs +++ b/embassy-nrf/src/chips/nrf9160.rs @@ -5,14 +5,16 @@ pub mod pac { // The nRF9160 has a secure and non-secure (NS) mode. // To avoid cfg spam, we remove _ns or _s suffixes here. - pub use nrf9160_pac::NVIC_PRIO_BITS; + #[cfg(feature="rt")] + pub use nrf_pac::NVIC_PRIO_BITS; + pub use nrf_pac::{common, shared}; #[cfg(feature="rt")] #[doc(no_inline)] - pub use nrf9160_pac::interrupt; + pub use nrf_pac::interrupt; #[doc(no_inline)] - pub use nrf9160_pac::{ + pub use nrf_pac::{ Interrupt, cc_host_rgf_s as cc_host_rgf, @@ -20,29 +22,29 @@ pub mod pac { cryptocell_s as cryptocell, ctrl_ap_peri_s as ctrl_ap_peri, dppic_ns as dppic, - egu0_ns as egu0, + egu_ns as egu, ficr_s as ficr, fpu_ns as fpu, - gpiote0_s as gpiote, + gpiote_s as gpiote, i2s_ns as i2s, ipc_ns as ipc, kmu_ns as kmu, nvmc_ns as nvmc, - p0_ns as p0, + gpio_ns as gpio, pdm_ns as pdm, power_ns as power, - pwm0_ns as pwm0, + pwm_ns as pwm, regulators_ns as regulators, - rtc0_ns as rtc0, + rtc_ns as rtc, saadc_ns as saadc, - spim0_ns as spim0, - spis0_ns as spis0, + spim_ns as spim, + spis_ns as spis, spu_s as spu, tad_s as tad, - timer0_ns as timer0, - twim0_ns as twim0, - twis0_ns as twis0, - uarte0_ns as uarte0, + timer_ns as timer, + twim_ns as twim, + twis_ns as twis, + uarte_ns as uarte, uicr_s as uicr, vmc_ns as vmc, wdt_ns as wdt, @@ -51,7 +53,7 @@ pub mod pac { /// Non-Secure mode (NS) peripherals pub mod ns { #[doc(no_inline)] - pub use nrf9160_pac::{ + pub use nrf_pac::{ CLOCK_NS as CLOCK, DPPIC_NS as DPPIC, EGU0_NS as EGU0, @@ -108,7 +110,7 @@ pub mod pac { /// Secure mode (S) peripherals pub mod s { #[doc(no_inline)] - pub use nrf9160_pac::{ + pub use nrf_pac::{ CC_HOST_RGF_S as CC_HOST_RGF, CLOCK_S as CLOCK, CRYPTOCELL_S as CRYPTOCELL, diff --git a/embassy-nrf/src/egu.rs b/embassy-nrf/src/egu.rs index 204446d29..7f9abdac4 100644 --- a/embassy-nrf/src/egu.rs +++ b/embassy-nrf/src/egu.rs @@ -34,7 +34,7 @@ impl<'d, T: Instance> Egu<'d, T> { } pub(crate) trait SealedInstance { - fn regs() -> &'static pac::egu0::RegisterBlock; + fn regs() -> pac::egu::Egu; } /// Basic Egu instance. @@ -47,8 +47,8 @@ pub trait Instance: Peripheral

+ SealedInstance + 'static + Send { macro_rules! impl_egu { ($type:ident, $pac_type:ident, $irq:ident) => { impl crate::egu::SealedInstance for peripherals::$type { - fn regs() -> &'static pac::egu0::RegisterBlock { - unsafe { &*pac::$pac_type::ptr() } + fn regs() -> pac::egu::Egu { + pac::$pac_type } } impl crate::egu::Instance for peripherals::$type { @@ -68,32 +68,26 @@ impl<'d, T: Instance> Trigger<'d, T> { pub fn task(&self) -> Task<'d> { let nr = self.number as usize; let regs = T::regs(); - Task::from_reg(®s.tasks_trigger[nr]) + Task::from_reg(regs.tasks_trigger(nr)) } /// Get event for this trigger to use with PPI. pub fn event(&self) -> Event<'d> { let nr = self.number as usize; let regs = T::regs(); - Event::from_reg(®s.events_triggered[nr]) + Event::from_reg(regs.events_triggered(nr)) } /// Enable interrupts for this trigger pub fn enable_interrupt(&mut self) { let regs = T::regs(); - unsafe { - regs.intenset - .modify(|r, w| w.bits(r.bits() | (1 << self.number as usize))) - }; + regs.intenset().modify(|w| w.set_triggered(self.number as usize, true)); } /// Enable interrupts for this trigger pub fn disable_interrupt(&mut self) { let regs = T::regs(); - unsafe { - regs.intenclr - .modify(|r, w| w.bits(r.bits() | (1 << self.number as usize))) - }; + regs.intenset().modify(|w| w.set_triggered(self.number as usize, false)); } } diff --git a/embassy-nrf/src/gpio.rs b/embassy-nrf/src/gpio.rs index dbc26ea3f..35b0f2e7b 100644 --- a/embassy-nrf/src/gpio.rs +++ b/embassy-nrf/src/gpio.rs @@ -7,14 +7,11 @@ use core::hint::unreachable_unchecked; use cfg_if::cfg_if; use embassy_hal_internal::{impl_peripheral, into_ref, PeripheralRef}; -#[cfg(feature = "nrf51")] +use crate::pac::common::{Reg, RW}; use crate::pac::gpio; -#[cfg(feature = "nrf51")] -use crate::pac::gpio::pin_cnf::{DRIVE_A, PULL_A}; -#[cfg(not(feature = "nrf51"))] -use crate::pac::p0 as gpio; -#[cfg(not(feature = "nrf51"))] -use crate::pac::p0::pin_cnf::{DRIVE_A, PULL_A}; +use crate::pac::gpio::vals; +#[cfg(not(feature = "_nrf51"))] +use crate::pac::shared::{regs::Psel, vals::Connect}; use crate::{pac, Peripheral}; /// A GPIO port with up to 32 pins. @@ -103,7 +100,7 @@ impl From for bool { } /// Drive strength settings for an output pin. -// These numbers match DRIVE_A exactly so hopefully the compiler will unify them. +// These numbers match vals::Drive exactly so hopefully the compiler will unify them. #[derive(Clone, Copy, Debug, PartialEq)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] #[repr(u8)] @@ -188,24 +185,24 @@ impl<'d> Output<'d> { } } -pub(crate) fn convert_drive(drive: OutputDrive) -> DRIVE_A { +pub(crate) fn convert_drive(drive: OutputDrive) -> vals::Drive { match drive { - OutputDrive::Standard => DRIVE_A::S0S1, - OutputDrive::HighDrive0Standard1 => DRIVE_A::H0S1, - OutputDrive::Standard0HighDrive1 => DRIVE_A::S0H1, - OutputDrive::HighDrive => DRIVE_A::H0H1, - OutputDrive::Disconnect0Standard1 => DRIVE_A::D0S1, - OutputDrive::Disconnect0HighDrive1 => DRIVE_A::D0H1, - OutputDrive::Standard0Disconnect1 => DRIVE_A::S0D1, - OutputDrive::HighDrive0Disconnect1 => DRIVE_A::H0D1, + OutputDrive::Standard => vals::Drive::S0S1, + OutputDrive::HighDrive0Standard1 => vals::Drive::H0S1, + OutputDrive::Standard0HighDrive1 => vals::Drive::S0H1, + OutputDrive::HighDrive => vals::Drive::H0H1, + OutputDrive::Disconnect0Standard1 => vals::Drive::D0S1, + OutputDrive::Disconnect0HighDrive1 => vals::Drive::D0H1, + OutputDrive::Standard0Disconnect1 => vals::Drive::S0D1, + OutputDrive::HighDrive0Disconnect1 => vals::Drive::H0D1, } } -fn convert_pull(pull: Pull) -> PULL_A { +fn convert_pull(pull: Pull) -> vals::Pull { match pull { - Pull::None => PULL_A::DISABLED, - Pull::Up => PULL_A::PULLUP, - Pull::Down => PULL_A::PULLDOWN, + Pull::None => vals::Pull::DISABLED, + Pull::Up => vals::Pull::PULLUP, + Pull::Down => vals::Pull::PULLDOWN, } } @@ -234,12 +231,11 @@ impl<'d> Flex<'d> { #[inline] pub fn set_as_input(&mut self, pull: Pull) { self.pin.conf().write(|w| { - w.dir().input(); - w.input().connect(); - w.pull().variant(convert_pull(pull)); - w.drive().s0s1(); - w.sense().disabled(); - w + w.set_dir(vals::Dir::INPUT); + w.set_input(vals::Input::CONNECT); + w.set_pull(convert_pull(pull)); + w.set_drive(vals::Drive::S0S1); + w.set_sense(vals::Sense::DISABLED); }); } @@ -250,12 +246,11 @@ impl<'d> Flex<'d> { #[inline] pub fn set_as_output(&mut self, drive: OutputDrive) { self.pin.conf().write(|w| { - w.dir().output(); - w.input().disconnect(); - w.pull().disabled(); - w.drive().variant(convert_drive(drive)); - w.sense().disabled(); - w + w.set_dir(vals::Dir::OUTPUT); + w.set_input(vals::Input::DISCONNECT); + w.set_pull(vals::Pull::DISABLED); + w.set_drive(convert_drive(drive)); + w.set_sense(vals::Sense::DISABLED); }); } @@ -271,31 +266,30 @@ impl<'d> Flex<'d> { #[inline] pub fn set_as_input_output(&mut self, pull: Pull, drive: OutputDrive) { self.pin.conf().write(|w| { - w.dir().output(); - w.input().connect(); - w.pull().variant(convert_pull(pull)); - w.drive().variant(convert_drive(drive)); - w.sense().disabled(); - w + w.set_dir(vals::Dir::OUTPUT); + w.set_input(vals::Input::CONNECT); + w.set_pull(convert_pull(pull)); + w.set_drive(convert_drive(drive)); + w.set_sense(vals::Sense::DISABLED); }); } /// Put the pin into disconnected mode. #[inline] pub fn set_as_disconnected(&mut self) { - self.pin.conf().reset(); + self.pin.conf().write(|_| ()); } /// Get whether the pin input level is high. #[inline] pub fn is_high(&self) -> bool { - !self.is_low() + self.pin.block().in_().read().pin(self.pin.pin() as _) } /// Get whether the pin input level is low. #[inline] pub fn is_low(&self) -> bool { - self.pin.block().in_.read().bits() & (1 << self.pin.pin()) == 0 + !self.is_high() } /// Get the pin input level. @@ -338,13 +332,13 @@ impl<'d> Flex<'d> { /// Get whether the output level is set to high. #[inline] pub fn is_set_high(&self) -> bool { - !self.is_set_low() + self.pin.block().out().read().pin(self.pin.pin() as _) } /// Get whether the output level is set to low. #[inline] pub fn is_set_low(&self) -> bool { - self.pin.block().out.read().bits() & (1 << self.pin.pin()) == 0 + !self.is_set_high() } /// Get the current output level. @@ -356,7 +350,7 @@ impl<'d> Flex<'d> { impl<'d> Drop for Flex<'d> { fn drop(&mut self) { - self.pin.conf().reset(); + self.pin.conf().write(|_| ()) } } @@ -375,35 +369,33 @@ pub(crate) trait SealedPin { } #[inline] - fn block(&self) -> &gpio::RegisterBlock { - unsafe { - match self.pin_port() / 32 { - #[cfg(feature = "nrf51")] - 0 => &*pac::GPIO::ptr(), - #[cfg(not(feature = "nrf51"))] - 0 => &*pac::P0::ptr(), - #[cfg(feature = "_gpio-p1")] - 1 => &*pac::P1::ptr(), - _ => unreachable_unchecked(), - } + fn block(&self) -> gpio::Gpio { + match self.pin_port() / 32 { + #[cfg(feature = "_nrf51")] + 0 => pac::GPIO, + #[cfg(not(feature = "_nrf51"))] + 0 => pac::P0, + #[cfg(feature = "_gpio-p1")] + 1 => pac::P1, + _ => unsafe { unreachable_unchecked() }, } } #[inline] - fn conf(&self) -> &gpio::PIN_CNF { - &self.block().pin_cnf[self._pin() as usize] + fn conf(&self) -> Reg { + self.block().pin_cnf(self._pin() as usize) } /// Set the output as high. #[inline] fn set_high(&self) { - unsafe { self.block().outset.write(|w| w.bits(1u32 << self._pin())) } + self.block().outset().write(|w| w.set_pin(self._pin() as _, true)) } /// Set the output as low. #[inline] fn set_low(&self) { - unsafe { self.block().outclr.write(|w| w.bits(1u32 << self._pin())) } + self.block().outclr().write(|w| w.set_pin(self._pin() as _, true)) } } @@ -429,8 +421,9 @@ pub trait Pin: Peripheral

+ Into + SealedPin + Sized + 'static /// Peripheral port register value #[inline] - fn psel_bits(&self) -> u32 { - self.pin_port() as u32 + #[cfg(not(feature = "_nrf51"))] + fn psel_bits(&self) -> pac::shared::regs::Psel { + pac::shared::regs::Psel(self.pin_port() as u32) } /// Convert from concrete pin type PX_XX to type erased `AnyPin`. @@ -471,26 +464,30 @@ impl SealedPin for AnyPin { #[cfg(not(feature = "_nrf51"))] pub(crate) trait PselBits { - fn psel_bits(&self) -> u32; + fn psel_bits(&self) -> pac::shared::regs::Psel; } #[cfg(not(feature = "_nrf51"))] impl<'a, P: Pin> PselBits for Option> { #[inline] - fn psel_bits(&self) -> u32 { + fn psel_bits(&self) -> pac::shared::regs::Psel { match self { Some(pin) => pin.psel_bits(), - None => 1u32 << 31, + None => DISCONNECTED, } } } +#[cfg(not(feature = "_nrf51"))] +pub(crate) const DISCONNECTED: Psel = Psel(1 << 31); + +#[cfg(not(feature = "_nrf51"))] #[allow(dead_code)] -pub(crate) fn deconfigure_pin(psel_bits: u32) { - if psel_bits & 0x8000_0000 != 0 { +pub(crate) fn deconfigure_pin(psel: Psel) { + if psel.connect() == Connect::DISCONNECTED { return; } - unsafe { AnyPin::steal(psel_bits as _).conf().reset() } + unsafe { AnyPin::steal(psel.0 as _).conf().write(|_| ()) } } // ==================== diff --git a/embassy-nrf/src/gpiote.rs b/embassy-nrf/src/gpiote.rs index 9d97c7be9..87bb405f4 100644 --- a/embassy-nrf/src/gpiote.rs +++ b/embassy-nrf/src/gpiote.rs @@ -9,10 +9,14 @@ use embassy_sync::waitqueue::AtomicWaker; use crate::gpio::{AnyPin, Flex, Input, Output, Pin as GpioPin, SealedPin as _}; use crate::interrupt::InterruptExt; +#[cfg(not(feature = "_nrf51"))] +use crate::pac::gpio::vals::Detectmode; +use crate::pac::gpio::vals::Sense; +use crate::pac::gpiote::vals::{Mode, Outinit, Polarity}; use crate::ppi::{Event, Task}; use crate::{interrupt, pac, peripherals}; -#[cfg(feature = "nrf51")] +#[cfg(feature = "_nrf51")] /// Amount of GPIOTE channels in the chip. const CHANNEL_COUNT: usize = 4; #[cfg(not(feature = "_nrf51"))] @@ -51,14 +55,14 @@ pub enum OutputChannelPolarity { Toggle, } -fn regs() -> &'static pac::gpiote::RegisterBlock { +fn regs() -> pac::gpiote::Gpiote { cfg_if::cfg_if! { if #[cfg(any(feature="nrf5340-app-s", feature="nrf9160-s", feature="nrf9120-s"))] { - unsafe { &*pac::GPIOTE0::ptr() } + pac::GPIOTE0 } else if #[cfg(any(feature="nrf5340-app-ns", feature="nrf9160-ns", feature="nrf9120-ns"))] { - unsafe { &*pac::GPIOTE1::ptr() } + pac::GPIOTE1 } else { - unsafe { &*pac::GPIOTE::ptr() } + pac::GPIOTE } } } @@ -68,15 +72,15 @@ pub(crate) fn init(irq_prio: crate::interrupt::Priority) { #[cfg(not(feature = "_nrf51"))] { #[cfg(any(feature = "nrf52833", feature = "nrf52840", feature = "_nrf5340"))] - let ports = unsafe { &[&*pac::P0::ptr(), &*pac::P1::ptr()] }; + let ports = &[pac::P0, pac::P1]; #[cfg(not(any(feature = "_nrf51", feature = "nrf52833", feature = "nrf52840", feature = "_nrf5340")))] - let ports = unsafe { &[&*pac::P0::ptr()] }; + let ports = &[pac::P0]; for &p in ports { // Enable latched detection - p.detectmode.write(|w| w.detectmode().ldetect()); + p.detectmode().write(|w| w.set_detectmode(Detectmode::LDETECT)); // Clear latch - p.latch.write(|w| unsafe { w.bits(0xFFFFFFFF) }) + p.latch().write(|w| w.0 = 0xFFFFFFFF) } } @@ -93,7 +97,7 @@ pub(crate) fn init(irq_prio: crate::interrupt::Priority) { unsafe { irq.enable() }; let g = regs(); - g.intenset.write(|w| w.port().set()); + g.intenset().write(|w| w.set_port(true)); } #[cfg(any(feature = "nrf5340-app-s", feature = "nrf9160-s", feature = "nrf9120-s"))] @@ -121,47 +125,47 @@ unsafe fn handle_gpiote_interrupt() { let g = regs(); for i in 0..CHANNEL_COUNT { - if g.events_in[i].read().bits() != 0 { - g.intenclr.write(|w| w.bits(1 << i)); + if g.events_in(i).read() != 0 { + g.intenclr().write(|w| w.0 = 1 << i); CHANNEL_WAKERS[i].wake(); } } - if g.events_port.read().bits() != 0 { - g.events_port.write(|w| w); + if g.events_port().read() != 0 { + g.events_port().write_value(0); #[cfg(any(feature = "nrf52833", feature = "nrf52840", feature = "_nrf5340"))] - let ports = &[&*pac::P0::ptr(), &*pac::P1::ptr()]; + let ports = &[pac::P0, pac::P1]; #[cfg(not(any(feature = "_nrf51", feature = "nrf52833", feature = "nrf52840", feature = "_nrf5340")))] - let ports = &[&*pac::P0::ptr()]; + let ports = &[pac::P0]; #[cfg(feature = "_nrf51")] - let ports = unsafe { &[&*pac::GPIO::ptr()] }; + let ports = &[pac::GPIO]; #[cfg(feature = "_nrf51")] for (port, &p) in ports.iter().enumerate() { - let inp = p.in_.read().bits(); + let inp = p.in_().read(); for pin in 0..32 { - let fired = match p.pin_cnf[pin as usize].read().sense().variant() { - Some(pac::gpio::pin_cnf::SENSE_A::HIGH) => inp & (1 << pin) != 0, - Some(pac::gpio::pin_cnf::SENSE_A::LOW) => inp & (1 << pin) == 0, + let fired = match p.pin_cnf(pin as usize).read().sense() { + Sense::HIGH => inp.pin(pin), + Sense::LOW => !inp.pin(pin), _ => false, }; if fired { PORT_WAKERS[port * 32 + pin as usize].wake(); - p.pin_cnf[pin as usize].modify(|_, w| w.sense().disabled()); + p.pin_cnf(pin as usize).modify(|w| w.set_sense(Sense::DISABLED)); } } } #[cfg(not(feature = "_nrf51"))] for (port, &p) in ports.iter().enumerate() { - let bits = p.latch.read().bits(); + let bits = p.latch().read().0; for pin in BitIter(bits) { - p.pin_cnf[pin as usize].modify(|_, w| w.sense().disabled()); + p.pin_cnf(pin as usize).modify(|w| w.set_sense(Sense::DISABLED)); PORT_WAKERS[port * 32 + pin as usize].wake(); } - p.latch.write(|w| w.bits(bits)); + p.latch().write(|w| w.0 = bits); } } } @@ -194,8 +198,8 @@ impl<'d> Drop for InputChannel<'d> { fn drop(&mut self) { let g = regs(); let num = self.ch.number(); - g.config[num].write(|w| w.mode().disabled()); - g.intenclr.write(|w| unsafe { w.bits(1 << num) }); + g.config(num).write(|w| w.set_mode(Mode::DISABLED)); + g.intenclr().write(|w| w.0 = 1 << num); } } @@ -207,22 +211,23 @@ impl<'d> InputChannel<'d> { let g = regs(); let num = ch.number(); - g.config[num].write(|w| { + g.config(num).write(|w| { + w.set_mode(Mode::EVENT); match polarity { - InputChannelPolarity::HiToLo => w.mode().event().polarity().hi_to_lo(), - InputChannelPolarity::LoToHi => w.mode().event().polarity().lo_to_hi(), - InputChannelPolarity::None => w.mode().event().polarity().none(), - InputChannelPolarity::Toggle => w.mode().event().polarity().toggle(), + InputChannelPolarity::HiToLo => w.set_polarity(Polarity::HI_TO_LO), + InputChannelPolarity::LoToHi => w.set_polarity(Polarity::LO_TO_HI), + InputChannelPolarity::None => w.set_polarity(Polarity::NONE), + InputChannelPolarity::Toggle => w.set_polarity(Polarity::TOGGLE), }; #[cfg(any(feature = "nrf52833", feature = "nrf52840", feature = "_nrf5340"))] - w.port().bit(match pin.pin.pin.port() { + w.set_port(match pin.pin.pin.port() { crate::gpio::Port::Port0 => false, crate::gpio::Port::Port1 => true, }); - unsafe { w.psel().bits(pin.pin.pin.pin()) } + w.set_psel(pin.pin.pin.pin()); }); - g.events_in[num].reset(); + g.events_in(num).write_value(0); InputChannel { ch: ch.map_into(), pin } } @@ -233,13 +238,13 @@ impl<'d> InputChannel<'d> { let num = self.ch.number(); // Enable interrupt - g.events_in[num].reset(); - g.intenset.write(|w| unsafe { w.bits(1 << num) }); + g.events_in(num).write_value(0); + g.intenset().write(|w| w.0 = 1 << num); poll_fn(|cx| { CHANNEL_WAKERS[num].register(cx.waker()); - if g.events_in[num].read().bits() != 0 { + if g.events_in(num).read() != 0 { Poll::Ready(()) } else { Poll::Pending @@ -251,7 +256,7 @@ impl<'d> InputChannel<'d> { /// Returns the IN event, for use with PPI. pub fn event_in(&self) -> Event<'d> { let g = regs(); - Event::from_reg(&g.events_in[self.ch.number()]) + Event::from_reg(g.events_in(self.ch.number())) } } @@ -265,8 +270,8 @@ impl<'d> Drop for OutputChannel<'d> { fn drop(&mut self) { let g = regs(); let num = self.ch.number(); - g.config[num].write(|w| w.mode().disabled()); - g.intenclr.write(|w| unsafe { w.bits(1 << num) }); + g.config(num).write(|w| w.set_mode(Mode::DISABLED)); + g.intenclr().write(|w| w.0 = 1 << num); } } @@ -277,23 +282,23 @@ impl<'d> OutputChannel<'d> { let g = regs(); let num = ch.number(); - g.config[num].write(|w| { - w.mode().task(); + g.config(num).write(|w| { + w.set_mode(Mode::TASK); match pin.is_set_high() { - true => w.outinit().high(), - false => w.outinit().low(), + true => w.set_outinit(Outinit::HIGH), + false => w.set_outinit(Outinit::LOW), }; match polarity { - OutputChannelPolarity::Set => w.polarity().lo_to_hi(), - OutputChannelPolarity::Clear => w.polarity().hi_to_lo(), - OutputChannelPolarity::Toggle => w.polarity().toggle(), + OutputChannelPolarity::Set => w.set_polarity(Polarity::HI_TO_LO), + OutputChannelPolarity::Clear => w.set_polarity(Polarity::LO_TO_HI), + OutputChannelPolarity::Toggle => w.set_polarity(Polarity::TOGGLE), }; #[cfg(any(feature = "nrf52833", feature = "nrf52840", feature = "_nrf5340"))] - w.port().bit(match pin.pin.pin.port() { + w.set_port(match pin.pin.pin.port() { crate::gpio::Port::Port0 => false, crate::gpio::Port::Port1 => true, }); - unsafe { w.psel().bits(pin.pin.pin.pin()) } + w.set_psel(pin.pin.pin.pin()); }); OutputChannel { @@ -305,41 +310,41 @@ impl<'d> OutputChannel<'d> { /// Triggers the OUT task (does the action as configured with task_out_polarity, defaults to Toggle). pub fn out(&self) { let g = regs(); - g.tasks_out[self.ch.number()].write(|w| unsafe { w.bits(1) }); + g.tasks_out(self.ch.number()).write_value(1); } /// Triggers the SET task (set associated pin high). - #[cfg(not(feature = "nrf51"))] + #[cfg(not(feature = "_nrf51"))] pub fn set(&self) { let g = regs(); - g.tasks_set[self.ch.number()].write(|w| unsafe { w.bits(1) }); + g.tasks_set(self.ch.number()).write_value(1); } /// Triggers the CLEAR task (set associated pin low). - #[cfg(not(feature = "nrf51"))] + #[cfg(not(feature = "_nrf51"))] pub fn clear(&self) { let g = regs(); - g.tasks_clr[self.ch.number()].write(|w| unsafe { w.bits(1) }); + g.tasks_clr(self.ch.number()).write_value(1); } /// Returns the OUT task, for use with PPI. pub fn task_out(&self) -> Task<'d> { let g = regs(); - Task::from_reg(&g.tasks_out[self.ch.number()]) + Task::from_reg(g.tasks_out(self.ch.number())) } /// Returns the CLR task, for use with PPI. - #[cfg(not(feature = "nrf51"))] + #[cfg(not(feature = "_nrf51"))] pub fn task_clr(&self) -> Task<'d> { let g = regs(); - Task::from_reg(&g.tasks_clr[self.ch.number()]) + Task::from_reg(g.tasks_clr(self.ch.number())) } /// Returns the SET task, for use with PPI. - #[cfg(not(feature = "nrf51"))] + #[cfg(not(feature = "_nrf51"))] pub fn task_set(&self) -> Task<'d> { let g = regs(); - Task::from_reg(&g.tasks_set[self.ch.number()]) + Task::from_reg(g.tasks_set(self.ch.number())) } } @@ -362,7 +367,7 @@ impl<'a> Unpin for PortInputFuture<'a> {} impl<'a> Drop for PortInputFuture<'a> { fn drop(&mut self) { - self.pin.conf().modify(|_, w| w.sense().disabled()); + self.pin.conf().modify(|w| w.set_sense(Sense::DISABLED)); } } @@ -372,7 +377,7 @@ impl<'a> Future for PortInputFuture<'a> { fn poll(self: core::pin::Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { PORT_WAKERS[self.pin.pin_port() as usize].register(cx.waker()); - if self.pin.conf().read().sense().is_disabled() { + if self.pin.conf().read().sense() == Sense::DISABLED { Poll::Ready(()) } else { Poll::Pending @@ -410,13 +415,13 @@ impl<'d> Input<'d> { impl<'d> Flex<'d> { /// Wait until the pin is high. If it is already high, return immediately. pub async fn wait_for_high(&mut self) { - self.pin.conf().modify(|_, w| w.sense().high()); + self.pin.conf().modify(|w| w.set_sense(Sense::HIGH)); PortInputFuture::new(&mut self.pin).await } /// Wait until the pin is low. If it is already low, return immediately. pub async fn wait_for_low(&mut self) { - self.pin.conf().modify(|_, w| w.sense().low()); + self.pin.conf().modify(|w| w.set_sense(Sense::LOW)); PortInputFuture::new(&mut self.pin).await } @@ -435,9 +440,9 @@ impl<'d> Flex<'d> { /// Wait for the pin to undergo any transition, i.e low to high OR high to low. pub async fn wait_for_any_edge(&mut self) { if self.is_high() { - self.pin.conf().modify(|_, w| w.sense().low()); + self.pin.conf().modify(|w| w.set_sense(Sense::LOW)); } else { - self.pin.conf().modify(|_, w| w.sense().high()); + self.pin.conf().modify(|w| w.set_sense(Sense::HIGH)); } PortInputFuture::new(&mut self.pin).await } @@ -504,13 +509,13 @@ impl_channel!(GPIOTE_CH0, 0); impl_channel!(GPIOTE_CH1, 1); impl_channel!(GPIOTE_CH2, 2); impl_channel!(GPIOTE_CH3, 3); -#[cfg(not(feature = "nrf51"))] +#[cfg(not(feature = "_nrf51"))] impl_channel!(GPIOTE_CH4, 4); -#[cfg(not(feature = "nrf51"))] +#[cfg(not(feature = "_nrf51"))] impl_channel!(GPIOTE_CH5, 5); -#[cfg(not(feature = "nrf51"))] +#[cfg(not(feature = "_nrf51"))] impl_channel!(GPIOTE_CH6, 6); -#[cfg(not(feature = "nrf51"))] +#[cfg(not(feature = "_nrf51"))] impl_channel!(GPIOTE_CH7, 7); // ==================== diff --git a/embassy-nrf/src/i2s.rs b/embassy-nrf/src/i2s.rs index 5f565a9b7..384a1637b 100644 --- a/embassy-nrf/src/i2s.rs +++ b/embassy-nrf/src/i2s.rs @@ -13,11 +13,11 @@ use embassy_hal_internal::drop::OnDrop; use embassy_hal_internal::{into_ref, PeripheralRef}; use embassy_sync::waitqueue::AtomicWaker; -use crate::gpio::{AnyPin, Pin as GpioPin}; +use crate::gpio::{AnyPin, Pin as GpioPin, PselBits}; use crate::interrupt::typelevel::Interrupt; -use crate::pac::i2s::RegisterBlock; +use crate::pac::i2s::vals; use crate::util::slice_in_ram_or; -use crate::{interrupt, Peripheral, EASY_DMA_SIZE}; +use crate::{interrupt, pac, Peripheral, EASY_DMA_SIZE}; /// Type alias for `MultiBuffering` with 2 buffers. pub type DoubleBuffering = MultiBuffering; @@ -117,9 +117,20 @@ pub enum MckFreq { } impl MckFreq { - const REGISTER_VALUES: &'static [u32] = &[ - 0x20000000, 0x18000000, 0x16000000, 0x11000000, 0x10000000, 0x0C000000, 0x0B000000, 0x08800000, 0x08400000, - 0x08000000, 0x06000000, 0x04100000, 0x020C0000, + const REGISTER_VALUES: &'static [vals::Mckfreq] = &[ + vals::Mckfreq::_32MDIV8, + vals::Mckfreq::_32MDIV10, + vals::Mckfreq::_32MDIV11, + vals::Mckfreq::_32MDIV15, + vals::Mckfreq::_32MDIV16, + vals::Mckfreq::_32MDIV21, + vals::Mckfreq::_32MDIV23, + vals::Mckfreq::_32MDIV30, + vals::Mckfreq::_32MDIV31, + vals::Mckfreq::_32MDIV32, + vals::Mckfreq::_32MDIV42, + vals::Mckfreq::_32MDIV63, + vals::Mckfreq::_32MDIV125, ]; const FREQUENCIES: &'static [u32] = &[ @@ -128,7 +139,7 @@ impl MckFreq { ]; /// Return the value that needs to be written to the register. - pub fn to_register_value(&self) -> u32 { + pub fn to_register_value(&self) -> vals::Mckfreq { Self::REGISTER_VALUES[usize::from(*self)] } @@ -174,8 +185,8 @@ impl Ratio { const RATIOS: &'static [u32] = &[32, 48, 64, 96, 128, 192, 256, 384, 512]; /// Return the value that needs to be written to the register. - pub fn to_register_value(&self) -> u8 { - usize::from(*self) as u8 + pub fn to_register_value(&self) -> vals::Ratio { + vals::Ratio::from_bits(*self as u8) } /// Return the divisor for this ratio @@ -304,9 +315,9 @@ pub enum SampleWidth { _24bit, } -impl From for u8 { +impl From for vals::Swidth { fn from(variant: SampleWidth) -> Self { - variant as _ + vals::Swidth::from_bits(variant as u8) } } @@ -319,11 +330,11 @@ pub enum Align { Right, } -impl From for bool { +impl From for vals::Align { fn from(variant: Align) -> Self { match variant { - Align::Left => false, - Align::Right => true, + Align::Left => vals::Align::LEFT, + Align::Right => vals::Align::RIGHT, } } } @@ -337,11 +348,11 @@ pub enum Format { Aligned, } -impl From for bool { +impl From for vals::Format { fn from(variant: Format) -> Self { match variant { - Format::I2S => false, - Format::Aligned => true, + Format::I2S => vals::Format::I2S, + Format::Aligned => vals::Format::ALIGNED, } } } @@ -357,9 +368,9 @@ pub enum Channels { MonoRight, } -impl From for u8 { +impl From for vals::Channels { fn from(variant: Channels) -> Self { - variant as _ + vals::Channels::from_bits(variant as u8) } } @@ -506,61 +517,32 @@ impl<'d, T: Instance> I2S<'d, T> { } fn apply_config(&self) { - let c = &T::regs().config; + let c = T::regs().config(); match &self.master_clock { Some(MasterClock { freq, ratio }) => { - c.mode.write(|w| w.mode().master()); - c.mcken.write(|w| w.mcken().enabled()); - c.mckfreq - .write(|w| unsafe { w.mckfreq().bits(freq.to_register_value()) }); - c.ratio.write(|w| unsafe { w.ratio().bits(ratio.to_register_value()) }); + c.mode().write(|w| w.set_mode(vals::Mode::MASTER)); + c.mcken().write(|w| w.set_mcken(true)); + c.mckfreq().write(|w| w.set_mckfreq(freq.to_register_value())); + c.ratio().write(|w| w.set_ratio(ratio.to_register_value())); } None => { - c.mode.write(|w| w.mode().slave()); + c.mode().write(|w| w.set_mode(vals::Mode::SLAVE)); } }; - c.swidth - .write(|w| unsafe { w.swidth().bits(self.config.sample_width.into()) }); - c.align.write(|w| w.align().bit(self.config.align.into())); - c.format.write(|w| w.format().bit(self.config.format.into())); - c.channels - .write(|w| unsafe { w.channels().bits(self.config.channels.into()) }); + c.swidth().write(|w| w.set_swidth(self.config.sample_width.into())); + c.align().write(|w| w.set_align(self.config.align.into())); + c.format().write(|w| w.set_format(self.config.format.into())); + c.channels().write(|w| w.set_channels(self.config.channels.into())); } fn select_pins(&self) { - let psel = &T::regs().psel; - - if let Some(mck) = &self.mck { - psel.mck.write(|w| { - unsafe { w.bits(mck.psel_bits()) }; - w.connect().connected() - }); - } - - psel.sck.write(|w| { - unsafe { w.bits(self.sck.psel_bits()) }; - w.connect().connected() - }); - - psel.lrck.write(|w| { - unsafe { w.bits(self.lrck.psel_bits()) }; - w.connect().connected() - }); - - if let Some(sdin) = &self.sdin { - psel.sdin.write(|w| { - unsafe { w.bits(sdin.psel_bits()) }; - w.connect().connected() - }); - } - - if let Some(sdout) = &self.sdout { - psel.sdout.write(|w| { - unsafe { w.bits(sdout.psel_bits()) }; - w.connect().connected() - }); - } + let psel = T::regs().psel(); + psel.mck().write_value(self.mck.psel_bits()); + psel.sck().write_value(self.sck.psel_bits()); + psel.lrck().write_value(self.lrck.psel_bits()); + psel.sdin().write_value(self.sdin.psel_bits()); + psel.sdout().write_value(self.sdout.psel_bits()); } fn setup_interrupt(&self) { @@ -888,7 +870,7 @@ impl<'d, T: Instance, S: Sample, const NB: usize, const NS: usize> FullDuplexStr } /// Helper encapsulating common I2S device operations. -struct Device(&'static RegisterBlock, PhantomData); +struct Device(pac::i2s::I2s, PhantomData); impl Device { fn new() -> Self { @@ -898,132 +880,132 @@ impl Device { #[inline(always)] pub fn enable(&self) { trace!("ENABLED"); - self.0.enable.write(|w| w.enable().enabled()); + self.0.enable().write(|w| w.set_enable(true)); } #[inline(always)] pub fn disable(&self) { trace!("DISABLED"); - self.0.enable.write(|w| w.enable().disabled()); + self.0.enable().write(|w| w.set_enable(false)); } #[inline(always)] fn enable_tx(&self) { trace!("TX ENABLED"); - self.0.config.txen.write(|w| w.txen().enabled()); + self.0.config().txen().write(|w| w.set_txen(true)); } #[inline(always)] fn disable_tx(&self) { trace!("TX DISABLED"); - self.0.config.txen.write(|w| w.txen().disabled()); + self.0.config().txen().write(|w| w.set_txen(false)); } #[inline(always)] fn enable_rx(&self) { trace!("RX ENABLED"); - self.0.config.rxen.write(|w| w.rxen().enabled()); + self.0.config().rxen().write(|w| w.set_rxen(true)); } #[inline(always)] fn disable_rx(&self) { trace!("RX DISABLED"); - self.0.config.rxen.write(|w| w.rxen().disabled()); + self.0.config().rxen().write(|w| w.set_rxen(false)); } #[inline(always)] fn start(&self) { trace!("START"); - self.0.tasks_start.write(|w| unsafe { w.bits(1) }); + self.0.tasks_start().write_value(1); } #[inline(always)] fn stop(&self) { - self.0.tasks_stop.write(|w| unsafe { w.bits(1) }); + self.0.tasks_stop().write_value(1); } #[inline(always)] fn is_stopped(&self) -> bool { - self.0.events_stopped.read().bits() != 0 + self.0.events_stopped().read() != 0 } #[inline(always)] fn reset_stopped_event(&self) { trace!("STOPPED EVENT: Reset"); - self.0.events_stopped.reset(); + self.0.events_stopped().write_value(0); } #[inline(always)] fn disable_stopped_interrupt(&self) { trace!("STOPPED INTERRUPT: Disabled"); - self.0.intenclr.write(|w| w.stopped().clear()); + self.0.intenclr().write(|w| w.set_stopped(true)); } #[inline(always)] fn enable_stopped_interrupt(&self) { trace!("STOPPED INTERRUPT: Enabled"); - self.0.intenset.write(|w| w.stopped().set()); + self.0.intenset().write(|w| w.set_stopped(true)); } #[inline(always)] fn reset_tx_ptr_event(&self) { trace!("TX PTR EVENT: Reset"); - self.0.events_txptrupd.reset(); + self.0.events_txptrupd().write_value(0); } #[inline(always)] fn reset_rx_ptr_event(&self) { trace!("RX PTR EVENT: Reset"); - self.0.events_rxptrupd.reset(); + self.0.events_rxptrupd().write_value(0); } #[inline(always)] fn disable_tx_ptr_interrupt(&self) { trace!("TX PTR INTERRUPT: Disabled"); - self.0.intenclr.write(|w| w.txptrupd().clear()); + self.0.intenclr().write(|w| w.set_txptrupd(true)); } #[inline(always)] fn disable_rx_ptr_interrupt(&self) { trace!("RX PTR INTERRUPT: Disabled"); - self.0.intenclr.write(|w| w.rxptrupd().clear()); + self.0.intenclr().write(|w| w.set_rxptrupd(true)); } #[inline(always)] fn enable_tx_ptr_interrupt(&self) { trace!("TX PTR INTERRUPT: Enabled"); - self.0.intenset.write(|w| w.txptrupd().set()); + self.0.intenset().write(|w| w.set_txptrupd(true)); } #[inline(always)] fn enable_rx_ptr_interrupt(&self) { trace!("RX PTR INTERRUPT: Enabled"); - self.0.intenset.write(|w| w.rxptrupd().set()); + self.0.intenset().write(|w| w.set_rxptrupd(true)); } #[inline(always)] fn is_tx_ptr_updated(&self) -> bool { - self.0.events_txptrupd.read().bits() != 0 + self.0.events_txptrupd().read() != 0 } #[inline(always)] fn is_rx_ptr_updated(&self) -> bool { - self.0.events_rxptrupd.read().bits() != 0 + self.0.events_rxptrupd().read() != 0 } #[inline] fn update_tx(&self, buffer_ptr: *const [S]) -> Result<(), Error> { let (ptr, maxcnt) = Self::validated_dma_parts(buffer_ptr)?; - self.0.rxtxd.maxcnt.write(|w| unsafe { w.bits(maxcnt) }); - self.0.txd.ptr.write(|w| unsafe { w.ptr().bits(ptr) }); + self.0.rxtxd().maxcnt().write(|w| w.0 = maxcnt); + self.0.txd().ptr().write_value(ptr); Ok(()) } #[inline] fn update_rx(&self, buffer_ptr: *const [S]) -> Result<(), Error> { let (ptr, maxcnt) = Self::validated_dma_parts(buffer_ptr)?; - self.0.rxtxd.maxcnt.write(|w| unsafe { w.bits(maxcnt) }); - self.0.rxd.ptr.write(|w| unsafe { w.ptr().bits(ptr) }); + self.0.rxtxd().maxcnt().write(|w| w.0 = maxcnt); + self.0.rxd().ptr().write_value(ptr); Ok(()) } @@ -1160,7 +1142,7 @@ impl State { } pub(crate) trait SealedInstance { - fn regs() -> &'static crate::pac::i2s::RegisterBlock; + fn regs() -> pac::i2s::I2s; fn state() -> &'static State; } @@ -1174,8 +1156,8 @@ pub trait Instance: Peripheral

+ SealedInstance + 'static + Send { macro_rules! impl_i2s { ($type:ident, $pac_type:ident, $irq:ident) => { impl crate::i2s::SealedInstance for peripherals::$type { - fn regs() -> &'static crate::pac::i2s::RegisterBlock { - unsafe { &*pac::$pac_type::ptr() } + fn regs() -> pac::i2s::I2s { + pac::$pac_type } fn state() -> &'static crate::i2s::State { static STATE: crate::i2s::State = crate::i2s::State::new(); diff --git a/embassy-nrf/src/lib.rs b/embassy-nrf/src/lib.rs index bd53664a2..03d3ca5f7 100644 --- a/embassy-nrf/src/lib.rs +++ b/embassy-nrf/src/lib.rs @@ -11,7 +11,7 @@ #![doc = document_features::document_features!(feature_label = r#"{feature}"#)] #[cfg(not(any( - feature = "nrf51", + feature = "_nrf51", feature = "nrf52805", feature = "nrf52810", feature = "nrf52811", @@ -68,7 +68,7 @@ pub(crate) mod util; #[cfg(feature = "_time-driver")] mod time_driver; -#[cfg(not(feature = "nrf51"))] +#[cfg(not(feature = "_nrf51"))] pub mod buffered_uarte; pub mod gpio; #[cfg(feature = "gpiote")] @@ -78,7 +78,7 @@ pub mod gpiote; #[cfg(not(any(feature = "_nrf91", feature = "_nrf5340-app")))] pub mod radio; -#[cfg(not(feature = "nrf51"))] +#[cfg(not(feature = "_nrf51"))] pub mod egu; #[cfg(any(feature = "nrf52832", feature = "nrf52833", feature = "nrf52840"))] pub mod i2s; @@ -95,32 +95,32 @@ pub mod nvmc; pub mod pdm; pub mod ppi; #[cfg(not(any( - feature = "nrf51", + feature = "_nrf51", feature = "nrf52805", feature = "nrf52820", feature = "_nrf5340-net" )))] pub mod pwm; -#[cfg(not(any(feature = "nrf51", feature = "_nrf91", feature = "_nrf5340-net")))] +#[cfg(not(any(feature = "_nrf51", feature = "_nrf91", feature = "_nrf5340-net")))] pub mod qdec; #[cfg(any(feature = "nrf52840", feature = "_nrf5340-app"))] pub mod qspi; #[cfg(not(any(feature = "_nrf5340-app", feature = "_nrf91")))] pub mod rng; -#[cfg(not(any(feature = "nrf51", feature = "nrf52820", feature = "_nrf5340-net")))] +#[cfg(not(any(feature = "_nrf51", feature = "nrf52820", feature = "_nrf5340-net")))] pub mod saadc; -#[cfg(not(feature = "nrf51"))] +#[cfg(not(feature = "_nrf51"))] pub mod spim; -#[cfg(not(feature = "nrf51"))] +#[cfg(not(feature = "_nrf51"))] pub mod spis; #[cfg(not(any(feature = "_nrf5340", feature = "_nrf91")))] pub mod temp; pub mod timer; -#[cfg(not(feature = "nrf51"))] +#[cfg(not(feature = "_nrf51"))] pub mod twim; -#[cfg(not(feature = "nrf51"))] +#[cfg(not(feature = "_nrf51"))] pub mod twis; -#[cfg(not(feature = "nrf51"))] +#[cfg(not(feature = "_nrf51"))] pub mod uarte; #[cfg(any( feature = "_nrf5340-app", @@ -133,7 +133,7 @@ pub mod usb; pub mod wdt; // This mod MUST go last, so that it sees all the `impl_foo!` macros -#[cfg_attr(feature = "nrf51", path = "chips/nrf51.rs")] +#[cfg_attr(feature = "_nrf51", path = "chips/nrf51.rs")] #[cfg_attr(feature = "nrf52805", path = "chips/nrf52805.rs")] #[cfg_attr(feature = "nrf52810", path = "chips/nrf52810.rs")] #[cfg_attr(feature = "nrf52811", path = "chips/nrf52811.rs")] @@ -216,6 +216,7 @@ pub use chip::{peripherals, Peripherals, EASY_DMA_SIZE}; pub use embassy_hal_internal::{into_ref, Peripheral, PeripheralRef}; pub use crate::chip::interrupt; +#[cfg(feature = "rt")] pub use crate::pac::NVIC_PRIO_BITS; pub mod config { @@ -405,7 +406,7 @@ mod consts { pub const APPROTECT_DISABLED: u32 = 0x0000_005a; } -#[cfg(not(feature = "nrf51"))] +#[cfg(not(feature = "_nrf51"))] #[derive(Debug, Copy, Clone, Eq, PartialEq)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] enum WriteResult { @@ -417,12 +418,12 @@ enum WriteResult { Failed, } -#[cfg(not(feature = "nrf51"))] +#[cfg(not(feature = "_nrf51"))] unsafe fn uicr_write(address: *mut u32, value: u32) -> WriteResult { uicr_write_masked(address, value, 0xFFFF_FFFF) } -#[cfg(not(feature = "nrf51"))] +#[cfg(not(feature = "_nrf51"))] unsafe fn uicr_write_masked(address: *mut u32, value: u32, mask: u32) -> WriteResult { let curr_val = address.read_volatile(); if curr_val & mask == value & mask { @@ -434,13 +435,13 @@ unsafe fn uicr_write_masked(address: *mut u32, value: u32, mask: u32) -> WriteRe return WriteResult::Failed; } - let nvmc = &*pac::NVMC::ptr(); - nvmc.config.write(|w| w.wen().wen()); - while nvmc.ready.read().ready().is_busy() {} + let nvmc = pac::NVMC; + nvmc.config().write(|w| w.set_wen(pac::nvmc::vals::Wen::WEN)); + while !nvmc.ready().read().ready() {} address.write_volatile(value | !mask); - while nvmc.ready.read().ready().is_busy() {} - nvmc.config.reset(); - while nvmc.ready.read().ready().is_busy() {} + while !nvmc.ready().read().ready() {} + nvmc.config().write(|_| {}); + while !nvmc.ready().read().ready() {} WriteResult::Written } @@ -459,7 +460,7 @@ pub fn init(config: config::Config) -> Peripherals { let mut needs_reset = false; // Setup debug protection. - #[cfg(not(feature = "nrf51"))] + #[cfg(not(feature = "_nrf51"))] match config.debug { config::Debug::Allowed => { #[cfg(feature = "_nrf52")] @@ -486,17 +487,17 @@ pub fn init(config: config::Config) -> Peripherals { #[cfg(feature = "_nrf5340")] unsafe { - let p = &*pac::CTRLAP::ptr(); + let p = pac::CTRLAP; let res = uicr_write(consts::UICR_APPROTECT, consts::APPROTECT_DISABLED); needs_reset |= res == WriteResult::Written; - p.approtect.disable.write(|w| w.bits(consts::APPROTECT_DISABLED)); + p.approtect().disable().write_value(consts::APPROTECT_DISABLED); #[cfg(feature = "_nrf5340-app")] { let res = uicr_write(consts::UICR_SECUREAPPROTECT, consts::APPROTECT_DISABLED); needs_reset |= res == WriteResult::Written; - p.secureapprotect.disable.write(|w| w.bits(consts::APPROTECT_DISABLED)); + p.secureapprotect().disable().write_value(consts::APPROTECT_DISABLED); } } @@ -576,85 +577,79 @@ pub fn init(config: config::Config) -> Peripherals { cortex_m::peripheral::SCB::sys_reset(); } - let r = unsafe { &*pac::CLOCK::ptr() }; + let r = pac::CLOCK; // Start HFCLK. match config.hfclk_source { config::HfclkSource::Internal => {} config::HfclkSource::ExternalXtal => { // Datasheet says this is likely to take 0.36ms - r.events_hfclkstarted.write(|w| unsafe { w.bits(0) }); - r.tasks_hfclkstart.write(|w| unsafe { w.bits(1) }); - while r.events_hfclkstarted.read().bits() == 0 {} + r.events_hfclkstarted().write_value(0); + r.tasks_hfclkstart().write_value(1); + while r.events_hfclkstarted().read() == 0 {} } } // Configure LFCLK. - #[cfg(not(any(feature = "nrf51", feature = "_nrf5340", feature = "_nrf91")))] + #[cfg(not(any(feature = "_nrf51", feature = "_nrf5340", feature = "_nrf91")))] match config.lfclk_source { - config::LfclkSource::InternalRC => r.lfclksrc.write(|w| w.src().rc()), - config::LfclkSource::Synthesized => r.lfclksrc.write(|w| w.src().synth()), - - config::LfclkSource::ExternalXtal => r.lfclksrc.write(|w| w.src().xtal()), - - config::LfclkSource::ExternalLowSwing => r.lfclksrc.write(|w| { - w.src().xtal(); - w.external().enabled(); - w.bypass().disabled(); - w + config::LfclkSource::InternalRC => r.lfclksrc().write(|w| w.set_src(pac::clock::vals::Lfclksrc::RC)), + config::LfclkSource::Synthesized => r.lfclksrc().write(|w| w.set_src(pac::clock::vals::Lfclksrc::SYNTH)), + config::LfclkSource::ExternalXtal => r.lfclksrc().write(|w| w.set_src(pac::clock::vals::Lfclksrc::XTAL)), + config::LfclkSource::ExternalLowSwing => r.lfclksrc().write(|w| { + w.set_src(pac::clock::vals::Lfclksrc::XTAL); + w.set_external(true); + w.set_bypass(false); }), - config::LfclkSource::ExternalFullSwing => r.lfclksrc.write(|w| { - w.src().xtal(); - w.external().enabled(); - w.bypass().enabled(); - w + config::LfclkSource::ExternalFullSwing => r.lfclksrc().write(|w| { + w.set_src(pac::clock::vals::Lfclksrc::XTAL); + w.set_external(true); + w.set_bypass(true); }), } #[cfg(feature = "_nrf91")] match config.lfclk_source { - config::LfclkSource::InternalRC => r.lfclksrc.write(|w| w.src().lfrc()), - config::LfclkSource::ExternalXtal => r.lfclksrc.write(|w| w.src().lfxo()), + config::LfclkSource::InternalRC => r.lfclksrc().write(|w| w.set_src(pac::clock::vals::Lfclksrc::LFRC)), + config::LfclkSource::ExternalXtal => r.lfclksrc().write(|w| w.set_src(pac::clock::vals::Lfclksrc::LFXO)), } // Start LFCLK. // Datasheet says this could take 100us from synth source // 600us from rc source, 0.25s from an external source. - r.events_lfclkstarted.write(|w| unsafe { w.bits(0) }); - r.tasks_lfclkstart.write(|w| unsafe { w.bits(1) }); - while r.events_lfclkstarted.read().bits() == 0 {} + r.events_lfclkstarted().write_value(0); + r.tasks_lfclkstart().write_value(1); + while r.events_lfclkstarted().read() == 0 {} #[cfg(not(any(feature = "_nrf5340", feature = "_nrf91")))] { // Setup DCDCs. - let pwr = unsafe { &*pac::POWER::ptr() }; #[cfg(feature = "nrf52840")] if config.dcdc.reg0 { - pwr.dcdcen0.write(|w| w.dcdcen().set_bit()); + pac::POWER.dcdcen0().write(|w| w.set_dcdcen(true)); } if config.dcdc.reg1 { - pwr.dcdcen.write(|w| w.dcdcen().set_bit()); + pac::POWER.dcdcen().write(|w| w.set_dcdcen(true)); } } #[cfg(feature = "_nrf91")] { // Setup DCDC. - let reg = unsafe { &*pac::REGULATORS::ptr() }; if config.dcdc.regmain { - reg.dcdcen.write(|w| w.dcdcen().set_bit()); + pac::REGULATORS.dcdcen().write(|w| w.set_dcdcen(true)); } } #[cfg(feature = "_nrf5340-app")] { // Setup DCDC. - let reg = unsafe { &*pac::REGULATORS::ptr() }; + let reg = pac::REGULATORS; if config.dcdc.regh { - reg.vregh.dcdcen.write(|w| w.dcdcen().set_bit()); + reg.vregh().dcdcen().write(|w| w.set_dcdcen(true)); } if config.dcdc.regmain { - reg.vregmain.dcdcen.write(|w| w.dcdcen().set_bit()); + reg.vregmain().dcdcen().write(|w| w.set_dcdcen(true)); } if config.dcdc.regradio { - reg.vregradio.dcdcen.write(|w| w.dcdcen().set_bit()); + reg.vregradio().dcdcen().write(|w| w.set_dcdcen(true)); } } @@ -668,9 +663,10 @@ pub fn init(config: config::Config) -> Peripherals { // Disable UARTE (enabled by default for some reason) #[cfg(feature = "_nrf91")] - unsafe { - (*pac::UARTE0::ptr()).enable.write(|w| w.enable().disabled()); - (*pac::UARTE1::ptr()).enable.write(|w| w.enable().disabled()); + { + use pac::uarte::vals::Enable; + pac::UARTE0.enable().write(|w| w.set_enable(Enable::DISABLED)); + pac::UARTE1.enable().write(|w| w.set_enable(Enable::DISABLED)); } peripherals diff --git a/embassy-nrf/src/nvmc.rs b/embassy-nrf/src/nvmc.rs index 9b17e7da0..6973b4847 100644 --- a/embassy-nrf/src/nvmc.rs +++ b/embassy-nrf/src/nvmc.rs @@ -7,6 +7,7 @@ use embedded_storage::nor_flash::{ ErrorType, MultiwriteNorFlash, NorFlash, NorFlashError, NorFlashErrorKind, ReadNorFlash, }; +use crate::pac::nvmc::vals; use crate::peripherals::NVMC; use crate::{pac, Peripheral}; @@ -51,13 +52,13 @@ impl<'d> Nvmc<'d> { Self { _p } } - fn regs() -> &'static pac::nvmc::RegisterBlock { - unsafe { &*pac::NVMC::ptr() } + fn regs() -> pac::nvmc::Nvmc { + pac::NVMC } fn wait_ready(&mut self) { let p = Self::regs(); - while p.ready.read().ready().is_busy() {} + while !p.ready().read().ready() {} } #[cfg(not(any(feature = "_nrf91", feature = "_nrf5340")))] @@ -68,12 +69,12 @@ impl<'d> Nvmc<'d> { #[cfg(any(feature = "_nrf91", feature = "_nrf5340"))] fn wait_ready_write(&mut self) { let p = Self::regs(); - while p.readynext.read().readynext().is_busy() {} + while !p.readynext().read().readynext() {} } #[cfg(not(any(feature = "_nrf91", feature = "_nrf5340")))] fn erase_page(&mut self, page_addr: u32) { - Self::regs().erasepage().write(|w| unsafe { w.bits(page_addr) }); + Self::regs().erasepage().write_value(page_addr); } #[cfg(any(feature = "_nrf91", feature = "_nrf5340"))] @@ -86,23 +87,23 @@ impl<'d> Nvmc<'d> { fn enable_erase(&self) { #[cfg(not(feature = "_ns"))] - Self::regs().config.write(|w| w.wen().een()); + Self::regs().config().write(|w| w.set_wen(vals::Wen::EEN)); #[cfg(feature = "_ns")] - Self::regs().configns.write(|w| w.wen().een()); + Self::regs().configns().write(|w| w.set_wen(vals::ConfignsWen::EEN)); } fn enable_read(&self) { #[cfg(not(feature = "_ns"))] - Self::regs().config.write(|w| w.wen().ren()); + Self::regs().config().write(|w| w.set_wen(vals::Wen::REN)); #[cfg(feature = "_ns")] - Self::regs().configns.write(|w| w.wen().ren()); + Self::regs().configns().write(|w| w.set_wen(vals::ConfignsWen::REN)); } fn enable_write(&self) { #[cfg(not(feature = "_ns"))] - Self::regs().config.write(|w| w.wen().wen()); + Self::regs().config().write(|w| w.set_wen(vals::Wen::WEN)); #[cfg(feature = "_ns")] - Self::regs().configns.write(|w| w.wen().wen()); + Self::regs().configns().write(|w| w.set_wen(vals::ConfignsWen::WEN)); } } diff --git a/embassy-nrf/src/pdm.rs b/embassy-nrf/src/pdm.rs index 5160fe3c4..483d1a644 100644 --- a/embassy-nrf/src/pdm.rs +++ b/embassy-nrf/src/pdm.rs @@ -13,18 +13,19 @@ use embassy_sync::waitqueue::AtomicWaker; use fixed::types::I7F1; use crate::chip::EASY_DMA_SIZE; -use crate::gpio::{AnyPin, Pin as GpioPin, SealedPin}; +use crate::gpio::{AnyPin, Pin as GpioPin, SealedPin, DISCONNECTED}; use crate::interrupt::typelevel::Interrupt; -use crate::pac::pdm::mode::{EDGE_A, OPERATION_A}; -pub use crate::pac::pdm::pdmclkctrl::FREQ_A as Frequency; +use crate::pac::gpio::vals as gpiovals; +use crate::pac::pdm::vals; +pub use crate::pac::pdm::vals::Freq as Frequency; #[cfg(any( feature = "nrf52840", feature = "nrf52833", feature = "_nrf5340-app", feature = "_nrf91", ))] -pub use crate::pac::pdm::ratio::RATIO_A as Ratio; -use crate::{interrupt, Peripheral}; +pub use crate::pac::pdm::vals::Ratio; +use crate::{interrupt, pac, Peripheral}; /// Interrupt handler pub struct InterruptHandler { @@ -35,16 +36,16 @@ impl interrupt::typelevel::Handler for InterruptHandl unsafe fn on_interrupt() { let r = T::regs(); - if r.events_end.read().bits() != 0 { - r.intenclr.write(|w| w.end().clear()); + if r.events_end().read() != 0 { + r.intenclr().write(|w| w.set_end(true)); } - if r.events_started.read().bits() != 0 { - r.intenclr.write(|w| w.started().clear()); + if r.events_started().read() != 0 { + r.intenclr().write(|w| w.set_started(true)); } - if r.events_stopped.read().bits() != 0 { - r.intenclr.write(|w| w.stopped().clear()); + if r.events_stopped().read() != 0 { + r.intenclr().write(|w| w.set_stopped(true)); } T::state().waker.wake(); @@ -109,50 +110,47 @@ impl<'d, T: Instance> Pdm<'d, T> { let r = T::regs(); // setup gpio pins - din.conf().write(|w| w.input().set_bit()); - r.psel.din.write(|w| unsafe { w.bits(din.psel_bits()) }); + din.conf().write(|w| w.set_input(gpiovals::Input::CONNECT)); + r.psel().din().write_value(din.psel_bits()); clk.set_low(); - clk.conf().write(|w| w.dir().output()); - r.psel.clk.write(|w| unsafe { w.bits(clk.psel_bits()) }); + clk.conf().write(|w| w.set_dir(gpiovals::Dir::OUTPUT)); + r.psel().clk().write_value(clk.psel_bits()); // configure - r.pdmclkctrl.write(|w| w.freq().variant(config.frequency)); + r.pdmclkctrl().write(|w| w.set_freq(config.frequency)); #[cfg(any( feature = "nrf52840", feature = "nrf52833", feature = "_nrf5340-app", feature = "_nrf91", ))] - r.ratio.write(|w| w.ratio().variant(config.ratio)); - r.mode.write(|w| { - w.operation().variant(config.operation_mode.into()); - w.edge().variant(config.edge.into()); - w + r.ratio().write(|w| w.set_ratio(config.ratio)); + r.mode().write(|w| { + w.set_operation(config.operation_mode.into()); + w.set_edge(config.edge.into()); }); Self::_set_gain(r, config.gain_left, config.gain_right); // Disable all events interrupts - r.intenclr.write(|w| unsafe { w.bits(0x003F_FFFF) }); + r.intenclr().write(|w| w.0 = 0x003F_FFFF); // IRQ T::Interrupt::unpend(); unsafe { T::Interrupt::enable() }; - r.enable.write(|w| w.enable().set_bit()); + r.enable().write(|w| w.set_enable(true)); Self { _peri: pdm } } - fn _set_gain(r: &crate::pac::pdm::RegisterBlock, gain_left: I7F1, gain_right: I7F1) { - let gain_to_bits = |gain: I7F1| -> u8 { - let gain = gain.saturating_add(I7F1::from_bits(0x28)).to_bits().clamp(0, 0x50); - unsafe { core::mem::transmute(gain) } + fn _set_gain(r: pac::pdm::Pdm, gain_left: I7F1, gain_right: I7F1) { + let gain_to_bits = |gain: I7F1| -> vals::Gain { + let gain: i8 = gain.saturating_add(I7F1::from_bits(0x28)).to_bits().clamp(0, 0x50); + vals::Gain::from_bits(gain as u8) }; - let gain_left = gain_to_bits(gain_left); - let gain_right = gain_to_bits(gain_right); - r.gainl.write(|w| unsafe { w.gainl().bits(gain_left) }); - r.gainr.write(|w| unsafe { w.gainr().bits(gain_right) }); + r.gainl().write(|w| w.set_gainl(gain_to_bits(gain_left))); + r.gainr().write(|w| w.set_gainr(gain_to_bits(gain_right))); } /// Adjust the gain of the PDM microphone on the fly @@ -166,21 +164,17 @@ impl<'d, T: Instance> Pdm<'d, T> { let r = T::regs(); // start dummy sampling because microphone needs some setup time - r.sample - .ptr - .write(|w| unsafe { w.sampleptr().bits(DUMMY_BUFFER.as_ptr() as u32) }); - r.sample - .maxcnt - .write(|w| unsafe { w.buffsize().bits(DUMMY_BUFFER.len() as _) }); + r.sample().ptr().write_value(DUMMY_BUFFER.as_ptr() as u32); + r.sample().maxcnt().write(|w| w.set_buffsize(DUMMY_BUFFER.len() as _)); - r.tasks_start.write(|w| unsafe { w.bits(1) }); + r.tasks_start().write_value(1); } /// Stop sampling microphone data inta a dummy buffer pub async fn stop(&mut self) { let r = T::regs(); - r.tasks_stop.write(|w| unsafe { w.bits(1) }); - r.events_started.reset(); + r.tasks_stop().write_value(1); + r.events_started().write_value(0); } /// Sample data into the given buffer @@ -194,41 +188,33 @@ impl<'d, T: Instance> Pdm<'d, T> { let r = T::regs(); - if r.events_started.read().bits() == 0 { + if r.events_started().read() == 0 { return Err(Error::NotRunning); } let drop = OnDrop::new(move || { - r.intenclr.write(|w| w.end().clear()); - r.events_stopped.reset(); + r.intenclr().write(|w| w.set_end(true)); + r.events_stopped().write_value(0); // reset to dummy buffer - r.sample - .ptr - .write(|w| unsafe { w.sampleptr().bits(DUMMY_BUFFER.as_ptr() as u32) }); - r.sample - .maxcnt - .write(|w| unsafe { w.buffsize().bits(DUMMY_BUFFER.len() as _) }); + r.sample().ptr().write_value(DUMMY_BUFFER.as_ptr() as u32); + r.sample().maxcnt().write(|w| w.set_buffsize(DUMMY_BUFFER.len() as _)); - while r.events_stopped.read().bits() == 0 {} + while r.events_stopped().read() == 0 {} }); // setup user buffer let ptr = buffer.as_ptr(); let len = buffer.len(); - r.sample.ptr.write(|w| unsafe { w.sampleptr().bits(ptr as u32) }); - r.sample.maxcnt.write(|w| unsafe { w.buffsize().bits(len as _) }); + r.sample().ptr().write_value(ptr as u32); + r.sample().maxcnt().write(|w| w.set_buffsize(len as _)); // wait till the current sample is finished and the user buffer sample is started Self::wait_for_sample().await; // reset the buffer back to the dummy buffer - r.sample - .ptr - .write(|w| unsafe { w.sampleptr().bits(DUMMY_BUFFER.as_ptr() as u32) }); - r.sample - .maxcnt - .write(|w| unsafe { w.buffsize().bits(DUMMY_BUFFER.len() as _) }); + r.sample().ptr().write_value(DUMMY_BUFFER.as_ptr() as u32); + r.sample().maxcnt().write(|w| w.set_buffsize(DUMMY_BUFFER.len() as _)); // wait till the user buffer is sampled Self::wait_for_sample().await; @@ -241,14 +227,14 @@ impl<'d, T: Instance> Pdm<'d, T> { async fn wait_for_sample() { let r = T::regs(); - r.events_end.reset(); - r.intenset.write(|w| w.end().set()); + r.events_end().write_value(0); + r.intenset().write(|w| w.set_end(true)); compiler_fence(Ordering::SeqCst); poll_fn(|cx| { T::state().waker.register(cx.waker()); - if r.events_end.read().bits() != 0 { + if r.events_end().read() != 0 { return Poll::Ready(()); } Poll::Pending @@ -279,40 +265,37 @@ impl<'d, T: Instance> Pdm<'d, T> { { let r = T::regs(); - if r.events_started.read().bits() != 0 { + if r.events_started().read() != 0 { return Err(Error::AlreadyRunning); } - r.sample - .ptr - .write(|w| unsafe { w.sampleptr().bits(bufs[0].as_mut_ptr() as u32) }); - r.sample.maxcnt.write(|w| unsafe { w.buffsize().bits(N as _) }); + r.sample().ptr().write_value(bufs[0].as_mut_ptr() as u32); + r.sample().maxcnt().write(|w| w.set_buffsize(N as _)); // Reset and enable the events - r.events_end.reset(); - r.events_started.reset(); - r.events_stopped.reset(); - r.intenset.write(|w| { - w.end().set(); - w.started().set(); - w.stopped().set(); - w + r.events_end().write_value(0); + r.events_started().write_value(0); + r.events_stopped().write_value(0); + r.intenset().write(|w| { + w.set_end(true); + w.set_started(true); + w.set_stopped(true); }); // Don't reorder the start event before the previous writes. Hopefully self // wouldn't happen anyway compiler_fence(Ordering::SeqCst); - r.tasks_start.write(|w| unsafe { w.bits(1) }); + r.tasks_start().write_value(1); let mut current_buffer = 0; let mut done = false; let drop = OnDrop::new(|| { - r.tasks_stop.write(|w| unsafe { w.bits(1) }); + r.tasks_stop().write_value(1); // N.B. It would be better if this were async, but Drop only support sync code - while r.events_stopped.read().bits() != 0 {} + while r.events_stopped().read() != 0 {} }); // Wait for events and complete when the sampler indicates it has had enough @@ -321,11 +304,11 @@ impl<'d, T: Instance> Pdm<'d, T> { T::state().waker.register(cx.waker()); - if r.events_end.read().bits() != 0 { + if r.events_end().read() != 0 { compiler_fence(Ordering::SeqCst); - r.events_end.reset(); - r.intenset.write(|w| w.end().set()); + r.events_end().write_value(0); + r.intenset().write(|w| w.set_end(true)); if !done { // Discard the last buffer after the user requested a stop @@ -333,23 +316,21 @@ impl<'d, T: Instance> Pdm<'d, T> { let next_buffer = 1 - current_buffer; current_buffer = next_buffer; } else { - r.tasks_stop.write(|w| unsafe { w.bits(1) }); + r.tasks_stop().write_value(1); done = true; }; }; } - if r.events_started.read().bits() != 0 { - r.events_started.reset(); - r.intenset.write(|w| w.started().set()); + if r.events_started().read() != 0 { + r.events_started().write_value(0); + r.intenset().write(|w| w.set_started(true)); let next_buffer = 1 - current_buffer; - r.sample - .ptr - .write(|w| unsafe { w.sampleptr().bits(bufs[next_buffer].as_mut_ptr() as u32) }); + r.sample().ptr().write_value(bufs[next_buffer].as_mut_ptr() as u32); } - if r.events_stopped.read().bits() != 0 { + if r.events_stopped().read() != 0 { return Poll::Ready(()); } @@ -411,11 +392,11 @@ pub enum OperationMode { Stereo, } -impl From for OPERATION_A { +impl From for vals::Operation { fn from(mode: OperationMode) -> Self { match mode { - OperationMode::Mono => OPERATION_A::MONO, - OperationMode::Stereo => OPERATION_A::STEREO, + OperationMode::Mono => vals::Operation::MONO, + OperationMode::Stereo => vals::Operation::STEREO, } } } @@ -429,11 +410,11 @@ pub enum Edge { LeftFalling, } -impl From for EDGE_A { +impl From for vals::Edge { fn from(edge: Edge) -> Self { match edge { - Edge::LeftRising => EDGE_A::LEFT_RISING, - Edge::LeftFalling => EDGE_A::LEFT_FALLING, + Edge::LeftRising => vals::Edge::LEFT_RISING, + Edge::LeftFalling => vals::Edge::LEFT_FALLING, } } } @@ -442,12 +423,12 @@ impl<'d, T: Instance> Drop for Pdm<'d, T> { fn drop(&mut self) { let r = T::regs(); - r.tasks_stop.write(|w| unsafe { w.bits(1) }); + r.tasks_stop().write_value(1); - r.enable.write(|w| w.enable().disabled()); + r.enable().write(|w| w.set_enable(false)); - r.psel.din.reset(); - r.psel.clk.reset(); + r.psel().din().write_value(DISCONNECTED); + r.psel().clk().write_value(DISCONNECTED); } } @@ -465,7 +446,7 @@ impl State { } pub(crate) trait SealedInstance { - fn regs() -> &'static crate::pac::pdm::RegisterBlock; + fn regs() -> crate::pac::pdm::Pdm; fn state() -> &'static State; } @@ -479,8 +460,8 @@ pub trait Instance: Peripheral

+ SealedInstance + 'static + Send { macro_rules! impl_pdm { ($type:ident, $pac_type:ident, $irq:ident) => { impl crate::pdm::SealedInstance for peripherals::$type { - fn regs() -> &'static crate::pac::pdm::RegisterBlock { - unsafe { &*pac::$pac_type::ptr() } + fn regs() -> crate::pac::pdm::Pdm { + pac::$pac_type } fn state() -> &'static crate::pdm::State { static STATE: crate::pdm::State = crate::pdm::State::new(); diff --git a/embassy-nrf/src/ppi/dppi.rs b/embassy-nrf/src/ppi/dppi.rs index 0bc7f821e..3c7b96df7 100644 --- a/embassy-nrf/src/ppi/dppi.rs +++ b/embassy-nrf/src/ppi/dppi.rs @@ -6,8 +6,8 @@ use crate::{pac, Peripheral}; const DPPI_ENABLE_BIT: u32 = 0x8000_0000; const DPPI_CHANNEL_MASK: u32 = 0x0000_00FF; -pub(crate) fn regs() -> &'static pac::dppic::RegisterBlock { - unsafe { &*pac::DPPIC::ptr() } +pub(crate) fn regs() -> pac::dppic::Dppic { + pac::DPPIC } impl<'d, C: ConfigurableChannel> Ppi<'d, C, 1, 1> { @@ -57,13 +57,13 @@ impl<'d, C: Channel, const EVENT_COUNT: usize, const TASK_COUNT: usize> Ppi<'d, /// Enables the channel. pub fn enable(&mut self) { let n = self.ch.number(); - regs().chenset.write(|w| unsafe { w.bits(1 << n) }); + regs().chenset().write(|w| w.0 = 1 << n); } /// Disables the channel. pub fn disable(&mut self) { let n = self.ch.number(); - regs().chenclr.write(|w| unsafe { w.bits(1 << n) }); + regs().chenclr().write(|w| w.0 = 1 << n); } } diff --git a/embassy-nrf/src/ppi/mod.rs b/embassy-nrf/src/ppi/mod.rs index 13f7dcc83..325e4ce00 100644 --- a/embassy-nrf/src/ppi/mod.rs +++ b/embassy-nrf/src/ppi/mod.rs @@ -20,6 +20,7 @@ use core::ptr::NonNull; use embassy_hal_internal::{impl_peripheral, into_ref, PeripheralRef}; +use crate::pac::common::{Reg, RW, W}; use crate::{peripherals, Peripheral}; #[cfg_attr(feature = "_dppi", path = "dppi.rs")] @@ -50,7 +51,7 @@ impl<'d, G: Group> PpiGroup<'d, G> { let r = regs(); let n = g.number(); - r.chg[n].write(|w| unsafe { w.bits(0) }); + r.chg(n).write(|_| ()); Self { g } } @@ -65,7 +66,7 @@ impl<'d, G: Group> PpiGroup<'d, G> { let r = regs(); let ng = self.g.number(); let nc = ch.ch.number(); - r.chg[ng].modify(|r, w| unsafe { w.bits(r.bits() | 1 << nc) }); + r.chg(ng).modify(|w| w.set_ch(nc, true)); } /// Remove a PPI channel from this group. @@ -78,19 +79,19 @@ impl<'d, G: Group> PpiGroup<'d, G> { let r = regs(); let ng = self.g.number(); let nc = ch.ch.number(); - r.chg[ng].modify(|r, w| unsafe { w.bits(r.bits() & !(1 << nc)) }); + r.chg(ng).modify(|w| w.set_ch(nc, false)); } /// Enable all the channels in this group. pub fn enable_all(&mut self) { let n = self.g.number(); - regs().tasks_chg[n].en.write(|w| unsafe { w.bits(1) }); + regs().tasks_chg(n).en().write_value(1); } /// Disable all the channels in this group. pub fn disable_all(&mut self) { let n = self.g.number(); - regs().tasks_chg[n].dis.write(|w| unsafe { w.bits(1) }); + regs().tasks_chg(n).dis().write_value(1); } /// Get a reference to the "enable all" task. @@ -98,7 +99,7 @@ impl<'d, G: Group> PpiGroup<'d, G> { /// When triggered, it will enable all the channels in this group. pub fn task_enable_all(&self) -> Task<'d> { let n = self.g.number(); - Task::from_reg(®s().tasks_chg[n].en) + Task::from_reg(regs().tasks_chg(n).en()) } /// Get a reference to the "disable all" task. @@ -106,7 +107,7 @@ impl<'d, G: Group> PpiGroup<'d, G> { /// When triggered, it will disable all the channels in this group. pub fn task_disable_all(&self) -> Task<'d> { let n = self.g.number(); - Task::from_reg(®s().tasks_chg[n].dis) + Task::from_reg(regs().tasks_chg(n).dis()) } } @@ -114,7 +115,7 @@ impl<'d, G: Group> Drop for PpiGroup<'d, G> { fn drop(&mut self) { let r = regs(); let n = self.g.number(); - r.chg[n].write(|w| unsafe { w.bits(0) }); + r.chg(n).write(|_| ()); } } @@ -143,11 +144,8 @@ impl<'d> Task<'d> { unsafe { self.0.as_ptr().write_volatile(1) }; } - pub(crate) fn from_reg(reg: &T) -> Self { - Self( - unsafe { NonNull::new_unchecked(reg as *const _ as *mut _) }, - PhantomData, - ) + pub(crate) fn from_reg(reg: Reg) -> Self { + Self(unsafe { NonNull::new_unchecked(reg.as_ptr()) }, PhantomData) } /// Address of subscription register for this task. @@ -178,11 +176,8 @@ impl<'d> Event<'d> { Self(ptr, PhantomData) } - pub(crate) fn from_reg(reg: &'d T) -> Self { - Self( - unsafe { NonNull::new_unchecked(reg as *const _ as *mut _) }, - PhantomData, - ) + pub(crate) fn from_reg(reg: Reg) -> Self { + Self(unsafe { NonNull::new_unchecked(reg.as_ptr()) }, PhantomData) } /// Describes whether this Event is currently in a triggered state. @@ -284,7 +279,7 @@ impl ConfigurableChannel for AnyConfigurableChannel { } } -#[cfg(not(feature = "nrf51"))] +#[cfg(not(feature = "_nrf51"))] macro_rules! impl_ppi_channel { ($type:ident, $number:expr) => { impl crate::ppi::SealedChannel for peripherals::$type {} @@ -366,7 +361,7 @@ impl_group!(PPI_GROUP0, 0); impl_group!(PPI_GROUP1, 1); impl_group!(PPI_GROUP2, 2); impl_group!(PPI_GROUP3, 3); -#[cfg(not(feature = "nrf51"))] +#[cfg(not(feature = "_nrf51"))] impl_group!(PPI_GROUP4, 4); -#[cfg(not(feature = "nrf51"))] +#[cfg(not(feature = "_nrf51"))] impl_group!(PPI_GROUP5, 5); diff --git a/embassy-nrf/src/ppi/ppi.rs b/embassy-nrf/src/ppi/ppi.rs index 8ff52ece3..a1beb9dcd 100644 --- a/embassy-nrf/src/ppi/ppi.rs +++ b/embassy-nrf/src/ppi/ppi.rs @@ -14,11 +14,11 @@ impl<'d> Event<'d> { } } -pub(crate) fn regs() -> &'static pac::ppi::RegisterBlock { - unsafe { &*pac::PPI::ptr() } +pub(crate) fn regs() -> pac::ppi::Ppi { + pac::PPI } -#[cfg(not(feature = "nrf51"))] // Not for nrf51 because of the fork task +#[cfg(not(feature = "_nrf51"))] // Not for nrf51 because of the fork task impl<'d, C: super::StaticChannel> Ppi<'d, C, 0, 1> { /// Configure PPI channel to trigger `task`. pub fn new_zero_to_one(ch: impl Peripheral

+ 'd, task: Task) -> Self { @@ -26,7 +26,7 @@ impl<'d, C: super::StaticChannel> Ppi<'d, C, 0, 1> { let r = regs(); let n = ch.number(); - r.fork[n].tep.write(|w| unsafe { w.bits(task.reg_val()) }); + r.fork(n).tep().write_value(task.reg_val()); Self { ch } } @@ -39,14 +39,14 @@ impl<'d, C: ConfigurableChannel> Ppi<'d, C, 1, 1> { let r = regs(); let n = ch.number(); - r.ch[n].eep.write(|w| unsafe { w.bits(event.reg_val()) }); - r.ch[n].tep.write(|w| unsafe { w.bits(task.reg_val()) }); + r.ch(n).eep().write_value(event.reg_val()); + r.ch(n).tep().write_value(task.reg_val()); Self { ch } } } -#[cfg(not(feature = "nrf51"))] // Not for nrf51 because of the fork task +#[cfg(not(feature = "_nrf51"))] // Not for nrf51 because of the fork task impl<'d, C: ConfigurableChannel> Ppi<'d, C, 1, 2> { /// Configure PPI channel to trigger both `task1` and `task2` on `event`. pub fn new_one_to_two(ch: impl Peripheral

+ 'd, event: Event<'d>, task1: Task<'d>, task2: Task<'d>) -> Self { @@ -54,9 +54,9 @@ impl<'d, C: ConfigurableChannel> Ppi<'d, C, 1, 2> { let r = regs(); let n = ch.number(); - r.ch[n].eep.write(|w| unsafe { w.bits(event.reg_val()) }); - r.ch[n].tep.write(|w| unsafe { w.bits(task1.reg_val()) }); - r.fork[n].tep.write(|w| unsafe { w.bits(task2.reg_val()) }); + r.ch(n).eep().write_value(event.reg_val()); + r.ch(n).tep().write_value(task1.reg_val()); + r.fork(n).tep().write_value(task2.reg_val()); Self { ch } } @@ -66,13 +66,13 @@ impl<'d, C: Channel, const EVENT_COUNT: usize, const TASK_COUNT: usize> Ppi<'d, /// Enables the channel. pub fn enable(&mut self) { let n = self.ch.number(); - regs().chenset.write(|w| unsafe { w.bits(1 << n) }); + regs().chenset().write(|w| w.set_ch(n, true)); } /// Disables the channel. pub fn disable(&mut self) { let n = self.ch.number(); - regs().chenclr.write(|w| unsafe { w.bits(1 << n) }); + regs().chenclr().write(|w| w.set_ch(n, true)); } } @@ -82,9 +82,9 @@ impl<'d, C: Channel, const EVENT_COUNT: usize, const TASK_COUNT: usize> Drop for let r = regs(); let n = self.ch.number(); - r.ch[n].eep.write(|w| unsafe { w.bits(0) }); - r.ch[n].tep.write(|w| unsafe { w.bits(0) }); - #[cfg(not(feature = "nrf51"))] - r.fork[n].tep.write(|w| unsafe { w.bits(0) }); + r.ch(n).eep().write_value(0); + r.ch(n).tep().write_value(0); + #[cfg(not(feature = "_nrf51"))] + r.fork(n).tep().write_value(0); } } diff --git a/embassy-nrf/src/pwm.rs b/embassy-nrf/src/pwm.rs index 8e8f166d7..c8f1e0b09 100644 --- a/embassy-nrf/src/pwm.rs +++ b/embassy-nrf/src/pwm.rs @@ -6,7 +6,9 @@ use core::sync::atomic::{compiler_fence, Ordering}; use embassy_hal_internal::{into_ref, PeripheralRef}; -use crate::gpio::{convert_drive, AnyPin, OutputDrive, Pin as GpioPin, PselBits, SealedPin as _}; +use crate::gpio::{convert_drive, AnyPin, OutputDrive, Pin as GpioPin, PselBits, SealedPin as _, DISCONNECTED}; +use crate::pac::gpio::vals as gpiovals; +use crate::pac::pwm::vals; use crate::ppi::{Event, Task}; use crate::util::slice_in_ram_or; use crate::{interrupt, pac, Peripheral}; @@ -128,52 +130,61 @@ impl<'d, T: Instance> SequencePwm<'d, T> { if let Some(pin) = &ch0 { pin.set_low(); - pin.conf() - .write(|w| w.dir().output().drive().variant(convert_drive(config.ch0_drive))); + pin.conf().write(|w| { + w.set_dir(gpiovals::Dir::OUTPUT); + w.set_drive(convert_drive(config.ch0_drive)); + }); } if let Some(pin) = &ch1 { pin.set_low(); - pin.conf() - .write(|w| w.dir().output().drive().variant(convert_drive(config.ch1_drive))); + pin.conf().write(|w| { + w.set_dir(gpiovals::Dir::OUTPUT); + w.set_drive(convert_drive(config.ch1_drive)); + }); } if let Some(pin) = &ch2 { pin.set_low(); - pin.conf() - .write(|w| w.dir().output().drive().variant(convert_drive(config.ch2_drive))); + pin.conf().write(|w| { + w.set_dir(gpiovals::Dir::OUTPUT); + w.set_drive(convert_drive(config.ch2_drive)); + }); } if let Some(pin) = &ch3 { pin.set_low(); - pin.conf() - .write(|w| w.dir().output().drive().variant(convert_drive(config.ch3_drive))); + pin.conf().write(|w| { + w.set_dir(gpiovals::Dir::OUTPUT); + w.set_drive(convert_drive(config.ch3_drive)); + }); } - r.psel.out[0].write(|w| unsafe { w.bits(ch0.psel_bits()) }); - r.psel.out[1].write(|w| unsafe { w.bits(ch1.psel_bits()) }); - r.psel.out[2].write(|w| unsafe { w.bits(ch2.psel_bits()) }); - r.psel.out[3].write(|w| unsafe { w.bits(ch3.psel_bits()) }); + r.psel().out(0).write_value(ch0.psel_bits()); + r.psel().out(1).write_value(ch1.psel_bits()); + r.psel().out(2).write_value(ch2.psel_bits()); + r.psel().out(3).write_value(ch3.psel_bits()); // Disable all interrupts - r.intenclr.write(|w| unsafe { w.bits(0xFFFF_FFFF) }); - r.shorts.reset(); - r.events_stopped.reset(); - r.events_loopsdone.reset(); - r.events_seqend[0].reset(); - r.events_seqend[1].reset(); - r.events_pwmperiodend.reset(); - r.events_seqstarted[0].reset(); - r.events_seqstarted[1].reset(); + r.intenclr().write(|w| w.0 = 0xFFFF_FFFF); + r.shorts().write(|_| ()); + r.events_stopped().write_value(0); + r.events_loopsdone().write_value(0); + r.events_seqend(0).write_value(0); + r.events_seqend(1).write_value(0); + r.events_pwmperiodend().write_value(0); + r.events_seqstarted(0).write_value(0); + r.events_seqstarted(1).write_value(0); - r.decoder.write(|w| { - w.load().bits(config.sequence_load as u8); - w.mode().refresh_count() + r.decoder().write(|w| { + w.set_load(vals::Load::from_bits(config.sequence_load as u8)); + w.set_mode(vals::Mode::REFRESH_COUNT); }); - r.mode.write(|w| match config.counter_mode { - CounterMode::UpAndDown => w.updown().up_and_down(), - CounterMode::Up => w.updown().up(), + r.mode().write(|w| match config.counter_mode { + CounterMode::UpAndDown => w.set_updown(vals::Updown::UP_AND_DOWN), + CounterMode::Up => w.set_updown(vals::Updown::UP), }); - r.prescaler.write(|w| w.prescaler().bits(config.prescaler as u8)); - r.countertop.write(|w| unsafe { w.countertop().bits(config.max_duty) }); + r.prescaler() + .write(|w| w.set_prescaler(vals::Prescaler::from_bits(config.prescaler as u8))); + r.countertop().write(|w| w.set_countertop(config.max_duty)); Ok(Self { _peri: _pwm, @@ -189,7 +200,7 @@ impl<'d, T: Instance> SequencePwm<'d, T> { pub fn event_stopped(&self) -> Event<'d> { let r = T::regs(); - Event::from_reg(&r.events_stopped) + Event::from_reg(r.events_stopped()) } /// Returns reference to `LoopsDone` event endpoint for PPI. @@ -197,7 +208,7 @@ impl<'d, T: Instance> SequencePwm<'d, T> { pub fn event_loops_done(&self) -> Event<'d> { let r = T::regs(); - Event::from_reg(&r.events_loopsdone) + Event::from_reg(r.events_loopsdone()) } /// Returns reference to `PwmPeriodEnd` event endpoint for PPI. @@ -205,7 +216,7 @@ impl<'d, T: Instance> SequencePwm<'d, T> { pub fn event_pwm_period_end(&self) -> Event<'d> { let r = T::regs(); - Event::from_reg(&r.events_pwmperiodend) + Event::from_reg(r.events_pwmperiodend()) } /// Returns reference to `Seq0 End` event endpoint for PPI. @@ -213,7 +224,7 @@ impl<'d, T: Instance> SequencePwm<'d, T> { pub fn event_seq_end(&self) -> Event<'d> { let r = T::regs(); - Event::from_reg(&r.events_seqend[0]) + Event::from_reg(r.events_seqend(0)) } /// Returns reference to `Seq1 End` event endpoint for PPI. @@ -221,7 +232,7 @@ impl<'d, T: Instance> SequencePwm<'d, T> { pub fn event_seq1_end(&self) -> Event<'d> { let r = T::regs(); - Event::from_reg(&r.events_seqend[1]) + Event::from_reg(r.events_seqend(1)) } /// Returns reference to `Seq0 Started` event endpoint for PPI. @@ -229,7 +240,7 @@ impl<'d, T: Instance> SequencePwm<'d, T> { pub fn event_seq0_started(&self) -> Event<'d> { let r = T::regs(); - Event::from_reg(&r.events_seqstarted[0]) + Event::from_reg(r.events_seqstarted(0)) } /// Returns reference to `Seq1 Started` event endpoint for PPI. @@ -237,7 +248,7 @@ impl<'d, T: Instance> SequencePwm<'d, T> { pub fn event_seq1_started(&self) -> Event<'d> { let r = T::regs(); - Event::from_reg(&r.events_seqstarted[1]) + Event::from_reg(r.events_seqstarted(1)) } /// Returns reference to `Seq0 Start` task endpoint for PPI. @@ -248,7 +259,7 @@ impl<'d, T: Instance> SequencePwm<'d, T> { pub unsafe fn task_start_seq0(&self) -> Task<'d> { let r = T::regs(); - Task::from_reg(&r.tasks_seqstart[0]) + Task::from_reg(r.tasks_seqstart(0)) } /// Returns reference to `Seq1 Started` task endpoint for PPI. @@ -259,7 +270,7 @@ impl<'d, T: Instance> SequencePwm<'d, T> { pub unsafe fn task_start_seq1(&self) -> Task<'d> { let r = T::regs(); - Task::from_reg(&r.tasks_seqstart[1]) + Task::from_reg(r.tasks_seqstart(1)) } /// Returns reference to `NextStep` task endpoint for PPI. @@ -270,7 +281,7 @@ impl<'d, T: Instance> SequencePwm<'d, T> { pub unsafe fn task_next_step(&self) -> Task<'d> { let r = T::regs(); - Task::from_reg(&r.tasks_nextstep) + Task::from_reg(r.tasks_nextstep()) } /// Returns reference to `Stop` task endpoint for PPI. @@ -281,7 +292,7 @@ impl<'d, T: Instance> SequencePwm<'d, T> { pub unsafe fn task_stop(&self) -> Task<'d> { let r = T::regs(); - Task::from_reg(&r.tasks_stop) + Task::from_reg(r.tasks_stop()) } } @@ -291,23 +302,23 @@ impl<'a, T: Instance> Drop for SequencePwm<'a, T> { if let Some(pin) = &self.ch0 { pin.set_low(); - pin.conf().reset(); - r.psel.out[0].reset(); + pin.conf().write(|_| ()); + r.psel().out(0).write_value(DISCONNECTED); } if let Some(pin) = &self.ch1 { pin.set_low(); - pin.conf().reset(); - r.psel.out[1].reset(); + pin.conf().write(|_| ()); + r.psel().out(1).write_value(DISCONNECTED); } if let Some(pin) = &self.ch2 { pin.set_low(); - pin.conf().reset(); - r.psel.out[2].reset(); + pin.conf().write(|_| ()); + r.psel().out(2).write_value(DISCONNECTED); } if let Some(pin) = &self.ch3 { pin.set_low(); - pin.conf().reset(); - r.psel.out[3].reset(); + pin.conf().write(|_| ()); + r.psel().out(3).write_value(DISCONNECTED); } } } @@ -463,21 +474,17 @@ impl<'d, 's, T: Instance> Sequencer<'d, 's, T> { let r = T::regs(); - r.seq0.refresh.write(|w| unsafe { w.bits(sequence0.config.refresh) }); - r.seq0.enddelay.write(|w| unsafe { w.bits(sequence0.config.end_delay) }); - r.seq0.ptr.write(|w| unsafe { w.bits(sequence0.words.as_ptr() as u32) }); - r.seq0.cnt.write(|w| unsafe { w.bits(sequence0.words.len() as u32) }); + r.seq(0).refresh().write(|w| w.0 = sequence0.config.refresh); + r.seq(0).enddelay().write(|w| w.0 = sequence0.config.end_delay); + r.seq(0).ptr().write_value(sequence0.words.as_ptr() as u32); + r.seq(0).cnt().write(|w| w.0 = sequence0.words.len() as u32); - r.seq1.refresh.write(|w| unsafe { w.bits(alt_sequence.config.refresh) }); - r.seq1 - .enddelay - .write(|w| unsafe { w.bits(alt_sequence.config.end_delay) }); - r.seq1 - .ptr - .write(|w| unsafe { w.bits(alt_sequence.words.as_ptr() as u32) }); - r.seq1.cnt.write(|w| unsafe { w.bits(alt_sequence.words.len() as u32) }); + r.seq(1).refresh().write(|w| w.0 = alt_sequence.config.refresh); + r.seq(1).enddelay().write(|w| w.0 = alt_sequence.config.end_delay); + r.seq(1).ptr().write_value(alt_sequence.words.as_ptr() as u32); + r.seq(1).cnt().write(|w| w.0 = alt_sequence.words.len() as u32); - r.enable.write(|w| w.enable().enabled()); + r.enable().write(|w| w.set_enable(true)); // defensive before seqstart compiler_fence(Ordering::SeqCst); @@ -486,18 +493,17 @@ impl<'d, 's, T: Instance> Sequencer<'d, 's, T> { match times { // just the one time, no loop count - SequenceMode::Loop(n) => { - r.loop_.write(|w| unsafe { w.cnt().bits(n) }); + SequenceMode::Loop(_) => { + r.loop_().write(|w| w.set_cnt(vals::LoopCnt::DISABLED)); } // to play infinitely, repeat the sequence one time, then have loops done self trigger seq0 again SequenceMode::Infinite => { - r.loop_.write(|w| unsafe { w.cnt().bits(0x1) }); - r.shorts.write(|w| w.loopsdone_seqstart0().enabled()); + r.loop_().write(|w| w.set_cnt(vals::LoopCnt::from_bits(1))); + r.shorts().write(|w| w.set_loopsdone_seqstart0(true)); } } - // tasks_seqstart() doesn't exist in all svds so write its bit instead - r.tasks_seqstart[seqstart_index].write(|w| unsafe { w.bits(0x01) }); + r.tasks_seqstart(seqstart_index).write_value(1); Ok(()) } @@ -509,14 +515,12 @@ impl<'d, 's, T: Instance> Sequencer<'d, 's, T> { pub fn stop(&self) { let r = T::regs(); - r.shorts.reset(); + r.shorts().write(|_| ()); compiler_fence(Ordering::SeqCst); - // tasks_stop() doesn't exist in all svds so write its bit instead - r.tasks_stop.write(|w| unsafe { w.bits(0x01) }); - - r.enable.write(|w| w.enable().disabled()); + r.tasks_stop().write_value(1); + r.enable().write(|w| w.set_enable(false)); } } @@ -672,29 +676,13 @@ impl<'d, T: Instance> SimplePwm<'d, T> { let r = T::regs(); - if let Some(pin) = &ch0 { - pin.set_low(); - pin.conf().write(|w| w.dir().output()); + for (i, ch) in [&ch0, &ch1, &ch2, &ch3].into_iter().enumerate() { + if let Some(pin) = ch { + pin.set_low(); + pin.conf().write(|w| w.set_dir(gpiovals::Dir::OUTPUT)); + } + r.psel().out(i).write_value(ch0.psel_bits()); } - if let Some(pin) = &ch1 { - pin.set_low(); - pin.conf().write(|w| w.dir().output()); - } - if let Some(pin) = &ch2 { - pin.set_low(); - pin.conf().write(|w| w.dir().output()); - } - if let Some(pin) = &ch3 { - pin.set_low(); - pin.conf().write(|w| w.dir().output()); - } - - // if NoPin provided writes disconnected (top bit 1) 0x80000000 else - // writes pin number ex 13 (0x0D) which is connected (top bit 0) - r.psel.out[0].write(|w| unsafe { w.bits(ch0.psel_bits()) }); - r.psel.out[1].write(|w| unsafe { w.bits(ch1.psel_bits()) }); - r.psel.out[2].write(|w| unsafe { w.bits(ch2.psel_bits()) }); - r.psel.out[3].write(|w| unsafe { w.bits(ch3.psel_bits()) }); let pwm = Self { _peri: _pwm, @@ -706,26 +694,25 @@ impl<'d, T: Instance> SimplePwm<'d, T> { }; // Disable all interrupts - r.intenclr.write(|w| unsafe { w.bits(0xFFFF_FFFF) }); - r.shorts.reset(); + r.intenclr().write(|w| w.0 = 0xFFFF_FFFF); + r.shorts().write(|_| ()); // Enable - r.enable.write(|w| w.enable().enabled()); + r.enable().write(|w| w.set_enable(true)); - r.seq0.ptr.write(|w| unsafe { w.bits((pwm.duty).as_ptr() as u32) }); + r.seq(0).ptr().write_value((pwm.duty).as_ptr() as u32); + r.seq(0).cnt().write(|w| w.0 = 4); + r.seq(0).refresh().write(|w| w.0 = 0); + r.seq(0).enddelay().write(|w| w.0 = 0); - r.seq0.cnt.write(|w| unsafe { w.bits(4) }); - r.seq0.refresh.write(|w| unsafe { w.bits(0) }); - r.seq0.enddelay.write(|w| unsafe { w.bits(0) }); - - r.decoder.write(|w| { - w.load().individual(); - w.mode().refresh_count() + r.decoder().write(|w| { + w.set_load(vals::Load::INDIVIDUAL); + w.set_mode(vals::Mode::REFRESH_COUNT); }); - r.mode.write(|w| w.updown().up()); - r.prescaler.write(|w| w.prescaler().div_16()); - r.countertop.write(|w| unsafe { w.countertop().bits(1000) }); - r.loop_.write(|w| w.cnt().disabled()); + r.mode().write(|w| w.set_updown(vals::Updown::UP)); + r.prescaler().write(|w| w.set_prescaler(vals::Prescaler::DIV_16)); + r.countertop().write(|w| w.set_countertop(1000)); + r.loop_().write(|w| w.set_cnt(vals::LoopCnt::DISABLED)); pwm } @@ -734,21 +721,21 @@ impl<'d, T: Instance> SimplePwm<'d, T> { #[inline(always)] pub fn is_enabled(&self) -> bool { let r = T::regs(); - r.enable.read().enable().bit_is_set() + r.enable().read().enable() } /// Enables the PWM generator. #[inline(always)] pub fn enable(&self) { let r = T::regs(); - r.enable.write(|w| w.enable().enabled()); + r.enable().write(|w| w.set_enable(true)); } /// Disables the PWM generator. Does NOT clear the last duty cycle from the pin. #[inline(always)] pub fn disable(&self) { let r = T::regs(); - r.enable.write(|w| w.enable().disabled()); + r.enable().write(|w| w.set_enable(false)); } /// Returns the current duty of the channel @@ -763,33 +750,35 @@ impl<'d, T: Instance> SimplePwm<'d, T> { self.duty[channel] = duty & 0x7FFF; // reload ptr in case self was moved - r.seq0.ptr.write(|w| unsafe { w.bits((self.duty).as_ptr() as u32) }); + r.seq(0).ptr().write_value((self.duty).as_ptr() as u32); // defensive before seqstart compiler_fence(Ordering::SeqCst); - r.events_seqend[0].reset(); + r.events_seqend(0).write_value(0); // tasks_seqstart() doesn't exist in all svds so write its bit instead - r.tasks_seqstart[0].write(|w| unsafe { w.bits(1) }); + r.tasks_seqstart(0).write_value(1); // defensive wait until waveform is loaded after seqstart so set_duty // can't be called again while dma is still reading if self.is_enabled() { - while r.events_seqend[0].read().bits() == 0 {} + while r.events_seqend(0).read() == 0 {} } } /// Sets the PWM clock prescaler. #[inline(always)] pub fn set_prescaler(&self, div: Prescaler) { - T::regs().prescaler.write(|w| w.prescaler().bits(div as u8)); + T::regs() + .prescaler() + .write(|w| w.set_prescaler(vals::Prescaler::from_bits(div as u8))); } /// Gets the PWM clock prescaler. #[inline(always)] pub fn prescaler(&self) -> Prescaler { - match T::regs().prescaler.read().prescaler().bits() { + match T::regs().prescaler().read().prescaler().to_bits() { 0 => Prescaler::Div1, 1 => Prescaler::Div2, 2 => Prescaler::Div4, @@ -805,15 +794,13 @@ impl<'d, T: Instance> SimplePwm<'d, T> { /// Sets the maximum duty cycle value. #[inline(always)] pub fn set_max_duty(&self, duty: u16) { - T::regs() - .countertop - .write(|w| unsafe { w.countertop().bits(duty.min(32767u16)) }); + T::regs().countertop().write(|w| w.set_countertop(duty.min(32767u16))); } /// Returns the maximum duty cycle value. #[inline(always)] pub fn max_duty(&self) -> u16 { - T::regs().countertop.read().countertop().bits() + T::regs().countertop().read().countertop() } /// Sets the PWM output frequency. @@ -836,7 +823,7 @@ impl<'d, T: Instance> SimplePwm<'d, T> { #[inline(always)] pub fn set_ch0_drive(&self, drive: OutputDrive) { if let Some(pin) = &self.ch0 { - pin.conf().modify(|_, w| w.drive().variant(convert_drive(drive))); + pin.conf().modify(|w| w.set_drive(convert_drive(drive))); } } @@ -844,7 +831,7 @@ impl<'d, T: Instance> SimplePwm<'d, T> { #[inline(always)] pub fn set_ch1_drive(&self, drive: OutputDrive) { if let Some(pin) = &self.ch1 { - pin.conf().modify(|_, w| w.drive().variant(convert_drive(drive))); + pin.conf().modify(|w| w.set_drive(convert_drive(drive))); } } @@ -852,7 +839,7 @@ impl<'d, T: Instance> SimplePwm<'d, T> { #[inline(always)] pub fn set_ch2_drive(&self, drive: OutputDrive) { if let Some(pin) = &self.ch2 { - pin.conf().modify(|_, w| w.drive().variant(convert_drive(drive))); + pin.conf().modify(|w| w.set_drive(convert_drive(drive))); } } @@ -860,7 +847,7 @@ impl<'d, T: Instance> SimplePwm<'d, T> { #[inline(always)] pub fn set_ch3_drive(&self, drive: OutputDrive) { if let Some(pin) = &self.ch3 { - pin.conf().modify(|_, w| w.drive().variant(convert_drive(drive))); + pin.conf().modify(|w| w.set_drive(convert_drive(drive))); } } } @@ -873,29 +860,29 @@ impl<'a, T: Instance> Drop for SimplePwm<'a, T> { if let Some(pin) = &self.ch0 { pin.set_low(); - pin.conf().reset(); - r.psel.out[0].reset(); + pin.conf().write(|_| ()); + r.psel().out(0).write_value(DISCONNECTED); } if let Some(pin) = &self.ch1 { pin.set_low(); - pin.conf().reset(); - r.psel.out[1].reset(); + pin.conf().write(|_| ()); + r.psel().out(1).write_value(DISCONNECTED); } if let Some(pin) = &self.ch2 { pin.set_low(); - pin.conf().reset(); - r.psel.out[2].reset(); + pin.conf().write(|_| ()); + r.psel().out(2).write_value(DISCONNECTED); } if let Some(pin) = &self.ch3 { pin.set_low(); - pin.conf().reset(); - r.psel.out[3].reset(); + pin.conf().write(|_| ()); + r.psel().out(3).write_value(DISCONNECTED); } } } pub(crate) trait SealedInstance { - fn regs() -> &'static pac::pwm0::RegisterBlock; + fn regs() -> pac::pwm::Pwm; } /// PWM peripheral instance. @@ -908,8 +895,8 @@ pub trait Instance: Peripheral

+ SealedInstance + 'static { macro_rules! impl_pwm { ($type:ident, $pac_type:ident, $irq:ident) => { impl crate::pwm::SealedInstance for peripherals::$type { - fn regs() -> &'static pac::pwm0::RegisterBlock { - unsafe { &*pac::$pac_type::ptr() } + fn regs() -> pac::pwm::Pwm { + pac::$pac_type } } impl crate::pwm::Instance for peripherals::$type { diff --git a/embassy-nrf/src/qdec.rs b/embassy-nrf/src/qdec.rs index 7409c9b1e..efd2a134c 100644 --- a/embassy-nrf/src/qdec.rs +++ b/embassy-nrf/src/qdec.rs @@ -11,7 +11,9 @@ use embassy_sync::waitqueue::AtomicWaker; use crate::gpio::{AnyPin, Pin as GpioPin, SealedPin as _}; use crate::interrupt::typelevel::Interrupt; -use crate::{interrupt, Peripheral}; +use crate::pac::gpio::vals as gpiovals; +use crate::pac::qdec::vals; +use crate::{interrupt, pac, Peripheral}; /// Quadrature decoder driver. pub struct Qdec<'d, T: Instance> { @@ -52,7 +54,7 @@ pub struct InterruptHandler { impl interrupt::typelevel::Handler for InterruptHandler { unsafe fn on_interrupt() { - T::regs().intenclr.write(|w| w.reportrdy().clear()); + T::regs().intenclr().write(|w| w.set_reportrdy(true)); T::state().waker.wake(); } } @@ -93,54 +95,59 @@ impl<'d, T: Instance> Qdec<'d, T> { let r = T::regs(); // Select pins. - a.conf().write(|w| w.input().connect().pull().pullup()); - b.conf().write(|w| w.input().connect().pull().pullup()); - r.psel.a.write(|w| unsafe { w.bits(a.psel_bits()) }); - r.psel.b.write(|w| unsafe { w.bits(b.psel_bits()) }); + a.conf().write(|w| { + w.set_input(gpiovals::Input::CONNECT); + w.set_pull(gpiovals::Pull::PULLUP); + }); + b.conf().write(|w| { + w.set_input(gpiovals::Input::CONNECT); + w.set_pull(gpiovals::Pull::PULLUP); + }); + r.psel().a().write_value(a.psel_bits()); + r.psel().b().write_value(b.psel_bits()); if let Some(led_pin) = &led { - led_pin.conf().write(|w| w.dir().output()); - r.psel.led.write(|w| unsafe { w.bits(led_pin.psel_bits()) }); + led_pin.conf().write(|w| w.set_dir(gpiovals::Dir::OUTPUT)); + r.psel().led().write_value(led_pin.psel_bits()); } // Enables/disable input debounce filters - r.dbfen.write(|w| match config.debounce { - true => w.dbfen().enabled(), - false => w.dbfen().disabled(), + r.dbfen().write(|w| match config.debounce { + true => w.set_dbfen(true), + false => w.set_dbfen(false), }); // Set LED output pin polarity - r.ledpol.write(|w| match config.led_polarity { - LedPolarity::ActiveHigh => w.ledpol().active_high(), - LedPolarity::ActiveLow => w.ledpol().active_low(), + r.ledpol().write(|w| match config.led_polarity { + LedPolarity::ActiveHigh => w.set_ledpol(vals::Ledpol::ACTIVE_HIGH), + LedPolarity::ActiveLow => w.set_ledpol(vals::Ledpol::ACTIVE_LOW), }); // Set time period the LED is switched ON prior to sampling (0..511 us). - r.ledpre - .write(|w| unsafe { w.ledpre().bits(config.led_pre_usecs.min(511)) }); + r.ledpre().write(|w| w.set_ledpre(config.led_pre_usecs.min(511))); // Set sample period - r.sampleper.write(|w| match config.period { - SamplePeriod::_128us => w.sampleper()._128us(), - SamplePeriod::_256us => w.sampleper()._256us(), - SamplePeriod::_512us => w.sampleper()._512us(), - SamplePeriod::_1024us => w.sampleper()._1024us(), - SamplePeriod::_2048us => w.sampleper()._2048us(), - SamplePeriod::_4096us => w.sampleper()._4096us(), - SamplePeriod::_8192us => w.sampleper()._8192us(), - SamplePeriod::_16384us => w.sampleper()._16384us(), - SamplePeriod::_32ms => w.sampleper()._32ms(), - SamplePeriod::_65ms => w.sampleper()._65ms(), - SamplePeriod::_131ms => w.sampleper()._131ms(), + r.sampleper().write(|w| match config.period { + SamplePeriod::_128us => w.set_sampleper(vals::Sampleper::_128US), + SamplePeriod::_256us => w.set_sampleper(vals::Sampleper::_256US), + SamplePeriod::_512us => w.set_sampleper(vals::Sampleper::_512US), + SamplePeriod::_1024us => w.set_sampleper(vals::Sampleper::_1024US), + SamplePeriod::_2048us => w.set_sampleper(vals::Sampleper::_2048US), + SamplePeriod::_4096us => w.set_sampleper(vals::Sampleper::_4096US), + SamplePeriod::_8192us => w.set_sampleper(vals::Sampleper::_8192US), + SamplePeriod::_16384us => w.set_sampleper(vals::Sampleper::_16384US), + SamplePeriod::_32ms => w.set_sampleper(vals::Sampleper::_32MS), + SamplePeriod::_65ms => w.set_sampleper(vals::Sampleper::_65MS), + SamplePeriod::_131ms => w.set_sampleper(vals::Sampleper::_131MS), }); T::Interrupt::unpend(); unsafe { T::Interrupt::enable() }; // Enable peripheral - r.enable.write(|w| w.enable().set_bit()); + r.enable().write(|w| w.set_enable(true)); // Start sampling - unsafe { r.tasks_start.write(|w| w.bits(1)) }; + r.tasks_start().write_value(1); Self { _p: p } } @@ -169,16 +176,16 @@ impl<'d, T: Instance> Qdec<'d, T> { /// ``` pub async fn read(&mut self) -> i16 { let t = T::regs(); - t.intenset.write(|w| w.reportrdy().set()); - unsafe { t.tasks_readclracc.write(|w| w.bits(1)) }; + t.intenset().write(|w| w.set_reportrdy(true)); + t.tasks_readclracc().write_value(1); poll_fn(|cx| { T::state().waker.register(cx.waker()); - if t.events_reportrdy.read().bits() == 0 { + if t.events_reportrdy().read() == 0 { Poll::Pending } else { - t.events_reportrdy.reset(); - let acc = t.accread.read().bits(); + t.events_reportrdy().write_value(0); + let acc = t.accread().read(); Poll::Ready(acc as i16) } }) @@ -259,7 +266,7 @@ impl State { } pub(crate) trait SealedInstance { - fn regs() -> &'static crate::pac::qdec::RegisterBlock; + fn regs() -> pac::qdec::Qdec; fn state() -> &'static State; } @@ -273,8 +280,8 @@ pub trait Instance: Peripheral

+ SealedInstance + 'static + Send { macro_rules! impl_qdec { ($type:ident, $pac_type:ident, $irq:ident) => { impl crate::qdec::SealedInstance for peripherals::$type { - fn regs() -> &'static crate::pac::qdec::RegisterBlock { - unsafe { &*pac::$pac_type::ptr() } + fn regs() -> pac::qdec::Qdec { + pac::$pac_type } fn state() -> &'static crate::qdec::State { static STATE: crate::qdec::State = crate::qdec::State::new(); diff --git a/embassy-nrf/src/qspi.rs b/embassy-nrf/src/qspi.rs index d40096edc..de9c268c1 100755 --- a/embassy-nrf/src/qspi.rs +++ b/embassy-nrf/src/qspi.rs @@ -14,11 +14,12 @@ use embedded_storage::nor_flash::{ErrorType, NorFlash, NorFlashError, NorFlashEr use crate::gpio::{self, Pin as GpioPin}; use crate::interrupt::typelevel::Interrupt; -pub use crate::pac::qspi::ifconfig0::{ - ADDRMODE_A as AddressMode, PPSIZE_A as WritePageSize, READOC_A as ReadOpcode, WRITEOC_A as WriteOpcode, +use crate::pac::gpio::vals as gpiovals; +use crate::pac::qspi::vals; +pub use crate::pac::qspi::vals::{ + Addrmode as AddressMode, Ppsize as WritePageSize, Readoc as ReadOpcode, Spimode as SpiMode, Writeoc as WriteOpcode, }; -pub use crate::pac::qspi::ifconfig1::SPIMODE_A as SpiMode; -use crate::{interrupt, Peripheral}; +use crate::{interrupt, pac, Peripheral}; /// Deep power-down config. pub struct DeepPowerDownConfig { @@ -129,9 +130,9 @@ impl interrupt::typelevel::Handler for InterruptHandl let r = T::regs(); let s = T::state(); - if r.events_ready.read().bits() != 0 { + if r.events_ready().read() != 0 { s.waker.wake(); - r.intenclr.write(|w| w.ready().clear()); + r.intenclr().write(|w| w.set_ready(true)); } } } @@ -164,13 +165,12 @@ impl<'d, T: Instance> Qspi<'d, T> { ($pin:ident) => { $pin.set_high(); $pin.conf().write(|w| { - w.dir().output(); - w.drive().h0h1(); + w.set_dir(gpiovals::Dir::OUTPUT); + w.set_drive(gpiovals::Drive::H0H1); #[cfg(all(feature = "_nrf5340", feature = "_s"))] - w.mcusel().peripheral(); - w + w.set_mcusel(gpiovals::Mcusel::PERIPHERAL); }); - r.psel.$pin.write(|w| unsafe { w.bits($pin.psel_bits()) }); + r.psel().$pin().write_value($pin.psel_bits()); }; } @@ -181,46 +181,39 @@ impl<'d, T: Instance> Qspi<'d, T> { config_pin!(io2); config_pin!(io3); - r.ifconfig0.write(|w| { - w.addrmode().variant(config.address_mode); - w.dpmenable().bit(config.deep_power_down.is_some()); - w.ppsize().variant(config.write_page_size); - w.readoc().variant(config.read_opcode); - w.writeoc().variant(config.write_opcode); - w + r.ifconfig0().write(|w| { + w.set_addrmode(config.address_mode); + w.set_dpmenable(config.deep_power_down.is_some()); + w.set_ppsize(config.write_page_size); + w.set_readoc(config.read_opcode); + w.set_writeoc(config.write_opcode); }); if let Some(dpd) = &config.deep_power_down { - r.dpmdur.write(|w| unsafe { - w.enter().bits(dpd.enter_time); - w.exit().bits(dpd.exit_time); - w + r.dpmdur().write(|w| { + w.set_enter(dpd.enter_time); + w.set_exit(dpd.exit_time); }) } - r.ifconfig1.write(|w| unsafe { - w.sckdelay().bits(config.sck_delay); - w.dpmen().exit(); - w.spimode().variant(config.spi_mode); - w.sckfreq().bits(config.frequency as u8); - w + r.ifconfig1().write(|w| { + w.set_sckdelay(config.sck_delay); + w.set_dpmen(false); + w.set_spimode(config.spi_mode); + w.set_sckfreq(config.frequency as u8); }); - r.iftiming.write(|w| unsafe { - w.rxdelay().bits(config.rx_delay & 0b111); - w + r.iftiming().write(|w| { + w.set_rxdelay(config.rx_delay & 0b111); }); - r.xipoffset.write(|w| unsafe { - w.xipoffset().bits(config.xip_offset); - w - }); + r.xipoffset().write_value(config.xip_offset); T::Interrupt::unpend(); unsafe { T::Interrupt::enable() }; // Enable it - r.enable.write(|w| w.enable().enabled()); + r.enable().write(|w| w.set_enable(true)); let res = Self { _peri: qspi, @@ -228,10 +221,10 @@ impl<'d, T: Instance> Qspi<'d, T> { capacity: config.capacity, }; - r.events_ready.reset(); - r.intenset.write(|w| w.ready().set()); + r.events_ready().write_value(0); + r.intenset().write(|w| w.set_ready(true)); - r.tasks_activate.write(|w| w.tasks_activate().bit(true)); + r.tasks_activate().write_value(1); Self::blocking_wait_ready(); @@ -284,22 +277,21 @@ impl<'d, T: Instance> Qspi<'d, T> { } let r = T::regs(); - r.cinstrdat0.write(|w| unsafe { w.bits(dat0) }); - r.cinstrdat1.write(|w| unsafe { w.bits(dat1) }); + r.cinstrdat0().write(|w| w.0 = dat0); + r.cinstrdat1().write(|w| w.0 = dat1); - r.events_ready.reset(); - r.intenset.write(|w| w.ready().set()); + r.events_ready().write_value(0); + r.intenset().write(|w| w.set_ready(true)); - r.cinstrconf.write(|w| { - let w = unsafe { w.opcode().bits(opcode) }; - let w = unsafe { w.length().bits(len + 1) }; - let w = w.lio2().bit(true); - let w = w.lio3().bit(true); - let w = w.wipwait().bit(true); - let w = w.wren().bit(true); - let w = w.lfen().bit(false); - let w = w.lfstop().bit(false); - w + r.cinstrconf().write(|w| { + w.set_opcode(opcode); + w.set_length(vals::Length::from_bits(len + 1)); + w.set_lio2(true); + w.set_lio3(true); + w.set_wipwait(true); + w.set_wren(true); + w.set_lfen(false); + w.set_lfstop(false); }); Ok(()) } @@ -307,8 +299,8 @@ impl<'d, T: Instance> Qspi<'d, T> { fn custom_instruction_finish(&mut self, resp: &mut [u8]) -> Result<(), Error> { let r = T::regs(); - let dat0 = r.cinstrdat0.read().bits(); - let dat1 = r.cinstrdat1.read().bits(); + let dat0 = r.cinstrdat0().read().0; + let dat1 = r.cinstrdat1().read().0; for i in 0..4 { if i < resp.len() { resp[i] = (dat0 >> (i * 8)) as u8; @@ -327,7 +319,7 @@ impl<'d, T: Instance> Qspi<'d, T> { let r = T::regs(); let s = T::state(); s.waker.register(cx.waker()); - if r.events_ready.read().bits() != 0 { + if r.events_ready().read() != 0 { return Poll::Ready(()); } Poll::Pending @@ -338,7 +330,7 @@ impl<'d, T: Instance> Qspi<'d, T> { fn blocking_wait_ready() { loop { let r = T::regs(); - if r.events_ready.read().bits() != 0 { + if r.events_ready().read() != 0 { break; } } @@ -352,13 +344,13 @@ impl<'d, T: Instance> Qspi<'d, T> { let r = T::regs(); - r.read.src.write(|w| unsafe { w.src().bits(address) }); - r.read.dst.write(|w| unsafe { w.dst().bits(data.as_ptr() as u32) }); - r.read.cnt.write(|w| unsafe { w.cnt().bits(data.len() as u32) }); + r.read().src().write_value(address); + r.read().dst().write_value(data.as_ptr() as u32); + r.read().cnt().write(|w| w.set_cnt(data.len() as u32)); - r.events_ready.reset(); - r.intenset.write(|w| w.ready().set()); - r.tasks_readstart.write(|w| w.tasks_readstart().bit(true)); + r.events_ready().write_value(0); + r.intenset().write(|w| w.set_ready(true)); + r.tasks_readstart().write_value(1); Ok(()) } @@ -370,13 +362,13 @@ impl<'d, T: Instance> Qspi<'d, T> { assert_eq!(address % 4, 0); let r = T::regs(); - r.write.src.write(|w| unsafe { w.src().bits(data.as_ptr() as u32) }); - r.write.dst.write(|w| unsafe { w.dst().bits(address) }); - r.write.cnt.write(|w| unsafe { w.cnt().bits(data.len() as u32) }); + r.write().src().write_value(data.as_ptr() as u32); + r.write().dst().write_value(address); + r.write().cnt().write(|w| w.set_cnt(data.len() as u32)); - r.events_ready.reset(); - r.intenset.write(|w| w.ready().set()); - r.tasks_writestart.write(|w| w.tasks_writestart().bit(true)); + r.events_ready().write_value(0); + r.intenset().write(|w| w.set_ready(true)); + r.tasks_writestart().write_value(1); Ok(()) } @@ -386,12 +378,12 @@ impl<'d, T: Instance> Qspi<'d, T> { assert_eq!(address % 4096, 0); let r = T::regs(); - r.erase.ptr.write(|w| unsafe { w.ptr().bits(address) }); - r.erase.len.write(|w| w.len()._4kb()); + r.erase().ptr().write_value(address); + r.erase().len().write(|w| w.set_len(vals::Len::_4KB)); - r.events_ready.reset(); - r.intenset.write(|w| w.ready().set()); - r.tasks_erasestart.write(|w| w.tasks_erasestart().bit(true)); + r.events_ready().write_value(0); + r.intenset().write(|w| w.set_ready(true)); + r.tasks_erasestart().write_value(1); Ok(()) } @@ -538,12 +530,12 @@ impl<'d, T: Instance> Drop for Qspi<'d, T> { if self.dpm_enabled { trace!("qspi: doing deep powerdown..."); - r.ifconfig1.modify(|_, w| w.dpmen().enter()); + r.ifconfig1().modify(|w| w.set_dpmen(true)); // Wait for DPM enter. // Unfortunately we must spin. There's no way to do this interrupt-driven. // The READY event does NOT fire on DPM enter (but it does fire on DPM exit :shrug:) - while r.status.read().dpm().is_disabled() {} + while !r.status().read().dpm() {} // Wait MORE for DPM enter. // I have absolutely no idea why, but the wait above is not enough :'( @@ -552,23 +544,23 @@ impl<'d, T: Instance> Drop for Qspi<'d, T> { } // it seems events_ready is not generated in response to deactivate. nrfx doesn't wait for it. - r.tasks_deactivate.write(|w| w.tasks_deactivate().set_bit()); + r.tasks_deactivate().write_value(1); // Workaround https://infocenter.nordicsemi.com/topic/errata_nRF52840_Rev1/ERR/nRF52840/Rev1/latest/anomaly_840_122.html?cp=4_0_1_2_1_7 // Note that the doc has 2 register writes, but the first one is really the write to tasks_deactivate, // so we only do the second one here. unsafe { ptr::write_volatile(0x40029054 as *mut u32, 1) } - r.enable.write(|w| w.enable().disabled()); + r.enable().write(|w| w.set_enable(false)); // Note: we do NOT deconfigure CSN here. If DPM is in use and we disconnect CSN, // leaving it floating, the flash chip might read it as zero which would cause it to // spuriously exit DPM. - gpio::deconfigure_pin(r.psel.sck.read().bits()); - gpio::deconfigure_pin(r.psel.io0.read().bits()); - gpio::deconfigure_pin(r.psel.io1.read().bits()); - gpio::deconfigure_pin(r.psel.io2.read().bits()); - gpio::deconfigure_pin(r.psel.io3.read().bits()); + gpio::deconfigure_pin(r.psel().sck().read()); + gpio::deconfigure_pin(r.psel().io0().read()); + gpio::deconfigure_pin(r.psel().io1().read()); + gpio::deconfigure_pin(r.psel().io2().read()); + gpio::deconfigure_pin(r.psel().io3().read()); trace!("qspi: dropped"); } @@ -667,7 +659,7 @@ impl State { } pub(crate) trait SealedInstance { - fn regs() -> &'static crate::pac::qspi::RegisterBlock; + fn regs() -> pac::qspi::Qspi; fn state() -> &'static State; } @@ -681,8 +673,8 @@ pub trait Instance: Peripheral

+ SealedInstance + 'static + Send { macro_rules! impl_qspi { ($type:ident, $pac_type:ident, $irq:ident) => { impl crate::qspi::SealedInstance for peripherals::$type { - fn regs() -> &'static crate::pac::qspi::RegisterBlock { - unsafe { &*pac::$pac_type::ptr() } + fn regs() -> pac::qspi::Qspi { + pac::$pac_type } fn state() -> &'static crate::qspi::State { static STATE: crate::qspi::State = crate::qspi::State::new(); diff --git a/embassy-nrf/src/radio/ble.rs b/embassy-nrf/src/radio/ble.rs index 4f0b0641f..682ca1c79 100644 --- a/embassy-nrf/src/radio/ble.rs +++ b/embassy-nrf/src/radio/ble.rs @@ -6,11 +6,12 @@ use core::task::Poll; use embassy_hal_internal::drop::OnDrop; use embassy_hal_internal::{into_ref, PeripheralRef}; -pub use pac::radio::mode::MODE_A as Mode; -#[cfg(not(feature = "nrf51"))] -use pac::radio::pcnf0::PLEN_A as PreambleLength; +pub use pac::radio::vals::Mode; +#[cfg(not(feature = "_nrf51"))] +use pac::radio::vals::Plen as PreambleLength; use crate::interrupt::typelevel::Interrupt; +use crate::pac::radio::vals; use crate::radio::*; pub use crate::radio::{Error, TxPower}; use crate::util::slice_in_ram_or; @@ -30,69 +31,63 @@ impl<'d, T: Instance> Radio<'d, T> { let r = T::regs(); - r.pcnf1.write(|w| unsafe { + r.pcnf1().write(|w| { // It is 0 bytes long in a standard BLE packet - w.statlen() - .bits(0) - // MaxLen configures the maximum packet payload plus add-on size in - // number of bytes that can be transmitted or received by the RADIO. This feature can be used to ensure - // that the RADIO does not overwrite, or read beyond, the RAM assigned to the packet payload. This means - // that if the packet payload length defined by PCNF1.STATLEN and the LENGTH field in the packet specifies a - // packet larger than MAXLEN, the payload will be truncated at MAXLEN - // - // To simplify the implementation, It is setted as the maximum value - // and the length of the packet is controlled only by the LENGTH field in the packet - .maxlen() - .bits(255) - // Configure the length of the address field in the packet - // The prefix after the address fields is always appended, so is always 1 byte less than the size of the address - // The base address is truncated from the least significant byte if the BALEN is less than 4 - // - // BLE address is always 4 bytes long - .balen() - .bits(3) // 3 bytes base address (+ 1 prefix); - // Configure the endianess - // For BLE is always little endian (LSB first) - .endian() - .little() - // Data whitening is used to avoid long sequences of zeros or - // ones, e.g., 0b0000000 or 0b1111111, in the data bit stream. - // The whitener and de-whitener are defined the same way, - // using a 7-bit linear feedback shift register with the - // polynomial x7 + x4 + 1. - // - // In BLE Whitening shall be applied on the PDU and CRC of all - // Link Layer packets and is performed after the CRC generation - // in the transmitter. No other parts of the packets are whitened. - // De-whitening is performed before the CRC checking in the receiver - // Before whitening or de-whitening, the shift register should be - // initialized based on the channel index. - .whiteen() - .set_bit() + w.set_statlen(0); + // MaxLen configures the maximum packet payload plus add-on size in + // number of bytes that can be transmitted or received by the RADIO. This feature can be used to ensure + // that the RADIO does not overwrite, or read beyond, the RAM assigned to the packet payload. This means + // that if the packet payload length defined by PCNF1.STATLEN and the LENGTH field in the packet specifies a + // packet larger than MAXLEN, the payload will be truncated at MAXLEN + // + // To simplify the implementation, It is setted as the maximum value + // and the length of the packet is controlled only by the LENGTH field in the packet + w.set_maxlen(255); + // Configure the length of the address field in the packet + // The prefix after the address fields is always appended, so is always 1 byte less than the size of the address + // The base address is truncated from the least significant byte if the BALEN is less than 4 + // + // BLE address is always 4 bytes long + w.set_balen(3); // 3 bytes base address (+ 1 prefix); + // Configure the endianess + // For BLE is always little endian (LSB first) + w.set_endian(vals::Endian::LITTLE); + // Data whitening is used to avoid long sequences of zeros or + // ones, e.g., 0b0000000 or 0b1111111, in the data bit stream. + // The whitener and de-whitener are defined the same way, + // using a 7-bit linear feedback shift register with the + // polynomial x7 + x4 + 1. + // + // In BLE Whitening shall be applied on the PDU and CRC of all + // Link Layer packets and is performed after the CRC generation + // in the transmitter. No other parts of the packets are whitened. + // De-whitening is performed before the CRC checking in the receiver + // Before whitening or de-whitening, the shift register should be + // initialized based on the channel index. + w.set_whiteen(true); }); // Configure CRC - r.crccnf.write(|w| { + r.crccnf().write(|w| { // In BLE the CRC shall be calculated on the PDU of all Link Layer // packets (even if the packet is encrypted). // It skips the address field - w.skipaddr() - .skip() - // In BLE 24-bit CRC = 3 bytes - .len() - .three() + w.set_skipaddr(vals::Skipaddr::SKIP); + // In BLE 24-bit CRC = 3 bytes + w.set_len(vals::Len::THREE); }); // Ch map between 2400 MHZ .. 2500 MHz // All modes use this range - #[cfg(not(feature = "nrf51"))] - r.frequency.write(|w| w.map().default()); + #[cfg(not(feature = "_nrf51"))] + r.frequency().write(|w| w.set_map(vals::Map::DEFAULT)); // Configure shortcuts to simplify and speed up sending and receiving packets. - r.shorts.write(|w| { + r.shorts().write(|w| { // start transmission/recv immediately after ramp-up // disable radio when transmission/recv is done - w.ready_start().enabled().end_disable().enabled() + w.set_ready_start(true); + w.set_end_disable(true); }); // Enable NVIC interrupt @@ -113,11 +108,11 @@ impl<'d, T: Instance> Radio<'d, T> { assert!(self.state() == RadioState::DISABLED); let r = T::regs(); - r.mode.write(|w| w.mode().variant(mode)); + r.mode().write(|w| w.set_mode(mode)); - #[cfg(not(feature = "nrf51"))] - r.pcnf0.write(|w| { - w.plen().variant(match mode { + #[cfg(not(feature = "_nrf51"))] + r.pcnf0().write(|w| { + w.set_plen(match mode { Mode::BLE_1MBIT => PreambleLength::_8BIT, Mode::BLE_2MBIT => PreambleLength::_16BIT, #[cfg(any( @@ -147,18 +142,14 @@ impl<'d, T: Instance> Radio<'d, T> { true => 8, }; - r.pcnf0.write(|w| unsafe { - w - // Configure S0 to 1 byte length, this will represent the Data/Adv header flags - .s0len() - .set_bit() - // Configure the length (in bits) field to 1 byte length, this will represent the length of the payload - // and also be used to know how many bytes to read/write from/to the buffer - .lflen() - .bits(8) - // Configure the lengh (in bits) of bits in the S1 field. It could be used to represent the CTEInfo for data packages in BLE. - .s1len() - .bits(s1len) + r.pcnf0().write(|w| { + // Configure S0 to 1 byte length, this will represent the Data/Adv header flags + w.set_s0len(true); + // Configure the length (in bits) field to 1 byte length, this will represent the length of the payload + // and also be used to know how many bytes to read/write from/to the buffer + w.set_lflen(0); + // Configure the lengh (in bits) of bits in the S1 field. It could be used to represent the CTEInfo for data packages in BLE. + w.set_s1len(s1len); }); } @@ -172,7 +163,7 @@ impl<'d, T: Instance> Radio<'d, T> { let r = T::regs(); - r.datawhiteiv.write(|w| unsafe { w.datawhiteiv().bits(whitening_init) }); + r.datawhiteiv().write(|w| w.set_datawhiteiv(whitening_init)); } /// Set the central frequency to be used @@ -185,8 +176,7 @@ impl<'d, T: Instance> Radio<'d, T> { let r = T::regs(); - r.frequency - .write(|w| unsafe { w.frequency().bits((frequency - 2400) as u8) }); + r.frequency().write(|w| w.set_frequency((frequency - 2400) as u8)); } /// Set the acess address @@ -204,31 +194,25 @@ impl<'d, T: Instance> Radio<'d, T> { // The byte ordering on air is always least significant byte first for the address // So for the address 0xAA_BB_CC_DD, the address on air will be DD CC BB AA // The package order is BASE, PREFIX so BASE=0xBB_CC_DD and PREFIX=0xAA - r.prefix0 - .write(|w| unsafe { w.ap0().bits((access_address >> 24) as u8) }); + r.prefix0().write(|w| w.set_ap0((access_address >> 24) as u8)); // The base address is truncated from the least significant byte (because the BALEN is less than 4) // So it shifts the address to the right - r.base0.write(|w| unsafe { w.bits(access_address << 8) }); + r.base0().write_value(access_address << 8); // Don't match tx address - r.txaddress.write(|w| unsafe { w.txaddress().bits(0) }); + r.txaddress().write(|w| w.set_txaddress(0)); // Match on logical address // This config only filter the packets by the address, // so only packages send to the previous address // will finish the reception (TODO: check the explanation) - r.rxaddresses.write(|w| { - w.addr0() - .enabled() - .addr1() - .enabled() - .addr2() - .enabled() - .addr3() - .enabled() - .addr4() - .enabled() + r.rxaddresses().write(|w| { + w.set_addr0(true); + w.set_addr1(true); + w.set_addr2(true); + w.set_addr3(true); + w.set_addr4(true); }); } @@ -241,7 +225,7 @@ impl<'d, T: Instance> Radio<'d, T> { let r = T::regs(); - r.crcpoly.write(|w| unsafe { + r.crcpoly().write(|w| { // Configure the CRC polynomial // Each term in the CRC polynomial is mapped to a bit in this // register which index corresponds to the term's exponent. @@ -249,7 +233,7 @@ impl<'d, T: Instance> Radio<'d, T> { // 1, and bit number 0 of the register content is ignored by // the hardware. The following example is for an 8 bit CRC // polynomial: x8 + x7 + x3 + x2 + 1 = 1 1000 1101 . - w.crcpoly().bits(crc_poly & 0xFFFFFF) + w.set_crcpoly(crc_poly & 0xFFFFFF) }); } @@ -263,7 +247,7 @@ impl<'d, T: Instance> Radio<'d, T> { let r = T::regs(); - r.crcinit.write(|w| unsafe { w.crcinit().bits(crc_init & 0xFFFFFF) }); + r.crcinit().write(|w| w.set_crcinit(crc_init & 0xFFFFFF)); } /// Set the radio tx power @@ -274,7 +258,7 @@ impl<'d, T: Instance> Radio<'d, T> { let r = T::regs(); - r.txpower.write(|w| w.txpower().variant(tx_power)); + r.txpower().write(|w| w.set_txpower(tx_power)); } /// Set buffer to read/write @@ -294,7 +278,7 @@ impl<'d, T: Instance> Radio<'d, T> { let ptr = buffer.as_ptr(); // Configure the payload - r.packetptr.write(|w| unsafe { w.bits(ptr as u32) }); + r.packetptr().write_value(ptr as u32); Ok(()) } @@ -310,7 +294,7 @@ impl<'d, T: Instance> Radio<'d, T> { // Initialize the transmission // trace!("txen"); - r.tasks_txen.write(|w| unsafe { w.bits(1) }); + r.tasks_txen().write_value(1); }) .await; @@ -327,7 +311,7 @@ impl<'d, T: Instance> Radio<'d, T> { self.trigger_and_wait_end(move || { // Initialize the transmission // trace!("rxen"); - r.tasks_rxen.write(|w| unsafe { w.bits(1) }); + r.tasks_rxen().write_value(1); }) .await; @@ -344,21 +328,21 @@ impl<'d, T: Instance> Radio<'d, T> { let drop = OnDrop::new(|| { trace!("radio drop: stopping"); - r.intenclr.write(|w| w.end().clear()); + r.intenclr().write(|w| w.set_end(true)); - r.tasks_stop.write(|w| unsafe { w.bits(1) }); + r.tasks_stop().write_value(1); - r.events_end.reset(); + r.events_end().write_value(0); trace!("radio drop: stopped"); }); // trace!("radio:enable interrupt"); // Clear some remnant side-effects (TODO: check if this is necessary) - r.events_end.reset(); + r.events_end().write_value(0); // Enable interrupt - r.intenset.write(|w| w.end().set()); + r.intenset().write(|w| w.set_end(true)); compiler_fence(Ordering::SeqCst); @@ -368,7 +352,7 @@ impl<'d, T: Instance> Radio<'d, T> { // On poll check if interrupt happen poll_fn(|cx| { s.event_waker.register(cx.waker()); - if r.events_end.read().bits() == 1 { + if r.events_end().read() == 1 { // trace!("radio:end"); return core::task::Poll::Ready(()); } @@ -377,7 +361,7 @@ impl<'d, T: Instance> Radio<'d, T> { .await; compiler_fence(Ordering::SeqCst); - r.events_end.reset(); // ACK + r.events_end().write_value(0); // ACK // Everthing ends fine, so it disable the drop drop.defuse(); @@ -392,15 +376,15 @@ impl<'d, T: Instance> Radio<'d, T> { if self.state() != RadioState::DISABLED { trace!("radio:disable"); // Trigger the disable task - r.tasks_disable.write(|w| unsafe { w.bits(1) }); + r.tasks_disable().write_value(1); // Wait until the radio is disabled - while r.events_disabled.read().bits() == 0 {} + while r.events_disabled().read() == 0 {} compiler_fence(Ordering::SeqCst); // Acknowledge it - r.events_disabled.reset(); + r.events_disabled().write_value(0); } } } diff --git a/embassy-nrf/src/radio/ieee802154.rs b/embassy-nrf/src/radio/ieee802154.rs index 298f8a574..083842f4a 100644 --- a/embassy-nrf/src/radio/ieee802154.rs +++ b/embassy-nrf/src/radio/ieee802154.rs @@ -9,6 +9,7 @@ use embassy_hal_internal::{into_ref, PeripheralRef}; use super::{state, Error, Instance, InterruptHandler, RadioState, TxPower}; use crate::interrupt::typelevel::Interrupt; use crate::interrupt::{self}; +use crate::pac::radio::vals; use crate::Peripheral; /// Default (IEEE compliant) Start of Frame Delimiter @@ -47,58 +48,47 @@ impl<'d, T: Instance> Radio<'d, T> { let r = T::regs(); // Disable and enable to reset peripheral - r.power.write(|w| w.power().disabled()); - r.power.write(|w| w.power().enabled()); + r.power().write(|w| w.set_power(false)); + r.power().write(|w| w.set_power(true)); // Enable 802.15.4 mode - r.mode.write(|w| w.mode().ieee802154_250kbit()); + r.mode().write(|w| w.set_mode(vals::Mode::IEEE802154_250KBIT)); // Configure CRC skip address - r.crccnf.write(|w| w.len().two().skipaddr().ieee802154()); - unsafe { - // Configure CRC polynomial and init - r.crcpoly.write(|w| w.crcpoly().bits(0x0001_1021)); - r.crcinit.write(|w| w.crcinit().bits(0)); - r.pcnf0.write(|w| { - // 8-bit on air length - w.lflen() - .bits(8) - // Zero bytes S0 field length - .s0len() - .clear_bit() - // Zero bytes S1 field length - .s1len() - .bits(0) - // Do not include S1 field in RAM if S1 length > 0 - .s1incl() - .clear_bit() - // Zero code Indicator length - .cilen() - .bits(0) - // 32-bit zero preamble - .plen() - ._32bit_zero() - // Include CRC in length - .crcinc() - .include() - }); - r.pcnf1.write(|w| { - // Maximum packet length - w.maxlen() - .bits(Packet::MAX_PSDU_LEN) - // Zero static length - .statlen() - .bits(0) - // Zero base address length - .balen() - .bits(0) - // Little-endian - .endian() - .clear_bit() - // Disable packet whitening - .whiteen() - .clear_bit() - }); - } + r.crccnf().write(|w| { + w.set_len(vals::Len::TWO); + w.set_skipaddr(vals::Skipaddr::IEEE802154); + }); + // Configure CRC polynomial and init + r.crcpoly().write(|w| w.set_crcpoly(0x0001_1021)); + r.crcinit().write(|w| w.set_crcinit(0)); + r.pcnf0().write(|w| { + // 8-bit on air length + w.set_lflen(8); + // Zero bytes S0 field length + w.set_s0len(false); + // Zero bytes S1 field length + w.set_s1len(0); + // Do not include S1 field in RAM if S1 length > 0 + w.set_s1incl(vals::S1incl::AUTOMATIC); + // Zero code Indicator length + w.set_cilen(0); + // 32-bit zero preamble + w.set_plen(vals::Plen::_32BIT_ZERO); + // Include CRC in length + w.set_crcinc(vals::Crcinc::INCLUDE); + }); + r.pcnf1().write(|w| { + // Maximum packet length + w.set_maxlen(Packet::MAX_PSDU_LEN); + // Zero static length + w.set_statlen(0); + // Zero base address length + w.set_balen(0); + // Little-endian + w.set_endian(vals::Endian::LITTLE); + // Disable packet whitening + w.set_whiteen(false); + }); // Enable NVIC interrupt T::Interrupt::unpend(); @@ -125,8 +115,10 @@ impl<'d, T: Instance> Radio<'d, T> { } let frequency_offset = (channel - 10) * 5; self.needs_enable = true; - r.frequency - .write(|w| unsafe { w.frequency().bits(frequency_offset).map().default() }); + r.frequency().write(|w| { + w.set_frequency(frequency_offset); + w.set_map(vals::Map::DEFAULT); + }); } /// Changes the Clear Channel Assessment method @@ -134,12 +126,14 @@ impl<'d, T: Instance> Radio<'d, T> { let r = T::regs(); self.needs_enable = true; match cca { - Cca::CarrierSense => r.ccactrl.write(|w| w.ccamode().carrier_mode()), + Cca::CarrierSense => r.ccactrl().write(|w| w.set_ccamode(vals::Ccamode::CARRIER_MODE)), Cca::EnergyDetection { ed_threshold } => { // "[ED] is enabled by first configuring the field CCAMODE=EdMode in CCACTRL // and writing the CCAEDTHRES field to a chosen value." - r.ccactrl - .write(|w| unsafe { w.ccamode().ed_mode().ccaedthres().bits(ed_threshold) }); + r.ccactrl().write(|w| { + w.set_ccamode(vals::Ccamode::ED_MODE); + w.set_ccaedthres(ed_threshold); + }); } } } @@ -147,13 +141,13 @@ impl<'d, T: Instance> Radio<'d, T> { /// Changes the Start of Frame Delimiter (SFD) pub fn set_sfd(&mut self, sfd: u8) { let r = T::regs(); - r.sfd.write(|w| unsafe { w.sfd().bits(sfd) }); + r.sfd().write(|w| w.set_sfd(sfd)); } /// Clear interrupts pub fn clear_all_interrupts(&mut self) { let r = T::regs(); - r.intenclr.write(|w| unsafe { w.bits(0xffff_ffff) }); + r.intenclr().write(|w| w.0 = 0xffff_ffff); } /// Changes the radio transmission power @@ -163,43 +157,43 @@ impl<'d, T: Instance> Radio<'d, T> { let tx_power: TxPower = match power { #[cfg(not(any(feature = "nrf52811", feature = "_nrf5340-net")))] - 8 => TxPower::POS8D_BM, + 8 => TxPower::POS8_DBM, #[cfg(not(any(feature = "nrf52811", feature = "_nrf5340-net")))] - 7 => TxPower::POS7D_BM, + 7 => TxPower::POS7_DBM, #[cfg(not(any(feature = "nrf52811", feature = "_nrf5340-net")))] - 6 => TxPower::POS6D_BM, + 6 => TxPower::POS6_DBM, #[cfg(not(any(feature = "nrf52811", feature = "_nrf5340-net")))] - 5 => TxPower::POS5D_BM, + 5 => TxPower::POS5_DBM, #[cfg(not(feature = "_nrf5340-net"))] - 4 => TxPower::POS4D_BM, + 4 => TxPower::POS4_DBM, #[cfg(not(feature = "_nrf5340-net"))] - 3 => TxPower::POS3D_BM, + 3 => TxPower::POS3_DBM, #[cfg(not(any(feature = "nrf52811", feature = "_nrf5340-net")))] - 2 => TxPower::POS2D_BM, - 0 => TxPower::_0D_BM, + 2 => TxPower::POS2_DBM, + 0 => TxPower::_0_DBM, #[cfg(feature = "_nrf5340-net")] - -1 => TxPower::NEG1D_BM, + -1 => TxPower::NEG1_DBM, #[cfg(feature = "_nrf5340-net")] - -2 => TxPower::NEG2D_BM, + -2 => TxPower::NEG2_DBM, #[cfg(feature = "_nrf5340-net")] - -3 => TxPower::NEG3D_BM, - -4 => TxPower::NEG4D_BM, + -3 => TxPower::NEG3_DBM, + -4 => TxPower::NEG4_DBM, #[cfg(feature = "_nrf5340-net")] - -5 => TxPower::NEG5D_BM, + -5 => TxPower::NEG5_DBM, #[cfg(feature = "_nrf5340-net")] - -6 => TxPower::NEG6D_BM, + -6 => TxPower::NEG6_DBM, #[cfg(feature = "_nrf5340-net")] - -7 => TxPower::NEG7D_BM, - -8 => TxPower::NEG8D_BM, - -12 => TxPower::NEG12D_BM, - -16 => TxPower::NEG16D_BM, - -20 => TxPower::NEG20D_BM, - -30 => TxPower::NEG30D_BM, - -40 => TxPower::NEG40D_BM, + -7 => TxPower::NEG7_DBM, + -8 => TxPower::NEG8_DBM, + -12 => TxPower::NEG12_DBM, + -16 => TxPower::NEG16_DBM, + -20 => TxPower::NEG20_DBM, + -30 => TxPower::NEG30_DBM, + -40 => TxPower::NEG40_DBM, _ => panic!("Invalid transmission power value"), }; - r.txpower.write(|w| w.txpower().variant(tx_power)); + r.txpower().write(|w| w.set_txpower(tx_power)); } /// Waits until the radio state matches the given `state` @@ -221,7 +215,7 @@ impl<'d, T: Instance> Radio<'d, T> { RadioState::DISABLED => return, // idle or ramping up RadioState::RX_RU | RadioState::RX_IDLE | RadioState::TX_RU | RadioState::TX_IDLE => { - r.tasks_disable.write(|w| w.tasks_disable().set_bit()); + r.tasks_disable().write_value(1); self.wait_for_radio_state(RadioState::DISABLED); return; } @@ -232,29 +226,30 @@ impl<'d, T: Instance> Radio<'d, T> { } // cancel ongoing transfer or ongoing CCA RadioState::RX => { - r.tasks_ccastop.write(|w| w.tasks_ccastop().set_bit()); - r.tasks_stop.write(|w| w.tasks_stop().set_bit()); + r.tasks_ccastop().write_value(1); + r.tasks_stop().write_value(1); self.wait_for_radio_state(RadioState::RX_IDLE); } RadioState::TX => { - r.tasks_stop.write(|w| w.tasks_stop().set_bit()); + r.tasks_stop().write_value(1); self.wait_for_radio_state(RadioState::TX_IDLE); } + _ => unreachable!(), } } } fn set_buffer(&mut self, buffer: &[u8]) { let r = T::regs(); - r.packetptr.write(|w| unsafe { w.bits(buffer.as_ptr() as u32) }); + r.packetptr().write_value(buffer.as_ptr() as u32); } /// Moves the radio to the RXIDLE state fn receive_prepare(&mut self) { // clear related events - T::regs().events_ccabusy.reset(); - T::regs().events_phyend.reset(); - // NOTE to avoid errata 204 (see rev1 v1.4) we do TX_IDLE -> DISABLED -> RX_IDLE + T::regs().events_ccabusy().write_value(0); + T::regs().events_phyend().write_value(0); + // NOTE to avoid errata 204 (see rev1 v1.4) we do TX_IDLE -> DISABLED -> RXIDLE let disable = match self.state() { RadioState::DISABLED => false, RadioState::RX_IDLE => self.needs_enable, @@ -279,7 +274,7 @@ impl<'d, T: Instance> Radio<'d, T> { // The radio goes through following states when receiving a 802.15.4 packet // // enable RX → ramp up RX → RX idle → Receive → end (PHYEND) - r.shorts.write(|w| w.rxready_start().enabled()); + r.shorts().write(|w| w.set_rxready_start(true)); // set up RX buffer self.set_buffer(packet.buffer.as_mut()); @@ -289,17 +284,17 @@ impl<'d, T: Instance> Radio<'d, T> { match self.state() { // Re-start receiver - RadioState::RX_IDLE => r.tasks_start.write(|w| w.tasks_start().set_bit()), + RadioState::RX_IDLE => r.tasks_start().write_value(1), // Enable receiver - _ => r.tasks_rxen.write(|w| w.tasks_rxen().set_bit()), + _ => r.tasks_rxen().write_value(1), } } /// Cancel receiving packet fn receive_cancel() { let r = T::regs(); - r.shorts.reset(); - r.tasks_stop.write(|w| w.tasks_stop().set_bit()); + r.shorts().write(|_| {}); + r.tasks_stop().write_value(1); loop { match state(r) { RadioState::DISABLED | RadioState::RX_IDLE => break, @@ -329,12 +324,12 @@ impl<'d, T: Instance> Radio<'d, T> { core::future::poll_fn(|cx| { s.event_waker.register(cx.waker()); - if r.events_phyend.read().events_phyend().bit_is_set() { - r.events_phyend.reset(); + if r.events_phyend().read() != 0 { + r.events_phyend().write_value(0); trace!("RX done poll"); return Poll::Ready(()); } else { - r.intenset.write(|w| w.phyend().set()); + r.intenset().write(|w| w.set_phyend(true)); }; Poll::Pending @@ -344,8 +339,8 @@ impl<'d, T: Instance> Radio<'d, T> { dma_end_fence(); dropper.defuse(); - let crc = r.rxcrc.read().rxcrc().bits() as u16; - if r.crcstatus.read().crcstatus().bit_is_set() { + let crc = r.rxcrc().read().rxcrc() as u16; + if r.crcstatus().read().crcstatus() == vals::Crcstatus::CRCOK { Ok(()) } else { Err(Error::CrcFailed(crc)) @@ -387,17 +382,12 @@ impl<'d, T: Instance> Radio<'d, T> { // CCA idle → enable TX → start TX → TX → end (PHYEND) → disabled // // CCA might end up in the event CCABUSY in which there will be no transmission - r.shorts.write(|w| { - w.rxready_ccastart() - .enabled() - .ccaidle_txen() - .enabled() - .txready_start() - .enabled() - .ccabusy_disable() - .enabled() - .phyend_disable() - .enabled() + r.shorts().write(|w| { + w.set_rxready_ccastart(true); + w.set_ccaidle_txen(true); + w.set_txready_start(true); + w.set_ccabusy_disable(true); + w.set_phyend_disable(true); }); // Set transmission buffer @@ -410,27 +400,30 @@ impl<'d, T: Instance> Radio<'d, T> { match self.state() { // Re-start receiver - RadioState::RX_IDLE => r.tasks_ccastart.write(|w| w.tasks_ccastart().set_bit()), + RadioState::RX_IDLE => r.tasks_ccastart().write_value(1), // Enable receiver - _ => r.tasks_rxen.write(|w| w.tasks_rxen().set_bit()), + _ => r.tasks_rxen().write_value(1), } self.clear_all_interrupts(); let result = core::future::poll_fn(|cx| { s.event_waker.register(cx.waker()); - if r.events_phyend.read().events_phyend().bit_is_set() { - r.events_phyend.reset(); - r.events_ccabusy.reset(); + if r.events_phyend().read() != 0 { + r.events_phyend().write_value(0); + r.events_ccabusy().write_value(0); trace!("TX done poll"); return Poll::Ready(TransmitResult::Success); - } else if r.events_ccabusy.read().events_ccabusy().bit_is_set() { - r.events_ccabusy.reset(); + } else if r.events_ccabusy().read() != 0 { + r.events_ccabusy().write_value(0); trace!("TX no CCA"); return Poll::Ready(TransmitResult::ChannelInUse); } - r.intenset.write(|w| w.phyend().set().ccabusy().set()); + r.intenset().write(|w| { + w.set_phyend(true); + w.set_ccabusy(true); + }); Poll::Pending }) diff --git a/embassy-nrf/src/radio/mod.rs b/embassy-nrf/src/radio/mod.rs index 8edca1df2..251f37d3d 100644 --- a/embassy-nrf/src/radio/mod.rs +++ b/embassy-nrf/src/radio/mod.rs @@ -20,8 +20,8 @@ pub mod ieee802154; use core::marker::PhantomData; use embassy_sync::waitqueue::AtomicWaker; -use pac::radio::state::STATE_A as RadioState; -pub use pac::radio::txpower::TXPOWER_A as TxPower; +use pac::radio::vals::State as RadioState; +pub use pac::radio::vals::Txpower as TxPower; use crate::{interrupt, pac, Peripheral}; @@ -52,7 +52,7 @@ impl interrupt::typelevel::Handler for InterruptHandl let r = T::regs(); let s = T::state(); // clear all interrupts - r.intenclr.write(|w| w.bits(0xffff_ffff)); + r.intenclr().write(|w| w.0 = 0xffff_ffff); s.event_waker.wake(); } } @@ -70,15 +70,15 @@ impl State { } pub(crate) trait SealedInstance { - fn regs() -> &'static crate::pac::radio::RegisterBlock; + fn regs() -> crate::pac::radio::Radio; fn state() -> &'static State; } macro_rules! impl_radio { ($type:ident, $pac_type:ident, $irq:ident) => { impl crate::radio::SealedInstance for peripherals::$type { - fn regs() -> &'static pac::radio::RegisterBlock { - unsafe { &*pac::$pac_type::ptr() } + fn regs() -> crate::pac::radio::Radio { + pac::$pac_type } fn state() -> &'static crate::radio::State { @@ -100,9 +100,6 @@ pub trait Instance: Peripheral

+ SealedInstance + 'static + Send { } /// Get the state of the radio -pub(crate) fn state(radio: &pac::radio::RegisterBlock) -> RadioState { - match radio.state.read().state().variant() { - Some(state) => state, - None => unreachable!(), - } +pub(crate) fn state(radio: pac::radio::Radio) -> RadioState { + radio.state().read().state() } diff --git a/embassy-nrf/src/rng.rs b/embassy-nrf/src/rng.rs index ff61e08f3..7a98ab2fb 100644 --- a/embassy-nrf/src/rng.rs +++ b/embassy-nrf/src/rng.rs @@ -14,7 +14,7 @@ use embassy_hal_internal::{into_ref, PeripheralRef}; use embassy_sync::waitqueue::WakerRegistration; use crate::interrupt::typelevel::Interrupt; -use crate::{interrupt, Peripheral}; +use crate::{interrupt, pac, Peripheral}; /// Interrupt handler. pub struct InterruptHandler { @@ -26,7 +26,7 @@ impl interrupt::typelevel::Handler for InterruptHandl let r = T::regs(); // Clear the event. - r.events_valrdy.reset(); + r.events_valrdy().write_value(0); // Mutate the slice within a critical section, // so that the future isn't dropped in between us loading the pointer and actually dereferencing it. @@ -40,7 +40,7 @@ impl interrupt::typelevel::Handler for InterruptHandl // The safety contract of `Rng::new` means that the future can't have been dropped // without calling its destructor. unsafe { - *state.ptr = r.value.read().value().bits(); + *state.ptr = r.value().read().value(); state.ptr = state.ptr.add(1); } @@ -84,19 +84,19 @@ impl<'d, T: Instance> Rng<'d, T> { } fn stop(&self) { - T::regs().tasks_stop.write(|w| unsafe { w.bits(1) }) + T::regs().tasks_stop().write_value(1) } fn start(&self) { - T::regs().tasks_start.write(|w| unsafe { w.bits(1) }) + T::regs().tasks_start().write_value(1) } fn enable_irq(&self) { - T::regs().intenset.write(|w| w.valrdy().set()); + T::regs().intenset().write(|w| w.set_valrdy(true)); } fn disable_irq(&self) { - T::regs().intenclr.write(|w| w.valrdy().clear()); + T::regs().intenclr().write(|w| w.set_valrdy(true)); } /// Enable or disable the RNG's bias correction. @@ -106,7 +106,7 @@ impl<'d, T: Instance> Rng<'d, T> { /// /// Defaults to disabled. pub fn set_bias_correction(&self, enable: bool) { - T::regs().config.write(|w| w.dercen().bit(enable)) + T::regs().config().write(|w| w.set_dercen(enable)) } /// Fill the buffer with random bytes. @@ -162,9 +162,9 @@ impl<'d, T: Instance> Rng<'d, T> { for byte in dest.iter_mut() { let regs = T::regs(); - while regs.events_valrdy.read().bits() == 0 {} - regs.events_valrdy.reset(); - *byte = regs.value.read().value().bits(); + while regs.events_valrdy().read() == 0 {} + regs.events_valrdy().write_value(0); + *byte = regs.value().read().value(); } self.stop(); @@ -244,7 +244,7 @@ impl InnerState { } pub(crate) trait SealedInstance { - fn regs() -> &'static crate::pac::rng::RegisterBlock; + fn regs() -> pac::rng::Rng; fn state() -> &'static State; } @@ -258,8 +258,8 @@ pub trait Instance: Peripheral

+ SealedInstance + 'static + Send { macro_rules! impl_rng { ($type:ident, $pac_type:ident, $irq:ident) => { impl crate::rng::SealedInstance for peripherals::$type { - fn regs() -> &'static crate::pac::rng::RegisterBlock { - unsafe { &*pac::$pac_type::ptr() } + fn regs() -> crate::pac::rng::Rng { + pac::$pac_type } fn state() -> &'static crate::rng::State { static STATE: crate::rng::State = crate::rng::State::new(); diff --git a/embassy-nrf/src/saadc.rs b/embassy-nrf/src/saadc.rs index bbfa9b3b9..70bda9f70 100644 --- a/embassy-nrf/src/saadc.rs +++ b/embassy-nrf/src/saadc.rs @@ -9,14 +9,10 @@ use core::task::Poll; use embassy_hal_internal::drop::OnDrop; use embassy_hal_internal::{impl_peripheral, into_ref, PeripheralRef}; use embassy_sync::waitqueue::AtomicWaker; -use pac::{saadc, SAADC}; -use saadc::ch::config::{GAIN_A, REFSEL_A, RESP_A, TACQ_A}; -// We treat the positive and negative channels with the same enum values to keep our type tidy and given they are the same -pub(crate) use saadc::ch::pselp::PSELP_A as InputChannel; -use saadc::oversample::OVERSAMPLE_A; -use saadc::resolution::VAL_A; +pub(crate) use vals::Psel as InputChannel; use crate::interrupt::InterruptExt; +use crate::pac::saadc::vals; use crate::ppi::{ConfigurableChannel, Event, Ppi, Task}; use crate::timer::{Frequency, Instance as TimerInstance, Timer}; use crate::{interrupt, pac, peripherals, Peripheral}; @@ -34,20 +30,20 @@ pub struct InterruptHandler { impl interrupt::typelevel::Handler for InterruptHandler { unsafe fn on_interrupt() { - let r = unsafe { &*SAADC::ptr() }; + let r = pac::SAADC; - if r.events_calibratedone.read().bits() != 0 { - r.intenclr.write(|w| w.calibratedone().clear()); + if r.events_calibratedone().read() != 0 { + r.intenclr().write(|w| w.set_calibratedone(true)); WAKER.wake(); } - if r.events_end.read().bits() != 0 { - r.intenclr.write(|w| w.end().clear()); + if r.events_end().read() != 0 { + r.intenclr().write(|w| w.set_end(true)); WAKER.wake(); } - if r.events_started.read().bits() != 0 { - r.intenclr.write(|w| w.started().clear()); + if r.events_started().read() != 0 { + r.intenclr().write(|w| w.set_started(true)); WAKER.wake(); } } @@ -150,44 +146,36 @@ impl<'d, const N: usize> Saadc<'d, N> { ) -> Self { into_ref!(saadc); - let r = unsafe { &*SAADC::ptr() }; + let r = pac::SAADC; let Config { resolution, oversample } = config; // Configure channels - r.enable.write(|w| w.enable().enabled()); - r.resolution.write(|w| w.val().variant(resolution.into())); - r.oversample.write(|w| w.oversample().variant(oversample.into())); + r.enable().write(|w| w.set_enable(true)); + r.resolution().write(|w| w.set_val(resolution.into())); + r.oversample().write(|w| w.set_oversample(oversample.into())); for (i, cc) in channel_configs.iter().enumerate() { - r.ch[i].pselp.write(|w| w.pselp().variant(cc.p_channel.channel())); + r.ch(i).pselp().write(|w| w.set_pselp(cc.p_channel.channel())); if let Some(n_channel) = &cc.n_channel { - r.ch[i] - .pseln - .write(|w| unsafe { w.pseln().bits(n_channel.channel() as u8) }); + r.ch(i).pseln().write(|w| w.set_pseln(n_channel.channel())); } - r.ch[i].config.write(|w| { - w.refsel().variant(cc.reference.into()); - w.gain().variant(cc.gain.into()); - w.tacq().variant(cc.time.into()); - if cc.n_channel.is_none() { - w.mode().se(); - } else { - w.mode().diff(); - } - w.resp().variant(cc.resistor.into()); - w.resn().bypass(); - if !matches!(oversample, Oversample::BYPASS) { - w.burst().enabled(); - } else { - w.burst().disabled(); - } - w + r.ch(i).config().write(|w| { + w.set_refsel(cc.reference.into()); + w.set_gain(cc.gain.into()); + w.set_tacq(cc.time.into()); + w.set_mode(match cc.n_channel { + None => vals::ConfigMode::SE, + Some(_) => vals::ConfigMode::DIFF, + }); + w.set_resp(cc.resistor.into()); + w.set_resn(vals::Resn::BYPASS); + w.set_burst(!matches!(oversample, Oversample::BYPASS)); }); } // Disable all events interrupts - r.intenclr.write(|w| unsafe { w.bits(0x003F_FFFF) }); + r.intenclr().write(|w| w.0 = 0x003F_FFFF); interrupt::SAADC.unpend(); unsafe { interrupt::SAADC.enable() }; @@ -195,8 +183,8 @@ impl<'d, const N: usize> Saadc<'d, N> { Self { _p: saadc } } - fn regs() -> &'static saadc::RegisterBlock { - unsafe { &*SAADC::ptr() } + fn regs() -> pac::saadc::Saadc { + pac::SAADC } /// Perform SAADC calibration. Completes when done. @@ -204,13 +192,13 @@ impl<'d, const N: usize> Saadc<'d, N> { let r = Self::regs(); // Reset and enable the end event - r.events_calibratedone.reset(); - r.intenset.write(|w| w.calibratedone().set()); + r.events_calibratedone().write_value(0); + r.intenset().write(|w| w.set_calibratedone(true)); // Order is important compiler_fence(Ordering::SeqCst); - r.tasks_calibrateoffset.write(|w| unsafe { w.bits(1) }); + r.tasks_calibrateoffset().write_value(1); // Wait for 'calibratedone' event. poll_fn(|cx| { @@ -218,8 +206,8 @@ impl<'d, const N: usize> Saadc<'d, N> { WAKER.register(cx.waker()); - if r.events_calibratedone.read().bits() != 0 { - r.events_calibratedone.reset(); + if r.events_calibratedone().read() != 0 { + r.events_calibratedone().write_value(0); return Poll::Ready(()); } @@ -239,19 +227,19 @@ impl<'d, const N: usize> Saadc<'d, N> { let r = Self::regs(); // Set up the DMA - r.result.ptr.write(|w| unsafe { w.ptr().bits(buf.as_mut_ptr() as u32) }); - r.result.maxcnt.write(|w| unsafe { w.maxcnt().bits(N as _) }); + r.result().ptr().write_value(buf.as_mut_ptr() as u32); + r.result().maxcnt().write(|w| w.set_maxcnt(N as _)); // Reset and enable the end event - r.events_end.reset(); - r.intenset.write(|w| w.end().set()); + r.events_end().write_value(0); + r.intenset().write(|w| w.set_end(true)); // Don't reorder the ADC start event before the previous writes. Hopefully self // wouldn't happen anyway. compiler_fence(Ordering::SeqCst); - r.tasks_start.write(|w| unsafe { w.bits(1) }); - r.tasks_sample.write(|w| unsafe { w.bits(1) }); + r.tasks_start().write_value(1); + r.tasks_sample().write_value(1); // Wait for 'end' event. poll_fn(|cx| { @@ -259,8 +247,8 @@ impl<'d, const N: usize> Saadc<'d, N> { WAKER.register(cx.waker()); - if r.events_end.read().bits() != 0 { - r.events_end.reset(); + if r.events_end().read() != 0 { + r.events_end().write_value(0); return Poll::Ready(()); } @@ -311,8 +299,11 @@ impl<'d, const N: usize> Saadc<'d, N> { // We want the task start to effectively short with the last one ending so // we don't miss any samples. It'd be great for the SAADC to offer a SHORTS // register instead, but it doesn't, so we must use PPI. - let mut start_ppi = - Ppi::new_one_to_one(ppi_ch1, Event::from_reg(&r.events_end), Task::from_reg(&r.tasks_start)); + let mut start_ppi = Ppi::new_one_to_one( + ppi_ch1, + Event::from_reg(r.events_end()), + Task::from_reg(r.tasks_start()), + ); start_ppi.enable(); let timer = Timer::new(timer); @@ -322,7 +313,7 @@ impl<'d, const N: usize> Saadc<'d, N> { let timer_cc = timer.cc(0); - let mut sample_ppi = Ppi::new_one_to_one(ppi_ch2, timer_cc.event_compare(), Task::from_reg(&r.tasks_sample)); + let mut sample_ppi = Ppi::new_one_to_one(ppi_ch2, timer_cc.event_compare(), Task::from_reg(r.tasks_sample())); timer.start(); @@ -355,43 +346,37 @@ impl<'d, const N: usize> Saadc<'d, N> { // Establish mode and sample rate match sample_rate_divisor { Some(sr) => { - r.samplerate.write(|w| unsafe { - w.cc().bits(sr); - w.mode().timers(); - w + r.samplerate().write(|w| { + w.set_cc(sr); + w.set_mode(vals::SamplerateMode::TIMERS); }); - r.tasks_sample.write(|w| unsafe { w.bits(1) }); // Need to kick-start the internal timer + r.tasks_sample().write_value(1); // Need to kick-start the internal timer } - None => r.samplerate.write(|w| unsafe { - w.cc().bits(0); - w.mode().task(); - w + None => r.samplerate().write(|w| { + w.set_cc(0); + w.set_mode(vals::SamplerateMode::TASK); }), } // Set up the initial DMA - r.result - .ptr - .write(|w| unsafe { w.ptr().bits(bufs[0].as_mut_ptr() as u32) }); - r.result.maxcnt.write(|w| unsafe { w.maxcnt().bits((N0 * N) as _) }); + r.result().ptr().write_value(bufs[0].as_mut_ptr() as u32); + r.result().maxcnt().write(|w| w.set_maxcnt((N0 * N) as _)); // Reset and enable the events - r.events_end.reset(); - r.events_started.reset(); - r.intenset.write(|w| { - w.end().set(); - w.started().set(); - w + r.events_end().write_value(0); + r.events_started().write_value(0); + r.intenset().write(|w| { + w.set_end(true); + w.set_started(true); }); // Don't reorder the ADC start event before the previous writes. Hopefully self // wouldn't happen anyway. compiler_fence(Ordering::SeqCst); - r.tasks_start.write(|w| unsafe { w.bits(1) }); + r.tasks_start().write_value(1); let mut inited = false; - let mut current_buffer = 0; // Wait for events and complete when the sampler indicates it has had enough. @@ -400,11 +385,11 @@ impl<'d, const N: usize> Saadc<'d, N> { WAKER.register(cx.waker()); - if r.events_end.read().bits() != 0 { + if r.events_end().read() != 0 { compiler_fence(Ordering::SeqCst); - r.events_end.reset(); - r.intenset.write(|w| w.end().set()); + r.events_end().write_value(0); + r.intenset().write(|w| w.set_end(true)); match callback(&bufs[current_buffer]) { CallbackResult::Continue => { @@ -417,9 +402,9 @@ impl<'d, const N: usize> Saadc<'d, N> { } } - if r.events_started.read().bits() != 0 { - r.events_started.reset(); - r.intenset.write(|w| w.started().set()); + if r.events_started().read() != 0 { + r.events_started().write_value(0); + r.intenset().write(|w| w.set_started(true)); if !inited { init(); @@ -427,9 +412,7 @@ impl<'d, const N: usize> Saadc<'d, N> { } let next_buffer = 1 - current_buffer; - r.result - .ptr - .write(|w| unsafe { w.ptr().bits(bufs[next_buffer].as_mut_ptr() as u32) }); + r.result().ptr().write_value(bufs[next_buffer].as_mut_ptr() as u32); } Poll::Pending @@ -447,11 +430,11 @@ impl<'d, const N: usize> Saadc<'d, N> { compiler_fence(Ordering::SeqCst); - r.events_stopped.reset(); - r.tasks_stop.write(|w| unsafe { w.bits(1) }); + r.events_stopped().write_value(0); + r.tasks_stop().write_value(1); - while r.events_stopped.read().bits() == 0 {} - r.events_stopped.reset(); + while r.events_stopped().read() == 0 {} + r.events_stopped().write_value(0); } } @@ -481,21 +464,21 @@ impl<'d> Saadc<'d, 1> { impl<'d, const N: usize> Drop for Saadc<'d, N> { fn drop(&mut self) { let r = Self::regs(); - r.enable.write(|w| w.enable().disabled()); + r.enable().write(|w| w.set_enable(false)); } } -impl From for GAIN_A { +impl From for vals::Gain { fn from(gain: Gain) -> Self { match gain { - Gain::GAIN1_6 => GAIN_A::GAIN1_6, - Gain::GAIN1_5 => GAIN_A::GAIN1_5, - Gain::GAIN1_4 => GAIN_A::GAIN1_4, - Gain::GAIN1_3 => GAIN_A::GAIN1_3, - Gain::GAIN1_2 => GAIN_A::GAIN1_2, - Gain::GAIN1 => GAIN_A::GAIN1, - Gain::GAIN2 => GAIN_A::GAIN2, - Gain::GAIN4 => GAIN_A::GAIN4, + Gain::GAIN1_6 => vals::Gain::GAIN1_6, + Gain::GAIN1_5 => vals::Gain::GAIN1_5, + Gain::GAIN1_4 => vals::Gain::GAIN1_4, + Gain::GAIN1_3 => vals::Gain::GAIN1_3, + Gain::GAIN1_2 => vals::Gain::GAIN1_2, + Gain::GAIN1 => vals::Gain::GAIN1, + Gain::GAIN2 => vals::Gain::GAIN2, + Gain::GAIN4 => vals::Gain::GAIN4, } } } @@ -522,11 +505,11 @@ pub enum Gain { GAIN4 = 7, } -impl From for REFSEL_A { +impl From for vals::Refsel { fn from(reference: Reference) -> Self { match reference { - Reference::INTERNAL => REFSEL_A::INTERNAL, - Reference::VDD1_4 => REFSEL_A::VDD1_4, + Reference::INTERNAL => vals::Refsel::INTERNAL, + Reference::VDD1_4 => vals::Refsel::VDD1_4, } } } @@ -541,13 +524,13 @@ pub enum Reference { VDD1_4 = 1, } -impl From for RESP_A { +impl From for vals::Resp { fn from(resistor: Resistor) -> Self { match resistor { - Resistor::BYPASS => RESP_A::BYPASS, - Resistor::PULLDOWN => RESP_A::PULLDOWN, - Resistor::PULLUP => RESP_A::PULLUP, - Resistor::VDD1_2 => RESP_A::VDD1_2, + Resistor::BYPASS => vals::Resp::BYPASS, + Resistor::PULLDOWN => vals::Resp::PULLDOWN, + Resistor::PULLUP => vals::Resp::PULLUP, + Resistor::VDD1_2 => vals::Resp::VDD1_2, } } } @@ -566,15 +549,15 @@ pub enum Resistor { VDD1_2 = 3, } -impl From

+ SealedInstance + 'static { macro_rules! impl_spim { ($type:ident, $pac_type:ident, $irq:ident) => { impl crate::spim::SealedInstance for peripherals::$type { - fn regs() -> &'static pac::spim0::RegisterBlock { - unsafe { &*pac::$pac_type::ptr() } + fn regs() -> pac::spim::Spim { + pac::$pac_type } fn state() -> &'static crate::spim::State { static STATE: crate::spim::State = crate::spim::State::new(); @@ -621,40 +618,35 @@ impl<'d, T: Instance> SetConfig for Spim<'d, T> { let r = T::regs(); // Configure mode. let mode = config.mode; - r.config.write(|w| { + r.config().write(|w| { + w.set_order(config.bit_order); match mode { MODE_0 => { - w.order().variant(config.bit_order); - w.cpol().active_high(); - w.cpha().leading(); + w.set_cpol(vals::Cpol::ACTIVE_HIGH); + w.set_cpha(vals::Cpha::LEADING); } MODE_1 => { - w.order().variant(config.bit_order); - w.cpol().active_high(); - w.cpha().trailing(); + w.set_cpol(vals::Cpol::ACTIVE_HIGH); + w.set_cpha(vals::Cpha::TRAILING); } MODE_2 => { - w.order().variant(config.bit_order); - w.cpol().active_low(); - w.cpha().leading(); + w.set_cpol(vals::Cpol::ACTIVE_LOW); + w.set_cpha(vals::Cpha::LEADING); } MODE_3 => { - w.order().variant(config.bit_order); - w.cpol().active_low(); - w.cpha().trailing(); + w.set_cpol(vals::Cpol::ACTIVE_LOW); + w.set_cpha(vals::Cpha::TRAILING); } } - - w }); // Configure frequency. let frequency = config.frequency; - r.frequency.write(|w| w.frequency().variant(frequency)); + r.frequency().write(|w| w.set_frequency(frequency)); // Set over-read character let orc = config.orc; - r.orc.write(|w| unsafe { w.orc().bits(orc) }); + r.orc().write(|w| w.set_orc(orc)); Ok(()) } diff --git a/embassy-nrf/src/spis.rs b/embassy-nrf/src/spis.rs index e98b34369..a363e5909 100644 --- a/embassy-nrf/src/spis.rs +++ b/embassy-nrf/src/spis.rs @@ -10,11 +10,13 @@ use embassy_embedded_hal::SetConfig; use embassy_hal_internal::{into_ref, PeripheralRef}; use embassy_sync::waitqueue::AtomicWaker; pub use embedded_hal_02::spi::{Mode, Phase, Polarity, MODE_0, MODE_1, MODE_2, MODE_3}; -pub use pac::spis0::config::ORDER_A as BitOrder; +pub use pac::spis::vals::Order as BitOrder; use crate::chip::{EASY_DMA_SIZE, FORCE_COPY_BUFFER_SIZE}; -use crate::gpio::{self, AnyPin, Pin as GpioPin, SealedPin as _}; +use crate::gpio::{self, convert_drive, AnyPin, OutputDrive, Pin as GpioPin, SealedPin as _}; use crate::interrupt::typelevel::Interrupt; +use crate::pac::gpio::vals as gpiovals; +use crate::pac::spis::vals; use crate::util::slice_in_ram_or; use crate::{interrupt, pac, Peripheral}; @@ -54,6 +56,9 @@ pub struct Config { /// Automatically make the firmware side acquire the semaphore on transfer end. pub auto_acquire: bool, + + /// Drive strength for the MISO line. + pub miso_drive: OutputDrive, } impl Default for Config { @@ -64,6 +69,7 @@ impl Default for Config { orc: 0x00, def: 0x00, auto_acquire: true, + miso_drive: OutputDrive::HighDrive, } } } @@ -78,14 +84,14 @@ impl interrupt::typelevel::Handler for InterruptHandl let r = T::regs(); let s = T::state(); - if r.events_end.read().bits() != 0 { + if r.events_end().read() != 0 { s.waker.wake(); - r.intenclr.write(|w| w.end().clear()); + r.intenclr().write(|w| w.set_end(true)); } - if r.events_acquired.read().bits() != 0 { + if r.events_acquired().read() != 0 { s.waker.wake(); - r.intenclr.write(|w| w.acquired().clear()); + r.intenclr().write(|w| w.set_acquired(true)); } } } @@ -184,23 +190,26 @@ impl<'d, T: Instance> Spis<'d, T> { let r = T::regs(); // Configure pins. - cs.conf().write(|w| w.input().connect().drive().h0h1()); - r.psel.csn.write(|w| unsafe { w.bits(cs.psel_bits()) }); + cs.conf().write(|w| w.set_input(gpiovals::Input::CONNECT)); + r.psel().csn().write_value(cs.psel_bits()); if let Some(sck) = &sck { - sck.conf().write(|w| w.input().connect().drive().h0h1()); - r.psel.sck.write(|w| unsafe { w.bits(sck.psel_bits()) }); + sck.conf().write(|w| w.set_input(gpiovals::Input::CONNECT)); + r.psel().sck().write_value(sck.psel_bits()); } if let Some(mosi) = &mosi { - mosi.conf().write(|w| w.input().connect().drive().h0h1()); - r.psel.mosi.write(|w| unsafe { w.bits(mosi.psel_bits()) }); + mosi.conf().write(|w| w.set_input(gpiovals::Input::CONNECT)); + r.psel().mosi().write_value(mosi.psel_bits()); } if let Some(miso) = &miso { - miso.conf().write(|w| w.dir().output().drive().h0h1()); - r.psel.miso.write(|w| unsafe { w.bits(miso.psel_bits()) }); + miso.conf().write(|w| { + w.set_dir(gpiovals::Dir::OUTPUT); + w.set_drive(convert_drive(config.miso_drive)) + }); + r.psel().miso().write_value(miso.psel_bits()); } // Enable SPIS instance. - r.enable.write(|w| w.enable().enabled()); + r.enable().write(|w| w.set_enable(vals::Enable::ENABLED)); let mut spis = Self { _p: spis }; @@ -208,7 +217,7 @@ impl<'d, T: Instance> Spis<'d, T> { Self::set_config(&mut spis, &config).unwrap(); // Disable all events interrupts. - r.intenclr.write(|w| unsafe { w.bits(0xFFFF_FFFF) }); + r.intenclr().write(|w| w.0 = 0xFFFF_FFFF); T::Interrupt::unpend(); unsafe { T::Interrupt::enable() }; @@ -229,21 +238,21 @@ impl<'d, T: Instance> Spis<'d, T> { if tx.len() > EASY_DMA_SIZE { return Err(Error::TxBufferTooLong); } - r.txd.ptr.write(|w| unsafe { w.ptr().bits(tx as *const u8 as _) }); - r.txd.maxcnt.write(|w| unsafe { w.maxcnt().bits(tx.len() as _) }); + r.txd().ptr().write_value(tx as *const u8 as _); + r.txd().maxcnt().write(|w| w.set_maxcnt(tx.len() as _)); // Set up the DMA read. if rx.len() > EASY_DMA_SIZE { return Err(Error::RxBufferTooLong); } - r.rxd.ptr.write(|w| unsafe { w.ptr().bits(rx as *mut u8 as _) }); - r.rxd.maxcnt.write(|w| unsafe { w.maxcnt().bits(rx.len() as _) }); + r.rxd().ptr().write_value(rx as *mut u8 as _); + r.rxd().maxcnt().write(|w| w.set_maxcnt(rx.len() as _)); // Reset end event. - r.events_end.reset(); + r.events_end().write_value(0); // Release the semaphore. - r.tasks_release.write(|w| unsafe { w.bits(1) }); + r.tasks_release().write_value(1); Ok(()) } @@ -253,20 +262,20 @@ impl<'d, T: Instance> Spis<'d, T> { let r = T::regs(); // Acquire semaphore. - if r.semstat.read().bits() != 1 { - r.events_acquired.reset(); - r.tasks_acquire.write(|w| unsafe { w.bits(1) }); + if r.semstat().read().0 != 1 { + r.events_acquired().write_value(0); + r.tasks_acquire().write_value(1); // Wait until CPU has acquired the semaphore. - while r.semstat.read().bits() != 1 {} + while r.semstat().read().0 != 1 {} } self.prepare(rx, tx)?; // Wait for 'end' event. - while r.events_end.read().bits() == 0 {} + while r.events_end().read() == 0 {} - let n_rx = r.rxd.amount.read().bits() as usize; - let n_tx = r.txd.amount.read().bits() as usize; + let n_rx = r.rxd().amount().read().0 as usize; + let n_tx = r.txd().amount().read().0 as usize; compiler_fence(Ordering::SeqCst); @@ -291,22 +300,25 @@ impl<'d, T: Instance> Spis<'d, T> { let s = T::state(); // Clear status register. - r.status.write(|w| w.overflow().clear().overread().clear()); + r.status().write(|w| { + w.set_overflow(true); + w.set_overread(true); + }); // Acquire semaphore. - if r.semstat.read().bits() != 1 { + if r.semstat().read().0 != 1 { // Reset and enable the acquire event. - r.events_acquired.reset(); - r.intenset.write(|w| w.acquired().set()); + r.events_acquired().write_value(0); + r.intenset().write(|w| w.set_acquired(true)); // Request acquiring the SPIS semaphore. - r.tasks_acquire.write(|w| unsafe { w.bits(1) }); + r.tasks_acquire().write_value(1); // Wait until CPU has acquired the semaphore. poll_fn(|cx| { s.waker.register(cx.waker()); - if r.events_acquired.read().bits() == 1 { - r.events_acquired.reset(); + if r.events_acquired().read() == 1 { + r.events_acquired().write_value(0); return Poll::Ready(()); } Poll::Pending @@ -317,19 +329,19 @@ impl<'d, T: Instance> Spis<'d, T> { self.prepare(rx, tx)?; // Wait for 'end' event. - r.intenset.write(|w| w.end().set()); + r.intenset().write(|w| w.set_end(true)); poll_fn(|cx| { s.waker.register(cx.waker()); - if r.events_end.read().bits() != 0 { - r.events_end.reset(); + if r.events_end().read() != 0 { + r.events_end().write_value(0); return Poll::Ready(()); } Poll::Pending }) .await; - let n_rx = r.rxd.amount.read().bits() as usize; - let n_tx = r.txd.amount.read().bits() as usize; + let n_rx = r.rxd().amount().read().0 as usize; + let n_tx = r.txd().amount().read().0 as usize; compiler_fence(Ordering::SeqCst); @@ -428,12 +440,12 @@ impl<'d, T: Instance> Spis<'d, T> { /// Checks if last transaction overread. pub fn is_overread(&mut self) -> bool { - T::regs().status.read().overread().is_present() + T::regs().status().read().overread() } /// Checks if last transaction overflowed. pub fn is_overflow(&mut self) -> bool { - T::regs().status.read().overflow().is_present() + T::regs().status().read().overflow() } } @@ -443,12 +455,12 @@ impl<'d, T: Instance> Drop for Spis<'d, T> { // Disable let r = T::regs(); - r.enable.write(|w| w.enable().disabled()); + r.enable().write(|w| w.set_enable(vals::Enable::DISABLED)); - gpio::deconfigure_pin(r.psel.sck.read().bits()); - gpio::deconfigure_pin(r.psel.csn.read().bits()); - gpio::deconfigure_pin(r.psel.miso.read().bits()); - gpio::deconfigure_pin(r.psel.mosi.read().bits()); + gpio::deconfigure_pin(r.psel().sck().read()); + gpio::deconfigure_pin(r.psel().csn().read()); + gpio::deconfigure_pin(r.psel().miso().read()); + gpio::deconfigure_pin(r.psel().mosi().read()); trace!("spis drop: done"); } @@ -467,7 +479,7 @@ impl State { } pub(crate) trait SealedInstance { - fn regs() -> &'static pac::spis0::RegisterBlock; + fn regs() -> pac::spis::Spis; fn state() -> &'static State; } @@ -481,8 +493,8 @@ pub trait Instance: Peripheral

+ SealedInstance + 'static { macro_rules! impl_spis { ($type:ident, $pac_type:ident, $irq:ident) => { impl crate::spis::SealedInstance for peripherals::$type { - fn regs() -> &'static pac::spis0::RegisterBlock { - unsafe { &*pac::$pac_type::ptr() } + fn regs() -> pac::spis::Spis { + pac::$pac_type } fn state() -> &'static crate::spis::State { static STATE: crate::spis::State = crate::spis::State::new(); @@ -504,44 +516,39 @@ impl<'d, T: Instance> SetConfig for Spis<'d, T> { let r = T::regs(); // Configure mode. let mode = config.mode; - r.config.write(|w| { + r.config().write(|w| { + w.set_order(config.bit_order); match mode { MODE_0 => { - w.order().variant(config.bit_order); - w.cpol().active_high(); - w.cpha().leading(); + w.set_cpol(vals::Cpol::ACTIVE_HIGH); + w.set_cpha(vals::Cpha::LEADING); } MODE_1 => { - w.order().variant(config.bit_order); - w.cpol().active_high(); - w.cpha().trailing(); + w.set_cpol(vals::Cpol::ACTIVE_HIGH); + w.set_cpha(vals::Cpha::TRAILING); } MODE_2 => { - w.order().variant(config.bit_order); - w.cpol().active_low(); - w.cpha().leading(); + w.set_cpol(vals::Cpol::ACTIVE_LOW); + w.set_cpha(vals::Cpha::LEADING); } MODE_3 => { - w.order().variant(config.bit_order); - w.cpol().active_low(); - w.cpha().trailing(); + w.set_cpol(vals::Cpol::ACTIVE_LOW); + w.set_cpha(vals::Cpha::TRAILING); } } - - w }); // Set over-read character. let orc = config.orc; - r.orc.write(|w| unsafe { w.orc().bits(orc) }); + r.orc().write(|w| w.set_orc(orc)); // Set default character. let def = config.def; - r.def.write(|w| unsafe { w.def().bits(def) }); + r.def().write(|w| w.set_def(def)); // Configure auto-acquire on 'transfer end' event. let auto_acquire = config.auto_acquire; - r.shorts.write(|w| w.end_acquire().bit(auto_acquire)); + r.shorts().write(|w| w.set_end_acquire(auto_acquire)); Ok(()) } diff --git a/embassy-nrf/src/temp.rs b/embassy-nrf/src/temp.rs index ed4a47713..1488c5c24 100644 --- a/embassy-nrf/src/temp.rs +++ b/embassy-nrf/src/temp.rs @@ -19,8 +19,8 @@ pub struct InterruptHandler { impl interrupt::typelevel::Handler for InterruptHandler { unsafe fn on_interrupt() { - let r = unsafe { &*pac::TEMP::PTR }; - r.intenclr.write(|w| w.datardy().clear()); + let r = pac::TEMP; + r.intenclr().write(|w| w.set_datardy(true)); WAKER.wake(); } } @@ -72,21 +72,21 @@ impl<'d> Temp<'d> { // In case the future is dropped, stop the task and reset events. let on_drop = OnDrop::new(|| { let t = Self::regs(); - t.tasks_stop.write(|w| unsafe { w.bits(1) }); - t.events_datardy.reset(); + t.tasks_stop().write_value(1); + t.events_datardy().write_value(0); }); let t = Self::regs(); - t.intenset.write(|w| w.datardy().set()); - unsafe { t.tasks_start.write(|w| w.bits(1)) }; + t.intenset().write(|w| w.set_datardy(true)); + t.tasks_start().write_value(1); let value = poll_fn(|cx| { WAKER.register(cx.waker()); - if t.events_datardy.read().bits() == 0 { + if t.events_datardy().read() == 0 { Poll::Pending } else { - t.events_datardy.reset(); - let raw = t.temp.read().bits(); + t.events_datardy().write_value(0); + let raw = t.temp().read(); Poll::Ready(I30F2::from_bits(raw as i32)) } }) @@ -95,7 +95,7 @@ impl<'d> Temp<'d> { value } - fn regs() -> &'static pac::temp::RegisterBlock { - unsafe { &*pac::TEMP::ptr() } + fn regs() -> pac::temp::Temp { + pac::TEMP } } diff --git a/embassy-nrf/src/time_driver.rs b/embassy-nrf/src/time_driver.rs index 3407c9504..e39c4ed52 100644 --- a/embassy-nrf/src/time_driver.rs +++ b/embassy-nrf/src/time_driver.rs @@ -10,8 +10,8 @@ use embassy_time_driver::{AlarmHandle, Driver}; use crate::interrupt::InterruptExt; use crate::{interrupt, pac}; -fn rtc() -> &'static pac::rtc0::RegisterBlock { - unsafe { &*pac::RTC1::ptr() } +fn rtc() -> pac::rtc::Rtc { + pac::RTC1 } /// Calculate the timestamp from the period count and the tick count. @@ -128,19 +128,18 @@ embassy_time_driver::time_driver_impl!(static DRIVER: RtcDriver = RtcDriver { impl RtcDriver { fn init(&'static self, irq_prio: crate::interrupt::Priority) { let r = rtc(); - r.cc[3].write(|w| unsafe { w.bits(0x800000) }); + r.cc(3).write(|w| w.set_compare(0x800000)); - r.intenset.write(|w| { - let w = w.ovrflw().set(); - let w = w.compare3().set(); - w + r.intenset().write(|w| { + w.set_ovrflw(true); + w.set_compare3(true); }); - r.tasks_clear.write(|w| unsafe { w.bits(1) }); - r.tasks_start.write(|w| unsafe { w.bits(1) }); + r.tasks_clear().write_value(1); + r.tasks_start().write_value(1); // Wait for clear - while r.counter.read().bits() != 0 {} + while r.counter().read().0 != 0 {} interrupt::RTC1.set_priority(irq_prio); unsafe { interrupt::RTC1.enable() }; @@ -148,19 +147,19 @@ impl RtcDriver { fn on_interrupt(&self) { let r = rtc(); - if r.events_ovrflw.read().bits() == 1 { - r.events_ovrflw.write(|w| w); + if r.events_ovrflw().read() == 1 { + r.events_ovrflw().write_value(0); self.next_period(); } - if r.events_compare[3].read().bits() == 1 { - r.events_compare[3].write(|w| w); + if r.events_compare(3).read() == 1 { + r.events_compare(3).write_value(0); self.next_period(); } for n in 0..ALARM_COUNT { - if r.events_compare[n].read().bits() == 1 { - r.events_compare[n].write(|w| w); + if r.events_compare(n).read() == 1 { + r.events_compare(n).write_value(0); critical_section::with(|cs| { self.trigger_alarm(n, cs); }) @@ -181,7 +180,7 @@ impl RtcDriver { if at < t + 0xc00000 { // just enable it. `set_alarm` has already set the correct CC val. - r.intenset.write(|w| unsafe { w.bits(compare_n(n)) }); + r.intenset().write(|w| w.0 = compare_n(n)); } } }) @@ -195,7 +194,7 @@ impl RtcDriver { fn trigger_alarm(&self, n: usize, cs: CriticalSection) { let r = rtc(); - r.intenclr.write(|w| unsafe { w.bits(compare_n(n)) }); + r.intenclr().write(|w| w.0 = compare_n(n)); let alarm = &self.alarms.borrow(cs)[n]; alarm.timestamp.set(u64::MAX); @@ -215,7 +214,7 @@ impl Driver for RtcDriver { // `period` MUST be read before `counter`, see comment at the top for details. let period = self.period.load(Ordering::Relaxed); compiler_fence(Ordering::Acquire); - let counter = rtc().counter.read().bits(); + let counter = rtc().counter().read().0; calc_now(period, counter) } @@ -252,7 +251,7 @@ impl Driver for RtcDriver { if timestamp <= t { // If alarm timestamp has passed the alarm will not fire. // Disarm the alarm and return `false` to indicate that. - r.intenclr.write(|w| unsafe { w.bits(compare_n(n)) }); + r.intenclr().write(|w| w.0 = compare_n(n)); alarm.timestamp.set(u64::MAX); @@ -277,15 +276,15 @@ impl Driver for RtcDriver { // by the Alarm trait contract. What's not allowed is triggering alarms *before* their scheduled time, // and we don't do that here. let safe_timestamp = timestamp.max(t + 3); - r.cc[n].write(|w| unsafe { w.bits(safe_timestamp as u32 & 0xFFFFFF) }); + r.cc(n).write(|w| w.set_compare(safe_timestamp as u32 & 0xFFFFFF)); let diff = timestamp - t; if diff < 0xc00000 { - r.intenset.write(|w| unsafe { w.bits(compare_n(n)) }); + r.intenset().write(|w| w.0 = compare_n(n)); } else { // If it's too far in the future, don't setup the compare channel yet. // It will be setup later by `next_period`. - r.intenclr.write(|w| unsafe { w.bits(compare_n(n)) }); + r.intenclr().write(|w| w.0 = compare_n(n)); } true diff --git a/embassy-nrf/src/timer.rs b/embassy-nrf/src/timer.rs index ac5328ded..a9aeb40fa 100644 --- a/embassy-nrf/src/timer.rs +++ b/embassy-nrf/src/timer.rs @@ -8,13 +8,14 @@ use embassy_hal_internal::{into_ref, PeripheralRef}; +use crate::pac::timer::vals; use crate::ppi::{Event, Task}; use crate::{pac, Peripheral}; pub(crate) trait SealedInstance { /// The number of CC registers this instance has. const CCS: usize; - fn regs() -> &'static pac::timer0::RegisterBlock; + fn regs() -> pac::timer::Timer; } /// Basic Timer instance. @@ -31,8 +32,8 @@ macro_rules! impl_timer { ($type:ident, $pac_type:ident, $irq:ident, $ccs:literal) => { impl crate::timer::SealedInstance for peripherals::$type { const CCS: usize = $ccs; - fn regs() -> &'static pac::timer0::RegisterBlock { - unsafe { &*(pac::$pac_type::ptr() as *const pac::timer0::RegisterBlock) } + fn regs() -> pac::timer::Timer { + unsafe { pac::timer::Timer::from_ptr(pac::$pac_type.as_ptr()) } } } impl crate::timer::Instance for peripherals::$type { @@ -114,19 +115,19 @@ impl<'d, T: Instance> Timer<'d, T> { // since changing BITMODE while running can cause 'unpredictable behaviour' according to the specification. this.stop(); - #[cfg(not(feature = "nrf51"))] - if _is_counter { - regs.mode.write(|w| w.mode().low_power_counter()); - } else { - regs.mode.write(|w| w.mode().timer()); - } - - #[cfg(feature = "nrf51")] - regs.mode.write(|w| w.mode().timer()); + regs.mode().write(|w| { + w.set_mode(match _is_counter { + #[cfg(not(feature = "_nrf51"))] + true => vals::Mode::LOW_POWER_COUNTER, + #[cfg(feature = "_nrf51")] + true => vals::Mode::COUNTER, + false => vals::Mode::TIMER, + }) + }); // Make the counter's max value as high as possible. // TODO: is there a reason someone would want to set this lower? - regs.bitmode.write(|w| w.bitmode()._32bit()); + regs.bitmode().write(|w| w.set_bitmode(vals::Bitmode::_32BIT)); // Initialize the counter at 0. this.clear(); @@ -148,38 +149,38 @@ impl<'d, T: Instance> Timer<'d, T> { /// Starts the timer. pub fn start(&self) { - T::regs().tasks_start.write(|w| unsafe { w.bits(1) }) + T::regs().tasks_start().write_value(1) } /// Stops the timer. pub fn stop(&self) { - T::regs().tasks_stop.write(|w| unsafe { w.bits(1) }) + T::regs().tasks_stop().write_value(1) } /// Reset the timer's counter to 0. pub fn clear(&self) { - T::regs().tasks_clear.write(|w| unsafe { w.bits(1) }) + T::regs().tasks_clear().write_value(1) } /// Returns the START task, for use with PPI. /// /// When triggered, this task starts the timer. pub fn task_start(&self) -> Task<'d> { - Task::from_reg(&T::regs().tasks_start) + Task::from_reg(T::regs().tasks_start()) } /// Returns the STOP task, for use with PPI. /// /// When triggered, this task stops the timer. pub fn task_stop(&self) -> Task<'d> { - Task::from_reg(&T::regs().tasks_stop) + Task::from_reg(T::regs().tasks_stop()) } /// Returns the CLEAR task, for use with PPI. /// /// When triggered, this task resets the timer's counter to 0. pub fn task_clear(&self) -> Task<'d> { - Task::from_reg(&T::regs().tasks_clear) + Task::from_reg(T::regs().tasks_clear()) } /// Returns the COUNT task, for use with PPI. @@ -187,7 +188,7 @@ impl<'d, T: Instance> Timer<'d, T> { /// When triggered, this task increments the timer's counter by 1. /// Only works in counter mode. pub fn task_count(&self) -> Task<'d> { - Task::from_reg(&T::regs().tasks_count) + Task::from_reg(T::regs().tasks_count()) } /// Change the timer's frequency. @@ -198,10 +199,10 @@ impl<'d, T: Instance> Timer<'d, T> { self.stop(); T::regs() - .prescaler + .prescaler() // SAFETY: `frequency` is a variant of `Frequency`, // whose values are all in the range of 0-9 (the valid range of `prescaler`). - .write(|w| unsafe { w.prescaler().bits(frequency as u8) }) + .write(|w| w.set_prescaler(frequency as u8)) } /// Returns this timer's `n`th CC register. @@ -234,28 +235,19 @@ pub struct Cc<'d, T: Instance> { impl<'d, T: Instance> Cc<'d, T> { /// Get the current value stored in the register. pub fn read(&self) -> u32 { - #[cfg(not(feature = "nrf51"))] - return T::regs().cc[self.n].read().cc().bits(); - - #[cfg(feature = "nrf51")] - return T::regs().cc[self.n].read().bits(); + return T::regs().cc(self.n).read(); } /// Set the value stored in the register. /// /// `event_compare` will fire when the timer's counter reaches this value. pub fn write(&self, value: u32) { - // SAFETY: there are no invalid values for the CC register. - #[cfg(not(feature = "nrf51"))] - T::regs().cc[self.n].write(|w| unsafe { w.cc().bits(value) }); - - #[cfg(feature = "nrf51")] - T::regs().cc[self.n].write(|w| unsafe { w.bits(value) }); + T::regs().cc(self.n).write_value(value); } /// Capture the current value of the timer's counter in this register, and return it. pub fn capture(&self) -> u32 { - T::regs().tasks_capture[self.n].write(|w| unsafe { w.bits(1) }); + T::regs().tasks_capture(self.n).write_value(1); self.read() } @@ -263,14 +255,14 @@ impl<'d, T: Instance> Cc<'d, T> { /// /// When triggered, this task will capture the current value of the timer's counter in this register. pub fn task_capture(&self) -> Task<'d> { - Task::from_reg(&T::regs().tasks_capture) + Task::from_reg(T::regs().tasks_capture(self.n)) } /// Returns this CC register's COMPARE event, for use with PPI. /// /// This event will fire when the timer's counter reaches the value in this CC register. pub fn event_compare(&self) -> Event<'d> { - Event::from_reg(&T::regs().events_compare[self.n]) + Event::from_reg(T::regs().events_compare(self.n)) } /// Enable the shortcut between this CC register's COMPARE event and the timer's CLEAR task. @@ -279,16 +271,12 @@ impl<'d, T: Instance> Cc<'d, T> { /// /// So, when the timer's counter reaches the value stored in this register, the timer's counter will be reset to 0. pub fn short_compare_clear(&self) { - T::regs() - .shorts - .modify(|r, w| unsafe { w.bits(r.bits() | (1 << self.n)) }) + T::regs().shorts().modify(|w| w.set_compare_clear(self.n, true)) } /// Disable the shortcut between this CC register's COMPARE event and the timer's CLEAR task. pub fn unshort_compare_clear(&self) { - T::regs() - .shorts - .modify(|r, w| unsafe { w.bits(r.bits() & !(1 << self.n)) }) + T::regs().shorts().modify(|w| w.set_compare_clear(self.n, false)) } /// Enable the shortcut between this CC register's COMPARE event and the timer's STOP task. @@ -297,15 +285,11 @@ impl<'d, T: Instance> Cc<'d, T> { /// /// So, when the timer's counter reaches the value stored in this register, the timer will stop counting up. pub fn short_compare_stop(&self) { - T::regs() - .shorts - .modify(|r, w| unsafe { w.bits(r.bits() | (1 << (8 + self.n))) }) + T::regs().shorts().modify(|w| w.set_compare_stop(self.n, true)) } /// Disable the shortcut between this CC register's COMPARE event and the timer's STOP task. pub fn unshort_compare_stop(&self) { - T::regs() - .shorts - .modify(|r, w| unsafe { w.bits(r.bits() & !(1 << (8 + self.n))) }) + T::regs().shorts().modify(|w| w.set_compare_stop(self.n, false)) } } diff --git a/embassy-nrf/src/twim.rs b/embassy-nrf/src/twim.rs index 187fce021..ebad39df2 100644 --- a/embassy-nrf/src/twim.rs +++ b/embassy-nrf/src/twim.rs @@ -15,24 +15,16 @@ use embassy_sync::waitqueue::AtomicWaker; #[cfg(feature = "time")] use embassy_time::{Duration, Instant}; use embedded_hal_1::i2c::Operation; +pub use pac::twim::vals::Frequency; use crate::chip::{EASY_DMA_SIZE, FORCE_COPY_BUFFER_SIZE}; use crate::gpio::Pin as GpioPin; use crate::interrupt::typelevel::Interrupt; +use crate::pac::gpio::vals as gpiovals; +use crate::pac::twim::vals; use crate::util::slice_in_ram; use crate::{gpio, interrupt, pac, Peripheral}; -/// TWI frequency -#[derive(Clone, Copy)] -pub enum Frequency { - /// 100 kbps - K100 = 26738688, - /// 250 kbps - K250 = 67108864, - /// 400 kbps - K400 = 104857600, -} - /// TWIM config. #[non_exhaustive] pub struct Config { @@ -105,17 +97,17 @@ impl interrupt::typelevel::Handler for InterruptHandl let r = T::regs(); let s = T::state(); - if r.events_suspended.read().bits() != 0 { + if r.events_suspended().read() != 0 { s.end_waker.wake(); - r.intenclr.write(|w| w.suspended().clear()); + r.intenclr().write(|w| w.set_suspended(true)); } - if r.events_stopped.read().bits() != 0 { + if r.events_stopped().read() != 0 { s.end_waker.wake(); - r.intenclr.write(|w| w.stopped().clear()); + r.intenclr().write(|w| w.set_stopped(true)); } - if r.events_error.read().bits() != 0 { + if r.events_error().read() != 0 { s.end_waker.wake(); - r.intenclr.write(|w| w.error().clear()); + r.intenclr().write(|w| w.set_error(true)); } } } @@ -140,38 +132,34 @@ impl<'d, T: Instance> Twim<'d, T> { // Configure pins sda.conf().write(|w| { - w.dir().input(); - w.input().connect(); - if config.sda_high_drive { - w.drive().h0d1(); - } else { - w.drive().s0d1(); - } + w.set_dir(gpiovals::Dir::OUTPUT); + w.set_input(gpiovals::Input::CONNECT); + w.set_drive(match config.sda_high_drive { + true => gpiovals::Drive::H0D1, + false => gpiovals::Drive::S0D1, + }); if config.sda_pullup { - w.pull().pullup(); + w.set_pull(gpiovals::Pull::PULLUP); } - w }); scl.conf().write(|w| { - w.dir().input(); - w.input().connect(); - if config.scl_high_drive { - w.drive().h0d1(); - } else { - w.drive().s0d1(); + w.set_dir(gpiovals::Dir::OUTPUT); + w.set_input(gpiovals::Input::CONNECT); + w.set_drive(match config.scl_high_drive { + true => gpiovals::Drive::H0D1, + false => gpiovals::Drive::S0D1, + }); + if config.sda_pullup { + w.set_pull(gpiovals::Pull::PULLUP); } - if config.scl_pullup { - w.pull().pullup(); - } - w }); // Select pins. - r.psel.sda.write(|w| unsafe { w.bits(sda.psel_bits()) }); - r.psel.scl.write(|w| unsafe { w.bits(scl.psel_bits()) }); + r.psel().sda().write_value(sda.psel_bits()); + r.psel().scl().write_value(scl.psel_bits()); // Enable TWIM instance. - r.enable.write(|w| w.enable().enabled()); + r.enable().write(|w| w.set_enable(vals::Enable::ENABLED)); let mut twim = Self { _p: twim }; @@ -179,7 +167,7 @@ impl<'d, T: Instance> Twim<'d, T> { Self::set_config(&mut twim, &config).unwrap(); // Disable all events interrupts - r.intenclr.write(|w| unsafe { w.bits(0xFFFF_FFFF) }); + r.intenclr().write(|w| w.0 = 0xFFFF_FFFF); T::Interrupt::unpend(); unsafe { T::Interrupt::enable() }; @@ -211,22 +199,18 @@ impl<'d, T: Instance> Twim<'d, T> { let r = T::regs(); - r.txd.ptr.write(|w| - // We're giving the register a pointer to the stack. Since we're - // waiting for the I2C transaction to end before this stack pointer - // becomes invalid, there's nothing wrong here. - // - // The PTR field is a full 32 bits wide and accepts the full range - // of values. - w.ptr().bits(buffer.as_ptr() as u32)); - r.txd.maxcnt.write(|w| + // We're giving the register a pointer to the stack. Since we're + // waiting for the I2C transaction to end before this stack pointer + // becomes invalid, there's nothing wrong here. + r.txd().ptr().write_value(buffer.as_ptr() as u32); + r.txd().maxcnt().write(|w| // We're giving it the length of the buffer, so no danger of // accessing invalid memory. We have verified that the length of the // buffer fits in an `u8`, so the cast to `u8` is also fine. // // The MAXCNT field is 8 bits wide and accepts the full range of // values. - w.maxcnt().bits(buffer.len() as _)); + w.set_maxcnt(buffer.len() as _)); Ok(()) } @@ -242,15 +226,11 @@ impl<'d, T: Instance> Twim<'d, T> { let r = T::regs(); - r.rxd.ptr.write(|w| - // We're giving the register a pointer to the stack. Since we're - // waiting for the I2C transaction to end before this stack pointer - // becomes invalid, there's nothing wrong here. - // - // The PTR field is a full 32 bits wide and accepts the full range - // of values. - w.ptr().bits(buffer.as_mut_ptr() as u32)); - r.rxd.maxcnt.write(|w| + // We're giving the register a pointer to the stack. Since we're + // waiting for the I2C transaction to end before this stack pointer + // becomes invalid, there's nothing wrong here. + r.rxd().ptr().write_value(buffer.as_mut_ptr() as u32); + r.rxd().maxcnt().write(|w| // We're giving it the length of the buffer, so no danger of // accessing invalid memory. We have verified that the length of the // buffer fits in an `u8`, so the cast to the type of maxcnt @@ -260,29 +240,32 @@ impl<'d, T: Instance> Twim<'d, T> { // type than a u8, so we use a `_` cast rather than a `u8` cast. // The MAXCNT field is thus at least 8 bits wide and accepts the // full range of values that fit in a `u8`. - w.maxcnt().bits(buffer.len() as _)); + w.set_maxcnt(buffer.len() as _)); Ok(()) } fn clear_errorsrc(&mut self) { let r = T::regs(); - r.errorsrc - .write(|w| w.anack().bit(true).dnack().bit(true).overrun().bit(true)); + r.errorsrc().write(|w| { + w.set_anack(true); + w.set_dnack(true); + w.set_overrun(true); + }); } /// Get Error instance, if any occurred. fn check_errorsrc(&self) -> Result<(), Error> { let r = T::regs(); - let err = r.errorsrc.read(); - if err.anack().is_received() { + let err = r.errorsrc().read(); + if err.anack() { return Err(Error::AddressNack); } - if err.dnack().is_received() { + if err.dnack() { return Err(Error::DataNack); } - if err.overrun().is_received() { + if err.overrun() { return Err(Error::Overrun); } Ok(()) @@ -290,7 +273,7 @@ impl<'d, T: Instance> Twim<'d, T> { fn check_rx(&self, len: usize) -> Result<(), Error> { let r = T::regs(); - if r.rxd.amount.read().bits() != len as u32 { + if r.rxd().amount().read().0 != len as u32 { Err(Error::Receive) } else { Ok(()) @@ -299,7 +282,7 @@ impl<'d, T: Instance> Twim<'d, T> { fn check_tx(&self, len: usize) -> Result<(), Error> { let r = T::regs(); - if r.txd.amount.read().bits() != len as u32 { + if r.txd().amount().read().0 != len as u32 { Err(Error::Transmit) } else { Ok(()) @@ -310,14 +293,14 @@ impl<'d, T: Instance> Twim<'d, T> { fn blocking_wait(&mut self) { let r = T::regs(); loop { - if r.events_suspended.read().bits() != 0 || r.events_stopped.read().bits() != 0 { - r.events_suspended.reset(); - r.events_stopped.reset(); + if r.events_suspended().read() != 0 || r.events_stopped().read() != 0 { + r.events_suspended().write_value(0); + r.events_stopped().write_value(0); break; } - if r.events_error.read().bits() != 0 { - r.events_error.reset(); - r.tasks_stop.write(|w| unsafe { w.bits(1) }); + if r.events_error().read() != 0 { + r.events_error().write_value(0); + r.tasks_stop().write_value(1); } } } @@ -328,16 +311,16 @@ impl<'d, T: Instance> Twim<'d, T> { let r = T::regs(); let deadline = Instant::now() + timeout; loop { - if r.events_suspended.read().bits() != 0 || r.events_stopped.read().bits() != 0 { - r.events_stopped.reset(); + if r.events_suspended().read() != 0 || r.events_stopped().read() != 0 { + r.events_stopped().write_value(0); break; } - if r.events_error.read().bits() != 0 { - r.events_error.reset(); - r.tasks_stop.write(|w| unsafe { w.bits(1) }); + if r.events_error().read() != 0 { + r.events_error().write_value(0); + r.tasks_stop().write_value(1); } if Instant::now() > deadline { - r.tasks_stop.write(|w| unsafe { w.bits(1) }); + r.tasks_stop().write_value(1); return Err(Error::Timeout); } } @@ -352,16 +335,16 @@ impl<'d, T: Instance> Twim<'d, T> { let s = T::state(); s.end_waker.register(cx.waker()); - if r.events_suspended.read().bits() != 0 || r.events_stopped.read().bits() != 0 { - r.events_stopped.reset(); + if r.events_suspended().read() != 0 || r.events_stopped().read() != 0 { + r.events_stopped().write_value(0); return Poll::Ready(()); } // stop if an error occurred - if r.events_error.read().bits() != 0 { - r.events_error.reset(); - r.tasks_stop.write(|w| unsafe { w.bits(1) }); + if r.events_error().read() != 0 { + r.events_error().write_value(0); + r.tasks_stop().write_value(1); } Poll::Pending @@ -380,18 +363,25 @@ impl<'d, T: Instance> Twim<'d, T> { compiler_fence(SeqCst); - r.address.write(|w| unsafe { w.address().bits(address) }); + r.address().write(|w| w.set_address(address)); - r.events_suspended.reset(); - r.events_stopped.reset(); - r.events_error.reset(); + r.events_suspended().write_value(0); + r.events_stopped().write_value(0); + r.events_error().write_value(0); self.clear_errorsrc(); if inten { - r.intenset.write(|w| w.suspended().set().stopped().set().error().set()); + r.intenset().write(|w| { + w.set_suspended(true); + w.set_stopped(true); + w.set_error(true); + }); } else { - r.intenclr - .write(|w| w.suspended().clear().stopped().clear().error().clear()); + r.intenclr().write(|w| { + w.set_suspended(true); + w.set_stopped(true); + w.set_error(true); + }); } assert!(!operations.is_empty()); @@ -408,26 +398,25 @@ impl<'d, T: Instance> Twim<'d, T> { self.set_rx_buffer(rd_buffer)?; } - r.shorts.write(|w| { - w.lastrx_starttx().enabled(); + r.shorts().write(|w| { + w.set_lastrx_starttx(true); if stop { - w.lasttx_stop().enabled(); + w.set_lasttx_stop(true); } else { - w.lasttx_suspend().enabled(); + w.set_lasttx_suspend(true); } - w }); // Start read+write operation. - r.tasks_startrx.write(|w| unsafe { w.bits(1) }); + r.tasks_startrx().write_value(1); if last_op.is_some() { - r.tasks_resume.write(|w| unsafe { w.bits(1) }); + r.tasks_resume().write_value(1); } // TODO: Handle empty write buffer if rd_buffer.is_empty() { // With a zero-length buffer, LASTRX doesn't fire (because there's no last byte!), so do the STARTTX ourselves. - r.tasks_starttx.write(|w| unsafe { w.bits(1) }); + r.tasks_starttx().write_value(1); } Ok(2) @@ -438,17 +427,17 @@ impl<'d, T: Instance> Twim<'d, T> { self.set_rx_buffer(buffer)?; } - r.shorts.write(|w| w.lastrx_stop().enabled()); + r.shorts().write(|w| w.set_lastrx_stop(true)); // Start read operation. - r.tasks_startrx.write(|w| unsafe { w.bits(1) }); + r.tasks_startrx().write_value(1); if last_op.is_some() { - r.tasks_resume.write(|w| unsafe { w.bits(1) }); + r.tasks_resume().write_value(1); } if buffer.is_empty() { // With a zero-length buffer, LASTRX doesn't fire (because there's no last byte!), so do the STOP ourselves. - r.tasks_stop.write(|w| unsafe { w.bits(1) }); + r.tasks_stop().write_value(1); } Ok(1) @@ -463,15 +452,14 @@ impl<'d, T: Instance> Twim<'d, T> { } // Start write+read operation. - r.shorts.write(|w| { - w.lasttx_startrx().enabled(); - w.lastrx_stop().enabled(); - w + r.shorts().write(|w| { + w.set_lasttx_startrx(true); + w.set_lastrx_stop(true); }); - r.tasks_starttx.write(|w| unsafe { w.bits(1) }); + r.tasks_starttx().write_value(1); if last_op.is_some() { - r.tasks_resume.write(|w| unsafe { w.bits(1) }); + r.tasks_resume().write_value(1); } Ok(2) @@ -485,26 +473,25 @@ impl<'d, T: Instance> Twim<'d, T> { } // Start write operation. - r.shorts.write(|w| { + r.shorts().write(|w| { if stop { - w.lasttx_stop().enabled(); + w.set_lasttx_stop(true); } else { - w.lasttx_suspend().enabled(); + w.set_lasttx_suspend(true); } - w }); - r.tasks_starttx.write(|w| unsafe { w.bits(1) }); + r.tasks_starttx().write_value(1); if last_op.is_some() { - r.tasks_resume.write(|w| unsafe { w.bits(1) }); + r.tasks_resume().write_value(1); } if buffer.is_empty() { // With a zero-length buffer, LASTTX doesn't fire (because there's no last byte!), so do the STOP/SUSPEND ourselves. if stop { - r.tasks_stop.write(|w| unsafe { w.bits(1) }); + r.tasks_stop().write_value(1); } else { - r.tasks_suspend.write(|w| unsafe { w.bits(1) }); + r.tasks_suspend().write_value(1); } } @@ -827,10 +814,10 @@ impl<'a, T: Instance> Drop for Twim<'a, T> { // disable! let r = T::regs(); - r.enable.write(|w| w.enable().disabled()); + r.enable().write(|w| w.set_enable(vals::Enable::DISABLED)); - gpio::deconfigure_pin(r.psel.sda.read().bits()); - gpio::deconfigure_pin(r.psel.scl.read().bits()); + gpio::deconfigure_pin(r.psel().sda().read()); + gpio::deconfigure_pin(r.psel().scl().read()); trace!("twim drop: done"); } @@ -849,7 +836,7 @@ impl State { } pub(crate) trait SealedInstance { - fn regs() -> &'static pac::twim0::RegisterBlock; + fn regs() -> pac::twim::Twim; fn state() -> &'static State; } @@ -863,8 +850,8 @@ pub trait Instance: Peripheral

+ SealedInstance + 'static { macro_rules! impl_twim { ($type:ident, $pac_type:ident, $irq:ident) => { impl crate::twim::SealedInstance for peripherals::$type { - fn regs() -> &'static pac::twim0::RegisterBlock { - unsafe { &*pac::$pac_type::ptr() } + fn regs() -> pac::twim::Twim { + pac::$pac_type } fn state() -> &'static crate::twim::State { static STATE: crate::twim::State = crate::twim::State::new(); @@ -948,8 +935,7 @@ impl<'d, T: Instance> SetConfig for Twim<'d, T> { type ConfigError = (); fn set_config(&mut self, config: &Self::Config) -> Result<(), Self::ConfigError> { let r = T::regs(); - r.frequency - .write(|w| unsafe { w.frequency().bits(config.frequency as u32) }); + r.frequency().write(|w| w.set_frequency(config.frequency)); Ok(()) } diff --git a/embassy-nrf/src/twis.rs b/embassy-nrf/src/twis.rs index f3eab008f..60de2ed9d 100644 --- a/embassy-nrf/src/twis.rs +++ b/embassy-nrf/src/twis.rs @@ -16,6 +16,8 @@ use embassy_time::{Duration, Instant}; use crate::chip::{EASY_DMA_SIZE, FORCE_COPY_BUFFER_SIZE}; use crate::gpio::Pin as GpioPin; use crate::interrupt::typelevel::Interrupt; +use crate::pac::gpio::vals as gpiovals; +use crate::pac::twis::vals; use crate::util::slice_in_ram_or; use crate::{gpio, interrupt, pac, Peripheral}; @@ -119,17 +121,20 @@ impl interrupt::typelevel::Handler for InterruptHandl let r = T::regs(); let s = T::state(); - if r.events_read.read().bits() != 0 || r.events_write.read().bits() != 0 { + if r.events_read().read() != 0 || r.events_write().read() != 0 { s.waker.wake(); - r.intenclr.modify(|_r, w| w.read().clear().write().clear()); + r.intenclr().write(|w| { + w.set_read(true); + w.set_write(true); + }); } - if r.events_stopped.read().bits() != 0 { + if r.events_stopped().read() != 0 { s.waker.wake(); - r.intenclr.modify(|_r, w| w.stopped().clear()); + r.intenclr().write(|w| w.set_stopped(true)); } - if r.events_error.read().bits() != 0 { + if r.events_error().read() != 0 { s.waker.wake(); - r.intenclr.modify(|_r, w| w.error().clear()); + r.intenclr().write(|w| w.set_error(true)); } } } @@ -154,55 +159,51 @@ impl<'d, T: Instance> Twis<'d, T> { // Configure pins sda.conf().write(|w| { - w.dir().input(); - w.input().connect(); - if config.sda_high_drive { - w.drive().h0d1(); - } else { - w.drive().s0d1(); - } + w.set_dir(gpiovals::Dir::INPUT); + w.set_input(gpiovals::Input::CONNECT); + w.set_drive(match config.sda_high_drive { + true => gpiovals::Drive::H0D1, + false => gpiovals::Drive::S0D1, + }); if config.sda_pullup { - w.pull().pullup(); + w.set_pull(gpiovals::Pull::PULLUP); } - w }); scl.conf().write(|w| { - w.dir().input(); - w.input().connect(); - if config.scl_high_drive { - w.drive().h0d1(); - } else { - w.drive().s0d1(); + w.set_dir(gpiovals::Dir::INPUT); + w.set_input(gpiovals::Input::CONNECT); + w.set_drive(match config.scl_high_drive { + true => gpiovals::Drive::H0D1, + false => gpiovals::Drive::S0D1, + }); + if config.sda_pullup { + w.set_pull(gpiovals::Pull::PULLUP); } - if config.scl_pullup { - w.pull().pullup(); - } - w }); // Select pins. - r.psel.sda.write(|w| unsafe { w.bits(sda.psel_bits()) }); - r.psel.scl.write(|w| unsafe { w.bits(scl.psel_bits()) }); + r.psel().sda().write_value(sda.psel_bits()); + r.psel().scl().write_value(scl.psel_bits()); // Enable TWIS instance. - r.enable.write(|w| w.enable().enabled()); + r.enable().write(|w| w.set_enable(vals::Enable::ENABLED)); // Disable all events interrupts - r.intenclr.write(|w| unsafe { w.bits(0xFFFF_FFFF) }); + r.intenclr().write(|w| w.0 = 0xFFFF_FFFF); // Set address - r.address[0].write(|w| unsafe { w.address().bits(config.address0) }); - r.config.write(|w| w.address0().enabled()); + r.address(0).write(|w| w.set_address(config.address0)); + r.config().write(|w| w.set_address0(true)); if let Some(address1) = config.address1 { - r.address[1].write(|w| unsafe { w.address().bits(address1) }); - r.config.modify(|_r, w| w.address1().enabled()); + r.address(1).write(|w| w.set_address(address1)); + r.config().modify(|w| w.set_address1(true)); } // Set over-read character - r.orc.write(|w| unsafe { w.orc().bits(config.orc) }); + r.orc().write(|w| w.set_orc(config.orc)); // Generate suspend on read event - r.shorts.write(|w| w.read_suspend().enabled()); + r.shorts().write(|w| w.set_read_suspend(true)); T::Interrupt::unpend(); unsafe { T::Interrupt::enable() }; @@ -220,22 +221,18 @@ impl<'d, T: Instance> Twis<'d, T> { let r = T::regs(); - r.txd.ptr.write(|w| - // We're giving the register a pointer to the stack. Since we're - // waiting for the I2C transaction to end before this stack pointer - // becomes invalid, there's nothing wrong here. - // - // The PTR field is a full 32 bits wide and accepts the full range - // of values. - w.ptr().bits(buffer.as_ptr() as u32)); - r.txd.maxcnt.write(|w| + // We're giving the register a pointer to the stack. Since we're + // waiting for the I2C transaction to end before this stack pointer + // becomes invalid, there's nothing wrong here. + r.txd().ptr().write_value(buffer.as_ptr() as u32); + r.txd().maxcnt().write(|w| // We're giving it the length of the buffer, so no danger of // accessing invalid memory. We have verified that the length of the // buffer fits in an `u8`, so the cast to `u8` is also fine. // // The MAXCNT field is 8 bits wide and accepts the full range of // values. - w.maxcnt().bits(buffer.len() as _)); + w.set_maxcnt(buffer.len() as _)); Ok(()) } @@ -251,15 +248,11 @@ impl<'d, T: Instance> Twis<'d, T> { let r = T::regs(); - r.rxd.ptr.write(|w| - // We're giving the register a pointer to the stack. Since we're - // waiting for the I2C transaction to end before this stack pointer - // becomes invalid, there's nothing wrong here. - // - // The PTR field is a full 32 bits wide and accepts the full range - // of values. - w.ptr().bits(buffer.as_mut_ptr() as u32)); - r.rxd.maxcnt.write(|w| + // We're giving the register a pointer to the stack. Since we're + // waiting for the I2C transaction to end before this stack pointer + // becomes invalid, there's nothing wrong here. + r.rxd().ptr().write_value(buffer.as_mut_ptr() as u32); + r.rxd().maxcnt().write(|w| // We're giving it the length of the buffer, so no danger of // accessing invalid memory. We have verified that the length of the // buffer fits in an `u8`, so the cast to the type of maxcnt @@ -269,48 +262,51 @@ impl<'d, T: Instance> Twis<'d, T> { // type than a u8, so we use a `_` cast rather than a `u8` cast. // The MAXCNT field is thus at least 8 bits wide and accepts the // full range of values that fit in a `u8`. - w.maxcnt().bits(buffer.len() as _)); + w.set_maxcnt(buffer.len() as _)); Ok(()) } fn clear_errorsrc(&mut self) { let r = T::regs(); - r.errorsrc - .write(|w| w.overflow().bit(true).overread().bit(true).dnack().bit(true)); + r.errorsrc().write(|w| { + w.set_overflow(true); + w.set_overread(true); + w.set_dnack(true); + }); } /// Returns matched address for latest command. pub fn address_match(&self) -> u8 { let r = T::regs(); - r.address[r.match_.read().bits() as usize].read().address().bits() + r.address(r.match_().read().0 as usize).read().address() } /// Returns the index of the address matched in the latest command. pub fn address_match_index(&self) -> usize { - T::regs().match_.read().bits() as _ + T::regs().match_().read().0 as _ } /// Wait for read, write, stop or error fn blocking_listen_wait(&mut self) -> Result { let r = T::regs(); loop { - if r.events_error.read().bits() != 0 { - r.events_error.reset(); - r.tasks_stop.write(|w| unsafe { w.bits(1) }); - while r.events_stopped.read().bits() == 0 {} + if r.events_error().read() != 0 { + r.events_error().write_value(0); + r.tasks_stop().write_value(1); + while r.events_stopped().read() == 0 {} return Err(Error::Overflow); } - if r.events_stopped.read().bits() != 0 { - r.events_stopped.reset(); + if r.events_stopped().read() != 0 { + r.events_stopped().write_value(0); return Err(Error::Bus); } - if r.events_read.read().bits() != 0 { - r.events_read.reset(); + if r.events_read().read() != 0 { + r.events_read().write_value(0); return Ok(Status::Read); } - if r.events_write.read().bits() != 0 { - r.events_write.reset(); + if r.events_write().read() != 0 { + r.events_write().write_value(0); return Ok(Status::Write); } } @@ -321,22 +317,22 @@ impl<'d, T: Instance> Twis<'d, T> { let r = T::regs(); loop { // stop if an error occurred - if r.events_error.read().bits() != 0 { - r.events_error.reset(); - r.tasks_stop.write(|w| unsafe { w.bits(1) }); + if r.events_error().read() != 0 { + r.events_error().write_value(0); + r.tasks_stop().write_value(1); return Err(Error::Overflow); - } else if r.events_stopped.read().bits() != 0 { - r.events_stopped.reset(); + } else if r.events_stopped().read() != 0 { + r.events_stopped().write_value(0); return match status { Status::Read => Ok(Command::Read), Status::Write => { - let n = r.rxd.amount.read().bits() as usize; + let n = r.rxd().amount().read().0 as usize; Ok(Command::Write(n)) } }; - } else if r.events_read.read().bits() != 0 { - r.events_read.reset(); - let n = r.rxd.amount.read().bits() as usize; + } else if r.events_read().read() != 0 { + r.events_read().write_value(0); + let n = r.rxd().amount().read().0 as usize; return Ok(Command::WriteRead(n)); } } @@ -347,20 +343,20 @@ impl<'d, T: Instance> Twis<'d, T> { let r = T::regs(); loop { // stop if an error occurred - if r.events_error.read().bits() != 0 { - r.events_error.reset(); - r.tasks_stop.write(|w| unsafe { w.bits(1) }); - let errorsrc = r.errorsrc.read(); - if errorsrc.overread().is_detected() { + if r.events_error().read() != 0 { + r.events_error().write_value(0); + r.tasks_stop().write_value(1); + let errorsrc = r.errorsrc().read(); + if errorsrc.overread() { return Err(Error::OverRead); - } else if errorsrc.dnack().is_received() { + } else if errorsrc.dnack() { return Err(Error::DataNack); } else { return Err(Error::Bus); } - } else if r.events_stopped.read().bits() != 0 { - r.events_stopped.reset(); - let n = r.txd.amount.read().bits() as usize; + } else if r.events_stopped().read() != 0 { + r.events_stopped().write_value(0); + let n = r.txd().amount().read().0 as usize; return Ok(n); } } @@ -373,23 +369,23 @@ impl<'d, T: Instance> Twis<'d, T> { let deadline = Instant::now() + timeout; loop { // stop if an error occurred - if r.events_error.read().bits() != 0 { - r.events_error.reset(); - r.tasks_stop.write(|w| unsafe { w.bits(1) }); - let errorsrc = r.errorsrc.read(); - if errorsrc.overread().is_detected() { + if r.events_error().read() != 0 { + r.events_error().write_value(0); + r.tasks_stop().write_value(1); + let errorsrc = r.errorsrc().read(); + if errorsrc.overread() { return Err(Error::OverRead); - } else if errorsrc.dnack().is_received() { + } else if errorsrc.dnack() { return Err(Error::DataNack); } else { return Err(Error::Bus); } - } else if r.events_stopped.read().bits() != 0 { - r.events_stopped.reset(); - let n = r.txd.amount.read().bits() as usize; + } else if r.events_stopped().read() != 0 { + r.events_stopped().write_value(0); + let n = r.txd().amount().read().0 as usize; return Ok(n); } else if Instant::now() > deadline { - r.tasks_stop.write(|w| unsafe { w.bits(1) }); + r.tasks_stop().write_value(1); return Err(Error::Timeout); } } @@ -401,26 +397,26 @@ impl<'d, T: Instance> Twis<'d, T> { let r = T::regs(); let deadline = Instant::now() + timeout; loop { - if r.events_error.read().bits() != 0 { - r.events_error.reset(); - r.tasks_stop.write(|w| unsafe { w.bits(1) }); - while r.events_stopped.read().bits() == 0 {} + if r.events_error().read() != 0 { + r.events_error().write_value(0); + r.tasks_stop().write_value(1); + while r.events_stopped().read() == 0 {} return Err(Error::Overflow); } - if r.events_stopped.read().bits() != 0 { - r.events_stopped.reset(); + if r.events_stopped().read() != 0 { + r.events_stopped().write_value(0); return Err(Error::Bus); } - if r.events_read.read().bits() != 0 { - r.events_read.reset(); + if r.events_read().read() != 0 { + r.events_read().write_value(0); return Ok(Status::Read); } - if r.events_write.read().bits() != 0 { - r.events_write.reset(); + if r.events_write().read() != 0 { + r.events_write().write_value(0); return Ok(Status::Write); } if Instant::now() > deadline { - r.tasks_stop.write(|w| unsafe { w.bits(1) }); + r.tasks_stop().write_value(1); return Err(Error::Timeout); } } @@ -433,25 +429,25 @@ impl<'d, T: Instance> Twis<'d, T> { let deadline = Instant::now() + timeout; loop { // stop if an error occurred - if r.events_error.read().bits() != 0 { - r.events_error.reset(); - r.tasks_stop.write(|w| unsafe { w.bits(1) }); + if r.events_error().read() != 0 { + r.events_error().write_value(0); + r.tasks_stop().write_value(1); return Err(Error::Overflow); - } else if r.events_stopped.read().bits() != 0 { - r.events_stopped.reset(); + } else if r.events_stopped().read() != 0 { + r.events_stopped().write_value(0); return match status { Status::Read => Ok(Command::Read), Status::Write => { - let n = r.rxd.amount.read().bits() as usize; + let n = r.rxd().amount().read().0 as usize; Ok(Command::Write(n)) } }; - } else if r.events_read.read().bits() != 0 { - r.events_read.reset(); - let n = r.rxd.amount.read().bits() as usize; + } else if r.events_read().read() != 0 { + r.events_read().write_value(0); + let n = r.rxd().amount().read().0 as usize; return Ok(Command::WriteRead(n)); } else if Instant::now() > deadline { - r.tasks_stop.write(|w| unsafe { w.bits(1) }); + r.tasks_stop().write_value(1); return Err(Error::Timeout); } } @@ -466,20 +462,20 @@ impl<'d, T: Instance> Twis<'d, T> { s.waker.register(cx.waker()); // stop if an error occurred - if r.events_error.read().bits() != 0 { - r.events_error.reset(); - r.tasks_stop.write(|w| unsafe { w.bits(1) }); - let errorsrc = r.errorsrc.read(); - if errorsrc.overread().is_detected() { + if r.events_error().read() != 0 { + r.events_error().write_value(0); + r.tasks_stop().write_value(1); + let errorsrc = r.errorsrc().read(); + if errorsrc.overread() { return Poll::Ready(Err(Error::OverRead)); - } else if errorsrc.dnack().is_received() { + } else if errorsrc.dnack() { return Poll::Ready(Err(Error::DataNack)); } else { return Poll::Ready(Err(Error::Bus)); } - } else if r.events_stopped.read().bits() != 0 { - r.events_stopped.reset(); - let n = r.txd.amount.read().bits() as usize; + } else if r.events_stopped().read() != 0 { + r.events_stopped().write_value(0); + let n = r.txd().amount().read().0 as usize; return Poll::Ready(Ok(n)); } @@ -496,18 +492,18 @@ impl<'d, T: Instance> Twis<'d, T> { s.waker.register(cx.waker()); // stop if an error occurred - if r.events_error.read().bits() != 0 { - r.events_error.reset(); - r.tasks_stop.write(|w| unsafe { w.bits(1) }); + if r.events_error().read() != 0 { + r.events_error().write_value(0); + r.tasks_stop().write_value(1); return Poll::Ready(Err(Error::Overflow)); - } else if r.events_read.read().bits() != 0 { - r.events_read.reset(); + } else if r.events_read().read() != 0 { + r.events_read().write_value(0); return Poll::Ready(Ok(Status::Read)); - } else if r.events_write.read().bits() != 0 { - r.events_write.reset(); + } else if r.events_write().read() != 0 { + r.events_write().write_value(0); return Poll::Ready(Ok(Status::Write)); - } else if r.events_stopped.read().bits() != 0 { - r.events_stopped.reset(); + } else if r.events_stopped().read() != 0 { + r.events_stopped().write_value(0); return Poll::Ready(Err(Error::Bus)); } Poll::Pending @@ -523,22 +519,22 @@ impl<'d, T: Instance> Twis<'d, T> { s.waker.register(cx.waker()); // stop if an error occurred - if r.events_error.read().bits() != 0 { - r.events_error.reset(); - r.tasks_stop.write(|w| unsafe { w.bits(1) }); + if r.events_error().read() != 0 { + r.events_error().write_value(0); + r.tasks_stop().write_value(1); return Poll::Ready(Err(Error::Overflow)); - } else if r.events_stopped.read().bits() != 0 { - r.events_stopped.reset(); + } else if r.events_stopped().read() != 0 { + r.events_stopped().write_value(0); return match status { Status::Read => Poll::Ready(Ok(Command::Read)), Status::Write => { - let n = r.rxd.amount.read().bits() as usize; + let n = r.rxd().amount().read().0 as usize; Poll::Ready(Ok(Command::Write(n))) } }; - } else if r.events_read.read().bits() != 0 { - r.events_read.reset(); - let n = r.rxd.amount.read().bits() as usize; + } else if r.events_read().read() != 0 { + r.events_read().write_value(0); + let n = r.rxd().amount().read().0 as usize; return Poll::Ready(Ok(Command::WriteRead(n))); } Poll::Pending @@ -554,19 +550,25 @@ impl<'d, T: Instance> Twis<'d, T> { unsafe { self.set_tx_buffer(buffer)? }; // Clear events - r.events_stopped.reset(); - r.events_error.reset(); + r.events_stopped().write_value(0); + r.events_error().write_value(0); self.clear_errorsrc(); if inten { - r.intenset.write(|w| w.stopped().set().error().set()); + r.intenset().write(|w| { + w.set_stopped(true); + w.set_error(true); + }); } else { - r.intenclr.write(|w| w.stopped().clear().error().clear()); + r.intenclr().write(|w| { + w.set_stopped(true); + w.set_error(true); + }); } // Start write operation. - r.tasks_preparetx.write(|w| unsafe { w.bits(1) }); - r.tasks_resume.write(|w| unsafe { w.bits(1) }); + r.tasks_preparetx().write_value(1); + r.tasks_resume().write_value(1); Ok(()) } @@ -591,22 +593,30 @@ impl<'d, T: Instance> Twis<'d, T> { unsafe { self.set_rx_buffer(buffer)? }; // Clear events - r.events_read.reset(); - r.events_write.reset(); - r.events_stopped.reset(); - r.events_error.reset(); + r.events_read().write_value(0); + r.events_write().write_value(0); + r.events_stopped().write_value(0); + r.events_error().write_value(0); self.clear_errorsrc(); if inten { - r.intenset - .write(|w| w.stopped().set().error().set().read().set().write().set()); + r.intenset().write(|w| { + w.set_stopped(true); + w.set_error(true); + w.set_read(true); + w.set_write(true); + }); } else { - r.intenclr - .write(|w| w.stopped().clear().error().clear().read().clear().write().clear()); + r.intenclr().write(|w| { + w.set_stopped(true); + w.set_error(true); + w.set_read(true); + w.set_write(true); + }); } // Start read operation. - r.tasks_preparerx.write(|w| unsafe { w.bits(1) }); + r.tasks_preparerx().write_value(1); Ok(()) } @@ -616,16 +626,24 @@ impl<'d, T: Instance> Twis<'d, T> { compiler_fence(SeqCst); // Clear events - r.events_read.reset(); - r.events_write.reset(); - r.events_stopped.reset(); - r.events_error.reset(); + r.events_read().write_value(0); + r.events_write().write_value(0); + r.events_stopped().write_value(0); + r.events_error().write_value(0); self.clear_errorsrc(); if inten { - r.intenset.write(|w| w.stopped().set().error().set().read().set()); + r.intenset().write(|w| { + w.set_stopped(true); + w.set_error(true); + w.set_read(true); + }); } else { - r.intenclr.write(|w| w.stopped().clear().error().clear().read().clear()); + r.intenclr().write(|w| { + w.set_stopped(true); + w.set_error(true); + w.set_read(true); + }); } Ok(()) @@ -745,10 +763,10 @@ impl<'a, T: Instance> Drop for Twis<'a, T> { // disable! let r = T::regs(); - r.enable.write(|w| w.enable().disabled()); + r.enable().write(|w| w.set_enable(vals::Enable::DISABLED)); - gpio::deconfigure_pin(r.psel.sda.read().bits()); - gpio::deconfigure_pin(r.psel.scl.read().bits()); + gpio::deconfigure_pin(r.psel().sda().read()); + gpio::deconfigure_pin(r.psel().scl().read()); trace!("twis drop: done"); } @@ -767,7 +785,7 @@ impl State { } pub(crate) trait SealedInstance { - fn regs() -> &'static pac::twis0::RegisterBlock; + fn regs() -> pac::twis::Twis; fn state() -> &'static State; } @@ -781,8 +799,8 @@ pub trait Instance: Peripheral

+ SealedInstance + 'static { macro_rules! impl_twis { ($type:ident, $pac_type:ident, $irq:ident) => { impl crate::twis::SealedInstance for peripherals::$type { - fn regs() -> &'static pac::twis0::RegisterBlock { - unsafe { &*pac::$pac_type::ptr() } + fn regs() -> pac::twis::Twis { + pac::$pac_type } fn state() -> &'static crate::twis::State { static STATE: crate::twis::State = crate::twis::State::new(); diff --git a/embassy-nrf/src/uarte.rs b/embassy-nrf/src/uarte.rs index 4cf193617..2a59d029d 100644 --- a/embassy-nrf/src/uarte.rs +++ b/embassy-nrf/src/uarte.rs @@ -21,13 +21,14 @@ use core::task::Poll; use embassy_hal_internal::drop::OnDrop; use embassy_hal_internal::{into_ref, PeripheralRef}; use embassy_sync::waitqueue::AtomicWaker; -use pac::uarte0::RegisterBlock; // Re-export SVD variants to allow user to directly set values. -pub use pac::uarte0::{baudrate::BAUDRATE_A as Baudrate, config::PARITY_A as Parity}; +pub use pac::uarte::vals::{Baudrate, ConfigParity as Parity}; use crate::chip::{EASY_DMA_SIZE, FORCE_COPY_BUFFER_SIZE}; -use crate::gpio::{self, AnyPin, Pin as GpioPin, PselBits, SealedPin as _}; +use crate::gpio::{self, AnyPin, Pin as GpioPin, PselBits, SealedPin as _, DISCONNECTED}; use crate::interrupt::typelevel::Interrupt; +use crate::pac::gpio::vals as gpiovals; +use crate::pac::uarte::vals; use crate::ppi::{AnyConfigurableChannel, ConfigurableChannel, Event, Ppi, Task}; use crate::timer::{Frequency, Instance as TimerInstance, Timer}; use crate::util::slice_in_ram_or; @@ -54,7 +55,7 @@ impl Default for Config { bitflags::bitflags! { /// Error source flags - pub struct ErrorSource: u32 { + pub(crate) struct ErrorSource: u32 { /// Buffer overrun const OVERRUN = 0x01; /// Parity error @@ -112,20 +113,20 @@ impl interrupt::typelevel::Handler for InterruptHandl let r = T::regs(); let s = T::state(); - let endrx = r.events_endrx.read().bits(); - let error = r.events_error.read().bits(); + let endrx = r.events_endrx().read(); + let error = r.events_error().read(); if endrx != 0 || error != 0 { s.rx_waker.wake(); if endrx != 0 { - r.intenclr.write(|w| w.endrx().clear()); + r.intenclr().write(|w| w.set_endrx(true)); } if error != 0 { - r.intenclr.write(|w| w.error().clear()); + r.intenclr().write(|w| w.set_error(true)); } } - if r.events_endtx.read().bits() != 0 { + if r.events_endtx().read() != 0 { s.tx_waker.wake(); - r.intenclr.write(|w| w.endtx().clear()); + r.intenclr().write(|w| w.set_endtx(true)); } } } @@ -200,28 +201,12 @@ impl<'d, T: Instance> Uarte<'d, T> { _ => panic!("RTS and CTS pins must be either both set or none set."), }; configure(r, config, hardware_flow_control); - - rxd.conf().write(|w| w.input().connect().drive().h0h1()); - r.psel.rxd.write(|w| unsafe { w.bits(rxd.psel_bits()) }); - - txd.set_high(); - txd.conf().write(|w| w.dir().output().drive().h0h1()); - r.psel.txd.write(|w| unsafe { w.bits(txd.psel_bits()) }); - - if let Some(pin) = &cts { - pin.conf().write(|w| w.input().connect().drive().h0h1()); - } - r.psel.cts.write(|w| unsafe { w.bits(cts.psel_bits()) }); - - if let Some(pin) = &rts { - pin.set_high(); - pin.conf().write(|w| w.dir().output().drive().h0h1()); - } - r.psel.rts.write(|w| unsafe { w.bits(rts.psel_bits()) }); + configure_rx_pins(r, rxd, rts); + configure_tx_pins(r, txd, cts); T::Interrupt::unpend(); unsafe { T::Interrupt::enable() }; - r.enable.write(|w| w.enable().enabled()); + r.enable().write(|w| w.set_enable(vals::Enable::ENABLED)); let s = T::state(); s.tx_rx_refcount.store(2, Ordering::Relaxed); @@ -264,7 +249,7 @@ impl<'d, T: Instance> Uarte<'d, T> { /// Return the endtx event for use with PPI pub fn event_endtx(&self) -> Event { let r = T::regs(); - Event::from_reg(&r.events_endtx) + Event::from_reg(r.events_endtx()) } /// Read bytes until the buffer is filled. @@ -298,27 +283,72 @@ impl<'d, T: Instance> Uarte<'d, T> { } } -pub(crate) fn configure(r: &RegisterBlock, config: Config, hardware_flow_control: bool) { - r.config.write(|w| { - w.hwfc().bit(hardware_flow_control); - w.parity().variant(config.parity); - w +pub(crate) fn configure_tx_pins( + r: pac::uarte::Uarte, + txd: PeripheralRef<'_, AnyPin>, + cts: Option>, +) { + txd.set_high(); + txd.conf().write(|w| { + w.set_dir(gpiovals::Dir::OUTPUT); + w.set_input(gpiovals::Input::DISCONNECT); + w.set_drive(gpiovals::Drive::H0H1); }); - r.baudrate.write(|w| w.baudrate().variant(config.baudrate)); + r.psel().txd().write_value(txd.psel_bits()); + + if let Some(pin) = &cts { + pin.conf().write(|w| { + w.set_dir(gpiovals::Dir::INPUT); + w.set_input(gpiovals::Input::CONNECT); + w.set_drive(gpiovals::Drive::H0H1); + }); + } + r.psel().cts().write_value(cts.psel_bits()); +} + +pub(crate) fn configure_rx_pins( + r: pac::uarte::Uarte, + rxd: PeripheralRef<'_, AnyPin>, + rts: Option>, +) { + rxd.conf().write(|w| { + w.set_dir(gpiovals::Dir::INPUT); + w.set_input(gpiovals::Input::CONNECT); + w.set_drive(gpiovals::Drive::H0H1); + }); + r.psel().rxd().write_value(rxd.psel_bits()); + + if let Some(pin) = &rts { + pin.set_high(); + pin.conf().write(|w| { + w.set_dir(gpiovals::Dir::OUTPUT); + w.set_input(gpiovals::Input::DISCONNECT); + w.set_drive(gpiovals::Drive::H0H1); + }); + } + r.psel().rts().write_value(rts.psel_bits()); +} + +pub(crate) fn configure(r: pac::uarte::Uarte, config: Config, hardware_flow_control: bool) { + r.config().write(|w| { + w.set_hwfc(hardware_flow_control); + w.set_parity(config.parity); + }); + r.baudrate().write(|w| w.set_baudrate(config.baudrate)); // Disable all interrupts - r.intenclr.write(|w| unsafe { w.bits(0xFFFF_FFFF) }); + r.intenclr().write(|w| w.0 = 0xFFFF_FFFF); // Reset rxstarted, txstarted. These are used by drop to know whether a transfer was // stopped midway or not. - r.events_rxstarted.reset(); - r.events_txstarted.reset(); + r.events_rxstarted().write_value(0); + r.events_txstarted().write_value(0); // reset all pins - r.psel.txd.write(|w| w.connect().disconnected()); - r.psel.rxd.write(|w| w.connect().disconnected()); - r.psel.cts.write(|w| w.connect().disconnected()); - r.psel.rts.write(|w| w.connect().disconnected()); + r.psel().txd().write_value(DISCONNECTED); + r.psel().rxd().write_value(DISCONNECTED); + r.psel().cts().write_value(DISCONNECTED); + r.psel().rts().write_value(DISCONNECTED); apply_workaround_for_enable_anomaly(r); } @@ -356,19 +386,11 @@ impl<'d, T: Instance> UarteTx<'d, T> { let r = T::regs(); configure(r, config, cts.is_some()); - - txd.set_high(); - txd.conf().write(|w| w.dir().output().drive().s0s1()); - r.psel.txd.write(|w| unsafe { w.bits(txd.psel_bits()) }); - - if let Some(pin) = &cts { - pin.conf().write(|w| w.input().connect().drive().h0h1()); - } - r.psel.cts.write(|w| unsafe { w.bits(cts.psel_bits()) }); + configure_tx_pins(r, txd, cts); T::Interrupt::unpend(); unsafe { T::Interrupt::enable() }; - r.enable.write(|w| w.enable().enabled()); + r.enable().write(|w| w.set_enable(vals::Enable::ENABLED)); let s = T::state(); s.tx_rx_refcount.store(1, Ordering::Relaxed); @@ -410,29 +432,29 @@ impl<'d, T: Instance> UarteTx<'d, T> { let drop = OnDrop::new(move || { trace!("write drop: stopping"); - r.intenclr.write(|w| w.endtx().clear()); - r.events_txstopped.reset(); - r.tasks_stoptx.write(|w| unsafe { w.bits(1) }); + r.intenclr().write(|w| w.set_endtx(true)); + r.events_txstopped().write_value(0); + r.tasks_stoptx().write_value(1); // TX is stopped almost instantly, spinning is fine. - while r.events_endtx.read().bits() == 0 {} + while r.events_endtx().read() == 0 {} trace!("write drop: stopped"); }); - r.txd.ptr.write(|w| unsafe { w.ptr().bits(ptr as u32) }); - r.txd.maxcnt.write(|w| unsafe { w.maxcnt().bits(len as _) }); + r.txd().ptr().write_value(ptr as u32); + r.txd().maxcnt().write(|w| w.set_maxcnt(len as _)); - r.events_endtx.reset(); - r.intenset.write(|w| w.endtx().set()); + r.events_endtx().write_value(0); + r.intenset().write(|w| w.set_endtx(true)); compiler_fence(Ordering::SeqCst); trace!("starttx"); - r.tasks_starttx.write(|w| unsafe { w.bits(1) }); + r.tasks_starttx().write_value(1); poll_fn(|cx| { s.tx_waker.register(cx.waker()); - if r.events_endtx.read().bits() != 0 { + if r.events_endtx().read() != 0 { return Poll::Ready(()); } Poll::Pending @@ -440,7 +462,7 @@ impl<'d, T: Instance> UarteTx<'d, T> { .await; compiler_fence(Ordering::SeqCst); - r.events_txstarted.reset(); + r.events_txstarted().write_value(0); drop.defuse(); Ok(()) @@ -476,21 +498,21 @@ impl<'d, T: Instance> UarteTx<'d, T> { let r = T::regs(); - r.txd.ptr.write(|w| unsafe { w.ptr().bits(ptr as u32) }); - r.txd.maxcnt.write(|w| unsafe { w.maxcnt().bits(len as _) }); + r.txd().ptr().write_value(ptr as u32); + r.txd().maxcnt().write(|w| w.set_maxcnt(len as _)); - r.events_endtx.reset(); - r.intenclr.write(|w| w.endtx().clear()); + r.events_endtx().write_value(0); + r.intenclr().write(|w| w.set_endtx(true)); compiler_fence(Ordering::SeqCst); trace!("starttx"); - r.tasks_starttx.write(|w| unsafe { w.bits(1) }); + r.tasks_starttx().write_value(1); - while r.events_endtx.read().bits() == 0 {} + while r.events_endtx().read() == 0 {} compiler_fence(Ordering::SeqCst); - r.events_txstarted.reset(); + r.events_txstarted().write_value(0); Ok(()) } @@ -502,11 +524,11 @@ impl<'a, T: Instance> Drop for UarteTx<'a, T> { let r = T::regs(); - let did_stoptx = r.events_txstarted.read().bits() != 0; + let did_stoptx = r.events_txstarted().read() != 0; trace!("did_stoptx {}", did_stoptx); // Wait for txstopped, if needed. - while did_stoptx && r.events_txstopped.read().bits() == 0 {} + while did_stoptx && r.events_txstopped().read() == 0 {} let s = T::state(); @@ -541,9 +563,9 @@ impl<'d, T: Instance> UarteRx<'d, T> { /// Check for errors and clear the error register if an error occured. fn check_and_clear_errors(&mut self) -> Result<(), Error> { let r = T::regs(); - let err_bits = r.errorsrc.read().bits(); - r.errorsrc.write(|w| unsafe { w.bits(err_bits) }); - ErrorSource::from_bits_truncate(err_bits).check() + let err_bits = r.errorsrc().read(); + r.errorsrc().write_value(err_bits); + ErrorSource::from_bits_truncate(err_bits.0).check() } fn new_inner( @@ -555,19 +577,11 @@ impl<'d, T: Instance> UarteRx<'d, T> { let r = T::regs(); configure(r, config, rts.is_some()); - - rxd.conf().write(|w| w.input().connect().drive().h0h1()); - r.psel.rxd.write(|w| unsafe { w.bits(rxd.psel_bits()) }); - - if let Some(pin) = &rts { - pin.set_high(); - pin.conf().write(|w| w.dir().output().drive().h0h1()); - } - r.psel.rts.write(|w| unsafe { w.bits(rts.psel_bits()) }); + configure_rx_pins(r, rxd, rts); T::Interrupt::unpend(); unsafe { T::Interrupt::enable() }; - r.enable.write(|w| w.enable().enabled()); + r.enable().write(|w| w.set_enable(vals::Enable::ENABLED)); let s = T::state(); s.tx_rx_refcount.store(1, Ordering::Relaxed); @@ -594,8 +608,8 @@ impl<'d, T: Instance> UarteRx<'d, T> { // We want to stop RX if line is idle for 2 bytes worth of time // That is 20 bits (each byte is 1 start bit + 8 data bits + 1 stop bit) // This gives us the amount of 16M ticks for 20 bits. - let baudrate = r.baudrate.read().baudrate().variant().unwrap(); - let timeout = 0x8000_0000 / (baudrate as u32 / 40); + let baudrate = r.baudrate().read().baudrate(); + let timeout = 0x8000_0000 / (baudrate.to_bits() / 40); timer.set_frequency(Frequency::F16MHz); timer.cc(0).write(timeout); @@ -604,7 +618,7 @@ impl<'d, T: Instance> UarteRx<'d, T> { let mut ppi_ch1 = Ppi::new_one_to_two( ppi_ch1.map_into(), - Event::from_reg(&r.events_rxdrdy), + Event::from_reg(r.events_rxdrdy()), timer.task_clear(), timer.task_start(), ); @@ -613,7 +627,7 @@ impl<'d, T: Instance> UarteRx<'d, T> { let mut ppi_ch2 = Ppi::new_one_to_one( ppi_ch2.map_into(), timer.cc(0).event_compare(), - Task::from_reg(&r.tasks_stoprx), + Task::from_reg(r.tasks_stoprx()), ); ppi_ch2.enable(); @@ -643,43 +657,42 @@ impl<'d, T: Instance> UarteRx<'d, T> { let drop = OnDrop::new(move || { trace!("read drop: stopping"); - r.intenclr.write(|w| { - w.endrx().clear(); - w.error().clear() + r.intenclr().write(|w| { + w.set_endrx(true); + w.set_error(true); }); - r.events_rxto.reset(); - r.events_error.reset(); - r.errorsrc.reset(); - r.tasks_stoprx.write(|w| unsafe { w.bits(1) }); + r.events_rxto().write_value(0); + r.events_error().write_value(0); + r.tasks_stoprx().write_value(1); - while r.events_endrx.read().bits() == 0 {} + while r.events_endrx().read() == 0 {} trace!("read drop: stopped"); }); - r.rxd.ptr.write(|w| unsafe { w.ptr().bits(ptr as u32) }); - r.rxd.maxcnt.write(|w| unsafe { w.maxcnt().bits(len as _) }); + r.rxd().ptr().write_value(ptr as u32); + r.rxd().maxcnt().write(|w| w.set_maxcnt(len as _)); - r.events_endrx.reset(); - r.events_error.reset(); - r.intenset.write(|w| { - w.endrx().set(); - w.error().set() + r.events_endrx().write_value(0); + r.events_error().write_value(0); + r.intenset().write(|w| { + w.set_endrx(true); + w.set_error(true); }); compiler_fence(Ordering::SeqCst); trace!("startrx"); - r.tasks_startrx.write(|w| unsafe { w.bits(1) }); + r.tasks_startrx().write_value(1); let result = poll_fn(|cx| { s.rx_waker.register(cx.waker()); if let Err(e) = self.check_and_clear_errors() { - r.tasks_stoprx.write(|w| unsafe { w.bits(1) }); + r.tasks_stoprx().write_value(1); return Poll::Ready(Err(e)); } - if r.events_endrx.read().bits() != 0 { + if r.events_endrx().read() != 0 { return Poll::Ready(Ok(())); } Poll::Pending @@ -687,7 +700,7 @@ impl<'d, T: Instance> UarteRx<'d, T> { .await; compiler_fence(Ordering::SeqCst); - r.events_rxstarted.reset(); + r.events_rxstarted().write_value(0); drop.defuse(); result @@ -707,25 +720,25 @@ impl<'d, T: Instance> UarteRx<'d, T> { let r = T::regs(); - r.rxd.ptr.write(|w| unsafe { w.ptr().bits(ptr as u32) }); - r.rxd.maxcnt.write(|w| unsafe { w.maxcnt().bits(len as _) }); + r.rxd().ptr().write_value(ptr as u32); + r.rxd().maxcnt().write(|w| w.set_maxcnt(len as _)); - r.events_endrx.reset(); - r.events_error.reset(); - r.intenclr.write(|w| { - w.endrx().clear(); - w.error().clear() + r.events_endrx().write_value(0); + r.events_error().write_value(0); + r.intenclr().write(|w| { + w.set_endrx(true); + w.set_error(true); }); compiler_fence(Ordering::SeqCst); trace!("startrx"); - r.tasks_startrx.write(|w| unsafe { w.bits(1) }); + r.tasks_startrx().write_value(1); - while r.events_endrx.read().bits() == 0 && r.events_error.read().bits() == 0 {} + while r.events_endrx().read() == 0 && r.events_error().read() == 0 {} compiler_fence(Ordering::SeqCst); - r.events_rxstarted.reset(); + r.events_rxstarted().write_value(0); self.check_and_clear_errors() } @@ -737,11 +750,11 @@ impl<'a, T: Instance> Drop for UarteRx<'a, T> { let r = T::regs(); - let did_stoprx = r.events_rxstarted.read().bits() != 0; + let did_stoprx = r.events_rxstarted().read() != 0; trace!("did_stoprx {}", did_stoprx); // Wait for rxto, if needed. - while did_stoprx && r.events_rxto.read().bits() == 0 {} + while did_stoprx && r.events_rxto().read() == 0 {} let s = T::state(); @@ -794,39 +807,39 @@ impl<'d, T: Instance, U: TimerInstance> UarteRxWithIdle<'d, T, U> { let drop = OnDrop::new(|| { self.timer.stop(); - r.intenclr.write(|w| { - w.endrx().clear(); - w.error().clear() + r.intenclr().write(|w| { + w.set_endrx(true); + w.set_error(true); }); - r.events_rxto.reset(); - r.events_error.reset(); - r.tasks_stoprx.write(|w| unsafe { w.bits(1) }); + r.events_rxto().write_value(0); + r.events_error().write_value(0); + r.tasks_stoprx().write_value(1); - while r.events_endrx.read().bits() == 0 {} + while r.events_endrx().read() == 0 {} }); - r.rxd.ptr.write(|w| unsafe { w.ptr().bits(ptr as u32) }); - r.rxd.maxcnt.write(|w| unsafe { w.maxcnt().bits(len as _) }); + r.rxd().ptr().write_value(ptr as u32); + r.rxd().maxcnt().write(|w| w.set_maxcnt(len as _)); - r.events_endrx.reset(); - r.events_error.reset(); - r.intenset.write(|w| { - w.endrx().set(); - w.error().set() + r.events_endrx().write_value(0); + r.events_error().write_value(0); + r.intenset().write(|w| { + w.set_endrx(true); + w.set_error(true); }); compiler_fence(Ordering::SeqCst); - r.tasks_startrx.write(|w| unsafe { w.bits(1) }); + r.tasks_startrx().write_value(1); let result = poll_fn(|cx| { s.rx_waker.register(cx.waker()); if let Err(e) = self.rx.check_and_clear_errors() { - r.tasks_stoprx.write(|w| unsafe { w.bits(1) }); + r.tasks_stoprx().write_value(1); return Poll::Ready(Err(e)); } - if r.events_endrx.read().bits() != 0 { + if r.events_endrx().read() != 0 { return Poll::Ready(Ok(())); } @@ -835,10 +848,10 @@ impl<'d, T: Instance, U: TimerInstance> UarteRxWithIdle<'d, T, U> { .await; compiler_fence(Ordering::SeqCst); - let n = r.rxd.amount.read().amount().bits() as usize; + let n = r.rxd().amount().read().0 as usize; self.timer.stop(); - r.events_rxstarted.reset(); + r.events_rxstarted().write_value(0); drop.defuse(); @@ -863,56 +876,57 @@ impl<'d, T: Instance, U: TimerInstance> UarteRxWithIdle<'d, T, U> { self.ppi_ch1.enable(); - r.rxd.ptr.write(|w| unsafe { w.ptr().bits(ptr as u32) }); - r.rxd.maxcnt.write(|w| unsafe { w.maxcnt().bits(len as _) }); + r.rxd().ptr().write_value(ptr as u32); + r.rxd().maxcnt().write(|w| w.set_maxcnt(len as _)); - r.events_endrx.reset(); - r.events_error.reset(); - r.intenclr.write(|w| { - w.endrx().clear(); - w.error().clear() + r.events_endrx().write_value(0); + r.events_error().write_value(0); + r.intenclr().write(|w| { + w.set_endrx(true); + w.set_error(true); }); compiler_fence(Ordering::SeqCst); - r.tasks_startrx.write(|w| unsafe { w.bits(1) }); + r.tasks_startrx().write_value(1); - while r.events_endrx.read().bits() == 0 && r.events_error.read().bits() == 0 {} + while r.events_endrx().read() == 0 && r.events_error().read() == 0 {} compiler_fence(Ordering::SeqCst); - let n = r.rxd.amount.read().amount().bits() as usize; + let n = r.rxd().amount().read().0 as usize; self.timer.stop(); - r.events_rxstarted.reset(); + r.events_rxstarted().write_value(0); self.rx.check_and_clear_errors().map(|_| n) } } #[cfg(not(any(feature = "_nrf9160", feature = "_nrf5340")))] -pub(crate) fn apply_workaround_for_enable_anomaly(_r: &crate::pac::uarte0::RegisterBlock) { +pub(crate) fn apply_workaround_for_enable_anomaly(_r: pac::uarte::Uarte) { // Do nothing } #[cfg(any(feature = "_nrf9160", feature = "_nrf5340"))] -pub(crate) fn apply_workaround_for_enable_anomaly(r: &crate::pac::uarte0::RegisterBlock) { +pub(crate) fn apply_workaround_for_enable_anomaly(r: pac::uarte::Uarte) { // Apply workaround for anomalies: // - nRF9160 - anomaly 23 // - nRF5340 - anomaly 44 - let rxenable_reg: *const u32 = ((r as *const _ as usize) + 0x564) as *const u32; - let txenable_reg: *const u32 = ((r as *const _ as usize) + 0x568) as *const u32; + let rp = r.as_ptr() as *mut u32; + let rxenable_reg = unsafe { rp.add(0x564 / 4) }; + let txenable_reg = unsafe { rp.add(0x568 / 4) }; // NB Safety: This is taken from Nordic's driver - // https://github.com/NordicSemiconductor/nrfx/blob/master/drivers/src/nrfx_uarte.c#L197 if unsafe { core::ptr::read_volatile(txenable_reg) } == 1 { - r.tasks_stoptx.write(|w| unsafe { w.bits(1) }); + r.tasks_stoptx().write_value(1); } // NB Safety: This is taken from Nordic's driver - // https://github.com/NordicSemiconductor/nrfx/blob/master/drivers/src/nrfx_uarte.c#L197 if unsafe { core::ptr::read_volatile(rxenable_reg) } == 1 { - r.enable.write(|w| w.enable().enabled()); - r.tasks_stoprx.write(|w| unsafe { w.bits(1) }); + r.enable().write(|w| w.set_enable(vals::Enable::ENABLED)); + r.tasks_stoprx().write_value(1); let mut workaround_succeded = false; // The UARTE is able to receive up to four bytes after the STOPRX task has been triggered. @@ -933,23 +947,23 @@ pub(crate) fn apply_workaround_for_enable_anomaly(r: &crate::pac::uarte0::Regist panic!("Failed to apply workaround for UART"); } - let errors = r.errorsrc.read().bits(); - // NB Safety: safe to write back the bits we just read to clear them - r.errorsrc.write(|w| unsafe { w.bits(errors) }); - r.enable.write(|w| w.enable().disabled()); + // write back the bits we just read to clear them + let errors = r.errorsrc().read(); + r.errorsrc().write_value(errors); + r.enable().write(|w| w.set_enable(vals::Enable::DISABLED)); } } -pub(crate) fn drop_tx_rx(r: &pac::uarte0::RegisterBlock, s: &State) { +pub(crate) fn drop_tx_rx(r: pac::uarte::Uarte, s: &State) { if s.tx_rx_refcount.fetch_sub(1, Ordering::Relaxed) == 1 { // Finally we can disable, and we do so for the peripheral // i.e. not just rx concerns. - r.enable.write(|w| w.enable().disabled()); + r.enable().write(|w| w.set_enable(vals::Enable::DISABLED)); - gpio::deconfigure_pin(r.psel.rxd.read().bits()); - gpio::deconfigure_pin(r.psel.txd.read().bits()); - gpio::deconfigure_pin(r.psel.rts.read().bits()); - gpio::deconfigure_pin(r.psel.cts.read().bits()); + gpio::deconfigure_pin(r.psel().rxd().read()); + gpio::deconfigure_pin(r.psel().txd().read()); + gpio::deconfigure_pin(r.psel().rts().read()); + gpio::deconfigure_pin(r.psel().cts().read()); trace!("uarte tx and rx drop: done"); } @@ -971,7 +985,7 @@ impl State { } pub(crate) trait SealedInstance { - fn regs() -> &'static pac::uarte0::RegisterBlock; + fn regs() -> pac::uarte::Uarte; fn state() -> &'static State; fn buffered_state() -> &'static crate::buffered_uarte::State; } @@ -986,8 +1000,8 @@ pub trait Instance: Peripheral

+ SealedInstance + 'static + Send { macro_rules! impl_uarte { ($type:ident, $pac_type:ident, $irq:ident) => { impl crate::uarte::SealedInstance for peripherals::$type { - fn regs() -> &'static pac::uarte0::RegisterBlock { - unsafe { &*pac::$pac_type::ptr() } + fn regs() -> pac::uarte::Uarte { + pac::$pac_type } fn state() -> &'static crate::uarte::State { static STATE: crate::uarte::State = crate::uarte::State::new(); diff --git a/embassy-nrf/src/usb/mod.rs b/embassy-nrf/src/usb/mod.rs index 8cbb1a350..52c9c532b 100644 --- a/embassy-nrf/src/usb/mod.rs +++ b/embassy-nrf/src/usb/mod.rs @@ -15,10 +15,10 @@ use embassy_hal_internal::{into_ref, PeripheralRef}; use embassy_sync::waitqueue::AtomicWaker; use embassy_usb_driver as driver; use embassy_usb_driver::{Direction, EndpointAddress, EndpointError, EndpointInfo, EndpointType, Event, Unsupported}; -use pac::usbd::RegisterBlock; use self::vbus_detect::VbusDetect; use crate::interrupt::typelevel::Interrupt; +use crate::pac::usbd::vals; use crate::util::slice_in_ram; use crate::{interrupt, pac, Peripheral}; @@ -38,19 +38,19 @@ impl interrupt::typelevel::Handler for InterruptHandl unsafe fn on_interrupt() { let regs = T::regs(); - if regs.events_usbreset.read().bits() != 0 { - regs.intenclr.write(|w| w.usbreset().clear()); + if regs.events_usbreset().read() != 0 { + regs.intenclr().write(|w| w.set_usbreset(true)); BUS_WAKER.wake(); EP0_WAKER.wake(); } - if regs.events_ep0setup.read().bits() != 0 { - regs.intenclr.write(|w| w.ep0setup().clear()); + if regs.events_ep0setup().read() != 0 { + regs.intenclr().write(|w| w.set_ep0setup(true)); EP0_WAKER.wake(); } - if regs.events_ep0datadone.read().bits() != 0 { - regs.intenclr.write(|w| w.ep0datadone().clear()); + if regs.events_ep0datadone().read() != 0 { + regs.intenclr().write(|w| w.set_ep0datadone(true)); EP0_WAKER.wake(); } @@ -63,22 +63,22 @@ impl interrupt::typelevel::Handler for InterruptHandl // Therefore, it's fine to clear just the event, and let main thread // check the individual bits in EVENTCAUSE and EPDATASTATUS. It // doesn't cause an infinite irq loop. - if regs.events_usbevent.read().bits() != 0 { - regs.events_usbevent.reset(); + if regs.events_usbevent().read() != 0 { + regs.events_usbevent().write_value(0); BUS_WAKER.wake(); } - if regs.events_epdata.read().bits() != 0 { - regs.events_epdata.reset(); + if regs.events_epdata().read() != 0 { + regs.events_epdata().write_value(0); - let r = regs.epdatastatus.read().bits(); - regs.epdatastatus.write(|w| unsafe { w.bits(r) }); - READY_ENDPOINTS.fetch_or(r, Ordering::AcqRel); + let r = regs.epdatastatus().read(); + regs.epdatastatus().write_value(r); + READY_ENDPOINTS.fetch_or(r.0, Ordering::AcqRel); for i in 1..=7 { - if r & In::mask(i) != 0 { + if r.0 & In::mask(i) != 0 { In::waker(i).wake(); } - if r & Out::mask(i) != 0 { + if r.0 & Out::mask(i) != 0 { Out::waker(i).wake(); } } @@ -181,35 +181,34 @@ impl<'d, T: Instance, V: VbusDetect> driver::Bus for Bus<'d, T, V> { errata::pre_enable(); - regs.enable.write(|w| w.enable().enabled()); + regs.enable().write(|w| w.set_enable(true)); // Wait until the peripheral is ready. - regs.intenset.write(|w| w.usbevent().set_bit()); + regs.intenset().write(|w| w.set_usbevent(true)); poll_fn(|cx| { BUS_WAKER.register(cx.waker()); - if regs.eventcause.read().ready().is_ready() { + if regs.eventcause().read().ready() { Poll::Ready(()) } else { Poll::Pending } }) .await; - regs.eventcause.write(|w| w.ready().clear_bit_by_one()); + regs.eventcause().write(|w| w.set_ready(true)); errata::post_enable(); unsafe { NVIC::unmask(pac::Interrupt::USBD) }; - regs.intenset.write(|w| { - w.usbreset().set_bit(); - w.usbevent().set_bit(); - w.epdata().set_bit(); - w + regs.intenset().write(|w| { + w.set_usbreset(true); + w.set_usbevent(true); + w.set_epdata(true); }); if self.vbus_detect.wait_power_ready().await.is_ok() { // Enable the USB pullup, allowing enumeration. - regs.usbpullup.write(|w| w.connect().enabled()); + regs.usbpullup().write(|w| w.set_connect(true)); trace!("enabled"); } else { trace!("usb power not ready due to usb removal"); @@ -218,7 +217,7 @@ impl<'d, T: Instance, V: VbusDetect> driver::Bus for Bus<'d, T, V> { async fn disable(&mut self) { let regs = T::regs(); - regs.enable.write(|x| x.enable().disabled()); + regs.enable().write(|x| x.set_enable(false)); } async fn poll(&mut self) -> Event { @@ -226,13 +225,13 @@ impl<'d, T: Instance, V: VbusDetect> driver::Bus for Bus<'d, T, V> { BUS_WAKER.register(cx.waker()); let regs = T::regs(); - if regs.events_usbreset.read().bits() != 0 { - regs.events_usbreset.reset(); - regs.intenset.write(|w| w.usbreset().set()); + if regs.events_usbreset().read() != 0 { + regs.events_usbreset().write_value(0); + regs.intenset().write(|w| w.set_usbreset(true)); // Disable all endpoints except EP0 - regs.epinen.write(|w| unsafe { w.bits(0x01) }); - regs.epouten.write(|w| unsafe { w.bits(0x01) }); + regs.epinen().write(|w| w.0 = 0x01); + regs.epouten().write(|w| w.0 = 0x01); READY_ENDPOINTS.store(In::mask(0), Ordering::Release); for i in 1..=7 { In::waker(i).wake(); @@ -242,27 +241,27 @@ impl<'d, T: Instance, V: VbusDetect> driver::Bus for Bus<'d, T, V> { return Poll::Ready(Event::Reset); } - let r = regs.eventcause.read(); + let r = regs.eventcause().read(); - if r.isooutcrc().bit() { - regs.eventcause.write(|w| w.isooutcrc().detected()); + if r.isooutcrc() { + regs.eventcause().write(|w| w.set_isooutcrc(true)); trace!("USB event: isooutcrc"); } - if r.usbwuallowed().bit() { - regs.eventcause.write(|w| w.usbwuallowed().allowed()); + if r.usbwuallowed() { + regs.eventcause().write(|w| w.set_usbwuallowed(true)); trace!("USB event: usbwuallowed"); } - if r.suspend().bit() { - regs.eventcause.write(|w| w.suspend().detected()); - regs.lowpower.write(|w| w.lowpower().low_power()); + if r.suspend() { + regs.eventcause().write(|w| w.set_suspend(true)); + regs.lowpower().write(|w| w.set_lowpower(vals::Lowpower::LOW_POWER)); return Poll::Ready(Event::Suspend); } - if r.resume().bit() { - regs.eventcause.write(|w| w.resume().detected()); + if r.resume() { + regs.eventcause().write(|w| w.set_resume(true)); return Poll::Ready(Event::Resume); } - if r.ready().bit() { - regs.eventcause.write(|w| w.ready().ready()); + if r.ready() { + regs.eventcause().write(|w| w.set_ready(true)); trace!("USB event: ready"); } @@ -284,16 +283,19 @@ impl<'d, T: Instance, V: VbusDetect> driver::Bus for Bus<'d, T, V> { fn endpoint_set_stalled(&mut self, ep_addr: EndpointAddress, stalled: bool) { let regs = T::regs(); - unsafe { - if ep_addr.index() == 0 { - regs.tasks_ep0stall.write(|w| w.tasks_ep0stall().bit(stalled)); - } else { - regs.epstall.write(|w| { - w.ep().bits(ep_addr.index() as u8 & 0b111); - w.io().bit(ep_addr.is_in()); - w.stall().bit(stalled) - }); + if ep_addr.index() == 0 { + if stalled { + regs.tasks_ep0stall().write_value(1); } + } else { + regs.epstall().write(|w| { + w.set_ep(ep_addr.index() as u8 & 0b111); + w.set_io(match ep_addr.direction() { + Direction::In => vals::Io::IN, + Direction::Out => vals::Io::OUT, + }); + w.set_stall(stalled); + }); } } @@ -301,8 +303,8 @@ impl<'d, T: Instance, V: VbusDetect> driver::Bus for Bus<'d, T, V> { let regs = T::regs(); let i = ep_addr.index(); match ep_addr.direction() { - Direction::Out => regs.halted.epout[i].read().getstatus().is_halted(), - Direction::In => regs.halted.epin[i].read().getstatus().is_halted(), + Direction::Out => regs.halted().epout(i).read().getstatus() == vals::Getstatus::HALTED, + Direction::In => regs.halted().epin(i).read().getstatus() == vals::Getstatus::HALTED, } } @@ -317,15 +319,13 @@ impl<'d, T: Instance, V: VbusDetect> driver::Bus for Bus<'d, T, V> { match ep_addr.direction() { Direction::In => { let mut was_enabled = false; - regs.epinen.modify(|r, w| { - let mut bits = r.bits(); - was_enabled = (bits & mask) != 0; + regs.epinen().modify(|w| { + was_enabled = (w.0 & mask) != 0; if enabled { - bits |= mask + w.0 |= mask } else { - bits &= !mask + w.0 &= !mask } - unsafe { w.bits(bits) } }); let ready_mask = In::mask(i); @@ -340,15 +340,8 @@ impl<'d, T: Instance, V: VbusDetect> driver::Bus for Bus<'d, T, V> { In::waker(i).wake(); } Direction::Out => { - regs.epouten.modify(|r, w| { - let mut bits = r.bits(); - if enabled { - bits |= mask - } else { - bits &= !mask - } - unsafe { w.bits(bits) } - }); + regs.epouten() + .modify(|w| if enabled { w.0 |= mask } else { w.0 &= !mask }); let ready_mask = Out::mask(i); if enabled { @@ -356,7 +349,7 @@ impl<'d, T: Instance, V: VbusDetect> driver::Bus for Bus<'d, T, V> { // peripheral will NAK all incoming packets) until we write a zero to the SIZE // register (see figure 203 of the 52840 manual). To avoid that we write a 0 to the // SIZE register - regs.size.epout[i].reset(); + regs.size().epout(i).write(|_| ()); } else { READY_ENDPOINTS.fetch_and(!ready_mask, Ordering::AcqRel); } @@ -370,25 +363,24 @@ impl<'d, T: Instance, V: VbusDetect> driver::Bus for Bus<'d, T, V> { async fn remote_wakeup(&mut self) -> Result<(), Unsupported> { let regs = T::regs(); - if regs.lowpower.read().lowpower().is_low_power() { + if regs.lowpower().read().lowpower() == vals::Lowpower::LOW_POWER { errata::pre_wakeup(); - regs.lowpower.write(|w| w.lowpower().force_normal()); + regs.lowpower().write(|w| w.set_lowpower(vals::Lowpower::FORCE_NORMAL)); poll_fn(|cx| { BUS_WAKER.register(cx.waker()); let regs = T::regs(); - let r = regs.eventcause.read(); + let r = regs.eventcause().read(); - if regs.events_usbreset.read().bits() != 0 { + if regs.events_usbreset().read() != 0 { Poll::Ready(()) - } else if r.resume().bit() { + } else if r.resume() { Poll::Ready(()) - } else if r.usbwuallowed().bit() { - regs.eventcause.write(|w| w.usbwuallowed().allowed()); - - regs.dpdmvalue.write(|w| w.state().resume()); - regs.tasks_dpdmdrive.write(|w| w.tasks_dpdmdrive().set_bit()); + } else if r.usbwuallowed() { + regs.eventcause().write(|w| w.set_usbwuallowed(true)); + regs.dpdmvalue().write(|w| w.set_state(vals::State::RESUME)); + regs.tasks_dpdmdrive().write_value(1); Poll::Ready(()) } else { @@ -413,7 +405,7 @@ pub enum In {} trait EndpointDir { fn waker(i: usize) -> &'static AtomicWaker; fn mask(i: usize) -> u32; - fn is_enabled(regs: &RegisterBlock, i: usize) -> bool; + fn is_enabled(regs: pac::usbd::Usbd, i: usize) -> bool; } impl EndpointDir for In { @@ -428,8 +420,8 @@ impl EndpointDir for In { } #[inline] - fn is_enabled(regs: &RegisterBlock, i: usize) -> bool { - (regs.epinen.read().bits() & (1 << i)) != 0 + fn is_enabled(regs: pac::usbd::Usbd, i: usize) -> bool { + regs.epinen().read().in_(i) } } @@ -445,8 +437,8 @@ impl EndpointDir for Out { } #[inline] - fn is_enabled(regs: &RegisterBlock, i: usize) -> bool { - (regs.epouten.read().bits() & (1 << i)) != 0 + fn is_enabled(regs: pac::usbd::Usbd, i: usize) -> bool { + regs.epouten().read().out(i) } } @@ -529,33 +521,23 @@ unsafe fn read_dma(i: usize, buf: &mut [u8]) -> Result buf.len() { return Err(EndpointError::BufferOverflow); } - let epout = [ - ®s.epout0, - ®s.epout1, - ®s.epout2, - ®s.epout3, - ®s.epout4, - ®s.epout5, - ®s.epout6, - ®s.epout7, - ]; - epout[i].ptr.write(|w| w.bits(buf.as_ptr() as u32)); + regs.epout(i).ptr().write_value(buf.as_ptr() as u32); // MAXCNT must match SIZE - epout[i].maxcnt.write(|w| w.bits(size as u32)); + regs.epout(i).maxcnt().write(|w| w.set_maxcnt(size as _)); dma_start(); - regs.events_endepout[i].reset(); - regs.tasks_startepout[i].write(|w| w.tasks_startepout().set_bit()); - while regs.events_endepout[i].read().events_endepout().bit_is_clear() {} - regs.events_endepout[i].reset(); + regs.events_endepout(i).write_value(0); + regs.tasks_startepout(i).write_value(1); + while regs.events_endepout(i).read() == 0 {} + regs.events_endepout(i).write_value(0); dma_end(); - regs.size.epout[i].reset(); + regs.size().epout(i).write(|_| ()); Ok(size) } @@ -574,27 +556,16 @@ unsafe fn write_dma(i: usize, buf: &[u8]) { buf.as_ptr() }; - let epin = [ - ®s.epin0, - ®s.epin1, - ®s.epin2, - ®s.epin3, - ®s.epin4, - ®s.epin5, - ®s.epin6, - ®s.epin7, - ]; - // Set the buffer length so the right number of bytes are transmitted. // Safety: `buf.len()` has been checked to be <= the max buffer length. - epin[i].ptr.write(|w| w.bits(ptr as u32)); - epin[i].maxcnt.write(|w| w.maxcnt().bits(buf.len() as u8)); + regs.epin(i).ptr().write_value(ptr as u32); + regs.epin(i).maxcnt().write(|w| w.set_maxcnt(buf.len() as u8)); - regs.events_endepin[i].reset(); + regs.events_endepin(i).write_value(0); dma_start(); - regs.tasks_startepin[i].write(|w| w.bits(1)); - while regs.events_endepin[i].read().bits() == 0 {} + regs.tasks_startepin(i).write_value(1); + while regs.events_endepin(i).read() == 0 {} dma_end(); } @@ -637,14 +608,14 @@ impl<'d, T: Instance> driver::ControlPipe for ControlPipe<'d, T> { let regs = T::regs(); // Reset shorts - regs.shorts.write(|w| w); + regs.shorts().write(|_| ()); // Wait for SETUP packet - regs.intenset.write(|w| w.ep0setup().set()); + regs.intenset().write(|w| w.set_ep0setup(true)); poll_fn(|cx| { EP0_WAKER.register(cx.waker()); let regs = T::regs(); - if regs.events_ep0setup.read().bits() != 0 { + if regs.events_ep0setup().read() != 0 { Poll::Ready(()) } else { Poll::Pending @@ -652,17 +623,17 @@ impl<'d, T: Instance> driver::ControlPipe for ControlPipe<'d, T> { }) .await; - regs.events_ep0setup.reset(); + regs.events_ep0setup().write_value(0); let mut buf = [0; 8]; - buf[0] = regs.bmrequesttype.read().bits() as u8; - buf[1] = regs.brequest.read().brequest().bits(); - buf[2] = regs.wvaluel.read().wvaluel().bits(); - buf[3] = regs.wvalueh.read().wvalueh().bits(); - buf[4] = regs.windexl.read().windexl().bits(); - buf[5] = regs.windexh.read().windexh().bits(); - buf[6] = regs.wlengthl.read().wlengthl().bits(); - buf[7] = regs.wlengthh.read().wlengthh().bits(); + buf[0] = regs.bmrequesttype().read().0 as u8; + buf[1] = regs.brequest().read().0 as u8; + buf[2] = regs.wvaluel().read().0 as u8; + buf[3] = regs.wvalueh().read().0 as u8; + buf[4] = regs.windexl().read().0 as u8; + buf[5] = regs.windexh().read().0 as u8; + buf[6] = regs.wlengthl().read().0 as u8; + buf[7] = regs.wlengthh().read().0 as u8; buf } @@ -670,26 +641,26 @@ impl<'d, T: Instance> driver::ControlPipe for ControlPipe<'d, T> { async fn data_out(&mut self, buf: &mut [u8], _first: bool, _last: bool) -> Result { let regs = T::regs(); - regs.events_ep0datadone.reset(); + regs.events_ep0datadone().write_value(0); // This starts a RX on EP0. events_ep0datadone notifies when done. - regs.tasks_ep0rcvout.write(|w| w.tasks_ep0rcvout().set_bit()); + regs.tasks_ep0rcvout().write_value(1); // Wait until ready - regs.intenset.write(|w| { - w.usbreset().set(); - w.ep0setup().set(); - w.ep0datadone().set() + regs.intenset().write(|w| { + w.set_usbreset(true); + w.set_ep0setup(true); + w.set_ep0datadone(true); }); poll_fn(|cx| { EP0_WAKER.register(cx.waker()); let regs = T::regs(); - if regs.events_ep0datadone.read().bits() != 0 { + if regs.events_ep0datadone().read() != 0 { Poll::Ready(Ok(())) - } else if regs.events_usbreset.read().bits() != 0 { + } else if regs.events_usbreset().read() != 0 { trace!("aborted control data_out: usb reset"); Poll::Ready(Err(EndpointError::Disabled)) - } else if regs.events_ep0setup.read().bits() != 0 { + } else if regs.events_ep0setup().read() != 0 { trace!("aborted control data_out: received another SETUP"); Poll::Ready(Err(EndpointError::Disabled)) } else { @@ -703,29 +674,29 @@ impl<'d, T: Instance> driver::ControlPipe for ControlPipe<'d, T> { async fn data_in(&mut self, buf: &[u8], _first: bool, last: bool) -> Result<(), EndpointError> { let regs = T::regs(); - regs.events_ep0datadone.reset(); + regs.events_ep0datadone().write_value(0); - regs.shorts.write(|w| w.ep0datadone_ep0status().bit(last)); + regs.shorts().write(|w| w.set_ep0datadone_ep0status(last)); // This starts a TX on EP0. events_ep0datadone notifies when done. unsafe { write_dma::(0, buf) } - regs.intenset.write(|w| { - w.usbreset().set(); - w.ep0setup().set(); - w.ep0datadone().set() + regs.intenset().write(|w| { + w.set_usbreset(true); + w.set_ep0setup(true); + w.set_ep0datadone(true); }); poll_fn(|cx| { cx.waker().wake_by_ref(); EP0_WAKER.register(cx.waker()); let regs = T::regs(); - if regs.events_ep0datadone.read().bits() != 0 { + if regs.events_ep0datadone().read() != 0 { Poll::Ready(Ok(())) - } else if regs.events_usbreset.read().bits() != 0 { + } else if regs.events_usbreset().read() != 0 { trace!("aborted control data_in: usb reset"); Poll::Ready(Err(EndpointError::Disabled)) - } else if regs.events_ep0setup.read().bits() != 0 { + } else if regs.events_ep0setup().read() != 0 { trace!("aborted control data_in: received another SETUP"); Poll::Ready(Err(EndpointError::Disabled)) } else { @@ -737,12 +708,12 @@ impl<'d, T: Instance> driver::ControlPipe for ControlPipe<'d, T> { async fn accept(&mut self) { let regs = T::regs(); - regs.tasks_ep0status.write(|w| w.tasks_ep0status().bit(true)); + regs.tasks_ep0status().write_value(1); } async fn reject(&mut self) { let regs = T::regs(); - regs.tasks_ep0stall.write(|w| w.tasks_ep0stall().bit(true)); + regs.tasks_ep0stall().write_value(1); } async fn accept_set_address(&mut self, _addr: u8) { @@ -806,7 +777,7 @@ impl Allocator { } pub(crate) trait SealedInstance { - fn regs() -> &'static pac::usbd::RegisterBlock; + fn regs() -> pac::usbd::Usbd; } /// USB peripheral instance. @@ -819,8 +790,8 @@ pub trait Instance: Peripheral

+ SealedInstance + 'static + Send { macro_rules! impl_usb { ($type:ident, $pac_type:ident, $irq:ident) => { impl crate::usb::SealedInstance for peripherals::$type { - fn regs() -> &'static pac::usbd::RegisterBlock { - unsafe { &*pac::$pac_type::ptr() } + fn regs() -> pac::usbd::Usbd { + pac::$pac_type } } impl crate::usb::Instance for peripherals::$type { diff --git a/embassy-nrf/src/usb/vbus_detect.rs b/embassy-nrf/src/usb/vbus_detect.rs index a05e5aa52..7f816a5ad 100644 --- a/embassy-nrf/src/usb/vbus_detect.rs +++ b/embassy-nrf/src/usb/vbus_detect.rs @@ -34,9 +34,9 @@ type UsbRegIrq = interrupt::typelevel::POWER_CLOCK; type UsbRegIrq = interrupt::typelevel::USBREGULATOR; #[cfg(not(feature = "_nrf5340"))] -type UsbRegPeri = pac::POWER; +const USB_REG_PERI: pac::power::Power = pac::POWER; #[cfg(feature = "_nrf5340")] -type UsbRegPeri = pac::USBREGULATOR; +const USB_REG_PERI: pac::usbregulator::Usbregulator = pac::USBREGULATOR; /// Interrupt handler. pub struct InterruptHandler { @@ -45,21 +45,21 @@ pub struct InterruptHandler { impl interrupt::typelevel::Handler for InterruptHandler { unsafe fn on_interrupt() { - let regs = unsafe { &*UsbRegPeri::ptr() }; + let regs = USB_REG_PERI; - if regs.events_usbdetected.read().bits() != 0 { - regs.events_usbdetected.reset(); + if regs.events_usbdetected().read() != 0 { + regs.events_usbdetected().write_value(0); BUS_WAKER.wake(); } - if regs.events_usbremoved.read().bits() != 0 { - regs.events_usbremoved.reset(); + if regs.events_usbremoved().read() != 0 { + regs.events_usbremoved().write_value(0); BUS_WAKER.wake(); POWER_WAKER.wake(); } - if regs.events_usbpwrrdy.read().bits() != 0 { - regs.events_usbpwrrdy.reset(); + if regs.events_usbpwrrdy().read() != 0 { + regs.events_usbpwrrdy().write_value(0); POWER_WAKER.wake(); } } @@ -78,13 +78,16 @@ static POWER_WAKER: AtomicWaker = AtomicWaker::new(); impl HardwareVbusDetect { /// Create a new `VbusDetectNative`. pub fn new(_irq: impl interrupt::typelevel::Binding + 'static) -> Self { - let regs = unsafe { &*UsbRegPeri::ptr() }; + let regs = USB_REG_PERI; UsbRegIrq::unpend(); unsafe { UsbRegIrq::enable() }; - regs.intenset - .write(|w| w.usbdetected().set().usbremoved().set().usbpwrrdy().set()); + regs.intenset().write(|w| { + w.set_usbdetected(true); + w.set_usbremoved(true); + w.set_usbpwrrdy(true); + }); Self { _private: () } } @@ -92,16 +95,16 @@ impl HardwareVbusDetect { impl VbusDetect for HardwareVbusDetect { fn is_usb_detected(&self) -> bool { - let regs = unsafe { &*UsbRegPeri::ptr() }; - regs.usbregstatus.read().vbusdetect().is_vbus_present() + let regs = USB_REG_PERI; + regs.usbregstatus().read().vbusdetect() } async fn wait_power_ready(&mut self) -> Result<(), ()> { poll_fn(move |cx| { POWER_WAKER.register(cx.waker()); - let regs = unsafe { &*UsbRegPeri::ptr() }; + let regs = USB_REG_PERI; - if regs.usbregstatus.read().outputrdy().is_ready() { + if regs.usbregstatus().read().outputrdy() { Poll::Ready(Ok(())) } else if !self.is_usb_detected() { Poll::Ready(Err(())) diff --git a/embassy-nrf/src/wdt.rs b/embassy-nrf/src/wdt.rs index e4cfa3344..dfe6cbec3 100644 --- a/embassy-nrf/src/wdt.rs +++ b/embassy-nrf/src/wdt.rs @@ -3,7 +3,8 @@ //! This HAL implements a basic watchdog timer with 1..=8 handles. //! Once the watchdog has been started, it cannot be stopped. -use crate::pac::WDT; +use crate::pac::wdt::vals; +pub use crate::pac::wdt::vals::{Halt as HaltConfig, Sleep as SleepConfig}; use crate::peripherals; const MIN_TICKS: u32 = 15; @@ -18,29 +19,29 @@ pub struct Config { pub timeout_ticks: u32, /// Should the watchdog continue to count during sleep modes? - pub run_during_sleep: bool, + pub action_during_sleep: SleepConfig, /// Should the watchdog continue to count when the CPU is halted for debug? - pub run_during_debug_halt: bool, + pub action_during_debug_halt: HaltConfig, } impl Config { /// Create a config structure from the current configuration of the WDT /// peripheral. pub fn try_new(_wdt: &peripherals::WDT) -> Option { - let r = unsafe { &*WDT::ptr() }; + let r = crate::pac::WDT; #[cfg(not(feature = "_nrf91"))] - let runstatus = r.runstatus.read().runstatus().bit(); + let runstatus = r.runstatus().read().runstatus(); #[cfg(feature = "_nrf91")] - let runstatus = r.runstatus.read().runstatuswdt().bit(); + let runstatus = r.runstatus().read().runstatuswdt(); if runstatus { - let config = r.config.read(); + let config = r.config().read(); Some(Self { - timeout_ticks: r.crv.read().bits(), - run_during_sleep: config.sleep().bit(), - run_during_debug_halt: config.halt().bit(), + timeout_ticks: r.crv().read(), + action_during_sleep: config.sleep(), + action_during_debug_halt: config.halt(), }) } else { None @@ -52,8 +53,8 @@ impl Default for Config { fn default() -> Self { Self { timeout_ticks: 32768, // 1 second - run_during_debug_halt: true, - run_during_sleep: true, + action_during_debug_halt: HaltConfig::RUN, + action_during_sleep: SleepConfig::RUN, } } } @@ -78,36 +79,35 @@ impl Watchdog { ) -> Result<(Self, [WatchdogHandle; N]), peripherals::WDT> { assert!(N >= 1 && N <= 8); - let r = unsafe { &*WDT::ptr() }; + let r = crate::pac::WDT; let crv = config.timeout_ticks.max(MIN_TICKS); - let rren = (1u32 << N) - 1; + let rren = crate::pac::wdt::regs::Rren((1u32 << N) - 1); #[cfg(not(feature = "_nrf91"))] - let runstatus = r.runstatus.read().runstatus().bit(); + let runstatus = r.runstatus().read().runstatus(); #[cfg(feature = "_nrf91")] - let runstatus = r.runstatus.read().runstatuswdt().bit(); + let runstatus = r.runstatus().read().runstatuswdt(); if runstatus { - let curr_config = r.config.read(); - if curr_config.halt().bit() != config.run_during_debug_halt - || curr_config.sleep().bit() != config.run_during_sleep - || r.crv.read().bits() != crv - || r.rren.read().bits() != rren + let curr_config = r.config().read(); + if curr_config.halt() != config.action_during_debug_halt + || curr_config.sleep() != config.action_during_sleep + || r.crv().read() != crv + || r.rren().read() != rren { return Err(wdt); } } else { - r.config.write(|w| { - w.sleep().bit(config.run_during_sleep); - w.halt().bit(config.run_during_debug_halt); - w + r.config().write(|w| { + w.set_sleep(config.action_during_sleep); + w.set_halt(config.action_during_debug_halt); }); - r.intenset.write(|w| w.timeout().set_bit()); + r.intenset().write(|w| w.set_timeout(true)); - r.crv.write(|w| unsafe { w.bits(crv) }); - r.rren.write(|w| unsafe { w.bits(rren) }); - r.tasks_start.write(|w| unsafe { w.bits(1) }); + r.crv().write_value(crv); + r.rren().write_value(rren); + r.tasks_start().write_value(1); } let this = Self { _private: () }; @@ -130,8 +130,7 @@ impl Watchdog { /// interrupt has been enabled. #[inline(always)] pub fn enable_interrupt(&mut self) { - let r = unsafe { &*WDT::ptr() }; - r.intenset.write(|w| w.timeout().set_bit()); + crate::pac::WDT.intenset().write(|w| w.set_timeout(true)); } /// Disable the watchdog interrupt. @@ -139,8 +138,7 @@ impl Watchdog { /// NOTE: This has no effect on the reset caused by the Watchdog. #[inline(always)] pub fn disable_interrupt(&mut self) { - let r = unsafe { &*WDT::ptr() }; - r.intenclr.write(|w| w.timeout().set_bit()); + crate::pac::WDT.intenclr().write(|w| w.set_timeout(true)); } /// Is the watchdog still awaiting pets from any handle? @@ -149,9 +147,9 @@ impl Watchdog { /// handles to prevent a reset this time period. #[inline(always)] pub fn awaiting_pets(&self) -> bool { - let r = unsafe { &*WDT::ptr() }; - let enabled = r.rren.read().bits(); - let status = r.reqstatus.read().bits(); + let r = crate::pac::WDT; + let enabled = r.rren().read().0; + let status = r.reqstatus().read().0; (status & enabled) == 0 } } @@ -170,16 +168,14 @@ impl WatchdogHandle { /// prevent a reset from occurring. #[inline] pub fn pet(&mut self) { - let r = unsafe { &*WDT::ptr() }; - r.rr[self.index as usize].write(|w| w.rr().reload()); + let r = crate::pac::WDT; + r.rr(self.index as usize).write(|w| w.set_rr(vals::Rr::RELOAD)); } /// Has this handle been pet within the current window? pub fn is_pet(&self) -> bool { - let r = unsafe { &*WDT::ptr() }; - let rd = r.reqstatus.read().bits(); - let idx = self.index as usize; - ((rd >> idx) & 0x1) == 0 + let r = crate::pac::WDT; + !r.reqstatus().read().rr(self.index as usize) } /// Steal a watchdog handle by index. diff --git a/examples/boot/bootloader/nrf/src/main.rs b/examples/boot/bootloader/nrf/src/main.rs index 67c700437..b849a0df3 100644 --- a/examples/boot/bootloader/nrf/src/main.rs +++ b/examples/boot/bootloader/nrf/src/main.rs @@ -8,7 +8,7 @@ use cortex_m_rt::{entry, exception}; use defmt_rtt as _; use embassy_boot_nrf::*; use embassy_nrf::nvmc::Nvmc; -use embassy_nrf::wdt; +use embassy_nrf::wdt::{self, HaltConfig, SleepConfig}; use embassy_sync::blocking_mutex::Mutex; #[entry] @@ -25,8 +25,8 @@ fn main() -> ! { let mut wdt_config = wdt::Config::default(); wdt_config.timeout_ticks = 32768 * 5; // timeout seconds - wdt_config.run_during_sleep = true; - wdt_config.run_during_debug_halt = false; + wdt_config.action_during_sleep = SleepConfig::RUN; + wdt_config.action_during_debug_halt = HaltConfig::PAUSE; let flash = WatchdogFlash::start(Nvmc::new(p.NVMC), p.WDT, wdt_config); let flash = Mutex::new(RefCell::new(flash)); diff --git a/examples/nrf52840/src/bin/usb_ethernet.rs b/examples/nrf52840/src/bin/usb_ethernet.rs index b07adac1f..82364ded8 100644 --- a/examples/nrf52840/src/bin/usb_ethernet.rs +++ b/examples/nrf52840/src/bin/usb_ethernet.rs @@ -1,8 +1,6 @@ #![no_std] #![no_main] -use core::mem; - use defmt::*; use embassy_executor::Spawner; use embassy_net::tcp::TcpSocket; @@ -46,11 +44,10 @@ async fn net_task(mut runner: embassy_net::Runner<'static, Device<'static, MTU>> #[embassy_executor::main] async fn main(spawner: Spawner) { let p = embassy_nrf::init(Default::default()); - let clock: pac::CLOCK = unsafe { mem::transmute(()) }; info!("Enabling ext hfosc..."); - clock.tasks_hfclkstart.write(|w| unsafe { w.bits(1) }); - while clock.events_hfclkstarted.read().bits() != 1 {} + pac::CLOCK.tasks_hfclkstart().write_value(1); + while pac::CLOCK.events_hfclkstarted().read() != 1 {} // Create the driver, from the HAL. let driver = Driver::new(p.USBD, Irqs, HardwareVbusDetect::new(Irqs)); diff --git a/examples/nrf52840/src/bin/usb_hid_keyboard.rs b/examples/nrf52840/src/bin/usb_hid_keyboard.rs index e33ee5866..3b752fd16 100644 --- a/examples/nrf52840/src/bin/usb_hid_keyboard.rs +++ b/examples/nrf52840/src/bin/usb_hid_keyboard.rs @@ -1,7 +1,6 @@ #![no_std] #![no_main] -use core::mem; use core::sync::atomic::{AtomicBool, Ordering}; use defmt::*; @@ -30,11 +29,10 @@ static SUSPENDED: AtomicBool = AtomicBool::new(false); #[embassy_executor::main] async fn main(_spawner: Spawner) { let p = embassy_nrf::init(Default::default()); - let clock: pac::CLOCK = unsafe { mem::transmute(()) }; info!("Enabling ext hfosc..."); - clock.tasks_hfclkstart.write(|w| unsafe { w.bits(1) }); - while clock.events_hfclkstarted.read().bits() != 1 {} + pac::CLOCK.tasks_hfclkstart().write_value(1); + while pac::CLOCK.events_hfclkstarted().read() != 1 {} // Create the driver, from the HAL. let driver = Driver::new(p.USBD, Irqs, HardwareVbusDetect::new(Irqs)); diff --git a/examples/nrf52840/src/bin/usb_hid_mouse.rs b/examples/nrf52840/src/bin/usb_hid_mouse.rs index 8076ac283..3f13a014e 100644 --- a/examples/nrf52840/src/bin/usb_hid_mouse.rs +++ b/examples/nrf52840/src/bin/usb_hid_mouse.rs @@ -1,8 +1,6 @@ #![no_std] #![no_main] -use core::mem; - use defmt::*; use embassy_executor::Spawner; use embassy_futures::join::join; @@ -24,11 +22,10 @@ bind_interrupts!(struct Irqs { #[embassy_executor::main] async fn main(_spawner: Spawner) { let p = embassy_nrf::init(Default::default()); - let clock: pac::CLOCK = unsafe { mem::transmute(()) }; info!("Enabling ext hfosc..."); - clock.tasks_hfclkstart.write(|w| unsafe { w.bits(1) }); - while clock.events_hfclkstarted.read().bits() != 1 {} + pac::CLOCK.tasks_hfclkstart().write_value(1); + while pac::CLOCK.events_hfclkstarted().read() != 1 {} // Create the driver, from the HAL. let driver = Driver::new(p.USBD, Irqs, HardwareVbusDetect::new(Irqs)); diff --git a/examples/nrf52840/src/bin/usb_serial.rs b/examples/nrf52840/src/bin/usb_serial.rs index 02048e692..30fe103ad 100644 --- a/examples/nrf52840/src/bin/usb_serial.rs +++ b/examples/nrf52840/src/bin/usb_serial.rs @@ -1,8 +1,6 @@ #![no_std] #![no_main] -use core::mem; - use defmt::{info, panic}; use embassy_executor::Spawner; use embassy_futures::join::join; @@ -22,11 +20,10 @@ bind_interrupts!(struct Irqs { #[embassy_executor::main] async fn main(_spawner: Spawner) { let p = embassy_nrf::init(Default::default()); - let clock: pac::CLOCK = unsafe { mem::transmute(()) }; info!("Enabling ext hfosc..."); - clock.tasks_hfclkstart.write(|w| unsafe { w.bits(1) }); - while clock.events_hfclkstarted.read().bits() != 1 {} + pac::CLOCK.tasks_hfclkstart().write_value(1); + while pac::CLOCK.events_hfclkstarted().read() != 1 {} // Create the driver, from the HAL. let driver = Driver::new(p.USBD, Irqs, HardwareVbusDetect::new(Irqs)); diff --git a/examples/nrf52840/src/bin/usb_serial_multitask.rs b/examples/nrf52840/src/bin/usb_serial_multitask.rs index 895cca8b9..05b5f0ec9 100644 --- a/examples/nrf52840/src/bin/usb_serial_multitask.rs +++ b/examples/nrf52840/src/bin/usb_serial_multitask.rs @@ -1,8 +1,6 @@ #![no_std] #![no_main] -use core::mem; - use defmt::{info, panic, unwrap}; use embassy_executor::Spawner; use embassy_nrf::usb::vbus_detect::HardwareVbusDetect; @@ -39,11 +37,10 @@ async fn echo_task(mut class: CdcAcmClass<'static, MyDriver>) { #[embassy_executor::main] async fn main(spawner: Spawner) { let p = embassy_nrf::init(Default::default()); - let clock: pac::CLOCK = unsafe { mem::transmute(()) }; info!("Enabling ext hfosc..."); - clock.tasks_hfclkstart.write(|w| unsafe { w.bits(1) }); - while clock.events_hfclkstarted.read().bits() != 1 {} + pac::CLOCK.tasks_hfclkstart().write_value(1); + while pac::CLOCK.events_hfclkstarted().read() != 1 {} // Create the driver, from the HAL. let driver = Driver::new(p.USBD, Irqs, HardwareVbusDetect::new(Irqs)); diff --git a/examples/nrf52840/src/bin/usb_serial_winusb.rs b/examples/nrf52840/src/bin/usb_serial_winusb.rs index c6675a3d3..7c07158e0 100644 --- a/examples/nrf52840/src/bin/usb_serial_winusb.rs +++ b/examples/nrf52840/src/bin/usb_serial_winusb.rs @@ -1,8 +1,6 @@ #![no_std] #![no_main] -use core::mem; - use defmt::{info, panic}; use embassy_executor::Spawner; use embassy_futures::join::join; @@ -27,11 +25,10 @@ const DEVICE_INTERFACE_GUIDS: &[&str] = &["{EAA9A5DC-30BA-44BC-9232-606CDC875321 #[embassy_executor::main] async fn main(_spawner: Spawner) { let p = embassy_nrf::init(Default::default()); - let clock: pac::CLOCK = unsafe { mem::transmute(()) }; info!("Enabling ext hfosc..."); - clock.tasks_hfclkstart.write(|w| unsafe { w.bits(1) }); - while clock.events_hfclkstarted.read().bits() != 1 {} + pac::CLOCK.tasks_hfclkstart().write_value(1); + while pac::CLOCK.events_hfclkstarted().read() != 1 {} // Create the driver, from the HAL. let driver = Driver::new(p.USBD, Irqs, HardwareVbusDetect::new(Irqs)); diff --git a/examples/nrf52840/src/bin/wdt.rs b/examples/nrf52840/src/bin/wdt.rs index ede88cc26..0d9ee3cf8 100644 --- a/examples/nrf52840/src/bin/wdt.rs +++ b/examples/nrf52840/src/bin/wdt.rs @@ -4,7 +4,7 @@ use defmt::*; use embassy_executor::Spawner; use embassy_nrf::gpio::{Input, Pull}; -use embassy_nrf::wdt::{Config, Watchdog}; +use embassy_nrf::wdt::{Config, HaltConfig, Watchdog}; use {defmt_rtt as _, panic_probe as _}; #[embassy_executor::main] @@ -17,7 +17,7 @@ async fn main(_spawner: Spawner) { // This is needed for `probe-rs run` to be able to catch the panic message // in the WDT interrupt. The core resets 2 ticks after firing the interrupt. - config.run_during_debug_halt = false; + config.action_during_debug_halt = HaltConfig::PAUSE; let (_wdt, [mut handle]) = match Watchdog::try_new(p.WDT, config) { Ok(x) => x, diff --git a/tests/nrf/Cargo.toml b/tests/nrf/Cargo.toml index d36ab9c67..6a710f29d 100644 --- a/tests/nrf/Cargo.toml +++ b/tests/nrf/Cargo.toml @@ -86,6 +86,11 @@ name = "gpiote" path = "src/bin/gpiote.rs" required-features = [] +[[bin]] +name = "spim" +path = "src/bin/spim.rs" +required-features = [ "easydma",] + [[bin]] name = "timer" path = "src/bin/timer.rs" diff --git a/tests/nrf/src/bin/buffered_uart_spam.rs b/tests/nrf/src/bin/buffered_uart_spam.rs index 45daaae0c..cf9ca50d2 100644 --- a/tests/nrf/src/bin/buffered_uart_spam.rs +++ b/tests/nrf/src/bin/buffered_uart_spam.rs @@ -50,15 +50,15 @@ async fn main(_spawner: Spawner) { const NSPAM: usize = 17; static mut TX_BUF: [u8; NSPAM] = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16]; let _spam = UarteTx::new(peri!(p, UART1), irqs!(UART1), peri!(p, PIN_A), config.clone()); - let spam_peri: pac::UARTE1 = unsafe { mem::transmute(()) }; - let event = unsafe { Event::new_unchecked(NonNull::new_unchecked(&spam_peri.events_endtx as *const _ as _)) }; - let task = unsafe { Task::new_unchecked(NonNull::new_unchecked(&spam_peri.tasks_starttx as *const _ as _)) }; + let spam_peri = pac::UARTE1; + let event = unsafe { Event::new_unchecked(NonNull::new_unchecked(spam_peri.events_endtx().as_ptr())) }; + let task = unsafe { Task::new_unchecked(NonNull::new_unchecked(spam_peri.tasks_starttx().as_ptr())) }; let mut spam_ppi = Ppi::new_one_to_one(p.PPI_CH2, event, task); spam_ppi.enable(); let p = (&raw mut TX_BUF) as *mut u8; - spam_peri.txd.ptr.write(|w| unsafe { w.ptr().bits(p as u32) }); - spam_peri.txd.maxcnt.write(|w| unsafe { w.maxcnt().bits(NSPAM as _) }); - spam_peri.tasks_starttx.write(|w| unsafe { w.bits(1) }); + spam_peri.txd().ptr().write_value(p as u32); + spam_peri.txd().maxcnt().write(|w| w.set_maxcnt(NSPAM as _)); + spam_peri.tasks_starttx().write_value(1); let mut i = 0; let mut total = 0; diff --git a/tests/nrf/src/bin/gpio.rs b/tests/nrf/src/bin/gpio.rs index 9e809a694..4995d244c 100644 --- a/tests/nrf/src/bin/gpio.rs +++ b/tests/nrf/src/bin/gpio.rs @@ -17,10 +17,12 @@ async fn main(_spawner: Spawner) { let mut output = Output::new(peri!(p, PIN_B), Level::Low, OutputDrive::Standard); output.set_low(); + assert!(output.is_set_low()); Timer::after_millis(10).await; assert!(input.is_low()); output.set_high(); + assert!(output.is_set_high()); Timer::after_millis(10).await; assert!(input.is_high()); diff --git a/tests/nrf/src/bin/spim.rs b/tests/nrf/src/bin/spim.rs new file mode 100644 index 000000000..c2ec90b88 --- /dev/null +++ b/tests/nrf/src/bin/spim.rs @@ -0,0 +1,42 @@ +// required-features: easydma +#![no_std] +#![no_main] + +#[path = "../common.rs"] +mod common; + +use defmt::{assert_eq, *}; +use embassy_executor::Spawner; +use embassy_nrf::spim::Spim; +use embassy_nrf::{peripherals, spim}; +use {defmt_rtt as _, panic_probe as _}; + +#[embassy_executor::main] +async fn main(_spawner: Spawner) { + let mut p = embassy_nrf::init(Default::default()); + let mut config = spim::Config::default(); + config.frequency = spim::Frequency::M1; + let mut spim = Spim::new( + &mut peri!(p, SPIM0), + irqs!(SPIM0), + &mut peri!(p, PIN_X), + &mut peri!(p, PIN_A), // MISO + &mut peri!(p, PIN_B), // MOSI + config.clone(), + ); + let data = [ + 0x42, 0x43, 0x44, 0x45, 0x66, 0x12, 0x23, 0x34, 0x45, 0x19, 0x91, 0xaa, 0xff, 0xa5, 0x5a, 0x77, + ]; + let mut buf = [0u8; 16]; + + buf.fill(0); + spim.blocking_transfer(&mut buf, &data).unwrap(); + assert_eq!(data, buf); + + buf.fill(0); + spim.transfer(&mut buf, &data).await.unwrap(); + assert_eq!(data, buf); + + info!("Test OK"); + cortex_m::asm::bkpt(); +} diff --git a/tests/nrf/src/common.rs b/tests/nrf/src/common.rs index ff5299b0f..c588dabf5 100644 --- a/tests/nrf/src/common.rs +++ b/tests/nrf/src/common.rs @@ -52,51 +52,66 @@ define_peris!(PIN_A = P0_13, PIN_B = P0_14,); #[cfg(feature = "nrf52832")] define_peris!( PIN_A = P0_11, PIN_B = P0_12, + PIN_X = P0_13, UART0 = UARTE0, + SPIM0 = TWISPI0, @irq UART0 = {UARTE0_UART0 => uarte::InterruptHandler;}, @irq UART0_BUFFERED = {UARTE0_UART0 => buffered_uarte::InterruptHandler;}, + @irq SPIM0 = {SPIM0_SPIS0_TWIM0_TWIS0_SPI0_TWI0 => spim::InterruptHandler;}, ); #[cfg(feature = "nrf52833")] define_peris!( PIN_A = P1_01, PIN_B = P1_02, + PIN_X = P1_03, UART0 = UARTE0, UART1 = UARTE1, + SPIM0 = TWISPI0, @irq UART0 = {UARTE0_UART0 => uarte::InterruptHandler;}, @irq UART1 = {UARTE1 => uarte::InterruptHandler;}, @irq UART0_BUFFERED = {UARTE0_UART0 => buffered_uarte::InterruptHandler;}, @irq UART1_BUFFERED = {UARTE1 => buffered_uarte::InterruptHandler;}, + @irq SPIM0 = {SPIM0_SPIS0_TWIM0_TWIS0_SPI0_TWI0 => spim::InterruptHandler;}, ); #[cfg(feature = "nrf52840")] define_peris!( PIN_A = P1_02, PIN_B = P1_03, + PIN_X = P1_04, UART0 = UARTE0, UART1 = UARTE1, + SPIM0 = TWISPI0, @irq UART0 = {UARTE0_UART0 => uarte::InterruptHandler;}, @irq UART1 = {UARTE1 => uarte::InterruptHandler;}, @irq UART0_BUFFERED = {UARTE0_UART0 => buffered_uarte::InterruptHandler;}, @irq UART1_BUFFERED = {UARTE1 => buffered_uarte::InterruptHandler;}, + @irq SPIM0 = {SPIM0_SPIS0_TWIM0_TWIS0_SPI0_TWI0 => spim::InterruptHandler;}, ); #[cfg(feature = "nrf5340")] define_peris!( PIN_A = P1_08, PIN_B = P1_09, + PIN_X = P1_10, UART0 = SERIAL0, UART1 = SERIAL1, + SPIM0 = SERIAL0, @irq UART0 = {SERIAL0 => uarte::InterruptHandler;}, @irq UART1 = {SERIAL1 => uarte::InterruptHandler;}, @irq UART0_BUFFERED = {SERIAL0 => buffered_uarte::InterruptHandler;}, @irq UART1_BUFFERED = {SERIAL1 => buffered_uarte::InterruptHandler;}, + @irq SPIM0 = {SERIAL0 => spim::InterruptHandler;}, ); #[cfg(feature = "nrf9160")] define_peris!( PIN_A = P0_00, PIN_B = P0_01, + PIN_X = P0_02, UART0 = SERIAL0, UART1 = SERIAL1, + SPIM0 = SERIAL0, @irq UART0 = {UARTE0_SPIM0_SPIS0_TWIM0_TWIS0 => uarte::InterruptHandler;}, @irq UART1 = {UARTE1_SPIM1_SPIS1_TWIM1_TWIS1 => uarte::InterruptHandler;}, @irq UART0_BUFFERED = {UARTE0_SPIM0_SPIS0_TWIM0_TWIS0 => buffered_uarte::InterruptHandler;}, @irq UART1_BUFFERED = {UARTE1_SPIM1_SPIS1_TWIM1_TWIS1 => buffered_uarte::InterruptHandler;}, + @irq SPIM0 = {UARTE0_SPIM0_SPIS0_TWIM0_TWIS0 => spim::InterruptHandler;}, ); From aa453caa79a0f612698e0bcb3bfff635a172eb5f Mon Sep 17 00:00:00 2001 From: Kenneth Knudsen Date: Mon, 4 Nov 2024 15:08:57 +0100 Subject: [PATCH 171/361] add split_ref for stm32 uart --- embassy-stm32/src/usart/buffered.rs | 7 +++++++ embassy-stm32/src/usart/mod.rs | 7 +++++++ 2 files changed, 14 insertions(+) diff --git a/embassy-stm32/src/usart/buffered.rs b/embassy-stm32/src/usart/buffered.rs index 7ba209063..d7ac33d07 100644 --- a/embassy-stm32/src/usart/buffered.rs +++ b/embassy-stm32/src/usart/buffered.rs @@ -396,6 +396,13 @@ impl<'d> BufferedUart<'d> { (self.tx, self.rx) } + /// Split the Uart into a transmitter and receiver by mutable reference, + /// which is particularly useful when having two tasks correlating to + /// transmitting and receiving. + pub fn split_ref(&mut self) -> (&mut BufferedUartTx<'d>, &mut BufferedUartRx<'d>) { + (&mut self.tx, &mut self.rx) + } + /// Reconfigure the driver pub fn set_config(&mut self, config: &Config) -> Result<(), ConfigError> { reconfigure(self.rx.info, self.rx.kernel_clock, config)?; diff --git a/embassy-stm32/src/usart/mod.rs b/embassy-stm32/src/usart/mod.rs index 333e01e36..8152fc560 100644 --- a/embassy-stm32/src/usart/mod.rs +++ b/embassy-stm32/src/usart/mod.rs @@ -1440,6 +1440,13 @@ impl<'d, M: Mode> Uart<'d, M> { (self.tx, self.rx) } + /// Split the Uart into a transmitter and receiver by mutable reference, + /// which is particularly useful when having two tasks correlating to + /// transmitting and receiving. + pub fn split_ref(&mut self) -> (&mut UartTx<'d, M>, &mut UartRx<'d, M>) { + (&mut self.tx, &mut self.rx) + } + /// Send break character pub fn send_break(&self) { self.tx.send_break(); From bffe7cf8a6b9c59475c0d8d2f2f8f9e88ad22087 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Mon, 4 Nov 2024 16:06:54 +0100 Subject: [PATCH 172/361] nrf: fix docs build. --- embassy-nrf/Cargo.toml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/embassy-nrf/Cargo.toml b/embassy-nrf/Cargo.toml index 90fa7a16c..24a3db91d 100644 --- a/embassy-nrf/Cargo.toml +++ b/embassy-nrf/Cargo.toml @@ -15,7 +15,7 @@ src_base_git = "https://github.com/embassy-rs/embassy/blob/$COMMIT/embassy-nrf/s features = ["time", "defmt", "unstable-pac", "gpiote", "time-driver-rtc1"] flavors = [ - { regex_feature = "_nrf51", target = "thumbv6m-none-eabi" }, + { regex_feature = "nrf51", target = "thumbv6m-none-eabi" }, { regex_feature = "nrf52.*", target = "thumbv7em-none-eabihf" }, { regex_feature = "nrf53.*", target = "thumbv8m.main-none-eabihf" }, { regex_feature = "nrf91.*", target = "thumbv8m.main-none-eabihf" }, @@ -150,4 +150,3 @@ embedded-storage = "0.3.1" embedded-storage-async = "0.4.1" cfg-if = "1.0.0" document-features = "0.2.7" - From 6dffb2213c295b57de13f4377a5f99878cb506e7 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Mon, 4 Nov 2024 17:46:26 +0100 Subject: [PATCH 173/361] Update Nightly. --- 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 acab9d615..d0c92e655 100644 --- a/rust-toolchain-nightly.toml +++ b/rust-toolchain-nightly.toml @@ -1,5 +1,5 @@ [toolchain] -channel = "nightly-2024-10-13" +channel = "nightly-2024-11-04" components = [ "rust-src", "rustfmt", "llvm-tools", "miri" ] targets = [ "thumbv7em-none-eabi", From fff304b3de85d63c1df404ab3abc17a2ac42844a Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Mon, 4 Nov 2024 18:41:31 +0100 Subject: [PATCH 174/361] nrf/pwm: fix bad pin assignment. --- embassy-nrf/src/pwm.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/embassy-nrf/src/pwm.rs b/embassy-nrf/src/pwm.rs index c8f1e0b09..ad88029b0 100644 --- a/embassy-nrf/src/pwm.rs +++ b/embassy-nrf/src/pwm.rs @@ -681,7 +681,7 @@ impl<'d, T: Instance> SimplePwm<'d, T> { pin.set_low(); pin.conf().write(|w| w.set_dir(gpiovals::Dir::OUTPUT)); } - r.psel().out(i).write_value(ch0.psel_bits()); + r.psel().out(i).write_value(ch.psel_bits()); } let pwm = Self { From f51ee98aef323b6d88e04ec4b3521a04d97485f9 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Mon, 4 Nov 2024 18:41:58 +0100 Subject: [PATCH 175/361] nrf/pwm: disconnect input. --- embassy-nrf/src/pwm.rs | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/embassy-nrf/src/pwm.rs b/embassy-nrf/src/pwm.rs index ad88029b0..7f1f568f4 100644 --- a/embassy-nrf/src/pwm.rs +++ b/embassy-nrf/src/pwm.rs @@ -132,6 +132,7 @@ impl<'d, T: Instance> SequencePwm<'d, T> { pin.set_low(); pin.conf().write(|w| { w.set_dir(gpiovals::Dir::OUTPUT); + w.set_input(gpiovals::Input::DISCONNECT); w.set_drive(convert_drive(config.ch0_drive)); }); } @@ -139,6 +140,7 @@ impl<'d, T: Instance> SequencePwm<'d, T> { pin.set_low(); pin.conf().write(|w| { w.set_dir(gpiovals::Dir::OUTPUT); + w.set_input(gpiovals::Input::DISCONNECT); w.set_drive(convert_drive(config.ch1_drive)); }); } @@ -146,6 +148,7 @@ impl<'d, T: Instance> SequencePwm<'d, T> { pin.set_low(); pin.conf().write(|w| { w.set_dir(gpiovals::Dir::OUTPUT); + w.set_input(gpiovals::Input::DISCONNECT); w.set_drive(convert_drive(config.ch2_drive)); }); } @@ -153,6 +156,7 @@ impl<'d, T: Instance> SequencePwm<'d, T> { pin.set_low(); pin.conf().write(|w| { w.set_dir(gpiovals::Dir::OUTPUT); + w.set_input(gpiovals::Input::DISCONNECT); w.set_drive(convert_drive(config.ch3_drive)); }); } @@ -679,7 +683,12 @@ impl<'d, T: Instance> SimplePwm<'d, T> { for (i, ch) in [&ch0, &ch1, &ch2, &ch3].into_iter().enumerate() { if let Some(pin) = ch { pin.set_low(); - pin.conf().write(|w| w.set_dir(gpiovals::Dir::OUTPUT)); + + pin.conf().write(|w| { + w.set_dir(gpiovals::Dir::OUTPUT); + w.set_input(gpiovals::Input::DISCONNECT); + w.set_drive(gpiovals::Drive::S0S1); + }); } r.psel().out(i).write_value(ch.psel_bits()); } From 03adeeddc2abd1c02a659f428f114848cf83e5ac Mon Sep 17 00:00:00 2001 From: Kaspar Schleiser Date: Mon, 4 Nov 2024 23:02:30 +0100 Subject: [PATCH 176/361] executor: allow overriding `embassy_executor` path in `task` macro --- embassy-executor-macros/src/macros/task.rs | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/embassy-executor-macros/src/macros/task.rs b/embassy-executor-macros/src/macros/task.rs index 2f2aeda76..e8134c6a9 100644 --- a/embassy-executor-macros/src/macros/task.rs +++ b/embassy-executor-macros/src/macros/task.rs @@ -1,3 +1,5 @@ +use std::str::FromStr; + use darling::export::NestedMeta; use darling::FromMeta; use proc_macro2::{Span, TokenStream}; @@ -11,6 +13,9 @@ use crate::util::*; struct Args { #[darling(default)] pool_size: Option, + /// Use this to override the `embassy_executor` crate path. Defaults to `::embassy_executor`. + #[darling(default)] + embassy_executor: Option, } pub fn run(args: TokenStream, item: TokenStream) -> TokenStream { @@ -42,6 +47,10 @@ pub fn run(args: TokenStream, item: TokenStream) -> TokenStream { lit: Lit::Int(LitInt::new("1", Span::call_site())), })); + let embassy_executor = args + .embassy_executor + .unwrap_or(Expr::Verbatim(TokenStream::from_str("::embassy_executor").unwrap())); + if f.sig.asyncness.is_none() { error(&mut errors, &f.sig, "task functions must be async"); } @@ -131,13 +140,13 @@ pub fn run(args: TokenStream, item: TokenStream) -> TokenStream { } const POOL_SIZE: usize = #pool_size; - static POOL: ::embassy_executor::raw::TaskPool<<() as _EmbassyInternalTaskTrait>::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 || <() as _EmbassyInternalTaskTrait>::construct(#(#full_args,)*)) } }; #[cfg(not(feature = "nightly"))] let mut task_outer_body = quote! { const POOL_SIZE: usize = #pool_size; - static POOL: ::embassy_executor::_export::TaskPoolRef = ::embassy_executor::_export::TaskPoolRef::new(); + static POOL: #embassy_executor::_export::TaskPoolRef = #embassy_executor::_export::TaskPoolRef::new(); unsafe { POOL.get::<_, POOL_SIZE>()._spawn_async_fn(move || #task_inner_ident(#(#full_args,)*)) } }; @@ -146,7 +155,7 @@ pub fn run(args: TokenStream, item: TokenStream) -> TokenStream { if !errors.is_empty() { task_outer_body = quote! { #![allow(unused_variables, unreachable_code)] - let _x: ::embassy_executor::SpawnToken<()> = ::core::todo!(); + let _x: #embassy_executor::SpawnToken<()> = ::core::todo!(); _x }; } @@ -164,7 +173,7 @@ pub fn run(args: TokenStream, item: TokenStream) -> TokenStream { #task_inner #(#task_outer_attrs)* - #visibility fn #task_ident #generics (#fargs) -> ::embassy_executor::SpawnToken #where_clause{ + #visibility fn #task_ident #generics (#fargs) -> #embassy_executor::SpawnToken #where_clause{ #task_outer_body } From fb004fb6e2bd1fe23c50bf0faccd9d4a8061e986 Mon Sep 17 00:00:00 2001 From: Georges Palauqui Date: Sun, 27 Oct 2024 10:23:18 +0100 Subject: [PATCH 177/361] add second example fo SPI display on RP --- examples/rp/Cargo.toml | 1 + examples/rp/src/bin/spi_gc9a01.rs | 126 ++++++++++++++++++++++++++++++ 2 files changed, 127 insertions(+) create mode 100644 examples/rp/src/bin/spi_gc9a01.rs diff --git a/examples/rp/Cargo.toml b/examples/rp/Cargo.toml index b55b20c63..901355aeb 100644 --- a/examples/rp/Cargo.toml +++ b/examples/rp/Cargo.toml @@ -45,6 +45,7 @@ byte-slice-cast = { version = "1.2.0", default-features = false } smart-leds = "0.4.0" heapless = "0.8" usbd-hid = "0.8.1" +rand_core = "0.6.4" embedded-hal-1 = { package = "embedded-hal", version = "1.0" } embedded-hal-async = "1.0" diff --git a/examples/rp/src/bin/spi_gc9a01.rs b/examples/rp/src/bin/spi_gc9a01.rs new file mode 100644 index 000000000..d1cf23e16 --- /dev/null +++ b/examples/rp/src/bin/spi_gc9a01.rs @@ -0,0 +1,126 @@ +//! This example shows how to use SPI (Serial Peripheral Interface) in the RP2040 chip. +//! +//! Example written for a display using the GC9A01 chip. Possibly the Waveshare RP2040-LCD-1.28 +//! (https://www.waveshare.com/wiki/RP2040-LCD-1.28) + +#![no_std] +#![no_main] + +use core::cell::RefCell; + +use defmt::*; +use display_interface_spi::SPIInterface; +use embassy_embedded_hal::shared_bus::blocking::spi::SpiDeviceWithConfig; +use embassy_executor::Spawner; +use embassy_rp::clocks::RoscRng; +use embassy_rp::gpio::{Level, Output}; +use embassy_rp::spi; +use embassy_rp::spi::{Blocking, Spi}; +use embassy_sync::blocking_mutex::raw::NoopRawMutex; +use embassy_sync::blocking_mutex::Mutex; +use embassy_time::{Delay, Duration, Timer}; +use embedded_graphics::image::{Image, ImageRawLE}; +use embedded_graphics::pixelcolor::Rgb565; +use embedded_graphics::prelude::*; +use embedded_graphics::primitives::{PrimitiveStyleBuilder, Rectangle}; +use mipidsi::models::GC9A01; +use mipidsi::options::{ColorOrder, ColorInversion}; +use mipidsi::Builder; +use rand_core::RngCore; +use {defmt_rtt as _, panic_probe as _}; + +const DISPLAY_FREQ: u32 = 64_000_000; +const LCD_X_RES: i32 = 240; +const LCD_Y_RES: i32 = 240; +const FERRIS_WIDTH: u32 = 86; +const FERRIS_HEIGHT: u32 = 64; + +#[embassy_executor::main] +async fn main(_spawner: Spawner) { + let p = embassy_rp::init(Default::default()); + let mut rng = RoscRng; + + info!("Hello World!"); + + let bl = p.PIN_25; + let rst = p.PIN_12; + let display_cs = p.PIN_9; + let dcx = p.PIN_8; + let mosi = p.PIN_11; + let clk = p.PIN_10; + + // create SPI + let mut display_config = spi::Config::default(); + display_config.frequency = DISPLAY_FREQ; + display_config.phase = spi::Phase::CaptureOnSecondTransition; + display_config.polarity = spi::Polarity::IdleHigh; + + let spi: Spi<'_, _, Blocking> = Spi::new_blocking_txonly(p.SPI1, clk, mosi, display_config.clone()); + let spi_bus: Mutex = Mutex::new(RefCell::new(spi)); + + let display_spi = SpiDeviceWithConfig::new(&spi_bus, Output::new(display_cs, Level::High), display_config); + let dcx = Output::new(dcx, Level::Low); + let rst = Output::new(rst, Level::Low); + // dcx: 0 = command, 1 = data + + // Enable LCD backlight + let _bl = Output::new(bl, Level::High); + + // display interface abstraction from SPI and DC + let di = SPIInterface::new(display_spi, dcx); + + // Define the display from the display interface and initialize it + let mut display = Builder::new(GC9A01, di) + .display_size(240, 240) + .reset_pin(rst) + .color_order(ColorOrder::Bgr) + .invert_colors(ColorInversion::Inverted) + .init(&mut Delay) + .unwrap(); + display.clear(Rgb565::BLACK).unwrap(); + + let raw_image_data = ImageRawLE::new(include_bytes!("../../assets/ferris.raw"), FERRIS_WIDTH); + let mut ferris = Image::new(&raw_image_data, Point::zero()); + + let r = rng.next_u32(); + let mut delta = Point { + x: ((r % 10) + 5) as i32, + y: (((r >> 8) % 10) + 5) as i32, + }; + loop { + // Move Ferris + let bb = ferris.bounding_box(); + let tl = bb.top_left; + let br = bb.bottom_right().unwrap(); + if tl.x < 0 || br.x > LCD_X_RES { + delta.x = -delta.x; + } + if tl.y < 0 || br.y > LCD_Y_RES { + delta.y = -delta.y; + } + + // Erase ghosting + let style = PrimitiveStyleBuilder::new().fill_color(Rgb565::BLACK).build(); + let mut off = Point { x: 0, y: 0 }; + if delta.x < 0 { + off.x = FERRIS_WIDTH as i32; + } + Rectangle::new(tl + off, Size::new(delta.x as u32, FERRIS_HEIGHT)) + .into_styled(style) + .draw(&mut display) + .unwrap(); + off = Point { x: 0, y: 0 }; + if delta.y < 0 { + off.y = FERRIS_HEIGHT as i32; + } + Rectangle::new(tl + off, Size::new(FERRIS_WIDTH, delta.y as u32)) + .into_styled(style) + .draw(&mut display) + .unwrap(); + // Translate Ferris + ferris.translate_mut(delta); + // Display the image + ferris.draw(&mut display).unwrap(); + Timer::after(Duration::from_millis(50)).await; + } +} From 9d21e3a1ae2bc22b5577032423aa3dac1ee09a3a Mon Sep 17 00:00:00 2001 From: Georges Palauqui Date: Tue, 5 Nov 2024 19:40:49 +0100 Subject: [PATCH 178/361] fix fmt --- examples/rp/src/bin/spi_gc9a01.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/rp/src/bin/spi_gc9a01.rs b/examples/rp/src/bin/spi_gc9a01.rs index d1cf23e16..30afc253d 100644 --- a/examples/rp/src/bin/spi_gc9a01.rs +++ b/examples/rp/src/bin/spi_gc9a01.rs @@ -24,7 +24,7 @@ use embedded_graphics::pixelcolor::Rgb565; use embedded_graphics::prelude::*; use embedded_graphics::primitives::{PrimitiveStyleBuilder, Rectangle}; use mipidsi::models::GC9A01; -use mipidsi::options::{ColorOrder, ColorInversion}; +use mipidsi::options::{ColorInversion, ColorOrder}; use mipidsi::Builder; use rand_core::RngCore; use {defmt_rtt as _, panic_probe as _}; From 682504eb0ea4e3b076c98885a1027c2ca47bdc53 Mon Sep 17 00:00:00 2001 From: Mathias Date: Wed, 6 Nov 2024 09:17:29 +0100 Subject: [PATCH 179/361] Fix get_state in cases where WRITE_SIZE != 1 --- embassy-boot/src/firmware_updater/asynch.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/embassy-boot/src/firmware_updater/asynch.rs b/embassy-boot/src/firmware_updater/asynch.rs index d9d15b004..0dc09e18d 100644 --- a/embassy-boot/src/firmware_updater/asynch.rs +++ b/embassy-boot/src/firmware_updater/asynch.rs @@ -304,7 +304,7 @@ impl<'d, STATE: NorFlash> FirmwareState<'d, STATE> { /// `mark_booted`. pub async fn get_state(&mut self) -> Result { self.state.read(0, &mut self.aligned).await?; - Ok(State::from(&self.aligned)) + Ok(State::from(&self.aligned[..STATE::WRITE_SIZE])) } /// Mark to trigger firmware swap on next boot. From 1e850ae79149e737c1ba39a383596eabcb0bb940 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?D=C3=A1niel=20Buga?= Date: Wed, 6 Nov 2024 10:44:01 +0100 Subject: [PATCH 180/361] Detect and allow older nightlies --- embassy-executor/Cargo.toml | 3 ++ embassy-executor/build.rs | 11 +++++++ embassy-executor/build_common.rs | 51 +++++++++++++++++++++++++++++++ embassy-executor/src/lib.rs | 1 + embassy-executor/src/raw/waker.rs | 11 ++++++- 5 files changed, 76 insertions(+), 1 deletion(-) diff --git a/embassy-executor/Cargo.toml b/embassy-executor/Cargo.toml index 22a176621..6011d8663 100644 --- a/embassy-executor/Cargo.toml +++ b/embassy-executor/Cargo.toml @@ -57,6 +57,9 @@ avr-device = { version = "0.5.3", optional = true } critical-section = { version = "1.1", features = ["std"] } trybuild = "1.0" +[build-dependencies] +rustc_version = "0.4.1" + [features] ## Enable nightly-only features diff --git a/embassy-executor/build.rs b/embassy-executor/build.rs index 8a41d7503..c4c86e1e2 100644 --- a/embassy-executor/build.rs +++ b/embassy-executor/build.rs @@ -96,4 +96,15 @@ fn main() { let mut rustc_cfgs = common::CfgSet::new(); common::set_target_cfgs(&mut rustc_cfgs); + + // Waker API changed on 2024-09-06 + rustc_cfgs.declare("at_least_2024_09_06"); + let Some(compiler) = common::compiler_info() else { + return; + }; + if compiler.channel == rustc_version::Channel::Nightly + && compiler.commit_date.map(|d| d >= "2024-09-06").unwrap_or(false) + { + rustc_cfgs.enable("at_least_2024_09_06"); + } } diff --git a/embassy-executor/build_common.rs b/embassy-executor/build_common.rs index 4f24e6d37..af6bb0618 100644 --- a/embassy-executor/build_common.rs +++ b/embassy-executor/build_common.rs @@ -92,3 +92,54 @@ pub fn set_target_cfgs(cfgs: &mut CfgSet) { cfgs.set("has_fpu", target.ends_with("-eabihf")); } + +#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)] +pub struct CompilerDate { + year: u16, + month: u8, + day: u8, +} + +impl CompilerDate { + fn parse(date: &str) -> Option { + let mut parts = date.split('-'); + let year = parts.next()?.parse().ok()?; + let month = parts.next()?.parse().ok()?; + let day = parts.next()?.parse().ok()?; + Some(Self { year, month, day }) + } +} + +impl PartialEq<&str> for CompilerDate { + fn eq(&self, other: &&str) -> bool { + let Some(other) = Self::parse(other) else { + return false; + }; + self.eq(&other) + } +} + +impl PartialOrd<&str> for CompilerDate { + fn partial_cmp(&self, other: &&str) -> Option { + Self::parse(other).map(|other| self.cmp(&other)) + } +} + +pub struct CompilerInfo { + #[allow(unused)] + pub version: rustc_version::Version, + pub channel: rustc_version::Channel, + pub commit_date: Option, +} + +pub fn compiler_info() -> Option { + let Ok(meta) = rustc_version::version_meta() else { + return None; + }; + + Some(CompilerInfo { + version: meta.semver, + channel: meta.channel, + commit_date: meta.commit_date.as_deref().and_then(CompilerDate::parse), + }) +} diff --git a/embassy-executor/src/lib.rs b/embassy-executor/src/lib.rs index d816539ac..8e07a8b18 100644 --- a/embassy-executor/src/lib.rs +++ b/embassy-executor/src/lib.rs @@ -1,4 +1,5 @@ #![cfg_attr(not(any(feature = "arch-std", feature = "arch-wasm")), no_std)] +#![cfg_attr(all(feature = "nightly", not(at_least_2024_09_06)), feature(waker_getters))] #![allow(clippy::new_without_default)] #![doc = include_str!("../README.md")] #![warn(missing_docs)] diff --git a/embassy-executor/src/raw/waker.rs b/embassy-executor/src/raw/waker.rs index 8bb2cfd05..30b8cdd4c 100644 --- a/embassy-executor/src/raw/waker.rs +++ b/embassy-executor/src/raw/waker.rs @@ -50,7 +50,16 @@ pub fn task_from_waker(waker: &Waker) -> TaskRef { #[cfg(feature = "nightly")] { - (waker.vtable(), waker.data()) + #[cfg(not(at_least_2024_09_06))] + { + let raw_waker = waker.as_raw(); + (raw_waker.vtable(), raw_waker.data()) + } + + #[cfg(at_least_2024_09_06)] + { + (waker.vtable(), waker.data()) + } } }; From 72109a7bda5cf1568b1ff2b1b4b7de06f73d3342 Mon Sep 17 00:00:00 2001 From: Kenneth Knudsen Date: Wed, 6 Nov 2024 10:52:03 +0100 Subject: [PATCH 181/361] Split_ref with shortened lifetime. When borrowed skip drop on rx and tx --- embassy-stm32/src/usart/buffered.rs | 78 +++++++++++++++++++---------- 1 file changed, 52 insertions(+), 26 deletions(-) diff --git a/embassy-stm32/src/usart/buffered.rs b/embassy-stm32/src/usart/buffered.rs index d7ac33d07..f7b2bf4b4 100644 --- a/embassy-stm32/src/usart/buffered.rs +++ b/embassy-stm32/src/usart/buffered.rs @@ -157,6 +157,7 @@ pub struct BufferedUartTx<'d> { tx: Option>, cts: Option>, de: Option>, + is_borrowed: bool, } /// Rx-only buffered UART @@ -168,6 +169,7 @@ pub struct BufferedUartRx<'d> { kernel_clock: Hertz, rx: Option>, rts: Option>, + is_borrowed: bool, } impl<'d> SetConfig for BufferedUart<'d> { @@ -341,6 +343,7 @@ impl<'d> BufferedUart<'d> { kernel_clock, rx, rts, + is_borrowed: false, }, tx: BufferedUartTx { info, @@ -349,6 +352,7 @@ impl<'d> BufferedUart<'d> { tx, cts, de, + is_borrowed: false, }, }; this.enable_and_configure(tx_buffer, rx_buffer, &config)?; @@ -396,11 +400,29 @@ impl<'d> BufferedUart<'d> { (self.tx, self.rx) } - /// Split the Uart into a transmitter and receiver by mutable reference, + /// Split the Uart into a transmitter and receiver, /// which is particularly useful when having two tasks correlating to /// transmitting and receiving. - pub fn split_ref(&mut self) -> (&mut BufferedUartTx<'d>, &mut BufferedUartRx<'d>) { - (&mut self.tx, &mut self.rx) + pub fn split_ref(&mut self) -> (BufferedUartTx<'_>, BufferedUartRx<'_>) { + ( + BufferedUartTx { + info: self.tx.info, + state: self.tx.state, + kernel_clock: self.tx.kernel_clock, + tx: self.tx.tx.as_mut().map(PeripheralRef::reborrow), + cts: self.tx.cts.as_mut().map(PeripheralRef::reborrow), + de: self.tx.de.as_mut().map(PeripheralRef::reborrow), + is_borrowed: true, + }, + BufferedUartRx { + info: self.rx.info, + state: self.rx.state, + kernel_clock: self.rx.kernel_clock, + rx: self.rx.rx.as_mut().map(PeripheralRef::reborrow), + rts: self.rx.rts.as_mut().map(PeripheralRef::reborrow), + is_borrowed: true, + }, + ) } /// Reconfigure the driver @@ -607,40 +629,44 @@ impl<'d> BufferedUartTx<'d> { impl<'d> Drop for BufferedUartRx<'d> { fn drop(&mut self) { - let state = self.state; - unsafe { - state.rx_buf.deinit(); + if !self.is_borrowed { + let state = self.state; + unsafe { + state.rx_buf.deinit(); - // TX is inactive if the the buffer is not available. - // We can now unregister the interrupt handler - if state.tx_buf.len() == 0 { - self.info.interrupt.disable(); + // TX is inactive if the the buffer is not available. + // We can now unregister the interrupt handler + if state.tx_buf.len() == 0 { + self.info.interrupt.disable(); + } } - } - self.rx.as_ref().map(|x| x.set_as_disconnected()); - self.rts.as_ref().map(|x| x.set_as_disconnected()); - drop_tx_rx(self.info, state); + self.rx.as_ref().map(|x| x.set_as_disconnected()); + self.rts.as_ref().map(|x| x.set_as_disconnected()); + drop_tx_rx(self.info, state); + } } } impl<'d> Drop for BufferedUartTx<'d> { fn drop(&mut self) { - let state = self.state; - unsafe { - state.tx_buf.deinit(); + if !self.is_borrowed { + let state = self.state; + unsafe { + state.tx_buf.deinit(); - // RX is inactive if the the buffer is not available. - // We can now unregister the interrupt handler - if state.rx_buf.len() == 0 { - self.info.interrupt.disable(); + // RX is inactive if the the buffer is not available. + // We can now unregister the interrupt handler + if state.rx_buf.len() == 0 { + self.info.interrupt.disable(); + } } - } - self.tx.as_ref().map(|x| x.set_as_disconnected()); - self.cts.as_ref().map(|x| x.set_as_disconnected()); - self.de.as_ref().map(|x| x.set_as_disconnected()); - drop_tx_rx(self.info, state); + self.tx.as_ref().map(|x| x.set_as_disconnected()); + self.cts.as_ref().map(|x| x.set_as_disconnected()); + self.de.as_ref().map(|x| x.set_as_disconnected()); + drop_tx_rx(self.info, state); + } } } From 94659325ab7e61381b31a156e7fc61ac912c794c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?D=C3=A1niel=20Buga?= Date: Wed, 6 Nov 2024 13:56:55 +0100 Subject: [PATCH 182/361] Prep executor 0.6.2 --- embassy-executor/CHANGELOG.md | 8 ++++++-- embassy-executor/Cargo.toml | 2 +- embassy-rp/Cargo.toml | 2 +- embassy-stm32/Cargo.toml | 2 +- embassy-time/Cargo.toml | 2 +- examples/boot/application/nrf/Cargo.toml | 2 +- examples/boot/application/rp/Cargo.toml | 2 +- examples/boot/application/stm32f3/Cargo.toml | 2 +- examples/boot/application/stm32f7/Cargo.toml | 2 +- examples/boot/application/stm32h7/Cargo.toml | 2 +- examples/boot/application/stm32l0/Cargo.toml | 2 +- examples/boot/application/stm32l1/Cargo.toml | 2 +- examples/boot/application/stm32l4/Cargo.toml | 2 +- examples/boot/application/stm32wb-dfu/Cargo.toml | 2 +- examples/boot/application/stm32wl/Cargo.toml | 2 +- examples/lpc55s69/Cargo.toml | 2 +- examples/nrf-rtos-trace/Cargo.toml | 2 +- examples/nrf51/Cargo.toml | 2 +- examples/nrf52810/Cargo.toml | 2 +- examples/nrf52840/Cargo.toml | 2 +- examples/nrf5340/Cargo.toml | 2 +- examples/nrf9151/ns/Cargo.toml | 2 +- examples/nrf9151/s/Cargo.toml | 2 +- examples/nrf9160/Cargo.toml | 2 +- examples/rp/Cargo.toml | 2 +- examples/rp23/Cargo.toml | 2 +- examples/std/Cargo.toml | 2 +- examples/stm32c0/Cargo.toml | 2 +- examples/stm32f0/Cargo.toml | 2 +- examples/stm32f1/Cargo.toml | 2 +- examples/stm32f2/Cargo.toml | 2 +- examples/stm32f3/Cargo.toml | 2 +- examples/stm32f334/Cargo.toml | 2 +- examples/stm32f4/Cargo.toml | 2 +- examples/stm32f469/Cargo.toml | 2 +- examples/stm32f7/Cargo.toml | 2 +- examples/stm32g0/Cargo.toml | 2 +- examples/stm32g4/Cargo.toml | 2 +- examples/stm32h5/Cargo.toml | 2 +- examples/stm32h7/Cargo.toml | 2 +- examples/stm32h735/Cargo.toml | 2 +- examples/stm32h755cm4/Cargo.toml | 2 +- examples/stm32h755cm7/Cargo.toml | 2 +- examples/stm32h7b0/Cargo.toml | 2 +- examples/stm32h7rs/Cargo.toml | 2 +- examples/stm32l0/Cargo.toml | 2 +- examples/stm32l1/Cargo.toml | 2 +- examples/stm32l4/Cargo.toml | 2 +- examples/stm32l5/Cargo.toml | 2 +- examples/stm32u0/Cargo.toml | 2 +- examples/stm32u5/Cargo.toml | 2 +- examples/stm32wb/Cargo.toml | 2 +- examples/stm32wba/Cargo.toml | 2 +- examples/stm32wl/Cargo.toml | 2 +- examples/wasm/Cargo.toml | 2 +- tests/nrf/Cargo.toml | 2 +- tests/riscv32/Cargo.toml | 2 +- tests/rp/Cargo.toml | 2 +- tests/stm32/Cargo.toml | 2 +- 59 files changed, 64 insertions(+), 60 deletions(-) diff --git a/embassy-executor/CHANGELOG.md b/embassy-executor/CHANGELOG.md index 3d8c48676..b342ccc32 100644 --- a/embassy-executor/CHANGELOG.md +++ b/embassy-executor/CHANGELOG.md @@ -7,20 +7,24 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## Unreleased +## 0.6.2 - 2024-11-06 + +- The `nightly` feature no longer requires `nightly-2024-09-06` or newer. + ## 0.6.1 - 2024-10-21 - Soundness fix: Deny using `impl Trait` in task arguments. This was previously accidentally allowed when not using the `nightly` feature, and could cause out of bounds memory accesses if spawning the same task mulitple times with different underlying types for the `impl Trait`. Affected versions are 0.4.x, 0.5.0 and 0.6.0, which have been yanked. - Add an architecture-agnostic executor that spins waiting for tasks to run, enabled with the `arch-spin` feature. -- Update for breaking change in the nightly waker_getters API. The `nightly` feature now requires`nightly-2024-09-06` or newer. +- Update for breaking change in the nightly waker_getters API. The `nightly` feature now requires `nightly-2024-09-06` or newer. - Improve macro error messages. ## 0.6.0 - 2024-08-05 - Add collapse_debuginfo to fmt.rs macros. - initial support for AVR -- use nightly waker_getters APIs +- use nightly waker_getters APIs ## 0.5.1 - 2024-10-21 diff --git a/embassy-executor/Cargo.toml b/embassy-executor/Cargo.toml index 6011d8663..6b16a6343 100644 --- a/embassy-executor/Cargo.toml +++ b/embassy-executor/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "embassy-executor" -version = "0.6.1" +version = "0.6.2" edition = "2021" license = "MIT OR Apache-2.0" description = "async/await executor designed for embedded usage" diff --git a/embassy-rp/Cargo.toml b/embassy-rp/Cargo.toml index 3e61641ff..4616116aa 100644 --- a/embassy-rp/Cargo.toml +++ b/embassy-rp/Cargo.toml @@ -147,5 +147,5 @@ rp-binary-info = { version = "0.1.0", optional = true } smart-leds = "0.4.0" [dev-dependencies] -embassy-executor = { version = "0.6.1", path = "../embassy-executor", features = ["arch-std", "executor-thread"] } +embassy-executor = { version = "0.6.2", path = "../embassy-executor", features = ["arch-std", "executor-thread"] } static_cell = { version = "2" } diff --git a/embassy-stm32/Cargo.toml b/embassy-stm32/Cargo.toml index 9fdc799d8..6ab6e162e 100644 --- a/embassy-stm32/Cargo.toml +++ b/embassy-stm32/Cargo.toml @@ -51,7 +51,7 @@ embassy-embedded-hal = {version = "0.2.0", path = "../embassy-embedded-hal", def embassy-net-driver = { version = "0.2.0", path = "../embassy-net-driver" } embassy-usb-driver = {version = "0.1.0", path = "../embassy-usb-driver" } embassy-usb-synopsys-otg = {version = "0.1.0", path = "../embassy-usb-synopsys-otg" } -embassy-executor = { version = "0.6.1", path = "../embassy-executor", optional = true } +embassy-executor = { version = "0.6.2", path = "../embassy-executor", optional = true } embedded-hal-02 = { package = "embedded-hal", version = "0.2.6", features = ["unproven"] } embedded-hal-1 = { package = "embedded-hal", version = "1.0" } diff --git a/embassy-time/Cargo.toml b/embassy-time/Cargo.toml index dc3561145..0e61434f1 100644 --- a/embassy-time/Cargo.toml +++ b/embassy-time/Cargo.toml @@ -431,4 +431,4 @@ wasm-timer = { version = "0.2.5", optional = true } [dev-dependencies] serial_test = "0.9" critical-section = { version = "1.1", features = ["std"] } -embassy-executor = { version = "0.6.1", path = "../embassy-executor" } +embassy-executor = { version = "0.6.2", path = "../embassy-executor" } diff --git a/examples/boot/application/nrf/Cargo.toml b/examples/boot/application/nrf/Cargo.toml index 3815ac196..e2ae240ae 100644 --- a/examples/boot/application/nrf/Cargo.toml +++ b/examples/boot/application/nrf/Cargo.toml @@ -6,7 +6,7 @@ license = "MIT OR Apache-2.0" [dependencies] embassy-sync = { version = "0.6.0", path = "../../../../embassy-sync" } -embassy-executor = { version = "0.6.1", path = "../../../../embassy-executor", features = ["task-arena-size-16384", "arch-cortex-m", "executor-thread", "integrated-timers", "arch-cortex-m", "executor-thread"] } +embassy-executor = { version = "0.6.2", path = "../../../../embassy-executor", features = ["task-arena-size-16384", "arch-cortex-m", "executor-thread", "integrated-timers", "arch-cortex-m", "executor-thread"] } embassy-time = { version = "0.3.2", path = "../../../../embassy-time", features = [] } embassy-nrf = { version = "0.2.0", path = "../../../../embassy-nrf", features = ["time-driver-rtc1", "gpiote", ] } embassy-boot = { version = "0.3.0", path = "../../../../embassy-boot", features = [] } diff --git a/examples/boot/application/rp/Cargo.toml b/examples/boot/application/rp/Cargo.toml index 86ea9fc2a..f61ac3fd9 100644 --- a/examples/boot/application/rp/Cargo.toml +++ b/examples/boot/application/rp/Cargo.toml @@ -6,7 +6,7 @@ license = "MIT OR Apache-2.0" [dependencies] embassy-sync = { version = "0.6.0", path = "../../../../embassy-sync" } -embassy-executor = { version = "0.6.1", path = "../../../../embassy-executor", features = ["task-arena-size-16384", "arch-cortex-m", "executor-thread", "integrated-timers", "arch-cortex-m", "executor-thread"] } +embassy-executor = { version = "0.6.2", path = "../../../../embassy-executor", features = ["task-arena-size-16384", "arch-cortex-m", "executor-thread", "integrated-timers", "arch-cortex-m", "executor-thread"] } embassy-time = { version = "0.3.2", path = "../../../../embassy-time", features = [] } embassy-rp = { version = "0.2.0", path = "../../../../embassy-rp", features = ["time-driver", "rp2040"] } embassy-boot-rp = { version = "0.3.0", path = "../../../../embassy-boot-rp", features = [] } diff --git a/examples/boot/application/stm32f3/Cargo.toml b/examples/boot/application/stm32f3/Cargo.toml index 29049e58c..dd20d2e0d 100644 --- a/examples/boot/application/stm32f3/Cargo.toml +++ b/examples/boot/application/stm32f3/Cargo.toml @@ -6,7 +6,7 @@ license = "MIT OR Apache-2.0" [dependencies] embassy-sync = { version = "0.6.0", path = "../../../../embassy-sync" } -embassy-executor = { version = "0.6.1", path = "../../../../embassy-executor", features = ["task-arena-size-8192", "arch-cortex-m", "executor-thread", "integrated-timers"] } +embassy-executor = { version = "0.6.2", path = "../../../../embassy-executor", features = ["task-arena-size-8192", "arch-cortex-m", "executor-thread", "integrated-timers"] } embassy-time = { version = "0.3.2", path = "../../../../embassy-time", features = [ "tick-hz-32_768"] } embassy-stm32 = { version = "0.1.0", path = "../../../../embassy-stm32", features = ["stm32f303re", "time-driver-any", "exti"] } embassy-boot-stm32 = { version = "0.2.0", path = "../../../../embassy-boot-stm32" } diff --git a/examples/boot/application/stm32f7/Cargo.toml b/examples/boot/application/stm32f7/Cargo.toml index ed102b073..ce38e9ab9 100644 --- a/examples/boot/application/stm32f7/Cargo.toml +++ b/examples/boot/application/stm32f7/Cargo.toml @@ -6,7 +6,7 @@ license = "MIT OR Apache-2.0" [dependencies] embassy-sync = { version = "0.6.0", path = "../../../../embassy-sync" } -embassy-executor = { version = "0.6.1", path = "../../../../embassy-executor", features = ["task-arena-size-8192", "arch-cortex-m", "executor-thread", "integrated-timers"] } +embassy-executor = { version = "0.6.2", path = "../../../../embassy-executor", features = ["task-arena-size-8192", "arch-cortex-m", "executor-thread", "integrated-timers"] } embassy-time = { version = "0.3.2", path = "../../../../embassy-time", features = [ "tick-hz-32_768"] } embassy-stm32 = { version = "0.1.0", path = "../../../../embassy-stm32", features = ["stm32f767zi", "time-driver-any", "exti"] } embassy-boot-stm32 = { version = "0.2.0", path = "../../../../embassy-boot-stm32", features = [] } diff --git a/examples/boot/application/stm32h7/Cargo.toml b/examples/boot/application/stm32h7/Cargo.toml index cc705b1d6..841075627 100644 --- a/examples/boot/application/stm32h7/Cargo.toml +++ b/examples/boot/application/stm32h7/Cargo.toml @@ -6,7 +6,7 @@ license = "MIT OR Apache-2.0" [dependencies] embassy-sync = { version = "0.6.0", path = "../../../../embassy-sync" } -embassy-executor = { version = "0.6.1", path = "../../../../embassy-executor", features = ["task-arena-size-8192", "arch-cortex-m", "executor-thread", "integrated-timers"] } +embassy-executor = { version = "0.6.2", path = "../../../../embassy-executor", features = ["task-arena-size-8192", "arch-cortex-m", "executor-thread", "integrated-timers"] } embassy-time = { version = "0.3.2", path = "../../../../embassy-time", features = [ "tick-hz-32_768"] } embassy-stm32 = { version = "0.1.0", path = "../../../../embassy-stm32", features = ["stm32h743zi", "time-driver-any", "exti"] } embassy-boot-stm32 = { version = "0.2.0", path = "../../../../embassy-boot-stm32", features = [] } diff --git a/examples/boot/application/stm32l0/Cargo.toml b/examples/boot/application/stm32l0/Cargo.toml index 17fc1d7d0..1a92517db 100644 --- a/examples/boot/application/stm32l0/Cargo.toml +++ b/examples/boot/application/stm32l0/Cargo.toml @@ -6,7 +6,7 @@ license = "MIT OR Apache-2.0" [dependencies] embassy-sync = { version = "0.6.0", path = "../../../../embassy-sync" } -embassy-executor = { version = "0.6.1", path = "../../../../embassy-executor", features = ["task-arena-size-8192", "arch-cortex-m", "executor-thread", "integrated-timers"] } +embassy-executor = { version = "0.6.2", path = "../../../../embassy-executor", features = ["task-arena-size-8192", "arch-cortex-m", "executor-thread", "integrated-timers"] } embassy-time = { version = "0.3.2", path = "../../../../embassy-time", features = [ "tick-hz-32_768"] } embassy-stm32 = { version = "0.1.0", path = "../../../../embassy-stm32", features = ["stm32l072cz", "time-driver-any", "exti", "memory-x"] } embassy-boot-stm32 = { version = "0.2.0", path = "../../../../embassy-boot-stm32", features = [] } diff --git a/examples/boot/application/stm32l1/Cargo.toml b/examples/boot/application/stm32l1/Cargo.toml index fd2c19f99..57eeb07a1 100644 --- a/examples/boot/application/stm32l1/Cargo.toml +++ b/examples/boot/application/stm32l1/Cargo.toml @@ -6,7 +6,7 @@ license = "MIT OR Apache-2.0" [dependencies] embassy-sync = { version = "0.6.0", path = "../../../../embassy-sync" } -embassy-executor = { version = "0.6.1", path = "../../../../embassy-executor", features = ["task-arena-size-8192", "arch-cortex-m", "executor-thread", "integrated-timers"] } +embassy-executor = { version = "0.6.2", path = "../../../../embassy-executor", features = ["task-arena-size-8192", "arch-cortex-m", "executor-thread", "integrated-timers"] } embassy-time = { version = "0.3.2", path = "../../../../embassy-time", features = [ "tick-hz-32_768"] } embassy-stm32 = { version = "0.1.0", path = "../../../../embassy-stm32", features = ["stm32l151cb-a", "time-driver-any", "exti"] } embassy-boot-stm32 = { version = "0.2.0", path = "../../../../embassy-boot-stm32", features = [] } diff --git a/examples/boot/application/stm32l4/Cargo.toml b/examples/boot/application/stm32l4/Cargo.toml index 3852261ac..f51ca29e1 100644 --- a/examples/boot/application/stm32l4/Cargo.toml +++ b/examples/boot/application/stm32l4/Cargo.toml @@ -6,7 +6,7 @@ license = "MIT OR Apache-2.0" [dependencies] embassy-sync = { version = "0.6.0", path = "../../../../embassy-sync" } -embassy-executor = { version = "0.6.1", path = "../../../../embassy-executor", features = ["task-arena-size-8192", "arch-cortex-m", "executor-thread", "integrated-timers"] } +embassy-executor = { version = "0.6.2", path = "../../../../embassy-executor", features = ["task-arena-size-8192", "arch-cortex-m", "executor-thread", "integrated-timers"] } embassy-time = { version = "0.3.2", path = "../../../../embassy-time", features = [ "tick-hz-32_768"] } embassy-stm32 = { version = "0.1.0", path = "../../../../embassy-stm32", features = ["stm32l475vg", "time-driver-any", "exti"] } embassy-boot-stm32 = { version = "0.2.0", path = "../../../../embassy-boot-stm32", features = [] } diff --git a/examples/boot/application/stm32wb-dfu/Cargo.toml b/examples/boot/application/stm32wb-dfu/Cargo.toml index 2d89f6d42..112de92f1 100644 --- a/examples/boot/application/stm32wb-dfu/Cargo.toml +++ b/examples/boot/application/stm32wb-dfu/Cargo.toml @@ -6,7 +6,7 @@ license = "MIT OR Apache-2.0" [dependencies] embassy-sync = { version = "0.6.0", path = "../../../../embassy-sync" } -embassy-executor = { version = "0.6.1", path = "../../../../embassy-executor", features = ["task-arena-size-8192", "arch-cortex-m", "executor-thread", "integrated-timers"] } +embassy-executor = { version = "0.6.2", path = "../../../../embassy-executor", features = ["task-arena-size-8192", "arch-cortex-m", "executor-thread", "integrated-timers"] } embassy-time = { version = "0.3.2", path = "../../../../embassy-time", features = [ "tick-hz-32_768"] } embassy-stm32 = { version = "0.1.0", path = "../../../../embassy-stm32", features = ["stm32wb55rg", "time-driver-any", "exti"] } embassy-boot-stm32 = { version = "0.2.0", path = "../../../../embassy-boot-stm32", features = [] } diff --git a/examples/boot/application/stm32wl/Cargo.toml b/examples/boot/application/stm32wl/Cargo.toml index e07d97d86..e2c42ce20 100644 --- a/examples/boot/application/stm32wl/Cargo.toml +++ b/examples/boot/application/stm32wl/Cargo.toml @@ -6,7 +6,7 @@ license = "MIT OR Apache-2.0" [dependencies] embassy-sync = { version = "0.6.0", path = "../../../../embassy-sync" } -embassy-executor = { version = "0.6.1", path = "../../../../embassy-executor", features = ["task-arena-size-8192", "arch-cortex-m", "executor-thread", "integrated-timers"] } +embassy-executor = { version = "0.6.2", path = "../../../../embassy-executor", features = ["task-arena-size-8192", "arch-cortex-m", "executor-thread", "integrated-timers"] } embassy-time = { version = "0.3.2", path = "../../../../embassy-time", features = [ "tick-hz-32_768"] } embassy-stm32 = { version = "0.1.0", path = "../../../../embassy-stm32", features = ["stm32wl55jc-cm4", "time-driver-any", "exti"] } embassy-boot-stm32 = { version = "0.2.0", path = "../../../../embassy-boot-stm32", features = [] } diff --git a/examples/lpc55s69/Cargo.toml b/examples/lpc55s69/Cargo.toml index a69007a2c..a18b29f2e 100644 --- a/examples/lpc55s69/Cargo.toml +++ b/examples/lpc55s69/Cargo.toml @@ -7,7 +7,7 @@ license = "MIT OR Apache-2.0" [dependencies] embassy-nxp = { version = "0.1.0", path = "../../embassy-nxp", features = ["rt"] } -embassy-executor = { version = "0.6.1", path = "../../embassy-executor", features = ["task-arena-size-32768", "arch-cortex-m", "executor-thread", "executor-interrupt"] } +embassy-executor = { version = "0.6.2", path = "../../embassy-executor", features = ["task-arena-size-32768", "arch-cortex-m", "executor-thread", "executor-interrupt"] } embassy-sync = { version = "0.6.0", path = "../../embassy-sync", features = ["defmt"] } embassy-time = { version = "0.3.0", path = "../../embassy-time", features = ["defmt"] } panic-halt = "0.2.0" diff --git a/examples/nrf-rtos-trace/Cargo.toml b/examples/nrf-rtos-trace/Cargo.toml index 2358ced56..fbdd2744c 100644 --- a/examples/nrf-rtos-trace/Cargo.toml +++ b/examples/nrf-rtos-trace/Cargo.toml @@ -16,7 +16,7 @@ log = [ [dependencies] embassy-sync = { version = "0.6.0", path = "../../embassy-sync" } -embassy-executor = { version = "0.6.1", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "rtos-trace", "integrated-timers"] } +embassy-executor = { version = "0.6.2", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "rtos-trace", "integrated-timers"] } embassy-time = { version = "0.3.2", path = "../../embassy-time" } embassy-nrf = { version = "0.2.0", path = "../../embassy-nrf", features = ["nrf52840", "time-driver-rtc1", "gpiote", "unstable-pac"] } diff --git a/examples/nrf51/Cargo.toml b/examples/nrf51/Cargo.toml index f94026158..3799a87cc 100644 --- a/examples/nrf51/Cargo.toml +++ b/examples/nrf51/Cargo.toml @@ -5,7 +5,7 @@ version = "0.1.0" license = "MIT OR Apache-2.0" [dependencies] -embassy-executor = { version = "0.6.1", path = "../../embassy-executor", features = ["task-arena-size-4096", "arch-cortex-m", "executor-thread", "executor-interrupt", "defmt", "integrated-timers"] } +embassy-executor = { version = "0.6.2", path = "../../embassy-executor", features = ["task-arena-size-4096", "arch-cortex-m", "executor-thread", "executor-interrupt", "defmt", "integrated-timers"] } embassy-time = { version = "0.3.2", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime"] } embassy-nrf = { version = "0.2.0", path = "../../embassy-nrf", features = ["defmt", "nrf51", "gpiote", "time-driver-rtc1", "unstable-pac", "time", "rt"] } diff --git a/examples/nrf52810/Cargo.toml b/examples/nrf52810/Cargo.toml index 867ead6d8..e3045bdba 100644 --- a/examples/nrf52810/Cargo.toml +++ b/examples/nrf52810/Cargo.toml @@ -7,7 +7,7 @@ license = "MIT OR Apache-2.0" [dependencies] embassy-futures = { version = "0.1.0", path = "../../embassy-futures" } embassy-sync = { version = "0.6.0", path = "../../embassy-sync", features = ["defmt"] } -embassy-executor = { version = "0.6.1", path = "../../embassy-executor", features = ["task-arena-size-8192", "arch-cortex-m", "executor-thread", "executor-interrupt", "defmt", "integrated-timers"] } +embassy-executor = { version = "0.6.2", path = "../../embassy-executor", features = ["task-arena-size-8192", "arch-cortex-m", "executor-thread", "executor-interrupt", "defmt", "integrated-timers"] } embassy-time = { version = "0.3.2", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime"] } embassy-nrf = { version = "0.2.0", path = "../../embassy-nrf", features = ["defmt", "nrf52810", "time-driver-rtc1", "gpiote", "unstable-pac", "time"] } diff --git a/examples/nrf52840/Cargo.toml b/examples/nrf52840/Cargo.toml index cb05b7204..e0a27c628 100644 --- a/examples/nrf52840/Cargo.toml +++ b/examples/nrf52840/Cargo.toml @@ -7,7 +7,7 @@ license = "MIT OR Apache-2.0" [dependencies] embassy-futures = { version = "0.1.0", path = "../../embassy-futures" } embassy-sync = { version = "0.6.0", path = "../../embassy-sync", features = ["defmt"] } -embassy-executor = { version = "0.6.1", path = "../../embassy-executor", features = ["task-arena-size-32768", "arch-cortex-m", "executor-thread", "executor-interrupt", "defmt", "integrated-timers"] } +embassy-executor = { version = "0.6.2", path = "../../embassy-executor", features = ["task-arena-size-32768", "arch-cortex-m", "executor-thread", "executor-interrupt", "defmt", "integrated-timers"] } embassy-time = { version = "0.3.2", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime"] } embassy-nrf = { version = "0.2.0", path = "../../embassy-nrf", features = ["defmt", "nrf52840", "time-driver-rtc1", "gpiote", "unstable-pac", "time"] } embassy-net = { version = "0.4.0", path = "../../embassy-net", features = ["defmt", "tcp", "dhcpv4", "medium-ethernet"] } diff --git a/examples/nrf5340/Cargo.toml b/examples/nrf5340/Cargo.toml index 2387eaa58..2a4e78b7c 100644 --- a/examples/nrf5340/Cargo.toml +++ b/examples/nrf5340/Cargo.toml @@ -7,7 +7,7 @@ license = "MIT OR Apache-2.0" [dependencies] embassy-futures = { version = "0.1.0", path = "../../embassy-futures" } embassy-sync = { version = "0.6.0", path = "../../embassy-sync", features = ["defmt"] } -embassy-executor = { version = "0.6.1", path = "../../embassy-executor", features = ["task-arena-size-32768", "arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] } +embassy-executor = { version = "0.6.2", path = "../../embassy-executor", features = ["task-arena-size-32768", "arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] } embassy-time = { version = "0.3.2", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime"] } embassy-nrf = { version = "0.2.0", path = "../../embassy-nrf", features = ["defmt", "nrf5340-app-s", "time-driver-rtc1", "gpiote", "unstable-pac"] } embassy-net = { version = "0.4.0", path = "../../embassy-net", features = ["defmt", "tcp", "dhcpv4", "medium-ethernet"] } diff --git a/examples/nrf9151/ns/Cargo.toml b/examples/nrf9151/ns/Cargo.toml index 78260dae3..679331716 100644 --- a/examples/nrf9151/ns/Cargo.toml +++ b/examples/nrf9151/ns/Cargo.toml @@ -5,7 +5,7 @@ version = "0.1.0" license = "MIT OR Apache-2.0" [dependencies] -embassy-executor = { version = "0.6.1", path = "../../../embassy-executor", features = ["task-arena-size-32768", "arch-cortex-m", "executor-thread", "executor-interrupt", "defmt", "integrated-timers"] } +embassy-executor = { version = "0.6.2", path = "../../../embassy-executor", features = ["task-arena-size-32768", "arch-cortex-m", "executor-thread", "executor-interrupt", "defmt", "integrated-timers"] } embassy-time = { version = "0.3.2", path = "../../../embassy-time", features = ["defmt", "defmt-timestamp-uptime"] } embassy-nrf = { version = "0.2.0", path = "../../../embassy-nrf", features = ["defmt", "nrf9120-ns", "time-driver-rtc1", "gpiote", "unstable-pac", "time"] } diff --git a/examples/nrf9151/s/Cargo.toml b/examples/nrf9151/s/Cargo.toml index 17ead4ab4..63114478b 100644 --- a/examples/nrf9151/s/Cargo.toml +++ b/examples/nrf9151/s/Cargo.toml @@ -5,7 +5,7 @@ version = "0.1.0" license = "MIT OR Apache-2.0" [dependencies] -embassy-executor = { version = "0.6.1", path = "../../../embassy-executor", features = ["task-arena-size-32768", "arch-cortex-m", "executor-thread", "executor-interrupt", "defmt", "integrated-timers"] } +embassy-executor = { version = "0.6.2", path = "../../../embassy-executor", features = ["task-arena-size-32768", "arch-cortex-m", "executor-thread", "executor-interrupt", "defmt", "integrated-timers"] } embassy-time = { version = "0.3.2", path = "../../../embassy-time", features = ["defmt", "defmt-timestamp-uptime"] } embassy-nrf = { version = "0.2.0", path = "../../../embassy-nrf", features = ["defmt", "nrf9120-s", "time-driver-rtc1", "gpiote", "unstable-pac", "time"] } diff --git a/examples/nrf9160/Cargo.toml b/examples/nrf9160/Cargo.toml index 2572f3353..d5b0f0a41 100644 --- a/examples/nrf9160/Cargo.toml +++ b/examples/nrf9160/Cargo.toml @@ -5,7 +5,7 @@ version = "0.1.0" license = "MIT OR Apache-2.0" [dependencies] -embassy-executor = { version = "0.6.1", path = "../../embassy-executor", features = ["task-arena-size-32768", "arch-cortex-m", "executor-thread", "executor-interrupt", "defmt", "integrated-timers"] } +embassy-executor = { version = "0.6.2", path = "../../embassy-executor", features = ["task-arena-size-32768", "arch-cortex-m", "executor-thread", "executor-interrupt", "defmt", "integrated-timers"] } embassy-time = { version = "0.3.2", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime"] } embassy-nrf = { version = "0.2.0", path = "../../embassy-nrf", features = ["defmt", "nrf9160-s", "time-driver-rtc1", "gpiote", "unstable-pac", "time"] } embassy-net-nrf91 = { version = "0.1.0", path = "../../embassy-net-nrf91", features = ["defmt"] } diff --git a/examples/rp/Cargo.toml b/examples/rp/Cargo.toml index b55b20c63..b45f5040a 100644 --- a/examples/rp/Cargo.toml +++ b/examples/rp/Cargo.toml @@ -8,7 +8,7 @@ license = "MIT OR Apache-2.0" [dependencies] embassy-embedded-hal = { version = "0.2.0", path = "../../embassy-embedded-hal", features = ["defmt"] } embassy-sync = { version = "0.6.0", path = "../../embassy-sync", features = ["defmt"] } -embassy-executor = { version = "0.6.1", path = "../../embassy-executor", features = ["task-arena-size-98304", "arch-cortex-m", "executor-thread", "executor-interrupt", "defmt", "integrated-timers"] } +embassy-executor = { version = "0.6.2", path = "../../embassy-executor", features = ["task-arena-size-98304", "arch-cortex-m", "executor-thread", "executor-interrupt", "defmt", "integrated-timers"] } embassy-time = { version = "0.3.2", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime"] } embassy-rp = { version = "0.2.0", path = "../../embassy-rp", features = ["defmt", "unstable-pac", "time-driver", "critical-section-impl", "rp2040"] } embassy-usb = { version = "0.3.0", path = "../../embassy-usb", features = ["defmt"] } diff --git a/examples/rp23/Cargo.toml b/examples/rp23/Cargo.toml index eec12a9ab..99a75a67f 100644 --- a/examples/rp23/Cargo.toml +++ b/examples/rp23/Cargo.toml @@ -8,7 +8,7 @@ license = "MIT OR Apache-2.0" [dependencies] embassy-embedded-hal = { version = "0.2.0", path = "../../embassy-embedded-hal", features = ["defmt"] } embassy-sync = { version = "0.6.0", path = "../../embassy-sync", features = ["defmt"] } -embassy-executor = { version = "0.6.1", path = "../../embassy-executor", features = ["task-arena-size-98304", "arch-cortex-m", "executor-thread", "executor-interrupt", "defmt", "integrated-timers"] } +embassy-executor = { version = "0.6.2", path = "../../embassy-executor", features = ["task-arena-size-98304", "arch-cortex-m", "executor-thread", "executor-interrupt", "defmt", "integrated-timers"] } embassy-time = { version = "0.3.2", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime"] } embassy-rp = { version = "0.2.0", path = "../../embassy-rp", features = ["defmt", "unstable-pac", "time-driver", "critical-section-impl", "rp235xa", "binary-info"] } embassy-usb = { version = "0.3.0", path = "../../embassy-usb", features = ["defmt"] } diff --git a/examples/std/Cargo.toml b/examples/std/Cargo.toml index 27d0062fb..fac180c0c 100644 --- a/examples/std/Cargo.toml +++ b/examples/std/Cargo.toml @@ -6,7 +6,7 @@ license = "MIT OR Apache-2.0" [dependencies] embassy-sync = { version = "0.6.0", path = "../../embassy-sync", features = ["log"] } -embassy-executor = { version = "0.6.1", path = "../../embassy-executor", features = ["task-arena-size-32768", "arch-std", "executor-thread", "log", "integrated-timers"] } +embassy-executor = { version = "0.6.2", path = "../../embassy-executor", features = ["task-arena-size-32768", "arch-std", "executor-thread", "log", "integrated-timers"] } embassy-time = { version = "0.3.2", path = "../../embassy-time", features = ["log", "std", ] } embassy-net = { version = "0.4.0", path = "../../embassy-net", features=[ "std", "log", "medium-ethernet", "medium-ip", "tcp", "udp", "dns", "dhcpv4", "proto-ipv6"] } embassy-net-tuntap = { version = "0.1.0", path = "../../embassy-net-tuntap" } diff --git a/examples/stm32c0/Cargo.toml b/examples/stm32c0/Cargo.toml index 46a25cc4d..f8a845095 100644 --- a/examples/stm32c0/Cargo.toml +++ b/examples/stm32c0/Cargo.toml @@ -8,7 +8,7 @@ license = "MIT OR Apache-2.0" # Change stm32c031c6 to your chip name, if necessary. embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = [ "defmt", "time-driver-any", "stm32c031c6", "memory-x", "unstable-pac", "exti"] } embassy-sync = { version = "0.6.0", path = "../../embassy-sync", features = ["defmt"] } -embassy-executor = { version = "0.6.1", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] } +embassy-executor = { version = "0.6.2", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] } embassy-time = { version = "0.3.2", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } defmt = "0.3" diff --git a/examples/stm32f0/Cargo.toml b/examples/stm32f0/Cargo.toml index 93d9ab7ce..6154fdc64 100644 --- a/examples/stm32f0/Cargo.toml +++ b/examples/stm32f0/Cargo.toml @@ -13,7 +13,7 @@ defmt = "0.3" defmt-rtt = "0.4" panic-probe = { version = "0.3", features = ["print-defmt"] } embassy-sync = { version = "0.6.0", path = "../../embassy-sync", features = ["defmt"] } -embassy-executor = { version = "0.6.1", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "executor-interrupt", "defmt", "integrated-timers"] } +embassy-executor = { version = "0.6.2", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "executor-interrupt", "defmt", "integrated-timers"] } embassy-time = { version = "0.3.2", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } static_cell = "2" portable-atomic = { version = "1.5", features = ["unsafe-assume-single-core"] } diff --git a/examples/stm32f1/Cargo.toml b/examples/stm32f1/Cargo.toml index a75d0fe4c..c4836859e 100644 --- a/examples/stm32f1/Cargo.toml +++ b/examples/stm32f1/Cargo.toml @@ -8,7 +8,7 @@ license = "MIT OR Apache-2.0" # Change stm32f103c8 to your chip name, if necessary. embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = [ "defmt", "stm32f103c8", "unstable-pac", "memory-x", "time-driver-any" ] } embassy-sync = { version = "0.6.0", path = "../../embassy-sync", features = ["defmt"] } -embassy-executor = { version = "0.6.1", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] } +embassy-executor = { version = "0.6.2", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] } embassy-time = { version = "0.3.2", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } embassy-usb = { version = "0.3.0", path = "../../embassy-usb", features = ["defmt"] } embassy-futures = { version = "0.1.0", path = "../../embassy-futures" } diff --git a/examples/stm32f2/Cargo.toml b/examples/stm32f2/Cargo.toml index 166729211..0e4827e5d 100644 --- a/examples/stm32f2/Cargo.toml +++ b/examples/stm32f2/Cargo.toml @@ -8,7 +8,7 @@ license = "MIT OR Apache-2.0" # Change stm32f207zg to your chip name, if necessary. embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = [ "defmt", "stm32f207zg", "unstable-pac", "memory-x", "time-driver-any", "exti"] } embassy-sync = { version = "0.6.0", path = "../../embassy-sync", features = ["defmt"] } -embassy-executor = { version = "0.6.1", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] } +embassy-executor = { version = "0.6.2", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] } embassy-time = { version = "0.3.2", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } defmt = "0.3" diff --git a/examples/stm32f3/Cargo.toml b/examples/stm32f3/Cargo.toml index e60ea80c6..08a921634 100644 --- a/examples/stm32f3/Cargo.toml +++ b/examples/stm32f3/Cargo.toml @@ -8,7 +8,7 @@ license = "MIT OR Apache-2.0" # Change stm32f303ze to your chip name, if necessary. embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = [ "defmt", "stm32f303ze", "unstable-pac", "memory-x", "time-driver-any", "exti"] } embassy-sync = { version = "0.6.0", path = "../../embassy-sync", features = ["defmt"] } -embassy-executor = { version = "0.6.1", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "executor-interrupt", "defmt", "integrated-timers"] } +embassy-executor = { version = "0.6.2", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "executor-interrupt", "defmt", "integrated-timers"] } embassy-time = { version = "0.3.2", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } embassy-usb = { version = "0.3.0", path = "../../embassy-usb", features = ["defmt"] } embassy-futures = { version = "0.1.0", path = "../../embassy-futures" } diff --git a/examples/stm32f334/Cargo.toml b/examples/stm32f334/Cargo.toml index 4ba993009..cba83bf8c 100644 --- a/examples/stm32f334/Cargo.toml +++ b/examples/stm32f334/Cargo.toml @@ -6,7 +6,7 @@ license = "MIT OR Apache-2.0" [dependencies] embassy-sync = { version = "0.6.0", path = "../../embassy-sync", features = ["defmt"] } -embassy-executor = { version = "0.6.1", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "executor-interrupt", "defmt", "integrated-timers"] } +embassy-executor = { version = "0.6.2", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "executor-interrupt", "defmt", "integrated-timers"] } embassy-time = { version = "0.3.2", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = [ "defmt", "stm32f334r8", "unstable-pac", "memory-x", "time-driver-any", "exti"] } embassy-usb = { version = "0.3.0", path = "../../embassy-usb", features = ["defmt"] } diff --git a/examples/stm32f4/Cargo.toml b/examples/stm32f4/Cargo.toml index 975e47398..32fb845cb 100644 --- a/examples/stm32f4/Cargo.toml +++ b/examples/stm32f4/Cargo.toml @@ -8,7 +8,7 @@ license = "MIT OR Apache-2.0" # Change stm32f429zi to your chip name, if necessary. embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["defmt", "stm32f429zi", "unstable-pac", "memory-x", "time-driver-any", "exti", "chrono"] } embassy-sync = { version = "0.6.0", path = "../../embassy-sync", features = ["defmt"] } -embassy-executor = { version = "0.6.1", path = "../../embassy-executor", features = ["task-arena-size-32768", "arch-cortex-m", "executor-thread", "executor-interrupt", "defmt", "integrated-timers"] } +embassy-executor = { version = "0.6.2", path = "../../embassy-executor", features = ["task-arena-size-32768", "arch-cortex-m", "executor-thread", "executor-interrupt", "defmt", "integrated-timers"] } embassy-time = { version = "0.3.2", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } embassy-usb = { version = "0.3.0", path = "../../embassy-usb", features = ["defmt" ] } embassy-net = { version = "0.4.0", path = "../../embassy-net", features = ["defmt", "tcp", "dhcpv4", "medium-ethernet", ] } diff --git a/examples/stm32f469/Cargo.toml b/examples/stm32f469/Cargo.toml index 4e1a778cd..139b4b937 100644 --- a/examples/stm32f469/Cargo.toml +++ b/examples/stm32f469/Cargo.toml @@ -7,7 +7,7 @@ license = "MIT OR Apache-2.0" [dependencies] # Specific examples only for stm32f469 embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["defmt", "stm32f469ni", "unstable-pac", "memory-x", "time-driver-any", "exti", "chrono"] } -embassy-executor = { version = "0.6.1", path = "../../embassy-executor", features = ["task-arena-size-32768", "arch-cortex-m", "executor-thread", "executor-interrupt", "defmt", "integrated-timers"] } +embassy-executor = { version = "0.6.2", path = "../../embassy-executor", features = ["task-arena-size-32768", "arch-cortex-m", "executor-thread", "executor-interrupt", "defmt", "integrated-timers"] } embassy-time = { version = "0.3.2", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } defmt = "0.3" diff --git a/examples/stm32f7/Cargo.toml b/examples/stm32f7/Cargo.toml index 879b05d91..2c55da04c 100644 --- a/examples/stm32f7/Cargo.toml +++ b/examples/stm32f7/Cargo.toml @@ -8,7 +8,7 @@ license = "MIT OR Apache-2.0" # Change stm32f777zi to your chip name, if necessary. embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["defmt", "stm32f777zi", "memory-x", "unstable-pac", "time-driver-any", "exti"] } embassy-sync = { version = "0.6.0", path = "../../embassy-sync", features = ["defmt"] } -embassy-executor = { version = "0.6.1", path = "../../embassy-executor", features = ["task-arena-size-32768", "arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] } +embassy-executor = { version = "0.6.2", path = "../../embassy-executor", features = ["task-arena-size-32768", "arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] } embassy-time = { version = "0.3.2", 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"] } embedded-io-async = { version = "0.6.1" } diff --git a/examples/stm32g0/Cargo.toml b/examples/stm32g0/Cargo.toml index 77f70e57d..bf258a7fd 100644 --- a/examples/stm32g0/Cargo.toml +++ b/examples/stm32g0/Cargo.toml @@ -8,7 +8,7 @@ license = "MIT OR Apache-2.0" # Change stm32g0b1re to your chip name, if necessary. embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = [ "defmt", "time-driver-any", "stm32g0b1re", "memory-x", "unstable-pac", "exti"] } embassy-sync = { version = "0.6.0", path = "../../embassy-sync", features = ["defmt"] } -embassy-executor = { version = "0.6.1", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] } +embassy-executor = { version = "0.6.2", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] } embassy-time = { version = "0.3.2", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } embassy-usb = { version = "0.3.0", path = "../../embassy-usb", default-features = false, features = ["defmt"] } embassy-futures = { version = "0.1.0", path = "../../embassy-futures" } diff --git a/examples/stm32g4/Cargo.toml b/examples/stm32g4/Cargo.toml index 86d29f94e..cc3b44b57 100644 --- a/examples/stm32g4/Cargo.toml +++ b/examples/stm32g4/Cargo.toml @@ -8,7 +8,7 @@ license = "MIT OR Apache-2.0" # Change stm32g491re to your chip name, if necessary. embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = [ "defmt", "time-driver-any", "stm32g491re", "memory-x", "unstable-pac", "exti"] } embassy-sync = { version = "0.6.0", path = "../../embassy-sync", features = ["defmt"] } -embassy-executor = { version = "0.6.1", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] } +embassy-executor = { version = "0.6.2", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] } embassy-time = { version = "0.3.2", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } embassy-usb = { version = "0.3.0", path = "../../embassy-usb", features = ["defmt"] } embassy-futures = { version = "0.1.0", path = "../../embassy-futures" } diff --git a/examples/stm32h5/Cargo.toml b/examples/stm32h5/Cargo.toml index 9ca8bc969..d1a865e4c 100644 --- a/examples/stm32h5/Cargo.toml +++ b/examples/stm32h5/Cargo.toml @@ -8,7 +8,7 @@ license = "MIT OR Apache-2.0" # Change stm32h563zi to your chip name, if necessary. embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["defmt", "stm32h563zi", "memory-x", "time-driver-any", "exti", "unstable-pac", "low-power"] } embassy-sync = { version = "0.6.0", path = "../../embassy-sync", features = ["defmt"] } -embassy-executor = { version = "0.6.1", path = "../../embassy-executor", features = ["task-arena-size-32768", "arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] } +embassy-executor = { version = "0.6.2", path = "../../embassy-executor", features = ["task-arena-size-32768", "arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] } embassy-time = { version = "0.3.2", 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"] } embassy-usb = { version = "0.3.0", path = "../../embassy-usb", features = ["defmt"] } diff --git a/examples/stm32h7/Cargo.toml b/examples/stm32h7/Cargo.toml index f2f207395..1ba792f72 100644 --- a/examples/stm32h7/Cargo.toml +++ b/examples/stm32h7/Cargo.toml @@ -9,7 +9,7 @@ license = "MIT OR Apache-2.0" 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.6.0", path = "../../embassy-sync", features = ["defmt"] } embassy-embedded-hal = { version = "0.2.0", path = "../../embassy-embedded-hal" } -embassy-executor = { version = "0.6.1", path = "../../embassy-executor", features = ["task-arena-size-32768", "arch-cortex-m", "executor-thread", "executor-interrupt", "defmt", "integrated-timers"] } +embassy-executor = { version = "0.6.2", path = "../../embassy-executor", features = ["task-arena-size-32768", "arch-cortex-m", "executor-thread", "executor-interrupt", "defmt", "integrated-timers"] } embassy-time = { version = "0.3.2", 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.3.0", path = "../../embassy-usb", features = ["defmt"] } diff --git a/examples/stm32h735/Cargo.toml b/examples/stm32h735/Cargo.toml index bac76ddfb..6a74d83ab 100644 --- a/examples/stm32h735/Cargo.toml +++ b/examples/stm32h735/Cargo.toml @@ -8,7 +8,7 @@ license = "MIT OR Apache-2.0" embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["defmt", "stm32h735ig", "time-driver-tim2", "exti", "memory-x", "unstable-pac", "chrono"] } embassy-sync = { version = "0.6.0", path = "../../embassy-sync", features = ["defmt"] } embassy-embedded-hal = { version = "0.2.0", path = "../../embassy-embedded-hal" } -embassy-executor = { version = "0.6.1", path = "../../embassy-executor", features = ["task-arena-size-32768", "arch-cortex-m", "executor-thread", "executor-interrupt", "defmt", "integrated-timers"] } +embassy-executor = { version = "0.6.2", path = "../../embassy-executor", features = ["task-arena-size-32768", "arch-cortex-m", "executor-thread", "executor-interrupt", "defmt", "integrated-timers"] } embassy-time = { version = "0.3.2", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } embassy-futures = { version = "0.1.0", path = "../../embassy-futures" } diff --git a/examples/stm32h755cm4/Cargo.toml b/examples/stm32h755cm4/Cargo.toml index 1fbceddde..ee96e64d6 100644 --- a/examples/stm32h755cm4/Cargo.toml +++ b/examples/stm32h755cm4/Cargo.toml @@ -9,7 +9,7 @@ license = "MIT OR Apache-2.0" embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["defmt", "stm32h755zi-cm4", "time-driver-tim2", "exti", "memory-x", "unstable-pac", "chrono"] } embassy-sync = { version = "0.6.0", path = "../../embassy-sync", features = ["defmt"] } embassy-embedded-hal = { version = "0.2.0", path = "../../embassy-embedded-hal" } -embassy-executor = { version = "0.6.1", path = "../../embassy-executor", features = ["task-arena-size-32768", "arch-cortex-m", "executor-thread", "executor-interrupt", "defmt", "integrated-timers"] } +embassy-executor = { version = "0.6.2", path = "../../embassy-executor", features = ["task-arena-size-32768", "arch-cortex-m", "executor-thread", "executor-interrupt", "defmt", "integrated-timers"] } embassy-time = { version = "0.3.1", 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.3.0", path = "../../embassy-usb", features = ["defmt"] } diff --git a/examples/stm32h755cm7/Cargo.toml b/examples/stm32h755cm7/Cargo.toml index 8b864169b..61c425719 100644 --- a/examples/stm32h755cm7/Cargo.toml +++ b/examples/stm32h755cm7/Cargo.toml @@ -9,7 +9,7 @@ license = "MIT OR Apache-2.0" embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["defmt", "stm32h755zi-cm7", "time-driver-tim3", "exti", "memory-x", "unstable-pac", "chrono"] } embassy-sync = { version = "0.6.0", path = "../../embassy-sync", features = ["defmt"] } embassy-embedded-hal = { version = "0.2.0", path = "../../embassy-embedded-hal" } -embassy-executor = { version = "0.6.1", path = "../../embassy-executor", features = ["task-arena-size-32768", "arch-cortex-m", "executor-thread", "executor-interrupt", "defmt", "integrated-timers"] } +embassy-executor = { version = "0.6.2", path = "../../embassy-executor", features = ["task-arena-size-32768", "arch-cortex-m", "executor-thread", "executor-interrupt", "defmt", "integrated-timers"] } embassy-time = { version = "0.3.1", 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.3.0", path = "../../embassy-usb", features = ["defmt"] } diff --git a/examples/stm32h7b0/Cargo.toml b/examples/stm32h7b0/Cargo.toml index 02c620443..255ce933c 100644 --- a/examples/stm32h7b0/Cargo.toml +++ b/examples/stm32h7b0/Cargo.toml @@ -8,7 +8,7 @@ license = "MIT OR Apache-2.0" embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["defmt", "stm32h7b0vb", "time-driver-tim2", "exti", "memory-x", "unstable-pac", "chrono"] } embassy-sync = { version = "0.6.0", path = "../../embassy-sync", features = ["defmt"] } embassy-embedded-hal = { version = "0.2.0", path = "../../embassy-embedded-hal" } -embassy-executor = { version = "0.6.1", path = "../../embassy-executor", features = ["task-arena-size-32768", "arch-cortex-m", "executor-thread", "executor-interrupt", "defmt", "integrated-timers"] } +embassy-executor = { version = "0.6.2", path = "../../embassy-executor", features = ["task-arena-size-32768", "arch-cortex-m", "executor-thread", "executor-interrupt", "defmt", "integrated-timers"] } embassy-time = { version = "0.3.2", 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.3.0", path = "../../embassy-usb", features = ["defmt"] } diff --git a/examples/stm32h7rs/Cargo.toml b/examples/stm32h7rs/Cargo.toml index 29881bf8b..055be6a91 100644 --- a/examples/stm32h7rs/Cargo.toml +++ b/examples/stm32h7rs/Cargo.toml @@ -8,7 +8,7 @@ license = "MIT OR Apache-2.0" # Change stm32h743bi to your chip name, if necessary. embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["defmt", "stm32h7s3l8", "time-driver-tim2", "exti", "memory-x", "unstable-pac", "chrono"] } embassy-sync = { version = "0.6.0", path = "../../embassy-sync", features = ["defmt"] } -embassy-executor = { version = "0.6.1", path = "../../embassy-executor", features = ["task-arena-size-32768", "arch-cortex-m", "executor-thread", "executor-interrupt", "defmt", "integrated-timers"] } +embassy-executor = { version = "0.6.2", path = "../../embassy-executor", features = ["task-arena-size-32768", "arch-cortex-m", "executor-thread", "executor-interrupt", "defmt", "integrated-timers"] } embassy-time = { version = "0.3.2", 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.3.0", path = "../../embassy-usb", features = ["defmt"] } diff --git a/examples/stm32l0/Cargo.toml b/examples/stm32l0/Cargo.toml index 273cdce1d..4e2bb8b67 100644 --- a/examples/stm32l0/Cargo.toml +++ b/examples/stm32l0/Cargo.toml @@ -8,7 +8,7 @@ license = "MIT OR Apache-2.0" # Change stm32l072cz to your chip name, if necessary. embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["defmt", "stm32l072cz", "unstable-pac", "time-driver-any", "exti", "memory-x"] } embassy-sync = { version = "0.6.0", path = "../../embassy-sync", features = ["defmt"] } -embassy-executor = { version = "0.6.1", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] } +embassy-executor = { version = "0.6.2", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] } embassy-time = { version = "0.3.2", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } defmt = "0.3" diff --git a/examples/stm32l1/Cargo.toml b/examples/stm32l1/Cargo.toml index af4775a74..865cad87f 100644 --- a/examples/stm32l1/Cargo.toml +++ b/examples/stm32l1/Cargo.toml @@ -6,7 +6,7 @@ license = "MIT OR Apache-2.0" [dependencies] embassy-sync = { version = "0.6.0", path = "../../embassy-sync", features = ["defmt"] } -embassy-executor = { version = "0.6.1", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] } +embassy-executor = { version = "0.6.2", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] } embassy-time = { version = "0.3.2", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = [ "defmt", "stm32l151cb-a", "time-driver-any", "memory-x"] } embassy-usb = { version = "0.3.0", path = "../../embassy-usb", features = ["defmt"] } diff --git a/examples/stm32l4/Cargo.toml b/examples/stm32l4/Cargo.toml index 70c5c5e26..7f963fc53 100644 --- a/examples/stm32l4/Cargo.toml +++ b/examples/stm32l4/Cargo.toml @@ -8,7 +8,7 @@ license = "MIT OR Apache-2.0" # Change stm32l4s5vi to your chip name, if necessary. embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = [ "defmt", "unstable-pac", "stm32l4s5qi", "memory-x", "time-driver-any", "exti", "chrono"] } embassy-sync = { version = "0.6.0", path = "../../embassy-sync", features = ["defmt"] } -embassy-executor = { version = "0.6.1", path = "../../embassy-executor", features = ["task-arena-size-32768", "arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] } +embassy-executor = { version = "0.6.2", path = "../../embassy-executor", features = ["task-arena-size-32768", "arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] } embassy-time = { version = "0.3.2", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768", ] } embassy-embedded-hal = { version = "0.2.0", path = "../../embassy-embedded-hal" } embassy-usb = { version = "0.3.0", path = "../../embassy-usb", features = ["defmt"] } diff --git a/examples/stm32l5/Cargo.toml b/examples/stm32l5/Cargo.toml index 78cb79eac..0604625f2 100644 --- a/examples/stm32l5/Cargo.toml +++ b/examples/stm32l5/Cargo.toml @@ -8,7 +8,7 @@ license = "MIT OR Apache-2.0" # Change stm32l552ze to your chip name, if necessary. embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = [ "defmt", "unstable-pac", "stm32l552ze", "time-driver-any", "exti", "memory-x", "low-power"] } embassy-sync = { version = "0.6.0", path = "../../embassy-sync", features = ["defmt"] } -embassy-executor = { version = "0.6.1", path = "../../embassy-executor", features = ["task-arena-size-32768", "arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] } +embassy-executor = { version = "0.6.2", path = "../../embassy-executor", features = ["task-arena-size-32768", "arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] } embassy-time = { version = "0.3.2", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } embassy-usb = { version = "0.3.0", path = "../../embassy-usb", features = ["defmt"] } embassy-net = { version = "0.4.0", path = "../../embassy-net", features = ["defmt", "tcp", "dhcpv4", "medium-ethernet"] } diff --git a/examples/stm32u0/Cargo.toml b/examples/stm32u0/Cargo.toml index 931409b51..97ef0b704 100644 --- a/examples/stm32u0/Cargo.toml +++ b/examples/stm32u0/Cargo.toml @@ -8,7 +8,7 @@ license = "MIT OR Apache-2.0" # Change stm32u083rc to your chip name, if necessary. embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = [ "defmt", "time-driver-any", "stm32u083rc", "memory-x", "unstable-pac", "exti", "chrono"] } embassy-sync = { version = "0.6.0", path = "../../embassy-sync", features = ["defmt"] } -embassy-executor = { version = "0.6.1", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] } +embassy-executor = { version = "0.6.2", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] } embassy-time = { version = "0.3.2", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } embassy-usb = { version = "0.3.0", path = "../../embassy-usb", default-features = false, features = ["defmt"] } embassy-futures = { version = "0.1.0", path = "../../embassy-futures" } diff --git a/examples/stm32u5/Cargo.toml b/examples/stm32u5/Cargo.toml index 8b576425c..f474e6db0 100644 --- a/examples/stm32u5/Cargo.toml +++ b/examples/stm32u5/Cargo.toml @@ -8,7 +8,7 @@ license = "MIT OR Apache-2.0" # Change stm32u5g9zj to your chip name, if necessary. embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["defmt", "unstable-pac", "stm32u5g9zj", "time-driver-any", "memory-x" ] } embassy-sync = { version = "0.6.0", path = "../../embassy-sync", features = ["defmt"] } -embassy-executor = { version = "0.6.1", path = "../../embassy-executor", features = ["task-arena-size-32768", "arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] } +embassy-executor = { version = "0.6.2", path = "../../embassy-executor", features = ["task-arena-size-32768", "arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] } embassy-time = { version = "0.3.2", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } embassy-usb = { version = "0.3.0", path = "../../embassy-usb", features = ["defmt"] } embassy-futures = { version = "0.1.0", path = "../../embassy-futures" } diff --git a/examples/stm32wb/Cargo.toml b/examples/stm32wb/Cargo.toml index 8cea2ace8..26de81122 100644 --- a/examples/stm32wb/Cargo.toml +++ b/examples/stm32wb/Cargo.toml @@ -9,7 +9,7 @@ license = "MIT OR Apache-2.0" embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = [ "defmt", "stm32wb55rg", "time-driver-any", "memory-x", "exti"] } embassy-stm32-wpan = { version = "0.1.0", path = "../../embassy-stm32-wpan", features = ["defmt", "stm32wb55rg"] } embassy-sync = { version = "0.6.0", path = "../../embassy-sync", features = ["defmt"] } -embassy-executor = { version = "0.6.1", path = "../../embassy-executor", features = ["task-arena-size-32768", "arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] } +embassy-executor = { version = "0.6.2", path = "../../embassy-executor", features = ["task-arena-size-32768", "arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] } embassy-time = { version = "0.3.2", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } embassy-net = { version = "0.4.0", path = "../../embassy-net", features = ["defmt", "udp", "proto-ipv6", "medium-ieee802154", ], optional=true } diff --git a/examples/stm32wba/Cargo.toml b/examples/stm32wba/Cargo.toml index 4261564dd..807f809e0 100644 --- a/examples/stm32wba/Cargo.toml +++ b/examples/stm32wba/Cargo.toml @@ -7,7 +7,7 @@ license = "MIT OR Apache-2.0" [dependencies] embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = [ "defmt", "stm32wba52cg", "time-driver-any", "memory-x", "exti"] } embassy-sync = { version = "0.6.0", path = "../../embassy-sync", features = ["defmt"] } -embassy-executor = { version = "0.6.1", path = "../../embassy-executor", features = ["task-arena-size-32768", "arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] } +embassy-executor = { version = "0.6.2", path = "../../embassy-executor", features = ["task-arena-size-32768", "arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] } embassy-time = { version = "0.3.2", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } embassy-net = { version = "0.4.0", path = "../../embassy-net", features = ["defmt", "udp", "proto-ipv6", "medium-ieee802154", ], optional=true } diff --git a/examples/stm32wl/Cargo.toml b/examples/stm32wl/Cargo.toml index f8d5afb86..3b4223977 100644 --- a/examples/stm32wl/Cargo.toml +++ b/examples/stm32wl/Cargo.toml @@ -8,7 +8,7 @@ license = "MIT OR Apache-2.0" # Change stm32wl55jc-cm4 to your chip name, if necessary. embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["defmt", "stm32wl55jc-cm4", "time-driver-any", "memory-x", "unstable-pac", "exti", "chrono"] } embassy-sync = { version = "0.6.0", path = "../../embassy-sync", features = ["defmt"] } -embassy-executor = { version = "0.6.1", path = "../../embassy-executor", features = ["task-arena-size-4096", "arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] } +embassy-executor = { version = "0.6.2", path = "../../embassy-executor", features = ["task-arena-size-4096", "arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] } embassy-time = { version = "0.3.2", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } embassy-embedded-hal = { version = "0.2.0", path = "../../embassy-embedded-hal" } diff --git a/examples/wasm/Cargo.toml b/examples/wasm/Cargo.toml index f8db71233..ea66d9b99 100644 --- a/examples/wasm/Cargo.toml +++ b/examples/wasm/Cargo.toml @@ -9,7 +9,7 @@ crate-type = ["cdylib"] [dependencies] embassy-sync = { version = "0.6.0", path = "../../embassy-sync", features = ["log"] } -embassy-executor = { version = "0.6.1", path = "../../embassy-executor", features = ["arch-wasm", "executor-thread", "log", "integrated-timers"] } +embassy-executor = { version = "0.6.2", path = "../../embassy-executor", features = ["arch-wasm", "executor-thread", "log", "integrated-timers"] } embassy-time = { version = "0.3.2", path = "../../embassy-time", features = ["log", "wasm", ] } wasm-logger = "0.2.0" diff --git a/tests/nrf/Cargo.toml b/tests/nrf/Cargo.toml index 6a710f29d..c1d386b93 100644 --- a/tests/nrf/Cargo.toml +++ b/tests/nrf/Cargo.toml @@ -9,7 +9,7 @@ teleprobe-meta = "1" embassy-futures = { version = "0.1.0", path = "../../embassy-futures" } embassy-sync = { version = "0.6.0", path = "../../embassy-sync", features = ["defmt", ] } -embassy-executor = { version = "0.6.1", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "defmt", "task-arena-size-16384", "integrated-timers"] } +embassy-executor = { version = "0.6.2", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "defmt", "task-arena-size-16384", "integrated-timers"] } embassy-time = { version = "0.3.2", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime"] } embassy-nrf = { version = "0.2.0", path = "../../embassy-nrf", features = ["defmt", "time-driver-rtc1", "gpiote", "unstable-pac"] } embedded-io-async = { version = "0.6.1", features = ["defmt-03"] } diff --git a/tests/riscv32/Cargo.toml b/tests/riscv32/Cargo.toml index 0ee7f5a00..35dd283eb 100644 --- a/tests/riscv32/Cargo.toml +++ b/tests/riscv32/Cargo.toml @@ -7,7 +7,7 @@ license = "MIT OR Apache-2.0" [dependencies] critical-section = { version = "1.1.1", features = ["restore-state-bool"] } embassy-sync = { version = "0.6.0", path = "../../embassy-sync" } -embassy-executor = { version = "0.6.1", path = "../../embassy-executor", features = ["arch-riscv32", "executor-thread"] } +embassy-executor = { version = "0.6.2", path = "../../embassy-executor", features = ["arch-riscv32", "executor-thread"] } embassy-time = { version = "0.3.2", path = "../../embassy-time" } embassy-futures = { version = "0.1.0", path = "../../embassy-futures" } diff --git a/tests/rp/Cargo.toml b/tests/rp/Cargo.toml index ce812af78..72f7ceb8f 100644 --- a/tests/rp/Cargo.toml +++ b/tests/rp/Cargo.toml @@ -8,7 +8,7 @@ license = "MIT OR Apache-2.0" teleprobe-meta = "1.1" embassy-sync = { version = "0.6.0", path = "../../embassy-sync", features = ["defmt"] } -embassy-executor = { version = "0.6.1", path = "../../embassy-executor", features = ["task-arena-size-32768", "arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] } +embassy-executor = { version = "0.6.2", path = "../../embassy-executor", features = ["task-arena-size-32768", "arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] } embassy-time = { version = "0.3.2", path = "../../embassy-time", features = ["defmt", ] } embassy-rp = { version = "0.2.0", path = "../../embassy-rp", features = [ "defmt", "unstable-pac", "time-driver", "critical-section-impl", "intrinsics", "rom-v2-intrinsics", "run-from-ram", "rp2040"] } embassy-futures = { version = "0.1.0", path = "../../embassy-futures" } diff --git a/tests/stm32/Cargo.toml b/tests/stm32/Cargo.toml index 468d2ed54..f30217fc0 100644 --- a/tests/stm32/Cargo.toml +++ b/tests/stm32/Cargo.toml @@ -60,7 +60,7 @@ cm0 = ["portable-atomic/unsafe-assume-single-core"] teleprobe-meta = "1" embassy-sync = { version = "0.6.0", path = "../../embassy-sync", features = ["defmt"] } -embassy-executor = { version = "0.6.1", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] } +embassy-executor = { version = "0.6.2", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] } embassy-time = { version = "0.3.2", path = "../../embassy-time", features = ["defmt", "tick-hz-131_072", "defmt-timestamp-uptime"] } embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = [ "defmt", "unstable-pac", "memory-x", "time-driver-any"] } embassy-futures = { version = "0.1.0", path = "../../embassy-futures" } From aeb85454e9e8f59f36a434ea25bdeef5d2f330ca Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?D=C3=A1niel=20Buga?= Date: Wed, 6 Nov 2024 14:09:41 +0100 Subject: [PATCH 183/361] Also bump macros --- embassy-executor-macros/Cargo.toml | 2 +- embassy-executor/Cargo.toml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/embassy-executor-macros/Cargo.toml b/embassy-executor-macros/Cargo.toml index 306cf8352..5a38f2f06 100644 --- a/embassy-executor-macros/Cargo.toml +++ b/embassy-executor-macros/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "embassy-executor-macros" -version = "0.6.1" +version = "0.6.2" edition = "2021" license = "MIT OR Apache-2.0" description = "macros for creating the entry point and tasks for embassy-executor" diff --git a/embassy-executor/Cargo.toml b/embassy-executor/Cargo.toml index 6b16a6343..2450ae61b 100644 --- a/embassy-executor/Cargo.toml +++ b/embassy-executor/Cargo.toml @@ -33,7 +33,7 @@ defmt = { version = "0.3", optional = true } log = { version = "0.4.14", optional = true } rtos-trace = { version = "0.1.2", optional = true } -embassy-executor-macros = { version = "0.6.1", path = "../embassy-executor-macros" } +embassy-executor-macros = { version = "0.6.2", path = "../embassy-executor-macros" } embassy-time-driver = { version = "0.1.0", path = "../embassy-time-driver", optional = true } embassy-time-queue-driver = { version = "0.1.0", path = "../embassy-time-queue-driver", optional = true } critical-section = "1.1" From 555231aeb510ed17996c2446ad412aefd4258cb3 Mon Sep 17 00:00:00 2001 From: elagil Date: Wed, 6 Nov 2024 19:46:52 +0100 Subject: [PATCH 184/361] chore: update stm32 data source --- 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 6ab6e162e..e2ba4fd60 100644 --- a/embassy-stm32/Cargo.toml +++ b/embassy-stm32/Cargo.toml @@ -72,7 +72,7 @@ rand_core = "0.6.3" sdio-host = "0.5.0" critical-section = "1.1" #stm32-metapac = { version = "15" } -stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-9b7414490b10ffbd5beb1b0dcf14adb018cbe37f" } +stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-7bb5f235587c3a6886a7be1c8f58fdf22c5257f3" } vcell = "0.1.3" nb = "1.0.0" @@ -101,7 +101,7 @@ proc-macro2 = "1.0.36" quote = "1.0.15" #stm32-metapac = { version = "15", default-features = false, features = ["metadata"]} -stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-9b7414490b10ffbd5beb1b0dcf14adb018cbe37f", default-features = false, features = ["metadata"] } +stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-7bb5f235587c3a6886a7be1c8f58fdf22c5257f3", default-features = false, features = ["metadata"] } [features] default = ["rt"] From e69be0a23bc182a78f2647cc1a739910a1a8e985 Mon Sep 17 00:00:00 2001 From: elagil Date: Wed, 6 Nov 2024 19:46:55 +0100 Subject: [PATCH 185/361] fix: STM32U5 RCC fields --- embassy-stm32/src/rcc/u5.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/embassy-stm32/src/rcc/u5.rs b/embassy-stm32/src/rcc/u5.rs index 28545ca51..1e2bfe62d 100644 --- a/embassy-stm32/src/rcc/u5.rs +++ b/embassy-stm32/src/rcc/u5.rs @@ -279,8 +279,10 @@ pub(crate) unsafe fn init(config: Config) { hsi48: hsi48, rtc: rtc, hse: hse, + hse_div_2: hse.map(|clk| clk / 2u32), hsi: hsi, pll1_p: pll1.p, + pll1_p_div_2: pll1.p.map(|clk| clk / 2u32), pll1_q: pll1.q, pll1_r: pll1.r, pll2_p: pll2.p, From 7231032f977fe4a1a486dfd963f7105b01bf684d Mon Sep 17 00:00:00 2001 From: Christian Enderle Date: Thu, 7 Nov 2024 13:23:39 +0100 Subject: [PATCH 186/361] RCC: added msik for stm32u5 --- embassy-stm32/src/rcc/u5.rs | 42 +++++++++++++++++++++++++++++++------ 1 file changed, 36 insertions(+), 6 deletions(-) diff --git a/embassy-stm32/src/rcc/u5.rs b/embassy-stm32/src/rcc/u5.rs index 1e2bfe62d..bb3d5f320 100644 --- a/embassy-stm32/src/rcc/u5.rs +++ b/embassy-stm32/src/rcc/u5.rs @@ -62,7 +62,8 @@ pub struct Pll { #[derive(Clone, Copy)] pub struct Config { // base clock sources - pub msi: Option, + pub msis: Option, + pub msik: Option, pub hsi: bool, pub hse: Option, pub hsi48: Option, @@ -94,7 +95,8 @@ pub struct Config { impl Default for Config { fn default() -> Self { Self { - msi: Some(Msirange::RANGE_4MHZ), + msis: Some(Msirange::RANGE_4MHZ), + msik: Some(Msirange::RANGE_4MHZ), hse: None, hsi: false, hsi48: Some(Default::default()), @@ -118,7 +120,7 @@ pub(crate) unsafe fn init(config: Config) { PWR.vosr().modify(|w| w.set_vos(config.voltage_range)); while !PWR.vosr().read().vosrdy() {} - let msi = config.msi.map(|range| { + let msis = config.msis.map(|range| { // Check MSI output per RM0456 § 11.4.10 match config.voltage_range { VoltageScale::RANGE4 => { @@ -147,6 +149,34 @@ pub(crate) unsafe fn init(config: Config) { msirange_to_hertz(range) }); + let msik = config.msik.map(|range| { + // Check MSI output per RM0456 § 11.4.10 + match config.voltage_range { + VoltageScale::RANGE4 => { + assert!(msirange_to_hertz(range).0 <= 24_000_000); + } + _ => {} + } + + // RM0456 § 11.8.2: spin until MSIS is off or MSIS is ready before setting its range + loop { + let cr = RCC.cr().read(); + if cr.msikon() == false || cr.msikrdy() == true { + break; + } + } + + RCC.icscr1().modify(|w| { + w.set_msikrange(range); + w.set_msirgsel(Msirgsel::ICSCR1); + }); + RCC.cr().write(|w| { + w.set_msikon(true); + }); + while !RCC.cr().read().msikrdy() {} + msirange_to_hertz(range) + }); + let hsi = config.hsi.then(|| { RCC.cr().write(|w| w.set_hsion(true)); while !RCC.cr().read().hsirdy() {} @@ -181,7 +211,7 @@ pub(crate) unsafe fn init(config: Config) { let hsi48 = config.hsi48.map(super::init_hsi48); - let pll_input = PllInput { hse, hsi, msi }; + let pll_input = PllInput { hse, hsi, msi: msis }; let pll1 = init_pll(PllInstance::Pll1, config.pll1, &pll_input, config.voltage_range); let pll2 = init_pll(PllInstance::Pll2, config.pll2, &pll_input, config.voltage_range); let pll3 = init_pll(PllInstance::Pll3, config.pll3, &pll_input, config.voltage_range); @@ -189,7 +219,7 @@ pub(crate) unsafe fn init(config: Config) { let sys_clk = match config.sys { Sysclk::HSE => hse.unwrap(), Sysclk::HSI => hsi.unwrap(), - Sysclk::MSIS => msi.unwrap(), + Sysclk::MSIS => msis.unwrap(), Sysclk::PLL1_R => pll1.r.unwrap(), }; @@ -276,6 +306,7 @@ pub(crate) unsafe fn init(config: Config) { pclk3: Some(pclk3), pclk1_tim: Some(pclk1_tim), pclk2_tim: Some(pclk2_tim), + msik: msik, hsi48: hsi48, rtc: rtc, hse: hse, @@ -300,7 +331,6 @@ pub(crate) unsafe fn init(config: Config) { hsi48_div_2: None, lse: None, lsi: None, - msik: None, shsi: None, shsi_div_2: None, ); From cf2424f5c21416571ccd695ccea61b447094c9ef Mon Sep 17 00:00:00 2001 From: Christian Enderle Date: Thu, 7 Nov 2024 14:16:10 +0100 Subject: [PATCH 187/361] RCC: add lsi and lse clock frequency for STM32U5 --- embassy-stm32/src/rcc/u5.rs | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/embassy-stm32/src/rcc/u5.rs b/embassy-stm32/src/rcc/u5.rs index bb3d5f320..af99c77bc 100644 --- a/embassy-stm32/src/rcc/u5.rs +++ b/embassy-stm32/src/rcc/u5.rs @@ -5,6 +5,7 @@ pub use crate::pac::rcc::vals::{ }; use crate::pac::rcc::vals::{Hseext, Msirgsel, Pllmboost, Pllrge}; use crate::pac::{FLASH, PWR, RCC}; +use crate::rcc::LSI_FREQ; use crate::time::Hertz; /// HSI speed @@ -294,6 +295,9 @@ pub(crate) unsafe fn init(config: Config) { let rtc = config.ls.init(); + let lse = config.ls.lse.map(|l| l.frequency); + let lsi = config.ls.lsi.then_some(LSI_FREQ); + config.mux.init(); set_clocks!( @@ -309,6 +313,8 @@ pub(crate) unsafe fn init(config: Config) { msik: msik, hsi48: hsi48, rtc: rtc, + lse: lse, + lsi: lsi, hse: hse, hse_div_2: hse.map(|clk| clk / 2u32), hsi: hsi, @@ -329,8 +335,6 @@ pub(crate) unsafe fn init(config: Config) { // TODO audioclk: None, hsi48_div_2: None, - lse: None, - lsi: None, shsi: None, shsi_div_2: None, ); From 0de204ccd7fea064f1b2ad5f0830a9b8ff0a09a9 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Fri, 8 Nov 2024 13:20:13 +0100 Subject: [PATCH 188/361] Fix "non-local impl definition" warning from recent nightlies. --- embassy-nrf/src/lib.rs | 59 +++++++++++++++++++++++----------------- embassy-rp/src/lib.rs | 15 ++++++++-- embassy-stm32/src/lib.rs | 11 +++++++- 3 files changed, 56 insertions(+), 29 deletions(-) diff --git a/embassy-nrf/src/lib.rs b/embassy-nrf/src/lib.rs index 03d3ca5f7..430b6fae7 100644 --- a/embassy-nrf/src/lib.rs +++ b/embassy-nrf/src/lib.rs @@ -177,34 +177,43 @@ mod chip; // developer note: this macro can't be in `embassy-hal-internal` due to the use of `$crate`. #[macro_export] macro_rules! bind_interrupts { - ($vis:vis struct $name:ident { - $( - $(#[cfg($cond_irq:meta)])? - $irq:ident => $( - $(#[cfg($cond_handler:meta)])? - $handler:ty - ),*; - )* - }) => { - #[derive(Copy, Clone)] - $vis struct $name; + ($vis:vis struct $name:ident { + $( + $(#[cfg($cond_irq:meta)])? + $irq:ident => $( + $(#[cfg($cond_handler:meta)])? + $handler:ty + ),*; + )* + }) => { + #[derive(Copy, Clone)] + $vis struct $name; - $( - #[allow(non_snake_case)] - #[no_mangle] - $(#[cfg($cond_irq)])? - unsafe extern "C" fn $irq() { - $( - $(#[cfg($cond_handler)])? - <$handler as $crate::interrupt::typelevel::Handler<$crate::interrupt::typelevel::$irq>>::on_interrupt(); + $( + #[allow(non_snake_case)] + #[no_mangle] + $(#[cfg($cond_irq)])? + unsafe extern "C" fn $irq() { + $( + $(#[cfg($cond_handler)])? + <$handler as $crate::interrupt::typelevel::Handler<$crate::interrupt::typelevel::$irq>>::on_interrupt(); - $(#[cfg($cond_handler)])? - unsafe impl $crate::interrupt::typelevel::Binding<$crate::interrupt::typelevel::$irq, $handler> for $name {} - )* - } - )* - }; + )* + } + + $(#[cfg($cond_irq)])? + $crate::bind_interrupts!(@inner + $( + $(#[cfg($cond_handler)])? + unsafe impl $crate::interrupt::typelevel::Binding<$crate::interrupt::typelevel::$irq, $handler> for $name {} + )* + ); + )* + }; + (@inner $($t:tt)*) => { + $($t)* } +} // Reexports diff --git a/embassy-rp/src/lib.rs b/embassy-rp/src/lib.rs index 51f6ed7f8..f0893b5a0 100644 --- a/embassy-rp/src/lib.rs +++ b/embassy-rp/src/lib.rs @@ -175,8 +175,8 @@ macro_rules! bind_interrupts { ),*; )* }) => { - #[derive(Copy, Clone)] - $vis struct $name; + #[derive(Copy, Clone)] + $vis struct $name; $( #[allow(non_snake_case)] @@ -187,12 +187,21 @@ macro_rules! bind_interrupts { $(#[cfg($cond_handler)])? <$handler as $crate::interrupt::typelevel::Handler<$crate::interrupt::typelevel::$irq>>::on_interrupt(); + )* + } + + $(#[cfg($cond_irq)])? + $crate::bind_interrupts!(@inner + $( $(#[cfg($cond_handler)])? unsafe impl $crate::interrupt::typelevel::Binding<$crate::interrupt::typelevel::$irq, $handler> for $name {} )* - } + ); )* }; + (@inner $($t:tt)*) => { + $($t)* + } } #[cfg(feature = "rp2040")] diff --git a/embassy-stm32/src/lib.rs b/embassy-stm32/src/lib.rs index 1e6185bc1..286a18da2 100644 --- a/embassy-stm32/src/lib.rs +++ b/embassy-stm32/src/lib.rs @@ -186,12 +186,21 @@ macro_rules! bind_interrupts { $(#[cfg($cond_handler)])? <$handler as $crate::interrupt::typelevel::Handler<$crate::interrupt::typelevel::$irq>>::on_interrupt(); + )* + } + + $(#[cfg($cond_irq)])? + $crate::bind_interrupts!(@inner + $( $(#[cfg($cond_handler)])? unsafe impl $crate::interrupt::typelevel::Binding<$crate::interrupt::typelevel::$irq, $handler> for $name {} )* - } + ); )* }; + (@inner $($t:tt)*) => { + $($t)* + } } // Reexports From af5e23ade0f0cb9ec945fded3005d11f8bc157d3 Mon Sep 17 00:00:00 2001 From: Giona Imperatori Date: Sat, 9 Nov 2024 21:50:20 +0100 Subject: [PATCH 189/361] fix(nrf5340-app): added SPIM4 peripheral --- embassy-nrf/src/chips/nrf5340_app.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/embassy-nrf/src/chips/nrf5340_app.rs b/embassy-nrf/src/chips/nrf5340_app.rs index 43588eef3..d7846b094 100644 --- a/embassy-nrf/src/chips/nrf5340_app.rs +++ b/embassy-nrf/src/chips/nrf5340_app.rs @@ -252,6 +252,7 @@ embassy_hal_internal::peripherals! { SERIAL1, SERIAL2, SERIAL3, + SPIM4, // SAADC SAADC, @@ -402,6 +403,7 @@ impl_spim!(SERIAL0, SPIM0, SERIAL0); impl_spim!(SERIAL1, SPIM1, SERIAL1); impl_spim!(SERIAL2, SPIM2, SERIAL2); impl_spim!(SERIAL3, SPIM3, SERIAL3); +impl_spim!(SPIM4, SPIM4, SPIM4); impl_spis!(SERIAL0, SPIS0, SERIAL0); impl_spis!(SERIAL1, SPIS1, SERIAL1); From f9a1511de4d3972da0fd4f7367043a65c673c593 Mon Sep 17 00:00:00 2001 From: Bronson Date: Sun, 10 Nov 2024 12:50:11 +1030 Subject: [PATCH 190/361] add default data to watch new() --- embassy-sync/src/watch.rs | 32 ++++++++++++++++---------------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/embassy-sync/src/watch.rs b/embassy-sync/src/watch.rs index 59798d04f..e0f5e14f9 100644 --- a/embassy-sync/src/watch.rs +++ b/embassy-sync/src/watch.rs @@ -31,7 +31,7 @@ use crate::waitqueue::MultiWakerRegistration; /// /// let f = async { /// -/// static WATCH: Watch = Watch::new(); +/// static WATCH: Watch = Watch::new(None); /// /// // Obtain receivers and sender /// let mut rcv0 = WATCH.receiver().unwrap(); @@ -299,10 +299,10 @@ impl WatchBehavior for Watch impl Watch { /// Create a new `Watch` channel. - pub const fn new() -> Self { + pub const fn new(data: Option) -> Self { Self { mutex: Mutex::new(RefCell::new(WatchState { - data: None, + data, current_id: 0, wakers: MultiWakerRegistration::new(), receiver_count: 0, @@ -775,7 +775,7 @@ mod tests { #[test] fn multiple_sends() { let f = async { - static WATCH: Watch = Watch::new(); + static WATCH: Watch = Watch::new(None); // Obtain receiver and sender let mut rcv = WATCH.receiver().unwrap(); @@ -801,7 +801,7 @@ mod tests { #[test] fn all_try_get() { let f = async { - static WATCH: Watch = Watch::new(); + static WATCH: Watch = Watch::new(None); // Obtain receiver and sender let mut rcv = WATCH.receiver().unwrap(); @@ -835,7 +835,7 @@ mod tests { static CONFIG0: u8 = 10; static CONFIG1: u8 = 20; - static WATCH: Watch = Watch::new(); + static WATCH: Watch = Watch::new(None); // Obtain receiver and sender let mut rcv = WATCH.receiver().unwrap(); @@ -867,7 +867,7 @@ mod tests { #[test] fn sender_modify() { let f = async { - static WATCH: Watch = Watch::new(); + static WATCH: Watch = Watch::new(None); // Obtain receiver and sender let mut rcv = WATCH.receiver().unwrap(); @@ -894,7 +894,7 @@ mod tests { #[test] fn predicate_fn() { let f = async { - static WATCH: Watch = Watch::new(); + static WATCH: Watch = Watch::new(None); // Obtain receiver and sender let mut rcv = WATCH.receiver().unwrap(); @@ -923,7 +923,7 @@ mod tests { #[test] fn receive_after_create() { let f = async { - static WATCH: Watch = Watch::new(); + static WATCH: Watch = Watch::new(None); // Obtain sender and send value let snd = WATCH.sender(); @@ -939,7 +939,7 @@ mod tests { #[test] fn max_receivers_drop() { let f = async { - static WATCH: Watch = Watch::new(); + static WATCH: Watch = Watch::new(None); // Try to create 3 receivers (only 2 can exist at once) let rcv0 = WATCH.receiver(); @@ -964,7 +964,7 @@ mod tests { #[test] fn multiple_receivers() { let f = async { - static WATCH: Watch = Watch::new(); + static WATCH: Watch = Watch::new(None); // Obtain receivers and sender let mut rcv0 = WATCH.receiver().unwrap(); @@ -989,7 +989,7 @@ mod tests { fn clone_senders() { let f = async { // Obtain different ways to send - static WATCH: Watch = Watch::new(); + static WATCH: Watch = Watch::new(None); let snd0 = WATCH.sender(); let snd1 = snd0.clone(); @@ -1010,7 +1010,7 @@ mod tests { #[test] fn use_dynamics() { let f = async { - static WATCH: Watch = Watch::new(); + static WATCH: Watch = Watch::new(None); // Obtain receiver and sender let mut anon_rcv = WATCH.dyn_anon_receiver(); @@ -1031,7 +1031,7 @@ mod tests { #[test] fn convert_to_dyn() { let f = async { - static WATCH: Watch = Watch::new(); + static WATCH: Watch = Watch::new(None); // Obtain receiver and sender let anon_rcv = WATCH.anon_receiver(); @@ -1057,7 +1057,7 @@ mod tests { #[test] fn dynamic_receiver_count() { let f = async { - static WATCH: Watch = Watch::new(); + static WATCH: Watch = Watch::new(None); // Obtain receiver and sender let rcv0 = WATCH.receiver(); @@ -1087,7 +1087,7 @@ mod tests { #[test] fn contains_value() { let f = async { - static WATCH: Watch = Watch::new(); + static WATCH: Watch = Watch::new(None); // Obtain receiver and sender let rcv = WATCH.receiver().unwrap(); From 8d0882991ee88ce4af52d32bbaebecf40fa57914 Mon Sep 17 00:00:00 2001 From: Bronson Date: Sun, 10 Nov 2024 12:54:37 +1030 Subject: [PATCH 191/361] added watch new_with() --- embassy-sync/src/watch.rs | 42 +++++++++++++++++++++++++-------------- 1 file changed, 27 insertions(+), 15 deletions(-) diff --git a/embassy-sync/src/watch.rs b/embassy-sync/src/watch.rs index e0f5e14f9..90dcf97ef 100644 --- a/embassy-sync/src/watch.rs +++ b/embassy-sync/src/watch.rs @@ -31,7 +31,7 @@ use crate::waitqueue::MultiWakerRegistration; /// /// let f = async { /// -/// static WATCH: Watch = Watch::new(None); +/// static WATCH: Watch = Watch::new(); /// /// // Obtain receivers and sender /// let mut rcv0 = WATCH.receiver().unwrap(); @@ -299,7 +299,19 @@ impl WatchBehavior for Watch impl Watch { /// Create a new `Watch` channel. - pub const fn new(data: Option) -> Self { + pub const fn new() -> Self { + Self { + mutex: Mutex::new(RefCell::new(WatchState { + data: None, + current_id: 0, + wakers: MultiWakerRegistration::new(), + receiver_count: 0, + })), + } +} + + /// Create a new `Watch` channel. + pub const fn new_with(data: Option) -> Self { Self { mutex: Mutex::new(RefCell::new(WatchState { data, @@ -775,7 +787,7 @@ mod tests { #[test] fn multiple_sends() { let f = async { - static WATCH: Watch = Watch::new(None); + static WATCH: Watch = Watch::new(); // Obtain receiver and sender let mut rcv = WATCH.receiver().unwrap(); @@ -801,7 +813,7 @@ mod tests { #[test] fn all_try_get() { let f = async { - static WATCH: Watch = Watch::new(None); + static WATCH: Watch = Watch::new(); // Obtain receiver and sender let mut rcv = WATCH.receiver().unwrap(); @@ -835,7 +847,7 @@ mod tests { static CONFIG0: u8 = 10; static CONFIG1: u8 = 20; - static WATCH: Watch = Watch::new(None); + static WATCH: Watch = Watch::new(); // Obtain receiver and sender let mut rcv = WATCH.receiver().unwrap(); @@ -867,7 +879,7 @@ mod tests { #[test] fn sender_modify() { let f = async { - static WATCH: Watch = Watch::new(None); + static WATCH: Watch = Watch::new(); // Obtain receiver and sender let mut rcv = WATCH.receiver().unwrap(); @@ -894,7 +906,7 @@ mod tests { #[test] fn predicate_fn() { let f = async { - static WATCH: Watch = Watch::new(None); + static WATCH: Watch = Watch::new(); // Obtain receiver and sender let mut rcv = WATCH.receiver().unwrap(); @@ -923,7 +935,7 @@ mod tests { #[test] fn receive_after_create() { let f = async { - static WATCH: Watch = Watch::new(None); + static WATCH: Watch = Watch::new(); // Obtain sender and send value let snd = WATCH.sender(); @@ -939,7 +951,7 @@ mod tests { #[test] fn max_receivers_drop() { let f = async { - static WATCH: Watch = Watch::new(None); + static WATCH: Watch = Watch::new(); // Try to create 3 receivers (only 2 can exist at once) let rcv0 = WATCH.receiver(); @@ -964,7 +976,7 @@ mod tests { #[test] fn multiple_receivers() { let f = async { - static WATCH: Watch = Watch::new(None); + static WATCH: Watch = Watch::new(); // Obtain receivers and sender let mut rcv0 = WATCH.receiver().unwrap(); @@ -989,7 +1001,7 @@ mod tests { fn clone_senders() { let f = async { // Obtain different ways to send - static WATCH: Watch = Watch::new(None); + static WATCH: Watch = Watch::new(); let snd0 = WATCH.sender(); let snd1 = snd0.clone(); @@ -1010,7 +1022,7 @@ mod tests { #[test] fn use_dynamics() { let f = async { - static WATCH: Watch = Watch::new(None); + static WATCH: Watch = Watch::new(); // Obtain receiver and sender let mut anon_rcv = WATCH.dyn_anon_receiver(); @@ -1031,7 +1043,7 @@ mod tests { #[test] fn convert_to_dyn() { let f = async { - static WATCH: Watch = Watch::new(None); + static WATCH: Watch = Watch::new(); // Obtain receiver and sender let anon_rcv = WATCH.anon_receiver(); @@ -1057,7 +1069,7 @@ mod tests { #[test] fn dynamic_receiver_count() { let f = async { - static WATCH: Watch = Watch::new(None); + static WATCH: Watch = Watch::new(); // Obtain receiver and sender let rcv0 = WATCH.receiver(); @@ -1087,7 +1099,7 @@ mod tests { #[test] fn contains_value() { let f = async { - static WATCH: Watch = Watch::new(None); + static WATCH: Watch = Watch::new(); // Obtain receiver and sender let rcv = WATCH.receiver().unwrap(); From 32f0cde1cc6f277fb3f91d9fc0cc754c09267376 Mon Sep 17 00:00:00 2001 From: Bronson Date: Sun, 10 Nov 2024 13:15:41 +1030 Subject: [PATCH 192/361] fix formatting --- embassy-sync/src/watch.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/embassy-sync/src/watch.rs b/embassy-sync/src/watch.rs index 90dcf97ef..d645ffe17 100644 --- a/embassy-sync/src/watch.rs +++ b/embassy-sync/src/watch.rs @@ -308,7 +308,7 @@ impl Watch { receiver_count: 0, })), } -} + } /// Create a new `Watch` channel. pub const fn new_with(data: Option) -> Self { From 730dde9ba901d89040d3d943cf36b4217ac52bf9 Mon Sep 17 00:00:00 2001 From: Bronson Date: Sun, 10 Nov 2024 21:09:24 +1030 Subject: [PATCH 193/361] remove option --- embassy-sync/src/watch.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/embassy-sync/src/watch.rs b/embassy-sync/src/watch.rs index d645ffe17..404e31714 100644 --- a/embassy-sync/src/watch.rs +++ b/embassy-sync/src/watch.rs @@ -310,11 +310,11 @@ impl Watch { } } - /// Create a new `Watch` channel. - pub const fn new_with(data: Option) -> Self { + /// Create a new `Watch` channel with default data. + pub const fn new_with(data: T) -> Self { Self { mutex: Mutex::new(RefCell::new(WatchState { - data, + data: Some(data), current_id: 0, wakers: MultiWakerRegistration::new(), receiver_count: 0, From 32dcff39959bda11a7a3ef35fb3c77e172d46ae4 Mon Sep 17 00:00:00 2001 From: rafael Date: Sun, 10 Nov 2024 17:20:59 +0100 Subject: [PATCH 194/361] add simple-robot to Embassy in the wild --- docs/pages/embassy_in_the_wild.adoc | 2 ++ 1 file changed, 2 insertions(+) diff --git a/docs/pages/embassy_in_the_wild.adoc b/docs/pages/embassy_in_the_wild.adoc index bb457a869..620794c31 100644 --- a/docs/pages/embassy_in_the_wild.adoc +++ b/docs/pages/embassy_in_the_wild.adoc @@ -2,6 +2,8 @@ Here are known examples of real-world projects which make use of Embassy. Feel free to link:https://github.com/embassy-rs/embassy/blob/main/docs/pages/embassy_in_the_wild.adoc[add more]! +* link:https://github.com/1-rafael-1/simple-robot[A simple tracked robot based on Raspberry Pi Pico 2] +** A hobbyist project building a tracked robot with basic autonomous and manual drive mode. * link:https://github.com/1-rafael-1/pi-pico-alarmclock-rust[A Raspberry Pi Pico W Alarmclock] ** A hobbyist project building an alarm clock around a Pi Pico W complete with code, components list and enclosure design files. * link:https://github.com/haobogu/rmk/[RMK: A feature-rich Rust keyboard firmware] From 4d75c4ee51266bd40b2018004425a91954bf7721 Mon Sep 17 00:00:00 2001 From: Junfeng Liu Date: Tue, 12 Nov 2024 10:36:44 +0800 Subject: [PATCH 195/361] Fix wrong unit --- embassy-stm32/src/rcc/h.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/embassy-stm32/src/rcc/h.rs b/embassy-stm32/src/rcc/h.rs index 55fe8ca9d..df2929ba4 100644 --- a/embassy-stm32/src/rcc/h.rs +++ b/embassy-stm32/src/rcc/h.rs @@ -779,7 +779,7 @@ fn init_pll(num: usize, config: Option, input: &PllInput) -> PllOutput { ..=3_999_999 => Pllrge::RANGE2, ..=7_999_999 => Pllrge::RANGE4, ..=16_000_000 => Pllrge::RANGE8, - x => panic!("pll ref_clk out of range: {} mhz", x), + x => panic!("pll ref_clk out of range: {} hz", x), }; // The smaller range (150 to 420 MHz) must From baeb59b5b8d63ef9bb6ecada518ea8b911d2dc30 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Tue, 12 Nov 2024 16:28:26 +0100 Subject: [PATCH 196/361] executor: use WakerHack unconditionally even if `nightly` feature is enabled. (#3528) This ensures the executor compiles with all recent nightly versions, including the stable-but-with-nightly-features-enabled xtensa rustc. --- embassy-executor/Cargo.toml | 3 --- embassy-executor/build.rs | 11 -------- embassy-executor/build_common.rs | 19 -------------- embassy-executor/src/lib.rs | 1 - embassy-executor/src/raw/waker.rs | 42 ++++++++----------------------- 5 files changed, 11 insertions(+), 65 deletions(-) diff --git a/embassy-executor/Cargo.toml b/embassy-executor/Cargo.toml index 2450ae61b..7141fe0f9 100644 --- a/embassy-executor/Cargo.toml +++ b/embassy-executor/Cargo.toml @@ -57,9 +57,6 @@ avr-device = { version = "0.5.3", optional = true } critical-section = { version = "1.1", features = ["std"] } trybuild = "1.0" -[build-dependencies] -rustc_version = "0.4.1" - [features] ## Enable nightly-only features diff --git a/embassy-executor/build.rs b/embassy-executor/build.rs index c4c86e1e2..8a41d7503 100644 --- a/embassy-executor/build.rs +++ b/embassy-executor/build.rs @@ -96,15 +96,4 @@ fn main() { let mut rustc_cfgs = common::CfgSet::new(); common::set_target_cfgs(&mut rustc_cfgs); - - // Waker API changed on 2024-09-06 - rustc_cfgs.declare("at_least_2024_09_06"); - let Some(compiler) = common::compiler_info() else { - return; - }; - if compiler.channel == rustc_version::Channel::Nightly - && compiler.commit_date.map(|d| d >= "2024-09-06").unwrap_or(false) - { - rustc_cfgs.enable("at_least_2024_09_06"); - } } diff --git a/embassy-executor/build_common.rs b/embassy-executor/build_common.rs index af6bb0618..b15a8369f 100644 --- a/embassy-executor/build_common.rs +++ b/embassy-executor/build_common.rs @@ -124,22 +124,3 @@ impl PartialOrd<&str> for CompilerDate { Self::parse(other).map(|other| self.cmp(&other)) } } - -pub struct CompilerInfo { - #[allow(unused)] - pub version: rustc_version::Version, - pub channel: rustc_version::Channel, - pub commit_date: Option, -} - -pub fn compiler_info() -> Option { - let Ok(meta) = rustc_version::version_meta() else { - return None; - }; - - Some(CompilerInfo { - version: meta.semver, - channel: meta.channel, - commit_date: meta.commit_date.as_deref().and_then(CompilerDate::parse), - }) -} diff --git a/embassy-executor/src/lib.rs b/embassy-executor/src/lib.rs index 8e07a8b18..d816539ac 100644 --- a/embassy-executor/src/lib.rs +++ b/embassy-executor/src/lib.rs @@ -1,5 +1,4 @@ #![cfg_attr(not(any(feature = "arch-std", feature = "arch-wasm")), no_std)] -#![cfg_attr(all(feature = "nightly", not(at_least_2024_09_06)), feature(waker_getters))] #![allow(clippy::new_without_default)] #![doc = include_str!("../README.md")] #![warn(missing_docs)] diff --git a/embassy-executor/src/raw/waker.rs b/embassy-executor/src/raw/waker.rs index 30b8cdd4c..d2256adfa 100644 --- a/embassy-executor/src/raw/waker.rs +++ b/embassy-executor/src/raw/waker.rs @@ -32,40 +32,20 @@ pub(crate) unsafe fn from_task(p: TaskRef) -> Waker { /// /// Panics if the waker is not created by the Embassy executor. pub fn task_from_waker(waker: &Waker) -> TaskRef { - let (vtable, data) = { - #[cfg(not(feature = "nightly"))] - { - struct WakerHack { - data: *const (), - vtable: &'static RawWakerVTable, - } + struct WakerHack { + data: *const (), + vtable: &'static RawWakerVTable, + } - // safety: OK because WakerHack has the same layout as Waker. - // This is not really guaranteed because the structs are `repr(Rust)`, it is - // indeed the case in the current implementation. - // TODO use waker_getters when stable. https://github.com/rust-lang/rust/issues/96992 - let hack: &WakerHack = unsafe { core::mem::transmute(waker) }; - (hack.vtable, hack.data) - } + // safety: OK because WakerHack has the same layout as Waker. + // This is not really guaranteed because the structs are `repr(Rust)`, it is + // indeed the case in the current implementation. + // TODO use waker_getters when stable. https://github.com/rust-lang/rust/issues/96992 + let hack: &WakerHack = unsafe { core::mem::transmute(waker) }; - #[cfg(feature = "nightly")] - { - #[cfg(not(at_least_2024_09_06))] - { - let raw_waker = waker.as_raw(); - (raw_waker.vtable(), raw_waker.data()) - } - - #[cfg(at_least_2024_09_06)] - { - (waker.vtable(), waker.data()) - } - } - }; - - if vtable != &VTABLE { + if hack.vtable != &VTABLE { panic!("Found waker not created by the Embassy executor. `embassy_time::Timer` only works with the Embassy executor.") } // safety: our wakers are always created with `TaskRef::as_ptr` - unsafe { TaskRef::from_ptr(data as *const TaskHeader) } + unsafe { TaskRef::from_ptr(hack.data as *const TaskHeader) } } From 853c5c567add8134b8419cf0a6a2b6c8cb0b0aa6 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Tue, 12 Nov 2024 16:30:46 +0100 Subject: [PATCH 197/361] executor: compare vtable addr instead of contents. Saves a whopping 44 bytes of text, yay. --- embassy-executor/src/raw/waker.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/embassy-executor/src/raw/waker.rs b/embassy-executor/src/raw/waker.rs index d2256adfa..9c70f995a 100644 --- a/embassy-executor/src/raw/waker.rs +++ b/embassy-executor/src/raw/waker.rs @@ -43,7 +43,9 @@ pub fn task_from_waker(waker: &Waker) -> TaskRef { // TODO use waker_getters when stable. https://github.com/rust-lang/rust/issues/96992 let hack: &WakerHack = unsafe { core::mem::transmute(waker) }; - if hack.vtable != &VTABLE { + // make sure to compare vtable addresses. Doing `==` on the references + // will compare the contents, which is slower. + if hack.vtable as *const _ != &VTABLE as *const _ { panic!("Found waker not created by the Embassy executor. `embassy_time::Timer` only works with the Embassy executor.") } // safety: our wakers are always created with `TaskRef::as_ptr` From 95b84494bc2f6a137e7dfe349971b4386ec66411 Mon Sep 17 00:00:00 2001 From: Scott Mabin Date: Tue, 12 Nov 2024 14:09:24 +0000 Subject: [PATCH 198/361] basic xtensa CI --- .github/ci/build-xtensa.sh | 37 ++++++++++++++++++++++++++++++++++++ ci-xtensa.sh | 39 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 76 insertions(+) create mode 100755 .github/ci/build-xtensa.sh create mode 100755 ci-xtensa.sh diff --git a/.github/ci/build-xtensa.sh b/.github/ci/build-xtensa.sh new file mode 100755 index 000000000..a13391c82 --- /dev/null +++ b/.github/ci/build-xtensa.sh @@ -0,0 +1,37 @@ +#!/bin/bash +## on push branch~=gh-readonly-queue/main/.* +## on pull_request + +set -euo pipefail + +export RUSTUP_HOME=/ci/cache/rustup +export CARGO_HOME=/ci/cache/cargo +export CARGO_TARGET_DIR=/ci/cache/target + +# needed for "dumb HTTP" transport support +# used when pointing stm32-metapac to a CI-built one. +export CARGO_NET_GIT_FETCH_WITH_CLI=true + +cargo install espup +/ci/cache/cargo/bin/espup install + +# Restore lockfiles +if [ -f /ci/cache/lockfiles.tar ]; then + echo Restoring lockfiles... + tar xf /ci/cache/lockfiles.tar +fi + +hashtime restore /ci/cache/filetime.json || true +hashtime save /ci/cache/filetime.json + +mkdir .cargo +cat > .cargo/config.toml<< EOF +[unstable] +build-std = ["alloc", "core"] +EOF + +./ci-xtensa.sh + +# Save lockfiles +echo Saving lockfiles... +find . -type f -name Cargo.lock -exec tar -cf /ci/cache/lockfiles.tar '{}' \+ diff --git a/ci-xtensa.sh b/ci-xtensa.sh new file mode 100755 index 000000000..32d362def --- /dev/null +++ b/ci-xtensa.sh @@ -0,0 +1,39 @@ +#!/bin/bash + +set -eo pipefail + +export RUSTFLAGS=-Dwarnings +export DEFMT_LOG=trace,embassy_hal_internal=debug,embassy_net_esp_hosted=debug,cyw43=info,cyw43_pio=info,smoltcp=info +export RUSTUP_TOOLCHAIN=esp +if [[ -z "${CARGO_TARGET_DIR}" ]]; then + export CARGO_TARGET_DIR=target_ci +fi + +cargo batch \ + --- build --release --manifest-path embassy-executor/Cargo.toml --target xtensa-esp32-none-elf \ + --- build --release --manifest-path embassy-executor/Cargo.toml --target xtensa-esp32-none-elf --features log \ + --- build --release --manifest-path embassy-executor/Cargo.toml --target xtensa-esp32-none-elf --features defmt \ + --- build --release --manifest-path embassy-executor/Cargo.toml --target xtensa-esp32s2-none-elf --features defmt \ + --- build --release --manifest-path embassy-executor/Cargo.toml --target xtensa-esp32-none-elf --features defmt,arch-spin,executor-thread,integrated-timers \ + --- build --release --manifest-path embassy-executor/Cargo.toml --target xtensa-esp32s2-none-elf --features defmt,arch-spin,executor-thread,integrated-timers \ + --- build --release --manifest-path embassy-executor/Cargo.toml --target xtensa-esp32s3-none-elf --features defmt,arch-spin,executor-thread,integrated-timers \ + --- build --release --manifest-path embassy-executor/Cargo.toml --target xtensa-esp32-none-elf --features arch-spin \ + --- build --release --manifest-path embassy-executor/Cargo.toml --target xtensa-esp32-none-elf --features arch-spin,integrated-timers \ + --- build --release --manifest-path embassy-executor/Cargo.toml --target xtensa-esp32-none-elf --features arch-spin,rtos-trace \ + --- build --release --manifest-path embassy-executor/Cargo.toml --target xtensa-esp32-none-elf --features arch-spin,integrated-timers,rtos-trace \ + --- build --release --manifest-path embassy-executor/Cargo.toml --target xtensa-esp32-none-elf --features arch-spin,executor-thread \ + --- build --release --manifest-path embassy-executor/Cargo.toml --target xtensa-esp32-none-elf --features arch-spin,executor-thread,integrated-timers \ + --- build --release --manifest-path embassy-sync/Cargo.toml --target xtensa-esp32s2-none-elf --features defmt \ + --- build --release --manifest-path embassy-time/Cargo.toml --target xtensa-esp32s2-none-elf --features defmt,defmt-timestamp-uptime,generic-queue-8,mock-driver \ + --- build --release --manifest-path embassy-net/Cargo.toml --target xtensa-esp32-none-elf --features defmt,tcp,udp,dns,proto-ipv4,medium-ethernet,packet-trace \ + --- build --release --manifest-path embassy-net/Cargo.toml --target xtensa-esp32-none-elf --features defmt,tcp,udp,dns,proto-ipv4,multicast,medium-ethernet \ + --- build --release --manifest-path embassy-net/Cargo.toml --target xtensa-esp32-none-elf --features defmt,tcp,udp,dns,dhcpv4,medium-ethernet \ + --- build --release --manifest-path embassy-net/Cargo.toml --target xtensa-esp32-none-elf --features defmt,tcp,udp,dns,dhcpv4,medium-ethernet,dhcpv4-hostname \ + --- build --release --manifest-path embassy-net/Cargo.toml --target xtensa-esp32-none-elf --features defmt,tcp,udp,dns,proto-ipv6,medium-ethernet \ + --- build --release --manifest-path embassy-net/Cargo.toml --target xtensa-esp32-none-elf --features defmt,tcp,udp,dns,proto-ipv6,medium-ieee802154 \ + --- build --release --manifest-path embassy-net/Cargo.toml --target xtensa-esp32-none-elf --features defmt,tcp,udp,dns,proto-ipv6,medium-ethernet,medium-ieee802154 \ + --- build --release --manifest-path embassy-net/Cargo.toml --target xtensa-esp32-none-elf --features defmt,tcp,udp,dns,proto-ipv6,medium-ethernet \ + --- build --release --manifest-path embassy-net/Cargo.toml --target xtensa-esp32-none-elf --features defmt,tcp,udp,dns,proto-ipv4,proto-ipv6,medium-ethernet \ + --- build --release --manifest-path embassy-net/Cargo.toml --target xtensa-esp32-none-elf --features defmt,tcp,udp,dns,proto-ipv4,proto-ipv6,medium-ip \ + --- build --release --manifest-path embassy-net/Cargo.toml --target xtensa-esp32-none-elf --features defmt,tcp,udp,dns,proto-ipv4,proto-ipv6,medium-ip,medium-ethernet \ + --- build --release --manifest-path embassy-net/Cargo.toml --target xtensa-esp32-none-elf --features defmt,tcp,udp,dns,proto-ipv4,proto-ipv6,medium-ip,medium-ethernet,medium-ieee802154 \ \ No newline at end of file From 796f6c034a148e1fedb3196a2c73a155f5d0545f Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Tue, 12 Nov 2024 17:48:36 +0100 Subject: [PATCH 199/361] Release embassy-executor 0.6.3. --- docs/examples/basic/Cargo.toml | 2 +- docs/examples/layer-by-layer/blinky-async/Cargo.toml | 2 +- docs/pages/new_project.adoc | 2 +- embassy-executor/CHANGELOG.md | 5 +++++ embassy-executor/Cargo.toml | 2 +- embassy-rp/Cargo.toml | 2 +- embassy-stm32/Cargo.toml | 2 +- embassy-time/Cargo.toml | 2 +- examples/boot/application/nrf/Cargo.toml | 2 +- examples/boot/application/rp/Cargo.toml | 2 +- examples/boot/application/stm32f3/Cargo.toml | 2 +- examples/boot/application/stm32f7/Cargo.toml | 2 +- examples/boot/application/stm32h7/Cargo.toml | 2 +- examples/boot/application/stm32l0/Cargo.toml | 2 +- examples/boot/application/stm32l1/Cargo.toml | 2 +- examples/boot/application/stm32l4/Cargo.toml | 2 +- examples/boot/application/stm32wb-dfu/Cargo.toml | 2 +- examples/boot/application/stm32wl/Cargo.toml | 2 +- examples/lpc55s69/Cargo.toml | 2 +- examples/nrf-rtos-trace/Cargo.toml | 2 +- examples/nrf51/Cargo.toml | 2 +- examples/nrf52810/Cargo.toml | 2 +- examples/nrf52840/Cargo.toml | 2 +- examples/nrf5340/Cargo.toml | 2 +- examples/nrf9151/ns/Cargo.toml | 2 +- examples/nrf9151/s/Cargo.toml | 2 +- examples/nrf9160/Cargo.toml | 2 +- examples/rp/Cargo.toml | 2 +- examples/rp23/Cargo.toml | 2 +- examples/std/Cargo.toml | 2 +- examples/stm32c0/Cargo.toml | 2 +- examples/stm32f0/Cargo.toml | 2 +- examples/stm32f1/Cargo.toml | 2 +- examples/stm32f2/Cargo.toml | 2 +- examples/stm32f3/Cargo.toml | 2 +- examples/stm32f334/Cargo.toml | 2 +- examples/stm32f4/Cargo.toml | 2 +- examples/stm32f469/Cargo.toml | 2 +- examples/stm32f7/Cargo.toml | 2 +- examples/stm32g0/Cargo.toml | 2 +- examples/stm32g4/Cargo.toml | 2 +- examples/stm32h5/Cargo.toml | 2 +- examples/stm32h7/Cargo.toml | 2 +- examples/stm32h735/Cargo.toml | 2 +- examples/stm32h755cm4/Cargo.toml | 2 +- examples/stm32h755cm7/Cargo.toml | 2 +- examples/stm32h7b0/Cargo.toml | 2 +- examples/stm32h7rs/Cargo.toml | 2 +- examples/stm32l0/Cargo.toml | 2 +- examples/stm32l1/Cargo.toml | 2 +- examples/stm32l4/Cargo.toml | 2 +- examples/stm32l5/Cargo.toml | 2 +- examples/stm32u0/Cargo.toml | 2 +- examples/stm32u5/Cargo.toml | 2 +- examples/stm32wb/Cargo.toml | 2 +- examples/stm32wba/Cargo.toml | 2 +- examples/stm32wl/Cargo.toml | 2 +- examples/wasm/Cargo.toml | 2 +- tests/nrf/Cargo.toml | 2 +- tests/riscv32/Cargo.toml | 2 +- tests/rp/Cargo.toml | 2 +- tests/stm32/Cargo.toml | 2 +- 62 files changed, 66 insertions(+), 61 deletions(-) diff --git a/docs/examples/basic/Cargo.toml b/docs/examples/basic/Cargo.toml index 5d391adf3..d46431b9a 100644 --- a/docs/examples/basic/Cargo.toml +++ b/docs/examples/basic/Cargo.toml @@ -6,7 +6,7 @@ version = "0.1.0" license = "MIT OR Apache-2.0" [dependencies] -embassy-executor = { version = "0.6.0", path = "../../../embassy-executor", features = ["defmt", "integrated-timers", "arch-cortex-m", "executor-thread"] } +embassy-executor = { version = "0.6.3", path = "../../../embassy-executor", features = ["defmt", "integrated-timers", "arch-cortex-m", "executor-thread"] } embassy-time = { version = "0.3.2", path = "../../../embassy-time", features = ["defmt"] } embassy-nrf = { version = "0.2.0", path = "../../../embassy-nrf", features = ["defmt", "nrf52840", "time-driver-rtc1", "gpiote"] } diff --git a/docs/examples/layer-by-layer/blinky-async/Cargo.toml b/docs/examples/layer-by-layer/blinky-async/Cargo.toml index 7f8d8af3e..f6d2e4fc7 100644 --- a/docs/examples/layer-by-layer/blinky-async/Cargo.toml +++ b/docs/examples/layer-by-layer/blinky-async/Cargo.toml @@ -8,7 +8,7 @@ license = "MIT OR Apache-2.0" cortex-m = "0.7" cortex-m-rt = "0.7" embassy-stm32 = { version = "0.1.0", features = ["stm32l475vg", "memory-x", "exti"] } -embassy-executor = { version = "0.6.0", features = ["arch-cortex-m", "executor-thread"] } +embassy-executor = { version = "0.6.3", features = ["arch-cortex-m", "executor-thread"] } defmt = "0.3.0" defmt-rtt = "0.3.0" diff --git a/docs/pages/new_project.adoc b/docs/pages/new_project.adoc index 38cea044b..f8dd848be 100644 --- a/docs/pages/new_project.adoc +++ b/docs/pages/new_project.adoc @@ -80,7 +80,7 @@ At the time of writing, embassy is already published to crates.io. Therefore, de ---- [dependencies] embassy-stm32 = { version = "0.1.0", features = ["defmt", "time-driver-any", "stm32g474re", "memory-x", "unstable-pac", "exti"] } -embassy-executor = { version = "0.6.1", features = ["nightly", "arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] } +embassy-executor = { version = "0.6.3", features = ["nightly", "arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] } embassy-time = { version = "0.3.2", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } ---- diff --git a/embassy-executor/CHANGELOG.md b/embassy-executor/CHANGELOG.md index b342ccc32..e07e3924f 100644 --- a/embassy-executor/CHANGELOG.md +++ b/embassy-executor/CHANGELOG.md @@ -7,6 +7,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## Unreleased +## 0.6.3 - 2024-11-12 + +- Building with the `nightly` feature now works with the Xtensa Rust compiler 1.82. +- Compare vtable address instead of contents. Saves 44 bytes of flash on cortex-m. + ## 0.6.2 - 2024-11-06 - The `nightly` feature no longer requires `nightly-2024-09-06` or newer. diff --git a/embassy-executor/Cargo.toml b/embassy-executor/Cargo.toml index 7141fe0f9..7c930b658 100644 --- a/embassy-executor/Cargo.toml +++ b/embassy-executor/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "embassy-executor" -version = "0.6.2" +version = "0.6.3" edition = "2021" license = "MIT OR Apache-2.0" description = "async/await executor designed for embedded usage" diff --git a/embassy-rp/Cargo.toml b/embassy-rp/Cargo.toml index 4616116aa..401ecd215 100644 --- a/embassy-rp/Cargo.toml +++ b/embassy-rp/Cargo.toml @@ -147,5 +147,5 @@ rp-binary-info = { version = "0.1.0", optional = true } smart-leds = "0.4.0" [dev-dependencies] -embassy-executor = { version = "0.6.2", path = "../embassy-executor", features = ["arch-std", "executor-thread"] } +embassy-executor = { version = "0.6.3", path = "../embassy-executor", features = ["arch-std", "executor-thread"] } static_cell = { version = "2" } diff --git a/embassy-stm32/Cargo.toml b/embassy-stm32/Cargo.toml index e2ba4fd60..4fe4ff358 100644 --- a/embassy-stm32/Cargo.toml +++ b/embassy-stm32/Cargo.toml @@ -51,7 +51,7 @@ embassy-embedded-hal = {version = "0.2.0", path = "../embassy-embedded-hal", def embassy-net-driver = { version = "0.2.0", path = "../embassy-net-driver" } embassy-usb-driver = {version = "0.1.0", path = "../embassy-usb-driver" } embassy-usb-synopsys-otg = {version = "0.1.0", path = "../embassy-usb-synopsys-otg" } -embassy-executor = { version = "0.6.2", path = "../embassy-executor", optional = true } +embassy-executor = { version = "0.6.3", path = "../embassy-executor", optional = true } embedded-hal-02 = { package = "embedded-hal", version = "0.2.6", features = ["unproven"] } embedded-hal-1 = { package = "embedded-hal", version = "1.0" } diff --git a/embassy-time/Cargo.toml b/embassy-time/Cargo.toml index 0e61434f1..8c7de9840 100644 --- a/embassy-time/Cargo.toml +++ b/embassy-time/Cargo.toml @@ -431,4 +431,4 @@ wasm-timer = { version = "0.2.5", optional = true } [dev-dependencies] serial_test = "0.9" critical-section = { version = "1.1", features = ["std"] } -embassy-executor = { version = "0.6.2", path = "../embassy-executor" } +embassy-executor = { version = "0.6.3", path = "../embassy-executor" } diff --git a/examples/boot/application/nrf/Cargo.toml b/examples/boot/application/nrf/Cargo.toml index e2ae240ae..3bf33d212 100644 --- a/examples/boot/application/nrf/Cargo.toml +++ b/examples/boot/application/nrf/Cargo.toml @@ -6,7 +6,7 @@ license = "MIT OR Apache-2.0" [dependencies] embassy-sync = { version = "0.6.0", path = "../../../../embassy-sync" } -embassy-executor = { version = "0.6.2", path = "../../../../embassy-executor", features = ["task-arena-size-16384", "arch-cortex-m", "executor-thread", "integrated-timers", "arch-cortex-m", "executor-thread"] } +embassy-executor = { version = "0.6.3", path = "../../../../embassy-executor", features = ["task-arena-size-16384", "arch-cortex-m", "executor-thread", "integrated-timers", "arch-cortex-m", "executor-thread"] } embassy-time = { version = "0.3.2", path = "../../../../embassy-time", features = [] } embassy-nrf = { version = "0.2.0", path = "../../../../embassy-nrf", features = ["time-driver-rtc1", "gpiote", ] } embassy-boot = { version = "0.3.0", path = "../../../../embassy-boot", features = [] } diff --git a/examples/boot/application/rp/Cargo.toml b/examples/boot/application/rp/Cargo.toml index f61ac3fd9..85515abc5 100644 --- a/examples/boot/application/rp/Cargo.toml +++ b/examples/boot/application/rp/Cargo.toml @@ -6,7 +6,7 @@ license = "MIT OR Apache-2.0" [dependencies] embassy-sync = { version = "0.6.0", path = "../../../../embassy-sync" } -embassy-executor = { version = "0.6.2", path = "../../../../embassy-executor", features = ["task-arena-size-16384", "arch-cortex-m", "executor-thread", "integrated-timers", "arch-cortex-m", "executor-thread"] } +embassy-executor = { version = "0.6.3", path = "../../../../embassy-executor", features = ["task-arena-size-16384", "arch-cortex-m", "executor-thread", "integrated-timers", "arch-cortex-m", "executor-thread"] } embassy-time = { version = "0.3.2", path = "../../../../embassy-time", features = [] } embassy-rp = { version = "0.2.0", path = "../../../../embassy-rp", features = ["time-driver", "rp2040"] } embassy-boot-rp = { version = "0.3.0", path = "../../../../embassy-boot-rp", features = [] } diff --git a/examples/boot/application/stm32f3/Cargo.toml b/examples/boot/application/stm32f3/Cargo.toml index dd20d2e0d..45394f1d5 100644 --- a/examples/boot/application/stm32f3/Cargo.toml +++ b/examples/boot/application/stm32f3/Cargo.toml @@ -6,7 +6,7 @@ license = "MIT OR Apache-2.0" [dependencies] embassy-sync = { version = "0.6.0", path = "../../../../embassy-sync" } -embassy-executor = { version = "0.6.2", path = "../../../../embassy-executor", features = ["task-arena-size-8192", "arch-cortex-m", "executor-thread", "integrated-timers"] } +embassy-executor = { version = "0.6.3", path = "../../../../embassy-executor", features = ["task-arena-size-8192", "arch-cortex-m", "executor-thread", "integrated-timers"] } embassy-time = { version = "0.3.2", path = "../../../../embassy-time", features = [ "tick-hz-32_768"] } embassy-stm32 = { version = "0.1.0", path = "../../../../embassy-stm32", features = ["stm32f303re", "time-driver-any", "exti"] } embassy-boot-stm32 = { version = "0.2.0", path = "../../../../embassy-boot-stm32" } diff --git a/examples/boot/application/stm32f7/Cargo.toml b/examples/boot/application/stm32f7/Cargo.toml index ce38e9ab9..8bb6c5df3 100644 --- a/examples/boot/application/stm32f7/Cargo.toml +++ b/examples/boot/application/stm32f7/Cargo.toml @@ -6,7 +6,7 @@ license = "MIT OR Apache-2.0" [dependencies] embassy-sync = { version = "0.6.0", path = "../../../../embassy-sync" } -embassy-executor = { version = "0.6.2", path = "../../../../embassy-executor", features = ["task-arena-size-8192", "arch-cortex-m", "executor-thread", "integrated-timers"] } +embassy-executor = { version = "0.6.3", path = "../../../../embassy-executor", features = ["task-arena-size-8192", "arch-cortex-m", "executor-thread", "integrated-timers"] } embassy-time = { version = "0.3.2", path = "../../../../embassy-time", features = [ "tick-hz-32_768"] } embassy-stm32 = { version = "0.1.0", path = "../../../../embassy-stm32", features = ["stm32f767zi", "time-driver-any", "exti"] } embassy-boot-stm32 = { version = "0.2.0", path = "../../../../embassy-boot-stm32", features = [] } diff --git a/examples/boot/application/stm32h7/Cargo.toml b/examples/boot/application/stm32h7/Cargo.toml index 841075627..85d3af65f 100644 --- a/examples/boot/application/stm32h7/Cargo.toml +++ b/examples/boot/application/stm32h7/Cargo.toml @@ -6,7 +6,7 @@ license = "MIT OR Apache-2.0" [dependencies] embassy-sync = { version = "0.6.0", path = "../../../../embassy-sync" } -embassy-executor = { version = "0.6.2", path = "../../../../embassy-executor", features = ["task-arena-size-8192", "arch-cortex-m", "executor-thread", "integrated-timers"] } +embassy-executor = { version = "0.6.3", path = "../../../../embassy-executor", features = ["task-arena-size-8192", "arch-cortex-m", "executor-thread", "integrated-timers"] } embassy-time = { version = "0.3.2", path = "../../../../embassy-time", features = [ "tick-hz-32_768"] } embassy-stm32 = { version = "0.1.0", path = "../../../../embassy-stm32", features = ["stm32h743zi", "time-driver-any", "exti"] } embassy-boot-stm32 = { version = "0.2.0", path = "../../../../embassy-boot-stm32", features = [] } diff --git a/examples/boot/application/stm32l0/Cargo.toml b/examples/boot/application/stm32l0/Cargo.toml index 1a92517db..a8a6f0bab 100644 --- a/examples/boot/application/stm32l0/Cargo.toml +++ b/examples/boot/application/stm32l0/Cargo.toml @@ -6,7 +6,7 @@ license = "MIT OR Apache-2.0" [dependencies] embassy-sync = { version = "0.6.0", path = "../../../../embassy-sync" } -embassy-executor = { version = "0.6.2", path = "../../../../embassy-executor", features = ["task-arena-size-8192", "arch-cortex-m", "executor-thread", "integrated-timers"] } +embassy-executor = { version = "0.6.3", path = "../../../../embassy-executor", features = ["task-arena-size-8192", "arch-cortex-m", "executor-thread", "integrated-timers"] } embassy-time = { version = "0.3.2", path = "../../../../embassy-time", features = [ "tick-hz-32_768"] } embassy-stm32 = { version = "0.1.0", path = "../../../../embassy-stm32", features = ["stm32l072cz", "time-driver-any", "exti", "memory-x"] } embassy-boot-stm32 = { version = "0.2.0", path = "../../../../embassy-boot-stm32", features = [] } diff --git a/examples/boot/application/stm32l1/Cargo.toml b/examples/boot/application/stm32l1/Cargo.toml index 57eeb07a1..0a8447aa6 100644 --- a/examples/boot/application/stm32l1/Cargo.toml +++ b/examples/boot/application/stm32l1/Cargo.toml @@ -6,7 +6,7 @@ license = "MIT OR Apache-2.0" [dependencies] embassy-sync = { version = "0.6.0", path = "../../../../embassy-sync" } -embassy-executor = { version = "0.6.2", path = "../../../../embassy-executor", features = ["task-arena-size-8192", "arch-cortex-m", "executor-thread", "integrated-timers"] } +embassy-executor = { version = "0.6.3", path = "../../../../embassy-executor", features = ["task-arena-size-8192", "arch-cortex-m", "executor-thread", "integrated-timers"] } embassy-time = { version = "0.3.2", path = "../../../../embassy-time", features = [ "tick-hz-32_768"] } embassy-stm32 = { version = "0.1.0", path = "../../../../embassy-stm32", features = ["stm32l151cb-a", "time-driver-any", "exti"] } embassy-boot-stm32 = { version = "0.2.0", path = "../../../../embassy-boot-stm32", features = [] } diff --git a/examples/boot/application/stm32l4/Cargo.toml b/examples/boot/application/stm32l4/Cargo.toml index f51ca29e1..1b5d33f78 100644 --- a/examples/boot/application/stm32l4/Cargo.toml +++ b/examples/boot/application/stm32l4/Cargo.toml @@ -6,7 +6,7 @@ license = "MIT OR Apache-2.0" [dependencies] embassy-sync = { version = "0.6.0", path = "../../../../embassy-sync" } -embassy-executor = { version = "0.6.2", path = "../../../../embassy-executor", features = ["task-arena-size-8192", "arch-cortex-m", "executor-thread", "integrated-timers"] } +embassy-executor = { version = "0.6.3", path = "../../../../embassy-executor", features = ["task-arena-size-8192", "arch-cortex-m", "executor-thread", "integrated-timers"] } embassy-time = { version = "0.3.2", path = "../../../../embassy-time", features = [ "tick-hz-32_768"] } embassy-stm32 = { version = "0.1.0", path = "../../../../embassy-stm32", features = ["stm32l475vg", "time-driver-any", "exti"] } embassy-boot-stm32 = { version = "0.2.0", path = "../../../../embassy-boot-stm32", features = [] } diff --git a/examples/boot/application/stm32wb-dfu/Cargo.toml b/examples/boot/application/stm32wb-dfu/Cargo.toml index 112de92f1..cde4a3ae6 100644 --- a/examples/boot/application/stm32wb-dfu/Cargo.toml +++ b/examples/boot/application/stm32wb-dfu/Cargo.toml @@ -6,7 +6,7 @@ license = "MIT OR Apache-2.0" [dependencies] embassy-sync = { version = "0.6.0", path = "../../../../embassy-sync" } -embassy-executor = { version = "0.6.2", path = "../../../../embassy-executor", features = ["task-arena-size-8192", "arch-cortex-m", "executor-thread", "integrated-timers"] } +embassy-executor = { version = "0.6.3", path = "../../../../embassy-executor", features = ["task-arena-size-8192", "arch-cortex-m", "executor-thread", "integrated-timers"] } embassy-time = { version = "0.3.2", path = "../../../../embassy-time", features = [ "tick-hz-32_768"] } embassy-stm32 = { version = "0.1.0", path = "../../../../embassy-stm32", features = ["stm32wb55rg", "time-driver-any", "exti"] } embassy-boot-stm32 = { version = "0.2.0", path = "../../../../embassy-boot-stm32", features = [] } diff --git a/examples/boot/application/stm32wl/Cargo.toml b/examples/boot/application/stm32wl/Cargo.toml index e2c42ce20..8e94051ce 100644 --- a/examples/boot/application/stm32wl/Cargo.toml +++ b/examples/boot/application/stm32wl/Cargo.toml @@ -6,7 +6,7 @@ license = "MIT OR Apache-2.0" [dependencies] embassy-sync = { version = "0.6.0", path = "../../../../embassy-sync" } -embassy-executor = { version = "0.6.2", path = "../../../../embassy-executor", features = ["task-arena-size-8192", "arch-cortex-m", "executor-thread", "integrated-timers"] } +embassy-executor = { version = "0.6.3", path = "../../../../embassy-executor", features = ["task-arena-size-8192", "arch-cortex-m", "executor-thread", "integrated-timers"] } embassy-time = { version = "0.3.2", path = "../../../../embassy-time", features = [ "tick-hz-32_768"] } embassy-stm32 = { version = "0.1.0", path = "../../../../embassy-stm32", features = ["stm32wl55jc-cm4", "time-driver-any", "exti"] } embassy-boot-stm32 = { version = "0.2.0", path = "../../../../embassy-boot-stm32", features = [] } diff --git a/examples/lpc55s69/Cargo.toml b/examples/lpc55s69/Cargo.toml index a18b29f2e..ee0c09dd3 100644 --- a/examples/lpc55s69/Cargo.toml +++ b/examples/lpc55s69/Cargo.toml @@ -7,7 +7,7 @@ license = "MIT OR Apache-2.0" [dependencies] embassy-nxp = { version = "0.1.0", path = "../../embassy-nxp", features = ["rt"] } -embassy-executor = { version = "0.6.2", path = "../../embassy-executor", features = ["task-arena-size-32768", "arch-cortex-m", "executor-thread", "executor-interrupt"] } +embassy-executor = { version = "0.6.3", path = "../../embassy-executor", features = ["task-arena-size-32768", "arch-cortex-m", "executor-thread", "executor-interrupt"] } embassy-sync = { version = "0.6.0", path = "../../embassy-sync", features = ["defmt"] } embassy-time = { version = "0.3.0", path = "../../embassy-time", features = ["defmt"] } panic-halt = "0.2.0" diff --git a/examples/nrf-rtos-trace/Cargo.toml b/examples/nrf-rtos-trace/Cargo.toml index fbdd2744c..ae8d62100 100644 --- a/examples/nrf-rtos-trace/Cargo.toml +++ b/examples/nrf-rtos-trace/Cargo.toml @@ -16,7 +16,7 @@ log = [ [dependencies] embassy-sync = { version = "0.6.0", path = "../../embassy-sync" } -embassy-executor = { version = "0.6.2", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "rtos-trace", "integrated-timers"] } +embassy-executor = { version = "0.6.3", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "rtos-trace", "integrated-timers"] } embassy-time = { version = "0.3.2", path = "../../embassy-time" } embassy-nrf = { version = "0.2.0", path = "../../embassy-nrf", features = ["nrf52840", "time-driver-rtc1", "gpiote", "unstable-pac"] } diff --git a/examples/nrf51/Cargo.toml b/examples/nrf51/Cargo.toml index 3799a87cc..05e702773 100644 --- a/examples/nrf51/Cargo.toml +++ b/examples/nrf51/Cargo.toml @@ -5,7 +5,7 @@ version = "0.1.0" license = "MIT OR Apache-2.0" [dependencies] -embassy-executor = { version = "0.6.2", path = "../../embassy-executor", features = ["task-arena-size-4096", "arch-cortex-m", "executor-thread", "executor-interrupt", "defmt", "integrated-timers"] } +embassy-executor = { version = "0.6.3", path = "../../embassy-executor", features = ["task-arena-size-4096", "arch-cortex-m", "executor-thread", "executor-interrupt", "defmt", "integrated-timers"] } embassy-time = { version = "0.3.2", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime"] } embassy-nrf = { version = "0.2.0", path = "../../embassy-nrf", features = ["defmt", "nrf51", "gpiote", "time-driver-rtc1", "unstable-pac", "time", "rt"] } diff --git a/examples/nrf52810/Cargo.toml b/examples/nrf52810/Cargo.toml index e3045bdba..3cc88ed7e 100644 --- a/examples/nrf52810/Cargo.toml +++ b/examples/nrf52810/Cargo.toml @@ -7,7 +7,7 @@ license = "MIT OR Apache-2.0" [dependencies] embassy-futures = { version = "0.1.0", path = "../../embassy-futures" } embassy-sync = { version = "0.6.0", path = "../../embassy-sync", features = ["defmt"] } -embassy-executor = { version = "0.6.2", path = "../../embassy-executor", features = ["task-arena-size-8192", "arch-cortex-m", "executor-thread", "executor-interrupt", "defmt", "integrated-timers"] } +embassy-executor = { version = "0.6.3", path = "../../embassy-executor", features = ["task-arena-size-8192", "arch-cortex-m", "executor-thread", "executor-interrupt", "defmt", "integrated-timers"] } embassy-time = { version = "0.3.2", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime"] } embassy-nrf = { version = "0.2.0", path = "../../embassy-nrf", features = ["defmt", "nrf52810", "time-driver-rtc1", "gpiote", "unstable-pac", "time"] } diff --git a/examples/nrf52840/Cargo.toml b/examples/nrf52840/Cargo.toml index e0a27c628..9ac6e3594 100644 --- a/examples/nrf52840/Cargo.toml +++ b/examples/nrf52840/Cargo.toml @@ -7,7 +7,7 @@ license = "MIT OR Apache-2.0" [dependencies] embassy-futures = { version = "0.1.0", path = "../../embassy-futures" } embassy-sync = { version = "0.6.0", path = "../../embassy-sync", features = ["defmt"] } -embassy-executor = { version = "0.6.2", path = "../../embassy-executor", features = ["task-arena-size-32768", "arch-cortex-m", "executor-thread", "executor-interrupt", "defmt", "integrated-timers"] } +embassy-executor = { version = "0.6.3", path = "../../embassy-executor", features = ["task-arena-size-32768", "arch-cortex-m", "executor-thread", "executor-interrupt", "defmt", "integrated-timers"] } embassy-time = { version = "0.3.2", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime"] } embassy-nrf = { version = "0.2.0", path = "../../embassy-nrf", features = ["defmt", "nrf52840", "time-driver-rtc1", "gpiote", "unstable-pac", "time"] } embassy-net = { version = "0.4.0", path = "../../embassy-net", features = ["defmt", "tcp", "dhcpv4", "medium-ethernet"] } diff --git a/examples/nrf5340/Cargo.toml b/examples/nrf5340/Cargo.toml index 2a4e78b7c..9103db907 100644 --- a/examples/nrf5340/Cargo.toml +++ b/examples/nrf5340/Cargo.toml @@ -7,7 +7,7 @@ license = "MIT OR Apache-2.0" [dependencies] embassy-futures = { version = "0.1.0", path = "../../embassy-futures" } embassy-sync = { version = "0.6.0", path = "../../embassy-sync", features = ["defmt"] } -embassy-executor = { version = "0.6.2", path = "../../embassy-executor", features = ["task-arena-size-32768", "arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] } +embassy-executor = { version = "0.6.3", path = "../../embassy-executor", features = ["task-arena-size-32768", "arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] } embassy-time = { version = "0.3.2", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime"] } embassy-nrf = { version = "0.2.0", path = "../../embassy-nrf", features = ["defmt", "nrf5340-app-s", "time-driver-rtc1", "gpiote", "unstable-pac"] } embassy-net = { version = "0.4.0", path = "../../embassy-net", features = ["defmt", "tcp", "dhcpv4", "medium-ethernet"] } diff --git a/examples/nrf9151/ns/Cargo.toml b/examples/nrf9151/ns/Cargo.toml index 679331716..96bf6700d 100644 --- a/examples/nrf9151/ns/Cargo.toml +++ b/examples/nrf9151/ns/Cargo.toml @@ -5,7 +5,7 @@ version = "0.1.0" license = "MIT OR Apache-2.0" [dependencies] -embassy-executor = { version = "0.6.2", path = "../../../embassy-executor", features = ["task-arena-size-32768", "arch-cortex-m", "executor-thread", "executor-interrupt", "defmt", "integrated-timers"] } +embassy-executor = { version = "0.6.3", path = "../../../embassy-executor", features = ["task-arena-size-32768", "arch-cortex-m", "executor-thread", "executor-interrupt", "defmt", "integrated-timers"] } embassy-time = { version = "0.3.2", path = "../../../embassy-time", features = ["defmt", "defmt-timestamp-uptime"] } embassy-nrf = { version = "0.2.0", path = "../../../embassy-nrf", features = ["defmt", "nrf9120-ns", "time-driver-rtc1", "gpiote", "unstable-pac", "time"] } diff --git a/examples/nrf9151/s/Cargo.toml b/examples/nrf9151/s/Cargo.toml index 63114478b..f7adf259d 100644 --- a/examples/nrf9151/s/Cargo.toml +++ b/examples/nrf9151/s/Cargo.toml @@ -5,7 +5,7 @@ version = "0.1.0" license = "MIT OR Apache-2.0" [dependencies] -embassy-executor = { version = "0.6.2", path = "../../../embassy-executor", features = ["task-arena-size-32768", "arch-cortex-m", "executor-thread", "executor-interrupt", "defmt", "integrated-timers"] } +embassy-executor = { version = "0.6.3", path = "../../../embassy-executor", features = ["task-arena-size-32768", "arch-cortex-m", "executor-thread", "executor-interrupt", "defmt", "integrated-timers"] } embassy-time = { version = "0.3.2", path = "../../../embassy-time", features = ["defmt", "defmt-timestamp-uptime"] } embassy-nrf = { version = "0.2.0", path = "../../../embassy-nrf", features = ["defmt", "nrf9120-s", "time-driver-rtc1", "gpiote", "unstable-pac", "time"] } diff --git a/examples/nrf9160/Cargo.toml b/examples/nrf9160/Cargo.toml index d5b0f0a41..47eba5552 100644 --- a/examples/nrf9160/Cargo.toml +++ b/examples/nrf9160/Cargo.toml @@ -5,7 +5,7 @@ version = "0.1.0" license = "MIT OR Apache-2.0" [dependencies] -embassy-executor = { version = "0.6.2", path = "../../embassy-executor", features = ["task-arena-size-32768", "arch-cortex-m", "executor-thread", "executor-interrupt", "defmt", "integrated-timers"] } +embassy-executor = { version = "0.6.3", path = "../../embassy-executor", features = ["task-arena-size-32768", "arch-cortex-m", "executor-thread", "executor-interrupt", "defmt", "integrated-timers"] } embassy-time = { version = "0.3.2", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime"] } embassy-nrf = { version = "0.2.0", path = "../../embassy-nrf", features = ["defmt", "nrf9160-s", "time-driver-rtc1", "gpiote", "unstable-pac", "time"] } embassy-net-nrf91 = { version = "0.1.0", path = "../../embassy-net-nrf91", features = ["defmt"] } diff --git a/examples/rp/Cargo.toml b/examples/rp/Cargo.toml index b45f5040a..6420ca32c 100644 --- a/examples/rp/Cargo.toml +++ b/examples/rp/Cargo.toml @@ -8,7 +8,7 @@ license = "MIT OR Apache-2.0" [dependencies] embassy-embedded-hal = { version = "0.2.0", path = "../../embassy-embedded-hal", features = ["defmt"] } embassy-sync = { version = "0.6.0", path = "../../embassy-sync", features = ["defmt"] } -embassy-executor = { version = "0.6.2", path = "../../embassy-executor", features = ["task-arena-size-98304", "arch-cortex-m", "executor-thread", "executor-interrupt", "defmt", "integrated-timers"] } +embassy-executor = { version = "0.6.3", path = "../../embassy-executor", features = ["task-arena-size-98304", "arch-cortex-m", "executor-thread", "executor-interrupt", "defmt", "integrated-timers"] } embassy-time = { version = "0.3.2", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime"] } embassy-rp = { version = "0.2.0", path = "../../embassy-rp", features = ["defmt", "unstable-pac", "time-driver", "critical-section-impl", "rp2040"] } embassy-usb = { version = "0.3.0", path = "../../embassy-usb", features = ["defmt"] } diff --git a/examples/rp23/Cargo.toml b/examples/rp23/Cargo.toml index 99a75a67f..3ea92f208 100644 --- a/examples/rp23/Cargo.toml +++ b/examples/rp23/Cargo.toml @@ -8,7 +8,7 @@ license = "MIT OR Apache-2.0" [dependencies] embassy-embedded-hal = { version = "0.2.0", path = "../../embassy-embedded-hal", features = ["defmt"] } embassy-sync = { version = "0.6.0", path = "../../embassy-sync", features = ["defmt"] } -embassy-executor = { version = "0.6.2", path = "../../embassy-executor", features = ["task-arena-size-98304", "arch-cortex-m", "executor-thread", "executor-interrupt", "defmt", "integrated-timers"] } +embassy-executor = { version = "0.6.3", path = "../../embassy-executor", features = ["task-arena-size-98304", "arch-cortex-m", "executor-thread", "executor-interrupt", "defmt", "integrated-timers"] } embassy-time = { version = "0.3.2", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime"] } embassy-rp = { version = "0.2.0", path = "../../embassy-rp", features = ["defmt", "unstable-pac", "time-driver", "critical-section-impl", "rp235xa", "binary-info"] } embassy-usb = { version = "0.3.0", path = "../../embassy-usb", features = ["defmt"] } diff --git a/examples/std/Cargo.toml b/examples/std/Cargo.toml index fac180c0c..6e918366c 100644 --- a/examples/std/Cargo.toml +++ b/examples/std/Cargo.toml @@ -6,7 +6,7 @@ license = "MIT OR Apache-2.0" [dependencies] embassy-sync = { version = "0.6.0", path = "../../embassy-sync", features = ["log"] } -embassy-executor = { version = "0.6.2", path = "../../embassy-executor", features = ["task-arena-size-32768", "arch-std", "executor-thread", "log", "integrated-timers"] } +embassy-executor = { version = "0.6.3", path = "../../embassy-executor", features = ["task-arena-size-32768", "arch-std", "executor-thread", "log", "integrated-timers"] } embassy-time = { version = "0.3.2", path = "../../embassy-time", features = ["log", "std", ] } embassy-net = { version = "0.4.0", path = "../../embassy-net", features=[ "std", "log", "medium-ethernet", "medium-ip", "tcp", "udp", "dns", "dhcpv4", "proto-ipv6"] } embassy-net-tuntap = { version = "0.1.0", path = "../../embassy-net-tuntap" } diff --git a/examples/stm32c0/Cargo.toml b/examples/stm32c0/Cargo.toml index f8a845095..06f11ef00 100644 --- a/examples/stm32c0/Cargo.toml +++ b/examples/stm32c0/Cargo.toml @@ -8,7 +8,7 @@ license = "MIT OR Apache-2.0" # Change stm32c031c6 to your chip name, if necessary. embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = [ "defmt", "time-driver-any", "stm32c031c6", "memory-x", "unstable-pac", "exti"] } embassy-sync = { version = "0.6.0", path = "../../embassy-sync", features = ["defmt"] } -embassy-executor = { version = "0.6.2", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] } +embassy-executor = { version = "0.6.3", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] } embassy-time = { version = "0.3.2", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } defmt = "0.3" diff --git a/examples/stm32f0/Cargo.toml b/examples/stm32f0/Cargo.toml index 6154fdc64..f5f1147fe 100644 --- a/examples/stm32f0/Cargo.toml +++ b/examples/stm32f0/Cargo.toml @@ -13,7 +13,7 @@ defmt = "0.3" defmt-rtt = "0.4" panic-probe = { version = "0.3", features = ["print-defmt"] } embassy-sync = { version = "0.6.0", path = "../../embassy-sync", features = ["defmt"] } -embassy-executor = { version = "0.6.2", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "executor-interrupt", "defmt", "integrated-timers"] } +embassy-executor = { version = "0.6.3", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "executor-interrupt", "defmt", "integrated-timers"] } embassy-time = { version = "0.3.2", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } static_cell = "2" portable-atomic = { version = "1.5", features = ["unsafe-assume-single-core"] } diff --git a/examples/stm32f1/Cargo.toml b/examples/stm32f1/Cargo.toml index c4836859e..846dcfd7c 100644 --- a/examples/stm32f1/Cargo.toml +++ b/examples/stm32f1/Cargo.toml @@ -8,7 +8,7 @@ license = "MIT OR Apache-2.0" # Change stm32f103c8 to your chip name, if necessary. embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = [ "defmt", "stm32f103c8", "unstable-pac", "memory-x", "time-driver-any" ] } embassy-sync = { version = "0.6.0", path = "../../embassy-sync", features = ["defmt"] } -embassy-executor = { version = "0.6.2", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] } +embassy-executor = { version = "0.6.3", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] } embassy-time = { version = "0.3.2", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } embassy-usb = { version = "0.3.0", path = "../../embassy-usb", features = ["defmt"] } embassy-futures = { version = "0.1.0", path = "../../embassy-futures" } diff --git a/examples/stm32f2/Cargo.toml b/examples/stm32f2/Cargo.toml index 0e4827e5d..ad87c973b 100644 --- a/examples/stm32f2/Cargo.toml +++ b/examples/stm32f2/Cargo.toml @@ -8,7 +8,7 @@ license = "MIT OR Apache-2.0" # Change stm32f207zg to your chip name, if necessary. embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = [ "defmt", "stm32f207zg", "unstable-pac", "memory-x", "time-driver-any", "exti"] } embassy-sync = { version = "0.6.0", path = "../../embassy-sync", features = ["defmt"] } -embassy-executor = { version = "0.6.2", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] } +embassy-executor = { version = "0.6.3", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] } embassy-time = { version = "0.3.2", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } defmt = "0.3" diff --git a/examples/stm32f3/Cargo.toml b/examples/stm32f3/Cargo.toml index 08a921634..f44d28473 100644 --- a/examples/stm32f3/Cargo.toml +++ b/examples/stm32f3/Cargo.toml @@ -8,7 +8,7 @@ license = "MIT OR Apache-2.0" # Change stm32f303ze to your chip name, if necessary. embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = [ "defmt", "stm32f303ze", "unstable-pac", "memory-x", "time-driver-any", "exti"] } embassy-sync = { version = "0.6.0", path = "../../embassy-sync", features = ["defmt"] } -embassy-executor = { version = "0.6.2", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "executor-interrupt", "defmt", "integrated-timers"] } +embassy-executor = { version = "0.6.3", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "executor-interrupt", "defmt", "integrated-timers"] } embassy-time = { version = "0.3.2", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } embassy-usb = { version = "0.3.0", path = "../../embassy-usb", features = ["defmt"] } embassy-futures = { version = "0.1.0", path = "../../embassy-futures" } diff --git a/examples/stm32f334/Cargo.toml b/examples/stm32f334/Cargo.toml index cba83bf8c..548ec1289 100644 --- a/examples/stm32f334/Cargo.toml +++ b/examples/stm32f334/Cargo.toml @@ -6,7 +6,7 @@ license = "MIT OR Apache-2.0" [dependencies] embassy-sync = { version = "0.6.0", path = "../../embassy-sync", features = ["defmt"] } -embassy-executor = { version = "0.6.2", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "executor-interrupt", "defmt", "integrated-timers"] } +embassy-executor = { version = "0.6.3", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "executor-interrupt", "defmt", "integrated-timers"] } embassy-time = { version = "0.3.2", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = [ "defmt", "stm32f334r8", "unstable-pac", "memory-x", "time-driver-any", "exti"] } embassy-usb = { version = "0.3.0", path = "../../embassy-usb", features = ["defmt"] } diff --git a/examples/stm32f4/Cargo.toml b/examples/stm32f4/Cargo.toml index 32fb845cb..368c5eb98 100644 --- a/examples/stm32f4/Cargo.toml +++ b/examples/stm32f4/Cargo.toml @@ -8,7 +8,7 @@ license = "MIT OR Apache-2.0" # Change stm32f429zi to your chip name, if necessary. embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["defmt", "stm32f429zi", "unstable-pac", "memory-x", "time-driver-any", "exti", "chrono"] } embassy-sync = { version = "0.6.0", path = "../../embassy-sync", features = ["defmt"] } -embassy-executor = { version = "0.6.2", path = "../../embassy-executor", features = ["task-arena-size-32768", "arch-cortex-m", "executor-thread", "executor-interrupt", "defmt", "integrated-timers"] } +embassy-executor = { version = "0.6.3", path = "../../embassy-executor", features = ["task-arena-size-32768", "arch-cortex-m", "executor-thread", "executor-interrupt", "defmt", "integrated-timers"] } embassy-time = { version = "0.3.2", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } embassy-usb = { version = "0.3.0", path = "../../embassy-usb", features = ["defmt" ] } embassy-net = { version = "0.4.0", path = "../../embassy-net", features = ["defmt", "tcp", "dhcpv4", "medium-ethernet", ] } diff --git a/examples/stm32f469/Cargo.toml b/examples/stm32f469/Cargo.toml index 139b4b937..382f7e485 100644 --- a/examples/stm32f469/Cargo.toml +++ b/examples/stm32f469/Cargo.toml @@ -7,7 +7,7 @@ license = "MIT OR Apache-2.0" [dependencies] # Specific examples only for stm32f469 embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["defmt", "stm32f469ni", "unstable-pac", "memory-x", "time-driver-any", "exti", "chrono"] } -embassy-executor = { version = "0.6.2", path = "../../embassy-executor", features = ["task-arena-size-32768", "arch-cortex-m", "executor-thread", "executor-interrupt", "defmt", "integrated-timers"] } +embassy-executor = { version = "0.6.3", path = "../../embassy-executor", features = ["task-arena-size-32768", "arch-cortex-m", "executor-thread", "executor-interrupt", "defmt", "integrated-timers"] } embassy-time = { version = "0.3.2", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } defmt = "0.3" diff --git a/examples/stm32f7/Cargo.toml b/examples/stm32f7/Cargo.toml index 2c55da04c..4f24d0eda 100644 --- a/examples/stm32f7/Cargo.toml +++ b/examples/stm32f7/Cargo.toml @@ -8,7 +8,7 @@ license = "MIT OR Apache-2.0" # Change stm32f777zi to your chip name, if necessary. embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["defmt", "stm32f777zi", "memory-x", "unstable-pac", "time-driver-any", "exti"] } embassy-sync = { version = "0.6.0", path = "../../embassy-sync", features = ["defmt"] } -embassy-executor = { version = "0.6.2", path = "../../embassy-executor", features = ["task-arena-size-32768", "arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] } +embassy-executor = { version = "0.6.3", path = "../../embassy-executor", features = ["task-arena-size-32768", "arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] } embassy-time = { version = "0.3.2", 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"] } embedded-io-async = { version = "0.6.1" } diff --git a/examples/stm32g0/Cargo.toml b/examples/stm32g0/Cargo.toml index bf258a7fd..ed74f1ba9 100644 --- a/examples/stm32g0/Cargo.toml +++ b/examples/stm32g0/Cargo.toml @@ -8,7 +8,7 @@ license = "MIT OR Apache-2.0" # Change stm32g0b1re to your chip name, if necessary. embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = [ "defmt", "time-driver-any", "stm32g0b1re", "memory-x", "unstable-pac", "exti"] } embassy-sync = { version = "0.6.0", path = "../../embassy-sync", features = ["defmt"] } -embassy-executor = { version = "0.6.2", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] } +embassy-executor = { version = "0.6.3", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] } embassy-time = { version = "0.3.2", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } embassy-usb = { version = "0.3.0", path = "../../embassy-usb", default-features = false, features = ["defmt"] } embassy-futures = { version = "0.1.0", path = "../../embassy-futures" } diff --git a/examples/stm32g4/Cargo.toml b/examples/stm32g4/Cargo.toml index cc3b44b57..3d5dd8069 100644 --- a/examples/stm32g4/Cargo.toml +++ b/examples/stm32g4/Cargo.toml @@ -8,7 +8,7 @@ license = "MIT OR Apache-2.0" # Change stm32g491re to your chip name, if necessary. embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = [ "defmt", "time-driver-any", "stm32g491re", "memory-x", "unstable-pac", "exti"] } embassy-sync = { version = "0.6.0", path = "../../embassy-sync", features = ["defmt"] } -embassy-executor = { version = "0.6.2", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] } +embassy-executor = { version = "0.6.3", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] } embassy-time = { version = "0.3.2", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } embassy-usb = { version = "0.3.0", path = "../../embassy-usb", features = ["defmt"] } embassy-futures = { version = "0.1.0", path = "../../embassy-futures" } diff --git a/examples/stm32h5/Cargo.toml b/examples/stm32h5/Cargo.toml index d1a865e4c..c71ad2db0 100644 --- a/examples/stm32h5/Cargo.toml +++ b/examples/stm32h5/Cargo.toml @@ -8,7 +8,7 @@ license = "MIT OR Apache-2.0" # Change stm32h563zi to your chip name, if necessary. embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["defmt", "stm32h563zi", "memory-x", "time-driver-any", "exti", "unstable-pac", "low-power"] } embassy-sync = { version = "0.6.0", path = "../../embassy-sync", features = ["defmt"] } -embassy-executor = { version = "0.6.2", path = "../../embassy-executor", features = ["task-arena-size-32768", "arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] } +embassy-executor = { version = "0.6.3", path = "../../embassy-executor", features = ["task-arena-size-32768", "arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] } embassy-time = { version = "0.3.2", 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"] } embassy-usb = { version = "0.3.0", path = "../../embassy-usb", features = ["defmt"] } diff --git a/examples/stm32h7/Cargo.toml b/examples/stm32h7/Cargo.toml index 1ba792f72..4a82d487f 100644 --- a/examples/stm32h7/Cargo.toml +++ b/examples/stm32h7/Cargo.toml @@ -9,7 +9,7 @@ license = "MIT OR Apache-2.0" 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.6.0", path = "../../embassy-sync", features = ["defmt"] } embassy-embedded-hal = { version = "0.2.0", path = "../../embassy-embedded-hal" } -embassy-executor = { version = "0.6.2", path = "../../embassy-executor", features = ["task-arena-size-32768", "arch-cortex-m", "executor-thread", "executor-interrupt", "defmt", "integrated-timers"] } +embassy-executor = { version = "0.6.3", path = "../../embassy-executor", features = ["task-arena-size-32768", "arch-cortex-m", "executor-thread", "executor-interrupt", "defmt", "integrated-timers"] } embassy-time = { version = "0.3.2", 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.3.0", path = "../../embassy-usb", features = ["defmt"] } diff --git a/examples/stm32h735/Cargo.toml b/examples/stm32h735/Cargo.toml index 6a74d83ab..e66e344d2 100644 --- a/examples/stm32h735/Cargo.toml +++ b/examples/stm32h735/Cargo.toml @@ -8,7 +8,7 @@ license = "MIT OR Apache-2.0" embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["defmt", "stm32h735ig", "time-driver-tim2", "exti", "memory-x", "unstable-pac", "chrono"] } embassy-sync = { version = "0.6.0", path = "../../embassy-sync", features = ["defmt"] } embassy-embedded-hal = { version = "0.2.0", path = "../../embassy-embedded-hal" } -embassy-executor = { version = "0.6.2", path = "../../embassy-executor", features = ["task-arena-size-32768", "arch-cortex-m", "executor-thread", "executor-interrupt", "defmt", "integrated-timers"] } +embassy-executor = { version = "0.6.3", path = "../../embassy-executor", features = ["task-arena-size-32768", "arch-cortex-m", "executor-thread", "executor-interrupt", "defmt", "integrated-timers"] } embassy-time = { version = "0.3.2", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } embassy-futures = { version = "0.1.0", path = "../../embassy-futures" } diff --git a/examples/stm32h755cm4/Cargo.toml b/examples/stm32h755cm4/Cargo.toml index ee96e64d6..8b46c3952 100644 --- a/examples/stm32h755cm4/Cargo.toml +++ b/examples/stm32h755cm4/Cargo.toml @@ -9,7 +9,7 @@ license = "MIT OR Apache-2.0" embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["defmt", "stm32h755zi-cm4", "time-driver-tim2", "exti", "memory-x", "unstable-pac", "chrono"] } embassy-sync = { version = "0.6.0", path = "../../embassy-sync", features = ["defmt"] } embassy-embedded-hal = { version = "0.2.0", path = "../../embassy-embedded-hal" } -embassy-executor = { version = "0.6.2", path = "../../embassy-executor", features = ["task-arena-size-32768", "arch-cortex-m", "executor-thread", "executor-interrupt", "defmt", "integrated-timers"] } +embassy-executor = { version = "0.6.3", path = "../../embassy-executor", features = ["task-arena-size-32768", "arch-cortex-m", "executor-thread", "executor-interrupt", "defmt", "integrated-timers"] } embassy-time = { version = "0.3.1", 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.3.0", path = "../../embassy-usb", features = ["defmt"] } diff --git a/examples/stm32h755cm7/Cargo.toml b/examples/stm32h755cm7/Cargo.toml index 61c425719..1db5143a4 100644 --- a/examples/stm32h755cm7/Cargo.toml +++ b/examples/stm32h755cm7/Cargo.toml @@ -9,7 +9,7 @@ license = "MIT OR Apache-2.0" embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["defmt", "stm32h755zi-cm7", "time-driver-tim3", "exti", "memory-x", "unstable-pac", "chrono"] } embassy-sync = { version = "0.6.0", path = "../../embassy-sync", features = ["defmt"] } embassy-embedded-hal = { version = "0.2.0", path = "../../embassy-embedded-hal" } -embassy-executor = { version = "0.6.2", path = "../../embassy-executor", features = ["task-arena-size-32768", "arch-cortex-m", "executor-thread", "executor-interrupt", "defmt", "integrated-timers"] } +embassy-executor = { version = "0.6.3", path = "../../embassy-executor", features = ["task-arena-size-32768", "arch-cortex-m", "executor-thread", "executor-interrupt", "defmt", "integrated-timers"] } embassy-time = { version = "0.3.1", 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.3.0", path = "../../embassy-usb", features = ["defmt"] } diff --git a/examples/stm32h7b0/Cargo.toml b/examples/stm32h7b0/Cargo.toml index 255ce933c..e22548bf0 100644 --- a/examples/stm32h7b0/Cargo.toml +++ b/examples/stm32h7b0/Cargo.toml @@ -8,7 +8,7 @@ license = "MIT OR Apache-2.0" embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["defmt", "stm32h7b0vb", "time-driver-tim2", "exti", "memory-x", "unstable-pac", "chrono"] } embassy-sync = { version = "0.6.0", path = "../../embassy-sync", features = ["defmt"] } embassy-embedded-hal = { version = "0.2.0", path = "../../embassy-embedded-hal" } -embassy-executor = { version = "0.6.2", path = "../../embassy-executor", features = ["task-arena-size-32768", "arch-cortex-m", "executor-thread", "executor-interrupt", "defmt", "integrated-timers"] } +embassy-executor = { version = "0.6.3", path = "../../embassy-executor", features = ["task-arena-size-32768", "arch-cortex-m", "executor-thread", "executor-interrupt", "defmt", "integrated-timers"] } embassy-time = { version = "0.3.2", 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.3.0", path = "../../embassy-usb", features = ["defmt"] } diff --git a/examples/stm32h7rs/Cargo.toml b/examples/stm32h7rs/Cargo.toml index 055be6a91..07c69977d 100644 --- a/examples/stm32h7rs/Cargo.toml +++ b/examples/stm32h7rs/Cargo.toml @@ -8,7 +8,7 @@ license = "MIT OR Apache-2.0" # Change stm32h743bi to your chip name, if necessary. embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["defmt", "stm32h7s3l8", "time-driver-tim2", "exti", "memory-x", "unstable-pac", "chrono"] } embassy-sync = { version = "0.6.0", path = "../../embassy-sync", features = ["defmt"] } -embassy-executor = { version = "0.6.2", path = "../../embassy-executor", features = ["task-arena-size-32768", "arch-cortex-m", "executor-thread", "executor-interrupt", "defmt", "integrated-timers"] } +embassy-executor = { version = "0.6.3", path = "../../embassy-executor", features = ["task-arena-size-32768", "arch-cortex-m", "executor-thread", "executor-interrupt", "defmt", "integrated-timers"] } embassy-time = { version = "0.3.2", 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.3.0", path = "../../embassy-usb", features = ["defmt"] } diff --git a/examples/stm32l0/Cargo.toml b/examples/stm32l0/Cargo.toml index 4e2bb8b67..f67fa6b00 100644 --- a/examples/stm32l0/Cargo.toml +++ b/examples/stm32l0/Cargo.toml @@ -8,7 +8,7 @@ license = "MIT OR Apache-2.0" # Change stm32l072cz to your chip name, if necessary. embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["defmt", "stm32l072cz", "unstable-pac", "time-driver-any", "exti", "memory-x"] } embassy-sync = { version = "0.6.0", path = "../../embassy-sync", features = ["defmt"] } -embassy-executor = { version = "0.6.2", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] } +embassy-executor = { version = "0.6.3", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] } embassy-time = { version = "0.3.2", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } defmt = "0.3" diff --git a/examples/stm32l1/Cargo.toml b/examples/stm32l1/Cargo.toml index 865cad87f..4d382f16e 100644 --- a/examples/stm32l1/Cargo.toml +++ b/examples/stm32l1/Cargo.toml @@ -6,7 +6,7 @@ license = "MIT OR Apache-2.0" [dependencies] embassy-sync = { version = "0.6.0", path = "../../embassy-sync", features = ["defmt"] } -embassy-executor = { version = "0.6.2", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] } +embassy-executor = { version = "0.6.3", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] } embassy-time = { version = "0.3.2", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = [ "defmt", "stm32l151cb-a", "time-driver-any", "memory-x"] } embassy-usb = { version = "0.3.0", path = "../../embassy-usb", features = ["defmt"] } diff --git a/examples/stm32l4/Cargo.toml b/examples/stm32l4/Cargo.toml index 7f963fc53..699358388 100644 --- a/examples/stm32l4/Cargo.toml +++ b/examples/stm32l4/Cargo.toml @@ -8,7 +8,7 @@ license = "MIT OR Apache-2.0" # Change stm32l4s5vi to your chip name, if necessary. embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = [ "defmt", "unstable-pac", "stm32l4s5qi", "memory-x", "time-driver-any", "exti", "chrono"] } embassy-sync = { version = "0.6.0", path = "../../embassy-sync", features = ["defmt"] } -embassy-executor = { version = "0.6.2", path = "../../embassy-executor", features = ["task-arena-size-32768", "arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] } +embassy-executor = { version = "0.6.3", path = "../../embassy-executor", features = ["task-arena-size-32768", "arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] } embassy-time = { version = "0.3.2", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768", ] } embassy-embedded-hal = { version = "0.2.0", path = "../../embassy-embedded-hal" } embassy-usb = { version = "0.3.0", path = "../../embassy-usb", features = ["defmt"] } diff --git a/examples/stm32l5/Cargo.toml b/examples/stm32l5/Cargo.toml index 0604625f2..ce9c76441 100644 --- a/examples/stm32l5/Cargo.toml +++ b/examples/stm32l5/Cargo.toml @@ -8,7 +8,7 @@ license = "MIT OR Apache-2.0" # Change stm32l552ze to your chip name, if necessary. embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = [ "defmt", "unstable-pac", "stm32l552ze", "time-driver-any", "exti", "memory-x", "low-power"] } embassy-sync = { version = "0.6.0", path = "../../embassy-sync", features = ["defmt"] } -embassy-executor = { version = "0.6.2", path = "../../embassy-executor", features = ["task-arena-size-32768", "arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] } +embassy-executor = { version = "0.6.3", path = "../../embassy-executor", features = ["task-arena-size-32768", "arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] } embassy-time = { version = "0.3.2", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } embassy-usb = { version = "0.3.0", path = "../../embassy-usb", features = ["defmt"] } embassy-net = { version = "0.4.0", path = "../../embassy-net", features = ["defmt", "tcp", "dhcpv4", "medium-ethernet"] } diff --git a/examples/stm32u0/Cargo.toml b/examples/stm32u0/Cargo.toml index 97ef0b704..6dd9bc7fa 100644 --- a/examples/stm32u0/Cargo.toml +++ b/examples/stm32u0/Cargo.toml @@ -8,7 +8,7 @@ license = "MIT OR Apache-2.0" # Change stm32u083rc to your chip name, if necessary. embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = [ "defmt", "time-driver-any", "stm32u083rc", "memory-x", "unstable-pac", "exti", "chrono"] } embassy-sync = { version = "0.6.0", path = "../../embassy-sync", features = ["defmt"] } -embassy-executor = { version = "0.6.2", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] } +embassy-executor = { version = "0.6.3", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] } embassy-time = { version = "0.3.2", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } embassy-usb = { version = "0.3.0", path = "../../embassy-usb", default-features = false, features = ["defmt"] } embassy-futures = { version = "0.1.0", path = "../../embassy-futures" } diff --git a/examples/stm32u5/Cargo.toml b/examples/stm32u5/Cargo.toml index f474e6db0..9a2b1dccf 100644 --- a/examples/stm32u5/Cargo.toml +++ b/examples/stm32u5/Cargo.toml @@ -8,7 +8,7 @@ license = "MIT OR Apache-2.0" # Change stm32u5g9zj to your chip name, if necessary. embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["defmt", "unstable-pac", "stm32u5g9zj", "time-driver-any", "memory-x" ] } embassy-sync = { version = "0.6.0", path = "../../embassy-sync", features = ["defmt"] } -embassy-executor = { version = "0.6.2", path = "../../embassy-executor", features = ["task-arena-size-32768", "arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] } +embassy-executor = { version = "0.6.3", path = "../../embassy-executor", features = ["task-arena-size-32768", "arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] } embassy-time = { version = "0.3.2", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } embassy-usb = { version = "0.3.0", path = "../../embassy-usb", features = ["defmt"] } embassy-futures = { version = "0.1.0", path = "../../embassy-futures" } diff --git a/examples/stm32wb/Cargo.toml b/examples/stm32wb/Cargo.toml index 26de81122..9a050e31e 100644 --- a/examples/stm32wb/Cargo.toml +++ b/examples/stm32wb/Cargo.toml @@ -9,7 +9,7 @@ license = "MIT OR Apache-2.0" embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = [ "defmt", "stm32wb55rg", "time-driver-any", "memory-x", "exti"] } embassy-stm32-wpan = { version = "0.1.0", path = "../../embassy-stm32-wpan", features = ["defmt", "stm32wb55rg"] } embassy-sync = { version = "0.6.0", path = "../../embassy-sync", features = ["defmt"] } -embassy-executor = { version = "0.6.2", path = "../../embassy-executor", features = ["task-arena-size-32768", "arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] } +embassy-executor = { version = "0.6.3", path = "../../embassy-executor", features = ["task-arena-size-32768", "arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] } embassy-time = { version = "0.3.2", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } embassy-net = { version = "0.4.0", path = "../../embassy-net", features = ["defmt", "udp", "proto-ipv6", "medium-ieee802154", ], optional=true } diff --git a/examples/stm32wba/Cargo.toml b/examples/stm32wba/Cargo.toml index 807f809e0..1697ffa1c 100644 --- a/examples/stm32wba/Cargo.toml +++ b/examples/stm32wba/Cargo.toml @@ -7,7 +7,7 @@ license = "MIT OR Apache-2.0" [dependencies] embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = [ "defmt", "stm32wba52cg", "time-driver-any", "memory-x", "exti"] } embassy-sync = { version = "0.6.0", path = "../../embassy-sync", features = ["defmt"] } -embassy-executor = { version = "0.6.2", path = "../../embassy-executor", features = ["task-arena-size-32768", "arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] } +embassy-executor = { version = "0.6.3", path = "../../embassy-executor", features = ["task-arena-size-32768", "arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] } embassy-time = { version = "0.3.2", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } embassy-net = { version = "0.4.0", path = "../../embassy-net", features = ["defmt", "udp", "proto-ipv6", "medium-ieee802154", ], optional=true } diff --git a/examples/stm32wl/Cargo.toml b/examples/stm32wl/Cargo.toml index 3b4223977..eeb13608c 100644 --- a/examples/stm32wl/Cargo.toml +++ b/examples/stm32wl/Cargo.toml @@ -8,7 +8,7 @@ license = "MIT OR Apache-2.0" # Change stm32wl55jc-cm4 to your chip name, if necessary. embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["defmt", "stm32wl55jc-cm4", "time-driver-any", "memory-x", "unstable-pac", "exti", "chrono"] } embassy-sync = { version = "0.6.0", path = "../../embassy-sync", features = ["defmt"] } -embassy-executor = { version = "0.6.2", path = "../../embassy-executor", features = ["task-arena-size-4096", "arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] } +embassy-executor = { version = "0.6.3", path = "../../embassy-executor", features = ["task-arena-size-4096", "arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] } embassy-time = { version = "0.3.2", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } embassy-embedded-hal = { version = "0.2.0", path = "../../embassy-embedded-hal" } diff --git a/examples/wasm/Cargo.toml b/examples/wasm/Cargo.toml index ea66d9b99..8c1b8a4f6 100644 --- a/examples/wasm/Cargo.toml +++ b/examples/wasm/Cargo.toml @@ -9,7 +9,7 @@ crate-type = ["cdylib"] [dependencies] embassy-sync = { version = "0.6.0", path = "../../embassy-sync", features = ["log"] } -embassy-executor = { version = "0.6.2", path = "../../embassy-executor", features = ["arch-wasm", "executor-thread", "log", "integrated-timers"] } +embassy-executor = { version = "0.6.3", path = "../../embassy-executor", features = ["arch-wasm", "executor-thread", "log", "integrated-timers"] } embassy-time = { version = "0.3.2", path = "../../embassy-time", features = ["log", "wasm", ] } wasm-logger = "0.2.0" diff --git a/tests/nrf/Cargo.toml b/tests/nrf/Cargo.toml index c1d386b93..0e58487d2 100644 --- a/tests/nrf/Cargo.toml +++ b/tests/nrf/Cargo.toml @@ -9,7 +9,7 @@ teleprobe-meta = "1" embassy-futures = { version = "0.1.0", path = "../../embassy-futures" } embassy-sync = { version = "0.6.0", path = "../../embassy-sync", features = ["defmt", ] } -embassy-executor = { version = "0.6.2", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "defmt", "task-arena-size-16384", "integrated-timers"] } +embassy-executor = { version = "0.6.3", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "defmt", "task-arena-size-16384", "integrated-timers"] } embassy-time = { version = "0.3.2", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime"] } embassy-nrf = { version = "0.2.0", path = "../../embassy-nrf", features = ["defmt", "time-driver-rtc1", "gpiote", "unstable-pac"] } embedded-io-async = { version = "0.6.1", features = ["defmt-03"] } diff --git a/tests/riscv32/Cargo.toml b/tests/riscv32/Cargo.toml index 35dd283eb..ae60dac10 100644 --- a/tests/riscv32/Cargo.toml +++ b/tests/riscv32/Cargo.toml @@ -7,7 +7,7 @@ license = "MIT OR Apache-2.0" [dependencies] critical-section = { version = "1.1.1", features = ["restore-state-bool"] } embassy-sync = { version = "0.6.0", path = "../../embassy-sync" } -embassy-executor = { version = "0.6.2", path = "../../embassy-executor", features = ["arch-riscv32", "executor-thread"] } +embassy-executor = { version = "0.6.3", path = "../../embassy-executor", features = ["arch-riscv32", "executor-thread"] } embassy-time = { version = "0.3.2", path = "../../embassy-time" } embassy-futures = { version = "0.1.0", path = "../../embassy-futures" } diff --git a/tests/rp/Cargo.toml b/tests/rp/Cargo.toml index 72f7ceb8f..359dae3bc 100644 --- a/tests/rp/Cargo.toml +++ b/tests/rp/Cargo.toml @@ -8,7 +8,7 @@ license = "MIT OR Apache-2.0" teleprobe-meta = "1.1" embassy-sync = { version = "0.6.0", path = "../../embassy-sync", features = ["defmt"] } -embassy-executor = { version = "0.6.2", path = "../../embassy-executor", features = ["task-arena-size-32768", "arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] } +embassy-executor = { version = "0.6.3", path = "../../embassy-executor", features = ["task-arena-size-32768", "arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] } embassy-time = { version = "0.3.2", path = "../../embassy-time", features = ["defmt", ] } embassy-rp = { version = "0.2.0", path = "../../embassy-rp", features = [ "defmt", "unstable-pac", "time-driver", "critical-section-impl", "intrinsics", "rom-v2-intrinsics", "run-from-ram", "rp2040"] } embassy-futures = { version = "0.1.0", path = "../../embassy-futures" } diff --git a/tests/stm32/Cargo.toml b/tests/stm32/Cargo.toml index f30217fc0..58da8c1b5 100644 --- a/tests/stm32/Cargo.toml +++ b/tests/stm32/Cargo.toml @@ -60,7 +60,7 @@ cm0 = ["portable-atomic/unsafe-assume-single-core"] teleprobe-meta = "1" embassy-sync = { version = "0.6.0", path = "../../embassy-sync", features = ["defmt"] } -embassy-executor = { version = "0.6.2", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] } +embassy-executor = { version = "0.6.3", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] } embassy-time = { version = "0.3.2", path = "../../embassy-time", features = ["defmt", "tick-hz-131_072", "defmt-timestamp-uptime"] } embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = [ "defmt", "unstable-pac", "memory-x", "time-driver-any"] } embassy-futures = { version = "0.1.0", path = "../../embassy-futures" } From 6801c6496c2f6e5dd94b1f2af8df03df0db61cc1 Mon Sep 17 00:00:00 2001 From: Cedric VINCENT Date: Wed, 13 Nov 2024 17:54:52 +0100 Subject: [PATCH 200/361] Fix multiprio examples for stm32 devices. Commit bbe1eebc has changed the order of TIM candidates when using the time-driver-any feature. For instance, in the case of STM32F3, it previously returned TIM2 but now returns TIM15. Consequently, the "multiprio" example no longer works as it requires three *free* CC register (for alarms), while TIM15 provides only one (CC1 is always reserved for regular time keeping). This commit was successfully tested on STM32F3. Additionally, I verified that all the examples using a timer for STM32F0 and STM32F4 still build correctly. Fixes #2749 --- examples/stm32f0/Cargo.toml | 2 +- examples/stm32f3/Cargo.toml | 2 +- examples/stm32f4/Cargo.toml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/examples/stm32f0/Cargo.toml b/examples/stm32f0/Cargo.toml index f5f1147fe..d80a7503b 100644 --- a/examples/stm32f0/Cargo.toml +++ b/examples/stm32f0/Cargo.toml @@ -6,7 +6,7 @@ license = "MIT OR Apache-2.0" [dependencies] # Change stm32f091rc to your chip name, if necessary. -embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = [ "defmt", "memory-x", "stm32f091rc", "time-driver-any", "exti", "unstable-pac"] } +embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = [ "defmt", "memory-x", "stm32f091rc", "time-driver-tim2", "exti", "unstable-pac"] } cortex-m = { version = "0.7.6", features = ["inline-asm", "critical-section-single-core"] } cortex-m-rt = "0.7.0" defmt = "0.3" diff --git a/examples/stm32f3/Cargo.toml b/examples/stm32f3/Cargo.toml index f44d28473..9c97e32d3 100644 --- a/examples/stm32f3/Cargo.toml +++ b/examples/stm32f3/Cargo.toml @@ -6,7 +6,7 @@ license = "MIT OR Apache-2.0" [dependencies] # Change stm32f303ze to your chip name, if necessary. -embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = [ "defmt", "stm32f303ze", "unstable-pac", "memory-x", "time-driver-any", "exti"] } +embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = [ "defmt", "stm32f303ze", "unstable-pac", "memory-x", "time-driver-tim2", "exti"] } embassy-sync = { version = "0.6.0", path = "../../embassy-sync", features = ["defmt"] } embassy-executor = { version = "0.6.3", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "executor-interrupt", "defmt", "integrated-timers"] } embassy-time = { version = "0.3.2", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } diff --git a/examples/stm32f4/Cargo.toml b/examples/stm32f4/Cargo.toml index 368c5eb98..cb8392922 100644 --- a/examples/stm32f4/Cargo.toml +++ b/examples/stm32f4/Cargo.toml @@ -6,7 +6,7 @@ license = "MIT OR Apache-2.0" [dependencies] # Change stm32f429zi to your chip name, if necessary. -embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["defmt", "stm32f429zi", "unstable-pac", "memory-x", "time-driver-any", "exti", "chrono"] } +embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["defmt", "stm32f429zi", "unstable-pac", "memory-x", "time-driver-tim4", "exti", "chrono"] } embassy-sync = { version = "0.6.0", path = "../../embassy-sync", features = ["defmt"] } embassy-executor = { version = "0.6.3", path = "../../embassy-executor", features = ["task-arena-size-32768", "arch-cortex-m", "executor-thread", "executor-interrupt", "defmt", "integrated-timers"] } embassy-time = { version = "0.3.2", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } From 19a06d6b8a00e3758d60246dbfed3b867fee35cb Mon Sep 17 00:00:00 2001 From: ibuki2003 Date: Thu, 14 Nov 2024 02:50:04 +0900 Subject: [PATCH 201/361] fix(rp): use uart dreq number defined in pac --- embassy-rp/src/uart/mod.rs | 22 ++++++++++++++++------ 1 file changed, 16 insertions(+), 6 deletions(-) diff --git a/embassy-rp/src/uart/mod.rs b/embassy-rp/src/uart/mod.rs index 08f20924c..8d12aeef6 100644 --- a/embassy-rp/src/uart/mod.rs +++ b/embassy-rp/src/uart/mod.rs @@ -1295,8 +1295,8 @@ impl<'d, T: Instance> embedded_io::Write for Uart<'d, T, Blocking> { trait SealedMode {} trait SealedInstance { - const TX_DREQ: u8; - const RX_DREQ: u8; + const TX_DREQ: pac::dma::vals::TreqSel; + const RX_DREQ: pac::dma::vals::TreqSel; fn regs() -> pac::uart::Uart; @@ -1334,8 +1334,8 @@ pub trait Instance: SealedInstance { macro_rules! impl_instance { ($inst:ident, $irq:ident, $tx_dreq:expr, $rx_dreq:expr) => { impl SealedInstance for peripherals::$inst { - const TX_DREQ: u8 = $tx_dreq; - const RX_DREQ: u8 = $rx_dreq; + const TX_DREQ: pac::dma::vals::TreqSel = $tx_dreq; + const RX_DREQ: pac::dma::vals::TreqSel = $rx_dreq; fn regs() -> pac::uart::Uart { pac::$inst @@ -1360,8 +1360,18 @@ macro_rules! impl_instance { }; } -impl_instance!(UART0, UART0_IRQ, 20, 21); -impl_instance!(UART1, UART1_IRQ, 22, 23); +impl_instance!( + UART0, + UART0_IRQ, + pac::dma::vals::TreqSel::UART0_TX, + pac::dma::vals::TreqSel::UART0_RX +); +impl_instance!( + UART1, + UART1_IRQ, + pac::dma::vals::TreqSel::UART1_TX, + pac::dma::vals::TreqSel::UART1_RX +); /// Trait for TX pins. pub trait TxPin: crate::gpio::Pin {} From 4692f06c33ba18bc6ecd5522ac794aea954ec9f2 Mon Sep 17 00:00:00 2001 From: elagil Date: Fri, 15 Nov 2024 00:01:40 +0100 Subject: [PATCH 202/361] fix: flush SAI FIFO on disable --- embassy-stm32/src/sai/mod.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/embassy-stm32/src/sai/mod.rs b/embassy-stm32/src/sai/mod.rs index 7d2f071de..1b26011db 100644 --- a/embassy-stm32/src/sai/mod.rs +++ b/embassy-stm32/src/sai/mod.rs @@ -1045,6 +1045,7 @@ impl<'d, T: Instance, W: word::Word> Drop for Sai<'d, T, W> { fn drop(&mut self) { let ch = T::REGS.ch(self.sub_block as usize); ch.cr1().modify(|w| w.set_saien(false)); + ch.cr2().modify(|w| w.set_fflush(true)); self.fs.as_ref().map(|x| x.set_as_disconnected()); self.sd.as_ref().map(|x| x.set_as_disconnected()); self.sck.as_ref().map(|x| x.set_as_disconnected()); From edb9b03deeddf306208c685b91d294a93d0ca54f Mon Sep 17 00:00:00 2001 From: elagil Date: Fri, 15 Nov 2024 00:07:23 +0100 Subject: [PATCH 203/361] fix: flush SAI FIFO on init --- embassy-stm32/src/sai/mod.rs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/embassy-stm32/src/sai/mod.rs b/embassy-stm32/src/sai/mod.rs index 1b26011db..62b44b77f 100644 --- a/embassy-stm32/src/sai/mod.rs +++ b/embassy-stm32/src/sai/mod.rs @@ -861,12 +861,15 @@ impl<'d, T: Instance, W: word::Word> Sai<'d, T, W> { ring_buffer: RingBuffer<'d, W>, config: Config, ) -> Self { + let ch = T::REGS.ch(sub_block as usize); + #[cfg(any(sai_v1, sai_v2, sai_v3_2pdm, sai_v3_4pdm, sai_v4_2pdm, sai_v4_4pdm))] { - let ch = T::REGS.ch(sub_block as usize); ch.cr1().modify(|w| w.set_saien(false)); } + ch.cr2().modify(|w| w.set_fflush(true)); + #[cfg(any(sai_v4_2pdm, sai_v4_4pdm))] { if let SyncInput::External(i) = config.sync_input { @@ -888,7 +891,6 @@ impl<'d, T: Instance, W: word::Word> Sai<'d, T, W> { #[cfg(any(sai_v1, sai_v2, sai_v3_2pdm, sai_v3_4pdm, sai_v4_2pdm, sai_v4_4pdm))] { - let ch = T::REGS.ch(sub_block as usize); ch.cr1().modify(|w| { w.set_mode(config.mode.mode(if Self::is_transmitter(&ring_buffer) { TxRx::Transmitter From 3f23fd5c98244921b97858912bbcdfd681b4f92e Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Fri, 15 Nov 2024 02:22:20 +0100 Subject: [PATCH 204/361] Update nrf-pac. --- embassy-net-nrf91/Cargo.toml | 2 +- embassy-nrf/Cargo.toml | 2 +- embassy-nrf/src/chips/nrf51.rs | 8 +- embassy-nrf/src/chips/nrf52805.rs | 28 +- embassy-nrf/src/chips/nrf52810.rs | 28 +- embassy-nrf/src/chips/nrf52811.rs | 36 +- embassy-nrf/src/chips/nrf52820.rs | 52 +-- embassy-nrf/src/chips/nrf52832.rs | 60 +-- embassy-nrf/src/chips/nrf52833.rs | 60 +-- embassy-nrf/src/chips/nrf52840.rs | 60 +-- embassy-nrf/src/chips/nrf5340_app.rs | 369 +++++++----------- embassy-nrf/src/chips/nrf5340_net.rs | 48 +-- embassy-nrf/src/chips/nrf9120.rs | 335 +++++++--------- embassy-nrf/src/chips/nrf9160.rs | 335 +++++++--------- embassy-nrf/src/lib.rs | 2 +- embassy-nrf/src/time_driver.rs | 2 +- embassy-nrf/src/usb/vbus_detect.rs | 4 +- examples/nrf52840-rtic/src/bin/blinky.rs | 2 +- examples/nrf52840/src/bin/buffered_uart.rs | 2 +- examples/nrf52840/src/bin/multiprio.rs | 16 +- examples/nrf52840/src/bin/spis.rs | 2 +- examples/nrf52840/src/bin/twim.rs | 2 +- examples/nrf52840/src/bin/twim_lowpower.rs | 2 +- examples/nrf52840/src/bin/twis.rs | 2 +- examples/nrf52840/src/bin/uart.rs | 2 +- examples/nrf52840/src/bin/uart_idle.rs | 2 +- examples/nrf52840/src/bin/uart_split.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/nrf9151/ns/src/bin/uart.rs | 2 +- examples/nrf9160/src/bin/modem_tcp_client.rs | 2 +- tests/nrf/src/common.rs | 28 +- 36 files changed, 641 insertions(+), 868 deletions(-) diff --git a/embassy-net-nrf91/Cargo.toml b/embassy-net-nrf91/Cargo.toml index 98356c1ba..77a1c4cb5 100644 --- a/embassy-net-nrf91/Cargo.toml +++ b/embassy-net-nrf91/Cargo.toml @@ -17,7 +17,7 @@ log = [ "dep:log" ] defmt = { version = "0.3", optional = true } log = { version = "0.4.14", optional = true } -nrf-pac = { git = "https://github.com/embassy-rs/nrf-pac", rev = "875a29629cc1c87aae00cfea647a956b3807d8be" } +nrf-pac = { git = "https://github.com/embassy-rs/nrf-pac", rev = "12e2461859acb0bfea9b2ef5cd73f1283c139ac0" } cortex-m = "0.7.7" embassy-time = { version = "0.3.1", path = "../embassy-time" } diff --git a/embassy-nrf/Cargo.toml b/embassy-nrf/Cargo.toml index 24a3db91d..7b20d2643 100644 --- a/embassy-nrf/Cargo.toml +++ b/embassy-nrf/Cargo.toml @@ -136,7 +136,7 @@ embedded-hal-async = { version = "1.0" } embedded-io = { version = "0.6.0" } embedded-io-async = { version = "0.6.1" } -nrf-pac = { git = "https://github.com/embassy-rs/nrf-pac", rev = "875a29629cc1c87aae00cfea647a956b3807d8be" } +nrf-pac = { git = "https://github.com/embassy-rs/nrf-pac", rev = "12e2461859acb0bfea9b2ef5cd73f1283c139ac0" } defmt = { version = "0.3", optional = true } bitflags = "2.4.2" diff --git a/embassy-nrf/src/chips/nrf51.rs b/embassy-nrf/src/chips/nrf51.rs index 95fa926c3..a0365a6ee 100644 --- a/embassy-nrf/src/chips/nrf51.rs +++ b/embassy-nrf/src/chips/nrf51.rs @@ -146,11 +146,11 @@ impl_pin!(P0_31, 0, 31); impl_radio!(RADIO, RADIO, RADIO); embassy_hal_internal::interrupt_mod!( - POWER_CLOCK, + CLOCK_POWER, RADIO, UART0, - SPI0_TWI0, - SPI1_TWI1, + TWISPI0, + TWISPI1, GPIOTE, ADC, TIMER0, @@ -160,7 +160,7 @@ embassy_hal_internal::interrupt_mod!( TEMP, RNG, ECB, - CCM_AAR, + AAR_CCM, WDT, RTC1, QDEC, diff --git a/embassy-nrf/src/chips/nrf52805.rs b/embassy-nrf/src/chips/nrf52805.rs index fc8db856c..a9cbccec4 100644 --- a/embassy-nrf/src/chips/nrf52805.rs +++ b/embassy-nrf/src/chips/nrf52805.rs @@ -138,15 +138,15 @@ embassy_hal_internal::peripherals! { EGU1, } -impl_uarte!(UARTE0, UARTE0, UARTE0_UART0); +impl_uarte!(UARTE0, UARTE0, UARTE0); -impl_spim!(SPI0, SPIM0, SPIM0_SPIS0_SPI0); +impl_spim!(SPI0, SPIM0, SPI0); -impl_spis!(SPI0, SPIS0, SPIM0_SPIS0_SPI0); +impl_spis!(SPI0, SPIS0, SPI0); -impl_twim!(TWI0, TWIM0, TWIM0_TWIS0_TWI0); +impl_twim!(TWI0, TWIM0, TWI0); -impl_twis!(TWI0, TWIS0, TWIM0_TWIS0_TWI0); +impl_twis!(TWI0, TWIS0, TWI0); impl_qdec!(QDEC, QDEC, QDEC); @@ -218,15 +218,15 @@ impl_saadc_input!(P0_05, ANALOG_INPUT3); impl_radio!(RADIO, RADIO, RADIO); -impl_egu!(EGU0, EGU0, SWI0_EGU0); -impl_egu!(EGU1, EGU1, SWI1_EGU1); +impl_egu!(EGU0, EGU0, EGU0_SWI0); +impl_egu!(EGU1, EGU1, EGU1_SWI1); embassy_hal_internal::interrupt_mod!( - POWER_CLOCK, + CLOCK_POWER, RADIO, - UARTE0_UART0, - TWIM0_TWIS0_TWI0, - SPIM0_SPIS0_SPI0, + UARTE0, + TWI0, + SPI0, GPIOTE, SAADC, TIMER0, @@ -236,12 +236,12 @@ embassy_hal_internal::interrupt_mod!( TEMP, RNG, ECB, - CCM_AAR, + AAR_CCM, WDT, RTC1, QDEC, - SWI0_EGU0, - SWI1_EGU1, + EGU0_SWI0, + EGU1_SWI1, SWI2, SWI3, SWI4, diff --git a/embassy-nrf/src/chips/nrf52810.rs b/embassy-nrf/src/chips/nrf52810.rs index 11a8b4dde..ca31c35e1 100644 --- a/embassy-nrf/src/chips/nrf52810.rs +++ b/embassy-nrf/src/chips/nrf52810.rs @@ -144,15 +144,15 @@ embassy_hal_internal::peripherals! { EGU1, } -impl_uarte!(UARTE0, UARTE0, UARTE0_UART0); +impl_uarte!(UARTE0, UARTE0, UARTE0); -impl_spim!(SPI0, SPIM0, SPIM0_SPIS0_SPI0); +impl_spim!(SPI0, SPIM0, SPI0); -impl_spis!(SPI0, SPIS0, SPIM0_SPIS0_SPI0); +impl_spis!(SPI0, SPIS0, SPI0); -impl_twim!(TWI0, TWIM0, TWIM0_TWIS0_TWI0); +impl_twim!(TWI0, TWIM0, TWI0); -impl_twis!(TWI0, TWIS0, TWIM0_TWIS0_TWI0); +impl_twis!(TWI0, TWIS0, TWI0); impl_pwm!(PWM0, PWM0, PWM0); @@ -244,15 +244,15 @@ impl_saadc_input!(P0_31, ANALOG_INPUT7); impl_radio!(RADIO, RADIO, RADIO); -impl_egu!(EGU0, EGU0, SWI0_EGU0); -impl_egu!(EGU1, EGU1, SWI1_EGU1); +impl_egu!(EGU0, EGU0, EGU0_SWI0); +impl_egu!(EGU1, EGU1, EGU1_SWI1); embassy_hal_internal::interrupt_mod!( - POWER_CLOCK, + CLOCK_POWER, RADIO, - UARTE0_UART0, - TWIM0_TWIS0_TWI0, - SPIM0_SPIS0_SPI0, + UARTE0, + TWI0, + SPI0, GPIOTE, SAADC, TIMER0, @@ -262,13 +262,13 @@ embassy_hal_internal::interrupt_mod!( TEMP, RNG, ECB, - CCM_AAR, + AAR_CCM, WDT, RTC1, QDEC, COMP, - SWI0_EGU0, - SWI1_EGU1, + EGU0_SWI0, + EGU1_SWI1, SWI2, SWI3, SWI4, diff --git a/embassy-nrf/src/chips/nrf52811.rs b/embassy-nrf/src/chips/nrf52811.rs index 077a36e31..caf3fdcf8 100644 --- a/embassy-nrf/src/chips/nrf52811.rs +++ b/embassy-nrf/src/chips/nrf52811.rs @@ -27,8 +27,8 @@ embassy_hal_internal::peripherals! { UARTE0, // SPI/TWI - TWISPI0, - SPI1, + TWI0_SPI1, + SPI0, // SAADC SAADC, @@ -144,17 +144,17 @@ embassy_hal_internal::peripherals! { EGU1, } -impl_uarte!(UARTE0, UARTE0, UARTE0_UART0); +impl_uarte!(UARTE0, UARTE0, UARTE0); -impl_spim!(TWISPI0, SPIM0, TWIM0_TWIS0_TWI0_SPIM0_SPIS0_SPI0); -impl_spim!(SPI1, SPIM1, SPIM1_SPIS1_SPI1); +impl_spim!(SPI0, SPIM0, SPI0); +impl_spim!(TWI0_SPI1, SPIM1, TWI0_SPI1); -impl_spis!(TWISPI0, SPIS0, TWIM0_TWIS0_TWI0_SPIM0_SPIS0_SPI0); -impl_spis!(SPI1, SPIS1, SPIM1_SPIS1_SPI1); +impl_spis!(SPI0, SPIS0, SPI0); +impl_spis!(TWI0_SPI1, SPIS1, TWI0_SPI1); -impl_twim!(TWISPI0, TWIM0, TWIM0_TWIS0_TWI0_SPIM0_SPIS0_SPI0); +impl_twim!(TWI0_SPI1, TWIM0, TWI0_SPI1); -impl_twis!(TWISPI0, TWIS0, TWIM0_TWIS0_TWI0_SPIM0_SPIS0_SPI0); +impl_twis!(TWI0_SPI1, TWIS0, TWI0_SPI1); impl_pwm!(PWM0, PWM0, PWM0); @@ -246,15 +246,15 @@ impl_saadc_input!(P0_31, ANALOG_INPUT7); impl_radio!(RADIO, RADIO, RADIO); -impl_egu!(EGU0, EGU0, SWI0_EGU0); -impl_egu!(EGU1, EGU1, SWI1_EGU1); +impl_egu!(EGU0, EGU0, EGU0_SWI0); +impl_egu!(EGU1, EGU1, EGU1_SWI1); embassy_hal_internal::interrupt_mod!( - POWER_CLOCK, + CLOCK_POWER, RADIO, - UARTE0_UART0, - TWIM0_TWIS0_TWI0_SPIM0_SPIS0_SPI0, - SPIM1_SPIS1_SPI1, + UARTE0, + TWI0_SPI1, + SPI0, GPIOTE, SAADC, TIMER0, @@ -264,13 +264,13 @@ embassy_hal_internal::interrupt_mod!( TEMP, RNG, ECB, - CCM_AAR, + AAR_CCM, WDT, RTC1, QDEC, COMP, - SWI0_EGU0, - SWI1_EGU1, + EGU0_SWI0, + EGU1_SWI1, SWI2, SWI3, SWI4, diff --git a/embassy-nrf/src/chips/nrf52820.rs b/embassy-nrf/src/chips/nrf52820.rs index 6ee16706d..39573e4a5 100644 --- a/embassy-nrf/src/chips/nrf52820.rs +++ b/embassy-nrf/src/chips/nrf52820.rs @@ -145,19 +145,19 @@ embassy_hal_internal::peripherals! { impl_usb!(USBD, USBD, USBD); -impl_uarte!(UARTE0, UARTE0, UARTE0_UART0); +impl_uarte!(UARTE0, UARTE0, UARTE0); -impl_spim!(TWISPI0, SPIM0, SPIM0_SPIS0_TWIM0_TWIS0_SPI0_TWI0); -impl_spim!(TWISPI1, SPIM1, SPIM1_SPIS1_TWIM1_TWIS1_SPI1_TWI1); +impl_spim!(TWISPI0, SPIM0, TWISPI0); +impl_spim!(TWISPI1, SPIM1, TWISPI1); -impl_spis!(TWISPI0, SPIS0, SPIM0_SPIS0_TWIM0_TWIS0_SPI0_TWI0); -impl_spis!(TWISPI1, SPIS1, SPIM1_SPIS1_TWIM1_TWIS1_SPI1_TWI1); +impl_spis!(TWISPI0, SPIS0, TWISPI0); +impl_spis!(TWISPI1, SPIS1, TWISPI1); -impl_twim!(TWISPI0, TWIM0, SPIM0_SPIS0_TWIM0_TWIS0_SPI0_TWI0); -impl_twim!(TWISPI1, TWIM1, SPIM1_SPIS1_TWIM1_TWIS1_SPI1_TWI1); +impl_twim!(TWISPI0, TWIM0, TWISPI0); +impl_twim!(TWISPI1, TWIM1, TWISPI1); -impl_twis!(TWISPI0, TWIS0, SPIM0_SPIS0_TWIM0_TWIS0_SPI0_TWI0); -impl_twis!(TWISPI1, TWIS1, SPIM1_SPIS1_TWIM1_TWIS1_SPI1_TWI1); +impl_twis!(TWISPI0, TWIS0, TWISPI0); +impl_twis!(TWISPI1, TWIS1, TWISPI1); impl_timer!(TIMER0, TIMER0, TIMER0); impl_timer!(TIMER1, TIMER1, TIMER1); @@ -237,19 +237,19 @@ impl_ppi_channel!(PPI_CH31, 31 => static); impl_radio!(RADIO, RADIO, RADIO); -impl_egu!(EGU0, EGU0, SWI0_EGU0); -impl_egu!(EGU1, EGU1, SWI1_EGU1); -impl_egu!(EGU2, EGU2, SWI2_EGU2); -impl_egu!(EGU3, EGU3, SWI3_EGU3); -impl_egu!(EGU4, EGU4, SWI4_EGU4); -impl_egu!(EGU5, EGU5, SWI5_EGU5); +impl_egu!(EGU0, EGU0, EGU0_SWI0); +impl_egu!(EGU1, EGU1, EGU1_SWI1); +impl_egu!(EGU2, EGU2, EGU2_SWI2); +impl_egu!(EGU3, EGU3, EGU3_SWI3); +impl_egu!(EGU4, EGU4, EGU4_SWI4); +impl_egu!(EGU5, EGU5, EGU5_SWI5); embassy_hal_internal::interrupt_mod!( - POWER_CLOCK, + CLOCK_POWER, RADIO, - UARTE0_UART0, - SPIM0_SPIS0_TWIM0_TWIS0_SPI0_TWI0, - SPIM1_SPIS1_TWIM1_TWIS1_SPI1_TWI1, + UARTE0, + TWISPI0, + TWISPI1, GPIOTE, TIMER0, TIMER1, @@ -258,17 +258,17 @@ embassy_hal_internal::interrupt_mod!( TEMP, RNG, ECB, - CCM_AAR, + AAR_CCM, WDT, RTC1, QDEC, COMP, - SWI0_EGU0, - SWI1_EGU1, - SWI2_EGU2, - SWI3_EGU3, - SWI4_EGU4, - SWI5_EGU5, + EGU0_SWI0, + EGU1_SWI1, + EGU2_SWI2, + EGU3_SWI3, + EGU4_SWI4, + EGU5_SWI5, TIMER3, USBD, ); diff --git a/embassy-nrf/src/chips/nrf52832.rs b/embassy-nrf/src/chips/nrf52832.rs index 4a7a29229..7ff58d63e 100644 --- a/embassy-nrf/src/chips/nrf52832.rs +++ b/embassy-nrf/src/chips/nrf52832.rs @@ -163,21 +163,21 @@ embassy_hal_internal::peripherals! { EGU5, } -impl_uarte!(UARTE0, UARTE0, UARTE0_UART0); +impl_uarte!(UARTE0, UARTE0, UARTE0); -impl_spim!(TWISPI0, SPIM0, SPIM0_SPIS0_TWIM0_TWIS0_SPI0_TWI0); -impl_spim!(TWISPI1, SPIM1, SPIM1_SPIS1_TWIM1_TWIS1_SPI1_TWI1); -impl_spim!(SPI2, SPIM2, SPIM2_SPIS2_SPI2); +impl_spim!(TWISPI0, SPIM0, TWISPI0); +impl_spim!(TWISPI1, SPIM1, TWISPI1); +impl_spim!(SPI2, SPIM2, SPI2); -impl_spis!(TWISPI0, SPIS0, SPIM0_SPIS0_TWIM0_TWIS0_SPI0_TWI0); -impl_spis!(TWISPI1, SPIS1, SPIM1_SPIS1_TWIM1_TWIS1_SPI1_TWI1); -impl_spis!(SPI2, SPIS2, SPIM2_SPIS2_SPI2); +impl_spis!(TWISPI0, SPIS0, TWISPI0); +impl_spis!(TWISPI1, SPIS1, TWISPI1); +impl_spis!(SPI2, SPIS2, SPI2); -impl_twim!(TWISPI0, TWIM0, SPIM0_SPIS0_TWIM0_TWIS0_SPI0_TWI0); -impl_twim!(TWISPI1, TWIM1, SPIM1_SPIS1_TWIM1_TWIS1_SPI1_TWI1); +impl_twim!(TWISPI0, TWIM0, TWISPI0); +impl_twim!(TWISPI1, TWIM1, TWISPI1); -impl_twis!(TWISPI0, TWIS0, SPIM0_SPIS0_TWIM0_TWIS0_SPI0_TWI0); -impl_twis!(TWISPI1, TWIS1, SPIM1_SPIS1_TWIM1_TWIS1_SPI1_TWI1); +impl_twis!(TWISPI0, TWIS0, TWISPI0); +impl_twis!(TWISPI1, TWIS1, TWISPI1); impl_pwm!(PWM0, PWM0, PWM0); impl_pwm!(PWM1, PWM1, PWM1); @@ -277,19 +277,19 @@ impl_i2s!(I2S, I2S, I2S); impl_radio!(RADIO, RADIO, RADIO); -impl_egu!(EGU0, EGU0, SWI0_EGU0); -impl_egu!(EGU1, EGU1, SWI1_EGU1); -impl_egu!(EGU2, EGU2, SWI2_EGU2); -impl_egu!(EGU3, EGU3, SWI3_EGU3); -impl_egu!(EGU4, EGU4, SWI4_EGU4); -impl_egu!(EGU5, EGU5, SWI5_EGU5); +impl_egu!(EGU0, EGU0, EGU0_SWI0); +impl_egu!(EGU1, EGU1, EGU1_SWI1); +impl_egu!(EGU2, EGU2, EGU2_SWI2); +impl_egu!(EGU3, EGU3, EGU3_SWI3); +impl_egu!(EGU4, EGU4, EGU4_SWI4); +impl_egu!(EGU5, EGU5, EGU5_SWI5); embassy_hal_internal::interrupt_mod!( - POWER_CLOCK, + CLOCK_POWER, RADIO, - UARTE0_UART0, - SPIM0_SPIS0_TWIM0_TWIS0_SPI0_TWI0, - SPIM1_SPIS1_TWIM1_TWIS1_SPI1_TWI1, + UARTE0, + TWISPI0, + TWISPI1, NFCT, GPIOTE, SAADC, @@ -300,17 +300,17 @@ embassy_hal_internal::interrupt_mod!( TEMP, RNG, ECB, - CCM_AAR, + AAR_CCM, WDT, RTC1, QDEC, COMP_LPCOMP, - SWI0_EGU0, - SWI1_EGU1, - SWI2_EGU2, - SWI3_EGU3, - SWI4_EGU4, - SWI5_EGU5, + EGU0_SWI0, + EGU1_SWI1, + EGU2_SWI2, + EGU3_SWI3, + EGU4_SWI4, + EGU5_SWI5, TIMER3, TIMER4, PWM0, @@ -318,8 +318,8 @@ embassy_hal_internal::interrupt_mod!( MWU, PWM1, PWM2, - SPIM2_SPIS2_SPI2, + SPI2, RTC2, - FPU, I2S, + FPU, ); diff --git a/embassy-nrf/src/chips/nrf52833.rs b/embassy-nrf/src/chips/nrf52833.rs index 6d70b763f..a6273452a 100644 --- a/embassy-nrf/src/chips/nrf52833.rs +++ b/embassy-nrf/src/chips/nrf52833.rs @@ -185,23 +185,23 @@ embassy_hal_internal::peripherals! { impl_usb!(USBD, USBD, USBD); -impl_uarte!(UARTE0, UARTE0, UARTE0_UART0); +impl_uarte!(UARTE0, UARTE0, UARTE0); impl_uarte!(UARTE1, UARTE1, UARTE1); -impl_spim!(TWISPI0, SPIM0, SPIM0_SPIS0_TWIM0_TWIS0_SPI0_TWI0); -impl_spim!(TWISPI1, SPIM1, SPIM1_SPIS1_TWIM1_TWIS1_SPI1_TWI1); -impl_spim!(SPI2, SPIM2, SPIM2_SPIS2_SPI2); +impl_spim!(TWISPI0, SPIM0, TWISPI0); +impl_spim!(TWISPI1, SPIM1, TWISPI1); +impl_spim!(SPI2, SPIM2, SPI2); impl_spim!(SPI3, SPIM3, SPIM3); -impl_spis!(TWISPI0, SPIS0, SPIM0_SPIS0_TWIM0_TWIS0_SPI0_TWI0); -impl_spis!(TWISPI1, SPIS1, SPIM1_SPIS1_TWIM1_TWIS1_SPI1_TWI1); -impl_spis!(SPI2, SPIS2, SPIM2_SPIS2_SPI2); +impl_spis!(TWISPI0, SPIS0, TWISPI0); +impl_spis!(TWISPI1, SPIS1, TWISPI1); +impl_spis!(SPI2, SPIS2, SPI2); -impl_twim!(TWISPI0, TWIM0, SPIM0_SPIS0_TWIM0_TWIS0_SPI0_TWI0); -impl_twim!(TWISPI1, TWIM1, SPIM1_SPIS1_TWIM1_TWIS1_SPI1_TWI1); +impl_twim!(TWISPI0, TWIM0, TWISPI0); +impl_twim!(TWISPI1, TWIM1, TWISPI1); -impl_twis!(TWISPI0, TWIS0, SPIM0_SPIS0_TWIM0_TWIS0_SPI0_TWI0); -impl_twis!(TWISPI1, TWIS1, SPIM1_SPIS1_TWIM1_TWIS1_SPI1_TWI1); +impl_twis!(TWISPI0, TWIS0, TWISPI0); +impl_twis!(TWISPI1, TWIS1, TWISPI1); impl_pwm!(PWM0, PWM0, PWM0); impl_pwm!(PWM1, PWM1, PWM1); @@ -319,19 +319,19 @@ impl_i2s!(I2S, I2S, I2S); impl_radio!(RADIO, RADIO, RADIO); -impl_egu!(EGU0, EGU0, SWI0_EGU0); -impl_egu!(EGU1, EGU1, SWI1_EGU1); -impl_egu!(EGU2, EGU2, SWI2_EGU2); -impl_egu!(EGU3, EGU3, SWI3_EGU3); -impl_egu!(EGU4, EGU4, SWI4_EGU4); -impl_egu!(EGU5, EGU5, SWI5_EGU5); +impl_egu!(EGU0, EGU0, EGU0_SWI0); +impl_egu!(EGU1, EGU1, EGU1_SWI1); +impl_egu!(EGU2, EGU2, EGU2_SWI2); +impl_egu!(EGU3, EGU3, EGU3_SWI3); +impl_egu!(EGU4, EGU4, EGU4_SWI4); +impl_egu!(EGU5, EGU5, EGU5_SWI5); embassy_hal_internal::interrupt_mod!( - POWER_CLOCK, + CLOCK_POWER, RADIO, - UARTE0_UART0, - SPIM0_SPIS0_TWIM0_TWIS0_SPI0_TWI0, - SPIM1_SPIS1_TWIM1_TWIS1_SPI1_TWI1, + UARTE0, + TWISPI0, + TWISPI1, NFCT, GPIOTE, SAADC, @@ -342,17 +342,17 @@ embassy_hal_internal::interrupt_mod!( TEMP, RNG, ECB, - CCM_AAR, + AAR_CCM, WDT, RTC1, QDEC, COMP_LPCOMP, - SWI0_EGU0, - SWI1_EGU1, - SWI2_EGU2, - SWI3_EGU3, - SWI4_EGU4, - SWI5_EGU5, + EGU0_SWI0, + EGU1_SWI1, + EGU2_SWI2, + EGU3_SWI3, + EGU4_SWI4, + EGU5_SWI5, TIMER3, TIMER4, PWM0, @@ -360,12 +360,12 @@ embassy_hal_internal::interrupt_mod!( MWU, PWM1, PWM2, - SPIM2_SPIS2_SPI2, + SPI2, RTC2, + I2S, FPU, USBD, UARTE1, PWM3, SPIM3, - I2S, ); diff --git a/embassy-nrf/src/chips/nrf52840.rs b/embassy-nrf/src/chips/nrf52840.rs index b6afbf213..fb341afd5 100644 --- a/embassy-nrf/src/chips/nrf52840.rs +++ b/embassy-nrf/src/chips/nrf52840.rs @@ -188,23 +188,23 @@ embassy_hal_internal::peripherals! { impl_usb!(USBD, USBD, USBD); -impl_uarte!(UARTE0, UARTE0, UARTE0_UART0); +impl_uarte!(UARTE0, UARTE0, UARTE0); impl_uarte!(UARTE1, UARTE1, UARTE1); -impl_spim!(TWISPI0, SPIM0, SPIM0_SPIS0_TWIM0_TWIS0_SPI0_TWI0); -impl_spim!(TWISPI1, SPIM1, SPIM1_SPIS1_TWIM1_TWIS1_SPI1_TWI1); -impl_spim!(SPI2, SPIM2, SPIM2_SPIS2_SPI2); +impl_spim!(TWISPI0, SPIM0, TWISPI0); +impl_spim!(TWISPI1, SPIM1, TWISPI1); +impl_spim!(SPI2, SPIM2, SPI2); impl_spim!(SPI3, SPIM3, SPIM3); -impl_spis!(TWISPI0, SPIS0, SPIM0_SPIS0_TWIM0_TWIS0_SPI0_TWI0); -impl_spis!(TWISPI1, SPIS1, SPIM1_SPIS1_TWIM1_TWIS1_SPI1_TWI1); -impl_spis!(SPI2, SPIS2, SPIM2_SPIS2_SPI2); +impl_spis!(TWISPI0, SPIS0, TWISPI0); +impl_spis!(TWISPI1, SPIS1, TWISPI1); +impl_spis!(SPI2, SPIS2, SPI2); -impl_twim!(TWISPI0, TWIM0, SPIM0_SPIS0_TWIM0_TWIS0_SPI0_TWI0); -impl_twim!(TWISPI1, TWIM1, SPIM1_SPIS1_TWIM1_TWIS1_SPI1_TWI1); +impl_twim!(TWISPI0, TWIM0, TWISPI0); +impl_twim!(TWISPI1, TWIM1, TWISPI1); -impl_twis!(TWISPI0, TWIS0, SPIM0_SPIS0_TWIM0_TWIS0_SPI0_TWI0); -impl_twis!(TWISPI1, TWIS1, SPIM1_SPIS1_TWIM1_TWIS1_SPI1_TWI1); +impl_twis!(TWISPI0, TWIS0, TWISPI0); +impl_twis!(TWISPI1, TWIS1, TWISPI1); impl_pwm!(PWM0, PWM0, PWM0); impl_pwm!(PWM1, PWM1, PWM1); @@ -324,19 +324,19 @@ impl_i2s!(I2S, I2S, I2S); impl_radio!(RADIO, RADIO, RADIO); -impl_egu!(EGU0, EGU0, SWI0_EGU0); -impl_egu!(EGU1, EGU1, SWI1_EGU1); -impl_egu!(EGU2, EGU2, SWI2_EGU2); -impl_egu!(EGU3, EGU3, SWI3_EGU3); -impl_egu!(EGU4, EGU4, SWI4_EGU4); -impl_egu!(EGU5, EGU5, SWI5_EGU5); +impl_egu!(EGU0, EGU0, EGU0_SWI0); +impl_egu!(EGU1, EGU1, EGU1_SWI1); +impl_egu!(EGU2, EGU2, EGU2_SWI2); +impl_egu!(EGU3, EGU3, EGU3_SWI3); +impl_egu!(EGU4, EGU4, EGU4_SWI4); +impl_egu!(EGU5, EGU5, EGU5_SWI5); embassy_hal_internal::interrupt_mod!( - POWER_CLOCK, + CLOCK_POWER, RADIO, - UARTE0_UART0, - SPIM0_SPIS0_TWIM0_TWIS0_SPI0_TWI0, - SPIM1_SPIS1_TWIM1_TWIS1_SPI1_TWI1, + UARTE0, + TWISPI0, + TWISPI1, NFCT, GPIOTE, SAADC, @@ -347,17 +347,17 @@ embassy_hal_internal::interrupt_mod!( TEMP, RNG, ECB, - CCM_AAR, + AAR_CCM, WDT, RTC1, QDEC, COMP_LPCOMP, - SWI0_EGU0, - SWI1_EGU1, - SWI2_EGU2, - SWI3_EGU3, - SWI4_EGU4, - SWI5_EGU5, + EGU0_SWI0, + EGU1_SWI1, + EGU2_SWI2, + EGU3_SWI3, + EGU4_SWI4, + EGU5_SWI5, TIMER3, TIMER4, PWM0, @@ -365,8 +365,9 @@ embassy_hal_internal::interrupt_mod!( MWU, PWM1, PWM2, - SPIM2_SPIS2_SPI2, + SPI2, RTC2, + I2S, FPU, USBD, UARTE1, @@ -374,5 +375,4 @@ embassy_hal_internal::interrupt_mod!( CRYPTOCELL, PWM3, SPIM3, - I2S, ); diff --git a/embassy-nrf/src/chips/nrf5340_app.rs b/embassy-nrf/src/chips/nrf5340_app.rs index d7846b094..6dc64fb4f 100644 --- a/embassy-nrf/src/chips/nrf5340_app.rs +++ b/embassy-nrf/src/chips/nrf5340_app.rs @@ -2,229 +2,158 @@ #[allow(unused_imports)] #[rustfmt::skip] pub mod pac { - // The nRF5340 has a secure and non-secure (NS) mode. - // To avoid cfg spam, we remove _ns or _s suffixes here. - - #[cfg(feature="rt")] - pub use nrf_pac::NVIC_PRIO_BITS; - pub use nrf_pac::{common, shared}; - - #[cfg(feature="rt")] - #[doc(no_inline)] - pub use nrf_pac::interrupt; - - #[doc(no_inline)] - pub use nrf_pac::{ - Interrupt, - - cache_s as cache, - cachedata_s as cachedata, - cacheinfo_s as cacheinfo, - clock_ns as clock, - comp_ns as comp, - cryptocell_s as cryptocell, - cti_s as cti, - ctrlap_ns as ctrlap, - dcnf_ns as dcnf, - dppic_ns as dppic, - egu_ns as egu, - ficr_s as ficr, - fpu_ns as fpu, - gpiote_s as gpiote, - i2s_ns as i2s, - ipc_ns as ipc, - kmu_ns as kmu, - lpcomp_ns as lpcomp, - mutex_ns as mutex, - nfct_ns as nfct, - nvmc_ns as nvmc, - oscillators_ns as oscillators, - gpio_ns as gpio, - pdm_ns as pdm, - power_ns as power, - pwm_ns as pwm, - qdec_ns as qdec, - qspi_ns as qspi, - regulators_ns as regulators, - reset_ns as reset, - rtc_ns as rtc, - saadc_ns as saadc, - spim_ns as spim, - spis_ns as spis, - spu_s as spu, - tad_s as tad, - timer_ns as timer, - twim_ns as twim, - twis_ns as twis, - uarte_ns as uarte, - uicr_s as uicr, - usbd_ns as usbd, - usbregulator_ns as usbregulator, - vmc_ns as vmc, - wdt_ns as wdt, - }; - - /// Non-Secure mode (NS) peripherals - pub mod ns { - #[cfg(feature = "nrf5340-app-ns")] - #[doc(no_inline)] - pub use nrf_pac::{ - CLOCK_NS as CLOCK, - COMP_NS as COMP, - CTRLAP_NS as CTRLAP, - DCNF_NS as DCNF, - DPPIC_NS as DPPIC, - EGU0_NS as EGU0, - EGU1_NS as EGU1, - EGU2_NS as EGU2, - EGU3_NS as EGU3, - EGU4_NS as EGU4, - EGU5_NS as EGU5, - FPU_NS as FPU, - GPIOTE1_NS as GPIOTE1, - I2S0_NS as I2S0, - IPC_NS as IPC, - KMU_NS as KMU, - LPCOMP_NS as LPCOMP, - MUTEX_NS as MUTEX, - NFCT_NS as NFCT, - NVMC_NS as NVMC, - OSCILLATORS_NS as OSCILLATORS, - P0_NS as P0, - P1_NS as P1, - PDM0_NS as PDM0, - POWER_NS as POWER, - PWM0_NS as PWM0, - PWM1_NS as PWM1, - PWM2_NS as PWM2, - PWM3_NS as PWM3, - QDEC0_NS as QDEC0, - QDEC1_NS as QDEC1, - QSPI_NS as QSPI, - REGULATORS_NS as REGULATORS, - RESET_NS as RESET, - RTC0_NS as RTC0, - RTC1_NS as RTC1, - SAADC_NS as SAADC, - SPIM0_NS as SPIM0, - SPIM1_NS as SPIM1, - SPIM2_NS as SPIM2, - SPIM3_NS as SPIM3, - SPIM4_NS as SPIM4, - SPIS0_NS as SPIS0, - SPIS1_NS as SPIS1, - SPIS2_NS as SPIS2, - SPIS3_NS as SPIS3, - TIMER0_NS as TIMER0, - TIMER1_NS as TIMER1, - TIMER2_NS as TIMER2, - TWIM0_NS as TWIM0, - TWIM1_NS as TWIM1, - TWIM2_NS as TWIM2, - TWIM3_NS as TWIM3, - TWIS0_NS as TWIS0, - TWIS1_NS as TWIS1, - TWIS2_NS as TWIS2, - TWIS3_NS as TWIS3, - UARTE0_NS as UARTE0, - UARTE1_NS as UARTE1, - UARTE2_NS as UARTE2, - UARTE3_NS as UARTE3, - USBD_NS as USBD, - USBREGULATOR_NS as USBREGULATOR, - VMC_NS as VMC, - WDT0_NS as WDT0, - WDT1_NS as WDT1, - }; - } - - /// Secure mode (S) peripherals - pub mod s { - #[cfg(feature = "nrf5340-app-s")] - #[doc(no_inline)] - pub use nrf_pac::{ - CACHEDATA_S as CACHEDATA, - CACHEINFO_S as CACHEINFO, - CACHE_S as CACHE, - CLOCK_S as CLOCK, - COMP_S as COMP, - CRYPTOCELL_S as CRYPTOCELL, - CTI_S as CTI, - CTRLAP_S as CTRLAP, - DCNF_S as DCNF, - DPPIC_S as DPPIC, - EGU0_S as EGU0, - EGU1_S as EGU1, - EGU2_S as EGU2, - EGU3_S as EGU3, - EGU4_S as EGU4, - EGU5_S as EGU5, - FICR_S as FICR, - FPU_S as FPU, - GPIOTE0_S as GPIOTE0, - I2S0_S as I2S0, - IPC_S as IPC, - KMU_S as KMU, - LPCOMP_S as LPCOMP, - MUTEX_S as MUTEX, - NFCT_S as NFCT, - NVMC_S as NVMC, - OSCILLATORS_S as OSCILLATORS, - P0_S as P0, - P1_S as P1, - PDM0_S as PDM0, - POWER_S as POWER, - PWM0_S as PWM0, - PWM1_S as PWM1, - PWM2_S as PWM2, - PWM3_S as PWM3, - QDEC0_S as QDEC0, - QDEC1_S as QDEC1, - QSPI_S as QSPI, - REGULATORS_S as REGULATORS, - RESET_S as RESET, - RTC0_S as RTC0, - RTC1_S as RTC1, - SAADC_S as SAADC, - SPIM0_S as SPIM0, - SPIM1_S as SPIM1, - SPIM2_S as SPIM2, - SPIM3_S as SPIM3, - SPIM4_S as SPIM4, - SPIS0_S as SPIS0, - SPIS1_S as SPIS1, - SPIS2_S as SPIS2, - SPIS3_S as SPIS3, - SPU_S as SPU, - TAD_S as TAD, - TIMER0_S as TIMER0, - TIMER1_S as TIMER1, - TIMER2_S as TIMER2, - TWIM0_S as TWIM0, - TWIM1_S as TWIM1, - TWIM2_S as TWIM2, - TWIM3_S as TWIM3, - TWIS0_S as TWIS0, - TWIS1_S as TWIS1, - TWIS2_S as TWIS2, - TWIS3_S as TWIS3, - UARTE0_S as UARTE0, - UARTE1_S as UARTE1, - UARTE2_S as UARTE2, - UARTE3_S as UARTE3, - UICR_S as UICR, - USBD_S as USBD, - USBREGULATOR_S as USBREGULATOR, - VMC_S as VMC, - WDT0_S as WDT0, - WDT1_S as WDT1, - }; - } + pub use nrf_pac::*; #[cfg(feature = "_ns")] - pub use ns::*; + #[doc(no_inline)] + pub use nrf_pac::{ + CLOCK_NS as CLOCK, + COMP_NS as COMP, + CTRLAP_NS as CTRLAP, + DCNF_NS as DCNF, + DPPIC_NS as DPPIC, + EGU0_NS as EGU0, + EGU1_NS as EGU1, + EGU2_NS as EGU2, + EGU3_NS as EGU3, + EGU4_NS as EGU4, + EGU5_NS as EGU5, + FPU_NS as FPU, + GPIOTE1_NS as GPIOTE1, + I2S0_NS as I2S0, + IPC_NS as IPC, + KMU_NS as KMU, + LPCOMP_NS as LPCOMP, + MUTEX_NS as MUTEX, + NFCT_NS as NFCT, + NVMC_NS as NVMC, + OSCILLATORS_NS as OSCILLATORS, + P0_NS as P0, + P1_NS as P1, + PDM0_NS as PDM0, + POWER_NS as POWER, + PWM0_NS as PWM0, + PWM1_NS as PWM1, + PWM2_NS as PWM2, + PWM3_NS as PWM3, + QDEC0_NS as QDEC0, + QDEC1_NS as QDEC1, + QSPI_NS as QSPI, + REGULATORS_NS as REGULATORS, + RESET_NS as RESET, + RTC0_NS as RTC0, + RTC1_NS as RTC1, + SAADC_NS as SAADC, + SPIM0_NS as SPIM0, + SPIM1_NS as SPIM1, + SPIM2_NS as SPIM2, + SPIM3_NS as SPIM3, + SPIM4_NS as SPIM4, + SPIS0_NS as SPIS0, + SPIS1_NS as SPIS1, + SPIS2_NS as SPIS2, + SPIS3_NS as SPIS3, + TIMER0_NS as TIMER0, + TIMER1_NS as TIMER1, + TIMER2_NS as TIMER2, + TWIM0_NS as TWIM0, + TWIM1_NS as TWIM1, + TWIM2_NS as TWIM2, + TWIM3_NS as TWIM3, + TWIS0_NS as TWIS0, + TWIS1_NS as TWIS1, + TWIS2_NS as TWIS2, + TWIS3_NS as TWIS3, + UARTE0_NS as UARTE0, + UARTE1_NS as UARTE1, + UARTE2_NS as UARTE2, + UARTE3_NS as UARTE3, + USBD_NS as USBD, + USBREGULATOR_NS as USBREGULATOR, + VMC_NS as VMC, + WDT0_NS as WDT0, + WDT1_NS as WDT1, + }; + #[cfg(feature = "_s")] - pub use s::*; + #[doc(no_inline)] + pub use nrf_pac::{ + CACHEDATA_S as CACHEDATA, + CACHEINFO_S as CACHEINFO, + CACHE_S as CACHE, + CLOCK_S as CLOCK, + COMP_S as COMP, + CRYPTOCELL_S as CRYPTOCELL, + CTI_S as CTI, + CTRLAP_S as CTRLAP, + DCNF_S as DCNF, + DPPIC_S as DPPIC, + EGU0_S as EGU0, + EGU1_S as EGU1, + EGU2_S as EGU2, + EGU3_S as EGU3, + EGU4_S as EGU4, + EGU5_S as EGU5, + FICR_S as FICR, + FPU_S as FPU, + GPIOTE0_S as GPIOTE0, + I2S0_S as I2S0, + IPC_S as IPC, + KMU_S as KMU, + LPCOMP_S as LPCOMP, + MUTEX_S as MUTEX, + NFCT_S as NFCT, + NVMC_S as NVMC, + OSCILLATORS_S as OSCILLATORS, + P0_S as P0, + P1_S as P1, + PDM0_S as PDM0, + POWER_S as POWER, + PWM0_S as PWM0, + PWM1_S as PWM1, + PWM2_S as PWM2, + PWM3_S as PWM3, + QDEC0_S as QDEC0, + QDEC1_S as QDEC1, + QSPI_S as QSPI, + REGULATORS_S as REGULATORS, + RESET_S as RESET, + RTC0_S as RTC0, + RTC1_S as RTC1, + SAADC_S as SAADC, + SPIM0_S as SPIM0, + SPIM1_S as SPIM1, + SPIM2_S as SPIM2, + SPIM3_S as SPIM3, + SPIM4_S as SPIM4, + SPIS0_S as SPIS0, + SPIS1_S as SPIS1, + SPIS2_S as SPIS2, + SPIS3_S as SPIS3, + SPU_S as SPU, + TAD_S as TAD, + TIMER0_S as TIMER0, + TIMER1_S as TIMER1, + TIMER2_S as TIMER2, + TWIM0_S as TWIM0, + TWIM1_S as TWIM1, + TWIM2_S as TWIM2, + TWIM3_S as TWIM3, + TWIS0_S as TWIS0, + TWIS1_S as TWIS1, + TWIS2_S as TWIS2, + TWIS3_S as TWIS3, + UARTE0_S as UARTE0, + UARTE1_S as UARTE1, + UARTE2_S as UARTE2, + UARTE3_S as UARTE3, + UICR_S as UICR, + USBD_S as USBD, + USBREGULATOR_S as USBREGULATOR, + VMC_S as VMC, + WDT0_S as WDT0, + WDT1_S as WDT1, + }; } /// The maximum buffer size that the EasyDMA can send/recv in one operation. diff --git a/embassy-nrf/src/chips/nrf5340_net.rs b/embassy-nrf/src/chips/nrf5340_net.rs index 00ff5fea6..6c6ac3fbb 100644 --- a/embassy-nrf/src/chips/nrf5340_net.rs +++ b/embassy-nrf/src/chips/nrf5340_net.rs @@ -2,55 +2,10 @@ #[allow(unused_imports)] #[rustfmt::skip] pub mod pac { - // The nRF5340 has a secure and non-secure (NS) mode. - // To avoid cfg spam, we remove _ns or _s suffixes here. - - #[cfg(feature="rt")] - pub use nrf_pac::NVIC_PRIO_BITS; - pub use nrf_pac::{common, shared}; - - #[cfg(feature="rt")] - #[doc(no_inline)] - pub use nrf_pac::interrupt; + pub use nrf_pac::*; #[doc(no_inline)] pub use nrf_pac::{ - Interrupt, - - aar_ns as aar, - acl_ns as acl, - appmutex_ns as appmutex, - ccm_ns as ccm, - clock_ns as clock, - cti_ns as cti, - ctrlap_ns as ctrlap, - dcnf_ns as dcnf, - dppic_ns as dppic, - ecb_ns as ecb, - egu_ns as egu, - ficr_ns as ficr, - gpiote_ns as gpiote, - ipc_ns as ipc, - nvmc_ns as nvmc, - gpio_ns as gpio, - power_ns as power, - radio_ns as radio, - reset_ns as reset, - rng_ns as rng, - rtc_ns as rtc, - spim_ns as spim, - spis_ns as spis, - swi_ns as swi, - temp_ns as temp, - timer_ns as timer, - twim_ns as twim, - twis_ns as twis, - uarte_ns as uarte, - uicr_ns as uicr, - vmc_ns as vmc, - vreqctrl_ns as vreqctrl, - wdt_ns as wdt, - AAR_NS as AAR, ACL_NS as ACL, APPMUTEX_NS as APPMUTEX, @@ -93,7 +48,6 @@ pub mod pac { VREQCTRL_NS as VREQCTRL, WDT_NS as WDT, }; - } /// The maximum buffer size that the EasyDMA can send/recv in one operation. diff --git a/embassy-nrf/src/chips/nrf9120.rs b/embassy-nrf/src/chips/nrf9120.rs index b89570dcd..b02b8c6d8 100644 --- a/embassy-nrf/src/chips/nrf9120.rs +++ b/embassy-nrf/src/chips/nrf9120.rs @@ -2,179 +2,124 @@ #[allow(unused_imports)] #[rustfmt::skip] pub mod pac { - // The nRF9120 has a secure and non-secure (NS) mode. - // To avoid cfg spam, we remove _ns or _s suffixes here. - - #[cfg(feature="rt")] - pub use nrf_pac::NVIC_PRIO_BITS; - pub use nrf_pac::{common, shared}; - - #[cfg(feature="rt")] - #[doc(no_inline)] - pub use nrf_pac::interrupt; - - #[doc(no_inline)] - pub use nrf_pac::{ - Interrupt, - - cc_host_rgf_s as cc_host_rgf, - clock_ns as clock, - cryptocell_s as cryptocell, - ctrl_ap_peri_s as ctrl_ap_peri, - dppic_ns as dppic, - egu_ns as egu, - ficr_s as ficr, - fpu_ns as fpu, - gpiote_s as gpiote, - i2s_ns as i2s, - ipc_ns as ipc, - kmu_ns as kmu, - nvmc_ns as nvmc, - gpio_ns as gpio, - pdm_ns as pdm, - power_ns as power, - pwm_ns as pwm, - regulators_ns as regulators, - rtc_ns as rtc, - saadc_ns as saadc, - spim_ns as spim, - spis_ns as spis, - spu_s as spu, - tad_s as tad, - timer_ns as timer, - twim_ns as twim, - twis_ns as twis, - uarte_ns as uarte, - uicr_s as uicr, - vmc_ns as vmc, - wdt_ns as wdt, - }; - - /// Non-Secure mode (NS) peripherals - pub mod ns { - #[doc(no_inline)] - pub use nrf_pac::{ - CLOCK_NS as CLOCK, - DPPIC_NS as DPPIC, - EGU0_NS as EGU0, - EGU1_NS as EGU1, - EGU2_NS as EGU2, - EGU3_NS as EGU3, - EGU4_NS as EGU4, - EGU5_NS as EGU5, - FPU_NS as FPU, - GPIOTE1_NS as GPIOTE1, - I2S_NS as I2S, - IPC_NS as IPC, - KMU_NS as KMU, - NVMC_NS as NVMC, - P0_NS as P0, - PDM_NS as PDM, - POWER_NS as POWER, - PWM0_NS as PWM0, - PWM1_NS as PWM1, - PWM2_NS as PWM2, - PWM3_NS as PWM3, - REGULATORS_NS as REGULATORS, - RTC0_NS as RTC0, - RTC1_NS as RTC1, - SAADC_NS as SAADC, - SPIM0_NS as SPIM0, - SPIM1_NS as SPIM1, - SPIM2_NS as SPIM2, - SPIM3_NS as SPIM3, - SPIS0_NS as SPIS0, - SPIS1_NS as SPIS1, - SPIS2_NS as SPIS2, - SPIS3_NS as SPIS3, - TIMER0_NS as TIMER0, - TIMER1_NS as TIMER1, - TIMER2_NS as TIMER2, - TWIM0_NS as TWIM0, - TWIM1_NS as TWIM1, - TWIM2_NS as TWIM2, - TWIM3_NS as TWIM3, - TWIS0_NS as TWIS0, - TWIS1_NS as TWIS1, - TWIS2_NS as TWIS2, - TWIS3_NS as TWIS3, - UARTE0_NS as UARTE0, - UARTE1_NS as UARTE1, - UARTE2_NS as UARTE2, - UARTE3_NS as UARTE3, - VMC_NS as VMC, - WDT_NS as WDT, - }; - } - - /// Secure mode (S) peripherals - pub mod s { - #[doc(no_inline)] - pub use nrf_pac::{ - CC_HOST_RGF_S as CC_HOST_RGF, - CLOCK_S as CLOCK, - CRYPTOCELL_S as CRYPTOCELL, - CTRL_AP_PERI_S as CTRL_AP_PERI, - DPPIC_S as DPPIC, - EGU0_S as EGU0, - EGU1_S as EGU1, - EGU2_S as EGU2, - EGU3_S as EGU3, - EGU4_S as EGU4, - EGU5_S as EGU5, - FICR_S as FICR, - FPU_NS as FPU, - GPIOTE0_S as GPIOTE0, - I2S_S as I2S, - IPC_S as IPC, - KMU_S as KMU, - NVMC_S as NVMC, - P0_S as P0, - PDM_S as PDM, - POWER_S as POWER, - PWM0_S as PWM0, - PWM1_S as PWM1, - PWM2_S as PWM2, - PWM3_S as PWM3, - REGULATORS_S as REGULATORS, - RTC0_S as RTC0, - RTC1_S as RTC1, - SAADC_S as SAADC, - SPIM0_S as SPIM0, - SPIM1_S as SPIM1, - SPIM2_S as SPIM2, - SPIM3_S as SPIM3, - SPIS0_S as SPIS0, - SPIS1_S as SPIS1, - SPIS2_S as SPIS2, - SPIS3_S as SPIS3, - SPU_S as SPU, - TAD_S as TAD, - TIMER0_S as TIMER0, - TIMER1_S as TIMER1, - TIMER2_S as TIMER2, - TWIM0_S as TWIM0, - TWIM1_S as TWIM1, - TWIM2_S as TWIM2, - TWIM3_S as TWIM3, - TWIS0_S as TWIS0, - TWIS1_S as TWIS1, - TWIS2_S as TWIS2, - TWIS3_S as TWIS3, - UARTE0_S as UARTE0, - UARTE1_S as UARTE1, - UARTE2_S as UARTE2, - UARTE3_S as UARTE3, - UICR_S as UICR, - VMC_S as VMC, - WDT_S as WDT, - }; - } + pub use nrf_pac::*; #[cfg(feature = "_ns")] - pub use ns::*; + #[doc(no_inline)] + pub use nrf_pac::{ + CLOCK_NS as CLOCK, + DPPIC_NS as DPPIC, + EGU0_NS as EGU0, + EGU1_NS as EGU1, + EGU2_NS as EGU2, + EGU3_NS as EGU3, + EGU4_NS as EGU4, + EGU5_NS as EGU5, + FPU_NS as FPU, + GPIOTE1_NS as GPIOTE1, + I2S_NS as I2S, + IPC_NS as IPC, + KMU_NS as KMU, + NVMC_NS as NVMC, + P0_NS as P0, + PDM_NS as PDM, + POWER_NS as POWER, + PWM0_NS as PWM0, + PWM1_NS as PWM1, + PWM2_NS as PWM2, + PWM3_NS as PWM3, + REGULATORS_NS as REGULATORS, + RTC0_NS as RTC0, + RTC1_NS as RTC1, + SAADC_NS as SAADC, + SPIM0_NS as SPIM0, + SPIM1_NS as SPIM1, + SPIM2_NS as SPIM2, + SPIM3_NS as SPIM3, + SPIS0_NS as SPIS0, + SPIS1_NS as SPIS1, + SPIS2_NS as SPIS2, + SPIS3_NS as SPIS3, + TIMER0_NS as TIMER0, + TIMER1_NS as TIMER1, + TIMER2_NS as TIMER2, + TWIM0_NS as TWIM0, + TWIM1_NS as TWIM1, + TWIM2_NS as TWIM2, + TWIM3_NS as TWIM3, + TWIS0_NS as TWIS0, + TWIS1_NS as TWIS1, + TWIS2_NS as TWIS2, + TWIS3_NS as TWIS3, + UARTE0_NS as UARTE0, + UARTE1_NS as UARTE1, + UARTE2_NS as UARTE2, + UARTE3_NS as UARTE3, + VMC_NS as VMC, + WDT_NS as WDT, + }; + #[cfg(feature = "_s")] - pub use s::*; + #[doc(no_inline)] + pub use nrf_pac::{ + CC_HOST_RGF_S as CC_HOST_RGF, + CLOCK_S as CLOCK, + CRYPTOCELL_S as CRYPTOCELL, + CTRL_AP_PERI_S as CTRL_AP_PERI, + DPPIC_S as DPPIC, + EGU0_S as EGU0, + EGU1_S as EGU1, + EGU2_S as EGU2, + EGU3_S as EGU3, + EGU4_S as EGU4, + EGU5_S as EGU5, + FICR_S as FICR, + FPU_NS as FPU, + GPIOTE0_S as GPIOTE0, + I2S_S as I2S, + IPC_S as IPC, + KMU_S as KMU, + NVMC_S as NVMC, + P0_S as P0, + PDM_S as PDM, + POWER_S as POWER, + PWM0_S as PWM0, + PWM1_S as PWM1, + PWM2_S as PWM2, + PWM3_S as PWM3, + REGULATORS_S as REGULATORS, + RTC0_S as RTC0, + RTC1_S as RTC1, + SAADC_S as SAADC, + SPIM0_S as SPIM0, + SPIM1_S as SPIM1, + SPIM2_S as SPIM2, + SPIM3_S as SPIM3, + SPIS0_S as SPIS0, + SPIS1_S as SPIS1, + SPIS2_S as SPIS2, + SPIS3_S as SPIS3, + SPU_S as SPU, + TAD_S as TAD, + TIMER0_S as TIMER0, + TIMER1_S as TIMER1, + TIMER2_S as TIMER2, + TWIM0_S as TWIM0, + TWIM1_S as TWIM1, + TWIM2_S as TWIM2, + TWIM3_S as TWIM3, + TWIS0_S as TWIS0, + TWIS1_S as TWIS1, + TWIS2_S as TWIS2, + TWIS3_S as TWIS3, + UARTE0_S as UARTE0, + UARTE1_S as UARTE1, + UARTE2_S as UARTE2, + UARTE3_S as UARTE3, + UICR_S as UICR, + VMC_S as VMC, + WDT_S as WDT, + }; } /// The maximum buffer size that the EasyDMA can send/recv in one operation. @@ -295,30 +240,30 @@ embassy_hal_internal::peripherals! { EGU5, } -impl_uarte!(SERIAL0, UARTE0, SPIM0_SPIS0_TWIM0_TWIS0_UARTE0); -impl_uarte!(SERIAL1, UARTE1, SPIM1_SPIS1_TWIM1_TWIS1_UARTE1); -impl_uarte!(SERIAL2, UARTE2, SPIM2_SPIS2_TWIM2_TWIS2_UARTE2); -impl_uarte!(SERIAL3, UARTE3, SPIM3_SPIS3_TWIM3_TWIS3_UARTE3); +impl_uarte!(SERIAL0, UARTE0, SERIAL0); +impl_uarte!(SERIAL1, UARTE1, SERIAL1); +impl_uarte!(SERIAL2, UARTE2, SERIAL2); +impl_uarte!(SERIAL3, UARTE3, SERIAL3); -impl_spim!(SERIAL0, SPIM0, SPIM0_SPIS0_TWIM0_TWIS0_UARTE0); -impl_spim!(SERIAL1, SPIM1, SPIM1_SPIS1_TWIM1_TWIS1_UARTE1); -impl_spim!(SERIAL2, SPIM2, SPIM2_SPIS2_TWIM2_TWIS2_UARTE2); -impl_spim!(SERIAL3, SPIM3, SPIM3_SPIS3_TWIM3_TWIS3_UARTE3); +impl_spim!(SERIAL0, SPIM0, SERIAL0); +impl_spim!(SERIAL1, SPIM1, SERIAL1); +impl_spim!(SERIAL2, SPIM2, SERIAL2); +impl_spim!(SERIAL3, SPIM3, SERIAL3); -impl_spis!(SERIAL0, SPIS0, SPIM0_SPIS0_TWIM0_TWIS0_UARTE0); -impl_spis!(SERIAL1, SPIS1, SPIM1_SPIS1_TWIM1_TWIS1_UARTE1); -impl_spis!(SERIAL2, SPIS2, SPIM2_SPIS2_TWIM2_TWIS2_UARTE2); -impl_spis!(SERIAL3, SPIS3, SPIM3_SPIS3_TWIM3_TWIS3_UARTE3); +impl_spis!(SERIAL0, SPIS0, SERIAL0); +impl_spis!(SERIAL1, SPIS1, SERIAL1); +impl_spis!(SERIAL2, SPIS2, SERIAL2); +impl_spis!(SERIAL3, SPIS3, SERIAL3); -impl_twim!(SERIAL0, TWIM0, SPIM0_SPIS0_TWIM0_TWIS0_UARTE0); -impl_twim!(SERIAL1, TWIM1, SPIM1_SPIS1_TWIM1_TWIS1_UARTE1); -impl_twim!(SERIAL2, TWIM2, SPIM2_SPIS2_TWIM2_TWIS2_UARTE2); -impl_twim!(SERIAL3, TWIM3, SPIM3_SPIS3_TWIM3_TWIS3_UARTE3); +impl_twim!(SERIAL0, TWIM0, SERIAL0); +impl_twim!(SERIAL1, TWIM1, SERIAL1); +impl_twim!(SERIAL2, TWIM2, SERIAL2); +impl_twim!(SERIAL3, TWIM3, SERIAL3); -impl_twis!(SERIAL0, TWIS0, SPIM0_SPIS0_TWIM0_TWIS0_UARTE0); -impl_twis!(SERIAL1, TWIS1, SPIM1_SPIS1_TWIM1_TWIS1_UARTE1); -impl_twis!(SERIAL2, TWIS2, SPIM2_SPIS2_TWIM2_TWIS2_UARTE2); -impl_twis!(SERIAL3, TWIS3, SPIM3_SPIS3_TWIM3_TWIS3_UARTE3); +impl_twis!(SERIAL0, TWIS0, SERIAL0); +impl_twis!(SERIAL1, TWIS1, SERIAL1); +impl_twis!(SERIAL2, TWIS2, SERIAL2); +impl_twis!(SERIAL3, TWIS3, SERIAL3); impl_pwm!(PWM0, PWM0, PWM0); impl_pwm!(PWM1, PWM1, PWM1); @@ -400,10 +345,10 @@ impl_egu!(EGU5, EGU5, EGU5); embassy_hal_internal::interrupt_mod!( SPU, CLOCK_POWER, - SPIM0_SPIS0_TWIM0_TWIS0_UARTE0, - SPIM1_SPIS1_TWIM1_TWIS1_UARTE1, - SPIM2_SPIS2_TWIM2_TWIS2_UARTE2, - SPIM3_SPIS3_TWIM3_TWIS3_UARTE3, + SERIAL0, + SERIAL1, + SERIAL2, + SERIAL3, GPIOTE0, SAADC, TIMER0, @@ -421,8 +366,8 @@ embassy_hal_internal::interrupt_mod!( PWM0, PWM1, PWM2, - PDM, PWM3, + PDM, I2S, IPC, FPU, diff --git a/embassy-nrf/src/chips/nrf9160.rs b/embassy-nrf/src/chips/nrf9160.rs index dba3d1ef5..b0981e3b5 100644 --- a/embassy-nrf/src/chips/nrf9160.rs +++ b/embassy-nrf/src/chips/nrf9160.rs @@ -2,179 +2,124 @@ #[allow(unused_imports)] #[rustfmt::skip] pub mod pac { - // The nRF9160 has a secure and non-secure (NS) mode. - // To avoid cfg spam, we remove _ns or _s suffixes here. - - #[cfg(feature="rt")] - pub use nrf_pac::NVIC_PRIO_BITS; - pub use nrf_pac::{common, shared}; - - #[cfg(feature="rt")] - #[doc(no_inline)] - pub use nrf_pac::interrupt; - - #[doc(no_inline)] - pub use nrf_pac::{ - Interrupt, - - cc_host_rgf_s as cc_host_rgf, - clock_ns as clock, - cryptocell_s as cryptocell, - ctrl_ap_peri_s as ctrl_ap_peri, - dppic_ns as dppic, - egu_ns as egu, - ficr_s as ficr, - fpu_ns as fpu, - gpiote_s as gpiote, - i2s_ns as i2s, - ipc_ns as ipc, - kmu_ns as kmu, - nvmc_ns as nvmc, - gpio_ns as gpio, - pdm_ns as pdm, - power_ns as power, - pwm_ns as pwm, - regulators_ns as regulators, - rtc_ns as rtc, - saadc_ns as saadc, - spim_ns as spim, - spis_ns as spis, - spu_s as spu, - tad_s as tad, - timer_ns as timer, - twim_ns as twim, - twis_ns as twis, - uarte_ns as uarte, - uicr_s as uicr, - vmc_ns as vmc, - wdt_ns as wdt, - }; - - /// Non-Secure mode (NS) peripherals - pub mod ns { - #[doc(no_inline)] - pub use nrf_pac::{ - CLOCK_NS as CLOCK, - DPPIC_NS as DPPIC, - EGU0_NS as EGU0, - EGU1_NS as EGU1, - EGU2_NS as EGU2, - EGU3_NS as EGU3, - EGU4_NS as EGU4, - EGU5_NS as EGU5, - FPU_NS as FPU, - GPIOTE1_NS as GPIOTE1, - I2S_NS as I2S, - IPC_NS as IPC, - KMU_NS as KMU, - NVMC_NS as NVMC, - P0_NS as P0, - PDM_NS as PDM, - POWER_NS as POWER, - PWM0_NS as PWM0, - PWM1_NS as PWM1, - PWM2_NS as PWM2, - PWM3_NS as PWM3, - REGULATORS_NS as REGULATORS, - RTC0_NS as RTC0, - RTC1_NS as RTC1, - SAADC_NS as SAADC, - SPIM0_NS as SPIM0, - SPIM1_NS as SPIM1, - SPIM2_NS as SPIM2, - SPIM3_NS as SPIM3, - SPIS0_NS as SPIS0, - SPIS1_NS as SPIS1, - SPIS2_NS as SPIS2, - SPIS3_NS as SPIS3, - TIMER0_NS as TIMER0, - TIMER1_NS as TIMER1, - TIMER2_NS as TIMER2, - TWIM0_NS as TWIM0, - TWIM1_NS as TWIM1, - TWIM2_NS as TWIM2, - TWIM3_NS as TWIM3, - TWIS0_NS as TWIS0, - TWIS1_NS as TWIS1, - TWIS2_NS as TWIS2, - TWIS3_NS as TWIS3, - UARTE0_NS as UARTE0, - UARTE1_NS as UARTE1, - UARTE2_NS as UARTE2, - UARTE3_NS as UARTE3, - VMC_NS as VMC, - WDT_NS as WDT, - }; - } - - /// Secure mode (S) peripherals - pub mod s { - #[doc(no_inline)] - pub use nrf_pac::{ - CC_HOST_RGF_S as CC_HOST_RGF, - CLOCK_S as CLOCK, - CRYPTOCELL_S as CRYPTOCELL, - CTRL_AP_PERI_S as CTRL_AP_PERI, - DPPIC_S as DPPIC, - EGU0_S as EGU0, - EGU1_S as EGU1, - EGU2_S as EGU2, - EGU3_S as EGU3, - EGU4_S as EGU4, - EGU5_S as EGU5, - FICR_S as FICR, - FPU_S as FPU, - GPIOTE0_S as GPIOTE0, - I2S_S as I2S, - IPC_S as IPC, - KMU_S as KMU, - NVMC_S as NVMC, - P0_S as P0, - PDM_S as PDM, - POWER_S as POWER, - PWM0_S as PWM0, - PWM1_S as PWM1, - PWM2_S as PWM2, - PWM3_S as PWM3, - REGULATORS_S as REGULATORS, - RTC0_S as RTC0, - RTC1_S as RTC1, - SAADC_S as SAADC, - SPIM0_S as SPIM0, - SPIM1_S as SPIM1, - SPIM2_S as SPIM2, - SPIM3_S as SPIM3, - SPIS0_S as SPIS0, - SPIS1_S as SPIS1, - SPIS2_S as SPIS2, - SPIS3_S as SPIS3, - SPU_S as SPU, - TAD_S as TAD, - TIMER0_S as TIMER0, - TIMER1_S as TIMER1, - TIMER2_S as TIMER2, - TWIM0_S as TWIM0, - TWIM1_S as TWIM1, - TWIM2_S as TWIM2, - TWIM3_S as TWIM3, - TWIS0_S as TWIS0, - TWIS1_S as TWIS1, - TWIS2_S as TWIS2, - TWIS3_S as TWIS3, - UARTE0_S as UARTE0, - UARTE1_S as UARTE1, - UARTE2_S as UARTE2, - UARTE3_S as UARTE3, - UICR_S as UICR, - VMC_S as VMC, - WDT_S as WDT, - }; - } + pub use nrf_pac::*; #[cfg(feature = "_ns")] - pub use ns::*; + #[doc(no_inline)] + pub use nrf_pac::{ + CLOCK_NS as CLOCK, + DPPIC_NS as DPPIC, + EGU0_NS as EGU0, + EGU1_NS as EGU1, + EGU2_NS as EGU2, + EGU3_NS as EGU3, + EGU4_NS as EGU4, + EGU5_NS as EGU5, + FPU_NS as FPU, + GPIOTE1_NS as GPIOTE1, + I2S_NS as I2S, + IPC_NS as IPC, + KMU_NS as KMU, + NVMC_NS as NVMC, + P0_NS as P0, + PDM_NS as PDM, + POWER_NS as POWER, + PWM0_NS as PWM0, + PWM1_NS as PWM1, + PWM2_NS as PWM2, + PWM3_NS as PWM3, + REGULATORS_NS as REGULATORS, + RTC0_NS as RTC0, + RTC1_NS as RTC1, + SAADC_NS as SAADC, + SPIM0_NS as SPIM0, + SPIM1_NS as SPIM1, + SPIM2_NS as SPIM2, + SPIM3_NS as SPIM3, + SPIS0_NS as SPIS0, + SPIS1_NS as SPIS1, + SPIS2_NS as SPIS2, + SPIS3_NS as SPIS3, + TIMER0_NS as TIMER0, + TIMER1_NS as TIMER1, + TIMER2_NS as TIMER2, + TWIM0_NS as TWIM0, + TWIM1_NS as TWIM1, + TWIM2_NS as TWIM2, + TWIM3_NS as TWIM3, + TWIS0_NS as TWIS0, + TWIS1_NS as TWIS1, + TWIS2_NS as TWIS2, + TWIS3_NS as TWIS3, + UARTE0_NS as UARTE0, + UARTE1_NS as UARTE1, + UARTE2_NS as UARTE2, + UARTE3_NS as UARTE3, + VMC_NS as VMC, + WDT_NS as WDT, + }; + #[cfg(feature = "_s")] - pub use s::*; + #[doc(no_inline)] + pub use nrf_pac::{ + CC_HOST_RGF_S as CC_HOST_RGF, + CLOCK_S as CLOCK, + CRYPTOCELL_S as CRYPTOCELL, + CTRL_AP_PERI_S as CTRL_AP_PERI, + DPPIC_S as DPPIC, + EGU0_S as EGU0, + EGU1_S as EGU1, + EGU2_S as EGU2, + EGU3_S as EGU3, + EGU4_S as EGU4, + EGU5_S as EGU5, + FICR_S as FICR, + FPU_S as FPU, + GPIOTE0_S as GPIOTE0, + I2S_S as I2S, + IPC_S as IPC, + KMU_S as KMU, + NVMC_S as NVMC, + P0_S as P0, + PDM_S as PDM, + POWER_S as POWER, + PWM0_S as PWM0, + PWM1_S as PWM1, + PWM2_S as PWM2, + PWM3_S as PWM3, + REGULATORS_S as REGULATORS, + RTC0_S as RTC0, + RTC1_S as RTC1, + SAADC_S as SAADC, + SPIM0_S as SPIM0, + SPIM1_S as SPIM1, + SPIM2_S as SPIM2, + SPIM3_S as SPIM3, + SPIS0_S as SPIS0, + SPIS1_S as SPIS1, + SPIS2_S as SPIS2, + SPIS3_S as SPIS3, + SPU_S as SPU, + TAD_S as TAD, + TIMER0_S as TIMER0, + TIMER1_S as TIMER1, + TIMER2_S as TIMER2, + TWIM0_S as TWIM0, + TWIM1_S as TWIM1, + TWIM2_S as TWIM2, + TWIM3_S as TWIM3, + TWIS0_S as TWIS0, + TWIS1_S as TWIS1, + TWIS2_S as TWIS2, + TWIS3_S as TWIS3, + UARTE0_S as UARTE0, + UARTE1_S as UARTE1, + UARTE2_S as UARTE2, + UARTE3_S as UARTE3, + UICR_S as UICR, + VMC_S as VMC, + WDT_S as WDT, + }; } /// The maximum buffer size that the EasyDMA can send/recv in one operation. @@ -295,30 +240,30 @@ embassy_hal_internal::peripherals! { EGU5, } -impl_uarte!(SERIAL0, UARTE0, UARTE0_SPIM0_SPIS0_TWIM0_TWIS0); -impl_uarte!(SERIAL1, UARTE1, UARTE1_SPIM1_SPIS1_TWIM1_TWIS1); -impl_uarte!(SERIAL2, UARTE2, UARTE2_SPIM2_SPIS2_TWIM2_TWIS2); -impl_uarte!(SERIAL3, UARTE3, UARTE3_SPIM3_SPIS3_TWIM3_TWIS3); +impl_uarte!(SERIAL0, UARTE0, SERIAL0); +impl_uarte!(SERIAL1, UARTE1, SERIAL1); +impl_uarte!(SERIAL2, UARTE2, SERIAL2); +impl_uarte!(SERIAL3, UARTE3, SERIAL3); -impl_spim!(SERIAL0, SPIM0, UARTE0_SPIM0_SPIS0_TWIM0_TWIS0); -impl_spim!(SERIAL1, SPIM1, UARTE1_SPIM1_SPIS1_TWIM1_TWIS1); -impl_spim!(SERIAL2, SPIM2, UARTE2_SPIM2_SPIS2_TWIM2_TWIS2); -impl_spim!(SERIAL3, SPIM3, UARTE3_SPIM3_SPIS3_TWIM3_TWIS3); +impl_spim!(SERIAL0, SPIM0, SERIAL0); +impl_spim!(SERIAL1, SPIM1, SERIAL1); +impl_spim!(SERIAL2, SPIM2, SERIAL2); +impl_spim!(SERIAL3, SPIM3, SERIAL3); -impl_spis!(SERIAL0, SPIS0, UARTE0_SPIM0_SPIS0_TWIM0_TWIS0); -impl_spis!(SERIAL1, SPIS1, UARTE1_SPIM1_SPIS1_TWIM1_TWIS1); -impl_spis!(SERIAL2, SPIS2, UARTE2_SPIM2_SPIS2_TWIM2_TWIS2); -impl_spis!(SERIAL3, SPIS3, UARTE3_SPIM3_SPIS3_TWIM3_TWIS3); +impl_spis!(SERIAL0, SPIS0, SERIAL0); +impl_spis!(SERIAL1, SPIS1, SERIAL1); +impl_spis!(SERIAL2, SPIS2, SERIAL2); +impl_spis!(SERIAL3, SPIS3, SERIAL3); -impl_twim!(SERIAL0, TWIM0, UARTE0_SPIM0_SPIS0_TWIM0_TWIS0); -impl_twim!(SERIAL1, TWIM1, UARTE1_SPIM1_SPIS1_TWIM1_TWIS1); -impl_twim!(SERIAL2, TWIM2, UARTE2_SPIM2_SPIS2_TWIM2_TWIS2); -impl_twim!(SERIAL3, TWIM3, UARTE3_SPIM3_SPIS3_TWIM3_TWIS3); +impl_twim!(SERIAL0, TWIM0, SERIAL0); +impl_twim!(SERIAL1, TWIM1, SERIAL1); +impl_twim!(SERIAL2, TWIM2, SERIAL2); +impl_twim!(SERIAL3, TWIM3, SERIAL3); -impl_twis!(SERIAL0, TWIS0, UARTE0_SPIM0_SPIS0_TWIM0_TWIS0); -impl_twis!(SERIAL1, TWIS1, UARTE1_SPIM1_SPIS1_TWIM1_TWIS1); -impl_twis!(SERIAL2, TWIS2, UARTE2_SPIM2_SPIS2_TWIM2_TWIS2); -impl_twis!(SERIAL3, TWIS3, UARTE3_SPIM3_SPIS3_TWIM3_TWIS3); +impl_twis!(SERIAL0, TWIS0, SERIAL0); +impl_twis!(SERIAL1, TWIS1, SERIAL1); +impl_twis!(SERIAL2, TWIS2, SERIAL2); +impl_twis!(SERIAL3, TWIS3, SERIAL3); impl_pwm!(PWM0, PWM0, PWM0); impl_pwm!(PWM1, PWM1, PWM1); @@ -400,10 +345,10 @@ impl_egu!(EGU5, EGU5, EGU5); embassy_hal_internal::interrupt_mod!( SPU, CLOCK_POWER, - UARTE0_SPIM0_SPIS0_TWIM0_TWIS0, - UARTE1_SPIM1_SPIS1_TWIM1_TWIS1, - UARTE2_SPIM2_SPIS2_TWIM2_TWIS2, - UARTE3_SPIM3_SPIS3_TWIM3_TWIS3, + SERIAL0, + SERIAL1, + SERIAL2, + SERIAL3, GPIOTE0, SAADC, TIMER0, @@ -421,8 +366,8 @@ embassy_hal_internal::interrupt_mod!( PWM0, PWM1, PWM2, - PDM, PWM3, + PDM, I2S, IPC, FPU, diff --git a/embassy-nrf/src/lib.rs b/embassy-nrf/src/lib.rs index 430b6fae7..8167b44f3 100644 --- a/embassy-nrf/src/lib.rs +++ b/embassy-nrf/src/lib.rs @@ -170,7 +170,7 @@ mod chip; /// /// bind_interrupts!(struct Irqs { /// SPIM3 => spim::InterruptHandler; -/// SPIM0_SPIS0_TWIM0_TWIS0_SPI0_TWI0 => twim::InterruptHandler; +/// TWISPI0 => twim::InterruptHandler; /// }); /// ``` diff --git a/embassy-nrf/src/time_driver.rs b/embassy-nrf/src/time_driver.rs index e39c4ed52..b6492ac97 100644 --- a/embassy-nrf/src/time_driver.rs +++ b/embassy-nrf/src/time_driver.rs @@ -132,7 +132,7 @@ impl RtcDriver { r.intenset().write(|w| { w.set_ovrflw(true); - w.set_compare3(true); + w.set_compare(3, true); }); r.tasks_clear().write_value(1); diff --git a/embassy-nrf/src/usb/vbus_detect.rs b/embassy-nrf/src/usb/vbus_detect.rs index 7f816a5ad..bdc088dcb 100644 --- a/embassy-nrf/src/usb/vbus_detect.rs +++ b/embassy-nrf/src/usb/vbus_detect.rs @@ -29,14 +29,14 @@ pub trait VbusDetect { } #[cfg(not(feature = "_nrf5340"))] -type UsbRegIrq = interrupt::typelevel::POWER_CLOCK; +type UsbRegIrq = interrupt::typelevel::CLOCK_POWER; #[cfg(feature = "_nrf5340")] type UsbRegIrq = interrupt::typelevel::USBREGULATOR; #[cfg(not(feature = "_nrf5340"))] const USB_REG_PERI: pac::power::Power = pac::POWER; #[cfg(feature = "_nrf5340")] -const USB_REG_PERI: pac::usbregulator::Usbregulator = pac::USBREGULATOR; +const USB_REG_PERI: pac::usbreg::Usbreg = pac::USBREGULATOR; /// Interrupt handler. pub struct InterruptHandler { diff --git a/examples/nrf52840-rtic/src/bin/blinky.rs b/examples/nrf52840-rtic/src/bin/blinky.rs index 060bb9ebc..5a074ea17 100644 --- a/examples/nrf52840-rtic/src/bin/blinky.rs +++ b/examples/nrf52840-rtic/src/bin/blinky.rs @@ -4,7 +4,7 @@ use {defmt_rtt as _, panic_probe as _}; -#[rtic::app(device = embassy_nrf, peripherals = false, dispatchers = [SWI0_EGU0, SWI1_EGU1])] +#[rtic::app(device = embassy_nrf, peripherals = false, dispatchers = [EGU0_SWI0, EGU1_SWI1])] mod app { use defmt::info; use embassy_nrf::gpio::{Level, Output, OutputDrive}; diff --git a/examples/nrf52840/src/bin/buffered_uart.rs b/examples/nrf52840/src/bin/buffered_uart.rs index 6ac72bcaf..77d017964 100644 --- a/examples/nrf52840/src/bin/buffered_uart.rs +++ b/examples/nrf52840/src/bin/buffered_uart.rs @@ -9,7 +9,7 @@ use embedded_io_async::Write; use {defmt_rtt as _, panic_probe as _}; bind_interrupts!(struct Irqs { - UARTE0_UART0 => buffered_uarte::InterruptHandler; + UARTE0 => buffered_uarte::InterruptHandler; }); #[embassy_executor::main] diff --git a/examples/nrf52840/src/bin/multiprio.rs b/examples/nrf52840/src/bin/multiprio.rs index 797be93a7..d58613da4 100644 --- a/examples/nrf52840/src/bin/multiprio.rs +++ b/examples/nrf52840/src/bin/multiprio.rs @@ -112,12 +112,12 @@ static EXECUTOR_MED: InterruptExecutor = InterruptExecutor::new(); static EXECUTOR_LOW: StaticCell = StaticCell::new(); #[interrupt] -unsafe fn SWI1_EGU1() { +unsafe fn EGU1_SWI1() { EXECUTOR_HIGH.on_interrupt() } #[interrupt] -unsafe fn SWI0_EGU0() { +unsafe fn EGU0_SWI0() { EXECUTOR_MED.on_interrupt() } @@ -127,14 +127,14 @@ fn main() -> ! { let _p = embassy_nrf::init(Default::default()); - // High-priority executor: SWI1_EGU1, priority level 6 - interrupt::SWI1_EGU1.set_priority(Priority::P6); - let spawner = EXECUTOR_HIGH.start(interrupt::SWI1_EGU1); + // High-priority executor: EGU1_SWI1, priority level 6 + interrupt::EGU1_SWI1.set_priority(Priority::P6); + let spawner = EXECUTOR_HIGH.start(interrupt::EGU1_SWI1); unwrap!(spawner.spawn(run_high())); - // Medium-priority executor: SWI0_EGU0, priority level 7 - interrupt::SWI0_EGU0.set_priority(Priority::P7); - let spawner = EXECUTOR_MED.start(interrupt::SWI0_EGU0); + // Medium-priority executor: EGU0_SWI0, priority level 7 + interrupt::EGU0_SWI0.set_priority(Priority::P7); + let spawner = EXECUTOR_MED.start(interrupt::EGU0_SWI0); unwrap!(spawner.spawn(run_med())); // Low priority executor: runs in thread mode, using WFE/SEV diff --git a/examples/nrf52840/src/bin/spis.rs b/examples/nrf52840/src/bin/spis.rs index 613cd37ab..4f28da07e 100644 --- a/examples/nrf52840/src/bin/spis.rs +++ b/examples/nrf52840/src/bin/spis.rs @@ -8,7 +8,7 @@ use embassy_nrf::{bind_interrupts, peripherals, spis}; use {defmt_rtt as _, panic_probe as _}; bind_interrupts!(struct Irqs { - SPIM2_SPIS2_SPI2 => spis::InterruptHandler; + SPI2 => spis::InterruptHandler; }); #[embassy_executor::main] diff --git a/examples/nrf52840/src/bin/twim.rs b/examples/nrf52840/src/bin/twim.rs index a9a0765e8..ceaafd784 100644 --- a/examples/nrf52840/src/bin/twim.rs +++ b/examples/nrf52840/src/bin/twim.rs @@ -14,7 +14,7 @@ use {defmt_rtt as _, panic_probe as _}; const ADDRESS: u8 = 0x50; bind_interrupts!(struct Irqs { - SPIM0_SPIS0_TWIM0_TWIS0_SPI0_TWI0 => twim::InterruptHandler; + TWISPI0 => twim::InterruptHandler; }); #[embassy_executor::main] diff --git a/examples/nrf52840/src/bin/twim_lowpower.rs b/examples/nrf52840/src/bin/twim_lowpower.rs index c743614b8..e2efbdd8d 100644 --- a/examples/nrf52840/src/bin/twim_lowpower.rs +++ b/examples/nrf52840/src/bin/twim_lowpower.rs @@ -19,7 +19,7 @@ use {defmt_rtt as _, panic_probe as _}; const ADDRESS: u8 = 0x50; bind_interrupts!(struct Irqs { - SPIM0_SPIS0_TWIM0_TWIS0_SPI0_TWI0 => twim::InterruptHandler; + TWISPI0 => twim::InterruptHandler; }); #[embassy_executor::main] diff --git a/examples/nrf52840/src/bin/twis.rs b/examples/nrf52840/src/bin/twis.rs index 88bd4cceb..856b34140 100644 --- a/examples/nrf52840/src/bin/twis.rs +++ b/examples/nrf52840/src/bin/twis.rs @@ -10,7 +10,7 @@ use embassy_nrf::{bind_interrupts, peripherals}; use {defmt_rtt as _, panic_probe as _}; bind_interrupts!(struct Irqs { - SPIM0_SPIS0_TWIM0_TWIS0_SPI0_TWI0 => twis::InterruptHandler; + TWISPI0 => twis::InterruptHandler; }); #[embassy_executor::main] diff --git a/examples/nrf52840/src/bin/uart.rs b/examples/nrf52840/src/bin/uart.rs index accaccea1..23154672f 100644 --- a/examples/nrf52840/src/bin/uart.rs +++ b/examples/nrf52840/src/bin/uart.rs @@ -7,7 +7,7 @@ use embassy_nrf::{bind_interrupts, peripherals, uarte}; use {defmt_rtt as _, panic_probe as _}; bind_interrupts!(struct Irqs { - UARTE0_UART0 => uarte::InterruptHandler; + UARTE0 => uarte::InterruptHandler; }); #[embassy_executor::main] diff --git a/examples/nrf52840/src/bin/uart_idle.rs b/examples/nrf52840/src/bin/uart_idle.rs index fa93bcf21..a42e84fa4 100644 --- a/examples/nrf52840/src/bin/uart_idle.rs +++ b/examples/nrf52840/src/bin/uart_idle.rs @@ -8,7 +8,7 @@ use embassy_nrf::{bind_interrupts, uarte}; use {defmt_rtt as _, panic_probe as _}; bind_interrupts!(struct Irqs { - UARTE0_UART0 => uarte::InterruptHandler; + UARTE0 => uarte::InterruptHandler; }); #[embassy_executor::main] diff --git a/examples/nrf52840/src/bin/uart_split.rs b/examples/nrf52840/src/bin/uart_split.rs index c7510a9a8..94af4be86 100644 --- a/examples/nrf52840/src/bin/uart_split.rs +++ b/examples/nrf52840/src/bin/uart_split.rs @@ -13,7 +13,7 @@ use {defmt_rtt as _, panic_probe as _}; static CHANNEL: Channel = Channel::new(); bind_interrupts!(struct Irqs { - UARTE0_UART0 => uarte::InterruptHandler; + UARTE0 => uarte::InterruptHandler; }); #[embassy_executor::main] diff --git a/examples/nrf52840/src/bin/usb_ethernet.rs b/examples/nrf52840/src/bin/usb_ethernet.rs index 82364ded8..88314b749 100644 --- a/examples/nrf52840/src/bin/usb_ethernet.rs +++ b/examples/nrf52840/src/bin/usb_ethernet.rs @@ -18,7 +18,7 @@ use {defmt_rtt as _, panic_probe as _}; bind_interrupts!(struct Irqs { USBD => usb::InterruptHandler; - POWER_CLOCK => usb::vbus_detect::InterruptHandler; + CLOCK_POWER => usb::vbus_detect::InterruptHandler; RNG => rng::InterruptHandler; }); diff --git a/examples/nrf52840/src/bin/usb_hid_keyboard.rs b/examples/nrf52840/src/bin/usb_hid_keyboard.rs index 3b752fd16..5a9dc90a2 100644 --- a/examples/nrf52840/src/bin/usb_hid_keyboard.rs +++ b/examples/nrf52840/src/bin/usb_hid_keyboard.rs @@ -21,7 +21,7 @@ use {defmt_rtt as _, panic_probe as _}; bind_interrupts!(struct Irqs { USBD => usb::InterruptHandler; - POWER_CLOCK => usb::vbus_detect::InterruptHandler; + CLOCK_POWER => usb::vbus_detect::InterruptHandler; }); static SUSPENDED: AtomicBool = AtomicBool::new(false); diff --git a/examples/nrf52840/src/bin/usb_hid_mouse.rs b/examples/nrf52840/src/bin/usb_hid_mouse.rs index 3f13a014e..80cda70e3 100644 --- a/examples/nrf52840/src/bin/usb_hid_mouse.rs +++ b/examples/nrf52840/src/bin/usb_hid_mouse.rs @@ -16,7 +16,7 @@ use {defmt_rtt as _, panic_probe as _}; bind_interrupts!(struct Irqs { USBD => usb::InterruptHandler; - POWER_CLOCK => usb::vbus_detect::InterruptHandler; + CLOCK_POWER => usb::vbus_detect::InterruptHandler; }); #[embassy_executor::main] diff --git a/examples/nrf52840/src/bin/usb_serial.rs b/examples/nrf52840/src/bin/usb_serial.rs index 30fe103ad..a534046d9 100644 --- a/examples/nrf52840/src/bin/usb_serial.rs +++ b/examples/nrf52840/src/bin/usb_serial.rs @@ -14,7 +14,7 @@ use {defmt_rtt as _, panic_probe as _}; bind_interrupts!(struct Irqs { USBD => usb::InterruptHandler; - POWER_CLOCK => usb::vbus_detect::InterruptHandler; + CLOCK_POWER => usb::vbus_detect::InterruptHandler; }); #[embassy_executor::main] diff --git a/examples/nrf52840/src/bin/usb_serial_multitask.rs b/examples/nrf52840/src/bin/usb_serial_multitask.rs index 05b5f0ec9..32fc5e094 100644 --- a/examples/nrf52840/src/bin/usb_serial_multitask.rs +++ b/examples/nrf52840/src/bin/usb_serial_multitask.rs @@ -14,7 +14,7 @@ use {defmt_rtt as _, panic_probe as _}; bind_interrupts!(struct Irqs { USBD => usb::InterruptHandler; - POWER_CLOCK => usb::vbus_detect::InterruptHandler; + CLOCK_POWER => usb::vbus_detect::InterruptHandler; }); type MyDriver = Driver<'static, peripherals::USBD, HardwareVbusDetect>; diff --git a/examples/nrf52840/src/bin/usb_serial_winusb.rs b/examples/nrf52840/src/bin/usb_serial_winusb.rs index 7c07158e0..0352f9c66 100644 --- a/examples/nrf52840/src/bin/usb_serial_winusb.rs +++ b/examples/nrf52840/src/bin/usb_serial_winusb.rs @@ -16,7 +16,7 @@ use {defmt_rtt as _, panic_probe as _}; bind_interrupts!(struct Irqs { USBD => usb::InterruptHandler; - POWER_CLOCK => usb::vbus_detect::InterruptHandler; + CLOCK_POWER => usb::vbus_detect::InterruptHandler; }); // This is a randomly generated GUID to allow clients on Windows to find our device diff --git a/examples/nrf9151/ns/src/bin/uart.rs b/examples/nrf9151/ns/src/bin/uart.rs index 2220dccfb..234ff35f2 100644 --- a/examples/nrf9151/ns/src/bin/uart.rs +++ b/examples/nrf9151/ns/src/bin/uart.rs @@ -7,7 +7,7 @@ use embassy_nrf::{bind_interrupts, peripherals, uarte}; use {defmt_rtt as _, panic_probe as _}; bind_interrupts!(struct Irqs { - SPIM0_SPIS0_TWIM0_TWIS0_UARTE0 => uarte::InterruptHandler; + SERIAL0 => uarte::InterruptHandler; }); #[embassy_executor::main] diff --git a/examples/nrf9160/src/bin/modem_tcp_client.rs b/examples/nrf9160/src/bin/modem_tcp_client.rs index 067ec4276..35900cdd8 100644 --- a/examples/nrf9160/src/bin/modem_tcp_client.rs +++ b/examples/nrf9160/src/bin/modem_tcp_client.rs @@ -28,7 +28,7 @@ fn IPC() { } bind_interrupts!(struct Irqs { - UARTE0_SPIM0_SPIS0_TWIM0_TWIS0 => buffered_uarte::InterruptHandler; + SERIAL0 => buffered_uarte::InterruptHandler; }); #[embassy_executor::task] diff --git a/tests/nrf/src/common.rs b/tests/nrf/src/common.rs index c588dabf5..ebd332d15 100644 --- a/tests/nrf/src/common.rs +++ b/tests/nrf/src/common.rs @@ -55,9 +55,9 @@ define_peris!( PIN_X = P0_13, UART0 = UARTE0, SPIM0 = TWISPI0, - @irq UART0 = {UARTE0_UART0 => uarte::InterruptHandler;}, - @irq UART0_BUFFERED = {UARTE0_UART0 => buffered_uarte::InterruptHandler;}, - @irq SPIM0 = {SPIM0_SPIS0_TWIM0_TWIS0_SPI0_TWI0 => spim::InterruptHandler;}, + @irq UART0 = {UARTE0 => uarte::InterruptHandler;}, + @irq UART0_BUFFERED = {UARTE0 => buffered_uarte::InterruptHandler;}, + @irq SPIM0 = {TWISPI0 => spim::InterruptHandler;}, ); #[cfg(feature = "nrf52833")] @@ -67,11 +67,11 @@ define_peris!( UART0 = UARTE0, UART1 = UARTE1, SPIM0 = TWISPI0, - @irq UART0 = {UARTE0_UART0 => uarte::InterruptHandler;}, + @irq UART0 = {UARTE0 => uarte::InterruptHandler;}, @irq UART1 = {UARTE1 => uarte::InterruptHandler;}, - @irq UART0_BUFFERED = {UARTE0_UART0 => buffered_uarte::InterruptHandler;}, + @irq UART0_BUFFERED = {UARTE0 => buffered_uarte::InterruptHandler;}, @irq UART1_BUFFERED = {UARTE1 => buffered_uarte::InterruptHandler;}, - @irq SPIM0 = {SPIM0_SPIS0_TWIM0_TWIS0_SPI0_TWI0 => spim::InterruptHandler;}, + @irq SPIM0 = {TWISPI0 => spim::InterruptHandler;}, ); #[cfg(feature = "nrf52840")] @@ -81,11 +81,11 @@ define_peris!( UART0 = UARTE0, UART1 = UARTE1, SPIM0 = TWISPI0, - @irq UART0 = {UARTE0_UART0 => uarte::InterruptHandler;}, + @irq UART0 = {UARTE0 => uarte::InterruptHandler;}, @irq UART1 = {UARTE1 => uarte::InterruptHandler;}, - @irq UART0_BUFFERED = {UARTE0_UART0 => buffered_uarte::InterruptHandler;}, + @irq UART0_BUFFERED = {UARTE0 => buffered_uarte::InterruptHandler;}, @irq UART1_BUFFERED = {UARTE1 => buffered_uarte::InterruptHandler;}, - @irq SPIM0 = {SPIM0_SPIS0_TWIM0_TWIS0_SPI0_TWI0 => spim::InterruptHandler;}, + @irq SPIM0 = {TWISPI0 => spim::InterruptHandler;}, ); #[cfg(feature = "nrf5340")] @@ -109,9 +109,9 @@ define_peris!( UART0 = SERIAL0, UART1 = SERIAL1, SPIM0 = SERIAL0, - @irq UART0 = {UARTE0_SPIM0_SPIS0_TWIM0_TWIS0 => uarte::InterruptHandler;}, - @irq UART1 = {UARTE1_SPIM1_SPIS1_TWIM1_TWIS1 => uarte::InterruptHandler;}, - @irq UART0_BUFFERED = {UARTE0_SPIM0_SPIS0_TWIM0_TWIS0 => buffered_uarte::InterruptHandler;}, - @irq UART1_BUFFERED = {UARTE1_SPIM1_SPIS1_TWIM1_TWIS1 => buffered_uarte::InterruptHandler;}, - @irq SPIM0 = {UARTE0_SPIM0_SPIS0_TWIM0_TWIS0 => spim::InterruptHandler;}, + @irq UART0 = {SERIAL0 => uarte::InterruptHandler;}, + @irq UART1 = {SERIAL1 => uarte::InterruptHandler;}, + @irq UART0_BUFFERED = {SERIAL0 => buffered_uarte::InterruptHandler;}, + @irq UART1_BUFFERED = {SERIAL1 => buffered_uarte::InterruptHandler;}, + @irq SPIM0 = {SERIAL0 => spim::InterruptHandler;}, ); From d592875ca6d4df2b126e67603d32ed7f3e71910b Mon Sep 17 00:00:00 2001 From: elagil Date: Sat, 16 Nov 2024 15:02:32 +0100 Subject: [PATCH 205/361] fix(SAI): disallow start without initial write --- embassy-stm32/src/dma/dma_bdma.rs | 3 --- embassy-stm32/src/sai/mod.rs | 27 ++++++++++++++------------- examples/stm32h7/src/bin/sai.rs | 1 - 3 files changed, 14 insertions(+), 17 deletions(-) diff --git a/embassy-stm32/src/dma/dma_bdma.rs b/embassy-stm32/src/dma/dma_bdma.rs index cdc603e2c..08c7a5508 100644 --- a/embassy-stm32/src/dma/dma_bdma.rs +++ b/embassy-stm32/src/dma/dma_bdma.rs @@ -828,7 +828,6 @@ impl<'a, W: Word> ReadableRingBuffer<'a, W> { /// You must call this after creating it for it to work. pub fn start(&mut self) { self.channel.start(); - self.clear(); } /// Clear all data in the ring buffer. @@ -981,7 +980,6 @@ impl<'a, W: Word> WritableRingBuffer<'a, W> { /// You must call this after creating it for it to work. pub fn start(&mut self) { self.channel.start(); - self.clear(); } /// Clear all data in the ring buffer. @@ -991,7 +989,6 @@ impl<'a, W: Word> WritableRingBuffer<'a, W> { /// Write elements directly to the raw buffer. /// This can be used to fill the buffer before starting the DMA transfer. - #[allow(dead_code)] pub fn write_immediate(&mut self, buf: &[W]) -> Result<(usize, usize), Error> { self.ringbuf.write_immediate(buf) } diff --git a/embassy-stm32/src/sai/mod.rs b/embassy-stm32/src/sai/mod.rs index 62b44b77f..057b21980 100644 --- a/embassy-stm32/src/sai/mod.rs +++ b/embassy-stm32/src/sai/mod.rs @@ -958,13 +958,14 @@ impl<'d, T: Instance, W: word::Word> Sai<'d, T, W> { } /// Start the SAI driver. - pub fn start(&mut self) { + /// + /// Only receivers can be started. Transmitters are started on the first writing operation. + pub fn start(&mut self) -> Result<(), Error> { match self.ring_buffer { - RingBuffer::Writable(ref mut rb) => { - rb.start(); - } + RingBuffer::Writable(_) => Err(Error::NotAReceiver), RingBuffer::Readable(ref mut rb) => { rb.start(); + Ok(()) } } } @@ -981,14 +982,6 @@ impl<'d, T: Instance, W: word::Word> Sai<'d, T, W> { rcc::enable_and_reset::(); } - /// Flush. - pub fn flush(&mut self) { - let ch = T::REGS.ch(self.sub_block as usize); - ch.cr1().modify(|w| w.set_saien(false)); - ch.cr2().modify(|w| w.set_fflush(true)); - ch.cr1().modify(|w| w.set_saien(true)); - } - /// Enable or disable mute. pub fn set_mute(&mut self, value: bool) { let ch = T::REGS.ch(self.sub_block as usize); @@ -1012,6 +1005,9 @@ impl<'d, T: Instance, W: word::Word> Sai<'d, T, W> { /// Write data to the SAI ringbuffer. /// + /// The first write starts the DMA after filling the ring buffer with the provided data. + /// This ensures that the DMA does not run before data is available in the ring buffer. + /// /// This appends the data to the buffer and returns immediately. The /// data will be transmitted in the background. /// @@ -1019,7 +1015,12 @@ impl<'d, T: Instance, W: word::Word> Sai<'d, T, W> { pub async fn write(&mut self, data: &[W]) -> Result<(), Error> { match &mut self.ring_buffer { RingBuffer::Writable(buffer) => { - buffer.write_exact(data).await?; + if buffer.is_running() { + buffer.write_exact(data).await?; + } else { + buffer.write_immediate(data)?; + buffer.start(); + } Ok(()) } _ => return Err(Error::NotATransmitter), diff --git a/examples/stm32h7/src/bin/sai.rs b/examples/stm32h7/src/bin/sai.rs index 04d14bd6b..0594c838a 100644 --- a/examples/stm32h7/src/bin/sai.rs +++ b/examples/stm32h7/src/bin/sai.rs @@ -108,7 +108,6 @@ async fn main(_spawner: Spawner) { let mut sai_receiver = Sai::new_synchronous(sub_block_rx, p.PE3, p.DMA1_CH1, rx_buffer, rx_config); sai_receiver.start(); - sai_transmitter.start(); let mut buf = [0u32; HALF_DMA_BUFFER_LENGTH]; From f2a46e2ac3d8764a205cdde20a1e2af3cbd7ec1e Mon Sep 17 00:00:00 2001 From: elagil Date: Sat, 16 Nov 2024 15:09:47 +0100 Subject: [PATCH 206/361] fix: unwrap sai receiver `start()` --- examples/stm32h7/src/bin/sai.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/stm32h7/src/bin/sai.rs b/examples/stm32h7/src/bin/sai.rs index 0594c838a..95ffe257a 100644 --- a/examples/stm32h7/src/bin/sai.rs +++ b/examples/stm32h7/src/bin/sai.rs @@ -107,7 +107,7 @@ async fn main(_spawner: Spawner) { let mut sai_receiver = Sai::new_synchronous(sub_block_rx, p.PE3, p.DMA1_CH1, rx_buffer, rx_config); - sai_receiver.start(); + sai_receiver.start().unwrap(); let mut buf = [0u32; HALF_DMA_BUFFER_LENGTH]; From 69cb30ebf3772a20612f53202ab4b8e880b0583c Mon Sep 17 00:00:00 2001 From: Ugljesa Jovanovic Date: Sat, 16 Nov 2024 22:22:47 +0100 Subject: [PATCH 207/361] Add OTP write --- embassy-rp/src/otp.rs | 71 +++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 69 insertions(+), 2 deletions(-) diff --git a/embassy-rp/src/otp.rs b/embassy-rp/src/otp.rs index cdaf5bded..6091f71b2 100644 --- a/embassy-rp/src/otp.rs +++ b/embassy-rp/src/otp.rs @@ -3,15 +3,24 @@ // Credit: taken from `rp-hal` (also licensed Apache+MIT) // https://github.com/rp-rs/rp-hal/blob/main/rp235x-hal/src/rom_data.rs -/// The ways in which we can fail to read OTP +use crate::rom_data::otp_access; + +/// The ways in which we can fail to access OTP #[derive(Debug, Clone)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] pub enum Error { /// The user passed an invalid index to a function. InvalidIndex, /// The hardware refused to let us read this word, probably due to - /// read lock set earlier in the boot process. + /// read or write lock set earlier in the boot process. InvalidPermissions, + /// Modification is impossible based on current state; e.g. + /// attempted to clear an OTP bit. + UnsupportedModification, + /// Value being written is bigger than 24 bits allowed for raw writes. + Overflow, + /// An unexpected failure that contains the exact return code + UnexpectedFailure(i32), } /// OTP read address, using automatic Error Correction. @@ -34,6 +43,9 @@ pub const NUM_ROWS_PER_PAGE: usize = 64; /// How many rows in OTP (post error-correction) pub const NUM_ROWS: usize = NUM_PAGES * NUM_ROWS_PER_PAGE; +/// 24bit mask for raw writes +pub const RAW_WRITE_BIT_MASK: u32 = 0x00FF_FFFF; + /// Read one ECC protected word from the OTP pub fn read_ecc_word(row: usize) -> Result { if row >= NUM_ROWS { @@ -72,6 +84,61 @@ pub fn read_raw_word(row: usize) -> Result { Ok(value) } } +/// Write one raw word to the OTP +/// +/// 24 bit value will be written to the OTP +pub fn write_raw_word(row: usize, data: u32) -> Result<(), Error> { + if data > RAW_WRITE_BIT_MASK { + return Err(Error::Overflow); + } + if row >= NUM_ROWS { + return Err(Error::InvalidIndex); + } + let row_with_write_bit = row | 0x00010000; + // # Safety + // + // We checked this row was in range already. + let result = unsafe { otp_access(data.to_le_bytes().as_mut_ptr(), 4, row_with_write_bit as u32) }; + if result == 0 { + Ok(()) + } else { + // 5.4.3. API Function Return Codes + let error = match result { + -4 => Error::InvalidPermissions, + -18 => Error::UnsupportedModification, + _ => Error::UnexpectedFailure(result), + }; + Err(error) + } +} + +/// Write one raw word to the OTP with ECC +/// +/// 16 bit value will be written + ECC +pub fn write_ecc_word(row: usize, data: u16) -> Result<(), Error> { + if row >= NUM_ROWS { + return Err(Error::InvalidIndex); + } + let row_with_write_and_ecc_bit = row | 0x00030000; + + // # Safety + // + // We checked this row was in range already. + + let result = unsafe { otp_access(data.to_le_bytes().as_mut_ptr(), 2, row_with_write_and_ecc_bit as u32) }; + if result == 0 { + Ok(()) + } else { + // 5.4.3. API Function Return Codes + // 5.4.3. API Function Return Codes + let error = match result { + -4 => Error::InvalidPermissions, + -18 => Error::UnsupportedModification, + _ => Error::UnexpectedFailure(result), + }; + Err(error) + } +} /// Get the random 64bit chipid from rows 0x0-0x3. pub fn get_chipid() -> Result { From a8d7a5eb1e2038d0961e5dda8b1d5d04826fe1fd Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Sun, 17 Nov 2024 14:32:35 +0100 Subject: [PATCH 208/361] nrf: add nrf54l base: gpio and time driver. --- ci.sh | 3 + embassy-net-nrf91/Cargo.toml | 2 +- embassy-nrf/Cargo.toml | 12 +- embassy-nrf/src/chips/nrf54l15_app.rs | 346 ++++++++++++++++++++++++++ embassy-nrf/src/gpio.rs | 131 +++++++++- embassy-nrf/src/lib.rs | 84 +++++-- embassy-nrf/src/pwm.rs | 16 +- embassy-nrf/src/spim.rs | 4 +- embassy-nrf/src/spis.rs | 2 +- embassy-nrf/src/time_driver.rs | 25 +- examples/nrf54l15/.cargo/config.toml | 9 + examples/nrf54l15/Cargo.toml | 20 ++ examples/nrf54l15/build.rs | 35 +++ examples/nrf54l15/memory.x | 5 + examples/nrf54l15/src/bin/blinky.rs | 23 ++ 15 files changed, 668 insertions(+), 49 deletions(-) create mode 100644 embassy-nrf/src/chips/nrf54l15_app.rs create mode 100644 examples/nrf54l15/.cargo/config.toml create mode 100644 examples/nrf54l15/Cargo.toml create mode 100644 examples/nrf54l15/build.rs create mode 100644 examples/nrf54l15/memory.x create mode 100644 examples/nrf54l15/src/bin/blinky.rs diff --git a/ci.sh b/ci.sh index 9f7a7a037..307e268c4 100755 --- a/ci.sh +++ b/ci.sh @@ -70,6 +70,8 @@ cargo batch \ --- build --release --manifest-path embassy-nrf/Cargo.toml --target thumbv8m.main-none-eabihf --features nrf5340-app-s,gpiote,time,time-driver-rtc1 \ --- build --release --manifest-path embassy-nrf/Cargo.toml --target thumbv8m.main-none-eabihf --features nrf5340-app-ns,gpiote,time,time-driver-rtc1 \ --- build --release --manifest-path embassy-nrf/Cargo.toml --target thumbv8m.main-none-eabihf --features nrf5340-net,gpiote,time,time-driver-rtc1 \ + --- build --release --manifest-path embassy-nrf/Cargo.toml --target thumbv8m.main-none-eabihf --features nrf54l15-app-s,gpiote,time,time-driver-rtc1 \ + --- build --release --manifest-path embassy-nrf/Cargo.toml --target thumbv8m.main-none-eabihf --features nrf54l15-app-ns,gpiote,time,time-driver-rtc1 \ --- build --release --manifest-path embassy-nrf/Cargo.toml --target thumbv7em-none-eabi --features nrf52840,gpiote,time \ --- build --release --manifest-path embassy-nrf/Cargo.toml --target thumbv7em-none-eabi --features nrf52840,gpiote,time-driver-rtc1 \ --- build --release --manifest-path embassy-nrf/Cargo.toml --target thumbv7em-none-eabi --features nrf52840,gpiote,time,time-driver-rtc1 \ @@ -197,6 +199,7 @@ cargo batch \ --- build --release --manifest-path examples/nrf52810/Cargo.toml --target thumbv7em-none-eabi --out-dir out/examples/nrf52810 \ --- build --release --manifest-path examples/nrf52840/Cargo.toml --target thumbv7em-none-eabi --out-dir out/examples/nrf52840 \ --- build --release --manifest-path examples/nrf5340/Cargo.toml --target thumbv8m.main-none-eabihf --out-dir out/examples/nrf5340 \ + --- build --release --manifest-path examples/nrf54l15/Cargo.toml --target thumbv8m.main-none-eabihf --out-dir out/examples/nrf54l15 \ --- build --release --manifest-path examples/nrf9160/Cargo.toml --target thumbv8m.main-none-eabihf --out-dir out/examples/nrf9160 \ --- build --release --manifest-path examples/nrf9151/s/Cargo.toml --target thumbv8m.main-none-eabihf --out-dir out/examples/nrf9151/s \ --- build --release --manifest-path examples/nrf9151/ns/Cargo.toml --target thumbv8m.main-none-eabihf --out-dir out/examples/nrf9151/ns \ diff --git a/embassy-net-nrf91/Cargo.toml b/embassy-net-nrf91/Cargo.toml index 77a1c4cb5..bc31f93f4 100644 --- a/embassy-net-nrf91/Cargo.toml +++ b/embassy-net-nrf91/Cargo.toml @@ -17,7 +17,7 @@ log = [ "dep:log" ] defmt = { version = "0.3", optional = true } log = { version = "0.4.14", optional = true } -nrf-pac = { git = "https://github.com/embassy-rs/nrf-pac", rev = "12e2461859acb0bfea9b2ef5cd73f1283c139ac0" } +nrf-pac = { git = "https://github.com/embassy-rs/nrf-pac", rev = "52e3a757f06035c94291bfc42b0c03f71e4d677e" } cortex-m = "0.7.7" embassy-time = { version = "0.3.1", path = "../embassy-time" } diff --git a/embassy-nrf/Cargo.toml b/embassy-nrf/Cargo.toml index 7b20d2643..ed9aba4e7 100644 --- a/embassy-nrf/Cargo.toml +++ b/embassy-nrf/Cargo.toml @@ -83,6 +83,11 @@ nrf5340-app-s = ["_nrf5340-app", "_s"] nrf5340-app-ns = ["_nrf5340-app", "_ns"] ## nRF5340 network core nrf5340-net = ["_nrf5340-net"] +## nRF54L15 application core in Secure mode +nrf54l15-app-s = ["_nrf54l15-app", "_s"] +## nRF54L15 application core in Non-Secure mode +nrf54l15-app-ns = ["_nrf54l15-app", "_ns"] + ## nRF9160 in Secure mode nrf9160-s = ["_nrf9160", "_s", "_nrf91"] ## nRF9160 in Non-Secure mode @@ -103,6 +108,10 @@ nrf9161-ns = ["nrf9120-ns"] _nrf5340-app = ["_nrf5340", "nrf-pac/nrf5340-app"] _nrf5340-net = ["_nrf5340", "nrf-pac/nrf5340-net"] _nrf5340 = ["_gpio-p1", "_dppi"] +_nrf54l15-app = ["_nrf54l15", "nrf-pac/nrf54l15-app"] +_nrf54l15 = ["_nrf54l", "_gpio-p1", "_gpio-p2"] +_nrf54l = ["_dppi"] + _nrf9160 = ["nrf-pac/nrf9160", "_dppi"] _nrf9120 = ["nrf-pac/nrf9120", "_dppi"] _nrf52 = ["_ppi"] @@ -118,6 +127,7 @@ _ns = [] _ppi = [] _dppi = [] _gpio-p1 = [] +_gpio-p2 = [] # Errata workarounds _nrf52832_anomaly_109 = [] @@ -136,7 +146,7 @@ embedded-hal-async = { version = "1.0" } embedded-io = { version = "0.6.0" } embedded-io-async = { version = "0.6.1" } -nrf-pac = { git = "https://github.com/embassy-rs/nrf-pac", rev = "12e2461859acb0bfea9b2ef5cd73f1283c139ac0" } +nrf-pac = { git = "https://github.com/embassy-rs/nrf-pac", rev = "52e3a757f06035c94291bfc42b0c03f71e4d677e" } defmt = { version = "0.3", optional = true } bitflags = "2.4.2" diff --git a/embassy-nrf/src/chips/nrf54l15_app.rs b/embassy-nrf/src/chips/nrf54l15_app.rs new file mode 100644 index 000000000..b133eb565 --- /dev/null +++ b/embassy-nrf/src/chips/nrf54l15_app.rs @@ -0,0 +1,346 @@ +/// Peripheral Access Crate +#[allow(unused_imports)] +#[rustfmt::skip] +pub mod pac { + pub use nrf_pac::*; + + #[cfg(feature = "_ns")] + #[doc(no_inline)] + pub use nrf_pac::{ + FICR_NS as FICR, + DPPIC00_NS as DPPIC00, + PPIB00_NS as PPIB00, + PPIB01_NS as PPIB01, + AAR00_NS as AAR00, + CCM00_NS as CCM00, + ECB00_NS as ECB00, + SPIM00_NS as SPIM00, + SPIS00_NS as SPIS00, + UARTE00_NS as UARTE00, + VPR00_NS as VPR00, + P2_NS as P2, + CTRLAP_NS as CTRLAP, + TAD_NS as TAD, + TIMER00_NS as TIMER00, + DPPIC10_NS as DPPIC10, + PPIB10_NS as PPIB10, + PPIB11_NS as PPIB11, + TIMER10_NS as TIMER10, + RTC10_NS as RTC10, + EGU10_NS as EGU10, + RADIO_NS as RADIO, + DPPIC20_NS as DPPIC20, + PPIB20_NS as PPIB20, + PPIB21_NS as PPIB21, + PPIB22_NS as PPIB22, + SPIM20_NS as SPIM20, + SPIS20_NS as SPIS20, + TWIM20_NS as TWIM20, + TWIS20_NS as TWIS20, + UARTE20_NS as UARTE20, + SPIM21_NS as SPIM21, + SPIS21_NS as SPIS21, + TWIM21_NS as TWIM21, + TWIS21_NS as TWIS21, + UARTE21_NS as UARTE21, + SPIM22_NS as SPIM22, + SPIS22_NS as SPIS22, + TWIM22_NS as TWIM22, + TWIS22_NS as TWIS22, + UARTE22_NS as UARTE22, + EGU20_NS as EGU20, + TIMER20_NS as TIMER20, + TIMER21_NS as TIMER21, + TIMER22_NS as TIMER22, + TIMER23_NS as TIMER23, + TIMER24_NS as TIMER24, + MEMCONF_NS as MEMCONF, + PDM20_NS as PDM20, + PDM21_NS as PDM21, + PWM20_NS as PWM20, + PWM21_NS as PWM21, + PWM22_NS as PWM22, + SAADC_NS as SAADC, + NFCT_NS as NFCT, + TEMP_NS as TEMP, + P1_NS as P1, + GPIOTE20_NS as GPIOTE20, + I2S20_NS as I2S20, + QDEC20_NS as QDEC20, + QDEC21_NS as QDEC21, + GRTC_NS as GRTC, + DPPIC30_NS as DPPIC30, + PPIB30_NS as PPIB30, + SPIM30_NS as SPIM30, + SPIS30_NS as SPIS30, + TWIM30_NS as TWIM30, + TWIS30_NS as TWIS30, + UARTE30_NS as UARTE30, + RTC30_NS as RTC30, + COMP_NS as COMP, + LPCOMP_NS as LPCOMP, + WDT31_NS as WDT31, + P0_NS as P0, + GPIOTE30_NS as GPIOTE30, + CLOCK_NS as CLOCK, + POWER_NS as POWER, + RESET_NS as RESET, + OSCILLATORS_NS as OSCILLATORS, + REGULATORS_NS as REGULATORS, + TPIU_NS as TPIU, + ETM_NS as ETM, + }; + + #[cfg(feature = "_s")] + #[doc(no_inline)] + pub use nrf_pac::{ + SICR_S as SICR, + ICACHEDATA_S as ICACHEDATA, + ICACHEINFO_S as ICACHEINFO, + SWI00_S as SWI00, + SWI01_S as SWI01, + SWI02_S as SWI02, + SWI03_S as SWI03, + SPU00_S as SPU00, + MPC00_S as MPC00, + DPPIC00_S as DPPIC00, + PPIB00_S as PPIB00, + PPIB01_S as PPIB01, + KMU_S as KMU, + AAR00_S as AAR00, + CCM00_S as CCM00, + ECB00_S as ECB00, + CRACEN_S as CRACEN, + SPIM00_S as SPIM00, + SPIS00_S as SPIS00, + UARTE00_S as UARTE00, + GLITCHDET_S as GLITCHDET, + RRAMC_S as RRAMC, + VPR00_S as VPR00, + P2_S as P2, + CTRLAP_S as CTRLAP, + TAD_S as TAD, + TIMER00_S as TIMER00, + SPU10_S as SPU10, + DPPIC10_S as DPPIC10, + PPIB10_S as PPIB10, + PPIB11_S as PPIB11, + TIMER10_S as TIMER10, + RTC10_S as RTC10, + EGU10_S as EGU10, + RADIO_S as RADIO, + SPU20_S as SPU20, + DPPIC20_S as DPPIC20, + PPIB20_S as PPIB20, + PPIB21_S as PPIB21, + PPIB22_S as PPIB22, + SPIM20_S as SPIM20, + SPIS20_S as SPIS20, + TWIM20_S as TWIM20, + TWIS20_S as TWIS20, + UARTE20_S as UARTE20, + SPIM21_S as SPIM21, + SPIS21_S as SPIS21, + TWIM21_S as TWIM21, + TWIS21_S as TWIS21, + UARTE21_S as UARTE21, + SPIM22_S as SPIM22, + SPIS22_S as SPIS22, + TWIM22_S as TWIM22, + TWIS22_S as TWIS22, + UARTE22_S as UARTE22, + EGU20_S as EGU20, + TIMER20_S as TIMER20, + TIMER21_S as TIMER21, + TIMER22_S as TIMER22, + TIMER23_S as TIMER23, + TIMER24_S as TIMER24, + MEMCONF_S as MEMCONF, + PDM20_S as PDM20, + PDM21_S as PDM21, + PWM20_S as PWM20, + PWM21_S as PWM21, + PWM22_S as PWM22, + SAADC_S as SAADC, + NFCT_S as NFCT, + TEMP_S as TEMP, + P1_S as P1, + GPIOTE20_S as GPIOTE20, + TAMPC_S as TAMPC, + I2S20_S as I2S20, + QDEC20_S as QDEC20, + QDEC21_S as QDEC21, + GRTC_S as GRTC, + SPU30_S as SPU30, + DPPIC30_S as DPPIC30, + PPIB30_S as PPIB30, + SPIM30_S as SPIM30, + SPIS30_S as SPIS30, + TWIM30_S as TWIM30, + TWIS30_S as TWIS30, + UARTE30_S as UARTE30, + RTC30_S as RTC30, + COMP_S as COMP, + LPCOMP_S as LPCOMP, + WDT30_S as WDT30, + WDT31_S as WDT31, + P0_S as P0, + GPIOTE30_S as GPIOTE30, + CLOCK_S as CLOCK, + POWER_S as POWER, + RESET_S as RESET, + OSCILLATORS_S as OSCILLATORS, + REGULATORS_S as REGULATORS, + CRACENCORE_S as CRACENCORE, + CPUC_S as CPUC, + ICACHE_S as ICACHE, + }; +} + +/// The maximum buffer size that the EasyDMA can send/recv in one operation. +pub const EASY_DMA_SIZE: usize = (1 << 16) - 1; +//pub const FORCE_COPY_BUFFER_SIZE: usize = 1024; + +//pub const FLASH_SIZE: usize = 1024 * 1024; + +embassy_hal_internal::peripherals! { + // GPIO port 0 + P0_00, + P0_01, + P0_02, + P0_03, + P0_04, + P0_05, + P0_06, + + // GPIO port 1 + P1_00, + P1_01, + P1_02, + P1_03, + P1_04, + P1_05, + P1_06, + P1_07, + P1_08, + P1_09, + P1_10, + P1_11, + P1_12, + P1_13, + P1_14, + P1_15, + P1_16, + + + // GPIO port 2 + P2_00, + P2_01, + P2_02, + P2_03, + P2_04, + P2_05, + P2_06, + P2_07, + P2_08, + P2_09, + P2_10, +} + +impl_pin!(P0_00, 0, 0); +impl_pin!(P0_01, 0, 1); +impl_pin!(P0_02, 0, 2); +impl_pin!(P0_03, 0, 3); +impl_pin!(P0_04, 0, 4); +impl_pin!(P0_05, 0, 5); +impl_pin!(P0_06, 0, 6); + +impl_pin!(P1_00, 1, 0); +impl_pin!(P1_01, 1, 1); +impl_pin!(P1_02, 1, 2); +impl_pin!(P1_03, 1, 3); +impl_pin!(P1_04, 1, 4); +impl_pin!(P1_05, 1, 5); +impl_pin!(P1_06, 1, 6); +impl_pin!(P1_07, 1, 7); +impl_pin!(P1_08, 1, 8); +impl_pin!(P1_09, 1, 9); +impl_pin!(P1_10, 1, 10); +impl_pin!(P1_11, 1, 11); +impl_pin!(P1_12, 1, 12); +impl_pin!(P1_13, 1, 13); +impl_pin!(P1_14, 1, 14); +impl_pin!(P1_15, 1, 15); +impl_pin!(P1_16, 1, 16); + +impl_pin!(P2_00, 2, 0); +impl_pin!(P2_01, 2, 1); +impl_pin!(P2_02, 2, 2); +impl_pin!(P2_03, 2, 3); +impl_pin!(P2_04, 2, 4); +impl_pin!(P2_05, 2, 5); +impl_pin!(P2_06, 2, 6); +impl_pin!(P2_07, 2, 7); +impl_pin!(P2_08, 2, 8); +impl_pin!(P2_09, 2, 9); +impl_pin!(P2_10, 2, 10); + +embassy_hal_internal::interrupt_mod!( + SWI00, + SWI01, + SWI02, + SWI03, + SPU00, + MPC00, + AAR00_CCM00, + ECB00, + CRACEN, + SERIAL00, + RRAMC, + VPR00, + CTRLAP, + TIMER00, + SPU10, + TIMER10, + RTC10, + EGU10, + RADIO_0, + RADIO_1, + SPU20, + SERIAL20, + SERIAL21, + SERIAL22, + EGU20, + TIMER20, + TIMER21, + TIMER22, + TIMER23, + TIMER24, + PDM20, + PDM21, + PWM20, + PWM21, + PWM22, + SAADC, + NFCT, + TEMP, + GPIOTE20_0, + GPIOTE20_1, + TAMPC, + I2S20, + QDEC20, + QDEC21, + GRTC_0, + GRTC_1, + GRTC_2, + GRTC_3, + SPU30, + SERIAL30, + RTC30, + COMP_LPCOMP, + WDT30, + WDT31, + GPIOTE30_0, + GPIOTE30_1, + CLOCK_POWER, +); diff --git a/embassy-nrf/src/gpio.rs b/embassy-nrf/src/gpio.rs index 35b0f2e7b..d271dbcff 100644 --- a/embassy-nrf/src/gpio.rs +++ b/embassy-nrf/src/gpio.rs @@ -23,6 +23,10 @@ pub enum Port { /// Port 1, only available on some MCUs. #[cfg(feature = "_gpio-p1")] Port1, + + /// Port 2, only available on some MCUs. + #[cfg(feature = "_gpio-p2")] + Port2, } /// Pull setting for an input. @@ -99,8 +103,83 @@ impl From for bool { } } +/// Drive strength settings for a given output level. +// These numbers match vals::Drive exactly so hopefully the compiler will unify them. +#[cfg(feature = "_nrf54l")] +#[derive(Clone, Copy, Debug, PartialEq)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +#[repr(u8)] +pub enum LevelDrive { + /// Disconnect (do not drive the output at all) + Disconnect = 2, + /// Standard + Standard = 0, + /// High drive + High = 1, + /// Extra high drive + ExtraHigh = 3, +} + +/// Drive strength settings for an output pin. +/// +/// This is a combination of two drive levels, used when the pin is set +/// low and high respectively. +#[cfg(feature = "_nrf54l")] +#[derive(Clone, Copy, Debug, PartialEq)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +pub struct OutputDrive { + low: LevelDrive, + high: LevelDrive, +} + +#[cfg(feature = "_nrf54l")] +#[allow(non_upper_case_globals)] +impl OutputDrive { + /// Standard '0', standard '1' + pub const Standard: Self = Self { + low: LevelDrive::Standard, + high: LevelDrive::Standard, + }; + /// High drive '0', standard '1' + pub const HighDrive0Standard1: Self = Self { + low: LevelDrive::High, + high: LevelDrive::Standard, + }; + /// Standard '0', high drive '1' + pub const Standard0HighDrive1: Self = Self { + low: LevelDrive::Standard, + high: LevelDrive::High, + }; + /// High drive '0', high 'drive '1' + pub const HighDrive: Self = Self { + low: LevelDrive::High, + high: LevelDrive::High, + }; + /// Disconnect '0' standard '1' (normally used for wired-or connections) + pub const Disconnect0Standard1: Self = Self { + low: LevelDrive::Disconnect, + high: LevelDrive::Standard, + }; + /// Disconnect '0', high drive '1' (normally used for wired-or connections) + pub const Disconnect0HighDrive1: Self = Self { + low: LevelDrive::Disconnect, + high: LevelDrive::High, + }; + /// Standard '0'. disconnect '1' (also known as "open drain", normally used for wired-and connections) + pub const Standard0Disconnect1: Self = Self { + low: LevelDrive::Standard, + high: LevelDrive::Disconnect, + }; + /// High drive '0', disconnect '1' (also known as "open drain", normally used for wired-and connections) + pub const HighDrive0Disconnect1: Self = Self { + low: LevelDrive::High, + high: LevelDrive::Disconnect, + }; +} + /// Drive strength settings for an output pin. // These numbers match vals::Drive exactly so hopefully the compiler will unify them. +#[cfg(not(feature = "_nrf54l"))] #[derive(Clone, Copy, Debug, PartialEq)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] #[repr(u8)] @@ -185,16 +264,35 @@ impl<'d> Output<'d> { } } -pub(crate) fn convert_drive(drive: OutputDrive) -> vals::Drive { - match drive { - OutputDrive::Standard => vals::Drive::S0S1, - OutputDrive::HighDrive0Standard1 => vals::Drive::H0S1, - OutputDrive::Standard0HighDrive1 => vals::Drive::S0H1, - OutputDrive::HighDrive => vals::Drive::H0H1, - OutputDrive::Disconnect0Standard1 => vals::Drive::D0S1, - OutputDrive::Disconnect0HighDrive1 => vals::Drive::D0H1, - OutputDrive::Standard0Disconnect1 => vals::Drive::S0D1, - OutputDrive::HighDrive0Disconnect1 => vals::Drive::H0D1, +pub(crate) fn convert_drive(w: &mut pac::gpio::regs::PinCnf, drive: OutputDrive) { + #[cfg(not(feature = "_nrf54l"))] + { + let drive = match drive { + OutputDrive::Standard => vals::Drive::S0S1, + OutputDrive::HighDrive0Standard1 => vals::Drive::H0S1, + OutputDrive::Standard0HighDrive1 => vals::Drive::S0H1, + OutputDrive::HighDrive => vals::Drive::H0H1, + OutputDrive::Disconnect0Standard1 => vals::Drive::D0S1, + OutputDrive::Disconnect0HighDrive1 => vals::Drive::D0H1, + OutputDrive::Standard0Disconnect1 => vals::Drive::S0D1, + OutputDrive::HighDrive0Disconnect1 => vals::Drive::H0D1, + }; + w.set_drive(drive); + } + + #[cfg(feature = "_nrf54l")] + { + fn convert(d: LevelDrive) -> vals::Drive { + match d { + LevelDrive::Disconnect => vals::Drive::D, + LevelDrive::Standard => vals::Drive::S, + LevelDrive::High => vals::Drive::H, + LevelDrive::ExtraHigh => vals::Drive::E, + } + } + + w.set_drive0(convert(drive.low)); + w.set_drive0(convert(drive.high)); } } @@ -234,7 +332,7 @@ impl<'d> Flex<'d> { w.set_dir(vals::Dir::INPUT); w.set_input(vals::Input::CONNECT); w.set_pull(convert_pull(pull)); - w.set_drive(vals::Drive::S0S1); + convert_drive(w, OutputDrive::Standard); w.set_sense(vals::Sense::DISABLED); }); } @@ -249,9 +347,10 @@ impl<'d> Flex<'d> { w.set_dir(vals::Dir::OUTPUT); w.set_input(vals::Input::DISCONNECT); w.set_pull(vals::Pull::DISABLED); - w.set_drive(convert_drive(drive)); + convert_drive(w, drive); w.set_sense(vals::Sense::DISABLED); }); + info!("pin_cnf: {:08x}", self.pin.conf().read().0); } /// Put the pin into input + output mode. @@ -269,7 +368,7 @@ impl<'d> Flex<'d> { w.set_dir(vals::Dir::OUTPUT); w.set_input(vals::Input::CONNECT); w.set_pull(convert_pull(pull)); - w.set_drive(convert_drive(drive)); + convert_drive(w, drive); w.set_sense(vals::Sense::DISABLED); }); } @@ -377,6 +476,8 @@ pub(crate) trait SealedPin { 0 => pac::P0, #[cfg(feature = "_gpio-p1")] 1 => pac::P1, + #[cfg(feature = "_gpio-p2")] + 2 => pac::P2, _ => unsafe { unreachable_unchecked() }, } } @@ -415,6 +516,8 @@ pub trait Pin: Peripheral

+ Into + SealedPin + Sized + 'static 0 => Port::Port0, #[cfg(feature = "_gpio-p1")] 1 => Port::Port1, + #[cfg(feature = "_gpio-p2")] + 2 => Port::Port2, _ => unsafe { unreachable_unchecked() }, } } @@ -463,6 +566,7 @@ impl SealedPin for AnyPin { // ==================== #[cfg(not(feature = "_nrf51"))] +#[cfg_attr(feature = "_nrf54l", allow(unused))] // TODO pub(crate) trait PselBits { fn psel_bits(&self) -> pac::shared::regs::Psel; } @@ -479,6 +583,7 @@ impl<'a, P: Pin> PselBits for Option> { } #[cfg(not(feature = "_nrf51"))] +#[cfg_attr(feature = "_nrf54l", allow(unused))] // TODO pub(crate) const DISCONNECTED: Psel = Psel(1 << 31); #[cfg(not(feature = "_nrf51"))] diff --git a/embassy-nrf/src/lib.rs b/embassy-nrf/src/lib.rs index 8167b44f3..33111e1bd 100644 --- a/embassy-nrf/src/lib.rs +++ b/embassy-nrf/src/lib.rs @@ -22,6 +22,8 @@ feature = "nrf5340-app-s", feature = "nrf5340-app-ns", feature = "nrf5340-net", + feature = "nrf54l15-app-s", + feature = "nrf54l15-app-ns", feature = "nrf9160-s", feature = "nrf9160-ns", feature = "nrf9120-s", @@ -44,6 +46,8 @@ compile_error!( nrf5340-app-s, nrf5340-app-ns, nrf5340-net, + nrf54l15-app-s, + nrf54l15-app-ns, nrf9160-s, nrf9160-ns, nrf9120-s, @@ -68,21 +72,22 @@ pub(crate) mod util; #[cfg(feature = "_time-driver")] mod time_driver; +#[cfg(not(feature = "_nrf54l"))] // TODO #[cfg(not(feature = "_nrf51"))] pub mod buffered_uarte; -pub mod gpio; -#[cfg(feature = "gpiote")] -pub mod gpiote; - -// TODO: tested on other chips -#[cfg(not(any(feature = "_nrf91", feature = "_nrf5340-app")))] -pub mod radio; - +#[cfg(not(feature = "_nrf54l"))] // TODO #[cfg(not(feature = "_nrf51"))] pub mod egu; +pub mod gpio; +#[cfg(not(feature = "_nrf54l"))] // TODO +#[cfg(feature = "gpiote")] +pub mod gpiote; +#[cfg(not(feature = "_nrf54l"))] // TODO #[cfg(any(feature = "nrf52832", feature = "nrf52833", feature = "nrf52840"))] pub mod i2s; +#[cfg(not(feature = "_nrf54l"))] // TODO pub mod nvmc; +#[cfg(not(feature = "_nrf54l"))] // TODO #[cfg(any( feature = "nrf52810", feature = "nrf52811", @@ -93,7 +98,9 @@ pub mod nvmc; feature = "_nrf91", ))] pub mod pdm; +#[cfg(not(feature = "_nrf54l"))] // TODO pub mod ppi; +#[cfg(not(feature = "_nrf54l"))] // TODO #[cfg(not(any( feature = "_nrf51", feature = "nrf52805", @@ -101,27 +108,42 @@ pub mod ppi; feature = "_nrf5340-net" )))] pub mod pwm; +#[cfg(not(feature = "_nrf54l"))] // TODO #[cfg(not(any(feature = "_nrf51", feature = "_nrf91", feature = "_nrf5340-net")))] pub mod qdec; +#[cfg(not(feature = "_nrf54l"))] // TODO #[cfg(any(feature = "nrf52840", feature = "_nrf5340-app"))] pub mod qspi; +#[cfg(not(feature = "_nrf54l"))] // TODO +#[cfg(not(any(feature = "_nrf91", feature = "_nrf5340-app")))] +pub mod radio; +#[cfg(not(feature = "_nrf54l"))] // TODO #[cfg(not(any(feature = "_nrf5340-app", feature = "_nrf91")))] pub mod rng; +#[cfg(not(feature = "_nrf54l"))] // TODO #[cfg(not(any(feature = "_nrf51", feature = "nrf52820", feature = "_nrf5340-net")))] pub mod saadc; +#[cfg(not(feature = "_nrf54l"))] // TODO #[cfg(not(feature = "_nrf51"))] pub mod spim; +#[cfg(not(feature = "_nrf54l"))] // TODO #[cfg(not(feature = "_nrf51"))] pub mod spis; +#[cfg(not(feature = "_nrf54l"))] // TODO #[cfg(not(any(feature = "_nrf5340", feature = "_nrf91")))] pub mod temp; +#[cfg(not(feature = "_nrf54l"))] // TODO pub mod timer; +#[cfg(not(feature = "_nrf54l"))] // TODO #[cfg(not(feature = "_nrf51"))] pub mod twim; +#[cfg(not(feature = "_nrf54l"))] // TODO #[cfg(not(feature = "_nrf51"))] pub mod twis; +#[cfg(not(feature = "_nrf54l"))] // TODO #[cfg(not(feature = "_nrf51"))] pub mod uarte; +#[cfg(not(feature = "_nrf54l"))] // TODO #[cfg(any( feature = "_nrf5340-app", feature = "nrf52820", @@ -129,6 +151,7 @@ pub mod uarte; feature = "nrf52840" ))] pub mod usb; +#[cfg(not(feature = "_nrf54l"))] // TODO #[cfg(not(feature = "_nrf5340"))] pub mod wdt; @@ -143,6 +166,7 @@ pub mod wdt; #[cfg_attr(feature = "nrf52840", path = "chips/nrf52840.rs")] #[cfg_attr(feature = "_nrf5340-app", path = "chips/nrf5340_app.rs")] #[cfg_attr(feature = "_nrf5340-net", path = "chips/nrf5340_net.rs")] +#[cfg_attr(feature = "_nrf54l15-app", path = "chips/nrf54l15_app.rs")] #[cfg_attr(feature = "_nrf9160", path = "chips/nrf9160.rs")] #[cfg_attr(feature = "_nrf9120", path = "chips/nrf9120.rs")] mod chip; @@ -249,10 +273,10 @@ pub mod config { /// External source from xtal. ExternalXtal, /// External source from xtal with low swing applied. - #[cfg(not(any(feature = "_nrf5340", feature = "_nrf91")))] + #[cfg(not(any(feature = "_nrf5340", feature = "_nrf91", feature = "_nrf54l")))] ExternalLowSwing, /// External source from xtal with full swing applied. - #[cfg(not(any(feature = "_nrf5340", feature = "_nrf91")))] + #[cfg(not(any(feature = "_nrf5340", feature = "_nrf91", feature = "_nrf54l")))] ExternalFullSwing, } @@ -325,7 +349,7 @@ pub mod config { pub hfclk_source: HfclkSource, /// Low frequency clock source. pub lfclk_source: LfclkSource, - #[cfg(not(feature = "_nrf5340-net"))] + #[cfg(not(any(feature = "_nrf5340-net", feature = "_nrf54l")))] /// DCDC configuration. pub dcdc: DcdcConfig, /// GPIOTE interrupt priority. Should be lower priority than softdevice if used. @@ -346,7 +370,7 @@ pub mod config { // xtals if they know they have them. hfclk_source: HfclkSource::Internal, lfclk_source: LfclkSource::InternalRC, - #[cfg(not(any(feature = "_nrf5340", feature = "_nrf91")))] + #[cfg(not(any(feature = "_nrf5340", feature = "_nrf91", feature = "_nrf54l")))] dcdc: DcdcConfig { #[cfg(feature = "nrf52840")] reg0: false, @@ -415,7 +439,7 @@ mod consts { pub const APPROTECT_DISABLED: u32 = 0x0000_005a; } -#[cfg(not(feature = "_nrf51"))] +#[cfg(not(any(feature = "_nrf51", feature = "_nrf54l")))] #[derive(Debug, Copy, Clone, Eq, PartialEq)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] enum WriteResult { @@ -427,12 +451,12 @@ enum WriteResult { Failed, } -#[cfg(not(feature = "_nrf51"))] +#[cfg(not(any(feature = "_nrf51", feature = "_nrf54l")))] unsafe fn uicr_write(address: *mut u32, value: u32) -> WriteResult { uicr_write_masked(address, value, 0xFFFF_FFFF) } -#[cfg(not(feature = "_nrf51"))] +#[cfg(not(any(feature = "_nrf51", feature = "_nrf54l")))] unsafe fn uicr_write_masked(address: *mut u32, value: u32, mask: u32) -> WriteResult { let curr_val = address.read_volatile(); if curr_val & mask == value & mask { @@ -469,6 +493,7 @@ pub fn init(config: config::Config) -> Peripherals { let mut needs_reset = false; // Setup debug protection. + #[cfg(not(feature = "_nrf54l"))] // TODO #[cfg(not(feature = "_nrf51"))] match config.debug { config::Debug::Allowed => { @@ -592,15 +617,25 @@ pub fn init(config: config::Config) -> Peripherals { match config.hfclk_source { config::HfclkSource::Internal => {} config::HfclkSource::ExternalXtal => { - // Datasheet says this is likely to take 0.36ms - r.events_hfclkstarted().write_value(0); - r.tasks_hfclkstart().write_value(1); - while r.events_hfclkstarted().read() == 0 {} + #[cfg(feature = "_nrf54l")] + { + r.events_xostarted().write_value(0); + r.tasks_xostart().write_value(1); + while r.events_xostarted().read() == 0 {} + } + + #[cfg(not(feature = "_nrf54l"))] + { + // Datasheet says this is likely to take 0.36ms + r.events_hfclkstarted().write_value(0); + r.tasks_hfclkstart().write_value(1); + while r.events_hfclkstarted().read() == 0 {} + } } } // Configure LFCLK. - #[cfg(not(any(feature = "_nrf51", feature = "_nrf5340", feature = "_nrf91")))] + #[cfg(not(any(feature = "_nrf51", feature = "_nrf5340", feature = "_nrf91", feature = "_nrf54l")))] match config.lfclk_source { config::LfclkSource::InternalRC => r.lfclksrc().write(|w| w.set_src(pac::clock::vals::Lfclksrc::RC)), config::LfclkSource::Synthesized => r.lfclksrc().write(|w| w.set_src(pac::clock::vals::Lfclksrc::SYNTH)), @@ -621,6 +656,12 @@ pub fn init(config: config::Config) -> Peripherals { config::LfclkSource::InternalRC => r.lfclksrc().write(|w| w.set_src(pac::clock::vals::Lfclksrc::LFRC)), config::LfclkSource::ExternalXtal => r.lfclksrc().write(|w| w.set_src(pac::clock::vals::Lfclksrc::LFXO)), } + #[cfg(feature = "_nrf54l")] + match config.lfclk_source { + config::LfclkSource::InternalRC => r.lfclk().src().write(|w| w.set_src(pac::clock::vals::Lfclksrc::LFRC)), + config::LfclkSource::Synthesized => r.lfclk().src().write(|w| w.set_src(pac::clock::vals::Lfclksrc::LFSYNT)), + config::LfclkSource::ExternalXtal => r.lfclk().src().write(|w| w.set_src(pac::clock::vals::Lfclksrc::LFXO)), + } // Start LFCLK. // Datasheet says this could take 100us from synth source @@ -629,7 +670,7 @@ pub fn init(config: config::Config) -> Peripherals { r.tasks_lfclkstart().write_value(1); while r.events_lfclkstarted().read() == 0 {} - #[cfg(not(any(feature = "_nrf5340", feature = "_nrf91")))] + #[cfg(not(any(feature = "_nrf5340", feature = "_nrf91", feature = "_nrf54l")))] { // Setup DCDCs. #[cfg(feature = "nrf52840")] @@ -663,6 +704,7 @@ pub fn init(config: config::Config) -> Peripherals { } // Init GPIOTE + #[cfg(not(feature = "_nrf54l"))] // TODO #[cfg(feature = "gpiote")] gpiote::init(config.gpiote_interrupt_priority); diff --git a/embassy-nrf/src/pwm.rs b/embassy-nrf/src/pwm.rs index 7f1f568f4..6247ff6a5 100644 --- a/embassy-nrf/src/pwm.rs +++ b/embassy-nrf/src/pwm.rs @@ -133,7 +133,7 @@ impl<'d, T: Instance> SequencePwm<'d, T> { pin.conf().write(|w| { w.set_dir(gpiovals::Dir::OUTPUT); w.set_input(gpiovals::Input::DISCONNECT); - w.set_drive(convert_drive(config.ch0_drive)); + convert_drive(w, config.ch0_drive); }); } if let Some(pin) = &ch1 { @@ -141,7 +141,7 @@ impl<'d, T: Instance> SequencePwm<'d, T> { pin.conf().write(|w| { w.set_dir(gpiovals::Dir::OUTPUT); w.set_input(gpiovals::Input::DISCONNECT); - w.set_drive(convert_drive(config.ch1_drive)); + convert_drive(w, config.ch1_drive); }); } if let Some(pin) = &ch2 { @@ -149,7 +149,7 @@ impl<'d, T: Instance> SequencePwm<'d, T> { pin.conf().write(|w| { w.set_dir(gpiovals::Dir::OUTPUT); w.set_input(gpiovals::Input::DISCONNECT); - w.set_drive(convert_drive(config.ch2_drive)); + convert_drive(w, config.ch2_drive); }); } if let Some(pin) = &ch3 { @@ -157,7 +157,7 @@ impl<'d, T: Instance> SequencePwm<'d, T> { pin.conf().write(|w| { w.set_dir(gpiovals::Dir::OUTPUT); w.set_input(gpiovals::Input::DISCONNECT); - w.set_drive(convert_drive(config.ch3_drive)); + convert_drive(w, config.ch3_drive); }); } @@ -832,7 +832,7 @@ impl<'d, T: Instance> SimplePwm<'d, T> { #[inline(always)] pub fn set_ch0_drive(&self, drive: OutputDrive) { if let Some(pin) = &self.ch0 { - pin.conf().modify(|w| w.set_drive(convert_drive(drive))); + pin.conf().modify(|w| convert_drive(w, drive)); } } @@ -840,7 +840,7 @@ impl<'d, T: Instance> SimplePwm<'d, T> { #[inline(always)] pub fn set_ch1_drive(&self, drive: OutputDrive) { if let Some(pin) = &self.ch1 { - pin.conf().modify(|w| w.set_drive(convert_drive(drive))); + pin.conf().modify(|w| convert_drive(w, drive)); } } @@ -848,7 +848,7 @@ impl<'d, T: Instance> SimplePwm<'d, T> { #[inline(always)] pub fn set_ch2_drive(&self, drive: OutputDrive) { if let Some(pin) = &self.ch2 { - pin.conf().modify(|w| w.set_drive(convert_drive(drive))); + pin.conf().modify(|w| convert_drive(w, drive)); } } @@ -856,7 +856,7 @@ impl<'d, T: Instance> SimplePwm<'d, T> { #[inline(always)] pub fn set_ch3_drive(&self, drive: OutputDrive) { if let Some(pin) = &self.ch3 { - pin.conf().modify(|w| w.set_drive(convert_drive(drive))); + pin.conf().modify(|w| convert_drive(w, drive)); } } } diff --git a/embassy-nrf/src/spim.rs b/embassy-nrf/src/spim.rs index ffaee2dc5..bd193cfe8 100644 --- a/embassy-nrf/src/spim.rs +++ b/embassy-nrf/src/spim.rs @@ -173,13 +173,13 @@ impl<'d, T: Instance> Spim<'d, T> { if let Some(sck) = &sck { sck.conf().write(|w| { w.set_dir(gpiovals::Dir::OUTPUT); - w.set_drive(convert_drive(config.sck_drive)) + convert_drive(w, config.sck_drive); }); } if let Some(mosi) = &mosi { mosi.conf().write(|w| { w.set_dir(gpiovals::Dir::OUTPUT); - w.set_drive(convert_drive(config.mosi_drive)) + convert_drive(w, config.mosi_drive); }); } if let Some(miso) = &miso { diff --git a/embassy-nrf/src/spis.rs b/embassy-nrf/src/spis.rs index a363e5909..88230fa26 100644 --- a/embassy-nrf/src/spis.rs +++ b/embassy-nrf/src/spis.rs @@ -203,7 +203,7 @@ impl<'d, T: Instance> Spis<'d, T> { if let Some(miso) = &miso { miso.conf().write(|w| { w.set_dir(gpiovals::Dir::OUTPUT); - w.set_drive(convert_drive(config.miso_drive)) + convert_drive(w, config.miso_drive); }); r.psel().miso().write_value(miso.psel_bits()); } diff --git a/embassy-nrf/src/time_driver.rs b/embassy-nrf/src/time_driver.rs index b6492ac97..81aabb11c 100644 --- a/embassy-nrf/src/time_driver.rs +++ b/embassy-nrf/src/time_driver.rs @@ -10,6 +10,11 @@ use embassy_time_driver::{AlarmHandle, Driver}; use crate::interrupt::InterruptExt; use crate::{interrupt, pac}; +#[cfg(feature = "_nrf54l")] +fn rtc() -> pac::rtc::Rtc { + pac::RTC30 +} +#[cfg(not(feature = "_nrf54l"))] fn rtc() -> pac::rtc::Rtc { pac::RTC1 } @@ -141,8 +146,16 @@ impl RtcDriver { // Wait for clear while r.counter().read().0 != 0 {} - interrupt::RTC1.set_priority(irq_prio); - unsafe { interrupt::RTC1.enable() }; + #[cfg(feature = "_nrf54l")] + { + interrupt::RTC30.set_priority(irq_prio); + unsafe { interrupt::RTC30.enable() }; + } + #[cfg(not(feature = "_nrf54l"))] + { + interrupt::RTC1.set_priority(irq_prio); + unsafe { interrupt::RTC1.enable() }; + } } fn on_interrupt(&self) { @@ -292,6 +305,14 @@ impl Driver for RtcDriver { } } +#[cfg(feature = "_nrf54l")] +#[cfg(feature = "rt")] +#[interrupt] +fn RTC30() { + DRIVER.on_interrupt() +} + +#[cfg(not(feature = "_nrf54l"))] #[cfg(feature = "rt")] #[interrupt] fn RTC1() { diff --git a/examples/nrf54l15/.cargo/config.toml b/examples/nrf54l15/.cargo/config.toml new file mode 100644 index 000000000..4a026ebbd --- /dev/null +++ b/examples/nrf54l15/.cargo/config.toml @@ -0,0 +1,9 @@ +[target.'cfg(all(target_arch = "arm", target_os = "none"))'] +# replace nRF82840_xxAA with your chip as listed in `probe-rs chip list` +runner = "../../sshprobe.sh" + +[build] +target = "thumbv8m.main-none-eabihf" + +[env] +DEFMT_LOG = "trace" diff --git a/examples/nrf54l15/Cargo.toml b/examples/nrf54l15/Cargo.toml new file mode 100644 index 000000000..6d11269f7 --- /dev/null +++ b/examples/nrf54l15/Cargo.toml @@ -0,0 +1,20 @@ +[package] +edition = "2021" +name = "embassy-nrf54l15-examples" +version = "0.1.0" +license = "MIT OR Apache-2.0" + +[dependencies] +embassy-executor = { version = "0.6.3", path = "../../embassy-executor", features = ["task-arena-size-32768", "arch-cortex-m", "executor-thread", "executor-interrupt", "defmt", "integrated-timers"] } +embassy-time = { version = "0.3.2", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime"] } +embassy-nrf = { version = "0.2.0", path = "../../embassy-nrf", features = ["defmt", "nrf54l15-app-s", "time-driver-rtc1", "gpiote", "unstable-pac", "time"] } + +defmt = "0.3" +defmt-rtt = "0.4" +panic-probe = { version = "0.3", features = ["print-defmt"] } + +cortex-m = { version = "0.7.6", features = ["inline-asm", "critical-section-single-core"] } +cortex-m-rt = "0.7.0" + +[profile.release] +debug = 2 diff --git a/examples/nrf54l15/build.rs b/examples/nrf54l15/build.rs new file mode 100644 index 000000000..30691aa97 --- /dev/null +++ b/examples/nrf54l15/build.rs @@ -0,0 +1,35 @@ +//! This build script copies the `memory.x` file from the crate root into +//! a directory where the linker can always find it at build time. +//! For many projects this is optional, as the linker always searches the +//! project root directory -- wherever `Cargo.toml` is. However, if you +//! are using a workspace or have a more complicated build setup, this +//! build script becomes required. Additionally, by requesting that +//! Cargo re-run the build script whenever `memory.x` is changed, +//! updating `memory.x` ensures a rebuild of the application with the +//! new memory settings. + +use std::env; +use std::fs::File; +use std::io::Write; +use std::path::PathBuf; + +fn main() { + // Put `memory.x` in our output directory and ensure it's + // on the linker search path. + let out = &PathBuf::from(env::var_os("OUT_DIR").unwrap()); + File::create(out.join("memory.x")) + .unwrap() + .write_all(include_bytes!("memory.x")) + .unwrap(); + println!("cargo:rustc-link-search={}", out.display()); + + // By default, Cargo will re-run a build script whenever + // any file in the project changes. By specifying `memory.x` + // here, we ensure the build script is only re-run when + // `memory.x` is changed. + println!("cargo:rerun-if-changed=memory.x"); + + println!("cargo:rustc-link-arg-bins=--nmagic"); + println!("cargo:rustc-link-arg-bins=-Tlink.x"); + println!("cargo:rustc-link-arg-bins=-Tdefmt.x"); +} diff --git a/examples/nrf54l15/memory.x b/examples/nrf54l15/memory.x new file mode 100644 index 000000000..1064c8a5c --- /dev/null +++ b/examples/nrf54l15/memory.x @@ -0,0 +1,5 @@ +MEMORY +{ + FLASH : ORIGIN = 0x00000000, LENGTH = 1536K + RAM : ORIGIN = 0x20000000, LENGTH = 256K +} diff --git a/examples/nrf54l15/src/bin/blinky.rs b/examples/nrf54l15/src/bin/blinky.rs new file mode 100644 index 000000000..71fcc461f --- /dev/null +++ b/examples/nrf54l15/src/bin/blinky.rs @@ -0,0 +1,23 @@ +#![no_std] +#![no_main] + +use defmt::info; +use embassy_executor::Spawner; +use embassy_nrf::gpio::{Level, Output, OutputDrive}; +use embassy_time::Timer; +use {defmt_rtt as _, panic_probe as _}; + +#[embassy_executor::main] +async fn main(_spawner: Spawner) { + let p = embassy_nrf::init(Default::default()); + let mut led = Output::new(p.P2_09, Level::Low, OutputDrive::Standard); + + loop { + info!("high!"); + led.set_high(); + Timer::after_millis(300).await; + info!("low!"); + led.set_low(); + Timer::after_millis(300).await; + } +} From f99d733b616e748eb9c7122b77d6521297d53171 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Sun, 17 Nov 2024 23:01:00 +0100 Subject: [PATCH 209/361] nrf: add docs build for nrf54. --- embassy-nrf/Cargo.toml | 1 + 1 file changed, 1 insertion(+) diff --git a/embassy-nrf/Cargo.toml b/embassy-nrf/Cargo.toml index ed9aba4e7..9e62bcca4 100644 --- a/embassy-nrf/Cargo.toml +++ b/embassy-nrf/Cargo.toml @@ -18,6 +18,7 @@ flavors = [ { regex_feature = "nrf51", target = "thumbv6m-none-eabi" }, { regex_feature = "nrf52.*", target = "thumbv7em-none-eabihf" }, { regex_feature = "nrf53.*", target = "thumbv8m.main-none-eabihf" }, + { regex_feature = "nrf54.*", target = "thumbv8m.main-none-eabihf" }, { regex_feature = "nrf91.*", target = "thumbv8m.main-none-eabihf" }, ] From 7ae28163414d0042c4e06cc02de900e67b62df01 Mon Sep 17 00:00:00 2001 From: elagil Date: Sun, 17 Nov 2024 23:09:51 +0100 Subject: [PATCH 210/361] feat: SAI/ringbuffer add function to wait for any write error --- embassy-stm32/src/dma/dma_bdma.rs | 7 +++++++ embassy-stm32/src/dma/ringbuffer/mod.rs | 13 +++++++++++++ embassy-stm32/src/sai/mod.rs | 16 ++++++++++++++++ 3 files changed, 36 insertions(+) diff --git a/embassy-stm32/src/dma/dma_bdma.rs b/embassy-stm32/src/dma/dma_bdma.rs index 08c7a5508..90160ed50 100644 --- a/embassy-stm32/src/dma/dma_bdma.rs +++ b/embassy-stm32/src/dma/dma_bdma.rs @@ -1006,6 +1006,13 @@ impl<'a, W: Word> WritableRingBuffer<'a, W> { .await } + /// Wait for any ring buffer write error. + pub async fn write_error(&mut self) -> Result { + self.ringbuf + .write_error(&mut DmaCtrlImpl(self.channel.reborrow())) + .await + } + /// The current length of the ringbuffer pub fn len(&mut self) -> Result { Ok(self.ringbuf.len(&mut DmaCtrlImpl(self.channel.reborrow()))?) diff --git a/embassy-stm32/src/dma/ringbuffer/mod.rs b/embassy-stm32/src/dma/ringbuffer/mod.rs index 0da8c374f..b7f98fbce 100644 --- a/embassy-stm32/src/dma/ringbuffer/mod.rs +++ b/embassy-stm32/src/dma/ringbuffer/mod.rs @@ -260,6 +260,19 @@ impl<'a, W: Word> WritableDmaRingBuffer<'a, W> { Ok((written, self.cap() - written)) } + /// Wait for any ring buffer write error. + pub async fn write_error(&mut self, dma: &mut impl DmaCtrl) -> Result { + poll_fn(|cx| { + dma.set_waker(cx.waker()); + + match self.len(dma) { + Ok(_) => Poll::Pending, + Err(e) => Poll::Ready(Err(e)), + } + }) + .await + } + /// Write an exact number of elements to the ringbuffer. pub async fn write_exact(&mut self, dma: &mut impl DmaCtrl, buffer: &[W]) -> Result { let mut written_data = 0; diff --git a/embassy-stm32/src/sai/mod.rs b/embassy-stm32/src/sai/mod.rs index 057b21980..fd61e5339 100644 --- a/embassy-stm32/src/sai/mod.rs +++ b/embassy-stm32/src/sai/mod.rs @@ -1003,6 +1003,22 @@ impl<'d, T: Instance, W: word::Word> Sai<'d, T, W> { } } + /// Wait until any SAI write error occurs. + /// + /// One useful application for this is stopping playback as soon as the SAI + /// experiences an overrun of the ring buffer. Then, instead of letting + /// the SAI peripheral play the last written buffer over and over again, SAI + /// can be muted or dropped instead. + pub async fn write_error(&mut self) -> Result<(), Error> { + match &mut self.ring_buffer { + RingBuffer::Writable(buffer) => { + buffer.write_error().await?; + Ok(()) + } + _ => return Err(Error::NotATransmitter), + } + } + /// Write data to the SAI ringbuffer. /// /// The first write starts the DMA after filling the ring buffer with the provided data. From ee9ca4470370ae9e0e98a54ceb94ec83da20eb19 Mon Sep 17 00:00:00 2001 From: elagil Date: Sun, 17 Nov 2024 23:56:45 +0100 Subject: [PATCH 211/361] refactor: naming of wait functions --- embassy-stm32/src/dma/dma_bdma.rs | 4 ++-- embassy-stm32/src/dma/ringbuffer/mod.rs | 2 +- embassy-stm32/src/sai/mod.rs | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/embassy-stm32/src/dma/dma_bdma.rs b/embassy-stm32/src/dma/dma_bdma.rs index 90160ed50..1945c3587 100644 --- a/embassy-stm32/src/dma/dma_bdma.rs +++ b/embassy-stm32/src/dma/dma_bdma.rs @@ -1007,9 +1007,9 @@ impl<'a, W: Word> WritableRingBuffer<'a, W> { } /// Wait for any ring buffer write error. - pub async fn write_error(&mut self) -> Result { + pub async fn wait_write_error(&mut self) -> Result { self.ringbuf - .write_error(&mut DmaCtrlImpl(self.channel.reborrow())) + .wait_write_error(&mut DmaCtrlImpl(self.channel.reborrow())) .await } diff --git a/embassy-stm32/src/dma/ringbuffer/mod.rs b/embassy-stm32/src/dma/ringbuffer/mod.rs index b7f98fbce..25bdc7522 100644 --- a/embassy-stm32/src/dma/ringbuffer/mod.rs +++ b/embassy-stm32/src/dma/ringbuffer/mod.rs @@ -261,7 +261,7 @@ impl<'a, W: Word> WritableDmaRingBuffer<'a, W> { } /// Wait for any ring buffer write error. - pub async fn write_error(&mut self, dma: &mut impl DmaCtrl) -> Result { + pub async fn wait_write_error(&mut self, dma: &mut impl DmaCtrl) -> Result { poll_fn(|cx| { dma.set_waker(cx.waker()); diff --git a/embassy-stm32/src/sai/mod.rs b/embassy-stm32/src/sai/mod.rs index fd61e5339..a8d02f825 100644 --- a/embassy-stm32/src/sai/mod.rs +++ b/embassy-stm32/src/sai/mod.rs @@ -1009,10 +1009,10 @@ impl<'d, T: Instance, W: word::Word> Sai<'d, T, W> { /// experiences an overrun of the ring buffer. Then, instead of letting /// the SAI peripheral play the last written buffer over and over again, SAI /// can be muted or dropped instead. - pub async fn write_error(&mut self) -> Result<(), Error> { + pub async fn wait_write_error(&mut self) -> Result<(), Error> { match &mut self.ring_buffer { RingBuffer::Writable(buffer) => { - buffer.write_error().await?; + buffer.wait_write_error().await?; Ok(()) } _ => return Err(Error::NotATransmitter), From e09e4e96127aaa77e58152901745b97860f22e6b Mon Sep 17 00:00:00 2001 From: Christian Enderle Date: Sat, 9 Nov 2024 20:35:19 +0100 Subject: [PATCH 212/361] Enable user to choose to pass lse clock to peripherals --- embassy-stm32/src/rcc/bd.rs | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/embassy-stm32/src/rcc/bd.rs b/embassy-stm32/src/rcc/bd.rs index 9ccca8a2a..78ecbfb94 100644 --- a/embassy-stm32/src/rcc/bd.rs +++ b/embassy-stm32/src/rcc/bd.rs @@ -20,6 +20,9 @@ pub enum LseMode { pub struct LseConfig { pub frequency: Hertz, pub mode: LseMode, + /// If peripherals other than RTC/TAMP or RCC functions need the lse this bit must be set + #[cfg(any(rcc_l5, rcc_u5, rcc_wle, rcc_wl5, rcc_wba))] + pub peripherals_clocked: bool, } #[allow(dead_code)] @@ -95,6 +98,8 @@ impl LsConfig { lse: Some(LseConfig { frequency: Hertz(32_768), mode: LseMode::Oscillator(LseDrive::MediumHigh), + #[cfg(any(rcc_l5, rcc_u5, rcc_wle, rcc_wl5, rcc_wba))] + peripherals_clocked: false, }), lsi: false, } @@ -148,6 +153,12 @@ impl LsConfig { }, None => (false, false, None), }; + #[cfg(any(rcc_l5, rcc_u5, rcc_wle, rcc_wl5, rcc_wba))] + let lse_sysen = if let Some(lse) = self.lse { + Some(lse.peripherals_clocked) + } else { + None + }; _ = lse_drv; // not all chips have it. // Disable backup domain write protection @@ -188,6 +199,10 @@ impl LsConfig { } ok &= reg.lseon() == lse_en; ok &= reg.lsebyp() == lse_byp; + #[cfg(any(rcc_l5, rcc_u5, rcc_wle, rcc_wl5, rcc_wba))] + if let Some(lse_sysen) = lse_sysen { + ok &= reg.lsesysen() == lse_sysen; + } #[cfg(not(any(rcc_f1, rcc_f1cl, rcc_f100, rcc_f2, rcc_f4, rcc_f410, rcc_l1)))] if let Some(lse_drv) = lse_drv { ok &= reg.lsedrv() == lse_drv.into(); @@ -235,6 +250,15 @@ impl LsConfig { }); while !bdcr().read().lserdy() {} + + #[cfg(any(rcc_l5, rcc_u5, rcc_wle, rcc_wl5, rcc_wba))] + if let Some(lse_sysen) = lse_sysen { + bdcr().modify(|w| { + w.set_lsesysen(lse_sysen); + }); + + while !bdcr().read().lsesysrdy() {} + } } if self.rtc != RtcClockSource::DISABLE { From e76473ae95b24d9772711ab4ece464e44e9558f6 Mon Sep 17 00:00:00 2001 From: Christian Enderle Date: Mon, 18 Nov 2024 12:20:15 +0100 Subject: [PATCH 213/361] fixed hanging when lse_sysen disabled --- embassy-stm32/src/rcc/bd.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/embassy-stm32/src/rcc/bd.rs b/embassy-stm32/src/rcc/bd.rs index 78ecbfb94..4aec3756f 100644 --- a/embassy-stm32/src/rcc/bd.rs +++ b/embassy-stm32/src/rcc/bd.rs @@ -257,7 +257,9 @@ impl LsConfig { w.set_lsesysen(lse_sysen); }); - while !bdcr().read().lsesysrdy() {} + if lse_sysen { + while !bdcr().read().lsesysrdy() {} + } } } From 62dbdcd45adfa7f3b74b377f0b4ef7eafaef78fd Mon Sep 17 00:00:00 2001 From: elagil Date: Mon, 18 Nov 2024 20:51:22 +0100 Subject: [PATCH 214/361] feat: add SPDIFRX driver --- embassy-stm32/build.rs | 12 ++ embassy-stm32/src/lib.rs | 2 + embassy-stm32/src/spdifrx/mod.rs | 336 +++++++++++++++++++++++++++++++ 3 files changed, 350 insertions(+) create mode 100644 embassy-stm32/src/spdifrx/mod.rs diff --git a/embassy-stm32/build.rs b/embassy-stm32/build.rs index 71bfb3747..348d48b9b 100644 --- a/embassy-stm32/build.rs +++ b/embassy-stm32/build.rs @@ -1220,6 +1220,17 @@ fn main() { impl_dac_pin!( #peri, #pin_name, #ch); }) } + + if regs.kind == "spdifrx" { + let peri = format_ident!("{}", p.name); + let pin_name = format_ident!("{}", pin.pin); + let af = pin.af.unwrap_or(0); + let sel: u8 = pin.signal.strip_prefix("IN").unwrap().parse().unwrap(); + + g.extend(quote! { + impl_spdifrx_pin!( #peri, #pin_name, #af, #sel); + }) + } } } } @@ -1244,6 +1255,7 @@ fn main() { (("sai", "B"), quote!(crate::sai::Dma)), (("spi", "RX"), quote!(crate::spi::RxDma)), (("spi", "TX"), quote!(crate::spi::TxDma)), + (("spdifrx", "RX"), quote!(crate::spdifrx::Dma)), (("i2c", "RX"), quote!(crate::i2c::RxDma)), (("i2c", "TX"), quote!(crate::i2c::TxDma)), (("dcmi", "DCMI"), quote!(crate::dcmi::FrameDma)), diff --git a/embassy-stm32/src/lib.rs b/embassy-stm32/src/lib.rs index 286a18da2..e189351d1 100644 --- a/embassy-stm32/src/lib.rs +++ b/embassy-stm32/src/lib.rs @@ -107,6 +107,8 @@ pub mod rtc; pub mod sai; #[cfg(sdmmc)] pub mod sdmmc; +#[cfg(spdifrx)] +pub mod spdifrx; #[cfg(spi)] pub mod spi; #[cfg(tsc)] diff --git a/embassy-stm32/src/spdifrx/mod.rs b/embassy-stm32/src/spdifrx/mod.rs new file mode 100644 index 000000000..bfc8bff29 --- /dev/null +++ b/embassy-stm32/src/spdifrx/mod.rs @@ -0,0 +1,336 @@ +//! S/PDIF receiver +#![macro_use] +#![cfg_attr(gpdma, allow(unused))] + +use core::marker::PhantomData; + +use embassy_hal_internal::{into_ref, PeripheralRef}; +use embassy_sync::waitqueue::AtomicWaker; + +use crate::dma::ringbuffer::Error as RingbufferError; +pub use crate::dma::word; +#[cfg(not(gpdma))] +use crate::dma::ReadableRingBuffer; +use crate::dma::{Channel, TransferOptions}; +use crate::gpio::{AfType, AnyPin, Pull, SealedPin as _}; +use crate::interrupt::typelevel::Interrupt; +use crate::pac::spdifrx::Spdifrx as Regs; +use crate::rcc::{RccInfo, SealedRccPeripheral}; +use crate::{interrupt, peripherals, Peripheral}; + +/// Possible S/PDIF preamble types. +#[allow(dead_code)] +#[repr(u8)] +enum PreambleType { + Unused = 0x00, + /// The preamble changes to preamble “B” once every 192 frames to identify the start of the block structure used to + /// organize the channel status and user information. + B = 0x01, + /// The first sub-frame (left or “A” channel in stereophonic operation and primary channel in monophonic operation) + /// normally starts with preamble “M” + M = 0x02, + /// The second sub-frame (right or “B” channel in stereophonic operation and secondary channel in monophonic + /// operation) always starts with preamble “W”. + W = 0x03, +} + +macro_rules! new_spdifrx_pin { + ($name:ident, $af_type:expr) => {{ + let pin = $name.into_ref(); + let input_sel = pin.input_sel(); + pin.set_as_af(pin.af_num(), $af_type); + (Some(pin.map_into()), input_sel) + }}; +} + +macro_rules! impl_spdifrx_pin { + ($inst:ident, $pin:ident, $af:expr, $sel:expr) => { + impl crate::spdifrx::InPin for crate::peripherals::$pin { + fn af_num(&self) -> u8 { + $af + } + fn input_sel(&self) -> u8 { + $sel + } + } + }; +} + +/// Ring-buffered SPDIFRX driver. +/// +/// Data is read by DMAs and stored in a ring buffer. +#[cfg(not(gpdma))] +pub struct Spdifrx<'d, T: Instance> { + _peri: PeripheralRef<'d, T>, + spdifrx_in: Option>, + data_ring_buffer: ReadableRingBuffer<'d, u32>, +} + +/// Gives the address of the data register. +fn dr_address(r: Regs) -> *mut u32 { + #[cfg(spdifrx_v1)] + let address = r.dr().as_ptr() as _; + #[cfg(spdifrx_h7)] + let address = r.fmt0_dr().as_ptr() as _; // All fmtx_dr() implementations have the same address. + + return address; +} + +/// Gives the address of the channel status register. +#[allow(unused)] +fn csr_address(r: Regs) -> *mut u32 { + r.csr().as_ptr() as _ +} + +/// Select the channel for capturing control information. +pub enum ControlChannelSelection { + /// Capture control info from channel A. + A, + /// Capture control info from channel B. + B, +} + +/// Configuration options for the SPDIFRX driver. +pub struct Config { + /// Select the channel for capturing control information. + pub control_channel_selection: ControlChannelSelection, +} + +/// S/PDIF errors. +#[derive(Debug)] +pub enum Error { + /// DMA overrun error. + RingbufferError(RingbufferError), + /// Left/right channel synchronization error. + ChannelSyncError, +} + +impl From for Error { + fn from(error: RingbufferError) -> Self { + Self::RingbufferError(error) + } +} + +impl Default for Config { + fn default() -> Self { + Self { + control_channel_selection: ControlChannelSelection::A, + } + } +} + +#[cfg(not(gpdma))] +impl<'d, T: Instance> Spdifrx<'d, T> { + fn dma_opts() -> TransferOptions { + TransferOptions { + half_transfer_ir: true, + // new_write() and new_read() always use circular mode + ..Default::default() + } + } + + /// Create a new `Spdifrx` instance. + pub fn new( + peri: impl Peripheral

+ 'd, + _irq: impl interrupt::typelevel::Binding> + 'd, + config: Config, + spdifrx_in: impl Peripheral

> + 'd, + data_dma: impl Peripheral

> + 'd, + data_dma_buf: &'d mut [u32], + ) -> Self { + let (spdifrx_in, input_sel) = new_spdifrx_pin!(spdifrx_in, AfType::input(Pull::None)); + Self::setup(config, input_sel); + + into_ref!(peri, data_dma); + + let regs = T::info().regs; + let dr_request = data_dma.request(); + let dr_ring_buffer = + unsafe { ReadableRingBuffer::new(data_dma, dr_request, dr_address(regs), data_dma_buf, Self::dma_opts()) }; + + Self { + _peri: peri, + spdifrx_in, + data_ring_buffer: dr_ring_buffer, + } + } + + fn setup(config: Config, input_sel: u8) { + T::info().rcc.enable_and_reset(); + T::GlobalInterrupt::unpend(); + unsafe { T::GlobalInterrupt::enable() }; + + let regs = T::info().regs; + + regs.imr().write(|imr| { + imr.set_ifeie(true); // Enables interrupts for TERR, SERR, FERR. + imr.set_syncdie(true); // Enables SYNCD interrupt. + }); + + regs.cr().write(|cr| { + cr.set_spdifen(0x00); // Disable SPDIF receiver synchronization. + cr.set_rxdmaen(true); // Use RX DMA for data. Enabled on `read`. + cr.set_cbdmaen(false); // Do not capture channel info. + cr.set_rxsteo(true); // Operate in stereo mode. + cr.set_drfmt(0x01); // Data is left-aligned (MSB). + + // Disable all status fields in the data register. + // Status can be obtained directly with the status register DMA. + cr.set_pmsk(false); // Write parity bit to the data register. FIXME: Add parity check. + cr.set_vmsk(false); // Write validity to the data register. + cr.set_cumsk(true); // Do not write C and U bits to the data register. + cr.set_ptmsk(false); // Write preamble bits to the data register. + + cr.set_chsel(match config.control_channel_selection { + ControlChannelSelection::A => false, + ControlChannelSelection::B => true, + }); // Select channel status source. + + cr.set_nbtr(0x02); // 16 attempts are allowed. + cr.set_wfa(true); // Wait for activity before going to synchronization phase. + cr.set_insel(input_sel); // Input pin selection. + + #[cfg(stm32h7)] + cr.set_cksen(true); // Generate a symbol clock. + + #[cfg(stm32h7)] + cr.set_cksbkpen(false); // Do not generate a backup symbol clock. + }); + } + + /// Start the SPDIFRX driver. + pub fn start(&mut self) { + self.data_ring_buffer.start(); + + T::info().regs.cr().modify(|cr| { + cr.set_spdifen(0x03); // Enable S/PDIF receiver. + }); + } + + /// Read from the SPDIFRX data ring buffer. + /// + /// SPDIFRX is always receiving data in the background. This function pops already-received + /// data from the buffer. + /// + /// If there's less than `data.len()` data in the buffer, this waits until there is. + pub async fn read(&mut self, data: &mut [u32]) -> Result<(), Error> { + self.data_ring_buffer.read_exact(data).await?; + + let first_preamble = (data[0] >> 4) & 0b11_u32; + if (first_preamble as u8) == (PreambleType::W as u8) { + trace!("S/PDIF left/right mismatch"); + + // Resynchronize until the first sample is for the left channel. + self.data_ring_buffer.clear(); + return Err(Error::ChannelSyncError); + }; + + for sample in data.as_mut() { + if (*sample & (0x0002_u32)) == 0x0001 { + // Discard invalid samples, setting them to mute level. + *sample = 0; + } else { + // Discard status information in the lowest byte. + *sample &= 0xFFFFFF00; + } + } + + Ok(()) + } +} + +#[cfg(not(gpdma))] +impl<'d, T: Instance> Drop for Spdifrx<'d, T> { + fn drop(&mut self) { + T::info().regs.cr().modify(|cr| cr.set_spdifen(0x00)); + self.spdifrx_in.as_ref().map(|x| x.set_as_disconnected()); + } +} + +struct State { + #[allow(unused)] + waker: AtomicWaker, +} + +impl State { + const fn new() -> Self { + Self { + waker: AtomicWaker::new(), + } + } +} + +struct Info { + regs: crate::pac::spdifrx::Spdifrx, + rcc: RccInfo, +} + +peri_trait!( + irqs: [GlobalInterrupt], +); + +/// SPIDFRX pin trait +pub trait InPin: crate::gpio::Pin { + /// Get the GPIO AF number needed to use this pin. + fn af_num(&self) -> u8; + /// Get the SPIDFRX INSEL number needed to use this pin. + fn input_sel(&self) -> u8; +} + +dma_trait!(Dma, Instance); + +/// Global interrupt handler. +pub struct GlobalInterruptHandler { + _phantom: PhantomData, +} + +impl interrupt::typelevel::Handler for GlobalInterruptHandler { + unsafe fn on_interrupt() { + T::state().waker.wake(); + + let regs = T::info().regs; + let sr = regs.sr().read(); + + if sr.serr() || sr.terr() || sr.ferr() { + trace!("SPDIFRX error, resync"); + + // Clear errors by disabling SPDIFRX, then reenable. + regs.cr().modify(|cr| cr.set_spdifen(0x00)); + regs.cr().modify(|cr| cr.set_spdifen(0x03)); + } else if sr.syncd() { + // Synchronization was successful. + trace!("SPDIFRX sync success"); + } + + // Clear interrupt flags. + regs.ifcr().write(|ifcr| { + ifcr.set_perrcf(true); // Clears parity error flag. + ifcr.set_ovrcf(true); // Clears overrun error flag. + ifcr.set_sbdcf(true); // Clears synchronization block detected flag. + ifcr.set_syncdcf(true); // Clears SYNCD from SR (was read above). + }); + } +} + +foreach_peripheral!( + (spdifrx, $inst:ident) => { + #[allow(private_interfaces)] + impl SealedInstance for peripherals::$inst { + fn info() -> &'static Info { + static INFO: Info = Info{ + regs: crate::pac::$inst, + rcc: crate::peripherals::$inst::RCC_INFO, + }; + &INFO + } + fn state() -> &'static State { + static STATE: State = State::new(); + &STATE + } + } + + impl Instance for peripherals::$inst { + type GlobalInterrupt = crate::_generated::peripheral_interrupts::$inst::GLOBAL; + } + }; +); From 1ce1e193b71a5addd8c809722e6f4c6e0cb78284 Mon Sep 17 00:00:00 2001 From: elagil Date: Mon, 18 Nov 2024 20:51:23 +0100 Subject: [PATCH 215/361] feat: Add SPDIFRX example --- examples/stm32h723/.cargo/config.toml | 8 ++ examples/stm32h723/Cargo.toml | 69 +++++++++++ examples/stm32h723/build.rs | 35 ++++++ examples/stm32h723/memory.x | 106 +++++++++++++++++ examples/stm32h723/src/bin/spdifrx.rs | 161 ++++++++++++++++++++++++++ 5 files changed, 379 insertions(+) create mode 100644 examples/stm32h723/.cargo/config.toml create mode 100644 examples/stm32h723/Cargo.toml create mode 100644 examples/stm32h723/build.rs create mode 100644 examples/stm32h723/memory.x create mode 100644 examples/stm32h723/src/bin/spdifrx.rs diff --git a/examples/stm32h723/.cargo/config.toml b/examples/stm32h723/.cargo/config.toml new file mode 100644 index 000000000..2e53663c5 --- /dev/null +++ b/examples/stm32h723/.cargo/config.toml @@ -0,0 +1,8 @@ +[target.thumbv7em-none-eabihf] +runner = 'probe-rs run --chip STM32H723ZGTx' + +[build] +target = "thumbv7em-none-eabihf" # Cortex-M4F and Cortex-M7F (with FPU) + +[env] +DEFMT_LOG = "trace" diff --git a/examples/stm32h723/Cargo.toml b/examples/stm32h723/Cargo.toml new file mode 100644 index 000000000..8ebc1051f --- /dev/null +++ b/examples/stm32h723/Cargo.toml @@ -0,0 +1,69 @@ +[package] +edition = "2021" +name = "embassy-stm32h7-examples" +version = "0.1.0" +license = "MIT OR Apache-2.0" + +[dependencies] +# Change stm32h723zg to your chip name, if necessary. +embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["defmt", "stm32h723zg", "time-driver-tim2", "exti", "memory-x", "unstable-pac", "chrono"] } +embassy-sync = { version = "0.6.0", path = "../../embassy-sync", features = ["defmt"] } +embassy-executor = { version = "0.6.2", path = "../../embassy-executor", features = ["task-arena-size-32768", "arch-cortex-m", "executor-thread", "executor-interrupt", "defmt", "integrated-timers"] } +embassy-time = { version = "0.3.2", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } +embassy-futures = { version = "0.1.0", path = "../../embassy-futures" } + +defmt = "0.3" +defmt-rtt = "0.4" + +cortex-m = { version = "0.7.6", features = ["inline-asm", "critical-section-single-core"] } +cortex-m-rt = "0.7.0" +embedded-hal = "0.2.6" +embedded-hal-1 = { package = "embedded-hal", version = "1.0" } +embedded-hal-async = { version = "1.0" } +embedded-nal-async = "0.8.0" +embedded-io-async = { version = "0.6.1" } +panic-probe = { version = "0.3", features = ["print-defmt"] } +heapless = { version = "0.8", default-features = false } +rand_core = "0.6.3" +critical-section = "1.1" +static_cell = "2" +chrono = { version = "^0.4", default-features = false } +grounded = "0.2.0" + +# cargo build/run +[profile.dev] +codegen-units = 1 +debug = 2 +debug-assertions = true # <- +incremental = false +opt-level = 3 # <- +overflow-checks = true # <- + +# cargo test +[profile.test] +codegen-units = 1 +debug = 2 +debug-assertions = true # <- +incremental = false +opt-level = 3 # <- +overflow-checks = true # <- + +# cargo build/run --release +[profile.release] +codegen-units = 1 +debug = 2 +debug-assertions = false # <- +incremental = false +lto = 'fat' +opt-level = 3 # <- +overflow-checks = false # <- + +# cargo test --release +[profile.bench] +codegen-units = 1 +debug = 2 +debug-assertions = false # <- +incremental = false +lto = 'fat' +opt-level = 3 # <- +overflow-checks = false # <- diff --git a/examples/stm32h723/build.rs b/examples/stm32h723/build.rs new file mode 100644 index 000000000..30691aa97 --- /dev/null +++ b/examples/stm32h723/build.rs @@ -0,0 +1,35 @@ +//! This build script copies the `memory.x` file from the crate root into +//! a directory where the linker can always find it at build time. +//! For many projects this is optional, as the linker always searches the +//! project root directory -- wherever `Cargo.toml` is. However, if you +//! are using a workspace or have a more complicated build setup, this +//! build script becomes required. Additionally, by requesting that +//! Cargo re-run the build script whenever `memory.x` is changed, +//! updating `memory.x` ensures a rebuild of the application with the +//! new memory settings. + +use std::env; +use std::fs::File; +use std::io::Write; +use std::path::PathBuf; + +fn main() { + // Put `memory.x` in our output directory and ensure it's + // on the linker search path. + let out = &PathBuf::from(env::var_os("OUT_DIR").unwrap()); + File::create(out.join("memory.x")) + .unwrap() + .write_all(include_bytes!("memory.x")) + .unwrap(); + println!("cargo:rustc-link-search={}", out.display()); + + // By default, Cargo will re-run a build script whenever + // any file in the project changes. By specifying `memory.x` + // here, we ensure the build script is only re-run when + // `memory.x` is changed. + println!("cargo:rerun-if-changed=memory.x"); + + println!("cargo:rustc-link-arg-bins=--nmagic"); + println!("cargo:rustc-link-arg-bins=-Tlink.x"); + println!("cargo:rustc-link-arg-bins=-Tdefmt.x"); +} diff --git a/examples/stm32h723/memory.x b/examples/stm32h723/memory.x new file mode 100644 index 000000000..aa4c00505 --- /dev/null +++ b/examples/stm32h723/memory.x @@ -0,0 +1,106 @@ +MEMORY +{ + /* This file is intended for parts in the STM32H723 family. (RM0468) */ + /* - FLASH and RAM are mandatory memory sections. */ + /* - The sum of all non-FLASH sections must add to 564k total device RAM. */ + /* - The FLASH section size must match your device, see table below. */ + + /* FLASH */ + /* Select the appropriate FLASH size for your device. */ + /* - STM32H730xB 128K */ + /* - STM32H723xE/725xE 512K */ + /* - STM32H723xG/725xG/733xG/735xG 1M */ + FLASH1 : ORIGIN = 0x08000000, LENGTH = 1M + + /* Data TCM */ + /* - Two contiguous 64KB RAMs. */ + /* - Used for interrupt handlers, stacks and general RAM. */ + /* - Zero wait-states. */ + /* - The DTCM is taken as the origin of the base ram. (See below.) */ + /* This is also where the interrupt table and such will live, */ + /* which is required for deterministic performance. */ + DTCM : ORIGIN = 0x20000000, LENGTH = 128K + + /* Instruction TCM */ + /* - More memory can be assigned to ITCM. See AXI SRAM notes, below. */ + /* - Used for latency-critical interrupt handlers etc. */ + /* - Zero wait-states. */ + ITCM : ORIGIN = 0x00000000, LENGTH = 64K + 0K + + /* AXI SRAM */ + /* - AXISRAM is in D1 and accessible by all system masters except BDMA. */ + /* - Suitable for application data not stored in DTCM. */ + /* - Zero wait-states. */ + /* - The 192k of extra shared RAM is fully allotted to the AXI SRAM by default. */ + /* As a result: 64k (64k + 0k) for ITCM and 320k (128k + 192k) for AXI SRAM. */ + /* This can be re-configured via the TCM_AXI_SHARED[1,0] register when more */ + /* ITCM is required. */ + AXISRAM : ORIGIN = 0x24000000, LENGTH = 128K + 192K + + /* AHB SRAM */ + /* - SRAM1-2 are in D2 and accessible by all system masters except BDMA, LTDC */ + /* and SDMMC1. Suitable for use as DMA buffers. */ + /* - SRAM4 is in D3 and additionally accessible by the BDMA. Used for BDMA */ + /* buffers, for storing application data in lower-power modes. */ + /* - Zero wait-states. */ + SRAM1 : ORIGIN = 0x30000000, LENGTH = 16K + SRAM2 : ORIGIN = 0x30040000, LENGTH = 16K + SRAM4 : ORIGIN = 0x38000000, LENGTH = 16K + + /* Backup SRAM */ + /* Used to store data during low-power sleeps. */ + BSRAM : ORIGIN = 0x38800000, LENGTH = 4K +} + +/* +/* Assign the memory regions defined above for use. */ +/* + +/* Provide the mandatory FLASH and RAM definitions for cortex-m-rt's linker script. */ +REGION_ALIAS(FLASH, FLASH1); +REGION_ALIAS(RAM, DTCM); + +/* The location of the stack can be overridden using the `_stack_start` symbol. */ +/* - Set the stack location at the end of RAM, using all remaining space. */ +_stack_start = ORIGIN(RAM) + LENGTH(RAM); + +/* The location of the .text section can be overridden using the */ +/* `_stext` symbol. By default it will place after .vector_table. */ +/* _stext = ORIGIN(FLASH) + 0x40c; */ + +/* Define sections for placing symbols into the extra memory regions above. */ +/* This makes them accessible from code. */ +/* - ITCM, DTCM and AXISRAM connect to a 64-bit wide bus -> align to 8 bytes. */ +/* - All other memories connect to a 32-bit wide bus -> align to 4 bytes. */ +SECTIONS { + .itcm (NOLOAD) : ALIGN(8) { + *(.itcm .itcm.*); + . = ALIGN(8); + } > ITCM + + .axisram (NOLOAD) : ALIGN(8) { + *(.axisram .axisram.*); + . = ALIGN(8); + } > AXISRAM + + .sram1 (NOLOAD) : ALIGN(4) { + *(.sram1 .sram1.*); + . = ALIGN(4); + } > SRAM1 + + .sram2 (NOLOAD) : ALIGN(4) { + *(.sram2 .sram2.*); + . = ALIGN(4); + } > SRAM2 + + .sram4 (NOLOAD) : ALIGN(4) { + *(.sram4 .sram4.*); + . = ALIGN(4); + } > SRAM4 + + .bsram (NOLOAD) : ALIGN(4) { + *(.bsram .bsram.*); + . = ALIGN(4); + } > BSRAM + +}; diff --git a/examples/stm32h723/src/bin/spdifrx.rs b/examples/stm32h723/src/bin/spdifrx.rs new file mode 100644 index 000000000..deee5ca8d --- /dev/null +++ b/examples/stm32h723/src/bin/spdifrx.rs @@ -0,0 +1,161 @@ +//! This example receives inputs on SPDIFRX and outputs on SAI4. +//! +//! Only very few controllers connect the SPDIFRX symbol clock to a SAI peripheral's clock input. +//! However, this is necessary for synchronizing the symbol rates and avoiding glitches. +#![no_std] +#![no_main] + +use defmt::{info, trace}; +use embassy_executor::Spawner; +use embassy_stm32::spdifrx::{self, Spdifrx}; +use embassy_stm32::{bind_interrupts, peripherals, sai}; +use grounded::uninit::GroundedArrayCell; +use hal::sai::*; +use {defmt_rtt as _, embassy_stm32 as hal, panic_probe as _}; + +bind_interrupts!(struct Irqs { + SPDIF_RX => spdifrx::GlobalInterruptHandler; +}); + +const CHANNEL_COUNT: usize = 2; +const BLOCK_LENGTH: usize = 64; +const HALF_DMA_BUFFER_LENGTH: usize = BLOCK_LENGTH * CHANNEL_COUNT; +const DMA_BUFFER_LENGTH: usize = HALF_DMA_BUFFER_LENGTH * 2; // 2 half-blocks + +// DMA buffers must be in special regions. Refer https://embassy.dev/book/#_stm32_bdma_only_working_out_of_some_ram_regions +#[link_section = ".sram1"] +static mut SPDIFRX_BUFFER: GroundedArrayCell = GroundedArrayCell::uninit(); + +#[link_section = ".sram4"] +static mut SAI_BUFFER: GroundedArrayCell = GroundedArrayCell::uninit(); + +#[embassy_executor::main] +async fn main(_spawner: Spawner) { + let mut peripheral_config = embassy_stm32::Config::default(); + { + use embassy_stm32::rcc::*; + peripheral_config.rcc.hsi = Some(HSIPrescaler::DIV1); + peripheral_config.rcc.pll1 = Some(Pll { + source: PllSource::HSI, + prediv: PllPreDiv::DIV16, + mul: PllMul::MUL200, + divp: Some(PllDiv::DIV2), // 400 MHz + divq: Some(PllDiv::DIV2), + divr: Some(PllDiv::DIV2), + }); + peripheral_config.rcc.sys = Sysclk::PLL1_P; + peripheral_config.rcc.ahb_pre = AHBPrescaler::DIV2; + peripheral_config.rcc.apb1_pre = APBPrescaler::DIV2; + peripheral_config.rcc.apb2_pre = APBPrescaler::DIV2; + peripheral_config.rcc.apb3_pre = APBPrescaler::DIV2; + peripheral_config.rcc.apb4_pre = APBPrescaler::DIV2; + + peripheral_config.rcc.mux.spdifrxsel = mux::Spdifrxsel::PLL1_Q; + } + let mut p = embassy_stm32::init(peripheral_config); + + info!("SPDIFRX to SAI4 bridge"); + + // Use SPDIFRX clock for SAI. + // This ensures equal rates of sample production and consumption. + let clk_source = embassy_stm32::pac::rcc::vals::Saiasel::_RESERVED_5; + embassy_stm32::pac::RCC.d3ccipr().modify(|w| { + w.set_sai4asel(clk_source); + }); + + let sai_buffer: &mut [u32] = unsafe { + SAI_BUFFER.initialize_all_copied(0); + let (ptr, len) = SAI_BUFFER.get_ptr_len(); + core::slice::from_raw_parts_mut(ptr, len) + }; + + let spdifrx_buffer: &mut [u32] = unsafe { + SPDIFRX_BUFFER.initialize_all_copied(0); + let (ptr, len) = SPDIFRX_BUFFER.get_ptr_len(); + core::slice::from_raw_parts_mut(ptr, len) + }; + + let mut spdif_receiver = new_spdif_receiver(&mut p.SPDIFRX1, &mut p.PD7, &mut p.DMA2_CH7, spdifrx_buffer); + let mut sai_transmitter = new_sai_transmitter( + &mut p.SAI4, + &mut p.PD13, + &mut p.PC1, + &mut p.PD12, + &mut p.BDMA_CH0, + sai_buffer, + ); + + spdif_receiver.start(); + sai_transmitter.start(); + + loop { + let mut buf = [0u32; HALF_DMA_BUFFER_LENGTH]; + + match spdif_receiver.read_data(&mut buf).await { + Ok(_) => (), + Err(spdifrx::Error::RingbufferError(_)) => { + trace!("SPDIFRX ringbuffer error. Renew."); + drop(spdif_receiver); + spdif_receiver = new_spdif_receiver(&mut p.SPDIFRX1, &mut p.PD7, &mut p.DMA2_CH7, spdifrx_buffer); + spdif_receiver.start(); + continue; + } + Err(spdifrx::Error::ChannelSyncError) => { + trace!("SPDIFRX channel sync (left/right assignment) error."); + continue; + } + Err(spdifrx::Error::SourceSyncError) => { + trace!("SPDIFRX source sync error, e.g. disconnect."); + continue; + } + }; + + if sai_transmitter.write(&buf).await.is_err() { + trace!("Renew SAI."); + drop(sai_transmitter); + sai_transmitter = new_sai_transmitter( + &mut p.SAI4, + &mut p.PD13, + &mut p.PC1, + &mut p.PD12, + &mut p.BDMA_CH0, + sai_buffer, + ); + sai_transmitter.start(); + } + } +} + +/// Creates a new SPDIFRX instance for receiving sample data. +/// +/// Used (again) after dropping the SPDIFRX instance, in case of errors (e.g. source disconnect). +fn new_spdif_receiver<'d>( + spdifrx: &'d mut peripherals::SPDIFRX1, + input_pin: &'d mut peripherals::PD7, + dma: &'d mut peripherals::DMA2_CH7, + buf: &'d mut [u32], +) -> Spdifrx<'d, peripherals::SPDIFRX1> { + Spdifrx::new_data_only(spdifrx, Irqs, spdifrx::Config::default(), input_pin, dma, buf) +} + +/// Creates a new SAI4 instance for transmitting sample data. +/// +/// Used (again) after dropping the SAI4 instance, in case of errors (e.g. buffer overrun). +fn new_sai_transmitter<'d>( + sai: &'d mut peripherals::SAI4, + sck: &'d mut peripherals::PD13, + sd: &'d mut peripherals::PC1, + fs: &'d mut peripherals::PD12, + dma: &'d mut peripherals::BDMA_CH0, + buf: &'d mut [u32], +) -> Sai<'d, peripherals::SAI4, u32> { + let mut sai_config = hal::sai::Config::default(); + sai_config.slot_count = hal::sai::word::U4(CHANNEL_COUNT as u8); + sai_config.slot_enable = 0xFFFF; // All slots + sai_config.data_size = sai::DataSize::Data32; + sai_config.frame_length = (CHANNEL_COUNT * 32) as u8; + sai_config.master_clock_divider = hal::sai::MasterClockDivider::MasterClockDisabled; + + let (sub_block_tx, _) = hal::sai::split_subblocks(sai); + Sai::new_asynchronous(sub_block_tx, sck, sd, fs, dma, buf, sai_config) +} From de7fae18226e88cfeccf904974d46a51d9ed893e Mon Sep 17 00:00:00 2001 From: elagil Date: Mon, 18 Nov 2024 20:51:23 +0100 Subject: [PATCH 216/361] fix: enable backup symbol clock --- embassy-stm32/src/spdifrx/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/embassy-stm32/src/spdifrx/mod.rs b/embassy-stm32/src/spdifrx/mod.rs index bfc8bff29..a205780ad 100644 --- a/embassy-stm32/src/spdifrx/mod.rs +++ b/embassy-stm32/src/spdifrx/mod.rs @@ -194,7 +194,7 @@ impl<'d, T: Instance> Spdifrx<'d, T> { cr.set_cksen(true); // Generate a symbol clock. #[cfg(stm32h7)] - cr.set_cksbkpen(false); // Do not generate a backup symbol clock. + cr.set_cksbkpen(true); // Generate a backup symbol clock. }); } From e8b03b75ec9be06a4d9bc476a2ebc1e1b14ac517 Mon Sep 17 00:00:00 2001 From: elagil Date: Mon, 18 Nov 2024 20:51:23 +0100 Subject: [PATCH 217/361] fix: use latest SAI driver --- examples/stm32h723/src/bin/spdifrx.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/examples/stm32h723/src/bin/spdifrx.rs b/examples/stm32h723/src/bin/spdifrx.rs index deee5ca8d..36c017592 100644 --- a/examples/stm32h723/src/bin/spdifrx.rs +++ b/examples/stm32h723/src/bin/spdifrx.rs @@ -86,7 +86,6 @@ async fn main(_spawner: Spawner) { ); spdif_receiver.start(); - sai_transmitter.start(); loop { let mut buf = [0u32; HALF_DMA_BUFFER_LENGTH]; From 99dd5e79db88cf91ee700c00cce87387c6a67d6f Mon Sep 17 00:00:00 2001 From: elagil Date: Mon, 18 Nov 2024 20:51:23 +0100 Subject: [PATCH 218/361] chore: update for latest SAI features --- examples/stm32h723/src/bin/spdifrx.rs | 53 +++++++++++++++------------ 1 file changed, 29 insertions(+), 24 deletions(-) diff --git a/examples/stm32h723/src/bin/spdifrx.rs b/examples/stm32h723/src/bin/spdifrx.rs index 36c017592..69ef5cd07 100644 --- a/examples/stm32h723/src/bin/spdifrx.rs +++ b/examples/stm32h723/src/bin/spdifrx.rs @@ -7,6 +7,7 @@ use defmt::{info, trace}; use embassy_executor::Spawner; +use embassy_futures::select::{self, select, Either}; use embassy_stm32::spdifrx::{self, Spdifrx}; use embassy_stm32::{bind_interrupts, peripherals, sai}; use grounded::uninit::GroundedArrayCell; @@ -75,7 +76,6 @@ async fn main(_spawner: Spawner) { core::slice::from_raw_parts_mut(ptr, len) }; - let mut spdif_receiver = new_spdif_receiver(&mut p.SPDIFRX1, &mut p.PD7, &mut p.DMA2_CH7, spdifrx_buffer); let mut sai_transmitter = new_sai_transmitter( &mut p.SAI4, &mut p.PD13, @@ -84,32 +84,15 @@ async fn main(_spawner: Spawner) { &mut p.BDMA_CH0, sai_buffer, ); - + let mut spdif_receiver = new_spdif_receiver(&mut p.SPDIFRX1, &mut p.PD7, &mut p.DMA2_CH7, spdifrx_buffer); spdif_receiver.start(); + let mut renew_sai = false; loop { let mut buf = [0u32; HALF_DMA_BUFFER_LENGTH]; - match spdif_receiver.read_data(&mut buf).await { - Ok(_) => (), - Err(spdifrx::Error::RingbufferError(_)) => { - trace!("SPDIFRX ringbuffer error. Renew."); - drop(spdif_receiver); - spdif_receiver = new_spdif_receiver(&mut p.SPDIFRX1, &mut p.PD7, &mut p.DMA2_CH7, spdifrx_buffer); - spdif_receiver.start(); - continue; - } - Err(spdifrx::Error::ChannelSyncError) => { - trace!("SPDIFRX channel sync (left/right assignment) error."); - continue; - } - Err(spdifrx::Error::SourceSyncError) => { - trace!("SPDIFRX source sync error, e.g. disconnect."); - continue; - } - }; - - if sai_transmitter.write(&buf).await.is_err() { + if renew_sai { + renew_sai = false; trace!("Renew SAI."); drop(sai_transmitter); sai_transmitter = new_sai_transmitter( @@ -120,8 +103,30 @@ async fn main(_spawner: Spawner) { &mut p.BDMA_CH0, sai_buffer, ); - sai_transmitter.start(); } + + match select(spdif_receiver.read(&mut buf), sai_transmitter.wait_write_error()).await { + Either::First(spdif_read_result) => match spdif_read_result { + Ok(_) => (), + Err(spdifrx::Error::RingbufferError(_)) => { + trace!("SPDIFRX ringbuffer error. Renew."); + drop(spdif_receiver); + spdif_receiver = new_spdif_receiver(&mut p.SPDIFRX1, &mut p.PD7, &mut p.DMA2_CH7, spdifrx_buffer); + spdif_receiver.start(); + continue; + } + Err(spdifrx::Error::ChannelSyncError) => { + trace!("SPDIFRX channel sync (left/right assignment) error."); + continue; + } + }, + Either::Second(_) => { + renew_sai = true; + continue; + } + }; + + renew_sai = sai_transmitter.write(&buf).await.is_err(); } } @@ -134,7 +139,7 @@ fn new_spdif_receiver<'d>( dma: &'d mut peripherals::DMA2_CH7, buf: &'d mut [u32], ) -> Spdifrx<'d, peripherals::SPDIFRX1> { - Spdifrx::new_data_only(spdifrx, Irqs, spdifrx::Config::default(), input_pin, dma, buf) + Spdifrx::new(spdifrx, Irqs, spdifrx::Config::default(), input_pin, dma, buf) } /// Creates a new SAI4 instance for transmitting sample data. From 1e0b6eefb79d478216d81437ec41474cbe8408e6 Mon Sep 17 00:00:00 2001 From: Priit Laes Date: Tue, 19 Nov 2024 09:55:39 +0200 Subject: [PATCH 219/361] nrf: Remove leftover info message and fix link to errata --- embassy-nrf/src/gpio.rs | 1 - embassy-nrf/src/qspi.rs | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/embassy-nrf/src/gpio.rs b/embassy-nrf/src/gpio.rs index d271dbcff..130339c15 100644 --- a/embassy-nrf/src/gpio.rs +++ b/embassy-nrf/src/gpio.rs @@ -350,7 +350,6 @@ impl<'d> Flex<'d> { convert_drive(w, drive); w.set_sense(vals::Sense::DISABLED); }); - info!("pin_cnf: {:08x}", self.pin.conf().read().0); } /// Put the pin into input + output mode. diff --git a/embassy-nrf/src/qspi.rs b/embassy-nrf/src/qspi.rs index de9c268c1..255b43c33 100755 --- a/embassy-nrf/src/qspi.rs +++ b/embassy-nrf/src/qspi.rs @@ -546,7 +546,7 @@ impl<'d, T: Instance> Drop for Qspi<'d, T> { // it seems events_ready is not generated in response to deactivate. nrfx doesn't wait for it. r.tasks_deactivate().write_value(1); - // Workaround https://infocenter.nordicsemi.com/topic/errata_nRF52840_Rev1/ERR/nRF52840/Rev1/latest/anomaly_840_122.html?cp=4_0_1_2_1_7 + // Workaround https://docs.nordicsemi.com/bundle/errata_nRF52840_Rev3/page/ERR/nRF52840/Rev3/latest/anomaly_840_122.html // Note that the doc has 2 register writes, but the first one is really the write to tasks_deactivate, // so we only do the second one here. unsafe { ptr::write_volatile(0x40029054 as *mut u32, 1) } From ff02ee1a221122ede6e30a94156c42e22b400578 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?D=C3=A1niel=20Buga?= Date: Tue, 19 Nov 2024 14:39:32 +0100 Subject: [PATCH 220/361] Only set callback once --- embassy-executor/CHANGELOG.md | 2 ++ embassy-executor/src/raw/mod.rs | 12 +++++++----- 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/embassy-executor/CHANGELOG.md b/embassy-executor/CHANGELOG.md index e07e3924f..eae5e3c2b 100644 --- a/embassy-executor/CHANGELOG.md +++ b/embassy-executor/CHANGELOG.md @@ -7,6 +7,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## Unreleased +- Only set integrated-timers callbacks once per executor. + ## 0.6.3 - 2024-11-12 - Building with the `nightly` feature now works with the Xtensa Rust compiler 1.82. diff --git a/embassy-executor/src/raw/mod.rs b/embassy-executor/src/raw/mod.rs index d9ea5c005..e8a5b8970 100644 --- a/embassy-executor/src/raw/mod.rs +++ b/embassy-executor/src/raw/mod.rs @@ -328,7 +328,7 @@ impl SyncExecutor { #[cfg(feature = "integrated-timers")] let alarm = unsafe { unwrap!(embassy_time_driver::allocate_alarm()) }; - Self { + let this = Self { run_queue: RunQueue::new(), pender, @@ -336,7 +336,12 @@ impl SyncExecutor { timer_queue: timer_queue::TimerQueue::new(), #[cfg(feature = "integrated-timers")] alarm, - } + }; + + #[cfg(feature = "integrated-timers")] + embassy_time_driver::set_alarm_callback(this.alarm, Self::alarm_callback, &this as *const _ as *mut ()); + + this } /// Enqueue a task in the task queue @@ -374,9 +379,6 @@ impl SyncExecutor { /// /// Same as [`Executor::poll`], plus you must only call this on the thread this executor was created. pub(crate) unsafe fn poll(&'static self) { - #[cfg(feature = "integrated-timers")] - embassy_time_driver::set_alarm_callback(self.alarm, Self::alarm_callback, self as *const _ as *mut ()); - #[allow(clippy::never_loop)] loop { #[cfg(feature = "integrated-timers")] From 8ebe059ecb311ee949f92dde33f2cb8d972b0f7b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?D=C3=A1niel=20Buga?= Date: Tue, 19 Nov 2024 15:59:31 +0100 Subject: [PATCH 221/361] Add initialize --- embassy-executor/CHANGELOG.md | 2 +- embassy-executor/src/arch/avr.rs | 4 ++++ embassy-executor/src/arch/cortex_m.rs | 6 ++++++ embassy-executor/src/arch/riscv32.rs | 4 ++++ embassy-executor/src/arch/spin.rs | 4 ++++ embassy-executor/src/arch/std.rs | 4 ++++ embassy-executor/src/arch/wasm.rs | 4 ++++ embassy-executor/src/raw/mod.rs | 21 ++++++++++++++++----- embassy-executor/tests/test.rs | 4 ++++ embassy-stm32/src/low_power.rs | 12 +++++++++--- 10 files changed, 56 insertions(+), 9 deletions(-) diff --git a/embassy-executor/CHANGELOG.md b/embassy-executor/CHANGELOG.md index eae5e3c2b..00b1bef28 100644 --- a/embassy-executor/CHANGELOG.md +++ b/embassy-executor/CHANGELOG.md @@ -7,7 +7,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## Unreleased -- Only set integrated-timers callbacks once per executor. +- `raw::Executor` now has an `fn initialize` that must be called once before starting to poll it. ## 0.6.3 - 2024-11-12 diff --git a/embassy-executor/src/arch/avr.rs b/embassy-executor/src/arch/avr.rs index 70085d04d..7f9ed4421 100644 --- a/embassy-executor/src/arch/avr.rs +++ b/embassy-executor/src/arch/avr.rs @@ -53,6 +53,10 @@ mod thread { /// /// This function never returns. pub fn run(&'static mut self, init: impl FnOnce(Spawner)) -> ! { + unsafe { + self.inner.initialize(); + } + init(self.inner.spawner()); loop { diff --git a/embassy-executor/src/arch/cortex_m.rs b/embassy-executor/src/arch/cortex_m.rs index 5c517e0a2..0c2af88a6 100644 --- a/embassy-executor/src/arch/cortex_m.rs +++ b/embassy-executor/src/arch/cortex_m.rs @@ -98,6 +98,9 @@ mod thread { /// /// This function never returns. pub fn run(&'static mut self, init: impl FnOnce(Spawner)) -> ! { + unsafe { + self.inner.initialize(); + } init(self.inner.spawner()); loop { @@ -207,6 +210,9 @@ mod interrupt { } let executor = unsafe { (&*self.executor.get()).assume_init_ref() }; + unsafe { + executor.initialize(); + } unsafe { NVIC::unmask(irq) } diff --git a/embassy-executor/src/arch/riscv32.rs b/embassy-executor/src/arch/riscv32.rs index 01e63a9fd..715e5f3cf 100644 --- a/embassy-executor/src/arch/riscv32.rs +++ b/embassy-executor/src/arch/riscv32.rs @@ -54,6 +54,10 @@ mod thread { /// /// This function never returns. pub fn run(&'static mut self, init: impl FnOnce(Spawner)) -> ! { + unsafe { + self.inner.initialize(); + } + init(self.inner.spawner()); loop { diff --git a/embassy-executor/src/arch/spin.rs b/embassy-executor/src/arch/spin.rs index 340023620..54c7458b3 100644 --- a/embassy-executor/src/arch/spin.rs +++ b/embassy-executor/src/arch/spin.rs @@ -48,6 +48,10 @@ mod thread { /// /// This function never returns. pub fn run(&'static mut self, init: impl FnOnce(Spawner)) -> ! { + unsafe { + self.inner.initialize(); + } + init(self.inner.spawner()); loop { diff --git a/embassy-executor/src/arch/std.rs b/embassy-executor/src/arch/std.rs index b02b15988..948c7711b 100644 --- a/embassy-executor/src/arch/std.rs +++ b/embassy-executor/src/arch/std.rs @@ -55,6 +55,10 @@ mod thread { /// /// This function never returns. pub fn run(&'static mut self, init: impl FnOnce(Spawner)) -> ! { + unsafe { + self.inner.initialize(); + } + init(self.inner.spawner()); loop { diff --git a/embassy-executor/src/arch/wasm.rs b/embassy-executor/src/arch/wasm.rs index f9d0f935c..35025f11f 100644 --- a/embassy-executor/src/arch/wasm.rs +++ b/embassy-executor/src/arch/wasm.rs @@ -70,6 +70,10 @@ mod thread { /// - a `static mut` (unsafe) /// - a local variable in a function you know never returns (like `fn main() -> !`), upgrading its lifetime with `transmute`. (unsafe) pub fn start(&'static mut self, init: impl FnOnce(Spawner)) { + unsafe { + self.inner.initialize(); + } + unsafe { let executor = &self.inner; let future = Closure::new(move |_| { diff --git a/embassy-executor/src/raw/mod.rs b/embassy-executor/src/raw/mod.rs index e8a5b8970..ebabee1ba 100644 --- a/embassy-executor/src/raw/mod.rs +++ b/embassy-executor/src/raw/mod.rs @@ -328,7 +328,7 @@ impl SyncExecutor { #[cfg(feature = "integrated-timers")] let alarm = unsafe { unwrap!(embassy_time_driver::allocate_alarm()) }; - let this = Self { + Self { run_queue: RunQueue::new(), pender, @@ -336,12 +336,12 @@ impl SyncExecutor { timer_queue: timer_queue::TimerQueue::new(), #[cfg(feature = "integrated-timers")] alarm, - }; + } + } + pub(crate) unsafe fn initialize(&'static self) { #[cfg(feature = "integrated-timers")] - embassy_time_driver::set_alarm_callback(this.alarm, Self::alarm_callback, &this as *const _ as *mut ()); - - this + embassy_time_driver::set_alarm_callback(self.alarm, Self::alarm_callback, self as *const _ as *mut ()); } /// Enqueue a task in the task queue @@ -494,6 +494,15 @@ impl Executor { } } + /// Initializes the executor. + /// + /// # Safety + /// + /// This function must be called once before any other method is called. + pub unsafe fn initialize(&'static self) { + self.inner.initialize(); + } + /// Spawn a task in this executor. /// /// # Safety @@ -518,6 +527,8 @@ impl Executor { /// /// # Safety /// + /// You must call `initialize` before calling this method. + /// /// You must NOT call `poll` reentrantly on the same executor. /// /// In particular, note that `poll` may call the pender synchronously. Therefore, you diff --git a/embassy-executor/tests/test.rs b/embassy-executor/tests/test.rs index 348cc7dc4..8054bf7eb 100644 --- a/embassy-executor/tests/test.rs +++ b/embassy-executor/tests/test.rs @@ -40,6 +40,10 @@ fn setup() -> (&'static Executor, Trace) { let trace = Trace::new(); let context = Box::leak(Box::new(trace.clone())) as *mut _ as *mut (); let executor = &*Box::leak(Box::new(Executor::new(context))); + unsafe { + executor.initialize(); + } + (executor, trace) } diff --git a/embassy-stm32/src/low_power.rs b/embassy-stm32/src/low_power.rs index a779b8a09..d74221864 100644 --- a/embassy-stm32/src/low_power.rs +++ b/embassy-stm32/src/low_power.rs @@ -155,7 +155,9 @@ impl Executor { time_driver: get_driver(), }); - EXECUTOR.as_mut().unwrap() + let executor = EXECUTOR.as_mut().unwrap(); + + executor }) } @@ -241,11 +243,15 @@ impl Executor { /// /// This function never returns. pub fn run(&'static mut self, init: impl FnOnce(Spawner)) -> ! { - init(unsafe { EXECUTOR.as_mut().unwrap() }.inner.spawner()); + let executor = unsafe { EXECUTOR.as_mut().unwrap() }; + unsafe { + executor.inner.initialize(); + } + init(executor.inner.spawner()); loop { unsafe { - EXECUTOR.as_mut().unwrap().inner.poll(); + executor.inner.poll(); self.configure_pwr(); asm!("wfe"); }; From 448db47263701bf09352eaba785492baa968e76f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?D=C3=A1niel=20Buga?= Date: Tue, 19 Nov 2024 17:30:31 +0100 Subject: [PATCH 222/361] Remove noop from queue_generic --- embassy-time/src/queue_generic.rs | 2 -- 1 file changed, 2 deletions(-) diff --git a/embassy-time/src/queue_generic.rs b/embassy-time/src/queue_generic.rs index 4882afd3e..0068edae8 100644 --- a/embassy-time/src/queue_generic.rs +++ b/embassy-time/src/queue_generic.rs @@ -142,8 +142,6 @@ impl Queue { critical_section::with(|cs| { let mut inner = self.inner.borrow_ref_mut(cs); - if inner.is_none() {} - inner .get_or_insert_with(|| { let handle = unsafe { allocate_alarm() }.unwrap(); From 3402e76f9876d58d271501102e2d80c9b2c389b3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aur=C3=A9lien=20Jacobs?= Date: Tue, 19 Nov 2024 17:26:45 +0100 Subject: [PATCH 223/361] stm32/timer: avoid max_compare_value >= u16::MAX With STM32 32 bits timers, the max_compare_value (AKA, ARR register) can currently be set greater than u16::MAX, which leads to the following assert!(max < u16::MAX as u32) in max_duty_cycle() when setting up a 1 kHz SimplePwm on 84 MHz MCU. The issue is fixed by forcing a max_compare_value that fits into 16 bits when setting the frequency for a PWM. --- embassy-stm32/src/timer/complementary_pwm.rs | 2 +- embassy-stm32/src/timer/low_level.rs | 24 +++++++++++++------- embassy-stm32/src/timer/simple_pwm.rs | 2 +- 3 files changed, 18 insertions(+), 10 deletions(-) diff --git a/embassy-stm32/src/timer/complementary_pwm.rs b/embassy-stm32/src/timer/complementary_pwm.rs index 46ccbf3df..02c01e900 100644 --- a/embassy-stm32/src/timer/complementary_pwm.rs +++ b/embassy-stm32/src/timer/complementary_pwm.rs @@ -116,7 +116,7 @@ impl<'d, T: AdvancedInstance4Channel> ComplementaryPwm<'d, T> { } else { 1u8 }; - self.inner.set_frequency(freq * multiplier); + self.inner.set_frequency_internal(freq * multiplier, 16); } /// Get max duty value. diff --git a/embassy-stm32/src/timer/low_level.rs b/embassy-stm32/src/timer/low_level.rs index 3136ea4e9..7360d6aef 100644 --- a/embassy-stm32/src/timer/low_level.rs +++ b/embassy-stm32/src/timer/low_level.rs @@ -242,16 +242,28 @@ impl<'d, T: CoreInstance> Timer<'d, T> { /// 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) { + match T::BITS { + TimerBits::Bits16 => { + self.set_frequency_internal(frequency, 16); + } + #[cfg(not(stm32l0))] + TimerBits::Bits32 => { + self.set_frequency_internal(frequency, 32); + } + } + } + + pub(crate) fn set_frequency_internal(&self, frequency: Hertz, max_divide_by_bits: u8) { let f = frequency.0; assert!(f > 0); let timer_f = T::frequency().0; + let pclk_ticks_per_timer_period = (timer_f / f) as u64; + let psc: u16 = unwrap!(((pclk_ticks_per_timer_period - 1) / (1 << max_divide_by_bits)).try_into()); + let divide_by = pclk_ticks_per_timer_period / (u64::from(psc) + 1); + 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)); @@ -265,10 +277,6 @@ impl<'d, T: CoreInstance> Timer<'d, T> { } #[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 divide_by = pclk_ticks_per_timer_period / (u64::from(psc) + 1); - // the timer counts `0..=arr`, we want it to count `0..divide_by` let arr: u32 = unwrap!(u32::try_from(divide_by - 1)); diff --git a/embassy-stm32/src/timer/simple_pwm.rs b/embassy-stm32/src/timer/simple_pwm.rs index 9e4a09095..56fb1871e 100644 --- a/embassy-stm32/src/timer/simple_pwm.rs +++ b/embassy-stm32/src/timer/simple_pwm.rs @@ -278,7 +278,7 @@ impl<'d, T: GeneralInstance4Channel> SimplePwm<'d, T> { } else { 1u8 }; - self.inner.set_frequency(freq * multiplier); + self.inner.set_frequency_internal(freq * multiplier, 16); } /// Get max duty value. From 0740b235ac546081d54d103b0cdd018e7ef2581c Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Wed, 20 Nov 2024 23:19:37 +0100 Subject: [PATCH 224/361] nrf: Add NFCT driver. Co-Authored-By: turbocool3r Co-Authored-By: ferris --- embassy-nrf/src/chips/nrf52832.rs | 3 + embassy-nrf/src/chips/nrf52833.rs | 3 + embassy-nrf/src/chips/nrf52840.rs | 3 + embassy-nrf/src/chips/nrf5340_app.rs | 3 + embassy-nrf/src/lib.rs | 8 + embassy-nrf/src/nfct.rs | 409 +++++++++++++++++++++++++++ examples/nrf52840/src/bin/nfct.rs | 79 ++++++ 7 files changed, 508 insertions(+) create mode 100644 embassy-nrf/src/nfct.rs create mode 100644 examples/nrf52840/src/bin/nfct.rs diff --git a/embassy-nrf/src/chips/nrf52832.rs b/embassy-nrf/src/chips/nrf52832.rs index 7ff58d63e..2d9346229 100644 --- a/embassy-nrf/src/chips/nrf52832.rs +++ b/embassy-nrf/src/chips/nrf52832.rs @@ -161,6 +161,9 @@ embassy_hal_internal::peripherals! { EGU3, EGU4, EGU5, + + // NFC + NFCT, } impl_uarte!(UARTE0, UARTE0, UARTE0); diff --git a/embassy-nrf/src/chips/nrf52833.rs b/embassy-nrf/src/chips/nrf52833.rs index a6273452a..4e4915c32 100644 --- a/embassy-nrf/src/chips/nrf52833.rs +++ b/embassy-nrf/src/chips/nrf52833.rs @@ -181,6 +181,9 @@ embassy_hal_internal::peripherals! { EGU3, EGU4, EGU5, + + // NFC + NFCT, } impl_usb!(USBD, USBD, USBD); diff --git a/embassy-nrf/src/chips/nrf52840.rs b/embassy-nrf/src/chips/nrf52840.rs index fb341afd5..70a56971b 100644 --- a/embassy-nrf/src/chips/nrf52840.rs +++ b/embassy-nrf/src/chips/nrf52840.rs @@ -184,6 +184,9 @@ embassy_hal_internal::peripherals! { EGU3, EGU4, EGU5, + + // NFC + NFCT, } impl_usb!(USBD, USBD, USBD); diff --git a/embassy-nrf/src/chips/nrf5340_app.rs b/embassy-nrf/src/chips/nrf5340_app.rs index 6dc64fb4f..bc613e351 100644 --- a/embassy-nrf/src/chips/nrf5340_app.rs +++ b/embassy-nrf/src/chips/nrf5340_app.rs @@ -176,6 +176,9 @@ embassy_hal_internal::peripherals! { // NVMC NVMC, + // NFC + NFCT, + // UARTE, TWI & SPI SERIAL0, SERIAL1, diff --git a/embassy-nrf/src/lib.rs b/embassy-nrf/src/lib.rs index 33111e1bd..7c5dd7c83 100644 --- a/embassy-nrf/src/lib.rs +++ b/embassy-nrf/src/lib.rs @@ -86,6 +86,14 @@ pub mod gpiote; #[cfg(any(feature = "nrf52832", feature = "nrf52833", feature = "nrf52840"))] pub mod i2s; #[cfg(not(feature = "_nrf54l"))] // TODO +#[cfg(any( + feature = "nrf52832", + feature = "nrf52833", + feature = "nrf52840", + feature = "_nrf5340-app" +))] +pub mod nfct; +#[cfg(not(feature = "_nrf54l"))] // TODO pub mod nvmc; #[cfg(not(feature = "_nrf54l"))] // TODO #[cfg(any( diff --git a/embassy-nrf/src/nfct.rs b/embassy-nrf/src/nfct.rs new file mode 100644 index 000000000..2756c7952 --- /dev/null +++ b/embassy-nrf/src/nfct.rs @@ -0,0 +1,409 @@ +//! NFC tag emulator driver. +//! +//! This driver implements support for emulating an ISO14443-3 card. Anticollision and selection +//! are handled automatically in hardware, then the driver lets you receive and reply to +//! raw ISO14443-3 frames in software. +//! +//! Higher layers such as ISO14443-4 aka ISO-DEP and ISO7816 must be handled on top +//! in software. + +#![macro_use] + +use core::future::poll_fn; +use core::sync::atomic::{compiler_fence, Ordering}; +use core::task::Poll; + +use embassy_hal_internal::{into_ref, PeripheralRef}; +use embassy_sync::waitqueue::AtomicWaker; +pub use vals::{Bitframesdd as SddPat, Discardmode as DiscardMode}; + +use crate::interrupt::InterruptExt; +use crate::pac::nfct::vals; +use crate::peripherals::NFCT; +use crate::util::slice_in_ram; +use crate::{interrupt, pac, Peripheral}; + +/// NFCID1 (aka UID) of different sizes. +#[derive(Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug)] +pub enum NfcId { + /// 4-byte UID. + SingleSize([u8; 4]), + /// 7-byte UID. + DoubleSize([u8; 7]), + /// 10-byte UID. + TripleSize([u8; 10]), +} + +/// The protocol field to be sent in the `SEL_RES` response byte (b6-b7). +#[derive(Default, Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug)] +pub enum SelResProtocol { + /// Configured for Type 2 Tag platform. + #[default] + Type2 = 0, + /// Configured for Type 4A Tag platform, compliant with ISO/IEC_14443. + Type4A = 1, + /// Configured for the NFC-DEP Protocol. + NfcDep = 2, + /// Configured for the NFC-DEP Protocol and Type 4A Tag platform. + NfcDepAndType4A = 3, +} + +/// Config for the `NFCT` peripheral driver. +#[derive(Clone)] +pub struct Config { + /// NFCID1 to use during autocollision. + pub nfcid1: NfcId, + /// SDD pattern to be sent in `SENS_RES`. + pub sdd_pat: SddPat, + /// Platform config to be sent in `SEL_RES`. + pub plat_conf: u8, + /// Protocol to be sent in the `SEL_RES` response. + pub protocol: SelResProtocol, +} + +/// Interrupt handler. +pub struct InterruptHandler { + _private: (), +} + +impl interrupt::typelevel::Handler for InterruptHandler { + unsafe fn on_interrupt() { + trace!("irq"); + pac::NFCT.inten().write(|w| w.0 = 0); + WAKER.wake(); + } +} + +static WAKER: AtomicWaker = AtomicWaker::new(); + +/// NFC error. +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +#[non_exhaustive] +pub enum Error { + /// Rx Error received while waiting for frame + RxError, + /// Rx buffer was overrun, increase your buffer size to resolve this + RxOverrun, + /// Lost field. + Deactivated, + /// Collision + Collision, + /// The buffer is not in data RAM. It's most likely in flash, and nRF's DMA cannot access flash. + BufferNotInRAM, +} + +/// NFC tag emulator driver. +pub struct NfcT<'d> { + _p: PeripheralRef<'d, NFCT>, + rx_buf: [u8; 256], + tx_buf: [u8; 256], +} + +impl<'d> NfcT<'d> { + /// Create an Nfc Tag driver + pub fn new( + _p: impl Peripheral

+ 'd, + _irq: impl interrupt::typelevel::Binding + 'd, + config: &Config, + ) -> Self { + into_ref!(_p); + + let r = pac::NFCT; + + unsafe { + let reset = (r.as_ptr() as *mut u32).add(0xFFC / 4); + reset.write_volatile(0); + reset.read_volatile(); + reset.write_volatile(1); + } + + let nfcid_size = match &config.nfcid1 { + NfcId::SingleSize(bytes) => { + r.nfcid1_last().write(|w| w.0 = u32::from_be_bytes(*bytes)); + + vals::Nfcidsize::NFCID1SINGLE + } + NfcId::DoubleSize(bytes) => { + let (bytes, chunk) = bytes.split_last_chunk::<4>().unwrap(); + r.nfcid1_last().write(|w| w.0 = u32::from_be_bytes(*chunk)); + + let mut chunk = [0u8; 4]; + chunk[1..].copy_from_slice(bytes); + r.nfcid1_2nd_last().write(|w| w.0 = u32::from_be_bytes(chunk)); + + vals::Nfcidsize::NFCID1DOUBLE + } + NfcId::TripleSize(bytes) => { + let (bytes, chunk) = bytes.split_last_chunk::<4>().unwrap(); + r.nfcid1_last().write(|w| w.0 = u32::from_be_bytes(*chunk)); + + let (bytes, chunk2) = bytes.split_last_chunk::<3>().unwrap(); + let mut chunk = [0u8; 4]; + chunk[1..].copy_from_slice(chunk2); + r.nfcid1_2nd_last().write(|w| w.0 = u32::from_be_bytes(chunk)); + + let mut chunk = [0u8; 4]; + chunk[1..].copy_from_slice(bytes); + r.nfcid1_3rd_last().write(|w| w.0 = u32::from_be_bytes(chunk)); + + vals::Nfcidsize::NFCID1TRIPLE + } + }; + + r.sensres().write(|w| { + w.set_nfcidsize(nfcid_size); + w.set_bitframesdd(config.sdd_pat); + w.set_platfconfig(config.plat_conf & 0xF); + }); + + r.selres().write(|w| { + w.set_protocol(config.protocol as u8); + }); + + // errata + #[cfg(feature = "nrf52832")] + unsafe { + // Errata 57 nrf52832 only + //(0x40005610 as *mut u32).write_volatile(0x00000005); + //(0x40005688 as *mut u32).write_volatile(0x00000001); + //(0x40005618 as *mut u32).write_volatile(0x00000000); + //(0x40005614 as *mut u32).write_volatile(0x0000003F); + + // Errata 98 + (0x4000568C as *mut u32).write_volatile(0x00038148); + } + + r.inten().write(|w| w.0 = 0); + + interrupt::NFCT.unpend(); + unsafe { interrupt::NFCT.enable() }; + + // clear all shorts + r.shorts().write(|_| {}); + + let res = Self { + _p, + tx_buf: [0u8; 256], + rx_buf: [0u8; 256], + }; + + assert!(slice_in_ram(&res.tx_buf), "TX Buf not in ram"); + assert!(slice_in_ram(&res.rx_buf), "RX Buf not in ram"); + + res + } + + /// Wait for field on and select. + /// + /// This waits for the field to become on, and then for a reader to select us. The ISO14443-3 + /// sense, anticollision and select procedure is handled entirely in hardware. + /// + /// When this returns, we have successfully been selected as a card. You must then + /// loop calling [`receive`](Self::receive) and responding with [`transmit`](Self::transmit). + pub async fn activate(&mut self) { + let r = pac::NFCT; + loop { + r.events_fieldlost().write_value(0); + r.events_fielddetected().write_value(0); + r.tasks_sense().write_value(1); + + // enable autocoll + #[cfg(not(feature = "nrf52832"))] + r.autocolresconfig().write(|w| w.0 = 0b10); + + r.framedelaymode().write(|w| { + w.set_framedelaymode(vals::Framedelaymode::WINDOW_GRID); + }); + + info!("waiting for field"); + poll_fn(|cx| { + WAKER.register(cx.waker()); + + if r.events_fielddetected().read() != 0 { + r.events_fielddetected().write_value(0); + return Poll::Ready(()); + } + + r.inten().write(|w| { + w.set_fielddetected(true); + }); + Poll::Pending + }) + .await; + + #[cfg(feature = "time")] + embassy_time::Timer::after_millis(1).await; // workaround errata 190 + + r.events_selected().write_value(0); + r.tasks_activate().write_value(1); + + trace!("Waiting to be selected"); + poll_fn(|cx| { + let r = pac::NFCT; + + WAKER.register(cx.waker()); + + if r.events_selected().read() != 0 || r.events_fieldlost().read() != 0 { + return Poll::Ready(()); + } + + r.inten().write(|w| { + w.set_selected(true); + w.set_fieldlost(true); + }); + Poll::Pending + }) + .await; + if r.events_fieldlost().read() != 0 { + continue; + } + + // TODO: add support for "window" frame delay, which is technically + // needed to be compliant with iso14443-4 + r.framedelaymode().write(|w| { + w.set_framedelaymode(vals::Framedelaymode::FREE_RUN); + }); + + // disable autocoll + #[cfg(not(feature = "nrf52832"))] + r.autocolresconfig().write(|w| w.0 = 0b11u32); + + return; + } + } + + /// Transmit an ISO14443-3 frame to the reader. + /// + /// You must call this only after receiving a frame with [`receive`](Self::receive), + /// and only once. Higher-layer protocols usually define timeouts, so calling this + /// too late can cause things to fail. + /// + /// This will fail with [`Error::Deactivated`] if we have been deselected due to either + /// the field being switched off or due to the ISO14443 state machine. When this happens, + /// you must stop calling [`receive`](Self::receive) and [`transmit`](Self::transmit), reset + /// all protocol state, and go back to calling [`activate`](Self::activate). + pub async fn transmit(&mut self, buf: &[u8]) -> Result<(), Error> { + let r = pac::NFCT; + + //Setup DMA + self.tx_buf[..buf.len()].copy_from_slice(buf); + r.packetptr().write_value(self.tx_buf.as_ptr() as u32); + r.maxlen().write(|w| w.0 = buf.len() as _); + + // Set packet length + r.txd().amount().write(|w| { + w.set_txdatabits(0); + w.set_txdatabytes(buf.len() as _); + }); + + r.txd().frameconfig().write(|w| { + w.set_crcmodetx(true); + w.set_discardmode(DiscardMode::DISCARD_END); + w.set_parity(true); + w.set_sof(true); + }); + + r.events_error().write_value(0); + r.events_txframeend().write_value(0); + r.errorstatus().write(|w| w.0 = 0xffff_ffff); + + // Start starttx task + compiler_fence(Ordering::SeqCst); + r.tasks_starttx().write_value(1); + + poll_fn(move |cx| { + trace!("polling tx"); + let r = pac::NFCT; + WAKER.register(cx.waker()); + + if r.events_fieldlost().read() != 0 { + return Poll::Ready(Err(Error::Deactivated)); + } + + if r.events_txframeend().read() != 0 { + trace!("Txframend hit, should be finished trasmitting"); + return Poll::Ready(Ok(())); + } + + if r.events_error().read() != 0 { + trace!("Got error?"); + warn!("errors: {:08x}", r.errorstatus().read().0); + r.events_error().write_value(0); + return Poll::Ready(Err(Error::RxError)); + } + + r.inten().write(|w| { + w.set_txframeend(true); + w.set_error(true); + w.set_fieldlost(true); + }); + + Poll::Pending + }) + .await + } + + /// Receive an ISO14443-3 frame from the reader. + /// + /// After calling this, you must send back a response with [`transmit`](Self::transmit), + /// and only once. Higher-layer protocols usually define timeouts, so calling this + /// too late can cause things to fail. + pub async fn receive(&mut self, buf: &mut [u8]) -> Result { + let r = pac::NFCT; + + r.rxd().frameconfig().write(|w| { + w.set_crcmoderx(true); + w.set_parity(true); + w.set_sof(true); + }); + + //Setup DMA + r.packetptr().write_value(self.rx_buf.as_mut_ptr() as u32); + r.maxlen().write(|w| w.0 = self.rx_buf.len() as _); + + // Reset and enable the end event + r.events_rxframeend().write_value(0); + r.events_rxerror().write_value(0); + + // Start enablerxdata only after configs are finished writing + compiler_fence(Ordering::SeqCst); + r.tasks_enablerxdata().write_value(1); + + poll_fn(move |cx| { + trace!("polling rx"); + let r = pac::NFCT; + WAKER.register(cx.waker()); + + if r.events_fieldlost().read() != 0 { + return Poll::Ready(Err(Error::Deactivated)); + } + + if r.events_rxerror().read() != 0 { + trace!("RXerror got in recv frame, should be back in idle state"); + r.events_rxerror().write_value(0); + warn!("errors: {:08x}", r.errorstatus().read().0); + return Poll::Ready(Err(Error::RxError)); + } + + if r.events_rxframeend().read() != 0 { + trace!("RX Frameend got in recv frame, should have data"); + r.events_rxframeend().write_value(0); + return Poll::Ready(Ok(())); + } + + r.inten().write(|w| { + w.set_rxframeend(true); + w.set_rxerror(true); + w.set_fieldlost(true); + }); + + Poll::Pending + }) + .await?; + + let n = r.rxd().amount().read().rxdatabytes() as usize - 2; + buf[..n].copy_from_slice(&self.rx_buf[..n]); + Ok(n) + } +} diff --git a/examples/nrf52840/src/bin/nfct.rs b/examples/nrf52840/src/bin/nfct.rs new file mode 100644 index 000000000..d559d006a --- /dev/null +++ b/examples/nrf52840/src/bin/nfct.rs @@ -0,0 +1,79 @@ +#![no_std] +#![no_main] + +use defmt::*; +use embassy_executor::Spawner; +use embassy_nrf::config::HfclkSource; +use embassy_nrf::nfct::{Config as NfcConfig, NfcId, NfcT}; +use embassy_nrf::{bind_interrupts, nfct}; +use {defmt_rtt as _, embassy_nrf as _, panic_probe as _}; + +bind_interrupts!(struct Irqs { + NFCT => nfct::InterruptHandler; +}); + +#[embassy_executor::main] +async fn main(_spawner: Spawner) { + let mut config = embassy_nrf::config::Config::default(); + config.hfclk_source = HfclkSource::ExternalXtal; + let p = embassy_nrf::init(config); + + dbg!("Setting up..."); + let config = NfcConfig { + nfcid1: NfcId::DoubleSize([0x04, 0x68, 0x95, 0x71, 0xFA, 0x5C, 0x64]), + sdd_pat: nfct::SddPat::SDD00100, + plat_conf: 0b0000, + protocol: nfct::SelResProtocol::Type4A, + }; + + let mut nfc = NfcT::new(p.NFCT, Irqs, &config); + + let mut buf = [0u8; 256]; + + loop { + info!("activating"); + nfc.activate().await; + + loop { + info!("rxing"); + let n = match nfc.receive(&mut buf).await { + Ok(n) => n, + Err(e) => { + error!("rx error {}", e); + break; + } + }; + let req = &buf[..n]; + info!("received frame {:02x}", req); + + let mut deselect = false; + let resp = match req { + [0xe0, ..] => { + info!("Got RATS, tx'ing ATS"); + &[0x06, 0x77, 0x77, 0x81, 0x02, 0x80][..] + } + [0xc2] => { + info!("Got deselect!"); + deselect = true; + &[0xc2] + } + _ => { + info!("Got unknown command!"); + &[0xFF] + } + }; + + match nfc.transmit(resp).await { + Ok(()) => {} + Err(e) => { + error!("tx error {}", e); + break; + } + } + + if deselect { + break; + } + } + } +} From be6eec772673ae6c46a6a022838b78cdc167bae4 Mon Sep 17 00:00:00 2001 From: Joseph <239260+simryang@users.noreply.github.com> Date: Thu, 21 Nov 2024 09:46:10 +0900 Subject: [PATCH 225/361] Update README.md (#3553) Fixed broken link to w5500-evb-pico --- embassy-net-wiznet/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/embassy-net-wiznet/README.md b/embassy-net-wiznet/README.md index 786aab18d..b66619821 100644 --- a/embassy-net-wiznet/README.md +++ b/embassy-net-wiznet/README.md @@ -2,7 +2,7 @@ [`embassy-net`](https://crates.io/crates/embassy-net) integration for the WIZnet SPI ethernet chips, operating in MACRAW mode. -See [`examples`](https://github.com/embassy-rs/embassy/tree/main/examples/rp) directory for usage examples with the rp2040 [`WIZnet W5500-EVB-Pico`](https://www.wiznet.io/product-item/w5500-evb-pico/) module. +See [`examples`](https://github.com/embassy-rs/embassy/tree/main/examples/rp) directory for usage examples with the rp2040 [`WIZnet W5500-EVB-Pico`](https://docs.wiznet.io/Product/iEthernet/W5500/w5500-evb-pico)) module. ## Supported chips From f598cae3766cd24894e665c145181a5bbeac24c2 Mon Sep 17 00:00:00 2001 From: Christian Enderle Date: Thu, 21 Nov 2024 12:12:00 +0100 Subject: [PATCH 226/361] compute lse and lsi frequency for STM32L and STM32U0 series --- embassy-stm32/src/rcc/l.rs | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/embassy-stm32/src/rcc/l.rs b/embassy-stm32/src/rcc/l.rs index 6120d33be..8223a657b 100644 --- a/embassy-stm32/src/rcc/l.rs +++ b/embassy-stm32/src/rcc/l.rs @@ -5,6 +5,7 @@ use crate::pac::rcc::regs::Cfgr; pub use crate::pac::rcc::vals::Hsepre as HsePrescaler; pub use crate::pac::rcc::vals::{Hpre as AHBPrescaler, Msirange as MSIRange, Ppre as APBPrescaler, Sw as Sysclk}; use crate::pac::{FLASH, RCC}; +use crate::rcc::LSI_FREQ; use crate::time::Hertz; /// HSI speed @@ -182,6 +183,9 @@ pub(crate) unsafe fn init(config: Config) { let rtc = config.ls.init(); + let lse = config.ls.lse.map(|l| l.frequency); + let lsi = config.ls.lsi.then_some(LSI_FREQ); + let msi = config.msi.map(|range| { msi_enable(range); msirange_to_hertz(range) @@ -425,12 +429,12 @@ pub(crate) unsafe fn init(config: Config) { dsi_phy: None, // DSI PLL clock not supported, don't call `RccPeripheral::frequency()` in the drivers rtc: rtc, + lse: lse, + lsi: lsi, // TODO sai1_extclk: None, sai2_extclk: None, - lsi: None, - lse: None, ); } From bd65906d149719ef3da214e72876b13579e8d392 Mon Sep 17 00:00:00 2001 From: Gabriel Smith Date: Thu, 21 Nov 2024 17:23:46 -0500 Subject: [PATCH 227/361] STM32H5xx ADC (#3557) * stm32: Update STM32 data source * stm32h5: Add ADC example --- embassy-stm32/Cargo.toml | 4 +-- embassy-stm32/src/lib.rs | 2 +- examples/stm32h5/src/bin/adc.rs | 59 +++++++++++++++++++++++++++++++++ 3 files changed, 62 insertions(+), 3 deletions(-) create mode 100644 examples/stm32h5/src/bin/adc.rs diff --git a/embassy-stm32/Cargo.toml b/embassy-stm32/Cargo.toml index 4fe4ff358..91d218da4 100644 --- a/embassy-stm32/Cargo.toml +++ b/embassy-stm32/Cargo.toml @@ -72,7 +72,7 @@ rand_core = "0.6.3" sdio-host = "0.5.0" critical-section = "1.1" #stm32-metapac = { version = "15" } -stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-7bb5f235587c3a6886a7be1c8f58fdf22c5257f3" } +stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-55b491ee982e52f80a21276a0ccbde4907982b5d" } vcell = "0.1.3" nb = "1.0.0" @@ -101,7 +101,7 @@ proc-macro2 = "1.0.36" quote = "1.0.15" #stm32-metapac = { version = "15", default-features = false, features = ["metadata"]} -stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-7bb5f235587c3a6886a7be1c8f58fdf22c5257f3", default-features = false, features = ["metadata"] } +stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-55b491ee982e52f80a21276a0ccbde4907982b5d", default-features = false, features = ["metadata"] } [features] default = ["rt"] diff --git a/embassy-stm32/src/lib.rs b/embassy-stm32/src/lib.rs index e189351d1..39f3dfd61 100644 --- a/embassy-stm32/src/lib.rs +++ b/embassy-stm32/src/lib.rs @@ -440,7 +440,7 @@ fn init_hw(config: Config) -> Peripherals { cr.set_stop(config.enable_debug_during_sleep); cr.set_standby(config.enable_debug_during_sleep); } - #[cfg(any(dbgmcu_f0, dbgmcu_c0, dbgmcu_g0, dbgmcu_u5, dbgmcu_wba, dbgmcu_l5))] + #[cfg(any(dbgmcu_f0, dbgmcu_c0, dbgmcu_g0, dbgmcu_u0, dbgmcu_u5, dbgmcu_wba, dbgmcu_l5))] { cr.set_dbg_stop(config.enable_debug_during_sleep); cr.set_dbg_standby(config.enable_debug_during_sleep); diff --git a/examples/stm32h5/src/bin/adc.rs b/examples/stm32h5/src/bin/adc.rs new file mode 100644 index 000000000..c5d508ece --- /dev/null +++ b/examples/stm32h5/src/bin/adc.rs @@ -0,0 +1,59 @@ +#![no_std] +#![no_main] + +use defmt::*; +use embassy_executor::Spawner; +use embassy_stm32::adc::{Adc, SampleTime}; +use embassy_stm32::Config; +use embassy_time::Timer; +use {defmt_rtt as _, panic_probe as _}; + +#[embassy_executor::main] +async fn main(_spawner: Spawner) { + let mut config = Config::default(); + { + use embassy_stm32::rcc::*; + config.rcc.hsi = Some(HSIPrescaler::DIV1); + config.rcc.csi = true; + config.rcc.pll1 = Some(Pll { + source: PllSource::HSI, + prediv: PllPreDiv::DIV4, + mul: PllMul::MUL25, + divp: Some(PllDiv::DIV2), + divq: Some(PllDiv::DIV4), // SPI1 cksel defaults to pll1_q + divr: None, + }); + config.rcc.pll2 = Some(Pll { + source: PllSource::HSI, + prediv: PllPreDiv::DIV4, + mul: PllMul::MUL25, + divp: None, + divq: None, + divr: Some(PllDiv::DIV4), // 100mhz + }); + config.rcc.sys = Sysclk::PLL1_P; // 200 Mhz + config.rcc.ahb_pre = AHBPrescaler::DIV1; // 200 Mhz + config.rcc.apb1_pre = APBPrescaler::DIV2; // 100 Mhz + config.rcc.apb2_pre = APBPrescaler::DIV2; // 100 Mhz + config.rcc.apb3_pre = APBPrescaler::DIV2; // 100 Mhz + config.rcc.voltage_scale = VoltageScale::Scale1; + config.rcc.mux.adcdacsel = mux::Adcdacsel::PLL2_R; + } + let mut p = embassy_stm32::init(config); + + info!("Hello World!"); + + let mut adc = Adc::new(p.ADC1); + + adc.set_sample_time(SampleTime::CYCLES24_5); + + let mut vrefint_channel = adc.enable_vrefint(); + + loop { + let vrefint = adc.blocking_read(&mut vrefint_channel); + info!("vrefint: {}", vrefint); + let measured = adc.blocking_read(&mut p.PA0); + info!("measured: {}", measured); + Timer::after_millis(500).await; + } +} From e05f6da2692f2b61986ef2b77789e27d68e4e74a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?D=C3=A1niel=20Buga?= Date: Fri, 22 Nov 2024 09:21:44 +0100 Subject: [PATCH 228/361] Generalize AtomicWaker --- embassy-sync/CHANGELOG.md | 1 + embassy-sync/src/waitqueue/atomic_waker.rs | 42 ++++++++++++++++------ 2 files changed, 33 insertions(+), 10 deletions(-) diff --git a/embassy-sync/CHANGELOG.md b/embassy-sync/CHANGELOG.md index a7dd6f66e..d12f0a74a 100644 --- a/embassy-sync/CHANGELOG.md +++ b/embassy-sync/CHANGELOG.md @@ -12,6 +12,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Add `clear`, `len`, `is_empty` and `is_full` functions to `zerocopy_channel`. - Add `capacity`, `free_capacity`, `clear`, `len`, `is_empty` and `is_full` functions to `channel::{Sender, Receiver}`. - Add `capacity`, `free_capacity`, `clear`, `len`, `is_empty` and `is_full` functions to `priority_channel::{Sender, Receiver}`. +- Add `GenericAtomicWaker` utility. ## 0.6.0 - 2024-05-29 diff --git a/embassy-sync/src/waitqueue/atomic_waker.rs b/embassy-sync/src/waitqueue/atomic_waker.rs index 63fe04a6e..231902c5a 100644 --- a/embassy-sync/src/waitqueue/atomic_waker.rs +++ b/embassy-sync/src/waitqueue/atomic_waker.rs @@ -1,26 +1,25 @@ use core::cell::Cell; use core::task::Waker; -use crate::blocking_mutex::raw::CriticalSectionRawMutex; +use crate::blocking_mutex::raw::{CriticalSectionRawMutex, RawMutex}; use crate::blocking_mutex::Mutex; /// Utility struct to register and wake a waker. -pub struct AtomicWaker { - waker: Mutex>>, +pub struct GenericAtomicWaker { + waker: Mutex>>, } -impl AtomicWaker { +impl GenericAtomicWaker { /// Create a new `AtomicWaker`. - pub const fn new() -> Self { + pub const fn new(mutex: M) -> Self { Self { - waker: Mutex::const_new(CriticalSectionRawMutex::new(), Cell::new(None)), + waker: Mutex::const_new(mutex, Cell::new(None)), } } /// Register a waker. Overwrites the previous waker, if any. pub fn register(&self, w: &Waker) { - critical_section::with(|cs| { - let cell = self.waker.borrow(cs); + self.waker.lock(|cell| { cell.set(match cell.replace(None) { Some(w2) if (w2.will_wake(w)) => Some(w2), _ => Some(w.clone()), @@ -30,8 +29,7 @@ impl AtomicWaker { /// Wake the registered waker, if any. pub fn wake(&self) { - critical_section::with(|cs| { - let cell = self.waker.borrow(cs); + self.waker.lock(|cell| { if let Some(w) = cell.replace(None) { w.wake_by_ref(); cell.set(Some(w)); @@ -39,3 +37,27 @@ impl AtomicWaker { }) } } + +/// Utility struct to register and wake a waker. +pub struct AtomicWaker { + waker: GenericAtomicWaker, +} + +impl AtomicWaker { + /// Create a new `AtomicWaker`. + pub const fn new() -> Self { + Self { + waker: GenericAtomicWaker::new(CriticalSectionRawMutex::new()), + } + } + + /// Register a waker. Overwrites the previous waker, if any. + pub fn register(&self, w: &Waker) { + self.waker.register(w); + } + + /// Wake the registered waker, if any. + pub fn wake(&self) { + self.waker.wake(); + } +} From 48fd80919a3248e8e6ce5f188cd0364f9fb3dc85 Mon Sep 17 00:00:00 2001 From: Christian Enderle Date: Thu, 21 Nov 2024 19:41:41 +0100 Subject: [PATCH 229/361] rcc: enable lse for stm32u0 --- embassy-stm32/src/rcc/bd.rs | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/embassy-stm32/src/rcc/bd.rs b/embassy-stm32/src/rcc/bd.rs index 4aec3756f..791367954 100644 --- a/embassy-stm32/src/rcc/bd.rs +++ b/embassy-stm32/src/rcc/bd.rs @@ -159,6 +159,9 @@ impl LsConfig { } else { None }; + #[cfg(rcc_u0)] + let lse_sysen = Some(lse_en); + _ = lse_drv; // not all chips have it. // Disable backup domain write protection @@ -199,7 +202,7 @@ impl LsConfig { } ok &= reg.lseon() == lse_en; ok &= reg.lsebyp() == lse_byp; - #[cfg(any(rcc_l5, rcc_u5, rcc_wle, rcc_wl5, rcc_wba))] + #[cfg(any(rcc_l5, rcc_u5, rcc_wle, rcc_wl5, rcc_wba, rcc_u0))] if let Some(lse_sysen) = lse_sysen { ok &= reg.lsesysen() == lse_sysen; } @@ -251,7 +254,7 @@ impl LsConfig { while !bdcr().read().lserdy() {} - #[cfg(any(rcc_l5, rcc_u5, rcc_wle, rcc_wl5, rcc_wba))] + #[cfg(any(rcc_l5, rcc_u5, rcc_wle, rcc_wl5, rcc_wba, rcc_u0))] if let Some(lse_sysen) = lse_sysen { bdcr().modify(|w| { w.set_lsesysen(lse_sysen); From 29934237a5d6fa783a3f7a83ab569c8aef9b95fe Mon Sep 17 00:00:00 2001 From: Christian Enderle Date: Thu, 21 Nov 2024 19:44:01 +0100 Subject: [PATCH 230/361] rtc: reorganize low-power SealedInstance --- embassy-stm32/src/rtc/v3.rs | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/embassy-stm32/src/rtc/v3.rs b/embassy-stm32/src/rtc/v3.rs index 12cb10bc7..a15d6ddc8 100644 --- a/embassy-stm32/src/rtc/v3.rs +++ b/embassy-stm32/src/rtc/v3.rs @@ -133,14 +133,20 @@ impl SealedInstance for crate::peripherals::RTC { cfg_if::cfg_if!( if #[cfg(stm32g4)] { const EXTI_WAKEUP_LINE: usize = 20; - type WakeupInterrupt = crate::interrupt::typelevel::RTC_WKUP; } else if #[cfg(stm32g0)] { const EXTI_WAKEUP_LINE: usize = 19; - type WakeupInterrupt = crate::interrupt::typelevel::RTC_TAMP; } else if #[cfg(any(stm32l5, stm32h5))] { const EXTI_WAKEUP_LINE: usize = 17; - type WakeupInterrupt = crate::interrupt::typelevel::RTC; - } else if #[cfg(stm32u5)] { + } + ); + + #[cfg(feature = "low-power")] + cfg_if::cfg_if!( + if #[cfg(stm32g4)] { + type WakeupInterrupt = crate::interrupt::typelevel::RTC_WKUP; + } else if #[cfg(any(stm32g0))] { + type WakeupInterrupt = crate::interrupt::typelevel::RTC_TAMP; + } else if #[cfg(any(stm32l5, stm32h5, stm32u5))] { type WakeupInterrupt = crate::interrupt::typelevel::RTC; } ); From a49289ce7be3e0d213d26acc1055c37bcb98f4ba Mon Sep 17 00:00:00 2001 From: Christian Enderle Date: Thu, 21 Nov 2024 19:44:48 +0100 Subject: [PATCH 231/361] low-power: add basic support for stm32u0 --- embassy-stm32/src/low_power.rs | 11 +++++++++++ embassy-stm32/src/rtc/low_power.rs | 6 +++--- embassy-stm32/src/rtc/mod.rs | 2 +- embassy-stm32/src/rtc/v3.rs | 2 +- 4 files changed, 16 insertions(+), 5 deletions(-) diff --git a/embassy-stm32/src/low_power.rs b/embassy-stm32/src/low_power.rs index d74221864..1423c9538 100644 --- a/embassy-stm32/src/low_power.rs +++ b/embassy-stm32/src/low_power.rs @@ -80,6 +80,17 @@ foreach_interrupt! { }; } +// only relevant for stm32u0 +foreach_interrupt! { + (RTC, rtc, $block:ident, TAMP, $irq:ident) => { + #[interrupt] + #[allow(non_snake_case)] + unsafe fn $irq() { + EXECUTOR.as_mut().unwrap().on_wakeup_irq(); + } + }; +} + #[allow(dead_code)] pub(crate) unsafe fn on_wakeup_irq() { EXECUTOR.as_mut().unwrap().on_wakeup_irq(); diff --git a/embassy-stm32/src/rtc/low_power.rs b/embassy-stm32/src/rtc/low_power.rs index bba359f31..5bf8742de 100644 --- a/embassy-stm32/src/rtc/low_power.rs +++ b/embassy-stm32/src/rtc/low_power.rs @@ -65,7 +65,7 @@ pub(crate) enum WakeupPrescaler { Div16 = 16, } -#[cfg(any(stm32f4, stm32l0, stm32g4, stm32l4, stm32l5, stm32wb, stm32h5, stm32g0, stm32u5))] +#[cfg(any(stm32f4, stm32l0, stm32g4, stm32l4, stm32l5, stm32wb, stm32h5, stm32g0, stm32u5, stm32u0))] impl From for crate::pac::rtc::vals::Wucksel { fn from(val: WakeupPrescaler) -> Self { use crate::pac::rtc::vals::Wucksel; @@ -79,7 +79,7 @@ impl From for crate::pac::rtc::vals::Wucksel { } } -#[cfg(any(stm32f4, stm32l0, stm32g4, stm32l4, stm32l5, stm32wb, stm32h5, stm32g0, stm32u5))] +#[cfg(any(stm32f4, stm32l0, stm32g4, stm32l4, stm32l5, stm32wb, stm32h5, stm32g0, stm32u5, stm32u0))] impl From for WakeupPrescaler { fn from(val: crate::pac::rtc::vals::Wucksel) -> Self { use crate::pac::rtc::vals::Wucksel; @@ -223,7 +223,7 @@ impl Rtc { ::WakeupInterrupt::unpend(); unsafe { ::WakeupInterrupt::enable() }; - #[cfg(not(stm32u5))] + #[cfg(not(any(stm32u5, stm32u0)))] { use crate::pac::EXTI; EXTI.rtsr(0).modify(|w| w.set_line(RTC::EXTI_WAKEUP_LINE, true)); diff --git a/embassy-stm32/src/rtc/mod.rs b/embassy-stm32/src/rtc/mod.rs index 3722d11ab..1a668cb37 100644 --- a/embassy-stm32/src/rtc/mod.rs +++ b/embassy-stm32/src/rtc/mod.rs @@ -285,7 +285,7 @@ trait SealedInstance { const BACKUP_REGISTER_COUNT: usize; #[cfg(feature = "low-power")] - #[cfg(not(stm32u5))] + #[cfg(not(any(stm32u5, stm32u0)))] const EXTI_WAKEUP_LINE: usize; #[cfg(feature = "low-power")] diff --git a/embassy-stm32/src/rtc/v3.rs b/embassy-stm32/src/rtc/v3.rs index a15d6ddc8..d543955a4 100644 --- a/embassy-stm32/src/rtc/v3.rs +++ b/embassy-stm32/src/rtc/v3.rs @@ -144,7 +144,7 @@ impl SealedInstance for crate::peripherals::RTC { cfg_if::cfg_if!( if #[cfg(stm32g4)] { type WakeupInterrupt = crate::interrupt::typelevel::RTC_WKUP; - } else if #[cfg(any(stm32g0))] { + } else if #[cfg(any(stm32g0, stm32u0))] { type WakeupInterrupt = crate::interrupt::typelevel::RTC_TAMP; } else if #[cfg(any(stm32l5, stm32h5, stm32u5))] { type WakeupInterrupt = crate::interrupt::typelevel::RTC; From 1fbb419f72a2663b472f31ae1e172bec0e4e6d1a Mon Sep 17 00:00:00 2001 From: Christian Enderle Date: Thu, 21 Nov 2024 19:49:32 +0100 Subject: [PATCH 232/361] low-power: add support for stop for stm32u0 --- embassy-stm32/src/low_power.rs | 9 +++++---- embassy-stm32/src/rtc/low_power.rs | 8 ++++++-- embassy-stm32/src/rtc/v3.rs | 2 +- 3 files changed, 12 insertions(+), 7 deletions(-) diff --git a/embassy-stm32/src/low_power.rs b/embassy-stm32/src/low_power.rs index 1423c9538..0b87bd95a 100644 --- a/embassy-stm32/src/low_power.rs +++ b/embassy-stm32/src/low_power.rs @@ -70,6 +70,7 @@ use crate::rtc::Rtc; static mut EXECUTOR: Option = None; +#[cfg(not(stm32u0))] foreach_interrupt! { (RTC, rtc, $block:ident, WKUP, $irq:ident) => { #[interrupt] @@ -80,7 +81,7 @@ foreach_interrupt! { }; } -// only relevant for stm32u0 +#[cfg(stm32u0)] foreach_interrupt! { (RTC, rtc, $block:ident, TAMP, $irq:ident) => { #[interrupt] @@ -123,10 +124,10 @@ pub enum StopMode { Stop2, } -#[cfg(any(stm32l4, stm32l5, stm32u5))] +#[cfg(any(stm32l4, stm32l5, stm32u5, stm32u0))] use stm32_metapac::pwr::vals::Lpms; -#[cfg(any(stm32l4, stm32l5, stm32u5))] +#[cfg(any(stm32l4, stm32l5, stm32u5, stm32u0))] impl Into for StopMode { fn into(self) -> Lpms { match self { @@ -197,7 +198,7 @@ impl Executor { #[allow(unused_variables)] fn configure_stop(&mut self, stop_mode: StopMode) { - #[cfg(any(stm32l4, stm32l5, stm32u5))] + #[cfg(any(stm32l4, stm32l5, stm32u5, stm32u0))] crate::pac::PWR.cr1().modify(|m| m.set_lpms(stop_mode.into())); #[cfg(stm32h5)] crate::pac::PWR.pmcr().modify(|v| { diff --git a/embassy-stm32/src/rtc/low_power.rs b/embassy-stm32/src/rtc/low_power.rs index 5bf8742de..cf7c4bb28 100644 --- a/embassy-stm32/src/rtc/low_power.rs +++ b/embassy-stm32/src/rtc/low_power.rs @@ -65,7 +65,9 @@ pub(crate) enum WakeupPrescaler { Div16 = 16, } -#[cfg(any(stm32f4, stm32l0, stm32g4, stm32l4, stm32l5, stm32wb, stm32h5, stm32g0, stm32u5, stm32u0))] +#[cfg(any( + stm32f4, stm32l0, stm32g4, stm32l4, stm32l5, stm32wb, stm32h5, stm32g0, stm32u5, stm32u0 +))] impl From for crate::pac::rtc::vals::Wucksel { fn from(val: WakeupPrescaler) -> Self { use crate::pac::rtc::vals::Wucksel; @@ -79,7 +81,9 @@ impl From for crate::pac::rtc::vals::Wucksel { } } -#[cfg(any(stm32f4, stm32l0, stm32g4, stm32l4, stm32l5, stm32wb, stm32h5, stm32g0, stm32u5, stm32u0))] +#[cfg(any( + stm32f4, stm32l0, stm32g4, stm32l4, stm32l5, stm32wb, stm32h5, stm32g0, stm32u5, stm32u0 +))] impl From for WakeupPrescaler { fn from(val: crate::pac::rtc::vals::Wucksel) -> Self { use crate::pac::rtc::vals::Wucksel; diff --git a/embassy-stm32/src/rtc/v3.rs b/embassy-stm32/src/rtc/v3.rs index d543955a4..de2c202bc 100644 --- a/embassy-stm32/src/rtc/v3.rs +++ b/embassy-stm32/src/rtc/v3.rs @@ -139,7 +139,7 @@ impl SealedInstance for crate::peripherals::RTC { const EXTI_WAKEUP_LINE: usize = 17; } ); - + #[cfg(feature = "low-power")] cfg_if::cfg_if!( if #[cfg(stm32g4)] { From c9abff53d77dfc71deb597ce93f358e25588775a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?D=C3=A1niel=20Buga?= Date: Fri, 22 Nov 2024 21:16:11 +0100 Subject: [PATCH 233/361] Bump sync version (#3562) * Bump sync version * Use old embassy-sync in rp bluetooth example * Downgrade update to minor --- cyw43/Cargo.toml | 2 +- embassy-boot-nrf/Cargo.toml | 2 +- embassy-boot-rp/Cargo.toml | 2 +- embassy-boot-stm32/Cargo.toml | 2 +- embassy-boot/Cargo.toml | 2 +- embassy-embedded-hal/Cargo.toml | 2 +- embassy-net-driver-channel/Cargo.toml | 2 +- embassy-net-esp-hosted/Cargo.toml | 2 +- embassy-net-nrf91/Cargo.toml | 2 +- embassy-net-ppp/Cargo.toml | 2 +- embassy-net/Cargo.toml | 2 +- embassy-nrf/Cargo.toml | 2 +- embassy-nxp/Cargo.toml | 2 +- embassy-rp/Cargo.toml | 2 +- embassy-stm32-wpan/Cargo.toml | 2 +- embassy-stm32/Cargo.toml | 2 +- embassy-sync/CHANGELOG.md | 2 +- embassy-sync/Cargo.toml | 2 +- embassy-usb-dfu/Cargo.toml | 2 +- embassy-usb-logger/Cargo.toml | 2 +- embassy-usb-synopsys-otg/Cargo.toml | 2 +- embassy-usb/Cargo.toml | 2 +- examples/boot/application/nrf/Cargo.toml | 2 +- examples/boot/application/rp/Cargo.toml | 2 +- examples/boot/application/stm32f3/Cargo.toml | 2 +- examples/boot/application/stm32f7/Cargo.toml | 2 +- examples/boot/application/stm32h7/Cargo.toml | 2 +- examples/boot/application/stm32l0/Cargo.toml | 2 +- examples/boot/application/stm32l1/Cargo.toml | 2 +- examples/boot/application/stm32l4/Cargo.toml | 2 +- examples/boot/application/stm32wb-dfu/Cargo.toml | 2 +- examples/boot/application/stm32wl/Cargo.toml | 2 +- examples/boot/bootloader/nrf/Cargo.toml | 2 +- examples/boot/bootloader/rp/Cargo.toml | 2 +- examples/boot/bootloader/stm32-dual-bank/Cargo.toml | 2 +- examples/boot/bootloader/stm32/Cargo.toml | 2 +- examples/boot/bootloader/stm32wb-dfu/Cargo.toml | 2 +- examples/lpc55s69/Cargo.toml | 2 +- examples/nrf-rtos-trace/Cargo.toml | 2 +- examples/nrf52810/Cargo.toml | 2 +- examples/nrf52840-rtic/Cargo.toml | 2 +- examples/nrf52840/Cargo.toml | 2 +- examples/nrf5340/Cargo.toml | 2 +- examples/rp/Cargo.toml | 2 +- examples/rp23/Cargo.toml | 2 +- examples/std/Cargo.toml | 2 +- examples/stm32c0/Cargo.toml | 2 +- examples/stm32f0/Cargo.toml | 2 +- examples/stm32f1/Cargo.toml | 2 +- examples/stm32f2/Cargo.toml | 2 +- examples/stm32f3/Cargo.toml | 2 +- examples/stm32f334/Cargo.toml | 2 +- examples/stm32f4/Cargo.toml | 2 +- examples/stm32f7/Cargo.toml | 2 +- examples/stm32g0/Cargo.toml | 2 +- examples/stm32g4/Cargo.toml | 2 +- examples/stm32h5/Cargo.toml | 2 +- examples/stm32h7/Cargo.toml | 2 +- examples/stm32h723/Cargo.toml | 2 +- examples/stm32h735/Cargo.toml | 2 +- examples/stm32h755cm4/Cargo.toml | 2 +- examples/stm32h755cm7/Cargo.toml | 2 +- examples/stm32h7b0/Cargo.toml | 2 +- examples/stm32h7rs/Cargo.toml | 2 +- examples/stm32l0/Cargo.toml | 2 +- examples/stm32l1/Cargo.toml | 2 +- examples/stm32l4/Cargo.toml | 2 +- examples/stm32l5/Cargo.toml | 2 +- examples/stm32u0/Cargo.toml | 2 +- examples/stm32u5/Cargo.toml | 2 +- examples/stm32wb/Cargo.toml | 2 +- examples/stm32wba/Cargo.toml | 2 +- examples/stm32wl/Cargo.toml | 2 +- examples/wasm/Cargo.toml | 2 +- tests/nrf/Cargo.toml | 2 +- tests/riscv32/Cargo.toml | 2 +- tests/rp/Cargo.toml | 2 +- tests/stm32/Cargo.toml | 2 +- 78 files changed, 78 insertions(+), 78 deletions(-) diff --git a/cyw43/Cargo.toml b/cyw43/Cargo.toml index 751e59a69..7c0260868 100644 --- a/cyw43/Cargo.toml +++ b/cyw43/Cargo.toml @@ -19,7 +19,7 @@ firmware-logs = [] [dependencies] embassy-time = { version = "0.3.2", path = "../embassy-time"} -embassy-sync = { version = "0.6.0", path = "../embassy-sync"} +embassy-sync = { version = "0.6.1", path = "../embassy-sync"} embassy-futures = { version = "0.1.0", path = "../embassy-futures"} embassy-net-driver-channel = { version = "0.3.0", path = "../embassy-net-driver-channel"} diff --git a/embassy-boot-nrf/Cargo.toml b/embassy-boot-nrf/Cargo.toml index 27407ae92..dd5c0780e 100644 --- a/embassy-boot-nrf/Cargo.toml +++ b/embassy-boot-nrf/Cargo.toml @@ -24,7 +24,7 @@ target = "thumbv7em-none-eabi" defmt = { version = "0.3", optional = true } log = { version = "0.4.17", optional = true } -embassy-sync = { version = "0.6.0", path = "../embassy-sync" } +embassy-sync = { version = "0.6.1", path = "../embassy-sync" } embassy-nrf = { version = "0.2.0", path = "../embassy-nrf", default-features = false } embassy-boot = { version = "0.3.0", path = "../embassy-boot" } cortex-m = { version = "0.7.6" } diff --git a/embassy-boot-rp/Cargo.toml b/embassy-boot-rp/Cargo.toml index bf5f34abe..fa603047e 100644 --- a/embassy-boot-rp/Cargo.toml +++ b/embassy-boot-rp/Cargo.toml @@ -24,7 +24,7 @@ features = ["embassy-rp/rp2040"] defmt = { version = "0.3", optional = true } log = { version = "0.4", optional = true } -embassy-sync = { version = "0.6.0", path = "../embassy-sync" } +embassy-sync = { version = "0.6.1", path = "../embassy-sync" } embassy-rp = { version = "0.2.0", path = "../embassy-rp", default-features = false } embassy-boot = { version = "0.3.0", path = "../embassy-boot" } embassy-time = { version = "0.3.2", path = "../embassy-time" } diff --git a/embassy-boot-stm32/Cargo.toml b/embassy-boot-stm32/Cargo.toml index e4ef9a612..1afdde75a 100644 --- a/embassy-boot-stm32/Cargo.toml +++ b/embassy-boot-stm32/Cargo.toml @@ -24,7 +24,7 @@ target = "thumbv7em-none-eabi" defmt = { version = "0.3", optional = true } log = { version = "0.4", optional = true } -embassy-sync = { version = "0.6.0", path = "../embassy-sync" } +embassy-sync = { version = "0.6.1", path = "../embassy-sync" } embassy-stm32 = { version = "0.1.0", path = "../embassy-stm32", default-features = false } embassy-boot = { version = "0.3.0", path = "../embassy-boot" } cortex-m = { version = "0.7.6" } diff --git a/embassy-boot/Cargo.toml b/embassy-boot/Cargo.toml index 79ba6456a..b385020cc 100644 --- a/embassy-boot/Cargo.toml +++ b/embassy-boot/Cargo.toml @@ -29,7 +29,7 @@ digest = "0.10" log = { version = "0.4", optional = true } ed25519-dalek = { version = "2", default-features = false, features = ["digest"], optional = true } embassy-embedded-hal = { version = "0.2.0", path = "../embassy-embedded-hal" } -embassy-sync = { version = "0.6.0", path = "../embassy-sync" } +embassy-sync = { version = "0.6.1", path = "../embassy-sync" } embedded-storage = "0.3.1" embedded-storage-async = { version = "0.4.1" } salty = { version = "0.3", optional = true } diff --git a/embassy-embedded-hal/Cargo.toml b/embassy-embedded-hal/Cargo.toml index 345dc3420..b7728c411 100644 --- a/embassy-embedded-hal/Cargo.toml +++ b/embassy-embedded-hal/Cargo.toml @@ -28,7 +28,7 @@ default = ["time"] [dependencies] embassy-futures = { version = "0.1.0", path = "../embassy-futures" } -embassy-sync = { version = "0.6.0", path = "../embassy-sync" } +embassy-sync = { version = "0.6.1", path = "../embassy-sync" } embassy-time = { version = "0.3.2", path = "../embassy-time", optional = true } embedded-hal-02 = { package = "embedded-hal", version = "0.2.6", features = [ "unproven", diff --git a/embassy-net-driver-channel/Cargo.toml b/embassy-net-driver-channel/Cargo.toml index abce9315b..cabc0c38f 100644 --- a/embassy-net-driver-channel/Cargo.toml +++ b/embassy-net-driver-channel/Cargo.toml @@ -25,6 +25,6 @@ features = ["defmt"] defmt = { version = "0.3", optional = true } log = { version = "0.4.14", optional = true } -embassy-sync = { version = "0.6.0", path = "../embassy-sync" } +embassy-sync = { version = "0.6.1", path = "../embassy-sync" } embassy-futures = { version = "0.1.0", path = "../embassy-futures" } embassy-net-driver = { version = "0.2.0", path = "../embassy-net-driver" } diff --git a/embassy-net-esp-hosted/Cargo.toml b/embassy-net-esp-hosted/Cargo.toml index 915eba7a0..d22ccb101 100644 --- a/embassy-net-esp-hosted/Cargo.toml +++ b/embassy-net-esp-hosted/Cargo.toml @@ -18,7 +18,7 @@ defmt = { version = "0.3", optional = true } log = { version = "0.4.14", optional = true } embassy-time = { version = "0.3.2", path = "../embassy-time" } -embassy-sync = { version = "0.6.0", path = "../embassy-sync"} +embassy-sync = { version = "0.6.1", path = "../embassy-sync"} embassy-futures = { version = "0.1.0", path = "../embassy-futures"} embassy-net-driver-channel = { version = "0.3.0", path = "../embassy-net-driver-channel"} diff --git a/embassy-net-nrf91/Cargo.toml b/embassy-net-nrf91/Cargo.toml index bc31f93f4..f9e55c0b1 100644 --- a/embassy-net-nrf91/Cargo.toml +++ b/embassy-net-nrf91/Cargo.toml @@ -21,7 +21,7 @@ nrf-pac = { git = "https://github.com/embassy-rs/nrf-pac", rev = "52e3a757f06035 cortex-m = "0.7.7" embassy-time = { version = "0.3.1", path = "../embassy-time" } -embassy-sync = { version = "0.6.0", path = "../embassy-sync"} +embassy-sync = { version = "0.6.1", path = "../embassy-sync"} embassy-futures = { version = "0.1.0", path = "../embassy-futures"} embassy-net-driver-channel = { version = "0.3.0", path = "../embassy-net-driver-channel"} diff --git a/embassy-net-ppp/Cargo.toml b/embassy-net-ppp/Cargo.toml index d2f43ef45..9c214d5ae 100644 --- a/embassy-net-ppp/Cargo.toml +++ b/embassy-net-ppp/Cargo.toml @@ -21,7 +21,7 @@ embedded-io-async = { version = "0.6.1" } embassy-net-driver-channel = { version = "0.3.0", path = "../embassy-net-driver-channel" } embassy-futures = { version = "0.1.0", path = "../embassy-futures" } ppproto = { version = "0.2.0"} -embassy-sync = { version = "0.6.0", path = "../embassy-sync" } +embassy-sync = { version = "0.6.1", path = "../embassy-sync" } [package.metadata.embassy_docs] src_base = "https://github.com/embassy-rs/embassy/blob/embassy-net-ppp-v$VERSION/embassy-net-ppp/src/" diff --git a/embassy-net/Cargo.toml b/embassy-net/Cargo.toml index 1090892e0..2c33ecc14 100644 --- a/embassy-net/Cargo.toml +++ b/embassy-net/Cargo.toml @@ -75,7 +75,7 @@ smoltcp = { git="https://github.com/smoltcp-rs/smoltcp", rev="fe0b4d102253465850 embassy-net-driver = { version = "0.2.0", path = "../embassy-net-driver" } embassy-time = { version = "0.3.2", path = "../embassy-time" } -embassy-sync = { version = "0.6.0", path = "../embassy-sync" } +embassy-sync = { version = "0.6.1", path = "../embassy-sync" } embedded-io-async = { version = "0.6.1" } managed = { version = "0.8.0", default-features = false, features = [ "map" ] } diff --git a/embassy-nrf/Cargo.toml b/embassy-nrf/Cargo.toml index 9e62bcca4..9da050a22 100644 --- a/embassy-nrf/Cargo.toml +++ b/embassy-nrf/Cargo.toml @@ -136,7 +136,7 @@ _nrf52832_anomaly_109 = [] [dependencies] embassy-time-driver = { version = "0.1", path = "../embassy-time-driver", optional = true } embassy-time = { version = "0.3.2", path = "../embassy-time", optional = true } -embassy-sync = { version = "0.6.0", path = "../embassy-sync" } +embassy-sync = { version = "0.6.1", path = "../embassy-sync" } embassy-hal-internal = {version = "0.2.0", path = "../embassy-hal-internal", features = ["cortex-m", "prio-bits-3"] } embassy-embedded-hal = {version = "0.2.0", path = "../embassy-embedded-hal" } embassy-usb-driver = {version = "0.1.0", path = "../embassy-usb-driver" } diff --git a/embassy-nxp/Cargo.toml b/embassy-nxp/Cargo.toml index df329be66..b0f73b63c 100644 --- a/embassy-nxp/Cargo.toml +++ b/embassy-nxp/Cargo.toml @@ -8,7 +8,7 @@ cortex-m = "0.7.7" cortex-m-rt = "0.7.0" critical-section = "1.1.2" embassy-hal-internal = {version = "0.2.0", path = "../embassy-hal-internal", features = ["cortex-m", "prio-bits-2"] } -embassy-sync = { version = "0.6.0", path = "../embassy-sync" } +embassy-sync = { version = "0.6.1", path = "../embassy-sync" } lpc55-pac = "0.5.0" defmt = "0.3.8" diff --git a/embassy-rp/Cargo.toml b/embassy-rp/Cargo.toml index 401ecd215..3809f1894 100644 --- a/embassy-rp/Cargo.toml +++ b/embassy-rp/Cargo.toml @@ -108,7 +108,7 @@ _test = [] binary-info = ["rt", "dep:rp-binary-info", "rp-binary-info?/binary-info"] [dependencies] -embassy-sync = { version = "0.6.0", path = "../embassy-sync" } +embassy-sync = { version = "0.6.1", path = "../embassy-sync" } embassy-time-driver = { version = "0.1", path = "../embassy-time-driver", optional = true } embassy-time = { version = "0.3.2", path = "../embassy-time" } embassy-futures = { version = "0.1.0", path = "../embassy-futures" } diff --git a/embassy-stm32-wpan/Cargo.toml b/embassy-stm32-wpan/Cargo.toml index 54dfd39a6..7dd1bc677 100644 --- a/embassy-stm32-wpan/Cargo.toml +++ b/embassy-stm32-wpan/Cargo.toml @@ -20,7 +20,7 @@ features = ["stm32wb55rg"] [dependencies] embassy-stm32 = { version = "0.1.0", path = "../embassy-stm32" } -embassy-sync = { version = "0.6.0", path = "../embassy-sync" } +embassy-sync = { version = "0.6.1", path = "../embassy-sync" } embassy-time = { version = "0.3.2", path = "../embassy-time", optional = true } embassy-futures = { version = "0.1.0", path = "../embassy-futures" } embassy-hal-internal = { version = "0.2.0", path = "../embassy-hal-internal" } diff --git a/embassy-stm32/Cargo.toml b/embassy-stm32/Cargo.toml index 91d218da4..0872afbd9 100644 --- a/embassy-stm32/Cargo.toml +++ b/embassy-stm32/Cargo.toml @@ -42,7 +42,7 @@ features = ["defmt", "unstable-pac", "exti", "time-driver-any", "time", "stm32h7 rustdoc-args = ["--cfg", "docsrs"] [dependencies] -embassy-sync = { version = "0.6.0", path = "../embassy-sync" } +embassy-sync = { version = "0.6.1", path = "../embassy-sync" } embassy-time = { version = "0.3.2", path = "../embassy-time", optional = true } embassy-time-driver = { version = "0.1", path = "../embassy-time-driver", optional = true } embassy-futures = { version = "0.1.0", path = "../embassy-futures" } diff --git a/embassy-sync/CHANGELOG.md b/embassy-sync/CHANGELOG.md index d12f0a74a..a7547422f 100644 --- a/embassy-sync/CHANGELOG.md +++ b/embassy-sync/CHANGELOG.md @@ -5,7 +5,7 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). -## Unreleased +## 0.6.1 - 2024-11-22 - Add `LazyLock` sync primitive. - Add `Watch` sync primitive. diff --git a/embassy-sync/Cargo.toml b/embassy-sync/Cargo.toml index 2f049b6bc..1bd2f290e 100644 --- a/embassy-sync/Cargo.toml +++ b/embassy-sync/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "embassy-sync" -version = "0.6.0" +version = "0.6.1" edition = "2021" description = "no-std, no-alloc synchronization primitives with async support" repository = "https://github.com/embassy-rs/embassy" diff --git a/embassy-usb-dfu/Cargo.toml b/embassy-usb-dfu/Cargo.toml index 481b2699a..16da468bd 100644 --- a/embassy-usb-dfu/Cargo.toml +++ b/embassy-usb-dfu/Cargo.toml @@ -33,7 +33,7 @@ bitflags = "2.4.1" cortex-m = { version = "0.7.7", features = ["inline-asm"], optional = true } embassy-boot = { version = "0.3.0", path = "../embassy-boot" } embassy-futures = { version = "0.1.1", path = "../embassy-futures" } -embassy-sync = { version = "0.6.0", path = "../embassy-sync" } +embassy-sync = { version = "0.6.1", path = "../embassy-sync" } embassy-time = { version = "0.3.2", path = "../embassy-time" } embassy-usb = { version = "0.3.0", path = "../embassy-usb", default-features = false } embedded-storage = { version = "0.3.1" } diff --git a/embassy-usb-logger/Cargo.toml b/embassy-usb-logger/Cargo.toml index d796e5d8e..d5147de49 100644 --- a/embassy-usb-logger/Cargo.toml +++ b/embassy-usb-logger/Cargo.toml @@ -16,6 +16,6 @@ target = "thumbv7em-none-eabi" [dependencies] embassy-usb = { version = "0.3.0", path = "../embassy-usb" } -embassy-sync = { version = "0.6.0", path = "../embassy-sync" } +embassy-sync = { version = "0.6.1", path = "../embassy-sync" } embassy-futures = { version = "0.1.0", path = "../embassy-futures" } log = "0.4" diff --git a/embassy-usb-synopsys-otg/Cargo.toml b/embassy-usb-synopsys-otg/Cargo.toml index 68ab0415f..d621577bf 100644 --- a/embassy-usb-synopsys-otg/Cargo.toml +++ b/embassy-usb-synopsys-otg/Cargo.toml @@ -18,7 +18,7 @@ target = "thumbv7em-none-eabi" [dependencies] critical-section = "1.1" -embassy-sync = { version = "0.6.0", path = "../embassy-sync" } +embassy-sync = { version = "0.6.1", path = "../embassy-sync" } embassy-usb-driver = {version = "0.1.0", path = "../embassy-usb-driver" } defmt = { version = "0.3", optional = true } diff --git a/embassy-usb/Cargo.toml b/embassy-usb/Cargo.toml index e310d8bae..9abf2f200 100644 --- a/embassy-usb/Cargo.toml +++ b/embassy-usb/Cargo.toml @@ -48,7 +48,7 @@ max-handler-count-8 = [] [dependencies] embassy-futures = { version = "0.1.0", path = "../embassy-futures" } embassy-usb-driver = { version = "0.1.0", path = "../embassy-usb-driver" } -embassy-sync = { version = "0.6.0", path = "../embassy-sync" } +embassy-sync = { version = "0.6.1", path = "../embassy-sync" } embassy-net-driver-channel = { version = "0.3.0", path = "../embassy-net-driver-channel" } defmt = { version = "0.3", optional = true } diff --git a/examples/boot/application/nrf/Cargo.toml b/examples/boot/application/nrf/Cargo.toml index 3bf33d212..046571e05 100644 --- a/examples/boot/application/nrf/Cargo.toml +++ b/examples/boot/application/nrf/Cargo.toml @@ -5,7 +5,7 @@ version = "0.1.0" license = "MIT OR Apache-2.0" [dependencies] -embassy-sync = { version = "0.6.0", path = "../../../../embassy-sync" } +embassy-sync = { version = "0.6.1", path = "../../../../embassy-sync" } embassy-executor = { version = "0.6.3", path = "../../../../embassy-executor", features = ["task-arena-size-16384", "arch-cortex-m", "executor-thread", "integrated-timers", "arch-cortex-m", "executor-thread"] } embassy-time = { version = "0.3.2", path = "../../../../embassy-time", features = [] } embassy-nrf = { version = "0.2.0", path = "../../../../embassy-nrf", features = ["time-driver-rtc1", "gpiote", ] } diff --git a/examples/boot/application/rp/Cargo.toml b/examples/boot/application/rp/Cargo.toml index 85515abc5..f859ddbc6 100644 --- a/examples/boot/application/rp/Cargo.toml +++ b/examples/boot/application/rp/Cargo.toml @@ -5,7 +5,7 @@ version = "0.1.0" license = "MIT OR Apache-2.0" [dependencies] -embassy-sync = { version = "0.6.0", path = "../../../../embassy-sync" } +embassy-sync = { version = "0.6.1", path = "../../../../embassy-sync" } embassy-executor = { version = "0.6.3", path = "../../../../embassy-executor", features = ["task-arena-size-16384", "arch-cortex-m", "executor-thread", "integrated-timers", "arch-cortex-m", "executor-thread"] } embassy-time = { version = "0.3.2", path = "../../../../embassy-time", features = [] } embassy-rp = { version = "0.2.0", path = "../../../../embassy-rp", features = ["time-driver", "rp2040"] } diff --git a/examples/boot/application/stm32f3/Cargo.toml b/examples/boot/application/stm32f3/Cargo.toml index 45394f1d5..d5b26d698 100644 --- a/examples/boot/application/stm32f3/Cargo.toml +++ b/examples/boot/application/stm32f3/Cargo.toml @@ -5,7 +5,7 @@ version = "0.1.0" license = "MIT OR Apache-2.0" [dependencies] -embassy-sync = { version = "0.6.0", path = "../../../../embassy-sync" } +embassy-sync = { version = "0.6.1", path = "../../../../embassy-sync" } embassy-executor = { version = "0.6.3", path = "../../../../embassy-executor", features = ["task-arena-size-8192", "arch-cortex-m", "executor-thread", "integrated-timers"] } embassy-time = { version = "0.3.2", path = "../../../../embassy-time", features = [ "tick-hz-32_768"] } embassy-stm32 = { version = "0.1.0", path = "../../../../embassy-stm32", features = ["stm32f303re", "time-driver-any", "exti"] } diff --git a/examples/boot/application/stm32f7/Cargo.toml b/examples/boot/application/stm32f7/Cargo.toml index 8bb6c5df3..d13692aa8 100644 --- a/examples/boot/application/stm32f7/Cargo.toml +++ b/examples/boot/application/stm32f7/Cargo.toml @@ -5,7 +5,7 @@ version = "0.1.0" license = "MIT OR Apache-2.0" [dependencies] -embassy-sync = { version = "0.6.0", path = "../../../../embassy-sync" } +embassy-sync = { version = "0.6.1", path = "../../../../embassy-sync" } embassy-executor = { version = "0.6.3", path = "../../../../embassy-executor", features = ["task-arena-size-8192", "arch-cortex-m", "executor-thread", "integrated-timers"] } embassy-time = { version = "0.3.2", path = "../../../../embassy-time", features = [ "tick-hz-32_768"] } embassy-stm32 = { version = "0.1.0", path = "../../../../embassy-stm32", features = ["stm32f767zi", "time-driver-any", "exti"] } diff --git a/examples/boot/application/stm32h7/Cargo.toml b/examples/boot/application/stm32h7/Cargo.toml index 85d3af65f..a2de827aa 100644 --- a/examples/boot/application/stm32h7/Cargo.toml +++ b/examples/boot/application/stm32h7/Cargo.toml @@ -5,7 +5,7 @@ version = "0.1.0" license = "MIT OR Apache-2.0" [dependencies] -embassy-sync = { version = "0.6.0", path = "../../../../embassy-sync" } +embassy-sync = { version = "0.6.1", path = "../../../../embassy-sync" } embassy-executor = { version = "0.6.3", path = "../../../../embassy-executor", features = ["task-arena-size-8192", "arch-cortex-m", "executor-thread", "integrated-timers"] } embassy-time = { version = "0.3.2", path = "../../../../embassy-time", features = [ "tick-hz-32_768"] } embassy-stm32 = { version = "0.1.0", path = "../../../../embassy-stm32", features = ["stm32h743zi", "time-driver-any", "exti"] } diff --git a/examples/boot/application/stm32l0/Cargo.toml b/examples/boot/application/stm32l0/Cargo.toml index a8a6f0bab..ddfddf652 100644 --- a/examples/boot/application/stm32l0/Cargo.toml +++ b/examples/boot/application/stm32l0/Cargo.toml @@ -5,7 +5,7 @@ version = "0.1.0" license = "MIT OR Apache-2.0" [dependencies] -embassy-sync = { version = "0.6.0", path = "../../../../embassy-sync" } +embassy-sync = { version = "0.6.1", path = "../../../../embassy-sync" } embassy-executor = { version = "0.6.3", path = "../../../../embassy-executor", features = ["task-arena-size-8192", "arch-cortex-m", "executor-thread", "integrated-timers"] } embassy-time = { version = "0.3.2", path = "../../../../embassy-time", features = [ "tick-hz-32_768"] } embassy-stm32 = { version = "0.1.0", path = "../../../../embassy-stm32", features = ["stm32l072cz", "time-driver-any", "exti", "memory-x"] } diff --git a/examples/boot/application/stm32l1/Cargo.toml b/examples/boot/application/stm32l1/Cargo.toml index 0a8447aa6..4780c20f4 100644 --- a/examples/boot/application/stm32l1/Cargo.toml +++ b/examples/boot/application/stm32l1/Cargo.toml @@ -5,7 +5,7 @@ version = "0.1.0" license = "MIT OR Apache-2.0" [dependencies] -embassy-sync = { version = "0.6.0", path = "../../../../embassy-sync" } +embassy-sync = { version = "0.6.1", path = "../../../../embassy-sync" } embassy-executor = { version = "0.6.3", path = "../../../../embassy-executor", features = ["task-arena-size-8192", "arch-cortex-m", "executor-thread", "integrated-timers"] } embassy-time = { version = "0.3.2", path = "../../../../embassy-time", features = [ "tick-hz-32_768"] } embassy-stm32 = { version = "0.1.0", path = "../../../../embassy-stm32", features = ["stm32l151cb-a", "time-driver-any", "exti"] } diff --git a/examples/boot/application/stm32l4/Cargo.toml b/examples/boot/application/stm32l4/Cargo.toml index 1b5d33f78..0a31d6b62 100644 --- a/examples/boot/application/stm32l4/Cargo.toml +++ b/examples/boot/application/stm32l4/Cargo.toml @@ -5,7 +5,7 @@ version = "0.1.0" license = "MIT OR Apache-2.0" [dependencies] -embassy-sync = { version = "0.6.0", path = "../../../../embassy-sync" } +embassy-sync = { version = "0.6.1", path = "../../../../embassy-sync" } embassy-executor = { version = "0.6.3", path = "../../../../embassy-executor", features = ["task-arena-size-8192", "arch-cortex-m", "executor-thread", "integrated-timers"] } embassy-time = { version = "0.3.2", path = "../../../../embassy-time", features = [ "tick-hz-32_768"] } embassy-stm32 = { version = "0.1.0", path = "../../../../embassy-stm32", features = ["stm32l475vg", "time-driver-any", "exti"] } diff --git a/examples/boot/application/stm32wb-dfu/Cargo.toml b/examples/boot/application/stm32wb-dfu/Cargo.toml index cde4a3ae6..67f6bde11 100644 --- a/examples/boot/application/stm32wb-dfu/Cargo.toml +++ b/examples/boot/application/stm32wb-dfu/Cargo.toml @@ -5,7 +5,7 @@ version = "0.1.0" license = "MIT OR Apache-2.0" [dependencies] -embassy-sync = { version = "0.6.0", path = "../../../../embassy-sync" } +embassy-sync = { version = "0.6.1", path = "../../../../embassy-sync" } embassy-executor = { version = "0.6.3", path = "../../../../embassy-executor", features = ["task-arena-size-8192", "arch-cortex-m", "executor-thread", "integrated-timers"] } embassy-time = { version = "0.3.2", path = "../../../../embassy-time", features = [ "tick-hz-32_768"] } embassy-stm32 = { version = "0.1.0", path = "../../../../embassy-stm32", features = ["stm32wb55rg", "time-driver-any", "exti"] } diff --git a/examples/boot/application/stm32wl/Cargo.toml b/examples/boot/application/stm32wl/Cargo.toml index 8e94051ce..da27d4601 100644 --- a/examples/boot/application/stm32wl/Cargo.toml +++ b/examples/boot/application/stm32wl/Cargo.toml @@ -5,7 +5,7 @@ version = "0.1.0" license = "MIT OR Apache-2.0" [dependencies] -embassy-sync = { version = "0.6.0", path = "../../../../embassy-sync" } +embassy-sync = { version = "0.6.1", path = "../../../../embassy-sync" } embassy-executor = { version = "0.6.3", path = "../../../../embassy-executor", features = ["task-arena-size-8192", "arch-cortex-m", "executor-thread", "integrated-timers"] } embassy-time = { version = "0.3.2", path = "../../../../embassy-time", features = [ "tick-hz-32_768"] } embassy-stm32 = { version = "0.1.0", path = "../../../../embassy-stm32", features = ["stm32wl55jc-cm4", "time-driver-any", "exti"] } diff --git a/examples/boot/bootloader/nrf/Cargo.toml b/examples/boot/bootloader/nrf/Cargo.toml index 9d5d51a13..c2e8bbe53 100644 --- a/examples/boot/bootloader/nrf/Cargo.toml +++ b/examples/boot/bootloader/nrf/Cargo.toml @@ -12,7 +12,7 @@ defmt-rtt = { version = "0.4", optional = true } embassy-nrf = { path = "../../../../embassy-nrf", features = [] } embassy-boot-nrf = { path = "../../../../embassy-boot-nrf" } cortex-m = { version = "0.7.6", features = ["inline-asm", "critical-section-single-core"] } -embassy-sync = { version = "0.6.0", path = "../../../../embassy-sync" } +embassy-sync = { version = "0.6.1", path = "../../../../embassy-sync" } cortex-m-rt = { version = "0.7" } cfg-if = "1.0.0" diff --git a/examples/boot/bootloader/rp/Cargo.toml b/examples/boot/bootloader/rp/Cargo.toml index 9df396e5e..24df3da82 100644 --- a/examples/boot/bootloader/rp/Cargo.toml +++ b/examples/boot/bootloader/rp/Cargo.toml @@ -11,7 +11,7 @@ defmt-rtt = { version = "0.4", optional = true } embassy-rp = { path = "../../../../embassy-rp", features = ["rp2040"] } embassy-boot-rp = { path = "../../../../embassy-boot-rp" } -embassy-sync = { version = "0.6.0", path = "../../../../embassy-sync" } +embassy-sync = { version = "0.6.1", path = "../../../../embassy-sync" } embassy-time = { path = "../../../../embassy-time", features = [] } cortex-m = { version = "0.7.6", features = ["inline-asm", "critical-section-single-core"] } diff --git a/examples/boot/bootloader/stm32-dual-bank/Cargo.toml b/examples/boot/bootloader/stm32-dual-bank/Cargo.toml index b91b05412..81e0026c6 100644 --- a/examples/boot/bootloader/stm32-dual-bank/Cargo.toml +++ b/examples/boot/bootloader/stm32-dual-bank/Cargo.toml @@ -15,7 +15,7 @@ cortex-m = { version = "0.7.6", features = [ "inline-asm", "critical-section-single-core", ] } -embassy-sync = { version = "0.6.0", path = "../../../../embassy-sync" } +embassy-sync = { version = "0.6.1", path = "../../../../embassy-sync" } cortex-m-rt = { version = "0.7" } embedded-storage = "0.3.1" embedded-storage-async = "0.4.0" diff --git a/examples/boot/bootloader/stm32/Cargo.toml b/examples/boot/bootloader/stm32/Cargo.toml index 541186949..f35e4e713 100644 --- a/examples/boot/bootloader/stm32/Cargo.toml +++ b/examples/boot/bootloader/stm32/Cargo.toml @@ -12,7 +12,7 @@ defmt-rtt = { version = "0.4", optional = true } embassy-stm32 = { path = "../../../../embassy-stm32", features = [] } embassy-boot-stm32 = { path = "../../../../embassy-boot-stm32" } cortex-m = { version = "0.7.6", features = ["inline-asm", "critical-section-single-core"] } -embassy-sync = { version = "0.6.0", path = "../../../../embassy-sync" } +embassy-sync = { version = "0.6.1", path = "../../../../embassy-sync" } cortex-m-rt = { version = "0.7" } embedded-storage = "0.3.1" embedded-storage-async = "0.4.0" diff --git a/examples/boot/bootloader/stm32wb-dfu/Cargo.toml b/examples/boot/bootloader/stm32wb-dfu/Cargo.toml index 050b672ce..1431e7cc3 100644 --- a/examples/boot/bootloader/stm32wb-dfu/Cargo.toml +++ b/examples/boot/bootloader/stm32wb-dfu/Cargo.toml @@ -12,7 +12,7 @@ defmt-rtt = { version = "0.4", optional = true } embassy-stm32 = { path = "../../../../embassy-stm32", features = [] } embassy-boot-stm32 = { path = "../../../../embassy-boot-stm32" } cortex-m = { version = "0.7.6", features = ["inline-asm", "critical-section-single-core"] } -embassy-sync = { version = "0.6.0", path = "../../../../embassy-sync" } +embassy-sync = { version = "0.6.1", path = "../../../../embassy-sync" } cortex-m-rt = { version = "0.7" } embedded-storage = "0.3.1" embedded-storage-async = "0.4.0" diff --git a/examples/lpc55s69/Cargo.toml b/examples/lpc55s69/Cargo.toml index ee0c09dd3..41a88f082 100644 --- a/examples/lpc55s69/Cargo.toml +++ b/examples/lpc55s69/Cargo.toml @@ -8,7 +8,7 @@ license = "MIT OR Apache-2.0" [dependencies] embassy-nxp = { version = "0.1.0", path = "../../embassy-nxp", features = ["rt"] } embassy-executor = { version = "0.6.3", path = "../../embassy-executor", features = ["task-arena-size-32768", "arch-cortex-m", "executor-thread", "executor-interrupt"] } -embassy-sync = { version = "0.6.0", path = "../../embassy-sync", features = ["defmt"] } +embassy-sync = { version = "0.6.1", path = "../../embassy-sync", features = ["defmt"] } embassy-time = { version = "0.3.0", path = "../../embassy-time", features = ["defmt"] } panic-halt = "0.2.0" cortex-m = { version = "0.7.6", features = ["inline-asm", "critical-section-single-core"] } diff --git a/examples/nrf-rtos-trace/Cargo.toml b/examples/nrf-rtos-trace/Cargo.toml index ae8d62100..449056409 100644 --- a/examples/nrf-rtos-trace/Cargo.toml +++ b/examples/nrf-rtos-trace/Cargo.toml @@ -15,7 +15,7 @@ log = [ ] [dependencies] -embassy-sync = { version = "0.6.0", path = "../../embassy-sync" } +embassy-sync = { version = "0.6.1", path = "../../embassy-sync" } embassy-executor = { version = "0.6.3", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "rtos-trace", "integrated-timers"] } embassy-time = { version = "0.3.2", path = "../../embassy-time" } embassy-nrf = { version = "0.2.0", path = "../../embassy-nrf", features = ["nrf52840", "time-driver-rtc1", "gpiote", "unstable-pac"] } diff --git a/examples/nrf52810/Cargo.toml b/examples/nrf52810/Cargo.toml index 3cc88ed7e..b0b73417b 100644 --- a/examples/nrf52810/Cargo.toml +++ b/examples/nrf52810/Cargo.toml @@ -6,7 +6,7 @@ license = "MIT OR Apache-2.0" [dependencies] embassy-futures = { version = "0.1.0", path = "../../embassy-futures" } -embassy-sync = { version = "0.6.0", path = "../../embassy-sync", features = ["defmt"] } +embassy-sync = { version = "0.6.1", path = "../../embassy-sync", features = ["defmt"] } embassy-executor = { version = "0.6.3", path = "../../embassy-executor", features = ["task-arena-size-8192", "arch-cortex-m", "executor-thread", "executor-interrupt", "defmt", "integrated-timers"] } embassy-time = { version = "0.3.2", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime"] } embassy-nrf = { version = "0.2.0", path = "../../embassy-nrf", features = ["defmt", "nrf52810", "time-driver-rtc1", "gpiote", "unstable-pac", "time"] } diff --git a/examples/nrf52840-rtic/Cargo.toml b/examples/nrf52840-rtic/Cargo.toml index 7fae7aefc..290b2fdb1 100644 --- a/examples/nrf52840-rtic/Cargo.toml +++ b/examples/nrf52840-rtic/Cargo.toml @@ -8,7 +8,7 @@ license = "MIT OR Apache-2.0" rtic = { version = "2", features = ["thumbv7-backend"] } embassy-futures = { version = "0.1.0", path = "../../embassy-futures" } -embassy-sync = { version = "0.6.0", path = "../../embassy-sync", features = ["defmt"] } +embassy-sync = { version = "0.6.1", path = "../../embassy-sync", features = ["defmt"] } embassy-time = { version = "0.3.2", path = "../../embassy-time", features = [ "defmt", "defmt-timestamp-uptime", "generic-queue"] } embassy-nrf = { version = "0.2.0", path = "../../embassy-nrf", features = [ "defmt", "nrf52840", "time-driver-rtc1", "gpiote", "unstable-pac", "time"] } diff --git a/examples/nrf52840/Cargo.toml b/examples/nrf52840/Cargo.toml index 9ac6e3594..9623c04b5 100644 --- a/examples/nrf52840/Cargo.toml +++ b/examples/nrf52840/Cargo.toml @@ -6,7 +6,7 @@ license = "MIT OR Apache-2.0" [dependencies] embassy-futures = { version = "0.1.0", path = "../../embassy-futures" } -embassy-sync = { version = "0.6.0", path = "../../embassy-sync", features = ["defmt"] } +embassy-sync = { version = "0.6.1", path = "../../embassy-sync", features = ["defmt"] } embassy-executor = { version = "0.6.3", path = "../../embassy-executor", features = ["task-arena-size-32768", "arch-cortex-m", "executor-thread", "executor-interrupt", "defmt", "integrated-timers"] } embassy-time = { version = "0.3.2", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime"] } embassy-nrf = { version = "0.2.0", path = "../../embassy-nrf", features = ["defmt", "nrf52840", "time-driver-rtc1", "gpiote", "unstable-pac", "time"] } diff --git a/examples/nrf5340/Cargo.toml b/examples/nrf5340/Cargo.toml index 9103db907..8c8f74d15 100644 --- a/examples/nrf5340/Cargo.toml +++ b/examples/nrf5340/Cargo.toml @@ -6,7 +6,7 @@ license = "MIT OR Apache-2.0" [dependencies] embassy-futures = { version = "0.1.0", path = "../../embassy-futures" } -embassy-sync = { version = "0.6.0", path = "../../embassy-sync", features = ["defmt"] } +embassy-sync = { version = "0.6.1", path = "../../embassy-sync", features = ["defmt"] } embassy-executor = { version = "0.6.3", path = "../../embassy-executor", features = ["task-arena-size-32768", "arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] } embassy-time = { version = "0.3.2", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime"] } embassy-nrf = { version = "0.2.0", path = "../../embassy-nrf", features = ["defmt", "nrf5340-app-s", "time-driver-rtc1", "gpiote", "unstable-pac"] } diff --git a/examples/rp/Cargo.toml b/examples/rp/Cargo.toml index 7e3b6812d..1448306a1 100644 --- a/examples/rp/Cargo.toml +++ b/examples/rp/Cargo.toml @@ -7,7 +7,7 @@ license = "MIT OR Apache-2.0" [dependencies] embassy-embedded-hal = { version = "0.2.0", path = "../../embassy-embedded-hal", features = ["defmt"] } -embassy-sync = { version = "0.6.0", path = "../../embassy-sync", features = ["defmt"] } +embassy-sync = { version = "0.6.1", path = "../../embassy-sync", features = ["defmt"] } embassy-executor = { version = "0.6.3", path = "../../embassy-executor", features = ["task-arena-size-98304", "arch-cortex-m", "executor-thread", "executor-interrupt", "defmt", "integrated-timers"] } embassy-time = { version = "0.3.2", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime"] } embassy-rp = { version = "0.2.0", path = "../../embassy-rp", features = ["defmt", "unstable-pac", "time-driver", "critical-section-impl", "rp2040"] } diff --git a/examples/rp23/Cargo.toml b/examples/rp23/Cargo.toml index 3ea92f208..6738339c0 100644 --- a/examples/rp23/Cargo.toml +++ b/examples/rp23/Cargo.toml @@ -7,7 +7,7 @@ license = "MIT OR Apache-2.0" [dependencies] embassy-embedded-hal = { version = "0.2.0", path = "../../embassy-embedded-hal", features = ["defmt"] } -embassy-sync = { version = "0.6.0", path = "../../embassy-sync", features = ["defmt"] } +embassy-sync = { version = "0.6.1", path = "../../embassy-sync", features = ["defmt"] } embassy-executor = { version = "0.6.3", path = "../../embassy-executor", features = ["task-arena-size-98304", "arch-cortex-m", "executor-thread", "executor-interrupt", "defmt", "integrated-timers"] } embassy-time = { version = "0.3.2", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime"] } embassy-rp = { version = "0.2.0", path = "../../embassy-rp", features = ["defmt", "unstable-pac", "time-driver", "critical-section-impl", "rp235xa", "binary-info"] } diff --git a/examples/std/Cargo.toml b/examples/std/Cargo.toml index 6e918366c..f6b209d06 100644 --- a/examples/std/Cargo.toml +++ b/examples/std/Cargo.toml @@ -5,7 +5,7 @@ version = "0.1.0" license = "MIT OR Apache-2.0" [dependencies] -embassy-sync = { version = "0.6.0", path = "../../embassy-sync", features = ["log"] } +embassy-sync = { version = "0.6.1", path = "../../embassy-sync", features = ["log"] } embassy-executor = { version = "0.6.3", path = "../../embassy-executor", features = ["task-arena-size-32768", "arch-std", "executor-thread", "log", "integrated-timers"] } embassy-time = { version = "0.3.2", path = "../../embassy-time", features = ["log", "std", ] } embassy-net = { version = "0.4.0", path = "../../embassy-net", features=[ "std", "log", "medium-ethernet", "medium-ip", "tcp", "udp", "dns", "dhcpv4", "proto-ipv6"] } diff --git a/examples/stm32c0/Cargo.toml b/examples/stm32c0/Cargo.toml index 06f11ef00..895e668da 100644 --- a/examples/stm32c0/Cargo.toml +++ b/examples/stm32c0/Cargo.toml @@ -7,7 +7,7 @@ license = "MIT OR Apache-2.0" [dependencies] # Change stm32c031c6 to your chip name, if necessary. embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = [ "defmt", "time-driver-any", "stm32c031c6", "memory-x", "unstable-pac", "exti"] } -embassy-sync = { version = "0.6.0", path = "../../embassy-sync", features = ["defmt"] } +embassy-sync = { version = "0.6.1", path = "../../embassy-sync", features = ["defmt"] } embassy-executor = { version = "0.6.3", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] } embassy-time = { version = "0.3.2", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } diff --git a/examples/stm32f0/Cargo.toml b/examples/stm32f0/Cargo.toml index d80a7503b..056f8470d 100644 --- a/examples/stm32f0/Cargo.toml +++ b/examples/stm32f0/Cargo.toml @@ -12,7 +12,7 @@ cortex-m-rt = "0.7.0" defmt = "0.3" defmt-rtt = "0.4" panic-probe = { version = "0.3", features = ["print-defmt"] } -embassy-sync = { version = "0.6.0", path = "../../embassy-sync", features = ["defmt"] } +embassy-sync = { version = "0.6.1", path = "../../embassy-sync", features = ["defmt"] } embassy-executor = { version = "0.6.3", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "executor-interrupt", "defmt", "integrated-timers"] } embassy-time = { version = "0.3.2", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } static_cell = "2" diff --git a/examples/stm32f1/Cargo.toml b/examples/stm32f1/Cargo.toml index 846dcfd7c..c081333d7 100644 --- a/examples/stm32f1/Cargo.toml +++ b/examples/stm32f1/Cargo.toml @@ -7,7 +7,7 @@ license = "MIT OR Apache-2.0" [dependencies] # Change stm32f103c8 to your chip name, if necessary. embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = [ "defmt", "stm32f103c8", "unstable-pac", "memory-x", "time-driver-any" ] } -embassy-sync = { version = "0.6.0", path = "../../embassy-sync", features = ["defmt"] } +embassy-sync = { version = "0.6.1", path = "../../embassy-sync", features = ["defmt"] } embassy-executor = { version = "0.6.3", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] } embassy-time = { version = "0.3.2", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } embassy-usb = { version = "0.3.0", path = "../../embassy-usb", features = ["defmt"] } diff --git a/examples/stm32f2/Cargo.toml b/examples/stm32f2/Cargo.toml index ad87c973b..f7993497c 100644 --- a/examples/stm32f2/Cargo.toml +++ b/examples/stm32f2/Cargo.toml @@ -7,7 +7,7 @@ license = "MIT OR Apache-2.0" [dependencies] # Change stm32f207zg to your chip name, if necessary. embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = [ "defmt", "stm32f207zg", "unstable-pac", "memory-x", "time-driver-any", "exti"] } -embassy-sync = { version = "0.6.0", path = "../../embassy-sync", features = ["defmt"] } +embassy-sync = { version = "0.6.1", path = "../../embassy-sync", features = ["defmt"] } embassy-executor = { version = "0.6.3", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] } embassy-time = { version = "0.3.2", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } diff --git a/examples/stm32f3/Cargo.toml b/examples/stm32f3/Cargo.toml index 9c97e32d3..a7b8935a9 100644 --- a/examples/stm32f3/Cargo.toml +++ b/examples/stm32f3/Cargo.toml @@ -7,7 +7,7 @@ license = "MIT OR Apache-2.0" [dependencies] # Change stm32f303ze to your chip name, if necessary. embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = [ "defmt", "stm32f303ze", "unstable-pac", "memory-x", "time-driver-tim2", "exti"] } -embassy-sync = { version = "0.6.0", path = "../../embassy-sync", features = ["defmt"] } +embassy-sync = { version = "0.6.1", path = "../../embassy-sync", features = ["defmt"] } embassy-executor = { version = "0.6.3", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "executor-interrupt", "defmt", "integrated-timers"] } embassy-time = { version = "0.3.2", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } embassy-usb = { version = "0.3.0", path = "../../embassy-usb", features = ["defmt"] } diff --git a/examples/stm32f334/Cargo.toml b/examples/stm32f334/Cargo.toml index 548ec1289..ed8348772 100644 --- a/examples/stm32f334/Cargo.toml +++ b/examples/stm32f334/Cargo.toml @@ -5,7 +5,7 @@ version = "0.1.0" license = "MIT OR Apache-2.0" [dependencies] -embassy-sync = { version = "0.6.0", path = "../../embassy-sync", features = ["defmt"] } +embassy-sync = { version = "0.6.1", path = "../../embassy-sync", features = ["defmt"] } embassy-executor = { version = "0.6.3", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "executor-interrupt", "defmt", "integrated-timers"] } embassy-time = { version = "0.3.2", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = [ "defmt", "stm32f334r8", "unstable-pac", "memory-x", "time-driver-any", "exti"] } diff --git a/examples/stm32f4/Cargo.toml b/examples/stm32f4/Cargo.toml index cb8392922..75e315e82 100644 --- a/examples/stm32f4/Cargo.toml +++ b/examples/stm32f4/Cargo.toml @@ -7,7 +7,7 @@ license = "MIT OR Apache-2.0" [dependencies] # Change stm32f429zi to your chip name, if necessary. embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["defmt", "stm32f429zi", "unstable-pac", "memory-x", "time-driver-tim4", "exti", "chrono"] } -embassy-sync = { version = "0.6.0", path = "../../embassy-sync", features = ["defmt"] } +embassy-sync = { version = "0.6.1", path = "../../embassy-sync", features = ["defmt"] } embassy-executor = { version = "0.6.3", path = "../../embassy-executor", features = ["task-arena-size-32768", "arch-cortex-m", "executor-thread", "executor-interrupt", "defmt", "integrated-timers"] } embassy-time = { version = "0.3.2", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } embassy-usb = { version = "0.3.0", path = "../../embassy-usb", features = ["defmt" ] } diff --git a/examples/stm32f7/Cargo.toml b/examples/stm32f7/Cargo.toml index 4f24d0eda..822d8152d 100644 --- a/examples/stm32f7/Cargo.toml +++ b/examples/stm32f7/Cargo.toml @@ -7,7 +7,7 @@ license = "MIT OR Apache-2.0" [dependencies] # Change stm32f777zi to your chip name, if necessary. embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["defmt", "stm32f777zi", "memory-x", "unstable-pac", "time-driver-any", "exti"] } -embassy-sync = { version = "0.6.0", path = "../../embassy-sync", features = ["defmt"] } +embassy-sync = { version = "0.6.1", path = "../../embassy-sync", features = ["defmt"] } embassy-executor = { version = "0.6.3", path = "../../embassy-executor", features = ["task-arena-size-32768", "arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] } embassy-time = { version = "0.3.2", 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"] } diff --git a/examples/stm32g0/Cargo.toml b/examples/stm32g0/Cargo.toml index ed74f1ba9..66cac1885 100644 --- a/examples/stm32g0/Cargo.toml +++ b/examples/stm32g0/Cargo.toml @@ -7,7 +7,7 @@ license = "MIT OR Apache-2.0" [dependencies] # Change stm32g0b1re to your chip name, if necessary. embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = [ "defmt", "time-driver-any", "stm32g0b1re", "memory-x", "unstable-pac", "exti"] } -embassy-sync = { version = "0.6.0", path = "../../embassy-sync", features = ["defmt"] } +embassy-sync = { version = "0.6.1", path = "../../embassy-sync", features = ["defmt"] } embassy-executor = { version = "0.6.3", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] } embassy-time = { version = "0.3.2", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } embassy-usb = { version = "0.3.0", path = "../../embassy-usb", default-features = false, features = ["defmt"] } diff --git a/examples/stm32g4/Cargo.toml b/examples/stm32g4/Cargo.toml index 3d5dd8069..36bef4787 100644 --- a/examples/stm32g4/Cargo.toml +++ b/examples/stm32g4/Cargo.toml @@ -7,7 +7,7 @@ license = "MIT OR Apache-2.0" [dependencies] # Change stm32g491re to your chip name, if necessary. embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = [ "defmt", "time-driver-any", "stm32g491re", "memory-x", "unstable-pac", "exti"] } -embassy-sync = { version = "0.6.0", path = "../../embassy-sync", features = ["defmt"] } +embassy-sync = { version = "0.6.1", path = "../../embassy-sync", features = ["defmt"] } embassy-executor = { version = "0.6.3", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] } embassy-time = { version = "0.3.2", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } embassy-usb = { version = "0.3.0", path = "../../embassy-usb", features = ["defmt"] } diff --git a/examples/stm32h5/Cargo.toml b/examples/stm32h5/Cargo.toml index c71ad2db0..91ca43845 100644 --- a/examples/stm32h5/Cargo.toml +++ b/examples/stm32h5/Cargo.toml @@ -7,7 +7,7 @@ license = "MIT OR Apache-2.0" [dependencies] # Change stm32h563zi to your chip name, if necessary. embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["defmt", "stm32h563zi", "memory-x", "time-driver-any", "exti", "unstable-pac", "low-power"] } -embassy-sync = { version = "0.6.0", path = "../../embassy-sync", features = ["defmt"] } +embassy-sync = { version = "0.6.1", path = "../../embassy-sync", features = ["defmt"] } embassy-executor = { version = "0.6.3", path = "../../embassy-executor", features = ["task-arena-size-32768", "arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] } embassy-time = { version = "0.3.2", 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"] } diff --git a/examples/stm32h7/Cargo.toml b/examples/stm32h7/Cargo.toml index 4a82d487f..949cefe1e 100644 --- a/examples/stm32h7/Cargo.toml +++ b/examples/stm32h7/Cargo.toml @@ -7,7 +7,7 @@ license = "MIT OR Apache-2.0" [dependencies] # Change stm32h743bi to your chip name, if necessary. embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["defmt", "stm32h743bi", "time-driver-tim2", "exti", "memory-x", "unstable-pac", "chrono"] } -embassy-sync = { version = "0.6.0", path = "../../embassy-sync", features = ["defmt"] } +embassy-sync = { version = "0.6.1", path = "../../embassy-sync", features = ["defmt"] } embassy-embedded-hal = { version = "0.2.0", path = "../../embassy-embedded-hal" } embassy-executor = { version = "0.6.3", path = "../../embassy-executor", features = ["task-arena-size-32768", "arch-cortex-m", "executor-thread", "executor-interrupt", "defmt", "integrated-timers"] } embassy-time = { version = "0.3.2", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } diff --git a/examples/stm32h723/Cargo.toml b/examples/stm32h723/Cargo.toml index 8ebc1051f..f68c91597 100644 --- a/examples/stm32h723/Cargo.toml +++ b/examples/stm32h723/Cargo.toml @@ -7,7 +7,7 @@ license = "MIT OR Apache-2.0" [dependencies] # Change stm32h723zg to your chip name, if necessary. embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["defmt", "stm32h723zg", "time-driver-tim2", "exti", "memory-x", "unstable-pac", "chrono"] } -embassy-sync = { version = "0.6.0", path = "../../embassy-sync", features = ["defmt"] } +embassy-sync = { version = "0.6.1", path = "../../embassy-sync", features = ["defmt"] } embassy-executor = { version = "0.6.2", path = "../../embassy-executor", features = ["task-arena-size-32768", "arch-cortex-m", "executor-thread", "executor-interrupt", "defmt", "integrated-timers"] } embassy-time = { version = "0.3.2", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } embassy-futures = { version = "0.1.0", path = "../../embassy-futures" } diff --git a/examples/stm32h735/Cargo.toml b/examples/stm32h735/Cargo.toml index e66e344d2..a9c66ec48 100644 --- a/examples/stm32h735/Cargo.toml +++ b/examples/stm32h735/Cargo.toml @@ -6,7 +6,7 @@ license = "MIT OR Apache-2.0" [dependencies] embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["defmt", "stm32h735ig", "time-driver-tim2", "exti", "memory-x", "unstable-pac", "chrono"] } -embassy-sync = { version = "0.6.0", path = "../../embassy-sync", features = ["defmt"] } +embassy-sync = { version = "0.6.1", path = "../../embassy-sync", features = ["defmt"] } embassy-embedded-hal = { version = "0.2.0", path = "../../embassy-embedded-hal" } embassy-executor = { version = "0.6.3", path = "../../embassy-executor", features = ["task-arena-size-32768", "arch-cortex-m", "executor-thread", "executor-interrupt", "defmt", "integrated-timers"] } embassy-time = { version = "0.3.2", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } diff --git a/examples/stm32h755cm4/Cargo.toml b/examples/stm32h755cm4/Cargo.toml index 8b46c3952..18a17dbaf 100644 --- a/examples/stm32h755cm4/Cargo.toml +++ b/examples/stm32h755cm4/Cargo.toml @@ -7,7 +7,7 @@ license = "MIT OR Apache-2.0" [dependencies] # Change stm32h755zi-cm4 to your chip name, if necessary. embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["defmt", "stm32h755zi-cm4", "time-driver-tim2", "exti", "memory-x", "unstable-pac", "chrono"] } -embassy-sync = { version = "0.6.0", path = "../../embassy-sync", features = ["defmt"] } +embassy-sync = { version = "0.6.1", path = "../../embassy-sync", features = ["defmt"] } embassy-embedded-hal = { version = "0.2.0", path = "../../embassy-embedded-hal" } embassy-executor = { version = "0.6.3", path = "../../embassy-executor", features = ["task-arena-size-32768", "arch-cortex-m", "executor-thread", "executor-interrupt", "defmt", "integrated-timers"] } embassy-time = { version = "0.3.1", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } diff --git a/examples/stm32h755cm7/Cargo.toml b/examples/stm32h755cm7/Cargo.toml index 1db5143a4..02f1fcbaf 100644 --- a/examples/stm32h755cm7/Cargo.toml +++ b/examples/stm32h755cm7/Cargo.toml @@ -7,7 +7,7 @@ license = "MIT OR Apache-2.0" [dependencies] # Change stm32h743bi to your chip name, if necessary. embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["defmt", "stm32h755zi-cm7", "time-driver-tim3", "exti", "memory-x", "unstable-pac", "chrono"] } -embassy-sync = { version = "0.6.0", path = "../../embassy-sync", features = ["defmt"] } +embassy-sync = { version = "0.6.1", path = "../../embassy-sync", features = ["defmt"] } embassy-embedded-hal = { version = "0.2.0", path = "../../embassy-embedded-hal" } embassy-executor = { version = "0.6.3", path = "../../embassy-executor", features = ["task-arena-size-32768", "arch-cortex-m", "executor-thread", "executor-interrupt", "defmt", "integrated-timers"] } embassy-time = { version = "0.3.1", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } diff --git a/examples/stm32h7b0/Cargo.toml b/examples/stm32h7b0/Cargo.toml index e22548bf0..78f65a4cc 100644 --- a/examples/stm32h7b0/Cargo.toml +++ b/examples/stm32h7b0/Cargo.toml @@ -6,7 +6,7 @@ license = "MIT OR Apache-2.0" [dependencies] embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["defmt", "stm32h7b0vb", "time-driver-tim2", "exti", "memory-x", "unstable-pac", "chrono"] } -embassy-sync = { version = "0.6.0", path = "../../embassy-sync", features = ["defmt"] } +embassy-sync = { version = "0.6.1", path = "../../embassy-sync", features = ["defmt"] } embassy-embedded-hal = { version = "0.2.0", path = "../../embassy-embedded-hal" } embassy-executor = { version = "0.6.3", path = "../../embassy-executor", features = ["task-arena-size-32768", "arch-cortex-m", "executor-thread", "executor-interrupt", "defmt", "integrated-timers"] } embassy-time = { version = "0.3.2", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } diff --git a/examples/stm32h7rs/Cargo.toml b/examples/stm32h7rs/Cargo.toml index 07c69977d..60013cb88 100644 --- a/examples/stm32h7rs/Cargo.toml +++ b/examples/stm32h7rs/Cargo.toml @@ -7,7 +7,7 @@ license = "MIT OR Apache-2.0" [dependencies] # Change stm32h743bi to your chip name, if necessary. embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["defmt", "stm32h7s3l8", "time-driver-tim2", "exti", "memory-x", "unstable-pac", "chrono"] } -embassy-sync = { version = "0.6.0", path = "../../embassy-sync", features = ["defmt"] } +embassy-sync = { version = "0.6.1", path = "../../embassy-sync", features = ["defmt"] } embassy-executor = { version = "0.6.3", path = "../../embassy-executor", features = ["task-arena-size-32768", "arch-cortex-m", "executor-thread", "executor-interrupt", "defmt", "integrated-timers"] } embassy-time = { version = "0.3.2", 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"] } diff --git a/examples/stm32l0/Cargo.toml b/examples/stm32l0/Cargo.toml index f67fa6b00..95e215b6f 100644 --- a/examples/stm32l0/Cargo.toml +++ b/examples/stm32l0/Cargo.toml @@ -7,7 +7,7 @@ license = "MIT OR Apache-2.0" [dependencies] # Change stm32l072cz to your chip name, if necessary. embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["defmt", "stm32l072cz", "unstable-pac", "time-driver-any", "exti", "memory-x"] } -embassy-sync = { version = "0.6.0", path = "../../embassy-sync", features = ["defmt"] } +embassy-sync = { version = "0.6.1", path = "../../embassy-sync", features = ["defmt"] } embassy-executor = { version = "0.6.3", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] } embassy-time = { version = "0.3.2", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } diff --git a/examples/stm32l1/Cargo.toml b/examples/stm32l1/Cargo.toml index 4d382f16e..33e4f96e5 100644 --- a/examples/stm32l1/Cargo.toml +++ b/examples/stm32l1/Cargo.toml @@ -5,7 +5,7 @@ version = "0.1.0" license = "MIT OR Apache-2.0" [dependencies] -embassy-sync = { version = "0.6.0", path = "../../embassy-sync", features = ["defmt"] } +embassy-sync = { version = "0.6.1", path = "../../embassy-sync", features = ["defmt"] } embassy-executor = { version = "0.6.3", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] } embassy-time = { version = "0.3.2", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = [ "defmt", "stm32l151cb-a", "time-driver-any", "memory-x"] } diff --git a/examples/stm32l4/Cargo.toml b/examples/stm32l4/Cargo.toml index 699358388..9673b097f 100644 --- a/examples/stm32l4/Cargo.toml +++ b/examples/stm32l4/Cargo.toml @@ -7,7 +7,7 @@ license = "MIT OR Apache-2.0" [dependencies] # Change stm32l4s5vi to your chip name, if necessary. embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = [ "defmt", "unstable-pac", "stm32l4s5qi", "memory-x", "time-driver-any", "exti", "chrono"] } -embassy-sync = { version = "0.6.0", path = "../../embassy-sync", features = ["defmt"] } +embassy-sync = { version = "0.6.1", path = "../../embassy-sync", features = ["defmt"] } embassy-executor = { version = "0.6.3", path = "../../embassy-executor", features = ["task-arena-size-32768", "arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] } embassy-time = { version = "0.3.2", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768", ] } embassy-embedded-hal = { version = "0.2.0", path = "../../embassy-embedded-hal" } diff --git a/examples/stm32l5/Cargo.toml b/examples/stm32l5/Cargo.toml index ce9c76441..014a3c4c8 100644 --- a/examples/stm32l5/Cargo.toml +++ b/examples/stm32l5/Cargo.toml @@ -7,7 +7,7 @@ license = "MIT OR Apache-2.0" [dependencies] # Change stm32l552ze to your chip name, if necessary. embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = [ "defmt", "unstable-pac", "stm32l552ze", "time-driver-any", "exti", "memory-x", "low-power"] } -embassy-sync = { version = "0.6.0", path = "../../embassy-sync", features = ["defmt"] } +embassy-sync = { version = "0.6.1", path = "../../embassy-sync", features = ["defmt"] } embassy-executor = { version = "0.6.3", path = "../../embassy-executor", features = ["task-arena-size-32768", "arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] } embassy-time = { version = "0.3.2", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } embassy-usb = { version = "0.3.0", path = "../../embassy-usb", features = ["defmt"] } diff --git a/examples/stm32u0/Cargo.toml b/examples/stm32u0/Cargo.toml index 6dd9bc7fa..fef695c82 100644 --- a/examples/stm32u0/Cargo.toml +++ b/examples/stm32u0/Cargo.toml @@ -7,7 +7,7 @@ license = "MIT OR Apache-2.0" [dependencies] # Change stm32u083rc to your chip name, if necessary. embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = [ "defmt", "time-driver-any", "stm32u083rc", "memory-x", "unstable-pac", "exti", "chrono"] } -embassy-sync = { version = "0.6.0", path = "../../embassy-sync", features = ["defmt"] } +embassy-sync = { version = "0.6.1", path = "../../embassy-sync", features = ["defmt"] } embassy-executor = { version = "0.6.3", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] } embassy-time = { version = "0.3.2", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } embassy-usb = { version = "0.3.0", path = "../../embassy-usb", default-features = false, features = ["defmt"] } diff --git a/examples/stm32u5/Cargo.toml b/examples/stm32u5/Cargo.toml index 9a2b1dccf..528429f4c 100644 --- a/examples/stm32u5/Cargo.toml +++ b/examples/stm32u5/Cargo.toml @@ -7,7 +7,7 @@ license = "MIT OR Apache-2.0" [dependencies] # Change stm32u5g9zj to your chip name, if necessary. embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["defmt", "unstable-pac", "stm32u5g9zj", "time-driver-any", "memory-x" ] } -embassy-sync = { version = "0.6.0", path = "../../embassy-sync", features = ["defmt"] } +embassy-sync = { version = "0.6.1", path = "../../embassy-sync", features = ["defmt"] } embassy-executor = { version = "0.6.3", path = "../../embassy-executor", features = ["task-arena-size-32768", "arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] } embassy-time = { version = "0.3.2", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } embassy-usb = { version = "0.3.0", path = "../../embassy-usb", features = ["defmt"] } diff --git a/examples/stm32wb/Cargo.toml b/examples/stm32wb/Cargo.toml index 9a050e31e..3da74e535 100644 --- a/examples/stm32wb/Cargo.toml +++ b/examples/stm32wb/Cargo.toml @@ -8,7 +8,7 @@ license = "MIT OR Apache-2.0" # Change stm32wb55rg to your chip name in both dependencies, if necessary. embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = [ "defmt", "stm32wb55rg", "time-driver-any", "memory-x", "exti"] } embassy-stm32-wpan = { version = "0.1.0", path = "../../embassy-stm32-wpan", features = ["defmt", "stm32wb55rg"] } -embassy-sync = { version = "0.6.0", path = "../../embassy-sync", features = ["defmt"] } +embassy-sync = { version = "0.6.1", path = "../../embassy-sync", features = ["defmt"] } embassy-executor = { version = "0.6.3", path = "../../embassy-executor", features = ["task-arena-size-32768", "arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] } embassy-time = { version = "0.3.2", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } embassy-net = { version = "0.4.0", path = "../../embassy-net", features = ["defmt", "udp", "proto-ipv6", "medium-ieee802154", ], optional=true } diff --git a/examples/stm32wba/Cargo.toml b/examples/stm32wba/Cargo.toml index 1697ffa1c..2c033ff7f 100644 --- a/examples/stm32wba/Cargo.toml +++ b/examples/stm32wba/Cargo.toml @@ -6,7 +6,7 @@ license = "MIT OR Apache-2.0" [dependencies] embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = [ "defmt", "stm32wba52cg", "time-driver-any", "memory-x", "exti"] } -embassy-sync = { version = "0.6.0", path = "../../embassy-sync", features = ["defmt"] } +embassy-sync = { version = "0.6.1", path = "../../embassy-sync", features = ["defmt"] } embassy-executor = { version = "0.6.3", path = "../../embassy-executor", features = ["task-arena-size-32768", "arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] } embassy-time = { version = "0.3.2", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } embassy-net = { version = "0.4.0", path = "../../embassy-net", features = ["defmt", "udp", "proto-ipv6", "medium-ieee802154", ], optional=true } diff --git a/examples/stm32wl/Cargo.toml b/examples/stm32wl/Cargo.toml index eeb13608c..6507fd1eb 100644 --- a/examples/stm32wl/Cargo.toml +++ b/examples/stm32wl/Cargo.toml @@ -7,7 +7,7 @@ license = "MIT OR Apache-2.0" [dependencies] # Change stm32wl55jc-cm4 to your chip name, if necessary. embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["defmt", "stm32wl55jc-cm4", "time-driver-any", "memory-x", "unstable-pac", "exti", "chrono"] } -embassy-sync = { version = "0.6.0", path = "../../embassy-sync", features = ["defmt"] } +embassy-sync = { version = "0.6.1", path = "../../embassy-sync", features = ["defmt"] } embassy-executor = { version = "0.6.3", path = "../../embassy-executor", features = ["task-arena-size-4096", "arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] } embassy-time = { version = "0.3.2", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } embassy-embedded-hal = { version = "0.2.0", path = "../../embassy-embedded-hal" } diff --git a/examples/wasm/Cargo.toml b/examples/wasm/Cargo.toml index 8c1b8a4f6..5e4d352b5 100644 --- a/examples/wasm/Cargo.toml +++ b/examples/wasm/Cargo.toml @@ -8,7 +8,7 @@ license = "MIT OR Apache-2.0" crate-type = ["cdylib"] [dependencies] -embassy-sync = { version = "0.6.0", path = "../../embassy-sync", features = ["log"] } +embassy-sync = { version = "0.6.1", path = "../../embassy-sync", features = ["log"] } embassy-executor = { version = "0.6.3", path = "../../embassy-executor", features = ["arch-wasm", "executor-thread", "log", "integrated-timers"] } embassy-time = { version = "0.3.2", path = "../../embassy-time", features = ["log", "wasm", ] } diff --git a/tests/nrf/Cargo.toml b/tests/nrf/Cargo.toml index 0e58487d2..00dfda6d6 100644 --- a/tests/nrf/Cargo.toml +++ b/tests/nrf/Cargo.toml @@ -8,7 +8,7 @@ license = "MIT OR Apache-2.0" teleprobe-meta = "1" embassy-futures = { version = "0.1.0", path = "../../embassy-futures" } -embassy-sync = { version = "0.6.0", path = "../../embassy-sync", features = ["defmt", ] } +embassy-sync = { version = "0.6.1", path = "../../embassy-sync", features = ["defmt", ] } embassy-executor = { version = "0.6.3", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "defmt", "task-arena-size-16384", "integrated-timers"] } embassy-time = { version = "0.3.2", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime"] } embassy-nrf = { version = "0.2.0", path = "../../embassy-nrf", features = ["defmt", "time-driver-rtc1", "gpiote", "unstable-pac"] } diff --git a/tests/riscv32/Cargo.toml b/tests/riscv32/Cargo.toml index ae60dac10..e76e07d7c 100644 --- a/tests/riscv32/Cargo.toml +++ b/tests/riscv32/Cargo.toml @@ -6,7 +6,7 @@ license = "MIT OR Apache-2.0" [dependencies] critical-section = { version = "1.1.1", features = ["restore-state-bool"] } -embassy-sync = { version = "0.6.0", path = "../../embassy-sync" } +embassy-sync = { version = "0.6.1", path = "../../embassy-sync" } embassy-executor = { version = "0.6.3", path = "../../embassy-executor", features = ["arch-riscv32", "executor-thread"] } embassy-time = { version = "0.3.2", path = "../../embassy-time" } embassy-futures = { version = "0.1.0", path = "../../embassy-futures" } diff --git a/tests/rp/Cargo.toml b/tests/rp/Cargo.toml index 359dae3bc..91854e659 100644 --- a/tests/rp/Cargo.toml +++ b/tests/rp/Cargo.toml @@ -7,7 +7,7 @@ license = "MIT OR Apache-2.0" [dependencies] teleprobe-meta = "1.1" -embassy-sync = { version = "0.6.0", path = "../../embassy-sync", features = ["defmt"] } +embassy-sync = { version = "0.6.1", path = "../../embassy-sync", features = ["defmt"] } embassy-executor = { version = "0.6.3", path = "../../embassy-executor", features = ["task-arena-size-32768", "arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] } embassy-time = { version = "0.3.2", path = "../../embassy-time", features = ["defmt", ] } embassy-rp = { version = "0.2.0", path = "../../embassy-rp", features = [ "defmt", "unstable-pac", "time-driver", "critical-section-impl", "intrinsics", "rom-v2-intrinsics", "run-from-ram", "rp2040"] } diff --git a/tests/stm32/Cargo.toml b/tests/stm32/Cargo.toml index 58da8c1b5..288c438bd 100644 --- a/tests/stm32/Cargo.toml +++ b/tests/stm32/Cargo.toml @@ -59,7 +59,7 @@ cm0 = ["portable-atomic/unsafe-assume-single-core"] [dependencies] teleprobe-meta = "1" -embassy-sync = { version = "0.6.0", path = "../../embassy-sync", features = ["defmt"] } +embassy-sync = { version = "0.6.1", path = "../../embassy-sync", features = ["defmt"] } embassy-executor = { version = "0.6.3", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] } embassy-time = { version = "0.3.2", path = "../../embassy-time", features = ["defmt", "tick-hz-131_072", "defmt-timestamp-uptime"] } embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = [ "defmt", "unstable-pac", "memory-x", "time-driver-any"] } From 54b39ba49237f315e757ede8ef97ca88118ea807 Mon Sep 17 00:00:00 2001 From: Ivan Li Date: Sat, 23 Nov 2024 12:30:09 +0800 Subject: [PATCH 234/361] stm32 adc g4 async read Signed-off-by: Ivan Li --- embassy-stm32/src/adc/g4.rs | 171 +++++++++++++++++++++++++++++++++--- 1 file changed, 160 insertions(+), 11 deletions(-) diff --git a/embassy-stm32/src/adc/g4.rs b/embassy-stm32/src/adc/g4.rs index 3e9ba8ae2..872cf3f06 100644 --- a/embassy-stm32/src/adc/g4.rs +++ b/embassy-stm32/src/adc/g4.rs @@ -5,8 +5,11 @@ use pac::adc::vals::{Adcaldif, Difsel, Exten}; #[cfg(stm32g4)] use pac::adc::vals::{Adcaldif, Difsel, Exten, Rovsm, Trovs}; use pac::adccommon::vals::Presc; +use stm32_metapac::adc::vals::{Adstp, Dmacfg, Dmaen}; -use super::{blocking_delay_us, Adc, AdcChannel, Instance, Resolution, SampleTime}; +use super::{blocking_delay_us, Adc, AdcChannel, AnyAdcChannel, Instance, Resolution, RxDma, SampleTime}; +use crate::adc::SealedAdcChannel; +use crate::dma::Transfer; use crate::time::Hertz; use crate::{pac, rcc, Peripheral}; @@ -191,10 +194,24 @@ impl<'d, T: Instance> Adc<'d, T> { } fn enable(&mut self) { - T::regs().isr().write(|w| w.set_adrdy(true)); - T::regs().cr().modify(|w| w.set_aden(true)); - while !T::regs().isr().read().adrdy() {} - T::regs().isr().write(|w| w.set_adrdy(true)); + // Make sure bits are off + while T::regs().cr().read().addis() { + // spin + } + + if !T::regs().cr().read().aden() { + // Enable ADC + T::regs().isr().modify(|reg| { + reg.set_adrdy(true); + }); + T::regs().cr().modify(|reg| { + reg.set_aden(true); + }); + + while !T::regs().isr().read().adrdy() { + // spin + } + } } fn configure(&mut self) { @@ -327,23 +344,146 @@ impl<'d, T: Instance> Adc<'d, T> { pub fn blocking_read(&mut self, channel: &mut impl AdcChannel) -> u16 { channel.setup(); - self.read_channel(channel.channel()) + self.read_channel(channel) } - fn read_channel(&mut self, channel: u8) -> u16 { - // Configure channel - Self::set_channel_sample_time(channel, self.sample_time); + /// Read one or multiple ADC channels using DMA. + /// + /// `sequence` iterator and `readings` must have the same length. + /// + /// Example + /// ```rust,ignore + /// use embassy_stm32::adc::{Adc, AdcChannel} + /// + /// let mut adc = Adc::new(p.ADC1); + /// let mut adc_pin0 = p.PA0.degrade_adc(); + /// let mut adc_pin1 = p.PA1.degrade_adc(); + /// let mut measurements = [0u16; 2]; + /// + /// adc.read_async( + /// p.DMA1_CH2, + /// [ + /// (&mut *adc_pin0, SampleTime::CYCLES160_5), + /// (&mut *adc_pin1, SampleTime::CYCLES160_5), + /// ] + /// .into_iter(), + /// &mut measurements, + /// ) + /// .await; + /// defmt::info!("measurements: {}", measurements); + /// ``` + pub async fn read( + &mut self, + rx_dma: &mut impl RxDma, + sequence: impl ExactSizeIterator, SampleTime)>, + readings: &mut [u16], + ) { + assert!(sequence.len() != 0, "Asynchronous read sequence cannot be empty"); + assert!( + sequence.len() == readings.len(), + "Sequence length must be equal to readings length" + ); + assert!( + sequence.len() <= 16, + "Asynchronous read sequence cannot be more than 16 in length" + ); + // Ensure no conversions are ongoing and ADC is enabled. + Self::cancel_conversions(); + self.enable(); + + // Set sequence length + T::regs().sqr1().modify(|w| { + w.set_l(sequence.len() as u8 - 1); + }); + + // Configure channels and ranks + for (_i, (channel, sample_time)) in sequence.enumerate() { + Self::configure_channel(channel, sample_time); + + match _i { + 0..=3 => { + T::regs().sqr1().modify(|w| { + w.set_sq(_i, channel.channel()); + }); + } + 4..=8 => { + T::regs().sqr2().modify(|w| { + w.set_sq(_i - 4, channel.channel()); + }); + } + 9..=13 => { + T::regs().sqr3().modify(|w| { + w.set_sq(_i - 9, channel.channel()); + }); + } + 14..=15 => { + T::regs().sqr4().modify(|w| { + w.set_sq(_i - 14, channel.channel()); + }); + } + _ => unreachable!(), + } + } + + // Set continuous mode with oneshot dma. + // Clear overrun flag before starting transfer. + T::regs().isr().modify(|reg| { + reg.set_ovr(true); + }); + + T::regs().cfgr().modify(|reg| { + reg.set_discen(false); + reg.set_cont(true); + reg.set_dmacfg(Dmacfg::ONESHOT); + reg.set_dmaen(Dmaen::ENABLE); + }); + + let request = rx_dma.request(); + let transfer = unsafe { + Transfer::new_read( + rx_dma, + request, + T::regs().dr().as_ptr() as *mut u16, + readings, + Default::default(), + ) + }; + + // Start conversion + T::regs().cr().modify(|reg| { + reg.set_adstart(true); + }); + + // Wait for conversion sequence to finish. + transfer.await; + + // Ensure conversions are finished. + Self::cancel_conversions(); + + // Reset configuration. + T::regs().cfgr().modify(|reg| { + reg.set_cont(false); + }); + } + + fn configure_channel(channel: &mut impl AdcChannel, sample_time: SampleTime) { + // Configure channel + Self::set_channel_sample_time(channel.channel(), sample_time); + } + + fn read_channel(&mut self, channel: &mut impl AdcChannel) -> u16 { + Self::configure_channel(channel, self.sample_time); #[cfg(stm32h7)] { T::regs().cfgr2().modify(|w| w.set_lshift(0)); T::regs() .pcsel() - .write(|w| w.set_pcsel(channel as _, Pcsel::PRESELECTED)); + .write(|w| w.set_pcsel(channel.channel() as _, Pcsel::PRESELECTED)); } T::regs().sqr1().write(|reg| { - reg.set_sq(0, channel); + reg.set_sq(0, channel.channel()); reg.set_l(0); }); @@ -358,4 +498,13 @@ impl<'d, T: Instance> Adc<'d, T> { T::regs().smpr2().modify(|reg| reg.set_smp((ch - 10) as _, sample_time)); } } + + fn cancel_conversions() { + if T::regs().cr().read().adstart() && !T::regs().cr().read().addis() { + T::regs().cr().modify(|reg| { + reg.set_adstp(Adstp::STOP); + }); + while T::regs().cr().read().adstart() {} + } + } } From b1bbe6bcf21b5e89f9abb15abbece7063127f303 Mon Sep 17 00:00:00 2001 From: Ivan Li Date: Sat, 23 Nov 2024 12:47:56 +0800 Subject: [PATCH 235/361] stm32 adc g4: example of async read Signed-off-by: Ivan Li --- examples/stm32g4/src/bin/adc_dma.rs | 60 +++++++++++++++++++++++++++++ 1 file changed, 60 insertions(+) create mode 100644 examples/stm32g4/src/bin/adc_dma.rs diff --git a/examples/stm32g4/src/bin/adc_dma.rs b/examples/stm32g4/src/bin/adc_dma.rs new file mode 100644 index 000000000..970623b32 --- /dev/null +++ b/examples/stm32g4/src/bin/adc_dma.rs @@ -0,0 +1,60 @@ +#![no_std] +#![no_main] + +use defmt::*; +use embassy_executor::Spawner; +use embassy_stm32::adc::{Adc, AdcChannel as _, SampleTime}; +use embassy_stm32::Config; +use embassy_time::Timer; +use {defmt_rtt as _, panic_probe as _}; + +static mut DMA_BUF: [u16; 2] = [0; 2]; + +#[embassy_executor::main] +async fn main(_spawner: Spawner) { + let mut read_buffer = unsafe { &mut DMA_BUF[..] }; + + let mut config = Config::default(); + { + use embassy_stm32::rcc::*; + config.rcc.pll = Some(Pll { + source: PllSource::HSI, + prediv: PllPreDiv::DIV4, + mul: PllMul::MUL85, + divp: None, + divq: None, + // Main system clock at 170 MHz + divr: Some(PllRDiv::DIV2), + }); + config.rcc.mux.adc12sel = mux::Adcsel::SYS; + config.rcc.sys = Sysclk::PLL1_R; + } + let p = embassy_stm32::init(config); + + info!("Hello World!"); + + let mut adc = Adc::new(p.ADC1); + + let mut dma = p.DMA1_CH1; + let mut vrefint_channel = adc.enable_vrefint().degrade_adc(); + let mut pa0 = p.PA0.degrade_adc(); + + loop { + adc.read( + &mut dma, + [ + (&mut vrefint_channel, SampleTime::CYCLES247_5), + (&mut pa0, SampleTime::CYCLES247_5), + ] + .into_iter(), + &mut read_buffer, + ) + .await; + + let vrefint = read_buffer[0]; + let measured = read_buffer[1]; + info!("vrefint: {}", vrefint); + info!("measured: {}", measured); + Timer::after_millis(500).await; + } +} From 1ca53e286b4e65f8b7ecceaee16bc553011f4b49 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Sat, 23 Nov 2024 00:10:48 +0100 Subject: [PATCH 236/361] otg: fix build with defmt enabled. --- embassy-stm32/Cargo.toml | 12 ++- embassy-usb-synopsys-otg/src/otg_v1.rs | 108 ++++++++++++------------- 2 files changed, 65 insertions(+), 55 deletions(-) diff --git a/embassy-stm32/Cargo.toml b/embassy-stm32/Cargo.toml index 0872afbd9..ebcfb73c9 100644 --- a/embassy-stm32/Cargo.toml +++ b/embassy-stm32/Cargo.toml @@ -110,7 +110,17 @@ default = ["rt"] rt = ["stm32-metapac/rt"] ## Use [`defmt`](https://docs.rs/defmt/latest/defmt/) for logging -defmt = ["dep:defmt", "embassy-sync/defmt", "embassy-embedded-hal/defmt", "embassy-hal-internal/defmt", "embedded-io-async/defmt-03", "embassy-usb-driver/defmt", "embassy-net-driver/defmt", "embassy-time?/defmt"] +defmt = [ + "dep:defmt", + "embassy-sync/defmt", + "embassy-embedded-hal/defmt", + "embassy-hal-internal/defmt", + "embedded-io-async/defmt-03", + "embassy-usb-driver/defmt", + "embassy-net-driver/defmt", + "embassy-time?/defmt", + "embassy-usb-synopsys-otg/defmt", +] exti = [] low-power = [ "dep:embassy-executor", "embassy-executor?/arch-cortex-m", "time" ] diff --git a/embassy-usb-synopsys-otg/src/otg_v1.rs b/embassy-usb-synopsys-otg/src/otg_v1.rs index 18e760fd1..9e65f2315 100644 --- a/embassy-usb-synopsys-otg/src/otg_v1.rs +++ b/embassy-usb-synopsys-otg/src/otg_v1.rs @@ -108,287 +108,287 @@ impl Otg { } #[doc = "Control and status register"] #[inline(always)] - pub const fn gotgctl(self) -> Reg { + pub fn gotgctl(self) -> Reg { unsafe { Reg::from_ptr(self.ptr.add(0x0usize) as _) } } #[doc = "Interrupt register"] #[inline(always)] - pub const fn gotgint(self) -> Reg { + pub fn gotgint(self) -> Reg { unsafe { Reg::from_ptr(self.ptr.add(0x04usize) as _) } } #[doc = "AHB configuration register"] #[inline(always)] - pub const fn gahbcfg(self) -> Reg { + pub fn gahbcfg(self) -> Reg { unsafe { Reg::from_ptr(self.ptr.add(0x08usize) as _) } } #[doc = "USB configuration register"] #[inline(always)] - pub const fn gusbcfg(self) -> Reg { + pub fn gusbcfg(self) -> Reg { unsafe { Reg::from_ptr(self.ptr.add(0x0cusize) as _) } } #[doc = "Reset register"] #[inline(always)] - pub const fn grstctl(self) -> Reg { + pub fn grstctl(self) -> Reg { unsafe { Reg::from_ptr(self.ptr.add(0x10usize) as _) } } #[doc = "Core interrupt register"] #[inline(always)] - pub const fn gintsts(self) -> Reg { + pub fn gintsts(self) -> Reg { unsafe { Reg::from_ptr(self.ptr.add(0x14usize) as _) } } #[doc = "Interrupt mask register"] #[inline(always)] - pub const fn gintmsk(self) -> Reg { + pub fn gintmsk(self) -> Reg { unsafe { Reg::from_ptr(self.ptr.add(0x18usize) as _) } } #[doc = "Receive status debug read register"] #[inline(always)] - pub const fn grxstsr(self) -> Reg { + pub fn grxstsr(self) -> Reg { unsafe { Reg::from_ptr(self.ptr.add(0x1cusize) as _) } } #[doc = "Status read and pop register"] #[inline(always)] - pub const fn grxstsp(self) -> Reg { + pub fn grxstsp(self) -> Reg { unsafe { Reg::from_ptr(self.ptr.add(0x20usize) as _) } } #[doc = "Receive FIFO size register"] #[inline(always)] - pub const fn grxfsiz(self) -> Reg { + pub fn grxfsiz(self) -> Reg { unsafe { Reg::from_ptr(self.ptr.add(0x24usize) as _) } } #[doc = "Endpoint 0 transmit FIFO size register (device mode)"] #[inline(always)] - pub const fn dieptxf0(self) -> Reg { + pub fn dieptxf0(self) -> Reg { unsafe { Reg::from_ptr(self.ptr.add(0x28usize) as _) } } #[doc = "Non-periodic transmit FIFO size register (host mode)"] #[inline(always)] - pub const fn hnptxfsiz(self) -> Reg { + pub fn hnptxfsiz(self) -> Reg { unsafe { Reg::from_ptr(self.ptr.add(0x28usize) as _) } } #[doc = "Non-periodic transmit FIFO/queue status register (host mode)"] #[inline(always)] - pub const fn hnptxsts(self) -> Reg { + pub fn hnptxsts(self) -> Reg { unsafe { Reg::from_ptr(self.ptr.add(0x2cusize) as _) } } #[doc = "OTG I2C access register"] #[inline(always)] - pub const fn gi2cctl(self) -> Reg { + pub fn gi2cctl(self) -> Reg { unsafe { Reg::from_ptr(self.ptr.add(0x30usize) as _) } } #[doc = "General core configuration register, for core_id 0x0000_1xxx"] #[inline(always)] - pub const fn gccfg_v1(self) -> Reg { + pub fn gccfg_v1(self) -> Reg { unsafe { Reg::from_ptr(self.ptr.add(0x38usize) as _) } } #[doc = "General core configuration register, for core_id 0x0000_\\[23\\]xxx"] #[inline(always)] - pub const fn gccfg_v2(self) -> Reg { + pub fn gccfg_v2(self) -> Reg { unsafe { Reg::from_ptr(self.ptr.add(0x38usize) as _) } } #[doc = "General core configuration register, for core_id 0x0000_5xxx"] #[inline(always)] - pub const fn gccfg_v3(self) -> Reg { + pub fn gccfg_v3(self) -> Reg { unsafe { Reg::from_ptr(self.ptr.add(0x38usize) as _) } } #[doc = "Core ID register"] #[inline(always)] - pub const fn cid(self) -> Reg { + pub fn cid(self) -> Reg { unsafe { Reg::from_ptr(self.ptr.add(0x3cusize) as _) } } #[doc = "OTG core LPM configuration register"] #[inline(always)] - pub const fn glpmcfg(self) -> Reg { + pub fn glpmcfg(self) -> Reg { unsafe { Reg::from_ptr(self.ptr.add(0x54usize) as _) } } #[doc = "Host periodic transmit FIFO size register"] #[inline(always)] - pub const fn hptxfsiz(self) -> Reg { + pub fn hptxfsiz(self) -> Reg { unsafe { Reg::from_ptr(self.ptr.add(0x0100usize) as _) } } #[doc = "Device IN endpoint transmit FIFO size register"] #[inline(always)] - pub const fn dieptxf(self, n: usize) -> Reg { + pub fn dieptxf(self, n: usize) -> Reg { assert!(n < 7usize); unsafe { Reg::from_ptr(self.ptr.add(0x0104usize + n * 4usize) as _) } } #[doc = "Host configuration register"] #[inline(always)] - pub const fn hcfg(self) -> Reg { + pub fn hcfg(self) -> Reg { unsafe { Reg::from_ptr(self.ptr.add(0x0400usize) as _) } } #[doc = "Host frame interval register"] #[inline(always)] - pub const fn hfir(self) -> Reg { + pub fn hfir(self) -> Reg { unsafe { Reg::from_ptr(self.ptr.add(0x0404usize) as _) } } #[doc = "Host frame number/frame time remaining register"] #[inline(always)] - pub const fn hfnum(self) -> Reg { + pub fn hfnum(self) -> Reg { unsafe { Reg::from_ptr(self.ptr.add(0x0408usize) as _) } } #[doc = "Periodic transmit FIFO/queue status register"] #[inline(always)] - pub const fn hptxsts(self) -> Reg { + pub fn hptxsts(self) -> Reg { unsafe { Reg::from_ptr(self.ptr.add(0x0410usize) as _) } } #[doc = "Host all channels interrupt register"] #[inline(always)] - pub const fn haint(self) -> Reg { + pub fn haint(self) -> Reg { unsafe { Reg::from_ptr(self.ptr.add(0x0414usize) as _) } } #[doc = "Host all channels interrupt mask register"] #[inline(always)] - pub const fn haintmsk(self) -> Reg { + pub fn haintmsk(self) -> Reg { unsafe { Reg::from_ptr(self.ptr.add(0x0418usize) as _) } } #[doc = "Host port control and status register"] #[inline(always)] - pub const fn hprt(self) -> Reg { + pub fn hprt(self) -> Reg { unsafe { Reg::from_ptr(self.ptr.add(0x0440usize) as _) } } #[doc = "Host channel characteristics register"] #[inline(always)] - pub const fn hcchar(self, n: usize) -> Reg { + pub fn hcchar(self, n: usize) -> Reg { assert!(n < 12usize); unsafe { Reg::from_ptr(self.ptr.add(0x0500usize + n * 32usize) as _) } } #[doc = "Host channel split control register"] #[inline(always)] - pub const fn hcsplt(self, n: usize) -> Reg { + pub fn hcsplt(self, n: usize) -> Reg { assert!(n < 12usize); unsafe { Reg::from_ptr(self.ptr.add(0x0504usize + n * 32usize) as _) } } #[doc = "Host channel interrupt register"] #[inline(always)] - pub const fn hcint(self, n: usize) -> Reg { + pub fn hcint(self, n: usize) -> Reg { assert!(n < 12usize); unsafe { Reg::from_ptr(self.ptr.add(0x0508usize + n * 32usize) as _) } } #[doc = "Host channel mask register"] #[inline(always)] - pub const fn hcintmsk(self, n: usize) -> Reg { + pub fn hcintmsk(self, n: usize) -> Reg { assert!(n < 12usize); unsafe { Reg::from_ptr(self.ptr.add(0x050cusize + n * 32usize) as _) } } #[doc = "Host channel transfer size register"] #[inline(always)] - pub const fn hctsiz(self, n: usize) -> Reg { + pub fn hctsiz(self, n: usize) -> Reg { assert!(n < 12usize); unsafe { Reg::from_ptr(self.ptr.add(0x0510usize + n * 32usize) as _) } } #[doc = "Host channel DMA address register"] #[inline(always)] - pub const fn hcdma(self, n: usize) -> Reg { + pub fn hcdma(self, n: usize) -> Reg { assert!(n < 12usize); unsafe { Reg::from_ptr(self.ptr.add(0x0514usize + n * 32usize) as _) } } #[doc = "Device configuration register"] #[inline(always)] - pub const fn dcfg(self) -> Reg { + pub fn dcfg(self) -> Reg { unsafe { Reg::from_ptr(self.ptr.add(0x0800usize) as _) } } #[doc = "Device control register"] #[inline(always)] - pub const fn dctl(self) -> Reg { + pub fn dctl(self) -> Reg { unsafe { Reg::from_ptr(self.ptr.add(0x0804usize) as _) } } #[doc = "Device status register"] #[inline(always)] - pub const fn dsts(self) -> Reg { + pub fn dsts(self) -> Reg { unsafe { Reg::from_ptr(self.ptr.add(0x0808usize) as _) } } #[doc = "Device IN endpoint common interrupt mask register"] #[inline(always)] - pub const fn diepmsk(self) -> Reg { + pub fn diepmsk(self) -> Reg { unsafe { Reg::from_ptr(self.ptr.add(0x0810usize) as _) } } #[doc = "Device OUT endpoint common interrupt mask register"] #[inline(always)] - pub const fn doepmsk(self) -> Reg { + pub fn doepmsk(self) -> Reg { unsafe { Reg::from_ptr(self.ptr.add(0x0814usize) as _) } } #[doc = "Device all endpoints interrupt register"] #[inline(always)] - pub const fn daint(self) -> Reg { + pub fn daint(self) -> Reg { unsafe { Reg::from_ptr(self.ptr.add(0x0818usize) as _) } } #[doc = "All endpoints interrupt mask register"] #[inline(always)] - pub const fn daintmsk(self) -> Reg { + pub fn daintmsk(self) -> Reg { unsafe { Reg::from_ptr(self.ptr.add(0x081cusize) as _) } } #[doc = "Device VBUS discharge time register"] #[inline(always)] - pub const fn dvbusdis(self) -> Reg { + pub fn dvbusdis(self) -> Reg { unsafe { Reg::from_ptr(self.ptr.add(0x0828usize) as _) } } #[doc = "Device VBUS pulsing time register"] #[inline(always)] - pub const fn dvbuspulse(self) -> Reg { + pub fn dvbuspulse(self) -> Reg { unsafe { Reg::from_ptr(self.ptr.add(0x082cusize) as _) } } #[doc = "Device IN endpoint FIFO empty interrupt mask register"] #[inline(always)] - pub const fn diepempmsk(self) -> Reg { + pub fn diepempmsk(self) -> Reg { unsafe { Reg::from_ptr(self.ptr.add(0x0834usize) as _) } } #[doc = "Device IN endpoint control register"] #[inline(always)] - pub const fn diepctl(self, n: usize) -> Reg { + pub fn diepctl(self, n: usize) -> Reg { assert!(n < 16usize); unsafe { Reg::from_ptr(self.ptr.add(0x0900usize + n * 32usize) as _) } } #[doc = "Device IN endpoint interrupt register"] #[inline(always)] - pub const fn diepint(self, n: usize) -> Reg { + pub fn diepint(self, n: usize) -> Reg { assert!(n < 16usize); unsafe { Reg::from_ptr(self.ptr.add(0x0908usize + n * 32usize) as _) } } #[doc = "Device IN endpoint transfer size register"] #[inline(always)] - pub const fn dieptsiz(self, n: usize) -> Reg { + pub fn dieptsiz(self, n: usize) -> Reg { assert!(n < 16usize); unsafe { Reg::from_ptr(self.ptr.add(0x0910usize + n * 32usize) as _) } } #[doc = "Device IN endpoint transmit FIFO status register"] #[inline(always)] - pub const fn dtxfsts(self, n: usize) -> Reg { + pub fn dtxfsts(self, n: usize) -> Reg { assert!(n < 16usize); unsafe { Reg::from_ptr(self.ptr.add(0x0918usize + n * 32usize) as _) } } #[doc = "Device OUT endpoint control register"] #[inline(always)] - pub const fn doepctl(self, n: usize) -> Reg { + pub fn doepctl(self, n: usize) -> Reg { assert!(n < 16usize); unsafe { Reg::from_ptr(self.ptr.add(0x0b00usize + n * 32usize) as _) } } #[doc = "Device OUT endpoint interrupt register"] #[inline(always)] - pub const fn doepint(self, n: usize) -> Reg { + pub fn doepint(self, n: usize) -> Reg { assert!(n < 16usize); unsafe { Reg::from_ptr(self.ptr.add(0x0b08usize + n * 32usize) as _) } } #[doc = "Device OUT endpoint transfer size register"] #[inline(always)] - pub const fn doeptsiz(self, n: usize) -> Reg { + pub fn doeptsiz(self, n: usize) -> Reg { assert!(n < 16usize); unsafe { Reg::from_ptr(self.ptr.add(0x0b10usize + n * 32usize) as _) } } #[doc = "Device OUT/IN endpoint DMA address register"] #[inline(always)] - pub const fn doepdma(self, n: usize) -> Reg { + pub fn doepdma(self, n: usize) -> Reg { assert!(n < 16usize); unsafe { Reg::from_ptr(self.ptr.add(0x0b14usize + n * 32usize) as _) } } #[doc = "Power and clock gating control register"] #[inline(always)] - pub const fn pcgcctl(self) -> Reg { + pub fn pcgcctl(self) -> Reg { unsafe { Reg::from_ptr(self.ptr.add(0x0e00usize) as _) } } #[doc = "Device endpoint / host channel FIFO register"] #[inline(always)] - pub const fn fifo(self, n: usize) -> Reg { + pub fn fifo(self, n: usize) -> Reg { assert!(n < 16usize); unsafe { Reg::from_ptr(self.ptr.add(0x1000usize + n * 4096usize) as _) } } From 4f459bb91846ed9d72a26c0b751e56cfb0098d21 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Sat, 23 Nov 2024 00:13:15 +0100 Subject: [PATCH 237/361] otg: improve trace logging, print bytes as hex. --- embassy-stm32/src/usb/otg.rs | 1 - embassy-usb-synopsys-otg/src/lib.rs | 12 ++++++++---- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/embassy-stm32/src/usb/otg.rs b/embassy-stm32/src/usb/otg.rs index 00cafe6e4..153542fbd 100644 --- a/embassy-stm32/src/usb/otg.rs +++ b/embassy-stm32/src/usb/otg.rs @@ -24,7 +24,6 @@ pub struct InterruptHandler { impl interrupt::typelevel::Handler for InterruptHandler { unsafe fn on_interrupt() { - trace!("irq"); let r = T::regs(); let state = T::state(); diff --git a/embassy-usb-synopsys-otg/src/lib.rs b/embassy-usb-synopsys-otg/src/lib.rs index f90403936..bd0ef04b0 100644 --- a/embassy-usb-synopsys-otg/src/lib.rs +++ b/embassy-usb-synopsys-otg/src/lib.rs @@ -18,6 +18,8 @@ use embassy_usb_driver::{ EndpointType, Event, Unsupported, }; +use crate::fmt::Bytes; + pub mod otg_v1; use otg_v1::{regs, vals, Otg}; @@ -29,6 +31,8 @@ pub unsafe fn on_interrupt( ep_count: usize, quirk_setup_late_cnak: bool, ) { + trace!("irq"); + let ints = r.gintsts().read(); if ints.wkupint() || ints.usbsusp() || ints.usbrst() || ints.enumdne() || ints.otgint() || ints.srqint() { // Mask interrupts and notify `Bus` to process them @@ -1126,7 +1130,7 @@ impl<'d> embassy_usb_driver::EndpointOut for Endpoint<'d, Out> { impl<'d> embassy_usb_driver::EndpointIn for Endpoint<'d, In> { async fn write(&mut self, buf: &[u8]) -> Result<(), EndpointError> { - trace!("write ep={:?} data={:?}", self.info.addr, buf); + trace!("write ep={:?} data={:?}", self.info.addr, Bytes(buf)); if buf.len() > self.info.max_packet_size as usize { return Err(EndpointError::BufferOverflow); @@ -1267,7 +1271,7 @@ impl<'d> embassy_usb_driver::ControlPipe for ControlPipe<'d> { .modify(|w| w.set_cnak(true)); } - trace!("SETUP received: {:?}", data); + trace!("SETUP received: {:?}", Bytes(&data)); Poll::Ready(data) } else { trace!("SETUP waiting"); @@ -1280,12 +1284,12 @@ impl<'d> embassy_usb_driver::ControlPipe for ControlPipe<'d> { async fn data_out(&mut self, buf: &mut [u8], _first: bool, _last: bool) -> Result { trace!("control: data_out"); let len = self.ep_out.read(buf).await?; - trace!("control: data_out read: {:?}", &buf[..len]); + trace!("control: data_out read: {:?}", Bytes(&buf[..len])); Ok(len) } async fn data_in(&mut self, data: &[u8], _first: bool, last: bool) -> Result<(), EndpointError> { - trace!("control: data_in write: {:?}", data); + trace!("control: data_in write: {:?}", Bytes(data)); self.ep_in.write(data).await?; // wait for status response from host after sending the last packet From 032af9d512140d8464680a444debc76f36222a31 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Sat, 23 Nov 2024 00:23:40 +0100 Subject: [PATCH 238/361] otg: fix corruption in CONTROL OUT transfers in stm32f4. The RM says we have to process STUP (and therefore clear CNAK to start the data stage) in the DOEPINT STUP interrupt. Seems doing it in RXFLVL when we receive the data is too early. This makes it work consistently on all chips, so the quirk is no longer needed. Fixes #3493 Fixes #3459 --- embassy-stm32/src/usb/otg.rs | 20 +---- embassy-usb-synopsys-otg/src/lib.rs | 109 ++++++++++++---------------- 2 files changed, 50 insertions(+), 79 deletions(-) diff --git a/embassy-stm32/src/usb/otg.rs b/embassy-stm32/src/usb/otg.rs index 153542fbd..6ca28b156 100644 --- a/embassy-stm32/src/usb/otg.rs +++ b/embassy-stm32/src/usb/otg.rs @@ -27,9 +27,7 @@ impl interrupt::typelevel::Handler for InterruptHandl let r = T::regs(); let state = T::state(); - let setup_late_cnak = quirk_setup_late_cnak(r); - - on_interrupt_impl(r, state, T::ENDPOINT_COUNT, setup_late_cnak); + on_interrupt_impl(r, state, T::ENDPOINT_COUNT); } } @@ -86,7 +84,6 @@ impl<'d, T: Instance> Driver<'d, T> { extra_rx_fifo_words: RX_FIFO_EXTRA_SIZE_WORDS, endpoint_count: T::ENDPOINT_COUNT, phy_type: PhyType::InternalFullSpeed, - quirk_setup_late_cnak: quirk_setup_late_cnak(regs), calculate_trdt_fn: calculate_trdt::, }; @@ -116,16 +113,13 @@ impl<'d, T: Instance> Driver<'d, T> { dp.set_as_af(dp.af_num(), AfType::output(OutputType::PushPull, Speed::VeryHigh)); dm.set_as_af(dm.af_num(), AfType::output(OutputType::PushPull, Speed::VeryHigh)); - let regs = T::regs(); - let instance = OtgInstance { - regs, + regs: T::regs(), state: T::state(), fifo_depth_words: T::FIFO_DEPTH_WORDS, extra_rx_fifo_words: RX_FIFO_EXTRA_SIZE_WORDS, endpoint_count: T::ENDPOINT_COUNT, phy_type: PhyType::InternalHighSpeed, - quirk_setup_late_cnak: quirk_setup_late_cnak(regs), calculate_trdt_fn: calculate_trdt::, }; @@ -165,8 +159,6 @@ impl<'d, T: Instance> Driver<'d, T> { ulpi_d7 ); - let regs = T::regs(); - let instance = OtgInstance { regs: T::regs(), state: T::state(), @@ -174,7 +166,6 @@ impl<'d, T: Instance> Driver<'d, T> { extra_rx_fifo_words: RX_FIFO_EXTRA_SIZE_WORDS, endpoint_count: T::ENDPOINT_COUNT, phy_type: PhyType::ExternalFullSpeed, - quirk_setup_late_cnak: quirk_setup_late_cnak(regs), calculate_trdt_fn: calculate_trdt::, }; @@ -216,8 +207,6 @@ impl<'d, T: Instance> Driver<'d, T> { ulpi_d7 ); - let regs = T::regs(); - let instance = OtgInstance { regs: T::regs(), state: T::state(), @@ -225,7 +214,6 @@ impl<'d, T: Instance> Driver<'d, T> { extra_rx_fifo_words: RX_FIFO_EXTRA_SIZE_WORDS, endpoint_count: T::ENDPOINT_COUNT, phy_type: PhyType::ExternalHighSpeed, - quirk_setup_late_cnak: quirk_setup_late_cnak(regs), calculate_trdt_fn: calculate_trdt::, }; @@ -578,7 +566,3 @@ fn calculate_trdt(speed: Dspd) -> u8 { _ => unimplemented!(), } } - -fn quirk_setup_late_cnak(r: Otg) -> bool { - r.cid().read().0 & 0xf000 == 0x1000 -} diff --git a/embassy-usb-synopsys-otg/src/lib.rs b/embassy-usb-synopsys-otg/src/lib.rs index bd0ef04b0..615faa2fc 100644 --- a/embassy-usb-synopsys-otg/src/lib.rs +++ b/embassy-usb-synopsys-otg/src/lib.rs @@ -9,7 +9,7 @@ mod fmt; use core::cell::UnsafeCell; use core::future::poll_fn; use core::marker::PhantomData; -use core::sync::atomic::{AtomicBool, AtomicU16, Ordering}; +use core::sync::atomic::{AtomicBool, AtomicU16, AtomicU32, Ordering}; use core::task::Poll; use embassy_sync::waitqueue::AtomicWaker; @@ -25,18 +25,17 @@ pub mod otg_v1; use otg_v1::{regs, vals, Otg}; /// Handle interrupts. -pub unsafe fn on_interrupt( - r: Otg, - state: &State, - ep_count: usize, - quirk_setup_late_cnak: bool, -) { +pub unsafe fn on_interrupt(r: Otg, state: &State, ep_count: usize) { trace!("irq"); let ints = r.gintsts().read(); if ints.wkupint() || ints.usbsusp() || ints.usbrst() || ints.enumdne() || ints.otgint() || ints.srqint() { // Mask interrupts and notify `Bus` to process them - r.gintmsk().write(|_| {}); + r.gintmsk().write(|w| { + w.set_iepint(true); + w.set_oepint(true); + w.set_rxflvlm(true); + }); state.bus_waker.wake(); } @@ -64,19 +63,9 @@ pub unsafe fn on_interrupt( while r.grstctl().read().txfflsh() {} } - if state.cp_state.setup_ready.load(Ordering::Relaxed) == false { - // SAFETY: exclusive access ensured by atomic bool - let data = unsafe { &mut *state.cp_state.setup_data.get() }; - data[0..4].copy_from_slice(&r.fifo(0).read().0.to_ne_bytes()); - data[4..8].copy_from_slice(&r.fifo(0).read().0.to_ne_bytes()); - state.cp_state.setup_ready.store(true, Ordering::Release); - state.ep_states[0].out_waker.wake(); - } else { - error!("received SETUP before previous finished processing"); - // discard FIFO - r.fifo(0).read(); - r.fifo(0).read(); - } + let data = &state.cp_state.setup_data; + data[0].store(r.fifo(0).read().data(), Ordering::Relaxed); + data[1].store(r.fifo(0).read().data(), Ordering::Relaxed); } vals::Pktstsd::OUT_DATA_RX => { trace!("OUT_DATA_RX ep={} len={}", ep_num, len); @@ -110,11 +99,6 @@ pub unsafe fn on_interrupt( } vals::Pktstsd::SETUP_DATA_DONE => { trace!("SETUP_DATA_DONE ep={}", ep_num); - - if quirk_setup_late_cnak { - // Clear NAK to indicate we are ready to receive more data - r.doepctl(ep_num).modify(|w| w.set_cnak(true)); - } } x => trace!("unknown PKTSTS: {}", x.to_bits()), } @@ -151,25 +135,30 @@ pub unsafe fn on_interrupt( } } - // not needed? reception handled in rxflvl - // OUT endpoint interrupt - // if ints.oepint() { - // let mut ep_mask = r.daint().read().oepint(); - // let mut ep_num = 0; + // out endpoint interrupt + if ints.oepint() { + trace!("oepint"); + let mut ep_mask = r.daint().read().oepint(); + let mut ep_num = 0; - // while ep_mask != 0 { - // if ep_mask & 1 != 0 { - // let ep_ints = r.doepint(ep_num).read(); - // // clear all - // r.doepint(ep_num).write_value(ep_ints); - // state.ep_out_wakers[ep_num].wake(); - // trace!("out ep={} irq val={:08x}", ep_num, ep_ints.0); - // } + // Iterate over endpoints while there are non-zero bits in the mask + while ep_mask != 0 { + if ep_mask & 1 != 0 { + let ep_ints = r.doepint(ep_num).read(); + // clear all + r.doepint(ep_num).write_value(ep_ints); - // ep_mask >>= 1; - // ep_num += 1; - // } - // } + if ep_ints.stup() { + state.cp_state.setup_ready.store(true, Ordering::Release); + } + state.ep_states[ep_num].out_waker.wake(); + trace!("out ep={} irq val={:08x}", ep_num, ep_ints.0); + } + + ep_mask >>= 1; + ep_num += 1; + } + } } /// USB PHY type @@ -236,7 +225,7 @@ unsafe impl Sync for EpState {} struct ControlPipeSetupState { /// Holds received SETUP packets. Available if [Ep0State::setup_ready] is true. - setup_data: UnsafeCell<[u8; 8]>, + setup_data: [AtomicU32; 2], setup_ready: AtomicBool, } @@ -265,7 +254,7 @@ impl State { Self { cp_state: ControlPipeSetupState { - setup_data: UnsafeCell::new([0u8; 8]), + setup_data: [const { AtomicU32::new(0) }; 2], setup_ready: AtomicBool::new(false), }, ep_states: [NEW_EP_STATE; EP_COUNT], @@ -480,7 +469,6 @@ impl<'d, const MAX_EP_COUNT: usize> embassy_usb_driver::Driver<'d> for Driver<'d trace!("start"); let regs = self.instance.regs; - let quirk_setup_late_cnak = self.instance.quirk_setup_late_cnak; let cp_setup_state = &self.instance.state.cp_state; ( Bus { @@ -496,7 +484,6 @@ impl<'d, const MAX_EP_COUNT: usize> embassy_usb_driver::Driver<'d> for Driver<'d ep_out, ep_in, regs, - quirk_setup_late_cnak, }, ) } @@ -632,6 +619,11 @@ impl<'d, const MAX_EP_COUNT: usize> Bus<'d, MAX_EP_COUNT> { w.set_xfrcm(true); }); + // Unmask SETUP received EP interrupt + r.doepmsk().write(|w| { + w.set_stupm(true); + }); + // Unmask and clear core interrupts self.restore_irqs(); r.gintsts().write_value(regs::Gintsts(0xFFFF_FFFF)); @@ -743,7 +735,7 @@ impl<'d, const MAX_EP_COUNT: usize> Bus<'d, MAX_EP_COUNT> { regs.doeptsiz(index).modify(|w| { w.set_xfrsiz(ep.max_packet_size as _); if index == 0 { - w.set_rxdpid_stupcnt(1); + w.set_rxdpid_stupcnt(3); } else { w.set_pktcnt(1); } @@ -755,8 +747,7 @@ impl<'d, const MAX_EP_COUNT: usize> Bus<'d, MAX_EP_COUNT> { // Enable IRQs for allocated endpoints regs.daintmsk().modify(|w| { w.set_iepm(ep_irq_mask(&self.ep_in)); - // OUT interrupts not used, handled in RXFLVL - // w.set_oepm(ep_irq_mask(&self.ep_out)); + w.set_oepm(ep_irq_mask(&self.ep_out)); }); } @@ -1242,7 +1233,6 @@ pub struct ControlPipe<'d> { setup_state: &'d ControlPipeSetupState, ep_in: Endpoint<'d, In>, ep_out: Endpoint<'d, Out>, - quirk_setup_late_cnak: bool, } impl<'d> embassy_usb_driver::ControlPipe for ControlPipe<'d> { @@ -1255,21 +1245,20 @@ impl<'d> embassy_usb_driver::ControlPipe for ControlPipe<'d> { self.ep_out.state.out_waker.register(cx.waker()); if self.setup_state.setup_ready.load(Ordering::Relaxed) { - let data = unsafe { *self.setup_state.setup_data.get() }; + let mut data = [0; 8]; + data[0..4].copy_from_slice(&self.setup_state.setup_data[0].load(Ordering::Relaxed).to_ne_bytes()); + data[4..8].copy_from_slice(&self.setup_state.setup_data[1].load(Ordering::Relaxed).to_ne_bytes()); self.setup_state.setup_ready.store(false, Ordering::Release); // EP0 should not be controlled by `Bus` so this RMW does not need a critical section - // Receive 1 SETUP packet self.regs.doeptsiz(self.ep_out.info.addr.index()).modify(|w| { - w.set_rxdpid_stupcnt(1); + w.set_rxdpid_stupcnt(3); }); // Clear NAK to indicate we are ready to receive more data - if !self.quirk_setup_late_cnak { - self.regs - .doepctl(self.ep_out.info.addr.index()) - .modify(|w| w.set_cnak(true)); - } + self.regs + .doepctl(self.ep_out.info.addr.index()) + .modify(|w| w.set_cnak(true)); trace!("SETUP received: {:?}", Bytes(&data)); Poll::Ready(data) @@ -1389,8 +1378,6 @@ pub struct OtgInstance<'d, const MAX_EP_COUNT: usize> { pub phy_type: PhyType, /// Extra RX FIFO words needed by some implementations. pub extra_rx_fifo_words: u16, - /// Whether to set up late cnak - pub quirk_setup_late_cnak: bool, /// Function to calculate TRDT value based on some internal clock speed. pub calculate_trdt_fn: fn(speed: vals::Dspd) -> u8, } From bc7372d7011c36157e4d55e05d0a3c5a82ba6f1c Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Sat, 23 Nov 2024 00:29:38 +0100 Subject: [PATCH 239/361] otg: use const blocks for init. --- embassy-usb-synopsys-otg/src/lib.rs | 21 +++++++++------------ 1 file changed, 9 insertions(+), 12 deletions(-) diff --git a/embassy-usb-synopsys-otg/src/lib.rs b/embassy-usb-synopsys-otg/src/lib.rs index 615faa2fc..44b2bd093 100644 --- a/embassy-usb-synopsys-otg/src/lib.rs +++ b/embassy-usb-synopsys-otg/src/lib.rs @@ -242,23 +242,20 @@ unsafe impl Sync for State {} impl State { /// Create a new State. pub const fn new() -> Self { - const NEW_AW: AtomicWaker = AtomicWaker::new(); - const NEW_BUF: UnsafeCell<*mut u8> = UnsafeCell::new(0 as _); - const NEW_SIZE: AtomicU16 = AtomicU16::new(EP_OUT_BUFFER_EMPTY); - const NEW_EP_STATE: EpState = EpState { - in_waker: NEW_AW, - out_waker: NEW_AW, - out_buffer: NEW_BUF, - out_size: NEW_SIZE, - }; - Self { cp_state: ControlPipeSetupState { setup_data: [const { AtomicU32::new(0) }; 2], setup_ready: AtomicBool::new(false), }, - ep_states: [NEW_EP_STATE; EP_COUNT], - bus_waker: NEW_AW, + ep_states: [const { + EpState { + in_waker: AtomicWaker::new(), + out_waker: AtomicWaker::new(), + out_buffer: UnsafeCell::new(0 as _), + out_size: AtomicU16::new(EP_OUT_BUFFER_EMPTY), + } + }; EP_COUNT], + bus_waker: AtomicWaker::new(), } } } From ffc7b732e90af9715a31526c7f179e76c2a96062 Mon Sep 17 00:00:00 2001 From: elagil Date: Sat, 2 Nov 2024 20:01:20 +0100 Subject: [PATCH 240/361] feat(usb): add USB Audio Class 1 --- embassy-usb/src/class/mod.rs | 1 + embassy-usb/src/class/uac1/class_codes.rs | 151 ++++ embassy-usb/src/class/uac1/mod.rs | 134 ++++ embassy-usb/src/class/uac1/speaker.rs | 778 ++++++++++++++++++++ embassy-usb/src/class/uac1/terminal_type.rs | 50 ++ 5 files changed, 1114 insertions(+) create mode 100644 embassy-usb/src/class/uac1/class_codes.rs create mode 100644 embassy-usb/src/class/uac1/mod.rs create mode 100644 embassy-usb/src/class/uac1/speaker.rs create mode 100644 embassy-usb/src/class/uac1/terminal_type.rs diff --git a/embassy-usb/src/class/mod.rs b/embassy-usb/src/class/mod.rs index b883ed4e5..4bd89eb66 100644 --- a/embassy-usb/src/class/mod.rs +++ b/embassy-usb/src/class/mod.rs @@ -3,4 +3,5 @@ pub mod cdc_acm; pub mod cdc_ncm; pub mod hid; pub mod midi; +pub mod uac1; pub mod web_usb; diff --git a/embassy-usb/src/class/uac1/class_codes.rs b/embassy-usb/src/class/uac1/class_codes.rs new file mode 100644 index 000000000..3f6956771 --- /dev/null +++ b/embassy-usb/src/class/uac1/class_codes.rs @@ -0,0 +1,151 @@ +//! Audio Device Class Codes as defined in Universal Serial Bus Device Class +//! Definition for Audio Devices, Release 1.0, Appendix A and Universal Serial +//! Bus Device Class Definition for Audio Data Formats, Release 1.0, Appendix +//! A.1.1 (Audio Data Format Type I Codes) +#![allow(dead_code)] + +/// The current version of the ADC specification (1.0) +pub const ADC_VERSION: u16 = 0x0100; + +/// The current version of the USB device (1.0) +pub const DEVICE_VERSION: u16 = 0x0100; + +/// Audio Interface Class Code +pub const USB_AUDIO_CLASS: u8 = 0x01; + +// Audio Interface Subclass Codes +pub const USB_UNDEFINED_SUBCLASS: u8 = 0x00; +pub const USB_AUDIOCONTROL_SUBCLASS: u8 = 0x01; +pub const USB_AUDIOSTREAMING_SUBCLASS: u8 = 0x02; +pub const USB_MIDISTREAMING_SUBCLASS: u8 = 0x03; + +// Audio Protocol Code +pub const PROTOCOL_NONE: u8 = 0x00; + +// Audio Class-Specific Descriptor Types +pub const CS_UNDEFINED: u8 = 0x20; +pub const CS_DEVICE: u8 = 0x21; +pub const CS_CONFIGURATION: u8 = 0x22; +pub const CS_STRING: u8 = 0x23; +pub const CS_INTERFACE: u8 = 0x24; +pub const CS_ENDPOINT: u8 = 0x25; + +// Descriptor Subtype +pub const AC_DESCRIPTOR_UNDEFINED: u8 = 0x00; +pub const HEADER_SUBTYPE: u8 = 0x01; +pub const INPUT_TERMINAL: u8 = 0x02; +pub const OUTPUT_TERMINAL: u8 = 0x03; +pub const MIXER_UNIT: u8 = 0x04; +pub const SELECTOR_UNIT: u8 = 0x05; +pub const FEATURE_UNIT: u8 = 0x06; +pub const PROCESSING_UNIT: u8 = 0x07; +pub const EXTENSION_UNIT: u8 = 0x08; + +// Audio Class-Specific AS Interface Descriptor Subtypes +pub const AS_DESCRIPTOR_UNDEFINED: u8 = 0x00; +pub const AS_GENERAL: u8 = 0x01; +pub const FORMAT_TYPE: u8 = 0x02; +pub const FORMAT_SPECIFIC: u8 = 0x03; + +// Processing Unit Process Types +pub const PROCESS_UNDEFINED: u16 = 0x00; +pub const UP_DOWNMIX_PROCESS: u16 = 0x01; +pub const DOLBY_PROLOGIC_PROCESS: u16 = 0x02; +pub const DDD_STEREO_EXTENDER_PROCESS: u16 = 0x03; +pub const REVERBERATION_PROCESS: u16 = 0x04; +pub const CHORUS_PROCESS: u16 = 0x05; +pub const DYN_RANGE_COMP_PROCESS: u16 = 0x06; + +// Audio Class-Specific Endpoint Descriptor Subtypes +pub const EP_DESCRIPTOR_UNDEFINED: u8 = 0x00; +pub const EP_GENERAL: u8 = 0x01; + +// Audio Class-Specific Request Codes +pub const REQUEST_CODE_UNDEFINED: u8 = 0x00; +pub const SET_CUR: u8 = 0x01; +pub const GET_CUR: u8 = 0x81; +pub const SET_MIN: u8 = 0x02; +pub const GET_MIN: u8 = 0x82; +pub const SET_MAX: u8 = 0x03; +pub const GET_MAX: u8 = 0x83; +pub const SET_RES: u8 = 0x04; +pub const GET_RES: u8 = 0x84; +pub const SET_MEM: u8 = 0x05; +pub const GET_MEM: u8 = 0x85; +pub const GET_STAT: u8 = 0xFF; + +// Terminal Control Selectors +pub const TE_CONTROL_UNDEFINED: u8 = 0x00; +pub const COPY_PROTECT_CONTROL: u8 = 0x01; + +// Feature Unit Control Selectors +pub const FU_CONTROL_UNDEFINED: u8 = 0x00; +pub const MUTE_CONTROL: u8 = 0x01; +pub const VOLUME_CONTROL: u8 = 0x02; +pub const BASS_CONTROL: u8 = 0x03; +pub const MID_CONTROL: u8 = 0x04; +pub const TREBLE_CONTROL: u8 = 0x05; +pub const GRAPHIC_EQUALIZER_CONTROL: u8 = 0x06; +pub const AUTOMATIC_GAIN_CONTROL: u8 = 0x07; +pub const DELAY_CONTROL: u8 = 0x08; +pub const BASS_BOOST_CONTROL: u8 = 0x09; +pub const LOUDNESS_CONTROL: u8 = 0x0A; + +// Up/Down-mix Processing Unit Control Selectors +pub const UD_CONTROL_UNDEFINED: u8 = 0x00; +pub const UD_ENABLE_CONTROL: u8 = 0x01; +pub const UD_MODE_SELECT_CONTROL: u8 = 0x02; + +// Dolby Prologic Processing Unit Control Selectors +pub const DP_CONTROL_UNDEFINED: u8 = 0x00; +pub const DP_ENABLE_CONTROL: u8 = 0x01; +pub const DP_MODE_SELECT_CONTROL: u8 = 0x2; + +// 3D Stereo Extender Processing Unit Control Selectors +pub const DDD_CONTROL_UNDEFINED: u8 = 0x00; +pub const DDD_ENABLE_CONTROL: u8 = 0x01; +pub const DDD_SPACIOUSNESS_CONTROL: u8 = 0x03; + +// Reverberation Processing Unit Control Selectors +pub const RV_CONTROL_UNDEFINED: u8 = 0x00; +pub const RV_ENABLE_CONTROL: u8 = 0x01; +pub const REVERB_LEVEL_CONTROL: u8 = 0x02; +pub const REVERB_TIME_CONTROL: u8 = 0x03; +pub const REVERB_FEEDBACK_CONTROL: u8 = 0x04; + +// Chorus Processing Unit Control Selectors +pub const CH_CONTROL_UNDEFINED: u8 = 0x00; +pub const CH_ENABLE_CONTROL: u8 = 0x01; +pub const CHORUS_LEVEL_CONTROL: u8 = 0x02; +pub const CHORUS_RATE_CONTROL: u8 = 0x03; +pub const CHORUS_DEPTH_CONTROL: u8 = 0x04; + +// Dynamic Range Compressor Processing Unit Control Selectors +pub const DR_CONTROL_UNDEFINED: u8 = 0x00; +pub const DR_ENABLE_CONTROL: u8 = 0x01; +pub const COMPRESSION_RATE_CONTROL: u8 = 0x02; +pub const MAXAMPL_CONTROL: u8 = 0x03; +pub const THRESHOLD_CONTROL: u8 = 0x04; +pub const ATTACK_TIME: u8 = 0x05; +pub const RELEASE_TIME: u8 = 0x06; + +// Extension Unit Control Selectors +pub const XU_CONTROL_UNDEFINED: u16 = 0x00; +pub const XU_ENABLE_CONTROL: u16 = 0x01; + +// Endpoint Control Selectors +pub const EP_CONTROL_UNDEFINED: u8 = 0x00; +pub const SAMPLING_FREQ_CONTROL: u8 = 0x01; +pub const PITCH_CONTROL: u8 = 0x02; + +// Format Type Codes +pub const FORMAT_TYPE_UNDEFINED: u8 = 0x00; +pub const FORMAT_TYPE_I: u8 = 0x01; + +// Audio Data Format Type I Codes +pub const TYPE_I_UNDEFINED: u16 = 0x0000; +pub const PCM: u16 = 0x0001; +pub const PCM8: u16 = 0x0002; +pub const IEEE_FLOAT: u16 = 0x0003; +pub const ALAW: u16 = 0x0004; +pub const MULAW: u16 = 0x0005; diff --git a/embassy-usb/src/class/uac1/mod.rs b/embassy-usb/src/class/uac1/mod.rs new file mode 100644 index 000000000..3d5f4e524 --- /dev/null +++ b/embassy-usb/src/class/uac1/mod.rs @@ -0,0 +1,134 @@ +//! USB Audio Class 1.0 implementations for different applications. +//! +//! Contains: +//! - The `speaker` class with a single audio streaming interface (host to device) + +pub mod speaker; + +mod class_codes; +mod terminal_type; + +/// The maximum supported audio channel index (corresponds to `Top`). +/// FIXME: Use `core::mem::variant_count(...)` when stabilized. +const MAX_AUDIO_CHANNEL_INDEX: usize = 12; + +/// The maximum number of supported audio channels. +/// +/// Includes all twelve channels from `Channel`, plus the Master channel. +const MAX_AUDIO_CHANNEL_COUNT: usize = MAX_AUDIO_CHANNEL_INDEX + 1; + +/// USB Audio Channel +#[derive(Debug, Clone, Copy, PartialEq)] +#[allow(missing_docs)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +pub enum Channel { + LeftFront, + RightFront, + CenterFront, + Lfe, + LeftSurround, + RightSurround, + LeftOfCenter, + RightOfCenter, + Surround, + SideLeft, + SideRight, + Top, +} + +impl Channel { + /// Map a `Channel` to its corresponding USB Audio `ChannelConfig`. + fn get_channel_config(&self) -> ChannelConfig { + match self { + Channel::LeftFront => ChannelConfig::LeftFront, + Channel::RightFront => ChannelConfig::RightFront, + Channel::CenterFront => ChannelConfig::CenterFront, + Channel::Lfe => ChannelConfig::Lfe, + Channel::LeftSurround => ChannelConfig::LeftSurround, + Channel::RightSurround => ChannelConfig::RightSurround, + Channel::LeftOfCenter => ChannelConfig::LeftOfCenter, + Channel::RightOfCenter => ChannelConfig::RightOfCenter, + Channel::Surround => ChannelConfig::Surround, + Channel::SideLeft => ChannelConfig::SideLeft, + Channel::SideRight => ChannelConfig::SideRight, + Channel::Top => ChannelConfig::Top, + } + } +} + +/// USB Audio Channel configuration +#[repr(u16)] +#[non_exhaustive] +// #[derive(Copy, Clone, Eq, PartialEq, Debug)] +enum ChannelConfig { + None = 0x0000, + LeftFront = 0x0001, + RightFront = 0x0002, + CenterFront = 0x0004, + Lfe = 0x0008, + LeftSurround = 0x0010, + RightSurround = 0x0020, + LeftOfCenter = 0x0040, + RightOfCenter = 0x0080, + Surround = 0x0100, + SideLeft = 0x0200, + SideRight = 0x0400, + Top = 0x0800, +} + +impl From for u16 { + fn from(t: ChannelConfig) -> u16 { + t as u16 + } +} + +/// Feedback period adjustment `bRefresh` [UAC 3.7.2.2] +/// +/// From the specification: "A new Ff value is available every 2^(10 – P) frames with P ranging from 1 to 9. The +/// bRefresh field of the synch standard endpoint descriptor is used to report the exponent (10-P) to the Host." +/// +/// This means: +/// - 512 ms (2^9 frames) to 2 ms (2^1 frames) for USB full-speed +/// - 64 ms (2^9 microframes) to 0.25 ms (2^1 microframes) for USB high-speed +#[repr(u8)] +#[allow(missing_docs)] +#[derive(Clone, Copy)] +pub enum FeedbackRefresh { + Period2Frames = 1, + Period4Frames = 2, + Period8Frames = 3, + Period16Frames = 4, + Period32Frames = 5, + Period64Frames = 6, + Period128Frames = 7, + Period256Frames = 8, + Period512Frames = 9, +} + +impl FeedbackRefresh { + /// Gets the number of frames, after which a new feedback frame is returned. + pub const fn frame_count(&self) -> usize { + 1 << (*self as usize) + } +} + +/// Audio sample width. +/// +/// Stored in number of bytes per sample. +#[repr(u8)] +#[derive(Clone, Copy)] +pub enum SampleWidth { + /// 16 bit audio + Width2Byte = 2, + /// 24 bit audio + Width3Byte = 3, + /// 32 bit audio + Width4Byte = 4, +} + +impl SampleWidth { + /// Get the audio sample resolution in number of bit. + pub const fn in_bit(self) -> usize { + 8 * self as usize + } +} diff --git a/embassy-usb/src/class/uac1/speaker.rs b/embassy-usb/src/class/uac1/speaker.rs new file mode 100644 index 000000000..96456d94a --- /dev/null +++ b/embassy-usb/src/class/uac1/speaker.rs @@ -0,0 +1,778 @@ +//! USB Audio Class 1.0 - Speaker device +//! +//! Provides a class with a single audio streaming interface (host to device), +//! that advertises itself as a speaker. Includes explicit sample rate feedback. +//! +//! Various aspects of the audio stream can be configured, for example: +//! - sample rate +//! - sample resolution +//! - audio channel count and assignment +//! +//! The class provides volume and mute controls for each channel. + +use core::cell::{Cell, RefCell}; +use core::future::poll_fn; +use core::marker::PhantomData; +use core::sync::atomic::{AtomicBool, AtomicU32, Ordering}; +use core::task::Poll; + +use embassy_sync::blocking_mutex::CriticalSectionMutex; +use embassy_sync::waitqueue::WakerRegistration; +use heapless::Vec; + +use super::class_codes::*; +use super::terminal_type::TerminalType; +use super::{Channel, ChannelConfig, FeedbackRefresh, SampleWidth, MAX_AUDIO_CHANNEL_COUNT, MAX_AUDIO_CHANNEL_INDEX}; +use crate::control::{self, InResponse, OutResponse, Recipient, Request, RequestType}; +use crate::descriptor::{SynchronizationType, UsageType}; +use crate::driver::{Driver, Endpoint, EndpointError, EndpointIn, EndpointOut, EndpointType}; +use crate::types::InterfaceNumber; +use crate::{Builder, Handler}; + +/// Maximum allowed sampling rate (3 bytes) in Hz. +const MAX_SAMPLE_RATE_HZ: u32 = 0x7FFFFF; + +/// Arbitrary unique identifier for the input unit. +const INPUT_UNIT_ID: u8 = 0x01; + +/// Arbitrary unique identifier for the feature unit. +const FEATURE_UNIT_ID: u8 = 0x02; + +/// Arbitrary unique identifier for the output unit. +const OUTPUT_UNIT_ID: u8 = 0x03; + +// Volume settings go from -25600 to 0, in steps of 256. +// Therefore, the volume settings are 8q8 values in units of dB. +const VOLUME_STEPS_PER_DB: i16 = 256; +const MIN_VOLUME_DB: i16 = -100; +const MAX_VOLUME_DB: i16 = 0; + +// Maximum number of supported discrete sample rates. +const MAX_SAMPLE_RATE_COUNT: usize = 10; + +/// The volume of an audio channel. +#[derive(Debug, Clone, Copy)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +pub enum Volume { + /// The channel is muted. + Muted, + /// The channel volume in dB. Ranges from `MIN_VOLUME_DB` (quietest) to `MAX_VOLUME_DB` (loudest). + DeciBel(f32), +} + +/// Internal state for the USB Audio Class. +pub struct State<'d> { + control: Option>, + shared: SharedControl<'d>, +} + +impl<'d> Default for State<'d> { + fn default() -> Self { + Self::new() + } +} + +impl<'d> State<'d> { + /// Create a new `State`. + pub fn new() -> Self { + Self { + control: None, + shared: SharedControl::default(), + } + } +} + +/// Implementation of the USB audio class 1.0. +pub struct Speaker<'d, D: Driver<'d>> { + phantom: PhantomData<&'d D>, +} + +impl<'d, D: Driver<'d>> Speaker<'d, D> { + /// Creates a new [`Speaker`] device, split into a stream, feedback, and a control change notifier. + /// + /// The packet size should be chosen, based on the expected transfer size of samples per (micro)frame. + /// For example, a stereo stream at 32 bit resolution and 48 kHz sample rate yields packets of 384 byte for + /// full-speed USB (1 ms frame interval) or 48 byte for high-speed USB (125 us microframe interval). + /// When using feedback, the packet size varies and thus, the `max_packet_size` should be increased (e.g. to double). + /// + /// # Arguments + /// + /// * `builder` - The builder for the class. + /// * `state` - The internal state of the class. + /// * `max_packet_size` - The maximum packet size per (micro)frame. + /// * `resolution` - The audio sample resolution. + /// * `sample_rates_hz` - The supported sample rates in Hz. + /// * `channels` - The advertised audio channels (up to 12). Entries must be unique, or this function panics. + /// * `feedback_refresh_period` - The refresh period for the feedback value. + pub fn new( + builder: &mut Builder<'d, D>, + state: &'d mut State<'d>, + max_packet_size: u16, + resolution: SampleWidth, + sample_rates_hz: &[u32], + channels: &'d [Channel], + feedback_refresh_period: FeedbackRefresh, + ) -> (Stream<'d, D>, Feedback<'d, D>, ControlMonitor<'d>) { + // The class and subclass fields of the IAD aren't required to match the class and subclass fields of + // the interfaces in the interface collection that the IAD describes. Microsoft recommends that + // the first interface of the collection has class and subclass fields that match the class and + // subclass fields of the IAD. + let mut func = builder.function(USB_AUDIO_CLASS, USB_AUDIOCONTROL_SUBCLASS, PROTOCOL_NONE); + + // Audio control interface (mandatory) [UAC 4.3.1] + let mut interface = func.interface(); + let control_interface = interface.interface_number().into(); + let streaming_interface = u8::from(control_interface) + 1; + let mut alt = interface.alt_setting(USB_AUDIO_CLASS, USB_AUDIOCONTROL_SUBCLASS, PROTOCOL_NONE, None); + + // Terminal topology: + // Input terminal (receives audio stream) -> Feature Unit (mute and volume) -> Output terminal (e.g. towards speaker) + + // ======================================= + // Input Terminal Descriptor [UAC 3.3.2.1] + // Audio input + let terminal_type: u16 = TerminalType::UsbStreaming.into(); + + // Assemble channel configuration field + let mut channel_config: u16 = ChannelConfig::None.into(); + for channel in channels { + let channel: u16 = channel.get_channel_config().into(); + + if channel_config & channel != 0 { + panic!("Invalid channel config, duplicate channel {}.", channel); + } + channel_config |= channel; + } + + let input_terminal_descriptor = [ + INPUT_TERMINAL, // bDescriptorSubtype + INPUT_UNIT_ID, // bTerminalID + terminal_type as u8, + (terminal_type >> 8) as u8, // wTerminalType + 0x00, // bAssocTerminal (none) + channels.len() as u8, // bNrChannels + channel_config as u8, + (channel_config >> 8) as u8, // wChannelConfig + 0x00, // iChannelNames (none) + 0x00, // iTerminal (none) + ]; + + // ======================================== + // Output Terminal Descriptor [UAC 4.3.2.2] + // Speaker output + let terminal_type: u16 = TerminalType::OutSpeaker.into(); + let output_terminal_descriptor = [ + OUTPUT_TERMINAL, // bDescriptorSubtype + OUTPUT_UNIT_ID, // bTerminalID + terminal_type as u8, + (terminal_type >> 8) as u8, // wTerminalType + 0x00, // bAssocTerminal (none) + FEATURE_UNIT_ID, // bSourceID (the feature unit) + 0x00, // iTerminal (none) + ]; + + // ===================================== + // Feature Unit Descriptor [UAC 4.3.2.5] + // Mute and volume control + let controls = MUTE_CONTROL | VOLUME_CONTROL; + + const FEATURE_UNIT_DESCRIPTOR_SIZE: usize = 5; + let mut feature_unit_descriptor: Vec = + Vec::from_slice(&[ + FEATURE_UNIT, // bDescriptorSubtype (Feature Unit) + FEATURE_UNIT_ID, // bUnitID + INPUT_UNIT_ID, // bSourceID + 1, // bControlSize (one byte per control) + FU_CONTROL_UNDEFINED, // Master controls (disabled, use only per-channel control) + ]) + .unwrap(); + + // Add per-channel controls + for _channel in channels { + feature_unit_descriptor.push(controls).unwrap(); + } + feature_unit_descriptor.push(0x00).unwrap(); // iFeature (none) + + // =============================================== + // Format desciptor [UAC 4.5.3] + // Used later, for operational streaming interface + let mut format_descriptor: Vec = Vec::from_slice(&[ + FORMAT_TYPE, // bDescriptorSubtype + FORMAT_TYPE_I, // bFormatType + channels.len() as u8, // bNrChannels + resolution as u8, // bSubframeSize + resolution.in_bit() as u8, // bBitResolution + ]) + .unwrap(); + + format_descriptor.push(sample_rates_hz.len() as u8).unwrap(); + + for sample_rate_hz in sample_rates_hz { + assert!(*sample_rate_hz <= MAX_SAMPLE_RATE_HZ); + format_descriptor.push((sample_rate_hz & 0xFF) as u8).unwrap(); + format_descriptor.push(((sample_rate_hz >> 8) & 0xFF) as u8).unwrap(); + format_descriptor.push(((sample_rate_hz >> 16) & 0xFF) as u8).unwrap(); + } + + // ================================================== + // Class-specific AC Interface Descriptor [UAC 4.3.2] + const DESCRIPTOR_HEADER_SIZE: usize = 2; + const INTERFACE_DESCRIPTOR_SIZE: usize = 7; + + let mut total_descriptor_length = 0; + + for size in [ + INTERFACE_DESCRIPTOR_SIZE, + input_terminal_descriptor.len(), + feature_unit_descriptor.len(), + output_terminal_descriptor.len(), + ] { + total_descriptor_length += size + DESCRIPTOR_HEADER_SIZE; + } + + let interface_descriptor: [u8; INTERFACE_DESCRIPTOR_SIZE] = [ + HEADER_SUBTYPE, // bDescriptorSubtype (Header) + ADC_VERSION as u8, + (ADC_VERSION >> 8) as u8, // bcdADC + total_descriptor_length as u8, + (total_descriptor_length >> 8) as u8, // wTotalLength + 0x01, // bInCollection (1 streaming interface) + streaming_interface, // baInterfaceNr + ]; + + alt.descriptor(CS_INTERFACE, &interface_descriptor); + alt.descriptor(CS_INTERFACE, &input_terminal_descriptor); + alt.descriptor(CS_INTERFACE, &feature_unit_descriptor); + alt.descriptor(CS_INTERFACE, &output_terminal_descriptor); + + // ===================================================== + // Audio streaming interface, zero-bandwidth [UAC 4.5.1] + let mut interface = func.interface(); + let alt = interface.alt_setting(USB_AUDIO_CLASS, USB_AUDIOSTREAMING_SUBCLASS, PROTOCOL_NONE, None); + drop(alt); + + // ================================================== + // Audio streaming interface, operational [UAC 4.5.1] + let mut alt = interface.alt_setting(USB_AUDIO_CLASS, USB_AUDIOSTREAMING_SUBCLASS, PROTOCOL_NONE, None); + + alt.descriptor( + CS_INTERFACE, + &[ + AS_GENERAL, // bDescriptorSubtype + INPUT_UNIT_ID, // bTerminalLink + 0x00, // bDelay (none) + PCM as u8, + (PCM >> 8) as u8, // wFormatTag (PCM format) + ], + ); + + alt.descriptor(CS_INTERFACE, &format_descriptor); + + let streaming_endpoint = alt.alloc_endpoint_out(EndpointType::Isochronous, max_packet_size, 1); + let feedback_endpoint = alt.alloc_endpoint_in( + EndpointType::Isochronous, + 4, // Feedback packets are 24 bit (10.14 format). + 1, + ); + + // Write the descriptor for the streaming endpoint, after knowing the address of the feedback endpoint. + alt.endpoint_descriptor( + streaming_endpoint.info(), + SynchronizationType::Asynchronous, + UsageType::DataEndpoint, + &[ + 0x00, // bRefresh (0) + feedback_endpoint.info().addr.into(), // bSynchAddress (the feedback endpoint) + ], + ); + + alt.descriptor( + CS_ENDPOINT, + &[ + AS_GENERAL, // bDescriptorSubtype (General) + SAMPLING_FREQ_CONTROL, // bmAttributes (support sampling frequency control) + 0x02, // bLockDelayUnits (PCM) + 0x0000 as u8, + (0x0000 >> 8) as u8, // wLockDelay (0) + ], + ); + + // Write the feedback endpoint descriptor after the streaming endpoint descriptor + // This is demanded by the USB audio class specification. + alt.endpoint_descriptor( + feedback_endpoint.info(), + SynchronizationType::NoSynchronization, + UsageType::FeedbackEndpoint, + &[ + feedback_refresh_period as u8, // bRefresh + 0x00, // bSynchAddress (none) + ], + ); + + // Free up the builder. + drop(func); + + // Store channel information + state.shared.channels = channels; + + state.control = Some(Control { + shared: &state.shared, + streaming_endpoint_address: streaming_endpoint.info().addr.into(), + control_interface_number: control_interface, + }); + + builder.handler(state.control.as_mut().unwrap()); + + let control = &state.shared; + + ( + Stream { streaming_endpoint }, + Feedback { feedback_endpoint }, + ControlMonitor { shared: control }, + ) + } +} + +/// Audio settings for the feature unit. +/// +/// Contains volume and mute control. +#[derive(Clone, Copy, Debug)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +pub struct AudioSettings { + /// Channel mute states. + muted: [bool; MAX_AUDIO_CHANNEL_COUNT], + /// Channel volume levels in 8.8 format (in dB). + volume_8q8_db: [i16; MAX_AUDIO_CHANNEL_COUNT], +} + +impl Default for AudioSettings { + fn default() -> Self { + AudioSettings { + muted: [true; MAX_AUDIO_CHANNEL_COUNT], + volume_8q8_db: [MAX_VOLUME_DB * VOLUME_STEPS_PER_DB; MAX_AUDIO_CHANNEL_COUNT], + } + } +} + +struct Control<'d> { + control_interface_number: InterfaceNumber, + streaming_endpoint_address: u8, + shared: &'d SharedControl<'d>, +} + +/// Shared data between [`Control`] and the [`Speaker`] class. +struct SharedControl<'d> { + /// The collection of audio settings (volumes, mute states). + audio_settings: CriticalSectionMutex>, + + /// Channel assignments. + channels: &'d [Channel], + + /// The audio sample rate in Hz. + sample_rate_hz: AtomicU32, + + // Notification mechanism. + waker: RefCell, + changed: AtomicBool, +} + +impl<'d> Default for SharedControl<'d> { + fn default() -> Self { + SharedControl { + audio_settings: CriticalSectionMutex::new(Cell::new(AudioSettings::default())), + channels: &[], + sample_rate_hz: AtomicU32::new(0), + waker: RefCell::new(WakerRegistration::new()), + changed: AtomicBool::new(false), + } + } +} + +impl<'d> SharedControl<'d> { + async fn changed(&self) { + poll_fn(|context| { + if self.changed.load(Ordering::Relaxed) { + self.changed.store(false, Ordering::Relaxed); + Poll::Ready(()) + } else { + self.waker.borrow_mut().register(context.waker()); + Poll::Pending + } + }) + .await; + } +} + +/// Used for reading audio frames. +pub struct Stream<'d, D: Driver<'d>> { + streaming_endpoint: D::EndpointOut, +} + +impl<'d, D: Driver<'d>> Stream<'d, D> { + /// Reads a single packet from the OUT endpoint + pub async fn read_packet(&mut self, data: &mut [u8]) -> Result { + self.streaming_endpoint.read(data).await + } + + /// Waits for the USB host to enable this interface + pub async fn wait_connection(&mut self) { + self.streaming_endpoint.wait_enabled().await; + } +} + +/// Used for writing sample rate information over the feedback endpoint. +pub struct Feedback<'d, D: Driver<'d>> { + feedback_endpoint: D::EndpointIn, +} + +impl<'d, D: Driver<'d>> Feedback<'d, D> { + /// Writes a single packet into the IN endpoint. + pub async fn write_packet(&mut self, data: &[u8]) -> Result<(), EndpointError> { + self.feedback_endpoint.write(data).await + } + + /// Waits for the USB host to enable this interface. + pub async fn wait_connection(&mut self) { + self.feedback_endpoint.wait_enabled().await; + } +} + +/// Control status change monitor +/// +/// Await [`ControlMonitor::changed`] for being notified of configuration changes. Afterwards, the updated +/// configuration settings can be read with [`ControlMonitor::volume`] and [`ControlMonitor::sample_rate_hz`]. +pub struct ControlMonitor<'d> { + shared: &'d SharedControl<'d>, +} + +impl<'d> ControlMonitor<'d> { + fn audio_settings(&self) -> AudioSettings { + let audio_settings = self.shared.audio_settings.lock(|x| x.get()); + + audio_settings + } + + fn get_logical_channel(&self, search_channel: Channel) -> Option { + let index = self.shared.channels.iter().position(|&c| c == search_channel)?; + + // The logical channels start at one (zero is the master channel). + Some(index + 1) + } + + /// Get the volume of a selected channel. + pub fn volume(&self, channel: Channel) -> Option { + let channel_index = self.get_logical_channel(channel)?; + + if self.audio_settings().muted[channel_index] { + return Some(Volume::Muted); + } + + Some(Volume::DeciBel( + (self.audio_settings().volume_8q8_db[channel_index] as f32) / 256.0f32, + )) + } + + /// Get the streaming endpoint's sample rate in Hz. + pub fn sample_rate_hz(&self) -> u32 { + self.shared.sample_rate_hz.load(Ordering::Relaxed) + } + + /// Return a future for when the control settings change. + pub async fn changed(&self) { + self.shared.changed().await; + } +} + +impl<'d> Control<'d> { + fn changed(&mut self) { + self.shared.changed.store(true, Ordering::Relaxed); + self.shared.waker.borrow_mut().wake(); + } + + fn interface_set_mute_state( + &mut self, + audio_settings: &mut AudioSettings, + channel_index: u8, + data: &[u8], + ) -> OutResponse { + let mute_state = data[0] != 0; + + match channel_index as usize { + ..=MAX_AUDIO_CHANNEL_INDEX => { + audio_settings.muted[channel_index as usize] = mute_state; + } + _ => { + debug!("Failed to set channel {} mute state: {}", channel_index, mute_state); + return OutResponse::Rejected; + } + } + + debug!("Set channel {} mute state: {}", channel_index, mute_state); + OutResponse::Accepted + } + + fn interface_set_volume( + &mut self, + audio_settings: &mut AudioSettings, + channel_index: u8, + data: &[u8], + ) -> OutResponse { + let volume = i16::from_ne_bytes(data[..2].try_into().expect("Failed to read volume.")); + + match channel_index as usize { + ..=MAX_AUDIO_CHANNEL_INDEX => { + audio_settings.volume_8q8_db[channel_index as usize] = volume; + } + _ => { + debug!("Failed to set channel {} volume: {}", channel_index, volume); + return OutResponse::Rejected; + } + } + + debug!("Set channel {} volume: {}", channel_index, volume); + OutResponse::Accepted + } + + fn interface_set_request(&mut self, req: control::Request, data: &[u8]) -> Option { + let interface_number = req.index as u8; + let entity_index = (req.index >> 8) as u8; + let channel_index = req.value as u8; + let control_unit = (req.value >> 8) as u8; + + if interface_number != self.control_interface_number.into() { + debug!("Unhandled interface set request for interface {}", interface_number); + return None; + } + + if entity_index != FEATURE_UNIT_ID { + debug!("Unsupported interface set request for entity {}", entity_index); + return Some(OutResponse::Rejected); + } + + if req.request != SET_CUR { + debug!("Unsupported interface set request type {}", req.request); + return Some(OutResponse::Rejected); + } + + let mut audio_settings = self.shared.audio_settings.lock(|x| x.get()); + let response = match control_unit { + MUTE_CONTROL => self.interface_set_mute_state(&mut audio_settings, channel_index, data), + VOLUME_CONTROL => self.interface_set_volume(&mut audio_settings, channel_index, data), + _ => OutResponse::Rejected, + }; + + if response == OutResponse::Rejected { + return Some(response); + } + + // Store updated settings + self.shared.audio_settings.lock(|x| x.set(audio_settings)); + + self.changed(); + + Some(OutResponse::Accepted) + } + + fn endpoint_set_request(&mut self, req: control::Request, data: &[u8]) -> Option { + let control_selector = (req.value >> 8) as u8; + let endpoint_address = req.index as u8; + + if endpoint_address != self.streaming_endpoint_address { + debug!( + "Unhandled endpoint set request for endpoint {} and control {} with data {}", + endpoint_address, control_selector, data + ); + return None; + } + + if control_selector != SAMPLING_FREQ_CONTROL { + debug!( + "Unsupported endpoint set request for control selector {}", + control_selector + ); + return Some(OutResponse::Rejected); + } + + let sample_rate_hz: u32 = (data[0] as u32) | (data[1] as u32) << 8 | (data[2] as u32) << 16; + self.shared.sample_rate_hz.store(sample_rate_hz, Ordering::Relaxed); + + debug!("Set endpoint {} sample rate to {} Hz", endpoint_address, sample_rate_hz); + + self.changed(); + + Some(OutResponse::Accepted) + } + + fn interface_get_request<'r>(&'r mut self, req: Request, buf: &'r mut [u8]) -> Option> { + let interface_number = req.index as u8; + let entity_index = (req.index >> 8) as u8; + let channel_index = req.value as u8; + let control_unit = (req.value >> 8) as u8; + + if interface_number != self.control_interface_number.into() { + debug!("Unhandled interface get request for interface {}.", interface_number); + return None; + } + + if entity_index != FEATURE_UNIT_ID { + // Only this function unit can be handled at the moment. + debug!("Unsupported interface get request for entity {}.", entity_index); + return Some(InResponse::Rejected); + } + + let audio_settings = self.shared.audio_settings.lock(|x| x.get()); + + match req.request { + GET_CUR => match control_unit { + VOLUME_CONTROL => { + let volume: i16; + + match channel_index as usize { + ..=MAX_AUDIO_CHANNEL_INDEX => volume = audio_settings.volume_8q8_db[channel_index as usize], + _ => return Some(InResponse::Rejected), + } + + buf[0] = volume as u8; + buf[1] = (volume >> 8) as u8; + + debug!("Got channel {} volume: {}.", channel_index, volume); + return Some(InResponse::Accepted(&buf[..2])); + } + MUTE_CONTROL => { + let mute_state: bool; + + match channel_index as usize { + ..=MAX_AUDIO_CHANNEL_INDEX => mute_state = audio_settings.muted[channel_index as usize], + _ => return Some(InResponse::Rejected), + } + + buf[0] = mute_state.into(); + debug!("Got channel {} mute state: {}.", channel_index, mute_state); + return Some(InResponse::Accepted(&buf[..1])); + } + _ => return Some(InResponse::Rejected), + }, + GET_MIN => match control_unit { + VOLUME_CONTROL => { + let min_volume = MIN_VOLUME_DB * VOLUME_STEPS_PER_DB; + buf[0] = min_volume as u8; + buf[1] = (min_volume >> 8) as u8; + return Some(InResponse::Accepted(&buf[..2])); + } + _ => return Some(InResponse::Rejected), + }, + GET_MAX => match control_unit { + VOLUME_CONTROL => { + let max_volume = MAX_VOLUME_DB * VOLUME_STEPS_PER_DB; + buf[0] = max_volume as u8; + buf[1] = (max_volume >> 8) as u8; + return Some(InResponse::Accepted(&buf[..2])); + } + _ => return Some(InResponse::Rejected), + }, + GET_RES => match control_unit { + VOLUME_CONTROL => { + buf[0] = VOLUME_STEPS_PER_DB as u8; + buf[1] = (VOLUME_STEPS_PER_DB >> 8) as u8; + return Some(InResponse::Accepted(&buf[..2])); + } + _ => return Some(InResponse::Rejected), + }, + _ => return Some(InResponse::Rejected), + } + } + + fn endpoint_get_request<'r>(&'r mut self, req: Request, buf: &'r mut [u8]) -> Option> { + let control_selector = (req.value >> 8) as u8; + let endpoint_address = req.index as u8; + + if endpoint_address != self.streaming_endpoint_address { + debug!("Unhandled endpoint get request for endpoint {}.", endpoint_address); + return None; + } + + if control_selector != SAMPLING_FREQ_CONTROL as u8 { + debug!( + "Unsupported endpoint get request for control selector {}.", + control_selector + ); + return Some(InResponse::Rejected); + } + + let sample_rate_hz = self.shared.sample_rate_hz.load(Ordering::Relaxed); + + buf[0] = (sample_rate_hz & 0xFF) as u8; + buf[1] = ((sample_rate_hz >> 8) & 0xFF) as u8; + buf[2] = ((sample_rate_hz >> 16) & 0xFF) as u8; + + Some(InResponse::Accepted(&buf[..3])) + } +} + +impl<'d> Handler for Control<'d> { + /// Called when the USB device has been enabled or disabled. + fn enabled(&mut self, enabled: bool) { + debug!("USB device enabled: {}", enabled); + } + + /// Called when the host has set the address of the device to `addr`. + fn addressed(&mut self, addr: u8) { + debug!("Host set address to: {}", addr); + } + + /// Called when the host has enabled or disabled the configuration of the device. + fn configured(&mut self, configured: bool) { + debug!("USB device configured: {}", configured); + } + + /// Called when remote wakeup feature is enabled or disabled. + fn remote_wakeup_enabled(&mut self, enabled: bool) { + debug!("USB remote wakeup enabled: {}", enabled); + } + + /// Called when a "set alternate setting" control request is done on the interface. + fn set_alternate_setting(&mut self, iface: InterfaceNumber, alternate_setting: u8) { + debug!( + "USB set interface number {} to alt setting {}.", + iface, alternate_setting + ); + } + + /// Called after a USB reset after the bus reset sequence is complete. + fn reset(&mut self) { + let shared = self.shared; + shared.audio_settings.lock(|x| x.set(AudioSettings::default())); + + shared.changed.store(true, Ordering::Relaxed); + shared.waker.borrow_mut().wake(); + } + + /// Called when the bus has entered or exited the suspend state. + fn suspended(&mut self, suspended: bool) { + debug!("USB device suspended: {}", suspended); + } + + // Handle control set requests. + fn control_out(&mut self, req: control::Request, data: &[u8]) -> Option { + match req.request_type { + RequestType::Class => match req.recipient { + Recipient::Interface => self.interface_set_request(req, data), + Recipient::Endpoint => self.endpoint_set_request(req, data), + _ => Some(OutResponse::Rejected), + }, + _ => None, + } + } + + // Handle control get requests. + fn control_in<'a>(&'a mut self, req: Request, buf: &'a mut [u8]) -> Option> { + match req.request_type { + RequestType::Class => match req.recipient { + Recipient::Interface => self.interface_get_request(req, buf), + Recipient::Endpoint => self.endpoint_get_request(req, buf), + _ => None, + }, + _ => None, + } + } +} diff --git a/embassy-usb/src/class/uac1/terminal_type.rs b/embassy-usb/src/class/uac1/terminal_type.rs new file mode 100644 index 000000000..65474a714 --- /dev/null +++ b/embassy-usb/src/class/uac1/terminal_type.rs @@ -0,0 +1,50 @@ +//! USB Audio Terminal Types from Universal Serial Bus Device Class Definition +//! for Terminal Types, Release 1.0 + +/// USB Audio Terminal Types from "Universal Serial Bus Device Class Definition +/// for Terminal Types, Release 1.0" +#[repr(u16)] +#[non_exhaustive] +#[derive(Copy, Clone, Eq, PartialEq, Debug)] +#[allow(missing_docs)] +pub enum TerminalType { + // USB Terminal Types + UsbUndefined = 0x0100, + UsbStreaming = 0x0101, + UsbVendor = 0x01ff, + + // Input Terminal Types + InUndefined = 0x0200, + InMicrophone = 0x0201, + InDesktopMicrophone = 0x0202, + InPersonalMicrophone = 0x0203, + InOmniDirectionalMicrophone = 0x0204, + InMicrophoneArray = 0x0205, + InProcessingMicrophoneArray = 0x0206, + + // Output Terminal Types + OutUndefined = 0x0300, + OutSpeaker = 0x0301, + OutHeadphones = 0x0302, + OutHeadMountedDisplayAudio = 0x0303, + OutDesktopSpeaker = 0x0304, + OutRoomSpeaker = 0x0305, + OutCommunicationSpeaker = 0x0306, + OutLowFrequencyEffectsSpeaker = 0x0307, + + // External Terminal Types + ExtUndefined = 0x0600, + ExtAnalogConnector = 0x0601, + ExtDigitalAudioInterface = 0x0602, + ExtLineConnector = 0x0603, + ExtLegacyAudioConnector = 0x0604, + ExtSpdifConnector = 0x0605, + Ext1394DaStream = 0x0606, + Ext1394DvStreamSoundtrack = 0x0607, +} + +impl From for u16 { + fn from(t: TerminalType) -> u16 { + t as u16 + } +} From 0d299301efaa0843d3da09909f02e795eeffc035 Mon Sep 17 00:00:00 2001 From: elagil Date: Sat, 2 Nov 2024 20:01:20 +0100 Subject: [PATCH 241/361] feat(stm32f4): add usb audio example --- examples/stm32f4/Cargo.toml | 1 + examples/stm32f4/src/bin/usb_uac_speaker.rs | 375 ++++++++++++++++++++ 2 files changed, 376 insertions(+) create mode 100644 examples/stm32f4/src/bin/usb_uac_speaker.rs diff --git a/examples/stm32f4/Cargo.toml b/examples/stm32f4/Cargo.toml index 75e315e82..435b0b43c 100644 --- a/examples/stm32f4/Cargo.toml +++ b/examples/stm32f4/Cargo.toml @@ -27,6 +27,7 @@ embedded-io-async = { version = "0.6.1" } panic-probe = { version = "0.3", features = ["print-defmt"] } futures-util = { version = "0.3.30", default-features = false } heapless = { version = "0.8", default-features = false } +critical-section = "1.1" nb = "1.0.0" embedded-storage = "0.3.1" micromath = "2.0.0" diff --git a/examples/stm32f4/src/bin/usb_uac_speaker.rs b/examples/stm32f4/src/bin/usb_uac_speaker.rs new file mode 100644 index 000000000..77c693ace --- /dev/null +++ b/examples/stm32f4/src/bin/usb_uac_speaker.rs @@ -0,0 +1,375 @@ +#![no_std] +#![no_main] + +use core::cell::RefCell; + +use defmt::{panic, *}; +use embassy_executor::Spawner; +use embassy_stm32::time::Hertz; +use embassy_stm32::{bind_interrupts, interrupt, peripherals, timer, usb, Config}; +use embassy_sync::blocking_mutex::raw::{CriticalSectionRawMutex, NoopRawMutex}; +use embassy_sync::blocking_mutex::Mutex; +use embassy_sync::signal::Signal; +use embassy_sync::zerocopy_channel; +use embassy_usb::class::uac1; +use embassy_usb::class::uac1::speaker::{self, Speaker}; +use embassy_usb::driver::EndpointError; +use heapless::Vec; +use micromath::F32Ext; +use static_cell::StaticCell; +use {defmt_rtt as _, panic_probe as _}; + +bind_interrupts!(struct Irqs { + OTG_FS => usb::InterruptHandler; +}); + +static TIMER: Mutex>>> = + Mutex::new(RefCell::new(None)); + +// A counter signal that is written by the feedback timer, once every `FEEDBACK_REFRESH_PERIOD`. +// At that point, a feedback value is sent to the host. +pub static FEEDBACK_SIGNAL: Signal = Signal::new(); + +// Stereo input +pub const INPUT_CHANNEL_COUNT: usize = 2; + +// This example uses a fixed sample rate of 48 kHz. +pub const SAMPLE_RATE_HZ: u32 = 48_000; +pub const FEEDBACK_COUNTER_TICK_RATE: u32 = 42_000_000; + +// Use 32 bit samples, which allow for a lot of (software) volume adjustment without degradation of quality. +pub const SAMPLE_WIDTH: uac1::SampleWidth = uac1::SampleWidth::Width4Byte; +pub const SAMPLE_WIDTH_BIT: usize = SAMPLE_WIDTH.in_bit(); +pub const SAMPLE_SIZE: usize = SAMPLE_WIDTH as usize; +pub const SAMPLE_SIZE_PER_S: usize = (SAMPLE_RATE_HZ as usize) * INPUT_CHANNEL_COUNT * SAMPLE_SIZE; + +// Size of audio samples per 1 ms - for the full-speed USB frame period of 1 ms. +pub const USB_FRAME_SIZE: usize = SAMPLE_SIZE_PER_S.div_ceil(1000); + +// Select front left and right audio channels. +pub const AUDIO_CHANNELS: [uac1::Channel; INPUT_CHANNEL_COUNT] = [uac1::Channel::LeftFront, uac1::Channel::RightFront]; + +// Factor of two as a margin for feedback (this is an excessive amount) +pub const USB_MAX_PACKET_SIZE: usize = 2 * USB_FRAME_SIZE; +pub const USB_MAX_SAMPLE_COUNT: usize = USB_MAX_PACKET_SIZE / SAMPLE_SIZE; + +// The data type that is exchanged via the zero-copy channel (a sample vector). +pub type SampleBlock = Vec; + +// Feedback is provided in 10.14 format for full-speed endpoints. +pub const FEEDBACK_REFRESH_PERIOD: uac1::FeedbackRefresh = uac1::FeedbackRefresh::Period8Frames; +const FEEDBACK_SHIFT: usize = 14; + +const TICKS_PER_SAMPLE: f32 = (FEEDBACK_COUNTER_TICK_RATE as f32) / (SAMPLE_RATE_HZ as f32); + +struct Disconnected {} + +impl From for Disconnected { + fn from(val: EndpointError) -> Self { + match val { + EndpointError::BufferOverflow => panic!("Buffer overflow"), + EndpointError::Disabled => Disconnected {}, + } + } +} + +/// Sends feedback messages to the host. +async fn feedback_handler<'d, T: usb::Instance + 'd>( + feedback: &mut speaker::Feedback<'d, usb::Driver<'d, T>>, + feedback_factor: f32, +) -> Result<(), Disconnected> { + let mut packet: Vec = Vec::new(); + + // Collects the fractional component of the feedback value that is lost by rounding. + let mut rest = 0.0_f32; + + loop { + let counter = FEEDBACK_SIGNAL.wait().await; + + packet.clear(); + + let raw_value = counter as f32 * feedback_factor + rest; + let value = raw_value.round(); + rest = raw_value - value; + + let value = value as u32; + packet.push(value as u8).unwrap(); + packet.push((value >> 8) as u8).unwrap(); + packet.push((value >> 16) as u8).unwrap(); + + feedback.write_packet(&packet).await?; + } +} + +/// Handles streaming of audio data from the host. +async fn stream_handler<'d, T: usb::Instance + 'd>( + stream: &mut speaker::Stream<'d, usb::Driver<'d, T>>, + sender: &mut zerocopy_channel::Sender<'static, NoopRawMutex, SampleBlock>, +) -> Result<(), Disconnected> { + loop { + let mut usb_data = [0u8; USB_MAX_PACKET_SIZE]; + let data_size = stream.read_packet(&mut usb_data).await?; + + let word_count = data_size / SAMPLE_SIZE; + + if word_count * SAMPLE_SIZE == data_size { + // Obtain a buffer from the channel + let samples = sender.send().await; + samples.clear(); + + for w in 0..word_count { + let byte_offset = w * SAMPLE_SIZE; + let sample = u32::from_le_bytes(usb_data[byte_offset..byte_offset + SAMPLE_SIZE].try_into().unwrap()); + + // Fill the sample buffer with data. + samples.push(sample).unwrap(); + } + + sender.send_done(); + } else { + debug!("Invalid USB buffer size of {}, skipped.", data_size); + } + } +} + +/// Receives audio samples from the USB streaming task and can play them back. +#[embassy_executor::task] +async fn audio_receiver_task(mut usb_audio_receiver: zerocopy_channel::Receiver<'static, NoopRawMutex, SampleBlock>) { + loop { + let _samples = usb_audio_receiver.receive().await; + // Use the samples, for example play back via the SAI peripheral. + + // Notify the channel that the buffer is now ready to be reused + usb_audio_receiver.receive_done(); + } +} + +/// Receives audio samples from the host. +#[embassy_executor::task] +async fn usb_streaming_task( + mut stream: speaker::Stream<'static, usb::Driver<'static, peripherals::USB_OTG_FS>>, + mut sender: zerocopy_channel::Sender<'static, NoopRawMutex, SampleBlock>, +) { + loop { + stream.wait_connection().await; + _ = stream_handler(&mut stream, &mut sender).await; + } +} + +/// Sends sample rate feedback to the host. +/// +/// The `feedback_factor` scales the feedback timer's counter value so that the result is the number of samples that +/// this device played back or "consumed" during one SOF period (1 ms) - in 10.14 format. +/// +/// Ideally, the `feedback_factor` that is calculated below would be an integer for avoiding numerical errors. +/// This is achieved by having `TICKS_PER_SAMPLE` be a power of two. For audio applications at a sample rate of 48 kHz, +/// 24.576 MHz would be one such option. +/// +/// A good choice for the STM32F4, which also has to generate a 48 MHz clock from its HSE (e.g. running at 8 MHz) +/// for USB, is to clock the feedback timer from the MCLK output of the SAI peripheral. The SAI peripheral then uses an +/// external clock. In that case, wiring the MCLK output to the timer clock input is required. +/// +/// This simple example just uses the internal clocks for supplying the feedback timer, +/// and does not even set up a SAI peripheral. +#[embassy_executor::task] +async fn usb_feedback_task(mut feedback: speaker::Feedback<'static, usb::Driver<'static, peripherals::USB_OTG_FS>>) { + let feedback_factor = + ((1 << FEEDBACK_SHIFT) as f32 / TICKS_PER_SAMPLE) / FEEDBACK_REFRESH_PERIOD.frame_count() as f32; + + // Should be 2.3405714285714287... + info!("Using a feedback factor of {}.", feedback_factor); + + loop { + feedback.wait_connection().await; + _ = feedback_handler(&mut feedback, feedback_factor).await; + } +} + +#[embassy_executor::task] +async fn usb_task(mut usb_device: embassy_usb::UsbDevice<'static, usb::Driver<'static, peripherals::USB_OTG_FS>>) { + usb_device.run().await; +} + +/// Checks for changes on the control monitor of the class. +/// +/// In this case, monitor changes of volume or mute state. +#[embassy_executor::task] +async fn usb_control_task(control_monitor: speaker::ControlMonitor<'static>) { + loop { + control_monitor.changed().await; + + for channel in AUDIO_CHANNELS { + let volume = control_monitor.volume(channel).unwrap(); + info!("Volume changed to {} on channel {}.", volume, channel); + } + } +} + +/// Feedback value measurement and calculation +/// +/// Used for measuring/calculating the number of samples that were received from the host during the +/// `FEEDBACK_REFRESH_PERIOD`. +/// +/// Configured in this example with +/// - a refresh period of 8 ms, and +/// - a tick rate of 42 MHz. +/// +/// This gives an (ideal) counter value of 336.000 for every update of the `FEEDBACK_SIGNAL`. +#[interrupt] +fn TIM2() { + static mut LAST_TICKS: u32 = 0; + static mut FRAME_COUNT: usize = 0; + + critical_section::with(|cs| { + // Read timer counter. + let ticks = TIMER.borrow(cs).borrow().as_ref().unwrap().regs_gp32().cnt().read(); + + // Clear trigger interrupt flag. + TIMER + .borrow(cs) + .borrow_mut() + .as_mut() + .unwrap() + .regs_gp32() + .sr() + .modify(|r| r.set_tif(false)); + + // Count up frames and emit a signal, when the refresh period is reached (here, every 8 ms). + *FRAME_COUNT += 1; + if *FRAME_COUNT >= FEEDBACK_REFRESH_PERIOD.frame_count() { + *FRAME_COUNT = 0; + FEEDBACK_SIGNAL.signal(ticks.wrapping_sub(*LAST_TICKS)); + *LAST_TICKS = ticks; + } + }); +} + +// If you are trying this and your USB device doesn't connect, the most +// common issues are the RCC config and vbus_detection +// +// See https://embassy.dev/book/#_the_usb_examples_are_not_working_on_my_board_is_there_anything_else_i_need_to_configure +// for more information. +#[embassy_executor::main] +async fn main(spawner: Spawner) { + info!("Hello World!"); + + let mut config = Config::default(); + { + use embassy_stm32::rcc::*; + config.rcc.hse = Some(Hse { + freq: Hertz(8_000_000), + mode: HseMode::Bypass, + }); + config.rcc.pll_src = PllSource::HSE; + config.rcc.pll = Some(Pll { + prediv: PllPreDiv::DIV4, + mul: PllMul::MUL168, + divp: Some(PllPDiv::DIV2), // ((8 MHz / 4) * 168) / 2 = 168 Mhz. + divq: Some(PllQDiv::DIV7), // ((8 MHz / 4) * 168) / 7 = 48 Mhz. + divr: None, + }); + config.rcc.ahb_pre = AHBPrescaler::DIV1; + config.rcc.apb1_pre = APBPrescaler::DIV4; + config.rcc.apb2_pre = APBPrescaler::DIV2; + config.rcc.sys = Sysclk::PLL1_P; + config.rcc.mux.clk48sel = mux::Clk48sel::PLL1_Q; + } + let p = embassy_stm32::init(config); + + // Configure all required buffers in a static way. + debug!("USB packet size is {} byte", USB_MAX_PACKET_SIZE); + static CONFIG_DESCRIPTOR: StaticCell<[u8; 256]> = StaticCell::new(); + let config_descriptor = CONFIG_DESCRIPTOR.init([0; 256]); + + static BOS_DESCRIPTOR: StaticCell<[u8; 32]> = StaticCell::new(); + let bos_descriptor = BOS_DESCRIPTOR.init([0; 32]); + + const CONTROL_BUF_SIZE: usize = 64; + static CONTROL_BUF: StaticCell<[u8; CONTROL_BUF_SIZE]> = StaticCell::new(); + let control_buf = CONTROL_BUF.init([0; CONTROL_BUF_SIZE]); + + const FEEDBACK_BUF_SIZE: usize = 4; + static EP_OUT_BUFFER: StaticCell<[u8; FEEDBACK_BUF_SIZE + CONTROL_BUF_SIZE + USB_MAX_PACKET_SIZE]> = + StaticCell::new(); + let ep_out_buffer = EP_OUT_BUFFER.init([0u8; FEEDBACK_BUF_SIZE + CONTROL_BUF_SIZE + USB_MAX_PACKET_SIZE]); + + static STATE: StaticCell = StaticCell::new(); + let state = STATE.init(speaker::State::new()); + + // Create the driver, from the HAL. + let mut usb_config = usb::Config::default(); + + // Do not enable vbus_detection. This is a safe default that works in all boards. + // However, if your USB device is self-powered (can stay powered on if USB is unplugged), you need + // to enable vbus_detection to comply with the USB spec. If you enable it, the board + // has to support it or USB won't work at all. See docs on `vbus_detection` for details. + usb_config.vbus_detection = false; + + let usb_driver = usb::Driver::new_fs(p.USB_OTG_FS, Irqs, p.PA12, p.PA11, ep_out_buffer, usb_config); + + // Basic USB device configuration + let mut config = embassy_usb::Config::new(0xc0de, 0xcafe); + config.manufacturer = Some("Embassy"); + config.product = Some("USB-audio-speaker example"); + config.serial_number = Some("12345678"); + + // Required for windows compatibility. + // https://developer.nordicsemi.com/nRF_Connect_SDK/doc/1.9.1/kconfig/CONFIG_CDC_ACM_IAD.html#help + config.device_class = 0xEF; + config.device_sub_class = 0x02; + config.device_protocol = 0x01; + config.composite_with_iads = true; + + let mut builder = embassy_usb::Builder::new( + usb_driver, + config, + config_descriptor, + bos_descriptor, + &mut [], // no msos descriptors + control_buf, + ); + + // Create the UAC1 Speaker class components + let (stream, feedback, control_monitor) = Speaker::new( + &mut builder, + state, + USB_MAX_PACKET_SIZE as u16, + uac1::SampleWidth::Width4Byte, + &[SAMPLE_RATE_HZ], + &AUDIO_CHANNELS, + FEEDBACK_REFRESH_PERIOD, + ); + + // Create the USB device + let usb_device = builder.build(); + + // Establish a zero-copy channel for transferring received audio samples between tasks + static SAMPLE_BLOCKS: StaticCell<[SampleBlock; 2]> = StaticCell::new(); + let sample_blocks = SAMPLE_BLOCKS.init([Vec::new(), Vec::new()]); + + static CHANNEL: StaticCell> = StaticCell::new(); + let channel = CHANNEL.init(zerocopy_channel::Channel::new(sample_blocks)); + let (sender, receiver) = channel.split(); + + // Run a timer for counting between SOF interrupts. + let mut tim2 = timer::low_level::Timer::new(p.TIM2); + tim2.set_tick_freq(Hertz(FEEDBACK_COUNTER_TICK_RATE)); + tim2.set_trigger_source(timer::low_level::TriggerSource::ITR1); // The USB SOF signal. + tim2.set_slave_mode(timer::low_level::SlaveMode::TRIGGER_MODE); + tim2.regs_gp16().dier().modify(|r| r.set_tie(true)); // Enable the trigger interrupt. + tim2.start(); + + TIMER.lock(|p| p.borrow_mut().replace(tim2)); + + // Unmask the TIM2 interrupt. + unsafe { + cortex_m::peripheral::NVIC::unmask(interrupt::TIM2); + } + + // Launch USB audio tasks. + unwrap!(spawner.spawn(usb_control_task(control_monitor))); + unwrap!(spawner.spawn(usb_streaming_task(stream, sender))); + unwrap!(spawner.spawn(usb_feedback_task(feedback))); + unwrap!(spawner.spawn(usb_task(usb_device))); + unwrap!(spawner.spawn(audio_receiver_task(receiver))); +} From 36292ada6228bff99cb0baa916786da394e05a42 Mon Sep 17 00:00:00 2001 From: elagil Date: Sat, 2 Nov 2024 20:01:20 +0100 Subject: [PATCH 242/361] feat(stm32h5): add usb audio example --- examples/stm32h5/src/bin/usb_uac_speaker.rs | 367 ++++++++++++++++++++ 1 file changed, 367 insertions(+) create mode 100644 examples/stm32h5/src/bin/usb_uac_speaker.rs diff --git a/examples/stm32h5/src/bin/usb_uac_speaker.rs b/examples/stm32h5/src/bin/usb_uac_speaker.rs new file mode 100644 index 000000000..6b992690f --- /dev/null +++ b/examples/stm32h5/src/bin/usb_uac_speaker.rs @@ -0,0 +1,367 @@ +#![no_std] +#![no_main] + +use core::cell::RefCell; + +use defmt::{panic, *}; +use embassy_executor::Spawner; +use embassy_stm32::time::Hertz; +use embassy_stm32::{bind_interrupts, interrupt, peripherals, timer, usb, Config}; +use embassy_sync::blocking_mutex::raw::{CriticalSectionRawMutex, NoopRawMutex}; +use embassy_sync::blocking_mutex::Mutex; +use embassy_sync::signal::Signal; +use embassy_sync::zerocopy_channel; +use embassy_usb::class::uac1; +use embassy_usb::class::uac1::speaker::{self, Speaker}; +use embassy_usb::driver::EndpointError; +use heapless::Vec; +use micromath::F32Ext; +use static_cell::StaticCell; +use {defmt_rtt as _, panic_probe as _}; + +bind_interrupts!(struct Irqs { + USB_DRD_FS => usb::InterruptHandler; +}); + +static TIMER: Mutex>>> = + Mutex::new(RefCell::new(None)); + +// A counter signal that is written by the feedback timer, once every `FEEDBACK_REFRESH_PERIOD`. +// At that point, a feedback value is sent to the host. +pub static FEEDBACK_SIGNAL: Signal = Signal::new(); + +// Stereo input +pub const INPUT_CHANNEL_COUNT: usize = 2; + +// This example uses a fixed sample rate of 48 kHz. +pub const SAMPLE_RATE_HZ: u32 = 48_000; +pub const FEEDBACK_COUNTER_TICK_RATE: u32 = 31_250_000; + +// Use 32 bit samples, which allow for a lot of (software) volume adjustment without degradation of quality. +pub const SAMPLE_WIDTH: uac1::SampleWidth = uac1::SampleWidth::Width4Byte; +pub const SAMPLE_WIDTH_BIT: usize = SAMPLE_WIDTH.in_bit(); +pub const SAMPLE_SIZE: usize = SAMPLE_WIDTH as usize; +pub const SAMPLE_SIZE_PER_S: usize = (SAMPLE_RATE_HZ as usize) * INPUT_CHANNEL_COUNT * SAMPLE_SIZE; + +// Size of audio samples per 1 ms - for the full-speed USB frame period of 1 ms. +pub const USB_FRAME_SIZE: usize = SAMPLE_SIZE_PER_S.div_ceil(1000); + +// Select front left and right audio channels. +pub const AUDIO_CHANNELS: [uac1::Channel; INPUT_CHANNEL_COUNT] = [uac1::Channel::LeftFront, uac1::Channel::RightFront]; + +// Factor of two as a margin for feedback (this is an excessive amount) +pub const USB_MAX_PACKET_SIZE: usize = 2 * USB_FRAME_SIZE; +pub const USB_MAX_SAMPLE_COUNT: usize = USB_MAX_PACKET_SIZE / SAMPLE_SIZE; + +// The data type that is exchanged via the zero-copy channel (a sample vector). +pub type SampleBlock = Vec; + +// Feedback is provided in 10.14 format for full-speed endpoints. +pub const FEEDBACK_REFRESH_PERIOD: uac1::FeedbackRefresh = uac1::FeedbackRefresh::Period8Frames; +const FEEDBACK_SHIFT: usize = 14; + +const TICKS_PER_SAMPLE: f32 = (FEEDBACK_COUNTER_TICK_RATE as f32) / (SAMPLE_RATE_HZ as f32); + +struct Disconnected {} + +impl From for Disconnected { + fn from(val: EndpointError) -> Self { + match val { + EndpointError::BufferOverflow => panic!("Buffer overflow"), + EndpointError::Disabled => Disconnected {}, + } + } +} + +/// Sends feedback messages to the host. +async fn feedback_handler<'d, T: usb::Instance + 'd>( + feedback: &mut speaker::Feedback<'d, usb::Driver<'d, T>>, + feedback_factor: f32, +) -> Result<(), Disconnected> { + let mut packet: Vec = Vec::new(); + + // Collects the fractional component of the feedback value that is lost by rounding. + let mut rest = 0.0_f32; + + loop { + let counter = FEEDBACK_SIGNAL.wait().await; + + packet.clear(); + + let raw_value = counter as f32 * feedback_factor + rest; + let value = raw_value.round(); + rest = raw_value - value; + + let value = value as u32; + + packet.push(value as u8).unwrap(); + packet.push((value >> 8) as u8).unwrap(); + packet.push((value >> 16) as u8).unwrap(); + + feedback.write_packet(&packet).await?; + } +} + +/// Handles streaming of audio data from the host. +async fn stream_handler<'d, T: usb::Instance + 'd>( + stream: &mut speaker::Stream<'d, usb::Driver<'d, T>>, + sender: &mut zerocopy_channel::Sender<'static, NoopRawMutex, SampleBlock>, +) -> Result<(), Disconnected> { + loop { + let mut usb_data = [0u8; USB_MAX_PACKET_SIZE]; + let data_size = stream.read_packet(&mut usb_data).await?; + + let word_count = data_size / SAMPLE_SIZE; + + if word_count * SAMPLE_SIZE == data_size { + // Obtain a buffer from the channel + let samples = sender.send().await; + samples.clear(); + + for w in 0..word_count { + let byte_offset = w * SAMPLE_SIZE; + let sample = u32::from_le_bytes(usb_data[byte_offset..byte_offset + SAMPLE_SIZE].try_into().unwrap()); + + // Fill the sample buffer with data. + samples.push(sample).unwrap(); + } + + sender.send_done(); + } else { + debug!("Invalid USB buffer size of {}, skipped.", data_size); + } + } +} + +/// Receives audio samples from the USB streaming task and can play them back. +#[embassy_executor::task] +async fn audio_receiver_task(mut usb_audio_receiver: zerocopy_channel::Receiver<'static, NoopRawMutex, SampleBlock>) { + loop { + let _samples = usb_audio_receiver.receive().await; + // Use the samples, for example play back via the SAI peripheral. + + // Notify the channel that the buffer is now ready to be reused + usb_audio_receiver.receive_done(); + } +} + +/// Receives audio samples from the host. +#[embassy_executor::task] +async fn usb_streaming_task( + mut stream: speaker::Stream<'static, usb::Driver<'static, peripherals::USB>>, + mut sender: zerocopy_channel::Sender<'static, NoopRawMutex, SampleBlock>, +) { + loop { + stream.wait_connection().await; + info!("USB connected."); + _ = stream_handler(&mut stream, &mut sender).await; + info!("USB disconnected."); + } +} + +/// Sends sample rate feedback to the host. +/// +/// The `feedback_factor` scales the feedback timer's counter value so that the result is the number of samples that +/// this device played back or "consumed" during one SOF period (1 ms) - in 10.14 format. +/// +/// Ideally, the `feedback_factor` that is calculated below would be an integer for avoiding numerical errors. +/// This is achieved by having `TICKS_PER_SAMPLE` be a power of two. For audio applications at a sample rate of 48 kHz, +/// 24.576 MHz would be one such option. +#[embassy_executor::task] +async fn usb_feedback_task(mut feedback: speaker::Feedback<'static, usb::Driver<'static, peripherals::USB>>) { + let feedback_factor = + ((1 << FEEDBACK_SHIFT) as f32 / TICKS_PER_SAMPLE) / FEEDBACK_REFRESH_PERIOD.frame_count() as f32; + + loop { + feedback.wait_connection().await; + _ = feedback_handler(&mut feedback, feedback_factor).await; + } +} + +#[embassy_executor::task] +async fn usb_task(mut usb_device: embassy_usb::UsbDevice<'static, usb::Driver<'static, peripherals::USB>>) { + usb_device.run().await; +} + +/// Checks for changes on the control monitor of the class. +/// +/// In this case, monitor changes of volume or mute state. +#[embassy_executor::task] +async fn usb_control_task(control_monitor: speaker::ControlMonitor<'static>) { + loop { + control_monitor.changed().await; + + for channel in AUDIO_CHANNELS { + let volume = control_monitor.volume(channel).unwrap(); + info!("Volume changed to {} on channel {}.", volume, channel); + } + } +} + +/// Feedback value measurement and calculation +/// +/// Used for measuring/calculating the number of samples that were received from the host during the +/// `FEEDBACK_REFRESH_PERIOD`. +/// +/// Configured in this example with +/// - a refresh period of 8 ms, and +/// - a tick rate of 42 MHz. +/// +/// This gives an (ideal) counter value of 336.000 for every update of the `FEEDBACK_SIGNAL`. +#[interrupt] +fn TIM5() { + static mut LAST_TICKS: u32 = 0; + static mut FRAME_COUNT: usize = 0; + + critical_section::with(|cs| { + // Read timer counter. + let ticks = TIMER.borrow(cs).borrow().as_ref().unwrap().regs_gp32().cnt().read(); + + // Clear trigger interrupt flag. + TIMER + .borrow(cs) + .borrow_mut() + .as_mut() + .unwrap() + .regs_gp32() + .sr() + .modify(|r| r.set_tif(false)); + + // Count up frames and emit a signal, when the refresh period is reached (here, every 8 ms). + *FRAME_COUNT += 1; + if *FRAME_COUNT >= FEEDBACK_REFRESH_PERIOD.frame_count() { + *FRAME_COUNT = 0; + FEEDBACK_SIGNAL.signal(ticks.wrapping_sub(*LAST_TICKS)); + *LAST_TICKS = ticks; + } + }); +} + +// If you are trying this and your USB device doesn't connect, the most +// common issues are the RCC config and vbus_detection +// +// See https://embassy.dev/book/#_the_usb_examples_are_not_working_on_my_board_is_there_anything_else_i_need_to_configure +// for more information. +#[embassy_executor::main] +async fn main(spawner: Spawner) { + let mut config = Config::default(); + { + use embassy_stm32::rcc::*; + config.rcc.hsi = None; + config.rcc.hsi48 = Some(Hsi48Config { sync_from_usb: true }); // needed for USB + config.rcc.hse = Some(Hse { + freq: Hertz(8_000_000), + mode: HseMode::BypassDigital, + }); + config.rcc.pll1 = Some(Pll { + source: PllSource::HSE, + prediv: PllPreDiv::DIV2, + mul: PllMul::MUL125, + divp: Some(PllDiv::DIV2), // 250 Mhz + divq: None, + divr: None, + }); + config.rcc.pll2 = Some(Pll { + source: PllSource::HSE, + prediv: PllPreDiv::DIV4, + mul: PllMul::MUL123, + divp: Some(PllDiv::DIV20), // 12.3 Mhz, close to 12.288 MHz for 48 kHz audio + divq: None, + divr: None, + }); + config.rcc.ahb_pre = AHBPrescaler::DIV2; + config.rcc.apb1_pre = APBPrescaler::DIV4; + config.rcc.apb2_pre = APBPrescaler::DIV2; + config.rcc.apb3_pre = APBPrescaler::DIV4; + config.rcc.sys = Sysclk::PLL1_P; + config.rcc.voltage_scale = VoltageScale::Scale0; + config.rcc.mux.usbsel = mux::Usbsel::HSI48; + config.rcc.mux.sai2sel = mux::Saisel::PLL2_P; + } + let p = embassy_stm32::init(config); + + info!("Hello World!"); + + // Configure all required buffers in a static way. + debug!("USB packet size is {} byte", USB_MAX_PACKET_SIZE); + static CONFIG_DESCRIPTOR: StaticCell<[u8; 256]> = StaticCell::new(); + let config_descriptor = CONFIG_DESCRIPTOR.init([0; 256]); + + static BOS_DESCRIPTOR: StaticCell<[u8; 32]> = StaticCell::new(); + let bos_descriptor = BOS_DESCRIPTOR.init([0; 32]); + + const CONTROL_BUF_SIZE: usize = 64; + static CONTROL_BUF: StaticCell<[u8; CONTROL_BUF_SIZE]> = StaticCell::new(); + let control_buf = CONTROL_BUF.init([0; CONTROL_BUF_SIZE]); + + static STATE: StaticCell = StaticCell::new(); + let state = STATE.init(speaker::State::new()); + + let usb_driver = usb::Driver::new(p.USB, Irqs, p.PA12, p.PA11); + + // Basic USB device configuration + let mut config = embassy_usb::Config::new(0xc0de, 0xcafe); + config.manufacturer = Some("Embassy"); + config.product = Some("USB-audio-speaker example"); + config.serial_number = Some("12345678"); + + // Required for windows compatibility. + // https://developer.nordicsemi.com/nRF_Connect_SDK/doc/1.9.1/kconfig/CONFIG_CDC_ACM_IAD.html#help + config.device_class = 0xEF; + config.device_sub_class = 0x02; + config.device_protocol = 0x01; + config.composite_with_iads = true; + + let mut builder = embassy_usb::Builder::new( + usb_driver, + config, + config_descriptor, + bos_descriptor, + &mut [], // no msos descriptors + control_buf, + ); + + // Create the UAC1 Speaker class components + let (stream, feedback, control_monitor) = Speaker::new( + &mut builder, + state, + USB_MAX_PACKET_SIZE as u16, + uac1::SampleWidth::Width4Byte, + &[SAMPLE_RATE_HZ], + &AUDIO_CHANNELS, + FEEDBACK_REFRESH_PERIOD, + ); + + // Create the USB device + let usb_device = builder.build(); + + // Establish a zero-copy channel for transferring received audio samples between tasks + static SAMPLE_BLOCKS: StaticCell<[SampleBlock; 2]> = StaticCell::new(); + let sample_blocks = SAMPLE_BLOCKS.init([Vec::new(), Vec::new()]); + + static CHANNEL: StaticCell> = StaticCell::new(); + let channel = CHANNEL.init(zerocopy_channel::Channel::new(sample_blocks)); + let (sender, receiver) = channel.split(); + + // Run a timer for counting between SOF interrupts. + let mut tim5 = timer::low_level::Timer::new(p.TIM5); + tim5.set_tick_freq(Hertz(FEEDBACK_COUNTER_TICK_RATE)); + tim5.set_trigger_source(timer::low_level::TriggerSource::ITR12); // The USB SOF signal. + tim5.set_slave_mode(timer::low_level::SlaveMode::TRIGGER_MODE); + tim5.regs_gp16().dier().modify(|r| r.set_tie(true)); // Enable the trigger interrupt. + tim5.start(); + + TIMER.lock(|p| p.borrow_mut().replace(tim5)); + + // Unmask the TIM5 interrupt. + unsafe { + cortex_m::peripheral::NVIC::unmask(interrupt::TIM5); + } + + // Launch USB audio tasks. + unwrap!(spawner.spawn(usb_control_task(control_monitor))); + unwrap!(spawner.spawn(usb_streaming_task(stream, sender))); + unwrap!(spawner.spawn(usb_feedback_task(feedback))); + unwrap!(spawner.spawn(usb_task(usb_device))); + unwrap!(spawner.spawn(audio_receiver_task(receiver))); +} From cc4b5ae9cb4d79f9fa378b2a06073eb7ae96d369 Mon Sep 17 00:00:00 2001 From: elagil Date: Sat, 23 Nov 2024 22:52:43 +0100 Subject: [PATCH 243/361] feat: change SOF timer to input capture --- examples/stm32f4/src/bin/usb_uac_speaker.rs | 50 +++++++++++++-------- examples/stm32h5/src/bin/usb_uac_speaker.rs | 49 ++++++++++++-------- 2 files changed, 61 insertions(+), 38 deletions(-) diff --git a/examples/stm32f4/src/bin/usb_uac_speaker.rs b/examples/stm32f4/src/bin/usb_uac_speaker.rs index 77c693ace..8d83afd1a 100644 --- a/examples/stm32f4/src/bin/usb_uac_speaker.rs +++ b/examples/stm32f4/src/bin/usb_uac_speaker.rs @@ -222,25 +222,24 @@ fn TIM2() { critical_section::with(|cs| { // Read timer counter. - let ticks = TIMER.borrow(cs).borrow().as_ref().unwrap().regs_gp32().cnt().read(); + let timer = TIMER.borrow(cs).borrow().as_ref().unwrap().regs_gp32(); + + let status = timer.sr().read(); + + const CHANNEL_INDEX: usize = 0; + if status.ccif(CHANNEL_INDEX) { + let ticks = timer.ccr(CHANNEL_INDEX).read(); + + *FRAME_COUNT += 1; + if *FRAME_COUNT >= FEEDBACK_REFRESH_PERIOD.frame_count() { + *FRAME_COUNT = 0; + FEEDBACK_SIGNAL.signal(ticks.wrapping_sub(*LAST_TICKS)); + *LAST_TICKS = ticks; + } + }; // Clear trigger interrupt flag. - TIMER - .borrow(cs) - .borrow_mut() - .as_mut() - .unwrap() - .regs_gp32() - .sr() - .modify(|r| r.set_tif(false)); - - // Count up frames and emit a signal, when the refresh period is reached (here, every 8 ms). - *FRAME_COUNT += 1; - if *FRAME_COUNT >= FEEDBACK_REFRESH_PERIOD.frame_count() { - *FRAME_COUNT = 0; - FEEDBACK_SIGNAL.signal(ticks.wrapping_sub(*LAST_TICKS)); - *LAST_TICKS = ticks; - } + timer.sr().modify(|r| r.set_tif(false)); }); } @@ -355,8 +354,21 @@ async fn main(spawner: Spawner) { let mut tim2 = timer::low_level::Timer::new(p.TIM2); tim2.set_tick_freq(Hertz(FEEDBACK_COUNTER_TICK_RATE)); tim2.set_trigger_source(timer::low_level::TriggerSource::ITR1); // The USB SOF signal. - tim2.set_slave_mode(timer::low_level::SlaveMode::TRIGGER_MODE); - tim2.regs_gp16().dier().modify(|r| r.set_tie(true)); // Enable the trigger interrupt. + + const TIMER_CHANNEL: timer::Channel = timer::Channel::Ch1; + tim2.set_input_ti_selection(TIMER_CHANNEL, timer::low_level::InputTISelection::TRC); + tim2.set_input_capture_prescaler(TIMER_CHANNEL, 0); + tim2.set_input_capture_filter(TIMER_CHANNEL, timer::low_level::FilterValue::FCK_INT_N2); + + // Reset all interrupt flags. + tim2.regs_gp32().sr().write(|r| r.0 = 0); + + // Enable routing of SOF to the timer. + tim2.regs_gp32().or().write(|r| *r = 0b10 << 10); + + tim2.enable_channel(TIMER_CHANNEL, true); + tim2.enable_input_interrupt(TIMER_CHANNEL, true); + tim2.start(); TIMER.lock(|p| p.borrow_mut().replace(tim2)); diff --git a/examples/stm32h5/src/bin/usb_uac_speaker.rs b/examples/stm32h5/src/bin/usb_uac_speaker.rs index 6b992690f..4fd4ccbbd 100644 --- a/examples/stm32h5/src/bin/usb_uac_speaker.rs +++ b/examples/stm32h5/src/bin/usb_uac_speaker.rs @@ -94,6 +94,8 @@ async fn feedback_handler<'d, T: usb::Instance + 'd>( let value = value as u32; + debug!("Feedback value: {}", value); + packet.push(value as u8).unwrap(); packet.push((value >> 8) as u8).unwrap(); packet.push((value >> 16) as u8).unwrap(); @@ -215,25 +217,24 @@ fn TIM5() { critical_section::with(|cs| { // Read timer counter. - let ticks = TIMER.borrow(cs).borrow().as_ref().unwrap().regs_gp32().cnt().read(); + let timer = TIMER.borrow(cs).borrow().as_ref().unwrap().regs_gp32(); + + let status = timer.sr().read(); + + const CHANNEL_INDEX: usize = 0; + if status.ccif(CHANNEL_INDEX) { + let ticks = timer.ccr(CHANNEL_INDEX).read(); + + *FRAME_COUNT += 1; + if *FRAME_COUNT >= FEEDBACK_REFRESH_PERIOD.frame_count() { + *FRAME_COUNT = 0; + FEEDBACK_SIGNAL.signal(ticks.wrapping_sub(*LAST_TICKS)); + *LAST_TICKS = ticks; + } + }; // Clear trigger interrupt flag. - TIMER - .borrow(cs) - .borrow_mut() - .as_mut() - .unwrap() - .regs_gp32() - .sr() - .modify(|r| r.set_tif(false)); - - // Count up frames and emit a signal, when the refresh period is reached (here, every 8 ms). - *FRAME_COUNT += 1; - if *FRAME_COUNT >= FEEDBACK_REFRESH_PERIOD.frame_count() { - *FRAME_COUNT = 0; - FEEDBACK_SIGNAL.signal(ticks.wrapping_sub(*LAST_TICKS)); - *LAST_TICKS = ticks; - } + timer.sr().modify(|r| r.set_tif(false)); }); } @@ -347,8 +348,18 @@ async fn main(spawner: Spawner) { let mut tim5 = timer::low_level::Timer::new(p.TIM5); tim5.set_tick_freq(Hertz(FEEDBACK_COUNTER_TICK_RATE)); tim5.set_trigger_source(timer::low_level::TriggerSource::ITR12); // The USB SOF signal. - tim5.set_slave_mode(timer::low_level::SlaveMode::TRIGGER_MODE); - tim5.regs_gp16().dier().modify(|r| r.set_tie(true)); // Enable the trigger interrupt. + + const TIMER_CHANNEL: timer::Channel = timer::Channel::Ch1; + tim5.set_input_ti_selection(TIMER_CHANNEL, timer::low_level::InputTISelection::TRC); + tim5.set_input_capture_prescaler(TIMER_CHANNEL, 0); + tim5.set_input_capture_filter(TIMER_CHANNEL, timer::low_level::FilterValue::FCK_INT_N2); + + // Reset all interrupt flags. + tim5.regs_gp32().sr().write(|r| r.0 = 0); + + tim5.enable_channel(TIMER_CHANNEL, true); + tim5.enable_input_interrupt(TIMER_CHANNEL, true); + tim5.start(); TIMER.lock(|p| p.borrow_mut().replace(tim5)); From aaad8450e990f74c6b398aca9a6ec495720bf845 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Sun, 24 Nov 2024 20:58:48 +0100 Subject: [PATCH 244/361] Use inline const for initializing arrays. (#3567) --- embassy-net-driver-channel/src/lib.rs | 6 ++---- embassy-nrf/src/gpiote.rs | 5 ++--- embassy-nrf/src/time_driver.rs | 3 +-- embassy-nrf/src/usb/mod.rs | 9 ++++----- embassy-nrf/src/wdt.rs | 3 +-- embassy-nxp/src/pint.rs | 16 +++++++++------- embassy-rp/src/dma.rs | 3 +-- embassy-rp/src/gpio.rs | 6 ++---- embassy-rp/src/pio/mod.rs | 4 +--- embassy-rp/src/time_driver.rs | 9 ++++----- embassy-rp/src/usb.rs | 7 +++---- embassy-stm32/src/eth/mod.rs | 6 ++---- embassy-stm32/src/exti.rs | 3 +-- embassy-stm32/src/ipcc.rs | 6 ++---- embassy-stm32/src/time_driver.rs | 5 +---- embassy-stm32/src/timer/mod.rs | 5 ++--- embassy-stm32/src/usb/usb.rs | 11 ++++------- embassy-time/src/driver_std.rs | 4 ++-- embassy-time/src/driver_wasm.rs | 4 ++-- 19 files changed, 46 insertions(+), 69 deletions(-) diff --git a/embassy-net-driver-channel/src/lib.rs b/embassy-net-driver-channel/src/lib.rs index 6390502a8..600efd9e5 100644 --- a/embassy-net-driver-channel/src/lib.rs +++ b/embassy-net-driver-channel/src/lib.rs @@ -26,13 +26,11 @@ pub struct State { } impl State { - const NEW_PACKET: PacketBuf = PacketBuf::new(); - /// Create a new channel state. pub const fn new() -> Self { Self { - rx: [Self::NEW_PACKET; N_RX], - tx: [Self::NEW_PACKET; N_TX], + rx: [const { PacketBuf::new() }; N_RX], + tx: [const { PacketBuf::new() }; N_TX], inner: MaybeUninit::uninit(), } } diff --git a/embassy-nrf/src/gpiote.rs b/embassy-nrf/src/gpiote.rs index 87bb405f4..8771f9f08 100644 --- a/embassy-nrf/src/gpiote.rs +++ b/embassy-nrf/src/gpiote.rs @@ -29,9 +29,8 @@ const PIN_COUNT: usize = 48; const PIN_COUNT: usize = 32; #[allow(clippy::declare_interior_mutable_const)] -const NEW_AW: AtomicWaker = AtomicWaker::new(); -static CHANNEL_WAKERS: [AtomicWaker; CHANNEL_COUNT] = [NEW_AW; CHANNEL_COUNT]; -static PORT_WAKERS: [AtomicWaker; PIN_COUNT] = [NEW_AW; PIN_COUNT]; +static CHANNEL_WAKERS: [AtomicWaker; CHANNEL_COUNT] = [const { AtomicWaker::new() }; CHANNEL_COUNT]; +static PORT_WAKERS: [AtomicWaker; PIN_COUNT] = [const { AtomicWaker::new() }; PIN_COUNT]; /// Polarity for listening to events for GPIOTE input channels. pub enum InputChannelPolarity { diff --git a/embassy-nrf/src/time_driver.rs b/embassy-nrf/src/time_driver.rs index 81aabb11c..9ba38ec1b 100644 --- a/embassy-nrf/src/time_driver.rs +++ b/embassy-nrf/src/time_driver.rs @@ -123,11 +123,10 @@ struct RtcDriver { alarms: Mutex<[AlarmState; ALARM_COUNT]>, } -const ALARM_STATE_NEW: AlarmState = AlarmState::new(); embassy_time_driver::time_driver_impl!(static DRIVER: RtcDriver = RtcDriver { period: AtomicU32::new(0), alarm_count: AtomicU8::new(0), - alarms: Mutex::const_new(CriticalSectionRawMutex::new(), [ALARM_STATE_NEW; ALARM_COUNT]), + alarms: Mutex::const_new(CriticalSectionRawMutex::new(), [const {AlarmState::new()}; ALARM_COUNT]), }); impl RtcDriver { diff --git a/embassy-nrf/src/usb/mod.rs b/embassy-nrf/src/usb/mod.rs index 52c9c532b..a9bf16708 100644 --- a/embassy-nrf/src/usb/mod.rs +++ b/embassy-nrf/src/usb/mod.rs @@ -22,11 +22,10 @@ use crate::pac::usbd::vals; use crate::util::slice_in_ram; use crate::{interrupt, pac, Peripheral}; -const NEW_AW: AtomicWaker = AtomicWaker::new(); -static BUS_WAKER: AtomicWaker = NEW_AW; -static EP0_WAKER: AtomicWaker = NEW_AW; -static EP_IN_WAKERS: [AtomicWaker; 8] = [NEW_AW; 8]; -static EP_OUT_WAKERS: [AtomicWaker; 8] = [NEW_AW; 8]; +static BUS_WAKER: AtomicWaker = AtomicWaker::new(); +static EP0_WAKER: AtomicWaker = AtomicWaker::new(); +static EP_IN_WAKERS: [AtomicWaker; 8] = [const { AtomicWaker::new() }; 8]; +static EP_OUT_WAKERS: [AtomicWaker; 8] = [const { AtomicWaker::new() }; 8]; static READY_ENDPOINTS: AtomicU32 = AtomicU32::new(0); /// Interrupt handler. diff --git a/embassy-nrf/src/wdt.rs b/embassy-nrf/src/wdt.rs index dfe6cbec3..f7812258c 100644 --- a/embassy-nrf/src/wdt.rs +++ b/embassy-nrf/src/wdt.rs @@ -112,8 +112,7 @@ impl Watchdog { let this = Self { _private: () }; - const DUMMY_HANDLE: WatchdogHandle = WatchdogHandle { index: 0 }; - let mut handles = [DUMMY_HANDLE; N]; + let mut handles = [const { WatchdogHandle { index: 0 } }; N]; for i in 0..N { handles[i] = WatchdogHandle { index: i as u8 }; handles[i].pet(); diff --git a/embassy-nxp/src/pint.rs b/embassy-nxp/src/pint.rs index 3313f91a6..809be4bff 100644 --- a/embassy-nxp/src/pint.rs +++ b/embassy-nxp/src/pint.rs @@ -52,14 +52,16 @@ impl PinInterrupt { } } -const NEW_PIN_INTERRUPT: PinInterrupt = PinInterrupt { - assigned: false, - waker: AtomicWaker::new(), - at_fault: false, -}; const INTERUPT_COUNT: usize = 8; -static PIN_INTERRUPTS: Mutex> = - Mutex::new(RefCell::new([NEW_PIN_INTERRUPT; INTERUPT_COUNT])); +static PIN_INTERRUPTS: Mutex> = Mutex::new(RefCell::new( + [const { + PinInterrupt { + assigned: false, + waker: AtomicWaker::new(), + at_fault: false, + } + }; INTERUPT_COUNT], +)); fn next_available_interrupt() -> Option { critical_section::with(|cs| { diff --git a/embassy-rp/src/dma.rs b/embassy-rp/src/dma.rs index 34abe3e2d..2edcfdf5b 100644 --- a/embassy-rp/src/dma.rs +++ b/embassy-rp/src/dma.rs @@ -212,8 +212,7 @@ impl<'a, C: Channel> Future for Transfer<'a, C> { pub(crate) const CHANNEL_COUNT: usize = 12; #[cfg(feature = "_rp235x")] pub(crate) const CHANNEL_COUNT: usize = 16; -const NEW_AW: AtomicWaker = AtomicWaker::new(); -static CHANNEL_WAKERS: [AtomicWaker; CHANNEL_COUNT] = [NEW_AW; CHANNEL_COUNT]; +static CHANNEL_WAKERS: [AtomicWaker; CHANNEL_COUNT] = [const { AtomicWaker::new() }; CHANNEL_COUNT]; trait SealedChannel {} trait SealedWord {} diff --git a/embassy-rp/src/gpio.rs b/embassy-rp/src/gpio.rs index cb54375e4..203192827 100644 --- a/embassy-rp/src/gpio.rs +++ b/embassy-rp/src/gpio.rs @@ -13,18 +13,16 @@ use crate::pac::common::{Reg, RW}; use crate::pac::SIO; use crate::{interrupt, pac, peripherals, Peripheral, RegExt}; -const NEW_AW: AtomicWaker = AtomicWaker::new(); - #[cfg(any(feature = "rp2040", feature = "rp235xa"))] pub(crate) const BANK0_PIN_COUNT: usize = 30; #[cfg(feature = "rp235xb")] pub(crate) const BANK0_PIN_COUNT: usize = 48; -static BANK0_WAKERS: [AtomicWaker; BANK0_PIN_COUNT] = [NEW_AW; BANK0_PIN_COUNT]; +static BANK0_WAKERS: [AtomicWaker; BANK0_PIN_COUNT] = [const { AtomicWaker::new() }; BANK0_PIN_COUNT]; #[cfg(feature = "qspi-as-gpio")] const QSPI_PIN_COUNT: usize = 6; #[cfg(feature = "qspi-as-gpio")] -static QSPI_WAKERS: [AtomicWaker; QSPI_PIN_COUNT] = [NEW_AW; QSPI_PIN_COUNT]; +static QSPI_WAKERS: [AtomicWaker; QSPI_PIN_COUNT] = [const { AtomicWaker::new() }; QSPI_PIN_COUNT]; /// Represents a digital input or output level. #[derive(Debug, Eq, PartialEq, Clone, Copy)] diff --git a/embassy-rp/src/pio/mod.rs b/embassy-rp/src/pio/mod.rs index 98f4f8943..e3c25020f 100644 --- a/embassy-rp/src/pio/mod.rs +++ b/embassy-rp/src/pio/mod.rs @@ -1270,9 +1270,7 @@ trait SealedInstance { #[inline] fn wakers() -> &'static Wakers { - const NEW_AW: AtomicWaker = AtomicWaker::new(); - static WAKERS: Wakers = Wakers([NEW_AW; 12]); - + static WAKERS: Wakers = Wakers([const { AtomicWaker::new() }; 12]); &WAKERS } diff --git a/embassy-rp/src/time_driver.rs b/embassy-rp/src/time_driver.rs index e5b407a29..40fc71bb1 100644 --- a/embassy-rp/src/time_driver.rs +++ b/embassy-rp/src/time_driver.rs @@ -21,10 +21,6 @@ struct AlarmState { unsafe impl Send for AlarmState {} const ALARM_COUNT: usize = 4; -const DUMMY_ALARM: AlarmState = AlarmState { - timestamp: Cell::new(0), - callback: Cell::new(None), -}; struct TimerDriver { alarms: Mutex, @@ -32,7 +28,10 @@ struct TimerDriver { } embassy_time_driver::time_driver_impl!(static DRIVER: TimerDriver = TimerDriver{ - alarms: Mutex::const_new(CriticalSectionRawMutex::new(), [DUMMY_ALARM; ALARM_COUNT]), + alarms: Mutex::const_new(CriticalSectionRawMutex::new(), [const{AlarmState { + timestamp: Cell::new(0), + callback: Cell::new(None), + }}; ALARM_COUNT]), next_alarm: AtomicU8::new(0), }); diff --git a/embassy-rp/src/usb.rs b/embassy-rp/src/usb.rs index 20ef881f9..26cb90d89 100644 --- a/embassy-rp/src/usb.rs +++ b/embassy-rp/src/usb.rs @@ -43,10 +43,9 @@ const EP_COUNT: usize = 16; const EP_MEMORY_SIZE: usize = 4096; const EP_MEMORY: *mut u8 = pac::USB_DPRAM.as_ptr() as *mut u8; -const NEW_AW: AtomicWaker = AtomicWaker::new(); -static BUS_WAKER: AtomicWaker = NEW_AW; -static EP_IN_WAKERS: [AtomicWaker; EP_COUNT] = [NEW_AW; EP_COUNT]; -static EP_OUT_WAKERS: [AtomicWaker; EP_COUNT] = [NEW_AW; EP_COUNT]; +static BUS_WAKER: AtomicWaker = AtomicWaker::new(); +static EP_IN_WAKERS: [AtomicWaker; EP_COUNT] = [const { AtomicWaker::new() }; EP_COUNT]; +static EP_OUT_WAKERS: [AtomicWaker; EP_COUNT] = [const { AtomicWaker::new() }; EP_COUNT]; struct EndpointBuffer { addr: u16, diff --git a/embassy-stm32/src/eth/mod.rs b/embassy-stm32/src/eth/mod.rs index 1b875b71a..773452bf2 100644 --- a/embassy-stm32/src/eth/mod.rs +++ b/embassy-stm32/src/eth/mod.rs @@ -42,11 +42,9 @@ pub struct PacketQueue { impl PacketQueue { /// Create a new packet queue. pub const fn new() -> Self { - const NEW_TDES: TDes = TDes::new(); - const NEW_RDES: RDes = RDes::new(); Self { - tx_desc: [NEW_TDES; TX], - rx_desc: [NEW_RDES; RX], + tx_desc: [const { TDes::new() }; TX], + rx_desc: [const { RDes::new() }; RX], tx_buf: [Packet([0; TX_BUFFER_SIZE]); TX], rx_buf: [Packet([0; RX_BUFFER_SIZE]); RX], } diff --git a/embassy-stm32/src/exti.rs b/embassy-stm32/src/exti.rs index 87512c92d..5cff74264 100644 --- a/embassy-stm32/src/exti.rs +++ b/embassy-stm32/src/exti.rs @@ -14,8 +14,7 @@ use crate::pac::EXTI; use crate::{interrupt, pac, peripherals, Peripheral}; const EXTI_COUNT: usize = 16; -const NEW_AW: AtomicWaker = AtomicWaker::new(); -static EXTI_WAKERS: [AtomicWaker; EXTI_COUNT] = [NEW_AW; EXTI_COUNT]; +static EXTI_WAKERS: [AtomicWaker; EXTI_COUNT] = [const { AtomicWaker::new() }; EXTI_COUNT]; #[cfg(exti_w)] fn cpu_regs() -> pac::exti::Cpu { diff --git a/embassy-stm32/src/ipcc.rs b/embassy-stm32/src/ipcc.rs index 6c8347311..20cd20dca 100644 --- a/embassy-stm32/src/ipcc.rs +++ b/embassy-stm32/src/ipcc.rs @@ -229,11 +229,9 @@ struct State { impl State { const fn new() -> Self { - const WAKER: AtomicWaker = AtomicWaker::new(); - Self { - rx_wakers: [WAKER; 6], - tx_wakers: [WAKER; 6], + rx_wakers: [const { AtomicWaker::new() }; 6], + tx_wakers: [const { AtomicWaker::new() }; 6], } } diff --git a/embassy-stm32/src/time_driver.rs b/embassy-stm32/src/time_driver.rs index f8041bf1e..74e4d0575 100644 --- a/embassy-stm32/src/time_driver.rs +++ b/embassy-stm32/src/time_driver.rs @@ -261,13 +261,10 @@ pub(crate) struct RtcDriver { rtc: Mutex>>, } -#[allow(clippy::declare_interior_mutable_const)] -const ALARM_STATE_NEW: AlarmState = AlarmState::new(); - embassy_time_driver::time_driver_impl!(static DRIVER: RtcDriver = RtcDriver { period: AtomicU32::new(0), alarm_count: AtomicU8::new(0), - alarms: Mutex::const_new(CriticalSectionRawMutex::new(), [ALARM_STATE_NEW; ALARM_COUNT]), + alarms: Mutex::const_new(CriticalSectionRawMutex::new(), [const{AlarmState::new()}; ALARM_COUNT]), #[cfg(feature = "low-power")] rtc: Mutex::const_new(CriticalSectionRawMutex::new(), Cell::new(None)), }); diff --git a/embassy-stm32/src/timer/mod.rs b/embassy-stm32/src/timer/mod.rs index aa9dd91d9..97740c2ed 100644 --- a/embassy-stm32/src/timer/mod.rs +++ b/embassy-stm32/src/timer/mod.rs @@ -59,10 +59,9 @@ struct State { impl State { const fn new() -> Self { - const NEW_AW: AtomicWaker = AtomicWaker::new(); Self { - up_waker: NEW_AW, - cc_waker: [NEW_AW; 4], + up_waker: AtomicWaker::new(), + cc_waker: [const { AtomicWaker::new() }; 4], } } } diff --git a/embassy-stm32/src/usb/usb.rs b/embassy-stm32/src/usb/usb.rs index 0ab2306c8..94af00b6e 100644 --- a/embassy-stm32/src/usb/usb.rs +++ b/embassy-stm32/src/usb/usb.rs @@ -119,15 +119,12 @@ const USBRAM_ALIGN: usize = 2; #[cfg(any(usbram_32_2048, usbram_32_1024))] const USBRAM_ALIGN: usize = 4; -const NEW_AW: AtomicWaker = AtomicWaker::new(); -static BUS_WAKER: AtomicWaker = NEW_AW; +static BUS_WAKER: AtomicWaker = AtomicWaker::new(); static EP0_SETUP: AtomicBool = AtomicBool::new(false); -const NEW_CTR_TRIGGERED: AtomicBool = AtomicBool::new(false); -static CTR_TRIGGERED: [AtomicBool; EP_COUNT] = [NEW_CTR_TRIGGERED; EP_COUNT]; - -static EP_IN_WAKERS: [AtomicWaker; EP_COUNT] = [NEW_AW; EP_COUNT]; -static EP_OUT_WAKERS: [AtomicWaker; EP_COUNT] = [NEW_AW; EP_COUNT]; +static CTR_TRIGGERED: [AtomicBool; EP_COUNT] = [const { AtomicBool::new(false) }; EP_COUNT]; +static EP_IN_WAKERS: [AtomicWaker; EP_COUNT] = [const { AtomicWaker::new() }; EP_COUNT]; +static EP_OUT_WAKERS: [AtomicWaker; EP_COUNT] = [const { AtomicWaker::new() }; EP_COUNT]; static IRQ_RESET: AtomicBool = AtomicBool::new(false); static IRQ_SUSPEND: AtomicBool = AtomicBool::new(false); static IRQ_RESUME: AtomicBool = AtomicBool::new(false); diff --git a/embassy-time/src/driver_std.rs b/embassy-time/src/driver_std.rs index d182f8331..cbef7aae1 100644 --- a/embassy-time/src/driver_std.rs +++ b/embassy-time/src/driver_std.rs @@ -43,7 +43,6 @@ struct TimeDriver { signaler: UninitCell, } -const ALARM_NEW: AlarmState = AlarmState::new(); embassy_time_driver::time_driver_impl!(static DRIVER: TimeDriver = TimeDriver { alarm_count: AtomicU8::new(0), @@ -56,7 +55,8 @@ embassy_time_driver::time_driver_impl!(static DRIVER: TimeDriver = TimeDriver { impl TimeDriver { fn init(&self) { self.once.call_once(|| unsafe { - self.alarms.write(CsMutex::new(RefCell::new([ALARM_NEW; ALARM_COUNT]))); + self.alarms + .write(CsMutex::new(RefCell::new([const { AlarmState::new() }; ALARM_COUNT]))); self.zero_instant.write(StdInstant::now()); self.signaler.write(Signaler::new()); diff --git a/embassy-time/src/driver_wasm.rs b/embassy-time/src/driver_wasm.rs index ad884f060..d65629e49 100644 --- a/embassy-time/src/driver_wasm.rs +++ b/embassy-time/src/driver_wasm.rs @@ -40,7 +40,6 @@ struct TimeDriver { zero_instant: UninitCell, } -const ALARM_NEW: AlarmState = AlarmState::new(); embassy_time_driver::time_driver_impl!(static DRIVER: TimeDriver = TimeDriver { alarm_count: AtomicU8::new(0), once: Once::new(), @@ -51,7 +50,8 @@ embassy_time_driver::time_driver_impl!(static DRIVER: TimeDriver = TimeDriver { impl TimeDriver { fn init(&self) { self.once.call_once(|| unsafe { - self.alarms.write(Mutex::new([ALARM_NEW; ALARM_COUNT])); + self.alarms + .write(Mutex::new([const { AlarmState::new() }; ALARM_COUNT])); self.zero_instant.write(StdInstant::now()); }); } From 20e2da677f65515b558c6bc4afef8bf7068ad3a7 Mon Sep 17 00:00:00 2001 From: elagil Date: Sun, 24 Nov 2024 22:50:51 +0100 Subject: [PATCH 245/361] chore: use latest metapac --- 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 ebcfb73c9..5b0a88404 100644 --- a/embassy-stm32/Cargo.toml +++ b/embassy-stm32/Cargo.toml @@ -72,7 +72,7 @@ rand_core = "0.6.3" sdio-host = "0.5.0" critical-section = "1.1" #stm32-metapac = { version = "15" } -stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-55b491ee982e52f80a21276a0ccbde4907982b5d" } +stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-04833817666290047257c65c6547d28e1bd10dc9" } vcell = "0.1.3" nb = "1.0.0" @@ -101,7 +101,7 @@ proc-macro2 = "1.0.36" quote = "1.0.15" #stm32-metapac = { version = "15", default-features = false, features = ["metadata"]} -stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-55b491ee982e52f80a21276a0ccbde4907982b5d", default-features = false, features = ["metadata"] } +stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-04833817666290047257c65c6547d28e1bd10dc9", default-features = false, features = ["metadata"] } [features] default = ["rt"] From 8eaa3c8fd3385d50bbf844c6fff0790884ac56af Mon Sep 17 00:00:00 2001 From: Bing Wen Date: Tue, 26 Nov 2024 12:27:43 +0800 Subject: [PATCH 246/361] Add new feature to enable overclocking --- embassy-stm32/Cargo.toml | 4 ++++ embassy-stm32/src/rcc/c0.rs | 13 +++++++++---- embassy-stm32/src/rcc/f013.rs | 18 ++++++++++++------ embassy-stm32/src/rcc/f247.rs | 14 ++++++++++---- embassy-stm32/src/rcc/g0.rs | 19 ++++++++++++++----- embassy-stm32/src/rcc/g4.rs | 18 ++++++++++++++---- 6 files changed, 63 insertions(+), 23 deletions(-) diff --git a/embassy-stm32/Cargo.toml b/embassy-stm32/Cargo.toml index ebcfb73c9..363bc9797 100644 --- a/embassy-stm32/Cargo.toml +++ b/embassy-stm32/Cargo.toml @@ -138,6 +138,10 @@ trustzone-secure = [] ## There are no plans to make this stable. unstable-pac = [] +## Enable this feature to disable the overclocking check. +## DO NOT ENABLE THIS FEATURE UNLESS YOU KNOW WHAT YOU'RE DOING. +unchecked-overclocking = [] + #! ## Time ## Enables additional driver features that depend on embassy-time diff --git a/embassy-stm32/src/rcc/c0.rs b/embassy-stm32/src/rcc/c0.rs index 6712aedc4..c6ac46491 100644 --- a/embassy-stm32/src/rcc/c0.rs +++ b/embassy-stm32/src/rcc/c0.rs @@ -109,9 +109,12 @@ pub(crate) unsafe fn init(config: Config) { None } Some(hse) => { - match hse.mode { - HseMode::Bypass => assert!(max::HSE_BYP.contains(&hse.freq)), - HseMode::Oscillator => assert!(max::HSE_OSC.contains(&hse.freq)), + #[cfg(not(feature = "unchecked-overclocking"))] + { + match hse.mode { + HseMode::Bypass => assert!(max::HSE_BYP.contains(&hse.freq)), + HseMode::Oscillator => assert!(max::HSE_OSC.contains(&hse.freq)), + } } RCC.cr().modify(|w| w.set_hsebyp(hse.mode != HseMode::Oscillator)); @@ -126,14 +129,16 @@ pub(crate) unsafe fn init(config: Config) { Sysclk::HSE => unwrap!(hse), _ => unreachable!(), }; - + #[cfg(not(feature = "unchecked-overclocking"))] assert!(max::SYSCLK.contains(&sys)); // Calculate the AHB frequency (HCLK), among other things so we can calculate the correct flash read latency. let hclk = sys / config.ahb_pre; + #[cfg(not(feature = "unchecked-overclocking"))] assert!(max::HCLK.contains(&hclk)); let (pclk1, pclk1_tim) = super::util::calc_pclk(hclk, config.apb1_pre); + #[cfg(not(feature = "unchecked-overclocking"))] assert!(max::PCLK.contains(&pclk1)); let latency = match hclk.0 { diff --git a/embassy-stm32/src/rcc/f013.rs b/embassy-stm32/src/rcc/f013.rs index 60577b213..20a0f3940 100644 --- a/embassy-stm32/src/rcc/f013.rs +++ b/embassy-stm32/src/rcc/f013.rs @@ -157,9 +157,12 @@ pub(crate) unsafe fn init(config: Config) { None } Some(hse) => { - match hse.mode { - HseMode::Bypass => assert!(max::HSE_BYP.contains(&hse.freq)), - HseMode::Oscillator => assert!(max::HSE_OSC.contains(&hse.freq)), + #[cfg(not(feature = "unchecked-overclocking"))] + { + match hse.mode { + HseMode::Bypass => assert!(max::HSE_BYP.contains(&hse.freq)), + HseMode::Oscillator => assert!(max::HSE_OSC.contains(&hse.freq)), + } } RCC.cr().modify(|w| w.set_hsebyp(hse.mode != HseMode::Oscillator)); @@ -192,7 +195,9 @@ pub(crate) unsafe fn init(config: Config) { PllSource::HSI48 => (Pllsrc::HSI48_DIV_PREDIV, unwrap!(hsi48)), }; let in_freq = src_freq / pll.prediv; + #[cfg(not(feature = "unchecked-overclocking"))] assert!(max::PLL_IN.contains(&in_freq)); + #[cfg(not(feature = "unchecked-overclocking"))] let out_freq = in_freq * pll.mul; assert!(max::PLL_OUT.contains(&out_freq)); @@ -238,15 +243,16 @@ pub(crate) unsafe fn init(config: Config) { let (pclk2, pclk2_tim) = super::util::calc_pclk(hclk, config.apb2_pre); #[cfg(stm32f0)] let (pclk2, pclk2_tim) = (pclk1, pclk1_tim); - + #[cfg(not(feature = "unchecked-overclocking"))] assert!(max::HCLK.contains(&hclk)); + #[cfg(not(feature = "unchecked-overclocking"))] assert!(max::PCLK1.contains(&pclk1)); - #[cfg(not(stm32f0))] + #[cfg(all(not(feature = "unchecked-overclocking"), not(stm32f0)))] assert!(max::PCLK2.contains(&pclk2)); #[cfg(stm32f1)] let adc = pclk2 / config.adc_pre; - #[cfg(stm32f1)] + #[cfg(all(not(feature = "unchecked-overclocking"), stm32f1))] assert!(max::ADC.contains(&adc)); // Set latency based on HCLK frquency diff --git a/embassy-stm32/src/rcc/f247.rs b/embassy-stm32/src/rcc/f247.rs index 58056301a..83d4428cc 100644 --- a/embassy-stm32/src/rcc/f247.rs +++ b/embassy-stm32/src/rcc/f247.rs @@ -169,9 +169,12 @@ pub(crate) unsafe fn init(config: Config) { None } Some(hse) => { - match hse.mode { - HseMode::Bypass => assert!(max::HSE_BYP.contains(&hse.freq)), - HseMode::Oscillator => assert!(max::HSE_OSC.contains(&hse.freq)), + #[cfg(not(feature = "unchecked-overclocking"))] + { + match hse.mode { + HseMode::Bypass => assert!(max::HSE_BYP.contains(&hse.freq)), + HseMode::Oscillator => assert!(max::HSE_OSC.contains(&hse.freq)), + } } RCC.cr().modify(|w| w.set_hsebyp(hse.mode != HseMode::Oscillator)); @@ -204,10 +207,13 @@ pub(crate) unsafe fn init(config: Config) { let hclk = sys / config.ahb_pre; let (pclk1, pclk1_tim) = super::util::calc_pclk(hclk, config.apb1_pre); let (pclk2, pclk2_tim) = super::util::calc_pclk(hclk, config.apb2_pre); - + #[cfg(not(feature = "unchecked-overclocking"))] assert!(max::SYSCLK.contains(&sys)); + #[cfg(not(feature = "unchecked-overclocking"))] assert!(max::HCLK.contains(&hclk)); + #[cfg(not(feature = "unchecked-overclocking"))] assert!(max::PCLK1.contains(&pclk1)); + #[cfg(not(feature = "unchecked-overclocking"))] assert!(max::PCLK2.contains(&pclk2)); let rtc = config.ls.init(); diff --git a/embassy-stm32/src/rcc/g0.rs b/embassy-stm32/src/rcc/g0.rs index c53c83b0e..9e1351eae 100644 --- a/embassy-stm32/src/rcc/g0.rs +++ b/embassy-stm32/src/rcc/g0.rs @@ -139,9 +139,12 @@ pub(crate) unsafe fn init(config: Config) { None } Some(hse) => { - match hse.mode { - HseMode::Bypass => assert!(max::HSE_BYP.contains(&hse.freq)), - HseMode::Oscillator => assert!(max::HSE_OSC.contains(&hse.freq)), + #[cfg(not(feature = "unchecked-overclocking"))] + { + match hse.mode { + HseMode::Bypass => assert!(max::HSE_BYP.contains(&hse.freq)), + HseMode::Oscillator => assert!(max::HSE_OSC.contains(&hse.freq)), + } } RCC.cr().modify(|w| w.set_hsebyp(hse.mode != HseMode::Oscillator)); @@ -169,9 +172,10 @@ pub(crate) unsafe fn init(config: Config) { while RCC.cr().read().pllrdy() {} let in_freq = src_freq / pll_config.prediv; + #[cfg(not(feature = "unchecked-overclocking"))] assert!(max::PLL_IN.contains(&in_freq)); let internal_freq = in_freq * pll_config.mul; - + #[cfg(not(feature = "unchecked-overclocking"))] assert!(max::PLL_VCO.contains(&internal_freq)); RCC.pllcfgr().write(|w| { @@ -186,6 +190,7 @@ pub(crate) unsafe fn init(config: Config) { w.set_pllpen(true); }); let freq = internal_freq / div_p; + #[cfg(not(feature = "unchecked-overclocking"))] assert!(max::PLL_P.contains(&freq)); freq }); @@ -196,6 +201,7 @@ pub(crate) unsafe fn init(config: Config) { w.set_pllqen(true); }); let freq = internal_freq / div_q; + #[cfg(not(feature = "unchecked-overclocking"))] assert!(max::PLL_Q.contains(&freq)); freq }); @@ -206,6 +212,7 @@ pub(crate) unsafe fn init(config: Config) { w.set_pllren(true); }); let freq = internal_freq / div_r; + #[cfg(not(feature = "unchecked-overclocking"))] assert!(max::PLL_R.contains(&freq)); freq }); @@ -228,14 +235,16 @@ pub(crate) unsafe fn init(config: Config) { Sysclk::PLL1_R => unwrap!(pll.pll_r), _ => unreachable!(), }; - + #[cfg(not(feature = "unchecked-overclocking"))] assert!(max::SYSCLK.contains(&sys)); // Calculate the AHB frequency (HCLK), among other things so we can calculate the correct flash read latency. let hclk = sys / config.ahb_pre; + #[cfg(not(feature = "unchecked-overclocking"))] assert!(max::HCLK.contains(&hclk)); let (pclk1, pclk1_tim) = super::util::calc_pclk(hclk, config.apb1_pre); + #[cfg(not(feature = "unchecked-overclocking"))] assert!(max::PCLK.contains(&pclk1)); let latency = match (config.voltage_range, hclk.0) { diff --git a/embassy-stm32/src/rcc/g4.rs b/embassy-stm32/src/rcc/g4.rs index 16561f908..09b2a8de8 100644 --- a/embassy-stm32/src/rcc/g4.rs +++ b/embassy-stm32/src/rcc/g4.rs @@ -140,9 +140,12 @@ pub(crate) unsafe fn init(config: Config) { None } Some(hse) => { - match hse.mode { - HseMode::Bypass => assert!(max::HSE_BYP.contains(&hse.freq)), - HseMode::Oscillator => assert!(max::HSE_OSC.contains(&hse.freq)), + #[cfg(not(feature = "unchecked-overclocking"))] + { + match hse.mode { + HseMode::Bypass => assert!(max::HSE_BYP.contains(&hse.freq)), + HseMode::Oscillator => assert!(max::HSE_OSC.contains(&hse.freq)), + } } RCC.cr().modify(|w| w.set_hsebyp(hse.mode != HseMode::Oscillator)); @@ -169,9 +172,11 @@ pub(crate) unsafe fn init(config: Config) { while RCC.cr().read().pllrdy() {} let in_freq = src_freq / pll_config.prediv; + #[cfg(not(feature = "unchecked-overclocking"))] assert!(max::PLL_IN.contains(&in_freq)); let internal_freq = in_freq * pll_config.mul; + #[cfg(not(feature = "unchecked-overclocking"))] assert!(max::PLL_VCO.contains(&internal_freq)); RCC.pllcfgr().write(|w| { @@ -186,6 +191,7 @@ pub(crate) unsafe fn init(config: Config) { w.set_pllpen(true); }); let freq = internal_freq / div_p; + #[cfg(not(feature = "unchecked-overclocking"))] assert!(max::PLL_P.contains(&freq)); freq }); @@ -196,6 +202,7 @@ pub(crate) unsafe fn init(config: Config) { w.set_pllqen(true); }); let freq = internal_freq / div_q; + #[cfg(not(feature = "unchecked-overclocking"))] assert!(max::PLL_Q.contains(&freq)); freq }); @@ -206,6 +213,7 @@ pub(crate) unsafe fn init(config: Config) { w.set_pllren(true); }); let freq = internal_freq / div_r; + #[cfg(not(feature = "unchecked-overclocking"))] assert!(max::PLL_R.contains(&freq)); freq }); @@ -229,15 +237,17 @@ pub(crate) unsafe fn init(config: Config) { _ => unreachable!(), }; + #[cfg(not(feature = "unchecked-overclocking"))] assert!(max::SYSCLK.contains(&sys)); // Calculate the AHB frequency (HCLK), among other things so we can calculate the correct flash read latency. let hclk = sys / config.ahb_pre; + #[cfg(not(feature = "unchecked-overclocking"))] assert!(max::HCLK.contains(&hclk)); let (pclk1, pclk1_tim) = super::util::calc_pclk(hclk, config.apb1_pre); let (pclk2, pclk2_tim) = super::util::calc_pclk(hclk, config.apb2_pre); - assert!(max::PCLK.contains(&pclk2)); + #[cfg(not(feature = "unchecked-overclocking"))] assert!(max::PCLK.contains(&pclk2)); // Configure Core Boost mode ([RM0440] p234 – inverted because setting r1mode to 0 enables boost mode!) From 52ab015facf9f5039f89cd772d0f178ec70d878b Mon Sep 17 00:00:00 2001 From: Bing Wen Date: Wed, 27 Nov 2024 12:23:13 +0800 Subject: [PATCH 247/361] Add new --- embassy-stm32/src/fmt.rs | 19 +++++++++++++++++++ embassy-stm32/src/rcc/c0.rs | 18 ++++++------------ embassy-stm32/src/rcc/f013.rs | 29 +++++++++++------------------ embassy-stm32/src/rcc/f247.rs | 22 ++++++++-------------- embassy-stm32/src/rcc/g0.rs | 33 +++++++++++---------------------- embassy-stm32/src/rcc/g4.rs | 33 +++++++++++---------------------- 6 files changed, 66 insertions(+), 88 deletions(-) diff --git a/embassy-stm32/src/fmt.rs b/embassy-stm32/src/fmt.rs index 8ca61bc39..660407569 100644 --- a/embassy-stm32/src/fmt.rs +++ b/embassy-stm32/src/fmt.rs @@ -6,6 +6,25 @@ use core::fmt::{Debug, Display, LowerHex}; #[cfg(all(feature = "defmt", feature = "log"))] compile_error!("You may not enable both `defmt` and `log` features."); +#[collapse_debuginfo(yes)] +macro_rules! rcc_assert { + ($($x:tt)*) => { + { + if cfg!(feature = "unchecked-overclocking") { + #[cfg(not(feature = "defmt"))] + ::core::assert!($($x)*); + #[cfg(feature = "defmt")] + ::defmt::assert!($($x)*); + } else { + #[cfg(feature = "log")] + ::log::warn!("`rcc_assert!` skipped: `unchecked-overclocking` feature is enabled."); + #[cfg(feature = "defmt")] + ::defmt::warn!("`rcc_assert!` skipped: `unchecked-overclocking` feature is enabled."); + } + } + }; +} + #[collapse_debuginfo(yes)] macro_rules! assert { ($($x:tt)*) => { diff --git a/embassy-stm32/src/rcc/c0.rs b/embassy-stm32/src/rcc/c0.rs index c6ac46491..c989a3891 100644 --- a/embassy-stm32/src/rcc/c0.rs +++ b/embassy-stm32/src/rcc/c0.rs @@ -109,12 +109,9 @@ pub(crate) unsafe fn init(config: Config) { None } Some(hse) => { - #[cfg(not(feature = "unchecked-overclocking"))] - { - match hse.mode { - HseMode::Bypass => assert!(max::HSE_BYP.contains(&hse.freq)), - HseMode::Oscillator => assert!(max::HSE_OSC.contains(&hse.freq)), - } + match hse.mode { + HseMode::Bypass => rcc_assert!(max::HSE_BYP.contains(&hse.freq)), + HseMode::Oscillator => rcc_assert!(max::HSE_OSC.contains(&hse.freq)), } RCC.cr().modify(|w| w.set_hsebyp(hse.mode != HseMode::Oscillator)); @@ -129,17 +126,14 @@ pub(crate) unsafe fn init(config: Config) { Sysclk::HSE => unwrap!(hse), _ => unreachable!(), }; - #[cfg(not(feature = "unchecked-overclocking"))] - assert!(max::SYSCLK.contains(&sys)); + rcc_assert!(max::SYSCLK.contains(&sys)); // Calculate the AHB frequency (HCLK), among other things so we can calculate the correct flash read latency. let hclk = sys / config.ahb_pre; - #[cfg(not(feature = "unchecked-overclocking"))] - assert!(max::HCLK.contains(&hclk)); + rcc_assert!(max::HCLK.contains(&hclk)); let (pclk1, pclk1_tim) = super::util::calc_pclk(hclk, config.apb1_pre); - #[cfg(not(feature = "unchecked-overclocking"))] - assert!(max::PCLK.contains(&pclk1)); + rcc_assert(max::PCLK.contains(&pclk1)); let latency = match hclk.0 { ..=24_000_000 => Latency::WS0, diff --git a/embassy-stm32/src/rcc/f013.rs b/embassy-stm32/src/rcc/f013.rs index 20a0f3940..cfe44ce54 100644 --- a/embassy-stm32/src/rcc/f013.rs +++ b/embassy-stm32/src/rcc/f013.rs @@ -157,12 +157,9 @@ pub(crate) unsafe fn init(config: Config) { None } Some(hse) => { - #[cfg(not(feature = "unchecked-overclocking"))] - { - match hse.mode { - HseMode::Bypass => assert!(max::HSE_BYP.contains(&hse.freq)), - HseMode::Oscillator => assert!(max::HSE_OSC.contains(&hse.freq)), - } + match hse.mode { + HseMode::Bypass => rcc_assert!(max::HSE_BYP.contains(&hse.freq)), + HseMode::Oscillator => rcc_assert!(max::HSE_OSC.contains(&hse.freq)), } RCC.cr().modify(|w| w.set_hsebyp(hse.mode != HseMode::Oscillator)); @@ -195,11 +192,9 @@ pub(crate) unsafe fn init(config: Config) { PllSource::HSI48 => (Pllsrc::HSI48_DIV_PREDIV, unwrap!(hsi48)), }; let in_freq = src_freq / pll.prediv; - #[cfg(not(feature = "unchecked-overclocking"))] - assert!(max::PLL_IN.contains(&in_freq)); - #[cfg(not(feature = "unchecked-overclocking"))] + rcc_assert!(max::PLL_IN.contains(&in_freq)); let out_freq = in_freq * pll.mul; - assert!(max::PLL_OUT.contains(&out_freq)); + rcc_assert!(max::PLL_OUT.contains(&out_freq)); #[cfg(not(stm32f1))] RCC.cfgr2().modify(|w| w.set_prediv(pll.prediv)); @@ -243,17 +238,15 @@ pub(crate) unsafe fn init(config: Config) { let (pclk2, pclk2_tim) = super::util::calc_pclk(hclk, config.apb2_pre); #[cfg(stm32f0)] let (pclk2, pclk2_tim) = (pclk1, pclk1_tim); - #[cfg(not(feature = "unchecked-overclocking"))] - assert!(max::HCLK.contains(&hclk)); - #[cfg(not(feature = "unchecked-overclocking"))] - assert!(max::PCLK1.contains(&pclk1)); - #[cfg(all(not(feature = "unchecked-overclocking"), not(stm32f0)))] - assert!(max::PCLK2.contains(&pclk2)); + rcc_assert!(max::HCLK.contains(&hclk)); + rcc_assert!(max::PCLK1.contains(&pclk1)); + #[cfg(not(stm32f0))] + rcc_assert!(max::PCLK2.contains(&pclk2)); #[cfg(stm32f1)] let adc = pclk2 / config.adc_pre; - #[cfg(all(not(feature = "unchecked-overclocking"), stm32f1))] - assert!(max::ADC.contains(&adc)); + #[cfg(stm32f1)] + rcc_assert!(max::ADC.contains(&adc)); // Set latency based on HCLK frquency #[cfg(stm32f0)] diff --git a/embassy-stm32/src/rcc/f247.rs b/embassy-stm32/src/rcc/f247.rs index 83d4428cc..3e7aff02d 100644 --- a/embassy-stm32/src/rcc/f247.rs +++ b/embassy-stm32/src/rcc/f247.rs @@ -169,12 +169,9 @@ pub(crate) unsafe fn init(config: Config) { None } Some(hse) => { - #[cfg(not(feature = "unchecked-overclocking"))] - { - match hse.mode { - HseMode::Bypass => assert!(max::HSE_BYP.contains(&hse.freq)), - HseMode::Oscillator => assert!(max::HSE_OSC.contains(&hse.freq)), - } + match hse.mode { + HseMode::Bypass => rcc_assert!(max::HSE_BYP.contains(&hse.freq)), + HseMode::Oscillator => rcc_assert!(max::HSE_OSC.contains(&hse.freq)), } RCC.cr().modify(|w| w.set_hsebyp(hse.mode != HseMode::Oscillator)); @@ -207,14 +204,11 @@ pub(crate) unsafe fn init(config: Config) { let hclk = sys / config.ahb_pre; let (pclk1, pclk1_tim) = super::util::calc_pclk(hclk, config.apb1_pre); let (pclk2, pclk2_tim) = super::util::calc_pclk(hclk, config.apb2_pre); - #[cfg(not(feature = "unchecked-overclocking"))] - assert!(max::SYSCLK.contains(&sys)); - #[cfg(not(feature = "unchecked-overclocking"))] - assert!(max::HCLK.contains(&hclk)); - #[cfg(not(feature = "unchecked-overclocking"))] - assert!(max::PCLK1.contains(&pclk1)); - #[cfg(not(feature = "unchecked-overclocking"))] - assert!(max::PCLK2.contains(&pclk2)); + + rcc_assert!(max::SYSCLK.contains(&sys)); + rcc_assert!(max::HCLK.contains(&hclk)); + rcc_assert!(max::PCLK1.contains(&pclk1)); + rcc_assert!(max::PCLK2.contains(&pclk2)); let rtc = config.ls.init(); diff --git a/embassy-stm32/src/rcc/g0.rs b/embassy-stm32/src/rcc/g0.rs index 9e1351eae..71e524a20 100644 --- a/embassy-stm32/src/rcc/g0.rs +++ b/embassy-stm32/src/rcc/g0.rs @@ -139,12 +139,9 @@ pub(crate) unsafe fn init(config: Config) { None } Some(hse) => { - #[cfg(not(feature = "unchecked-overclocking"))] - { - match hse.mode { - HseMode::Bypass => assert!(max::HSE_BYP.contains(&hse.freq)), - HseMode::Oscillator => assert!(max::HSE_OSC.contains(&hse.freq)), - } + match hse.mode { + HseMode::Bypass => rcc_assert!(max::HSE_BYP.contains(&hse.freq)), + HseMode::Oscillator => rcc_assert!(max::HSE_OSC.contains(&hse.freq)), } RCC.cr().modify(|w| w.set_hsebyp(hse.mode != HseMode::Oscillator)); @@ -172,11 +169,9 @@ pub(crate) unsafe fn init(config: Config) { while RCC.cr().read().pllrdy() {} let in_freq = src_freq / pll_config.prediv; - #[cfg(not(feature = "unchecked-overclocking"))] - assert!(max::PLL_IN.contains(&in_freq)); + rcc_assert!(max::PLL_IN.contains(&in_freq)); let internal_freq = in_freq * pll_config.mul; - #[cfg(not(feature = "unchecked-overclocking"))] - assert!(max::PLL_VCO.contains(&internal_freq)); + rcc_assert!(max::PLL_VCO.contains(&internal_freq)); RCC.pllcfgr().write(|w| { w.set_plln(pll_config.mul); @@ -190,8 +185,7 @@ pub(crate) unsafe fn init(config: Config) { w.set_pllpen(true); }); let freq = internal_freq / div_p; - #[cfg(not(feature = "unchecked-overclocking"))] - assert!(max::PLL_P.contains(&freq)); + rcc_assert!(max::PLL_P.contains(&freq)); freq }); @@ -201,8 +195,7 @@ pub(crate) unsafe fn init(config: Config) { w.set_pllqen(true); }); let freq = internal_freq / div_q; - #[cfg(not(feature = "unchecked-overclocking"))] - assert!(max::PLL_Q.contains(&freq)); + rcc_assert!(max::PLL_Q.contains(&freq)); freq }); @@ -212,8 +205,7 @@ pub(crate) unsafe fn init(config: Config) { w.set_pllren(true); }); let freq = internal_freq / div_r; - #[cfg(not(feature = "unchecked-overclocking"))] - assert!(max::PLL_R.contains(&freq)); + rcc_assert!(max::PLL_R.contains(&freq)); freq }); @@ -235,17 +227,14 @@ pub(crate) unsafe fn init(config: Config) { Sysclk::PLL1_R => unwrap!(pll.pll_r), _ => unreachable!(), }; - #[cfg(not(feature = "unchecked-overclocking"))] - assert!(max::SYSCLK.contains(&sys)); + rcc_assert!(max::SYSCLK.contains(&sys)); // Calculate the AHB frequency (HCLK), among other things so we can calculate the correct flash read latency. let hclk = sys / config.ahb_pre; - #[cfg(not(feature = "unchecked-overclocking"))] - assert!(max::HCLK.contains(&hclk)); + rcc_assert!(max::HCLK.contains(&hclk)); let (pclk1, pclk1_tim) = super::util::calc_pclk(hclk, config.apb1_pre); - #[cfg(not(feature = "unchecked-overclocking"))] - assert!(max::PCLK.contains(&pclk1)); + rcc_assert!(max::PCLK.contains(&pclk1)); let latency = match (config.voltage_range, hclk.0) { (VoltageRange::RANGE1, ..=24_000_000) => Latency::WS0, diff --git a/embassy-stm32/src/rcc/g4.rs b/embassy-stm32/src/rcc/g4.rs index 09b2a8de8..e09b2915e 100644 --- a/embassy-stm32/src/rcc/g4.rs +++ b/embassy-stm32/src/rcc/g4.rs @@ -140,12 +140,9 @@ pub(crate) unsafe fn init(config: Config) { None } Some(hse) => { - #[cfg(not(feature = "unchecked-overclocking"))] - { - match hse.mode { - HseMode::Bypass => assert!(max::HSE_BYP.contains(&hse.freq)), - HseMode::Oscillator => assert!(max::HSE_OSC.contains(&hse.freq)), - } + match hse.mode { + HseMode::Bypass => rcc_assert!(max::HSE_BYP.contains(&hse.freq)), + HseMode::Oscillator => rcc_assert!(max::HSE_OSC.contains(&hse.freq)), } RCC.cr().modify(|w| w.set_hsebyp(hse.mode != HseMode::Oscillator)); @@ -172,12 +169,10 @@ pub(crate) unsafe fn init(config: Config) { while RCC.cr().read().pllrdy() {} let in_freq = src_freq / pll_config.prediv; - #[cfg(not(feature = "unchecked-overclocking"))] - assert!(max::PLL_IN.contains(&in_freq)); + rcc_assert!(max::PLL_IN.contains(&in_freq)); let internal_freq = in_freq * pll_config.mul; - #[cfg(not(feature = "unchecked-overclocking"))] - assert!(max::PLL_VCO.contains(&internal_freq)); + rcc_assert!(max::PLL_VCO.contains(&internal_freq)); RCC.pllcfgr().write(|w| { w.set_plln(pll_config.mul); @@ -191,8 +186,7 @@ pub(crate) unsafe fn init(config: Config) { w.set_pllpen(true); }); let freq = internal_freq / div_p; - #[cfg(not(feature = "unchecked-overclocking"))] - assert!(max::PLL_P.contains(&freq)); + rcc_assert!(max::PLL_P.contains(&freq)); freq }); @@ -202,8 +196,7 @@ pub(crate) unsafe fn init(config: Config) { w.set_pllqen(true); }); let freq = internal_freq / div_q; - #[cfg(not(feature = "unchecked-overclocking"))] - assert!(max::PLL_Q.contains(&freq)); + rcc_assert!(max::PLL_Q.contains(&freq)); freq }); @@ -213,8 +206,7 @@ pub(crate) unsafe fn init(config: Config) { w.set_pllren(true); }); let freq = internal_freq / div_r; - #[cfg(not(feature = "unchecked-overclocking"))] - assert!(max::PLL_R.contains(&freq)); + rcc_assert!(max::PLL_R.contains(&freq)); freq }); @@ -237,18 +229,15 @@ pub(crate) unsafe fn init(config: Config) { _ => unreachable!(), }; - #[cfg(not(feature = "unchecked-overclocking"))] - assert!(max::SYSCLK.contains(&sys)); + rcc_assert!(max::SYSCLK.contains(&sys)); // Calculate the AHB frequency (HCLK), among other things so we can calculate the correct flash read latency. let hclk = sys / config.ahb_pre; - #[cfg(not(feature = "unchecked-overclocking"))] - assert!(max::HCLK.contains(&hclk)); + rcc_assert!(max::HCLK.contains(&hclk)); let (pclk1, pclk1_tim) = super::util::calc_pclk(hclk, config.apb1_pre); let (pclk2, pclk2_tim) = super::util::calc_pclk(hclk, config.apb2_pre); - #[cfg(not(feature = "unchecked-overclocking"))] - assert!(max::PCLK.contains(&pclk2)); + rcc_assert!(max::PCLK.contains(&pclk2)); // Configure Core Boost mode ([RM0440] p234 – inverted because setting r1mode to 0 enables boost mode!) if config.boost { From d0340ad2971232b505be13a1a5e353674d838c35 Mon Sep 17 00:00:00 2001 From: Bing Wen Date: Wed, 27 Nov 2024 12:33:32 +0800 Subject: [PATCH 248/361] Fix & Revert --- embassy-stm32/src/rcc/c0.rs | 3 ++- embassy-stm32/src/rcc/f013.rs | 1 + embassy-stm32/src/rcc/g0.rs | 1 + 3 files changed, 4 insertions(+), 1 deletion(-) diff --git a/embassy-stm32/src/rcc/c0.rs b/embassy-stm32/src/rcc/c0.rs index c989a3891..977b2e7a2 100644 --- a/embassy-stm32/src/rcc/c0.rs +++ b/embassy-stm32/src/rcc/c0.rs @@ -126,6 +126,7 @@ pub(crate) unsafe fn init(config: Config) { Sysclk::HSE => unwrap!(hse), _ => unreachable!(), }; + rcc_assert!(max::SYSCLK.contains(&sys)); // Calculate the AHB frequency (HCLK), among other things so we can calculate the correct flash read latency. @@ -133,7 +134,7 @@ pub(crate) unsafe fn init(config: Config) { rcc_assert!(max::HCLK.contains(&hclk)); let (pclk1, pclk1_tim) = super::util::calc_pclk(hclk, config.apb1_pre); - rcc_assert(max::PCLK.contains(&pclk1)); + rcc_assert!(max::PCLK.contains(&pclk1)); let latency = match hclk.0 { ..=24_000_000 => Latency::WS0, diff --git a/embassy-stm32/src/rcc/f013.rs b/embassy-stm32/src/rcc/f013.rs index cfe44ce54..c915f1b16 100644 --- a/embassy-stm32/src/rcc/f013.rs +++ b/embassy-stm32/src/rcc/f013.rs @@ -238,6 +238,7 @@ pub(crate) unsafe fn init(config: Config) { let (pclk2, pclk2_tim) = super::util::calc_pclk(hclk, config.apb2_pre); #[cfg(stm32f0)] let (pclk2, pclk2_tim) = (pclk1, pclk1_tim); + rcc_assert!(max::HCLK.contains(&hclk)); rcc_assert!(max::PCLK1.contains(&pclk1)); #[cfg(not(stm32f0))] diff --git a/embassy-stm32/src/rcc/g0.rs b/embassy-stm32/src/rcc/g0.rs index 71e524a20..5da33720c 100644 --- a/embassy-stm32/src/rcc/g0.rs +++ b/embassy-stm32/src/rcc/g0.rs @@ -227,6 +227,7 @@ pub(crate) unsafe fn init(config: Config) { Sysclk::PLL1_R => unwrap!(pll.pll_r), _ => unreachable!(), }; + rcc_assert!(max::SYSCLK.contains(&sys)); // Calculate the AHB frequency (HCLK), among other things so we can calculate the correct flash read latency. From b225d73dc5ea6632c35d5ba063467e8b32abd14c Mon Sep 17 00:00:00 2001 From: Bing Wen Date: Wed, 27 Nov 2024 14:00:45 +0800 Subject: [PATCH 249/361] Change compile condition --- embassy-stm32/src/fmt.rs | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/embassy-stm32/src/fmt.rs b/embassy-stm32/src/fmt.rs index 660407569..b6ae24ee8 100644 --- a/embassy-stm32/src/fmt.rs +++ b/embassy-stm32/src/fmt.rs @@ -10,12 +10,15 @@ compile_error!("You may not enable both `defmt` and `log` features."); macro_rules! rcc_assert { ($($x:tt)*) => { { - if cfg!(feature = "unchecked-overclocking") { + #[cfg(not(feature = "unchecked-overclocking"))] + { #[cfg(not(feature = "defmt"))] ::core::assert!($($x)*); #[cfg(feature = "defmt")] ::defmt::assert!($($x)*); - } else { + } + #[cfg(feature = "unchecked-overclocking")] + { #[cfg(feature = "log")] ::log::warn!("`rcc_assert!` skipped: `unchecked-overclocking` feature is enabled."); #[cfg(feature = "defmt")] From 09c9f64b8e0bf12cccc0474fb689c313d2b58ff3 Mon Sep 17 00:00:00 2001 From: Bing Wen Date: Wed, 27 Nov 2024 17:44:03 +0800 Subject: [PATCH 250/361] Add missing clock check --- embassy-stm32/src/rcc/g4.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/embassy-stm32/src/rcc/g4.rs b/embassy-stm32/src/rcc/g4.rs index e09b2915e..6b34aa306 100644 --- a/embassy-stm32/src/rcc/g4.rs +++ b/embassy-stm32/src/rcc/g4.rs @@ -237,6 +237,7 @@ pub(crate) unsafe fn init(config: Config) { let (pclk1, pclk1_tim) = super::util::calc_pclk(hclk, config.apb1_pre); let (pclk2, pclk2_tim) = super::util::calc_pclk(hclk, config.apb2_pre); + rcc_assert!(max::PCLK.contains(&pclk1)); rcc_assert!(max::PCLK.contains(&pclk2)); // Configure Core Boost mode ([RM0440] p234 – inverted because setting r1mode to 0 enables boost mode!) From 7a9c4889960aa2fdc48af99d3765a6277da30e8e Mon Sep 17 00:00:00 2001 From: William <174336620+williams-one@users.noreply.github.com> Date: Wed, 27 Nov 2024 16:52:26 +0100 Subject: [PATCH 251/361] stm32: Update STM32 data source --- 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 f6ffa29fa..09b7f805a 100644 --- a/embassy-stm32/Cargo.toml +++ b/embassy-stm32/Cargo.toml @@ -72,7 +72,7 @@ rand_core = "0.6.3" sdio-host = "0.5.0" critical-section = "1.1" #stm32-metapac = { version = "15" } -stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-04833817666290047257c65c6547d28e1bd10dc9" } +stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-ab0ec4c19f81854189bab8215544ccd1256e1045" } vcell = "0.1.3" nb = "1.0.0" @@ -101,7 +101,7 @@ proc-macro2 = "1.0.36" quote = "1.0.15" #stm32-metapac = { version = "15", default-features = false, features = ["metadata"]} -stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-04833817666290047257c65c6547d28e1bd10dc9", default-features = false, features = ["metadata"] } +stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-ab0ec4c19f81854189bab8215544ccd1256e1045", default-features = false, features = ["metadata"] } [features] default = ["rt"] From b035ff114502b0e63f578441250b6ff94c5efda1 Mon Sep 17 00:00:00 2001 From: William <174336620+williams-one@users.noreply.github.com> Date: Wed, 27 Nov 2024 17:29:08 +0100 Subject: [PATCH 252/361] stm32u5: Add flash bank selection when erasing a sector --- embassy-stm32/src/flash/u5.rs | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/embassy-stm32/src/flash/u5.rs b/embassy-stm32/src/flash/u5.rs index 0601017ce..e5af4f1f7 100644 --- a/embassy-stm32/src/flash/u5.rs +++ b/embassy-stm32/src/flash/u5.rs @@ -1,7 +1,7 @@ use core::ptr::write_volatile; use core::sync::atomic::{fence, Ordering}; -use super::{FlashRegion, FlashSector, FLASH_REGIONS, WRITE_SIZE}; +use super::{FlashBank, FlashRegion, FlashSector, FLASH_REGIONS, WRITE_SIZE}; use crate::flash::Error; use crate::pac; @@ -70,12 +70,22 @@ pub(crate) unsafe fn blocking_erase_sector(sector: &FlashSector) -> Result<(), E #[cfg(feature = "trustzone-secure")] pac::FLASH.seccr().modify(|w| { w.set_per(pac::flash::vals::SeccrPer::B_0X1); - w.set_pnb(sector.index_in_bank) + w.set_pnb(sector.index_in_bank); + // TODO: add check for bank swap + w.set_bker(match sector.bank { + FlashBank::Bank1 => pac::flash::vals::SeccrBker::B_0X0, + FlashBank::Bank2 => pac::flash::vals::SeccrBker::B_0X1, + }); }); #[cfg(not(feature = "trustzone-secure"))] pac::FLASH.nscr().modify(|w| { w.set_per(pac::flash::vals::NscrPer::B_0X1); - w.set_pnb(sector.index_in_bank) + w.set_pnb(sector.index_in_bank); + // TODO: add check for bank swap + w.set_bker(match sector.bank { + FlashBank::Bank1 => pac::flash::vals::NscrBker::B_0X0, + FlashBank::Bank2 => pac::flash::vals::NscrBker::B_0X1, + }); }); #[cfg(feature = "trustzone-secure")] From 8b8b7fa05ef0524a337a050584835026afa3015a Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Thu, 28 Nov 2024 01:30:27 +0100 Subject: [PATCH 253/361] net: update to smoltcp v0.12.0 --- embassy-net/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/embassy-net/Cargo.toml b/embassy-net/Cargo.toml index 2c33ecc14..23748973a 100644 --- a/embassy-net/Cargo.toml +++ b/embassy-net/Cargo.toml @@ -68,7 +68,7 @@ multicast = ["smoltcp/multicast"] defmt = { version = "0.3.8", optional = true } log = { version = "0.4.14", optional = true } -smoltcp = { git="https://github.com/smoltcp-rs/smoltcp", rev="fe0b4d102253465850cd1cf39cd33d4721a4a8d5", default-features = false, features = [ +smoltcp = { version = "0.12.0", default-features = false, features = [ "socket", "async", ] } From c12ebb3a80fec9a98793d89c43c2193b4607ee91 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Thu, 28 Nov 2024 01:33:23 +0100 Subject: [PATCH 254/361] net: release v0.5.0 --- embassy-net/CHANGELOG.md | 28 ++++++++++++++++++++++++++++ embassy-net/Cargo.toml | 2 +- examples/nrf52840/Cargo.toml | 2 +- examples/nrf5340/Cargo.toml | 2 +- examples/nrf9160/Cargo.toml | 2 +- examples/rp/Cargo.toml | 2 +- examples/rp23/Cargo.toml | 2 +- examples/std/Cargo.toml | 2 +- examples/stm32f4/Cargo.toml | 2 +- examples/stm32f7/Cargo.toml | 2 +- examples/stm32h5/Cargo.toml | 2 +- examples/stm32h7/Cargo.toml | 2 +- examples/stm32h755cm4/Cargo.toml | 2 +- examples/stm32h755cm7/Cargo.toml | 2 +- examples/stm32h7b0/Cargo.toml | 2 +- examples/stm32h7rs/Cargo.toml | 2 +- examples/stm32l4/Cargo.toml | 2 +- examples/stm32l5/Cargo.toml | 2 +- examples/stm32wb/Cargo.toml | 2 +- examples/stm32wba/Cargo.toml | 2 +- tests/nrf/Cargo.toml | 2 +- tests/perf-client/Cargo.toml | 2 +- tests/rp/Cargo.toml | 2 +- tests/stm32/Cargo.toml | 2 +- 24 files changed, 51 insertions(+), 23 deletions(-) diff --git a/embassy-net/CHANGELOG.md b/embassy-net/CHANGELOG.md index 56e245b92..c4ccff277 100644 --- a/embassy-net/CHANGELOG.md +++ b/embassy-net/CHANGELOG.md @@ -7,6 +7,34 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## Unreleased +No unreleased changes yet... Quick, go send a PR! + +## 0.5 - 2024-11-28 + +- Refactor the API structure, simplifying lifetimes and generics. + - Stack is now a thin handle that implements `Copy+Clone`. Instead of passing `&Stack` around, you can now pass `Stack`. + - `Stack` and `DnsSocket` no longer need a generic parameter for the device driver. + - The `run()` method has been moved to a new `Runner` struct. + - Sockets are covariant wrt their lifetime. + - An implication of the refactor is now you need only one `StaticCell` instead of two if you need to share the network stack between tasks. +- Use standard `core::net` IP types instead of custom ones from smoltcp. +- Update to `smoltcp` v0.12. +- Add `mdns` Cargo feature. +- dns: properly handle `AddrType::Either` in `get_host_by_name()` +- dns: truncate instead of panic if the DHCP server gives us more DNS servers than the configured maximum. +- stack: add `wait_link_up()`, `wait_link_down()`, `wait_config_down()`. +- tcp: Add `recv_queue()`, `send_queue()`. +- tcp: Add `wait_read_ready()`, `wait_write_ready()`. +- tcp: allow setting timeout through `embedded-nal` client. +- tcp: fix `flush()` hanging forever if socket is closed with pending data. +- tcp: fix `flush()` not waiting for ACK of FIN. +- tcp: implement `ReadReady`, `WriteReady` traits from `embedded-io`. +- udp, raw: Add `wait_send_ready()`, `wait_recv_ready()`, `flush()`. +- udp: add `recv_from_with()`, `send_to_with()` methods, allowing for IO with one less copy. +- udp: send/recv now takes/returns full `UdpMetadata` instead of just the remote `IpEndpoint`. +- raw: add raw sockets. + + ## 0.4 - 2024-01-11 - Update to `embassy-time` v0.3. diff --git a/embassy-net/Cargo.toml b/embassy-net/Cargo.toml index 23748973a..1d79a2a07 100644 --- a/embassy-net/Cargo.toml +++ b/embassy-net/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "embassy-net" -version = "0.4.0" +version = "0.5.0" edition = "2021" license = "MIT OR Apache-2.0" description = "Async TCP/IP network stack for embedded systems" diff --git a/examples/nrf52840/Cargo.toml b/examples/nrf52840/Cargo.toml index 9623c04b5..701911a30 100644 --- a/examples/nrf52840/Cargo.toml +++ b/examples/nrf52840/Cargo.toml @@ -10,7 +10,7 @@ embassy-sync = { version = "0.6.1", path = "../../embassy-sync", features = ["de embassy-executor = { version = "0.6.3", path = "../../embassy-executor", features = ["task-arena-size-32768", "arch-cortex-m", "executor-thread", "executor-interrupt", "defmt", "integrated-timers"] } embassy-time = { version = "0.3.2", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime"] } embassy-nrf = { version = "0.2.0", path = "../../embassy-nrf", features = ["defmt", "nrf52840", "time-driver-rtc1", "gpiote", "unstable-pac", "time"] } -embassy-net = { version = "0.4.0", path = "../../embassy-net", features = ["defmt", "tcp", "dhcpv4", "medium-ethernet"] } +embassy-net = { version = "0.5.0", path = "../../embassy-net", features = ["defmt", "tcp", "dhcpv4", "medium-ethernet"] } embassy-usb = { version = "0.3.0", path = "../../embassy-usb", features = ["defmt"] } embedded-io = { version = "0.6.0", features = ["defmt-03"] } embedded-io-async = { version = "0.6.1", features = ["defmt-03"] } diff --git a/examples/nrf5340/Cargo.toml b/examples/nrf5340/Cargo.toml index 8c8f74d15..13442405d 100644 --- a/examples/nrf5340/Cargo.toml +++ b/examples/nrf5340/Cargo.toml @@ -10,7 +10,7 @@ embassy-sync = { version = "0.6.1", path = "../../embassy-sync", features = ["de embassy-executor = { version = "0.6.3", path = "../../embassy-executor", features = ["task-arena-size-32768", "arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] } embassy-time = { version = "0.3.2", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime"] } embassy-nrf = { version = "0.2.0", path = "../../embassy-nrf", features = ["defmt", "nrf5340-app-s", "time-driver-rtc1", "gpiote", "unstable-pac"] } -embassy-net = { version = "0.4.0", path = "../../embassy-net", features = ["defmt", "tcp", "dhcpv4", "medium-ethernet"] } +embassy-net = { version = "0.5.0", path = "../../embassy-net", features = ["defmt", "tcp", "dhcpv4", "medium-ethernet"] } embassy-usb = { version = "0.3.0", path = "../../embassy-usb", features = ["defmt"] } embedded-io-async = { version = "0.6.1" } diff --git a/examples/nrf9160/Cargo.toml b/examples/nrf9160/Cargo.toml index 47eba5552..3b404c730 100644 --- a/examples/nrf9160/Cargo.toml +++ b/examples/nrf9160/Cargo.toml @@ -9,7 +9,7 @@ embassy-executor = { version = "0.6.3", path = "../../embassy-executor", feature embassy-time = { version = "0.3.2", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime"] } embassy-nrf = { version = "0.2.0", path = "../../embassy-nrf", features = ["defmt", "nrf9160-s", "time-driver-rtc1", "gpiote", "unstable-pac", "time"] } embassy-net-nrf91 = { version = "0.1.0", path = "../../embassy-net-nrf91", features = ["defmt"] } -embassy-net = { version = "0.4.0", path = "../../embassy-net", features = ["defmt", "tcp", "proto-ipv4", "medium-ip"] } +embassy-net = { version = "0.5.0", path = "../../embassy-net", features = ["defmt", "tcp", "proto-ipv4", "medium-ip"] } defmt = "0.3" defmt-rtt = "0.4" diff --git a/examples/rp/Cargo.toml b/examples/rp/Cargo.toml index 1448306a1..2dce1676a 100644 --- a/examples/rp/Cargo.toml +++ b/examples/rp/Cargo.toml @@ -12,7 +12,7 @@ embassy-executor = { version = "0.6.3", path = "../../embassy-executor", feature embassy-time = { version = "0.3.2", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime"] } embassy-rp = { version = "0.2.0", path = "../../embassy-rp", features = ["defmt", "unstable-pac", "time-driver", "critical-section-impl", "rp2040"] } embassy-usb = { version = "0.3.0", path = "../../embassy-usb", features = ["defmt"] } -embassy-net = { version = "0.4.0", path = "../../embassy-net", features = ["defmt", "tcp", "udp", "raw", "dhcpv4", "medium-ethernet", "dns", "proto-ipv4", "proto-ipv6", "multicast"] } +embassy-net = { version = "0.5.0", path = "../../embassy-net", features = ["defmt", "tcp", "udp", "raw", "dhcpv4", "medium-ethernet", "dns", "proto-ipv4", "proto-ipv6", "multicast"] } embassy-net-wiznet = { version = "0.1.0", path = "../../embassy-net-wiznet", features = ["defmt"] } embassy-futures = { version = "0.1.0", path = "../../embassy-futures" } embassy-usb-logger = { version = "0.2.0", path = "../../embassy-usb-logger" } diff --git a/examples/rp23/Cargo.toml b/examples/rp23/Cargo.toml index 6738339c0..2fcad247d 100644 --- a/examples/rp23/Cargo.toml +++ b/examples/rp23/Cargo.toml @@ -12,7 +12,7 @@ embassy-executor = { version = "0.6.3", path = "../../embassy-executor", feature embassy-time = { version = "0.3.2", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime"] } embassy-rp = { version = "0.2.0", path = "../../embassy-rp", features = ["defmt", "unstable-pac", "time-driver", "critical-section-impl", "rp235xa", "binary-info"] } embassy-usb = { version = "0.3.0", path = "../../embassy-usb", features = ["defmt"] } -embassy-net = { version = "0.4.0", path = "../../embassy-net", features = ["defmt", "tcp", "udp", "raw", "dhcpv4", "medium-ethernet", "dns"] } +embassy-net = { version = "0.5.0", path = "../../embassy-net", features = ["defmt", "tcp", "udp", "raw", "dhcpv4", "medium-ethernet", "dns"] } embassy-net-wiznet = { version = "0.1.0", path = "../../embassy-net-wiznet", features = ["defmt"] } embassy-futures = { version = "0.1.0", path = "../../embassy-futures" } embassy-usb-logger = { version = "0.2.0", path = "../../embassy-usb-logger" } diff --git a/examples/std/Cargo.toml b/examples/std/Cargo.toml index f6b209d06..77948515a 100644 --- a/examples/std/Cargo.toml +++ b/examples/std/Cargo.toml @@ -8,7 +8,7 @@ license = "MIT OR Apache-2.0" embassy-sync = { version = "0.6.1", path = "../../embassy-sync", features = ["log"] } embassy-executor = { version = "0.6.3", path = "../../embassy-executor", features = ["task-arena-size-32768", "arch-std", "executor-thread", "log", "integrated-timers"] } embassy-time = { version = "0.3.2", path = "../../embassy-time", features = ["log", "std", ] } -embassy-net = { version = "0.4.0", path = "../../embassy-net", features=[ "std", "log", "medium-ethernet", "medium-ip", "tcp", "udp", "dns", "dhcpv4", "proto-ipv6"] } +embassy-net = { version = "0.5.0", path = "../../embassy-net", features=[ "std", "log", "medium-ethernet", "medium-ip", "tcp", "udp", "dns", "dhcpv4", "proto-ipv6"] } embassy-net-tuntap = { version = "0.1.0", path = "../../embassy-net-tuntap" } embassy-net-ppp = { version = "0.1.0", path = "../../embassy-net-ppp", features = ["log"]} embedded-io-async = { version = "0.6.1" } diff --git a/examples/stm32f4/Cargo.toml b/examples/stm32f4/Cargo.toml index 435b0b43c..2a0b7c507 100644 --- a/examples/stm32f4/Cargo.toml +++ b/examples/stm32f4/Cargo.toml @@ -11,7 +11,7 @@ embassy-sync = { version = "0.6.1", path = "../../embassy-sync", features = ["de embassy-executor = { version = "0.6.3", path = "../../embassy-executor", features = ["task-arena-size-32768", "arch-cortex-m", "executor-thread", "executor-interrupt", "defmt", "integrated-timers"] } embassy-time = { version = "0.3.2", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } embassy-usb = { version = "0.3.0", path = "../../embassy-usb", features = ["defmt" ] } -embassy-net = { version = "0.4.0", path = "../../embassy-net", features = ["defmt", "tcp", "dhcpv4", "medium-ethernet", ] } +embassy-net = { version = "0.5.0", path = "../../embassy-net", features = ["defmt", "tcp", "dhcpv4", "medium-ethernet", ] } embassy-net-wiznet = { version = "0.1.0", path = "../../embassy-net-wiznet", features = ["defmt"] } embassy-futures = { version = "0.1.0", path = "../../embassy-futures" } diff --git a/examples/stm32f7/Cargo.toml b/examples/stm32f7/Cargo.toml index 822d8152d..480694dca 100644 --- a/examples/stm32f7/Cargo.toml +++ b/examples/stm32f7/Cargo.toml @@ -10,7 +10,7 @@ embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = [" embassy-sync = { version = "0.6.1", path = "../../embassy-sync", features = ["defmt"] } embassy-executor = { version = "0.6.3", path = "../../embassy-executor", features = ["task-arena-size-32768", "arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] } embassy-time = { version = "0.3.2", 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"] } +embassy-net = { version = "0.5.0", path = "../../embassy-net", features = ["defmt", "tcp", "dhcpv4", "medium-ethernet"] } embedded-io-async = { version = "0.6.1" } embassy-usb = { version = "0.3.0", path = "../../embassy-usb", features = ["defmt"] } embassy-futures = { version = "0.1.0", path = "../../embassy-futures" } diff --git a/examples/stm32h5/Cargo.toml b/examples/stm32h5/Cargo.toml index 91ca43845..1a5791c83 100644 --- a/examples/stm32h5/Cargo.toml +++ b/examples/stm32h5/Cargo.toml @@ -10,7 +10,7 @@ embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = [" embassy-sync = { version = "0.6.1", path = "../../embassy-sync", features = ["defmt"] } embassy-executor = { version = "0.6.3", path = "../../embassy-executor", features = ["task-arena-size-32768", "arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] } embassy-time = { version = "0.3.2", 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"] } +embassy-net = { version = "0.5.0", path = "../../embassy-net", features = ["defmt", "tcp", "dhcpv4", "medium-ethernet", "proto-ipv6"] } embassy-usb = { version = "0.3.0", path = "../../embassy-usb", features = ["defmt"] } embassy-futures = { version = "0.1.0", path = "../../embassy-futures" } diff --git a/examples/stm32h7/Cargo.toml b/examples/stm32h7/Cargo.toml index 949cefe1e..b90a6a455 100644 --- a/examples/stm32h7/Cargo.toml +++ b/examples/stm32h7/Cargo.toml @@ -11,7 +11,7 @@ embassy-sync = { version = "0.6.1", path = "../../embassy-sync", features = ["de embassy-embedded-hal = { version = "0.2.0", path = "../../embassy-embedded-hal" } embassy-executor = { version = "0.6.3", path = "../../embassy-executor", features = ["task-arena-size-32768", "arch-cortex-m", "executor-thread", "executor-interrupt", "defmt", "integrated-timers"] } embassy-time = { version = "0.3.2", 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-net = { version = "0.5.0", path = "../../embassy-net", features = ["defmt", "tcp", "dhcpv4", "medium-ethernet", "proto-ipv6", "dns"] } embassy-usb = { version = "0.3.0", path = "../../embassy-usb", features = ["defmt"] } embassy-futures = { version = "0.1.0", path = "../../embassy-futures" } diff --git a/examples/stm32h755cm4/Cargo.toml b/examples/stm32h755cm4/Cargo.toml index 18a17dbaf..455dee98b 100644 --- a/examples/stm32h755cm4/Cargo.toml +++ b/examples/stm32h755cm4/Cargo.toml @@ -11,7 +11,7 @@ embassy-sync = { version = "0.6.1", path = "../../embassy-sync", features = ["de embassy-embedded-hal = { version = "0.2.0", path = "../../embassy-embedded-hal" } embassy-executor = { version = "0.6.3", path = "../../embassy-executor", features = ["task-arena-size-32768", "arch-cortex-m", "executor-thread", "executor-interrupt", "defmt", "integrated-timers"] } embassy-time = { version = "0.3.1", 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-net = { version = "0.5.0", path = "../../embassy-net", features = ["defmt", "tcp", "dhcpv4", "medium-ethernet", "proto-ipv6", "dns"] } embassy-usb = { version = "0.3.0", path = "../../embassy-usb", features = ["defmt"] } embassy-futures = { version = "0.1.0", path = "../../embassy-futures" } diff --git a/examples/stm32h755cm7/Cargo.toml b/examples/stm32h755cm7/Cargo.toml index 02f1fcbaf..4d6167ab2 100644 --- a/examples/stm32h755cm7/Cargo.toml +++ b/examples/stm32h755cm7/Cargo.toml @@ -11,7 +11,7 @@ embassy-sync = { version = "0.6.1", path = "../../embassy-sync", features = ["de embassy-embedded-hal = { version = "0.2.0", path = "../../embassy-embedded-hal" } embassy-executor = { version = "0.6.3", path = "../../embassy-executor", features = ["task-arena-size-32768", "arch-cortex-m", "executor-thread", "executor-interrupt", "defmt", "integrated-timers"] } embassy-time = { version = "0.3.1", 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-net = { version = "0.5.0", path = "../../embassy-net", features = ["defmt", "tcp", "dhcpv4", "medium-ethernet", "proto-ipv6", "dns"] } embassy-usb = { version = "0.3.0", path = "../../embassy-usb", features = ["defmt"] } embassy-futures = { version = "0.1.0", path = "../../embassy-futures" } diff --git a/examples/stm32h7b0/Cargo.toml b/examples/stm32h7b0/Cargo.toml index 78f65a4cc..41f0fb5ca 100644 --- a/examples/stm32h7b0/Cargo.toml +++ b/examples/stm32h7b0/Cargo.toml @@ -10,7 +10,7 @@ embassy-sync = { version = "0.6.1", path = "../../embassy-sync", features = ["de embassy-embedded-hal = { version = "0.2.0", path = "../../embassy-embedded-hal" } embassy-executor = { version = "0.6.3", path = "../../embassy-executor", features = ["task-arena-size-32768", "arch-cortex-m", "executor-thread", "executor-interrupt", "defmt", "integrated-timers"] } embassy-time = { version = "0.3.2", 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-net = { version = "0.5.0", path = "../../embassy-net", features = ["defmt", "tcp", "dhcpv4", "medium-ethernet", "proto-ipv6", "dns"] } embassy-usb = { version = "0.3.0", path = "../../embassy-usb", features = ["defmt"] } embassy-futures = { version = "0.1.0", path = "../../embassy-futures" } diff --git a/examples/stm32h7rs/Cargo.toml b/examples/stm32h7rs/Cargo.toml index 60013cb88..24065dbce 100644 --- a/examples/stm32h7rs/Cargo.toml +++ b/examples/stm32h7rs/Cargo.toml @@ -10,7 +10,7 @@ embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = [" embassy-sync = { version = "0.6.1", path = "../../embassy-sync", features = ["defmt"] } embassy-executor = { version = "0.6.3", path = "../../embassy-executor", features = ["task-arena-size-32768", "arch-cortex-m", "executor-thread", "executor-interrupt", "defmt", "integrated-timers"] } embassy-time = { version = "0.3.2", 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-net = { version = "0.5.0", path = "../../embassy-net", features = ["defmt", "tcp", "dhcpv4", "medium-ethernet", "proto-ipv6", "dns"] } embassy-usb = { version = "0.3.0", path = "../../embassy-usb", features = ["defmt"] } embassy-futures = { version = "0.1.0", path = "../../embassy-futures" } diff --git a/examples/stm32l4/Cargo.toml b/examples/stm32l4/Cargo.toml index 9673b097f..b172878c1 100644 --- a/examples/stm32l4/Cargo.toml +++ b/examples/stm32l4/Cargo.toml @@ -13,7 +13,7 @@ embassy-time = { version = "0.3.2", path = "../../embassy-time", features = ["de embassy-embedded-hal = { version = "0.2.0", path = "../../embassy-embedded-hal" } embassy-usb = { version = "0.3.0", path = "../../embassy-usb", features = ["defmt"] } embassy-net-adin1110 = { version = "0.2.0", path = "../../embassy-net-adin1110" } -embassy-net = { version = "0.4.0", path = "../../embassy-net", features = ["defmt", "udp", "tcp", "dhcpv4", "medium-ethernet"] } +embassy-net = { version = "0.5.0", path = "../../embassy-net", features = ["defmt", "udp", "tcp", "dhcpv4", "medium-ethernet"] } embassy-futures = { version = "0.1.0", path = "../../embassy-futures" } embedded-io-async = { version = "0.6.1", features = ["defmt-03"] } embedded-io = { version = "0.6.0", features = ["defmt-03"] } diff --git a/examples/stm32l5/Cargo.toml b/examples/stm32l5/Cargo.toml index 014a3c4c8..e09311f9d 100644 --- a/examples/stm32l5/Cargo.toml +++ b/examples/stm32l5/Cargo.toml @@ -11,7 +11,7 @@ embassy-sync = { version = "0.6.1", path = "../../embassy-sync", features = ["de embassy-executor = { version = "0.6.3", path = "../../embassy-executor", features = ["task-arena-size-32768", "arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] } embassy-time = { version = "0.3.2", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } embassy-usb = { version = "0.3.0", path = "../../embassy-usb", features = ["defmt"] } -embassy-net = { version = "0.4.0", path = "../../embassy-net", features = ["defmt", "tcp", "dhcpv4", "medium-ethernet"] } +embassy-net = { version = "0.5.0", path = "../../embassy-net", features = ["defmt", "tcp", "dhcpv4", "medium-ethernet"] } embassy-futures = { version = "0.1.0", path = "../../embassy-futures" } usbd-hid = "0.8.1" diff --git a/examples/stm32wb/Cargo.toml b/examples/stm32wb/Cargo.toml index 3da74e535..400c7b20c 100644 --- a/examples/stm32wb/Cargo.toml +++ b/examples/stm32wb/Cargo.toml @@ -11,7 +11,7 @@ embassy-stm32-wpan = { version = "0.1.0", path = "../../embassy-stm32-wpan", fea embassy-sync = { version = "0.6.1", path = "../../embassy-sync", features = ["defmt"] } embassy-executor = { version = "0.6.3", path = "../../embassy-executor", features = ["task-arena-size-32768", "arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] } embassy-time = { version = "0.3.2", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } -embassy-net = { version = "0.4.0", path = "../../embassy-net", features = ["defmt", "udp", "proto-ipv6", "medium-ieee802154", ], optional=true } +embassy-net = { version = "0.5.0", path = "../../embassy-net", features = ["defmt", "udp", "proto-ipv6", "medium-ieee802154", ], optional=true } defmt = "0.3" defmt-rtt = "0.4" diff --git a/examples/stm32wba/Cargo.toml b/examples/stm32wba/Cargo.toml index 2c033ff7f..9e4251ce1 100644 --- a/examples/stm32wba/Cargo.toml +++ b/examples/stm32wba/Cargo.toml @@ -9,7 +9,7 @@ embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = [ embassy-sync = { version = "0.6.1", path = "../../embassy-sync", features = ["defmt"] } embassy-executor = { version = "0.6.3", path = "../../embassy-executor", features = ["task-arena-size-32768", "arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] } embassy-time = { version = "0.3.2", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } -embassy-net = { version = "0.4.0", path = "../../embassy-net", features = ["defmt", "udp", "proto-ipv6", "medium-ieee802154", ], optional=true } +embassy-net = { version = "0.5.0", path = "../../embassy-net", features = ["defmt", "udp", "proto-ipv6", "medium-ieee802154", ], optional=true } defmt = "0.3" defmt-rtt = "0.4" diff --git a/tests/nrf/Cargo.toml b/tests/nrf/Cargo.toml index 00dfda6d6..f91ab8b06 100644 --- a/tests/nrf/Cargo.toml +++ b/tests/nrf/Cargo.toml @@ -13,7 +13,7 @@ embassy-executor = { version = "0.6.3", path = "../../embassy-executor", feature embassy-time = { version = "0.3.2", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime"] } embassy-nrf = { version = "0.2.0", path = "../../embassy-nrf", features = ["defmt", "time-driver-rtc1", "gpiote", "unstable-pac"] } embedded-io-async = { version = "0.6.1", features = ["defmt-03"] } -embassy-net = { version = "0.4.0", path = "../../embassy-net", features = ["defmt", "tcp", "dhcpv4", "medium-ethernet", ] } +embassy-net = { version = "0.5.0", path = "../../embassy-net", features = ["defmt", "tcp", "dhcpv4", "medium-ethernet", ] } embassy-net-esp-hosted = { version = "0.1.0", path = "../../embassy-net-esp-hosted", features = ["defmt"] } embassy-net-enc28j60 = { version = "0.1.0", path = "../../embassy-net-enc28j60", features = ["defmt"] } embedded-hal-async = { version = "1.0" } diff --git a/tests/perf-client/Cargo.toml b/tests/perf-client/Cargo.toml index eb2a33a30..3bbc1b613 100644 --- a/tests/perf-client/Cargo.toml +++ b/tests/perf-client/Cargo.toml @@ -4,7 +4,7 @@ version = "0.1.0" edition = "2021" [dependencies] -embassy-net = { version = "0.4.0", path = "../../embassy-net", features = ["defmt", "tcp", "dhcpv4"] } +embassy-net = { version = "0.5.0", path = "../../embassy-net", features = ["defmt", "tcp", "dhcpv4"] } embassy-time = { version = "0.3.2", path = "../../embassy-time", features = ["defmt", ] } embassy-futures = { version = "0.1.0", path = "../../embassy-futures" } defmt = "0.3.0" diff --git a/tests/rp/Cargo.toml b/tests/rp/Cargo.toml index 91854e659..7fb791578 100644 --- a/tests/rp/Cargo.toml +++ b/tests/rp/Cargo.toml @@ -12,7 +12,7 @@ embassy-executor = { version = "0.6.3", path = "../../embassy-executor", feature embassy-time = { version = "0.3.2", path = "../../embassy-time", features = ["defmt", ] } embassy-rp = { version = "0.2.0", path = "../../embassy-rp", features = [ "defmt", "unstable-pac", "time-driver", "critical-section-impl", "intrinsics", "rom-v2-intrinsics", "run-from-ram", "rp2040"] } embassy-futures = { version = "0.1.0", path = "../../embassy-futures" } -embassy-net = { version = "0.4.0", path = "../../embassy-net", features = ["defmt", "tcp", "udp", "dhcpv4", "medium-ethernet"] } +embassy-net = { version = "0.5.0", path = "../../embassy-net", features = ["defmt", "tcp", "udp", "dhcpv4", "medium-ethernet"] } embassy-net-wiznet = { version = "0.1.0", path = "../../embassy-net-wiznet", features = ["defmt"] } embassy-embedded-hal = { version = "0.2.0", path = "../../embassy-embedded-hal/"} cyw43 = { path = "../../cyw43", features = ["defmt", "firmware-logs"] } diff --git a/tests/stm32/Cargo.toml b/tests/stm32/Cargo.toml index 288c438bd..599f7c702 100644 --- a/tests/stm32/Cargo.toml +++ b/tests/stm32/Cargo.toml @@ -65,7 +65,7 @@ embassy-time = { version = "0.3.2", path = "../../embassy-time", features = ["de embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = [ "defmt", "unstable-pac", "memory-x", "time-driver-any"] } embassy-futures = { version = "0.1.0", path = "../../embassy-futures" } embassy-stm32-wpan = { version = "0.1.0", path = "../../embassy-stm32-wpan", optional = true, features = ["defmt", "stm32wb55rg", "ble"] } -embassy-net = { version = "0.4.0", path = "../../embassy-net", features = ["defmt", "tcp", "udp", "dhcpv4", "medium-ethernet"] } +embassy-net = { version = "0.5.0", path = "../../embassy-net", features = ["defmt", "tcp", "udp", "dhcpv4", "medium-ethernet"] } perf-client = { path = "../perf-client" } defmt = "0.3.0" From f3e674a79cc6530d8185d3ff02e6bed1d7533c11 Mon Sep 17 00:00:00 2001 From: eZio Pan Date: Thu, 28 Nov 2024 15:48:27 +0800 Subject: [PATCH 255/361] stm32: remove redundant time-driver macro --- embassy-stm32/src/time_driver.rs | 24 ------------------------ 1 file changed, 24 deletions(-) diff --git a/embassy-stm32/src/time_driver.rs b/embassy-stm32/src/time_driver.rs index 74e4d0575..88b6c48bb 100644 --- a/embassy-stm32/src/time_driver.rs +++ b/embassy-stm32/src/time_driver.rs @@ -67,14 +67,6 @@ type T = peripherals::TIM23; type T = peripherals::TIM24; foreach_interrupt! { - (TIM1, timer, $block:ident, CC, $irq:ident) => { - #[cfg(time_driver_tim1)] - #[cfg(feature = "rt")] - #[interrupt] - fn $irq() { - DRIVER.on_interrupt() - } - }; (TIM1, timer, $block:ident, CC, $irq:ident) => { #[cfg(time_driver_tim1)] #[cfg(feature = "rt")] @@ -123,14 +115,6 @@ foreach_interrupt! { DRIVER.on_interrupt() } }; - (TIM8, timer, $block:ident, CC, $irq:ident) => { - #[cfg(time_driver_tim8)] - #[cfg(feature = "rt")] - #[interrupt] - fn $irq() { - DRIVER.on_interrupt() - } - }; (TIM9, timer, $block:ident, CC, $irq:ident) => { #[cfg(time_driver_tim9)] #[cfg(feature = "rt")] @@ -163,14 +147,6 @@ foreach_interrupt! { DRIVER.on_interrupt() } }; - (TIM20, timer, $block:ident, CC, $irq:ident) => { - #[cfg(time_driver_tim20)] - #[cfg(feature = "rt")] - #[interrupt] - fn $irq() { - DRIVER.on_interrupt() - } - }; (TIM21, timer, $block:ident, CC, $irq:ident) => { #[cfg(time_driver_tim21)] #[cfg(feature = "rt")] From 152d8ee0d9526a9b5d41350385ee2b2102c0c43f Mon Sep 17 00:00:00 2001 From: elagil Date: Thu, 28 Nov 2024 17:36:14 +0100 Subject: [PATCH 256/361] fix: make `write_immediate()` for ring buffers right-aligned --- embassy-stm32/src/dma/ringbuffer/mod.rs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/embassy-stm32/src/dma/ringbuffer/mod.rs b/embassy-stm32/src/dma/ringbuffer/mod.rs index 25bdc7522..4dc1b51a9 100644 --- a/embassy-stm32/src/dma/ringbuffer/mod.rs +++ b/embassy-stm32/src/dma/ringbuffer/mod.rs @@ -252,9 +252,13 @@ impl<'a, W: Word> WritableDmaRingBuffer<'a, W> { } /// Write elements directly to the buffer. + /// + /// Data is aligned towards the end of the buffer. pub fn write_immediate(&mut self, buf: &[W]) -> Result<(usize, usize), Error> { + let start = self.cap() - buf.len(); + for (i, data) in buf.iter().enumerate() { - self.write_buf(i, *data) + self.write_buf(start + i, *data) } let written = buf.len().min(self.cap()); Ok((written, self.cap() - written)) From 5d2b38c979b045d69fee1e2b1bb7f209043ceece Mon Sep 17 00:00:00 2001 From: elagil Date: Thu, 28 Nov 2024 17:45:00 +0100 Subject: [PATCH 257/361] doc: improve comment --- embassy-stm32/src/dma/ringbuffer/mod.rs | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/embassy-stm32/src/dma/ringbuffer/mod.rs b/embassy-stm32/src/dma/ringbuffer/mod.rs index 4dc1b51a9..44ea497fe 100644 --- a/embassy-stm32/src/dma/ringbuffer/mod.rs +++ b/embassy-stm32/src/dma/ringbuffer/mod.rs @@ -253,10 +253,17 @@ impl<'a, W: Word> WritableDmaRingBuffer<'a, W> { /// Write elements directly to the buffer. /// + /// Subsequent writes will overwrite the content of the buffer, so it is not useful to call this more than once. /// Data is aligned towards the end of the buffer. + /// + /// In case of success, returns the written length, and the empty space in front of the written block. + /// Fails if the data to write exceeds the buffer capacity. pub fn write_immediate(&mut self, buf: &[W]) -> Result<(usize, usize), Error> { - let start = self.cap() - buf.len(); + if buf.len() > self.cap() { + return Err(Error::Overrun); + } + let start = self.cap() - buf.len(); for (i, data) in buf.iter().enumerate() { self.write_buf(start + i, *data) } From d147161879ecf96b8e7ba7cd6f189c4283a5f61d Mon Sep 17 00:00:00 2001 From: Ulf Lilleengen Date: Fri, 29 Nov 2024 09:59:24 +0100 Subject: [PATCH 258/361] Rename example crate to remove warning --- examples/stm32h723/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/stm32h723/Cargo.toml b/examples/stm32h723/Cargo.toml index f68c91597..6e3f0dd03 100644 --- a/examples/stm32h723/Cargo.toml +++ b/examples/stm32h723/Cargo.toml @@ -1,6 +1,6 @@ [package] edition = "2021" -name = "embassy-stm32h7-examples" +name = "embassy-stm32h723-examples" version = "0.1.0" license = "MIT OR Apache-2.0" From 721c6820d4a6e3bbf2546997205a32975e6bad8b Mon Sep 17 00:00:00 2001 From: michel Date: Wed, 7 Aug 2024 21:58:49 +0200 Subject: [PATCH 259/361] STM32-TSC: enable discriminating between pins within same TSC group and improve TSC library in general --- .gitignore | 3 + embassy-stm32/src/tsc/acquisition_banks.rs | 209 ++++ embassy-stm32/src/tsc/config.rs | 175 +++ embassy-stm32/src/tsc/errors.rs | 21 + embassy-stm32/src/tsc/mod.rs | 1011 ++--------------- embassy-stm32/src/tsc/pin_groups.rs | 675 +++++++++++ embassy-stm32/src/tsc/tsc.rs | 456 ++++++++ .../src/tsc/{enums.rs => tsc_io_pin.rs} | 202 ++-- embassy-stm32/src/tsc/types.rs | 93 ++ examples/stm32f3/README.md | 24 + examples/stm32f3/src/bin/blocking-tsc.rs | 98 -- examples/stm32f3/src/bin/tsc_blocking.rs | 138 +++ examples/stm32l0/.cargo/config.toml | 2 +- examples/stm32l0/Cargo.toml | 2 +- examples/stm32l0/README.md | 24 + examples/stm32l0/src/bin/async-tsc.rs | 122 -- examples/stm32l0/src/bin/blocking-tsc.rs | 116 -- examples/stm32l0/src/bin/tsc_async.rs | 116 ++ examples/stm32l0/src/bin/tsc_blocking.rs | 142 +++ examples/stm32l0/src/bin/tsc_multipin.rs | 233 ++++ examples/stm32l4/.cargo/config.toml | 3 +- examples/stm32l4/Cargo.toml | 2 +- examples/stm32l4/README.md | 24 + examples/stm32l4/src/bin/tsc_async.rs | 108 ++ examples/stm32l4/src/bin/tsc_blocking.rs | 147 +++ examples/stm32l4/src/bin/tsc_multipin.rs | 222 ++++ examples/stm32u5/src/bin/tsc.rs | 77 +- 27 files changed, 3007 insertions(+), 1438 deletions(-) create mode 100644 embassy-stm32/src/tsc/acquisition_banks.rs create mode 100644 embassy-stm32/src/tsc/config.rs create mode 100644 embassy-stm32/src/tsc/errors.rs create mode 100644 embassy-stm32/src/tsc/pin_groups.rs create mode 100644 embassy-stm32/src/tsc/tsc.rs rename embassy-stm32/src/tsc/{enums.rs => tsc_io_pin.rs} (52%) create mode 100644 embassy-stm32/src/tsc/types.rs create mode 100644 examples/stm32f3/README.md delete mode 100644 examples/stm32f3/src/bin/blocking-tsc.rs create mode 100644 examples/stm32f3/src/bin/tsc_blocking.rs create mode 100644 examples/stm32l0/README.md delete mode 100644 examples/stm32l0/src/bin/async-tsc.rs delete mode 100644 examples/stm32l0/src/bin/blocking-tsc.rs create mode 100644 examples/stm32l0/src/bin/tsc_async.rs create mode 100644 examples/stm32l0/src/bin/tsc_blocking.rs create mode 100644 examples/stm32l0/src/bin/tsc_multipin.rs create mode 100644 examples/stm32l4/README.md create mode 100644 examples/stm32l4/src/bin/tsc_async.rs create mode 100644 examples/stm32l4/src/bin/tsc_blocking.rs create mode 100644 examples/stm32l4/src/bin/tsc_multipin.rs diff --git a/.gitignore b/.gitignore index a0b5d6a70..352c1f1af 100644 --- a/.gitignore +++ b/.gitignore @@ -5,4 +5,7 @@ Cargo.lock third_party /Cargo.toml out/ +# editor artifacts .zed +.neoconf.json +*.vim diff --git a/embassy-stm32/src/tsc/acquisition_banks.rs b/embassy-stm32/src/tsc/acquisition_banks.rs new file mode 100644 index 000000000..21a5c3f87 --- /dev/null +++ b/embassy-stm32/src/tsc/acquisition_banks.rs @@ -0,0 +1,209 @@ +#[cfg(any(tsc_v2, tsc_v3))] +use super::pin_groups::G7; +#[cfg(tsc_v3)] +use super::pin_groups::G8; +use super::pin_groups::{tsc_pin_roles, G1, G2, G3, G4, G5, G6}; +use super::tsc_io_pin::*; +use super::types::{Group, GroupStatus}; +use super::TSC_NUM_GROUPS; + +/// Represents a collection of TSC (Touch Sensing Controller) pins for an acquisition bank. +/// +/// This struct holds optional `TscIOPin` values for each TSC group, allowing for flexible +/// configuration of TSC acquisition banks. Each field corresponds to a specific TSC group +/// and can be set to `Some(TscIOPin)` if that group is to be included in the acquisition, +/// or `None` if it should be excluded. +#[allow(missing_docs)] +#[derive(Default)] +pub struct TscAcquisitionBankPins { + pub g1_pin: Option>, + pub g2_pin: Option>, + pub g3_pin: Option>, + pub g4_pin: Option>, + pub g5_pin: Option>, + pub g6_pin: Option>, + #[cfg(any(tsc_v2, tsc_v3))] + pub g7_pin: Option>, + #[cfg(tsc_v3)] + pub g8_pin: Option>, +} + +impl TscAcquisitionBankPins { + /// Returns an iterator over the pins in this acquisition bank. + /// + /// This method allows for easy traversal of all configured pins in the bank. + pub fn iter(&self) -> TscAcquisitionBankPinsIterator { + TscAcquisitionBankPinsIterator(TscAcquisitionBankIterator::new(self)) + } +} + +/// Iterator for TSC acquisition banks. +/// +/// This iterator allows traversing through the pins of a `TscAcquisitionBankPins` struct, +/// yielding each configured pin in order of the TSC groups. +pub struct TscAcquisitionBankIterator<'a> { + pins: &'a TscAcquisitionBankPins, + current_group: u8, +} + +impl<'a> TscAcquisitionBankIterator<'a> { + fn new(pins: &'a TscAcquisitionBankPins) -> Self { + Self { pins, current_group: 0 } + } + + fn next_pin(&mut self) -> Option { + while self.current_group < TSC_NUM_GROUPS as u8 { + let pin = match self.current_group { + 0 => self.pins.g1_pin.map(TscIOPinWithRole::get_pin), + 1 => self.pins.g2_pin.map(TscIOPinWithRole::get_pin), + 2 => self.pins.g3_pin.map(TscIOPinWithRole::get_pin), + 3 => self.pins.g4_pin.map(TscIOPinWithRole::get_pin), + 4 => self.pins.g5_pin.map(TscIOPinWithRole::get_pin), + 5 => self.pins.g6_pin.map(TscIOPinWithRole::get_pin), + #[cfg(any(tsc_v2, tsc_v3))] + 6 => self.pins.g7_pin.map(TscIOPinWithRole::get_pin), + #[cfg(tsc_v3)] + 7 => self.pins.g8_pin.map(TscIOPinWithRole::get_pin), + _ => None, + }; + self.current_group += 1; + if pin.is_some() { + return pin; + } + } + None + } +} + +/// Iterator for TSC acquisition bank pins. +/// +/// This iterator yields `TscIOPin` values for each configured pin in the acquisition bank. +pub struct TscAcquisitionBankPinsIterator<'a>(TscAcquisitionBankIterator<'a>); + +impl<'a> Iterator for TscAcquisitionBankPinsIterator<'a> { + type Item = TscIOPin; + + fn next(&mut self) -> Option { + self.0.next_pin() + } +} + +impl TscAcquisitionBankPins { + /// Returns an iterator over the available pins in the bank + pub fn pins_iterator(&self) -> TscAcquisitionBankPinsIterator { + TscAcquisitionBankPinsIterator(TscAcquisitionBankIterator::new(self)) + } +} + +/// Represents a collection of TSC pins to be acquired simultaneously. +/// +/// This struct contains a set of pins to be used in a TSC acquisition with a pre-computed and +/// verified mask for efficiently setting up the TSC peripheral before performing an acquisition. +/// It ensures that only one channel pin per TSC group is included, adhering to hardware limitations. +pub struct TscAcquisitionBank { + pub(super) pins: TscAcquisitionBankPins, + pub(super) mask: u32, +} + +impl TscAcquisitionBank { + /// Returns an iterator over the available pins in the bank. + pub fn pins_iterator(&self) -> TscAcquisitionBankPinsIterator { + self.pins.pins_iterator() + } + + /// Returns the mask for this bank. + pub fn mask(&self) -> u32 { + self.mask + } + + /// Retrieves the TSC I/O pin for a given group in this acquisition bank. + /// + /// # Arguments + /// * `group` - The TSC group to retrieve the pin for. + /// + /// # Returns + /// An `Option` containing the pin if it exists for the given group, or `None` if not. + pub fn get_pin(&self, group: Group) -> Option { + match group { + Group::One => self.pins.g1_pin.map(|p| p.pin), + Group::Two => self.pins.g2_pin.map(|p| p.pin), + Group::Three => self.pins.g3_pin.map(|p| p.pin), + Group::Four => self.pins.g4_pin.map(|p| p.pin), + Group::Five => self.pins.g5_pin.map(|p| p.pin), + Group::Six => self.pins.g6_pin.map(|p| p.pin), + #[cfg(any(tsc_v2, tsc_v3))] + Group::Seven => self.pins.g7_pin.map(|p| p.pin), + #[cfg(tsc_v3)] + Group::Eight => self.pins.g8_pin.map(|p| p.pin), + } + } +} + +/// Represents the status of all TSC groups in an acquisition bank +#[derive(Default)] +pub struct TscAcquisitionBankStatus { + pub(super) groups: [Option; TSC_NUM_GROUPS], +} + +impl TscAcquisitionBankStatus { + /// Check if all groups in the bank are complete + pub fn all_complete(&self) -> bool { + self.groups + .iter() + .all(|&status| status.map_or(true, |s| s == GroupStatus::Complete)) + } + + /// Check if any group in the bank is ongoing + pub fn any_ongoing(&self) -> bool { + self.groups.iter().any(|&status| status == Some(GroupStatus::Ongoing)) + } + + /// Get the status of a specific group, if the group is present in the bank + pub fn get_group_status(&self, group: Group) -> Option { + let index: usize = group.into(); + self.groups[index] + } + + /// Iterator for groups present in the bank + pub fn iter(&self) -> impl Iterator + '_ { + self.groups.iter().enumerate().filter_map(|(group_num, status)| { + status.and_then(|s| Group::try_from(group_num).ok().map(|group| (group, s))) + }) + } +} + +/// Represents the result of a Touch Sensing Controller (TSC) acquisition for a specific pin. +/// +/// This struct contains a reference to the `TscIOPin` from which a value was read, +/// along with the actual sensor reading for that pin. It provides a convenient way +/// to associate TSC readings with their corresponding pins after an acquisition. +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +#[derive(Clone, Copy, Debug)] +pub struct TscChannelReading { + /// The sensor reading value obtained from the TSC acquisition. + /// Lower values typically indicate a detected touch, while higher values indicate no touch. + pub sensor_value: u16, + + /// The `TscIOPin` associated with this reading. + /// This allows for easy identification of which pin the reading corresponds to. + pub tsc_pin: TscIOPin, +} + +/// Represents the readings from all TSC groups +#[derive(Default)] +pub struct TscAcquisitionBankReadings { + pub(super) groups: [Option; TSC_NUM_GROUPS], +} + +impl TscAcquisitionBankReadings { + /// Get the reading for a specific group, if the group is present in the bank + pub fn get_group_reading(&self, group: Group) -> Option { + let index: usize = group.into(); + self.groups[index] + } + + /// Iterator for readings for groups present in the bank + pub fn iter(&self) -> impl Iterator + '_ { + self.groups.iter().filter_map(|&x| x) + } +} diff --git a/embassy-stm32/src/tsc/config.rs b/embassy-stm32/src/tsc/config.rs new file mode 100644 index 000000000..efa1f9a0d --- /dev/null +++ b/embassy-stm32/src/tsc/config.rs @@ -0,0 +1,175 @@ +/// Charge transfer pulse cycles +#[allow(missing_docs)] +#[derive(Copy, Clone, PartialEq)] +pub enum ChargeTransferPulseCycle { + _1, + _2, + _3, + _4, + _5, + _6, + _7, + _8, + _9, + _10, + _11, + _12, + _13, + _14, + _15, + _16, +} + +impl Into for ChargeTransferPulseCycle { + fn into(self) -> u8 { + match self { + ChargeTransferPulseCycle::_1 => 0, + ChargeTransferPulseCycle::_2 => 1, + ChargeTransferPulseCycle::_3 => 2, + ChargeTransferPulseCycle::_4 => 3, + ChargeTransferPulseCycle::_5 => 4, + ChargeTransferPulseCycle::_6 => 5, + ChargeTransferPulseCycle::_7 => 6, + ChargeTransferPulseCycle::_8 => 7, + ChargeTransferPulseCycle::_9 => 8, + ChargeTransferPulseCycle::_10 => 9, + ChargeTransferPulseCycle::_11 => 10, + ChargeTransferPulseCycle::_12 => 11, + ChargeTransferPulseCycle::_13 => 12, + ChargeTransferPulseCycle::_14 => 13, + ChargeTransferPulseCycle::_15 => 14, + ChargeTransferPulseCycle::_16 => 15, + } + } +} + +/// Max count +#[allow(missing_docs)] +#[derive(Copy, Clone)] +pub enum MaxCount { + _255, + _511, + _1023, + _2047, + _4095, + _8191, + _16383, +} + +impl Into for MaxCount { + fn into(self) -> u8 { + match self { + MaxCount::_255 => 0, + MaxCount::_511 => 1, + MaxCount::_1023 => 2, + MaxCount::_2047 => 3, + MaxCount::_4095 => 4, + MaxCount::_8191 => 5, + MaxCount::_16383 => 6, + } + } +} + +/// Prescaler divider +#[allow(missing_docs)] +#[derive(Copy, Clone, PartialEq)] +pub enum PGPrescalerDivider { + _1, + _2, + _4, + _8, + _16, + _32, + _64, + _128, +} + +impl Into for PGPrescalerDivider { + fn into(self) -> u8 { + match self { + PGPrescalerDivider::_1 => 0, + PGPrescalerDivider::_2 => 1, + PGPrescalerDivider::_4 => 2, + PGPrescalerDivider::_8 => 3, + PGPrescalerDivider::_16 => 4, + PGPrescalerDivider::_32 => 5, + PGPrescalerDivider::_64 => 6, + PGPrescalerDivider::_128 => 7, + } + } +} + +/// Error type for SSDeviation +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum SSDeviationError { + /// The provided value is too low (0) + ValueTooLow, + /// The provided value is too high (greater than 128) + ValueTooHigh, +} + +/// Spread Spectrum Deviation +#[derive(Copy, Clone)] +pub struct SSDeviation(u8); +impl SSDeviation { + /// Create new deviation value, acceptable inputs are 1-128 + pub fn new(val: u8) -> Result { + if val == 0 { + return Err(SSDeviationError::ValueTooLow); + } else if val > 128 { + return Err(SSDeviationError::ValueTooHigh); + } + Ok(Self(val - 1)) + } +} + +impl Into for SSDeviation { + fn into(self) -> u8 { + self.0 + } +} + +/// Peripheral configuration +#[derive(Clone, Copy)] +pub struct Config { + /// Duration of high state of the charge transfer pulse + pub ct_pulse_high_length: ChargeTransferPulseCycle, + /// Duration of the low state of the charge transfer pulse + pub ct_pulse_low_length: ChargeTransferPulseCycle, + /// Enable/disable of spread spectrum feature + pub spread_spectrum: bool, + /// Adds variable number of periods of the SS clk to pulse high state + pub spread_spectrum_deviation: SSDeviation, + /// Selects AHB clock divider used to generate SS clk + pub spread_spectrum_prescaler: bool, + /// Selects AHB clock divider used to generate pulse generator clk + pub pulse_generator_prescaler: PGPrescalerDivider, + /// Maximum number of charge transfer pulses that can be generated before error + pub max_count_value: MaxCount, + /// Defines config of all IOs when no ongoing acquisition + pub io_default_mode: bool, + /// Polarity of sync input pin + pub synchro_pin_polarity: bool, + /// Acquisition starts when start bit is set or with sync pin input + pub acquisition_mode: bool, + /// Enable max count interrupt + pub max_count_interrupt: bool, +} + +impl Default for Config { + fn default() -> Self { + Self { + ct_pulse_high_length: ChargeTransferPulseCycle::_1, + ct_pulse_low_length: ChargeTransferPulseCycle::_1, + spread_spectrum: false, + spread_spectrum_deviation: SSDeviation::new(1).unwrap(), + spread_spectrum_prescaler: false, + pulse_generator_prescaler: PGPrescalerDivider::_1, + max_count_value: MaxCount::_255, + io_default_mode: false, + synchro_pin_polarity: false, + acquisition_mode: false, + max_count_interrupt: false, + } + } +} diff --git a/embassy-stm32/src/tsc/errors.rs b/embassy-stm32/src/tsc/errors.rs new file mode 100644 index 000000000..21f6441ba --- /dev/null +++ b/embassy-stm32/src/tsc/errors.rs @@ -0,0 +1,21 @@ +/// Represents errors that can occur when configuring or validating TSC pin groups. +#[derive(Debug)] +pub enum GroupError { + /// Error when a group has no sampling capacitor + NoSamplingCapacitor, + /// Error when a group has neither channel IOs nor a shield IO + NoChannelOrShield, + /// Error when a group has both channel IOs and a shield IO + MixedChannelAndShield, + /// Error when there is more than one shield IO across all groups + MultipleShields, +} + +/// Error returned when attempting to set an invalid channel pin as active in the TSC. +#[derive(Debug)] +pub enum AcquisitionBankError { + /// Indicates that one or more of the provided pins is not a valid channel pin. + InvalidChannelPin, + /// Indicates that multiple channels from the same group were provided. + MultipleChannelsPerGroup, +} diff --git a/embassy-stm32/src/tsc/mod.rs b/embassy-stm32/src/tsc/mod.rs index 17240e6bc..2df3847bc 100644 --- a/embassy-stm32/src/tsc/mod.rs +++ b/embassy-stm32/src/tsc/mod.rs @@ -1,90 +1,119 @@ //! TSC Peripheral Interface //! +//! This module provides an interface for the Touch Sensing Controller (TSC) peripheral. +//! It supports both blocking and async modes of operation, as well as different TSC versions (v1, v2, v3). +//! +//! # Key Concepts +//! +//! - **Pin Groups**: TSC pins are organized into groups, each containing up to four IOs. +//! - **Pin Roles**: Each pin in a group can have a role: Channel, Sample, or Shield. +//! - **Acquisition Banks**: Used for efficient, repeated TSC acquisitions on specific sets of pins. //! //! # Example (stm32) -//! ``` rust, ignore -//! -//! let mut device_config = embassy_stm32::Config::default(); -//! { -//! device_config.rcc.mux = ClockSrc::MSI(Msirange::RANGE_4MHZ); -//! } //! +//! ```rust +//! let device_config = embassy_stm32::Config::default(); //! let context = embassy_stm32::init(device_config); //! //! let config = tsc::Config { -//! ct_pulse_high_length: ChargeTransferPulseCycle::_2, -//! ct_pulse_low_length: ChargeTransferPulseCycle::_2, +//! ct_pulse_high_length: ChargeTransferPulseCycle::_4, +//! ct_pulse_low_length: ChargeTransferPulseCycle::_4, //! spread_spectrum: false, //! spread_spectrum_deviation: SSDeviation::new(2).unwrap(), //! spread_spectrum_prescaler: false, -//! pulse_generator_prescaler: PGPrescalerDivider::_4, -//! max_count_value: MaxCount::_8191, +//! pulse_generator_prescaler: PGPrescalerDivider::_16, +//! max_count_value: MaxCount::_255, //! io_default_mode: false, //! synchro_pin_polarity: false, //! acquisition_mode: false, //! max_count_interrupt: false, -//! channel_ios: TscIOPin::Group2Io2 | TscIOPin::Group7Io3, -//! shield_ios: TscIOPin::Group1Io3.into(), -//! sampling_ios: TscIOPin::Group1Io2 | TscIOPin::Group2Io1 | TscIOPin::Group7Io2, //! }; //! -//! let mut g1: PinGroup = PinGroup::new(); -//! g1.set_io2(context.PB13, PinType::Sample); -//! g1.set_io3(context.PB14, PinType::Shield); +//! let mut g2: PinGroupWithRoles = PinGroupWithRoles::new(); +//! g2.set_io1::(context.PB4); +//! let sensor_pin = g2.set_io2::(context.PB5); //! -//! let mut g2: PinGroup = PinGroup::new(); -//! g2.set_io1(context.PB4, PinType::Sample); -//! g2.set_io2(context.PB5, PinType::Channel); -//! -//! let mut g7: PinGroup = PinGroup::new(); -//! g7.set_io2(context.PE3, PinType::Sample); -//! g7.set_io3(context.PE4, PinType::Channel); +//! let pin_groups = PinGroups { +//! g2: Some(g2.pin_group), +//! ..Default::default() +//! }; //! //! let mut touch_controller = tsc::Tsc::new_blocking( //! context.TSC, -//! Some(g1), -//! Some(g2), -//! None, -//! None, -//! None, -//! None, -//! Some(g7), -//! None, +//! pin_groups, //! config, -//! ); +//! ).unwrap(); //! -//! touch_controller.discharge_io(true); -//! Timer::after_millis(1).await; +//! let discharge_delay = 5; // ms //! -//! touch_controller.start(); +//! loop { +//! touch_controller.set_active_channels_mask(sensor_pin.pin.into()); +//! touch_controller.start(); +//! touch_controller.poll_for_acquisition(); +//! touch_controller.discharge_io(true); +//! Timer::after_millis(discharge_delay).await; //! +//! match touch_controller.group_get_status(sensor_pin.pin.group()) { +//! GroupStatus::Complete => { +//! let group_val = touch_controller.group_get_value(sensor_pin.pin.group()); +//! // Process the touch value +//! // ... +//! } +//! GroupStatus::Ongoing => { +//! // Handle ongoing acquisition +//! // ... +//! } +//! } +//! } //! ``` +//! +//! # Async Usage +//! +//! For async operation, use `Tsc::new_async` and `pend_for_acquisition` instead of polling. #![macro_use] -/// Enums defined for peripheral parameters -pub mod enums; +/// Configuration structures and enums for the TSC peripheral. +pub mod config; + +/// Definitions and implementations for TSC pin groups. +pub mod pin_groups; + +/// Definitions and implementations for individual TSC I/O pins. +pub mod tsc_io_pin; + +/// Structures and implementations for TSC acquisition banks. +pub mod acquisition_banks; + +/// Core implementation of the TSC (Touch Sensing Controller) driver. +pub mod tsc; + +/// Type definitions used throughout the TSC module. +pub mod types; + +/// Error types and definitions for the TSC module. +pub mod errors; -use core::future::poll_fn; use core::marker::PhantomData; -use core::task::Poll; -use embassy_hal_internal::{into_ref, PeripheralRef}; +pub use acquisition_banks::*; +pub use config::*; use embassy_sync::waitqueue::AtomicWaker; -pub use enums::*; +pub use errors::*; +pub use pin_groups::*; +pub use tsc::*; +pub use tsc_io_pin::*; +pub use types::*; -use crate::gpio::{AfType, AnyPin, OutputType, Speed}; -use crate::interrupt::typelevel::Interrupt; -use crate::mode::{Async, Blocking, Mode as PeriMode}; -use crate::rcc::{self, RccPeripheral}; +use crate::rcc::RccPeripheral; use crate::{interrupt, peripherals, Peripheral}; #[cfg(tsc_v1)] -const TSC_NUM_GROUPS: u32 = 6; +const TSC_NUM_GROUPS: usize = 6; #[cfg(tsc_v2)] -const TSC_NUM_GROUPS: u32 = 7; +const TSC_NUM_GROUPS: usize = 7; #[cfg(tsc_v3)] -const TSC_NUM_GROUPS: u32 = 8; +const TSC_NUM_GROUPS: usize = 8; /// Error type defined for TSC #[derive(Debug, Clone, Copy)] @@ -106,859 +135,6 @@ impl interrupt::typelevel::Handler for InterruptHandl } } -/// Pin type definition to control IO parameters -pub enum PinType { - /// Sensing channel pin connected to an electrode - Channel, - /// Sampling capacitor pin, one required for every pin group - Sample, - /// Shield pin connected to capacitive sensing shield - Shield, -} - -/// Peripheral state -#[derive(PartialEq, Clone, Copy)] -pub enum State { - /// Peripheral is being setup or reconfigured - Reset, - /// Ready to start acquisition - Ready, - /// In process of sensor acquisition - Busy, - /// Error occured during acquisition - Error, -} - -/// Individual group status checked after acquisition reported as complete -/// For groups with multiple channel pins, may take longer because acquisitions -/// are done sequentially. Check this status before pulling count for each -/// sampled channel -#[derive(PartialEq)] -pub enum GroupStatus { - /// Acquisition for channel still in progress - Ongoing, - /// Acquisition either not started or complete - Complete, -} - -/// Group identifier used to interrogate status -#[allow(missing_docs)] -pub enum Group { - One, - Two, - Three, - Four, - Five, - Six, - #[cfg(any(tsc_v2, tsc_v3))] - Seven, - #[cfg(tsc_v3)] - Eight, -} - -impl Into for Group { - fn into(self) -> usize { - match self { - Group::One => 0, - Group::Two => 1, - Group::Three => 2, - Group::Four => 3, - Group::Five => 4, - Group::Six => 5, - #[cfg(any(tsc_v2, tsc_v3))] - Group::Seven => 6, - #[cfg(tsc_v3)] - Group::Eight => 7, - } - } -} - -/// Peripheral configuration -#[derive(Clone, Copy)] -pub struct Config { - /// Duration of high state of the charge transfer pulse - pub ct_pulse_high_length: ChargeTransferPulseCycle, - /// Duration of the low state of the charge transfer pulse - pub ct_pulse_low_length: ChargeTransferPulseCycle, - /// Enable/disable of spread spectrum feature - pub spread_spectrum: bool, - /// Adds variable number of periods of the SS clk to pulse high state - pub spread_spectrum_deviation: SSDeviation, - /// Selects AHB clock divider used to generate SS clk - pub spread_spectrum_prescaler: bool, - /// Selects AHB clock divider used to generate pulse generator clk - pub pulse_generator_prescaler: PGPrescalerDivider, - /// Maximum number of charge transfer pulses that can be generated before error - pub max_count_value: MaxCount, - /// Defines config of all IOs when no ongoing acquisition - pub io_default_mode: bool, - /// Polarity of sync input pin - pub synchro_pin_polarity: bool, - /// Acquisition starts when start bit is set or with sync pin input - pub acquisition_mode: bool, - /// Enable max count interrupt - pub max_count_interrupt: bool, - /// Channel IO mask - pub channel_ios: u32, - /// Shield IO mask - pub shield_ios: u32, - /// Sampling IO mask - pub sampling_ios: u32, -} - -impl Default for Config { - fn default() -> Self { - Self { - ct_pulse_high_length: ChargeTransferPulseCycle::_1, - ct_pulse_low_length: ChargeTransferPulseCycle::_1, - spread_spectrum: false, - spread_spectrum_deviation: SSDeviation::new(1).unwrap(), - spread_spectrum_prescaler: false, - pulse_generator_prescaler: PGPrescalerDivider::_1, - max_count_value: MaxCount::_255, - io_default_mode: false, - synchro_pin_polarity: false, - acquisition_mode: false, - max_count_interrupt: false, - channel_ios: 0, - shield_ios: 0, - sampling_ios: 0, - } - } -} - -/// Pin struct that maintains usage -#[allow(missing_docs)] -pub struct TscPin<'d, T, C> { - _pin: PeripheralRef<'d, AnyPin>, - role: PinType, - phantom: PhantomData<(T, C)>, -} - -enum GroupError { - NoSample, - ChannelShield, -} - -/// Pin group definition -/// Pins are organized into groups of four IOs, all groups with a -/// sampling channel must also have a sampling capacitor channel. -#[allow(missing_docs)] -#[derive(Default)] -pub struct PinGroup<'d, T, C> { - d1: Option>, - d2: Option>, - d3: Option>, - d4: Option>, -} - -impl<'d, T: Instance, C> PinGroup<'d, T, C> { - /// Create new sensing group - pub fn new() -> Self { - Self { - d1: None, - d2: None, - d3: None, - d4: None, - } - } - - fn contains_shield(&self) -> bool { - let mut shield_count = 0; - - if let Some(pin) = &self.d1 { - if let PinType::Shield = pin.role { - shield_count += 1; - } - } - - if let Some(pin) = &self.d2 { - if let PinType::Shield = pin.role { - shield_count += 1; - } - } - - if let Some(pin) = &self.d3 { - if let PinType::Shield = pin.role { - shield_count += 1; - } - } - - if let Some(pin) = &self.d4 { - if let PinType::Shield = pin.role { - shield_count += 1; - } - } - - shield_count == 1 - } - - fn check_group(&self) -> Result<(), GroupError> { - let mut channel_count = 0; - let mut shield_count = 0; - let mut sample_count = 0; - if let Some(pin) = &self.d1 { - match pin.role { - PinType::Channel => { - channel_count += 1; - } - PinType::Shield => { - shield_count += 1; - } - PinType::Sample => { - sample_count += 1; - } - } - } - - if let Some(pin) = &self.d2 { - match pin.role { - PinType::Channel => { - channel_count += 1; - } - PinType::Shield => { - shield_count += 1; - } - PinType::Sample => { - sample_count += 1; - } - } - } - - if let Some(pin) = &self.d3 { - match pin.role { - PinType::Channel => { - channel_count += 1; - } - PinType::Shield => { - shield_count += 1; - } - PinType::Sample => { - sample_count += 1; - } - } - } - - if let Some(pin) = &self.d4 { - match pin.role { - PinType::Channel => { - channel_count += 1; - } - PinType::Shield => { - shield_count += 1; - } - PinType::Sample => { - sample_count += 1; - } - } - } - - // Every group requires one sampling capacitor - if sample_count != 1 { - return Err(GroupError::NoSample); - } - - // Each group must have at least one shield or channel IO - if shield_count == 0 && channel_count == 0 { - return Err(GroupError::ChannelShield); - } - - // Any group can either contain channel ios or a shield IO - if shield_count != 0 && channel_count != 0 { - return Err(GroupError::ChannelShield); - } - - // No more than one shield IO is allow per group and amongst all groups - if shield_count > 1 { - return Err(GroupError::ChannelShield); - } - - Ok(()) - } -} - -macro_rules! group_impl { - ($group:ident, $trait1:ident, $trait2:ident, $trait3:ident, $trait4:ident) => { - impl<'d, T: Instance> PinGroup<'d, T, $group> { - #[doc = concat!("Create a new pin1 for ", stringify!($group), " TSC group instance.")] - pub fn set_io1(&mut self, pin: impl Peripheral

> + 'd, role: PinType) { - into_ref!(pin); - critical_section::with(|_| { - pin.set_low(); - pin.set_as_af( - pin.af_num(), - AfType::output( - match role { - PinType::Channel => OutputType::PushPull, - PinType::Sample => OutputType::OpenDrain, - PinType::Shield => OutputType::PushPull, - }, - Speed::VeryHigh, - ), - ); - self.d1 = Some(TscPin { - _pin: pin.map_into(), - role: role, - phantom: PhantomData, - }) - }) - } - - #[doc = concat!("Create a new pin2 for ", stringify!($group), " TSC group instance.")] - pub fn set_io2(&mut self, pin: impl Peripheral

> + 'd, role: PinType) { - into_ref!(pin); - critical_section::with(|_| { - pin.set_low(); - pin.set_as_af( - pin.af_num(), - AfType::output( - match role { - PinType::Channel => OutputType::PushPull, - PinType::Sample => OutputType::OpenDrain, - PinType::Shield => OutputType::PushPull, - }, - Speed::VeryHigh, - ), - ); - self.d2 = Some(TscPin { - _pin: pin.map_into(), - role: role, - phantom: PhantomData, - }) - }) - } - - #[doc = concat!("Create a new pin3 for ", stringify!($group), " TSC group instance.")] - pub fn set_io3(&mut self, pin: impl Peripheral

> + 'd, role: PinType) { - into_ref!(pin); - critical_section::with(|_| { - pin.set_low(); - pin.set_as_af( - pin.af_num(), - AfType::output( - match role { - PinType::Channel => OutputType::PushPull, - PinType::Sample => OutputType::OpenDrain, - PinType::Shield => OutputType::PushPull, - }, - Speed::VeryHigh, - ), - ); - self.d3 = Some(TscPin { - _pin: pin.map_into(), - role: role, - phantom: PhantomData, - }) - }) - } - - #[doc = concat!("Create a new pin4 for ", stringify!($group), " TSC group instance.")] - pub fn set_io4(&mut self, pin: impl Peripheral

> + 'd, role: PinType) { - into_ref!(pin); - critical_section::with(|_| { - pin.set_low(); - pin.set_as_af( - pin.af_num(), - AfType::output( - match role { - PinType::Channel => OutputType::PushPull, - PinType::Sample => OutputType::OpenDrain, - PinType::Shield => OutputType::PushPull, - }, - Speed::VeryHigh, - ), - ); - self.d4 = Some(TscPin { - _pin: pin.map_into(), - role: role, - phantom: PhantomData, - }) - }) - } - } - }; -} - -group_impl!(G1, G1IO1Pin, G1IO2Pin, G1IO3Pin, G1IO4Pin); -group_impl!(G2, G2IO1Pin, G2IO2Pin, G2IO3Pin, G2IO4Pin); -group_impl!(G3, G3IO1Pin, G3IO2Pin, G3IO3Pin, G3IO4Pin); -group_impl!(G4, G4IO1Pin, G4IO2Pin, G4IO3Pin, G4IO4Pin); -group_impl!(G5, G5IO1Pin, G5IO2Pin, G5IO3Pin, G5IO4Pin); -group_impl!(G6, G6IO1Pin, G6IO2Pin, G6IO3Pin, G6IO4Pin); -group_impl!(G7, G7IO1Pin, G7IO2Pin, G7IO3Pin, G7IO4Pin); -group_impl!(G8, G8IO1Pin, G8IO2Pin, G8IO3Pin, G8IO4Pin); - -/// Group 1 marker type. -pub enum G1 {} -/// Group 2 marker type. -pub enum G2 {} -/// Group 3 marker type. -pub enum G3 {} -/// Group 4 marker type. -pub enum G4 {} -/// Group 5 marker type. -pub enum G5 {} -/// Group 6 marker type. -pub enum G6 {} -/// Group 7 marker type. -pub enum G7 {} -/// Group 8 marker type. -pub enum G8 {} - -/// TSC driver -pub struct Tsc<'d, T: Instance, K: PeriMode> { - _peri: PeripheralRef<'d, T>, - _g1: Option>, - _g2: Option>, - _g3: Option>, - _g4: Option>, - _g5: Option>, - _g6: Option>, - #[cfg(any(tsc_v2, tsc_v3))] - _g7: Option>, - #[cfg(tsc_v3)] - _g8: Option>, - state: State, - config: Config, - _kind: PhantomData, -} - -impl<'d, T: Instance> Tsc<'d, T, Async> { - /// Create a Tsc instance that can be awaited for completion - pub fn new_async( - peri: impl Peripheral

+ 'd, - g1: Option>, - g2: Option>, - g3: Option>, - g4: Option>, - g5: Option>, - g6: Option>, - #[cfg(any(tsc_v2, tsc_v3))] g7: Option>, - #[cfg(tsc_v3)] g8: Option>, - config: Config, - _irq: impl interrupt::typelevel::Binding> + 'd, - ) -> Self { - // Need to check valid pin configuration input - let g1 = g1.filter(|b| b.check_group().is_ok()); - let g2 = g2.filter(|b| b.check_group().is_ok()); - let g3 = g3.filter(|b| b.check_group().is_ok()); - let g4 = g4.filter(|b| b.check_group().is_ok()); - let g5 = g5.filter(|b| b.check_group().is_ok()); - let g6 = g6.filter(|b| b.check_group().is_ok()); - #[cfg(any(tsc_v2, tsc_v3))] - let g7 = g7.filter(|b| b.check_group().is_ok()); - #[cfg(tsc_v3)] - let g8 = g8.filter(|b| b.check_group().is_ok()); - - match Self::check_shields( - &g1, - &g2, - &g3, - &g4, - &g5, - &g6, - #[cfg(any(tsc_v2, tsc_v3))] - &g7, - #[cfg(tsc_v3)] - &g8, - ) { - Ok(()) => Self::new_inner( - peri, - g1, - g2, - g3, - g4, - g5, - g6, - #[cfg(any(tsc_v2, tsc_v3))] - g7, - #[cfg(tsc_v3)] - g8, - config, - ), - Err(_) => Self::new_inner( - peri, - None, - None, - None, - None, - None, - None, - #[cfg(any(tsc_v2, tsc_v3))] - None, - #[cfg(tsc_v3)] - None, - config, - ), - } - } - /// Asyncronously wait for the end of an acquisition - pub async fn pend_for_acquisition(&mut self) { - poll_fn(|cx| match self.get_state() { - State::Busy => { - T::waker().register(cx.waker()); - T::regs().ier().write(|w| w.set_eoaie(true)); - if self.get_state() != State::Busy { - T::regs().ier().write(|w| w.set_eoaie(false)); - return Poll::Ready(()); - } - Poll::Pending - } - _ => { - T::regs().ier().write(|w| w.set_eoaie(false)); - Poll::Ready(()) - } - }) - .await; - } -} - -impl<'d, T: Instance> Tsc<'d, T, Blocking> { - /// Create a Tsc instance that must be polled for completion - pub fn new_blocking( - peri: impl Peripheral

+ 'd, - g1: Option>, - g2: Option>, - g3: Option>, - g4: Option>, - g5: Option>, - g6: Option>, - #[cfg(any(tsc_v2, tsc_v3))] g7: Option>, - #[cfg(tsc_v3)] g8: Option>, - config: Config, - ) -> Self { - // Need to check valid pin configuration input - let g1 = g1.filter(|b| b.check_group().is_ok()); - let g2 = g2.filter(|b| b.check_group().is_ok()); - let g3 = g3.filter(|b| b.check_group().is_ok()); - let g4 = g4.filter(|b| b.check_group().is_ok()); - let g5 = g5.filter(|b| b.check_group().is_ok()); - let g6 = g6.filter(|b| b.check_group().is_ok()); - #[cfg(any(tsc_v2, tsc_v3))] - let g7 = g7.filter(|b| b.check_group().is_ok()); - #[cfg(tsc_v3)] - let g8 = g8.filter(|b| b.check_group().is_ok()); - - match Self::check_shields( - &g1, - &g2, - &g3, - &g4, - &g5, - &g6, - #[cfg(any(tsc_v2, tsc_v3))] - &g7, - #[cfg(tsc_v3)] - &g8, - ) { - Ok(()) => Self::new_inner( - peri, - g1, - g2, - g3, - g4, - g5, - g6, - #[cfg(any(tsc_v2, tsc_v3))] - g7, - #[cfg(tsc_v3)] - g8, - config, - ), - Err(_) => Self::new_inner( - peri, - None, - None, - None, - None, - None, - None, - #[cfg(any(tsc_v2, tsc_v3))] - None, - #[cfg(tsc_v3)] - None, - config, - ), - } - } - /// Wait for end of acquisition - pub fn poll_for_acquisition(&mut self) { - while self.get_state() == State::Busy {} - } -} - -impl<'d, T: Instance, K: PeriMode> Tsc<'d, T, K> { - /// Create new TSC driver - fn check_shields( - g1: &Option>, - g2: &Option>, - g3: &Option>, - g4: &Option>, - g5: &Option>, - g6: &Option>, - #[cfg(any(tsc_v2, tsc_v3))] g7: &Option>, - #[cfg(tsc_v3)] g8: &Option>, - ) -> Result<(), GroupError> { - let mut shield_count = 0; - - if let Some(pin_group) = g1 { - if pin_group.contains_shield() { - shield_count += 1; - } - }; - if let Some(pin_group) = g2 { - if pin_group.contains_shield() { - shield_count += 1; - } - }; - if let Some(pin_group) = g3 { - if pin_group.contains_shield() { - shield_count += 1; - } - }; - if let Some(pin_group) = g4 { - if pin_group.contains_shield() { - shield_count += 1; - } - }; - if let Some(pin_group) = g5 { - if pin_group.contains_shield() { - shield_count += 1; - } - }; - if let Some(pin_group) = g6 { - if pin_group.contains_shield() { - shield_count += 1; - } - }; - #[cfg(any(tsc_v2, tsc_v3))] - if let Some(pin_group) = g7 { - if pin_group.contains_shield() { - shield_count += 1; - } - }; - #[cfg(tsc_v3)] - if let Some(pin_group) = g8 { - if pin_group.contains_shield() { - shield_count += 1; - } - }; - - if shield_count > 1 { - return Err(GroupError::ChannelShield); - } - - Ok(()) - } - - fn extract_groups(io_mask: u32) -> u32 { - let mut groups: u32 = 0; - for idx in 0..TSC_NUM_GROUPS { - if io_mask & (0x0F << idx * 4) != 0 { - groups |= 1 << idx - } - } - groups - } - - fn new_inner( - peri: impl Peripheral

+ 'd, - g1: Option>, - g2: Option>, - g3: Option>, - g4: Option>, - g5: Option>, - g6: Option>, - #[cfg(any(tsc_v2, tsc_v3))] g7: Option>, - #[cfg(tsc_v3)] g8: Option>, - config: Config, - ) -> Self { - into_ref!(peri); - - rcc::enable_and_reset::(); - - T::regs().cr().modify(|w| { - w.set_tsce(true); - w.set_ctph(config.ct_pulse_high_length.into()); - w.set_ctpl(config.ct_pulse_low_length.into()); - w.set_sse(config.spread_spectrum); - // Prevent invalid configuration for pulse generator prescaler - if config.ct_pulse_low_length == ChargeTransferPulseCycle::_1 - && (config.pulse_generator_prescaler == PGPrescalerDivider::_1 - || config.pulse_generator_prescaler == PGPrescalerDivider::_2) - { - w.set_pgpsc(PGPrescalerDivider::_4.into()); - } else if config.ct_pulse_low_length == ChargeTransferPulseCycle::_2 - && config.pulse_generator_prescaler == PGPrescalerDivider::_1 - { - w.set_pgpsc(PGPrescalerDivider::_2.into()); - } else { - w.set_pgpsc(config.pulse_generator_prescaler.into()); - } - w.set_ssd(config.spread_spectrum_deviation.into()); - w.set_sspsc(config.spread_spectrum_prescaler); - - w.set_mcv(config.max_count_value.into()); - w.set_syncpol(config.synchro_pin_polarity); - w.set_am(config.acquisition_mode); - }); - - // Set IO configuration - // Disable Schmitt trigger hysteresis on all used TSC IOs - T::regs() - .iohcr() - .write(|w| w.0 = !(config.channel_ios | config.shield_ios | config.sampling_ios)); - - // Set channel and shield IOs - T::regs() - .ioccr() - .write(|w| w.0 = config.channel_ios | config.shield_ios); - - // Set sampling IOs - T::regs().ioscr().write(|w| w.0 = config.sampling_ios); - - // Set the groups to be acquired - T::regs() - .iogcsr() - .write(|w| w.0 = Self::extract_groups(config.channel_ios)); - - // Disable interrupts - T::regs().ier().modify(|w| { - w.set_eoaie(false); - w.set_mceie(false); - }); - - // Clear flags - T::regs().icr().modify(|w| { - w.set_eoaic(true); - w.set_mceic(true); - }); - - unsafe { - T::Interrupt::enable(); - } - - Self { - _peri: peri, - _g1: g1, - _g2: g2, - _g3: g3, - _g4: g4, - _g5: g5, - _g6: g6, - #[cfg(any(tsc_v2, tsc_v3))] - _g7: g7, - #[cfg(tsc_v3)] - _g8: g8, - state: State::Ready, - config, - _kind: PhantomData, - } - } - - /// Start charge transfer acquisition - pub fn start(&mut self) { - self.state = State::Busy; - - // Disable interrupts - T::regs().ier().modify(|w| { - w.set_eoaie(false); - w.set_mceie(false); - }); - - // Clear flags - T::regs().icr().modify(|w| { - w.set_eoaic(true); - w.set_mceic(true); - }); - - // Set the touch sensing IOs not acquired to the default mode - T::regs().cr().modify(|w| { - w.set_iodef(self.config.io_default_mode); - }); - - // Start the acquisition - T::regs().cr().modify(|w| { - w.set_start(true); - }); - } - - /// Stop charge transfer acquisition - pub fn stop(&mut self) { - T::regs().cr().modify(|w| { - w.set_start(false); - }); - - // Set the touch sensing IOs in low power mode - T::regs().cr().modify(|w| { - w.set_iodef(false); - }); - - // Clear flags - T::regs().icr().modify(|w| { - w.set_eoaic(true); - w.set_mceic(true); - }); - - self.state = State::Ready; - } - - /// Get current state of acquisition - pub fn get_state(&mut self) -> State { - if self.state == State::Busy { - if T::regs().isr().read().eoaf() { - if T::regs().isr().read().mcef() { - self.state = State::Error - } else { - self.state = State::Ready - } - } - } - self.state - } - - /// Get the individual group status to check acquisition complete - pub fn group_get_status(&mut self, index: Group) -> GroupStatus { - // Status bits are set by hardware when the acquisition on the corresponding - // enabled analog IO group is complete, cleared when new acquisition is started - let status = match index { - Group::One => T::regs().iogcsr().read().g1s(), - Group::Two => T::regs().iogcsr().read().g2s(), - Group::Three => T::regs().iogcsr().read().g3s(), - Group::Four => T::regs().iogcsr().read().g4s(), - Group::Five => T::regs().iogcsr().read().g5s(), - Group::Six => T::regs().iogcsr().read().g6s(), - #[cfg(any(tsc_v2, tsc_v3))] - Group::Seven => T::regs().iogcsr().read().g7s(), - #[cfg(tsc_v3)] - Group::Eight => T::regs().iogcsr().read().g8s(), - }; - match status { - true => GroupStatus::Complete, - false => GroupStatus::Ongoing, - } - } - - /// Get the count for the acquisiton, valid once group status is set - pub fn group_get_value(&mut self, index: Group) -> u16 { - T::regs().iogcr(index.into()).read().cnt() - } - - /// Discharge the IOs for subsequent acquisition - pub fn discharge_io(&mut self, status: bool) { - // Set the touch sensing IOs in low power mode - T::regs().cr().modify(|w| { - w.set_iodef(!status); - }); - } -} - -impl<'d, T: Instance, K: PeriMode> Drop for Tsc<'d, T, K> { - fn drop(&mut self) { - rcc::disable::(); - } -} - pub(crate) trait SealedInstance { fn regs() -> crate::pac::tsc::Tsc; fn waker() -> &'static AtomicWaker; @@ -988,36 +164,3 @@ foreach_interrupt!( } }; ); - -pin_trait!(G1IO1Pin, Instance); -pin_trait!(G1IO2Pin, Instance); -pin_trait!(G1IO3Pin, Instance); -pin_trait!(G1IO4Pin, Instance); -pin_trait!(G2IO1Pin, Instance); -pin_trait!(G2IO2Pin, Instance); -pin_trait!(G2IO3Pin, Instance); -pin_trait!(G2IO4Pin, Instance); -pin_trait!(G3IO1Pin, Instance); -pin_trait!(G3IO2Pin, Instance); -pin_trait!(G3IO3Pin, Instance); -pin_trait!(G3IO4Pin, Instance); -pin_trait!(G4IO1Pin, Instance); -pin_trait!(G4IO2Pin, Instance); -pin_trait!(G4IO3Pin, Instance); -pin_trait!(G4IO4Pin, Instance); -pin_trait!(G5IO1Pin, Instance); -pin_trait!(G5IO2Pin, Instance); -pin_trait!(G5IO3Pin, Instance); -pin_trait!(G5IO4Pin, Instance); -pin_trait!(G6IO1Pin, Instance); -pin_trait!(G6IO2Pin, Instance); -pin_trait!(G6IO3Pin, Instance); -pin_trait!(G6IO4Pin, Instance); -pin_trait!(G7IO1Pin, Instance); -pin_trait!(G7IO2Pin, Instance); -pin_trait!(G7IO3Pin, Instance); -pin_trait!(G7IO4Pin, Instance); -pin_trait!(G8IO1Pin, Instance); -pin_trait!(G8IO2Pin, Instance); -pin_trait!(G8IO3Pin, Instance); -pin_trait!(G8IO4Pin, Instance); diff --git a/embassy-stm32/src/tsc/pin_groups.rs b/embassy-stm32/src/tsc/pin_groups.rs new file mode 100644 index 000000000..b15890d6f --- /dev/null +++ b/embassy-stm32/src/tsc/pin_groups.rs @@ -0,0 +1,675 @@ +use core::marker::PhantomData; +use core::ops::BitOr; + +use embassy_hal_internal::{into_ref, PeripheralRef}; + +use super::errors::GroupError; +use super::tsc_io_pin::*; +use super::Instance; +use crate::gpio::{AfType, AnyPin, OutputType, Speed}; +use crate::Peripheral; + +/// Pin type definition to control IO parameters +#[derive(PartialEq, Clone, Copy)] +pub enum PinType { + /// Sensing channel pin connected to an electrode + Channel, + /// Sampling capacitor pin, one required for every pin group + Sample, + /// Shield pin connected to capacitive sensing shield + Shield, +} + +/// Pin struct that maintains usage +#[allow(missing_docs)] +pub struct TscPin<'d, T, Group> { + _pin: PeripheralRef<'d, AnyPin>, + role: PinType, + tsc_io_pin: TscIOPin, + phantom: PhantomData<(T, Group)>, +} + +impl<'d, T, Group> TscPin<'d, T, Group> { + /// Returns the role of this TSC pin. + /// + /// The role indicates whether this pin is configured as a channel, + /// sampling capacitor, or shield in the TSC group. + /// + /// # Returns + /// The `PinType` representing the role of this pin. + pub fn role(&self) -> PinType { + self.role + } + + /// Returns the TSC IO pin associated with this pin. + /// + /// This method provides access to the specific TSC IO pin configuration, + /// which includes information about the pin's group and position within that group. + /// + /// # Returns + /// The `TscIOPin` representing this pin's TSC-specific configuration. + pub fn tsc_io_pin(&self) -> TscIOPin { + self.tsc_io_pin + } +} + +/// Represents a group of TSC (Touch Sensing Controller) pins. +/// +/// In the TSC peripheral, pins are organized into groups of four IOs. Each group +/// must have exactly one sampling capacitor pin and can have multiple channel pins +/// or a single shield pin. This structure encapsulates these pin configurations +/// for a single TSC group. +/// +/// # Pin Roles +/// - Sampling Capacitor: One required per group, used for charge transfer. +/// - Channel: Sensing pins connected to electrodes for touch detection. +/// - Shield: Optional, used for active shielding to improve sensitivity. +/// +/// # Constraints +/// - Each group must have exactly one sampling capacitor pin. +/// - A group can have either channel pins or a shield pin, but not both. +/// - No more than one shield pin is allowed across all groups. +#[allow(missing_docs)] +pub struct PinGroup<'d, T, Group> { + pin1: Option>, + pin2: Option>, + pin3: Option>, + pin4: Option>, +} + +impl<'d, T, G> Default for PinGroup<'d, T, G> { + fn default() -> Self { + Self { + pin1: None, + pin2: None, + pin3: None, + pin4: None, + } + } +} + +/// Defines roles and traits for TSC (Touch Sensing Controller) pins. +/// +/// This module contains marker types and traits that represent different roles +/// a TSC pin can have, such as channel, sample, or shield. +pub mod tsc_pin_roles { + use super::{OutputType, PinType}; + + /// Marker type for a TSC channel pin. + #[derive(PartialEq, Clone, Copy, Debug)] + pub struct Channel; + + /// Marker type for a TSC sampling pin. + #[derive(PartialEq, Clone, Copy, Debug)] + pub struct Sample; + + /// Marker type for a TSC shield pin. + #[derive(PartialEq, Clone, Copy, Debug)] + pub struct Shield; + + /// Trait for TSC pin roles. + /// + /// This trait defines the behavior and properties of different TSC pin roles. + /// It is implemented by the marker types `Channel`, `Sample`, and `Shield`. + pub trait Role { + /// Returns the `PinType` associated with this role. + fn pin_type() -> PinType; + + /// Returns the `OutputType` associated with this role. + fn output_type() -> OutputType; + } + + impl Role for Channel { + fn pin_type() -> PinType { + PinType::Channel + } + fn output_type() -> OutputType { + OutputType::PushPull + } + } + + impl Role for Sample { + fn pin_type() -> PinType { + PinType::Sample + } + fn output_type() -> OutputType { + OutputType::OpenDrain + } + } + + impl Role for Shield { + fn pin_type() -> PinType { + PinType::Shield + } + fn output_type() -> OutputType { + OutputType::PushPull + } + } +} + +/// Represents a group of TSC pins with their associated roles. +/// +/// This struct allows for type-safe configuration of TSC pin groups, +/// ensuring that pins are assigned appropriate roles within their group. +/// This type is essentially just a wrapper type around a `PinGroup` value. +/// +/// # Type Parameters +/// - `'d`: Lifetime of the pin group. +/// - `T`: The TSC instance type. +/// - `G`: The group identifier. +/// - `R1`, `R2`, `R3`, `R4`: Role types for each pin in the group, defaulting to `Channel`. +pub struct PinGroupWithRoles< + 'd, + T: Instance, + G, + R1 = tsc_pin_roles::Channel, + R2 = tsc_pin_roles::Channel, + R3 = tsc_pin_roles::Channel, + R4 = tsc_pin_roles::Channel, +> { + /// The underlying pin group without role information. + pub pin_group: PinGroup<'d, T, G>, + _phantom: PhantomData<(R1, R2, R3, R4)>, +} + +impl<'d, T: Instance, G, R1, R2, R3, R4> Default for PinGroupWithRoles<'d, T, G, R1, R2, R3, R4> { + fn default() -> Self { + Self { + pin_group: PinGroup::default(), + _phantom: PhantomData, + } + } +} + +impl<'d, T: Instance, G> PinGroup<'d, T, G> { + fn contains_exactly_one_shield_pin(&self) -> bool { + let shield_count = self.shield_pins().count(); + shield_count == 1 + } + + fn check_group(&self) -> Result<(), GroupError> { + let mut channel_count = 0; + let mut shield_count = 0; + let mut sample_count = 0; + for pin in self.pins().into_iter().flatten() { + match pin.role { + PinType::Channel => { + channel_count += 1; + } + PinType::Shield => { + shield_count += 1; + } + PinType::Sample => { + sample_count += 1; + } + } + } + + // Every group requires exactly one sampling capacitor + if sample_count != 1 { + return Err(GroupError::NoSamplingCapacitor); + } + + // Each group must have at least one shield or channel IO + if shield_count == 0 && channel_count == 0 { + return Err(GroupError::NoChannelOrShield); + } + + // Any group can either contain channel ios or a shield IO. + // (An active shield requires its own sampling capacitor) + if shield_count != 0 && channel_count != 0 { + return Err(GroupError::MixedChannelAndShield); + } + + // No more than one shield IO is allow per group and amongst all groups + if shield_count > 1 { + return Err(GroupError::MultipleShields); + } + + Ok(()) + } + + /// Returns a reference to the first pin in the group, if configured. + pub fn pin1(&self) -> Option<&TscPin<'d, T, G>> { + self.pin1.as_ref() + } + + /// Returns a reference to the second pin in the group, if configured. + pub fn pin2(&self) -> Option<&TscPin<'d, T, G>> { + self.pin2.as_ref() + } + + /// Returns a reference to the third pin in the group, if configured. + pub fn pin3(&self) -> Option<&TscPin<'d, T, G>> { + self.pin3.as_ref() + } + + /// Returns a reference to the fourth pin in the group, if configured. + pub fn pin4(&self) -> Option<&TscPin<'d, T, G>> { + self.pin4.as_ref() + } + + fn sample_pins(&self) -> impl Iterator + '_ { + self.pins_filtered(PinType::Sample) + } + + fn shield_pins(&self) -> impl Iterator + '_ { + self.pins_filtered(PinType::Shield) + } + + fn channel_pins(&self) -> impl Iterator + '_ { + self.pins_filtered(PinType::Channel) + } + + fn pins_filtered(&self, pin_type: PinType) -> impl Iterator + '_ { + self.pins().into_iter().filter_map(move |pin| { + pin.as_ref() + .and_then(|p| if p.role == pin_type { Some(p.tsc_io_pin) } else { None }) + }) + } + + fn make_channel_ios_mask(&self) -> u32 { + self.channel_pins().fold(0, u32::bitor) + } + + fn make_shield_ios_mask(&self) -> u32 { + self.shield_pins().fold(0, u32::bitor) + } + + fn make_sample_ios_mask(&self) -> u32 { + self.sample_pins().fold(0, u32::bitor) + } + + fn pins(&self) -> [&Option>; 4] { + [&self.pin1, &self.pin2, &self.pin3, &self.pin4] + } + + fn pins_mut(&mut self) -> [&mut Option>; 4] { + [&mut self.pin1, &mut self.pin2, &mut self.pin3, &mut self.pin4] + } +} + +#[cfg(any(tsc_v2, tsc_v3))] +macro_rules! TSC_V2_V3_GUARD { + ($e:expr) => {{ + #[cfg(any(tsc_v2, tsc_v3))] + { + $e + } + #[cfg(not(any(tsc_v2, tsc_v3)))] + { + compile_error!("Group 7 is not supported in this TSC version") + } + }}; +} + +#[cfg(tsc_v3)] +macro_rules! TSC_V3_GUARD { + ($e:expr) => {{ + #[cfg(tsc_v3)] + { + $e + } + #[cfg(not(tsc_v3))] + { + compile_error!("Group 8 is not supported in this TSC version") + } + }}; +} + +macro_rules! trait_to_tsc_io_pin { + (G1IO1Pin) => { + TscIOPin::Group1Io1 + }; + (G1IO2Pin) => { + TscIOPin::Group1Io2 + }; + (G1IO3Pin) => { + TscIOPin::Group1Io3 + }; + (G1IO4Pin) => { + TscIOPin::Group1Io4 + }; + + (G2IO1Pin) => { + TscIOPin::Group2Io1 + }; + (G2IO2Pin) => { + TscIOPin::Group2Io2 + }; + (G2IO3Pin) => { + TscIOPin::Group2Io3 + }; + (G2IO4Pin) => { + TscIOPin::Group2Io4 + }; + + (G3IO1Pin) => { + TscIOPin::Group3Io1 + }; + (G3IO2Pin) => { + TscIOPin::Group3Io2 + }; + (G3IO3Pin) => { + TscIOPin::Group3Io3 + }; + (G3IO4Pin) => { + TscIOPin::Group3Io4 + }; + + (G4IO1Pin) => { + TscIOPin::Group4Io1 + }; + (G4IO2Pin) => { + TscIOPin::Group4Io2 + }; + (G4IO3Pin) => { + TscIOPin::Group4Io3 + }; + (G4IO4Pin) => { + TscIOPin::Group4Io4 + }; + + (G5IO1Pin) => { + TscIOPin::Group5Io1 + }; + (G5IO2Pin) => { + TscIOPin::Group5Io2 + }; + (G5IO3Pin) => { + TscIOPin::Group5Io3 + }; + (G5IO4Pin) => { + TscIOPin::Group5Io4 + }; + + (G6IO1Pin) => { + TscIOPin::Group6Io1 + }; + (G6IO2Pin) => { + TscIOPin::Group6Io2 + }; + (G6IO3Pin) => { + TscIOPin::Group6Io3 + }; + (G6IO4Pin) => { + TscIOPin::Group6Io4 + }; + + (G7IO1Pin) => { + TSC_V2_V3_GUARD!(TscIOPin::Group7Io1) + }; + (G7IO2Pin) => { + TSC_V2_V3_GUARD!(TscIOPin::Group7Io2) + }; + (G7IO3Pin) => { + TSC_V2_V3_GUARD!(TscIOPin::Group7Io3) + }; + (G7IO4Pin) => { + TSC_V2_V3_GUARD!(TscIOPin::Group7Io4) + }; + + (G8IO1Pin) => { + TSC_V3_GUARD!(TscIOPin::Group8Io1) + }; + (G8IO2Pin) => { + TSC_V3_GUARD!(TscIOPin::Group8Io2) + }; + (G8IO3Pin) => { + TSC_V3_GUARD!(TscIOPin::Group8Io3) + }; + (G8IO4Pin) => { + TSC_V3_GUARD!(TscIOPin::Group8Io4) + }; +} + +macro_rules! impl_set_io { + ($method:ident, $group:ident, $trait:ident, $index:expr) => { + #[doc = concat!("Create a new pin1 for ", stringify!($group), " TSC group instance.")] + pub fn $method( + &mut self, + pin: impl Peripheral

> + 'd, + ) -> TscIOPinWithRole<$group, Role> { + into_ref!(pin); + critical_section::with(|_| { + pin.set_low(); + pin.set_as_af(pin.af_num(), AfType::output(Role::output_type(), Speed::VeryHigh)); + let tsc_io_pin = trait_to_tsc_io_pin!($trait); + let new_pin = TscPin { + _pin: pin.map_into(), + role: Role::pin_type(), + tsc_io_pin, + phantom: PhantomData, + }; + *self.pin_group.pins_mut()[$index] = Some(new_pin); + TscIOPinWithRole { + pin: tsc_io_pin, + phantom: PhantomData, + } + }) + } + }; +} + +macro_rules! group_impl { + ($group:ident, $trait1:ident, $trait2:ident, $trait3:ident, $trait4:ident) => { + impl< + 'd, + T: Instance, + R1: tsc_pin_roles::Role, + R2: tsc_pin_roles::Role, + R3: tsc_pin_roles::Role, + R4: tsc_pin_roles::Role, + > PinGroupWithRoles<'d, T, $group, R1, R2, R3, R4> + { + impl_set_io!(set_io1, $group, $trait1, 0); + impl_set_io!(set_io2, $group, $trait2, 1); + impl_set_io!(set_io3, $group, $trait3, 2); + impl_set_io!(set_io4, $group, $trait4, 3); + } + }; +} + +group_impl!(G1, G1IO1Pin, G1IO2Pin, G1IO3Pin, G1IO4Pin); +group_impl!(G2, G2IO1Pin, G2IO2Pin, G2IO3Pin, G2IO4Pin); +group_impl!(G3, G3IO1Pin, G3IO2Pin, G3IO3Pin, G3IO4Pin); +group_impl!(G4, G4IO1Pin, G4IO2Pin, G4IO3Pin, G4IO4Pin); +group_impl!(G5, G5IO1Pin, G5IO2Pin, G5IO3Pin, G5IO4Pin); +group_impl!(G6, G6IO1Pin, G6IO2Pin, G6IO3Pin, G6IO4Pin); +#[cfg(any(tsc_v2, tsc_v3))] +group_impl!(G7, G7IO1Pin, G7IO2Pin, G7IO3Pin, G7IO4Pin); +#[cfg(tsc_v3)] +group_impl!(G8, G8IO1Pin, G8IO2Pin, G8IO3Pin, G8IO4Pin); + +/// Group 1 marker type. +#[derive(Clone, Copy, Debug)] +pub enum G1 {} +/// Group 2 marker type. +#[derive(Clone, Copy, Debug)] +pub enum G2 {} +/// Group 3 marker type. +#[derive(Clone, Copy, Debug)] +pub enum G3 {} +/// Group 4 marker type. +#[derive(Clone, Copy, Debug)] +pub enum G4 {} +/// Group 5 marker type. +#[derive(Clone, Copy, Debug)] +pub enum G5 {} +/// Group 6 marker type. +#[derive(Clone, Copy, Debug)] +pub enum G6 {} +/// Group 7 marker type. +#[derive(Clone, Copy, Debug)] +pub enum G7 {} +/// Group 8 marker type. +#[derive(Clone, Copy, Debug)] +pub enum G8 {} + +/// Represents the collection of pin groups for the Touch Sensing Controller (TSC). +/// +/// Each field corresponds to a specific group of TSC pins: +#[allow(missing_docs)] +pub struct PinGroups<'d, T: Instance> { + pub g1: Option>, + pub g2: Option>, + pub g3: Option>, + pub g4: Option>, + pub g5: Option>, + pub g6: Option>, + #[cfg(any(tsc_v2, tsc_v3))] + pub g7: Option>, + #[cfg(tsc_v3)] + pub g8: Option>, +} + +impl<'d, T: Instance> PinGroups<'d, T> { + pub(super) fn check(&self) -> Result<(), GroupError> { + let mut shield_count = 0; + + // Helper function to check a single group + fn check_group( + group: &Option>, + shield_count: &mut u32, + ) -> Result<(), GroupError> { + if let Some(group) = group { + group.check_group()?; + if group.contains_exactly_one_shield_pin() { + *shield_count += 1; + if *shield_count > 1 { + return Err(GroupError::MultipleShields); + } + } + } + Ok(()) + } + + // Check each group + check_group(&self.g1, &mut shield_count)?; + check_group(&self.g2, &mut shield_count)?; + check_group(&self.g3, &mut shield_count)?; + check_group(&self.g4, &mut shield_count)?; + check_group(&self.g5, &mut shield_count)?; + check_group(&self.g6, &mut shield_count)?; + #[cfg(any(tsc_v2, tsc_v3))] + check_group(&self.g7, &mut shield_count)?; + #[cfg(tsc_v3)] + check_group(&self.g8, &mut shield_count)?; + + Ok(()) + } + + pub(super) fn make_channel_ios_mask(&self) -> u32 { + #[allow(unused_mut)] + let mut mask = self.g1.as_ref().map_or(0, |g| g.make_channel_ios_mask()) + | self.g2.as_ref().map_or(0, |g| g.make_channel_ios_mask()) + | self.g3.as_ref().map_or(0, |g| g.make_channel_ios_mask()) + | self.g4.as_ref().map_or(0, |g| g.make_channel_ios_mask()) + | self.g5.as_ref().map_or(0, |g| g.make_channel_ios_mask()) + | self.g6.as_ref().map_or(0, |g| g.make_channel_ios_mask()); + #[cfg(any(tsc_v2, tsc_v3))] + { + mask |= self.g7.as_ref().map_or(0, |g| g.make_channel_ios_mask()); + } + #[cfg(tsc_v3)] + { + mask |= self.g8.as_ref().map_or(0, |g| g.make_channel_ios_mask()); + } + mask + } + + pub(super) fn make_shield_ios_mask(&self) -> u32 { + #[allow(unused_mut)] + let mut mask = self.g1.as_ref().map_or(0, |g| g.make_shield_ios_mask()) + | self.g2.as_ref().map_or(0, |g| g.make_shield_ios_mask()) + | self.g3.as_ref().map_or(0, |g| g.make_shield_ios_mask()) + | self.g4.as_ref().map_or(0, |g| g.make_shield_ios_mask()) + | self.g5.as_ref().map_or(0, |g| g.make_shield_ios_mask()) + | self.g6.as_ref().map_or(0, |g| g.make_shield_ios_mask()); + #[cfg(any(tsc_v2, tsc_v3))] + { + mask |= self.g7.as_ref().map_or(0, |g| g.make_shield_ios_mask()); + } + #[cfg(tsc_v3)] + { + mask |= self.g8.as_ref().map_or(0, |g| g.make_shield_ios_mask()); + } + mask + } + + pub(super) fn make_sample_ios_mask(&self) -> u32 { + #[allow(unused_mut)] + let mut mask = self.g1.as_ref().map_or(0, |g| g.make_sample_ios_mask()) + | self.g2.as_ref().map_or(0, |g| g.make_sample_ios_mask()) + | self.g3.as_ref().map_or(0, |g| g.make_sample_ios_mask()) + | self.g4.as_ref().map_or(0, |g| g.make_sample_ios_mask()) + | self.g5.as_ref().map_or(0, |g| g.make_sample_ios_mask()) + | self.g6.as_ref().map_or(0, |g| g.make_sample_ios_mask()); + #[cfg(any(tsc_v2, tsc_v3))] + { + mask |= self.g7.as_ref().map_or(0, |g| g.make_sample_ios_mask()); + } + #[cfg(tsc_v3)] + { + mask |= self.g8.as_ref().map_or(0, |g| g.make_sample_ios_mask()); + } + mask + } +} + +impl<'d, T: Instance> Default for PinGroups<'d, T> { + fn default() -> Self { + Self { + g1: None, + g2: None, + g3: None, + g4: None, + g5: None, + g6: None, + #[cfg(any(tsc_v2, tsc_v3))] + g7: None, + #[cfg(tsc_v3)] + g8: None, + } + } +} + +pin_trait!(G1IO1Pin, Instance); +pin_trait!(G1IO2Pin, Instance); +pin_trait!(G1IO3Pin, Instance); +pin_trait!(G1IO4Pin, Instance); + +pin_trait!(G2IO1Pin, Instance); +pin_trait!(G2IO2Pin, Instance); +pin_trait!(G2IO3Pin, Instance); +pin_trait!(G2IO4Pin, Instance); + +pin_trait!(G3IO1Pin, Instance); +pin_trait!(G3IO2Pin, Instance); +pin_trait!(G3IO3Pin, Instance); +pin_trait!(G3IO4Pin, Instance); + +pin_trait!(G4IO1Pin, Instance); +pin_trait!(G4IO2Pin, Instance); +pin_trait!(G4IO3Pin, Instance); +pin_trait!(G4IO4Pin, Instance); + +pin_trait!(G5IO1Pin, Instance); +pin_trait!(G5IO2Pin, Instance); +pin_trait!(G5IO3Pin, Instance); +pin_trait!(G5IO4Pin, Instance); + +pin_trait!(G6IO1Pin, Instance); +pin_trait!(G6IO2Pin, Instance); +pin_trait!(G6IO3Pin, Instance); +pin_trait!(G6IO4Pin, Instance); + +pin_trait!(G7IO1Pin, Instance); +pin_trait!(G7IO2Pin, Instance); +pin_trait!(G7IO3Pin, Instance); +pin_trait!(G7IO4Pin, Instance); + +pin_trait!(G8IO1Pin, Instance); +pin_trait!(G8IO2Pin, Instance); +pin_trait!(G8IO3Pin, Instance); +pin_trait!(G8IO4Pin, Instance); diff --git a/embassy-stm32/src/tsc/tsc.rs b/embassy-stm32/src/tsc/tsc.rs new file mode 100644 index 000000000..58f9d9d2e --- /dev/null +++ b/embassy-stm32/src/tsc/tsc.rs @@ -0,0 +1,456 @@ +use core::future::poll_fn; +use core::marker::PhantomData; +use core::ops::BitOr; +use core::task::Poll; + +use embassy_hal_internal::{into_ref, PeripheralRef}; + +use super::acquisition_banks::*; +use super::config::*; +use super::errors::*; +use super::pin_groups::*; +use super::tsc_io_pin::*; +use super::types::*; +use super::{Instance, InterruptHandler, TSC_NUM_GROUPS}; +use crate::interrupt::typelevel::Interrupt; +use crate::mode::{Async, Blocking, Mode as PeriMode}; +use crate::{interrupt, rcc, Peripheral}; + +/// Internal structure holding masks for different types of TSC IOs. +/// +/// These masks are used during the initial configuration of the TSC peripheral +/// and for validating pin types during operations like creating acquisition banks. +struct TscIOMasks { + /// Mask representing all configured channel IOs + channel_ios: u32, + /// Mask representing all configured shield IOs + shield_ios: u32, + /// Mask representing all configured sampling IOs + sampling_ios: u32, +} + +/// TSC driver +pub struct Tsc<'d, T: Instance, K: PeriMode> { + _peri: PeripheralRef<'d, T>, + _pin_groups: PinGroups<'d, T>, + state: State, + config: Config, + masks: TscIOMasks, + _kind: PhantomData, +} + +impl<'d, T: Instance, K: PeriMode> Tsc<'d, T, K> { + // Helper method to check if a pin is a channel pin + fn is_channel_pin(&self, pin: TscIOPin) -> bool { + (self.masks.channel_ios & pin) != 0 + } + + /// Get the status of all groups involved in a TscAcquisitionBank + pub fn get_acquisition_bank_status(&self, bank: &TscAcquisitionBank) -> TscAcquisitionBankStatus { + let mut bank_status = TscAcquisitionBankStatus::default(); + for pin in bank.pins_iterator() { + let group = pin.group(); + let group_status = self.group_get_status(group); + let index: usize = group.into(); + bank_status.groups[index] = Some(group_status); + } + bank_status + } + + /// Get the values for all channels involved in a TscAcquisitionBank + pub fn get_acquisition_bank_values(&self, bank: &TscAcquisitionBank) -> TscAcquisitionBankReadings { + let mut bank_readings = TscAcquisitionBankReadings::default(); + for pin in bank.pins_iterator() { + let group = pin.group(); + let value = self.group_get_value(group); + let reading = TscChannelReading { + sensor_value: value, + tsc_pin: pin, + }; + let index: usize = group.into(); + bank_readings.groups[index] = Some(reading); + } + bank_readings + } + + /// Creates a new TSC acquisition bank from the provided pin configuration. + /// + /// This method creates a `TscAcquisitionBank` that can be used for efficient, + /// repeated TSC acquisitions. It automatically generates the appropriate mask + /// for the provided pins. + /// + /// # Note on TSC Hardware Limitation + /// + /// The TSC hardware can only read one channel pin from each TSC group per acquisition. + /// + /// # Arguments + /// * `acquisition_bank_pins` - The pin configuration for the acquisition bank. + /// + /// # Returns + /// A new `TscAcquisitionBank` instance. + /// + /// # Example + /// + /// ``` + /// let tsc = // ... initialize TSC + /// let tsc_sensor1: TscIOPinWithRole = ...; + /// let tsc_sensor2: TscIOPinWithRole = ...; + /// + /// let bank = tsc.create_acquisition_bank(TscAcquisitionBankPins { + /// g1_pin: Some(tsc_sensor1), + /// g2_pin: Some(tsc_sensor2), + /// ..Default::default() + /// }); + /// + /// // Use the bank for acquisitions + /// tsc.set_active_channels_bank(&bank); + /// tsc.start(); + /// // ... perform acquisition ... + /// ``` + pub fn create_acquisition_bank(&self, acquisition_bank_pins: TscAcquisitionBankPins) -> TscAcquisitionBank { + let bank_mask = acquisition_bank_pins.iter().fold(0u32, BitOr::bitor); + + TscAcquisitionBank { + pins: acquisition_bank_pins, + mask: bank_mask, + } + } + + fn make_channels_mask(&self, channels: Itt) -> Result + where + Itt: IntoIterator, + { + let mut group_mask = 0u32; + let mut channel_mask = 0u32; + + for channel in channels { + if !self.is_channel_pin(channel) { + return Err(AcquisitionBankError::InvalidChannelPin); + } + + let group = channel.group(); + let group_bit: u32 = 1 << Into::::into(group); + if group_mask & group_bit != 0 { + return Err(AcquisitionBankError::MultipleChannelsPerGroup); + } + + group_mask |= group_bit; + channel_mask |= channel; + } + + Ok(channel_mask) + } + + /// Sets the active channels for the next TSC acquisition. + /// + /// This is a low-level method that directly sets the channel mask. For most use cases, + /// consider using `set_active_channels_bank` with a `TscAcquisitionBank` instead, which + /// provides a higher-level interface and additional safety checks. + /// + /// This method configures which sensor channels will be read during the next + /// touch sensing acquisition cycle. It should be called before starting a new + /// acquisition with the start() method. + /// + /// # Arguments + /// * `mask` - A 32-bit mask where each bit represents a channel. Set bits indicate + /// active channels. + /// + /// # Note + /// Only one pin from each TSC group can be read for each acquisition. This method + /// does not perform checks to ensure this limitation is met. Incorrect masks may + /// lead to unexpected behavior. + /// + /// # Safety + /// This method doesn't perform extensive checks on the provided mask. Ensure that + /// the mask is valid and adheres to hardware limitations to avoid undefined behavior. + pub fn set_active_channels_mask(&mut self, mask: u32) { + T::regs().ioccr().write(|w| w.0 = mask | self.masks.shield_ios); + } + + /// Convenience method for setting active channels directly from a slice of TscIOPin. + /// This method performs safety checks but is less efficient for repeated use. + pub fn set_active_channels(&mut self, channels: &[TscIOPin]) -> Result<(), AcquisitionBankError> { + let mask = self.make_channels_mask(channels.iter().cloned())?; + self.set_active_channels_mask(mask); + Ok(()) + } + + /// Sets the active channels for the next TSC acquisition using a pre-configured acquisition bank. + /// + /// This method efficiently configures the TSC peripheral to read the channels specified + /// in the provided `TscAcquisitionBank`. It's the recommended way to set up + /// channel configurations for acquisition, especially when using the same set of channels repeatedly. + /// + /// # Arguments + /// + /// * `bank` - A reference to a `TscAcquisitionBank` containing the pre-configured + /// TSC channel mask. + /// + /// # Example + /// + /// ``` + /// let tsc_sensor1: TscIOPinWithRole = ...; + /// let tsc_sensor2: TscIOPinWithRole = ...; + /// let mut touch_controller: Tsc<'_, TSC, Async> = ...; + /// let bank = touch_controller.create_acquisition_bank(TscAcquisitionBankPins { + /// g1_pin: Some(tsc_sensor1), + /// g2_pin: Some(tsc_sensor2), + /// ..Default::default() + /// }); + /// + /// touch_controller.set_active_channels_bank(&bank); + /// touch_controller.start(); + /// // ... perform acquisition ... + /// ``` + /// + /// This method should be called before starting a new acquisition with the `start()` method. + pub fn set_active_channels_bank(&mut self, bank: &TscAcquisitionBank) { + self.set_active_channels_mask(bank.mask) + } + + fn extract_groups(io_mask: u32) -> u32 { + let mut groups: u32 = 0; + for idx in 0..TSC_NUM_GROUPS { + if io_mask & (0x0F << (idx * 4)) != 0 { + groups |= 1 << idx + } + } + groups + } + + fn new_inner( + peri: impl Peripheral

+ 'd, + pin_groups: PinGroups<'d, T>, + config: Config, + ) -> Result { + into_ref!(peri); + + pin_groups.check()?; + + let masks = TscIOMasks { + channel_ios: pin_groups.make_channel_ios_mask(), + shield_ios: pin_groups.make_shield_ios_mask(), + sampling_ios: pin_groups.make_sample_ios_mask(), + }; + + rcc::enable_and_reset::(); + + T::regs().cr().modify(|w| { + w.set_tsce(true); + w.set_ctph(config.ct_pulse_high_length.into()); + w.set_ctpl(config.ct_pulse_low_length.into()); + w.set_sse(config.spread_spectrum); + // Prevent invalid configuration for pulse generator prescaler + if config.ct_pulse_low_length == ChargeTransferPulseCycle::_1 + && (config.pulse_generator_prescaler == PGPrescalerDivider::_1 + || config.pulse_generator_prescaler == PGPrescalerDivider::_2) + { + w.set_pgpsc(PGPrescalerDivider::_4.into()); + } else if config.ct_pulse_low_length == ChargeTransferPulseCycle::_2 + && config.pulse_generator_prescaler == PGPrescalerDivider::_1 + { + w.set_pgpsc(PGPrescalerDivider::_2.into()); + } else { + w.set_pgpsc(config.pulse_generator_prescaler.into()); + } + w.set_ssd(config.spread_spectrum_deviation.into()); + w.set_sspsc(config.spread_spectrum_prescaler); + + w.set_mcv(config.max_count_value.into()); + w.set_syncpol(config.synchro_pin_polarity); + w.set_am(config.acquisition_mode); + }); + + // Set IO configuration + // Disable Schmitt trigger hysteresis on all used TSC IOs + T::regs() + .iohcr() + .write(|w| w.0 = !(masks.channel_ios | masks.shield_ios | masks.sampling_ios)); + + // Set channel and shield IOs + T::regs().ioccr().write(|w| w.0 = masks.channel_ios | masks.shield_ios); + + // Set sampling IOs + T::regs().ioscr().write(|w| w.0 = masks.sampling_ios); + + // Set the groups to be acquired + // Lower bits of `iogcsr` are for enabling groups, while the higher bits are for reading + // status of acquisiton for a group, see method `Tsc::group_get_status`. + T::regs() + .iogcsr() + .write(|w| w.0 = Self::extract_groups(masks.channel_ios)); + + // Disable interrupts + T::regs().ier().modify(|w| { + w.set_eoaie(false); + w.set_mceie(false); + }); + + // Clear flags + T::regs().icr().modify(|w| { + w.set_eoaic(true); + w.set_mceic(true); + }); + + unsafe { + T::Interrupt::enable(); + } + + Ok(Self { + _peri: peri, + _pin_groups: pin_groups, + state: State::Ready, + config, + masks, + _kind: PhantomData, + }) + } + + /// Start charge transfer acquisition + pub fn start(&mut self) { + self.state = State::Busy; + + // Disable interrupts + T::regs().ier().modify(|w| { + w.set_eoaie(false); + w.set_mceie(false); + }); + + // Clear flags + T::regs().icr().modify(|w| { + w.set_eoaic(true); + w.set_mceic(true); + }); + + // Set the touch sensing IOs not acquired to the default mode + T::regs().cr().modify(|w| { + w.set_iodef(self.config.io_default_mode); + }); + + // Start the acquisition + T::regs().cr().modify(|w| { + w.set_start(true); + }); + } + + /// Stop charge transfer acquisition + pub fn stop(&mut self) { + T::regs().cr().modify(|w| { + w.set_start(false); + }); + + // Set the touch sensing IOs in low power mode + T::regs().cr().modify(|w| { + w.set_iodef(false); + }); + + // Clear flags + T::regs().icr().modify(|w| { + w.set_eoaic(true); + w.set_mceic(true); + }); + + self.state = State::Ready; + } + + /// Get current state of acquisition + pub fn get_state(&mut self) -> State { + if self.state == State::Busy && T::regs().isr().read().eoaf() { + if T::regs().isr().read().mcef() { + self.state = State::Error + } else { + self.state = State::Ready + } + } + self.state + } + + /// Get the individual group status to check acquisition complete + pub fn group_get_status(&self, index: Group) -> GroupStatus { + // Status bits are set by hardware when the acquisition on the corresponding + // enabled analog IO group is complete, cleared when new acquisition is started + let status = match index { + Group::One => T::regs().iogcsr().read().g1s(), + Group::Two => T::regs().iogcsr().read().g2s(), + Group::Three => T::regs().iogcsr().read().g3s(), + Group::Four => T::regs().iogcsr().read().g4s(), + Group::Five => T::regs().iogcsr().read().g5s(), + Group::Six => T::regs().iogcsr().read().g6s(), + #[cfg(any(tsc_v2, tsc_v3))] + Group::Seven => T::regs().iogcsr().read().g7s(), + #[cfg(tsc_v3)] + Group::Eight => T::regs().iogcsr().read().g8s(), + }; + match status { + true => GroupStatus::Complete, + false => GroupStatus::Ongoing, + } + } + + /// Get the count for the acquisiton, valid once group status is set + pub fn group_get_value(&self, index: Group) -> u16 { + T::regs().iogcr(index.into()).read().cnt() + } + + /// Discharge the IOs for subsequent acquisition + pub fn discharge_io(&mut self, status: bool) { + // Set the touch sensing IOs in low power mode + T::regs().cr().modify(|w| { + w.set_iodef(!status); + }); + } +} + +impl<'d, T: Instance, K: PeriMode> Drop for Tsc<'d, T, K> { + fn drop(&mut self) { + rcc::disable::(); + } +} + +impl<'d, T: Instance> Tsc<'d, T, Async> { + /// Create a Tsc instance that can be awaited for completion + pub fn new_async( + peri: impl Peripheral

+ 'd, + pin_groups: PinGroups<'d, T>, + config: Config, + _irq: impl interrupt::typelevel::Binding> + 'd, + ) -> Result { + Self::new_inner(peri, pin_groups, config) + } + + /// Asyncronously wait for the end of an acquisition + pub async fn pend_for_acquisition(&mut self) { + poll_fn(|cx| match self.get_state() { + State::Busy => { + T::waker().register(cx.waker()); + T::regs().ier().write(|w| w.set_eoaie(true)); + if self.get_state() != State::Busy { + T::regs().ier().write(|w| w.set_eoaie(false)); + return Poll::Ready(()); + } + Poll::Pending + } + _ => { + T::regs().ier().write(|w| w.set_eoaie(false)); + Poll::Ready(()) + } + }) + .await; + } +} + +impl<'d, T: Instance> Tsc<'d, T, Blocking> { + /// Create a Tsc instance that must be polled for completion + pub fn new_blocking( + peri: impl Peripheral

+ 'd, + pin_groups: PinGroups<'d, T>, + config: Config, + ) -> Result { + Self::new_inner(peri, pin_groups, config) + } + + /// Wait for end of acquisition + pub fn poll_for_acquisition(&mut self) { + while self.get_state() == State::Busy {} + } +} diff --git a/embassy-stm32/src/tsc/enums.rs b/embassy-stm32/src/tsc/tsc_io_pin.rs similarity index 52% rename from embassy-stm32/src/tsc/enums.rs rename to embassy-stm32/src/tsc/tsc_io_pin.rs index 0d34a43ec..38b27d0b3 100644 --- a/embassy-stm32/src/tsc/enums.rs +++ b/embassy-stm32/src/tsc/tsc_io_pin.rs @@ -1,7 +1,13 @@ -use core::ops::BitOr; +use core::marker::PhantomData; +use core::ops::{BitAnd, BitOr, BitOrAssign}; + +use super::tsc_pin_roles; +use super::types::Group; /// Pin defines #[allow(missing_docs)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +#[derive(PartialEq, Clone, Copy, Debug)] pub enum TscIOPin { Group1Io1, Group1Io2, @@ -45,6 +51,53 @@ pub enum TscIOPin { Group8Io4, } +/// Represents a TSC I/O pin with associated group and role information. +/// +/// This type combines a `TscIOPin` with phantom type parameters to statically +/// encode the pin's group and role. This allows for type-safe operations +/// on TSC pins within their specific contexts. +/// +/// - `Group`: A type parameter representing the TSC group (e.g., `G1`, `G2`). +/// - `Role`: A type parameter representing the pin's role (e.g., `Channel`, `Sample`). +#[derive(Clone, Copy, Debug)] +pub struct TscIOPinWithRole { + /// The underlying TSC I/O pin. + pub pin: TscIOPin, + pub(super) phantom: PhantomData<(Group, Role)>, +} + +impl TscIOPinWithRole { + pub(super) fn get_pin(wrapped_pin: TscIOPinWithRole) -> TscIOPin { + wrapped_pin.pin + } +} + +impl TscIOPin { + /// Maps this TscIOPin to the Group it belongs to. + /// + /// This method provides a convenient way to determine which Group + /// a specific TSC I/O pin is associated with. + pub const fn group(&self) -> Group { + match self { + TscIOPin::Group1Io1 | TscIOPin::Group1Io2 | TscIOPin::Group1Io3 | TscIOPin::Group1Io4 => Group::One, + TscIOPin::Group2Io1 | TscIOPin::Group2Io2 | TscIOPin::Group2Io3 | TscIOPin::Group2Io4 => Group::Two, + TscIOPin::Group3Io1 | TscIOPin::Group3Io2 | TscIOPin::Group3Io3 | TscIOPin::Group3Io4 => Group::Three, + TscIOPin::Group4Io1 | TscIOPin::Group4Io2 | TscIOPin::Group4Io3 | TscIOPin::Group4Io4 => Group::Four, + TscIOPin::Group5Io1 | TscIOPin::Group5Io2 | TscIOPin::Group5Io3 | TscIOPin::Group5Io4 => Group::Five, + TscIOPin::Group6Io1 | TscIOPin::Group6Io2 | TscIOPin::Group6Io3 | TscIOPin::Group6Io4 => Group::Six, + #[cfg(any(tsc_v2, tsc_v3))] + TscIOPin::Group7Io1 | TscIOPin::Group7Io2 | TscIOPin::Group7Io3 | TscIOPin::Group7Io4 => Group::Seven, + #[cfg(tsc_v3)] + TscIOPin::Group8Io1 | TscIOPin::Group8Io2 | TscIOPin::Group8Io3 | TscIOPin::Group8Io4 => Group::Eight, + } + } + + /// Returns the `Group` associated with the given `TscIOPin`. + pub fn get_group(pin: TscIOPin) -> Group { + pin.group() + } +} + impl BitOr for u32 { type Output = u32; fn bitor(self, rhs: TscIOPin) -> Self::Output { @@ -70,8 +123,31 @@ impl BitOr for TscIOPin { } } -impl Into for TscIOPin { - fn into(self) -> u32 { +impl BitOrAssign for u32 { + fn bitor_assign(&mut self, rhs: TscIOPin) { + let rhs: u32 = rhs.into(); + *self |= rhs; + } +} + +impl BitAnd for u32 { + type Output = u32; + fn bitand(self, rhs: TscIOPin) -> Self::Output { + let rhs: u32 = rhs.into(); + self & rhs + } +} + +impl BitAnd for TscIOPin { + type Output = u32; + fn bitand(self, rhs: u32) -> Self::Output { + let val: u32 = self.into(); + val & rhs + } +} + +impl TscIOPin { + const fn to_u32(self) -> u32 { match self { TscIOPin::Group1Io1 => 0x00000001, TscIOPin::Group1Io2 => 0x00000002, @@ -117,122 +193,8 @@ impl Into for TscIOPin { } } -/// Spread Spectrum Deviation -#[derive(Copy, Clone)] -pub struct SSDeviation(u8); -impl SSDeviation { - /// Create new deviation value, acceptable inputs are 1-128 - pub fn new(val: u8) -> Result { - if val == 0 || val > 128 { - return Err(()); - } - Ok(Self(val - 1)) - } -} - -impl Into for SSDeviation { - fn into(self) -> u8 { - self.0 - } -} - -/// Charge transfer pulse cycles -#[allow(missing_docs)] -#[derive(Copy, Clone, PartialEq)] -pub enum ChargeTransferPulseCycle { - _1, - _2, - _3, - _4, - _5, - _6, - _7, - _8, - _9, - _10, - _11, - _12, - _13, - _14, - _15, - _16, -} - -impl Into for ChargeTransferPulseCycle { - fn into(self) -> u8 { - match self { - ChargeTransferPulseCycle::_1 => 0, - ChargeTransferPulseCycle::_2 => 1, - ChargeTransferPulseCycle::_3 => 2, - ChargeTransferPulseCycle::_4 => 3, - ChargeTransferPulseCycle::_5 => 4, - ChargeTransferPulseCycle::_6 => 5, - ChargeTransferPulseCycle::_7 => 6, - ChargeTransferPulseCycle::_8 => 7, - ChargeTransferPulseCycle::_9 => 8, - ChargeTransferPulseCycle::_10 => 9, - ChargeTransferPulseCycle::_11 => 10, - ChargeTransferPulseCycle::_12 => 11, - ChargeTransferPulseCycle::_13 => 12, - ChargeTransferPulseCycle::_14 => 13, - ChargeTransferPulseCycle::_15 => 14, - ChargeTransferPulseCycle::_16 => 15, - } - } -} - -/// Prescaler divider -#[allow(missing_docs)] -#[derive(Copy, Clone, PartialEq)] -pub enum PGPrescalerDivider { - _1, - _2, - _4, - _8, - _16, - _32, - _64, - _128, -} - -impl Into for PGPrescalerDivider { - fn into(self) -> u8 { - match self { - PGPrescalerDivider::_1 => 0, - PGPrescalerDivider::_2 => 1, - PGPrescalerDivider::_4 => 2, - PGPrescalerDivider::_8 => 3, - PGPrescalerDivider::_16 => 4, - PGPrescalerDivider::_32 => 5, - PGPrescalerDivider::_64 => 6, - PGPrescalerDivider::_128 => 7, - } - } -} - -/// Max count -#[allow(missing_docs)] -#[derive(Copy, Clone)] -pub enum MaxCount { - _255, - _511, - _1023, - _2047, - _4095, - _8191, - _16383, -} - -impl Into for MaxCount { - fn into(self) -> u8 { - match self { - MaxCount::_255 => 0, - MaxCount::_511 => 1, - MaxCount::_1023 => 2, - MaxCount::_2047 => 3, - MaxCount::_4095 => 4, - MaxCount::_8191 => 5, - MaxCount::_16383 => 6, - } +impl Into for TscIOPin { + fn into(self) -> u32 { + self.to_u32() } } diff --git a/embassy-stm32/src/tsc/types.rs b/embassy-stm32/src/tsc/types.rs new file mode 100644 index 000000000..0e8fa7f28 --- /dev/null +++ b/embassy-stm32/src/tsc/types.rs @@ -0,0 +1,93 @@ +/// Peripheral state +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +#[derive(PartialEq, Clone, Copy)] +pub enum State { + /// Peripheral is being setup or reconfigured + Reset, + /// Ready to start acquisition + Ready, + /// In process of sensor acquisition + Busy, + /// Error occured during acquisition + Error, +} + +/// Individual group status checked after acquisition reported as complete +/// For groups with multiple channel pins, may take longer because acquisitions +/// are done sequentially. Check this status before pulling count for each +/// sampled channel +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +#[derive(PartialEq, Clone, Copy)] +pub enum GroupStatus { + /// Acquisition for channel still in progress + Ongoing, + /// Acquisition either not started or complete + Complete, +} + +/// Group identifier used to interrogate status +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +#[allow(missing_docs)] +#[derive(PartialEq, Clone, Copy)] +pub enum Group { + One, + Two, + Three, + Four, + Five, + Six, + #[cfg(any(tsc_v2, tsc_v3))] + Seven, + #[cfg(tsc_v3)] + Eight, +} + +impl Into for Group { + fn into(self) -> usize { + match self { + Group::One => 0, + Group::Two => 1, + Group::Three => 2, + Group::Four => 3, + Group::Five => 4, + Group::Six => 5, + #[cfg(any(tsc_v2, tsc_v3))] + Group::Seven => 6, + #[cfg(tsc_v3)] + Group::Eight => 7, + } + } +} + +/// Error returned when attempting to create a Group from an invalid numeric value. +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub struct InvalidGroupError { + invalid_value: usize, +} + +impl InvalidGroupError { + #[allow(missing_docs)] + pub fn new(value: usize) -> Self { + Self { invalid_value: value } + } +} + +impl TryFrom for Group { + type Error = InvalidGroupError; + + fn try_from(value: usize) -> Result { + match value { + 0 => Ok(Group::One), + 1 => Ok(Group::Two), + 2 => Ok(Group::Three), + 3 => Ok(Group::Four), + 4 => Ok(Group::Five), + 5 => Ok(Group::Six), + #[cfg(any(tsc_v2, tsc_v3))] + 6 => Ok(Group::Two), + #[cfg(tsc_v3)] + 7 => Ok(Group::Two), + n => Err(InvalidGroupError::new(n)), + } + } +} diff --git a/examples/stm32f3/README.md b/examples/stm32f3/README.md new file mode 100644 index 000000000..0a85c4858 --- /dev/null +++ b/examples/stm32f3/README.md @@ -0,0 +1,24 @@ +# Examples for STM32F3 family +Run individual examples with +``` +cargo run --bin +``` +for example +``` +cargo run --bin blinky +``` + +## Checklist before running examples +You might need to adjust `.cargo/config.toml`, `Cargo.toml` and possibly update pin numbers or peripherals to match the specific MCU or board you are using. + +* [ ] Update .cargo/config.toml with the correct probe-rs command to use your specific MCU. For example for F303ZE it should be `probe-rs run --chip STM32F303ZETx`. (use `probe-rs chip list` to find your chip) +* [ ] Update Cargo.toml to have the correct `embassy-stm32` feature. For example for F303ZE it should be `stm32f303ze`. Look in the `Cargo.toml` file of the `embassy-stm32` project to find the correct feature flag for your chip. +* [ ] If your board has a special clock or power configuration, make sure that it is set up appropriately. +* [ ] If your board has different pin mapping, update any pin numbers or peripherals in the given example code to match your schematic + +If you are unsure, please drop by the Embassy Matrix chat for support, and let us know: + +* Which example you are trying to run +* Which chip and board you are using + +Embassy Chat: https://matrix.to/#/#embassy-rs:matrix.org diff --git a/examples/stm32f3/src/bin/blocking-tsc.rs b/examples/stm32f3/src/bin/blocking-tsc.rs deleted file mode 100644 index 5c8dac94f..000000000 --- a/examples/stm32f3/src/bin/blocking-tsc.rs +++ /dev/null @@ -1,98 +0,0 @@ -// Example of polling TSC (Touch Sensing Controller) that lights an LED when touch is detected. -// -// Suggested physical setup on STM32F303ZE Nucleo board: -// - Connect a 1000pF capacitor between pin A0 and GND. This is your sampling capacitor. -// - Connect one end of a 1K resistor to pin A1 and leave the other end loose. -// The loose end will act as touch sensor which will register your touch. -// -// Troubleshooting the setup: -// - If no touch seems to be registered, then try to disconnect the sampling capacitor from GND momentarily, -// now the led should light up. Next try using a different value for the sampling capacitor. -// Also experiment with increasing the values for `ct_pulse_high_length`, `ct_pulse_low_length`, `pulse_generator_prescaler`, `max_count_value` and `discharge_delay`. -// -// All configuration values and sampling capacitor value have been determined experimentally. -// Suitable configuration and discharge delay values are highly dependent on the value of the sample capacitor. For example, a shorter discharge delay can be used with smaller capacitor values. -// -#![no_std] -#![no_main] - -use defmt::*; -use embassy_stm32::gpio::{Level, Output, Speed}; -use embassy_stm32::tsc::{self, *}; -use embassy_time::Timer; -use {defmt_rtt as _, panic_probe as _}; - -/// This example is written for the nucleo-stm32f303ze, with a stm32f303ze chip. -/// -/// Make sure you check/update the following (whether you use the F303ZE or another board): -/// -/// * [ ] Update .cargo/config.toml with the correct `probe-rs run --chip STM32F303ZETx`chip name. -/// * [ ] Update Cargo.toml to have the correct `embassy-stm32` feature, for F303ZE it should be `stm32f303ze`. -/// * [ ] If your board has a special clock or power configuration, make sure that it is -/// set up appropriately. -/// * [ ] If your board has different pin mapping, update any pin numbers or peripherals -/// to match your schematic -/// -/// If you are unsure, please drop by the Embassy Matrix chat for support, and let us know: -/// -/// * Which example you are trying to run -/// * Which chip and board you are using -/// -/// Embassy Chat: https://matrix.to/#/#embassy-rs:matrix.org -#[embassy_executor::main] -async fn main(_spawner: embassy_executor::Spawner) { - let device_config = embassy_stm32::Config::default(); - let context = embassy_stm32::init(device_config); - - let tsc_conf = Config { - ct_pulse_high_length: ChargeTransferPulseCycle::_8, - ct_pulse_low_length: ChargeTransferPulseCycle::_8, - spread_spectrum: false, - spread_spectrum_deviation: SSDeviation::new(2).unwrap(), - spread_spectrum_prescaler: false, - pulse_generator_prescaler: PGPrescalerDivider::_32, - max_count_value: MaxCount::_255, - io_default_mode: false, - synchro_pin_polarity: false, - acquisition_mode: false, - max_count_interrupt: false, - channel_ios: TscIOPin::Group1Io1.into(), - shield_ios: 0, // no shield - sampling_ios: TscIOPin::Group1Io2.into(), - }; - - let mut g1: PinGroup = PinGroup::new(); - g1.set_io1(context.PA0, PinType::Sample); - g1.set_io2(context.PA1, PinType::Channel); - - let mut touch_controller = tsc::Tsc::new_blocking(context.TSC, Some(g1), None, None, None, None, None, tsc_conf); - - // LED2 on the STM32F303ZE nucleo-board - let mut led = Output::new(context.PB7, Level::High, Speed::Low); - - // smaller sample capacitor discharge faster and can be used with shorter delay. - let discharge_delay = 5; // ms - - // the interval at which the loop polls for new touch sensor values - let polling_interval = 100; // ms - - info!("polling for touch"); - loop { - touch_controller.start(); - touch_controller.poll_for_acquisition(); - touch_controller.discharge_io(true); - Timer::after_millis(discharge_delay).await; - - let grp1_status = touch_controller.group_get_status(Group::One); - match grp1_status { - GroupStatus::Complete => { - let group_one_val = touch_controller.group_get_value(Group::One); - info!("{}", group_one_val); - led.set_high(); - } - GroupStatus::Ongoing => led.set_low(), - } - - Timer::after_millis(polling_interval).await; - } -} diff --git a/examples/stm32f3/src/bin/tsc_blocking.rs b/examples/stm32f3/src/bin/tsc_blocking.rs new file mode 100644 index 000000000..fa7f718e6 --- /dev/null +++ b/examples/stm32f3/src/bin/tsc_blocking.rs @@ -0,0 +1,138 @@ +// Example of blocking TSC (Touch Sensing Controller) that lights an LED when touch is detected. +// +// This example demonstrates: +// 1. Configuring a single TSC channel pin +// 2. Using the blocking TSC interface with polling +// 3. Waiting for acquisition completion using `poll_for_acquisition` +// 4. Reading touch values and controlling an LED based on the results +// +// Suggested physical setup on STM32F303ZE Nucleo board: +// - Connect a 1000pF capacitor between pin PA10 and GND. This is your sampling capacitor. +// - Connect one end of a 1K resistor to pin PA9 and leave the other end loose. +// The loose end will act as the touch sensor which will register your touch. +// +// The example uses two pins from Group 4 of the TSC: +// - PA10 as the sampling capacitor, TSC group 4 IO2 (D68 on the STM32F303ZE nucleo-board) +// - PA9 as the channel pin, TSC group 4 IO1 (D69 on the STM32F303ZE nucleo-board) +// +// The program continuously reads the touch sensor value: +// - It starts acquisition, waits for completion using `poll_for_acquisition`, and reads the value. +// - The LED is turned on when touch is detected (sensor value < 40). +// - Touch values are logged to the console. +// +// Troubleshooting: +// - If touch is not detected, try adjusting the SENSOR_THRESHOLD value. +// - Experiment with different values for ct_pulse_high_length, ct_pulse_low_length, +// pulse_generator_prescaler, max_count_value, and discharge_delay to optimize sensitivity. +// +// Note: Configuration values and sampling capacitor value have been determined experimentally. +// Optimal values may vary based on your specific hardware setup. +// Pins have been chosen for their convenient locations on the STM32F303ZE board. Refer to the +// official relevant STM32 datasheets and user nucleo-board user manuals to find suitable +// alternative pins. + +#![no_std] +#![no_main] + +use defmt::*; +use embassy_stm32::gpio::{Level, Output, Speed}; +use embassy_stm32::tsc::{self, *}; +use embassy_stm32::{mode, peripherals}; +use embassy_time::Timer; +use {defmt_rtt as _, panic_probe as _}; + +const SENSOR_THRESHOLD: u16 = 25; // Adjust this value based on your setup + +#[embassy_executor::main] +async fn main(_spawner: embassy_executor::Spawner) { + let device_config = embassy_stm32::Config::default(); + let context = embassy_stm32::init(device_config); + + let tsc_conf = Config { + ct_pulse_high_length: ChargeTransferPulseCycle::_4, + ct_pulse_low_length: ChargeTransferPulseCycle::_4, + spread_spectrum: false, + spread_spectrum_deviation: SSDeviation::new(2).unwrap(), + spread_spectrum_prescaler: false, + pulse_generator_prescaler: PGPrescalerDivider::_16, + max_count_value: MaxCount::_255, + io_default_mode: false, + synchro_pin_polarity: false, + acquisition_mode: false, + max_count_interrupt: false, + }; + + let mut g: PinGroupWithRoles = PinGroupWithRoles::default(); + // D68 on the STM32F303ZE nucleo-board + g.set_io2::(context.PA10); + // D69 on the STM32F303ZE nucleo-board + let tsc_sensor = g.set_io1::(context.PA9); + + let pin_groups: PinGroups = PinGroups { + g4: Some(g.pin_group), + ..Default::default() + }; + + let mut touch_controller = tsc::Tsc::new_blocking(context.TSC, pin_groups, tsc_conf).unwrap(); + + // Check if TSC is ready + if touch_controller.get_state() != State::Ready { + crate::panic!("TSC not ready!"); + } + info!("TSC initialized successfully"); + + // LED2 on the STM32F303ZE nucleo-board + let mut led = Output::new(context.PB7, Level::High, Speed::Low); + + // smaller sample capacitor discharge faster and can be used with shorter delay. + let discharge_delay = 5; // ms + + // the interval at which the loop polls for new touch sensor values + let polling_interval = 100; // ms + + info!("polling for touch"); + loop { + touch_controller.set_active_channels_mask(tsc_sensor.pin.into()); + touch_controller.start(); + touch_controller.poll_for_acquisition(); + touch_controller.discharge_io(true); + Timer::after_millis(discharge_delay).await; + + match read_touch_value(&mut touch_controller, tsc_sensor.pin).await { + Some(v) => { + info!("sensor value {}", v); + if v < SENSOR_THRESHOLD { + led.set_high(); + } else { + led.set_low(); + } + } + None => led.set_low(), + } + + Timer::after_millis(polling_interval).await; + } +} + +const MAX_GROUP_STATUS_READ_ATTEMPTS: usize = 10; + +// attempt to read group status and delay when still ongoing +async fn read_touch_value( + touch_controller: &mut tsc::Tsc<'_, peripherals::TSC, mode::Blocking>, + sensor_pin: TscIOPin, +) -> Option { + for _ in 0..MAX_GROUP_STATUS_READ_ATTEMPTS { + match touch_controller.group_get_status(sensor_pin.group()) { + GroupStatus::Complete => { + return Some(touch_controller.group_get_value(sensor_pin.group())); + } + GroupStatus::Ongoing => { + // if you end up here a lot, then you prob need to increase discharge_delay + // or consider changing the code to adjust the discharge_delay dynamically + info!("Acquisition still ongoing"); + Timer::after_millis(1).await; + } + } + } + None +} diff --git a/examples/stm32l0/.cargo/config.toml b/examples/stm32l0/.cargo/config.toml index b050334b2..fed9cf9ce 100644 --- a/examples/stm32l0/.cargo/config.toml +++ b/examples/stm32l0/.cargo/config.toml @@ -1,6 +1,6 @@ [target.'cfg(all(target_arch = "arm", target_os = "none"))'] # replace your chip as listed in `probe-rs chip list` -runner = "probe-rs run --chip STM32L053R8Tx" +runner = "probe-rs run --chip STM32L073RZTx" [build] target = "thumbv6m-none-eabi" diff --git a/examples/stm32l0/Cargo.toml b/examples/stm32l0/Cargo.toml index 95e215b6f..9d234804a 100644 --- a/examples/stm32l0/Cargo.toml +++ b/examples/stm32l0/Cargo.toml @@ -6,7 +6,7 @@ license = "MIT OR Apache-2.0" [dependencies] # Change stm32l072cz to your chip name, if necessary. -embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["defmt", "stm32l072cz", "unstable-pac", "time-driver-any", "exti", "memory-x"] } +embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["defmt", "stm32l073rz", "unstable-pac", "time-driver-any", "exti", "memory-x"] } embassy-sync = { version = "0.6.1", path = "../../embassy-sync", features = ["defmt"] } embassy-executor = { version = "0.6.3", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] } embassy-time = { version = "0.3.2", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } diff --git a/examples/stm32l0/README.md b/examples/stm32l0/README.md new file mode 100644 index 000000000..82d222027 --- /dev/null +++ b/examples/stm32l0/README.md @@ -0,0 +1,24 @@ +# Examples for STM32L0 family +Run individual examples with +``` +cargo run --bin +``` +for example +``` +cargo run --bin blinky +``` + +## Checklist before running examples +You might need to adjust `.cargo/config.toml`, `Cargo.toml` and possibly update pin numbers or peripherals to match the specific MCU or board you are using. + +* [ ] Update .cargo/config.toml with the correct probe-rs command to use your specific MCU. For example for L073RZ it should be `probe-rs run --chip STM32L073RZTx`. (use `probe-rs chip list` to find your chip) +* [ ] Update Cargo.toml to have the correct `embassy-stm32` feature. For example for L073RZ it should be `stm32l073rz`. Look in the `Cargo.toml` file of the `embassy-stm32` project to find the correct feature flag for your chip. +* [ ] If your board has a special clock or power configuration, make sure that it is set up appropriately. +* [ ] If your board has different pin mapping, update any pin numbers or peripherals in the given example code to match your schematic + +If you are unsure, please drop by the Embassy Matrix chat for support, and let us know: + +* Which example you are trying to run +* Which chip and board you are using + +Embassy Chat: https://matrix.to/#/#embassy-rs:matrix.org diff --git a/examples/stm32l0/src/bin/async-tsc.rs b/examples/stm32l0/src/bin/async-tsc.rs deleted file mode 100644 index c40b86af9..000000000 --- a/examples/stm32l0/src/bin/async-tsc.rs +++ /dev/null @@ -1,122 +0,0 @@ -// Example of async TSC (Touch Sensing Controller) that lights an LED when touch is detected. -// -// Suggested physical setup on STM32L073RZ Nucleo board: -// - Connect a 1000pF capacitor between pin A0 and GND. This is your sampling capacitor. -// - Connect one end of a 1K resistor to pin A1 and leave the other end loose. -// The loose end will act as touch sensor which will register your touch. -// -// Troubleshooting the setup: -// - If no touch seems to be registered, then try to disconnect the sampling capacitor from GND momentarily, -// now the led should light up. Next try using a different value for the sampling capacitor. -// Also experiment with increasing the values for `ct_pulse_high_length`, `ct_pulse_low_length`, `pulse_generator_prescaler`, `max_count_value` and `discharge_delay`. -// -// All configuration values and sampling capacitor value have been determined experimentally. -// Suitable configuration and discharge delay values are highly dependent on the value of the sample capacitor. For example, a shorter discharge delay can be used with smaller capacitor values. -// -#![no_std] -#![no_main] - -use defmt::*; -use embassy_stm32::bind_interrupts; -use embassy_stm32::gpio::{Level, Output, Speed}; -use embassy_stm32::tsc::{self, *}; -use embassy_time::Timer; -use {defmt_rtt as _, panic_probe as _}; - -bind_interrupts!(struct Irqs { - TSC => InterruptHandler; -}); - -#[cortex_m_rt::exception] -unsafe fn HardFault(_: &cortex_m_rt::ExceptionFrame) -> ! { - cortex_m::peripheral::SCB::sys_reset(); -} - -/// This example is written for the nucleo-stm32l073rz, with a stm32l073rz chip. -/// -/// Make sure you check/update the following (whether you use the L073RZ or another board): -/// -/// * [ ] Update .cargo/config.toml with the correct `probe-rs run --chip STM32L073RZTx`chip name. -/// * [ ] Update Cargo.toml to have the correct `embassy-stm32` feature, for L073RZ it should be `stm32l073rz`. -/// * [ ] If your board has a special clock or power configuration, make sure that it is -/// set up appropriately. -/// * [ ] If your board has different pin mapping, update any pin numbers or peripherals -/// to match your schematic -/// -/// If you are unsure, please drop by the Embassy Matrix chat for support, and let us know: -/// -/// * Which example you are trying to run -/// * Which chip and board you are using -/// -/// Embassy Chat: https://matrix.to/#/#embassy-rs:matrix.org -#[embassy_executor::main] -async fn main(_spawner: embassy_executor::Spawner) { - let device_config = embassy_stm32::Config::default(); - let context = embassy_stm32::init(device_config); - - let config = tsc::Config { - ct_pulse_high_length: ChargeTransferPulseCycle::_4, - ct_pulse_low_length: ChargeTransferPulseCycle::_4, - spread_spectrum: false, - spread_spectrum_deviation: SSDeviation::new(2).unwrap(), - spread_spectrum_prescaler: false, - pulse_generator_prescaler: PGPrescalerDivider::_16, - max_count_value: MaxCount::_255, - io_default_mode: false, - synchro_pin_polarity: false, - acquisition_mode: false, - max_count_interrupt: false, - channel_ios: TscIOPin::Group1Io1.into(), - shield_ios: 0, // no shield - sampling_ios: TscIOPin::Group1Io2.into(), - }; - - let mut g1: PinGroup = PinGroup::new(); - g1.set_io1(context.PA0, PinType::Sample); - g1.set_io2(context.PA1, PinType::Channel); - - let mut touch_controller = tsc::Tsc::new_async( - context.TSC, - Some(g1), - None, - None, - None, - None, - None, - None, - None, - config, - Irqs, - ); - - // Check if TSC is ready - if touch_controller.get_state() != State::Ready { - info!("TSC not ready!"); - loop {} // Halt execution - } - info!("TSC initialized successfully"); - - // LED2 on the STM32L073RZ nucleo-board (PA5) - let mut led = Output::new(context.PA5, Level::High, Speed::Low); - - // smaller sample capacitor discharge faster and can be used with shorter delay. - let discharge_delay = 5; // ms - - info!("Starting touch_controller interface"); - loop { - touch_controller.start(); - touch_controller.pend_for_acquisition().await; - touch_controller.discharge_io(true); - Timer::after_millis(discharge_delay).await; - - let grp1_status = touch_controller.group_get_status(Group::One); - match grp1_status { - GroupStatus::Complete => { - let group_one_val = touch_controller.group_get_value(Group::One); - info!("{}", group_one_val); - led.set_high(); - } - GroupStatus::Ongoing => led.set_low(), - } - } -} diff --git a/examples/stm32l0/src/bin/blocking-tsc.rs b/examples/stm32l0/src/bin/blocking-tsc.rs deleted file mode 100644 index 7e4f40946..000000000 --- a/examples/stm32l0/src/bin/blocking-tsc.rs +++ /dev/null @@ -1,116 +0,0 @@ -// Example of polling TSC (Touch Sensing Controller) that lights an LED when touch is detected. -// -// Suggested physical setup on STM32L073RZ Nucleo board: -// - Connect a 1000pF capacitor between pin A0 and GND. This is your sampling capacitor. -// - Connect one end of a 1K resistor to pin A1 and leave the other end loose. -// The loose end will act as touch sensor which will register your touch. -// -// Troubleshooting the setup: -// - If no touch seems to be registered, then try to disconnect the sampling capacitor from GND momentarily, -// now the led should light up. Next try using a different value for the sampling capacitor. -// Also experiment with increasing the values for `ct_pulse_high_length`, `ct_pulse_low_length`, `pulse_generator_prescaler`, `max_count_value` and `discharge_delay`. -// -// All configuration values and sampling capacitor value have been determined experimentally. -// Suitable configuration and discharge delay values are highly dependent on the value of the sample capacitor. For example, a shorter discharge delay can be used with smaller capacitor values. -// -#![no_std] -#![no_main] - -use defmt::*; -use embassy_stm32::gpio::{Level, Output, Speed}; -use embassy_stm32::tsc::{self, *}; -use embassy_time::Timer; -use {defmt_rtt as _, panic_probe as _}; - -/// This example is written for the nucleo-stm32l073rz, with a stm32l073rz chip. -/// -/// Make sure you check/update the following (whether you use the L073RZ or another board): -/// -/// * [ ] Update .cargo/config.toml with the correct `probe-rs run --chip STM32L073RZTx`chip name. -/// * [ ] Update Cargo.toml to have the correct `embassy-stm32` feature, for L073RZ it should be `stm32l073rz`. -/// * [ ] If your board has a special clock or power configuration, make sure that it is -/// set up appropriately. -/// * [ ] If your board has different pin mapping, update any pin numbers or peripherals -/// to match your schematic -/// -/// If you are unsure, please drop by the Embassy Matrix chat for support, and let us know: -/// -/// * Which example you are trying to run -/// * Which chip and board you are using -/// -/// Embassy Chat: https://matrix.to/#/#embassy-rs:matrix.org -#[embassy_executor::main] -async fn main(_spawner: embassy_executor::Spawner) { - let device_config = embassy_stm32::Config::default(); - let context = embassy_stm32::init(device_config); - - let tsc_conf = Config { - ct_pulse_high_length: ChargeTransferPulseCycle::_4, - ct_pulse_low_length: ChargeTransferPulseCycle::_4, - spread_spectrum: false, - spread_spectrum_deviation: SSDeviation::new(2).unwrap(), - spread_spectrum_prescaler: false, - pulse_generator_prescaler: PGPrescalerDivider::_16, - max_count_value: MaxCount::_255, - io_default_mode: false, - synchro_pin_polarity: false, - acquisition_mode: false, - max_count_interrupt: false, - channel_ios: TscIOPin::Group1Io1.into(), - shield_ios: 0, // no shield - sampling_ios: TscIOPin::Group1Io2.into(), - }; - - let mut g1: PinGroup = PinGroup::new(); - g1.set_io1(context.PA0, PinType::Sample); - g1.set_io2(context.PA1, PinType::Channel); - - let mut touch_controller = tsc::Tsc::new_blocking( - context.TSC, - Some(g1), - None, - None, - None, - None, - None, - None, - None, - tsc_conf, - ); - - // Check if TSC is ready - if touch_controller.get_state() != State::Ready { - info!("TSC not ready!"); - loop {} // Halt execution - } - info!("TSC initialized successfully"); - - // LED2 on the STM32L073RZ nucleo-board (PA5) - let mut led = Output::new(context.PA5, Level::High, Speed::Low); - - // smaller sample capacitor discharge faster and can be used with shorter delay. - let discharge_delay = 5; // ms - - // the interval at which the loop polls for new touch sensor values - let polling_interval = 100; // ms - - info!("polling for touch"); - loop { - touch_controller.start(); - touch_controller.poll_for_acquisition(); - touch_controller.discharge_io(true); - Timer::after_millis(discharge_delay).await; - - let grp1_status = touch_controller.group_get_status(Group::One); - match grp1_status { - GroupStatus::Complete => { - let group_one_val = touch_controller.group_get_value(Group::One); - info!("{}", group_one_val); - led.set_high(); - } - GroupStatus::Ongoing => led.set_low(), - } - - Timer::after_millis(polling_interval).await; - } -} diff --git a/examples/stm32l0/src/bin/tsc_async.rs b/examples/stm32l0/src/bin/tsc_async.rs new file mode 100644 index 000000000..cebe9712f --- /dev/null +++ b/examples/stm32l0/src/bin/tsc_async.rs @@ -0,0 +1,116 @@ +// Example of async TSC (Touch Sensing Controller) that lights an LED when touch is detected. +// +// This example demonstrates: +// 1. Configuring a single TSC channel pin +// 2. Using the blocking TSC interface with polling +// 3. Waiting for acquisition completion using `poll_for_acquisition` +// 4. Reading touch values and controlling an LED based on the results +// +// Suggested physical setup on STM32L073RZ Nucleo board: +// - Connect a 1000pF capacitor between pin PA0 and GND. This is your sampling capacitor. +// - Connect one end of a 1K resistor to pin PA1 and leave the other end loose. +// The loose end will act as the touch sensor which will register your touch. +// +// The example uses two pins from Group 1 of the TSC on the STM32L073RZ Nucleo board: +// - PA0 as the sampling capacitor, TSC group 1 IO1 (label A0) +// - PA1 as the channel pin, TSC group 1 IO2 (label A1) +// +// The program continuously reads the touch sensor value: +// - It starts acquisition, waits for completion using `poll_for_acquisition`, and reads the value. +// - The LED is turned on when touch is detected (sensor value < 25). +// - Touch values are logged to the console. +// +// Troubleshooting: +// - If touch is not detected, try adjusting the SENSOR_THRESHOLD value. +// - Experiment with different values for ct_pulse_high_length, ct_pulse_low_length, +// pulse_generator_prescaler, max_count_value, and discharge_delay to optimize sensitivity. +// +// Note: Configuration values and sampling capacitor value have been determined experimentally. +// Optimal values may vary based on your specific hardware setup. +// Pins have been chosen for their convenient locations on the STM32L073RZ board. Refer to the +// official relevant STM32 datasheets and nucleo-board user manuals to find suitable +// alternative pins. +// +// Beware for STM32L073RZ nucleo-board, that PA2 and PA3 is used for the uart connection to +// the programmer chip. If you try to use these two pins for TSC, you will get strange +// readings, unless you somehow reconfigure/re-wire your nucleo-board. +// No errors or warnings will be emitted, they will just silently not work as expected. +// (see nucleo user manual UM1724, Rev 14, page 25) + +#![no_std] +#![no_main] + +use defmt::*; +use embassy_stm32::gpio::{Level, Output, Speed}; +use embassy_stm32::tsc::{self, *}; +use embassy_stm32::{bind_interrupts, peripherals}; +use embassy_time::Timer; +use {defmt_rtt as _, panic_probe as _}; + +bind_interrupts!(struct Irqs { + TSC => InterruptHandler; +}); +const SENSOR_THRESHOLD: u16 = 25; // Adjust this value based on your setup + +#[embassy_executor::main] +async fn main(_spawner: embassy_executor::Spawner) { + let device_config = embassy_stm32::Config::default(); + let context = embassy_stm32::init(device_config); + + let mut pin_group: PinGroupWithRoles = PinGroupWithRoles::default(); + pin_group.set_io1::(context.PA0); + let sensor = pin_group.set_io2::(context.PA1); + + let tsc_conf = Config { + ct_pulse_high_length: ChargeTransferPulseCycle::_4, + ct_pulse_low_length: ChargeTransferPulseCycle::_4, + spread_spectrum: false, + spread_spectrum_deviation: SSDeviation::new(2).unwrap(), + spread_spectrum_prescaler: false, + pulse_generator_prescaler: PGPrescalerDivider::_16, + max_count_value: MaxCount::_255, + io_default_mode: false, + synchro_pin_polarity: false, + acquisition_mode: false, + max_count_interrupt: false, + }; + + let pin_groups: PinGroups = PinGroups { + g1: Some(pin_group.pin_group), + ..Default::default() + }; + + let mut touch_controller = tsc::Tsc::new_async(context.TSC, pin_groups, tsc_conf, Irqs).unwrap(); + + // Check if TSC is ready + if touch_controller.get_state() != State::Ready { + info!("TSC not ready!"); + return; + } + info!("TSC initialized successfully"); + + // LED2 on the STM32L073RZ nucleo-board (PA5) + let mut led = Output::new(context.PA5, Level::Low, Speed::Low); + + let discharge_delay = 5; // ms + + info!("Starting touch_controller interface"); + loop { + touch_controller.set_active_channels_mask(sensor.pin.into()); + touch_controller.start(); + touch_controller.pend_for_acquisition().await; + touch_controller.discharge_io(true); + Timer::after_millis(discharge_delay).await; + + let group_val = touch_controller.group_get_value(sensor.pin.group()); + info!("Touch value: {}", group_val); + + if group_val < SENSOR_THRESHOLD { + led.set_high(); + } else { + led.set_low(); + } + + Timer::after_millis(100).await; + } +} diff --git a/examples/stm32l0/src/bin/tsc_blocking.rs b/examples/stm32l0/src/bin/tsc_blocking.rs new file mode 100644 index 000000000..65203925c --- /dev/null +++ b/examples/stm32l0/src/bin/tsc_blocking.rs @@ -0,0 +1,142 @@ +// Example of blocking TSC (Touch Sensing Controller) that lights an LED when touch is detected. +// +// This example demonstrates: +// 1. Configuring a single TSC channel pin +// 2. Using the blocking TSC interface with polling +// 3. Waiting for acquisition completion using `poll_for_acquisition` +// 4. Reading touch values and controlling an LED based on the results +// +// Suggested physical setup on STM32L073RZ Nucleo board: +// - Connect a 1000pF capacitor between pin PA0 and GND. This is your sampling capacitor. +// - Connect one end of a 1K resistor to pin PA1 and leave the other end loose. +// The loose end will act as the touch sensor which will register your touch. +// +// The example uses two pins from Group 1 of the TSC on the STM32L073RZ Nucleo board: +// - PA0 as the sampling capacitor, TSC group 1 IO1 (label A0) +// - PA1 as the channel pin, TSC group 1 IO2 (label A1) +// +// The program continuously reads the touch sensor value: +// - It starts acquisition, waits for completion using `poll_for_acquisition`, and reads the value. +// - The LED is turned on when touch is detected (sensor value < 25). +// - Touch values are logged to the console. +// +// Troubleshooting: +// - If touch is not detected, try adjusting the SENSOR_THRESHOLD value. +// - Experiment with different values for ct_pulse_high_length, ct_pulse_low_length, +// pulse_generator_prescaler, max_count_value, and discharge_delay to optimize sensitivity. +// +// Note: Configuration values and sampling capacitor value have been determined experimentally. +// Optimal values may vary based on your specific hardware setup. +// Pins have been chosen for their convenient locations on the STM32L073RZ board. Refer to the +// official relevant STM32 datasheets and nucleo-board user manuals to find suitable +// alternative pins. +// +// Beware for STM32L073RZ nucleo-board, that PA2 and PA3 is used for the uart connection to +// the programmer chip. If you try to use these two pins for TSC, you will get strange +// readings, unless you somehow reconfigure/re-wire your nucleo-board. +// No errors or warnings will be emitted, they will just silently not work as expected. +// (see nucleo user manual UM1724, Rev 14, page 25) + +#![no_std] +#![no_main] + +use defmt::*; +use embassy_stm32::gpio::{Level, Output, Speed}; +use embassy_stm32::tsc::{self, *}; +use embassy_stm32::{mode, peripherals}; +use embassy_time::Timer; +use {defmt_rtt as _, panic_probe as _}; + +const SENSOR_THRESHOLD: u16 = 25; // Adjust this value based on your setup + +#[embassy_executor::main] +async fn main(_spawner: embassy_executor::Spawner) { + let device_config = embassy_stm32::Config::default(); + let context = embassy_stm32::init(device_config); + + let tsc_conf = Config { + ct_pulse_high_length: ChargeTransferPulseCycle::_4, + ct_pulse_low_length: ChargeTransferPulseCycle::_4, + spread_spectrum: false, + spread_spectrum_deviation: SSDeviation::new(2).unwrap(), + spread_spectrum_prescaler: false, + pulse_generator_prescaler: PGPrescalerDivider::_16, + max_count_value: MaxCount::_255, + io_default_mode: false, + synchro_pin_polarity: false, + acquisition_mode: false, + max_count_interrupt: false, + }; + + let mut g1: PinGroupWithRoles = PinGroupWithRoles::default(); + g1.set_io1::(context.PA0); + let tsc_sensor = g1.set_io2::(context.PA1); + + let pin_groups: PinGroups = PinGroups { + g1: Some(g1.pin_group), + ..Default::default() + }; + + let mut touch_controller = tsc::Tsc::new_blocking(context.TSC, pin_groups, tsc_conf).unwrap(); + + // Check if TSC is ready + if touch_controller.get_state() != State::Ready { + crate::panic!("TSC not ready!"); + } + info!("TSC initialized successfully"); + + // LED2 on the STM32L073RZ nucleo-board (PA5) + let mut led = Output::new(context.PA5, Level::High, Speed::Low); + + // smaller sample capacitor discharge faster and can be used with shorter delay. + let discharge_delay = 5; // ms + + // the interval at which the loop polls for new touch sensor values + let polling_interval = 100; // ms + + info!("polling for touch"); + loop { + touch_controller.set_active_channels_mask(tsc_sensor.pin.into()); + touch_controller.start(); + touch_controller.poll_for_acquisition(); + touch_controller.discharge_io(true); + Timer::after_millis(discharge_delay).await; + + match read_touch_value(&mut touch_controller, tsc_sensor.pin).await { + Some(v) => { + info!("sensor value {}", v); + if v < SENSOR_THRESHOLD { + led.set_high(); + } else { + led.set_low(); + } + } + None => led.set_low(), + } + + Timer::after_millis(polling_interval).await; + } +} + +const MAX_GROUP_STATUS_READ_ATTEMPTS: usize = 10; + +// attempt to read group status and delay when still ongoing +async fn read_touch_value( + touch_controller: &mut tsc::Tsc<'_, peripherals::TSC, mode::Blocking>, + sensor_pin: TscIOPin, +) -> Option { + for _ in 0..MAX_GROUP_STATUS_READ_ATTEMPTS { + match touch_controller.group_get_status(sensor_pin.group()) { + GroupStatus::Complete => { + return Some(touch_controller.group_get_value(sensor_pin.group())); + } + GroupStatus::Ongoing => { + // if you end up here a lot, then you prob need to increase discharge_delay + // or consider changing the code to adjust the discharge_delay dynamically + info!("Acquisition still ongoing"); + Timer::after_millis(1).await; + } + } + } + None +} diff --git a/examples/stm32l0/src/bin/tsc_multipin.rs b/examples/stm32l0/src/bin/tsc_multipin.rs new file mode 100644 index 000000000..6170d0799 --- /dev/null +++ b/examples/stm32l0/src/bin/tsc_multipin.rs @@ -0,0 +1,233 @@ +// Example of TSC (Touch Sensing Controller) using multiple pins from the same tsc-group. +// +// What is special about using multiple TSC pins as sensor channels from the same TSC group, +// is that only one TSC pin for each TSC group can be acquired and read at the time. +// To control which channel pins are acquired and read, we must write a mask before initiating an +// acquisition. To help manage and abstract all this business away, we can organize our channel +// pins into acquisition banks. Each acquisition bank can contain exactly one channel pin per TSC +// group and it will contain the relevant mask. +// +// This example demonstrates how to: +// 1. Configure multiple channel pins within a single TSC group +// 2. Use the set_active_channels method to switch between different channels +// 3. Read and interpret touch values from multiple channels in the same group +// +// Suggested physical setup on STM32L073RZ Nucleo board: +// - Connect a 1000pF capacitor between pin PA0 (label A0) and GND. This is the sampling capacitor for TSC +// group 1. +// - Connect one end of a 1K resistor to pin PA1 (label A1) and leave the other end loose. +// The loose end will act as a touch sensor. +// +// - Connect a 1000pF capacitor between pin PB3 (label D3) and GND. This is the sampling capacitor for TSC +// group 5. +// - Connect one end of another 1K resistor to pin PB4 and leave the other end loose. +// The loose end will act as a touch sensor. +// - Connect one end of another 1K resistor to pin PB6 and leave the other end loose. +// The loose end will act as a touch sensor. +// +// The example uses pins from two TSC groups. +// - PA0 as sampling capacitor, TSC group 1 IO1 (label A0) +// - PA1 as channel, TSC group 1 IO2 (label A1) +// - PB3 as sampling capacitor, TSC group 5 IO1 (label D3) +// - PB4 as channel, TSC group 5 IO2 (label D3) +// - PB6 as channel, TSC group 5 IO3 (label D5) +// +// The pins have been chosen to make it easy to simply add capacitors directly onto the board and +// connect one leg to GND, and to easily add resistors to the board with no special connectors, +// breadboards, special wires or soldering required. All you need is the capacitors and resistors. +// +// The program reads the designated channel pins and adjusts the LED blinking +// pattern based on which sensor(s) are touched: +// - No touch: LED off +// - one sensor touched: Slow blinking +// - two sensors touched: Fast blinking +// - three sensors touched: LED constantly on +// +// Troubleshooting: +// - If touch is not detected, try adjusting the SENSOR_THRESHOLD value. +// - Experiment with different values for ct_pulse_high_length, ct_pulse_low_length, +// pulse_generator_prescaler, max_count_value, and discharge_delay to optimize sensitivity. +// +// Note: Configuration values and sampling capacitor value have been determined experimentally. +// Optimal values may vary based on your specific hardware setup. +// Pins have been chosen for their convenient locations on the STM32L073RZ board. Refer to the +// official relevant STM32 datasheets and nucleo-board user manuals to find suitable +// alternative pins. +// +// Beware for STM32L073RZ nucleo-board, that PA2 and PA3 is used for the uart connection to +// the programmer chip. If you try to use these two pins for TSC, you will get strange +// readings, unless you somehow reconfigure/re-wire your nucleo-board. +// No errors or warnings will be emitted, they will just silently not work as expected. +// (see nucleo user manual UM1724, Rev 14, page 25) + +#![no_std] +#![no_main] + +use defmt::*; +use embassy_stm32::gpio::{Level, Output, Speed}; +use embassy_stm32::tsc::{self, *}; +use embassy_stm32::{bind_interrupts, mode, peripherals}; +use embassy_time::Timer; +use {defmt_rtt as _, panic_probe as _}; + +bind_interrupts!(struct Irqs { + TSC => InterruptHandler; +}); + +const MAX_GROUP_STATUS_READ_ATTEMPTS: usize = 10; + +async fn read_touch_values( + touch_controller: &mut tsc::Tsc<'_, peripherals::TSC, mode::Async>, + tsc_acquisition_bank: &TscAcquisitionBank, +) -> Option { + for _ in 0..MAX_GROUP_STATUS_READ_ATTEMPTS { + let status = touch_controller.get_acquisition_bank_status(tsc_acquisition_bank); + if status.all_complete() { + let r = touch_controller.get_acquisition_bank_values(tsc_acquisition_bank); + return Some(r); + } else { + info!("Acquisition still ongoing"); + Timer::after_millis(1).await; + } + } + info!("Acquisition failed after {} attempts", MAX_GROUP_STATUS_READ_ATTEMPTS); + None +} + +const SENSOR_THRESHOLD: u16 = 35; + +async fn acquire_sensors( + touch_controller: &mut Tsc<'static, peripherals::TSC, mode::Async>, + tsc_acquisition_bank: &TscAcquisitionBank, +) { + touch_controller.set_active_channels_mask(tsc_acquisition_bank.mask()); + touch_controller.start(); + touch_controller.pend_for_acquisition().await; + touch_controller.discharge_io(true); + let discharge_delay = 5; // ms + Timer::after_millis(discharge_delay).await; +} + +#[embassy_executor::main] +async fn main(_spawner: embassy_executor::Spawner) { + let device_config = embassy_stm32::Config::default(); + let context = embassy_stm32::init(device_config); + + // ---------- initial configuration of TSC ---------- + let mut pin_group1: PinGroupWithRoles = PinGroupWithRoles::default(); + pin_group1.set_io1::(context.PA0); + let tsc_sensor0 = pin_group1.set_io2(context.PA1); + + let mut pin_group5: PinGroupWithRoles = PinGroupWithRoles::default(); + pin_group5.set_io1::(context.PB3); + let tsc_sensor1 = pin_group5.set_io2(context.PB4); + let tsc_sensor2 = pin_group5.set_io3(context.PB6); + + let config = tsc::Config { + ct_pulse_high_length: ChargeTransferPulseCycle::_16, + ct_pulse_low_length: ChargeTransferPulseCycle::_16, + spread_spectrum: false, + spread_spectrum_deviation: SSDeviation::new(2).unwrap(), + spread_spectrum_prescaler: false, + pulse_generator_prescaler: PGPrescalerDivider::_16, + max_count_value: MaxCount::_255, + io_default_mode: false, + synchro_pin_polarity: false, + acquisition_mode: false, + max_count_interrupt: false, + }; + + let pin_groups: PinGroups = PinGroups { + g1: Some(pin_group1.pin_group), + g5: Some(pin_group5.pin_group), + ..Default::default() + }; + + let mut touch_controller = tsc::Tsc::new_async(context.TSC, pin_groups, config, Irqs).unwrap(); + + // ---------- setting up acquisition banks ---------- + // sensor0 and sensor1 in this example belong to different TSC-groups, + // therefore we can acquire and read them both in one go. + let bank1 = touch_controller.create_acquisition_bank(TscAcquisitionBankPins { + g1_pin: Some(tsc_sensor0), + g5_pin: Some(tsc_sensor1), + ..Default::default() + }); + // `sensor1` and `sensor2` belongs to the same TSC-group, therefore we must make sure to + // acquire them one at the time. Therefore, we organize them into different acquisition banks. + let bank2 = touch_controller.create_acquisition_bank(TscAcquisitionBankPins { + g5_pin: Some(tsc_sensor2), + ..Default::default() + }); + + // Check if TSC is ready + if touch_controller.get_state() != State::Ready { + crate::panic!("TSC not ready!"); + } + + info!("TSC initialized successfully"); + + // LED2 on the STM32L073RZ nucleo-board (PA5) + let mut led = Output::new(context.PA5, Level::High, Speed::Low); + + let mut led_state = false; + + loop { + acquire_sensors(&mut touch_controller, &bank1).await; + let readings1: TscAcquisitionBankReadings = read_touch_values(&mut touch_controller, &bank1) + .await + .expect("should be able to read values for bank 1"); + acquire_sensors(&mut touch_controller, &bank2).await; + let readings2: TscAcquisitionBankReadings = read_touch_values(&mut touch_controller, &bank2) + .await + .expect("should be able to read values for bank 2"); + + let mut touched_sensors_count = 0; + for reading in readings1.iter() { + info!("{}", reading); + if reading.sensor_value < SENSOR_THRESHOLD { + touched_sensors_count += 1; + } + } + for reading in readings2.iter() { + info!("{}", reading); + if reading.sensor_value < SENSOR_THRESHOLD { + touched_sensors_count += 1; + } + } + + match touched_sensors_count { + 0 => { + // No sensors touched, turn off the LED + led.set_low(); + led_state = false; + } + 1 => { + // One sensor touched, blink slowly + led_state = !led_state; + if led_state { + led.set_high(); + } else { + led.set_low(); + } + Timer::after_millis(200).await; + } + 2 => { + // Two sensors touched, blink faster + led_state = !led_state; + if led_state { + led.set_high(); + } else { + led.set_low(); + } + Timer::after_millis(50).await; + } + 3 => { + // All three sensors touched, LED constantly on + led.set_high(); + led_state = true; + } + _ => crate::unreachable!(), // This case should never occur with 3 sensors + } + } +} diff --git a/examples/stm32l4/.cargo/config.toml b/examples/stm32l4/.cargo/config.toml index 83fc6d6f8..d71fb1517 100644 --- a/examples/stm32l4/.cargo/config.toml +++ b/examples/stm32l4/.cargo/config.toml @@ -2,7 +2,8 @@ # replace STM32F429ZITx with your chip as listed in `probe-rs chip list` #runner = "probe-rs run --chip STM32L475VGT6" #runner = "probe-rs run --chip STM32L475VG" -runner = "probe-rs run --chip STM32L4S5QI" +#runner = "probe-rs run --chip STM32L4S5QI" +runner = "probe-rs run --chip STM32L4R5ZITxP" [build] target = "thumbv7em-none-eabi" diff --git a/examples/stm32l4/Cargo.toml b/examples/stm32l4/Cargo.toml index b172878c1..512bb8064 100644 --- a/examples/stm32l4/Cargo.toml +++ b/examples/stm32l4/Cargo.toml @@ -6,7 +6,7 @@ license = "MIT OR Apache-2.0" [dependencies] # Change stm32l4s5vi to your chip name, if necessary. -embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = [ "defmt", "unstable-pac", "stm32l4s5qi", "memory-x", "time-driver-any", "exti", "chrono"] } +embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = [ "defmt", "unstable-pac", "stm32l4r5zi", "memory-x", "time-driver-any", "exti", "chrono"] } embassy-sync = { version = "0.6.1", path = "../../embassy-sync", features = ["defmt"] } embassy-executor = { version = "0.6.3", path = "../../embassy-executor", features = ["task-arena-size-32768", "arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] } embassy-time = { version = "0.3.2", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768", ] } diff --git a/examples/stm32l4/README.md b/examples/stm32l4/README.md new file mode 100644 index 000000000..e463c18a0 --- /dev/null +++ b/examples/stm32l4/README.md @@ -0,0 +1,24 @@ +# Examples for STM32L4 family +Run individual examples with +``` +cargo run --bin +``` +for example +``` +cargo run --bin blinky +``` + +## Checklist before running examples +You might need to adjust `.cargo/config.toml`, `Cargo.toml` and possibly update pin numbers or peripherals to match the specific MCU or board you are using. + +* [ ] Update .cargo/config.toml with the correct probe-rs command to use your specific MCU. For example for L4R5ZI-P it should be `probe-rs run --chip STM32L4R5ZITxP`. (use `probe-rs chip list` to find your chip) +* [ ] Update Cargo.toml to have the correct `embassy-stm32` feature. For example for L4R5ZI-P it should be `stm32l4r5zi`. Look in the `Cargo.toml` file of the `embassy-stm32` project to find the correct feature flag for your chip. +* [ ] If your board has a special clock or power configuration, make sure that it is set up appropriately. +* [ ] If your board has different pin mapping, update any pin numbers or peripherals in the given example code to match your schematic + +If you are unsure, please drop by the Embassy Matrix chat for support, and let us know: + +* Which example you are trying to run +* Which chip and board you are using + +Embassy Chat: https://matrix.to/#/#embassy-rs:matrix.org diff --git a/examples/stm32l4/src/bin/tsc_async.rs b/examples/stm32l4/src/bin/tsc_async.rs new file mode 100644 index 000000000..ada2c468f --- /dev/null +++ b/examples/stm32l4/src/bin/tsc_async.rs @@ -0,0 +1,108 @@ +// Example of async TSC (Touch Sensing Controller) that lights an LED when touch is detected. +// +// This example demonstrates: +// 1. Configuring a single TSC channel pin +// 2. Using the async TSC interface +// 3. Waiting for acquisition completion using `pend_for_acquisition` +// 4. Reading touch values and controlling an LED based on the results +// +// Suggested physical setup on STM32L4R5ZI-P board: +// - Connect a 1000pF capacitor between pin PB4 (D25) and GND. This is your sampling capacitor. +// - Connect one end of a 1K resistor to pin PB5 (D21) and leave the other end loose. +// The loose end will act as the touch sensor which will register your touch. +// +// The example uses two pins from Group 2 of the TSC: +// - PB4 (D25) as the sampling capacitor, TSC group 2 IO1 +// - PB5 (D21) as the channel pin, TSC group 2 IO2 +// +// The program continuously reads the touch sensor value: +// - It starts acquisition, waits for completion using `pend_for_acquisition`, and reads the value. +// - The LED (connected to PB14) is turned on when touch is detected (sensor value < SENSOR_THRESHOLD). +// - Touch values are logged to the console. +// +// Troubleshooting: +// - If touch is not detected, try adjusting the SENSOR_THRESHOLD value. +// - Experiment with different values for ct_pulse_high_length, ct_pulse_low_length, +// pulse_generator_prescaler, max_count_value, and discharge_delay to optimize sensitivity. +// +// Note: Configuration values and sampling capacitor value have been determined experimentally. +// Optimal values may vary based on your specific hardware setup. + +#![no_std] +#![no_main] + +use defmt::*; +use embassy_stm32::gpio::{Level, Output, Speed}; +use embassy_stm32::tsc::{self, *}; +use embassy_stm32::{bind_interrupts, peripherals}; +use embassy_time::Timer; +use {defmt_rtt as _, panic_probe as _}; + +bind_interrupts!(struct Irqs { + TSC => InterruptHandler; +}); +const SENSOR_THRESHOLD: u16 = 25; // Adjust this value based on your setup + +#[embassy_executor::main] +async fn main(_spawner: embassy_executor::Spawner) { + let device_config = embassy_stm32::Config::default(); + let context = embassy_stm32::init(device_config); + + let mut pin_group: PinGroupWithRoles = PinGroupWithRoles::default(); + // D25 + pin_group.set_io1::(context.PB4); + // D21 + let tsc_sensor = pin_group.set_io2::(context.PB5); + + let pin_groups: PinGroups = PinGroups { + g2: Some(pin_group.pin_group), + ..Default::default() + }; + + let tsc_conf = Config { + ct_pulse_high_length: ChargeTransferPulseCycle::_4, + ct_pulse_low_length: ChargeTransferPulseCycle::_4, + spread_spectrum: false, + spread_spectrum_deviation: SSDeviation::new(2).unwrap(), + spread_spectrum_prescaler: false, + pulse_generator_prescaler: PGPrescalerDivider::_16, + max_count_value: MaxCount::_255, + io_default_mode: false, + synchro_pin_polarity: false, + acquisition_mode: false, + max_count_interrupt: false, + }; + + let mut touch_controller = tsc::Tsc::new_async(context.TSC, pin_groups, tsc_conf, Irqs).unwrap(); + + // Check if TSC is ready + if touch_controller.get_state() != State::Ready { + info!("TSC not ready!"); + return; + } + info!("TSC initialized successfully"); + + let mut led = Output::new(context.PB14, Level::High, Speed::Low); + + let discharge_delay = 1; // ms + + info!("Starting touch_controller interface"); + loop { + touch_controller.set_active_channels_mask(tsc_sensor.pin.into()); + touch_controller.start(); + touch_controller.pend_for_acquisition().await; + touch_controller.discharge_io(true); + Timer::after_millis(discharge_delay).await; + + let group_val = touch_controller.group_get_value(tsc_sensor.pin.group()); + info!("Touch value: {}", group_val); + + if group_val < SENSOR_THRESHOLD { + led.set_high(); + } else { + led.set_low(); + } + + Timer::after_millis(100).await; + } +} diff --git a/examples/stm32l4/src/bin/tsc_blocking.rs b/examples/stm32l4/src/bin/tsc_blocking.rs new file mode 100644 index 000000000..76aba55ba --- /dev/null +++ b/examples/stm32l4/src/bin/tsc_blocking.rs @@ -0,0 +1,147 @@ +// # Example of blocking TSC (Touch Sensing Controller) that lights an LED when touch is detected +// +// This example demonstrates how to use the Touch Sensing Controller (TSC) in blocking mode on an STM32L4R5ZI-P board. +// +// ## This example demonstrates: +// +// 1. Configuring a single TSC channel pin +// 2. Using the blocking TSC interface with polling +// 3. Waiting for acquisition completion using `poll_for_acquisition` +// 4. Reading touch values and controlling an LED based on the results +// +// ## Suggested physical setup on STM32L4R5ZI-P board: +// +// - Connect a 1000pF capacitor between pin PB4 (D25) and GND. This is your sampling capacitor. +// - Connect one end of a 1K resistor to pin PB5 (D21) and leave the other end loose. +// The loose end will act as the touch sensor which will register your touch. +// +// ## Pin Configuration: +// +// The example uses two pins from Group 2 of the TSC: +// - PB4 (D25) as the sampling capacitor, TSC group 2 IO1 +// - PB5 (D21) as the channel pin, TSC group 2 IO2 +// +// ## Program Behavior: +// +// The program continuously reads the touch sensor value: +// - It starts acquisition, waits for completion using `poll_for_acquisition`, and reads the value. +// - The LED (connected to PB14) is turned on when touch is detected (sensor value < SENSOR_THRESHOLD). +// - Touch values are logged to the console. +// +// ## Troubleshooting: +// +// - If touch is not detected, try adjusting the SENSOR_THRESHOLD value (currently set to 25). +// - Experiment with different values for ct_pulse_high_length, ct_pulse_low_length, +// pulse_generator_prescaler, max_count_value, and discharge_delay to optimize sensitivity. +// - Be aware that for some boards, there might be overlapping concerns between some pins, +// such as UART connections for the programmer. No errors or warnings will be emitted if you +// try to use such a pin for TSC, but you may get strange sensor readings. +// +// Note: Configuration values and sampling capacitor value have been determined experimentally. +// Optimal values may vary based on your specific hardware setup. Refer to the official +// STM32L4R5ZI-P datasheet and user manuals for more information on pin configurations and TSC functionality. + +#![no_std] +#![no_main] + +use defmt::*; +use embassy_stm32::gpio::{Level, Output, Speed}; +use embassy_stm32::tsc::{self, *}; +use embassy_stm32::{mode, peripherals}; +use embassy_time::Timer; +use {defmt_rtt as _, panic_probe as _}; + +const SENSOR_THRESHOLD: u16 = 25; // Adjust this value based on your setup + +#[embassy_executor::main] +async fn main(_spawner: embassy_executor::Spawner) { + let device_config = embassy_stm32::Config::default(); + let context = embassy_stm32::init(device_config); + + let tsc_conf = Config { + ct_pulse_high_length: ChargeTransferPulseCycle::_4, + ct_pulse_low_length: ChargeTransferPulseCycle::_4, + spread_spectrum: false, + spread_spectrum_deviation: SSDeviation::new(2).unwrap(), + spread_spectrum_prescaler: false, + pulse_generator_prescaler: PGPrescalerDivider::_16, + max_count_value: MaxCount::_255, + io_default_mode: false, + synchro_pin_polarity: false, + acquisition_mode: false, + max_count_interrupt: false, + }; + + let mut g2: PinGroupWithRoles = PinGroupWithRoles::default(); + // D25 + g2.set_io1::(context.PB4); + // D21 + let tsc_sensor = g2.set_io2::(context.PB5); + + let pin_groups: PinGroups = PinGroups { + g2: Some(g2.pin_group), + ..Default::default() + }; + + let mut touch_controller = tsc::Tsc::new_blocking(context.TSC, pin_groups, tsc_conf).unwrap(); + + // Check if TSC is ready + if touch_controller.get_state() != State::Ready { + crate::panic!("TSC not ready!"); + } + info!("TSC initialized successfully"); + + let mut led = Output::new(context.PB14, Level::High, Speed::Low); + + // smaller sample capacitor discharge faster and can be used with shorter delay. + let discharge_delay = 5; // ms + + // the interval at which the loop polls for new touch sensor values + let polling_interval = 100; // ms + + info!("polling for touch"); + loop { + touch_controller.set_active_channels_mask(tsc_sensor.pin.into()); + touch_controller.start(); + touch_controller.poll_for_acquisition(); + touch_controller.discharge_io(true); + Timer::after_millis(discharge_delay).await; + + match read_touch_value(&mut touch_controller, tsc_sensor.pin).await { + Some(v) => { + info!("sensor value {}", v); + if v < SENSOR_THRESHOLD { + led.set_high(); + } else { + led.set_low(); + } + } + None => led.set_low(), + } + + Timer::after_millis(polling_interval).await; + } +} + +const MAX_GROUP_STATUS_READ_ATTEMPTS: usize = 10; + +// attempt to read group status and delay when still ongoing +async fn read_touch_value( + touch_controller: &mut tsc::Tsc<'_, peripherals::TSC, mode::Blocking>, + sensor_pin: TscIOPin, +) -> Option { + for _ in 0..MAX_GROUP_STATUS_READ_ATTEMPTS { + match touch_controller.group_get_status(sensor_pin.group()) { + GroupStatus::Complete => { + return Some(touch_controller.group_get_value(sensor_pin.group())); + } + GroupStatus::Ongoing => { + // if you end up here a lot, then you prob need to increase discharge_delay + // or consider changing the code to adjust the discharge_delay dynamically + info!("Acquisition still ongoing"); + Timer::after_millis(1).await; + } + } + } + None +} diff --git a/examples/stm32l4/src/bin/tsc_multipin.rs b/examples/stm32l4/src/bin/tsc_multipin.rs new file mode 100644 index 000000000..20a559514 --- /dev/null +++ b/examples/stm32l4/src/bin/tsc_multipin.rs @@ -0,0 +1,222 @@ +// # Example of TSC (Touch Sensing Controller) using multiple pins from the same TSC group +// +// This example demonstrates how to use the Touch Sensing Controller (TSC) with multiple pins, including pins from the same TSC group, on an STM32L4R5ZI-P board. +// +// ## Key Concepts +// +// - Only one TSC pin for each TSC group can be acquired and read at a time. +// - To control which channel pins are acquired and read, we must write a mask before initiating an acquisition. +// - We organize channel pins into acquisition banks to manage this process efficiently. +// - Each acquisition bank can contain exactly one channel pin per TSC group and will contain the relevant mask. +// +// ## This example demonstrates how to: +// +// 1. Configure multiple channel pins within a single TSC group +// 2. Use the set_active_channels method to switch between different channels +// 3. Read and interpret touch values from multiple channels in the same group +// +// ## Suggested physical setup on STM32L4R5ZI-P board: +// +// - Connect a 1000pF capacitor between pin PB12 (D19) and GND. This is the sampling capacitor for TSC group 1. +// - Connect one end of a 1K resistor to pin PB13 (D18) and leave the other end loose. This will act as a touch sensor. +// - Connect a 1000pF capacitor between pin PB4 (D25) and GND. This is the sampling capacitor for TSC group 2. +// - Connect one end of a 1K resistor to pin PB5 (D22) and leave the other end loose. This will act as a touch sensor. +// - Connect one end of another 1K resistor to pin PB6 (D71) and leave the other end loose. This will act as a touch sensor. +// +// ## Pin Configuration: +// +// The example uses pins from two TSC groups: +// +// - Group 1: +// - PB12 (D19) as sampling capacitor (TSC group 1 IO1) +// - PB13 (D18) as channel (TSC group 1 IO2) +// - Group 2: +// - PB4 (D25) as sampling capacitor (TSC group 2 IO1) +// - PB5 (D22) as channel (TSC group 2 IO2) +// - PB6 (D71) as channel (TSC group 2 IO3) +// +// The pins have been chosen for their convenient locations on the STM32L4R5ZI-P board, making it easy to add capacitors and resistors directly to the board without special connectors, breadboards, or soldering. +// +// ## Program Behavior: +// +// The program reads the designated channel pins and adjusts the LED (connected to PB14) blinking pattern based on which sensor(s) are touched: +// +// - No touch: LED off +// - One sensor touched: Slow blinking +// - Two sensors touched: Fast blinking +// - Three sensors touched: LED constantly on +// +// ## Troubleshooting: +// +// - If touch is not detected, try adjusting the SENSOR_THRESHOLD value (currently set to 20). +// - Experiment with different values for ct_pulse_high_length, ct_pulse_low_length, pulse_generator_prescaler, max_count_value, and discharge_delay to optimize sensitivity. +// - Be aware that for some boards there will be overlapping concerns between some pins, for +// example UART connection for the programmer to the MCU and a TSC pin. No errors or warning will +// be emitted if you try to use such a pin for TSC, but you will get strange sensor readings. +// +// Note: Configuration values and sampling capacitor values have been determined experimentally. Optimal values may vary based on your specific hardware setup. Refer to the official STM32L4R5ZI-P datasheet and user manuals for more information on pin configurations and TSC functionality. + +#![no_std] +#![no_main] + +use defmt::*; +use embassy_stm32::gpio::{Level, Output, Speed}; +use embassy_stm32::tsc::{self, *}; +use embassy_stm32::{bind_interrupts, mode, peripherals}; +use embassy_time::Timer; +use {defmt_rtt as _, panic_probe as _}; + +bind_interrupts!(struct Irqs { + TSC => InterruptHandler; +}); + +const MAX_GROUP_STATUS_READ_ATTEMPTS: usize = 10; + +async fn read_touch_values( + touch_controller: &mut tsc::Tsc<'_, peripherals::TSC, mode::Async>, + tsc_acquisition_bank: &TscAcquisitionBank, +) -> Option { + for _ in 0..MAX_GROUP_STATUS_READ_ATTEMPTS { + let status = touch_controller.get_acquisition_bank_status(tsc_acquisition_bank); + if status.all_complete() { + let r = touch_controller.get_acquisition_bank_values(tsc_acquisition_bank); + return Some(r); + } else { + info!("Acquisition still ongoing"); + Timer::after_millis(1).await; + } + } + info!("Acquisition failed after {} attempts", MAX_GROUP_STATUS_READ_ATTEMPTS); + None +} + +const SENSOR_THRESHOLD: u16 = 20; + +async fn acquire_sensors( + touch_controller: &mut Tsc<'static, peripherals::TSC, mode::Async>, + tsc_acquisition_bank: &TscAcquisitionBank, +) { + touch_controller.set_active_channels_mask(tsc_acquisition_bank.mask()); + touch_controller.start(); + touch_controller.pend_for_acquisition().await; + touch_controller.discharge_io(true); + let discharge_delay = 1; // ms + Timer::after_millis(discharge_delay).await; +} + +#[embassy_executor::main] +async fn main(_spawner: embassy_executor::Spawner) { + let device_config = embassy_stm32::Config::default(); + let context = embassy_stm32::init(device_config); + + // ---------- initial configuration of TSC ---------- + let mut g1: PinGroupWithRoles = PinGroupWithRoles::default(); + g1.set_io1::(context.PB12); + let sensor0 = g1.set_io2::(context.PB13); + + let mut g2: PinGroupWithRoles = PinGroupWithRoles::default(); + g2.set_io1::(context.PB4); + let sensor1 = g2.set_io2(context.PB5); + let sensor2 = g2.set_io3(context.PB6); + + let config = tsc::Config { + ct_pulse_high_length: ChargeTransferPulseCycle::_16, + ct_pulse_low_length: ChargeTransferPulseCycle::_16, + spread_spectrum: false, + spread_spectrum_deviation: SSDeviation::new(2).unwrap(), + spread_spectrum_prescaler: false, + pulse_generator_prescaler: PGPrescalerDivider::_16, + max_count_value: MaxCount::_255, + io_default_mode: false, + synchro_pin_polarity: false, + acquisition_mode: false, + max_count_interrupt: false, + }; + + let pin_groups: PinGroups = PinGroups { + g1: Some(g1.pin_group), + g2: Some(g2.pin_group), + ..Default::default() + }; + + let mut touch_controller = tsc::Tsc::new_async(context.TSC, pin_groups, config, Irqs).unwrap(); + + // ---------- setting up acquisition banks ---------- + // sensor0 and sensor1 belong to different TSC-groups, therefore we can acquire and + // read them both in one go. + let bank1 = touch_controller.create_acquisition_bank(TscAcquisitionBankPins { + g1_pin: Some(sensor0), + g2_pin: Some(sensor1), + ..Default::default() + }); + // `sensor1` and `sensor2` belongs to the same TSC-group, therefore we must make sure to + // acquire them one at the time. We do this by organizing them into different acquisition banks. + let bank2 = touch_controller.create_acquisition_bank(TscAcquisitionBankPins { + g2_pin: Some(sensor2), + ..Default::default() + }); + + // Check if TSC is ready + if touch_controller.get_state() != State::Ready { + crate::panic!("TSC not ready!"); + } + + info!("TSC initialized successfully"); + + let mut led = Output::new(context.PB14, Level::High, Speed::Low); + + let mut led_state = false; + + loop { + acquire_sensors(&mut touch_controller, &bank1).await; + let readings1: TscAcquisitionBankReadings = read_touch_values(&mut touch_controller, &bank1) + .await + .expect("should be able to read values for bank 1"); + acquire_sensors(&mut touch_controller, &bank2).await; + let readings2: TscAcquisitionBankReadings = read_touch_values(&mut touch_controller, &bank2) + .await + .expect("should be able to read values for bank 2"); + + let mut touched_sensors_count = 0; + for reading in readings1.iter().chain(readings2.iter()) { + info!("{}", reading); + if reading.sensor_value < SENSOR_THRESHOLD { + touched_sensors_count += 1; + } + } + + match touched_sensors_count { + 0 => { + // No sensors touched, turn off the LED + led.set_low(); + led_state = false; + } + 1 => { + // One sensor touched, blink slowly + led_state = !led_state; + if led_state { + led.set_high(); + } else { + led.set_low(); + } + Timer::after_millis(200).await; + } + 2 => { + // Two sensors touched, blink faster + led_state = !led_state; + if led_state { + led.set_high(); + } else { + led.set_low(); + } + Timer::after_millis(50).await; + } + 3 => { + // All three sensors touched, LED constantly on + led.set_high(); + led_state = true; + } + _ => crate::unreachable!(), // This case should never occur with 3 sensors + } + } +} diff --git a/examples/stm32u5/src/bin/tsc.rs b/examples/stm32u5/src/bin/tsc.rs index eb15d275a..800486665 100644 --- a/examples/stm32u5/src/bin/tsc.rs +++ b/examples/stm32u5/src/bin/tsc.rs @@ -2,8 +2,8 @@ #![no_main] use defmt::*; -use embassy_stm32::bind_interrupts; use embassy_stm32::tsc::{self, *}; +use embassy_stm32::{bind_interrupts, peripherals}; use embassy_time::Timer; use {defmt_rtt as _, panic_probe as _}; @@ -33,63 +33,52 @@ async fn main(_spawner: embassy_executor::Spawner) { synchro_pin_polarity: false, acquisition_mode: false, max_count_interrupt: false, - channel_ios: TscIOPin::Group2Io2 | TscIOPin::Group7Io3, - shield_ios: TscIOPin::Group1Io3.into(), - sampling_ios: TscIOPin::Group1Io2 | TscIOPin::Group2Io1 | TscIOPin::Group7Io2, }; - let mut g1: PinGroup = PinGroup::new(); - g1.set_io2(context.PB13, PinType::Sample); - g1.set_io3(context.PB14, PinType::Shield); + let mut g1: PinGroupWithRoles = PinGroupWithRoles::default(); + g1.set_io2::(context.PB13); + g1.set_io3::(context.PB14); - let mut g2: PinGroup = PinGroup::new(); - g2.set_io1(context.PB4, PinType::Sample); - g2.set_io2(context.PB5, PinType::Channel); + let mut g2: PinGroupWithRoles = PinGroupWithRoles::default(); + g2.set_io1::(context.PB4); + let sensor0 = g2.set_io2(context.PB5); - let mut g7: PinGroup = PinGroup::new(); - g7.set_io2(context.PE3, PinType::Sample); - g7.set_io3(context.PE4, PinType::Channel); + let mut g7: PinGroupWithRoles = PinGroupWithRoles::default(); + g7.set_io2::(context.PE3); + let sensor1 = g7.set_io3(context.PE4); - let mut touch_controller = tsc::Tsc::new_async( - context.TSC, - Some(g1), - Some(g2), - None, - None, - None, - None, - Some(g7), - None, - config, - Irqs, - ); + let pin_groups: PinGroups = PinGroups { + g1: Some(g1.pin_group), + g2: Some(g2.pin_group), + g7: Some(g7.pin_group), + ..Default::default() + }; - touch_controller.discharge_io(true); - Timer::after_millis(1).await; + let mut touch_controller = tsc::Tsc::new_async(context.TSC, pin_groups, config, Irqs).unwrap(); - touch_controller.start(); + let acquisition_bank = touch_controller.create_acquisition_bank(TscAcquisitionBankPins { + g2_pin: Some(sensor0), + g7_pin: Some(sensor1), + ..Default::default() + }); + + touch_controller.set_active_channels_bank(&acquisition_bank); - let mut group_two_val = 0; - let mut group_seven_val = 0; info!("Starting touch_controller interface"); loop { + touch_controller.start(); touch_controller.pend_for_acquisition().await; touch_controller.discharge_io(true); Timer::after_millis(1).await; - if touch_controller.group_get_status(Group::Two) == GroupStatus::Complete { - group_two_val = touch_controller.group_get_value(Group::Two); + let status = touch_controller.get_acquisition_bank_status(&acquisition_bank); + + if status.all_complete() { + let read_values = touch_controller.get_acquisition_bank_values(&acquisition_bank); + let group2_reading = read_values.get_group_reading(Group::Two).unwrap(); + let group7_reading = read_values.get_group_reading(Group::Seven).unwrap(); + info!("group 2 value: {}", group2_reading.sensor_value); + info!("group 7 value: {}", group7_reading.sensor_value); } - - if touch_controller.group_get_status(Group::Seven) == GroupStatus::Complete { - group_seven_val = touch_controller.group_get_value(Group::Seven); - } - - info!( - "Group Two value: {}, Group Seven value: {},", - group_two_val, group_seven_val - ); - - touch_controller.start(); } } From a5b34a7980edaba5d8de05c68a48972dd9239f75 Mon Sep 17 00:00:00 2001 From: michel Date: Thu, 10 Oct 2024 14:53:04 +0200 Subject: [PATCH 260/361] stm32 multipin examples: remove check for group status since we are using the async pending method --- examples/stm32l0/src/bin/tsc_multipin.rs | 28 ++---------------------- examples/stm32l4/src/bin/tsc_multipin.rs | 28 ++---------------------- 2 files changed, 4 insertions(+), 52 deletions(-) diff --git a/examples/stm32l0/src/bin/tsc_multipin.rs b/examples/stm32l0/src/bin/tsc_multipin.rs index 6170d0799..85feb50b0 100644 --- a/examples/stm32l0/src/bin/tsc_multipin.rs +++ b/examples/stm32l0/src/bin/tsc_multipin.rs @@ -74,26 +74,6 @@ bind_interrupts!(struct Irqs { TSC => InterruptHandler; }); -const MAX_GROUP_STATUS_READ_ATTEMPTS: usize = 10; - -async fn read_touch_values( - touch_controller: &mut tsc::Tsc<'_, peripherals::TSC, mode::Async>, - tsc_acquisition_bank: &TscAcquisitionBank, -) -> Option { - for _ in 0..MAX_GROUP_STATUS_READ_ATTEMPTS { - let status = touch_controller.get_acquisition_bank_status(tsc_acquisition_bank); - if status.all_complete() { - let r = touch_controller.get_acquisition_bank_values(tsc_acquisition_bank); - return Some(r); - } else { - info!("Acquisition still ongoing"); - Timer::after_millis(1).await; - } - } - info!("Acquisition failed after {} attempts", MAX_GROUP_STATUS_READ_ATTEMPTS); - None -} - const SENSOR_THRESHOLD: u16 = 35; async fn acquire_sensors( @@ -174,13 +154,9 @@ async fn main(_spawner: embassy_executor::Spawner) { loop { acquire_sensors(&mut touch_controller, &bank1).await; - let readings1: TscAcquisitionBankReadings = read_touch_values(&mut touch_controller, &bank1) - .await - .expect("should be able to read values for bank 1"); + let readings1 = touch_controller.get_acquisition_bank_values(&bank1); acquire_sensors(&mut touch_controller, &bank2).await; - let readings2: TscAcquisitionBankReadings = read_touch_values(&mut touch_controller, &bank2) - .await - .expect("should be able to read values for bank 2"); + let readings2 = touch_controller.get_acquisition_bank_values(&bank1); let mut touched_sensors_count = 0; for reading in readings1.iter() { diff --git a/examples/stm32l4/src/bin/tsc_multipin.rs b/examples/stm32l4/src/bin/tsc_multipin.rs index 20a559514..f26a6f4eb 100644 --- a/examples/stm32l4/src/bin/tsc_multipin.rs +++ b/examples/stm32l4/src/bin/tsc_multipin.rs @@ -70,26 +70,6 @@ bind_interrupts!(struct Irqs { TSC => InterruptHandler; }); -const MAX_GROUP_STATUS_READ_ATTEMPTS: usize = 10; - -async fn read_touch_values( - touch_controller: &mut tsc::Tsc<'_, peripherals::TSC, mode::Async>, - tsc_acquisition_bank: &TscAcquisitionBank, -) -> Option { - for _ in 0..MAX_GROUP_STATUS_READ_ATTEMPTS { - let status = touch_controller.get_acquisition_bank_status(tsc_acquisition_bank); - if status.all_complete() { - let r = touch_controller.get_acquisition_bank_values(tsc_acquisition_bank); - return Some(r); - } else { - info!("Acquisition still ongoing"); - Timer::after_millis(1).await; - } - } - info!("Acquisition failed after {} attempts", MAX_GROUP_STATUS_READ_ATTEMPTS); - None -} - const SENSOR_THRESHOLD: u16 = 20; async fn acquire_sensors( @@ -169,13 +149,9 @@ async fn main(_spawner: embassy_executor::Spawner) { loop { acquire_sensors(&mut touch_controller, &bank1).await; - let readings1: TscAcquisitionBankReadings = read_touch_values(&mut touch_controller, &bank1) - .await - .expect("should be able to read values for bank 1"); + let readings1 = touch_controller.get_acquisition_bank_values(&bank1); acquire_sensors(&mut touch_controller, &bank2).await; - let readings2: TscAcquisitionBankReadings = read_touch_values(&mut touch_controller, &bank2) - .await - .expect("should be able to read values for bank 2"); + let readings2 = touch_controller.get_acquisition_bank_values(&bank2); let mut touched_sensors_count = 0; for reading in readings1.iter().chain(readings2.iter()) { From 31da5155e840d97e432cf2fa06c6fa4c2a19bf9a Mon Sep 17 00:00:00 2001 From: michel Date: Thu, 10 Oct 2024 15:14:53 +0200 Subject: [PATCH 261/361] Refactor TSC module: Remove redundant 'Tsc' prefixes for improved naming consistency --- embassy-stm32/src/tsc/acquisition_banks.rs | 108 +++++------ embassy-stm32/src/tsc/io_pin.rs | 200 +++++++++++++++++++++ embassy-stm32/src/tsc/mod.rs | 4 +- embassy-stm32/src/tsc/pin_groups.rs | 136 +++++++------- embassy-stm32/src/tsc/tsc.rs | 58 +++--- embassy-stm32/src/tsc/tsc_io_pin.rs | 200 --------------------- examples/stm32f3/src/bin/tsc_blocking.rs | 6 +- examples/stm32l0/src/bin/tsc_async.rs | 4 +- examples/stm32l0/src/bin/tsc_blocking.rs | 6 +- examples/stm32l0/src/bin/tsc_multipin.rs | 10 +- examples/stm32l4/src/bin/tsc_async.rs | 4 +- examples/stm32l4/src/bin/tsc_blocking.rs | 6 +- examples/stm32l4/src/bin/tsc_multipin.rs | 12 +- examples/stm32u5/src/bin/tsc.rs | 10 +- 14 files changed, 379 insertions(+), 385 deletions(-) create mode 100644 embassy-stm32/src/tsc/io_pin.rs delete mode 100644 embassy-stm32/src/tsc/tsc_io_pin.rs diff --git a/embassy-stm32/src/tsc/acquisition_banks.rs b/embassy-stm32/src/tsc/acquisition_banks.rs index 21a5c3f87..6791ef6c1 100644 --- a/embassy-stm32/src/tsc/acquisition_banks.rs +++ b/embassy-stm32/src/tsc/acquisition_banks.rs @@ -1,69 +1,69 @@ +use super::io_pin::*; #[cfg(any(tsc_v2, tsc_v3))] use super::pin_groups::G7; #[cfg(tsc_v3)] use super::pin_groups::G8; -use super::pin_groups::{tsc_pin_roles, G1, G2, G3, G4, G5, G6}; -use super::tsc_io_pin::*; +use super::pin_groups::{pin_roles, G1, G2, G3, G4, G5, G6}; use super::types::{Group, GroupStatus}; use super::TSC_NUM_GROUPS; /// Represents a collection of TSC (Touch Sensing Controller) pins for an acquisition bank. /// -/// This struct holds optional `TscIOPin` values for each TSC group, allowing for flexible +/// This struct holds optional `tsc::IOPin` values for each TSC group, allowing for flexible /// configuration of TSC acquisition banks. Each field corresponds to a specific TSC group -/// and can be set to `Some(TscIOPin)` if that group is to be included in the acquisition, +/// and can be set to `Some(tsc::IOPin)` if that group is to be included in the acquisition, /// or `None` if it should be excluded. #[allow(missing_docs)] #[derive(Default)] -pub struct TscAcquisitionBankPins { - pub g1_pin: Option>, - pub g2_pin: Option>, - pub g3_pin: Option>, - pub g4_pin: Option>, - pub g5_pin: Option>, - pub g6_pin: Option>, +pub struct AcquisitionBankPins { + pub g1_pin: Option>, + pub g2_pin: Option>, + pub g3_pin: Option>, + pub g4_pin: Option>, + pub g5_pin: Option>, + pub g6_pin: Option>, #[cfg(any(tsc_v2, tsc_v3))] - pub g7_pin: Option>, + pub g7_pin: Option>, #[cfg(tsc_v3)] - pub g8_pin: Option>, + pub g8_pin: Option>, } -impl TscAcquisitionBankPins { +impl AcquisitionBankPins { /// Returns an iterator over the pins in this acquisition bank. /// /// This method allows for easy traversal of all configured pins in the bank. - pub fn iter(&self) -> TscAcquisitionBankPinsIterator { - TscAcquisitionBankPinsIterator(TscAcquisitionBankIterator::new(self)) + pub fn iter(&self) -> AcquisitionBankPinsIterator { + AcquisitionBankPinsIterator(AcquisitionBankIterator::new(self)) } } /// Iterator for TSC acquisition banks. /// -/// This iterator allows traversing through the pins of a `TscAcquisitionBankPins` struct, +/// This iterator allows traversing through the pins of a `AcquisitionBankPins` struct, /// yielding each configured pin in order of the TSC groups. -pub struct TscAcquisitionBankIterator<'a> { - pins: &'a TscAcquisitionBankPins, +pub struct AcquisitionBankIterator<'a> { + pins: &'a AcquisitionBankPins, current_group: u8, } -impl<'a> TscAcquisitionBankIterator<'a> { - fn new(pins: &'a TscAcquisitionBankPins) -> Self { +impl<'a> AcquisitionBankIterator<'a> { + fn new(pins: &'a AcquisitionBankPins) -> Self { Self { pins, current_group: 0 } } - fn next_pin(&mut self) -> Option { + fn next_pin(&mut self) -> Option { while self.current_group < TSC_NUM_GROUPS as u8 { let pin = match self.current_group { - 0 => self.pins.g1_pin.map(TscIOPinWithRole::get_pin), - 1 => self.pins.g2_pin.map(TscIOPinWithRole::get_pin), - 2 => self.pins.g3_pin.map(TscIOPinWithRole::get_pin), - 3 => self.pins.g4_pin.map(TscIOPinWithRole::get_pin), - 4 => self.pins.g5_pin.map(TscIOPinWithRole::get_pin), - 5 => self.pins.g6_pin.map(TscIOPinWithRole::get_pin), + 0 => self.pins.g1_pin.map(IOPinWithRole::get_pin), + 1 => self.pins.g2_pin.map(IOPinWithRole::get_pin), + 2 => self.pins.g3_pin.map(IOPinWithRole::get_pin), + 3 => self.pins.g4_pin.map(IOPinWithRole::get_pin), + 4 => self.pins.g5_pin.map(IOPinWithRole::get_pin), + 5 => self.pins.g6_pin.map(IOPinWithRole::get_pin), #[cfg(any(tsc_v2, tsc_v3))] - 6 => self.pins.g7_pin.map(TscIOPinWithRole::get_pin), + 6 => self.pins.g7_pin.map(IOPinWithRole::get_pin), #[cfg(tsc_v3)] - 7 => self.pins.g8_pin.map(TscIOPinWithRole::get_pin), + 7 => self.pins.g8_pin.map(IOPinWithRole::get_pin), _ => None, }; self.current_group += 1; @@ -77,21 +77,21 @@ impl<'a> TscAcquisitionBankIterator<'a> { /// Iterator for TSC acquisition bank pins. /// -/// This iterator yields `TscIOPin` values for each configured pin in the acquisition bank. -pub struct TscAcquisitionBankPinsIterator<'a>(TscAcquisitionBankIterator<'a>); +/// This iterator yields `tsc::IOPin` values for each configured pin in the acquisition bank. +pub struct AcquisitionBankPinsIterator<'a>(AcquisitionBankIterator<'a>); -impl<'a> Iterator for TscAcquisitionBankPinsIterator<'a> { - type Item = TscIOPin; +impl<'a> Iterator for AcquisitionBankPinsIterator<'a> { + type Item = IOPin; fn next(&mut self) -> Option { self.0.next_pin() } } -impl TscAcquisitionBankPins { +impl AcquisitionBankPins { /// Returns an iterator over the available pins in the bank - pub fn pins_iterator(&self) -> TscAcquisitionBankPinsIterator { - TscAcquisitionBankPinsIterator(TscAcquisitionBankIterator::new(self)) + pub fn pins_iterator(&self) -> AcquisitionBankPinsIterator { + AcquisitionBankPinsIterator(AcquisitionBankIterator::new(self)) } } @@ -100,14 +100,14 @@ impl TscAcquisitionBankPins { /// This struct contains a set of pins to be used in a TSC acquisition with a pre-computed and /// verified mask for efficiently setting up the TSC peripheral before performing an acquisition. /// It ensures that only one channel pin per TSC group is included, adhering to hardware limitations. -pub struct TscAcquisitionBank { - pub(super) pins: TscAcquisitionBankPins, +pub struct AcquisitionBank { + pub(super) pins: AcquisitionBankPins, pub(super) mask: u32, } -impl TscAcquisitionBank { +impl AcquisitionBank { /// Returns an iterator over the available pins in the bank. - pub fn pins_iterator(&self) -> TscAcquisitionBankPinsIterator { + pub fn pins_iterator(&self) -> AcquisitionBankPinsIterator { self.pins.pins_iterator() } @@ -122,8 +122,8 @@ impl TscAcquisitionBank { /// * `group` - The TSC group to retrieve the pin for. /// /// # Returns - /// An `Option` containing the pin if it exists for the given group, or `None` if not. - pub fn get_pin(&self, group: Group) -> Option { + /// An `Option` containing the pin if it exists for the given group, or `None` if not. + pub fn get_pin(&self, group: Group) -> Option { match group { Group::One => self.pins.g1_pin.map(|p| p.pin), Group::Two => self.pins.g2_pin.map(|p| p.pin), @@ -141,11 +141,11 @@ impl TscAcquisitionBank { /// Represents the status of all TSC groups in an acquisition bank #[derive(Default)] -pub struct TscAcquisitionBankStatus { +pub struct AcquisitionBankStatus { pub(super) groups: [Option; TSC_NUM_GROUPS], } -impl TscAcquisitionBankStatus { +impl AcquisitionBankStatus { /// Check if all groups in the bank are complete pub fn all_complete(&self) -> bool { self.groups @@ -174,36 +174,36 @@ impl TscAcquisitionBankStatus { /// Represents the result of a Touch Sensing Controller (TSC) acquisition for a specific pin. /// -/// This struct contains a reference to the `TscIOPin` from which a value was read, +/// This struct contains a reference to the `tsc::IOPin` from which a value was read, /// along with the actual sensor reading for that pin. It provides a convenient way /// to associate TSC readings with their corresponding pins after an acquisition. #[cfg_attr(feature = "defmt", derive(defmt::Format))] #[derive(Clone, Copy, Debug)] -pub struct TscChannelReading { +pub struct ChannelReading { /// The sensor reading value obtained from the TSC acquisition. /// Lower values typically indicate a detected touch, while higher values indicate no touch. pub sensor_value: u16, - /// The `TscIOPin` associated with this reading. + /// The `tsc::IOPin` associated with this reading. /// This allows for easy identification of which pin the reading corresponds to. - pub tsc_pin: TscIOPin, + pub tsc_pin: IOPin, } /// Represents the readings from all TSC groups #[derive(Default)] -pub struct TscAcquisitionBankReadings { - pub(super) groups: [Option; TSC_NUM_GROUPS], +pub struct AcquisitionBankReadings { + pub(super) groups: [Option; TSC_NUM_GROUPS], } -impl TscAcquisitionBankReadings { +impl AcquisitionBankReadings { /// Get the reading for a specific group, if the group is present in the bank - pub fn get_group_reading(&self, group: Group) -> Option { + pub fn get_group_reading(&self, group: Group) -> Option { let index: usize = group.into(); self.groups[index] } /// Iterator for readings for groups present in the bank - pub fn iter(&self) -> impl Iterator + '_ { + pub fn iter(&self) -> impl Iterator + '_ { self.groups.iter().filter_map(|&x| x) } } diff --git a/embassy-stm32/src/tsc/io_pin.rs b/embassy-stm32/src/tsc/io_pin.rs new file mode 100644 index 000000000..ad2010ed7 --- /dev/null +++ b/embassy-stm32/src/tsc/io_pin.rs @@ -0,0 +1,200 @@ +use core::marker::PhantomData; +use core::ops::{BitAnd, BitOr, BitOrAssign}; + +use super::pin_roles; +use super::types::Group; + +/// Pin defines +#[allow(missing_docs)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +#[derive(PartialEq, Clone, Copy, Debug)] +pub enum IOPin { + Group1Io1, + Group1Io2, + Group1Io3, + Group1Io4, + Group2Io1, + Group2Io2, + Group2Io3, + Group2Io4, + Group3Io1, + Group3Io2, + Group3Io3, + Group3Io4, + Group4Io1, + Group4Io2, + Group4Io3, + Group4Io4, + Group5Io1, + Group5Io2, + Group5Io3, + Group5Io4, + Group6Io1, + Group6Io2, + Group6Io3, + Group6Io4, + #[cfg(any(tsc_v2, tsc_v3))] + Group7Io1, + #[cfg(any(tsc_v2, tsc_v3))] + Group7Io2, + #[cfg(any(tsc_v2, tsc_v3))] + Group7Io3, + #[cfg(any(tsc_v2, tsc_v3))] + Group7Io4, + #[cfg(tsc_v3)] + Group8Io1, + #[cfg(tsc_v3)] + Group8Io2, + #[cfg(tsc_v3)] + Group8Io3, + #[cfg(tsc_v3)] + Group8Io4, +} + +/// Represents a TSC I/O pin with associated group and role information. +/// +/// This type combines an `tsc::IOPin` with phantom type parameters to statically +/// encode the pin's group and role. This allows for type-safe operations +/// on TSC pins within their specific contexts. +/// +/// - `Group`: A type parameter representing the TSC group (e.g., `G1`, `G2`). +/// - `Role`: A type parameter representing the pin's role (e.g., `Channel`, `Sample`). +#[derive(Clone, Copy, Debug)] +pub struct IOPinWithRole { + /// The underlying TSC I/O pin. + pub pin: IOPin, + pub(super) phantom: PhantomData<(Group, Role)>, +} + +impl IOPinWithRole { + pub(super) fn get_pin(wrapped_pin: IOPinWithRole) -> IOPin { + wrapped_pin.pin + } +} + +impl IOPin { + /// Maps this IOPin to the Group it belongs to. + /// + /// This method provides a convenient way to determine which Group + /// a specific TSC I/O pin is associated with. + pub const fn group(&self) -> Group { + match self { + IOPin::Group1Io1 | IOPin::Group1Io2 | IOPin::Group1Io3 | IOPin::Group1Io4 => Group::One, + IOPin::Group2Io1 | IOPin::Group2Io2 | IOPin::Group2Io3 | IOPin::Group2Io4 => Group::Two, + IOPin::Group3Io1 | IOPin::Group3Io2 | IOPin::Group3Io3 | IOPin::Group3Io4 => Group::Three, + IOPin::Group4Io1 | IOPin::Group4Io2 | IOPin::Group4Io3 | IOPin::Group4Io4 => Group::Four, + IOPin::Group5Io1 | IOPin::Group5Io2 | IOPin::Group5Io3 | IOPin::Group5Io4 => Group::Five, + IOPin::Group6Io1 | IOPin::Group6Io2 | IOPin::Group6Io3 | IOPin::Group6Io4 => Group::Six, + #[cfg(any(tsc_v2, tsc_v3))] + IOPin::Group7Io1 | IOPin::Group7Io2 | IOPin::Group7Io3 | IOPin::Group7Io4 => Group::Seven, + #[cfg(tsc_v3)] + IOPin::Group8Io1 | IOPin::Group8Io2 | IOPin::Group8Io3 | IOPin::Group8Io4 => Group::Eight, + } + } + + /// Returns the `Group` associated with the given `IOPin`. + pub fn get_group(pin: IOPin) -> Group { + pin.group() + } +} + +impl BitOr for u32 { + type Output = u32; + fn bitor(self, rhs: IOPin) -> Self::Output { + let rhs: u32 = rhs.into(); + self | rhs + } +} + +impl BitOr for IOPin { + type Output = u32; + fn bitor(self, rhs: u32) -> Self::Output { + let val: u32 = self.into(); + val | rhs + } +} + +impl BitOr for IOPin { + type Output = u32; + fn bitor(self, rhs: Self) -> Self::Output { + let val: u32 = self.into(); + let rhs: u32 = rhs.into(); + val | rhs + } +} + +impl BitOrAssign for u32 { + fn bitor_assign(&mut self, rhs: IOPin) { + let rhs: u32 = rhs.into(); + *self |= rhs; + } +} + +impl BitAnd for u32 { + type Output = u32; + fn bitand(self, rhs: IOPin) -> Self::Output { + let rhs: u32 = rhs.into(); + self & rhs + } +} + +impl BitAnd for IOPin { + type Output = u32; + fn bitand(self, rhs: u32) -> Self::Output { + let val: u32 = self.into(); + val & rhs + } +} + +impl IOPin { + const fn to_u32(self) -> u32 { + match self { + IOPin::Group1Io1 => 0x00000001, + IOPin::Group1Io2 => 0x00000002, + IOPin::Group1Io3 => 0x00000004, + IOPin::Group1Io4 => 0x00000008, + IOPin::Group2Io1 => 0x00000010, + IOPin::Group2Io2 => 0x00000020, + IOPin::Group2Io3 => 0x00000040, + IOPin::Group2Io4 => 0x00000080, + IOPin::Group3Io1 => 0x00000100, + IOPin::Group3Io2 => 0x00000200, + IOPin::Group3Io3 => 0x00000400, + IOPin::Group3Io4 => 0x00000800, + IOPin::Group4Io1 => 0x00001000, + IOPin::Group4Io2 => 0x00002000, + IOPin::Group4Io3 => 0x00004000, + IOPin::Group4Io4 => 0x00008000, + IOPin::Group5Io1 => 0x00010000, + IOPin::Group5Io2 => 0x00020000, + IOPin::Group5Io3 => 0x00040000, + IOPin::Group5Io4 => 0x00080000, + IOPin::Group6Io1 => 0x00100000, + IOPin::Group6Io2 => 0x00200000, + IOPin::Group6Io3 => 0x00400000, + IOPin::Group6Io4 => 0x00800000, + #[cfg(any(tsc_v2, tsc_v3))] + IOPin::Group7Io1 => 0x01000000, + #[cfg(any(tsc_v2, tsc_v3))] + IOPin::Group7Io2 => 0x02000000, + #[cfg(any(tsc_v2, tsc_v3))] + IOPin::Group7Io3 => 0x04000000, + #[cfg(any(tsc_v2, tsc_v3))] + IOPin::Group7Io4 => 0x08000000, + #[cfg(tsc_v3)] + IOPin::Group8Io1 => 0x10000000, + #[cfg(tsc_v3)] + IOPin::Group8Io2 => 0x20000000, + #[cfg(tsc_v3)] + IOPin::Group8Io3 => 0x40000000, + #[cfg(tsc_v3)] + IOPin::Group8Io4 => 0x80000000, + } + } +} + +impl Into for IOPin { + fn into(self) -> u32 { + self.to_u32() + } +} diff --git a/embassy-stm32/src/tsc/mod.rs b/embassy-stm32/src/tsc/mod.rs index 2df3847bc..0d5c27465 100644 --- a/embassy-stm32/src/tsc/mod.rs +++ b/embassy-stm32/src/tsc/mod.rs @@ -80,7 +80,7 @@ pub mod config; pub mod pin_groups; /// Definitions and implementations for individual TSC I/O pins. -pub mod tsc_io_pin; +pub mod io_pin; /// Structures and implementations for TSC acquisition banks. pub mod acquisition_banks; @@ -100,9 +100,9 @@ pub use acquisition_banks::*; pub use config::*; use embassy_sync::waitqueue::AtomicWaker; pub use errors::*; +pub use io_pin::*; pub use pin_groups::*; pub use tsc::*; -pub use tsc_io_pin::*; pub use types::*; use crate::rcc::RccPeripheral; diff --git a/embassy-stm32/src/tsc/pin_groups.rs b/embassy-stm32/src/tsc/pin_groups.rs index b15890d6f..1f3aafa35 100644 --- a/embassy-stm32/src/tsc/pin_groups.rs +++ b/embassy-stm32/src/tsc/pin_groups.rs @@ -4,7 +4,7 @@ use core::ops::BitOr; use embassy_hal_internal::{into_ref, PeripheralRef}; use super::errors::GroupError; -use super::tsc_io_pin::*; +use super::io_pin::*; use super::Instance; use crate::gpio::{AfType, AnyPin, OutputType, Speed}; use crate::Peripheral; @@ -22,14 +22,14 @@ pub enum PinType { /// Pin struct that maintains usage #[allow(missing_docs)] -pub struct TscPin<'d, T, Group> { +pub struct Pin<'d, T, Group> { _pin: PeripheralRef<'d, AnyPin>, role: PinType, - tsc_io_pin: TscIOPin, + tsc_io_pin: IOPin, phantom: PhantomData<(T, Group)>, } -impl<'d, T, Group> TscPin<'d, T, Group> { +impl<'d, T, Group> Pin<'d, T, Group> { /// Returns the role of this TSC pin. /// /// The role indicates whether this pin is configured as a channel, @@ -47,8 +47,8 @@ impl<'d, T, Group> TscPin<'d, T, Group> { /// which includes information about the pin's group and position within that group. /// /// # Returns - /// The `TscIOPin` representing this pin's TSC-specific configuration. - pub fn tsc_io_pin(&self) -> TscIOPin { + /// The `IOPin` representing this pin's TSC-specific configuration. + pub fn tsc_io_pin(&self) -> IOPin { self.tsc_io_pin } } @@ -71,10 +71,10 @@ impl<'d, T, Group> TscPin<'d, T, Group> { /// - No more than one shield pin is allowed across all groups. #[allow(missing_docs)] pub struct PinGroup<'d, T, Group> { - pin1: Option>, - pin2: Option>, - pin3: Option>, - pin4: Option>, + pin1: Option>, + pin2: Option>, + pin3: Option>, + pin4: Option>, } impl<'d, T, G> Default for PinGroup<'d, T, G> { @@ -92,7 +92,7 @@ impl<'d, T, G> Default for PinGroup<'d, T, G> { /// /// This module contains marker types and traits that represent different roles /// a TSC pin can have, such as channel, sample, or shield. -pub mod tsc_pin_roles { +pub mod pin_roles { use super::{OutputType, PinType}; /// Marker type for a TSC channel pin. @@ -162,10 +162,10 @@ pub struct PinGroupWithRoles< 'd, T: Instance, G, - R1 = tsc_pin_roles::Channel, - R2 = tsc_pin_roles::Channel, - R3 = tsc_pin_roles::Channel, - R4 = tsc_pin_roles::Channel, + R1 = pin_roles::Channel, + R2 = pin_roles::Channel, + R3 = pin_roles::Channel, + R4 = pin_roles::Channel, > { /// The underlying pin group without role information. pub pin_group: PinGroup<'d, T, G>, @@ -230,38 +230,38 @@ impl<'d, T: Instance, G> PinGroup<'d, T, G> { } /// Returns a reference to the first pin in the group, if configured. - pub fn pin1(&self) -> Option<&TscPin<'d, T, G>> { + pub fn pin1(&self) -> Option<&Pin<'d, T, G>> { self.pin1.as_ref() } /// Returns a reference to the second pin in the group, if configured. - pub fn pin2(&self) -> Option<&TscPin<'d, T, G>> { + pub fn pin2(&self) -> Option<&Pin<'d, T, G>> { self.pin2.as_ref() } /// Returns a reference to the third pin in the group, if configured. - pub fn pin3(&self) -> Option<&TscPin<'d, T, G>> { + pub fn pin3(&self) -> Option<&Pin<'d, T, G>> { self.pin3.as_ref() } /// Returns a reference to the fourth pin in the group, if configured. - pub fn pin4(&self) -> Option<&TscPin<'d, T, G>> { + pub fn pin4(&self) -> Option<&Pin<'d, T, G>> { self.pin4.as_ref() } - fn sample_pins(&self) -> impl Iterator + '_ { + fn sample_pins(&self) -> impl Iterator + '_ { self.pins_filtered(PinType::Sample) } - fn shield_pins(&self) -> impl Iterator + '_ { + fn shield_pins(&self) -> impl Iterator + '_ { self.pins_filtered(PinType::Shield) } - fn channel_pins(&self) -> impl Iterator + '_ { + fn channel_pins(&self) -> impl Iterator + '_ { self.pins_filtered(PinType::Channel) } - fn pins_filtered(&self, pin_type: PinType) -> impl Iterator + '_ { + fn pins_filtered(&self, pin_type: PinType) -> impl Iterator + '_ { self.pins().into_iter().filter_map(move |pin| { pin.as_ref() .and_then(|p| if p.role == pin_type { Some(p.tsc_io_pin) } else { None }) @@ -280,11 +280,11 @@ impl<'d, T: Instance, G> PinGroup<'d, T, G> { self.sample_pins().fold(0, u32::bitor) } - fn pins(&self) -> [&Option>; 4] { + fn pins(&self) -> [&Option>; 4] { [&self.pin1, &self.pin2, &self.pin3, &self.pin4] } - fn pins_mut(&mut self) -> [&mut Option>; 4] { + fn pins_mut(&mut self) -> [&mut Option>; 4] { [&mut self.pin1, &mut self.pin2, &mut self.pin3, &mut self.pin4] } } @@ -317,132 +317,132 @@ macro_rules! TSC_V3_GUARD { }}; } -macro_rules! trait_to_tsc_io_pin { +macro_rules! trait_to_io_pin { (G1IO1Pin) => { - TscIOPin::Group1Io1 + IOPin::Group1Io1 }; (G1IO2Pin) => { - TscIOPin::Group1Io2 + IOPin::Group1Io2 }; (G1IO3Pin) => { - TscIOPin::Group1Io3 + IOPin::Group1Io3 }; (G1IO4Pin) => { - TscIOPin::Group1Io4 + IOPin::Group1Io4 }; (G2IO1Pin) => { - TscIOPin::Group2Io1 + IOPin::Group2Io1 }; (G2IO2Pin) => { - TscIOPin::Group2Io2 + IOPin::Group2Io2 }; (G2IO3Pin) => { - TscIOPin::Group2Io3 + IOPin::Group2Io3 }; (G2IO4Pin) => { - TscIOPin::Group2Io4 + IOPin::Group2Io4 }; (G3IO1Pin) => { - TscIOPin::Group3Io1 + IOPin::Group3Io1 }; (G3IO2Pin) => { - TscIOPin::Group3Io2 + IOPin::Group3Io2 }; (G3IO3Pin) => { - TscIOPin::Group3Io3 + IOPin::Group3Io3 }; (G3IO4Pin) => { - TscIOPin::Group3Io4 + IOPin::Group3Io4 }; (G4IO1Pin) => { - TscIOPin::Group4Io1 + IOPin::Group4Io1 }; (G4IO2Pin) => { - TscIOPin::Group4Io2 + IOPin::Group4Io2 }; (G4IO3Pin) => { - TscIOPin::Group4Io3 + IOPin::Group4Io3 }; (G4IO4Pin) => { - TscIOPin::Group4Io4 + IOPin::Group4Io4 }; (G5IO1Pin) => { - TscIOPin::Group5Io1 + IOPin::Group5Io1 }; (G5IO2Pin) => { - TscIOPin::Group5Io2 + IOPin::Group5Io2 }; (G5IO3Pin) => { - TscIOPin::Group5Io3 + IOPin::Group5Io3 }; (G5IO4Pin) => { - TscIOPin::Group5Io4 + IOPin::Group5Io4 }; (G6IO1Pin) => { - TscIOPin::Group6Io1 + IOPin::Group6Io1 }; (G6IO2Pin) => { - TscIOPin::Group6Io2 + IOPin::Group6Io2 }; (G6IO3Pin) => { - TscIOPin::Group6Io3 + IOPin::Group6Io3 }; (G6IO4Pin) => { - TscIOPin::Group6Io4 + IOPin::Group6Io4 }; (G7IO1Pin) => { - TSC_V2_V3_GUARD!(TscIOPin::Group7Io1) + TSC_V2_V3_GUARD!(IOPin::Group7Io1) }; (G7IO2Pin) => { - TSC_V2_V3_GUARD!(TscIOPin::Group7Io2) + TSC_V2_V3_GUARD!(IOPin::Group7Io2) }; (G7IO3Pin) => { - TSC_V2_V3_GUARD!(TscIOPin::Group7Io3) + TSC_V2_V3_GUARD!(IOPin::Group7Io3) }; (G7IO4Pin) => { - TSC_V2_V3_GUARD!(TscIOPin::Group7Io4) + TSC_V2_V3_GUARD!(IOPin::Group7Io4) }; (G8IO1Pin) => { - TSC_V3_GUARD!(TscIOPin::Group8Io1) + TSC_V3_GUARD!(IOPin::Group8Io1) }; (G8IO2Pin) => { - TSC_V3_GUARD!(TscIOPin::Group8Io2) + TSC_V3_GUARD!(IOPin::Group8Io2) }; (G8IO3Pin) => { - TSC_V3_GUARD!(TscIOPin::Group8Io3) + TSC_V3_GUARD!(IOPin::Group8Io3) }; (G8IO4Pin) => { - TSC_V3_GUARD!(TscIOPin::Group8Io4) + TSC_V3_GUARD!(IOPin::Group8Io4) }; } macro_rules! impl_set_io { ($method:ident, $group:ident, $trait:ident, $index:expr) => { #[doc = concat!("Create a new pin1 for ", stringify!($group), " TSC group instance.")] - pub fn $method( + pub fn $method( &mut self, pin: impl Peripheral

> + 'd, - ) -> TscIOPinWithRole<$group, Role> { + ) -> IOPinWithRole<$group, Role> { into_ref!(pin); critical_section::with(|_| { pin.set_low(); pin.set_as_af(pin.af_num(), AfType::output(Role::output_type(), Speed::VeryHigh)); - let tsc_io_pin = trait_to_tsc_io_pin!($trait); - let new_pin = TscPin { + let tsc_io_pin = trait_to_io_pin!($trait); + let new_pin = Pin { _pin: pin.map_into(), role: Role::pin_type(), tsc_io_pin, phantom: PhantomData, }; *self.pin_group.pins_mut()[$index] = Some(new_pin); - TscIOPinWithRole { + IOPinWithRole { pin: tsc_io_pin, phantom: PhantomData, } @@ -453,14 +453,8 @@ macro_rules! impl_set_io { macro_rules! group_impl { ($group:ident, $trait1:ident, $trait2:ident, $trait3:ident, $trait4:ident) => { - impl< - 'd, - T: Instance, - R1: tsc_pin_roles::Role, - R2: tsc_pin_roles::Role, - R3: tsc_pin_roles::Role, - R4: tsc_pin_roles::Role, - > PinGroupWithRoles<'d, T, $group, R1, R2, R3, R4> + impl<'d, T: Instance, R1: pin_roles::Role, R2: pin_roles::Role, R3: pin_roles::Role, R4: pin_roles::Role> + PinGroupWithRoles<'d, T, $group, R1, R2, R3, R4> { impl_set_io!(set_io1, $group, $trait1, 0); impl_set_io!(set_io2, $group, $trait2, 1); diff --git a/embassy-stm32/src/tsc/tsc.rs b/embassy-stm32/src/tsc/tsc.rs index 58f9d9d2e..17d2da82f 100644 --- a/embassy-stm32/src/tsc/tsc.rs +++ b/embassy-stm32/src/tsc/tsc.rs @@ -8,8 +8,8 @@ use embassy_hal_internal::{into_ref, PeripheralRef}; use super::acquisition_banks::*; use super::config::*; use super::errors::*; +use super::io_pin::*; use super::pin_groups::*; -use super::tsc_io_pin::*; use super::types::*; use super::{Instance, InterruptHandler, TSC_NUM_GROUPS}; use crate::interrupt::typelevel::Interrupt; @@ -20,7 +20,7 @@ use crate::{interrupt, rcc, Peripheral}; /// /// These masks are used during the initial configuration of the TSC peripheral /// and for validating pin types during operations like creating acquisition banks. -struct TscIOMasks { +struct IOMasks { /// Mask representing all configured channel IOs channel_ios: u32, /// Mask representing all configured shield IOs @@ -35,19 +35,19 @@ pub struct Tsc<'d, T: Instance, K: PeriMode> { _pin_groups: PinGroups<'d, T>, state: State, config: Config, - masks: TscIOMasks, + masks: IOMasks, _kind: PhantomData, } impl<'d, T: Instance, K: PeriMode> Tsc<'d, T, K> { // Helper method to check if a pin is a channel pin - fn is_channel_pin(&self, pin: TscIOPin) -> bool { + fn is_channel_pin(&self, pin: IOPin) -> bool { (self.masks.channel_ios & pin) != 0 } - /// Get the status of all groups involved in a TscAcquisitionBank - pub fn get_acquisition_bank_status(&self, bank: &TscAcquisitionBank) -> TscAcquisitionBankStatus { - let mut bank_status = TscAcquisitionBankStatus::default(); + /// Get the status of all groups involved in a AcquisitionBank + pub fn get_acquisition_bank_status(&self, bank: &AcquisitionBank) -> AcquisitionBankStatus { + let mut bank_status = AcquisitionBankStatus::default(); for pin in bank.pins_iterator() { let group = pin.group(); let group_status = self.group_get_status(group); @@ -57,13 +57,13 @@ impl<'d, T: Instance, K: PeriMode> Tsc<'d, T, K> { bank_status } - /// Get the values for all channels involved in a TscAcquisitionBank - pub fn get_acquisition_bank_values(&self, bank: &TscAcquisitionBank) -> TscAcquisitionBankReadings { - let mut bank_readings = TscAcquisitionBankReadings::default(); + /// Get the values for all channels involved in a AcquisitionBank + pub fn get_acquisition_bank_values(&self, bank: &AcquisitionBank) -> AcquisitionBankReadings { + let mut bank_readings = AcquisitionBankReadings::default(); for pin in bank.pins_iterator() { let group = pin.group(); let value = self.group_get_value(group); - let reading = TscChannelReading { + let reading = ChannelReading { sensor_value: value, tsc_pin: pin, }; @@ -75,7 +75,7 @@ impl<'d, T: Instance, K: PeriMode> Tsc<'d, T, K> { /// Creates a new TSC acquisition bank from the provided pin configuration. /// - /// This method creates a `TscAcquisitionBank` that can be used for efficient, + /// This method creates a `AcquisitionBank` that can be used for efficient, /// repeated TSC acquisitions. It automatically generates the appropriate mask /// for the provided pins. /// @@ -87,16 +87,16 @@ impl<'d, T: Instance, K: PeriMode> Tsc<'d, T, K> { /// * `acquisition_bank_pins` - The pin configuration for the acquisition bank. /// /// # Returns - /// A new `TscAcquisitionBank` instance. + /// A new `AcquisitionBank` instance. /// /// # Example /// /// ``` /// let tsc = // ... initialize TSC - /// let tsc_sensor1: TscIOPinWithRole = ...; - /// let tsc_sensor2: TscIOPinWithRole = ...; + /// let tsc_sensor1: tsc::IOPinWithRole = ...; + /// let tsc_sensor2: tsc::IOPinWithRole = ...; /// - /// let bank = tsc.create_acquisition_bank(TscAcquisitionBankPins { + /// let bank = tsc.create_acquisition_bank(AcquisitionBankPins { /// g1_pin: Some(tsc_sensor1), /// g2_pin: Some(tsc_sensor2), /// ..Default::default() @@ -107,10 +107,10 @@ impl<'d, T: Instance, K: PeriMode> Tsc<'d, T, K> { /// tsc.start(); /// // ... perform acquisition ... /// ``` - pub fn create_acquisition_bank(&self, acquisition_bank_pins: TscAcquisitionBankPins) -> TscAcquisitionBank { + pub fn create_acquisition_bank(&self, acquisition_bank_pins: AcquisitionBankPins) -> AcquisitionBank { let bank_mask = acquisition_bank_pins.iter().fold(0u32, BitOr::bitor); - TscAcquisitionBank { + AcquisitionBank { pins: acquisition_bank_pins, mask: bank_mask, } @@ -118,7 +118,7 @@ impl<'d, T: Instance, K: PeriMode> Tsc<'d, T, K> { fn make_channels_mask(&self, channels: Itt) -> Result where - Itt: IntoIterator, + Itt: IntoIterator, { let mut group_mask = 0u32; let mut channel_mask = 0u32; @@ -144,7 +144,7 @@ impl<'d, T: Instance, K: PeriMode> Tsc<'d, T, K> { /// Sets the active channels for the next TSC acquisition. /// /// This is a low-level method that directly sets the channel mask. For most use cases, - /// consider using `set_active_channels_bank` with a `TscAcquisitionBank` instead, which + /// consider using `set_active_channels_bank` with a `AcquisitionBank` instead, which /// provides a higher-level interface and additional safety checks. /// /// This method configures which sensor channels will be read during the next @@ -167,9 +167,9 @@ impl<'d, T: Instance, K: PeriMode> Tsc<'d, T, K> { T::regs().ioccr().write(|w| w.0 = mask | self.masks.shield_ios); } - /// Convenience method for setting active channels directly from a slice of TscIOPin. + /// Convenience method for setting active channels directly from a slice of tsc::IOPin. /// This method performs safety checks but is less efficient for repeated use. - pub fn set_active_channels(&mut self, channels: &[TscIOPin]) -> Result<(), AcquisitionBankError> { + pub fn set_active_channels(&mut self, channels: &[IOPin]) -> Result<(), AcquisitionBankError> { let mask = self.make_channels_mask(channels.iter().cloned())?; self.set_active_channels_mask(mask); Ok(()) @@ -178,21 +178,21 @@ impl<'d, T: Instance, K: PeriMode> Tsc<'d, T, K> { /// Sets the active channels for the next TSC acquisition using a pre-configured acquisition bank. /// /// This method efficiently configures the TSC peripheral to read the channels specified - /// in the provided `TscAcquisitionBank`. It's the recommended way to set up + /// in the provided `AcquisitionBank`. It's the recommended way to set up /// channel configurations for acquisition, especially when using the same set of channels repeatedly. /// /// # Arguments /// - /// * `bank` - A reference to a `TscAcquisitionBank` containing the pre-configured + /// * `bank` - A reference to a `AcquisitionBank` containing the pre-configured /// TSC channel mask. /// /// # Example /// /// ``` - /// let tsc_sensor1: TscIOPinWithRole = ...; - /// let tsc_sensor2: TscIOPinWithRole = ...; + /// let tsc_sensor1: tsc::IOPinWithRole = ...; + /// let tsc_sensor2: tsc::IOPinWithRole = ...; /// let mut touch_controller: Tsc<'_, TSC, Async> = ...; - /// let bank = touch_controller.create_acquisition_bank(TscAcquisitionBankPins { + /// let bank = touch_controller.create_acquisition_bank(AcquisitionBankPins { /// g1_pin: Some(tsc_sensor1), /// g2_pin: Some(tsc_sensor2), /// ..Default::default() @@ -204,7 +204,7 @@ impl<'d, T: Instance, K: PeriMode> Tsc<'d, T, K> { /// ``` /// /// This method should be called before starting a new acquisition with the `start()` method. - pub fn set_active_channels_bank(&mut self, bank: &TscAcquisitionBank) { + pub fn set_active_channels_bank(&mut self, bank: &AcquisitionBank) { self.set_active_channels_mask(bank.mask) } @@ -227,7 +227,7 @@ impl<'d, T: Instance, K: PeriMode> Tsc<'d, T, K> { pin_groups.check()?; - let masks = TscIOMasks { + let masks = IOMasks { channel_ios: pin_groups.make_channel_ios_mask(), shield_ios: pin_groups.make_shield_ios_mask(), sampling_ios: pin_groups.make_sample_ios_mask(), diff --git a/embassy-stm32/src/tsc/tsc_io_pin.rs b/embassy-stm32/src/tsc/tsc_io_pin.rs deleted file mode 100644 index 38b27d0b3..000000000 --- a/embassy-stm32/src/tsc/tsc_io_pin.rs +++ /dev/null @@ -1,200 +0,0 @@ -use core::marker::PhantomData; -use core::ops::{BitAnd, BitOr, BitOrAssign}; - -use super::tsc_pin_roles; -use super::types::Group; - -/// Pin defines -#[allow(missing_docs)] -#[cfg_attr(feature = "defmt", derive(defmt::Format))] -#[derive(PartialEq, Clone, Copy, Debug)] -pub enum TscIOPin { - Group1Io1, - Group1Io2, - Group1Io3, - Group1Io4, - Group2Io1, - Group2Io2, - Group2Io3, - Group2Io4, - Group3Io1, - Group3Io2, - Group3Io3, - Group3Io4, - Group4Io1, - Group4Io2, - Group4Io3, - Group4Io4, - Group5Io1, - Group5Io2, - Group5Io3, - Group5Io4, - Group6Io1, - Group6Io2, - Group6Io3, - Group6Io4, - #[cfg(any(tsc_v2, tsc_v3))] - Group7Io1, - #[cfg(any(tsc_v2, tsc_v3))] - Group7Io2, - #[cfg(any(tsc_v2, tsc_v3))] - Group7Io3, - #[cfg(any(tsc_v2, tsc_v3))] - Group7Io4, - #[cfg(tsc_v3)] - Group8Io1, - #[cfg(tsc_v3)] - Group8Io2, - #[cfg(tsc_v3)] - Group8Io3, - #[cfg(tsc_v3)] - Group8Io4, -} - -/// Represents a TSC I/O pin with associated group and role information. -/// -/// This type combines a `TscIOPin` with phantom type parameters to statically -/// encode the pin's group and role. This allows for type-safe operations -/// on TSC pins within their specific contexts. -/// -/// - `Group`: A type parameter representing the TSC group (e.g., `G1`, `G2`). -/// - `Role`: A type parameter representing the pin's role (e.g., `Channel`, `Sample`). -#[derive(Clone, Copy, Debug)] -pub struct TscIOPinWithRole { - /// The underlying TSC I/O pin. - pub pin: TscIOPin, - pub(super) phantom: PhantomData<(Group, Role)>, -} - -impl TscIOPinWithRole { - pub(super) fn get_pin(wrapped_pin: TscIOPinWithRole) -> TscIOPin { - wrapped_pin.pin - } -} - -impl TscIOPin { - /// Maps this TscIOPin to the Group it belongs to. - /// - /// This method provides a convenient way to determine which Group - /// a specific TSC I/O pin is associated with. - pub const fn group(&self) -> Group { - match self { - TscIOPin::Group1Io1 | TscIOPin::Group1Io2 | TscIOPin::Group1Io3 | TscIOPin::Group1Io4 => Group::One, - TscIOPin::Group2Io1 | TscIOPin::Group2Io2 | TscIOPin::Group2Io3 | TscIOPin::Group2Io4 => Group::Two, - TscIOPin::Group3Io1 | TscIOPin::Group3Io2 | TscIOPin::Group3Io3 | TscIOPin::Group3Io4 => Group::Three, - TscIOPin::Group4Io1 | TscIOPin::Group4Io2 | TscIOPin::Group4Io3 | TscIOPin::Group4Io4 => Group::Four, - TscIOPin::Group5Io1 | TscIOPin::Group5Io2 | TscIOPin::Group5Io3 | TscIOPin::Group5Io4 => Group::Five, - TscIOPin::Group6Io1 | TscIOPin::Group6Io2 | TscIOPin::Group6Io3 | TscIOPin::Group6Io4 => Group::Six, - #[cfg(any(tsc_v2, tsc_v3))] - TscIOPin::Group7Io1 | TscIOPin::Group7Io2 | TscIOPin::Group7Io3 | TscIOPin::Group7Io4 => Group::Seven, - #[cfg(tsc_v3)] - TscIOPin::Group8Io1 | TscIOPin::Group8Io2 | TscIOPin::Group8Io3 | TscIOPin::Group8Io4 => Group::Eight, - } - } - - /// Returns the `Group` associated with the given `TscIOPin`. - pub fn get_group(pin: TscIOPin) -> Group { - pin.group() - } -} - -impl BitOr for u32 { - type Output = u32; - fn bitor(self, rhs: TscIOPin) -> Self::Output { - let rhs: u32 = rhs.into(); - self | rhs - } -} - -impl BitOr for TscIOPin { - type Output = u32; - fn bitor(self, rhs: u32) -> Self::Output { - let val: u32 = self.into(); - val | rhs - } -} - -impl BitOr for TscIOPin { - type Output = u32; - fn bitor(self, rhs: Self) -> Self::Output { - let val: u32 = self.into(); - let rhs: u32 = rhs.into(); - val | rhs - } -} - -impl BitOrAssign for u32 { - fn bitor_assign(&mut self, rhs: TscIOPin) { - let rhs: u32 = rhs.into(); - *self |= rhs; - } -} - -impl BitAnd for u32 { - type Output = u32; - fn bitand(self, rhs: TscIOPin) -> Self::Output { - let rhs: u32 = rhs.into(); - self & rhs - } -} - -impl BitAnd for TscIOPin { - type Output = u32; - fn bitand(self, rhs: u32) -> Self::Output { - let val: u32 = self.into(); - val & rhs - } -} - -impl TscIOPin { - const fn to_u32(self) -> u32 { - match self { - TscIOPin::Group1Io1 => 0x00000001, - TscIOPin::Group1Io2 => 0x00000002, - TscIOPin::Group1Io3 => 0x00000004, - TscIOPin::Group1Io4 => 0x00000008, - TscIOPin::Group2Io1 => 0x00000010, - TscIOPin::Group2Io2 => 0x00000020, - TscIOPin::Group2Io3 => 0x00000040, - TscIOPin::Group2Io4 => 0x00000080, - TscIOPin::Group3Io1 => 0x00000100, - TscIOPin::Group3Io2 => 0x00000200, - TscIOPin::Group3Io3 => 0x00000400, - TscIOPin::Group3Io4 => 0x00000800, - TscIOPin::Group4Io1 => 0x00001000, - TscIOPin::Group4Io2 => 0x00002000, - TscIOPin::Group4Io3 => 0x00004000, - TscIOPin::Group4Io4 => 0x00008000, - TscIOPin::Group5Io1 => 0x00010000, - TscIOPin::Group5Io2 => 0x00020000, - TscIOPin::Group5Io3 => 0x00040000, - TscIOPin::Group5Io4 => 0x00080000, - TscIOPin::Group6Io1 => 0x00100000, - TscIOPin::Group6Io2 => 0x00200000, - TscIOPin::Group6Io3 => 0x00400000, - TscIOPin::Group6Io4 => 0x00800000, - #[cfg(any(tsc_v2, tsc_v3))] - TscIOPin::Group7Io1 => 0x01000000, - #[cfg(any(tsc_v2, tsc_v3))] - TscIOPin::Group7Io2 => 0x02000000, - #[cfg(any(tsc_v2, tsc_v3))] - TscIOPin::Group7Io3 => 0x04000000, - #[cfg(any(tsc_v2, tsc_v3))] - TscIOPin::Group7Io4 => 0x08000000, - #[cfg(tsc_v3)] - TscIOPin::Group8Io1 => 0x10000000, - #[cfg(tsc_v3)] - TscIOPin::Group8Io2 => 0x20000000, - #[cfg(tsc_v3)] - TscIOPin::Group8Io3 => 0x40000000, - #[cfg(tsc_v3)] - TscIOPin::Group8Io4 => 0x80000000, - } - } -} - -impl Into for TscIOPin { - fn into(self) -> u32 { - self.to_u32() - } -} diff --git a/examples/stm32f3/src/bin/tsc_blocking.rs b/examples/stm32f3/src/bin/tsc_blocking.rs index fa7f718e6..2c33838e5 100644 --- a/examples/stm32f3/src/bin/tsc_blocking.rs +++ b/examples/stm32f3/src/bin/tsc_blocking.rs @@ -64,9 +64,9 @@ async fn main(_spawner: embassy_executor::Spawner) { let mut g: PinGroupWithRoles = PinGroupWithRoles::default(); // D68 on the STM32F303ZE nucleo-board - g.set_io2::(context.PA10); + g.set_io2::(context.PA10); // D69 on the STM32F303ZE nucleo-board - let tsc_sensor = g.set_io1::(context.PA9); + let tsc_sensor = g.set_io1::(context.PA9); let pin_groups: PinGroups = PinGroups { g4: Some(g.pin_group), @@ -119,7 +119,7 @@ const MAX_GROUP_STATUS_READ_ATTEMPTS: usize = 10; // attempt to read group status and delay when still ongoing async fn read_touch_value( touch_controller: &mut tsc::Tsc<'_, peripherals::TSC, mode::Blocking>, - sensor_pin: TscIOPin, + sensor_pin: tsc::IOPin, ) -> Option { for _ in 0..MAX_GROUP_STATUS_READ_ATTEMPTS { match touch_controller.group_get_status(sensor_pin.group()) { diff --git a/examples/stm32l0/src/bin/tsc_async.rs b/examples/stm32l0/src/bin/tsc_async.rs index cebe9712f..dae351c2e 100644 --- a/examples/stm32l0/src/bin/tsc_async.rs +++ b/examples/stm32l0/src/bin/tsc_async.rs @@ -58,8 +58,8 @@ async fn main(_spawner: embassy_executor::Spawner) { let context = embassy_stm32::init(device_config); let mut pin_group: PinGroupWithRoles = PinGroupWithRoles::default(); - pin_group.set_io1::(context.PA0); - let sensor = pin_group.set_io2::(context.PA1); + pin_group.set_io1::(context.PA0); + let sensor = pin_group.set_io2::(context.PA1); let tsc_conf = Config { ct_pulse_high_length: ChargeTransferPulseCycle::_4, diff --git a/examples/stm32l0/src/bin/tsc_blocking.rs b/examples/stm32l0/src/bin/tsc_blocking.rs index 65203925c..e1f24639b 100644 --- a/examples/stm32l0/src/bin/tsc_blocking.rs +++ b/examples/stm32l0/src/bin/tsc_blocking.rs @@ -69,8 +69,8 @@ async fn main(_spawner: embassy_executor::Spawner) { }; let mut g1: PinGroupWithRoles = PinGroupWithRoles::default(); - g1.set_io1::(context.PA0); - let tsc_sensor = g1.set_io2::(context.PA1); + g1.set_io1::(context.PA0); + let tsc_sensor = g1.set_io2::(context.PA1); let pin_groups: PinGroups = PinGroups { g1: Some(g1.pin_group), @@ -123,7 +123,7 @@ const MAX_GROUP_STATUS_READ_ATTEMPTS: usize = 10; // attempt to read group status and delay when still ongoing async fn read_touch_value( touch_controller: &mut tsc::Tsc<'_, peripherals::TSC, mode::Blocking>, - sensor_pin: TscIOPin, + sensor_pin: tsc::IOPin, ) -> Option { for _ in 0..MAX_GROUP_STATUS_READ_ATTEMPTS { match touch_controller.group_get_status(sensor_pin.group()) { diff --git a/examples/stm32l0/src/bin/tsc_multipin.rs b/examples/stm32l0/src/bin/tsc_multipin.rs index 85feb50b0..bf75a5657 100644 --- a/examples/stm32l0/src/bin/tsc_multipin.rs +++ b/examples/stm32l0/src/bin/tsc_multipin.rs @@ -78,7 +78,7 @@ const SENSOR_THRESHOLD: u16 = 35; async fn acquire_sensors( touch_controller: &mut Tsc<'static, peripherals::TSC, mode::Async>, - tsc_acquisition_bank: &TscAcquisitionBank, + tsc_acquisition_bank: &AcquisitionBank, ) { touch_controller.set_active_channels_mask(tsc_acquisition_bank.mask()); touch_controller.start(); @@ -95,11 +95,11 @@ async fn main(_spawner: embassy_executor::Spawner) { // ---------- initial configuration of TSC ---------- let mut pin_group1: PinGroupWithRoles = PinGroupWithRoles::default(); - pin_group1.set_io1::(context.PA0); + pin_group1.set_io1::(context.PA0); let tsc_sensor0 = pin_group1.set_io2(context.PA1); let mut pin_group5: PinGroupWithRoles = PinGroupWithRoles::default(); - pin_group5.set_io1::(context.PB3); + pin_group5.set_io1::(context.PB3); let tsc_sensor1 = pin_group5.set_io2(context.PB4); let tsc_sensor2 = pin_group5.set_io3(context.PB6); @@ -128,14 +128,14 @@ async fn main(_spawner: embassy_executor::Spawner) { // ---------- setting up acquisition banks ---------- // sensor0 and sensor1 in this example belong to different TSC-groups, // therefore we can acquire and read them both in one go. - let bank1 = touch_controller.create_acquisition_bank(TscAcquisitionBankPins { + let bank1 = touch_controller.create_acquisition_bank(AcquisitionBankPins { g1_pin: Some(tsc_sensor0), g5_pin: Some(tsc_sensor1), ..Default::default() }); // `sensor1` and `sensor2` belongs to the same TSC-group, therefore we must make sure to // acquire them one at the time. Therefore, we organize them into different acquisition banks. - let bank2 = touch_controller.create_acquisition_bank(TscAcquisitionBankPins { + let bank2 = touch_controller.create_acquisition_bank(AcquisitionBankPins { g5_pin: Some(tsc_sensor2), ..Default::default() }); diff --git a/examples/stm32l4/src/bin/tsc_async.rs b/examples/stm32l4/src/bin/tsc_async.rs index ada2c468f..b9a059e2e 100644 --- a/examples/stm32l4/src/bin/tsc_async.rs +++ b/examples/stm32l4/src/bin/tsc_async.rs @@ -50,9 +50,9 @@ async fn main(_spawner: embassy_executor::Spawner) { let mut pin_group: PinGroupWithRoles = PinGroupWithRoles::default(); // D25 - pin_group.set_io1::(context.PB4); + pin_group.set_io1::(context.PB4); // D21 - let tsc_sensor = pin_group.set_io2::(context.PB5); + let tsc_sensor = pin_group.set_io2::(context.PB5); let pin_groups: PinGroups = PinGroups { g2: Some(pin_group.pin_group), diff --git a/examples/stm32l4/src/bin/tsc_blocking.rs b/examples/stm32l4/src/bin/tsc_blocking.rs index 76aba55ba..12084f8e2 100644 --- a/examples/stm32l4/src/bin/tsc_blocking.rs +++ b/examples/stm32l4/src/bin/tsc_blocking.rs @@ -74,9 +74,9 @@ async fn main(_spawner: embassy_executor::Spawner) { let mut g2: PinGroupWithRoles = PinGroupWithRoles::default(); // D25 - g2.set_io1::(context.PB4); + g2.set_io1::(context.PB4); // D21 - let tsc_sensor = g2.set_io2::(context.PB5); + let tsc_sensor = g2.set_io2::(context.PB5); let pin_groups: PinGroups = PinGroups { g2: Some(g2.pin_group), @@ -128,7 +128,7 @@ const MAX_GROUP_STATUS_READ_ATTEMPTS: usize = 10; // attempt to read group status and delay when still ongoing async fn read_touch_value( touch_controller: &mut tsc::Tsc<'_, peripherals::TSC, mode::Blocking>, - sensor_pin: TscIOPin, + sensor_pin: tsc::IOPin, ) -> Option { for _ in 0..MAX_GROUP_STATUS_READ_ATTEMPTS { match touch_controller.group_get_status(sensor_pin.group()) { diff --git a/examples/stm32l4/src/bin/tsc_multipin.rs b/examples/stm32l4/src/bin/tsc_multipin.rs index f26a6f4eb..2fadbe16a 100644 --- a/examples/stm32l4/src/bin/tsc_multipin.rs +++ b/examples/stm32l4/src/bin/tsc_multipin.rs @@ -74,7 +74,7 @@ const SENSOR_THRESHOLD: u16 = 20; async fn acquire_sensors( touch_controller: &mut Tsc<'static, peripherals::TSC, mode::Async>, - tsc_acquisition_bank: &TscAcquisitionBank, + tsc_acquisition_bank: &AcquisitionBank, ) { touch_controller.set_active_channels_mask(tsc_acquisition_bank.mask()); touch_controller.start(); @@ -91,11 +91,11 @@ async fn main(_spawner: embassy_executor::Spawner) { // ---------- initial configuration of TSC ---------- let mut g1: PinGroupWithRoles = PinGroupWithRoles::default(); - g1.set_io1::(context.PB12); - let sensor0 = g1.set_io2::(context.PB13); + g1.set_io1::(context.PB12); + let sensor0 = g1.set_io2::(context.PB13); let mut g2: PinGroupWithRoles = PinGroupWithRoles::default(); - g2.set_io1::(context.PB4); + g2.set_io1::(context.PB4); let sensor1 = g2.set_io2(context.PB5); let sensor2 = g2.set_io3(context.PB6); @@ -124,14 +124,14 @@ async fn main(_spawner: embassy_executor::Spawner) { // ---------- setting up acquisition banks ---------- // sensor0 and sensor1 belong to different TSC-groups, therefore we can acquire and // read them both in one go. - let bank1 = touch_controller.create_acquisition_bank(TscAcquisitionBankPins { + let bank1 = touch_controller.create_acquisition_bank(AcquisitionBankPins { g1_pin: Some(sensor0), g2_pin: Some(sensor1), ..Default::default() }); // `sensor1` and `sensor2` belongs to the same TSC-group, therefore we must make sure to // acquire them one at the time. We do this by organizing them into different acquisition banks. - let bank2 = touch_controller.create_acquisition_bank(TscAcquisitionBankPins { + let bank2 = touch_controller.create_acquisition_bank(AcquisitionBankPins { g2_pin: Some(sensor2), ..Default::default() }); diff --git a/examples/stm32u5/src/bin/tsc.rs b/examples/stm32u5/src/bin/tsc.rs index 800486665..a85acc4c7 100644 --- a/examples/stm32u5/src/bin/tsc.rs +++ b/examples/stm32u5/src/bin/tsc.rs @@ -36,15 +36,15 @@ async fn main(_spawner: embassy_executor::Spawner) { }; let mut g1: PinGroupWithRoles = PinGroupWithRoles::default(); - g1.set_io2::(context.PB13); - g1.set_io3::(context.PB14); + g1.set_io2::(context.PB13); + g1.set_io3::(context.PB14); let mut g2: PinGroupWithRoles = PinGroupWithRoles::default(); - g2.set_io1::(context.PB4); + g2.set_io1::(context.PB4); let sensor0 = g2.set_io2(context.PB5); let mut g7: PinGroupWithRoles = PinGroupWithRoles::default(); - g7.set_io2::(context.PE3); + g7.set_io2::(context.PE3); let sensor1 = g7.set_io3(context.PE4); let pin_groups: PinGroups = PinGroups { @@ -56,7 +56,7 @@ async fn main(_spawner: embassy_executor::Spawner) { let mut touch_controller = tsc::Tsc::new_async(context.TSC, pin_groups, config, Irqs).unwrap(); - let acquisition_bank = touch_controller.create_acquisition_bank(TscAcquisitionBankPins { + let acquisition_bank = touch_controller.create_acquisition_bank(AcquisitionBankPins { g2_pin: Some(sensor0), g7_pin: Some(sensor1), ..Default::default() From efbe7fb8e8ac77eaad2787bb0ea981b1e0f875c0 Mon Sep 17 00:00:00 2001 From: michel Date: Fri, 22 Nov 2024 16:48:18 +0100 Subject: [PATCH 262/361] stm32 tsc examples: minor corrections --- examples/stm32l0/src/bin/tsc_multipin.rs | 8 ++++---- examples/stm32l4/src/bin/tsc_multipin.rs | 4 ++-- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/examples/stm32l0/src/bin/tsc_multipin.rs b/examples/stm32l0/src/bin/tsc_multipin.rs index bf75a5657..6343de141 100644 --- a/examples/stm32l0/src/bin/tsc_multipin.rs +++ b/examples/stm32l0/src/bin/tsc_multipin.rs @@ -9,7 +9,7 @@ // // This example demonstrates how to: // 1. Configure multiple channel pins within a single TSC group -// 2. Use the set_active_channels method to switch between different channels +// 2. Use the set_active_channels_bank method to switch between sets of different channels (acquisition banks) // 3. Read and interpret touch values from multiple channels in the same group // // Suggested physical setup on STM32L073RZ Nucleo board: @@ -29,7 +29,7 @@ // - PA0 as sampling capacitor, TSC group 1 IO1 (label A0) // - PA1 as channel, TSC group 1 IO2 (label A1) // - PB3 as sampling capacitor, TSC group 5 IO1 (label D3) -// - PB4 as channel, TSC group 5 IO2 (label D3) +// - PB4 as channel, TSC group 5 IO2 (label D10) // - PB6 as channel, TSC group 5 IO3 (label D5) // // The pins have been chosen to make it easy to simply add capacitors directly onto the board and @@ -80,7 +80,7 @@ async fn acquire_sensors( touch_controller: &mut Tsc<'static, peripherals::TSC, mode::Async>, tsc_acquisition_bank: &AcquisitionBank, ) { - touch_controller.set_active_channels_mask(tsc_acquisition_bank.mask()); + touch_controller.set_active_channels_bank(tsc_acquisition_bank); touch_controller.start(); touch_controller.pend_for_acquisition().await; touch_controller.discharge_io(true); @@ -156,7 +156,7 @@ async fn main(_spawner: embassy_executor::Spawner) { acquire_sensors(&mut touch_controller, &bank1).await; let readings1 = touch_controller.get_acquisition_bank_values(&bank1); acquire_sensors(&mut touch_controller, &bank2).await; - let readings2 = touch_controller.get_acquisition_bank_values(&bank1); + let readings2 = touch_controller.get_acquisition_bank_values(&bank2); let mut touched_sensors_count = 0; for reading in readings1.iter() { diff --git a/examples/stm32l4/src/bin/tsc_multipin.rs b/examples/stm32l4/src/bin/tsc_multipin.rs index 2fadbe16a..8fec5ddc4 100644 --- a/examples/stm32l4/src/bin/tsc_multipin.rs +++ b/examples/stm32l4/src/bin/tsc_multipin.rs @@ -12,7 +12,7 @@ // ## This example demonstrates how to: // // 1. Configure multiple channel pins within a single TSC group -// 2. Use the set_active_channels method to switch between different channels +// 2. Use the set_active_channels_bank method to switch between sets of different channels (acquisition banks) // 3. Read and interpret touch values from multiple channels in the same group // // ## Suggested physical setup on STM32L4R5ZI-P board: @@ -76,7 +76,7 @@ async fn acquire_sensors( touch_controller: &mut Tsc<'static, peripherals::TSC, mode::Async>, tsc_acquisition_bank: &AcquisitionBank, ) { - touch_controller.set_active_channels_mask(tsc_acquisition_bank.mask()); + touch_controller.set_active_channels_bank(tsc_acquisition_bank); touch_controller.start(); touch_controller.pend_for_acquisition().await; touch_controller.discharge_io(true); From dcd6284996c501b2d376f5c1d4af6fc5a0f00521 Mon Sep 17 00:00:00 2001 From: michel Date: Fri, 22 Nov 2024 16:49:06 +0100 Subject: [PATCH 263/361] stm32 tsc: added multipin example for stm32f3 --- examples/stm32f3/src/bin/tsc_multipin.rs | 204 +++++++++++++++++++++++ 1 file changed, 204 insertions(+) create mode 100644 examples/stm32f3/src/bin/tsc_multipin.rs diff --git a/examples/stm32f3/src/bin/tsc_multipin.rs b/examples/stm32f3/src/bin/tsc_multipin.rs new file mode 100644 index 000000000..c524c3760 --- /dev/null +++ b/examples/stm32f3/src/bin/tsc_multipin.rs @@ -0,0 +1,204 @@ +// Example of TSC (Touch Sensing Controller) using multiple pins from the same tsc-group. +// +// What is special about using multiple TSC pins as sensor channels from the same TSC group, +// is that only one TSC pin for each TSC group can be acquired and read at the time. +// To control which channel pins are acquired and read, we must write a mask before initiating an +// acquisition. To help manage and abstract all this business away, we can organize our channel +// pins into acquisition banks. Each acquisition bank can contain exactly one channel pin per TSC +// group and it will contain the relevant mask. +// +// This example demonstrates how to: +// 1. Configure multiple channel pins within a single TSC group +// 2. Use the set_active_channels_bank method to switch between sets of different channels (acquisition banks) +// 3. Read and interpret touch values from multiple channels in the same group +// +// Suggested physical setup on STM32F303ZE Nucleo board: +// - Connect a 1000pF capacitor between pin PA10 and GND. This is the sampling capacitor for TSC +// group 4. +// - Connect one end of a 1K resistor to pin PA9 and leave the other end loose. +// The loose end will act as a touch sensor. +// +// - Connect a 1000pF capacitor between pin PA7 and GND. This is the sampling capacitor for TSC +// group 2. +// - Connect one end of another 1K resistor to pin PA6 and leave the other end loose. +// The loose end will act as a touch sensor. +// - Connect one end of another 1K resistor to pin PA5 and leave the other end loose. +// The loose end will act as a touch sensor. +// +// The example uses pins from two TSC groups. +// - PA10 as sampling capacitor, TSC group 4 IO2 +// - PA9 as channel, TSC group 4 IO1 +// - PA7 as sampling capacitor, TSC group 2 IO4 +// - PA6 as channel, TSC group 2 IO3 +// - PA5 as channel, TSC group 2 IO2 +// +// The pins have been chosen to make it easy to simply add capacitors directly onto the board and +// connect one leg to GND, and to easily add resistors to the board with no special connectors, +// breadboards, special wires or soldering required. All you need is the capacitors and resistors. +// +// The program reads the designated channel pins and adjusts the LED blinking +// pattern based on which sensor(s) are touched: +// - No touch: LED off +// - one sensor touched: Slow blinking +// - two sensors touched: Fast blinking +// - three sensors touched: LED constantly on +// +// ## Troubleshooting: +// +// - If touch is not detected, try adjusting the SENSOR_THRESHOLD value (currently set to 20). +// - Experiment with different values for ct_pulse_high_length, ct_pulse_low_length, pulse_generator_prescaler, max_count_value, and discharge_delay to optimize sensitivity. +// - Be aware that for some boards there will be overlapping concerns between some pins, for +// example UART connection for the programmer to the MCU and a TSC pin. No errors or warning will +// be emitted if you try to use such a pin for TSC, but you will get strange sensor readings. +// +// Note: Configuration values and sampling capacitor values have been determined experimentally. Optimal values may vary based on your specific hardware setup. Refer to the official STM32 datasheet and user manuals for more information on pin configurations and TSC functionality. + +#![no_std] +#![no_main] + +use defmt::*; +use embassy_stm32::gpio::{Level, Output, Speed}; +use embassy_stm32::tsc::{self, *}; +use embassy_stm32::{mode, peripherals}; +use embassy_time::Timer; +use {defmt_rtt as _, panic_probe as _}; + +const SENSOR_THRESHOLD: u16 = 10; + +async fn acquire_sensors( + touch_controller: &mut Tsc<'static, peripherals::TSC, mode::Blocking>, + tsc_acquisition_bank: &AcquisitionBank, +) { + touch_controller.set_active_channels_bank(tsc_acquisition_bank); + touch_controller.start(); + touch_controller.poll_for_acquisition(); + touch_controller.discharge_io(true); + let discharge_delay = 5; // ms + Timer::after_millis(discharge_delay).await; +} + +#[embassy_executor::main] +async fn main(_spawner: embassy_executor::Spawner) { + let device_config = embassy_stm32::Config::default(); + let context = embassy_stm32::init(device_config); + + // ---------- initial configuration of TSC ---------- + // + let mut pin_group4: PinGroupWithRoles = PinGroupWithRoles::default(); + // D68 on the STM32F303ZE nucleo-board + pin_group4.set_io2::(context.PA10); + // D69 on the STM32F303ZE nucleo-board + let tsc_sensor0 = pin_group4.set_io1(context.PA9); + + let mut pin_group2: PinGroupWithRoles = PinGroupWithRoles::default(); + // D11 on the STM32F303ZE nucleo-board + pin_group2.set_io4::(context.PA7); + // D12 on the STM32F303ZE nucleo-board + let tsc_sensor1 = pin_group2.set_io3(context.PA6); + // D13 on the STM32F303ZE nucleo-board + let tsc_sensor2 = pin_group2.set_io2(context.PA5); + + let config = Config { + ct_pulse_high_length: ChargeTransferPulseCycle::_4, + ct_pulse_low_length: ChargeTransferPulseCycle::_4, + spread_spectrum: false, + spread_spectrum_deviation: SSDeviation::new(2).unwrap(), + spread_spectrum_prescaler: false, + pulse_generator_prescaler: PGPrescalerDivider::_16, + max_count_value: MaxCount::_255, + io_default_mode: false, + synchro_pin_polarity: false, + acquisition_mode: false, + max_count_interrupt: false, + }; + + let pin_groups: PinGroups = PinGroups { + g4: Some(pin_group4.pin_group), + g2: Some(pin_group2.pin_group), + ..Default::default() + }; + + let mut touch_controller = tsc::Tsc::new_blocking(context.TSC, pin_groups, config).unwrap(); + + // ---------- setting up acquisition banks ---------- + // sensor0 and sensor1 in this example belong to different TSC-groups, + // therefore we can acquire and read them both in one go. + let bank1 = touch_controller.create_acquisition_bank(AcquisitionBankPins { + g4_pin: Some(tsc_sensor0), + g2_pin: Some(tsc_sensor1), + ..Default::default() + }); + // `sensor1` and `sensor2` belongs to the same TSC-group, therefore we must make sure to + // acquire them one at the time. Therefore, we organize them into different acquisition banks. + let bank2 = touch_controller.create_acquisition_bank(AcquisitionBankPins { + g2_pin: Some(tsc_sensor2), + ..Default::default() + }); + + // Check if TSC is ready + if touch_controller.get_state() != State::Ready { + crate::panic!("TSC not ready!"); + } + + info!("TSC initialized successfully"); + + // LED2 on the STM32F303ZE nucleo-board + let mut led = Output::new(context.PB7, Level::High, Speed::Low); + + let mut led_state = false; + + loop { + acquire_sensors(&mut touch_controller, &bank1).await; + let readings1 = touch_controller.get_acquisition_bank_values(&bank1); + acquire_sensors(&mut touch_controller, &bank2).await; + let readings2 = touch_controller.get_acquisition_bank_values(&bank2); + + let mut touched_sensors_count = 0; + for reading in readings1.iter() { + info!("{}", reading); + if reading.sensor_value < SENSOR_THRESHOLD { + touched_sensors_count += 1; + } + } + for reading in readings2.iter() { + info!("{}", reading); + if reading.sensor_value < SENSOR_THRESHOLD { + touched_sensors_count += 1; + } + } + + match touched_sensors_count { + 0 => { + // No sensors touched, turn off the LED + led.set_low(); + led_state = false; + } + 1 => { + // One sensor touched, blink slowly + led_state = !led_state; + if led_state { + led.set_high(); + } else { + led.set_low(); + } + Timer::after_millis(200).await; + } + 2 => { + // Two sensors touched, blink faster + led_state = !led_state; + if led_state { + led.set_high(); + } else { + led.set_low(); + } + Timer::after_millis(50).await; + } + 3 => { + // All three sensors touched, LED constantly on + led.set_high(); + led_state = true; + } + _ => crate::unreachable!(), // This case should never occur with 3 sensors + } + } +} From d0b1819aa52666a4fca37211ddab82b01eea84ce Mon Sep 17 00:00:00 2001 From: sawyer bristol Date: Fri, 29 Nov 2024 20:29:43 -0700 Subject: [PATCH 264/361] custom bcd usb version --- embassy-usb/src/builder.rs | 6 ++++++ embassy-usb/src/descriptor.rs | 24 ++++++++++++------------ 2 files changed, 18 insertions(+), 12 deletions(-) diff --git a/embassy-usb/src/builder.rs b/embassy-usb/src/builder.rs index e1bf8041f..c7d83e710 100644 --- a/embassy-usb/src/builder.rs +++ b/embassy-usb/src/builder.rs @@ -15,6 +15,11 @@ pub struct Config<'a> { pub(crate) vendor_id: u16, pub(crate) product_id: u16, + /// Device BCD USB version. + /// + /// Default: `0x02` ("2.1") + pub bcd_usb: u16, + /// Device class code assigned by USB.org. Set to `0xff` for vendor-specific /// devices that do not conform to any class. /// @@ -108,6 +113,7 @@ impl<'a> Config<'a> { vendor_id: vid, product_id: pid, device_release: 0x0010, + bcd_usb: 0x02, manufacturer: None, product: None, serial_number: None, diff --git a/embassy-usb/src/descriptor.rs b/embassy-usb/src/descriptor.rs index 06ebe0481..8824a0355 100644 --- a/embassy-usb/src/descriptor.rs +++ b/embassy-usb/src/descriptor.rs @@ -326,11 +326,11 @@ 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.bcd_usb >> 8) as u8, // bcdUSB + 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, @@ -353,13 +353,13 @@ pub(crate) fn device_qualifier_descriptor(config: &Config) -> [u8; 10] { 10, // bLength 0x06, // 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 - 1, // bNumConfigurations - 0, // Reserved + (config.bcd_usb >> 8) as u8, // bcdUSB + config.device_class, // bDeviceClass + config.device_sub_class, // bDeviceSubClass + config.device_protocol, // bDeviceProtocol + config.max_packet_size_0, // bMaxPacketSize0 + 1, // bNumConfigurations + 0, // Reserved ] } From fdb8ee2e8a70fc078cbd2a4419c919f9fd361d56 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?D=C3=A1niel=20Buga?= Date: Sat, 30 Nov 2024 10:50:57 +0100 Subject: [PATCH 265/361] RTC: Trigger expired alarms --- embassy-stm32/src/time_driver.rs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/embassy-stm32/src/time_driver.rs b/embassy-stm32/src/time_driver.rs index 88b6c48bb..8cf74ef6c 100644 --- a/embassy-stm32/src/time_driver.rs +++ b/embassy-stm32/src/time_driver.rs @@ -414,7 +414,10 @@ impl RtcDriver { let alarm_handle = unsafe { AlarmHandle::new(i as u8) }; let alarm = self.get_alarm(cs, alarm_handle); - self.set_alarm(alarm_handle, alarm.timestamp.get()); + if !self.set_alarm(alarm_handle, alarm.timestamp.get()) { + // If the alarm timestamp has passed, we need to trigger it + self.trigger_alarm(i, cs); + } } } From efee03744e0c82c3b6b4419e072129aad3dae841 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?D=C3=A1niel=20Buga?= Date: Sat, 30 Nov 2024 11:07:20 +0100 Subject: [PATCH 266/361] Only recompute allocated alarms --- 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 8cf74ef6c..00aa3cfa4 100644 --- a/embassy-stm32/src/time_driver.rs +++ b/embassy-stm32/src/time_driver.rs @@ -410,7 +410,7 @@ impl RtcDriver { regs_gp16().cnt().write(|w| w.set_cnt(cnt as u16)); // Now, recompute all alarms - for i in 0..ALARM_COUNT { + for i in 0..self.alarm_count.load(Ordering::Relaxed) as usize { let alarm_handle = unsafe { AlarmHandle::new(i as u8) }; let alarm = self.get_alarm(cs, alarm_handle); From 7d15ec921a279682c4e0b8a9eb5176e541f99b17 Mon Sep 17 00:00:00 2001 From: Derek Hageman Date: Sun, 1 Dec 2024 09:30:23 -0700 Subject: [PATCH 267/361] stm32/usart: Implement data bit selection Implement data bit size selection and add 7-bit mode. --- embassy-stm32/src/usart/mod.rs | 85 +++++++++++++++++++++++++--------- 1 file changed, 63 insertions(+), 22 deletions(-) diff --git a/embassy-stm32/src/usart/mod.rs b/embassy-stm32/src/usart/mod.rs index 8152fc560..d2fb85ee3 100644 --- a/embassy-stm32/src/usart/mod.rs +++ b/embassy-stm32/src/usart/mod.rs @@ -91,6 +91,8 @@ unsafe fn on_interrupt(r: Regs, s: &'static State) { #[cfg_attr(feature = "defmt", derive(defmt::Format))] /// Number of data bits pub enum DataBits { + /// 7 Data Bits + DataBits7, /// 8 Data Bits DataBits8, /// 9 Data Bits @@ -134,6 +136,8 @@ pub enum ConfigError { BaudrateTooHigh, /// Rx or Tx not enabled RxOrTxNotEnabled, + /// Data bits and parity combination not supported + DataParityNotSupported, } #[non_exhaustive] @@ -1617,31 +1621,66 @@ fn configure( w.set_re(enable_rx); } - // configure word size - // if using odd or even parity it must be configured to 9bits - w.set_m0(if config.parity != Parity::ParityNone { - trace!("USART: m0: vals::M0::BIT9"); - vals::M0::BIT9 - } else { - trace!("USART: m0: vals::M0::BIT8"); - vals::M0::BIT8 - }); - // configure parity - w.set_pce(config.parity != Parity::ParityNone); - w.set_ps(match config.parity { - Parity::ParityOdd => { - trace!("USART: set_ps: vals::Ps::ODD"); - vals::Ps::ODD + // configure word size and parity, since the parity bit is inserted into the MSB position, + // it increases the effective word size + match (config.parity, config.data_bits) { + (Parity::ParityNone, DataBits::DataBits8) => { + trace!("USART: m0: 8 data bits, no parity"); + w.set_m0(vals::M0::BIT8); + #[cfg(any(usart_v3, usart_v4))] + w.set_m1(vals::M1::M0); + w.set_pce(false); } - Parity::ParityEven => { - trace!("USART: set_ps: vals::Ps::EVEN"); - vals::Ps::EVEN + (Parity::ParityNone, DataBits::DataBits9) => { + trace!("USART: m0: 9 data bits, no parity"); + w.set_m0(vals::M0::BIT9); + #[cfg(any(usart_v3, usart_v4))] + w.set_m1(vals::M1::M0); + w.set_pce(false); + } + #[cfg(any(usart_v3, usart_v4))] + (Parity::ParityNone, DataBits::DataBits7) => { + trace!("USART: m0: 7 data bits, no parity"); + w.set_m0(vals::M0::BIT8); + w.set_m1(vals::M1::BIT7); + w.set_pce(false); + } + (Parity::ParityEven, DataBits::DataBits8) => { + trace!("USART: m0: 8 data bits, even parity"); + w.set_m0(vals::M0::BIT9); + #[cfg(any(usart_v3, usart_v4))] + w.set_m1(vals::M1::M0); + w.set_pce(true); + w.set_ps(vals::Ps::EVEN); + } + (Parity::ParityEven, DataBits::DataBits7) => { + trace!("USART: m0: 7 data bits, even parity"); + w.set_m0(vals::M0::BIT8); + #[cfg(any(usart_v3, usart_v4))] + w.set_m1(vals::M1::M0); + w.set_pce(true); + w.set_ps(vals::Ps::EVEN); + } + (Parity::ParityOdd, DataBits::DataBits8) => { + trace!("USART: m0: 8 data bits, odd parity"); + w.set_m0(vals::M0::BIT9); + #[cfg(any(usart_v3, usart_v4))] + w.set_m1(vals::M1::M0); + w.set_pce(true); + w.set_ps(vals::Ps::ODD); + } + (Parity::ParityOdd, DataBits::DataBits7) => { + trace!("USART: m0: 7 data bits, odd parity"); + w.set_m0(vals::M0::BIT8); + #[cfg(any(usart_v3, usart_v4))] + w.set_m1(vals::M1::M0); + w.set_pce(true); + w.set_ps(vals::Ps::ODD); } _ => { - trace!("USART: set_ps: vals::Ps::EVEN"); - vals::Ps::EVEN + return Err(ConfigError::DataParityNotSupported); } - }); + } #[cfg(not(usart_v1))] w.set_over8(vals::Over8::from_bits(over8 as _)); #[cfg(usart_v4)] @@ -1649,7 +1688,9 @@ fn configure( trace!("USART: set_fifoen: true (usart_v4)"); w.set_fifoen(true); } - }); + + Ok(()) + })?; Ok(()) } From 18020d672d20d19fa0191a002413457fcf5c4182 Mon Sep 17 00:00:00 2001 From: decaday Date: Mon, 2 Dec 2024 16:51:50 +0800 Subject: [PATCH 268/361] fix: error message for multiple time-driver-xxx cargo features --- embassy-stm32/build.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/embassy-stm32/build.rs b/embassy-stm32/build.rs index 348d48b9b..f92d8b8ba 100644 --- a/embassy-stm32/build.rs +++ b/embassy-stm32/build.rs @@ -195,7 +195,7 @@ fn main() { .to_ascii_lowercase(), ), Err(GetOneError::None) => None, - Err(GetOneError::Multiple) => panic!("Multiple stm32xx Cargo features enabled"), + Err(GetOneError::Multiple) => panic!("Multiple time-driver-xxx Cargo features enabled"), }; let time_driver_singleton = match time_driver.as_ref().map(|x| x.as_ref()) { From ed1f44e58bb38fa1c490f0094400b673bd4bccc3 Mon Sep 17 00:00:00 2001 From: Gerhard de Clercq <11624490+Gerharddc@users.noreply.github.com> Date: Mon, 2 Dec 2024 11:04:17 +0100 Subject: [PATCH 269/361] embassy-dfu-usb: Improve debuggability This commit adds logging to embassy-dfu-usb which helps with debugging issues such as https://github.com/embassy-rs/embassy/issues/3536. It also cleans up a few repeated code blocks and avoid re-initialising the local buffer for every iteration. --- embassy-usb-dfu/Cargo.toml | 2 +- embassy-usb-dfu/src/consts.rs | 41 +++++++++--------- embassy-usb-dfu/src/dfu.rs | 78 ++++++++++++++++++++++------------- 3 files changed, 70 insertions(+), 51 deletions(-) diff --git a/embassy-usb-dfu/Cargo.toml b/embassy-usb-dfu/Cargo.toml index 16da468bd..763c9600d 100644 --- a/embassy-usb-dfu/Cargo.toml +++ b/embassy-usb-dfu/Cargo.toml @@ -42,4 +42,4 @@ esp32c3-hal = { version = "0.13.0", optional = true, default-features = false } [features] dfu = [] application = [] -defmt = ["dep:defmt"] +defmt = ["dep:defmt", "embassy-boot/defmt", "embassy-usb/defmt"] diff --git a/embassy-usb-dfu/src/consts.rs b/embassy-usb-dfu/src/consts.rs index f8a056e5c..190b5ad5a 100644 --- a/embassy-usb-dfu/src/consts.rs +++ b/embassy-usb-dfu/src/consts.rs @@ -7,30 +7,29 @@ pub(crate) const DFU_PROTOCOL_DFU: u8 = 0x02; pub(crate) const DFU_PROTOCOL_RT: u8 = 0x01; pub(crate) const DESC_DFU_FUNCTIONAL: u8 = 0x21; -#[cfg(feature = "defmt")] -defmt::bitflags! { - pub struct DfuAttributes: u8 { - const WILL_DETACH = 0b0000_1000; - const MANIFESTATION_TOLERANT = 0b0000_0100; - const CAN_UPLOAD = 0b0000_0010; - const CAN_DOWNLOAD = 0b0000_0001; - } +macro_rules! define_dfu_attributes { + ($macro:path) => { + $macro! { + /// Attributes supported by the DFU controller. + pub struct DfuAttributes: u8 { + /// Generate WillDetache sequence on bus. + const WILL_DETACH = 0b0000_1000; + /// Device can communicate during manifestation phase. + const MANIFESTATION_TOLERANT = 0b0000_0100; + /// Capable of upload. + const CAN_UPLOAD = 0b0000_0010; + /// Capable of download. + const CAN_DOWNLOAD = 0b0000_0001; + } + } + }; } +#[cfg(feature = "defmt")] +define_dfu_attributes!(defmt::bitflags); + #[cfg(not(feature = "defmt"))] -bitflags::bitflags! { - /// Attributes supported by the DFU controller. - pub struct DfuAttributes: u8 { - /// Generate WillDetache sequence on bus. - const WILL_DETACH = 0b0000_1000; - /// Device can communicate during manifestation phase. - const MANIFESTATION_TOLERANT = 0b0000_0100; - /// Capable of upload. - const CAN_UPLOAD = 0b0000_0010; - /// Capable of download. - const CAN_DOWNLOAD = 0b0000_0001; - } -} +define_dfu_attributes!(bitflags::bitflags); #[derive(Copy, Clone, PartialEq, Eq)] #[repr(u8)] diff --git a/embassy-usb-dfu/src/dfu.rs b/embassy-usb-dfu/src/dfu.rs index e99aa70c3..abd929a9e 100644 --- a/embassy-usb-dfu/src/dfu.rs +++ b/embassy-usb-dfu/src/dfu.rs @@ -1,6 +1,6 @@ use core::marker::PhantomData; -use embassy_boot::{AlignedBuffer, BlockingFirmwareUpdater}; +use embassy_boot::{AlignedBuffer, BlockingFirmwareUpdater, FirmwareUpdaterError}; use embassy_usb::control::{InResponse, OutResponse, Recipient, RequestType}; use embassy_usb::driver::Driver; use embassy_usb::{Builder, Handler}; @@ -19,6 +19,7 @@ pub struct Control<'d, DFU: NorFlash, STATE: NorFlash, RST: Reset, const BLOCK_S state: State, status: Status, offset: usize, + buf: AlignedBuffer, _rst: PhantomData, } @@ -31,6 +32,7 @@ impl<'d, DFU: NorFlash, STATE: NorFlash, RST: Reset, const BLOCK_SIZE: usize> Co state: State::DfuIdle, status: Status::Ok, offset: 0, + buf: AlignedBuffer([0; BLOCK_SIZE]), _rst: PhantomData, } } @@ -42,6 +44,20 @@ impl<'d, DFU: NorFlash, STATE: NorFlash, RST: Reset, const BLOCK_SIZE: usize> Co } } +impl From for Status { + fn from(e: FirmwareUpdaterError) -> Self { + match e { + FirmwareUpdaterError::Flash(e) => match e { + NorFlashErrorKind::NotAligned => Status::ErrWrite, + NorFlashErrorKind::OutOfBounds => Status::ErrAddress, + _ => Status::ErrUnknown, + }, + FirmwareUpdaterError::Signature(_) => Status::ErrVerify, + FirmwareUpdaterError::BadState => Status::ErrUnknown, + } + } +} + impl<'d, DFU: NorFlash, STATE: NorFlash, RST: Reset, const BLOCK_SIZE: usize> Handler for Control<'d, DFU, STATE, RST, BLOCK_SIZE> { @@ -51,65 +67,67 @@ impl<'d, DFU: NorFlash, STATE: NorFlash, RST: Reset, const BLOCK_SIZE: usize> Ha data: &[u8], ) -> Option { if (req.request_type, req.recipient) != (RequestType::Class, Recipient::Interface) { + debug!("Unknown out request: {:?}", req); return None; } match Request::try_from(req.request) { Ok(Request::Abort) => { + info!("Abort requested"); self.reset_state(); Some(OutResponse::Accepted) } Ok(Request::Dnload) if self.attrs.contains(DfuAttributes::CAN_DOWNLOAD) => { if req.value == 0 { + info!("Download starting"); self.state = State::Download; self.offset = 0; } - let mut buf = AlignedBuffer([0; BLOCK_SIZE]); - buf.as_mut()[..data.len()].copy_from_slice(data); + if self.state != State::Download { + error!("Unexpected DNLOAD while chip is waiting for a GETSTATUS"); + self.status = Status::ErrUnknown; + self.state = State::Error; + return Some(OutResponse::Rejected); + } + + if data.len() > BLOCK_SIZE { + error!("USB data len exceeded block size"); + self.status = Status::ErrUnknown; + self.state = State::Error; + return Some(OutResponse::Rejected); + } + + debug!("Copying {} bytes to buffer", data.len()); + self.buf.as_mut()[..data.len()].copy_from_slice(data); + + let final_transfer = req.length == 0; + if final_transfer { + debug!("Receiving final transfer"); - if req.length == 0 { match self.updater.mark_updated() { Ok(_) => { self.status = Status::Ok; self.state = State::ManifestSync; + info!("Update complete"); } Err(e) => { + error!("Error completing update: {}", e); self.state = State::Error; - match e { - embassy_boot::FirmwareUpdaterError::Flash(e) => match e { - NorFlashErrorKind::NotAligned => self.status = Status::ErrWrite, - NorFlashErrorKind::OutOfBounds => self.status = Status::ErrAddress, - _ => self.status = Status::ErrUnknown, - }, - embassy_boot::FirmwareUpdaterError::Signature(_) => self.status = Status::ErrVerify, - embassy_boot::FirmwareUpdaterError::BadState => self.status = Status::ErrUnknown, - } + self.status = e.into(); } } } else { - if self.state != State::Download { - // Unexpected DNLOAD while chip is waiting for a GETSTATUS - self.status = Status::ErrUnknown; - self.state = State::Error; - return Some(OutResponse::Rejected); - } - match self.updater.write_firmware(self.offset, buf.as_ref()) { + debug!("Writing {} bytes at {}", data.len(), self.offset); + match self.updater.write_firmware(self.offset, self.buf.as_ref()) { Ok(_) => { self.status = Status::Ok; self.state = State::DlSync; self.offset += data.len(); } Err(e) => { + error!("Error writing firmware: {:?}", e); self.state = State::Error; - match e { - embassy_boot::FirmwareUpdaterError::Flash(e) => match e { - NorFlashErrorKind::NotAligned => self.status = Status::ErrWrite, - NorFlashErrorKind::OutOfBounds => self.status = Status::ErrAddress, - _ => self.status = Status::ErrUnknown, - }, - embassy_boot::FirmwareUpdaterError::Signature(_) => self.status = Status::ErrVerify, - embassy_boot::FirmwareUpdaterError::BadState => self.status = Status::ErrUnknown, - } + self.status = e.into(); } } } @@ -118,6 +136,7 @@ impl<'d, DFU: NorFlash, STATE: NorFlash, RST: Reset, const BLOCK_SIZE: usize> Ha } Ok(Request::Detach) => Some(OutResponse::Accepted), // Device is already in DFU mode Ok(Request::ClrStatus) => { + info!("Clear status requested"); self.reset_state(); Some(OutResponse::Accepted) } @@ -131,6 +150,7 @@ impl<'d, DFU: NorFlash, STATE: NorFlash, RST: Reset, const BLOCK_SIZE: usize> Ha buf: &'a mut [u8], ) -> Option> { if (req.request_type, req.recipient) != (RequestType::Class, Recipient::Interface) { + debug!("Unknown in request: {:?}", req); return None; } match Request::try_from(req.request) { From f25830a5b67caad4a89eeed2cd602ac12198f6c5 Mon Sep 17 00:00:00 2001 From: sawyer bristol Date: Mon, 2 Dec 2024 15:44:01 -0700 Subject: [PATCH 270/361] bcd default to 2.1 --- embassy-usb/src/builder.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/embassy-usb/src/builder.rs b/embassy-usb/src/builder.rs index c7d83e710..28a449a9f 100644 --- a/embassy-usb/src/builder.rs +++ b/embassy-usb/src/builder.rs @@ -17,7 +17,7 @@ pub struct Config<'a> { /// Device BCD USB version. /// - /// Default: `0x02` ("2.1") + /// Default: `0x0210` ("2.1") pub bcd_usb: u16, /// Device class code assigned by USB.org. Set to `0xff` for vendor-specific @@ -113,7 +113,7 @@ impl<'a> Config<'a> { vendor_id: vid, product_id: pid, device_release: 0x0010, - bcd_usb: 0x02, + bcd_usb: 0x0210, manufacturer: None, product: None, serial_number: None, From 8068f7092efcc8f42fa2031d3977dcade74981d2 Mon Sep 17 00:00:00 2001 From: sawyer bristol Date: Mon, 2 Dec 2024 15:44:29 -0700 Subject: [PATCH 271/361] fix bug and allow bcd to be .0 --- embassy-usb/src/descriptor.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/embassy-usb/src/descriptor.rs b/embassy-usb/src/descriptor.rs index 8824a0355..09ce50951 100644 --- a/embassy-usb/src/descriptor.rs +++ b/embassy-usb/src/descriptor.rs @@ -325,7 +325,7 @@ pub(crate) fn device_descriptor(config: &Config) -> [u8; 18] { [ 18, // bLength 0x01, // bDescriptorType - 0x10, + config.bcd_usb as u8, (config.bcd_usb >> 8) as u8, // bcdUSB config.device_class, // bDeviceClass config.device_sub_class, // bDeviceSubClass From 75382336164c8284196eb1fad057050ba735a72d Mon Sep 17 00:00:00 2001 From: rafael Date: Sat, 9 Nov 2024 17:19:06 +0100 Subject: [PATCH 272/361] correct rp pwm dutycycle examples: desired frequency --- examples/rp/src/bin/pwm.rs | 12 ++++++++---- examples/rp23/src/bin/pwm.rs | 10 +++++++--- examples/rp23/src/bin/pwm_tb6612fng_motor_driver.rs | 11 +++++++---- 3 files changed, 22 insertions(+), 11 deletions(-) diff --git a/examples/rp/src/bin/pwm.rs b/examples/rp/src/bin/pwm.rs index 791b88b5b..06b9313f2 100644 --- a/examples/rp/src/bin/pwm.rs +++ b/examples/rp/src/bin/pwm.rs @@ -48,11 +48,15 @@ async fn pwm_set_dutycycle(slice2: PWM_SLICE2, pin4: PIN_4) { // If we aim for a specific frequency, here is how we can calculate the top value. // The top value sets the period of the PWM cycle, so a counter goes from 0 to top and then wraps around to 0. // Every such wraparound is one PWM cycle. So here is how we get 25KHz: + let desired_freq_hz = 25_000; + let clock_freq_hz = embassy_rp::clocks::clk_sys_freq(); + let divider = 16u8; + let period = (clock_freq_hz / (desired_freq_hz * divider as u32)) as u16 - 1; + let mut c = Config::default(); - let pwm_freq = 25_000; // Hz, our desired frequency - let clock_freq = embassy_rp::clocks::clk_sys_freq(); - c.top = (clock_freq / pwm_freq) as u16 - 1; - + c.top = period; + c.divider = divider.into(); + let mut pwm = Pwm::new_output_a(slice2, pin4, c.clone()); loop { diff --git a/examples/rp23/src/bin/pwm.rs b/examples/rp23/src/bin/pwm.rs index 5a4457158..ed3c94f15 100644 --- a/examples/rp23/src/bin/pwm.rs +++ b/examples/rp23/src/bin/pwm.rs @@ -53,10 +53,14 @@ async fn pwm_set_dutycycle(slice2: PWM_SLICE2, pin4: PIN_4) { // If we aim for a specific frequency, here is how we can calculate the top value. // The top value sets the period of the PWM cycle, so a counter goes from 0 to top and then wraps around to 0. // Every such wraparound is one PWM cycle. So here is how we get 25KHz: + let desired_freq_hz = 25_000; + let clock_freq_hz = embassy_rp::clocks::clk_sys_freq(); + let divider = 16u8; + let period = (clock_freq_hz / (desired_freq_hz * divider as u32)) as u16 - 1; + let mut c = Config::default(); - let pwm_freq = 25_000; // Hz, our desired frequency - let clock_freq = embassy_rp::clocks::clk_sys_freq(); - c.top = (clock_freq / pwm_freq) as u16 - 1; + c.top = period; + c.divider = divider.into(); let mut pwm = Pwm::new_output_a(slice2, pin4, c.clone()); diff --git a/examples/rp23/src/bin/pwm_tb6612fng_motor_driver.rs b/examples/rp23/src/bin/pwm_tb6612fng_motor_driver.rs index 3fad2928c..0682888e8 100644 --- a/examples/rp23/src/bin/pwm_tb6612fng_motor_driver.rs +++ b/examples/rp23/src/bin/pwm_tb6612fng_motor_driver.rs @@ -40,10 +40,11 @@ async fn main(_spawner: Spawner) { let s = split_resources!(p); let r = s.motor; - // we want a PWM frequency of 1KHz, especially cheaper motors do not respond well to higher frequencies - let pwm_freq = 1_000; // Hz, our desired frequency - let clock_freq = embassy_rp::clocks::clk_sys_freq(); - let period = (clock_freq / pwm_freq) as u16 - 1; + // we want a PWM frequency of 10KHz, especially cheaper motors do not respond well to higher frequencies + let desired_freq_hz = 10_000; + let clock_freq_hz = embassy_rp::clocks::clk_sys_freq(); + let divider = 16u8; + let period = (clock_freq_hz / (desired_freq_hz * divider as u32)) as u16 - 1; // we need a standby output and two motors to construct a full TB6612FNG @@ -55,6 +56,7 @@ async fn main(_spawner: Spawner) { let left_bckw = gpio::Output::new(r.left_backward_pin, gpio::Level::Low); let mut left_speed = pwm::Config::default(); left_speed.top = period; + left_speed.divider = divider.into(); let left_pwm = pwm::Pwm::new_output_a(r.left_slice, r.left_pwm_pin, left_speed); let left_motor = Motor::new(left_fwd, left_bckw, left_pwm).unwrap(); @@ -63,6 +65,7 @@ async fn main(_spawner: Spawner) { let right_bckw = gpio::Output::new(r.right_backward_pin, gpio::Level::Low); let mut right_speed = pwm::Config::default(); right_speed.top = period; + right_speed.divider = divider.into(); let right_pwm = pwm::Pwm::new_output_b(r.right_slice, r.right_pwm_pin, right_speed); let right_motor = Motor::new(right_fwd, right_bckw, right_pwm).unwrap(); From ccf2e0c528a85518e9d64456d9dfde9e951ef613 Mon Sep 17 00:00:00 2001 From: rafael Date: Sat, 9 Nov 2024 17:37:24 +0100 Subject: [PATCH 273/361] formatting --- examples/rp/src/bin/pwm.rs | 8 ++++---- examples/rp23/src/bin/pwm.rs | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/examples/rp/src/bin/pwm.rs b/examples/rp/src/bin/pwm.rs index 06b9313f2..cf6531994 100644 --- a/examples/rp/src/bin/pwm.rs +++ b/examples/rp/src/bin/pwm.rs @@ -9,7 +9,7 @@ use defmt::*; use embassy_executor::Spawner; -use embassy_rp::peripherals::{PIN_25, PIN_4, PWM_SLICE2, PWM_SLICE4}; +use embassy_rp::peripherals::{PIN_4, PIN_25, PWM_SLICE2, PWM_SLICE4}; use embassy_rp::pwm::{Config, Pwm, SetDutyCycle}; use embassy_time::Timer; use {defmt_rtt as _, panic_probe as _}; @@ -48,15 +48,15 @@ async fn pwm_set_dutycycle(slice2: PWM_SLICE2, pin4: PIN_4) { // If we aim for a specific frequency, here is how we can calculate the top value. // The top value sets the period of the PWM cycle, so a counter goes from 0 to top and then wraps around to 0. // Every such wraparound is one PWM cycle. So here is how we get 25KHz: - let desired_freq_hz = 25_000; + let desired_freq_hz = 25_000; let clock_freq_hz = embassy_rp::clocks::clk_sys_freq(); let divider = 16u8; let period = (clock_freq_hz / (desired_freq_hz * divider as u32)) as u16 - 1; - + let mut c = Config::default(); c.top = period; c.divider = divider.into(); - + let mut pwm = Pwm::new_output_a(slice2, pin4, c.clone()); loop { diff --git a/examples/rp23/src/bin/pwm.rs b/examples/rp23/src/bin/pwm.rs index ed3c94f15..bd3c287ad 100644 --- a/examples/rp23/src/bin/pwm.rs +++ b/examples/rp23/src/bin/pwm.rs @@ -10,7 +10,7 @@ use defmt::*; use embassy_executor::Spawner; use embassy_rp::block::ImageDef; -use embassy_rp::peripherals::{PIN_25, PIN_4, PWM_SLICE2, PWM_SLICE4}; +use embassy_rp::peripherals::{PIN_4, PIN_25, PWM_SLICE2, PWM_SLICE4}; use embassy_rp::pwm::{Config, Pwm, SetDutyCycle}; use embassy_time::Timer; use {defmt_rtt as _, panic_probe as _}; From ed10b9de7b0292a23036d3144146cff8137caf4f Mon Sep 17 00:00:00 2001 From: rafael Date: Sat, 9 Nov 2024 17:39:21 +0100 Subject: [PATCH 274/361] formatting --- examples/rp/src/bin/pwm.rs | 2 +- examples/rp23/src/bin/pwm.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/rp/src/bin/pwm.rs b/examples/rp/src/bin/pwm.rs index cf6531994..2f5f94870 100644 --- a/examples/rp/src/bin/pwm.rs +++ b/examples/rp/src/bin/pwm.rs @@ -9,7 +9,7 @@ use defmt::*; use embassy_executor::Spawner; -use embassy_rp::peripherals::{PIN_4, PIN_25, PWM_SLICE2, PWM_SLICE4}; +use embassy_rp::peripherals::{PIN_25, PIN_4, PWM_SLICE2, PWM_SLICE4}; use embassy_rp::pwm::{Config, Pwm, SetDutyCycle}; use embassy_time::Timer; use {defmt_rtt as _, panic_probe as _}; diff --git a/examples/rp23/src/bin/pwm.rs b/examples/rp23/src/bin/pwm.rs index bd3c287ad..ed3c94f15 100644 --- a/examples/rp23/src/bin/pwm.rs +++ b/examples/rp23/src/bin/pwm.rs @@ -10,7 +10,7 @@ use defmt::*; use embassy_executor::Spawner; use embassy_rp::block::ImageDef; -use embassy_rp::peripherals::{PIN_4, PIN_25, PWM_SLICE2, PWM_SLICE4}; +use embassy_rp::peripherals::{PIN_25, PIN_4, PWM_SLICE2, PWM_SLICE4}; use embassy_rp::pwm::{Config, Pwm, SetDutyCycle}; use embassy_time::Timer; use {defmt_rtt as _, panic_probe as _}; From 34899491e56db35a1b7e045a7854d10c0ca05457 Mon Sep 17 00:00:00 2001 From: sawyer bristol Date: Mon, 2 Dec 2024 15:57:58 -0700 Subject: [PATCH 275/361] add named bcd versions --- embassy-usb/src/builder.rs | 11 +++++++++-- embassy-usb/src/descriptor.rs | 24 ++++++++++++------------ 2 files changed, 21 insertions(+), 14 deletions(-) diff --git a/embassy-usb/src/builder.rs b/embassy-usb/src/builder.rs index 28a449a9f..8d6d06a6b 100644 --- a/embassy-usb/src/builder.rs +++ b/embassy-usb/src/builder.rs @@ -7,6 +7,13 @@ use crate::msos::{DeviceLevelDescriptor, FunctionLevelDescriptor, MsOsDescriptor use crate::types::{InterfaceNumber, StringIndex}; use crate::{Handler, Interface, UsbDevice, MAX_INTERFACE_COUNT, STRING_INDEX_CUSTOM_START}; +#[derive(Debug, Copy, Clone)] +/// Allows Configuring the Bcd USB version below 2.1 +pub enum BcdUsbVersion { + Two = 0x0200, + TwoOne = 0x0210, +} + #[derive(Debug, Copy, Clone)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] #[non_exhaustive] @@ -18,7 +25,7 @@ pub struct Config<'a> { /// Device BCD USB version. /// /// Default: `0x0210` ("2.1") - pub bcd_usb: u16, + pub bcd_usb: BcdUsbVersion, /// Device class code assigned by USB.org. Set to `0xff` for vendor-specific /// devices that do not conform to any class. @@ -113,7 +120,7 @@ impl<'a> Config<'a> { vendor_id: vid, product_id: pid, device_release: 0x0010, - bcd_usb: 0x0210, + bcd_usb: BcdUsbVersion::TwoOne, manufacturer: None, product: None, serial_number: None, diff --git a/embassy-usb/src/descriptor.rs b/embassy-usb/src/descriptor.rs index 09ce50951..51cd2b39a 100644 --- a/embassy-usb/src/descriptor.rs +++ b/embassy-usb/src/descriptor.rs @@ -326,11 +326,11 @@ pub(crate) fn device_descriptor(config: &Config) -> [u8; 18] { 18, // bLength 0x01, // bDescriptorType config.bcd_usb as u8, - (config.bcd_usb >> 8) as u8, // bcdUSB - config.device_class, // bDeviceClass - config.device_sub_class, // bDeviceSubClass - config.device_protocol, // bDeviceProtocol - config.max_packet_size_0, // bMaxPacketSize0 + (config.bcd_usb as u16 >> 8) as u8, // bcdUSB + 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, @@ -353,13 +353,13 @@ pub(crate) fn device_qualifier_descriptor(config: &Config) -> [u8; 10] { 10, // bLength 0x06, // bDescriptorType 0x10, - (config.bcd_usb >> 8) as u8, // bcdUSB - config.device_class, // bDeviceClass - config.device_sub_class, // bDeviceSubClass - config.device_protocol, // bDeviceProtocol - config.max_packet_size_0, // bMaxPacketSize0 - 1, // bNumConfigurations - 0, // Reserved + (config.bcd_usb as u16 >> 8) as u8, // bcdUSB + config.device_class, // bDeviceClass + config.device_sub_class, // bDeviceSubClass + config.device_protocol, // bDeviceProtocol + config.max_packet_size_0, // bMaxPacketSize0 + 1, // bNumConfigurations + 0, // Reserved ] } From 4d9ee16f3c25241337a4ec730ed3079f7ded19bc Mon Sep 17 00:00:00 2001 From: sawyer bristol Date: Mon, 2 Dec 2024 15:58:39 -0700 Subject: [PATCH 276/361] fix device_qualifier_descriptor with custom bcd version --- embassy-usb/src/descriptor.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/embassy-usb/src/descriptor.rs b/embassy-usb/src/descriptor.rs index 51cd2b39a..e9a6fd79a 100644 --- a/embassy-usb/src/descriptor.rs +++ b/embassy-usb/src/descriptor.rs @@ -352,7 +352,7 @@ pub(crate) fn device_qualifier_descriptor(config: &Config) -> [u8; 10] { [ 10, // bLength 0x06, // bDescriptorType - 0x10, + config.bcd_usb as u8, (config.bcd_usb as u16 >> 8) as u8, // bcdUSB config.device_class, // bDeviceClass config.device_sub_class, // bDeviceSubClass From 180d816e00a9575da95682782da25409eb7deb68 Mon Sep 17 00:00:00 2001 From: sawyer bristol Date: Mon, 2 Dec 2024 16:03:38 -0700 Subject: [PATCH 277/361] add fmt --- embassy-usb/src/builder.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/embassy-usb/src/builder.rs b/embassy-usb/src/builder.rs index 8d6d06a6b..9e5aa0574 100644 --- a/embassy-usb/src/builder.rs +++ b/embassy-usb/src/builder.rs @@ -8,6 +8,8 @@ use crate::types::{InterfaceNumber, StringIndex}; use crate::{Handler, Interface, UsbDevice, MAX_INTERFACE_COUNT, STRING_INDEX_CUSTOM_START}; #[derive(Debug, Copy, Clone)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +#[non_exhaustive] /// Allows Configuring the Bcd USB version below 2.1 pub enum BcdUsbVersion { Two = 0x0200, From fe2c82e98cfe843eeb01196e3fc19fac61b6e6bf Mon Sep 17 00:00:00 2001 From: sawyer bristol Date: Mon, 2 Dec 2024 16:07:10 -0700 Subject: [PATCH 278/361] rename BcdUsbVersion -> UsbVersion --- embassy-usb/src/builder.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/embassy-usb/src/builder.rs b/embassy-usb/src/builder.rs index 9e5aa0574..6c42b07dc 100644 --- a/embassy-usb/src/builder.rs +++ b/embassy-usb/src/builder.rs @@ -11,7 +11,7 @@ use crate::{Handler, Interface, UsbDevice, MAX_INTERFACE_COUNT, STRING_INDEX_CUS #[cfg_attr(feature = "defmt", derive(defmt::Format))] #[non_exhaustive] /// Allows Configuring the Bcd USB version below 2.1 -pub enum BcdUsbVersion { +pub enum UsbVersion { Two = 0x0200, TwoOne = 0x0210, } @@ -27,7 +27,7 @@ pub struct Config<'a> { /// Device BCD USB version. /// /// Default: `0x0210` ("2.1") - pub bcd_usb: BcdUsbVersion, + pub bcd_usb: UsbVersion, /// Device class code assigned by USB.org. Set to `0xff` for vendor-specific /// devices that do not conform to any class. @@ -122,7 +122,7 @@ impl<'a> Config<'a> { vendor_id: vid, product_id: pid, device_release: 0x0010, - bcd_usb: BcdUsbVersion::TwoOne, + bcd_usb: UsbVersion::TwoOne, manufacturer: None, product: None, serial_number: None, From dcf228e4487982b2aef577eb914d80fcb2ecbdc3 Mon Sep 17 00:00:00 2001 From: dstric-aqueduct <96876452+dstric-aqueduct@users.noreply.github.com> Date: Tue, 15 Oct 2024 11:48:54 -0400 Subject: [PATCH 279/361] Add `set_config` method to RP SPI driver Add a `set_config` method to `Spi` to allow reconfiguring SPI mode after creation. The existing implementation of the `embassy-embedded-hal` trait `SetConfig` is changed to use the new method. Existing uses of `SetConfig` trait may need to explicitly call the trait method to maintain current return type. --- embassy-rp/src/spi.rs | 52 +++++++++++++++++++----------- examples/rp23/src/bin/spi_sdmmc.rs | 2 +- 2 files changed, 35 insertions(+), 19 deletions(-) diff --git a/embassy-rp/src/spi.rs b/embassy-rp/src/spi.rs index a8f4e72c7..c48b5c54f 100644 --- a/embassy-rp/src/spi.rs +++ b/embassy-rp/src/spi.rs @@ -84,16 +84,9 @@ impl<'d, T: Instance, M: Mode> Spi<'d, T, M> { ) -> Self { into_ref!(inner); - let p = inner.regs(); - let (presc, postdiv) = calc_prescs(config.frequency); + Self::apply_config(&inner, &config); - p.cpsr().write(|w| w.set_cpsdvsr(presc)); - p.cr0().write(|w| { - w.set_dss(0b0111); // 8bit - w.set_spo(config.polarity == Polarity::IdleHigh); - w.set_sph(config.phase == Phase::CaptureOnSecondTransition); - w.set_scr(postdiv); - }); + let p = inner.regs(); // Always enable DREQ signals -- harmless if DMA is not listening p.dmacr().write(|reg| { @@ -164,6 +157,23 @@ impl<'d, T: Instance, M: Mode> Spi<'d, T, M> { } } + /// Private function to apply SPI configuration (phase, polarity, frequency) settings. + /// + /// Driver should be disabled before making changes and reenabled after the modifications + /// are applied. + fn apply_config(inner: &PeripheralRef<'d, T>, config: &Config) { + let p = inner.regs(); + let (presc, postdiv) = calc_prescs(config.frequency); + + p.cpsr().write(|w| w.set_cpsdvsr(presc)); + p.cr0().write(|w| { + w.set_dss(0b0111); // 8bit + w.set_spo(config.polarity == Polarity::IdleHigh); + w.set_sph(config.phase == Phase::CaptureOnSecondTransition); + w.set_scr(postdiv); + }); + } + /// Write data to SPI blocking execution until done. pub fn blocking_write(&mut self, data: &[u8]) -> Result<(), Error> { let p = self.inner.regs(); @@ -244,6 +254,20 @@ impl<'d, T: Instance, M: Mode> Spi<'d, T, M> { // enable p.cr1().write(|w| w.set_sse(true)); } + + /// Set SPI config. + pub fn set_config(&mut self, config: &Config) { + let p = self.inner.regs(); + + // disable + p.cr1().write(|w| w.set_sse(false)); + + // change stuff + Self::apply_config(&self.inner, config); + + // enable + p.cr1().write(|w| w.set_sse(true)); + } } impl<'d, T: Instance> Spi<'d, T, Blocking> { @@ -697,15 +721,7 @@ impl<'d, T: Instance, M: Mode> SetConfig for Spi<'d, T, M> { type Config = Config; type ConfigError = (); fn set_config(&mut self, config: &Self::Config) -> Result<(), ()> { - let p = self.inner.regs(); - let (presc, postdiv) = calc_prescs(config.frequency); - p.cpsr().write(|w| w.set_cpsdvsr(presc)); - p.cr0().write(|w| { - w.set_dss(0b0111); // 8bit - w.set_spo(config.polarity == Polarity::IdleHigh); - w.set_sph(config.phase == Phase::CaptureOnSecondTransition); - w.set_scr(postdiv); - }); + self.set_config(config); Ok(()) } diff --git a/examples/rp23/src/bin/spi_sdmmc.rs b/examples/rp23/src/bin/spi_sdmmc.rs index aa6b44ffa..cfc38dfd9 100644 --- a/examples/rp23/src/bin/spi_sdmmc.rs +++ b/examples/rp23/src/bin/spi_sdmmc.rs @@ -56,7 +56,7 @@ async fn main(_spawner: Spawner) { // Now that the card is initialized, the SPI clock can go faster let mut config = spi::Config::default(); config.frequency = 16_000_000; - sdcard.spi(|dev| dev.bus_mut().set_config(&config)).ok(); + sdcard.spi(|dev| SetConfig::set_config(dev.bus_mut(), &config)).ok(); // Now let's look for volumes (also known as partitions) on our block device. // To do this we need a Volume Manager. It will take ownership of the block device. From 0138bc5dd8d0c49c86b01668b0af8a2e8dca699c Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Tue, 3 Dec 2024 00:31:36 +0100 Subject: [PATCH 280/361] examples/rp: update sdmmc. --- examples/rp/src/bin/spi_sdmmc.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/examples/rp/src/bin/spi_sdmmc.rs b/examples/rp/src/bin/spi_sdmmc.rs index 4cbc82f7b..a60850d0f 100644 --- a/examples/rp/src/bin/spi_sdmmc.rs +++ b/examples/rp/src/bin/spi_sdmmc.rs @@ -7,7 +7,6 @@ #![no_main] use defmt::*; -use embassy_embedded_hal::SetConfig; use embassy_executor::Spawner; use embassy_rp::spi::Spi; use embassy_rp::{gpio, spi}; @@ -51,7 +50,7 @@ async fn main(_spawner: Spawner) { // Now that the card is initialized, the SPI clock can go faster let mut config = spi::Config::default(); config.frequency = 16_000_000; - sdcard.spi(|dev| dev.bus_mut().set_config(&config)).ok(); + sdcard.spi(|dev| dev.bus_mut().set_config(&config)); // Now let's look for volumes (also known as partitions) on our block device. // To do this we need a Volume Manager. It will take ownership of the block device. From 6494429a20f6d32e9afdd82417392bcfafc73687 Mon Sep 17 00:00:00 2001 From: Enmanuel Parache Date: Wed, 6 Nov 2024 17:02:39 -0400 Subject: [PATCH 281/361] stm32/usart: Changing baud rate --- embassy-stm32/src/usart/mod.rs | 149 +++++++++++++++++++++++++++--- examples/stm32l1/src/bin/usart.rs | 37 ++++++++ 2 files changed, 171 insertions(+), 15 deletions(-) create mode 100644 examples/stm32l1/src/bin/usart.rs diff --git a/embassy-stm32/src/usart/mod.rs b/embassy-stm32/src/usart/mod.rs index d2fb85ee3..b5db672f2 100644 --- a/embassy-stm32/src/usart/mod.rs +++ b/embassy-stm32/src/usart/mod.rs @@ -539,6 +539,12 @@ impl<'d, M: Mode> UartTx<'d, M> { pub fn send_break(&self) { send_break(&self.info.regs); } + + /// Set baudrate + pub fn set_baudrate(&self, baudrate: u32) -> Result<(), ConfigError> + { + set_baudrate(self.info, self.kernel_clock, baudrate) + } } /// Wait until transmission complete @@ -1014,6 +1020,12 @@ impl<'d, M: Mode> UartRx<'d, M> { } Ok(()) } + + /// Set baudrate + pub fn set_baudrate(&self, baudrate: u32) -> Result<(), ConfigError> + { + set_baudrate(self.info, self.kernel_clock, baudrate) + } } impl<'d, M: Mode> Drop for UartTx<'d, M> { @@ -1455,6 +1467,14 @@ impl<'d, M: Mode> Uart<'d, M> { pub fn send_break(&self) { self.tx.send_break(); } + + /// Set baudrate + pub fn set_baudrate(&self, baudrate: u32) -> Result<(), ConfigError> + { + self.tx.set_baudrate(baudrate)?; + self.rx.set_baudrate(baudrate)?; + Ok(()) + } } fn reconfigure(info: &Info, kernel_clock: Hertz, config: &Config) -> Result<(), ConfigError> { @@ -1470,6 +1490,120 @@ fn reconfigure(info: &Info, kernel_clock: Hertz, config: &Config) -> Result<(), Ok(()) } +fn calculate_brr(baud: u32, pclk: u32, presc: u32, mul: u32) -> u32 { + // The calculation to be done to get the BRR is `mul * pclk / presc / baud` + // To do this in 32-bit only we can't multiply `mul` and `pclk` + let clock = pclk / presc; + + // The mul is applied as the last operation to prevent overflow + let brr = clock / baud * mul; + + // The BRR calculation will be a bit off because of integer rounding. + // Because we multiplied our inaccuracy with mul, our rounding now needs to be in proportion to mul. + let rounding = ((clock % baud) * mul + (baud / 2)) / baud; + + brr + rounding +} + +fn set_baudrate(info: &Info, kernel_clock: Hertz, baudrate: u32) -> Result<(), ConfigError> { + info.interrupt.disable(); + + set_usart_baudrate(info, kernel_clock, baudrate)?; + + info.interrupt.unpend(); + unsafe { info.interrupt.enable() }; + + Ok(()) +} + +fn set_usart_baudrate(info: &Info, kernel_clock: Hertz, baudrate: u32) -> Result<(), ConfigError> { + let r = info.regs; + let kind = info.kind; + + #[cfg(not(usart_v4))] + static DIVS: [(u16, ()); 1] = [(1, ())]; + + #[cfg(usart_v4)] + static DIVS: [(u16, vals::Presc); 12] = [ + (1, vals::Presc::DIV1), + (2, vals::Presc::DIV2), + (4, vals::Presc::DIV4), + (6, vals::Presc::DIV6), + (8, vals::Presc::DIV8), + (10, vals::Presc::DIV10), + (12, vals::Presc::DIV12), + (16, vals::Presc::DIV16), + (32, vals::Presc::DIV32), + (64, vals::Presc::DIV64), + (128, vals::Presc::DIV128), + (256, vals::Presc::DIV256), + ]; + + let (mul, brr_min, brr_max) = match kind { + #[cfg(any(usart_v3, usart_v4))] + Kind::Lpuart => { + trace!("USART: Kind::Lpuart"); + (256, 0x300, 0x10_0000) + } + Kind::Uart => { + trace!("USART: Kind::Uart"); + (1, 0x10, 0x1_0000) + } + }; + + r.cr1().modify(|w| { + // disable uart + w.set_ue(false); + }); + + let mut found_brr = None; + for &(presc, _presc_val) in &DIVS { + let brr = calculate_brr(baudrate, kernel_clock.0, presc as u32, mul); + trace!( + "USART: presc={}, div=0x{:08x} (mantissa = {}, fraction = {})", + presc, + brr, + brr >> 4, + brr & 0x0F + ); + + if brr < brr_min { + #[cfg(not(usart_v1))] + if brr * 2 >= brr_min && kind == Kind::Uart && !cfg!(usart_v1) { + r.brr().write_value(regs::Brr(((brr << 1) & !0xF) | (brr & 0x07))); + #[cfg(usart_v4)] + r.presc().write(|w| w.set_prescaler(_presc_val)); + found_brr = Some(brr); + break; + } + return Err(ConfigError::BaudrateTooHigh); + } + + if brr < brr_max { + r.brr().write_value(regs::Brr(brr)); + #[cfg(usart_v4)] + r.presc().write(|w| w.set_prescaler(_presc_val)); + found_brr = Some(brr); + break; + } + } + + let brr = found_brr.ok_or(ConfigError::BaudrateTooLow)?; + + trace!( + "Desired baudrate: {}, actual baudrate: {}", + baudrate, + kernel_clock.0 / brr * mul + ); + + r.cr1().modify(|w| { + // enable uart + w.set_ue(true); + }); + + Ok(()) +} + fn configure( info: &Info, kernel_clock: Hertz, @@ -1515,21 +1649,6 @@ fn configure( } }; - fn calculate_brr(baud: u32, pclk: u32, presc: u32, mul: u32) -> u32 { - // The calculation to be done to get the BRR is `mul * pclk / presc / baud` - // To do this in 32-bit only we can't multiply `mul` and `pclk` - let clock = pclk / presc; - - // The mul is applied as the last operation to prevent overflow - let brr = clock / baud * mul; - - // The BRR calculation will be a bit off because of integer rounding. - // Because we multiplied our inaccuracy with mul, our rounding now needs to be in proportion to mul. - let rounding = ((clock % baud) * mul + (baud / 2)) / baud; - - brr + rounding - } - // UART must be disabled during configuration. r.cr1().modify(|w| { w.set_ue(false); diff --git a/examples/stm32l1/src/bin/usart.rs b/examples/stm32l1/src/bin/usart.rs new file mode 100644 index 000000000..dba79b8b4 --- /dev/null +++ b/examples/stm32l1/src/bin/usart.rs @@ -0,0 +1,37 @@ +#![no_std] +#![no_main] + +use cortex_m_rt::entry; +use defmt::*; +use embassy_stm32::usart::{Config, Uart}; +use embassy_stm32::{bind_interrupts, peripherals, usart}; +use {defmt_rtt as _, panic_probe as _}; + +bind_interrupts!(struct Irqs { + USART2 => usart::InterruptHandler; +}); + +#[entry] +fn main() -> ! { + info!("Hello World!"); + + let p = embassy_stm32::init(Default::default()); + + let config = Config::default(); + let mut usart = Uart::new_blocking(p.USART2, p.PA3, p.PA2, config).unwrap(); + let desired_baudrate = 9600; // Default is 115200 and 9600 is used as example + + match usart.set_baudrate(desired_baudrate) { + Ok(_) => info!("Baud rate set to {}", desired_baudrate), + Err(err) => error!("Error setting baudrate to {}: {}", desired_baudrate, err), + } + + unwrap!(usart.blocking_write(b"Hello Embassy World!\r\n")); + info!("wrote Hello, starting echo"); + + let mut buf = [0u8; 1]; + loop { + unwrap!(usart.blocking_read(&mut buf)); + unwrap!(usart.blocking_write(&buf)); + } +} From def2601558dcc075fc9ee7dc13b5ab5e55fcb96e Mon Sep 17 00:00:00 2001 From: Enmanuel Parache Date: Wed, 6 Nov 2024 17:23:55 -0400 Subject: [PATCH 282/361] rustfmt --- embassy-stm32/src/usart/mod.rs | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/embassy-stm32/src/usart/mod.rs b/embassy-stm32/src/usart/mod.rs index b5db672f2..dc73de0c7 100644 --- a/embassy-stm32/src/usart/mod.rs +++ b/embassy-stm32/src/usart/mod.rs @@ -541,8 +541,7 @@ impl<'d, M: Mode> UartTx<'d, M> { } /// Set baudrate - pub fn set_baudrate(&self, baudrate: u32) -> Result<(), ConfigError> - { + pub fn set_baudrate(&self, baudrate: u32) -> Result<(), ConfigError> { set_baudrate(self.info, self.kernel_clock, baudrate) } } @@ -1022,8 +1021,7 @@ impl<'d, M: Mode> UartRx<'d, M> { } /// Set baudrate - pub fn set_baudrate(&self, baudrate: u32) -> Result<(), ConfigError> - { + pub fn set_baudrate(&self, baudrate: u32) -> Result<(), ConfigError> { set_baudrate(self.info, self.kernel_clock, baudrate) } } @@ -1469,8 +1467,7 @@ impl<'d, M: Mode> Uart<'d, M> { } /// Set baudrate - pub fn set_baudrate(&self, baudrate: u32) -> Result<(), ConfigError> - { + pub fn set_baudrate(&self, baudrate: u32) -> Result<(), ConfigError> { self.tx.set_baudrate(baudrate)?; self.rx.set_baudrate(baudrate)?; Ok(()) From 63298e2f7c5c31eecdf0f734e85e94ad5e423bcb Mon Sep 17 00:00:00 2001 From: Enmanuel Parache Date: Thu, 7 Nov 2024 14:43:27 -0400 Subject: [PATCH 283/361] Reduced code duplication --- embassy-stm32/src/usart/mod.rs | 183 ++++++++++++--------------------- 1 file changed, 63 insertions(+), 120 deletions(-) diff --git a/embassy-stm32/src/usart/mod.rs b/embassy-stm32/src/usart/mod.rs index dc73de0c7..6bde827b9 100644 --- a/embassy-stm32/src/usart/mod.rs +++ b/embassy-stm32/src/usart/mod.rs @@ -1513,10 +1513,12 @@ fn set_baudrate(info: &Info, kernel_clock: Hertz, baudrate: u32) -> Result<(), C Ok(()) } -fn set_usart_baudrate(info: &Info, kernel_clock: Hertz, baudrate: u32) -> Result<(), ConfigError> { - let r = info.regs; - let kind = info.kind; - +fn find_and_set_brr( + r: stm32_metapac::usart::Usart, + kind: Kind, + kernel_clock: Hertz, + baudrate: u32, +) -> Result { #[cfg(not(usart_v4))] static DIVS: [(u16, ()); 1] = [(1, ())]; @@ -1548,12 +1550,8 @@ fn set_usart_baudrate(info: &Info, kernel_clock: Hertz, baudrate: u32) -> Result } }; - r.cr1().modify(|w| { - // disable uart - w.set_ue(false); - }); - let mut found_brr = None; + let mut over8 = false; for &(presc, _presc_val) in &DIVS { let brr = calculate_brr(baudrate, kernel_clock.0, presc as u32, mul); trace!( @@ -1564,106 +1562,6 @@ fn set_usart_baudrate(info: &Info, kernel_clock: Hertz, baudrate: u32) -> Result brr & 0x0F ); - if brr < brr_min { - #[cfg(not(usart_v1))] - if brr * 2 >= brr_min && kind == Kind::Uart && !cfg!(usart_v1) { - r.brr().write_value(regs::Brr(((brr << 1) & !0xF) | (brr & 0x07))); - #[cfg(usart_v4)] - r.presc().write(|w| w.set_prescaler(_presc_val)); - found_brr = Some(brr); - break; - } - return Err(ConfigError::BaudrateTooHigh); - } - - if brr < brr_max { - r.brr().write_value(regs::Brr(brr)); - #[cfg(usart_v4)] - r.presc().write(|w| w.set_prescaler(_presc_val)); - found_brr = Some(brr); - break; - } - } - - let brr = found_brr.ok_or(ConfigError::BaudrateTooLow)?; - - trace!( - "Desired baudrate: {}, actual baudrate: {}", - baudrate, - kernel_clock.0 / brr * mul - ); - - r.cr1().modify(|w| { - // enable uart - w.set_ue(true); - }); - - Ok(()) -} - -fn configure( - info: &Info, - kernel_clock: Hertz, - config: &Config, - enable_rx: bool, - enable_tx: bool, -) -> Result<(), ConfigError> { - let r = info.regs; - let kind = info.kind; - - if !enable_rx && !enable_tx { - return Err(ConfigError::RxOrTxNotEnabled); - } - - #[cfg(not(usart_v4))] - static DIVS: [(u16, ()); 1] = [(1, ())]; - - #[cfg(usart_v4)] - static DIVS: [(u16, vals::Presc); 12] = [ - (1, vals::Presc::DIV1), - (2, vals::Presc::DIV2), - (4, vals::Presc::DIV4), - (6, vals::Presc::DIV6), - (8, vals::Presc::DIV8), - (10, vals::Presc::DIV10), - (12, vals::Presc::DIV12), - (16, vals::Presc::DIV16), - (32, vals::Presc::DIV32), - (64, vals::Presc::DIV64), - (128, vals::Presc::DIV128), - (256, vals::Presc::DIV256), - ]; - - let (mul, brr_min, brr_max) = match kind { - #[cfg(any(usart_v3, usart_v4))] - Kind::Lpuart => { - trace!("USART: Kind::Lpuart"); - (256, 0x300, 0x10_0000) - } - Kind::Uart => { - trace!("USART: Kind::Uart"); - (1, 0x10, 0x1_0000) - } - }; - - // UART must be disabled during configuration. - r.cr1().modify(|w| { - w.set_ue(false); - }); - - #[cfg(not(usart_v1))] - let mut over8 = false; - let mut found_brr = None; - for &(presc, _presc_val) in &DIVS { - let brr = calculate_brr(config.baudrate, kernel_clock.0, presc as u32, mul); - trace!( - "USART: presc={}, div=0x{:08x} (mantissa = {}, fraction = {})", - presc, - brr, - brr >> 4, - brr & 0x0F - ); - if brr < brr_min { #[cfg(not(usart_v1))] if brr * 2 >= brr_min && kind == Kind::Uart && !cfg!(usart_v1) { @@ -1686,18 +1584,63 @@ fn configure( } } - let brr = found_brr.ok_or(ConfigError::BaudrateTooLow)?; + match found_brr { + Some(brr) => { + #[cfg(not(usart_v1))] + let oversampling = if over8 { "8 bit" } else { "16 bit" }; + #[cfg(usart_v1)] + let oversampling = "default"; + trace!( + "Using {} oversampling, desired baudrate: {}, actual baudrate: {}", + oversampling, + baudrate, + kernel_clock.0 / brr * mul + ); + Ok(over8) + } + None => Err(ConfigError::BaudrateTooLow), + } +} - #[cfg(not(usart_v1))] - let oversampling = if over8 { "8 bit" } else { "16 bit" }; - #[cfg(usart_v1)] - let oversampling = "default"; - trace!( - "Using {} oversampling, desired baudrate: {}, actual baudrate: {}", - oversampling, - config.baudrate, - kernel_clock.0 / brr * mul - ); +fn set_usart_baudrate(info: &Info, kernel_clock: Hertz, baudrate: u32) -> Result<(), ConfigError> { + let r = info.regs; + r.cr1().modify(|w| { + // disable uart + w.set_ue(false); + }); + let over8 = find_and_set_brr(r, info.kind, kernel_clock, baudrate)?; + + r.cr1().modify(|w| { + // enable uart + w.set_ue(true); + + #[cfg(not(usart_v1))] + w.set_over8(vals::Over8::from_bits(over8 as _)); + }); + + Ok(()) +} + +fn configure( + info: &Info, + kernel_clock: Hertz, + config: &Config, + enable_rx: bool, + enable_tx: bool, +) -> Result<(), ConfigError> { + let r = info.regs; + let kind = info.kind; + + if !enable_rx && !enable_tx { + return Err(ConfigError::RxOrTxNotEnabled); + } + + // UART must be disabled during configuration. + r.cr1().modify(|w| { + w.set_ue(false); + }); + + let over8 = find_and_set_brr(r, kind, kernel_clock, config.baudrate)?; r.cr2().write(|w| { w.set_stop(match config.stop_bits { From 32a1b232cb74e61edc98aa5f499e58a1b13d055b Mon Sep 17 00:00:00 2001 From: Enmanuel Parache Date: Thu, 7 Nov 2024 15:24:23 -0400 Subject: [PATCH 284/361] Fixed build error --- embassy-stm32/src/usart/mod.rs | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/embassy-stm32/src/usart/mod.rs b/embassy-stm32/src/usart/mod.rs index 6bde827b9..2d801e6bf 100644 --- a/embassy-stm32/src/usart/mod.rs +++ b/embassy-stm32/src/usart/mod.rs @@ -1513,12 +1513,7 @@ fn set_baudrate(info: &Info, kernel_clock: Hertz, baudrate: u32) -> Result<(), C Ok(()) } -fn find_and_set_brr( - r: stm32_metapac::usart::Usart, - kind: Kind, - kernel_clock: Hertz, - baudrate: u32, -) -> Result { +fn find_and_set_brr(r: Regs, kind: Kind, kernel_clock: Hertz, baudrate: u32) -> Result { #[cfg(not(usart_v4))] static DIVS: [(u16, ()); 1] = [(1, ())]; @@ -1551,7 +1546,11 @@ fn find_and_set_brr( }; let mut found_brr = None; + #[cfg(not(usart_v1))] let mut over8 = false; + #[cfg(usart_v1)] + let over8 = false; + for &(presc, _presc_val) in &DIVS { let brr = calculate_brr(baudrate, kernel_clock.0, presc as u32, mul); trace!( @@ -1608,7 +1607,11 @@ fn set_usart_baudrate(info: &Info, kernel_clock: Hertz, baudrate: u32) -> Result // disable uart w.set_ue(false); }); + + #[cfg(not(usart_v1))] let over8 = find_and_set_brr(r, info.kind, kernel_clock, baudrate)?; + #[cfg(usart_v1)] + let _over8 = find_and_set_brr(r, info.kind, kernel_clock, baudrate)?; r.cr1().modify(|w| { // enable uart @@ -1640,7 +1643,10 @@ fn configure( w.set_ue(false); }); + #[cfg(not(usart_v1))] let over8 = find_and_set_brr(r, kind, kernel_clock, config.baudrate)?; + #[cfg(usart_v1)] + let _over8 = find_and_set_brr(r, kind, kernel_clock, config.baudrate)?; r.cr2().write(|w| { w.set_stop(match config.stop_bits { From 8d2fbf0e143c99c0f10b204e5efbaa81749decbf Mon Sep 17 00:00:00 2001 From: Enmanuel Parache Date: Sat, 9 Nov 2024 12:38:02 -0400 Subject: [PATCH 285/361] Extend set_baudrate implementation to Buffered and RingBuffered structs --- embassy-stm32/src/usart/buffered.rs | 21 +++++++++++++++++++-- embassy-stm32/src/usart/ringbuffered.rs | 9 ++++++++- 2 files changed, 27 insertions(+), 3 deletions(-) diff --git a/embassy-stm32/src/usart/buffered.rs b/embassy-stm32/src/usart/buffered.rs index f7b2bf4b4..814be2858 100644 --- a/embassy-stm32/src/usart/buffered.rs +++ b/embassy-stm32/src/usart/buffered.rs @@ -12,8 +12,8 @@ use embassy_sync::waitqueue::AtomicWaker; #[cfg(not(any(usart_v1, usart_v2)))] use super::DePin; use super::{ - clear_interrupt_flags, configure, rdr, reconfigure, send_break, sr, tdr, Config, ConfigError, CtsPin, Error, Info, - Instance, Regs, RtsPin, RxPin, TxPin, + clear_interrupt_flags, configure, rdr, reconfigure, send_break, set_baudrate, sr, tdr, Config, ConfigError, CtsPin, + Error, Info, Instance, Regs, RtsPin, RxPin, TxPin, }; use crate::gpio::{AfType, AnyPin, OutputType, Pull, SealedPin as _, Speed}; use crate::interrupt::{self, InterruptExt}; @@ -441,6 +441,13 @@ impl<'d> BufferedUart<'d> { pub fn send_break(&self) { self.tx.send_break() } + + /// Set baudrate + pub fn set_baudrate(&self, baudrate: u32) -> Result<(), ConfigError> { + self.tx.set_baudrate(baudrate)?; + self.rx.set_baudrate(baudrate)?; + Ok(()) + } } impl<'d> BufferedUartRx<'d> { @@ -535,6 +542,11 @@ impl<'d> BufferedUartRx<'d> { Ok(()) } + + /// Set baudrate + pub fn set_baudrate(&self, baudrate: u32) -> Result<(), ConfigError> { + set_baudrate(self.info, self.kernel_clock, baudrate) + } } impl<'d> BufferedUartTx<'d> { @@ -625,6 +637,11 @@ impl<'d> BufferedUartTx<'d> { pub fn send_break(&self) { send_break(&self.info.regs); } + + /// Set baudrate + pub fn set_baudrate(&self, baudrate: u32) -> Result<(), ConfigError> { + set_baudrate(self.info, self.kernel_clock, baudrate) + } } impl<'d> Drop for BufferedUartRx<'d> { diff --git a/embassy-stm32/src/usart/ringbuffered.rs b/embassy-stm32/src/usart/ringbuffered.rs index a791ac2a9..560ce4e8f 100644 --- a/embassy-stm32/src/usart/ringbuffered.rs +++ b/embassy-stm32/src/usart/ringbuffered.rs @@ -8,7 +8,9 @@ use embassy_hal_internal::PeripheralRef; use embedded_io_async::ReadReady; use futures_util::future::{select, Either}; -use super::{clear_interrupt_flags, rdr, reconfigure, sr, Config, ConfigError, Error, Info, State, UartRx}; +use super::{ + clear_interrupt_flags, rdr, reconfigure, set_baudrate, sr, Config, ConfigError, Error, Info, State, UartRx, +}; use crate::dma::ReadableRingBuffer; use crate::gpio::{AnyPin, SealedPin as _}; use crate::mode::Async; @@ -213,6 +215,11 @@ impl<'d> RingBufferedUartRx<'d> { Either::Right(((), _)) => Ok(()), } } + + /// Set baudrate + pub fn set_baudrate(&self, baudrate: u32) -> Result<(), ConfigError> { + set_baudrate(self.info, self.kernel_clock, baudrate) + } } impl Drop for RingBufferedUartRx<'_> { From 52ac74982d412a80409858e9b10479812d71cb82 Mon Sep 17 00:00:00 2001 From: Michael de Silva Date: Thu, 30 May 2024 23:41:07 +0530 Subject: [PATCH 286/361] Remove code that isn't used by the example --- examples/rp/src/bin/wifi_scan.rs | 5 ----- 1 file changed, 5 deletions(-) diff --git a/examples/rp/src/bin/wifi_scan.rs b/examples/rp/src/bin/wifi_scan.rs index 434f0074c..2ef899080 100644 --- a/examples/rp/src/bin/wifi_scan.rs +++ b/examples/rp/src/bin/wifi_scan.rs @@ -26,11 +26,6 @@ async fn cyw43_task(runner: cyw43::Runner<'static, Output<'static>, PioSpi<'stat runner.run().await } -#[embassy_executor::task] -async fn net_task(mut runner: embassy_net::Runner<'static, cyw43::NetDriver<'static>>) -> ! { - runner.run().await -} - #[embassy_executor::main] async fn main(spawner: Spawner) { info!("Hello World!"); From 1f9e678066fec147f724cf5635102d795acd8031 Mon Sep 17 00:00:00 2001 From: sawyer bristol Date: Mon, 2 Dec 2024 19:41:50 -0700 Subject: [PATCH 287/361] forgot to expose UsbVersion --- embassy-usb/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/embassy-usb/src/lib.rs b/embassy-usb/src/lib.rs index a5478ca0e..93dd6b4d2 100644 --- a/embassy-usb/src/lib.rs +++ b/embassy-usb/src/lib.rs @@ -23,7 +23,7 @@ mod config { use embassy_futures::select::{select, Either}; use heapless::Vec; -pub use crate::builder::{Builder, Config, FunctionBuilder, InterfaceAltBuilder, InterfaceBuilder}; +pub use crate::builder::{Builder, Config, FunctionBuilder, InterfaceAltBuilder, InterfaceBuilder, UsbVersion}; use crate::config::{MAX_HANDLER_COUNT, MAX_INTERFACE_COUNT}; use crate::control::{InResponse, OutResponse, Recipient, Request, RequestType}; use crate::descriptor::{descriptor_type, lang_id}; From 6065bc63021224b5d3650ad204b387eb622fd8c6 Mon Sep 17 00:00:00 2001 From: Ulf Lilleengen Date: Tue, 3 Dec 2024 15:13:22 +0100 Subject: [PATCH 288/361] Free tx buffer on ipc send failure --- embassy-net-nrf91/src/lib.rs | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/embassy-net-nrf91/src/lib.rs b/embassy-net-nrf91/src/lib.rs index 80d08f7f5..110d0ff24 100644 --- a/embassy-net-nrf91/src/lib.rs +++ b/embassy-net-nrf91/src/lib.rs @@ -507,6 +507,7 @@ impl StateInner { if data.is_empty() { msg.data = ptr::null_mut(); msg.data_len = 0; + self.send_message_raw(msg) } else { assert!(data.len() <= TX_BUF_SIZE); let buf_idx = self.find_free_tx_buf().ok_or(NoFreeBufs)?; @@ -517,10 +518,15 @@ impl StateInner { self.tx_buf_used[buf_idx] = true; fence(Ordering::SeqCst); // synchronize copy_nonoverlapping (non-volatile) with volatile writes below. + if let Err(e) = self.send_message_raw(msg) { + msg.data = ptr::null_mut(); + msg.data_len = 0; + self.tx_buf_used[buf_idx] = false; + Err(e) + } else { + Ok(()) + } } - - // TODO free data buf if send_message_raw fails. - self.send_message_raw(msg) } fn send_message_raw(&mut self, msg: &Message) -> Result<(), NoFreeBufs> { From 0b7f9d84befa3ebf0f04bd0036b0fe9a222f7ecf Mon Sep 17 00:00:00 2001 From: sawyer bristol Date: Tue, 3 Dec 2024 09:54:15 -0700 Subject: [PATCH 289/361] add docs for usb version variants --- embassy-usb/src/builder.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/embassy-usb/src/builder.rs b/embassy-usb/src/builder.rs index 6c42b07dc..008a10f72 100644 --- a/embassy-usb/src/builder.rs +++ b/embassy-usb/src/builder.rs @@ -12,7 +12,9 @@ use crate::{Handler, Interface, UsbDevice, MAX_INTERFACE_COUNT, STRING_INDEX_CUS #[non_exhaustive] /// Allows Configuring the Bcd USB version below 2.1 pub enum UsbVersion { + /// Usb version 2.0 Two = 0x0200, + /// Usb version 2.1 TwoOne = 0x0210, } From aa1bd827ca36f88baa116c2bf5c2cb3c74e6b026 Mon Sep 17 00:00:00 2001 From: Ulf Lilleengen Date: Wed, 4 Dec 2024 14:24:18 +0100 Subject: [PATCH 290/361] Wait for tx buffers to be available for request fn --- embassy-net-nrf91/src/lib.rs | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/embassy-net-nrf91/src/lib.rs b/embassy-net-nrf91/src/lib.rs index 110d0ff24..d8878f147 100644 --- a/embassy-net-nrf91/src/lib.rs +++ b/embassy-net-nrf91/src/lib.rs @@ -209,6 +209,7 @@ async fn new_internal<'a>( tx_seq_no: 0, tx_buf_used: [false; TX_BUF_COUNT], + tx_waker: WakerRegistration::new(), trace_chans: Vec::new(), trace_check: PointerChecker { @@ -311,6 +312,7 @@ struct StateInner { tx_seq_no: u16, tx_buf_used: [bool; TX_BUF_COUNT], + tx_waker: WakerRegistration, trace_chans: Vec, trace_check: PointerChecker, @@ -522,6 +524,7 @@ impl StateInner { msg.data = ptr::null_mut(); msg.data_len = 0; self.tx_buf_used[buf_idx] = false; + self.tx_waker.wake(); Err(e) } else { Ok(()) @@ -586,6 +589,7 @@ impl StateInner { ); } self.tx_buf_used[idx] = false; + self.tx_waker.wake(); } fn handle_data(&mut self, msg: &Message, ch: &mut ch::Runner) { @@ -761,10 +765,22 @@ impl<'a> Control<'a> { state.next_req_serial = state.next_req_serial.wrapping_add(1); } + drop(state); // don't borrow state across awaits. + msg.param[0..4].copy_from_slice(&req_serial.to_le_bytes()); - unwrap!(state.send_message(msg, req_data)); + + poll_fn(|cx| { + let mut state = self.state.borrow_mut(); + state.tx_waker.register(cx.waker()); + match state.send_message(msg, req_data) { + Ok(_) => Poll::Ready(()), + Err(NoFreeBufs) => Poll::Pending, + } + }) + .await; // Setup the pending request state. + let mut state = self.state.borrow_mut(); let (req_slot_idx, req_slot) = state .requests .iter_mut() From a5ab19276e5b421de4ca034121909a6e7257a5d4 Mon Sep 17 00:00:00 2001 From: Badr Bouslikhin Date: Thu, 5 Dec 2024 18:12:31 +0100 Subject: [PATCH 291/361] docs: document softdevice/embassy-boot-nrf integration --- embassy-boot-nrf/README.md | 25 ++++++++++++++++++++++--- 1 file changed, 22 insertions(+), 3 deletions(-) diff --git a/embassy-boot-nrf/README.md b/embassy-boot-nrf/README.md index f0d87e18c..b77cf80bd 100644 --- a/embassy-boot-nrf/README.md +++ b/embassy-boot-nrf/README.md @@ -6,6 +6,25 @@ An adaptation of `embassy-boot` for nRF. ## Features -* Load applications with or without the softdevice. -* Configure bootloader partitions based on linker script. -* Using watchdog timer to detect application failure. +- Load applications with or without the softdevice. +- Configure bootloader partitions based on linker script. +- Using watchdog timer to detect application failure. + +## Working with a SoftDevice + +When a SoftDevice is present, it handles starting the bootloader and the application as needed. + +The SoftDevice architecture supports the bootloader via a configurable base address, referred to as `BOOTLOADERADDR`, in the application flash region. This address can be specified either: + +1. At the `MBR_BOOTLOADER_ADDR` location in flash memory (defined in `nrf_mbr.h`), or +2. In the `UICR.NRFFW[0]` register. + +The `UICR.NRFFW[0]` register is used only if `MBR_BOOTLOADER_ADDR` has its default value of `0xFFFFFFFF`. This bootloader relies on the latter approach. + +In the `memory.x` linker script, there is a section `.uicr_bootloader_start_address` (origin `0x10001014`, length `0x4`) that stores the `BOOTLOADERADDR` value. +Ensure that `__bootloader_start` is set to the origin address of the bootloader partition. + +When a bootloader is present, the SoftDevice forwards interrupts to it and executes the bootloader reset handler, defined in the bootloader's vector table at `BOOTLOADERADDR`. + +Once the bootloader loads the application, the SoftDevice initiates the Application Reset Handler, defined in the application’s vector table at APP_CODE_BASE hardcoded in the SoftDevice. +The active partition's origin **must** match the `APP_CODE_BASE` value hardcoded within the SoftDevice. This value can be found in the release notes for each SoftDevice version. From 75a21e5045c3d770c69737df1b586e668ba5b1b1 Mon Sep 17 00:00:00 2001 From: Badr Bouslikhin Date: Thu, 5 Dec 2024 18:39:54 +0100 Subject: [PATCH 292/361] docs: add an faq on bootloader failures --- docs/pages/faq.adoc | 46 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 46 insertions(+) diff --git a/docs/pages/faq.adoc b/docs/pages/faq.adoc index d4f49bfc1..3e32563cc 100644 --- a/docs/pages/faq.adoc +++ b/docs/pages/faq.adoc @@ -385,3 +385,49 @@ async fn idle() { loop { embassy_futures::yield_now().await; } } ---- + +== Why is my bootloader restarting in loop? + +== Troubleshooting Bootloader Restart Loops + +If your bootloader restarts in a loop, there could be multiple reasons. Here are some things to check: + +=== Validate the `memory.x` File +The bootloader performs critical checks when creating partitions using the addresses defined in `memory.x`. Ensure the following assertions hold true: + +[source,rust] +---- +const { + core::assert!(Self::PAGE_SIZE % ACTIVE::WRITE_SIZE as u32 == 0); + core::assert!(Self::PAGE_SIZE % ACTIVE::ERASE_SIZE as u32 == 0); + core::assert!(Self::PAGE_SIZE % DFU::WRITE_SIZE as u32 == 0); + core::assert!(Self::PAGE_SIZE % DFU::ERASE_SIZE as u32 == 0); +} + +// Ensure enough progress pages to store copy progress +assert_eq!(0, Self::PAGE_SIZE % aligned_buf.len() as u32); +assert!(aligned_buf.len() >= STATE::WRITE_SIZE); +assert_eq!(0, aligned_buf.len() % ACTIVE::WRITE_SIZE); +assert_eq!(0, aligned_buf.len() % DFU::WRITE_SIZE); +---- + +If any of these assertions fail, the bootloader will likely restart in a loop. This failure might not log any messages (e.g., when using `defmt`). Confirm that your `memory.x` file and flash memory align with these requirements. + +=== Handling Panic Logging +Some panic errors might log messages, but certain microcontrollers reset before the message is fully printed. To ensure panic messages are logged, add a delay using no-operation (NOP) instructions before the reset: + +[source,rust] +---- +#[panic_handler] +fn panic(_info: &core::panic::PanicInfo) -> ! { + for _ in 0..10_000_000 { + cortex_m::asm::nop(); + } + cortex_m::asm::udf(); +} +---- + +=== Feed the watchdog + + +Some `embassy-boot` implementations (like `embassy-boot-nrf` and `embassy-boot-rp`) rely on a watchdog timer to detect application failure. The bootloader will restart if your application code does not properly feed the watchdog timer. Make sure to feed it correctly. From 78b536e74ba158917946ee9516924f1966f20ecd Mon Sep 17 00:00:00 2001 From: Badr Bouslikhin Date: Thu, 5 Dec 2024 18:47:33 +0100 Subject: [PATCH 293/361] docs: improve stm32 embassy-usb-dfu example --- .../boot/bootloader/stm32wb-dfu/README.md | 32 +++++++++++++++++-- 1 file changed, 29 insertions(+), 3 deletions(-) diff --git a/examples/boot/bootloader/stm32wb-dfu/README.md b/examples/boot/bootloader/stm32wb-dfu/README.md index d5c6ea57c..3c5f268a0 100644 --- a/examples/boot/bootloader/stm32wb-dfu/README.md +++ b/examples/boot/bootloader/stm32wb-dfu/README.md @@ -1,11 +1,37 @@ # Bootloader for STM32 -The bootloader uses `embassy-boot` to interact with the flash. +This bootloader implementation uses `embassy-boot` and `embassy-usb-dfu` to manage firmware updates and interact with the flash memory on STM32WB55 devices. -# Usage +## Prerequisites -Flash the bootloader +- Rust toolchain with `cargo` installed +- `cargo-flash` for flashing the bootloader +- `dfu-util` for firmware updates +- `cargo-binutils` for binary generation + +## Usage + +### 1. Flash the Bootloader + +First, flash the bootloader to your device: ``` cargo flash --features embassy-stm32/stm32wb55rg --release --chip STM32WB55RGVx ``` + +### 2. Build and Flash Application + +Generate your application binary and flash it using DFU: + +``` +cargo objcopy --release -- -O binary fw.bin +dfu-util -d c0de:cafe -w -D fw.bin +``` + +## Troubleshooting + +- Make sure your device is in DFU mode before flashing +- Verify the USB VID:PID matches your device (c0de:cafe) +- Check USB connections if the device is not detected +- Make sure the transfer size option of `dfu-util` matches the bootloader configuration. By default, `dfu-util` will use the transfer size reported by the device, but you can override it with the `-t` option if needed. +- Make sure `control_buf` size is larger than or equal to the `usb_dfu` `BLOCK_SIZE` parameter (in this example, both are set to 4096 bytes). From 03082a9cdf65bbb675b47fc6b4ea657939fcf01b Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Fri, 6 Dec 2024 11:29:55 +0100 Subject: [PATCH 294/361] nrf/nfct: set correct frame delay timing. This makes it work both with and without trace logging, before it would only work with. --- embassy-nrf/src/nfct.rs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/embassy-nrf/src/nfct.rs b/embassy-nrf/src/nfct.rs index 2756c7952..a79cc5840 100644 --- a/embassy-nrf/src/nfct.rs +++ b/embassy-nrf/src/nfct.rs @@ -215,6 +215,12 @@ impl<'d> NfcT<'d> { r.framedelaymode().write(|w| { w.set_framedelaymode(vals::Framedelaymode::WINDOW_GRID); }); + r.framedelaymin().write(|w| { + w.set_framedelaymin(1152); + }); + r.framedelaymax().write(|w| { + w.set_framedelaymax(0xFFFF); // max + }); info!("waiting for field"); poll_fn(|cx| { @@ -259,12 +265,6 @@ impl<'d> NfcT<'d> { continue; } - // TODO: add support for "window" frame delay, which is technically - // needed to be compliant with iso14443-4 - r.framedelaymode().write(|w| { - w.set_framedelaymode(vals::Framedelaymode::FREE_RUN); - }); - // disable autocoll #[cfg(not(feature = "nrf52832"))] r.autocolresconfig().write(|w| w.0 = 0b11u32); From 0225221f8bfe71723f8673d9e316e9ab7d180782 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Fri, 6 Dec 2024 11:30:33 +0100 Subject: [PATCH 295/361] nrf/nfct: use the right error register for rx and tx. --- embassy-nrf/src/nfct.rs | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/embassy-nrf/src/nfct.rs b/embassy-nrf/src/nfct.rs index a79cc5840..85866063a 100644 --- a/embassy-nrf/src/nfct.rs +++ b/embassy-nrf/src/nfct.rs @@ -328,7 +328,9 @@ impl<'d> NfcT<'d> { if r.events_error().read() != 0 { trace!("Got error?"); - warn!("errors: {:08x}", r.errorstatus().read().0); + let errs = r.errorstatus().read(); + r.errorstatus().write(|w| w.0 = 0xFFFF_FFFF); + trace!("errors: {:08x}", errs.0); r.events_error().write_value(0); return Poll::Ready(Err(Error::RxError)); } @@ -382,7 +384,9 @@ impl<'d> NfcT<'d> { if r.events_rxerror().read() != 0 { trace!("RXerror got in recv frame, should be back in idle state"); r.events_rxerror().write_value(0); - warn!("errors: {:08x}", r.errorstatus().read().0); + let errs = r.framestatus().rx().read(); + r.framestatus().rx().write(|w| w.0 = 0xFFFF_FFFF); + trace!("errors: {:08x}", errs.0); return Poll::Ready(Err(Error::RxError)); } From 306099f9d522e941c5297d9335fd239b732bbf41 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?D=C3=A1niel=20Buga?= Date: Fri, 6 Dec 2024 13:23:06 +0100 Subject: [PATCH 296/361] Prepare embassy-usb-synopsys-otg 0.2.0 --- embassy-stm32/Cargo.toml | 2 +- embassy-usb-synopsys-otg/CHANGELOG.md | 25 +++++++++++++++++++++++++ embassy-usb-synopsys-otg/Cargo.toml | 2 +- embassy-usb-synopsys-otg/README.md | 4 ++-- 4 files changed, 29 insertions(+), 4 deletions(-) create mode 100644 embassy-usb-synopsys-otg/CHANGELOG.md diff --git a/embassy-stm32/Cargo.toml b/embassy-stm32/Cargo.toml index 09b7f805a..607e90739 100644 --- a/embassy-stm32/Cargo.toml +++ b/embassy-stm32/Cargo.toml @@ -50,7 +50,7 @@ embassy-hal-internal = {version = "0.2.0", path = "../embassy-hal-internal", fea embassy-embedded-hal = {version = "0.2.0", path = "../embassy-embedded-hal", default-features = false } embassy-net-driver = { version = "0.2.0", path = "../embassy-net-driver" } embassy-usb-driver = {version = "0.1.0", path = "../embassy-usb-driver" } -embassy-usb-synopsys-otg = {version = "0.1.0", path = "../embassy-usb-synopsys-otg" } +embassy-usb-synopsys-otg = {version = "0.2.0", path = "../embassy-usb-synopsys-otg" } embassy-executor = { version = "0.6.3", path = "../embassy-executor", optional = true } embedded-hal-02 = { package = "embedded-hal", version = "0.2.6", features = ["unproven"] } diff --git a/embassy-usb-synopsys-otg/CHANGELOG.md b/embassy-usb-synopsys-otg/CHANGELOG.md new file mode 100644 index 000000000..293363d9a --- /dev/null +++ b/embassy-usb-synopsys-otg/CHANGELOG.md @@ -0,0 +1,25 @@ +# Changelog for embassy-usb-synopsys-otg + +All notable changes to this project will be documented in this file. + +The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), +and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). + +## Unreleased + +## 0.2.0 - 2024-12-06 + +- Fix corruption in CONTROL OUT transfers (and remove `quirk_setup_late_cnak`) +- Fix build with `defmt` enabled +- Add USBPHYC clock configuration for H7RS series +- Add support for ISO endpoints +- Add support for a full-speed ULPI mode +- Add OTG core DMA address registers +- Ensure endpoint allocation fails when `endpoint_count < MAX_EP_COUNT`. +- New configuration option: `xcvrdly` (transceiver delay). +- `EpState` now implements `Send` and `Sync`. +- The default value of `vbus_detection` is now `false`. + +## 0.1.0 - 2024-04-30 + +Initial release. diff --git a/embassy-usb-synopsys-otg/Cargo.toml b/embassy-usb-synopsys-otg/Cargo.toml index d621577bf..7a9143ef9 100644 --- a/embassy-usb-synopsys-otg/Cargo.toml +++ b/embassy-usb-synopsys-otg/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "embassy-usb-synopsys-otg" -version = "0.1.0" +version = "0.2.0" edition = "2021" license = "MIT OR Apache-2.0" description = "`embassy-usb-driver` implementation for Synopsys OTG USB controllers" diff --git a/embassy-usb-synopsys-otg/README.md b/embassy-usb-synopsys-otg/README.md index 5354f07bf..ba3c8116a 100644 --- a/embassy-usb-synopsys-otg/README.md +++ b/embassy-usb-synopsys-otg/README.md @@ -1,6 +1,6 @@ # Embassy USB driver for the Synopsys USB OTG core -This crate implements [`embassy-usb-driver`](https://crates.io/crates/embassy-usb-driver) for Synopsys USB OTG devices. +This crate implements [`embassy-usb-driver`](https://crates.io/crates/embassy-usb-driver) for Synopsys USB OTG devices. It contains the "core" of the driver that is common across all chips using the Synopsys OTG IP, but it doesn't contain chip-specific initialization such @@ -12,5 +12,5 @@ List of HALs integrating this driver: - [`embassy-stm32`](https://crates.io/crates/embassy-stm32), for STMicroelectronics STM32 chips. - [`esp-hal`](https://crates.io/crates/esp-hal), for Espressif ESP32 chips. -If you wish to integrate this crate into your device's HAL, you will need to add the +If you wish to integrate this crate into your device's HAL, you will need to add the device-specific initialization. See the above crates for examples on how to do it. \ No newline at end of file From 1bb6e455bd8de70a6132dfea19811e7cbecba479 Mon Sep 17 00:00:00 2001 From: Ulf Lilleengen Date: Fri, 6 Dec 2024 15:47:21 +0100 Subject: [PATCH 297/361] Keep track of raw fd used for net data --- embassy-net-nrf91/src/lib.rs | 24 +++++++++++++++--------- 1 file changed, 15 insertions(+), 9 deletions(-) diff --git a/embassy-net-nrf91/src/lib.rs b/embassy-net-nrf91/src/lib.rs index d8878f147..3abe2c766 100644 --- a/embassy-net-nrf91/src/lib.rs +++ b/embassy-net-nrf91/src/lib.rs @@ -198,6 +198,7 @@ async fn new_internal<'a>( cb, requests: [const { None }; REQ_COUNT], next_req_serial: 0x12345678, + net_fd: None, rx_control_list: ptr::null_mut(), rx_data_list: ptr::null_mut(), @@ -305,6 +306,8 @@ struct StateInner { requests: [Option; REQ_COUNT], next_req_serial: u32, + net_fd: Option, + rx_control_list: *mut List, rx_data_list: *mut List, rx_seq_no: u16, @@ -885,6 +888,8 @@ impl<'a> Control<'a> { assert_eq!(status, 0); assert_eq!(msg.param_len, 16); let fd = u32::from_le_bytes(msg.param[12..16].try_into().unwrap()); + self.state.borrow_mut().net_fd.replace(fd); + trace!("got FD: {}", fd); fd } @@ -924,16 +929,17 @@ impl<'a> Runner<'a> { state.poll(&mut self.trace_writer, &mut self.ch); if let Poll::Ready(buf) = self.ch.poll_tx_buf(cx) { - let fd = 128u32; // TODO unhardcode - let mut msg: Message = unsafe { mem::zeroed() }; - msg.channel = 2; // data - msg.id = 0x7006_0004; // IP send - msg.param_len = 12; - msg.param[4..8].copy_from_slice(&fd.to_le_bytes()); - if let Err(e) = state.send_message(&mut msg, buf) { - warn!("tx failed: {:?}", e); + if let Some(fd) = state.net_fd { + let mut msg: Message = unsafe { mem::zeroed() }; + msg.channel = 2; // data + msg.id = 0x7006_0004; // IP send + msg.param_len = 12; + msg.param[4..8].copy_from_slice(&fd.to_le_bytes()); + if let Err(e) = state.send_message(&mut msg, buf) { + warn!("tx failed: {:?}", e); + } + self.ch.tx_done(); } - self.ch.tx_done(); } Poll::Pending From 0bec981882d5dda657a54f46afd3cfac054cd00f Mon Sep 17 00:00:00 2001 From: David Lawrence Date: Fri, 6 Dec 2024 11:07:38 -0500 Subject: [PATCH 298/361] STM32: bump to latest stm32-metapac HRTIM driver is updated per https://github.com/embassy-rs/stm32-data/pull/544 --- embassy-stm32/Cargo.toml | 4 ++-- embassy-stm32/src/hrtim/mod.rs | 22 ++++------------------ examples/stm32f334/src/bin/pwm.rs | 4 ++-- 3 files changed, 8 insertions(+), 22 deletions(-) diff --git a/embassy-stm32/Cargo.toml b/embassy-stm32/Cargo.toml index 607e90739..8b4e7c929 100644 --- a/embassy-stm32/Cargo.toml +++ b/embassy-stm32/Cargo.toml @@ -72,7 +72,7 @@ rand_core = "0.6.3" sdio-host = "0.5.0" critical-section = "1.1" #stm32-metapac = { version = "15" } -stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-ab0ec4c19f81854189bab8215544ccd1256e1045" } +stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-ddb0e7abab14bf3e1399875767b8834442382988" } vcell = "0.1.3" nb = "1.0.0" @@ -101,7 +101,7 @@ proc-macro2 = "1.0.36" quote = "1.0.15" #stm32-metapac = { version = "15", default-features = false, features = ["metadata"]} -stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-ab0ec4c19f81854189bab8215544ccd1256e1045", default-features = false, features = ["metadata"] } +stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-ddb0e7abab14bf3e1399875767b8834442382988", default-features = false, features = ["metadata"] } [features] default = ["rt"] diff --git a/embassy-stm32/src/hrtim/mod.rs b/embassy-stm32/src/hrtim/mod.rs index 13343fc2a..d9b7c16fb 100644 --- a/embassy-stm32/src/hrtim/mod.rs +++ b/embassy-stm32/src/hrtim/mod.rs @@ -236,8 +236,6 @@ pub struct BridgeConverter> { impl> BridgeConverter { /// Create a new HRTIM bridge converter driver. pub fn new(_channel: C, frequency: Hertz) -> Self { - use crate::pac::hrtim::vals::{Activeeffect, Inactiveeffect}; - T::set_channel_frequency(C::raw(), frequency); // Always enable preload @@ -258,28 +256,16 @@ impl> BridgeConverter { // Therefore, software-implemented dead time must be used when setting the duty cycles // Set output 1 to active on a period event - T::regs() - .tim(C::raw()) - .setr(0) - .modify(|w| w.set_per(Activeeffect::SETACTIVE)); + T::regs().tim(C::raw()).setr(0).modify(|w| w.set_per(true)); // Set output 1 to inactive on a compare 1 event - T::regs() - .tim(C::raw()) - .rstr(0) - .modify(|w| w.set_cmp(0, Inactiveeffect::SETINACTIVE)); + T::regs().tim(C::raw()).rstr(0).modify(|w| w.set_cmp(0, true)); // Set output 2 to active on a compare 2 event - T::regs() - .tim(C::raw()) - .setr(1) - .modify(|w| w.set_cmp(1, Activeeffect::SETACTIVE)); + T::regs().tim(C::raw()).setr(1).modify(|w| w.set_cmp(1, true)); // Set output 2 to inactive on a compare 3 event - T::regs() - .tim(C::raw()) - .rstr(1) - .modify(|w| w.set_cmp(2, Inactiveeffect::SETINACTIVE)); + T::regs().tim(C::raw()).rstr(1).modify(|w| w.set_cmp(2, true)); Self { timer: PhantomData, diff --git a/examples/stm32f334/src/bin/pwm.rs b/examples/stm32f334/src/bin/pwm.rs index e6d1a6c02..2b0686121 100644 --- a/examples/stm32f334/src/bin/pwm.rs +++ b/examples/stm32f334/src/bin/pwm.rs @@ -57,14 +57,14 @@ async fn main(_spawner: Spawner) { // embassy_stm32::pac::HRTIM1 // .tim(0) // .setr(0) - // .modify(|w| w.set_sst(Activeeffect::SETACTIVE)); + // .modify(|w| w.set_sst(true)); // // Timer::after_millis(500).await; // // embassy_stm32::pac::HRTIM1 // .tim(0) // .rstr(0) - // .modify(|w| w.set_srt(Inactiveeffect::SETINACTIVE)); + // .modify(|w| w.set_srt(true)); let max_duty = buck_converter.get_max_compare_value(); From e7ca6a131d50991a0cb244b652df279e972c78dc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?D=C3=A1niel=20Buga?= Date: Sat, 7 Dec 2024 18:20:09 +0100 Subject: [PATCH 299/361] Shrink task arena of tests --- tests/nrf/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/nrf/Cargo.toml b/tests/nrf/Cargo.toml index f91ab8b06..0d82f9866 100644 --- a/tests/nrf/Cargo.toml +++ b/tests/nrf/Cargo.toml @@ -9,7 +9,7 @@ teleprobe-meta = "1" embassy-futures = { version = "0.1.0", path = "../../embassy-futures" } embassy-sync = { version = "0.6.1", path = "../../embassy-sync", features = ["defmt", ] } -embassy-executor = { version = "0.6.3", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "defmt", "task-arena-size-16384", "integrated-timers"] } +embassy-executor = { version = "0.6.3", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] } embassy-time = { version = "0.3.2", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime"] } embassy-nrf = { version = "0.2.0", path = "../../embassy-nrf", features = ["defmt", "time-driver-rtc1", "gpiote", "unstable-pac"] } embedded-io-async = { version = "0.6.1", features = ["defmt-03"] } From b7a58a6d69e0d14385bdc872721afac047edd8a6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?D=C3=A1niel=20Buga?= Date: Sat, 7 Dec 2024 21:02:23 +0100 Subject: [PATCH 300/361] Keep 52840's task arena unchanged --- tests/nrf/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/nrf/Cargo.toml b/tests/nrf/Cargo.toml index 0d82f9866..766318998 100644 --- a/tests/nrf/Cargo.toml +++ b/tests/nrf/Cargo.toml @@ -33,7 +33,7 @@ portable-atomic = { version = "1.6.0" } nrf51422 = ["embassy-nrf/nrf51", "portable-atomic/unsafe-assume-single-core"] nrf52832 = ["embassy-nrf/nrf52832", "easydma"] nrf52833 = ["embassy-nrf/nrf52833", "easydma", "two-uarts"] -nrf52840 = ["embassy-nrf/nrf52840", "easydma", "two-uarts"] +nrf52840 = ["embassy-nrf/nrf52840", "easydma", "two-uarts", "embassy-executor/task-arena-size-16384"] nrf5340 = ["embassy-nrf/nrf5340-app-s", "easydma", "two-uarts"] nrf9160 = ["embassy-nrf/nrf9160-s", "easydma", "two-uarts"] From d1df927c03d837a50f49d349060e8e7b3aa511b9 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Sat, 7 Dec 2024 22:03:23 +0100 Subject: [PATCH 301/361] nrf/nfct: actually fix frame timing. Now it works both with android and iOS, and both with and without logs. --- embassy-nrf/src/nfct.rs | 21 +++++++++++++++------ 1 file changed, 15 insertions(+), 6 deletions(-) diff --git a/embassy-nrf/src/nfct.rs b/embassy-nrf/src/nfct.rs index 85866063a..cbd3920ee 100644 --- a/embassy-nrf/src/nfct.rs +++ b/embassy-nrf/src/nfct.rs @@ -212,15 +212,13 @@ impl<'d> NfcT<'d> { #[cfg(not(feature = "nrf52832"))] r.autocolresconfig().write(|w| w.0 = 0b10); + // framedelaymax=4096 is needed to make it work with phones from + // a certain company named after some fruit. + r.framedelaymin().write(|w| w.set_framedelaymin(1152)); + r.framedelaymax().write(|w| w.set_framedelaymax(4096)); r.framedelaymode().write(|w| { w.set_framedelaymode(vals::Framedelaymode::WINDOW_GRID); }); - r.framedelaymin().write(|w| { - w.set_framedelaymin(1152); - }); - r.framedelaymax().write(|w| { - w.set_framedelaymax(0xFFFF); // max - }); info!("waiting for field"); poll_fn(|cx| { @@ -269,6 +267,17 @@ impl<'d> NfcT<'d> { #[cfg(not(feature = "nrf52832"))] r.autocolresconfig().write(|w| w.0 = 0b11u32); + // once anticoll is done, set framedelaymax to the maximum possible. + // this gives the firmware as much time as possible to reply. + // higher layer still has to reply faster than the FWT it specifies in the iso14443-4 ATS, + // but that's not our concern. + // + // nrf52832 field is 16bit instead of 20bit. this seems to force a too short timeout, maybe it's a SVD bug? + #[cfg(not(feature = "nrf52832"))] + r.framedelaymax().write(|w| w.set_framedelaymax(0xF_FFFF)); + #[cfg(feature = "nrf52832")] + r.framedelaymax().write(|w| w.set_framedelaymax(0xFFFF)); + return; } } From e2e5d0f16b946303c53be549b72a5cdc082827fe Mon Sep 17 00:00:00 2001 From: Tommy Gilligan <7865781+tommy-gilligan@users.noreply.github.com> Date: Sun, 8 Dec 2024 11:52:42 +1100 Subject: [PATCH 302/361] rp23: port usb_hid_keyboard example from rp --- examples/rp23/src/bin/usb_hid_keyboard.rs | 193 ++++++++++++++++++++++ 1 file changed, 193 insertions(+) create mode 100644 examples/rp23/src/bin/usb_hid_keyboard.rs diff --git a/examples/rp23/src/bin/usb_hid_keyboard.rs b/examples/rp23/src/bin/usb_hid_keyboard.rs new file mode 100644 index 000000000..29be85a49 --- /dev/null +++ b/examples/rp23/src/bin/usb_hid_keyboard.rs @@ -0,0 +1,193 @@ +#![no_std] +#![no_main] + +use core::sync::atomic::{AtomicBool, Ordering}; + +use defmt::*; +use embassy_executor::Spawner; +use embassy_futures::join::join; +use embassy_rp::bind_interrupts; +use embassy_rp::gpio::{Input, Pull}; +use embassy_rp::block::ImageDef; +use embassy_rp::peripherals::USB; +use embassy_rp::usb::{Driver as UsbDriver, InterruptHandler}; +use embassy_usb::class::hid::{HidReaderWriter, ReportId, RequestHandler, State as HidState}; +use embassy_usb::control::OutResponse; +use embassy_usb::{Builder, Config, Handler}; +use usbd_hid::descriptor::{KeyboardReport, SerializedDescriptor}; +use {defmt_rtt as _, panic_probe as _}; + +#[link_section = ".start_block"] +#[used] +pub static IMAGE_DEF: ImageDef = ImageDef::secure_exe(); + +bind_interrupts!(struct Irqs { + USBCTRL_IRQ => InterruptHandler; +}); + +#[embassy_executor::main] +async fn main(_spawner: Spawner) { + let p = embassy_rp::init(Default::default()); + // Create the driver, from the HAL. + let driver = UsbDriver::new(p.USB, Irqs); + + // Create embassy-usb Config + let mut config = Config::new(0xc0de, 0xcafe); + config.manufacturer = Some("Embassy"); + config.product = Some("HID keyboard example"); + config.serial_number = Some("12345678"); + config.max_power = 100; + config.max_packet_size_0 = 64; + + // Create embassy-usb DeviceBuilder using the driver and config. + // It needs some buffers for building the descriptors. + let mut config_descriptor = [0; 256]; + let mut bos_descriptor = [0; 256]; + // You can also add a Microsoft OS descriptor. + let mut msos_descriptor = [0; 256]; + let mut control_buf = [0; 64]; + let mut request_handler = MyRequestHandler {}; + let mut device_handler = MyDeviceHandler::new(); + + let mut state = HidState::new(); + + let mut builder = Builder::new( + driver, + config, + &mut config_descriptor, + &mut bos_descriptor, + &mut msos_descriptor, + &mut control_buf, + ); + + builder.handler(&mut device_handler); + + // Create classes on the builder. + let config = embassy_usb::class::hid::Config { + report_descriptor: KeyboardReport::desc(), + request_handler: None, + poll_ms: 60, + max_packet_size: 64, + }; + let hid = HidReaderWriter::<_, 1, 8>::new(&mut builder, &mut state, config); + + // Build the builder. + let mut usb = builder.build(); + + // Run the USB device. + let usb_fut = usb.run(); + + // Set up the signal pin that will be used to trigger the keyboard. + let mut signal_pin = Input::new(p.PIN_16, Pull::None); + + // Enable the schmitt trigger to slightly debounce. + signal_pin.set_schmitt(true); + + let (reader, mut writer) = hid.split(); + + // Do stuff with the class! + let in_fut = async { + loop { + info!("Waiting for HIGH on pin 16"); + signal_pin.wait_for_high().await; + info!("HIGH DETECTED"); + // Create a report with the A key pressed. (no shift modifier) + let report = KeyboardReport { + keycodes: [4, 0, 0, 0, 0, 0], + leds: 0, + modifier: 0, + reserved: 0, + }; + // Send the report. + match writer.write_serialize(&report).await { + Ok(()) => {} + Err(e) => warn!("Failed to send report: {:?}", e), + }; + signal_pin.wait_for_low().await; + info!("LOW DETECTED"); + let report = KeyboardReport { + keycodes: [0, 0, 0, 0, 0, 0], + leds: 0, + modifier: 0, + reserved: 0, + }; + match writer.write_serialize(&report).await { + Ok(()) => {} + Err(e) => warn!("Failed to send report: {:?}", e), + }; + } + }; + + let out_fut = async { + reader.run(false, &mut request_handler).await; + }; + + // Run everything concurrently. + // If we had made everything `'static` above instead, we could do this using separate tasks instead. + join(usb_fut, join(in_fut, out_fut)).await; +} + +struct MyRequestHandler {} + +impl RequestHandler for MyRequestHandler { + fn get_report(&mut self, id: ReportId, _buf: &mut [u8]) -> Option { + info!("Get report for {:?}", id); + None + } + + fn set_report(&mut self, id: ReportId, data: &[u8]) -> OutResponse { + info!("Set report for {:?}: {=[u8]}", id, data); + OutResponse::Accepted + } + + fn set_idle_ms(&mut self, id: Option, dur: u32) { + info!("Set idle rate for {:?} to {:?}", id, dur); + } + + fn get_idle_ms(&mut self, id: Option) -> Option { + info!("Get idle rate for {:?}", id); + None + } +} + +struct MyDeviceHandler { + configured: AtomicBool, +} + +impl MyDeviceHandler { + fn new() -> Self { + MyDeviceHandler { + configured: AtomicBool::new(false), + } + } +} + +impl Handler for MyDeviceHandler { + fn enabled(&mut self, enabled: bool) { + self.configured.store(false, Ordering::Relaxed); + if enabled { + info!("Device enabled"); + } else { + info!("Device disabled"); + } + } + + fn reset(&mut self) { + self.configured.store(false, Ordering::Relaxed); + info!("Bus reset, the Vbus current limit is 100mA"); + } + + fn addressed(&mut self, addr: u8) { + self.configured.store(false, Ordering::Relaxed); + info!("USB address set to: {}", addr); + } + + fn configured(&mut self, configured: bool) { + self.configured.store(configured, Ordering::Relaxed); + if configured { + info!("Device configured, it may now draw up to the configured current limit from Vbus.") + } else { + info!("Device is no longer configured, the Vbus current limit is 100mA."); + } + } +} From f0be2fdce4856888bd412fe9475ae55e05cf20a2 Mon Sep 17 00:00:00 2001 From: Ulf Lilleengen Date: Mon, 9 Dec 2024 15:16:03 +0100 Subject: [PATCH 303/361] Extend tracing api to support executor id and end task Allow applications to provide a trace implementation that only needs to implement APIs used by the embassy executor, and provide more context in the event of multiple executors being used. --- embassy-executor/Cargo.toml | 6 ++- embassy-executor/src/raw/mod.rs | 52 +++++------------- embassy-executor/src/raw/trace.rs | 90 +++++++++++++++++++++++++++++++ 3 files changed, 107 insertions(+), 41 deletions(-) create mode 100644 embassy-executor/src/raw/trace.rs diff --git a/embassy-executor/Cargo.toml b/embassy-executor/Cargo.toml index 7c930b658..0a5360e5d 100644 --- a/embassy-executor/Cargo.toml +++ b/embassy-executor/Cargo.toml @@ -31,7 +31,7 @@ features = ["defmt", "arch-cortex-m", "executor-thread", "executor-interrupt"] [dependencies] defmt = { version = "0.3", optional = true } log = { version = "0.4.14", optional = true } -rtos-trace = { version = "0.1.2", optional = true } +rtos-trace = { version = "0.1.3", optional = true } embassy-executor-macros = { version = "0.6.2", path = "../embassy-executor-macros" } embassy-time-driver = { version = "0.1.0", path = "../embassy-time-driver", optional = true } @@ -91,6 +91,10 @@ arch-spin = ["_arch"] executor-thread = [] ## Enable the interrupt-mode executor (available in Cortex-M only) executor-interrupt = [] +## Enable tracing support (adds some overhead) +trace = [] +## Enable support for rtos-trace framework +rtos-trace = ["dep:rtos-trace", "trace"] #! ### Task Arena Size #! Sets the [task arena](#task-arena) size. Necessary if you’re not using `nightly`. diff --git a/embassy-executor/src/raw/mod.rs b/embassy-executor/src/raw/mod.rs index ebabee1ba..3f93eae6f 100644 --- a/embassy-executor/src/raw/mod.rs +++ b/embassy-executor/src/raw/mod.rs @@ -18,6 +18,8 @@ mod state; #[cfg(feature = "integrated-timers")] mod timer_queue; +#[cfg(feature = "trace")] +mod trace; pub(crate) mod util; #[cfg_attr(feature = "turbowakers", path = "waker_turbo.rs")] mod waker; @@ -31,8 +33,6 @@ use core::task::{Context, Poll}; #[cfg(feature = "integrated-timers")] use embassy_time_driver::AlarmHandle; -#[cfg(feature = "rtos-trace")] -use rtos_trace::trace; use self::run_queue::{RunQueue, RunQueueItem}; use self::state::State; @@ -352,8 +352,8 @@ impl SyncExecutor { /// - `task` must NOT be already enqueued (in this executor or another one). #[inline(always)] unsafe fn enqueue(&self, task: TaskRef) { - #[cfg(feature = "rtos-trace")] - trace::task_ready_begin(task.as_ptr() as u32); + #[cfg(feature = "trace")] + trace::task_ready_begin(self, &task); if self.run_queue.enqueue(task) { self.pender.pend(); @@ -369,8 +369,8 @@ impl SyncExecutor { pub(super) unsafe fn spawn(&'static self, task: TaskRef) { task.header().executor.set(Some(self)); - #[cfg(feature = "rtos-trace")] - trace::task_new(task.as_ptr() as u32); + #[cfg(feature = "trace")] + trace::task_new(self, &task); self.enqueue(task); } @@ -400,14 +400,14 @@ impl SyncExecutor { return; } - #[cfg(feature = "rtos-trace")] - trace::task_exec_begin(p.as_ptr() as u32); + #[cfg(feature = "trace")] + trace::task_exec_begin(self, &p); // Run the task task.poll_fn.get().unwrap_unchecked()(p); - #[cfg(feature = "rtos-trace")] - trace::task_exec_end(); + #[cfg(feature = "trace")] + trace::task_exec_end(self, &p); // Enqueue or update into timer_queue #[cfg(feature = "integrated-timers")] @@ -430,8 +430,8 @@ impl SyncExecutor { } } - #[cfg(feature = "rtos-trace")] - trace::system_idle(); + #[cfg(feature = "trace")] + trace::executor_idle(self) } } @@ -593,31 +593,3 @@ impl embassy_time_queue_driver::TimerQueue for TimerQueue { #[cfg(feature = "integrated-timers")] embassy_time_queue_driver::timer_queue_impl!(static TIMER_QUEUE: TimerQueue = TimerQueue); - -#[cfg(all(feature = "rtos-trace", feature = "integrated-timers"))] -const fn gcd(a: u64, b: u64) -> u64 { - if b == 0 { - a - } else { - gcd(b, a % b) - } -} - -#[cfg(feature = "rtos-trace")] -impl rtos_trace::RtosTraceOSCallbacks for Executor { - fn task_list() { - // We don't know what tasks exist, so we can't send them. - } - #[cfg(feature = "integrated-timers")] - fn time() -> u64 { - const GCD_1M: u64 = gcd(embassy_time_driver::TICK_HZ, 1_000_000); - embassy_time_driver::now() * (1_000_000 / GCD_1M) / (embassy_time_driver::TICK_HZ / GCD_1M) - } - #[cfg(not(feature = "integrated-timers"))] - fn time() -> u64 { - 0 - } -} - -#[cfg(feature = "rtos-trace")] -rtos_trace::global_os_callbacks! {Executor} diff --git a/embassy-executor/src/raw/trace.rs b/embassy-executor/src/raw/trace.rs new file mode 100644 index 000000000..c7bcf9c11 --- /dev/null +++ b/embassy-executor/src/raw/trace.rs @@ -0,0 +1,90 @@ +#![allow(unused)] +use crate::raw::{SyncExecutor, TaskRef}; + +#[cfg(not(feature = "rtos-trace"))] +extern "Rust" { + fn _embassy_trace_task_new(executor_id: u32, task_id: u32); + fn _embassy_trace_task_exec_begin(executor_id: u32, task_id: u32); + fn _embassy_trace_task_exec_end(excutor_id: u32, task_id: u32); + fn _embassy_trace_task_ready_begin(executor_id: u32, task_id: u32); + fn _embassy_trace_executor_idle(executor_id: u32); +} + +#[inline] +pub(crate) fn task_new(executor: &SyncExecutor, task: &TaskRef) { + #[cfg(not(feature = "rtos-trace"))] + unsafe { + _embassy_trace_task_new(executor as *const _ as u32, task.as_ptr() as u32) + } + + #[cfg(feature = "rtos-trace")] + rtos_trace::trace::task_new(task.as_ptr() as u32); +} + +#[inline] +pub(crate) fn task_ready_begin(executor: &SyncExecutor, task: &TaskRef) { + #[cfg(not(feature = "rtos-trace"))] + unsafe { + _embassy_trace_task_ready_begin(executor as *const _ as u32, task.as_ptr() as u32) + } + #[cfg(feature = "rtos-trace")] + rtos_trace::trace::task_ready_begin(task.as_ptr() as u32); +} + +#[inline] +pub(crate) fn task_exec_begin(executor: &SyncExecutor, task: &TaskRef) { + #[cfg(not(feature = "rtos-trace"))] + unsafe { + _embassy_trace_task_exec_begin(executor as *const _ as u32, task.as_ptr() as u32) + } + #[cfg(feature = "rtos-trace")] + rtos_trace::trace::task_exec_begin(task.as_ptr() as u32); +} + +#[inline] +pub(crate) fn task_exec_end(executor: &SyncExecutor, task: &TaskRef) { + #[cfg(not(feature = "rtos-trace"))] + unsafe { + _embassy_trace_task_exec_end(executor as *const _ as u32, task.as_ptr() as u32) + } + #[cfg(feature = "rtos-trace")] + rtos_trace::trace::task_exec_end(); +} + +#[inline] +pub(crate) fn executor_idle(executor: &SyncExecutor) { + #[cfg(not(feature = "rtos-trace"))] + unsafe { + _embassy_trace_executor_idle(executor as *const _ as u32) + } + #[cfg(feature = "rtos-trace")] + rtos_trace::trace::system_idle(); +} + +#[cfg(all(feature = "rtos-trace", feature = "integrated-timers"))] +const fn gcd(a: u64, b: u64) -> u64 { + if b == 0 { + a + } else { + gcd(b, a % b) + } +} + +#[cfg(feature = "rtos-trace")] +impl rtos_trace::RtosTraceOSCallbacks for crate::raw::SyncExecutor { + fn task_list() { + // We don't know what tasks exist, so we can't send them. + } + #[cfg(feature = "integrated-timers")] + fn time() -> u64 { + const GCD_1M: u64 = gcd(embassy_time_driver::TICK_HZ, 1_000_000); + embassy_time_driver::now() * (1_000_000 / GCD_1M) / (embassy_time_driver::TICK_HZ / GCD_1M) + } + #[cfg(not(feature = "integrated-timers"))] + fn time() -> u64 { + 0 + } +} + +#[cfg(feature = "rtos-trace")] +rtos_trace::global_os_callbacks! {SyncExecutor} From 30ba7b85df4329ab22588628907bceac89318051 Mon Sep 17 00:00:00 2001 From: Tommy Gilligan <7865781+tommy-gilligan@users.noreply.github.com> Date: Tue, 10 Dec 2024 06:27:22 +1100 Subject: [PATCH 304/361] cargo +nightly fmt --- examples/rp23/src/bin/usb_hid_keyboard.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/rp23/src/bin/usb_hid_keyboard.rs b/examples/rp23/src/bin/usb_hid_keyboard.rs index 29be85a49..ec1e88746 100644 --- a/examples/rp23/src/bin/usb_hid_keyboard.rs +++ b/examples/rp23/src/bin/usb_hid_keyboard.rs @@ -7,8 +7,8 @@ use defmt::*; use embassy_executor::Spawner; use embassy_futures::join::join; use embassy_rp::bind_interrupts; -use embassy_rp::gpio::{Input, Pull}; use embassy_rp::block::ImageDef; +use embassy_rp::gpio::{Input, Pull}; use embassy_rp::peripherals::USB; use embassy_rp::usb::{Driver as UsbDriver, InterruptHandler}; use embassy_usb::class::hid::{HidReaderWriter, ReportId, RequestHandler, State as HidState}; From 0b9cdd86ab398ddbc96f6859c6a33f55c788fbc6 Mon Sep 17 00:00:00 2001 From: Gerhard de Clercq <11624490+Gerharddc@users.noreply.github.com> Date: Tue, 10 Dec 2024 09:21:37 +0100 Subject: [PATCH 305/361] embassy-usb-dfu: use correct function descriptors This should allow things to work properly even when IADs are used. --- embassy-usb-dfu/src/dfu.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/embassy-usb-dfu/src/dfu.rs b/embassy-usb-dfu/src/dfu.rs index abd929a9e..c23286cf5 100644 --- a/embassy-usb-dfu/src/dfu.rs +++ b/embassy-usb-dfu/src/dfu.rs @@ -189,7 +189,7 @@ pub fn usb_dfu<'d, D: Driver<'d>, DFU: NorFlash, STATE: NorFlash, RST: Reset, co builder: &mut Builder<'d, D>, handler: &'d mut Control<'d, DFU, STATE, RST, BLOCK_SIZE>, ) { - let mut func = builder.function(0x00, 0x00, 0x00); + let mut func = builder.function(USB_CLASS_APPN_SPEC, APPN_SPEC_SUBCLASS_DFU, DFU_PROTOCOL_DFU); let mut iface = func.interface(); let mut alt = iface.alt_setting(USB_CLASS_APPN_SPEC, APPN_SPEC_SUBCLASS_DFU, DFU_PROTOCOL_DFU, None); alt.descriptor( From 5963a10d4d541ef93e42ccfe339aa9aa343f2914 Mon Sep 17 00:00:00 2001 From: Gerhard de Clercq <11624490+Gerharddc@users.noreply.github.com> Date: Tue, 10 Dec 2024 09:31:47 +0100 Subject: [PATCH 306/361] stm32wb-dfu: add MSOS headers The USB DFU example now shows how to automatically get the WinUSB driver assigned. --- .../boot/bootloader/stm32wb-dfu/src/main.rs | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/examples/boot/bootloader/stm32wb-dfu/src/main.rs b/examples/boot/bootloader/stm32wb-dfu/src/main.rs index 093b39f9d..b09d53cf0 100644 --- a/examples/boot/bootloader/stm32wb-dfu/src/main.rs +++ b/examples/boot/bootloader/stm32wb-dfu/src/main.rs @@ -12,7 +12,7 @@ use embassy_stm32::rcc::WPAN_DEFAULT; use embassy_stm32::usb::Driver; use embassy_stm32::{bind_interrupts, peripherals, usb}; use embassy_sync::blocking_mutex::Mutex; -use embassy_usb::Builder; +use embassy_usb::{msos, Builder}; use embassy_usb_dfu::consts::DfuAttributes; use embassy_usb_dfu::{usb_dfu, Control, ResetImmediate}; @@ -20,6 +20,9 @@ bind_interrupts!(struct Irqs { USB_LP => usb::InterruptHandler; }); +// This is a randomly generated GUID to allow clients on Windows to find our device +const DEVICE_INTERFACE_GUIDS: &[&str] = &["{EAA9A5DC-30BA-44BC-9232-606CDC875321}"]; + #[entry] fn main() -> ! { let mut config = embassy_stm32::Config::default(); @@ -62,6 +65,18 @@ fn main() -> ! { &mut control_buf, ); + // We add MSOS headers so that the device automatically gets assigned the WinUSB driver on Windows. + // Otherwise users need to do this manually using a tool like Zadig. + // + // It seems it is important for the DFU class that these headers be on the Device level. + // + builder.msos_descriptor(msos::windows_version::WIN8_1, 2); + builder.msos_feature(msos::CompatibleIdFeatureDescriptor::new("WINUSB", "")); + builder.msos_feature(msos::RegistryPropertyFeatureDescriptor::new( + "DeviceInterfaceGUIDs", + msos::PropertyData::RegMultiSz(DEVICE_INTERFACE_GUIDS), + )); + usb_dfu::<_, _, _, ResetImmediate, 4096>(&mut builder, &mut state); let mut dev = builder.build(); From 501d3942e8b6b04fd1cb4c14728ba4c2e118d8b5 Mon Sep 17 00:00:00 2001 From: Dave Marples Date: Thu, 5 Dec 2024 16:35:56 +0000 Subject: [PATCH 307/361] Add support for stm32u595/5a5 OTG_HS in client mode --- embassy-stm32/src/usb/mod.rs | 20 ++++ embassy-stm32/src/usb/otg.rs | 32 ++++-- examples/stm32u5/src/bin/usb_hs_serial.rs | 129 ++++++++++++++++++++++ 3 files changed, 174 insertions(+), 7 deletions(-) create mode 100644 examples/stm32u5/src/bin/usb_hs_serial.rs diff --git a/embassy-stm32/src/usb/mod.rs b/embassy-stm32/src/usb/mod.rs index a473285bf..9737b9b3f 100644 --- a/embassy-stm32/src/usb/mod.rs +++ b/embassy-stm32/src/usb/mod.rs @@ -26,6 +26,7 @@ fn common_init() { // Clock might not be exact 48Mhz due to rounding errors in PLL calculation, or if the user // has tight clock restrictions due to something else (like audio). #[cfg(not(stm32h7rs))] + #[cfg(not(all(stm32u5, peri_usb_otg_hs)))] if freq.0.abs_diff(48_000_000) > 120_000 { panic!( "USB clock should be 48Mhz but is {} Hz. Please double-check your RCC settings.", @@ -33,6 +34,15 @@ fn common_init() { ) } + // For OTG-HS on STM32U5 only the 32MHz clock is fast enough (Table 762, Sect 73.14.4) + #[cfg(all(stm32u5, peri_usb_otg_hs))] + if freq.0.abs_diff(32_000_000) > 120_000 { + panic!( + "USB clock should be 32Mhz but is {} Hz. Please double-check your RCC settings.", + freq.0 + ) + } + #[cfg(any(stm32l4, stm32l5, stm32wb, stm32u0))] critical_section::with(|_| crate::pac::PWR.cr2().modify(|w| w.set_usv(true))); @@ -90,6 +100,16 @@ fn common_init() { // Wait for USB power to stabilize while !crate::pac::PWR.svmsr().read().vddusbrdy() {} + + // Now set up transceiver power if it's a OTG-HS + #[cfg(peri_usb_otg_hs)] + { + crate::pac::PWR.vosr().modify(|w| { + w.set_usbpwren(true); + w.set_usbboosten(true); + }); + while !crate::pac::PWR.vosr().read().usbboostrdy() {} + } } T::Interrupt::unpend(); diff --git a/embassy-stm32/src/usb/otg.rs b/embassy-stm32/src/usb/otg.rs index 6ca28b156..d1b38a558 100644 --- a/embassy-stm32/src/usb/otg.rs +++ b/embassy-stm32/src/usb/otg.rs @@ -26,7 +26,6 @@ impl interrupt::typelevel::Handler for InterruptHandl unsafe fn on_interrupt() { let r = T::regs(); let state = T::state(); - on_interrupt_impl(r, state, T::ENDPOINT_COUNT); } } @@ -103,15 +102,18 @@ impl<'d, T: Instance> Driver<'d, T> { pub fn new_hs( _peri: impl Peripheral

+ 'd, _irq: impl interrupt::typelevel::Binding> + 'd, - dp: impl Peripheral

> + 'd, - dm: impl Peripheral

> + 'd, + _dp: impl Peripheral

> + 'd, + _dm: impl Peripheral

> + 'd, ep_out_buffer: &'d mut [u8], config: Config, ) -> Self { - into_ref!(dp, dm); - - dp.set_as_af(dp.af_num(), AfType::output(OutputType::PushPull, Speed::VeryHigh)); - dm.set_as_af(dm.af_num(), AfType::output(OutputType::PushPull, Speed::VeryHigh)); + // For STM32U5 High speed pins need to be left in analog mode + #[cfg(not(all(stm32u5, peri_usb_otg_hs)))] + { + into_ref!(_dp, _dm); + _dp.set_as_af(_dp.af_num(), AfType::output(OutputType::PushPull, Speed::VeryHigh)); + _dm.set_as_af(_dm.af_num(), AfType::output(OutputType::PushPull, Speed::VeryHigh)); + } let instance = OtgInstance { regs: T::regs(), @@ -311,6 +313,22 @@ impl<'d, T: Instance> Bus<'d, T> { }); }); + #[cfg(all(stm32u5, peri_usb_otg_hs))] + { + // Only the 32MHz clock is suitable here, which the magic number represents + crate::pac::SYSCFG.otghsphycr().modify(|w| { + w.set_clksel(11); + w.set_en(true); + }); + + critical_section::with(|_| { + crate::pac::RCC.ahb2enr1().modify(|w| { + w.set_usb_otg_hsen(true); + w.set_usb_otg_hs_phyen(true); + }); + }); + } + let r = T::regs(); let core_id = r.cid().read().0; trace!("Core id {:08x}", core_id); diff --git a/examples/stm32u5/src/bin/usb_hs_serial.rs b/examples/stm32u5/src/bin/usb_hs_serial.rs new file mode 100644 index 000000000..5549e2cbb --- /dev/null +++ b/examples/stm32u5/src/bin/usb_hs_serial.rs @@ -0,0 +1,129 @@ +#![no_std] +#![no_main] + +use defmt::{panic, *}; +use defmt_rtt as _; // global logger +use embassy_executor::Spawner; +use embassy_futures::join::join; +use embassy_stm32::usb::{Driver, Instance}; +use embassy_stm32::{bind_interrupts, peripherals, usb, Config}; +use embassy_usb::class::cdc_acm::{CdcAcmClass, State}; +use embassy_usb::driver::EndpointError; +use embassy_usb::Builder; +use panic_probe as _; + +bind_interrupts!(struct Irqs { + OTG_HS => usb::InterruptHandler; +}); + +#[embassy_executor::main] +async fn main(_spawner: Spawner) { + info!("Hello World!"); + + let mut config = Config::default(); + { + use embassy_stm32::rcc::*; + use embassy_stm32::time::Hertz; + config.rcc.hse = Some(Hse { + freq: Hertz(16_000_000), + mode: HseMode::Oscillator, + }); + config.rcc.pll1 = Some(Pll { + source: PllSource::HSE, + prediv: PllPreDiv::DIV2, // HSE / 2 = 8MHz + mul: PllMul::MUL60, // 8MHz * 60 = 480MHz + divr: Some(PllDiv::DIV3), // 480MHz / 3 = 160MHz (sys_ck) + divq: Some(PllDiv::DIV10), // 480MHz / 10 = 48MHz (USB) + divp: Some(PllDiv::DIV15), // 480MHz / 15 = 32MHz (USBOTG) + }); + config.rcc.mux.otghssel = mux::Otghssel::PLL1_P; + config.rcc.voltage_range = VoltageScale::RANGE1; + config.rcc.sys = Sysclk::PLL1_R; + } + + let p = embassy_stm32::init(config); + + // Create the driver, from the HAL. + let mut ep_out_buffer = [0u8; 256]; + let mut config = embassy_stm32::usb::Config::default(); + // Do not enable vbus_detection. This is a safe default that works in all boards. + // However, if your USB device is self-powered (can stay powered on if USB is unplugged), you need + // to enable vbus_detection to comply with the USB spec. If you enable it, the board + // has to support it or USB won't work at all. See docs on `vbus_detection` for details. + config.vbus_detection = false; + let driver = Driver::new_hs(p.USB_OTG_HS, Irqs, p.PA12, p.PA11, &mut ep_out_buffer, config); + + // Create embassy-usb Config + let mut config = embassy_usb::Config::new(0xc0de, 0xcafe); + config.manufacturer = Some("Embassy"); + config.product = Some("USB-serial example"); + config.serial_number = Some("12345678"); + + // Required for windows compatibility. + // https://developer.nordicsemi.com/nRF_Connect_SDK/doc/1.9.1/kconfig/CONFIG_CDC_ACM_IAD.html#help + config.device_class = 0xEF; + config.device_sub_class = 0x02; + config.device_protocol = 0x01; + config.composite_with_iads = true; + + // Create embassy-usb DeviceBuilder using the driver and config. + // It needs some buffers for building the descriptors. + let mut config_descriptor = [0; 256]; + let mut bos_descriptor = [0; 256]; + let mut control_buf = [0; 64]; + + let mut state = State::new(); + + let mut builder = Builder::new( + driver, + config, + &mut config_descriptor, + &mut bos_descriptor, + &mut [], // no msos descriptors + &mut control_buf, + ); + + // Create classes on the builder. + let mut class = CdcAcmClass::new(&mut builder, &mut state, 64); + + // Build the builder. + let mut usb = builder.build(); + + // Run the USB device. + let usb_fut = usb.run(); + + // Do stuff with the class! + let echo_fut = async { + loop { + class.wait_connection().await; + info!("Connected"); + let _ = echo(&mut class).await; + info!("Disconnected"); + } + }; + + // Run everything concurrently. + // If we had made everything `'static` above instead, we could do this using separate tasks instead. + join(usb_fut, echo_fut).await; +} + +struct Disconnected {} + +impl From for Disconnected { + fn from(val: EndpointError) -> Self { + match val { + EndpointError::BufferOverflow => panic!("Buffer overflow"), + EndpointError::Disabled => Disconnected {}, + } + } +} + +async fn echo<'d, T: Instance + 'd>(class: &mut CdcAcmClass<'d, Driver<'d, T>>) -> Result<(), Disconnected> { + let mut buf = [0; 64]; + loop { + let n = class.read_packet(&mut buf).await?; + let data = &buf[..n]; + info!("data: {:x}", data); + class.write_packet(data).await?; + } +} From a0e056a629c166b38c536c68afbf852819f10143 Mon Sep 17 00:00:00 2001 From: Marvin Drees Date: Fri, 6 Dec 2024 16:22:08 +0100 Subject: [PATCH 308/361] Update STM32U5 OTG HS clock handling Signed-off-by: Marvin Drees --- embassy-stm32/src/rcc/u5.rs | 29 +++++++++++++++++++++++++++++ embassy-stm32/src/usb/mod.rs | 14 ++------------ embassy-stm32/src/usb/otg.rs | 2 -- 3 files changed, 31 insertions(+), 14 deletions(-) diff --git a/embassy-stm32/src/rcc/u5.rs b/embassy-stm32/src/rcc/u5.rs index af99c77bc..dc77dc540 100644 --- a/embassy-stm32/src/rcc/u5.rs +++ b/embassy-stm32/src/rcc/u5.rs @@ -1,9 +1,13 @@ pub use crate::pac::pwr::vals::Vos as VoltageScale; +#[cfg(all(peri_usb_otg_hs))] +pub use crate::pac::rcc::vals::Otghssel; pub use crate::pac::rcc::vals::{ Hpre as AHBPrescaler, Msirange, Msirange as MSIRange, Plldiv as PllDiv, Pllm as PllPreDiv, Plln as PllMul, Pllsrc as PllSource, Ppre as APBPrescaler, Sw as Sysclk, }; use crate::pac::rcc::vals::{Hseext, Msirgsel, Pllmboost, Pllrge}; +#[cfg(all(peri_usb_otg_hs))] +pub use crate::pac::{syscfg::vals::Usbrefcksel, SYSCFG}; use crate::pac::{FLASH, PWR, RCC}; use crate::rcc::LSI_FREQ; use crate::time::Hertz; @@ -295,6 +299,31 @@ pub(crate) unsafe fn init(config: Config) { let rtc = config.ls.init(); + #[cfg(all(stm32u5, peri_usb_otg_hs))] + let usb_refck = match config.mux.otghssel { + Otghssel::HSE => hse, + Otghssel::HSE_DIV_2 => hse.map(|hse_val| hse_val / 2u8), + Otghssel::PLL1_P => pll1.p, + Otghssel::PLL1_P_DIV_2 => pll1.p.map(|pll1p_val| pll1p_val / 2u8), + }; + #[cfg(all(stm32u5, peri_usb_otg_hs))] + let usb_refck_sel = match usb_refck { + Some(clk_val) => match clk_val { + Hertz(16_000_000) => Usbrefcksel::MHZ16, + Hertz(19_200_000) => Usbrefcksel::MHZ19_2, + Hertz(20_000_000) => Usbrefcksel::MHZ20, + Hertz(24_000_000) => Usbrefcksel::MHZ24, + Hertz(26_000_000) => Usbrefcksel::MHZ26, + Hertz(32_000_000) => Usbrefcksel::MHZ32, + _ => panic!("cannot select OTG_HS reference clock with source frequency of {} Hz, must be one of 16, 19.2, 20, 24, 26, 32 MHz", clk_val), + }, + None => Usbrefcksel::MHZ24, + }; + #[cfg(all(stm32u5, peri_usb_otg_hs))] + SYSCFG.otghsphycr().modify(|w| { + w.set_clksel(usb_refck_sel); + }); + let lse = config.ls.lse.map(|l| l.frequency); let lsi = config.ls.lsi.then_some(LSI_FREQ); diff --git a/embassy-stm32/src/usb/mod.rs b/embassy-stm32/src/usb/mod.rs index 9737b9b3f..ae5963420 100644 --- a/embassy-stm32/src/usb/mod.rs +++ b/embassy-stm32/src/usb/mod.rs @@ -15,7 +15,7 @@ fn common_init() { let freq = T::frequency(); // On the H7RS, the USBPHYC embeds a PLL accepting one of the input frequencies listed below and providing 48MHz to OTG_FS and 60MHz to OTG_HS internally - #[cfg(stm32h7rs)] + #[cfg(any(stm32h7rs, all(stm32u5, peri_usb_otg_hs)))] if ![16_000_000, 19_200_000, 20_000_000, 24_000_000, 26_000_000, 32_000_000].contains(&freq.0) { panic!( "USB clock should be one of 16, 19.2, 20, 24, 26, 32Mhz but is {} Hz. Please double-check your RCC settings.", @@ -25,8 +25,7 @@ fn common_init() { // Check frequency is within the 0.25% tolerance allowed by the spec. // Clock might not be exact 48Mhz due to rounding errors in PLL calculation, or if the user // has tight clock restrictions due to something else (like audio). - #[cfg(not(stm32h7rs))] - #[cfg(not(all(stm32u5, peri_usb_otg_hs)))] + #[cfg(not(any(stm32h7rs, all(stm32u5, peri_usb_otg_hs))))] if freq.0.abs_diff(48_000_000) > 120_000 { panic!( "USB clock should be 48Mhz but is {} Hz. Please double-check your RCC settings.", @@ -34,15 +33,6 @@ fn common_init() { ) } - // For OTG-HS on STM32U5 only the 32MHz clock is fast enough (Table 762, Sect 73.14.4) - #[cfg(all(stm32u5, peri_usb_otg_hs))] - if freq.0.abs_diff(32_000_000) > 120_000 { - panic!( - "USB clock should be 32Mhz but is {} Hz. Please double-check your RCC settings.", - freq.0 - ) - } - #[cfg(any(stm32l4, stm32l5, stm32wb, stm32u0))] critical_section::with(|_| crate::pac::PWR.cr2().modify(|w| w.set_usv(true))); diff --git a/embassy-stm32/src/usb/otg.rs b/embassy-stm32/src/usb/otg.rs index d1b38a558..d3c7978e4 100644 --- a/embassy-stm32/src/usb/otg.rs +++ b/embassy-stm32/src/usb/otg.rs @@ -315,9 +315,7 @@ impl<'d, T: Instance> Bus<'d, T> { #[cfg(all(stm32u5, peri_usb_otg_hs))] { - // Only the 32MHz clock is suitable here, which the magic number represents crate::pac::SYSCFG.otghsphycr().modify(|w| { - w.set_clksel(11); w.set_en(true); }); From 1e2cbeae68d12a37ff73292294bb96c9282476bd Mon Sep 17 00:00:00 2001 From: ROMemories <152802150+ROMemories@users.noreply.github.com> Date: Tue, 10 Dec 2024 14:17:53 +0100 Subject: [PATCH 309/361] fix(embassy-net): make the `Config` constructors `const` --- embassy-net/src/lib.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/embassy-net/src/lib.rs b/embassy-net/src/lib.rs index ec7f10fdd..831e01d44 100644 --- a/embassy-net/src/lib.rs +++ b/embassy-net/src/lib.rs @@ -180,7 +180,7 @@ pub struct Config { impl Config { /// IPv4 configuration with static addressing. #[cfg(feature = "proto-ipv4")] - pub fn ipv4_static(config: StaticConfigV4) -> Self { + pub const fn ipv4_static(config: StaticConfigV4) -> Self { Self { ipv4: ConfigV4::Static(config), #[cfg(feature = "proto-ipv6")] @@ -190,7 +190,7 @@ impl Config { /// IPv6 configuration with static addressing. #[cfg(feature = "proto-ipv6")] - pub fn ipv6_static(config: StaticConfigV6) -> Self { + pub const fn ipv6_static(config: StaticConfigV6) -> Self { Self { #[cfg(feature = "proto-ipv4")] ipv4: ConfigV4::None, @@ -206,7 +206,7 @@ impl Config { /// let _cfg = Config::dhcpv4(Default::default()); /// ``` #[cfg(feature = "dhcpv4")] - pub fn dhcpv4(config: DhcpConfig) -> Self { + pub const fn dhcpv4(config: DhcpConfig) -> Self { Self { ipv4: ConfigV4::Dhcp(config), #[cfg(feature = "proto-ipv6")] From cfe6bc172404131cb03c3078cc6d91b1dd5a8014 Mon Sep 17 00:00:00 2001 From: Gabrael Levine Date: Tue, 10 Dec 2024 11:06:28 -0800 Subject: [PATCH 310/361] Add missing opamp external outputs for STM32G4 --- embassy-stm32/src/opamp.rs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/embassy-stm32/src/opamp.rs b/embassy-stm32/src/opamp.rs index c86c18e22..d1c53a740 100644 --- a/embassy-stm32/src/opamp.rs +++ b/embassy-stm32/src/opamp.rs @@ -251,10 +251,12 @@ foreach_peripheral!( impl_opamp_external_output!(OPAMP2, ADC2, 3); }; (opamp, OPAMP3) => { + impl_opamp_external_output!(OPAMP3, ADC1, 12); impl_opamp_external_output!(OPAMP3, ADC3, 1); }; // OPAMP4 only in STM32G4 Cat 3 devices (opamp, OPAMP4) => { + impl_opamp_external_output!(OPAMP4, ADC1, 11); impl_opamp_external_output!(OPAMP4, ADC4, 3); }; // OPAMP5 only in STM32G4 Cat 3 devices @@ -264,6 +266,7 @@ foreach_peripheral!( // OPAMP6 only in STM32G4 Cat 3/4 devices (opamp, OPAMP6) => { impl_opamp_external_output!(OPAMP6, ADC1, 14); + impl_opamp_external_output!(OPAMP6, ADC2, 14); }; ); From 5a5495aac43d75610735f2ca80fb6c8e8f31ed71 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?D=C3=A1niel=20Buga?= Date: Tue, 26 Nov 2024 23:54:21 +0100 Subject: [PATCH 311/361] Refactor integrated-timers --- .github/ci/test.sh | 2 +- ci-xtensa.sh | 4 +- ci.sh | 4 +- embassy-executor/Cargo.toml | 2 +- embassy-executor/src/arch/avr.rs | 4 - embassy-executor/src/arch/cortex_m.rs | 6 - embassy-executor/src/arch/riscv32.rs | 4 - embassy-executor/src/arch/spin.rs | 4 - embassy-executor/src/arch/std.rs | 4 - embassy-executor/src/arch/wasm.rs | 4 - embassy-executor/src/raw/mod.rs | 135 ++----- embassy-executor/src/raw/timer_queue.rs | 89 +++-- embassy-executor/src/raw/util.rs | 5 + embassy-executor/tests/test.rs | 3 - embassy-nrf/Cargo.toml | 3 +- embassy-nrf/src/time_driver.rs | 117 ++---- embassy-rp/Cargo.toml | 3 +- embassy-rp/src/time_driver.rs | 130 ++----- embassy-stm32/Cargo.toml | 4 +- embassy-stm32/src/low_power.rs | 3 - embassy-stm32/src/time_driver.rs | 154 +++----- embassy-time-driver/src/lib.rs | 135 +------ embassy-time-queue-driver/Cargo.toml | 33 ++ embassy-time-queue-driver/src/lib.rs | 136 ++++++- .../src/queue_generic.rs | 146 ++++++++ embassy-time/Cargo.toml | 24 -- embassy-time/src/driver_mock.rs | 85 ++--- embassy-time/src/driver_std.rs | 119 ++---- embassy-time/src/driver_wasm.rs | 80 ++-- embassy-time/src/lib.rs | 2 - embassy-time/src/queue_generic.rs | 346 ------------------ examples/nrf52840-rtic/Cargo.toml | 3 +- 32 files changed, 611 insertions(+), 1182 deletions(-) create mode 100644 embassy-time-queue-driver/src/queue_generic.rs delete mode 100644 embassy-time/src/queue_generic.rs diff --git a/.github/ci/test.sh b/.github/ci/test.sh index 0fe088bfe..285f3f29e 100755 --- a/.github/ci/test.sh +++ b/.github/ci/test.sh @@ -17,7 +17,7 @@ cargo test --manifest-path ./embassy-futures/Cargo.toml cargo test --manifest-path ./embassy-sync/Cargo.toml cargo test --manifest-path ./embassy-embedded-hal/Cargo.toml cargo test --manifest-path ./embassy-hal-internal/Cargo.toml -cargo test --manifest-path ./embassy-time/Cargo.toml --features generic-queue,mock-driver +cargo test --manifest-path ./embassy-time/Cargo.toml --features mock-driver cargo test --manifest-path ./embassy-time-driver/Cargo.toml cargo test --manifest-path ./embassy-boot/Cargo.toml diff --git a/ci-xtensa.sh b/ci-xtensa.sh index 32d362def..2cac7444c 100755 --- a/ci-xtensa.sh +++ b/ci-xtensa.sh @@ -24,7 +24,9 @@ cargo batch \ --- build --release --manifest-path embassy-executor/Cargo.toml --target xtensa-esp32-none-elf --features arch-spin,executor-thread \ --- build --release --manifest-path embassy-executor/Cargo.toml --target xtensa-esp32-none-elf --features arch-spin,executor-thread,integrated-timers \ --- build --release --manifest-path embassy-sync/Cargo.toml --target xtensa-esp32s2-none-elf --features defmt \ - --- build --release --manifest-path embassy-time/Cargo.toml --target xtensa-esp32s2-none-elf --features defmt,defmt-timestamp-uptime,generic-queue-8,mock-driver \ + --- build --release --manifest-path embassy-time/Cargo.toml --target xtensa-esp32s2-none-elf --features defmt,defmt-timestamp-uptime,mock-driver \ + --- build --release --manifest-path embassy-time-queue-driver/Cargo.toml --target xtensa-esp32s2-none-elf --features integrated-timers \ + --- build --release --manifest-path embassy-time-queue-driver/Cargo.toml --target xtensa-esp32s2-none-elf --features generic-queue-8 \ --- build --release --manifest-path embassy-net/Cargo.toml --target xtensa-esp32-none-elf --features defmt,tcp,udp,dns,proto-ipv4,medium-ethernet,packet-trace \ --- build --release --manifest-path embassy-net/Cargo.toml --target xtensa-esp32-none-elf --features defmt,tcp,udp,dns,proto-ipv4,multicast,medium-ethernet \ --- build --release --manifest-path embassy-net/Cargo.toml --target xtensa-esp32-none-elf --features defmt,tcp,udp,dns,dhcpv4,medium-ethernet \ diff --git a/ci.sh b/ci.sh index 307e268c4..71b862632 100755 --- a/ci.sh +++ b/ci.sh @@ -45,7 +45,9 @@ cargo batch \ --- build --release --manifest-path embassy-executor/Cargo.toml --target riscv32imac-unknown-none-elf --features arch-riscv32,executor-thread \ --- build --release --manifest-path embassy-executor/Cargo.toml --target riscv32imac-unknown-none-elf --features arch-riscv32,executor-thread,integrated-timers \ --- build --release --manifest-path embassy-sync/Cargo.toml --target thumbv6m-none-eabi --features defmt \ - --- build --release --manifest-path embassy-time/Cargo.toml --target thumbv6m-none-eabi --features defmt,defmt-timestamp-uptime,generic-queue-8,mock-driver \ + --- build --release --manifest-path embassy-time/Cargo.toml --target thumbv6m-none-eabi --features defmt,defmt-timestamp-uptime,mock-driver \ + --- build --release --manifest-path embassy-time-queue-driver/Cargo.toml --target thumbv6m-none-eabi --features integrated-timers \ + --- build --release --manifest-path embassy-time-queue-driver/Cargo.toml --target thumbv6m-none-eabi --features generic-queue-8 \ --- build --release --manifest-path embassy-net/Cargo.toml --target thumbv7em-none-eabi --features defmt,tcp,udp,dns,proto-ipv4,medium-ethernet,packet-trace \ --- build --release --manifest-path embassy-net/Cargo.toml --target thumbv7em-none-eabi --features defmt,tcp,udp,dns,proto-ipv4,multicast,medium-ethernet \ --- build --release --manifest-path embassy-net/Cargo.toml --target thumbv7em-none-eabi --features defmt,tcp,udp,dns,dhcpv4,medium-ethernet \ diff --git a/embassy-executor/Cargo.toml b/embassy-executor/Cargo.toml index 0a5360e5d..862d25b59 100644 --- a/embassy-executor/Cargo.toml +++ b/embassy-executor/Cargo.toml @@ -68,7 +68,7 @@ nightly = ["embassy-executor-macros/nightly"] turbowakers = [] ## Use the executor-integrated `embassy-time` timer queue. -integrated-timers = ["dep:embassy-time-driver", "dep:embassy-time-queue-driver"] +integrated-timers = ["dep:embassy-time-driver"] #! ### Architecture _arch = [] # some arch was picked diff --git a/embassy-executor/src/arch/avr.rs b/embassy-executor/src/arch/avr.rs index 7f9ed4421..70085d04d 100644 --- a/embassy-executor/src/arch/avr.rs +++ b/embassy-executor/src/arch/avr.rs @@ -53,10 +53,6 @@ mod thread { /// /// This function never returns. pub fn run(&'static mut self, init: impl FnOnce(Spawner)) -> ! { - unsafe { - self.inner.initialize(); - } - init(self.inner.spawner()); loop { diff --git a/embassy-executor/src/arch/cortex_m.rs b/embassy-executor/src/arch/cortex_m.rs index 0c2af88a6..5c517e0a2 100644 --- a/embassy-executor/src/arch/cortex_m.rs +++ b/embassy-executor/src/arch/cortex_m.rs @@ -98,9 +98,6 @@ mod thread { /// /// This function never returns. pub fn run(&'static mut self, init: impl FnOnce(Spawner)) -> ! { - unsafe { - self.inner.initialize(); - } init(self.inner.spawner()); loop { @@ -210,9 +207,6 @@ mod interrupt { } let executor = unsafe { (&*self.executor.get()).assume_init_ref() }; - unsafe { - executor.initialize(); - } unsafe { NVIC::unmask(irq) } diff --git a/embassy-executor/src/arch/riscv32.rs b/embassy-executor/src/arch/riscv32.rs index 715e5f3cf..01e63a9fd 100644 --- a/embassy-executor/src/arch/riscv32.rs +++ b/embassy-executor/src/arch/riscv32.rs @@ -54,10 +54,6 @@ mod thread { /// /// This function never returns. pub fn run(&'static mut self, init: impl FnOnce(Spawner)) -> ! { - unsafe { - self.inner.initialize(); - } - init(self.inner.spawner()); loop { diff --git a/embassy-executor/src/arch/spin.rs b/embassy-executor/src/arch/spin.rs index 54c7458b3..340023620 100644 --- a/embassy-executor/src/arch/spin.rs +++ b/embassy-executor/src/arch/spin.rs @@ -48,10 +48,6 @@ mod thread { /// /// This function never returns. pub fn run(&'static mut self, init: impl FnOnce(Spawner)) -> ! { - unsafe { - self.inner.initialize(); - } - init(self.inner.spawner()); loop { diff --git a/embassy-executor/src/arch/std.rs b/embassy-executor/src/arch/std.rs index 948c7711b..b02b15988 100644 --- a/embassy-executor/src/arch/std.rs +++ b/embassy-executor/src/arch/std.rs @@ -55,10 +55,6 @@ mod thread { /// /// This function never returns. pub fn run(&'static mut self, init: impl FnOnce(Spawner)) -> ! { - unsafe { - self.inner.initialize(); - } - init(self.inner.spawner()); loop { diff --git a/embassy-executor/src/arch/wasm.rs b/embassy-executor/src/arch/wasm.rs index 35025f11f..f9d0f935c 100644 --- a/embassy-executor/src/arch/wasm.rs +++ b/embassy-executor/src/arch/wasm.rs @@ -70,10 +70,6 @@ mod thread { /// - a `static mut` (unsafe) /// - a local variable in a function you know never returns (like `fn main() -> !`), upgrading its lifetime with `transmute`. (unsafe) pub fn start(&'static mut self, init: impl FnOnce(Spawner)) { - unsafe { - self.inner.initialize(); - } - unsafe { let executor = &self.inner; let future = Closure::new(move |_| { diff --git a/embassy-executor/src/raw/mod.rs b/embassy-executor/src/raw/mod.rs index 3f93eae6f..80bd49bad 100644 --- a/embassy-executor/src/raw/mod.rs +++ b/embassy-executor/src/raw/mod.rs @@ -17,7 +17,7 @@ mod run_queue; mod state; #[cfg(feature = "integrated-timers")] -mod timer_queue; +pub mod timer_queue; #[cfg(feature = "trace")] mod trace; pub(crate) mod util; @@ -31,9 +31,6 @@ use core::pin::Pin; use core::ptr::NonNull; use core::task::{Context, Poll}; -#[cfg(feature = "integrated-timers")] -use embassy_time_driver::AlarmHandle; - use self::run_queue::{RunQueue, RunQueueItem}; use self::state::State; use self::util::{SyncUnsafeCell, UninitCell}; @@ -47,8 +44,7 @@ pub(crate) struct TaskHeader { pub(crate) executor: SyncUnsafeCell>, poll_fn: SyncUnsafeCell>, - #[cfg(feature = "integrated-timers")] - pub(crate) expires_at: SyncUnsafeCell, + /// Integrated timer queue storage. This field should not be accessed outside of the timer queue. #[cfg(feature = "integrated-timers")] pub(crate) timer_queue_item: timer_queue::TimerQueueItem, } @@ -80,6 +76,12 @@ impl TaskRef { unsafe { self.ptr.as_ref() } } + /// Returns a reference to the executor that the task is currently running on. + #[cfg(feature = "integrated-timers")] + pub unsafe fn executor(self) -> Option<&'static Executor> { + self.header().executor.get().map(|e| Executor::wrap(e)) + } + /// The returned pointer is valid for the entire TaskStorage. pub(crate) fn as_ptr(self) -> *const TaskHeader { self.ptr.as_ptr() @@ -120,8 +122,6 @@ impl TaskStorage { // Note: this is lazily initialized so that a static `TaskStorage` will go in `.bss` poll_fn: SyncUnsafeCell::new(None), - #[cfg(feature = "integrated-timers")] - expires_at: SyncUnsafeCell::new(0), #[cfg(feature = "integrated-timers")] timer_queue_item: timer_queue::TimerQueueItem::new(), }, @@ -160,9 +160,6 @@ impl TaskStorage { Poll::Ready(_) => { this.future.drop_in_place(); this.raw.state.despawn(); - - #[cfg(feature = "integrated-timers")] - this.raw.expires_at.set(u64::MAX); } Poll::Pending => {} } @@ -316,34 +313,16 @@ impl Pender { pub(crate) struct SyncExecutor { run_queue: RunQueue, pender: Pender, - - #[cfg(feature = "integrated-timers")] - pub(crate) timer_queue: timer_queue::TimerQueue, - #[cfg(feature = "integrated-timers")] - alarm: AlarmHandle, } impl SyncExecutor { pub(crate) fn new(pender: Pender) -> Self { - #[cfg(feature = "integrated-timers")] - let alarm = unsafe { unwrap!(embassy_time_driver::allocate_alarm()) }; - Self { run_queue: RunQueue::new(), pender, - - #[cfg(feature = "integrated-timers")] - timer_queue: timer_queue::TimerQueue::new(), - #[cfg(feature = "integrated-timers")] - alarm, } } - pub(crate) unsafe fn initialize(&'static self) { - #[cfg(feature = "integrated-timers")] - embassy_time_driver::set_alarm_callback(self.alarm, Self::alarm_callback, self as *const _ as *mut ()); - } - /// Enqueue a task in the task queue /// /// # Safety @@ -360,12 +339,6 @@ impl SyncExecutor { } } - #[cfg(feature = "integrated-timers")] - fn alarm_callback(ctx: *mut ()) { - let this: &Self = unsafe { &*(ctx as *const Self) }; - this.pender.pend(); - } - pub(super) unsafe fn spawn(&'static self, task: TaskRef) { task.header().executor.set(Some(self)); @@ -379,56 +352,27 @@ impl SyncExecutor { /// /// Same as [`Executor::poll`], plus you must only call this on the thread this executor was created. pub(crate) unsafe fn poll(&'static self) { - #[allow(clippy::never_loop)] - loop { - #[cfg(feature = "integrated-timers")] - self.timer_queue - .dequeue_expired(embassy_time_driver::now(), wake_task_no_pend); + self.run_queue.dequeue_all(|p| { + let task = p.header(); - self.run_queue.dequeue_all(|p| { - let task = p.header(); - - #[cfg(feature = "integrated-timers")] - task.expires_at.set(u64::MAX); - - if !task.state.run_dequeue() { - // If task is not running, ignore it. This can happen in the following scenario: - // - Task gets dequeued, poll starts - // - While task is being polled, it gets woken. It gets placed in the queue. - // - Task poll finishes, returning done=true - // - RUNNING bit is cleared, but the task is already in the queue. - return; - } - - #[cfg(feature = "trace")] - trace::task_exec_begin(self, &p); - - // Run the task - task.poll_fn.get().unwrap_unchecked()(p); - - #[cfg(feature = "trace")] - trace::task_exec_end(self, &p); - - // Enqueue or update into timer_queue - #[cfg(feature = "integrated-timers")] - self.timer_queue.update(p); - }); - - #[cfg(feature = "integrated-timers")] - { - // If this is already in the past, set_alarm might return false - // In that case do another poll loop iteration. - let next_expiration = self.timer_queue.next_expiration(); - if embassy_time_driver::set_alarm(self.alarm, next_expiration) { - break; - } + if !task.state.run_dequeue() { + // If task is not running, ignore it. This can happen in the following scenario: + // - Task gets dequeued, poll starts + // - While task is being polled, it gets woken. It gets placed in the queue. + // - Task poll finishes, returning done=true + // - RUNNING bit is cleared, but the task is already in the queue. + return; } - #[cfg(not(feature = "integrated-timers"))] - { - break; - } - } + #[cfg(feature = "trace")] + trace::task_exec_begin(self, &p); + + // Run the task + task.poll_fn.get().unwrap_unchecked()(p); + + #[cfg(feature = "trace")] + trace::task_exec_end(self, &p); + }); #[cfg(feature = "trace")] trace::executor_idle(self) @@ -494,15 +438,6 @@ impl Executor { } } - /// Initializes the executor. - /// - /// # Safety - /// - /// This function must be called once before any other method is called. - pub unsafe fn initialize(&'static self) { - self.inner.initialize(); - } - /// Spawn a task in this executor. /// /// # Safety @@ -575,21 +510,3 @@ pub fn wake_task_no_pend(task: TaskRef) { } } } - -#[cfg(feature = "integrated-timers")] -struct TimerQueue; - -#[cfg(feature = "integrated-timers")] -impl embassy_time_queue_driver::TimerQueue for TimerQueue { - fn schedule_wake(&'static self, at: u64, waker: &core::task::Waker) { - let task = waker::task_from_waker(waker); - let task = task.header(); - unsafe { - let expires_at = task.expires_at.get(); - task.expires_at.set(expires_at.min(at)); - } - } -} - -#[cfg(feature = "integrated-timers")] -embassy_time_queue_driver::timer_queue_impl!(static TIMER_QUEUE: TimerQueue = TimerQueue); diff --git a/embassy-executor/src/raw/timer_queue.rs b/embassy-executor/src/raw/timer_queue.rs index 94a5f340b..953bf014f 100644 --- a/embassy-executor/src/raw/timer_queue.rs +++ b/embassy-executor/src/raw/timer_queue.rs @@ -1,75 +1,100 @@ +//! Timer queue operations. use core::cmp::min; +use super::util::SyncUnsafeCell; use super::TaskRef; -use crate::raw::util::SyncUnsafeCell; pub(crate) struct TimerQueueItem { next: SyncUnsafeCell>, + expires_at: SyncUnsafeCell, } impl TimerQueueItem { pub const fn new() -> Self { Self { next: SyncUnsafeCell::new(None), + expires_at: SyncUnsafeCell::new(0), } } } -pub(crate) struct TimerQueue { +/// A timer queue, with items integrated into tasks. +pub struct TimerQueue { head: SyncUnsafeCell>, } impl TimerQueue { + /// Creates a new timer queue. pub const fn new() -> Self { Self { head: SyncUnsafeCell::new(None), } } - pub(crate) unsafe fn update(&self, p: TaskRef) { - let task = p.header(); - if task.expires_at.get() != u64::MAX { + /// Schedules a task to run at a specific time. + /// + /// If this function returns `true`, the called should find the next expiration time and set + /// a new alarm for that time. + pub fn schedule_wake(&mut self, at: u64, p: TaskRef) -> bool { + unsafe { + let task = p.header(); + let item = &task.timer_queue_item; if task.state.timer_enqueue() { - task.timer_queue_item.next.set(self.head.get()); - self.head.set(Some(p)); + // If not in the queue, add it and update. + let prev = self.head.replace(Some(p)); + item.next.set(prev); + } else if at <= item.expires_at.get() { + // If expiration is sooner than previously set, update. + } else { + // Task does not need to be updated. + return false; } + + item.expires_at.set(at); + true } } - pub(crate) unsafe fn next_expiration(&self) -> u64 { - let mut res = u64::MAX; - self.retain(|p| { - let task = p.header(); - let expires = task.expires_at.get(); - res = min(res, expires); - expires != u64::MAX - }); - res - } + /// Dequeues expired timers and returns the next alarm time. + /// + /// The provided callback will be called for each expired task. Tasks that never expire + /// will be removed, but the callback will not be called. + pub fn next_expiration(&mut self, now: u64) -> u64 { + let mut next_expiration = u64::MAX; - pub(crate) unsafe fn dequeue_expired(&self, now: u64, on_task: impl Fn(TaskRef)) { self.retain(|p| { let task = p.header(); - if task.expires_at.get() <= now { - on_task(p); + let item = &task.timer_queue_item; + let expires = unsafe { item.expires_at.get() }; + + if expires <= now { + // Timer expired, process task. + super::wake_task(p); false } else { - true + // Timer didn't yet expire, or never expires. + next_expiration = min(next_expiration, expires); + expires != u64::MAX } }); + + next_expiration } - pub(crate) unsafe fn retain(&self, mut f: impl FnMut(TaskRef) -> bool) { - let mut prev = &self.head; - while let Some(p) = prev.get() { - let task = p.header(); - if f(p) { - // Skip to next - prev = &task.timer_queue_item.next; - } else { - // Remove it - prev.set(task.timer_queue_item.next.get()); - task.state.timer_dequeue(); + fn retain(&self, mut f: impl FnMut(TaskRef) -> bool) { + unsafe { + let mut prev = &self.head; + while let Some(p) = prev.get() { + let task = p.header(); + let item = &task.timer_queue_item; + if f(p) { + // Skip to next + prev = &item.next; + } else { + // Remove it + prev.set(item.next.get()); + task.state.timer_dequeue(); + } } } } diff --git a/embassy-executor/src/raw/util.rs b/embassy-executor/src/raw/util.rs index c46085e45..e2633658a 100644 --- a/embassy-executor/src/raw/util.rs +++ b/embassy-executor/src/raw/util.rs @@ -54,4 +54,9 @@ impl SyncUnsafeCell { { *self.value.get() } + + #[cfg(feature = "integrated-timers")] + pub unsafe fn replace(&self, value: T) -> T { + core::mem::replace(&mut *self.value.get(), value) + } } diff --git a/embassy-executor/tests/test.rs b/embassy-executor/tests/test.rs index 8054bf7eb..0ce1f1891 100644 --- a/embassy-executor/tests/test.rs +++ b/embassy-executor/tests/test.rs @@ -40,9 +40,6 @@ fn setup() -> (&'static Executor, Trace) { let trace = Trace::new(); let context = Box::leak(Box::new(trace.clone())) as *mut _ as *mut (); let executor = &*Box::leak(Box::new(Executor::new(context))); - unsafe { - executor.initialize(); - } (executor, trace) } diff --git a/embassy-nrf/Cargo.toml b/embassy-nrf/Cargo.toml index 9da050a22..48f80bb5e 100644 --- a/embassy-nrf/Cargo.toml +++ b/embassy-nrf/Cargo.toml @@ -119,7 +119,7 @@ _nrf52 = ["_ppi"] _nrf51 = ["_ppi"] _nrf91 = [] -_time-driver = ["dep:embassy-time-driver", "embassy-time-driver?/tick-hz-32_768"] +_time-driver = ["dep:embassy-time-driver", "embassy-time-driver?/tick-hz-32_768", "dep:embassy-time-queue-driver"] # trustzone state. _s = [] @@ -135,6 +135,7 @@ _nrf52832_anomaly_109 = [] [dependencies] embassy-time-driver = { version = "0.1", path = "../embassy-time-driver", optional = true } +embassy-time-queue-driver = { version = "0.1", path = "../embassy-time-queue-driver", optional = true } embassy-time = { version = "0.3.2", path = "../embassy-time", optional = true } embassy-sync = { version = "0.6.1", path = "../embassy-sync" } embassy-hal-internal = {version = "0.2.0", path = "../embassy-hal-internal", features = ["cortex-m", "prio-bits-3"] } diff --git a/embassy-nrf/src/time_driver.rs b/embassy-nrf/src/time_driver.rs index 9ba38ec1b..f8b3c4bbc 100644 --- a/embassy-nrf/src/time_driver.rs +++ b/embassy-nrf/src/time_driver.rs @@ -1,11 +1,11 @@ use core::cell::Cell; -use core::sync::atomic::{compiler_fence, AtomicU32, AtomicU8, Ordering}; -use core::{mem, ptr}; +use core::sync::atomic::{compiler_fence, AtomicU32, Ordering}; use critical_section::CriticalSection; use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex; use embassy_sync::blocking_mutex::CriticalSectionMutex as Mutex; -use embassy_time_driver::{AlarmHandle, Driver}; +use embassy_time_driver::Driver; +use embassy_time_queue_driver::GlobalTimerQueue; use crate::interrupt::InterruptExt; use crate::{interrupt, pac}; @@ -94,11 +94,6 @@ mod test { struct AlarmState { timestamp: Cell, - - // This is really a Option<(fn(*mut ()), *mut ())> - // but fn pointers aren't allowed in const yet - callback: Cell<*const ()>, - ctx: Cell<*mut ()>, } unsafe impl Send for AlarmState {} @@ -107,26 +102,20 @@ impl AlarmState { const fn new() -> Self { Self { timestamp: Cell::new(u64::MAX), - callback: Cell::new(ptr::null()), - ctx: Cell::new(ptr::null_mut()), } } } -const ALARM_COUNT: usize = 3; - struct RtcDriver { /// Number of 2^23 periods elapsed since boot. period: AtomicU32, - alarm_count: AtomicU8, /// Timestamp at which to fire alarm. u64::MAX if no alarm is scheduled. - alarms: Mutex<[AlarmState; ALARM_COUNT]>, + alarms: Mutex, } embassy_time_driver::time_driver_impl!(static DRIVER: RtcDriver = RtcDriver { period: AtomicU32::new(0), - alarm_count: AtomicU8::new(0), - alarms: Mutex::const_new(CriticalSectionRawMutex::new(), [const {AlarmState::new()}; ALARM_COUNT]), + alarms: Mutex::const_new(CriticalSectionRawMutex::new(), AlarmState::new()), }); impl RtcDriver { @@ -169,13 +158,12 @@ impl RtcDriver { self.next_period(); } - for n in 0..ALARM_COUNT { - if r.events_compare(n).read() == 1 { - r.events_compare(n).write_value(0); - critical_section::with(|cs| { - self.trigger_alarm(n, cs); - }) - } + let n = 0; + if r.events_compare(n).read() == 1 { + r.events_compare(n).write_value(0); + critical_section::with(|cs| { + self.trigger_alarm(cs); + }); } } @@ -186,75 +174,33 @@ impl RtcDriver { self.period.store(period, Ordering::Relaxed); let t = (period as u64) << 23; - for n in 0..ALARM_COUNT { - let alarm = &self.alarms.borrow(cs)[n]; - let at = alarm.timestamp.get(); + let n = 0; + let alarm = &self.alarms.borrow(cs); + let at = alarm.timestamp.get(); - if at < t + 0xc00000 { - // just enable it. `set_alarm` has already set the correct CC val. - r.intenset().write(|w| w.0 = compare_n(n)); - } + if at < t + 0xc00000 { + // just enable it. `set_alarm` has already set the correct CC val. + r.intenset().write(|w| w.0 = compare_n(n)); } }) } - fn get_alarm<'a>(&'a self, cs: CriticalSection<'a>, alarm: AlarmHandle) -> &'a AlarmState { - // safety: we're allowed to assume the AlarmState is created by us, and - // we never create one that's out of bounds. - unsafe { self.alarms.borrow(cs).get_unchecked(alarm.id() as usize) } - } - - fn trigger_alarm(&self, n: usize, cs: CriticalSection) { + fn trigger_alarm(&self, cs: CriticalSection) { + let n = 0; let r = rtc(); r.intenclr().write(|w| w.0 = compare_n(n)); - let alarm = &self.alarms.borrow(cs)[n]; + let alarm = &self.alarms.borrow(cs); alarm.timestamp.set(u64::MAX); // Call after clearing alarm, so the callback can set another alarm. - - // safety: - // - we can ignore the possiblity of `f` being unset (null) because of the safety contract of `allocate_alarm`. - // - other than that we only store valid function pointers into alarm.callback - let f: fn(*mut ()) = unsafe { mem::transmute(alarm.callback.get()) }; - f(alarm.ctx.get()); - } -} - -impl Driver for RtcDriver { - fn now(&self) -> u64 { - // `period` MUST be read before `counter`, see comment at the top for details. - let period = self.period.load(Ordering::Relaxed); - compiler_fence(Ordering::Acquire); - let counter = rtc().counter().read().0; - calc_now(period, counter) + TIMER_QUEUE_DRIVER.dispatch(); } - unsafe fn allocate_alarm(&self) -> Option { - critical_section::with(|_| { - let id = self.alarm_count.load(Ordering::Relaxed); - if id < ALARM_COUNT as u8 { - self.alarm_count.store(id + 1, Ordering::Relaxed); - Some(AlarmHandle::new(id)) - } else { - None - } - }) - } - - fn set_alarm_callback(&self, alarm: AlarmHandle, callback: fn(*mut ()), ctx: *mut ()) { + fn set_alarm(&self, timestamp: u64) -> bool { critical_section::with(|cs| { - let alarm = self.get_alarm(cs, alarm); - - alarm.callback.set(callback as *const ()); - alarm.ctx.set(ctx); - }) - } - - fn set_alarm(&self, alarm: AlarmHandle, timestamp: u64) -> bool { - critical_section::with(|cs| { - let n = alarm.id() as _; - let alarm = self.get_alarm(cs, alarm); + let n = 0; + let alarm = &self.alarms.borrow(cs); alarm.timestamp.set(timestamp); let r = rtc(); @@ -304,6 +250,16 @@ impl Driver for RtcDriver { } } +impl Driver for RtcDriver { + fn now(&self) -> u64 { + // `period` MUST be read before `counter`, see comment at the top for details. + let period = self.period.load(Ordering::Relaxed); + compiler_fence(Ordering::Acquire); + let counter = rtc().counter().read().0; + calc_now(period, counter) + } +} + #[cfg(feature = "_nrf54l")] #[cfg(feature = "rt")] #[interrupt] @@ -321,3 +277,8 @@ fn RTC1() { pub(crate) fn init(irq_prio: crate::interrupt::Priority) { DRIVER.init(irq_prio) } + +embassy_time_queue_driver::timer_queue_impl!( + static TIMER_QUEUE_DRIVER: GlobalTimerQueue + = GlobalTimerQueue::new(|next_expiration| DRIVER.set_alarm(next_expiration)) +); diff --git a/embassy-rp/Cargo.toml b/embassy-rp/Cargo.toml index 3809f1894..94de82fa9 100644 --- a/embassy-rp/Cargo.toml +++ b/embassy-rp/Cargo.toml @@ -40,7 +40,7 @@ critical-section-impl = ["critical-section/restore-state-u8"] unstable-pac = [] ## Enable the timer for use with `embassy-time` with a 1MHz tick rate. -time-driver = ["dep:embassy-time-driver", "embassy-time-driver?/tick-hz-1_000_000"] +time-driver = ["dep:embassy-time-driver", "embassy-time-driver?/tick-hz-1_000_000", "dep:embassy-time-queue-driver"] ## Enable ROM function cache. This will store the address of a ROM function when first used, improving performance of subsequent calls. rom-func-cache = [] @@ -110,6 +110,7 @@ binary-info = ["rt", "dep:rp-binary-info", "rp-binary-info?/binary-info"] [dependencies] embassy-sync = { version = "0.6.1", path = "../embassy-sync" } embassy-time-driver = { version = "0.1", path = "../embassy-time-driver", optional = true } +embassy-time-queue-driver = { version = "0.1", path = "../embassy-time-queue-driver", optional = true } embassy-time = { version = "0.3.2", path = "../embassy-time" } embassy-futures = { version = "0.1.0", path = "../embassy-futures" } embassy-hal-internal = {version = "0.2.0", path = "../embassy-hal-internal", features = ["cortex-m", "prio-bits-2"] } diff --git a/embassy-rp/src/time_driver.rs b/embassy-rp/src/time_driver.rs index 40fc71bb1..17ae5fff3 100644 --- a/embassy-rp/src/time_driver.rs +++ b/embassy-rp/src/time_driver.rs @@ -1,11 +1,10 @@ //! Timer driver. use core::cell::Cell; -use atomic_polyfill::{AtomicU8, Ordering}; -use critical_section::CriticalSection; use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex; use embassy_sync::blocking_mutex::Mutex; -use embassy_time_driver::{AlarmHandle, Driver}; +use embassy_time_driver::Driver; +use embassy_time_queue_driver::GlobalTimerQueue; #[cfg(feature = "rp2040")] use pac::TIMER; #[cfg(feature = "_rp235x")] @@ -16,23 +15,17 @@ use crate::{interrupt, pac}; struct AlarmState { timestamp: Cell, - callback: Cell>, } unsafe impl Send for AlarmState {} -const ALARM_COUNT: usize = 4; - struct TimerDriver { - alarms: Mutex, - next_alarm: AtomicU8, + alarms: Mutex, } embassy_time_driver::time_driver_impl!(static DRIVER: TimerDriver = TimerDriver{ - alarms: Mutex::const_new(CriticalSectionRawMutex::new(), [const{AlarmState { + alarms: Mutex::const_new(CriticalSectionRawMutex::new(), AlarmState { timestamp: Cell::new(0), - callback: Cell::new(None), - }}; ALARM_COUNT]), - next_alarm: AtomicU8::new(0), + }), }); impl Driver for TimerDriver { @@ -46,34 +39,13 @@ impl Driver for TimerDriver { } } } +} - unsafe fn allocate_alarm(&self) -> Option { - let id = self.next_alarm.fetch_update(Ordering::AcqRel, Ordering::Acquire, |x| { - if x < ALARM_COUNT as u8 { - Some(x + 1) - } else { - None - } - }); - - match id { - Ok(id) => Some(AlarmHandle::new(id)), - Err(_) => None, - } - } - - fn set_alarm_callback(&self, alarm: AlarmHandle, callback: fn(*mut ()), ctx: *mut ()) { - let n = alarm.id() as usize; +impl TimerDriver { + fn set_alarm(&self, timestamp: u64) -> bool { + let n = 0; critical_section::with(|cs| { - let alarm = &self.alarms.borrow(cs)[n]; - alarm.callback.set(Some((callback, ctx))); - }) - } - - fn set_alarm(&self, alarm: AlarmHandle, timestamp: u64) -> bool { - let n = alarm.id() as usize; - critical_section::with(|cs| { - let alarm = &self.alarms.borrow(cs)[n]; + let alarm = &self.alarms.borrow(cs); alarm.timestamp.set(timestamp); // Arm it. @@ -96,15 +68,14 @@ impl Driver for TimerDriver { } }) } -} -impl TimerDriver { - fn check_alarm(&self, n: usize) { + fn check_alarm(&self) { + let n = 0; critical_section::with(|cs| { - let alarm = &self.alarms.borrow(cs)[n]; + let alarm = &self.alarms.borrow(cs); let timestamp = alarm.timestamp.get(); if timestamp <= self.now() { - self.trigger_alarm(n, cs) + self.trigger_alarm() } else { // Not elapsed, arm it again. // This can happen if it was set more than 2^32 us in the future. @@ -116,17 +87,8 @@ impl TimerDriver { TIMER.intr().write(|w| w.set_alarm(n, true)); } - fn trigger_alarm(&self, n: usize, cs: CriticalSection) { - // disarm - TIMER.armed().write(|w| w.set_armed(1 << n)); - - let alarm = &self.alarms.borrow(cs)[n]; - alarm.timestamp.set(u64::MAX); - - // Call after clearing alarm, so the callback can set another alarm. - if let Some((f, ctx)) = alarm.callback.get() { - f(ctx); - } + fn trigger_alarm(&self) { + TIMER_QUEUE_DRIVER.dispatch(); } } @@ -134,79 +96,37 @@ impl TimerDriver { pub unsafe fn init() { // init alarms critical_section::with(|cs| { - let alarms = DRIVER.alarms.borrow(cs); - for a in alarms { - a.timestamp.set(u64::MAX); - } + let alarm = DRIVER.alarms.borrow(cs); + alarm.timestamp.set(u64::MAX); }); - // enable all irqs + // enable irq TIMER.inte().write(|w| { w.set_alarm(0, true); - w.set_alarm(1, true); - w.set_alarm(2, true); - w.set_alarm(3, true); }); #[cfg(feature = "rp2040")] { interrupt::TIMER_IRQ_0.enable(); - interrupt::TIMER_IRQ_1.enable(); - interrupt::TIMER_IRQ_2.enable(); - interrupt::TIMER_IRQ_3.enable(); } #[cfg(feature = "_rp235x")] { interrupt::TIMER0_IRQ_0.enable(); - interrupt::TIMER0_IRQ_1.enable(); - interrupt::TIMER0_IRQ_2.enable(); - interrupt::TIMER0_IRQ_3.enable(); } } #[cfg(all(feature = "rt", feature = "rp2040"))] #[interrupt] fn TIMER_IRQ_0() { - DRIVER.check_alarm(0) -} - -#[cfg(all(feature = "rt", feature = "rp2040"))] -#[interrupt] -fn TIMER_IRQ_1() { - DRIVER.check_alarm(1) -} - -#[cfg(all(feature = "rt", feature = "rp2040"))] -#[interrupt] -fn TIMER_IRQ_2() { - DRIVER.check_alarm(2) -} - -#[cfg(all(feature = "rt", feature = "rp2040"))] -#[interrupt] -fn TIMER_IRQ_3() { - DRIVER.check_alarm(3) + DRIVER.check_alarm() } #[cfg(all(feature = "rt", feature = "_rp235x"))] #[interrupt] fn TIMER0_IRQ_0() { - DRIVER.check_alarm(0) + DRIVER.check_alarm() } -#[cfg(all(feature = "rt", feature = "_rp235x"))] -#[interrupt] -fn TIMER0_IRQ_1() { - DRIVER.check_alarm(1) -} - -#[cfg(all(feature = "rt", feature = "_rp235x"))] -#[interrupt] -fn TIMER0_IRQ_2() { - DRIVER.check_alarm(2) -} - -#[cfg(all(feature = "rt", feature = "_rp235x"))] -#[interrupt] -fn TIMER0_IRQ_3() { - DRIVER.check_alarm(3) -} +embassy_time_queue_driver::timer_queue_impl!( + static TIMER_QUEUE_DRIVER: GlobalTimerQueue + = GlobalTimerQueue::new(|next_expiration| DRIVER.set_alarm(next_expiration)) +); diff --git a/embassy-stm32/Cargo.toml b/embassy-stm32/Cargo.toml index 8b4e7c929..82030f99f 100644 --- a/embassy-stm32/Cargo.toml +++ b/embassy-stm32/Cargo.toml @@ -45,6 +45,7 @@ rustdoc-args = ["--cfg", "docsrs"] embassy-sync = { version = "0.6.1", path = "../embassy-sync" } embassy-time = { version = "0.3.2", path = "../embassy-time", optional = true } embassy-time-driver = { version = "0.1", path = "../embassy-time-driver", optional = true } +embassy-time-queue-driver = { version = "0.1", path = "../embassy-time-queue-driver", optional = true } embassy-futures = { version = "0.1.0", path = "../embassy-futures" } embassy-hal-internal = {version = "0.2.0", path = "../embassy-hal-internal", features = ["cortex-m", "prio-bits-4"] } embassy-embedded-hal = {version = "0.2.0", path = "../embassy-embedded-hal", default-features = false } @@ -125,6 +126,7 @@ defmt = [ exti = [] low-power = [ "dep:embassy-executor", "embassy-executor?/arch-cortex-m", "time" ] low-power-debug-with-sleep = [] +integrated-timers = ["dep:embassy-executor", "_time-driver"] ## Automatically generate `memory.x` file using [`stm32-metapac`](https://docs.rs/stm32-metapac/) memory-x = ["stm32-metapac/memory-x"] @@ -149,7 +151,7 @@ time = ["dep:embassy-time", "embassy-embedded-hal/time"] # Features starting with `_` are for internal use only. They're not intended # to be enabled by other crates, and are not covered by semver guarantees. -_time-driver = ["dep:embassy-time-driver", "time"] +_time-driver = ["dep:embassy-time-driver", "time", "dep:embassy-time-queue-driver"] ## Use any time driver time-driver-any = ["_time-driver"] diff --git a/embassy-stm32/src/low_power.rs b/embassy-stm32/src/low_power.rs index 0b87bd95a..7734365f1 100644 --- a/embassy-stm32/src/low_power.rs +++ b/embassy-stm32/src/low_power.rs @@ -256,9 +256,6 @@ impl Executor { /// This function never returns. pub fn run(&'static mut self, init: impl FnOnce(Spawner)) -> ! { let executor = unsafe { EXECUTOR.as_mut().unwrap() }; - unsafe { - executor.inner.initialize(); - } init(executor.inner.spawner()); loop { diff --git a/embassy-stm32/src/time_driver.rs b/embassy-stm32/src/time_driver.rs index 00aa3cfa4..290f857ad 100644 --- a/embassy-stm32/src/time_driver.rs +++ b/embassy-stm32/src/time_driver.rs @@ -1,13 +1,13 @@ #![allow(non_snake_case)] use core::cell::Cell; -use core::sync::atomic::{compiler_fence, AtomicU32, AtomicU8, Ordering}; -use core::{mem, ptr}; +use core::sync::atomic::{compiler_fence, AtomicU32, Ordering}; 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 embassy_time_driver::{Driver, TICK_HZ}; +use embassy_time_queue_driver::GlobalTimerQueue; use stm32_metapac::timer::{regs, TimGp16}; use crate::interrupt::typelevel::Interrupt; @@ -24,18 +24,6 @@ use crate::{interrupt, peripherals}; // additional CC capabilities to provide timer alarms to embassy-time. embassy-time requires AT LEAST // one alarm to be allocatable, which means timers that only have CC1, such as TIM16/TIM17, are not // candidates for use as an embassy-time driver provider. (a.k.a 1CH and 1CH_CMP are not, others are good.) -// -// The values of ALARM_COUNT below are not the TOTAL CC registers available, but rather the number -// available after reserving CC1 for regular time keeping. For example, TIM2 has four CC registers: -// CC1, CC2, CC3, and CC4, so it can provide ALARM_COUNT = 3. - -cfg_if::cfg_if! { - if #[cfg(any(time_driver_tim9, time_driver_tim12, time_driver_tim15, time_driver_tim21, time_driver_tim22))] { - const ALARM_COUNT: usize = 1; - } else { - const ALARM_COUNT: usize = 3; - } -} #[cfg(time_driver_tim1)] type T = peripherals::TIM1; @@ -208,11 +196,6 @@ fn calc_now(period: u32, counter: u16) -> u64 { struct AlarmState { timestamp: Cell, - - // This is really a Option<(fn(*mut ()), *mut ())> - // but fn pointers aren't allowed in const yet - callback: Cell<*const ()>, - ctx: Cell<*mut ()>, } unsafe impl Send for AlarmState {} @@ -221,8 +204,6 @@ impl AlarmState { const fn new() -> Self { Self { timestamp: Cell::new(u64::MAX), - callback: Cell::new(ptr::null()), - ctx: Cell::new(ptr::null_mut()), } } } @@ -230,17 +211,14 @@ impl AlarmState { pub(crate) struct RtcDriver { /// Number of 2^15 periods elapsed since boot. period: AtomicU32, - alarm_count: AtomicU8, - /// Timestamp at which to fire alarm. u64::MAX if no alarm is scheduled. - alarms: Mutex, + alarm: Mutex, #[cfg(feature = "low-power")] rtc: Mutex>>, } embassy_time_driver::time_driver_impl!(static DRIVER: RtcDriver = RtcDriver { period: AtomicU32::new(0), - alarm_count: AtomicU8::new(0), - alarms: Mutex::const_new(CriticalSectionRawMutex::new(), [const{AlarmState::new()}; ALARM_COUNT]), + alarm: Mutex::const_new(CriticalSectionRawMutex::new(), AlarmState::new()), #[cfg(feature = "low-power")] rtc: Mutex::const_new(CriticalSectionRawMutex::new(), Cell::new(None)), }); @@ -289,7 +267,7 @@ impl RtcDriver { let r = regs_gp16(); // XXX: reduce the size of this critical section ? - critical_section::with(|cs| { + critical_section::with(|_cs| { let sr = r.sr().read(); let dier = r.dier().read(); @@ -308,10 +286,9 @@ impl RtcDriver { self.next_period(); } - for n in 0..ALARM_COUNT { - if sr.ccif(n + 1) && dier.ccie(n + 1) { - self.trigger_alarm(n, cs); - } + let n = 0; + if sr.ccif(n + 1) && dier.ccie(n + 1) { + self.trigger_alarm(); } }) } @@ -326,36 +303,20 @@ impl RtcDriver { critical_section::with(move |cs| { r.dier().modify(move |w| { - for n in 0..ALARM_COUNT { - let alarm = &self.alarms.borrow(cs)[n]; - let at = alarm.timestamp.get(); + let n = 0; + let alarm = self.alarm.borrow(cs); + let at = alarm.timestamp.get(); - if at < t + 0xc000 { - // just enable it. `set_alarm` has already set the correct CCR val. - w.set_ccie(n + 1, true); - } + if at < t + 0xc000 { + // just enable it. `set_alarm` has already set the correct CCR val. + w.set_ccie(n + 1, true); } }) }) } - fn get_alarm<'a>(&'a self, cs: CriticalSection<'a>, alarm: AlarmHandle) -> &'a AlarmState { - // safety: we're allowed to assume the AlarmState is created by us, and - // we never create one that's out of bounds. - unsafe { self.alarms.borrow(cs).get_unchecked(alarm.id() as usize) } - } - - fn trigger_alarm(&self, n: usize, cs: CriticalSection) { - let alarm = &self.alarms.borrow(cs)[n]; - alarm.timestamp.set(u64::MAX); - - // Call after clearing alarm, so the callback can set another alarm. - - // safety: - // - we can ignore the possibility of `f` being unset (null) because of the safety contract of `allocate_alarm`. - // - other than that we only store valid function pointers into alarm.callback - let f: fn(*mut ()) = unsafe { mem::transmute(alarm.callback.get()) }; - f(alarm.ctx.get()); + fn trigger_alarm(&self) { + TIMER_QUEUE_DRIVER.dispatch(); } /* @@ -367,14 +328,7 @@ impl RtcDriver { fn time_until_next_alarm(&self, cs: CriticalSection) -> embassy_time::Duration { let now = self.now() + 32; - embassy_time::Duration::from_ticks( - self.alarms - .borrow(cs) - .iter() - .map(|alarm: &AlarmState| alarm.timestamp.get().saturating_sub(now)) - .min() - .unwrap_or(u64::MAX), - ) + embassy_time::Duration::from_ticks(self.alarm.borrow(cs).timestamp.get().saturating_sub(now)) } #[cfg(feature = "low-power")] @@ -409,15 +363,12 @@ impl RtcDriver { self.period.store(period, Ordering::SeqCst); regs_gp16().cnt().write(|w| w.set_cnt(cnt as u16)); - // Now, recompute all alarms - for i in 0..self.alarm_count.load(Ordering::Relaxed) as usize { - let alarm_handle = unsafe { AlarmHandle::new(i as u8) }; - let alarm = self.get_alarm(cs, alarm_handle); + // Now, recompute alarm + let alarm = self.alarm.borrow(cs); - if !self.set_alarm(alarm_handle, alarm.timestamp.get()) { - // If the alarm timestamp has passed, we need to trigger it - self.trigger_alarm(i, cs); - } + if !self.set_alarm(alarm.timestamp.get()) { + // If the alarm timestamp has passed, we need to trigger it + self.trigger_alarm(); } } @@ -489,46 +440,13 @@ impl RtcDriver { regs_gp16().cr1().modify(|w| w.set_cen(true)); }) } -} -impl Driver for RtcDriver { - fn now(&self) -> u64 { - let r = regs_gp16(); - - let period = self.period.load(Ordering::Relaxed); - compiler_fence(Ordering::Acquire); - let counter = r.cnt().read().cnt(); - calc_now(period, counter) - } - - unsafe fn allocate_alarm(&self) -> Option { - critical_section::with(|_| { - let id = self.alarm_count.load(Ordering::Relaxed); - if id < ALARM_COUNT as u8 { - self.alarm_count.store(id + 1, Ordering::Relaxed); - Some(AlarmHandle::new(id)) - } else { - None - } - }) - } - - fn set_alarm_callback(&self, alarm: AlarmHandle, callback: fn(*mut ()), ctx: *mut ()) { - critical_section::with(|cs| { - let alarm = self.get_alarm(cs, alarm); - - alarm.callback.set(callback as *const ()); - alarm.ctx.set(ctx); - }) - } - - fn set_alarm(&self, alarm: AlarmHandle, timestamp: u64) -> bool { + fn set_alarm(&self, timestamp: u64) -> bool { critical_section::with(|cs| { let r = regs_gp16(); - let n = alarm.id() as usize; - let alarm = self.get_alarm(cs, alarm); - alarm.timestamp.set(timestamp); + let n = 0; + self.alarm.borrow(cs).timestamp.set(timestamp); let t = self.now(); if timestamp <= t { @@ -536,7 +454,7 @@ impl Driver for RtcDriver { // Disarm the alarm and return `false` to indicate that. r.dier().modify(|w| w.set_ccie(n + 1, false)); - alarm.timestamp.set(u64::MAX); + self.alarm.borrow(cs).timestamp.set(u64::MAX); return false; } @@ -558,7 +476,7 @@ impl Driver for RtcDriver { // It is the caller's responsibility to handle this ambiguity. r.dier().modify(|w| w.set_ccie(n + 1, false)); - alarm.timestamp.set(u64::MAX); + self.alarm.borrow(cs).timestamp.set(u64::MAX); return false; } @@ -569,6 +487,17 @@ impl Driver for RtcDriver { } } +impl Driver for RtcDriver { + fn now(&self) -> u64 { + let r = regs_gp16(); + + let period = self.period.load(Ordering::Relaxed); + compiler_fence(Ordering::Acquire); + let counter = r.cnt().read().cnt(); + calc_now(period, counter) + } +} + #[cfg(feature = "low-power")] pub(crate) fn get_driver() -> &'static RtcDriver { &DRIVER @@ -577,3 +506,8 @@ pub(crate) fn get_driver() -> &'static RtcDriver { pub(crate) fn init(cs: CriticalSection) { DRIVER.init(cs) } + +embassy_time_queue_driver::timer_queue_impl!( + static TIMER_QUEUE_DRIVER: GlobalTimerQueue + = GlobalTimerQueue::new(|next_expiration| DRIVER.set_alarm(next_expiration)) +); diff --git a/embassy-time-driver/src/lib.rs b/embassy-time-driver/src/lib.rs index 12f40b9b9..ffb363cd7 100644 --- a/embassy-time-driver/src/lib.rs +++ b/embassy-time-driver/src/lib.rs @@ -21,8 +21,8 @@ //! //! Instead of the usual "trait + generic params" approach, calls from embassy to the driver are done via `extern` functions. //! -//! `embassy` internally defines the driver functions as `extern "Rust" { fn _embassy_time_now() -> u64; }` and calls them. -//! The driver crate defines the functions as `#[no_mangle] fn _embassy_time_now() -> u64`. The linker will resolve the +//! `embassy` internally defines the driver function as `extern "Rust" { fn _embassy_time_now() -> u64; }` and calls it. +//! The driver crate defines the function as `#[no_mangle] fn _embassy_time_now() -> u64`. The linker will resolve the //! calls from the `embassy` crate to call into the driver crate. //! //! If there is none or multiple drivers in the crate tree, linking will fail. @@ -38,7 +38,7 @@ //! # Example //! //! ``` -//! use embassy_time_driver::{Driver, AlarmHandle}; +//! use embassy_time_driver::Driver; //! //! struct MyDriver{} // not public! //! @@ -46,15 +46,6 @@ //! fn now(&self) -> u64 { //! todo!() //! } -//! unsafe fn allocate_alarm(&self) -> Option { -//! todo!() -//! } -//! fn set_alarm_callback(&self, alarm: AlarmHandle, callback: fn(*mut ()), ctx: *mut ()) { -//! todo!() -//! } -//! fn set_alarm(&self, alarm: AlarmHandle, timestamp: u64) -> bool { -//! todo!() -//! } //! } //! //! embassy_time_driver::time_driver_impl!(static DRIVER: MyDriver = MyDriver{}); @@ -70,28 +61,6 @@ mod tick; /// This value is specified by the [`tick-*` Cargo features](crate#tick-rate) pub const TICK_HZ: u64 = tick::TICK_HZ; -/// Alarm handle, assigned by the driver. -#[derive(Clone, Copy)] -pub struct AlarmHandle { - id: u8, -} - -impl AlarmHandle { - /// Create an AlarmHandle - /// - /// Safety: May only be called by the current global Driver impl. - /// The impl is allowed to rely on the fact that all `AlarmHandle` instances - /// are created by itself in unsafe code (e.g. indexing operations) - pub unsafe fn new(id: u8) -> Self { - Self { id } - } - - /// Get the ID of the AlarmHandle. - pub fn id(&self) -> u8 { - self.id - } -} - /// Time driver pub trait Driver: Send + Sync + 'static { /// Return the current timestamp in ticks. @@ -105,76 +74,10 @@ pub trait Driver: Send + Sync + 'static { /// you MUST extend them to 64-bit, for example by counting overflows in software, /// or chaining multiple timers together. fn now(&self) -> u64; - - /// Try allocating an alarm handle. Returns None if no alarms left. - /// Initially the alarm has no callback set, and a null `ctx` pointer. - /// - /// The allocated alarm is a reusable resource and can be used multiple times. - /// Once the alarm has fired, it remains allocated and can be set again without needing - /// to be reallocated. - /// - /// # Safety - /// It is UB to make the alarm fire before setting a callback. - unsafe fn allocate_alarm(&self) -> Option; - - /// Set the callback function to be called when the alarm triggers. - /// The callback may be called from any context (interrupt or thread mode). - /// - /// The callback is maintained after the alarm has fired. Callers do not need - /// to set a callback again before setting another alarm, unless they want to - /// change the callback function or context. - fn set_alarm_callback(&self, alarm: AlarmHandle, callback: fn(*mut ()), ctx: *mut ()); - - /// Set an alarm at the given timestamp. - /// - /// ## Behavior - /// - /// If `timestamp` is in the future, `set_alarm` schedules calling the callback function - /// at that time, and returns `true`. - /// - /// If `timestamp` is in the past, `set_alarm` has two allowed behaviors. Implementations can pick whether to: - /// - /// - Schedule calling the callback function "immediately", as if the requested timestamp was "now+epsilon" and return `true`, or - /// - Not schedule the callback, and return `false`. - /// - /// Callers must ensure to behave correctly with either behavior. - /// - /// When callback is called, it is guaranteed that `now()` will return a value greater than or equal to `timestamp`. - /// - /// ## Reentrancy - /// - /// Calling the callback from `set_alarm` synchronously is not allowed. If the implementation chooses the first option above, - /// it must still call the callback from another context (i.e. an interrupt handler or background thread), it's not allowed - /// to call it synchronously in the context `set_alarm` is running. - /// - /// The reason for the above is callers are explicitly permitted to do both of: - /// - Lock a mutex in the alarm callback. - /// - Call `set_alarm` while having that mutex locked. - /// - /// If `set_alarm` called the callback synchronously, it'd cause a deadlock or panic because it'd cause the - /// mutex to be locked twice reentrantly in the same context. - /// - /// ## Overwriting alarms - /// - /// Only one alarm can be active at a time for each `AlarmHandle`. This overwrites any previously-set alarm if any. - /// - /// ## Unsetting the alarm - /// - /// There is no `unset_alarm` API. Instead, callers can call `set_alarm` with `timestamp` set to `u64::MAX`. - /// - /// This allows for more efficient implementations, since they don't need to distinguish between the "alarm set" and - /// "alarm not set" cases, thanks to the fact "Alarm set for u64::MAX" is effectively equivalent for "alarm not set". - /// - /// This means implementations need to be careful to avoid timestamp overflows. The recommendation is to make `timestamp` - /// be in the same units as hardware ticks to avoid any conversions, which makes avoiding overflow easier. - fn set_alarm(&self, alarm: AlarmHandle, timestamp: u64) -> bool; } extern "Rust" { fn _embassy_time_now() -> u64; - fn _embassy_time_allocate_alarm() -> Option; - fn _embassy_time_set_alarm_callback(alarm: AlarmHandle, callback: fn(*mut ()), ctx: *mut ()); - fn _embassy_time_set_alarm(alarm: AlarmHandle, timestamp: u64) -> bool; } /// See [`Driver::now`] @@ -182,23 +85,6 @@ pub fn now() -> u64 { unsafe { _embassy_time_now() } } -/// See [`Driver::allocate_alarm`] -/// -/// Safety: it is UB to make the alarm fire before setting a callback. -pub unsafe fn allocate_alarm() -> Option { - _embassy_time_allocate_alarm() -} - -/// See [`Driver::set_alarm_callback`] -pub fn set_alarm_callback(alarm: AlarmHandle, callback: fn(*mut ()), ctx: *mut ()) { - unsafe { _embassy_time_set_alarm_callback(alarm, callback, ctx) } -} - -/// See [`Driver::set_alarm`] -pub fn set_alarm(alarm: AlarmHandle, timestamp: u64) -> bool { - unsafe { _embassy_time_set_alarm(alarm, timestamp) } -} - /// Set the time Driver implementation. /// /// See the module documentation for an example. @@ -211,20 +97,5 @@ macro_rules! time_driver_impl { fn _embassy_time_now() -> u64 { <$t as $crate::Driver>::now(&$name) } - - #[no_mangle] - unsafe fn _embassy_time_allocate_alarm() -> Option<$crate::AlarmHandle> { - <$t as $crate::Driver>::allocate_alarm(&$name) - } - - #[no_mangle] - fn _embassy_time_set_alarm_callback(alarm: $crate::AlarmHandle, callback: fn(*mut ()), ctx: *mut ()) { - <$t as $crate::Driver>::set_alarm_callback(&$name, alarm, callback, ctx) - } - - #[no_mangle] - fn _embassy_time_set_alarm(alarm: $crate::AlarmHandle, timestamp: u64) -> bool { - <$t as $crate::Driver>::set_alarm(&$name, alarm, timestamp) - } }; } diff --git a/embassy-time-queue-driver/Cargo.toml b/embassy-time-queue-driver/Cargo.toml index 9ce9d79bb..599041a3f 100644 --- a/embassy-time-queue-driver/Cargo.toml +++ b/embassy-time-queue-driver/Cargo.toml @@ -20,6 +20,39 @@ categories = [ # This is especially common when mixing crates from crates.io and git. links = "embassy-time-queue" +[dependencies] +critical-section = "1.2.0" +heapless = "0.8" +embassy-executor = { version = "0.6.3", path = "../embassy-executor", optional = true } +embassy-time-driver = { version = "0.1.0", path = "../embassy-time-driver" } + +[features] +#! ### Generic Queue + +## Use the executor-integrated `embassy-time` timer queue. The timer items are stored inside +## the task headers, so you do not need to set a capacity for the queue. +## To use this you must have a time driver provided. +## +## If this feature is not enabled, a generic queue is available with a configurable capacity. +integrated-timers = ["embassy-executor/integrated-timers"] + +#! The following features set how many timers are used for the generic queue. At most one +#! `generic-queue-*` feature can be enabled. If none is enabled, a default of 64 timers is used. +#! +#! When using embassy-time from libraries, you should *not* enable any `generic-queue-*` feature, to allow the +#! end user to pick. + +## Generic Queue with 8 timers +generic-queue-8 = [] +## Generic Queue with 16 timers +generic-queue-16 = [] +## Generic Queue with 32 timers +generic-queue-32 = [] +## Generic Queue with 64 timers +generic-queue-64 = [] +## Generic Queue with 128 timers +generic-queue-128 = [] + [package.metadata.embassy_docs] src_base = "https://github.com/embassy-rs/embassy/blob/embassy-time-queue-driver-v$VERSION/embassy-time-queue-driver/src/" src_base_git = "https://github.com/embassy-rs/embassy/blob/$COMMIT/embassy-time-queue-driver/src/" diff --git a/embassy-time-queue-driver/src/lib.rs b/embassy-time-queue-driver/src/lib.rs index 50736e8c7..c5e989854 100644 --- a/embassy-time-queue-driver/src/lib.rs +++ b/embassy-time-queue-driver/src/lib.rs @@ -6,7 +6,29 @@ //! //! - Define a struct `MyTimerQueue` //! - Implement [`TimerQueue`] for it -//! - Register it as the global timer queue with [`timer_queue_impl`](crate::timer_queue_impl). +//! - Register it as the global timer queue with [`timer_queue_impl`]. +//! - Ensure that you process the timer queue when `schedule_wake` is due. This usually involves +//! waking expired tasks, finding the next expiration time and setting an alarm. +//! +//! If a single global timer queue is sufficient for you, you can use the +//! [`GlobalTimerQueue`] type, which is a wrapper around a global timer queue +//! protected by a critical section. +//! +//! ``` +//! use embassy_time_queue_driver::GlobalTimerQueue; +//! embassy_time_queue_driver::timer_queue_impl!( +//! static TIMER_QUEUE_DRIVER: GlobalTimerQueue +//! = GlobalTimerQueue::new(|next_expiration| todo!("Set an alarm")) +//! ); +//! ``` +//! +//! You can also use the `queue_generic` or the `embassy_executor::raw::timer_queue` modules to +//! implement your own timer queue. These modules contain queue implementations which you can wrap +//! and tailor to your needs. +//! +//! If you are providing an embassy-executor implementation besides a timer queue, you can choose to +//! expose the `integrated-timers` feature in your implementation. This feature stores timer items +//! in the tasks themselves, so you don't need a fixed-size queue or dynamic memory allocation. //! //! ## Example //! @@ -14,7 +36,7 @@ //! use core::task::Waker; //! //! use embassy_time::Instant; -//! use embassy_time::queue::{TimerQueue}; +//! use embassy_time::queue::TimerQueue; //! //! struct MyTimerQueue{}; // not public! //! @@ -26,11 +48,18 @@ //! //! embassy_time_queue_driver::timer_queue_impl!(static QUEUE: MyTimerQueue = MyTimerQueue{}); //! ``` + +pub mod queue_generic; + +use core::cell::RefCell; use core::task::Waker; +use critical_section::Mutex; + /// Timer queue pub trait TimerQueue { /// Schedules a waker in the queue to be awoken at moment `at`. + /// /// If this moment is in the past, the waker might be awoken immediately. fn schedule_wake(&'static self, at: u64, waker: &Waker); } @@ -58,3 +87,106 @@ macro_rules! timer_queue_impl { } }; } + +#[cfg(feature = "integrated-timers")] +type InnerQueue = embassy_executor::raw::timer_queue::TimerQueue; + +#[cfg(not(feature = "integrated-timers"))] +type InnerQueue = queue_generic::Queue; + +/// A timer queue implementation that can be used as a global timer queue. +/// +/// This implementation is not thread-safe, and should be protected by a mutex of some sort. +pub struct GenericTimerQueue bool> { + queue: InnerQueue, + set_alarm: F, +} + +impl bool> GenericTimerQueue { + /// Creates a new timer queue. + /// + /// `set_alarm` is a function that should set the next alarm time. The function should + /// return `true` if the alarm was set, and `false` if the alarm was in the past. + pub const fn new(set_alarm: F) -> Self { + Self { + queue: InnerQueue::new(), + set_alarm, + } + } + + /// Schedules a task to run at a specific time, and returns whether any changes were made. + pub fn schedule_wake(&mut self, at: u64, waker: &core::task::Waker) { + #[cfg(feature = "integrated-timers")] + let waker = embassy_executor::raw::task_from_waker(waker); + + if self.queue.schedule_wake(at, waker) { + self.dispatch() + } + } + + /// Dequeues expired timers and returns the next alarm time. + pub fn next_expiration(&mut self, now: u64) -> u64 { + self.queue.next_expiration(now) + } + + /// Handle the alarm. + /// + /// Call this function when the next alarm is due. + pub fn dispatch(&mut self) { + let mut next_expiration = self.next_expiration(embassy_time_driver::now()); + + while !(self.set_alarm)(next_expiration) { + // next_expiration is in the past, dequeue and find a new expiration + next_expiration = self.next_expiration(next_expiration); + } + } +} + +/// A [`GenericTimerQueue`] protected by a critical section. Directly useable as a [`TimerQueue`]. +pub struct GlobalTimerQueue { + inner: Mutex bool>>>, +} + +impl GlobalTimerQueue { + /// Creates a new timer queue. + /// + /// `set_alarm` is a function that should set the next alarm time. The function should + /// return `true` if the alarm was set, and `false` if the alarm was in the past. + pub const fn new(set_alarm: fn(u64) -> bool) -> Self { + Self { + inner: Mutex::new(RefCell::new(GenericTimerQueue::new(set_alarm))), + } + } + + /// Schedules a task to run at a specific time, and returns whether any changes were made. + pub fn schedule_wake(&self, at: u64, waker: &core::task::Waker) { + critical_section::with(|cs| { + let mut inner = self.inner.borrow_ref_mut(cs); + inner.schedule_wake(at, waker); + }); + } + + /// Dequeues expired timers and returns the next alarm time. + pub fn next_expiration(&self, now: u64) -> u64 { + critical_section::with(|cs| { + let mut inner = self.inner.borrow_ref_mut(cs); + inner.next_expiration(now) + }) + } + + /// Handle the alarm. + /// + /// Call this function when the next alarm is due. + pub fn dispatch(&self) { + critical_section::with(|cs| { + let mut inner = self.inner.borrow_ref_mut(cs); + inner.dispatch() + }) + } +} + +impl TimerQueue for GlobalTimerQueue { + fn schedule_wake(&'static self, at: u64, waker: &Waker) { + GlobalTimerQueue::schedule_wake(self, at, waker) + } +} diff --git a/embassy-time-queue-driver/src/queue_generic.rs b/embassy-time-queue-driver/src/queue_generic.rs new file mode 100644 index 000000000..232035bc6 --- /dev/null +++ b/embassy-time-queue-driver/src/queue_generic.rs @@ -0,0 +1,146 @@ +//! Generic timer queue implementations. +//! +//! Time queue drivers may use this to simplify their implementation. + +use core::cmp::{min, Ordering}; +use core::task::Waker; + +use heapless::Vec; + +#[derive(Debug)] +struct Timer { + at: u64, + waker: Waker, +} + +impl PartialEq for Timer { + fn eq(&self, other: &Self) -> bool { + self.at == other.at + } +} + +impl Eq for Timer {} + +impl PartialOrd for Timer { + fn partial_cmp(&self, other: &Self) -> Option { + self.at.partial_cmp(&other.at) + } +} + +impl Ord for Timer { + fn cmp(&self, other: &Self) -> Ordering { + self.at.cmp(&other.at) + } +} + +/// A timer queue with a pre-determined capacity. +pub struct ConstGenericQueue { + queue: Vec, +} + +impl ConstGenericQueue { + /// Creates a new timer queue. + pub const fn new() -> Self { + Self { queue: Vec::new() } + } + + /// Schedules a task to run at a specific time, and returns whether any changes were made. + /// + /// If this function returns `true`, the called should find the next expiration time and set + /// a new alarm for that time. + pub fn schedule_wake(&mut self, at: u64, waker: &Waker) -> bool { + self.queue + .iter_mut() + .find(|timer| timer.waker.will_wake(waker)) + .map(|timer| { + if timer.at > at { + timer.at = at; + true + } else { + false + } + }) + .unwrap_or_else(|| { + let mut timer = Timer { + waker: waker.clone(), + at, + }; + + loop { + match self.queue.push(timer) { + Ok(()) => break, + Err(e) => timer = e, + } + + self.queue.pop().unwrap().waker.wake(); + } + + true + }) + } + + /// Dequeues expired timers and returns the next alarm time. + pub fn next_expiration(&mut self, now: u64) -> u64 { + let mut next_alarm = u64::MAX; + + let mut i = 0; + while i < self.queue.len() { + let timer = &self.queue[i]; + if timer.at <= now { + let timer = self.queue.swap_remove(i); + timer.waker.wake(); + } else { + next_alarm = min(next_alarm, timer.at); + i += 1; + } + } + + next_alarm + } +} + +#[cfg(feature = "generic-queue-8")] +const QUEUE_SIZE: usize = 8; +#[cfg(feature = "generic-queue-16")] +const QUEUE_SIZE: usize = 16; +#[cfg(feature = "generic-queue-32")] +const QUEUE_SIZE: usize = 32; +#[cfg(feature = "generic-queue-64")] +const QUEUE_SIZE: usize = 64; +#[cfg(feature = "generic-queue-128")] +const QUEUE_SIZE: usize = 128; +#[cfg(not(any( + feature = "generic-queue-8", + feature = "generic-queue-16", + feature = "generic-queue-32", + feature = "generic-queue-64", + feature = "generic-queue-128" +)))] +const QUEUE_SIZE: usize = 64; + +/// A timer queue with a pre-determined capacity. +pub struct Queue { + queue: ConstGenericQueue, +} + +impl Queue { + /// Creates a new timer queue. + pub const fn new() -> Self { + Self { + queue: ConstGenericQueue::new(), + } + } + + /// Schedules a task to run at a specific time, and returns whether any changes were made. + /// + /// If this function returns `true`, the called should find the next expiration time and set + /// a new alarm for that time. + pub fn schedule_wake(&mut self, at: u64, waker: &Waker) -> bool { + self.queue.schedule_wake(at, waker) + } + + /// Dequeues expired timers and returns the next alarm time. + pub fn next_expiration(&mut self, now: u64) -> u64 { + self.queue.next_expiration(now) + } +} diff --git a/embassy-time/Cargo.toml b/embassy-time/Cargo.toml index 8c7de9840..e3074119f 100644 --- a/embassy-time/Cargo.toml +++ b/embassy-time/Cargo.toml @@ -42,29 +42,6 @@ defmt-timestamp-uptime-tus = ["defmt"] ## Create a `MockDriver` that can be manually advanced for testing purposes. mock-driver = ["tick-hz-1_000_000"] -#! ### Generic Queue - -## Create a global, generic queue that can be used with any executor. -## To use this you must have a time driver provided. -generic-queue = [] - -#! The following features set how many timers are used for the generic queue. At most one -#! `generic-queue-*` feature can be enabled. If none is enabled, a default of 64 timers is used. -#! -#! When using embassy-time from libraries, you should *not* enable any `generic-queue-*` feature, to allow the -#! end user to pick. - -## Generic Queue with 8 timers -generic-queue-8 = ["generic-queue"] -## Generic Queue with 16 timers -generic-queue-16 = ["generic-queue"] -## Generic Queue with 32 timers -generic-queue-32 = ["generic-queue"] -## Generic Queue with 64 timers -generic-queue-64 = ["generic-queue"] -## Generic Queue with 128 timers -generic-queue-128 = ["generic-queue"] - #! ### Tick Rate #! #! At most 1 `tick-*` feature can be enabled. If none is enabled, a default of 1MHz is used. @@ -419,7 +396,6 @@ embedded-hal-async = { version = "1.0" } futures-util = { version = "0.3.17", default-features = false } critical-section = "1.1" cfg-if = "1.0.0" -heapless = "0.8" document-features = "0.2.7" diff --git a/embassy-time/src/driver_mock.rs b/embassy-time/src/driver_mock.rs index 8587f9172..829eb0437 100644 --- a/embassy-time/src/driver_mock.rs +++ b/embassy-time/src/driver_mock.rs @@ -1,7 +1,7 @@ use core::cell::RefCell; use critical_section::Mutex as CsMutex; -use embassy_time_driver::{AlarmHandle, Driver}; +use embassy_time_driver::Driver; use crate::{Duration, Instant}; @@ -60,15 +60,13 @@ impl MockDriver { let now = inner.now.as_ticks(); - inner - .alarm - .as_mut() - .filter(|alarm| alarm.timestamp <= now) - .map(|alarm| { - alarm.timestamp = u64::MAX; + if inner.alarm.timestamp <= now { + inner.alarm.timestamp = u64::MAX; - (alarm.callback, alarm.ctx) - }) + Some((inner.alarm.callback, inner.alarm.ctx)) + } else { + None + } }) }; @@ -76,68 +74,48 @@ impl MockDriver { (callback)(ctx); } } -} -impl Driver for MockDriver { - fn now(&self) -> u64 { - critical_section::with(|cs| self.0.borrow_ref(cs).now).as_ticks() - } - - unsafe fn allocate_alarm(&self) -> Option { + /// Configures a callback to be called when the alarm fires. + pub fn set_alarm_callback(&self, callback: fn(*mut ()), ctx: *mut ()) { critical_section::with(|cs| { let mut inner = self.0.borrow_ref_mut(cs); - if inner.alarm.is_some() { - None - } else { - inner.alarm.replace(AlarmState::new()); - - Some(AlarmHandle::new(0)) - } - }) - } - - fn set_alarm_callback(&self, _alarm: AlarmHandle, callback: fn(*mut ()), ctx: *mut ()) { - critical_section::with(|cs| { - let mut inner = self.0.borrow_ref_mut(cs); - - let Some(alarm) = inner.alarm.as_mut() else { - panic!("Alarm not allocated"); - }; - - alarm.callback = callback; - alarm.ctx = ctx; + inner.alarm.callback = callback; + inner.alarm.ctx = ctx; }); } - fn set_alarm(&self, _alarm: AlarmHandle, timestamp: u64) -> bool { + /// Sets the alarm to fire at the specified timestamp. + pub fn set_alarm(&self, timestamp: u64) -> bool { critical_section::with(|cs| { let mut inner = self.0.borrow_ref_mut(cs); if timestamp <= inner.now.as_ticks() { false } else { - let Some(alarm) = inner.alarm.as_mut() else { - panic!("Alarm not allocated"); - }; - - alarm.timestamp = timestamp; + inner.alarm.timestamp = timestamp; true } }) } } +impl Driver for MockDriver { + fn now(&self) -> u64 { + critical_section::with(|cs| self.0.borrow_ref(cs).now).as_ticks() + } +} + struct InnerMockDriver { now: Instant, - alarm: Option, + alarm: AlarmState, } impl InnerMockDriver { const fn new() -> Self { Self { now: Instant::from_ticks(0), - alarm: None, + alarm: AlarmState::new(), } } } @@ -189,8 +167,7 @@ mod tests { setup(); let driver = MockDriver::get(); - let alarm = unsafe { AlarmHandle::new(0) }; - assert_eq!(false, driver.set_alarm(alarm, driver.now())); + assert_eq!(false, driver.set_alarm(driver.now())); } #[test] @@ -199,23 +176,11 @@ mod tests { setup(); let driver = MockDriver::get(); - let alarm = unsafe { driver.allocate_alarm() }.expect("No alarms available"); static mut CALLBACK_CALLED: bool = false; - let ctx = &mut () as *mut (); - driver.set_alarm_callback(alarm, |_| unsafe { CALLBACK_CALLED = true }, ctx); - driver.set_alarm(alarm, driver.now() + 1); + driver.set_alarm_callback(|_| unsafe { CALLBACK_CALLED = true }, core::ptr::null_mut()); + driver.set_alarm(driver.now() + 1); assert_eq!(false, unsafe { CALLBACK_CALLED }); driver.advance(Duration::from_secs(1)); assert_eq!(true, unsafe { CALLBACK_CALLED }); } - - #[test] - #[serial] - fn test_allocate_alarm() { - setup(); - - let driver = MockDriver::get(); - assert!(unsafe { driver.allocate_alarm() }.is_some()); - assert!(unsafe { driver.allocate_alarm() }.is_none()); - } } diff --git a/embassy-time/src/driver_std.rs b/embassy-time/src/driver_std.rs index cbef7aae1..45467f09b 100644 --- a/embassy-time/src/driver_std.rs +++ b/embassy-time/src/driver_std.rs @@ -1,53 +1,38 @@ -use core::sync::atomic::{AtomicU8, Ordering}; use std::cell::{RefCell, UnsafeCell}; use std::mem::MaybeUninit; use std::sync::{Condvar, Mutex, Once}; use std::time::{Duration as StdDuration, Instant as StdInstant}; -use std::{mem, ptr, thread}; +use std::{ptr, thread}; use critical_section::Mutex as CsMutex; -use embassy_time_driver::{AlarmHandle, Driver}; - -const ALARM_COUNT: usize = 4; +use embassy_time_driver::Driver; +use embassy_time_queue_driver::GlobalTimerQueue; struct AlarmState { timestamp: u64, - - // This is really a Option<(fn(*mut ()), *mut ())> - // but fn pointers aren't allowed in const yet - callback: *const (), - ctx: *mut (), } unsafe impl Send for AlarmState {} impl AlarmState { const fn new() -> Self { - Self { - timestamp: u64::MAX, - callback: ptr::null(), - ctx: ptr::null_mut(), - } + Self { timestamp: u64::MAX } } } struct TimeDriver { - alarm_count: AtomicU8, - once: Once, - // The STD Driver implementation requires the alarms' mutex to be reentrant, which the STD Mutex isn't + // The STD Driver implementation requires the alarm's mutex to be reentrant, which the STD Mutex isn't // Fortunately, mutexes based on the `critical-section` crate are reentrant, because the critical sections // themselves are reentrant - alarms: UninitCell>>, + alarm: UninitCell>>, zero_instant: UninitCell, signaler: UninitCell, } embassy_time_driver::time_driver_impl!(static DRIVER: TimeDriver = TimeDriver { - alarm_count: AtomicU8::new(0), - once: Once::new(), - alarms: UninitCell::uninit(), + alarm: UninitCell::uninit(), zero_instant: UninitCell::uninit(), signaler: UninitCell::uninit(), }); @@ -55,8 +40,8 @@ embassy_time_driver::time_driver_impl!(static DRIVER: TimeDriver = TimeDriver { impl TimeDriver { fn init(&self) { self.once.call_once(|| unsafe { - self.alarms - .write(CsMutex::new(RefCell::new([const { AlarmState::new() }; ALARM_COUNT]))); + self.alarm + .write(CsMutex::new(RefCell::new(const { AlarmState::new() }))); self.zero_instant.write(StdInstant::now()); self.signaler.write(Signaler::new()); @@ -70,36 +55,13 @@ impl TimeDriver { let now = DRIVER.now(); let next_alarm = critical_section::with(|cs| { - let alarms = unsafe { DRIVER.alarms.as_ref() }.borrow(cs); - loop { - let pending = alarms - .borrow_mut() - .iter_mut() - .find(|alarm| alarm.timestamp <= now) - .map(|alarm| { - alarm.timestamp = u64::MAX; + let mut alarm = unsafe { DRIVER.alarm.as_ref() }.borrow_ref_mut(cs); + if alarm.timestamp <= now { + alarm.timestamp = u64::MAX; - (alarm.callback, alarm.ctx) - }); - - if let Some((callback, ctx)) = pending { - // safety: - // - we can ignore the possiblity of `f` being unset (null) because of the safety contract of `allocate_alarm`. - // - other than that we only store valid function pointers into alarm.callback - let f: fn(*mut ()) = unsafe { mem::transmute(callback) }; - f(ctx); - } else { - // No alarm due - break; - } + TIMER_QUEUE_DRIVER.dispatch(); } - - alarms - .borrow() - .iter() - .map(|alarm| alarm.timestamp) - .min() - .unwrap_or(u64::MAX) + alarm.timestamp }); // Ensure we don't overflow @@ -110,6 +72,17 @@ impl TimeDriver { unsafe { DRIVER.signaler.as_ref() }.wait_until(until); } } + + fn set_alarm(&self, timestamp: u64) -> bool { + self.init(); + critical_section::with(|cs| { + let mut alarm = unsafe { self.alarm.as_ref() }.borrow_ref_mut(cs); + alarm.timestamp = timestamp; + unsafe { self.signaler.as_ref() }.signal(); + }); + + true + } } impl Driver for TimeDriver { @@ -119,43 +92,6 @@ impl Driver for TimeDriver { let zero = unsafe { self.zero_instant.read() }; StdInstant::now().duration_since(zero).as_micros() as u64 } - - unsafe fn allocate_alarm(&self) -> Option { - let id = self.alarm_count.fetch_update(Ordering::AcqRel, Ordering::Acquire, |x| { - if x < ALARM_COUNT as u8 { - Some(x + 1) - } else { - None - } - }); - - match id { - Ok(id) => Some(AlarmHandle::new(id)), - Err(_) => None, - } - } - - fn set_alarm_callback(&self, alarm: AlarmHandle, callback: fn(*mut ()), ctx: *mut ()) { - self.init(); - critical_section::with(|cs| { - let mut alarms = unsafe { self.alarms.as_ref() }.borrow_ref_mut(cs); - let alarm = &mut alarms[alarm.id() as usize]; - alarm.callback = callback as *const (); - alarm.ctx = ctx; - }); - } - - fn set_alarm(&self, alarm: AlarmHandle, timestamp: u64) -> bool { - self.init(); - critical_section::with(|cs| { - let mut alarms = unsafe { self.alarms.as_ref() }.borrow_ref_mut(cs); - let alarm = &mut alarms[alarm.id() as usize]; - alarm.timestamp = timestamp; - unsafe { self.signaler.as_ref() }.signal(); - }); - - true - } } struct Signaler { @@ -228,3 +164,8 @@ impl UninitCell { ptr::read(self.as_mut_ptr()) } } + +embassy_time_queue_driver::timer_queue_impl!( + static TIMER_QUEUE_DRIVER: GlobalTimerQueue + = GlobalTimerQueue::new(|next_expiration| DRIVER.set_alarm(next_expiration)) +); diff --git a/embassy-time/src/driver_wasm.rs b/embassy-time/src/driver_wasm.rs index d65629e49..dcc935fde 100644 --- a/embassy-time/src/driver_wasm.rs +++ b/embassy-time/src/driver_wasm.rs @@ -1,28 +1,22 @@ -use core::sync::atomic::{AtomicU8, Ordering}; use std::cell::UnsafeCell; use std::mem::MaybeUninit; use std::ptr; use std::sync::{Mutex, Once}; -use embassy_time_driver::{AlarmHandle, Driver}; +use embassy_time_driver::Driver; +use embassy_time_queue_driver::GlobalTimerQueue; use wasm_bindgen::prelude::*; use wasm_timer::Instant as StdInstant; -const ALARM_COUNT: usize = 4; - struct AlarmState { token: Option, - closure: Option>, } unsafe impl Send for AlarmState {} impl AlarmState { const fn new() -> Self { - Self { - token: None, - closure: None, - } + Self { token: None } } } @@ -33,66 +27,32 @@ extern "C" { } struct TimeDriver { - alarm_count: AtomicU8, - once: Once, - alarms: UninitCell>, + alarm: UninitCell>, zero_instant: UninitCell, + closure: UninitCell>, } embassy_time_driver::time_driver_impl!(static DRIVER: TimeDriver = TimeDriver { - alarm_count: AtomicU8::new(0), once: Once::new(), - alarms: UninitCell::uninit(), + alarm: UninitCell::uninit(), zero_instant: UninitCell::uninit(), + closure: UninitCell::uninit() }); impl TimeDriver { fn init(&self) { self.once.call_once(|| unsafe { - self.alarms - .write(Mutex::new([const { AlarmState::new() }; ALARM_COUNT])); + self.alarm.write(Mutex::new(const { AlarmState::new() })); self.zero_instant.write(StdInstant::now()); + self.closure + .write(Closure::new(Box::new(|| TIMER_QUEUE_DRIVER.dispatch()))); }); } -} -impl Driver for TimeDriver { - fn now(&self) -> u64 { + fn set_alarm(&self, timestamp: u64) -> bool { self.init(); - - let zero = unsafe { self.zero_instant.read() }; - StdInstant::now().duration_since(zero).as_micros() as u64 - } - - unsafe fn allocate_alarm(&self) -> Option { - let id = self.alarm_count.fetch_update(Ordering::AcqRel, Ordering::Acquire, |x| { - if x < ALARM_COUNT as u8 { - Some(x + 1) - } else { - None - } - }); - - match id { - Ok(id) => Some(AlarmHandle::new(id)), - Err(_) => None, - } - } - - fn set_alarm_callback(&self, alarm: AlarmHandle, callback: fn(*mut ()), ctx: *mut ()) { - self.init(); - let mut alarms = unsafe { self.alarms.as_ref() }.lock().unwrap(); - let alarm = &mut alarms[alarm.id() as usize]; - alarm.closure.replace(Closure::new(move || { - callback(ctx); - })); - } - - fn set_alarm(&self, alarm: AlarmHandle, timestamp: u64) -> bool { - self.init(); - let mut alarms = unsafe { self.alarms.as_ref() }.lock().unwrap(); - let alarm = &mut alarms[alarm.id() as usize]; + let mut alarm = unsafe { self.alarm.as_ref() }.lock().unwrap(); if let Some(token) = alarm.token { clearTimeout(token); } @@ -102,13 +62,22 @@ impl Driver for TimeDriver { false } else { let timeout = (timestamp - now) as u32; - alarm.token = Some(setTimeout(alarm.closure.as_ref().unwrap(), timeout / 1000)); + alarm.token = Some(setTimeout(unsafe { self.closure.as_ref() }, timeout / 1000)); true } } } +impl Driver for TimeDriver { + fn now(&self) -> u64 { + self.init(); + + let zero = unsafe { self.zero_instant.read() }; + StdInstant::now().duration_since(zero).as_micros() as u64 + } +} + pub(crate) struct UninitCell(MaybeUninit>); unsafe impl Send for UninitCell {} unsafe impl Sync for UninitCell {} @@ -139,3 +108,8 @@ impl UninitCell { ptr::read(self.as_mut_ptr()) } } + +embassy_time_queue_driver::timer_queue_impl!( + static TIMER_QUEUE_DRIVER: GlobalTimerQueue + = GlobalTimerQueue::new(|next_expiration| DRIVER.set_alarm(next_expiration)) +); diff --git a/embassy-time/src/lib.rs b/embassy-time/src/lib.rs index 8d0648ce5..80a359413 100644 --- a/embassy-time/src/lib.rs +++ b/embassy-time/src/lib.rs @@ -25,8 +25,6 @@ pub use driver_mock::MockDriver; mod driver_std; #[cfg(feature = "wasm")] mod driver_wasm; -#[cfg(feature = "generic-queue")] -mod queue_generic; pub use delay::{block_for, Delay}; pub use duration::Duration; diff --git a/embassy-time/src/queue_generic.rs b/embassy-time/src/queue_generic.rs deleted file mode 100644 index 0068edae8..000000000 --- a/embassy-time/src/queue_generic.rs +++ /dev/null @@ -1,346 +0,0 @@ -use core::cell::RefCell; -use core::cmp::{min, Ordering}; -use core::task::Waker; - -use critical_section::Mutex; -use embassy_time_driver::{allocate_alarm, set_alarm, set_alarm_callback, AlarmHandle}; -use embassy_time_queue_driver::TimerQueue; -use heapless::Vec; - -use crate::Instant; - -#[cfg(feature = "generic-queue-8")] -const QUEUE_SIZE: usize = 8; -#[cfg(feature = "generic-queue-16")] -const QUEUE_SIZE: usize = 16; -#[cfg(feature = "generic-queue-32")] -const QUEUE_SIZE: usize = 32; -#[cfg(feature = "generic-queue-64")] -const QUEUE_SIZE: usize = 64; -#[cfg(feature = "generic-queue-128")] -const QUEUE_SIZE: usize = 128; -#[cfg(not(any( - feature = "generic-queue-8", - feature = "generic-queue-16", - feature = "generic-queue-32", - feature = "generic-queue-64", - feature = "generic-queue-128" -)))] -const QUEUE_SIZE: usize = 64; - -#[derive(Debug)] -struct Timer { - at: Instant, - waker: Waker, -} - -impl PartialEq for Timer { - fn eq(&self, other: &Self) -> bool { - self.at == other.at - } -} - -impl Eq for Timer {} - -impl PartialOrd for Timer { - fn partial_cmp(&self, other: &Self) -> Option { - self.at.partial_cmp(&other.at) - } -} - -impl Ord for Timer { - fn cmp(&self, other: &Self) -> Ordering { - self.at.cmp(&other.at) - } -} - -struct InnerQueue { - queue: Vec, - alarm: AlarmHandle, -} - -impl InnerQueue { - fn schedule_wake(&mut self, at: Instant, waker: &Waker) { - self.queue - .iter_mut() - .find(|timer| timer.waker.will_wake(waker)) - .map(|timer| { - timer.at = min(timer.at, at); - }) - .unwrap_or_else(|| { - let mut timer = Timer { - waker: waker.clone(), - at, - }; - - loop { - match self.queue.push(timer) { - Ok(()) => break, - Err(e) => timer = e, - } - - self.queue.pop().unwrap().waker.wake(); - } - }); - - // Don't wait for the alarm callback to trigger and directly - // dispatch all timers that are already due - // - // Then update the alarm if necessary - self.dispatch(); - } - - fn dispatch(&mut self) { - loop { - let now = Instant::now(); - - let mut next_alarm = Instant::MAX; - - let mut i = 0; - while i < self.queue.len() { - let timer = &self.queue[i]; - if timer.at <= now { - let timer = self.queue.swap_remove(i); - timer.waker.wake(); - } else { - next_alarm = min(next_alarm, timer.at); - i += 1; - } - } - - if self.update_alarm(next_alarm) { - break; - } - } - } - - fn update_alarm(&mut self, next_alarm: Instant) -> bool { - if next_alarm == Instant::MAX { - true - } else { - set_alarm(self.alarm, next_alarm.as_ticks()) - } - } - - fn handle_alarm(&mut self) { - self.dispatch(); - } -} - -struct Queue { - inner: Mutex>>, -} - -impl Queue { - const fn new() -> Self { - Self { - inner: Mutex::new(RefCell::new(None)), - } - } - - fn schedule_wake(&'static self, at: Instant, waker: &Waker) { - critical_section::with(|cs| { - let mut inner = self.inner.borrow_ref_mut(cs); - - inner - .get_or_insert_with(|| { - let handle = unsafe { allocate_alarm() }.unwrap(); - set_alarm_callback(handle, Self::handle_alarm_callback, self as *const _ as _); - InnerQueue { - queue: Vec::new(), - alarm: handle, - } - }) - .schedule_wake(at, waker) - }); - } - - fn handle_alarm(&self) { - critical_section::with(|cs| self.inner.borrow_ref_mut(cs).as_mut().unwrap().handle_alarm()) - } - - fn handle_alarm_callback(ctx: *mut ()) { - unsafe { (ctx as *const Self).as_ref().unwrap() }.handle_alarm(); - } -} - -impl TimerQueue for Queue { - fn schedule_wake(&'static self, at: u64, waker: &Waker) { - Queue::schedule_wake(self, Instant::from_ticks(at), waker); - } -} - -embassy_time_queue_driver::timer_queue_impl!(static QUEUE: Queue = Queue::new()); - -#[cfg(test)] -#[cfg(feature = "mock-driver")] -mod tests { - use core::sync::atomic::{AtomicBool, Ordering}; - use core::task::Waker; - use std::sync::Arc; - use std::task::Wake; - - use serial_test::serial; - - use crate::driver_mock::MockDriver; - use crate::queue_generic::QUEUE; - use crate::{Duration, Instant}; - - struct TestWaker { - pub awoken: AtomicBool, - } - - impl Wake for TestWaker { - fn wake(self: Arc) { - self.awoken.store(true, Ordering::Relaxed); - } - - fn wake_by_ref(self: &Arc) { - self.awoken.store(true, Ordering::Relaxed); - } - } - - fn test_waker() -> (Arc, Waker) { - let arc = Arc::new(TestWaker { - awoken: AtomicBool::new(false), - }); - let waker = Waker::from(arc.clone()); - - (arc, waker) - } - - fn setup() { - MockDriver::get().reset(); - critical_section::with(|cs| *QUEUE.inner.borrow_ref_mut(cs) = None); - } - - fn queue_len() -> usize { - critical_section::with(|cs| { - QUEUE - .inner - .borrow_ref(cs) - .as_ref() - .map(|inner| inner.queue.iter().count()) - .unwrap_or(0) - }) - } - - #[test] - #[serial] - fn test_schedule() { - setup(); - - assert_eq!(queue_len(), 0); - - let (flag, waker) = test_waker(); - - QUEUE.schedule_wake(Instant::from_secs(1), &waker); - - assert!(!flag.awoken.load(Ordering::Relaxed)); - assert_eq!(queue_len(), 1); - } - - #[test] - #[serial] - fn test_schedule_same() { - setup(); - - let (_flag, waker) = test_waker(); - - QUEUE.schedule_wake(Instant::from_secs(1), &waker); - - assert_eq!(queue_len(), 1); - - QUEUE.schedule_wake(Instant::from_secs(1), &waker); - - assert_eq!(queue_len(), 1); - - QUEUE.schedule_wake(Instant::from_secs(100), &waker); - - assert_eq!(queue_len(), 1); - - let (_flag2, waker2) = test_waker(); - - QUEUE.schedule_wake(Instant::from_secs(100), &waker2); - - assert_eq!(queue_len(), 2); - } - - #[test] - #[serial] - fn test_trigger() { - setup(); - - let (flag, waker) = test_waker(); - - QUEUE.schedule_wake(Instant::from_secs(100), &waker); - - assert!(!flag.awoken.load(Ordering::Relaxed)); - - MockDriver::get().advance(Duration::from_secs(99)); - - assert!(!flag.awoken.load(Ordering::Relaxed)); - - assert_eq!(queue_len(), 1); - - MockDriver::get().advance(Duration::from_secs(1)); - - assert!(flag.awoken.load(Ordering::Relaxed)); - - assert_eq!(queue_len(), 0); - } - - #[test] - #[serial] - fn test_immediate_trigger() { - setup(); - - let (flag, waker) = test_waker(); - - QUEUE.schedule_wake(Instant::from_secs(100), &waker); - - MockDriver::get().advance(Duration::from_secs(50)); - - let (flag2, waker2) = test_waker(); - - QUEUE.schedule_wake(Instant::from_secs(40), &waker2); - - assert!(!flag.awoken.load(Ordering::Relaxed)); - assert!(flag2.awoken.load(Ordering::Relaxed)); - assert_eq!(queue_len(), 1); - } - - #[test] - #[serial] - fn test_queue_overflow() { - setup(); - - for i in 1..super::QUEUE_SIZE { - let (flag, waker) = test_waker(); - - QUEUE.schedule_wake(Instant::from_secs(310), &waker); - - assert_eq!(queue_len(), i); - assert!(!flag.awoken.load(Ordering::Relaxed)); - } - - let (flag, waker) = test_waker(); - - QUEUE.schedule_wake(Instant::from_secs(300), &waker); - - assert_eq!(queue_len(), super::QUEUE_SIZE); - assert!(!flag.awoken.load(Ordering::Relaxed)); - - let (flag2, waker2) = test_waker(); - - QUEUE.schedule_wake(Instant::from_secs(305), &waker2); - - assert_eq!(queue_len(), super::QUEUE_SIZE); - assert!(flag.awoken.load(Ordering::Relaxed)); - - let (_flag3, waker3) = test_waker(); - QUEUE.schedule_wake(Instant::from_secs(320), &waker3); - assert_eq!(queue_len(), super::QUEUE_SIZE); - assert!(flag2.awoken.load(Ordering::Relaxed)); - } -} diff --git a/examples/nrf52840-rtic/Cargo.toml b/examples/nrf52840-rtic/Cargo.toml index 290b2fdb1..326355dd6 100644 --- a/examples/nrf52840-rtic/Cargo.toml +++ b/examples/nrf52840-rtic/Cargo.toml @@ -9,7 +9,8 @@ rtic = { version = "2", features = ["thumbv7-backend"] } embassy-futures = { version = "0.1.0", path = "../../embassy-futures" } embassy-sync = { version = "0.6.1", path = "../../embassy-sync", features = ["defmt"] } -embassy-time = { version = "0.3.2", path = "../../embassy-time", features = [ "defmt", "defmt-timestamp-uptime", "generic-queue"] } +embassy-time = { version = "0.3.2", path = "../../embassy-time", features = [ "defmt", "defmt-timestamp-uptime"] } +embassy-time-queue-driver = { version = "0.1.0", path = "../../embassy-time-queue-driver" } embassy-nrf = { version = "0.2.0", path = "../../embassy-nrf", features = [ "defmt", "nrf52840", "time-driver-rtc1", "gpiote", "unstable-pac", "time"] } defmt = "0.3" From 6cc8709ecc9e8f71a13ec62b42be52bc8adf2c7b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?D=C3=A1niel=20Buga?= Date: Sun, 8 Dec 2024 20:05:37 +0100 Subject: [PATCH 312/361] Changelog --- embassy-executor/CHANGELOG.md | 5 ++++- embassy-time-driver/CHANGELOG.md | 14 ++++++++++++++ embassy-time-queue-driver/CHANGELOG.md | 16 ++++++++++++++++ embassy-time/CHANGELOG.md | 5 ++++- 4 files changed, 38 insertions(+), 2 deletions(-) create mode 100644 embassy-time-driver/CHANGELOG.md create mode 100644 embassy-time-queue-driver/CHANGELOG.md diff --git a/embassy-executor/CHANGELOG.md b/embassy-executor/CHANGELOG.md index 00b1bef28..1aef57a70 100644 --- a/embassy-executor/CHANGELOG.md +++ b/embassy-executor/CHANGELOG.md @@ -7,7 +7,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## Unreleased -- `raw::Executor` now has an `fn initialize` that must be called once before starting to poll it. +- embassy-executor no longer provides an `embassy-time-queue-driver` implementation +- Added `TaskRef::executor` to obtain a reference to a task's executor +- integrated-timers are no longer processed when polling the executor. +- `raw::timer_queue::TimerQueue` is now public. ## 0.6.3 - 2024-11-12 diff --git a/embassy-time-driver/CHANGELOG.md b/embassy-time-driver/CHANGELOG.md new file mode 100644 index 000000000..ebc37b6f4 --- /dev/null +++ b/embassy-time-driver/CHANGELOG.md @@ -0,0 +1,14 @@ +# Changelog for embassy-time-queue-driver + +All notable changes to this project will be documented in this file. + +The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), +and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). + +## Unreleased + +- The `allocate_alarm`, `set_alarm_callback`, `set_alarm` functions have been removed. + +## 0.1.0 - 2024-01-11 + +Initial release diff --git a/embassy-time-queue-driver/CHANGELOG.md b/embassy-time-queue-driver/CHANGELOG.md new file mode 100644 index 000000000..3b2aa8695 --- /dev/null +++ b/embassy-time-queue-driver/CHANGELOG.md @@ -0,0 +1,16 @@ +# Changelog for embassy-time-queue-driver + +All notable changes to this project will be documented in this file. + +The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), +and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). + +## Unreleased + +- Added `integrated-timers` and `generic-queue-N` features +- Added `queue_generic` module which contains `Queue` (configured via the `generic-queue-N` features) and `ConstGenericQueue`. +- Added `GenericTimerQueue` and `GlobalTimerQueue` structs that can be used to implement timer queues. + +## 0.1.0 - 2024-01-11 + +Initial release diff --git a/embassy-time/CHANGELOG.md b/embassy-time/CHANGELOG.md index 3b4d93387..a6acb1ad6 100644 --- a/embassy-time/CHANGELOG.md +++ b/embassy-time/CHANGELOG.md @@ -7,10 +7,13 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## Unreleased +- The `generic-queue` and related features have been removed (moved to embassy-time-queue-driver) +- embassy-time no longer provides an `embassy-time-queue-driver` implementation + ## 0.3.2 - 2024-08-05 - Implement with_timeout()/with_deadline() method style call on Future -- Add collapse_debuginfo to fmt.rs macros. +- Add collapse_debuginfo to fmt.rs macros. ## 0.3.1 - 2024-01-11 From 12f58fbcfd3f10b43795936127a890c6a0f8f280 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?D=C3=A1niel=20Buga?= Date: Sun, 8 Dec 2024 23:04:43 +0100 Subject: [PATCH 313/361] Remove TIMER_QUEUED state --- embassy-executor/src/raw/state_atomics.rs | 18 ---------------- embassy-executor/src/raw/state_atomics_arm.rs | 19 ++--------------- .../src/raw/state_critical_section.rs | 21 ------------------- embassy-executor/src/raw/timer_queue.rs | 4 ++-- 4 files changed, 4 insertions(+), 58 deletions(-) diff --git a/embassy-executor/src/raw/state_atomics.rs b/embassy-executor/src/raw/state_atomics.rs index e1279ac0b..e4127897e 100644 --- a/embassy-executor/src/raw/state_atomics.rs +++ b/embassy-executor/src/raw/state_atomics.rs @@ -4,9 +4,6 @@ use core::sync::atomic::{AtomicU32, Ordering}; pub(crate) const STATE_SPAWNED: u32 = 1 << 0; /// Task is in the executor run queue pub(crate) const STATE_RUN_QUEUED: u32 = 1 << 1; -/// Task is in the executor timer queue -#[cfg(feature = "integrated-timers")] -pub(crate) const STATE_TIMER_QUEUED: u32 = 1 << 2; pub(crate) struct State { state: AtomicU32, @@ -55,19 +52,4 @@ impl State { let state = self.state.fetch_and(!STATE_RUN_QUEUED, Ordering::AcqRel); state & STATE_SPAWNED != 0 } - - /// Mark the task as timer-queued. Return whether it was newly queued (i.e. not queued before) - #[cfg(feature = "integrated-timers")] - #[inline(always)] - pub fn timer_enqueue(&self) -> bool { - let old_state = self.state.fetch_or(STATE_TIMER_QUEUED, Ordering::AcqRel); - old_state & STATE_TIMER_QUEUED == 0 - } - - /// Unmark the task as timer-queued. - #[cfg(feature = "integrated-timers")] - #[inline(always)] - pub fn timer_dequeue(&self) { - self.state.fetch_and(!STATE_TIMER_QUEUED, Ordering::AcqRel); - } } diff --git a/embassy-executor/src/raw/state_atomics_arm.rs b/embassy-executor/src/raw/state_atomics_arm.rs index e4dfe5093..b673c7359 100644 --- a/embassy-executor/src/raw/state_atomics_arm.rs +++ b/embassy-executor/src/raw/state_atomics_arm.rs @@ -11,9 +11,8 @@ pub(crate) struct State { spawned: AtomicBool, /// Task is in the executor run queue run_queued: AtomicBool, - /// Task is in the executor timer queue - timer_queued: AtomicBool, pad: AtomicBool, + pad2: AtomicBool, } impl State { @@ -21,8 +20,8 @@ impl State { Self { spawned: AtomicBool::new(false), run_queued: AtomicBool::new(false), - timer_queued: AtomicBool::new(false), pad: AtomicBool::new(false), + pad2: AtomicBool::new(false), } } @@ -86,18 +85,4 @@ impl State { self.run_queued.store(false, Ordering::Relaxed); r } - - /// Mark the task as timer-queued. Return whether it was newly queued (i.e. not queued before) - #[cfg(feature = "integrated-timers")] - #[inline(always)] - pub fn timer_enqueue(&self) -> bool { - !self.timer_queued.swap(true, Ordering::Relaxed) - } - - /// Unmark the task as timer-queued. - #[cfg(feature = "integrated-timers")] - #[inline(always)] - pub fn timer_dequeue(&self) { - self.timer_queued.store(false, Ordering::Relaxed); - } } diff --git a/embassy-executor/src/raw/state_critical_section.rs b/embassy-executor/src/raw/state_critical_section.rs index c3cc1b0b7..b92eed006 100644 --- a/embassy-executor/src/raw/state_critical_section.rs +++ b/embassy-executor/src/raw/state_critical_section.rs @@ -6,9 +6,6 @@ use critical_section::Mutex; pub(crate) const STATE_SPAWNED: u32 = 1 << 0; /// Task is in the executor run queue pub(crate) const STATE_RUN_QUEUED: u32 = 1 << 1; -/// Task is in the executor timer queue -#[cfg(feature = "integrated-timers")] -pub(crate) const STATE_TIMER_QUEUED: u32 = 1 << 2; pub(crate) struct State { state: Mutex>, @@ -72,22 +69,4 @@ impl State { ok }) } - - /// Mark the task as timer-queued. Return whether it was newly queued (i.e. not queued before) - #[cfg(feature = "integrated-timers")] - #[inline(always)] - pub fn timer_enqueue(&self) -> bool { - self.update(|s| { - let ok = *s & STATE_TIMER_QUEUED == 0; - *s |= STATE_TIMER_QUEUED; - ok - }) - } - - /// Unmark the task as timer-queued. - #[cfg(feature = "integrated-timers")] - #[inline(always)] - pub fn timer_dequeue(&self) { - self.update(|s| *s &= !STATE_TIMER_QUEUED); - } } diff --git a/embassy-executor/src/raw/timer_queue.rs b/embassy-executor/src/raw/timer_queue.rs index 953bf014f..513397090 100644 --- a/embassy-executor/src/raw/timer_queue.rs +++ b/embassy-executor/src/raw/timer_queue.rs @@ -39,7 +39,7 @@ impl TimerQueue { unsafe { let task = p.header(); let item = &task.timer_queue_item; - if task.state.timer_enqueue() { + if item.next.get().is_none() { // If not in the queue, add it and update. let prev = self.head.replace(Some(p)); item.next.set(prev); @@ -93,7 +93,7 @@ impl TimerQueue { } else { // Remove it prev.set(item.next.get()); - task.state.timer_dequeue(); + item.next.set(None); } } } From dc18ee29a0f93ce34892731ee0580a3e9e3f2298 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?D=C3=A1niel=20Buga?= Date: Sun, 8 Dec 2024 23:07:35 +0100 Subject: [PATCH 314/361] Do not access task header --- embassy-executor/src/raw/mod.rs | 6 ++++++ embassy-executor/src/raw/timer_queue.rs | 14 ++++++-------- 2 files changed, 12 insertions(+), 8 deletions(-) diff --git a/embassy-executor/src/raw/mod.rs b/embassy-executor/src/raw/mod.rs index 80bd49bad..f9c6509f1 100644 --- a/embassy-executor/src/raw/mod.rs +++ b/embassy-executor/src/raw/mod.rs @@ -82,6 +82,12 @@ impl TaskRef { self.header().executor.get().map(|e| Executor::wrap(e)) } + /// Returns a reference to the timer queue item. + #[cfg(feature = "integrated-timers")] + pub fn timer_queue_item(&self) -> &'static timer_queue::TimerQueueItem { + &self.header().timer_queue_item + } + /// The returned pointer is valid for the entire TaskStorage. pub(crate) fn as_ptr(self) -> *const TaskHeader { self.ptr.as_ptr() diff --git a/embassy-executor/src/raw/timer_queue.rs b/embassy-executor/src/raw/timer_queue.rs index 513397090..e0a22f4d4 100644 --- a/embassy-executor/src/raw/timer_queue.rs +++ b/embassy-executor/src/raw/timer_queue.rs @@ -4,13 +4,14 @@ use core::cmp::min; use super::util::SyncUnsafeCell; use super::TaskRef; -pub(crate) struct TimerQueueItem { +/// An item in the timer queue. +pub struct TimerQueueItem { next: SyncUnsafeCell>, expires_at: SyncUnsafeCell, } impl TimerQueueItem { - pub const fn new() -> Self { + pub(crate) const fn new() -> Self { Self { next: SyncUnsafeCell::new(None), expires_at: SyncUnsafeCell::new(0), @@ -37,8 +38,7 @@ impl TimerQueue { /// a new alarm for that time. pub fn schedule_wake(&mut self, at: u64, p: TaskRef) -> bool { unsafe { - let task = p.header(); - let item = &task.timer_queue_item; + let item = p.timer_queue_item(); if item.next.get().is_none() { // If not in the queue, add it and update. let prev = self.head.replace(Some(p)); @@ -63,8 +63,7 @@ impl TimerQueue { let mut next_expiration = u64::MAX; self.retain(|p| { - let task = p.header(); - let item = &task.timer_queue_item; + let item = p.timer_queue_item(); let expires = unsafe { item.expires_at.get() }; if expires <= now { @@ -85,8 +84,7 @@ impl TimerQueue { unsafe { let mut prev = &self.head; while let Some(p) = prev.get() { - let task = p.header(); - let item = &task.timer_queue_item; + let item = p.timer_queue_item(); if f(p) { // Skip to next prev = &item.next; From d45ea43892198484b5f6dcea4c351dc11d226cc4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?D=C3=A1niel=20Buga?= Date: Sun, 8 Dec 2024 23:21:53 +0100 Subject: [PATCH 315/361] Move integrated timer queue into time-queue-driver --- embassy-executor/CHANGELOG.md | 1 - embassy-executor/src/raw/timer_queue.rs | 96 +++---------------- embassy-executor/src/raw/util.rs | 5 - embassy-time-queue-driver/src/lib.rs | 11 ++- .../src/queue_integrated.rs | 78 +++++++++++++++ 5 files changed, 96 insertions(+), 95 deletions(-) create mode 100644 embassy-time-queue-driver/src/queue_integrated.rs diff --git a/embassy-executor/CHANGELOG.md b/embassy-executor/CHANGELOG.md index 1aef57a70..068156210 100644 --- a/embassy-executor/CHANGELOG.md +++ b/embassy-executor/CHANGELOG.md @@ -10,7 +10,6 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - embassy-executor no longer provides an `embassy-time-queue-driver` implementation - Added `TaskRef::executor` to obtain a reference to a task's executor - integrated-timers are no longer processed when polling the executor. -- `raw::timer_queue::TimerQueue` is now public. ## 0.6.3 - 2024-11-12 diff --git a/embassy-executor/src/raw/timer_queue.rs b/embassy-executor/src/raw/timer_queue.rs index e0a22f4d4..46e346c1b 100644 --- a/embassy-executor/src/raw/timer_queue.rs +++ b/embassy-executor/src/raw/timer_queue.rs @@ -1,99 +1,25 @@ //! Timer queue operations. -use core::cmp::min; -use super::util::SyncUnsafeCell; +use core::cell::Cell; + use super::TaskRef; /// An item in the timer queue. pub struct TimerQueueItem { - next: SyncUnsafeCell>, - expires_at: SyncUnsafeCell, + /// The next item in the queue. + pub next: Cell>, + + /// The time at which this item expires. + pub expires_at: Cell, } +unsafe impl Sync for TimerQueueItem {} + impl TimerQueueItem { pub(crate) const fn new() -> Self { Self { - next: SyncUnsafeCell::new(None), - expires_at: SyncUnsafeCell::new(0), - } - } -} - -/// A timer queue, with items integrated into tasks. -pub struct TimerQueue { - head: SyncUnsafeCell>, -} - -impl TimerQueue { - /// Creates a new timer queue. - pub const fn new() -> Self { - Self { - head: SyncUnsafeCell::new(None), - } - } - - /// Schedules a task to run at a specific time. - /// - /// If this function returns `true`, the called should find the next expiration time and set - /// a new alarm for that time. - pub fn schedule_wake(&mut self, at: u64, p: TaskRef) -> bool { - unsafe { - let item = p.timer_queue_item(); - if item.next.get().is_none() { - // If not in the queue, add it and update. - let prev = self.head.replace(Some(p)); - item.next.set(prev); - } else if at <= item.expires_at.get() { - // If expiration is sooner than previously set, update. - } else { - // Task does not need to be updated. - return false; - } - - item.expires_at.set(at); - true - } - } - - /// Dequeues expired timers and returns the next alarm time. - /// - /// The provided callback will be called for each expired task. Tasks that never expire - /// will be removed, but the callback will not be called. - pub fn next_expiration(&mut self, now: u64) -> u64 { - let mut next_expiration = u64::MAX; - - self.retain(|p| { - let item = p.timer_queue_item(); - let expires = unsafe { item.expires_at.get() }; - - if expires <= now { - // Timer expired, process task. - super::wake_task(p); - false - } else { - // Timer didn't yet expire, or never expires. - next_expiration = min(next_expiration, expires); - expires != u64::MAX - } - }); - - next_expiration - } - - fn retain(&self, mut f: impl FnMut(TaskRef) -> bool) { - unsafe { - let mut prev = &self.head; - while let Some(p) = prev.get() { - let item = p.timer_queue_item(); - if f(p) { - // Skip to next - prev = &item.next; - } else { - // Remove it - prev.set(item.next.get()); - item.next.set(None); - } - } + next: Cell::new(None), + expires_at: Cell::new(0), } } } diff --git a/embassy-executor/src/raw/util.rs b/embassy-executor/src/raw/util.rs index e2633658a..c46085e45 100644 --- a/embassy-executor/src/raw/util.rs +++ b/embassy-executor/src/raw/util.rs @@ -54,9 +54,4 @@ impl SyncUnsafeCell { { *self.value.get() } - - #[cfg(feature = "integrated-timers")] - pub unsafe fn replace(&self, value: T) -> T { - core::mem::replace(&mut *self.value.get(), value) - } } diff --git a/embassy-time-queue-driver/src/lib.rs b/embassy-time-queue-driver/src/lib.rs index c5e989854..0c78921ed 100644 --- a/embassy-time-queue-driver/src/lib.rs +++ b/embassy-time-queue-driver/src/lib.rs @@ -22,9 +22,9 @@ //! ); //! ``` //! -//! You can also use the `queue_generic` or the `embassy_executor::raw::timer_queue` modules to -//! implement your own timer queue. These modules contain queue implementations which you can wrap -//! and tailor to your needs. +//! You can also use the `queue_generic` or the `queue_integrated` modules to implement your own +//! timer queue. These modules contain queue implementations which you can wrap and tailor to +//! your needs. //! //! If you are providing an embassy-executor implementation besides a timer queue, you can choose to //! expose the `integrated-timers` feature in your implementation. This feature stores timer items @@ -49,7 +49,10 @@ //! embassy_time_queue_driver::timer_queue_impl!(static QUEUE: MyTimerQueue = MyTimerQueue{}); //! ``` +#[cfg(not(feature = "integrated-timers"))] pub mod queue_generic; +#[cfg(feature = "integrated-timers")] +pub mod queue_integrated; use core::cell::RefCell; use core::task::Waker; @@ -89,7 +92,7 @@ macro_rules! timer_queue_impl { } #[cfg(feature = "integrated-timers")] -type InnerQueue = embassy_executor::raw::timer_queue::TimerQueue; +type InnerQueue = queue_integrated::TimerQueue; #[cfg(not(feature = "integrated-timers"))] type InnerQueue = queue_generic::Queue; diff --git a/embassy-time-queue-driver/src/queue_integrated.rs b/embassy-time-queue-driver/src/queue_integrated.rs new file mode 100644 index 000000000..cb0f79356 --- /dev/null +++ b/embassy-time-queue-driver/src/queue_integrated.rs @@ -0,0 +1,78 @@ +//! Timer queue operations. +use core::cell::Cell; +use core::cmp::min; + +use embassy_executor::raw::TaskRef; + +/// A timer queue, with items integrated into tasks. +pub struct TimerQueue { + head: Cell>, +} + +impl TimerQueue { + /// Creates a new timer queue. + pub const fn new() -> Self { + Self { head: Cell::new(None) } + } + + /// Schedules a task to run at a specific time. + /// + /// If this function returns `true`, the called should find the next expiration time and set + /// a new alarm for that time. + pub fn schedule_wake(&mut self, at: u64, p: TaskRef) -> bool { + let item = p.timer_queue_item(); + if item.next.get().is_none() { + // If not in the queue, add it and update. + let prev = self.head.replace(Some(p)); + item.next.set(prev); + } else if at <= item.expires_at.get() { + // If expiration is sooner than previously set, update. + } else { + // Task does not need to be updated. + return false; + } + + item.expires_at.set(at); + true + } + + /// Dequeues expired timers and returns the next alarm time. + /// + /// The provided callback will be called for each expired task. Tasks that never expire + /// will be removed, but the callback will not be called. + pub fn next_expiration(&mut self, now: u64) -> u64 { + let mut next_expiration = u64::MAX; + + self.retain(|p| { + let item = p.timer_queue_item(); + let expires = item.expires_at.get(); + + if expires <= now { + // Timer expired, process task. + embassy_executor::raw::wake_task(p); + false + } else { + // Timer didn't yet expire, or never expires. + next_expiration = min(next_expiration, expires); + expires != u64::MAX + } + }); + + next_expiration + } + + fn retain(&self, mut f: impl FnMut(TaskRef) -> bool) { + let mut prev = &self.head; + while let Some(p) = prev.get() { + let item = p.timer_queue_item(); + if f(p) { + // Skip to next + prev = &item.next; + } else { + // Remove it + prev.set(item.next.get()); + item.next.set(None); + } + } + } +} From df5a399125aea0014a1629d01ef1559b5ae04af9 Mon Sep 17 00:00:00 2001 From: Alessandro Gasbarroni Date: Tue, 10 Dec 2024 10:40:24 +0100 Subject: [PATCH 316/361] nrf: Add RESET operations helpers for the nrf5340 --- embassy-nrf/src/lib.rs | 3 ++ embassy-nrf/src/reset.rs | 82 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 85 insertions(+) create mode 100644 embassy-nrf/src/reset.rs diff --git a/embassy-nrf/src/lib.rs b/embassy-nrf/src/lib.rs index 7c5dd7c83..e7ec4eb9d 100644 --- a/embassy-nrf/src/lib.rs +++ b/embassy-nrf/src/lib.rs @@ -126,6 +126,9 @@ pub mod qspi; #[cfg(not(any(feature = "_nrf91", feature = "_nrf5340-app")))] pub mod radio; #[cfg(not(feature = "_nrf54l"))] // TODO +#[cfg(feature = "_nrf5340")] +pub mod reset; +#[cfg(not(feature = "_nrf54l"))] // TODO #[cfg(not(any(feature = "_nrf5340-app", feature = "_nrf91")))] pub mod rng; #[cfg(not(feature = "_nrf54l"))] // TODO diff --git a/embassy-nrf/src/reset.rs b/embassy-nrf/src/reset.rs new file mode 100644 index 000000000..a33d8f31f --- /dev/null +++ b/embassy-nrf/src/reset.rs @@ -0,0 +1,82 @@ +//! Reset + +#![macro_use] + +use bitflags::bitflags; +use nrf_pac::reset::regs::Resetreas; +#[cfg(not(feature = "_nrf5340-net"))] +use nrf_pac::reset::vals::Forceoff; + +use crate::chip::pac::RESET; + +bitflags! { + #[derive(Debug, Copy, Clone, PartialEq)] + /// Bitflag representation of the `RESETREAS` register + pub struct ResetReason: u32 { + /// Reset Pin + const RESETPIN = 1; + /// Application watchdog timer 0 + const DOG0 = 1 << 1; + /// Application CTRL-AP + const CTRLAP = 1 << 2; + /// Application soft reset + const SREQ = 1 << 3; + /// Application CPU lockup + const LOCKUP = 1 << 4; + /// Wakeup from System OFF when wakeup is triggered by DETECT signal from GPIO + const OFF = 1 << 5; + /// Wakeup from System OFF when wakeup is triggered by ANADETECT signal from LPCOMP + const LPCOMP = 1 << 6; + /// Wakeup from System OFF when wakeup is triggered by entering the Debug Interface mode + const DIF = 1 << 7; + /// Network soft reset + #[cfg(feature = "_nrf5340-net")] + const LSREQ = 1 << 16; + /// Network CPU lockup + #[cfg(feature = "_nrf5340-net")] + const LLOCKUP = 1 << 17; + /// Network watchdog timer + #[cfg(feature = "_nrf5340-net")] + const LDOG = 1 << 18; + /// Force-OFF reset from application core + #[cfg(feature = "_nrf5340-net")] + const MFORCEOFF = 1 << 23; + /// Wakeup from System OFF mode due to NFC field being detected + const NFC = 1 << 24; + /// Application watchdog timer 1 + const DOG1 = 1 << 25; + /// Wakeup from System OFF mode due to VBUS rising into valid range + const VBUS = 1 << 26; + /// Network CTRL-AP + #[cfg(feature = "_nrf5340-net")] + const LCTRLAP = 1 << 27; + } +} + +/// Reads the bitflag of the reset reasons +pub fn read_reasons() -> ResetReason { + ResetReason::from_bits_retain(RESET.resetreas().read().0) +} + +/// Resets the reset reasons +pub fn clear_reasons() { + RESET.resetreas().write(|w| *w = Resetreas(ResetReason::all().bits())); +} + +/// Returns if the network core is held in reset +#[cfg(not(feature = "_nrf5340-net"))] +pub fn network_core_held() -> bool { + RESET.network().forceoff().read().forceoff() == Forceoff::HOLD +} + +/// Releases the network core from the FORCEOFF state +#[cfg(not(feature = "_nrf5340-net"))] +pub fn release_network_core() { + RESET.network().forceoff().write(|w| w.set_forceoff(Forceoff::RELEASE)); +} + +/// Holds the network core in the FORCEOFF state +#[cfg(not(feature = "_nrf5340-net"))] +pub fn hold_network_core() { + RESET.network().forceoff().write(|w| w.set_forceoff(Forceoff::HOLD)); +} From 415db448c7c476f8c2d81e157437657868d10158 Mon Sep 17 00:00:00 2001 From: Alexander Walter Date: Fri, 13 Dec 2024 19:38:30 +0100 Subject: [PATCH 317/361] Added system off and wake-on-field --- embassy-nrf/src/lib.rs | 3 +++ embassy-nrf/src/power.rs | 13 +++++++++++++ 2 files changed, 16 insertions(+) create mode 100644 embassy-nrf/src/power.rs diff --git a/embassy-nrf/src/lib.rs b/embassy-nrf/src/lib.rs index e7ec4eb9d..b6283c7f5 100644 --- a/embassy-nrf/src/lib.rs +++ b/embassy-nrf/src/lib.rs @@ -107,6 +107,9 @@ pub mod nvmc; ))] pub mod pdm; #[cfg(not(feature = "_nrf54l"))] // TODO +#[cfg(feature = "nrf52840")] +pub mod power; +#[cfg(not(feature = "_nrf54l"))] // TODO pub mod ppi; #[cfg(not(feature = "_nrf54l"))] // TODO #[cfg(not(any( diff --git a/embassy-nrf/src/power.rs b/embassy-nrf/src/power.rs new file mode 100644 index 000000000..440028380 --- /dev/null +++ b/embassy-nrf/src/power.rs @@ -0,0 +1,13 @@ +//! Power + +use crate::chip::pac::{NFCT, POWER}; + +/// Puts the MCU into "System Off" mode with a power usage 0f 0.4 uA +pub fn set_system_off() { + POWER.systemoff().write(|w| w.set_systemoff(true)); +} + +/// Wake the system if there if an NFC field close to the nrf52840's antenna +pub fn wake_on_nfc_sense() { + NFCT.tasks_sense().write_value(0x01); +} From ec96395d084d5edc8be25ddaea8547e2ebd447a6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?D=C3=A1niel=20Buga?= Date: Mon, 9 Dec 2024 08:43:57 +0100 Subject: [PATCH 318/361] Prevent task from respawning while in the timer queue --- embassy-executor/src/raw/mod.rs | 36 ++++++++++++++++- embassy-executor/src/raw/state_atomics.rs | 36 +++++++++++++++++ embassy-executor/src/raw/state_atomics_arm.rs | 40 ++++++++++++++++++- .../src/raw/state_critical_section.rs | 29 ++++++++++++++ embassy-executor/src/raw/timer_queue.rs | 15 ++++++- embassy-time-queue-driver/src/lib.rs | 14 +++++++ .../src/queue_integrated.rs | 20 +++++++--- 7 files changed, 181 insertions(+), 9 deletions(-) diff --git a/embassy-executor/src/raw/mod.rs b/embassy-executor/src/raw/mod.rs index f9c6509f1..14d689900 100644 --- a/embassy-executor/src/raw/mod.rs +++ b/embassy-executor/src/raw/mod.rs @@ -50,7 +50,7 @@ pub(crate) struct TaskHeader { } /// This is essentially a `&'static TaskStorage` where the type of the future has been erased. -#[derive(Clone, Copy)] +#[derive(Clone, Copy, PartialEq)] pub struct TaskRef { ptr: NonNull, } @@ -72,6 +72,16 @@ impl TaskRef { } } + /// # Safety + /// + /// The result of this function must only be compared + /// for equality, or stored, but not used. + pub const unsafe fn dangling() -> Self { + Self { + ptr: NonNull::dangling(), + } + } + pub(crate) fn header(self) -> &'static TaskHeader { unsafe { self.ptr.as_ref() } } @@ -88,6 +98,30 @@ impl TaskRef { &self.header().timer_queue_item } + /// Mark the task as timer-queued. Return whether it was newly queued (i.e. not queued before) + /// + /// Entering this state prevents the task from being respawned while in a timer queue. + /// + /// Safety: + /// + /// This functions should only be called by the timer queue implementation, before + /// enqueueing the timer item. + #[cfg(feature = "integrated-timers")] + pub unsafe fn timer_enqueue(&self) -> timer_queue::TimerEnqueueOperation { + self.header().state.timer_enqueue() + } + + /// Unmark the task as timer-queued. + /// + /// Safety: + /// + /// This functions should only be called by the timer queue implementation, after the task has + /// been removed from the timer queue. + #[cfg(feature = "integrated-timers")] + pub unsafe fn timer_dequeue(&self) { + self.header().state.timer_dequeue() + } + /// The returned pointer is valid for the entire TaskStorage. pub(crate) fn as_ptr(self) -> *const TaskHeader { self.ptr.as_ptr() diff --git a/embassy-executor/src/raw/state_atomics.rs b/embassy-executor/src/raw/state_atomics.rs index e4127897e..d03c61ade 100644 --- a/embassy-executor/src/raw/state_atomics.rs +++ b/embassy-executor/src/raw/state_atomics.rs @@ -1,9 +1,15 @@ use core::sync::atomic::{AtomicU32, Ordering}; +#[cfg(feature = "integrated-timers")] +use super::timer_queue::TimerEnqueueOperation; + /// Task is spawned (has a future) pub(crate) const STATE_SPAWNED: u32 = 1 << 0; /// Task is in the executor run queue pub(crate) const STATE_RUN_QUEUED: u32 = 1 << 1; +/// Task is in the executor timer queue +#[cfg(feature = "integrated-timers")] +pub(crate) const STATE_TIMER_QUEUED: u32 = 1 << 2; pub(crate) struct State { state: AtomicU32, @@ -52,4 +58,34 @@ impl State { let state = self.state.fetch_and(!STATE_RUN_QUEUED, Ordering::AcqRel); state & STATE_SPAWNED != 0 } + + /// Mark the task as timer-queued. Return whether it can be enqueued. + #[cfg(feature = "integrated-timers")] + #[inline(always)] + pub fn timer_enqueue(&self) -> TimerEnqueueOperation { + if self + .state + .fetch_update(Ordering::SeqCst, Ordering::SeqCst, |state| { + // If not started, ignore it + if state & STATE_SPAWNED == 0 { + None + } else { + // Mark it as enqueued + Some(state | STATE_TIMER_QUEUED) + } + }) + .is_ok() + { + TimerEnqueueOperation::Enqueue + } else { + TimerEnqueueOperation::Ignore + } + } + + /// Unmark the task as timer-queued. + #[cfg(feature = "integrated-timers")] + #[inline(always)] + pub fn timer_dequeue(&self) { + self.state.fetch_and(!STATE_TIMER_QUEUED, Ordering::Relaxed); + } } diff --git a/embassy-executor/src/raw/state_atomics_arm.rs b/embassy-executor/src/raw/state_atomics_arm.rs index b673c7359..f6f2e8f08 100644 --- a/embassy-executor/src/raw/state_atomics_arm.rs +++ b/embassy-executor/src/raw/state_atomics_arm.rs @@ -1,9 +1,14 @@ use core::arch::asm; use core::sync::atomic::{compiler_fence, AtomicBool, AtomicU32, Ordering}; +#[cfg(feature = "integrated-timers")] +use super::timer_queue::TimerEnqueueOperation; + // Must be kept in sync with the layout of `State`! pub(crate) const STATE_SPAWNED: u32 = 1 << 0; pub(crate) const STATE_RUN_QUEUED: u32 = 1 << 8; +#[cfg(feature = "integrated-timers")] +pub(crate) const STATE_TIMER_QUEUED: u32 = 1 << 16; #[repr(C, align(4))] pub(crate) struct State { @@ -11,8 +16,9 @@ pub(crate) struct State { spawned: AtomicBool, /// Task is in the executor run queue run_queued: AtomicBool, + /// Task is in the executor timer queue + timer_queued: AtomicBool, pad: AtomicBool, - pad2: AtomicBool, } impl State { @@ -20,8 +26,8 @@ impl State { Self { spawned: AtomicBool::new(false), run_queued: AtomicBool::new(false), + timer_queued: AtomicBool::new(false), pad: AtomicBool::new(false), - pad2: AtomicBool::new(false), } } @@ -85,4 +91,34 @@ impl State { self.run_queued.store(false, Ordering::Relaxed); r } + + /// Mark the task as timer-queued. Return whether it can be enqueued. + #[cfg(feature = "integrated-timers")] + #[inline(always)] + pub fn timer_enqueue(&self) -> TimerEnqueueOperation { + if self + .as_u32() + .fetch_update(Ordering::SeqCst, Ordering::SeqCst, |state| { + // If not started, ignore it + if state & STATE_SPAWNED == 0 { + None + } else { + // Mark it as enqueued + Some(state | STATE_TIMER_QUEUED) + } + }) + .is_ok() + { + TimerEnqueueOperation::Enqueue + } else { + TimerEnqueueOperation::Ignore + } + } + + /// Unmark the task as timer-queued. + #[cfg(feature = "integrated-timers")] + #[inline(always)] + pub fn timer_dequeue(&self) { + self.timer_queued.store(false, Ordering::Relaxed); + } } diff --git a/embassy-executor/src/raw/state_critical_section.rs b/embassy-executor/src/raw/state_critical_section.rs index b92eed006..c0ec2f530 100644 --- a/embassy-executor/src/raw/state_critical_section.rs +++ b/embassy-executor/src/raw/state_critical_section.rs @@ -2,10 +2,16 @@ use core::cell::Cell; use critical_section::Mutex; +#[cfg(feature = "integrated-timers")] +use super::timer_queue::TimerEnqueueOperation; + /// Task is spawned (has a future) pub(crate) const STATE_SPAWNED: u32 = 1 << 0; /// Task is in the executor run queue pub(crate) const STATE_RUN_QUEUED: u32 = 1 << 1; +/// Task is in the executor timer queue +#[cfg(feature = "integrated-timers")] +pub(crate) const STATE_TIMER_QUEUED: u32 = 1 << 2; pub(crate) struct State { state: Mutex>, @@ -69,4 +75,27 @@ impl State { ok }) } + + /// Mark the task as timer-queued. Return whether it can be enqueued. + #[cfg(feature = "integrated-timers")] + #[inline(always)] + pub fn timer_enqueue(&self) -> TimerEnqueueOperation { + self.update(|s| { + // FIXME: we need to split SPAWNED into two phases, to prevent enqueueing a task that is + // just being spawned, because its executor pointer may still be changing. + if *s & STATE_SPAWNED == STATE_SPAWNED { + *s |= STATE_TIMER_QUEUED; + TimerEnqueueOperation::Enqueue + } else { + TimerEnqueueOperation::Ignore + } + }) + } + + /// Unmark the task as timer-queued. + #[cfg(feature = "integrated-timers")] + #[inline(always)] + pub fn timer_dequeue(&self) { + self.update(|s| *s &= !STATE_TIMER_QUEUED); + } } diff --git a/embassy-executor/src/raw/timer_queue.rs b/embassy-executor/src/raw/timer_queue.rs index 46e346c1b..c36708401 100644 --- a/embassy-executor/src/raw/timer_queue.rs +++ b/embassy-executor/src/raw/timer_queue.rs @@ -7,6 +7,9 @@ use super::TaskRef; /// An item in the timer queue. pub struct TimerQueueItem { /// The next item in the queue. + /// + /// If this field contains `Some`, the item is in the queue. The last item in the queue has a + /// value of `Some(dangling_pointer)` pub next: Cell>, /// The time at which this item expires. @@ -19,7 +22,17 @@ impl TimerQueueItem { pub(crate) const fn new() -> Self { Self { next: Cell::new(None), - expires_at: Cell::new(0), + expires_at: Cell::new(u64::MAX), } } } + +/// The operation to perform after `timer_enqueue` is called. +#[derive(Debug, Copy, Clone, PartialEq)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +pub enum TimerEnqueueOperation { + /// Enqueue the task. + Enqueue, + /// Update the task's expiration time. + Ignore, +} diff --git a/embassy-time-queue-driver/src/lib.rs b/embassy-time-queue-driver/src/lib.rs index 0c78921ed..2d5fd449a 100644 --- a/embassy-time-queue-driver/src/lib.rs +++ b/embassy-time-queue-driver/src/lib.rs @@ -73,6 +73,20 @@ extern "Rust" { /// Schedule the given waker to be woken at `at`. pub fn schedule_wake(at: u64, waker: &Waker) { + #[cfg(feature = "integrated-timers")] + { + use embassy_executor::raw::task_from_waker; + use embassy_executor::raw::timer_queue::TimerEnqueueOperation; + // The very first thing we must do, before we even access the timer queue, is to + // mark the task a TIMER_QUEUED. This ensures that the task that is being scheduled + // can not be respawn while we are accessing the timer queue. + let task = task_from_waker(waker); + if unsafe { task.timer_enqueue() } == TimerEnqueueOperation::Ignore { + // We are not allowed to enqueue the task in the timer queue. This is because the + // task is not spawned, and so it makes no sense to schedule it. + return; + } + } unsafe { _embassy_time_schedule_wake(at, waker) } } diff --git a/embassy-time-queue-driver/src/queue_integrated.rs b/embassy-time-queue-driver/src/queue_integrated.rs index cb0f79356..b905c00c3 100644 --- a/embassy-time-queue-driver/src/queue_integrated.rs +++ b/embassy-time-queue-driver/src/queue_integrated.rs @@ -24,16 +24,21 @@ impl TimerQueue { if item.next.get().is_none() { // If not in the queue, add it and update. let prev = self.head.replace(Some(p)); - item.next.set(prev); + item.next.set(if prev.is_none() { + Some(unsafe { TaskRef::dangling() }) + } else { + prev + }); + item.expires_at.set(at); + true } else if at <= item.expires_at.get() { // If expiration is sooner than previously set, update. + item.expires_at.set(at); + true } else { // Task does not need to be updated. - return false; + false } - - item.expires_at.set(at); - true } /// Dequeues expired timers and returns the next alarm time. @@ -64,6 +69,10 @@ impl TimerQueue { fn retain(&self, mut f: impl FnMut(TaskRef) -> bool) { let mut prev = &self.head; while let Some(p) = prev.get() { + if unsafe { p == TaskRef::dangling() } { + // prev was the last item, stop + break; + } let item = p.timer_queue_item(); if f(p) { // Skip to next @@ -72,6 +81,7 @@ impl TimerQueue { // Remove it prev.set(item.next.get()); item.next.set(None); + unsafe { p.timer_dequeue() }; } } } From b268b1795fed58544c166c41842ce0d66328aa3e Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Sun, 8 Dec 2024 23:27:32 +0100 Subject: [PATCH 319/361] Merge time-driver and time-queue-driver traits, make HALs own and handle the queue. --- .github/ci/test.sh | 2 +- embassy-nrf/src/time_driver.rs | 109 ++++++------ embassy-rp/src/time_driver.rs | 72 ++++---- embassy-stm32/src/time_driver.rs | 104 ++++++----- embassy-time-driver/src/lib.rs | 17 ++ embassy-time-queue-driver/src/lib.rs | 140 ++------------- .../src/queue_integrated.rs | 12 +- embassy-time/Cargo.toml | 1 - embassy-time/src/driver_mock.rs | 113 ++++-------- embassy-time/src/driver_std.rs | 163 ++++++------------ embassy-time/src/driver_wasm.rs | 112 ++++++------ 11 files changed, 328 insertions(+), 517 deletions(-) diff --git a/.github/ci/test.sh b/.github/ci/test.sh index 285f3f29e..0fd6820d2 100755 --- a/.github/ci/test.sh +++ b/.github/ci/test.sh @@ -17,7 +17,7 @@ cargo test --manifest-path ./embassy-futures/Cargo.toml cargo test --manifest-path ./embassy-sync/Cargo.toml cargo test --manifest-path ./embassy-embedded-hal/Cargo.toml cargo test --manifest-path ./embassy-hal-internal/Cargo.toml -cargo test --manifest-path ./embassy-time/Cargo.toml --features mock-driver +cargo test --manifest-path ./embassy-time/Cargo.toml --features mock-driver,embassy-time-queue-driver/generic-queue-8 cargo test --manifest-path ./embassy-time-driver/Cargo.toml cargo test --manifest-path ./embassy-boot/Cargo.toml diff --git a/embassy-nrf/src/time_driver.rs b/embassy-nrf/src/time_driver.rs index f8b3c4bbc..a27fae9a8 100644 --- a/embassy-nrf/src/time_driver.rs +++ b/embassy-nrf/src/time_driver.rs @@ -1,11 +1,11 @@ -use core::cell::Cell; +use core::cell::{Cell, RefCell}; use core::sync::atomic::{compiler_fence, AtomicU32, Ordering}; use critical_section::CriticalSection; use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex; use embassy_sync::blocking_mutex::CriticalSectionMutex as Mutex; use embassy_time_driver::Driver; -use embassy_time_queue_driver::GlobalTimerQueue; +use embassy_time_queue_driver::Queue; use crate::interrupt::InterruptExt; use crate::{interrupt, pac}; @@ -111,11 +111,13 @@ struct RtcDriver { period: AtomicU32, /// Timestamp at which to fire alarm. u64::MAX if no alarm is scheduled. alarms: Mutex, + queue: Mutex>, } embassy_time_driver::time_driver_impl!(static DRIVER: RtcDriver = RtcDriver { period: AtomicU32::new(0), alarms: Mutex::const_new(CriticalSectionRawMutex::new(), AlarmState::new()), + queue: Mutex::new(RefCell::new(Queue::new())), }); impl RtcDriver { @@ -194,59 +196,60 @@ impl RtcDriver { alarm.timestamp.set(u64::MAX); // Call after clearing alarm, so the callback can set another alarm. - TIMER_QUEUE_DRIVER.dispatch(); + let mut next = self.queue.borrow(cs).borrow_mut().next_expiration(self.now()); + while !self.set_alarm(cs, next) { + next = self.queue.borrow(cs).borrow_mut().next_expiration(self.now()); + } } - fn set_alarm(&self, timestamp: u64) -> bool { - critical_section::with(|cs| { - let n = 0; - let alarm = &self.alarms.borrow(cs); - alarm.timestamp.set(timestamp); + fn set_alarm(&self, cs: CriticalSection, timestamp: u64) -> bool { + let n = 0; + let alarm = &self.alarms.borrow(cs); + alarm.timestamp.set(timestamp); - let r = rtc(); + let r = rtc(); - let t = self.now(); - if timestamp <= t { - // If alarm timestamp has passed the alarm will not fire. - // Disarm the alarm and return `false` to indicate that. - r.intenclr().write(|w| w.0 = compare_n(n)); + let t = self.now(); + if timestamp <= t { + // If alarm timestamp has passed the alarm will not fire. + // Disarm the alarm and return `false` to indicate that. + r.intenclr().write(|w| w.0 = compare_n(n)); - alarm.timestamp.set(u64::MAX); + alarm.timestamp.set(u64::MAX); - return false; - } + return false; + } - // If it hasn't triggered yet, setup it in the compare channel. + // If it hasn't triggered yet, setup it in the compare channel. - // Write the CC value regardless of whether we're going to enable it now or not. - // This way, when we enable it later, the right value is already set. + // Write the CC value regardless of whether we're going to enable it now or not. + // This way, when we enable it later, the right value is already set. - // nrf52 docs say: - // If the COUNTER is N, writing N or N+1 to a CC register may not trigger a COMPARE event. - // To workaround this, we never write a timestamp smaller than N+3. - // N+2 is not safe because rtc can tick from N to N+1 between calling now() and writing cc. - // - // It is impossible for rtc to tick more than once because - // - this code takes less time than 1 tick - // - it runs with interrupts disabled so nothing else can preempt it. - // - // This means that an alarm can be delayed for up to 2 ticks (from t+1 to t+3), but this is allowed - // by the Alarm trait contract. What's not allowed is triggering alarms *before* their scheduled time, - // and we don't do that here. - let safe_timestamp = timestamp.max(t + 3); - r.cc(n).write(|w| w.set_compare(safe_timestamp as u32 & 0xFFFFFF)); + // nrf52 docs say: + // If the COUNTER is N, writing N or N+1 to a CC register may not trigger a COMPARE event. + // To workaround this, we never write a timestamp smaller than N+3. + // N+2 is not safe because rtc can tick from N to N+1 between calling now() and writing cc. + // + // It is impossible for rtc to tick more than once because + // - this code takes less time than 1 tick + // - it runs with interrupts disabled so nothing else can preempt it. + // + // This means that an alarm can be delayed for up to 2 ticks (from t+1 to t+3), but this is allowed + // by the Alarm trait contract. What's not allowed is triggering alarms *before* their scheduled time, + // and we don't do that here. + let safe_timestamp = timestamp.max(t + 3); + r.cc(n).write(|w| w.set_compare(safe_timestamp as u32 & 0xFFFFFF)); - let diff = timestamp - t; - if diff < 0xc00000 { - r.intenset().write(|w| w.0 = compare_n(n)); - } else { - // If it's too far in the future, don't setup the compare channel yet. - // It will be setup later by `next_period`. - r.intenclr().write(|w| w.0 = compare_n(n)); - } + let diff = timestamp - t; + if diff < 0xc00000 { + r.intenset().write(|w| w.0 = compare_n(n)); + } else { + // If it's too far in the future, don't setup the compare channel yet. + // It will be setup later by `next_period`. + r.intenclr().write(|w| w.0 = compare_n(n)); + } - true - }) + true } } @@ -258,6 +261,19 @@ impl Driver for RtcDriver { let counter = rtc().counter().read().0; calc_now(period, counter) } + + fn schedule_wake(&self, at: u64, waker: &core::task::Waker) { + critical_section::with(|cs| { + let mut queue = self.queue.borrow(cs).borrow_mut(); + + if queue.schedule_wake(at, waker) { + let mut next = queue.next_expiration(self.now()); + while !self.set_alarm(cs, next) { + next = queue.next_expiration(self.now()); + } + } + }) + } } #[cfg(feature = "_nrf54l")] @@ -277,8 +293,3 @@ fn RTC1() { pub(crate) fn init(irq_prio: crate::interrupt::Priority) { DRIVER.init(irq_prio) } - -embassy_time_queue_driver::timer_queue_impl!( - static TIMER_QUEUE_DRIVER: GlobalTimerQueue - = GlobalTimerQueue::new(|next_expiration| DRIVER.set_alarm(next_expiration)) -); diff --git a/embassy-rp/src/time_driver.rs b/embassy-rp/src/time_driver.rs index 17ae5fff3..a0eaec10e 100644 --- a/embassy-rp/src/time_driver.rs +++ b/embassy-rp/src/time_driver.rs @@ -1,10 +1,11 @@ //! Timer driver. -use core::cell::Cell; +use core::cell::{Cell, RefCell}; +use critical_section::CriticalSection; use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex; use embassy_sync::blocking_mutex::Mutex; use embassy_time_driver::Driver; -use embassy_time_queue_driver::GlobalTimerQueue; +use embassy_time_queue_driver::Queue; #[cfg(feature = "rp2040")] use pac::TIMER; #[cfg(feature = "_rp235x")] @@ -20,12 +21,14 @@ unsafe impl Send for AlarmState {} struct TimerDriver { alarms: Mutex, + queue: Mutex>, } embassy_time_driver::time_driver_impl!(static DRIVER: TimerDriver = TimerDriver{ alarms: Mutex::const_new(CriticalSectionRawMutex::new(), AlarmState { timestamp: Cell::new(0), }), + queue: Mutex::new(RefCell::new(Queue::new())) }); impl Driver for TimerDriver { @@ -39,34 +42,45 @@ impl Driver for TimerDriver { } } } + + fn schedule_wake(&self, at: u64, waker: &core::task::Waker) { + critical_section::with(|cs| { + let mut queue = self.queue.borrow(cs).borrow_mut(); + + if queue.schedule_wake(at, waker) { + let mut next = queue.next_expiration(self.now()); + while !self.set_alarm(cs, next) { + next = queue.next_expiration(self.now()); + } + } + }) + } } impl TimerDriver { - fn set_alarm(&self, timestamp: u64) -> bool { + fn set_alarm(&self, cs: CriticalSection, timestamp: u64) -> bool { let n = 0; - critical_section::with(|cs| { - let alarm = &self.alarms.borrow(cs); - alarm.timestamp.set(timestamp); + let alarm = &self.alarms.borrow(cs); + alarm.timestamp.set(timestamp); - // Arm it. - // Note that we're not checking the high bits at all. This means the irq may fire early - // if the alarm is more than 72 minutes (2^32 us) in the future. This is OK, since on irq fire - // it is checked if the alarm time has passed. - TIMER.alarm(n).write_value(timestamp as u32); + // Arm it. + // Note that we're not checking the high bits at all. This means the irq may fire early + // if the alarm is more than 72 minutes (2^32 us) in the future. This is OK, since on irq fire + // it is checked if the alarm time has passed. + TIMER.alarm(n).write_value(timestamp as u32); - let now = self.now(); - if timestamp <= now { - // If alarm timestamp has passed the alarm will not fire. - // Disarm the alarm and return `false` to indicate that. - TIMER.armed().write(|w| w.set_armed(1 << n)); + let now = self.now(); + if timestamp <= now { + // If alarm timestamp has passed the alarm will not fire. + // Disarm the alarm and return `false` to indicate that. + TIMER.armed().write(|w| w.set_armed(1 << n)); - alarm.timestamp.set(u64::MAX); + alarm.timestamp.set(u64::MAX); - false - } else { - true - } - }) + false + } else { + true + } } fn check_alarm(&self) { @@ -75,7 +89,7 @@ impl TimerDriver { let alarm = &self.alarms.borrow(cs); let timestamp = alarm.timestamp.get(); if timestamp <= self.now() { - self.trigger_alarm() + self.trigger_alarm(cs) } else { // Not elapsed, arm it again. // This can happen if it was set more than 2^32 us in the future. @@ -87,8 +101,11 @@ impl TimerDriver { TIMER.intr().write(|w| w.set_alarm(n, true)); } - fn trigger_alarm(&self) { - TIMER_QUEUE_DRIVER.dispatch(); + fn trigger_alarm(&self, cs: CriticalSection) { + let mut next = self.queue.borrow(cs).borrow_mut().next_expiration(self.now()); + while !self.set_alarm(cs, next) { + next = self.queue.borrow(cs).borrow_mut().next_expiration(self.now()); + } } } @@ -125,8 +142,3 @@ fn TIMER_IRQ_0() { fn TIMER0_IRQ_0() { DRIVER.check_alarm() } - -embassy_time_queue_driver::timer_queue_impl!( - static TIMER_QUEUE_DRIVER: GlobalTimerQueue - = GlobalTimerQueue::new(|next_expiration| DRIVER.set_alarm(next_expiration)) -); diff --git a/embassy-stm32/src/time_driver.rs b/embassy-stm32/src/time_driver.rs index 290f857ad..a4c333d82 100644 --- a/embassy-stm32/src/time_driver.rs +++ b/embassy-stm32/src/time_driver.rs @@ -1,13 +1,13 @@ #![allow(non_snake_case)] -use core::cell::Cell; +use core::cell::{Cell, RefCell}; use core::sync::atomic::{compiler_fence, AtomicU32, Ordering}; use critical_section::CriticalSection; use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex; use embassy_sync::blocking_mutex::Mutex; use embassy_time_driver::{Driver, TICK_HZ}; -use embassy_time_queue_driver::GlobalTimerQueue; +use embassy_time_queue_driver::Queue; use stm32_metapac::timer::{regs, TimGp16}; use crate::interrupt::typelevel::Interrupt; @@ -214,6 +214,7 @@ pub(crate) struct RtcDriver { alarm: Mutex, #[cfg(feature = "low-power")] rtc: Mutex>>, + queue: Mutex>, } embassy_time_driver::time_driver_impl!(static DRIVER: RtcDriver = RtcDriver { @@ -221,6 +222,7 @@ embassy_time_driver::time_driver_impl!(static DRIVER: RtcDriver = RtcDriver { alarm: Mutex::const_new(CriticalSectionRawMutex::new(), AlarmState::new()), #[cfg(feature = "low-power")] rtc: Mutex::const_new(CriticalSectionRawMutex::new(), Cell::new(None)), + queue: Mutex::new(RefCell::new(Queue::new())) }); impl RtcDriver { @@ -266,8 +268,7 @@ impl RtcDriver { fn on_interrupt(&self) { let r = regs_gp16(); - // XXX: reduce the size of this critical section ? - critical_section::with(|_cs| { + critical_section::with(|cs| { let sr = r.sr().read(); let dier = r.dier().read(); @@ -288,7 +289,7 @@ impl RtcDriver { let n = 0; if sr.ccif(n + 1) && dier.ccie(n + 1) { - self.trigger_alarm(); + self.trigger_alarm(cs); } }) } @@ -315,8 +316,11 @@ impl RtcDriver { }) } - fn trigger_alarm(&self) { - TIMER_QUEUE_DRIVER.dispatch(); + fn trigger_alarm(&self, cs: CriticalSection) { + let mut next = self.queue.borrow(cs).borrow_mut().next_expiration(self.now()); + while !self.set_alarm(cs, next) { + next = self.queue.borrow(cs).borrow_mut().next_expiration(self.now()); + } } /* @@ -366,9 +370,9 @@ impl RtcDriver { // Now, recompute alarm let alarm = self.alarm.borrow(cs); - if !self.set_alarm(alarm.timestamp.get()) { + if !self.set_alarm(cs, alarm.timestamp.get()) { // If the alarm timestamp has passed, we need to trigger it - self.trigger_alarm(); + self.trigger_alarm(cs); } } @@ -441,49 +445,47 @@ impl RtcDriver { }) } - fn set_alarm(&self, timestamp: u64) -> bool { - critical_section::with(|cs| { - let r = regs_gp16(); + fn set_alarm(&self, cs: CriticalSection, timestamp: u64) -> bool { + let r = regs_gp16(); - let n = 0; - self.alarm.borrow(cs).timestamp.set(timestamp); + let n = 0; + self.alarm.borrow(cs).timestamp.set(timestamp); - let t = self.now(); - if timestamp <= t { - // If alarm timestamp has passed the alarm will not fire. - // Disarm the alarm and return `false` to indicate that. - r.dier().modify(|w| w.set_ccie(n + 1, false)); + let t = self.now(); + if timestamp <= t { + // If alarm timestamp has passed the alarm will not fire. + // Disarm the alarm and return `false` to indicate that. + r.dier().modify(|w| w.set_ccie(n + 1, false)); - self.alarm.borrow(cs).timestamp.set(u64::MAX); + self.alarm.borrow(cs).timestamp.set(u64::MAX); - return false; - } + return false; + } - // Write the CCR value regardless of whether we're going to enable it now or not. - // This way, when we enable it later, the right value is already set. - r.ccr(n + 1).write(|w| w.set_ccr(timestamp as u16)); + // Write the CCR value regardless of whether we're going to enable it now or not. + // This way, when we enable it later, the right value is already set. + r.ccr(n + 1).write(|w| w.set_ccr(timestamp as u16)); - // Enable it if it'll happen soon. Otherwise, `next_period` will enable it. - let diff = timestamp - t; - r.dier().modify(|w| w.set_ccie(n + 1, diff < 0xc000)); + // Enable it if it'll happen soon. Otherwise, `next_period` will enable it. + let diff = timestamp - t; + r.dier().modify(|w| w.set_ccie(n + 1, diff < 0xc000)); - // Reevaluate if the alarm timestamp is still in the future - let t = self.now(); - if timestamp <= t { - // If alarm timestamp has passed since we set it, we have a race condition and - // the alarm may or may not have fired. - // Disarm the alarm and return `false` to indicate that. - // It is the caller's responsibility to handle this ambiguity. - r.dier().modify(|w| w.set_ccie(n + 1, false)); + // Reevaluate if the alarm timestamp is still in the future + let t = self.now(); + if timestamp <= t { + // If alarm timestamp has passed since we set it, we have a race condition and + // the alarm may or may not have fired. + // Disarm the alarm and return `false` to indicate that. + // It is the caller's responsibility to handle this ambiguity. + r.dier().modify(|w| w.set_ccie(n + 1, false)); - self.alarm.borrow(cs).timestamp.set(u64::MAX); + self.alarm.borrow(cs).timestamp.set(u64::MAX); - return false; - } + return false; + } - // We're confident the alarm will ring in the future. - true - }) + // We're confident the alarm will ring in the future. + true } } @@ -496,6 +498,19 @@ impl Driver for RtcDriver { let counter = r.cnt().read().cnt(); calc_now(period, counter) } + + fn schedule_wake(&self, at: u64, waker: &core::task::Waker) { + critical_section::with(|cs| { + let mut queue = self.queue.borrow(cs).borrow_mut(); + + if queue.schedule_wake(at, waker) { + let mut next = queue.next_expiration(self.now()); + while !self.set_alarm(cs, next) { + next = queue.next_expiration(self.now()); + } + } + }) + } } #[cfg(feature = "low-power")] @@ -506,8 +521,3 @@ pub(crate) fn get_driver() -> &'static RtcDriver { pub(crate) fn init(cs: CriticalSection) { DRIVER.init(cs) } - -embassy_time_queue_driver::timer_queue_impl!( - static TIMER_QUEUE_DRIVER: GlobalTimerQueue - = GlobalTimerQueue::new(|next_expiration| DRIVER.set_alarm(next_expiration)) -); diff --git a/embassy-time-driver/src/lib.rs b/embassy-time-driver/src/lib.rs index ffb363cd7..090969d8c 100644 --- a/embassy-time-driver/src/lib.rs +++ b/embassy-time-driver/src/lib.rs @@ -38,6 +38,8 @@ //! # Example //! //! ``` +//! use core::task::Waker; +//! //! use embassy_time_driver::Driver; //! //! struct MyDriver{} // not public! @@ -46,6 +48,10 @@ //! fn now(&self) -> u64 { //! todo!() //! } +//! +//! fn schedule_wake(&self, at: u64, waker: &Waker) { +//! todo!() +//! } //! } //! //! embassy_time_driver::time_driver_impl!(static DRIVER: MyDriver = MyDriver{}); @@ -54,6 +60,8 @@ //! ## Feature flags #![doc = document_features::document_features!(feature_label = r#"{feature}"#)] +use core::task::Waker; + mod tick; /// Ticks per second of the global timebase. @@ -74,6 +82,10 @@ pub trait Driver: Send + Sync + 'static { /// you MUST extend them to 64-bit, for example by counting overflows in software, /// or chaining multiple timers together. fn now(&self) -> u64; + + /// Schedules a waker to be awoken at moment `at`. + /// If this moment is in the past, the waker might be awoken immediately. + fn schedule_wake(&self, at: u64, waker: &Waker); } extern "Rust" { @@ -97,5 +109,10 @@ macro_rules! time_driver_impl { fn _embassy_time_now() -> u64 { <$t as $crate::Driver>::now(&$name) } + + #[no_mangle] + fn _embassy_time_schedule_wake(at: u64, waker: &core::task::Waker) { + <$t as $crate::Driver>::schedule_wake(&$name, at, waker); + } }; } diff --git a/embassy-time-queue-driver/src/lib.rs b/embassy-time-queue-driver/src/lib.rs index 2d5fd449a..ed490a0ef 100644 --- a/embassy-time-queue-driver/src/lib.rs +++ b/embassy-time-queue-driver/src/lib.rs @@ -49,23 +49,18 @@ //! embassy_time_queue_driver::timer_queue_impl!(static QUEUE: MyTimerQueue = MyTimerQueue{}); //! ``` +use core::task::Waker; + #[cfg(not(feature = "integrated-timers"))] pub mod queue_generic; #[cfg(feature = "integrated-timers")] pub mod queue_integrated; -use core::cell::RefCell; -use core::task::Waker; +#[cfg(feature = "integrated-timers")] +pub use queue_integrated::Queue; -use critical_section::Mutex; - -/// Timer queue -pub trait TimerQueue { - /// Schedules a waker in the queue to be awoken at moment `at`. - /// - /// If this moment is in the past, the waker might be awoken immediately. - fn schedule_wake(&'static self, at: u64, waker: &Waker); -} +#[cfg(not(feature = "integrated-timers"))] +pub use queue_generic::Queue; extern "Rust" { fn _embassy_time_schedule_wake(at: u64, waker: &Waker); @@ -73,7 +68,10 @@ extern "Rust" { /// Schedule the given waker to be woken at `at`. pub fn schedule_wake(at: u64, waker: &Waker) { - #[cfg(feature = "integrated-timers")] + // This function is not implemented in embassy-time-driver because it needs access to executor + // internals. The function updates task state, then delegates to the implementation provided + // by the time driver. + #[cfg(not(feature = "_generic-queue"))] { use embassy_executor::raw::task_from_waker; use embassy_executor::raw::timer_queue::TimerEnqueueOperation; @@ -89,121 +87,3 @@ pub fn schedule_wake(at: u64, waker: &Waker) { } unsafe { _embassy_time_schedule_wake(at, waker) } } - -/// Set the TimerQueue implementation. -/// -/// See the module documentation for an example. -#[macro_export] -macro_rules! timer_queue_impl { - (static $name:ident: $t: ty = $val:expr) => { - static $name: $t = $val; - - #[no_mangle] - fn _embassy_time_schedule_wake(at: u64, waker: &core::task::Waker) { - <$t as $crate::TimerQueue>::schedule_wake(&$name, at, waker); - } - }; -} - -#[cfg(feature = "integrated-timers")] -type InnerQueue = queue_integrated::TimerQueue; - -#[cfg(not(feature = "integrated-timers"))] -type InnerQueue = queue_generic::Queue; - -/// A timer queue implementation that can be used as a global timer queue. -/// -/// This implementation is not thread-safe, and should be protected by a mutex of some sort. -pub struct GenericTimerQueue bool> { - queue: InnerQueue, - set_alarm: F, -} - -impl bool> GenericTimerQueue { - /// Creates a new timer queue. - /// - /// `set_alarm` is a function that should set the next alarm time. The function should - /// return `true` if the alarm was set, and `false` if the alarm was in the past. - pub const fn new(set_alarm: F) -> Self { - Self { - queue: InnerQueue::new(), - set_alarm, - } - } - - /// Schedules a task to run at a specific time, and returns whether any changes were made. - pub fn schedule_wake(&mut self, at: u64, waker: &core::task::Waker) { - #[cfg(feature = "integrated-timers")] - let waker = embassy_executor::raw::task_from_waker(waker); - - if self.queue.schedule_wake(at, waker) { - self.dispatch() - } - } - - /// Dequeues expired timers and returns the next alarm time. - pub fn next_expiration(&mut self, now: u64) -> u64 { - self.queue.next_expiration(now) - } - - /// Handle the alarm. - /// - /// Call this function when the next alarm is due. - pub fn dispatch(&mut self) { - let mut next_expiration = self.next_expiration(embassy_time_driver::now()); - - while !(self.set_alarm)(next_expiration) { - // next_expiration is in the past, dequeue and find a new expiration - next_expiration = self.next_expiration(next_expiration); - } - } -} - -/// A [`GenericTimerQueue`] protected by a critical section. Directly useable as a [`TimerQueue`]. -pub struct GlobalTimerQueue { - inner: Mutex bool>>>, -} - -impl GlobalTimerQueue { - /// Creates a new timer queue. - /// - /// `set_alarm` is a function that should set the next alarm time. The function should - /// return `true` if the alarm was set, and `false` if the alarm was in the past. - pub const fn new(set_alarm: fn(u64) -> bool) -> Self { - Self { - inner: Mutex::new(RefCell::new(GenericTimerQueue::new(set_alarm))), - } - } - - /// Schedules a task to run at a specific time, and returns whether any changes were made. - pub fn schedule_wake(&self, at: u64, waker: &core::task::Waker) { - critical_section::with(|cs| { - let mut inner = self.inner.borrow_ref_mut(cs); - inner.schedule_wake(at, waker); - }); - } - - /// Dequeues expired timers and returns the next alarm time. - pub fn next_expiration(&self, now: u64) -> u64 { - critical_section::with(|cs| { - let mut inner = self.inner.borrow_ref_mut(cs); - inner.next_expiration(now) - }) - } - - /// Handle the alarm. - /// - /// Call this function when the next alarm is due. - pub fn dispatch(&self) { - critical_section::with(|cs| { - let mut inner = self.inner.borrow_ref_mut(cs); - inner.dispatch() - }) - } -} - -impl TimerQueue for GlobalTimerQueue { - fn schedule_wake(&'static self, at: u64, waker: &Waker) { - GlobalTimerQueue::schedule_wake(self, at, waker) - } -} diff --git a/embassy-time-queue-driver/src/queue_integrated.rs b/embassy-time-queue-driver/src/queue_integrated.rs index b905c00c3..6bb4c0c1a 100644 --- a/embassy-time-queue-driver/src/queue_integrated.rs +++ b/embassy-time-queue-driver/src/queue_integrated.rs @@ -1,15 +1,16 @@ //! Timer queue operations. use core::cell::Cell; use core::cmp::min; +use core::task::Waker; use embassy_executor::raw::TaskRef; /// A timer queue, with items integrated into tasks. -pub struct TimerQueue { +pub struct Queue { head: Cell>, } -impl TimerQueue { +impl Queue { /// Creates a new timer queue. pub const fn new() -> Self { Self { head: Cell::new(None) } @@ -19,11 +20,12 @@ impl TimerQueue { /// /// If this function returns `true`, the called should find the next expiration time and set /// a new alarm for that time. - pub fn schedule_wake(&mut self, at: u64, p: TaskRef) -> bool { - let item = p.timer_queue_item(); + pub fn schedule_wake(&mut self, at: u64, waker: &Waker) -> bool { + let task = embassy_executor::raw::task_from_waker(waker); + let item = task.timer_queue_item(); if item.next.get().is_none() { // If not in the queue, add it and update. - let prev = self.head.replace(Some(p)); + let prev = self.head.replace(Some(task)); item.next.set(if prev.is_none() { Some(unsafe { TaskRef::dangling() }) } else { diff --git a/embassy-time/Cargo.toml b/embassy-time/Cargo.toml index e3074119f..9959e2863 100644 --- a/embassy-time/Cargo.toml +++ b/embassy-time/Cargo.toml @@ -384,7 +384,6 @@ tick-hz-5_242_880_000 = ["embassy-time-driver/tick-hz-5_242_880_000"] [dependencies] embassy-time-driver = { version = "0.1.0", path = "../embassy-time-driver" } -embassy-time-queue-driver = { version = "0.1.0", path = "../embassy-time-queue-driver" } defmt = { version = "0.3", optional = true } log = { version = "0.4.14", optional = true } diff --git a/embassy-time/src/driver_mock.rs b/embassy-time/src/driver_mock.rs index 829eb0437..138d60499 100644 --- a/embassy-time/src/driver_mock.rs +++ b/embassy-time/src/driver_mock.rs @@ -1,7 +1,9 @@ use core::cell::RefCell; +use core::task::Waker; use critical_section::Mutex as CsMutex; use embassy_time_driver::Driver; +use embassy_time_queue_driver::Queue; use crate::{Duration, Instant}; @@ -52,50 +54,12 @@ impl MockDriver { /// Advances the time by the specified [`Duration`]. /// Calling any alarm callbacks that are due. pub fn advance(&self, duration: Duration) { - let notify = { - critical_section::with(|cs| { - let mut inner = self.0.borrow_ref_mut(cs); - - inner.now += duration; - - let now = inner.now.as_ticks(); - - if inner.alarm.timestamp <= now { - inner.alarm.timestamp = u64::MAX; - - Some((inner.alarm.callback, inner.alarm.ctx)) - } else { - None - } - }) - }; - - if let Some((callback, ctx)) = notify { - (callback)(ctx); - } - } - - /// Configures a callback to be called when the alarm fires. - pub fn set_alarm_callback(&self, callback: fn(*mut ()), ctx: *mut ()) { critical_section::with(|cs| { - let mut inner = self.0.borrow_ref_mut(cs); + let inner = &mut *self.0.borrow_ref_mut(cs); - inner.alarm.callback = callback; - inner.alarm.ctx = ctx; - }); - } - - /// Sets the alarm to fire at the specified timestamp. - pub fn set_alarm(&self, timestamp: u64) -> bool { - critical_section::with(|cs| { - let mut inner = self.0.borrow_ref_mut(cs); - - if timestamp <= inner.now.as_ticks() { - false - } else { - inner.alarm.timestamp = timestamp; - true - } + inner.now += duration; + // wake expired tasks. + inner.queue.next_expiration(inner.now.as_ticks()); }) } } @@ -104,44 +68,38 @@ impl Driver for MockDriver { fn now(&self) -> u64 { critical_section::with(|cs| self.0.borrow_ref(cs).now).as_ticks() } + + fn schedule_wake(&self, at: u64, waker: &Waker) { + critical_section::with(|cs| { + let inner = &mut *self.0.borrow_ref_mut(cs); + // enqueue it + inner.queue.schedule_wake(at, waker); + // wake it if it's in the past. + inner.queue.next_expiration(inner.now.as_ticks()); + }) + } } struct InnerMockDriver { now: Instant, - alarm: AlarmState, + queue: Queue, } impl InnerMockDriver { const fn new() -> Self { Self { now: Instant::from_ticks(0), - alarm: AlarmState::new(), + queue: Queue::new(), } } } -struct AlarmState { - timestamp: u64, - callback: fn(*mut ()), - ctx: *mut (), -} - -impl AlarmState { - const fn new() -> Self { - Self { - timestamp: u64::MAX, - callback: Self::noop, - ctx: core::ptr::null_mut(), - } - } - - fn noop(_ctx: *mut ()) {} -} - -unsafe impl Send for AlarmState {} - #[cfg(test)] mod tests { + use core::sync::atomic::{AtomicBool, Ordering}; + use std::sync::Arc; + use std::task::Wake; + use serial_test::serial; use super::*; @@ -163,24 +121,25 @@ mod tests { #[test] #[serial] - fn test_set_alarm_not_in_future() { + fn test_schedule_wake() { setup(); - let driver = MockDriver::get(); - assert_eq!(false, driver.set_alarm(driver.now())); - } + static CALLBACK_CALLED: AtomicBool = AtomicBool::new(false); - #[test] - #[serial] - fn test_alarm() { - setup(); + struct MockWaker; + + impl Wake for MockWaker { + fn wake(self: Arc) { + CALLBACK_CALLED.store(true, Ordering::Relaxed); + } + } + let waker = Arc::new(MockWaker).into(); let driver = MockDriver::get(); - static mut CALLBACK_CALLED: bool = false; - driver.set_alarm_callback(|_| unsafe { CALLBACK_CALLED = true }, core::ptr::null_mut()); - driver.set_alarm(driver.now() + 1); - assert_eq!(false, unsafe { CALLBACK_CALLED }); + + driver.schedule_wake(driver.now() + 1, &waker); + assert_eq!(false, CALLBACK_CALLED.load(Ordering::Relaxed)); driver.advance(Duration::from_secs(1)); - assert_eq!(true, unsafe { CALLBACK_CALLED }); + assert_eq!(true, CALLBACK_CALLED.load(Ordering::Relaxed)); } } diff --git a/embassy-time/src/driver_std.rs b/embassy-time/src/driver_std.rs index 45467f09b..35888fddd 100644 --- a/embassy-time/src/driver_std.rs +++ b/embassy-time/src/driver_std.rs @@ -1,97 +1,67 @@ -use std::cell::{RefCell, UnsafeCell}; -use std::mem::MaybeUninit; -use std::sync::{Condvar, Mutex, Once}; +use std::sync::{Condvar, Mutex}; +use std::thread; use std::time::{Duration as StdDuration, Instant as StdInstant}; -use std::{ptr, thread}; -use critical_section::Mutex as CsMutex; use embassy_time_driver::Driver; -use embassy_time_queue_driver::GlobalTimerQueue; - -struct AlarmState { - timestamp: u64, -} - -unsafe impl Send for AlarmState {} - -impl AlarmState { - const fn new() -> Self { - Self { timestamp: u64::MAX } - } -} +use embassy_time_queue_driver::Queue; struct TimeDriver { - once: Once, - // The STD Driver implementation requires the alarm's mutex to be reentrant, which the STD Mutex isn't - // Fortunately, mutexes based on the `critical-section` crate are reentrant, because the critical sections - // themselves are reentrant - alarm: UninitCell>>, - zero_instant: UninitCell, - signaler: UninitCell, + signaler: Signaler, + inner: Mutex, +} + +struct Inner { + zero_instant: Option, + queue: Queue, } embassy_time_driver::time_driver_impl!(static DRIVER: TimeDriver = TimeDriver { - once: Once::new(), - alarm: UninitCell::uninit(), - zero_instant: UninitCell::uninit(), - signaler: UninitCell::uninit(), + inner: Mutex::new(Inner{ + zero_instant: None, + queue: Queue::new(), + }), + signaler: Signaler::new(), }); -impl TimeDriver { - fn init(&self) { - self.once.call_once(|| unsafe { - self.alarm - .write(CsMutex::new(RefCell::new(const { AlarmState::new() }))); - self.zero_instant.write(StdInstant::now()); - self.signaler.write(Signaler::new()); - - thread::spawn(Self::alarm_thread); - }); - } - - fn alarm_thread() { - let zero = unsafe { DRIVER.zero_instant.read() }; - loop { - let now = DRIVER.now(); - - let next_alarm = critical_section::with(|cs| { - let mut alarm = unsafe { DRIVER.alarm.as_ref() }.borrow_ref_mut(cs); - if alarm.timestamp <= now { - alarm.timestamp = u64::MAX; - - TIMER_QUEUE_DRIVER.dispatch(); - } - alarm.timestamp - }); - - // Ensure we don't overflow - let until = zero - .checked_add(StdDuration::from_micros(next_alarm)) - .unwrap_or_else(|| StdInstant::now() + StdDuration::from_secs(1)); - - unsafe { DRIVER.signaler.as_ref() }.wait_until(until); - } - } - - fn set_alarm(&self, timestamp: u64) -> bool { - self.init(); - critical_section::with(|cs| { - let mut alarm = unsafe { self.alarm.as_ref() }.borrow_ref_mut(cs); - alarm.timestamp = timestamp; - unsafe { self.signaler.as_ref() }.signal(); - }); - - true +impl Inner { + fn init(&mut self) -> StdInstant { + *self.zero_instant.get_or_insert_with(|| { + thread::spawn(alarm_thread); + StdInstant::now() + }) } } impl Driver for TimeDriver { fn now(&self) -> u64 { - self.init(); - - let zero = unsafe { self.zero_instant.read() }; + let mut inner = self.inner.lock().unwrap(); + let zero = inner.init(); StdInstant::now().duration_since(zero).as_micros() as u64 } + + fn schedule_wake(&self, at: u64, waker: &core::task::Waker) { + let mut inner = self.inner.lock().unwrap(); + inner.init(); + if inner.queue.schedule_wake(at, waker) { + self.signaler.signal(); + } + } +} + +fn alarm_thread() { + let zero = DRIVER.inner.lock().unwrap().zero_instant.unwrap(); + loop { + let now = DRIVER.now(); + + let next_alarm = DRIVER.inner.lock().unwrap().queue.next_expiration(now); + + // Ensure we don't overflow + let until = zero + .checked_add(StdDuration::from_micros(next_alarm)) + .unwrap_or_else(|| StdInstant::now() + StdDuration::from_secs(1)); + + DRIVER.signaler.wait_until(until); + } } struct Signaler { @@ -100,7 +70,7 @@ struct Signaler { } impl Signaler { - fn new() -> Self { + const fn new() -> Self { Self { mutex: Mutex::new(false), condvar: Condvar::new(), @@ -132,40 +102,3 @@ impl Signaler { self.condvar.notify_one(); } } - -pub(crate) struct UninitCell(MaybeUninit>); -unsafe impl Send for UninitCell {} -unsafe impl Sync for UninitCell {} - -impl UninitCell { - pub const fn uninit() -> Self { - Self(MaybeUninit::uninit()) - } - - pub unsafe fn as_ptr(&self) -> *const T { - (*self.0.as_ptr()).get() - } - - pub unsafe fn as_mut_ptr(&self) -> *mut T { - (*self.0.as_ptr()).get() - } - - pub unsafe fn as_ref(&self) -> &T { - &*self.as_ptr() - } - - pub unsafe fn write(&self, val: T) { - ptr::write(self.as_mut_ptr(), val) - } -} - -impl UninitCell { - pub unsafe fn read(&self) -> T { - ptr::read(self.as_mut_ptr()) - } -} - -embassy_time_queue_driver::timer_queue_impl!( - static TIMER_QUEUE_DRIVER: GlobalTimerQueue - = GlobalTimerQueue::new(|next_expiration| DRIVER.set_alarm(next_expiration)) -); diff --git a/embassy-time/src/driver_wasm.rs b/embassy-time/src/driver_wasm.rs index dcc935fde..bcdd1670b 100644 --- a/embassy-time/src/driver_wasm.rs +++ b/embassy-time/src/driver_wasm.rs @@ -1,10 +1,7 @@ -use std::cell::UnsafeCell; -use std::mem::MaybeUninit; -use std::ptr; -use std::sync::{Mutex, Once}; +use std::sync::Mutex; use embassy_time_driver::Driver; -use embassy_time_queue_driver::GlobalTimerQueue; +use embassy_time_queue_driver::Queue; use wasm_bindgen::prelude::*; use wasm_timer::Instant as StdInstant; @@ -12,8 +9,6 @@ struct AlarmState { token: Option, } -unsafe impl Send for AlarmState {} - impl AlarmState { const fn new() -> Self { Self { token: None } @@ -27,33 +22,38 @@ extern "C" { } struct TimeDriver { - once: Once, - alarm: UninitCell>, - zero_instant: UninitCell, - closure: UninitCell>, + inner: Mutex, } +struct Inner { + alarm: AlarmState, + zero_instant: Option, + queue: Queue, + closure: Option>, +} + +unsafe impl Send for Inner {} + embassy_time_driver::time_driver_impl!(static DRIVER: TimeDriver = TimeDriver { - once: Once::new(), - alarm: UninitCell::uninit(), - zero_instant: UninitCell::uninit(), - closure: UninitCell::uninit() + inner: Mutex::new(Inner{ + zero_instant: None, + queue: Queue::new(), + alarm: AlarmState::new(), + closure: None, + }), }); -impl TimeDriver { - fn init(&self) { - self.once.call_once(|| unsafe { - self.alarm.write(Mutex::new(const { AlarmState::new() })); - self.zero_instant.write(StdInstant::now()); - self.closure - .write(Closure::new(Box::new(|| TIMER_QUEUE_DRIVER.dispatch()))); - }); +impl Inner { + fn init(&mut self) -> StdInstant { + *self.zero_instant.get_or_insert_with(StdInstant::now) } - fn set_alarm(&self, timestamp: u64) -> bool { - self.init(); - let mut alarm = unsafe { self.alarm.as_ref() }.lock().unwrap(); - if let Some(token) = alarm.token { + fn now(&mut self) -> u64 { + StdInstant::now().duration_since(self.zero_instant.unwrap()).as_micros() as u64 + } + + fn set_alarm(&mut self, timestamp: u64) -> bool { + if let Some(token) = self.alarm.token { clearTimeout(token); } @@ -62,7 +62,8 @@ impl TimeDriver { false } else { let timeout = (timestamp - now) as u32; - alarm.token = Some(setTimeout(unsafe { self.closure.as_ref() }, timeout / 1000)); + let closure = self.closure.get_or_insert_with(|| Closure::new(dispatch)); + self.alarm.token = Some(setTimeout(closure, timeout / 1000)); true } @@ -71,45 +72,32 @@ impl TimeDriver { impl Driver for TimeDriver { fn now(&self) -> u64 { - self.init(); - - let zero = unsafe { self.zero_instant.read() }; + let mut inner = self.inner.lock().unwrap(); + let zero = inner.init(); StdInstant::now().duration_since(zero).as_micros() as u64 } -} -pub(crate) struct UninitCell(MaybeUninit>); -unsafe impl Send for UninitCell {} -unsafe impl Sync for UninitCell {} - -impl UninitCell { - pub const fn uninit() -> Self { - Self(MaybeUninit::uninit()) - } - unsafe fn as_ptr(&self) -> *const T { - (*self.0.as_ptr()).get() - } - - pub unsafe fn as_mut_ptr(&self) -> *mut T { - (*self.0.as_ptr()).get() - } - - pub unsafe fn as_ref(&self) -> &T { - &*self.as_ptr() - } - - pub unsafe fn write(&self, val: T) { - ptr::write(self.as_mut_ptr(), val) + fn schedule_wake(&self, at: u64, waker: &core::task::Waker) { + let mut inner = self.inner.lock().unwrap(); + inner.init(); + if inner.queue.schedule_wake(at, waker) { + let now = inner.now(); + let mut next = inner.queue.next_expiration(now); + while !inner.set_alarm(next) { + let now = inner.now(); + next = inner.queue.next_expiration(now); + } + } } } -impl UninitCell { - pub unsafe fn read(&self) -> T { - ptr::read(self.as_mut_ptr()) +fn dispatch() { + let inner = &mut *DRIVER.inner.lock().unwrap(); + + let now = inner.now(); + let mut next = inner.queue.next_expiration(now); + while !inner.set_alarm(next) { + let now = inner.now(); + next = inner.queue.next_expiration(now); } } - -embassy_time_queue_driver::timer_queue_impl!( - static TIMER_QUEUE_DRIVER: GlobalTimerQueue - = GlobalTimerQueue::new(|next_expiration| DRIVER.set_alarm(next_expiration)) -); From 6f08c62d5ddcbeba01e098e11b9bc4ed1dfa7cc2 Mon Sep 17 00:00:00 2001 From: Alexander Walter Date: Sat, 14 Dec 2024 00:11:32 +0100 Subject: [PATCH 320/361] Add nrf9160 --- embassy-nrf/src/lib.rs | 2 +- embassy-nrf/src/power.rs | 15 +++++++++++++-- 2 files changed, 14 insertions(+), 3 deletions(-) diff --git a/embassy-nrf/src/lib.rs b/embassy-nrf/src/lib.rs index b6283c7f5..ec5e9f864 100644 --- a/embassy-nrf/src/lib.rs +++ b/embassy-nrf/src/lib.rs @@ -107,7 +107,7 @@ pub mod nvmc; ))] pub mod pdm; #[cfg(not(feature = "_nrf54l"))] // TODO -#[cfg(feature = "nrf52840")] +#[cfg(any(feature = "nrf52840", feature = "nrf9160-s", feature = "nrf9160-ns"))] pub mod power; #[cfg(not(feature = "_nrf54l"))] // TODO pub mod ppi; diff --git a/embassy-nrf/src/power.rs b/embassy-nrf/src/power.rs index 440028380..66dfbae65 100644 --- a/embassy-nrf/src/power.rs +++ b/embassy-nrf/src/power.rs @@ -1,13 +1,24 @@ //! Power -use crate::chip::pac::{NFCT, POWER}; +#[cfg(feature = "nrf52840")] +use crate::chip::pac::NFCT; -/// Puts the MCU into "System Off" mode with a power usage 0f 0.4 uA +#[cfg(feature = "nrf52840")] +use crate::chip::pac::POWER; + +#[cfg(any(feature = "nrf9160-s", feature = "nrf9160-ns"))] +use crate::chip::pac::REGULATORS; + +/// Puts the MCU into "System Off" mode with minimal power usage pub fn set_system_off() { + #[cfg(feature = "nrf52840")] POWER.systemoff().write(|w| w.set_systemoff(true)); + #[cfg(any(feature = "nrf9160-s", feature = "nrf9160-ns"))] + REGULATORS.systemoff().write(|w| w.set_systemoff(true)); } /// Wake the system if there if an NFC field close to the nrf52840's antenna +#[cfg(feature = "nrf52840")] pub fn wake_on_nfc_sense() { NFCT.tasks_sense().write_value(0x01); } From be1ed104e47e16eca1ec4e403db9ae02da5c9e27 Mon Sep 17 00:00:00 2001 From: Alexander Walter Date: Sat, 14 Dec 2024 00:57:55 +0100 Subject: [PATCH 321/361] Add trait embedded_io_async to uarte --- embassy-nrf/src/uarte.rs | 57 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 57 insertions(+) diff --git a/embassy-nrf/src/uarte.rs b/embassy-nrf/src/uarte.rs index 2a59d029d..378325749 100644 --- a/embassy-nrf/src/uarte.rs +++ b/embassy-nrf/src/uarte.rs @@ -1047,3 +1047,60 @@ mod eh02 { } } } + +mod _embedded_io { + use super::*; + + impl embedded_io_async::Error for Error { + fn kind(&self) -> embedded_io_async::ErrorKind { + match *self { + Error::BufferTooLong => embedded_io_async::ErrorKind::InvalidInput, + Error::BufferNotInRAM => embedded_io_async::ErrorKind::Unsupported, + Error::Framing => embedded_io_async::ErrorKind::InvalidData, + Error::Parity => embedded_io_async::ErrorKind::InvalidData, + Error::Overrun => embedded_io_async::ErrorKind::OutOfMemory, + Error::Break => embedded_io_async::ErrorKind::ConnectionAborted, + } + } + } + + impl<'d, U: Instance> embedded_io_async::ErrorType for Uarte<'d, U> { + type Error = Error; + } + + impl<'d, U: Instance> embedded_io_async::ErrorType for UarteRx<'d, U> { + type Error = Error; + } + + impl<'d, U: Instance> embedded_io_async::ErrorType for UarteTx<'d, U> { + type Error = Error; + } + + impl<'d, U: Instance> embedded_io_async::Read for Uarte<'d, U> { + async fn read(&mut self, buf: &mut [u8]) -> Result { + self.read(buf).await?; + Ok(buf.len()) + } + } + + impl<'d: 'd, U: Instance> embedded_io_async::Read for UarteRx<'d, U> { + async fn read(&mut self, buf: &mut [u8]) -> Result { + self.read(buf).await?; + Ok(buf.len()) + } + } + + impl<'d, U: Instance> embedded_io_async::Write for Uarte<'d, U> { + async fn write(&mut self, buf: &[u8]) -> Result { + self.write(buf).await?; + Ok(buf.len()) + } + } + + impl<'d: 'd, U: Instance> embedded_io_async::Write for UarteTx<'d, U> { + async fn write(&mut self, buf: &[u8]) -> Result { + self.write(buf).await?; + Ok(buf.len()) + } + } +} From c4f6509cf28e1a8c2401a67f771dd8b959ae7c49 Mon Sep 17 00:00:00 2001 From: Alexander Walter Date: Sat, 14 Dec 2024 01:02:35 +0100 Subject: [PATCH 322/361] rustfmt --- embassy-nrf/src/power.rs | 2 -- 1 file changed, 2 deletions(-) diff --git a/embassy-nrf/src/power.rs b/embassy-nrf/src/power.rs index 66dfbae65..ab6460625 100644 --- a/embassy-nrf/src/power.rs +++ b/embassy-nrf/src/power.rs @@ -2,10 +2,8 @@ #[cfg(feature = "nrf52840")] use crate::chip::pac::NFCT; - #[cfg(feature = "nrf52840")] use crate::chip::pac::POWER; - #[cfg(any(feature = "nrf9160-s", feature = "nrf9160-ns"))] use crate::chip::pac::REGULATORS; From 854d1f3743e8759095bbcd25fb0d73884562ee9d Mon Sep 17 00:00:00 2001 From: vinsynth <1.5vhunt@gmail.com> Date: Sat, 14 Dec 2024 00:32:47 -0500 Subject: [PATCH 323/361] add sysclk frequency argument to PioI2Out::new --- embassy-rp/src/pio_programs/i2s.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/embassy-rp/src/pio_programs/i2s.rs b/embassy-rp/src/pio_programs/i2s.rs index e3f1f89d4..8989afb4c 100644 --- a/embassy-rp/src/pio_programs/i2s.rs +++ b/embassy-rp/src/pio_programs/i2s.rs @@ -51,6 +51,7 @@ impl<'a, P: Instance, const S: usize> PioI2sOut<'a, P, S> { data_pin: impl PioPin, bit_clock_pin: impl PioPin, lr_clock_pin: impl PioPin, + sysclk_frequency: u32, sample_rate: u32, bit_depth: u32, channels: u32, @@ -67,7 +68,7 @@ impl<'a, P: Instance, const S: usize> PioI2sOut<'a, P, S> { cfg.use_program(&program.prg, &[&bit_clock_pin, &left_right_clock_pin]); cfg.set_out_pins(&[&data_pin]); let clock_frequency = sample_rate * bit_depth * channels; - cfg.clock_divider = (125_000_000. / clock_frequency as f64 / 2.).to_fixed(); + cfg.clock_divider = (sysclk_frequency as f64 / clock_frequency as f64 / 2.).to_fixed(); cfg.shift_out = ShiftConfig { threshold: 32, direction: ShiftDirection::Left, From 3b5993cf48c0a1a5f88f5d0f771df16ceb4de3fb Mon Sep 17 00:00:00 2001 From: Alexander Walter Date: Sat, 14 Dec 2024 15:40:08 +0100 Subject: [PATCH 324/361] Move wake on sense to nfct --- embassy-nrf/src/nfct.rs | 7 ++++++- embassy-nrf/src/power.rs | 6 ------ 2 files changed, 6 insertions(+), 7 deletions(-) diff --git a/embassy-nrf/src/nfct.rs b/embassy-nrf/src/nfct.rs index cbd3920ee..06cb0916c 100644 --- a/embassy-nrf/src/nfct.rs +++ b/embassy-nrf/src/nfct.rs @@ -18,7 +18,7 @@ use embassy_sync::waitqueue::AtomicWaker; pub use vals::{Bitframesdd as SddPat, Discardmode as DiscardMode}; use crate::interrupt::InterruptExt; -use crate::pac::nfct::vals; +use crate::pac::{nfct::vals, NFCT}; use crate::peripherals::NFCT; use crate::util::slice_in_ram; use crate::{interrupt, pac, Peripheral}; @@ -420,3 +420,8 @@ impl<'d> NfcT<'d> { Ok(n) } } + +/// Wake the system if there if an NFC field close to the antenna +pub fn wake_on_nfc_sense() { + NFCT.tasks_sense().write_value(0x01); +} diff --git a/embassy-nrf/src/power.rs b/embassy-nrf/src/power.rs index ab6460625..730efe094 100644 --- a/embassy-nrf/src/power.rs +++ b/embassy-nrf/src/power.rs @@ -14,9 +14,3 @@ pub fn set_system_off() { #[cfg(any(feature = "nrf9160-s", feature = "nrf9160-ns"))] REGULATORS.systemoff().write(|w| w.set_systemoff(true)); } - -/// Wake the system if there if an NFC field close to the nrf52840's antenna -#[cfg(feature = "nrf52840")] -pub fn wake_on_nfc_sense() { - NFCT.tasks_sense().write_value(0x01); -} From f5ead85377ab78d8f463df4f1e358770ff3eca5b Mon Sep 17 00:00:00 2001 From: Alexander Walter Date: Sat, 14 Dec 2024 15:41:31 +0100 Subject: [PATCH 325/361] Just impl Write --- embassy-nrf/src/uarte.rs | 18 ------------------ 1 file changed, 18 deletions(-) diff --git a/embassy-nrf/src/uarte.rs b/embassy-nrf/src/uarte.rs index 378325749..ebb4dd941 100644 --- a/embassy-nrf/src/uarte.rs +++ b/embassy-nrf/src/uarte.rs @@ -1068,28 +1068,10 @@ mod _embedded_io { type Error = Error; } - impl<'d, U: Instance> embedded_io_async::ErrorType for UarteRx<'d, U> { - type Error = Error; - } - impl<'d, U: Instance> embedded_io_async::ErrorType for UarteTx<'d, U> { type Error = Error; } - impl<'d, U: Instance> embedded_io_async::Read for Uarte<'d, U> { - async fn read(&mut self, buf: &mut [u8]) -> Result { - self.read(buf).await?; - Ok(buf.len()) - } - } - - impl<'d: 'd, U: Instance> embedded_io_async::Read for UarteRx<'d, U> { - async fn read(&mut self, buf: &mut [u8]) -> Result { - self.read(buf).await?; - Ok(buf.len()) - } - } - impl<'d, U: Instance> embedded_io_async::Write for Uarte<'d, U> { async fn write(&mut self, buf: &[u8]) -> Result { self.write(buf).await?; From ea374a47368fb917e6a89b0bc567a225778f4f9f Mon Sep 17 00:00:00 2001 From: Alexander Walter Date: Sat, 14 Dec 2024 15:48:18 +0100 Subject: [PATCH 326/361] Fix rustfm and warning --- embassy-nrf/src/nfct.rs | 3 ++- embassy-nrf/src/power.rs | 2 -- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/embassy-nrf/src/nfct.rs b/embassy-nrf/src/nfct.rs index 06cb0916c..8b4b6dfe0 100644 --- a/embassy-nrf/src/nfct.rs +++ b/embassy-nrf/src/nfct.rs @@ -18,7 +18,8 @@ use embassy_sync::waitqueue::AtomicWaker; pub use vals::{Bitframesdd as SddPat, Discardmode as DiscardMode}; use crate::interrupt::InterruptExt; -use crate::pac::{nfct::vals, NFCT}; +use crate::pac::nfct::vals; +use crate::pac::NFCT; use crate::peripherals::NFCT; use crate::util::slice_in_ram; use crate::{interrupt, pac, Peripheral}; diff --git a/embassy-nrf/src/power.rs b/embassy-nrf/src/power.rs index 730efe094..f93bf8f49 100644 --- a/embassy-nrf/src/power.rs +++ b/embassy-nrf/src/power.rs @@ -1,7 +1,5 @@ //! Power -#[cfg(feature = "nrf52840")] -use crate::chip::pac::NFCT; #[cfg(feature = "nrf52840")] use crate::chip::pac::POWER; #[cfg(any(feature = "nrf9160-s", feature = "nrf9160-ns"))] From ffbef9316d523724d050f32e800668a56e534657 Mon Sep 17 00:00:00 2001 From: vinsynth <1.5vhunt@gmail.com> Date: Sat, 14 Dec 2024 11:02:18 -0500 Subject: [PATCH 327/361] i2s frequency relative to sysclk --- embassy-rp/src/pio_programs/i2s.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/embassy-rp/src/pio_programs/i2s.rs b/embassy-rp/src/pio_programs/i2s.rs index 8989afb4c..87fb2e19f 100644 --- a/embassy-rp/src/pio_programs/i2s.rs +++ b/embassy-rp/src/pio_programs/i2s.rs @@ -51,7 +51,6 @@ impl<'a, P: Instance, const S: usize> PioI2sOut<'a, P, S> { data_pin: impl PioPin, bit_clock_pin: impl PioPin, lr_clock_pin: impl PioPin, - sysclk_frequency: u32, sample_rate: u32, bit_depth: u32, channels: u32, @@ -68,7 +67,7 @@ impl<'a, P: Instance, const S: usize> PioI2sOut<'a, P, S> { cfg.use_program(&program.prg, &[&bit_clock_pin, &left_right_clock_pin]); cfg.set_out_pins(&[&data_pin]); let clock_frequency = sample_rate * bit_depth * channels; - cfg.clock_divider = (sysclk_frequency as f64 / clock_frequency as f64 / 2.).to_fixed(); + cfg.clock_divider = (crate::clocks::clk_sys_freq() as f64 / clock_frequency as f64 / 2.).to_fixed(); cfg.shift_out = ShiftConfig { threshold: 32, direction: ShiftDirection::Left, From 4b31639dcacd23ce24d9a33d3a491c8d58ca9cfd Mon Sep 17 00:00:00 2001 From: Fabian Wolter Date: Sat, 14 Dec 2024 23:32:08 +0100 Subject: [PATCH 328/361] STM32F0 fix using HSI48 as SYSCLK on devices with CRS Fixes #3651 --- embassy-stm32/src/rcc/f013.rs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/embassy-stm32/src/rcc/f013.rs b/embassy-stm32/src/rcc/f013.rs index c915f1b16..a38ca955e 100644 --- a/embassy-stm32/src/rcc/f013.rs +++ b/embassy-stm32/src/rcc/f013.rs @@ -229,6 +229,9 @@ pub(crate) unsafe fn init(config: Config) { Sysclk::HSI => unwrap!(hsi), Sysclk::HSE => unwrap!(hse), Sysclk::PLL1_P => unwrap!(pll), + #[cfg(crs)] + Sysclk::HSI48 => unwrap!(hsi48), + #[cfg(not(crs))] _ => unreachable!(), }; From 2f2e2c6031a1abaecdac5ed2febe109e647fe6fd Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Mon, 9 Dec 2024 00:28:14 +0100 Subject: [PATCH 329/361] Make `integrated-timers` the default, remove Cargo feature. --- ci-nightly.sh | 9 +----- ci-xtensa.sh | 13 ++++----- ci.sh | 11 ++----- docs/examples/basic/Cargo.toml | 2 +- docs/pages/new_project.adoc | 4 +-- embassy-executor/Cargo.toml | 6 +--- embassy-executor/src/raw/mod.rs | 7 ----- embassy-executor/src/raw/state_atomics.rs | 4 --- embassy-executor/src/raw/state_atomics_arm.rs | 4 --- .../src/raw/state_critical_section.rs | 4 --- embassy-executor/src/raw/trace.rs | 22 +++++--------- embassy-stm32/Cargo.toml | 1 - embassy-time-queue-driver/Cargo.toml | 29 ++++++++++--------- embassy-time-queue-driver/src/lib.rs | 11 ++++--- embassy-time/Cargo.toml | 1 + examples/boot/application/nrf/Cargo.toml | 2 +- examples/boot/application/rp/Cargo.toml | 2 +- examples/boot/application/stm32f3/Cargo.toml | 2 +- examples/boot/application/stm32f7/Cargo.toml | 2 +- examples/boot/application/stm32h7/Cargo.toml | 2 +- examples/boot/application/stm32l0/Cargo.toml | 2 +- examples/boot/application/stm32l1/Cargo.toml | 2 +- examples/boot/application/stm32l4/Cargo.toml | 2 +- .../boot/application/stm32wb-dfu/Cargo.toml | 2 +- examples/boot/application/stm32wl/Cargo.toml | 2 +- examples/nrf-rtos-trace/Cargo.toml | 2 +- examples/nrf51/Cargo.toml | 2 +- examples/nrf52810/Cargo.toml | 2 +- examples/nrf52840-rtic/Cargo.toml | 2 +- examples/nrf52840/Cargo.toml | 2 +- examples/nrf5340/Cargo.toml | 2 +- examples/nrf54l15/Cargo.toml | 2 +- examples/nrf9151/ns/Cargo.toml | 2 +- examples/nrf9151/s/Cargo.toml | 2 +- examples/nrf9160/Cargo.toml | 2 +- examples/rp/Cargo.toml | 2 +- examples/rp23/Cargo.toml | 2 +- examples/std/Cargo.toml | 2 +- examples/stm32c0/Cargo.toml | 2 +- examples/stm32f0/Cargo.toml | 2 +- examples/stm32f1/Cargo.toml | 2 +- examples/stm32f2/Cargo.toml | 2 +- examples/stm32f3/Cargo.toml | 2 +- examples/stm32f334/Cargo.toml | 2 +- examples/stm32f4/Cargo.toml | 2 +- examples/stm32f469/Cargo.toml | 2 +- examples/stm32f7/Cargo.toml | 2 +- examples/stm32g0/Cargo.toml | 2 +- examples/stm32g4/Cargo.toml | 2 +- examples/stm32h5/Cargo.toml | 2 +- examples/stm32h7/Cargo.toml | 2 +- examples/stm32h723/Cargo.toml | 2 +- examples/stm32h735/Cargo.toml | 2 +- examples/stm32h755cm4/Cargo.toml | 2 +- examples/stm32h755cm7/Cargo.toml | 2 +- examples/stm32h7b0/Cargo.toml | 2 +- examples/stm32h7rs/Cargo.toml | 2 +- examples/stm32l0/Cargo.toml | 2 +- examples/stm32l1/Cargo.toml | 2 +- examples/stm32l4/Cargo.toml | 2 +- examples/stm32l5/Cargo.toml | 2 +- examples/stm32u0/Cargo.toml | 2 +- examples/stm32u5/Cargo.toml | 2 +- examples/stm32wb/Cargo.toml | 2 +- examples/stm32wba/Cargo.toml | 2 +- examples/stm32wl/Cargo.toml | 2 +- examples/wasm/Cargo.toml | 2 +- tests/nrf/Cargo.toml | 2 +- tests/rp/Cargo.toml | 2 +- tests/stm32/Cargo.toml | 2 +- 70 files changed, 96 insertions(+), 142 deletions(-) diff --git a/ci-nightly.sh b/ci-nightly.sh index bdb364f53..1b69cc18e 100755 --- a/ci-nightly.sh +++ b/ci-nightly.sh @@ -13,20 +13,13 @@ cargo batch \ --- build --release --manifest-path embassy-executor/Cargo.toml --target thumbv7em-none-eabi --features nightly,log \ --- build --release --manifest-path embassy-executor/Cargo.toml --target thumbv7em-none-eabi --features nightly,defmt \ --- build --release --manifest-path embassy-executor/Cargo.toml --target thumbv6m-none-eabi --features nightly,defmt \ - --- build --release --manifest-path embassy-executor/Cargo.toml --target thumbv6m-none-eabi --features nightly,defmt,arch-cortex-m,executor-thread,executor-interrupt,integrated-timers \ + --- build --release --manifest-path embassy-executor/Cargo.toml --target thumbv6m-none-eabi --features nightly,defmt,arch-cortex-m,executor-thread,executor-interrupt \ --- build --release --manifest-path embassy-executor/Cargo.toml --target thumbv7em-none-eabi --features nightly,arch-cortex-m \ - --- build --release --manifest-path embassy-executor/Cargo.toml --target thumbv7em-none-eabi --features nightly,arch-cortex-m,integrated-timers \ --- build --release --manifest-path embassy-executor/Cargo.toml --target thumbv7em-none-eabi --features nightly,arch-cortex-m,executor-thread \ - --- build --release --manifest-path embassy-executor/Cargo.toml --target thumbv7em-none-eabi --features nightly,arch-cortex-m,executor-thread,integrated-timers \ --- build --release --manifest-path embassy-executor/Cargo.toml --target thumbv7em-none-eabi --features nightly,arch-cortex-m,executor-interrupt \ - --- build --release --manifest-path embassy-executor/Cargo.toml --target thumbv7em-none-eabi --features nightly,arch-cortex-m,executor-interrupt,integrated-timers \ --- build --release --manifest-path embassy-executor/Cargo.toml --target thumbv7em-none-eabi --features nightly,arch-cortex-m,executor-thread,executor-interrupt \ - --- build --release --manifest-path embassy-executor/Cargo.toml --target thumbv7em-none-eabi --features nightly,arch-cortex-m,executor-thread,executor-interrupt,integrated-timers \ --- build --release --manifest-path embassy-executor/Cargo.toml --target riscv32imac-unknown-none-elf --features nightly,arch-riscv32 \ - --- build --release --manifest-path embassy-executor/Cargo.toml --target riscv32imac-unknown-none-elf --features nightly,arch-riscv32,integrated-timers \ --- build --release --manifest-path embassy-executor/Cargo.toml --target riscv32imac-unknown-none-elf --features nightly,arch-riscv32,executor-thread \ - --- build --release --manifest-path embassy-executor/Cargo.toml --target riscv32imac-unknown-none-elf --features nightly,arch-riscv32,executor-thread,integrated-timers \ --- build --release --manifest-path examples/nrf52840-rtic/Cargo.toml --target thumbv7em-none-eabi --out-dir out/examples/nrf52840-rtic \ cargo build --release --manifest-path embassy-executor/Cargo.toml --target avr-unknown-gnu-atmega328 -Z build-std=core,alloc --features nightly,arch-avr,avr-device/atmega328p -cargo build --release --manifest-path embassy-executor/Cargo.toml --target avr-unknown-gnu-atmega328 -Z build-std=core,alloc --features nightly,arch-avr,integrated-timers,avr-device/atmega328p diff --git a/ci-xtensa.sh b/ci-xtensa.sh index 2cac7444c..056e85d48 100755 --- a/ci-xtensa.sh +++ b/ci-xtensa.sh @@ -14,18 +14,15 @@ cargo batch \ --- build --release --manifest-path embassy-executor/Cargo.toml --target xtensa-esp32-none-elf --features log \ --- build --release --manifest-path embassy-executor/Cargo.toml --target xtensa-esp32-none-elf --features defmt \ --- build --release --manifest-path embassy-executor/Cargo.toml --target xtensa-esp32s2-none-elf --features defmt \ - --- build --release --manifest-path embassy-executor/Cargo.toml --target xtensa-esp32-none-elf --features defmt,arch-spin,executor-thread,integrated-timers \ - --- build --release --manifest-path embassy-executor/Cargo.toml --target xtensa-esp32s2-none-elf --features defmt,arch-spin,executor-thread,integrated-timers \ - --- build --release --manifest-path embassy-executor/Cargo.toml --target xtensa-esp32s3-none-elf --features defmt,arch-spin,executor-thread,integrated-timers \ + --- build --release --manifest-path embassy-executor/Cargo.toml --target xtensa-esp32-none-elf --features defmt,arch-spin,executor-thread \ + --- build --release --manifest-path embassy-executor/Cargo.toml --target xtensa-esp32s2-none-elf --features defmt,arch-spin,executor-thread \ + --- build --release --manifest-path embassy-executor/Cargo.toml --target xtensa-esp32s3-none-elf --features defmt,arch-spin,executor-thread \ --- build --release --manifest-path embassy-executor/Cargo.toml --target xtensa-esp32-none-elf --features arch-spin \ - --- build --release --manifest-path embassy-executor/Cargo.toml --target xtensa-esp32-none-elf --features arch-spin,integrated-timers \ --- build --release --manifest-path embassy-executor/Cargo.toml --target xtensa-esp32-none-elf --features arch-spin,rtos-trace \ - --- build --release --manifest-path embassy-executor/Cargo.toml --target xtensa-esp32-none-elf --features arch-spin,integrated-timers,rtos-trace \ --- build --release --manifest-path embassy-executor/Cargo.toml --target xtensa-esp32-none-elf --features arch-spin,executor-thread \ - --- build --release --manifest-path embassy-executor/Cargo.toml --target xtensa-esp32-none-elf --features arch-spin,executor-thread,integrated-timers \ --- build --release --manifest-path embassy-sync/Cargo.toml --target xtensa-esp32s2-none-elf --features defmt \ --- build --release --manifest-path embassy-time/Cargo.toml --target xtensa-esp32s2-none-elf --features defmt,defmt-timestamp-uptime,mock-driver \ - --- build --release --manifest-path embassy-time-queue-driver/Cargo.toml --target xtensa-esp32s2-none-elf --features integrated-timers \ + --- build --release --manifest-path embassy-time-queue-driver/Cargo.toml --target xtensa-esp32s2-none-elf \ --- build --release --manifest-path embassy-time-queue-driver/Cargo.toml --target xtensa-esp32s2-none-elf --features generic-queue-8 \ --- build --release --manifest-path embassy-net/Cargo.toml --target xtensa-esp32-none-elf --features defmt,tcp,udp,dns,proto-ipv4,medium-ethernet,packet-trace \ --- build --release --manifest-path embassy-net/Cargo.toml --target xtensa-esp32-none-elf --features defmt,tcp,udp,dns,proto-ipv4,multicast,medium-ethernet \ @@ -38,4 +35,4 @@ cargo batch \ --- build --release --manifest-path embassy-net/Cargo.toml --target xtensa-esp32-none-elf --features defmt,tcp,udp,dns,proto-ipv4,proto-ipv6,medium-ethernet \ --- build --release --manifest-path embassy-net/Cargo.toml --target xtensa-esp32-none-elf --features defmt,tcp,udp,dns,proto-ipv4,proto-ipv6,medium-ip \ --- build --release --manifest-path embassy-net/Cargo.toml --target xtensa-esp32-none-elf --features defmt,tcp,udp,dns,proto-ipv4,proto-ipv6,medium-ip,medium-ethernet \ - --- build --release --manifest-path embassy-net/Cargo.toml --target xtensa-esp32-none-elf --features defmt,tcp,udp,dns,proto-ipv4,proto-ipv6,medium-ip,medium-ethernet,medium-ieee802154 \ \ No newline at end of file + --- build --release --manifest-path embassy-net/Cargo.toml --target xtensa-esp32-none-elf --features defmt,tcp,udp,dns,proto-ipv4,proto-ipv6,medium-ip,medium-ethernet,medium-ieee802154 \ diff --git a/ci.sh b/ci.sh index 71b862632..cb3a2f3f7 100755 --- a/ci.sh +++ b/ci.sh @@ -29,24 +29,17 @@ cargo batch \ --- build --release --manifest-path embassy-executor/Cargo.toml --target thumbv7em-none-eabi --features log \ --- build --release --manifest-path embassy-executor/Cargo.toml --target thumbv7em-none-eabi --features defmt \ --- build --release --manifest-path embassy-executor/Cargo.toml --target thumbv6m-none-eabi --features defmt \ - --- build --release --manifest-path embassy-executor/Cargo.toml --target thumbv6m-none-eabi --features defmt,arch-cortex-m,executor-thread,executor-interrupt,integrated-timers \ + --- build --release --manifest-path embassy-executor/Cargo.toml --target thumbv6m-none-eabi --features defmt,arch-cortex-m,executor-thread,executor-interrupt \ --- build --release --manifest-path embassy-executor/Cargo.toml --target thumbv7em-none-eabi --features arch-cortex-m \ - --- build --release --manifest-path embassy-executor/Cargo.toml --target thumbv7em-none-eabi --features arch-cortex-m,integrated-timers \ --- build --release --manifest-path embassy-executor/Cargo.toml --target thumbv7em-none-eabi --features arch-cortex-m,rtos-trace \ - --- build --release --manifest-path embassy-executor/Cargo.toml --target thumbv7em-none-eabi --features arch-cortex-m,integrated-timers,rtos-trace \ --- build --release --manifest-path embassy-executor/Cargo.toml --target thumbv7em-none-eabi --features arch-cortex-m,executor-thread \ - --- build --release --manifest-path embassy-executor/Cargo.toml --target thumbv7em-none-eabi --features arch-cortex-m,executor-thread,integrated-timers \ --- build --release --manifest-path embassy-executor/Cargo.toml --target thumbv7em-none-eabi --features arch-cortex-m,executor-interrupt \ - --- build --release --manifest-path embassy-executor/Cargo.toml --target thumbv7em-none-eabi --features arch-cortex-m,executor-interrupt,integrated-timers \ --- build --release --manifest-path embassy-executor/Cargo.toml --target thumbv7em-none-eabi --features arch-cortex-m,executor-thread,executor-interrupt \ - --- build --release --manifest-path embassy-executor/Cargo.toml --target thumbv7em-none-eabi --features arch-cortex-m,executor-thread,executor-interrupt,integrated-timers \ --- build --release --manifest-path embassy-executor/Cargo.toml --target riscv32imac-unknown-none-elf --features arch-riscv32 \ - --- build --release --manifest-path embassy-executor/Cargo.toml --target riscv32imac-unknown-none-elf --features arch-riscv32,integrated-timers \ --- build --release --manifest-path embassy-executor/Cargo.toml --target riscv32imac-unknown-none-elf --features arch-riscv32,executor-thread \ - --- build --release --manifest-path embassy-executor/Cargo.toml --target riscv32imac-unknown-none-elf --features arch-riscv32,executor-thread,integrated-timers \ --- build --release --manifest-path embassy-sync/Cargo.toml --target thumbv6m-none-eabi --features defmt \ --- build --release --manifest-path embassy-time/Cargo.toml --target thumbv6m-none-eabi --features defmt,defmt-timestamp-uptime,mock-driver \ - --- build --release --manifest-path embassy-time-queue-driver/Cargo.toml --target thumbv6m-none-eabi --features integrated-timers \ + --- build --release --manifest-path embassy-time-queue-driver/Cargo.toml --target thumbv6m-none-eabi \ --- build --release --manifest-path embassy-time-queue-driver/Cargo.toml --target thumbv6m-none-eabi --features generic-queue-8 \ --- build --release --manifest-path embassy-net/Cargo.toml --target thumbv7em-none-eabi --features defmt,tcp,udp,dns,proto-ipv4,medium-ethernet,packet-trace \ --- build --release --manifest-path embassy-net/Cargo.toml --target thumbv7em-none-eabi --features defmt,tcp,udp,dns,proto-ipv4,multicast,medium-ethernet \ diff --git a/docs/examples/basic/Cargo.toml b/docs/examples/basic/Cargo.toml index d46431b9a..daf83873d 100644 --- a/docs/examples/basic/Cargo.toml +++ b/docs/examples/basic/Cargo.toml @@ -6,7 +6,7 @@ version = "0.1.0" license = "MIT OR Apache-2.0" [dependencies] -embassy-executor = { version = "0.6.3", path = "../../../embassy-executor", features = ["defmt", "integrated-timers", "arch-cortex-m", "executor-thread"] } +embassy-executor = { version = "0.6.3", path = "../../../embassy-executor", features = ["defmt", "arch-cortex-m", "executor-thread"] } embassy-time = { version = "0.3.2", path = "../../../embassy-time", features = ["defmt"] } embassy-nrf = { version = "0.2.0", path = "../../../embassy-nrf", features = ["defmt", "nrf52840", "time-driver-rtc1", "gpiote"] } diff --git a/docs/pages/new_project.adoc b/docs/pages/new_project.adoc index f8dd848be..63340016b 100644 --- a/docs/pages/new_project.adoc +++ b/docs/pages/new_project.adoc @@ -80,7 +80,7 @@ At the time of writing, embassy is already published to crates.io. Therefore, de ---- [dependencies] embassy-stm32 = { version = "0.1.0", features = ["defmt", "time-driver-any", "stm32g474re", "memory-x", "unstable-pac", "exti"] } -embassy-executor = { version = "0.6.3", features = ["nightly", "arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] } +embassy-executor = { version = "0.6.3", features = ["nightly", "arch-cortex-m", "executor-thread", "defmt"] } embassy-time = { version = "0.3.2", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } ---- @@ -100,7 +100,7 @@ An example Cargo.toml file might look as follows: ---- [dependencies] embassy-stm32 = {version = "0.1.0", features = ["defmt", "time-driver-any", "stm32g474re", "memory-x", "unstable-pac", "exti"]} -embassy-executor = { version = "0.3.3", features = ["nightly", "arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] } +embassy-executor = { version = "0.3.3", features = ["nightly", "arch-cortex-m", "executor-thread", "defmt"] } embassy-time = { version = "0.2", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } [patch.crates-io] diff --git a/embassy-executor/Cargo.toml b/embassy-executor/Cargo.toml index 862d25b59..60fe7087a 100644 --- a/embassy-executor/Cargo.toml +++ b/embassy-executor/Cargo.toml @@ -35,7 +35,6 @@ rtos-trace = { version = "0.1.3", optional = true } embassy-executor-macros = { version = "0.6.2", path = "../embassy-executor-macros" } embassy-time-driver = { version = "0.1.0", path = "../embassy-time-driver", optional = true } -embassy-time-queue-driver = { version = "0.1.0", path = "../embassy-time-queue-driver", optional = true } critical-section = "1.1" document-features = "0.2.7" @@ -67,9 +66,6 @@ nightly = ["embassy-executor-macros/nightly"] # See: https://github.com/embassy-rs/embassy/pull/1263 turbowakers = [] -## Use the executor-integrated `embassy-time` timer queue. -integrated-timers = ["dep:embassy-time-driver"] - #! ### Architecture _arch = [] # some arch was picked ## std @@ -94,7 +90,7 @@ executor-interrupt = [] ## Enable tracing support (adds some overhead) trace = [] ## Enable support for rtos-trace framework -rtos-trace = ["dep:rtos-trace", "trace"] +rtos-trace = ["dep:rtos-trace", "trace", "dep:embassy-time-driver"] #! ### Task Arena Size #! Sets the [task arena](#task-arena) size. Necessary if you’re not using `nightly`. diff --git a/embassy-executor/src/raw/mod.rs b/embassy-executor/src/raw/mod.rs index 14d689900..2feaab155 100644 --- a/embassy-executor/src/raw/mod.rs +++ b/embassy-executor/src/raw/mod.rs @@ -16,7 +16,6 @@ mod run_queue; #[cfg_attr(not(target_has_atomic = "8"), path = "state_critical_section.rs")] mod state; -#[cfg(feature = "integrated-timers")] pub mod timer_queue; #[cfg(feature = "trace")] mod trace; @@ -45,7 +44,6 @@ pub(crate) struct TaskHeader { poll_fn: SyncUnsafeCell>, /// Integrated timer queue storage. This field should not be accessed outside of the timer queue. - #[cfg(feature = "integrated-timers")] pub(crate) timer_queue_item: timer_queue::TimerQueueItem, } @@ -87,13 +85,11 @@ impl TaskRef { } /// Returns a reference to the executor that the task is currently running on. - #[cfg(feature = "integrated-timers")] pub unsafe fn executor(self) -> Option<&'static Executor> { self.header().executor.get().map(|e| Executor::wrap(e)) } /// Returns a reference to the timer queue item. - #[cfg(feature = "integrated-timers")] pub fn timer_queue_item(&self) -> &'static timer_queue::TimerQueueItem { &self.header().timer_queue_item } @@ -106,7 +102,6 @@ impl TaskRef { /// /// This functions should only be called by the timer queue implementation, before /// enqueueing the timer item. - #[cfg(feature = "integrated-timers")] pub unsafe fn timer_enqueue(&self) -> timer_queue::TimerEnqueueOperation { self.header().state.timer_enqueue() } @@ -117,7 +112,6 @@ impl TaskRef { /// /// This functions should only be called by the timer queue implementation, after the task has /// been removed from the timer queue. - #[cfg(feature = "integrated-timers")] pub unsafe fn timer_dequeue(&self) { self.header().state.timer_dequeue() } @@ -162,7 +156,6 @@ impl TaskStorage { // Note: this is lazily initialized so that a static `TaskStorage` will go in `.bss` poll_fn: SyncUnsafeCell::new(None), - #[cfg(feature = "integrated-timers")] timer_queue_item: timer_queue::TimerQueueItem::new(), }, future: UninitCell::uninit(), diff --git a/embassy-executor/src/raw/state_atomics.rs b/embassy-executor/src/raw/state_atomics.rs index d03c61ade..15eb9a368 100644 --- a/embassy-executor/src/raw/state_atomics.rs +++ b/embassy-executor/src/raw/state_atomics.rs @@ -1,6 +1,5 @@ use core::sync::atomic::{AtomicU32, Ordering}; -#[cfg(feature = "integrated-timers")] use super::timer_queue::TimerEnqueueOperation; /// Task is spawned (has a future) @@ -8,7 +7,6 @@ pub(crate) const STATE_SPAWNED: u32 = 1 << 0; /// Task is in the executor run queue pub(crate) const STATE_RUN_QUEUED: u32 = 1 << 1; /// Task is in the executor timer queue -#[cfg(feature = "integrated-timers")] pub(crate) const STATE_TIMER_QUEUED: u32 = 1 << 2; pub(crate) struct State { @@ -60,7 +58,6 @@ impl State { } /// Mark the task as timer-queued. Return whether it can be enqueued. - #[cfg(feature = "integrated-timers")] #[inline(always)] pub fn timer_enqueue(&self) -> TimerEnqueueOperation { if self @@ -83,7 +80,6 @@ impl State { } /// Unmark the task as timer-queued. - #[cfg(feature = "integrated-timers")] #[inline(always)] pub fn timer_dequeue(&self) { self.state.fetch_and(!STATE_TIMER_QUEUED, Ordering::Relaxed); diff --git a/embassy-executor/src/raw/state_atomics_arm.rs b/embassy-executor/src/raw/state_atomics_arm.rs index f6f2e8f08..7a152e8c0 100644 --- a/embassy-executor/src/raw/state_atomics_arm.rs +++ b/embassy-executor/src/raw/state_atomics_arm.rs @@ -1,13 +1,11 @@ use core::arch::asm; use core::sync::atomic::{compiler_fence, AtomicBool, AtomicU32, Ordering}; -#[cfg(feature = "integrated-timers")] use super::timer_queue::TimerEnqueueOperation; // Must be kept in sync with the layout of `State`! pub(crate) const STATE_SPAWNED: u32 = 1 << 0; pub(crate) const STATE_RUN_QUEUED: u32 = 1 << 8; -#[cfg(feature = "integrated-timers")] pub(crate) const STATE_TIMER_QUEUED: u32 = 1 << 16; #[repr(C, align(4))] @@ -93,7 +91,6 @@ impl State { } /// Mark the task as timer-queued. Return whether it can be enqueued. - #[cfg(feature = "integrated-timers")] #[inline(always)] pub fn timer_enqueue(&self) -> TimerEnqueueOperation { if self @@ -116,7 +113,6 @@ impl State { } /// Unmark the task as timer-queued. - #[cfg(feature = "integrated-timers")] #[inline(always)] pub fn timer_dequeue(&self) { self.timer_queued.store(false, Ordering::Relaxed); diff --git a/embassy-executor/src/raw/state_critical_section.rs b/embassy-executor/src/raw/state_critical_section.rs index c0ec2f530..367162ba2 100644 --- a/embassy-executor/src/raw/state_critical_section.rs +++ b/embassy-executor/src/raw/state_critical_section.rs @@ -2,7 +2,6 @@ use core::cell::Cell; use critical_section::Mutex; -#[cfg(feature = "integrated-timers")] use super::timer_queue::TimerEnqueueOperation; /// Task is spawned (has a future) @@ -10,7 +9,6 @@ pub(crate) const STATE_SPAWNED: u32 = 1 << 0; /// Task is in the executor run queue pub(crate) const STATE_RUN_QUEUED: u32 = 1 << 1; /// Task is in the executor timer queue -#[cfg(feature = "integrated-timers")] pub(crate) const STATE_TIMER_QUEUED: u32 = 1 << 2; pub(crate) struct State { @@ -77,7 +75,6 @@ impl State { } /// Mark the task as timer-queued. Return whether it can be enqueued. - #[cfg(feature = "integrated-timers")] #[inline(always)] pub fn timer_enqueue(&self) -> TimerEnqueueOperation { self.update(|s| { @@ -93,7 +90,6 @@ impl State { } /// Unmark the task as timer-queued. - #[cfg(feature = "integrated-timers")] #[inline(always)] pub fn timer_dequeue(&self) { self.update(|s| *s &= !STATE_TIMER_QUEUED); diff --git a/embassy-executor/src/raw/trace.rs b/embassy-executor/src/raw/trace.rs index c7bcf9c11..b34387b58 100644 --- a/embassy-executor/src/raw/trace.rs +++ b/embassy-executor/src/raw/trace.rs @@ -61,29 +61,23 @@ pub(crate) fn executor_idle(executor: &SyncExecutor) { rtos_trace::trace::system_idle(); } -#[cfg(all(feature = "rtos-trace", feature = "integrated-timers"))] -const fn gcd(a: u64, b: u64) -> u64 { - if b == 0 { - a - } else { - gcd(b, a % b) - } -} - #[cfg(feature = "rtos-trace")] impl rtos_trace::RtosTraceOSCallbacks for crate::raw::SyncExecutor { fn task_list() { // We don't know what tasks exist, so we can't send them. } - #[cfg(feature = "integrated-timers")] fn time() -> u64 { + const fn gcd(a: u64, b: u64) -> u64 { + if b == 0 { + a + } else { + gcd(b, a % b) + } + } + const GCD_1M: u64 = gcd(embassy_time_driver::TICK_HZ, 1_000_000); embassy_time_driver::now() * (1_000_000 / GCD_1M) / (embassy_time_driver::TICK_HZ / GCD_1M) } - #[cfg(not(feature = "integrated-timers"))] - fn time() -> u64 { - 0 - } } #[cfg(feature = "rtos-trace")] diff --git a/embassy-stm32/Cargo.toml b/embassy-stm32/Cargo.toml index 82030f99f..47e9e8bb9 100644 --- a/embassy-stm32/Cargo.toml +++ b/embassy-stm32/Cargo.toml @@ -126,7 +126,6 @@ defmt = [ exti = [] low-power = [ "dep:embassy-executor", "embassy-executor?/arch-cortex-m", "time" ] low-power-debug-with-sleep = [] -integrated-timers = ["dep:embassy-executor", "_time-driver"] ## Automatically generate `memory.x` file using [`stm32-metapac`](https://docs.rs/stm32-metapac/) memory-x = ["stm32-metapac/memory-x"] diff --git a/embassy-time-queue-driver/Cargo.toml b/embassy-time-queue-driver/Cargo.toml index 599041a3f..7a10e29b4 100644 --- a/embassy-time-queue-driver/Cargo.toml +++ b/embassy-time-queue-driver/Cargo.toml @@ -23,35 +23,36 @@ links = "embassy-time-queue" [dependencies] critical-section = "1.2.0" heapless = "0.8" -embassy-executor = { version = "0.6.3", path = "../embassy-executor", optional = true } +embassy-executor = { version = "0.6.3", path = "../embassy-executor" } embassy-time-driver = { version = "0.1.0", path = "../embassy-time-driver" } [features] #! ### Generic Queue -## Use the executor-integrated `embassy-time` timer queue. The timer items are stored inside -## the task headers, so you do not need to set a capacity for the queue. -## To use this you must have a time driver provided. -## -## If this feature is not enabled, a generic queue is available with a configurable capacity. -integrated-timers = ["embassy-executor/integrated-timers"] - -#! The following features set how many timers are used for the generic queue. At most one +#! By default this crate uses a timer queue implementation that is faster but depends on `embassy-executor`. +#! It will panic if you try to await any timer when using another executor. +#! +#! Alternatively, you can choose to use a "generic" timer queue implementation that works on any executor. +#! To enable it, enable any of the features below. +#! +#! The features also set how many timers are used for the generic queue. At most one #! `generic-queue-*` feature can be enabled. If none is enabled, a default of 64 timers is used. #! #! When using embassy-time from libraries, you should *not* enable any `generic-queue-*` feature, to allow the #! end user to pick. ## Generic Queue with 8 timers -generic-queue-8 = [] +generic-queue-8 = ["_generic-queue"] ## Generic Queue with 16 timers -generic-queue-16 = [] +generic-queue-16 = ["_generic-queue"] ## Generic Queue with 32 timers -generic-queue-32 = [] +generic-queue-32 = ["_generic-queue"] ## Generic Queue with 64 timers -generic-queue-64 = [] +generic-queue-64 = ["_generic-queue"] ## Generic Queue with 128 timers -generic-queue-128 = [] +generic-queue-128 = ["_generic-queue"] + +_generic-queue = [] [package.metadata.embassy_docs] src_base = "https://github.com/embassy-rs/embassy/blob/embassy-time-queue-driver-v$VERSION/embassy-time-queue-driver/src/" diff --git a/embassy-time-queue-driver/src/lib.rs b/embassy-time-queue-driver/src/lib.rs index ed490a0ef..46dd646ca 100644 --- a/embassy-time-queue-driver/src/lib.rs +++ b/embassy-time-queue-driver/src/lib.rs @@ -51,16 +51,15 @@ use core::task::Waker; -#[cfg(not(feature = "integrated-timers"))] +#[cfg(feature = "_generic-queue")] pub mod queue_generic; -#[cfg(feature = "integrated-timers")] +#[cfg(not(feature = "_generic-queue"))] pub mod queue_integrated; -#[cfg(feature = "integrated-timers")] -pub use queue_integrated::Queue; - -#[cfg(not(feature = "integrated-timers"))] +#[cfg(feature = "_generic-queue")] pub use queue_generic::Queue; +#[cfg(not(feature = "_generic-queue"))] +pub use queue_integrated::Queue; extern "Rust" { fn _embassy_time_schedule_wake(at: u64, waker: &Waker); diff --git a/embassy-time/Cargo.toml b/embassy-time/Cargo.toml index 9959e2863..e3074119f 100644 --- a/embassy-time/Cargo.toml +++ b/embassy-time/Cargo.toml @@ -384,6 +384,7 @@ tick-hz-5_242_880_000 = ["embassy-time-driver/tick-hz-5_242_880_000"] [dependencies] embassy-time-driver = { version = "0.1.0", path = "../embassy-time-driver" } +embassy-time-queue-driver = { version = "0.1.0", path = "../embassy-time-queue-driver" } defmt = { version = "0.3", optional = true } log = { version = "0.4.14", optional = true } diff --git a/examples/boot/application/nrf/Cargo.toml b/examples/boot/application/nrf/Cargo.toml index 046571e05..45ad341fc 100644 --- a/examples/boot/application/nrf/Cargo.toml +++ b/examples/boot/application/nrf/Cargo.toml @@ -6,7 +6,7 @@ license = "MIT OR Apache-2.0" [dependencies] embassy-sync = { version = "0.6.1", path = "../../../../embassy-sync" } -embassy-executor = { version = "0.6.3", path = "../../../../embassy-executor", features = ["task-arena-size-16384", "arch-cortex-m", "executor-thread", "integrated-timers", "arch-cortex-m", "executor-thread"] } +embassy-executor = { version = "0.6.3", path = "../../../../embassy-executor", features = ["task-arena-size-16384", "arch-cortex-m", "executor-thread", "arch-cortex-m", "executor-thread"] } embassy-time = { version = "0.3.2", path = "../../../../embassy-time", features = [] } embassy-nrf = { version = "0.2.0", path = "../../../../embassy-nrf", features = ["time-driver-rtc1", "gpiote", ] } embassy-boot = { version = "0.3.0", path = "../../../../embassy-boot", features = [] } diff --git a/examples/boot/application/rp/Cargo.toml b/examples/boot/application/rp/Cargo.toml index f859ddbc6..ec99f2605 100644 --- a/examples/boot/application/rp/Cargo.toml +++ b/examples/boot/application/rp/Cargo.toml @@ -6,7 +6,7 @@ license = "MIT OR Apache-2.0" [dependencies] embassy-sync = { version = "0.6.1", path = "../../../../embassy-sync" } -embassy-executor = { version = "0.6.3", path = "../../../../embassy-executor", features = ["task-arena-size-16384", "arch-cortex-m", "executor-thread", "integrated-timers", "arch-cortex-m", "executor-thread"] } +embassy-executor = { version = "0.6.3", path = "../../../../embassy-executor", features = ["task-arena-size-16384", "arch-cortex-m", "executor-thread", "arch-cortex-m", "executor-thread"] } embassy-time = { version = "0.3.2", path = "../../../../embassy-time", features = [] } embassy-rp = { version = "0.2.0", path = "../../../../embassy-rp", features = ["time-driver", "rp2040"] } embassy-boot-rp = { version = "0.3.0", path = "../../../../embassy-boot-rp", features = [] } diff --git a/examples/boot/application/stm32f3/Cargo.toml b/examples/boot/application/stm32f3/Cargo.toml index d5b26d698..d2138db87 100644 --- a/examples/boot/application/stm32f3/Cargo.toml +++ b/examples/boot/application/stm32f3/Cargo.toml @@ -6,7 +6,7 @@ license = "MIT OR Apache-2.0" [dependencies] embassy-sync = { version = "0.6.1", path = "../../../../embassy-sync" } -embassy-executor = { version = "0.6.3", path = "../../../../embassy-executor", features = ["task-arena-size-8192", "arch-cortex-m", "executor-thread", "integrated-timers"] } +embassy-executor = { version = "0.6.3", path = "../../../../embassy-executor", features = ["task-arena-size-8192", "arch-cortex-m", "executor-thread"] } embassy-time = { version = "0.3.2", path = "../../../../embassy-time", features = [ "tick-hz-32_768"] } embassy-stm32 = { version = "0.1.0", path = "../../../../embassy-stm32", features = ["stm32f303re", "time-driver-any", "exti"] } embassy-boot-stm32 = { version = "0.2.0", path = "../../../../embassy-boot-stm32" } diff --git a/examples/boot/application/stm32f7/Cargo.toml b/examples/boot/application/stm32f7/Cargo.toml index d13692aa8..b86c66f5d 100644 --- a/examples/boot/application/stm32f7/Cargo.toml +++ b/examples/boot/application/stm32f7/Cargo.toml @@ -6,7 +6,7 @@ license = "MIT OR Apache-2.0" [dependencies] embassy-sync = { version = "0.6.1", path = "../../../../embassy-sync" } -embassy-executor = { version = "0.6.3", path = "../../../../embassy-executor", features = ["task-arena-size-8192", "arch-cortex-m", "executor-thread", "integrated-timers"] } +embassy-executor = { version = "0.6.3", path = "../../../../embassy-executor", features = ["task-arena-size-8192", "arch-cortex-m", "executor-thread"] } embassy-time = { version = "0.3.2", path = "../../../../embassy-time", features = [ "tick-hz-32_768"] } embassy-stm32 = { version = "0.1.0", path = "../../../../embassy-stm32", features = ["stm32f767zi", "time-driver-any", "exti"] } embassy-boot-stm32 = { version = "0.2.0", path = "../../../../embassy-boot-stm32", features = [] } diff --git a/examples/boot/application/stm32h7/Cargo.toml b/examples/boot/application/stm32h7/Cargo.toml index a2de827aa..e2e2fe711 100644 --- a/examples/boot/application/stm32h7/Cargo.toml +++ b/examples/boot/application/stm32h7/Cargo.toml @@ -6,7 +6,7 @@ license = "MIT OR Apache-2.0" [dependencies] embassy-sync = { version = "0.6.1", path = "../../../../embassy-sync" } -embassy-executor = { version = "0.6.3", path = "../../../../embassy-executor", features = ["task-arena-size-8192", "arch-cortex-m", "executor-thread", "integrated-timers"] } +embassy-executor = { version = "0.6.3", path = "../../../../embassy-executor", features = ["task-arena-size-8192", "arch-cortex-m", "executor-thread"] } embassy-time = { version = "0.3.2", path = "../../../../embassy-time", features = [ "tick-hz-32_768"] } embassy-stm32 = { version = "0.1.0", path = "../../../../embassy-stm32", features = ["stm32h743zi", "time-driver-any", "exti"] } embassy-boot-stm32 = { version = "0.2.0", path = "../../../../embassy-boot-stm32", features = [] } diff --git a/examples/boot/application/stm32l0/Cargo.toml b/examples/boot/application/stm32l0/Cargo.toml index ddfddf652..7e9c52ffa 100644 --- a/examples/boot/application/stm32l0/Cargo.toml +++ b/examples/boot/application/stm32l0/Cargo.toml @@ -6,7 +6,7 @@ license = "MIT OR Apache-2.0" [dependencies] embassy-sync = { version = "0.6.1", path = "../../../../embassy-sync" } -embassy-executor = { version = "0.6.3", path = "../../../../embassy-executor", features = ["task-arena-size-8192", "arch-cortex-m", "executor-thread", "integrated-timers"] } +embassy-executor = { version = "0.6.3", path = "../../../../embassy-executor", features = ["task-arena-size-8192", "arch-cortex-m", "executor-thread"] } embassy-time = { version = "0.3.2", path = "../../../../embassy-time", features = [ "tick-hz-32_768"] } embassy-stm32 = { version = "0.1.0", path = "../../../../embassy-stm32", features = ["stm32l072cz", "time-driver-any", "exti", "memory-x"] } embassy-boot-stm32 = { version = "0.2.0", path = "../../../../embassy-boot-stm32", features = [] } diff --git a/examples/boot/application/stm32l1/Cargo.toml b/examples/boot/application/stm32l1/Cargo.toml index 4780c20f4..42353a24c 100644 --- a/examples/boot/application/stm32l1/Cargo.toml +++ b/examples/boot/application/stm32l1/Cargo.toml @@ -6,7 +6,7 @@ license = "MIT OR Apache-2.0" [dependencies] embassy-sync = { version = "0.6.1", path = "../../../../embassy-sync" } -embassy-executor = { version = "0.6.3", path = "../../../../embassy-executor", features = ["task-arena-size-8192", "arch-cortex-m", "executor-thread", "integrated-timers"] } +embassy-executor = { version = "0.6.3", path = "../../../../embassy-executor", features = ["task-arena-size-8192", "arch-cortex-m", "executor-thread"] } embassy-time = { version = "0.3.2", path = "../../../../embassy-time", features = [ "tick-hz-32_768"] } embassy-stm32 = { version = "0.1.0", path = "../../../../embassy-stm32", features = ["stm32l151cb-a", "time-driver-any", "exti"] } embassy-boot-stm32 = { version = "0.2.0", path = "../../../../embassy-boot-stm32", features = [] } diff --git a/examples/boot/application/stm32l4/Cargo.toml b/examples/boot/application/stm32l4/Cargo.toml index 0a31d6b62..cf0b0242a 100644 --- a/examples/boot/application/stm32l4/Cargo.toml +++ b/examples/boot/application/stm32l4/Cargo.toml @@ -6,7 +6,7 @@ license = "MIT OR Apache-2.0" [dependencies] embassy-sync = { version = "0.6.1", path = "../../../../embassy-sync" } -embassy-executor = { version = "0.6.3", path = "../../../../embassy-executor", features = ["task-arena-size-8192", "arch-cortex-m", "executor-thread", "integrated-timers"] } +embassy-executor = { version = "0.6.3", path = "../../../../embassy-executor", features = ["task-arena-size-8192", "arch-cortex-m", "executor-thread"] } embassy-time = { version = "0.3.2", path = "../../../../embassy-time", features = [ "tick-hz-32_768"] } embassy-stm32 = { version = "0.1.0", path = "../../../../embassy-stm32", features = ["stm32l475vg", "time-driver-any", "exti"] } embassy-boot-stm32 = { version = "0.2.0", path = "../../../../embassy-boot-stm32", features = [] } diff --git a/examples/boot/application/stm32wb-dfu/Cargo.toml b/examples/boot/application/stm32wb-dfu/Cargo.toml index 67f6bde11..ea2879fb5 100644 --- a/examples/boot/application/stm32wb-dfu/Cargo.toml +++ b/examples/boot/application/stm32wb-dfu/Cargo.toml @@ -6,7 +6,7 @@ license = "MIT OR Apache-2.0" [dependencies] embassy-sync = { version = "0.6.1", path = "../../../../embassy-sync" } -embassy-executor = { version = "0.6.3", path = "../../../../embassy-executor", features = ["task-arena-size-8192", "arch-cortex-m", "executor-thread", "integrated-timers"] } +embassy-executor = { version = "0.6.3", path = "../../../../embassy-executor", features = ["task-arena-size-8192", "arch-cortex-m", "executor-thread"] } embassy-time = { version = "0.3.2", path = "../../../../embassy-time", features = [ "tick-hz-32_768"] } embassy-stm32 = { version = "0.1.0", path = "../../../../embassy-stm32", features = ["stm32wb55rg", "time-driver-any", "exti"] } embassy-boot-stm32 = { version = "0.2.0", path = "../../../../embassy-boot-stm32", features = [] } diff --git a/examples/boot/application/stm32wl/Cargo.toml b/examples/boot/application/stm32wl/Cargo.toml index da27d4601..6417b8430 100644 --- a/examples/boot/application/stm32wl/Cargo.toml +++ b/examples/boot/application/stm32wl/Cargo.toml @@ -6,7 +6,7 @@ license = "MIT OR Apache-2.0" [dependencies] embassy-sync = { version = "0.6.1", path = "../../../../embassy-sync" } -embassy-executor = { version = "0.6.3", path = "../../../../embassy-executor", features = ["task-arena-size-8192", "arch-cortex-m", "executor-thread", "integrated-timers"] } +embassy-executor = { version = "0.6.3", path = "../../../../embassy-executor", features = ["task-arena-size-8192", "arch-cortex-m", "executor-thread"] } embassy-time = { version = "0.3.2", path = "../../../../embassy-time", features = [ "tick-hz-32_768"] } embassy-stm32 = { version = "0.1.0", path = "../../../../embassy-stm32", features = ["stm32wl55jc-cm4", "time-driver-any", "exti"] } embassy-boot-stm32 = { version = "0.2.0", path = "../../../../embassy-boot-stm32", features = [] } diff --git a/examples/nrf-rtos-trace/Cargo.toml b/examples/nrf-rtos-trace/Cargo.toml index 449056409..6d13d668a 100644 --- a/examples/nrf-rtos-trace/Cargo.toml +++ b/examples/nrf-rtos-trace/Cargo.toml @@ -16,7 +16,7 @@ log = [ [dependencies] embassy-sync = { version = "0.6.1", path = "../../embassy-sync" } -embassy-executor = { version = "0.6.3", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "rtos-trace", "integrated-timers"] } +embassy-executor = { version = "0.6.3", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "rtos-trace"] } embassy-time = { version = "0.3.2", path = "../../embassy-time" } embassy-nrf = { version = "0.2.0", path = "../../embassy-nrf", features = ["nrf52840", "time-driver-rtc1", "gpiote", "unstable-pac"] } diff --git a/examples/nrf51/Cargo.toml b/examples/nrf51/Cargo.toml index 05e702773..8d995cfd8 100644 --- a/examples/nrf51/Cargo.toml +++ b/examples/nrf51/Cargo.toml @@ -5,7 +5,7 @@ version = "0.1.0" license = "MIT OR Apache-2.0" [dependencies] -embassy-executor = { version = "0.6.3", path = "../../embassy-executor", features = ["task-arena-size-4096", "arch-cortex-m", "executor-thread", "executor-interrupt", "defmt", "integrated-timers"] } +embassy-executor = { version = "0.6.3", path = "../../embassy-executor", features = ["task-arena-size-4096", "arch-cortex-m", "executor-thread", "executor-interrupt", "defmt"] } embassy-time = { version = "0.3.2", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime"] } embassy-nrf = { version = "0.2.0", path = "../../embassy-nrf", features = ["defmt", "nrf51", "gpiote", "time-driver-rtc1", "unstable-pac", "time", "rt"] } diff --git a/examples/nrf52810/Cargo.toml b/examples/nrf52810/Cargo.toml index b0b73417b..fa2a27aaa 100644 --- a/examples/nrf52810/Cargo.toml +++ b/examples/nrf52810/Cargo.toml @@ -7,7 +7,7 @@ license = "MIT OR Apache-2.0" [dependencies] embassy-futures = { version = "0.1.0", path = "../../embassy-futures" } embassy-sync = { version = "0.6.1", path = "../../embassy-sync", features = ["defmt"] } -embassy-executor = { version = "0.6.3", path = "../../embassy-executor", features = ["task-arena-size-8192", "arch-cortex-m", "executor-thread", "executor-interrupt", "defmt", "integrated-timers"] } +embassy-executor = { version = "0.6.3", path = "../../embassy-executor", features = ["task-arena-size-8192", "arch-cortex-m", "executor-thread", "executor-interrupt", "defmt"] } embassy-time = { version = "0.3.2", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime"] } embassy-nrf = { version = "0.2.0", path = "../../embassy-nrf", features = ["defmt", "nrf52810", "time-driver-rtc1", "gpiote", "unstable-pac", "time"] } diff --git a/examples/nrf52840-rtic/Cargo.toml b/examples/nrf52840-rtic/Cargo.toml index 326355dd6..6b15b24da 100644 --- a/examples/nrf52840-rtic/Cargo.toml +++ b/examples/nrf52840-rtic/Cargo.toml @@ -10,7 +10,7 @@ rtic = { version = "2", features = ["thumbv7-backend"] } embassy-futures = { version = "0.1.0", path = "../../embassy-futures" } embassy-sync = { version = "0.6.1", path = "../../embassy-sync", features = ["defmt"] } embassy-time = { version = "0.3.2", path = "../../embassy-time", features = [ "defmt", "defmt-timestamp-uptime"] } -embassy-time-queue-driver = { version = "0.1.0", path = "../../embassy-time-queue-driver" } +embassy-time-queue-driver = { version = "0.1.0", path = "../../embassy-time-queue-driver", features = ["generic-queue-8"] } embassy-nrf = { version = "0.2.0", path = "../../embassy-nrf", features = [ "defmt", "nrf52840", "time-driver-rtc1", "gpiote", "unstable-pac", "time"] } defmt = "0.3" diff --git a/examples/nrf52840/Cargo.toml b/examples/nrf52840/Cargo.toml index 701911a30..fa29d52b9 100644 --- a/examples/nrf52840/Cargo.toml +++ b/examples/nrf52840/Cargo.toml @@ -7,7 +7,7 @@ license = "MIT OR Apache-2.0" [dependencies] embassy-futures = { version = "0.1.0", path = "../../embassy-futures" } embassy-sync = { version = "0.6.1", path = "../../embassy-sync", features = ["defmt"] } -embassy-executor = { version = "0.6.3", path = "../../embassy-executor", features = ["task-arena-size-32768", "arch-cortex-m", "executor-thread", "executor-interrupt", "defmt", "integrated-timers"] } +embassy-executor = { version = "0.6.3", path = "../../embassy-executor", features = ["task-arena-size-32768", "arch-cortex-m", "executor-thread", "executor-interrupt", "defmt"] } embassy-time = { version = "0.3.2", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime"] } embassy-nrf = { version = "0.2.0", path = "../../embassy-nrf", features = ["defmt", "nrf52840", "time-driver-rtc1", "gpiote", "unstable-pac", "time"] } embassy-net = { version = "0.5.0", path = "../../embassy-net", features = ["defmt", "tcp", "dhcpv4", "medium-ethernet"] } diff --git a/examples/nrf5340/Cargo.toml b/examples/nrf5340/Cargo.toml index 13442405d..1792b277c 100644 --- a/examples/nrf5340/Cargo.toml +++ b/examples/nrf5340/Cargo.toml @@ -7,7 +7,7 @@ license = "MIT OR Apache-2.0" [dependencies] embassy-futures = { version = "0.1.0", path = "../../embassy-futures" } embassy-sync = { version = "0.6.1", path = "../../embassy-sync", features = ["defmt"] } -embassy-executor = { version = "0.6.3", path = "../../embassy-executor", features = ["task-arena-size-32768", "arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] } +embassy-executor = { version = "0.6.3", path = "../../embassy-executor", features = ["task-arena-size-32768", "arch-cortex-m", "executor-thread", "defmt"] } embassy-time = { version = "0.3.2", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime"] } embassy-nrf = { version = "0.2.0", path = "../../embassy-nrf", features = ["defmt", "nrf5340-app-s", "time-driver-rtc1", "gpiote", "unstable-pac"] } embassy-net = { version = "0.5.0", path = "../../embassy-net", features = ["defmt", "tcp", "dhcpv4", "medium-ethernet"] } diff --git a/examples/nrf54l15/Cargo.toml b/examples/nrf54l15/Cargo.toml index 6d11269f7..7288ef6af 100644 --- a/examples/nrf54l15/Cargo.toml +++ b/examples/nrf54l15/Cargo.toml @@ -5,7 +5,7 @@ version = "0.1.0" license = "MIT OR Apache-2.0" [dependencies] -embassy-executor = { version = "0.6.3", path = "../../embassy-executor", features = ["task-arena-size-32768", "arch-cortex-m", "executor-thread", "executor-interrupt", "defmt", "integrated-timers"] } +embassy-executor = { version = "0.6.3", path = "../../embassy-executor", features = ["task-arena-size-32768", "arch-cortex-m", "executor-thread", "executor-interrupt", "defmt"] } embassy-time = { version = "0.3.2", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime"] } embassy-nrf = { version = "0.2.0", path = "../../embassy-nrf", features = ["defmt", "nrf54l15-app-s", "time-driver-rtc1", "gpiote", "unstable-pac", "time"] } diff --git a/examples/nrf9151/ns/Cargo.toml b/examples/nrf9151/ns/Cargo.toml index 96bf6700d..0353cf598 100644 --- a/examples/nrf9151/ns/Cargo.toml +++ b/examples/nrf9151/ns/Cargo.toml @@ -5,7 +5,7 @@ version = "0.1.0" license = "MIT OR Apache-2.0" [dependencies] -embassy-executor = { version = "0.6.3", path = "../../../embassy-executor", features = ["task-arena-size-32768", "arch-cortex-m", "executor-thread", "executor-interrupt", "defmt", "integrated-timers"] } +embassy-executor = { version = "0.6.3", path = "../../../embassy-executor", features = ["task-arena-size-32768", "arch-cortex-m", "executor-thread", "executor-interrupt", "defmt"] } embassy-time = { version = "0.3.2", path = "../../../embassy-time", features = ["defmt", "defmt-timestamp-uptime"] } embassy-nrf = { version = "0.2.0", path = "../../../embassy-nrf", features = ["defmt", "nrf9120-ns", "time-driver-rtc1", "gpiote", "unstable-pac", "time"] } diff --git a/examples/nrf9151/s/Cargo.toml b/examples/nrf9151/s/Cargo.toml index f7adf259d..5d2302574 100644 --- a/examples/nrf9151/s/Cargo.toml +++ b/examples/nrf9151/s/Cargo.toml @@ -5,7 +5,7 @@ version = "0.1.0" license = "MIT OR Apache-2.0" [dependencies] -embassy-executor = { version = "0.6.3", path = "../../../embassy-executor", features = ["task-arena-size-32768", "arch-cortex-m", "executor-thread", "executor-interrupt", "defmt", "integrated-timers"] } +embassy-executor = { version = "0.6.3", path = "../../../embassy-executor", features = ["task-arena-size-32768", "arch-cortex-m", "executor-thread", "executor-interrupt", "defmt"] } embassy-time = { version = "0.3.2", path = "../../../embassy-time", features = ["defmt", "defmt-timestamp-uptime"] } embassy-nrf = { version = "0.2.0", path = "../../../embassy-nrf", features = ["defmt", "nrf9120-s", "time-driver-rtc1", "gpiote", "unstable-pac", "time"] } diff --git a/examples/nrf9160/Cargo.toml b/examples/nrf9160/Cargo.toml index 3b404c730..b52cd4af0 100644 --- a/examples/nrf9160/Cargo.toml +++ b/examples/nrf9160/Cargo.toml @@ -5,7 +5,7 @@ version = "0.1.0" license = "MIT OR Apache-2.0" [dependencies] -embassy-executor = { version = "0.6.3", path = "../../embassy-executor", features = ["task-arena-size-32768", "arch-cortex-m", "executor-thread", "executor-interrupt", "defmt", "integrated-timers"] } +embassy-executor = { version = "0.6.3", path = "../../embassy-executor", features = ["task-arena-size-32768", "arch-cortex-m", "executor-thread", "executor-interrupt", "defmt"] } embassy-time = { version = "0.3.2", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime"] } embassy-nrf = { version = "0.2.0", path = "../../embassy-nrf", features = ["defmt", "nrf9160-s", "time-driver-rtc1", "gpiote", "unstable-pac", "time"] } embassy-net-nrf91 = { version = "0.1.0", path = "../../embassy-net-nrf91", features = ["defmt"] } diff --git a/examples/rp/Cargo.toml b/examples/rp/Cargo.toml index 2dce1676a..ce812b2e0 100644 --- a/examples/rp/Cargo.toml +++ b/examples/rp/Cargo.toml @@ -8,7 +8,7 @@ license = "MIT OR Apache-2.0" [dependencies] embassy-embedded-hal = { version = "0.2.0", path = "../../embassy-embedded-hal", features = ["defmt"] } embassy-sync = { version = "0.6.1", path = "../../embassy-sync", features = ["defmt"] } -embassy-executor = { version = "0.6.3", path = "../../embassy-executor", features = ["task-arena-size-98304", "arch-cortex-m", "executor-thread", "executor-interrupt", "defmt", "integrated-timers"] } +embassy-executor = { version = "0.6.3", path = "../../embassy-executor", features = ["task-arena-size-98304", "arch-cortex-m", "executor-thread", "executor-interrupt", "defmt"] } embassy-time = { version = "0.3.2", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime"] } embassy-rp = { version = "0.2.0", path = "../../embassy-rp", features = ["defmt", "unstable-pac", "time-driver", "critical-section-impl", "rp2040"] } embassy-usb = { version = "0.3.0", path = "../../embassy-usb", features = ["defmt"] } diff --git a/examples/rp23/Cargo.toml b/examples/rp23/Cargo.toml index 2fcad247d..72eef222d 100644 --- a/examples/rp23/Cargo.toml +++ b/examples/rp23/Cargo.toml @@ -8,7 +8,7 @@ license = "MIT OR Apache-2.0" [dependencies] embassy-embedded-hal = { version = "0.2.0", path = "../../embassy-embedded-hal", features = ["defmt"] } embassy-sync = { version = "0.6.1", path = "../../embassy-sync", features = ["defmt"] } -embassy-executor = { version = "0.6.3", path = "../../embassy-executor", features = ["task-arena-size-98304", "arch-cortex-m", "executor-thread", "executor-interrupt", "defmt", "integrated-timers"] } +embassy-executor = { version = "0.6.3", path = "../../embassy-executor", features = ["task-arena-size-98304", "arch-cortex-m", "executor-thread", "executor-interrupt", "defmt"] } embassy-time = { version = "0.3.2", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime"] } embassy-rp = { version = "0.2.0", path = "../../embassy-rp", features = ["defmt", "unstable-pac", "time-driver", "critical-section-impl", "rp235xa", "binary-info"] } embassy-usb = { version = "0.3.0", path = "../../embassy-usb", features = ["defmt"] } diff --git a/examples/std/Cargo.toml b/examples/std/Cargo.toml index 77948515a..e43fd77c8 100644 --- a/examples/std/Cargo.toml +++ b/examples/std/Cargo.toml @@ -6,7 +6,7 @@ license = "MIT OR Apache-2.0" [dependencies] embassy-sync = { version = "0.6.1", path = "../../embassy-sync", features = ["log"] } -embassy-executor = { version = "0.6.3", path = "../../embassy-executor", features = ["task-arena-size-32768", "arch-std", "executor-thread", "log", "integrated-timers"] } +embassy-executor = { version = "0.6.3", path = "../../embassy-executor", features = ["task-arena-size-32768", "arch-std", "executor-thread", "log"] } embassy-time = { version = "0.3.2", path = "../../embassy-time", features = ["log", "std", ] } embassy-net = { version = "0.5.0", path = "../../embassy-net", features=[ "std", "log", "medium-ethernet", "medium-ip", "tcp", "udp", "dns", "dhcpv4", "proto-ipv6"] } embassy-net-tuntap = { version = "0.1.0", path = "../../embassy-net-tuntap" } diff --git a/examples/stm32c0/Cargo.toml b/examples/stm32c0/Cargo.toml index 895e668da..5ac3018e1 100644 --- a/examples/stm32c0/Cargo.toml +++ b/examples/stm32c0/Cargo.toml @@ -8,7 +8,7 @@ license = "MIT OR Apache-2.0" # Change stm32c031c6 to your chip name, if necessary. embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = [ "defmt", "time-driver-any", "stm32c031c6", "memory-x", "unstable-pac", "exti"] } embassy-sync = { version = "0.6.1", path = "../../embassy-sync", features = ["defmt"] } -embassy-executor = { version = "0.6.3", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] } +embassy-executor = { version = "0.6.3", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "defmt"] } embassy-time = { version = "0.3.2", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } defmt = "0.3" diff --git a/examples/stm32f0/Cargo.toml b/examples/stm32f0/Cargo.toml index 056f8470d..af3ef7abb 100644 --- a/examples/stm32f0/Cargo.toml +++ b/examples/stm32f0/Cargo.toml @@ -13,7 +13,7 @@ defmt = "0.3" defmt-rtt = "0.4" panic-probe = { version = "0.3", features = ["print-defmt"] } embassy-sync = { version = "0.6.1", path = "../../embassy-sync", features = ["defmt"] } -embassy-executor = { version = "0.6.3", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "executor-interrupt", "defmt", "integrated-timers"] } +embassy-executor = { version = "0.6.3", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "executor-interrupt", "defmt"] } embassy-time = { version = "0.3.2", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } static_cell = "2" portable-atomic = { version = "1.5", features = ["unsafe-assume-single-core"] } diff --git a/examples/stm32f1/Cargo.toml b/examples/stm32f1/Cargo.toml index c081333d7..538e95dfb 100644 --- a/examples/stm32f1/Cargo.toml +++ b/examples/stm32f1/Cargo.toml @@ -8,7 +8,7 @@ license = "MIT OR Apache-2.0" # Change stm32f103c8 to your chip name, if necessary. embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = [ "defmt", "stm32f103c8", "unstable-pac", "memory-x", "time-driver-any" ] } embassy-sync = { version = "0.6.1", path = "../../embassy-sync", features = ["defmt"] } -embassy-executor = { version = "0.6.3", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] } +embassy-executor = { version = "0.6.3", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "defmt"] } embassy-time = { version = "0.3.2", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } embassy-usb = { version = "0.3.0", path = "../../embassy-usb", features = ["defmt"] } embassy-futures = { version = "0.1.0", path = "../../embassy-futures" } diff --git a/examples/stm32f2/Cargo.toml b/examples/stm32f2/Cargo.toml index f7993497c..48d524b90 100644 --- a/examples/stm32f2/Cargo.toml +++ b/examples/stm32f2/Cargo.toml @@ -8,7 +8,7 @@ license = "MIT OR Apache-2.0" # Change stm32f207zg to your chip name, if necessary. embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = [ "defmt", "stm32f207zg", "unstable-pac", "memory-x", "time-driver-any", "exti"] } embassy-sync = { version = "0.6.1", path = "../../embassy-sync", features = ["defmt"] } -embassy-executor = { version = "0.6.3", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] } +embassy-executor = { version = "0.6.3", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "defmt"] } embassy-time = { version = "0.3.2", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } defmt = "0.3" diff --git a/examples/stm32f3/Cargo.toml b/examples/stm32f3/Cargo.toml index a7b8935a9..66fb34223 100644 --- a/examples/stm32f3/Cargo.toml +++ b/examples/stm32f3/Cargo.toml @@ -8,7 +8,7 @@ license = "MIT OR Apache-2.0" # Change stm32f303ze to your chip name, if necessary. embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = [ "defmt", "stm32f303ze", "unstable-pac", "memory-x", "time-driver-tim2", "exti"] } embassy-sync = { version = "0.6.1", path = "../../embassy-sync", features = ["defmt"] } -embassy-executor = { version = "0.6.3", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "executor-interrupt", "defmt", "integrated-timers"] } +embassy-executor = { version = "0.6.3", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "executor-interrupt", "defmt"] } embassy-time = { version = "0.3.2", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } embassy-usb = { version = "0.3.0", path = "../../embassy-usb", features = ["defmt"] } embassy-futures = { version = "0.1.0", path = "../../embassy-futures" } diff --git a/examples/stm32f334/Cargo.toml b/examples/stm32f334/Cargo.toml index ed8348772..c6b311fa5 100644 --- a/examples/stm32f334/Cargo.toml +++ b/examples/stm32f334/Cargo.toml @@ -6,7 +6,7 @@ license = "MIT OR Apache-2.0" [dependencies] embassy-sync = { version = "0.6.1", path = "../../embassy-sync", features = ["defmt"] } -embassy-executor = { version = "0.6.3", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "executor-interrupt", "defmt", "integrated-timers"] } +embassy-executor = { version = "0.6.3", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "executor-interrupt", "defmt"] } embassy-time = { version = "0.3.2", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = [ "defmt", "stm32f334r8", "unstable-pac", "memory-x", "time-driver-any", "exti"] } embassy-usb = { version = "0.3.0", path = "../../embassy-usb", features = ["defmt"] } diff --git a/examples/stm32f4/Cargo.toml b/examples/stm32f4/Cargo.toml index 2a0b7c507..4f0629fc6 100644 --- a/examples/stm32f4/Cargo.toml +++ b/examples/stm32f4/Cargo.toml @@ -8,7 +8,7 @@ license = "MIT OR Apache-2.0" # Change stm32f429zi to your chip name, if necessary. embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["defmt", "stm32f429zi", "unstable-pac", "memory-x", "time-driver-tim4", "exti", "chrono"] } embassy-sync = { version = "0.6.1", path = "../../embassy-sync", features = ["defmt"] } -embassy-executor = { version = "0.6.3", path = "../../embassy-executor", features = ["task-arena-size-32768", "arch-cortex-m", "executor-thread", "executor-interrupt", "defmt", "integrated-timers"] } +embassy-executor = { version = "0.6.3", path = "../../embassy-executor", features = ["task-arena-size-32768", "arch-cortex-m", "executor-thread", "executor-interrupt", "defmt"] } embassy-time = { version = "0.3.2", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } embassy-usb = { version = "0.3.0", path = "../../embassy-usb", features = ["defmt" ] } embassy-net = { version = "0.5.0", path = "../../embassy-net", features = ["defmt", "tcp", "dhcpv4", "medium-ethernet", ] } diff --git a/examples/stm32f469/Cargo.toml b/examples/stm32f469/Cargo.toml index 382f7e485..a80409801 100644 --- a/examples/stm32f469/Cargo.toml +++ b/examples/stm32f469/Cargo.toml @@ -7,7 +7,7 @@ license = "MIT OR Apache-2.0" [dependencies] # Specific examples only for stm32f469 embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["defmt", "stm32f469ni", "unstable-pac", "memory-x", "time-driver-any", "exti", "chrono"] } -embassy-executor = { version = "0.6.3", path = "../../embassy-executor", features = ["task-arena-size-32768", "arch-cortex-m", "executor-thread", "executor-interrupt", "defmt", "integrated-timers"] } +embassy-executor = { version = "0.6.3", path = "../../embassy-executor", features = ["task-arena-size-32768", "arch-cortex-m", "executor-thread", "executor-interrupt", "defmt"] } embassy-time = { version = "0.3.2", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } defmt = "0.3" diff --git a/examples/stm32f7/Cargo.toml b/examples/stm32f7/Cargo.toml index 480694dca..520b8bc42 100644 --- a/examples/stm32f7/Cargo.toml +++ b/examples/stm32f7/Cargo.toml @@ -8,7 +8,7 @@ license = "MIT OR Apache-2.0" # Change stm32f777zi to your chip name, if necessary. embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["defmt", "stm32f777zi", "memory-x", "unstable-pac", "time-driver-any", "exti"] } embassy-sync = { version = "0.6.1", path = "../../embassy-sync", features = ["defmt"] } -embassy-executor = { version = "0.6.3", path = "../../embassy-executor", features = ["task-arena-size-32768", "arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] } +embassy-executor = { version = "0.6.3", path = "../../embassy-executor", features = ["task-arena-size-32768", "arch-cortex-m", "executor-thread", "defmt"] } embassy-time = { version = "0.3.2", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } embassy-net = { version = "0.5.0", path = "../../embassy-net", features = ["defmt", "tcp", "dhcpv4", "medium-ethernet"] } embedded-io-async = { version = "0.6.1" } diff --git a/examples/stm32g0/Cargo.toml b/examples/stm32g0/Cargo.toml index 66cac1885..3d11610ce 100644 --- a/examples/stm32g0/Cargo.toml +++ b/examples/stm32g0/Cargo.toml @@ -8,7 +8,7 @@ license = "MIT OR Apache-2.0" # Change stm32g0b1re to your chip name, if necessary. embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = [ "defmt", "time-driver-any", "stm32g0b1re", "memory-x", "unstable-pac", "exti"] } embassy-sync = { version = "0.6.1", path = "../../embassy-sync", features = ["defmt"] } -embassy-executor = { version = "0.6.3", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] } +embassy-executor = { version = "0.6.3", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "defmt"] } embassy-time = { version = "0.3.2", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } embassy-usb = { version = "0.3.0", path = "../../embassy-usb", default-features = false, features = ["defmt"] } embassy-futures = { version = "0.1.0", path = "../../embassy-futures" } diff --git a/examples/stm32g4/Cargo.toml b/examples/stm32g4/Cargo.toml index 36bef4787..87fa2c53a 100644 --- a/examples/stm32g4/Cargo.toml +++ b/examples/stm32g4/Cargo.toml @@ -8,7 +8,7 @@ license = "MIT OR Apache-2.0" # Change stm32g491re to your chip name, if necessary. embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = [ "defmt", "time-driver-any", "stm32g491re", "memory-x", "unstable-pac", "exti"] } embassy-sync = { version = "0.6.1", path = "../../embassy-sync", features = ["defmt"] } -embassy-executor = { version = "0.6.3", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] } +embassy-executor = { version = "0.6.3", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "defmt"] } embassy-time = { version = "0.3.2", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } embassy-usb = { version = "0.3.0", path = "../../embassy-usb", features = ["defmt"] } embassy-futures = { version = "0.1.0", path = "../../embassy-futures" } diff --git a/examples/stm32h5/Cargo.toml b/examples/stm32h5/Cargo.toml index 1a5791c83..516d491e5 100644 --- a/examples/stm32h5/Cargo.toml +++ b/examples/stm32h5/Cargo.toml @@ -8,7 +8,7 @@ license = "MIT OR Apache-2.0" # Change stm32h563zi to your chip name, if necessary. embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["defmt", "stm32h563zi", "memory-x", "time-driver-any", "exti", "unstable-pac", "low-power"] } embassy-sync = { version = "0.6.1", path = "../../embassy-sync", features = ["defmt"] } -embassy-executor = { version = "0.6.3", path = "../../embassy-executor", features = ["task-arena-size-32768", "arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] } +embassy-executor = { version = "0.6.3", path = "../../embassy-executor", features = ["task-arena-size-32768", "arch-cortex-m", "executor-thread", "defmt"] } embassy-time = { version = "0.3.2", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } embassy-net = { version = "0.5.0", path = "../../embassy-net", features = ["defmt", "tcp", "dhcpv4", "medium-ethernet", "proto-ipv6"] } embassy-usb = { version = "0.3.0", path = "../../embassy-usb", features = ["defmt"] } diff --git a/examples/stm32h7/Cargo.toml b/examples/stm32h7/Cargo.toml index b90a6a455..68a0c3d88 100644 --- a/examples/stm32h7/Cargo.toml +++ b/examples/stm32h7/Cargo.toml @@ -9,7 +9,7 @@ license = "MIT OR Apache-2.0" 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.6.1", path = "../../embassy-sync", features = ["defmt"] } embassy-embedded-hal = { version = "0.2.0", path = "../../embassy-embedded-hal" } -embassy-executor = { version = "0.6.3", path = "../../embassy-executor", features = ["task-arena-size-32768", "arch-cortex-m", "executor-thread", "executor-interrupt", "defmt", "integrated-timers"] } +embassy-executor = { version = "0.6.3", path = "../../embassy-executor", features = ["task-arena-size-32768", "arch-cortex-m", "executor-thread", "executor-interrupt", "defmt"] } embassy-time = { version = "0.3.2", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } embassy-net = { version = "0.5.0", path = "../../embassy-net", features = ["defmt", "tcp", "dhcpv4", "medium-ethernet", "proto-ipv6", "dns"] } embassy-usb = { version = "0.3.0", path = "../../embassy-usb", features = ["defmt"] } diff --git a/examples/stm32h723/Cargo.toml b/examples/stm32h723/Cargo.toml index 6e3f0dd03..82f3cb9c2 100644 --- a/examples/stm32h723/Cargo.toml +++ b/examples/stm32h723/Cargo.toml @@ -8,7 +8,7 @@ license = "MIT OR Apache-2.0" # Change stm32h723zg to your chip name, if necessary. embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["defmt", "stm32h723zg", "time-driver-tim2", "exti", "memory-x", "unstable-pac", "chrono"] } embassy-sync = { version = "0.6.1", path = "../../embassy-sync", features = ["defmt"] } -embassy-executor = { version = "0.6.2", path = "../../embassy-executor", features = ["task-arena-size-32768", "arch-cortex-m", "executor-thread", "executor-interrupt", "defmt", "integrated-timers"] } +embassy-executor = { version = "0.6.2", path = "../../embassy-executor", features = ["task-arena-size-32768", "arch-cortex-m", "executor-thread", "executor-interrupt", "defmt"] } embassy-time = { version = "0.3.2", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } embassy-futures = { version = "0.1.0", path = "../../embassy-futures" } diff --git a/examples/stm32h735/Cargo.toml b/examples/stm32h735/Cargo.toml index a9c66ec48..a517b9727 100644 --- a/examples/stm32h735/Cargo.toml +++ b/examples/stm32h735/Cargo.toml @@ -8,7 +8,7 @@ license = "MIT OR Apache-2.0" embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["defmt", "stm32h735ig", "time-driver-tim2", "exti", "memory-x", "unstable-pac", "chrono"] } embassy-sync = { version = "0.6.1", path = "../../embassy-sync", features = ["defmt"] } embassy-embedded-hal = { version = "0.2.0", path = "../../embassy-embedded-hal" } -embassy-executor = { version = "0.6.3", path = "../../embassy-executor", features = ["task-arena-size-32768", "arch-cortex-m", "executor-thread", "executor-interrupt", "defmt", "integrated-timers"] } +embassy-executor = { version = "0.6.3", path = "../../embassy-executor", features = ["task-arena-size-32768", "arch-cortex-m", "executor-thread", "executor-interrupt", "defmt"] } embassy-time = { version = "0.3.2", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } embassy-futures = { version = "0.1.0", path = "../../embassy-futures" } diff --git a/examples/stm32h755cm4/Cargo.toml b/examples/stm32h755cm4/Cargo.toml index 455dee98b..1d4d3eb85 100644 --- a/examples/stm32h755cm4/Cargo.toml +++ b/examples/stm32h755cm4/Cargo.toml @@ -9,7 +9,7 @@ license = "MIT OR Apache-2.0" embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["defmt", "stm32h755zi-cm4", "time-driver-tim2", "exti", "memory-x", "unstable-pac", "chrono"] } embassy-sync = { version = "0.6.1", path = "../../embassy-sync", features = ["defmt"] } embassy-embedded-hal = { version = "0.2.0", path = "../../embassy-embedded-hal" } -embassy-executor = { version = "0.6.3", path = "../../embassy-executor", features = ["task-arena-size-32768", "arch-cortex-m", "executor-thread", "executor-interrupt", "defmt", "integrated-timers"] } +embassy-executor = { version = "0.6.3", path = "../../embassy-executor", features = ["task-arena-size-32768", "arch-cortex-m", "executor-thread", "executor-interrupt", "defmt"] } embassy-time = { version = "0.3.1", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } embassy-net = { version = "0.5.0", path = "../../embassy-net", features = ["defmt", "tcp", "dhcpv4", "medium-ethernet", "proto-ipv6", "dns"] } embassy-usb = { version = "0.3.0", path = "../../embassy-usb", features = ["defmt"] } diff --git a/examples/stm32h755cm7/Cargo.toml b/examples/stm32h755cm7/Cargo.toml index 4d6167ab2..76c88c806 100644 --- a/examples/stm32h755cm7/Cargo.toml +++ b/examples/stm32h755cm7/Cargo.toml @@ -9,7 +9,7 @@ license = "MIT OR Apache-2.0" embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["defmt", "stm32h755zi-cm7", "time-driver-tim3", "exti", "memory-x", "unstable-pac", "chrono"] } embassy-sync = { version = "0.6.1", path = "../../embassy-sync", features = ["defmt"] } embassy-embedded-hal = { version = "0.2.0", path = "../../embassy-embedded-hal" } -embassy-executor = { version = "0.6.3", path = "../../embassy-executor", features = ["task-arena-size-32768", "arch-cortex-m", "executor-thread", "executor-interrupt", "defmt", "integrated-timers"] } +embassy-executor = { version = "0.6.3", path = "../../embassy-executor", features = ["task-arena-size-32768", "arch-cortex-m", "executor-thread", "executor-interrupt", "defmt"] } embassy-time = { version = "0.3.1", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } embassy-net = { version = "0.5.0", path = "../../embassy-net", features = ["defmt", "tcp", "dhcpv4", "medium-ethernet", "proto-ipv6", "dns"] } embassy-usb = { version = "0.3.0", path = "../../embassy-usb", features = ["defmt"] } diff --git a/examples/stm32h7b0/Cargo.toml b/examples/stm32h7b0/Cargo.toml index 41f0fb5ca..aba398fa5 100644 --- a/examples/stm32h7b0/Cargo.toml +++ b/examples/stm32h7b0/Cargo.toml @@ -8,7 +8,7 @@ license = "MIT OR Apache-2.0" embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["defmt", "stm32h7b0vb", "time-driver-tim2", "exti", "memory-x", "unstable-pac", "chrono"] } embassy-sync = { version = "0.6.1", path = "../../embassy-sync", features = ["defmt"] } embassy-embedded-hal = { version = "0.2.0", path = "../../embassy-embedded-hal" } -embassy-executor = { version = "0.6.3", path = "../../embassy-executor", features = ["task-arena-size-32768", "arch-cortex-m", "executor-thread", "executor-interrupt", "defmt", "integrated-timers"] } +embassy-executor = { version = "0.6.3", path = "../../embassy-executor", features = ["task-arena-size-32768", "arch-cortex-m", "executor-thread", "executor-interrupt", "defmt"] } embassy-time = { version = "0.3.2", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } embassy-net = { version = "0.5.0", path = "../../embassy-net", features = ["defmt", "tcp", "dhcpv4", "medium-ethernet", "proto-ipv6", "dns"] } embassy-usb = { version = "0.3.0", path = "../../embassy-usb", features = ["defmt"] } diff --git a/examples/stm32h7rs/Cargo.toml b/examples/stm32h7rs/Cargo.toml index 24065dbce..1d957e2cc 100644 --- a/examples/stm32h7rs/Cargo.toml +++ b/examples/stm32h7rs/Cargo.toml @@ -8,7 +8,7 @@ license = "MIT OR Apache-2.0" # Change stm32h743bi to your chip name, if necessary. embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["defmt", "stm32h7s3l8", "time-driver-tim2", "exti", "memory-x", "unstable-pac", "chrono"] } embassy-sync = { version = "0.6.1", path = "../../embassy-sync", features = ["defmt"] } -embassy-executor = { version = "0.6.3", path = "../../embassy-executor", features = ["task-arena-size-32768", "arch-cortex-m", "executor-thread", "executor-interrupt", "defmt", "integrated-timers"] } +embassy-executor = { version = "0.6.3", path = "../../embassy-executor", features = ["task-arena-size-32768", "arch-cortex-m", "executor-thread", "executor-interrupt", "defmt"] } embassy-time = { version = "0.3.2", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } embassy-net = { version = "0.5.0", path = "../../embassy-net", features = ["defmt", "tcp", "dhcpv4", "medium-ethernet", "proto-ipv6", "dns"] } embassy-usb = { version = "0.3.0", path = "../../embassy-usb", features = ["defmt"] } diff --git a/examples/stm32l0/Cargo.toml b/examples/stm32l0/Cargo.toml index 9d234804a..5cc312a50 100644 --- a/examples/stm32l0/Cargo.toml +++ b/examples/stm32l0/Cargo.toml @@ -8,7 +8,7 @@ license = "MIT OR Apache-2.0" # Change stm32l072cz to your chip name, if necessary. embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["defmt", "stm32l073rz", "unstable-pac", "time-driver-any", "exti", "memory-x"] } embassy-sync = { version = "0.6.1", path = "../../embassy-sync", features = ["defmt"] } -embassy-executor = { version = "0.6.3", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] } +embassy-executor = { version = "0.6.3", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "defmt"] } embassy-time = { version = "0.3.2", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } defmt = "0.3" diff --git a/examples/stm32l1/Cargo.toml b/examples/stm32l1/Cargo.toml index 33e4f96e5..31b6785fa 100644 --- a/examples/stm32l1/Cargo.toml +++ b/examples/stm32l1/Cargo.toml @@ -6,7 +6,7 @@ license = "MIT OR Apache-2.0" [dependencies] embassy-sync = { version = "0.6.1", path = "../../embassy-sync", features = ["defmt"] } -embassy-executor = { version = "0.6.3", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] } +embassy-executor = { version = "0.6.3", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "defmt"] } embassy-time = { version = "0.3.2", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = [ "defmt", "stm32l151cb-a", "time-driver-any", "memory-x"] } embassy-usb = { version = "0.3.0", path = "../../embassy-usb", features = ["defmt"] } diff --git a/examples/stm32l4/Cargo.toml b/examples/stm32l4/Cargo.toml index 512bb8064..3fde18ecd 100644 --- a/examples/stm32l4/Cargo.toml +++ b/examples/stm32l4/Cargo.toml @@ -8,7 +8,7 @@ license = "MIT OR Apache-2.0" # Change stm32l4s5vi to your chip name, if necessary. embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = [ "defmt", "unstable-pac", "stm32l4r5zi", "memory-x", "time-driver-any", "exti", "chrono"] } embassy-sync = { version = "0.6.1", path = "../../embassy-sync", features = ["defmt"] } -embassy-executor = { version = "0.6.3", path = "../../embassy-executor", features = ["task-arena-size-32768", "arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] } +embassy-executor = { version = "0.6.3", path = "../../embassy-executor", features = ["task-arena-size-32768", "arch-cortex-m", "executor-thread", "defmt"] } embassy-time = { version = "0.3.2", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768", ] } embassy-embedded-hal = { version = "0.2.0", path = "../../embassy-embedded-hal" } embassy-usb = { version = "0.3.0", path = "../../embassy-usb", features = ["defmt"] } diff --git a/examples/stm32l5/Cargo.toml b/examples/stm32l5/Cargo.toml index e09311f9d..2b8a2c064 100644 --- a/examples/stm32l5/Cargo.toml +++ b/examples/stm32l5/Cargo.toml @@ -8,7 +8,7 @@ license = "MIT OR Apache-2.0" # Change stm32l552ze to your chip name, if necessary. embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = [ "defmt", "unstable-pac", "stm32l552ze", "time-driver-any", "exti", "memory-x", "low-power"] } embassy-sync = { version = "0.6.1", path = "../../embassy-sync", features = ["defmt"] } -embassy-executor = { version = "0.6.3", path = "../../embassy-executor", features = ["task-arena-size-32768", "arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] } +embassy-executor = { version = "0.6.3", path = "../../embassy-executor", features = ["task-arena-size-32768", "arch-cortex-m", "executor-thread", "defmt"] } embassy-time = { version = "0.3.2", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } embassy-usb = { version = "0.3.0", path = "../../embassy-usb", features = ["defmt"] } embassy-net = { version = "0.5.0", path = "../../embassy-net", features = ["defmt", "tcp", "dhcpv4", "medium-ethernet"] } diff --git a/examples/stm32u0/Cargo.toml b/examples/stm32u0/Cargo.toml index fef695c82..11953acfc 100644 --- a/examples/stm32u0/Cargo.toml +++ b/examples/stm32u0/Cargo.toml @@ -8,7 +8,7 @@ license = "MIT OR Apache-2.0" # Change stm32u083rc to your chip name, if necessary. embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = [ "defmt", "time-driver-any", "stm32u083rc", "memory-x", "unstable-pac", "exti", "chrono"] } embassy-sync = { version = "0.6.1", path = "../../embassy-sync", features = ["defmt"] } -embassy-executor = { version = "0.6.3", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] } +embassy-executor = { version = "0.6.3", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "defmt"] } embassy-time = { version = "0.3.2", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } embassy-usb = { version = "0.3.0", path = "../../embassy-usb", default-features = false, features = ["defmt"] } embassy-futures = { version = "0.1.0", path = "../../embassy-futures" } diff --git a/examples/stm32u5/Cargo.toml b/examples/stm32u5/Cargo.toml index 528429f4c..68a17ce43 100644 --- a/examples/stm32u5/Cargo.toml +++ b/examples/stm32u5/Cargo.toml @@ -8,7 +8,7 @@ license = "MIT OR Apache-2.0" # Change stm32u5g9zj to your chip name, if necessary. embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["defmt", "unstable-pac", "stm32u5g9zj", "time-driver-any", "memory-x" ] } embassy-sync = { version = "0.6.1", path = "../../embassy-sync", features = ["defmt"] } -embassy-executor = { version = "0.6.3", path = "../../embassy-executor", features = ["task-arena-size-32768", "arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] } +embassy-executor = { version = "0.6.3", path = "../../embassy-executor", features = ["task-arena-size-32768", "arch-cortex-m", "executor-thread", "defmt"] } embassy-time = { version = "0.3.2", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } embassy-usb = { version = "0.3.0", path = "../../embassy-usb", features = ["defmt"] } embassy-futures = { version = "0.1.0", path = "../../embassy-futures" } diff --git a/examples/stm32wb/Cargo.toml b/examples/stm32wb/Cargo.toml index 400c7b20c..ecc72397b 100644 --- a/examples/stm32wb/Cargo.toml +++ b/examples/stm32wb/Cargo.toml @@ -9,7 +9,7 @@ license = "MIT OR Apache-2.0" embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = [ "defmt", "stm32wb55rg", "time-driver-any", "memory-x", "exti"] } embassy-stm32-wpan = { version = "0.1.0", path = "../../embassy-stm32-wpan", features = ["defmt", "stm32wb55rg"] } embassy-sync = { version = "0.6.1", path = "../../embassy-sync", features = ["defmt"] } -embassy-executor = { version = "0.6.3", path = "../../embassy-executor", features = ["task-arena-size-32768", "arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] } +embassy-executor = { version = "0.6.3", path = "../../embassy-executor", features = ["task-arena-size-32768", "arch-cortex-m", "executor-thread", "defmt"] } embassy-time = { version = "0.3.2", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } embassy-net = { version = "0.5.0", path = "../../embassy-net", features = ["defmt", "udp", "proto-ipv6", "medium-ieee802154", ], optional=true } diff --git a/examples/stm32wba/Cargo.toml b/examples/stm32wba/Cargo.toml index 9e4251ce1..7735dfdde 100644 --- a/examples/stm32wba/Cargo.toml +++ b/examples/stm32wba/Cargo.toml @@ -7,7 +7,7 @@ license = "MIT OR Apache-2.0" [dependencies] embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = [ "defmt", "stm32wba52cg", "time-driver-any", "memory-x", "exti"] } embassy-sync = { version = "0.6.1", path = "../../embassy-sync", features = ["defmt"] } -embassy-executor = { version = "0.6.3", path = "../../embassy-executor", features = ["task-arena-size-32768", "arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] } +embassy-executor = { version = "0.6.3", path = "../../embassy-executor", features = ["task-arena-size-32768", "arch-cortex-m", "executor-thread", "defmt"] } embassy-time = { version = "0.3.2", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } embassy-net = { version = "0.5.0", path = "../../embassy-net", features = ["defmt", "udp", "proto-ipv6", "medium-ieee802154", ], optional=true } diff --git a/examples/stm32wl/Cargo.toml b/examples/stm32wl/Cargo.toml index 6507fd1eb..0182745e5 100644 --- a/examples/stm32wl/Cargo.toml +++ b/examples/stm32wl/Cargo.toml @@ -8,7 +8,7 @@ license = "MIT OR Apache-2.0" # Change stm32wl55jc-cm4 to your chip name, if necessary. embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["defmt", "stm32wl55jc-cm4", "time-driver-any", "memory-x", "unstable-pac", "exti", "chrono"] } embassy-sync = { version = "0.6.1", path = "../../embassy-sync", features = ["defmt"] } -embassy-executor = { version = "0.6.3", path = "../../embassy-executor", features = ["task-arena-size-4096", "arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] } +embassy-executor = { version = "0.6.3", path = "../../embassy-executor", features = ["task-arena-size-4096", "arch-cortex-m", "executor-thread", "defmt"] } embassy-time = { version = "0.3.2", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } embassy-embedded-hal = { version = "0.2.0", path = "../../embassy-embedded-hal" } diff --git a/examples/wasm/Cargo.toml b/examples/wasm/Cargo.toml index 5e4d352b5..f5dcdc0a2 100644 --- a/examples/wasm/Cargo.toml +++ b/examples/wasm/Cargo.toml @@ -9,7 +9,7 @@ crate-type = ["cdylib"] [dependencies] embassy-sync = { version = "0.6.1", path = "../../embassy-sync", features = ["log"] } -embassy-executor = { version = "0.6.3", path = "../../embassy-executor", features = ["arch-wasm", "executor-thread", "log", "integrated-timers"] } +embassy-executor = { version = "0.6.3", path = "../../embassy-executor", features = ["arch-wasm", "executor-thread", "log"] } embassy-time = { version = "0.3.2", path = "../../embassy-time", features = ["log", "wasm", ] } wasm-logger = "0.2.0" diff --git a/tests/nrf/Cargo.toml b/tests/nrf/Cargo.toml index 766318998..7af3d0649 100644 --- a/tests/nrf/Cargo.toml +++ b/tests/nrf/Cargo.toml @@ -9,7 +9,7 @@ teleprobe-meta = "1" embassy-futures = { version = "0.1.0", path = "../../embassy-futures" } embassy-sync = { version = "0.6.1", path = "../../embassy-sync", features = ["defmt", ] } -embassy-executor = { version = "0.6.3", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] } +embassy-executor = { version = "0.6.3", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "defmt"] } embassy-time = { version = "0.3.2", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime"] } embassy-nrf = { version = "0.2.0", path = "../../embassy-nrf", features = ["defmt", "time-driver-rtc1", "gpiote", "unstable-pac"] } embedded-io-async = { version = "0.6.1", features = ["defmt-03"] } diff --git a/tests/rp/Cargo.toml b/tests/rp/Cargo.toml index 7fb791578..8cd40418a 100644 --- a/tests/rp/Cargo.toml +++ b/tests/rp/Cargo.toml @@ -8,7 +8,7 @@ license = "MIT OR Apache-2.0" teleprobe-meta = "1.1" embassy-sync = { version = "0.6.1", path = "../../embassy-sync", features = ["defmt"] } -embassy-executor = { version = "0.6.3", path = "../../embassy-executor", features = ["task-arena-size-32768", "arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] } +embassy-executor = { version = "0.6.3", path = "../../embassy-executor", features = ["task-arena-size-32768", "arch-cortex-m", "executor-thread", "defmt"] } embassy-time = { version = "0.3.2", path = "../../embassy-time", features = ["defmt", ] } embassy-rp = { version = "0.2.0", path = "../../embassy-rp", features = [ "defmt", "unstable-pac", "time-driver", "critical-section-impl", "intrinsics", "rom-v2-intrinsics", "run-from-ram", "rp2040"] } embassy-futures = { version = "0.1.0", path = "../../embassy-futures" } diff --git a/tests/stm32/Cargo.toml b/tests/stm32/Cargo.toml index 599f7c702..5ae6878cc 100644 --- a/tests/stm32/Cargo.toml +++ b/tests/stm32/Cargo.toml @@ -60,7 +60,7 @@ cm0 = ["portable-atomic/unsafe-assume-single-core"] teleprobe-meta = "1" embassy-sync = { version = "0.6.1", path = "../../embassy-sync", features = ["defmt"] } -embassy-executor = { version = "0.6.3", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] } +embassy-executor = { version = "0.6.3", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "defmt"] } embassy-time = { version = "0.3.2", path = "../../embassy-time", features = ["defmt", "tick-hz-131_072", "defmt-timestamp-uptime"] } embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = [ "defmt", "unstable-pac", "memory-x", "time-driver-any"] } embassy-futures = { version = "0.1.0", path = "../../embassy-futures" } From 5c4983236c2e68b6ba2ce325ed77ec39466fc3b6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?D=C3=A1niel=20Buga?= Date: Fri, 13 Dec 2024 21:45:52 +0100 Subject: [PATCH 330/361] Make sure an exited task does not get stuck in a timer queue --- embassy-executor/src/raw/mod.rs | 14 ++++++++++++++ embassy-executor/tests/test.rs | 4 ++++ 2 files changed, 18 insertions(+) diff --git a/embassy-executor/src/raw/mod.rs b/embassy-executor/src/raw/mod.rs index 2feaab155..b825fa6c2 100644 --- a/embassy-executor/src/raw/mod.rs +++ b/embassy-executor/src/raw/mod.rs @@ -192,7 +192,17 @@ impl TaskStorage { match future.poll(&mut cx) { Poll::Ready(_) => { this.future.drop_in_place(); + + // Mark this task to be timer queued, to prevent re-queueing it. + this.raw.state.timer_enqueue(); + + // Now mark the task as not spawned, so that + // - it can be spawned again once it has been removed from the timer queue + // - it can not be timer-queued again this.raw.state.despawn(); + + // Schedule the task by hand in the past, so it runs immediately. + unsafe { _embassy_time_schedule_wake(0, &waker) } } Poll::Pending => {} } @@ -211,6 +221,10 @@ impl TaskStorage { } } +extern "Rust" { + fn _embassy_time_schedule_wake(at: u64, waker: &core::task::Waker); +} + /// An uninitialized [`TaskStorage`]. pub struct AvailableTask { task: &'static TaskStorage, diff --git a/embassy-executor/tests/test.rs b/embassy-executor/tests/test.rs index 0ce1f1891..992ab3da9 100644 --- a/embassy-executor/tests/test.rs +++ b/embassy-executor/tests/test.rs @@ -150,3 +150,7 @@ fn executor_task_cfg_args() { let (_, _, _) = (a, b, c); } } + +// We need this for the test to compile, even though we don't want to use timers at the moment. +#[no_mangle] +fn _embassy_time_schedule_wake(_at: u64, _waker: &core::task::Waker) {} From e861344b179b3e955ac47f1985b7f97fdfb93892 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?D=C3=A1niel=20Buga?= Date: Sun, 15 Dec 2024 17:44:42 +0100 Subject: [PATCH 331/361] Fix comments and tweak task exit --- embassy-executor/src/raw/mod.rs | 21 +++++++++++++++------ embassy-executor/src/raw/timer_queue.rs | 5 +++-- 2 files changed, 18 insertions(+), 8 deletions(-) diff --git a/embassy-executor/src/raw/mod.rs b/embassy-executor/src/raw/mod.rs index b825fa6c2..7da14468d 100644 --- a/embassy-executor/src/raw/mod.rs +++ b/embassy-executor/src/raw/mod.rs @@ -94,13 +94,14 @@ impl TaskRef { &self.header().timer_queue_item } - /// Mark the task as timer-queued. Return whether it was newly queued (i.e. not queued before) + /// Mark the task as timer-queued. Return whether it should be actually enqueued + /// using `_embassy_time_schedule_wake`. /// /// Entering this state prevents the task from being respawned while in a timer queue. /// /// Safety: /// - /// This functions should only be called by the timer queue implementation, before + /// This functions should only be called by the timer queue driver, before /// enqueueing the timer item. pub unsafe fn timer_enqueue(&self) -> timer_queue::TimerEnqueueOperation { self.header().state.timer_enqueue() @@ -193,16 +194,24 @@ impl TaskStorage { Poll::Ready(_) => { this.future.drop_in_place(); - // Mark this task to be timer queued, to prevent re-queueing it. - this.raw.state.timer_enqueue(); + // Mark this task to be timer queued. + // We're splitting the enqueue in two parts, so that we can change task state + // to something that prevent re-queueing. + let op = this.raw.state.timer_enqueue(); // Now mark the task as not spawned, so that // - it can be spawned again once it has been removed from the timer queue // - it can not be timer-queued again + // We must do this before scheduling the wake, to prevent the task from being + // dequeued by the time driver while it's still SPAWNED. this.raw.state.despawn(); - // Schedule the task by hand in the past, so it runs immediately. - unsafe { _embassy_time_schedule_wake(0, &waker) } + // Now let's finish enqueueing. While we shouldn't get an `Ignore` here, it's + // better to be safe. + if op == timer_queue::TimerEnqueueOperation::Enqueue { + // Schedule the task in the past, so it gets dequeued ASAP. + unsafe { _embassy_time_schedule_wake(0, &waker) } + } } Poll::Pending => {} } diff --git a/embassy-executor/src/raw/timer_queue.rs b/embassy-executor/src/raw/timer_queue.rs index c36708401..cd9a73822 100644 --- a/embassy-executor/src/raw/timer_queue.rs +++ b/embassy-executor/src/raw/timer_queue.rs @@ -30,9 +30,10 @@ impl TimerQueueItem { /// The operation to perform after `timer_enqueue` is called. #[derive(Debug, Copy, Clone, PartialEq)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] +#[must_use] pub enum TimerEnqueueOperation { - /// Enqueue the task. + /// Enqueue the task (or update its expiration time). Enqueue, - /// Update the task's expiration time. + /// The task must not be enqueued in the timer queue. Ignore, } From 0492dba5368e7cb22ede2d41d26d4d0431ba2252 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?D=C3=A1niel=20Buga?= Date: Sun, 15 Dec 2024 19:24:49 +0100 Subject: [PATCH 332/361] Update documentation and changelogs --- embassy-time-driver/CHANGELOG.md | 3 +- embassy-time-driver/src/lib.rs | 79 +++++++++++++++++++------- embassy-time-queue-driver/CHANGELOG.md | 5 +- embassy-time-queue-driver/src/lib.rs | 49 ++-------------- 4 files changed, 69 insertions(+), 67 deletions(-) diff --git a/embassy-time-driver/CHANGELOG.md b/embassy-time-driver/CHANGELOG.md index ebc37b6f4..2af1dc736 100644 --- a/embassy-time-driver/CHANGELOG.md +++ b/embassy-time-driver/CHANGELOG.md @@ -1,4 +1,4 @@ -# Changelog for embassy-time-queue-driver +# Changelog for embassy-time-driver All notable changes to this project will be documented in this file. @@ -8,6 +8,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## Unreleased - The `allocate_alarm`, `set_alarm_callback`, `set_alarm` functions have been removed. +- `schedule_wake` has been added to the `Driver` trait. ## 0.1.0 - 2024-01-11 diff --git a/embassy-time-driver/src/lib.rs b/embassy-time-driver/src/lib.rs index 090969d8c..57a9f7587 100644 --- a/embassy-time-driver/src/lib.rs +++ b/embassy-time-driver/src/lib.rs @@ -17,25 +17,7 @@ //! Otherwise, don’t enable any `tick-hz-*` feature to let the user configure the tick rate themselves by //! enabling a feature on `embassy-time`. //! -//! # Linkage details -//! -//! Instead of the usual "trait + generic params" approach, calls from embassy to the driver are done via `extern` functions. -//! -//! `embassy` internally defines the driver function as `extern "Rust" { fn _embassy_time_now() -> u64; }` and calls it. -//! The driver crate defines the function as `#[no_mangle] fn _embassy_time_now() -> u64`. The linker will resolve the -//! calls from the `embassy` crate to call into the driver crate. -//! -//! If there is none or multiple drivers in the crate tree, linking will fail. -//! -//! This method has a few key advantages for something as foundational as timekeeping: -//! -//! - The time driver is available everywhere easily, without having to thread the implementation -//! through generic parameters. This is especially helpful for libraries. -//! - It means comparing `Instant`s will always make sense: if there were multiple drivers -//! active, one could compare an `Instant` from driver A to an `Instant` from driver B, which -//! would yield incorrect results. -//! -//! # Example +//! ### Example //! //! ``` //! use core::task::Waker; @@ -56,6 +38,65 @@ //! //! embassy_time_driver::time_driver_impl!(static DRIVER: MyDriver = MyDriver{}); //! ``` +//! +//! ## Implementing the timer queue +//! +//! The simplest (but suboptimal) way to implement a timer queue is to define a single queue in the +//! time driver. Declare a field protected by an appropriate mutex (e.g. `critical_section::Mutex`). +//! +//! Then, you'll need to adapt the `schedule_wake` method to use this queue. +//! +//! ```ignore +//! use core::cell::RefCell; +//! use core::task::Waker; +//! +//! use embassy_time_queue_driver::Queue; +//! use embassy_time_driver::Driver; +//! +//! struct MyDriver { +//! timer_queue: critical_section::Mutex>, +//! } +//! +//! impl MyDriver { +//! fn set_alarm(&self, cs: &CriticalSection, at: u64) -> bool { +//! todo!() +//! } +//! } +//! +//! impl Driver for MyDriver { +//! // fn now(&self) -> u64 { ... } +//! +//! fn schedule_wake(&self, at: u64, waker: &Waker) { +//! critical_section::with(|cs| { +//! let mut queue = self.queue.borrow(cs).borrow_mut(); +//! if queue.schedule_wake(at, waker) { +//! let mut next = queue.next_expiration(self.now()); +//! while !self.set_alarm(cs, next) { +//! next = queue.next_expiration(self.now()); +//! } +//! } +//! }); +//! } +//! } +//! ``` +//! +//! # Linkage details +//! +//! Instead of the usual "trait + generic params" approach, calls from embassy to the driver are done via `extern` functions. +//! +//! `embassy` internally defines the driver function as `extern "Rust" { fn _embassy_time_now() -> u64; }` and calls it. +//! The driver crate defines the function as `#[no_mangle] fn _embassy_time_now() -> u64`. The linker will resolve the +//! calls from the `embassy` crate to call into the driver crate. +//! +//! If there is none or multiple drivers in the crate tree, linking will fail. +//! +//! This method has a few key advantages for something as foundational as timekeeping: +//! +//! - The time driver is available everywhere easily, without having to thread the implementation +//! through generic parameters. This is especially helpful for libraries. +//! - It means comparing `Instant`s will always make sense: if there were multiple drivers +//! active, one could compare an `Instant` from driver A to an `Instant` from driver B, which +//! would yield incorrect results. //! ## Feature flags #![doc = document_features::document_features!(feature_label = r#"{feature}"#)] diff --git a/embassy-time-queue-driver/CHANGELOG.md b/embassy-time-queue-driver/CHANGELOG.md index 3b2aa8695..a99f250ed 100644 --- a/embassy-time-queue-driver/CHANGELOG.md +++ b/embassy-time-queue-driver/CHANGELOG.md @@ -7,9 +7,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## Unreleased -- Added `integrated-timers` and `generic-queue-N` features -- Added `queue_generic` module which contains `Queue` (configured via the `generic-queue-N` features) and `ConstGenericQueue`. -- Added `GenericTimerQueue` and `GlobalTimerQueue` structs that can be used to implement timer queues. +- Added `generic-queue-N` features. +- Added `embassy_time_queue_driver::Queue` struct which uses integrated or a generic storage (configured using `generic-queue-N`). ## 0.1.0 - 2024-01-11 diff --git a/embassy-time-queue-driver/src/lib.rs b/embassy-time-queue-driver/src/lib.rs index 46dd646ca..d8b01df3b 100644 --- a/embassy-time-queue-driver/src/lib.rs +++ b/embassy-time-queue-driver/src/lib.rs @@ -2,52 +2,13 @@ #![doc = include_str!("../README.md")] #![warn(missing_docs)] -//! ## Implementing a timer queue +//! This crate is an implementation detail of `embassy-time-driver`. //! -//! - Define a struct `MyTimerQueue` -//! - Implement [`TimerQueue`] for it -//! - Register it as the global timer queue with [`timer_queue_impl`]. -//! - Ensure that you process the timer queue when `schedule_wake` is due. This usually involves -//! waking expired tasks, finding the next expiration time and setting an alarm. +//! As a HAL user, you should only depend on this crate if your application does not use +//! `embassy-executor` and your HAL does not configure a generic queue by itself. //! -//! If a single global timer queue is sufficient for you, you can use the -//! [`GlobalTimerQueue`] type, which is a wrapper around a global timer queue -//! protected by a critical section. -//! -//! ``` -//! use embassy_time_queue_driver::GlobalTimerQueue; -//! embassy_time_queue_driver::timer_queue_impl!( -//! static TIMER_QUEUE_DRIVER: GlobalTimerQueue -//! = GlobalTimerQueue::new(|next_expiration| todo!("Set an alarm")) -//! ); -//! ``` -//! -//! You can also use the `queue_generic` or the `queue_integrated` modules to implement your own -//! timer queue. These modules contain queue implementations which you can wrap and tailor to -//! your needs. -//! -//! If you are providing an embassy-executor implementation besides a timer queue, you can choose to -//! expose the `integrated-timers` feature in your implementation. This feature stores timer items -//! in the tasks themselves, so you don't need a fixed-size queue or dynamic memory allocation. -//! -//! ## Example -//! -//! ``` -//! use core::task::Waker; -//! -//! use embassy_time::Instant; -//! use embassy_time::queue::TimerQueue; -//! -//! struct MyTimerQueue{}; // not public! -//! -//! impl TimerQueue for MyTimerQueue { -//! fn schedule_wake(&'static self, at: u64, waker: &Waker) { -//! todo!() -//! } -//! } -//! -//! embassy_time_queue_driver::timer_queue_impl!(static QUEUE: MyTimerQueue = MyTimerQueue{}); -//! ``` +//! As a HAL implementer, you need to depend on this crate if you want to implement a time driver, +//! but how you should do so is documented in [`embassy_time_driver`]. use core::task::Waker; From e77ac50248639e299f93b1820e73c9df9ceb09e6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?D=C3=A1niel=20Buga?= Date: Sun, 15 Dec 2024 20:27:08 +0100 Subject: [PATCH 333/361] Remove critical_section dependency --- embassy-time-queue-driver/Cargo.toml | 1 - 1 file changed, 1 deletion(-) diff --git a/embassy-time-queue-driver/Cargo.toml b/embassy-time-queue-driver/Cargo.toml index 7a10e29b4..208b64668 100644 --- a/embassy-time-queue-driver/Cargo.toml +++ b/embassy-time-queue-driver/Cargo.toml @@ -21,7 +21,6 @@ categories = [ links = "embassy-time-queue" [dependencies] -critical-section = "1.2.0" heapless = "0.8" embassy-executor = { version = "0.6.3", path = "../embassy-executor" } embassy-time-driver = { version = "0.1.0", path = "../embassy-time-driver" } From 4df4ffbbd49729cde38a3a4a73cdafd208372a53 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?D=C3=A1niel=20Buga?= Date: Sun, 15 Dec 2024 20:28:12 +0100 Subject: [PATCH 334/361] Remove time-driver dependency --- embassy-time-queue-driver/Cargo.toml | 1 - embassy-time-queue-driver/src/lib.rs | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/embassy-time-queue-driver/Cargo.toml b/embassy-time-queue-driver/Cargo.toml index 208b64668..a104f5c39 100644 --- a/embassy-time-queue-driver/Cargo.toml +++ b/embassy-time-queue-driver/Cargo.toml @@ -23,7 +23,6 @@ links = "embassy-time-queue" [dependencies] heapless = "0.8" embassy-executor = { version = "0.6.3", path = "../embassy-executor" } -embassy-time-driver = { version = "0.1.0", path = "../embassy-time-driver" } [features] #! ### Generic Queue diff --git a/embassy-time-queue-driver/src/lib.rs b/embassy-time-queue-driver/src/lib.rs index d8b01df3b..97c81a124 100644 --- a/embassy-time-queue-driver/src/lib.rs +++ b/embassy-time-queue-driver/src/lib.rs @@ -8,7 +8,7 @@ //! `embassy-executor` and your HAL does not configure a generic queue by itself. //! //! As a HAL implementer, you need to depend on this crate if you want to implement a time driver, -//! but how you should do so is documented in [`embassy_time_driver`]. +//! but how you should do so is documented in `embassy-time-driver`. use core::task::Waker; From a10290b28e41922b0f53aafbcc82c49ee3f4e22f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?D=C3=A1niel=20Buga?= Date: Mon, 16 Dec 2024 09:15:15 +0100 Subject: [PATCH 335/361] Zero-inizialize expires_at --- embassy-executor/src/raw/timer_queue.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/embassy-executor/src/raw/timer_queue.rs b/embassy-executor/src/raw/timer_queue.rs index cd9a73822..2ba0e00a9 100644 --- a/embassy-executor/src/raw/timer_queue.rs +++ b/embassy-executor/src/raw/timer_queue.rs @@ -22,7 +22,7 @@ impl TimerQueueItem { pub(crate) const fn new() -> Self { Self { next: Cell::new(None), - expires_at: Cell::new(u64::MAX), + expires_at: Cell::new(0), } } } From e1c00613288024623f7fde61f65c4c40c9a5381a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?D=C3=A1niel=20Buga?= Date: Mon, 16 Dec 2024 12:54:45 +0100 Subject: [PATCH 336/361] Disable failing test --- ci.sh | 3 +++ 1 file changed, 3 insertions(+) diff --git a/ci.sh b/ci.sh index cb3a2f3f7..6b523193c 100755 --- a/ci.sh +++ b/ci.sh @@ -302,6 +302,9 @@ rm out/tests/stm32wb55rg/wpan_ble # unstable, I think it's running out of RAM? rm out/tests/stm32f207zg/eth +# temporarily disabled, hard faults for unknown reasons +rm out/tests/stm32f207zg/usart_rx_ringbuffered + # doesn't work, gives "noise error", no idea why. usart_dma does pass. rm out/tests/stm32u5a5zj/usart From f389ba37219d842d7db0ab94cd421c69645a5757 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?D=C3=A1niel=20Buga?= Date: Fri, 13 Dec 2024 20:35:40 +0100 Subject: [PATCH 337/361] Only lock once to wake a task --- embassy-executor/src/raw/mod.rs | 20 +++++---- embassy-executor/src/raw/run_queue_atomics.rs | 2 +- .../src/raw/run_queue_critical_section.rs | 10 ++--- embassy-executor/src/raw/state_atomics.rs | 20 +++++++-- embassy-executor/src/raw/state_atomics_arm.rs | 19 +++++++-- .../src/raw/state_critical_section.rs | 42 +++++++++++-------- 6 files changed, 73 insertions(+), 40 deletions(-) diff --git a/embassy-executor/src/raw/mod.rs b/embassy-executor/src/raw/mod.rs index 7da14468d..bcbd214a9 100644 --- a/embassy-executor/src/raw/mod.rs +++ b/embassy-executor/src/raw/mod.rs @@ -386,11 +386,11 @@ impl SyncExecutor { /// - `task` must be set up to run in this executor. /// - `task` must NOT be already enqueued (in this executor or another one). #[inline(always)] - unsafe fn enqueue(&self, task: TaskRef) { + unsafe fn enqueue(&self, task: TaskRef, l: state::Token) { #[cfg(feature = "trace")] trace::task_ready_begin(self, &task); - if self.run_queue.enqueue(task) { + if self.run_queue.enqueue(task, l) { self.pender.pend(); } } @@ -401,7 +401,9 @@ impl SyncExecutor { #[cfg(feature = "trace")] trace::task_new(self, &task); - self.enqueue(task); + state::locked(|l| { + self.enqueue(task, l); + }) } /// # Safety @@ -544,13 +546,13 @@ impl Executor { /// You can obtain a `TaskRef` from a `Waker` using [`task_from_waker`]. pub fn wake_task(task: TaskRef) { let header = task.header(); - if header.state.run_enqueue() { + header.state.run_enqueue(|l| { // We have just marked the task as scheduled, so enqueue it. unsafe { let executor = header.executor.get().unwrap_unchecked(); - executor.enqueue(task); + executor.enqueue(task, l); } - } + }); } /// Wake a task by `TaskRef` without calling pend. @@ -558,11 +560,11 @@ pub fn wake_task(task: TaskRef) { /// You can obtain a `TaskRef` from a `Waker` using [`task_from_waker`]. pub fn wake_task_no_pend(task: TaskRef) { let header = task.header(); - if header.state.run_enqueue() { + header.state.run_enqueue(|l| { // We have just marked the task as scheduled, so enqueue it. unsafe { let executor = header.executor.get().unwrap_unchecked(); - executor.run_queue.enqueue(task); + executor.run_queue.enqueue(task, l); } - } + }); } diff --git a/embassy-executor/src/raw/run_queue_atomics.rs b/embassy-executor/src/raw/run_queue_atomics.rs index 90907cfda..efdafdff0 100644 --- a/embassy-executor/src/raw/run_queue_atomics.rs +++ b/embassy-executor/src/raw/run_queue_atomics.rs @@ -45,7 +45,7 @@ impl RunQueue { /// /// `item` must NOT be already enqueued in any queue. #[inline(always)] - pub(crate) unsafe fn enqueue(&self, task: TaskRef) -> bool { + pub(crate) unsafe fn enqueue(&self, task: TaskRef, _: super::state::Token) -> bool { let mut was_empty = false; self.head diff --git a/embassy-executor/src/raw/run_queue_critical_section.rs b/embassy-executor/src/raw/run_queue_critical_section.rs index ba59c8f29..90f09e8c8 100644 --- a/embassy-executor/src/raw/run_queue_critical_section.rs +++ b/embassy-executor/src/raw/run_queue_critical_section.rs @@ -44,13 +44,11 @@ impl RunQueue { /// /// `item` must NOT be already enqueued in any queue. #[inline(always)] - pub(crate) unsafe fn enqueue(&self, task: TaskRef) -> bool { - critical_section::with(|cs| { - let prev = self.head.borrow(cs).replace(Some(task)); - task.header().run_queue_item.next.borrow(cs).set(prev); + pub(crate) unsafe fn enqueue(&self, task: TaskRef, cs: CriticalSection<'_>) -> bool { + let prev = self.head.borrow(cs).replace(Some(task)); + task.header().run_queue_item.next.borrow(cs).set(prev); - prev.is_none() - }) + prev.is_none() } /// Empty the queue, then call `on_task` for each task that was in the queue. diff --git a/embassy-executor/src/raw/state_atomics.rs b/embassy-executor/src/raw/state_atomics.rs index 15eb9a368..abfe94486 100644 --- a/embassy-executor/src/raw/state_atomics.rs +++ b/embassy-executor/src/raw/state_atomics.rs @@ -2,6 +2,15 @@ use core::sync::atomic::{AtomicU32, Ordering}; use super::timer_queue::TimerEnqueueOperation; +pub(crate) struct Token(()); + +/// Creates a token and passes it to the closure. +/// +/// This is a no-op replacement for `CriticalSection::with` because we don't need any locking. +pub(crate) fn locked(f: impl FnOnce(Token)) { + f(Token(())); +} + /// Task is spawned (has a future) pub(crate) const STATE_SPAWNED: u32 = 1 << 0; /// Task is in the executor run queue @@ -34,10 +43,12 @@ impl State { self.state.fetch_and(!STATE_SPAWNED, Ordering::AcqRel); } - /// Mark the task as run-queued if it's spawned and isn't already run-queued. Return true on success. + /// Mark the task as run-queued if it's spawned and isn't already run-queued. Run the given + /// function if the task was successfully marked. #[inline(always)] - pub fn run_enqueue(&self) -> bool { - self.state + pub fn run_enqueue(&self, f: impl FnOnce(Token)) { + if self + .state .fetch_update(Ordering::SeqCst, Ordering::SeqCst, |state| { // If already scheduled, or if not started, if (state & STATE_RUN_QUEUED != 0) || (state & STATE_SPAWNED == 0) { @@ -48,6 +59,9 @@ impl State { } }) .is_ok() + { + locked(f); + } } /// Unmark the task as run-queued. Return whether the task is spawned. diff --git a/embassy-executor/src/raw/state_atomics_arm.rs b/embassy-executor/src/raw/state_atomics_arm.rs index 7a152e8c0..f0f014652 100644 --- a/embassy-executor/src/raw/state_atomics_arm.rs +++ b/embassy-executor/src/raw/state_atomics_arm.rs @@ -3,6 +3,15 @@ use core::sync::atomic::{compiler_fence, AtomicBool, AtomicU32, Ordering}; use super::timer_queue::TimerEnqueueOperation; +pub(crate) struct Token(()); + +/// Creates a token and passes it to the closure. +/// +/// This is a no-op replacement for `CriticalSection::with` because we don't need any locking. +pub(crate) fn locked(f: impl FnOnce(Token)) { + f(Token(())); +} + // Must be kept in sync with the layout of `State`! pub(crate) const STATE_SPAWNED: u32 = 1 << 0; pub(crate) const STATE_RUN_QUEUED: u32 = 1 << 8; @@ -57,9 +66,10 @@ impl State { self.spawned.store(false, Ordering::Relaxed); } - /// Mark the task as run-queued if it's spawned and isn't already run-queued. Return true on success. + /// Mark the task as run-queued if it's spawned and isn't already run-queued. Run the given + /// function if the task was successfully marked. #[inline(always)] - pub fn run_enqueue(&self) -> bool { + pub fn run_enqueue(&self, f: impl FnOnce(Token)) { unsafe { loop { let state: u32; @@ -67,14 +77,15 @@ impl State { if (state & STATE_RUN_QUEUED != 0) || (state & STATE_SPAWNED == 0) { asm!("clrex", options(nomem, nostack)); - return false; + return; } let outcome: usize; let new_state = state | STATE_RUN_QUEUED; asm!("strex {}, {}, [{}]", out(reg) outcome, in(reg) new_state, in(reg) self, options(nostack)); if outcome == 0 { - return true; + locked(f); + return; } } } diff --git a/embassy-executor/src/raw/state_critical_section.rs b/embassy-executor/src/raw/state_critical_section.rs index 367162ba2..8e570b33c 100644 --- a/embassy-executor/src/raw/state_critical_section.rs +++ b/embassy-executor/src/raw/state_critical_section.rs @@ -1,6 +1,7 @@ use core::cell::Cell; -use critical_section::Mutex; +pub(crate) use critical_section::{with as locked, CriticalSection as Token}; +use critical_section::{CriticalSection, Mutex}; use super::timer_queue::TimerEnqueueOperation; @@ -23,13 +24,15 @@ impl State { } fn update(&self, f: impl FnOnce(&mut u32) -> R) -> R { - critical_section::with(|cs| { - let s = self.state.borrow(cs); - let mut val = s.get(); - let r = f(&mut val); - s.set(val); - r - }) + critical_section::with(|cs| self.update_with_cs(cs, f)) + } + + fn update_with_cs(&self, cs: CriticalSection<'_>, f: impl FnOnce(&mut u32) -> R) -> R { + let s = self.state.borrow(cs); + let mut val = s.get(); + let r = f(&mut val); + s.set(val); + r } /// If task is idle, mark it as spawned + run_queued and return true. @@ -51,17 +54,22 @@ impl State { self.update(|s| *s &= !STATE_SPAWNED); } - /// Mark the task as run-queued if it's spawned and isn't already run-queued. Return true on success. + /// Mark the task as run-queued if it's spawned and isn't already run-queued. Run the given + /// function if the task was successfully marked. #[inline(always)] - pub fn run_enqueue(&self) -> bool { - self.update(|s| { - if (*s & STATE_RUN_QUEUED != 0) || (*s & STATE_SPAWNED == 0) { - false - } else { - *s |= STATE_RUN_QUEUED; - true + pub fn run_enqueue(&self, f: impl FnOnce(Token)) { + critical_section::with(|cs| { + if self.update_with_cs(cs, |s| { + if (*s & STATE_RUN_QUEUED != 0) || (*s & STATE_SPAWNED == 0) { + false + } else { + *s |= STATE_RUN_QUEUED; + true + } + }) { + f(cs); } - }) + }); } /// Unmark the task as run-queued. Return whether the task is spawned. From b44ef5ccb40d6b778e623e6e68a234c2e0615d25 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?D=C3=A1niel=20Buga?= Date: Mon, 16 Dec 2024 15:56:55 +0100 Subject: [PATCH 338/361] Fix racy access of TaskHeader::executor --- embassy-executor/src/raw/mod.rs | 70 +++++++++++++++++-- embassy-executor/src/raw/state_atomics.rs | 5 +- embassy-executor/src/raw/state_atomics_arm.rs | 5 +- embassy-executor/src/spawner.rs | 8 ++- 4 files changed, 75 insertions(+), 13 deletions(-) diff --git a/embassy-executor/src/raw/mod.rs b/embassy-executor/src/raw/mod.rs index bcbd214a9..808a78389 100644 --- a/embassy-executor/src/raw/mod.rs +++ b/embassy-executor/src/raw/mod.rs @@ -14,7 +14,58 @@ mod run_queue; #[cfg_attr(all(cortex_m, target_has_atomic = "8"), path = "state_atomics_arm.rs")] #[cfg_attr(all(not(cortex_m), target_has_atomic = "8"), path = "state_atomics.rs")] #[cfg_attr(not(target_has_atomic = "8"), path = "state_critical_section.rs")] -mod state; +pub(crate) mod state; + +#[cfg(target_has_atomic = "ptr")] +mod owner { + use core::sync::atomic::{AtomicPtr, Ordering}; + + use super::{state::Token, SyncExecutor}; + + pub(crate) struct ExecutorRef(AtomicPtr); + + impl ExecutorRef { + pub const fn new() -> Self { + Self(AtomicPtr::new(core::ptr::null_mut())) + } + + pub fn set(&self, executor: Option<&'static SyncExecutor>, _: Token) { + let ptr = executor.map(|e| e as *const SyncExecutor).unwrap_or(core::ptr::null()); + self.0.store(ptr.cast_mut(), Ordering::Release); + } + + pub fn get(&self, _: Token) -> *const SyncExecutor { + self.0.load(Ordering::Acquire).cast_const() + } + } +} +#[cfg(not(target_has_atomic = "ptr"))] +mod owner { + use super::{state::Token, SyncExecutor}; + use core::cell::Cell; + + use critical_section::Mutex; + + pub(crate) struct ExecutorRef(Mutex>); + + unsafe impl Send for ExecutorRef {} + unsafe impl Sync for ExecutorRef {} + + impl ExecutorRef { + pub const fn new() -> Self { + Self(Mutex::new(Cell::new(core::ptr::null()))) + } + + pub fn set(&self, executor: Option<&'static SyncExecutor>, cs: Token) { + let ptr = executor.map(|e| e as *const SyncExecutor).unwrap_or(core::ptr::null()); + self.0.borrow(cs).set(ptr); + } + + pub fn get(&self, cs: Token) -> *const SyncExecutor { + self.0.borrow(cs).get() + } + } +} pub mod timer_queue; #[cfg(feature = "trace")] @@ -30,6 +81,8 @@ use core::pin::Pin; use core::ptr::NonNull; use core::task::{Context, Poll}; +use crate::raw::owner::ExecutorRef; + use self::run_queue::{RunQueue, RunQueueItem}; use self::state::State; use self::util::{SyncUnsafeCell, UninitCell}; @@ -40,7 +93,7 @@ use super::SpawnToken; pub(crate) struct TaskHeader { pub(crate) state: State, pub(crate) run_queue_item: RunQueueItem, - pub(crate) executor: SyncUnsafeCell>, + pub(crate) executor: ExecutorRef, poll_fn: SyncUnsafeCell>, /// Integrated timer queue storage. This field should not be accessed outside of the timer queue. @@ -86,7 +139,8 @@ impl TaskRef { /// Returns a reference to the executor that the task is currently running on. pub unsafe fn executor(self) -> Option<&'static Executor> { - self.header().executor.get().map(|e| Executor::wrap(e)) + let executor = state::locked(|token| self.header().executor.get(token)); + executor.as_ref().map(|e| Executor::wrap(e)) } /// Returns a reference to the timer queue item. @@ -153,7 +207,7 @@ impl TaskStorage { raw: TaskHeader { state: State::new(), run_queue_item: RunQueueItem::new(), - executor: SyncUnsafeCell::new(None), + executor: ExecutorRef::new(), // Note: this is lazily initialized so that a static `TaskStorage` will go in `.bss` poll_fn: SyncUnsafeCell::new(None), @@ -396,7 +450,9 @@ impl SyncExecutor { } pub(super) unsafe fn spawn(&'static self, task: TaskRef) { - task.header().executor.set(Some(self)); + state::locked(|l| { + task.header().executor.set(Some(self), l); + }); #[cfg(feature = "trace")] trace::task_new(self, &task); @@ -549,7 +605,7 @@ pub fn wake_task(task: TaskRef) { header.state.run_enqueue(|l| { // We have just marked the task as scheduled, so enqueue it. unsafe { - let executor = header.executor.get().unwrap_unchecked(); + let executor = header.executor.get(l).as_ref().unwrap_unchecked(); executor.enqueue(task, l); } }); @@ -563,7 +619,7 @@ pub fn wake_task_no_pend(task: TaskRef) { header.state.run_enqueue(|l| { // We have just marked the task as scheduled, so enqueue it. unsafe { - let executor = header.executor.get().unwrap_unchecked(); + let executor = header.executor.get(l).as_ref().unwrap_unchecked(); executor.run_queue.enqueue(task, l); } }); diff --git a/embassy-executor/src/raw/state_atomics.rs b/embassy-executor/src/raw/state_atomics.rs index abfe94486..d7350464f 100644 --- a/embassy-executor/src/raw/state_atomics.rs +++ b/embassy-executor/src/raw/state_atomics.rs @@ -2,13 +2,14 @@ use core::sync::atomic::{AtomicU32, Ordering}; use super::timer_queue::TimerEnqueueOperation; +#[derive(Clone, Copy)] pub(crate) struct Token(()); /// Creates a token and passes it to the closure. /// /// This is a no-op replacement for `CriticalSection::with` because we don't need any locking. -pub(crate) fn locked(f: impl FnOnce(Token)) { - f(Token(())); +pub(crate) fn locked(f: impl FnOnce(Token) -> R) -> R { + f(Token(())) } /// Task is spawned (has a future) diff --git a/embassy-executor/src/raw/state_atomics_arm.rs b/embassy-executor/src/raw/state_atomics_arm.rs index f0f014652..c1e8f69ab 100644 --- a/embassy-executor/src/raw/state_atomics_arm.rs +++ b/embassy-executor/src/raw/state_atomics_arm.rs @@ -3,13 +3,14 @@ use core::sync::atomic::{compiler_fence, AtomicBool, AtomicU32, Ordering}; use super::timer_queue::TimerEnqueueOperation; +#[derive(Clone, Copy)] pub(crate) struct Token(()); /// Creates a token and passes it to the closure. /// /// This is a no-op replacement for `CriticalSection::with` because we don't need any locking. -pub(crate) fn locked(f: impl FnOnce(Token)) { - f(Token(())); +pub(crate) fn locked(f: impl FnOnce(Token) -> R) -> R { + f(Token(())) } // Must be kept in sync with the layout of `State`! diff --git a/embassy-executor/src/spawner.rs b/embassy-executor/src/spawner.rs index 271606244..bc243bee7 100644 --- a/embassy-executor/src/spawner.rs +++ b/embassy-executor/src/spawner.rs @@ -92,7 +92,9 @@ impl Spawner { pub async fn for_current_executor() -> Self { poll_fn(|cx| { let task = raw::task_from_waker(cx.waker()); - let executor = unsafe { task.header().executor.get().unwrap_unchecked() }; + let executor = raw::state::locked(|l| { + unsafe { task.header().executor.get(l).as_ref().unwrap_unchecked() } + }); let executor = unsafe { raw::Executor::wrap(executor) }; Poll::Ready(Self::new(executor)) }) @@ -164,7 +166,9 @@ impl SendSpawner { pub async fn for_current_executor() -> Self { poll_fn(|cx| { let task = raw::task_from_waker(cx.waker()); - let executor = unsafe { task.header().executor.get().unwrap_unchecked() }; + let executor = raw::state::locked(|l| { + unsafe { task.header().executor.get(l).as_ref().unwrap_unchecked() } + }); Poll::Ready(Self::new(executor)) }) .await From 87280463b24ce4d50b655aa526ab034b0dc2ad61 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Thu, 28 Nov 2024 17:59:45 +0100 Subject: [PATCH 339/361] Update to Rust 1.83 --- rust-toolchain.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rust-toolchain.toml b/rust-toolchain.toml index 69a964040..704d2e3a2 100644 --- a/rust-toolchain.toml +++ b/rust-toolchain.toml @@ -1,5 +1,5 @@ [toolchain] -channel = "1.82" +channel = "1.83" components = [ "rust-src", "rustfmt", "llvm-tools" ] targets = [ "thumbv7em-none-eabi", From 2846647c94d75b433232c959655b88558c560767 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Mon, 16 Dec 2024 16:09:01 +0100 Subject: [PATCH 340/361] Remove use of static mut. --- examples/stm32f4/src/bin/usb_uac_speaker.rs | 19 +++++++++++-------- examples/stm32h5/src/bin/usb_uac_speaker.rs | 19 +++++++++++-------- 2 files changed, 22 insertions(+), 16 deletions(-) diff --git a/examples/stm32f4/src/bin/usb_uac_speaker.rs b/examples/stm32f4/src/bin/usb_uac_speaker.rs index 8d83afd1a..e22e07e63 100644 --- a/examples/stm32f4/src/bin/usb_uac_speaker.rs +++ b/examples/stm32f4/src/bin/usb_uac_speaker.rs @@ -1,7 +1,7 @@ #![no_std] #![no_main] -use core::cell::RefCell; +use core::cell::{Cell, RefCell}; use defmt::{panic, *}; use embassy_executor::Spawner; @@ -217,8 +217,8 @@ async fn usb_control_task(control_monitor: speaker::ControlMonitor<'static>) { /// This gives an (ideal) counter value of 336.000 for every update of the `FEEDBACK_SIGNAL`. #[interrupt] fn TIM2() { - static mut LAST_TICKS: u32 = 0; - static mut FRAME_COUNT: usize = 0; + static LAST_TICKS: Mutex> = Mutex::new(Cell::new(0)); + static FRAME_COUNT: Mutex> = Mutex::new(Cell::new(0)); critical_section::with(|cs| { // Read timer counter. @@ -230,11 +230,14 @@ fn TIM2() { if status.ccif(CHANNEL_INDEX) { let ticks = timer.ccr(CHANNEL_INDEX).read(); - *FRAME_COUNT += 1; - if *FRAME_COUNT >= FEEDBACK_REFRESH_PERIOD.frame_count() { - *FRAME_COUNT = 0; - FEEDBACK_SIGNAL.signal(ticks.wrapping_sub(*LAST_TICKS)); - *LAST_TICKS = ticks; + let frame_count = FRAME_COUNT.borrow(cs); + let last_ticks = LAST_TICKS.borrow(cs); + + frame_count.set(frame_count.get() + 1); + if frame_count.get() >= FEEDBACK_REFRESH_PERIOD.frame_count() { + frame_count.set(0); + FEEDBACK_SIGNAL.signal(ticks.wrapping_sub(last_ticks.get())); + last_ticks.set(ticks); } }; diff --git a/examples/stm32h5/src/bin/usb_uac_speaker.rs b/examples/stm32h5/src/bin/usb_uac_speaker.rs index 4fd4ccbbd..8c24fa916 100644 --- a/examples/stm32h5/src/bin/usb_uac_speaker.rs +++ b/examples/stm32h5/src/bin/usb_uac_speaker.rs @@ -1,7 +1,7 @@ #![no_std] #![no_main] -use core::cell::RefCell; +use core::cell::{Cell, RefCell}; use defmt::{panic, *}; use embassy_executor::Spawner; @@ -212,8 +212,8 @@ async fn usb_control_task(control_monitor: speaker::ControlMonitor<'static>) { /// This gives an (ideal) counter value of 336.000 for every update of the `FEEDBACK_SIGNAL`. #[interrupt] fn TIM5() { - static mut LAST_TICKS: u32 = 0; - static mut FRAME_COUNT: usize = 0; + static LAST_TICKS: Mutex> = Mutex::new(Cell::new(0)); + static FRAME_COUNT: Mutex> = Mutex::new(Cell::new(0)); critical_section::with(|cs| { // Read timer counter. @@ -225,11 +225,14 @@ fn TIM5() { if status.ccif(CHANNEL_INDEX) { let ticks = timer.ccr(CHANNEL_INDEX).read(); - *FRAME_COUNT += 1; - if *FRAME_COUNT >= FEEDBACK_REFRESH_PERIOD.frame_count() { - *FRAME_COUNT = 0; - FEEDBACK_SIGNAL.signal(ticks.wrapping_sub(*LAST_TICKS)); - *LAST_TICKS = ticks; + let frame_count = FRAME_COUNT.borrow(cs); + let last_ticks = LAST_TICKS.borrow(cs); + + frame_count.set(frame_count.get() + 1); + if frame_count.get() >= FEEDBACK_REFRESH_PERIOD.frame_count() { + frame_count.set(0); + FEEDBACK_SIGNAL.signal(ticks.wrapping_sub(last_ticks.get())); + last_ticks.set(ticks); } }; From b47a631abf0c200c3b29b8e4ec199421835a0525 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?D=C3=A1niel=20Buga?= Date: Mon, 16 Dec 2024 17:24:17 +0100 Subject: [PATCH 341/361] Rely on atomic load-store on all targets --- embassy-executor/src/raw/mod.rs | 72 +++++---------------------------- embassy-executor/src/spawner.rs | 21 +++++++--- 2 files changed, 25 insertions(+), 68 deletions(-) diff --git a/embassy-executor/src/raw/mod.rs b/embassy-executor/src/raw/mod.rs index 808a78389..5a476213b 100644 --- a/embassy-executor/src/raw/mod.rs +++ b/embassy-executor/src/raw/mod.rs @@ -14,58 +14,7 @@ mod run_queue; #[cfg_attr(all(cortex_m, target_has_atomic = "8"), path = "state_atomics_arm.rs")] #[cfg_attr(all(not(cortex_m), target_has_atomic = "8"), path = "state_atomics.rs")] #[cfg_attr(not(target_has_atomic = "8"), path = "state_critical_section.rs")] -pub(crate) mod state; - -#[cfg(target_has_atomic = "ptr")] -mod owner { - use core::sync::atomic::{AtomicPtr, Ordering}; - - use super::{state::Token, SyncExecutor}; - - pub(crate) struct ExecutorRef(AtomicPtr); - - impl ExecutorRef { - pub const fn new() -> Self { - Self(AtomicPtr::new(core::ptr::null_mut())) - } - - pub fn set(&self, executor: Option<&'static SyncExecutor>, _: Token) { - let ptr = executor.map(|e| e as *const SyncExecutor).unwrap_or(core::ptr::null()); - self.0.store(ptr.cast_mut(), Ordering::Release); - } - - pub fn get(&self, _: Token) -> *const SyncExecutor { - self.0.load(Ordering::Acquire).cast_const() - } - } -} -#[cfg(not(target_has_atomic = "ptr"))] -mod owner { - use super::{state::Token, SyncExecutor}; - use core::cell::Cell; - - use critical_section::Mutex; - - pub(crate) struct ExecutorRef(Mutex>); - - unsafe impl Send for ExecutorRef {} - unsafe impl Sync for ExecutorRef {} - - impl ExecutorRef { - pub const fn new() -> Self { - Self(Mutex::new(Cell::new(core::ptr::null()))) - } - - pub fn set(&self, executor: Option<&'static SyncExecutor>, cs: Token) { - let ptr = executor.map(|e| e as *const SyncExecutor).unwrap_or(core::ptr::null()); - self.0.borrow(cs).set(ptr); - } - - pub fn get(&self, cs: Token) -> *const SyncExecutor { - self.0.borrow(cs).get() - } - } -} +mod state; pub mod timer_queue; #[cfg(feature = "trace")] @@ -79,10 +28,9 @@ use core::marker::PhantomData; use core::mem; use core::pin::Pin; use core::ptr::NonNull; +use core::sync::atomic::{AtomicPtr, Ordering}; use core::task::{Context, Poll}; -use crate::raw::owner::ExecutorRef; - use self::run_queue::{RunQueue, RunQueueItem}; use self::state::State; use self::util::{SyncUnsafeCell, UninitCell}; @@ -93,7 +41,7 @@ use super::SpawnToken; pub(crate) struct TaskHeader { pub(crate) state: State, pub(crate) run_queue_item: RunQueueItem, - pub(crate) executor: ExecutorRef, + pub(crate) executor: AtomicPtr, poll_fn: SyncUnsafeCell>, /// Integrated timer queue storage. This field should not be accessed outside of the timer queue. @@ -139,7 +87,7 @@ impl TaskRef { /// Returns a reference to the executor that the task is currently running on. pub unsafe fn executor(self) -> Option<&'static Executor> { - let executor = state::locked(|token| self.header().executor.get(token)); + let executor = self.header().executor.load(Ordering::Relaxed); executor.as_ref().map(|e| Executor::wrap(e)) } @@ -207,7 +155,7 @@ impl TaskStorage { raw: TaskHeader { state: State::new(), run_queue_item: RunQueueItem::new(), - executor: ExecutorRef::new(), + executor: AtomicPtr::new(core::ptr::null_mut()), // Note: this is lazily initialized so that a static `TaskStorage` will go in `.bss` poll_fn: SyncUnsafeCell::new(None), @@ -450,9 +398,9 @@ impl SyncExecutor { } pub(super) unsafe fn spawn(&'static self, task: TaskRef) { - state::locked(|l| { - task.header().executor.set(Some(self), l); - }); + task.header() + .executor + .store((self as *const Self).cast_mut(), Ordering::Relaxed); #[cfg(feature = "trace")] trace::task_new(self, &task); @@ -605,7 +553,7 @@ pub fn wake_task(task: TaskRef) { header.state.run_enqueue(|l| { // We have just marked the task as scheduled, so enqueue it. unsafe { - let executor = header.executor.get(l).as_ref().unwrap_unchecked(); + let executor = header.executor.load(Ordering::Relaxed).as_ref().unwrap_unchecked(); executor.enqueue(task, l); } }); @@ -619,7 +567,7 @@ pub fn wake_task_no_pend(task: TaskRef) { header.state.run_enqueue(|l| { // We have just marked the task as scheduled, so enqueue it. unsafe { - let executor = header.executor.get(l).as_ref().unwrap_unchecked(); + let executor = header.executor.load(Ordering::Relaxed).as_ref().unwrap_unchecked(); executor.run_queue.enqueue(task, l); } }); diff --git a/embassy-executor/src/spawner.rs b/embassy-executor/src/spawner.rs index bc243bee7..16347ad71 100644 --- a/embassy-executor/src/spawner.rs +++ b/embassy-executor/src/spawner.rs @@ -1,6 +1,7 @@ use core::future::poll_fn; use core::marker::PhantomData; use core::mem; +use core::sync::atomic::Ordering; use core::task::Poll; use super::raw; @@ -92,9 +93,13 @@ impl Spawner { pub async fn for_current_executor() -> Self { poll_fn(|cx| { let task = raw::task_from_waker(cx.waker()); - let executor = raw::state::locked(|l| { - unsafe { task.header().executor.get(l).as_ref().unwrap_unchecked() } - }); + let executor = unsafe { + task.header() + .executor + .load(Ordering::Relaxed) + .as_ref() + .unwrap_unchecked() + }; let executor = unsafe { raw::Executor::wrap(executor) }; Poll::Ready(Self::new(executor)) }) @@ -166,9 +171,13 @@ impl SendSpawner { pub async fn for_current_executor() -> Self { poll_fn(|cx| { let task = raw::task_from_waker(cx.waker()); - let executor = raw::state::locked(|l| { - unsafe { task.header().executor.get(l).as_ref().unwrap_unchecked() } - }); + let executor = unsafe { + task.header() + .executor + .load(Ordering::Relaxed) + .as_ref() + .unwrap_unchecked() + }; Poll::Ready(Self::new(executor)) }) .await From 3c121e5425e0a1901c459d27e3e5929f86d0a206 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?D=C3=A1niel=20Buga?= Date: Mon, 16 Dec 2024 18:08:19 +0100 Subject: [PATCH 342/361] Remove special handling of integrated timer queue --- embassy-executor/src/raw/mod.rs | 22 ---------------------- embassy-time-driver/src/lib.rs | 6 ++++++ embassy-time-queue-driver/src/lib.rs | 28 ---------------------------- embassy-time/Cargo.toml | 8 ++++---- embassy-time/src/timer.rs | 6 +++--- 5 files changed, 13 insertions(+), 57 deletions(-) diff --git a/embassy-executor/src/raw/mod.rs b/embassy-executor/src/raw/mod.rs index 5a476213b..997db6756 100644 --- a/embassy-executor/src/raw/mod.rs +++ b/embassy-executor/src/raw/mod.rs @@ -195,25 +195,7 @@ impl TaskStorage { match future.poll(&mut cx) { Poll::Ready(_) => { this.future.drop_in_place(); - - // Mark this task to be timer queued. - // We're splitting the enqueue in two parts, so that we can change task state - // to something that prevent re-queueing. - let op = this.raw.state.timer_enqueue(); - - // Now mark the task as not spawned, so that - // - it can be spawned again once it has been removed from the timer queue - // - it can not be timer-queued again - // We must do this before scheduling the wake, to prevent the task from being - // dequeued by the time driver while it's still SPAWNED. this.raw.state.despawn(); - - // Now let's finish enqueueing. While we shouldn't get an `Ignore` here, it's - // better to be safe. - if op == timer_queue::TimerEnqueueOperation::Enqueue { - // Schedule the task in the past, so it gets dequeued ASAP. - unsafe { _embassy_time_schedule_wake(0, &waker) } - } } Poll::Pending => {} } @@ -232,10 +214,6 @@ impl TaskStorage { } } -extern "Rust" { - fn _embassy_time_schedule_wake(at: u64, waker: &core::task::Waker); -} - /// An uninitialized [`TaskStorage`]. pub struct AvailableTask { task: &'static TaskStorage, diff --git a/embassy-time-driver/src/lib.rs b/embassy-time-driver/src/lib.rs index 57a9f7587..c776fbdf8 100644 --- a/embassy-time-driver/src/lib.rs +++ b/embassy-time-driver/src/lib.rs @@ -131,6 +131,7 @@ pub trait Driver: Send + Sync + 'static { extern "Rust" { fn _embassy_time_now() -> u64; + fn _embassy_time_schedule_wake(at: u64, waker: &Waker); } /// See [`Driver::now`] @@ -138,6 +139,11 @@ pub fn now() -> u64 { unsafe { _embassy_time_now() } } +/// Schedule the given waker to be woken at `at`. +pub fn schedule_wake(at: u64, waker: &Waker) { + unsafe { _embassy_time_schedule_wake(at, waker) } +} + /// Set the time Driver implementation. /// /// See the module documentation for an example. diff --git a/embassy-time-queue-driver/src/lib.rs b/embassy-time-queue-driver/src/lib.rs index 97c81a124..333b6124d 100644 --- a/embassy-time-queue-driver/src/lib.rs +++ b/embassy-time-queue-driver/src/lib.rs @@ -10,8 +10,6 @@ //! As a HAL implementer, you need to depend on this crate if you want to implement a time driver, //! but how you should do so is documented in `embassy-time-driver`. -use core::task::Waker; - #[cfg(feature = "_generic-queue")] pub mod queue_generic; #[cfg(not(feature = "_generic-queue"))] @@ -21,29 +19,3 @@ pub mod queue_integrated; pub use queue_generic::Queue; #[cfg(not(feature = "_generic-queue"))] pub use queue_integrated::Queue; - -extern "Rust" { - fn _embassy_time_schedule_wake(at: u64, waker: &Waker); -} - -/// Schedule the given waker to be woken at `at`. -pub fn schedule_wake(at: u64, waker: &Waker) { - // This function is not implemented in embassy-time-driver because it needs access to executor - // internals. The function updates task state, then delegates to the implementation provided - // by the time driver. - #[cfg(not(feature = "_generic-queue"))] - { - use embassy_executor::raw::task_from_waker; - use embassy_executor::raw::timer_queue::TimerEnqueueOperation; - // The very first thing we must do, before we even access the timer queue, is to - // mark the task a TIMER_QUEUED. This ensures that the task that is being scheduled - // can not be respawn while we are accessing the timer queue. - let task = task_from_waker(waker); - if unsafe { task.timer_enqueue() } == TimerEnqueueOperation::Ignore { - // We are not allowed to enqueue the task in the timer queue. This is because the - // task is not spawned, and so it makes no sense to schedule it. - return; - } - } - unsafe { _embassy_time_schedule_wake(at, waker) } -} diff --git a/embassy-time/Cargo.toml b/embassy-time/Cargo.toml index e3074119f..4f4ea0b14 100644 --- a/embassy-time/Cargo.toml +++ b/embassy-time/Cargo.toml @@ -24,8 +24,8 @@ target = "x86_64-unknown-linux-gnu" features = ["defmt", "std"] [features] -std = ["tick-hz-1_000_000", "critical-section/std"] -wasm = ["dep:wasm-bindgen", "dep:js-sys", "dep:wasm-timer", "tick-hz-1_000_000"] +std = ["tick-hz-1_000_000", "critical-section/std", "dep:embassy-time-queue-driver"] +wasm = ["dep:wasm-bindgen", "dep:js-sys", "dep:wasm-timer", "tick-hz-1_000_000", "dep:embassy-time-queue-driver"] ## Display the time since startup next to defmt log messages. ## At most 1 `defmt-timestamp-uptime-*` feature can be used. @@ -40,7 +40,7 @@ defmt-timestamp-uptime-tms = ["defmt"] defmt-timestamp-uptime-tus = ["defmt"] ## Create a `MockDriver` that can be manually advanced for testing purposes. -mock-driver = ["tick-hz-1_000_000"] +mock-driver = ["tick-hz-1_000_000", "dep:embassy-time-queue-driver"] #! ### Tick Rate #! @@ -384,7 +384,7 @@ tick-hz-5_242_880_000 = ["embassy-time-driver/tick-hz-5_242_880_000"] [dependencies] embassy-time-driver = { version = "0.1.0", path = "../embassy-time-driver" } -embassy-time-queue-driver = { version = "0.1.0", path = "../embassy-time-queue-driver" } +embassy-time-queue-driver = { version = "0.1.0", path = "../embassy-time-queue-driver", optional = true} defmt = { version = "0.3", optional = true } log = { version = "0.4.14", optional = true } diff --git a/embassy-time/src/timer.rs b/embassy-time/src/timer.rs index 4d7194b20..295ddbd9b 100644 --- a/embassy-time/src/timer.rs +++ b/embassy-time/src/timer.rs @@ -157,7 +157,7 @@ impl Future for Timer { if self.yielded_once && self.expires_at <= Instant::now() { Poll::Ready(()) } else { - embassy_time_queue_driver::schedule_wake(self.expires_at.as_ticks(), cx.waker()); + embassy_time_driver::schedule_wake(self.expires_at.as_ticks(), cx.waker()); self.yielded_once = true; Poll::Pending } @@ -238,7 +238,7 @@ impl Ticker { self.expires_at += dur; Poll::Ready(()) } else { - embassy_time_queue_driver::schedule_wake(self.expires_at.as_ticks(), cx.waker()); + embassy_time_driver::schedule_wake(self.expires_at.as_ticks(), cx.waker()); Poll::Pending } }) @@ -255,7 +255,7 @@ impl Stream for Ticker { self.expires_at += dur; Poll::Ready(Some(())) } else { - embassy_time_queue_driver::schedule_wake(self.expires_at.as_ticks(), cx.waker()); + embassy_time_driver::schedule_wake(self.expires_at.as_ticks(), cx.waker()); Poll::Pending } } From c9f32b7e3667f29c4ab15d4dbab37acdb471d0ed Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?D=C3=A1niel=20Buga?= Date: Mon, 16 Dec 2024 18:51:09 +0100 Subject: [PATCH 343/361] Attach payload to TimerQueueItem --- embassy-executor/Cargo.toml | 16 +++++++++ embassy-executor/src/raw/timer_queue.rs | 45 +++++++++++++++++++++++++ 2 files changed, 61 insertions(+) diff --git a/embassy-executor/Cargo.toml b/embassy-executor/Cargo.toml index 60fe7087a..2a64b9c83 100644 --- a/embassy-executor/Cargo.toml +++ b/embassy-executor/Cargo.toml @@ -92,6 +92,22 @@ trace = [] ## Enable support for rtos-trace framework rtos-trace = ["dep:rtos-trace", "trace", "dep:embassy-time-driver"] +#! ### Timer Item Payload Size +#! Sets the size of the payload for timer items, allowing integrated timer implementors to store +#! additional data in the timer item. The payload field will be aligned to this value as well. +#! If these features are not defined, the timer item will contain no payload field. + +_timer-item-payload = [] # A size was picked + +## 1 bytes +timer-item-payload-size-1 = ["_timer-item-payload"] +## 2 bytes +timer-item-payload-size-2 = ["_timer-item-payload"] +## 4 bytes +timer-item-payload-size-4 = ["_timer-item-payload"] +## 8 bytes +timer-item-payload-size-8 = ["_timer-item-payload"] + #! ### Task Arena Size #! Sets the [task arena](#task-arena) size. Necessary if you’re not using `nightly`. #! diff --git a/embassy-executor/src/raw/timer_queue.rs b/embassy-executor/src/raw/timer_queue.rs index 2ba0e00a9..c4dba18ff 100644 --- a/embassy-executor/src/raw/timer_queue.rs +++ b/embassy-executor/src/raw/timer_queue.rs @@ -4,6 +4,45 @@ use core::cell::Cell; use super::TaskRef; +#[cfg(feature = "_timer-item-payload")] +macro_rules! define_opaque { + ($size:tt) => { + /// An opaque data type. + #[repr(align($size))] + pub struct OpaqueData { + data: [u8; $size], + } + + impl OpaqueData { + const fn new() -> Self { + Self { data: [0; $size] } + } + + /// Access the data as a reference to a type `T`. + /// + /// Safety: + /// + /// The caller must ensure that the size of the type `T` is less than, or equal to + /// the size of the payload, and must ensure that the alignment of the type `T` is + /// less than, or equal to the alignment of the payload. + /// + /// The type must be valid when zero-initialized. + pub unsafe fn as_ref(&self) -> &T { + &*(self.data.as_ptr() as *const T) + } + } + }; +} + +#[cfg(feature = "timer-item-payload-size-1")] +define_opaque!(1); +#[cfg(feature = "timer-item-payload-size-2")] +define_opaque!(2); +#[cfg(feature = "timer-item-payload-size-4")] +define_opaque!(4); +#[cfg(feature = "timer-item-payload-size-8")] +define_opaque!(8); + /// An item in the timer queue. pub struct TimerQueueItem { /// The next item in the queue. @@ -14,6 +53,10 @@ pub struct TimerQueueItem { /// The time at which this item expires. pub expires_at: Cell, + + /// Some implementation-defined, zero-initialized piece of data. + #[cfg(feature = "_timer-item-payload")] + pub payload: OpaqueData, } unsafe impl Sync for TimerQueueItem {} @@ -23,6 +66,8 @@ impl TimerQueueItem { Self { next: Cell::new(None), expires_at: Cell::new(0), + #[cfg(feature = "_timer-item-payload")] + payload: OpaqueData::new(), } } } From fbd0fe06bde7949d3374d10e540291493e088314 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?D=C3=A1niel=20Buga?= Date: Mon, 16 Dec 2024 18:56:42 +0100 Subject: [PATCH 344/361] Remove special handling of integrated timer items --- embassy-executor/src/raw/mod.rs | 23 ------------------- embassy-executor/src/raw/timer_queue.rs | 11 --------- .../src/queue_integrated.rs | 1 - 3 files changed, 35 deletions(-) diff --git a/embassy-executor/src/raw/mod.rs b/embassy-executor/src/raw/mod.rs index 997db6756..bdd5ff5ae 100644 --- a/embassy-executor/src/raw/mod.rs +++ b/embassy-executor/src/raw/mod.rs @@ -96,29 +96,6 @@ impl TaskRef { &self.header().timer_queue_item } - /// Mark the task as timer-queued. Return whether it should be actually enqueued - /// using `_embassy_time_schedule_wake`. - /// - /// Entering this state prevents the task from being respawned while in a timer queue. - /// - /// Safety: - /// - /// This functions should only be called by the timer queue driver, before - /// enqueueing the timer item. - pub unsafe fn timer_enqueue(&self) -> timer_queue::TimerEnqueueOperation { - self.header().state.timer_enqueue() - } - - /// Unmark the task as timer-queued. - /// - /// Safety: - /// - /// This functions should only be called by the timer queue implementation, after the task has - /// been removed from the timer queue. - pub unsafe fn timer_dequeue(&self) { - self.header().state.timer_dequeue() - } - /// The returned pointer is valid for the entire TaskStorage. pub(crate) fn as_ptr(self) -> *const TaskHeader { self.ptr.as_ptr() diff --git a/embassy-executor/src/raw/timer_queue.rs b/embassy-executor/src/raw/timer_queue.rs index c4dba18ff..e52453be4 100644 --- a/embassy-executor/src/raw/timer_queue.rs +++ b/embassy-executor/src/raw/timer_queue.rs @@ -71,14 +71,3 @@ impl TimerQueueItem { } } } - -/// The operation to perform after `timer_enqueue` is called. -#[derive(Debug, Copy, Clone, PartialEq)] -#[cfg_attr(feature = "defmt", derive(defmt::Format))] -#[must_use] -pub enum TimerEnqueueOperation { - /// Enqueue the task (or update its expiration time). - Enqueue, - /// The task must not be enqueued in the timer queue. - Ignore, -} diff --git a/embassy-time-queue-driver/src/queue_integrated.rs b/embassy-time-queue-driver/src/queue_integrated.rs index 6bb4c0c1a..246cf1d63 100644 --- a/embassy-time-queue-driver/src/queue_integrated.rs +++ b/embassy-time-queue-driver/src/queue_integrated.rs @@ -83,7 +83,6 @@ impl Queue { // Remove it prev.set(item.next.get()); item.next.set(None); - unsafe { p.timer_dequeue() }; } } } From 71812d0c9ba3c0c9f8689299f825ebc1318db938 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?D=C3=A1niel=20Buga?= Date: Mon, 16 Dec 2024 18:59:00 +0100 Subject: [PATCH 345/361] Changelog --- embassy-executor/CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/embassy-executor/CHANGELOG.md b/embassy-executor/CHANGELOG.md index 068156210..59ea88d0c 100644 --- a/embassy-executor/CHANGELOG.md +++ b/embassy-executor/CHANGELOG.md @@ -10,6 +10,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - embassy-executor no longer provides an `embassy-time-queue-driver` implementation - Added `TaskRef::executor` to obtain a reference to a task's executor - integrated-timers are no longer processed when polling the executor. +- Added the option to store data in timer queue items ## 0.6.3 - 2024-11-12 From a8617429e4c3177a3e7c31b20ee36a2dfe6b6430 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?D=C3=A1niel=20Buga?= Date: Mon, 16 Dec 2024 19:00:01 +0100 Subject: [PATCH 346/361] Add note --- embassy-time-driver/src/lib.rs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/embassy-time-driver/src/lib.rs b/embassy-time-driver/src/lib.rs index c776fbdf8..9f2795a01 100644 --- a/embassy-time-driver/src/lib.rs +++ b/embassy-time-driver/src/lib.rs @@ -46,6 +46,9 @@ //! //! Then, you'll need to adapt the `schedule_wake` method to use this queue. //! +//! Note that if you are using multiple queues, you will need to ensure that a single timer +//! queue item is only ever enqueued into a single queue at a time. +//! //! ```ignore //! use core::cell::RefCell; //! use core::task::Waker; From c90d048ecb611908f5696b4f57d689bdb254aee6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?D=C3=A1niel=20Buga?= Date: Mon, 16 Dec 2024 19:03:00 +0100 Subject: [PATCH 347/361] Remove TIMER_QUEUED --- embassy-executor/src/raw/state_atomics.rs | 32 ----------------- embassy-executor/src/raw/state_atomics_arm.rs | 36 ++----------------- .../src/raw/state_critical_section.rs | 25 ------------- 3 files changed, 2 insertions(+), 91 deletions(-) diff --git a/embassy-executor/src/raw/state_atomics.rs b/embassy-executor/src/raw/state_atomics.rs index d7350464f..6f5266bda 100644 --- a/embassy-executor/src/raw/state_atomics.rs +++ b/embassy-executor/src/raw/state_atomics.rs @@ -1,7 +1,5 @@ use core::sync::atomic::{AtomicU32, Ordering}; -use super::timer_queue::TimerEnqueueOperation; - #[derive(Clone, Copy)] pub(crate) struct Token(()); @@ -16,8 +14,6 @@ pub(crate) fn locked(f: impl FnOnce(Token) -> R) -> R { pub(crate) const STATE_SPAWNED: u32 = 1 << 0; /// Task is in the executor run queue pub(crate) const STATE_RUN_QUEUED: u32 = 1 << 1; -/// Task is in the executor timer queue -pub(crate) const STATE_TIMER_QUEUED: u32 = 1 << 2; pub(crate) struct State { state: AtomicU32, @@ -71,32 +67,4 @@ impl State { let state = self.state.fetch_and(!STATE_RUN_QUEUED, Ordering::AcqRel); state & STATE_SPAWNED != 0 } - - /// Mark the task as timer-queued. Return whether it can be enqueued. - #[inline(always)] - pub fn timer_enqueue(&self) -> TimerEnqueueOperation { - if self - .state - .fetch_update(Ordering::SeqCst, Ordering::SeqCst, |state| { - // If not started, ignore it - if state & STATE_SPAWNED == 0 { - None - } else { - // Mark it as enqueued - Some(state | STATE_TIMER_QUEUED) - } - }) - .is_ok() - { - TimerEnqueueOperation::Enqueue - } else { - TimerEnqueueOperation::Ignore - } - } - - /// Unmark the task as timer-queued. - #[inline(always)] - pub fn timer_dequeue(&self) { - self.state.fetch_and(!STATE_TIMER_QUEUED, Ordering::Relaxed); - } } diff --git a/embassy-executor/src/raw/state_atomics_arm.rs b/embassy-executor/src/raw/state_atomics_arm.rs index c1e8f69ab..4896b33c5 100644 --- a/embassy-executor/src/raw/state_atomics_arm.rs +++ b/embassy-executor/src/raw/state_atomics_arm.rs @@ -1,8 +1,6 @@ use core::arch::asm; use core::sync::atomic::{compiler_fence, AtomicBool, AtomicU32, Ordering}; -use super::timer_queue::TimerEnqueueOperation; - #[derive(Clone, Copy)] pub(crate) struct Token(()); @@ -16,7 +14,6 @@ pub(crate) fn locked(f: impl FnOnce(Token) -> R) -> R { // Must be kept in sync with the layout of `State`! pub(crate) const STATE_SPAWNED: u32 = 1 << 0; pub(crate) const STATE_RUN_QUEUED: u32 = 1 << 8; -pub(crate) const STATE_TIMER_QUEUED: u32 = 1 << 16; #[repr(C, align(4))] pub(crate) struct State { @@ -24,9 +21,8 @@ pub(crate) struct State { spawned: AtomicBool, /// Task is in the executor run queue run_queued: AtomicBool, - /// Task is in the executor timer queue - timer_queued: AtomicBool, pad: AtomicBool, + pad2: AtomicBool, } impl State { @@ -34,8 +30,8 @@ impl State { Self { spawned: AtomicBool::new(false), run_queued: AtomicBool::new(false), - timer_queued: AtomicBool::new(false), pad: AtomicBool::new(false), + pad2: AtomicBool::new(false), } } @@ -101,32 +97,4 @@ impl State { self.run_queued.store(false, Ordering::Relaxed); r } - - /// Mark the task as timer-queued. Return whether it can be enqueued. - #[inline(always)] - pub fn timer_enqueue(&self) -> TimerEnqueueOperation { - if self - .as_u32() - .fetch_update(Ordering::SeqCst, Ordering::SeqCst, |state| { - // If not started, ignore it - if state & STATE_SPAWNED == 0 { - None - } else { - // Mark it as enqueued - Some(state | STATE_TIMER_QUEUED) - } - }) - .is_ok() - { - TimerEnqueueOperation::Enqueue - } else { - TimerEnqueueOperation::Ignore - } - } - - /// Unmark the task as timer-queued. - #[inline(always)] - pub fn timer_dequeue(&self) { - self.timer_queued.store(false, Ordering::Relaxed); - } } diff --git a/embassy-executor/src/raw/state_critical_section.rs b/embassy-executor/src/raw/state_critical_section.rs index 8e570b33c..29b10f6e3 100644 --- a/embassy-executor/src/raw/state_critical_section.rs +++ b/embassy-executor/src/raw/state_critical_section.rs @@ -3,14 +3,10 @@ use core::cell::Cell; pub(crate) use critical_section::{with as locked, CriticalSection as Token}; use critical_section::{CriticalSection, Mutex}; -use super::timer_queue::TimerEnqueueOperation; - /// Task is spawned (has a future) pub(crate) const STATE_SPAWNED: u32 = 1 << 0; /// Task is in the executor run queue pub(crate) const STATE_RUN_QUEUED: u32 = 1 << 1; -/// Task is in the executor timer queue -pub(crate) const STATE_TIMER_QUEUED: u32 = 1 << 2; pub(crate) struct State { state: Mutex>, @@ -81,25 +77,4 @@ impl State { ok }) } - - /// Mark the task as timer-queued. Return whether it can be enqueued. - #[inline(always)] - pub fn timer_enqueue(&self) -> TimerEnqueueOperation { - self.update(|s| { - // FIXME: we need to split SPAWNED into two phases, to prevent enqueueing a task that is - // just being spawned, because its executor pointer may still be changing. - if *s & STATE_SPAWNED == STATE_SPAWNED { - *s |= STATE_TIMER_QUEUED; - TimerEnqueueOperation::Enqueue - } else { - TimerEnqueueOperation::Ignore - } - }) - } - - /// Unmark the task as timer-queued. - #[inline(always)] - pub fn timer_dequeue(&self) { - self.update(|s| *s &= !STATE_TIMER_QUEUED); - } } From c3c571e01ef902ccd2c3b176a4bc6ee3547d5da7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?D=C3=A1niel=20Buga?= Date: Mon, 16 Dec 2024 20:17:43 +0100 Subject: [PATCH 348/361] Remove test implementation of schedule_wake --- embassy-executor/tests/test.rs | 4 ---- 1 file changed, 4 deletions(-) diff --git a/embassy-executor/tests/test.rs b/embassy-executor/tests/test.rs index 992ab3da9..0ce1f1891 100644 --- a/embassy-executor/tests/test.rs +++ b/embassy-executor/tests/test.rs @@ -150,7 +150,3 @@ fn executor_task_cfg_args() { let (_, _, _) = (a, b, c); } } - -// We need this for the test to compile, even though we don't want to use timers at the moment. -#[no_mangle] -fn _embassy_time_schedule_wake(_at: u64, _waker: &core::task::Waker) {} From fc25fca00b48630073575d14bcc713912d0b0104 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Tue, 17 Dec 2024 13:06:31 +0100 Subject: [PATCH 349/361] Remove WakerHack for good. Now that 1.83 xtensa is out, we can remove it unconditionally. --- embassy-executor/src/raw/waker.rs | 15 ++------------- 1 file changed, 2 insertions(+), 13 deletions(-) diff --git a/embassy-executor/src/raw/waker.rs b/embassy-executor/src/raw/waker.rs index 9c70f995a..b7d57c314 100644 --- a/embassy-executor/src/raw/waker.rs +++ b/embassy-executor/src/raw/waker.rs @@ -32,22 +32,11 @@ pub(crate) unsafe fn from_task(p: TaskRef) -> Waker { /// /// Panics if the waker is not created by the Embassy executor. pub fn task_from_waker(waker: &Waker) -> TaskRef { - struct WakerHack { - data: *const (), - vtable: &'static RawWakerVTable, - } - - // safety: OK because WakerHack has the same layout as Waker. - // This is not really guaranteed because the structs are `repr(Rust)`, it is - // indeed the case in the current implementation. - // TODO use waker_getters when stable. https://github.com/rust-lang/rust/issues/96992 - let hack: &WakerHack = unsafe { core::mem::transmute(waker) }; - // make sure to compare vtable addresses. Doing `==` on the references // will compare the contents, which is slower. - if hack.vtable as *const _ != &VTABLE as *const _ { + if waker.vtable() as *const _ != &VTABLE as *const _ { panic!("Found waker not created by the Embassy executor. `embassy_time::Timer` only works with the Embassy executor.") } // safety: our wakers are always created with `TaskRef::as_ptr` - unsafe { TaskRef::from_ptr(hack.data as *const TaskHeader) } + unsafe { TaskRef::from_ptr(waker.data() as *const TaskHeader) } } From 7eac184af0b6bf88c43158b9a791d7c169d5bb3c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?D=C3=A1niel=20Buga?= Date: Mon, 9 Dec 2024 17:11:47 +0100 Subject: [PATCH 350/361] Document task states and state transitions --- embassy-executor/src/raw/mod.rs | 38 +++++++++++++++++++++++++++++++++ 1 file changed, 38 insertions(+) diff --git a/embassy-executor/src/raw/mod.rs b/embassy-executor/src/raw/mod.rs index bdd5ff5ae..0ac569946 100644 --- a/embassy-executor/src/raw/mod.rs +++ b/embassy-executor/src/raw/mod.rs @@ -38,6 +38,44 @@ pub use self::waker::task_from_waker; use super::SpawnToken; /// Raw task header for use in task pointers. +/// +/// A task can be in one of the following states: +/// +/// - Not spawned: the task is ready to spawn. +/// - `SPAWNED`: the task is currently spawned and may be running. +/// - `RUN_ENQUEUED`: the task is enqueued to be polled. Note that the task may be `!SPAWNED`. +/// In this case, the `RUN_ENQUEUED` state will be cleared when the task is next polled, without +/// polling the task's future. +/// +/// A task's complete life cycle is as follows: +/// +/// ```text +/// ┌────────────┐ ┌────────────────────────┐ +/// ┌─►│Not spawned │◄─6┤Not spawned|Run enqueued│ +/// │ │ │ │ │ +/// │ └─────┬──────┘ └──────▲─────────────────┘ +/// │ 1 │ +/// │ │ ┌────────────┘ +/// │ │ 5 +/// │ ┌─────▼────┴─────────┐ +/// │ │Spawned|Run enqueued│ +/// │ │ │ +/// │ └─────┬▲─────────────┘ +/// │ 2│ +/// │ │3 +/// │ ┌─────▼┴─────┐ +/// └─4┤ Spawned │ +/// │ │ +/// └────────────┘ +/// ``` +/// +/// Transitions: +/// - 1: Task is spawned - `AvailableTask::claim -> Executor::spawn` +/// - 2: During poll - `RunQueue::dequeue_all -> State::run_dequeue` +/// - 3: Task wakes itself, waker wakes task - `Waker::wake -> wake_task -> State::run_enqueue` +/// - 4: Task exits - `TaskStorage::poll -> Poll::Ready` +/// - 5: A run-queued task exits - `TaskStorage::poll -> Poll::Ready` +/// - 6: Task is dequeued and then ignored via `State::run_dequeue` pub(crate) struct TaskHeader { pub(crate) state: State, pub(crate) run_queue_item: RunQueueItem, From c6ca46c82529e014aaceb218ad88978c50f0db07 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?D=C3=A1niel=20Buga?= Date: Tue, 26 Nov 2024 00:20:34 +0100 Subject: [PATCH 351/361] Set RUN_QUEUED unconditionally --- embassy-executor/Cargo.toml | 1 + embassy-executor/src/raw/mod.rs | 3 +- embassy-executor/src/raw/state_atomics.rs | 15 +- embassy-executor/src/raw/state_atomics_arm.rs | 2 +- .../src/raw/state_critical_section.rs | 9 +- embassy-executor/tests/test.rs | 133 ++++++++++++++++++ 6 files changed, 142 insertions(+), 21 deletions(-) diff --git a/embassy-executor/Cargo.toml b/embassy-executor/Cargo.toml index 2a64b9c83..5effaca65 100644 --- a/embassy-executor/Cargo.toml +++ b/embassy-executor/Cargo.toml @@ -55,6 +55,7 @@ avr-device = { version = "0.5.3", optional = true } [dev-dependencies] critical-section = { version = "1.1", features = ["std"] } trybuild = "1.0" +embassy-sync = { path = "../embassy-sync" } [features] diff --git a/embassy-executor/src/raw/mod.rs b/embassy-executor/src/raw/mod.rs index 0ac569946..6503b556f 100644 --- a/embassy-executor/src/raw/mod.rs +++ b/embassy-executor/src/raw/mod.rs @@ -52,7 +52,7 @@ use super::SpawnToken; /// ```text /// ┌────────────┐ ┌────────────────────────┐ /// ┌─►│Not spawned │◄─6┤Not spawned|Run enqueued│ -/// │ │ │ │ │ +/// │ │ ├7─►│ │ /// │ └─────┬──────┘ └──────▲─────────────────┘ /// │ 1 │ /// │ │ ┌────────────┘ @@ -76,6 +76,7 @@ use super::SpawnToken; /// - 4: Task exits - `TaskStorage::poll -> Poll::Ready` /// - 5: A run-queued task exits - `TaskStorage::poll -> Poll::Ready` /// - 6: Task is dequeued and then ignored via `State::run_dequeue` +/// - 7: A task is waken when it is not spawned - `wake_task -> State::run_enqueue` pub(crate) struct TaskHeader { pub(crate) state: State, pub(crate) run_queue_item: RunQueueItem, diff --git a/embassy-executor/src/raw/state_atomics.rs b/embassy-executor/src/raw/state_atomics.rs index 6f5266bda..bdd317b53 100644 --- a/embassy-executor/src/raw/state_atomics.rs +++ b/embassy-executor/src/raw/state_atomics.rs @@ -44,19 +44,8 @@ impl State { /// function if the task was successfully marked. #[inline(always)] pub fn run_enqueue(&self, f: impl FnOnce(Token)) { - if self - .state - .fetch_update(Ordering::SeqCst, Ordering::SeqCst, |state| { - // If already scheduled, or if not started, - if (state & STATE_RUN_QUEUED != 0) || (state & STATE_SPAWNED == 0) { - None - } else { - // Mark it as scheduled - Some(state | STATE_RUN_QUEUED) - } - }) - .is_ok() - { + let prev = self.state.fetch_or(STATE_RUN_QUEUED, Ordering::AcqRel); + if prev & STATE_RUN_QUEUED == 0 { locked(f); } } diff --git a/embassy-executor/src/raw/state_atomics_arm.rs b/embassy-executor/src/raw/state_atomics_arm.rs index 4896b33c5..06bf24343 100644 --- a/embassy-executor/src/raw/state_atomics_arm.rs +++ b/embassy-executor/src/raw/state_atomics_arm.rs @@ -72,7 +72,7 @@ impl State { let state: u32; asm!("ldrex {}, [{}]", out(reg) state, in(reg) self, options(nostack)); - if (state & STATE_RUN_QUEUED != 0) || (state & STATE_SPAWNED == 0) { + if state & STATE_RUN_QUEUED != 0 { asm!("clrex", options(nomem, nostack)); return; } diff --git a/embassy-executor/src/raw/state_critical_section.rs b/embassy-executor/src/raw/state_critical_section.rs index 29b10f6e3..4733af278 100644 --- a/embassy-executor/src/raw/state_critical_section.rs +++ b/embassy-executor/src/raw/state_critical_section.rs @@ -56,12 +56,9 @@ impl State { pub fn run_enqueue(&self, f: impl FnOnce(Token)) { critical_section::with(|cs| { if self.update_with_cs(cs, |s| { - if (*s & STATE_RUN_QUEUED != 0) || (*s & STATE_SPAWNED == 0) { - false - } else { - *s |= STATE_RUN_QUEUED; - true - } + let ok = *s & STATE_RUN_QUEUED == 0; + *s |= STATE_RUN_QUEUED; + ok }) { f(cs); } diff --git a/embassy-executor/tests/test.rs b/embassy-executor/tests/test.rs index 0ce1f1891..78c49c071 100644 --- a/embassy-executor/tests/test.rs +++ b/embassy-executor/tests/test.rs @@ -137,6 +137,139 @@ fn executor_task_self_wake_twice() { ) } +#[test] +fn waking_after_completion_does_not_poll() { + use embassy_sync::waitqueue::AtomicWaker; + + #[task] + async fn task1(trace: Trace, waker: &'static AtomicWaker) { + poll_fn(|cx| { + trace.push("poll task1"); + waker.register(cx.waker()); + Poll::Ready(()) + }) + .await + } + + let waker = Box::leak(Box::new(AtomicWaker::new())); + + let (executor, trace) = setup(); + executor.spawner().spawn(task1(trace.clone(), waker)).unwrap(); + + unsafe { executor.poll() }; + waker.wake(); + unsafe { executor.poll() }; + + // Exited task may be waken but is not polled + waker.wake(); + waker.wake(); + unsafe { executor.poll() }; // Clears running status + + // Can respawn waken-but-dead task + executor.spawner().spawn(task1(trace.clone(), waker)).unwrap(); + + unsafe { executor.poll() }; + + assert_eq!( + trace.get(), + &[ + "pend", // spawning a task pends the executor + "poll task1", // + "pend", // manual wake, gets cleared by poll + "pend", // manual wake, single pend for two wakes + "pend", // respawning a task pends the executor + "poll task1", // + ] + ) +} + +#[test] +fn waking_with_old_waker_after_respawn() { + use embassy_sync::waitqueue::AtomicWaker; + + async fn yield_now(trace: Trace) { + let mut yielded = false; + poll_fn(|cx| { + if yielded { + Poll::Ready(()) + } else { + trace.push("yield_now"); + yielded = true; + cx.waker().wake_by_ref(); + Poll::Pending + } + }) + .await + } + + #[task] + async fn task1(trace: Trace, waker: &'static AtomicWaker) { + yield_now(trace.clone()).await; + poll_fn(|cx| { + trace.push("poll task1"); + waker.register(cx.waker()); + Poll::Ready(()) + }) + .await; + } + + let waker = Box::leak(Box::new(AtomicWaker::new())); + + let (executor, trace) = setup(); + executor.spawner().spawn(task1(trace.clone(), waker)).unwrap(); + + unsafe { executor.poll() }; + unsafe { executor.poll() }; // progress to registering the waker + waker.wake(); + unsafe { executor.poll() }; + // Task has exited + + assert_eq!( + trace.get(), + &[ + "pend", // spawning a task pends the executor + "yield_now", // + "pend", // yield_now wakes the task + "poll task1", // + "pend", // task self-wakes + ] + ); + + // Can respawn task on another executor + let (other_executor, other_trace) = setup(); + other_executor + .spawner() + .spawn(task1(other_trace.clone(), waker)) + .unwrap(); + + unsafe { other_executor.poll() }; // just run to the yield_now + waker.wake(); // trigger old waker registration + unsafe { executor.poll() }; + unsafe { other_executor.poll() }; + + // First executor's trace has not changed + assert_eq!( + trace.get(), + &[ + "pend", // spawning a task pends the executor + "yield_now", // + "pend", // yield_now wakes the task + "poll task1", // + "pend", // task self-wakes + ] + ); + + assert_eq!( + other_trace.get(), + &[ + "pend", // spawning a task pends the executor + "yield_now", // + "pend", // manual wake, gets cleared by poll + "poll task1", // + ] + ); +} + #[test] fn executor_task_cfg_args() { // simulate cfg'ing away argument c From 889b419fc40f252726dbdc8a67bc4d27aa5b81f3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?D=C3=A1niel=20Buga?= Date: Tue, 17 Dec 2024 17:14:59 +0100 Subject: [PATCH 352/361] Simplify ARM run_enqueue --- embassy-executor/src/raw/state_atomics_arm.rs | 21 +++---------------- 1 file changed, 3 insertions(+), 18 deletions(-) diff --git a/embassy-executor/src/raw/state_atomics_arm.rs b/embassy-executor/src/raw/state_atomics_arm.rs index 06bf24343..cbda0d89d 100644 --- a/embassy-executor/src/raw/state_atomics_arm.rs +++ b/embassy-executor/src/raw/state_atomics_arm.rs @@ -1,4 +1,3 @@ -use core::arch::asm; use core::sync::atomic::{compiler_fence, AtomicBool, AtomicU32, Ordering}; #[derive(Clone, Copy)] @@ -67,24 +66,10 @@ impl State { /// function if the task was successfully marked. #[inline(always)] pub fn run_enqueue(&self, f: impl FnOnce(Token)) { - unsafe { - loop { - let state: u32; - asm!("ldrex {}, [{}]", out(reg) state, in(reg) self, options(nostack)); + let old = self.run_queued.swap(true, Ordering::AcqRel); - if state & STATE_RUN_QUEUED != 0 { - asm!("clrex", options(nomem, nostack)); - return; - } - - let outcome: usize; - let new_state = state | STATE_RUN_QUEUED; - asm!("strex {}, {}, [{}]", out(reg) outcome, in(reg) new_state, in(reg) self, options(nostack)); - if outcome == 0 { - locked(f); - return; - } - } + if !old { + locked(f); } } From edb8f21a741358f7c80b744f008f1e5acc77b429 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?D=C3=A1niel=20Buga?= Date: Sat, 7 Dec 2024 14:45:16 +0100 Subject: [PATCH 353/361] Take critical section instead of unsafe --- embassy-executor/src/raw/mod.rs | 9 --------- embassy-executor/src/raw/run_queue_atomics.rs | 11 ++++++++++- .../src/raw/run_queue_critical_section.rs | 16 ++++++++++++---- 3 files changed, 22 insertions(+), 14 deletions(-) diff --git a/embassy-executor/src/raw/mod.rs b/embassy-executor/src/raw/mod.rs index 6503b556f..c79fdae60 100644 --- a/embassy-executor/src/raw/mod.rs +++ b/embassy-executor/src/raw/mod.rs @@ -411,15 +411,6 @@ impl SyncExecutor { self.run_queue.dequeue_all(|p| { let task = p.header(); - if !task.state.run_dequeue() { - // If task is not running, ignore it. This can happen in the following scenario: - // - Task gets dequeued, poll starts - // - While task is being polled, it gets woken. It gets placed in the queue. - // - Task poll finishes, returning done=true - // - RUNNING bit is cleared, but the task is already in the queue. - return; - } - #[cfg(feature = "trace")] trace::task_exec_begin(self, &p); diff --git a/embassy-executor/src/raw/run_queue_atomics.rs b/embassy-executor/src/raw/run_queue_atomics.rs index efdafdff0..aad90d767 100644 --- a/embassy-executor/src/raw/run_queue_atomics.rs +++ b/embassy-executor/src/raw/run_queue_atomics.rs @@ -81,7 +81,16 @@ impl RunQueue { // safety: there are no concurrent accesses to `next` next = unsafe { task.header().run_queue_item.next.get() }; - on_task(task); + let run_task = task.header().state.run_dequeue(); + + if run_task { + // If task is not running, ignore it. This can happen in the following scenario: + // - Task gets dequeued, poll starts + // - While task is being polled, it gets woken. It gets placed in the queue. + // - Task poll finishes, returning done=true + // - RUNNING bit is cleared, but the task is already in the queue. + on_task(task); + } } } } diff --git a/embassy-executor/src/raw/run_queue_critical_section.rs b/embassy-executor/src/raw/run_queue_critical_section.rs index 90f09e8c8..4f1b2855a 100644 --- a/embassy-executor/src/raw/run_queue_critical_section.rs +++ b/embassy-executor/src/raw/run_queue_critical_section.rs @@ -63,11 +63,19 @@ impl RunQueue { // If the task re-enqueues itself, the `next` pointer will get overwritten. // Therefore, first read the next pointer, and only then process the task. - // safety: we know if the task is enqueued, no one else will touch the `next` pointer. - let cs = unsafe { CriticalSection::new() }; - next = task.header().run_queue_item.next.borrow(cs).get(); + let run_task = critical_section::with(|cs| { + next = task.header().run_queue_item.next.borrow(cs).get(); + task.header().state.run_dequeue(cs) + }); - on_task(task); + if run_task { + // If task is not running, ignore it. This can happen in the following scenario: + // - Task gets dequeued, poll starts + // - While task is being polled, it gets woken. It gets placed in the queue. + // - Task poll finishes, returning done=true + // - RUNNING bit is cleared, but the task is already in the queue. + on_task(task); + } } } } From 8fd08b1e97533c7526bb4937770060d18bb37410 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?D=C3=A1niel=20Buga?= Date: Tue, 17 Dec 2024 18:05:48 +0100 Subject: [PATCH 354/361] Swap poll_fn to allow polling exited tasks --- embassy-executor/src/raw/mod.rs | 17 +++++++++++++++-- embassy-executor/src/raw/run_queue_atomics.rs | 12 ++---------- .../src/raw/run_queue_critical_section.rs | 13 +++---------- embassy-executor/src/raw/state_atomics.rs | 5 ++--- embassy-executor/src/raw/state_atomics_arm.rs | 4 +--- .../src/raw/state_critical_section.rs | 8 ++------ 6 files changed, 25 insertions(+), 34 deletions(-) diff --git a/embassy-executor/src/raw/mod.rs b/embassy-executor/src/raw/mod.rs index c79fdae60..242e9c365 100644 --- a/embassy-executor/src/raw/mod.rs +++ b/embassy-executor/src/raw/mod.rs @@ -202,16 +202,29 @@ impl TaskStorage { } } + unsafe fn poll_to_despawn(p: TaskRef) { + // The task's future has already been dropped, we just mark it as `!SPAWNED`. + let this = &*p.as_ptr().cast::>(); + this.raw.state.despawn(); + } + unsafe fn poll(p: TaskRef) { - let this = &*(p.as_ptr() as *const TaskStorage); + let this = &*p.as_ptr().cast::>(); let future = Pin::new_unchecked(this.future.as_mut()); let waker = waker::from_task(p); let mut cx = Context::from_waker(&waker); match future.poll(&mut cx) { Poll::Ready(_) => { + waker.wake_by_ref(); + + // As the future has finished and this function will not be called + // again, we can safely drop the future here. this.future.drop_in_place(); - this.raw.state.despawn(); + + // We replace the poll_fn with a despawn function, so that the task is cleaned up + // when the executor polls it next. + this.raw.poll_fn.set(Some(Self::poll_to_despawn)); } Poll::Pending => {} } diff --git a/embassy-executor/src/raw/run_queue_atomics.rs b/embassy-executor/src/raw/run_queue_atomics.rs index aad90d767..ce511d79a 100644 --- a/embassy-executor/src/raw/run_queue_atomics.rs +++ b/embassy-executor/src/raw/run_queue_atomics.rs @@ -81,16 +81,8 @@ impl RunQueue { // safety: there are no concurrent accesses to `next` next = unsafe { task.header().run_queue_item.next.get() }; - let run_task = task.header().state.run_dequeue(); - - if run_task { - // If task is not running, ignore it. This can happen in the following scenario: - // - Task gets dequeued, poll starts - // - While task is being polled, it gets woken. It gets placed in the queue. - // - Task poll finishes, returning done=true - // - RUNNING bit is cleared, but the task is already in the queue. - on_task(task); - } + task.header().state.run_dequeue(); + on_task(task); } } } diff --git a/embassy-executor/src/raw/run_queue_critical_section.rs b/embassy-executor/src/raw/run_queue_critical_section.rs index 4f1b2855a..86c4085ed 100644 --- a/embassy-executor/src/raw/run_queue_critical_section.rs +++ b/embassy-executor/src/raw/run_queue_critical_section.rs @@ -63,19 +63,12 @@ impl RunQueue { // If the task re-enqueues itself, the `next` pointer will get overwritten. // Therefore, first read the next pointer, and only then process the task. - let run_task = critical_section::with(|cs| { + critical_section::with(|cs| { next = task.header().run_queue_item.next.borrow(cs).get(); - task.header().state.run_dequeue(cs) + task.header().state.run_dequeue(cs); }); - if run_task { - // If task is not running, ignore it. This can happen in the following scenario: - // - Task gets dequeued, poll starts - // - While task is being polled, it gets woken. It gets placed in the queue. - // - Task poll finishes, returning done=true - // - RUNNING bit is cleared, but the task is already in the queue. - on_task(task); - } + on_task(task); } } } diff --git a/embassy-executor/src/raw/state_atomics.rs b/embassy-executor/src/raw/state_atomics.rs index bdd317b53..b6576bfc2 100644 --- a/embassy-executor/src/raw/state_atomics.rs +++ b/embassy-executor/src/raw/state_atomics.rs @@ -52,8 +52,7 @@ impl State { /// Unmark the task as run-queued. Return whether the task is spawned. #[inline(always)] - pub fn run_dequeue(&self) -> bool { - let state = self.state.fetch_and(!STATE_RUN_QUEUED, Ordering::AcqRel); - state & STATE_SPAWNED != 0 + pub fn run_dequeue(&self) { + self.state.fetch_and(!STATE_RUN_QUEUED, Ordering::AcqRel); } } diff --git a/embassy-executor/src/raw/state_atomics_arm.rs b/embassy-executor/src/raw/state_atomics_arm.rs index cbda0d89d..b743dcc2c 100644 --- a/embassy-executor/src/raw/state_atomics_arm.rs +++ b/embassy-executor/src/raw/state_atomics_arm.rs @@ -75,11 +75,9 @@ impl State { /// Unmark the task as run-queued. Return whether the task is spawned. #[inline(always)] - pub fn run_dequeue(&self) -> bool { + pub fn run_dequeue(&self) { compiler_fence(Ordering::Release); - let r = self.spawned.load(Ordering::Relaxed); self.run_queued.store(false, Ordering::Relaxed); - r } } diff --git a/embassy-executor/src/raw/state_critical_section.rs b/embassy-executor/src/raw/state_critical_section.rs index 4733af278..6b627ff79 100644 --- a/embassy-executor/src/raw/state_critical_section.rs +++ b/embassy-executor/src/raw/state_critical_section.rs @@ -67,11 +67,7 @@ impl State { /// Unmark the task as run-queued. Return whether the task is spawned. #[inline(always)] - pub fn run_dequeue(&self) -> bool { - self.update(|s| { - let ok = *s & STATE_SPAWNED != 0; - *s &= !STATE_RUN_QUEUED; - ok - }) + pub fn run_dequeue(&self, cs: CriticalSection<'_>) { + self.update_with_cs(cs, |s| *s &= !STATE_RUN_QUEUED) } } From b51bd9ad040754728142df9991763b4672c31ecd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?D=C3=A1niel=20Buga?= Date: Tue, 17 Dec 2024 18:09:51 +0100 Subject: [PATCH 355/361] Update tests --- embassy-executor/tests/test.rs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/embassy-executor/tests/test.rs b/embassy-executor/tests/test.rs index 78c49c071..d8c5a6ae3 100644 --- a/embassy-executor/tests/test.rs +++ b/embassy-executor/tests/test.rs @@ -69,6 +69,7 @@ fn executor_task() { &[ "pend", // spawning a task pends the executor "poll task1", // poll only once. + "pend", // task is done, wakes itself to exit ] ) } @@ -179,6 +180,7 @@ fn waking_after_completion_does_not_poll() { "pend", // manual wake, single pend for two wakes "pend", // respawning a task pends the executor "poll task1", // + "pend", // task is done, wakes itself to exit ] ) } @@ -266,6 +268,7 @@ fn waking_with_old_waker_after_respawn() { "yield_now", // "pend", // manual wake, gets cleared by poll "poll task1", // + "pend", // task is done, wakes itself to exit ] ); } From 7d5fbe26c955bae4bd394d1092702bd81f849c9b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?D=C3=A1niel=20Buga?= Date: Tue, 17 Dec 2024 18:17:36 +0100 Subject: [PATCH 356/361] Update state diagram --- embassy-executor/src/raw/mod.rs | 43 ++++++++++++++++----------------- 1 file changed, 21 insertions(+), 22 deletions(-) diff --git a/embassy-executor/src/raw/mod.rs b/embassy-executor/src/raw/mod.rs index 242e9c365..5df5ca9e1 100644 --- a/embassy-executor/src/raw/mod.rs +++ b/embassy-executor/src/raw/mod.rs @@ -50,33 +50,32 @@ use super::SpawnToken; /// A task's complete life cycle is as follows: /// /// ```text -/// ┌────────────┐ ┌────────────────────────┐ -/// ┌─►│Not spawned │◄─6┤Not spawned|Run enqueued│ -/// │ │ ├7─►│ │ -/// │ └─────┬──────┘ └──────▲─────────────────┘ -/// │ 1 │ -/// │ │ ┌────────────┘ -/// │ │ 5 -/// │ ┌─────▼────┴─────────┐ -/// │ │Spawned|Run enqueued│ -/// │ │ │ -/// │ └─────┬▲─────────────┘ -/// │ 2│ -/// │ │3 -/// │ ┌─────▼┴─────┐ -/// └─4┤ Spawned │ -/// │ │ -/// └────────────┘ +/// ┌────────────┐ ┌────────────────────────┐ +/// │Not spawned │◄─5┤Not spawned|Run enqueued│ +/// │ ├6─►│ │ +/// └─────┬──────┘ └──────▲─────────────────┘ +/// 1 │ +/// │ ┌────────────┘ +/// │ 4 +/// ┌─────▼────┴─────────┐ +/// │Spawned|Run enqueued│ +/// │ │ +/// └─────┬▲─────────────┘ +/// 2│ +/// │3 +/// ┌─────▼┴─────┐ +/// │ Spawned │ +/// │ │ +/// └────────────┘ /// ``` /// /// Transitions: /// - 1: Task is spawned - `AvailableTask::claim -> Executor::spawn` /// - 2: During poll - `RunQueue::dequeue_all -> State::run_dequeue` -/// - 3: Task wakes itself, waker wakes task - `Waker::wake -> wake_task -> State::run_enqueue` -/// - 4: Task exits - `TaskStorage::poll -> Poll::Ready` -/// - 5: A run-queued task exits - `TaskStorage::poll -> Poll::Ready` -/// - 6: Task is dequeued and then ignored via `State::run_dequeue` -/// - 7: A task is waken when it is not spawned - `wake_task -> State::run_enqueue` +/// - 3: Task wakes itself, waker wakes task, or task exits - `Waker::wake -> wake_task -> State::run_enqueue` +/// - 4: A run-queued task exits - `TaskStorage::poll -> Poll::Ready` +/// - 5: Task is dequeued. The task's future is not polled, because exiting the task replaces its `poll_fn`. +/// - 6: A task is waken when it is not spawned - `wake_task -> State::run_enqueue` pub(crate) struct TaskHeader { pub(crate) state: State, pub(crate) run_queue_item: RunQueueItem, From a011f487690465f8ae64fd74f4c51a8be3979890 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?D=C3=A1niel=20Buga?= Date: Tue, 17 Dec 2024 18:37:17 +0100 Subject: [PATCH 357/361] Make poll_to_despawn non-generic --- embassy-executor/src/raw/mod.rs | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/embassy-executor/src/raw/mod.rs b/embassy-executor/src/raw/mod.rs index 5df5ca9e1..39d2d73ab 100644 --- a/embassy-executor/src/raw/mod.rs +++ b/embassy-executor/src/raw/mod.rs @@ -161,6 +161,12 @@ pub struct TaskStorage { future: UninitCell, // Valid if STATE_SPAWNED } +unsafe fn poll_to_despawn(p: TaskRef) { + // The task's future has already been dropped, we just mark it as `!SPAWNED`. + let this = p.header(); + this.state.despawn(); +} + impl TaskStorage { const NEW: Self = Self::new(); @@ -201,12 +207,6 @@ impl TaskStorage { } } - unsafe fn poll_to_despawn(p: TaskRef) { - // The task's future has already been dropped, we just mark it as `!SPAWNED`. - let this = &*p.as_ptr().cast::>(); - this.raw.state.despawn(); - } - unsafe fn poll(p: TaskRef) { let this = &*p.as_ptr().cast::>(); @@ -223,7 +223,7 @@ impl TaskStorage { // We replace the poll_fn with a despawn function, so that the task is cleaned up // when the executor polls it next. - this.raw.poll_fn.set(Some(Self::poll_to_despawn)); + this.raw.poll_fn.set(Some(poll_to_despawn)); } Poll::Pending => {} } From 2ca374fc9c0d0abe579716d1a7c2dc0724321ee7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?D=C3=A1niel=20Buga?= Date: Tue, 17 Dec 2024 18:46:32 +0100 Subject: [PATCH 358/361] Don't force a wake to despawn --- embassy-executor/src/raw/mod.rs | 6 ++++-- embassy-executor/tests/test.rs | 3 --- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/embassy-executor/src/raw/mod.rs b/embassy-executor/src/raw/mod.rs index 39d2d73ab..4a4ecf603 100644 --- a/embassy-executor/src/raw/mod.rs +++ b/embassy-executor/src/raw/mod.rs @@ -215,8 +215,6 @@ impl TaskStorage { let mut cx = Context::from_waker(&waker); match future.poll(&mut cx) { Poll::Ready(_) => { - waker.wake_by_ref(); - // As the future has finished and this function will not be called // again, we can safely drop the future here. this.future.drop_in_place(); @@ -224,6 +222,10 @@ impl TaskStorage { // We replace the poll_fn with a despawn function, so that the task is cleaned up // when the executor polls it next. this.raw.poll_fn.set(Some(poll_to_despawn)); + + // Make sure we despawn last, so that other threads can only spawn the task + // after we're done with it. + this.raw.state.despawn(); } Poll::Pending => {} } diff --git a/embassy-executor/tests/test.rs b/embassy-executor/tests/test.rs index d8c5a6ae3..78c49c071 100644 --- a/embassy-executor/tests/test.rs +++ b/embassy-executor/tests/test.rs @@ -69,7 +69,6 @@ fn executor_task() { &[ "pend", // spawning a task pends the executor "poll task1", // poll only once. - "pend", // task is done, wakes itself to exit ] ) } @@ -180,7 +179,6 @@ fn waking_after_completion_does_not_poll() { "pend", // manual wake, single pend for two wakes "pend", // respawning a task pends the executor "poll task1", // - "pend", // task is done, wakes itself to exit ] ) } @@ -268,7 +266,6 @@ fn waking_with_old_waker_after_respawn() { "yield_now", // "pend", // manual wake, gets cleared by poll "poll task1", // - "pend", // task is done, wakes itself to exit ] ); } From 76d8a896bbff612e3d2db27554891c71d28988af Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?D=C3=A1niel=20Buga?= Date: Tue, 17 Dec 2024 18:51:22 +0100 Subject: [PATCH 359/361] Make poll_to_despawn a no_op --- embassy-executor/src/raw/mod.rs | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/embassy-executor/src/raw/mod.rs b/embassy-executor/src/raw/mod.rs index 4a4ecf603..e38a2af66 100644 --- a/embassy-executor/src/raw/mod.rs +++ b/embassy-executor/src/raw/mod.rs @@ -161,10 +161,8 @@ pub struct TaskStorage { future: UninitCell, // Valid if STATE_SPAWNED } -unsafe fn poll_to_despawn(p: TaskRef) { - // The task's future has already been dropped, we just mark it as `!SPAWNED`. - let this = p.header(); - this.state.despawn(); +unsafe fn poll_exited(_p: TaskRef) { + // Nothing to do, the task is already !SPAWNED and dequeued. } impl TaskStorage { @@ -221,7 +219,7 @@ impl TaskStorage { // We replace the poll_fn with a despawn function, so that the task is cleaned up // when the executor polls it next. - this.raw.poll_fn.set(Some(poll_to_despawn)); + this.raw.poll_fn.set(Some(poll_exited)); // Make sure we despawn last, so that other threads can only spawn the task // after we're done with it. From b2e82684707e8675f61bba01e818947ba536d3c7 Mon Sep 17 00:00:00 2001 From: Piotr Esden-Tempski Date: Tue, 17 Dec 2024 14:45:52 -0800 Subject: [PATCH 360/361] stm32/(o|q)spi: command naming convention fix The naming convention is to prefix blocking functions with blocking. As both command implementations are blocking the async for the ospi implementation should also be dropped. --- embassy-stm32/src/ospi/mod.rs | 2 +- embassy-stm32/src/qspi/mod.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/embassy-stm32/src/ospi/mod.rs b/embassy-stm32/src/ospi/mod.rs index f8ef66216..38217a9a4 100644 --- a/embassy-stm32/src/ospi/mod.rs +++ b/embassy-stm32/src/ospi/mod.rs @@ -520,7 +520,7 @@ impl<'d, T: Instance, M: PeriMode> Ospi<'d, T, M> { } /// Function used to control or configure the target device without data transfer - pub async fn command(&mut self, command: &TransferConfig) -> Result<(), OspiError> { + pub fn blocking_command(&mut self, command: &TransferConfig) -> Result<(), OspiError> { // Wait for peripheral to be free while T::REGS.sr().read().busy() {} diff --git a/embassy-stm32/src/qspi/mod.rs b/embassy-stm32/src/qspi/mod.rs index 49836aa57..0c65d0556 100644 --- a/embassy-stm32/src/qspi/mod.rs +++ b/embassy-stm32/src/qspi/mod.rs @@ -148,7 +148,7 @@ impl<'d, T: Instance, M: PeriMode> Qspi<'d, T, M> { } /// Do a QSPI command. - pub fn command(&mut self, transaction: TransferConfig) { + pub fn blocking_command(&mut self, transaction: TransferConfig) { #[cfg(not(stm32h7))] T::REGS.cr().modify(|v| v.set_dmaen(false)); self.setup_transaction(QspiMode::IndirectWrite, &transaction, None); From bafcdedebe1b94a9eb35a397553ee9ecab237080 Mon Sep 17 00:00:00 2001 From: Piotr Esden-Tempski Date: Tue, 17 Dec 2024 14:56:45 -0800 Subject: [PATCH 361/361] Update (q|o)spi examples. --- examples/stm32f7/src/bin/qspi.rs | 4 ++-- examples/stm32h7b0/src/bin/ospi_memory_mapped.rs | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/examples/stm32f7/src/bin/qspi.rs b/examples/stm32f7/src/bin/qspi.rs index 90d319b7a..bd3287964 100644 --- a/examples/stm32f7/src/bin/qspi.rs +++ b/examples/stm32f7/src/bin/qspi.rs @@ -72,7 +72,7 @@ impl FlashMemory { address: None, dummy: DummyCycles::_0, }; - self.qspi.command(transaction); + self.qspi.blocking_command(transaction); } pub fn reset_memory(&mut self) { @@ -143,7 +143,7 @@ impl FlashMemory { dummy: DummyCycles::_0, }; self.enable_write(); - self.qspi.command(transaction); + self.qspi.blocking_command(transaction); self.wait_write_finish(); } diff --git a/examples/stm32h7b0/src/bin/ospi_memory_mapped.rs b/examples/stm32h7b0/src/bin/ospi_memory_mapped.rs index 9c397e507..dffb740a9 100644 --- a/examples/stm32h7b0/src/bin/ospi_memory_mapped.rs +++ b/examples/stm32h7b0/src/bin/ospi_memory_mapped.rs @@ -223,7 +223,7 @@ impl FlashMemory { dummy: DummyCycles::_0, ..Default::default() }; - self.ospi.command(&transaction).await.unwrap(); + self.ospi.blocking_command(&transaction).unwrap(); } async fn exec_command(&mut self, cmd: u8) { @@ -238,7 +238,7 @@ impl FlashMemory { ..Default::default() }; // info!("Excuting command: {:x}", transaction.instruction); - self.ospi.command(&transaction).await.unwrap(); + self.ospi.blocking_command(&transaction).unwrap(); } pub async fn reset_memory(&mut self) { @@ -318,7 +318,7 @@ impl FlashMemory { ..Default::default() }; self.enable_write().await; - self.ospi.command(&transaction).await.unwrap(); + self.ospi.blocking_command(&transaction).unwrap(); self.wait_write_finish(); }