diff --git a/.github/workflows/build-bsp.yml b/.github/workflows/build-bsp.yml index 2976344bbd14..5c1732eb2bf5 100644 --- a/.github/workflows/build-bsp.yml +++ b/.github/workflows/build-bsp.yml @@ -60,7 +60,7 @@ jobs: $(${clippy_invocation}) - name: Done - uses: actions/upload-artifact@v6 + uses: actions/upload-artifact@v7 with: # name needs to be unique in the workspace name: "${{ matrix.bsp.name }}-${{ matrix.toolchain }}" @@ -71,7 +71,7 @@ jobs: needs: [setup, build] steps: - name: Download artifacts - uses: actions/download-artifact@v7 + uses: actions/download-artifact@v8 with: path: successful-jobs - name: Do checks diff --git a/boards/atsame54_xpro/CHANGELOG.md b/boards/atsame54_xpro/CHANGELOG.md index dc68db6c7702..4bee8d6fc5c1 100644 --- a/boards/atsame54_xpro/CHANGELOG.md +++ b/boards/atsame54_xpro/CHANGELOG.md @@ -7,6 +7,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +## [0.13.3](https://github.com/atsamd-rs/atsamd/compare/atsame54_xpro-0.13.2...atsame54_xpro-0.13.3) - 2026-03-02 + +### Other + +- updated the following local packages: atsamd-hal + ## [0.13.2](https://github.com/atsamd-rs/atsamd/compare/atsame54_xpro-0.13.1...atsame54_xpro-0.13.2) - 2026-02-20 ### Other diff --git a/boards/atsame54_xpro/Cargo.toml b/boards/atsame54_xpro/Cargo.toml index c311730dfe65..175936b23b97 100644 --- a/boards/atsame54_xpro/Cargo.toml +++ b/boards/atsame54_xpro/Cargo.toml @@ -11,7 +11,7 @@ license = "MIT OR Apache-2.0" name = "atsame54_xpro" readme = "README.md" repository = "https://github.com/atsamd-rs/atsamd" -version = "0.13.2" +version = "0.13.3" [dependencies.cortex-m-rt] optional = true @@ -24,7 +24,7 @@ version = "0.7" [dependencies.atsamd-hal] default-features = false path = "../../hal" -version = "0.23.2" +version = "0.23.3" [dependencies.usb-device] optional = true diff --git a/boards/feather_m0/CHANGELOG.md b/boards/feather_m0/CHANGELOG.md index 3c53c514a042..62b20d557e78 100644 --- a/boards/feather_m0/CHANGELOG.md +++ b/boards/feather_m0/CHANGELOG.md @@ -7,6 +7,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +## [0.20.3](https://github.com/atsamd-rs/atsamd/compare/feather_m0-0.20.2...feather_m0-0.20.3) - 2026-03-02 + +### Other + +- updated the following local packages: atsamd-hal + ## [0.20.2](https://github.com/atsamd-rs/atsamd/compare/feather_m0-0.20.1...feather_m0-0.20.2) - 2026-02-20 ### Other diff --git a/boards/feather_m0/Cargo.toml b/boards/feather_m0/Cargo.toml index 4bfeb8415826..cda71df03fcb 100644 --- a/boards/feather_m0/Cargo.toml +++ b/boards/feather_m0/Cargo.toml @@ -9,7 +9,7 @@ name = "feather_m0" readme = "README.md" repository = "https://github.com/atsamd-rs/atsamd" resolver = "2" -version = "0.20.2" +version = "0.20.3" # for cargo flash [package.metadata] @@ -22,7 +22,7 @@ version = "0.7" [dependencies.atsamd-hal] default-features = false path = "../../hal" -version = "0.23.2" +version = "0.23.3" [dependencies.cortex-m] features = ["critical-section-single-core"] diff --git a/boards/feather_m4/CHANGELOG.md b/boards/feather_m4/CHANGELOG.md index 1a75dda961a4..94c78dbfd744 100644 --- a/boards/feather_m4/CHANGELOG.md +++ b/boards/feather_m4/CHANGELOG.md @@ -7,6 +7,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +## [0.18.3](https://github.com/atsamd-rs/atsamd/compare/feather_m4-0.18.2...feather_m4-0.18.3) - 2026-03-02 + +### Other + +- updated the following local packages: atsamd-hal + ## [0.18.2](https://github.com/atsamd-rs/atsamd/compare/feather_m4-0.18.1...feather_m4-0.18.2) - 2026-02-20 ### Other diff --git a/boards/feather_m4/Cargo.toml b/boards/feather_m4/Cargo.toml index e3e973dfaa71..8c7da29207f7 100644 --- a/boards/feather_m4/Cargo.toml +++ b/boards/feather_m4/Cargo.toml @@ -9,7 +9,7 @@ license = "MIT OR Apache-2.0" name = "feather_m4" readme = "README.md" repository = "https://github.com/atsamd-rs/atsamd" -version = "0.18.2" +version = "0.18.3" # for cargo flash [package.metadata] @@ -26,7 +26,7 @@ version = "0.7" [dependencies.atsamd-hal] default-features = false path = "../../hal" -version = "0.23.2" +version = "0.23.3" features = ["undoc-features"] [dependencies.usb-device] diff --git a/boards/metro_m0/CHANGELOG.md b/boards/metro_m0/CHANGELOG.md index 446f7fd4df8e..293982cb4868 100644 --- a/boards/metro_m0/CHANGELOG.md +++ b/boards/metro_m0/CHANGELOG.md @@ -7,6 +7,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +## [0.20.3](https://github.com/atsamd-rs/atsamd/compare/metro_m0-0.20.2...metro_m0-0.20.3) - 2026-03-02 + +### Other + +- updated the following local packages: atsamd-hal + ## [0.20.2](https://github.com/atsamd-rs/atsamd/compare/metro_m0-0.20.1...metro_m0-0.20.2) - 2026-02-20 ### Other diff --git a/boards/metro_m0/Cargo.toml b/boards/metro_m0/Cargo.toml index c33d1532bd7d..103de8532a77 100644 --- a/boards/metro_m0/Cargo.toml +++ b/boards/metro_m0/Cargo.toml @@ -8,7 +8,7 @@ license = "MIT OR Apache-2.0" name = "metro_m0" readme = "README.md" repository = "https://github.com/atsamd-rs/atsamd" -version = "0.20.2" +version = "0.20.3" # for cargo flash [package.metadata] @@ -25,7 +25,7 @@ version = "0.7" [dependencies.atsamd-hal] default-features = false path = "../../hal" -version = "0.23.2" +version = "0.23.3" [dependencies.rtic] features = ["thumbv6-backend"] diff --git a/boards/metro_m4/CHANGELOG.md b/boards/metro_m4/CHANGELOG.md index 6138b3ec66c4..bf4a9de429b6 100644 --- a/boards/metro_m4/CHANGELOG.md +++ b/boards/metro_m4/CHANGELOG.md @@ -7,6 +7,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +## [0.19.3](https://github.com/atsamd-rs/atsamd/compare/metro_m4-0.19.2...metro_m4-0.19.3) - 2026-03-02 + +### Other + +- updated the following local packages: atsamd-hal + ## [0.19.2](https://github.com/atsamd-rs/atsamd/compare/metro_m4-0.19.1...metro_m4-0.19.2) - 2026-02-20 ### Other diff --git a/boards/metro_m4/Cargo.toml b/boards/metro_m4/Cargo.toml index 42a28089bc61..29f730f241e1 100644 --- a/boards/metro_m4/Cargo.toml +++ b/boards/metro_m4/Cargo.toml @@ -8,7 +8,7 @@ license = "MIT OR Apache-2.0" name = "metro_m4" readme = "README.md" repository = "https://github.com/atsamd-rs/atsamd" -version = "0.19.2" +version = "0.19.3" # for cargo flash [package.metadata] @@ -21,7 +21,7 @@ version = "0.7" [dependencies.atsamd-hal] default-features = false path = "../../hal" -version = "0.23.2" +version = "0.23.3" features = ["undoc-features"] [dependencies.usb-device] diff --git a/boards/pygamer/CHANGELOG.md b/boards/pygamer/CHANGELOG.md index 8e55bb23c74b..89e8203e0e30 100644 --- a/boards/pygamer/CHANGELOG.md +++ b/boards/pygamer/CHANGELOG.md @@ -7,6 +7,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +## [0.16.3](https://github.com/atsamd-rs/atsamd/compare/pygamer-0.16.2...pygamer-0.16.3) - 2026-03-02 + +### Other + +- updated the following local packages: atsamd-hal + ## [0.16.2](https://github.com/atsamd-rs/atsamd/compare/pygamer-0.16.1...pygamer-0.16.2) - 2026-02-20 ### Other diff --git a/boards/pygamer/Cargo.toml b/boards/pygamer/Cargo.toml index 57aae9409cde..8393af5de64d 100644 --- a/boards/pygamer/Cargo.toml +++ b/boards/pygamer/Cargo.toml @@ -12,7 +12,7 @@ license = "MIT OR Apache-2.0" name = "pygamer" readme = "README.md" repository = "https://github.com/atsamd-rs/atsamd" -version = "0.16.2" +version = "0.16.3" [dependencies] cortex-m = {version = "0.7", features = ["critical-section-single-core"]} @@ -27,7 +27,7 @@ version = "0.7" [dependencies.atsamd-hal] default-features = false path = "../../hal" -version = "0.23.2" +version = "0.23.3" features = ["undoc-features"] [dependencies.usb-device] diff --git a/boards/samd11_bare/CHANGELOG.md b/boards/samd11_bare/CHANGELOG.md index e70452136ef0..66c8f829c1f4 100644 --- a/boards/samd11_bare/CHANGELOG.md +++ b/boards/samd11_bare/CHANGELOG.md @@ -7,6 +7,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +## [0.16.3](https://github.com/atsamd-rs/atsamd/compare/samd11_bare-0.16.2...samd11_bare-0.16.3) - 2026-03-02 + +### Other + +- updated the following local packages: atsamd-hal + ## [0.16.2](https://github.com/atsamd-rs/atsamd/compare/samd11_bare-0.16.1...samd11_bare-0.16.2) - 2026-02-20 ### Other diff --git a/boards/samd11_bare/Cargo.toml b/boards/samd11_bare/Cargo.toml index 317c845414f8..c7653549ca54 100644 --- a/boards/samd11_bare/Cargo.toml +++ b/boards/samd11_bare/Cargo.toml @@ -7,7 +7,7 @@ license = "MIT OR Apache-2.0" name = "samd11_bare" readme = "README.md" repository = "https://github.com/atsamd-rs/atsamd" -version = "0.16.2" +version = "0.16.3" # for cargo flash [package.metadata] @@ -24,7 +24,7 @@ version = "0.7" [dependencies.atsamd-hal] default-features = false path = "../../hal" -version = "0.23.2" +version = "0.23.3" [dependencies.rtic] features = ["thumbv6-backend"] diff --git a/hal/CHANGELOG.md b/hal/CHANGELOG.md index 367cae510bef..1ab6ecd97223 100644 --- a/hal/CHANGELOG.md +++ b/hal/CHANGELOG.md @@ -7,6 +7,16 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +## [0.23.3](https://github.com/atsamd-rs/atsamd/compare/atsamd-hal-0.23.2...atsamd-hal-0.23.3) - 2026-03-02 + +### Fixed + +- *(adc)* ADC clearing flags when reading value & make it somewhat consistent ([#989](https://github.com/atsamd-rs/atsamd/pull/989)) + +### Other + +- *(clippy)* New lint doesn't like nesting if in match ([#993](https://github.com/atsamd-rs/atsamd/pull/993)) + ## [0.23.2](https://github.com/atsamd-rs/atsamd/compare/atsamd-hal-0.23.1...atsamd-hal-0.23.2) - 2026-02-20 ### Added diff --git a/hal/Cargo.toml b/hal/Cargo.toml index 7b4fe2bc21ea..8f5b29172c87 100644 --- a/hal/Cargo.toml +++ b/hal/Cargo.toml @@ -20,7 +20,7 @@ name = "atsamd-hal" readme = "README.md" repository = "https://github.com/atsamd-rs/atsamd" rust-version = "1.85.1" -version = "0.23.2" +version = "0.23.3" [package.metadata.docs.rs] features = ["samd21g", "samd21g-rt", "usb", "dma", "async", "rtic"] diff --git a/hal/src/dmac/channel/mod.rs b/hal/src/dmac/channel/mod.rs index 68d0da72e818..3a2224ae938e 100644 --- a/hal/src/dmac/channel/mod.rs +++ b/hal/src/dmac/channel/mod.rs @@ -25,6 +25,68 @@ //! [`Busy`]. These statuses are checked at compile time to ensure they are //! properly initialized before launching DMA transfers. //! +//! # Interrupt availability +//! +//! Channels carry an [`Interrupts`] type parameter — either [`Available`] or +//! [`Blocked`] — that controls access to interrupt configuration. +//! +//! Channels from [`DmaController::split`](super::DmaController::split) are +//! [`Available`], allowing [`enable_interrupts`](Channel::enable_interrupts) +//! and [`disable_interrupts`](Channel::disable_interrupts) to be called freely. +//! +//! Channels from the async +//! [`DmaController::split`](super::DmaController::split) are [`Blocked`], +//! because the async runtime manages interrupts internally. [`Blocked`] +//! channels can be converted between async and blocking statuses via [`From`] +//! impls, for use with the [`Transfer`] API, while retaining the interrupt +//! restriction. +//! +//! # Converting between async and blocking channels +//! +//! [`Blocked`] channels support [`From`] conversions between their async +//! (`ReadyFuture`, `UninitializedFuture`) and blocking ([`Ready`], +//! [`Uninitialized`]) statuses. This enables using the [`Transfer`] API +//! with channels obtained from the async +//! [`DmaController`](super::DmaController). +//! +//! Each conversion performs a hardware channel reset (SWRST), which clears +//! all channel register configuration back to defaults. This includes the +//! priority level set by [`Channel::init`], which resets to +//! `PriorityLevel::LVL0`. If you need a non-default priority level after +//! conversion, call [`init`](Channel::init) again. +//! +//! ``` +//! // Assume `async_chan` is a Channel from the +//! // async DmaController. +//! +//! // Convert to a blocking-mode channel. The channel hardware is reset, +//! // so priority returns to LVL0. We want a higher priority for this +//! // circular transfer, so we chain .init() to set LVL3. +//! let blocking_chan: Channel<_, Ready, Blocked> = +//! Channel::from(async_chan).init(PriorityLevel::LVL3); +//! +//! // Use with the Transfer API — for example, a circular transfer, +//! // which is not available through the async channel API. +//! let xfer = Transfer::new(blocking_chan, buf_src, buf_dest, true) +//! .unwrap() +//! .begin(TriggerSource::Disable, TriggerAction::Block); +//! +//! // ... do work while the circular transfer runs ... +//! +//! let (chan, src, dest) = xfer.stop(); +//! +//! // Convert back to async when done. The channel is reset again; +//! // LVL0 is fine for async use, so no .init() call needed. +//! let async_chan: Channel<_, ReadyFuture, Blocked> = chan.into(); +//! ``` +//! +//! Note that [`Blocked`] channels cannot call +//! [`enable_interrupts`](Channel::enable_interrupts) or +//! [`disable_interrupts`](Channel::disable_interrupts). Blocking DMA methods +//! that enable interrupts internally (such as +//! [`Uart::receive_with_dma`](crate::sercom::uart::Uart::receive_with_dma)) +//! require [`Available`] channels. +//! //! # Resetting //! //! Calling the [`Channel::reset`] method will reset the channel to its @@ -112,39 +174,61 @@ impl ReadyChannel for Ready {} #[cfg(feature = "async")] impl ReadyChannel for ReadyFuture {} +/// Tracks whether the channel can use interrupts, or if they're in use already +/// by the Async impl +pub trait Interrupts: Sealed {} + +/// Interrupts are available +pub enum Available {} +impl Sealed for Available {} +impl Interrupts for Available {} + +/// Interrupts are managed by the `async` runtime and cannot be directly +/// configured +pub enum Blocked {} +impl Sealed for Blocked {} +impl Interrupts for Blocked {} + //============================================================================== // AnyChannel //============================================================================== pub trait AnyChannel: Sealed + Is> { type Status: Status; type Id: ChId; + type Interrupts: Interrupts; } -pub type SpecificChannel = Channel<::Id, ::Status>; +pub type SpecificChannel = + Channel<::Id, ::Status, ::Interrupts>; pub type ChannelStatus = ::Status; pub type ChannelId = ::Id; +pub type ChannelInterrupts = ::Interrupts; -impl Sealed for Channel +impl Sealed for Channel where Id: ChId, S: Status, + I: Interrupts, { } -impl AnyChannel for Channel +impl AnyChannel for Channel where Id: ChId, S: Status, + I: Interrupts, { type Id = Id; type Status = S; + type Interrupts = I; } -impl AsRef for Channel +impl AsRef for Channel where Id: ChId, S: Status, + I: Interrupts, { #[inline] fn as_ref(&self) -> &Self { @@ -152,10 +236,11 @@ where } } -impl AsMut for Channel +impl AsMut for Channel where Id: ChId, S: Status, + I: Interrupts, { #[inline] fn as_mut(&mut self) -> &mut Self { @@ -169,31 +254,37 @@ where /// DMA channel, capable of executing /// [`Transfer`]s. Ongoing DMA transfers are automatically stopped when a -/// [`Channel`] is dropped. -pub struct Channel { +/// [`Channel`] is dropped. [`Interrupts`] are either [`Available`] or +/// [`Blocked`], depending on whether the async controller is used. +pub struct Channel { regs: RegisterBlock, _status: PhantomData, + _interrupts: PhantomData, } #[inline] -pub(super) fn new_chan(_id: PhantomData) -> Channel { +pub(super) fn new_chan(_id: PhantomData) -> Channel { Channel { regs: RegisterBlock::new(_id), _status: PhantomData, + _interrupts: PhantomData, } } #[cfg(feature = "async")] #[inline] -pub(super) fn new_chan_future(_id: PhantomData) -> Channel { +pub(super) fn new_chan_future( + _id: PhantomData, +) -> Channel { Channel { regs: RegisterBlock::new(_id), _status: PhantomData, + _interrupts: PhantomData, } } /// These methods may be used on any DMA channel in any configuration -impl Channel { +impl Channel { /// Configure the DMA channel so that it is ready to be used by a /// [`Transfer`]. /// @@ -202,7 +293,7 @@ impl Channel { /// A `Channel` with a `Ready` status #[inline] #[hal_macro_helper] - pub fn init(mut self, lvl: PriorityLevel) -> Channel { + pub fn init(mut self, lvl: PriorityLevel) -> Channel { // Software reset the channel for good measure self._reset_private(); @@ -216,26 +307,6 @@ impl Channel { self.change_status() } - /// Selectively enable interrupts - #[inline] - pub fn enable_interrupts(&mut self, flags: InterruptFlags) { - // SAFETY: This is safe as InterruptFlags is only capable of writing in - // non-reserved bits - self.regs - .chintenset - .write(|w| unsafe { w.bits(flags.into()) }); - } - - /// Selectively disable interrupts - #[inline] - pub fn disable_interrupts(&mut self, flags: InterruptFlags) { - // SAFETY: This is safe as InterruptFlags is only capable of writing in - // non-reserved bits - self.regs - .chintenclr - .write(|w| unsafe { w.bits(flags.into()) }); - } - /// Check the specified `flags`, clear then return any that were set #[inline] pub fn check_and_clear_interrupts(&mut self, flags: InterruptFlags) -> InterruptFlags { @@ -249,10 +320,11 @@ impl Channel { } #[inline] - pub(super) fn change_status(self) -> Channel { + pub(super) fn change_status(self) -> Channel { Channel { regs: self.regs, _status: PhantomData, + _interrupts: PhantomData, } } @@ -379,15 +451,93 @@ impl Channel { } } -impl Channel +/// Interrupt configuration methods, only available on channels with +/// [`Available`] interrupts. Channels from the async +/// [`DmaController`](super::DmaController) are [`Blocked`] and cannot use these +/// methods. +impl Channel { + /// Selectively enable interrupts. + /// + /// Only available on channels with [`Available`] interrupts. + #[inline] + pub fn enable_interrupts(&mut self, flags: InterruptFlags) { + // SAFETY: Type parameters ensure this is valid for this channel + unsafe { self._enable_interrupts_unchecked(flags) } + } + + /// Selectively disable interrupts. + /// + /// Only available on channels with [`Available`] interrupts. + #[inline] + pub fn disable_interrupts(&mut self, flags: InterruptFlags) { + // SAFETY: Type parameters ensure this is valid for this channel + unsafe { self._disable_interrupts_unchecked(flags) } + } + + /// Irreversibly consider this channel to be [`Blocked`]. For API + /// compatibility, this will promise that this channel will never have + /// interrupts enabled. + /// + /// Be aware that calling `.into::>()` on + /// a [`Blocked`] channel you got from `into_blocked()` will produce + /// a nonfunctional channel - Async channels don't work if the DMAC wasn't + /// initialized as Async. + pub fn into_blocked(mut self) -> Channel { + self.disable_interrupts( + InterruptFlags::new() + .with_tcmpl(true) + .with_terr(true) + .with_susp(true), + ); + Channel { + _interrupts: PhantomData, + _status: self._status, + regs: self.regs, + } + } +} + +/// Unsafe internals for async impl +impl Channel { + /// Selectively enable interrupts. + /// + /// # Safety + /// Ensure the channel is Interrupts=Available, or you're correctly enabling + /// TCMPL for Async + #[inline] + pub(crate) unsafe fn _enable_interrupts_unchecked(&mut self, flags: InterruptFlags) { + // SAFETY: This is safe as InterruptFlags is only capable of writing in + // non-reserved bits + self.regs + .chintenset + .write(|w| unsafe { w.bits(flags.into()) }); + } + + /// Selectively disable interrupts + /// + /// # Safety + /// Ensure the channel is Interrupts=Available, or you're correctly + /// disabling TCMPL for Async + #[inline] + pub(crate) unsafe fn _disable_interrupts_unchecked(&mut self, flags: InterruptFlags) { + // SAFETY: This is safe as InterruptFlags is only capable of writing in + // non-reserved bits + self.regs + .chintenclr + .write(|w| unsafe { w.bits(flags.into()) }); + } +} + +impl Channel where Id: ChId, R: ReadyChannel, + I: Interrupts, { /// Issue a software reset to the channel. This will return the channel to /// its startup state #[inline] - pub fn reset(mut self) -> Channel { + pub fn reset(mut self) -> Channel { self._reset_private(); self.change_status() } @@ -471,7 +621,7 @@ where } } -impl Channel { +impl Channel { /// Start transfer on channel using the specified trigger source. /// /// # Return @@ -482,7 +632,7 @@ impl Channel { mut self, trig_src: TriggerSource, trig_act: TriggerAction, - ) -> Channel { + ) -> Channel { unsafe { self._start_private(trig_src, trig_act); } @@ -583,7 +733,7 @@ impl Channel { } /// These methods may only be used on a `Busy` DMA channel -impl Channel { +impl Channel { /// Issue a software trigger to the channel #[inline] pub(crate) fn software_trigger(&mut self) { @@ -598,7 +748,7 @@ impl Channel { /// A `Channel` with a `Ready` status, ready to be reused by a new /// [`Transfer`](super::transfer::Transfer) #[inline] - pub(crate) fn free(mut self) -> Channel { + pub(crate) fn free(mut self) -> Channel { self.stop(); self.change_status() } @@ -610,15 +760,15 @@ impl Channel { } } -impl From> for Channel { - fn from(mut item: Channel) -> Self { +impl From> for Channel { + fn from(mut item: Channel) -> Self { item._reset_private(); item.change_status() } } #[cfg(feature = "async")] -impl Channel { +impl Channel { /// Begin DMA transfer using `async` operation. /// /// If [`TriggerSource::Disable`] is used, a software @@ -708,12 +858,15 @@ impl Channel { } } - self.disable_interrupts( - InterruptFlags::new() - .with_susp(true) - .with_tcmpl(true) - .with_terr(true), - ); + // SAFETY: This is the only place this is allowed + unsafe { + self._disable_interrupts_unchecked( + InterruptFlags::new() + .with_susp(true) + .with_tcmpl(true) + .with_terr(true), + ); + } self.configure_trigger(trig_src, trig_act); @@ -728,6 +881,58 @@ impl Channel { } } +/// Convert an async [`ReadyFuture`] channel to a blocking [`Ready`] channel, +/// enabling use with the [`Transfer`] API. The channel retains [`Blocked`] +/// interrupts. +/// +/// The channel hardware is reset during conversion, which clears all channel +/// configuration including the priority level. If you need a non-default +/// priority level, call [`init`](Channel::init) again after conversion. +#[cfg(feature = "async")] +impl From> for Channel { + fn from(mut item: Channel) -> Self { + item._reset_private(); + item.change_status() + } +} + +#[cfg(feature = "async")] +impl From> + for Channel +{ + fn from(mut item: Channel) -> Self { + item._reset_private(); + item.change_status() + } +} + +/// Restore a blocking [`Ready`] channel to async [`ReadyFuture`] status. +/// +/// The channel hardware is reset during conversion, which clears all channel +/// configuration including the priority level. If you need a non-default +/// priority level, call [`init`](Channel::init) again after conversion. +/// +/// Be aware that calling this on a [`Blocked`] channel you got from +/// `into_blocked()` will produce a nonfunctional channel - Async channels don't +/// work if the DMAC wasn't initialized as Async. +#[cfg(feature = "async")] +impl From> for Channel { + fn from(mut item: Channel) -> Self { + item._reset_private(); + item.change_status() + } +} + +#[cfg(feature = "async")] +impl From> + for Channel +{ + fn from(mut item: Channel) -> Self { + item._reset_private(); + item.change_status() + } +} + #[cfg(feature = "async")] mod transfer_future { use super::*; @@ -744,11 +949,14 @@ mod transfer_future { pub(super) struct TransferFuture<'a, Id: ChId> { triggered: bool, trig_src: TriggerSource, - chan: &'a mut Channel, + chan: &'a mut Channel, } impl<'a, Id: ChId> TransferFuture<'a, Id> { - pub(super) fn new(chan: &'a mut Channel, trig_src: TriggerSource) -> Self { + pub(super) fn new( + chan: &'a mut Channel, + trig_src: TriggerSource, + ) -> Self { Self { triggered: false, trig_src, @@ -782,7 +990,8 @@ mod transfer_future { Poll::Ready(Err(super::Error::TransferError)) } else { WAKERS[Id::USIZE].register(cx.waker()); - self.chan.enable_interrupts(flags_to_check); + // SAFETY: This is the only place this is allowed + unsafe { self.chan._enable_interrupts_unchecked(flags_to_check) }; self.chan._enable_private(); if !self.triggered && self.trig_src == TriggerSource::Disable { diff --git a/hal/src/dmac/dma_controller.rs b/hal/src/dmac/dma_controller.rs index 588488c4ce4e..6b6126b1bfe3 100644 --- a/hal/src/dmac/dma_controller.rs +++ b/hal/src/dmac/dma_controller.rs @@ -139,7 +139,7 @@ macro_rules! define_channels_struct_future { /// Struct generating individual handles to each DMA channel for `async` operation pub struct FutureChannels( #( - pub Channel, + pub Channel, )* ); }); @@ -373,7 +373,10 @@ impl DmaController { macro_rules! define_split_future { ($num_channels:literal) => { seq!(N in 0..$num_channels { - /// Split the DMAC into individual channels + /// Split the DMAC into individual channels for `async` operation. + /// + /// Returned channels have [`Blocked`](super::channel::Blocked) + /// interrupts, managed by the async runtime. #[inline] pub fn split(&mut self) -> FutureChannels { FutureChannels( diff --git a/hal/src/dmac/mod.rs b/hal/src/dmac/mod.rs index c6cbc8207943..77b85f1c6c87 100644 --- a/hal/src/dmac/mod.rs +++ b/hal/src/dmac/mod.rs @@ -80,12 +80,18 @@ //! //! # Interrupts //! -//! This driver does not use or manage interrupts issued by the DMAC. Individual -//! channels can be configured to generate interrupts when the transfer is -//! complete, an error is detected or the channel is suspended. However, these -//! interrupts will not be triggered unless the DMAC interrupt is unmasked in -//! the NVIC. You will be responsible for clearing the interrupt flags in the -//! ISR. +//! This driver does not use or manage interrupts issued by the DMAC in blocking +//! mode. Individual channels can be configured to generate interrupts when the +//! transfer is complete, an error is detected or the channel is suspended. +//! However, these interrupts will not be triggered unless the DMAC interrupt is +//! unmasked in the NVIC. You will be responsible for clearing the interrupt +//! flags in the ISR. +//! +//! In `async` mode, interrupts are managed by the async runtime. Channels +//! obtained from the async [`DmaController`] carry [`Blocked`] +//! interrupts, preventing user code from enabling or disabling channel +//! interrupts directly. See the [`channel`] module documentation for details +//! on converting between async and blocking channel modes. //! //! # About static lifetimes //! diff --git a/hal/src/dmac/transfer.rs b/hal/src/dmac/transfer.rs index 7d9012327f5e..8dfaf76a6f9b 100644 --- a/hal/src/dmac/transfer.rs +++ b/hal/src/dmac/transfer.rs @@ -83,8 +83,8 @@ //! stopped. use super::{ - Error, ReadyChannel, Result, - channel::{AnyChannel, Busy, Channel, ChannelId, InterruptFlags, Ready}, + Error, Result, + channel::{AnyChannel, Busy, Channel, ChannelId, ChannelInterrupts, InterruptFlags, Ready}, dma_controller::{TriggerAction, TriggerSource}, }; use crate::typelevel::{Is, Sealed}; @@ -311,12 +311,27 @@ where complete: bool, } -impl Transfer> +/// A [`Transfer`] with a [`Busy`] channel that is actively running. +/// +/// This is the return type of [`Transfer::begin`]. The transfer can be +/// polled with [`complete`](Transfer::complete), waited on with +/// [`wait`](Transfer::wait), or stopped with [`stop`](Transfer::stop). +pub type BusyTransfer = + Transfer, Busy, ChannelInterrupts>, BufferPair>; + +/// A [`Transfer`] with a [`Ready`] channel that has not yet been started. +/// +/// Constructed via [`Transfer::new`], [`Transfer::new_unchecked`], or +/// [`Transfer::new_from_arrays`]. Call [`begin`](Transfer::begin) to +/// start the transfer. +pub type ReadyTransfer = + Transfer, Ready, ChannelInterrupts>, BufferPair>; + +impl Transfer> where S: Buffer + 'static, D: Buffer + 'static, - C: AnyChannel, - R: ReadyChannel, + C: AnyChannel, { /// Safely construct a new `Transfer`. To guarantee memory safety, both /// buffers are required to be `'static`. @@ -370,12 +385,11 @@ where } } -impl Transfer> +impl Transfer> where S: Buffer, D: Buffer, - C: AnyChannel, - R: ReadyChannel, + C: AnyChannel, { /// Construct a new `Transfer` without checking for memory safety. /// @@ -434,7 +448,7 @@ where mut self, trig_src: TriggerSource, trig_act: TriggerAction, - ) -> Transfer, Busy>, BufferPair> { + ) -> BusyTransfer { // Reset the complete flag before triggering the transfer. // This way an interrupt handler could set complete to true // before this function returns. @@ -454,7 +468,7 @@ where /// Similar to [`stop`](Transfer::stop), but it acts on a [`Transfer`] /// holding a [`Ready`] channel, so there is no need to explicitly stop the /// transfer. - pub fn free(self) -> (Channel, Ready>, S, D) { + pub fn free(self) -> (Channel, Ready, ChannelInterrupts>, S, D) { ( self.chan.into(), self.buffers.source, @@ -463,11 +477,10 @@ where } } -impl Transfer> +impl Transfer> where B: 'static + Beat, - C: AnyChannel, - R: ReadyChannel, + C: AnyChannel, { /// Create a new `Transfer` from static array references of the same type /// and length. When two array references are available (instead of slice @@ -531,7 +544,7 @@ where /// /// # Blocking: This method may block #[inline] - pub fn wait(mut self) -> (Channel, Ready>, S, D) { + pub fn wait(mut self) -> (Channel, Ready, ChannelInterrupts>, S, D) { // Wait for transfer to complete while !self.complete() {} self.stop() @@ -641,7 +654,7 @@ where /// Non-blocking; Immediately stop the DMA transfer and release all owned /// resources #[inline] - pub fn stop(self) -> (Channel, Ready>, S, D) { + pub fn stop(self) -> (Channel, Ready, ChannelInterrupts>, S, D) { // `free()` stops the transfer, waits for the burst to finish, and emits a // compiler fence. let chan = self.chan.into().free(); diff --git a/hal/src/sercom/dma.rs b/hal/src/sercom/dma.rs index 02e1c0a337d1..64b1598d4404 100644 --- a/hal/src/sercom/dma.rs +++ b/hal/src/sercom/dma.rs @@ -9,10 +9,10 @@ use core::{marker::PhantomData, ops::Range}; use atsamd_hal_macros::hal_macro_helper; use crate::dmac::{ - self, Beat, Buffer, Transfer, TriggerAction, - channel::{AnyChannel, Busy, Channel, InterruptFlags, Ready}, + self, Beat, Buffer, TriggerAction, + channel::{AnyChannel, Available, InterruptFlags, Ready}, sram::DmacDescriptor, - transfer::BufferPair, + transfer::BusyTransfer, }; use crate::sercom::{ Sercom, @@ -172,8 +172,8 @@ where C: uart::ValidConfig, D: uart::Receive, { - /// Transform an [`Uart`] into a DMA [`Transfer`]) and start reveiving into - /// the provided buffer. + /// Transform an [`Uart`] into a DMA [`Transfer`](crate::dmac::Transfer)) + /// and start receiving into the provided buffer. /// /// In order to be (safely) non-blocking, his method has to take a `'static` /// buffer. If you'd rather use DMA with the blocking @@ -182,13 +182,9 @@ where /// use [`Uart::with_rx_channel`](Self::with_tx_channel) instead. #[inline] #[hal_macro_helper] - pub fn receive_with_dma( - self, - buf: B, - mut channel: Ch, - ) -> Transfer, BufferPair> + pub fn receive_with_dma(self, buf: B, mut channel: Ch) -> BusyTransfer where - Ch: AnyChannel, + Ch: AnyChannel, B: Buffer + 'static, { channel @@ -214,8 +210,8 @@ where C: uart::ValidConfig, D: uart::Transmit, { - /// Transform an [`Uart`] into a DMA [`Transfer`]) and start sending the - /// provided buffer. + /// Transform an [`Uart`] into a DMA [`Transfer`](crate::dmac::Transfer)) + /// and start sending the provided buffer. /// /// In order to be (safely) non-blocking, his method takes a `'static` /// buffer. If you'd rather use DMA with the blocking @@ -224,13 +220,9 @@ where /// use[`Uart::with_tx_channel`](Self::with_tx_channel) instead. #[inline] #[hal_macro_helper] - pub fn send_with_dma( - self, - buf: B, - mut channel: Ch, - ) -> Transfer, BufferPair> + pub fn send_with_dma(self, buf: B, mut channel: Ch) -> BusyTransfer where - Ch: AnyChannel, + Ch: AnyChannel, B: Buffer + 'static, { channel @@ -373,14 +365,14 @@ pub(super) unsafe fn write_dma_linked( /// corresponding DMA transfer implementations. #[cfg(feature = "async")] pub(crate) mod async_dma { - use dmac::{Error, ReadyFuture}; + use dmac::{Blocked, Error, ReadyFuture}; use super::*; /// Perform a SERCOM DMA read with a provided `&mut [T]` #[inline] pub(in super::super) async fn read_dma( - channel: &mut impl AnyChannel, + channel: &mut impl AnyChannel, sercom_ptr: SercomPtr, buf: &mut B, ) -> Result<(), Error> @@ -403,7 +395,7 @@ pub(crate) mod async_dma { #[inline] #[hal_macro_helper] pub(in super::super) async unsafe fn read_dma_linked( - channel: &mut impl AnyChannel, + channel: &mut impl AnyChannel, mut sercom_ptr: SercomPtr, buf: &mut B, next: Option<&mut DmacDescriptor>, @@ -438,7 +430,7 @@ pub(crate) mod async_dma { /// Perform a SERCOM DMA write with a provided `&[T]` #[inline] pub(in super::super) async fn write_dma( - channel: &mut impl AnyChannel, + channel: &mut impl AnyChannel, sercom_ptr: SercomPtr, buf: &mut B, ) -> Result<(), Error> @@ -461,7 +453,7 @@ pub(crate) mod async_dma { #[inline] #[hal_macro_helper] pub(in super::super) async unsafe fn write_dma_linked( - channel: &mut impl AnyChannel, + channel: &mut impl AnyChannel, mut sercom_ptr: SercomPtr, buf: &mut B, next: Option<&mut DmacDescriptor>, diff --git a/hal/src/sercom/i2c/async_api.rs b/hal/src/sercom/i2c/async_api.rs index 7b07651bcd3f..e9a017339ed9 100644 --- a/hal/src/sercom/i2c/async_api.rs +++ b/hal/src/sercom/i2c/async_api.rs @@ -108,7 +108,12 @@ where /// Use a DMA channel for reads/writes #[cfg(feature = "dma")] - pub fn with_dma_channel>( + pub fn with_dma_channel< + D: crate::dmac::AnyChannel< + Status = crate::dmac::ReadyFuture, + Interrupts = crate::dmac::Blocked, + >, + >( self, dma_channel: D, ) -> I2cFuture { @@ -280,7 +285,7 @@ mod dma { use super::*; use crate::dmac::sram::DmacDescriptor; - use crate::dmac::{AnyChannel, Channel, ReadyFuture}; + use crate::dmac::{AnyChannel, Blocked, Channel, ReadyFuture}; use crate::sercom::dma::SharedSliceBuffer; use crate::sercom::dma::async_dma::{read_dma_linked, write_dma_linked}; @@ -288,12 +293,12 @@ mod dma { /// Convenience type for a [`I2cFuture`] in DMA mode. /// /// The type parameter `I` represents the DMA channel ID (`ChX`). - pub type I2cFutureDma = I2cFuture>; + pub type I2cFutureDma = I2cFuture>; impl I2cFuture where C: AnyConfig, - D: AnyChannel, + D: AnyChannel, { /// Reclaim the DMA channel. Any subsequent I2C operations will no /// longer use DMA. @@ -307,7 +312,7 @@ mod dma { where C: AnyConfig, S: Sercom, - D: AnyChannel, + D: AnyChannel, { /// Make an async I2C write transaction, with the option to add in /// linked transfers after this first transaction. @@ -402,7 +407,7 @@ mod dma { impl I2cTrait for I2cFuture where C: AnyConfig, - D: AnyChannel, + D: AnyChannel, { #[inline] async fn read(&mut self, address: u8, buffer: &mut [u8]) -> Result<(), Self::Error> { diff --git a/hal/src/sercom/spi/async_api/dma.rs b/hal/src/sercom/spi/async_api/dma.rs index 87141318ea72..947a3ff4cf14 100644 --- a/hal/src/sercom/spi/async_api/dma.rs +++ b/hal/src/sercom/spi/async_api/dma.rs @@ -4,7 +4,7 @@ use num_traits::{AsPrimitive, PrimInt}; use super::SpiFuture; use crate::{ dmac::{ - AnyChannel, Beat, Buffer, ReadyFuture, + AnyChannel, Beat, Blocked, Buffer, ReadyFuture, channel::{self, Channel}, sram::DmacDescriptor, }, @@ -28,17 +28,17 @@ use crate::{ /// The type parameter `R` represents the RX DMA channel ID (`ChX`), and /// `T` represents the TX DMA channel ID. pub type SpiFutureDuplexDma = - SpiFuture, Channel>; + SpiFuture, Channel>; /// Convenience type for a [`SpiFuture`] with RX capabilities in DMA mode. /// /// The type parameter `R` represents the RX DMA channel ID (`ChX`). -pub type SpiFutureRxDma = SpiFuture, NoneT>; +pub type SpiFutureRxDma = SpiFuture, NoneT>; /// Convenience type for a [`SpiFuture`] with TX capabilities in DMA mode. /// /// The type parameter `T` represents the TX DMA channel ID (`ChX`). -pub type SpiFutureTxDma = SpiFuture>; +pub type SpiFutureTxDma = SpiFuture>; impl SpiFuture where @@ -51,7 +51,7 @@ where /// carry out its transactions. In Slave mode, a [`Rx`] [`SpiFuture`] only /// needs a single DMA channel. #[inline] - pub fn with_rx_dma_channel>( + pub fn with_rx_dma_channel>( self, rx_channel: Chan, ) -> SpiFuture { @@ -77,7 +77,7 @@ where /// carry out its transactions. For [`Tx`] [`SpiFuture`]s, only a single DMA /// channel is necessary. #[inline] - pub fn with_tx_dma_channel>( + pub fn with_tx_dma_channel>( self, tx_channel: Chan, ) -> SpiFuture { @@ -106,8 +106,8 @@ where #[inline] pub fn with_dma_channels(self, rx_channel: R, tx_channel: T) -> SpiFuture where - R: AnyChannel, - T: AnyChannel, + R: AnyChannel, + T: AnyChannel, { SpiFuture { spi: Spi { @@ -130,8 +130,8 @@ where /// needs two DMA channels. pub fn with_dma_channels_slave(self, rx: R, tx: T) -> SpiFuture where - R: AnyChannel, - T: AnyChannel, + R: AnyChannel, + T: AnyChannel, { SpiFuture { spi: Spi { @@ -153,8 +153,8 @@ where /// not use DMA. pub fn take_dma_channels(self) -> (SpiFuture, R, T) where - R: AnyChannel, - T: AnyChannel, + R: AnyChannel, + T: AnyChannel, { let (spi, rx, tx) = self.spi.take_dma_channels(); (SpiFuture { spi }, rx, tx) @@ -164,7 +164,7 @@ where /// use DMA. pub fn take_rx_channel(self) -> (SpiFuture, R) where - R: AnyChannel, + R: AnyChannel, { let (spi, channel) = self.spi.take_rx_channel(); (SpiFuture { spi }, channel) @@ -174,7 +174,7 @@ where /// use DMA. pub fn take_tx_channel(self) -> (SpiFuture, T) where - T: AnyChannel, + T: AnyChannel, { let (spi, channel) = self.spi.take_tx_channel(); (SpiFuture { spi }, channel) @@ -192,7 +192,7 @@ where S: Sercom, Z::Word: PrimInt + AsPrimitive + Beat, DataWidth: AsPrimitive, - T: AnyChannel, + T: AnyChannel, { /// Write words from a buffer asynchronously, using DMA. #[inline] @@ -230,8 +230,8 @@ where C::Word: PrimInt + AsPrimitive + Beat, D: Capability, DataWidth: AsPrimitive, - R: AnyChannel, - T: AnyChannel, + R: AnyChannel, + T: AnyChannel, { #[inline] async fn transfer_blocking, Dest: Buffer>( @@ -278,8 +278,8 @@ where C: Size + 'static, C::Word: PrimInt + AsPrimitive + Beat, DataWidth: AsPrimitive, - R: AnyChannel, - T: AnyChannel, + R: AnyChannel, + T: AnyChannel, { #[inline] async fn read(&mut self, words: &mut [C::Word]) -> Result<(), Self::Error> { @@ -423,7 +423,7 @@ where Z: Size + 'static, Config: ValidConfig, D: Transmit, - T: AnyChannel, + T: AnyChannel, { async fn write(&mut self, buf: &[u8]) -> Result { SpiFuture::write_dma(self, buf).await @@ -444,8 +444,8 @@ where Z: Size + 'static, Config: ValidConfig, D: Receive, - R: AnyChannel, - T: AnyChannel, + R: AnyChannel, + T: AnyChannel, { async fn read(&mut self, buf: &mut [u8]) -> Result { self.read_dma_master(buf).await?; @@ -462,7 +462,7 @@ where Config: ValidConfig, D: Receive, S: Sercom, - R: AnyChannel, + R: AnyChannel, { async fn read(&mut self, mut buf: &mut [u8]) -> Result { if buf.is_empty() { diff --git a/hal/src/sercom/uart/async_api.rs b/hal/src/sercom/uart/async_api.rs index a7d963234e25..4a3399e1ac63 100644 --- a/hal/src/sercom/uart/async_api.rs +++ b/hal/src/sercom/uart/async_api.rs @@ -218,7 +218,12 @@ where /// Use a DMA channel for receiving words on the RX line #[cfg(feature = "dma")] #[inline] - pub fn with_rx_dma_channel>( + pub fn with_rx_dma_channel< + Chan: crate::dmac::AnyChannel< + Status = crate::dmac::ReadyFuture, + Interrupts = crate::dmac::Blocked, + >, + >( self, rx_channel: Chan, ) -> UartFuture { @@ -291,7 +296,12 @@ where /// Use a DMA channel for sending words on the TX line #[cfg(feature = "dma")] #[inline] - pub fn with_tx_dma_channel>( + pub fn with_tx_dma_channel< + Chan: crate::dmac::AnyChannel< + Status = crate::dmac::ReadyFuture, + Interrupts = crate::dmac::Blocked, + >, + >( self, tx_channel: Chan, ) -> UartFuture { @@ -368,7 +378,7 @@ where mod dma { use super::*; use crate::{ - dmac::{AnyChannel, Beat, Channel, ReadyFuture}, + dmac::{AnyChannel, Beat, Blocked, Channel, ReadyFuture}, sercom::dma::{ SharedSliceBuffer, async_dma::{read_dma, write_dma}, @@ -381,35 +391,37 @@ mod dma { /// The type parameter `R` represents the RX DMA channel ID (`ChX`), and /// `T` represents the TX DMA channel ID. pub type UartFutureDuplexDma = - UartFuture, Channel>; + UartFuture, Channel>; /// Convenience type for a RX-only [`UartFuture`] in DMA mode. /// /// The type parameter `R` represents the RX DMA channel ID (`ChX`). - pub type UartFutureRxDma = UartFuture, NoneT>; + pub type UartFutureRxDma = UartFuture, NoneT>; /// Convenience type for the RX half of a [`Duplex`] [`UartFuture`] in DMA /// mode. /// /// The type parameter `R` represents the RX DMA channel ID (`ChX`). - pub type UartFutureRxDuplexDma = UartFuture, NoneT>; + pub type UartFutureRxDuplexDma = + UartFuture, NoneT>; /// Convenience type for a TX-only [`UartFuture`] in DMA mode. /// /// The type parameter `T` represents the TX DMA channel ID (`ChX`). - pub type UartFutureTxDma = UartFuture>; + pub type UartFutureTxDma = UartFuture>; /// Convenience type for the TX half of a [`Duplex`] [`UartFuture`] in DMA /// mode. /// /// The type parameter `T` represents the TX DMA channel ID (`ChX`). - pub type UartFutureTxDuplexDma = UartFuture>; + pub type UartFutureTxDuplexDma = + UartFuture>; impl UartFuture where C: ValidConfig, D: Capability, - R: AnyChannel, + R: AnyChannel, { /// Reclaim the RX DMA channel. Subsequent RX operations will no longer /// use DMA. @@ -423,7 +435,7 @@ mod dma { where C: ValidConfig, D: Capability, - T: AnyChannel, + T: AnyChannel, { /// Reclaim the TX DMA channel. Subsequent TX operations will no longer /// use DMA. @@ -440,7 +452,7 @@ mod dma { D: Receive, S: Sercom + 'static, DataReg: AsPrimitive, - R: AnyChannel, + R: AnyChannel, { /// Read the specified number of [`Word`](crate::sercom::uart::Word)s /// into a buffer using DMA. @@ -461,7 +473,7 @@ mod dma { D: Receive, S: Sercom + 'static, DataReg: AsPrimitive, - R: AnyChannel, + R: AnyChannel, { #[inline] async fn read(&mut self, words: &mut [u8]) -> Result { @@ -476,7 +488,7 @@ mod dma { C::Word: Beat, D: Transmit, S: Sercom + 'static, - T: AnyChannel, + T: AnyChannel, { /// Write words from a buffer asynchronously, using DMA. #[inline] @@ -497,7 +509,7 @@ mod dma { C: ValidConfig, D: Transmit, S: Sercom + 'static, - T: AnyChannel, + T: AnyChannel, { #[inline] async fn write(&mut self, words: &[u8]) -> Result {