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 | |
11 | pub mod enums; |
12 | |
13 | use core::marker::PhantomData; |
14 | |
15 | use embassy_embedded_hal::{GetConfig, SetConfig}; |
16 | use embassy_hal_internal::{into_ref, PeripheralRef}; |
17 | pub use enums::*; |
18 | |
19 | use crate::dma::{word, ChannelAndRequest}; |
20 | use crate::gpio::{AfType, AnyPin, OutputType, Pull, SealedPin as _, Speed}; |
21 | use crate::mode::{Async, Blocking, Mode as PeriMode}; |
22 | use crate::pac::hspi::Hspi as Regs; |
23 | use crate::rcc::{self, RccPeripheral}; |
24 | use crate::{peripherals, Peripheral}; |
25 | |
26 | /// HSPI driver config. |
27 | #[derive (Clone, Copy, defmt::Format)] |
28 | pub 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 | |
66 | impl 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. |
88 | pub 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 | |
125 | impl 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))] |
154 | pub 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. |
164 | pub 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 | |
192 | impl<'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 | |
578 | impl<'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 | |
669 | impl<'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 | |
902 | impl<'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 | |
929 | fn 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. |
939 | pub(crate) trait SealedInstance { |
940 | const REGS: Regs; |
941 | } |
942 | |
943 | /// HSPI instance trait. |
944 | #[allow (private_bounds)] |
945 | pub trait Instance: Peripheral<P = Self> + SealedInstance + RccPeripheral {} |
946 | |
947 | pin_trait!(SckPin, Instance); |
948 | pin_trait!(NckPin, Instance); |
949 | pin_trait!(D0Pin, Instance); |
950 | pin_trait!(D1Pin, Instance); |
951 | pin_trait!(D2Pin, Instance); |
952 | pin_trait!(D3Pin, Instance); |
953 | pin_trait!(D4Pin, Instance); |
954 | pin_trait!(D5Pin, Instance); |
955 | pin_trait!(D6Pin, Instance); |
956 | pin_trait!(D7Pin, Instance); |
957 | pin_trait!(D8Pin, Instance); |
958 | pin_trait!(D9Pin, Instance); |
959 | pin_trait!(D10Pin, Instance); |
960 | pin_trait!(D11Pin, Instance); |
961 | pin_trait!(D12Pin, Instance); |
962 | pin_trait!(D13Pin, Instance); |
963 | pin_trait!(D14Pin, Instance); |
964 | pin_trait!(D15Pin, Instance); |
965 | pin_trait!(DQS0Pin, Instance); |
966 | pin_trait!(DQS1Pin, Instance); |
967 | pin_trait!(NSSPin, Instance); |
968 | dma_trait!(HspiDma, Instance); |
969 | |
970 | foreach_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 | |
980 | impl<'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 | |
989 | impl<'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)] |
998 | pub trait Word: word::Word {} |
999 | |
1000 | macro_rules! impl_word { |
1001 | ($T:ty) => { |
1002 | impl Word for $T {} |
1003 | }; |
1004 | } |
1005 | |
1006 | impl_word!(u8); |
1007 | impl_word!(u16); |
1008 | impl_word!(u32); |
1009 | |