1use core::{fmt, marker::PhantomData, ops::Deref};
10
11use crate::hal::blocking::spi;
12use crate::hal::spi::FullDuplex;
13pub use crate::hal::spi::{Mode, Phase, Polarity};
14#[cfg(feature = "gpio-f303e")]
15use crate::pac::SPI4;
16use crate::pac::{
17 self, spi1,
18 spi1::cr2::{DS_A, FRXTH_A},
19 Interrupt, SPI1, SPI2, SPI3,
20};
21
22use crate::{
23 gpio::{self, PushPull, AF5, AF6},
24 rcc::{self, Clocks},
25 time::rate,
26};
27use num_traits::{AsPrimitive, PrimInt};
28
29#[derive(Copy, Clone, PartialEq, Eq, Debug)]
31#[cfg_attr(feature = "defmt", derive(defmt::Format))]
32#[non_exhaustive]
33pub enum Error {
34 Overrun,
36 ModeFault,
38 Crc,
40}
41
42pub trait SckPin<SPI>: crate::private::Sealed {}
44
45pub trait MisoPin<SPI>: crate::private::Sealed {}
47
48pub trait MosiPin<SPI>: crate::private::Sealed {}
50
51impl SckPin<SPI1> for gpio::PA5<AF5<PushPull>> {}
55impl MisoPin<SPI1> for gpio::PA6<AF5<PushPull>> {}
56impl MosiPin<SPI1> for gpio::PA7<AF5<PushPull>> {}
57impl MosiPin<SPI1> for gpio::PB5<AF5<PushPull>> {}
58
59#[cfg(not(feature = "gpio-f373"))]
60impl SckPin<SPI2> for gpio::PB13<AF5<PushPull>> {}
61#[cfg(feature = "gpio-f373")]
62impl SckPin<SPI2> for gpio::PB10<AF5<PushPull>> {}
63impl MisoPin<SPI2> for gpio::PB14<AF5<PushPull>> {}
64impl MosiPin<SPI2> for gpio::PB15<AF5<PushPull>> {}
65
66impl MosiPin<SPI3> for gpio::PB5<AF6<PushPull>> {}
67
68impl SckPin<SPI3> for gpio::PC10<AF6<PushPull>> {}
69impl MisoPin<SPI3> for gpio::PC11<AF6<PushPull>> {}
70impl MosiPin<SPI3> for gpio::PC12<AF6<PushPull>> {}
71
72cfg_if::cfg_if! {
73 if #[cfg(feature = "gpio-f373")] {
74 impl SckPin<SPI2> for gpio::PB8<AF5<PushPull>> {}
75 impl SckPin<SPI2> for gpio::PD7<AF5<PushPull>> {}
76 impl SckPin<SPI2> for gpio::PD8<AF5<PushPull>> {}
77
78 impl SckPin<SPI1> for gpio::PA12<AF6<PushPull>> {}
79 impl MisoPin<SPI1> for gpio::PA13<AF6<PushPull>> {}
80
81 impl MosiPin<SPI1> for gpio::PB0<AF5<PushPull>> {}
82 impl MosiPin<SPI1> for gpio::PF6<AF5<PushPull>> {}
83
84 impl SckPin<SPI1> for gpio::PC7<AF5<PushPull>> {}
85 impl MisoPin<SPI1> for gpio::PC8<AF5<PushPull>> {}
86 impl MosiPin<SPI1> for gpio::PC9<AF5<PushPull>> {}
87
88
89 impl SckPin<SPI2> for gpio::PA8<AF5<PushPull>> {}
90 impl MisoPin<SPI2> for gpio::PA9<AF5<PushPull>> {}
91 impl MosiPin<SPI2> for gpio::PA10<AF5<PushPull>> {}
92
93 impl MisoPin<SPI2> for gpio::PC2<AF5<PushPull>> {}
94 impl MisoPin<SPI2> for gpio::PC3<AF5<PushPull>> {}
95
96 impl MisoPin<SPI2> for gpio::PD3<AF5<PushPull>> {}
97 impl MisoPin<SPI2> for gpio::PD4<AF5<PushPull>> {}
98
99 impl SckPin<SPI3> for gpio::PA1<AF6<PushPull>> {}
100 impl MisoPin<SPI3> for gpio::PA2<AF6<PushPull>> {}
101 impl MisoPin<SPI3> for gpio::PA3<AF6<PushPull>> {}
102
103 impl SckPin<SPI3> for gpio::PB3<AF6<PushPull>> {}
104 impl MisoPin<SPI3> for gpio::PB4<AF6<PushPull>> {}
105 }
106}
107
108cfg_if::cfg_if! {
109 if #[cfg(any(feature = "gpio-f303", feature = "gpio-f303e",))] {
110 impl SckPin<SPI2> for gpio::PF9<AF5<PushPull>> {}
111 impl SckPin<SPI2> for gpio::PF10<AF5<PushPull>> {}
112 }
113}
114
115cfg_if::cfg_if! {
116 if #[cfg(feature = "gpio-f303e")] {
117 impl SckPin<SPI4> for gpio::PE2<AF5<PushPull>> {}
118 impl MisoPin<SPI4> for gpio::PE5<AF5<PushPull>> {}
119 impl MosiPin<SPI4> for gpio::PE6<AF5<PushPull>> {}
120
121 impl SckPin<SPI4> for gpio::PE12<AF5<PushPull>> {}
122 impl MisoPin<SPI4> for gpio::PE13<AF5<PushPull>> {}
123 impl MosiPin<SPI4> for gpio::PE14<AF5<PushPull>> {}
124 }
125}
126
127cfg_if::cfg_if! {
128 if #[cfg(not(feature = "gpio-f302"))] {
129 impl SckPin<SPI1> for gpio::PB3<AF5<PushPull>> {}
130 impl MisoPin<SPI1> for gpio::PB4<AF5<PushPull>> {}
131 }
132}
133
134cfg_if::cfg_if! {
135 if #[cfg(all(
136 not(feature = "stm32f301"),
137 any(feature = "gpio-f303e", feature = "gpio-f302"),
138 ))] {
139 impl SckPin<SPI2> for gpio::PF1<AF5<PushPull>> {}
140 impl MisoPin<SPI2> for gpio::PA10<AF5<PushPull>> {}
141 impl MosiPin<SPI2> for gpio::PA11<AF5<PushPull>> {}
142 }
143}
144
145cfg_if::cfg_if! {
146 if #[cfg(all(
147 not(feature = "stm32f301"),
148 any(feature = "gpio-f302", feature = "gpio-f303", feature = "gpio-f303e"),
149 ))] {
150 impl SckPin<SPI3> for gpio::PB3<AF6<PushPull>> {}
151 impl MisoPin<SPI3> for gpio::PB4<AF6<PushPull>> {}
152 }
153}
154
155pub trait Word {
158 fn register_config() -> (FRXTH_A, DS_A);
161}
162
163impl Word for u8 {
164 fn register_config() -> (FRXTH_A, DS_A) {
165 (FRXTH_A::Quarter, DS_A::EightBit)
166 }
167}
168
169impl Word for u16 {
170 fn register_config() -> (FRXTH_A, DS_A) {
171 (FRXTH_A::Half, DS_A::SixteenBit)
172 }
173}
174
175pub struct Spi<SPI, Pins, Word = u8> {
177 spi: SPI,
178 pins: Pins,
179 _word: PhantomData<Word>,
180}
181
182pub mod config;
183
184impl<SPI, Sck, Miso, Mosi, WORD> Spi<SPI, (Sck, Miso, Mosi), WORD> {
185 pub fn new<Config>(
203 spi: SPI,
204 pins: (Sck, Miso, Mosi),
205 config: Config,
206 clocks: Clocks,
207 apb: &mut <SPI as rcc::RccBus>::Bus,
208 ) -> Self
209 where
210 SPI: Instance,
211 Sck: SckPin<SPI>,
212 Miso: MisoPin<SPI>,
213 Mosi: MosiPin<SPI>,
214 WORD: Word,
215 Config: Into<config::Config>,
216 {
217 let config = config.into();
218 SPI::enable(apb);
219 SPI::reset(apb);
220
221 let (frxth, ds) = WORD::register_config();
222 spi.cr2.write(|w| {
223 w.frxth().variant(frxth);
224 w.ds().variant(ds);
225 w.ssoe().disabled()
227 });
228
229 spi.cr1.write(|w| {
240 w.mstr().master();
241
242 match config.mode.phase {
243 Phase::CaptureOnFirstTransition => w.cpha().first_edge(),
244 Phase::CaptureOnSecondTransition => w.cpha().second_edge(),
245 };
246
247 match config.mode.polarity {
248 Polarity::IdleLow => w.cpol().idle_low(),
249 Polarity::IdleHigh => w.cpol().idle_high(),
250 };
251
252 w.br()
253 .variant(Self::compute_baud_rate(clocks, config.frequency));
254
255 w.spe()
256 .enabled()
257 .lsbfirst()
258 .msbfirst()
259 .ssi()
260 .slave_not_selected()
261 .ssm()
262 .enabled()
263 .crcen()
264 .disabled()
265 .bidimode()
266 .unidirectional()
267 });
268
269 Spi {
270 spi,
271 pins,
272 _word: PhantomData,
273 }
274 }
275
276 pub unsafe fn peripheral(&mut self) -> &mut SPI {
286 &mut self.spi
287 }
288
289 pub fn free(self) -> (SPI, (Sck, Miso, Mosi)) {
291 (self.spi, self.pins)
292 }
293}
294
295impl<SPI, Pins, Word> Spi<SPI, Pins, Word>
296where
297 SPI: Instance,
298{
299 pub fn reclock(&mut self, freq: impl Into<rate::Generic<u32>>, clocks: Clocks) {
301 self.spi.cr1.modify(|_, w| w.spe().disabled());
302
303 self.spi.cr1.modify(|_, w| {
304 w.br().variant(Self::compute_baud_rate(clocks, freq.into()));
305 w.spe().enabled()
306 });
307 }
308
309 fn compute_baud_rate(clocks: Clocks, freq: rate::Generic<u32>) -> spi1::cr1::BR_A {
310 use spi1::cr1::BR_A;
311 match SPI::clock(&clocks).0 / (freq.integer() * *freq.scaling_factor()) {
312 0 => crate::unreachable!(),
313 1..=2 => BR_A::Div2,
314 3..=5 => BR_A::Div4,
315 6..=11 => BR_A::Div8,
316 12..=23 => BR_A::Div16,
317 24..=39 => BR_A::Div32,
318 40..=95 => BR_A::Div64,
319 96..=191 => BR_A::Div128,
320 _ => BR_A::Div256,
321 }
322 }
323
324 #[doc(alias = "unmask")]
329 pub fn interrupt(&self) -> <SPI as crate::interrupts::InterruptNumber>::Interrupt {
330 <SPI as crate::interrupts::InterruptNumber>::INTERRUPT
331 }
332}
333
334impl<SPI, Sck, Miso, Mosi, Word> FullDuplex<Word> for Spi<SPI, (Sck, Miso, Mosi), Word>
335where
336 SPI: Instance,
337 Miso: MisoPin<SPI>,
340 Mosi: MosiPin<SPI>,
341 Word: PrimInt + Into<u32> + 'static,
342 u32: AsPrimitive<Word>,
343{
344 type Error = Error;
345
346 fn read(&mut self) -> nb::Result<Word, Error> {
347 let sr = self.spi.sr.read();
348
349 Err(if sr.ovr().is_overrun() {
350 nb::Error::Other(Error::Overrun)
351 } else if sr.modf().is_fault() {
352 nb::Error::Other(Error::ModeFault)
353 } else if sr.crcerr().is_no_match() {
354 nb::Error::Other(Error::Crc)
355 } else if sr.rxne().is_not_empty() {
356 let read_ptr = core::ptr::addr_of!(self.spi.dr) as *const Word;
357 let value = unsafe { core::ptr::read_volatile(read_ptr) };
359 return Ok(value);
360 } else {
361 nb::Error::WouldBlock
362 })
363 }
364
365 fn send(&mut self, word: Word) -> nb::Result<(), Error> {
366 let sr = self.spi.sr.read();
367
368 Err(if sr.ovr().is_overrun() {
369 nb::Error::Other(Error::Overrun)
370 } else if sr.modf().is_fault() {
371 nb::Error::Other(Error::ModeFault)
372 } else if sr.crcerr().is_no_match() {
373 nb::Error::Other(Error::Crc)
374 } else if sr.txe().is_empty() {
375 let write_ptr = core::ptr::addr_of!(self.spi.dr) as *mut Word;
376 unsafe { core::ptr::write_volatile(write_ptr, word) };
378 return Ok(());
379 } else {
380 nb::Error::WouldBlock
381 })
382 }
383}
384
385impl<SPI, Sck, Miso, Mosi, Word> spi::transfer::Default<Word> for Spi<SPI, (Sck, Miso, Mosi), Word>
386where
387 SPI: Instance,
388 Miso: MisoPin<SPI>,
389 Mosi: MosiPin<SPI>,
390 Word: PrimInt + Into<u32> + 'static,
391 u32: AsPrimitive<Word>,
392{
393}
394
395impl<SPI, Sck, Miso, Mosi, Word> spi::write::Default<Word> for Spi<SPI, (Sck, Miso, Mosi), Word>
396where
397 SPI: Instance,
398 Miso: MisoPin<SPI>,
399 Mosi: MosiPin<SPI>,
400 Word: PrimInt + Into<u32> + 'static,
401 u32: AsPrimitive<Word>,
402{
403}
404
405pub trait Instance:
407 Deref<Target = spi1::RegisterBlock>
408 + crate::interrupts::InterruptNumber
409 + crate::private::Sealed
410 + rcc::Enable
411 + rcc::Reset
412 + rcc::BusClock
413{
414}
415
416macro_rules! spi {
417 ($($SPIX:ident: ($APBX:ident, $pclkX:ident),)+) => {
418 $(
419 impl crate::interrupts::InterruptNumber for pac::$SPIX {
420 type Interrupt = Interrupt;
421 const INTERRUPT: Self::Interrupt = interrupts::$SPIX;
422 }
423
424 impl Instance for pac::$SPIX { }
425
426 #[cfg(feature = "defmt")]
427 impl<Pins> defmt::Format for Spi<pac::$SPIX, Pins> {
428 fn format(&self, f: defmt::Formatter) {
429 defmt::write!(
435 f,
436 "SPI {{ spi: {}, pins: ? }}",
437 stringify!($SPIX),
438 );
439 }
440 }
441
442 impl<Pins> fmt::Debug for Spi<$SPIX, Pins> {
443 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
444 f.debug_struct(stringify!(Serial))
445 .field("spi", &stringify!($USARTX))
446 .field("pins", &"?")
447 .finish()
448 }
449 }
450 )+
451 };
452
453 ([ $(($X:literal, $APB:literal)),+ ]) => {
454 paste::paste! {
455 spi!(
456 $(
457 [<SPI $X>]: (
458 [<APB $APB>],
459 [<pclk $APB>]
460 ),
461 )+
462 );
463 }
464 };
465}
466
467mod interrupts {
468 use crate::pac::Interrupt;
469
470 cfg_if::cfg_if! {
471 if #[cfg(feature = "svd-f301")] {
472 #[allow(unused)]
473 pub(crate) const SPI1: Interrupt = Interrupt::SPI1_IRQ;
474 #[allow(unused)]
475 pub(crate) const SPI2: Interrupt = Interrupt::SPI2_IRQ;
476 #[allow(unused)]
477 pub(crate) const SPI3: Interrupt = Interrupt::SPI3_IRQ;
478 } else if #[cfg(feature = "svd-f3x4")] {
479 pub(crate) const SPI1: Interrupt = Interrupt::SPI1;
480 } else {
481 #[allow(unused)]
482 pub(crate) const SPI1: Interrupt = Interrupt::SPI1;
483 #[allow(unused)]
484 pub(crate) const SPI2: Interrupt = Interrupt::SPI2;
485 #[allow(unused)]
486 pub(crate) const SPI3: Interrupt = Interrupt::SPI3;
487 }
488 }
489
490 cfg_if::cfg_if! {
491 if #[cfg(any(feature = "gpio-f303e", feature = "svd-f302"))] {
492 #[allow(unused)]
498 pub(crate) const SPI4: Interrupt = Interrupt::SPI3;
499 } else if #[cfg(feature = "gpio-f303e")] {
500 pub(crate) const SPI4: Interrupt = Interrupt::SPI4;
501 }
502 }
503}
504
505#[cfg(feature = "gpio-f333")]
506spi!([(1, 2)]);
507
508#[cfg(feature = "gpio-f302")]
509spi!([(2, 1), (3, 1)]);
510
511#[cfg(any(feature = "gpio-f303", feature = "gpio-f373",))]
512spi!([(1, 2), (2, 1), (3, 1)]);
513
514#[cfg(feature = "gpio-f303e")]
515spi!([(1, 2), (2, 1), (3, 1), (4, 2)]);