diff --git a/embassy-stm32/src/ospi/mod.rs b/embassy-stm32/src/ospi/mod.rs index 48b1ef789..8fb5f06e4 100644 --- a/embassy-stm32/src/ospi/mod.rs +++ b/embassy-stm32/src/ospi/mod.rs @@ -8,7 +8,7 @@ pub mod enums; use core::ops::Add; use core::ptr; -use embassy_embedded_hal::SetConfig; +use embassy_embedded_hal::{GetConfig, SetConfig}; use embassy_futures::join::join; use embassy_hal_internal::{into_ref, PeripheralRef}; // use embedded_hal_02::spi; @@ -23,6 +23,7 @@ use crate::rcc::RccPeripheral; use crate::{peripherals, Peripheral}; /// OPSI driver config. +#[derive(Clone, Copy)] pub struct Config { /// Fifo threshold used by the peripheral to generate the interrupt indicating data /// or space is available in the FIFO @@ -63,6 +64,7 @@ pub struct Config { /// Enables the refresh feature, chip select is released every refresh + 1 clock cycles pub refresh: u32, } + impl Default for Config { fn default() -> Self { Self { @@ -873,6 +875,83 @@ impl<'d, T: Instance, Dma> Ospi<'d, T, Dma> { Ok(()) } + + /// Set new bus configuration + pub fn set_config(&mut self, config: &Config) -> Result<(), ()> { + // Wait for busy flag to clear + while T::REGS.sr().read().busy() {} + + // Disable DMA channel while configuring the peripheral + T::REGS.cr().modify(|w| { + w.set_dmaen(false); + }); + + // Device configuration + T::REGS.dcr1().modify(|w| { + w.set_devsize(config.device_size.into()); + w.set_mtyp(config.memory_type); + w.set_csht(config.chip_select_high_time.into()); + w.set_dlybyp(config.delay_block_bypass); + w.set_frck(false); + w.set_ckmode(config.clock_mode); + }); + + T::REGS.dcr2().modify(|w| { + w.set_wrapsize(config.wrap_size.into()); + }); + + T::REGS.dcr3().modify(|w| { + w.set_csbound(config.chip_select_boundary); + w.set_maxtran(config.max_transfer); + }); + + T::REGS.dcr4().modify(|w| { + w.set_refresh(config.refresh); + }); + + T::REGS.cr().modify(|w| { + w.set_fthres(vals::Threshold(config.fifo_threshold.into())); + }); + + // Wait for busy flag to clear + while T::REGS.sr().read().busy() {} + + T::REGS.dcr2().modify(|w| { + w.set_prescaler(config.clock_prescaler); + }); + + T::REGS.cr().modify(|w| { + w.set_dmm(config.dual_quad); + }); + + T::REGS.tcr().modify(|w| { + w.set_sshift(match config.sample_shifting { + true => vals::SampleShift::HALFCYCLE, + false => vals::SampleShift::NONE, + }); + w.set_dhqc(config.delay_hold_quarter_cycle); + }); + + // Enable peripheral + T::REGS.cr().modify(|w| { + w.set_en(true); + }); + + // Free running clock needs to be set after peripheral enable + if config.free_running_clock { + T::REGS.dcr1().modify(|w| { + w.set_frck(config.free_running_clock); + }); + } + + self.config = *config; + Ok(()) + } + + /// Get current configuration + pub fn get_config(&self) -> Config { + self.config + } } impl<'d, T: Instance, Dma> Drop for Ospi<'d, T, Dma> { @@ -902,6 +981,17 @@ fn finish_dma(regs: Regs) { }); } +trait RegsExt { + fn dr_ptr<W>(&self) -> *mut W; +} + +impl RegsExt for Regs { + fn dr_ptr<W>(&self) -> *mut W { + let dr = self.dr(); + dr.as_ptr() as *mut W + } +} + pub(crate) mod sealed { use super::*; @@ -936,3 +1026,18 @@ foreach_peripheral!( impl Instance for peripherals::$inst {} }; ); + +impl<'d, T: Instance, Dma> SetConfig for Ospi<'d, T, Dma> { + type Config = Config; + type ConfigError = (); + fn set_config(&mut self, config: &Self::Config) -> Result<(), ()> { + self.set_config(config) + } +} + +impl<'d, T: Instance, Dma> GetConfig for Ospi<'d, T, Dma> { + type Config = Config; + fn get_config(&self) -> Self::Config { + self.get_config() + } +}