#[cfg(esp32)]
use core::cell::Cell;
use core::{
cell::UnsafeCell,
future::Future,
marker::PhantomData,
mem::MaybeUninit,
pin::Pin,
task::{Context, Poll},
};
#[cfg(spi_master_supports_dma)]
mod dma;
#[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::{EnumSet, EnumSetType, enum_set};
use procmacros::doc_replace;
use super::{BitOrder, Error, Mode};
use crate::{
Async,
Blocking,
DriverMode,
RegisterToggle,
asynch::AtomicWaker,
clock::Clocks,
gpio::{
InputConfig,
InputSignal,
NoPin,
OutputConfig,
OutputSignal,
PinGuard,
interconnect::{self, PeripheralInput, PeripheralOutput},
},
handler,
interrupt::InterruptHandler,
pac::spi2::RegisterBlock,
private::{self, DropGuard, Sealed},
ram,
system::PeripheralGuard,
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 = if cfg!(esp32s2) { 72 } else { 64 };
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,
}
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
#[instability::unstable]
pub enum ClockSource {
Apb,
}
#[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,
}
impl Default for Config {
fn default() -> Self {
let mut this = Config {
reg: Ok(0),
frequency: Rate::from_mhz(1),
clock_source: ClockSource::Apb,
mode: Mode::_0,
read_bit_order: BitOrder::MsbFirst,
write_bit_order: BitOrder::MsbFirst,
};
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 {
let clocks = Clocks::get();
cfg_if::cfg_if! {
if #[cfg(esp32h2)] {
let _clocks = clocks;
Rate::from_mhz(48)
} else if #[cfg(any(esp32c5, esp32c61))] {
let _clocks = clocks;
Rate::from_mhz(80) } else if #[cfg(esp32c6)] {
let _clocks = clocks;
Rate::from_mhz(80)
} else {
clocks.apb_clock
}
}
}
fn recalculate(&self) -> Result<u32, ConfigError> {
let source_freq = self.clock_source_freq_hz();
let reg_val: u32;
let duty_cycle = 128;
if self.frequency > ((source_freq / 4) * 3) {
reg_val = 1 << 31;
} else {
let mut pre: i32;
let mut bestn: i32 = -1;
let mut bestpre: i32 = -1;
let mut besterr: i32 = 0;
let mut errval: i32;
let target_freq_hz = self.frequency.as_hz() as i32;
let source_freq_hz = source_freq.as_hz() as i32;
for n in 2..=64 {
pre = ((source_freq_hz / n) + (target_freq_hz / 2)) / target_freq_hz;
if pre <= 0 {
pre = 1;
}
if pre > 16 {
pre = 16;
}
errval = (source_freq_hz / (pre * n) - target_freq_hz).abs();
if bestn == -1 || errval <= besterr {
besterr = errval;
bestn = n;
bestpre = pre;
}
}
let n: i32 = bestn;
pre = bestpre;
let l: i32 = n;
let mut h: i32 = (duty_cycle * n + 127) / 256;
if h <= 0 {
h = 1;
}
reg_val = (l as u32 - 1)
| ((h as u32 - 1) << 6)
| ((n as u32 - 1) << 12)
| ((pre as u32 - 1) << 18);
}
Ok(reg_val)
}
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")
}
}
}
}
#[derive(Debug)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
struct SpiWrapper<'d> {
spi: AnySpi<'d>,
_guard: PeripheralGuard,
}
impl<'d> SpiWrapper<'d> {
fn new(spi: impl Instance + 'd) -> Self {
let p = spi.info().peripheral;
let this = Self {
spi: spi.degrade(),
_guard: PeripheralGuard::new(p),
};
unsafe {
this.state()
.pins
.get()
.write(MaybeUninit::new(SpiPinGuard::new_unconnected()))
}
this
}
fn info(&self) -> &'static Info {
self.spi.info()
}
fn state(&self) -> &'static State {
self.spi.state()
}
fn disable_peri_interrupt_on_all_cores(&self) {
self.spi.disable_peri_interrupt_on_all_cores();
}
fn set_interrupt_handler(&self, handler: InterruptHandler) {
self.spi.set_interrupt_handler(handler);
}
fn pins(&mut self) -> &mut SpiPinGuard {
unsafe {
self.state().pins()
}
}
}
impl Drop for SpiWrapper<'_> {
fn drop(&mut self) {
unsafe {
self.spi.state().deinit();
}
}
}
#[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> {
self.driver().flush_async().await;
Ok(())
}
#[procmacros::doc_replace]
pub async fn transfer_in_place_async(&mut self, words: &mut [u8]) -> Result<(), Error> {
self.driver().flush_async().await;
self.driver().setup_full_duplex()?;
self.driver().transfer_in_place_async(words).await
}
async fn read_async(&mut self, words: &mut [u8]) -> Result<(), Error> {
self.driver().flush_async().await;
self.driver().setup_full_duplex()?;
self.driver().read_async(words).await
}
async fn write_async(&mut self, words: &[u8]) -> Result<(), Error> {
self.driver().flush_async().await;
self.driver().setup_full_duplex()?;
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> {
self.driver().flush()?;
self.driver().setup_full_duplex()?;
for chunk in words.chunks(FIFO_SIZE) {
self.driver().write_one(chunk)?;
self.driver().flush()?;
}
Ok(())
}
#[procmacros::doc_replace]
pub fn read(&mut self, words: &mut [u8]) -> Result<(), Error> {
self.driver().flush()?;
self.driver().setup_full_duplex()?;
self.driver().read(words)
}
#[procmacros::doc_replace]
pub fn transfer(&mut self, words: &mut [u8]) -> Result<(), Error> {
self.driver().flush()?;
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> {
if buffer.len() > FIFO_SIZE {
return Err(Error::FifoSizeExeeded);
}
if buffer.is_empty() {
error!("Half-duplex mode does not support empty buffer");
return Err(Error::Unsupported);
}
self.flush()?;
self.driver().setup_half_duplex(
false,
cmd,
address,
false,
dummy,
buffer.is_empty(),
data_mode,
)?;
self.driver().configure_datalen(buffer.len(), 0);
self.driver().start_operation();
self.driver().flush()?;
self.driver().read_from_fifo(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> {
if buffer.len() > FIFO_SIZE {
return Err(Error::FifoSizeExeeded);
}
self.flush()?;
cfg_if::cfg_if! {
if #[cfg(all(esp32, spi_address_workaround))] {
let mut buffer = buffer;
let mut data_mode = data_mode;
let mut address = address;
let addr_bytes;
if buffer.is_empty() && !address.is_none() {
let bytes_to_write = address.width().div_ceil(8);
addr_bytes = address.value().to_be_bytes();
buffer = &addr_bytes[4 - bytes_to_write..][..bytes_to_write];
data_mode = address.mode();
address = Address::None;
}
if dummy > 0 {
error!("Dummy bits are not supported without data");
return Err(Error::Unsupported);
}
}
}
self.driver().setup_half_duplex(
true,
cmd,
address,
false,
dummy,
buffer.is_empty(),
data_mode,
)?;
if !buffer.is_empty() {
self.driver().configure_datalen(0, buffer.len());
self.driver().fill_fifo(buffer);
}
self.driver().start_operation();
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)
}
}
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.driver().flush()?;
self.driver().setup_full_duplex()?;
for chunk in words.chunks(FIFO_SIZE) {
self.driver().flush()?;
self.driver().write_one(chunk)?;
}
Ok(())
}
fn transfer(&mut self, read: &mut [u8], write: &[u8]) -> Result<(), Self::Error> {
self.driver().flush()?;
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> {
self.driver().flush()?;
self.driver().setup_full_duplex()?;
self.driver().transfer_in_place(words)
}
fn flush(&mut self) -> Result<(), Self::Error> {
self.driver().flush()
}
}
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> {
self.driver().flush_async().await;
self.driver().setup_full_duplex()?;
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> {
self.flush_async().await
}
}
pub trait Instance: private::Sealed + any::Degrade {
#[doc(hidden)]
fn parts(&self) -> (&'static Info, &'static State);
#[doc(hidden)]
#[inline(always)]
fn info(&self) -> &'static Info {
self.parts().0
}
#[doc(hidden)]
#[inline(always)]
fn state(&self) -> &'static State {
self.parts().1
}
}
#[doc(hidden)]
pub trait QspiInstance: Instance {}
#[doc(hidden)]
#[non_exhaustive]
#[allow(private_interfaces, reason = "Unstable details")]
pub struct Info {
pub register_block: *const RegisterBlock,
pub peripheral: crate::system::Peripheral,
pub async_handler: InterruptHandler,
pub sclk: OutputSignal,
pub cs: &'static [OutputSignal],
pub sio_inputs: &'static [InputSignal],
pub sio_outputs: &'static [OutputSignal],
}
impl Info {
fn cs(&self, n: usize) -> OutputSignal {
*unwrap!(self.cs.get(n), "CS{} is not defined", n)
}
fn opt_sio_input(&self, n: usize) -> Option<InputSignal> {
self.sio_inputs.get(n).copied()
}
fn opt_sio_output(&self, n: usize) -> Option<OutputSignal> {
self.sio_outputs.get(n).copied()
}
fn sio_input(&self, n: usize) -> InputSignal {
unwrap!(self.opt_sio_input(n), "SIO{} is not defined", n)
}
fn sio_output(&self, n: usize) -> OutputSignal {
unwrap!(self.opt_sio_output(n), "SIO{} is not defined", n)
}
}
struct Driver {
info: &'static Info,
state: &'static State,
}
impl Driver {
pub fn regs(&self) -> &RegisterBlock {
unsafe { &*self.info.register_block }
}
fn abort_transfer(&self) {
cfg_if::cfg_if! {
if #[cfg(esp32)] {
self.regs().slave().toggle(|w, en| w.mode().bit(en));
} else {
self.configure_datalen(1, 1);
}
}
self.update();
}
fn init(&self) {
self.regs().user().modify(|_, w| {
w.usr_miso_highpart().clear_bit();
w.usr_mosi_highpart().clear_bit();
w.doutdin().set_bit();
w.usr_miso().set_bit();
w.usr_mosi().set_bit();
w.cs_hold().set_bit();
w.usr_dummy_idle().set_bit();
w.usr_addr().clear_bit();
w.usr_command().clear_bit()
});
#[cfg(not(any(esp32, esp32s2)))]
self.regs().clk_gate().modify(|_, w| {
w.clk_en().set_bit();
w.mst_clk_active().set_bit();
w.mst_clk_sel().set_bit()
});
#[cfg(soc_has_pcr)]
unsafe {
crate::peripherals::PCR::regs()
.spi2_clkm_conf()
.modify(|_, w| {
#[cfg(spi_master_has_clk_pre_div)]
w.spi2_clkm_div_num().bits(1);
w.spi2_clkm_sel().bits(1);
w.spi2_clkm_en().set_bit()
});
}
cfg_if::cfg_if! {
if #[cfg(esp32)] {
self.regs().ctrl().modify(|_, w| w.wp().clear_bit());
} else {
self.regs().ctrl().modify(|_, w| {
w.q_pol().clear_bit();
w.d_pol().clear_bit();
#[cfg(esp32s2)]
w.wp().clear_bit();
#[cfg(not(esp32s2))]
w.hold_pol().clear_bit();
w
});
self.regs().misc().write(|w| unsafe { w.bits(0) });
}
}
self.regs().slave().write(|w| unsafe { w.bits(0) });
}
#[cfg(not(esp32))]
fn init_spi_data_mode(
&self,
cmd_mode: DataMode,
address_mode: DataMode,
data_mode: DataMode,
) -> Result<(), Error> {
self.regs().ctrl().modify(|_, w| {
w.fcmd_dual().bit(cmd_mode == DataMode::Dual);
w.fcmd_quad().bit(cmd_mode == DataMode::Quad);
w.faddr_dual().bit(address_mode == DataMode::Dual);
w.faddr_quad().bit(address_mode == DataMode::Quad);
w.fread_dual().bit(data_mode == DataMode::Dual);
w.fread_quad().bit(data_mode == DataMode::Quad)
});
self.regs().user().modify(|_, w| {
w.fwrite_dual().bit(data_mode == DataMode::Dual);
w.fwrite_quad().bit(data_mode == DataMode::Quad)
});
Ok(())
}
#[cfg(esp32)]
fn init_spi_data_mode(
&self,
cmd_mode: DataMode,
address_mode: DataMode,
data_mode: DataMode,
) -> Result<(), Error> {
match cmd_mode {
DataMode::Single => (),
DataMode::SingleTwoDataLines => (),
_ => {
error!("Commands must be single bit wide");
return Err(Error::Unsupported);
}
}
match address_mode {
DataMode::Single | DataMode::SingleTwoDataLines => {
self.regs().ctrl().modify(|_, w| {
w.fastrd_mode()
.bit(matches!(data_mode, DataMode::Dual | DataMode::Quad));
w.fread_dio().clear_bit();
w.fread_qio().clear_bit();
w.fread_dual().bit(data_mode == DataMode::Dual);
w.fread_quad().bit(data_mode == DataMode::Quad)
});
self.regs().user().modify(|_, w| {
w.fwrite_dio().clear_bit();
w.fwrite_qio().clear_bit();
w.fwrite_dual().bit(data_mode == DataMode::Dual);
w.fwrite_quad().bit(data_mode == DataMode::Quad)
});
}
address_mode if address_mode == data_mode => {
self.regs().ctrl().modify(|_, w| {
w.fastrd_mode()
.bit(matches!(data_mode, DataMode::Dual | DataMode::Quad));
w.fread_dio().bit(address_mode == DataMode::Dual);
w.fread_qio().bit(address_mode == DataMode::Quad);
w.fread_dual().clear_bit();
w.fread_quad().clear_bit()
});
self.regs().user().modify(|_, w| {
w.fwrite_dio().bit(address_mode == DataMode::Dual);
w.fwrite_qio().bit(address_mode == DataMode::Quad);
w.fwrite_dual().clear_bit();
w.fwrite_quad().clear_bit()
});
}
_ => {
error!("Address mode must be single bit wide or equal to the data mode");
return Err(Error::Unsupported);
}
}
Ok(())
}
#[cfg_attr(not(feature = "unstable"), allow(dead_code))]
fn enable_listen(&self, interrupts: EnumSet<SpiInterrupt>, enable: bool) {
cfg_if::cfg_if! {
if #[cfg(esp32)] {
self.regs().slave().modify(|_, w| {
for interrupt in interrupts {
match interrupt {
SpiInterrupt::TransferDone => w.trans_inten().bit(enable),
};
}
w
});
} else if #[cfg(esp32s2)] {
self.regs().slave().modify(|_, w| {
for interrupt in interrupts {
match interrupt {
SpiInterrupt::TransferDone => w.int_trans_done_en().bit(enable),
SpiInterrupt::DmaSegmentedTransferDone => w.int_dma_seg_trans_en().bit(enable),
};
}
w
});
} else {
self.regs().dma_int_ena().modify(|_, w| {
for interrupt in interrupts {
match interrupt {
SpiInterrupt::TransferDone => w.trans_done().bit(enable),
#[cfg(spi_master_has_dma_segmented_transfer)]
SpiInterrupt::DmaSegmentedTransferDone => w.dma_seg_trans_done().bit(enable),
#[cfg(spi_master_has_app_interrupts)]
SpiInterrupt::App2 => w.app2().bit(enable),
#[cfg(spi_master_has_app_interrupts)]
SpiInterrupt::App1 => w.app1().bit(enable),
};
}
w
});
}
}
}
#[cfg_attr(not(feature = "unstable"), allow(dead_code))]
fn interrupts(&self) -> EnumSet<SpiInterrupt> {
let mut res = EnumSet::new();
cfg_if::cfg_if! {
if #[cfg(esp32)] {
if self.regs().slave().read().trans_done().bit() {
res.insert(SpiInterrupt::TransferDone);
}
} else if #[cfg(esp32s2)] {
if self.regs().slave().read().trans_done().bit() {
res.insert(SpiInterrupt::TransferDone);
}
if self.regs().hold().read().dma_seg_trans_done().bit() {
res.insert(SpiInterrupt::DmaSegmentedTransferDone);
}
} else {
let ints = self.regs().dma_int_raw().read();
if ints.trans_done().bit() {
res.insert(SpiInterrupt::TransferDone);
}
#[cfg(spi_master_has_dma_segmented_transfer)]
if ints.dma_seg_trans_done().bit() {
res.insert(SpiInterrupt::DmaSegmentedTransferDone);
}
#[cfg(spi_master_has_app_interrupts)]
if ints.app2().bit() {
res.insert(SpiInterrupt::App2);
}
#[cfg(spi_master_has_app_interrupts)]
if ints.app1().bit() {
res.insert(SpiInterrupt::App1);
}
}
}
res
}
fn clear_interrupts(&self, interrupts: EnumSet<SpiInterrupt>) {
cfg_if::cfg_if! {
if #[cfg(esp32)] {
for interrupt in interrupts {
match interrupt {
SpiInterrupt::TransferDone => {
self.regs().slave().modify(|_, w| w.trans_done().clear_bit());
}
}
}
} else if #[cfg(esp32s2)] {
for interrupt in interrupts {
match interrupt {
SpiInterrupt::TransferDone => {
self.regs().slave().modify(|_, w| w.trans_done().clear_bit());
}
SpiInterrupt::DmaSegmentedTransferDone => {
self.regs()
.hold()
.modify(|_, w| w.dma_seg_trans_done().clear_bit());
}
}
}
} else {
self.regs().dma_int_clr().write(|w| {
for interrupt in interrupts {
match interrupt {
SpiInterrupt::TransferDone => w.trans_done().clear_bit_by_one(),
#[cfg(spi_master_has_dma_segmented_transfer)]
SpiInterrupt::DmaSegmentedTransferDone => w.dma_seg_trans_done().clear_bit_by_one(),
#[cfg(spi_master_has_app_interrupts)]
SpiInterrupt::App2 => w.app2().clear_bit_by_one(),
#[cfg(spi_master_has_app_interrupts)]
SpiInterrupt::App1 => w.app1().clear_bit_by_one(),
};
}
w
});
}
}
}
fn apply_config(&self, config: &Config) -> Result<(), ConfigError> {
config.validate()?;
self.ch_bus_freq(config)?;
self.set_bit_order(config.read_bit_order, config.write_bit_order);
self.set_data_mode(config.mode);
#[cfg(esp32)]
self.calculate_half_duplex_values(config);
Ok(())
}
#[cfg(esp32)]
fn calculate_half_duplex_values(&self, config: &Config) {
let f_apb = 80_000_000;
let source_freq_hz = match config.clock_source {
ClockSource::Apb => f_apb,
};
let clock_reg = self.regs().clock().read();
let eff_clk = if clock_reg.clk_equ_sysclk().bit_is_set() {
f_apb
} else {
let pre = clock_reg.clkdiv_pre().bits() as i32 + 1;
let n = clock_reg.clkcnt_n().bits() as i32 + 1;
f_apb / (pre * n)
};
let apbclk_khz = source_freq_hz / 1000;
let spiclk_apb_n = source_freq_hz / eff_clk;
let input_delay_ns = 25; let delay_apb_n = (1 + input_delay_ns) * apbclk_khz / 1000 / 1000;
let dummy_required = delay_apb_n / spiclk_apb_n;
let timing_miso_delay = if dummy_required > 0 {
Some(((dummy_required + 1) * spiclk_apb_n - delay_apb_n - 1) as u8)
} else if delay_apb_n * 4 <= spiclk_apb_n {
None
} else {
Some(0)
};
self.state.esp32_hack.extra_dummy.set(dummy_required as u8);
self.state
.esp32_hack
.timing_miso_delay
.set(timing_miso_delay);
}
fn set_data_mode(&self, data_mode: Mode) {
cfg_if::cfg_if! {
if #[cfg(esp32)] {
let pin_reg = self.regs().pin();
} else {
let pin_reg = self.regs().misc();
}
};
pin_reg.modify(|_, w| {
w.ck_idle_edge()
.bit(matches!(data_mode, Mode::_2 | Mode::_3))
});
self.regs().user().modify(|_, w| {
w.ck_out_edge()
.bit(matches!(data_mode, Mode::_1 | Mode::_2))
});
}
fn ch_bus_freq(&self, bus_clock_config: &Config) -> Result<(), ConfigError> {
fn enable_clocks(_reg_block: &RegisterBlock, _enable: bool) {
#[cfg(not(any(esp32, esp32s2)))]
_reg_block.clk_gate().modify(|_, w| {
w.clk_en().bit(_enable);
w.mst_clk_active().bit(_enable);
w.mst_clk_sel().bit(true) });
}
let raw = bus_clock_config.raw_clock_reg_value()?;
enable_clocks(self.regs(), false);
self.regs().clock().write(|w| unsafe { w.bits(raw) });
enable_clocks(self.regs(), true);
Ok(())
}
#[cfg(not(any(esp32, esp32c3, esp32s2)))]
fn set_bit_order(&self, read_order: BitOrder, write_order: BitOrder) {
let read_value = match read_order {
BitOrder::MsbFirst => 0,
BitOrder::LsbFirst => 1,
};
let write_value = match write_order {
BitOrder::MsbFirst => 0,
BitOrder::LsbFirst => 1,
};
self.regs().ctrl().modify(|_, w| unsafe {
w.rd_bit_order().bits(read_value);
w.wr_bit_order().bits(write_value);
w
});
}
#[cfg(any(esp32, esp32c3, esp32s2))]
fn set_bit_order(&self, read_order: BitOrder, write_order: BitOrder) {
let read_value = match read_order {
BitOrder::MsbFirst => false,
BitOrder::LsbFirst => true,
};
let write_value = match write_order {
BitOrder::MsbFirst => false,
BitOrder::LsbFirst => true,
};
self.regs().ctrl().modify(|_, w| {
w.rd_bit_order().bit(read_value);
w.wr_bit_order().bit(write_value);
w
});
}
#[cfg_attr(place_spi_master_driver_in_ram, ram)]
fn fill_fifo(&self, chunk: &[u8]) {
let mut c_iter = chunk.chunks_exact(4);
let mut w_iter = self.regs().w_iter();
for c in c_iter.by_ref() {
if let Some(w_reg) = w_iter.next() {
let word = (c[0] as u32)
| ((c[1] as u32) << 8)
| ((c[2] as u32) << 16)
| ((c[3] as u32) << 24);
w_reg.write(|w| w.buf().set(word));
}
}
let rem = c_iter.remainder();
if !rem.is_empty()
&& let Some(w_reg) = w_iter.next()
{
let word = match rem.len() {
3 => (rem[0] as u32) | ((rem[1] as u32) << 8) | ((rem[2] as u32) << 16),
2 => (rem[0] as u32) | ((rem[1] as u32) << 8),
1 => rem[0] as u32,
_ => unreachable!(),
};
w_reg.write(|w| w.buf().set(word));
}
}
#[cfg_attr(place_spi_master_driver_in_ram, ram)]
fn write_one(&self, words: &[u8]) -> Result<(), Error> {
if words.len() > FIFO_SIZE {
return Err(Error::FifoSizeExeeded);
}
self.configure_datalen(0, words.len());
self.fill_fifo(words);
self.start_operation();
Ok(())
}
#[cfg_attr(place_spi_master_driver_in_ram, ram)]
fn write(&self, words: &[u8]) -> Result<(), Error> {
for chunk in words.chunks(FIFO_SIZE) {
self.flush()?;
self.write_one(chunk)?;
}
Ok(())
}
#[cfg_attr(place_spi_master_driver_in_ram, ram)]
async fn write_async(&self, words: &[u8]) -> Result<(), Error> {
for chunk in words.chunks(FIFO_SIZE) {
self.write_one(chunk)?;
self.flush_async().await;
}
Ok(())
}
#[cfg_attr(place_spi_master_driver_in_ram, ram)]
fn read(&self, words: &mut [u8]) -> Result<(), Error> {
let empty_array = [EMPTY_WRITE_PAD; FIFO_SIZE];
for chunk in words.chunks_mut(FIFO_SIZE) {
self.write_one(&empty_array[0..chunk.len()])?;
self.flush()?;
self.read_from_fifo(chunk)?;
}
Ok(())
}
#[cfg_attr(place_spi_master_driver_in_ram, ram)]
async fn read_async(&self, words: &mut [u8]) -> Result<(), Error> {
let empty_array = [EMPTY_WRITE_PAD; FIFO_SIZE];
for chunk in words.chunks_mut(FIFO_SIZE) {
self.write_one(&empty_array[0..chunk.len()])?;
self.flush_async().await;
self.read_from_fifo(chunk)?;
}
Ok(())
}
#[cfg_attr(place_spi_master_driver_in_ram, ram)]
fn read_from_fifo(&self, words: &mut [u8]) -> Result<(), Error> {
if words.len() > FIFO_SIZE {
return Err(Error::FifoSizeExeeded);
}
for (chunk, w_reg) in words.chunks_mut(4).zip(self.regs().w_iter()) {
let reg_val = w_reg.read().bits();
let bytes = reg_val.to_le_bytes();
let len = chunk.len();
chunk.copy_from_slice(&bytes[..len]);
}
Ok(())
}
fn busy(&self) -> bool {
self.regs().cmd().read().usr().bit_is_set()
}
#[cfg_attr(place_spi_master_driver_in_ram, ram)]
fn flush_async(&self) -> impl Future<Output = ()> {
SpiFuture { driver: self }
}
#[cfg_attr(place_spi_master_driver_in_ram, ram)]
fn flush(&self) -> Result<(), Error> {
while self.busy() {
}
Ok(())
}
#[cfg_attr(place_spi_master_driver_in_ram, ram)]
fn transfer_in_place(&self, words: &mut [u8]) -> Result<(), Error> {
for chunk in words.chunks_mut(FIFO_SIZE) {
self.write_one(chunk)?;
self.flush()?;
self.read_from_fifo(chunk)?;
}
Ok(())
}
#[cfg_attr(place_spi_master_driver_in_ram, ram)]
fn transfer(&self, read: &mut [u8], write: &[u8]) -> Result<(), Error> {
let mut write_from = 0;
let mut read_from = 0;
loop {
let write_inc = core::cmp::min(FIFO_SIZE, write.len() - write_from);
let read_inc = core::cmp::min(FIFO_SIZE, read.len() - read_from);
if (write_inc == 0) && (read_inc == 0) {
break;
}
self.flush()?;
if write_inc < read_inc {
let mut empty = [EMPTY_WRITE_PAD; FIFO_SIZE];
empty[0..write_inc].copy_from_slice(&write[write_from..][..write_inc]);
self.write_one(&empty[..read_inc])?;
} else {
self.write_one(&write[write_from..][..write_inc])?;
}
if read_inc > 0 {
self.flush()?;
self.read_from_fifo(&mut read[read_from..][..read_inc])?;
}
write_from += write_inc;
read_from += read_inc;
}
Ok(())
}
#[cfg_attr(place_spi_master_driver_in_ram, ram)]
async fn transfer_in_place_async(&self, words: &mut [u8]) -> Result<(), Error> {
for chunk in words.chunks_mut(FIFO_SIZE) {
let cancel_on_drop = DropGuard::new((), |_| {
self.abort_transfer();
while self.busy() {}
});
let res = self.write_one(chunk);
self.flush_async().await;
cancel_on_drop.defuse();
res?;
self.read_from_fifo(chunk)?;
}
Ok(())
}
#[cfg_attr(place_spi_master_driver_in_ram, ram)]
async fn transfer_async(&self, read: &mut [u8], write: &[u8]) -> Result<(), Error> {
let mut write_from = 0;
let mut read_from = 0;
loop {
let write_inc = core::cmp::min(FIFO_SIZE, write.len() - write_from);
let read_inc = core::cmp::min(FIFO_SIZE, read.len() - read_from);
if (write_inc == 0) && (read_inc == 0) {
break;
}
self.flush_async().await;
if write_inc < read_inc {
let mut empty = [EMPTY_WRITE_PAD; FIFO_SIZE];
empty[0..write_inc].copy_from_slice(&write[write_from..][..write_inc]);
self.write_one(&empty[..read_inc])?;
} else {
self.write_one(&write[write_from..][..write_inc])?;
}
self.flush_async().await;
if read_inc > 0 {
self.read_from_fifo(&mut read[read_from..][..read_inc])?;
}
write_from += write_inc;
read_from += read_inc;
}
Ok(())
}
#[cfg_attr(place_spi_master_driver_in_ram, ram)]
fn start_operation(&self) {
self.update();
self.clear_interrupts(SpiInterrupt::TransferDone.into());
self.regs().cmd().modify(|_, w| w.usr().set_bit());
}
fn setup_full_duplex(&self) -> Result<(), Error> {
self.regs().user().modify(|_, w| {
w.usr_miso().set_bit();
w.usr_mosi().set_bit();
w.doutdin().set_bit();
w.usr_dummy().clear_bit();
w.sio().clear_bit()
});
self.init_spi_data_mode(
DataMode::SingleTwoDataLines,
DataMode::SingleTwoDataLines,
DataMode::SingleTwoDataLines,
)?;
#[cfg(esp32)]
self.regs().ctrl2().modify(|_, w| unsafe {
w.miso_delay_mode().bits(0);
w.miso_delay_num().bits(0)
});
Ok(())
}
#[expect(clippy::too_many_arguments)]
fn setup_half_duplex(
&self,
is_write: bool,
cmd: Command,
address: Address,
dummy_idle: bool,
dummy: u8,
no_mosi_miso: bool,
data_mode: DataMode,
) -> Result<(), Error> {
self.init_spi_data_mode(cmd.mode(), address.mode(), data_mode)?;
#[cfg(esp32)]
let mut dummy = dummy;
#[cfg(esp32)]
self.regs().ctrl2().modify(|_, w| {
let mut delay_mode = 0;
let mut delay_num = 0;
if !is_write {
let timing_miso_delay = self.state.esp32_hack.timing_miso_delay.get();
let extra_dummy = self.state.esp32_hack.extra_dummy.get();
dummy += extra_dummy;
if let Some(delay) = timing_miso_delay {
delay_num = if extra_dummy > 0 { delay } else { 0 };
} else {
let out_edge = self.regs().user().read().ck_out_edge().bit_is_set();
delay_mode = if out_edge { 1 } else { 2 };
}
}
unsafe {
w.miso_delay_mode().bits(delay_mode);
w.miso_delay_num().bits(delay_num)
}
});
let reg_block = self.regs();
reg_block.user().modify(|_, w| {
w.usr_miso_highpart().clear_bit();
w.usr_mosi_highpart().clear_bit();
w.sio().bit(data_mode == DataMode::Single);
w.doutdin().clear_bit();
w.usr_miso().bit(!is_write && !no_mosi_miso);
w.usr_mosi().bit(is_write && !no_mosi_miso);
w.cs_hold().set_bit();
w.usr_dummy_idle().bit(dummy_idle);
w.usr_dummy().bit(dummy != 0);
w.usr_addr().bit(!address.is_none());
w.usr_command().bit(!cmd.is_none())
});
#[cfg(not(any(esp32, esp32s2)))]
reg_block.clk_gate().modify(|_, w| {
w.clk_en().set_bit();
w.mst_clk_active().set_bit();
w.mst_clk_sel().set_bit()
});
#[cfg(soc_has_pcr)]
crate::peripherals::PCR::regs()
.spi2_clkm_conf()
.modify(|_, w| unsafe { w.spi2_clkm_sel().bits(1) });
#[cfg(not(esp32))]
reg_block.misc().write(|w| unsafe { w.bits(0) });
reg_block.slave().write(|w| unsafe { w.bits(0) });
self.update();
self.set_up_common_phases(cmd, address, dummy);
Ok(())
}
fn set_up_common_phases(&self, cmd: Command, address: Address, dummy: u8) {
let reg_block = self.regs();
if !cmd.is_none() {
reg_block.user2().modify(|_, w| unsafe {
w.usr_command_bitlen().bits((cmd.width() - 1) as u8);
w.usr_command_value().bits(cmd.value())
});
}
if !address.is_none() {
reg_block
.user1()
.modify(|_, w| unsafe { w.usr_addr_bitlen().bits((address.width() - 1) as u8) });
let addr = address.value() << (32 - address.width());
#[cfg(not(esp32))]
reg_block
.addr()
.write(|w| unsafe { w.usr_addr_value().bits(addr) });
#[cfg(esp32)]
reg_block.addr().write(|w| unsafe { w.bits(addr) });
}
if dummy > 0 {
reg_block
.user1()
.modify(|_, w| unsafe { w.usr_dummy_cyclelen().bits(dummy - 1) });
}
}
fn update(&self) {
cfg_if::cfg_if! {
if #[cfg(not(any(esp32, esp32s2)))] {
let reg_block = self.regs();
reg_block.cmd().modify(|_, w| w.update().set_bit());
while reg_block.cmd().read().update().bit_is_set() {
}
} else {
}
}
}
fn configure_datalen(&self, rx_len_bytes: usize, tx_len_bytes: usize) {
let reg_block = self.regs();
let rx_len = rx_len_bytes as u32 * 8;
let tx_len = tx_len_bytes as u32 * 8;
let rx_len = rx_len.saturating_sub(1);
let tx_len = tx_len.saturating_sub(1);
cfg_if::cfg_if! {
if #[cfg(esp32)] {
let len = rx_len.max(tx_len);
reg_block
.mosi_dlen()
.write(|w| unsafe { w.usr_mosi_dbitlen().bits(len) });
reg_block
.miso_dlen()
.write(|w| unsafe { w.usr_miso_dbitlen().bits(len) });
} else if #[cfg(esp32s2)] {
reg_block
.mosi_dlen()
.write(|w| unsafe { w.usr_mosi_dbitlen().bits(tx_len) });
reg_block
.miso_dlen()
.write(|w| unsafe { w.usr_miso_dbitlen().bits(rx_len) });
} else {
reg_block
.ms_dlen()
.write(|w| unsafe { w.ms_data_bitlen().bits(rx_len.max(tx_len)) });
}
}
}
}
impl PartialEq for Info {
fn eq(&self, other: &Self) -> bool {
core::ptr::eq(self.register_block, other.register_block)
}
}
unsafe impl Sync for Info {}
for_each_spi_master! {
($peri:ident, $sys:ident, $sclk:ident [$($cs:ident),+] [$($sio:ident),*] $(, $is_qspi:tt)?) => {
impl Instance for crate::peripherals::$peri<'_> {
#[inline(always)]
fn parts(&self) -> (&'static Info, &'static State) {
#[handler]
#[ram]
fn irq_handler() {
handle_async(&INFO, &STATE)
}
static INFO: Info = Info {
register_block: crate::peripherals::$peri::regs(),
peripheral: crate::system::Peripheral::$sys,
async_handler: irq_handler,
sclk: OutputSignal::$sclk,
cs: &[$(OutputSignal::$cs),+],
sio_inputs: &[$(InputSignal::$sio),*],
sio_outputs: &[$(OutputSignal::$sio),*],
};
static STATE: State = State {
waker: AtomicWaker::new(),
pins: UnsafeCell::new(MaybeUninit::uninit()),
#[cfg(esp32)]
esp32_hack: Esp32Hack {
timing_miso_delay: Cell::new(None),
extra_dummy: Cell::new(0),
},
};
(&INFO, &STATE)
}
}
$(
$crate::ignore!($is_qspi);
impl QspiInstance for crate::peripherals::$peri<'_> {}
)?
};
}
impl QspiInstance for AnySpi<'_> {}
#[doc(hidden)]
pub struct State {
waker: AtomicWaker,
pins: UnsafeCell<MaybeUninit<SpiPinGuard>>,
#[cfg(esp32)]
esp32_hack: Esp32Hack,
}
impl State {
#[allow(
clippy::mut_from_ref,
reason = "Safety requirements ensure this is okay"
)]
unsafe fn pins(&self) -> &mut SpiPinGuard {
unsafe { (&mut *self.pins.get()).assume_init_mut() }
}
unsafe fn deinit(&self) {
unsafe {
let mut old = self.pins.get().replace(MaybeUninit::uninit());
old.assume_init_drop();
}
}
}
#[cfg(esp32)]
struct Esp32Hack {
timing_miso_delay: Cell<Option<u8>>,
extra_dummy: Cell<u8>,
}
unsafe impl Sync for State {}
#[ram]
fn handle_async(info: &'static Info, state: &'static State) {
let driver = Driver { info, state };
if driver.interrupts().contains(SpiInterrupt::TransferDone) {
driver.enable_listen(SpiInterrupt::TransferDone.into(), false);
state.waker.wake();
}
}
#[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>),
}
}
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);
}
}
struct SpiFuture<'a> {
driver: &'a Driver,
}
impl SpiFuture<'_> {
const EVENTS: EnumSet<SpiInterrupt> = enum_set!(SpiInterrupt::TransferDone);
}
impl Future for SpiFuture<'_> {
type Output = ();
#[cfg_attr(place_spi_master_driver_in_ram, ram)]
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
if self.driver.busy() {
self.driver.state.waker.register(cx.waker());
self.driver.enable_listen(Self::EVENTS, true);
Poll::Pending
} else {
self.driver.clear_interrupts(Self::EVENTS);
Poll::Ready(())
}
}
}
impl Drop for SpiFuture<'_> {
fn drop(&mut self) {
self.driver.enable_listen(Self::EVENTS, false);
}
}