Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[DMA 1/N] Add mode to DMA channel drivers #2519

Merged
merged 3 commits into from
Nov 14, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions esp-hal/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- `I2c` SCL timeout is now defined in bus clock cycles. (#2477)
- Trying to send a single-shot RMT transmission will result in an error now, `RMT` deals with `u32` now, `PulseCode` is a convenience trait now (#2463)
- Removed `get_` prefixes from functions (#2528)
- The `Camera` and `I8080` drivers' constructors now only accepts blocking-mode DMA channels. (#2519)

### Fixed

Expand Down Expand Up @@ -180,6 +181,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- The DMA channel types have been removed from peripherals (#2261)
- `I2C` driver renamed to `I2c` (#2320)
- The GPIO pins are now accessible via `Peripherals` and are no longer part of the `Io` struct (#2508)
- `dma::{ChannelRx, ChannelTx}` now have a `Mode` type parameter (#2519)

### Fixed

Expand Down
47 changes: 45 additions & 2 deletions esp-hal/MIGRATING-0.21.md
Original file line number Diff line number Diff line change
Expand Up @@ -276,7 +276,9 @@ For example:
}
```

## Circular DMA transfer's `available` returns `Result<usize, DmaError>` now
## DMA related changes

### Circular DMA transfer's `available` returns `Result<usize, DmaError>` now

In case of any error you should drop the transfer and restart it.

Expand All @@ -293,6 +295,22 @@ In case of any error you should drop the transfer and restart it.
+ };
```

### Channel, ChannelRx and ChannelTx types have changed

- `Channel`'s `Async`/`Blocking` mode has been moved before the channel instance parameter.
- `ChannelRx` and `ChannelTx` have gained a new `Async`/`Blocking` mode parameter.

```diff
-Channel<'d, DmaChannel0, Async>
+Channel<'d, Async, DmaChannel0>

-ChannelRx<'d, DmaChannel0>
+ChannelRx<'d, Async, DmaChannel0>

-ChannelTx<'d, DmaChannel0>
+ChannelTx<'d, Async, DmaChannel0>
```

## Removed `peripheral_input` and `into_peripheral_output` from GPIO pin types

Creating peripheral interconnect signals now consume the GPIO pin used for the connection.
Expand Down Expand Up @@ -357,7 +375,9 @@ refer to the `Config` struct as `uart::Config`.
+)
```

## I8080 driver split `set_byte_order()` into `set_8bits_order()` and `set_byte_order()`.
## LCD_CAM changes

### I8080 driver split `set_byte_order()` into `set_8bits_order()` and `set_byte_order()`.

If you were using an 8-bit bus.

Expand All @@ -371,6 +391,29 @@ If you were using an 16-bit bus, you don't need to change anything, `set_byte_or
If you were sharing the bus between an 8-bit and 16-bit device, you will have to call the corresponding method when
you switch between devices. Be sure to read the documentation of the new methods.

### Mixed mode constructors

It is no longer possible to construct `I8080` or `Camera` with an async-mode DMA channel.
Convert the DMA channel into blocking before passing it to these constructors.

```diff
let lcd_cam = LcdCam::new(peripherals.LCD_CAM);
let channel = ctx
.dma
.channel0
- .configure(false, DmaPriority::Priority0)
- .into_async();
+ .configure(false, DmaPriority::Priority0);

let i8080 = I8080::new(
lcd_cam.lcd,
channel.tx,
pins,
20.MHz(),
Config::default(),
);
```

## `rmt::Channel::transmit` now returns `Result`, `PulseCode` is now `u32`

When trying to send a one-shot transmission will fail if it doesn't end with an end-marker.
Expand Down
12 changes: 6 additions & 6 deletions esp-hal/src/aes/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -276,21 +276,21 @@ pub mod dma {
/// The underlying [`Aes`](super::Aes) driver
pub aes: super::Aes<'d>,

channel: Channel<'d, <AES as DmaEligible>::Dma, Blocking>,
channel: Channel<'d, Blocking, <AES as DmaEligible>::Dma>,
rx_chain: DescriptorChain,
tx_chain: DescriptorChain,
}

impl<'d> crate::aes::Aes<'d> {
/// Enable DMA for the current instance of the AES driver
pub fn with_dma<C>(
pub fn with_dma<CH>(
self,
channel: Channel<'d, C, Blocking>,
channel: Channel<'d, Blocking, CH>,
rx_descriptors: &'static mut [DmaDescriptor],
tx_descriptors: &'static mut [DmaDescriptor],
) -> AesDma<'d>
where
C: DmaChannelConvert<<AES as DmaEligible>::Dma>,
CH: DmaChannelConvert<<AES as DmaEligible>::Dma>,
{
AesDma {
aes: self,
Expand Down Expand Up @@ -324,7 +324,7 @@ pub mod dma {
}

impl<'d> DmaSupportTx for AesDma<'d> {
type TX = ChannelTx<'d, <AES as DmaEligible>::Dma>;
type TX = ChannelTx<'d, Blocking, <AES as DmaEligible>::Dma>;

fn tx(&mut self) -> &mut Self::TX {
&mut self.channel.tx
Expand All @@ -336,7 +336,7 @@ pub mod dma {
}

impl<'d> DmaSupportRx for AesDma<'d> {
type RX = ChannelRx<'d, <AES as DmaEligible>::Dma>;
type RX = ChannelRx<'d, Blocking, <AES as DmaEligible>::Dma>;

fn rx(&mut self) -> &mut Self::RX {
&mut self.channel.rx
Expand Down
147 changes: 97 additions & 50 deletions esp-hal/src/dma/gdma.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,48 +25,78 @@ use crate::{
#[doc(hidden)]
pub trait GdmaChannel {
fn number(&self) -> u8;
}

/// An arbitrary GDMA channel
#[non_exhaustive]
pub struct AnyGdmaChannel(u8);
fn async_handler_out(&self) -> Option<InterruptHandler> {
match self.number() {
0 => DmaChannel0::handler_out(),
#[cfg(not(esp32c2))]
1 => DmaChannel1::handler_out(),
#[cfg(not(esp32c2))]
2 => DmaChannel2::handler_out(),
#[cfg(esp32s3)]
3 => DmaChannel3::handler_out(),
#[cfg(esp32s3)]
4 => DmaChannel4::handler_out(),
_ => unreachable!(),
}
}

impl crate::private::Sealed for AnyGdmaChannel {}
impl DmaChannel for AnyGdmaChannel {
type Rx = ChannelRxImpl<Self>;
type Tx = ChannelTxImpl<Self>;
fn peripheral_interrupt_out(&self) -> Option<Interrupt> {
match self.number() {
0 => DmaChannel0::isr_out(),
#[cfg(not(esp32c2))]
1 => DmaChannel1::isr_out(),
#[cfg(not(esp32c2))]
2 => DmaChannel2::isr_out(),
#[cfg(esp32s3)]
3 => DmaChannel3::isr_out(),
#[cfg(esp32s3)]
4 => DmaChannel4::isr_out(),
_ => unreachable!(),
}
}

fn async_handler<M: Mode>(ch: &Channel<'_, Self, M>) -> InterruptHandler {
match ch.tx.tx_impl.0.number() {
0 => DmaChannel0::handler(),
fn async_handler_in(&self) -> Option<InterruptHandler> {
match self.number() {
0 => DmaChannel0::handler_in(),
#[cfg(not(esp32c2))]
1 => DmaChannel1::handler(),
1 => DmaChannel1::handler_in(),
#[cfg(not(esp32c2))]
2 => DmaChannel2::handler(),
2 => DmaChannel2::handler_in(),
#[cfg(esp32s3)]
3 => DmaChannel3::handler(),
3 => DmaChannel3::handler_in(),
#[cfg(esp32s3)]
4 => DmaChannel4::handler(),
4 => DmaChannel4::handler_in(),
_ => unreachable!(),
}
}

fn interrupts<M: Mode>(ch: &Channel<'_, Self, M>) -> &'static [Interrupt] {
match ch.tx.tx_impl.0.number() {
0 => DmaChannel0::isrs(),
fn peripheral_interrupt_in(&self) -> Option<Interrupt> {
match self.number() {
0 => DmaChannel0::isr_in(),
#[cfg(not(esp32c2))]
1 => DmaChannel1::isrs(),
1 => DmaChannel1::isr_in(),
#[cfg(not(esp32c2))]
2 => DmaChannel2::isrs(),
2 => DmaChannel2::isr_in(),
#[cfg(esp32s3)]
3 => DmaChannel3::isrs(),
3 => DmaChannel3::isr_in(),
#[cfg(esp32s3)]
4 => DmaChannel4::isrs(),
4 => DmaChannel4::isr_in(),
_ => unreachable!(),
}
}
}

/// An arbitrary GDMA channel
#[non_exhaustive]
pub struct AnyGdmaChannel(u8);

impl crate::private::Sealed for AnyGdmaChannel {}
impl DmaChannel for AnyGdmaChannel {
type Rx = ChannelRxImpl<Self>;
type Tx = ChannelTxImpl<Self>;
}

#[non_exhaustive]
#[doc(hidden)]
pub struct SpecificGdmaChannel<const N: u8> {}
Expand Down Expand Up @@ -202,6 +232,14 @@ impl<C: GdmaChannel> TxRegisterAccess for ChannelTxImpl<C> {
.out_eof_des_addr()
.bits() as _
}

fn async_handler(&self) -> Option<InterruptHandler> {
self.0.async_handler_out()
}

fn peripheral_interrupt(&self) -> Option<Interrupt> {
self.0.peripheral_interrupt_out()
}
}

impl<C: GdmaChannel> InterruptAccess<DmaTxInterrupt> for ChannelTxImpl<C> {
Expand Down Expand Up @@ -385,6 +423,14 @@ impl<C: GdmaChannel> RxRegisterAccess for ChannelRxImpl<C> {
.in_conf0()
.modify(|_, w| w.mem_trans_en().bit(value));
}

fn async_handler(&self) -> Option<InterruptHandler> {
self.0.async_handler_in()
}

fn peripheral_interrupt(&self) -> Option<Interrupt> {
self.0.peripheral_interrupt_in()
}
}

impl<C: GdmaChannel> InterruptAccess<DmaRxInterrupt> for ChannelRxImpl<C> {
Expand Down Expand Up @@ -473,15 +519,15 @@ impl<C: GdmaChannel> InterruptAccess<DmaRxInterrupt> for ChannelRxImpl<C> {
#[non_exhaustive]
pub struct ChannelCreator<const N: u8> {}

impl<CH: DmaChannel, M: Mode> Channel<'_, CH, M> {
impl<CH: DmaChannel, M: Mode> Channel<'_, M, CH> {
/// Asserts that the channel is compatible with the given peripheral.
pub fn runtime_ensure_compatible<P: DmaEligible>(&self, _peripheral: &PeripheralRef<'_, P>) {
// No runtime checks; GDMA channels are compatible with any peripheral
}
}

macro_rules! impl_channel {
($num: literal, $async_handler: path, $($interrupt: ident),* ) => {
($num:literal, $interrupt_in:ident, $async_handler:path $(, $interrupt_out:ident , $async_handler_out:path)? ) => {
paste::paste! {
/// A description of a specific GDMA channel
#[non_exhaustive]
Expand All @@ -490,26 +536,26 @@ macro_rules! impl_channel {
impl crate::private::Sealed for [<DmaChannel $num>] {}

impl [<DmaChannel $num>] {
fn handler() -> InterruptHandler {
$async_handler
fn handler_in() -> Option<InterruptHandler> {
Some($async_handler)
}

fn isr_in() -> Option<Interrupt> {
Some(Interrupt::$interrupt_in)
}

fn handler_out() -> Option<InterruptHandler> {
$crate::if_set! { $(Some($async_handler_out))?, None }
}

fn isrs() -> &'static [Interrupt] {
&[$(Interrupt::$interrupt),*]
fn isr_out() -> Option<Interrupt> {
$crate::if_set! { $(Some(Interrupt::$interrupt_out))?, None }
}
}

impl DmaChannel for [<DmaChannel $num>] {
type Rx = ChannelRxImpl<SpecificGdmaChannel<$num>>;
type Tx = ChannelTxImpl<SpecificGdmaChannel<$num>>;

fn async_handler<M: Mode>(_ch: &Channel<'_, Self, M>) -> InterruptHandler {
Self::handler()
}

fn interrupts<M: Mode>(_ch: &Channel<'_, Self, M>,) -> &'static [Interrupt] {
Self::isrs()
}
}

impl DmaChannelConvert<AnyGdmaChannel> for [<DmaChannel $num>] {
Expand Down Expand Up @@ -537,11 +583,10 @@ macro_rules! impl_channel {
self,
burst_mode: bool,
priority: DmaPriority,
) -> Channel<'a, [<DmaChannel $num>], Blocking> {
) -> Channel<'a, Blocking, [<DmaChannel $num>]> {
let mut this = Channel {
tx: ChannelTx::new(ChannelTxImpl(SpecificGdmaChannel::<$num> {})),
rx: ChannelRx::new(ChannelRxImpl(SpecificGdmaChannel::<$num> {})),
phantom: PhantomData,
};

this.configure(burst_mode, priority);
Expand All @@ -553,27 +598,29 @@ macro_rules! impl_channel {
};
}

use super::asynch::interrupt as asynch_handler;

cfg_if::cfg_if! {
if #[cfg(esp32c2)] {
const CHANNEL_COUNT: usize = 1;
impl_channel!(0, super::asynch::interrupt::interrupt_handler_ch0, DMA_CH0);
impl_channel!(0, DMA_CH0, asynch_handler::interrupt_handler_ch0);
} else if #[cfg(esp32c3)] {
const CHANNEL_COUNT: usize = 3;
impl_channel!(0, super::asynch::interrupt::interrupt_handler_ch0, DMA_CH0);
impl_channel!(1, super::asynch::interrupt::interrupt_handler_ch1, DMA_CH1);
impl_channel!(2, super::asynch::interrupt::interrupt_handler_ch2, DMA_CH2);
impl_channel!(0, DMA_CH0, asynch_handler::interrupt_handler_ch0);
impl_channel!(1, DMA_CH1, asynch_handler::interrupt_handler_ch1);
impl_channel!(2, DMA_CH2, asynch_handler::interrupt_handler_ch2);
} else if #[cfg(any(esp32c6, esp32h2))] {
const CHANNEL_COUNT: usize = 3;
impl_channel!(0, super::asynch::interrupt::interrupt_handler_ch0, DMA_IN_CH0, DMA_OUT_CH0);
impl_channel!(1, super::asynch::interrupt::interrupt_handler_ch1, DMA_IN_CH1, DMA_OUT_CH1);
impl_channel!(2, super::asynch::interrupt::interrupt_handler_ch2, DMA_IN_CH2, DMA_OUT_CH2);
impl_channel!(0, DMA_IN_CH0, asynch_handler::interrupt_handler_ch0, DMA_OUT_CH0, asynch_handler::interrupt_handler_ch0);
impl_channel!(1, DMA_IN_CH1, asynch_handler::interrupt_handler_ch1, DMA_OUT_CH1, asynch_handler::interrupt_handler_ch1);
impl_channel!(2, DMA_IN_CH2, asynch_handler::interrupt_handler_ch2, DMA_OUT_CH2, asynch_handler::interrupt_handler_ch2);
} else if #[cfg(esp32s3)] {
const CHANNEL_COUNT: usize = 5;
impl_channel!(0, super::asynch::interrupt::interrupt_handler_ch0, DMA_IN_CH0, DMA_OUT_CH0);
impl_channel!(1, super::asynch::interrupt::interrupt_handler_ch1, DMA_IN_CH1, DMA_OUT_CH1);
impl_channel!(2, super::asynch::interrupt::interrupt_handler_ch2, DMA_IN_CH2, DMA_OUT_CH2);
impl_channel!(3, super::asynch::interrupt::interrupt_handler_ch3, DMA_IN_CH3, DMA_OUT_CH3);
impl_channel!(4, super::asynch::interrupt::interrupt_handler_ch4, DMA_IN_CH4, DMA_OUT_CH4);
impl_channel!(0, DMA_IN_CH0, asynch_handler::interrupt_handler_ch0, DMA_OUT_CH0, asynch_handler::interrupt_handler_ch0);
impl_channel!(1, DMA_IN_CH1, asynch_handler::interrupt_handler_ch1, DMA_OUT_CH1, asynch_handler::interrupt_handler_ch1);
impl_channel!(2, DMA_IN_CH2, asynch_handler::interrupt_handler_ch2, DMA_OUT_CH2, asynch_handler::interrupt_handler_ch2);
impl_channel!(3, DMA_IN_CH3, asynch_handler::interrupt_handler_ch3, DMA_OUT_CH3, asynch_handler::interrupt_handler_ch3);
impl_channel!(4, DMA_IN_CH4, asynch_handler::interrupt_handler_ch4, DMA_OUT_CH4, asynch_handler::interrupt_handler_ch4);
bugadani marked this conversation as resolved.
Show resolved Hide resolved
}
}

Expand Down
Loading