mirror of
https://github.com/esp-rs/esp-hal.git
synced 2025-10-02 06:40:47 +00:00
spi: docs improvement, re-shuffle and merge impl blocks (#3223)
* spi: docs improvement, re-shuffle and merge impl blocks * format doc examples
This commit is contained in:
parent
38eb5aefc9
commit
cdcd3bee4d
@ -644,8 +644,10 @@ impl core::fmt::Display for ConfigError {
|
|||||||
/// # use esp_hal::spi::master::{Config, Spi};
|
/// # use esp_hal::spi::master::{Config, Spi};
|
||||||
/// let mut spi = Spi::new(
|
/// let mut spi = Spi::new(
|
||||||
/// peripherals.SPI2,
|
/// peripherals.SPI2,
|
||||||
/// Config::default().with_frequency(Rate::from_khz(100)).
|
/// Config::default()
|
||||||
/// with_mode(Mode::_0) )?
|
/// .with_frequency(Rate::from_khz(100))
|
||||||
|
/// .with_mode(Mode::_0),
|
||||||
|
/// )?
|
||||||
/// .with_sck(peripherals.GPIO0)
|
/// .with_sck(peripherals.GPIO0)
|
||||||
/// .with_mosi(peripherals.GPIO1)
|
/// .with_mosi(peripherals.GPIO1)
|
||||||
/// .with_miso(peripherals.GPIO2);
|
/// .with_miso(peripherals.GPIO2);
|
||||||
@ -663,41 +665,6 @@ pub struct Spi<'d, Dm: DriverMode> {
|
|||||||
|
|
||||||
impl<Dm: DriverMode> Sealed for Spi<'_, Dm> {}
|
impl<Dm: DriverMode> Sealed for Spi<'_, Dm> {}
|
||||||
|
|
||||||
impl<Dm> Spi<'_, Dm>
|
|
||||||
where
|
|
||||||
Dm: DriverMode,
|
|
||||||
{
|
|
||||||
fn driver(&self) -> Driver {
|
|
||||||
Driver {
|
|
||||||
info: self.spi.info(),
|
|
||||||
state: self.spi.state(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Write bytes to SPI. After writing, flush is called to ensure all data
|
|
||||||
/// has been transmitted.
|
|
||||||
pub fn write(&mut self, words: &[u8]) -> Result<(), Error> {
|
|
||||||
self.driver().setup_full_duplex()?;
|
|
||||||
self.driver().write(words)?;
|
|
||||||
self.driver().flush()?;
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Read bytes from SPI. The provided slice is filled with data received
|
|
||||||
/// from the slave.
|
|
||||||
pub fn read(&mut self, words: &mut [u8]) -> Result<(), Error> {
|
|
||||||
self.driver().setup_full_duplex()?;
|
|
||||||
self.driver().read(words)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Sends `words` to the slave. Returns the `words` received from the slave.
|
|
||||||
pub fn transfer<'w>(&mut self, words: &'w mut [u8]) -> Result<&'w [u8], Error> {
|
|
||||||
self.driver().setup_full_duplex()?;
|
|
||||||
self.driver().transfer(words)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'d> Spi<'d, Blocking> {
|
impl<'d> Spi<'d, Blocking> {
|
||||||
/// Constructs an SPI instance in 8bit dataframe mode.
|
/// Constructs an SPI instance in 8bit dataframe mode.
|
||||||
///
|
///
|
||||||
@ -795,8 +762,10 @@ impl<'d> Spi<'d, Blocking> {
|
|||||||
///
|
///
|
||||||
/// let mut spi = Spi::new(
|
/// let mut spi = Spi::new(
|
||||||
/// peripherals.SPI2,
|
/// peripherals.SPI2,
|
||||||
/// Config::default().with_frequency(Rate::from_khz(100)).
|
/// Config::default()
|
||||||
/// with_mode(Mode::_0) )?
|
/// .with_frequency(Rate::from_khz(100))
|
||||||
|
/// .with_mode(Mode::_0),
|
||||||
|
/// )?
|
||||||
/// .with_dma(dma_channel)
|
/// .with_dma(dma_channel)
|
||||||
/// .with_buffers(dma_rx_buf, dma_tx_buf);
|
/// .with_buffers(dma_rx_buf, dma_tx_buf);
|
||||||
/// # Ok(())
|
/// # Ok(())
|
||||||
@ -894,6 +863,20 @@ impl<'d, Dm> Spi<'d, Dm>
|
|||||||
where
|
where
|
||||||
Dm: DriverMode,
|
Dm: DriverMode,
|
||||||
{
|
{
|
||||||
|
/// Assign the SCK (Serial Clock) pin for the SPI instance.
|
||||||
|
///
|
||||||
|
/// Configures the specified pin to push-pull output and connects it to the
|
||||||
|
/// SPI clock signal.
|
||||||
|
///
|
||||||
|
/// Disconnects the previous pin that was assigned with `with_sck`.
|
||||||
|
pub fn with_sck<SCK: PeripheralOutput>(mut self, sclk: impl Peripheral<P = SCK> + 'd) -> Self {
|
||||||
|
crate::into_mapped_ref!(sclk);
|
||||||
|
sclk.set_to_push_pull_output();
|
||||||
|
self.pins.sclk_pin = OutputConnection::connect_with_guard(sclk, self.driver().info.sclk);
|
||||||
|
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
/// Assign the MOSI (Master Out Slave In) pin for the SPI instance.
|
/// Assign the MOSI (Master Out Slave In) pin for the SPI instance.
|
||||||
///
|
///
|
||||||
/// Enables output functionality for the pin, and connects it as the MOSI
|
/// Enables output functionality for the pin, and connects it as the MOSI
|
||||||
@ -914,6 +897,22 @@ where
|
|||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Assign the MISO (Master In Slave Out) pin for the SPI instance.
|
||||||
|
///
|
||||||
|
/// Enables input functionality for the pin, and connects it to the MISO
|
||||||
|
/// signal.
|
||||||
|
///
|
||||||
|
/// You want to use this for full-duplex SPI or
|
||||||
|
/// [DataMode::SingleTwoDataLines]
|
||||||
|
pub fn with_miso<MISO: PeripheralInput>(self, miso: impl Peripheral<P = MISO> + 'd) -> Self {
|
||||||
|
crate::into_mapped_ref!(miso);
|
||||||
|
miso.enable_input(true);
|
||||||
|
|
||||||
|
self.driver().info.miso.connect_to(miso);
|
||||||
|
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
/// Assign the SIO0 pin for the SPI instance.
|
/// Assign the SIO0 pin for the SPI instance.
|
||||||
///
|
///
|
||||||
/// Enables both input and output functionality for the pin, and connects it
|
/// Enables both input and output functionality for the pin, and connects it
|
||||||
@ -942,22 +941,6 @@ where
|
|||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Assign the MISO (Master In Slave Out) pin for the SPI instance.
|
|
||||||
///
|
|
||||||
/// Enables input functionality for the pin, and connects it to the MISO
|
|
||||||
/// signal.
|
|
||||||
///
|
|
||||||
/// You want to use this for full-duplex SPI or
|
|
||||||
/// [DataMode::SingleTwoDataLines]
|
|
||||||
pub fn with_miso<MISO: PeripheralInput>(self, miso: impl Peripheral<P = MISO> + 'd) -> Self {
|
|
||||||
crate::into_mapped_ref!(miso);
|
|
||||||
miso.enable_input(true);
|
|
||||||
|
|
||||||
self.driver().info.miso.connect_to(miso);
|
|
||||||
|
|
||||||
self
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Assign the SIO1/MISO pin for the SPI instance.
|
/// Assign the SIO1/MISO pin for the SPI instance.
|
||||||
///
|
///
|
||||||
/// Enables both input and output functionality for the pin, and connects it
|
/// Enables both input and output functionality for the pin, and connects it
|
||||||
@ -986,71 +969,6 @@ where
|
|||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Assign the SCK (Serial Clock) pin for the SPI instance.
|
|
||||||
///
|
|
||||||
/// Configures the specified pin to push-pull output and connects it to the
|
|
||||||
/// SPI clock signal.
|
|
||||||
///
|
|
||||||
/// Disconnects the previous pin that was assigned with `with_sck`.
|
|
||||||
pub fn with_sck<SCK: PeripheralOutput>(mut self, sclk: impl Peripheral<P = SCK> + 'd) -> Self {
|
|
||||||
crate::into_mapped_ref!(sclk);
|
|
||||||
sclk.set_to_push_pull_output();
|
|
||||||
self.pins.sclk_pin = OutputConnection::connect_with_guard(sclk, self.driver().info.sclk);
|
|
||||||
|
|
||||||
self
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Assign the CS (Chip Select) pin for the SPI instance.
|
|
||||||
///
|
|
||||||
/// Configures the specified pin to push-pull output and connects it to the
|
|
||||||
/// SPI CS signal.
|
|
||||||
///
|
|
||||||
/// Disconnects the previous pin that was assigned with `with_cs`.
|
|
||||||
///
|
|
||||||
/// # Current Stability Limitations
|
|
||||||
/// The hardware chip select functionality is limited; only one CS line can
|
|
||||||
/// be set, regardless of the total number available. There is no
|
|
||||||
/// mechanism to select which CS line to use.
|
|
||||||
#[instability::unstable]
|
|
||||||
pub fn with_cs<CS: PeripheralOutput>(mut self, cs: impl Peripheral<P = CS> + 'd) -> Self {
|
|
||||||
crate::into_mapped_ref!(cs);
|
|
||||||
cs.set_to_push_pull_output();
|
|
||||||
self.pins.cs_pin = OutputConnection::connect_with_guard(cs, self.driver().info.cs);
|
|
||||||
|
|
||||||
self
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Change the bus configuration.
|
|
||||||
///
|
|
||||||
/// # Errors
|
|
||||||
///
|
|
||||||
/// If frequency passed in config exceeds
|
|
||||||
#[cfg_attr(not(esp32h2), doc = " 80MHz")]
|
|
||||||
#[cfg_attr(esp32h2, doc = " 48MHz")]
|
|
||||||
/// or is below 70kHz,
|
|
||||||
/// [`ConfigError::UnsupportedFrequency`] error will be returned.
|
|
||||||
pub fn apply_config(&mut self, config: &Config) -> Result<(), ConfigError> {
|
|
||||||
self.driver().apply_config(config)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[instability::unstable]
|
|
||||||
impl<Dm> embassy_embedded_hal::SetConfig for Spi<'_, Dm>
|
|
||||||
where
|
|
||||||
Dm: DriverMode,
|
|
||||||
{
|
|
||||||
type Config = Config;
|
|
||||||
type ConfigError = ConfigError;
|
|
||||||
|
|
||||||
fn set_config(&mut self, config: &Self::Config) -> Result<(), Self::ConfigError> {
|
|
||||||
self.apply_config(config)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'d, Dm> Spi<'d, Dm>
|
|
||||||
where
|
|
||||||
Dm: DriverMode,
|
|
||||||
{
|
|
||||||
/// Assign the SIO2 pin for the SPI instance.
|
/// Assign the SIO2 pin for the SPI instance.
|
||||||
///
|
///
|
||||||
/// Enables both input and output functionality for the pin, and connects it
|
/// Enables both input and output functionality for the pin, and connects it
|
||||||
@ -1106,12 +1024,63 @@ where
|
|||||||
|
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
impl<Dm> Spi<'_, Dm>
|
/// Assign the CS (Chip Select) pin for the SPI instance.
|
||||||
where
|
///
|
||||||
Dm: DriverMode,
|
/// Configures the specified pin to push-pull output and connects it to the
|
||||||
{
|
/// SPI CS signal.
|
||||||
|
///
|
||||||
|
/// Disconnects the previous pin that was assigned with `with_cs`.
|
||||||
|
///
|
||||||
|
/// # Current Stability Limitations
|
||||||
|
/// The hardware chip select functionality is limited; only one CS line can
|
||||||
|
/// be set, regardless of the total number available. There is no
|
||||||
|
/// mechanism to select which CS line to use.
|
||||||
|
#[instability::unstable]
|
||||||
|
pub fn with_cs<CS: PeripheralOutput>(mut self, cs: impl Peripheral<P = CS> + 'd) -> Self {
|
||||||
|
crate::into_mapped_ref!(cs);
|
||||||
|
cs.set_to_push_pull_output();
|
||||||
|
self.pins.cs_pin = OutputConnection::connect_with_guard(cs, self.driver().info.cs);
|
||||||
|
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Change the bus configuration.
|
||||||
|
///
|
||||||
|
/// # Errors
|
||||||
|
///
|
||||||
|
/// If frequency passed in config exceeds
|
||||||
|
#[cfg_attr(not(esp32h2), doc = " 80MHz")]
|
||||||
|
#[cfg_attr(esp32h2, doc = " 48MHz")]
|
||||||
|
/// or is below 70kHz,
|
||||||
|
/// [`ConfigError::UnsupportedFrequency`] error will be returned.
|
||||||
|
pub fn apply_config(&mut self, config: &Config) -> Result<(), ConfigError> {
|
||||||
|
self.driver().apply_config(config)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Write bytes to SPI. After writing, flush is called to ensure all data
|
||||||
|
/// has been transmitted.
|
||||||
|
pub fn write(&mut self, words: &[u8]) -> Result<(), Error> {
|
||||||
|
self.driver().setup_full_duplex()?;
|
||||||
|
self.driver().write(words)?;
|
||||||
|
self.driver().flush()?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Read bytes from SPI. The provided slice is filled with data received
|
||||||
|
/// from the slave.
|
||||||
|
pub fn read(&mut self, words: &mut [u8]) -> Result<(), Error> {
|
||||||
|
self.driver().setup_full_duplex()?;
|
||||||
|
self.driver().read(words)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Sends `words` to the slave. Returns the `words` received from the slave.
|
||||||
|
pub fn transfer<'w>(&mut self, words: &'w mut [u8]) -> Result<&'w [u8], Error> {
|
||||||
|
self.driver().setup_full_duplex()?;
|
||||||
|
self.driver().transfer(words)
|
||||||
|
}
|
||||||
|
|
||||||
/// Half-duplex read.
|
/// Half-duplex read.
|
||||||
///
|
///
|
||||||
/// # Errors
|
/// # Errors
|
||||||
@ -1222,6 +1191,26 @@ where
|
|||||||
|
|
||||||
self.driver().flush()
|
self.driver().flush()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn driver(&self) -> Driver {
|
||||||
|
Driver {
|
||||||
|
info: self.spi.info(),
|
||||||
|
state: self.spi.state(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[instability::unstable]
|
||||||
|
impl<Dm> embassy_embedded_hal::SetConfig for Spi<'_, Dm>
|
||||||
|
where
|
||||||
|
Dm: DriverMode,
|
||||||
|
{
|
||||||
|
type Config = Config;
|
||||||
|
type ConfigError = ConfigError;
|
||||||
|
|
||||||
|
fn set_config(&mut self, config: &Self::Config) -> Result<(), Self::ConfigError> {
|
||||||
|
self.apply_config(config)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
mod dma {
|
mod dma {
|
||||||
@ -1232,13 +1221,16 @@ mod dma {
|
|||||||
};
|
};
|
||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
use crate::dma::{
|
use crate::{
|
||||||
asynch::{DmaRxFuture, DmaTxFuture},
|
dma::{
|
||||||
Channel,
|
asynch::{DmaRxFuture, DmaTxFuture},
|
||||||
DmaRxBuf,
|
Channel,
|
||||||
DmaTxBuf,
|
DmaRxBuf,
|
||||||
EmptyBuf,
|
DmaTxBuf,
|
||||||
PeripheralDmaChannel,
|
EmptyBuf,
|
||||||
|
PeripheralDmaChannel,
|
||||||
|
},
|
||||||
|
spi::master::dma::asynch::DropGuard,
|
||||||
};
|
};
|
||||||
|
|
||||||
/// A DMA capable SPI instance.
|
/// A DMA capable SPI instance.
|
||||||
@ -1274,8 +1266,10 @@ mod dma {
|
|||||||
///
|
///
|
||||||
/// let mut spi = Spi::new(
|
/// let mut spi = Spi::new(
|
||||||
/// peripherals.SPI2,
|
/// peripherals.SPI2,
|
||||||
/// Config::default().with_frequency(Rate::from_khz(100)).
|
/// Config::default()
|
||||||
/// with_mode(Mode::_0) )?
|
/// .with_frequency(Rate::from_khz(100))
|
||||||
|
/// .with_mode(Mode::_0),
|
||||||
|
/// )?
|
||||||
/// .with_dma(dma_channel)
|
/// .with_dma(dma_channel)
|
||||||
/// .with_buffers(dma_rx_buf, dma_tx_buf);
|
/// .with_buffers(dma_rx_buf, dma_tx_buf);
|
||||||
/// # Ok(())
|
/// # Ok(())
|
||||||
@ -1313,6 +1307,71 @@ mod dma {
|
|||||||
pins: self.pins,
|
pins: self.pins,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub(super) fn new(
|
||||||
|
spi: PeripheralRef<'d, AnySpi>,
|
||||||
|
pins: SpiPinGuard,
|
||||||
|
channel: PeripheralRef<'d, PeripheralDmaChannel<AnySpi>>,
|
||||||
|
) -> Self {
|
||||||
|
let channel = Channel::new(channel);
|
||||||
|
channel.runtime_ensure_compatible(&spi);
|
||||||
|
#[cfg(all(esp32, spi_address_workaround))]
|
||||||
|
let address_buffer = {
|
||||||
|
use crate::dma::DmaDescriptor;
|
||||||
|
const SPI_NUM: usize = 2;
|
||||||
|
static mut DESCRIPTORS: [[DmaDescriptor; 1]; SPI_NUM] =
|
||||||
|
[[DmaDescriptor::EMPTY]; SPI_NUM];
|
||||||
|
static mut BUFFERS: [[u32; 1]; SPI_NUM] = [[0; 1]; SPI_NUM];
|
||||||
|
|
||||||
|
let id = if spi.info() == unsafe { crate::peripherals::SPI2::steal().info() } {
|
||||||
|
0
|
||||||
|
} else {
|
||||||
|
1
|
||||||
|
};
|
||||||
|
|
||||||
|
unwrap!(DmaTxBuf::new(
|
||||||
|
unsafe { &mut DESCRIPTORS[id] },
|
||||||
|
crate::dma::as_mut_byte_array!(BUFFERS[id], 4)
|
||||||
|
))
|
||||||
|
};
|
||||||
|
|
||||||
|
let guard = PeripheralGuard::new(spi.info().peripheral);
|
||||||
|
|
||||||
|
Self {
|
||||||
|
spi,
|
||||||
|
channel,
|
||||||
|
#[cfg(all(esp32, spi_address_workaround))]
|
||||||
|
address_buffer,
|
||||||
|
tx_transfer_in_progress: false,
|
||||||
|
rx_transfer_in_progress: false,
|
||||||
|
guard,
|
||||||
|
pins,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Listen for the given interrupts
|
||||||
|
#[instability::unstable]
|
||||||
|
pub fn listen(&mut self, interrupts: impl Into<EnumSet<SpiInterrupt>>) {
|
||||||
|
self.driver().enable_listen(interrupts.into(), true);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Unlisten the given interrupts
|
||||||
|
#[instability::unstable]
|
||||||
|
pub fn unlisten(&mut self, interrupts: impl Into<EnumSet<SpiInterrupt>>) {
|
||||||
|
self.driver().enable_listen(interrupts.into(), false);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Gets asserted interrupts
|
||||||
|
#[instability::unstable]
|
||||||
|
pub fn interrupts(&mut self) -> EnumSet<SpiInterrupt> {
|
||||||
|
self.driver().interrupts()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Resets asserted interrupts
|
||||||
|
#[instability::unstable]
|
||||||
|
pub fn clear_interrupts(&mut self, interrupts: impl Into<EnumSet<SpiInterrupt>>) {
|
||||||
|
self.driver().clear_interrupts(interrupts.into());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'d> SpiDma<'d, Async> {
|
impl<'d> SpiDma<'d, Async> {
|
||||||
@ -1360,76 +1419,7 @@ mod dma {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl SpiDma<'_, Blocking> {
|
impl<Dm> SpiDma<'_, Dm>
|
||||||
/// Listen for the given interrupts
|
|
||||||
#[instability::unstable]
|
|
||||||
pub fn listen(&mut self, interrupts: impl Into<EnumSet<SpiInterrupt>>) {
|
|
||||||
self.driver().enable_listen(interrupts.into(), true);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Unlisten the given interrupts
|
|
||||||
#[instability::unstable]
|
|
||||||
pub fn unlisten(&mut self, interrupts: impl Into<EnumSet<SpiInterrupt>>) {
|
|
||||||
self.driver().enable_listen(interrupts.into(), false);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Gets asserted interrupts
|
|
||||||
#[instability::unstable]
|
|
||||||
pub fn interrupts(&mut self) -> EnumSet<SpiInterrupt> {
|
|
||||||
self.driver().interrupts()
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Resets asserted interrupts
|
|
||||||
#[instability::unstable]
|
|
||||||
pub fn clear_interrupts(&mut self, interrupts: impl Into<EnumSet<SpiInterrupt>>) {
|
|
||||||
self.driver().clear_interrupts(interrupts.into());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'d> SpiDma<'d, Blocking> {
|
|
||||||
pub(super) fn new(
|
|
||||||
spi: PeripheralRef<'d, AnySpi>,
|
|
||||||
pins: SpiPinGuard,
|
|
||||||
channel: PeripheralRef<'d, PeripheralDmaChannel<AnySpi>>,
|
|
||||||
) -> Self {
|
|
||||||
let channel = Channel::new(channel);
|
|
||||||
channel.runtime_ensure_compatible(&spi);
|
|
||||||
#[cfg(all(esp32, spi_address_workaround))]
|
|
||||||
let address_buffer = {
|
|
||||||
use crate::dma::DmaDescriptor;
|
|
||||||
const SPI_NUM: usize = 2;
|
|
||||||
static mut DESCRIPTORS: [[DmaDescriptor; 1]; SPI_NUM] =
|
|
||||||
[[DmaDescriptor::EMPTY]; SPI_NUM];
|
|
||||||
static mut BUFFERS: [[u32; 1]; SPI_NUM] = [[0; 1]; SPI_NUM];
|
|
||||||
|
|
||||||
let id = if spi.info() == unsafe { crate::peripherals::SPI2::steal().info() } {
|
|
||||||
0
|
|
||||||
} else {
|
|
||||||
1
|
|
||||||
};
|
|
||||||
|
|
||||||
unwrap!(DmaTxBuf::new(
|
|
||||||
unsafe { &mut DESCRIPTORS[id] },
|
|
||||||
crate::dma::as_mut_byte_array!(BUFFERS[id], 4)
|
|
||||||
))
|
|
||||||
};
|
|
||||||
|
|
||||||
let guard = PeripheralGuard::new(spi.info().peripheral);
|
|
||||||
|
|
||||||
Self {
|
|
||||||
spi,
|
|
||||||
channel,
|
|
||||||
#[cfg(all(esp32, spi_address_workaround))]
|
|
||||||
address_buffer,
|
|
||||||
tx_transfer_in_progress: false,
|
|
||||||
rx_transfer_in_progress: false,
|
|
||||||
guard,
|
|
||||||
pins,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'d, Dm> SpiDma<'d, Dm>
|
|
||||||
where
|
where
|
||||||
Dm: DriverMode,
|
Dm: DriverMode,
|
||||||
{
|
{
|
||||||
@ -1601,30 +1591,6 @@ mod dma {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Change the bus configuration.
|
|
||||||
///
|
|
||||||
/// # Errors
|
|
||||||
///
|
|
||||||
/// If frequency passed in config exceeds
|
|
||||||
#[cfg_attr(not(esp32h2), doc = " 80MHz")]
|
|
||||||
#[cfg_attr(esp32h2, doc = " 48MHz")]
|
|
||||||
/// or is below 70kHz,
|
|
||||||
/// [`ConfigError::UnsupportedFrequency`] error will be returned.
|
|
||||||
#[instability::unstable]
|
|
||||||
pub fn apply_config(&mut self, config: &Config) -> Result<(), ConfigError> {
|
|
||||||
self.driver().apply_config(config)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Configures the DMA buffers for the SPI instance.
|
|
||||||
///
|
|
||||||
/// This method sets up both RX and TX buffers for DMA transfers.
|
|
||||||
/// It returns an instance of `SpiDmaBus` that can be used for SPI
|
|
||||||
/// communication.
|
|
||||||
#[instability::unstable]
|
|
||||||
pub fn with_buffers(self, dma_rx_buf: DmaRxBuf, dma_tx_buf: DmaTxBuf) -> SpiDmaBus<'d, Dm> {
|
|
||||||
SpiDmaBus::new(self, dma_rx_buf, dma_tx_buf)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[instability::unstable]
|
#[instability::unstable]
|
||||||
@ -1653,6 +1619,16 @@ mod dma {
|
|||||||
dma_buf: ManuallyDrop<Buf>,
|
dma_buf: ManuallyDrop<Buf>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl<Buf> SpiDmaTransfer<'_, Async, Buf> {
|
||||||
|
/// Waits for the DMA transfer to complete asynchronously.
|
||||||
|
///
|
||||||
|
/// This method awaits the completion of both RX and TX operations.
|
||||||
|
#[instability::unstable]
|
||||||
|
pub async fn wait_for_done(&mut self) {
|
||||||
|
self.spi_dma.wait_for_idle_async().await;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl<'d, Dm, Buf> SpiDmaTransfer<'d, Dm, Buf>
|
impl<'d, Dm, Buf> SpiDmaTransfer<'d, Dm, Buf>
|
||||||
where
|
where
|
||||||
Dm: DriverMode,
|
Dm: DriverMode,
|
||||||
@ -1715,16 +1691,6 @@ mod dma {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<Buf> SpiDmaTransfer<'_, Async, Buf> {
|
|
||||||
/// Waits for the DMA transfer to complete asynchronously.
|
|
||||||
///
|
|
||||||
/// This method awaits the completion of both RX and TX operations.
|
|
||||||
#[instability::unstable]
|
|
||||||
pub async fn wait_for_done(&mut self) {
|
|
||||||
self.spi_dma.wait_for_idle_async().await;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'d, Dm> SpiDma<'d, Dm>
|
impl<'d, Dm> SpiDma<'d, Dm>
|
||||||
where
|
where
|
||||||
Dm: DriverMode,
|
Dm: DriverMode,
|
||||||
@ -1742,6 +1708,16 @@ mod dma {
|
|||||||
self.start_dma_transfer(0, bytes_to_write, &mut EmptyBuf, buffer)
|
self.start_dma_transfer(0, bytes_to_write, &mut EmptyBuf, buffer)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Configures the DMA buffers for the SPI instance.
|
||||||
|
///
|
||||||
|
/// This method sets up both RX and TX buffers for DMA transfers.
|
||||||
|
/// It returns an instance of `SpiDmaBus` that can be used for SPI
|
||||||
|
/// communication.
|
||||||
|
#[instability::unstable]
|
||||||
|
pub fn with_buffers(self, dma_rx_buf: DmaRxBuf, dma_tx_buf: DmaTxBuf) -> SpiDmaBus<'d, Dm> {
|
||||||
|
SpiDmaBus::new(self, dma_rx_buf, dma_tx_buf)
|
||||||
|
}
|
||||||
|
|
||||||
/// Perform a DMA write.
|
/// Perform a DMA write.
|
||||||
///
|
///
|
||||||
/// This will return a [SpiDmaTransfer] owning the buffer and the
|
/// This will return a [SpiDmaTransfer] owning the buffer and the
|
||||||
@ -1970,6 +1946,20 @@ mod dma {
|
|||||||
Err(e) => Err((e, self, buffer)),
|
Err(e) => Err((e, self, buffer)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Change the bus configuration.
|
||||||
|
///
|
||||||
|
/// # Errors
|
||||||
|
///
|
||||||
|
/// If frequency passed in config exceeds
|
||||||
|
#[cfg_attr(not(esp32h2), doc = " 80MHz")]
|
||||||
|
#[cfg_attr(esp32h2, doc = " 48MHz")]
|
||||||
|
/// or is below 70kHz,
|
||||||
|
/// [`ConfigError::UnsupportedFrequency`] error will be returned.
|
||||||
|
#[instability::unstable]
|
||||||
|
pub fn apply_config(&mut self, config: &Config) -> Result<(), ConfigError> {
|
||||||
|
self.driver().apply_config(config)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A DMA-capable SPI bus.
|
/// A DMA-capable SPI bus.
|
||||||
@ -2000,53 +1990,7 @@ mod dma {
|
|||||||
tx_buf: self.tx_buf,
|
tx_buf: self.tx_buf,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
impl<'d> SpiDmaBus<'d, Async> {
|
|
||||||
/// Converts the SPI instance into async mode.
|
|
||||||
#[instability::unstable]
|
|
||||||
pub fn into_blocking(self) -> SpiDmaBus<'d, Blocking> {
|
|
||||||
SpiDmaBus {
|
|
||||||
spi_dma: self.spi_dma.into_blocking(),
|
|
||||||
rx_buf: self.rx_buf,
|
|
||||||
tx_buf: self.tx_buf,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'d, Dm> SpiDmaBus<'d, Dm>
|
|
||||||
where
|
|
||||||
Dm: DriverMode,
|
|
||||||
{
|
|
||||||
/// Creates a new `SpiDmaBus` with the specified SPI instance and DMA
|
|
||||||
/// buffers.
|
|
||||||
pub fn new(spi_dma: SpiDma<'d, Dm>, rx_buf: DmaRxBuf, tx_buf: DmaTxBuf) -> Self {
|
|
||||||
Self {
|
|
||||||
spi_dma,
|
|
||||||
rx_buf,
|
|
||||||
tx_buf,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Splits [SpiDmaBus] back into [SpiDma], [DmaRxBuf] and [DmaTxBuf].
|
|
||||||
#[instability::unstable]
|
|
||||||
pub fn split(mut self) -> (SpiDma<'d, Dm>, DmaRxBuf, DmaTxBuf) {
|
|
||||||
self.wait_for_idle();
|
|
||||||
(self.spi_dma, self.rx_buf, self.tx_buf)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[instability::unstable]
|
|
||||||
impl crate::interrupt::InterruptConfigurable for SpiDmaBus<'_, Blocking> {
|
|
||||||
/// Sets the interrupt handler
|
|
||||||
///
|
|
||||||
/// Interrupts are not enabled at the peripheral level here.
|
|
||||||
fn set_interrupt_handler(&mut self, handler: InterruptHandler) {
|
|
||||||
self.spi_dma.set_interrupt_handler(handler);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl SpiDmaBus<'_, Blocking> {
|
|
||||||
/// Listen for the given interrupts
|
/// Listen for the given interrupts
|
||||||
#[instability::unstable]
|
#[instability::unstable]
|
||||||
pub fn listen(&mut self, interrupts: impl Into<EnumSet<SpiInterrupt>>) {
|
pub fn listen(&mut self, interrupts: impl Into<EnumSet<SpiInterrupt>>) {
|
||||||
@ -2072,10 +2016,162 @@ mod dma {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<Dm> SpiDmaBus<'_, Dm>
|
impl<'d> SpiDmaBus<'d, Async> {
|
||||||
|
/// Converts the SPI instance into async mode.
|
||||||
|
#[instability::unstable]
|
||||||
|
pub fn into_blocking(self) -> SpiDmaBus<'d, Blocking> {
|
||||||
|
SpiDmaBus {
|
||||||
|
spi_dma: self.spi_dma.into_blocking(),
|
||||||
|
rx_buf: self.rx_buf,
|
||||||
|
tx_buf: self.tx_buf,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Fill the given buffer with data from the bus.
|
||||||
|
#[instability::unstable]
|
||||||
|
pub async fn read_async(&mut self, words: &mut [u8]) -> Result<(), Error> {
|
||||||
|
self.spi_dma.wait_for_idle_async().await;
|
||||||
|
self.spi_dma.driver().setup_full_duplex()?;
|
||||||
|
let chunk_size = self.rx_buf.capacity();
|
||||||
|
|
||||||
|
for chunk in words.chunks_mut(chunk_size) {
|
||||||
|
self.rx_buf.set_length(chunk.len());
|
||||||
|
|
||||||
|
let mut spi = DropGuard::new(&mut self.spi_dma, |spi| spi.cancel_transfer());
|
||||||
|
|
||||||
|
unsafe { spi.start_dma_transfer(chunk.len(), 0, &mut self.rx_buf, &mut EmptyBuf)? };
|
||||||
|
|
||||||
|
spi.wait_for_idle_async().await;
|
||||||
|
|
||||||
|
let bytes_read = self.rx_buf.read_received_data(chunk);
|
||||||
|
debug_assert_eq!(bytes_read, chunk.len());
|
||||||
|
|
||||||
|
spi.defuse();
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Transmit the given buffer to the bus.
|
||||||
|
#[instability::unstable]
|
||||||
|
pub async fn write_async(&mut self, words: &[u8]) -> Result<(), Error> {
|
||||||
|
self.spi_dma.wait_for_idle_async().await;
|
||||||
|
self.spi_dma.driver().setup_full_duplex()?;
|
||||||
|
|
||||||
|
let mut spi = DropGuard::new(&mut self.spi_dma, |spi| spi.cancel_transfer());
|
||||||
|
let chunk_size = self.tx_buf.capacity();
|
||||||
|
|
||||||
|
for chunk in words.chunks(chunk_size) {
|
||||||
|
self.tx_buf.fill(chunk);
|
||||||
|
|
||||||
|
unsafe { spi.start_dma_transfer(0, chunk.len(), &mut EmptyBuf, &mut self.tx_buf)? };
|
||||||
|
|
||||||
|
spi.wait_for_idle_async().await;
|
||||||
|
}
|
||||||
|
spi.defuse();
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Transfer by writing out a buffer and reading the response from
|
||||||
|
/// the bus into another buffer.
|
||||||
|
#[instability::unstable]
|
||||||
|
pub async fn transfer_async(&mut self, read: &mut [u8], write: &[u8]) -> Result<(), Error> {
|
||||||
|
self.spi_dma.wait_for_idle_async().await;
|
||||||
|
self.spi_dma.driver().setup_full_duplex()?;
|
||||||
|
|
||||||
|
let mut spi = DropGuard::new(&mut self.spi_dma, |spi| spi.cancel_transfer());
|
||||||
|
let chunk_size = min(self.tx_buf.capacity(), self.rx_buf.capacity());
|
||||||
|
|
||||||
|
let common_length = min(read.len(), write.len());
|
||||||
|
let (read_common, read_remainder) = read.split_at_mut(common_length);
|
||||||
|
let (write_common, write_remainder) = write.split_at(common_length);
|
||||||
|
|
||||||
|
for (read_chunk, write_chunk) in read_common
|
||||||
|
.chunks_mut(chunk_size)
|
||||||
|
.zip(write_common.chunks(chunk_size))
|
||||||
|
{
|
||||||
|
self.tx_buf.fill(write_chunk);
|
||||||
|
self.rx_buf.set_length(read_chunk.len());
|
||||||
|
|
||||||
|
unsafe {
|
||||||
|
spi.start_dma_transfer(
|
||||||
|
read_chunk.len(),
|
||||||
|
write_chunk.len(),
|
||||||
|
&mut self.rx_buf,
|
||||||
|
&mut self.tx_buf,
|
||||||
|
)?;
|
||||||
|
}
|
||||||
|
spi.wait_for_idle_async().await;
|
||||||
|
|
||||||
|
let bytes_read = self.rx_buf.read_received_data(read_chunk);
|
||||||
|
debug_assert_eq!(bytes_read, read_chunk.len());
|
||||||
|
}
|
||||||
|
|
||||||
|
spi.defuse();
|
||||||
|
|
||||||
|
if !read_remainder.is_empty() {
|
||||||
|
self.read_async(read_remainder).await
|
||||||
|
} else if !write_remainder.is_empty() {
|
||||||
|
self.write_async(write_remainder).await
|
||||||
|
} else {
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Transfer by writing out a buffer and reading the response from
|
||||||
|
/// the bus into the same buffer.
|
||||||
|
#[instability::unstable]
|
||||||
|
pub async fn transfer_in_place_async(&mut self, words: &mut [u8]) -> Result<(), Error> {
|
||||||
|
self.spi_dma.wait_for_idle_async().await;
|
||||||
|
self.spi_dma.driver().setup_full_duplex()?;
|
||||||
|
|
||||||
|
let mut spi = DropGuard::new(&mut self.spi_dma, |spi| spi.cancel_transfer());
|
||||||
|
for chunk in words.chunks_mut(self.tx_buf.capacity()) {
|
||||||
|
self.tx_buf.fill(chunk);
|
||||||
|
self.rx_buf.set_length(chunk.len());
|
||||||
|
|
||||||
|
unsafe {
|
||||||
|
spi.start_dma_transfer(
|
||||||
|
chunk.len(),
|
||||||
|
chunk.len(),
|
||||||
|
&mut self.rx_buf,
|
||||||
|
&mut self.tx_buf,
|
||||||
|
)?;
|
||||||
|
}
|
||||||
|
spi.wait_for_idle_async().await;
|
||||||
|
|
||||||
|
let bytes_read = self.rx_buf.read_received_data(chunk);
|
||||||
|
debug_assert_eq!(bytes_read, chunk.len());
|
||||||
|
}
|
||||||
|
|
||||||
|
spi.defuse();
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'d, Dm> SpiDmaBus<'d, Dm>
|
||||||
where
|
where
|
||||||
Dm: DriverMode,
|
Dm: DriverMode,
|
||||||
{
|
{
|
||||||
|
/// Creates a new `SpiDmaBus` with the specified SPI instance and DMA
|
||||||
|
/// buffers.
|
||||||
|
pub fn new(spi_dma: SpiDma<'d, Dm>, rx_buf: DmaRxBuf, tx_buf: DmaTxBuf) -> Self {
|
||||||
|
Self {
|
||||||
|
spi_dma,
|
||||||
|
rx_buf,
|
||||||
|
tx_buf,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Splits [SpiDmaBus] back into [SpiDma], [DmaRxBuf] and [DmaTxBuf].
|
||||||
|
#[instability::unstable]
|
||||||
|
pub fn split(mut self) -> (SpiDma<'d, Dm>, DmaRxBuf, DmaTxBuf) {
|
||||||
|
self.wait_for_idle();
|
||||||
|
(self.spi_dma, self.rx_buf, self.tx_buf)
|
||||||
|
}
|
||||||
|
|
||||||
fn wait_for_idle(&mut self) {
|
fn wait_for_idle(&mut self) {
|
||||||
self.spi_dma.wait_for_idle();
|
self.spi_dma.wait_for_idle();
|
||||||
}
|
}
|
||||||
@ -2280,6 +2376,16 @@ mod dma {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[instability::unstable]
|
||||||
|
impl crate::interrupt::InterruptConfigurable for SpiDmaBus<'_, Blocking> {
|
||||||
|
/// Sets the interrupt handler
|
||||||
|
///
|
||||||
|
/// Interrupts are not enabled at the peripheral level here.
|
||||||
|
fn set_interrupt_handler(&mut self, handler: InterruptHandler) {
|
||||||
|
self.spi_dma.set_interrupt_handler(handler);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[instability::unstable]
|
#[instability::unstable]
|
||||||
impl<Dm> embassy_embedded_hal::SetConfig for SpiDmaBus<'_, Dm>
|
impl<Dm> embassy_embedded_hal::SetConfig for SpiDmaBus<'_, Dm>
|
||||||
where
|
where
|
||||||
@ -2295,28 +2401,26 @@ mod dma {
|
|||||||
|
|
||||||
/// Async functionality
|
/// Async functionality
|
||||||
mod asynch {
|
mod asynch {
|
||||||
#[cfg(any(doc, feature = "unstable"))]
|
|
||||||
use core::cmp::min;
|
|
||||||
use core::ops::{Deref, DerefMut};
|
use core::ops::{Deref, DerefMut};
|
||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
#[cfg_attr(not(feature = "unstable"), allow(dead_code))]
|
#[cfg_attr(not(feature = "unstable"), allow(dead_code))]
|
||||||
struct DropGuard<I, F: FnOnce(I)> {
|
pub(crate) struct DropGuard<I, F: FnOnce(I)> {
|
||||||
inner: ManuallyDrop<I>,
|
inner: ManuallyDrop<I>,
|
||||||
on_drop: ManuallyDrop<F>,
|
on_drop: ManuallyDrop<F>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg_attr(not(feature = "unstable"), allow(dead_code))]
|
#[cfg_attr(not(feature = "unstable"), allow(dead_code))]
|
||||||
impl<I, F: FnOnce(I)> DropGuard<I, F> {
|
impl<I, F: FnOnce(I)> DropGuard<I, F> {
|
||||||
fn new(inner: I, on_drop: F) -> Self {
|
pub(crate) fn new(inner: I, on_drop: F) -> Self {
|
||||||
Self {
|
Self {
|
||||||
inner: ManuallyDrop::new(inner),
|
inner: ManuallyDrop::new(inner),
|
||||||
on_drop: ManuallyDrop::new(on_drop),
|
on_drop: ManuallyDrop::new(on_drop),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn defuse(self) {}
|
pub(crate) fn defuse(self) {}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<I, F: FnOnce(I)> Drop for DropGuard<I, F> {
|
impl<I, F: FnOnce(I)> Drop for DropGuard<I, F> {
|
||||||
@ -2341,139 +2445,6 @@ mod dma {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl SpiDmaBus<'_, Async> {
|
|
||||||
/// Fill the given buffer with data from the bus.
|
|
||||||
#[instability::unstable]
|
|
||||||
pub async fn read_async(&mut self, words: &mut [u8]) -> Result<(), Error> {
|
|
||||||
self.spi_dma.wait_for_idle_async().await;
|
|
||||||
self.spi_dma.driver().setup_full_duplex()?;
|
|
||||||
let chunk_size = self.rx_buf.capacity();
|
|
||||||
|
|
||||||
for chunk in words.chunks_mut(chunk_size) {
|
|
||||||
self.rx_buf.set_length(chunk.len());
|
|
||||||
|
|
||||||
let mut spi = DropGuard::new(&mut self.spi_dma, |spi| spi.cancel_transfer());
|
|
||||||
|
|
||||||
unsafe {
|
|
||||||
spi.start_dma_transfer(chunk.len(), 0, &mut self.rx_buf, &mut EmptyBuf)?
|
|
||||||
};
|
|
||||||
|
|
||||||
spi.wait_for_idle_async().await;
|
|
||||||
|
|
||||||
let bytes_read = self.rx_buf.read_received_data(chunk);
|
|
||||||
debug_assert_eq!(bytes_read, chunk.len());
|
|
||||||
|
|
||||||
spi.defuse();
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Transmit the given buffer to the bus.
|
|
||||||
#[instability::unstable]
|
|
||||||
pub async fn write_async(&mut self, words: &[u8]) -> Result<(), Error> {
|
|
||||||
self.spi_dma.wait_for_idle_async().await;
|
|
||||||
self.spi_dma.driver().setup_full_duplex()?;
|
|
||||||
|
|
||||||
let mut spi = DropGuard::new(&mut self.spi_dma, |spi| spi.cancel_transfer());
|
|
||||||
let chunk_size = self.tx_buf.capacity();
|
|
||||||
|
|
||||||
for chunk in words.chunks(chunk_size) {
|
|
||||||
self.tx_buf.fill(chunk);
|
|
||||||
|
|
||||||
unsafe {
|
|
||||||
spi.start_dma_transfer(0, chunk.len(), &mut EmptyBuf, &mut self.tx_buf)?
|
|
||||||
};
|
|
||||||
|
|
||||||
spi.wait_for_idle_async().await;
|
|
||||||
}
|
|
||||||
spi.defuse();
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Transfer by writing out a buffer and reading the response from
|
|
||||||
/// the bus into another buffer.
|
|
||||||
#[instability::unstable]
|
|
||||||
pub async fn transfer_async(
|
|
||||||
&mut self,
|
|
||||||
read: &mut [u8],
|
|
||||||
write: &[u8],
|
|
||||||
) -> Result<(), Error> {
|
|
||||||
self.spi_dma.wait_for_idle_async().await;
|
|
||||||
self.spi_dma.driver().setup_full_duplex()?;
|
|
||||||
|
|
||||||
let mut spi = DropGuard::new(&mut self.spi_dma, |spi| spi.cancel_transfer());
|
|
||||||
let chunk_size = min(self.tx_buf.capacity(), self.rx_buf.capacity());
|
|
||||||
|
|
||||||
let common_length = min(read.len(), write.len());
|
|
||||||
let (read_common, read_remainder) = read.split_at_mut(common_length);
|
|
||||||
let (write_common, write_remainder) = write.split_at(common_length);
|
|
||||||
|
|
||||||
for (read_chunk, write_chunk) in read_common
|
|
||||||
.chunks_mut(chunk_size)
|
|
||||||
.zip(write_common.chunks(chunk_size))
|
|
||||||
{
|
|
||||||
self.tx_buf.fill(write_chunk);
|
|
||||||
self.rx_buf.set_length(read_chunk.len());
|
|
||||||
|
|
||||||
unsafe {
|
|
||||||
spi.start_dma_transfer(
|
|
||||||
read_chunk.len(),
|
|
||||||
write_chunk.len(),
|
|
||||||
&mut self.rx_buf,
|
|
||||||
&mut self.tx_buf,
|
|
||||||
)?;
|
|
||||||
}
|
|
||||||
spi.wait_for_idle_async().await;
|
|
||||||
|
|
||||||
let bytes_read = self.rx_buf.read_received_data(read_chunk);
|
|
||||||
debug_assert_eq!(bytes_read, read_chunk.len());
|
|
||||||
}
|
|
||||||
|
|
||||||
spi.defuse();
|
|
||||||
|
|
||||||
if !read_remainder.is_empty() {
|
|
||||||
self.read_async(read_remainder).await
|
|
||||||
} else if !write_remainder.is_empty() {
|
|
||||||
self.write_async(write_remainder).await
|
|
||||||
} else {
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Transfer by writing out a buffer and reading the response from
|
|
||||||
/// the bus into the same buffer.
|
|
||||||
#[instability::unstable]
|
|
||||||
pub async fn transfer_in_place_async(&mut self, words: &mut [u8]) -> Result<(), Error> {
|
|
||||||
self.spi_dma.wait_for_idle_async().await;
|
|
||||||
self.spi_dma.driver().setup_full_duplex()?;
|
|
||||||
|
|
||||||
let mut spi = DropGuard::new(&mut self.spi_dma, |spi| spi.cancel_transfer());
|
|
||||||
for chunk in words.chunks_mut(self.tx_buf.capacity()) {
|
|
||||||
self.tx_buf.fill(chunk);
|
|
||||||
self.rx_buf.set_length(chunk.len());
|
|
||||||
|
|
||||||
unsafe {
|
|
||||||
spi.start_dma_transfer(
|
|
||||||
chunk.len(),
|
|
||||||
chunk.len(),
|
|
||||||
&mut self.rx_buf,
|
|
||||||
&mut self.tx_buf,
|
|
||||||
)?;
|
|
||||||
}
|
|
||||||
spi.wait_for_idle_async().await;
|
|
||||||
|
|
||||||
let bytes_read = self.rx_buf.read_received_data(chunk);
|
|
||||||
debug_assert_eq!(bytes_read, chunk.len());
|
|
||||||
}
|
|
||||||
|
|
||||||
spi.defuse();
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[instability::unstable]
|
#[instability::unstable]
|
||||||
impl embedded_hal_async::spi::SpiBus for SpiDmaBus<'_, Async> {
|
impl embedded_hal_async::spi::SpiBus for SpiDmaBus<'_, Async> {
|
||||||
async fn read(&mut self, words: &mut [u8]) -> Result<(), Self::Error> {
|
async fn read(&mut self, words: &mut [u8]) -> Result<(), Self::Error> {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user