diff --git a/embassy-rp/src/dma.rs b/embassy-rp/src/dma.rs index 75d7492e0..acf338225 100644 --- a/embassy-rp/src/dma.rs +++ b/embassy-rp/src/dma.rs @@ -40,10 +40,10 @@ pub(crate) unsafe fn init() { pub unsafe fn read<'a, C: Channel, W: Word>( ch: impl Peripheral
+ 'a, from: *const W, - to: &mut [W], + to: *mut [W], dreq: u8, ) -> Transfer<'a, C> { - let (to_ptr, len) = crate::dma::slice_ptr_parts_mut(to); + let (to_ptr, len) = crate::dma::slice_ptr_parts(to); copy_inner( ch, from as *const u32, @@ -58,7 +58,7 @@ pub unsafe fn read<'a, C: Channel, W: Word>( pub unsafe fn write<'a, C: Channel, W: Word>( ch: impl Peripheral
+ 'a,
- from: &[W],
+ from: *const [W],
to: *mut W,
dreq: u8,
) -> Transfer<'a, C> {
diff --git a/embassy-rp/src/spi.rs b/embassy-rp/src/spi.rs
index d0261598e..74f0b04de 100644
--- a/embassy-rp/src/spi.rs
+++ b/embassy-rp/src/spi.rs
@@ -1,7 +1,11 @@
+use core::marker::PhantomData;
+
use embassy_embedded_hal::SetConfig;
use embassy_hal_common::{into_ref, PeripheralRef};
pub use embedded_hal_02::spi::{Phase, Polarity};
+use futures::future::join;
+use crate::dma::{AnyChannel, Channel};
use crate::gpio::sealed::Pin as _;
use crate::gpio::{AnyPin, Pin as GpioPin};
use crate::{pac, peripherals, Peripheral};
@@ -30,8 +34,11 @@ impl Default for Config {
}
}
-pub struct Spi<'d, T: Instance> {
+pub struct Spi<'d, T: Instance, M: Mode> {
inner: PeripheralRef<'d, T>,
+ tx_dma: Option + 'd,
- clk: impl Peripheral + 'd> + 'd,
- mosi: impl Peripheral + 'd> + 'd,
- miso: impl Peripheral + 'd> + 'd,
- config: Config,
- ) -> Self {
- into_ref!(clk, mosi, miso);
- Self::new_inner(
- inner,
- Some(clk.map_into()),
- Some(mosi.map_into()),
- Some(miso.map_into()),
- None,
- config,
- )
- }
-
- pub fn new_txonly(
- inner: impl Peripheral + 'd,
- clk: impl Peripheral + 'd> + 'd,
- mosi: impl Peripheral + 'd> + 'd,
- config: Config,
- ) -> Self {
- into_ref!(clk, mosi);
- Self::new_inner(inner, Some(clk.map_into()), Some(mosi.map_into()), None, None, config)
- }
-
- pub fn new_rxonly(
- inner: impl Peripheral + 'd,
- clk: impl Peripheral + 'd> + 'd,
- miso: impl Peripheral + 'd> + 'd,
- config: Config,
- ) -> Self {
- into_ref!(clk, miso);
- Self::new_inner(inner, Some(clk.map_into()), None, Some(miso.map_into()), None, config)
- }
-
+impl<'d, T: Instance, M: Mode> Spi<'d, T, M> {
fn new_inner(
inner: impl Peripheral + 'd,
clk: Option + 'd,
+ clk: impl Peripheral + 'd> + 'd,
+ mosi: impl Peripheral + 'd> + 'd,
+ miso: impl Peripheral + 'd> + 'd,
+ config: Config,
+ ) -> Self {
+ into_ref!(clk, mosi, miso);
+ Self::new_inner(
+ inner,
+ Some(clk.map_into()),
+ Some(mosi.map_into()),
+ Some(miso.map_into()),
+ None,
+ None,
+ None,
+ config,
+ )
+ }
+
+ pub fn new_blocking_txonly(
+ inner: impl Peripheral + 'd,
+ clk: impl Peripheral + 'd> + 'd,
+ mosi: impl Peripheral + 'd> + 'd,
+ config: Config,
+ ) -> Self {
+ into_ref!(clk, mosi);
+ Self::new_inner(
+ inner,
+ Some(clk.map_into()),
+ Some(mosi.map_into()),
+ None,
+ None,
+ None,
+ None,
+ config,
+ )
+ }
+
+ pub fn new_blocking_rxonly(
+ inner: impl Peripheral + 'd,
+ clk: impl Peripheral + 'd> + 'd,
+ miso: impl Peripheral + 'd> + 'd,
+ config: Config,
+ ) -> Self {
+ into_ref!(clk, miso);
+ Self::new_inner(
+ inner,
+ Some(clk.map_into()),
+ None,
+ Some(miso.map_into()),
+ None,
+ None,
+ None,
+ config,
+ )
+ }
+}
+
+impl<'d, T: Instance> Spi<'d, T, Async> {
+ pub fn new(
+ inner: impl Peripheral + 'd,
+ clk: impl Peripheral + 'd> + 'd,
+ mosi: impl Peripheral + 'd> + 'd,
+ miso: impl Peripheral + 'd> + 'd,
+ tx_dma: impl Peripheral + 'd,
+ rx_dma: impl Peripheral + 'd,
+ config: Config,
+ ) -> Self {
+ into_ref!(tx_dma, rx_dma, clk, mosi, miso);
+ Self::new_inner(
+ inner,
+ Some(clk.map_into()),
+ Some(mosi.map_into()),
+ Some(miso.map_into()),
+ None,
+ Some(tx_dma.map_into()),
+ Some(rx_dma.map_into()),
+ config,
+ )
+ }
+
+ pub fn new_txonly(
+ inner: impl Peripheral + 'd,
+ clk: impl Peripheral + 'd> + 'd,
+ mosi: impl Peripheral + 'd> + 'd,
+ tx_dma: impl Peripheral + 'd,
+ config: Config,
+ ) -> Self {
+ into_ref!(tx_dma, clk, mosi);
+ Self::new_inner(
+ inner,
+ Some(clk.map_into()),
+ Some(mosi.map_into()),
+ None,
+ None,
+ Some(tx_dma.map_into()),
+ None,
+ config,
+ )
+ }
+
+ pub fn new_rxonly(
+ inner: impl Peripheral + 'd,
+ clk: impl Peripheral + 'd> + 'd,
+ miso: impl Peripheral + 'd> + 'd,
+ rx_dma: impl Peripheral + 'd,
+ config: Config,
+ ) -> Self {
+ into_ref!(rx_dma, clk, miso);
+ Self::new_inner(
+ inner,
+ Some(clk.map_into()),
+ None,
+ Some(miso.map_into()),
+ None,
+ None,
+ Some(rx_dma.map_into()),
+ config,
+ )
+ }
+
+ pub async fn write(&mut self, buffer: &[u8]) -> Result<(), Error> {
+ let ch = self.tx_dma.as_mut().unwrap();
+ let transfer = unsafe {
+ self.inner.regs().dmacr().modify(|reg| {
+ reg.set_txdmae(true);
+ });
+ // If we don't assign future to a variable, the data register pointer
+ // is held across an await and makes the future non-Send.
+ crate::dma::write(ch, buffer, self.inner.regs().dr().ptr() as *mut _, T::TX_DREQ)
+ };
+ transfer.await;
+ Ok(())
+ }
+
+ pub async fn read(&mut self, buffer: &mut [u8]) -> Result<(), Error> {
+ let ch = self.rx_dma.as_mut().unwrap();
+ let transfer = unsafe {
+ self.inner.regs().dmacr().modify(|reg| {
+ reg.set_rxdmae(true);
+ });
+ // If we don't assign future to a variable, the data register pointer
+ // is held across an await and makes the future non-Send.
+ crate::dma::read(ch, self.inner.regs().dr().ptr() as *const _, buffer, T::RX_DREQ)
+ };
+ transfer.await;
+ Ok(())
+ }
+
+ pub async fn transfer(&mut self, rx_buffer: &mut [u8], tx_buffer: &[u8]) -> Result<(), Error> {
+ self.transfer_inner(rx_buffer, tx_buffer).await
+ }
+
+ pub async fn transfer_in_place(&mut self, words: &mut [u8]) -> Result<(), Error> {
+ self.transfer_inner(words, words).await
+ }
+
+ async fn transfer_inner(&mut self, rx_ptr: *mut [u8], tx_ptr: *const [u8]) -> Result<(), Error> {
+ let (_, from_len) = crate::dma::slice_ptr_parts(tx_ptr);
+ let (_, to_len) = crate::dma::slice_ptr_parts_mut(rx_ptr);
+ assert_eq!(from_len, to_len);
+ let tx_ch = self.tx_dma.as_mut().unwrap();
+ let tx_transfer = unsafe {
+ self.inner.regs().dmacr().modify(|reg| {
+ reg.set_txdmae(true);
+ });
+ // If we don't assign future to a variable, the data register pointer
+ // is held across an await and makes the future non-Send.
+ crate::dma::write(tx_ch, tx_ptr, self.inner.regs().dr().ptr() as *mut _, T::TX_DREQ)
+ };
+ let rx_ch = self.rx_dma.as_mut().unwrap();
+ let rx_transfer = unsafe {
+ self.inner.regs().dmacr().modify(|reg| {
+ reg.set_rxdmae(true);
+ });
+ // If we don't assign future to a variable, the data register pointer
+ // is held across an await and makes the future non-Send.
+ crate::dma::read(rx_ch, self.inner.regs().dr().ptr() as *const _, rx_ptr, T::RX_DREQ)
+ };
+ join(tx_transfer, rx_transfer).await;
+ Ok(())
+ }
+}
+
mod sealed {
use super::*;
+ pub trait Mode {}
+
pub trait Instance {
+ const TX_DREQ: u8;
+ const RX_DREQ: u8;
+
fn regs(&self) -> pac::spi::Spi;
}
}
+pub trait Mode: sealed::Mode {}
pub trait Instance: sealed::Instance {}
macro_rules! impl_instance {
- ($type:ident, $irq:ident) => {
+ ($type:ident, $irq:ident, $tx_dreq:expr, $rx_dreq:expr) => {
impl sealed::Instance for peripherals::$type {
+ const TX_DREQ: u8 = $tx_dreq;
+ const RX_DREQ: u8 = $rx_dreq;
+
fn regs(&self) -> pac::spi::Spi {
pac::$type
}
@@ -246,8 +417,8 @@ macro_rules! impl_instance {
};
}
-impl_instance!(SPI0, Spi0);
-impl_instance!(SPI1, Spi1);
+impl_instance!(SPI0, Spi0, 16, 17);
+impl_instance!(SPI1, Spi1, 18, 19);
pub trait ClkPin