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},
10};
11
12use embassy_nrf::{
13 bind_interrupts,
14 gpio::Pin as GpioPin,
15 peripherals,
16 spim::{InterruptHandler, Spim},
17};
18
19#[derive(Clone)]
21#[non_exhaustive]
22pub struct Config {
23 pub frequency: Frequency,
25 pub mode: Mode,
27 #[doc(hidden)]
28 pub bit_order: BitOrder,
29}
30
31impl Default for Config {
32 fn default() -> Self {
33 Self {
34 frequency: Frequency::_1M,
35 mode: Mode::Mode0,
36 bit_order: BitOrder::default(),
37 }
38 }
39}
40
41#[derive(Debug, Copy, Clone, PartialEq, Eq)]
44#[cfg_attr(feature = "defmt", derive(defmt::Format))]
45#[repr(u32)]
46pub enum Frequency {
47 _125k,
49 _250k,
51 _500k,
53 _1M,
55 _2M,
57 _4M,
59 _8M,
61 }
68
69#[doc(hidden)]
70impl Frequency {
71 #[must_use]
72 pub const fn first() -> Self {
73 Self::_125k
74 }
75
76 #[must_use]
77 pub const fn last() -> Self {
78 Self::_8M
79 }
80
81 #[must_use]
82 pub const fn next(self) -> Option<Self> {
83 match self {
84 Self::_125k => Some(Self::_250k),
85 Self::_250k => Some(Self::_500k),
86 Self::_500k => Some(Self::_1M),
87 Self::_1M => Some(Self::_2M),
88 Self::_2M => Some(Self::_4M),
89 Self::_4M => Some(Self::_8M),
90 Self::_8M => None,
91 }
92 }
93
94 #[must_use]
95 pub const fn prev(self) -> Option<Self> {
96 match self {
97 Self::_125k => None,
98 Self::_250k => Some(Self::_125k),
99 Self::_500k => Some(Self::_250k),
100 Self::_1M => Some(Self::_500k),
101 Self::_2M => Some(Self::_1M),
102 Self::_4M => Some(Self::_2M),
103 Self::_8M => Some(Self::_4M),
104 }
105 }
106
107 #[must_use]
108 pub const fn khz(self) -> u32 {
109 match self {
110 Self::_125k => 125,
111 Self::_250k => 250,
112 Self::_500k => 500,
113 Self::_1M => 1000,
114 Self::_2M => 2000,
115 Self::_4M => 4000,
116 Self::_8M => 8000,
117 }
118 }
119}
120
121impl From<ariel_os_embassy_common::spi::main::Frequency> for Frequency {
122 fn from(freq: ariel_os_embassy_common::spi::main::Frequency) -> Self {
123 match freq {
124 ariel_os_embassy_common::spi::main::Frequency::_125k => Self::_125k,
125 ariel_os_embassy_common::spi::main::Frequency::_250k => Self::_250k,
126 ariel_os_embassy_common::spi::main::Frequency::_500k => Self::_500k,
127 ariel_os_embassy_common::spi::main::Frequency::_1M => Self::_1M,
128 ariel_os_embassy_common::spi::main::Frequency::_2M => Self::_2M,
129 ariel_os_embassy_common::spi::main::Frequency::_4M => Self::_4M,
130 ariel_os_embassy_common::spi::main::Frequency::_8M => Self::_8M,
131 }
132 }
133}
134
135impl From<Frequency> for embassy_nrf::spim::Frequency {
136 fn from(freq: Frequency) -> Self {
137 match freq {
138 Frequency::_125k => embassy_nrf::spim::Frequency::K125,
139 Frequency::_250k => embassy_nrf::spim::Frequency::K250,
140 Frequency::_500k => embassy_nrf::spim::Frequency::K500,
141 Frequency::_1M => embassy_nrf::spim::Frequency::M1,
142 Frequency::_2M => embassy_nrf::spim::Frequency::M2,
143 Frequency::_4M => embassy_nrf::spim::Frequency::M4,
144 Frequency::_8M => embassy_nrf::spim::Frequency::M8,
145 }
146 }
147}
148
149macro_rules! define_spi_drivers {
150 ($( $interrupt:ident => $peripheral:ident ),* $(,)?) => {
151 $(
152 pub struct $peripheral {
154 spim: Spim<'static>,
155 }
156
157 paste::paste! {
159 static [< ACTIVE_ $peripheral >]: AtomicBool = AtomicBool::new(false);
160 }
161
162 impl $peripheral {
163 #[expect(clippy::new_ret_no_self)]
166 #[must_use]
167 pub fn new<SCK: GpioPin, MISO: GpioPin, MOSI: GpioPin>(
168 sck_pin: impl $crate::IntoPeripheral<'static, SCK>,
169 miso_pin: impl $crate::IntoPeripheral<'static, MISO>,
170 mosi_pin: impl $crate::IntoPeripheral<'static, MOSI>,
171 config: Config,
172 ) -> Spi {
173 let mut spi_config = embassy_nrf::spim::Config::default();
174 spi_config.frequency = config.frequency.into();
175 spi_config.mode = crate::spi::from_mode(config.mode);
176 spi_config.bit_order = crate::spi::from_bit_order(config.bit_order);
177
178 bind_interrupts!(
179 struct Irqs {
180 $interrupt => InterruptHandler<peripherals::$peripheral>;
181 }
182 );
183
184 paste::paste! {
187 #[allow(dead_code)]
188 static [<PREVENT_MULTIPLE_ $peripheral>]: () = ();
189 }
190
191 paste::paste! {
193 if [< ACTIVE_ $peripheral >].swap(true, Ordering::AcqRel) {
194 panic!("SPI peripheral already initialized")
195 }
196 }
197
198 let spim_peripheral = unsafe { peripherals::$peripheral::steal() };
202
203 let spim = Spim::new(
204 spim_peripheral,
205 Irqs,
206 sck_pin.into_hal_peripheral(),
207 miso_pin.into_hal_peripheral(),
208 mosi_pin.into_hal_peripheral(),
209 spi_config,
210 );
211
212 Spi::$peripheral(Self { spim })
213 }
214 }
215
216 impl Drop for $peripheral {
217 fn drop(&mut self) {
218 paste::paste! {
219 [< ACTIVE_ $peripheral >].store(false, Ordering::Release);
220 }
221 }
222 }
223 )*
224
225 pub enum Spi {
227 $(
228 #[doc = concat!(stringify!($peripheral), " peripheral.")]
229 $peripheral($peripheral)
230 ),*
231 }
232
233 impl embedded_hal_async::spi::ErrorType for Spi {
234 type Error = embassy_nrf::spim::Error;
235 }
236
237 impl_async_spibus_for_driver_enum!(Spi, $( $peripheral ),*);
238 };
239}
240
241#[cfg(context = "nrf52833")]
243define_spi_drivers!(
244 SPIM3 => SPI3,
249);
250#[cfg(context = "nrf52840")]
251define_spi_drivers!(
252 SPIM3 => SPI3,
257);
258#[cfg(context = "nrf5340-app")]
260define_spi_drivers!(
261 SERIAL2 => SERIAL2,
262 );
265#[cfg(context = "nrf91")]
267define_spi_drivers!(
268 SERIAL2 => SERIAL2,
269 );