1#[cfg(all(feature = "blocking", any(feature = "RefCellBus", feature = "CriticalSectionBus")))]
2use core::cell::RefCell;
3#[cfg(all(feature = "async", feature = "AsyncBus"))]
4use embassy_embedded_hal::shared_bus::asynch::spi::SpiDevice as EmbassySpiDevice;
5#[cfg(all(feature = "async", feature = "AsyncBus"))]
6use embassy_sync::blocking_mutex::raw::NoopRawMutex;
7#[cfg(all(feature = "async", feature = "AsyncBus"))]
8use embassy_sync::mutex::Mutex as AsyncMutex;
9#[cfg(feature = "AtomicBus")]
10use embedded_hal_bus::{spi::AtomicDevice, util::AtomicCell};
11use esp_hal::gpio::Output;
12use esp_hal::gpio::interconnect::{PeripheralInput, PeripheralOutput};
13use esp_hal::peripherals::SPI2;
14use esp_hal::spi::master::{Config as SpiConfig, Spi};
15use esp_hal::time::Rate;
16use static_cell::StaticCell;
17
18#[cfg(all(feature = "async", feature = "blocking"))]
21compile_error!("'async' і 'blocking' — взаємовиключні features");
22#[cfg(not(any(feature = "async", feature = "blocking")))]
23compile_error!("Either 'async' or 'blocking' feature must be enabled");
24#[cfg(all(feature = "RefCellBus", feature = "CriticalSectionBus"))]
25compile_error!("'RefCellBus' і 'CriticalSectionBus' — взаємовиключні");
26#[cfg(all(feature = "RefCellBus", feature = "AtomicBus"))]
27compile_error!("'RefCellBus' і 'AtomicBus' — взаємовиключні");
28#[cfg(all(feature = "CriticalSectionBus", feature = "AtomicBus"))]
29compile_error!("'CriticalSectionBus' і 'AtomicBus' — взаємовиключні");
30#[cfg(all(feature = "AsyncBus", feature = "RefCellBus"))]
31compile_error!("'AsyncBus' і 'RefCellBus' — взаємовиключні");
32#[cfg(all(feature = "AsyncBus", feature = "CriticalSectionBus"))]
33compile_error!("'AsyncBus' і 'CriticalSectionBus' — взаємовиключні");
34#[cfg(all(feature = "AsyncBus", feature = "AtomicBus"))]
35compile_error!("'AsyncBus' і 'AtomicBus' — взаємовиключні");
36#[cfg(all(feature = "async", feature = "RefCellBus"))]
37compile_error!("'RefCellBus' дозволено лише з feature 'blocking'");
38#[cfg(all(feature = "blocking", feature = "AsyncBus"))]
39compile_error!("'AsyncBus' дозволено лише з feature 'async'");
40#[cfg(all(feature = "async", not(feature = "AsyncBus")))]
41compile_error!("Для 'async' потрібно ввімкнути 'AsyncBus'");
42#[cfg(all(
43 feature = "blocking",
44 not(any(feature = "RefCellBus", feature = "CriticalSectionBus", feature = "AtomicBus"))
45))]
46compile_error!("Для 'blocking' потрібно ввімкнути 'RefCellBus', 'CriticalSectionBus' або 'AtomicBus'");
47
48#[cfg(feature = "blocking")]
55pub type SpiMode = esp_hal::Blocking;
56#[cfg(feature = "async")]
57pub type SpiMode = esp_hal::Async;
58
59pub type SpiBusInner = Spi<'static, SpiMode>;
62
63#[cfg(feature = "blocking")]
68pub type InnerDelay = esp_hal::delay::Delay;
69#[cfg(feature = "async")]
70pub type InnerDelay = embassy_time::Delay;
71
72#[cfg(all(feature = "blocking", feature = "RefCellBus"))]
87pub type SpiBusWrapper = RefCell<SpiBusInner>;
88#[cfg(all(feature = "blocking", feature = "CriticalSectionBus"))]
89pub type SpiBusWrapper = critical_section::Mutex<RefCell<SpiBusInner>>;
90#[cfg(all(feature = "blocking", feature = "AtomicBus"))]
91pub type SpiBusWrapper = AtomicCell<SpiBusInner>;
92#[cfg(all(feature = "async", feature = "AsyncBus"))]
93pub type SpiBusWrapper = AsyncMutex<NoopRawMutex, SpiBusInner>;
94
95#[cfg(all(feature = "blocking", feature = "RefCellBus"))]
106pub type SpiDev<'a> =
107 embedded_hal_bus::spi::RefCellDevice<'a, SpiBusInner, Output<'a>, InnerDelay>;
108#[cfg(all(feature = "blocking", feature = "CriticalSectionBus"))]
109pub type SpiDev<'a> =
110 embedded_hal_bus::spi::CriticalSectionDevice<'a, SpiBusInner, Output<'a>, InnerDelay>;
111#[cfg(all(feature = "blocking", feature = "AtomicBus"))]
112pub type SpiDev<'a> = AtomicDevice<'a, SpiBusInner, Output<'a>, InnerDelay>;
113#[cfg(all(feature = "async", feature = "AsyncBus"))]
114pub type SpiDev<'a> = EmbassySpiDevice<'a, NoopRawMutex, SpiBusInner, Output<'a>>;
115
116static SPI_BUS: StaticCell<SpiBusWrapper> = StaticCell::new();
117
118#[derive(Debug, Clone, Copy, PartialEq, Eq)]
122pub enum SpiInitError {
123 DeviceInitFailed,
125 BusInitFailed,
127}
128
129impl SpiInitError {
130 pub const fn as_str(self) -> &'static str {
132 match self {
133 Self::DeviceInitFailed => "SPI device init failed",
134 Self::BusInitFailed => "SPI bus init failed",
135 }
136 }
137}
138
139
140
141#[cfg(all(feature = "blocking", feature = "RefCellBus"))]
164pub fn init_bus(spi: SpiBusInner) -> &'static SpiBusWrapper {
165 SPI_BUS.init(RefCell::new(spi))
166}
167#[cfg(all(feature = "blocking", feature = "CriticalSectionBus"))]
168pub fn init_bus(spi: SpiBusInner) -> &'static SpiBusWrapper {
169 use critical_section::Mutex;
170 SPI_BUS.init(Mutex::new(RefCell::new(spi)))
171}
172#[cfg(all(feature = "blocking", feature = "AtomicBus"))]
173pub fn init_bus(spi: SpiBusInner) -> &'static SpiBusWrapper {
174 SPI_BUS.init(AtomicCell::new(spi))
175}
176#[cfg(all(feature = "async", feature = "AsyncBus"))]
177pub fn init_bus(spi: SpiBusInner) -> &'static SpiBusWrapper {
178 SPI_BUS.init(AsyncMutex::new(spi))
179}
180
181pub fn make_device<'a>(
208 bus: &'a SpiBusWrapper,
209 cs: Output<'a>,
210 delay: InnerDelay,
211) -> Result<SpiDev<'a>, SpiInitError> {
212 #[cfg(feature = "blocking")]
213 {
214 #[cfg(feature = "RefCellBus")]
215 {
216 embedded_hal_bus::spi::RefCellDevice::new(bus, cs, delay)
217 .map_err(|_| SpiInitError::DeviceInitFailed)
218 }
219 #[cfg(feature = "CriticalSectionBus")]
220 {
221 embedded_hal_bus::spi::CriticalSectionDevice::new(bus, cs, delay)
222 .map_err(|_| SpiInitError::DeviceInitFailed)
223 }
224 #[cfg(feature = "AtomicBus")]
225 {
226 AtomicDevice::new(bus, cs, delay).map_err(|_| SpiInitError::DeviceInitFailed)
227 }
228 }
229 #[cfg(all(feature = "async", feature = "AsyncBus"))]
230 {
231 let _ = delay;
232 Ok(EmbassySpiDevice::new(bus, cs))
233 }
234}
235
236pub fn init_spi_master(
279 spi2: SPI2<'static>,
280 sck: impl PeripheralOutput<'static>,
281 mosi: impl PeripheralOutput<'static>,
282 miso: impl PeripheralInput<'static>,
283 frequency: fugit::HertzU32,
284) -> Result<SpiBusInner, SpiInitError> {
285 let spi = Spi::new(
286 spi2,
287 SpiConfig::default().with_frequency(Rate::from_hz(frequency.raw())),
288 )
289 .map_err(|_| SpiInitError::BusInitFailed)?
290 .with_sck(sck)
291 .with_mosi(mosi)
292 .with_miso(miso);
293
294 #[cfg(feature = "async")]
295 let spi = spi.into_async();
296
297 Ok(spi)
298}