Skip to content

Commit

Permalink
Document async sercom implementations
Browse files Browse the repository at this point in the history
  • Loading branch information
jbeaurivage committed Jan 8, 2024
1 parent e486158 commit 2f0732b
Show file tree
Hide file tree
Showing 8 changed files with 133 additions and 53 deletions.
8 changes: 5 additions & 3 deletions hal/src/sercom/dma.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,10 @@ use crate::{
// I2C DMA transfers
//=============================================================================

/// Token type representing an [`I2c`](super::i2c::I2c) for which the bus is
/// ready to start a transaction. For use with
/// Token type representing an [`I2c`] for which the bus is
/// ready to start a transaction.
///
/// For use with
/// [`send_with_dma`](super::i2c::I2c::send_with_dma) and
/// [`receive_with_dma`](super::i2c::I2c::send_with_dma).
pub struct I2cBusReady;
Expand Down Expand Up @@ -59,7 +61,7 @@ impl<C: i2c::AnyConfig> I2c<C> {
/// # fn init_transfer<A: i2c::AnyConfig, C: AnyChannel<dmac::Ready>>(i2c: I2c<A>, chan0: C, buf_src: &'static mut [u8]){
/// // Assume `i2c` is a fully configured `I2c`, and `chan0` a fully configured `dmac::Channel`.
/// let token = i2c.init_dma_transfer()?;
/// i2c.send_with_dma(ADDRESS, token, buf_src, chan0, |_| {});
/// i2c.send_with_dma(ADDRESS, token, buf_src, chan0);
/// # }
/// ```
///
Expand Down
20 changes: 20 additions & 0 deletions hal/src/sercom/i2c.rs
Original file line number Diff line number Diff line change
Expand Up @@ -254,6 +254,26 @@ fn i2c_send_with_dma<A: AnyConfig, C: AnyChannel<Status = Ready>>(i2c: I2c<A>, c
"
)]
//! # `async` operation
//!
//! When the `async` Cargo feature is enabled, an [`I2c`] can be used for
//! `async` operations. Configuring an [`I2c`] in async mode is relatively
//! simple:
//!
//! * Bind the corresponding `SERCOM` interrupt source to the SPI
//! [`InterruptHandler`] (refer to the module-level [`async_hal`]
//! documentation for more information).
//! * Turn a previously configured [`I2c`] into an [`I2cFuture`] by calling
//! [`I2c::into_future`]
//! * Optionally, add a DMA channel by using [`I2cFuture::with_dma_channel`].
//! The API is exactly the same whether a DMA channel is used or not.
//! * Use the provided async methods for reading or writing to the I2C
//! peripheral. [`I2cFuture`] implements [`embedded_hal_async::i2c::I2c`].
//!
//! `I2cFuture` implements `AsRef<I2c>` and `AsMut<I2c>` so
//! that it can be reconfigured using the regular [`I2c`] methods.
//!
//! [`async_hal`]: crate::async_hal

#[cfg(feature = "thumbv6")]
#[path = "i2c/pads_thumbv6m.rs"]
Expand Down
27 changes: 10 additions & 17 deletions hal/src/sercom/i2c/async_api.rs
Original file line number Diff line number Diff line change
Expand Up @@ -67,8 +67,9 @@ where
}

#[cfg(feature = "dma")]
/// Convenience type for a [`I2cFuture`] in DMA
/// mode. The type parameter `I` represents the DMA channel ID (`ChX`).
/// Convenience type for a [`I2cFuture`] in DMA mode.
///
/// The type parameter `I` represents the DMA channel ID (`ChX`).
pub type I2cFutureDma<C, I> = I2cFuture<C, crate::dmac::Channel<I, crate::dmac::ReadyFuture>>;

impl<C, S, D> I2cFuture<C, D>
Expand Down Expand Up @@ -174,9 +175,10 @@ where
Ok(())
}

/// Asynchronously write from a buffer, then read into a buffer. This is an
/// extremely common pattern: writing a register address, then
/// read its value from the slave.
/// Asynchronously write from a buffer, then read into a buffer.
///
/// This is an extremely common pattern: for example, writing a register
/// address, then read its value from the slave.
#[inline]
pub async fn write_read(
&mut self,
Expand All @@ -195,17 +197,6 @@ where
}
}

// impl<C, N, D> Drop for I2cFuture<C, N, D>
// where
// C: AnyConfig,
// N: InterruptNumber,
// {
// #[inline]
// fn drop(&mut self) {
// cortex_m::peripheral::NVIC::mask(self.irq_number);
// }
// }

impl<C, N> AsRef<I2c<C>> for I2cFuture<C, N>
where
C: AnyConfig,
Expand Down Expand Up @@ -394,7 +385,9 @@ mod dma {
}

/// Asynchronously write from a buffer, then read into a buffer, all
/// using DMA. This is an extremely common pattern: writing a
/// using DMA.
///
/// This is an extremely common pattern: for example, writing a
/// register address, then read its value from the slave.
#[inline]
pub async fn write_read(
Expand Down
2 changes: 1 addition & 1 deletion hal/src/sercom/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,7 @@ pub trait Sercom: Sealed + Deref<Target = sercom0::RegisterBlock> {
fn enable_apb_clock(&mut self, ctrl: &APB_CLK_CTRL);

/// Get a reference to the sercom from a
/// [`Peripherals`](crate::pac::Peripherals) block
/// [`Peripherals`] block
fn reg_block(peripherals: &mut Peripherals) -> &crate::pac::sercom0::RegisterBlock;

/// Get a reference to this [`Sercom`]'s associated RX Waker
Expand Down
27 changes: 27 additions & 0 deletions hal/src/sercom/spi.rs
Original file line number Diff line number Diff line change
Expand Up @@ -311,6 +311,27 @@ let (chan0, _, spi, _) = dma_transfer.wait();
[`dmac`]: crate::dmac
"
)]
//! # `async` operation
//!
//! When the `async` Cargo feature is enabled, a [`Spi`] can be used for
//! `async` operations. Configuring a [`Spi`] in async mode is relatively
//! simple:
//!
//! * Bind the corresponding `SERCOM` interrupt source to the SPI
//! [`InterruptHandler`] (refer to the module-level [`async_hal`]
//! documentation for more information).
//! * Turn a previously configured [`Spi`] into a [`SpiFuture`] by calling
//! [`Spi::into_future`]
//! * Optionally, add DMA channels to RX, TX or both using
//! [`SpiFuture::with_rx_dma_channel`] and [`SpiFuture::with_tx_dma_channel`].
//! The API is exactly the same whether DMA channels are used or not.
//! * Use the provided async methods for reading or writing to the SPI
//! peripheral. [`SpiFuture`] implements [`embedded_hal_async::spi::SpiBus`].
//!
//! `SpiFuture` implements `AsRef<Spi>` and `AsMut<Spi>` so
//! that it can be reconfigured using the regular [`Spi`] methods.
//!
//! [`async_hal`]: crate::async_hal

use core::convert::TryFrom;
use core::marker::PhantomData;
Expand Down Expand Up @@ -758,6 +779,8 @@ where

/// Obtain a reference to the PAC `SERCOM` struct
///
/// # Safety
///
/// Directly accessing the `SERCOM` could break the invariants of the
/// type-level tracking in this module, so it is unsafe.
#[inline]
Expand Down Expand Up @@ -1278,6 +1301,8 @@ where

/// Read from the DATA register
///
/// # Safety
///
/// Reading from the data register directly is `unsafe`, because it will
/// clear the RXC flag, which could break assumptions made elsewhere in
/// this module.
Expand All @@ -1288,6 +1313,8 @@ where

/// Write to the DATA register
///
/// # Safety
///
/// Writing to the data register directly is `unsafe`, because it will clear
/// the DRE flag, which could break assumptions made elsewhere in this
/// module.
Expand Down
40 changes: 24 additions & 16 deletions hal/src/sercom/spi/async_api.rs
Original file line number Diff line number Diff line change
Expand Up @@ -51,10 +51,12 @@ where
A: Capability,
S: Sercom,
{
/// Turn an [`Spi`] into a [`SpiFuture`]. In cases where the underlying
/// [`Spi`] is [`Duplex`], reading words need to be accompanied with sending
/// a no-op word. By default it is set to 0x00, but you can configure it
/// by using the [`nop_word`](SpiFuture::nop_word) method.
/// Turn an [`Spi`] into a [`SpiFuture`].
///
/// In cases where the underlying [`Spi`] is [`Duplex`], reading words need
/// to be accompanied with sending a no-op word. By default it is set to
/// 0x00, but you can configure it by using the
/// [`nop_word`](SpiFuture::nop_word) method.
#[inline]
pub fn into_future<I>(self, _interrupts: I) -> SpiFuture<C, A>
where
Expand Down Expand Up @@ -110,7 +112,9 @@ pub type SpiFutureTx<C> = SpiFuture<C, Tx>;

#[cfg(feature = "dma")]
/// Convenience type for a [`SpiFuture`] with RX and TX capabilities in DMA
/// mode. The type parameter `R` represents the RX DMA channel ID (`ChX`), and
/// mode.
///
/// The type parameter `R` represents the RX DMA channel ID (`ChX`), and
/// `T` represents the TX DMA channel ID.
pub type SpiFutureDuplexDma<C, R, T> = SpiFuture<
C,
Expand All @@ -120,14 +124,16 @@ pub type SpiFutureDuplexDma<C, R, T> = SpiFuture<
>;

#[cfg(feature = "dma")]
/// Convenience type for a [`SpiFuture`] with RX capabilities in DMA mode. The
/// type parameter `R` represents the RX DMA channel ID (`ChX`).
/// 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<C, R> =
SpiFuture<C, Rx, crate::dmac::Channel<R, crate::dmac::ReadyFuture>, NoneT>;

#[cfg(feature = "dma")]
/// Convenience type for a [`SpiFuture`] with TX capabilities in DMA mode. The
/// type parameter `T` represents the TX DMA channel ID (`ChX`).
/// 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<C, T> =
SpiFuture<C, Tx, NoneT, crate::dmac::Channel<T, crate::dmac::ReadyFuture>>;

Expand Down Expand Up @@ -187,10 +193,11 @@ where
DataWidth: AsPrimitive<C::Word>,
S: Sercom,
{
/// Read words into a buffer asynchronously, word by word. Since we are
/// using a [`Duplex`] [`SpiFuture`], we need to send a word simultaneously
/// while receiving one. This `no-op` word is configurable via the
/// [`nop_word`](Self::nop_word) method.
/// Read words into a buffer asynchronously, word by word.
///
/// Since we are using a [`Duplex`] [`SpiFuture`], we need to send a word
/// simultaneously while receiving one. This `no-op` word is
/// configurable via the [`nop_word`](Self::nop_word) method.
#[inline]
pub async fn read(&mut self, buffer: &mut [C::Word]) -> Result<(), Error> {
for byte in buffer.iter_mut() {
Expand Down Expand Up @@ -235,9 +242,10 @@ where
Ok(word)
}

/// Perform a transfer, word by word. No-op words will be written if `read`
/// is longer than `write`. Extra words are ignored if `write` is longer
/// than `read`.
/// Perform a transfer, word by word.
///
/// No-op words will be written if `read` is longer than `write`. Extra
/// words are ignored if `write` is longer than `read`.
async fn transfer_word_by_word(
&mut self,
read: &mut [C::Word],
Expand Down
35 changes: 29 additions & 6 deletions hal/src/sercom/uart.rs
Original file line number Diff line number Diff line change
Expand Up @@ -386,6 +386,30 @@ let (chan1, rx, rx_buffer) = rx_dma.wait();
"
)]
//! # `async` operation
//!
//! When the `async` Cargo feature is enabled, a [`Uart`] can be used for
//! `async` operations. Configuring a [`Uart`] in async mode is relatively
//! simple:
//!
//! * Bind the corresponding `SERCOM` interrupt source to the UART
//! [`InterruptHandler`] (refer to the module-level [`async_hal`]
//! documentation for more information).
//! * Turn a previously configured [`Uart`] into a [`UartFuture`] by calling
//! [`Uart::into_future`]
//! * Optionally, add DMA channels to RX, TX or both using
//! [`UartFuture::with_rx_dma_channel`] and
//! [`UartFuture::with_tx_dma_channel`]. The API is exactly the same whether
//! DMA channels are used or not.
//! * Use the provided async methods for reading or writing to the UART
//! peripheral.
//!
//! `UartFuture` implements `AsRef<Uart>` and `AsMut<Uart>` so
//! that it can be reconfigured using the regular [`Uart`] methods. It also
//! exposes a [`split`](UartFuture::split) method to split it into its RX and TX
//! parts.
//!
//! [`async_hal`]: crate::async_hal

#[cfg(feature = "thumbv6")]
#[path = "uart/pads_thumbv6m.rs"]
Expand Down Expand Up @@ -943,13 +967,12 @@ where
#[inline]
pub fn flush_rx_buffer(&mut self) {
// TODO Is this a hardware bug???
/*
usart.ctrlb.modify(|_, w| w.rxen().clear_bit());
while usart.syncbusy.read().ctrlb().bit() || usart.ctrlb.read().rxen().bit_is_set() {}

usart.ctrlb.modify(|_, w| w.rxen().set_bit());
while usart.syncbusy.read().ctrlb().bit() || usart.ctrlb.read().rxen().bit_is_clear() {}
*/
// usart.ctrlb.modify(|_, w| w.rxen().clear_bit());
// while usart.syncbusy.read().ctrlb().bit() || usart.ctrlb.read().rxen().bit_is_set() {}

// usart.ctrlb.modify(|_, w| w.rxen().set_bit());
// while usart.syncbusy.read().ctrlb().bit() || usart.ctrlb.read().rxen().bit_is_clear() {}

for _ in 0..=2 {
let _data = unsafe { self.config.as_mut().registers.read_data() };
Expand Down
27 changes: 17 additions & 10 deletions hal/src/sercom/uart/async_api.rs
Original file line number Diff line number Diff line change
Expand Up @@ -54,8 +54,8 @@ where
S: Sercom,
{
/// Turn a [`Uart`] into a [`UartFuture`]. This method is only available for
/// [`Uart`]s which have a [`Tx`](crate::sercom::uart::Tx),
/// [`Rx`](crate::sercom::uart::Rx) or [`Duplex`] [`Capability`].
/// [`Uart`]s which have a [`Tx`],
/// [`Rx`] or [`Duplex`] [`Capability`].
#[inline]
pub fn into_future<I>(self, _interrupts: I) -> UartFuture<C, D>
where
Expand Down Expand Up @@ -84,9 +84,9 @@ where
}
}

/// `async` version of [`Uart`].
/// `async` version of a [`Uart`].
///
/// Create this struct by calling [`I2c::into_future`](I2c::into_future).
/// Create this struct by calling [`Uart::into_future`](Uart::into_future).
pub struct UartFuture<C, D, R = NoneT, T = NoneT>
where
C: ValidConfig,
Expand Down Expand Up @@ -114,7 +114,9 @@ pub type UartFutureTxDuplex<C> = UartFuture<C, TxDuplex>;

#[cfg(feature = "dma")]
/// Convenience type for a [`UartFuture`] with RX and TX capabilities in DMA
/// mode. The type parameter `R` represents the RX DMA channel ID (`ChX`), and
/// mode.
///
/// The type parameter `R` represents the RX DMA channel ID (`ChX`), and
/// `T` represents the TX DMA channel ID.
pub type UartFutureDuplexDma<C, R, T> = UartFuture<
C,
Expand All @@ -125,24 +127,28 @@ pub type UartFutureDuplexDma<C, R, T> = UartFuture<

#[cfg(feature = "dma")]
/// Convenience type for a RX-only [`UartFuture`] in DMA mode.
///
/// The type parameter `R` represents the RX DMA channel ID (`ChX`).
pub type UartFutureRxDma<C, R> =
UartFuture<C, Rx, crate::dmac::Channel<R, crate::dmac::ReadyFuture>, NoneT>;

#[cfg(feature = "dma")]
/// 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<C, R> =
UartFuture<C, RxDuplex, crate::dmac::Channel<R, crate::dmac::ReadyFuture>, NoneT>;

#[cfg(feature = "dma")]
/// Convenience type for a TX-only [`UartFuture`] in DMA mode.
///
/// The type parameter `T` represents the TX DMA channel ID (`ChX`).
pub type UartFutureTxDma<C, T> =
UartFuture<C, Tx, NoneT, crate::dmac::Channel<T, crate::dmac::ReadyFuture>>;

#[cfg(feature = "dma")]
/// 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<C, T> =
UartFuture<C, TxDuplex, NoneT, crate::dmac::Channel<T, crate::dmac::ReadyFuture>>;
Expand Down Expand Up @@ -246,7 +252,7 @@ where
S: Sercom,
DataReg: AsPrimitive<C::Word>,
{
/// Add a DMA channel for receiving words
/// Use a DMA channel for receiving words on the RX line
#[cfg(feature = "dma")]
#[inline]
pub fn with_rx_dma_channel<Chan: crate::dmac::AnyChannel<Status = crate::dmac::ReadyFuture>>(
Expand Down Expand Up @@ -277,9 +283,10 @@ where
DataReg: AsPrimitive<C::Word>,
{
/// Read the specified number of [`Word`](crate::sercom::uart::Word)s into a
/// buffer, word by word. In case of an error, returns `Err(Error, usize)`
/// where the `usize` represents the number of valid words read before
/// the error occured.
/// buffer, word by word.
///
/// In case of an error, returns `Err(Error, usize)` where the `usize`
/// represents the number of valid words read before the error occured.
#[inline]
pub async fn read(&mut self, buffer: &mut [C::Word]) -> Result<(), (Error, usize)> {
for (i, word) in buffer.iter_mut().enumerate() {
Expand All @@ -302,7 +309,7 @@ where
D: Transmit,
S: Sercom,
{
/// Add a DMA channel for sending words
/// Use a DMA channel for sending words on the TX line
#[cfg(feature = "dma")]
#[inline]
pub fn with_tx_dma_channel<Chan: crate::dmac::AnyChannel<Status = crate::dmac::ReadyFuture>>(
Expand Down

0 comments on commit 2f0732b

Please sign in to comment.