diff --git a/embassy-stm32/CHANGELOG.md b/embassy-stm32/CHANGELOG.md index a6ee5c4b81..2005128d51 100644 --- a/embassy-stm32/CHANGELOG.md +++ b/embassy-stm32/CHANGELOG.md @@ -20,7 +20,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - feat: Configurable gpio speed for QSPI - feat: derive Clone, Copy and defmt::Format for all *SPI-related configs - fix: handle address and data-length errors in OSPI -- feat: Allow OSPI DMA writes larger than 64kB using chunking +- feat: Allow OSPI/HSPI/XSPI DMA writes larger than 64kB using chunking - feat: More ADC enums for g0 PAC, API change for oversampling, allow separate sample times - feat: Add USB CRS sync support for STM32C071 - fix: RTC register definition for STM32L4P5 and L4Q5 as they use v3 register map. @@ -31,6 +31,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - feat: stm32/usart: add `eager_reads` option to control if buffered readers return as soon as possible or after more data is available ([#4668](https://github.com/embassy-rs/embassy/pull/4668)) - feat: stm32/usart: add `de_assertion_time` and `de_deassertion_time` config options - change: stm32/uart: BufferedUartRx now returns all available bytes from the internal buffer +- fix: Properly set the transfer size for OSPI/HSPI/XSPI transfers with word sizes other than 8 bits. ## 0.4.0 - 2025-08-26 diff --git a/embassy-stm32/src/hspi/mod.rs b/embassy-stm32/src/hspi/mod.rs index 95d9e50996..59dd7ca161 100644 --- a/embassy-stm32/src/hspi/mod.rs +++ b/embassy-stm32/src/hspi/mod.rs @@ -391,7 +391,7 @@ impl<'d, T: Instance, M: PeriMode> Hspi<'d, T, M> { while T::REGS.sr().read().busy() {} T::REGS.cr().modify(|w| { - w.set_fmode(0.into()); + w.set_fmode(FunctionalMode::IndirectWrite.into()); }); // Configure alternate bytes @@ -498,7 +498,8 @@ impl<'d, T: Instance, M: PeriMode> Hspi<'d, T, M> { w.set_dmaen(false); }); - self.configure_command(&transaction, Some(buf.len()))?; + let transfer_size_bytes = buf.len() * W::size().bytes(); + self.configure_command(&transaction, Some(transfer_size_bytes))?; let current_address = T::REGS.ar().read().address(); let current_instruction = T::REGS.ir().read().instruction(); @@ -537,7 +538,8 @@ impl<'d, T: Instance, M: PeriMode> Hspi<'d, T, M> { w.set_dmaen(false); }); - self.configure_command(&transaction, Some(buf.len()))?; + let transfer_size_bytes = buf.len() * W::size().bytes(); + self.configure_command(&transaction, Some(transfer_size_bytes))?; T::REGS .cr() @@ -767,7 +769,8 @@ impl<'d, T: Instance> Hspi<'d, T, Async> { // Wait for peripheral to be free while T::REGS.sr().read().busy() {} - self.configure_command(&transaction, Some(buf.len()))?; + let transfer_size_bytes = buf.len() * W::size().bytes(); + self.configure_command(&transaction, Some(transfer_size_bytes))?; let current_address = T::REGS.ar().read().address(); let current_instruction = T::REGS.ir().read().instruction(); @@ -782,16 +785,18 @@ impl<'d, T: Instance> Hspi<'d, T, Async> { T::REGS.ar().write(|v| v.set_address(current_address)); } - let transfer = unsafe { - self.dma - .as_mut() - .unwrap() - .read(T::REGS.dr().as_ptr() as *mut W, buf, Default::default()) - }; + for chunk in buf.chunks_mut(0xFFFF / W::size().bytes()) { + let transfer = unsafe { + self.dma + .as_mut() + .unwrap() + .read(T::REGS.dr().as_ptr() as *mut W, chunk, Default::default()) + }; - T::REGS.cr().modify(|w| w.set_dmaen(true)); + T::REGS.cr().modify(|w| w.set_dmaen(true)); - transfer.blocking_wait(); + transfer.blocking_wait(); + } finish_dma(T::REGS); @@ -807,21 +812,24 @@ impl<'d, T: Instance> Hspi<'d, T, Async> { // Wait for peripheral to be free while T::REGS.sr().read().busy() {} - self.configure_command(&transaction, Some(buf.len()))?; + let transfer_size_bytes = buf.len() * W::size().bytes(); + self.configure_command(&transaction, Some(transfer_size_bytes))?; T::REGS .cr() .modify(|v| v.set_fmode(FunctionalMode::IndirectWrite.into())); - let transfer = unsafe { - self.dma - .as_mut() - .unwrap() - .write(buf, T::REGS.dr().as_ptr() as *mut W, Default::default()) - }; + for chunk in buf.chunks(0xFFFF / W::size().bytes()) { + let transfer = unsafe { + self.dma + .as_mut() + .unwrap() + .write(chunk, T::REGS.dr().as_ptr() as *mut W, Default::default()) + }; - T::REGS.cr().modify(|w| w.set_dmaen(true)); + T::REGS.cr().modify(|w| w.set_dmaen(true)); - transfer.blocking_wait(); + transfer.blocking_wait(); + } finish_dma(T::REGS); @@ -837,7 +845,8 @@ impl<'d, T: Instance> Hspi<'d, T, Async> { // Wait for peripheral to be free while T::REGS.sr().read().busy() {} - self.configure_command(&transaction, Some(buf.len()))?; + let transfer_size_bytes = buf.len() * W::size().bytes(); + self.configure_command(&transaction, Some(transfer_size_bytes))?; let current_address = T::REGS.ar().read().address(); let current_instruction = T::REGS.ir().read().instruction(); @@ -852,16 +861,18 @@ impl<'d, T: Instance> Hspi<'d, T, Async> { T::REGS.ar().write(|v| v.set_address(current_address)); } - let transfer = unsafe { - self.dma - .as_mut() - .unwrap() - .read(T::REGS.dr().as_ptr() as *mut W, buf, Default::default()) - }; + for chunk in buf.chunks_mut(0xFFFF / W::size().bytes()) { + let transfer = unsafe { + self.dma + .as_mut() + .unwrap() + .read(T::REGS.dr().as_ptr() as *mut W, chunk, Default::default()) + }; - T::REGS.cr().modify(|w| w.set_dmaen(true)); + T::REGS.cr().modify(|w| w.set_dmaen(true)); - transfer.await; + transfer.await; + } finish_dma(T::REGS); @@ -877,21 +888,25 @@ impl<'d, T: Instance> Hspi<'d, T, Async> { // Wait for peripheral to be free while T::REGS.sr().read().busy() {} - self.configure_command(&transaction, Some(buf.len()))?; + let transfer_size_bytes = buf.len() * W::size().bytes(); + self.configure_command(&transaction, Some(transfer_size_bytes))?; T::REGS .cr() .modify(|v| v.set_fmode(FunctionalMode::IndirectWrite.into())); - let transfer = unsafe { - self.dma - .as_mut() - .unwrap() - .write(buf, T::REGS.dr().as_ptr() as *mut W, Default::default()) - }; + // TODO: implement this using a LinkedList DMA to offload the whole transfer off the CPU. + for chunk in buf.chunks(0xFFFF / W::size().bytes()) { + let transfer = unsafe { + self.dma + .as_mut() + .unwrap() + .write(chunk, T::REGS.dr().as_ptr() as *mut W, Default::default()) + }; - T::REGS.cr().modify(|w| w.set_dmaen(true)); + T::REGS.cr().modify(|w| w.set_dmaen(true)); - transfer.await; + transfer.await; + } finish_dma(T::REGS); diff --git a/embassy-stm32/src/ospi/mod.rs b/embassy-stm32/src/ospi/mod.rs index d93cecb69e..dbcf07469a 100644 --- a/embassy-stm32/src/ospi/mod.rs +++ b/embassy-stm32/src/ospi/mod.rs @@ -451,7 +451,7 @@ impl<'d, T: Instance, M: PeriMode> Ospi<'d, T, M> { } T::REGS.cr().modify(|w| { - w.set_fmode(0.into()); + w.set_fmode(vals::FunctionalMode::INDIRECT_WRITE); }); // Configure alternate bytes @@ -577,7 +577,8 @@ impl<'d, T: Instance, M: PeriMode> Ospi<'d, T, M> { w.set_dmaen(false); }); - self.configure_command(&transaction, Some(buf.len()))?; + let transfer_size_bytes = buf.len() * W::size().bytes(); + self.configure_command(&transaction, Some(transfer_size_bytes))?; let current_address = T::REGS.ar().read().address(); let current_instruction = T::REGS.ir().read().instruction(); @@ -616,7 +617,8 @@ impl<'d, T: Instance, M: PeriMode> Ospi<'d, T, M> { w.set_dmaen(false); }); - self.configure_command(&transaction, Some(buf.len()))?; + let transfer_size_bytes = buf.len() * W::size().bytes(); + self.configure_command(&transaction, Some(transfer_size_bytes))?; T::REGS .cr() @@ -1153,7 +1155,8 @@ impl<'d, T: Instance> Ospi<'d, T, Async> { // Wait for peripheral to be free while T::REGS.sr().read().busy() {} - self.configure_command(&transaction, Some(buf.len()))?; + let transfer_size_bytes = buf.len() * W::size().bytes(); + self.configure_command(&transaction, Some(transfer_size_bytes))?; let current_address = T::REGS.ar().read().address(); let current_instruction = T::REGS.ir().read().instruction(); @@ -1168,16 +1171,18 @@ impl<'d, T: Instance> Ospi<'d, T, Async> { T::REGS.ar().write(|v| v.set_address(current_address)); } - let transfer = unsafe { - self.dma - .as_mut() - .unwrap() - .read(T::REGS.dr().as_ptr() as *mut W, buf, Default::default()) - }; + for chunk in buf.chunks_mut(0xFFFF / W::size().bytes()) { + let transfer = unsafe { + self.dma + .as_mut() + .unwrap() + .read(T::REGS.dr().as_ptr() as *mut W, chunk, Default::default()) + }; - T::REGS.cr().modify(|w| w.set_dmaen(true)); + T::REGS.cr().modify(|w| w.set_dmaen(true)); - transfer.blocking_wait(); + transfer.blocking_wait(); + } finish_dma(T::REGS); @@ -1193,13 +1198,14 @@ impl<'d, T: Instance> Ospi<'d, T, Async> { // Wait for peripheral to be free while T::REGS.sr().read().busy() {} - self.configure_command(&transaction, Some(buf.len()))?; + let transfer_size_bytes = buf.len() * W::size().bytes(); + self.configure_command(&transaction, Some(transfer_size_bytes))?; T::REGS .cr() .modify(|v| v.set_fmode(vals::FunctionalMode::INDIRECT_WRITE)); // TODO: implement this using a LinkedList DMA to offload the whole transfer off the CPU. - for chunk in buf.chunks(0xFFFF) { + for chunk in buf.chunks(0xFFFF / W::size().bytes()) { let transfer = unsafe { self.dma .as_mut() @@ -1226,7 +1232,8 @@ impl<'d, T: Instance> Ospi<'d, T, Async> { // Wait for peripheral to be free while T::REGS.sr().read().busy() {} - self.configure_command(&transaction, Some(buf.len()))?; + let transfer_size_bytes = buf.len() * W::size().bytes(); + self.configure_command(&transaction, Some(transfer_size_bytes))?; let current_address = T::REGS.ar().read().address(); let current_instruction = T::REGS.ir().read().instruction(); @@ -1241,16 +1248,18 @@ impl<'d, T: Instance> Ospi<'d, T, Async> { T::REGS.ar().write(|v| v.set_address(current_address)); } - let transfer = unsafe { - self.dma - .as_mut() - .unwrap() - .read(T::REGS.dr().as_ptr() as *mut W, buf, Default::default()) - }; + for chunk in buf.chunks_mut(0xFFFF / W::size().bytes()) { + let transfer = unsafe { + self.dma + .as_mut() + .unwrap() + .read(T::REGS.dr().as_ptr() as *mut W, chunk, Default::default()) + }; - T::REGS.cr().modify(|w| w.set_dmaen(true)); + T::REGS.cr().modify(|w| w.set_dmaen(true)); - transfer.await; + transfer.await; + } finish_dma(T::REGS); @@ -1266,13 +1275,14 @@ impl<'d, T: Instance> Ospi<'d, T, Async> { // Wait for peripheral to be free while T::REGS.sr().read().busy() {} - self.configure_command(&transaction, Some(buf.len()))?; + let transfer_size_bytes = buf.len() * W::size().bytes(); + self.configure_command(&transaction, Some(transfer_size_bytes))?; T::REGS .cr() .modify(|v| v.set_fmode(vals::FunctionalMode::INDIRECT_WRITE)); // TODO: implement this using a LinkedList DMA to offload the whole transfer off the CPU. - for chunk in buf.chunks(0xFFFF) { + for chunk in buf.chunks(0xFFFF / W::size().bytes()) { let transfer = unsafe { self.dma .as_mut() diff --git a/embassy-stm32/src/xspi/mod.rs b/embassy-stm32/src/xspi/mod.rs index 901569f648..6f224ab99e 100644 --- a/embassy-stm32/src/xspi/mod.rs +++ b/embassy-stm32/src/xspi/mod.rs @@ -420,9 +420,9 @@ impl<'d, T: Instance, M: PeriMode> Xspi<'d, T, M> { return Err(XspiError::InvalidCommand); } - T::REGS.cr().modify(|w| { - w.set_fmode(0.into()); - }); + T::REGS + .cr() + .modify(|w| w.set_fmode(Fmode::from_bits(XspiMode::IndirectWrite.into()))); // Configure alternate bytes if let Some(ab) = command.alternate_bytes { @@ -538,8 +538,8 @@ impl<'d, T: Instance, M: PeriMode> Xspi<'d, T, M> { w.set_dmaen(false); }); - // self.configure_command(&transaction, Some(buf.len()))?; - self.configure_command(&transaction, Some(buf.len())).unwrap(); + let transfer_size_bytes = buf.len() * W::size().bytes(); + self.configure_command(&transaction, Some(transfer_size_bytes))?; let current_address = T::REGS.ar().read().address(); let current_instruction = T::REGS.ir().read().instruction(); @@ -578,7 +578,8 @@ impl<'d, T: Instance, M: PeriMode> Xspi<'d, T, M> { w.set_dmaen(false); }); - self.configure_command(&transaction, Some(buf.len()))?; + let transfer_size_bytes = buf.len() * W::size().bytes(); + self.configure_command(&transaction, Some(transfer_size_bytes))?; T::REGS .cr() @@ -1145,7 +1146,8 @@ impl<'d, T: Instance> Xspi<'d, T, Async> { // Wait for peripheral to be free while T::REGS.sr().read().busy() {} - self.configure_command(&transaction, Some(buf.len()))?; + let transfer_size_bytes = buf.len() * W::size().bytes(); + self.configure_command(&transaction, Some(transfer_size_bytes))?; let current_address = T::REGS.ar().read().address(); let current_instruction = T::REGS.ir().read().instruction(); @@ -1160,16 +1162,18 @@ impl<'d, T: Instance> Xspi<'d, T, Async> { T::REGS.ar().write(|v| v.set_address(current_address)); } - let transfer = unsafe { - self.dma - .as_mut() - .unwrap() - .read(T::REGS.dr().as_ptr() as *mut W, buf, Default::default()) - }; + for chunk in buf.chunks_mut(0xFFFF / W::size().bytes()) { + let transfer = unsafe { + self.dma + .as_mut() + .unwrap() + .read(T::REGS.dr().as_ptr() as *mut W, chunk, Default::default()) + }; - T::REGS.cr().modify(|w| w.set_dmaen(true)); + T::REGS.cr().modify(|w| w.set_dmaen(true)); - transfer.blocking_wait(); + transfer.blocking_wait(); + } finish_dma(T::REGS); @@ -1185,21 +1189,24 @@ impl<'d, T: Instance> Xspi<'d, T, Async> { // Wait for peripheral to be free while T::REGS.sr().read().busy() {} - self.configure_command(&transaction, Some(buf.len()))?; + let transfer_size_bytes = buf.len() * W::size().bytes(); + self.configure_command(&transaction, Some(transfer_size_bytes))?; T::REGS .cr() .modify(|v| v.set_fmode(Fmode::from_bits(XspiMode::IndirectWrite.into()))); - let transfer = unsafe { - self.dma - .as_mut() - .unwrap() - .write(buf, T::REGS.dr().as_ptr() as *mut W, Default::default()) - }; + for chunk in buf.chunks(0xFFFF / W::size().bytes()) { + let transfer = unsafe { + self.dma + .as_mut() + .unwrap() + .write(chunk, T::REGS.dr().as_ptr() as *mut W, Default::default()) + }; - T::REGS.cr().modify(|w| w.set_dmaen(true)); + T::REGS.cr().modify(|w| w.set_dmaen(true)); - transfer.blocking_wait(); + transfer.blocking_wait(); + } finish_dma(T::REGS); @@ -1215,7 +1222,8 @@ impl<'d, T: Instance> Xspi<'d, T, Async> { // Wait for peripheral to be free while T::REGS.sr().read().busy() {} - self.configure_command(&transaction, Some(buf.len()))?; + let transfer_size_bytes = buf.len() * W::size().bytes(); + self.configure_command(&transaction, Some(transfer_size_bytes))?; let current_address = T::REGS.ar().read().address(); let current_instruction = T::REGS.ir().read().instruction(); @@ -1230,16 +1238,18 @@ impl<'d, T: Instance> Xspi<'d, T, Async> { T::REGS.ar().write(|v| v.set_address(current_address)); } - let transfer = unsafe { - self.dma - .as_mut() - .unwrap() - .read(T::REGS.dr().as_ptr() as *mut W, buf, Default::default()) - }; + for chunk in buf.chunks_mut(0xFFFF / W::size().bytes()) { + let transfer = unsafe { + self.dma + .as_mut() + .unwrap() + .read(T::REGS.dr().as_ptr() as *mut W, chunk, Default::default()) + }; - T::REGS.cr().modify(|w| w.set_dmaen(true)); + T::REGS.cr().modify(|w| w.set_dmaen(true)); - transfer.await; + transfer.await; + } finish_dma(T::REGS); @@ -1255,21 +1265,25 @@ impl<'d, T: Instance> Xspi<'d, T, Async> { // Wait for peripheral to be free while T::REGS.sr().read().busy() {} - self.configure_command(&transaction, Some(buf.len()))?; + let transfer_size_bytes = buf.len() * W::size().bytes(); + self.configure_command(&transaction, Some(transfer_size_bytes))?; T::REGS .cr() .modify(|v| v.set_fmode(Fmode::from_bits(XspiMode::IndirectWrite.into()))); - let transfer = unsafe { - self.dma - .as_mut() - .unwrap() - .write(buf, T::REGS.dr().as_ptr() as *mut W, Default::default()) - }; + // TODO: implement this using a LinkedList DMA to offload the whole transfer off the CPU. + for chunk in buf.chunks(0xFFFF / W::size().bytes()) { + let transfer = unsafe { + self.dma + .as_mut() + .unwrap() + .write(chunk, T::REGS.dr().as_ptr() as *mut W, Default::default()) + }; - T::REGS.cr().modify(|w| w.set_dmaen(true)); + T::REGS.cr().modify(|w| w.set_dmaen(true)); - transfer.await; + transfer.await; + } finish_dma(T::REGS);