diff --git a/CHANGELOG.md b/CHANGELOG.md index 492d498..0769efe 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,7 +11,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/). ### Added - DelayUs and DelayMs trait implementations for timer -- SPI implementation for blocking API +- SPI implementation for blocking API, supports blockmode as well ## [0.2.1] diff --git a/examples/spi.rs b/examples/spi.rs index e714a6e..e1c31cc 100644 --- a/examples/spi.rs +++ b/examples/spi.rs @@ -31,11 +31,11 @@ pub enum SpiBusSelect { SpiBPortB, } -const EXAMPLE_SEL: ExampleSelect = ExampleSelect::Loopback; +const EXAMPLE_SEL: ExampleSelect = ExampleSelect::TestBuffer; const SPI_BUS_SEL: SpiBusSelect = SpiBusSelect::SpiBPortB; const SPI_SPEED_KHZ: u32 = 1000; const SPI_MODE: Mode = MODE_0; -const BLOCKMODE: bool = false; +const BLOCKMODE: bool = true; #[entry] fn main() -> ! { @@ -45,14 +45,17 @@ fn main() -> ! { let spia_ref: RefCell>> = RefCell::new(None); let spib_ref: RefCell>> = RefCell::new(None); + let pinsa = PinsA::new(&mut dp.SYSCONFIG, None, dp.PORTA); + let pinsb = PinsB::new(&mut dp.SYSCONFIG, Some(dp.IOCONFIG), dp.PORTB); + let mut spi_cfg = spi::SpiConfig::default(); if EXAMPLE_SEL == ExampleSelect::Loopback { spi_cfg = spi_cfg.loopback(true) } + // Set up the SPI peripheral match SPI_BUS_SEL { SpiBusSelect::SpiAPortA => { - let pinsa = PinsA::new(&mut dp.SYSCONFIG, None, dp.PORTA); let (sck, mosi, miso) = ( pinsa.pa31.into_funsel_1(), pinsa.pa30.into_funsel_1(), @@ -71,7 +74,6 @@ fn main() -> ! { ); } SpiBusSelect::SpiAPortB => { - let pinsb = PinsB::new(&mut dp.SYSCONFIG, Some(dp.IOCONFIG), dp.PORTB); let (sck, mosi, miso) = ( pinsb.pb9.into_funsel_2(), pinsb.pb8.into_funsel_2(), @@ -90,7 +92,6 @@ fn main() -> ! { ); } SpiBusSelect::SpiBPortB => { - let pinsb = PinsB::new(&mut dp.SYSCONFIG, Some(dp.IOCONFIG), dp.PORTB); let (sck, mosi, miso) = ( pinsb.pb5.into_funsel_1(), pinsb.pb4.into_funsel_1(), @@ -109,19 +110,41 @@ fn main() -> ! { ); } } + // Configure transfer specific properties here + match SPI_BUS_SEL { + SpiBusSelect::SpiAPortA | SpiBusSelect::SpiAPortB => { + if let Some(ref mut spi) = *spia_ref.borrow_mut() { + let transfer_cfg = TransferConfig::::new( + SPI_SPEED_KHZ.khz().into(), + SPI_MODE, + None, + BLOCKMODE, + false, + ); + spi.cfg_transfer(&transfer_cfg); + } + } + SpiBusSelect::SpiBPortB => { + if let Some(ref mut spi) = *spib_ref.borrow_mut() { + let hw_cs_pin = pinsb.pb2.into_funsel_1(); + let transfer_cfg = TransferConfig::new( + SPI_SPEED_KHZ.khz().into(), + SPI_MODE, + Some(hw_cs_pin), + BLOCKMODE, + false, + ); + spi.cfg_transfer(&transfer_cfg); + } + } + } + + // Application logic let mut delay_tim = CountDownTimer::tim1(&mut dp.SYSCONFIG, 50.mhz().into(), dp.TIM1); loop { match SPI_BUS_SEL { SpiBusSelect::SpiAPortA | SpiBusSelect::SpiAPortB => { if let Some(ref mut spi) = *spia_ref.borrow_mut() { - let transfer_cfg: TransferConfig = TransferConfig { - spi_clk: SPI_SPEED_KHZ.khz().into(), - blockmode: BLOCKMODE, - hw_cs: None, - mode: SPI_MODE, - sod: false, - }; - spi.cfg_transfer(&transfer_cfg); if EXAMPLE_SEL == ExampleSelect::Loopback { nb::block!(spi.send(0x42_u8)).unwrap(); let word = nb::block!(spi.read()).unwrap(); @@ -134,7 +157,7 @@ fn main() -> ! { assert_eq!(reply, &[0x03, 0x02, 0x01]); delay_tim.delay_ms(500_u32); } else { - let mut send_buf: [u8; 3] = [0x00, 0x01, 0x02]; + let mut send_buf: [u8; 3] = [0x01, 0x02, 0x03]; let reply = spi.transfer(&mut send_buf).unwrap(); rprintln!("Received reply: {}, {}, {}", reply[0], reply[1], reply[2]); delay_tim.delay_ms(1000_u32); @@ -143,14 +166,6 @@ fn main() -> ! { } SpiBusSelect::SpiBPortB => { if let Some(ref mut spi) = *spib_ref.borrow_mut() { - let transfer_cfg: TransferConfig = TransferConfig { - spi_clk: SPI_SPEED_KHZ.khz().into(), - blockmode: BLOCKMODE, - hw_cs: None, - mode: SPI_MODE, - sod: false, - }; - spi.cfg_transfer(&transfer_cfg); if EXAMPLE_SEL == ExampleSelect::Loopback { nb::block!(spi.send(0x42_u8)).unwrap(); let word = nb::block!(spi.read()).unwrap(); @@ -163,7 +178,7 @@ fn main() -> ! { assert_eq!(reply, &[0x03, 0x02, 0x01]); delay_tim.delay_ms(500_u32); } else { - let mut send_buf: [u8; 3] = [0x00, 0x01, 0x02]; + let mut send_buf: [u8; 3] = [0x01, 0x02, 0x03]; let reply = spi.transfer(&mut send_buf).unwrap(); rprintln!("Received reply: {}, {}, {}", reply[0], reply[1], reply[2]); delay_tim.delay_ms(1000_u32); diff --git a/src/gpio/dynpins.rs b/src/gpio/dynpins.rs index 8a1dd9f..28b7a1f 100644 --- a/src/gpio/dynpins.rs +++ b/src/gpio/dynpins.rs @@ -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, diff --git a/src/gpio/mod.rs b/src/gpio/mod.rs index 53dbe5f..9b7bba4 100644 --- a/src/gpio/mod.rs +++ b/src/gpio/mod.rs @@ -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. diff --git a/src/gpio/pins.rs b/src/gpio/pins.rs index 79d4a30..6b8a0fe 100644 --- a/src/gpio/pins.rs +++ b/src/gpio/pins.rs @@ -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 { cfg: PhantomData, } @@ -543,7 +546,8 @@ pub type SpecificPin

= Pin<

::Id,

::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> { diff --git a/src/spi.rs b/src/spi.rs index 65c6325..e0016c5 100644 --- a/src/spi.rs +++ b/src/spi.rs @@ -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 { pub spi_clk: Hertz, @@ -189,9 +195,30 @@ pub struct TransferConfig { /// false pub hw_cs: Option, 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 TransferConfig { + pub fn new( + spi_clk: Hertz, + mode: Mode, + hw_cs: Option, + blockmode: bool, + sod: bool, + ) -> Self { + TransferConfig { + spi_clk, + mode, + hw_cs, + sod, + blockmode, + } + } +} + impl GenericTransferConfig for TransferConfig { /// Slave Output Disable fn sod(&mut self, sod: bool) { @@ -211,32 +238,6 @@ impl GenericTransferConfig for TransferConfig { } } -/// Configuration options for a single transfer. These can be used to reconfigure the SPI bus -/// for transaction to different devices -impl> TransferConfig { - pub fn cfg_spia(spi_clk: Hertz, mode: Mode, hw_cs: Option) -> Self { - TransferConfig { - spi_clk, - mode, - hw_cs, - sod: false, - blockmode: false, - } - } -} - -impl> TransferConfig { - pub fn cfg_spib(spi_clk: Hertz, mode: Mode, hw_cs: Option) -> 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: SPI, cfg: SpiConfig, sys_clk: Hertz, + blockmode: bool, _word: PhantomData, } pub struct Spi { @@ -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>(&mut self, transfer_cfg: &TransferConfig) { 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 , Miso: PinMiso<$SPIX>, Mosi: PinMosi<$SPIX>> From> 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 , Miso: PinMiso<$SPIX>, Mosi: PinMosi<$SPIX>> From> 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, 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(); diff --git a/src/typelevel.rs b/src/typelevel.rs index 256a336..2d77e9a 100644 --- a/src/typelevel.rs +++ b/src/typelevel.rs @@ -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 /// `::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