diff --git a/src/adc.rs b/src/adc.rs index 1df4623..d2bbf73 100644 --- a/src/adc.rs +++ b/src/adc.rs @@ -852,7 +852,7 @@ impl Channel for TempSensor { } // Setup PDC Rx functionality -pdc_rx! { Adc: adc } +pdc_rx! { Adc: adc, isr } impl Adc { /// Configures the ADC with PDC in single sequence mode @@ -1075,7 +1075,13 @@ where Transfer::w(buffer, self) } +} +impl ReadDmaPaused for AdcDma +where + Self: TransferPayload, + B: StaticWriteBuffer, +{ /// Assigns the buffer, prepares PDC but does not enable the PDC /// Useful when there is strict timing on when the ADC conversion should start /// diff --git a/src/lib.rs b/src/lib.rs index fc046af..7afb8f6 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -88,11 +88,11 @@ pub mod clock; pub mod delay; pub mod efc; pub mod gpio; -#[cfg(feature = "atsam4s")] pub mod pdc; pub mod prelude; pub mod rtt; pub mod serial; +pub mod spi; pub mod static_memory_controller; pub mod timer; #[cfg(all(feature = "usb", any(feature = "atsam4e", feature = "atsam4s")))] diff --git a/src/pdc.rs b/src/pdc.rs index 4cadbd5..ff77b8e 100644 --- a/src/pdc.rs +++ b/src/pdc.rs @@ -20,6 +20,16 @@ pub struct RxDma { pub(crate) payload: PAYLOAD, } +/// DMA Transmitter +pub struct TxDma { + pub(crate) payload: PAYLOAD, +} + +/// DMA Receiver/Transmitter +pub struct RxTxDma { + pub(crate) payload: PAYLOAD, +} + pub trait Receive { type TransmittedWord; } @@ -35,6 +45,14 @@ where Self: core::marker::Sized + TransferPayload, { fn read(self, buffer: B) -> Transfer; +} + +/// Trait for DMA readings from peripheral to memory, start paused. +pub trait ReadDmaPaused: Receive +where + B: StaticWriteBuffer, + Self: core::marker::Sized + TransferPayload, +{ fn read_paused(self, buffer: B) -> Transfer; } @@ -48,7 +66,7 @@ where } /// Trait for DMA simultaneously reading and writing within one synchronous operation. Panics if both buffers are not of equal length. -pub trait ReadWriteDma: Transmit +pub trait ReadWriteDma: Transmit + Receive where RXB: StaticWriteBuffer, TXB: StaticReadBuffer, @@ -57,6 +75,23 @@ where fn read_write(self, rx_buffer: RXB, tx_buffer: TXB) -> Transfer; } +/// Trait for manually specifying the DMA length used, even if the buffer is larger +/// Panics if the buffer(s) are too small +pub trait ReadWriteDmaLen: Transmit + Receive +where + RXB: StaticWriteBuffer, + TXB: StaticReadBuffer, + Self: core::marker::Sized + TransferPayload, +{ + fn read_write_len( + self, + rx_buffer: RXB, + rx_buf_len: usize, + tx_buffer: TXB, + tx_buf_len: usize, + ) -> Transfer; +} + pub trait TransferPayload { fn start(&mut self); fn stop(&mut self); @@ -72,7 +107,6 @@ where payload: PAYLOAD, } -/* TODO Needed for pdc_tx impl Transfer where PAYLOAD: TransferPayload, @@ -85,7 +119,6 @@ where } } } -*/ impl Transfer where @@ -110,57 +143,67 @@ where } } -impl Transfer> -where - RxDma: TransferPayload, -{ - pub fn is_done(&self) -> bool { - !self.payload.in_progress() - } - - pub fn wait(mut self) -> (BUFFER, RxDma) { - while !self.is_done() {} - - atomic::compiler_fence(Ordering::Acquire); +macro_rules! pdc_transfer { + ( + $DmaType:ident + ) => { + impl Transfer> + where + $DmaType: TransferPayload, + { + pub fn is_done(&self) -> bool { + !self.payload.in_progress() + } - self.payload.stop(); + pub fn wait(mut self) -> (BUFFER, $DmaType) { + while !self.is_done() {} + + atomic::compiler_fence(Ordering::Acquire); + + self.payload.stop(); + + // we need a read here to make the Acquire fence effective + // we do *not* need this if `dma.stop` does a RMW operation + unsafe { + ptr::read_volatile(&0); + } + + // we need a fence here for the same reason we need one in `Transfer.wait` + atomic::compiler_fence(Ordering::Acquire); + + // `Transfer` needs to have a `Drop` implementation, because we accept + // managed buffers that can free their memory on drop. Because of that + // we can't move out of the `Transfer`'s fields, so we use `ptr::read` + // and `mem::forget`. + // + // NOTE(unsafe) There is no panic branch between getting the resources + // and forgetting `self`. + unsafe { + let buffer = ptr::read(&self.buffer); + let payload = ptr::read(&self.payload); + mem::forget(self); + (buffer, payload) + } + } - // we need a read here to make the Acquire fence effective - // we do *not* need this if `dma.stop` does a RMW operation - unsafe { - ptr::read_volatile(&0); - } + pub fn pause(&mut self) { + self.payload.stop(); + } - // we need a fence here for the same reason we need one in `Transfer.wait` - atomic::compiler_fence(Ordering::Acquire); - - // `Transfer` needs to have a `Drop` implementation, because we accept - // managed buffers that can free their memory on drop. Because of that - // we can't move out of the `Transfer`'s fields, so we use `ptr::read` - // and `mem::forget`. - // - // NOTE(unsafe) There is no panic branch between getting the resources - // and forgetting `self`. - unsafe { - let buffer = ptr::read(&self.buffer); - let payload = ptr::read(&self.payload); - mem::forget(self); - (buffer, payload) + pub fn resume(&mut self) { + self.payload.start(); + } } - } - - pub fn pause(&mut self) { - self.payload.stop(); - } - - pub fn resume(&mut self) { - self.payload.start(); - } + }; } +pdc_transfer!(RxDma); +pdc_transfer!(TxDma); +pdc_transfer!(RxTxDma); + macro_rules! pdc_rx { ( - $Periph:ident: $periph:ident + $Periph:ident: $periph:ident, $isr:ident ) => { impl $Periph { /// Sets the PDC receive address pointer @@ -209,7 +252,7 @@ macro_rules! pdc_rx { /// Returns `true` if DMA is still in progress /// Uses rxbuff, which checks both receive and receive next counters to see if they are 0 pub fn rx_in_progress(&self) -> bool { - !self.$periph.isr.read().rxbuff().bit() + !self.$periph.$isr.read().rxbuff().bit() } /// Enable ENDRX (End of Receive) interrupt @@ -238,29 +281,28 @@ macro_rules! pdc_rx { } pub(crate) use pdc_rx; -/* TODO Commenting out until first usage (likely SPI) macro_rules! pdc_tx { ( - $Periph:ident: $periph:ident + $Periph:ident: $periph:ident, $isr:ident ) => { impl $Periph { /// Sets the PDC transmit address pointer pub fn set_transmit_address(&mut self, address: u32) { self.$periph - .rpr + .tpr .write(|w| unsafe { w.txptr().bits(address) }); } /// Sets the transmit increment counter /// Will increment by the count * size of the peripheral data pub fn set_transmit_counter(&mut self, count: u16) { - self.$periph.rcr.write(|w| unsafe { w.txctr().bits(count) }); + self.$periph.tcr.write(|w| unsafe { w.txctr().bits(count) }); } /// Sets the PDC transmit next address pointer pub fn set_transmit_next_address(&mut self, address: u32) { self.$periph - .rnpr + .tnpr .write(|w| unsafe { w.txnptr().bits(address) }); } @@ -268,18 +310,22 @@ macro_rules! pdc_tx { /// Will increment by the count * size of the peripheral data pub fn set_transmit_next_counter(&mut self, count: u16) { self.$periph - .rncr + .tncr .write(|w| unsafe { w.txnctr().bits(count) }); } /// Starts the PDC transfer pub fn start_tx_pdc(&mut self) { - self.$periph.ptcr.write_with_zero(|w| w.txten().set_bit()); + unsafe { + self.$periph.ptcr.write_with_zero(|w| w.txten().set_bit()); + } } /// Stops the PDC transfer pub fn stop_tx_pdc(&mut self) { - self.$periph.ptcr.write_with_zero(|w| w.txtdis().set_bit()); + unsafe { + self.$periph.ptcr.write_with_zero(|w| w.txtdis().set_bit()); + } } /// Returns `true` if the PDC is active and may be receiving data @@ -290,32 +336,66 @@ macro_rules! pdc_tx { /// Returns `true` if DMA is still in progress /// Uses rxbuff, which checks both transmit and transmit next counters to see if they are 0 pub fn tx_in_progress(&self) -> bool { - !self.$periph.isr.read().txbufe().bit() + !self.$periph.$isr.read().txbufe().bit() } /// Enable ENDRX (End of Transmit) interrupt /// Triggered when RCR reaches 0 - pub fn enable_endrx_interrupt(&mut self) { - self.$periph.ier.write_with_zero(|w| w.endtx().set_bit()); + pub fn enable_endtx_interrupt(&mut self) { + unsafe { + self.$periph.ier.write_with_zero(|w| w.endtx().set_bit()); + } } /// Disable ENDRX (End of Transmit) interrupt - pub fn disable_endrx_interrupt(&mut self) { - self.$periph.idr.write_with_zero(|w| w.endtx().set_bit()); + pub fn disable_endtx_interrupt(&mut self) { + unsafe { + self.$periph.idr.write_with_zero(|w| w.endtx().set_bit()); + } } /// Enable RXBUFF (Transmit Buffer Full) interrupt /// Triggered when RCR and RNCR reach 0 - pub fn enable_rxbuff_interrupt(&mut self) { - self.$periph.ier.write_with_zero(|w| w.txbufe().set_bit()); + pub fn enable_txbufe_interrupt(&mut self) { + unsafe { + self.$periph.ier.write_with_zero(|w| w.txbufe().set_bit()); + } } /// Disable RXBUFF (Transmit Buffer Full) interrupt - pub fn disable_rxbuff_interrupt(&mut self) { - self.$periph.idr.write_with_zero(|w| w.txbufe().set_bit()); + pub fn disable_txbufe_interrupt(&mut self) { + unsafe { + self.$periph.idr.write_with_zero(|w| w.txbufe().set_bit()); + } } } }; } pub(crate) use pdc_tx; -*/ + +macro_rules! pdc_rxtx { + ( + $Periph:ident: $periph:ident + ) => { + impl $Periph { + /// Starts the PDC transfer (rx+tx) + pub fn start_rxtx_pdc(&mut self) { + unsafe { + self.$periph + .ptcr + .write_with_zero(|w| w.txten().set_bit().rxten().set_bit()); + } + } + + /// Stops the PDC transfer (rx+tx) + pub fn stop_rxtx_pdc(&mut self) { + unsafe { + self.$periph + .ptcr + .write_with_zero(|w| w.txtdis().set_bit().rxtdis().set_bit()); + } + } + } + }; +} +pub(crate) use pdc_rxtx; diff --git a/src/spi.rs b/src/spi.rs new file mode 100644 index 0000000..41671ff --- /dev/null +++ b/src/spi.rs @@ -0,0 +1,1000 @@ +//! SPI Implementation +use crate::clock::{get_master_clock_frequency, Enabled, SpiClock}; +use crate::gpio::{Pa12, Pa13, Pa14, PfA}; +use crate::pac::SPI; +use crate::pdc::*; +use core::marker::PhantomData; +use core::sync::atomic::{compiler_fence, Ordering}; +use embedded_dma::{StaticReadBuffer, StaticWriteBuffer}; +use paste::paste; + +pub use embedded_hal::spi; +pub use embedded_time::rate::{Hertz, Kilohertz, Megahertz}; + +/// u8 that can convert back and forth with u16 +/// Needed for some of the register bit fields +#[derive(Copy, Clone)] +#[repr(transparent)] +pub struct SpiU8(u8); + +impl From for SpiU8 { + fn from(val: u8) -> Self { + Self(val) + } +} + +impl From for SpiU8 { + // Yes this will lose bits; however in this mode only the first 8bits are used + fn from(val: u16) -> Self { + Self(val as _) + } +} + +impl From for SpiU8 { + fn from(val: SpiU16) -> Self { + Self(val.0 as _) + } +} + +impl From for u8 { + fn from(val: SpiU8) -> Self { + val.0 as _ + } +} + +/// u16 that can convert back and forth with u8 +/// Needed for some of the register bit fields +#[derive(Copy, Clone)] +#[repr(transparent)] +pub struct SpiU16(u16); + +impl From for SpiU16 { + fn from(val: u8) -> Self { + Self(val as _) + } +} + +impl From for SpiU16 { + fn from(val: u16) -> Self { + Self(val) + } +} + +impl From for SpiU16 { + fn from(val: SpiU8) -> Self { + Self(val.0 as _) + } +} + +impl From for u16 { + fn from(val: SpiU16) -> Self { + val.0 as _ + } +} + +/// SPI Error +#[derive(Clone, Copy, Debug, PartialEq, Eq, defmt::Format)] +pub enum Error { + /// Overrun occurred + Overrun, + /// Underrun occurred (slave mode only) + Underrun, + /// Mode fault occurred + ModeFault, + /// SPI Disabled + SpiDisabled, + /// Invalid Chip Select + InvalidCs(u8), + /// Fixed Mode Set + FixedModeSet, + /// Variable Mode Set + VariableModeSet, + /// PCS read unexpected (data, pcs) + UnexpectedPcs(u16, u8), +} + +/// Chip Select Active Settings +/// This enum controls: +/// CNSAAT -> Chip Select Not Active After Transfer +/// CSAAT -> Chip Select Active After Transfer +#[derive(Clone, Copy, Debug, PartialEq, Eq, defmt::Format)] +pub enum ChipSelectActive { + /// csaat = 1, csnaat = 0 + ActiveAfterTransfer, + /// csaat = 0, csnaat = 0 + ActiveOnConsecutiveTransfers, + /// csaat = 0, csnaat = 1 + InactiveAfterEachTransfer, +} + +/// Transfer Width +/// NOTE: Transfer Widths larger than 8-bits require using 16-bit with send/read +#[derive(Clone, Copy, Debug, PartialEq, Eq, defmt::Format)] +pub enum BitWidth { + Width8Bit = 0, + Width9Bit = 1, + Width10Bit = 2, + Width11Bit = 3, + Width12Bit = 4, + Width13Bit = 5, + Width14Bit = 6, + Width15Bit = 7, + Width16Bit = 8, +} + +/// Peripheral Select Mode +#[derive(Clone, Copy, Debug, PartialEq, Eq, defmt::Format)] +pub enum PeripheralSelectMode { + /// Fixed Peripheral Select Mode (ps = 0, pcsdec = 0) + Fixed, + /// Variable Peripheral Select Mode (ps = 1, pcsdec = 0) + Variable, + /// Chip Select Decode (Variable) (ps = 1, pcsdec = 1) + ChipSelectDecode, +} + +/// SPI Chip Select Settings +/// +/// CPOL -> MODE +/// NCPHA -> MODE +/// CSNAAT -> CS not active after transfer (ignored if CSAAT = 1) => csa +/// CSAAT -> CS active after transfer => csa +/// BITS -> 8bit through 16bits +/// SCBR -> Serial clock rate (SCBR = f_periph / SPCK bit rate) (0 forbidden) +/// DLYBS -> Delay before SPCK (DLYBS x f_periph) +/// DLYBCT -> Delay between consecutive transfers (DLYBCT x f_periph / 32) +#[derive(Clone, PartialEq, Eq)] +pub struct ChipSelectSettings { + mode: spi::Mode, + csa: ChipSelectActive, + scbr: u8, + dlybs: u8, + dlybct: u8, + bits: BitWidth, +} + +impl ChipSelectSettings { + /// mode: SPI Mode + /// csa: Chip Select behaviour after transfer + /// bits: SPI bit width + /// baud: SPI speed in Hertz + /// dlybs: Cycles to delay from CS to first valid SPCK + /// 0 is half the SPCK clock period + /// Otherwise dlybs = Delay Before SPCK x f_periph + /// dlybct: Cycles to delay between consecutive transfers + /// 0 is no delay + /// Otherwise dlybct = Delay between consecutive transfers x f_periph / 32 + pub fn new( + mode: spi::Mode, + csa: ChipSelectActive, + bits: BitWidth, + baud: Hertz, + dlybs: u8, + dlybct: u8, + ) -> ChipSelectSettings { + let pclk = get_master_clock_frequency(); + + // Calculate baud divider + // (f_periph + baud - 1) / baud + let scbr = ((pclk.0 + baud.0 - 1) / baud.0) as u8; + if scbr < 1 { + panic!("scbr must be greater than 0: {}", scbr); + } + + ChipSelectSettings { + mode, + csa, + scbr, + dlybs, + dlybct, + bits, + } + } +} + +/// SPI Master +/// +/// Example on how to individually read/write to SPI CS channels +/// ``` +/// use atsam4_hal::clock::{ClockController, MainClock, SlowClock}; +/// use atsam4_hal::pac::Peripherals; +/// +/// let peripherals = Peripherals::take().unwrap(); +/// let clocks = ClockController::new( +/// peripherals.PMC, +/// &peripherals.SUPC, +/// &peripherals.EFC0, +/// MainClock::Crystal12Mhz, +/// SlowClock::RcOscillator32Khz, +/// ); +/// let gpio_ports = Ports::new( +/// ( +/// peripherals.PIOA, +/// clocks.peripheral_clocks.pio_a.into_enabled_clock(), +/// ), +/// ( +/// peripherals.PIOB, +/// clocks.peripheral_clocks.pio_b.into_enabled_clock(), +/// ), +/// ); +/// let mut pins = Pins::new(gpio_ports, &peripherals.MATRIX) +/// +/// // Setup SPI Master +/// let wdrbt = false; // Wait data read before transfer enabled +/// let llb = false; // Local loopback +/// // Cycles to delay between consecutive transfers +/// let dlybct = 0; // No delay +/// // SpiU8 can be used as we're only using 8-bit SPI +/// // SpiU16 can be used for 8 to 16-bit SPI +/// let mut spi = SpiMaster::::new( +/// cx.device.SPI, +/// clocks.peripheral_clocks.spi.into_enabled_clock(), +/// pins.spi_miso, +/// pins.spi_mosi, +/// pins.spi_sck, +/// spi::PeripheralSelectMode::Variable, +/// wdrbt, +/// llb, +/// dlybct, +/// ); +/// +/// // Setup CS0 channel +/// let mode = spi::spi::MODE_3; +/// let csa = spi::ChipSelectActive::ActiveAfterTransfer; +/// let bits = spi::BitWidth::Width8Bit; +/// let baud = spi::Hertz(12_000_000_u32); // 12 MHz +/// // Cycles to delay from CS to first valid SPCK +/// let dlybs = 0; // Half an SPCK clock period +/// let cs_settings = spi::ChipSelectSettings::new(mode, csa, bits, baud, dlybs, dlybct); +/// spi.cs_setup(0, cs_settings.clone()).unwrap(); +/// +/// // Enable CS0 +/// spi.cs_select(0).unwrap(); +/// +/// // Write value +/// let val: u8 = 0x39; +/// spi.send(val.into()).unwrap(); +/// +/// // Read value +/// let val = match spi.read() { +/// Ok(val) => val, +/// _ => {} +/// }; +/// ``` +pub struct SpiMaster { + spi: SPI, + clock: PhantomData>, + miso: PhantomData>, + mosi: PhantomData>, + spck: PhantomData>, + cs: u8, + lastxfer: bool, + framesize: PhantomData, +} + +impl SpiMaster { + /// Initialize SPI as Master + /// PSM - Peripheral Select Mode + /// WDRBT - Wait Data Read Before Transfer Enabled + /// LLB - Local Loopback + /// DLYBCS - Delay between chip selects = DLYBCS / f_periph + #[allow(clippy::too_many_arguments)] + pub fn new( + spi: SPI, + _clock: SpiClock, + _miso: Pa12, + _mosi: Pa13, + _spck: Pa14, + psm: PeripheralSelectMode, + wdrbt: bool, + llb: bool, + dlybcs: u8, + ) -> SpiMaster { + unsafe { + // Disable SPI + spi.cr.write_with_zero(|w| w.spidis().set_bit()); + + // Software reset SPI (this will reset SPI into Slave Mode) + spi.cr.write_with_zero(|w| w.swrst().set_bit()); + + // Enable SPI + spi.cr.write_with_zero(|w| w.spien().set_bit()); + + // Determine peripheral select mode + let (ps, pcsdec) = match psm { + PeripheralSelectMode::Fixed => (false, false), + PeripheralSelectMode::Variable => (true, false), + PeripheralSelectMode::ChipSelectDecode => (true, true), + }; + + // Setup SPI Master + // Master Mode + // Variable Peripheral Select (more flexible and less initial options to set) + // Mode Fault Detection Enabled + spi.mr.write_with_zero(|w| { + w.mstr() + .set_bit() + .ps() + .bit(ps) + .pcsdec() + .bit(pcsdec) + .modfdis() + .clear_bit() + .wdrbt() + .bit(wdrbt) + .llb() + .bit(llb) + .dlybcs() + .bits(dlybcs) + }); + } + + SpiMaster { + spi, + clock: PhantomData, + miso: PhantomData, + mosi: PhantomData, + spck: PhantomData, + cs: 0, // Default to NPCS0 + lastxfer: false, // Reset to false on each call to send() + framesize: PhantomData, + } + } + + /// Apply settings to a specific channel + /// Uses cs 0..3 for spi channel settings + /// When using pcsdec (Chip Decode Select) + /// csr0 -> 0..3 + /// csr1 -> 4..7 + /// csr2 -> 8..11 + /// csr3 -> 12..14 + pub fn cs_setup(&mut self, cs: u8, settings: ChipSelectSettings) -> Result<(), Error> { + // Lookup cs when using pcsdec + let cs = if self.spi.mr.read().pcsdec().bit_is_set() { + match cs { + 0..=3 => 0, + 4..=7 => 1, + 8..=11 => 2, + 12..=14 => 3, + _ => { + return Err(Error::InvalidCs(cs)); + } + } + + // Otherwise validate the cs + } else if cs > 3 { + return Err(Error::InvalidCs(cs)); + } else { + cs + }; + + let cpol = match settings.mode.polarity { + spi::Polarity::IdleLow => false, + spi::Polarity::IdleHigh => true, + }; + let ncpha = match settings.mode.phase { + spi::Phase::CaptureOnFirstTransition => true, + spi::Phase::CaptureOnSecondTransition => false, + }; + let (csaat, csnaat) = match settings.csa { + ChipSelectActive::ActiveAfterTransfer => (true, false), + ChipSelectActive::ActiveOnConsecutiveTransfers => (false, false), + ChipSelectActive::InactiveAfterEachTransfer => (false, true), + }; + unsafe { + self.spi.csr[cs as usize].write_with_zero(|w| { + w.cpol() + .bit(cpol) + .ncpha() + .bit(ncpha) + .csnaat() + .bit(csnaat) + .csaat() + .bit(csaat) + .bits_() + .bits(settings.bits as u8) + .scbr() + .bits(settings.scbr) + .dlybs() + .bits(settings.dlybs) + .dlybct() + .bits(settings.dlybct) + }); + } + + Ok(()) + } + + /// Select ChipSelect for next read/write FullDuplex trait functions + /// Works around limitations in the embedded-hal trait + /// Valid cs: + /// 0 -> 3 (as long as NPCS0..3 are configured) + /// 0 -> 15 (uses NPCS0..3 as the input to a 4 to 16 mux), pcsdec must be enabled + pub fn cs_select(&mut self, cs: u8) -> Result<(), Error> { + // Map cs to id + let pcs_id = match cs { + 0 => 0b0000, // xxx0 => NPCS[3:0] = 1110 + 1 => 0b0001, // xx01 => NPCS[3:0] = 1101 + 2 => 0b0011, // x011 => NPCS[3:0] = 1011 + 3 => 0b0111, // 0111 => NPCS[3:0] = 0111 + _ => 0b1111, // Forbidden + }; + + // Fixed mode + if self.spi.mr.read().ps().bit_is_clear() { + self.spi.mr.modify(|_, w| unsafe { w.pcs().bits(pcs_id) }); + + // Variable Mode + } else { + // Check for pcsdec + if self.spi.mr.read().pcsdec().bit_is_set() { + if cs > 15 { + return Err(Error::InvalidCs(cs)); + } + self.cs = cs; + } else { + if cs > 3 { + return Err(Error::InvalidCs(cs)); + } + // Map cs to id + self.cs = pcs_id; + } + } + Ok(()) + } + + /// lastxfer set + /// Fixed Mode + /// Sets lastxfer register + /// Variable Mode + /// Use to set lastxfer for the next call to send() + pub fn lastxfer(&mut self, lastxfer: bool) { + // Fixed mode + if self.spi.mr.read().ps().bit_is_clear() { + unsafe { + self.spi.cr.write_with_zero(|w| w.lastxfer().set_bit()); + } + // Variable Mode + } else { + self.lastxfer = lastxfer; + } + } + + /// Enable Receive Data Register Full (RDRF) interrupt + /// NOTE: Do not enable this if planning on using PDC as the PDC uses it to load the register + pub fn enable_rdrf_interrupt(&mut self) { + unsafe { + self.spi.ier.write_with_zero(|w| w.rdrf().set_bit()); + } + } + + /// Disable Receive Data Register Full (RDRF) interrupt + pub fn disable_rdrf_interrupt(&mut self) { + unsafe { + self.spi.idr.write_with_zero(|w| w.rdrf().set_bit()); + } + } + + /// Enable Transmit Data Register Empty (TDRE) interrupt + /// NOTE: Do not enable this if planning on using PDC as the PDC uses it to load the register + pub fn enable_tdre_interrupt(&mut self) { + unsafe { + self.spi.ier.write_with_zero(|w| w.tdre().set_bit()); + } + } + + /// Disable Transmit Data Register Empty (TDRE) interrupt + pub fn disable_tdre_interrupt(&mut self) { + unsafe { + self.spi.idr.write_with_zero(|w| w.tdre().set_bit()); + } + } + + /// Enable Mode Fault Error (MODF) interrupt + /// NOTE: Generally used in multi-master SPI environments + pub fn enable_modf_interrupt(&mut self) { + unsafe { + self.spi.ier.write_with_zero(|w| w.modf().set_bit()); + } + } + + /// Disable Mode Fault Error (MODF) interrupt + pub fn disable_modf_interrupt(&mut self) { + unsafe { + self.spi.idr.write_with_zero(|w| w.modf().set_bit()); + } + } + + /// Enable Overrun Error Status (OVRES) interrupt + pub fn enable_ovres_interrupt(&mut self) { + unsafe { + self.spi.ier.write_with_zero(|w| w.ovres().set_bit()); + } + } + + /// Disable Overrun Error Status (OVRES) interrupt + pub fn disable_ovres_interrupt(&mut self) { + unsafe { + self.spi.idr.write_with_zero(|w| w.ovres().set_bit()); + } + } +} + +/// Used to convert from variable pcs to cs +/// See (33.8.4) +/// +fn variable_pcs_to_cs(pcs: u8) -> Result { + // CS0 + if (pcs & 0x1) == 0 { + Ok(0) + } else if (pcs & 0x2) == 0 { + Ok(1) + } else if (pcs & 0x4) == 0 { + Ok(2) + } else if (pcs & 0x8) == 0 { + Ok(3) + } else { + Err(Error::InvalidCs(0xF)) + } +} + +impl spi::FullDuplex for SpiMaster +where + FRAMESIZE: Copy + From, + SpiU16: From + From, +{ + type Error = Error; + + fn read(&mut self) -> nb::Result { + let sr = self.spi.sr.read(); + + // Check for errors (return error) + // Check for data to read (and read it) + // Return WouldBlock if no data available + Err(if sr.ovres().bit_is_set() { + nb::Error::Other(Error::Overrun) + } else if sr.modf().bit_is_set() { + nb::Error::Other(Error::ModeFault) + } else if sr.spiens().bit_is_clear() { + nb::Error::Other(Error::SpiDisabled) + } else if sr.rdrf().bit_is_set() { + let rdr = self.spi.rdr.read(); + + // In variable mode, verify pcs is what we expect + if self.spi.mr.read().ps().bit_is_set() + && variable_pcs_to_cs(rdr.pcs().bits())? != self.cs + { + nb::Error::Other(Error::UnexpectedPcs(rdr.rd().bits(), rdr.pcs().bits())) + } else { + return Ok(SpiU16(rdr.rd().bits()).into()); + } + } else { + nb::Error::WouldBlock + }) + } + + fn send(&mut self, byte: FRAMESIZE) -> nb::Result<(), Error> { + let sr = self.spi.sr.read(); + + // Check for errors (return error) + // Make sure buffer is empty (then write if available) + // Return WouldBlock if buffer is full + Err(if sr.ovres().bit_is_set() { + nb::Error::Other(Error::Overrun) + } else if sr.modf().bit_is_set() { + nb::Error::Other(Error::ModeFault) + } else if sr.spiens().bit_is_clear() { + nb::Error::Other(Error::SpiDisabled) + } else if sr.tdre().bit_is_set() { + // Fixed Mode + if self.spi.mr.read().ps().bit_is_clear() { + self.write_fixed_data_reg(byte); + + // Variable Mode + } else { + self.write_variable_data_reg(byte); + } + return Ok(()); + } else { + nb::Error::WouldBlock + }) + } +} + +impl crate::hal::blocking::spi::transfer::Default for SpiMaster +where + FRAMESIZE: Copy + From, + SpiU16: From + From, +{ +} + +impl crate::hal::blocking::spi::Write for SpiMaster { + type Error = Error; + + fn write(&mut self, words: &[SpiU8]) -> Result<(), Error> { + self.spi_write(words) + } +} + +impl crate::hal::blocking::spi::Write for SpiMaster { + type Error = Error; + + fn write(&mut self, words: &[SpiU16]) -> Result<(), Error> { + self.spi_write(words) + } +} + +pub trait SpiReadWrite { + fn read_data_reg(&mut self) -> T; + fn write_fixed_data_reg(&mut self, data: T); + fn write_variable_data_reg(&mut self, data: T); + fn spi_write(&mut self, words: &[T]) -> Result<(), Error>; +} + +impl SpiReadWrite for SpiMaster +where + FRAMESIZE: Copy + From, + SpiU16: From + From, +{ + fn read_data_reg(&mut self) -> FRAMESIZE { + let rdr = self.spi.rdr.read(); + SpiU16(rdr.rd().bits()).into() + } + + fn write_fixed_data_reg(&mut self, data: FRAMESIZE) { + unsafe { + let data: SpiU16 = data.into(); + self.spi.tdr.write_with_zero(|w| w.td().bits(data.0)); + } + } + + fn write_variable_data_reg(&mut self, data: FRAMESIZE) { + // NOTE: Uses self.cs to write the pcs register field + unsafe { + let data: SpiU16 = data.into(); + self.spi.tdr.write_with_zero(|w| { + w.td() + .bits(data.0) + .pcs() + .bits(self.cs) + .lastxfer() + .bit(self.lastxfer) + }); + } + } + + fn spi_write(&mut self, words: &[FRAMESIZE]) -> Result<(), Error> { + for word in words { + loop { + let sr = self.spi.sr.read(); + if sr.tdre().bit_is_set() { + // Fixed Mode + if self.spi.mr.read().ps().bit_is_clear() { + self.write_fixed_data_reg(*word); + + // Variable Mode + } else { + self.write_variable_data_reg(*word); + } + if sr.modf().bit_is_set() { + return Err(Error::ModeFault); + } + } + } + } + Ok(()) + } +} + +/// 8-bit fixed mode +/// 8-bit data storage +/// Any SPI settings must be done using the registers +/// See section: 33.7.3.6 +/// +/// +/// or +/// +/// 9-16 bit fixed mode +/// 16-bit data storage +/// Any SPI settings must be done using the registers +/// See section: 33.7.3.6 +/// +pub struct Fixed; + +/// Variable mode +/// 8-16 bit transfer sizes +/// Can do per data word setting adjustments using the DMA stream +/// 32-bits store: +/// - data (8-16 bits) +/// - pcs (CS) (4 bits) +/// - lastxfer (1 bit) +/// +/// Not as efficient RAM/flash wise, but fewer interrupts and polling loops are required as PDC can +/// handle entire sequences talking to many SPI chips. +/// +/// See section: 33.7.3.6 +/// +pub struct Variable; + +pub struct SpiPayload { + spi: SpiMaster, + _mode: PhantomData, +} + +pub type SpiRxDma = RxDma>; +pub type SpiTxDma = TxDma>; +pub type SpiRxTxDma = RxTxDma>; + +macro_rules! spi_pdc { + ( + $Mode:ident, $Framesize:ident + ) => { + paste! { + impl SpiMaster<$Framesize> { + /// SPI with PDC, Rx only + pub fn with_pdc_rx(self) -> SpiRxDma<$Mode, $Framesize> { + let payload = SpiPayload { + spi: self, + _mode: PhantomData, + }; + RxDma { payload } + } + + /// SPI with PDC, Tx only + pub fn with_pdc_tx(self) -> SpiTxDma<$Mode, $Framesize> { + let payload = SpiPayload { + spi: self, + _mode: PhantomData, + }; + TxDma { payload } + } + + /// SPI with PDC, Rx+TX + /// ``` + /// use atsam4_hal::clock::{ClockController, MainClock, SlowClock}; + /// use atsam4_hal::pac::Peripherals; + /// + /// let peripherals = Peripherals::take().unwrap(); + /// let clocks = ClockController::new( + /// peripherals.PMC, + /// &peripherals.SUPC, + /// &peripherals.EFC0, + /// MainClock::Crystal12Mhz, + /// SlowClock::RcOscillator32Khz, + /// ); + /// let gpio_ports = Ports::new( + /// ( + /// peripherals.PIOA, + /// clocks.peripheral_clocks.pio_a.into_enabled_clock(), + /// ), + /// ( + /// peripherals.PIOB, + /// clocks.peripheral_clocks.pio_b.into_enabled_clock(), + /// ), + /// ); + /// let mut pins = Pins::new(gpio_ports, &peripherals.MATRIX) + /// + /// // Setup SPI Master + /// let wdrbt = false; // Wait data read before transfer enabled + /// let llb = false; // Local loopback + /// // Cycles to delay between consecutive transfers + /// let dlybct = 0; // No delay + /// // SpiU8 can be used as we're only using 8-bit SPI + /// // SpiU16 can be used for 8 to 16-bit SPI + /// let mut spi = SpiMaster::::new( + /// cx.device.SPI, + /// clocks.peripheral_clocks.spi.into_enabled_clock(), + /// pins.spi_miso, + /// pins.spi_mosi, + /// pins.spi_sck, + /// spi::PeripheralSelectMode::Variable, + /// wdrbt, + /// llb, + /// dlybct, + /// ); + /// + /// // Setup SPI with pdc + /// let spi_tx_buf: [u32; 10] = [5; 10], + /// let spi_rx_buf: [u32; 10] = [0; 10], + /// let mut spi = spi.with_pdc_rxtx(); + /// // Same as read_write() but use a smaller subset of the given buffer + /// let txfr = spi.read_write_len(spi_rx_buf, spi_tx_buf, 7); + /// let ((rx_buf, tx_buf), spi) = txfr.wait(); + /// ``` + pub fn with_pdc_rxtx(self) -> SpiRxTxDma<$Mode, $Framesize> { + let payload = SpiPayload { + spi: self, + _mode: PhantomData, + }; + RxTxDma { payload } + } + } + + // Setup PDC Rx/Tx functionality + pub type [] = SpiMaster<$Framesize>; + pdc_rx! { []: spi, sr } + pdc_tx! { []: spi, sr } + pdc_rxtx! { []: spi } + + impl Transmit for SpiTxDma<$Mode, $Framesize> { + type ReceivedWord = $Framesize; + } + + impl Receive for SpiRxDma<$Mode, $Framesize> { + type TransmittedWord = $Framesize; + } + + impl Receive for SpiRxTxDma<$Mode, $Framesize> { + type TransmittedWord = $Framesize; + } + + impl Transmit for SpiRxTxDma<$Mode, $Framesize> { + type ReceivedWord = $Framesize; + } + + impl SpiRxDma<$Mode, $Framesize> { + /// Reverts SpiRxDma back to SpiMaster + pub fn revert(mut self) -> SpiMaster<$Framesize> { + self.payload.spi.stop_rx_pdc(); + self.payload.spi + } + } + + impl ReadDma for SpiRxDma<$Mode, $Framesize> + where + Self: TransferPayload, + B: StaticWriteBuffer, + { + /// Assigns the buffer, enables PDC and starts SPI transaction + fn read(mut self, mut buffer: B) -> Transfer { + // NOTE(unsafe) We own the buffer now and we won't call other `&mut` on it + // until the end of the transfer. + let (ptr, len) = unsafe { buffer.static_write_buffer() }; + self.payload.spi.set_receive_address(ptr as u32); + self.payload.spi.set_receive_counter(len as u16); + + compiler_fence(Ordering::Release); + self.start(); + + Transfer::w(buffer, self) + } + } + + impl TransferPayload for SpiRxDma<$Mode, $Framesize> { + fn start(&mut self) { + self.payload.spi.start_rx_pdc(); + } + fn stop(&mut self) { + self.payload.spi.stop_rx_pdc(); + } + fn in_progress(&self) -> bool { + self.payload.spi.rx_in_progress() + } + } + + impl SpiTxDma<$Mode, $Framesize> { + /// Reverts SpiTxDma back to SpiMaster + pub fn revert(mut self) -> SpiMaster<$Framesize> { + self.payload.spi.stop_tx_pdc(); + self.payload.spi + } + } + + impl WriteDma for SpiTxDma<$Mode, $Framesize> + where + Self: TransferPayload, + B: StaticReadBuffer, + { + /// Assigns the write buffer, enables PDC and starts SPI transaction + fn write(mut self, buffer: B) -> Transfer { + // NOTE(unsafe) We own the buffer now and we won't call other `&mut` on it + // until the end of the transfer. + let (ptr, len) = unsafe { buffer.static_read_buffer() }; + self.payload.spi.set_transmit_address(ptr as u32); + self.payload.spi.set_transmit_counter(len as u16); + + compiler_fence(Ordering::Release); + self.start(); + + Transfer::r(buffer, self) + } + } + + impl TransferPayload for SpiTxDma<$Mode, $Framesize> { + fn start(&mut self) { + self.payload.spi.start_tx_pdc(); + } + fn stop(&mut self) { + self.payload.spi.stop_tx_pdc(); + } + fn in_progress(&self) -> bool { + self.payload.spi.tx_in_progress() + } + } + + impl SpiRxTxDma<$Mode, $Framesize> { + /// Reverts SpiRxTxDma back to SpiMaster + pub fn revert(mut self) -> SpiMaster<$Framesize> { + self.payload.spi.stop_rxtx_pdc(); + self.payload.spi + } + } + + impl ReadWriteDma for SpiRxTxDma<$Mode, $Framesize> + where + Self: TransferPayload, + RXB: StaticWriteBuffer, + TXB: StaticReadBuffer, + { + fn read_write(mut self, mut rx_buffer: RXB, tx_buffer: TXB) -> Transfer { + // NOTE(unsafe) We own the buffer now and we won't call other `&mut` on it + // until the end of the transfer. + let (ptr, rx_len) = unsafe { rx_buffer.static_write_buffer() }; + self.payload.spi.set_receive_address(ptr as u32); + self.payload.spi.set_receive_counter(rx_len as u16); + + let (ptr, tx_len) = unsafe { tx_buffer.static_read_buffer() }; + self.payload.spi.set_transmit_address(ptr as u32); + self.payload.spi.set_transmit_counter(tx_len as u16); + + if rx_len != tx_len { + panic!("rx_len: {} != tx:len: {}", rx_len, tx_len); + } + + compiler_fence(Ordering::Release); + self.start(); + + Transfer::w((rx_buffer, tx_buffer), self) + } + } + + impl ReadWriteDmaLen for SpiRxTxDma<$Mode, $Framesize> + where + Self: TransferPayload, + RXB: StaticWriteBuffer, + TXB: StaticReadBuffer, + { + /// Same as read_write(), but allows for a specified length + fn read_write_len(mut self, mut rx_buffer: RXB, rx_buf_len: usize, tx_buffer: TXB, tx_buf_len: usize) -> Transfer { + // NOTE(unsafe) We own the buffer now and we won't call other `&mut` on it + // until the end of the transfer. + let (ptr, rx_len) = unsafe { rx_buffer.static_write_buffer() }; + self.payload.spi.set_receive_address(ptr as u32); + self.payload.spi.set_receive_counter(rx_buf_len as u16); + if rx_len < rx_buf_len { + panic!("rx_len: {} < rx_buf_len: {}", rx_len, rx_buf_len); + } + + let (ptr, tx_len) = unsafe { tx_buffer.static_read_buffer() }; + self.payload.spi.set_transmit_address(ptr as u32); + self.payload.spi.set_transmit_counter(tx_buf_len as u16); + if tx_len < tx_buf_len { + panic!("tx_len: {} < tx_buf_len: {}", tx_len, tx_buf_len); + } + + compiler_fence(Ordering::Release); + self.start(); + + Transfer::w((rx_buffer, tx_buffer), self) + } + } + + impl TransferPayload for SpiRxTxDma<$Mode, $Framesize> { + fn start(&mut self) { + self.payload.spi.start_rxtx_pdc(); + } + fn stop(&mut self) { + self.payload.spi.stop_rxtx_pdc(); + } + fn in_progress(&self) -> bool { + self.payload.spi.tx_in_progress() || self.payload.spi.rx_in_progress() + } + } + } + } +} + +// Setup SPI for each of the 3 different datastructures +spi_pdc! { Fixed, u8 } +spi_pdc! { Fixed, u16 } +spi_pdc! { Variable, u32 }