ariel_os_nrf/spi/main/
mod.rs

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