From 5e5ef72e7244f92436604a1c5934890821e5049e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Alse=CC=81r?= Date: Mon, 21 Sep 2020 02:31:12 +0200 Subject: [PATCH 1/9] Add SPIS module --- examples/spis-demo/Cargo.toml | 17 + examples/spis-demo/Embed.toml | 21 ++ examples/spis-demo/src/main.rs | 84 +++++ nrf-hal-common/src/lib.rs | 2 + nrf-hal-common/src/spis.rs | 646 +++++++++++++++++++++++++++++++++ 5 files changed, 770 insertions(+) create mode 100644 examples/spis-demo/Cargo.toml create mode 100755 examples/spis-demo/Embed.toml create mode 100644 examples/spis-demo/src/main.rs create mode 100644 nrf-hal-common/src/spis.rs diff --git a/examples/spis-demo/Cargo.toml b/examples/spis-demo/Cargo.toml new file mode 100644 index 00000000..93c28485 --- /dev/null +++ b/examples/spis-demo/Cargo.toml @@ -0,0 +1,17 @@ +[package] +name = "spis-demo" +version = "0.1.0" +authors = ["Henrik Alsér"] +edition = "2018" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +cortex-m = "0.6.2" +cortex-m-rtic = "0.5.3" +rtt-target = {version = "0.2.0", features = ["cortex-m"] } +nrf52840-hal = { features = ["rt"], path = "../../nrf52840-hal" } + +[dependencies.embedded-hal] +version = "0.2.3" +features = ["unproven"] diff --git a/examples/spis-demo/Embed.toml b/examples/spis-demo/Embed.toml new file mode 100755 index 00000000..eda5e3e0 --- /dev/null +++ b/examples/spis-demo/Embed.toml @@ -0,0 +1,21 @@ +[default.probe] +protocol = "Swd" + +[default.flashing] +enabled = true +halt_afterwards = false +restore_unwritten_bytes = false + +[default.general] +chip = "nRF52840" +chip_descriptions = [] +log_level = "Warn" + +[default.rtt] +enabled = true +channels = [] +timeout = 3000 +show_timestamps = true + +[default.gdb] +enabled = false \ No newline at end of file diff --git a/examples/spis-demo/src/main.rs b/examples/spis-demo/src/main.rs new file mode 100644 index 00000000..15caf85c --- /dev/null +++ b/examples/spis-demo/src/main.rs @@ -0,0 +1,84 @@ +#![no_std] +#![no_main] + +use { + core::{ + panic::PanicInfo, + sync::atomic::{compiler_fence, Ordering}, + }, + hal::{gpiote::Gpiote, pac::SPIS0, spis::*}, + nrf52840_hal as hal, + rtt_target::{rprintln, rtt_init_print}, +}; + +#[rtic::app(device = crate::hal::pac, peripherals = true)] +const APP: () = { + struct Resources { + gpiote: Gpiote, + transfer: Option>, + } + + #[init] + fn init(ctx: init::Context) -> init::LateResources { + static mut BUF: [u8; 8] = [0; 8]; + + let _clocks = hal::clocks::Clocks::new(ctx.device.CLOCK).enable_ext_hfosc(); + rtt_init_print!(); + rprintln!("Send me [u8; 8] over SPI"); + rprintln!("Press button to reset buffer"); + + let p0 = hal::gpio::p0::Parts::new(ctx.device.P0); + let cs_pin = p0.p0_25.into_floating_input().degrade(); + let sck_pin = p0.p0_24.into_floating_input().degrade(); + let copi_pin = p0.p0_16.into_floating_input().degrade(); + let cipo_pin = p0.p0_14.into_floating_input().degrade(); + + let spis = Spis::new( + ctx.device.SPIS0, + &sck_pin, + &cs_pin, + Some(&copi_pin), + Some(&cipo_pin), + ); + spis.enable_interrupt(SpisEvent::End); + + let btn = p0.p0_29.into_pullup_input().degrade(); + let gpiote = Gpiote::new(ctx.device.GPIOTE); + gpiote.port().input_pin(&btn).low(); + gpiote.port().enable_interrupt(); + + init::LateResources { + gpiote, + transfer: spis.transfer(BUF).ok(), + } + } + + #[task(binds = GPIOTE, resources = [gpiote, transfer])] + fn on_gpiote(ctx: on_gpiote::Context) { + ctx.resources.gpiote.reset_events(); + rprintln!("Reset buffer"); + let (buf, spis) = ctx.resources.transfer.take().unwrap().wait(); + spis.acquire(); // Acquire the SPIS semaphore to be able to safely update `buf` + buf.copy_from_slice(&[0; 8][..]); + rprintln!("{:?}", buf); + *ctx.resources.transfer = spis.transfer(buf).ok(); + } + + #[task(binds = SPIM0_SPIS0_TWIM0_TWIS0_SPI0_TWI0, resources = [transfer])] + fn on_spis(ctx: on_spis::Context) { + let (buf, spis) = ctx.resources.transfer.take().unwrap().wait(); + spis.reset_event(SpisEvent::End); + rprintln!("Received: {:?}", buf); + *ctx.resources.transfer = spis.transfer(buf).ok(); + } +}; + +#[inline(never)] +#[panic_handler] +fn panic(info: &PanicInfo) -> ! { + cortex_m::interrupt::disable(); + rprintln!("{}", info); + loop { + compiler_fence(Ordering::SeqCst); + } +} diff --git a/nrf-hal-common/src/lib.rs b/nrf-hal-common/src/lib.rs index a8468a53..e710a0d7 100644 --- a/nrf-hal-common/src/lib.rs +++ b/nrf-hal-common/src/lib.rs @@ -57,6 +57,8 @@ pub mod saadc; pub mod spi; #[cfg(not(feature = "51"))] pub mod spim; +#[cfg(not(feature = "51"))] +pub mod spis; #[cfg(not(feature = "9160"))] pub mod temp; pub mod time; diff --git a/nrf-hal-common/src/spis.rs b/nrf-hal-common/src/spis.rs new file mode 100644 index 00000000..23ddf18e --- /dev/null +++ b/nrf-hal-common/src/spis.rs @@ -0,0 +1,646 @@ +use core::{ + ops::Deref, + sync::atomic::{compiler_fence, Ordering}, +}; + +#[cfg(feature = "9160")] +use crate::pac::{ + spis0_ns::{ + self as spis0, _EVENTS_ACQUIRED, _EVENTS_END, _EVENTS_ENDRX, _TASKS_ACQUIRE, _TASKS_RELEASE, + }, + SPIS0_NS as SPIS0, +}; + +#[cfg(not(feature = "9160"))] +use crate::pac::{ + spis0::{self, _EVENTS_ACQUIRED, _EVENTS_END, _EVENTS_ENDRX, _TASKS_ACQUIRE, _TASKS_RELEASE}, + SPIS0, +}; + +#[cfg(any(feature = "52832", feature = "52833", feature = "52840"))] +use crate::pac::{SPIS1, SPIS2}; + +use crate::{ + gpio::{Floating, Input, Pin}, + pac::{generic::Reg, Interrupt}, + target_constants::{EASY_DMA_SIZE, SRAM_LOWER, SRAM_UPPER}, +}; +use embedded_dma::*; + +pub struct Spis(T); + +impl Spis +where + T: Instance, +{ + /// Takes ownership of the raw SPIS peripheral, returning a safe wrapper. + pub fn new( + spis: T, + sck_pin: &Pin>, + cs_pin: &Pin>, + copi_pin: Option<&Pin>>, + cipo_pin: Option<&Pin>>, + ) -> Self { + spis.psel.sck.write(|w| { + unsafe { w.pin().bits(sck_pin.pin()) }; + #[cfg(any(feature = "52833", feature = "52840"))] + w.port().bit(sck_pin.port().bit()); + w.connect().connected() + }); + spis.psel.csn.write(|w| { + unsafe { w.pin().bits(cs_pin.pin()) }; + #[cfg(any(feature = "52833", feature = "52840"))] + w.port().bit(cs_pin.port().bit()); + w.connect().connected() + }); + + if let Some(p) = copi_pin { + spis.psel.mosi.write(|w| { + unsafe { w.pin().bits(p.pin()) }; + #[cfg(any(feature = "52833", feature = "52840"))] + w.port().bit(p.port().bit()); + w.connect().connected() + }); + } + + if let Some(p) = cipo_pin { + spis.psel.miso.write(|w| { + unsafe { w.pin().bits(p.pin()) }; + #[cfg(any(feature = "52833", feature = "52840"))] + w.port().bit(p.port().bit()); + w.connect().connected() + }); + } + + spis.config + .modify(|_r, w| w.cpol().bit(Polarity::ActiveHigh.into())); + spis.config + .modify(|_r, w| w.cpha().bit(Phase::Trailing.into())); + spis.enable.write(|w| w.enable().enabled()); + Self(spis) + } + + /// Sets the ´default´ character (character clocked out in case of an ignored transaction). + #[inline(always)] + pub fn set_default_char(&self, def: u8) -> &Self { + self.0.def.write(|w| unsafe { w.def().bits(def) }); + self + } + + /// Sets the over-read character (character sent on over-read of the transmit buffer). + #[inline(always)] + pub fn set_orc(&self, orc: u8) -> &Self { + self.0.orc.write(|w| unsafe { w.orc().bits(orc) }); + self + } + + /// Sets bit order. + #[inline(always)] + pub fn set_order(&self, order: Order) -> &Self { + self.0.config.modify(|_r, w| w.order().bit(order.into())); + self + } + + /// Sets serial clock (SCK) polarity. + #[inline(always)] + pub fn set_polarity(&self, polarity: Polarity) -> &Self { + self.0.config.modify(|_r, w| w.cpol().bit(polarity.into())); + self + } + + /// Sets serial clock (SCK) phase. + #[inline(always)] + pub fn set_phase(&self, phase: Phase) -> &Self { + self.0.config.modify(|_r, w| w.cpha().bit(phase.into())); + self + } + + /// Sets SPI mode. + #[inline(always)] + pub fn set_mode(&self, mode: Mode) -> &Self { + match mode { + Mode::Mode0 => { + self.set_polarity(Polarity::ActiveHigh); + self.set_phase(Phase::Trailing); + } + Mode::Mode1 => { + self.set_polarity(Polarity::ActiveHigh); + self.set_phase(Phase::Leading); + } + Mode::Mode2 => { + self.set_polarity(Polarity::ActiveLow); + self.set_phase(Phase::Trailing); + } + Mode::Mode3 => { + self.set_polarity(Polarity::ActiveLow); + self.set_phase(Phase::Leading); + } + } + self + } + + /// Enables the SPIS instance. + #[inline(always)] + pub fn enable(&self) -> &Self { + self.0.enable.write(|w| w.enable().enabled()); + self + } + + /// Disables the SPIS module. + #[inline(always)] + pub fn disable(&self) -> &Self { + self.0.enable.write(|w| w.enable().disabled()); + self + } + + /// Requests acquiring the SPIS semaphore and waits until acquired. + #[inline(always)] + pub fn acquire(&self) -> &Self { + self.enable(); + self.0.tasks_acquire.write(|w| unsafe { w.bits(1) }); + while self.0.events_acquired.read().bits() == 0 {} + self + } + + /// Releases the SPIS semaphore, enabling the SPIS to acquire it. + #[inline(always)] + pub fn release(&self) -> &Self { + self.0.tasks_release.write(|w| unsafe { w.bits(1) }); + self + } + + /// Enables interrupt for specified event. + #[inline(always)] + pub fn enable_interrupt(&self, command: SpisEvent) -> &Self { + self.0.intenset.modify(|_r, w| match command { + SpisEvent::Acquired => w.acquired().set_bit(), + SpisEvent::End => w.end().set_bit(), + SpisEvent::EndRx => w.endrx().set_bit(), + }); + self + } + + /// Disables interrupt for specified event. + #[inline(always)] + pub fn disable_interrupt(&self, command: SpisEvent) -> &Self { + self.0.intenclr.write(|w| match command { + SpisEvent::Acquired => w.acquired().set_bit(), + SpisEvent::End => w.end().set_bit(), + SpisEvent::EndRx => w.endrx().set_bit(), + }); + self + } + + /// Automatically acquire the semaphore after transfer has ended. + #[inline(always)] + pub fn auto_acquire(&self, enabled: bool) -> &Self { + self.0.shorts.write(|w| w.end_acquire().bit(enabled)); + self + } + + /// Resets all events. + #[inline(always)] + pub fn reset_events(&self) { + self.0.events_acquired.reset(); + self.0.events_end.reset(); + self.0.events_endrx.reset(); + } + + /// Resets specified event. + #[inline(always)] + pub fn reset_event(&self, event: SpisEvent) { + match event { + SpisEvent::Acquired => self.0.events_acquired.reset(), + SpisEvent::End => self.0.events_end.reset(), + SpisEvent::EndRx => self.0.events_endrx.reset(), + }; + } + + /// Checks if specified event has been triggered. + #[inline(always)] + pub fn is_event_triggered(&self, event: SpisEvent) -> bool { + match event { + SpisEvent::Acquired => self.0.events_acquired.read().bits() != 0, + SpisEvent::End => self.0.events_end.read().bits() != 0, + SpisEvent::EndRx => self.0.events_endrx.read().bits() != 0, + } + } + + /// Checks if the granted transfer is done. + #[inline(always)] + pub fn is_done(&self) -> bool { + self.0.events_end.read().bits() != 0 || self.0.events_endrx.read().bits() != 0 + } + + /// Checks if the semaphore is acquired. + #[inline(always)] + pub fn is_acquired(&self) -> bool { + self.0.events_acquired.read().bits() != 0 + } + + /// Checks if last transaction overread. + #[inline(always)] + pub fn is_overread(&self) -> bool { + self.0.status.read().overread().is_present() + } + + /// Checks if last transaction overflowed. + #[inline(always)] + pub fn is_overflow(&self) -> bool { + self.0.status.read().overflow().is_present() + } + + /// Returns number of bytes received in last granted transaction. + #[inline(always)] + pub fn amount(&self) -> u32 { + self.0.rxd.amount.read().bits() + } + + /// Returns the semaphore status. + #[inline(always)] + pub fn semaphore_status(&self) -> SemaphoreStatus { + match self.0.semstat.read().bits() { + 0 => SemaphoreStatus::Free, + 1 => SemaphoreStatus::CPU, + 2 => SemaphoreStatus::SPIS, + _ => SemaphoreStatus::CPUPending, + } + } + + /// Returns reference to `Acquired` event endpoint for PPI. + #[inline(always)] + pub fn event_acquired(&self) -> &Reg { + &self.0.events_acquired + } + + /// Returns reference to `End` event endpoint for PPI. + #[inline(always)] + pub fn event_end(&self) -> &Reg { + &self.0.events_end + } + + /// Returns reference to `EndRx` event endpoint for PPI. + #[inline(always)] + pub fn event_end_rx(&self) -> &Reg { + &self.0.events_endrx + } + + /// Returns reference to `Acquire` task endpoint for PPI. + #[inline(always)] + pub fn task_acquire(&self) -> &Reg { + &self.0.tasks_acquire + } + + /// Returns reference to `Release` task endpoint for PPI. + #[inline(always)] + pub fn task_release(&self) -> &Reg { + &self.0.tasks_release + } + + /// Receives data into the given `buffer` until it's filled. + /// Buffer must be located in RAM. + /// Returns a value that represents the in-progress DMA transfer. + #[allow(unused_mut)] + pub fn rx(mut self, mut buffer: B) -> Result, Error> + where + B: WriteBuffer, + { + let (ptr, len) = unsafe { buffer.write_buffer() }; + let maxcnt = len * core::mem::size_of::(); + if maxcnt > EASY_DMA_SIZE { + return Err(Error::BufferTooLong); + } + self.0 + .rxd + .ptr + .write(|w| unsafe { w.ptr().bits(ptr as u32) }); + self.0 + .rxd + .maxcnt + .write(|w| unsafe { w.bits(maxcnt as u32) }); + + self.release(); + Ok(Transfer { + inner: Some(Inner { buffer, spis: self }), + }) + } + + /// Full duplex DMA transfer. + /// Transmits the given buffer while simultaneously receiving data into the same buffer until it is filled. + /// Buffer must be located in RAM. + /// Returns a value that represents the in-progress DMA transfer. + #[allow(unused_mut)] + pub fn transfer(mut self, mut buffer: B) -> Result, Error> + where + B: WriteBuffer, + { + let (ptr, len) = unsafe { buffer.write_buffer() }; + let maxcnt = len * core::mem::size_of::(); + if maxcnt > EASY_DMA_SIZE { + return Err(Error::BufferTooLong); + } + self.0 + .txd + .ptr + .write(|w| unsafe { w.ptr().bits(ptr as u32) }); + self.0 + .rxd + .ptr + .write(|w| unsafe { w.ptr().bits(ptr as u32) }); + self.0 + .txd + .maxcnt + .write(|w| unsafe { w.bits(maxcnt as u32) }); + self.0 + .rxd + .maxcnt + .write(|w| unsafe { w.bits(maxcnt as u32) }); + + self.release(); + Ok(Transfer { + inner: Some(Inner { buffer, spis: self }), + }) + } + + /// Full duplex DMA transfer. + /// Transmits the given `tx_buffer` while simultaneously receiving data + /// into the given `rx_buffer` until it is filled. + /// The buffers must be located in RAM. + /// Returns a value that represents the in-progress DMA transfer. + #[allow(unused_mut)] + pub fn transfer_split( + mut self, + tx_buffer: TxB, + mut rx_buffer: RxB, + ) -> Result, Error> + where + TxB: ReadBuffer, + RxB: WriteBuffer, + { + let (rx_ptr, rx_len) = unsafe { rx_buffer.write_buffer() }; + let (tx_ptr, tx_len) = unsafe { tx_buffer.read_buffer() }; + let rx_maxcnt = rx_len * core::mem::size_of::(); + let tx_maxcnt = tx_len * core::mem::size_of::(); + if rx_maxcnt > EASY_DMA_SIZE || tx_maxcnt > EASY_DMA_SIZE { + return Err(Error::BufferTooLong); + } + if (tx_ptr as usize) < SRAM_LOWER || (tx_ptr as usize) > SRAM_UPPER { + return Err(Error::DMABufferNotInDataMemory); + } + + self.0 + .txd + .ptr + .write(|w| unsafe { w.ptr().bits(tx_ptr as u32) }); + self.0 + .rxd + .ptr + .write(|w| unsafe { w.ptr().bits(rx_ptr as u32) }); + self.0 + .rxd + .maxcnt + .write(|w| unsafe { w.bits(rx_maxcnt as u32) }); + self.0 + .txd + .maxcnt + .write(|w| unsafe { w.bits(tx_maxcnt as u32) }); + + self.release(); + Ok(TransferSplit { + inner: Some(InnerFullDuplex { + tx_buffer, + rx_buffer, + spis: self, + }), + }) + } + + /// Transmits the given `tx_buffer`. Buffer must be located in RAM. + /// Returns a value that represents the in-progress DMA transfer. + #[allow(unused_mut)] + pub fn tx(mut self, buffer: B) -> Result, Error> + where + B: ReadBuffer, + { + let (ptr, len) = unsafe { buffer.read_buffer() }; + let maxcnt = len * core::mem::size_of::(); + if maxcnt > EASY_DMA_SIZE { + return Err(Error::BufferTooLong); + } + if (ptr as usize) < SRAM_LOWER || (ptr as usize) > SRAM_UPPER { + return Err(Error::DMABufferNotInDataMemory); + } + + self.0 + .txd + .ptr + .write(|w| unsafe { w.ptr().bits(ptr as u32) }); + self.0 + .txd + .maxcnt + .write(|w| unsafe { w.bits(maxcnt as u32) }); + + self.release(); + Ok(Transfer { + inner: Some(Inner { buffer, spis: self }), + }) + } + + /// Returns the raw interface to the underlying SPIS peripheral. + pub fn free(self) -> T { + self.0 + } +} + +/// A DMA transfer +pub struct Transfer { + inner: Option>, +} + +struct Inner { + buffer: B, + spis: Spis, +} + +impl Transfer { + /// Blocks until the transfer is done and returns the buffer. + pub fn wait(mut self) -> (B, Spis) { + let inner = self + .inner + .take() + .unwrap_or_else(|| unsafe { core::hint::unreachable_unchecked() }); + while !inner.spis.is_done() {} + compiler_fence(Ordering::Acquire); + (inner.buffer, inner.spis) + } + + /// Checks if the granted transfer is done. + #[inline(always)] + pub fn is_done(&mut self) -> bool { + let inner = self + .inner + .take() + .unwrap_or_else(|| unsafe { core::hint::unreachable_unchecked() }); + inner.spis.is_done() + } +} + +impl Drop for Transfer { + fn drop(&mut self) { + if let Some(inner) = self.inner.as_mut() { + while !inner.spis.is_done() {} + inner.spis.disable(); + compiler_fence(Ordering::Acquire); + } + } +} +/// A full duplex DMA transfer +pub struct TransferSplit { + inner: Option>, +} + +struct InnerFullDuplex { + tx_buffer: TxB, + rx_buffer: RxB, + spis: Spis, +} + +impl TransferSplit { + /// Blocks until the transfer is done and returns the buffer. + pub fn wait(mut self) -> (TxB, RxB, Spis) { + let inner = self + .inner + .take() + .unwrap_or_else(|| unsafe { core::hint::unreachable_unchecked() }); + while !inner.spis.is_done() {} + compiler_fence(Ordering::Acquire); + (inner.tx_buffer, inner.rx_buffer, inner.spis) + } + + /// Checks if the granted transfer is done. + #[inline(always)] + pub fn is_done(&mut self) -> bool { + let inner = self + .inner + .take() + .unwrap_or_else(|| unsafe { core::hint::unreachable_unchecked() }); + inner.spis.is_done() + } +} + +impl Drop for TransferSplit { + fn drop(&mut self) { + if let Some(inner) = self.inner.as_mut() { + while !inner.spis.is_done() {} + inner.spis.disable(); + compiler_fence(Ordering::Acquire); + } + } +} + +/// SPIS events +#[derive(Debug, Eq, PartialEq, Clone, Copy)] +pub enum SpisEvent { + End, + EndRx, + Acquired, +} +/// Semaphore status +#[derive(Debug, Eq, PartialEq, Clone, Copy)] +pub enum SemaphoreStatus { + Free, + CPU, + SPIS, + CPUPending, +} +/// Bit order +#[derive(Debug, Eq, PartialEq, Clone, Copy)] +pub enum Order { + MsbFirst, + LsbFirst, +} +impl From for bool { + fn from(variant: Order) -> Self { + match variant { + Order::MsbFirst => false, + Order::LsbFirst => true, + } + } +} + +/// Serial clock (SCK) phase +#[derive(Debug, Eq, PartialEq, Clone, Copy)] +pub enum Phase { + Trailing, + Leading, +} +impl From for bool { + fn from(variant: Phase) -> Self { + match variant { + Phase::Trailing => false, + Phase::Leading => true, + } + } +} + +/// Serial clock (SCK) polarity +#[derive(Debug, Eq, PartialEq, Clone, Copy)] +pub enum Polarity { + ActiveHigh, + ActiveLow, +} +impl From for bool { + fn from(variant: Polarity) -> Self { + match variant { + Polarity::ActiveHigh => false, + Polarity::ActiveLow => true, + } + } +} + +/// SPI mode +#[derive(Debug, Eq, PartialEq, Clone, Copy)] +pub enum Mode { + Mode0, + Mode1, + Mode2, + Mode3, +} + +#[derive(Debug, Eq, PartialEq, Clone, Copy)] +pub enum Error { + DMABufferNotInDataMemory, + BufferTooLong, +} + +mod sealed { + pub trait Sealed {} + impl Sealed for super::SPIS0 {} + #[cfg(not(any(feature = "9160", feature = "52810")))] + impl Sealed for super::SPIS1 {} + #[cfg(not(any(feature = "9160", feature = "52810")))] + impl Sealed for super::SPIS2 {} +} + +pub trait Instance: sealed::Sealed + Deref { + const INTERRUPT: Interrupt; +} + +impl Instance for SPIS0 { + #[cfg(not(any(feature = "9160", feature = "52810")))] + const INTERRUPT: Interrupt = Interrupt::SPIM0_SPIS0_TWIM0_TWIS0_SPI0_TWI0; + #[cfg(feature = "9160")] + const INTERRUPT: Interrupt = Interrupt::UARTE0_SPIM0_SPIS0_TWIM0_TWIS0; + #[cfg(feature = "52810")] + const INTERRUPT: Interrupt = Interrupt::SPIM0_SPIS0_SPI0; +} + +#[cfg(not(any(feature = "9160", feature = "52810")))] +impl Instance for SPIS1 { + const INTERRUPT: Interrupt = Interrupt::SPIM1_SPIS1_TWIM1_TWIS1_SPI1_TWI1; +} + +#[cfg(not(any(feature = "9160", feature = "52810")))] +impl Instance for SPIS2 { + const INTERRUPT: Interrupt = Interrupt::SPIM2_SPIS2_SPI2; +} From 2ba98ad4eb149a32110ed3aa0102dab57df9b181 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Alse=CC=81r?= Date: Mon, 21 Sep 2020 09:05:43 +0200 Subject: [PATCH 2/9] Add demo to xtask --- xtask/src/lib.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/xtask/src/lib.rs b/xtask/src/lib.rs index 2c390ea2..782ea84c 100644 --- a/xtask/src/lib.rs +++ b/xtask/src/lib.rs @@ -22,6 +22,7 @@ pub static EXAMPLES: &[(&str, &[&str])] = &[ ("qdec-demo", &[]), ("rtic-demo", &["51", "52810", "52832", "52840"]), ("spi-demo", &[]), + ("spis-demo", &[]), ("twi-ssd1306", &["52832", "52840"]), ("twim-demo", &[]), ("twis-demo", &[]), From 290f681429bb314133d62ccba9f4c760a8b3ae6d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Alse=CC=81r?= Date: Mon, 21 Sep 2020 09:20:53 +0200 Subject: [PATCH 3/9] Cleanup, add demo description --- examples/spis-demo/src/main.rs | 3 +++ nrf-hal-common/src/spis.rs | 6 +++--- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/examples/spis-demo/src/main.rs b/examples/spis-demo/src/main.rs index 15caf85c..a00e993d 100644 --- a/examples/spis-demo/src/main.rs +++ b/examples/spis-demo/src/main.rs @@ -1,6 +1,9 @@ #![no_std] #![no_main] +// Demo for the SPIS module, transmitting the current buffer contents while receiving new data. +// Press button to zero the buffer. + use { core::{ panic::PanicInfo, diff --git a/nrf-hal-common/src/spis.rs b/nrf-hal-common/src/spis.rs index 23ddf18e..aa320a79 100644 --- a/nrf-hal-common/src/spis.rs +++ b/nrf-hal-common/src/spis.rs @@ -407,7 +407,7 @@ where self.release(); Ok(TransferSplit { - inner: Some(InnerFullDuplex { + inner: Some(InnerSplit { tx_buffer, rx_buffer, spis: self, @@ -496,10 +496,10 @@ impl Drop for Transfer { } /// A full duplex DMA transfer pub struct TransferSplit { - inner: Option>, + inner: Option>, } -struct InnerFullDuplex { +struct InnerSplit { tx_buffer: TxB, rx_buffer: RxB, spis: Spis, From b1e66aff8efce5268f9e79ca0e9f0ba2d3cfb151 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Alse=CC=81r?= Date: Mon, 21 Sep 2020 11:19:54 +0200 Subject: [PATCH 4/9] Acquire semaphore in transfer wait --- examples/spis-demo/src/main.rs | 1 - nrf-hal-common/src/spis.rs | 2 ++ 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/examples/spis-demo/src/main.rs b/examples/spis-demo/src/main.rs index a00e993d..84ea7845 100644 --- a/examples/spis-demo/src/main.rs +++ b/examples/spis-demo/src/main.rs @@ -61,7 +61,6 @@ const APP: () = { ctx.resources.gpiote.reset_events(); rprintln!("Reset buffer"); let (buf, spis) = ctx.resources.transfer.take().unwrap().wait(); - spis.acquire(); // Acquire the SPIS semaphore to be able to safely update `buf` buf.copy_from_slice(&[0; 8][..]); rprintln!("{:?}", buf); *ctx.resources.transfer = spis.transfer(buf).ok(); diff --git a/nrf-hal-common/src/spis.rs b/nrf-hal-common/src/spis.rs index aa320a79..a75a798b 100644 --- a/nrf-hal-common/src/spis.rs +++ b/nrf-hal-common/src/spis.rs @@ -470,6 +470,7 @@ impl Transfer { .take() .unwrap_or_else(|| unsafe { core::hint::unreachable_unchecked() }); while !inner.spis.is_done() {} + inner.spis.acquire(); compiler_fence(Ordering::Acquire); (inner.buffer, inner.spis) } @@ -513,6 +514,7 @@ impl TransferSplit { .take() .unwrap_or_else(|| unsafe { core::hint::unreachable_unchecked() }); while !inner.spis.is_done() {} + inner.spis.acquire(); compiler_fence(Ordering::Acquire); (inner.tx_buffer, inner.rx_buffer, inner.spis) } From f9c3ea82cac39e3068dbf203168cbd7ffe676ebe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Alse=CC=81r?= Date: Mon, 21 Sep 2020 11:57:42 +0200 Subject: [PATCH 5/9] Remove rx & tx functions --- nrf-hal-common/src/spis.rs | 59 -------------------------------------- 1 file changed, 59 deletions(-) diff --git a/nrf-hal-common/src/spis.rs b/nrf-hal-common/src/spis.rs index a75a798b..ea9f01d0 100644 --- a/nrf-hal-common/src/spis.rs +++ b/nrf-hal-common/src/spis.rs @@ -297,34 +297,6 @@ where &self.0.tasks_release } - /// Receives data into the given `buffer` until it's filled. - /// Buffer must be located in RAM. - /// Returns a value that represents the in-progress DMA transfer. - #[allow(unused_mut)] - pub fn rx(mut self, mut buffer: B) -> Result, Error> - where - B: WriteBuffer, - { - let (ptr, len) = unsafe { buffer.write_buffer() }; - let maxcnt = len * core::mem::size_of::(); - if maxcnt > EASY_DMA_SIZE { - return Err(Error::BufferTooLong); - } - self.0 - .rxd - .ptr - .write(|w| unsafe { w.ptr().bits(ptr as u32) }); - self.0 - .rxd - .maxcnt - .write(|w| unsafe { w.bits(maxcnt as u32) }); - - self.release(); - Ok(Transfer { - inner: Some(Inner { buffer, spis: self }), - }) - } - /// Full duplex DMA transfer. /// Transmits the given buffer while simultaneously receiving data into the same buffer until it is filled. /// Buffer must be located in RAM. @@ -415,37 +387,6 @@ where }) } - /// Transmits the given `tx_buffer`. Buffer must be located in RAM. - /// Returns a value that represents the in-progress DMA transfer. - #[allow(unused_mut)] - pub fn tx(mut self, buffer: B) -> Result, Error> - where - B: ReadBuffer, - { - let (ptr, len) = unsafe { buffer.read_buffer() }; - let maxcnt = len * core::mem::size_of::(); - if maxcnt > EASY_DMA_SIZE { - return Err(Error::BufferTooLong); - } - if (ptr as usize) < SRAM_LOWER || (ptr as usize) > SRAM_UPPER { - return Err(Error::DMABufferNotInDataMemory); - } - - self.0 - .txd - .ptr - .write(|w| unsafe { w.ptr().bits(ptr as u32) }); - self.0 - .txd - .maxcnt - .write(|w| unsafe { w.bits(maxcnt as u32) }); - - self.release(); - Ok(Transfer { - inner: Some(Inner { buffer, spis: self }), - }) - } - /// Returns the raw interface to the underlying SPIS peripheral. pub fn free(self) -> T { self.0 From 0f5af4c06f2542201f515065ae04598e1cd00179 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Alse=CC=81r?= Date: Mon, 21 Sep 2020 14:38:58 +0200 Subject: [PATCH 6/9] Add compiler fences --- nrf-hal-common/src/spis.rs | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/nrf-hal-common/src/spis.rs b/nrf-hal-common/src/spis.rs index ea9f01d0..15fbcf32 100644 --- a/nrf-hal-common/src/spis.rs +++ b/nrf-hal-common/src/spis.rs @@ -156,7 +156,7 @@ where /// Requests acquiring the SPIS semaphore and waits until acquired. #[inline(always)] pub fn acquire(&self) -> &Self { - self.enable(); + compiler_fence(Ordering::SeqCst); self.0.tasks_acquire.write(|w| unsafe { w.bits(1) }); while self.0.events_acquired.read().bits() == 0 {} self @@ -311,6 +311,7 @@ where if maxcnt > EASY_DMA_SIZE { return Err(Error::BufferTooLong); } + compiler_fence(Ordering::SeqCst); self.0 .txd .ptr @@ -359,7 +360,7 @@ where if (tx_ptr as usize) < SRAM_LOWER || (tx_ptr as usize) > SRAM_UPPER { return Err(Error::DMABufferNotInDataMemory); } - + compiler_fence(Ordering::SeqCst); self.0 .txd .ptr @@ -406,13 +407,13 @@ struct Inner { impl Transfer { /// Blocks until the transfer is done and returns the buffer. pub fn wait(mut self) -> (B, Spis) { + compiler_fence(Ordering::SeqCst); let inner = self .inner .take() .unwrap_or_else(|| unsafe { core::hint::unreachable_unchecked() }); while !inner.spis.is_done() {} inner.spis.acquire(); - compiler_fence(Ordering::Acquire); (inner.buffer, inner.spis) } @@ -430,9 +431,9 @@ impl Transfer { impl Drop for Transfer { fn drop(&mut self) { if let Some(inner) = self.inner.as_mut() { + compiler_fence(Ordering::SeqCst); while !inner.spis.is_done() {} inner.spis.disable(); - compiler_fence(Ordering::Acquire); } } } @@ -450,13 +451,13 @@ struct InnerSplit { impl TransferSplit { /// Blocks until the transfer is done and returns the buffer. pub fn wait(mut self) -> (TxB, RxB, Spis) { + compiler_fence(Ordering::SeqCst); let inner = self .inner .take() .unwrap_or_else(|| unsafe { core::hint::unreachable_unchecked() }); while !inner.spis.is_done() {} inner.spis.acquire(); - compiler_fence(Ordering::Acquire); (inner.tx_buffer, inner.rx_buffer, inner.spis) } @@ -474,9 +475,9 @@ impl TransferSplit { impl Drop for TransferSplit { fn drop(&mut self) { if let Some(inner) = self.inner.as_mut() { + compiler_fence(Ordering::SeqCst); while !inner.spis.is_done() {} inner.spis.disable(); - compiler_fence(Ordering::Acquire); } } } From ccdd6a0003c18c35428c4e8296999768df87db4e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Alse=CC=81r?= Date: Tue, 22 Sep 2020 10:43:48 +0200 Subject: [PATCH 7/9] Add Pins struct owning pins, add try_acquire, add docs, cleanup --- CHANGELOG.md | 2 +- examples/spis-demo/src/main.rs | 10 ++- nrf-hal-common/src/spis.rs | 143 +++++++++++++++++++-------------- 3 files changed, 91 insertions(+), 64 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2a540f7a..06836e73 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,7 @@ - TWIS module ([#196]). - PWM module ([#200]). - I2S module ([#201]). +- SPIS module ([#226]). ### Fixes @@ -95,6 +96,5 @@ None [#168]: https://github.com/nrf-rs/nrf-hal/pull/168 [#167]: https://github.com/nrf-rs/nrf-hal/pull/167 [#172]: https://github.com/nrf-rs/nrf-hal/pull/172 - [0.11.0]: https://github.com/nrf-rs/nrf-hal/releases/tag/v0.11.0 [0.11.1]: https://github.com/nrf-rs/nrf-hal/releases/tag/v0.11.1 diff --git a/examples/spis-demo/src/main.rs b/examples/spis-demo/src/main.rs index 84ea7845..104a8e4a 100644 --- a/examples/spis-demo/src/main.rs +++ b/examples/spis-demo/src/main.rs @@ -38,10 +38,12 @@ const APP: () = { let spis = Spis::new( ctx.device.SPIS0, - &sck_pin, - &cs_pin, - Some(&copi_pin), - Some(&cipo_pin), + Pins { + sck: sck_pin, + cs: cs_pin, + copi: Some(copi_pin), + cipo: Some(cipo_pin), + }, ); spis.enable_interrupt(SpisEvent::End); diff --git a/nrf-hal-common/src/spis.rs b/nrf-hal-common/src/spis.rs index 15fbcf32..4166ee22 100644 --- a/nrf-hal-common/src/spis.rs +++ b/nrf-hal-common/src/spis.rs @@ -1,3 +1,7 @@ +//! HAL interface to the SPIS peripheral. +//! +//! A module for SPI communication in peripheral mode. + use core::{ ops::Deref, sync::atomic::{compiler_fence, Ordering}, @@ -27,34 +31,32 @@ use crate::{ }; use embedded_dma::*; -pub struct Spis(T); +/// Interface to a SPIS instance. +pub struct Spis { + spis: T, + pins: Pins, +} impl Spis where T: Instance, { /// Takes ownership of the raw SPIS peripheral, returning a safe wrapper. - pub fn new( - spis: T, - sck_pin: &Pin>, - cs_pin: &Pin>, - copi_pin: Option<&Pin>>, - cipo_pin: Option<&Pin>>, - ) -> Self { + pub fn new(spis: T, pins: Pins) -> Self { spis.psel.sck.write(|w| { - unsafe { w.pin().bits(sck_pin.pin()) }; + unsafe { w.pin().bits(pins.sck.pin()) }; #[cfg(any(feature = "52833", feature = "52840"))] - w.port().bit(sck_pin.port().bit()); + w.port().bit(pins.sck.port().bit()); w.connect().connected() }); spis.psel.csn.write(|w| { - unsafe { w.pin().bits(cs_pin.pin()) }; + unsafe { w.pin().bits(pins.cs.pin()) }; #[cfg(any(feature = "52833", feature = "52840"))] - w.port().bit(cs_pin.port().bit()); + w.port().bit(pins.cs.port().bit()); w.connect().connected() }); - if let Some(p) = copi_pin { + if let Some(p) = &pins.copi { spis.psel.mosi.write(|w| { unsafe { w.pin().bits(p.pin()) }; #[cfg(any(feature = "52833", feature = "52840"))] @@ -63,7 +65,7 @@ where }); } - if let Some(p) = cipo_pin { + if let Some(p) = &pins.cipo { spis.psel.miso.write(|w| { unsafe { w.pin().bits(p.pin()) }; #[cfg(any(feature = "52833", feature = "52840"))] @@ -77,41 +79,43 @@ where spis.config .modify(|_r, w| w.cpha().bit(Phase::Trailing.into())); spis.enable.write(|w| w.enable().enabled()); - Self(spis) + Self { spis, pins } } /// Sets the ´default´ character (character clocked out in case of an ignored transaction). #[inline(always)] pub fn set_default_char(&self, def: u8) -> &Self { - self.0.def.write(|w| unsafe { w.def().bits(def) }); + self.spis.def.write(|w| unsafe { w.def().bits(def) }); self } /// Sets the over-read character (character sent on over-read of the transmit buffer). #[inline(always)] pub fn set_orc(&self, orc: u8) -> &Self { - self.0.orc.write(|w| unsafe { w.orc().bits(orc) }); + self.spis.orc.write(|w| unsafe { w.orc().bits(orc) }); self } /// Sets bit order. #[inline(always)] pub fn set_order(&self, order: Order) -> &Self { - self.0.config.modify(|_r, w| w.order().bit(order.into())); + self.spis.config.modify(|_r, w| w.order().bit(order.into())); self } /// Sets serial clock (SCK) polarity. #[inline(always)] pub fn set_polarity(&self, polarity: Polarity) -> &Self { - self.0.config.modify(|_r, w| w.cpol().bit(polarity.into())); + self.spis + .config + .modify(|_r, w| w.cpol().bit(polarity.into())); self } /// Sets serial clock (SCK) phase. #[inline(always)] pub fn set_phase(&self, phase: Phase) -> &Self { - self.0.config.modify(|_r, w| w.cpha().bit(phase.into())); + self.spis.config.modify(|_r, w| w.cpha().bit(phase.into())); self } @@ -142,14 +146,14 @@ where /// Enables the SPIS instance. #[inline(always)] pub fn enable(&self) -> &Self { - self.0.enable.write(|w| w.enable().enabled()); + self.spis.enable.write(|w| w.enable().enabled()); self } /// Disables the SPIS module. #[inline(always)] pub fn disable(&self) -> &Self { - self.0.enable.write(|w| w.enable().disabled()); + self.spis.enable.write(|w| w.enable().disabled()); self } @@ -157,22 +161,29 @@ where #[inline(always)] pub fn acquire(&self) -> &Self { compiler_fence(Ordering::SeqCst); - self.0.tasks_acquire.write(|w| unsafe { w.bits(1) }); - while self.0.events_acquired.read().bits() == 0 {} + self.spis.tasks_acquire.write(|w| unsafe { w.bits(1) }); + while self.spis.events_acquired.read().bits() == 0 {} + self + } + + /// Requests acquiring the SPIS semaphore. + #[inline(always)] + pub fn try_acquire(&self) -> &Self { + self.spis.tasks_acquire.write(|w| unsafe { w.bits(1) }); self } /// Releases the SPIS semaphore, enabling the SPIS to acquire it. #[inline(always)] pub fn release(&self) -> &Self { - self.0.tasks_release.write(|w| unsafe { w.bits(1) }); + self.spis.tasks_release.write(|w| unsafe { w.bits(1) }); self } /// Enables interrupt for specified event. #[inline(always)] pub fn enable_interrupt(&self, command: SpisEvent) -> &Self { - self.0.intenset.modify(|_r, w| match command { + self.spis.intenset.modify(|_r, w| match command { SpisEvent::Acquired => w.acquired().set_bit(), SpisEvent::End => w.end().set_bit(), SpisEvent::EndRx => w.endrx().set_bit(), @@ -183,7 +194,7 @@ where /// Disables interrupt for specified event. #[inline(always)] pub fn disable_interrupt(&self, command: SpisEvent) -> &Self { - self.0.intenclr.write(|w| match command { + self.spis.intenclr.write(|w| match command { SpisEvent::Acquired => w.acquired().set_bit(), SpisEvent::End => w.end().set_bit(), SpisEvent::EndRx => w.endrx().set_bit(), @@ -194,25 +205,25 @@ where /// Automatically acquire the semaphore after transfer has ended. #[inline(always)] pub fn auto_acquire(&self, enabled: bool) -> &Self { - self.0.shorts.write(|w| w.end_acquire().bit(enabled)); + self.spis.shorts.write(|w| w.end_acquire().bit(enabled)); self } /// Resets all events. #[inline(always)] pub fn reset_events(&self) { - self.0.events_acquired.reset(); - self.0.events_end.reset(); - self.0.events_endrx.reset(); + self.spis.events_acquired.reset(); + self.spis.events_end.reset(); + self.spis.events_endrx.reset(); } /// Resets specified event. #[inline(always)] pub fn reset_event(&self, event: SpisEvent) { match event { - SpisEvent::Acquired => self.0.events_acquired.reset(), - SpisEvent::End => self.0.events_end.reset(), - SpisEvent::EndRx => self.0.events_endrx.reset(), + SpisEvent::Acquired => self.spis.events_acquired.reset(), + SpisEvent::End => self.spis.events_end.reset(), + SpisEvent::EndRx => self.spis.events_endrx.reset(), }; } @@ -220,46 +231,46 @@ where #[inline(always)] pub fn is_event_triggered(&self, event: SpisEvent) -> bool { match event { - SpisEvent::Acquired => self.0.events_acquired.read().bits() != 0, - SpisEvent::End => self.0.events_end.read().bits() != 0, - SpisEvent::EndRx => self.0.events_endrx.read().bits() != 0, + SpisEvent::Acquired => self.spis.events_acquired.read().bits() != 0, + SpisEvent::End => self.spis.events_end.read().bits() != 0, + SpisEvent::EndRx => self.spis.events_endrx.read().bits() != 0, } } /// Checks if the granted transfer is done. #[inline(always)] pub fn is_done(&self) -> bool { - self.0.events_end.read().bits() != 0 || self.0.events_endrx.read().bits() != 0 + self.spis.events_end.read().bits() != 0 || self.spis.events_endrx.read().bits() != 0 } /// Checks if the semaphore is acquired. #[inline(always)] pub fn is_acquired(&self) -> bool { - self.0.events_acquired.read().bits() != 0 + self.spis.events_acquired.read().bits() != 0 } /// Checks if last transaction overread. #[inline(always)] pub fn is_overread(&self) -> bool { - self.0.status.read().overread().is_present() + self.spis.status.read().overread().is_present() } /// Checks if last transaction overflowed. #[inline(always)] pub fn is_overflow(&self) -> bool { - self.0.status.read().overflow().is_present() + self.spis.status.read().overflow().is_present() } /// Returns number of bytes received in last granted transaction. #[inline(always)] pub fn amount(&self) -> u32 { - self.0.rxd.amount.read().bits() + self.spis.rxd.amount.read().bits() } /// Returns the semaphore status. #[inline(always)] pub fn semaphore_status(&self) -> SemaphoreStatus { - match self.0.semstat.read().bits() { + match self.spis.semstat.read().bits() { 0 => SemaphoreStatus::Free, 1 => SemaphoreStatus::CPU, 2 => SemaphoreStatus::SPIS, @@ -270,31 +281,31 @@ where /// Returns reference to `Acquired` event endpoint for PPI. #[inline(always)] pub fn event_acquired(&self) -> &Reg { - &self.0.events_acquired + &self.spis.events_acquired } /// Returns reference to `End` event endpoint for PPI. #[inline(always)] pub fn event_end(&self) -> &Reg { - &self.0.events_end + &self.spis.events_end } /// Returns reference to `EndRx` event endpoint for PPI. #[inline(always)] pub fn event_end_rx(&self) -> &Reg { - &self.0.events_endrx + &self.spis.events_endrx } /// Returns reference to `Acquire` task endpoint for PPI. #[inline(always)] pub fn task_acquire(&self) -> &Reg { - &self.0.tasks_acquire + &self.spis.tasks_acquire } /// Returns reference to `Release` task endpoint for PPI. #[inline(always)] pub fn task_release(&self) -> &Reg { - &self.0.tasks_release + &self.spis.tasks_release } /// Full duplex DMA transfer. @@ -312,19 +323,19 @@ where return Err(Error::BufferTooLong); } compiler_fence(Ordering::SeqCst); - self.0 + self.spis .txd .ptr .write(|w| unsafe { w.ptr().bits(ptr as u32) }); - self.0 + self.spis .rxd .ptr .write(|w| unsafe { w.ptr().bits(ptr as u32) }); - self.0 + self.spis .txd .maxcnt .write(|w| unsafe { w.bits(maxcnt as u32) }); - self.0 + self.spis .rxd .maxcnt .write(|w| unsafe { w.bits(maxcnt as u32) }); @@ -354,26 +365,26 @@ where let (tx_ptr, tx_len) = unsafe { tx_buffer.read_buffer() }; let rx_maxcnt = rx_len * core::mem::size_of::(); let tx_maxcnt = tx_len * core::mem::size_of::(); - if rx_maxcnt > EASY_DMA_SIZE || tx_maxcnt > EASY_DMA_SIZE { + if rx_maxcnt.max(tx_maxcnt) > EASY_DMA_SIZE { return Err(Error::BufferTooLong); } if (tx_ptr as usize) < SRAM_LOWER || (tx_ptr as usize) > SRAM_UPPER { return Err(Error::DMABufferNotInDataMemory); } compiler_fence(Ordering::SeqCst); - self.0 + self.spis .txd .ptr .write(|w| unsafe { w.ptr().bits(tx_ptr as u32) }); - self.0 + self.spis .rxd .ptr .write(|w| unsafe { w.ptr().bits(rx_ptr as u32) }); - self.0 + self.spis .rxd .maxcnt .write(|w| unsafe { w.bits(rx_maxcnt as u32) }); - self.0 + self.spis .txd .maxcnt .write(|w| unsafe { w.bits(tx_maxcnt as u32) }); @@ -389,8 +400,8 @@ where } /// Returns the raw interface to the underlying SPIS peripheral. - pub fn free(self) -> T { - self.0 + pub fn free(self) -> (T, Pins) { + (self.spis, self.pins) } } @@ -557,6 +568,20 @@ pub enum Error { BufferTooLong, } +/// GPIO pins for SPIS interface. +pub struct Pins { + /// SPI clock + pub sck: Pin>, + /// Chip select + pub cs: Pin>, + /// COPI Controller out, peripheral in + /// None if unused + pub copi: Option>>, + /// CIPO Controller in, peripheral out + /// None if unused + pub cipo: Option>>, +} + mod sealed { pub trait Sealed {} impl Sealed for super::SPIS0 {} From 9f0b23ce6032e82a7d618e9de43d128a5338ad7b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Alse=CC=81r?= Date: Tue, 22 Sep 2020 10:54:28 +0200 Subject: [PATCH 8/9] Cleanup internal naming --- nrf-hal-common/src/spis.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/nrf-hal-common/src/spis.rs b/nrf-hal-common/src/spis.rs index 4166ee22..44784c16 100644 --- a/nrf-hal-common/src/spis.rs +++ b/nrf-hal-common/src/spis.rs @@ -182,8 +182,8 @@ where /// Enables interrupt for specified event. #[inline(always)] - pub fn enable_interrupt(&self, command: SpisEvent) -> &Self { - self.spis.intenset.modify(|_r, w| match command { + pub fn enable_interrupt(&self, event: SpisEvent) -> &Self { + self.spis.intenset.modify(|_r, w| match event { SpisEvent::Acquired => w.acquired().set_bit(), SpisEvent::End => w.end().set_bit(), SpisEvent::EndRx => w.endrx().set_bit(), @@ -193,8 +193,8 @@ where /// Disables interrupt for specified event. #[inline(always)] - pub fn disable_interrupt(&self, command: SpisEvent) -> &Self { - self.spis.intenclr.write(|w| match command { + pub fn disable_interrupt(&self, event: SpisEvent) -> &Self { + self.spis.intenclr.write(|w| match event { SpisEvent::Acquired => w.acquired().set_bit(), SpisEvent::End => w.end().set_bit(), SpisEvent::EndRx => w.endrx().set_bit(), From 23f285d95bae177644ca7e2b448743c1c5c3a842 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Alse=CC=81r?= Date: Tue, 22 Sep 2020 11:06:17 +0200 Subject: [PATCH 9/9] Change try_acquire to return Result, add SemaphoreNotAvailable error, update docs --- nrf-hal-common/src/spis.rs | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) diff --git a/nrf-hal-common/src/spis.rs b/nrf-hal-common/src/spis.rs index 44784c16..b45e64bc 100644 --- a/nrf-hal-common/src/spis.rs +++ b/nrf-hal-common/src/spis.rs @@ -41,7 +41,8 @@ impl Spis where T: Instance, { - /// Takes ownership of the raw SPIS peripheral, returning a safe wrapper. + /// Takes ownership of the raw SPIS peripheral and relevant pins, + /// returning a safe wrapper. pub fn new(spis: T, pins: Pins) -> Self { spis.psel.sck.write(|w| { unsafe { w.pin().bits(pins.sck.pin()) }; @@ -166,11 +167,20 @@ where self } - /// Requests acquiring the SPIS semaphore. + /// Requests acquiring the SPIS semaphore, returning an error if not + /// possible. + /// + /// Note: The semaphore will still be requested, and will be made + /// available at a later point. #[inline(always)] - pub fn try_acquire(&self) -> &Self { + pub fn try_acquire(&self) -> Result<&Self, Error> { + compiler_fence(Ordering::SeqCst); self.spis.tasks_acquire.write(|w| unsafe { w.bits(1) }); - self + if self.spis.events_acquired.read().bits() != 0 { + Ok(self) + } else { + Err(Error::SemaphoreNotAvailable) + } } /// Releases the SPIS semaphore, enabling the SPIS to acquire it. @@ -566,6 +576,7 @@ pub enum Mode { pub enum Error { DMABufferNotInDataMemory, BufferTooLong, + SemaphoreNotAvailable, } /// GPIO pins for SPIS interface.