Skip to content

Commit a137681

Browse files
committed
spi: enable SPI interrupts for end of transfer detection + cleanup
1 parent 01f1f41 commit a137681

File tree

2 files changed

+108
-27
lines changed

2 files changed

+108
-27
lines changed

src/spi.rs

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -520,6 +520,19 @@ impl<SPI: Instance, W: Word> Inner<SPI, W> {
520520
self.spi.cr2().modify(|_, w| w.tsize().set(0));
521521
}
522522

523+
fn enable_dma_transfer_interrupts(&self) {
524+
self.spi.ier().modify(|_, w| {
525+
w.eotie()
526+
.enabled()
527+
.udrie() // Underrun
528+
.enabled()
529+
.ovrie() // Overrun
530+
.enabled()
531+
.modfie() // Mode fault
532+
.enabled()
533+
});
534+
}
535+
523536
/// Disable SPI
524537
fn disable(&mut self) {
525538
self.spi.cr1().modify(|_, w| w.spe().disabled());
@@ -901,7 +914,6 @@ impl<SPI: Instance, W: Word> Spi<SPI, W> {
901914
/// This must always be called when all data has been sent to
902915
/// properly terminate the transaction and reset the SPI peripheral.
903916
fn end_transaction(&mut self) {
904-
// Result is only () or WouldBlock. Discard result.
905917
while !self.end_transaction_if_done() {}
906918
}
907919

src/spi/dma.rs

Lines changed: 95 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -10,10 +10,14 @@ use embedded_hal_async::spi::SpiBus;
1010
use futures_util::join;
1111
use futures_util::task::AtomicWaker;
1212

13-
use crate::gpdma::{
14-
config::DmaConfig,
15-
periph::{DmaDuplex, DmaRx, DmaTx, Rx, RxAddr, Tx, TxAddr},
16-
DmaChannel, DmaTransfer, Error as DmaError, Word as DmaWord,
13+
use crate::{
14+
gpdma::{
15+
config::DmaConfig,
16+
periph::{DmaDuplex, DmaRx, DmaTx, Rx, RxAddr, Tx, TxAddr},
17+
DmaChannel, DmaTransfer, Error as DmaError, Word as DmaWord,
18+
},
19+
interrupt,
20+
spi::CommunicationMode,
1721
};
1822

1923
use super::{Error, Instance, Spi, Word};
@@ -42,19 +46,33 @@ where
4246
where
4347
CH: DmaChannel,
4448
{
49+
assert!(self.inner.is_transmitter());
4550
SpiDma::new_simplex_transmitter(self, channel)
4651
}
4752

53+
/// Use DMA for receiving data only in simplex receiver mode, or in half duplex mode as a
54+
/// receiver
4855
pub fn use_dma_rx<CH>(
4956
self,
5057
channel: CH,
5158
) -> SpiDma<SPI, DmaRx<SPI, W, CH>, W>
5259
where
5360
CH: DmaChannel,
5461
{
62+
// Using DMA for receiving data requires that the SPI is configured as a simplex receiver or
63+
// in half duplex mode when receiving data only
64+
// otherwise no data will be received because no clock pulses are generated
65+
assert!(
66+
self.inner.communication_mode()
67+
== CommunicationMode::SimplexReceiver
68+
|| (self.inner.communication_mode()
69+
== CommunicationMode::HalfDuplex
70+
&& !self.inner.is_half_duplex_transmitter())
71+
);
5572
SpiDma::new_simplex_receiver(self, channel)
5673
}
5774

75+
/// Use DMA for full duplex transfers
5876
pub fn use_dma_duplex<TX, RX>(
5977
self,
6078
tx_channel: TX,
@@ -64,6 +82,9 @@ where
6482
TX: DmaChannel,
6583
RX: DmaChannel,
6684
{
85+
assert!(
86+
self.inner.communication_mode() == CommunicationMode::FullDuplex
87+
);
6788
SpiDma::new_duplex(self, tx_channel, rx_channel)
6889
}
6990
}
@@ -73,9 +94,10 @@ pub struct SpiDma<SPI, MODE, W: Word = u8> {
7394
mode: MODE,
7495
}
7596

97+
#[allow(private_bounds)]
7698
impl<SPI, MODE, W> SpiDma<SPI, MODE, W>
7799
where
78-
SPI: Instance,
100+
SPI: Instance + Waker,
79101
W: Word,
80102
{
81103
pub fn new(spi: Spi<SPI, W>, mode: MODE) -> Self {
@@ -199,9 +221,10 @@ where
199221
}
200222
}
201223

224+
#[allow(private_bounds)]
202225
impl<SPI, MODE, W> SpiDma<SPI, MODE, W>
203226
where
204-
SPI: Instance,
227+
SPI: Instance + Waker,
205228
W: Word + DmaWord,
206229
MODE: Rx<W>,
207230
{
@@ -233,9 +256,10 @@ where
233256
}
234257
}
235258

259+
#[allow(private_bounds)]
236260
impl<SPI, MODE, W> SpiDma<SPI, MODE, W>
237261
where
238-
SPI: Instance,
262+
SPI: Instance + Waker,
239263
W: Word + DmaWord,
240264
MODE: Tx<W>,
241265
{
@@ -266,9 +290,10 @@ where
266290
}
267291
}
268292

293+
#[allow(private_bounds)]
269294
impl<SPI, TX, RX, W> SpiDma<SPI, DmaDuplex<SPI, W, TX, RX>, W>
270295
where
271-
SPI: Instance,
296+
SPI: Instance + Waker,
272297
W: Word + DmaWord,
273298
TX: DmaChannel,
274299
RX: DmaChannel,
@@ -278,6 +303,12 @@ where
278303
read: &'a mut [W],
279304
write: &'a [W],
280305
) -> Result<(DmaTransfer<'a, TX>, DmaTransfer<'a, RX>), Error> {
306+
assert_eq!(
307+
read.len(),
308+
write.len(),
309+
"Read and write buffers must have the same length"
310+
);
311+
281312
let tx_config = DmaConfig::new().with_request(SPI::tx_dma_request());
282313
let rx_config = DmaConfig::new().with_request(SPI::rx_dma_request());
283314

@@ -332,7 +363,7 @@ where
332363

333364
impl<SPI, CH, W> SpiBus<W> for SpiDma<SPI, DmaTx<SPI, W, CH>, W>
334365
where
335-
SPI: Instance,
366+
SPI: Instance + Waker,
336367
W: Word + DmaWord,
337368
CH: DmaChannel,
338369
{
@@ -367,7 +398,7 @@ where
367398

368399
impl<SPI, CH, W> SpiBus<W> for SpiDma<SPI, DmaRx<SPI, W, CH>, W>
369400
where
370-
SPI: Instance,
401+
SPI: Instance + Waker,
371402
W: Word + DmaWord,
372403
CH: DmaChannel,
373404
{
@@ -402,7 +433,7 @@ where
402433

403434
impl<SPI, TX, RX, W> SpiBus<W> for SpiDma<SPI, DmaDuplex<SPI, W, TX, RX>, W>
404435
where
405-
SPI: Instance,
436+
SPI: Instance + Waker,
406437
W: Word + DmaWord,
407438
TX: DmaChannel,
408439
RX: DmaChannel,
@@ -438,46 +469,84 @@ where
438469

439470
struct SpiDmaFuture<'a, SPI: Instance, MODE, W: Word> {
440471
spi: &'a mut SpiDma<SPI, MODE, W>,
441-
waker: AtomicWaker,
442472
}
443473

444474
impl<'a, SPI: Instance, MODE, W: Word> SpiDmaFuture<'a, SPI, MODE, W> {
445475
fn new(spi: &'a mut SpiDma<SPI, MODE, W>) -> Self {
446-
Self {
447-
spi,
448-
waker: AtomicWaker::new(),
449-
}
476+
spi.inner.enable_dma_transfer_interrupts();
477+
Self { spi }
450478
}
451479
}
452480

453481
impl<SPI: Instance, MODE, W: Word> Unpin for SpiDmaFuture<'_, SPI, MODE, W> {}
454482

455483
impl<SPI: Instance, MODE, W: Word> Drop for SpiDmaFuture<'_, SPI, MODE, W> {
456484
fn drop(&mut self) {
457-
if !self.spi.is_transaction_complete() {
458-
self.spi.abort_transaction();
459-
} else if self.spi.inner.is_enabled() {
460-
self.spi.disable();
461-
} else {
462-
// do nothing if the transaction is already complete
463-
}
485+
self.spi.disable();
464486
}
465487
}
466488

467-
impl<SPI: Instance, MODE, W: Word> Future for SpiDmaFuture<'_, SPI, MODE, W> {
489+
impl<SPI: Instance + Waker, MODE, W: Word> Future
490+
for SpiDmaFuture<'_, SPI, MODE, W>
491+
{
468492
type Output = Result<(), Error>;
469493

470494
fn poll(
471495
mut self: Pin<&mut Self>,
472496
cx: &mut Context<'_>,
473497
) -> Poll<Self::Output> {
474-
self.waker.register(cx.waker());
498+
SPI::waker().register(cx.waker());
475499

476500
if self.spi.is_transaction_complete() {
477-
self.spi.disable();
478501
Poll::Ready(Ok(()))
479502
} else {
480503
Poll::Pending
481504
}
482505
}
483506
}
507+
508+
trait Waker {
509+
fn waker() -> &'static AtomicWaker;
510+
}
511+
512+
macro_rules! spi_dma_irq {
513+
($SPI:ident) => {
514+
paste::item! {
515+
static [<$SPI _WAKER>]: AtomicWaker = AtomicWaker::new();
516+
517+
impl Waker for $SPI {
518+
#[inline(always)]
519+
fn waker() -> &'static AtomicWaker {
520+
&[<$SPI _WAKER>]
521+
}
522+
}
523+
524+
#[interrupt]
525+
fn $SPI() {
526+
let spi = unsafe { &*$SPI::ptr() };
527+
unsafe { spi.ier().write_with_zero(|w| w); };
528+
$SPI::waker().wake();
529+
}
530+
}
531+
};
532+
}
533+
use crate::pac::{SPI1, SPI2, SPI3};
534+
535+
spi_dma_irq!(SPI1);
536+
spi_dma_irq!(SPI2);
537+
spi_dma_irq!(SPI3);
538+
539+
#[cfg(feature = "rm0481")]
540+
mod rm0481 {
541+
use super::*;
542+
use crate::pac::SPI4;
543+
spi_dma_irq!(SPI4);
544+
}
545+
546+
#[cfg(feature = "h56x_h573")]
547+
mod h56x_h573 {
548+
use super::*;
549+
use crate::pac::{SPI5, SPI6};
550+
spi_dma_irq!(SPI5);
551+
spi_dma_irq!(SPI6);
552+
}

0 commit comments

Comments
 (0)