Async RMT (#787)

* Async RMT

* Add CHANGELOG item

* Pin toml_edit transitive dependency
This commit is contained in:
Björn Quentin 2023-09-13 09:02:36 +02:00 committed by GitHub
parent 4c34ebe9e2
commit a642ee40da
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
21 changed files with 1965 additions and 8 deletions

View File

@ -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

View File

@ -459,6 +459,9 @@ macro_rules! impl_tx_channel_creator {
}
impl<const CHANNEL: u8> $crate::rmt::TxChannel<CHANNEL> for $crate::rmt::[< Channel $channel >]<CHANNEL> {}
#[cfg(feature = "async")]
impl<const CHANNEL: u8> $crate::rmt::asynch::TxChannelAsync<CHANNEL> for $crate::rmt::[< Channel $channel >]<CHANNEL> {}
}
}
}
@ -474,6 +477,9 @@ macro_rules! impl_rx_channel_creator {
}
impl<const CHANNEL: u8> $crate::rmt::RxChannel<CHANNEL> for $crate::rmt::[< Channel $channel >]<CHANNEL> {}
#[cfg(feature = "async")]
impl<const CHANNEL: u8> $crate::rmt::asynch::RxChannelAsync<CHANNEL> for $crate::rmt::[< Channel $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<const CHANNEL: u8>: private::RxChannelInternal<CHANNEL> {
}
}
#[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<T, const CHANNEL: u8>
where
T: TxChannelAsync<CHANNEL>,
{
_phantom: PhantomData<T>,
}
impl<T, const CHANNEL: u8> RmtTxFuture<T, CHANNEL>
where
T: TxChannelAsync<CHANNEL>,
{
pub fn new(_instance: &T) -> Self {
Self {
_phantom: PhantomData,
}
}
}
impl<T, const CHANNEL: u8> core::future::Future for RmtTxFuture<T, CHANNEL>
where
T: TxChannelAsync<CHANNEL>,
{
type Output = ();
fn poll(self: Pin<&mut Self>, ctx: &mut Context<'_>) -> Poll<Self::Output> {
WAKER[CHANNEL as usize].register(ctx.waker());
if T::is_error() || T::is_done() {
Poll::Ready(())
} else {
Poll::Pending
}
}
}
pub trait TxChannelAsync<const CHANNEL: u8>: private::TxChannelInternal<CHANNEL> {
/// 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<u32> + 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<T, const CHANNEL: u8>
where
T: RxChannelAsync<CHANNEL>,
{
_phantom: PhantomData<T>,
}
impl<T, const CHANNEL: u8> RmtRxFuture<T, CHANNEL>
where
T: RxChannelAsync<CHANNEL>,
{
pub fn new(_instance: &T) -> Self {
Self {
_phantom: PhantomData,
}
}
}
impl<T, const CHANNEL: u8> core::future::Future for RmtRxFuture<T, CHANNEL>
where
T: RxChannelAsync<CHANNEL>,
{
type Output = ();
fn poll(self: Pin<&mut Self>, ctx: &mut Context<'_>) -> Poll<Self::Output> {
WAKER[CHANNEL as usize].register(ctx.waker());
if T::is_error() || T::is_done() {
Poll::Ready(())
} else {
Poll::Pending
}
}
}
pub trait RxChannelAsync<const CHANNEL: u8>: private::RxChannelInternal<CHANNEL> {
/// 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<u32> + 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 => {
<Channel0<0> as super::private::TxChannelInternal<0>>::unlisten_interrupt(
Event::End,
);
<Channel0<0> as super::private::TxChannelInternal<0>>::unlisten_interrupt(
Event::Error,
);
<Channel0<0> as super::private::RxChannelInternal<0>>::unlisten_interrupt(
Event::End,
);
<Channel0<0> as super::private::RxChannelInternal<0>>::unlisten_interrupt(
Event::Error,
);
}
1 => {
<Channel1<1> as super::private::TxChannelInternal<1>>::unlisten_interrupt(
Event::End,
);
<Channel1<1> as super::private::TxChannelInternal<1>>::unlisten_interrupt(
Event::Error,
);
<Channel1<1> as super::private::RxChannelInternal<1>>::unlisten_interrupt(
Event::End,
);
<Channel1<1> as super::private::RxChannelInternal<1>>::unlisten_interrupt(
Event::Error,
);
}
2 => {
<Channel2<2> as super::private::TxChannelInternal<2>>::unlisten_interrupt(
Event::End,
);
<Channel2<2> as super::private::TxChannelInternal<2>>::unlisten_interrupt(
Event::Error,
);
<Channel2<2> as super::private::RxChannelInternal<2>>::unlisten_interrupt(
Event::End,
);
<Channel2<2> as super::private::RxChannelInternal<2>>::unlisten_interrupt(
Event::Error,
);
}
3 => {
<Channel3<3> as super::private::TxChannelInternal<3>>::unlisten_interrupt(
Event::End,
);
<Channel3<3> as super::private::TxChannelInternal<3>>::unlisten_interrupt(
Event::Error,
);
<Channel3<3> as super::private::RxChannelInternal<3>>::unlisten_interrupt(
Event::End,
);
<Channel3<3> as super::private::RxChannelInternal<3>>::unlisten_interrupt(
Event::Error,
);
}
#[cfg(any(esp32))]
4 => {
<Channel4<4> as super::private::TxChannelInternal<4>>::unlisten_interrupt(
Event::End,
);
<Channel4<4> as super::private::TxChannelInternal<4>>::unlisten_interrupt(
Event::Error,
);
<Channel4<4> as super::private::RxChannelInternal<4>>::unlisten_interrupt(
Event::End,
);
<Channel4<4> as super::private::RxChannelInternal<4>>::unlisten_interrupt(
Event::Error,
);
}
#[cfg(any(esp32, esp32s3))]
5 => {
<Channel5<5> as super::private::TxChannelInternal<5>>::unlisten_interrupt(
Event::End,
);
<Channel5<5> as super::private::TxChannelInternal<5>>::unlisten_interrupt(
Event::Error,
);
<Channel5<5> as super::private::RxChannelInternal<5>>::unlisten_interrupt(
Event::End,
);
<Channel5<5> as super::private::RxChannelInternal<5>>::unlisten_interrupt(
Event::Error,
);
}
#[cfg(any(esp32, esp32s3))]
6 => {
<Channel6<6> as super::private::TxChannelInternal<6>>::unlisten_interrupt(
Event::End,
);
<Channel6<6> as super::private::TxChannelInternal<6>>::unlisten_interrupt(
Event::Error,
);
<Channel6<6> as super::private::RxChannelInternal<6>>::unlisten_interrupt(
Event::End,
);
<Channel6<6> as super::private::RxChannelInternal<6>>::unlisten_interrupt(
Event::Error,
);
}
#[cfg(any(esp32, esp32s3))]
7 => {
<Channel7<7> as super::private::TxChannelInternal<7>>::unlisten_interrupt(
Event::End,
);
<Channel7<7> as super::private::TxChannelInternal<7>>::unlisten_interrupt(
Event::Error,
);
<Channel7<7> as super::private::RxChannelInternal<7>>::unlisten_interrupt(
Event::End,
);
<Channel7<7> 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<P = crate::peripherals::RMT> + 'd) -> Self;
}
@ -986,6 +1343,10 @@ mod private {
}
fn stop();
fn listen_interrupt(event: Event);
fn unlisten_interrupt(event: Event);
}
pub trait RxChannelInternal<const CHANNEL: u8> {
@ -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<usize> {
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<usize> {
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<const CHANNEL: u8> $crate::rmt::private::RxChannelInternal<CHANNEL> for $crate::rmt:: $channel <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<usize> {
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<usize> {
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());
}
}
}
}
}
}

View File

@ -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 = []

View File

@ -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"]

View File

@ -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<Output<PushPull>>) {
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();
});
}

View File

@ -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();
});
}

View File

@ -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"]

View File

@ -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();
});
}

View File

@ -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();
});
}

View File

@ -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"]

View File

@ -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();
});
}

View File

@ -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();
});
}

View File

@ -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"]

View File

@ -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();
});
}

View File

@ -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();
});
}

View File

@ -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"]

View File

@ -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<Output<PushPull>>) {
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();
});
}

View File

@ -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();
});
}

View File

@ -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"]

View File

@ -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();
});
}

View File

@ -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();
});
}