diff --git a/esp-hal/CHANGELOG.md b/esp-hal/CHANGELOG.md
index 47ff2831a7..7c636d4535 100644
--- a/esp-hal/CHANGELOG.md
+++ b/esp-hal/CHANGELOG.md
@@ -25,7 +25,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- `Rng` and `Trng` now implement `Peripheral
` (#2992)
- SPI, UART, I2C: `with_` functions of peripheral drivers now disconnect the previously assigned pins from the peripheral. (#3012)
- SPI, UART, I2C: Dropping a driver now disconnects pins from their peripherals. (#3012)
-
+- Migrate PARL_IO driver to DMA move API (#0000)
- `Async` drivers are no longer `Send` (#2980)
- GPIO drivers now take configuration structs, and their constructors are fallible (#2990)
- `flip-link` feature is now a config option
diff --git a/esp-hal/MIGRATING-0.23.md b/esp-hal/MIGRATING-0.23.md
index 86cb55ddb6..795aa39e94 100644
--- a/esp-hal/MIGRATING-0.23.md
+++ b/esp-hal/MIGRATING-0.23.md
@@ -162,6 +162,34 @@ config/config.toml
+ ESP_HAL_CONFIG_PSRAM_MODE = "octal"
```
+## PARL_IO changes
+Parallel IO now uses the newer DMA Move API.
+
+Changes on the TX side
+```diff
+ let (_, _, tx_buffer, tx_descriptors) = dma_buffers!(0, 32000);
++ let mut dma_tx_buf = DmaTxBuf::new(tx_descriptors, tx_buffer).unwrap();
+
+- let transfer = parl_io_tx.write_dma(&tx_buffer).unwrap();
+- transfer.wait().unwrap();
++ let transfer = parl_io_tx.write(Some(dma_tx_buf.len()), dma_tx_buf).unwrap();
++ (result, parl_io_tx, dma_tx_buf) = transfer.wait();
++ result.unwrap();
+```
+
+Changes on the RX side
+```diff
+ let (rx_buffer, rx_descriptors, _, _) = dma_buffers!(32000, 0);
++ let mut dma_rx_buf = DmaRxBuf::new(rx_descriptors, rx_buffer).unwrap();
+- let transfer = parl_io_rx.read_dma(&mut rx_buffer).unwrap();
+- transfer.wait().unwrap();
++ let transfer = parl_io_rx.read(Some(dma_rx_buf.len()), dma_rx_buf).unwrap();
++ (_, parl_io_rx, dma_rx_buf) = transfer.wait();
+```
+
+On the RX side, the `EofMode` is now decided at transfer time, rather than config time.
+- `EofMode::ByteLen` -> `Some()`
+- `EofMode::EnableSignal` -> `None`
## UART halves have their configuration split too
diff --git a/esp-hal/src/parl_io.rs b/esp-hal/src/parl_io.rs
index 5b0414512b..fccb38890e 100644
--- a/esp-hal/src/parl_io.rs
+++ b/esp-hal/src/parl_io.rs
@@ -22,6 +22,7 @@
//!
//! // Initialize DMA buffer and descriptors for data reception
//! let (rx_buffer, rx_descriptors, _, _) = dma_buffers!(32000, 0);
+//! let mut dma_rx_buf = DmaRxBuf::new(rx_descriptors, rx_buffer).unwrap();
//! let dma_channel = peripherals.DMA_CH0;
//!
//! // Configure the 4-bit input pins and clock pin
@@ -38,7 +39,6 @@
//! let parl_io = ParlIoRxOnly::new(
//! peripherals.PARL_IO,
//! dma_channel,
-//! rx_descriptors,
//! 1.MHz(),
//! )
//! .unwrap();
@@ -54,29 +54,28 @@
//! .unwrap();
//!
//! // Initialize the buffer and delay
-//! let mut buffer = rx_buffer;
-//! buffer.fill(0u8);
+//! dma_rx_buf.as_mut_slice().fill(0u8);
//! let delay = Delay::new();
//!
//! loop {
//! // Read data via DMA and print received values
-//! let transfer = parl_io_rx.read_dma(&mut buffer).unwrap();
-//! transfer.wait().unwrap();
+//! let transfer = parl_io_rx.read(Some(dma_rx_buf.len()), dma_rx_buf).unwrap();
+//! (_, parl_io_rx, dma_rx_buf) = transfer.wait();
//!
//! delay.delay_millis(500);
//! }
//! # }
//! ```
-//!
+//!
//! ### Initialization for TX
//! ```rust, no_run
#![doc = crate::before_snippet!()]
//! # use esp_hal::delay::Delay;
-//! # use esp_hal::dma_buffers;
+//! # use esp_hal::dma_tx_buffer;
//! # use esp_hal::parl_io::{BitPackOrder, ParlIoTxOnly, TxFourBits, SampleEdge, ClkOutPin, TxPinConfigWithValidPin};
//!
//! // Initialize DMA buffer and descriptors for data reception
-//! let (_, _, tx_buffer, tx_descriptors) = dma_buffers!(0, 32000);
+//! let mut dma_tx_buf = dma_tx_buffer!(32000).unwrap();
//! let dma_channel = peripherals.DMA_CH0;
//!
//! // Configure the 4-bit input pins and clock pin
@@ -94,7 +93,6 @@
//! let parl_io = ParlIoTxOnly::new(
//! peripherals.PARL_IO,
//! dma_channel,
-//! tx_descriptors,
//! 1.MHz(),
//! )
//! .unwrap();
@@ -112,19 +110,21 @@
//! .unwrap();
//!
//! let buffer = tx_buffer;
-//! for i in 0..buffer.len() {
-//! buffer[i] = (i % 255) as u8;
+//! for i in 0..dma_tx_buf.len() {
+//! dma_tx_buf.as_mut_slice()[i] = (i % 255) as u8;
//! }
//!
//! let delay = Delay::new();
//! loop {
-//! let transfer = parl_io_tx.write_dma(&buffer).unwrap();
-//! transfer.wait().unwrap();
+//! let transfer = parl_io_tx.write(Some(dma_tx_buf.len()), dma_tx_buf).unwrap();
+//! (_, parl_io_tx, dma_tx_buf) = transfer.wait();
//! delay.delay_millis(500);
//! }
//! # }
//! ```
+use core::mem::ManuallyDrop;
+use core::ops::{Deref, DerefMut};
use enumset::{EnumSet, EnumSetType};
use fugit::HertzU32;
use peripheral::PeripheralRef;
@@ -132,20 +132,14 @@ use private::*;
use crate::{
dma::{
- dma_private::{DmaSupport, DmaSupportRx, DmaSupportTx},
Channel,
ChannelRx,
ChannelTx,
- DescriptorChain,
DmaChannelFor,
- DmaDescriptor,
DmaError,
DmaPeripheral,
- DmaTransferRx,
- DmaTransferTx,
PeripheralRxChannel,
PeripheralTxChannel,
- ReadBuffer,
Rx,
RxChannelFor,
Tx,
@@ -164,6 +158,8 @@ use crate::{
Blocking,
DriverMode,
};
+use crate::dma::{DmaRxBuffer, DmaTxBuffer};
+use crate::parl_io::asynch::interrupt_handler;
#[allow(unused)]
const MAX_DMA_SIZE: usize = 32736;
@@ -355,16 +351,6 @@ impl EnableMode {
}
}
-/// Generation of GDMA SUC EOF
-#[derive(Debug, Clone, Copy, PartialEq, Eq)]
-#[cfg_attr(feature = "defmt", derive(defmt::Format))]
-pub enum EofMode {
- /// Generate GDMA SUC EOF by data byte length
- ByteLen,
- /// Generate GDMA SUC EOF by the external enable signal
- EnableSignal,
-}
-
/// Used to configure no pin as clock output
impl TxClkPin for NoPin {
fn configure(&mut self) {
@@ -631,7 +617,6 @@ where
rx_pins: P,
valid_pin: PeripheralRef<'d, InputConnection>,
enable_mode: EnableMode,
- eof_mode: EofMode,
}
impl<'d, P> RxPinConfigWithValidPin<'d, P>
@@ -643,14 +628,12 @@ where
rx_pins: P,
valid_pin: impl Peripheral + 'd,
enable_mode: EnableMode,
- eof_mode: EofMode,
) -> Self {
crate::into_mapped_ref!(valid_pin);
Self {
rx_pins,
valid_pin,
enable_mode,
- eof_mode,
}
}
}
@@ -678,7 +661,6 @@ where
if let Some(sel) = self.enable_mode.smp_model_sel() {
Instance::set_rx_sample_mode(sel);
}
- Instance::set_eof_gen_sel(self.eof_mode);
Ok(())
}
@@ -691,7 +673,6 @@ where
{
rx_pins: P,
enable_mode: EnableMode,
- eof_mode: EofMode,
}
impl
RxPinConfigIncludingValidPin
@@ -699,11 +680,10 @@ where
P: ContainsValidSignalPin + RxPins + ConfigurePins,
{
/// Create a new [RxPinConfigIncludingValidPin]
- pub fn new(rx_pins: P, enable_mode: EnableMode, eof_mode: EofMode) -> Self {
+ pub fn new(rx_pins: P, enable_mode: EnableMode) -> Self {
Self {
rx_pins,
enable_mode,
- eof_mode,
}
}
}
@@ -729,7 +709,6 @@ where
if let Some(sel) = self.enable_mode.smp_model_sel() {
Instance::set_rx_sample_mode(sel);
}
- Instance::set_eof_gen_sel(self.eof_mode);
Ok(())
}
@@ -867,7 +846,6 @@ where
Ok(ParlIoTx {
tx_channel: self.tx_channel,
- tx_chain: DescriptorChain::new(self.descriptors),
_guard: self._guard,
})
}
@@ -899,7 +877,6 @@ where
Ok(ParlIoTx {
tx_channel: self.tx_channel,
- tx_chain: DescriptorChain::new(self.descriptors),
_guard: self._guard,
})
}
@@ -912,7 +889,6 @@ where
Dm: DriverMode,
{
tx_channel: ChannelTx<'d, Dm, PeripheralTxChannel>,
- tx_chain: DescriptorChain,
_guard: GenericPeripheralGuard<{ crate::system::Peripheral::ParlIo as u8 }>,
}
@@ -951,7 +927,6 @@ where
Ok(ParlIoRx {
rx_channel: self.rx_channel,
- rx_chain: DescriptorChain::new(self.descriptors),
_guard: guard,
})
}
@@ -981,7 +956,6 @@ where
Ok(ParlIoRx {
rx_channel: self.rx_channel,
- rx_chain: DescriptorChain::new(self.descriptors),
_guard: self._guard,
})
}
@@ -994,7 +968,6 @@ where
Dm: DriverMode,
{
rx_channel: ChannelRx<'d, Dm, PeripheralRxChannel>,
- rx_chain: DescriptorChain,
_guard: GenericPeripheralGuard<{ crate::system::Peripheral::ParlIo as u8 }>,
}
@@ -1109,8 +1082,6 @@ impl<'d> ParlIoFullDuplex<'d, Blocking> {
pub fn new(
_parl_io: impl Peripheral + 'd,
dma_channel: impl Peripheral
+ 'd,
- tx_descriptors: &'static mut [DmaDescriptor],
- rx_descriptors: &'static mut [DmaDescriptor],
frequency: HertzU32,
) -> Result
where
@@ -1124,12 +1095,10 @@ impl<'d> ParlIoFullDuplex<'d, Blocking> {
Ok(Self {
tx: TxCreatorFullDuplex {
tx_channel: dma_channel.tx,
- descriptors: tx_descriptors,
_guard: tx_guard,
},
rx: RxCreatorFullDuplex {
rx_channel: dma_channel.rx,
- descriptors: rx_descriptors,
_guard: rx_guard,
},
})
@@ -1148,15 +1117,32 @@ impl<'d> ParlIoFullDuplex<'d, Blocking> {
crate::interrupt::disable(core, Interrupt::PARL_IO_TX);
}
}
+
+ #[cfg(esp32c6)]
+ {
+ unsafe {
+ crate::interrupt::bind_interrupt(Interrupt::PARL_IO, interrupt_handler.handler());
+ }
+ unwrap!(crate::interrupt::enable(Interrupt::PARL_IO, interrupt_handler.priority()));
+ }
+ #[cfg(esp32h2)]
+ {
+ unsafe {
+ crate::interrupt::bind_interrupt(Interrupt::PARL_IO_TX, interrupt_handler.handler());
+ }
+ unwrap!(crate::interrupt::enable(
+ Interrupt::PARL_IO_TX,
+ interrupt_handler.priority()
+ ));
+ }
+
ParlIoFullDuplex {
tx: TxCreatorFullDuplex {
tx_channel: self.tx.tx_channel.into_async(),
- descriptors: self.tx.descriptors,
_guard: self.tx._guard,
},
rx: RxCreatorFullDuplex {
rx_channel: self.rx.rx_channel.into_async(),
- descriptors: self.rx.descriptors,
_guard: self.rx._guard,
},
}
@@ -1205,12 +1191,10 @@ impl<'d> ParlIoFullDuplex<'d, Async> {
ParlIoFullDuplex {
tx: TxCreatorFullDuplex {
tx_channel: self.tx.tx_channel.into_blocking(),
- descriptors: self.tx.descriptors,
_guard: self.tx._guard,
},
rx: RxCreatorFullDuplex {
rx_channel: self.rx.rx_channel.into_blocking(),
- descriptors: self.rx.descriptors,
_guard: self.rx._guard,
},
}
@@ -1232,7 +1216,6 @@ impl<'d> ParlIoTxOnly<'d, Blocking> {
pub fn new(
_parl_io: impl Peripheral + 'd,
dma_channel: impl Peripheral
+ 'd,
- descriptors: &'static mut [DmaDescriptor],
frequency: HertzU32,
) -> Result
where
@@ -1245,7 +1228,6 @@ impl<'d> ParlIoTxOnly<'d, Blocking> {
Ok(Self {
tx: TxCreator {
tx_channel,
- descriptors,
_guard: guard,
},
})
@@ -1264,10 +1246,27 @@ impl<'d> ParlIoTxOnly<'d, Blocking> {
crate::interrupt::disable(core, Interrupt::PARL_IO_TX);
}
}
+ #[cfg(esp32c6)]
+ {
+ unsafe {
+ crate::interrupt::bind_interrupt(Interrupt::PARL_IO, interrupt_handler.handler());
+ }
+ unwrap!(crate::interrupt::enable(Interrupt::PARL_IO, interrupt_handler.priority()));
+ }
+ #[cfg(esp32h2)]
+ {
+ unsafe {
+ crate::interrupt::bind_interrupt(Interrupt::PARL_IO_TX, interrupt_handler.handler());
+ }
+ unwrap!(crate::interrupt::enable(
+ Interrupt::PARL_IO_TX,
+ interrupt_handler.priority()
+ ));
+ }
+
ParlIoTxOnly {
tx: TxCreator {
tx_channel: self.tx.tx_channel.into_async(),
- descriptors: self.tx.descriptors,
_guard: self.tx._guard,
},
}
@@ -1308,7 +1307,6 @@ impl<'d> ParlIoTxOnly<'d, Async> {
ParlIoTxOnly {
tx: TxCreator {
tx_channel: self.tx.tx_channel.into_blocking(),
- descriptors: self.tx.descriptors,
_guard: self.tx._guard,
},
}
@@ -1338,7 +1336,6 @@ impl<'d> ParlIoRxOnly<'d, Blocking> {
pub fn new(
_parl_io: impl Peripheral + 'd,
dma_channel: impl Peripheral
+ 'd,
- descriptors: &'static mut [DmaDescriptor],
frequency: HertzU32,
) -> Result
where
@@ -1351,7 +1348,6 @@ impl<'d> ParlIoRxOnly<'d, Blocking> {
Ok(Self {
rx: RxCreator {
rx_channel,
- descriptors,
_guard: guard,
},
})
@@ -1370,11 +1366,27 @@ impl<'d> ParlIoRxOnly<'d, Blocking> {
crate::interrupt::disable(core, Interrupt::PARL_IO_TX);
}
}
+ #[cfg(esp32c6)]
+ {
+ unsafe {
+ crate::interrupt::bind_interrupt(Interrupt::PARL_IO, interrupt_handler.handler());
+ }
+ unwrap!(crate::interrupt::enable(Interrupt::PARL_IO, interrupt_handler.priority()));
+ }
+ #[cfg(esp32h2)]
+ {
+ unsafe {
+ crate::interrupt::bind_interrupt(Interrupt::PARL_IO_TX, interrupt_handler.handler());
+ }
+ unwrap!(crate::interrupt::enable(
+ Interrupt::PARL_IO_TX,
+ interrupt_handler.priority()
+ ));
+ }
ParlIoRxOnly {
rx: RxCreator {
rx_channel: self.rx.rx_channel.into_async(),
- descriptors: self.rx.descriptors,
_guard: self.rx._guard,
},
}
@@ -1415,7 +1427,6 @@ impl<'d> ParlIoRxOnly<'d, Async> {
ParlIoRxOnly {
rx: RxCreator {
rx_channel: self.rx.rx_channel.into_blocking(),
- descriptors: self.rx.descriptors,
_guard: self.rx._guard,
},
}
@@ -1458,47 +1469,37 @@ fn internal_init(frequency: HertzU32) -> Result<(), Error> {
Ok(())
}
-impl ParlIoTx<'_, Dm>
+impl<'d, Dm> ParlIoTx<'d, Dm>
where
Dm: DriverMode,
{
/// Perform a DMA write.
///
- /// This will return a [DmaTransferTx]
+ /// This will return a [ParlIoTxTransfer]
///
/// The maximum amount of data to be sent is 32736 bytes.
- pub fn write_dma<'t, TXBUF>(
- &'t mut self,
- words: &'t TXBUF,
- ) -> Result, Error>
+ pub fn write(
+ mut self,
+ number_of_bytes: usize,
+ mut buffer: BUF,
+ ) -> Result, Error>
where
- TXBUF: ReadBuffer,
+ BUF: DmaTxBuffer,
{
- let (ptr, len) = unsafe { words.read_buffer() };
-
- if len > MAX_DMA_SIZE {
+ if number_of_bytes > MAX_DMA_SIZE {
return Err(Error::MaxDmaTransferSizeExceeded);
}
- self.start_write_bytes_dma(ptr, len)?;
-
- Ok(DmaTransferTx::new(self))
- }
-
- fn start_write_bytes_dma(&mut self, ptr: *const u8, len: usize) -> Result<(), Error> {
PCR::regs()
.parl_clk_tx_conf()
.modify(|_, w| w.parl_tx_rst_en().set_bit());
Instance::clear_tx_interrupts();
- Instance::set_tx_bytes(len as u16);
-
- self.tx_channel.is_done();
+ Instance::set_tx_bytes(number_of_bytes as u16);
unsafe {
- self.tx_chain.fill_for_tx(false, ptr, len)?;
self.tx_channel
- .prepare_transfer_without_start(DmaPeripheral::ParlIo, &self.tx_chain)
+ .prepare_transfer(DmaPeripheral::ParlIo, &mut buffer)
.and_then(|_| self.tx_channel.start_transfer())?;
}
@@ -1510,37 +1511,82 @@ where
.parl_clk_tx_conf()
.modify(|_, w| w.parl_tx_rst_en().clear_bit());
- Ok(())
+ Ok(ParlIoTxTransfer {
+ parl_io: ManuallyDrop::new(self),
+ buf_view: ManuallyDrop::new(buffer.into_view()),
+ })
}
}
-impl DmaSupport for ParlIoTx<'_, Dm>
-where
- Dm: DriverMode,
-{
- fn peripheral_wait_dma(&mut self, _is_rx: bool, _is_tx: bool) {
- while !Instance::is_tx_eof() {}
+/// Represents an ongoing (or potentially finished) transfer using the PARL_IO TX.
+pub struct ParlIoTxTransfer<'d, BUF: DmaTxBuffer, Dm: DriverMode> {
+ parl_io: ManuallyDrop>,
+ buf_view: ManuallyDrop,
+}
+
+impl<'d, BUF: DmaTxBuffer, Dm: DriverMode> ParlIoTxTransfer<'d, BUF, Dm> {
+ /// Returns true when [Self::wait] will not block.
+ pub fn is_done(&self) -> bool {
+ Instance::is_tx_eof()
+ }
+
+ /// Waits for the transfer to finish and returns the peripheral and buffer.
+ pub fn wait(mut self) -> (Result<(), DmaError>, ParlIoTx<'d, Dm>, BUF) {
+ while !self.is_done() {}
Instance::set_tx_start(false);
+
+ // Stop the DMA as it doesn't know that the parl io has stopped.
+ self.parl_io.tx_channel.stop_transfer();
+
+ let (parl_io, view) = self.release();
+
+ let result = if parl_io.tx_channel.has_error() {
+ Err(DmaError::DescriptorError)
+ } else {
+ Ok(())
+ };
+
+ (result, parl_io, BUF::from_view(view))
}
- fn peripheral_dma_stop(&mut self) {
- unreachable!("unsupported")
+ fn release(mut self) -> (ParlIoTx<'d, Dm>, BUF::View) {
+ let (parl_io, view) = unsafe {
+ (ManuallyDrop::take(&mut self.parl_io),
+ ManuallyDrop::take(&mut self.buf_view))
+ };
+ core::mem::forget(self);
+ (parl_io, view)
}
}
-impl<'d, Dm> DmaSupportTx for ParlIoTx<'d, Dm>
-where
- Dm: DriverMode,
-{
- type TX = ChannelTx<'d, Dm, PeripheralTxChannel>;
+impl<'d, BUF: DmaTxBuffer, Dm: DriverMode> Deref for ParlIoTxTransfer<'d, BUF, Dm> {
+ type Target = BUF::View;
+
+ fn deref(&self) -> &Self::Target {
+ &self.buf_view
+ }
+}
- fn tx(&mut self) -> &mut Self::TX {
- &mut self.tx_channel
+impl<'d, BUF: DmaTxBuffer, Dm: DriverMode> DerefMut for ParlIoTxTransfer<'d, BUF, Dm> {
+ fn deref_mut(&mut self) -> &mut Self::Target {
+ &mut self.buf_view
}
+}
+
+impl<'d, BUF: DmaTxBuffer, Dm: DriverMode> Drop for ParlIoTxTransfer<'d, BUF, Dm> {
+ fn drop(&mut self) {
+ // There's no documented way to cancel the PARL IO transfer, so we'll just stop the DMA to
+ // stop the memory access.
+ self.parl_io.tx_channel.stop_transfer();
- fn chain(&mut self) -> &mut DescriptorChain {
- &mut self.tx_chain
+ // SAFETY: This is Drop, we know that self.parl_io and self.buf_view
+ // won't be touched again.
+ let view = unsafe {
+ ManuallyDrop::drop(&mut self.parl_io);
+ ManuallyDrop::take(&mut self.buf_view)
+ };
+ let _ = BUF::from_view(view);
}
}
@@ -1550,36 +1596,22 @@ where
{
/// Perform a DMA read.
///
- /// This will return a [DmaTransferRx]
+ /// This will return a [ParlIoRxTransfer]
///
- /// The maximum amount of data is 32736 bytes when using [EofMode::ByteLen].
+ /// When the number of bytes is specified, the maximum amount of data is 32736 bytes and
+ /// the transfer ends when the number of specified bytes is received.
///
- /// It's only limited by the size of the DMA buffer when using
- /// [EofMode::EnableSignal].
- pub fn read_dma<'t, RXBUF>(
- &'t mut self,
- words: &'t mut RXBUF,
- ) -> Result, Error>
+ /// When the number of bytes is unspecified, there's no limit the amount of data transferred
+ /// and the transfer ends when the enable signal signals the end or the DMA buffer runs out of
+ /// space.
+ pub fn read(
+ mut self,
+ number_of_bytes: Option,
+ mut buffer: BUF,
+ ) -> Result, Error>
where
- RXBUF: WriteBuffer,
+ BUF: DmaRxBuffer,
{
- let (ptr, len) = unsafe { words.write_buffer() };
-
- if !Instance::is_suc_eof_generated_externally() && len > MAX_DMA_SIZE {
- return Err(Error::MaxDmaTransferSizeExceeded);
- }
-
- Self::start_receive_bytes_dma(&mut self.rx_channel, &mut self.rx_chain, ptr, len)?;
-
- Ok(DmaTransferRx::new(self))
- }
-
- fn start_receive_bytes_dma(
- rx_channel: &mut ChannelRx<'d, Dm, PeripheralRxChannel>,
- rx_chain: &mut DescriptorChain,
- ptr: *mut u8,
- len: usize,
- ) -> Result<(), Error> {
PCR::regs()
.parl_clk_rx_conf()
.modify(|_, w| w.parl_rx_rst_en().set_bit());
@@ -1588,56 +1620,110 @@ where
.modify(|_, w| w.parl_rx_rst_en().clear_bit());
Instance::clear_rx_interrupts();
- Instance::set_rx_bytes(len as u16);
+ if let Some(number_of_bytes) = number_of_bytes {
+ if number_of_bytes > MAX_DMA_SIZE {
+ return Err(Error::MaxDmaTransferSizeExceeded);
+ }
+ Instance::set_rx_bytes(number_of_bytes as u16);
+ Instance::set_eof_gen_sel(EofMode::ByteLen);
+ } else {
+ Instance::set_eof_gen_sel(EofMode::EnableSignal);
+ }
unsafe {
- rx_chain.fill_for_rx(false, ptr, len)?;
- rx_channel
- .prepare_transfer_without_start(DmaPeripheral::ParlIo, rx_chain)
- .and_then(|_| rx_channel.start_transfer())?;
+ self.rx_channel
+ .prepare_transfer(DmaPeripheral::ParlIo, &mut buffer)
+ .and_then(|_| self.rx_channel.start_transfer())?;
}
Instance::set_rx_reg_update();
Instance::set_rx_start(true);
- Ok(())
+
+ Ok(ParlIoRxTransfer {
+ parl_io: ManuallyDrop::new(self),
+ buf_view: ManuallyDrop::new(buffer.into_view()),
+ dma_result: None,
+ })
}
}
-impl DmaSupport for ParlIoRx<'_, Dm>
-where
- Dm: DriverMode,
-{
- fn peripheral_wait_dma(&mut self, _is_rx: bool, _is_tx: bool) {
- loop {
- if self.rx_channel.is_done()
- || self.rx_channel.has_eof_error()
- || self.rx_channel.has_dscr_empty_error()
- {
- break;
- }
+/// Represents an ongoing (or potentially finished) transfer using the PARL_IO TX.
+pub struct ParlIoRxTransfer<'d, BUF: DmaRxBuffer, Dm: DriverMode> {
+ parl_io: ManuallyDrop>,
+ buf_view: ManuallyDrop,
+ // Needed to use DmaRxFuture, which clear the bits we check in is_done()
+ dma_result: Option>,
+}
+
+impl<'d, BUF: DmaRxBuffer, Dm: DriverMode> ParlIoRxTransfer<'d, BUF, Dm> {
+ /// Returns true when [Self::wait] will not block.
+ pub fn is_done(&self) -> bool {
+ if self.dma_result.is_some() {
+ return true;
}
+ let ch = &self.parl_io.rx_channel;
+ ch.is_done() || ch.has_eof_error() || ch.has_dscr_empty_error()
+ }
+
+ /// Waits for the transfer to finish and returns the peripheral and buffer.
+ pub fn wait(mut self) -> (Result<(), DmaError>, ParlIoRx<'d, Dm>, BUF) {
+ while !self.is_done() {}
Instance::set_rx_start(false);
+
+ // Stop the DMA as it doesn't know that the parl io has stopped.
+ self.parl_io.rx_channel.stop_transfer();
+
+ let dma_result = self.dma_result.take();
+ let (parl_io, view) = self.release();
+
+ let result = if parl_io.rx_channel.has_error() {
+ Err(DmaError::DescriptorError)
+ } else {
+ dma_result.unwrap_or(Ok(()))
+ };
+
+ (result, parl_io, BUF::from_view(view))
}
- fn peripheral_dma_stop(&mut self) {
- unreachable!("unsupported")
+ fn release(mut self) -> (ParlIoRx<'d, Dm>, BUF::View) {
+ let (parl_io, view) = unsafe {
+ (ManuallyDrop::take(&mut self.parl_io),
+ ManuallyDrop::take(&mut self.buf_view))
+ };
+ core::mem::forget(self);
+ (parl_io, view)
}
}
-impl<'d, Dm> DmaSupportRx for ParlIoRx<'d, Dm>
-where
- Dm: DriverMode,
-{
- type RX = ChannelRx<'d, Dm, PeripheralRxChannel>;
+impl<'d, BUF: DmaRxBuffer, Dm: DriverMode> Deref for ParlIoRxTransfer<'d, BUF, Dm> {
+ type Target = BUF::View;
- fn rx(&mut self) -> &mut Self::RX {
- &mut self.rx_channel
+ fn deref(&self) -> &Self::Target {
+ &self.buf_view
}
+}
+
+impl<'d, BUF: DmaRxBuffer, Dm: DriverMode> DerefMut for ParlIoRxTransfer<'d, BUF, Dm> {
+ fn deref_mut(&mut self) -> &mut Self::Target {
+ &mut self.buf_view
+ }
+}
- fn chain(&mut self) -> &mut DescriptorChain {
- &mut self.rx_chain
+impl<'d, BUF: DmaRxBuffer, Dm: DriverMode> Drop for ParlIoRxTransfer<'d, BUF, Dm> {
+ fn drop(&mut self) {
+ // There's no documented way to cancel the PARL IO transfer, so we'll just stop the DMA to
+ // stop the memory access.
+ self.parl_io.rx_channel.stop_transfer();
+
+ // SAFETY: This is Drop, we know that self.parl_io and self.buf_view
+ // won't be touched again.
+ let view = unsafe {
+ ManuallyDrop::drop(&mut self.parl_io);
+ ManuallyDrop::take(&mut self.buf_view)
+ };
+ let _ = BUF::from_view(view);
}
}
@@ -1647,7 +1733,6 @@ where
Dm: DriverMode,
{
tx_channel: ChannelTx<'d, Dm, PeripheralTxChannel>,
- descriptors: &'static mut [DmaDescriptor],
_guard: GenericPeripheralGuard<{ system::Peripheral::ParlIo as u8 }>,
}
@@ -1657,7 +1742,6 @@ where
Dm: DriverMode,
{
rx_channel: ChannelRx<'d, Dm, PeripheralRxChannel>,
- descriptors: &'static mut [DmaDescriptor],
_guard: GenericPeripheralGuard<{ system::Peripheral::ParlIo as u8 }>,
}
@@ -1667,7 +1751,6 @@ where
Dm: DriverMode,
{
tx_channel: ChannelTx<'d, Dm, PeripheralTxChannel>,
- descriptors: &'static mut [DmaDescriptor],
_guard: GenericPeripheralGuard<{ system::Peripheral::ParlIo as u8 }>,
}
@@ -1677,7 +1760,6 @@ where
Dm: DriverMode,
{
rx_channel: ChannelRx<'d, Dm, PeripheralRxChannel>,
- descriptors: &'static mut [DmaDescriptor],
_guard: GenericPeripheralGuard<{ system::Peripheral::ParlIo as u8 }>,
}
@@ -1687,12 +1769,12 @@ pub mod asynch {
use procmacros::handler;
- use super::{private::Instance, Error, ParlIoRx, ParlIoTx, MAX_DMA_SIZE};
+ use super::{private::Instance, ParlIoRxTransfer, ParlIoTxTransfer};
use crate::{
asynch::AtomicWaker,
- dma::{asynch::DmaRxFuture, ReadBuffer, WriteBuffer},
- peripherals::{Interrupt, PARL_IO},
+ dma::asynch::DmaRxFuture,
};
+ use crate::dma::{DmaRxBuffer, DmaTxBuffer};
static TX_WAKER: AtomicWaker = AtomicWaker::new();
@@ -1701,26 +1783,6 @@ pub mod asynch {
impl TxDoneFuture {
pub fn new() -> Self {
- Instance::listen_tx_done();
- let mut parl_io = unsafe { PARL_IO::steal() };
-
- #[cfg(esp32c6)]
- {
- parl_io.bind_parl_io_interrupt(interrupt_handler.handler());
- unwrap!(crate::interrupt::enable(
- Interrupt::PARL_IO,
- interrupt_handler.priority()
- ));
- }
- #[cfg(esp32h2)]
- {
- parl_io.bind_parl_io_tx_interrupt(interrupt_handler.handler());
- unwrap!(crate::interrupt::enable(
- Interrupt::PARL_IO_TX,
- interrupt_handler.priority()
- ));
- }
-
Self {}
}
}
@@ -1733,73 +1795,51 @@ pub mod asynch {
cx: &mut core::task::Context<'_>,
) -> Poll {
TX_WAKER.register(cx.waker());
- if Instance::is_listening_tx_done() {
- Poll::Pending
- } else {
+ if Instance::is_tx_done_set() {
Poll::Ready(())
+ } else {
+ Instance::listen_tx_done();
+ Poll::Pending
}
}
}
+ impl Drop for TxDoneFuture {
+ fn drop(&mut self) {
+ Instance::unlisten_tx_done();
+ }
+ }
+
#[handler]
- fn interrupt_handler() {
+ pub(super) fn interrupt_handler() {
if Instance::is_tx_done_set() {
- Instance::clear_is_tx_done();
Instance::unlisten_tx_done();
TX_WAKER.wake()
}
}
- impl ParlIoTx<'_, crate::Async> {
- /// Perform a DMA write.
- ///
- /// The maximum amount of data to be sent is 32736 bytes.
- pub async fn write_dma_async(&mut self, words: &TXBUF) -> Result<(), Error>
- where
- TXBUF: ReadBuffer,
- {
- let (ptr, len) = unsafe { words.read_buffer() };
-
- if len > MAX_DMA_SIZE {
- return Err(Error::MaxDmaTransferSizeExceeded);
- }
-
+ impl ParlIoTxTransfer<'_, BUF, crate::Async> {
+ /// Waits for [Self::is_done] to return true.
+ pub async fn wait_for_done(&mut self) {
let future = TxDoneFuture::new();
- self.start_write_bytes_dma(ptr, len)?;
future.await;
-
- Ok(())
}
}
- impl ParlIoRx<'_, crate::Async> {
- /// Perform a DMA write.
- ///
- /// The maximum amount of data to be sent is 32736 bytes.
- pub async fn read_dma_async<'t, RXBUF>(
- &'t mut self,
- words: &'t mut RXBUF,
- ) -> Result<(), Error>
- where
- RXBUF: WriteBuffer,
- {
- let (ptr, len) = unsafe { words.write_buffer() };
-
- if !Instance::is_suc_eof_generated_externally() && len > MAX_DMA_SIZE {
- return Err(Error::MaxDmaTransferSizeExceeded);
+ impl ParlIoRxTransfer<'_, BUF, crate::Async> {
+ /// Waits for [Self::is_done] to return true.
+ pub async fn wait_for_done(&mut self) {
+ if self.dma_result.is_some() {
+ return;
}
-
- let future = DmaRxFuture::new(&mut self.rx_channel);
- Self::start_receive_bytes_dma(future.rx, &mut self.rx_chain, ptr, len)?;
- future.await?;
-
- Ok(())
+ let future = DmaRxFuture::new(&mut self.parl_io.rx_channel);
+ self.dma_result = Some(future.await);
}
}
}
mod private {
- use super::{BitPackOrder, EofMode, Error, SampleEdge};
+ use super::{BitPackOrder, Error, SampleEdge};
use crate::peripherals::PARL_IO;
pub trait FullDuplex {}
@@ -1847,6 +1887,14 @@ mod private {
InternalSoftwareEnable = 2,
}
+ /// Generation of GDMA SUC EOF
+ pub(super) enum EofMode {
+ /// Generate GDMA SUC EOF by data byte length
+ ByteLen,
+ /// Generate GDMA SUC EOF by the external enable signal
+ EnableSignal,
+ }
+
pub(super) struct Instance;
#[cfg(esp32c6)]
@@ -1976,7 +2024,7 @@ mod private {
reg_block
.rx_cfg0()
- .modify(|_, w| w.rx_eof_gen_sel().bit(mode == EofMode::EnableSignal));
+ .modify(|_, w| w.rx_eof_gen_sel().bit(matches!(mode, EofMode::EnableSignal)));
}
pub fn set_rx_pulse_submode_sel(sel: u8) {
@@ -2205,7 +2253,7 @@ mod private {
reg_block
.rx_genrl_cfg()
- .modify(|_, w| w.rx_eof_gen_sel().bit(mode == EofMode::EnableSignal));
+ .modify(|_, w| w.rx_eof_gen_sel().bit(matches!(mode, EofMode::EnableSignal)));
}
pub fn set_rx_pulse_submode_sel(sel: u8) {
diff --git a/hil-test/tests/parl_io_tx.rs b/hil-test/tests/parl_io_tx.rs
index e361f5dce5..4d4db42ccc 100644
--- a/hil-test/tests/parl_io_tx.rs
+++ b/hil-test/tests/parl_io_tx.rs
@@ -9,7 +9,8 @@
#[cfg(esp32c6)]
use esp_hal::parl_io::{TxPinConfigWithValidPin, TxSixteenBits};
use esp_hal::{
- dma::DmaChannel0,
+ dma::{DmaChannel0, DmaTxBuf},
+ dma_tx_buffer,
gpio::{
interconnect::{InputSignal, OutputSignal},
NoPin,
@@ -78,8 +79,8 @@ mod tests {
#[test]
fn test_parl_io_tx_16bit_valid_clock_count(ctx: Context) {
const BUFFER_SIZE: usize = 64;
- let tx_buffer = [0u16; BUFFER_SIZE];
- let (_, tx_descriptors) = esp_hal::dma_descriptors!(0, 2 * BUFFER_SIZE);
+
+ let mut dma_tx_buf: DmaTxBuf = dma_tx_buffer!(2 * BUFFER_SIZE).unwrap();
let pins = TxSixteenBits::new(
NoPin, NoPin, NoPin, NoPin, NoPin, NoPin, NoPin, NoPin, NoPin, NoPin, NoPin, NoPin,
@@ -88,8 +89,7 @@ mod tests {
let mut pins = TxPinConfigIncludingValidPin::new(pins);
let mut clock_pin = ClkOutPin::new(ctx.clock);
- let pio =
- ParlIoTxOnly::new(ctx.parl_io, ctx.dma_channel, tx_descriptors, 10.MHz()).unwrap();
+ let pio = ParlIoTxOnly::new(ctx.parl_io, ctx.dma_channel, 10.MHz()).unwrap();
let mut pio = pio
.tx
@@ -102,7 +102,7 @@ mod tests {
)
.unwrap(); // TODO: handle error
- // use a PCNT unit to count the negitive clock edges only when valid is high
+ // use a PCNT unit to count the negative clock edges only when valid is high
let clock_unit = ctx.pcnt_unit;
clock_unit.channel0.set_edge_signal(ctx.clock_loopback);
clock_unit.channel0.set_ctrl_signal(ctx.valid_loopback);
@@ -115,8 +115,8 @@ mod tests {
for _ in 0..100 {
clock_unit.clear();
- let xfer = pio.write_dma(&tx_buffer).unwrap();
- xfer.wait().unwrap();
+ let xfer = pio.write(dma_tx_buf.len(), dma_tx_buf).unwrap();
+ (_, pio, dma_tx_buf) = xfer.wait();
info!("clock count: {}", clock_unit.value());
assert_eq!(clock_unit.value(), BUFFER_SIZE as _);
}
@@ -125,8 +125,7 @@ mod tests {
#[test]
fn test_parl_io_tx_8bit_valid_clock_count(ctx: Context) {
const BUFFER_SIZE: usize = 64;
- let tx_buffer = [0u8; BUFFER_SIZE];
- let (_, tx_descriptors) = esp_hal::dma_descriptors!(0, 2 * BUFFER_SIZE);
+ let mut dma_tx_buf: DmaTxBuf = dma_tx_buffer!(BUFFER_SIZE).unwrap();
let pins = TxEightBits::new(
NoPin,
@@ -149,8 +148,7 @@ mod tests {
let mut clock_pin = ClkOutPin::new(ctx.clock);
- let pio =
- ParlIoTxOnly::new(ctx.parl_io, ctx.dma_channel, tx_descriptors, 10.MHz()).unwrap();
+ let pio = ParlIoTxOnly::new(ctx.parl_io, ctx.dma_channel, 10.MHz()).unwrap();
let mut pio = pio
.tx
@@ -176,8 +174,8 @@ mod tests {
for _ in 0..100 {
clock_unit.clear();
- let xfer = pio.write_dma(&tx_buffer).unwrap();
- xfer.wait().unwrap();
+ let xfer = pio.write(dma_tx_buf.len(), dma_tx_buf).unwrap();
+ (_, pio, dma_tx_buf) = xfer.wait();
info!("clock count: {}", clock_unit.value());
assert_eq!(clock_unit.value(), BUFFER_SIZE as _);
}
diff --git a/hil-test/tests/parl_io_tx_async.rs b/hil-test/tests/parl_io_tx_async.rs
index ecde95ae75..d60b2bd1e3 100644
--- a/hil-test/tests/parl_io_tx_async.rs
+++ b/hil-test/tests/parl_io_tx_async.rs
@@ -9,7 +9,8 @@
#[cfg(esp32c6)]
use esp_hal::parl_io::{TxPinConfigWithValidPin, TxSixteenBits};
use esp_hal::{
- dma::DmaChannel0,
+ dma::{DmaChannel0, DmaTxBuf},
+ dma_tx_buffer,
gpio::{
interconnect::{InputSignal, OutputSignal},
NoPin,
@@ -78,8 +79,7 @@ mod tests {
#[test]
async fn test_parl_io_tx_async_16bit_valid_clock_count(ctx: Context) {
const BUFFER_SIZE: usize = 64;
- let tx_buffer = [0u16; BUFFER_SIZE];
- let (_, tx_descriptors) = esp_hal::dma_descriptors!(0, 2 * BUFFER_SIZE);
+ let mut dma_tx_buf: DmaTxBuf = dma_tx_buffer!(2 * BUFFER_SIZE).unwrap();
let pins = TxSixteenBits::new(
NoPin, NoPin, NoPin, NoPin, NoPin, NoPin, NoPin, NoPin, NoPin, NoPin, NoPin, NoPin,
@@ -88,7 +88,7 @@ mod tests {
let mut pins = TxPinConfigIncludingValidPin::new(pins);
let mut clock_pin = ClkOutPin::new(ctx.clock);
- let pio = ParlIoTxOnly::new(ctx.parl_io, ctx.dma_channel, tx_descriptors, 10.MHz())
+ let pio = ParlIoTxOnly::new(ctx.parl_io, ctx.dma_channel, 10.MHz())
.unwrap()
.into_async();
@@ -116,7 +116,9 @@ mod tests {
for _ in 0..100 {
clock_unit.clear();
- pio.write_dma_async(&tx_buffer).await.unwrap();
+ let mut xfer = pio.write(dma_tx_buf.len(), dma_tx_buf).unwrap();
+ xfer.wait_for_done().await;
+ (_, pio, dma_tx_buf) = xfer.wait();
info!("clock count: {}", clock_unit.value());
assert_eq!(clock_unit.value(), BUFFER_SIZE as _);
}
@@ -125,8 +127,8 @@ mod tests {
#[test]
async fn test_parl_io_tx_async_8bit_valid_clock_count(ctx: Context) {
const BUFFER_SIZE: usize = 64;
- let tx_buffer = [0u8; BUFFER_SIZE];
- let (_, tx_descriptors) = esp_hal::dma_descriptors!(0, 2 * BUFFER_SIZE);
+
+ let mut dma_tx_buf: DmaTxBuf = dma_tx_buffer!(BUFFER_SIZE).unwrap();
let pins = TxEightBits::new(
NoPin,
@@ -149,7 +151,7 @@ mod tests {
let mut clock_pin = ClkOutPin::new(ctx.clock);
- let pio = ParlIoTxOnly::new(ctx.parl_io, ctx.dma_channel, tx_descriptors, 10.MHz())
+ let pio = ParlIoTxOnly::new(ctx.parl_io, ctx.dma_channel, 10.MHz())
.unwrap()
.into_async();
@@ -178,7 +180,9 @@ mod tests {
for _ in 0..100 {
clock_unit.clear();
- pio.write_dma_async(&tx_buffer).await.unwrap();
+ let mut xfer = pio.write(dma_tx_buf.len(), dma_tx_buf).unwrap();
+ xfer.wait_for_done().await;
+ (_, pio, dma_tx_buf) = xfer.wait();
info!("clock count: {}", clock_unit.value());
assert_eq!(clock_unit.value(), BUFFER_SIZE as _);
}