rp2040_hal/
spi.rs

1//! Serial Peripheral Interface (SPI)
2//!
3//! [`Spi`] is the main struct exported by this module, representing a configured Spi bus. See its
4//! docs for more information on its type parameters.
5//!
6//! See [Chapter 4 Section 4](https://datasheets.raspberrypi.org/rp2040/rp2040-datasheet.pdf) for more details
7//!
8//! ## Usage
9//!
10//! ```no_run
11//! use embedded_hal::spi::MODE_0;
12//! use fugit::RateExtU32;
13//! use rp2040_hal::{spi::Spi, gpio::{Pins, FunctionSpi}, pac, Sio};
14//!
15//! let mut peripherals = pac::Peripherals::take().unwrap();
16//! let sio = Sio::new(peripherals.SIO);
17//! let pins = Pins::new(peripherals.IO_BANK0, peripherals.PADS_BANK0, sio.gpio_bank0, &mut peripherals.RESETS);
18//!
19//! let sclk = pins.gpio2.into_function::<FunctionSpi>();
20//! let mosi = pins.gpio3.into_function::<FunctionSpi>();
21//!
22//! let spi_device = peripherals.SPI0;
23//! let spi_pin_layout = (mosi, sclk);
24//!
25//! let spi = Spi::<_, _, _, 8>::new(spi_device, spi_pin_layout)
26//!     .init(&mut peripherals.RESETS, 125_000_000u32.Hz(), 16_000_000u32.Hz(), MODE_0);
27//! ```
28
29use core::{convert::Infallible, marker::PhantomData, ops::Deref};
30
31use embedded_hal::spi::{self, Phase, Polarity};
32// Support Embedded HAL 0.2 for backwards-compatibility
33use embedded_hal_0_2::{blocking::spi as blocking_spi02, spi as spi02};
34use embedded_hal_nb::spi::FullDuplex;
35use fugit::{HertzU32, RateExtU32};
36
37use crate::{
38    dma::{EndlessReadTarget, EndlessWriteTarget, ReadTarget, WriteTarget},
39    pac::{self, dma::ch::ch_ctrl_trig::TREQ_SEL_A, RESETS},
40    resets::SubsystemReset,
41    typelevel::Sealed,
42};
43
44mod pins;
45pub use pins::*;
46
47impl From<spi::Mode> for FrameFormat {
48    fn from(f: spi::Mode) -> Self {
49        Self::MotorolaSpi(f)
50    }
51}
52
53impl From<&spi::Mode> for FrameFormat {
54    fn from(f: &spi::Mode) -> Self {
55        Self::MotorolaSpi(*f)
56    }
57}
58
59/// SPI frame format
60#[derive(Clone, Copy, PartialEq, Eq)]
61pub enum FrameFormat {
62    /// Motorola SPI format. See section 4.4.3.9 of RP2040 datasheet.
63    MotorolaSpi(spi::Mode),
64    /// Texas Instruments synchronous serial frame format. See section 4.4.3.8 of RP2040 datasheet.
65    TexasInstrumentsSynchronousSerial,
66    /// National Semiconductor Microwire frame format. See section 4.4.3.14 of RP2040 datasheet.
67    NationalSemiconductorMicrowire,
68}
69
70impl From<&embedded_hal_0_2::spi::Mode> for FrameFormat {
71    fn from(f: &embedded_hal_0_2::spi::Mode) -> Self {
72        let embedded_hal_0_2::spi::Mode { polarity, phase } = f;
73        match (polarity, phase) {
74            (spi02::Polarity::IdleLow, spi02::Phase::CaptureOnFirstTransition) => {
75                FrameFormat::MotorolaSpi(spi::MODE_0)
76            }
77            (spi02::Polarity::IdleLow, spi02::Phase::CaptureOnSecondTransition) => {
78                FrameFormat::MotorolaSpi(spi::MODE_1)
79            }
80            (spi02::Polarity::IdleHigh, spi02::Phase::CaptureOnFirstTransition) => {
81                FrameFormat::MotorolaSpi(spi::MODE_2)
82            }
83            (spi02::Polarity::IdleHigh, spi02::Phase::CaptureOnSecondTransition) => {
84                FrameFormat::MotorolaSpi(spi::MODE_3)
85            }
86        }
87    }
88}
89
90impl From<embedded_hal_0_2::spi::Mode> for FrameFormat {
91    fn from(f: embedded_hal_0_2::spi::Mode) -> Self {
92        From::from(&f)
93    }
94}
95
96/// State of the SPI
97pub trait State: Sealed {}
98
99/// Spi is disabled
100pub struct Disabled {
101    __private: (),
102}
103
104/// Spi is enabled
105pub struct Enabled {
106    __private: (),
107}
108
109impl State for Disabled {}
110impl Sealed for Disabled {}
111impl State for Enabled {}
112impl Sealed for Enabled {}
113
114/// Pac SPI device
115pub trait SpiDevice: Deref<Target = pac::spi0::RegisterBlock> + SubsystemReset + Sealed {
116    /// Index of the peripheral.
117    const ID: usize;
118
119    /// The DREQ number for which TX DMA requests are triggered.
120    fn tx_dreq() -> u8;
121    /// The DREQ number for which RX DMA requests are triggered.
122    fn rx_dreq() -> u8;
123}
124
125impl Sealed for pac::SPI0 {}
126impl SpiDevice for pac::SPI0 {
127    const ID: usize = 0;
128    fn tx_dreq() -> u8 {
129        TREQ_SEL_A::SPI0_TX.into()
130    }
131    fn rx_dreq() -> u8 {
132        TREQ_SEL_A::SPI0_RX.into()
133    }
134}
135impl Sealed for pac::SPI1 {}
136impl SpiDevice for pac::SPI1 {
137    const ID: usize = 1;
138    fn tx_dreq() -> u8 {
139        TREQ_SEL_A::SPI1_TX.into()
140    }
141    fn rx_dreq() -> u8 {
142        TREQ_SEL_A::SPI1_RX.into()
143    }
144}
145
146/// Data size used in spi
147pub trait DataSize: Sealed {}
148
149impl DataSize for u8 {}
150impl DataSize for u16 {}
151impl Sealed for u8 {}
152impl Sealed for u16 {}
153
154/// Configured Spi bus.
155///
156/// This struct implements the `embedded-hal` Spi-related traits. It represents unique ownership
157/// of the entire Spi bus of a single configured RP2040 Spi peripheral.
158///
159/// `Spi` has four generic parameters:
160/// - `S`: a typestate for whether the bus is [`Enabled`] or [`Disabled`]. Upon initial creation,
161///   the bus is [`Disabled`]. You will then need to initialize it as either a main (master) or sub
162///   (slave) device, providing the necessary configuration, at which point it will become [`Enabled`].
163/// - `D`: Which of the concrete Spi peripherals is being used, [`pac::SPI0`] or [`pac::SPI1`]
164/// - `P`: Which pins are being used to configure the Spi peripheral `D`. A table of valid
165///   pinouts for each Spi peripheral can be found in section 1.4.3 of the RP2040 datasheet.
166///   The [`ValidSpiPinout`] trait is implemented for tuples of pin types that follow the layout:
167///     - `(Tx, Sck)` (i.e. first the "Tx"/"MOSI" pin, then the "Sck"/"Clock" pin)
168///     - `(Tx, Rx, Sck)` (i.e. first "Tx"/"MOSI", then "Rx"/"MISO", then "Sck"/"Clock" pin)
169///
170///   If you select an invalid layout, you will get a compile error that `P` does not implement
171///   [`ValidSpiPinout`] for your specified [`SpiDevice`] peripheral `D`
172/// - `DS`: The "data size", i.e. the number of bits transferred per data frame. Defaults to 8.
173///
174/// In most cases you won't have to specify these types manually and can let the compiler infer
175/// them for you based on the values you pass in to `new`. If you want to select a different
176/// data frame size, you'll need to do that by specifying the `DS` parameter manually.
177///
178/// See [the module level docs][self] for an example.
179pub struct Spi<S: State, D: SpiDevice, P: ValidSpiPinout<D>, const DS: u8 = 8u8> {
180    device: D,
181    pins: P,
182    state: PhantomData<S>,
183}
184
185impl<S: State, D: SpiDevice, P: ValidSpiPinout<D>, const DS: u8> Spi<S, D, P, DS> {
186    fn transition<To: State>(self, _: To) -> Spi<To, D, P, DS> {
187        Spi {
188            device: self.device,
189            pins: self.pins,
190            state: PhantomData,
191        }
192    }
193
194    /// Releases the underlying device and pins.
195    pub fn free(self) -> (D, P) {
196        (self.device, self.pins)
197    }
198
199    /// Set device pre-scale and post-div properties to match the given baudrate as
200    /// closely as possible based on the given peripheral clock frequency.
201    ///
202    /// Typically the peripheral clock is set to 125_000_000 Hz.
203    ///
204    /// Returns the frequency that we were able to achieve, which may not be exactly
205    /// the requested baudrate.
206    pub fn set_baudrate<F: Into<HertzU32>, B: Into<HertzU32>>(
207        &mut self,
208        peri_frequency: F,
209        baudrate: B,
210    ) -> HertzU32 {
211        let freq_in = peri_frequency.into().to_Hz();
212        let baudrate = baudrate.into().to_Hz();
213        let mut prescale: u8 = u8::MAX;
214        let mut postdiv: u8 = 0;
215
216        // Find smallest prescale value which puts output frequency in range of
217        // post-divide. Prescale is an even number from 2 to 254 inclusive.
218        for prescale_option in (2u32..=254).step_by(2) {
219            // We need to use an saturating_mul here because with a high baudrate certain invalid prescale
220            // values might not fit in u32. However we can be sure those values exceed the max sys_clk frequency
221            // So clamping a u32::MAX is fine here...
222            if freq_in < ((prescale_option + 2) * 256).saturating_mul(baudrate) {
223                prescale = prescale_option as u8;
224                break;
225            }
226        }
227
228        // We might not find a prescale value that lowers the clock freq enough, so we leave it at max
229        debug_assert_ne!(prescale, u8::MAX);
230
231        // Find largest post-divide which makes output <= baudrate. Post-divide is
232        // an integer in the range 0 to 255 inclusive.
233        for postdiv_option in (1..=255u8).rev() {
234            if freq_in / (prescale as u32 * postdiv_option as u32) > baudrate {
235                postdiv = postdiv_option;
236                break;
237            }
238        }
239
240        self.device
241            .sspcpsr()
242            .write(|w| unsafe { w.cpsdvsr().bits(prescale) });
243        self.device
244            .sspcr0()
245            .modify(|_, w| unsafe { w.scr().bits(postdiv) });
246
247        // Return the frequency we were able to achieve
248        (freq_in / (prescale as u32 * (1 + postdiv as u32))).Hz()
249    }
250
251    /// Set format
252    pub fn set_format(&mut self, frame_format: FrameFormat) {
253        self.device.sspcr0().modify(|_, w| unsafe {
254            w.dss().bits(DS - 1).frf().bits(match &frame_format {
255                FrameFormat::MotorolaSpi(_) => 0x00,
256                FrameFormat::TexasInstrumentsSynchronousSerial => 0x01,
257                FrameFormat::NationalSemiconductorMicrowire => 0x10,
258            });
259
260            /*
261             * Clock polarity (SPO) and clock phase (SPH) are only applicable to
262             * the Motorola SPI frame format.
263             */
264            if let FrameFormat::MotorolaSpi(ref mode) = frame_format {
265                w.spo()
266                    .bit(mode.polarity == Polarity::IdleHigh)
267                    .sph()
268                    .bit(mode.phase == Phase::CaptureOnSecondTransition);
269            }
270            w
271        });
272    }
273}
274
275impl<D: SpiDevice, P: ValidSpiPinout<D>, const DS: u8> Spi<Disabled, D, P, DS> {
276    /// Create new not initialized Spi bus. Initialize it with [`.init`][Self::init]
277    /// or [`.init_slave`][Self::init_slave].
278    ///
279    /// Valid pin sets are in the form of `(Tx, Sck)` or `(Tx, Rx, Sck)`
280    ///
281    /// If your pins are dynamically identified (`Pin<DynPinId, _, _>`) they will first need to pass
282    /// validation using their corresponding [`ValidatedPinXX`](ValidatedPinTx).
283    pub fn new(device: D, pins: P) -> Spi<Disabled, D, P, DS> {
284        Spi {
285            device,
286            pins,
287            state: PhantomData,
288        }
289    }
290
291    /// Set master/slave
292    fn set_slave(&mut self, slave: bool) {
293        if slave {
294            self.device.sspcr1().modify(|_, w| w.ms().set_bit());
295        } else {
296            self.device.sspcr1().modify(|_, w| w.ms().clear_bit());
297        }
298    }
299
300    fn init_spi<F: Into<HertzU32>, B: Into<HertzU32>>(
301        mut self,
302        resets: &mut RESETS,
303        peri_frequency: F,
304        baudrate: B,
305        frame_format: FrameFormat,
306        slave: bool,
307    ) -> Spi<Enabled, D, P, DS> {
308        self.device.reset_bring_down(resets);
309        self.device.reset_bring_up(resets);
310
311        self.set_baudrate(peri_frequency, baudrate);
312        self.set_format(frame_format);
313        self.set_slave(slave);
314        // Always enable DREQ signals -- harmless if DMA is not listening
315        self.device
316            .sspdmacr()
317            .modify(|_, w| w.txdmae().set_bit().rxdmae().set_bit());
318
319        // Finally enable the SPI
320        self.device.sspcr1().modify(|_, w| w.sse().set_bit());
321
322        self.transition(Enabled { __private: () })
323    }
324
325    /// Initialize the SPI in master mode
326    pub fn init<F: Into<HertzU32>, B: Into<HertzU32>, M: Into<FrameFormat>>(
327        self,
328        resets: &mut RESETS,
329        peri_frequency: F,
330        baudrate: B,
331        frame_format: M,
332    ) -> Spi<Enabled, D, P, DS> {
333        self.init_spi(resets, peri_frequency, baudrate, frame_format.into(), false)
334    }
335
336    /// Initialize the SPI in slave mode
337    pub fn init_slave<M: Into<FrameFormat>>(
338        self,
339        resets: &mut RESETS,
340        frame_format: M,
341    ) -> Spi<Enabled, D, P, DS> {
342        // Use dummy values for frequency and baudrate.
343        // With both values 0, set_baudrate will set prescale == u8::MAX, which will break if debug assertions are enabled.
344        // u8::MAX is outside the allowed range 2..=254 for CPSDVSR, which might interfere with proper operation in slave mode.
345        self.init_spi(
346            resets,
347            1000u32.Hz(),
348            1000u32.Hz(),
349            frame_format.into(),
350            true,
351        )
352    }
353}
354
355impl<D: SpiDevice, P: ValidSpiPinout<D>, const DS: u8> Spi<Enabled, D, P, DS> {
356    fn is_writable(&self) -> bool {
357        self.device.sspsr().read().tnf().bit_is_set()
358    }
359    fn is_readable(&self) -> bool {
360        self.device.sspsr().read().rne().bit_is_set()
361    }
362
363    /// Check if spi is busy transmitting and/or receiving
364    pub fn is_busy(&self) -> bool {
365        self.device.sspsr().read().bsy().bit_is_set()
366    }
367
368    /// Disable the spi to reset its configuration. You'll then need to initialize it again to use
369    /// it.
370    pub fn disable(self) -> Spi<Disabled, D, P, DS> {
371        self.device.sspcr1().modify(|_, w| w.sse().clear_bit());
372
373        self.transition(Disabled { __private: () })
374    }
375}
376
377macro_rules! impl_write {
378    ($type:ident, [$($nr:expr),+]) => {
379
380        $(
381        impl<D: SpiDevice, P: ValidSpiPinout<D>> spi02::FullDuplex<$type> for Spi<Enabled, D, P, $nr> {
382            type Error = Infallible;
383
384            fn read(&mut self) -> Result<$type, nb::Error<Infallible>> {
385                if !self.is_readable() {
386                    return Err(nb::Error::WouldBlock);
387                }
388
389                Ok(self.device.sspdr().read().data().bits() as $type)
390            }
391            fn send(&mut self, word: $type) -> Result<(), nb::Error<Infallible>> {
392                // Write to TX FIFO whilst ignoring RX, then clean up afterward. When RX
393                // is full, PL022 inhibits RX pushes, and sets a sticky flag on
394                // push-on-full, but continues shifting. Safe if SSPIMSC_RORIM is not set.
395                if !self.is_writable() {
396                    return Err(nb::Error::WouldBlock);
397                }
398
399                self.device
400                    .sspdr()
401                    .write(|w| unsafe { w.data().bits(word as u16) });
402                Ok(())
403            }
404        }
405
406        impl<D: SpiDevice, P: ValidSpiPinout<D>> blocking_spi02::write::Default<$type> for Spi<Enabled, D, P, $nr> {}
407        impl<D: SpiDevice, P: ValidSpiPinout<D>> blocking_spi02::transfer::Default<$type> for Spi<Enabled, D, P, $nr> {}
408        impl<D: SpiDevice, P: ValidSpiPinout<D>> blocking_spi02::write_iter::Default<$type> for Spi<Enabled, D, P, $nr> {}
409
410        impl<D: SpiDevice, P: ValidSpiPinout<D>> spi::ErrorType for Spi<Enabled, D, P, $nr> {
411            type Error = Infallible;
412        }
413
414        impl<D: SpiDevice, P: ValidSpiPinout<D>> spi::SpiBus<$type> for Spi<Enabled, D, P, $nr> {
415            fn read(&mut self, words: &mut [$type]) -> Result<(), Self::Error> {
416                for word in words.iter_mut() {
417                    // write empty word
418                    while !self.is_writable() {}
419                    self.device
420                        .sspdr()
421                        .write(|w| unsafe { w.data().bits(0) });
422
423                    // read one word
424                    while !self.is_readable() {}
425                    *word = self.device.sspdr().read().data().bits() as $type;
426                }
427                Ok(())
428            }
429
430            fn write(&mut self, words: &[$type]) -> Result<(), Self::Error> {
431                for word in words.iter() {
432                    // write one word
433                    while !self.is_writable() {}
434                    self.device
435                        .sspdr()
436                        .write(|w| unsafe { w.data().bits(*word as u16) });
437
438                    // drop read wordd
439                    while !self.is_readable() {}
440                    let _ = self.device.sspdr().read().data().bits();
441                }
442                Ok(())
443            }
444
445            fn transfer(&mut self, read: &mut [$type], write: &[$type]) -> Result<(), Self::Error>{
446                let len = read.len().max(write.len());
447                for i in 0..len {
448                    // write one word. Send empty word if buffer is empty.
449                    let wb = write.get(i).copied().unwrap_or(0);
450                    while !self.is_writable() {}
451                    self.device
452                        .sspdr()
453                        .write(|w| unsafe { w.data().bits(wb as u16) });
454
455                    // read one word. Drop extra words if buffer is full.
456                    while !self.is_readable() {}
457                    let rb = self.device.sspdr().read().data().bits() as $type;
458                    if let Some(r) = read.get_mut(i) {
459                        *r = rb;
460                    }
461                }
462
463                Ok(())
464            }
465
466            fn transfer_in_place(&mut self, words: &mut [$type]) -> Result<(), Self::Error>{
467                for word in words.iter_mut() {
468                    // write one word
469                    while !self.is_writable() {}
470                    self.device
471                        .sspdr()
472                        .write(|w| unsafe { w.data().bits(*word as u16) });
473
474                    // read one word
475                    while !self.is_readable() {}
476                    *word = self.device.sspdr().read().data().bits() as $type;
477                }
478
479                Ok(())
480            }
481
482            fn flush(&mut self) -> Result<(), Self::Error> {
483                while self.is_busy() {}
484                Ok(())
485            }
486        }
487
488        impl<D: SpiDevice, P: ValidSpiPinout<D>> FullDuplex<$type> for Spi<Enabled, D, P, $nr> {
489            fn read(&mut self) -> Result<$type, nb::Error<Infallible>> {
490                if !self.is_readable() {
491                    return Err(nb::Error::WouldBlock);
492                }
493
494                Ok(self.device.sspdr().read().data().bits() as $type)
495            }
496            fn write(&mut self, word: $type) -> Result<(), nb::Error<Infallible>> {
497                // Write to TX FIFO whilst ignoring RX, then clean up afterward. When RX
498                // is full, PL022 inhibits RX pushes, and sets a sticky flag on
499                // push-on-full, but continues shifting. Safe if SSPIMSC_RORIM is not set.
500                if !self.is_writable() {
501                    return Err(nb::Error::WouldBlock);
502                }
503
504                self.device
505                    .sspdr()
506                    .write(|w| unsafe { w.data().bits(word as u16) });
507                Ok(())
508            }
509        }
510
511        // Safety: This only reads from the RX fifo, so it doesn't
512        // interact with rust-managed memory.
513        unsafe impl<D: SpiDevice, P: ValidSpiPinout<D>> ReadTarget for Spi<Enabled, D, P, $nr> {
514            type ReceivedWord = $type;
515
516            fn rx_treq() -> Option<u8> {
517                Some(D::rx_dreq())
518            }
519
520            fn rx_address_count(&self) -> (u32, u32) {
521                (
522                    self.device.sspdr().as_ptr() as u32,
523                    u32::MAX,
524                )
525            }
526
527            fn rx_increment(&self) -> bool {
528                false
529            }
530        }
531
532        impl<D: SpiDevice, P: ValidSpiPinout<D>> EndlessReadTarget for Spi<Enabled, D, P, $nr> {}
533
534        // Safety: This only writes to the TX fifo, so it doesn't
535        // interact with rust-managed memory.
536        unsafe impl<D: SpiDevice, P: ValidSpiPinout<D>> WriteTarget for Spi<Enabled, D, P, $nr> {
537            type TransmittedWord = $type;
538
539            fn tx_treq() -> Option<u8> {
540                Some(D::tx_dreq())
541            }
542
543            fn tx_address_count(&mut self) -> (u32, u32) {
544                (
545                    self.device.sspdr().as_ptr() as u32,
546                    u32::MAX,
547                )
548            }
549
550            fn tx_increment(&self) -> bool {
551                false
552            }
553        }
554
555        impl<D: SpiDevice, P: ValidSpiPinout<D>> EndlessWriteTarget for Spi<Enabled, D, P, $nr> {}
556    )+
557
558    };
559}
560
561impl_write!(u8, [4, 5, 6, 7, 8]);
562impl_write!(u16, [9, 10, 11, 12, 13, 14, 15, 16]);