use core::{marker::PhantomData, sync::atomic::Ordering};
#[cfg(spi_master_supports_dma)]
mod dma;
mod low_level;
#[instability::unstable]
#[cfg(spi_master_supports_dma)]
pub use dma::*;
use embedded_hal::spi::SpiBus;
use embedded_hal_async::spi::SpiBus as SpiBusAsync;
use enumset::EnumSetType;
use low_level::{Driver, SpiWrapper};
pub use low_level::{Info, Instance, QspiInstance, State};
use procmacros::doc_replace;
use super::{BitOrder, Error, Mode};
use crate::{
Async,
Blocking,
DriverMode,
gpio::{
InputConfig,
NoPin,
OutputConfig,
OutputSignal,
PinGuard,
interconnect::{self, PeripheralInput, PeripheralOutput},
},
interrupt::InterruptHandler,
private::Sealed,
spi::master::low_level::SpiClockGuard,
time::Rate,
};
#[derive(Debug, Hash, EnumSetType)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
#[non_exhaustive]
#[instability::unstable]
pub enum SpiInterrupt {
TransferDone,
#[cfg(spi_master_has_dma_segmented_transfer)]
DmaSegmentedTransferDone,
#[cfg(spi_master_has_app_interrupts)]
App2,
#[cfg(spi_master_has_app_interrupts)]
App1,
}
const FIFO_SIZE: usize = property!("spi_master.fifo_size");
const EMPTY_WRITE_PAD: u8 = 0x00;
#[non_exhaustive]
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
#[instability::unstable]
pub enum Command {
None,
_1Bit(u16, DataMode),
_2Bit(u16, DataMode),
_3Bit(u16, DataMode),
_4Bit(u16, DataMode),
_5Bit(u16, DataMode),
_6Bit(u16, DataMode),
_7Bit(u16, DataMode),
_8Bit(u16, DataMode),
_9Bit(u16, DataMode),
_10Bit(u16, DataMode),
_11Bit(u16, DataMode),
_12Bit(u16, DataMode),
_13Bit(u16, DataMode),
_14Bit(u16, DataMode),
_15Bit(u16, DataMode),
_16Bit(u16, DataMode),
}
impl Command {
fn width(&self) -> usize {
match self {
Command::None => 0,
Command::_1Bit(_, _) => 1,
Command::_2Bit(_, _) => 2,
Command::_3Bit(_, _) => 3,
Command::_4Bit(_, _) => 4,
Command::_5Bit(_, _) => 5,
Command::_6Bit(_, _) => 6,
Command::_7Bit(_, _) => 7,
Command::_8Bit(_, _) => 8,
Command::_9Bit(_, _) => 9,
Command::_10Bit(_, _) => 10,
Command::_11Bit(_, _) => 11,
Command::_12Bit(_, _) => 12,
Command::_13Bit(_, _) => 13,
Command::_14Bit(_, _) => 14,
Command::_15Bit(_, _) => 15,
Command::_16Bit(_, _) => 16,
}
}
fn value(&self) -> u16 {
match self {
Command::None => 0,
Command::_1Bit(value, _)
| Command::_2Bit(value, _)
| Command::_3Bit(value, _)
| Command::_4Bit(value, _)
| Command::_5Bit(value, _)
| Command::_6Bit(value, _)
| Command::_7Bit(value, _)
| Command::_8Bit(value, _)
| Command::_9Bit(value, _)
| Command::_10Bit(value, _)
| Command::_11Bit(value, _)
| Command::_12Bit(value, _)
| Command::_13Bit(value, _)
| Command::_14Bit(value, _)
| Command::_15Bit(value, _)
| Command::_16Bit(value, _) => *value,
}
}
fn mode(&self) -> DataMode {
match self {
Command::None => DataMode::SingleTwoDataLines,
Command::_1Bit(_, mode)
| Command::_2Bit(_, mode)
| Command::_3Bit(_, mode)
| Command::_4Bit(_, mode)
| Command::_5Bit(_, mode)
| Command::_6Bit(_, mode)
| Command::_7Bit(_, mode)
| Command::_8Bit(_, mode)
| Command::_9Bit(_, mode)
| Command::_10Bit(_, mode)
| Command::_11Bit(_, mode)
| Command::_12Bit(_, mode)
| Command::_13Bit(_, mode)
| Command::_14Bit(_, mode)
| Command::_15Bit(_, mode)
| Command::_16Bit(_, mode) => *mode,
}
}
fn is_none(&self) -> bool {
matches!(self, Command::None)
}
}
#[non_exhaustive]
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
#[instability::unstable]
pub enum Address {
None,
_1Bit(u32, DataMode),
_2Bit(u32, DataMode),
_3Bit(u32, DataMode),
_4Bit(u32, DataMode),
_5Bit(u32, DataMode),
_6Bit(u32, DataMode),
_7Bit(u32, DataMode),
_8Bit(u32, DataMode),
_9Bit(u32, DataMode),
_10Bit(u32, DataMode),
_11Bit(u32, DataMode),
_12Bit(u32, DataMode),
_13Bit(u32, DataMode),
_14Bit(u32, DataMode),
_15Bit(u32, DataMode),
_16Bit(u32, DataMode),
_17Bit(u32, DataMode),
_18Bit(u32, DataMode),
_19Bit(u32, DataMode),
_20Bit(u32, DataMode),
_21Bit(u32, DataMode),
_22Bit(u32, DataMode),
_23Bit(u32, DataMode),
_24Bit(u32, DataMode),
_25Bit(u32, DataMode),
_26Bit(u32, DataMode),
_27Bit(u32, DataMode),
_28Bit(u32, DataMode),
_29Bit(u32, DataMode),
_30Bit(u32, DataMode),
_31Bit(u32, DataMode),
_32Bit(u32, DataMode),
}
impl Address {
fn width(&self) -> usize {
match self {
Address::None => 0,
Address::_1Bit(_, _) => 1,
Address::_2Bit(_, _) => 2,
Address::_3Bit(_, _) => 3,
Address::_4Bit(_, _) => 4,
Address::_5Bit(_, _) => 5,
Address::_6Bit(_, _) => 6,
Address::_7Bit(_, _) => 7,
Address::_8Bit(_, _) => 8,
Address::_9Bit(_, _) => 9,
Address::_10Bit(_, _) => 10,
Address::_11Bit(_, _) => 11,
Address::_12Bit(_, _) => 12,
Address::_13Bit(_, _) => 13,
Address::_14Bit(_, _) => 14,
Address::_15Bit(_, _) => 15,
Address::_16Bit(_, _) => 16,
Address::_17Bit(_, _) => 17,
Address::_18Bit(_, _) => 18,
Address::_19Bit(_, _) => 19,
Address::_20Bit(_, _) => 20,
Address::_21Bit(_, _) => 21,
Address::_22Bit(_, _) => 22,
Address::_23Bit(_, _) => 23,
Address::_24Bit(_, _) => 24,
Address::_25Bit(_, _) => 25,
Address::_26Bit(_, _) => 26,
Address::_27Bit(_, _) => 27,
Address::_28Bit(_, _) => 28,
Address::_29Bit(_, _) => 29,
Address::_30Bit(_, _) => 30,
Address::_31Bit(_, _) => 31,
Address::_32Bit(_, _) => 32,
}
}
fn value(&self) -> u32 {
match self {
Address::None => 0,
Address::_1Bit(value, _)
| Address::_2Bit(value, _)
| Address::_3Bit(value, _)
| Address::_4Bit(value, _)
| Address::_5Bit(value, _)
| Address::_6Bit(value, _)
| Address::_7Bit(value, _)
| Address::_8Bit(value, _)
| Address::_9Bit(value, _)
| Address::_10Bit(value, _)
| Address::_11Bit(value, _)
| Address::_12Bit(value, _)
| Address::_13Bit(value, _)
| Address::_14Bit(value, _)
| Address::_15Bit(value, _)
| Address::_16Bit(value, _)
| Address::_17Bit(value, _)
| Address::_18Bit(value, _)
| Address::_19Bit(value, _)
| Address::_20Bit(value, _)
| Address::_21Bit(value, _)
| Address::_22Bit(value, _)
| Address::_23Bit(value, _)
| Address::_24Bit(value, _)
| Address::_25Bit(value, _)
| Address::_26Bit(value, _)
| Address::_27Bit(value, _)
| Address::_28Bit(value, _)
| Address::_29Bit(value, _)
| Address::_30Bit(value, _)
| Address::_31Bit(value, _)
| Address::_32Bit(value, _) => *value,
}
}
fn is_none(&self) -> bool {
matches!(self, Address::None)
}
fn mode(&self) -> DataMode {
match self {
Address::None => DataMode::SingleTwoDataLines,
Address::_1Bit(_, mode)
| Address::_2Bit(_, mode)
| Address::_3Bit(_, mode)
| Address::_4Bit(_, mode)
| Address::_5Bit(_, mode)
| Address::_6Bit(_, mode)
| Address::_7Bit(_, mode)
| Address::_8Bit(_, mode)
| Address::_9Bit(_, mode)
| Address::_10Bit(_, mode)
| Address::_11Bit(_, mode)
| Address::_12Bit(_, mode)
| Address::_13Bit(_, mode)
| Address::_14Bit(_, mode)
| Address::_15Bit(_, mode)
| Address::_16Bit(_, mode)
| Address::_17Bit(_, mode)
| Address::_18Bit(_, mode)
| Address::_19Bit(_, mode)
| Address::_20Bit(_, mode)
| Address::_21Bit(_, mode)
| Address::_22Bit(_, mode)
| Address::_23Bit(_, mode)
| Address::_24Bit(_, mode)
| Address::_25Bit(_, mode)
| Address::_26Bit(_, mode)
| Address::_27Bit(_, mode)
| Address::_28Bit(_, mode)
| Address::_29Bit(_, mode)
| Address::_30Bit(_, mode)
| Address::_31Bit(_, mode)
| Address::_32Bit(_, mode) => *mode,
}
}
}
#[instability::unstable]
pub use crate::soc::clocks::SpiFunctionClockConfig as ClockSource;
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, procmacros::BuilderLite)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
#[non_exhaustive]
pub struct Config {
#[builder_lite(skip)]
reg: Result<u32, ConfigError>,
#[builder_lite(skip_setter)]
frequency: Rate,
#[builder_lite(unstable)]
#[builder_lite(skip_setter)]
clock_source: ClockSource,
mode: Mode,
read_bit_order: BitOrder,
write_bit_order: BitOrder,
#[builder_lite(unstable)]
min_async_transfer_size: usize,
}
impl Default for Config {
fn default() -> Self {
let mut this = Config {
reg: Ok(0),
frequency: Rate::from_mhz(1),
clock_source: ClockSource::default(),
mode: Mode::_0,
read_bit_order: BitOrder::MsbFirst,
write_bit_order: BitOrder::MsbFirst,
min_async_transfer_size: 0,
};
this.reg = this.recalculate();
this
}
}
impl Config {
pub fn with_frequency(mut self, frequency: Rate) -> Self {
self.frequency = frequency;
self.reg = self.recalculate();
self
}
#[instability::unstable]
pub fn with_clock_source(mut self, clock_source: ClockSource) -> Self {
self.clock_source = clock_source;
self.reg = self.recalculate();
self
}
fn clock_source_freq_hz(&self) -> Rate {
Rate::from_hz(
crate::soc::clocks::SpiInstance::function_clock_source_frequency(self.clock_source),
)
}
fn recalculate(&self) -> Result<u32, ConfigError> {
let source_freq = self.clock_source_freq_hz();
if self.frequency >= source_freq {
return Ok(1 << 31);
}
let (n, pre) = Self::divider_pair(source_freq.as_hz(), self.frequency.as_hz());
let l = n;
let h = (n / 2).max(1);
Ok((l - 1) | ((h - 1) << 6) | ((n - 1) << 12) | ((pre - 1) << 18)) }
fn divider_pair(source_freq_hz: u32, target_freq_hz: u32) -> (u32, u32) {
if target_freq_hz == 0 {
return (64, 16);
}
let min_divider = source_freq_hz.div_ceil(target_freq_hz).max(2);
if min_divider <= 64 {
return (min_divider, 1);
}
let mut best = (64, 16);
let mut best_divider = 64 * 16;
let mut pre = min_divider.div_ceil(64);
while pre <= 16 {
let n = min_divider.div_ceil(pre);
let divider = pre * n;
if divider < best_divider {
best = (n, pre);
best_divider = divider;
if divider == min_divider {
break;
}
}
pre += 1;
}
best
}
fn raw_clock_reg_value(&self) -> Result<u32, ConfigError> {
self.reg
}
fn validate(&self) -> Result<(), ConfigError> {
let source_freq = self.clock_source_freq_hz();
let min_divider = 1;
let max_divider = 16 * 64;
if self.frequency < source_freq / max_divider || self.frequency > source_freq / min_divider
{
return Err(ConfigError::FrequencyOutOfRange);
}
Ok(())
}
}
const SIO_PIN_COUNT: usize = 4 + cfg!(spi_master_has_octal) as usize * 4;
#[derive(Debug)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
struct SpiPinGuard {
sclk_pin: PinGuard,
cs_pin: PinGuard,
sio_pins: [PinGuard; SIO_PIN_COUNT],
}
impl SpiPinGuard {
const fn new_unconnected() -> Self {
Self {
sclk_pin: PinGuard::new_unconnected(),
cs_pin: PinGuard::new_unconnected(),
sio_pins: [const { PinGuard::new_unconnected() }; SIO_PIN_COUNT],
}
}
}
#[non_exhaustive]
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum ConfigError {
FrequencyOutOfRange,
}
impl core::error::Error for ConfigError {}
impl core::fmt::Display for ConfigError {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
match self {
ConfigError::FrequencyOutOfRange => {
write!(f, "The requested frequency is not in the supported range")
}
}
}
}
#[procmacros::doc_replace]
#[derive(Debug)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct Spi<'d, Dm: DriverMode> {
spi: SpiWrapper<'d>,
_mode: PhantomData<Dm>,
}
impl<Dm: DriverMode> Sealed for Spi<'_, Dm> {}
impl<'d> Spi<'d, Blocking> {
#[procmacros::doc_replace]
pub fn new(spi: impl Instance + 'd, config: Config) -> Result<Self, ConfigError> {
let mut this = Spi {
_mode: PhantomData,
spi: SpiWrapper::new(spi),
};
this.driver().init();
this.apply_config(&config)?;
let this = this.with_sck(NoPin).with_cs(NoPin);
for sio in 0..8 {
if let Some(signal) = this.driver().info.opt_sio_input(sio) {
signal.connect_to(&NoPin);
}
if let Some(signal) = this.driver().info.opt_sio_output(sio) {
signal.connect_to(&NoPin);
}
}
Ok(this)
}
pub fn into_async(mut self) -> Spi<'d, Async> {
self.set_interrupt_handler(self.spi.info().async_handler);
Spi {
spi: self.spi,
_mode: PhantomData,
}
}
#[doc_replace(
"peripheral_on" => {
cfg(multi_core) => "peripheral on the current core",
_ => "peripheral",
}
)]
#[instability::unstable]
pub fn set_interrupt_handler(&mut self, handler: InterruptHandler) {
self.spi.set_interrupt_handler(handler);
}
}
#[instability::unstable]
impl crate::interrupt::InterruptConfigurable for Spi<'_, Blocking> {
fn set_interrupt_handler(&mut self, handler: InterruptHandler) {
self.set_interrupt_handler(handler);
}
}
impl<'d> Spi<'d, Async> {
pub fn into_blocking(self) -> Spi<'d, Blocking> {
self.spi.disable_peri_interrupt_on_all_cores();
Spi {
spi: self.spi,
_mode: PhantomData,
}
}
#[procmacros::doc_replace]
pub async fn flush_async(&mut self) -> Result<(), Error> {
Ok(())
}
#[procmacros::doc_replace]
pub async fn transfer_in_place_async(&mut self, words: &mut [u8]) -> Result<(), Error> {
let _clock = SpiClockGuard::new(self.spi.info());
self.driver().setup_full_duplex()?;
if self.use_blocking_transfer(words.len()) {
return self.driver().transfer_in_place(words);
}
self.driver().transfer_in_place_async(words).await
}
#[instability::unstable]
pub async fn half_duplex_read_async(
&mut self,
data_mode: DataMode,
cmd: Command,
address: Address,
dummy: u8,
buffer: &mut [u8],
) -> Result<(), Error> {
let _clock = SpiClockGuard::new(self.spi.info());
if self.use_blocking_transfer(buffer.len()) {
return self
.driver()
.half_duplex_read(data_mode, cmd, address, dummy, buffer);
}
self.driver()
.half_duplex_read_async(data_mode, cmd, address, dummy, buffer)
.await
}
#[cfg_attr(
esp32,
doc = "Dummy phase configuration is currently not supported, only value `0` is valid (see issue [#2240](https://github.com/esp-rs/esp-hal/issues/2240))."
)]
#[instability::unstable]
pub async fn half_duplex_write_async(
&mut self,
data_mode: DataMode,
cmd: Command,
address: Address,
dummy: u8,
buffer: &[u8],
) -> Result<(), Error> {
let _clock = SpiClockGuard::new(self.spi.info());
if self.use_blocking_transfer(buffer.len()) {
return self
.driver()
.half_duplex_write(data_mode, cmd, address, dummy, buffer);
}
self.driver()
.half_duplex_write_async(data_mode, cmd, address, dummy, buffer)
.await
}
async fn read_async(&mut self, words: &mut [u8]) -> Result<(), Error> {
let _clock = SpiClockGuard::new(self.spi.info());
self.driver().setup_full_duplex()?;
if self.use_blocking_transfer(words.len()) {
return self.driver().read(words);
}
self.driver().read_async(words).await
}
async fn write_async(&mut self, words: &[u8]) -> Result<(), Error> {
let _clock = SpiClockGuard::new(self.spi.info());
self.driver().setup_full_duplex()?;
if self.use_blocking_transfer(words.len()) {
return self.driver().write(words);
}
self.driver().write_async(words).await
}
}
macro_rules! def_with_sio_pin {
($fn:ident, $n:literal) => {
#[doc = concat!(" Assign the SIO", stringify!($n), " pin for the SPI instance.")]
#[doc = " "]
#[doc = " Enables both input and output functionality for the pin, and connects it"]
#[doc = concat!(" to the SIO", stringify!($n), " output and input signals.")]
#[instability::unstable]
pub fn $fn(mut self, sio: impl PeripheralInput<'d> + PeripheralOutput<'d>) -> Self {
self.spi.pins().sio_pins[$n] = self.connect_sio_pin(sio.into(), $n);
self
}
};
}
impl<'d, Dm> Spi<'d, Dm>
where
Dm: DriverMode,
{
fn connect_sio_pin(&self, pin: interconnect::OutputSignal<'d>, n: usize) -> PinGuard {
let in_signal = self.spi.info().sio_input(n);
let out_signal = self.spi.info().sio_output(n);
pin.apply_input_config(&InputConfig::default());
pin.apply_output_config(&OutputConfig::default());
pin.set_input_enable(true);
pin.set_output_enable(false);
in_signal.connect_to(&pin);
pin.connect_with_guard(out_signal)
}
fn connect_sio_output_pin(&self, pin: interconnect::OutputSignal<'d>, n: usize) -> PinGuard {
let out_signal = self.spi.info().sio_output(n);
self.connect_output_pin(pin, out_signal)
}
fn connect_output_pin(
&self,
pin: interconnect::OutputSignal<'d>,
signal: OutputSignal,
) -> PinGuard {
pin.apply_output_config(&OutputConfig::default());
pin.set_output_enable(true);
pin.connect_with_guard(signal)
}
#[procmacros::doc_replace]
pub fn with_sck(mut self, sclk: impl PeripheralOutput<'d>) -> Self {
let info = self.spi.info();
self.spi.pins().sclk_pin = self.connect_output_pin(sclk.into(), info.sclk);
self
}
#[procmacros::doc_replace]
pub fn with_mosi(mut self, mosi: impl PeripheralOutput<'d>) -> Self {
self.spi.pins().sio_pins[0] = self.connect_sio_output_pin(mosi.into(), 0);
self
}
#[procmacros::doc_replace]
pub fn with_miso(self, miso: impl PeripheralInput<'d>) -> Self {
let miso = miso.into();
miso.apply_input_config(&InputConfig::default());
miso.set_input_enable(true);
self.driver().info.sio_input(1).connect_to(&miso);
self
}
#[instability::unstable]
pub fn with_sio0(mut self, mosi: impl PeripheralInput<'d> + PeripheralOutput<'d>) -> Self {
self.spi.pins().sio_pins[0] = self.connect_sio_pin(mosi.into(), 0);
self
}
#[instability::unstable]
pub fn with_sio1(mut self, sio1: impl PeripheralInput<'d> + PeripheralOutput<'d>) -> Self {
self.spi.pins().sio_pins[1] = self.connect_sio_pin(sio1.into(), 1);
self
}
def_with_sio_pin!(with_sio2, 2);
def_with_sio_pin!(with_sio3, 3);
#[cfg(spi_master_has_octal)]
def_with_sio_pin!(with_sio4, 4);
#[cfg(spi_master_has_octal)]
def_with_sio_pin!(with_sio5, 5);
#[cfg(spi_master_has_octal)]
def_with_sio_pin!(with_sio6, 6);
#[cfg(spi_master_has_octal)]
def_with_sio_pin!(with_sio7, 7);
#[instability::unstable]
pub fn with_cs(mut self, cs: impl PeripheralOutput<'d>) -> Self {
let info = self.spi.info();
self.spi.pins().cs_pin = self.connect_output_pin(cs.into(), info.cs(0));
self
}
#[doc_replace(
"max_frequency" => {
cfg(esp32h2) => "48MHz",
_ => "80MHz",
}
)]
pub fn apply_config(&mut self, config: &Config) -> Result<(), ConfigError> {
self.driver().apply_config(config)
}
#[procmacros::doc_replace]
pub fn write(&mut self, words: &[u8]) -> Result<(), Error> {
let _clock = SpiClockGuard::new(self.spi.info());
self.driver().setup_full_duplex()?;
self.driver().write(words)
}
#[procmacros::doc_replace]
pub fn read(&mut self, words: &mut [u8]) -> Result<(), Error> {
let _clock = SpiClockGuard::new(self.spi.info());
self.driver().setup_full_duplex()?;
self.driver().read(words)
}
#[procmacros::doc_replace]
pub fn transfer(&mut self, words: &mut [u8]) -> Result<(), Error> {
let _clock = SpiClockGuard::new(self.spi.info());
self.driver().setup_full_duplex()?;
self.driver().transfer_in_place(words)
}
#[instability::unstable]
pub fn half_duplex_read(
&mut self,
data_mode: DataMode,
cmd: Command,
address: Address,
dummy: u8,
buffer: &mut [u8],
) -> Result<(), Error> {
let _clock = SpiClockGuard::new(self.spi.info());
self.driver()
.half_duplex_read(data_mode, cmd, address, dummy, buffer)
}
#[cfg_attr(
esp32,
doc = "Dummy phase configuration is currently not supported, only value `0` is valid (see issue [#2240](https://github.com/esp-rs/esp-hal/issues/2240))."
)]
#[instability::unstable]
pub fn half_duplex_write(
&mut self,
data_mode: DataMode,
cmd: Command,
address: Address,
dummy: u8,
buffer: &[u8],
) -> Result<(), Error> {
let _clock = SpiClockGuard::new(self.spi.info());
self.driver()
.half_duplex_write(data_mode, cmd, address, dummy, buffer)
}
fn use_blocking_transfer(&self, transfer_size: usize) -> bool {
let threshold = self
.spi
.state()
.min_async_transfer_size
.load(Ordering::Relaxed);
threshold > 0 && transfer_size < threshold
}
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)
}
}
impl<Dm> embedded_hal::spi::ErrorType for Spi<'_, Dm>
where
Dm: DriverMode,
{
type Error = Error;
}
impl<Dm> SpiBus for Spi<'_, Dm>
where
Dm: DriverMode,
{
fn read(&mut self, words: &mut [u8]) -> Result<(), Self::Error> {
self.read(words)
}
fn write(&mut self, words: &[u8]) -> Result<(), Self::Error> {
self.write(words)
}
fn transfer(&mut self, read: &mut [u8], write: &[u8]) -> Result<(), Self::Error> {
let _clock = SpiClockGuard::new(self.spi.info());
self.driver().setup_full_duplex()?;
if read.is_empty() {
self.driver().write(write)
} else if write.is_empty() {
self.driver().read(read)
} else {
self.driver().transfer(read, write)
}
}
fn transfer_in_place(&mut self, words: &mut [u8]) -> Result<(), Self::Error> {
let _clock = SpiClockGuard::new(self.spi.info());
self.driver().setup_full_duplex()?;
self.driver().transfer_in_place(words)
}
fn flush(&mut self) -> Result<(), Self::Error> {
Ok(())
}
}
impl SpiBusAsync for Spi<'_, Async> {
async fn read(&mut self, words: &mut [u8]) -> Result<(), Self::Error> {
self.read_async(words).await
}
async fn write(&mut self, words: &[u8]) -> Result<(), Self::Error> {
self.write_async(words).await
}
async fn transfer(&mut self, read: &mut [u8], write: &[u8]) -> Result<(), Self::Error> {
let _clock = SpiClockGuard::new(self.spi.info());
self.driver().setup_full_duplex()?;
if self.use_blocking_transfer(read.len().max(write.len())) {
return if read.is_empty() {
self.driver().write(write)
} else if write.is_empty() {
self.driver().read(read)
} else {
self.driver().transfer(read, write)
};
}
if read.is_empty() {
self.driver().write_async(write).await
} else if write.is_empty() {
self.driver().read_async(read).await
} else {
self.driver().transfer_async(read, write).await
}
}
async fn transfer_in_place(&mut self, words: &mut [u8]) -> Result<(), Self::Error> {
self.transfer_in_place_async(words).await
}
async fn flush(&mut self) -> Result<(), Self::Error> {
Ok(())
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
#[instability::unstable]
pub enum DataMode {
SingleTwoDataLines,
Single,
Dual,
Quad,
#[cfg(spi_master_has_octal)]
Octal,
}
crate::any_peripheral! {
pub peripheral AnySpi<'d> {
#[cfg(spi_master_spi2)]
Spi2(crate::peripherals::SPI2<'d>),
#[cfg(spi_master_spi3)]
Spi3(crate::peripherals::SPI3<'d>),
}
}
#[cfg(spi_master_supports_dma)]
with_spi_master_dma_engine! {
($engine:tt, $any_ch:ident) => {
use crate::dma::DmaEligiblePeripheral;
impl<'d> DmaEligiblePeripheral<crate::dma::$any_ch<'d>> for AnySpi<'d> {
fn dma_peripheral(&self) -> crate::dma::DmaPeripheral {
any::delegate!(self, spi => { spi.dma_peripheral() })
}
}
};
}
impl QspiInstance for AnySpi<'_> {}
impl Instance for AnySpi<'_> {
#[inline]
fn parts(&self) -> (&'static Info, &'static State) {
any::delegate!(self, spi => { spi.parts() })
}
}
impl AnySpi<'_> {
fn bind_peri_interrupt(&self, handler: InterruptHandler) {
any::delegate!(self, spi => { spi.bind_peri_interrupt(handler) })
}
fn disable_peri_interrupt_on_all_cores(&self) {
any::delegate!(self, spi => { spi.disable_peri_interrupt_on_all_cores() })
}
fn set_interrupt_handler(&self, handler: InterruptHandler) {
self.disable_peri_interrupt_on_all_cores();
self.bind_peri_interrupt(handler);
}
}