#![macro_use]
pub mod enums;
use core::marker::PhantomData;
use embassy_hal_internal::PeripheralType;
use embassy_sync::waitqueue::AtomicWaker;
use enums::*;
use crate::dma::ChannelAndRequest;
use crate::gpio::{AfType, Flex, OutputType, Pull, Speed};
use crate::interrupt::typelevel::{Binding, Interrupt};
use crate::mode::{Async, Blocking, Mode as PeriMode};
use crate::pac::quadspi::Quadspi as Regs;
use crate::rcc::{self, RccPeripheral};
use crate::{Peri, interrupt};
#[derive(Clone, Copy)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct TransferConfig {
pub iwidth: QspiWidth,
pub awidth: QspiWidth,
pub dwidth: QspiWidth,
pub instruction: u8,
pub address: Option<u32>,
pub dummy: DummyCycles,
}
impl Default for TransferConfig {
fn default() -> Self {
Self {
iwidth: QspiWidth::NONE,
awidth: QspiWidth::NONE,
dwidth: QspiWidth::NONE,
instruction: 0,
address: None,
dummy: DummyCycles::_0,
}
}
}
#[derive(Clone, Copy)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
#[non_exhaustive]
pub struct Config {
pub memory_size: MemorySize,
pub address_size: AddressSize,
pub prescaler: u8,
pub fifo_threshold: FIFOThresholdLevel,
pub cs_high_time: ChipSelectHighTime,
pub sample_shifting: SampleShifting,
pub gpio_speed: Speed,
pub dual_flash: bool,
}
impl Default for Config {
fn default() -> Self {
Self {
memory_size: MemorySize::Other(0),
address_size: AddressSize::_24bit,
prescaler: 128,
fifo_threshold: FIFOThresholdLevel::_17Bytes,
cs_high_time: ChipSelectHighTime::_5Cycle,
sample_shifting: SampleShifting::None,
gpio_speed: Speed::VeryHigh,
dual_flash: false,
}
}
}
#[allow(dead_code)]
pub struct Qspi<'d, T: Instance, M: PeriMode> {
_peri: Peri<'d, T>,
sck: Option<Flex<'d>>,
bk1d0: Option<Flex<'d>>,
bk1d1: Option<Flex<'d>>,
bk1d2: Option<Flex<'d>>,
bk1d3: Option<Flex<'d>>,
bk2d0: Option<Flex<'d>>,
bk2d1: Option<Flex<'d>>,
bk2d2: Option<Flex<'d>>,
bk2d3: Option<Flex<'d>>,
bk1nss: Option<Flex<'d>>,
bk2nss: Option<Flex<'d>>,
dma: Option<ChannelAndRequest<'d>>,
_phantom: PhantomData<M>,
config: Config,
}
impl<'d, T: Instance, M: PeriMode> Qspi<'d, T, M> {
fn new_inner(
peri: Peri<'d, T>,
bk1d0: Option<Flex<'d>>,
bk1d1: Option<Flex<'d>>,
bk1d2: Option<Flex<'d>>,
bk1d3: Option<Flex<'d>>,
bk2d0: Option<Flex<'d>>,
bk2d1: Option<Flex<'d>>,
bk2d2: Option<Flex<'d>>,
bk2d3: Option<Flex<'d>>,
sck: Option<Flex<'d>>,
bk1nss: Option<Flex<'d>>,
bk2nss: Option<Flex<'d>>,
dma: Option<ChannelAndRequest<'d>>,
config: Config,
fsel: FlashSelection,
) -> Self {
rcc::enable_and_reset_without_stop::<T>();
while T::REGS.sr().read().busy() {}
#[cfg(stm32h7)]
{
use stm32_metapac::quadspi::regs::Cr;
T::REGS.cr().write_value(Cr(0));
while T::REGS.sr().read().busy() {}
T::REGS.cr().write_value(Cr(0xFF000001));
T::REGS.ccr().write(|w| w.set_frcm(true));
T::REGS.ccr().write(|w| w.set_frcm(true));
T::REGS.cr().write_value(Cr(0));
while T::REGS.sr().read().busy() {}
}
T::REGS.cr().modify(|w| {
w.set_en(true);
w.set_sshift(config.sample_shifting.into());
w.set_fthres(config.fifo_threshold.into());
w.set_prescaler(config.prescaler);
w.set_fsel(fsel.into());
w.set_dfm(config.dual_flash.into());
});
T::REGS.dcr().modify(|w| {
w.set_fsize(config.memory_size.into());
w.set_csht(config.cs_high_time.into());
w.set_ckmode(true);
});
Self {
_peri: peri,
sck,
bk1d0,
bk1d1,
bk1d2,
bk1d3,
bk2d0,
bk2d1,
bk2d2,
bk2d3,
bk1nss,
bk2nss,
dma,
_phantom: PhantomData,
config,
}
}
pub fn blocking_command(&mut self, transaction: TransferConfig) {
#[cfg(not(stm32h7))]
T::REGS.cr().modify(|v| v.set_dmaen(false));
self.setup_transaction(QspiMode::IndirectWrite, &transaction, None);
while !T::REGS.sr().read().tcf() {}
T::REGS.fcr().modify(|v| v.set_ctcf(true));
}
pub fn blocking_read(&mut self, buf: &mut [u8], transaction: TransferConfig) {
#[cfg(not(stm32h7))]
T::REGS.cr().modify(|v| v.set_dmaen(false));
self.setup_transaction(QspiMode::IndirectWrite, &transaction, Some(buf.len()));
let current_ar = T::REGS.ar().read().address();
T::REGS.ccr().modify(|v| {
v.set_fmode(QspiMode::IndirectRead.into());
});
T::REGS.ar().write(|v| {
v.set_address(current_ar);
});
for b in buf {
while !T::REGS.sr().read().tcf() && (T::REGS.sr().read().flevel() == 0) {}
*b = unsafe { (T::REGS.dr().as_ptr() as *mut u8).read_volatile() };
}
while !T::REGS.sr().read().tcf() {}
T::REGS.fcr().modify(|v| v.set_ctcf(true));
}
pub fn blocking_write(&mut self, buf: &[u8], transaction: TransferConfig) {
#[cfg(not(stm32h7))]
T::REGS.cr().modify(|v| v.set_dmaen(false));
self.setup_transaction(QspiMode::IndirectWrite, &transaction, Some(buf.len()));
T::REGS.ccr().modify(|v| {
v.set_fmode(QspiMode::IndirectWrite.into());
});
for &b in buf {
while !T::REGS.sr().read().ftf() {}
unsafe { (T::REGS.dr().as_ptr() as *mut u8).write_volatile(b) };
}
while !T::REGS.sr().read().tcf() {}
T::REGS.fcr().modify(|v| v.set_ctcf(true));
}
pub fn enable_memory_map(&mut self, transaction: &TransferConfig) {
T::REGS.fcr().modify(|v| {
v.set_csmf(true);
v.set_ctcf(true);
v.set_ctef(true);
v.set_ctof(true);
});
T::REGS.ccr().write(|v| {
v.set_fmode(QspiMode::MemoryMapped.into());
v.set_imode(transaction.iwidth.into());
v.set_instruction(transaction.instruction);
v.set_admode(transaction.awidth.into());
v.set_adsize(self.config.address_size.into());
v.set_dmode(transaction.dwidth.into());
v.set_abmode(QspiWidth::NONE.into());
v.set_dcyc(transaction.dummy.into());
});
}
pub fn blocking_auto_poll(
&mut self,
transaction: TransferConfig,
interval: u16,
mask: u32,
match_value: u32,
data_len: usize,
match_mode: MatchMode,
#[cfg(feature = "time")] timeout: embassy_time::Duration,
) -> Result<(), Error> {
self.setup_auto_poll(transaction, interval, mask, match_value, data_len, match_mode);
#[cfg(feature = "time")]
let deadline = embassy_time::Instant::now() + timeout;
while !T::REGS.sr().read().smf() {
#[cfg(feature = "time")]
if embassy_time::Instant::now() > deadline {
return Err(Error::AutoPollTimeout);
}
}
Ok(())
}
fn setup_auto_poll(
&mut self,
transaction: TransferConfig,
interval: u16,
mask: u32,
match_value: u32,
data_len: usize,
match_mode: MatchMode,
) {
assert!(data_len >= 1);
assert!(data_len <= 4);
while T::REGS.sr().read().busy() {}
T::REGS.fcr().modify(|v| {
v.set_csmf(true);
v.set_ctcf(true);
v.set_ctef(true);
v.set_ctof(true);
});
T::REGS.cr().modify(|m| {
m.set_pmm(match_mode.into());
m.set_apms(true);
});
T::REGS.psmkr().write(|w| w.set_mask(mask));
T::REGS.psmar().write(|w| w.set_match_(match_value));
T::REGS.pir().write(|w| w.set_interval(interval));
self.setup_transaction(QspiMode::AutoPolling, &transaction, Some(data_len));
}
fn setup_transaction(&mut self, fmode: QspiMode, transaction: &TransferConfig, data_len: Option<usize>) {
match (transaction.address, transaction.awidth) {
(Some(_), QspiWidth::NONE) => panic!("QSPI address can't be sent with an address width of NONE"),
(Some(_), _) => {}
(None, QspiWidth::NONE) => {}
(None, _) => panic!("QSPI address is not set, so the address width should be NONE"),
}
match (data_len, transaction.dwidth) {
(Some(0), _) => panic!("QSPI data must be at least one byte"),
(Some(_), QspiWidth::NONE) => panic!("QSPI data can't be sent with a data width of NONE"),
(Some(_), _) => {}
(None, QspiWidth::NONE) => {}
(None, _) => panic!("QSPI data is empty, so the data width should be NONE"),
}
T::REGS.fcr().modify(|v| {
v.set_csmf(true);
v.set_ctcf(true);
v.set_ctef(true);
v.set_ctof(true);
});
while T::REGS.sr().read().busy() {}
if let Some(len) = data_len {
T::REGS.dlr().write(|v| v.set_dl(len as u32 - 1));
}
T::REGS.ccr().write(|v| {
v.set_fmode(fmode.into());
v.set_imode(transaction.iwidth.into());
v.set_instruction(transaction.instruction);
v.set_admode(transaction.awidth.into());
v.set_adsize(self.config.address_size.into());
v.set_dmode(transaction.dwidth.into());
v.set_abmode(QspiWidth::NONE.into());
v.set_dcyc(transaction.dummy.into());
});
if let Some(addr) = transaction.address {
T::REGS.ar().write(|v| {
v.set_address(addr);
});
}
}
}
impl<'d, T: Instance> Qspi<'d, T, Blocking> {
pub fn new_blocking_bank1(
peri: Peri<'d, T>,
d0: Peri<'d, impl BK1D0Pin<T>>,
d1: Peri<'d, impl BK1D1Pin<T>>,
d2: Peri<'d, impl BK1D2Pin<T>>,
d3: Peri<'d, impl BK1D3Pin<T>>,
sck: Peri<'d, impl SckPin<T>>,
nss: Peri<'d, impl BK1NSSPin<T>>,
config: Config,
) -> Self {
Self::new_inner(
peri,
new_pin!(d0, AfType::output(OutputType::PushPull, config.gpio_speed)),
new_pin!(d1, AfType::output(OutputType::PushPull, config.gpio_speed)),
new_pin!(d2, AfType::output(OutputType::PushPull, config.gpio_speed)),
new_pin!(d3, AfType::output(OutputType::PushPull, config.gpio_speed)),
None,
None,
None,
None,
new_pin!(sck, AfType::output(OutputType::PushPull, config.gpio_speed)),
new_pin!(
nss,
AfType::output_pull(OutputType::PushPull, config.gpio_speed, Pull::Up)
),
None,
None,
config,
FlashSelection::Flash1,
)
}
pub fn new_blocking_bank2(
peri: Peri<'d, T>,
d0: Peri<'d, impl BK2D0Pin<T>>,
d1: Peri<'d, impl BK2D1Pin<T>>,
d2: Peri<'d, impl BK2D2Pin<T>>,
d3: Peri<'d, impl BK2D3Pin<T>>,
sck: Peri<'d, impl SckPin<T>>,
nss: Peri<'d, impl BK2NSSPin<T>>,
config: Config,
) -> Self {
Self::new_inner(
peri,
None,
None,
None,
None,
new_pin!(d0, AfType::output(OutputType::PushPull, config.gpio_speed)),
new_pin!(d1, AfType::output(OutputType::PushPull, config.gpio_speed)),
new_pin!(d2, AfType::output(OutputType::PushPull, config.gpio_speed)),
new_pin!(d3, AfType::output(OutputType::PushPull, config.gpio_speed)),
new_pin!(sck, AfType::output(OutputType::PushPull, config.gpio_speed)),
None,
new_pin!(
nss,
AfType::output_pull(OutputType::PushPull, config.gpio_speed, Pull::Up)
),
None,
config,
FlashSelection::Flash2,
)
}
pub fn new_blocking_dual_bank(
peri: Peri<'d, T>,
bk1d0: Peri<'d, impl BK1D0Pin<T>>,
bk1d1: Peri<'d, impl BK1D1Pin<T>>,
bk1d2: Peri<'d, impl BK1D2Pin<T>>,
bk1d3: Peri<'d, impl BK1D3Pin<T>>,
bk2d0: Peri<'d, impl BK2D0Pin<T>>,
bk2d1: Peri<'d, impl BK2D1Pin<T>>,
bk2d2: Peri<'d, impl BK2D2Pin<T>>,
bk2d3: Peri<'d, impl BK2D3Pin<T>>,
sck: Peri<'d, impl SckPin<T>>,
bk1nss: Peri<'d, impl BK1NSSPin<T>>,
bk2nss: Peri<'d, impl BK2NSSPin<T>>,
config: Config,
) -> Self {
Self::new_inner(
peri,
new_pin!(bk1d0, AfType::output(OutputType::PushPull, config.gpio_speed)),
new_pin!(bk1d1, AfType::output(OutputType::PushPull, config.gpio_speed)),
new_pin!(bk1d2, AfType::output(OutputType::PushPull, config.gpio_speed)),
new_pin!(bk1d3, AfType::output(OutputType::PushPull, config.gpio_speed)),
new_pin!(bk2d0, AfType::output(OutputType::PushPull, config.gpio_speed)),
new_pin!(bk2d1, AfType::output(OutputType::PushPull, config.gpio_speed)),
new_pin!(bk2d2, AfType::output(OutputType::PushPull, config.gpio_speed)),
new_pin!(bk2d3, AfType::output(OutputType::PushPull, config.gpio_speed)),
new_pin!(sck, AfType::output(OutputType::PushPull, config.gpio_speed)),
new_pin!(bk1nss, AfType::output(OutputType::PushPull, config.gpio_speed)),
new_pin!(bk2nss, AfType::output(OutputType::PushPull, config.gpio_speed)),
None,
config,
FlashSelection::Flash1, )
}
}
impl<'d, T: Instance> Qspi<'d, T, Async> {
pub fn new_bank1<D, I>(
peri: Peri<'d, T>,
d0: Peri<'d, impl BK1D0Pin<T>>,
d1: Peri<'d, impl BK1D1Pin<T>>,
d2: Peri<'d, impl BK1D2Pin<T>>,
d3: Peri<'d, impl BK1D3Pin<T>>,
sck: Peri<'d, impl SckPin<T>>,
nss: Peri<'d, impl BK1NSSPin<T>>,
dma: Peri<'d, D>,
_irq: I,
config: Config,
) -> Self
where
D: QuadDma<T>,
I: Binding<D::Interrupt, crate::dma::InterruptHandler<D>> + Binding<T::Interrupt, InterruptHandler<T>> + 'd,
{
T::Interrupt::unpend();
unsafe { T::Interrupt::enable() };
Self::new_inner(
peri,
new_pin!(d0, AfType::output(OutputType::PushPull, config.gpio_speed)),
new_pin!(d1, AfType::output(OutputType::PushPull, config.gpio_speed)),
new_pin!(d2, AfType::output(OutputType::PushPull, config.gpio_speed)),
new_pin!(d3, AfType::output(OutputType::PushPull, config.gpio_speed)),
None,
None,
None,
None,
new_pin!(sck, AfType::output(OutputType::PushPull, config.gpio_speed)),
new_pin!(
nss,
AfType::output_pull(OutputType::PushPull, config.gpio_speed, Pull::Up)
),
None,
new_dma!(dma, _irq),
config,
FlashSelection::Flash1,
)
}
pub fn new_bank2<D, I>(
peri: Peri<'d, T>,
d0: Peri<'d, impl BK2D0Pin<T>>,
d1: Peri<'d, impl BK2D1Pin<T>>,
d2: Peri<'d, impl BK2D2Pin<T>>,
d3: Peri<'d, impl BK2D3Pin<T>>,
sck: Peri<'d, impl SckPin<T>>,
nss: Peri<'d, impl BK2NSSPin<T>>,
dma: Peri<'d, D>,
_irq: I,
config: Config,
) -> Self
where
D: QuadDma<T>,
I: Binding<D::Interrupt, crate::dma::InterruptHandler<D>> + Binding<T::Interrupt, InterruptHandler<T>> + 'd,
{
T::Interrupt::unpend();
unsafe { T::Interrupt::enable() };
Self::new_inner(
peri,
None,
None,
None,
None,
new_pin!(d0, AfType::output(OutputType::PushPull, config.gpio_speed)),
new_pin!(d1, AfType::output(OutputType::PushPull, config.gpio_speed)),
new_pin!(d2, AfType::output(OutputType::PushPull, config.gpio_speed)),
new_pin!(d3, AfType::output(OutputType::PushPull, config.gpio_speed)),
new_pin!(sck, AfType::output(OutputType::PushPull, config.gpio_speed)),
None,
new_pin!(
nss,
AfType::output_pull(OutputType::PushPull, config.gpio_speed, Pull::Up)
),
new_dma!(dma, _irq),
config,
FlashSelection::Flash2,
)
}
pub fn blocking_read_dma(&mut self, buf: &mut [u8], transaction: TransferConfig) {
let transfer = self.start_read_transfer(transaction, buf);
transfer.blocking_wait();
}
pub async fn read_dma(&mut self, buf: &mut [u8], transaction: TransferConfig) {
let _scoped_wake_guard = T::RCC_INFO.wake_guard();
let transfer = self.start_read_transfer(transaction, buf);
transfer.await;
}
fn start_read_transfer<'a>(
&'a mut self,
transaction: TransferConfig,
buf: &'a mut [u8],
) -> crate::dma::Transfer<'a> {
self.setup_transaction(QspiMode::IndirectWrite, &transaction, Some(buf.len()));
T::REGS.ccr().modify(|v| {
v.set_fmode(QspiMode::IndirectRead.into());
});
let current_ar = T::REGS.ar().read().address();
T::REGS.ar().write(|v| {
v.set_address(current_ar);
});
let transfer = unsafe {
self.dma
.as_mut()
.unwrap()
.read(T::REGS.dr().as_ptr() as *mut u8, buf, Default::default())
};
#[cfg(not(stm32h7))]
T::REGS.cr().modify(|v| v.set_dmaen(true));
transfer
}
pub fn blocking_write_dma(&mut self, buf: &[u8], transaction: TransferConfig) {
let transfer = self.start_write_transfer(transaction, buf);
transfer.blocking_wait();
}
pub async fn write_dma(&mut self, buf: &[u8], transaction: TransferConfig) {
let _scoped_wake_guard = T::RCC_INFO.wake_guard();
let transfer = self.start_write_transfer(transaction, buf);
transfer.await;
}
fn start_write_transfer<'a>(&'a mut self, transaction: TransferConfig, buf: &'a [u8]) -> crate::dma::Transfer<'a> {
self.setup_transaction(QspiMode::IndirectWrite, &transaction, Some(buf.len()));
T::REGS.ccr().modify(|v| {
v.set_fmode(QspiMode::IndirectWrite.into());
});
let transfer = unsafe {
self.dma
.as_mut()
.unwrap()
.write(buf, T::REGS.dr().as_ptr() as *mut u8, Default::default())
};
#[cfg(not(stm32h7))]
T::REGS.cr().modify(|v| v.set_dmaen(true));
transfer
}
pub async fn auto_poll(
&mut self,
transaction: TransferConfig,
interval: u16,
mask: u32,
match_value: u32,
data_len: usize,
match_mode: MatchMode,
) {
T::REGS.cr().modify(|m| {
m.set_smie(true);
});
self.setup_auto_poll(transaction, interval, mask, match_value, data_len, match_mode);
AutoPollFuture {
_peri: self._peri.reborrow(),
}
.await
}
}
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum Error {
AutoPollTimeout,
}
trait SealedInstance {
const REGS: Regs;
}
#[allow(private_bounds)]
pub trait Instance: SealedInstance + PeripheralType + RccPeripheral {
type Interrupt: interrupt::typelevel::Interrupt;
}
pin_trait!(SckPin, Instance);
pin_trait!(BK1D0Pin, Instance);
pin_trait!(BK1D1Pin, Instance);
pin_trait!(BK1D2Pin, Instance);
pin_trait!(BK1D3Pin, Instance);
pin_trait!(BK1NSSPin, Instance);
pin_trait!(BK2D0Pin, Instance);
pin_trait!(BK2D1Pin, Instance);
pin_trait!(BK2D2Pin, Instance);
pin_trait!(BK2D3Pin, Instance);
pin_trait!(BK2NSSPin, Instance);
dma_trait!(QuadDma, Instance);
macro_rules! impl_peripheral {
($inst:ident, $irq:ident) => {
impl SealedInstance for crate::peripherals::$inst {
const REGS: Regs = crate::pac::$inst;
}
impl Instance for crate::peripherals::$inst {
type Interrupt = crate::interrupt::typelevel::$irq;
}
};
}
foreach_interrupt! {
($inst:ident, quadspi, $block:ident, GLOBAL, $irq:ident) => {
impl_peripheral!($inst, $irq);
};
}
#[must_use = "futures do nothing unless you `.await` or poll them"]
struct AutoPollFuture<'d, T: Instance> {
_peri: Peri<'d, T>,
}
impl<'d, T: Instance> Unpin for AutoPollFuture<'d, T> {}
impl<'d, T: Instance> Drop for AutoPollFuture<'d, T> {
fn drop(&mut self) {
T::REGS.cr().modify(|m| {
m.set_smie(false);
});
if T::REGS.ccr().read().fmode() == QspiMode::AutoPolling.into() && T::REGS.sr().read().busy() {
T::REGS.cr().modify(|m| m.set_abort(true));
while T::REGS.sr().read().busy() {}
}
}
}
impl<'d, T: Instance> Future for AutoPollFuture<'d, T> {
type Output = ();
fn poll(self: core::pin::Pin<&mut Self>, cx: &mut core::task::Context<'_>) -> core::task::Poll<Self::Output> {
AUTOPOLL_WAKER.register(cx.waker());
if T::REGS.sr().read().busy() {
core::task::Poll::Pending
} else {
core::task::Poll::Ready(())
}
}
}
static AUTOPOLL_WAKER: AtomicWaker = AtomicWaker::new();
pub enum MatchMode {
OR,
AND,
}
impl From<MatchMode> for bool {
fn from(mode: MatchMode) -> Self {
match mode {
MatchMode::OR => true,
MatchMode::AND => false,
}
}
}
pub struct InterruptHandler<T: Instance> {
_phantom: PhantomData<T>,
}
impl<T: Instance> crate::interrupt::typelevel::Handler<T::Interrupt> for InterruptHandler<T> {
unsafe fn on_interrupt() {
if T::REGS.sr().read().smf() {
T::REGS.fcr().modify(|m| m.set_csmf(true));
AUTOPOLL_WAKER.wake();
}
}
}