From 525c7fe1eb77e3aa7440a14d6a6decfc0b0ca576 Mon Sep 17 00:00:00 2001 From: jake-taf Date: Sat, 23 Aug 2025 18:05:47 -0400 Subject: OSPI RAM Support - Make DQSE / SIOO configurable - Make write instruction configurable - Fix bug where the address DTR was using the config for the instruction DTR instead of its own - Configure DQS pin --- embassy-stm32/CHANGELOG.md | 1 + embassy-stm32/src/ospi/mod.rs | 109 ++++++++++++++++++++++++++++++++++++++---- 2 files changed, 101 insertions(+), 9 deletions(-) diff --git a/embassy-stm32/CHANGELOG.md b/embassy-stm32/CHANGELOG.md index 69f15013d..a473834dd 100644 --- a/embassy-stm32/CHANGELOG.md +++ b/embassy-stm32/CHANGELOG.md @@ -16,6 +16,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Fixed - STM32: Prevent dropped DacChannel from disabling Dac peripheral if another DacChannel is still in scope ([#4577](https://github.com/embassy-rs/embassy/pull/4577)) +- feat: Added support for more OctoSPI configurations (e.g. APS6408 RAM) ([#4581](https://github.com/embassy-rs/embassy/pull/4581)) ## 0.4.0 - 2025-08-26 diff --git a/embassy-stm32/src/ospi/mod.rs b/embassy-stm32/src/ospi/mod.rs index 74edfd5e4..8384f4fc4 100644 --- a/embassy-stm32/src/ospi/mod.rs +++ b/embassy-stm32/src/ospi/mod.rs @@ -118,6 +118,11 @@ pub struct TransferConfig { /// Number of dummy cycles (DCYC) pub dummy: DummyCycles, + + /// Data strobe (DQS) management enable + pub dqse: bool, + /// Send instruction only once (SIOO) mode enable + pub sioo: bool, } impl Default for TransferConfig { @@ -142,6 +147,9 @@ impl Default for TransferConfig { ddtr: false, dummy: DummyCycles::_0, + + dqse: false, + sioo: true, } } } @@ -192,26 +200,27 @@ impl<'d, T: Instance, M: PeriMode> Ospi<'d, T, M> { let reg = T::REGS; while reg.sr().read().busy() {} - reg.ccr().modify(|r| { - r.set_dqse(false); - r.set_sioo(true); - }); + if let Some(instruction) = write_config.instruction { + reg.wir().write(|r| { + r.set_instruction(instruction); + }); + } - // Set wrting configurations, there are separate registers for write configurations in memory mapped mode + // Set writing configurations, there are separate registers for write configurations in memory mapped mode reg.wccr().modify(|w| { w.set_imode(PhaseMode::from_bits(write_config.iwidth.into())); w.set_idtr(write_config.idtr); w.set_isize(SizeInBits::from_bits(write_config.isize.into())); w.set_admode(PhaseMode::from_bits(write_config.adwidth.into())); - w.set_addtr(write_config.idtr); + w.set_addtr(write_config.addtr); w.set_adsize(SizeInBits::from_bits(write_config.adsize.into())); w.set_dmode(PhaseMode::from_bits(write_config.dwidth.into())); w.set_ddtr(write_config.ddtr); w.set_abmode(PhaseMode::from_bits(write_config.abwidth.into())); - w.set_dqse(true); + w.set_dqse(write_config.dqse); }); reg.wtcr().modify(|w| w.set_dcyc(write_config.dummy.into())); @@ -465,18 +474,21 @@ impl<'d, T: Instance, M: PeriMode> Ospi<'d, T, M> { }) } - // Configure instruction/address/data modes + // Configure instruction/address/data/communication modes T::REGS.ccr().modify(|w| { w.set_imode(PhaseMode::from_bits(command.iwidth.into())); w.set_idtr(command.idtr); w.set_isize(SizeInBits::from_bits(command.isize.into())); w.set_admode(PhaseMode::from_bits(command.adwidth.into())); - w.set_addtr(command.idtr); + w.set_addtr(command.addtr); w.set_adsize(SizeInBits::from_bits(command.adsize.into())); w.set_dmode(PhaseMode::from_bits(command.dwidth.into())); w.set_ddtr(command.ddtr); + + w.set_dqse(command.dqse); + w.set_sioo(command.sioo); }); // Set informationrequired to initiate transaction @@ -854,6 +866,45 @@ impl<'d, T: Instance> Ospi<'d, T, Blocking> { false, ) } + + /// Create new blocking OSPI driver for octospi external chips with DQS support + pub fn new_blocking_octospi_with_dqs( + peri: Peri<'d, T>, + sck: Peri<'d, impl SckPin>, + d0: Peri<'d, impl D0Pin>, + d1: Peri<'d, impl D1Pin>, + d2: Peri<'d, impl D2Pin>, + d3: Peri<'d, impl D3Pin>, + d4: Peri<'d, impl D4Pin>, + d5: Peri<'d, impl D5Pin>, + d6: Peri<'d, impl D6Pin>, + d7: Peri<'d, impl D7Pin>, + nss: Peri<'d, impl NSSPin>, + dqs: Peri<'d, impl DQSPin>, + config: Config, + ) -> Self { + Self::new_inner( + peri, + new_pin!(d0, AfType::output(OutputType::PushPull, Speed::VeryHigh)), + new_pin!(d1, AfType::output(OutputType::PushPull, Speed::VeryHigh)), + new_pin!(d2, AfType::output(OutputType::PushPull, Speed::VeryHigh)), + new_pin!(d3, AfType::output(OutputType::PushPull, Speed::VeryHigh)), + new_pin!(d4, AfType::output(OutputType::PushPull, Speed::VeryHigh)), + new_pin!(d5, AfType::output(OutputType::PushPull, Speed::VeryHigh)), + new_pin!(d6, AfType::output(OutputType::PushPull, Speed::VeryHigh)), + new_pin!(d7, AfType::output(OutputType::PushPull, Speed::VeryHigh)), + new_pin!(sck, AfType::output(OutputType::PushPull, Speed::VeryHigh)), + new_pin!( + nss, + AfType::output_pull(OutputType::PushPull, Speed::VeryHigh, Pull::Up) + ), + new_pin!(dqs, AfType::output(OutputType::PushPull, Speed::VeryHigh)), + None, + config, + OspiWidth::OCTO, + false, + ) + } } impl<'d, T: Instance> Ospi<'d, T, Async> { @@ -1036,6 +1087,46 @@ impl<'d, T: Instance> Ospi<'d, T, Async> { ) } + /// Create new blocking OSPI driver for octospi external chips with DQS support + pub fn new_octospi_with_dqs( + peri: Peri<'d, T>, + sck: Peri<'d, impl SckPin>, + d0: Peri<'d, impl D0Pin>, + d1: Peri<'d, impl D1Pin>, + d2: Peri<'d, impl D2Pin>, + d3: Peri<'d, impl D3Pin>, + d4: Peri<'d, impl D4Pin>, + d5: Peri<'d, impl D5Pin>, + d6: Peri<'d, impl D6Pin>, + d7: Peri<'d, impl D7Pin>, + nss: Peri<'d, impl NSSPin>, + dqs: Peri<'d, impl DQSPin>, + dma: Peri<'d, impl OctoDma>, + config: Config, + ) -> Self { + Self::new_inner( + peri, + new_pin!(d0, AfType::output(OutputType::PushPull, Speed::VeryHigh)), + new_pin!(d1, AfType::output(OutputType::PushPull, Speed::VeryHigh)), + new_pin!(d2, AfType::output(OutputType::PushPull, Speed::VeryHigh)), + new_pin!(d3, AfType::output(OutputType::PushPull, Speed::VeryHigh)), + new_pin!(d4, AfType::output(OutputType::PushPull, Speed::VeryHigh)), + new_pin!(d5, AfType::output(OutputType::PushPull, Speed::VeryHigh)), + new_pin!(d6, AfType::output(OutputType::PushPull, Speed::VeryHigh)), + new_pin!(d7, AfType::output(OutputType::PushPull, Speed::VeryHigh)), + new_pin!(sck, AfType::output(OutputType::PushPull, Speed::VeryHigh)), + new_pin!( + nss, + AfType::output_pull(OutputType::PushPull, Speed::VeryHigh, Pull::Up) + ), + new_pin!(dqs, AfType::output(OutputType::PushPull, Speed::VeryHigh)), + new_dma!(dma), + config, + OspiWidth::OCTO, + false, + ) + } + /// Blocking read with DMA transfer pub fn blocking_read_dma(&mut self, buf: &mut [W], transaction: TransferConfig) -> Result<(), OspiError> { if buf.is_empty() { -- cgit