diff --git a/src/xspi/mod.rs b/src/xspi/mod.rs index cdcd8a23..f9a8e376 100644 --- a/src/xspi/mod.rs +++ b/src/xspi/mod.rs @@ -116,8 +116,8 @@ mod qspi; #[cfg(any(feature = "rm0433", feature = "rm0399"))] pub use common::{ - Bank, Xspi as Qspi, XspiError as QspiError, XspiMode as QspiMode, - XspiWord as QspiWord, + Bank, BankError, BankSelect, Xspi as Qspi, XspiError as QspiError, + XspiMode as QspiMode, XspiWord as QspiWord, }; #[cfg(any(feature = "rm0433", feature = "rm0399"))] pub use qspi::QspiExt as XspiExt; @@ -248,7 +248,7 @@ mod common { Error, } - /// Indicates a specific QUADSPI bank to use + /// Indicates a specific QUADSPI bank to use. #[derive(Debug, Copy, Clone, PartialEq, Eq)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] #[cfg(any(feature = "rm0433", feature = "rm0399"))] @@ -260,6 +260,45 @@ mod common { // Banks are not supported by the Octospi peripheral (there's two Octospi // peripherals instead) + #[derive(Debug, Copy, Clone, PartialEq, Eq)] + #[cfg_attr(feature = "defmt", derive(defmt::Format))] + #[cfg(any(feature = "rm0433", feature = "rm0399"))] + pub enum BankError { + DualModeNotSupported, + } + + #[cfg(any(feature = "rm0433", feature = "rm0399"))] + impl TryFrom for BankSelect { + type Error = BankError; + + fn try_from(value: Bank) -> Result { + match value { + Bank::One => Ok(BankSelect::One), + Bank::Two => Ok(BankSelect::Two), + Bank::Dual => Err(BankError::DualModeNotSupported), + } + } + } + + /// Indicates one of the two existing QUADSPI bank. + #[derive(Debug, Copy, Clone, PartialEq, Eq)] + #[cfg_attr(feature = "defmt", derive(defmt::Format))] + #[cfg(any(feature = "rm0433", feature = "rm0399"))] + pub enum BankSelect { + One, + Two, + } + + #[cfg(any(feature = "rm0433", feature = "rm0399"))] + impl From for Bank { + fn from(val: BankSelect) -> Self { + match val { + BankSelect::One => Bank::One, + BankSelect::Two => Bank::Two, + } + } + } + /// A structure for specifying the XSPI configuration. /// /// This structure uses builder semantics to generate the configuration. diff --git a/src/xspi/qspi.rs b/src/xspi/qspi.rs index e4b03c95..d54a5ca2 100644 --- a/src/xspi/qspi.rs +++ b/src/xspi/qspi.rs @@ -8,7 +8,7 @@ use crate::{ stm32, }; -use super::{Bank, Config, Qspi, SamplingEdge}; +use super::{common::BankSelect, Bank, Config, Qspi, QspiError, SamplingEdge}; /// Used to indicate that an IO pin is not used by the QSPI interface. pub struct NoIo {} @@ -28,6 +28,9 @@ pub trait PinIo1Bank2 {} pub trait PinIo2Bank2 {} pub trait PinIo3Bank2 {} +/// Indicates a set of pins can be used for the QSPI interface on bank 1 and 2. +pub trait PinsBank1And2 {} + pub trait PinSck {} impl PinsBank1 for (SCK, IO0, IO1, IO2, IO3) @@ -50,6 +53,41 @@ where { } +impl< + SCK, + BK1_IO0, + BK1_IO1, + BK1_IO2, + BK1_IO3, + BK2_IO0, + BK2_IO1, + BK2_IO2, + BK2_IO3, + > PinsBank1And2 + for ( + SCK, + BK1_IO0, + BK1_IO1, + BK1_IO2, + BK1_IO3, + BK2_IO0, + BK2_IO1, + BK2_IO2, + BK2_IO3, + ) +where + SCK: PinSck, + BK1_IO0: PinIo0Bank1, + BK1_IO1: PinIo1Bank1, + BK1_IO2: PinIo2Bank1, + BK1_IO3: PinIo3Bank1, + BK2_IO0: PinIo0Bank2, + BK2_IO1: PinIo1Bank2, + BK2_IO2: PinIo2Bank2, + BK2_IO3: PinIo3Bank2, +{ +} + macro_rules! pins { (Bank1: [IO0: [$($IO0:ty),*] IO1: [$($IO1:ty),*] IO2: [$($IO2:ty),*] IO3: [$($IO3:ty),*]]) => { $( @@ -167,6 +205,18 @@ pub trait QspiExt { CONFIG: Into, PINS: PinsBank2; + fn bank_switch( + self, + _pins: PINS, + initial_bank: BankSelect, + config: CONFIG, + clocks: &CoreClocks, + prec: rec::Qspi, + ) -> Qspi + where + CONFIG: Into, + PINS: PinsBank1And2; + fn qspi_unchecked( self, config: CONFIG, @@ -179,6 +229,25 @@ pub trait QspiExt { } impl Qspi { + /// Switches between single-bank mode on Bank 1 and single-bank mode on Bank 2. + /// Note that it has no effect in dual-flash mode. + pub fn change_bank_unchecked( + &mut self, + bank: BankSelect, + ) -> Result<(), QspiError> { + // Ensure that the peripheral is no longer busy before changing FSEL and DFM bits + if self.is_busy().is_err() { + return Err(QspiError::Busy); + }; + + self.rb.cr.modify(|_, w| w.dfm().clear_bit()); + match bank { + BankSelect::One => self.rb.cr.modify(|_, w| w.fsel().clear_bit()), + BankSelect::Two => self.rb.cr.modify(|_, w| w.fsel().set_bit()), + } + Ok(()) + } + pub fn qspi_unchecked( regs: stm32::QUADSPI, config: CONFIG, @@ -275,6 +344,10 @@ impl Qspi { } impl QspiExt for stm32::QUADSPI { + /// Create and initialize a new QUADSPI peripheral that will use the single-bank mode on the Bank 1 of the flash memory. + /// + /// A list of pins `(sck, io0, io1, io2, io3)` for this QSPI peripheral should be passed as pins. + /// Even if the pins are not used, the function will consume them to avoid accessing to them manually. fn bank1( self, _pins: PINS, @@ -289,6 +362,10 @@ impl QspiExt for stm32::QUADSPI { Qspi::qspi_unchecked(self, config, Bank::One, clocks, prec) } + /// Create and initialize a new QUADSPI peripheral that will use the single-bank mode on the Bank 2 of the flash memory. + /// + /// A list of pins `(sck, io0, io1, io2, io3)` for this QSPI peripheral should be passed as pins. + /// Even if the pins are not used, the function will consume them to avoid accessing to them manually. fn bank2( self, _pins: PINS, @@ -303,6 +380,31 @@ impl QspiExt for stm32::QUADSPI { Qspi::qspi_unchecked(self, config, Bank::Two, clocks, prec) } + /// Create and initialize a new QUADSPI peripheral that can switch between both banks of the flash memory. + /// + /// The Bank to use at initialization is given by the `initial_bank` parameter. + /// A list of pins `(sck, bank1_io0, bank1_io1, bank1_io2, bank1_io3, bank2_io0, bank2_io1, bank2_io2, bank2_io3)` for this QSPI peripheral should be passed as pins. + /// Even if the pins are not used, the function will consume them to avoid accessing to them manually. + /// + /// Note that the peripheral will still be initialized in single-bank mode and the used bank have to be changed using the [`change_bank_unchecked`](../xspi/struct.Qspi.html#method.change_bank_unchecked) method. + fn bank_switch( + self, + _pins: PINS, + initial_bank: BankSelect, + config: CONFIG, + clocks: &CoreClocks, + prec: rec::Qspi, + ) -> Qspi + where + CONFIG: Into, + PINS: PinsBank1And2, + { + Qspi::qspi_unchecked(self, config, initial_bank.into(), clocks, prec) + } + + /// Create and initialize a new QUADSPI peripheral without consuming any pins. + /// + /// The given `bank` allow to chose between communication to just one of the two banks (single-bank mode) or to both at the same time (dual-flash mode). fn qspi_unchecked( self, config: CONFIG,