1//! HSPI Serial Peripheral Interface
2//!
3
4// NOTE: This is a partial implementation of the HSPI driver.
5// It implements only Single and Octal SPI modes, but additional
6// modes can be added as needed following the same pattern and
7// using ospi/mod.rs as a reference.
8
9#![macro_use]
10
11pub mod enums;
12
13use core::marker::PhantomData;
14
15use embassy_embedded_hal::{GetConfig, SetConfig};
16use embassy_hal_internal::{into_ref, PeripheralRef};
17pub use enums::*;
18
19use crate::dma::{word, ChannelAndRequest};
20use crate::gpio::{AfType, AnyPin, OutputType, Pull, SealedPin as _, Speed};
21use crate::mode::{Async, Blocking, Mode as PeriMode};
22use crate::pac::hspi::Hspi as Regs;
23use crate::rcc::{self, RccPeripheral};
24use crate::{peripherals, Peripheral};
25
26/// HSPI driver config.
27#[derive(Clone, Copy, defmt::Format)]
28pub struct Config {
29 /// Fifo threshold used by the peripheral to generate the interrupt indicating data
30 /// or space is available in the FIFO
31 pub fifo_threshold: FIFOThresholdLevel,
32 /// Indicates the type of external device connected
33 pub memory_type: MemoryType, // Need to add an additional enum to provide this public interface
34 /// Defines the size of the external device connected to the HSPI corresponding
35 /// to the number of address bits required to access the device
36 pub device_size: MemorySize,
37 /// Sets the minimum number of clock cycles that the chip select signal must be held high
38 /// between commands
39 pub chip_select_high_time: ChipSelectHighTime,
40 /// Enables the free running clock
41 pub free_running_clock: bool,
42 /// Sets the clock level when the device is not selected
43 pub clock_mode: bool,
44 /// Indicates the wrap size corresponding to the external device configuration
45 pub wrap_size: WrapSize,
46 /// Specified the prescaler factor used for generating the external clock based
47 /// on the AHB clock
48 pub clock_prescaler: u8,
49 /// Allows the delay of 1/2 cycle the data sampling to account for external
50 /// signal delays
51 pub sample_shifting: bool,
52 /// Allows hold to 1/4 cycle the data
53 pub delay_hold_quarter_cycle: bool,
54 /// Enables the transaction boundary feature and defines the boundary to release
55 /// the chip select
56 pub chip_select_boundary: u8,
57 /// Enables the delay block bypass so the sampling is not affected by the delay block
58 pub delay_block_bypass: bool,
59 /// Enables communication regulation feature. Chip select is released when the other
60 /// HSPI requests access to the bus
61 pub max_transfer: u8,
62 /// Enables the refresh feature, chip select is released every refresh + 1 clock cycles
63 pub refresh: u32,
64}
65
66impl Default for Config {
67 fn default() -> Self {
68 Self {
69 fifo_threshold: FIFOThresholdLevel::_16Bytes,
70 memory_type: MemoryType::Micron,
71 device_size: MemorySize::Other(0),
72 chip_select_high_time: ChipSelectHighTime::_5Cycle,
73 free_running_clock: false,
74 clock_mode: false,
75 wrap_size: WrapSize::None,
76 clock_prescaler: 0,
77 sample_shifting: false,
78 delay_hold_quarter_cycle: false,
79 chip_select_boundary: 0, // Acceptable range 0 to 31
80 delay_block_bypass: true,
81 max_transfer: 0,
82 refresh: 0,
83 }
84 }
85}
86
87/// HSPI transfer configuration.
88pub struct TransferConfig {
89 /// Instruction width (IMODE)
90 pub iwidth: HspiWidth,
91 /// Instruction Id
92 pub instruction: Option<u32>,
93 /// Number of Instruction Bytes
94 pub isize: AddressSize,
95 /// Instruction Double Transfer rate enable
96 pub idtr: bool,
97
98 /// Address width (ADMODE)
99 pub adwidth: HspiWidth,
100 /// Device memory address
101 pub address: Option<u32>,
102 /// Number of Address Bytes
103 pub adsize: AddressSize,
104 /// Address Double Transfer rate enable
105 pub addtr: bool,
106
107 /// Alternate bytes width (ABMODE)
108 pub abwidth: HspiWidth,
109 /// Alternate Bytes
110 pub alternate_bytes: Option<u32>,
111 /// Number of Alternate Bytes
112 pub absize: AddressSize,
113 /// Alternate Bytes Double Transfer rate enable
114 pub abdtr: bool,
115
116 /// Data width (DMODE)
117 pub dwidth: HspiWidth,
118 /// Data buffer
119 pub ddtr: bool,
120
121 /// Number of dummy cycles (DCYC)
122 pub dummy: DummyCycles,
123}
124
125impl Default for TransferConfig {
126 fn default() -> Self {
127 Self {
128 iwidth: HspiWidth::NONE,
129 instruction: None,
130 isize: AddressSize::_8Bit,
131 idtr: false,
132
133 adwidth: HspiWidth::NONE,
134 address: None,
135 adsize: AddressSize::_8Bit,
136 addtr: false,
137
138 abwidth: HspiWidth::NONE,
139 alternate_bytes: None,
140 absize: AddressSize::_8Bit,
141 abdtr: false,
142
143 dwidth: HspiWidth::NONE,
144 ddtr: false,
145
146 dummy: DummyCycles::_0,
147 }
148 }
149}
150
151/// Error used for HSPI implementation
152#[derive(Debug)]
153#[cfg_attr(feature = "defmt", derive(defmt::Format))]
154pub enum HspiError {
155 /// Peripheral configuration is invalid
156 InvalidConfiguration,
157 /// Operation configuration is invalid
158 InvalidCommand,
159 /// Size zero buffer passed to instruction
160 EmptyBuffer,
161}
162
163/// HSPI driver.
164pub struct Hspi<'d, T: Instance, M: PeriMode> {
165 _peri: PeripheralRef<'d, T>,
166 sck: Option<PeripheralRef<'d, AnyPin>>,
167 d0: Option<PeripheralRef<'d, AnyPin>>,
168 d1: Option<PeripheralRef<'d, AnyPin>>,
169 d2: Option<PeripheralRef<'d, AnyPin>>,
170 d3: Option<PeripheralRef<'d, AnyPin>>,
171 d4: Option<PeripheralRef<'d, AnyPin>>,
172 d5: Option<PeripheralRef<'d, AnyPin>>,
173 d6: Option<PeripheralRef<'d, AnyPin>>,
174 d7: Option<PeripheralRef<'d, AnyPin>>,
175 d8: Option<PeripheralRef<'d, AnyPin>>,
176 d9: Option<PeripheralRef<'d, AnyPin>>,
177 d10: Option<PeripheralRef<'d, AnyPin>>,
178 d11: Option<PeripheralRef<'d, AnyPin>>,
179 d12: Option<PeripheralRef<'d, AnyPin>>,
180 d13: Option<PeripheralRef<'d, AnyPin>>,
181 d14: Option<PeripheralRef<'d, AnyPin>>,
182 d15: Option<PeripheralRef<'d, AnyPin>>,
183 nss: Option<PeripheralRef<'d, AnyPin>>,
184 dqs0: Option<PeripheralRef<'d, AnyPin>>,
185 dqs1: Option<PeripheralRef<'d, AnyPin>>,
186 dma: Option<ChannelAndRequest<'d>>,
187 _phantom: PhantomData<M>,
188 config: Config,
189 width: HspiWidth,
190}
191
192impl<'d, T: Instance, M: PeriMode> Hspi<'d, T, M> {
193 /// Enter memory mode.
194 /// The Input `read_config` is used to configure the read operation in memory mode
195 pub fn enable_memory_mapped_mode(
196 &mut self,
197 read_config: TransferConfig,
198 write_config: TransferConfig,
199 ) -> Result<(), HspiError> {
200 // Use configure command to set read config
201 self.configure_command(&read_config, None)?;
202
203 // Set writing configurations, there are separate registers for write configurations in memory mapped mode
204 T::REGS.wccr().modify(|w| {
205 w.set_imode(write_config.iwidth.into());
206 w.set_idtr(write_config.idtr);
207 w.set_isize(write_config.isize.into());
208
209 w.set_admode(write_config.adwidth.into());
210 w.set_addtr(write_config.idtr);
211 w.set_adsize(write_config.adsize.into());
212
213 w.set_dmode(write_config.dwidth.into());
214 w.set_ddtr(write_config.ddtr);
215
216 w.set_abmode(write_config.abwidth.into());
217 w.set_dqse(true);
218 });
219
220 T::REGS.wtcr().modify(|w| w.set_dcyc(write_config.dummy.into()));
221
222 // Enable memory mapped mode
223 T::REGS.cr().modify(|r| {
224 r.set_fmode(FunctionalMode::MemoryMapped.into());
225 r.set_tcen(false);
226 });
227 Ok(())
228 }
229
230 /// Quit from memory mapped mode
231 pub fn disable_memory_mapped_mode(&mut self) {
232 T::REGS.cr().modify(|r| {
233 r.set_fmode(FunctionalMode::IndirectWrite.into());
234 r.set_abort(true);
235 r.set_dmaen(false);
236 r.set_en(false);
237 });
238
239 // Clear transfer complete flag
240 T::REGS.fcr().write(|w| w.set_ctcf(true));
241
242 // Re-enable HSPI
243 T::REGS.cr().modify(|r| {
244 r.set_en(true);
245 });
246 }
247
248 fn new_inner(
249 peri: impl Peripheral<P = T> + 'd,
250 d0: Option<PeripheralRef<'d, AnyPin>>,
251 d1: Option<PeripheralRef<'d, AnyPin>>,
252 d2: Option<PeripheralRef<'d, AnyPin>>,
253 d3: Option<PeripheralRef<'d, AnyPin>>,
254 d4: Option<PeripheralRef<'d, AnyPin>>,
255 d5: Option<PeripheralRef<'d, AnyPin>>,
256 d6: Option<PeripheralRef<'d, AnyPin>>,
257 d7: Option<PeripheralRef<'d, AnyPin>>,
258 d8: Option<PeripheralRef<'d, AnyPin>>,
259 d9: Option<PeripheralRef<'d, AnyPin>>,
260 d10: Option<PeripheralRef<'d, AnyPin>>,
261 d11: Option<PeripheralRef<'d, AnyPin>>,
262 d12: Option<PeripheralRef<'d, AnyPin>>,
263 d13: Option<PeripheralRef<'d, AnyPin>>,
264 d14: Option<PeripheralRef<'d, AnyPin>>,
265 d15: Option<PeripheralRef<'d, AnyPin>>,
266 sck: Option<PeripheralRef<'d, AnyPin>>,
267 nss: Option<PeripheralRef<'d, AnyPin>>,
268 dqs0: Option<PeripheralRef<'d, AnyPin>>,
269 dqs1: Option<PeripheralRef<'d, AnyPin>>,
270 dma: Option<ChannelAndRequest<'d>>,
271 config: Config,
272 width: HspiWidth,
273 dual_memory_mode: bool,
274 ) -> Self {
275 into_ref!(peri);
276
277 // System configuration
278 rcc::enable_and_reset::<T>();
279
280 // Call this function just to check that the clock for HSPI1 is properly setup
281 let _ = T::frequency();
282
283 while T::REGS.sr().read().busy() {}
284
285 Self::configure_registers(&config, Some(dual_memory_mode));
286
287 Self {
288 _peri: peri,
289 sck,
290 d0,
291 d1,
292 d2,
293 d3,
294 d4,
295 d5,
296 d6,
297 d7,
298 d8,
299 d9,
300 d10,
301 d11,
302 d12,
303 d13,
304 d14,
305 d15,
306 nss,
307 dqs0,
308 dqs1,
309 dma,
310 _phantom: PhantomData,
311 config,
312 width,
313 }
314 }
315
316 fn configure_registers(config: &Config, dual_memory_mode: Option<bool>) {
317 // Device configuration
318 T::REGS.dcr1().modify(|w| {
319 w.set_mtyp(config.memory_type.into());
320 w.set_devsize(config.device_size.into());
321 w.set_csht(config.chip_select_high_time.into());
322 w.set_frck(false);
323 w.set_ckmode(config.clock_mode);
324 w.set_dlybyp(config.delay_block_bypass);
325 });
326
327 T::REGS.dcr2().modify(|w| {
328 w.set_wrapsize(config.wrap_size.into());
329 });
330
331 T::REGS.dcr3().modify(|w| {
332 w.set_csbound(config.chip_select_boundary);
333 w.set_maxtran(config.max_transfer);
334 });
335
336 T::REGS.dcr4().modify(|w| {
337 w.set_refresh(config.refresh);
338 });
339
340 T::REGS.cr().modify(|w| {
341 w.set_fthres(config.fifo_threshold.into());
342 });
343
344 // Wait for busy flag to clear
345 while T::REGS.sr().read().busy() {}
346
347 T::REGS.dcr2().modify(|w| {
348 w.set_prescaler(config.clock_prescaler);
349 });
350
351 // The configuration of clock prescaler trigger automatically a calibration process
352 // So it is necessary to wait the calibration is complete
353 while T::REGS.sr().read().busy() {}
354
355 if let Some(dual_memory_mode) = dual_memory_mode {
356 T::REGS.cr().modify(|w| {
357 w.set_dmm(dual_memory_mode);
358 });
359 }
360
361 T::REGS.tcr().modify(|w| {
362 w.set_sshift(config.sample_shifting);
363 w.set_dhqc(config.delay_hold_quarter_cycle);
364 });
365
366 // Enable peripheral
367 T::REGS.cr().modify(|w| {
368 w.set_en(true);
369 });
370
371 // Free running clock needs to be set after peripheral enable
372 if config.free_running_clock {
373 T::REGS.dcr1().modify(|w| {
374 w.set_frck(config.free_running_clock);
375 });
376 }
377 }
378
379 // Function to configure the peripheral for the requested command
380 fn configure_command(&mut self, command: &TransferConfig, data_len: Option<usize>) -> Result<(), HspiError> {
381 // Check that transaction doesn't use more than hardware initialized pins
382 if <enums::HspiWidth as Into<u8>>::into(command.iwidth) > <enums::HspiWidth as Into<u8>>::into(self.width)
383 || <enums::HspiWidth as Into<u8>>::into(command.adwidth) > <enums::HspiWidth as Into<u8>>::into(self.width)
384 || <enums::HspiWidth as Into<u8>>::into(command.abwidth) > <enums::HspiWidth as Into<u8>>::into(self.width)
385 || <enums::HspiWidth as Into<u8>>::into(command.dwidth) > <enums::HspiWidth as Into<u8>>::into(self.width)
386 {
387 return Err(HspiError::InvalidCommand);
388 }
389
390 while T::REGS.sr().read().busy() {}
391
392 T::REGS.cr().modify(|w| {
393 w.set_fmode(0.into());
394 });
395
396 // Configure alternate bytes
397 if let Some(ab) = command.alternate_bytes {
398 T::REGS.abr().write(|v| v.set_alternate(ab));
399 T::REGS.ccr().modify(|w| {
400 w.set_abmode(command.abwidth.into());
401 w.set_abdtr(command.abdtr);
402 w.set_absize(command.absize.into());
403 })
404 }
405
406 // Configure dummy cycles
407 T::REGS.tcr().modify(|w| {
408 w.set_dcyc(command.dummy.into());
409 });
410
411 // Configure data
412 if let Some(data_length) = data_len {
413 T::REGS.dlr().write(|v| {
414 v.set_dl((data_length - 1) as u32);
415 })
416 } else {
417 T::REGS.dlr().write(|v| {
418 v.set_dl((0) as u32);
419 })
420 }
421
422 // Configure instruction/address/data modes
423 T::REGS.ccr().modify(|w| {
424 w.set_imode(command.iwidth.into());
425 w.set_idtr(command.idtr);
426 w.set_isize(command.isize.into());
427
428 w.set_admode(command.adwidth.into());
429 w.set_addtr(command.addtr);
430 w.set_adsize(command.adsize.into());
431
432 w.set_dmode(command.dwidth.into());
433 w.set_ddtr(command.ddtr);
434 });
435
436 // Configure DQS
437 T::REGS.ccr().modify(|w| {
438 w.set_dqse(command.ddtr && command.instruction.unwrap_or(0) != 0x12ED);
439 });
440
441 // Set information required to initiate transaction
442 if let Some(instruction) = command.instruction {
443 if let Some(address) = command.address {
444 T::REGS.ir().write(|v| {
445 v.set_instruction(instruction);
446 });
447
448 T::REGS.ar().write(|v| {
449 v.set_address(address);
450 });
451 } else {
452 T::REGS.ir().write(|v| {
453 v.set_instruction(instruction);
454 });
455 }
456 } else {
457 if let Some(address) = command.address {
458 T::REGS.ar().write(|v| {
459 v.set_address(address);
460 });
461 } else {
462 // The only single phase transaction supported is instruction only
463 return Err(HspiError::InvalidCommand);
464 }
465 }
466
467 Ok(())
468 }
469
470 /// Function used to control or configure the target device without data transfer
471 pub fn blocking_command(&mut self, command: &TransferConfig) -> Result<(), HspiError> {
472 // Wait for peripheral to be free
473 while T::REGS.sr().read().busy() {}
474
475 // Need additional validation that command configuration doesn't have data set
476 self.configure_command(command, None)?;
477
478 // Transaction initiated by setting final configuration, i.e the instruction register
479 while !T::REGS.sr().read().tcf() {}
480 T::REGS.fcr().write(|w| {
481 w.set_ctcf(true);
482 });
483
484 Ok(())
485 }
486
487 /// Blocking read with byte by byte data transfer
488 pub fn blocking_read<W: Word>(&mut self, buf: &mut [W], transaction: TransferConfig) -> Result<(), HspiError> {
489 if buf.is_empty() {
490 return Err(HspiError::EmptyBuffer);
491 }
492
493 // Wait for peripheral to be free
494 while T::REGS.sr().read().busy() {}
495
496 // Ensure DMA is not enabled for this transaction
497 T::REGS.cr().modify(|w| {
498 w.set_dmaen(false);
499 });
500
501 self.configure_command(&transaction, Some(buf.len()))?;
502
503 let current_address = T::REGS.ar().read().address();
504 let current_instruction = T::REGS.ir().read().instruction();
505
506 // For a indirect read transaction, the transaction begins when the instruction/address is set
507 T::REGS
508 .cr()
509 .modify(|v| v.set_fmode(FunctionalMode::IndirectRead.into()));
510 if T::REGS.ccr().read().admode() == HspiWidth::NONE.into() {
511 T::REGS.ir().write(|v| v.set_instruction(current_instruction));
512 } else {
513 T::REGS.ar().write(|v| v.set_address(current_address));
514 }
515
516 for idx in 0..buf.len() {
517 while !T::REGS.sr().read().tcf() && !T::REGS.sr().read().ftf() {}
518 buf[idx] = unsafe { (T::REGS.dr().as_ptr() as *mut W).read_volatile() };
519 }
520
521 while !T::REGS.sr().read().tcf() {}
522 T::REGS.fcr().write(|v| v.set_ctcf(true));
523
524 Ok(())
525 }
526
527 /// Blocking write with byte by byte data transfer
528 pub fn blocking_write<W: Word>(&mut self, buf: &[W], transaction: TransferConfig) -> Result<(), HspiError> {
529 if buf.is_empty() {
530 return Err(HspiError::EmptyBuffer);
531 }
532
533 // Wait for peripheral to be free
534 while T::REGS.sr().read().busy() {}
535
536 T::REGS.cr().modify(|w| {
537 w.set_dmaen(false);
538 });
539
540 self.configure_command(&transaction, Some(buf.len()))?;
541
542 T::REGS
543 .cr()
544 .modify(|v| v.set_fmode(FunctionalMode::IndirectWrite.into()));
545
546 for idx in 0..buf.len() {
547 while !T::REGS.sr().read().ftf() {}
548 unsafe { (T::REGS.dr().as_ptr() as *mut W).write_volatile(buf[idx]) };
549 }
550
551 while !T::REGS.sr().read().tcf() {}
552 T::REGS.fcr().write(|v| v.set_ctcf(true));
553
554 Ok(())
555 }
556
557 /// Set new bus configuration
558 pub fn set_config(&mut self, config: &Config) {
559 // Wait for busy flag to clear
560 while T::REGS.sr().read().busy() {}
561
562 // Disable DMA channel while configuring the peripheral
563 T::REGS.cr().modify(|w| {
564 w.set_dmaen(false);
565 });
566
567 Self::configure_registers(config, None);
568
569 self.config = *config;
570 }
571
572 /// Get current configuration
573 pub fn get_config(&self) -> Config {
574 self.config
575 }
576}
577
578impl<'d, T: Instance> Hspi<'d, T, Blocking> {
579 /// Create new blocking HSPI driver for single spi external chip
580 pub fn new_blocking_singlespi(
581 peri: impl Peripheral<P = T> + 'd,
582 sck: impl Peripheral<P = impl SckPin<T>> + 'd,
583 d0: impl Peripheral<P = impl D0Pin<T>> + 'd,
584 d1: impl Peripheral<P = impl D1Pin<T>> + 'd,
585 nss: impl Peripheral<P = impl NSSPin<T>> + 'd,
586 config: Config,
587 ) -> Self {
588 Self::new_inner(
589 peri,
590 new_pin!(d0, AfType::output(OutputType::PushPull, Speed::VeryHigh)),
591 new_pin!(d1, AfType::input(Pull::None)),
592 None,
593 None,
594 None,
595 None,
596 None,
597 None,
598 None,
599 None,
600 None,
601 None,
602 None,
603 None,
604 None,
605 None,
606 new_pin!(sck, AfType::output(OutputType::PushPull, Speed::VeryHigh)),
607 new_pin!(
608 nss,
609 AfType::output_pull(OutputType::PushPull, Speed::VeryHigh, Pull::Up)
610 ),
611 None,
612 None,
613 None,
614 config,
615 HspiWidth::SING,
616 false,
617 )
618 }
619
620 /// Create new blocking HSPI driver for octospi external chip
621 pub fn new_blocking_octospi(
622 peri: impl Peripheral<P = T> + 'd,
623 sck: impl Peripheral<P = impl SckPin<T>> + 'd,
624 d0: impl Peripheral<P = impl D0Pin<T>> + 'd,
625 d1: impl Peripheral<P = impl D1Pin<T>> + 'd,
626 d2: impl Peripheral<P = impl D2Pin<T>> + 'd,
627 d3: impl Peripheral<P = impl D3Pin<T>> + 'd,
628 d4: impl Peripheral<P = impl D4Pin<T>> + 'd,
629 d5: impl Peripheral<P = impl D5Pin<T>> + 'd,
630 d6: impl Peripheral<P = impl D6Pin<T>> + 'd,
631 d7: impl Peripheral<P = impl D7Pin<T>> + 'd,
632 nss: impl Peripheral<P = impl NSSPin<T>> + 'd,
633 dqs0: impl Peripheral<P = impl DQS0Pin<T>> + 'd,
634 config: Config,
635 ) -> Self {
636 Self::new_inner(
637 peri,
638 new_pin!(d0, AfType::output(OutputType::PushPull, Speed::VeryHigh)),
639 new_pin!(d1, AfType::output(OutputType::PushPull, Speed::VeryHigh)),
640 new_pin!(d2, AfType::output(OutputType::PushPull, Speed::VeryHigh)),
641 new_pin!(d3, AfType::output(OutputType::PushPull, Speed::VeryHigh)),
642 new_pin!(d4, AfType::output(OutputType::PushPull, Speed::VeryHigh)),
643 new_pin!(d5, AfType::output(OutputType::PushPull, Speed::VeryHigh)),
644 new_pin!(d6, AfType::output(OutputType::PushPull, Speed::VeryHigh)),
645 new_pin!(d7, AfType::output(OutputType::PushPull, Speed::VeryHigh)),
646 None,
647 None,
648 None,
649 None,
650 None,
651 None,
652 None,
653 None,
654 new_pin!(sck, AfType::output(OutputType::PushPull, Speed::VeryHigh)),
655 new_pin!(
656 nss,
657 AfType::output_pull(OutputType::PushPull, Speed::VeryHigh, Pull::Up)
658 ),
659 new_pin!(dqs0, AfType::output(OutputType::PushPull, Speed::VeryHigh)),
660 None,
661 None,
662 config,
663 HspiWidth::OCTO,
664 false,
665 )
666 }
667}
668
669impl<'d, T: Instance> Hspi<'d, T, Async> {
670 /// Create new HSPI driver for a single spi external chip
671 pub fn new_singlespi(
672 peri: impl Peripheral<P = T> + 'd,
673 sck: impl Peripheral<P = impl SckPin<T>> + 'd,
674 d0: impl Peripheral<P = impl D0Pin<T>> + 'd,
675 d1: impl Peripheral<P = impl D1Pin<T>> + 'd,
676 nss: impl Peripheral<P = impl NSSPin<T>> + 'd,
677 dma: impl Peripheral<P = impl HspiDma<T>> + 'd,
678 config: Config,
679 ) -> Self {
680 Self::new_inner(
681 peri,
682 new_pin!(d0, AfType::output(OutputType::PushPull, Speed::VeryHigh)),
683 new_pin!(d1, AfType::input(Pull::None)),
684 None,
685 None,
686 None,
687 None,
688 None,
689 None,
690 None,
691 None,
692 None,
693 None,
694 None,
695 None,
696 None,
697 None,
698 new_pin!(sck, AfType::output(OutputType::PushPull, Speed::VeryHigh)),
699 new_pin!(
700 nss,
701 AfType::output_pull(OutputType::PushPull, Speed::VeryHigh, Pull::Up)
702 ),
703 None,
704 None,
705 new_dma!(dma),
706 config,
707 HspiWidth::SING,
708 false,
709 )
710 }
711
712 /// Create new HSPI driver for octospi external chip
713 pub fn new_octospi(
714 peri: impl Peripheral<P = T> + 'd,
715 sck: impl Peripheral<P = impl SckPin<T>> + 'd,
716 d0: impl Peripheral<P = impl D0Pin<T>> + 'd,
717 d1: impl Peripheral<P = impl D1Pin<T>> + 'd,
718 d2: impl Peripheral<P = impl D2Pin<T>> + 'd,
719 d3: impl Peripheral<P = impl D3Pin<T>> + 'd,
720 d4: impl Peripheral<P = impl D4Pin<T>> + 'd,
721 d5: impl Peripheral<P = impl D5Pin<T>> + 'd,
722 d6: impl Peripheral<P = impl D6Pin<T>> + 'd,
723 d7: impl Peripheral<P = impl D7Pin<T>> + 'd,
724 nss: impl Peripheral<P = impl NSSPin<T>> + 'd,
725 dqs0: impl Peripheral<P = impl DQS0Pin<T>> + 'd,
726 dma: impl Peripheral<P = impl HspiDma<T>> + 'd,
727 config: Config,
728 ) -> Self {
729 Self::new_inner(
730 peri,
731 new_pin!(d0, AfType::output(OutputType::PushPull, Speed::VeryHigh)),
732 new_pin!(d1, AfType::output(OutputType::PushPull, Speed::VeryHigh)),
733 new_pin!(d2, AfType::output(OutputType::PushPull, Speed::VeryHigh)),
734 new_pin!(d3, AfType::output(OutputType::PushPull, Speed::VeryHigh)),
735 new_pin!(d4, AfType::output(OutputType::PushPull, Speed::VeryHigh)),
736 new_pin!(d5, AfType::output(OutputType::PushPull, Speed::VeryHigh)),
737 new_pin!(d6, AfType::output(OutputType::PushPull, Speed::VeryHigh)),
738 new_pin!(d7, AfType::output(OutputType::PushPull, Speed::VeryHigh)),
739 new_pin!(sck, AfType::output(OutputType::PushPull, Speed::VeryHigh)),
740 None,
741 None,
742 None,
743 None,
744 None,
745 None,
746 None,
747 None,
748 new_pin!(
749 nss,
750 AfType::output_pull(OutputType::PushPull, Speed::VeryHigh, Pull::Up)
751 ),
752 new_pin!(dqs0, AfType::output(OutputType::PushPull, Speed::VeryHigh)),
753 None,
754 new_dma!(dma),
755 config,
756 HspiWidth::OCTO,
757 false,
758 )
759 }
760
761 /// Blocking read with DMA transfer
762 pub fn blocking_read_dma<W: Word>(&mut self, buf: &mut [W], transaction: TransferConfig) -> Result<(), HspiError> {
763 if buf.is_empty() {
764 return Err(HspiError::EmptyBuffer);
765 }
766
767 // Wait for peripheral to be free
768 while T::REGS.sr().read().busy() {}
769
770 self.configure_command(&transaction, Some(buf.len()))?;
771
772 let current_address = T::REGS.ar().read().address();
773 let current_instruction = T::REGS.ir().read().instruction();
774
775 // For a indirect read transaction, the transaction begins when the instruction/address is set
776 T::REGS
777 .cr()
778 .modify(|v| v.set_fmode(FunctionalMode::IndirectRead.into()));
779 if T::REGS.ccr().read().admode() == HspiWidth::NONE.into() {
780 T::REGS.ir().write(|v| v.set_instruction(current_instruction));
781 } else {
782 T::REGS.ar().write(|v| v.set_address(current_address));
783 }
784
785 let transfer = unsafe {
786 self.dma
787 .as_mut()
788 .unwrap()
789 .read(T::REGS.dr().as_ptr() as *mut W, buf, Default::default())
790 };
791
792 T::REGS.cr().modify(|w| w.set_dmaen(true));
793
794 transfer.blocking_wait();
795
796 finish_dma(T::REGS);
797
798 Ok(())
799 }
800
801 /// Blocking write with DMA transfer
802 pub fn blocking_write_dma<W: Word>(&mut self, buf: &[W], transaction: TransferConfig) -> Result<(), HspiError> {
803 if buf.is_empty() {
804 return Err(HspiError::EmptyBuffer);
805 }
806
807 // Wait for peripheral to be free
808 while T::REGS.sr().read().busy() {}
809
810 self.configure_command(&transaction, Some(buf.len()))?;
811 T::REGS
812 .cr()
813 .modify(|v| v.set_fmode(FunctionalMode::IndirectWrite.into()));
814
815 let transfer = unsafe {
816 self.dma
817 .as_mut()
818 .unwrap()
819 .write(buf, T::REGS.dr().as_ptr() as *mut W, Default::default())
820 };
821
822 T::REGS.cr().modify(|w| w.set_dmaen(true));
823
824 transfer.blocking_wait();
825
826 finish_dma(T::REGS);
827
828 Ok(())
829 }
830
831 /// Asynchronous read from external device
832 pub async fn read<W: Word>(&mut self, buf: &mut [W], transaction: TransferConfig) -> Result<(), HspiError> {
833 if buf.is_empty() {
834 return Err(HspiError::EmptyBuffer);
835 }
836
837 // Wait for peripheral to be free
838 while T::REGS.sr().read().busy() {}
839
840 self.configure_command(&transaction, Some(buf.len()))?;
841
842 let current_address = T::REGS.ar().read().address();
843 let current_instruction = T::REGS.ir().read().instruction();
844
845 // For a indirect read transaction, the transaction begins when the instruction/address is set
846 T::REGS
847 .cr()
848 .modify(|v| v.set_fmode(FunctionalMode::IndirectRead.into()));
849 if T::REGS.ccr().read().admode() == HspiWidth::NONE.into() {
850 T::REGS.ir().write(|v| v.set_instruction(current_instruction));
851 } else {
852 T::REGS.ar().write(|v| v.set_address(current_address));
853 }
854
855 let transfer = unsafe {
856 self.dma
857 .as_mut()
858 .unwrap()
859 .read(T::REGS.dr().as_ptr() as *mut W, buf, Default::default())
860 };
861
862 T::REGS.cr().modify(|w| w.set_dmaen(true));
863
864 transfer.await;
865
866 finish_dma(T::REGS);
867
868 Ok(())
869 }
870
871 /// Asynchronous write to external device
872 pub async fn write<W: Word>(&mut self, buf: &[W], transaction: TransferConfig) -> Result<(), HspiError> {
873 if buf.is_empty() {
874 return Err(HspiError::EmptyBuffer);
875 }
876
877 // Wait for peripheral to be free
878 while T::REGS.sr().read().busy() {}
879
880 self.configure_command(&transaction, Some(buf.len()))?;
881 T::REGS
882 .cr()
883 .modify(|v| v.set_fmode(FunctionalMode::IndirectWrite.into()));
884
885 let transfer = unsafe {
886 self.dma
887 .as_mut()
888 .unwrap()
889 .write(buf, T::REGS.dr().as_ptr() as *mut W, Default::default())
890 };
891
892 T::REGS.cr().modify(|w| w.set_dmaen(true));
893
894 transfer.await;
895
896 finish_dma(T::REGS);
897
898 Ok(())
899 }
900}
901
902impl<'d, T: Instance, M: PeriMode> Drop for Hspi<'d, T, M> {
903 fn drop(&mut self) {
904 self.sck.as_ref().map(|x| x.set_as_disconnected());
905 self.d0.as_ref().map(|x| x.set_as_disconnected());
906 self.d1.as_ref().map(|x| x.set_as_disconnected());
907 self.d2.as_ref().map(|x| x.set_as_disconnected());
908 self.d3.as_ref().map(|x| x.set_as_disconnected());
909 self.d4.as_ref().map(|x| x.set_as_disconnected());
910 self.d5.as_ref().map(|x| x.set_as_disconnected());
911 self.d6.as_ref().map(|x| x.set_as_disconnected());
912 self.d7.as_ref().map(|x| x.set_as_disconnected());
913 self.d8.as_ref().map(|x| x.set_as_disconnected());
914 self.d9.as_ref().map(|x| x.set_as_disconnected());
915 self.d10.as_ref().map(|x| x.set_as_disconnected());
916 self.d11.as_ref().map(|x| x.set_as_disconnected());
917 self.d12.as_ref().map(|x| x.set_as_disconnected());
918 self.d13.as_ref().map(|x| x.set_as_disconnected());
919 self.d14.as_ref().map(|x| x.set_as_disconnected());
920 self.d15.as_ref().map(|x| x.set_as_disconnected());
921 self.nss.as_ref().map(|x| x.set_as_disconnected());
922 self.dqs0.as_ref().map(|x| x.set_as_disconnected());
923 self.dqs1.as_ref().map(|x| x.set_as_disconnected());
924
925 rcc::disable::<T>();
926 }
927}
928
929fn finish_dma(regs: Regs) {
930 while !regs.sr().read().tcf() {}
931 regs.fcr().write(|v: &mut Fcr| v.set_ctcf(val:true));
932
933 regs.cr().modify(|w: &mut Cr| {
934 w.set_dmaen(val:false);
935 });
936}
937
938/// HSPI instance trait.
939pub(crate) trait SealedInstance {
940 const REGS: Regs;
941}
942
943/// HSPI instance trait.
944#[allow(private_bounds)]
945pub trait Instance: Peripheral<P = Self> + SealedInstance + RccPeripheral {}
946
947pin_trait!(SckPin, Instance);
948pin_trait!(NckPin, Instance);
949pin_trait!(D0Pin, Instance);
950pin_trait!(D1Pin, Instance);
951pin_trait!(D2Pin, Instance);
952pin_trait!(D3Pin, Instance);
953pin_trait!(D4Pin, Instance);
954pin_trait!(D5Pin, Instance);
955pin_trait!(D6Pin, Instance);
956pin_trait!(D7Pin, Instance);
957pin_trait!(D8Pin, Instance);
958pin_trait!(D9Pin, Instance);
959pin_trait!(D10Pin, Instance);
960pin_trait!(D11Pin, Instance);
961pin_trait!(D12Pin, Instance);
962pin_trait!(D13Pin, Instance);
963pin_trait!(D14Pin, Instance);
964pin_trait!(D15Pin, Instance);
965pin_trait!(DQS0Pin, Instance);
966pin_trait!(DQS1Pin, Instance);
967pin_trait!(NSSPin, Instance);
968dma_trait!(HspiDma, Instance);
969
970foreach_peripheral!(
971 (hspi, $inst:ident) => {
972 impl SealedInstance for peripherals::$inst {
973 const REGS: Regs = crate::pac::$inst;
974 }
975
976 impl Instance for peripherals::$inst {}
977 };
978);
979
980impl<'d, T: Instance, M: PeriMode> SetConfig for Hspi<'d, T, M> {
981 type Config = Config;
982 type ConfigError = ();
983 fn set_config(&mut self, config: &Self::Config) -> Result<(), ()> {
984 self.set_config(config);
985 Ok(())
986 }
987}
988
989impl<'d, T: Instance, M: PeriMode> GetConfig for Hspi<'d, T, M> {
990 type Config = Config;
991 fn get_config(&self) -> Self::Config {
992 self.get_config()
993 }
994}
995
996/// Word sizes usable for HSPI.
997#[allow(private_bounds)]
998pub trait Word: word::Word {}
999
1000macro_rules! impl_word {
1001 ($T:ty) => {
1002 impl Word for $T {}
1003 };
1004}
1005
1006impl_word!(u8);
1007impl_word!(u16);
1008impl_word!(u32);
1009