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}