From 32f7e55f1e8714aa24dffd33b8d5791d0959940d Mon Sep 17 00:00:00 2001 From: Florian Jung Date: Sat, 15 May 2021 00:41:23 +0200 Subject: [PATCH 1/5] Add DMA RX and RX/TX support to SPI Implements #200 --- CHANGELOG.md | 4 +- src/dma.rs | 68 +++++++++++++++- src/spi.rs | 216 +++++++++++++++++++++++++++++++++++++++++++++++++-- 3 files changed, 278 insertions(+), 10 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4c38547a..4013636e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -14,7 +14,9 @@ and this project adheres to [Semantic Versioning](http://semver.org/). - Support for CAN peripherals with the `bxcan` crate - Add DAC, UART4, UART5 clock in RCC for the f103 high density line - `start_raw` function and `arr`, `bsc` getters for more fine grained - control over the timer. + control over the timer +- Added RxTxDma support support to the DMA infrastructure +- Added DMA receive support for `SPI` ### Fixed - Fix > 2 byte i2c reads diff --git a/src/dma.rs b/src/dma.rs index b999fc28..dd9e8579 100644 --- a/src/dma.rs +++ b/src/dma.rs @@ -128,7 +128,7 @@ macro_rules! dma { use crate::pac::{$DMAX, dma1}; - use crate::dma::{CircBuffer, DmaExt, Error, Event, Half, Transfer, W, RxDma, TxDma, TransferPayload}; + use crate::dma::{CircBuffer, DmaExt, Error, Event, Half, Transfer, W, RxDma, TxDma, RxTxDma, TransferPayload}; use crate::rcc::{AHB, Enable}; #[allow(clippy::manual_non_exhaustive)] @@ -371,6 +371,45 @@ macro_rules! dma { } } + impl Transfer> + where + RxTxDma: TransferPayload, + { + pub fn is_done(&self) -> bool { + !self.payload.rxchannel.in_progress() + } + + pub fn wait(mut self) -> (BUFFER, RxTxDma) { + while !self.is_done() {} + + atomic::compiler_fence(Ordering::Acquire); + + self.payload.stop(); + + // we need a read here to make the Acquire fence effective + // we do *not* need this if `dma.stop` does a RMW operation + unsafe { ptr::read_volatile(&0); } + + // we need a fence here for the same reason we need one in `Transfer.wait` + atomic::compiler_fence(Ordering::Acquire); + + // `Transfer` needs to have a `Drop` implementation, because we accept + // managed buffers that can free their memory on drop. Because of that + // we can't move out of the `Transfer`'s fields, so we use `ptr::read` + // and `mem::forget`. + // + // NOTE(unsafe) There is no panic branch between getting the resources + // and forgetting `self`. + unsafe { + let buffer = ptr::read(&self.buffer); + let payload = ptr::read(&self.payload); + mem::forget(self); + (buffer, payload) + } + } + } + + impl Transfer> where RxDma: TransferPayload, @@ -387,6 +426,23 @@ macro_rules! dma { &slice[..(capacity - pending)] } } + + impl Transfer> + where + RxTxDma: TransferPayload, + { + pub fn peek(&self) -> &[T] + where + RXBUFFER: AsRef<[T]>, + { + let pending = self.payload.rxchannel.get_ndtr() as usize; + + let slice = self.buffer.0.as_ref(); + let capacity = slice.len(); + + &slice[..(capacity - pending)] + } + } )+ impl DmaExt for $DMAX { @@ -532,3 +588,13 @@ where { fn write(self, buffer: B) -> Transfer; } + +/// Trait for DMA simultaneously reading and writing within one synchronous operation. Panics if both buffers are not of equal length. +pub trait ReadWriteDma: Transmit +where + RXB: StaticWriteBuffer, + TXB: StaticReadBuffer, + Self: core::marker::Sized + TransferPayload, +{ + fn read_write(self, rx_buffer: RXB, tx_buffer: TXB) -> Transfer; +} diff --git a/src/spi.rs b/src/spi.rs index e08cfbba..1f7f042b 100644 --- a/src/spi.rs +++ b/src/spi.rs @@ -42,10 +42,10 @@ use crate::pac::SPI3; use crate::pac::{SPI1, SPI2}; use crate::afio::MAPR; -use crate::dma::dma1::{C3, C5}; +use crate::dma::dma1; #[cfg(feature = "connectivity")] -use crate::dma::dma2::C2; -use crate::dma::{Transfer, TransferPayload, Transmit, TxDma, R}; +use crate::dma::dma2; +use crate::dma::{Receive, RxDma, RxTxDma, Transfer, TransferPayload, Transmit, TxDma, R, W}; use crate::gpio::gpioa::{PA5, PA6, PA7}; use crate::gpio::gpiob::{PB13, PB14, PB15, PB3, PB4, PB5}; #[cfg(feature = "connectivity")] @@ -55,7 +55,7 @@ use crate::rcc::{Clocks, Enable, GetBusFreq, Reset, APB1, APB2}; use crate::time::Hertz; use core::sync::atomic::{self, Ordering}; -use embedded_dma::StaticReadBuffer; +use embedded_dma::{StaticReadBuffer, StaticWriteBuffer}; /// SPI error #[derive(Debug)] @@ -551,19 +551,53 @@ pub struct SpiPayload { } pub type SpiTxDma = TxDma, CHANNEL>; +pub type SpiRxDma = RxDma, CHANNEL>; +pub type SpiRxTxDma = + RxTxDma, RXCHANNEL, TXCHANNEL>; macro_rules! spi_dma { - ($SPIi:ident, $TCi:ident) => { + ($SPIi:ident, $RCi:ty, $TCi:ty) => { impl Transmit for SpiTxDma<$SPIi, REMAP, PINS, $TCi> { type TxChannel = $TCi; type ReceivedWord = u8; } + impl Receive for SpiRxDma<$SPIi, REMAP, PINS, $RCi> { + type RxChannel = $RCi; + type TransmittedWord = u8; + } + + impl Transmit for SpiRxTxDma<$SPIi, REMAP, PINS, $RCi, $TCi> { + type TxChannel = $TCi; + type ReceivedWord = u8; + } + + impl Receive for SpiRxTxDma<$SPIi, REMAP, PINS, $RCi, $TCi> { + type RxChannel = $RCi; + type TransmittedWord = u8; + } + impl Spi<$SPIi, REMAP, PINS, u8> { pub fn with_tx_dma(self, channel: $TCi) -> SpiTxDma<$SPIi, REMAP, PINS, $TCi> { let payload = SpiPayload { spi: self }; SpiTxDma { payload, channel } } + pub fn with_rx_dma(self, channel: $RCi) -> SpiRxDma<$SPIi, REMAP, PINS, $RCi> { + let payload = SpiPayload { spi: self }; + SpiRxDma { payload, channel } + } + pub fn with_rx_tx_dma( + self, + rxchannel: $RCi, + txchannel: $TCi, + ) -> SpiRxTxDma<$SPIi, REMAP, PINS, $RCi, $TCi> { + let payload = SpiPayload { spi: self }; + SpiRxTxDma { + payload, + rxchannel, + txchannel, + } + } } impl TransferPayload for SpiTxDma<$SPIi, REMAP, PINS, $TCi> { @@ -585,6 +619,89 @@ macro_rules! spi_dma { } } + impl TransferPayload for SpiRxDma<$SPIi, REMAP, PINS, $RCi> { + fn start(&mut self) { + self.payload + .spi + .spi + .cr2 + .modify(|_, w| w.rxdmaen().set_bit()); + self.channel.start(); + } + fn stop(&mut self) { + self.payload + .spi + .spi + .cr2 + .modify(|_, w| w.rxdmaen().clear_bit()); + self.channel.stop(); + } + } + + impl TransferPayload for SpiRxTxDma<$SPIi, REMAP, PINS, $RCi, $TCi> { + fn start(&mut self) { + self.payload + .spi + .spi + .cr2 + .modify(|_, w| w.rxdmaen().set_bit().txdmaen().set_bit()); + self.rxchannel.start(); + self.txchannel.start(); + } + fn stop(&mut self) { + self.payload + .spi + .spi + .cr2 + .modify(|_, w| w.txdmaen().clear_bit().rxdmaen().clear_bit()); + self.txchannel.stop(); + self.rxchannel.stop(); + } + } + + impl crate::dma::ReadDma for SpiRxDma<$SPIi, REMAP, PIN, $RCi> + where + B: StaticWriteBuffer, + { + fn read(mut self, mut buffer: B) -> Transfer { + // NOTE(unsafe) We own the buffer now and we won't call other `&mut` on it + // until the end of the transfer. + let (ptr, len) = unsafe { buffer.static_write_buffer() }; + self.channel.set_peripheral_address( + unsafe { &(*$SPIi::ptr()).dr as *const _ as u32 }, + false, + ); + self.channel.set_memory_address(ptr as u32, true); + self.channel.set_transfer_length(len); + + atomic::compiler_fence(Ordering::Release); + self.channel.ch().cr.modify(|_, w| { + w + // memory to memory mode disabled + .mem2mem() + .clear_bit() + // medium channel priority level + .pl() + .medium() + // 8-bit memory size + .msize() + .bits8() + // 8-bit peripheral size + .psize() + .bits8() + // circular mode disabled + .circ() + .clear_bit() + // write to memory + .dir() + .clear_bit() + }); + self.start(); + + Transfer::w(buffer, self) + } + } + impl crate::dma::WriteDma for SpiTxDma<$SPIi, REMAP, PIN, $TCi> where B: StaticReadBuffer, @@ -627,10 +744,93 @@ macro_rules! spi_dma { Transfer::r(buffer, self) } } + + impl crate::dma::ReadWriteDma + for SpiRxTxDma<$SPIi, REMAP, PIN, $RCi, $TCi> + where + RXB: StaticWriteBuffer, + TXB: StaticReadBuffer, + { + fn read_write( + mut self, + mut rxbuffer: RXB, + txbuffer: TXB, + ) -> Transfer { + // NOTE(unsafe) We own the buffer now and we won't call other `&mut` on it + // until the end of the transfer. + let (rxptr, rxlen) = unsafe { rxbuffer.static_write_buffer() }; + let (txptr, txlen) = unsafe { txbuffer.static_read_buffer() }; + + if rxlen != txlen { + panic!("receive and send buffer lengths do not match!"); + } + + self.rxchannel.set_peripheral_address( + unsafe { &(*$SPIi::ptr()).dr as *const _ as u32 }, + false, + ); + self.rxchannel.set_memory_address(rxptr as u32, true); + self.rxchannel.set_transfer_length(rxlen); + + self.txchannel.set_peripheral_address( + unsafe { &(*$SPIi::ptr()).dr as *const _ as u32 }, + false, + ); + self.txchannel.set_memory_address(txptr as u32, true); + self.txchannel.set_transfer_length(txlen); + + atomic::compiler_fence(Ordering::Release); + self.rxchannel.ch().cr.modify(|_, w| { + w + // memory to memory mode disabled + .mem2mem() + .clear_bit() + // medium channel priority level + .pl() + .medium() + // 8-bit memory size + .msize() + .bits8() + // 8-bit peripheral size + .psize() + .bits8() + // circular mode disabled + .circ() + .clear_bit() + // write to memory + .dir() + .clear_bit() + }); + self.txchannel.ch().cr.modify(|_, w| { + w + // memory to memory mode disabled + .mem2mem() + .clear_bit() + // medium channel priority level + .pl() + .medium() + // 8-bit memory size + .msize() + .bits8() + // 8-bit peripheral size + .psize() + .bits8() + // circular mode disabled + .circ() + .clear_bit() + // read from memory + .dir() + .set_bit() + }); + self.start(); + + Transfer::w((rxbuffer, txbuffer), self) + } + } }; } -spi_dma!(SPI1, C3); -spi_dma!(SPI2, C5); +spi_dma!(SPI1, dma1::C2, dma1::C3); +spi_dma!(SPI2, dma1::C4, dma1::C5); #[cfg(feature = "connectivity")] -spi_dma!(SPI3, C2); +spi_dma!(SPI3, dma2::C1, dma2::C2); From 7138771b6bb7b658f91a47a1635d27cf9745bdd8 Mon Sep 17 00:00:00 2001 From: Florian Jung Date: Thu, 20 May 2021 23:57:11 +0200 Subject: [PATCH 2/5] Move SPI DMA enable to with_tx_dma and add release function As per https://github.com/stm32-rs/stm32f1xx-hal/issues/334#issuecomment-843724427 --- CHANGELOG.md | 2 ++ src/spi.rs | 65 +++++++++++++++++++++++++++++----------------------- 2 files changed, 38 insertions(+), 29 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4013636e..240a6dd6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -17,6 +17,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/). control over the timer - Added RxTxDma support support to the DMA infrastructure - Added DMA receive support for `SPI` +- Added `release` functions to SPI DMA ### Fixed - Fix > 2 byte i2c reads @@ -26,6 +27,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/). ### Changed - Use `cortex-m-rtic` instead of `cortex-m-rtfm` in the examples +- Enable SPI DMA in `with_tx_dma`, not in `SpiTxDma::start` ## [v0.7.0]- 2020-10-17 diff --git a/src/spi.rs b/src/spi.rs index 1f7f042b..c522b44b 100644 --- a/src/spi.rs +++ b/src/spi.rs @@ -579,10 +579,12 @@ macro_rules! spi_dma { impl Spi<$SPIi, REMAP, PINS, u8> { pub fn with_tx_dma(self, channel: $TCi) -> SpiTxDma<$SPIi, REMAP, PINS, $TCi> { + self.spi.cr2.modify(|_, w| w.txdmaen().set_bit()); let payload = SpiPayload { spi: self }; SpiTxDma { payload, channel } } pub fn with_rx_dma(self, channel: $RCi) -> SpiRxDma<$SPIi, REMAP, PINS, $RCi> { + self.spi.cr2.modify(|_, w| w.rxdmaen().set_bit()); let payload = SpiPayload { spi: self }; SpiRxDma { payload, channel } } @@ -591,6 +593,9 @@ macro_rules! spi_dma { rxchannel: $RCi, txchannel: $TCi, ) -> SpiRxTxDma<$SPIi, REMAP, PINS, $RCi, $TCi> { + self.spi + .cr2 + .modify(|_, w| w.rxdmaen().set_bit().txdmaen().set_bit()); let payload = SpiPayload { spi: self }; SpiRxTxDma { payload, @@ -600,60 +605,62 @@ macro_rules! spi_dma { } } - impl TransferPayload for SpiTxDma<$SPIi, REMAP, PINS, $TCi> { - fn start(&mut self) { - self.payload + impl SpiTxDma<$SPIi, REMAP, PINS, $TCi> { + pub fn release(self) -> (Spi<$SPIi, REMAP, PINS, u8>, $TCi) { + let SpiTxDma { payload, channel } = self; + payload.spi.spi.cr2.modify(|_, w| w.txdmaen().clear_bit()); + (payload.spi, channel) + } + } + + impl SpiRxDma<$SPIi, REMAP, PINS, $RCi> { + pub fn release(self) -> (Spi<$SPIi, REMAP, PINS, u8>, $RCi) { + let SpiRxDma { payload, channel } = self; + payload.spi.spi.cr2.modify(|_, w| w.rxdmaen().clear_bit()); + (payload.spi, channel) + } + } + + impl SpiRxTxDma<$SPIi, REMAP, PINS, $RCi, $TCi> { + pub fn release(self) -> (Spi<$SPIi, REMAP, PINS, u8>, $RCi, $TCi) { + let SpiRxTxDma { + payload, + rxchannel, + txchannel, + } = self; + payload .spi .spi .cr2 - .modify(|_, w| w.txdmaen().set_bit()); + .modify(|_, w| w.rxdmaen().clear_bit().txdmaen().clear_bit()); + (payload.spi, rxchannel, txchannel) + } + } + + impl TransferPayload for SpiTxDma<$SPIi, REMAP, PINS, $TCi> { + fn start(&mut self) { self.channel.start(); } fn stop(&mut self) { - self.payload - .spi - .spi - .cr2 - .modify(|_, w| w.txdmaen().clear_bit()); self.channel.stop(); } } impl TransferPayload for SpiRxDma<$SPIi, REMAP, PINS, $RCi> { fn start(&mut self) { - self.payload - .spi - .spi - .cr2 - .modify(|_, w| w.rxdmaen().set_bit()); self.channel.start(); } fn stop(&mut self) { - self.payload - .spi - .spi - .cr2 - .modify(|_, w| w.rxdmaen().clear_bit()); self.channel.stop(); } } impl TransferPayload for SpiRxTxDma<$SPIi, REMAP, PINS, $RCi, $TCi> { fn start(&mut self) { - self.payload - .spi - .spi - .cr2 - .modify(|_, w| w.rxdmaen().set_bit().txdmaen().set_bit()); self.rxchannel.start(); self.txchannel.start(); } fn stop(&mut self) { - self.payload - .spi - .spi - .cr2 - .modify(|_, w| w.txdmaen().clear_bit().rxdmaen().clear_bit()); self.txchannel.stop(); self.rxchannel.stop(); } From e6796e07056fa898cfc73f46d38363a794ced4cd Mon Sep 17 00:00:00 2001 From: Florian Jung Date: Sat, 22 May 2021 18:30:33 +0200 Subject: [PATCH 3/5] Add dma::ReadWriteDma to the prelude --- src/prelude.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/prelude.rs b/src/prelude.rs index ff349473..05c353e1 100644 --- a/src/prelude.rs +++ b/src/prelude.rs @@ -4,6 +4,7 @@ pub use crate::crc::CrcExt as _stm32_hal_crc_CrcExt; pub use crate::dma::CircReadDma as _stm32_hal_dma_CircReadDma; pub use crate::dma::DmaExt as _stm32_hal_dma_DmaExt; pub use crate::dma::ReadDma as _stm32_hal_dma_ReadDma; +pub use crate::dma::ReadWriteDma as _stm32_hal_dma_ReadWriteDma; pub use crate::dma::WriteDma as _stm32_hal_dma_WriteDma; pub use crate::flash::FlashExt as _stm32_hal_flash_FlashExt; pub use crate::gpio::GpioExt as _stm32_hal_gpio_GpioExt; From 866dafa461896c4f8d5312a344c18e933d82dcca Mon Sep 17 00:00:00 2001 From: Florian Jung Date: Mon, 24 May 2021 21:53:13 +0200 Subject: [PATCH 4/5] Remove SpiPayload wrapper --- src/spi.rs | 36 +++++++++++++++++------------------- 1 file changed, 17 insertions(+), 19 deletions(-) diff --git a/src/spi.rs b/src/spi.rs index c522b44b..ca0cdceb 100644 --- a/src/spi.rs +++ b/src/spi.rs @@ -546,14 +546,10 @@ where // DMA -pub struct SpiPayload { - spi: Spi, -} - -pub type SpiTxDma = TxDma, CHANNEL>; -pub type SpiRxDma = RxDma, CHANNEL>; +pub type SpiTxDma = TxDma, CHANNEL>; +pub type SpiRxDma = RxDma, CHANNEL>; pub type SpiRxTxDma = - RxTxDma, RXCHANNEL, TXCHANNEL>; + RxTxDma, RXCHANNEL, TXCHANNEL>; macro_rules! spi_dma { ($SPIi:ident, $RCi:ty, $TCi:ty) => { @@ -580,13 +576,17 @@ macro_rules! spi_dma { impl Spi<$SPIi, REMAP, PINS, u8> { pub fn with_tx_dma(self, channel: $TCi) -> SpiTxDma<$SPIi, REMAP, PINS, $TCi> { self.spi.cr2.modify(|_, w| w.txdmaen().set_bit()); - let payload = SpiPayload { spi: self }; - SpiTxDma { payload, channel } + SpiTxDma { + payload: self, + channel, + } } pub fn with_rx_dma(self, channel: $RCi) -> SpiRxDma<$SPIi, REMAP, PINS, $RCi> { self.spi.cr2.modify(|_, w| w.rxdmaen().set_bit()); - let payload = SpiPayload { spi: self }; - SpiRxDma { payload, channel } + SpiRxDma { + payload: self, + channel, + } } pub fn with_rx_tx_dma( self, @@ -596,9 +596,8 @@ macro_rules! spi_dma { self.spi .cr2 .modify(|_, w| w.rxdmaen().set_bit().txdmaen().set_bit()); - let payload = SpiPayload { spi: self }; SpiRxTxDma { - payload, + payload: self, rxchannel, txchannel, } @@ -608,16 +607,16 @@ macro_rules! spi_dma { impl SpiTxDma<$SPIi, REMAP, PINS, $TCi> { pub fn release(self) -> (Spi<$SPIi, REMAP, PINS, u8>, $TCi) { let SpiTxDma { payload, channel } = self; - payload.spi.spi.cr2.modify(|_, w| w.txdmaen().clear_bit()); - (payload.spi, channel) + payload.spi.cr2.modify(|_, w| w.txdmaen().clear_bit()); + (payload, channel) } } impl SpiRxDma<$SPIi, REMAP, PINS, $RCi> { pub fn release(self) -> (Spi<$SPIi, REMAP, PINS, u8>, $RCi) { let SpiRxDma { payload, channel } = self; - payload.spi.spi.cr2.modify(|_, w| w.rxdmaen().clear_bit()); - (payload.spi, channel) + payload.spi.cr2.modify(|_, w| w.rxdmaen().clear_bit()); + (payload, channel) } } @@ -629,11 +628,10 @@ macro_rules! spi_dma { txchannel, } = self; payload - .spi .spi .cr2 .modify(|_, w| w.rxdmaen().clear_bit().txdmaen().clear_bit()); - (payload.spi, rxchannel, txchannel) + (payload, rxchannel, txchannel) } } From eb83bd4a3ff733747ee14d5603d394511a629f87 Mon Sep 17 00:00:00 2001 From: Florian Jung Date: Tue, 25 May 2021 14:01:41 +0200 Subject: [PATCH 5/5] Add Spi1TxDma etc. type aliases --- src/spi.rs | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/src/spi.rs b/src/spi.rs index ca0cdceb..42c29348 100644 --- a/src/spi.rs +++ b/src/spi.rs @@ -552,7 +552,11 @@ pub type SpiRxTxDma = RxTxDma, RXCHANNEL, TXCHANNEL>; macro_rules! spi_dma { - ($SPIi:ident, $RCi:ty, $TCi:ty) => { + ($SPIi:ident, $RCi:ty, $TCi:ty, $rxdma:ident, $txdma:ident, $rxtxdma:ident) => { + pub type $rxdma = SpiRxDma<$SPIi, REMAP, PINS, $RCi>; + pub type $txdma = SpiTxDma<$SPIi, REMAP, PINS, $TCi>; + pub type $rxtxdma = SpiRxTxDma<$SPIi, REMAP, PINS, $RCi, $TCi>; + impl Transmit for SpiTxDma<$SPIi, REMAP, PINS, $TCi> { type TxChannel = $TCi; type ReceivedWord = u8; @@ -835,7 +839,7 @@ macro_rules! spi_dma { }; } -spi_dma!(SPI1, dma1::C2, dma1::C3); -spi_dma!(SPI2, dma1::C4, dma1::C5); +spi_dma!(SPI1, dma1::C2, dma1::C3, Spi1RxDma, Spi1TxDma, Spi1RxTxDma); +spi_dma!(SPI2, dma1::C4, dma1::C5, Spi2RxDma, Spi2TxDma, Spi2RxTxDma); #[cfg(feature = "connectivity")] -spi_dma!(SPI3, dma2::C1, dma2::C2); +spi_dma!(SPI3, dma2::C1, dma2::C2, Spi3RxDma, Spi3TxDma, Spi3RxTxDma);