ariel_os_rp/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::{Mode, main::Kilohertz},
10};
11use embassy_embedded_hal::adapter::{BlockingAsync, YieldingAsync};
12use embassy_rp::{
13 peripherals,
14 spi::{Blocking, ClkPin, MisoPin, MosiPin, Spi as InnerSpi},
15};
16
17#[cfg(context = "rp2040")]
20const MAX_FREQUENCY: Kilohertz = Kilohertz::kHz(62_500);
21#[cfg(context = "rp235xa")]
22const MAX_FREQUENCY: Kilohertz = Kilohertz::kHz(70_500);
23
24#[derive(Clone)]
26#[non_exhaustive]
27pub struct Config {
28 pub frequency: Frequency,
30 pub mode: Mode,
32}
33
34impl Default for Config {
35 fn default() -> Self {
36 Self {
37 frequency: Frequency::F(Kilohertz::MHz(1)),
38 mode: Mode::Mode0,
39 }
40 }
41}
42
43#[derive(Debug, Copy, Clone, PartialEq, Eq)]
45#[cfg_attr(feature = "defmt", derive(defmt::Format))]
46#[repr(u32)]
47pub enum Frequency {
48 F(Kilohertz),
50}
51
52ariel_os_embassy_common::impl_spi_from_frequency!();
53ariel_os_embassy_common::impl_spi_frequency_const_functions!(MAX_FREQUENCY);
54
55impl Frequency {
56 fn as_hz(self) -> u32 {
57 match self {
58 Self::F(kilohertz) => kilohertz.to_Hz(),
59 }
60 }
61}
62
63macro_rules! define_spi_drivers {
64 ($( $peripheral:ident ),* $(,)?) => {
65 $(
66 pub struct $peripheral {
68 spim: YieldingAsync<BlockingAsync<InnerSpi<'static, peripherals::$peripheral, Blocking>>>,
69 }
70
71 paste::paste! {
73 static [< ACTIVE_ $peripheral >]: AtomicBool = AtomicBool::new(false);
74 }
75
76 impl $peripheral {
77 #[expect(clippy::new_ret_no_self)]
80 #[must_use]
81 pub fn new<
82 SCK: ClkPin<peripherals::$peripheral>,
83 MISO: MisoPin<peripherals::$peripheral>,
84 MOSI: MosiPin<peripherals::$peripheral>
85 >(
86 sck_pin: impl $crate::IntoPeripheral<'static, SCK>,
87 miso_pin: impl $crate::IntoPeripheral<'static, MISO>,
88 mosi_pin: impl $crate::IntoPeripheral<'static, MOSI>,
89 config: Config,
90 ) -> Spi {
91 paste::paste! {
94 #[allow(dead_code)]
95 static [<PREVENT_MULTIPLE_ $peripheral>]: () = ();
96 }
97
98 let (pol, phase) = crate::spi::from_mode(config.mode);
99
100 let mut spi_config = embassy_rp::spi::Config::default();
101 spi_config.frequency = config.frequency.as_hz();
102 spi_config.polarity = pol;
103 spi_config.phase = phase;
104
105 paste::paste! {
107 if [< ACTIVE_ $peripheral >].swap(true, Ordering::AcqRel) {
108 panic!("SPI peripheral already initialized")
109 }
110 }
111
112 let spi_peripheral = unsafe { peripherals::$peripheral::steal() };
116
117 let spi = InnerSpi::new_blocking(
119 spi_peripheral,
120 sck_pin.into_hal_peripheral(),
121 mosi_pin.into_hal_peripheral(),
122 miso_pin.into_hal_peripheral(),
123 spi_config,
124 );
125
126 Spi::$peripheral(Self { spim: YieldingAsync::new(BlockingAsync::new(spi)) })
127 }
128 }
129
130 impl Drop for $peripheral {
131 fn drop(&mut self) {
132 paste::paste! {
133 [< ACTIVE_ $peripheral >].store(false, Ordering::Release);
134 }
135 }
136 }
137 )*
138
139 pub enum Spi {
141 $(
142 #[doc = concat!(stringify!($peripheral), " peripheral.")]
143 $peripheral($peripheral)
144 ),*
145 }
146
147 impl embedded_hal_async::spi::ErrorType for Spi {
148 type Error = embassy_rp::spi::Error;
149 }
150
151 impl_async_spibus_for_driver_enum!(Spi, $( $peripheral ),*);
152 };
153}
154
155define_spi_drivers!(SPI0, SPI1);