Skip to content

Commit

Permalink
Introduce traits for the DMA buffer objects
Browse files Browse the repository at this point in the history
  • Loading branch information
Dominic Fischer committed Aug 20, 2024
1 parent a33ebe1 commit 8002aa0
Show file tree
Hide file tree
Showing 3 changed files with 162 additions and 45 deletions.
1 change: 1 addition & 0 deletions esp-hal/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
### Added

- Introduce DMA buffer objects (#1856)
- Introduce traits for the DMA buffer objects (#1976)
- Added new `Io::new_no_bind_interrupt` constructor (#1861)
- Added touch pad support for esp32 (#1873, #1956)
- Allow configuration of period updating method for MCPWM timers (#1898)
Expand Down
125 changes: 120 additions & 5 deletions esp-hal/src/dma/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1878,6 +1878,29 @@ where
}
}

/// Holds all the information need to configure a DMA channel for a transfer.
pub struct Preparation {
pub(crate) start: *mut DmaDescriptor,
pub(crate) length: usize,
// 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 return
/// information required to use this buffer.
fn prepare(&mut self) -> Preparation;
}

/// [DmaRxBuffer] is a DMA descriptor + memory combo that can be used for receiving
/// data from a peripheral's FIFO to a DMA channel.
pub trait DmaRxBuffer {
/// Prepares the buffer for an imminent transfer and return
/// information required to use this buffer.
fn prepare(&mut self) -> Preparation;
}

/// Error returned from Dma[Tx|Rx|TxRx]Buf operations.
#[derive(Debug)]
pub enum DmaBufError {
Expand Down Expand Up @@ -2016,9 +2039,26 @@ impl DmaTxBuf {
pub fn as_slice(&self) -> &[u8] {
self.buffer
}
}

impl DmaTxBuffer for DmaTxBuf {
fn prepare(&mut self) -> Preparation {
// Calculate length and setup descriptor flags.
let mut length = 0;
for desc in self.descriptors.iter_mut() {
// Give ownership to the DMA
desc.set_owner(Owner::Dma);

length += desc.len();
if desc.next.is_null() {
break;
}
}

pub(crate) fn first(&self) -> *mut DmaDescriptor {
self.descriptors.as_ptr() as _
Preparation {
start: self.descriptors.as_mut_ptr(),
length,
}
}
}

Expand Down Expand Up @@ -2227,9 +2267,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 {
// Calculate length and setup descriptor flags.
let mut length = 0;
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);

length += desc.size();
if desc.next.is_null() {
break;
}
}

Preparation {
start: self.descriptors.as_mut_ptr(),
length,
}
}
}

Expand Down Expand Up @@ -2320,7 +2385,7 @@ impl DmaTxRxBuf {
}

/// 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[..]
}

Expand Down Expand Up @@ -2391,6 +2456,56 @@ impl DmaTxRxBuf {
}
}

impl DmaTxBuffer for DmaTxRxBuf {
fn prepare(&mut self) -> Preparation {
// Calculate length and setup descriptor flags.
let mut length = 0;
for desc in self.tx_descriptors.iter_mut() {
// Give ownership to the DMA
desc.set_owner(Owner::Dma);

length += desc.len();
if desc.next.is_null() {
break;
}
}

Preparation {
start: self.tx_descriptors.as_mut_ptr(),
length,
}
}
}

impl DmaRxBuffer for DmaTxRxBuf {
fn prepare(&mut self) -> Preparation {
// Calculate length and setup descriptor flags.
let mut length = 0;
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);

length += desc.size();
if desc.next.is_null() {
break;
}
}

Preparation {
start: self.rx_descriptors.as_mut_ptr(),
length,
}
}
}

pub(crate) mod dma_private {
use super::*;

Expand Down
81 changes: 41 additions & 40 deletions esp-hal/src/spi/master.rs
Original file line number Diff line number Diff line change
Expand Up @@ -866,6 +866,7 @@ pub mod dma {
InterruptConfigurable,
Mode,
};
use crate::dma::{DmaRxBuffer, DmaTxBuffer};

pub trait WithDmaSpi2<'d, C, M, DmaMode>
where
Expand Down Expand Up @@ -1178,19 +1179,19 @@ pub mod dma {
/// bytes.
#[allow(clippy::type_complexity)]
#[cfg_attr(feature = "place-spi-driver-in-ram", ram)]
pub fn dma_write(
pub fn dma_write<TX: DmaTxBuffer>(
mut self,
buffer: DmaTxBuf,
) -> Result<SpiDmaTransfer<'d, T, C, M, DmaMode, DmaTxBuf>, (Error, Self, DmaTxBuf)>
mut buffer: TX,
) -> Result<SpiDmaTransfer<'d, T, C, M, DmaMode, TX>, (Error, Self, TX)>
{
let bytes_to_write = buffer.len();
if bytes_to_write > MAX_DMA_SIZE {
let preparation = buffer.prepare();
if preparation.length > 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)
.start_write_bytes_dma(preparation.start, preparation.length, &mut self.channel.tx)
};
if let Err(e) = result {
return Err((e, self, buffer));
Expand All @@ -1206,19 +1207,19 @@ pub 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<RX: DmaRxBuffer>(
mut self,
buffer: DmaRxBuf,
) -> Result<SpiDmaTransfer<'d, T, C, M, DmaMode, DmaRxBuf>, (Error, Self, DmaRxBuf)>
mut buffer: RX,
) -> Result<SpiDmaTransfer<'d, T, C, M, DmaMode, RX>, (Error, Self, RX)>
{
let bytes_to_read = buffer.len();
if bytes_to_read > MAX_DMA_SIZE {
let preparation = buffer.prepare();
if preparation.length > 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)
.start_read_bytes_dma(preparation.start, preparation.length, &mut self.channel.rx)
};
if let Err(e) = result {
return Err((e, self, buffer));
Expand All @@ -1233,18 +1234,18 @@ pub 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<TX: DmaTxBuffer, RX: DmaRxBuffer>(
mut self,
tx_buffer: DmaTxBuf,
rx_buffer: DmaRxBuf,
mut tx_buffer: TX,
mut rx_buffer: RX,
) -> Result<
SpiDmaTransfer<'d, T, C, M, DmaMode, (DmaTxBuf, DmaRxBuf)>,
(Error, Self, DmaTxBuf, DmaRxBuf),
SpiDmaTransfer<'d, T, C, M, DmaMode, (TX, RX)>,
(Error, Self, TX, RX),
> {
let bytes_to_write = tx_buffer.len();
let bytes_to_read = rx_buffer.len();
let tx_preparation = tx_buffer.prepare();
let rx_preparation = rx_buffer.prepare();

if bytes_to_write > MAX_DMA_SIZE || bytes_to_read > MAX_DMA_SIZE {
if tx_preparation.length > MAX_DMA_SIZE || rx_preparation.length > MAX_DMA_SIZE {
return Err((
Error::MaxDmaTransferSizeExceeded,
self,
Expand All @@ -1255,10 +1256,10 @@ pub mod dma {

let result = unsafe {
self.spi.start_transfer_dma(
tx_buffer.first(),
rx_buffer.first(),
bytes_to_write,
bytes_to_read,
tx_preparation.start,
rx_preparation.start,
tx_preparation.length,
rx_preparation.length,
&mut self.channel.tx,
&mut self.channel.rx,
)
Expand Down Expand Up @@ -1286,17 +1287,17 @@ pub mod dma {
{
#[allow(clippy::type_complexity)]
#[cfg_attr(feature = "place-spi-driver-in-ram", ram)]
pub fn read(
pub fn read<RX: DmaRxBuffer>(
mut self,
data_mode: SpiDataMode,
cmd: Command,
address: Address,
dummy: u8,
buffer: DmaRxBuf,
) -> Result<SpiDmaTransfer<'d, T, C, M, DmaMode, DmaRxBuf>, (Error, Self, DmaRxBuf)>
mut buffer: RX,
) -> Result<SpiDmaTransfer<'d, T, C, M, DmaMode, RX>, (Error, Self, RX)>
{
let bytes_to_read = buffer.len();
if bytes_to_read > MAX_DMA_SIZE {
let preparation = buffer.prepare();
if preparation.length > MAX_DMA_SIZE {
return Err((Error::MaxDmaTransferSizeExceeded, self, buffer));
}

Expand All @@ -1306,7 +1307,7 @@ pub mod dma {
!address.is_none(),
false,
dummy != 0,
bytes_to_read == 0,
preparation.length == 0,
);
self.spi
.init_spi_data_mode(cmd.mode(), address.mode(), data_mode);
Expand Down Expand Up @@ -1352,28 +1353,28 @@ pub mod dma {

let result = unsafe {
self.spi
.start_read_bytes_dma(buffer.first(), bytes_to_read, &mut self.channel.rx)
.start_read_bytes_dma(preparation.start, preparation.length, &mut self.channel.rx)
};
if let Err(e) = result {
return Err((e, self, buffer));
}

Ok(SpiDmaTransfer::new(self, buffer, bytes_to_read > 0, false))
Ok(SpiDmaTransfer::new(self, buffer, preparation.length > 0, false))
}

#[allow(clippy::type_complexity)]
#[cfg_attr(feature = "place-spi-driver-in-ram", ram)]
pub fn write(
pub fn write<TX: DmaTxBuffer>(
mut self,
data_mode: SpiDataMode,
cmd: Command,
address: Address,
dummy: u8,
buffer: DmaTxBuf,
) -> Result<SpiDmaTransfer<'d, T, C, M, DmaMode, DmaTxBuf>, (Error, Self, DmaTxBuf)>
mut buffer: TX,
) -> Result<SpiDmaTransfer<'d, T, C, M, DmaMode, TX>, (Error, Self, TX)>
{
let bytes_to_write = buffer.len();
if bytes_to_write > MAX_DMA_SIZE {
let preparation = buffer.prepare();
if preparation.length > MAX_DMA_SIZE {
return Err((Error::MaxDmaTransferSizeExceeded, self, buffer));
}

Expand All @@ -1383,7 +1384,7 @@ pub mod dma {
!address.is_none(),
false,
dummy != 0,
bytes_to_write == 0,
preparation.length == 0,
);
self.spi
.init_spi_data_mode(cmd.mode(), address.mode(), data_mode);
Expand Down Expand Up @@ -1429,13 +1430,13 @@ pub mod dma {

let result = unsafe {
self.spi
.start_write_bytes_dma(buffer.first(), bytes_to_write, &mut self.channel.tx)
.start_write_bytes_dma(preparation.start, preparation.length, &mut self.channel.tx)
};
if let Err(e) = result {
return Err((e, self, buffer));
}

Ok(SpiDmaTransfer::new(self, buffer, false, bytes_to_write > 0))
Ok(SpiDmaTransfer::new(self, buffer, false, preparation.length > 0))
}
}

Expand Down

0 comments on commit 8002aa0

Please sign in to comment.