Doc fixes and SPI block mode support

- Various smaller fixes for documentation
- Added support for SPI blockmode
This commit is contained in:
2021-11-21 00:20:35 +01:00
parent 186e7192cb
commit 283d9d5991
7 changed files with 114 additions and 76 deletions

View File

@ -55,7 +55,7 @@
//! `Error = core::convert::Infallible`, the value-level API can return a real
//! error. If the [`DynPin`] is not in the correct [`DynPinMode`] for the
//! operation, the trait functions will return
//! [`InvalidPinType`](Error::InvalidPinType).
//! [`InvalidPinType`](PinError::InvalidPinType).
use super::pins::{
common_reg_if_functions, FilterType, InterruptEdge, InterruptLevel, Pin, PinError, PinId,

View File

@ -3,8 +3,8 @@
//! The implementation of this GPIO module is heavily based on the
//! [ATSAMD HAL implementation](https://docs.rs/atsamd-hal/0.13.0/atsamd_hal/gpio/v2/index.html).
//!
//! This API provides two different submodules, [`pins`] and [`dynpins`],
//! representing two different ways to handle GPIO pins. The default, [`pins`],
//! This API provides two different submodules, [`mod@pins`] and [`dynpins`],
//! representing two different ways to handle GPIO pins. The default, [`mod@pins`],
//! is a type-level API that tracks the state of each pin at compile-time. The
//! alternative, [`dynpins`] is a type-erased, value-level API that tracks the
//! state of each pin at run-time.

View File

@ -26,16 +26,17 @@
//! }
//! ```
//!
//! A `PinId` identifies a pin by it's group (A, B) and pin number. Each
//! `PinId` instance is named according to its datasheet identifier, e.g.
//! [`PA02`].
//! A [`PinId`] identifies a pin by it's group (A, B) and pin number. Each
//! [`PinId`] instance is named according to its datasheet identifier, e.g.
//! PA02.
//!
//! A `PinMode` represents the various pin modes. The available `PinMode`
//! variants are [`Input`], [`Output`] and [`Alternate`], each with its own corresponding
//! configurations.
//!
//! It is not possible for users to create new instances of a [`Pin`]. Singleton
//! instances of each pin are made available to users through the [`Pins`]
//! instances of each pin are made available to users through the [`PinsA`] and
//! [`PinsB`]
//! struct.
//!
//! To create the [`PinsA`] or [`PinsB`] struct, users must supply the PAC
@ -84,7 +85,8 @@
//! This module also provides additional, type-level tools to work with GPIO
//! pins.
//!
//! The [`AnyPin`] trait defines an [`AnyKind`] type class
//! The [`AnyPin`] trait defines an
//! [`AnyKind`](https://docs.rs/atsamd-hal/0.13.0/atsamd_hal/typelevel/index.html) type class
//! for all `Pin` types.
use super::dynpins::{DynAlternate, DynGroup, DynInput, DynOutput, DynPinId, DynPinMode};
@ -229,7 +231,8 @@ impl OutputConfig for ReadableOpenDrain {
/// Type-level variant of [`PinMode`] for output modes
///
/// Type `C` is one of two output configurations: [`PushPull`] or [`Readable`]
/// Type `C` is one of four output configurations: [`PushPull`], [`OpenDrain`] or
/// their respective readable versions
pub struct Output<C: OutputConfig> {
cfg: PhantomData<C>,
}
@ -543,7 +546,8 @@ pub type SpecificPin<P> = Pin<<P as AnyPin>::Id, <P as AnyPin>::Mode>;
/// Type class for [`Pin`] types
///
/// This trait uses the [`AnyKind`] trait pattern to create a [type class] for
/// This trait uses the [`AnyKind`](https://docs.rs/atsamd-hal/0.13.0/atsamd_hal/typelevel/index.html)
/// trait pattern to create a [type class] for
/// [`Pin`] types. See the `AnyKind` documentation for more details on the
/// pattern.
pub trait AnyPin: Is<Type = SpecificPin<Self>> {

View File

@ -1,4 +1,8 @@
//! API for the SPI peripheral
//!
//! ## Examples
//!
//! - [Blocking SPI example](https://github.com/robamu-org/va108xx-hal-rs/blob/main/examples/spi.rs)
use crate::Sealed;
use crate::{
clock::{enable_peripheral_clock, PeripheralClocks},
@ -181,6 +185,8 @@ pub trait GenericTransferConfig {
fn frequency(&mut self, spi_clk: Hertz);
}
/// This struct contains all configuration parameter which are transfer specific
/// and might change for transfers to different SPI slaves
#[derive(Copy, Clone)]
pub struct TransferConfig<HWCS> {
pub spi_clk: Hertz,
@ -189,9 +195,30 @@ pub struct TransferConfig<HWCS> {
/// false
pub hw_cs: Option<HWCS>,
pub sod: bool,
/// If this is enabled, all data in the FIFO is transmitted in a single frame unless
/// the BMSTOP bit is set on a dataword. A frame is defined as CSn being active for the
/// duration of multiple data words
pub blockmode: bool,
}
impl<HWCS> TransferConfig<HWCS> {
pub fn new(
spi_clk: Hertz,
mode: Mode,
hw_cs: Option<HWCS>,
blockmode: bool,
sod: bool,
) -> Self {
TransferConfig {
spi_clk,
mode,
hw_cs,
sod,
blockmode,
}
}
}
impl<HWCS> GenericTransferConfig for TransferConfig<HWCS> {
/// Slave Output Disable
fn sod(&mut self, sod: bool) {
@ -211,32 +238,6 @@ impl<HWCS> GenericTransferConfig for TransferConfig<HWCS> {
}
}
/// Configuration options for a single transfer. These can be used to reconfigure the SPI bus
/// for transaction to different devices
impl<HWCS: OptionalHwCs<SPIA>> TransferConfig<HWCS> {
pub fn cfg_spia(spi_clk: Hertz, mode: Mode, hw_cs: Option<HWCS>) -> Self {
TransferConfig {
spi_clk,
mode,
hw_cs,
sod: false,
blockmode: false,
}
}
}
impl<HWCS: OptionalHwCs<SPIB>> TransferConfig<HWCS> {
pub fn cfg_spib(spi_clk: Hertz, mode: Mode, hw_cs: Option<HWCS>) -> Self {
TransferConfig {
spi_clk,
mode,
hw_cs,
sod: false,
blockmode: false,
}
}
}
#[derive(Default)]
/// Configuration options for the whole SPI bus. See Programmer Guide p.92 for more details
pub struct SpiConfig {
@ -301,6 +302,7 @@ pub struct SpiBase<SPI, Word = u8> {
spi: SPI,
cfg: SpiConfig,
sys_clk: Hertz,
blockmode: bool,
_word: PhantomData<Word>,
}
pub struct Spi<SPI, PINS, Word = u8> {
@ -330,7 +332,8 @@ macro_rules! spi {
/// )
/// ```
///
/// in the function call
/// in the function call. You can delete the pin type information by calling
/// the [`downgrade`](Self::downgrade) function
///
/// ## Arguments
/// * `transfer_cfg` - Transfer configuration which includes configuration
@ -359,12 +362,14 @@ macro_rules! spi {
let mut mode = MODE_0;
let mut clk_prescale = 0x02;
let mut ss = 0;
let mut init_blockmode = false;
if let Some(transfer_cfg) = transfer_cfg {
mode = transfer_cfg.mode;
clk_prescale = sys_clk.0 / (transfer_cfg.spi_clk.0 * (scrdv as u32 + 1));
if transfer_cfg.hw_cs.is_some() {
ss = HwCs::CS_ID as u8;
}
init_blockmode = transfer_cfg.blockmode;
}
let (cpo_bit, cph_bit) = match mode {
@ -404,6 +409,7 @@ macro_rules! spi {
spi,
cfg: spi_cfg,
sys_clk,
blockmode: init_blockmode,
_word: PhantomData,
},
pins,
@ -470,6 +476,7 @@ macro_rules! spi {
pub fn cfg_transfer<HwCs: OptionalHwCs<$SPIX>>(&mut self, transfer_cfg: &TransferConfig<HwCs>) {
self.cfg_clock(transfer_cfg.spi_clk);
self.cfg_mode(transfer_cfg.mode);
self.blockmode = transfer_cfg.blockmode;
self.spi.ctrl1.modify(|_, w| {
if transfer_cfg.sod {
w.sod().set_bit();
@ -491,7 +498,7 @@ macro_rules! spi {
}
}
// Changing the word size also requires a type conversion
/// Changing the word size also requires a type conversion
impl <Sck: PinSck<$SPIX>, Miso: PinMiso<$SPIX>, Mosi: PinMosi<$SPIX>>
From<Spi<$SPIX, (Sck, Miso, Mosi), u8>> for Spi<$SPIX, (Sck, Miso, Mosi), u16>
{
@ -507,6 +514,7 @@ macro_rules! spi {
spi_base: SpiBase {
spi: old_spi.spi_base.spi,
cfg: old_spi.spi_base.cfg,
blockmode: old_spi.spi_base.blockmode,
sys_clk: old_spi.spi_base.sys_clk,
_word: PhantomData,
},
@ -515,7 +523,7 @@ macro_rules! spi {
}
}
// Changing the word size also requires a type conversion
/// Changing the word size also requires a type conversion
impl <Sck: PinSck<$SPIX>, Miso: PinMiso<$SPIX>, Mosi: PinMosi<$SPIX>>
From<Spi<$SPIX, (Sck, Miso, Mosi), u16>> for
Spi<$SPIX, (Sck, Miso, Mosi), u8>
@ -532,6 +540,7 @@ macro_rules! spi {
spi_base: SpiBase {
spi: old_spi.spi_base.spi,
cfg: old_spi.spi_base.cfg,
blockmode: old_spi.spi_base.blockmode,
sys_clk: old_spi.spi_base.sys_clk,
_word: PhantomData,
},
@ -546,15 +555,6 @@ macro_rules! spi {
{
type Error = Infallible;
/// Read a word from the slave. Must be preceeded by a [`send`] call
#[inline(always)]
fn read(&mut self) -> nb::Result<$WORD, Self::Error> {
if self.spi.status.read().rne().bit_is_clear() {
return Err(nb::Error::WouldBlock);
}
Ok((self.spi.data.read().bits() & 0xffff) as $WORD)
}
/// Sends a word to the slave
#[inline(always)]
fn send(&mut self, word: $WORD) -> nb::Result<(), Self::Error> {
@ -564,6 +564,15 @@ macro_rules! spi {
self.spi.data.write(|w| unsafe { w.bits(word as u32) });
Ok(())
}
/// Read a word from the slave. Must be preceeded by a [`send`](Self::send) call
#[inline(always)]
fn read(&mut self) -> nb::Result<$WORD, Self::Error> {
if self.spi.status.read().rne().bit_is_clear() {
return Err(nb::Error::WouldBlock);
}
Ok((self.spi.data.read().bits() & 0xffff) as $WORD)
}
}
impl<Sck: PinSck<$SPIX>, Miso: PinMiso<$SPIX>, Mosi: PinMosi<$SPIX>>
@ -592,15 +601,25 @@ macro_rules! spi {
write_words: &'w [$WORD],
read_words: Option<&'w mut [$WORD]>,
) -> Result<(), Infallible> {
// FIFO has a depth of 16. Only fill the first half for now
const FIFO_WORDS: usize = 8;
// FIFO has a depth of 16.
const FIFO_WORDS: usize = 12;
if self.blockmode {
self.spi.ctrl1.modify(|_, w| {
w.mtxpause().set_bit()
})
}
// Fill the first half of the write FIFO
let len = write_words.len();
let mut write = write_words.iter();
for _ in 0..core::cmp::min(FIFO_WORDS, len) {
nb::block!(self.send(*write.next().unwrap())).ok().unwrap();
}
if self.blockmode {
self.spi.ctrl1.modify(|_, w| {
w.mtxpause().clear_bit()
})
}
if let Some(read) = read_words {
let mut read = read.iter_mut();

View File

@ -12,8 +12,8 @@ impl Sealed for NoneT {}
/// Marker trait for type identity
///
/// This trait is used as part of the [`AnyKind`] trait pattern. It represents
/// the concept of type identity, because all implementors have
/// This trait is used as part of the [`AnyKind`](https://docs.rs/atsamd-hal/0.13.0/atsamd_hal/typelevel/index.html)
/// trait pattern. It represents the concept of type identity, because all implementors have
/// `<Self as Is>::Type == Self`. When used as a trait bound with a specific
/// type, it guarantees that the corresponding type parameter is exactly the
/// specific type. Stated differently, it guarantees that `T == Specific` in