Skip to content
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
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Add UART support for splitting into TX and RX (#754)
- Async support for I2S (#801)
- UART/ESP32: fix calculating FIFO counter with `get_rx_fifo_count()` (#804)
- Async support for PARL_IO (#807)

### Changed

Expand Down
206 changes: 206 additions & 0 deletions esp-hal-common/src/parl_io.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1412,6 +1412,124 @@ where
}
}

#[cfg(feature = "async")]
pub mod asynch {
use core::task::Poll;

use embassy_sync::waitqueue::AtomicWaker;

use super::{
private::{ConfigurePins, Instance, RxClkPin, RxPins, TxClkPin, TxPins},
Error,
ParlIoRx,
ParlIoTx,
MAX_DMA_SIZE,
};
use crate::{
dma::{asynch::DmaRxDoneChFuture, ChannelTypes, ParlIoPeripheral},
macros::interrupt,
};

static TX_WAKER: AtomicWaker = AtomicWaker::new();

pub struct TxDoneFuture {}

impl TxDoneFuture {
pub fn new() -> Self {
Instance::listen_tx_done();
Self {}
}
}

impl core::future::Future for TxDoneFuture {
type Output = ();

fn poll(
self: core::pin::Pin<&mut Self>,
cx: &mut core::task::Context<'_>,
) -> Poll<Self::Output> {
TX_WAKER.register(cx.waker());
if Instance::is_listening_tx_done() {
Poll::Pending
} else {
Poll::Ready(())
}
}
}

#[cfg(esp32c6)]
#[interrupt]
fn PARL_IO() {
if Instance::is_tx_done_set() {
Instance::clear_is_tx_done();
Instance::unlisten_tx_done();
TX_WAKER.wake()
}
}

#[cfg(esp32h2)]
#[interrupt]
fn PARL_IO_TX() {
if Instance::is_tx_done_set() {
Instance::clear_is_tx_done();
Instance::unlisten_tx_done();
TX_WAKER.wake()
}
}

impl<'d, CH, P, CP> ParlIoTx<'d, CH, P, CP>
where
CH: ChannelTypes,
CH::P: ParlIoPeripheral,
P: TxPins + ConfigurePins,
CP: TxClkPin,
{
/// Perform a DMA write.
///
/// The maximum amount of data to be sent is 32736 bytes.
pub async fn write_dma_async(&mut self, words: &mut [u8]) -> Result<(), Error> {
let (ptr, len) = (words.as_ptr(), words.len());

if len > MAX_DMA_SIZE {
return Err(Error::MaxDmaTransferSizeExceeded);
}

self.start_write_bytes_dma(ptr, len)?;

TxDoneFuture::new().await;

Ok(())
}
}

impl<'d, CH, P, CP> ParlIoRx<'d, CH, P, CP>
where
CH: ChannelTypes,
CH::P: ParlIoPeripheral,
P: RxPins + ConfigurePins,
CP: RxClkPin,
{
/// Perform a DMA write.
///
/// The maximum amount of data to be sent is 32736 bytes.
pub async fn read_dma_async(&mut self, words: &mut [u8]) -> Result<(), Error> {
let (ptr, len) = (words.as_mut_ptr(), words.len());

if !Instance::is_suc_eof_generated_externally() {
if len > MAX_DMA_SIZE {
return Err(Error::MaxDmaTransferSizeExceeded);
}
}

self.start_receive_bytes_dma(ptr, len)?;

DmaRxDoneChFuture::new(&mut self.rx_channel).await;

Ok(())
}
}
}

mod private {
use super::{BitPackOrder, EofMode, Error, SampleEdge};
use crate::dma::ChannelTypes;
Expand Down Expand Up @@ -1705,6 +1823,50 @@ mod private {

reg_block.rx_cfg0.read().rx_eof_gen_sel().bit_is_set()
}

#[cfg(feature = "async")]
pub fn listen_tx_done() {
let reg_block: crate::peripherals::PARL_IO =
unsafe { crate::peripherals::PARL_IO::steal() };

reg_block
.int_ena
.modify(|_, w| w.tx_eof_int_ena().set_bit());
}

#[cfg(feature = "async")]
pub fn unlisten_tx_done() {
let reg_block: crate::peripherals::PARL_IO =
unsafe { crate::peripherals::PARL_IO::steal() };

reg_block
.int_ena
.modify(|_, w| w.tx_eof_int_ena().clear_bit());
}

#[cfg(feature = "async")]
pub fn is_listening_tx_done() -> bool {
let reg_block: crate::peripherals::PARL_IO =
unsafe { crate::peripherals::PARL_IO::steal() };

reg_block.int_ena.read().tx_eof_int_ena().bit()
}

#[cfg(feature = "async")]
pub fn is_tx_done_set() -> bool {
let reg_block: crate::peripherals::PARL_IO =
unsafe { crate::peripherals::PARL_IO::steal() };

reg_block.int_raw.read().tx_eof_int_raw().bit()
}

#[cfg(feature = "async")]
pub fn clear_is_tx_done() {
let reg_block: crate::peripherals::PARL_IO =
unsafe { crate::peripherals::PARL_IO::steal() };

reg_block.int_clr.write(|w| w.tx_eof_int_clr().set_bit());
}
}

#[cfg(esp32h2)]
Expand Down Expand Up @@ -1924,5 +2086,49 @@ mod private {

reg_block.rx_genrl_cfg.read().rx_eof_gen_sel().bit_is_set()
}

#[cfg(feature = "async")]
pub fn listen_tx_done() {
let reg_block: crate::peripherals::PARL_IO =
unsafe { crate::peripherals::PARL_IO::steal() };

reg_block
.int_ena
.modify(|_, w| w.tx_eof_int_ena().set_bit());
}

#[cfg(feature = "async")]
pub fn unlisten_tx_done() {
let reg_block: crate::peripherals::PARL_IO =
unsafe { crate::peripherals::PARL_IO::steal() };

reg_block
.int_ena
.modify(|_, w| w.tx_eof_int_ena().clear_bit());
}

#[cfg(feature = "async")]
pub fn is_listening_tx_done() -> bool {
let reg_block: crate::peripherals::PARL_IO =
unsafe { crate::peripherals::PARL_IO::steal() };

reg_block.int_ena.read().tx_eof_int_ena().bit()
}

#[cfg(feature = "async")]
pub fn is_tx_done_set() -> bool {
let reg_block: crate::peripherals::PARL_IO =
unsafe { crate::peripherals::PARL_IO::steal() };

reg_block.int_raw.read().tx_eof_int_raw().bit()
}

#[cfg(feature = "async")]
pub fn clear_is_tx_done() {
let reg_block: crate::peripherals::PARL_IO =
unsafe { crate::peripherals::PARL_IO::steal() };

reg_block.int_clr.write(|w| w.tx_eof_int_clr().set_bit());
}
}
}
8 changes: 8 additions & 0 deletions esp32c6-hal/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -127,3 +127,11 @@ required-features = ["embassy", "async"]
[[example]]
name = "embassy_i2s_read"
required-features = ["embassy", "async"]

[[example]]
name = "embassy_parl_io_tx"
required-features = ["embassy", "async"]

[[example]]
name = "embassy_parl_io_rx"
required-features = ["embassy", "async"]
120 changes: 120 additions & 0 deletions esp32c6-hal/examples/embassy_parl_io_rx.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
//! This shows using Parallel IO to input 4 bit parallel data at 1MHz clock
//! rate.
//!
//! Uses GPIO 1, 2, 3 and 4 as the data pins.

#![no_std]
#![no_main]
#![feature(type_alias_impl_trait)]

use embassy_executor::Executor;
use embassy_time::{Duration, Timer};
use esp32c6_hal::{
clock::ClockControl,
dma::DmaPriority,
embassy,
gdma::{self, Gdma},
gpio::{GpioPin, Unknown, IO},
interrupt,
parl_io::{BitPackOrder, NoClkPin, ParlIoRx, ParlIoRxOnly, RxFourBits},
peripherals,
peripherals::Peripherals,
prelude::*,
};
use esp_backtrace as _;
use esp_println::println;
use static_cell::make_static;

#[embassy_executor::task]
async fn parl_io_task(
mut parl_io_rx: ParlIoRx<
'static,
gdma::Channel0,
RxFourBits<
'static,
GpioPin<Unknown, 1>,
GpioPin<Unknown, 2>,
GpioPin<Unknown, 3>,
GpioPin<Unknown, 4>,
>,
NoClkPin,
>,
) {
let buffer = dma_buffer();
loop {
parl_io_rx.read_dma_async(buffer).await.unwrap();
println!("Received: {:02x?} ...", &buffer[..30]);

Timer::after(Duration::from_millis(500)).await;
}
}

#[entry]
fn main() -> ! {
esp_println::println!("Init!");
let peripherals = Peripherals::take();
let mut system = peripherals.PCR.split();
let clocks = ClockControl::boot_defaults(system.clock_control).freeze();

#[cfg(feature = "embassy-time-systick")]
embassy::init(
&clocks,
esp32c6_hal::systimer::SystemTimer::new(peripherals.SYSTIMER),
);

#[cfg(feature = "embassy-time-timg0")]
{
let timer_group0 = esp32c6_hal::timer::TimerGroup::new(
peripherals.TIMG0,
&clocks,
&mut system.peripheral_clock_control,
);
embassy::init(&clocks, timer_group0.timer0);
}

let io = IO::new(peripherals.GPIO, peripherals.IO_MUX);

let tx_descriptors = make_static!([0u32; 8 * 3]);
let rx_descriptors = make_static!([0u32; 8 * 3]);

let dma = Gdma::new(peripherals.DMA, &mut system.peripheral_clock_control);
let dma_channel = dma.channel0;

let rx_pins = RxFourBits::new(io.pins.gpio1, io.pins.gpio2, io.pins.gpio3, io.pins.gpio4);

let parl_io = ParlIoRxOnly::new(
peripherals.PARL_IO,
dma_channel.configure(
false,
tx_descriptors,
rx_descriptors,
DmaPriority::Priority0,
),
1u32.MHz(),
&mut system.peripheral_clock_control,
&clocks,
)
.unwrap();

let parl_io_rx = parl_io
.rx
.with_config(rx_pins, NoClkPin, BitPackOrder::Msb, Some(0xfff))
.unwrap();

// you need to manually enable the DMA channel's interrupt!
interrupt::enable(
peripherals::Interrupt::DMA_IN_CH0,
interrupt::Priority::Priority1,
)
.unwrap();

let executor = make_static!(Executor::new());
executor.run(|spawner| {
spawner.spawn(parl_io_task(parl_io_rx)).ok();
});
}

fn dma_buffer() -> &'static mut [u8; 4092 * 4] {
static mut BUFFER: [u8; 4092 * 4] = [0u8; 4092 * 4];
unsafe { &mut BUFFER }
}
Loading