embassy_stm32/qspi/
mod.rs

1//! Quad Serial Peripheral Interface (QSPI)
2
3#![macro_use]
4
5pub mod enums;
6
7use core::marker::PhantomData;
8
9use embassy_hal_internal::PeripheralType;
10use enums::*;
11
12use crate::dma::ChannelAndRequest;
13use crate::gpio::{AfType, AnyPin, OutputType, Pull, Speed};
14use crate::mode::{Async, Blocking, Mode as PeriMode};
15use crate::pac::quadspi::Quadspi as Regs;
16use crate::rcc::{self, RccPeripheral};
17use crate::{peripherals, Peri};
18
19/// QSPI transfer configuration.
20pub struct TransferConfig {
21    /// Instruction width (IMODE)
22    pub iwidth: QspiWidth,
23    /// Address width (ADMODE)
24    pub awidth: QspiWidth,
25    /// Data width (DMODE)
26    pub dwidth: QspiWidth,
27    /// Instruction Id
28    pub instruction: u8,
29    /// Flash memory address
30    pub address: Option<u32>,
31    /// Number of dummy cycles (DCYC)
32    pub dummy: DummyCycles,
33}
34
35impl Default for TransferConfig {
36    fn default() -> Self {
37        Self {
38            iwidth: QspiWidth::NONE,
39            awidth: QspiWidth::NONE,
40            dwidth: QspiWidth::NONE,
41            instruction: 0,
42            address: None,
43            dummy: DummyCycles::_0,
44        }
45    }
46}
47
48/// QSPI driver configuration.
49pub struct Config {
50    /// Flash memory size representend as 2^[0-32], as reasonable minimum 1KiB(9) was chosen.
51    /// If you need other value the whose predefined use `Other` variant.
52    pub memory_size: MemorySize,
53    /// Address size (8/16/24/32-bit)
54    pub address_size: AddressSize,
55    /// Scalar factor for generating CLK [0-255]
56    pub prescaler: u8,
57    /// Number of bytes to trigger FIFO threshold flag.
58    pub fifo_threshold: FIFOThresholdLevel,
59    /// Minimum number of cycles that chip select must be high between issued commands
60    pub cs_high_time: ChipSelectHighTime,
61    /// Shift sampling point of input data (none, or half-cycle)
62    pub sample_shifting: SampleShifting,
63}
64
65impl Default for Config {
66    fn default() -> Self {
67        Self {
68            memory_size: MemorySize::Other(0),
69            address_size: AddressSize::_24bit,
70            prescaler: 128,
71            fifo_threshold: FIFOThresholdLevel::_17Bytes,
72            cs_high_time: ChipSelectHighTime::_5Cycle,
73            sample_shifting: SampleShifting::None,
74        }
75    }
76}
77
78/// QSPI driver.
79#[allow(dead_code)]
80pub struct Qspi<'d, T: Instance, M: PeriMode> {
81    _peri: Peri<'d, T>,
82    sck: Option<Peri<'d, AnyPin>>,
83    d0: Option<Peri<'d, AnyPin>>,
84    d1: Option<Peri<'d, AnyPin>>,
85    d2: Option<Peri<'d, AnyPin>>,
86    d3: Option<Peri<'d, AnyPin>>,
87    nss: Option<Peri<'d, AnyPin>>,
88    dma: Option<ChannelAndRequest<'d>>,
89    _phantom: PhantomData<M>,
90    config: Config,
91}
92
93impl<'d, T: Instance, M: PeriMode> Qspi<'d, T, M> {
94    fn new_inner(
95        peri: Peri<'d, T>,
96        d0: Option<Peri<'d, AnyPin>>,
97        d1: Option<Peri<'d, AnyPin>>,
98        d2: Option<Peri<'d, AnyPin>>,
99        d3: Option<Peri<'d, AnyPin>>,
100        sck: Option<Peri<'d, AnyPin>>,
101        nss: Option<Peri<'d, AnyPin>>,
102        dma: Option<ChannelAndRequest<'d>>,
103        config: Config,
104        fsel: FlashSelection,
105    ) -> Self {
106        rcc::enable_and_reset::<T>();
107
108        while T::REGS.sr().read().busy() {}
109
110        #[cfg(stm32h7)]
111        {
112            use stm32_metapac::quadspi::regs::Cr;
113            // Apply precautionary steps according to the errata...
114            T::REGS.cr().write_value(Cr(0));
115            while T::REGS.sr().read().busy() {}
116            T::REGS.cr().write_value(Cr(0xFF000001));
117            T::REGS.ccr().write(|w| w.set_frcm(true));
118            T::REGS.ccr().write(|w| w.set_frcm(true));
119            T::REGS.cr().write_value(Cr(0));
120            while T::REGS.sr().read().busy() {}
121        }
122
123        T::REGS.cr().modify(|w| {
124            w.set_en(true);
125            //w.set_tcen(false);
126            w.set_sshift(config.sample_shifting.into());
127            w.set_fthres(config.fifo_threshold.into());
128            w.set_prescaler(config.prescaler);
129            w.set_fsel(fsel.into());
130        });
131        T::REGS.dcr().modify(|w| {
132            w.set_fsize(config.memory_size.into());
133            w.set_csht(config.cs_high_time.into());
134            w.set_ckmode(true);
135        });
136
137        Self {
138            _peri: peri,
139            sck,
140            d0,
141            d1,
142            d2,
143            d3,
144            nss,
145            dma,
146            _phantom: PhantomData,
147            config,
148        }
149    }
150
151    /// Do a QSPI command.
152    pub fn blocking_command(&mut self, transaction: TransferConfig) {
153        #[cfg(not(stm32h7))]
154        T::REGS.cr().modify(|v| v.set_dmaen(false));
155        self.setup_transaction(QspiMode::IndirectWrite, &transaction, None);
156
157        while !T::REGS.sr().read().tcf() {}
158        T::REGS.fcr().modify(|v| v.set_ctcf(true));
159    }
160
161    /// Blocking read data.
162    pub fn blocking_read(&mut self, buf: &mut [u8], transaction: TransferConfig) {
163        #[cfg(not(stm32h7))]
164        T::REGS.cr().modify(|v| v.set_dmaen(false));
165        self.setup_transaction(QspiMode::IndirectWrite, &transaction, Some(buf.len()));
166
167        let current_ar = T::REGS.ar().read().address();
168        T::REGS.ccr().modify(|v| {
169            v.set_fmode(QspiMode::IndirectRead.into());
170        });
171        T::REGS.ar().write(|v| {
172            v.set_address(current_ar);
173        });
174
175        for b in buf {
176            while !T::REGS.sr().read().tcf() && (T::REGS.sr().read().flevel() == 0) {}
177            *b = unsafe { (T::REGS.dr().as_ptr() as *mut u8).read_volatile() };
178        }
179
180        while !T::REGS.sr().read().tcf() {}
181        T::REGS.fcr().modify(|v| v.set_ctcf(true));
182    }
183
184    /// Blocking write data.
185    pub fn blocking_write(&mut self, buf: &[u8], transaction: TransferConfig) {
186        // STM32H7 does not have dmaen
187        #[cfg(not(stm32h7))]
188        T::REGS.cr().modify(|v| v.set_dmaen(false));
189
190        self.setup_transaction(QspiMode::IndirectWrite, &transaction, Some(buf.len()));
191
192        T::REGS.ccr().modify(|v| {
193            v.set_fmode(QspiMode::IndirectWrite.into());
194        });
195
196        for &b in buf {
197            while !T::REGS.sr().read().ftf() {}
198            unsafe { (T::REGS.dr().as_ptr() as *mut u8).write_volatile(b) };
199        }
200
201        while !T::REGS.sr().read().tcf() {}
202        T::REGS.fcr().modify(|v| v.set_ctcf(true));
203    }
204
205    /// Enable memory map mode
206    pub fn enable_memory_map(&mut self, transaction: &TransferConfig) {
207        T::REGS.fcr().modify(|v| {
208            v.set_csmf(true);
209            v.set_ctcf(true);
210            v.set_ctef(true);
211            v.set_ctof(true);
212        });
213        T::REGS.ccr().write(|v| {
214            v.set_fmode(QspiMode::MemoryMapped.into());
215            v.set_imode(transaction.iwidth.into());
216            v.set_instruction(transaction.instruction);
217            v.set_admode(transaction.awidth.into());
218            v.set_adsize(self.config.address_size.into());
219            v.set_dmode(transaction.dwidth.into());
220            v.set_abmode(QspiWidth::NONE.into());
221            v.set_dcyc(transaction.dummy.into());
222        });
223    }
224
225    fn setup_transaction(&mut self, fmode: QspiMode, transaction: &TransferConfig, data_len: Option<usize>) {
226        match (transaction.address, transaction.awidth) {
227            (Some(_), QspiWidth::NONE) => panic!("QSPI address can't be sent with an address width of NONE"),
228            (Some(_), _) => {}
229            (None, QspiWidth::NONE) => {}
230            (None, _) => panic!("QSPI address is not set, so the address width should be NONE"),
231        }
232
233        match (data_len, transaction.dwidth) {
234            (Some(0), _) => panic!("QSPI data must be at least one byte"),
235            (Some(_), QspiWidth::NONE) => panic!("QSPI data can't be sent with a data width of NONE"),
236            (Some(_), _) => {}
237            (None, QspiWidth::NONE) => {}
238            (None, _) => panic!("QSPI data is empty, so the data width should be NONE"),
239        }
240
241        T::REGS.fcr().modify(|v| {
242            v.set_csmf(true);
243            v.set_ctcf(true);
244            v.set_ctef(true);
245            v.set_ctof(true);
246        });
247
248        while T::REGS.sr().read().busy() {}
249
250        if let Some(len) = data_len {
251            T::REGS.dlr().write(|v| v.set_dl(len as u32 - 1));
252        }
253
254        T::REGS.ccr().write(|v| {
255            v.set_fmode(fmode.into());
256            v.set_imode(transaction.iwidth.into());
257            v.set_instruction(transaction.instruction);
258            v.set_admode(transaction.awidth.into());
259            v.set_adsize(self.config.address_size.into());
260            v.set_dmode(transaction.dwidth.into());
261            v.set_abmode(QspiWidth::NONE.into());
262            v.set_dcyc(transaction.dummy.into());
263        });
264
265        if let Some(addr) = transaction.address {
266            T::REGS.ar().write(|v| {
267                v.set_address(addr);
268            });
269        }
270    }
271}
272
273impl<'d, T: Instance> Qspi<'d, T, Blocking> {
274    /// Create a new QSPI driver for bank 1, in blocking mode.
275    pub fn new_blocking_bank1(
276        peri: Peri<'d, T>,
277        d0: Peri<'d, impl BK1D0Pin<T>>,
278        d1: Peri<'d, impl BK1D1Pin<T>>,
279        d2: Peri<'d, impl BK1D2Pin<T>>,
280        d3: Peri<'d, impl BK1D3Pin<T>>,
281        sck: Peri<'d, impl SckPin<T>>,
282        nss: Peri<'d, impl BK1NSSPin<T>>,
283        config: Config,
284    ) -> Self {
285        Self::new_inner(
286            peri,
287            new_pin!(d0, AfType::output(OutputType::PushPull, Speed::VeryHigh)),
288            new_pin!(d1, AfType::output(OutputType::PushPull, Speed::VeryHigh)),
289            new_pin!(d2, AfType::output(OutputType::PushPull, Speed::VeryHigh)),
290            new_pin!(d3, AfType::output(OutputType::PushPull, Speed::VeryHigh)),
291            new_pin!(sck, AfType::output(OutputType::PushPull, Speed::VeryHigh)),
292            new_pin!(
293                nss,
294                AfType::output_pull(OutputType::PushPull, Speed::VeryHigh, Pull::Up)
295            ),
296            None,
297            config,
298            FlashSelection::Flash1,
299        )
300    }
301
302    /// Create a new QSPI driver for bank 2, in blocking mode.
303    pub fn new_blocking_bank2(
304        peri: Peri<'d, T>,
305        d0: Peri<'d, impl BK2D0Pin<T>>,
306        d1: Peri<'d, impl BK2D1Pin<T>>,
307        d2: Peri<'d, impl BK2D2Pin<T>>,
308        d3: Peri<'d, impl BK2D3Pin<T>>,
309        sck: Peri<'d, impl SckPin<T>>,
310        nss: Peri<'d, impl BK2NSSPin<T>>,
311        config: Config,
312    ) -> Self {
313        Self::new_inner(
314            peri,
315            new_pin!(d0, AfType::output(OutputType::PushPull, Speed::VeryHigh)),
316            new_pin!(d1, AfType::output(OutputType::PushPull, Speed::VeryHigh)),
317            new_pin!(d2, AfType::output(OutputType::PushPull, Speed::VeryHigh)),
318            new_pin!(d3, AfType::output(OutputType::PushPull, Speed::VeryHigh)),
319            new_pin!(sck, AfType::output(OutputType::PushPull, Speed::VeryHigh)),
320            new_pin!(
321                nss,
322                AfType::output_pull(OutputType::PushPull, Speed::VeryHigh, Pull::Up)
323            ),
324            None,
325            config,
326            FlashSelection::Flash2,
327        )
328    }
329}
330
331impl<'d, T: Instance> Qspi<'d, T, Async> {
332    /// Create a new QSPI driver for bank 1.
333    pub fn new_bank1(
334        peri: Peri<'d, T>,
335        d0: Peri<'d, impl BK1D0Pin<T>>,
336        d1: Peri<'d, impl BK1D1Pin<T>>,
337        d2: Peri<'d, impl BK1D2Pin<T>>,
338        d3: Peri<'d, impl BK1D3Pin<T>>,
339        sck: Peri<'d, impl SckPin<T>>,
340        nss: Peri<'d, impl BK1NSSPin<T>>,
341        dma: Peri<'d, impl QuadDma<T>>,
342        config: Config,
343    ) -> Self {
344        Self::new_inner(
345            peri,
346            new_pin!(d0, AfType::output(OutputType::PushPull, Speed::VeryHigh)),
347            new_pin!(d1, AfType::output(OutputType::PushPull, Speed::VeryHigh)),
348            new_pin!(d2, AfType::output(OutputType::PushPull, Speed::VeryHigh)),
349            new_pin!(d3, AfType::output(OutputType::PushPull, Speed::VeryHigh)),
350            new_pin!(sck, AfType::output(OutputType::PushPull, Speed::VeryHigh)),
351            new_pin!(
352                nss,
353                AfType::output_pull(OutputType::PushPull, Speed::VeryHigh, Pull::Up)
354            ),
355            new_dma!(dma),
356            config,
357            FlashSelection::Flash1,
358        )
359    }
360
361    /// Create a new QSPI driver for bank 2.
362    pub fn new_bank2(
363        peri: Peri<'d, T>,
364        d0: Peri<'d, impl BK2D0Pin<T>>,
365        d1: Peri<'d, impl BK2D1Pin<T>>,
366        d2: Peri<'d, impl BK2D2Pin<T>>,
367        d3: Peri<'d, impl BK2D3Pin<T>>,
368        sck: Peri<'d, impl SckPin<T>>,
369        nss: Peri<'d, impl BK2NSSPin<T>>,
370        dma: Peri<'d, impl QuadDma<T>>,
371        config: Config,
372    ) -> Self {
373        Self::new_inner(
374            peri,
375            new_pin!(d0, AfType::output(OutputType::PushPull, Speed::VeryHigh)),
376            new_pin!(d1, AfType::output(OutputType::PushPull, Speed::VeryHigh)),
377            new_pin!(d2, AfType::output(OutputType::PushPull, Speed::VeryHigh)),
378            new_pin!(d3, AfType::output(OutputType::PushPull, Speed::VeryHigh)),
379            new_pin!(sck, AfType::output(OutputType::PushPull, Speed::VeryHigh)),
380            new_pin!(
381                nss,
382                AfType::output_pull(OutputType::PushPull, Speed::VeryHigh, Pull::Up)
383            ),
384            new_dma!(dma),
385            config,
386            FlashSelection::Flash2,
387        )
388    }
389
390    /// Blocking read data, using DMA.
391    pub fn blocking_read_dma(&mut self, buf: &mut [u8], transaction: TransferConfig) {
392        let transfer = self.start_read_transfer(transaction, buf);
393        transfer.blocking_wait();
394    }
395
396    /// Async read data, using DMA.
397    pub async fn read_dma(&mut self, buf: &mut [u8], transaction: TransferConfig) {
398        let transfer = self.start_read_transfer(transaction, buf);
399        transfer.await;
400    }
401
402    fn start_read_transfer<'a>(
403        &'a mut self,
404        transaction: TransferConfig,
405        buf: &'a mut [u8],
406    ) -> crate::dma::Transfer<'a> {
407        self.setup_transaction(QspiMode::IndirectWrite, &transaction, Some(buf.len()));
408
409        T::REGS.ccr().modify(|v| {
410            v.set_fmode(QspiMode::IndirectRead.into());
411        });
412        let current_ar = T::REGS.ar().read().address();
413        T::REGS.ar().write(|v| {
414            v.set_address(current_ar);
415        });
416
417        let transfer = unsafe {
418            self.dma
419                .as_mut()
420                .unwrap()
421                .read(T::REGS.dr().as_ptr() as *mut u8, buf, Default::default())
422        };
423
424        // STM32H7 does not have dmaen
425        #[cfg(not(stm32h7))]
426        T::REGS.cr().modify(|v| v.set_dmaen(true));
427        transfer
428    }
429
430    /// Blocking write data, using DMA.
431    pub fn blocking_write_dma(&mut self, buf: &[u8], transaction: TransferConfig) {
432        let transfer = self.start_write_transfer(transaction, buf);
433        transfer.blocking_wait();
434    }
435
436    /// Async write data, using DMA.
437    pub async fn write_dma(&mut self, buf: &[u8], transaction: TransferConfig) {
438        let transfer = self.start_write_transfer(transaction, buf);
439        transfer.await;
440    }
441
442    fn start_write_transfer<'a>(&'a mut self, transaction: TransferConfig, buf: &'a [u8]) -> crate::dma::Transfer<'a> {
443        self.setup_transaction(QspiMode::IndirectWrite, &transaction, Some(buf.len()));
444
445        T::REGS.ccr().modify(|v| {
446            v.set_fmode(QspiMode::IndirectWrite.into());
447        });
448
449        let transfer = unsafe {
450            self.dma
451                .as_mut()
452                .unwrap()
453                .write(buf, T::REGS.dr().as_ptr() as *mut u8, Default::default())
454        };
455
456        // STM32H7 does not have dmaen
457        #[cfg(not(stm32h7))]
458        T::REGS.cr().modify(|v| v.set_dmaen(true));
459        transfer
460    }
461}
462
463trait SealedInstance {
464    const REGS: Regs;
465}
466
467/// QSPI instance trait.
468#[allow(private_bounds)]
469pub trait Instance: SealedInstance + PeripheralType + RccPeripheral {}
470
471pin_trait!(SckPin, Instance);
472pin_trait!(BK1D0Pin, Instance);
473pin_trait!(BK1D1Pin, Instance);
474pin_trait!(BK1D2Pin, Instance);
475pin_trait!(BK1D3Pin, Instance);
476pin_trait!(BK1NSSPin, Instance);
477
478pin_trait!(BK2D0Pin, Instance);
479pin_trait!(BK2D1Pin, Instance);
480pin_trait!(BK2D2Pin, Instance);
481pin_trait!(BK2D3Pin, Instance);
482pin_trait!(BK2NSSPin, Instance);
483
484dma_trait!(QuadDma, Instance);
485
486foreach_peripheral!(
487    (quadspi, $inst:ident) => {
488        impl SealedInstance for peripherals::$inst {
489            const REGS: Regs = crate::pac::$inst;
490        }
491
492        impl Instance for peripherals::$inst {}
493    };
494);