SpiDma<Async> now uses the SPI interrupt (instead of DMA) to wait f… (#3303)

* `SpiDma<Async>` now uses the SPI interrupt (instead of DMA) to wait for completion

* fmt and fix

* hail mary

* review comment
This commit is contained in:
Dominic Fischer 2025-03-28 15:44:29 +00:00 committed by GitHub
parent 4dcb1c7193
commit 6d84ee2acf
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 65 additions and 12 deletions

View File

@ -20,6 +20,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- `esp_hal::i2s::master::AnyI2s` has been moved to `esp_hal::i2s::AnyI2s` (#3226)
- `esp_hal::i2c::master::AnyI2c` has been moved to `esp_hal::i2c::AnyI2c` (#3226)
- `SpiDmaBus` no longer adjusts the DMA buffer length for each transfer (#3263)
- `SpiDma<Async>` now uses the SPI interrupt (instead of DMA) to wait for completion (#3303)
- `gpio::interconnect` types now have a lifetime associated with them (#3302)

View File

@ -1277,7 +1277,8 @@ mod dma {
impl<'d> SpiDma<'d, Blocking> {
/// Converts the SPI instance into async mode.
#[instability::unstable]
pub fn into_async(self) -> SpiDma<'d, Async> {
pub fn into_async(mut self) -> SpiDma<'d, Async> {
self.set_interrupt_handler(self.spi.handler());
SpiDma {
spi: self.spi,
channel: self.channel.into_async(),
@ -1354,12 +1355,42 @@ mod dma {
pub fn clear_interrupts(&mut self, interrupts: impl Into<EnumSet<SpiInterrupt>>) {
self.driver().clear_interrupts(interrupts.into());
}
#[cfg_attr(
not(multi_core),
doc = "Registers an interrupt handler for the peripheral."
)]
#[cfg_attr(
multi_core,
doc = "Registers an interrupt handler for the peripheral on the current core."
)]
#[doc = ""]
/// Note that this will replace any previously registered interrupt
/// handlers.
///
/// You can restore the default/unhandled interrupt handler by using
/// [crate::interrupt::DEFAULT_INTERRUPT_HANDLER]
///
/// # Panics
///
/// Panics if passed interrupt handler is invalid (e.g. has priority
/// `None`)
#[instability::unstable]
pub fn set_interrupt_handler(&mut self, handler: InterruptHandler) {
let interrupt = self.driver().info.interrupt;
for core in Cpu::other() {
crate::interrupt::disable(core, interrupt);
}
unsafe { crate::interrupt::bind_interrupt(interrupt, handler.handler()) };
unwrap!(crate::interrupt::enable(interrupt, handler.priority()));
}
}
impl<'d> SpiDma<'d, Async> {
/// Converts the SPI instance into async mode.
#[instability::unstable]
pub fn into_blocking(self) -> SpiDma<'d, Blocking> {
crate::interrupt::disable(Cpu::current(), self.driver().info.interrupt);
SpiDma {
spi: self.spi,
channel: self.channel.into_blocking(),
@ -1448,23 +1479,44 @@ mod dma {
}
async fn wait_for_idle_async(&mut self) {
// As a future enhancement, setup Spi Future in here as well.
if self.rx_transfer_in_progress {
_ = DmaRxFuture::new(&mut self.channel.rx).await;
self.rx_transfer_in_progress = false;
}
core::future::poll_fn(|cx| {
use core::task::Poll;
if self.is_done() {
Poll::Ready(())
} else {
cx.waker().wake_by_ref();
struct Fut(Driver);
impl Future for Fut {
type Output = ();
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
if self.0.interrupts().contains(SpiInterrupt::TransferDone) {
#[cfg(esp32)]
// Need to poll for done-ness even after interrupt fires.
if self.0.busy() {
cx.waker().wake_by_ref();
return Poll::Pending;
}
self.0.clear_interrupts(SpiInterrupt::TransferDone.into());
return Poll::Ready(());
}
self.0.state.waker.register(cx.waker());
self.0
.enable_listen(SpiInterrupt::TransferDone.into(), true);
Poll::Pending
}
})
.await;
}
impl Drop for Fut {
fn drop(&mut self) {
self.0
.enable_listen(SpiInterrupt::TransferDone.into(), false);
}
}
if !self.is_done() {
Fut(self.driver()).await;
}
if self.tx_transfer_in_progress {
// In case DMA TX buffer is bigger than what the SPI consumes, stop the DMA.
@ -3735,8 +3787,8 @@ fn handle_async<I: Instance>(instance: I) {
let driver = Driver { info, state };
if driver.interrupts().contains(SpiInterrupt::TransferDone) {
state.waker.wake();
driver.enable_listen(SpiInterrupt::TransferDone.into(), false);
state.waker.wake();
}
}