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