ariel_os_esp/spi/main/
mod.rs1#![expect(unsafe_code)]
4
5use portable_atomic::{AtomicBool, Ordering};
6
7use ariel_os_embassy_common::{
8 impl_async_spibus_for_driver_enum,
9 spi::{BitOrder, Mode, main::Kilohertz},
10};
11use embassy_embedded_hal::adapter::{BlockingAsync, YieldingAsync};
12use esp_hal::{
13 gpio::{
14 self,
15 interconnect::{PeripheralInput, PeripheralOutput},
16 },
17 peripherals,
18 spi::master::Spi as InnerSpi,
19};
20
21#[cfg(any(
24 context = "esp32",
25 context = "esp32c2",
26 context = "esp32c3",
27 context = "esp32c6",
28 context = "esp32h2",
29 context = "esp32s2",
30 context = "esp32s3"
31))]
32const MAX_FREQUENCY: Kilohertz = Kilohertz::MHz(80);
33
34#[derive(Clone)]
36#[non_exhaustive]
37pub struct Config {
38 pub frequency: Frequency,
40 pub mode: Mode,
42 #[doc(hidden)]
43 pub bit_order: BitOrder,
44}
45
46impl Default for Config {
47 fn default() -> Self {
48 Self {
49 frequency: Frequency::F(Kilohertz::MHz(80)),
50 mode: Mode::Mode0,
51 bit_order: BitOrder::default(),
52 }
53 }
54}
55
56#[derive(Debug, Copy, Clone, PartialEq, Eq)]
59#[cfg_attr(feature = "defmt", derive(defmt::Format))]
60#[repr(u32)]
61pub enum Frequency {
62 F(Kilohertz),
64}
65
66ariel_os_embassy_common::impl_spi_from_frequency!();
67ariel_os_embassy_common::impl_spi_frequency_const_functions!(MAX_FREQUENCY);
68
69impl From<Frequency> for esp_hal::time::Rate {
70 fn from(freq: Frequency) -> Self {
71 match freq {
72 Frequency::F(kilohertz) => esp_hal::time::Rate::from_khz(kilohertz.raw()),
73 }
74 }
75}
76
77macro_rules! define_spi_drivers {
78 ($( $peripheral:ident ),* $(,)?) => {
79 $(
80 pub struct $peripheral {
82 spim: YieldingAsync<BlockingAsync<InnerSpi<'static, esp_hal::Blocking>>>,
83 }
84
85 paste::paste! {
87 static [< ACTIVE_ $peripheral >]: AtomicBool = AtomicBool::new(false);
88 }
89
90 impl $peripheral {
91 #[expect(clippy::new_ret_no_self)]
94 #[must_use]
95 pub fn new<
96 SCK: PeripheralOutput<'static>,
97 MISO: PeripheralInput<'static>,
98 MOSI: PeripheralOutput<'static>,
99 >(
100 sck_pin: impl $crate::IntoPeripheral<'static, SCK>,
101 miso_pin: impl $crate::IntoPeripheral<'static, MISO>,
102 mosi_pin: impl $crate::IntoPeripheral<'static, MOSI>,
103 config: Config,
104 ) -> Spi {
105 paste::paste! {
108 #[allow(dead_code)]
109 static [<PREVENT_MULTIPLE_ $peripheral>]: () = ();
110 }
111
112 let spi_config = esp_hal::spi::master::Config::default()
113 .with_frequency(config.frequency.into())
114 .with_mode(crate::spi::from_mode(config.mode))
115 .with_read_bit_order(crate::spi::from_bit_order(config.bit_order))
116 .with_write_bit_order(crate::spi::from_bit_order(config.bit_order));
117
118 paste::paste! {
120 if [< ACTIVE_ $peripheral >].swap(true, Ordering::AcqRel) {
121 panic!("SPI peripheral already initialized")
122 }
123 }
124
125 let spi_peripheral = unsafe { peripherals::$peripheral::steal() };
129
130 let spi = esp_hal::spi::master::Spi::new(
131 spi_peripheral,
132 spi_config,
133 )
134 .unwrap()
135 .with_sck(sck_pin.into_hal_peripheral())
136 .with_mosi(mosi_pin.into_hal_peripheral())
137 .with_miso(miso_pin.into_hal_peripheral())
138 .with_cs(gpio::NoPin); Spi::$peripheral(Self { spim: YieldingAsync::new(BlockingAsync::new(spi)) })
141 }
142 }
143
144 impl Drop for $peripheral {
145 fn drop(&mut self) {
146 paste::paste! {
147 [< ACTIVE_ $peripheral >].store(false, Ordering::Release);
148 }
149 }
150 }
151 )*
152
153 pub enum Spi {
155 $(
156 #[doc = concat!(stringify!($peripheral), " peripheral.")]
157 $peripheral($peripheral)
158 ),*
159 }
160
161 impl embedded_hal_async::spi::ErrorType for Spi {
162 type Error = esp_hal::spi::Error;
163 }
164
165 impl_async_spibus_for_driver_enum!(Spi, $( $peripheral ),*);
166 };
167}
168
169#[cfg(context = "esp32")]
172define_spi_drivers!(SPI2, SPI3);
173#[cfg(context = "esp32c2")]
174define_spi_drivers!(SPI2);
175#[cfg(context = "esp32c3")]
176define_spi_drivers!(SPI2);
177#[cfg(context = "esp32c6")]
178define_spi_drivers!(SPI2);
179#[cfg(context = "esp32h2")]
180define_spi_drivers!(SPI2);
181#[cfg(context = "esp32s2")]
182define_spi_drivers!(SPI2, SPI3);
183#[cfg(context = "esp32s3")]
184define_spi_drivers!(SPI2, SPI3);