Skip to content

Commit e98674e

Browse files
authored
[DMA 6/8] More helper types & working split (#2532)
* Add helper traits to simplify DMA channel trait bounds * Document changes * Update doc example * Remove clutter from MG * Move DmaEligible down to place helper types closer * Rename * Include split in the MG
1 parent c1f0c13 commit e98674e

File tree

14 files changed

+270
-98
lines changed

14 files changed

+270
-98
lines changed

esp-hal/CHANGELOG.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
1212
- ESP32-S3: Added SDMMC signals (#2556)
1313
- Added `set_priority` to the `DmaChannel` trait on GDMA devices (#2403, #2526)
1414
- Added `into_async` and `into_blocking` functions for `ParlIoTxOnly`, `ParlIoRxOnly` (#2526)
15-
- ESP32-C6, H2, S3: Added `split` function to the `DmaChannel` trait. (#2526)
15+
- ESP32-C6, H2, S3: Added `split` function to the `DmaChannel` trait. (#2526, #2532)
16+
- DMA: `PeripheralDmaChannel` type aliasses and `DmaChannelFor` traits to improve usability. (#2532)
1617

1718
### Changed
1819

esp-hal/MIGRATING-0.22.md

Lines changed: 73 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
# Migration Guide from 0.22.x to v1.0.0-beta.0
22

3-
## DMA configuration changes
3+
## DMA changes
4+
5+
### Configuration changes
46

57
- `configure_for_async` and `configure` have been removed
68
- PDMA devices (ESP32, ESP32-S2) provide no configurability
@@ -27,6 +29,76 @@
2729
+.with_dma(dma_channel);
2830
```
2931

32+
### Usability changes affecting applications
33+
34+
Individual channels are no longer wrapped in `Channel`, but they implement the `DmaChannel` trait.
35+
This means that if you want to split them into an `rx` and a `tx` half (which is only supported on
36+
the H2, C6 and S3 currently), you can't move out of the channel but instead you need to call
37+
the `split` method.
38+
39+
```diff
40+
-let tx = channel.tx;
41+
+use esp_hal::dma::DmaChannel;
42+
+let (rx, tx) = channel.split();
43+
```
44+
45+
The `Channel` types remain available for use in peripheral drivers.
46+
47+
It is now simpler to work with DMA channels in generic contexts. esp-hal now provides convenience
48+
traits and type aliasses to specify peripheral compatibility. The `ChannelCreator` types have been
49+
removed, further simplifying use.
50+
51+
For example, previously you may have needed to write something like this to accept a DMA channel
52+
in a generic function:
53+
54+
```rust
55+
fn new_foo<'d, T>(
56+
dma_channel: ChannelCreator<2>, // It wasn't possible to accept a generic ChannelCreator.
57+
peripheral: impl Peripheral<P = T> + 'd,
58+
)
59+
where
60+
T: SomePeripheralInstance,
61+
ChannelCreator<2>: DmaChannelConvert<<T as DmaEligible>::Dma>,
62+
{
63+
let dma_channel = dma_channel.configure_for_async(false, DmaPriority::Priority0);
64+
65+
let driver = PeripheralDriver::new(peripheral, config).with_dma(dma_channel);
66+
67+
// ...
68+
}
69+
```
70+
71+
From now on a similar, but more flexible implementation may look like:
72+
73+
```rust
74+
fn new_foo<'d, T, CH>(
75+
dma_channel: impl Peripheral<P = CH> + 'd,
76+
peripheral: impl Peripheral<P = T> + 'd,
77+
)
78+
where
79+
T: SomePeripheralInstance,
80+
CH: DmaChannelFor<T>,
81+
{
82+
// Optionally: dma_channel.set_priority(DmaPriority::Priority2);
83+
84+
let driver = PeripheralDriver::new(peripheral, config).with_dma(dma_channel);
85+
86+
// ...
87+
}
88+
```
89+
90+
### Usability changes affecting third party peripheral drivers
91+
92+
If you are writing a driver and need to store a channel in a structure, you can use one of the
93+
`ChannelFor` type aliasses.
94+
95+
```diff
96+
struct Aes<'d> {
97+
- channel: ChannelTx<'d, Blocking, <AES as DmaEligible>::Dma>,
98+
+ channel: ChannelTx<'d, Blocking, PeripheralTxChannel<AES>>,
99+
}
100+
```
101+
30102
## Timer changes
31103

32104
The low level timers, `SystemTimer` and `TimerGroup` are now "dumb". They contain no logic for operating modes or trait implementations (except the low level `Timer` trait).

esp-hal/src/aes/mod.rs

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -241,16 +241,16 @@ pub mod dma {
241241
ChannelRx,
242242
ChannelTx,
243243
DescriptorChain,
244-
DmaChannelConvert,
245244
DmaChannelFor,
246245
DmaDescriptor,
247246
DmaPeripheral,
248247
DmaTransferRxTx,
248+
PeripheralDmaChannel,
249+
PeripheralRxChannel,
250+
PeripheralTxChannel,
249251
ReadBuffer,
250252
Rx,
251-
RxChannelFor,
252253
Tx,
253-
TxChannelFor,
254254
WriteBuffer,
255255
},
256256
peripheral::Peripheral,
@@ -281,7 +281,7 @@ pub mod dma {
281281
/// The underlying [`Aes`](super::Aes) driver
282282
pub aes: super::Aes<'d>,
283283

284-
channel: Channel<'d, Blocking, DmaChannelFor<AES>>,
284+
channel: Channel<'d, Blocking, PeripheralDmaChannel<AES>>,
285285
rx_chain: DescriptorChain,
286286
tx_chain: DescriptorChain,
287287
}
@@ -295,7 +295,7 @@ pub mod dma {
295295
tx_descriptors: &'static mut [DmaDescriptor],
296296
) -> AesDma<'d>
297297
where
298-
CH: DmaChannelConvert<DmaChannelFor<AES>>,
298+
CH: DmaChannelFor<AES>,
299299
{
300300
let channel = Channel::new(channel.map(|ch| ch.degrade()));
301301
channel.runtime_ensure_compatible(&self.aes);
@@ -331,7 +331,7 @@ pub mod dma {
331331
}
332332

333333
impl<'d> DmaSupportTx for AesDma<'d> {
334-
type TX = ChannelTx<'d, Blocking, TxChannelFor<AES>>;
334+
type TX = ChannelTx<'d, Blocking, PeripheralTxChannel<AES>>;
335335

336336
fn tx(&mut self) -> &mut Self::TX {
337337
&mut self.channel.tx
@@ -343,7 +343,7 @@ pub mod dma {
343343
}
344344

345345
impl<'d> DmaSupportRx for AesDma<'d> {
346-
type RX = ChannelRx<'d, Blocking, RxChannelFor<AES>>;
346+
type RX = ChannelRx<'d, Blocking, PeripheralRxChannel<AES>>;
347347

348348
fn rx(&mut self) -> &mut Self::RX {
349349
&mut self.channel.rx

esp-hal/src/dma/gdma.rs

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,12 @@ impl Peripheral for AnyGdmaRxChannel {
6060
}
6161
}
6262

63+
impl DmaChannelConvert<AnyGdmaRxChannel> for AnyGdmaRxChannel {
64+
fn degrade(self) -> AnyGdmaRxChannel {
65+
self
66+
}
67+
}
68+
6369
/// An arbitrary GDMA TX channel
6470
pub struct AnyGdmaTxChannel(u8);
6571

@@ -71,6 +77,12 @@ impl Peripheral for AnyGdmaTxChannel {
7177
}
7278
}
7379

80+
impl DmaChannelConvert<AnyGdmaTxChannel> for AnyGdmaTxChannel {
81+
fn degrade(self) -> AnyGdmaTxChannel {
82+
self
83+
}
84+
}
85+
7486
use embassy_sync::waitqueue::AtomicWaker;
7587

7688
static TX_WAKERS: [AtomicWaker; CHANNEL_COUNT] = [const { AtomicWaker::new() }; CHANNEL_COUNT];

esp-hal/src/dma/mod.rs

Lines changed: 121 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -944,38 +944,6 @@ pub trait DmaEligible {
944944
fn dma_peripheral(&self) -> DmaPeripheral;
945945
}
946946

947-
/// Helper type to get the DMA (Rx and Tx) channel for a peripheral.
948-
pub type DmaChannelFor<T> = <T as DmaEligible>::Dma;
949-
/// Helper type to get the DMA Rx channel for a peripheral.
950-
pub type RxChannelFor<T> = <DmaChannelFor<T> as DmaChannel>::Rx;
951-
/// Helper type to get the DMA Tx channel for a peripheral.
952-
pub type TxChannelFor<T> = <DmaChannelFor<T> as DmaChannel>::Tx;
953-
954-
#[doc(hidden)]
955-
#[macro_export]
956-
macro_rules! impl_dma_eligible {
957-
([$dma_ch:ident] $name:ident => $dma:ident) => {
958-
impl $crate::dma::DmaEligible for $crate::peripherals::$name {
959-
type Dma = $dma_ch;
960-
961-
fn dma_peripheral(&self) -> $crate::dma::DmaPeripheral {
962-
$crate::dma::DmaPeripheral::$dma
963-
}
964-
}
965-
};
966-
967-
(
968-
$dma_ch:ident {
969-
$($(#[$cfg:meta])? $name:ident => $dma:ident,)*
970-
}
971-
) => {
972-
$(
973-
$(#[$cfg])?
974-
$crate::impl_dma_eligible!([$dma_ch] $name => $dma);
975-
)*
976-
};
977-
}
978-
979947
#[doc(hidden)]
980948
#[derive(Debug)]
981949
pub struct DescriptorChain {
@@ -1593,6 +1561,38 @@ impl RxCircularState {
15931561
}
15941562
}
15951563

1564+
#[doc(hidden)]
1565+
#[macro_export]
1566+
macro_rules! impl_dma_eligible {
1567+
([$dma_ch:ident] $name:ident => $dma:ident) => {
1568+
impl $crate::dma::DmaEligible for $crate::peripherals::$name {
1569+
type Dma = $dma_ch;
1570+
1571+
fn dma_peripheral(&self) -> $crate::dma::DmaPeripheral {
1572+
$crate::dma::DmaPeripheral::$dma
1573+
}
1574+
}
1575+
};
1576+
1577+
(
1578+
$dma_ch:ident {
1579+
$($(#[$cfg:meta])? $name:ident => $dma:ident,)*
1580+
}
1581+
) => {
1582+
$(
1583+
$(#[$cfg])?
1584+
$crate::impl_dma_eligible!([$dma_ch] $name => $dma);
1585+
)*
1586+
};
1587+
}
1588+
1589+
/// Helper type to get the DMA (Rx and Tx) channel for a peripheral.
1590+
pub type PeripheralDmaChannel<T> = <T as DmaEligible>::Dma;
1591+
/// Helper type to get the DMA Rx channel for a peripheral.
1592+
pub type PeripheralRxChannel<T> = <PeripheralDmaChannel<T> as DmaChannel>::Rx;
1593+
/// Helper type to get the DMA Tx channel for a peripheral.
1594+
pub type PeripheralTxChannel<T> = <PeripheralDmaChannel<T> as DmaChannel>::Tx;
1595+
15961596
#[doc(hidden)]
15971597
pub trait DmaRxChannel:
15981598
RxRegisterAccess + InterruptAccess<DmaRxInterrupt> + Peripheral<P = Self>
@@ -1647,7 +1647,7 @@ pub trait DmaChannelExt: DmaChannel {
16471647
note = "Not all channels are useable with all peripherals"
16481648
)]
16491649
#[doc(hidden)]
1650-
pub trait DmaChannelConvert<DEG>: DmaChannel {
1650+
pub trait DmaChannelConvert<DEG> {
16511651
fn degrade(self) -> DEG;
16521652
}
16531653

@@ -1657,6 +1657,94 @@ impl<DEG: DmaChannel> DmaChannelConvert<DEG> for DEG {
16571657
}
16581658
}
16591659

1660+
/// Trait implemented for DMA channels that are compatible with a particular
1661+
/// peripheral.
1662+
///
1663+
/// You can use this in places where a peripheral driver would expect a
1664+
/// `DmaChannel` implementation.
1665+
#[cfg_attr(pdma, doc = "")]
1666+
#[cfg_attr(
1667+
pdma,
1668+
doc = "Note that using mismatching channels (e.g. trying to use `spi2channel` with SPI3) may compile, but will panic in runtime."
1669+
)]
1670+
#[cfg_attr(pdma, doc = "")]
1671+
/// ## Example
1672+
///
1673+
/// The following example demonstrates how this trait can be used to only accept
1674+
/// types compatible with a specific peripheral.
1675+
///
1676+
/// ```rust,no_run
1677+
#[doc = crate::before_snippet!()]
1678+
/// use esp_hal::spi::master::{Spi, SpiDma, Config, Instance as SpiInstance};
1679+
/// use esp_hal::dma::DmaChannelFor;
1680+
/// use esp_hal::peripheral::Peripheral;
1681+
/// use esp_hal::Blocking;
1682+
/// use esp_hal::dma::Dma;
1683+
///
1684+
/// fn configures_spi_dma<'d, S, CH>(
1685+
/// spi: Spi<'d, Blocking, S>,
1686+
/// channel: impl Peripheral<P = CH> + 'd,
1687+
/// ) -> SpiDma<'d, Blocking, S>
1688+
/// where
1689+
/// S: SpiInstance,
1690+
/// CH: DmaChannelFor<S> + 'd,
1691+
/// {
1692+
/// spi.with_dma(channel)
1693+
/// }
1694+
///
1695+
/// let dma = Dma::new(peripherals.DMA);
1696+
#[cfg_attr(pdma, doc = "let dma_channel = dma.spi2channel;")]
1697+
#[cfg_attr(gdma, doc = "let dma_channel = dma.channel0;")]
1698+
#[doc = ""]
1699+
/// let spi = Spi::new_with_config(
1700+
/// peripherals.SPI2,
1701+
/// Config::default(),
1702+
/// );
1703+
///
1704+
/// let spi_dma = configures_spi_dma(spi, dma_channel);
1705+
/// # }
1706+
/// ```
1707+
pub trait DmaChannelFor<P: DmaEligible>:
1708+
DmaChannel + DmaChannelConvert<PeripheralDmaChannel<P>>
1709+
{
1710+
}
1711+
impl<P, CH> DmaChannelFor<P> for CH
1712+
where
1713+
P: DmaEligible,
1714+
CH: DmaChannel + DmaChannelConvert<PeripheralDmaChannel<P>>,
1715+
{
1716+
}
1717+
1718+
/// Trait implemented for the RX half of split DMA channels that are compatible
1719+
/// with a particular peripheral. Accepts complete DMA channels or split halves.
1720+
///
1721+
/// This trait is similar in use to [`DmaChannelFor`].
1722+
///
1723+
/// You can use this in places where a peripheral driver would expect a
1724+
/// `DmaRxChannel` implementation.
1725+
pub trait RxChannelFor<P: DmaEligible>: DmaChannelConvert<PeripheralRxChannel<P>> {}
1726+
impl<P, RX> RxChannelFor<P> for RX
1727+
where
1728+
P: DmaEligible,
1729+
RX: DmaChannelConvert<PeripheralRxChannel<P>>,
1730+
{
1731+
}
1732+
1733+
/// Trait implemented for the TX half of split DMA channels that are compatible
1734+
/// with a particular peripheral. Accepts complete DMA channels or split halves.
1735+
///
1736+
/// This trait is similar in use to [`DmaChannelFor`].
1737+
///
1738+
/// You can use this in places where a peripheral driver would expect a
1739+
/// `DmaTxChannel` implementation.
1740+
pub trait TxChannelFor<PER: DmaEligible>: DmaChannelConvert<PeripheralTxChannel<PER>> {}
1741+
impl<P, TX> TxChannelFor<P> for TX
1742+
where
1743+
P: DmaEligible,
1744+
TX: DmaChannelConvert<PeripheralTxChannel<P>>,
1745+
{
1746+
}
1747+
16601748
/// The functions here are not meant to be used outside the HAL
16611749
#[doc(hidden)]
16621750
pub trait Rx: crate::private::Sealed {

0 commit comments

Comments
 (0)