1 | //! Serial Peripheral Interface (SPI) |
2 | #![macro_use ] |
3 | |
4 | use core::marker::PhantomData; |
5 | use core::ptr; |
6 | |
7 | use embassy_embedded_hal::SetConfig; |
8 | use embassy_futures::join::join; |
9 | use embassy_hal_internal::PeripheralRef; |
10 | pub use embedded_hal_02::spi::{Mode, Phase, Polarity, MODE_0, MODE_1, MODE_2, MODE_3}; |
11 | |
12 | use crate::dma::{word, ChannelAndRequest}; |
13 | use crate::gpio::{AfType, AnyPin, OutputType, Pull, SealedPin as _, Speed}; |
14 | use crate::mode::{Async, Blocking, Mode as PeriMode}; |
15 | use crate::pac::spi::{regs, vals, Spi as Regs}; |
16 | use crate::rcc::{RccInfo, SealedRccPeripheral}; |
17 | use crate::time::Hertz; |
18 | use crate::Peripheral; |
19 | |
20 | /// SPI error. |
21 | #[derive (Debug, PartialEq, Eq, Clone, Copy)] |
22 | #[cfg_attr (feature = "defmt" , derive(defmt::Format))] |
23 | pub enum Error { |
24 | /// Invalid framing. |
25 | Framing, |
26 | /// CRC error (only if hardware CRC checking is enabled). |
27 | Crc, |
28 | /// Mode fault |
29 | ModeFault, |
30 | /// Overrun. |
31 | Overrun, |
32 | } |
33 | |
34 | /// SPI bit order |
35 | #[derive (Copy, Clone)] |
36 | pub enum BitOrder { |
37 | /// Least significant bit first. |
38 | LsbFirst, |
39 | /// Most significant bit first. |
40 | MsbFirst, |
41 | } |
42 | |
43 | /// SPI configuration. |
44 | #[non_exhaustive ] |
45 | #[derive (Copy, Clone)] |
46 | pub struct Config { |
47 | /// SPI mode. |
48 | pub mode: Mode, |
49 | /// Bit order. |
50 | pub bit_order: BitOrder, |
51 | /// Clock frequency. |
52 | pub frequency: Hertz, |
53 | /// Enable internal pullup on MISO. |
54 | /// |
55 | /// There are some ICs that require a pull-up on the MISO pin for some applications. |
56 | /// If you are unsure, you probably don't need this. |
57 | pub miso_pull: Pull, |
58 | /// signal rise/fall speed (slew rate) - defaults to `Medium`. |
59 | /// Increase for high SPI speeds. Change to `Low` to reduce ringing. |
60 | pub rise_fall_speed: Speed, |
61 | } |
62 | |
63 | impl Default for Config { |
64 | fn default() -> Self { |
65 | Self { |
66 | mode: MODE_0, |
67 | bit_order: BitOrder::MsbFirst, |
68 | frequency: Hertz(1_000_000), |
69 | miso_pull: Pull::None, |
70 | rise_fall_speed: Speed::VeryHigh, |
71 | } |
72 | } |
73 | } |
74 | |
75 | impl Config { |
76 | fn raw_phase(&self) -> vals::Cpha { |
77 | match self.mode.phase { |
78 | Phase::CaptureOnSecondTransition => vals::Cpha::SECOND_EDGE, |
79 | Phase::CaptureOnFirstTransition => vals::Cpha::FIRST_EDGE, |
80 | } |
81 | } |
82 | |
83 | fn raw_polarity(&self) -> vals::Cpol { |
84 | match self.mode.polarity { |
85 | Polarity::IdleHigh => vals::Cpol::IDLE_HIGH, |
86 | Polarity::IdleLow => vals::Cpol::IDLE_LOW, |
87 | } |
88 | } |
89 | |
90 | fn raw_byte_order(&self) -> vals::Lsbfirst { |
91 | match self.bit_order { |
92 | BitOrder::LsbFirst => vals::Lsbfirst::LSBFIRST, |
93 | BitOrder::MsbFirst => vals::Lsbfirst::MSBFIRST, |
94 | } |
95 | } |
96 | |
97 | #[cfg (gpio_v1)] |
98 | fn sck_af(&self) -> AfType { |
99 | AfType::output(OutputType::PushPull, self.rise_fall_speed) |
100 | } |
101 | |
102 | #[cfg (gpio_v2)] |
103 | fn sck_af(&self) -> AfType { |
104 | AfType::output_pull( |
105 | OutputType::PushPull, |
106 | self.rise_fall_speed, |
107 | match self.mode.polarity { |
108 | Polarity::IdleLow => Pull::Down, |
109 | Polarity::IdleHigh => Pull::Up, |
110 | }, |
111 | ) |
112 | } |
113 | } |
114 | /// SPI driver. |
115 | pub struct Spi<'d, M: PeriMode> { |
116 | pub(crate) info: &'static Info, |
117 | kernel_clock: Hertz, |
118 | sck: Option<PeripheralRef<'d, AnyPin>>, |
119 | mosi: Option<PeripheralRef<'d, AnyPin>>, |
120 | miso: Option<PeripheralRef<'d, AnyPin>>, |
121 | tx_dma: Option<ChannelAndRequest<'d>>, |
122 | rx_dma: Option<ChannelAndRequest<'d>>, |
123 | _phantom: PhantomData<M>, |
124 | current_word_size: word_impl::Config, |
125 | rise_fall_speed: Speed, |
126 | } |
127 | |
128 | impl<'d, M: PeriMode> Spi<'d, M> { |
129 | fn new_inner<T: Instance>( |
130 | _peri: impl Peripheral<P = T> + 'd, |
131 | sck: Option<PeripheralRef<'d, AnyPin>>, |
132 | mosi: Option<PeripheralRef<'d, AnyPin>>, |
133 | miso: Option<PeripheralRef<'d, AnyPin>>, |
134 | tx_dma: Option<ChannelAndRequest<'d>>, |
135 | rx_dma: Option<ChannelAndRequest<'d>>, |
136 | config: Config, |
137 | ) -> Self { |
138 | let mut this = Self { |
139 | info: T::info(), |
140 | kernel_clock: T::frequency(), |
141 | sck, |
142 | mosi, |
143 | miso, |
144 | tx_dma, |
145 | rx_dma, |
146 | current_word_size: <u8 as SealedWord>::CONFIG, |
147 | _phantom: PhantomData, |
148 | rise_fall_speed: config.rise_fall_speed, |
149 | }; |
150 | this.enable_and_init(config); |
151 | this |
152 | } |
153 | |
154 | fn enable_and_init(&mut self, config: Config) { |
155 | let br = compute_baud_rate(self.kernel_clock, config.frequency); |
156 | let cpha = config.raw_phase(); |
157 | let cpol = config.raw_polarity(); |
158 | let lsbfirst = config.raw_byte_order(); |
159 | |
160 | self.info.rcc.enable_and_reset(); |
161 | |
162 | let regs = self.info.regs; |
163 | #[cfg (any(spi_v1, spi_f1))] |
164 | { |
165 | regs.cr2().modify(|w| { |
166 | w.set_ssoe(false); |
167 | }); |
168 | regs.cr1().modify(|w| { |
169 | w.set_cpha(cpha); |
170 | w.set_cpol(cpol); |
171 | |
172 | w.set_mstr(vals::Mstr::MASTER); |
173 | w.set_br(br); |
174 | w.set_spe(true); |
175 | w.set_lsbfirst(lsbfirst); |
176 | w.set_ssi(true); |
177 | w.set_ssm(true); |
178 | w.set_crcen(false); |
179 | w.set_bidimode(vals::Bidimode::UNIDIRECTIONAL); |
180 | // we're doing "fake rxonly", by actually writing one |
181 | // byte to TXDR for each byte we want to receive. if we |
182 | // set OUTPUTDISABLED here, this hangs. |
183 | w.set_rxonly(vals::Rxonly::FULL_DUPLEX); |
184 | w.set_dff(<u8 as SealedWord>::CONFIG) |
185 | }); |
186 | } |
187 | #[cfg (spi_v2)] |
188 | { |
189 | regs.cr2().modify(|w| { |
190 | let (ds, frxth) = <u8 as SealedWord>::CONFIG; |
191 | w.set_frxth(frxth); |
192 | w.set_ds(ds); |
193 | w.set_ssoe(false); |
194 | }); |
195 | regs.cr1().modify(|w| { |
196 | w.set_cpha(cpha); |
197 | w.set_cpol(cpol); |
198 | |
199 | w.set_mstr(vals::Mstr::MASTER); |
200 | w.set_br(br); |
201 | w.set_lsbfirst(lsbfirst); |
202 | w.set_ssi(true); |
203 | w.set_ssm(true); |
204 | w.set_crcen(false); |
205 | w.set_bidimode(vals::Bidimode::UNIDIRECTIONAL); |
206 | w.set_spe(true); |
207 | }); |
208 | } |
209 | #[cfg (any(spi_v3, spi_v4, spi_v5))] |
210 | { |
211 | regs.ifcr().write(|w| w.0 = 0xffff_ffff); |
212 | regs.cfg2().modify(|w| { |
213 | //w.set_ssoe(true); |
214 | w.set_ssoe(false); |
215 | w.set_cpha(cpha); |
216 | w.set_cpol(cpol); |
217 | w.set_lsbfirst(lsbfirst); |
218 | w.set_ssm(true); |
219 | w.set_master(vals::Master::MASTER); |
220 | w.set_comm(vals::Comm::FULL_DUPLEX); |
221 | w.set_ssom(vals::Ssom::ASSERTED); |
222 | w.set_midi(0); |
223 | w.set_mssi(0); |
224 | w.set_afcntr(true); |
225 | w.set_ssiop(vals::Ssiop::ACTIVE_HIGH); |
226 | }); |
227 | regs.cfg1().modify(|w| { |
228 | w.set_crcen(false); |
229 | w.set_mbr(br); |
230 | w.set_dsize(<u8 as SealedWord>::CONFIG); |
231 | w.set_fthlv(vals::Fthlv::ONE_FRAME); |
232 | }); |
233 | regs.cr2().modify(|w| { |
234 | w.set_tsize(0); |
235 | }); |
236 | regs.cr1().modify(|w| { |
237 | w.set_ssi(false); |
238 | w.set_spe(true); |
239 | }); |
240 | } |
241 | } |
242 | |
243 | /// Reconfigures it with the supplied config. |
244 | pub fn set_config(&mut self, config: &Config) -> Result<(), ()> { |
245 | let cpha = config.raw_phase(); |
246 | let cpol = config.raw_polarity(); |
247 | |
248 | let lsbfirst = config.raw_byte_order(); |
249 | |
250 | let br = compute_baud_rate(self.kernel_clock, config.frequency); |
251 | |
252 | #[cfg (gpio_v2)] |
253 | { |
254 | self.rise_fall_speed = config.rise_fall_speed; |
255 | if let Some(sck) = self.sck.as_ref() { |
256 | sck.set_speed(config.rise_fall_speed); |
257 | } |
258 | if let Some(mosi) = self.mosi.as_ref() { |
259 | mosi.set_speed(config.rise_fall_speed); |
260 | } |
261 | } |
262 | |
263 | #[cfg (any(spi_v1, spi_f1, spi_v2))] |
264 | self.info.regs.cr1().modify(|w| { |
265 | w.set_cpha(cpha); |
266 | w.set_cpol(cpol); |
267 | w.set_br(br); |
268 | w.set_lsbfirst(lsbfirst); |
269 | }); |
270 | |
271 | #[cfg (any(spi_v3, spi_v4, spi_v5))] |
272 | { |
273 | self.info.regs.cfg2().modify(|w| { |
274 | w.set_cpha(cpha); |
275 | w.set_cpol(cpol); |
276 | w.set_lsbfirst(lsbfirst); |
277 | }); |
278 | self.info.regs.cfg1().modify(|w| { |
279 | w.set_mbr(br); |
280 | }); |
281 | } |
282 | Ok(()) |
283 | } |
284 | |
285 | /// Get current SPI configuration. |
286 | pub fn get_current_config(&self) -> Config { |
287 | #[cfg (any(spi_v1, spi_f1, spi_v2))] |
288 | let cfg = self.info.regs.cr1().read(); |
289 | #[cfg (any(spi_v3, spi_v4, spi_v5))] |
290 | let cfg = self.info.regs.cfg2().read(); |
291 | #[cfg (any(spi_v3, spi_v4, spi_v5))] |
292 | let cfg1 = self.info.regs.cfg1().read(); |
293 | |
294 | let polarity = if cfg.cpol() == vals::Cpol::IDLE_LOW { |
295 | Polarity::IdleLow |
296 | } else { |
297 | Polarity::IdleHigh |
298 | }; |
299 | let phase = if cfg.cpha() == vals::Cpha::FIRST_EDGE { |
300 | Phase::CaptureOnFirstTransition |
301 | } else { |
302 | Phase::CaptureOnSecondTransition |
303 | }; |
304 | |
305 | let bit_order = if cfg.lsbfirst() == vals::Lsbfirst::LSBFIRST { |
306 | BitOrder::LsbFirst |
307 | } else { |
308 | BitOrder::MsbFirst |
309 | }; |
310 | |
311 | let miso_pull = match &self.miso { |
312 | None => Pull::None, |
313 | Some(pin) => pin.pull(), |
314 | }; |
315 | |
316 | #[cfg (any(spi_v1, spi_f1, spi_v2))] |
317 | let br = cfg.br(); |
318 | #[cfg (any(spi_v3, spi_v4, spi_v5))] |
319 | let br = cfg1.mbr(); |
320 | |
321 | let frequency = compute_frequency(self.kernel_clock, br); |
322 | |
323 | Config { |
324 | mode: Mode { polarity, phase }, |
325 | bit_order, |
326 | frequency, |
327 | miso_pull, |
328 | rise_fall_speed: self.rise_fall_speed, |
329 | } |
330 | } |
331 | |
332 | pub(crate) fn set_word_size(&mut self, word_size: word_impl::Config) { |
333 | if self.current_word_size == word_size { |
334 | return; |
335 | } |
336 | |
337 | self.info.regs.cr1().modify(|w| { |
338 | w.set_spe(false); |
339 | }); |
340 | |
341 | #[cfg (any(spi_v1, spi_f1))] |
342 | self.info.regs.cr1().modify(|reg| { |
343 | reg.set_dff(word_size); |
344 | }); |
345 | #[cfg (spi_v2)] |
346 | self.info.regs.cr2().modify(|w| { |
347 | w.set_frxth(word_size.1); |
348 | w.set_ds(word_size.0); |
349 | }); |
350 | #[cfg (any(spi_v3, spi_v4, spi_v5))] |
351 | self.info.regs.cfg1().modify(|w| { |
352 | w.set_dsize(word_size); |
353 | }); |
354 | |
355 | self.current_word_size = word_size; |
356 | } |
357 | |
358 | /// Blocking write. |
359 | pub fn blocking_write<W: Word>(&mut self, words: &[W]) -> Result<(), Error> { |
360 | // needed in v3+ to avoid overrun causing the SPI RX state machine to get stuck...? |
361 | #[cfg (any(spi_v3, spi_v4, spi_v5))] |
362 | self.info.regs.cr1().modify(|w| w.set_spe(false)); |
363 | self.set_word_size(W::CONFIG); |
364 | self.info.regs.cr1().modify(|w| w.set_spe(true)); |
365 | flush_rx_fifo(self.info.regs); |
366 | for word in words.iter() { |
367 | // this cannot use `transfer_word` because on SPIv2 and higher, |
368 | // the SPI RX state machine hangs if no physical pin is connected to the SCK AF. |
369 | // This is the case when the SPI has been created with `new_(blocking_?)txonly_nosck`. |
370 | // See https://github.com/embassy-rs/embassy/issues/2902 |
371 | // This is not documented as an errata by ST, and I've been unable to find anything online... |
372 | #[cfg (not(any(spi_v1, spi_f1)))] |
373 | write_word(self.info.regs, *word)?; |
374 | |
375 | // if we're doing tx only, after writing the last byte to FIFO we have to wait |
376 | // until it's actually sent. On SPIv1 you're supposed to use the BSY flag for this |
377 | // but apparently it's broken, it clears too soon. Workaround is to wait for RXNE: |
378 | // when it gets set you know the transfer is done, even if you don't care about rx. |
379 | // Luckily this doesn't affect SPIv2+. |
380 | // See http://efton.sk/STM32/gotcha/g68.html |
381 | // ST doesn't seem to document this in errata sheets (?) |
382 | #[cfg (any(spi_v1, spi_f1))] |
383 | transfer_word(self.info.regs, *word)?; |
384 | } |
385 | |
386 | // wait until last word is transmitted. (except on v1, see above) |
387 | #[cfg (not(any(spi_v1, spi_f1, spi_v2)))] |
388 | while !self.info.regs.sr().read().txc() {} |
389 | #[cfg (spi_v2)] |
390 | while self.info.regs.sr().read().bsy() {} |
391 | |
392 | Ok(()) |
393 | } |
394 | |
395 | /// Blocking read. |
396 | pub fn blocking_read<W: Word>(&mut self, words: &mut [W]) -> Result<(), Error> { |
397 | // needed in v3+ to avoid overrun causing the SPI RX state machine to get stuck...? |
398 | #[cfg (any(spi_v3, spi_v4, spi_v5))] |
399 | self.info.regs.cr1().modify(|w| w.set_spe(false)); |
400 | self.set_word_size(W::CONFIG); |
401 | self.info.regs.cr1().modify(|w| w.set_spe(true)); |
402 | flush_rx_fifo(self.info.regs); |
403 | for word in words.iter_mut() { |
404 | *word = transfer_word(self.info.regs, W::default())?; |
405 | } |
406 | Ok(()) |
407 | } |
408 | |
409 | /// Blocking in-place bidirectional transfer. |
410 | /// |
411 | /// This writes the contents of `data` on MOSI, and puts the received data on MISO in `data`, at the same time. |
412 | pub fn blocking_transfer_in_place<W: Word>(&mut self, words: &mut [W]) -> Result<(), Error> { |
413 | // needed in v3+ to avoid overrun causing the SPI RX state machine to get stuck...? |
414 | #[cfg (any(spi_v3, spi_v4, spi_v5))] |
415 | self.info.regs.cr1().modify(|w| w.set_spe(false)); |
416 | self.set_word_size(W::CONFIG); |
417 | self.info.regs.cr1().modify(|w| w.set_spe(true)); |
418 | flush_rx_fifo(self.info.regs); |
419 | for word in words.iter_mut() { |
420 | *word = transfer_word(self.info.regs, *word)?; |
421 | } |
422 | Ok(()) |
423 | } |
424 | |
425 | /// Blocking bidirectional transfer. |
426 | /// |
427 | /// This transfers both buffers at the same time, so it is NOT equivalent to `write` followed by `read`. |
428 | /// |
429 | /// The transfer runs for `max(read.len(), write.len())` bytes. If `read` is shorter extra bytes are ignored. |
430 | /// If `write` is shorter it is padded with zero bytes. |
431 | pub fn blocking_transfer<W: Word>(&mut self, read: &mut [W], write: &[W]) -> Result<(), Error> { |
432 | // needed in v3+ to avoid overrun causing the SPI RX state machine to get stuck...? |
433 | #[cfg (any(spi_v3, spi_v4, spi_v5))] |
434 | self.info.regs.cr1().modify(|w| w.set_spe(false)); |
435 | self.set_word_size(W::CONFIG); |
436 | self.info.regs.cr1().modify(|w| w.set_spe(true)); |
437 | flush_rx_fifo(self.info.regs); |
438 | let len = read.len().max(write.len()); |
439 | for i in 0..len { |
440 | let wb = write.get(i).copied().unwrap_or_default(); |
441 | let rb = transfer_word(self.info.regs, wb)?; |
442 | if let Some(r) = read.get_mut(i) { |
443 | *r = rb; |
444 | } |
445 | } |
446 | Ok(()) |
447 | } |
448 | } |
449 | |
450 | impl<'d> Spi<'d, Blocking> { |
451 | /// Create a new blocking SPI driver. |
452 | pub fn new_blocking<T: Instance>( |
453 | peri: impl Peripheral<P = T> + 'd, |
454 | sck: impl Peripheral<P = impl SckPin<T>> + 'd, |
455 | mosi: impl Peripheral<P = impl MosiPin<T>> + 'd, |
456 | miso: impl Peripheral<P = impl MisoPin<T>> + 'd, |
457 | config: Config, |
458 | ) -> Self { |
459 | Self::new_inner( |
460 | peri, |
461 | new_pin!(sck, config.sck_af()), |
462 | new_pin!(mosi, AfType::output(OutputType::PushPull, config.rise_fall_speed)), |
463 | new_pin!(miso, AfType::input(config.miso_pull)), |
464 | None, |
465 | None, |
466 | config, |
467 | ) |
468 | } |
469 | |
470 | /// Create a new blocking SPI driver, in RX-only mode (only MISO pin, no MOSI). |
471 | pub fn new_blocking_rxonly<T: Instance>( |
472 | peri: impl Peripheral<P = T> + 'd, |
473 | sck: impl Peripheral<P = impl SckPin<T>> + 'd, |
474 | miso: impl Peripheral<P = impl MisoPin<T>> + 'd, |
475 | config: Config, |
476 | ) -> Self { |
477 | Self::new_inner( |
478 | peri, |
479 | new_pin!(sck, config.sck_af()), |
480 | None, |
481 | new_pin!(miso, AfType::input(config.miso_pull)), |
482 | None, |
483 | None, |
484 | config, |
485 | ) |
486 | } |
487 | |
488 | /// Create a new blocking SPI driver, in TX-only mode (only MOSI pin, no MISO). |
489 | pub fn new_blocking_txonly<T: Instance>( |
490 | peri: impl Peripheral<P = T> + 'd, |
491 | sck: impl Peripheral<P = impl SckPin<T>> + 'd, |
492 | mosi: impl Peripheral<P = impl MosiPin<T>> + 'd, |
493 | config: Config, |
494 | ) -> Self { |
495 | Self::new_inner( |
496 | peri, |
497 | new_pin!(sck, config.sck_af()), |
498 | new_pin!(mosi, AfType::output(OutputType::PushPull, config.rise_fall_speed)), |
499 | None, |
500 | None, |
501 | None, |
502 | config, |
503 | ) |
504 | } |
505 | |
506 | /// Create a new SPI driver, in TX-only mode, without SCK pin. |
507 | /// |
508 | /// This can be useful for bit-banging non-SPI protocols. |
509 | pub fn new_blocking_txonly_nosck<T: Instance>( |
510 | peri: impl Peripheral<P = T> + 'd, |
511 | mosi: impl Peripheral<P = impl MosiPin<T>> + 'd, |
512 | config: Config, |
513 | ) -> Self { |
514 | Self::new_inner( |
515 | peri, |
516 | None, |
517 | new_pin!(mosi, AfType::output(OutputType::PushPull, config.rise_fall_speed)), |
518 | None, |
519 | None, |
520 | None, |
521 | config, |
522 | ) |
523 | } |
524 | } |
525 | |
526 | impl<'d> Spi<'d, Async> { |
527 | /// Create a new SPI driver. |
528 | pub fn new<T: Instance>( |
529 | peri: impl Peripheral<P = T> + 'd, |
530 | sck: impl Peripheral<P = impl SckPin<T>> + 'd, |
531 | mosi: impl Peripheral<P = impl MosiPin<T>> + 'd, |
532 | miso: impl Peripheral<P = impl MisoPin<T>> + 'd, |
533 | tx_dma: impl Peripheral<P = impl TxDma<T>> + 'd, |
534 | rx_dma: impl Peripheral<P = impl RxDma<T>> + 'd, |
535 | config: Config, |
536 | ) -> Self { |
537 | Self::new_inner( |
538 | peri, |
539 | new_pin!(sck, config.sck_af()), |
540 | new_pin!(mosi, AfType::output(OutputType::PushPull, config.rise_fall_speed)), |
541 | new_pin!(miso, AfType::input(config.miso_pull)), |
542 | new_dma!(tx_dma), |
543 | new_dma!(rx_dma), |
544 | config, |
545 | ) |
546 | } |
547 | |
548 | /// Create a new SPI driver, in RX-only mode (only MISO pin, no MOSI). |
549 | pub fn new_rxonly<T: Instance>( |
550 | peri: impl Peripheral<P = T> + 'd, |
551 | sck: impl Peripheral<P = impl SckPin<T>> + 'd, |
552 | miso: impl Peripheral<P = impl MisoPin<T>> + 'd, |
553 | #[cfg (any(spi_v1, spi_f1, spi_v2))] tx_dma: impl Peripheral<P = impl TxDma<T>> + 'd, |
554 | rx_dma: impl Peripheral<P = impl RxDma<T>> + 'd, |
555 | config: Config, |
556 | ) -> Self { |
557 | Self::new_inner( |
558 | peri, |
559 | new_pin!(sck, config.sck_af()), |
560 | None, |
561 | new_pin!(miso, AfType::input(config.miso_pull)), |
562 | #[cfg (any(spi_v1, spi_f1, spi_v2))] |
563 | new_dma!(tx_dma), |
564 | #[cfg (any(spi_v3, spi_v4, spi_v5))] |
565 | None, |
566 | new_dma!(rx_dma), |
567 | config, |
568 | ) |
569 | } |
570 | |
571 | /// Create a new SPI driver, in TX-only mode (only MOSI pin, no MISO). |
572 | pub fn new_txonly<T: Instance>( |
573 | peri: impl Peripheral<P = T> + 'd, |
574 | sck: impl Peripheral<P = impl SckPin<T>> + 'd, |
575 | mosi: impl Peripheral<P = impl MosiPin<T>> + 'd, |
576 | tx_dma: impl Peripheral<P = impl TxDma<T>> + 'd, |
577 | config: Config, |
578 | ) -> Self { |
579 | Self::new_inner( |
580 | peri, |
581 | new_pin!(sck, config.sck_af()), |
582 | new_pin!(mosi, AfType::output(OutputType::PushPull, config.rise_fall_speed)), |
583 | None, |
584 | new_dma!(tx_dma), |
585 | None, |
586 | config, |
587 | ) |
588 | } |
589 | |
590 | /// Create a new SPI driver, in TX-only mode, without SCK pin. |
591 | /// |
592 | /// This can be useful for bit-banging non-SPI protocols. |
593 | pub fn new_txonly_nosck<T: Instance>( |
594 | peri: impl Peripheral<P = T> + 'd, |
595 | mosi: impl Peripheral<P = impl MosiPin<T>> + 'd, |
596 | tx_dma: impl Peripheral<P = impl TxDma<T>> + 'd, |
597 | config: Config, |
598 | ) -> Self { |
599 | Self::new_inner( |
600 | peri, |
601 | None, |
602 | new_pin!(mosi, AfType::output(OutputType::PushPull, config.rise_fall_speed)), |
603 | None, |
604 | new_dma!(tx_dma), |
605 | None, |
606 | config, |
607 | ) |
608 | } |
609 | |
610 | #[cfg (stm32wl)] |
611 | /// Useful for on chip peripherals like SUBGHZ which are hardwired. |
612 | pub fn new_subghz<T: Instance>( |
613 | peri: impl Peripheral<P = T> + 'd, |
614 | tx_dma: impl Peripheral<P = impl TxDma<T>> + 'd, |
615 | rx_dma: impl Peripheral<P = impl RxDma<T>> + 'd, |
616 | ) -> Self { |
617 | // see RM0453 rev 1 section 7.2.13 page 291 |
618 | // The SUBGHZSPI_SCK frequency is obtained by PCLK3 divided by two. |
619 | // The SUBGHZSPI_SCK clock maximum speed must not exceed 16 MHz. |
620 | let pclk3_freq = <crate::peripherals::SUBGHZSPI as SealedRccPeripheral>::frequency().0; |
621 | let freq = Hertz(core::cmp::min(pclk3_freq / 2, 16_000_000)); |
622 | let mut config = Config::default(); |
623 | config.mode = MODE_0; |
624 | config.bit_order = BitOrder::MsbFirst; |
625 | config.frequency = freq; |
626 | |
627 | Self::new_inner(peri, None, None, None, new_dma!(tx_dma), new_dma!(rx_dma), config) |
628 | } |
629 | |
630 | #[allow (dead_code)] |
631 | pub(crate) fn new_internal<T: Instance>( |
632 | peri: impl Peripheral<P = T> + 'd, |
633 | tx_dma: Option<ChannelAndRequest<'d>>, |
634 | rx_dma: Option<ChannelAndRequest<'d>>, |
635 | config: Config, |
636 | ) -> Self { |
637 | Self::new_inner(peri, None, None, None, tx_dma, rx_dma, config) |
638 | } |
639 | |
640 | /// SPI write, using DMA. |
641 | pub async fn write<W: Word>(&mut self, data: &[W]) -> Result<(), Error> { |
642 | if data.is_empty() { |
643 | return Ok(()); |
644 | } |
645 | |
646 | self.info.regs.cr1().modify(|w| { |
647 | w.set_spe(false); |
648 | }); |
649 | self.set_word_size(W::CONFIG); |
650 | |
651 | let tx_dst = self.info.regs.tx_ptr(); |
652 | let tx_f = unsafe { self.tx_dma.as_mut().unwrap().write(data, tx_dst, Default::default()) }; |
653 | |
654 | set_txdmaen(self.info.regs, true); |
655 | self.info.regs.cr1().modify(|w| { |
656 | w.set_spe(true); |
657 | }); |
658 | #[cfg (any(spi_v3, spi_v4, spi_v5))] |
659 | self.info.regs.cr1().modify(|w| { |
660 | w.set_cstart(true); |
661 | }); |
662 | |
663 | tx_f.await; |
664 | |
665 | finish_dma(self.info.regs); |
666 | |
667 | Ok(()) |
668 | } |
669 | |
670 | /// SPI read, using DMA. |
671 | #[cfg (any(spi_v3, spi_v4, spi_v5))] |
672 | pub async fn read<W: Word>(&mut self, data: &mut [W]) -> Result<(), Error> { |
673 | if data.is_empty() { |
674 | return Ok(()); |
675 | } |
676 | |
677 | let regs = self.info.regs; |
678 | |
679 | regs.cr1().modify(|w| { |
680 | w.set_spe(false); |
681 | }); |
682 | |
683 | self.set_word_size(W::CONFIG); |
684 | |
685 | let comm = regs.cfg2().modify(|w| { |
686 | let prev = w.comm(); |
687 | w.set_comm(vals::Comm::RECEIVER); |
688 | prev |
689 | }); |
690 | |
691 | #[cfg (spi_v3)] |
692 | let i2scfg = regs.i2scfgr().modify(|w| { |
693 | w.i2smod().then(|| { |
694 | let prev = w.i2scfg(); |
695 | w.set_i2scfg(match prev { |
696 | vals::I2scfg::SLAVE_RX | vals::I2scfg::SLAVE_FULL_DUPLEX => vals::I2scfg::SLAVE_RX, |
697 | vals::I2scfg::MASTER_RX | vals::I2scfg::MASTER_FULL_DUPLEX => vals::I2scfg::MASTER_RX, |
698 | _ => panic!("unsupported configuration" ), |
699 | }); |
700 | prev |
701 | }) |
702 | }); |
703 | |
704 | let rx_src = regs.rx_ptr(); |
705 | |
706 | for mut chunk in data.chunks_mut(u16::max_value().into()) { |
707 | set_rxdmaen(regs, true); |
708 | |
709 | let tsize = chunk.len(); |
710 | |
711 | let transfer = unsafe { |
712 | self.rx_dma |
713 | .as_mut() |
714 | .unwrap() |
715 | .read(rx_src, &mut chunk, Default::default()) |
716 | }; |
717 | |
718 | regs.cr2().modify(|w| { |
719 | w.set_tsize(tsize as u16); |
720 | }); |
721 | |
722 | regs.cr1().modify(|w| { |
723 | w.set_spe(true); |
724 | }); |
725 | |
726 | regs.cr1().modify(|w| { |
727 | w.set_cstart(true); |
728 | }); |
729 | |
730 | transfer.await; |
731 | |
732 | finish_dma(regs); |
733 | } |
734 | |
735 | regs.cr1().modify(|w| { |
736 | w.set_spe(false); |
737 | }); |
738 | |
739 | regs.cfg2().modify(|w| { |
740 | w.set_comm(comm); |
741 | }); |
742 | |
743 | regs.cr2().modify(|w| { |
744 | w.set_tsize(0); |
745 | }); |
746 | |
747 | #[cfg (spi_v3)] |
748 | if let Some(i2scfg) = i2scfg { |
749 | regs.i2scfgr().modify(|w| { |
750 | w.set_i2scfg(i2scfg); |
751 | }); |
752 | } |
753 | |
754 | Ok(()) |
755 | } |
756 | |
757 | /// SPI read, using DMA. |
758 | #[cfg (any(spi_v1, spi_f1, spi_v2))] |
759 | pub async fn read<W: Word>(&mut self, data: &mut [W]) -> Result<(), Error> { |
760 | if data.is_empty() { |
761 | return Ok(()); |
762 | } |
763 | |
764 | self.info.regs.cr1().modify(|w| { |
765 | w.set_spe(false); |
766 | }); |
767 | |
768 | self.set_word_size(W::CONFIG); |
769 | |
770 | // SPIv3 clears rxfifo on SPE=0 |
771 | #[cfg (not(any(spi_v3, spi_v4, spi_v5)))] |
772 | flush_rx_fifo(self.info.regs); |
773 | |
774 | set_rxdmaen(self.info.regs, true); |
775 | |
776 | let clock_byte_count = data.len(); |
777 | |
778 | let rx_src = self.info.regs.rx_ptr(); |
779 | let rx_f = unsafe { self.rx_dma.as_mut().unwrap().read(rx_src, data, Default::default()) }; |
780 | |
781 | let tx_dst = self.info.regs.tx_ptr(); |
782 | let clock_byte = W::default(); |
783 | let tx_f = unsafe { |
784 | self.tx_dma |
785 | .as_mut() |
786 | .unwrap() |
787 | .write_repeated(&clock_byte, clock_byte_count, tx_dst, Default::default()) |
788 | }; |
789 | |
790 | set_txdmaen(self.info.regs, true); |
791 | self.info.regs.cr1().modify(|w| { |
792 | w.set_spe(true); |
793 | }); |
794 | #[cfg (any(spi_v3, spi_v4, spi_v5))] |
795 | self.info.regs.cr1().modify(|w| { |
796 | w.set_cstart(true); |
797 | }); |
798 | |
799 | join(tx_f, rx_f).await; |
800 | |
801 | finish_dma(self.info.regs); |
802 | |
803 | Ok(()) |
804 | } |
805 | |
806 | async fn transfer_inner<W: Word>(&mut self, read: *mut [W], write: *const [W]) -> Result<(), Error> { |
807 | assert_eq!(read.len(), write.len()); |
808 | if read.len() == 0 { |
809 | return Ok(()); |
810 | } |
811 | |
812 | self.info.regs.cr1().modify(|w| { |
813 | w.set_spe(false); |
814 | }); |
815 | |
816 | self.set_word_size(W::CONFIG); |
817 | |
818 | // SPIv3 clears rxfifo on SPE=0 |
819 | #[cfg (not(any(spi_v3, spi_v4, spi_v5)))] |
820 | flush_rx_fifo(self.info.regs); |
821 | |
822 | set_rxdmaen(self.info.regs, true); |
823 | |
824 | let rx_src = self.info.regs.rx_ptr(); |
825 | let rx_f = unsafe { self.rx_dma.as_mut().unwrap().read_raw(rx_src, read, Default::default()) }; |
826 | |
827 | let tx_dst = self.info.regs.tx_ptr(); |
828 | let tx_f = unsafe { |
829 | self.tx_dma |
830 | .as_mut() |
831 | .unwrap() |
832 | .write_raw(write, tx_dst, Default::default()) |
833 | }; |
834 | |
835 | set_txdmaen(self.info.regs, true); |
836 | self.info.regs.cr1().modify(|w| { |
837 | w.set_spe(true); |
838 | }); |
839 | #[cfg (any(spi_v3, spi_v4, spi_v5))] |
840 | self.info.regs.cr1().modify(|w| { |
841 | w.set_cstart(true); |
842 | }); |
843 | |
844 | join(tx_f, rx_f).await; |
845 | |
846 | finish_dma(self.info.regs); |
847 | |
848 | Ok(()) |
849 | } |
850 | |
851 | /// Bidirectional transfer, using DMA. |
852 | /// |
853 | /// This transfers both buffers at the same time, so it is NOT equivalent to `write` followed by `read`. |
854 | /// |
855 | /// The transfer runs for `max(read.len(), write.len())` bytes. If `read` is shorter extra bytes are ignored. |
856 | /// If `write` is shorter it is padded with zero bytes. |
857 | pub async fn transfer<W: Word>(&mut self, read: &mut [W], write: &[W]) -> Result<(), Error> { |
858 | self.transfer_inner(read, write).await |
859 | } |
860 | |
861 | /// In-place bidirectional transfer, using DMA. |
862 | /// |
863 | /// This writes the contents of `data` on MOSI, and puts the received data on MISO in `data`, at the same time. |
864 | pub async fn transfer_in_place<W: Word>(&mut self, data: &mut [W]) -> Result<(), Error> { |
865 | self.transfer_inner(data, data).await |
866 | } |
867 | } |
868 | |
869 | impl<'d, M: PeriMode> Drop for Spi<'d, M> { |
870 | fn drop(&mut self) { |
871 | self.sck.as_ref().map(|x: &PeripheralRef<'_, AnyPin>| x.set_as_disconnected()); |
872 | self.mosi.as_ref().map(|x: &PeripheralRef<'_, AnyPin>| x.set_as_disconnected()); |
873 | self.miso.as_ref().map(|x: &PeripheralRef<'_, AnyPin>| x.set_as_disconnected()); |
874 | |
875 | self.info.rcc.disable(); |
876 | } |
877 | } |
878 | |
879 | #[cfg (not(any(spi_v3, spi_v4, spi_v5)))] |
880 | use vals::Br; |
881 | #[cfg (any(spi_v3, spi_v4, spi_v5))] |
882 | use vals::Mbr as Br; |
883 | |
884 | fn compute_baud_rate(kernel_clock: Hertz, freq: Hertz) -> Br { |
885 | let val: () = match kernel_clock.0 / freq.0 { |
886 | 0 => panic!("You are trying to reach a frequency higher than the clock" ), |
887 | 1..=2 => 0b000, |
888 | 3..=5 => 0b001, |
889 | 6..=11 => 0b010, |
890 | 12..=23 => 0b011, |
891 | 24..=39 => 0b100, |
892 | 40..=95 => 0b101, |
893 | 96..=191 => 0b110, |
894 | _ => 0b111, |
895 | }; |
896 | |
897 | Br::from_bits(val) |
898 | } |
899 | |
900 | fn compute_frequency(kernel_clock: Hertz, br: Br) -> Hertz { |
901 | let div: u16 = match br { |
902 | Br::DIV2 => 2, |
903 | Br::DIV4 => 4, |
904 | Br::DIV8 => 8, |
905 | Br::DIV16 => 16, |
906 | Br::DIV32 => 32, |
907 | Br::DIV64 => 64, |
908 | Br::DIV128 => 128, |
909 | Br::DIV256 => 256, |
910 | }; |
911 | |
912 | kernel_clock / div |
913 | } |
914 | |
915 | pub(crate) trait RegsExt { |
916 | fn tx_ptr<W>(&self) -> *mut W; |
917 | fn rx_ptr<W>(&self) -> *mut W; |
918 | } |
919 | |
920 | impl RegsExt for Regs { |
921 | fn tx_ptr<W>(&self) -> *mut W { |
922 | #[cfg (any(spi_v1, spi_f1))] |
923 | let dr = self.dr(); |
924 | #[cfg (spi_v2)] |
925 | let dr = self.dr16(); |
926 | #[cfg (any(spi_v3, spi_v4, spi_v5))] |
927 | let dr: Reg = self.txdr32(); |
928 | dr.as_ptr() as *mut W |
929 | } |
930 | |
931 | fn rx_ptr<W>(&self) -> *mut W { |
932 | #[cfg (any(spi_v1, spi_f1))] |
933 | let dr = self.dr(); |
934 | #[cfg (spi_v2)] |
935 | let dr = self.dr16(); |
936 | #[cfg (any(spi_v3, spi_v4, spi_v5))] |
937 | let dr: Reg = self.rxdr32(); |
938 | dr.as_ptr() as *mut W |
939 | } |
940 | } |
941 | |
942 | fn check_error_flags(sr: regs::Sr, ovr: bool) -> Result<(), Error> { |
943 | if sr.ovr() && ovr { |
944 | return Err(Error::Overrun); |
945 | } |
946 | #[cfg (not(any(spi_f1, spi_v3, spi_v4, spi_v5)))] |
947 | if sr.fre() { |
948 | return Err(Error::Framing); |
949 | } |
950 | #[cfg (any(spi_v3, spi_v4, spi_v5))] |
951 | if sr.tifre() { |
952 | return Err(Error::Framing); |
953 | } |
954 | if sr.modf() { |
955 | return Err(Error::ModeFault); |
956 | } |
957 | #[cfg (not(any(spi_v3, spi_v4, spi_v5)))] |
958 | if sr.crcerr() { |
959 | return Err(Error::Crc); |
960 | } |
961 | #[cfg (any(spi_v3, spi_v4, spi_v5))] |
962 | if sr.crce() { |
963 | return Err(Error::Crc); |
964 | } |
965 | |
966 | Ok(()) |
967 | } |
968 | |
969 | fn spin_until_tx_ready(regs: Regs, ovr: bool) -> Result<(), Error> { |
970 | loop { |
971 | let sr: Sr = regs.sr().read(); |
972 | |
973 | check_error_flags(sr, ovr)?; |
974 | |
975 | #[cfg (not(any(spi_v3, spi_v4, spi_v5)))] |
976 | if sr.txe() { |
977 | return Ok(()); |
978 | } |
979 | #[cfg (any(spi_v3, spi_v4, spi_v5))] |
980 | if sr.txp() { |
981 | return Ok(()); |
982 | } |
983 | } |
984 | } |
985 | |
986 | fn spin_until_rx_ready(regs: Regs) -> Result<(), Error> { |
987 | loop { |
988 | let sr: Sr = regs.sr().read(); |
989 | |
990 | check_error_flags(sr, ovr:true)?; |
991 | |
992 | #[cfg (not(any(spi_v3, spi_v4, spi_v5)))] |
993 | if sr.rxne() { |
994 | return Ok(()); |
995 | } |
996 | #[cfg (any(spi_v3, spi_v4, spi_v5))] |
997 | if sr.rxp() { |
998 | return Ok(()); |
999 | } |
1000 | } |
1001 | } |
1002 | |
1003 | pub(crate) fn flush_rx_fifo(regs: Regs) { |
1004 | #[cfg (not(any(spi_v3, spi_v4, spi_v5)))] |
1005 | while regs.sr().read().rxne() { |
1006 | #[cfg (not(spi_v2))] |
1007 | let _ = regs.dr().read(); |
1008 | #[cfg (spi_v2)] |
1009 | let _ = regs.dr16().read(); |
1010 | } |
1011 | #[cfg (any(spi_v3, spi_v4, spi_v5))] |
1012 | while regs.sr().read().rxp() { |
1013 | let _ = regs.rxdr32().read(); |
1014 | } |
1015 | } |
1016 | |
1017 | pub(crate) fn set_txdmaen(regs: Regs, val: bool) { |
1018 | #[cfg (not(any(spi_v3, spi_v4, spi_v5)))] |
1019 | regs.cr2().modify(|reg| { |
1020 | reg.set_txdmaen(val); |
1021 | }); |
1022 | #[cfg (any(spi_v3, spi_v4, spi_v5))] |
1023 | regs.cfg1().modify(|reg: &mut Cfg1| { |
1024 | reg.set_txdmaen(val); |
1025 | }); |
1026 | } |
1027 | |
1028 | pub(crate) fn set_rxdmaen(regs: Regs, val: bool) { |
1029 | #[cfg (not(any(spi_v3, spi_v4, spi_v5)))] |
1030 | regs.cr2().modify(|reg| { |
1031 | reg.set_rxdmaen(val); |
1032 | }); |
1033 | #[cfg (any(spi_v3, spi_v4, spi_v5))] |
1034 | regs.cfg1().modify(|reg: &mut Cfg1| { |
1035 | reg.set_rxdmaen(val); |
1036 | }); |
1037 | } |
1038 | |
1039 | fn finish_dma(regs: Regs) { |
1040 | #[cfg (spi_v2)] |
1041 | while regs.sr().read().ftlvl().to_bits() > 0 {} |
1042 | |
1043 | #[cfg (any(spi_v3, spi_v4, spi_v5))] |
1044 | { |
1045 | if regs.cr2().read().tsize() == 0 { |
1046 | while !regs.sr().read().txc() {} |
1047 | } else { |
1048 | while !regs.sr().read().eot() {} |
1049 | } |
1050 | } |
1051 | #[cfg (not(any(spi_v3, spi_v4, spi_v5)))] |
1052 | while regs.sr().read().bsy() {} |
1053 | |
1054 | // Disable the spi peripheral |
1055 | regs.cr1().modify(|w| { |
1056 | w.set_spe(false); |
1057 | }); |
1058 | |
1059 | // The peripheral automatically disables the DMA stream on completion without error, |
1060 | // but it does not clear the RXDMAEN/TXDMAEN flag in CR2. |
1061 | #[cfg (not(any(spi_v3, spi_v4, spi_v5)))] |
1062 | regs.cr2().modify(|reg| { |
1063 | reg.set_txdmaen(false); |
1064 | reg.set_rxdmaen(false); |
1065 | }); |
1066 | #[cfg (any(spi_v3, spi_v4, spi_v5))] |
1067 | regs.cfg1().modify(|reg| { |
1068 | reg.set_txdmaen(false); |
1069 | reg.set_rxdmaen(false); |
1070 | }); |
1071 | } |
1072 | |
1073 | fn transfer_word<W: Word>(regs: Regs, tx_word: W) -> Result<W, Error> { |
1074 | spin_until_tx_ready(regs, ovr:true)?; |
1075 | |
1076 | unsafe { |
1077 | ptr::write_volatile(dst:regs.tx_ptr(), src:tx_word); |
1078 | |
1079 | #[cfg (any(spi_v3, spi_v4, spi_v5))] |
1080 | regs.cr1().modify(|reg: &mut Cr1| reg.set_cstart(val:true)); |
1081 | } |
1082 | |
1083 | spin_until_rx_ready(regs)?; |
1084 | |
1085 | let rx_word: W = unsafe { ptr::read_volatile(src:regs.rx_ptr()) }; |
1086 | Ok(rx_word) |
1087 | } |
1088 | |
1089 | #[allow (unused)] // unused in SPIv1 |
1090 | fn write_word<W: Word>(regs: Regs, tx_word: W) -> Result<(), Error> { |
1091 | // for write, we intentionally ignore the rx fifo, which will cause |
1092 | // overrun errors that we have to ignore. |
1093 | spin_until_tx_ready(regs, ovr:false)?; |
1094 | |
1095 | unsafe { |
1096 | ptr::write_volatile(dst:regs.tx_ptr(), src:tx_word); |
1097 | |
1098 | #[cfg (any(spi_v3, spi_v4, spi_v5))] |
1099 | regs.cr1().modify(|reg: &mut Cr1| reg.set_cstart(val:true)); |
1100 | } |
1101 | Ok(()) |
1102 | } |
1103 | |
1104 | // Note: It is not possible to impl these traits generically in embedded-hal 0.2 due to a conflict with |
1105 | // some marker traits. For details, see https://github.com/rust-embedded/embedded-hal/pull/289 |
1106 | macro_rules! impl_blocking { |
1107 | ($w:ident) => { |
1108 | impl<'d, M: PeriMode> embedded_hal_02::blocking::spi::Write<$w> for Spi<'d, M> { |
1109 | type Error = Error; |
1110 | |
1111 | fn write(&mut self, words: &[$w]) -> Result<(), Self::Error> { |
1112 | self.blocking_write(words) |
1113 | } |
1114 | } |
1115 | |
1116 | impl<'d, M: PeriMode> embedded_hal_02::blocking::spi::Transfer<$w> for Spi<'d, M> { |
1117 | type Error = Error; |
1118 | |
1119 | fn transfer<'w>(&mut self, words: &'w mut [$w]) -> Result<&'w [$w], Self::Error> { |
1120 | self.blocking_transfer_in_place(words)?; |
1121 | Ok(words) |
1122 | } |
1123 | } |
1124 | }; |
1125 | } |
1126 | |
1127 | impl_blocking!(u8); |
1128 | impl_blocking!(u16); |
1129 | |
1130 | impl<'d, M: PeriMode> embedded_hal_1::spi::ErrorType for Spi<'d, M> { |
1131 | type Error = Error; |
1132 | } |
1133 | |
1134 | impl<'d, W: Word, M: PeriMode> embedded_hal_1::spi::SpiBus<W> for Spi<'d, M> { |
1135 | fn flush(&mut self) -> Result<(), Self::Error> { |
1136 | Ok(()) |
1137 | } |
1138 | |
1139 | fn read(&mut self, words: &mut [W]) -> Result<(), Self::Error> { |
1140 | self.blocking_read(words) |
1141 | } |
1142 | |
1143 | fn write(&mut self, words: &[W]) -> Result<(), Self::Error> { |
1144 | self.blocking_write(words) |
1145 | } |
1146 | |
1147 | fn transfer(&mut self, read: &mut [W], write: &[W]) -> Result<(), Self::Error> { |
1148 | self.blocking_transfer(read, write) |
1149 | } |
1150 | |
1151 | fn transfer_in_place(&mut self, words: &mut [W]) -> Result<(), Self::Error> { |
1152 | self.blocking_transfer_in_place(words) |
1153 | } |
1154 | } |
1155 | |
1156 | impl embedded_hal_1::spi::Error for Error { |
1157 | fn kind(&self) -> embedded_hal_1::spi::ErrorKind { |
1158 | match *self { |
1159 | Self::Framing => embedded_hal_1::spi::ErrorKind::FrameFormat, |
1160 | Self::Crc => embedded_hal_1::spi::ErrorKind::Other, |
1161 | Self::ModeFault => embedded_hal_1::spi::ErrorKind::ModeFault, |
1162 | Self::Overrun => embedded_hal_1::spi::ErrorKind::Overrun, |
1163 | } |
1164 | } |
1165 | } |
1166 | |
1167 | impl<'d, W: Word> embedded_hal_async::spi::SpiBus<W> for Spi<'d, Async> { |
1168 | async fn flush(&mut self) -> Result<(), Self::Error> { |
1169 | Ok(()) |
1170 | } |
1171 | |
1172 | async fn write(&mut self, words: &[W]) -> Result<(), Self::Error> { |
1173 | self.write(data:words).await |
1174 | } |
1175 | |
1176 | async fn read(&mut self, words: &mut [W]) -> Result<(), Self::Error> { |
1177 | self.read(data:words).await |
1178 | } |
1179 | |
1180 | async fn transfer(&mut self, read: &mut [W], write: &[W]) -> Result<(), Self::Error> { |
1181 | self.transfer(read, write).await |
1182 | } |
1183 | |
1184 | async fn transfer_in_place(&mut self, words: &mut [W]) -> Result<(), Self::Error> { |
1185 | self.transfer_in_place(data:words).await |
1186 | } |
1187 | } |
1188 | |
1189 | pub(crate) trait SealedWord { |
1190 | const CONFIG: word_impl::Config; |
1191 | } |
1192 | |
1193 | /// Word sizes usable for SPI. |
1194 | #[allow (private_bounds)] |
1195 | pub trait Word: word::Word + SealedWord + Default {} |
1196 | |
1197 | macro_rules! impl_word { |
1198 | ($T:ty, $config:expr) => { |
1199 | impl SealedWord for $T { |
1200 | const CONFIG: Config = $config; |
1201 | } |
1202 | impl Word for $T {} |
1203 | }; |
1204 | } |
1205 | |
1206 | #[cfg (any(spi_v1, spi_f1))] |
1207 | mod word_impl { |
1208 | use super::*; |
1209 | |
1210 | pub type Config = vals::Dff; |
1211 | |
1212 | impl_word!(u8, vals::Dff::BITS8); |
1213 | impl_word!(u16, vals::Dff::BITS16); |
1214 | } |
1215 | |
1216 | #[cfg (spi_v2)] |
1217 | mod word_impl { |
1218 | use super::*; |
1219 | |
1220 | pub type Config = (vals::Ds, vals::Frxth); |
1221 | |
1222 | impl_word!(word::U4, (vals::Ds::BITS4, vals::Frxth::QUARTER)); |
1223 | impl_word!(word::U5, (vals::Ds::BITS5, vals::Frxth::QUARTER)); |
1224 | impl_word!(word::U6, (vals::Ds::BITS6, vals::Frxth::QUARTER)); |
1225 | impl_word!(word::U7, (vals::Ds::BITS7, vals::Frxth::QUARTER)); |
1226 | impl_word!(u8, (vals::Ds::BITS8, vals::Frxth::QUARTER)); |
1227 | impl_word!(word::U9, (vals::Ds::BITS9, vals::Frxth::HALF)); |
1228 | impl_word!(word::U10, (vals::Ds::BITS10, vals::Frxth::HALF)); |
1229 | impl_word!(word::U11, (vals::Ds::BITS11, vals::Frxth::HALF)); |
1230 | impl_word!(word::U12, (vals::Ds::BITS12, vals::Frxth::HALF)); |
1231 | impl_word!(word::U13, (vals::Ds::BITS13, vals::Frxth::HALF)); |
1232 | impl_word!(word::U14, (vals::Ds::BITS14, vals::Frxth::HALF)); |
1233 | impl_word!(word::U15, (vals::Ds::BITS15, vals::Frxth::HALF)); |
1234 | impl_word!(u16, (vals::Ds::BITS16, vals::Frxth::HALF)); |
1235 | } |
1236 | |
1237 | #[cfg (any(spi_v3, spi_v4, spi_v5))] |
1238 | mod word_impl { |
1239 | use super::*; |
1240 | |
1241 | pub type Config = u8; |
1242 | |
1243 | impl_word!(word::U4, 4 - 1); |
1244 | impl_word!(word::U5, 5 - 1); |
1245 | impl_word!(word::U6, 6 - 1); |
1246 | impl_word!(word::U7, 7 - 1); |
1247 | impl_word!(u8, 8 - 1); |
1248 | impl_word!(word::U9, 9 - 1); |
1249 | impl_word!(word::U10, 10 - 1); |
1250 | impl_word!(word::U11, 11 - 1); |
1251 | impl_word!(word::U12, 12 - 1); |
1252 | impl_word!(word::U13, 13 - 1); |
1253 | impl_word!(word::U14, 14 - 1); |
1254 | impl_word!(word::U15, 15 - 1); |
1255 | impl_word!(u16, 16 - 1); |
1256 | impl_word!(word::U17, 17 - 1); |
1257 | impl_word!(word::U18, 18 - 1); |
1258 | impl_word!(word::U19, 19 - 1); |
1259 | impl_word!(word::U20, 20 - 1); |
1260 | impl_word!(word::U21, 21 - 1); |
1261 | impl_word!(word::U22, 22 - 1); |
1262 | impl_word!(word::U23, 23 - 1); |
1263 | impl_word!(word::U24, 24 - 1); |
1264 | impl_word!(word::U25, 25 - 1); |
1265 | impl_word!(word::U26, 26 - 1); |
1266 | impl_word!(word::U27, 27 - 1); |
1267 | impl_word!(word::U28, 28 - 1); |
1268 | impl_word!(word::U29, 29 - 1); |
1269 | impl_word!(word::U30, 30 - 1); |
1270 | impl_word!(word::U31, 31 - 1); |
1271 | impl_word!(u32, 32 - 1); |
1272 | } |
1273 | |
1274 | pub(crate) struct Info { |
1275 | pub(crate) regs: Regs, |
1276 | pub(crate) rcc: RccInfo, |
1277 | } |
1278 | |
1279 | struct State {} |
1280 | |
1281 | impl State { |
1282 | #[allow (unused)] |
1283 | const fn new() -> Self { |
1284 | Self {} |
1285 | } |
1286 | } |
1287 | |
1288 | peri_trait!(); |
1289 | |
1290 | pin_trait!(SckPin, Instance); |
1291 | pin_trait!(MosiPin, Instance); |
1292 | pin_trait!(MisoPin, Instance); |
1293 | pin_trait!(CsPin, Instance); |
1294 | pin_trait!(MckPin, Instance); |
1295 | pin_trait!(CkPin, Instance); |
1296 | pin_trait!(WsPin, Instance); |
1297 | dma_trait!(RxDma, Instance); |
1298 | dma_trait!(TxDma, Instance); |
1299 | |
1300 | foreach_peripheral!( |
1301 | (spi, $inst:ident) => { |
1302 | peri_trait_impl!($inst, Info { |
1303 | regs: crate::pac::$inst, |
1304 | rcc: crate::peripherals::$inst::RCC_INFO, |
1305 | }); |
1306 | }; |
1307 | ); |
1308 | |
1309 | impl<'d, M: PeriMode> SetConfig for Spi<'d, M> { |
1310 | type Config = Config; |
1311 | type ConfigError = (); |
1312 | fn set_config(&mut self, config: &Self::Config) -> Result<(), ()> { |
1313 | self.set_config(config) |
1314 | } |
1315 | } |
1316 | |