1#![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 embassy_stm32::{
13 gpio,
14 mode::Blocking,
15 peripherals,
16 spi::{MisoPin, MosiPin, SckPin, Spi as InnerSpi},
17 time::Hertz,
18};
19
20#[cfg(context = "stm32c031c6")]
24const MAX_FREQUENCY: Kilohertz = Kilohertz::MHz(24);
25#[cfg(any(context = "stm32f303cb", context = "stm32f303re"))]
26const MAX_FREQUENCY: Kilohertz = Kilohertz::MHz(18);
27#[cfg(context = "stm32f401re")]
28const MAX_FREQUENCY: Kilohertz = Kilohertz::MHz(21);
29#[cfg(context = "stm32f411re")]
30const MAX_FREQUENCY: Kilohertz = Kilohertz::MHz(25);
31#[cfg(context = "stm32g431rb")]
32const MAX_FREQUENCY: Kilohertz = Kilohertz::MHz(75);
33#[cfg(any(context = "stm32h755zi", context = "stm32h753zi"))]
34const MAX_FREQUENCY: Kilohertz = Kilohertz::MHz(150);
35#[cfg(context = "stm32l475vg")]
36const MAX_FREQUENCY: Kilohertz = Kilohertz::MHz(40);
37#[cfg(any(context = "stm32u073kc", context = "stm32u083mc"))]
38const MAX_FREQUENCY: Kilohertz = Kilohertz::MHz(32);
39#[cfg(context = "stm32u585ai")]
42const MAX_FREQUENCY: Kilohertz = Kilohertz::MHz(80);
43#[cfg(context = "stm32wb55rg")]
44const MAX_FREQUENCY: Kilohertz = Kilohertz::MHz(32);
45
46#[derive(Clone)]
48#[non_exhaustive]
49pub struct Config {
50 pub frequency: Frequency,
52 pub mode: Mode,
54 #[doc(hidden)]
55 pub bit_order: BitOrder,
56}
57
58impl Default for Config {
59 fn default() -> Self {
60 Self {
61 frequency: Frequency::F(Kilohertz::MHz(1)),
62 mode: Mode::Mode0,
63 bit_order: BitOrder::default(),
64 }
65 }
66}
67
68#[derive(Debug, Copy, Clone, PartialEq, Eq)]
70#[cfg_attr(feature = "defmt", derive(defmt::Format))]
71#[repr(u32)]
72pub enum Frequency {
73 F(Kilohertz),
75}
76
77impl From<Frequency> for Hertz {
78 fn from(freq: Frequency) -> Self {
79 match freq {
80 Frequency::F(kilohertz) => Hertz::khz(kilohertz.to_kHz()),
81 }
82 }
83}
84
85ariel_os_embassy_common::impl_spi_from_frequency!();
86ariel_os_embassy_common::impl_spi_frequency_const_functions!(MAX_FREQUENCY);
87
88macro_rules! define_spi_drivers {
89 ($( $interrupt:ident => $peripheral:ident ),* $(,)?) => {
90 $(
91 pub struct $peripheral {
93 spim: YieldingAsync<BlockingAsync<InnerSpi<'static, Blocking>>>,
94 }
95
96 paste::paste! {
98 static [< ACTIVE_ $peripheral >]: AtomicBool = AtomicBool::new(false);
99 }
100
101 impl $peripheral {
102 #[expect(clippy::new_ret_no_self)]
105 #[must_use]
106 pub fn new<
107 SCK: SckPin<peripherals::$peripheral>,
108 MISO: MisoPin<peripherals::$peripheral>,
109 MOSI: MosiPin<peripherals::$peripheral>,
110 >(
111 sck_pin: impl $crate::IntoPeripheral<'static, SCK>,
112 miso_pin: impl $crate::IntoPeripheral<'static, MISO>,
113 mosi_pin: impl $crate::IntoPeripheral<'static, MOSI>,
114 config: Config,
115 ) -> Spi {
116 paste::paste! {
119 #[allow(dead_code)]
120 static [<PREVENT_MULTIPLE_ $peripheral>]: () = ();
121 }
122
123 let mut spi_config = embassy_stm32::spi::Config::default();
124 spi_config.frequency = config.frequency.into();
125 spi_config.mode = crate::spi::from_mode(config.mode);
126 spi_config.bit_order = crate::spi::from_bit_order(config.bit_order);
127 spi_config.miso_pull = gpio::Pull::None;
128
129 paste::paste! {
131 if [< ACTIVE_ $peripheral >].swap(true, Ordering::AcqRel) {
132 panic!("SPI peripheral already initialized")
133 }
134 }
135
136 let spim_peripheral = unsafe { peripherals::$peripheral::steal() };
140
141 let spim = InnerSpi::new_blocking(
143 spim_peripheral,
144 sck_pin.into_hal_peripheral(),
145 mosi_pin.into_hal_peripheral(),
146 miso_pin.into_hal_peripheral(),
147 spi_config,
148 );
149
150 Spi::$peripheral(Self { spim: YieldingAsync::new(BlockingAsync::new(spim)) })
151 }
152 }
153
154 impl Drop for $peripheral {
155 fn drop(&mut self) {
156 paste::paste! {
157 [< ACTIVE_ $peripheral >].store(false, Ordering::Release);
158 }
159 }
160 }
161 )*
162
163 pub enum Spi {
165 $(
166 #[doc = concat!(stringify!($peripheral), " peripheral.")]
167 $peripheral($peripheral)
168 ),*
169 }
170
171 impl embedded_hal_async::spi::ErrorType for Spi {
172 type Error = embassy_stm32::spi::Error;
173 }
174
175 impl_async_spibus_for_driver_enum!(Spi, $( $peripheral ),*);
176 };
177}
178
179#[cfg(context = "stm32c031c6")]
181define_spi_drivers!(
182 SPI1 => SPI1,
183);
184#[cfg(context = "stm32f303cb")]
185define_spi_drivers!(
186 SPI1 => SPI1,
187 SPI2 => SPI2,
188 SPI3 => SPI3,
189);
190#[cfg(context = "stm32f303re")]
191define_spi_drivers!(
192 SPI1 => SPI1,
193 SPI2 => SPI2,
194 SPI3 => SPI3,
195 );
197#[cfg(context = "stm32f401re")]
198define_spi_drivers!(
199 SPI1 => SPI1,
200 SPI2 => SPI2,
201 SPI3 => SPI3,
202);
203#[cfg(context = "stm32f411re")]
204define_spi_drivers!(
205 SPI1 => SPI1,
206 SPI2 => SPI2,
207 SPI3 => SPI3,
208 SPI4 => SPI4,
209 SPI5 => SPI5,
210);
211#[cfg(context = "stm32g431rb")]
212define_spi_drivers!(
213 SPI1 => SPI1,
214 SPI2 => SPI2,
215 SPI3 => SPI3,
216);
217#[cfg(any(context = "stm32h755zi", context = "stm32h753zi"))]
218define_spi_drivers!(
219 SPI1 => SPI1,
220 SPI2 => SPI2,
221 SPI3 => SPI3,
222 SPI4 => SPI4,
223 SPI5 => SPI5,
224 SPI6 => SPI6,
225);
226#[cfg(context = "stm32l475vg")]
227define_spi_drivers!(
228 SPI1 => SPI1,
229 SPI2 => SPI2,
230 SPI3 => SPI3,
231);
232#[cfg(any(context = "stm32u073kc", context = "stm32u083mc"))]
233define_spi_drivers!(
234 SPI1 => SPI1,
235 );
237#[cfg(context = "stm32u585ai")]
238define_spi_drivers!(
239 SPI1 => SPI1,
240 SPI2 => SPI2,
241 SPI3 => SPI3,
242);
243#[cfg(context = "stm32wb55rg")]
244define_spi_drivers!(
245 SPI1 => SPI1,
246 SPI2 => SPI2,
247);