Skip to content

Commit

Permalink
Document all the things!
Browse files Browse the repository at this point in the history
  • Loading branch information
jbeaurivage committed Jan 23, 2024
1 parent 9f13a24 commit 52286af
Show file tree
Hide file tree
Showing 14 changed files with 302 additions and 45 deletions.
5 changes: 5 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,11 @@ The Peripheral Access Crates (PACs) are automatically generated from Microchip S
[wio_terminal]: https://github.com/atsamd-rs/atsamd/tree/master/boards/wio_terminal
[xiao_m0]: https://github.com/atsamd-rs/atsamd/tree/master/boards/xiao_m0


### `async` APIs

[`atsamd_hal`](https://crates.io/crate/atsamd-hal) provides APIs for using `async`/`await` constructs with some of its peripherals. To enable `async` support, use the `async` Cargo feature. Detailed documentation is provided in the `atsamd_hal::async_hal` module.

### Examples

The BSPs include examples to quickly get up and running with the board. Building the examples
Expand Down
2 changes: 1 addition & 1 deletion boards/metro_m4/examples/async_dmac.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ use hal::{
};
use metro_m4 as bsp;

atsamd_hal::bind_interrupts!(struct Irqs {
atsamd_hal::bind_multiple_interrupts!(struct Irqs {
DMAC: [DMAC_0, DMAC_1, DMAC_2, DMAC_OTHER] => atsamd_hal::dmac::InterruptHandler;
});

Expand Down
98 changes: 74 additions & 24 deletions hal/src/async_hal/interrupts.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,39 @@
//! # Async interrupts
//!
//! This module provides APIs for working with interrupts, tailored towards
//! async peripherals.
//!
//! Asynchronous programming relies on tasks that can be paused and resumed
//! without blocking the entire program. When an async task is waiting for a
//! particular event, such as data from a peripheral, it enters a suspended
//! state. It is crucial that the task is properly woken up when the expected
//! event occurs to resume its execution.
//!
//! By having peripherals take interrupts, they can signal the occurrence of
//! relevant events, effectively waking up the associated async tasks. This
//! ensures that the async runtime can schedule and resume tasks in a timely
//! manner, providing the responsiveness required in embedded systems.
//!
//! ## Typelevel and enum-level interrupts
//!
//! There are two main ways of representing interrupts in the HAL: either by
//! using [`pac::Interrupt`], where each interrupt is represented as an enum
//! variant, or by using the typelevel interrupts defined in this module. Each
//! interrupt source *that is usable with async peripherals* is declared as a
//! struct with the same name of the corresponsing [`pac::Interrupt`] variant.
//! Therefore, two distinct traits are needed to perform basic tasks on
//! interrupt types:
//!
//! * Use [`Interrupt`] when dealing with the typelevel interrupt types defined
//! in this module;
//! * Use [`InterruptExt`] when dealing with enum-level interrupt types defined
//! in [`pac`].
//!
//! [`pac::Interrupt`]: crate::pac::Interrupt
//! [`Interrupt`]: crate::async_hal::interrupts::Interrupt
//! [`InterruptExt`]: crate::async_hal::interrupts::InterruptExt
//! [`pac`]: crate::pac
use crate::typelevel::Sealed;
use core::{
mem,
Expand All @@ -10,10 +46,14 @@ use seq_macro::seq;

/// Marker trait indicating that an interrupt source has one binding and
/// one handler.
///
/// May not be implemented outside of this HAL.
pub trait SingleInterruptSource: Sealed {}

/// Marker trait indicating that an interrupt source has multiple bindings and
/// handlers.
///
/// May not be implemented outside of this HAL.
pub trait MultipleInterruptSources: Sealed {}

macro_rules! declare_interrupts {
Expand All @@ -39,9 +79,10 @@ macro_rules! declare_interrupts {
}
}

// Useful when we need to bind multiple interrupt sources to the same handler.
// Calling the `InterruptSource` methods on the created struct will act on all
// interrupt sources at once.
/// Useful when we need to bind multiple interrupt sources to the same handler.
/// Calling the `InterruptSource` methods on the created struct will act on all
/// interrupt sources at once.
// Lint allowed because the macro is unused for thumbv6 devices.
#[allow(unused_macros)]
macro_rules! declare_multiple_interrupts {
($(#[$cfg:meta])* $name:ident: [ $($irq:ident),+ $(,)? ]) => {
Expand Down Expand Up @@ -81,9 +122,6 @@ macro_rules! declare_multiple_interrupts {
#[cfg(all(feature = "dma", feature = "thumbv7"))]
declare_multiple_interrupts!(DMAC: [DMAC_0, DMAC_1, DMAC_2, DMAC_OTHER]);

#[cfg(all(feature = "dma", feature = "thumbv7"))]
declare_interrupts!(DMAC_OTHER);

#[cfg(all(feature = "dma", feature = "thumbv6"))]
declare_interrupts!(DMAC);

Expand Down Expand Up @@ -121,12 +159,16 @@ seq!(N in 0..= 15 {
}
});

/// Interrupt source. This trait may implemented directly when multiple
/// interrupt sources are needed to operate a single peripheral (eg, SERCOM and
/// DMAC for thumbv7 devices). If using one interrupt source per peripheral,
/// An interrupt source that may have one or many interrupt bindings.
///
/// This trait may implemented directly when multiple interrupt sources are
/// needed to operate a single peripheral (eg, SERCOM and DMAC for thumbv7
/// devices). If using one interrupt source per peripheral,
/// implement [`Interrupt`] instead. When implemented on a type that handles
/// multiple interrupt sources, the methods will act on all interrupt sources at
/// once.
///
/// May not be implemented outside of this HAL.
pub trait InterruptSource: crate::typelevel::Sealed {
/// Enable the interrupt.
///
Expand Down Expand Up @@ -165,13 +207,14 @@ impl<T: Interrupt> InterruptSource for T {

/// Type-level interrupt.
///
/// This trait is implemented for all typelevel single interrupt types in this
/// module.
/// This trait is implemented for all typelevel single interrupt types defined
/// in this module. May not be implemented outside of this HAL.
pub trait Interrupt: crate::typelevel::Sealed {
/// Interrupt enum variant.
///
/// This allows going from typelevel interrupts (one type per interrupt) to
/// non-typelevel interrupts (a single `Interrupt` enum type, with one
/// This allows going from typelevel interrupts (one type per interrupt,
/// defined in [`this module`](self)) to non-typelevel interrupts (a
/// single [`Interrupt`](crate::pac::Interrupt) enum type, with one
/// variant per interrupt).
const IRQ: crate::pac::Interrupt;

Expand Down Expand Up @@ -227,20 +270,24 @@ pub trait Interrupt: crate::typelevel::Sealed {
Self::IRQ.set_priority(prio)
}

/// Set the interrupt priority with an already-acquired critical section
/// Set the interrupt priority with an already-acquired critical section.
///
/// Equivalent to [`set_priority`](Self::set_priority), except you pass a
/// [`CriticalSection`] to prove you've already acquired a critical
/// section. This prevents acquiring another one, which saves code size.
#[inline]
fn set_priority_with_cs(cs: critical_section::CriticalSection, prio: Priority) {
Self::IRQ.set_priority_with_cs(cs, prio)
}
}

/// Interrupt handler trait.
/// Interrupt handler.
///
/// Drivers that need to handle interrupts implement this trait.
/// The user must ensure `on_interrupt()` is called every time the interrupt
/// fires. Drivers must use use [`Binding`] to assert at compile time that the
/// user has done so.
pub trait Handler<I: InterruptSource> {
pub trait Handler<I: InterruptSource>: Sealed {
/// Interrupt handler function.
///
/// Must be called every time the `I` interrupt fires, synchronously from
Expand All @@ -265,9 +312,11 @@ pub trait Handler<I: InterruptSource> {
/// This allows drivers to check bindings at compile-time.
pub unsafe trait Binding<I: InterruptSource, H: Handler<I>> {}

/// Represents an interrupt type that can be configured by the HAL to handle
/// An interrupt type that can be configured by the HAL to handle
/// interrupts.
pub unsafe trait InterruptExt: InterruptNumber + Copy {
///
/// The PAC defined enum-level interrupts implement this trait.
pub trait InterruptExt: InterruptNumber + Copy {
/// Enable the interrupt.
///
/// # Safety
Expand Down Expand Up @@ -338,11 +387,11 @@ pub unsafe trait InterruptExt: InterruptNumber + Copy {
}
}

/// Set the interrupt priority with an already-acquired critical section
/// Set the interrupt priority with an already-acquired critical section.
///
/// Equivalent to `set_priority`, except you pass a `CriticalSection` to
/// prove you've already acquired a critical section. This prevents
/// acquiring another one, which saves code size.
/// Equivalent to [`set_priority`](Self::set_priority), except you pass a
/// [`CriticalSection`] to prove you've already acquired a critical
/// section. This prevents acquiring another one, which saves code size.
#[inline]
fn set_priority_with_cs(self, _cs: CriticalSection, prio: Priority) {
unsafe {
Expand All @@ -352,7 +401,7 @@ pub unsafe trait InterruptExt: InterruptNumber + Copy {
}
}

unsafe impl<T: InterruptNumber + Copy> InterruptExt for T {}
impl<T: InterruptNumber + Copy> InterruptExt for T {}

#[cfg(feature = "thumbv6")]
const NVIC_PRIO_BITS: u8 = 2;
Expand Down Expand Up @@ -419,8 +468,9 @@ impl Priority {
/// * 0x00,
/// * 0x20,
/// * 0x40,
/// * and so on.
///
/// and so on. Any other value will cause a panic. To save yourself some
/// Any other value will cause a panic. To save yourself some
/// trouble, use this method only with hardware priority values gotten
/// directly from the NVIC.
#[inline]
Expand Down
Loading

0 comments on commit 52286af

Please sign in to comment.