ariel_os_nrf/spi/main/
mod.rs

1//! Provides support for the SPI communication bus in main mode.
2
3#![expect(unsafe_code)]
4
5use ariel_os_embassy_common::{
6    impl_async_spibus_for_driver_enum,
7    spi::{BitOrder, Mode},
8};
9
10use embassy_nrf::{
11    Peripheral, bind_interrupts,
12    gpio::Pin as GpioPin,
13    peripherals,
14    spim::{InterruptHandler, Spim},
15};
16
17/// SPI bus configuration.
18#[derive(Clone)]
19#[non_exhaustive]
20pub struct Config {
21    /// The frequency at which the bus should operate.
22    pub frequency: Frequency,
23    /// The SPI mode to use.
24    pub mode: Mode,
25    #[doc(hidden)]
26    pub bit_order: BitOrder,
27}
28
29impl Default for Config {
30    fn default() -> Self {
31        Self {
32            frequency: Frequency::_1M,
33            mode: Mode::Mode0,
34            bit_order: BitOrder::default(),
35        }
36    }
37}
38
39/// SPI bus frequency.
40// NOTE(hal): limited set of frequencies available.
41#[derive(Debug, Copy, Clone, PartialEq, Eq)]
42#[cfg_attr(feature = "defmt", derive(defmt::Format))]
43#[repr(u32)]
44pub enum Frequency {
45    /// 125 kHz.
46    _125k,
47    /// 250 kHz.
48    _250k,
49    /// 500 kHz.
50    _500k,
51    /// 1 MHz.
52    _1M,
53    /// 2 MHz.
54    _2M,
55    /// 4 MHz.
56    _4M,
57    /// 8 MHz.
58    _8M,
59    // FIXME(embassy): these frequencies are supported by hardware but do not seem supported by
60    // Embassy.
61    // #[cfg(any(context = "nrf52833", context = "nrf5340"))]
62    // _16M,
63    // #[cfg(any(context = "nrf52833", context = "nrf5340"))]
64    // _32M,
65}
66
67#[doc(hidden)]
68impl Frequency {
69    #[must_use]
70    pub const fn first() -> Self {
71        Self::_125k
72    }
73
74    #[must_use]
75    pub const fn last() -> Self {
76        Self::_8M
77    }
78
79    #[must_use]
80    pub const fn next(self) -> Option<Self> {
81        match self {
82            Self::_125k => Some(Self::_250k),
83            Self::_250k => Some(Self::_500k),
84            Self::_500k => Some(Self::_1M),
85            Self::_1M => Some(Self::_2M),
86            Self::_2M => Some(Self::_4M),
87            Self::_4M => Some(Self::_8M),
88            Self::_8M => None,
89        }
90    }
91
92    #[must_use]
93    pub const fn prev(self) -> Option<Self> {
94        match self {
95            Self::_125k => None,
96            Self::_250k => Some(Self::_125k),
97            Self::_500k => Some(Self::_250k),
98            Self::_1M => Some(Self::_500k),
99            Self::_2M => Some(Self::_1M),
100            Self::_4M => Some(Self::_2M),
101            Self::_8M => Some(Self::_4M),
102        }
103    }
104
105    #[must_use]
106    pub const fn khz(self) -> u32 {
107        match self {
108            Self::_125k => 125,
109            Self::_250k => 250,
110            Self::_500k => 500,
111            Self::_1M => 1000,
112            Self::_2M => 2000,
113            Self::_4M => 4000,
114            Self::_8M => 8000,
115        }
116    }
117}
118
119impl From<ariel_os_embassy_common::spi::main::Frequency> for Frequency {
120    fn from(freq: ariel_os_embassy_common::spi::main::Frequency) -> Self {
121        match freq {
122            ariel_os_embassy_common::spi::main::Frequency::_125k => Self::_125k,
123            ariel_os_embassy_common::spi::main::Frequency::_250k => Self::_250k,
124            ariel_os_embassy_common::spi::main::Frequency::_500k => Self::_500k,
125            ariel_os_embassy_common::spi::main::Frequency::_1M => Self::_1M,
126            ariel_os_embassy_common::spi::main::Frequency::_2M => Self::_2M,
127            ariel_os_embassy_common::spi::main::Frequency::_4M => Self::_4M,
128            ariel_os_embassy_common::spi::main::Frequency::_8M => Self::_8M,
129        }
130    }
131}
132
133impl From<Frequency> for embassy_nrf::spim::Frequency {
134    fn from(freq: Frequency) -> Self {
135        match freq {
136            Frequency::_125k => embassy_nrf::spim::Frequency::K125,
137            Frequency::_250k => embassy_nrf::spim::Frequency::K250,
138            Frequency::_500k => embassy_nrf::spim::Frequency::K500,
139            Frequency::_1M => embassy_nrf::spim::Frequency::M1,
140            Frequency::_2M => embassy_nrf::spim::Frequency::M2,
141            Frequency::_4M => embassy_nrf::spim::Frequency::M4,
142            Frequency::_8M => embassy_nrf::spim::Frequency::M8,
143        }
144    }
145}
146
147macro_rules! define_spi_drivers {
148    ($( $interrupt:ident => $peripheral:ident ),* $(,)?) => {
149        $(
150            /// Peripheral-specific SPI driver.
151            pub struct $peripheral {
152                spim: Spim<'static, peripherals::$peripheral>,
153            }
154
155            impl $peripheral {
156                /// Returns a driver implementing [`embedded_hal_async::spi::SpiBus`] for this SPI
157                /// peripheral.
158                #[expect(clippy::new_ret_no_self)]
159                #[must_use]
160                pub fn new(
161                    sck_pin: impl Peripheral<P: GpioPin> + 'static,
162                    miso_pin: impl Peripheral<P: GpioPin> + 'static,
163                    mosi_pin: impl Peripheral<P: GpioPin> + 'static,
164                    config: Config,
165                ) -> Spi {
166                    let mut spi_config = embassy_nrf::spim::Config::default();
167                    spi_config.frequency = config.frequency.into();
168                    spi_config.mode = crate::spi::from_mode(config.mode);
169                    spi_config.bit_order = crate::spi::from_bit_order(config.bit_order);
170
171                    bind_interrupts!(
172                        struct Irqs {
173                            $interrupt => InterruptHandler<peripherals::$peripheral>;
174                        }
175                    );
176
177                    // Make this struct a compile-time-enforced singleton: having multiple statics
178                    // defined with the same name would result in a compile-time error.
179                    paste::paste! {
180                        #[allow(dead_code)]
181                        static [<PREVENT_MULTIPLE_ $peripheral>]: () = ();
182                    }
183
184                    // FIXME(safety): enforce that the init code indeed has run
185                    // SAFETY: this struct being a singleton prevents us from stealing the
186                    // peripheral multiple times.
187                    let spim_peripheral = unsafe { peripherals::$peripheral::steal() };
188
189                    let spim = Spim::new(
190                        spim_peripheral,
191                        Irqs,
192                        sck_pin,
193                        miso_pin,
194                        mosi_pin,
195                        spi_config,
196                    );
197
198                    Spi::$peripheral(Self { spim })
199                }
200            }
201        )*
202
203        /// Peripheral-agnostic driver.
204        pub enum Spi {
205            $(
206                #[doc = concat!(stringify!($peripheral), " peripheral.")]
207                $peripheral($peripheral)
208            ),*
209        }
210
211        impl embedded_hal_async::spi::ErrorType for Spi {
212            type Error = embassy_nrf::spim::Error;
213        }
214
215        impl_async_spibus_for_driver_enum!(Spi, $( $peripheral ),*);
216    };
217}
218
219// Define a driver per peripheral
220#[cfg(context = "nrf52833")]
221define_spi_drivers!(
222    // FIXME: arbitrary selected peripherals
223    // SPIM0_SPIS0_TWIM0_TWIS0_SPI0_TWI0 => TWISPI0,
224    // SPIM1_SPIS1_TWIM1_TWIS1_SPI1_TWI1 => TWISPI1,
225    // SPIM2_SPIS2_SPI2 => SPI2,
226    SPIM3 => SPI3,
227);
228#[cfg(context = "nrf52840")]
229define_spi_drivers!(
230    // FIXME: arbitrary selected peripherals
231    // SPIM0_SPIS0_TWIM0_TWIS0_SPI0_TWI0 => TWISPI0,
232    // SPIM1_SPIS1_TWIM1_TWIS1_SPI1_TWI1 => TWISPI1,
233    // SPIM2_SPIS2_SPI2 => SPI2,
234    SPIM3 => SPI3,
235);
236// FIXME: arbitrary selected peripherals
237#[cfg(context = "nrf5340")]
238define_spi_drivers!(
239    SERIAL2 => SERIAL2,
240    // Used by UART
241    // SERIAL3 => SERIAL3,
242);
243// FIXME: arbitrary selected peripherals
244#[cfg(context = "nrf91")]
245define_spi_drivers!(
246    SERIAL2 => SERIAL2,
247    // Used by UART
248    // SERIAL3 => SERIAL3,
249);