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>, + rx_dma: Option>, + phantom: PhantomData<(&'d mut T, M)>, } fn div_roundup(a: u32, b: u32) -> u32 { @@ -57,51 +64,15 @@ fn calc_prescs(freq: u32) -> (u8, u8) { ((presc * 2) as u8, (postdiv - 1) as u8) } -impl<'d, T: Instance> Spi<'d, T> { - pub fn new( - inner: impl Peripheral

+ '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>, mosi: Option>, miso: Option>, cs: Option>, + tx_dma: Option>, + rx_dma: Option>, config: Config, ) -> Self { into_ref!(inner); @@ -134,7 +105,12 @@ impl<'d, T: Instance> Spi<'d, T> { pin.io().ctrl().write(|w| w.set_funcsel(1)); } } - Self { inner } + Self { + inner, + tx_dma, + rx_dma, + phantom: PhantomData, + } } pub fn blocking_write(&mut self, data: &[u8]) -> Result<(), Error> { @@ -225,19 +201,214 @@ impl<'d, T: Instance> Spi<'d, T> { } } +impl<'d, T: Instance> Spi<'d, T, Blocking> { + pub fn new_blocking( + inner: impl Peripheral

+ '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: GpioPin {} pub trait CsPin: GpioPin {} @@ -281,12 +452,25 @@ impl_pin!(PIN_17, SPI0, CsPin); impl_pin!(PIN_18, SPI0, ClkPin); impl_pin!(PIN_19, SPI0, MosiPin); +macro_rules! impl_mode { + ($name:ident) => { + impl sealed::Mode for $name {} + impl Mode for $name {} + }; +} + +pub struct Blocking; +pub struct Async; + +impl_mode!(Blocking); +impl_mode!(Async); + // ==================== mod eh02 { use super::*; - impl<'d, T: Instance> embedded_hal_02::blocking::spi::Transfer for Spi<'d, T> { + impl<'d, T: Instance, M: Mode> embedded_hal_02::blocking::spi::Transfer for Spi<'d, T, M> { type Error = Error; fn transfer<'w>(&mut self, words: &'w mut [u8]) -> Result<&'w [u8], Self::Error> { self.blocking_transfer_in_place(words)?; @@ -294,7 +478,7 @@ mod eh02 { } } - impl<'d, T: Instance> embedded_hal_02::blocking::spi::Write for Spi<'d, T> { + impl<'d, T: Instance, M: Mode> embedded_hal_02::blocking::spi::Write for Spi<'d, T, M> { type Error = Error; fn write(&mut self, words: &[u8]) -> Result<(), Self::Error> { @@ -313,29 +497,29 @@ mod eh1 { } } - impl<'d, T: Instance> embedded_hal_1::spi::ErrorType for Spi<'d, T> { + impl<'d, T: Instance, M: Mode> embedded_hal_1::spi::ErrorType for Spi<'d, T, M> { type Error = Error; } - impl<'d, T: Instance> embedded_hal_1::spi::blocking::SpiBusFlush for Spi<'d, T> { + impl<'d, T: Instance, M: Mode> embedded_hal_1::spi::blocking::SpiBusFlush for Spi<'d, T, M> { fn flush(&mut self) -> Result<(), Self::Error> { Ok(()) } } - impl<'d, T: Instance> embedded_hal_1::spi::blocking::SpiBusRead for Spi<'d, T> { + impl<'d, T: Instance, M: Mode> embedded_hal_1::spi::blocking::SpiBusRead for Spi<'d, T, M> { fn read(&mut self, words: &mut [u8]) -> Result<(), Self::Error> { self.blocking_transfer(words, &[]) } } - impl<'d, T: Instance> embedded_hal_1::spi::blocking::SpiBusWrite for Spi<'d, T> { + impl<'d, T: Instance, M: Mode> embedded_hal_1::spi::blocking::SpiBusWrite for Spi<'d, T, M> { fn write(&mut self, words: &[u8]) -> Result<(), Self::Error> { self.blocking_write(words) } } - impl<'d, T: Instance> embedded_hal_1::spi::blocking::SpiBus for Spi<'d, T> { + impl<'d, T: Instance, M: Mode> embedded_hal_1::spi::blocking::SpiBus for Spi<'d, T, M> { fn transfer(&mut self, read: &mut [u8], write: &[u8]) -> Result<(), Self::Error> { self.blocking_transfer(read, write) } @@ -346,7 +530,52 @@ mod eh1 { } } -impl<'d, T: Instance> SetConfig for Spi<'d, T> { +#[cfg(all(feature = "unstable-traits", feature = "nightly"))] +mod eha { + use core::future::Future; + + use super::*; + + impl<'d, T: Instance> embedded_hal_async::spi::SpiBusFlush for Spi<'d, T, Async> { + type FlushFuture<'a> = impl Future> + 'a where Self: 'a; + + fn flush<'a>(&'a mut self) -> Self::FlushFuture<'a> { + async { Ok(()) } + } + } + + impl<'d, T: Instance> embedded_hal_async::spi::SpiBusWrite for Spi<'d, T, Async> { + type WriteFuture<'a> = impl Future> + 'a where Self: 'a; + + fn write<'a>(&'a mut self, data: &'a [u8]) -> Self::WriteFuture<'a> { + self.write(data) + } + } + + impl<'d, T: Instance> embedded_hal_async::spi::SpiBusRead for Spi<'d, T, Async> { + type ReadFuture<'a> = impl Future> + 'a where Self: 'a; + + fn read<'a>(&'a mut self, data: &'a mut [u8]) -> Self::ReadFuture<'a> { + self.read(data) + } + } + + impl<'d, T: Instance> embedded_hal_async::spi::SpiBus for Spi<'d, T, Async> { + type TransferFuture<'a> = impl Future> + 'a where Self: 'a; + + fn transfer<'a>(&'a mut self, rx: &'a mut [u8], tx: &'a [u8]) -> Self::TransferFuture<'a> { + self.transfer(rx, tx) + } + + type TransferInPlaceFuture<'a> = impl Future> + 'a where Self: 'a; + + fn transfer_in_place<'a>(&'a mut self, words: &'a mut [u8]) -> Self::TransferInPlaceFuture<'a> { + self.transfer_in_place(words) + } + } +} + +impl<'d, T: Instance, M: Mode> SetConfig for Spi<'d, T, M> { type Config = Config; fn set_config(&mut self, config: &Self::Config) { let p = self.inner.regs(); diff --git a/examples/rp/src/bin/spi.rs b/examples/rp/src/bin/spi.rs index 88003ee17..a830a17a2 100644 --- a/examples/rp/src/bin/spi.rs +++ b/examples/rp/src/bin/spi.rs @@ -24,7 +24,7 @@ async fn main(_spawner: Spawner) { // create SPI let mut config = spi::Config::default(); config.frequency = 2_000_000; - let mut spi = Spi::new(p.SPI1, clk, mosi, miso, config); + let mut spi = Spi::new_blocking(p.SPI1, clk, mosi, miso, config); // Configure CS let mut cs = Output::new(touch_cs, Level::Low); diff --git a/examples/rp/src/bin/spi_async.rs b/examples/rp/src/bin/spi_async.rs new file mode 100644 index 000000000..671a9caaf --- /dev/null +++ b/examples/rp/src/bin/spi_async.rs @@ -0,0 +1,29 @@ +#![no_std] +#![no_main] +#![feature(type_alias_impl_trait)] + +use defmt::*; +use embassy_executor::Spawner; +use embassy_rp::spi::{Config, Spi}; +use embassy_time::{Duration, Timer}; +use {defmt_rtt as _, panic_probe as _}; + +#[embassy_executor::main] +async fn main(_spawner: Spawner) { + let p = embassy_rp::init(Default::default()); + info!("Hello World!"); + + let miso = p.PIN_12; + let mosi = p.PIN_11; + let clk = p.PIN_10; + + let mut spi = Spi::new(p.SPI1, clk, mosi, miso, p.DMA_CH0, p.DMA_CH1, Config::default()); + + loop { + let tx_buf = [1_u8, 2, 3, 4, 5, 6]; + let mut rx_buf = [0_u8; 6]; + spi.transfer(&mut rx_buf, &tx_buf).await.unwrap(); + info!("{:?}", rx_buf); + Timer::after(Duration::from_secs(1)).await; + } +} diff --git a/examples/rp/src/bin/spi_display.rs b/examples/rp/src/bin/spi_display.rs index f0e54d87f..23cd4355e 100644 --- a/examples/rp/src/bin/spi_display.rs +++ b/examples/rp/src/bin/spi_display.rs @@ -8,7 +8,7 @@ use defmt::*; use embassy_executor::Spawner; use embassy_rp::gpio::{Level, Output}; use embassy_rp::spi; -use embassy_rp::spi::Spi; +use embassy_rp::spi::{Blocking, Spi}; use embassy_time::Delay; use embedded_graphics::image::{Image, ImageRawLE}; use embedded_graphics::mono_font::ascii::FONT_10X20; @@ -48,7 +48,8 @@ async fn main(_spawner: Spawner) { config.phase = spi::Phase::CaptureOnSecondTransition; config.polarity = spi::Polarity::IdleHigh; - let spi_bus = RefCell::new(Spi::new(p.SPI1, clk, mosi, miso, config)); + let spi: Spi<'_, _, Blocking> = Spi::new_blocking(p.SPI1, clk, mosi, miso, config); + let spi_bus = RefCell::new(spi); let display_spi = SpiDeviceWithCs::new(&spi_bus, Output::new(display_cs, Level::High)); let touch_spi = SpiDeviceWithCs::new(&spi_bus, Output::new(touch_cs, Level::High)); diff --git a/tests/rp/src/bin/spi.rs b/tests/rp/src/bin/spi.rs new file mode 100644 index 000000000..478d62ee0 --- /dev/null +++ b/tests/rp/src/bin/spi.rs @@ -0,0 +1,28 @@ +#![no_std] +#![no_main] +#![feature(type_alias_impl_trait)] + +use defmt::{assert_eq, *}; +use embassy_executor::Spawner; +use embassy_rp::spi::{Config, Spi}; +use {defmt_rtt as _, panic_probe as _}; + +#[embassy_executor::main] +async fn main(_spawner: Spawner) { + let p = embassy_rp::init(Default::default()); + info!("Hello World!"); + + let clk = p.PIN_2; + let mosi = p.PIN_3; + let miso = p.PIN_4; + + let mut spi = Spi::new_blocking(p.SPI0, clk, mosi, miso, Config::default()); + + let tx_buf = [1_u8, 2, 3, 4, 5, 6]; + let mut rx_buf = [0_u8; 6]; + spi.blocking_transfer(&mut rx_buf, &tx_buf).unwrap(); + assert_eq!(rx_buf, tx_buf); + + info!("Test OK"); + cortex_m::asm::bkpt(); +} diff --git a/tests/rp/src/bin/spi_async.rs b/tests/rp/src/bin/spi_async.rs new file mode 100644 index 000000000..6c85ef60a --- /dev/null +++ b/tests/rp/src/bin/spi_async.rs @@ -0,0 +1,28 @@ +#![no_std] +#![no_main] +#![feature(type_alias_impl_trait)] + +use defmt::{assert_eq, *}; +use embassy_executor::Spawner; +use embassy_rp::spi::{Config, Spi}; +use {defmt_rtt as _, panic_probe as _}; + +#[embassy_executor::main] +async fn main(_spawner: Spawner) { + let p = embassy_rp::init(Default::default()); + info!("Hello World!"); + + let clk = p.PIN_2; + let mosi = p.PIN_3; + let miso = p.PIN_4; + + let mut spi = Spi::new(p.SPI0, clk, mosi, miso, p.DMA_CH0, p.DMA_CH1, Config::default()); + + let tx_buf = [1_u8, 2, 3, 4, 5, 6]; + let mut rx_buf = [0_u8; 6]; + spi.transfer(&mut rx_buf, &tx_buf).await.unwrap(); + assert_eq!(rx_buf, tx_buf); + + info!("Test OK"); + cortex_m::asm::bkpt(); +}