diff --git a/CHANGELOG.md b/CHANGELOG.md index 78fac08b..9c648908 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -14,7 +14,10 @@ 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` +- Added `release` functions to SPI DMA ### Fixed - Fix > 2 byte i2c reads @@ -27,6 +30,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/). - Use `cortex-m-rtic` instead of `cortex-m-rtfm` in the examples - Renamed `serial`'s `RxDma`/`TxDma`'s `split` method into `release` - Renamed I2C's `free` method into `release` +- Enable SPI DMA in `with_tx_dma`, not in `SpiTxDma::start` ## [v0.7.0]- 2020-10-17 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/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; diff --git a/src/spi.rs b/src/spi.rs index 45799511..51f5ce76 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)] @@ -528,45 +528,171 @@ where // DMA -pub struct SpiPayload { - spi: Spi, -} - -pub type SpiTxDma = TxDma, CHANNEL>; +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, $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; } + 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 } + self.spi.cr2.modify(|_, w| w.txdmaen().set_bit()); + 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()); + SpiRxDma { + payload: self, + channel, + } + } + pub fn with_rx_tx_dma( + self, + rxchannel: $RCi, + txchannel: $TCi, + ) -> SpiRxTxDma<$SPIi, REMAP, PINS, $RCi, $TCi> { + self.spi + .cr2 + .modify(|_, w| w.rxdmaen().set_bit().txdmaen().set_bit()); + SpiRxTxDma { + payload: self, + rxchannel, + txchannel, + } } } - impl TransferPayload for SpiTxDma<$SPIi, REMAP, PINS, $TCi> { - fn start(&mut self) { - self.payload - .spi + impl SpiTxDma<$SPIi, REMAP, PINS, $TCi> { + pub fn release(self) -> (Spi<$SPIi, REMAP, PINS, u8>, $TCi) { + let SpiTxDma { payload, channel } = self; + 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.cr2.modify(|_, w| w.rxdmaen().clear_bit()); + (payload, 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 .cr2 - .modify(|_, w| w.txdmaen().set_bit()); + .modify(|_, w| w.rxdmaen().clear_bit().txdmaen().clear_bit()); + (payload, 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.channel.start(); + } + fn stop(&mut self) { + self.channel.stop(); + } + } + + impl TransferPayload for SpiRxTxDma<$SPIi, REMAP, PINS, $RCi, $TCi> { + fn start(&mut self) { + self.rxchannel.start(); + self.txchannel.start(); + } + fn stop(&mut self) { + 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, @@ -609,10 +735,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, Spi1RxDma, Spi1TxDma, Spi1RxTxDma); +spi_dma!(SPI2, dma1::C4, dma1::C5, Spi2RxDma, Spi2TxDma, Spi2RxTxDma); #[cfg(feature = "connectivity")] -spi_dma!(SPI3, C2); +spi_dma!(SPI3, dma2::C1, dma2::C2, Spi3RxDma, Spi3TxDma, Spi3RxTxDma);