Skip to content

Commit 10db576

Browse files
committed
spi: add future for waiting for a transaction to complete after DMA transfer completes
This is intended to support async SPI DMA usage without SPI interrupts enabled.
1 parent 7861a33 commit 10db576

File tree

2 files changed

+101
-21
lines changed

2 files changed

+101
-21
lines changed

src/spi.rs

Lines changed: 23 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -503,6 +503,10 @@ impl<SPI: Instance, W: Word> Inner<SPI, W> {
503503
self.spi.cr1().modify(|_, w| w.spe().enabled());
504504
}
505505

506+
fn is_enabled(&self) -> bool {
507+
self.spi.cr1().read().spe().is_enabled()
508+
}
509+
506510
#[inline]
507511
pub fn start_transfer(&self) {
508512
self.spi.cr1().modify(|_, w| w.cstart().started());
@@ -857,17 +861,28 @@ impl<SPI: Instance, W: Word> Spi<SPI, W> {
857861
self.inner.start_transfer();
858862
}
859863

860-
/// Checks if the current transaction is complete and disables the
861-
/// peripheral if it is, returning true. If it isn't, returns false.
862-
fn end_transaction_if_done(&mut self) -> bool {
864+
fn is_transaction_complete(&mut self) -> bool {
863865
let sr = self.spi().sr().read();
864-
let is_complete = if self.inner.is_transmitter() {
866+
if self.inner.is_transmitter() {
865867
sr.eot().is_completed() && sr.txc().is_completed()
866868
} else {
867869
sr.eot().is_completed()
868-
};
870+
}
871+
}
872+
873+
fn disable(&mut self) {
874+
self.spi()
875+
.ifcr()
876+
.write(|w| w.txtfc().clear().eotc().clear().suspc().clear());
877+
878+
self.inner.disable();
879+
self.inner.reset_transfer_word_count();
880+
}
869881

870-
if !is_complete {
882+
/// Checks if the current transaction is complete and disables the
883+
/// peripheral if it is, returning true. If it isn't, returns false.
884+
fn end_transaction_if_done(&mut self) -> bool {
885+
if !self.is_transaction_complete() {
871886
return false;
872887
}
873888

@@ -878,12 +893,7 @@ impl<SPI: Instance, W: Word> Spi<SPI, W> {
878893
cortex_m::asm::nop()
879894
}
880895

881-
self.spi()
882-
.ifcr()
883-
.write(|w| w.txtfc().clear().eotc().clear().suspc().clear());
884-
885-
self.inner.disable();
886-
self.inner.reset_transfer_word_count();
896+
self.disable();
887897
true
888898
}
889899

@@ -896,8 +906,7 @@ impl<SPI: Instance, W: Word> Spi<SPI, W> {
896906
}
897907

898908
fn abort_transaction(&mut self) {
899-
self.inner.disable();
900-
self.inner.reset_transfer_word_count();
909+
self.disable();
901910
}
902911

903912
/// Deconstructs the SPI peripheral and returns the component parts.

src/spi/dma.rs

Lines changed: 78 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,18 @@
1-
use core::ops::{Deref, DerefMut};
1+
use core::{
2+
future::Future,
3+
ops::{Deref, DerefMut},
4+
pin::Pin,
5+
task::{Context, Poll},
6+
};
27

8+
use atomic_waker::AtomicWaker;
39
use embedded_hal::spi::ErrorType;
410
use embedded_hal_async::spi::SpiBus;
511

612
use crate::gpdma::{
7-
config::DmaConfig, periph::{DmaDuplex, DmaRx, DmaTx, Rx, RxAddr, Tx, TxAddr}, ChannelRegs, DmaChannel, DmaTransfer, Error as DmaError, Word as DmaWord
13+
config::DmaConfig,
14+
periph::{DmaDuplex, DmaRx, DmaTx, Rx, RxAddr, Tx, TxAddr},
15+
ChannelRegs, DmaChannel, DmaTransfer, Error as DmaError, Word as DmaWord,
816
};
917

1018
use super::{Error, Instance, Spi, Word};
@@ -59,7 +67,6 @@ where
5967
}
6068
}
6169

62-
6370
pub struct SpiDma<SPI, W: Word, MODE> {
6471
spi: Spi<SPI, W>,
6572
mode: MODE,
@@ -91,6 +98,22 @@ where
9198
self.inner.disable_dma();
9299
result
93100
}
101+
102+
async fn finish_transfer_async(
103+
&mut self,
104+
result: Result<(), DmaError>,
105+
) -> Result<(), Error> {
106+
let result = match result {
107+
Ok(_) => {
108+
SpiDmaFuture::new(self).await
109+
}
110+
Err(error) => {
111+
self.abort_transaction();
112+
Err(Error::DmaError(error))
113+
}
114+
};
115+
result
116+
}
94117
}
95118

96119
impl<SPI, W, CH> SpiDma<SPI, W, DmaTx<SPI, W, CH>>
@@ -110,7 +133,9 @@ where
110133
}
111134

112135
pub fn free(self) -> (Spi<SPI, W>, DmaChannel<CH>) {
113-
(self.spi, self.mode.into())
136+
let spi = self.spi;
137+
let channel = self.mode.into();
138+
(spi, channel)
114139
}
115140
}
116141

@@ -212,7 +237,7 @@ where
212237

213238
async fn read_dma(&mut self, words: &mut [W]) -> Result<(), Error> {
214239
let result = self.start_dma_read(words)?.to_async().await;
215-
self.finish_transfer(result)
240+
self.finish_transfer_async(result).await
216241
}
217242
}
218243

@@ -246,7 +271,7 @@ where
246271

247272
async fn write_dma(&mut self, words: &[W]) -> Result<(), Error> {
248273
let result = self.start_dma_write(words)?.to_async().await;
249-
self.finish_transfer(result)
274+
self.finish_transfer_async(result).await
250275
}
251276
}
252277

@@ -296,7 +321,7 @@ where
296321
let (tx, rx) = (tx.to_async(), rx.to_async());
297322
let result = tx.await.and(rx.await);
298323

299-
self.finish_transfer(result)
324+
self.finish_transfer_async(result).await
300325
}
301326

302327
async fn transfer_inplace_dma(
@@ -423,3 +448,49 @@ where
423448
Ok(())
424449
}
425450
}
451+
452+
struct SpiDmaFuture<'a, SPI: Instance, W: Word, MODE> {
453+
spi: &'a mut SpiDma<SPI, W, MODE>,
454+
waker: AtomicWaker,
455+
}
456+
457+
impl<'a, SPI: Instance, W: Word, MODE> SpiDmaFuture<'a, SPI, W, MODE> {
458+
fn new(spi: &'a mut SpiDma<SPI, W, MODE>) -> Self {
459+
Self {
460+
spi,
461+
waker: AtomicWaker::new(),
462+
}
463+
}
464+
}
465+
466+
impl<SPI: Instance, W: Word, MODE> Unpin for SpiDmaFuture<'_, SPI, W, MODE> {}
467+
468+
impl<SPI: Instance, W: Word, MODE> Drop for SpiDmaFuture<'_, SPI, W, MODE> {
469+
fn drop(&mut self) {
470+
if !self.spi.is_transaction_complete() {
471+
self.spi.abort_transaction();
472+
} else if self.spi.inner.is_enabled() {
473+
self.spi.disable();
474+
} else {
475+
// do nothing if the transaction is already complete
476+
}
477+
}
478+
}
479+
480+
impl<SPI: Instance, W: Word, MODE> Future for SpiDmaFuture<'_, SPI, W, MODE> {
481+
type Output = Result<(), Error>;
482+
483+
fn poll(
484+
mut self: Pin<&mut Self>,
485+
cx: &mut Context<'_>,
486+
) -> Poll<Self::Output> {
487+
self.waker.register(cx.waker());
488+
489+
if self.spi.is_transaction_complete() {
490+
self.spi.disable();
491+
Poll::Ready(Ok(()))
492+
} else {
493+
Poll::Pending
494+
}
495+
}
496+
}

0 commit comments

Comments
 (0)