stm32f3xx_hal/
spi.rs

1//! # Serial Peripheral Interface (SPI) bus
2//!
3//! ## Examples
4//!
5//! A usage example of the can peripheral can be found at [examples/spi.rs]
6//!
7//! [examples/spi.rs]: https://github.com/stm32-rs/stm32f3xx-hal/blob/v0.10.0/examples/spi.rs
8
9use 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/// SPI error
30#[derive(Copy, Clone, PartialEq, Eq, Debug)]
31#[cfg_attr(feature = "defmt", derive(defmt::Format))]
32#[non_exhaustive]
33pub enum Error {
34    /// Overrun occurred
35    Overrun,
36    /// Mode fault occurred
37    ModeFault,
38    /// CRC error
39    Crc,
40}
41
42/// SCK pin
43pub trait SckPin<SPI>: crate::private::Sealed {}
44
45/// MISO pin
46pub trait MisoPin<SPI>: crate::private::Sealed {}
47
48/// MOSI pin
49pub trait MosiPin<SPI>: crate::private::Sealed {}
50
51// TODO(Sh3Rm4n): NSS Pins
52// pub trait NssPin<SPI>: crate::private::Sealed {}
53
54impl 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
155/// Configuration trait for the Word Size
156/// used by the SPI peripheral
157pub trait Word {
158    /// Returns the register configuration
159    /// to set the word size
160    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
175/// SPI peripheral operating in full duplex master mode
176pub 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    /// Configures the SPI peripheral to operate in full duplex master mode.
186    ///
187    /// The most convinient way to get a device is like that:
188    ///
189    /// ```ignore
190    /// use stm32f3xx_hal::prelude::*;
191    /// use stm32f3xx_hal::spi::Spi;
192    ///
193    /// // ...
194    ///
195    /// let spi = Spi::new(dp.SPI1, (sck_pin, mosi_pin, miso_pin), 1.MHz, clocks, &mut dp.abp1);
196    ///
197    /// ```
198    ///
199    /// To get a better example, look [here](https://github.com/stm32-rs/stm32f3xx-hal/blob/v0.10.0/examples/spi.rs).
200    ///
201    // TODO(Sh3Rm4n): See alternative modes provided besides FullDuplex (as listed in Stm32CubeMx).
202    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            // Slave Select output disabled
226            w.ssoe().disabled()
227        });
228
229        // CPHA: phase
230        // CPOL: polarity
231        // MSTR: master mode
232        // BR: 1 MHz
233        // SPE: SPI disabled
234        // LSBFIRST: MSB first
235        // SSM: enable software slave management (NSS pin free for other uses)
236        // SSI: set nss high = master mode
237        // CRCEN: hardware CRC calculation disabled
238        // BIDIMODE: 2 line unidirectional (full duplex)
239        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    /// Get access to the underlying register block.
277    ///
278    /// # Safety
279    ///
280    /// This function is not _memory_ unsafe per se, but does not guarantee
281    /// anything about assumptions of invariants made in this implementation.
282    ///
283    /// Changing specific options can lead to un-expected behavior and nothing
284    /// is guaranteed.
285    pub unsafe fn peripheral(&mut self) -> &mut SPI {
286        &mut self.spi
287    }
288
289    /// Releases the SPI peripheral and associated pins
290    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    /// Change the baud rate of the SPI
300    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    /// Obtain the associated interrupt number for the serial peripheral.
325    ///
326    /// Used to unmask / enable the interrupt with [`cortex_m::peripheral::NVIC::unmask()`].
327    /// This is useful for all `cortex_m::peripheral::INTERRUPT` functions.
328    #[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    // Full Duplex needs the Miso and Mosi pins.
338    // SckPin could technically be omitted, though not advisable.
339    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            // SAFETY: Read from register owned by this Spi struct
358            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            // SAFETY: Write to register owned by this Spi struct
377            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
405/// SPI instance
406pub 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                    // Omitting pins makes it:
430                    // 1. Easier.
431                    // 2. Not to specialized to use it ergonimically for users
432                    //    even in a generic context.
433                    // 3. Not require specialization.
434                    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            // XXX This is a hack.
493            // SPI4 should have a corresponding nvic interrupt number. But the svd does not
494            // generated an enum-number for that. The RM0365 does also not list it as an interrupt.
495            // Strangly though, the Stm32CubeMx program let's us choose to enable the NVIC
496            // interrupt. This is probably a documentation bug.
497            #[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)]);