From a642ee40dafab6edd641bfd41347b82ccf27213d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Quentin?= Date: Wed, 13 Sep 2023 09:02:36 +0200 Subject: [PATCH] Async RMT (#787) * Async RMT * Add CHANGELOG item * Pin toml_edit transitive dependency --- CHANGELOG.md | 1 + esp-hal-common/src/rmt.rs | 593 ++++++++++++++++++++++++- esp-hal-procmacros/Cargo.toml | 2 + esp32-hal/Cargo.toml | 8 + esp32-hal/examples/embassy_rmt_rx.rs | 136 ++++++ esp32-hal/examples/embassy_rmt_tx.rs | 88 ++++ esp32c3-hal/Cargo.toml | 8 + esp32c3-hal/examples/embassy_rmt_rx.rs | 126 ++++++ esp32c3-hal/examples/embassy_rmt_tx.rs | 94 ++++ esp32c6-hal/Cargo.toml | 8 + esp32c6-hal/examples/embassy_rmt_rx.rs | 126 ++++++ esp32c6-hal/examples/embassy_rmt_tx.rs | 94 ++++ esp32h2-hal/Cargo.toml | 8 + esp32h2-hal/examples/embassy_rmt_rx.rs | 126 ++++++ esp32h2-hal/examples/embassy_rmt_tx.rs | 94 ++++ esp32s2-hal/Cargo.toml | 8 + esp32s2-hal/examples/embassy_rmt_rx.rs | 137 ++++++ esp32s2-hal/examples/embassy_rmt_tx.rs | 89 ++++ esp32s3-hal/Cargo.toml | 8 + esp32s3-hal/examples/embassy_rmt_rx.rs | 126 ++++++ esp32s3-hal/examples/embassy_rmt_tx.rs | 93 ++++ 21 files changed, 1965 insertions(+), 8 deletions(-) create mode 100644 esp32-hal/examples/embassy_rmt_rx.rs create mode 100644 esp32-hal/examples/embassy_rmt_tx.rs create mode 100644 esp32c3-hal/examples/embassy_rmt_rx.rs create mode 100644 esp32c3-hal/examples/embassy_rmt_tx.rs create mode 100644 esp32c6-hal/examples/embassy_rmt_rx.rs create mode 100644 esp32c6-hal/examples/embassy_rmt_tx.rs create mode 100644 esp32h2-hal/examples/embassy_rmt_rx.rs create mode 100644 esp32h2-hal/examples/embassy_rmt_tx.rs create mode 100644 esp32s2-hal/examples/embassy_rmt_rx.rs create mode 100644 esp32s2-hal/examples/embassy_rmt_tx.rs create mode 100644 esp32s3-hal/examples/embassy_rmt_rx.rs create mode 100644 esp32s3-hal/examples/embassy_rmt_tx.rs diff --git a/CHANGELOG.md b/CHANGELOG.md index 2bb07561e..0c8c0bfc3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,6 +12,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Added - Implement enabling/disabling BLE clock on ESP32-C6 (#784) +- Async support for RMT (#787) ### Changed diff --git a/esp-hal-common/src/rmt.rs b/esp-hal-common/src/rmt.rs index c9f0997a4..559712d28 100644 --- a/esp-hal-common/src/rmt.rs +++ b/esp-hal-common/src/rmt.rs @@ -459,6 +459,9 @@ macro_rules! impl_tx_channel_creator { } impl $crate::rmt::TxChannel for $crate::rmt::[< Channel $channel >] {} + + #[cfg(feature = "async")] + impl $crate::rmt::asynch::TxChannelAsync for $crate::rmt::[< Channel $channel >] {} } } } @@ -474,6 +477,9 @@ macro_rules! impl_rx_channel_creator { } impl $crate::rmt::RxChannel for $crate::rmt::[< Channel $channel >] {} + + #[cfg(feature = "async")] + impl $crate::rmt::asynch::RxChannelAsync for $crate::rmt::[< Channel $channel >] {} } } } @@ -523,8 +529,8 @@ mod impl_for_chip { super::chip_specific::impl_tx_channel!(Channel0, RMT_SIG_0, 0); super::chip_specific::impl_tx_channel!(Channel1, RMT_SIG_1, 1); - super::chip_specific::impl_rx_channel!(Channel2, RMT_SIG_0, 2); - super::chip_specific::impl_rx_channel!(Channel3, RMT_SIG_1, 3); + super::chip_specific::impl_rx_channel!(Channel2, RMT_SIG_0, 2, 0); + super::chip_specific::impl_rx_channel!(Channel3, RMT_SIG_1, 3, 1); } #[cfg(any(esp32))] @@ -740,10 +746,10 @@ mod impl_for_chip { super::chip_specific::impl_tx_channel!(Channel2, RMT_SIG_2, 2); super::chip_specific::impl_tx_channel!(Channel3, RMT_SIG_3, 3); - super::chip_specific::impl_rx_channel!(Channel4, RMT_SIG_0, 4); - super::chip_specific::impl_rx_channel!(Channel5, RMT_SIG_1, 5); - super::chip_specific::impl_rx_channel!(Channel6, RMT_SIG_2, 6); - super::chip_specific::impl_rx_channel!(Channel7, RMT_SIG_3, 7); + super::chip_specific::impl_rx_channel!(Channel4, RMT_SIG_0, 4, 0); + super::chip_specific::impl_rx_channel!(Channel5, RMT_SIG_1, 5, 1); + super::chip_specific::impl_rx_channel!(Channel6, RMT_SIG_2, 6, 2); + super::chip_specific::impl_rx_channel!(Channel7, RMT_SIG_3, 7, 3); } /// RMT Channel 0 @@ -909,9 +915,360 @@ pub trait RxChannel: private::RxChannelInternal { } } +#[cfg(feature = "async")] +pub mod asynch { + use core::{ + marker::PhantomData, + pin::Pin, + task::{Context, Poll}, + }; + + use embassy_sync::waitqueue::AtomicWaker; + use procmacros::interrupt; + + use super::{private::Event, *}; + + #[cfg(any(esp32, esp32s3))] + const NUM_CHANNELS: usize = 8; + #[cfg(not(any(esp32, esp32s3)))] + const NUM_CHANNELS: usize = 4; + + const INIT: AtomicWaker = AtomicWaker::new(); + static WAKER: [AtomicWaker; NUM_CHANNELS] = [INIT; NUM_CHANNELS]; + + pub(crate) struct RmtTxFuture + where + T: TxChannelAsync, + { + _phantom: PhantomData, + } + + impl RmtTxFuture + where + T: TxChannelAsync, + { + pub fn new(_instance: &T) -> Self { + Self { + _phantom: PhantomData, + } + } + } + + impl core::future::Future for RmtTxFuture + where + T: TxChannelAsync, + { + type Output = (); + + fn poll(self: Pin<&mut Self>, ctx: &mut Context<'_>) -> Poll { + WAKER[CHANNEL as usize].register(ctx.waker()); + + if T::is_error() || T::is_done() { + Poll::Ready(()) + } else { + Poll::Pending + } + } + } + + pub trait TxChannelAsync: private::TxChannelInternal { + /// Start transmitting the given pulse code sequence. + /// The length of sequence cannot exceed the size of the allocated RMT + /// RAM. + async fn transmit<'a, T: Into + Copy>(&mut self, data: &'a [T]) -> Result<(), Error> + where + Self: Sized, + { + if data.len() > constants::RMT_CHANNEL_RAM_SIZE { + return Err(Error::InvalidArgument); + } + + Self::clear_interrupts(); + Self::listen_interrupt(super::private::Event::End); + Self::listen_interrupt(super::private::Event::Error); + Self::send_raw(data, false, 0); + + RmtTxFuture::new(self).await; + + if Self::is_error() { + Err(Error::TransmissionError) + } else { + Ok(()) + } + } + } + + pub(crate) struct RmtRxFuture + where + T: RxChannelAsync, + { + _phantom: PhantomData, + } + + impl RmtRxFuture + where + T: RxChannelAsync, + { + pub fn new(_instance: &T) -> Self { + Self { + _phantom: PhantomData, + } + } + } + + impl core::future::Future for RmtRxFuture + where + T: RxChannelAsync, + { + type Output = (); + + fn poll(self: Pin<&mut Self>, ctx: &mut Context<'_>) -> Poll { + WAKER[CHANNEL as usize].register(ctx.waker()); + if T::is_error() || T::is_done() { + Poll::Ready(()) + } else { + Poll::Pending + } + } + } + + pub trait RxChannelAsync: private::RxChannelInternal { + /// Start receiving a pulse code sequence. + /// The length of sequence cannot exceed the size of the allocated RMT + /// RAM. + async fn receive<'a, T: From + Copy>(&mut self, data: &'a mut [T]) -> Result<(), Error> + where + Self: Sized, + { + if data.len() > constants::RMT_CHANNEL_RAM_SIZE { + return Err(Error::InvalidArgument); + } + + Self::clear_interrupts(); + Self::listen_interrupt(super::private::Event::End); + Self::listen_interrupt(super::private::Event::Error); + Self::start_receive_raw(); + + RmtRxFuture::new(self).await; + + if Self::is_error() { + Err(Error::TransmissionError) + } else { + Self::stop(); + Self::clear_interrupts(); + Self::update(); + + let ptr = (constants::RMT_RAM_START + + CHANNEL as usize * constants::RMT_CHANNEL_RAM_SIZE * 4) + as *mut u32; + let len = data.len(); + for (idx, entry) in data.iter_mut().take(len).enumerate() { + *entry = unsafe { ptr.add(idx).read_volatile().into() }; + } + + Ok(()) + } + } + } + + #[cfg(not(any(esp32, esp32s2)))] + #[interrupt] + fn RMT() { + if let Some(channel) = super::chip_specific::pending_interrupt_for_channel() { + use crate::rmt::private::{RxChannelInternal, TxChannelInternal}; + + match channel { + 0 => { + super::Channel0::<0>::unlisten_interrupt(Event::End); + super::Channel0::<0>::unlisten_interrupt(Event::Error); + } + 1 => { + super::Channel1::<1>::unlisten_interrupt(Event::End); + super::Channel1::<1>::unlisten_interrupt(Event::Error); + } + 2 => { + super::Channel2::<2>::unlisten_interrupt(Event::End); + super::Channel2::<2>::unlisten_interrupt(Event::Error); + } + 3 => { + super::Channel3::<3>::unlisten_interrupt(Event::End); + super::Channel3::<3>::unlisten_interrupt(Event::Error); + } + + // TODO ... how to handle chips which can use a channel for tx AND rx? + #[cfg(any(esp32, esp32s3))] + 4 => { + super::Channel4::<4>::unlisten_interrupt(Event::End); + super::Channel4::<4>::unlisten_interrupt(Event::Error); + } + #[cfg(any(esp32, esp32s3))] + 5 => { + super::Channel5::<5>::unlisten_interrupt(Event::End); + super::Channel5::<5>::unlisten_interrupt(Event::Error); + } + #[cfg(any(esp32, esp32s3))] + 6 => { + super::Channel6::<6>::unlisten_interrupt(Event::End); + super::Channel6::<6>::unlisten_interrupt(Event::Error); + } + #[cfg(any(esp32, esp32s3))] + 7 => { + super::Channel7::<7>::unlisten_interrupt(Event::End); + super::Channel7::<7>::unlisten_interrupt(Event::Error); + } + + _ => unreachable!(), + } + + let rmt = unsafe { &*crate::peripherals::RMT::PTR }; + rmt.int_ena.write(|w| unsafe { w.bits(0) }); + + WAKER[channel].wake(); + } + } + + #[cfg(any(esp32, esp32s2))] + #[interrupt] + fn RMT() { + if let Some(channel) = super::chip_specific::pending_interrupt_for_channel() { + match channel { + 0 => { + as super::private::TxChannelInternal<0>>::unlisten_interrupt( + Event::End, + ); + as super::private::TxChannelInternal<0>>::unlisten_interrupt( + Event::Error, + ); + as super::private::RxChannelInternal<0>>::unlisten_interrupt( + Event::End, + ); + as super::private::RxChannelInternal<0>>::unlisten_interrupt( + Event::Error, + ); + } + 1 => { + as super::private::TxChannelInternal<1>>::unlisten_interrupt( + Event::End, + ); + as super::private::TxChannelInternal<1>>::unlisten_interrupt( + Event::Error, + ); + as super::private::RxChannelInternal<1>>::unlisten_interrupt( + Event::End, + ); + as super::private::RxChannelInternal<1>>::unlisten_interrupt( + Event::Error, + ); + } + 2 => { + as super::private::TxChannelInternal<2>>::unlisten_interrupt( + Event::End, + ); + as super::private::TxChannelInternal<2>>::unlisten_interrupt( + Event::Error, + ); + as super::private::RxChannelInternal<2>>::unlisten_interrupt( + Event::End, + ); + as super::private::RxChannelInternal<2>>::unlisten_interrupt( + Event::Error, + ); + } + 3 => { + as super::private::TxChannelInternal<3>>::unlisten_interrupt( + Event::End, + ); + as super::private::TxChannelInternal<3>>::unlisten_interrupt( + Event::Error, + ); + as super::private::RxChannelInternal<3>>::unlisten_interrupt( + Event::End, + ); + as super::private::RxChannelInternal<3>>::unlisten_interrupt( + Event::Error, + ); + } + #[cfg(any(esp32))] + 4 => { + as super::private::TxChannelInternal<4>>::unlisten_interrupt( + Event::End, + ); + as super::private::TxChannelInternal<4>>::unlisten_interrupt( + Event::Error, + ); + as super::private::RxChannelInternal<4>>::unlisten_interrupt( + Event::End, + ); + as super::private::RxChannelInternal<4>>::unlisten_interrupt( + Event::Error, + ); + } + #[cfg(any(esp32, esp32s3))] + 5 => { + as super::private::TxChannelInternal<5>>::unlisten_interrupt( + Event::End, + ); + as super::private::TxChannelInternal<5>>::unlisten_interrupt( + Event::Error, + ); + as super::private::RxChannelInternal<5>>::unlisten_interrupt( + Event::End, + ); + as super::private::RxChannelInternal<5>>::unlisten_interrupt( + Event::Error, + ); + } + #[cfg(any(esp32, esp32s3))] + 6 => { + as super::private::TxChannelInternal<6>>::unlisten_interrupt( + Event::End, + ); + as super::private::TxChannelInternal<6>>::unlisten_interrupt( + Event::Error, + ); + as super::private::RxChannelInternal<6>>::unlisten_interrupt( + Event::End, + ); + as super::private::RxChannelInternal<6>>::unlisten_interrupt( + Event::Error, + ); + } + #[cfg(any(esp32, esp32s3))] + 7 => { + as super::private::TxChannelInternal<7>>::unlisten_interrupt( + Event::End, + ); + as super::private::TxChannelInternal<7>>::unlisten_interrupt( + Event::Error, + ); + as super::private::RxChannelInternal<7>>::unlisten_interrupt( + Event::End, + ); + as super::private::RxChannelInternal<7>>::unlisten_interrupt( + Event::Error, + ); + } + + _ => unreachable!(), + } + + let rmt = unsafe { &*crate::peripherals::RMT::PTR }; + rmt.int_ena.write(|w| unsafe { w.bits(0) }); + + WAKER[channel].wake(); + } + } +} + mod private { use crate::{peripheral::Peripheral, soc::constants}; + pub enum Event { + Error, + Threshold, + End, + } + pub trait CreateInstance<'d> { fn create(peripheral: impl Peripheral

+ 'd) -> Self; } @@ -986,6 +1343,10 @@ mod private { } fn stop(); + + fn listen_interrupt(event: Event); + + fn unlisten_interrupt(event: Event); } pub trait RxChannelInternal { @@ -1024,6 +1385,10 @@ mod private { fn set_filter_threshold(value: u8); fn set_idle_threshold(value: u16); + + fn listen_interrupt(event: Event); + + fn unlisten_interrupt(event: Event); } } @@ -1068,6 +1433,52 @@ mod chip_specific { } } + #[allow(unused)] + #[cfg(not(esp32s3))] + pub fn pending_interrupt_for_channel() -> Option { + let rmt = unsafe { &*crate::peripherals::RMT::PTR }; + let st = rmt.int_st.read(); + + if st.ch0_tx_end().bit() || st.ch0_tx_err().bit() { + Some(0) + } else if st.ch1_tx_end().bit() || st.ch1_tx_err().bit() { + Some(1) + } else if st.ch2_rx_end().bit() || st.ch2_rx_err().bit() { + Some(2) + } else if st.ch3_rx_end().bit() || st.ch3_rx_err().bit() { + Some(3) + } else { + None + } + } + + #[allow(unused)] + #[cfg(esp32s3)] + pub fn pending_interrupt_for_channel() -> Option { + let rmt = unsafe { &*crate::peripherals::RMT::PTR }; + let st = rmt.int_st.read(); + + if st.ch0_tx_end().bit() || st.ch0_tx_err().bit() { + Some(0) + } else if st.ch1_tx_end().bit() || st.ch1_tx_err().bit() { + Some(1) + } else if st.ch2_tx_end().bit() || st.ch2_tx_err().bit() { + Some(2) + } else if st.ch3_tx_end().bit() || st.ch3_tx_err().bit() { + Some(3) + } else if st.ch4_rx_end().bit() || st.ch4_rx_err().bit() { + Some(4) + } else if st.ch5_rx_end().bit() || st.ch5_rx_err().bit() { + Some(5) + } else if st.ch6_rx_end().bit() || st.ch6_rx_err().bit() { + Some(6) + } else if st.ch7_rx_end().bit() || st.ch7_rx_err().bit() { + Some(7) + } else { + None + } + } + macro_rules! impl_tx_channel { ($channel:ident, $signal:ident, $ch_num:literal) => { paste::paste! { @@ -1222,13 +1633,43 @@ mod chip_specific { rmt.ch_tx_conf0[$ch_num].modify(|_, w| w.tx_stop().set_bit()); Self::update(); } + + fn listen_interrupt(event: $crate::rmt::private::Event) { + let rmt = unsafe { &*crate::peripherals::RMT::PTR }; + match event { + $crate::rmt::private::Event::Error => { + rmt.int_ena.modify(|_,w| w.[< ch $ch_num _tx_err >]().set_bit()); + } + $crate::rmt::private::Event::End => { + rmt.int_ena.modify(|_,w| w.[< ch $ch_num _tx_end >]().set_bit()); + } + $crate::rmt::private::Event::Threshold => { + rmt.int_ena.modify(|_,w| w.[< ch $ch_num _tx_thr_event >]().set_bit()); + } + } + } + + fn unlisten_interrupt(event: $crate::rmt::private::Event) { + let rmt = unsafe { &*crate::peripherals::RMT::PTR }; + match event { + $crate::rmt::private::Event::Error => { + rmt.int_ena.modify(|_,w| w.[< ch $ch_num _tx_err >]().clear_bit()); + } + $crate::rmt::private::Event::End => { + rmt.int_ena.modify(|_,w| w.[< ch $ch_num _tx_end >]().clear_bit()); + } + $crate::rmt::private::Event::Threshold => { + rmt.int_ena.modify(|_,w| w.[< ch $ch_num _tx_thr_event >]().clear_bit()); + } + } + } } } } } macro_rules! impl_rx_channel { - ($channel:ident, $signal:ident, $ch_num:literal) => { + ($channel:ident, $signal:ident, $ch_num:literal, $ch_index:literal) => { paste::paste! { impl $crate::rmt::private::RxChannelInternal for $crate::rmt:: $channel { fn new() -> Self { @@ -1270,7 +1711,7 @@ mod chip_specific { fn set_carrier(carrier: bool, high: u16, low: u16, level: bool) { let rmt = unsafe { &*crate::peripherals::RMT::PTR }; - rmt.ch_rx_carrier_rm[$ch_num].write(|w| { + rmt.ch_rx_carrier_rm[$ch_index].write(|w| { w.carrier_high_thres() .variant(high) .carrier_low_thres() @@ -1331,6 +1772,36 @@ mod chip_specific { rmt.[< ch $ch_num _rx_conf0 >].modify(|_, w| w.idle_thres().variant(value)); } + + fn listen_interrupt(event: $crate::rmt::private::Event) { + let rmt = unsafe { &*crate::peripherals::RMT::PTR }; + match event { + $crate::rmt::private::Event::Error => { + rmt.int_ena.modify(|_,w| w.[< ch $ch_num _rx_err >]().set_bit()); + } + $crate::rmt::private::Event::End => { + rmt.int_ena.modify(|_,w| w.[< ch $ch_num _rx_end >]().set_bit()); + } + $crate::rmt::private::Event::Threshold => { + rmt.int_ena.modify(|_,w| w.[< ch $ch_num _rx_thr_event >]().set_bit()); + } + } + } + + fn unlisten_interrupt(event: $crate::rmt::private::Event) { + let rmt = unsafe { &*crate::peripherals::RMT::PTR }; + match event { + $crate::rmt::private::Event::Error => { + rmt.int_ena.modify(|_,w| w.[< ch $ch_num _rx_err >]().clear_bit()); + } + $crate::rmt::private::Event::End => { + rmt.int_ena.modify(|_,w| w.[< ch $ch_num _rx_end >]().clear_bit()); + } + $crate::rmt::private::Event::Threshold => { + rmt.int_ena.modify(|_,w| w.[< ch $ch_num _rx_thr_event >]().clear_bit()); + } + } + } } } } @@ -1363,6 +1834,52 @@ mod chip_specific { rmt.apb_conf.modify(|_, w| w.clk_en().set_bit()); } + #[allow(unused)] + #[cfg(esp32)] + pub fn pending_interrupt_for_channel() -> Option { + let rmt = unsafe { &*crate::peripherals::RMT::PTR }; + let st = rmt.int_st.read(); + + if st.ch0_rx_end().bit() || st.ch0_tx_end().bit() || st.ch0_err().bit() { + Some(0) + } else if st.ch1_rx_end().bit() || st.ch1_tx_end().bit() || st.ch1_err().bit() { + Some(1) + } else if st.ch2_rx_end().bit() || st.ch2_tx_end().bit() || st.ch2_err().bit() { + Some(2) + } else if st.ch3_rx_end().bit() || st.ch3_tx_end().bit() || st.ch3_err().bit() { + Some(3) + } else if st.ch4_rx_end().bit() || st.ch4_tx_end().bit() || st.ch4_err().bit() { + Some(4) + } else if st.ch5_rx_end().bit() || st.ch5_tx_end().bit() || st.ch5_err().bit() { + Some(5) + } else if st.ch6_rx_end().bit() || st.ch6_tx_end().bit() || st.ch6_err().bit() { + Some(6) + } else if st.ch7_rx_end().bit() || st.ch7_tx_end().bit() || st.ch7_err().bit() { + Some(7) + } else { + None + } + } + + #[allow(unused)] + #[cfg(esp32s2)] + pub fn pending_interrupt_for_channel() -> Option { + let rmt = unsafe { &*crate::peripherals::RMT::PTR }; + let st = rmt.int_st.read(); + + if st.ch0_rx_end().bit() || st.ch0_tx_end().bit() || st.ch0_err().bit() { + Some(0) + } else if st.ch1_rx_end().bit() || st.ch1_tx_end().bit() || st.ch1_err().bit() { + Some(1) + } else if st.ch2_rx_end().bit() || st.ch2_tx_end().bit() || st.ch2_err().bit() { + Some(2) + } else if st.ch3_rx_end().bit() || st.ch3_tx_end().bit() || st.ch3_err().bit() { + Some(3) + } else { + None + } + } + macro_rules! impl_tx_channel { ($channel:ident, $signal:ident, $ch_num:literal) => { paste::paste! { @@ -1497,6 +2014,36 @@ mod chip_specific { rmt.[< ch $ch_num conf1 >].modify(|_, w| w.tx_stop().set_bit()); } } + + fn listen_interrupt(event: $crate::rmt::private::Event) { + let rmt = unsafe { &*crate::peripherals::RMT::PTR }; + match event { + $crate::rmt::private::Event::Error => { + rmt.int_ena.modify(|_,w| w.[< ch $ch_num _err >]().set_bit()); + } + $crate::rmt::private::Event::End => { + rmt.int_ena.modify(|_,w| w.[< ch $ch_num _tx_end >]().set_bit()); + } + $crate::rmt::private::Event::Threshold => { + rmt.int_ena.modify(|_,w| w.[< ch $ch_num _tx_thr_event >]().set_bit()); + } + } + } + + fn unlisten_interrupt(event: $crate::rmt::private::Event) { + let rmt = unsafe { &*crate::peripherals::RMT::PTR }; + match event { + $crate::rmt::private::Event::Error => { + rmt.int_ena.modify(|_,w| w.[< ch $ch_num _err >]().clear_bit()); + } + $crate::rmt::private::Event::End => { + rmt.int_ena.modify(|_,w| w.[< ch $ch_num _tx_end >]().clear_bit()); + } + $crate::rmt::private::Event::Threshold => { + rmt.int_ena.modify(|_,w| w.[< ch $ch_num _tx_thr_event >]().clear_bit()); + } + } + } } } } @@ -1613,6 +2160,36 @@ mod chip_specific { rmt.[< ch $ch_num conf0 >].modify(|_, w| w.idle_thres().variant(value)); } + + fn listen_interrupt(event: $crate::rmt::private::Event) { + let rmt = unsafe { &*crate::peripherals::RMT::PTR }; + match event { + $crate::rmt::private::Event::Error => { + rmt.int_ena.modify(|_,w| w.[< ch $ch_num _err >]().set_bit()); + } + $crate::rmt::private::Event::End => { + rmt.int_ena.modify(|_,w| w.[< ch $ch_num _rx_end >]().set_bit()); + } + $crate::rmt::private::Event::Threshold => { + rmt.int_ena.modify(|_,w| w.[< ch $ch_num _tx_thr_event >]().set_bit()); + } + } + } + + fn unlisten_interrupt(event: $crate::rmt::private::Event) { + let rmt = unsafe { &*crate::peripherals::RMT::PTR }; + match event { + $crate::rmt::private::Event::Error => { + rmt.int_ena.modify(|_,w| w.[< ch $ch_num _err >]().clear_bit()); + } + $crate::rmt::private::Event::End => { + rmt.int_ena.modify(|_,w| w.[< ch $ch_num _rx_end >]().clear_bit()); + } + $crate::rmt::private::Event::Threshold => { + rmt.int_ena.modify(|_,w| w.[< ch $ch_num _tx_thr_event >]().clear_bit()); + } + } + } } } } diff --git a/esp-hal-procmacros/Cargo.toml b/esp-hal-procmacros/Cargo.toml index 276429f01..51908a816 100644 --- a/esp-hal-procmacros/Cargo.toml +++ b/esp-hal-procmacros/Cargo.toml @@ -24,6 +24,8 @@ proc-macro-error = "1.0.4" proc-macro2 = "1.0.66" quote = "1.0.33" syn = {version = "2.0.31", features = ["extra-traits", "full"]} +# toml_edit is a dependency of proc-macro-crate. Unfortunately they raised their MSRV on 0.19.15 so we pin the version for now +toml_edit = "=0.19.14" [features] esp32 = [] diff --git a/esp32-hal/Cargo.toml b/esp32-hal/Cargo.toml index 1c5cb8c1f..b96a3ed6e 100644 --- a/esp32-hal/Cargo.toml +++ b/esp32-hal/Cargo.toml @@ -121,3 +121,11 @@ required-features = ["embassy", "embassy-executor-thread", "async"] [[example]] name = "embassy_i2c" required-features = ["embassy", "embassy-executor-thread", "async"] + +[[example]] +name = "embassy_rmt_tx" +required-features = ["embassy", "async"] + +[[example]] +name = "embassy_rmt_rx" +required-features = ["embassy", "async"] diff --git a/esp32-hal/examples/embassy_rmt_rx.rs b/esp32-hal/examples/embassy_rmt_rx.rs new file mode 100644 index 000000000..fd45e44e0 --- /dev/null +++ b/esp32-hal/examples/embassy_rmt_rx.rs @@ -0,0 +1,136 @@ +//! Demonstrates decoding pulse sequences with RMT +//! Connect GPIO15 to GPIO4 + +#![no_std] +#![no_main] +#![feature(type_alias_impl_trait)] + +use embassy_time::{Duration, Timer}; +use esp32_hal::{ + clock::ClockControl, + embassy::{self, executor::Executor}, + peripherals::Peripherals, + prelude::*, + rmt::{asynch::RxChannelAsync, Channel2, PulseCode, RxChannelConfig, RxChannelCreator}, + Rmt, + IO, +}; +use esp_backtrace as _; +use esp_hal_common::gpio::{Gpio15, Output, PushPull}; +use esp_println::{print, println}; +use static_cell::make_static; + +const WIDTH: usize = 80; + +#[cfg(debug_assertions)] +compile_error!("Run this example in release mode"); + +#[embassy_executor::task] +async fn rmt_task(mut channel: Channel2<2>) { + let mut data = [PulseCode { + level1: true, + length1: 1, + level2: false, + length2: 1, + }; 48]; + + loop { + println!("receive"); + channel.receive(&mut data).await.unwrap(); + let mut total = 0usize; + for entry in &data[..data.len()] { + if entry.length1 == 0 { + break; + } + total += entry.length1 as usize; + + if entry.length2 == 0 { + break; + } + total += entry.length2 as usize; + } + + for entry in &data[..data.len()] { + if entry.length1 == 0 { + break; + } + + let count = WIDTH / (total / entry.length1 as usize); + let c = if entry.level1 { '-' } else { '_' }; + for _ in 0..count + 1 { + print!("{}", c); + } + + if entry.length2 == 0 { + break; + } + + let count = WIDTH / (total / entry.length2 as usize); + let c = if entry.level2 { '-' } else { '_' }; + for _ in 0..count + 1 { + print!("{}", c); + } + } + + println!(); + } +} + +#[embassy_executor::task] +async fn signal_task(mut pin: Gpio15>) { + loop { + for _ in 0..10 { + pin.toggle().unwrap(); + Timer::after(Duration::from_micros(10)).await; + } + Timer::after(Duration::from_millis(1000)).await; + } +} + +#[entry] +fn main() -> ! { + esp_println::logger::init_logger_from_env(); + println!("Init!"); + let peripherals = Peripherals::take(); + let system = peripherals.DPORT.split(); + let clocks = ClockControl::boot_defaults(system.clock_control).freeze(); + let mut clock_control = system.peripheral_clock_control; + + #[cfg(feature = "embassy-time-timg0")] + { + let timer_group0 = + esp32_hal::timer::TimerGroup::new(peripherals.TIMG0, &clocks, &mut clock_control); + embassy::init(&clocks, timer_group0.timer0); + } + + let io = IO::new(peripherals.GPIO, peripherals.IO_MUX); + + let rmt = Rmt::new(peripherals.RMT, 80u32.MHz(), &mut clock_control, &clocks).unwrap(); + + let channel = rmt + .channel2 + .configure( + io.pins.gpio4, + RxChannelConfig { + clk_divider: 1, + idle_threshold: 0b111_1111_1111_1111, + ..RxChannelConfig::default() + }, + ) + .unwrap(); + + // you have to enable the interrupt for async to work + esp32_hal::interrupt::enable( + esp32_hal::peripherals::Interrupt::RMT, + esp32_hal::interrupt::Priority::Priority1, + ) + .unwrap(); + + let executor = make_static!(Executor::new()); + executor.run(|spawner| { + spawner.spawn(rmt_task(channel)).ok(); + spawner + .spawn(signal_task(io.pins.gpio15.into_push_pull_output())) + .ok(); + }); +} diff --git a/esp32-hal/examples/embassy_rmt_tx.rs b/esp32-hal/examples/embassy_rmt_tx.rs new file mode 100644 index 000000000..cbbca79ee --- /dev/null +++ b/esp32-hal/examples/embassy_rmt_tx.rs @@ -0,0 +1,88 @@ +//! Demonstrates generating pulse sequences with RMT +//! Connect a logic analyzer to GPIO4 to see the generated pulses. + +#![no_std] +#![no_main] +#![feature(type_alias_impl_trait)] + +use embassy_time::{Duration, Timer}; +use esp32_hal::{ + clock::ClockControl, + embassy::{self, executor::Executor}, + peripherals::Peripherals, + prelude::*, + rmt::{asynch::TxChannelAsync, Channel0, PulseCode, TxChannelConfig, TxChannelCreator}, + Rmt, + IO, +}; +use esp_backtrace as _; +use static_cell::make_static; + +#[embassy_executor::task] +async fn rmt_task(mut channel: Channel0<0>) { + let mut data = [PulseCode { + level1: true, + length1: 200, + level2: false, + length2: 50, + }; 20]; + + data[data.len() - 2] = PulseCode { + level1: true, + length1: 3000, + level2: false, + length2: 500, + }; + data[data.len() - 1] = PulseCode::default(); + + loop { + esp_println::println!("transmit"); + channel.transmit(&data).await.unwrap(); + esp_println::println!("transmitted\n"); + Timer::after(Duration::from_millis(500)).await; + } +} + +#[entry] +fn main() -> ! { + esp_println::logger::init_logger_from_env(); + esp_println::println!("Init!"); + let peripherals = Peripherals::take(); + let system = peripherals.DPORT.split(); + let clocks = ClockControl::boot_defaults(system.clock_control).freeze(); + let mut clock_control = system.peripheral_clock_control; + + #[cfg(feature = "embassy-time-timg0")] + { + let timer_group0 = + esp32_hal::timer::TimerGroup::new(peripherals.TIMG0, &clocks, &mut clock_control); + embassy::init(&clocks, timer_group0.timer0); + } + + let io = IO::new(peripherals.GPIO, peripherals.IO_MUX); + + let rmt = Rmt::new(peripherals.RMT, 80u32.MHz(), &mut clock_control, &clocks).unwrap(); + + let channel = rmt + .channel0 + .configure( + io.pins.gpio4.into_push_pull_output(), + TxChannelConfig { + clk_divider: 255, + ..TxChannelConfig::default() + }, + ) + .unwrap(); + + // you have to enable the interrupt for async to work + esp32_hal::interrupt::enable( + esp32_hal::peripherals::Interrupt::RMT, + esp32_hal::interrupt::Priority::Priority1, + ) + .unwrap(); + + let executor = make_static!(Executor::new()); + executor.run(|spawner| { + spawner.spawn(rmt_task(channel)).ok(); + }); +} diff --git a/esp32c3-hal/Cargo.toml b/esp32c3-hal/Cargo.toml index 8fc17b059..083d43fe6 100644 --- a/esp32c3-hal/Cargo.toml +++ b/esp32c3-hal/Cargo.toml @@ -106,6 +106,14 @@ required-features = ["embassy", "async"] name = "embassy_i2c" required-features = ["embassy", "async"] +[[example]] +name = "embassy_rmt_tx" +required-features = ["embassy", "async"] + +[[example]] +name = "embassy_rmt_rx" +required-features = ["embassy", "async"] + [[example]] name = "direct-vectoring" required-features = ["direct-vectoring"] diff --git a/esp32c3-hal/examples/embassy_rmt_rx.rs b/esp32c3-hal/examples/embassy_rmt_rx.rs new file mode 100644 index 000000000..31c0963a0 --- /dev/null +++ b/esp32c3-hal/examples/embassy_rmt_rx.rs @@ -0,0 +1,126 @@ +//! Demonstrates decoding pulse sequences with RMT +//! This uses the boot button as input - press the button a couple of +//! times to generate a pulse sequence and then wait for the idle timeout to see +//! the recorded pulse sequence + +#![no_std] +#![no_main] +#![feature(type_alias_impl_trait)] + +use embassy_executor::Executor; +use esp32c3_hal::{ + clock::ClockControl, + embassy::{self}, + peripherals::Peripherals, + prelude::*, + rmt::{asynch::RxChannelAsync, Channel2, PulseCode, RxChannelConfig, RxChannelCreator}, + Rmt, + IO, +}; +use esp_backtrace as _; +use esp_println::{print, println}; +use static_cell::make_static; + +const WIDTH: usize = 80; + +#[embassy_executor::task] +async fn rmt_task(mut channel: Channel2<2>) { + let mut data = [PulseCode { + level1: true, + length1: 1, + level2: false, + length2: 1, + }; 48]; + + loop { + println!("receive"); + channel.receive(&mut data).await.unwrap(); + println!("received"); + let mut total = 0usize; + for entry in &data[..data.len()] { + if entry.length1 == 0 { + break; + } + total += entry.length1 as usize; + + if entry.length2 == 0 { + break; + } + total += entry.length2 as usize; + } + + for entry in &data[..data.len()] { + if entry.length1 == 0 { + break; + } + + let count = WIDTH / (total / entry.length1 as usize); + let c = if entry.level1 { '-' } else { '_' }; + for _ in 0..count + 1 { + print!("{}", c); + } + + if entry.length2 == 0 { + break; + } + + let count = WIDTH / (total / entry.length2 as usize); + let c = if entry.level2 { '-' } else { '_' }; + for _ in 0..count + 1 { + print!("{}", c); + } + } + + println!(); + } +} + +#[entry] +fn main() -> ! { + esp_println::logger::init_logger_from_env(); + println!("Init!"); + let peripherals = Peripherals::take(); + let system = peripherals.SYSTEM.split(); + let clocks = ClockControl::boot_defaults(system.clock_control).freeze(); + let mut clock_control = system.peripheral_clock_control; + + #[cfg(feature = "embassy-time-systick")] + embassy::init( + &clocks, + esp32c3_hal::systimer::SystemTimer::new(peripherals.SYSTIMER), + ); + + #[cfg(feature = "embassy-time-timg0")] + embassy::init( + &clocks, + esp32c3_hal::timer::TimerGroup::new(peripherals.TIMG0, &clocks, &mut clock_control).timer0, + ); + + let io = IO::new(peripherals.GPIO, peripherals.IO_MUX); + + let rmt = Rmt::new(peripherals.RMT, 8u32.MHz(), &mut clock_control, &clocks).unwrap(); + + let channel = rmt + .channel2 + .configure( + io.pins.gpio9, + RxChannelConfig { + clk_divider: 255, + idle_threshold: 10000, + ..RxChannelConfig::default() + }, + ) + .unwrap(); + + // you have to enable the interrupt for async to work + esp32c3_hal::interrupt::enable( + esp32c3_hal::peripherals::Interrupt::RMT, + esp32c3_hal::interrupt::Priority::Priority1, + ) + .unwrap(); + + let executor = make_static!(Executor::new()); + executor.run(|spawner| { + spawner.spawn(rmt_task(channel)).ok(); + }); +} diff --git a/esp32c3-hal/examples/embassy_rmt_tx.rs b/esp32c3-hal/examples/embassy_rmt_tx.rs new file mode 100644 index 000000000..6bf1c1ba4 --- /dev/null +++ b/esp32c3-hal/examples/embassy_rmt_tx.rs @@ -0,0 +1,94 @@ +//! Demonstrates generating pulse sequences with RMT +//! Connect a logic analyzer to GPIO1 to see the generated pulses. + +#![no_std] +#![no_main] +#![feature(type_alias_impl_trait)] + +use embassy_executor::Executor; +use embassy_time::{Duration, Timer}; +use esp32c3_hal::{ + clock::ClockControl, + embassy, + peripherals::Peripherals, + prelude::*, + rmt::{asynch::TxChannelAsync, Channel0, PulseCode, TxChannelConfig, TxChannelCreator}, + Rmt, + IO, +}; +use esp_backtrace as _; +use static_cell::make_static; + +#[embassy_executor::task] +async fn rmt_task(mut channel: Channel0<0>) { + let mut data = [PulseCode { + level1: true, + length1: 200, + level2: false, + length2: 50, + }; 20]; + + data[data.len() - 2] = PulseCode { + level1: true, + length1: 3000, + level2: false, + length2: 500, + }; + data[data.len() - 1] = PulseCode::default(); + + loop { + esp_println::println!("transmit"); + channel.transmit(&data).await.unwrap(); + esp_println::println!("transmitted\n"); + Timer::after(Duration::from_millis(500)).await; + } +} + +#[entry] +fn main() -> ! { + esp_println::logger::init_logger_from_env(); + esp_println::println!("Init!"); + let peripherals = Peripherals::take(); + let system = peripherals.SYSTEM.split(); + let clocks = ClockControl::boot_defaults(system.clock_control).freeze(); + let mut clock_control = system.peripheral_clock_control; + + #[cfg(feature = "embassy-time-systick")] + embassy::init( + &clocks, + esp32c3_hal::systimer::SystemTimer::new(peripherals.SYSTIMER), + ); + + #[cfg(feature = "embassy-time-timg0")] + embassy::init( + &clocks, + esp32c3_hal::timer::TimerGroup::new(peripherals.TIMG0, &clocks, &mut clock_control).timer0, + ); + + let io = IO::new(peripherals.GPIO, peripherals.IO_MUX); + + let rmt = Rmt::new(peripherals.RMT, 8u32.MHz(), &mut clock_control, &clocks).unwrap(); + + let channel = rmt + .channel0 + .configure( + io.pins.gpio1.into_push_pull_output(), + TxChannelConfig { + clk_divider: 255, + ..TxChannelConfig::default() + }, + ) + .unwrap(); + + // you have to enable the interrupt for async to work + esp32c3_hal::interrupt::enable( + esp32c3_hal::peripherals::Interrupt::RMT, + esp32c3_hal::interrupt::Priority::Priority1, + ) + .unwrap(); + + let executor = make_static!(Executor::new()); + executor.run(|spawner| { + spawner.spawn(rmt_task(channel)).ok(); + }); +} diff --git a/esp32c6-hal/Cargo.toml b/esp32c6-hal/Cargo.toml index dc3d1edb7..4ac1c5f55 100644 --- a/esp32c6-hal/Cargo.toml +++ b/esp32c6-hal/Cargo.toml @@ -107,3 +107,11 @@ required-features = ["embassy", "async"] [[example]] name = "direct-vectoring" required-features = ["direct-vectoring"] + +[[example]] +name = "embassy_rmt_tx" +required-features = ["embassy", "async"] + +[[example]] +name = "embassy_rmt_rx" +required-features = ["embassy", "async"] diff --git a/esp32c6-hal/examples/embassy_rmt_rx.rs b/esp32c6-hal/examples/embassy_rmt_rx.rs new file mode 100644 index 000000000..dbe01a573 --- /dev/null +++ b/esp32c6-hal/examples/embassy_rmt_rx.rs @@ -0,0 +1,126 @@ +//! Demonstrates decoding pulse sequences with RMT +//! This uses the boot button as input - press the button a couple of +//! times to generate a pulse sequence and then wait for the idle timeout to see +//! the recorded pulse sequence + +#![no_std] +#![no_main] +#![feature(type_alias_impl_trait)] + +use embassy_executor::Executor; +use esp32c6_hal::{ + clock::ClockControl, + embassy::{self}, + peripherals::Peripherals, + prelude::*, + rmt::{asynch::RxChannelAsync, Channel2, PulseCode, RxChannelConfig, RxChannelCreator}, + Rmt, + IO, +}; +use esp_backtrace as _; +use esp_println::{print, println}; +use static_cell::make_static; + +const WIDTH: usize = 80; + +#[embassy_executor::task] +async fn rmt_task(mut channel: Channel2<2>) { + let mut data = [PulseCode { + level1: true, + length1: 1, + level2: false, + length2: 1, + }; 48]; + + loop { + println!("receive"); + channel.receive(&mut data).await.unwrap(); + println!("received"); + let mut total = 0usize; + for entry in &data[..data.len()] { + if entry.length1 == 0 { + break; + } + total += entry.length1 as usize; + + if entry.length2 == 0 { + break; + } + total += entry.length2 as usize; + } + + for entry in &data[..data.len()] { + if entry.length1 == 0 { + break; + } + + let count = WIDTH / (total / entry.length1 as usize); + let c = if entry.level1 { '-' } else { '_' }; + for _ in 0..count + 1 { + print!("{}", c); + } + + if entry.length2 == 0 { + break; + } + + let count = WIDTH / (total / entry.length2 as usize); + let c = if entry.level2 { '-' } else { '_' }; + for _ in 0..count + 1 { + print!("{}", c); + } + } + + println!(); + } +} + +#[entry] +fn main() -> ! { + esp_println::logger::init_logger_from_env(); + println!("Init!"); + let peripherals = Peripherals::take(); + let system = peripherals.PCR.split(); + let clocks = ClockControl::boot_defaults(system.clock_control).freeze(); + let mut clock_control = system.peripheral_clock_control; + + #[cfg(feature = "embassy-time-systick")] + embassy::init( + &clocks, + esp32c6_hal::systimer::SystemTimer::new(peripherals.SYSTIMER), + ); + + #[cfg(feature = "embassy-time-timg0")] + embassy::init( + &clocks, + esp32c6_hal::timer::TimerGroup::new(peripherals.TIMG0, &clocks, &mut clock_control).timer0, + ); + + let io = IO::new(peripherals.GPIO, peripherals.IO_MUX); + + let rmt = Rmt::new(peripherals.RMT, 8u32.MHz(), &mut clock_control, &clocks).unwrap(); + + let channel = rmt + .channel2 + .configure( + io.pins.gpio9, + RxChannelConfig { + clk_divider: 255, + idle_threshold: 10000, + ..RxChannelConfig::default() + }, + ) + .unwrap(); + + // you have to enable the interrupt for async to work + esp32c6_hal::interrupt::enable( + esp32c6_hal::peripherals::Interrupt::RMT, + esp32c6_hal::interrupt::Priority::Priority1, + ) + .unwrap(); + + let executor = make_static!(Executor::new()); + executor.run(|spawner| { + spawner.spawn(rmt_task(channel)).ok(); + }); +} diff --git a/esp32c6-hal/examples/embassy_rmt_tx.rs b/esp32c6-hal/examples/embassy_rmt_tx.rs new file mode 100644 index 000000000..081d81298 --- /dev/null +++ b/esp32c6-hal/examples/embassy_rmt_tx.rs @@ -0,0 +1,94 @@ +//! Demonstrates generating pulse sequences with RMT +//! Connect a logic analyzer to GPIO1 to see the generated pulses. + +#![no_std] +#![no_main] +#![feature(type_alias_impl_trait)] + +use embassy_executor::Executor; +use embassy_time::{Duration, Timer}; +use esp32c6_hal::{ + clock::ClockControl, + embassy, + peripherals::Peripherals, + prelude::*, + rmt::{asynch::TxChannelAsync, Channel0, PulseCode, TxChannelConfig, TxChannelCreator}, + Rmt, + IO, +}; +use esp_backtrace as _; +use static_cell::make_static; + +#[embassy_executor::task] +async fn rmt_task(mut channel: Channel0<0>) { + let mut data = [PulseCode { + level1: true, + length1: 200, + level2: false, + length2: 50, + }; 20]; + + data[data.len() - 2] = PulseCode { + level1: true, + length1: 3000, + level2: false, + length2: 500, + }; + data[data.len() - 1] = PulseCode::default(); + + loop { + esp_println::println!("transmit"); + channel.transmit(&data).await.unwrap(); + esp_println::println!("transmitted\n"); + Timer::after(Duration::from_millis(500)).await; + } +} + +#[entry] +fn main() -> ! { + esp_println::logger::init_logger_from_env(); + esp_println::println!("Init!"); + let peripherals = Peripherals::take(); + let system = peripherals.PCR.split(); + let clocks = ClockControl::boot_defaults(system.clock_control).freeze(); + let mut clock_control = system.peripheral_clock_control; + + #[cfg(feature = "embassy-time-systick")] + embassy::init( + &clocks, + esp32c6_hal::systimer::SystemTimer::new(peripherals.SYSTIMER), + ); + + #[cfg(feature = "embassy-time-timg0")] + embassy::init( + &clocks, + esp32c6_hal::timer::TimerGroup::new(peripherals.TIMG0, &clocks, &mut clock_control).timer0, + ); + + let io = IO::new(peripherals.GPIO, peripherals.IO_MUX); + + let rmt = Rmt::new(peripherals.RMT, 8u32.MHz(), &mut clock_control, &clocks).unwrap(); + + let channel = rmt + .channel0 + .configure( + io.pins.gpio1.into_push_pull_output(), + TxChannelConfig { + clk_divider: 255, + ..TxChannelConfig::default() + }, + ) + .unwrap(); + + // you have to enable the interrupt for async to work + esp32c6_hal::interrupt::enable( + esp32c6_hal::peripherals::Interrupt::RMT, + esp32c6_hal::interrupt::Priority::Priority1, + ) + .unwrap(); + + let executor = make_static!(Executor::new()); + executor.run(|spawner| { + spawner.spawn(rmt_task(channel)).ok(); + }); +} diff --git a/esp32h2-hal/Cargo.toml b/esp32h2-hal/Cargo.toml index ada8274cc..839eb3574 100644 --- a/esp32h2-hal/Cargo.toml +++ b/esp32h2-hal/Cargo.toml @@ -100,6 +100,14 @@ required-features = ["async", "embassy"] name = "embassy_wait" required-features = ["async", "embassy"] +[[example]] +name = "embassy_rmt_tx" +required-features = ["embassy", "async"] + +[[example]] +name = "embassy_rmt_rx" +required-features = ["embassy", "async"] + [[example]] name = "interrupt_preemption" required-features = ["interrupt-preemption"] diff --git a/esp32h2-hal/examples/embassy_rmt_rx.rs b/esp32h2-hal/examples/embassy_rmt_rx.rs new file mode 100644 index 000000000..3dae9d045 --- /dev/null +++ b/esp32h2-hal/examples/embassy_rmt_rx.rs @@ -0,0 +1,126 @@ +//! Demonstrates decoding pulse sequences with RMT +//! This uses the boot button as input - press the button a couple of +//! times to generate a pulse sequence and then wait for the idle timeout to see +//! the recorded pulse sequence + +#![no_std] +#![no_main] +#![feature(type_alias_impl_trait)] + +use embassy_executor::Executor; +use esp32h2_hal::{ + clock::ClockControl, + embassy::{self}, + peripherals::Peripherals, + prelude::*, + rmt::{asynch::RxChannelAsync, Channel2, PulseCode, RxChannelConfig, RxChannelCreator}, + Rmt, + IO, +}; +use esp_backtrace as _; +use esp_println::{print, println}; +use static_cell::make_static; + +const WIDTH: usize = 80; + +#[embassy_executor::task] +async fn rmt_task(mut channel: Channel2<2>) { + let mut data = [PulseCode { + level1: true, + length1: 1, + level2: false, + length2: 1, + }; 48]; + + loop { + println!("receive"); + channel.receive(&mut data).await.unwrap(); + println!("received"); + let mut total = 0usize; + for entry in &data[..data.len()] { + if entry.length1 == 0 { + break; + } + total += entry.length1 as usize; + + if entry.length2 == 0 { + break; + } + total += entry.length2 as usize; + } + + for entry in &data[..data.len()] { + if entry.length1 == 0 { + break; + } + + let count = WIDTH / (total / entry.length1 as usize); + let c = if entry.level1 { '-' } else { '_' }; + for _ in 0..count + 1 { + print!("{}", c); + } + + if entry.length2 == 0 { + break; + } + + let count = WIDTH / (total / entry.length2 as usize); + let c = if entry.level2 { '-' } else { '_' }; + for _ in 0..count + 1 { + print!("{}", c); + } + } + + println!(); + } +} + +#[entry] +fn main() -> ! { + esp_println::logger::init_logger_from_env(); + println!("Init!"); + let peripherals = Peripherals::take(); + let system = peripherals.PCR.split(); + let clocks = ClockControl::boot_defaults(system.clock_control).freeze(); + let mut clock_control = system.peripheral_clock_control; + + #[cfg(feature = "embassy-time-systick")] + embassy::init( + &clocks, + esp32h2_hal::systimer::SystemTimer::new(peripherals.SYSTIMER), + ); + + #[cfg(feature = "embassy-time-timg0")] + embassy::init( + &clocks, + esp32h2_hal::timer::TimerGroup::new(peripherals.TIMG0, &clocks, &mut clock_control).timer0, + ); + + let io = IO::new(peripherals.GPIO, peripherals.IO_MUX); + + let rmt = Rmt::new(peripherals.RMT, 8u32.MHz(), &mut clock_control, &clocks).unwrap(); + + let channel = rmt + .channel2 + .configure( + io.pins.gpio9, + RxChannelConfig { + clk_divider: 255, + idle_threshold: 10000, + ..RxChannelConfig::default() + }, + ) + .unwrap(); + + // you have to enable the interrupt for async to work + esp32h2_hal::interrupt::enable( + esp32h2_hal::peripherals::Interrupt::RMT, + esp32h2_hal::interrupt::Priority::Priority1, + ) + .unwrap(); + + let executor = make_static!(Executor::new()); + executor.run(|spawner| { + spawner.spawn(rmt_task(channel)).ok(); + }); +} diff --git a/esp32h2-hal/examples/embassy_rmt_tx.rs b/esp32h2-hal/examples/embassy_rmt_tx.rs new file mode 100644 index 000000000..f9af079a6 --- /dev/null +++ b/esp32h2-hal/examples/embassy_rmt_tx.rs @@ -0,0 +1,94 @@ +//! Demonstrates generating pulse sequences with RMT +//! Connect a logic analyzer to GPIO1 to see the generated pulses. + +#![no_std] +#![no_main] +#![feature(type_alias_impl_trait)] + +use embassy_executor::Executor; +use embassy_time::{Duration, Timer}; +use esp32h2_hal::{ + clock::ClockControl, + embassy, + peripherals::Peripherals, + prelude::*, + rmt::{asynch::TxChannelAsync, Channel0, PulseCode, TxChannelConfig, TxChannelCreator}, + Rmt, + IO, +}; +use esp_backtrace as _; +use static_cell::make_static; + +#[embassy_executor::task] +async fn rmt_task(mut channel: Channel0<0>) { + let mut data = [PulseCode { + level1: true, + length1: 200, + level2: false, + length2: 50, + }; 20]; + + data[data.len() - 2] = PulseCode { + level1: true, + length1: 3000, + level2: false, + length2: 500, + }; + data[data.len() - 1] = PulseCode::default(); + + loop { + esp_println::println!("transmit"); + channel.transmit(&data).await.unwrap(); + esp_println::println!("transmitted\n"); + Timer::after(Duration::from_millis(500)).await; + } +} + +#[entry] +fn main() -> ! { + esp_println::logger::init_logger_from_env(); + esp_println::println!("Init!"); + let peripherals = Peripherals::take(); + let system = peripherals.PCR.split(); + let clocks = ClockControl::boot_defaults(system.clock_control).freeze(); + let mut clock_control = system.peripheral_clock_control; + + #[cfg(feature = "embassy-time-systick")] + embassy::init( + &clocks, + esp32h2_hal::systimer::SystemTimer::new(peripherals.SYSTIMER), + ); + + #[cfg(feature = "embassy-time-timg0")] + embassy::init( + &clocks, + esp32h2_hal::timer::TimerGroup::new(peripherals.TIMG0, &clocks, &mut clock_control).timer0, + ); + + let io = IO::new(peripherals.GPIO, peripherals.IO_MUX); + + let rmt = Rmt::new(peripherals.RMT, 8u32.MHz(), &mut clock_control, &clocks).unwrap(); + + let channel = rmt + .channel0 + .configure( + io.pins.gpio1.into_push_pull_output(), + TxChannelConfig { + clk_divider: 255, + ..TxChannelConfig::default() + }, + ) + .unwrap(); + + // you have to enable the interrupt for async to work + esp32h2_hal::interrupt::enable( + esp32h2_hal::peripherals::Interrupt::RMT, + esp32h2_hal::interrupt::Priority::Priority1, + ) + .unwrap(); + + let executor = make_static!(Executor::new()); + executor.run(|spawner| { + spawner.spawn(rmt_task(channel)).ok(); + }); +} diff --git a/esp32s2-hal/Cargo.toml b/esp32s2-hal/Cargo.toml index 6315a3394..d986ec557 100644 --- a/esp32s2-hal/Cargo.toml +++ b/esp32s2-hal/Cargo.toml @@ -118,3 +118,11 @@ required-features = ["embassy", "embassy-executor-thread", "async"] [[example]] name = "embassy_i2c" required-features = ["embassy", "embassy-executor-thread", "async"] + +[[example]] +name = "embassy_rmt_tx" +required-features = ["embassy", "async"] + +[[example]] +name = "embassy_rmt_rx" +required-features = ["embassy", "async"] diff --git a/esp32s2-hal/examples/embassy_rmt_rx.rs b/esp32s2-hal/examples/embassy_rmt_rx.rs new file mode 100644 index 000000000..5543513e2 --- /dev/null +++ b/esp32s2-hal/examples/embassy_rmt_rx.rs @@ -0,0 +1,137 @@ +//! Demonstrates decoding pulse sequences with RMT +//! Connect GPIO15 to GPIO4 + +#![no_std] +#![no_main] +#![feature(type_alias_impl_trait)] + +use embassy_time::{Duration, Timer}; +use esp32s2_hal::{ + clock::ClockControl, + embassy::{self, executor::Executor}, + peripherals::Peripherals, + prelude::*, + rmt::{asynch::RxChannelAsync, Channel2, PulseCode, RxChannelConfig, RxChannelCreator}, + Rmt, + IO, +}; +use esp_backtrace as _; +use esp_hal_common::gpio::{Gpio15, Output, PushPull}; +use esp_println::{print, println}; +use static_cell::make_static; +use xtensa_atomic_emulation_trap as _; + +const WIDTH: usize = 80; + +#[cfg(debug_assertions)] +compile_error!("Run this example in release mode"); + +#[embassy_executor::task] +async fn rmt_task(mut channel: Channel2<2>) { + let mut data = [PulseCode { + level1: true, + length1: 1, + level2: false, + length2: 1, + }; 48]; + + loop { + println!("receive"); + channel.receive(&mut data).await.unwrap(); + let mut total = 0usize; + for entry in &data[..data.len()] { + if entry.length1 == 0 { + break; + } + total += entry.length1 as usize; + + if entry.length2 == 0 { + break; + } + total += entry.length2 as usize; + } + + for entry in &data[..data.len()] { + if entry.length1 == 0 { + break; + } + + let count = WIDTH / (total / entry.length1 as usize); + let c = if entry.level1 { '-' } else { '_' }; + for _ in 0..count + 1 { + print!("{}", c); + } + + if entry.length2 == 0 { + break; + } + + let count = WIDTH / (total / entry.length2 as usize); + let c = if entry.level2 { '-' } else { '_' }; + for _ in 0..count + 1 { + print!("{}", c); + } + } + + println!(); + } +} + +#[embassy_executor::task] +async fn signal_task(mut pin: Gpio15>) { + loop { + for _ in 0..10 { + pin.toggle().unwrap(); + Timer::after(Duration::from_micros(10)).await; + } + Timer::after(Duration::from_millis(1000)).await; + } +} + +#[entry] +fn main() -> ! { + esp_println::logger::init_logger_from_env(); + println!("Init!"); + let peripherals = Peripherals::take(); + let system = peripherals.SYSTEM.split(); + let clocks = ClockControl::boot_defaults(system.clock_control).freeze(); + let mut clock_control = system.peripheral_clock_control; + + #[cfg(feature = "embassy-time-timg0")] + { + let timer_group0 = + esp32s2_hal::timer::TimerGroup::new(peripherals.TIMG0, &clocks, &mut clock_control); + embassy::init(&clocks, timer_group0.timer0); + } + + let io = IO::new(peripherals.GPIO, peripherals.IO_MUX); + + let rmt = Rmt::new(peripherals.RMT, 80u32.MHz(), &mut clock_control, &clocks).unwrap(); + + let channel = rmt + .channel2 + .configure( + io.pins.gpio4, + RxChannelConfig { + clk_divider: 1, + idle_threshold: 0b111_1111_1111_1111, + ..RxChannelConfig::default() + }, + ) + .unwrap(); + + // you have to enable the interrupt for async to work + esp32s2_hal::interrupt::enable( + esp32s2_hal::peripherals::Interrupt::RMT, + esp32s2_hal::interrupt::Priority::Priority1, + ) + .unwrap(); + + let executor = make_static!(Executor::new()); + executor.run(|spawner| { + spawner.spawn(rmt_task(channel)).ok(); + spawner + .spawn(signal_task(io.pins.gpio15.into_push_pull_output())) + .ok(); + }); +} diff --git a/esp32s2-hal/examples/embassy_rmt_tx.rs b/esp32s2-hal/examples/embassy_rmt_tx.rs new file mode 100644 index 000000000..99d60aeea --- /dev/null +++ b/esp32s2-hal/examples/embassy_rmt_tx.rs @@ -0,0 +1,89 @@ +//! Demonstrates generating pulse sequences with RMT +//! Connect a logic analyzer to GPIO4 to see the generated pulses. + +#![no_std] +#![no_main] +#![feature(type_alias_impl_trait)] + +use embassy_time::{Duration, Timer}; +use esp32s2_hal::{ + clock::ClockControl, + embassy::{self, executor::Executor}, + peripherals::Peripherals, + prelude::*, + rmt::{asynch::TxChannelAsync, Channel0, PulseCode, TxChannelConfig, TxChannelCreator}, + Rmt, + IO, +}; +use esp_backtrace as _; +use static_cell::make_static; +use xtensa_atomic_emulation_trap as _; + +#[embassy_executor::task] +async fn rmt_task(mut channel: Channel0<0>) { + let mut data = [PulseCode { + level1: true, + length1: 200, + level2: false, + length2: 50, + }; 20]; + + data[data.len() - 2] = PulseCode { + level1: true, + length1: 3000, + level2: false, + length2: 500, + }; + data[data.len() - 1] = PulseCode::default(); + + loop { + esp_println::println!("transmit"); + channel.transmit(&data).await.unwrap(); + esp_println::println!("transmitted\n"); + Timer::after(Duration::from_millis(500)).await; + } +} + +#[entry] +fn main() -> ! { + esp_println::logger::init_logger_from_env(); + esp_println::println!("Init!"); + let peripherals = Peripherals::take(); + let system = peripherals.SYSTEM.split(); + let clocks = ClockControl::boot_defaults(system.clock_control).freeze(); + let mut clock_control = system.peripheral_clock_control; + + #[cfg(feature = "embassy-time-timg0")] + { + let timer_group0 = + esp32s2_hal::timer::TimerGroup::new(peripherals.TIMG0, &clocks, &mut clock_control); + embassy::init(&clocks, timer_group0.timer0); + } + + let io = IO::new(peripherals.GPIO, peripherals.IO_MUX); + + let rmt = Rmt::new(peripherals.RMT, 80u32.MHz(), &mut clock_control, &clocks).unwrap(); + + let channel = rmt + .channel0 + .configure( + io.pins.gpio4.into_push_pull_output(), + TxChannelConfig { + clk_divider: 255, + ..TxChannelConfig::default() + }, + ) + .unwrap(); + + // you have to enable the interrupt for async to work + esp32s2_hal::interrupt::enable( + esp32s2_hal::peripherals::Interrupt::RMT, + esp32s2_hal::interrupt::Priority::Priority1, + ) + .unwrap(); + + let executor = make_static!(Executor::new()); + executor.run(|spawner| { + spawner.spawn(rmt_task(channel)).ok(); + }); +} diff --git a/esp32s3-hal/Cargo.toml b/esp32s3-hal/Cargo.toml index 3e21af906..5e88bd39b 100644 --- a/esp32s3-hal/Cargo.toml +++ b/esp32s3-hal/Cargo.toml @@ -135,3 +135,11 @@ required-features = ["embassy", "embassy-executor-thread", "async"] [[example]] name = "embassy_i2c" required-features = ["embassy", "embassy-executor-thread", "async"] + +[[example]] +name = "embassy_rmt_tx" +required-features = ["embassy", "async"] + +[[example]] +name = "embassy_rmt_rx" +required-features = ["embassy", "async"] diff --git a/esp32s3-hal/examples/embassy_rmt_rx.rs b/esp32s3-hal/examples/embassy_rmt_rx.rs new file mode 100644 index 000000000..944442b2f --- /dev/null +++ b/esp32s3-hal/examples/embassy_rmt_rx.rs @@ -0,0 +1,126 @@ +//! Demonstrates decoding pulse sequences with RMT +//! This uses the boot button as input - press the button a couple of +//! times to generate a pulse sequence and then wait for the idle timeout to see +//! the recorded pulse sequence + +#![no_std] +#![no_main] +#![feature(type_alias_impl_trait)] + +use esp32s3_hal::{ + clock::ClockControl, + embassy::{self, executor::Executor}, + peripherals::Peripherals, + prelude::*, + rmt::{asynch::RxChannelAsync, Channel4, PulseCode, RxChannelConfig, RxChannelCreator}, + Rmt, + IO, +}; +use esp_backtrace as _; +use esp_println::{print, println}; +use static_cell::make_static; + +const WIDTH: usize = 80; + +#[embassy_executor::task] +async fn rmt_task(mut channel: Channel4<4>) { + let mut data = [PulseCode { + level1: true, + length1: 1, + level2: false, + length2: 1, + }; 48]; + + loop { + println!("receive"); + channel.receive(&mut data).await.unwrap(); + println!("received"); + let mut total = 0usize; + for entry in &data[..data.len()] { + if entry.length1 == 0 { + break; + } + total += entry.length1 as usize; + + if entry.length2 == 0 { + break; + } + total += entry.length2 as usize; + } + + for entry in &data[..data.len()] { + if entry.length1 == 0 { + break; + } + + let count = WIDTH / (total / entry.length1 as usize); + let c = if entry.level1 { '-' } else { '_' }; + for _ in 0..count + 1 { + print!("{}", c); + } + + if entry.length2 == 0 { + break; + } + + let count = WIDTH / (total / entry.length2 as usize); + let c = if entry.level2 { '-' } else { '_' }; + for _ in 0..count + 1 { + print!("{}", c); + } + } + + println!(); + } +} + +#[entry] +fn main() -> ! { + esp_println::logger::init_logger_from_env(); + println!("Init!"); + let peripherals = Peripherals::take(); + let system = peripherals.SYSTEM.split(); + let clocks = ClockControl::boot_defaults(system.clock_control).freeze(); + let mut clock_control = system.peripheral_clock_control; + + #[cfg(feature = "embassy-time-systick")] + embassy::init( + &clocks, + esp32s3_hal::systimer::SystemTimer::new(peripherals.SYSTIMER), + ); + + #[cfg(feature = "embassy-time-timg0")] + { + let timer_group0 = + esp32s3_hal::timer::TimerGroup::new(peripherals.TIMG0, &clocks, &mut clock_control); + embassy::init(&clocks, timer_group0.timer0); + } + + let io = IO::new(peripherals.GPIO, peripherals.IO_MUX); + + let rmt = Rmt::new(peripherals.RMT, 8u32.MHz(), &mut clock_control, &clocks).unwrap(); + + let channel = rmt + .channel4 + .configure( + io.pins.gpio0, + RxChannelConfig { + clk_divider: 255, + idle_threshold: 10000, + ..RxChannelConfig::default() + }, + ) + .unwrap(); + + // you have to enable the interrupt for async to work + esp32s3_hal::interrupt::enable( + esp32s3_hal::peripherals::Interrupt::RMT, + esp32s3_hal::interrupt::Priority::Priority1, + ) + .unwrap(); + + let executor = make_static!(Executor::new()); + executor.run(|spawner| { + spawner.spawn(rmt_task(channel)).ok(); + }); +} diff --git a/esp32s3-hal/examples/embassy_rmt_tx.rs b/esp32s3-hal/examples/embassy_rmt_tx.rs new file mode 100644 index 000000000..875f8493d --- /dev/null +++ b/esp32s3-hal/examples/embassy_rmt_tx.rs @@ -0,0 +1,93 @@ +//! Demonstrates generating pulse sequences with RMT +//! Connect a logic analyzer to GPIO1 to see the generated pulses. + +#![no_std] +#![no_main] +#![feature(type_alias_impl_trait)] + +use embassy_time::{Duration, Timer}; +use esp32s3_hal::{ + clock::ClockControl, + embassy::{self, executor::Executor}, + peripherals::Peripherals, + prelude::*, + rmt::{asynch::TxChannelAsync, Channel0, PulseCode, TxChannelConfig, TxChannelCreator}, + Rmt, + IO, +}; +use esp_backtrace as _; +use static_cell::make_static; + +#[embassy_executor::task] +async fn rmt_task(mut channel: Channel0<0>) { + let mut data = [PulseCode { + level1: true, + length1: 200, + level2: false, + length2: 50, + }; 20]; + + data[data.len() - 2] = PulseCode { + level1: true, + length1: 3000, + level2: false, + length2: 500, + }; + data[data.len() - 1] = PulseCode::default(); + + loop { + esp_println::println!("transmit"); + channel.transmit(&data).await.unwrap(); + esp_println::println!("transmitted\n"); + Timer::after(Duration::from_millis(500)).await; + } +} + +#[entry] +fn main() -> ! { + esp_println::logger::init_logger_from_env(); + esp_println::println!("Init!"); + let peripherals = Peripherals::take(); + let system = peripherals.SYSTEM.split(); + let clocks = ClockControl::boot_defaults(system.clock_control).freeze(); + let mut clock_control = system.peripheral_clock_control; + + #[cfg(feature = "embassy-time-systick")] + embassy::init( + &clocks, + esp32c3_hal::systimer::SystemTimer::new(peripherals.SYSTIMER), + ); + + #[cfg(feature = "embassy-time-timg0")] + embassy::init( + &clocks, + esp32s3_hal::timer::TimerGroup::new(peripherals.TIMG0, &clocks, &mut clock_control).timer0, + ); + + let io = IO::new(peripherals.GPIO, peripherals.IO_MUX); + + let rmt = Rmt::new(peripherals.RMT, 8u32.MHz(), &mut clock_control, &clocks).unwrap(); + + let channel = rmt + .channel0 + .configure( + io.pins.gpio1.into_push_pull_output(), + TxChannelConfig { + clk_divider: 255, + ..TxChannelConfig::default() + }, + ) + .unwrap(); + + // you have to enable the interrupt for async to work + esp32s3_hal::interrupt::enable( + esp32s3_hal::peripherals::Interrupt::RMT, + esp32s3_hal::interrupt::Priority::Priority1, + ) + .unwrap(); + + let executor = make_static!(Executor::new()); + executor.run(|spawner| { + spawner.spawn(rmt_task(channel)).ok(); + }); +}