stm32l4xx_hal/
spi.rs

1//! Serial Peripheral Interface (SPI) bus
2//!
3//! The PACs and SVDs are not set up granularity enough to handle all peripheral configurations.
4//! SPI2 is enabled for stm32l4x2 feature at a HAL level even though some variants do and some
5//! don't have it (L432xx and L442xx don't, L452xx does). Users of this MCU variant that
6//! don't have it shouldn't attempt to use it. Relevant info is on user-manual level.
7
8use core::ptr;
9use core::sync::atomic;
10use core::sync::atomic::Ordering;
11
12#[cfg(not(any(feature = "stm32l433", feature = "stm32l443",)))]
13use crate::dma::dma2;
14use crate::dma::{self, dma1, TransferPayload};
15use crate::dmamux::{DmaInput, DmaMux};
16use crate::gpio::{Alternate, PushPull};
17use crate::hal::spi::{FullDuplex, Mode, Phase, Polarity};
18use crate::rcc::{Clocks, Enable, RccBus, Reset};
19use crate::time::Hertz;
20
21use embedded_dma::{StaticReadBuffer, StaticWriteBuffer};
22
23/// SPI error
24#[non_exhaustive]
25#[derive(Debug)]
26pub enum Error {
27    /// Overrun occurred
28    Overrun,
29    /// Mode fault occurred
30    ModeFault,
31    /// CRC error
32    Crc,
33}
34
35#[doc(hidden)]
36mod private {
37    pub trait Sealed {}
38}
39
40/// SCK pin. This trait is sealed and cannot be implemented.
41pub trait SckPin<SPI>: private::Sealed {}
42/// MISO pin. This trait is sealed and cannot be implemented.
43pub trait MisoPin<SPI>: private::Sealed {}
44/// MOSI pin. This trait is sealed and cannot be implemented.
45pub trait MosiPin<SPI>: private::Sealed {}
46
47macro_rules! pins {
48    ($spi:ident, $af:literal, SCK: [$($sck:ident),*], MISO: [$($miso:ident),*], MOSI: [$($mosi:ident),*]) => {
49        $(
50            impl private::Sealed for $sck<Alternate<PushPull, $af>> {}
51            impl SckPin<$spi> for $sck<Alternate<PushPull, $af>> {}
52        )*
53        $(
54            impl private::Sealed for $miso<Alternate<PushPull, $af>> {}
55            impl MisoPin<$spi> for $miso<Alternate<PushPull, $af>> {}
56        )*
57        $(
58            impl private::Sealed for $mosi<Alternate<PushPull, $af>> {}
59            impl MosiPin<$spi> for $mosi<Alternate<PushPull, $af>> {}
60        )*
61    }
62}
63
64/// SPI peripheral operating in full duplex master mode
65pub struct Spi<SPI, PINS> {
66    spi: SPI,
67    pins: PINS,
68}
69
70macro_rules! hal {
71    ($($SPIX:ident: ($spiX:ident, $spiX_slave:ident, $pclkX:ident),)+) => {
72        $(
73            impl<SCK, MISO, MOSI> Spi<$SPIX, (SCK, MISO, MOSI)> {
74                /// Configures the SPI peripheral to operate in full duplex master mode
75                #[allow(unused_unsafe)]  // Necessary for stm32l4r9
76                pub fn $spiX(
77                    spi: $SPIX,
78                    pins: (SCK, MISO, MOSI),
79                    mode: Mode,
80                    freq: Hertz,
81                    clocks: Clocks,
82                    apb2: &mut <$SPIX as RccBus>::Bus,
83                ) -> Self
84                where
85                    SCK: SckPin<$SPIX>,
86                    MISO: MisoPin<$SPIX>,
87                    MOSI: MosiPin<$SPIX>,
88                {
89                    // enable or reset $SPIX
90                    <$SPIX>::enable(apb2);
91                    <$SPIX>::reset(apb2);
92
93                    // FRXTH: RXNE event is generated if the FIFO level is greater than or equal to
94                    //        8-bit
95                    // DS: 8-bit data size
96                    // SSOE: Slave Select output disabled
97                    spi.cr2
98                        .write(|w| unsafe {
99                            w.frxth().set_bit().ds().bits(0b111).ssoe().clear_bit()
100                        });
101
102                    let br = Self::compute_baud_rate(clocks.$pclkX(), freq);
103
104                    // CPHA: phase
105                    // CPOL: polarity
106                    // MSTR: master mode
107                    // BR: 1 MHz
108                    // SPE: SPI disabled
109                    // LSBFIRST: MSB first
110                    // SSM: enable software slave management (NSS pin free for other uses)
111                    // SSI: set nss high = master mode
112                    // CRCEN: hardware CRC calculation disabled
113                    // BIDIMODE: 2 line unidirectional (full duplex)
114                    spi.cr1.write(|w| unsafe {
115                        w.cpha()
116                            .bit(mode.phase == Phase::CaptureOnSecondTransition)
117                            .cpol()
118                            .bit(mode.polarity == Polarity::IdleHigh)
119                            .mstr()
120                            .set_bit()
121                            .br()
122                            .bits(br)
123                            .spe()
124                            .set_bit()
125                            .lsbfirst()
126                            .clear_bit()
127                            .ssi()
128                            .set_bit()
129                            .ssm()
130                            .set_bit()
131                            .crcen()
132                            .clear_bit()
133                            .bidimode()
134                            .clear_bit()
135                    });
136
137                    Spi { spi, pins }
138                }
139
140                pub fn $spiX_slave(spi: $SPIX, pins: (SCK, MISO, MOSI), mode: Mode, apb2: &mut <$SPIX as RccBus>::Bus) -> Self
141                where
142                    SCK: SckPin<$SPIX>,
143                    MISO: MisoPin<$SPIX>,
144                    MOSI: MosiPin<$SPIX>,
145                {
146                    // enable or reset $SPIX
147                    <$SPIX>::enable(apb2);
148                    <$SPIX>::reset(apb2);
149
150                    // CPOL: polarity
151                    // CPHA: phase
152                    // BIDIMODE: 2 line unidirectional (full duplex)
153                    // LSBFIRST: MSB first
154                    // CRCEN: hardware CRC calculation disabled
155                    // MSTR: master mode
156                    // SSM: disable software slave management (NSS pin not free for other uses)
157                    // SPE: SPI disabled
158                    spi.cr1.write(|w| {
159                        w.cpol()
160                            .bit(mode.polarity == Polarity::IdleHigh)
161                            .cpha()
162                            .bit(mode.phase == Phase::CaptureOnSecondTransition)
163                            .bidimode()
164                            .clear_bit()
165                            .lsbfirst()
166                            .clear_bit()
167                            .crcen()
168                            .clear_bit()
169                            .ssm()
170                            .clear_bit()
171                            .mstr()
172                            .clear_bit()
173                    });
174
175                    // DS: 8-bit data size
176                    // FRXTH: RXNE event is generated if the FIFO level is greater than or equal to
177                    //        8-bit
178                    spi.cr2
179                        .write(|w| unsafe { w.ds().bits(0b111).frxth().set_bit() });
180
181                    // SPE: SPI enabled
182                    spi.cr1.write(|w| w.spe().set_bit());
183
184                    Spi { spi, pins }
185                }
186
187                pub fn clear_overrun(&mut self) {
188                    self.spi.dr.read().dr();
189                    self.spi.sr.read().ovr();
190                }
191
192                /// Change the baud rate of the SPI
193                #[allow(unused_unsafe)]  // Necessary for stm32l4r9
194                pub fn reclock(&mut self, freq: Hertz, clocks: Clocks) {
195                    self.spi.cr1.modify(|_, w| w.spe().clear_bit());
196                    self.spi.cr1.modify(|_, w| unsafe {
197                        w.br().bits(Self::compute_baud_rate(clocks.$pclkX(), freq));
198                        w.spe().set_bit()
199                    });
200                }
201
202                fn compute_baud_rate(clocks: Hertz, freq: Hertz) -> u8 {
203                    match clocks / freq {
204                        0 => unreachable!(),
205                        1..=2 => 0b000,
206                        3..=5 => 0b001,
207                        6..=11 => 0b010,
208                        12..=23 => 0b011,
209                        24..=39 => 0b100,
210                        40..=95 => 0b101,
211                        96..=191 => 0b110,
212                        _ => 0b111,
213                    }
214                }
215
216                /// Releases the SPI peripheral and associated pins
217                pub fn free(self) -> ($SPIX, (SCK, MISO, MOSI)) {
218                    (self.spi, self.pins)
219                }
220            }
221
222            impl<PINS> FullDuplex<u8> for Spi<$SPIX, PINS> {
223                type Error = Error;
224
225                fn read(&mut self) -> nb::Result<u8, Error> {
226                    let sr = self.spi.sr.read();
227
228                    Err(if sr.ovr().bit_is_set() {
229                        nb::Error::Other(Error::Overrun)
230                    } else if sr.modf().bit_is_set() {
231                        nb::Error::Other(Error::ModeFault)
232                    } else if sr.crcerr().bit_is_set() {
233                        nb::Error::Other(Error::Crc)
234                    } else if sr.rxne().bit_is_set() {
235                        // NOTE(read_volatile) read only 1 byte (the svd2rust API only allows
236                        // reading a half-word)
237                        return Ok(unsafe {
238                            ptr::read_volatile(&self.spi.dr as *const _ as *const u8)
239                        });
240                    } else {
241                        nb::Error::WouldBlock
242                    })
243                }
244
245                fn send(&mut self, byte: u8) -> nb::Result<(), Error> {
246                    let sr = self.spi.sr.read();
247
248                    Err(if sr.ovr().bit_is_set() {
249                        nb::Error::Other(Error::Overrun)
250                    } else if sr.modf().bit_is_set() {
251                        nb::Error::Other(Error::ModeFault)
252                    } else if sr.crcerr().bit_is_set() {
253                        nb::Error::Other(Error::Crc)
254                    } else if sr.txe().bit_is_set() {
255                        // NOTE(write_volatile) see note above
256                        unsafe { ptr::write_volatile(&self.spi.dr as *const _ as *mut u8, byte) }
257                        return Ok(());
258                    } else {
259                        nb::Error::WouldBlock
260                    })
261                }
262            }
263
264            impl<PINS> crate::hal::blocking::spi::transfer::Default<u8> for Spi<$SPIX, PINS> {}
265
266            impl<PINS> crate::hal::blocking::spi::write::Default<u8> for Spi<$SPIX, PINS> {}
267        )+
268    }
269}
270
271use crate::gpio::gpiod::*;
272#[cfg(any(
273    // feature = "stm32l471",  // missing PAC support for Port G
274    feature = "stm32l475",
275    feature = "stm32l476",
276    feature = "stm32l485",
277    feature = "stm32l486",
278    feature = "stm32l496",
279    feature = "stm32l4a6",
280    // feature = "stm32l4p5",
281    // feature = "stm32l4q5",
282    // feature = "stm32l4r5",
283    // feature = "stm32l4s5",
284    // feature = "stm32l4r7",
285    // feature = "stm32l4s7",
286    feature = "stm32l4r9",
287    feature = "stm32l4s9",
288))]
289use crate::gpio::gpiog::*;
290use crate::gpio::{gpioa::*, gpiob::*, gpioc::*, gpioe::*};
291
292use crate::stm32::SPI1;
293hal! {
294    SPI1: (spi1, spi1_slave, pclk2),
295}
296
297pins!(SPI1, 5,
298    SCK: [PA5, PB3, PE13],
299    MISO: [PA6, PB4, PE14],
300    MOSI: [PA7, PB5, PE15]);
301
302#[cfg(any(
303    // feature = "stm32l471", // missing PAC support for Port G
304    feature = "stm32l475",
305    feature = "stm32l476",
306    feature = "stm32l485",
307    feature = "stm32l486",
308    feature = "stm32l496",
309    feature = "stm32l4a6",
310    // feature = "stm32l4p5",
311    // feature = "stm32l4q5",
312    // feature = "stm32l4r5",
313    // feature = "stm32l4s5",
314    // feature = "stm32l4r7",
315    // feature = "stm32l4s7",
316    feature = "stm32l4r9",
317    feature = "stm32l4s9",
318))]
319pins!(SPI1, 5, SCK: [PG2], MISO: [PG3], MOSI: [PG4]);
320
321#[cfg(not(any(feature = "stm32l433", feature = "stm32l443",)))]
322use crate::stm32::SPI3;
323
324#[cfg(not(any(feature = "stm32l433", feature = "stm32l443",)))]
325hal! {
326    SPI3: (spi3, spi3_slave, pclk1),
327}
328
329#[cfg(not(any(feature = "stm32l433", feature = "stm32l443",)))]
330pins!(SPI3, 6,
331    SCK: [PB3, PC10],
332    MISO: [PB4, PC11],
333    MOSI: [PB5, PC12]);
334
335#[cfg(any(
336    // feature = "stm32l471", // missing PAC support for Port G
337    feature = "stm32l475",
338    feature = "stm32l476",
339    feature = "stm32l485",
340    feature = "stm32l486",
341    feature = "stm32l496",
342    feature = "stm32l4a6",
343    // feature = "stm32l4p5",
344    // feature = "stm32l4q5",
345    // feature = "stm32l4r5",
346    // feature = "stm32l4s5",
347    // feature = "stm32l4r7",
348    // feature = "stm32l4s7",
349    feature = "stm32l4r9",
350    feature = "stm32l4s9",
351))]
352pins!(SPI3, 6, SCK: [PG9], MISO: [PG10], MOSI: [PG11]);
353
354use crate::stm32::SPI2;
355
356hal! {
357    SPI2: (spi2, spi2_slave, pclk1),
358}
359
360pins!(SPI2, 5,
361    SCK: [PB13, PB10, PD1],
362    MISO: [PB14, PC2, PD3],
363    MOSI: [PB15, PC3, PD4]);
364
365pub struct SpiPayload<SPI, PINS> {
366    spi: Spi<SPI, PINS>,
367}
368
369pub type SpiRxDma<SPI, PINS, CHANNEL> = dma::RxDma<SpiPayload<SPI, PINS>, CHANNEL>;
370
371pub type SpiTxDma<SPI, PINS, CHANNEL> = dma::TxDma<SpiPayload<SPI, PINS>, CHANNEL>;
372
373pub type SpiRxTxDma<SPI, PINS, RXCH, TXCH> = dma::RxTxDma<SpiPayload<SPI, PINS>, RXCH, TXCH>;
374
375macro_rules! spi_dma {
376    ($SPIX:ident, $RX_CH:path, $RX_CHSEL:path, $TX_CH:path, $TX_CHSEL:path) => {
377        impl<PINS> dma::Receive for SpiRxDma<$SPIX, PINS, $RX_CH> {
378            type RxChannel = $RX_CH;
379            type TransmittedWord = u8;
380        }
381
382        impl<PINS> dma::Transmit for SpiTxDma<$SPIX, PINS, $TX_CH> {
383            type TxChannel = $TX_CH;
384            type ReceivedWord = u8;
385        }
386
387        impl<PINS> dma::ReceiveTransmit for SpiRxTxDma<$SPIX, PINS, $RX_CH, $TX_CH> {
388            type RxChannel = $RX_CH;
389            type TxChannel = $TX_CH;
390            type TransferedWord = u8;
391        }
392
393        impl<PINS> Spi<$SPIX, PINS> {
394            pub fn with_rx_dma(self, mut channel: $RX_CH) -> SpiRxDma<$SPIX, PINS, $RX_CH> {
395                let payload = SpiPayload { spi: self };
396
397                // Perform one-time setup actions to keep the work minimal when using the driver.
398
399                channel.set_peripheral_address(
400                    unsafe { &(*$SPIX::ptr()).dr as *const _ as u32 },
401                    false,
402                );
403                channel.set_request_line($RX_CHSEL).unwrap();
404                channel.ccr().modify(|_, w| {
405                    w
406                        // memory to memory mode disabled
407                        .mem2mem()
408                        .clear_bit()
409                        // medium channel priority level
410                        .pl()
411                        .medium()
412                        // 8-bit memory size
413                        .msize()
414                        .bits8()
415                        // 8-bit peripheral size
416                        .psize()
417                        .bits8()
418                        // circular mode disabled
419                        .circ()
420                        .clear_bit()
421                        // write to memory
422                        .dir()
423                        .clear_bit()
424                });
425
426                SpiRxDma { payload, channel }
427            }
428
429            pub fn with_tx_dma(self, mut channel: $TX_CH) -> SpiTxDma<$SPIX, PINS, $TX_CH> {
430                let payload = SpiPayload { spi: self };
431
432                // Perform one-time setup actions to keep the work minimal when using the driver.
433
434                channel.set_peripheral_address(
435                    unsafe { &(*$SPIX::ptr()).dr as *const _ as u32 },
436                    false,
437                );
438                channel.set_request_line($TX_CHSEL).unwrap();
439                channel.ccr().modify(|_, w| {
440                    w
441                        // memory to memory mode disabled
442                        .mem2mem()
443                        .clear_bit()
444                        // medium channel priority level
445                        .pl()
446                        .medium()
447                        // 8-bit memory size
448                        .msize()
449                        .bits8()
450                        // 8-bit peripheral size
451                        .psize()
452                        .bits8()
453                        // circular mode disabled
454                        .circ()
455                        .clear_bit()
456                        // write to peripheral
457                        .dir()
458                        .set_bit()
459                });
460
461                SpiTxDma { payload, channel }
462            }
463
464            pub fn with_rxtx_dma(
465                self,
466                mut rx_channel: $RX_CH,
467                mut tx_channel: $TX_CH,
468            ) -> SpiRxTxDma<$SPIX, PINS, $RX_CH, $TX_CH> {
469                let payload = SpiPayload { spi: self };
470
471                // Perform one-time setup actions to keep the work minimal when using the driver.
472
473                //
474                // Setup RX channel
475                //
476                rx_channel.set_peripheral_address(
477                    unsafe { &(*$SPIX::ptr()).dr as *const _ as u32 },
478                    false,
479                );
480                rx_channel.set_request_line($RX_CHSEL).unwrap();
481
482                rx_channel.ccr().modify(|_, w| {
483                    w
484                        // memory to memory mode disabled
485                        .mem2mem()
486                        .clear_bit()
487                        // medium channel priority level
488                        .pl()
489                        .medium()
490                        // 8-bit memory size
491                        .msize()
492                        .bits8()
493                        // 8-bit peripheral size
494                        .psize()
495                        .bits8()
496                        // circular mode disabled
497                        .circ()
498                        .clear_bit()
499                        // write to memory
500                        .dir()
501                        .clear_bit()
502                });
503
504                //
505                // Setup TX channel
506                //
507                tx_channel.set_peripheral_address(
508                    unsafe { &(*$SPIX::ptr()).dr as *const _ as u32 },
509                    false,
510                );
511                tx_channel.set_request_line($TX_CHSEL).unwrap();
512
513                tx_channel.ccr().modify(|_, w| {
514                    w
515                        // memory to memory mode disabled
516                        .mem2mem()
517                        .clear_bit()
518                        // medium channel priority level
519                        .pl()
520                        .medium()
521                        // 8-bit memory size
522                        .msize()
523                        .bits8()
524                        // 8-bit peripheral size
525                        .psize()
526                        .bits8()
527                        // circular mode disabled
528                        .circ()
529                        .clear_bit()
530                        // write to peripheral
531                        .dir()
532                        .set_bit()
533                });
534
535                SpiRxTxDma {
536                    payload,
537                    rx_channel,
538                    tx_channel,
539                }
540            }
541        }
542
543        impl<PINS> SpiRxDma<$SPIX, PINS, $RX_CH> {
544            pub fn split(mut self) -> (Spi<$SPIX, PINS>, $RX_CH) {
545                self.stop();
546                (self.payload.spi, self.channel)
547            }
548        }
549
550        impl<PINS> SpiTxDma<$SPIX, PINS, $TX_CH> {
551            pub fn split(mut self) -> (Spi<$SPIX, PINS>, $TX_CH) {
552                self.stop();
553                (self.payload.spi, self.channel)
554            }
555        }
556
557        impl<PINS> SpiRxTxDma<$SPIX, PINS, $RX_CH, $TX_CH> {
558            pub fn split(mut self) -> (Spi<$SPIX, PINS>, $RX_CH, $TX_CH) {
559                self.stop();
560                (self.payload.spi, self.rx_channel, self.tx_channel)
561            }
562        }
563
564        impl<PINS> dma::TransferPayload for SpiRxDma<$SPIX, PINS, $RX_CH> {
565            fn start(&mut self) {
566                // Setup DMA channels in accordance with RM 40.4.9, subheading "Communication using
567                // DMA (direct memory addressing)".
568                // It is mandatory to follow these steps in order:
569                //
570                // 0. SPI disabled during setup.
571                // 1. Enable DMA Rx buffer in the RXDMAEN bit in the SPI_CR2 register, if DMA Rx is used.
572                // 2. Enable DMA streams for Tx and Rx in DMA registers, if the streams are used.
573                // 3. Enable DMA Tx buffer in the TXDMAEN bit in the SPI_CR2 register, if DMA Tx is used.
574                // 4. Enable the SPI by setting the SPE bit.
575                self.payload.spi.spi.cr1.modify(|_, w| w.spe().clear_bit()); // 0.
576                self.payload
577                    .spi
578                    .spi
579                    .cr2
580                    .modify(|_, w| w.rxdmaen().set_bit()); // 1.
581                self.channel.start(); // 2.
582                self.payload.spi.spi.cr1.modify(|_, w| w.spe().set_bit()); // 4.
583            }
584
585            fn stop(&mut self) {
586                // Stop DMA channels in accordance with RM 40.4.9, subheading "Communication using
587                // DMA (direct memory addressing)".
588                // It is mandatory to follow these steps in order:
589                //
590                // 1. Disable DMA streams for Tx and Rx in the DMA registers, if the streams are used.
591                // 2. Disable the SPI by following the SPI disable procedure.
592                // 3. Disable DMA Tx and Rx buffers by clearing the TXDMAEN and RXDMAEN bits in the
593                //    SPI_CR2 register, if DMA Tx and/or DMA Rx are used.
594                self.channel.stop(); // 1.
595                self.payload.spi.spi.cr1.modify(|_, w| w.spe().clear_bit()); // 2.
596                self.payload
597                    .spi
598                    .spi
599                    .cr2
600                    .modify(|_, w| w.rxdmaen().clear_bit()); // 3.
601            }
602        }
603
604        impl<PINS> dma::TransferPayload for SpiTxDma<$SPIX, PINS, $TX_CH> {
605            fn start(&mut self) {
606                // Setup DMA channels in accordance with RM 40.4.9, subheading "Communication using
607                // DMA (direct memory addressing)".
608                // It is mandatory to follow these steps in order:
609                //
610                // 0. SPI disabled during setup.
611                // 1. Enable DMA Rx buffer in the RXDMAEN bit in the SPI_CR2 register, if DMA Rx is used.
612                // 2. Enable DMA streams for Tx and Rx in DMA registers, if the streams are used.
613                // 3. Enable DMA Tx buffer in the TXDMAEN bit in the SPI_CR2 register, if DMA Tx is used.
614                // 4. Enable the SPI by setting the SPE bit.
615                self.payload.spi.spi.cr1.modify(|_, w| w.spe().clear_bit()); // 0.
616                self.channel.start(); // 2.
617                self.payload
618                    .spi
619                    .spi
620                    .cr2
621                    .modify(|_, w| w.txdmaen().set_bit()); // 3.
622                self.payload.spi.spi.cr1.modify(|_, w| w.spe().set_bit()); // 4.
623            }
624
625            fn stop(&mut self) {
626                // Stop DMA channels in accordance with RM 40.4.9, subheading "Communication using
627                // DMA (direct memory addressing)".
628                // It is mandatory to follow these steps in order:
629                //
630                // 1. Disable DMA streams for Tx and Rx in the DMA registers, if the streams are used.
631                // 2. Disable the SPI by following the SPI disable procedure.
632                // 3. Disable DMA Tx and Rx buffers by clearing the TXDMAEN and RXDMAEN bits in the
633                //    SPI_CR2 register, if DMA Tx and/or DMA Rx are used.
634                self.channel.stop(); // 1.
635                self.payload.spi.spi.cr1.modify(|_, w| w.spe().clear_bit()); // 2.
636                self.payload
637                    .spi
638                    .spi
639                    .cr2
640                    .modify(|_, w| w.txdmaen().clear_bit()); // 3.
641            }
642        }
643
644        impl<PINS> dma::TransferPayload for SpiRxTxDma<$SPIX, PINS, $RX_CH, $TX_CH> {
645            fn start(&mut self) {
646                // Setup DMA channels in accordance with RM 40.4.9, subheading "Communication using
647                // DMA (direct memory addressing)".
648                // It is mandatory to follow these steps in order:
649                //
650                // 0. SPI disabled during setup.
651                // 1. Enable DMA Rx buffer in the RXDMAEN bit in the SPI_CR2 register, if DMA Rx is used.
652                // 2. Enable DMA streams for Tx and Rx in DMA registers, if the streams are used.
653                // 3. Enable DMA Tx buffer in the TXDMAEN bit in the SPI_CR2 register, if DMA Tx is used.
654                // 4. Enable the SPI by setting the SPE bit.
655                self.payload.spi.spi.cr1.modify(|_, w| w.spe().clear_bit()); // 0.
656                self.payload
657                    .spi
658                    .spi
659                    .cr2
660                    .modify(|_, w| w.rxdmaen().set_bit()); // 1.
661                self.rx_channel.start(); // 2.
662                self.tx_channel.start(); // 2.
663                self.payload
664                    .spi
665                    .spi
666                    .cr2
667                    .modify(|_, w| w.txdmaen().set_bit()); // 3.
668                self.payload.spi.spi.cr1.modify(|_, w| w.spe().set_bit()); // 4.
669            }
670
671            fn stop(&mut self) {
672                // Stop DMA channels in accordance with RM 40.4.9, subheading "Communication using
673                // DMA (direct memory addressing)".
674                // It is mandatory to follow these steps in order:
675                //
676                // 1. Disable DMA streams for Tx and Rx in the DMA registers, if the streams are used.
677                // 2. Disable the SPI by following the SPI disable procedure.
678                // 3. Disable DMA Tx and Rx buffers by clearing the TXDMAEN and RXDMAEN bits in the
679                //    SPI_CR2 register, if DMA Tx and/or DMA Rx are used.
680                self.tx_channel.stop(); // 1.
681                self.rx_channel.stop(); // 1.
682                self.payload.spi.spi.cr1.modify(|_, w| w.spe().clear_bit()); // 2.
683                self.payload
684                    .spi
685                    .spi
686                    .cr2
687                    .modify(|_, w| w.rxdmaen().clear_bit().txdmaen().clear_bit()); // 3.
688            }
689        }
690
691        impl<B, PINS> dma::ReadDma<B, u8> for SpiRxDma<$SPIX, PINS, $RX_CH>
692        where
693            B: StaticWriteBuffer<Word = u8>,
694        {
695            fn read(mut self, mut buffer: B) -> dma::Transfer<dma::W, B, Self> {
696                // Setup DMA channels in accordance with RM 40.4.9, subheading "Communication using
697                // DMA (direct memory addressing)"
698
699                // NOTE(unsafe) We own the buffer now and we won't call other `&mut` on it
700                // until the end of the transfer.
701                let (ptr, len) = unsafe { buffer.static_write_buffer() };
702
703                // Setup RX channel addresses and length
704                self.channel.set_memory_address(ptr as u32, true);
705                self.channel.set_transfer_length(len as u16);
706
707                // Fences and start
708                atomic::compiler_fence(Ordering::Release);
709                self.start();
710
711                dma::Transfer::w(buffer, self)
712            }
713        }
714
715        impl<B, PINS> dma::WriteDma<B, u8> for SpiTxDma<$SPIX, PINS, $TX_CH>
716        where
717            B: StaticReadBuffer<Word = u8>,
718        {
719            fn write(mut self, buffer: B) -> dma::Transfer<dma::R, B, Self> {
720                // Setup DMA channels in accordance with RM 40.4.9, subheading "Communication using
721                // DMA (direct memory addressing)"
722
723                // NOTE(unsafe) We own the buffer now and we won't call other `&mut` on it
724                // until the end of the transfer.
725                let (ptr, len) = unsafe { buffer.static_read_buffer() };
726
727                // Setup TX channel addresses and length
728                self.channel.set_memory_address(ptr as u32, true);
729                self.channel.set_transfer_length(len as u16);
730
731                // Fences and start
732                atomic::compiler_fence(Ordering::Release);
733                self.start();
734
735                dma::Transfer::r(buffer, self)
736            }
737        }
738
739        impl<B, PINS> dma::TransferDma<B, u8> for SpiRxTxDma<$SPIX, PINS, $RX_CH, $TX_CH>
740        where
741            B: StaticWriteBuffer<Word = u8>,
742        {
743            fn transfer(mut self, mut buffer: B) -> dma::Transfer<dma::RW, B, Self> {
744                // Setup DMA channels in accordance with RM 40.4.9, subheading "Communication using
745                // DMA (direct memory addressing)"
746
747                // Transfer: we use the same buffer for RX and TX
748
749                // NOTE(unsafe) We own the buffer now and we won't call other `&mut` on it
750                // until the end of the transfer.
751                let (ptr, len) = unsafe { buffer.static_write_buffer() };
752
753                // Setup RX channel addresses and length
754                self.rx_channel.set_memory_address(ptr as u32, true);
755                self.rx_channel.set_transfer_length(len as u16);
756
757                // Setup TX channel addresses and length
758                self.tx_channel.set_memory_address(ptr as u32, true);
759                self.tx_channel.set_transfer_length(len as u16);
760
761                // Fences and start
762                atomic::compiler_fence(Ordering::Release);
763                self.start();
764
765                dma::Transfer::rw(buffer, self)
766            }
767        }
768    };
769}
770
771spi_dma!(SPI1, dma1::C2, DmaInput::Spi1Rx, dma1::C3, DmaInput::Spi1Tx);
772#[cfg(not(any(
773    feature = "stm32l412",
774    feature = "stm32l422",
775    feature = "stm32l432",
776    feature = "stm32l442",
777    feature = "stm32l452",
778    feature = "stm32l462",
779)))]
780spi_dma!(SPI2, dma1::C4, DmaInput::Spi2Rx, dma1::C5, DmaInput::Spi2Tx);
781// spi_dma!(SPI1, dma2::C3, c3s, map4, dma2::C4, c4s, map4);
782#[cfg(not(any(feature = "stm32l433", feature = "stm32l443",)))]
783spi_dma!(SPI3, dma2::C1, DmaInput::Spi3Rx, dma2::C2, DmaInput::Spi3Tx);