stm32_hal2/spi/
baseline.rs

1use core::{ops::Deref, ptr};
2
3use super::*;
4use crate::{
5    check_errors,
6    error::{Error, Result},
7    pac::{self, RCC},
8    util::{RccPeriph, bounded_loop},
9};
10
11// Depth of FIFO to use. See G4 RM, table 359.
12#[cfg(feature = "g4")]
13const FIFO_LEN: usize = 4;
14
15/// Possible interrupt types. Enable these in SPIx_CR2. Check and clear with SR. There is no explicit
16/// way to clear these.
17#[derive(Copy, Clone)]
18pub enum SpiInterrupt {
19    /// Tx buffer empty (TXEIE)
20    TxBufEmpty,
21    /// Rx buffer not empty (RXNEIE)
22    RxBufNotEmpty,
23    /// Error (ERRIE)
24    Error,
25}
26
27/// These bits configure the data length for SPI transfers. Sets `SPI_CR2` register, `DS` field.
28#[derive(Copy, Clone)]
29#[repr(u8)]
30pub enum DataSize {
31    D4 = 0b0011,
32    D5 = 0b0100,
33    D6 = 0b0101,
34    D7 = 0b0110,
35    D8 = 0b0111,
36    D9 = 0b1000,
37    D10 = 0b1001,
38    D11 = 0b1010,
39    D12 = 0b1011,
40    D13 = 0b1100,
41    D14 = 0b1101,
42    D15 = 0b1110,
43    D16 = 0b1111,
44}
45
46impl<R> Spi<R>
47where
48    R: Deref<Target = pac::spi1::RegisterBlock> + RccPeriph,
49{
50    /// Initialize an SPI peripheral, including configuration register writes, and enabling and resetting
51    /// its RCC peripheral clock.
52    pub fn new(regs: R, cfg: SpiConfig, baud_rate: BaudRate) -> Self {
53        let rcc = unsafe { &(*RCC::ptr()) };
54        R::en_reset(rcc);
55
56        // L44 RM, section 40.4.7: Configuration of SPI
57        // The configuration procedure is almost the same for master and slave. For specific mode
58        // setups, follow the dedicated sections. When a standard communication is to be initialized,
59        // perform these steps:
60
61        // 1. Write proper GPIO registers: Configure GPIO for MOSI, MISO and SCK pins.
62        // (Handled in GPIO modules and user code)
63
64        // 2. Write to the SPI_CR1 register:
65        regs.cr1().modify(|_, w| unsafe {
66            // a) Configure the serial clock baud rate using the BR[2:0] bits (Note: 4)
67            w.br().bits(baud_rate as u8);
68            // b) Configure the CPOL and CPHA bits combination to define one of the four
69            // relationships between the data transfer and the serial clock (CPHA must be
70            // cleared in NSSP mode). (Note: 2 - except the case when CRC is enabled at TI
71            // mode).
72            w.cpol().bit(cfg.mode.polarity as u8 != 0);
73            w.cpha().bit(cfg.mode.phase as u8 != 0);
74            // c) Select simplex or half-duplex mode by configuring RXONLY or BIDIMODE and
75            // BIDIOE (RXONLY and BIDIMODE can't be set at the same time).
76            w.bidimode().bit(cfg.comm_mode == SpiCommMode::HalfDuplex);
77            w.rxonly().bit(cfg.comm_mode == SpiCommMode::ReceiveOnly);
78            // d) Configure the LSBFIRST bit to define the frame format (Note: 2).
79            w.lsbfirst().clear_bit();
80            // e) Configure the CRCL and CRCEN bits if CRC is needed (while SCK clock signal is
81            // at idle state).
82            w.crcen().clear_bit();
83            // f) Configure SSM and SSI (Notes: 2 & 3).
84            w.ssm().bit(cfg.slave_select == SlaveSelect::Software);
85            w.ssi().bit(cfg.slave_select == SlaveSelect::Software);
86            // g) Configure the MSTR bit (in multimaster NSS configuration, avoid conflict state on
87            // NSS if master is configured to prevent MODF error).
88            w.mstr().bit(true);
89            w.spe().bit(true) // Enable SPI
90        });
91
92        // 3. Write to SPI_CR2 register:
93        #[cfg(feature = "f4")]
94        regs.cr2().modify(|_, w| {
95            w.ssoe()
96                .bit(cfg.slave_select == SlaveSelect::HardwareOutEnable)
97        });
98
99        #[cfg(not(feature = "f4"))]
100        regs.cr2().modify(|_, w| unsafe {
101            // a) Configure the DS[3:0] bits to select the data length for the transfer.
102            w.ds().bits(cfg.data_size as u8);
103            // b) Configure SSOE (Notes: 1 & 2 & 3).
104            w.ssoe()
105                .bit(cfg.slave_select == SlaveSelect::HardwareOutEnable);
106            // e) Configure the FRXTH bit. The RXFIFO threshold must be aligned to the read
107            // access size for the SPIx_DR register.
108            w.frxth().bit(cfg.fifo_reception_thresh as u8 != 0)
109        });
110
111        // c) Set the FRF bit if the TI protocol is required (keep NSSP bit cleared in TI mode).
112        // d) Set the NSSP bit if the NSS pulse mode between two data units is required (keep
113        // CHPA and TI bits cleared in NSSP mode).
114
115        // f) Initialize LDMA_TX and LDMA_RX bits if DMA is used in packed mode.
116        // 4. Write to SPI_CRCPR register: Configure the CRC polynomial if needed.
117        // 5. Write proper DMA registers: Configure DMA streams dedicated for SPI Tx and Rx in
118        // DMA registers if the DMA streams are used.
119
120        // todo: It sounds like you should enable and disable spi during writes, not on init!
121        // todo: This lets you use hardware CS management, and seems to be teh way the RM
122        // todo steers you towards regardless.
123
124        Self { regs, cfg }
125    }
126
127    /// Change the SPI baud rate.
128    pub fn reclock(&mut self, baud_rate: BaudRate) {
129        self.regs.cr1().modify(|_, w| w.spe().clear_bit());
130
131        self.regs.cr1().modify(|_, w| unsafe {
132            w.br().bits(baud_rate as u8);
133            w.spe().bit(true)
134        });
135    }
136
137    /// L44 RM, section 40.4.9: "Procedure for disabling the SPI"
138    /// When SPI is disabled, it is mandatory to follow the disable procedures described in this
139    /// paragraph. It is important to do this before the system enters a low-power mode when the
140    /// peripheral clock is stopped. Ongoing transactions can be corrupted in this case. In some
141    /// modes the disable procedure is the only way to stop continuous communication running.
142    pub fn disable(&mut self) -> Result<()> {
143        // The correct disable procedure is (except when receive only mode is used):
144
145        // 1. Wait until FTLVL[1:0] = 00 (no more data to transmit).
146        #[cfg(not(feature = "f4"))]
147        bounded_loop!(
148            self.regs.sr().read().ftlvl().bits() != 0,
149            Error::RegisterUnchanged
150        );
151        // 2. Wait until BSY=0 (the last data frame is processed).
152        bounded_loop!(
153            self.regs.sr().read().bsy().bit_is_set(),
154            Error::RegisterUnchanged
155        );
156        // 3. Disable the SPI (SPE=0).
157        // todo: Instructions say to stop SPI (including to close DMA comms), but this breaks non-DMA writes, which assume
158        // todo SPI is enabled, the way we structure things.
159        self.regs.cr1().modify(|_, w| w.spe().clear_bit());
160        // 4. Read data until FRLVL[1:0] = 00 (read all the received data).
161        // todo: make bounded
162        #[cfg(not(feature = "f4"))]
163        while self.regs.sr().read().frlvl().bits() != 0 {
164            unsafe { ptr::read_volatile(self.regs.dr().as_ptr() as *const u8) };
165        }
166
167        Ok(())
168    }
169
170    /// Read a single byte if available, or block until it's available.
171    pub fn read(&mut self) -> Result<u8> {
172        check_errors!(self.regs.sr().read());
173
174        // todo: Use fIFO like in H7 code?
175
176        bounded_loop!(
177            !self.regs.sr().read().rxne().bit_is_set(),
178            Error::RegisterUnchanged
179        );
180
181        Ok(unsafe { ptr::read_volatile(self.regs.dr().as_ptr() as *const u8) })
182    }
183
184    /// Write a single byte if available, or block until it's available.
185    /// See L44 RM, section 40.4.9: Data transmission and reception procedures.
186    pub fn write_one(&mut self, byte: u8) -> Result<()> {
187        check_errors!(self.regs.sr().read());
188
189        bounded_loop!(
190            !self.regs.sr().read().txe().bit_is_set(),
191            Error::RegisterUnchanged
192        );
193
194        #[allow(invalid_reference_casting)]
195        unsafe {
196            ptr::write_volatile(self.regs.dr().as_ptr() as *mut u8, byte)
197        };
198
199        Ok(())
200    }
201
202    /// Write multiple bytes on the SPI line, blocking until complete.
203    /// See L44 RM, section 40.4.9: Data transmission and reception procedures.
204    pub fn write(&mut self, words: &[u8]) -> Result<()> {
205        // todo: Take advantage of the FIFO, like H7?
206        for word in words {
207            self.write_one(*word)?;
208            self.read()?;
209        }
210
211        Ok(())
212    }
213
214    /// Read multiple bytes to a buffer, blocking until complete.
215    /// See L44 RM, section 40.4.9: Data transmission and reception procedures.
216    pub fn transfer<'w>(&mut self, words: &'w mut [u8]) -> Result<()> {
217        for word in words.iter_mut() {
218            self.write_one(*word)?;
219            *word = self.read()?;
220        }
221
222        Ok(())
223    }
224
225    /// An alternative transfer API, using separate read and write buffers.
226    pub fn transfer_type2<'w>(
227        &mut self,
228        write_buf: &'w [u8],
229        read_buf: &'w mut [u8],
230    ) -> Result<()> {
231        for (i, word) in write_buf.iter().enumerate() {
232            self.write_one(*word)?;
233            if i < read_buf.len() {
234                read_buf[i] = self.read()?;
235            }
236        }
237
238        // for (i_read, word) in read_buf.iter().enumerate() {
239        //     self.write_one(*word)?;
240        //     read_buf[i_write] = self.read()?;
241        //     println!("read: {}", read_buf[i_write]);
242        // }
243
244        // for (i_write, word) in write_buf.iter().enumerate() {
245        //     self.write_one(*word)?;
246        //     let i_read = i + write_buf.len();
247        //
248        //     if i_write >= write_buf.len() - 1 {
249        //         // let i_read = i_write - write_buf.len() + 1;
250        //         read_buf[i_read] = self.read()?;
251        //         println!("read. i:{} v:{:?}", i_read, read_buf[i_read]);
252        //         i_read += 1;
253        //     }
254        // }
255
256        Ok(())
257    }
258
259    /// Receive data using DMA. See L44 RM, section 40.4.9: Communication using DMA.
260    /// Note that the `channel` argument is unused on F3 and L4, since it is hard-coded,
261    /// and can't be configured using the DMAMUX peripheral. (`dma::mux()` fn).
262    #[cfg(not(any(feature = "f4", feature = "l552")))]
263    pub unsafe fn read_dma(
264        &mut self,
265        buf: &mut [u8],
266        channel: DmaChannel,
267        channel_cfg: ChannelCfg,
268        dma_periph: dma::DmaPeriph,
269    ) -> Result<()> {
270        // todo: Accept u16 words too.
271        let (ptr, len) = (buf.as_mut_ptr(), buf.len());
272
273        self.regs.cr1().modify(|_, w| w.spe().clear_bit());
274        self.regs.cr2().modify(|_, w| w.rxdmaen().bit(true));
275
276        #[cfg(any(feature = "f3", feature = "l4"))]
277        let channel = R::read_chan();
278        #[cfg(feature = "l4")]
279        let mut dma_regs = unsafe { &(*DMA1::ptr()) }; // todo: Hardcoded DMA1
280        #[cfg(feature = "l4")]
281        R::write_sel(&mut dma_regs);
282
283        let periph_addr = self.regs.dr().as_ptr() as u32;
284        let num_data = len as u32;
285
286        match dma_periph {
287            dma::DmaPeriph::Dma1 => {
288                let mut regs = unsafe { &(*DMA1::ptr()) };
289                dma::cfg_channel(
290                    &mut regs,
291                    channel,
292                    periph_addr,
293                    ptr as u32,
294                    num_data,
295                    dma::Direction::ReadFromPeriph,
296                    dma::DataSize::S8,
297                    dma::DataSize::S8,
298                    channel_cfg,
299                )?;
300            }
301            #[cfg(dma2)]
302            dma::DmaPeriph::Dma2 => {
303                let mut regs = unsafe { &(*pac::DMA2::ptr()) };
304                dma::cfg_channel(
305                    &mut regs,
306                    channel,
307                    periph_addr,
308                    ptr as u32,
309                    num_data,
310                    dma::Direction::ReadFromPeriph,
311                    dma::DataSize::S8,
312                    dma::DataSize::S8,
313                    channel_cfg,
314                )?;
315            }
316        }
317
318        self.regs.cr1().modify(|_, w| w.spe().bit(true));
319
320        Ok(())
321    }
322
323    /// Transmit data using DMA. See L44 RM, section 40.4.9: Communication using DMA.
324    /// Note that the `channel` argument is unused on F3 and L4, since it is hard-coded,
325    /// and can't be configured using the DMAMUX peripheral. (`dma::mux()` fn).
326    #[cfg(not(any(feature = "f4", feature = "l552")))]
327    pub unsafe fn write_dma(
328        &mut self,
329        buf: &[u8],
330        channel: DmaChannel,
331        channel_cfg: ChannelCfg,
332        dma_periph: dma::DmaPeriph,
333    ) -> Result<()> {
334        // Static write and read buffers?
335        let (ptr, len) = (buf.as_ptr(), buf.len());
336
337        self.regs.cr1().modify(|_, w| w.spe().clear_bit());
338
339        // todo: Accept u16 words too.
340
341        // A DMA access is requested when the TXE or RXNE enable bit in the SPIx_CR2 register is
342        // set. Separate requests must be issued to the Tx and Rx buffers.
343        // In transmission, a DMA request is issued each time TXE is set to 1. The DMA then
344        // writes to the SPIx_DR register.
345
346        // When starting communication using DMA, to prevent DMA channel management raising
347        // error events, these steps must be followed in order:
348        //
349        // 1. Enable DMA Rx buffer in the RXDMAEN bit in the SPI_CR2 register, if DMA Rx is
350        // used.
351        // (N/A)
352
353        // 2. Enable DMA streams for Tx and Rx in DMA registers, if the streams are used.
354        #[cfg(any(feature = "f3", feature = "l4"))]
355        let channel = R::write_chan();
356        #[cfg(feature = "l4")]
357        let mut dma_regs = unsafe { &(*DMA1::ptr()) }; // todo: Hardcoded DMA1
358        #[cfg(feature = "l4")]
359        R::write_sel(&mut dma_regs);
360
361        let periph_addr = self.regs.dr().as_ptr() as u32;
362        let num_data = len as u32;
363
364        match dma_periph {
365            dma::DmaPeriph::Dma1 => {
366                let mut regs = unsafe { &(*DMA1::ptr()) };
367                dma::cfg_channel(
368                    &mut regs,
369                    channel,
370                    periph_addr,
371                    ptr as u32,
372                    num_data,
373                    dma::Direction::ReadFromMem,
374                    dma::DataSize::S8,
375                    dma::DataSize::S8,
376                    channel_cfg,
377                )?;
378            }
379            #[cfg(dma2)]
380            dma::DmaPeriph::Dma2 => {
381                let mut regs = unsafe { &(*pac::DMA2::ptr()) };
382                dma::cfg_channel(
383                    &mut regs,
384                    channel,
385                    periph_addr,
386                    ptr as u32,
387                    num_data,
388                    dma::Direction::ReadFromMem,
389                    dma::DataSize::S8,
390                    dma::DataSize::S8,
391                    channel_cfg,
392                )?;
393            }
394        }
395
396        // 3. Enable DMA Tx buffer in the TXDMAEN bit in the SPI_CR2 register, if DMA Tx is used.
397        self.regs.cr2().modify(|_, w| w.txdmaen().bit(true));
398
399        // 4. Enable the SPI by setting the SPE bit.
400        self.regs.cr1().modify(|_, w| w.spe().bit(true));
401
402        Ok(())
403    }
404
405    /// Transfer data from DMA; this is the basic reading API, using both write and read transfers:
406    /// It performs a write with register data, and reads to a buffer.
407    #[cfg(not(any(feature = "f4", feature = "l552")))]
408    pub unsafe fn transfer_dma(
409        &mut self,
410        buf_write: &[u8],
411        buf_read: &mut [u8],
412        channel_write: DmaChannel,
413        channel_read: DmaChannel,
414        channel_cfg_write: ChannelCfg,
415        channel_cfg_read: ChannelCfg,
416        dma_periph: dma::DmaPeriph,
417    ) -> Result<()> {
418        // todo: Accept u16 words too.
419        let (ptr_write, len_write) = (buf_write.as_ptr(), buf_write.len());
420        let (ptr_read, len_read) = (buf_read.as_mut_ptr(), buf_read.len());
421
422        self.regs.cr1().modify(|_, w| w.spe().clear_bit());
423
424        // todo: DRY here, with `write_dma`, and `read_dma`.
425
426        let periph_addr_write = self.regs.dr().as_ptr() as u32;
427        let periph_addr_read = self.regs.dr().as_ptr() as u32;
428
429        let num_data_write = len_write as u32;
430        let num_data_read = len_read as u32;
431
432        // Be careful - order of enabling Rx and Tx may matter, along with other things like when we
433        // enable the channels, and the SPI periph.
434        self.regs.cr2().modify(|_, w| w.rxdmaen().bit(true));
435
436        #[cfg(any(feature = "f3", feature = "l4"))]
437        let channel_write = R::write_chan();
438        #[cfg(feature = "l4")]
439        let mut dma_regs = unsafe { &(*DMA1::ptr()) }; // todo: Hardcoded DMA1
440        #[cfg(feature = "l4")]
441        R::write_sel(&mut dma_regs);
442
443        #[cfg(any(feature = "f3", feature = "l4"))]
444        let channel_read = R::read_chan();
445        #[cfg(feature = "l4")]
446        let mut dma_regs = unsafe { &(*DMA1::ptr()) }; // todo: Hardcoded DMA1
447        #[cfg(feature = "l4")]
448        R::write_sel(&mut dma_regs);
449        match dma_periph {
450            dma::DmaPeriph::Dma1 => {
451                let mut regs = unsafe { &(*DMA1::ptr()) };
452                dma::cfg_channel(
453                    &mut regs,
454                    channel_write,
455                    periph_addr_write,
456                    ptr_write as u32,
457                    num_data_write,
458                    dma::Direction::ReadFromMem,
459                    dma::DataSize::S8,
460                    dma::DataSize::S8,
461                    channel_cfg_write,
462                )?;
463
464                dma::cfg_channel(
465                    &mut regs,
466                    channel_read,
467                    periph_addr_read,
468                    ptr_read as u32,
469                    num_data_read,
470                    dma::Direction::ReadFromPeriph,
471                    dma::DataSize::S8,
472                    dma::DataSize::S8,
473                    channel_cfg_read,
474                )?;
475            }
476            #[cfg(dma2)]
477            dma::DmaPeriph::Dma2 => {
478                let mut regs = unsafe { &(*pac::DMA2::ptr()) };
479                dma::cfg_channel(
480                    &mut regs,
481                    channel_write,
482                    periph_addr_write,
483                    ptr_write as u32,
484                    num_data_write,
485                    dma::Direction::ReadFromMem,
486                    dma::DataSize::S8,
487                    dma::DataSize::S8,
488                    channel_cfg_write,
489                )?;
490
491                dma::cfg_channel(
492                    &mut regs,
493                    channel_read,
494                    periph_addr_read,
495                    ptr_read as u32,
496                    num_data_read,
497                    dma::Direction::ReadFromPeriph,
498                    dma::DataSize::S8,
499                    dma::DataSize::S8,
500                    channel_cfg_read,
501                )?;
502            }
503        }
504
505        self.regs.cr2().modify(|_, w| w.txdmaen().bit(true));
506        self.regs.cr1().modify(|_, w| w.spe().bit(true));
507
508        Ok(())
509    }
510
511    /// Enable an interrupt. Note that unlike on other peripherals, there's no explicit way to
512    /// clear these. RM: "Writing to the transmit data register always clears the TXE bit.
513    /// The TXE flag is set by hardware."
514    pub fn enable_interrupt(&mut self, interrupt_type: SpiInterrupt) {
515        self.regs.cr2().modify(|_, w| match interrupt_type {
516            SpiInterrupt::TxBufEmpty => w.txeie().bit(true),
517            SpiInterrupt::RxBufNotEmpty => w.rxneie().bit(true),
518            SpiInterrupt::Error => w.errie().bit(true),
519        });
520    }
521}