#[cfg(all(feature = "blocking", any(feature = "RefCellBus", feature = "CriticalSectionBus")))]
use core::cell::RefCell;
#[cfg(all(feature = "async", any(feature = "CriticalSectionBus", feature = "AsyncBus")))]
use embassy_embedded_hal::shared_bus::asynch::spi::SpiDevice as EmbassySpiDevice;
#[cfg(all(feature = "async", feature = "CriticalSectionBus"))]
use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex;
#[cfg(all(feature = "async", feature = "AsyncBus"))]
use embassy_sync::blocking_mutex::raw::NoopRawMutex;
#[cfg(all(feature = "async", any(feature = "CriticalSectionBus", feature = "AsyncBus")))]
use embassy_sync::mutex::Mutex as AsyncMutex;
#[cfg(feature = "AtomicBus")]
use embedded_hal_bus::{spi::AtomicDevice, util::AtomicCell};
use esp_hal::gpio::Output;
use esp_hal::gpio::interconnect::{PeripheralInput, PeripheralOutput};
use esp_hal::peripherals::SPI2;
use esp_hal::spi::master::{Config as SpiConfig, Spi};
use esp_hal::time::Rate;
use static_cell::StaticCell;
#[cfg(all(feature = "async", feature = "blocking"))]
compile_error!("'async' і 'blocking' — взаємовиключні features");
#[cfg(not(any(feature = "async", feature = "blocking")))]
compile_error!("Either 'async' or 'blocking' feature must be enabled");
#[cfg(all(feature = "RefCellBus", feature = "CriticalSectionBus"))]
compile_error!("'RefCellBus' і 'CriticalSectionBus' — взаємовиключні");
#[cfg(all(feature = "RefCellBus", feature = "AtomicBus"))]
compile_error!("'RefCellBus' і 'AtomicBus' — взаємовиключні");
#[cfg(all(feature = "CriticalSectionBus", feature = "AtomicBus"))]
compile_error!("'CriticalSectionBus' і 'AtomicBus' — взаємовиключні");
#[cfg(all(feature = "AsyncBus", feature = "RefCellBus"))]
compile_error!("'AsyncBus' і 'RefCellBus' — взаємовиключні");
#[cfg(all(feature = "AsyncBus", feature = "CriticalSectionBus"))]
compile_error!("'AsyncBus' і 'CriticalSectionBus' — взаємовиключні");
#[cfg(all(feature = "AsyncBus", feature = "AtomicBus"))]
compile_error!("'AsyncBus' і 'AtomicBus' — взаємовиключні");
#[cfg(all(feature = "async", feature = "RefCellBus"))]
compile_error!("'RefCellBus' дозволено лише з feature 'blocking'");
#[cfg(all(feature = "blocking", feature = "AsyncBus"))]
compile_error!("'AsyncBus' дозволено лише з feature 'async'");
#[cfg(all(
feature = "async",
not(any(feature = "CriticalSectionBus", feature = "AtomicBus", feature = "AsyncBus"))
))]
compile_error!("Для 'async' потрібно ввімкнути 'CriticalSectionBus', 'AtomicBus' або 'AsyncBus'");
#[cfg(all(
feature = "blocking",
not(any(feature = "RefCellBus", feature = "CriticalSectionBus", feature = "AtomicBus"))
))]
compile_error!("Для 'blocking' потрібно ввімкнути 'RefCellBus', 'CriticalSectionBus' або 'AtomicBus'");
#[cfg(feature = "blocking")]
pub type SpiMode = esp_hal::Blocking;
#[cfg(feature = "async")]
pub type SpiMode = esp_hal::Async;
pub type SpiBusInner = Spi<'static, SpiMode>;
#[cfg(feature = "blocking")]
pub type InnerDelay = esp_hal::delay::Delay;
#[cfg(feature = "async")]
pub type InnerDelay = embassy_time::Delay;
#[cfg(all(feature = "blocking", feature = "RefCellBus"))]
pub type SpiBusWrapper = RefCell<SpiBusInner>;
#[cfg(all(feature = "blocking", feature = "CriticalSectionBus"))]
pub type SpiBusWrapper = critical_section::Mutex<RefCell<SpiBusInner>>;
#[cfg(all(feature = "blocking", feature = "AtomicBus"))]
pub type SpiBusWrapper = AtomicCell<SpiBusInner>;
#[cfg(all(feature = "async", feature = "CriticalSectionBus"))]
type SpiRawMutex = CriticalSectionRawMutex;
#[cfg(all(feature = "async", feature = "AsyncBus"))]
type SpiRawMutex = NoopRawMutex;
#[cfg(all(feature = "async", any(feature = "CriticalSectionBus", feature = "AsyncBus")))]
pub type SpiBusWrapper = AsyncMutex<SpiRawMutex, SpiBusInner>;
#[cfg(all(feature = "async", feature = "AtomicBus"))]
pub type SpiBusWrapper = AtomicCell<SpiBusInner>;
#[cfg(all(feature = "blocking", feature = "RefCellBus"))]
pub type SpiDev<'a> =
embedded_hal_bus::spi::RefCellDevice<'a, SpiBusInner, Output<'a>, InnerDelay>;
#[cfg(all(feature = "blocking", feature = "CriticalSectionBus"))]
pub type SpiDev<'a> =
embedded_hal_bus::spi::CriticalSectionDevice<'a, SpiBusInner, Output<'a>, InnerDelay>;
#[cfg(all(feature = "blocking", feature = "AtomicBus"))]
pub type SpiDev<'a> = AtomicDevice<'a, SpiBusInner, Output<'a>, InnerDelay>;
#[cfg(all(feature = "async", feature = "AsyncBus"))]
pub type SpiDev<'a> = EmbassySpiDevice<'a, SpiRawMutex, SpiBusInner, Output<'a>>;
#[cfg(all(feature = "async", feature = "AtomicBus"))]
pub type SpiDev<'a> = AtomicDevice<'a, SpiBusInner, Output<'a>, InnerDelay>;
static SPI_BUS: StaticCell<SpiBusWrapper> = StaticCell::new();
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum SpiInitError {
DeviceInitFailed,
BusInitFailed,
}
impl SpiInitError {
pub const fn as_str(self) -> &'static str {
match self {
Self::DeviceInitFailed => "SPI device init failed",
Self::BusInitFailed => "SPI bus init failed",
}
}
}
#[cfg(all(feature = "blocking", feature = "RefCellBus"))]
pub fn init_bus(spi: SpiBusInner) -> &'static SpiBusWrapper {
SPI_BUS.init(RefCell::new(spi))
}
#[cfg(all(feature = "blocking", feature = "CriticalSectionBus"))]
pub fn init_bus(spi: SpiBusInner) -> &'static SpiBusWrapper {
use critical_section::Mutex;
SPI_BUS.init(Mutex::new(RefCell::new(spi)))
}
#[cfg(all(feature = "blocking", feature = "AtomicBus"))]
pub fn init_bus(spi: SpiBusInner) -> &'static SpiBusWrapper {
SPI_BUS.init(AtomicCell::new(spi))
}
#[cfg(all(feature = "async", feature = "AsyncBus"))]
pub fn init_bus(spi: SpiBusInner) -> &'static SpiBusWrapper {
SPI_BUS.init(AsyncMutex::new(spi))
}
#[cfg(all(feature = "async", feature = "AtomicBus"))]
pub fn init_bus(spi: SpiBusInner) -> &'static SpiBusWrapper {
SPI_BUS.init(AtomicCell::new(spi))
}
pub fn make_device<'a>(
bus: &'a SpiBusWrapper,
cs: Output<'a>,
delay: InnerDelay,
) -> Result<SpiDev<'a>, SpiInitError> {
#[cfg(feature = "blocking")]
{
#[cfg(feature = "RefCellBus")]
{
embedded_hal_bus::spi::RefCellDevice::new(bus, cs, delay)
.map_err(|_| SpiInitError::DeviceInitFailed)
}
#[cfg(feature = "CriticalSectionBus")]
{
embedded_hal_bus::spi::CriticalSectionDevice::new(bus, cs, delay)
.map_err(|_| SpiInitError::DeviceInitFailed)
}
#[cfg(feature = "AtomicBus")]
{
AtomicDevice::new(bus, cs, delay).map_err(|_| SpiInitError::DeviceInitFailed)
}
}
#[cfg(feature = "async")]
{
#[cfg(any(feature = "CriticalSectionBus", feature = "AsyncBus"))]
{
let mut cs = cs;
let _ = delay;
cs.set_high().map_err(|_| SpiInitError::DeviceInitFailed)?;
Ok(EmbassySpiDevice::new(bus, cs))
}
#[cfg(feature = "AtomicBus")]
{
AtomicDevice::new(bus, cs, delay).map_err(|_| SpiInitError::DeviceInitFailed)
}
}
}
pub fn init_spi_master(
spi2: SPI2<'static>,
sck: impl PeripheralOutput<'static>,
mosi: impl PeripheralOutput<'static>,
miso: impl PeripheralInput<'static>,
frequency: fugit::HertzU32,
) -> Result<SpiBusInner, SpiInitError> {
let spi = Spi::new(
spi2,
SpiConfig::default().with_frequency(Rate::from_hz(frequency.raw())),
)
.map_err(|_| SpiInitError::BusInitFailed)?
.with_sck(sck)
.with_mosi(mosi)
.with_miso(miso);
#[cfg(feature = "async")]
let spi = spi.into_async();
Ok(spi)
}