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
This commit is contained in:
jake-taf 2025-08-23 18:05:47 -04:00 committed by Dario Nieuwenhuis
parent 4ac4452c16
commit 525c7fe1eb
2 changed files with 101 additions and 9 deletions

View File

@ -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

View File

@ -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<T>>,
d0: Peri<'d, impl D0Pin<T>>,
d1: Peri<'d, impl D1Pin<T>>,
d2: Peri<'d, impl D2Pin<T>>,
d3: Peri<'d, impl D3Pin<T>>,
d4: Peri<'d, impl D4Pin<T>>,
d5: Peri<'d, impl D5Pin<T>>,
d6: Peri<'d, impl D6Pin<T>>,
d7: Peri<'d, impl D7Pin<T>>,
nss: Peri<'d, impl NSSPin<T>>,
dqs: Peri<'d, impl DQSPin<T>>,
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<T>>,
d0: Peri<'d, impl D0Pin<T>>,
d1: Peri<'d, impl D1Pin<T>>,
d2: Peri<'d, impl D2Pin<T>>,
d3: Peri<'d, impl D3Pin<T>>,
d4: Peri<'d, impl D4Pin<T>>,
d5: Peri<'d, impl D5Pin<T>>,
d6: Peri<'d, impl D6Pin<T>>,
d7: Peri<'d, impl D7Pin<T>>,
nss: Peri<'d, impl NSSPin<T>>,
dqs: Peri<'d, impl DQSPin<T>>,
dma: Peri<'d, impl OctoDma<T>>,
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<W: Word>(&mut self, buf: &mut [W], transaction: TransferConfig) -> Result<(), OspiError> {
if buf.is_empty() {