Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
139 changes: 126 additions & 13 deletions embassy-rp/src/spi.rs
Original file line number Diff line number Diff line change
Expand Up @@ -81,19 +81,25 @@ impl<'d, T: Instance, M: Mode> Spi<'d, T, M> {
tx_dma: Option<Channel<'d>>,
rx_dma: Option<Channel<'d>>,
config: Config,
slave: bool,
) -> Self {
Self::apply_config(&inner, &config);

let p = inner.regs();

// disable (to ensure correct master/slave bit)
p.cr1().write(|w| w.set_sse(false));
// Its necessary to disable SPI before setting slave!
p.cr1().modify(|w| w.set_ms(slave));

Self::apply_config(&inner, &config);

// Always enable DREQ signals -- harmless if DMA is not listening
p.dmacr().write(|reg| {
reg.set_rxdmae(true);
reg.set_txdmae(true);
});

// finally, enable.
p.cr1().write(|w| w.set_sse(true));
// finally, enable (preserving MS bit)
p.cr1().modify(|w| w.set_sse(true));

if let Some(pin) = &clk {
pin.gpio().ctrl().write(|w| w.set_funcsel(1));
Expand Down Expand Up @@ -240,31 +246,31 @@ impl<'d, T: Instance, M: Mode> Spi<'d, T, M> {
pub fn set_frequency(&mut self, freq: u32) {
let (presc, postdiv) = calc_prescs(freq);
let p = self.inner.regs();
// disable
p.cr1().write(|w| w.set_sse(false));
// disable (preserving MS bit)
p.cr1().modify(|w| w.set_sse(false));

// change stuff
p.cpsr().write(|w| w.set_cpsdvsr(presc));
p.cr0().modify(|w| {
w.set_scr(postdiv);
});

// enable
p.cr1().write(|w| w.set_sse(true));
// enable (preserving MS bit)
p.cr1().modify(|w| w.set_sse(true));
}

/// Set SPI config.
pub fn set_config(&mut self, config: &Config) {
let p = self.inner.regs();

// disable
p.cr1().write(|w| w.set_sse(false));
// disable (preserving MS bit)
p.cr1().modify(|w| w.set_sse(false));

// change stuff
Self::apply_config(&self.inner, config);

// enable
p.cr1().write(|w| w.set_sse(true));
// enable (preserving MS bit)
p.cr1().modify(|w| w.set_sse(true));
}
}

Expand All @@ -286,6 +292,7 @@ impl<'d, T: Instance> Spi<'d, T, Blocking> {
None,
None,
config,
false,
)
}

Expand All @@ -305,12 +312,13 @@ impl<'d, T: Instance> Spi<'d, T, Blocking> {
None,
None,
config,
false,
)
}

/// Create an SPI driver in blocking mode supporting writes only, without SCK pin.
pub fn new_blocking_txonly_nosck(inner: Peri<'d, T>, mosi: Peri<'d, impl MosiPin<T> + 'd>, config: Config) -> Self {
Self::new_inner(inner, None, Some(mosi.into()), None, None, None, None, config)
Self::new_inner(inner, None, Some(mosi.into()), None, None, None, None, config, false)
}

/// Create an SPI driver in blocking mode supporting reads only.
Expand All @@ -329,6 +337,50 @@ impl<'d, T: Instance> Spi<'d, T, Blocking> {
None,
None,
config,
false,
)
}

/// Create an SPI driver in blocking slave mode.
pub fn new_blocking_slave(
inner: Peri<'d, T>,
clk: Peri<'d, impl ClkPin<T> + 'd>,
mosi: Peri<'d, impl MosiPin<T> + 'd>,
miso: Peri<'d, impl MisoPin<T> + 'd>,
cs: Peri<'d, impl CsPin<T> + 'd>,
config: Config,
) -> Self {
Self::new_inner(
inner,
Some(clk.into()),
Some(mosi.into()),
Some(miso.into()),
Some(cs.into()),
None,
None,
config,
true,
)
}

/// Create an SPI driver in blocking slave mode supporting reads only.
pub fn new_blocking_slave_rxonly(
inner: Peri<'d, T>,
clk: Peri<'d, impl ClkPin<T> + 'd>,
miso: Peri<'d, impl MisoPin<T> + 'd>,
cs: Peri<'d, impl CsPin<T> + 'd>,
config: Config,
) -> Self {
Self::new_inner(
inner,
Some(clk.into()),
None,
Some(miso.into()),
Some(cs.into()),
None,
None,
config,
true,
)
}
}
Expand Down Expand Up @@ -358,6 +410,7 @@ impl<'d, T: Instance> Spi<'d, T, Async> {
Some(tx_dma_ch),
Some(rx_dma_ch),
config,
false,
)
}

Expand All @@ -380,6 +433,7 @@ impl<'d, T: Instance> Spi<'d, T, Async> {
Some(tx_dma_ch),
None,
config,
false,
)
}

Expand All @@ -402,6 +456,7 @@ impl<'d, T: Instance> Spi<'d, T, Async> {
Some(tx_dma_ch),
None,
config,
false,
)
}

Expand All @@ -428,6 +483,64 @@ impl<'d, T: Instance> Spi<'d, T, Async> {
Some(tx_dma_ch),
Some(rx_dma_ch),
config,
false,
)
}

/// Create an SPI driver in async mode supporting DMA operations in slave mode.
pub fn new_slave<TxDma: ChannelInstance, RxDma: ChannelInstance>(
inner: Peri<'d, T>,
clk: Peri<'d, impl ClkPin<T> + 'd>,
mosi: Peri<'d, impl MosiPin<T> + 'd>,
miso: Peri<'d, impl MisoPin<T> + 'd>,
cs: Peri<'d, impl CsPin<T> + 'd>,
tx_dma: Peri<'d, TxDma>,
rx_dma: Peri<'d, RxDma>,
irq: impl interrupt::typelevel::Binding<TxDma::Interrupt, dma::InterruptHandler<TxDma>>
+ interrupt::typelevel::Binding<RxDma::Interrupt, dma::InterruptHandler<RxDma>>
+ 'd,
config: Config,
) -> Self {
let tx_dma_ch = dma::Channel::new(tx_dma, irq);
let rx_dma_ch = dma::Channel::new(rx_dma, irq);
Self::new_inner(
inner,
Some(clk.into()),
Some(mosi.into()),
Some(miso.into()),
Some(cs.into()),
Some(tx_dma_ch),
Some(rx_dma_ch),
config,
true,
)
}

/// Create an SPI driver in async mode supporting DMA read operations only in slave mode.
pub fn new_slave_rxonly<TxDma: ChannelInstance, RxDma: ChannelInstance>(
inner: Peri<'d, T>,
clk: Peri<'d, impl ClkPin<T> + 'd>,
miso: Peri<'d, impl MisoPin<T> + 'd>,
cs: Peri<'d, impl CsPin<T> + 'd>,
tx_dma: Peri<'d, TxDma>,
rx_dma: Peri<'d, RxDma>,
irq: impl interrupt::typelevel::Binding<TxDma::Interrupt, dma::InterruptHandler<TxDma>>
+ interrupt::typelevel::Binding<RxDma::Interrupt, dma::InterruptHandler<RxDma>>
+ 'd,
config: Config,
) -> Self {
let tx_dma_ch = dma::Channel::new(tx_dma, irq);
let rx_dma_ch = dma::Channel::new(rx_dma, irq);
Self::new_inner(
inner,
Some(clk.into()),
None,
Some(miso.into()),
Some(cs.into()),
Some(tx_dma_ch),
Some(rx_dma_ch),
config,
true,
)
}

Expand Down
113 changes: 113 additions & 0 deletions examples/rp/src/bin/spi_slave_async.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
//! This example shows how to use SPI (Serial Peripheral Interface) master and slave in the RP2040 chip.
//! Note: By default the SPI slave on the RP2040 requires CS to be toggled for each byte
//!
//!
//! No specific hardware is specified in this example.
//! Connect the following pins:
//! Master (MISO) PIN 12 <- PIN 3 (MOSI) Slave
//! Master (MOSI) PIN 11 -> PIN 0 (MISO) Slave
//! Master (CLK) PIN 10 -> PIN 2 (CLK) Slave
//! Master (CS) PIN 13 -> PIN 1 (CS) Slave
//!
//! You should recieve the tx in rx of the other.
//! Sample output:
//! 0.008 [INFO ] Master: TX: [1, 2, 3, 4, 5, 6] RX: [a, b, c, d, e, f]
//! 0.009 [INFO ] Slave: TX: [a, b, c, d, e, f] RX: [1, 2, 3, 4, 5, 6]

#![no_std]
#![no_main]

use defmt::*;
use embassy_executor::Spawner;
use embassy_futures::join::join;
use embassy_rp::gpio::{Level, Output};
use embassy_rp::peripherals::{DMA_CH0, DMA_CH1, DMA_CH2, DMA_CH3};
use embassy_rp::spi::{Config, Spi};
use embassy_rp::{bind_interrupts, dma};
use embassy_time::Timer;
use {defmt_rtt as _, panic_probe as _};

bind_interrupts!(struct Irqs {
DMA_IRQ_0 => dma::InterruptHandler<DMA_CH0>, dma::InterruptHandler<DMA_CH1>, dma::InterruptHandler<DMA_CH2>, dma::InterruptHandler<DMA_CH3>;
});

#[embassy_executor::main]
async fn main(_spawner: Spawner) {
let p = embassy_rp::init(Default::default());
info!("Starting up!");

// Master wiring on SPI1
let master_miso = p.PIN_12;
let master_mosi = p.PIN_11;
let master_clk = p.PIN_10;
let master_cs = Output::new(p.PIN_13, Level::High);

// Slave wiring on SPI0
let slave_miso = p.PIN_0;
let slave_mosi = p.PIN_3;
let slave_clk = p.PIN_2;
let slave_cs_pin = p.PIN_1;

let master_spi = Spi::new(
p.SPI1,
master_clk,
master_mosi,
master_miso,
p.DMA_CH0,
p.DMA_CH1,
Irqs,
Config::default(),
);

// Pull down CS for every byte
let config = Config::default();
// If you need a single CS pulse for multiple bytes, use the following:
// let mut config = Config::default();
// config.phase = embassy_rp::spi::Phase::CaptureOnSecondTransition;
// config.polarity = embassy_rp::spi::Polarity::IdleHigh;
let slave_spi = Spi::new_slave(
p.SPI0,
slave_clk,
slave_mosi,
slave_miso,
slave_cs_pin,
p.DMA_CH2,
p.DMA_CH3,
Irqs,
config,
);

let master_fut = async move {
let mut spi = master_spi;
let mut cs = master_cs;

loop {
let tx_buf = [1_u8, 2, 3, 4, 5, 6];
let mut rx_buf = [0_u8; 6];

// CS needs to be toggled for each byte transfer (for slave work correctly)
for i in 0..tx_buf.len() {
cs.set_low();
spi.transfer(&mut rx_buf[i..i + 1], &tx_buf[i..i + 1]).await.unwrap();
cs.set_high();
}

info!("Master: TX: {:x} RX: {:x}", tx_buf, rx_buf);
Timer::after_secs(1).await;
}
};

let slave_fut = async move {
let mut spi = slave_spi;
let tx_buf = [0xA_u8, 0xB, 0xC, 0xD, 0xE, 0xF];
let mut rx_buf = [0_u8; 6];

loop {
spi.transfer(&mut rx_buf, &tx_buf).await.unwrap();

info!("Slave: TX: {:x} RX: {:x}", tx_buf, rx_buf);
}
};

join(slave_fut, master_fut).await;
}
Loading