diff --git a/esp-hal/CHANGELOG.md b/esp-hal/CHANGELOG.md index 7af3845d6..23dd6c16a 100644 --- a/esp-hal/CHANGELOG.md +++ b/esp-hal/CHANGELOG.md @@ -11,6 +11,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Added +- Introduce traits for the DMA buffer objects (#1976) - Implement `embedded-hal` output pin traits for `DummyPin` (#2019) - Added `esp_hal::init` to simplify HAL initialisation (#1970, #1999) - Added GpioPin::degrade to create ErasePins easily. Same for AnyPin by accident. (#2075) diff --git a/esp-hal/src/dma/mod.rs b/esp-hal/src/dma/mod.rs index 5e5bce6d8..f354fb374 100644 --- a/esp-hal/src/dma/mod.rs +++ b/esp-hal/src/dma/mod.rs @@ -1187,10 +1187,10 @@ pub trait RxPrivate: crate::private::Sealed { chain: &DescriptorChain, ) -> Result<(), DmaError>; - unsafe fn prepare_transfer( + unsafe fn prepare_transfer( &mut self, peri: DmaPeripheral, - first_desc: *mut DmaDescriptor, + buffer: &mut BUF, ) -> Result<(), DmaError>; fn start_transfer(&mut self) -> Result<(), DmaError>; @@ -1371,18 +1371,20 @@ where .prepare_transfer_without_start(chain.first() as _, peri) } - unsafe fn prepare_transfer( + unsafe fn prepare_transfer( &mut self, peri: DmaPeripheral, - first_desc: *mut DmaDescriptor, + buffer: &mut BUF, ) -> Result<(), DmaError> { - // TODO: Figure out burst mode for DmaBuf. + let preparation = buffer.prepare(); + + // TODO: Get burst mode from DmaBuf. if self.burst_mode { return Err(DmaError::InvalidAlignment); } self.rx_impl - .prepare_transfer_without_start(first_desc, peri) + .prepare_transfer_without_start(preparation.start, peri) } fn start_transfer(&mut self) -> Result<(), DmaError> { @@ -1509,10 +1511,10 @@ pub trait TxPrivate: crate::private::Sealed { chain: &DescriptorChain, ) -> Result<(), DmaError>; - unsafe fn prepare_transfer( + unsafe fn prepare_transfer( &mut self, peri: DmaPeripheral, - desc: *mut DmaDescriptor, + buffer: &mut BUF, ) -> Result<(), DmaError>; fn start_transfer(&mut self) -> Result<(), DmaError>; @@ -1696,12 +1698,15 @@ where .prepare_transfer_without_start(chain.first() as _, peri) } - unsafe fn prepare_transfer( + unsafe fn prepare_transfer( &mut self, peri: DmaPeripheral, - desc: *mut DmaDescriptor, + buffer: &mut BUF, ) -> Result<(), DmaError> { - self.tx_impl.prepare_transfer_without_start(desc, peri) + let preparation = buffer.prepare(); + + self.tx_impl + .prepare_transfer_without_start(preparation.start, peri) } fn start_transfer(&mut self) -> Result<(), DmaError> { @@ -1934,6 +1939,49 @@ where } } +/// Holds all the information needed to configure a DMA channel for a transfer. +pub struct Preparation { + start: *mut DmaDescriptor, + // burst_mode, alignment, check_owner, etc. +} + +/// [DmaTxBuffer] is a DMA descriptor + memory combo that can be used for +/// transmitting data from a DMA channel to a peripheral's FIFO. +pub trait DmaTxBuffer { + /// Prepares the buffer for an imminent transfer and returns + /// information required to use this buffer. + /// + /// Note: This operation is idempotent. + fn prepare(&mut self) -> Preparation; + + /// Returns the maximum number of bytes that would be transmitted by this + /// buffer. + /// + /// This is a convenience hint for SPI. Most peripherals don't care how long + /// the transfer is. + fn length(&self) -> usize; +} + +/// [DmaRxBuffer] is a DMA descriptor + memory combo that can be used for +/// receiving data from a peripheral's FIFO to a DMA channel. +/// +/// Note: Implementations of this trait may only support having a single EOF bit +/// which resides in the last descriptor. There will be a separate trait in +/// future to support multiple EOFs. +pub trait DmaRxBuffer { + /// Prepares the buffer for an imminent transfer and returns + /// information required to use this buffer. + /// + /// Note: This operation is idempotent. + fn prepare(&mut self) -> Preparation; + + /// Returns the maximum number of bytes that can be received by this buffer. + /// + /// This is a convenience hint for SPI. Most peripherals don't care how long + /// the transfer is. + fn length(&self) -> usize; +} + /// Error returned from Dma[Rx|Tx|RxTx]Buf operations. #[derive(Debug)] pub enum DmaBufError { @@ -2072,9 +2120,26 @@ impl DmaTxBuf { pub fn as_slice(&self) -> &[u8] { self.buffer } +} - pub(crate) fn first(&self) -> *mut DmaDescriptor { - self.descriptors.as_ptr() as _ +impl DmaTxBuffer for DmaTxBuf { + fn prepare(&mut self) -> Preparation { + for desc in self.descriptors.iter_mut() { + // Give ownership to the DMA + desc.set_owner(Owner::Dma); + + if desc.next.is_null() { + break; + } + } + + Preparation { + start: self.descriptors.as_mut_ptr(), + } + } + + fn length(&self) -> usize { + self.len() } } @@ -2283,9 +2348,34 @@ impl DmaRxBuf { Some(chunk) }) } +} - pub(crate) fn first(&self) -> *mut DmaDescriptor { - self.descriptors.as_ptr() as _ +impl DmaRxBuffer for DmaRxBuf { + fn prepare(&mut self) -> Preparation { + for desc in self.descriptors.iter_mut() { + // Give ownership to the DMA + desc.set_owner(Owner::Dma); + + // Clear this to allow hardware to set it when the peripheral returns an EOF + // bit. + desc.set_suc_eof(false); + + // Clear this to allow hardware to set it when it's + // done receiving data for this descriptor. + desc.set_length(0); + + if desc.next.is_null() { + break; + } + } + + Preparation { + start: self.descriptors.as_mut_ptr(), + } + } + + fn length(&self) -> usize { + self.len() } } @@ -2370,13 +2460,26 @@ impl DmaRxTxBuf { self.buffer.len() } + /// Return the number of bytes that would be transmitted by this buf. + #[allow(clippy::len_without_is_empty)] + pub fn len(&self) -> usize { + let mut result = 0; + for desc in self.tx_descriptors.iter() { + result += desc.len(); + if desc.next.is_null() { + break; + } + } + result + } + /// Returns the entire buf as a slice than can be read. pub fn as_slice(&self) -> &[u8] { self.buffer } /// Returns the entire buf as a slice than can be written. - pub fn as_slice_mut(&mut self) -> &mut [u8] { + pub fn as_mut_slice(&mut self) -> &mut [u8] { &mut self.buffer[..] } @@ -2447,6 +2550,56 @@ impl DmaRxTxBuf { } } +impl DmaTxBuffer for DmaRxTxBuf { + fn prepare(&mut self) -> Preparation { + for desc in self.tx_descriptors.iter_mut() { + // Give ownership to the DMA + desc.set_owner(Owner::Dma); + + if desc.next.is_null() { + break; + } + } + + Preparation { + start: self.tx_descriptors.as_mut_ptr(), + } + } + + fn length(&self) -> usize { + self.len() + } +} + +impl DmaRxBuffer for DmaRxTxBuf { + fn prepare(&mut self) -> Preparation { + for desc in self.rx_descriptors.iter_mut() { + // Give ownership to the DMA + desc.set_owner(Owner::Dma); + + // Clear this to allow hardware to set it when the peripheral returns an EOF + // bit. + desc.set_suc_eof(false); + + // Clear this to allow hardware to set it when it's + // done receiving data for this descriptor. + desc.set_length(0); + + if desc.next.is_null() { + break; + } + } + + Preparation { + start: self.rx_descriptors.as_mut_ptr(), + } + } + + fn length(&self) -> usize { + self.len() + } +} + pub(crate) mod dma_private { use super::*; diff --git a/esp-hal/src/spi/master.rs b/esp-hal/src/spi/master.rs index fcdeaeebb..6943ddecc 100644 --- a/esp-hal/src/spi/master.rs +++ b/esp-hal/src/spi/master.rs @@ -81,7 +81,7 @@ use super::{ }; use crate::{ clock::Clocks, - dma::{DmaDescriptor, DmaPeripheral, Rx, Tx}, + dma::{DmaPeripheral, DmaRxBuffer, DmaTxBuffer, Rx, Tx}, gpio::{InputPin, InputSignal, OutputPin, OutputSignal}, interrupt::InterruptHandler, peripheral::{Peripheral, PeripheralRef}, @@ -947,7 +947,9 @@ mod dma { Channel, DmaChannel, DmaRxBuf, + DmaRxBuffer, DmaTxBuf, + DmaTxBuffer, RxPrivate, Spi2Peripheral, SpiPeripheral, @@ -1266,23 +1268,18 @@ mod dma { /// bytes. #[allow(clippy::type_complexity)] #[cfg_attr(feature = "place-spi-driver-in-ram", ram)] - pub fn dma_write( + pub fn dma_write( mut self, - buffer: DmaTxBuf, - ) -> Result, (Error, Self, DmaTxBuf)> - { - let bytes_to_write = buffer.len(); + mut buffer: TX, + ) -> Result, (Error, Self, TX)> { + let bytes_to_write = buffer.length(); if bytes_to_write > MAX_DMA_SIZE { return Err((Error::MaxDmaTransferSizeExceeded, self, buffer)); } let result = unsafe { - self.spi.start_write_bytes_dma( - buffer.first(), - bytes_to_write, - &mut self.channel.tx, - true, - ) + self.spi + .start_write_bytes_dma(&mut buffer, &mut self.channel.tx, true) }; if let Err(e) = result { return Err((e, self, buffer)); @@ -1298,23 +1295,18 @@ mod dma { /// received is 32736 bytes. #[allow(clippy::type_complexity)] #[cfg_attr(feature = "place-spi-driver-in-ram", ram)] - pub fn dma_read( + pub fn dma_read( mut self, - buffer: DmaRxBuf, - ) -> Result, (Error, Self, DmaRxBuf)> - { - let bytes_to_read = buffer.len(); + mut buffer: RX, + ) -> Result, (Error, Self, RX)> { + let bytes_to_read = buffer.length(); if bytes_to_read > MAX_DMA_SIZE { return Err((Error::MaxDmaTransferSizeExceeded, self, buffer)); } let result = unsafe { - self.spi.start_read_bytes_dma( - buffer.first(), - bytes_to_read, - &mut self.channel.rx, - true, - ) + self.spi + .start_read_bytes_dma(&mut buffer, &mut self.channel.rx, true) }; if let Err(e) = result { return Err((e, self, buffer)); @@ -1329,16 +1321,14 @@ mod dma { /// the SPI instance. The maximum amount of data to be /// sent/received is 32736 bytes. #[allow(clippy::type_complexity)] - pub fn dma_transfer( + pub fn dma_transfer( mut self, - rx_buffer: DmaRxBuf, - tx_buffer: DmaTxBuf, - ) -> Result< - SpiDmaTransfer<'d, T, C, FullDuplexMode, M, (DmaRxBuf, DmaTxBuf)>, - (Error, Self, DmaRxBuf, DmaTxBuf), - > { - let bytes_to_read = rx_buffer.len(); - let bytes_to_write = tx_buffer.len(); + mut rx_buffer: RX, + mut tx_buffer: TX, + ) -> Result, (Error, Self, RX, TX)> + { + let bytes_to_read = rx_buffer.length(); + let bytes_to_write = tx_buffer.length(); if bytes_to_write > MAX_DMA_SIZE || bytes_to_read > MAX_DMA_SIZE { return Err(( @@ -1351,10 +1341,8 @@ mod dma { let result = unsafe { self.spi.start_transfer_dma( - rx_buffer.first(), - tx_buffer.first(), - bytes_to_read, - bytes_to_write, + &mut rx_buffer, + &mut tx_buffer, &mut self.channel.rx, &mut self.channel.tx, ) @@ -1382,16 +1370,15 @@ mod dma { /// Perform a half-duplex read operation using DMA. #[allow(clippy::type_complexity)] #[cfg_attr(feature = "place-spi-driver-in-ram", ram)] - pub fn read( + pub fn read( mut self, data_mode: SpiDataMode, cmd: Command, address: Address, dummy: u8, - buffer: DmaRxBuf, - ) -> Result, (Error, Self, DmaRxBuf)> - { - let bytes_to_read = buffer.len(); + mut buffer: RX, + ) -> Result, (Error, Self, RX)> { + let bytes_to_read = buffer.length(); if bytes_to_read > MAX_DMA_SIZE { return Err((Error::MaxDmaTransferSizeExceeded, self, buffer)); } @@ -1447,12 +1434,8 @@ mod dma { } let result = unsafe { - self.spi.start_read_bytes_dma( - buffer.first(), - bytes_to_read, - &mut self.channel.rx, - false, - ) + self.spi + .start_read_bytes_dma(&mut buffer, &mut self.channel.rx, false) }; if let Err(e) = result { return Err((e, self, buffer)); @@ -1464,16 +1447,15 @@ mod dma { /// Perform a half-duplex write operation using DMA. #[allow(clippy::type_complexity)] #[cfg_attr(feature = "place-spi-driver-in-ram", ram)] - pub fn write( + pub fn write( mut self, data_mode: SpiDataMode, cmd: Command, address: Address, dummy: u8, - buffer: DmaTxBuf, - ) -> Result, (Error, Self, DmaTxBuf)> - { - let bytes_to_write = buffer.len(); + mut buffer: TX, + ) -> Result, (Error, Self, TX)> { + let bytes_to_write = buffer.length(); if bytes_to_write > MAX_DMA_SIZE { return Err((Error::MaxDmaTransferSizeExceeded, self, buffer)); } @@ -1529,12 +1511,8 @@ mod dma { } let result = unsafe { - self.spi.start_write_bytes_dma( - buffer.first(), - bytes_to_write, - &mut self.channel.tx, - false, - ) + self.spi + .start_write_bytes_dma(&mut buffer, &mut self.channel.tx, false) }; if let Err(e) = result { return Err((e, self, buffer)); @@ -2296,10 +2274,8 @@ pub trait InstanceDma: Instance { #[allow(clippy::too_many_arguments)] unsafe fn start_transfer_dma( &mut self, - rx_desc: *mut DmaDescriptor, - tx_desc: *mut DmaDescriptor, - read_buffer_len: usize, - write_buffer_len: usize, + rx_buffer: &mut impl DmaRxBuffer, + tx_buffer: &mut impl DmaTxBuffer, rx: &mut RX, tx: &mut TX, ) -> Result<(), Error> { @@ -2312,7 +2288,7 @@ pub trait InstanceDma: Instance { reg_block.dma_in_link().write(|w| w.bits(0)); } - self.configure_datalen(usize::max(read_buffer_len, write_buffer_len) as u32 * 8); + self.configure_datalen(usize::max(rx_buffer.length(), tx_buffer.length()) as u32 * 8); rx.is_done(); tx.is_done(); @@ -2327,9 +2303,9 @@ pub trait InstanceDma: Instance { self.clear_dma_interrupts(); reset_dma_before_load_dma_dscr(reg_block); - rx.prepare_transfer(self.dma_peripheral(), rx_desc) + rx.prepare_transfer(self.dma_peripheral(), rx_buffer) .and_then(|_| rx.start_transfer())?; - tx.prepare_transfer(self.dma_peripheral(), tx_desc) + tx.prepare_transfer(self.dma_peripheral(), tx_buffer) .and_then(|_| tx.start_transfer())?; reset_dma_before_usr_cmd(reg_block); @@ -2342,13 +2318,12 @@ pub trait InstanceDma: Instance { #[cfg_attr(feature = "place-spi-driver-in-ram", ram)] unsafe fn start_write_bytes_dma( &mut self, - first_desc: *mut DmaDescriptor, - len: usize, + buffer: &mut impl DmaTxBuffer, tx: &mut TX, full_duplex: bool, ) -> Result<(), Error> { let reg_block = self.register_block(); - self.configure_datalen(len as u32 * 8); + self.configure_datalen(buffer.length() as u32 * 8); tx.is_done(); @@ -2379,7 +2354,7 @@ pub trait InstanceDma: Instance { reset_dma_before_load_dma_dscr(reg_block); self.clear_dma_interrupts(); - tx.prepare_transfer(self.dma_peripheral(), first_desc)?; + tx.prepare_transfer(self.dma_peripheral(), buffer)?; tx.start_transfer()?; reset_dma_before_usr_cmd(reg_block); @@ -2396,10 +2371,9 @@ pub trait InstanceDma: Instance { } #[cfg_attr(feature = "place-spi-driver-in-ram", ram)] - unsafe fn start_read_bytes_dma( + unsafe fn start_read_bytes_dma( &mut self, - desc: *mut DmaDescriptor, - data_length: usize, + buffer: &mut BUF, rx: &mut RX, full_duplex: bool, ) -> Result<(), Error> { @@ -2412,7 +2386,7 @@ pub trait InstanceDma: Instance { reg_block.dma_in_link().write(|w| w.bits(0)); } - self.configure_datalen(data_length as u32 * 8); + self.configure_datalen(buffer.length() as u32 * 8); rx.is_done(); @@ -2431,7 +2405,7 @@ pub trait InstanceDma: Instance { self.clear_dma_interrupts(); reset_dma_before_usr_cmd(reg_block); - rx.prepare_transfer(self.dma_peripheral(), desc)?; + rx.prepare_transfer(self.dma_peripheral(), buffer)?; rx.start_transfer()?; reg_block.cmd().modify(|_, w| w.usr().set_bit());