Skip to content

Commit

Permalink
Improve docs, minor refactor
Browse files Browse the repository at this point in the history
  • Loading branch information
jbeaurivage committed Nov 25, 2024
1 parent e79628b commit 5f14e99
Show file tree
Hide file tree
Showing 6 changed files with 147 additions and 40 deletions.
2 changes: 1 addition & 1 deletion hal/src/dmac/dma_controller.rs
Original file line number Diff line number Diff line change
Expand Up @@ -112,7 +112,7 @@ macro_rules! define_channels_struct {
seq!(N in 0..$num_channels {
#(
/// Type alias for a channel number
pub struct Ch~N;
pub enum Ch~N {}

impl ChId for Ch~N {
const U8: u8 = N;
Expand Down
158 changes: 135 additions & 23 deletions hal/src/peripherals/eic.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,67 @@
//! # External Interrupt Controller
//!
//! This module provides typesafe APIs for interacting with the EIC peripheral,
//! which is used to generate interrupts based on the state of a GPIO.
//!
//! Each chip has a number of EXTINT channels:
//!
//! * SAMD11: 8 channels
//! * SAMD21/SAMx5x: 16 channels
//!
//! Each channel can operate independently, and sense state changes for a single
//! GPIO pin at a time. Refer to the datasheet for GPIO pin/EXTINT channel
//! compatibility. In this module, an [`ExtInt`] represents an EXTINT channel
//! which is tied to a GPIO [`Pin`], and is capable of sensing state changes.
//!
//! ## Steps to create an [`ExtInt`]
//!
//! 1. Start by creating an [`Eic`] struct, by calling [`Eic::new`]. This
//! initializes the EIC peripheral and sets up the correct clocking.
//!
//! 1. Turn the [`Eic`] into a tuple of [`Channel`]s by calling [`Eic::split`].
//! Each channel represents a single EXTINT channel.
//!
//! 1. Assign a pin to a channel by calling [`Channel::with_pin`]. This returns
//! a fully configured and ready to use [`ExtInt`]. A [`Pin`] can also be
//! directly converted into an [`ExtInt`] by calling one of the methods
//! provided by the [`EicPin`] trait.
//!
//! ### Example setup
//!
//! ```no_run
//! let eic_clock = clocks.eic(&gclk0).unwrap();
//! // Initialize the EIC peripheral
//! let eic = Eic::new(&mut peripherals.pm, eic_clock, peripherals.eic);
//! // Split into channels
//! let eic_channels = eic.split();
//!
//! // Take the pin that we want to use
//! let button: Pin<_, PullUpInterrupt> = pins.d10.into();
//!
//! // Turn the EXTINT[2] channel into an ExtInt struct
//! let mut extint = eic_channels.2.with_pin(button);
//! ```
//!
//! ## `async` operation <span class="stab portability" title="Available on crate feature `async` only"><code>async</code></span>
//!
//! [`ExtInt`]s can be used for async operations. Configuring the [`Eic`] in
//! async mode is relatively simple:
//!
//! * Bind the corresponding `EIC` interrupt source to the SPI
//! [`InterruptHandler`] (refer to the module-level
//! [`async_hal`](crate::async_hal) documentation for more information).
//! *
//! * SAMD11/SAMD21: Turn an [`Eic`] into an async-enabled [`Eic`] by
//! calling [`Eic::into_future`]. Since there is only a single interrupt
//! handler for the EIC peripheral, all EXTINT channels must be turned
//! into async channels at once.
//! * SAMx5x: Turn an individuel [`ExtInt`] into an async-enabled [`ExtInt`]
//! by calling [`ExtInt::into_future`]. Each channel has a dedicated
//! interrupt source, therefore you must individually choose which
//! channels to turn into async channels.
//! * Use the provided [`wait`](ExtInt::wait) method. async-enabled [`ExtInt`]s
//! also implement [`embedded_hal_async::digital::Wait`].
use core::marker::PhantomData;

use atsamd_hal_macros::{hal_cfg, hal_module};
Expand All @@ -15,16 +79,17 @@ use crate::{
"eic-d5x" => "eic/d5x/mod.rs",
)]
mod impls {}
pub use impls::*;
#[cfg(feature = "async")]
pub use impls::async_api::*;

pub type Sense = pac::eic::config::Sense0select;

/// Trait representing a EIC EXTINT channel ID
/// Trait representing an EXTINT channel ID.
pub trait ChId {
const ID: usize;
}

/// Marker struct that represents an EXTINT channel capable of doing async
/// Marker type that represents an EXTINT channel capable of doing async
/// operations.
#[cfg(feature = "async")]
pub enum EicFuture {}
Expand All @@ -51,31 +116,33 @@ pub trait EicPin: AnyPin + Sealed {
fn into_pull_down_ei(self, chan: Channel<Self::ChId>) -> Self::PullDown;
}

/// Represents a numbered external interrupt. The external interrupt is generic
/// over any pin, only the EicPin implementations in this module make sense.
/// A numbered external interrupt, which can be used to sense state changes on
/// its pin.
pub struct ExtInt<P, Id, F = NoneT>
where
P: EicPin,
Id: ChId,
{
chan: Channel<Id, F>,
_pin: Pin<P::Id, P::Mode>,
pin: Pin<P::Id, P::Mode>,
}

// impl !Send for [<$PadType $num>]<GPIO> {}
// impl !Sync for [<$PadType $num>]<GPIO> {}

impl<P, Id, F> ExtInt<P, Id, F>
where
P: EicPin,
Id: ChId,
{
/// Release the underlying resources: [`Pin`] and [`Channel`].
pub fn free(self) -> (Pin<P::Id, P::Mode>, Channel<Id, F>) {
(self.pin, self.chan)
}

/// Construct pad from the appropriate pin in any mode.
/// You may find it more convenient to use the `into_pad` trait
/// and avoid referencing the pad type.
fn new(pin: P, chan: Channel<Id, F>) -> Self {
ExtInt {
_pin: pin.into(),
pin: pin.into(),
chan,
}
}
Expand Down Expand Up @@ -117,9 +184,11 @@ impl<Id: ChId, F> Channel<Id, F> {
}
}

/// External Interrupt Controller. Use [`split`](Self::split) to split the
/// struct into individual channels, which can then be used to create
/// [`ExtInt`]s, by calling [`Channel::with_pin`].
/// External Interrupt Controller.
///
/// Use [`split`](Self::split) to split the struct into individual channels,
/// which can then be used to create [`ExtInt`]s, by calling
/// [`Channel::with_pin`].
pub struct Eic<I = NoneT> {
eic: pac::Eic,
_irqs: PhantomData<I>,
Expand Down Expand Up @@ -153,29 +222,71 @@ impl Eic {
pub fn new(mclk: &mut pac::Mclk, _clock: EicClock, eic: pac::Eic) -> Self {
mclk.apbamask().modify(|_, w| w.eic_().set_bit());

let mut eic = Self {
eic,
_irqs: PhantomData,
};

// Reset the EIC
eic.ctrla().modify(|_, w| w.swrst().set_bit());
while eic.syncbusy().read().swrst().bit_is_set() {
core::hint::spin_loop();
}
eic.swreset();

// Use the low-power 32k clock and enable.
eic.ctrla().modify(|_, w| {
eic.eic.ctrla().modify(|_, w| {
w.cksel().set_bit();
w.enable().set_bit()
});

while eic.syncbusy().read().enable().bit_is_set() {
while eic.eic.syncbusy().read().enable().bit_is_set() {
core::hint::spin_loop();
}

Self {
eic,
_irqs: PhantomData,
eic
}

/// Release the EIC and return the register block.
///
/// **Note**: The [`Channels`] struct is consumed by this method. This means
/// that any [`Channel`] obtained by [`split`](Eic::split) must be
/// moved back into the [`Channels`] struct before being able to pass it
/// into [`free`](Eic::free).
pub fn free(mut self, _channels: Channels) -> pac::Eic {
self.swreset();

self.eic
}
}

impl<F> Eic<F> {
/// Reset the EIC
#[atsamd_hal_macros::hal_macro_helper]
fn swreset(&mut self) {
#[hal_cfg(any("eic-d11", "eic-d21"))]
let ctrl = self.eic.ctrl();

#[hal_cfg("eic-d5x")]
let ctrl = self.eic.ctrla();

ctrl.modify(|_, w| w.swrst().set_bit());
while ctrl.read().swrst().bit_is_set() {
core::hint::spin_loop();
}
}
}

#[cfg(feature = "async")]
impl Eic<EicFuture> {
/// Release the EIC and return the register block.
///
/// **Note**: The [`Channels`] struct is consumed by this method. This means
/// that any [`Channel`] obtained by [`split`](Eic::split) must be
/// moved back into the [`Channels`] struct before being able to pass it
/// into [`free`](Eic::free).
pub fn free(mut self, _channels: FutureChannels) -> pac::Eic {
self.swreset();
self.eic
}
}

#[hal_cfg("eic-d11")]
macro_rules! with_num_channels {
($some_macro:ident) => {
Expand All @@ -196,14 +307,15 @@ macro_rules! get {
};
}

/// The number of EXTINT channels on this chip.
pub const NUM_CHANNELS: usize = with_num_channels!(get);

macro_rules! define_channels_struct {
($num_channels:literal) => {
seq!(N in 0..$num_channels {
#(
/// Type alias for a channel number
pub struct Ch~N;
pub enum Ch~N {}

impl ChId for Ch~N {
const ID: usize = N;
Expand Down
7 changes: 2 additions & 5 deletions hal/src/peripherals/eic/d11/mod.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
pub mod pin;
mod pin;

#[cfg(feature = "async")]
mod async_api {
pub(super) mod async_api {
use crate::async_hal::interrupts::{Binding, Handler, Interrupt, EIC as EicInterrupt};
use crate::eic::{Eic, EicFuture, NUM_CHANNELS};
use crate::pac;
Expand Down Expand Up @@ -53,6 +53,3 @@ mod async_api {
const NEW_WAKER: AtomicWaker = AtomicWaker::new();
pub(super) static WAKERS: [AtomicWaker; NUM_CHANNELS] = [NEW_WAKER; NUM_CHANNELS];
}

#[cfg(feature = "async")]
pub use async_api::*;
4 changes: 2 additions & 2 deletions hal/src/peripherals/eic/d11/pin.rs
Original file line number Diff line number Diff line change
Expand Up @@ -142,11 +142,11 @@ where
type Error = core::convert::Infallible;
#[inline]
fn is_high(&self) -> Result<bool, Self::Error> {
self._pin.is_high()
self.pin.is_high()
}
#[inline]
fn is_low(&self) -> Result<bool, Self::Error> {
self._pin.is_low()
self.pin.is_low()
}
}

Expand Down
8 changes: 3 additions & 5 deletions hal/src/peripherals/eic/d5x/mod.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
use super::{ChId, Channel};
use crate::pac;

pub mod pin;
mod pin;

impl<Id: ChId, F> Channel<Id, F> {
/// Run the provided closure with the EIC peripheral disabled. The
Expand Down Expand Up @@ -29,13 +29,14 @@ impl<Id: ChId, F> Channel<Id, F> {
}

#[cfg(feature = "async")]
mod async_api {
pub(super) mod async_api {
use super::*;
use crate::async_hal::interrupts::Handler;
use crate::eic::NUM_CHANNELS;
use crate::util::BitIter;
use embassy_sync::waitqueue::AtomicWaker;

/// Interrupt handler used for `async` operations.
pub struct InterruptHandler {
_private: (),
}
Expand Down Expand Up @@ -66,6 +67,3 @@ mod async_api {
const NEW_WAKER: AtomicWaker = AtomicWaker::new();
pub(super) static WAKERS: [AtomicWaker; NUM_CHANNELS] = [NEW_WAKER; NUM_CHANNELS];
}

#[cfg(feature = "async")]
pub use async_api::*;
8 changes: 4 additions & 4 deletions hal/src/peripherals/eic/d5x/pin.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ use crate::eic::*;
use crate::gpio::{
self, pin::*, AnyPin, FloatingInterrupt, PinMode, PullDownInterrupt, PullUpInterrupt,
};
use crate::typelevel::NoneT;

/// The pad macro defines the given EIC pin and implements EicPin for the
/// given pins. The EicPin implementation will configure the pin for the
Expand Down Expand Up @@ -166,11 +165,11 @@ where
type Error = core::convert::Infallible;
#[inline]
fn is_high(&self) -> Result<bool, Self::Error> {
self._pin.is_high()
self.pin.is_high()
}
#[inline]
fn is_low(&self) -> Result<bool, Self::Error> {
self._pin.is_low()
self.pin.is_low()
}
}

Expand All @@ -184,6 +183,7 @@ mod async_impls {
use crate::{
async_hal::interrupts::{Binding, Handler, InterruptSource},
eic::EicFuture,
typelevel::NoneT,
};

use super::{
Expand All @@ -207,7 +207,7 @@ mod async_impls {
unsafe { P::InterruptSource::enable() };

ExtInt {
_pin: self._pin,
pin: self.pin,
chan: self.chan.change_mode(),
}
}
Expand Down

0 comments on commit 5f14e99

Please sign in to comment.